compat-drivers-2012-09-18/0000755000175000017500000000000012026211316014361 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/0000755000175000017500000000000012026211315016036 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/0000755000175000017500000000000012026211315016631 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/0000755000175000017500000000000012026211315017413 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/vmwgfx/0000755000175000017500000000000012026211315020731 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/via/0000755000175000017500000000000012026211315020172 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/ttm/0000755000175000017500000000000012026211315020217 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/radeon/0000755000175000017500000000000012026211315020663 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/nouveau/0000755000175000017500000000000012026211315021075 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/mgag200/0000755000175000017500000000000012026211315020550 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/i915/0000755000175000017500000000000012026211315020102 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/i810/0000755000175000017500000000000012026211315020074 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/i2c/0000755000175000017500000000000012026211315020070 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/gma500/0000755000175000017500000000000012026211315020404 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/cirrus/0000755000175000017500000000000012026211315020722 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/gpu/drm/ast/0000755000175000017500000000000012026211315020202 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/bluetooth/0000755000175000017500000000000012026211315020043 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/bluetooth/hci_ath.c0000644000175000017500000001222612026211315021611 0ustar mcgrofmcgrof/* * Atheros Communication Bluetooth HCIATH3K UART protocol * * HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's * power management protocol extension to H4 to support AR300x Bluetooth Chip. * * Copyright (c) 2009-2010 Atheros Communications Inc. * * Acknowledgements: * This file is based on hci_h4.c, which was written * by Maxim Krasnyansky and Marcel Holtmann. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include "hci_uart.h" struct ath_struct { struct hci_uart *hu; unsigned int cur_sleep; struct sk_buff_head txq; struct work_struct ctxtsw; }; static int ath_wakeup_ar3k(struct tty_struct *tty) { struct ktermios ktermios; int status = tty->driver->ops->tiocmget(tty); if (status & TIOCM_CTS) return status; /* Disable Automatic RTSCTS */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) ktermios = tty->termios; #else memcpy(&ktermios, tty->termios, sizeof(ktermios)); #endif ktermios.c_cflag &= ~CRTSCTS; tty_set_termios(tty, &ktermios); /* Clear RTS first */ status = tty->driver->ops->tiocmget(tty); tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS); mdelay(20); /* Set RTS, wake up board */ status = tty->driver->ops->tiocmget(tty); tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00); mdelay(20); status = tty->driver->ops->tiocmget(tty); /* Disable Automatic RTSCTS */ ktermios.c_cflag |= CRTSCTS; status = tty_set_termios(tty, &ktermios); return status; } static void ath_hci_uart_work(struct work_struct *work) { int status; struct ath_struct *ath; struct hci_uart *hu; struct tty_struct *tty; ath = container_of(work, struct ath_struct, ctxtsw); hu = ath->hu; tty = hu->tty; /* verify and wake up controller */ if (ath->cur_sleep) { status = ath_wakeup_ar3k(tty); if (!(status & TIOCM_CTS)) return; } /* Ready to send Data */ clear_bit(HCI_UART_SENDING, &hu->tx_state); hci_uart_tx_wakeup(hu); } /* Initialize protocol */ static int ath_open(struct hci_uart *hu) { struct ath_struct *ath; BT_DBG("hu %p", hu); ath = kzalloc(sizeof(*ath), GFP_KERNEL); if (!ath) return -ENOMEM; skb_queue_head_init(&ath->txq); hu->priv = ath; ath->hu = hu; INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); return 0; } /* Flush protocol data */ static int ath_flush(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; BT_DBG("hu %p", hu); skb_queue_purge(&ath->txq); return 0; } /* Close protocol */ static int ath_close(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; BT_DBG("hu %p", hu); skb_queue_purge(&ath->txq); cancel_work_sync(&ath->ctxtsw); hu->priv = NULL; kfree(ath); return 0; } #define HCI_OP_ATH_SLEEP 0xFC04 /* Enqueue frame for transmittion */ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct ath_struct *ath = hu->priv; if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { kfree_skb(skb); return 0; } /* * Update power management enable flag with parameters of * HCI sleep enable vendor specific HCI command. */ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { struct hci_command_hdr *hdr = (void *)skb->data; if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP) ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE]; } BT_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&ath->txq, skb); set_bit(HCI_UART_SENDING, &hu->tx_state); schedule_work(&ath->ctxtsw); return 0; } static struct sk_buff *ath_dequeue(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; return skb_dequeue(&ath->txq); } /* Recv data */ static int ath_recv(struct hci_uart *hu, void *data, int count) { int ret; ret = hci_recv_stream_fragment(hu->hdev, data, count); if (ret < 0) { BT_ERR("Frame Reassembly Failed"); return ret; } return count; } static struct hci_uart_proto athp = { .id = HCI_UART_ATH3K, .open = ath_open, .close = ath_close, .recv = ath_recv, .enqueue = ath_enqueue, .dequeue = ath_dequeue, .flush = ath_flush, }; int __init ath_init(void) { int err = hci_uart_register_proto(&athp); if (!err) BT_INFO("HCIATH3K protocol initialized"); else BT_ERR("HCIATH3K protocol registration failed"); return err; } int __exit ath_deinit(void) { return hci_uart_unregister_proto(&athp); } compat-drivers-2012-09-18/drivers/bluetooth/btusb.c0000644000175000017500000007343612026211315021343 0ustar mcgrofmcgrof/* * * Generic Bluetooth USB driver * * Copyright (C) 2005-2008 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #define VERSION "0.6" static bool ignore_dga; static bool ignore_csr; static bool ignore_sniffer; static bool disable_scofix; static bool force_scofix; static bool reset = 1; static struct usb_driver btusb_driver; #define BTUSB_IGNORE 0x01 #define BTUSB_DIGIANSWER 0x02 #define BTUSB_CSR 0x04 #define BTUSB_SNIFFER 0x08 #define BTUSB_BCM92035 0x10 #define BTUSB_BROKEN_ISOC 0x20 #define BTUSB_WRONG_SCO_MTU 0x40 #define BTUSB_ATH3012 0x80 static struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, /* Apple-specific (Broadcom) devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) }, /* Broadcom SoftSailing reporting vendor specific */ { USB_DEVICE(0x0a5c, 0x21e1) }, /* Apple MacBookPro 7,1 */ { USB_DEVICE(0x05ac, 0x8213) }, /* Apple iMac11,1 */ { USB_DEVICE(0x05ac, 0x8215) }, /* Apple MacBookPro6,2 */ { USB_DEVICE(0x05ac, 0x8218) }, /* Apple MacBookAir3,1, MacBookAir3,2 */ { USB_DEVICE(0x05ac, 0x821b) }, /* Apple MacBookAir4,1 */ { USB_DEVICE(0x05ac, 0x821f) }, /* Apple MacBookPro8,2 */ { USB_DEVICE(0x05ac, 0x821a) }, /* Apple MacMini5,1 */ { USB_DEVICE(0x05ac, 0x8281) }, /* AVM BlueFRITZ! USB v2.0 */ { USB_DEVICE(0x057c, 0x3800) }, /* Bluetooth Ultraport Module from IBM */ { USB_DEVICE(0x04bf, 0x030a) }, /* ALPS Modules with non-standard id */ { USB_DEVICE(0x044e, 0x3001) }, { USB_DEVICE(0x044e, 0x3002) }, /* Ericsson with non-standard id */ { USB_DEVICE(0x0bdb, 0x1002) }, /* Canyon CN-BTU1 with HID interfaces */ { USB_DEVICE(0x0c10, 0x0000) }, /* Broadcom BCM20702A0 */ { USB_DEVICE(0x0489, 0xe042) }, { USB_DEVICE(0x413c, 0x8197) }, /* Foxconn - Hon Hai */ { USB_DEVICE(0x0489, 0xe033) }, /*Broadcom devices with vendor specific id */ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, btusb_table); static struct usb_device_id blacklist_table[] = { /* CSR BlueCore devices */ { USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR }, /* Broadcom BCM2033 without firmware */ { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE }, /* Atheros 3011 with sflash firmware */ { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE }, /* Atheros AR9285 Malbec with sflash firmware */ { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE }, /* Atheros 3012 with sflash firmware */ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 }, /* Broadcom BCM2035 */ { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 }, /* Broadcom BCM2045 */ { USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_WRONG_SCO_MTU }, /* IBM/Lenovo ThinkPad with Broadcom chip */ { USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_WRONG_SCO_MTU }, /* HP laptop with Broadcom chip */ { USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_WRONG_SCO_MTU }, /* Dell laptop with Broadcom chip */ { USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_WRONG_SCO_MTU }, /* Dell Wireless 370 and 410 devices */ { USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_WRONG_SCO_MTU }, /* Belkin F8T012 and F8T013 devices */ { USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_WRONG_SCO_MTU }, /* Asus WL-BTD202 device */ { USB_DEVICE(0x0b05, 0x1715), .driver_info = BTUSB_WRONG_SCO_MTU }, /* Kensington Bluetooth USB adapter */ { USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_WRONG_SCO_MTU }, /* RTX Telecom based adapters with buggy SCO support */ { USB_DEVICE(0x0400, 0x0807), .driver_info = BTUSB_BROKEN_ISOC }, { USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC }, /* CONWISE Technology based adapters with buggy SCO support */ { USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC }, /* Digianswer devices */ { USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER }, { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE }, /* CSR BlueCore Bluetooth Sniffer */ { USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER }, /* Frontline ComProbe Bluetooth Sniffer */ { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER }, { } /* Terminating entry */ }; #define BTUSB_MAX_ISOC_FRAMES 10 #define BTUSB_INTR_RUNNING 0 #define BTUSB_BULK_RUNNING 1 #define BTUSB_ISOC_RUNNING 2 #define BTUSB_SUSPENDING 3 #define BTUSB_DID_ISO_RESUME 4 struct btusb_data { struct hci_dev *hdev; struct usb_device *udev; struct usb_interface *intf; struct usb_interface *isoc; spinlock_t lock; unsigned long flags; struct work_struct work; struct work_struct waker; struct usb_anchor tx_anchor; struct usb_anchor intr_anchor; struct usb_anchor bulk_anchor; struct usb_anchor isoc_anchor; struct usb_anchor deferred; int tx_in_flight; spinlock_t txlock; struct usb_endpoint_descriptor *intr_ep; struct usb_endpoint_descriptor *bulk_tx_ep; struct usb_endpoint_descriptor *bulk_rx_ep; struct usb_endpoint_descriptor *isoc_tx_ep; struct usb_endpoint_descriptor *isoc_rx_ep; __u8 cmdreq_type; unsigned int sco_num; int isoc_altsetting; int suspend_count; }; static int inc_tx(struct btusb_data *data) { unsigned long flags; int rv; spin_lock_irqsave(&data->txlock, flags); rv = test_bit(BTUSB_SUSPENDING, &data->flags); if (!rv) data->tx_in_flight++; spin_unlock_irqrestore(&data->txlock, flags); return rv; } static void btusb_intr_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); if (!test_bit(HCI_RUNNING, &hdev->flags)) return; if (urb->status == 0) { hdev->stat.byte_rx += urb->actual_length; if (hci_recv_fragment(hdev, HCI_EVENT_PKT, urb->transfer_buffer, urb->actual_length) < 0) { BT_ERR("%s corrupted event packet", hdev->name); hdev->stat.err_rx++; } } if (!test_bit(BTUSB_INTR_RUNNING, &data->flags)) return; usb_mark_last_busy(data->udev); usb_anchor_urb(urb, &data->intr_anchor); err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { /* -EPERM: urb is being killed; * -ENODEV: device got disconnected */ if (err != -EPERM && err != -ENODEV) BT_ERR("%s urb %p failed to resubmit (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } } static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) { struct btusb_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; int err, size; BT_DBG("%s", hdev->name); if (!data->intr_ep) return -ENODEV; urb = usb_alloc_urb(0, mem_flags); if (!urb) return -ENOMEM; size = le16_to_cpu(data->intr_ep->wMaxPacketSize); buf = kmalloc(size, mem_flags); if (!buf) { usb_free_urb(urb); return -ENOMEM; } pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress); usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_intr_complete, hdev, data->intr_ep->bInterval); urb->transfer_flags |= URB_FREE_BUFFER; usb_anchor_urb(urb, &data->intr_anchor); err = usb_submit_urb(urb, mem_flags); if (err < 0) { if (err != -EPERM && err != -ENODEV) BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } usb_free_urb(urb); return err; } static void btusb_bulk_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); if (!test_bit(HCI_RUNNING, &hdev->flags)) return; if (urb->status == 0) { hdev->stat.byte_rx += urb->actual_length; if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT, urb->transfer_buffer, urb->actual_length) < 0) { BT_ERR("%s corrupted ACL packet", hdev->name); hdev->stat.err_rx++; } } if (!test_bit(BTUSB_BULK_RUNNING, &data->flags)) return; usb_anchor_urb(urb, &data->bulk_anchor); usb_mark_last_busy(data->udev); err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { /* -EPERM: urb is being killed; * -ENODEV: device got disconnected */ if (err != -EPERM && err != -ENODEV) BT_ERR("%s urb %p failed to resubmit (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } } static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) { struct btusb_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; int err, size = HCI_MAX_FRAME_SIZE; BT_DBG("%s", hdev->name); if (!data->bulk_rx_ep) return -ENODEV; urb = usb_alloc_urb(0, mem_flags); if (!urb) return -ENOMEM; buf = kmalloc(size, mem_flags); if (!buf) { usb_free_urb(urb); return -ENOMEM; } pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress); usb_fill_bulk_urb(urb, data->udev, pipe, buf, size, btusb_bulk_complete, hdev); urb->transfer_flags |= URB_FREE_BUFFER; usb_mark_last_busy(data->udev); usb_anchor_urb(urb, &data->bulk_anchor); err = usb_submit_urb(urb, mem_flags); if (err < 0) { if (err != -EPERM && err != -ENODEV) BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } usb_free_urb(urb); return err; } static void btusb_isoc_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; struct btusb_data *data = hci_get_drvdata(hdev); int i, err; BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); if (!test_bit(HCI_RUNNING, &hdev->flags)) return; if (urb->status == 0) { for (i = 0; i < urb->number_of_packets; i++) { unsigned int offset = urb->iso_frame_desc[i].offset; unsigned int length = urb->iso_frame_desc[i].actual_length; if (urb->iso_frame_desc[i].status) continue; hdev->stat.byte_rx += length; if (hci_recv_fragment(hdev, HCI_SCODATA_PKT, urb->transfer_buffer + offset, length) < 0) { BT_ERR("%s corrupted SCO packet", hdev->name); hdev->stat.err_rx++; } } } if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags)) return; usb_anchor_urb(urb, &data->isoc_anchor); err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { /* -EPERM: urb is being killed; * -ENODEV: device got disconnected */ if (err != -EPERM && err != -ENODEV) BT_ERR("%s urb %p failed to resubmit (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } } static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu) { int i, offset = 0; BT_DBG("len %d mtu %d", len, mtu); for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) { urb->iso_frame_desc[i].offset = offset; urb->iso_frame_desc[i].length = mtu; } if (len && i < BTUSB_MAX_ISOC_FRAMES) { urb->iso_frame_desc[i].offset = offset; urb->iso_frame_desc[i].length = len; i++; } urb->number_of_packets = i; } static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) { struct btusb_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; int err, size; BT_DBG("%s", hdev->name); if (!data->isoc_rx_ep) return -ENODEV; urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags); if (!urb) return -ENOMEM; size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) * BTUSB_MAX_ISOC_FRAMES; buf = kmalloc(size, mem_flags); if (!buf) { usb_free_urb(urb); return -ENOMEM; } pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress); usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete, hdev, data->isoc_rx_ep->bInterval); urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP; __fill_isoc_descriptor(urb, size, le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize)); usb_anchor_urb(urb, &data->isoc_anchor); err = usb_submit_urb(urb, mem_flags); if (err < 0) { if (err != -EPERM && err != -ENODEV) BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } usb_free_urb(urb); return err; } static void btusb_tx_complete(struct urb *urb) { struct sk_buff *skb = urb->context; struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct btusb_data *data = hci_get_drvdata(hdev); BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); if (!test_bit(HCI_RUNNING, &hdev->flags)) goto done; if (!urb->status) hdev->stat.byte_tx += urb->transfer_buffer_length; else hdev->stat.err_tx++; done: spin_lock(&data->txlock); data->tx_in_flight--; spin_unlock(&data->txlock); kfree(urb->setup_packet); kfree_skb(skb); } static void btusb_isoc_tx_complete(struct urb *urb) { struct sk_buff *skb = urb->context; struct hci_dev *hdev = (struct hci_dev *) skb->dev; BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); if (!test_bit(HCI_RUNNING, &hdev->flags)) goto done; if (!urb->status) hdev->stat.byte_tx += urb->transfer_buffer_length; else hdev->stat.err_tx++; done: kfree(urb->setup_packet); kfree_skb(skb); } static int btusb_open(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); err = usb_autopm_get_interface(data->intf); if (err < 0) return err; data->intf->needs_remote_wakeup = 1; if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) goto done; if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) goto done; err = btusb_submit_intr_urb(hdev, GFP_KERNEL); if (err < 0) goto failed; err = btusb_submit_bulk_urb(hdev, GFP_KERNEL); if (err < 0) { usb_kill_anchored_urbs(&data->intr_anchor); goto failed; } set_bit(BTUSB_BULK_RUNNING, &data->flags); btusb_submit_bulk_urb(hdev, GFP_KERNEL); done: usb_autopm_put_interface(data->intf); return 0; failed: clear_bit(BTUSB_INTR_RUNNING, &data->flags); clear_bit(HCI_RUNNING, &hdev->flags); usb_autopm_put_interface(data->intf); return err; } static void btusb_stop_traffic(struct btusb_data *data) { usb_kill_anchored_urbs(&data->intr_anchor); usb_kill_anchored_urbs(&data->bulk_anchor); usb_kill_anchored_urbs(&data->isoc_anchor); } static int btusb_close(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; cancel_work_sync(&data->work); cancel_work_sync(&data->waker); clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_INTR_RUNNING, &data->flags); btusb_stop_traffic(data); err = usb_autopm_get_interface(data->intf); if (err < 0) goto failed; data->intf->needs_remote_wakeup = 0; usb_autopm_put_interface(data->intf); failed: usb_scuttle_anchored_urbs(&data->deferred); return 0; } static int btusb_flush(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); usb_kill_anchored_urbs(&data->tx_anchor); return 0; } static int btusb_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct btusb_data *data = hci_get_drvdata(hdev); struct usb_ctrlrequest *dr; struct urb *urb; unsigned int pipe; int err; BT_DBG("%s", hdev->name); if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) return -ENOMEM; dr = kmalloc(sizeof(*dr), GFP_ATOMIC); if (!dr) { usb_free_urb(urb); return -ENOMEM; } dr->bRequestType = data->cmdreq_type; dr->bRequest = 0; dr->wIndex = 0; dr->wValue = 0; dr->wLength = __cpu_to_le16(skb->len); pipe = usb_sndctrlpipe(data->udev, 0x00); usb_fill_control_urb(urb, data->udev, pipe, (void *) dr, skb->data, skb->len, btusb_tx_complete, skb); hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: if (!data->bulk_tx_ep) return -ENODEV; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) return -ENOMEM; pipe = usb_sndbulkpipe(data->udev, data->bulk_tx_ep->bEndpointAddress); usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len, btusb_tx_complete, skb); hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1) return -ENODEV; urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC); if (!urb) return -ENOMEM; pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress); usb_fill_int_urb(urb, data->udev, pipe, skb->data, skb->len, btusb_isoc_tx_complete, skb, data->isoc_tx_ep->bInterval); urb->transfer_flags = URB_ISO_ASAP; __fill_isoc_descriptor(urb, skb->len, le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize)); hdev->stat.sco_tx++; goto skip_waking; default: return -EILSEQ; } err = inc_tx(data); if (err) { usb_anchor_urb(urb, &data->deferred); schedule_work(&data->waker); err = 0; goto done; } skip_waking: usb_anchor_urb(urb, &data->tx_anchor); err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { if (err != -EPERM && err != -ENODEV) BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); kfree(urb->setup_packet); usb_unanchor_urb(urb); } else { usb_mark_last_busy(data->udev); } done: usb_free_urb(urb); return err; } static void btusb_notify(struct hci_dev *hdev, unsigned int evt) { struct btusb_data *data = hci_get_drvdata(hdev); BT_DBG("%s evt %d", hdev->name, evt); if (hdev->conn_hash.sco_num != data->sco_num) { data->sco_num = hdev->conn_hash.sco_num; schedule_work(&data->work); } } static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) { struct btusb_data *data = hci_get_drvdata(hdev); struct usb_interface *intf = data->isoc; struct usb_endpoint_descriptor *ep_desc; int i, err; if (!data->isoc) return -ENODEV; err = usb_set_interface(data->udev, 1, altsetting); if (err < 0) { BT_ERR("%s setting interface failed (%d)", hdev->name, -err); return err; } data->isoc_altsetting = altsetting; data->isoc_tx_ep = NULL; data->isoc_rx_ep = NULL; for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { ep_desc = &intf->cur_altsetting->endpoint[i].desc; if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) { data->isoc_tx_ep = ep_desc; continue; } if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) { data->isoc_rx_ep = ep_desc; continue; } } if (!data->isoc_tx_ep || !data->isoc_rx_ep) { BT_ERR("%s invalid SCO descriptors", hdev->name); return -ENODEV; } return 0; } static void btusb_work(struct work_struct *work) { struct btusb_data *data = container_of(work, struct btusb_data, work); struct hci_dev *hdev = data->hdev; int new_alts; int err; if (hdev->conn_hash.sco_num > 0) { if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) { err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf); if (err < 0) { clear_bit(BTUSB_ISOC_RUNNING, &data->flags); usb_kill_anchored_urbs(&data->isoc_anchor); return; } set_bit(BTUSB_DID_ISO_RESUME, &data->flags); } if (hdev->voice_setting & 0x0020) { static const int alts[3] = { 2, 4, 5 }; new_alts = alts[hdev->conn_hash.sco_num - 1]; } else { new_alts = hdev->conn_hash.sco_num; } if (data->isoc_altsetting != new_alts) { clear_bit(BTUSB_ISOC_RUNNING, &data->flags); usb_kill_anchored_urbs(&data->isoc_anchor); if (__set_isoc_interface(hdev, new_alts) < 0) return; } if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) { if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0) clear_bit(BTUSB_ISOC_RUNNING, &data->flags); else btusb_submit_isoc_urb(hdev, GFP_KERNEL); } } else { clear_bit(BTUSB_ISOC_RUNNING, &data->flags); usb_kill_anchored_urbs(&data->isoc_anchor); __set_isoc_interface(hdev, 0); if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags)) usb_autopm_put_interface(data->isoc ? data->isoc : data->intf); } } static void btusb_waker(struct work_struct *work) { struct btusb_data *data = container_of(work, struct btusb_data, waker); int err; err = usb_autopm_get_interface(data->intf); if (err < 0) return; usb_autopm_put_interface(data->intf); } static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_endpoint_descriptor *ep_desc; struct btusb_data *data; struct hci_dev *hdev; int i, err; BT_DBG("intf %p id %p", intf, id); /* interface numbers are hardcoded in the spec */ if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; if (!id->driver_info) { const struct usb_device_id *match; match = usb_match_id(intf, blacklist_table); if (match) id = match; } if (id->driver_info == BTUSB_IGNORE) return -ENODEV; if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER) return -ENODEV; if (ignore_csr && id->driver_info & BTUSB_CSR) return -ENODEV; if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER) return -ENODEV; if (id->driver_info & BTUSB_ATH3012) { struct usb_device *udev = interface_to_usbdev(intf); /* Old firmware would otherwise let ath3k driver load * patch and sysconfig files */ if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001) return -ENODEV; } data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { ep_desc = &intf->cur_altsetting->endpoint[i].desc; if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) { data->intr_ep = ep_desc; continue; } if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) { data->bulk_tx_ep = ep_desc; continue; } if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) { data->bulk_rx_ep = ep_desc; continue; } } if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) return -ENODEV; data->cmdreq_type = USB_TYPE_CLASS; data->udev = interface_to_usbdev(intf); data->intf = intf; spin_lock_init(&data->lock); INIT_WORK(&data->work, btusb_work); INIT_WORK(&data->waker, btusb_waker); spin_lock_init(&data->txlock); init_usb_anchor(&data->tx_anchor); init_usb_anchor(&data->intr_anchor); init_usb_anchor(&data->bulk_anchor); init_usb_anchor(&data->isoc_anchor); init_usb_anchor(&data->deferred); hdev = hci_alloc_dev(); if (!hdev) return -ENOMEM; hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); data->hdev = hdev; SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = btusb_open; hdev->close = btusb_close; hdev->flush = btusb_flush; hdev->send = btusb_send_frame; hdev->notify = btusb_notify; /* Interface numbers are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, 1); if (!reset) set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) { if (!disable_scofix) set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks); } if (id->driver_info & BTUSB_BROKEN_ISOC) data->isoc = NULL; if (id->driver_info & BTUSB_DIGIANSWER) { data->cmdreq_type = USB_TYPE_VENDOR; set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); } if (id->driver_info & BTUSB_CSR) { struct usb_device *udev = data->udev; /* Old firmware would otherwise execute USB reset */ if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117) set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); } if (id->driver_info & BTUSB_SNIFFER) { struct usb_device *udev = data->udev; /* New sniffer firmware has crippled HCI interface */ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); data->isoc = NULL; } if (id->driver_info & BTUSB_BCM92035) { unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 }; struct sk_buff *skb; skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL); if (skb) { memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd)); skb_queue_tail(&hdev->driver_init, skb); } } if (data->isoc) { err = usb_driver_claim_interface(&btusb_driver, data->isoc, data); if (err < 0) { hci_free_dev(hdev); return err; } } err = hci_register_dev(hdev); if (err < 0) { hci_free_dev(hdev); return err; } usb_set_intfdata(intf, data); return 0; } static void btusb_disconnect(struct usb_interface *intf) { struct btusb_data *data = usb_get_intfdata(intf); struct hci_dev *hdev; BT_DBG("intf %p", intf); if (!data) return; hdev = data->hdev; usb_set_intfdata(data->intf, NULL); if (data->isoc) usb_set_intfdata(data->isoc, NULL); hci_unregister_dev(hdev); if (intf == data->isoc) usb_driver_release_interface(&btusb_driver, data->intf); else if (data->isoc) usb_driver_release_interface(&btusb_driver, data->isoc); hci_free_dev(hdev); } #ifdef CONFIG_PM static int btusb_suspend(struct usb_interface *intf, pm_message_t message) { struct btusb_data *data = usb_get_intfdata(intf); BT_DBG("intf %p", intf); if (data->suspend_count++) return 0; spin_lock_irq(&data->txlock); if (!(PMSG_IS_AUTO(message) && data->tx_in_flight)) { set_bit(BTUSB_SUSPENDING, &data->flags); spin_unlock_irq(&data->txlock); } else { spin_unlock_irq(&data->txlock); data->suspend_count--; return -EBUSY; } cancel_work_sync(&data->work); btusb_stop_traffic(data); usb_kill_anchored_urbs(&data->tx_anchor); return 0; } static void play_deferred(struct btusb_data *data) { struct urb *urb; int err; while ((urb = usb_get_from_anchor(&data->deferred))) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) break; data->tx_in_flight++; } usb_scuttle_anchored_urbs(&data->deferred); } static int btusb_resume(struct usb_interface *intf) { struct btusb_data *data = usb_get_intfdata(intf); struct hci_dev *hdev = data->hdev; int err = 0; BT_DBG("intf %p", intf); if (--data->suspend_count) return 0; if (!test_bit(HCI_RUNNING, &hdev->flags)) goto done; if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) { err = btusb_submit_intr_urb(hdev, GFP_NOIO); if (err < 0) { clear_bit(BTUSB_INTR_RUNNING, &data->flags); goto failed; } } if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) { err = btusb_submit_bulk_urb(hdev, GFP_NOIO); if (err < 0) { clear_bit(BTUSB_BULK_RUNNING, &data->flags); goto failed; } btusb_submit_bulk_urb(hdev, GFP_NOIO); } if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) { if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0) clear_bit(BTUSB_ISOC_RUNNING, &data->flags); else btusb_submit_isoc_urb(hdev, GFP_NOIO); } spin_lock_irq(&data->txlock); play_deferred(data); clear_bit(BTUSB_SUSPENDING, &data->flags); spin_unlock_irq(&data->txlock); schedule_work(&data->work); return 0; failed: usb_scuttle_anchored_urbs(&data->deferred); done: spin_lock_irq(&data->txlock); clear_bit(BTUSB_SUSPENDING, &data->flags); spin_unlock_irq(&data->txlock); return err; } #endif static struct usb_driver btusb_driver = { .name = "btusb", .probe = btusb_probe, .disconnect = btusb_disconnect, #ifdef CONFIG_PM .suspend = btusb_suspend, .resume = btusb_resume, #endif .id_table = btusb_table, .supports_autosuspend = 1, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(btusb_driver); module_param(ignore_dga, bool, 0644); MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001"); module_param(ignore_csr, bool, 0644); MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001"); module_param(ignore_sniffer, bool, 0644); MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002"); module_param(disable_scofix, bool, 0644); MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size"); module_param(force_scofix, bool, 0644); MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size"); module_param(reset, bool, 0644); MODULE_PARM_DESC(reset, "Send HCI reset command on initialization"); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/bluetooth/bpa10x.c0000644000175000017500000002457412026211315021316 0ustar mcgrofmcgrof/* * * Digianswer Bluetooth USB driver * * Copyright (C) 2004-2007 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #define VERSION "0.10" static struct usb_device_id bpa10x_table[] = { /* Tektronix BPA 100/105 (Digianswer) */ { USB_DEVICE(0x08fd, 0x0002) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, bpa10x_table); struct bpa10x_data { struct hci_dev *hdev; struct usb_device *udev; struct usb_anchor tx_anchor; struct usb_anchor rx_anchor; struct sk_buff *rx_skb[2]; }; #define HCI_VENDOR_HDR_SIZE 5 struct hci_vendor_hdr { __u8 type; __le16 snum; __le16 dlen; } __packed; static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) { struct bpa10x_data *data = hci_get_drvdata(hdev); BT_DBG("%s queue %d buffer %p count %d", hdev->name, queue, buf, count); if (queue < 0 || queue > 1) return -EILSEQ; hdev->stat.byte_rx += count; while (count) { struct sk_buff *skb = data->rx_skb[queue]; struct { __u8 type; int expect; } *scb; int type, len = 0; if (!skb) { /* Start of the frame */ type = *((__u8 *) buf); count--; buf++; switch (type) { case HCI_EVENT_PKT: if (count >= HCI_EVENT_HDR_SIZE) { struct hci_event_hdr *h = buf; len = HCI_EVENT_HDR_SIZE + h->plen; } else return -EILSEQ; break; case HCI_ACLDATA_PKT: if (count >= HCI_ACL_HDR_SIZE) { struct hci_acl_hdr *h = buf; len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen); } else return -EILSEQ; break; case HCI_SCODATA_PKT: if (count >= HCI_SCO_HDR_SIZE) { struct hci_sco_hdr *h = buf; len = HCI_SCO_HDR_SIZE + h->dlen; } else return -EILSEQ; break; case HCI_VENDOR_PKT: if (count >= HCI_VENDOR_HDR_SIZE) { struct hci_vendor_hdr *h = buf; len = HCI_VENDOR_HDR_SIZE + __le16_to_cpu(h->dlen); } else return -EILSEQ; break; } skb = bt_skb_alloc(len, GFP_ATOMIC); if (!skb) { BT_ERR("%s no memory for packet", hdev->name); return -ENOMEM; } skb->dev = (void *) hdev; data->rx_skb[queue] = skb; scb = (void *) skb->cb; scb->type = type; scb->expect = len; } else { /* Continuation */ scb = (void *) skb->cb; len = scb->expect; } len = min(len, count); memcpy(skb_put(skb, len), buf, len); scb->expect -= len; if (scb->expect == 0) { /* Complete frame */ data->rx_skb[queue] = NULL; bt_cb(skb)->pkt_type = scb->type; hci_recv_frame(skb); } count -= len; buf += len; } return 0; } static void bpa10x_tx_complete(struct urb *urb) { struct sk_buff *skb = urb->context; struct hci_dev *hdev = (struct hci_dev *) skb->dev; BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); if (!test_bit(HCI_RUNNING, &hdev->flags)) goto done; if (!urb->status) hdev->stat.byte_tx += urb->transfer_buffer_length; else hdev->stat.err_tx++; done: kfree(urb->setup_packet); kfree_skb(skb); } static void bpa10x_rx_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; struct bpa10x_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); if (!test_bit(HCI_RUNNING, &hdev->flags)) return; if (urb->status == 0) { if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe), urb->transfer_buffer, urb->actual_length) < 0) { BT_ERR("%s corrupted event packet", hdev->name); hdev->stat.err_rx++; } } usb_anchor_urb(urb, &data->rx_anchor); err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { BT_ERR("%s urb %p failed to resubmit (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } } static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev) { struct bpa10x_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; int err, size = 16; BT_DBG("%s", hdev->name); urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; buf = kmalloc(size, GFP_KERNEL); if (!buf) { usb_free_urb(urb); return -ENOMEM; } pipe = usb_rcvintpipe(data->udev, 0x81); usb_fill_int_urb(urb, data->udev, pipe, buf, size, bpa10x_rx_complete, hdev, 1); urb->transfer_flags |= URB_FREE_BUFFER; usb_anchor_urb(urb, &data->rx_anchor); err = usb_submit_urb(urb, GFP_KERNEL); if (err < 0) { BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } usb_free_urb(urb); return err; } static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev) { struct bpa10x_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; int err, size = 64; BT_DBG("%s", hdev->name); urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; buf = kmalloc(size, GFP_KERNEL); if (!buf) { usb_free_urb(urb); return -ENOMEM; } pipe = usb_rcvbulkpipe(data->udev, 0x82); usb_fill_bulk_urb(urb, data->udev, pipe, buf, size, bpa10x_rx_complete, hdev); urb->transfer_flags |= URB_FREE_BUFFER; usb_anchor_urb(urb, &data->rx_anchor); err = usb_submit_urb(urb, GFP_KERNEL); if (err < 0) { BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } usb_free_urb(urb); return err; } static int bpa10x_open(struct hci_dev *hdev) { struct bpa10x_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) return 0; err = bpa10x_submit_intr_urb(hdev); if (err < 0) goto error; err = bpa10x_submit_bulk_urb(hdev); if (err < 0) goto error; return 0; error: usb_kill_anchored_urbs(&data->rx_anchor); clear_bit(HCI_RUNNING, &hdev->flags); return err; } static int bpa10x_close(struct hci_dev *hdev) { struct bpa10x_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; usb_kill_anchored_urbs(&data->rx_anchor); return 0; } static int bpa10x_flush(struct hci_dev *hdev) { struct bpa10x_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); usb_kill_anchored_urbs(&data->tx_anchor); return 0; } static int bpa10x_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct bpa10x_data *data = hci_get_drvdata(hdev); struct usb_ctrlrequest *dr; struct urb *urb; unsigned int pipe; int err; BT_DBG("%s", hdev->name); if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) return -ENOMEM; /* Prepend skb with frame type */ *skb_push(skb, 1) = bt_cb(skb)->pkt_type; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: dr = kmalloc(sizeof(*dr), GFP_ATOMIC); if (!dr) { usb_free_urb(urb); return -ENOMEM; } dr->bRequestType = USB_TYPE_VENDOR; dr->bRequest = 0; dr->wIndex = 0; dr->wValue = 0; dr->wLength = __cpu_to_le16(skb->len); pipe = usb_sndctrlpipe(data->udev, 0x00); usb_fill_control_urb(urb, data->udev, pipe, (void *) dr, skb->data, skb->len, bpa10x_tx_complete, skb); hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: pipe = usb_sndbulkpipe(data->udev, 0x02); usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len, bpa10x_tx_complete, skb); hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: pipe = usb_sndbulkpipe(data->udev, 0x02); usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len, bpa10x_tx_complete, skb); hdev->stat.sco_tx++; break; default: usb_free_urb(urb); return -EILSEQ; } usb_anchor_urb(urb, &data->tx_anchor); err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { BT_ERR("%s urb %p submission failed", hdev->name, urb); kfree(urb->setup_packet); usb_unanchor_urb(urb); } usb_free_urb(urb); return 0; } static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct bpa10x_data *data; struct hci_dev *hdev; int err; BT_DBG("intf %p id %p", intf, id); if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->udev = interface_to_usbdev(intf); init_usb_anchor(&data->tx_anchor); init_usb_anchor(&data->rx_anchor); hdev = hci_alloc_dev(); if (!hdev) return -ENOMEM; hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); data->hdev = hdev; SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = bpa10x_open; hdev->close = bpa10x_close; hdev->flush = bpa10x_flush; hdev->send = bpa10x_send_frame; set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); err = hci_register_dev(hdev); if (err < 0) { hci_free_dev(hdev); return err; } usb_set_intfdata(intf, data); return 0; } static void bpa10x_disconnect(struct usb_interface *intf) { struct bpa10x_data *data = usb_get_intfdata(intf); BT_DBG("intf %p", intf); if (!data) return; usb_set_intfdata(intf, NULL); hci_unregister_dev(data->hdev); hci_free_dev(data->hdev); kfree_skb(data->rx_skb[0]); kfree_skb(data->rx_skb[1]); } static struct usb_driver bpa10x_driver = { .name = "bpa10x", .probe = bpa10x_probe, .disconnect = bpa10x_disconnect, .id_table = bpa10x_table, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(bpa10x_driver); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/bluetooth/bfusb.c0000644000175000017500000004025212026211315021313 0ustar mcgrofmcgrof/* * * AVM BlueFRITZ! USB driver * * Copyright (C) 2003-2006 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "1.2" static struct usb_driver bfusb_driver; static struct usb_device_id bfusb_table[] = { /* AVM BlueFRITZ! USB */ { USB_DEVICE(0x057c, 0x2200) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, bfusb_table); #define BFUSB_MAX_BLOCK_SIZE 256 #define BFUSB_BLOCK_TIMEOUT 3000 #define BFUSB_TX_PROCESS 1 #define BFUSB_TX_WAKEUP 2 #define BFUSB_MAX_BULK_TX 2 #define BFUSB_MAX_BULK_RX 2 struct bfusb_data { struct hci_dev *hdev; unsigned long state; struct usb_device *udev; unsigned int bulk_in_ep; unsigned int bulk_out_ep; unsigned int bulk_pkt_size; rwlock_t lock; struct sk_buff_head transmit_q; struct sk_buff *reassembly; atomic_t pending_tx; struct sk_buff_head pending_q; struct sk_buff_head completed_q; }; struct bfusb_data_scb { struct urb *urb; }; static void bfusb_tx_complete(struct urb *urb); static void bfusb_rx_complete(struct urb *urb); static struct urb *bfusb_get_completed(struct bfusb_data *data) { struct sk_buff *skb; struct urb *urb = NULL; BT_DBG("bfusb %p", data); skb = skb_dequeue(&data->completed_q); if (skb) { urb = ((struct bfusb_data_scb *) skb->cb)->urb; kfree_skb(skb); } return urb; } static void bfusb_unlink_urbs(struct bfusb_data *data) { struct sk_buff *skb; struct urb *urb; BT_DBG("bfusb %p", data); while ((skb = skb_dequeue(&data->pending_q))) { urb = ((struct bfusb_data_scb *) skb->cb)->urb; usb_kill_urb(urb); skb_queue_tail(&data->completed_q, skb); } while ((urb = bfusb_get_completed(data))) usb_free_urb(urb); } static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb) { struct bfusb_data_scb *scb = (void *) skb->cb; struct urb *urb = bfusb_get_completed(data); int err, pipe; BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len); if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) return -ENOMEM; pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep); usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len, bfusb_tx_complete, skb); scb->urb = urb; skb_queue_tail(&data->pending_q, skb); err = usb_submit_urb(urb, GFP_ATOMIC); if (err) { BT_ERR("%s bulk tx submit failed urb %p err %d", data->hdev->name, urb, err); skb_unlink(skb, &data->pending_q); usb_free_urb(urb); } else atomic_inc(&data->pending_tx); return err; } static void bfusb_tx_wakeup(struct bfusb_data *data) { struct sk_buff *skb; BT_DBG("bfusb %p", data); if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) { set_bit(BFUSB_TX_WAKEUP, &data->state); return; } do { clear_bit(BFUSB_TX_WAKEUP, &data->state); while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) && (skb = skb_dequeue(&data->transmit_q))) { if (bfusb_send_bulk(data, skb) < 0) { skb_queue_head(&data->transmit_q, skb); break; } } } while (test_bit(BFUSB_TX_WAKEUP, &data->state)); clear_bit(BFUSB_TX_PROCESS, &data->state); } static void bfusb_tx_complete(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct bfusb_data *data = (struct bfusb_data *) skb->dev; BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len); atomic_dec(&data->pending_tx); if (!test_bit(HCI_RUNNING, &data->hdev->flags)) return; if (!urb->status) data->hdev->stat.byte_tx += skb->len; else data->hdev->stat.err_tx++; read_lock(&data->lock); skb_unlink(skb, &data->pending_q); skb_queue_tail(&data->completed_q, skb); bfusb_tx_wakeup(data); read_unlock(&data->lock); } static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb) { struct bfusb_data_scb *scb; struct sk_buff *skb; int err, pipe, size = HCI_MAX_FRAME_SIZE + 32; BT_DBG("bfusb %p urb %p", data, urb); if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) return -ENOMEM; skb = bt_skb_alloc(size, GFP_ATOMIC); if (!skb) { usb_free_urb(urb); return -ENOMEM; } skb->dev = (void *) data; scb = (struct bfusb_data_scb *) skb->cb; scb->urb = urb; pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep); usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size, bfusb_rx_complete, skb); skb_queue_tail(&data->pending_q, skb); err = usb_submit_urb(urb, GFP_ATOMIC); if (err) { BT_ERR("%s bulk rx submit failed urb %p err %d", data->hdev->name, urb, err); skb_unlink(skb, &data->pending_q); kfree_skb(skb); usb_free_urb(urb); } return err; } static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len) { BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len); if (hdr & 0x10) { BT_ERR("%s error in block", data->hdev->name); kfree_skb(data->reassembly); data->reassembly = NULL; return -EIO; } if (hdr & 0x04) { struct sk_buff *skb; unsigned char pkt_type; int pkt_len = 0; if (data->reassembly) { BT_ERR("%s unexpected start block", data->hdev->name); kfree_skb(data->reassembly); data->reassembly = NULL; } if (len < 1) { BT_ERR("%s no packet type found", data->hdev->name); return -EPROTO; } pkt_type = *buf++; len--; switch (pkt_type) { case HCI_EVENT_PKT: if (len >= HCI_EVENT_HDR_SIZE) { struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf; pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen; } else { BT_ERR("%s event block is too short", data->hdev->name); return -EILSEQ; } break; case HCI_ACLDATA_PKT: if (len >= HCI_ACL_HDR_SIZE) { struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf; pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen); } else { BT_ERR("%s data block is too short", data->hdev->name); return -EILSEQ; } break; case HCI_SCODATA_PKT: if (len >= HCI_SCO_HDR_SIZE) { struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf; pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen; } else { BT_ERR("%s audio block is too short", data->hdev->name); return -EILSEQ; } break; } skb = bt_skb_alloc(pkt_len, GFP_ATOMIC); if (!skb) { BT_ERR("%s no memory for the packet", data->hdev->name); return -ENOMEM; } skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = pkt_type; data->reassembly = skb; } else { if (!data->reassembly) { BT_ERR("%s unexpected continuation block", data->hdev->name); return -EIO; } } if (len > 0) memcpy(skb_put(data->reassembly, len), buf, len); if (hdr & 0x08) { hci_recv_frame(data->reassembly); data->reassembly = NULL; } return 0; } static void bfusb_rx_complete(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct bfusb_data *data = (struct bfusb_data *) skb->dev; unsigned char *buf = urb->transfer_buffer; int count = urb->actual_length; int err, hdr, len; BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len); read_lock(&data->lock); if (!test_bit(HCI_RUNNING, &data->hdev->flags)) goto unlock; if (urb->status || !count) goto resubmit; data->hdev->stat.byte_rx += count; skb_put(skb, count); while (count) { hdr = buf[0] | (buf[1] << 8); if (hdr & 0x4000) { len = 0; count -= 2; buf += 2; } else { len = (buf[2] == 0) ? 256 : buf[2]; count -= 3; buf += 3; } if (count < len) { BT_ERR("%s block extends over URB buffer ranges", data->hdev->name); } if ((hdr & 0xe1) == 0xc1) bfusb_recv_block(data, hdr, buf, len); count -= len; buf += len; } skb_unlink(skb, &data->pending_q); kfree_skb(skb); bfusb_rx_submit(data, urb); read_unlock(&data->lock); return; resubmit: urb->dev = data->udev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) { BT_ERR("%s bulk resubmit failed urb %p err %d", data->hdev->name, urb, err); } unlock: read_unlock(&data->lock); } static int bfusb_open(struct hci_dev *hdev) { struct bfusb_data *data = hci_get_drvdata(hdev); unsigned long flags; int i, err; BT_DBG("hdev %p bfusb %p", hdev, data); if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) return 0; write_lock_irqsave(&data->lock, flags); err = bfusb_rx_submit(data, NULL); if (!err) { for (i = 1; i < BFUSB_MAX_BULK_RX; i++) bfusb_rx_submit(data, NULL); } else { clear_bit(HCI_RUNNING, &hdev->flags); } write_unlock_irqrestore(&data->lock, flags); return err; } static int bfusb_flush(struct hci_dev *hdev) { struct bfusb_data *data = hci_get_drvdata(hdev); BT_DBG("hdev %p bfusb %p", hdev, data); skb_queue_purge(&data->transmit_q); return 0; } static int bfusb_close(struct hci_dev *hdev) { struct bfusb_data *data = hci_get_drvdata(hdev); unsigned long flags; BT_DBG("hdev %p bfusb %p", hdev, data); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; write_lock_irqsave(&data->lock, flags); write_unlock_irqrestore(&data->lock, flags); bfusb_unlink_urbs(data); bfusb_flush(hdev); return 0; } static int bfusb_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct bfusb_data *data; struct sk_buff *nskb; unsigned char buf[3]; int sent = 0, size, count; BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len); if (!hdev) { BT_ERR("Frame for unknown HCI device (hdev=NULL)"); return -ENODEV; } if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; data = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; }; /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); count = skb->len; /* Max HCI frame size seems to be 1511 + 1 */ nskb = bt_skb_alloc(count + 32, GFP_ATOMIC); if (!nskb) { BT_ERR("Can't allocate memory for new packet"); return -ENOMEM; } nskb->dev = (void *) data; while (count) { size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE); buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0); buf[1] = 0x00; buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size; memcpy(skb_put(nskb, 3), buf, 3); skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size); sent += size; count -= size; } /* Don't send frame with multiple size of bulk max packet */ if ((nskb->len % data->bulk_pkt_size) == 0) { buf[0] = 0xdd; buf[1] = 0x00; memcpy(skb_put(nskb, 2), buf, 2); } read_lock(&data->lock); skb_queue_tail(&data->transmit_q, nskb); bfusb_tx_wakeup(data); read_unlock(&data->lock); kfree_skb(skb); return 0; } static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } static int bfusb_load_firmware(struct bfusb_data *data, const unsigned char *firmware, int count) { unsigned char *buf; int err, pipe, len, size, sent = 0; BT_DBG("bfusb %p udev %p", data, data->udev); BT_INFO("BlueFRITZ! USB loading firmware"); buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL); if (!buf) { BT_ERR("Can't allocate memory chunk for firmware"); return -ENOMEM; } pipe = usb_sndctrlpipe(data->udev, 0); if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION, 0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) { BT_ERR("Can't change to loading configuration"); kfree(buf); return -EBUSY; } data->udev->toggle[0] = data->udev->toggle[1] = 0; pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep); while (count) { size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3); memcpy(buf, firmware + sent, size); err = usb_bulk_msg(data->udev, pipe, buf, size, &len, BFUSB_BLOCK_TIMEOUT); if (err || (len != size)) { BT_ERR("Error in firmware loading"); goto error; } sent += size; count -= size; } err = usb_bulk_msg(data->udev, pipe, NULL, 0, &len, BFUSB_BLOCK_TIMEOUT); if (err < 0) { BT_ERR("Error in null packet request"); goto error; } pipe = usb_sndctrlpipe(data->udev, 0); err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION, 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (err < 0) { BT_ERR("Can't change to running configuration"); goto error; } data->udev->toggle[0] = data->udev->toggle[1] = 0; BT_INFO("BlueFRITZ! USB device ready"); kfree(buf); return 0; error: kfree(buf); pipe = usb_sndctrlpipe(data->udev, 0); usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION, 0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); return err; } static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { const struct firmware *firmware; struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_endpoint *bulk_out_ep; struct usb_host_endpoint *bulk_in_ep; struct hci_dev *hdev; struct bfusb_data *data; BT_DBG("intf %p id %p", intf, id); /* Check number of endpoints */ if (intf->cur_altsetting->desc.bNumEndpoints < 2) return -EIO; bulk_out_ep = &intf->cur_altsetting->endpoint[0]; bulk_in_ep = &intf->cur_altsetting->endpoint[1]; if (!bulk_out_ep || !bulk_in_ep) { BT_ERR("Bulk endpoints not found"); goto done; } /* Initialize control structure and load firmware */ data = devm_kzalloc(&intf->dev, sizeof(struct bfusb_data), GFP_KERNEL); if (!data) { BT_ERR("Can't allocate memory for control structure"); goto done; } data->udev = udev; data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress; data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress; data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize); rwlock_init(&data->lock); data->reassembly = NULL; skb_queue_head_init(&data->transmit_q); skb_queue_head_init(&data->pending_q); skb_queue_head_init(&data->completed_q); if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) { BT_ERR("Firmware request failed"); goto done; } BT_DBG("firmware data %p size %zu", firmware->data, firmware->size); if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) { BT_ERR("Firmware loading failed"); goto release; } release_firmware(firmware); /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); goto done; } data->hdev = hdev; hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = bfusb_open; hdev->close = bfusb_close; hdev->flush = bfusb_flush; hdev->send = bfusb_send_frame; hdev->ioctl = bfusb_ioctl; if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); goto done; } usb_set_intfdata(intf, data); return 0; release: release_firmware(firmware); done: return -EIO; } static void bfusb_disconnect(struct usb_interface *intf) { struct bfusb_data *data = usb_get_intfdata(intf); struct hci_dev *hdev = data->hdev; BT_DBG("intf %p", intf); if (!hdev) return; usb_set_intfdata(intf, NULL); bfusb_close(hdev); hci_unregister_dev(hdev); hci_free_dev(hdev); } static struct usb_driver bfusb_driver = { .name = "bfusb", .probe = bfusb_probe, .disconnect = bfusb_disconnect, .id_table = bfusb_table, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(bfusb_driver); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("bfubase.frm"); compat-drivers-2012-09-18/drivers/bluetooth/bcm203x.c0000644000175000017500000001547212026211315021376 0ustar mcgrofmcgrof/* * * Broadcom Blutonium firmware driver * * Copyright (C) 2003 Maxim Krasnyansky * Copyright (C) 2003 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #define VERSION "1.2" static const struct usb_device_id bcm203x_table[] = { /* Broadcom Blutonium (BCM2033) */ { USB_DEVICE(0x0a5c, 0x2033) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, bcm203x_table); #define BCM203X_ERROR 0 #define BCM203X_RESET 1 #define BCM203X_LOAD_MINIDRV 2 #define BCM203X_SELECT_MEMORY 3 #define BCM203X_CHECK_MEMORY 4 #define BCM203X_LOAD_FIRMWARE 5 #define BCM203X_CHECK_FIRMWARE 6 #define BCM203X_IN_EP 0x81 #define BCM203X_OUT_EP 0x02 struct bcm203x_data { struct usb_device *udev; unsigned long state; struct work_struct work; atomic_t shutdown; struct urb *urb; unsigned char *buffer; unsigned char *fw_data; unsigned int fw_size; unsigned int fw_sent; }; static void bcm203x_complete(struct urb *urb) { struct bcm203x_data *data = urb->context; struct usb_device *udev = urb->dev; int len; BT_DBG("udev %p urb %p", udev, urb); if (urb->status) { BT_ERR("URB failed with status %d", urb->status); data->state = BCM203X_ERROR; return; } switch (data->state) { case BCM203X_LOAD_MINIDRV: memcpy(data->buffer, "#", 1); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP), data->buffer, 1, bcm203x_complete, data); data->state = BCM203X_SELECT_MEMORY; /* use workqueue to have a small delay */ schedule_work(&data->work); break; case BCM203X_SELECT_MEMORY: usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP), data->buffer, 32, bcm203x_complete, data, 1); data->state = BCM203X_CHECK_MEMORY; if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) BT_ERR("Can't submit URB"); break; case BCM203X_CHECK_MEMORY: if (data->buffer[0] != '#') { BT_ERR("Memory select failed"); data->state = BCM203X_ERROR; break; } data->state = BCM203X_LOAD_FIRMWARE; case BCM203X_LOAD_FIRMWARE: if (data->fw_sent == data->fw_size) { usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP), data->buffer, 32, bcm203x_complete, data, 1); data->state = BCM203X_CHECK_FIRMWARE; } else { len = min_t(uint, data->fw_size - data->fw_sent, 4096); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP), data->fw_data + data->fw_sent, len, bcm203x_complete, data); data->fw_sent += len; } if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) BT_ERR("Can't submit URB"); break; case BCM203X_CHECK_FIRMWARE: if (data->buffer[0] != '.') { BT_ERR("Firmware loading failed"); data->state = BCM203X_ERROR; break; } data->state = BCM203X_RESET; break; } } static void bcm203x_work(struct work_struct *work) { struct bcm203x_data *data = container_of(work, struct bcm203x_data, work); if (atomic_read(&data->shutdown)) return; if (usb_submit_urb(data->urb, GFP_KERNEL) < 0) BT_ERR("Can't submit URB"); } static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id) { const struct firmware *firmware; struct usb_device *udev = interface_to_usbdev(intf); struct bcm203x_data *data; int size; BT_DBG("intf %p id %p", intf, id); if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); if (!data) { BT_ERR("Can't allocate memory for data structure"); return -ENOMEM; } data->udev = udev; data->state = BCM203X_LOAD_MINIDRV; data->urb = usb_alloc_urb(0, GFP_KERNEL); if (!data->urb) { BT_ERR("Can't allocate URB"); return -ENOMEM; } if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) { BT_ERR("Mini driver request failed"); usb_free_urb(data->urb); return -EIO; } BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size); size = max_t(uint, firmware->size, 4096); data->buffer = kmalloc(size, GFP_KERNEL); if (!data->buffer) { BT_ERR("Can't allocate memory for mini driver"); release_firmware(firmware); usb_free_urb(data->urb); return -ENOMEM; } memcpy(data->buffer, firmware->data, firmware->size); usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP), data->buffer, firmware->size, bcm203x_complete, data); release_firmware(firmware); if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) { BT_ERR("Firmware request failed"); usb_free_urb(data->urb); kfree(data->buffer); return -EIO; } BT_DBG("firmware data %p size %zu", firmware->data, firmware->size); data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL); if (!data->fw_data) { BT_ERR("Can't allocate memory for firmware image"); release_firmware(firmware); usb_free_urb(data->urb); kfree(data->buffer); return -ENOMEM; } data->fw_size = firmware->size; data->fw_sent = 0; release_firmware(firmware); INIT_WORK(&data->work, bcm203x_work); usb_set_intfdata(intf, data); /* use workqueue to have a small delay */ schedule_work(&data->work); return 0; } static void bcm203x_disconnect(struct usb_interface *intf) { struct bcm203x_data *data = usb_get_intfdata(intf); BT_DBG("intf %p", intf); atomic_inc(&data->shutdown); cancel_work_sync(&data->work); usb_kill_urb(data->urb); usb_set_intfdata(intf, NULL); usb_free_urb(data->urb); kfree(data->fw_data); kfree(data->buffer); } static struct usb_driver bcm203x_driver = { .name = "bcm203x", .probe = bcm203x_probe, .disconnect = bcm203x_disconnect, .id_table = bcm203x_table, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(bcm203x_driver); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("BCM2033-MD.hex"); MODULE_FIRMWARE("BCM2033-FW.bin"); compat-drivers-2012-09-18/drivers/bluetooth/ath3k.c0000644000175000017500000002650312026211315021227 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #define VERSION "1.0" #define ATH3K_FIRMWARE "ath3k-1.fw" #define ATH3K_DNLOAD 0x01 #define ATH3K_GETSTATE 0x05 #define ATH3K_SET_NORMAL_MODE 0x07 #define ATH3K_GETVERSION 0x09 #define USB_REG_SWITCH_VID_PID 0x0a #define ATH3K_MODE_MASK 0x3F #define ATH3K_NORMAL_MODE 0x0E #define ATH3K_PATCH_UPDATE 0x80 #define ATH3K_SYSCFG_UPDATE 0x40 #define ATH3K_XTAL_FREQ_26M 0x00 #define ATH3K_XTAL_FREQ_40M 0x01 #define ATH3K_XTAL_FREQ_19P2 0x02 #define ATH3K_NAME_LEN 0xFF struct ath3k_version { unsigned int rom_version; unsigned int build_version; unsigned int ram_version; unsigned char ref_clock; unsigned char reserved[0x07]; }; static struct usb_device_id ath3k_table[] = { /* Atheros AR3011 */ { USB_DEVICE(0x0CF3, 0x3000) }, /* Atheros AR3011 with sflash firmware*/ { USB_DEVICE(0x0CF3, 0x3002) }, { USB_DEVICE(0x0CF3, 0xE019) }, { USB_DEVICE(0x13d3, 0x3304) }, { USB_DEVICE(0x0930, 0x0215) }, { USB_DEVICE(0x0489, 0xE03D) }, /* Atheros AR9285 Malbec with sflash firmware */ { USB_DEVICE(0x03F0, 0x311D) }, /* Atheros AR3012 with sflash firmware*/ { USB_DEVICE(0x0CF3, 0x3004) }, { USB_DEVICE(0x0CF3, 0x311D) }, { USB_DEVICE(0x13d3, 0x3375) }, { USB_DEVICE(0x04CA, 0x3005) }, { USB_DEVICE(0x13d3, 0x3362) }, { USB_DEVICE(0x0CF3, 0xE004) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0489, 0xe057) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, /* Atheros AR5BBU22 with sflash firmware */ { USB_DEVICE(0x0489, 0xE03C) }, { USB_DEVICE(0x0489, 0xE036) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, ath3k_table); #define BTUSB_ATH3012 0x80 /* This table is to load patch and sysconfig files * for AR3012 */ static struct usb_device_id ath3k_blist_tbl[] = { /* Atheros AR3012 with sflash firmware*/ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 }, { } /* Terminating entry */ }; #define USB_REQ_DFU_DNLOAD 1 #define BULK_SIZE 4096 #define FW_HDR_SIZE 20 static int ath3k_load_firmware(struct usb_device *udev, const struct firmware *firmware) { u8 *send_buf; int err, pipe, len, size, sent = 0; int count = firmware->size; BT_DBG("udev %p", udev); pipe = usb_sndctrlpipe(udev, 0); send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); if (!send_buf) { BT_ERR("Can't allocate memory chunk for firmware"); return -ENOMEM; } memcpy(send_buf, firmware->data, 20); if ((err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR, 0, 0, send_buf, 20, USB_CTRL_SET_TIMEOUT)) < 0) { BT_ERR("Can't change to loading configuration err"); goto error; } sent += 20; count -= 20; while (count) { size = min_t(uint, count, BULK_SIZE); pipe = usb_sndbulkpipe(udev, 0x02); memcpy(send_buf, firmware->data + sent, size); err = usb_bulk_msg(udev, pipe, send_buf, size, &len, 3000); if (err || (len != size)) { BT_ERR("Error in firmware loading err = %d," "len = %d, size = %d", err, len, size); goto error; } sent += size; count -= size; } error: kfree(send_buf); return err; } static int ath3k_get_state(struct usb_device *udev, unsigned char *state) { int pipe = 0; pipe = usb_rcvctrlpipe(udev, 0); return usb_control_msg(udev, pipe, ATH3K_GETSTATE, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, state, 0x01, USB_CTRL_SET_TIMEOUT); } static int ath3k_get_version(struct usb_device *udev, struct ath3k_version *version) { int pipe = 0; pipe = usb_rcvctrlpipe(udev, 0); return usb_control_msg(udev, pipe, ATH3K_GETVERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, version, sizeof(struct ath3k_version), USB_CTRL_SET_TIMEOUT); } static int ath3k_load_fwfile(struct usb_device *udev, const struct firmware *firmware) { u8 *send_buf; int err, pipe, len, size, count, sent = 0; int ret; count = firmware->size; send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); if (!send_buf) { BT_ERR("Can't allocate memory chunk for firmware"); return -ENOMEM; } size = min_t(uint, count, FW_HDR_SIZE); memcpy(send_buf, firmware->data, size); pipe = usb_sndctrlpipe(udev, 0); ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD, USB_TYPE_VENDOR, 0, 0, send_buf, size, USB_CTRL_SET_TIMEOUT); if (ret < 0) { BT_ERR("Can't change to loading configuration err"); kfree(send_buf); return ret; } sent += size; count -= size; while (count) { size = min_t(uint, count, BULK_SIZE); pipe = usb_sndbulkpipe(udev, 0x02); memcpy(send_buf, firmware->data + sent, size); err = usb_bulk_msg(udev, pipe, send_buf, size, &len, 3000); if (err || (len != size)) { BT_ERR("Error in firmware loading err = %d," "len = %d, size = %d", err, len, size); kfree(send_buf); return err; } sent += size; count -= size; } kfree(send_buf); return 0; } static int ath3k_switch_pid(struct usb_device *udev) { int pipe = 0; pipe = usb_sndctrlpipe(udev, 0); return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID, USB_TYPE_VENDOR, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } static int ath3k_set_normal_mode(struct usb_device *udev) { unsigned char fw_state; int pipe = 0, ret; ret = ath3k_get_state(udev, &fw_state); if (ret < 0) { BT_ERR("Can't get state to change to normal mode err"); return ret; } if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) { BT_DBG("firmware was already in normal mode"); return 0; } pipe = usb_sndctrlpipe(udev, 0); return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE, USB_TYPE_VENDOR, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } static int ath3k_load_patch(struct usb_device *udev) { unsigned char fw_state; char filename[ATH3K_NAME_LEN] = {0}; const struct firmware *firmware; struct ath3k_version fw_version, pt_version; int ret; ret = ath3k_get_state(udev, &fw_state); if (ret < 0) { BT_ERR("Can't get state to change to load ram patch err"); return ret; } if (fw_state & ATH3K_PATCH_UPDATE) { BT_DBG("Patch was already downloaded"); return 0; } ret = ath3k_get_version(udev, &fw_version); if (ret < 0) { BT_ERR("Can't get version to change to load ram patch err"); return ret; } snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu", fw_version.rom_version); ret = request_firmware(&firmware, filename, &udev->dev); if (ret < 0) { BT_ERR("Patch file not found %s", filename); return ret; } pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8); pt_version.build_version = *(int *) (firmware->data + firmware->size - 4); if ((pt_version.rom_version != fw_version.rom_version) || (pt_version.build_version <= fw_version.build_version)) { BT_ERR("Patch file version did not match with firmware"); release_firmware(firmware); return -EINVAL; } ret = ath3k_load_fwfile(udev, firmware); release_firmware(firmware); return ret; } static int ath3k_load_syscfg(struct usb_device *udev) { unsigned char fw_state; char filename[ATH3K_NAME_LEN] = {0}; const struct firmware *firmware; struct ath3k_version fw_version; int clk_value, ret; ret = ath3k_get_state(udev, &fw_state); if (ret < 0) { BT_ERR("Can't get state to change to load configration err"); return -EBUSY; } ret = ath3k_get_version(udev, &fw_version); if (ret < 0) { BT_ERR("Can't get version to change to load ram patch err"); return ret; } switch (fw_version.ref_clock) { case ATH3K_XTAL_FREQ_26M: clk_value = 26; break; case ATH3K_XTAL_FREQ_40M: clk_value = 40; break; case ATH3K_XTAL_FREQ_19P2: clk_value = 19; break; default: clk_value = 0; break; } snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s", fw_version.rom_version, clk_value, ".dfu"); ret = request_firmware(&firmware, filename, &udev->dev); if (ret < 0) { BT_ERR("Configuration file not found %s", filename); return ret; } ret = ath3k_load_fwfile(udev, firmware); release_firmware(firmware); return ret; } static int ath3k_probe(struct usb_interface *intf, const struct usb_device_id *id) { const struct firmware *firmware; struct usb_device *udev = interface_to_usbdev(intf); int ret; BT_DBG("intf %p id %p", intf, id); if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; /* match device ID in ath3k blacklist table */ if (!id->driver_info) { const struct usb_device_id *match; match = usb_match_id(intf, ath3k_blist_tbl); if (match) id = match; } /* load patch and sysconfig files for AR3012 */ if (id->driver_info & BTUSB_ATH3012) { /* New firmware with patch and sysconfig files already loaded */ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001) return -ENODEV; ret = ath3k_load_patch(udev); if (ret < 0) { BT_ERR("Loading patch file failed"); return ret; } ret = ath3k_load_syscfg(udev); if (ret < 0) { BT_ERR("Loading sysconfig file failed"); return ret; } ret = ath3k_set_normal_mode(udev); if (ret < 0) { BT_ERR("Set normal mode failed"); return ret; } ath3k_switch_pid(udev); return 0; } ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev); if (ret < 0) { if (ret == -ENOENT) BT_ERR("Firmware file \"%s\" not found", ATH3K_FIRMWARE); else BT_ERR("Firmware file \"%s\" request failed (err=%d)", ATH3K_FIRMWARE, ret); return ret; } ret = ath3k_load_firmware(udev, firmware); release_firmware(firmware); return ret; } static void ath3k_disconnect(struct usb_interface *intf) { BT_DBG("ath3k_disconnect intf %p", intf); } static struct usb_driver ath3k_driver = { .name = "ath3k", .probe = ath3k_probe, .disconnect = ath3k_disconnect, .id_table = ath3k_table, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(ath3k_driver); MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Atheros AR30xx firmware driver"); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(ATH3K_FIRMWARE); compat-drivers-2012-09-18/drivers/bluetooth/hci_ldisc.c0000644000175000017500000003300512026211315022131 0ustar mcgrofmcgrof/* * * Bluetooth HCI UART driver * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hci_uart.h" #define VERSION "2.2" static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO]; int hci_uart_register_proto(struct hci_uart_proto *p) { if (p->id >= HCI_UART_MAX_PROTO) return -EINVAL; if (hup[p->id]) return -EEXIST; hup[p->id] = p; return 0; } int hci_uart_unregister_proto(struct hci_uart_proto *p) { if (p->id >= HCI_UART_MAX_PROTO) return -EINVAL; if (!hup[p->id]) return -EINVAL; hup[p->id] = NULL; return 0; } static struct hci_uart_proto *hci_uart_get_proto(unsigned int id) { if (id >= HCI_UART_MAX_PROTO) return NULL; return hup[id]; } static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) { struct hci_dev *hdev = hu->hdev; /* Update HCI stat counters */ switch (pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; } } static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) { struct sk_buff *skb = hu->tx_skb; if (!skb) skb = hu->proto->dequeue(hu); else hu->tx_skb = NULL; return skb; } int hci_uart_tx_wakeup(struct hci_uart *hu) { struct tty_struct *tty = hu->tty; struct hci_dev *hdev = hu->hdev; struct sk_buff *skb; if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); return 0; } BT_DBG(""); restart: clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); while ((skb = hci_uart_dequeue(hu))) { int len; set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); len = tty->ops->write(tty, skb->data, skb->len); hdev->stat.byte_tx += len; skb_pull(skb, len); if (skb->len) { hu->tx_skb = skb; break; } hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type); kfree_skb(skb); } if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) goto restart; clear_bit(HCI_UART_SENDING, &hu->tx_state); return 0; } static void hci_uart_init_work(struct work_struct *work) { struct hci_uart *hu = container_of(work, struct hci_uart, init_ready); int err; if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) return; err = hci_register_dev(hu->hdev); if (err < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hu->hdev); hu->hdev = NULL; hu->proto->close(hu); } set_bit(HCI_UART_REGISTERED, &hu->flags); } int hci_uart_init_ready(struct hci_uart *hu) { if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) return -EALREADY; schedule_work(&hu->init_ready); return 0; } /* ------- Interface to HCI layer ------ */ /* Initialize device */ static int hci_uart_open(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); /* Nothing to do for UART driver */ set_bit(HCI_RUNNING, &hdev->flags); return 0; } /* Reset device */ static int hci_uart_flush(struct hci_dev *hdev) { struct hci_uart *hu = hci_get_drvdata(hdev); struct tty_struct *tty = hu->tty; BT_DBG("hdev %p tty %p", hdev, tty); if (hu->tx_skb) { kfree_skb(hu->tx_skb); hu->tx_skb = NULL; } /* Flush any pending characters in the driver and discipline. */ tty_ldisc_flush(tty); tty_driver_flush_buffer(tty); if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) hu->proto->flush(hu); return 0; } /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { BT_DBG("hdev %p", hdev); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; hci_uart_flush(hdev); hdev->flush = NULL; return 0; } /* Send frames from HCI layer */ static int hci_uart_send_frame(struct sk_buff *skb) { struct hci_dev* hdev = (struct hci_dev *) skb->dev; struct hci_uart *hu; if (!hdev) { BT_ERR("Frame for unknown device (hdev=NULL)"); return -ENODEV; } if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; hu = hci_get_drvdata(hdev); BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); hu->proto->enqueue(hu, skb); hci_uart_tx_wakeup(hu); return 0; } /* ------ LDISC part ------ */ /* hci_uart_tty_open * * Called when line discipline changed to HCI_UART. * * Arguments: * tty pointer to tty info structure * Return Value: * 0 if success, otherwise error code */ static int hci_uart_tty_open(struct tty_struct *tty) { struct hci_uart *hu = (void *) tty->disc_data; BT_DBG("tty %p", tty); /* FIXME: This btw is bogus, nothing requires the old ldisc to clear the pointer */ if (hu) return -EEXIST; /* Error if the tty has no write op instead of leaving an exploitable hole */ if (tty->ops->write == NULL) return -EOPNOTSUPP; if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) { BT_ERR("Can't allocate control structure"); return -ENFILE; } tty->disc_data = hu; hu->tty = tty; tty->receive_room = 65536; INIT_WORK(&hu->init_ready, hci_uart_init_work); spin_lock_init(&hu->rx_lock); /* Flush any pending characters in the driver and line discipline. */ /* FIXME: why is this needed. Note don't use ldisc_ref here as the open path is before the ldisc is referencable */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) if (tty->ldisc->ops->flush_buffer) tty->ldisc->ops->flush_buffer(tty); #else if (tty->ldisc.ops->flush_buffer) tty->ldisc.ops->flush_buffer(tty); #endif tty_driver_flush_buffer(tty); return 0; } /* hci_uart_tty_close() * * Called when the line discipline is changed to something * else, the tty is closed, or the tty detects a hangup. */ static void hci_uart_tty_close(struct tty_struct *tty) { struct hci_uart *hu = (void *)tty->disc_data; struct hci_dev *hdev; BT_DBG("tty %p", tty); /* Detach from the tty */ tty->disc_data = NULL; if (!hu) return; hdev = hu->hdev; if (hdev) hci_uart_close(hdev); if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); hci_free_dev(hdev); } hu->proto->close(hu); } kfree(hu); } /* hci_uart_tty_wakeup() * * Callback for transmit wakeup. Called when low level * device driver can accept more send data. * * Arguments: tty pointer to associated tty instance data * Return Value: None */ static void hci_uart_tty_wakeup(struct tty_struct *tty) { struct hci_uart *hu = (void *)tty->disc_data; BT_DBG(""); if (!hu) return; clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); if (tty != hu->tty) return; if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) hci_uart_tx_wakeup(hu); } /* hci_uart_tty_receive() * * Called by tty low level driver when receive data is * available. * * Arguments: tty pointer to tty isntance data * data pointer to received data * flags pointer to flags for data * count count of received data in bytes * * Return Value: None */ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) { struct hci_uart *hu = (void *)tty->disc_data; if (!hu || tty != hu->tty) return; if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) return; spin_lock(&hu->rx_lock); hu->proto->recv(hu, (void *) data, count); hu->hdev->stat.byte_rx += count; spin_unlock(&hu->rx_lock); tty_unthrottle(tty); } static int hci_uart_register_dev(struct hci_uart *hu) { struct hci_dev *hdev; BT_DBG(""); /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); return -ENOMEM; } hu->hdev = hdev; hdev->bus = HCI_UART; hci_set_drvdata(hdev, hu); hdev->open = hci_uart_open; hdev->close = hci_uart_close; hdev->flush = hci_uart_flush; hdev->send = hci_uart_send_frame; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) SET_HCIDEV_DEV(hdev, hu->tty->dev); #endif if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) hdev->dev_type = HCI_AMP; else hdev->dev_type = HCI_BREDR; if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) return 0; if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); return -ENODEV; } set_bit(HCI_UART_REGISTERED, &hu->flags); return 0; } static int hci_uart_set_proto(struct hci_uart *hu, int id) { struct hci_uart_proto *p; int err; p = hci_uart_get_proto(id); if (!p) return -EPROTONOSUPPORT; err = p->open(hu); if (err) return err; hu->proto = p; err = hci_uart_register_dev(hu); if (err) { p->close(hu); return err; } return 0; } /* hci_uart_tty_ioctl() * * Process IOCTL system call for the tty device. * * Arguments: * * tty pointer to tty instance data * file pointer to open file object for device * cmd IOCTL command code * arg argument for IOCTL call (cmd dependent) * * Return Value: Command dependent */ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct hci_uart *hu = (void *)tty->disc_data; int err = 0; BT_DBG(""); /* Verify the status of the device */ if (!hu) return -EBADF; switch (cmd) { case HCIUARTSETPROTO: if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) { err = hci_uart_set_proto(hu, arg); if (err) { clear_bit(HCI_UART_PROTO_SET, &hu->flags); return err; } } else return -EBUSY; break; case HCIUARTGETPROTO: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return hu->proto->id; return -EUNATCH; case HCIUARTGETDEVICE: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return hu->hdev->id; return -EUNATCH; case HCIUARTSETFLAGS: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return -EBUSY; hu->hdev_flags = arg; break; case HCIUARTGETFLAGS: return hu->hdev_flags; default: #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) err = n_tty_ioctl_helper(tty, file, cmd, arg); #else err = n_tty_ioctl(tty, file, cmd, arg); #endif break; } return err; } /* * We don't provide read/write/poll interface for user space. */ static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { return 0; } static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count) { return 0; } static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait) { return 0; } static int __init hci_uart_init(void) { static struct tty_ldisc_ops hci_uart_ldisc; int err; BT_INFO("HCI UART driver ver %s", VERSION); /* Register the tty discipline */ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc)); hci_uart_ldisc.magic = TTY_LDISC_MAGIC; hci_uart_ldisc.name = "n_hci"; hci_uart_ldisc.open = hci_uart_tty_open; hci_uart_ldisc.close = hci_uart_tty_close; hci_uart_ldisc.read = hci_uart_tty_read; hci_uart_ldisc.write = hci_uart_tty_write; hci_uart_ldisc.ioctl = hci_uart_tty_ioctl; hci_uart_ldisc.poll = hci_uart_tty_poll; hci_uart_ldisc.receive_buf = hci_uart_tty_receive; hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup; hci_uart_ldisc.owner = THIS_MODULE; if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) { BT_ERR("HCI line discipline registration failed. (%d)", err); return err; } #ifdef CONFIG_BT_HCIUART_H4 h4_init(); #endif #ifdef CONFIG_BT_HCIUART_BCSP bcsp_init(); #endif #ifdef CONFIG_BT_HCIUART_LL ll_init(); #endif #ifdef CONFIG_BT_HCIUART_ATH3K ath_init(); #endif #ifdef CONFIG_BT_HCIUART_3WIRE h5_init(); #endif return 0; } static void __exit hci_uart_exit(void) { int err; #ifdef CONFIG_BT_HCIUART_H4 h4_deinit(); #endif #ifdef CONFIG_BT_HCIUART_BCSP bcsp_deinit(); #endif #ifdef CONFIG_BT_HCIUART_LL ll_deinit(); #endif #ifdef CONFIG_BT_HCIUART_ATH3K ath_deinit(); #endif #ifdef CONFIG_BT_HCIUART_3WIRE h5_deinit(); #endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) BT_ERR("Can't unregister HCI line discipline (%d)", err); } module_init(hci_uart_init); module_exit(hci_uart_exit); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS_LDISC(N_HCI); compat-drivers-2012-09-18/drivers/bluetooth/btmrvl_sdio.c0000644000175000017500000006423212026211315022542 0ustar mcgrofmcgrof/** * Marvell BT-over-SDIO driver: SDIO interface related functions. * * Copyright (C) 2009, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. **/ #include #include #include #include #include #include #include #include "btmrvl_drv.h" #include "btmrvl_sdio.h" #define VERSION "1.0" /* The btmrvl_sdio_remove() callback function is called * when user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases * differently. * If the user is removing the module, a MODULE_SHUTDOWN_REQ * command is sent to firmware and interrupt will be disabled. * If the card is removed, there is no need to send command * or disable interrupt. * * The variable 'user_rmmod' is used to distinguish these two * scenarios. This flag is initialized as FALSE in case the card * is removed, and will be set to TRUE for module removal when * module_exit function is called. */ static u8 user_rmmod; static u8 sdio_ireg; static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = { .cfg = 0x03, .host_int_mask = 0x04, .host_intstatus = 0x05, .card_status = 0x20, .sq_read_base_addr_a0 = 0x10, .sq_read_base_addr_a1 = 0x11, .card_fw_status0 = 0x40, .card_fw_status1 = 0x41, .card_rx_len = 0x42, .card_rx_unit = 0x43, .io_port_0 = 0x00, .io_port_1 = 0x01, .io_port_2 = 0x02, }; static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = { .cfg = 0x00, .host_int_mask = 0x02, .host_intstatus = 0x03, .card_status = 0x30, .sq_read_base_addr_a0 = 0x40, .sq_read_base_addr_a1 = 0x41, .card_revision = 0x5c, .card_fw_status0 = 0x60, .card_fw_status1 = 0x61, .card_rx_len = 0x62, .card_rx_unit = 0x63, .io_port_0 = 0x78, .io_port_1 = 0x79, .io_port_2 = 0x7a, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .helper = "sd8688_helper.bin", .firmware = "sd8688.bin", .reg = &btmrvl_reg_8688, .sd_blksz_fw_dl = 64, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { .helper = NULL, .firmware = "mrvl/sd8787_uapsta.bin", .reg = &btmrvl_reg_87xx, .sd_blksz_fw_dl = 256, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { .helper = NULL, .firmware = "mrvl/sd8797_uapsta.bin", .reg = &btmrvl_reg_87xx, .sd_blksz_fw_dl = 256, }; static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8688 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105), .driver_data = (unsigned long) &btmrvl_sdio_sd8688 }, /* Marvell SD8787 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A), .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, /* Marvell SD8787 Bluetooth AMP device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B), .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, /* Marvell SD8797 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A), .driver_data = (unsigned long) &btmrvl_sdio_sd8797 }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(sdio, btmrvl_sdio_ids); static int btmrvl_sdio_get_rx_unit(struct btmrvl_sdio_card *card) { u8 reg; int ret; reg = sdio_readb(card->func, card->reg->card_rx_unit, &ret); if (!ret) card->rx_unit = reg; return ret; } static int btmrvl_sdio_read_fw_status(struct btmrvl_sdio_card *card, u16 *dat) { u8 fws0, fws1; int ret; *dat = 0; fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret); if (ret) return -EIO; fws1 = sdio_readb(card->func, card->reg->card_fw_status1, &ret); if (ret) return -EIO; *dat = (((u16) fws1) << 8) | fws0; return 0; } static int btmrvl_sdio_read_rx_len(struct btmrvl_sdio_card *card, u16 *dat) { u8 reg; int ret; reg = sdio_readb(card->func, card->reg->card_rx_len, &ret); if (!ret) *dat = (u16) reg << card->rx_unit; return ret; } static int btmrvl_sdio_enable_host_int_mask(struct btmrvl_sdio_card *card, u8 mask) { int ret; sdio_writeb(card->func, mask, card->reg->host_int_mask, &ret); if (ret) { BT_ERR("Unable to enable the host interrupt!"); ret = -EIO; } return ret; } static int btmrvl_sdio_disable_host_int_mask(struct btmrvl_sdio_card *card, u8 mask) { u8 host_int_mask; int ret; host_int_mask = sdio_readb(card->func, card->reg->host_int_mask, &ret); if (ret) return -EIO; host_int_mask &= ~mask; sdio_writeb(card->func, host_int_mask, card->reg->host_int_mask, &ret); if (ret < 0) { BT_ERR("Unable to disable the host interrupt!"); return -EIO; } return 0; } static int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits) { unsigned int tries; u8 status; int ret; for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { status = sdio_readb(card->func, card->reg->card_status, &ret); if (ret) goto failed; if ((status & bits) == bits) return ret; udelay(1); } ret = -ETIMEDOUT; failed: BT_ERR("FAILED! ret=%d", ret); return ret; } static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card, int pollnum) { int ret = -ETIMEDOUT; u16 firmwarestat; unsigned int tries; /* Wait for firmware to become ready */ for (tries = 0; tries < pollnum; tries++) { if (btmrvl_sdio_read_fw_status(card, &firmwarestat) < 0) continue; if (firmwarestat == FIRMWARE_READY) { ret = 0; break; } else { msleep(10); } } return ret; } static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card) { const struct firmware *fw_helper = NULL; const u8 *helper = NULL; int ret; void *tmphlprbuf = NULL; int tmphlprbufsz, hlprblknow, helperlen; u8 *helperbuf; u32 tx_len; ret = request_firmware(&fw_helper, card->helper, &card->func->dev); if ((ret < 0) || !fw_helper) { BT_ERR("request_firmware(helper) failed, error code = %d", ret); ret = -ENOENT; goto done; } helper = fw_helper->data; helperlen = fw_helper->size; BT_DBG("Downloading helper image (%d bytes), block size %d bytes", helperlen, SDIO_BLOCK_SIZE); tmphlprbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); tmphlprbuf = kzalloc(tmphlprbufsz, GFP_KERNEL); if (!tmphlprbuf) { BT_ERR("Unable to allocate buffer for helper." " Terminating download"); ret = -ENOMEM; goto done; } helperbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, BTSDIO_DMA_ALIGN); /* Perform helper data transfer */ tx_len = (FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE) - SDIO_HEADER_LEN; hlprblknow = 0; do { ret = btmrvl_sdio_poll_card_status(card, CARD_IO_READY | DN_LD_CARD_RDY); if (ret < 0) { BT_ERR("Helper download poll status timeout @ %d", hlprblknow); goto done; } /* Check if there is more data? */ if (hlprblknow >= helperlen) break; if (helperlen - hlprblknow < tx_len) tx_len = helperlen - hlprblknow; /* Little-endian */ helperbuf[0] = ((tx_len & 0x000000ff) >> 0); helperbuf[1] = ((tx_len & 0x0000ff00) >> 8); helperbuf[2] = ((tx_len & 0x00ff0000) >> 16); helperbuf[3] = ((tx_len & 0xff000000) >> 24); memcpy(&helperbuf[SDIO_HEADER_LEN], &helper[hlprblknow], tx_len); /* Now send the data */ ret = sdio_writesb(card->func, card->ioport, helperbuf, FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE); if (ret < 0) { BT_ERR("IO error during helper download @ %d", hlprblknow); goto done; } hlprblknow += tx_len; } while (true); BT_DBG("Transferring helper image EOF block"); memset(helperbuf, 0x0, SDIO_BLOCK_SIZE); ret = sdio_writesb(card->func, card->ioport, helperbuf, SDIO_BLOCK_SIZE); if (ret < 0) { BT_ERR("IO error in writing helper image EOF block"); goto done; } ret = 0; done: kfree(tmphlprbuf); release_firmware(fw_helper); return ret; } static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card) { const struct firmware *fw_firmware = NULL; const u8 *firmware = NULL; int firmwarelen, tmpfwbufsz, ret; unsigned int tries, offset; u8 base0, base1; void *tmpfwbuf = NULL; u8 *fwbuf; u16 len, blksz_dl = card->sd_blksz_fw_dl; int txlen = 0, tx_blocks = 0, count = 0; ret = request_firmware(&fw_firmware, card->firmware, &card->func->dev); if ((ret < 0) || !fw_firmware) { BT_ERR("request_firmware(firmware) failed, error code = %d", ret); ret = -ENOENT; goto done; } firmware = fw_firmware->data; firmwarelen = fw_firmware->size; BT_DBG("Downloading FW image (%d bytes)", firmwarelen); tmpfwbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); if (!tmpfwbuf) { BT_ERR("Unable to allocate buffer for firmware." " Terminating download"); ret = -ENOMEM; goto done; } /* Ensure aligned firmware buffer */ fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, BTSDIO_DMA_ALIGN); /* Perform firmware data transfer */ offset = 0; do { ret = btmrvl_sdio_poll_card_status(card, CARD_IO_READY | DN_LD_CARD_RDY); if (ret < 0) { BT_ERR("FW download with helper poll status" " timeout @ %d", offset); goto done; } /* Check if there is more data ? */ if (offset >= firmwarelen) break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { base0 = sdio_readb(card->func, card->reg->sq_read_base_addr_a0, &ret); if (ret) { BT_ERR("BASE0 register read failed:" " base0 = 0x%04X(%d)." " Terminating download", base0, base0); ret = -EIO; goto done; } base1 = sdio_readb(card->func, card->reg->sq_read_base_addr_a1, &ret); if (ret) { BT_ERR("BASE1 register read failed:" " base1 = 0x%04X(%d)." " Terminating download", base1, base1); ret = -EIO; goto done; } len = (((u16) base1) << 8) | base0; if (len) break; udelay(10); } if (!len) break; else if (len > BTM_UPLD_SIZE) { BT_ERR("FW download failure @%d, invalid length %d", offset, len); ret = -EINVAL; goto done; } txlen = len; if (len & BIT(0)) { count++; if (count > MAX_WRITE_IOMEM_RETRY) { BT_ERR("FW download failure @%d, " "over max retry count", offset); ret = -EIO; goto done; } BT_ERR("FW CRC error indicated by the helper: " "len = 0x%04X, txlen = %d", len, txlen); len &= ~BIT(0); /* Set txlen to 0 so as to resend from same offset */ txlen = 0; } else { count = 0; /* Last block ? */ if (firmwarelen - offset < txlen) txlen = firmwarelen - offset; tx_blocks = (txlen + blksz_dl - 1) / blksz_dl; memcpy(fwbuf, &firmware[offset], txlen); } ret = sdio_writesb(card->func, card->ioport, fwbuf, tx_blocks * blksz_dl); if (ret < 0) { BT_ERR("FW download, writesb(%d) failed @%d", count, offset); sdio_writeb(card->func, HOST_CMD53_FIN, card->reg->cfg, &ret); if (ret) BT_ERR("writeb failed (CFG)"); } offset += txlen; } while (true); BT_DBG("FW download over, size %d bytes", offset); ret = 0; done: kfree(tmpfwbuf); release_firmware(fw_firmware); return ret; } static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) { u16 buf_len = 0; int ret, buf_block_len, blksz; struct sk_buff *skb = NULL; u32 type; u8 *payload = NULL; struct hci_dev *hdev = priv->btmrvl_dev.hcidev; struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; if (!card || !card->func) { BT_ERR("card or function is NULL!"); ret = -EINVAL; goto exit; } /* Read the length of data to be transferred */ ret = btmrvl_sdio_read_rx_len(card, &buf_len); if (ret < 0) { BT_ERR("read rx_len failed"); ret = -EIO; goto exit; } blksz = SDIO_BLOCK_SIZE; buf_block_len = (buf_len + blksz - 1) / blksz; if (buf_len <= SDIO_HEADER_LEN || (buf_block_len * blksz) > ALLOC_BUF_SIZE) { BT_ERR("invalid packet length: %d", buf_len); ret = -EINVAL; goto exit; } /* Allocate buffer */ skb = bt_skb_alloc(buf_block_len * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC); if (skb == NULL) { BT_ERR("No free skb"); goto exit; } if ((unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)) { skb_put(skb, (unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)); skb_pull(skb, (unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)); } payload = skb->data; ret = sdio_readsb(card->func, payload, card->ioport, buf_block_len * blksz); if (ret < 0) { BT_ERR("readsb failed: %d", ret); ret = -EIO; goto exit; } /* This is SDIO specific header length: byte[2][1][0], type: byte[3] * (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ buf_len = payload[0]; buf_len |= (u16) payload[1] << 8; type = payload[3]; switch (type) { case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: case HCI_EVENT_PKT: bt_cb(skb)->pkt_type = type; skb->dev = (void *)hdev; skb_put(skb, buf_len); skb_pull(skb, SDIO_HEADER_LEN); if (type == HCI_EVENT_PKT) { if (btmrvl_check_evtpkt(priv, skb)) hci_recv_frame(skb); } else { hci_recv_frame(skb); } hdev->stat.byte_rx += buf_len; break; case MRVL_VENDOR_PKT: bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; skb->dev = (void *)hdev; skb_put(skb, buf_len); skb_pull(skb, SDIO_HEADER_LEN); if (btmrvl_process_event(priv, skb)) hci_recv_frame(skb); hdev->stat.byte_rx += buf_len; break; default: BT_ERR("Unknown packet type:%d", type); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload, blksz * buf_block_len); kfree_skb(skb); skb = NULL; break; } exit: if (ret) { hdev->stat.err_rx++; kfree_skb(skb); } return ret; } static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv) { ulong flags; u8 ireg; struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; spin_lock_irqsave(&priv->driver_lock, flags); ireg = sdio_ireg; sdio_ireg = 0; spin_unlock_irqrestore(&priv->driver_lock, flags); sdio_claim_host(card->func); if (ireg & DN_LD_HOST_INT_STATUS) { if (priv->btmrvl_dev.tx_dnld_rdy) BT_DBG("tx_done already received: " " int_status=0x%x", ireg); else priv->btmrvl_dev.tx_dnld_rdy = true; } if (ireg & UP_LD_HOST_INT_STATUS) btmrvl_sdio_card_to_host(priv); sdio_release_host(card->func); return 0; } static void btmrvl_sdio_interrupt(struct sdio_func *func) { struct btmrvl_private *priv; struct btmrvl_sdio_card *card; ulong flags; u8 ireg = 0; int ret; card = sdio_get_drvdata(func); if (!card || !card->priv) { BT_ERR("sbi_interrupt(%p) card or priv is " "NULL, card=%p\n", func, card); return; } priv = card->priv; ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret); if (ret) { BT_ERR("sdio_readb: read int status register failed"); return; } if (ireg != 0) { /* * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS * Clear the interrupt status register and re-enable the * interrupt. */ BT_DBG("ireg = 0x%x", ireg); sdio_writeb(card->func, ~(ireg) & (DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS), card->reg->host_intstatus, &ret); if (ret) { BT_ERR("sdio_writeb: clear int status register failed"); return; } } spin_lock_irqsave(&priv->driver_lock, flags); sdio_ireg |= ireg; spin_unlock_irqrestore(&priv->driver_lock, flags); btmrvl_interrupt(priv); } static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card) { struct sdio_func *func; u8 reg; int ret = 0; if (!card || !card->func) { BT_ERR("Error: card or function is NULL!"); ret = -EINVAL; goto failed; } func = card->func; sdio_claim_host(func); ret = sdio_enable_func(func); if (ret) { BT_ERR("sdio_enable_func() failed: ret=%d", ret); ret = -EIO; goto release_host; } ret = sdio_claim_irq(func, btmrvl_sdio_interrupt); if (ret) { BT_ERR("sdio_claim_irq failed: ret=%d", ret); ret = -EIO; goto disable_func; } ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE); if (ret) { BT_ERR("cannot set SDIO block size"); ret = -EIO; goto release_irq; } reg = sdio_readb(func, card->reg->io_port_0, &ret); if (ret < 0) { ret = -EIO; goto release_irq; } card->ioport = reg; reg = sdio_readb(func, card->reg->io_port_1, &ret); if (ret < 0) { ret = -EIO; goto release_irq; } card->ioport |= (reg << 8); reg = sdio_readb(func, card->reg->io_port_2, &ret); if (ret < 0) { ret = -EIO; goto release_irq; } card->ioport |= (reg << 16); BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport); sdio_set_drvdata(func, card); sdio_release_host(func); return 0; release_irq: sdio_release_irq(func); disable_func: sdio_disable_func(func); release_host: sdio_release_host(func); failed: return ret; } static int btmrvl_sdio_unregister_dev(struct btmrvl_sdio_card *card) { if (card && card->func) { sdio_claim_host(card->func); sdio_release_irq(card->func); sdio_disable_func(card->func); sdio_release_host(card->func); sdio_set_drvdata(card->func, NULL); } return 0; } static int btmrvl_sdio_enable_host_int(struct btmrvl_sdio_card *card) { int ret; if (!card || !card->func) return -EINVAL; sdio_claim_host(card->func); ret = btmrvl_sdio_enable_host_int_mask(card, HIM_ENABLE); btmrvl_sdio_get_rx_unit(card); sdio_release_host(card->func); return ret; } static int btmrvl_sdio_disable_host_int(struct btmrvl_sdio_card *card) { int ret; if (!card || !card->func) return -EINVAL; sdio_claim_host(card->func); ret = btmrvl_sdio_disable_host_int_mask(card, HIM_DISABLE); sdio_release_host(card->func); return ret; } static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv, u8 *payload, u16 nb) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret = 0; int buf_block_len; int blksz; int i = 0; u8 *buf = NULL; void *tmpbuf = NULL; int tmpbufsz; if (!card || !card->func) { BT_ERR("card or function is NULL!"); return -EINVAL; } buf = payload; if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1)) { tmpbufsz = ALIGN_SZ(nb, BTSDIO_DMA_ALIGN); tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL); if (!tmpbuf) return -ENOMEM; buf = (u8 *) ALIGN_ADDR(tmpbuf, BTSDIO_DMA_ALIGN); memcpy(buf, payload, nb); } blksz = SDIO_BLOCK_SIZE; buf_block_len = (nb + blksz - 1) / blksz; sdio_claim_host(card->func); do { /* Transfer data to card */ ret = sdio_writesb(card->func, card->ioport, buf, buf_block_len * blksz); if (ret < 0) { i++; BT_ERR("i=%d writesb failed: %d", i, ret); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload, nb); ret = -EIO; if (i > MAX_WRITE_IOMEM_RETRY) goto exit; } } while (ret); priv->btmrvl_dev.tx_dnld_rdy = false; exit: sdio_release_host(card->func); kfree(tmpbuf); return ret; } static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card) { int ret = 0; u8 fws0; int pollnum = MAX_POLL_TRIES; if (!card || !card->func) { BT_ERR("card or function is NULL!"); return -EINVAL; } sdio_claim_host(card->func); if (!btmrvl_sdio_verify_fw_download(card, 1)) { BT_DBG("Firmware already downloaded!"); goto done; } /* Check if other function driver is downloading the firmware */ fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret); if (ret) { BT_ERR("Failed to read FW downloading status!"); ret = -EIO; goto done; } if (fws0) { BT_DBG("BT not the winner (%#x). Skip FW downloading", fws0); /* Give other function more time to download the firmware */ pollnum *= 10; } else { if (card->helper) { ret = btmrvl_sdio_download_helper(card); if (ret) { BT_ERR("Failed to download helper!"); ret = -EIO; goto done; } } if (btmrvl_sdio_download_fw_w_helper(card)) { BT_ERR("Failed to download firmware!"); ret = -EIO; goto done; } } if (btmrvl_sdio_verify_fw_download(card, pollnum)) { BT_ERR("FW failed to be active in time!"); ret = -ETIMEDOUT; goto done; } done: sdio_release_host(card->func); return ret; } static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret = 0; if (!card || !card->func) { BT_ERR("card or function is NULL!"); return -EINVAL; } sdio_claim_host(card->func); sdio_writeb(card->func, HOST_POWER_UP, card->reg->cfg, &ret); sdio_release_host(card->func); BT_DBG("wake up firmware"); return ret; } static int btmrvl_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int ret = 0; struct btmrvl_private *priv = NULL; struct btmrvl_sdio_card *card = NULL; BT_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d", id->vendor, id->device, id->class, func->num); card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; card->func = func; if (id->driver_data) { struct btmrvl_sdio_device *data = (void *) id->driver_data; card->helper = data->helper; card->firmware = data->firmware; card->reg = data->reg; card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; } if (btmrvl_sdio_register_dev(card) < 0) { BT_ERR("Failed to register BT device!"); return -ENODEV; } /* Disable the interrupts on the card */ btmrvl_sdio_disable_host_int(card); if (btmrvl_sdio_download_fw(card)) { BT_ERR("Downloading firmware failed!"); ret = -ENODEV; goto unreg_dev; } msleep(100); btmrvl_sdio_enable_host_int(card); priv = btmrvl_add_card(card); if (!priv) { BT_ERR("Initializing card failed!"); ret = -ENODEV; goto disable_host_int; } card->priv = priv; /* Initialize the interface specific function pointers */ priv->hw_host_to_card = btmrvl_sdio_host_to_card; priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; priv->hw_process_int_status = btmrvl_sdio_process_int_status; if (btmrvl_register_hdev(priv)) { BT_ERR("Register hdev failed!"); ret = -ENODEV; goto disable_host_int; } priv->btmrvl_dev.psmode = 1; btmrvl_enable_ps(priv); priv->btmrvl_dev.gpio_gap = 0xffff; btmrvl_send_hscfg_cmd(priv); return 0; disable_host_int: btmrvl_sdio_disable_host_int(card); unreg_dev: btmrvl_sdio_unregister_dev(card); return ret; } static void btmrvl_sdio_remove(struct sdio_func *func) { struct btmrvl_sdio_card *card; if (func) { card = sdio_get_drvdata(func); if (card) { /* Send SHUTDOWN command & disable interrupt * if user removes the module. */ if (user_rmmod) { btmrvl_send_module_cfg_cmd(card->priv, MODULE_SHUTDOWN_REQ); btmrvl_sdio_disable_host_int(card); } BT_DBG("unregester dev"); btmrvl_sdio_unregister_dev(card); btmrvl_remove_card(card->priv); } } } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int btmrvl_sdio_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct btmrvl_sdio_card *card; struct btmrvl_private *priv; mmc_pm_flag_t pm_flags; struct hci_dev *hcidev; if (func) { pm_flags = sdio_get_host_pm_caps(func); BT_DBG("%s: suspend: PM flags = 0x%x", sdio_func_id(func), pm_flags); if (!(pm_flags & MMC_PM_KEEP_POWER)) { BT_ERR("%s: cannot remain alive while suspended", sdio_func_id(func)); return -ENOSYS; } card = sdio_get_drvdata(func); if (!card || !card->priv) { BT_ERR("card or priv structure is not valid"); return 0; } } else { BT_ERR("sdio_func is not specified"); return 0; } priv = card->priv; if (priv->adapter->hs_state != HS_ACTIVATED) { if (btmrvl_enable_hs(priv)) { BT_ERR("HS not actived, suspend failed!"); return -EBUSY; } } hcidev = priv->btmrvl_dev.hcidev; BT_DBG("%s: SDIO suspend", hcidev->name); hci_suspend_dev(hcidev); skb_queue_purge(&priv->adapter->tx_queue); priv->adapter->is_suspended = true; /* We will keep the power when hs enabled successfully */ if (priv->adapter->hs_state == HS_ACTIVATED) { BT_DBG("suspend with MMC_PM_KEEP_POWER"); return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); } else { BT_DBG("suspend without MMC_PM_KEEP_POWER"); return 0; } } static int btmrvl_sdio_resume(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct btmrvl_sdio_card *card; struct btmrvl_private *priv; mmc_pm_flag_t pm_flags; struct hci_dev *hcidev; if (func) { pm_flags = sdio_get_host_pm_caps(func); BT_DBG("%s: resume: PM flags = 0x%x", sdio_func_id(func), pm_flags); card = sdio_get_drvdata(func); if (!card || !card->priv) { BT_ERR("card or priv structure is not valid"); return 0; } } else { BT_ERR("sdio_func is not specified"); return 0; } priv = card->priv; if (!priv->adapter->is_suspended) { BT_DBG("device already resumed"); return 0; } priv->adapter->is_suspended = false; hcidev = priv->btmrvl_dev.hcidev; BT_DBG("%s: SDIO resume", hcidev->name); hci_resume_dev(hcidev); priv->hw_wakeup_firmware(priv); priv->adapter->hs_state = HS_DEACTIVATED; BT_DBG("%s: HS DEACTIVATED in resume!", hcidev->name); return 0; } static const struct dev_pm_ops btmrvl_sdio_pm_ops = { .suspend = btmrvl_sdio_suspend, .resume = btmrvl_sdio_resume, }; #endif static struct sdio_driver bt_mrvl_sdio = { .name = "btmrvl_sdio", .id_table = btmrvl_sdio_ids, .probe = btmrvl_sdio_probe, .remove = btmrvl_sdio_remove, .drv = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .pm = &btmrvl_sdio_pm_ops, #endif } }; static int __init btmrvl_sdio_init_module(void) { if (sdio_register_driver(&bt_mrvl_sdio) != 0) { BT_ERR("SDIO Driver Registration Failed"); return -ENODEV; } /* Clear the flag in case user removes the card. */ user_rmmod = 0; return 0; } static void __exit btmrvl_sdio_exit_module(void) { /* Set the flag as user is removing this module. */ user_rmmod = 1; sdio_unregister_driver(&bt_mrvl_sdio); } module_init(btmrvl_sdio_init_module); module_exit(btmrvl_sdio_exit_module); MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE("sd8688_helper.bin"); MODULE_FIRMWARE("sd8688.bin"); MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin"); compat-drivers-2012-09-18/drivers/bluetooth/dtl1_cs.c0000644000175000017500000003626312026211315021552 0ustar mcgrofmcgrof/* * * A driver for Nokia Connectivity Card DTL-1 devices * * Copyright (C) 2001-2002 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The initial developer of the original code is David A. Hinds * . Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ======================== Module parameters ======================== */ MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1"); MODULE_LICENSE("GPL"); /* ======================== Local structures ======================== */ typedef struct dtl1_info_t { struct pcmcia_device *p_dev; struct hci_dev *hdev; spinlock_t lock; /* For serializing operations */ unsigned long flowmask; /* HCI flow mask */ int ri_latch; struct sk_buff_head txq; unsigned long tx_state; unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; } dtl1_info_t; static int dtl1_config(struct pcmcia_device *link); /* Transmit states */ #define XMIT_SENDING 1 #define XMIT_WAKEUP 2 #define XMIT_WAITING 8 /* Receiver States */ #define RECV_WAIT_NSH 0 #define RECV_WAIT_DATA 1 typedef struct { u8 type; u8 zero; u16 len; } __packed nsh_t; /* Nokia Specific Header */ #define NSHL 4 /* Nokia Specific Header Length */ /* ======================== Interrupt handling ======================== */ static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) { int actual = 0; /* Tx FIFO should be empty */ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) return 0; /* Fill FIFO with current frame */ while ((fifo_size-- > 0) && (actual < len)) { /* Transmit next byte */ outb(buf[actual], iobase + UART_TX); actual++; } return actual; } static void dtl1_write_wakeup(dtl1_info_t *info) { if (!info) { BT_ERR("Unknown device"); return; } if (test_bit(XMIT_WAITING, &(info->tx_state))) { set_bit(XMIT_WAKEUP, &(info->tx_state)); return; } if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { set_bit(XMIT_WAKEUP, &(info->tx_state)); return; } do { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif register struct sk_buff *skb; int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); if (!pcmcia_dev_present(info->p_dev)) return; if (!(skb = skb_dequeue(&(info->txq)))) break; /* Send frame */ len = dtl1_write(iobase, 32, skb->data, skb->len); if (len == skb->len) { set_bit(XMIT_WAITING, &(info->tx_state)); kfree_skb(skb); } else { skb_pull(skb, len); skb_queue_head(&(info->txq), skb); } info->hdev->stat.byte_tx += len; } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); clear_bit(XMIT_SENDING, &(info->tx_state)); } static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb) { u8 flowmask = *(u8 *)skb->data; int i; printk(KERN_INFO "Bluetooth: Nokia control data ="); for (i = 0; i < skb->len; i++) { printk(" %02x", skb->data[i]); } printk("\n"); /* transition to active state */ if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) { clear_bit(XMIT_WAITING, &(info->tx_state)); dtl1_write_wakeup(info); } info->flowmask = flowmask; kfree_skb(skb); } static void dtl1_receive(dtl1_info_t *info) { unsigned int iobase; nsh_t *nsh; int boguscount = 0; if (!info) { BT_ERR("Unknown device"); return; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif do { info->hdev->stat.byte_rx++; /* Allocate packet */ if (info->rx_skb == NULL) if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { BT_ERR("Can't allocate mem for new packet"); info->rx_state = RECV_WAIT_NSH; info->rx_count = NSHL; return; } *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); nsh = (nsh_t *)info->rx_skb->data; info->rx_count--; if (info->rx_count == 0) { switch (info->rx_state) { case RECV_WAIT_NSH: info->rx_state = RECV_WAIT_DATA; info->rx_count = nsh->len + (nsh->len & 0x0001); break; case RECV_WAIT_DATA: bt_cb(info->rx_skb)->pkt_type = nsh->type; /* remove PAD byte if it exists */ if (nsh->len & 0x0001) { info->rx_skb->tail--; info->rx_skb->len--; } /* remove NSH */ skb_pull(info->rx_skb, NSHL); switch (bt_cb(info->rx_skb)->pkt_type) { case 0x80: /* control data for the Nokia Card */ dtl1_control(info, info->rx_skb); break; case 0x82: case 0x83: case 0x84: /* send frame to the HCI layer */ info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type &= 0x0f; hci_recv_frame(info->rx_skb); break; default: /* unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); kfree_skb(info->rx_skb); break; } info->rx_state = RECV_WAIT_NSH; info->rx_count = NSHL; info->rx_skb = NULL; break; } } /* Make sure we don't stay here too long */ if (boguscount++ > 32) break; } while (inb(iobase + UART_LSR) & UART_LSR_DR); } static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) { dtl1_info_t *info = dev_inst; unsigned int iobase; unsigned char msr; int boguscount = 0; int iir, lsr; irqreturn_t r = IRQ_NONE; if (!info || !info->hdev) /* our irq handler is shared */ return IRQ_NONE; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif spin_lock(&(info->lock)); iir = inb(iobase + UART_IIR) & UART_IIR_ID; while (iir) { r = IRQ_HANDLED; /* Clear interrupt */ lsr = inb(iobase + UART_LSR); switch (iir) { case UART_IIR_RLSI: BT_ERR("RLSI"); break; case UART_IIR_RDI: /* Receive interrupt */ dtl1_receive(info); break; case UART_IIR_THRI: if (lsr & UART_LSR_THRE) { /* Transmitter ready for data */ dtl1_write_wakeup(info); } break; default: BT_ERR("Unhandled IIR=%#x", iir); break; } /* Make sure we don't stay here too long */ if (boguscount++ > 100) break; iir = inb(iobase + UART_IIR) & UART_IIR_ID; } msr = inb(iobase + UART_MSR); if (info->ri_latch ^ (msr & UART_MSR_RI)) { info->ri_latch = msr & UART_MSR_RI; clear_bit(XMIT_WAITING, &(info->tx_state)); dtl1_write_wakeup(info); r = IRQ_HANDLED; } spin_unlock(&(info->lock)); return r; } /* ======================== HCI interface ======================== */ static int dtl1_hci_open(struct hci_dev *hdev) { set_bit(HCI_RUNNING, &(hdev->flags)); return 0; } static int dtl1_hci_flush(struct hci_dev *hdev) { dtl1_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); return 0; } static int dtl1_hci_close(struct hci_dev *hdev) { if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) return 0; dtl1_hci_flush(hdev); return 0; } static int dtl1_hci_send_frame(struct sk_buff *skb) { dtl1_info_t *info; struct hci_dev *hdev = (struct hci_dev *)(skb->dev); struct sk_buff *s; nsh_t nsh; if (!hdev) { BT_ERR("Frame for unknown HCI device (hdev=NULL)"); return -ENODEV; } info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; nsh.type = 0x81; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; nsh.type = 0x82; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; nsh.type = 0x83; break; default: return -EILSEQ; }; nsh.zero = 0; nsh.len = skb->len; s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC); if (!s) return -ENOMEM; skb_reserve(s, NSHL); skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len); if (skb->len & 0x0001) *skb_put(s, 1) = 0; /* PAD */ /* Prepend skb with Nokia frame header and queue */ memcpy(skb_push(s, NSHL), &nsh, NSHL); skb_queue_tail(&(info->txq), s); dtl1_write_wakeup(info); kfree_skb(skb); return 0; } static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } /* ======================== Card services HCI interaction ======================== */ static int dtl1_open(dtl1_info_t *info) { unsigned long flags; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif struct hci_dev *hdev; spin_lock_init(&(info->lock)); skb_queue_head_init(&(info->txq)); info->rx_state = RECV_WAIT_NSH; info->rx_count = NSHL; info->rx_skb = NULL; set_bit(XMIT_WAITING, &(info->tx_state)); /* Initialize HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); return -ENOMEM; } info->hdev = hdev; hdev->bus = HCI_PCCARD; hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = dtl1_hci_open; hdev->close = dtl1_hci_close; hdev->flush = dtl1_hci_flush; hdev->send = dtl1_hci_send_frame; hdev->ioctl = dtl1_hci_ioctl; spin_lock_irqsave(&(info->lock), flags); /* Reset UART */ outb(0, iobase + UART_MCR); /* Turn off interrupts */ outb(0, iobase + UART_IER); /* Initialize UART */ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR) & UART_MSR_RI; #else info->ri_latch = inb(info->p_dev->io.BasePort1 + UART_MSR) & UART_MSR_RI; #endif /* Turn on interrupts */ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); spin_unlock_irqrestore(&(info->lock), flags); /* Timeout before it is safe to send the first HCI packet */ msleep(2000); /* Register HCI device */ if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); info->hdev = NULL; hci_free_dev(hdev); return -ENODEV; } return 0; } static int dtl1_close(dtl1_info_t *info) { unsigned long flags; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif struct hci_dev *hdev = info->hdev; if (!hdev) return -ENODEV; dtl1_hci_close(hdev); spin_lock_irqsave(&(info->lock), flags); /* Reset UART */ outb(0, iobase + UART_MCR); /* Turn off interrupts */ outb(0, iobase + UART_IER); spin_unlock_irqrestore(&(info->lock), flags); hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; } static int dtl1_probe(struct pcmcia_device *link) { dtl1_info_t *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; info->p_dev = link; link->priv = info; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; #else #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 8; #else link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1= 8; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.Handler = dtl1_interrupt; #endif link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; #endif return dtl1_config(link); } static void dtl1_detach(struct pcmcia_device *link) { dtl1_info_t *info = link->priv; dtl1_close(info); pcmcia_disable_device(link); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data) { if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8)) return -ENODEV; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; return pcmcia_request_io(p_dev); } #else static int dtl1_confcheck(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { if ((cf->io.nwin != 1) || (cf->io.win[0].len <= 8)) return -ENODEV; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->resource[0]->start = cf->io.win[0].base; p_dev->resource[0]->end = cf->io.win[0].len; /*yo */ p_dev->io_lines = cf->io.flags & CISTPL_IO_LINES_MASK; return pcmcia_request_io(p_dev); #else p_dev->io.BasePort1 = cf->io.win[0].base; p_dev->io.NumPorts1 = cf->io.win[0].len; /*yo */ p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; return pcmcia_request_io(p_dev, &p_dev->io); #endif } #endif static int dtl1_config(struct pcmcia_device *link) { dtl1_info_t *info = link->priv; int ret; /* Look for a generic full-sized window */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->end = 8; #else link->io.NumPorts1 = 8; #endif ret = pcmcia_loop_config(link, dtl1_confcheck, NULL); if (ret) goto failed; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = pcmcia_request_irq(link, dtl1_interrupt); if (ret) goto failed; #else ret = pcmcia_request_irq(link, &link->irq); if (ret != 0) link->irq.AssignedIRQ = 0; #endif ret = pcmcia_enable_device(link); if (ret) goto failed; ret = dtl1_open(info); if (ret) goto failed; return 0; failed: dtl1_detach(link); return ret; } static const struct pcmcia_device_id dtl1_ids[] = { PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d), PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82), PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863), PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3), PCMCIA_DEVICE_NULL }; MODULE_DEVICE_TABLE(pcmcia, dtl1_ids); static struct pcmcia_driver dtl1_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "dtl1_cs", #else .drv = { .name = "dtl1_cs", }, #endif .probe = dtl1_probe, .remove = dtl1_detach, .id_table = dtl1_ids, }; static int __init init_dtl1_cs(void) { return pcmcia_register_driver(&dtl1_driver); } static void __exit exit_dtl1_cs(void) { pcmcia_unregister_driver(&dtl1_driver); } module_init(init_dtl1_cs); module_exit(exit_dtl1_cs); compat-drivers-2012-09-18/drivers/bluetooth/btuart_cs.c0000644000175000017500000004303112026211315022176 0ustar mcgrofmcgrof/* * * Driver for Bluetooth PCMCIA cards with HCI UART interface * * Copyright (C) 2001-2002 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The initial developer of the original code is David A. Hinds * . Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ======================== Module parameters ======================== */ MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth driver for Bluetooth PCMCIA cards with HCI UART interface"); MODULE_LICENSE("GPL"); /* ======================== Local structures ======================== */ typedef struct btuart_info_t { struct pcmcia_device *p_dev; struct hci_dev *hdev; spinlock_t lock; /* For serializing operations */ struct sk_buff_head txq; unsigned long tx_state; unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; } btuart_info_t; static int btuart_config(struct pcmcia_device *link); static void btuart_release(struct pcmcia_device *link); static void btuart_detach(struct pcmcia_device *p_dev); /* Maximum baud rate */ #define SPEED_MAX 115200 /* Default baud rate: 57600, 115200, 230400 or 460800 */ #define DEFAULT_BAUD_RATE 115200 /* Transmit states */ #define XMIT_SENDING 1 #define XMIT_WAKEUP 2 #define XMIT_WAITING 8 /* Receiver states */ #define RECV_WAIT_PACKET_TYPE 0 #define RECV_WAIT_EVENT_HEADER 1 #define RECV_WAIT_ACL_HEADER 2 #define RECV_WAIT_SCO_HEADER 3 #define RECV_WAIT_DATA 4 /* ======================== Interrupt handling ======================== */ static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) { int actual = 0; /* Tx FIFO should be empty */ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) return 0; /* Fill FIFO with current frame */ while ((fifo_size-- > 0) && (actual < len)) { /* Transmit next byte */ outb(buf[actual], iobase + UART_TX); actual++; } return actual; } static void btuart_write_wakeup(btuart_info_t *info) { if (!info) { BT_ERR("Unknown device"); return; } if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { set_bit(XMIT_WAKEUP, &(info->tx_state)); return; } do { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif register struct sk_buff *skb; int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); if (!pcmcia_dev_present(info->p_dev)) return; if (!(skb = skb_dequeue(&(info->txq)))) break; /* Send frame */ len = btuart_write(iobase, 16, skb->data, skb->len); set_bit(XMIT_WAKEUP, &(info->tx_state)); if (len == skb->len) { kfree_skb(skb); } else { skb_pull(skb, len); skb_queue_head(&(info->txq), skb); } info->hdev->stat.byte_tx += len; } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); clear_bit(XMIT_SENDING, &(info->tx_state)); } static void btuart_receive(btuart_info_t *info) { unsigned int iobase; int boguscount = 0; if (!info) { BT_ERR("Unknown device"); return; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif do { info->hdev->stat.byte_rx++; /* Allocate packet */ if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { BT_ERR("Can't allocate mem for new packet"); return; } } if (info->rx_state == RECV_WAIT_PACKET_TYPE) { info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX); switch (bt_cb(info->rx_skb)->pkt_type) { case HCI_EVENT_PKT: info->rx_state = RECV_WAIT_EVENT_HEADER; info->rx_count = HCI_EVENT_HDR_SIZE; break; case HCI_ACLDATA_PKT: info->rx_state = RECV_WAIT_ACL_HEADER; info->rx_count = HCI_ACL_HDR_SIZE; break; case HCI_SCODATA_PKT: info->rx_state = RECV_WAIT_SCO_HEADER; info->rx_count = HCI_SCO_HDR_SIZE; break; default: /* Unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); info->hdev->stat.err_rx++; clear_bit(HCI_RUNNING, &(info->hdev->flags)); kfree_skb(info->rx_skb); info->rx_skb = NULL; break; } } else { *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); info->rx_count--; if (info->rx_count == 0) { int dlen; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; case RECV_WAIT_DATA: hci_recv_frame(info->rx_skb); info->rx_skb = NULL; break; } } } /* Make sure we don't stay here too long */ if (boguscount++ > 16) break; } while (inb(iobase + UART_LSR) & UART_LSR_DR); } static irqreturn_t btuart_interrupt(int irq, void *dev_inst) { btuart_info_t *info = dev_inst; unsigned int iobase; int boguscount = 0; int iir, lsr; irqreturn_t r = IRQ_NONE; if (!info || !info->hdev) /* our irq handler is shared */ return IRQ_NONE; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif spin_lock(&(info->lock)); iir = inb(iobase + UART_IIR) & UART_IIR_ID; while (iir) { r = IRQ_HANDLED; /* Clear interrupt */ lsr = inb(iobase + UART_LSR); switch (iir) { case UART_IIR_RLSI: BT_ERR("RLSI"); break; case UART_IIR_RDI: /* Receive interrupt */ btuart_receive(info); break; case UART_IIR_THRI: if (lsr & UART_LSR_THRE) { /* Transmitter ready for data */ btuart_write_wakeup(info); } break; default: BT_ERR("Unhandled IIR=%#x", iir); break; } /* Make sure we don't stay here too long */ if (boguscount++ > 100) break; iir = inb(iobase + UART_IIR) & UART_IIR_ID; } spin_unlock(&(info->lock)); return r; } static void btuart_change_speed(btuart_info_t *info, unsigned int speed) { unsigned long flags; unsigned int iobase; int fcr; /* FIFO control reg */ int lcr; /* Line control reg */ int divisor; if (!info) { BT_ERR("Unknown device"); return; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif spin_lock_irqsave(&(info->lock), flags); /* Turn off interrupts */ outb(0, iobase + UART_IER); divisor = SPEED_MAX / speed; fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; /* * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and * almost 1,7 ms at 19200 bps. At speeds above that we can just forget * about this timeout since it will always be fast enough. */ if (speed < 38400) fcr |= UART_FCR_TRIGGER_1; else fcr |= UART_FCR_TRIGGER_14; /* Bluetooth cards use 8N1 */ lcr = UART_LCR_WLEN8; outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */ outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */ outb(divisor >> 8, iobase + UART_DLM); outb(lcr, iobase + UART_LCR); /* Set 8N1 */ outb(fcr, iobase + UART_FCR); /* Enable FIFO's */ /* Turn on interrupts */ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); spin_unlock_irqrestore(&(info->lock), flags); } /* ======================== HCI interface ======================== */ static int btuart_hci_flush(struct hci_dev *hdev) { btuart_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); return 0; } static int btuart_hci_open(struct hci_dev *hdev) { set_bit(HCI_RUNNING, &(hdev->flags)); return 0; } static int btuart_hci_close(struct hci_dev *hdev) { if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) return 0; btuart_hci_flush(hdev); return 0; } static int btuart_hci_send_frame(struct sk_buff *skb) { btuart_info_t *info; struct hci_dev *hdev = (struct hci_dev *)(skb->dev); if (!hdev) { BT_ERR("Frame for unknown HCI device (hdev=NULL)"); return -ENODEV; } info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; } /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&(info->txq), skb); btuart_write_wakeup(info); return 0; } static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } /* ======================== Card services HCI interaction ======================== */ static int btuart_open(btuart_info_t *info) { unsigned long flags; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif struct hci_dev *hdev; spin_lock_init(&(info->lock)); skb_queue_head_init(&(info->txq)); info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = NULL; /* Initialize HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); return -ENOMEM; } info->hdev = hdev; hdev->bus = HCI_PCCARD; hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = btuart_hci_open; hdev->close = btuart_hci_close; hdev->flush = btuart_hci_flush; hdev->send = btuart_hci_send_frame; hdev->ioctl = btuart_hci_ioctl; spin_lock_irqsave(&(info->lock), flags); /* Reset UART */ outb(0, iobase + UART_MCR); /* Turn off interrupts */ outb(0, iobase + UART_IER); /* Initialize UART */ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); /* Turn on interrupts */ // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); spin_unlock_irqrestore(&(info->lock), flags); btuart_change_speed(info, DEFAULT_BAUD_RATE); /* Timeout before it is safe to send the first HCI packet */ msleep(1000); /* Register HCI device */ if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); info->hdev = NULL; hci_free_dev(hdev); return -ENODEV; } return 0; } static int btuart_close(btuart_info_t *info) { unsigned long flags; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif struct hci_dev *hdev = info->hdev; if (!hdev) return -ENODEV; btuart_hci_close(hdev); spin_lock_irqsave(&(info->lock), flags); /* Reset UART */ outb(0, iobase + UART_MCR); /* Turn off interrupts */ outb(0, iobase + UART_IER); spin_unlock_irqrestore(&(info->lock), flags); hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; } static int btuart_probe(struct pcmcia_device *link) { btuart_info_t *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; info->p_dev = link; link->priv = info; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; #else #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 8; #else link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1= 8; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.Handler = btuart_interrupt; #endif link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; #endif return btuart_config(link); } static void btuart_detach(struct pcmcia_device *link) { btuart_release(link); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data) { int *try = priv_data; if (!try) p_dev->io_lines = 16; if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) return -EINVAL; p_dev->resource[0]->end = 8; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; return pcmcia_request_io(p_dev); } static int btuart_check_config_notpicky(struct pcmcia_device *p_dev, void *priv_data) { static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; if (p_dev->io_lines > 3) return -ENODEV; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; p_dev->resource[0]->end = 8; for (j = 0; j < 5; j++) { p_dev->resource[0]->start = base[j]; p_dev->io_lines = base[j] ? 16 : 3; if (!pcmcia_request_io(p_dev)) return 0; } return -ENODEV; } #else static int btuart_check_config(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { int *try = priv_data; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; #endif if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->resource[0]->start = cf->io.win[0].base; if (!pcmcia_request_io(p_dev)) #else p_dev->io.BasePort1 = cf->io.win[0].base; p_dev->io.IOAddrLines = (*try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; if (!pcmcia_request_io(p_dev, &p_dev->io)) #endif return 0; } return -ENODEV; } static int btuart_check_config_notpicky(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { for (j = 0; j < 5; j++) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->resource[0]->start = base[j]; p_dev->io_lines = base[j] ? 16 : 3; if (!pcmcia_request_io(p_dev)) #else p_dev->io.BasePort1 = base[j]; p_dev->io.IOAddrLines = base[j] ? 16 : 3; if (!pcmcia_request_io(p_dev, &p_dev->io)) #endif return 0; } } return -ENODEV; } #endif static int btuart_config(struct pcmcia_device *link) { btuart_info_t *info = link->priv; int i; int try; /* First pass: look for a config entry that looks normal. Two tries: without IO aliases, then with aliases */ for (try = 0; try < 2; try++) if (!pcmcia_loop_config(link, btuart_check_config, &try)) goto found_port; /* Second pass: try to find an entry that isn't picky about its base address, then try to grab any standard serial port address, and finally try to get any free port. */ if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL)) goto found_port; BT_ERR("No usable port range found"); goto failed; found_port: #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = pcmcia_request_irq(link, btuart_interrupt); if (i != 0) goto failed; #else i = pcmcia_request_irq(link, &link->irq); if (i != 0) link->irq.AssignedIRQ = 0; #endif i = pcmcia_enable_device(link); if (i != 0) goto failed; if (btuart_open(info) != 0) goto failed; return 0; failed: btuart_release(link); return -ENODEV; } static void btuart_release(struct pcmcia_device *link) { btuart_info_t *info = link->priv; btuart_close(info); pcmcia_disable_device(link); } static const struct pcmcia_device_id btuart_ids[] = { /* don't use this driver. Use serial_cs + hci_uart instead */ PCMCIA_DEVICE_NULL }; MODULE_DEVICE_TABLE(pcmcia, btuart_ids); static struct pcmcia_driver btuart_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "btuart_cs", #else .drv = { .name = "btuart_cs", }, #endif .probe = btuart_probe, .remove = btuart_detach, .id_table = btuart_ids, }; static int __init init_btuart_cs(void) { return pcmcia_register_driver(&btuart_driver); } static void __exit exit_btuart_cs(void) { pcmcia_unregister_driver(&btuart_driver); } module_init(init_btuart_cs); module_exit(exit_btuart_cs); compat-drivers-2012-09-18/drivers/bluetooth/bt3c_cs.c0000644000175000017500000004432212026211315021534 0ustar mcgrofmcgrof/* * * Driver for the 3Com Bluetooth PCMCIA card * * Copyright (C) 2001-2002 Marcel Holtmann * Jose Orlando Pereira * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The initial developer of the original code is David A. Hinds * . Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ======================== Module parameters ======================== */ MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("BT3CPCC.bin"); /* ======================== Local structures ======================== */ typedef struct bt3c_info_t { struct pcmcia_device *p_dev; struct hci_dev *hdev; spinlock_t lock; /* For serializing operations */ struct sk_buff_head txq; unsigned long tx_state; unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; } bt3c_info_t; static int bt3c_config(struct pcmcia_device *link); static void bt3c_release(struct pcmcia_device *link); static void bt3c_detach(struct pcmcia_device *p_dev); /* Transmit states */ #define XMIT_SENDING 1 #define XMIT_WAKEUP 2 #define XMIT_WAITING 8 /* Receiver states */ #define RECV_WAIT_PACKET_TYPE 0 #define RECV_WAIT_EVENT_HEADER 1 #define RECV_WAIT_ACL_HEADER 2 #define RECV_WAIT_SCO_HEADER 3 #define RECV_WAIT_DATA 4 /* ======================== Special I/O functions ======================== */ #define DATA_L 0 #define DATA_H 1 #define ADDR_L 2 #define ADDR_H 3 #define CONTROL 4 static inline void bt3c_address(unsigned int iobase, unsigned short addr) { outb(addr & 0xff, iobase + ADDR_L); outb((addr >> 8) & 0xff, iobase + ADDR_H); } static inline void bt3c_put(unsigned int iobase, unsigned short value) { outb(value & 0xff, iobase + DATA_L); outb((value >> 8) & 0xff, iobase + DATA_H); } static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value) { bt3c_address(iobase, addr); bt3c_put(iobase, value); } static inline unsigned short bt3c_get(unsigned int iobase) { unsigned short value = inb(iobase + DATA_L); value |= inb(iobase + DATA_H) << 8; return value; } static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr) { bt3c_address(iobase, addr); return bt3c_get(iobase); } /* ======================== Interrupt handling ======================== */ static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) { int actual = 0; bt3c_address(iobase, 0x7080); /* Fill FIFO with current frame */ while (actual < len) { /* Transmit next byte */ bt3c_put(iobase, buf[actual]); actual++; } bt3c_io_write(iobase, 0x7005, actual); return actual; } static void bt3c_write_wakeup(bt3c_info_t *info) { if (!info) { BT_ERR("Unknown device"); return; } if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) return; do { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif register struct sk_buff *skb; int len; if (!pcmcia_dev_present(info->p_dev)) break; if (!(skb = skb_dequeue(&(info->txq)))) { clear_bit(XMIT_SENDING, &(info->tx_state)); break; } /* Send frame */ len = bt3c_write(iobase, 256, skb->data, skb->len); if (len != skb->len) { BT_ERR("Very strange"); } kfree_skb(skb); info->hdev->stat.byte_tx += len; } while (0); } static void bt3c_receive(bt3c_info_t *info) { unsigned int iobase; int size = 0, avail; if (!info) { BT_ERR("Unknown device"); return; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif avail = bt3c_read(iobase, 0x7006); //printk("bt3c_cs: receiving %d bytes\n", avail); bt3c_address(iobase, 0x7480); while (size < avail) { size++; info->hdev->stat.byte_rx++; /* Allocate packet */ if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { BT_ERR("Can't allocate mem for new packet"); return; } } if (info->rx_state == RECV_WAIT_PACKET_TYPE) { info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L); inb(iobase + DATA_H); //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type); switch (bt_cb(info->rx_skb)->pkt_type) { case HCI_EVENT_PKT: info->rx_state = RECV_WAIT_EVENT_HEADER; info->rx_count = HCI_EVENT_HDR_SIZE; break; case HCI_ACLDATA_PKT: info->rx_state = RECV_WAIT_ACL_HEADER; info->rx_count = HCI_ACL_HDR_SIZE; break; case HCI_SCODATA_PKT: info->rx_state = RECV_WAIT_SCO_HEADER; info->rx_count = HCI_SCO_HDR_SIZE; break; default: /* Unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); info->hdev->stat.err_rx++; clear_bit(HCI_RUNNING, &(info->hdev->flags)); kfree_skb(info->rx_skb); info->rx_skb = NULL; break; } } else { __u8 x = inb(iobase + DATA_L); *skb_put(info->rx_skb, 1) = x; inb(iobase + DATA_H); info->rx_count--; if (info->rx_count == 0) { int dlen; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; case RECV_WAIT_DATA: hci_recv_frame(info->rx_skb); info->rx_skb = NULL; break; } } } } bt3c_io_write(iobase, 0x7006, 0x0000); } static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) { bt3c_info_t *info = dev_inst; unsigned int iobase; int iir; irqreturn_t r = IRQ_NONE; if (!info || !info->hdev) /* our irq handler is shared */ return IRQ_NONE; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif spin_lock(&(info->lock)); iir = inb(iobase + CONTROL); if (iir & 0x80) { int stat = bt3c_read(iobase, 0x7001); if ((stat & 0xff) == 0x7f) { BT_ERR("Very strange (stat=0x%04x)", stat); } else if ((stat & 0xff) != 0xff) { if (stat & 0x0020) { int status = bt3c_read(iobase, 0x7002) & 0x10; BT_INFO("%s: Antenna %s", info->hdev->name, status ? "out" : "in"); } if (stat & 0x0001) bt3c_receive(info); if (stat & 0x0002) { //BT_ERR("Ack (stat=0x%04x)", stat); clear_bit(XMIT_SENDING, &(info->tx_state)); bt3c_write_wakeup(info); } bt3c_io_write(iobase, 0x7001, 0x0000); outb(iir, iobase + CONTROL); } r = IRQ_HANDLED; } spin_unlock(&(info->lock)); return r; } /* ======================== HCI interface ======================== */ static int bt3c_hci_flush(struct hci_dev *hdev) { bt3c_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); return 0; } static int bt3c_hci_open(struct hci_dev *hdev) { set_bit(HCI_RUNNING, &(hdev->flags)); return 0; } static int bt3c_hci_close(struct hci_dev *hdev) { if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) return 0; bt3c_hci_flush(hdev); return 0; } static int bt3c_hci_send_frame(struct sk_buff *skb) { bt3c_info_t *info; struct hci_dev *hdev = (struct hci_dev *)(skb->dev); unsigned long flags; if (!hdev) { BT_ERR("Frame for unknown HCI device (hdev=NULL)"); return -ENODEV; } info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; }; /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&(info->txq), skb); spin_lock_irqsave(&(info->lock), flags); bt3c_write_wakeup(info); spin_unlock_irqrestore(&(info->lock), flags); return 0; } static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } /* ======================== Card services HCI interaction ======================== */ static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware, int count) { char *ptr = (char *) firmware; char b[9]; unsigned int iobase, size, addr, fcs, tmp; int i, err = 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif /* Reset */ bt3c_io_write(iobase, 0x8040, 0x0404); bt3c_io_write(iobase, 0x8040, 0x0400); udelay(1); bt3c_io_write(iobase, 0x8040, 0x0404); udelay(17); /* Load */ while (count) { if (ptr[0] != 'S') { BT_ERR("Bad address in firmware"); err = -EFAULT; goto error; } memset(b, 0, sizeof(b)); memcpy(b, ptr + 2, 2); size = simple_strtoul(b, NULL, 16); memset(b, 0, sizeof(b)); memcpy(b, ptr + 4, 8); addr = simple_strtoul(b, NULL, 16); memset(b, 0, sizeof(b)); memcpy(b, ptr + (size * 2) + 2, 2); fcs = simple_strtoul(b, NULL, 16); memset(b, 0, sizeof(b)); for (tmp = 0, i = 0; i < size; i++) { memcpy(b, ptr + (i * 2) + 2, 2); tmp += simple_strtol(b, NULL, 16); } if (((tmp + fcs) & 0xff) != 0xff) { BT_ERR("Checksum error in firmware"); err = -EILSEQ; goto error; } if (ptr[1] == '3') { bt3c_address(iobase, addr); memset(b, 0, sizeof(b)); for (i = 0; i < (size - 4) / 2; i++) { memcpy(b, ptr + (i * 4) + 12, 4); tmp = simple_strtoul(b, NULL, 16); bt3c_put(iobase, tmp); } } ptr += (size * 2) + 6; count -= (size * 2) + 6; } udelay(17); /* Boot */ bt3c_address(iobase, 0x3000); outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL); error: udelay(17); /* Clear */ bt3c_io_write(iobase, 0x7006, 0x0000); bt3c_io_write(iobase, 0x7005, 0x0000); bt3c_io_write(iobase, 0x7001, 0x0000); return err; } static int bt3c_open(bt3c_info_t *info) { const struct firmware *firmware; struct hci_dev *hdev; int err; spin_lock_init(&(info->lock)); skb_queue_head_init(&(info->txq)); info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = NULL; /* Initialize HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); return -ENOMEM; } info->hdev = hdev; hdev->bus = HCI_PCCARD; hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = bt3c_hci_open; hdev->close = bt3c_hci_close; hdev->flush = bt3c_hci_flush; hdev->send = bt3c_hci_send_frame; hdev->ioctl = bt3c_hci_ioctl; /* Load firmware */ err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev); if (err < 0) { BT_ERR("Firmware request failed"); goto error; } err = bt3c_load_firmware(info, firmware->data, firmware->size); release_firmware(firmware); if (err < 0) { BT_ERR("Firmware loading failed"); goto error; } /* Timeout before it is safe to send the first HCI packet */ msleep(1000); /* Register HCI device */ err = hci_register_dev(hdev); if (err < 0) { BT_ERR("Can't register HCI device"); goto error; } return 0; error: info->hdev = NULL; hci_free_dev(hdev); return err; } static int bt3c_close(bt3c_info_t *info) { struct hci_dev *hdev = info->hdev; if (!hdev) return -ENODEV; bt3c_hci_close(hdev); hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; } static int bt3c_probe(struct pcmcia_device *link) { bt3c_info_t *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; info->p_dev = link; link->priv = info; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; #else #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 8; #else link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1= 8; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.Handler = bt3c_interrupt; #endif link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; #endif return bt3c_config(link); } static void bt3c_detach(struct pcmcia_device *link) { bt3c_release(link); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data) { int *try = priv_data; if (!try) p_dev->io_lines = 16; if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) return -EINVAL; p_dev->resource[0]->end = 8; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; return pcmcia_request_io(p_dev); } static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev, void *priv_data) { static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; if (p_dev->io_lines > 3) return -ENODEV; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; p_dev->resource[0]->end = 8; for (j = 0; j < 5; j++) { p_dev->resource[0]->start = base[j]; p_dev->io_lines = base[j] ? 16 : 3; if (!pcmcia_request_io(p_dev)) return 0; } return -ENODEV; } #else static int bt3c_check_config(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { unsigned long try = (unsigned long) priv_data; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; #endif if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->resource[0]->start = cf->io.win[0].base; if (!pcmcia_request_io(p_dev)) #else p_dev->io.BasePort1 = cf->io.win[0].base; p_dev->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; if (!pcmcia_request_io(p_dev, &p_dev->io)) #endif return 0; } return -ENODEV; } static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { for (j = 0; j < 5; j++) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->resource[0]->start = base[j]; p_dev->io_lines = base[j] ? 16 : 3; if (!pcmcia_request_io(p_dev)) #else p_dev->io.BasePort1 = base[j]; p_dev->io.IOAddrLines = base[j] ? 16 : 3; if (!pcmcia_request_io(p_dev, &p_dev->io)) #endif return 0; } } return -ENODEV; } #endif static int bt3c_config(struct pcmcia_device *link) { bt3c_info_t *info = link->priv; int i; unsigned long try; /* First pass: look for a config entry that looks normal. Two tries: without IO aliases, then with aliases */ for (try = 0; try < 2; try++) if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try)) goto found_port; /* Second pass: try to find an entry that isn't picky about its base address, then try to grab any standard serial port address, and finally try to get any free port. */ if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL)) goto found_port; BT_ERR("No usable port range found"); goto failed; found_port: #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = pcmcia_request_irq(link, &bt3c_interrupt); if (i != 0) goto failed; #else i = pcmcia_request_irq(link, &link->irq); if (i != 0) link->irq.AssignedIRQ = 0; #endif i = pcmcia_enable_device(link); if (i != 0) goto failed; if (bt3c_open(info) != 0) goto failed; return 0; failed: bt3c_release(link); return -ENODEV; } static void bt3c_release(struct pcmcia_device *link) { bt3c_info_t *info = link->priv; bt3c_close(info); pcmcia_disable_device(link); } static const struct pcmcia_device_id bt3c_ids[] = { PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02), PCMCIA_DEVICE_NULL }; MODULE_DEVICE_TABLE(pcmcia, bt3c_ids); static struct pcmcia_driver bt3c_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "bt3c_cs", #else .drv = { .name = "bt3c_cs", }, #endif .probe = bt3c_probe, .remove = bt3c_detach, .id_table = bt3c_ids, }; static int __init init_bt3c_cs(void) { return pcmcia_register_driver(&bt3c_driver); } static void __exit exit_bt3c_cs(void) { pcmcia_unregister_driver(&bt3c_driver); } module_init(init_bt3c_cs); module_exit(exit_bt3c_cs); compat-drivers-2012-09-18/drivers/bluetooth/bluecard_cs.c0000644000175000017500000005473412026211315022472 0ustar mcgrofmcgrof/* * * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041) * * Copyright (C) 2001-2002 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The initial developer of the original code is David A. Hinds * . Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ======================== Module parameters ======================== */ MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)"); MODULE_LICENSE("GPL"); /* ======================== Local structures ======================== */ typedef struct bluecard_info_t { struct pcmcia_device *p_dev; struct hci_dev *hdev; spinlock_t lock; /* For serializing operations */ struct timer_list timer; /* For LED control */ struct sk_buff_head txq; unsigned long tx_state; unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; unsigned char ctrl_reg; unsigned long hw_state; /* Status of the hardware and LED control */ } bluecard_info_t; static int bluecard_config(struct pcmcia_device *link); static void bluecard_release(struct pcmcia_device *link); static void bluecard_detach(struct pcmcia_device *p_dev); /* Default baud rate: 57600, 115200, 230400 or 460800 */ #define DEFAULT_BAUD_RATE 230400 /* Hardware states */ #define CARD_READY 1 #define CARD_HAS_PCCARD_ID 4 #define CARD_HAS_POWER_LED 5 #define CARD_HAS_ACTIVITY_LED 6 /* Transmit states */ #define XMIT_SENDING 1 #define XMIT_WAKEUP 2 #define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */ #define XMIT_BUF_ONE_READY 6 #define XMIT_BUF_TWO_READY 7 #define XMIT_SENDING_READY 8 /* Receiver states */ #define RECV_WAIT_PACKET_TYPE 0 #define RECV_WAIT_EVENT_HEADER 1 #define RECV_WAIT_ACL_HEADER 2 #define RECV_WAIT_SCO_HEADER 3 #define RECV_WAIT_DATA 4 /* Special packet types */ #define PKT_BAUD_RATE_57600 0x80 #define PKT_BAUD_RATE_115200 0x81 #define PKT_BAUD_RATE_230400 0x82 #define PKT_BAUD_RATE_460800 0x83 /* These are the register offsets */ #define REG_COMMAND 0x20 #define REG_INTERRUPT 0x21 #define REG_CONTROL 0x22 #define REG_RX_CONTROL 0x24 #define REG_CARD_RESET 0x30 #define REG_LED_CTRL 0x30 /* REG_COMMAND */ #define REG_COMMAND_TX_BUF_ONE 0x01 #define REG_COMMAND_TX_BUF_TWO 0x02 #define REG_COMMAND_RX_BUF_ONE 0x04 #define REG_COMMAND_RX_BUF_TWO 0x08 #define REG_COMMAND_RX_WIN_ONE 0x00 #define REG_COMMAND_RX_WIN_TWO 0x10 /* REG_CONTROL */ #define REG_CONTROL_BAUD_RATE_57600 0x00 #define REG_CONTROL_BAUD_RATE_115200 0x01 #define REG_CONTROL_BAUD_RATE_230400 0x02 #define REG_CONTROL_BAUD_RATE_460800 0x03 #define REG_CONTROL_RTS 0x04 #define REG_CONTROL_BT_ON 0x08 #define REG_CONTROL_BT_RESET 0x10 #define REG_CONTROL_BT_RES_PU 0x20 #define REG_CONTROL_INTERRUPT 0x40 #define REG_CONTROL_CARD_RESET 0x80 /* REG_RX_CONTROL */ #define RTS_LEVEL_SHIFT_BITS 0x02 /* ======================== LED handling routines ======================== */ static void bluecard_activity_led_timeout(u_long arg) { bluecard_info_t *info = (bluecard_info_t *)arg; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) return; if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) { /* Disable activity LED */ outb(0x08 | 0x20, iobase + 0x30); } else { /* Disable power LED */ outb(0x00, iobase + 0x30); } } static void bluecard_enable_activity_led(bluecard_info_t *info) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) return; if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) { /* Enable activity LED */ outb(0x10 | 0x40, iobase + 0x30); /* Stop the LED after HZ/4 */ mod_timer(&(info->timer), jiffies + HZ / 4); } else { /* Enable power LED */ outb(0x08 | 0x20, iobase + 0x30); /* Stop the LED after HZ/2 */ mod_timer(&(info->timer), jiffies + HZ / 2); } } /* ======================== Interrupt handling ======================== */ static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len) { int i, actual; actual = (len > 15) ? 15 : len; outb_p(actual, iobase + offset); for (i = 0; i < actual; i++) outb_p(buf[i], iobase + offset + i + 1); return actual; } static void bluecard_write_wakeup(bluecard_info_t *info) { if (!info) { BT_ERR("Unknown device"); return; } if (!test_bit(XMIT_SENDING_READY, &(info->tx_state))) return; if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { set_bit(XMIT_WAKEUP, &(info->tx_state)); return; } do { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif unsigned int offset; unsigned char command; unsigned long ready_bit; register struct sk_buff *skb; int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); if (!pcmcia_dev_present(info->p_dev)) return; if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) { if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state))) break; offset = 0x10; command = REG_COMMAND_TX_BUF_TWO; ready_bit = XMIT_BUF_TWO_READY; } else { if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state))) break; offset = 0x00; command = REG_COMMAND_TX_BUF_ONE; ready_bit = XMIT_BUF_ONE_READY; } if (!(skb = skb_dequeue(&(info->txq)))) break; if (bt_cb(skb)->pkt_type & 0x80) { /* Disable RTS */ info->ctrl_reg |= REG_CONTROL_RTS; outb(info->ctrl_reg, iobase + REG_CONTROL); } /* Activate LED */ bluecard_enable_activity_led(info); /* Send frame */ len = bluecard_write(iobase, offset, skb->data, skb->len); /* Tell the FPGA to send the data */ outb_p(command, iobase + REG_COMMAND); /* Mark the buffer as dirty */ clear_bit(ready_bit, &(info->tx_state)); if (bt_cb(skb)->pkt_type & 0x80) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); DEFINE_WAIT(wait); unsigned char baud_reg; switch (bt_cb(skb)->pkt_type) { case PKT_BAUD_RATE_460800: baud_reg = REG_CONTROL_BAUD_RATE_460800; break; case PKT_BAUD_RATE_230400: baud_reg = REG_CONTROL_BAUD_RATE_230400; break; case PKT_BAUD_RATE_115200: baud_reg = REG_CONTROL_BAUD_RATE_115200; break; case PKT_BAUD_RATE_57600: /* Fall through... */ default: baud_reg = REG_CONTROL_BAUD_RATE_57600; break; } /* Wait until the command reaches the baseband */ prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE); schedule_timeout(HZ/10); finish_wait(&wq, &wait); /* Set baud on baseband */ info->ctrl_reg &= ~0x03; info->ctrl_reg |= baud_reg; outb(info->ctrl_reg, iobase + REG_CONTROL); /* Enable RTS */ info->ctrl_reg &= ~REG_CONTROL_RTS; outb(info->ctrl_reg, iobase + REG_CONTROL); /* Wait before the next HCI packet can be send */ prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE); schedule_timeout(HZ); finish_wait(&wq, &wait); } if (len == skb->len) { kfree_skb(skb); } else { skb_pull(skb, len); skb_queue_head(&(info->txq), skb); } info->hdev->stat.byte_tx += len; /* Change buffer */ change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state)); } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); clear_bit(XMIT_SENDING, &(info->tx_state)); } static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size) { int i, n, len; outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND); len = inb(iobase + offset); n = 0; i = 1; while (n < len) { if (i == 16) { outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND); i = 0; } buf[n] = inb(iobase + offset + i); n++; i++; } return len; } static void bluecard_receive(bluecard_info_t *info, unsigned int offset) { unsigned int iobase; unsigned char buf[31]; int i, len; if (!info) { BT_ERR("Unknown device"); return; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif if (test_bit(XMIT_SENDING_READY, &(info->tx_state))) bluecard_enable_activity_led(info); len = bluecard_read(iobase, offset, buf, sizeof(buf)); for (i = 0; i < len; i++) { /* Allocate packet */ if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { BT_ERR("Can't allocate mem for new packet"); return; } } if (info->rx_state == RECV_WAIT_PACKET_TYPE) { info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type = buf[i]; switch (bt_cb(info->rx_skb)->pkt_type) { case 0x00: /* init packet */ if (offset != 0x00) { set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); set_bit(XMIT_SENDING_READY, &(info->tx_state)); bluecard_write_wakeup(info); } kfree_skb(info->rx_skb); info->rx_skb = NULL; break; case HCI_EVENT_PKT: info->rx_state = RECV_WAIT_EVENT_HEADER; info->rx_count = HCI_EVENT_HDR_SIZE; break; case HCI_ACLDATA_PKT: info->rx_state = RECV_WAIT_ACL_HEADER; info->rx_count = HCI_ACL_HDR_SIZE; break; case HCI_SCODATA_PKT: info->rx_state = RECV_WAIT_SCO_HEADER; info->rx_count = HCI_SCO_HDR_SIZE; break; default: /* unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); info->hdev->stat.err_rx++; kfree_skb(info->rx_skb); info->rx_skb = NULL; break; } } else { *skb_put(info->rx_skb, 1) = buf[i]; info->rx_count--; if (info->rx_count == 0) { int dlen; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; case RECV_WAIT_DATA: hci_recv_frame(info->rx_skb); info->rx_skb = NULL; break; } } } } info->hdev->stat.byte_rx += len; } static irqreturn_t bluecard_interrupt(int irq, void *dev_inst) { bluecard_info_t *info = dev_inst; unsigned int iobase; unsigned char reg; if (!info || !info->hdev) /* our irq handler is shared */ return IRQ_NONE; if (!test_bit(CARD_READY, &(info->hw_state))) return IRQ_HANDLED; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; #else iobase = info->p_dev->io.BasePort1; #endif spin_lock(&(info->lock)); /* Disable interrupt */ info->ctrl_reg &= ~REG_CONTROL_INTERRUPT; outb(info->ctrl_reg, iobase + REG_CONTROL); reg = inb(iobase + REG_INTERRUPT); if ((reg != 0x00) && (reg != 0xff)) { if (reg & 0x04) { bluecard_receive(info, 0x00); outb(0x04, iobase + REG_INTERRUPT); outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND); } if (reg & 0x08) { bluecard_receive(info, 0x10); outb(0x08, iobase + REG_INTERRUPT); outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND); } if (reg & 0x01) { set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); outb(0x01, iobase + REG_INTERRUPT); bluecard_write_wakeup(info); } if (reg & 0x02) { set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); outb(0x02, iobase + REG_INTERRUPT); bluecard_write_wakeup(info); } } /* Enable interrupt */ info->ctrl_reg |= REG_CONTROL_INTERRUPT; outb(info->ctrl_reg, iobase + REG_CONTROL); spin_unlock(&(info->lock)); return IRQ_HANDLED; } /* ======================== Device specific HCI commands ======================== */ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) { bluecard_info_t *info = hci_get_drvdata(hdev); struct sk_buff *skb; /* Ericsson baud rate command */ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 }; if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { BT_ERR("Can't allocate mem for new packet"); return -1; } switch (baud) { case 460800: cmd[4] = 0x00; bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800; break; case 230400: cmd[4] = 0x01; bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400; break; case 115200: cmd[4] = 0x02; bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200; break; case 57600: /* Fall through... */ default: cmd[4] = 0x03; bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600; break; } memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd)); skb_queue_tail(&(info->txq), skb); bluecard_write_wakeup(info); return 0; } /* ======================== HCI interface ======================== */ static int bluecard_hci_flush(struct hci_dev *hdev) { bluecard_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); return 0; } static int bluecard_hci_open(struct hci_dev *hdev) { bluecard_info_t *info = hci_get_drvdata(hdev); if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); if (test_and_set_bit(HCI_RUNNING, &(hdev->flags))) return 0; if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif /* Enable LED */ outb(0x08 | 0x20, iobase + 0x30); } return 0; } static int bluecard_hci_close(struct hci_dev *hdev) { bluecard_info_t *info = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) return 0; bluecard_hci_flush(hdev); if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif /* Disable LED */ outb(0x00, iobase + 0x30); } return 0; } static int bluecard_hci_send_frame(struct sk_buff *skb) { bluecard_info_t *info; struct hci_dev *hdev = (struct hci_dev *)(skb->dev); if (!hdev) { BT_ERR("Frame for unknown HCI device (hdev=NULL)"); return -ENODEV; } info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; } /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&(info->txq), skb); bluecard_write_wakeup(info); return 0; } static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } /* ======================== Card services HCI interaction ======================== */ static int bluecard_open(bluecard_info_t *info) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif struct hci_dev *hdev; unsigned char id; spin_lock_init(&(info->lock)); init_timer(&(info->timer)); info->timer.function = &bluecard_activity_led_timeout; info->timer.data = (u_long)info; skb_queue_head_init(&(info->txq)); info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = NULL; /* Initialize HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); return -ENOMEM; } info->hdev = hdev; hdev->bus = HCI_PCCARD; hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = bluecard_hci_open; hdev->close = bluecard_hci_close; hdev->flush = bluecard_hci_flush; hdev->send = bluecard_hci_send_frame; hdev->ioctl = bluecard_hci_ioctl; id = inb(iobase + 0x30); if ((id & 0x0f) == 0x02) set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)); if (id & 0x10) set_bit(CARD_HAS_POWER_LED, &(info->hw_state)); if (id & 0x20) set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state)); /* Reset card */ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET; outb(info->ctrl_reg, iobase + REG_CONTROL); /* Turn FPGA off */ outb(0x80, iobase + 0x30); /* Wait some time */ msleep(10); /* Turn FPGA on */ outb(0x00, iobase + 0x30); /* Activate card */ info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU; outb(info->ctrl_reg, iobase + REG_CONTROL); /* Enable interrupt */ outb(0xff, iobase + REG_INTERRUPT); info->ctrl_reg |= REG_CONTROL_INTERRUPT; outb(info->ctrl_reg, iobase + REG_CONTROL); if ((id & 0x0f) == 0x03) { /* Disable RTS */ info->ctrl_reg |= REG_CONTROL_RTS; outb(info->ctrl_reg, iobase + REG_CONTROL); /* Set baud rate */ info->ctrl_reg |= 0x03; outb(info->ctrl_reg, iobase + REG_CONTROL); /* Enable RTS */ info->ctrl_reg &= ~REG_CONTROL_RTS; outb(info->ctrl_reg, iobase + REG_CONTROL); set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); set_bit(XMIT_SENDING_READY, &(info->tx_state)); } /* Start the RX buffers */ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND); outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND); /* Signal that the hardware is ready */ set_bit(CARD_READY, &(info->hw_state)); /* Drop TX queue */ skb_queue_purge(&(info->txq)); /* Control the point at which RTS is enabled */ outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL); /* Timeout before it is safe to send the first HCI packet */ msleep(1250); /* Register HCI device */ if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); info->hdev = NULL; hci_free_dev(hdev); return -ENODEV; } return 0; } static int bluecard_close(bluecard_info_t *info) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; #else unsigned int iobase = info->p_dev->io.BasePort1; #endif struct hci_dev *hdev = info->hdev; if (!hdev) return -ENODEV; bluecard_hci_close(hdev); clear_bit(CARD_READY, &(info->hw_state)); /* Reset card */ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET; outb(info->ctrl_reg, iobase + REG_CONTROL); /* Turn FPGA off */ outb(0x80, iobase + 0x30); hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; } static int bluecard_probe(struct pcmcia_device *link) { bluecard_info_t *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; info->p_dev = link; link->priv = info; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.Handler = bluecard_interrupt; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ; #else link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; #endif return bluecard_config(link); } static void bluecard_detach(struct pcmcia_device *link) { bluecard_release(link); } static int bluecard_config(struct pcmcia_device *link) { bluecard_info_t *info = link->priv; int i, n; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_index = 0x20; #else link->conf.ConfigIndex = 0x20; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 64; link->io_lines = 6; #else link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 64; link->io.IOAddrLines = 6; #endif for (n = 0; n < 0x400; n += 0x40) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->start = n ^ 0x300; i = pcmcia_request_io(link); #else link->io.BasePort1 = n ^ 0x300; i = pcmcia_request_io(link, &link->io); #endif if (i == 0) break; } if (i != 0) goto failed; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = pcmcia_request_irq(link, bluecard_interrupt); if (i != 0) goto failed; #else i = pcmcia_request_irq(link, &link->irq); if (i != 0) link->irq.AssignedIRQ = 0; #endif i = pcmcia_enable_device(link); if (i != 0) goto failed; if (bluecard_open(info) != 0) goto failed; return 0; failed: bluecard_release(link); return -ENODEV; } static void bluecard_release(struct pcmcia_device *link) { bluecard_info_t *info = link->priv; bluecard_close(info); del_timer(&(info->timer)); pcmcia_disable_device(link); } static const struct pcmcia_device_id bluecard_ids[] = { PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e), PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c), PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab), PCMCIA_DEVICE_NULL }; MODULE_DEVICE_TABLE(pcmcia, bluecard_ids); static struct pcmcia_driver bluecard_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "bluecard_cs", #else .drv = { .name = "bluecard_cs", }, #endif .probe = bluecard_probe, .remove = bluecard_detach, .id_table = bluecard_ids, }; static int __init init_bluecard_cs(void) { return pcmcia_register_driver(&bluecard_driver); } static void __exit exit_bluecard_cs(void) { pcmcia_unregister_driver(&bluecard_driver); } module_init(init_bluecard_cs); module_exit(exit_bluecard_cs); compat-drivers-2012-09-18/drivers/bluetooth/hci_vhci.c0000644000175000017500000001433612026211315021772 0ustar mcgrofmcgrof/* * * Bluetooth virtual HCI driver * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2006 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "1.3" static bool amp; struct vhci_data { struct hci_dev *hdev; unsigned long flags; wait_queue_head_t read_wait; struct sk_buff_head readq; }; static int vhci_open_dev(struct hci_dev *hdev) { set_bit(HCI_RUNNING, &hdev->flags); return 0; } static int vhci_close_dev(struct hci_dev *hdev) { struct vhci_data *data = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; skb_queue_purge(&data->readq); return 0; } static int vhci_flush(struct hci_dev *hdev) { struct vhci_data *data = hci_get_drvdata(hdev); skb_queue_purge(&data->readq); return 0; } static int vhci_send_frame(struct sk_buff *skb) { struct hci_dev* hdev = (struct hci_dev *) skb->dev; struct vhci_data *data; if (!hdev) { BT_ERR("Frame for unknown HCI device (hdev=NULL)"); return -ENODEV; } if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; data = hci_get_drvdata(hdev); memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&data->readq, skb); wake_up_interruptible(&data->read_wait); return 0; } static inline ssize_t vhci_get_user(struct vhci_data *data, const char __user *buf, size_t count) { struct sk_buff *skb; if (count > HCI_MAX_FRAME_SIZE) return -EINVAL; skb = bt_skb_alloc(count, GFP_KERNEL); if (!skb) return -ENOMEM; if (copy_from_user(skb_put(skb, count), buf, count)) { kfree_skb(skb); return -EFAULT; } skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = *((__u8 *) skb->data); skb_pull(skb, 1); hci_recv_frame(skb); return count; } static inline ssize_t vhci_put_user(struct vhci_data *data, struct sk_buff *skb, char __user *buf, int count) { char __user *ptr = buf; int len, total = 0; len = min_t(unsigned int, skb->len, count); if (copy_to_user(ptr, skb->data, len)) return -EFAULT; total += len; data->hdev->stat.byte_tx += len; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: data->hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: data->hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: data->hdev->stat.sco_tx++; break; } return total; } static ssize_t vhci_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { struct vhci_data *data = file->private_data; struct sk_buff *skb; ssize_t ret = 0; while (count) { skb = skb_dequeue(&data->readq); if (skb) { ret = vhci_put_user(data, skb, buf, count); if (ret < 0) skb_queue_head(&data->readq, skb); else kfree_skb(skb); break; } if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; break; } ret = wait_event_interruptible(data->read_wait, !skb_queue_empty(&data->readq)); if (ret < 0) break; } return ret; } static ssize_t vhci_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { struct vhci_data *data = file->private_data; return vhci_get_user(data, buf, count); } static unsigned int vhci_poll(struct file *file, poll_table *wait) { struct vhci_data *data = file->private_data; poll_wait(file, &data->read_wait, wait); if (!skb_queue_empty(&data->readq)) return POLLIN | POLLRDNORM; return POLLOUT | POLLWRNORM; } static int vhci_open(struct inode *inode, struct file *file) { struct vhci_data *data; struct hci_dev *hdev; data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL); if (!data) return -ENOMEM; skb_queue_head_init(&data->readq); init_waitqueue_head(&data->read_wait); hdev = hci_alloc_dev(); if (!hdev) { kfree(data); return -ENOMEM; } data->hdev = hdev; hdev->bus = HCI_VIRTUAL; hci_set_drvdata(hdev, data); if (amp) hdev->dev_type = HCI_AMP; hdev->open = vhci_open_dev; hdev->close = vhci_close_dev; hdev->flush = vhci_flush; hdev->send = vhci_send_frame; if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); kfree(data); hci_free_dev(hdev); return -EBUSY; } file->private_data = data; nonseekable_open(inode, file); return 0; } static int vhci_release(struct inode *inode, struct file *file) { struct vhci_data *data = file->private_data; struct hci_dev *hdev = data->hdev; hci_unregister_dev(hdev); hci_free_dev(hdev); file->private_data = NULL; kfree(data); return 0; } static const struct file_operations vhci_fops = { .owner = THIS_MODULE, .read = vhci_read, .write = vhci_write, .poll = vhci_poll, .open = vhci_open, .release = vhci_release, .llseek = no_llseek, }; static struct miscdevice vhci_miscdev= { .name = "vhci", .fops = &vhci_fops, .minor = MISC_DYNAMIC_MINOR, }; static int __init vhci_init(void) { BT_INFO("Virtual HCI driver ver %s", VERSION); return misc_register(&vhci_miscdev); } static void __exit vhci_exit(void) { misc_deregister(&vhci_miscdev); } module_init(vhci_init); module_exit(vhci_exit); module_param(amp, bool, 0644); MODULE_PARM_DESC(amp, "Create AMP controller device"); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/bluetooth/hci_uart.h0000644000175000017500000000556712026211315022027 0ustar mcgrofmcgrof/* * * Bluetooth HCI UART driver * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef N_HCI #define N_HCI 15 #endif /* Ioctls */ #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) #define HCIUARTGETDEVICE _IOR('U', 202, int) #define HCIUARTSETFLAGS _IOW('U', 203, int) #define HCIUARTGETFLAGS _IOR('U', 204, int) /* UART protocols */ #define HCI_UART_MAX_PROTO 6 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 #define HCI_UART_ATH3K 5 #define HCI_UART_RAW_DEVICE 0 #define HCI_UART_RESET_ON_INIT 1 #define HCI_UART_CREATE_AMP 2 #define HCI_UART_INIT_PENDING 3 struct hci_uart; struct hci_uart_proto { unsigned int id; int (*open)(struct hci_uart *hu); int (*close)(struct hci_uart *hu); int (*flush)(struct hci_uart *hu); int (*recv)(struct hci_uart *hu, void *data, int len); int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb); struct sk_buff *(*dequeue)(struct hci_uart *hu); }; struct hci_uart { struct tty_struct *tty; struct hci_dev *hdev; unsigned long flags; unsigned long hdev_flags; struct work_struct init_ready; struct hci_uart_proto *proto; void *priv; struct sk_buff *tx_skb; unsigned long tx_state; spinlock_t rx_lock; }; /* HCI_UART proto flag bits */ #define HCI_UART_PROTO_SET 0 #define HCI_UART_REGISTERED 1 /* TX states */ #define HCI_UART_SENDING 1 #define HCI_UART_TX_WAKEUP 2 int hci_uart_register_proto(struct hci_uart_proto *p); int hci_uart_unregister_proto(struct hci_uart_proto *p); int hci_uart_tx_wakeup(struct hci_uart *hu); int hci_uart_init_ready(struct hci_uart *hu); #ifdef CONFIG_BT_HCIUART_H4 int h4_init(void); int h4_deinit(void); #endif #ifdef CONFIG_BT_HCIUART_BCSP int bcsp_init(void); int bcsp_deinit(void); #endif #ifdef CONFIG_BT_HCIUART_LL int ll_init(void); int ll_deinit(void); #endif #ifdef CONFIG_BT_HCIUART_ATH3K int ath_init(void); int ath_deinit(void); #endif #ifdef CONFIG_BT_HCIUART_3WIRE int h5_init(void); int h5_deinit(void); #endif compat-drivers-2012-09-18/drivers/bluetooth/hci_ll.c0000644000175000017500000002764112026211315021453 0ustar mcgrofmcgrof/* * Texas Instruments' Bluetooth HCILL UART protocol * * HCILL (HCI Low Level) is a Texas Instruments' power management * protocol extension to H4. * * Copyright (C) 2007 Texas Instruments, Inc. * * Written by Ohad Ben-Cohen * * Acknowledgements: * This file is based on hci_h4.c, which was written * by Maxim Krasnyansky and Marcel Holtmann. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hci_uart.h" /* HCILL commands */ #define HCILL_GO_TO_SLEEP_IND 0x30 #define HCILL_GO_TO_SLEEP_ACK 0x31 #define HCILL_WAKE_UP_IND 0x32 #define HCILL_WAKE_UP_ACK 0x33 /* HCILL receiver States */ #define HCILL_W4_PACKET_TYPE 0 #define HCILL_W4_EVENT_HDR 1 #define HCILL_W4_ACL_HDR 2 #define HCILL_W4_SCO_HDR 3 #define HCILL_W4_DATA 4 /* HCILL states */ enum hcill_states_e { HCILL_ASLEEP, HCILL_ASLEEP_TO_AWAKE, HCILL_AWAKE, HCILL_AWAKE_TO_ASLEEP }; struct hcill_cmd { u8 cmd; } __packed; struct ll_struct { unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; struct sk_buff_head txq; spinlock_t hcill_lock; /* HCILL state lock */ unsigned long hcill_state; /* HCILL power state */ struct sk_buff_head tx_wait_q; /* HCILL wait queue */ }; /* * Builds and sends an HCILL command packet. * These are very simple packets with only 1 cmd byte */ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu) { int err = 0; struct sk_buff *skb = NULL; struct ll_struct *ll = hu->priv; struct hcill_cmd *hcill_packet; BT_DBG("hu %p cmd 0x%x", hu, cmd); /* allocate packet */ skb = bt_skb_alloc(1, GFP_ATOMIC); if (!skb) { BT_ERR("cannot allocate memory for HCILL packet"); err = -ENOMEM; goto out; } /* prepare packet */ hcill_packet = (struct hcill_cmd *) skb_put(skb, 1); hcill_packet->cmd = cmd; skb->dev = (void *) hu->hdev; /* send packet */ skb_queue_tail(&ll->txq, skb); out: return err; } /* Initialize protocol */ static int ll_open(struct hci_uart *hu) { struct ll_struct *ll; BT_DBG("hu %p", hu); ll = kzalloc(sizeof(*ll), GFP_KERNEL); if (!ll) return -ENOMEM; skb_queue_head_init(&ll->txq); skb_queue_head_init(&ll->tx_wait_q); spin_lock_init(&ll->hcill_lock); ll->hcill_state = HCILL_AWAKE; hu->priv = ll; return 0; } /* Flush protocol data */ static int ll_flush(struct hci_uart *hu) { struct ll_struct *ll = hu->priv; BT_DBG("hu %p", hu); skb_queue_purge(&ll->tx_wait_q); skb_queue_purge(&ll->txq); return 0; } /* Close protocol */ static int ll_close(struct hci_uart *hu) { struct ll_struct *ll = hu->priv; BT_DBG("hu %p", hu); skb_queue_purge(&ll->tx_wait_q); skb_queue_purge(&ll->txq); kfree_skb(ll->rx_skb); hu->priv = NULL; kfree(ll); return 0; } /* * internal function, which does common work of the device wake up process: * 1. places all pending packets (waiting in tx_wait_q list) in txq list. * 2. changes internal state to HCILL_AWAKE. * Note: assumes that hcill_lock spinlock is taken, * shouldn't be called otherwise! */ static void __ll_do_awake(struct ll_struct *ll) { struct sk_buff *skb = NULL; while ((skb = skb_dequeue(&ll->tx_wait_q))) skb_queue_tail(&ll->txq, skb); ll->hcill_state = HCILL_AWAKE; } /* * Called upon a wake-up-indication from the device */ static void ll_device_want_to_wakeup(struct hci_uart *hu) { unsigned long flags; struct ll_struct *ll = hu->priv; BT_DBG("hu %p", hu); /* lock hcill state */ spin_lock_irqsave(&ll->hcill_lock, flags); switch (ll->hcill_state) { case HCILL_ASLEEP_TO_AWAKE: /* * This state means that both the host and the BRF chip * have simultaneously sent a wake-up-indication packet. * Traditionally, in this case, receiving a wake-up-indication * was enough and an additional wake-up-ack wasn't needed. * This has changed with the BRF6350, which does require an * explicit wake-up-ack. Other BRF versions, which do not * require an explicit ack here, do accept it, thus it is * perfectly safe to always send one. */ BT_DBG("dual wake-up-indication"); /* deliberate fall-through - do not add break */ case HCILL_ASLEEP: /* acknowledge device wake up */ if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) { BT_ERR("cannot acknowledge device wake up"); goto out; } break; default: /* any other state is illegal */ BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state); break; } /* send pending packets and change state to HCILL_AWAKE */ __ll_do_awake(ll); out: spin_unlock_irqrestore(&ll->hcill_lock, flags); /* actually send the packets */ hci_uart_tx_wakeup(hu); } /* * Called upon a sleep-indication from the device */ static void ll_device_want_to_sleep(struct hci_uart *hu) { unsigned long flags; struct ll_struct *ll = hu->priv; BT_DBG("hu %p", hu); /* lock hcill state */ spin_lock_irqsave(&ll->hcill_lock, flags); /* sanity check */ if (ll->hcill_state != HCILL_AWAKE) BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state); /* acknowledge device sleep */ if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) { BT_ERR("cannot acknowledge device sleep"); goto out; } /* update state */ ll->hcill_state = HCILL_ASLEEP; out: spin_unlock_irqrestore(&ll->hcill_lock, flags); /* actually send the sleep ack packet */ hci_uart_tx_wakeup(hu); } /* * Called upon wake-up-acknowledgement from the device */ static void ll_device_woke_up(struct hci_uart *hu) { unsigned long flags; struct ll_struct *ll = hu->priv; BT_DBG("hu %p", hu); /* lock hcill state */ spin_lock_irqsave(&ll->hcill_lock, flags); /* sanity check */ if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE) BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state); /* send pending packets and change state to HCILL_AWAKE */ __ll_do_awake(ll); spin_unlock_irqrestore(&ll->hcill_lock, flags); /* actually send the packets */ hci_uart_tx_wakeup(hu); } /* Enqueue frame for transmittion (padding, crc, etc) */ /* may be called from two simultaneous tasklets */ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) { unsigned long flags = 0; struct ll_struct *ll = hu->priv; BT_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); /* lock hcill state */ spin_lock_irqsave(&ll->hcill_lock, flags); /* act according to current state */ switch (ll->hcill_state) { case HCILL_AWAKE: BT_DBG("device awake, sending normally"); skb_queue_tail(&ll->txq, skb); break; case HCILL_ASLEEP: BT_DBG("device asleep, waking up and queueing packet"); /* save packet for later */ skb_queue_tail(&ll->tx_wait_q, skb); /* awake device */ if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) { BT_ERR("cannot wake up device"); break; } ll->hcill_state = HCILL_ASLEEP_TO_AWAKE; break; case HCILL_ASLEEP_TO_AWAKE: BT_DBG("device waking up, queueing packet"); /* transient state; just keep packet for later */ skb_queue_tail(&ll->tx_wait_q, skb); break; default: BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state); kfree_skb(skb); break; } spin_unlock_irqrestore(&ll->hcill_lock, flags); return 0; } static inline int ll_check_data_len(struct ll_struct *ll, int len) { int room = skb_tailroom(ll->rx_skb); BT_DBG("len %d room %d", len, room); if (!len) { hci_recv_frame(ll->rx_skb); } else if (len > room) { BT_ERR("Data length is too large"); kfree_skb(ll->rx_skb); } else { ll->rx_state = HCILL_W4_DATA; ll->rx_count = len; return len; } ll->rx_state = HCILL_W4_PACKET_TYPE; ll->rx_skb = NULL; ll->rx_count = 0; return 0; } /* Recv data */ static int ll_recv(struct hci_uart *hu, void *data, int count) { struct ll_struct *ll = hu->priv; char *ptr; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; int len, type, dlen; BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count); ptr = data; while (count) { if (ll->rx_count) { len = min_t(unsigned int, ll->rx_count, count); memcpy(skb_put(ll->rx_skb, len), ptr, len); ll->rx_count -= len; count -= len; ptr += len; if (ll->rx_count) continue; switch (ll->rx_state) { case HCILL_W4_DATA: BT_DBG("Complete data"); hci_recv_frame(ll->rx_skb); ll->rx_state = HCILL_W4_PACKET_TYPE; ll->rx_skb = NULL; continue; case HCILL_W4_EVENT_HDR: eh = hci_event_hdr(ll->rx_skb); BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); ll_check_data_len(ll, eh->plen); continue; case HCILL_W4_ACL_HDR: ah = hci_acl_hdr(ll->rx_skb); dlen = __le16_to_cpu(ah->dlen); BT_DBG("ACL header: dlen %d", dlen); ll_check_data_len(ll, dlen); continue; case HCILL_W4_SCO_HDR: sh = hci_sco_hdr(ll->rx_skb); BT_DBG("SCO header: dlen %d", sh->dlen); ll_check_data_len(ll, sh->dlen); continue; } } /* HCILL_W4_PACKET_TYPE */ switch (*ptr) { case HCI_EVENT_PKT: BT_DBG("Event packet"); ll->rx_state = HCILL_W4_EVENT_HDR; ll->rx_count = HCI_EVENT_HDR_SIZE; type = HCI_EVENT_PKT; break; case HCI_ACLDATA_PKT: BT_DBG("ACL packet"); ll->rx_state = HCILL_W4_ACL_HDR; ll->rx_count = HCI_ACL_HDR_SIZE; type = HCI_ACLDATA_PKT; break; case HCI_SCODATA_PKT: BT_DBG("SCO packet"); ll->rx_state = HCILL_W4_SCO_HDR; ll->rx_count = HCI_SCO_HDR_SIZE; type = HCI_SCODATA_PKT; break; /* HCILL signals */ case HCILL_GO_TO_SLEEP_IND: BT_DBG("HCILL_GO_TO_SLEEP_IND packet"); ll_device_want_to_sleep(hu); ptr++; count--; continue; case HCILL_GO_TO_SLEEP_ACK: /* shouldn't happen */ BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state); ptr++; count--; continue; case HCILL_WAKE_UP_IND: BT_DBG("HCILL_WAKE_UP_IND packet"); ll_device_want_to_wakeup(hu); ptr++; count--; continue; case HCILL_WAKE_UP_ACK: BT_DBG("HCILL_WAKE_UP_ACK packet"); ll_device_woke_up(hu); ptr++; count--; continue; default: BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); hu->hdev->stat.err_rx++; ptr++; count--; continue; } ptr++; count--; /* Allocate packet */ ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); if (!ll->rx_skb) { BT_ERR("Can't allocate mem for new packet"); ll->rx_state = HCILL_W4_PACKET_TYPE; ll->rx_count = 0; return -ENOMEM; } ll->rx_skb->dev = (void *) hu->hdev; bt_cb(ll->rx_skb)->pkt_type = type; } return count; } static struct sk_buff *ll_dequeue(struct hci_uart *hu) { struct ll_struct *ll = hu->priv; return skb_dequeue(&ll->txq); } static struct hci_uart_proto llp = { .id = HCI_UART_LL, .open = ll_open, .close = ll_close, .recv = ll_recv, .enqueue = ll_enqueue, .dequeue = ll_dequeue, .flush = ll_flush, }; int __init ll_init(void) { int err = hci_uart_register_proto(&llp); if (!err) BT_INFO("HCILL protocol initialized"); else BT_ERR("HCILL protocol registration failed"); return err; } int __exit ll_deinit(void) { return hci_uart_unregister_proto(&llp); } compat-drivers-2012-09-18/drivers/bluetooth/hci_h5.c0000644000175000017500000003753512026211315021363 0ustar mcgrofmcgrof/* * * Bluetooth HCI Three-wire UART driver * * Copyright (C) 2012 Intel Corporation * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include "hci_uart.h" #define HCI_3WIRE_ACK_PKT 0 #define HCI_3WIRE_LINK_PKT 15 /* Sliding window size */ #define H5_TX_WIN_MAX 4 #define H5_ACK_TIMEOUT msecs_to_jiffies(250) #define H5_SYNC_TIMEOUT msecs_to_jiffies(100) /* * Maximum Three-wire packet: * 4 byte header + max value for 12-bit length + 2 bytes for CRC */ #define H5_MAX_LEN (4 + 0xfff + 2) /* Convenience macros for reading Three-wire header values */ #define H5_HDR_SEQ(hdr) ((hdr)[0] & 0x07) #define H5_HDR_ACK(hdr) (((hdr)[0] >> 3) & 0x07) #define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01) #define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01) #define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f) #define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0xff) + ((hdr)[2] << 4)) #define SLIP_DELIMITER 0xc0 #define SLIP_ESC 0xdb #define SLIP_ESC_DELIM 0xdc #define SLIP_ESC_ESC 0xdd /* H5 state flags */ enum { H5_RX_ESC, /* SLIP escape mode */ H5_TX_ACK_REQ, /* Pending ack to send */ }; struct h5 { struct sk_buff_head unack; /* Unack'ed packets queue */ struct sk_buff_head rel; /* Reliable packets queue */ struct sk_buff_head unrel; /* Unreliable packets queue */ unsigned long flags; struct sk_buff *rx_skb; /* Receive buffer */ size_t rx_pending; /* Expecting more bytes */ u8 rx_ack; /* Last ack number received */ int (*rx_func) (struct hci_uart *hu, u8 c); struct timer_list timer; /* Retransmission timer */ u8 tx_seq; /* Next seq number to send */ u8 tx_ack; /* Next ack number to send */ u8 tx_win; /* Sliding window size */ enum { H5_UNINITIALIZED, H5_INITIALIZED, H5_ACTIVE, } state; enum { H5_AWAKE, H5_SLEEPING, H5_WAKING_UP, } sleep; }; static void h5_reset_rx(struct h5 *h5); static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) { struct h5 *h5 = hu->priv; struct sk_buff *nskb; nskb = alloc_skb(3, GFP_ATOMIC); if (!nskb) return; bt_cb(nskb)->pkt_type = HCI_3WIRE_LINK_PKT; memcpy(skb_put(nskb, len), data, len); skb_queue_tail(&h5->unrel, nskb); } static u8 h5_cfg_field(struct h5 *h5) { u8 field = 0; /* Sliding window size (first 3 bits) */ field |= (h5->tx_win & 7); return field; } static void h5_timed_event(unsigned long arg) { const unsigned char sync_req[] = { 0x01, 0x7e }; unsigned char conf_req[] = { 0x03, 0xfc, 0x01 }; struct hci_uart *hu = (struct hci_uart *) arg; struct h5 *h5 = hu->priv; struct sk_buff *skb; unsigned long flags; BT_DBG("%s", hu->hdev->name); if (h5->state == H5_UNINITIALIZED) h5_link_control(hu, sync_req, sizeof(sync_req)); if (h5->state == H5_INITIALIZED) { conf_req[2] = h5_cfg_field(h5); h5_link_control(hu, conf_req, sizeof(conf_req)); } if (h5->state != H5_ACTIVE) { mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT); goto wakeup; } if (h5->sleep != H5_AWAKE) { h5->sleep = H5_SLEEPING; goto wakeup; } BT_DBG("hu %p retransmitting %u pkts", hu, h5->unack.qlen); spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) { h5->tx_seq = (h5->tx_seq - 1) & 0x07; skb_queue_head(&h5->rel, skb); } spin_unlock_irqrestore(&h5->unack.lock, flags); wakeup: hci_uart_tx_wakeup(hu); } static int h5_open(struct hci_uart *hu) { struct h5 *h5; const unsigned char sync[] = { 0x01, 0x7e }; BT_DBG("hu %p", hu); h5 = kzalloc(sizeof(*h5), GFP_KERNEL); if (!h5) return -ENOMEM; hu->priv = h5; skb_queue_head_init(&h5->unack); skb_queue_head_init(&h5->rel); skb_queue_head_init(&h5->unrel); h5_reset_rx(h5); init_timer(&h5->timer); h5->timer.function = h5_timed_event; h5->timer.data = (unsigned long) hu; h5->tx_win = H5_TX_WIN_MAX; set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags); /* Send initial sync request */ h5_link_control(hu, sync, sizeof(sync)); mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT); return 0; } static int h5_close(struct hci_uart *hu) { struct h5 *h5 = hu->priv; skb_queue_purge(&h5->unack); skb_queue_purge(&h5->rel); skb_queue_purge(&h5->unrel); del_timer(&h5->timer); kfree(h5); return 0; } static void h5_pkt_cull(struct h5 *h5) { struct sk_buff *skb, *tmp; unsigned long flags; int i, to_remove; u8 seq; spin_lock_irqsave(&h5->unack.lock, flags); to_remove = skb_queue_len(&h5->unack); if (to_remove == 0) goto unlock; seq = h5->tx_seq; while (to_remove > 0) { if (h5->rx_ack == seq) break; to_remove--; seq = (seq - 1) % 8; } if (seq != h5->rx_ack) BT_ERR("Controller acked invalid packet"); i = 0; skb_queue_walk_safe(&h5->unack, skb, tmp) { if (i++ >= to_remove) break; __skb_unlink(skb, &h5->unack); kfree_skb(skb); } if (skb_queue_empty(&h5->unack)) del_timer(&h5->timer); unlock: spin_unlock_irqrestore(&h5->unack.lock, flags); } static void h5_handle_internal_rx(struct hci_uart *hu) { struct h5 *h5 = hu->priv; const unsigned char sync_req[] = { 0x01, 0x7e }; const unsigned char sync_rsp[] = { 0x02, 0x7d }; unsigned char conf_req[] = { 0x03, 0xfc, 0x01 }; const unsigned char conf_rsp[] = { 0x04, 0x7b }; const unsigned char wakeup_req[] = { 0x05, 0xfa }; const unsigned char woken_req[] = { 0x06, 0xf9 }; const unsigned char sleep_req[] = { 0x07, 0x78 }; const unsigned char *hdr = h5->rx_skb->data; const unsigned char *data = &h5->rx_skb->data[4]; BT_DBG("%s", hu->hdev->name); if (H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) return; if (H5_HDR_LEN(hdr) < 2) return; conf_req[2] = h5_cfg_field(h5); if (memcmp(data, sync_req, 2) == 0) { h5_link_control(hu, sync_rsp, 2); } else if (memcmp(data, sync_rsp, 2) == 0) { h5->state = H5_INITIALIZED; h5_link_control(hu, conf_req, 3); } else if (memcmp(data, conf_req, 2) == 0) { h5_link_control(hu, conf_rsp, 2); h5_link_control(hu, conf_req, 3); } else if (memcmp(data, conf_rsp, 2) == 0) { if (H5_HDR_LEN(hdr) > 2) h5->tx_win = (data[2] & 7); BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win); h5->state = H5_ACTIVE; hci_uart_init_ready(hu); return; } else if (memcmp(data, sleep_req, 2) == 0) { BT_DBG("Peer went to sleep"); h5->sleep = H5_SLEEPING; return; } else if (memcmp(data, woken_req, 2) == 0) { BT_DBG("Peer woke up"); h5->sleep = H5_AWAKE; } else if (memcmp(data, wakeup_req, 2) == 0) { BT_DBG("Peer requested wakeup"); h5_link_control(hu, woken_req, 2); h5->sleep = H5_AWAKE; } else { BT_DBG("Link Control: 0x%02hhx 0x%02hhx", data[0], data[1]); return; } hci_uart_tx_wakeup(hu); } static void h5_complete_rx_pkt(struct hci_uart *hu) { struct h5 *h5 = hu->priv; const unsigned char *hdr = h5->rx_skb->data; if (H5_HDR_RELIABLE(hdr)) { h5->tx_ack = (h5->tx_ack + 1) % 8; set_bit(H5_TX_ACK_REQ, &h5->flags); hci_uart_tx_wakeup(hu); } h5->rx_ack = H5_HDR_ACK(hdr); h5_pkt_cull(h5); switch (H5_HDR_PKT_TYPE(hdr)) { case HCI_EVENT_PKT: case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: bt_cb(h5->rx_skb)->pkt_type = H5_HDR_PKT_TYPE(hdr); /* Remove Three-wire header */ skb_pull(h5->rx_skb, 4); hci_recv_frame(h5->rx_skb); h5->rx_skb = NULL; break; default: h5_handle_internal_rx(hu); break; } h5_reset_rx(h5); } static int h5_rx_crc(struct hci_uart *hu, unsigned char c) { struct h5 *h5 = hu->priv; h5_complete_rx_pkt(hu); h5_reset_rx(h5); return 0; } static int h5_rx_payload(struct hci_uart *hu, unsigned char c) { struct h5 *h5 = hu->priv; const unsigned char *hdr = h5->rx_skb->data; if (H5_HDR_CRC(hdr)) { h5->rx_func = h5_rx_crc; h5->rx_pending = 2; } else { h5_complete_rx_pkt(hu); h5_reset_rx(h5); } return 0; } static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c) { struct h5 *h5 = hu->priv; const unsigned char *hdr = h5->rx_skb->data; BT_DBG("%s rx: seq %u ack %u crc %u rel %u type %u len %u", hu->hdev->name, H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr), H5_HDR_CRC(hdr), H5_HDR_RELIABLE(hdr), H5_HDR_PKT_TYPE(hdr), H5_HDR_LEN(hdr)); if (((hdr[0] + hdr[1] + hdr[2] + hdr[3]) & 0xff) != 0xff) { BT_ERR("Invalid header checksum"); h5_reset_rx(h5); return 0; } if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != h5->tx_ack) { BT_ERR("Out-of-order packet arrived (%u != %u)", H5_HDR_SEQ(hdr), h5->tx_ack); h5_reset_rx(h5); return 0; } if (h5->state != H5_ACTIVE && H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) { BT_ERR("Non-link packet received in non-active state"); h5_reset_rx(h5); } h5->rx_func = h5_rx_payload; h5->rx_pending = H5_HDR_LEN(hdr); return 0; } static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c) { struct h5 *h5 = hu->priv; if (c == SLIP_DELIMITER) return 1; h5->rx_func = h5_rx_3wire_hdr; h5->rx_pending = 4; h5->rx_skb = bt_skb_alloc(H5_MAX_LEN, GFP_ATOMIC); if (!h5->rx_skb) { BT_ERR("Can't allocate mem for new packet"); h5_reset_rx(h5); return -ENOMEM; } h5->rx_skb->dev = (void *) hu->hdev; return 0; } static int h5_rx_delimiter(struct hci_uart *hu, unsigned char c) { struct h5 *h5 = hu->priv; if (c == SLIP_DELIMITER) h5->rx_func = h5_rx_pkt_start; return 1; } static void h5_unslip_one_byte(struct h5 *h5, unsigned char c) { const u8 delim = SLIP_DELIMITER, esc = SLIP_ESC; const u8 *byte = &c; if (!test_bit(H5_RX_ESC, &h5->flags) && c == SLIP_ESC) { set_bit(H5_RX_ESC, &h5->flags); return; } if (test_and_clear_bit(H5_RX_ESC, &h5->flags)) { switch (c) { case SLIP_ESC_DELIM: byte = &delim; break; case SLIP_ESC_ESC: byte = &esc; break; default: BT_ERR("Invalid esc byte 0x%02hhx", c); h5_reset_rx(h5); return; } } memcpy(skb_put(h5->rx_skb, 1), byte, 1); h5->rx_pending--; BT_DBG("unsliped 0x%02hhx, rx_pending %zu", *byte, h5->rx_pending); } static void h5_reset_rx(struct h5 *h5) { if (h5->rx_skb) { kfree_skb(h5->rx_skb); h5->rx_skb = NULL; } h5->rx_func = h5_rx_delimiter; h5->rx_pending = 0; clear_bit(H5_RX_ESC, &h5->flags); } static int h5_recv(struct hci_uart *hu, void *data, int count) { struct h5 *h5 = hu->priv; unsigned char *ptr = data; BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending, count); while (count > 0) { int processed; if (h5->rx_pending > 0) { if (*ptr == SLIP_DELIMITER) { BT_ERR("Too short H5 packet"); h5_reset_rx(h5); continue; } h5_unslip_one_byte(h5, *ptr); ptr++; count--; continue; } processed = h5->rx_func(hu, *ptr); if (processed < 0) return processed; ptr += processed; count -= processed; } return 0; } static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct h5 *h5 = hu->priv; if (skb->len > 0xfff) { BT_ERR("Packet too long (%u bytes)", skb->len); kfree_skb(skb); return 0; } if (h5->state != H5_ACTIVE) { BT_ERR("Ignoring HCI data in non-active state"); kfree_skb(skb); return 0; } switch (bt_cb(skb)->pkt_type) { case HCI_ACLDATA_PKT: case HCI_COMMAND_PKT: skb_queue_tail(&h5->rel, skb); break; case HCI_SCODATA_PKT: skb_queue_tail(&h5->unrel, skb); break; default: BT_ERR("Unknown packet type %u", bt_cb(skb)->pkt_type); kfree_skb(skb); break; } return 0; } static void h5_slip_delim(struct sk_buff *skb) { const char delim = SLIP_DELIMITER; memcpy(skb_put(skb, 1), &delim, 1); } static void h5_slip_one_byte(struct sk_buff *skb, u8 c) { const char esc_delim[2] = { SLIP_ESC, SLIP_ESC_DELIM }; const char esc_esc[2] = { SLIP_ESC, SLIP_ESC_ESC }; switch (c) { case SLIP_DELIMITER: memcpy(skb_put(skb, 2), &esc_delim, 2); break; case SLIP_ESC: memcpy(skb_put(skb, 2), &esc_esc, 2); break; default: memcpy(skb_put(skb, 1), &c, 1); } } static bool valid_packet_type(u8 type) { switch (type) { case HCI_ACLDATA_PKT: case HCI_COMMAND_PKT: case HCI_SCODATA_PKT: case HCI_3WIRE_LINK_PKT: case HCI_3WIRE_ACK_PKT: return true; default: return false; } } static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type, const u8 *data, size_t len) { struct h5 *h5 = hu->priv; struct sk_buff *nskb; u8 hdr[4]; int i; if (!valid_packet_type(pkt_type)) { BT_ERR("Unknown packet type %u", pkt_type); return NULL; } /* * Max len of packet: (original len + 4 (H5 hdr) + 2 (crc)) * 2 * (because bytes 0xc0 and 0xdb are escaped, worst case is when * the packet is all made of 0xc0 and 0xdb) + 2 (0xc0 * delimiters at start and end). */ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC); if (!nskb) return NULL; bt_cb(nskb)->pkt_type = pkt_type; h5_slip_delim(nskb); hdr[0] = h5->tx_ack << 3; clear_bit(H5_TX_ACK_REQ, &h5->flags); /* Reliable packet? */ if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) { hdr[0] |= 1 << 7; hdr[0] |= h5->tx_seq; h5->tx_seq = (h5->tx_seq + 1) % 8; } hdr[1] = pkt_type | ((len & 0x0f) << 4); hdr[2] = len >> 4; hdr[3] = ~((hdr[0] + hdr[1] + hdr[2]) & 0xff); BT_DBG("%s tx: seq %u ack %u crc %u rel %u type %u len %u", hu->hdev->name, H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr), H5_HDR_CRC(hdr), H5_HDR_RELIABLE(hdr), H5_HDR_PKT_TYPE(hdr), H5_HDR_LEN(hdr)); for (i = 0; i < 4; i++) h5_slip_one_byte(nskb, hdr[i]); for (i = 0; i < len; i++) h5_slip_one_byte(nskb, data[i]); h5_slip_delim(nskb); return nskb; } static struct sk_buff *h5_dequeue(struct hci_uart *hu) { struct h5 *h5 = hu->priv; unsigned long flags; struct sk_buff *skb, *nskb; if (h5->sleep != H5_AWAKE) { const unsigned char wakeup_req[] = { 0x05, 0xfa }; if (h5->sleep == H5_WAKING_UP) return NULL; h5->sleep = H5_WAKING_UP; BT_DBG("Sending wakeup request"); mod_timer(&h5->timer, jiffies + HZ / 100); return h5_prepare_pkt(hu, HCI_3WIRE_LINK_PKT, wakeup_req, 2); } if ((skb = skb_dequeue(&h5->unrel)) != NULL) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { kfree_skb(skb); return nskb; } skb_queue_head(&h5->unrel, skb); BT_ERR("Could not dequeue pkt because alloc_skb failed"); } spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); if (h5->unack.qlen >= h5->tx_win) goto unlock; if ((skb = skb_dequeue(&h5->rel)) != NULL) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { __skb_queue_tail(&h5->unack, skb); mod_timer(&h5->timer, jiffies + H5_ACK_TIMEOUT); spin_unlock_irqrestore(&h5->unack.lock, flags); return nskb; } skb_queue_head(&h5->rel, skb); BT_ERR("Could not dequeue pkt because alloc_skb failed"); } unlock: spin_unlock_irqrestore(&h5->unack.lock, flags); if (test_bit(H5_TX_ACK_REQ, &h5->flags)) return h5_prepare_pkt(hu, HCI_3WIRE_ACK_PKT, NULL, 0); return NULL; } static int h5_flush(struct hci_uart *hu) { BT_DBG("hu %p", hu); return 0; } static struct hci_uart_proto h5p = { .id = HCI_UART_3WIRE, .open = h5_open, .close = h5_close, .recv = h5_recv, .enqueue = h5_enqueue, .dequeue = h5_dequeue, .flush = h5_flush, }; int __init h5_init(void) { int err = hci_uart_register_proto(&h5p); if (!err) BT_INFO("HCI Three-wire UART (H5) protocol initialized"); else BT_ERR("HCI Three-wire UART (H5) protocol init failed"); return err; } int __exit h5_deinit(void) { return hci_uart_unregister_proto(&h5p); } compat-drivers-2012-09-18/drivers/bluetooth/hci_h4.c0000644000175000017500000000763312026211315021356 0ustar mcgrofmcgrof/* * * Bluetooth HCI UART driver * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hci_uart.h" #define VERSION "1.2" struct h4_struct { unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; struct sk_buff_head txq; }; /* H4 receiver States */ #define H4_W4_PACKET_TYPE 0 #define H4_W4_EVENT_HDR 1 #define H4_W4_ACL_HDR 2 #define H4_W4_SCO_HDR 3 #define H4_W4_DATA 4 /* Initialize protocol */ static int h4_open(struct hci_uart *hu) { struct h4_struct *h4; BT_DBG("hu %p", hu); h4 = kzalloc(sizeof(*h4), GFP_KERNEL); if (!h4) return -ENOMEM; skb_queue_head_init(&h4->txq); hu->priv = h4; return 0; } /* Flush protocol data */ static int h4_flush(struct hci_uart *hu) { struct h4_struct *h4 = hu->priv; BT_DBG("hu %p", hu); skb_queue_purge(&h4->txq); return 0; } /* Close protocol */ static int h4_close(struct hci_uart *hu) { struct h4_struct *h4 = hu->priv; hu->priv = NULL; BT_DBG("hu %p", hu); skb_queue_purge(&h4->txq); kfree_skb(h4->rx_skb); hu->priv = NULL; kfree(h4); return 0; } /* Enqueue frame for transmittion (padding, crc, etc) */ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct h4_struct *h4 = hu->priv; BT_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&h4->txq, skb); return 0; } static inline int h4_check_data_len(struct h4_struct *h4, int len) { int room = skb_tailroom(h4->rx_skb); BT_DBG("len %d room %d", len, room); if (!len) { hci_recv_frame(h4->rx_skb); } else if (len > room) { BT_ERR("Data length is too large"); kfree_skb(h4->rx_skb); } else { h4->rx_state = H4_W4_DATA; h4->rx_count = len; return len; } h4->rx_state = H4_W4_PACKET_TYPE; h4->rx_skb = NULL; h4->rx_count = 0; return 0; } /* Recv data */ static int h4_recv(struct hci_uart *hu, void *data, int count) { int ret; ret = hci_recv_stream_fragment(hu->hdev, data, count); if (ret < 0) { BT_ERR("Frame Reassembly Failed"); return ret; } return count; } static struct sk_buff *h4_dequeue(struct hci_uart *hu) { struct h4_struct *h4 = hu->priv; return skb_dequeue(&h4->txq); } static struct hci_uart_proto h4p = { .id = HCI_UART_H4, .open = h4_open, .close = h4_close, .recv = h4_recv, .enqueue = h4_enqueue, .dequeue = h4_dequeue, .flush = h4_flush, }; int __init h4_init(void) { int err = hci_uart_register_proto(&h4p); if (!err) BT_INFO("HCI H4 protocol initialized"); else BT_ERR("HCI H4 protocol registration failed"); return err; } int __exit h4_deinit(void) { return hci_uart_unregister_proto(&h4p); } compat-drivers-2012-09-18/drivers/bluetooth/hci_bcsp.c0000644000175000017500000004461612026211315021774 0ustar mcgrofmcgrof/* * * Bluetooth HCI UART driver * * Copyright (C) 2002-2003 Fabrizio Gennari * Copyright (C) 2004-2005 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hci_uart.h" #define VERSION "0.3" static bool txcrc = 1; static bool hciextn = 1; #define BCSP_TXWINSIZE 4 #define BCSP_ACK_PKT 0x05 #define BCSP_LE_PKT 0x06 struct bcsp_struct { struct sk_buff_head unack; /* Unack'ed packets queue */ struct sk_buff_head rel; /* Reliable packets queue */ struct sk_buff_head unrel; /* Unreliable packets queue */ unsigned long rx_count; struct sk_buff *rx_skb; u8 rxseq_txack; /* rxseq == txack. */ u8 rxack; /* Last packet sent by us that the peer ack'ed */ struct timer_list tbcsp; enum { BCSP_W4_PKT_DELIMITER, BCSP_W4_PKT_START, BCSP_W4_BCSP_HDR, BCSP_W4_DATA, BCSP_W4_CRC } rx_state; enum { BCSP_ESCSTATE_NOESC, BCSP_ESCSTATE_ESC } rx_esc_state; u8 use_crc; u16 message_crc; u8 txack_req; /* Do we need to send ack's to the peer? */ /* Reliable packet sequence number - used to assign seq to each rel pkt. */ u8 msgq_txseq; }; /* ---- BCSP CRC calculation ---- */ /* Table for calculating CRC for polynomial 0x1021, LSB processed first, initial value 0xffff, bits shifted in reverse order. */ static const u16 crc_table[] = { 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, 0x8408, 0x9489, 0xa50a, 0xb58b, 0xc60c, 0xd68d, 0xe70e, 0xf78f }; /* Initialise the crc calculator */ #define BCSP_CRC_INIT(x) x = 0xffff /* Update crc with next data byte Implementation note The data byte is treated as two nibbles. The crc is generated in reverse, i.e., bits are fed into the register from the top. */ static void bcsp_crc_update(u16 *crc, u8 d) { u16 reg = *crc; reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f]; reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f]; *crc = reg; } /* ---- BCSP core ---- */ static void bcsp_slip_msgdelim(struct sk_buff *skb) { const char pkt_delim = 0xc0; memcpy(skb_put(skb, 1), &pkt_delim, 1); } static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c) { const char esc_c0[2] = { 0xdb, 0xdc }; const char esc_db[2] = { 0xdb, 0xdd }; switch (c) { case 0xc0: memcpy(skb_put(skb, 2), &esc_c0, 2); break; case 0xdb: memcpy(skb_put(skb, 2), &esc_db, 2); break; default: memcpy(skb_put(skb, 1), &c, 1); } } static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct bcsp_struct *bcsp = hu->priv; if (skb->len > 0xFFF) { BT_ERR("Packet too long"); kfree_skb(skb); return 0; } switch (bt_cb(skb)->pkt_type) { case HCI_ACLDATA_PKT: case HCI_COMMAND_PKT: skb_queue_tail(&bcsp->rel, skb); break; case HCI_SCODATA_PKT: skb_queue_tail(&bcsp->unrel, skb); break; default: BT_ERR("Unknown packet type"); kfree_skb(skb); break; } return 0; } static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, int len, int pkt_type) { struct sk_buff *nskb; u8 hdr[4], chan; u16 BCSP_CRC_INIT(bcsp_txmsg_crc); int rel, i; switch (pkt_type) { case HCI_ACLDATA_PKT: chan = 6; /* BCSP ACL channel */ rel = 1; /* reliable channel */ break; case HCI_COMMAND_PKT: chan = 5; /* BCSP cmd/evt channel */ rel = 1; /* reliable channel */ break; case HCI_SCODATA_PKT: chan = 7; /* BCSP SCO channel */ rel = 0; /* unreliable channel */ break; case BCSP_LE_PKT: chan = 1; /* BCSP LE channel */ rel = 0; /* unreliable channel */ break; case BCSP_ACK_PKT: chan = 0; /* BCSP internal channel */ rel = 0; /* unreliable channel */ break; default: BT_ERR("Unknown packet type"); return NULL; } if (hciextn && chan == 5) { __le16 opcode = ((struct hci_command_hdr *)data)->opcode; /* Vendor specific commands */ if (hci_opcode_ogf(__le16_to_cpu(opcode)) == 0x3f) { u8 desc = *(data + HCI_COMMAND_HDR_SIZE); if ((desc & 0xf0) == 0xc0) { data += HCI_COMMAND_HDR_SIZE + 1; len -= HCI_COMMAND_HDR_SIZE + 1; chan = desc & 0x0f; } } } /* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2 (because bytes 0xc0 and 0xdb are escaped, worst case is when the packet is all made of 0xc0 and 0xdb :) ) + 2 (0xc0 delimiters at start and end). */ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC); if (!nskb) return NULL; bt_cb(nskb)->pkt_type = pkt_type; bcsp_slip_msgdelim(nskb); hdr[0] = bcsp->rxseq_txack << 3; bcsp->txack_req = 0; BT_DBG("We request packet no %u to card", bcsp->rxseq_txack); if (rel) { hdr[0] |= 0x80 + bcsp->msgq_txseq; BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq); bcsp->msgq_txseq = (bcsp->msgq_txseq + 1) & 0x07; } if (bcsp->use_crc) hdr[0] |= 0x40; hdr[1] = ((len << 4) & 0xff) | chan; hdr[2] = len >> 4; hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]); /* Put BCSP header */ for (i = 0; i < 4; i++) { bcsp_slip_one_byte(nskb, hdr[i]); if (bcsp->use_crc) bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]); } /* Put payload */ for (i = 0; i < len; i++) { bcsp_slip_one_byte(nskb, data[i]); if (bcsp->use_crc) bcsp_crc_update(&bcsp_txmsg_crc, data[i]); } /* Put CRC */ if (bcsp->use_crc) { bcsp_txmsg_crc = bitrev16(bcsp_txmsg_crc); bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff)); bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff)); } bcsp_slip_msgdelim(nskb); return nskb; } /* This is a rewrite of pkt_avail in ABCSP */ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) { struct bcsp_struct *bcsp = hu->priv; unsigned long flags; struct sk_buff *skb; /* First of all, check for unreliable messages in the queue, since they have priority */ if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) { struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type); if (nskb) { kfree_skb(skb); return nskb; } else { skb_queue_head(&bcsp->unrel, skb); BT_ERR("Could not dequeue pkt because alloc_skb failed"); } } /* Now, try to send a reliable pkt. We can only send a reliable packet if the number of packets sent but not yet ack'ed is < than the winsize */ spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING); if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) { struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type); if (nskb) { __skb_queue_tail(&bcsp->unack, skb); mod_timer(&bcsp->tbcsp, jiffies + HZ / 4); spin_unlock_irqrestore(&bcsp->unack.lock, flags); return nskb; } else { skb_queue_head(&bcsp->rel, skb); BT_ERR("Could not dequeue pkt because alloc_skb failed"); } } spin_unlock_irqrestore(&bcsp->unack.lock, flags); /* We could not send a reliable packet, either because there are none or because there are too many unack'ed pkts. Did we receive any packets we have not acknowledged yet ? */ if (bcsp->txack_req) { /* if so, craft an empty ACK pkt and send it on BCSP unreliable channel 0 */ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT); return nskb; } /* We have nothing to send */ return NULL; } static int bcsp_flush(struct hci_uart *hu) { BT_DBG("hu %p", hu); return 0; } /* Remove ack'ed packets */ static void bcsp_pkt_cull(struct bcsp_struct *bcsp) { struct sk_buff *skb, *tmp; unsigned long flags; int i, pkts_to_be_removed; u8 seqno; spin_lock_irqsave(&bcsp->unack.lock, flags); pkts_to_be_removed = skb_queue_len(&bcsp->unack); seqno = bcsp->msgq_txseq; while (pkts_to_be_removed) { if (bcsp->rxack == seqno) break; pkts_to_be_removed--; seqno = (seqno - 1) & 0x07; } if (bcsp->rxack != seqno) BT_ERR("Peer acked invalid packet"); BT_DBG("Removing %u pkts out of %u, up to seqno %u", pkts_to_be_removed, skb_queue_len(&bcsp->unack), (seqno - 1) & 0x07); i = 0; skb_queue_walk_safe(&bcsp->unack, skb, tmp) { if (i >= pkts_to_be_removed) break; i++; __skb_unlink(skb, &bcsp->unack); kfree_skb(skb); } if (skb_queue_empty(&bcsp->unack)) del_timer(&bcsp->tbcsp); spin_unlock_irqrestore(&bcsp->unack.lock, flags); if (i != pkts_to_be_removed) BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed); } /* Handle BCSP link-establishment packets. When we detect a "sync" packet, symptom that the BT module has reset, we do nothing :) (yet) */ static void bcsp_handle_le_pkt(struct hci_uart *hu) { struct bcsp_struct *bcsp = hu->priv; u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed }; u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 }; u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed }; /* spot "conf" pkts and reply with a "conf rsp" pkt */ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) { struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC); BT_DBG("Found a LE conf pkt"); if (!nskb) return; memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4); bt_cb(nskb)->pkt_type = BCSP_LE_PKT; skb_queue_head(&bcsp->unrel, nskb); hci_uart_tx_wakeup(hu); } /* Spot "sync" pkts. If we find one...disaster! */ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) { BT_ERR("Found a LE sync pkt, card has reset"); } } static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte) { const u8 c0 = 0xc0, db = 0xdb; switch (bcsp->rx_esc_state) { case BCSP_ESCSTATE_NOESC: switch (byte) { case 0xdb: bcsp->rx_esc_state = BCSP_ESCSTATE_ESC; break; default: memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1); if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && bcsp->rx_state != BCSP_W4_CRC) bcsp_crc_update(&bcsp->message_crc, byte); bcsp->rx_count--; } break; case BCSP_ESCSTATE_ESC: switch (byte) { case 0xdc: memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1); if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && bcsp->rx_state != BCSP_W4_CRC) bcsp_crc_update(&bcsp-> message_crc, 0xc0); bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; bcsp->rx_count--; break; case 0xdd: memcpy(skb_put(bcsp->rx_skb, 1), &db, 1); if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && bcsp->rx_state != BCSP_W4_CRC) bcsp_crc_update(&bcsp-> message_crc, 0xdb); bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; bcsp->rx_count--; break; default: BT_ERR ("Invalid byte %02x after esc byte", byte); kfree_skb(bcsp->rx_skb); bcsp->rx_skb = NULL; bcsp->rx_state = BCSP_W4_PKT_DELIMITER; bcsp->rx_count = 0; } } } static void bcsp_complete_rx_pkt(struct hci_uart *hu) { struct bcsp_struct *bcsp = hu->priv; int pass_up; if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack); bcsp->rxseq_txack++; bcsp->rxseq_txack %= 0x8; bcsp->txack_req = 1; /* If needed, transmit an ack pkt */ hci_uart_tx_wakeup(hu); } bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07; BT_DBG("Request for pkt %u from card", bcsp->rxack); bcsp_pkt_cull(bcsp); if ((bcsp->rx_skb->data[1] & 0x0f) == 6 && bcsp->rx_skb->data[0] & 0x80) { bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT; pass_up = 1; } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 && bcsp->rx_skb->data[0] & 0x80) { bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT; pass_up = 1; } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) { bt_cb(bcsp->rx_skb)->pkt_type = HCI_SCODATA_PKT; pass_up = 1; } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 && !(bcsp->rx_skb->data[0] & 0x80)) { bcsp_handle_le_pkt(hu); pass_up = 0; } else pass_up = 0; if (!pass_up) { struct hci_event_hdr hdr; u8 desc = (bcsp->rx_skb->data[1] & 0x0f); if (desc != 0 && desc != 1) { if (hciextn) { desc |= 0xc0; skb_pull(bcsp->rx_skb, 4); memcpy(skb_push(bcsp->rx_skb, 1), &desc, 1); hdr.evt = 0xff; hdr.plen = bcsp->rx_skb->len; memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE); bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT; hci_recv_frame(bcsp->rx_skb); } else { BT_ERR ("Packet for unknown channel (%u %s)", bcsp->rx_skb->data[1] & 0x0f, bcsp->rx_skb->data[0] & 0x80 ? "reliable" : "unreliable"); kfree_skb(bcsp->rx_skb); } } else kfree_skb(bcsp->rx_skb); } else { /* Pull out BCSP hdr */ skb_pull(bcsp->rx_skb, 4); hci_recv_frame(bcsp->rx_skb); } bcsp->rx_state = BCSP_W4_PKT_DELIMITER; bcsp->rx_skb = NULL; } static u16 bscp_get_crc(struct bcsp_struct *bcsp) { return get_unaligned_be16(&bcsp->rx_skb->data[bcsp->rx_skb->len - 2]); } /* Recv data */ static int bcsp_recv(struct hci_uart *hu, void *data, int count) { struct bcsp_struct *bcsp = hu->priv; unsigned char *ptr; BT_DBG("hu %p count %d rx_state %d rx_count %ld", hu, count, bcsp->rx_state, bcsp->rx_count); ptr = data; while (count) { if (bcsp->rx_count) { if (*ptr == 0xc0) { BT_ERR("Short BCSP packet"); kfree_skb(bcsp->rx_skb); bcsp->rx_state = BCSP_W4_PKT_START; bcsp->rx_count = 0; } else bcsp_unslip_one_byte(bcsp, *ptr); ptr++; count--; continue; } switch (bcsp->rx_state) { case BCSP_W4_BCSP_HDR: if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] + bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) { BT_ERR("Error in BCSP hdr checksum"); kfree_skb(bcsp->rx_skb); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; bcsp->rx_count = 0; continue; } if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */ && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) { BT_ERR ("Out-of-order packet arrived, got %u expected %u", bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack); kfree_skb(bcsp->rx_skb); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; bcsp->rx_count = 0; continue; } bcsp->rx_state = BCSP_W4_DATA; bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) + (bcsp->rx_skb->data[2] << 4); /* May be 0 */ continue; case BCSP_W4_DATA: if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */ bcsp->rx_state = BCSP_W4_CRC; bcsp->rx_count = 2; } else bcsp_complete_rx_pkt(hu); continue; case BCSP_W4_CRC: if (bitrev16(bcsp->message_crc) != bscp_get_crc(bcsp)) { BT_ERR ("Checksum failed: computed %04x received %04x", bitrev16(bcsp->message_crc), bscp_get_crc(bcsp)); kfree_skb(bcsp->rx_skb); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; bcsp->rx_count = 0; continue; } skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2); bcsp_complete_rx_pkt(hu); continue; case BCSP_W4_PKT_DELIMITER: switch (*ptr) { case 0xc0: bcsp->rx_state = BCSP_W4_PKT_START; break; default: /*BT_ERR("Ignoring byte %02x", *ptr);*/ break; } ptr++; count--; break; case BCSP_W4_PKT_START: switch (*ptr) { case 0xc0: ptr++; count--; break; default: bcsp->rx_state = BCSP_W4_BCSP_HDR; bcsp->rx_count = 4; bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; BCSP_CRC_INIT(bcsp->message_crc); /* Do not increment ptr or decrement count * Allocate packet. Max len of a BCSP pkt= * 0xFFF (payload) +4 (header) +2 (crc) */ bcsp->rx_skb = bt_skb_alloc(0x1005, GFP_ATOMIC); if (!bcsp->rx_skb) { BT_ERR("Can't allocate mem for new packet"); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; bcsp->rx_count = 0; return 0; } bcsp->rx_skb->dev = (void *) hu->hdev; break; } break; } } return count; } /* Arrange to retransmit all messages in the relq. */ static void bcsp_timed_event(unsigned long arg) { struct hci_uart *hu = (struct hci_uart *) arg; struct bcsp_struct *bcsp = hu->priv; struct sk_buff *skb; unsigned long flags; BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen); spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING); while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) { bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07; skb_queue_head(&bcsp->rel, skb); } spin_unlock_irqrestore(&bcsp->unack.lock, flags); hci_uart_tx_wakeup(hu); } static int bcsp_open(struct hci_uart *hu) { struct bcsp_struct *bcsp; BT_DBG("hu %p", hu); bcsp = kzalloc(sizeof(*bcsp), GFP_KERNEL); if (!bcsp) return -ENOMEM; hu->priv = bcsp; skb_queue_head_init(&bcsp->unack); skb_queue_head_init(&bcsp->rel); skb_queue_head_init(&bcsp->unrel); init_timer(&bcsp->tbcsp); bcsp->tbcsp.function = bcsp_timed_event; bcsp->tbcsp.data = (u_long) hu; bcsp->rx_state = BCSP_W4_PKT_DELIMITER; if (txcrc) bcsp->use_crc = 1; return 0; } static int bcsp_close(struct hci_uart *hu) { struct bcsp_struct *bcsp = hu->priv; hu->priv = NULL; BT_DBG("hu %p", hu); skb_queue_purge(&bcsp->unack); skb_queue_purge(&bcsp->rel); skb_queue_purge(&bcsp->unrel); del_timer(&bcsp->tbcsp); kfree(bcsp); return 0; } static struct hci_uart_proto bcsp = { .id = HCI_UART_BCSP, .open = bcsp_open, .close = bcsp_close, .enqueue = bcsp_enqueue, .dequeue = bcsp_dequeue, .recv = bcsp_recv, .flush = bcsp_flush }; int __init bcsp_init(void) { int err = hci_uart_register_proto(&bcsp); if (!err) BT_INFO("HCI BCSP protocol initialized"); else BT_ERR("HCI BCSP protocol registration failed"); return err; } int __exit bcsp_deinit(void) { return hci_uart_unregister_proto(&bcsp); } module_param(txcrc, bool, 0644); MODULE_PARM_DESC(txcrc, "Transmit CRC with every BCSP packet"); module_param(hciextn, bool, 0644); MODULE_PARM_DESC(hciextn, "Convert HCI Extensions into BCSP packets"); compat-drivers-2012-09-18/drivers/bluetooth/btwilink.c0000644000175000017500000002165012026211315022036 0ustar mcgrofmcgrof/* * Texas Instrument's Bluetooth Driver For Shared Transport. * * Bluetooth Driver acts as interface between HCI core and * TI Shared Transport Layer. * * Copyright (C) 2009-2010 Texas Instruments * Author: Raja Mani * Pavan Savoy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define DEBUG #include #include #include #include #include #include /* Bluetooth Driver Version */ #define VERSION "1.0" #define MAX_BT_CHNL_IDS 3 /* Number of seconds to wait for registration completion * when ST returns PENDING status. */ #define BT_REGISTER_TIMEOUT 6000 /* 6 sec */ /** * struct ti_st - driver operation structure * @hdev: hci device pointer which binds to bt driver * @reg_status: ST registration callback status * @st_write: write function provided by the ST driver * to be used by the driver during send_frame. * @wait_reg_completion - completion sync between ti_st_open * and st_reg_completion_cb. */ struct ti_st { struct hci_dev *hdev; char reg_status; long (*st_write) (struct sk_buff *); struct completion wait_reg_completion; }; /* Increments HCI counters based on pocket ID (cmd,acl,sco) */ static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type) { struct hci_dev *hdev = hst->hdev; /* Update HCI stat counters */ switch (pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; } } /* ------- Interfaces to Shared Transport ------ */ /* Called by ST layer to indicate protocol registration completion * status.ti_st_open() function will wait for signal from this * API when st_register() function returns ST_PENDING. */ static void st_reg_completion_cb(void *priv_data, char data) { struct ti_st *lhst = priv_data; /* Save registration status for use in ti_st_open() */ lhst->reg_status = data; /* complete the wait in ti_st_open() */ complete(&lhst->wait_reg_completion); } /* Called by Shared Transport layer when receive data is * available */ static long st_receive(void *priv_data, struct sk_buff *skb) { struct ti_st *lhst = priv_data; int err; if (!skb) return -EFAULT; if (!lhst) { kfree_skb(skb); return -EFAULT; } skb->dev = (void *) lhst->hdev; /* Forward skb to HCI core layer */ err = hci_recv_frame(skb); if (err < 0) { BT_ERR("Unable to push skb to HCI core(%d)", err); return err; } lhst->hdev->stat.byte_rx += skb->len; return 0; } /* ------- Interfaces to HCI layer ------ */ /* protocol structure registered with shared transport */ static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = { { .chnl_id = HCI_EVENT_PKT, /* HCI Events */ .hdr_len = sizeof(struct hci_event_hdr), .offset_len_in_hdr = offsetof(struct hci_event_hdr, plen), .len_size = 1, /* sizeof(plen) in struct hci_event_hdr */ .reserve = 8, }, { .chnl_id = HCI_ACLDATA_PKT, /* ACL */ .hdr_len = sizeof(struct hci_acl_hdr), .offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen), .len_size = 2, /* sizeof(dlen) in struct hci_acl_hdr */ .reserve = 8, }, { .chnl_id = HCI_SCODATA_PKT, /* SCO */ .hdr_len = sizeof(struct hci_sco_hdr), .offset_len_in_hdr = offsetof(struct hci_sco_hdr, dlen), .len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */ .reserve = 8, }, }; /* Called from HCI core to initialize the device */ static int ti_st_open(struct hci_dev *hdev) { unsigned long timeleft; struct ti_st *hst; int err, i; BT_DBG("%s %p", hdev->name, hdev); if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; /* provide contexts for callbacks from ST */ hst = hci_get_drvdata(hdev); for (i = 0; i < MAX_BT_CHNL_IDS; i++) { ti_st_proto[i].priv_data = hst; ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE; ti_st_proto[i].recv = st_receive; ti_st_proto[i].reg_complete_cb = st_reg_completion_cb; /* Prepare wait-for-completion handler */ init_completion(&hst->wait_reg_completion); /* Reset ST registration callback status flag, * this value will be updated in * st_reg_completion_cb() * function whenever it called from ST driver. */ hst->reg_status = -EINPROGRESS; err = st_register(&ti_st_proto[i]); if (!err) goto done; if (err != -EINPROGRESS) { clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("st_register failed %d", err); return err; } /* ST is busy with either protocol * registration or firmware download. */ BT_DBG("waiting for registration " "completion signal from ST"); timeleft = wait_for_completion_timeout (&hst->wait_reg_completion, msecs_to_jiffies(BT_REGISTER_TIMEOUT)); if (!timeleft) { clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("Timeout(%d sec),didn't get reg " "completion signal from ST", BT_REGISTER_TIMEOUT / 1000); return -ETIMEDOUT; } /* Is ST registration callback * called with ERROR status? */ if (hst->reg_status != 0) { clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("ST registration completed with invalid " "status %d", hst->reg_status); return -EAGAIN; } done: hst->st_write = ti_st_proto[i].write; if (!hst->st_write) { BT_ERR("undefined ST write function"); clear_bit(HCI_RUNNING, &hdev->flags); for (i = 0; i < MAX_BT_CHNL_IDS; i++) { /* Undo registration with ST */ err = st_unregister(&ti_st_proto[i]); if (err) BT_ERR("st_unregister() failed with " "error %d", err); hst->st_write = NULL; } return -EIO; } } return 0; } /* Close device */ static int ti_st_close(struct hci_dev *hdev) { int err, i; struct ti_st *hst = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) { err = st_unregister(&ti_st_proto[i]); if (err) BT_ERR("st_unregister(%d) failed with error %d", ti_st_proto[i].chnl_id, err); } hst->st_write = NULL; return err; } static int ti_st_send_frame(struct sk_buff *skb) { struct hci_dev *hdev; struct ti_st *hst; long len; hdev = (struct hci_dev *)skb->dev; if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; hst = hci_get_drvdata(hdev); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); /* Insert skb to shared transport layer's transmit queue. * Freeing skb memory is taken care in shared transport layer, * so don't free skb memory here. */ len = hst->st_write(skb); if (len < 0) { kfree_skb(skb); BT_ERR("ST write failed (%ld)", len); /* Try Again, would only fail if UART has gone bad */ return -EAGAIN; } /* ST accepted our skb. So, Go ahead and do rest */ hdev->stat.byte_tx += len; ti_st_tx_complete(hst, bt_cb(skb)->pkt_type); return 0; } static int bt_ti_probe(struct platform_device *pdev) { static struct ti_st *hst; struct hci_dev *hdev; int err; hst = devm_kzalloc(&pdev->dev, sizeof(struct ti_st), GFP_KERNEL); if (!hst) return -ENOMEM; /* Expose "hciX" device to user space */ hdev = hci_alloc_dev(); if (!hdev) return -ENOMEM; BT_DBG("hdev %p", hdev); hst->hdev = hdev; hdev->bus = HCI_UART; hci_set_drvdata(hdev, hst); hdev->open = ti_st_open; hdev->close = ti_st_close; hdev->flush = NULL; hdev->send = ti_st_send_frame; err = hci_register_dev(hdev); if (err < 0) { BT_ERR("Can't register HCI device error %d", err); hci_free_dev(hdev); return err; } BT_DBG("HCI device registered (hdev %p)", hdev); dev_set_drvdata(&pdev->dev, hst); return err; } static int bt_ti_remove(struct platform_device *pdev) { struct hci_dev *hdev; struct ti_st *hst = dev_get_drvdata(&pdev->dev); if (!hst) return -EFAULT; BT_DBG("%s", hst->hdev->name); hdev = hst->hdev; ti_st_close(hdev); hci_unregister_dev(hdev); hci_free_dev(hdev); dev_set_drvdata(&pdev->dev, NULL); return 0; } static struct platform_driver btwilink_driver = { .probe = bt_ti_probe, .remove = bt_ti_remove, .driver = { .name = "btwilink", .owner = THIS_MODULE, }, }; module_platform_driver(btwilink_driver); /* ------ Module Info ------ */ MODULE_AUTHOR("Raja Mani "); MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/bluetooth/btsdio.c0000644000175000017500000002034712026211315021501 0ustar mcgrofmcgrof/* * * Generic Bluetooth SDIO driver * * Copyright (C) 2007 Cambridge Silicon Radio Ltd. * Copyright (C) 2007 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "0.1" static const struct sdio_device_id btsdio_table[] = { /* Generic Bluetooth Type-A SDIO device */ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) }, /* Generic Bluetooth Type-B SDIO device */ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) }, /* Generic Bluetooth AMP controller */ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_AMP) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(sdio, btsdio_table); struct btsdio_data { struct hci_dev *hdev; struct sdio_func *func; struct work_struct work; struct sk_buff_head txq; }; #define REG_RDAT 0x00 /* Receiver Data */ #define REG_TDAT 0x00 /* Transmitter Data */ #define REG_PC_RRT 0x10 /* Read Packet Control */ #define REG_PC_WRT 0x11 /* Write Packet Control */ #define REG_RTC_STAT 0x12 /* Retry Control Status */ #define REG_RTC_SET 0x12 /* Retry Control Set */ #define REG_INTRD 0x13 /* Interrupt Indication */ #define REG_CL_INTRD 0x13 /* Interrupt Clear */ #define REG_EN_INTRD 0x14 /* Interrupt Enable */ #define REG_MD_STAT 0x20 /* Bluetooth Mode Status */ static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb) { int err; BT_DBG("%s", data->hdev->name); /* Prepend Type-A header */ skb_push(skb, 4); skb->data[0] = (skb->len & 0x0000ff); skb->data[1] = (skb->len & 0x00ff00) >> 8; skb->data[2] = (skb->len & 0xff0000) >> 16; skb->data[3] = bt_cb(skb)->pkt_type; err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len); if (err < 0) { skb_pull(skb, 4); sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL); return err; } data->hdev->stat.byte_tx += skb->len; kfree_skb(skb); return 0; } static void btsdio_work(struct work_struct *work) { struct btsdio_data *data = container_of(work, struct btsdio_data, work); struct sk_buff *skb; int err; BT_DBG("%s", data->hdev->name); sdio_claim_host(data->func); while ((skb = skb_dequeue(&data->txq))) { err = btsdio_tx_packet(data, skb); if (err < 0) { data->hdev->stat.err_tx++; skb_queue_head(&data->txq, skb); break; } } sdio_release_host(data->func); } static int btsdio_rx_packet(struct btsdio_data *data) { u8 hdr[4] __attribute__ ((aligned(4))); struct sk_buff *skb; int err, len; BT_DBG("%s", data->hdev->name); err = sdio_readsb(data->func, hdr, REG_RDAT, 4); if (err < 0) return err; len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16); if (len < 4 || len > 65543) return -EILSEQ; skb = bt_skb_alloc(len - 4, GFP_KERNEL); if (!skb) { /* Out of memory. Prepare a read retry and just * return with the expectation that the next time * we're called we'll have more memory. */ return -ENOMEM; } skb_put(skb, len - 4); err = sdio_readsb(data->func, skb->data, REG_RDAT, len - 4); if (err < 0) { kfree_skb(skb); return err; } data->hdev->stat.byte_rx += len; skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = hdr[3]; err = hci_recv_frame(skb); if (err < 0) return err; sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL); return 0; } static void btsdio_interrupt(struct sdio_func *func) { struct btsdio_data *data = sdio_get_drvdata(func); int intrd; BT_DBG("%s", data->hdev->name); intrd = sdio_readb(func, REG_INTRD, NULL); if (intrd & 0x01) { sdio_writeb(func, 0x01, REG_CL_INTRD, NULL); if (btsdio_rx_packet(data) < 0) { data->hdev->stat.err_rx++; sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL); } } } static int btsdio_open(struct hci_dev *hdev) { struct btsdio_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) return 0; sdio_claim_host(data->func); err = sdio_enable_func(data->func); if (err < 0) { clear_bit(HCI_RUNNING, &hdev->flags); goto release; } err = sdio_claim_irq(data->func, btsdio_interrupt); if (err < 0) { sdio_disable_func(data->func); clear_bit(HCI_RUNNING, &hdev->flags); goto release; } if (data->func->class == SDIO_CLASS_BT_B) sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL); sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL); release: sdio_release_host(data->func); return err; } static int btsdio_close(struct hci_dev *hdev) { struct btsdio_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; sdio_claim_host(data->func); sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL); sdio_release_irq(data->func); sdio_disable_func(data->func); sdio_release_host(data->func); return 0; } static int btsdio_flush(struct hci_dev *hdev) { struct btsdio_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); skb_queue_purge(&data->txq); return 0; } static int btsdio_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct btsdio_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; default: return -EILSEQ; } skb_queue_tail(&data->txq, skb); schedule_work(&data->work); return 0; } static int btsdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct btsdio_data *data; struct hci_dev *hdev; struct sdio_func_tuple *tuple = func->tuples; int err; BT_DBG("func %p id %p class 0x%04x", func, id, func->class); while (tuple) { BT_DBG("code 0x%x size %d", tuple->code, tuple->size); tuple = tuple->next; } data = devm_kzalloc(&func->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->func = func; INIT_WORK(&data->work, btsdio_work); skb_queue_head_init(&data->txq); hdev = hci_alloc_dev(); if (!hdev) return -ENOMEM; hdev->bus = HCI_SDIO; hci_set_drvdata(hdev, data); if (id->class == SDIO_CLASS_BT_AMP) hdev->dev_type = HCI_AMP; else hdev->dev_type = HCI_BREDR; data->hdev = hdev; SET_HCIDEV_DEV(hdev, &func->dev); hdev->open = btsdio_open; hdev->close = btsdio_close; hdev->flush = btsdio_flush; hdev->send = btsdio_send_frame; err = hci_register_dev(hdev); if (err < 0) { hci_free_dev(hdev); return err; } sdio_set_drvdata(func, data); return 0; } static void btsdio_remove(struct sdio_func *func) { struct btsdio_data *data = sdio_get_drvdata(func); struct hci_dev *hdev; BT_DBG("func %p", func); if (!data) return; hdev = data->hdev; sdio_set_drvdata(func, NULL); hci_unregister_dev(hdev); hci_free_dev(hdev); } static struct sdio_driver btsdio_driver = { .name = "btsdio", .probe = btsdio_probe, .remove = btsdio_remove, .id_table = btsdio_table, }; static int __init btsdio_init(void) { BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION); return sdio_register_driver(&btsdio_driver); } static void __exit btsdio_exit(void) { sdio_unregister_driver(&btsdio_driver); } module_init(btsdio_init); module_exit(btsdio_exit); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/bluetooth/btmrvl_sdio.h0000644000175000017500000000573712026211315022554 0ustar mcgrofmcgrof/** * Marvell BT-over-SDIO driver: SDIO interface related definitions * * Copyright (C) 2009, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. * **/ #define SDIO_HEADER_LEN 4 /* SD block size can not bigger than 64 due to buf size limit in firmware */ /* define SD block size for data Tx/Rx */ #define SDIO_BLOCK_SIZE 64 /* Number of blocks for firmware transfer */ #define FIRMWARE_TRANSFER_NBLOCK 2 /* This is for firmware specific length */ #define FW_EXTRA_LEN 36 #define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) #define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \ (HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN) #define ALLOC_BUF_SIZE (((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \ MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \ + SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \ * SDIO_BLOCK_SIZE) /* The number of times to try when polling for status */ #define MAX_POLL_TRIES 100 /* Max retry number of CMD53 write */ #define MAX_WRITE_IOMEM_RETRY 2 /* register bitmasks */ #define HOST_POWER_UP BIT(1) #define HOST_CMD53_FIN BIT(2) #define HIM_DISABLE 0xff #define HIM_ENABLE (BIT(0) | BIT(1)) #define UP_LD_HOST_INT_STATUS BIT(0) #define DN_LD_HOST_INT_STATUS BIT(1) #define DN_LD_CARD_RDY BIT(0) #define CARD_IO_READY BIT(3) #define FIRMWARE_READY 0xfedc struct btmrvl_sdio_card_reg { u8 cfg; u8 host_int_mask; u8 host_intstatus; u8 card_status; u8 sq_read_base_addr_a0; u8 sq_read_base_addr_a1; u8 card_revision; u8 card_fw_status0; u8 card_fw_status1; u8 card_rx_len; u8 card_rx_unit; u8 io_port_0; u8 io_port_1; u8 io_port_2; }; struct btmrvl_sdio_card { struct sdio_func *func; u32 ioport; const char *helper; const char *firmware; const struct btmrvl_sdio_card_reg *reg; u16 sd_blksz_fw_dl; u8 rx_unit; struct btmrvl_private *priv; }; struct btmrvl_sdio_device { const char *helper; const char *firmware; const struct btmrvl_sdio_card_reg *reg; u16 sd_blksz_fw_dl; }; /* Platform specific DMA alignment */ #define BTSDIO_DMA_ALIGN 8 /* Macros for Data Alignment : size */ #define ALIGN_SZ(p, a) \ (((p) + ((a) - 1)) & ~((a) - 1)) /* Macros for Data Alignment : address */ #define ALIGN_ADDR(p, a) \ ((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \ ~(((unsigned long)(a)) - 1)) compat-drivers-2012-09-18/drivers/bluetooth/btmrvl_main.c0000644000175000017500000003604212026211315022526 0ustar mcgrofmcgrof/** * Marvell Bluetooth driver * * Copyright (C) 2009, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. **/ #include #include #include #include "btmrvl_drv.h" #define VERSION "1.0" /* * This function is called by interface specific interrupt handler. * It updates Power Save & Host Sleep states, and wakes up the main * thread. */ void btmrvl_interrupt(struct btmrvl_private *priv) { priv->adapter->ps_state = PS_AWAKE; priv->adapter->wakeup_tries = 0; priv->adapter->int_count++; wake_up_interruptible(&priv->main_thread.wait_q); } EXPORT_SYMBOL_GPL(btmrvl_interrupt); bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; if (hdr->evt == HCI_EV_CMD_COMPLETE) { struct hci_ev_cmd_complete *ec; u16 opcode, ocf, ogf; ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE); opcode = __le16_to_cpu(ec->opcode); ocf = hci_opcode_ocf(opcode); ogf = hci_opcode_ogf(opcode); if (ocf == BT_CMD_MODULE_CFG_REQ && priv->btmrvl_dev.sendcmdflag) { priv->btmrvl_dev.sendcmdflag = false; priv->adapter->cmd_complete = true; wake_up_interruptible(&priv->adapter->cmd_wait_q); } if (ogf == OGF) { BT_DBG("vendor event skipped: ogf 0x%4.4x ocf 0x%4.4x", ogf, ocf); kfree_skb(skb); return false; } } return true; } EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt); int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) { struct btmrvl_adapter *adapter = priv->adapter; struct btmrvl_event *event; int ret = 0; event = (struct btmrvl_event *) skb->data; if (event->ec != 0xff) { BT_DBG("Not Marvell Event=%x", event->ec); ret = -EINVAL; goto exit; } switch (event->data[0]) { case BT_CMD_AUTO_SLEEP_MODE: if (!event->data[2]) { if (event->data[1] == BT_PS_ENABLE) adapter->psmode = 1; else adapter->psmode = 0; BT_DBG("PS Mode:%s", (adapter->psmode) ? "Enable" : "Disable"); } else { BT_DBG("PS Mode command failed"); } break; case BT_CMD_HOST_SLEEP_CONFIG: if (!event->data[3]) BT_DBG("gpio=%x, gap=%x", event->data[1], event->data[2]); else BT_DBG("HSCFG command failed"); break; case BT_CMD_HOST_SLEEP_ENABLE: if (!event->data[1]) { adapter->hs_state = HS_ACTIVATED; if (adapter->psmode) adapter->ps_state = PS_SLEEP; wake_up_interruptible(&adapter->cmd_wait_q); BT_DBG("HS ACTIVATED!"); } else { BT_DBG("HS Enable failed"); } break; case BT_CMD_MODULE_CFG_REQ: if (priv->btmrvl_dev.sendcmdflag && event->data[1] == MODULE_BRINGUP_REQ) { BT_DBG("EVENT:%s", ((event->data[2] == MODULE_BROUGHT_UP) || (event->data[2] == MODULE_ALREADY_UP)) ? "Bring-up succeed" : "Bring-up failed"); if (event->length > 3 && event->data[3]) priv->btmrvl_dev.dev_type = HCI_AMP; else priv->btmrvl_dev.dev_type = HCI_BREDR; BT_DBG("dev_type: %d", priv->btmrvl_dev.dev_type); } else if (priv->btmrvl_dev.sendcmdflag && event->data[1] == MODULE_SHUTDOWN_REQ) { BT_DBG("EVENT:%s", (event->data[2]) ? "Shutdown failed" : "Shutdown succeed"); } else { BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP"); ret = -EINVAL; } break; case BT_EVENT_POWER_STATE: if (event->data[1] == BT_PS_SLEEP) adapter->ps_state = PS_SLEEP; BT_DBG("EVENT:%s", (adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); break; default: BT_DBG("Unknown Event=%d", event->data[0]); ret = -EINVAL; break; } exit: if (!ret) kfree_skb(skb); return ret; } EXPORT_SYMBOL_GPL(btmrvl_process_event); int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) { struct sk_buff *skb; struct btmrvl_cmd *cmd; int ret = 0; skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); if (skb == NULL) { BT_ERR("No free skb"); return -ENOMEM; } cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ)); cmd->length = 1; cmd->data[0] = subcmd; bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; skb->dev = (void *) priv->btmrvl_dev.hcidev; skb_queue_head(&priv->adapter->tx_queue, skb); priv->btmrvl_dev.sendcmdflag = true; priv->adapter->cmd_complete = false; BT_DBG("Queue module cfg Command"); wake_up_interruptible(&priv->main_thread.wait_q); if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) { ret = -ETIMEDOUT; BT_ERR("module_cfg_cmd(%x): timeout: %d", subcmd, priv->btmrvl_dev.sendcmdflag); } BT_DBG("module cfg Command done"); return ret; } EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) { struct sk_buff *skb; struct btmrvl_cmd *cmd; skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); if (!skb) { BT_ERR("No free skb"); return -ENOMEM; } cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_CONFIG)); cmd->length = 2; cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff); bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; skb->dev = (void *) priv->btmrvl_dev.hcidev; skb_queue_head(&priv->adapter->tx_queue, skb); BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x", cmd->data[0], cmd->data[1]); return 0; } EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd); int btmrvl_enable_ps(struct btmrvl_private *priv) { struct sk_buff *skb; struct btmrvl_cmd *cmd; skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); if (skb == NULL) { BT_ERR("No free skb"); return -ENOMEM; } cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_AUTO_SLEEP_MODE)); cmd->length = 1; if (priv->btmrvl_dev.psmode) cmd->data[0] = BT_PS_ENABLE; else cmd->data[0] = BT_PS_DISABLE; bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; skb->dev = (void *) priv->btmrvl_dev.hcidev; skb_queue_head(&priv->adapter->tx_queue, skb); BT_DBG("Queue PSMODE Command:%d", cmd->data[0]); return 0; } EXPORT_SYMBOL_GPL(btmrvl_enable_ps); int btmrvl_enable_hs(struct btmrvl_private *priv) { struct sk_buff *skb; struct btmrvl_cmd *cmd; int ret = 0; skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); if (skb == NULL) { BT_ERR("No free skb"); return -ENOMEM; } cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE)); cmd->length = 0; bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; skb->dev = (void *) priv->btmrvl_dev.hcidev; skb_queue_head(&priv->adapter->tx_queue, skb); BT_DBG("Queue hs enable Command"); wake_up_interruptible(&priv->main_thread.wait_q); if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, priv->adapter->hs_state, msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) { ret = -ETIMEDOUT; BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state, priv->adapter->ps_state, priv->adapter->wakeup_tries); } return ret; } EXPORT_SYMBOL_GPL(btmrvl_enable_hs); int btmrvl_prepare_command(struct btmrvl_private *priv) { int ret = 0; if (priv->btmrvl_dev.hscfgcmd) { priv->btmrvl_dev.hscfgcmd = 0; btmrvl_send_hscfg_cmd(priv); } if (priv->btmrvl_dev.pscmd) { priv->btmrvl_dev.pscmd = 0; btmrvl_enable_ps(priv); } if (priv->btmrvl_dev.hscmd) { priv->btmrvl_dev.hscmd = 0; if (priv->btmrvl_dev.hsmode) { ret = btmrvl_enable_hs(priv); } else { ret = priv->hw_wakeup_firmware(priv); priv->adapter->hs_state = HS_DEACTIVATED; } } return ret; } static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) { int ret = 0; if (!skb || !skb->data) return -EINVAL; if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) { BT_ERR("Tx Error: Bad skb length %d : %d", skb->len, BTM_UPLD_SIZE); return -EINVAL; } if (skb_headroom(skb) < BTM_HEADER_LEN) { struct sk_buff *tmp = skb; skb = skb_realloc_headroom(skb, BTM_HEADER_LEN); if (!skb) { BT_ERR("Tx Error: realloc_headroom failed %d", BTM_HEADER_LEN); skb = tmp; return -EINVAL; } kfree_skb(tmp); } skb_push(skb, BTM_HEADER_LEN); /* header type: byte[3] * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor * header length: byte[2][1][0] */ skb->data[0] = (skb->len & 0x0000ff); skb->data[1] = (skb->len & 0x00ff00) >> 8; skb->data[2] = (skb->len & 0xff0000) >> 16; skb->data[3] = bt_cb(skb)->pkt_type; if (priv->hw_host_to_card) ret = priv->hw_host_to_card(priv, skb->data, skb->len); return ret; } static void btmrvl_init_adapter(struct btmrvl_private *priv) { skb_queue_head_init(&priv->adapter->tx_queue); priv->adapter->ps_state = PS_AWAKE; init_waitqueue_head(&priv->adapter->cmd_wait_q); } static void btmrvl_free_adapter(struct btmrvl_private *priv) { skb_queue_purge(&priv->adapter->tx_queue); kfree(priv->adapter); priv->adapter = NULL; } static int btmrvl_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } static int btmrvl_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct btmrvl_private *priv = NULL; BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len); if (!hdev) { BT_ERR("Frame for unknown HCI device"); return -ENODEV; } priv = hci_get_drvdata(hdev); if (!test_bit(HCI_RUNNING, &hdev->flags)) { BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags); print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET, skb->data, skb->len); return -EBUSY; } switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; } skb_queue_tail(&priv->adapter->tx_queue, skb); wake_up_interruptible(&priv->main_thread.wait_q); return 0; } static int btmrvl_flush(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); skb_queue_purge(&priv->adapter->tx_queue); return 0; } static int btmrvl_close(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; skb_queue_purge(&priv->adapter->tx_queue); return 0; } static int btmrvl_open(struct hci_dev *hdev) { set_bit(HCI_RUNNING, &hdev->flags); return 0; } /* * This function handles the event generated by firmware, rx data * received from firmware, and tx data sent from kernel. */ static int btmrvl_service_main_thread(void *data) { struct btmrvl_thread *thread = data; struct btmrvl_private *priv = thread->priv; struct btmrvl_adapter *adapter = priv->adapter; wait_queue_t wait; struct sk_buff *skb; ulong flags; init_waitqueue_entry(&wait, current); for (;;) { add_wait_queue(&thread->wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); if (adapter->wakeup_tries || ((!adapter->int_count) && (!priv->btmrvl_dev.tx_dnld_rdy || skb_queue_empty(&adapter->tx_queue)))) { BT_DBG("main_thread is sleeping..."); schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&thread->wait_q, &wait); BT_DBG("main_thread woke up"); if (kthread_should_stop()) { BT_DBG("main_thread: break from main thread"); break; } spin_lock_irqsave(&priv->driver_lock, flags); if (adapter->int_count) { adapter->int_count = 0; spin_unlock_irqrestore(&priv->driver_lock, flags); priv->hw_process_int_status(priv); } else if (adapter->ps_state == PS_SLEEP && !skb_queue_empty(&adapter->tx_queue)) { spin_unlock_irqrestore(&priv->driver_lock, flags); adapter->wakeup_tries++; priv->hw_wakeup_firmware(priv); continue; } else { spin_unlock_irqrestore(&priv->driver_lock, flags); } if (adapter->ps_state == PS_SLEEP) continue; if (!priv->btmrvl_dev.tx_dnld_rdy) continue; skb = skb_dequeue(&adapter->tx_queue); if (skb) { if (btmrvl_tx_pkt(priv, skb)) priv->btmrvl_dev.hcidev->stat.err_tx++; else priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len; kfree_skb(skb); } } return 0; } int btmrvl_register_hdev(struct btmrvl_private *priv) { struct hci_dev *hdev = NULL; int ret; hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can not allocate HCI device"); goto err_hdev; } priv->btmrvl_dev.hcidev = hdev; hci_set_drvdata(hdev, priv); hdev->bus = HCI_SDIO; hdev->open = btmrvl_open; hdev->close = btmrvl_close; hdev->flush = btmrvl_flush; hdev->send = btmrvl_send_frame; hdev->ioctl = btmrvl_ioctl; btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); hdev->dev_type = priv->btmrvl_dev.dev_type; ret = hci_register_dev(hdev); if (ret < 0) { BT_ERR("Can not register HCI device"); goto err_hci_register_dev; } #ifdef CONFIG_DEBUG_FS btmrvl_debugfs_init(hdev); #endif return 0; err_hci_register_dev: hci_free_dev(hdev); err_hdev: /* Stop the thread servicing the interrupts */ kthread_stop(priv->main_thread.task); btmrvl_free_adapter(priv); kfree(priv); return -ENOMEM; } EXPORT_SYMBOL_GPL(btmrvl_register_hdev); struct btmrvl_private *btmrvl_add_card(void *card) { struct btmrvl_private *priv; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { BT_ERR("Can not allocate priv"); goto err_priv; } priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL); if (!priv->adapter) { BT_ERR("Allocate buffer for btmrvl_adapter failed!"); goto err_adapter; } btmrvl_init_adapter(priv); BT_DBG("Starting kthread..."); priv->main_thread.priv = priv; spin_lock_init(&priv->driver_lock); init_waitqueue_head(&priv->main_thread.wait_q); priv->main_thread.task = kthread_run(btmrvl_service_main_thread, &priv->main_thread, "btmrvl_main_service"); priv->btmrvl_dev.card = card; priv->btmrvl_dev.tx_dnld_rdy = true; return priv; err_adapter: kfree(priv); err_priv: return NULL; } EXPORT_SYMBOL_GPL(btmrvl_add_card); int btmrvl_remove_card(struct btmrvl_private *priv) { struct hci_dev *hdev; hdev = priv->btmrvl_dev.hcidev; wake_up_interruptible(&priv->adapter->cmd_wait_q); kthread_stop(priv->main_thread.task); #ifdef CONFIG_DEBUG_FS btmrvl_debugfs_remove(hdev); #endif hci_unregister_dev(hdev); hci_free_dev(hdev); priv->btmrvl_dev.hcidev = NULL; btmrvl_free_adapter(priv); kfree(priv); return 0; } EXPORT_SYMBOL_GPL(btmrvl_remove_card); MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL v2"); compat-drivers-2012-09-18/drivers/bluetooth/btmrvl_drv.h0000644000175000017500000000756012026211315022405 0ustar mcgrofmcgrof/* * Marvell Bluetooth driver: global definitions & declarations * * Copyright (C) 2009, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. * */ #include #include #include #include #define BTM_HEADER_LEN 4 #define BTM_UPLD_SIZE 2312 /* Time to wait until Host Sleep state change in millisecond */ #define WAIT_UNTIL_HS_STATE_CHANGED 5000 /* Time to wait for command response in millisecond */ #define WAIT_UNTIL_CMD_RESP 5000 struct btmrvl_thread { struct task_struct *task; wait_queue_head_t wait_q; void *priv; }; struct btmrvl_device { void *card; struct hci_dev *hcidev; u8 dev_type; u8 tx_dnld_rdy; u8 psmode; u8 pscmd; u8 hsmode; u8 hscmd; /* Low byte is gap, high byte is GPIO */ u16 gpio_gap; u8 hscfgcmd; u8 sendcmdflag; }; struct btmrvl_adapter { u32 int_count; struct sk_buff_head tx_queue; u8 psmode; u8 ps_state; u8 hs_state; u8 wakeup_tries; wait_queue_head_t cmd_wait_q; u8 cmd_complete; bool is_suspended; }; struct btmrvl_private { struct btmrvl_device btmrvl_dev; struct btmrvl_adapter *adapter; struct btmrvl_thread main_thread; int (*hw_host_to_card) (struct btmrvl_private *priv, u8 *payload, u16 nb); int (*hw_wakeup_firmware) (struct btmrvl_private *priv); int (*hw_process_int_status) (struct btmrvl_private *priv); spinlock_t driver_lock; /* spinlock used by driver */ #ifdef CONFIG_DEBUG_FS void *debugfs_data; #endif }; #define MRVL_VENDOR_PKT 0xFE /* Bluetooth commands */ #define BT_CMD_AUTO_SLEEP_MODE 0x23 #define BT_CMD_HOST_SLEEP_CONFIG 0x59 #define BT_CMD_HOST_SLEEP_ENABLE 0x5A #define BT_CMD_MODULE_CFG_REQ 0x5B /* Sub-commands: Module Bringup/Shutdown Request/Response */ #define MODULE_BRINGUP_REQ 0xF1 #define MODULE_BROUGHT_UP 0x00 #define MODULE_ALREADY_UP 0x0C #define MODULE_SHUTDOWN_REQ 0xF2 #define BT_EVENT_POWER_STATE 0x20 /* Bluetooth Power States */ #define BT_PS_ENABLE 0x02 #define BT_PS_DISABLE 0x03 #define BT_PS_SLEEP 0x01 #define OGF 0x3F /* Host Sleep states */ #define HS_ACTIVATED 0x01 #define HS_DEACTIVATED 0x00 /* Power Save modes */ #define PS_SLEEP 0x01 #define PS_AWAKE 0x00 struct btmrvl_cmd { __le16 ocf_ogf; u8 length; u8 data[4]; } __packed; struct btmrvl_event { u8 ec; /* event counter */ u8 length; u8 data[4]; } __packed; /* Prototype of global function */ int btmrvl_register_hdev(struct btmrvl_private *priv); struct btmrvl_private *btmrvl_add_card(void *card); int btmrvl_remove_card(struct btmrvl_private *priv); void btmrvl_interrupt(struct btmrvl_private *priv); bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb); int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb); int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd); int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); int btmrvl_enable_ps(struct btmrvl_private *priv); int btmrvl_prepare_command(struct btmrvl_private *priv); int btmrvl_enable_hs(struct btmrvl_private *priv); #ifdef CONFIG_DEBUG_FS void btmrvl_debugfs_init(struct hci_dev *hdev); void btmrvl_debugfs_remove(struct hci_dev *hdev); #endif compat-drivers-2012-09-18/drivers/bluetooth/btmrvl_debugfs.c0000644000175000017500000002640112026211315023217 0ustar mcgrofmcgrof/** * Marvell Bluetooth driver: debugfs related functions * * Copyright (C) 2009, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. **/ #include #include #include #include #include "btmrvl_drv.h" struct btmrvl_debugfs_data { struct dentry *config_dir; struct dentry *status_dir; /* config */ struct dentry *psmode; struct dentry *pscmd; struct dentry *hsmode; struct dentry *hscmd; struct dentry *gpiogap; struct dentry *hscfgcmd; /* status */ struct dentry *curpsmode; struct dentry *hsstate; struct dentry *psstate; struct dentry *txdnldready; }; static ssize_t btmrvl_hscfgcmd_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; long result, ret; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; ret = strict_strtol(buf, 10, &result); if (ret) return ret; priv->btmrvl_dev.hscfgcmd = result; if (priv->btmrvl_dev.hscfgcmd) { btmrvl_prepare_command(priv); wake_up_interruptible(&priv->main_thread.wait_q); } return count; } static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscfgcmd); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_hscfgcmd_fops = { .read = btmrvl_hscfgcmd_read, .write = btmrvl_hscfgcmd_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_psmode_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; long result, ret; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; ret = strict_strtol(buf, 10, &result); if (ret) return ret; priv->btmrvl_dev.psmode = result; return count; } static ssize_t btmrvl_psmode_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.psmode); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_psmode_fops = { .read = btmrvl_psmode_read, .write = btmrvl_psmode_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; long result, ret; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; ret = strict_strtol(buf, 10, &result); if (ret) return ret; priv->btmrvl_dev.pscmd = result; if (priv->btmrvl_dev.pscmd) { btmrvl_prepare_command(priv); wake_up_interruptible(&priv->main_thread.wait_q); } return count; } static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.pscmd); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_pscmd_fops = { .read = btmrvl_pscmd_read, .write = btmrvl_pscmd_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_gpiogap_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; long result, ret; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; ret = strict_strtol(buf, 16, &result); if (ret) return ret; priv->btmrvl_dev.gpio_gap = result; return count; } static ssize_t btmrvl_gpiogap_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "0x%x\n", priv->btmrvl_dev.gpio_gap); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_gpiogap_fops = { .read = btmrvl_gpiogap_read, .write = btmrvl_gpiogap_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; long result, ret; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; ret = strict_strtol(buf, 10, &result); if (ret) return ret; priv->btmrvl_dev.hscmd = result; if (priv->btmrvl_dev.hscmd) { btmrvl_prepare_command(priv); wake_up_interruptible(&priv->main_thread.wait_q); } return count; } static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscmd); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_hscmd_fops = { .read = btmrvl_hscmd_read, .write = btmrvl_hscmd_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_hsmode_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; long result, ret; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; ret = strict_strtol(buf, 10, &result); if (ret) return ret; priv->btmrvl_dev.hsmode = result; return count; } static ssize_t btmrvl_hsmode_read(struct file *file, char __user * userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hsmode); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_hsmode_fops = { .read = btmrvl_hsmode_read, .write = btmrvl_hsmode_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_curpsmode_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->psmode); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_curpsmode_fops = { .read = btmrvl_curpsmode_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_psstate_read(struct file *file, char __user * userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->ps_state); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_psstate_fops = { .read = btmrvl_psstate_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_hsstate_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->hs_state); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_hsstate_fops = { .read = btmrvl_hsstate_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t btmrvl_txdnldready_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; int ret; ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.tx_dnld_rdy); return simple_read_from_buffer(userbuf, count, ppos, buf, ret); } static const struct file_operations btmrvl_txdnldready_fops = { .read = btmrvl_txdnldready_read, .open = simple_open, .llseek = default_llseek, }; void btmrvl_debugfs_init(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); struct btmrvl_debugfs_data *dbg; if (!hdev->debugfs) return; dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); priv->debugfs_data = dbg; if (!dbg) { BT_ERR("Can not allocate memory for btmrvl_debugfs_data."); return; } dbg->config_dir = debugfs_create_dir("config", hdev->debugfs); dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir, priv, &btmrvl_psmode_fops); dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir, priv, &btmrvl_pscmd_fops); dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir, priv, &btmrvl_gpiogap_fops); dbg->hsmode = debugfs_create_file("hsmode", 0644, dbg->config_dir, priv, &btmrvl_hsmode_fops); dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir, priv, &btmrvl_hscmd_fops); dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, priv, &btmrvl_hscfgcmd_fops); dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); dbg->curpsmode = debugfs_create_file("curpsmode", 0444, dbg->status_dir, priv, &btmrvl_curpsmode_fops); dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir, priv, &btmrvl_psstate_fops); dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir, priv, &btmrvl_hsstate_fops); dbg->txdnldready = debugfs_create_file("txdnldready", 0444, dbg->status_dir, priv, &btmrvl_txdnldready_fops); } void btmrvl_debugfs_remove(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); struct btmrvl_debugfs_data *dbg = priv->debugfs_data; if (!dbg) return; debugfs_remove(dbg->psmode); debugfs_remove(dbg->pscmd); debugfs_remove(dbg->gpiogap); debugfs_remove(dbg->hsmode); debugfs_remove(dbg->hscmd); debugfs_remove(dbg->hscfgcmd); debugfs_remove(dbg->config_dir); debugfs_remove(dbg->curpsmode); debugfs_remove(dbg->psstate); debugfs_remove(dbg->hsstate); debugfs_remove(dbg->txdnldready); debugfs_remove(dbg->status_dir); kfree(dbg); } compat-drivers-2012-09-18/drivers/bluetooth/Makefile0000644000175000017500000000200112026211315021474 0ustar mcgrofmcgrof# # Makefile for the Linux Bluetooth HCI device drivers. # obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o obj-$(CONFIG_BT_HCIUART) += hci_uart.o obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o obj-$(CONFIG_BT_HCIBTUSB) += btusb.o obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o obj-$(CONFIG_BT_ATH3K) += ath3k.o obj-$(CONFIG_BT_MRVL) += btmrvl.o obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o obj-$(CONFIG_BT_WILINK) += btwilink.o btmrvl-y := btmrvl_main.o btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o hci_uart-objs := $(hci_uart-y) compat-drivers-2012-09-18/drivers/bluetooth/Kconfig0000644000175000017500000001710412026211315021351 0ustar mcgrofmcgrof menu "Bluetooth device drivers" depends on BT config BT_HCIBTUSB tristate "HCI USB driver" depends on USB help Bluetooth HCI USB driver. This driver is required if you want to use Bluetooth devices with USB interface. Say Y here to compile support for Bluetooth USB devices into the kernel or say M to compile it as module (btusb). config BT_HCIBTSDIO tristate "HCI SDIO driver" depends on MMC help Bluetooth HCI SDIO driver. This driver is required if you want to use Bluetooth device with SDIO interface. Say Y here to compile support for Bluetooth SDIO devices into the kernel or say M to compile it as module (btsdio). config BT_HCIUART tristate "HCI UART driver" help Bluetooth HCI UART driver. This driver is required if you want to use Bluetooth devices with serial port interface. You will also need this driver if you have UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card adapter and BrainBoxes Bluetooth PC Card. Say Y here to compile support for Bluetooth UART devices into the kernel or say M to compile it as module (hci_uart). config BT_HCIUART_H4 bool "UART (H4) protocol support" depends on BT_HCIUART help UART (H4) is serial protocol for communication between Bluetooth device and host. This protocol is required for most Bluetooth devices with UART interface, including PCMCIA and CF cards. Say Y here to compile support for HCI UART (H4) protocol. config BT_HCIUART_BCSP bool "BCSP protocol support" depends on BT_HCIUART select BITREVERSE help BCSP (BlueCore Serial Protocol) is serial protocol for communication between Bluetooth device and host. This protocol is required for non USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and CF cards. Say Y here to compile support for HCI BCSP protocol. config BT_HCIUART_ATH3K bool "Atheros AR300x serial support" depends on BT_HCIUART help HCIATH3K (HCI Atheros AR300x) is a serial protocol for communication between host and Atheros AR300x Bluetooth devices. This protocol enables AR300x chips to be enabled with power management support. Enable this if you have Atheros AR300x serial Bluetooth device. Say Y here to compile support for HCI UART ATH3K protocol. config BT_HCIUART_LL bool "HCILL protocol support" depends on BT_HCIUART help HCILL (HCI Low Level) is a serial protocol for communication between Bluetooth device and host. This protocol is required for serial Bluetooth devices that are based on Texas Instruments' BRF chips. Say Y here to compile support for HCILL protocol. config BT_HCIUART_3WIRE bool "Three-wire UART (H5) protocol support" depends on BT_HCIUART help The HCI Three-wire UART Transport Layer makes it possible to user the Bluetooth HCI over a serial port interface. The HCI Three-wire UART Transport Layer assumes that the UART communication may have bit errors, overrun errors or burst errors and thereby making CTS/RTS lines unnecessary. Say Y here to compile support for Three-wire UART protocol. config BT_HCIBCM203X tristate "HCI BCM203x USB driver" depends on USB select FW_LOADER help Bluetooth HCI BCM203x USB driver. This driver provides the firmware loading mechanism for the Broadcom Blutonium based devices. Say Y here to compile support for HCI BCM203x devices into the kernel or say M to compile it as module (bcm203x). config BT_HCIBPA10X tristate "HCI BPA10x USB driver" depends on USB help Bluetooth HCI BPA10x USB driver. This driver provides support for the Digianswer BPA 100/105 Bluetooth sniffer devices. Say Y here to compile support for HCI BPA10x devices into the kernel or say M to compile it as module (bpa10x). config BT_HCIBFUSB tristate "HCI BlueFRITZ! USB driver" depends on USB select FW_LOADER help Bluetooth HCI BlueFRITZ! USB driver. This driver provides support for Bluetooth USB devices with AVM interface: AVM BlueFRITZ! USB Say Y here to compile support for HCI BFUSB devices into the kernel or say M to compile it as module (bfusb). config BT_HCIDTL1 tristate "HCI DTL1 (PC Card) driver" depends on PCMCIA help Bluetooth HCI DTL1 (PC Card) driver. This driver provides support for Bluetooth PCMCIA devices with Nokia DTL1 interface: Nokia Bluetooth Card Socket Bluetooth CF Card Say Y here to compile support for HCI DTL1 devices into the kernel or say M to compile it as module (dtl1_cs). config BT_HCIBT3C tristate "HCI BT3C (PC Card) driver" depends on PCMCIA select FW_LOADER help Bluetooth HCI BT3C (PC Card) driver. This driver provides support for Bluetooth PCMCIA devices with 3Com BT3C interface: 3Com Bluetooth Card (3CRWB6096) HP Bluetooth Card Say Y here to compile support for HCI BT3C devices into the kernel or say M to compile it as module (bt3c_cs). config BT_HCIBLUECARD tristate "HCI BlueCard (PC Card) driver" depends on PCMCIA help Bluetooth HCI BlueCard (PC Card) driver. This driver provides support for Bluetooth PCMCIA devices with Anycom BlueCard interface: Anycom Bluetooth PC Card Anycom Bluetooth CF Card Say Y here to compile support for HCI BlueCard devices into the kernel or say M to compile it as module (bluecard_cs). config BT_HCIBTUART tristate "HCI UART (PC Card) device driver" depends on PCMCIA help Bluetooth HCI UART (PC Card) driver. This driver provides support for Bluetooth PCMCIA devices with an UART interface: Xircom CreditCard Bluetooth Adapter Xircom RealPort2 Bluetooth Adapter Sphinx PICO Card H-Soft blue+Card Cyber-blue Compact Flash Card Say Y here to compile support for HCI UART devices into the kernel or say M to compile it as module (btuart_cs). config BT_HCIVHCI tristate "HCI VHCI (Virtual HCI device) driver" help Bluetooth Virtual HCI device driver. This driver is required if you want to use HCI Emulation software. Say Y here to compile support for virtual HCI devices into the kernel or say M to compile it as module (hci_vhci). config BT_MRVL tristate "Marvell Bluetooth driver support" help The core driver to support Marvell Bluetooth devices. This driver is required if you want to support Marvell Bluetooth devices, such as 8688/8787/8797. Say Y here to compile Marvell Bluetooth driver into the kernel or say M to compile it as module. config BT_MRVL_SDIO tristate "Marvell BT-over-SDIO driver" depends on BT_MRVL && MMC select FW_LOADER help The driver for Marvell Bluetooth chipsets with SDIO interface. This driver is required if you want to use Marvell Bluetooth devices with SDIO interface. Currently SD8688/SD8787/SD8797 chipsets are supported. Say Y here to compile support for Marvell BT-over-SDIO driver into the kernel or say M to compile it as module. config BT_ATH3K tristate "Atheros firmware download driver" depends on BT_HCIBTUSB select FW_LOADER help Bluetooth firmware download driver. This driver loads the firmware into the Atheros Bluetooth chipset. Say Y here to compile support for "Atheros firmware download driver" into the kernel or say M to compile it as module (ath3k). config BT_WILINK tristate "Texas Instruments WiLink7 driver" depends on TI_ST help This enables the Bluetooth driver for Texas Instrument's BT/FM/GPS combo devices. This makes use of shared transport line discipline core driver to communicate with the BT core of the combo chip. Say Y here to compile support for Texas Instrument's WiLink7 driver into the kernel or say M to compile it as module. endmenu compat-drivers-2012-09-18/drivers/staging/0000755000175000017500000000000012026211315017472 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/ssb/0000755000175000017500000000000012026211315016625 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/ssb/main.c0000644000175000017500000010240112026211315017713 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Subsystem core * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include "ssb_private.h" #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DESCRIPTION("Sonics Silicon Backplane driver"); MODULE_LICENSE("GPL"); /* Temporary list of yet-to-be-attached buses */ static LIST_HEAD(attach_queue); /* List if running buses */ static LIST_HEAD(buses); /* Software ID counter */ static unsigned int next_busnumber; /* buses_mutes locks the two buslists and the next_busnumber. * Don't lock this directly, but use ssb_buses_[un]lock() below. */ static DEFINE_MUTEX(buses_mutex); /* There are differences in the codeflow, if the bus is * initialized from early boot, as various needed services * are not available early. This is a mechanism to delay * these initializations to after early boot has finished. * It's also used to avoid mutex locking, as that's not * available and needed early. */ static bool ssb_is_early_boot = 1; static void ssb_buses_lock(void); static void ssb_buses_unlock(void); #ifdef CONFIG_SSB_PCIHOST struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev) { struct ssb_bus *bus; ssb_buses_lock(); list_for_each_entry(bus, &buses, list) { if (bus->bustype == SSB_BUSTYPE_PCI && bus->host_pci == pdev) goto found; } bus = NULL; found: ssb_buses_unlock(); return bus; } #endif /* CONFIG_SSB_PCIHOST */ #ifdef CONFIG_SSB_PCMCIAHOST struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev) { struct ssb_bus *bus; ssb_buses_lock(); list_for_each_entry(bus, &buses, list) { if (bus->bustype == SSB_BUSTYPE_PCMCIA && bus->host_pcmcia == pdev) goto found; } bus = NULL; found: ssb_buses_unlock(); return bus; } #endif /* CONFIG_SSB_PCMCIAHOST */ #ifdef CONFIG_SSB_SDIOHOST struct ssb_bus *ssb_sdio_func_to_bus(struct sdio_func *func) { struct ssb_bus *bus; ssb_buses_lock(); list_for_each_entry(bus, &buses, list) { if (bus->bustype == SSB_BUSTYPE_SDIO && bus->host_sdio == func) goto found; } bus = NULL; found: ssb_buses_unlock(); return bus; } #endif /* CONFIG_SSB_SDIOHOST */ int ssb_for_each_bus_call(unsigned long data, int (*func)(struct ssb_bus *bus, unsigned long data)) { struct ssb_bus *bus; int res; ssb_buses_lock(); list_for_each_entry(bus, &buses, list) { res = func(bus, data); if (res >= 0) { ssb_buses_unlock(); return res; } } ssb_buses_unlock(); return -ENODEV; } static struct ssb_device *ssb_device_get(struct ssb_device *dev) { if (dev) get_device(dev->dev); return dev; } static void ssb_device_put(struct ssb_device *dev) { if (dev) put_device(dev->dev); } static int ssb_device_resume(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_driver *ssb_drv; int err = 0; if (dev->driver) { ssb_drv = drv_to_ssb_drv(dev->driver); if (ssb_drv && ssb_drv->resume) err = ssb_drv->resume(ssb_dev); if (err) goto out; } out: return err; } static int ssb_device_suspend(struct device *dev, pm_message_t state) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_driver *ssb_drv; int err = 0; if (dev->driver) { ssb_drv = drv_to_ssb_drv(dev->driver); if (ssb_drv && ssb_drv->suspend) err = ssb_drv->suspend(ssb_dev, state); if (err) goto out; } out: return err; } int ssb_bus_resume(struct ssb_bus *bus) { int err; /* Reset HW state information in memory, so that HW is * completely reinitialized. */ bus->mapped_device = NULL; #ifdef CONFIG_SSB_DRIVER_PCICORE bus->pcicore.setup_done = 0; #endif err = ssb_bus_powerup(bus, 0); if (err) return err; err = ssb_pcmcia_hardware_setup(bus); if (err) { ssb_bus_may_powerdown(bus); return err; } ssb_chipco_resume(&bus->chipco); ssb_bus_may_powerdown(bus); return 0; } EXPORT_SYMBOL(ssb_bus_resume); int ssb_bus_suspend(struct ssb_bus *bus) { ssb_chipco_suspend(&bus->chipco); ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); return 0; } EXPORT_SYMBOL(ssb_bus_suspend); #ifdef CONFIG_SSB_SPROM /** ssb_devices_freeze - Freeze all devices on the bus. * * After freezing no device driver will be handling a device * on this bus anymore. ssb_devices_thaw() must be called after * a successful freeze to reactivate the devices. * * @bus: The bus. * @ctx: Context structure. Pass this to ssb_devices_thaw(). */ int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx) { struct ssb_device *sdev; struct ssb_driver *sdrv; unsigned int i; memset(ctx, 0, sizeof(*ctx)); ctx->bus = bus; SSB_WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen)); for (i = 0; i < bus->nr_devices; i++) { sdev = ssb_device_get(&bus->devices[i]); if (!sdev->dev || !sdev->dev->driver || !device_is_registered(sdev->dev)) { ssb_device_put(sdev); continue; } sdrv = drv_to_ssb_drv(sdev->dev->driver); if (SSB_WARN_ON(!sdrv->remove)) continue; sdrv->remove(sdev); ctx->device_frozen[i] = 1; } return 0; } /** ssb_devices_thaw - Unfreeze all devices on the bus. * * This will re-attach the device drivers and re-init the devices. * * @ctx: The context structure from ssb_devices_freeze() */ int ssb_devices_thaw(struct ssb_freeze_context *ctx) { struct ssb_bus *bus = ctx->bus; struct ssb_device *sdev; struct ssb_driver *sdrv; unsigned int i; int err, result = 0; for (i = 0; i < bus->nr_devices; i++) { if (!ctx->device_frozen[i]) continue; sdev = &bus->devices[i]; if (SSB_WARN_ON(!sdev->dev || !sdev->dev->driver)) continue; sdrv = drv_to_ssb_drv(sdev->dev->driver); if (SSB_WARN_ON(!sdrv || !sdrv->probe)) continue; err = sdrv->probe(sdev, &sdev->id); if (err) { ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n", dev_name(sdev->dev)); result = err; } ssb_device_put(sdev); } return result; } #endif /* CONFIG_SSB_SPROM */ static void ssb_device_shutdown(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_driver *ssb_drv; if (!dev->driver) return; ssb_drv = drv_to_ssb_drv(dev->driver); if (ssb_drv && ssb_drv->shutdown) ssb_drv->shutdown(ssb_dev); } static int ssb_device_remove(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver); if (ssb_drv && ssb_drv->remove) ssb_drv->remove(ssb_dev); ssb_device_put(ssb_dev); return 0; } static int ssb_device_probe(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver); int err = 0; ssb_device_get(ssb_dev); if (ssb_drv && ssb_drv->probe) err = ssb_drv->probe(ssb_dev, &ssb_dev->id); if (err) ssb_device_put(ssb_dev); return err; } static int ssb_match_devid(const struct ssb_device_id *tabid, const struct ssb_device_id *devid) { if ((tabid->vendor != devid->vendor) && tabid->vendor != SSB_ANY_VENDOR) return 0; if ((tabid->coreid != devid->coreid) && tabid->coreid != SSB_ANY_ID) return 0; if ((tabid->revision != devid->revision) && tabid->revision != SSB_ANY_REV) return 0; return 1; } static int ssb_bus_match(struct device *dev, struct device_driver *drv) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_driver *ssb_drv = drv_to_ssb_drv(drv); const struct ssb_device_id *id; for (id = ssb_drv->id_table; id->vendor || id->coreid || id->revision; id++) { if (ssb_match_devid(id, &ssb_dev->id)) return 1; /* found */ } return 0; } static int ssb_device_uevent(struct device *dev, struct kobj_uevent_env *env) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); if (!dev) return -ENODEV; return add_uevent_var(env, "MODALIAS=ssb:v%04Xid%04Xrev%02X", ssb_dev->id.vendor, ssb_dev->id.coreid, ssb_dev->id.revision); } #define ssb_config_attr(attrib, field, format_string) \ static ssize_t \ attrib##_show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ return sprintf(buf, format_string, dev_to_ssb_dev(dev)->field); \ } ssb_config_attr(core_num, core_index, "%u\n") ssb_config_attr(coreid, id.coreid, "0x%04x\n") ssb_config_attr(vendor, id.vendor, "0x%04x\n") ssb_config_attr(revision, id.revision, "%u\n") ssb_config_attr(irq, irq, "%u\n") static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", ssb_core_name(dev_to_ssb_dev(dev)->id.coreid)); } static struct device_attribute ssb_device_attrs[] = { __ATTR_RO(name), __ATTR_RO(core_num), __ATTR_RO(coreid), __ATTR_RO(vendor), __ATTR_RO(revision), __ATTR_RO(irq), __ATTR_NULL, }; static struct bus_type ssb_bustype = { .name = "ssb", .match = ssb_bus_match, .probe = ssb_device_probe, .remove = ssb_device_remove, .shutdown = ssb_device_shutdown, .suspend = ssb_device_suspend, .resume = ssb_device_resume, .uevent = ssb_device_uevent, .dev_attrs = ssb_device_attrs, }; static void ssb_buses_lock(void) { /* See the comment at the ssb_is_early_boot definition */ if (!ssb_is_early_boot) mutex_lock(&buses_mutex); } static void ssb_buses_unlock(void) { /* See the comment at the ssb_is_early_boot definition */ if (!ssb_is_early_boot) mutex_unlock(&buses_mutex); } static void ssb_devices_unregister(struct ssb_bus *bus) { struct ssb_device *sdev; int i; for (i = bus->nr_devices - 1; i >= 0; i--) { sdev = &(bus->devices[i]); if (sdev->dev) device_unregister(sdev->dev); } } void ssb_bus_unregister(struct ssb_bus *bus) { ssb_buses_lock(); ssb_devices_unregister(bus); list_del(&bus->list); ssb_buses_unlock(); ssb_pcmcia_exit(bus); ssb_pci_exit(bus); ssb_iounmap(bus); } EXPORT_SYMBOL(ssb_bus_unregister); static void ssb_release_dev(struct device *dev) { struct __ssb_dev_wrapper *devwrap; devwrap = container_of(dev, struct __ssb_dev_wrapper, dev); kfree(devwrap); } static int ssb_devices_register(struct ssb_bus *bus) { struct ssb_device *sdev; struct device *dev; struct __ssb_dev_wrapper *devwrap; int i, err = 0; int dev_idx = 0; for (i = 0; i < bus->nr_devices; i++) { sdev = &(bus->devices[i]); /* We don't register SSB-system devices to the kernel, * as the drivers for them are built into SSB. */ switch (sdev->id.coreid) { case SSB_DEV_CHIPCOMMON: case SSB_DEV_PCI: case SSB_DEV_PCIE: case SSB_DEV_PCMCIA: case SSB_DEV_MIPS: case SSB_DEV_MIPS_3302: case SSB_DEV_EXTIF: continue; } devwrap = kzalloc(sizeof(*devwrap), GFP_KERNEL); if (!devwrap) { ssb_printk(KERN_ERR PFX "Could not allocate device\n"); err = -ENOMEM; goto error; } dev = &devwrap->dev; devwrap->sdev = sdev; dev->release = ssb_release_dev; dev->bus = &ssb_bustype; dev_set_name(dev, "ssb%u:%d", bus->busnumber, dev_idx); switch (bus->bustype) { case SSB_BUSTYPE_PCI: #ifdef CONFIG_SSB_PCIHOST sdev->irq = bus->host_pci->irq; dev->parent = &bus->host_pci->dev; sdev->dma_dev = dev->parent; #endif break; case SSB_BUSTYPE_PCMCIA: #ifdef CONFIG_SSB_PCMCIAHOST #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) sdev->irq = bus->host_pcmcia->irq; #else sdev->irq = bus->host_pcmcia->irq.AssignedIRQ; #endif dev->parent = &bus->host_pcmcia->dev; #endif break; case SSB_BUSTYPE_SDIO: #ifdef CONFIG_SSB_SDIOHOST dev->parent = &bus->host_sdio->dev; #endif break; case SSB_BUSTYPE_SSB: dev->dma_mask = &dev->coherent_dma_mask; sdev->dma_dev = dev; break; } sdev->dev = dev; err = device_register(dev); if (err) { ssb_printk(KERN_ERR PFX "Could not register %s\n", dev_name(dev)); /* Set dev to NULL to not unregister * dev on error unwinding. */ sdev->dev = NULL; kfree(devwrap); goto error; } dev_idx++; } return 0; error: /* Unwind the already registered devices. */ ssb_devices_unregister(bus); return err; } /* Needs ssb_buses_lock() */ static int __devinit ssb_attach_queued_buses(void) { struct ssb_bus *bus, *n; int err = 0; int drop_them_all = 0; list_for_each_entry_safe(bus, n, &attach_queue, list) { if (drop_them_all) { list_del(&bus->list); continue; } /* Can't init the PCIcore in ssb_bus_register(), as that * is too early in boot for embedded systems * (no udelay() available). So do it here in attach stage. */ err = ssb_bus_powerup(bus, 0); if (err) goto error; ssb_pcicore_init(&bus->pcicore); ssb_bus_may_powerdown(bus); err = ssb_devices_register(bus); error: if (err) { drop_them_all = 1; list_del(&bus->list); continue; } list_move_tail(&bus->list, &buses); } return err; } static u8 ssb_ssb_read8(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; offset += dev->core_index * SSB_CORE_SIZE; return readb(bus->mmio + offset); } static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; offset += dev->core_index * SSB_CORE_SIZE; return readw(bus->mmio + offset); } static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; offset += dev->core_index * SSB_CORE_SIZE; return readl(bus->mmio + offset); } #ifdef CONFIG_SSB_BLOCKIO static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { struct ssb_bus *bus = dev->bus; void __iomem *addr; offset += dev->core_index * SSB_CORE_SIZE; addr = bus->mmio + offset; switch (reg_width) { case sizeof(u8): { u8 *buf = buffer; while (count) { *buf = __raw_readb(addr); buf++; count--; } break; } case sizeof(u16): { __le16 *buf = buffer; SSB_WARN_ON(count & 1); while (count) { *buf = (__force __le16)__raw_readw(addr); buf++; count -= 2; } break; } case sizeof(u32): { __le32 *buf = buffer; SSB_WARN_ON(count & 3); while (count) { *buf = (__force __le32)__raw_readl(addr); buf++; count -= 4; } break; } default: SSB_WARN_ON(1); } } #endif /* CONFIG_SSB_BLOCKIO */ static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value) { struct ssb_bus *bus = dev->bus; offset += dev->core_index * SSB_CORE_SIZE; writeb(value, bus->mmio + offset); } static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value) { struct ssb_bus *bus = dev->bus; offset += dev->core_index * SSB_CORE_SIZE; writew(value, bus->mmio + offset); } static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value) { struct ssb_bus *bus = dev->bus; offset += dev->core_index * SSB_CORE_SIZE; writel(value, bus->mmio + offset); } #ifdef CONFIG_SSB_BLOCKIO static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { struct ssb_bus *bus = dev->bus; void __iomem *addr; offset += dev->core_index * SSB_CORE_SIZE; addr = bus->mmio + offset; switch (reg_width) { case sizeof(u8): { const u8 *buf = buffer; while (count) { __raw_writeb(*buf, addr); buf++; count--; } break; } case sizeof(u16): { const __le16 *buf = buffer; SSB_WARN_ON(count & 1); while (count) { __raw_writew((__force u16)(*buf), addr); buf++; count -= 2; } break; } case sizeof(u32): { const __le32 *buf = buffer; SSB_WARN_ON(count & 3); while (count) { __raw_writel((__force u32)(*buf), addr); buf++; count -= 4; } break; } default: SSB_WARN_ON(1); } } #endif /* CONFIG_SSB_BLOCKIO */ /* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */ static const struct ssb_bus_ops ssb_ssb_ops = { .read8 = ssb_ssb_read8, .read16 = ssb_ssb_read16, .read32 = ssb_ssb_read32, .write8 = ssb_ssb_write8, .write16 = ssb_ssb_write16, .write32 = ssb_ssb_write32, #ifdef CONFIG_SSB_BLOCKIO .block_read = ssb_ssb_block_read, .block_write = ssb_ssb_block_write, #endif }; static int ssb_fetch_invariants(struct ssb_bus *bus, ssb_invariants_func_t get_invariants) { struct ssb_init_invariants iv; int err; memset(&iv, 0, sizeof(iv)); err = get_invariants(bus, &iv); if (err) goto out; memcpy(&bus->boardinfo, &iv.boardinfo, sizeof(iv.boardinfo)); memcpy(&bus->sprom, &iv.sprom, sizeof(iv.sprom)); bus->has_cardbus_slot = iv.has_cardbus_slot; out: return err; } static int __devinit ssb_bus_register(struct ssb_bus *bus, ssb_invariants_func_t get_invariants, unsigned long baseaddr) { int err; spin_lock_init(&bus->bar_lock); INIT_LIST_HEAD(&bus->list); #ifdef CONFIG_SSB_EMBEDDED spin_lock_init(&bus->gpio_lock); #endif /* Powerup the bus */ err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); if (err) goto out; /* Init SDIO-host device (if any), before the scan */ err = ssb_sdio_init(bus); if (err) goto err_disable_xtal; ssb_buses_lock(); bus->busnumber = next_busnumber; /* Scan for devices (cores) */ err = ssb_bus_scan(bus, baseaddr); if (err) goto err_sdio_exit; /* Init PCI-host device (if any) */ err = ssb_pci_init(bus); if (err) goto err_unmap; /* Init PCMCIA-host device (if any) */ err = ssb_pcmcia_init(bus); if (err) goto err_pci_exit; /* Initialize basic system devices (if available) */ err = ssb_bus_powerup(bus, 0); if (err) goto err_pcmcia_exit; ssb_chipcommon_init(&bus->chipco); ssb_mipscore_init(&bus->mipscore); err = ssb_fetch_invariants(bus, get_invariants); if (err) { ssb_bus_may_powerdown(bus); goto err_pcmcia_exit; } ssb_bus_may_powerdown(bus); /* Queue it for attach. * See the comment at the ssb_is_early_boot definition. */ list_add_tail(&bus->list, &attach_queue); if (!ssb_is_early_boot) { /* This is not early boot, so we must attach the bus now */ err = ssb_attach_queued_buses(); if (err) goto err_dequeue; } next_busnumber++; ssb_buses_unlock(); out: return err; err_dequeue: list_del(&bus->list); err_pcmcia_exit: ssb_pcmcia_exit(bus); err_pci_exit: ssb_pci_exit(bus); err_unmap: ssb_iounmap(bus); err_sdio_exit: ssb_sdio_exit(bus); err_disable_xtal: ssb_buses_unlock(); ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); return err; } #ifdef CONFIG_SSB_PCIHOST int __devinit ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci) { int err; bus->bustype = SSB_BUSTYPE_PCI; bus->host_pci = host_pci; bus->ops = &ssb_pci_ops; err = ssb_bus_register(bus, ssb_pci_get_invariants, 0); if (!err) { ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on " "PCI device %s\n", dev_name(&host_pci->dev)); } else { ssb_printk(KERN_ERR PFX "Failed to register PCI version" " of SSB with error %d\n", err); } return err; } EXPORT_SYMBOL(ssb_bus_pcibus_register); #endif /* CONFIG_SSB_PCIHOST */ #ifdef CONFIG_SSB_PCMCIAHOST int __devinit ssb_bus_pcmciabus_register(struct ssb_bus *bus, struct pcmcia_device *pcmcia_dev, unsigned long baseaddr) { int err; bus->bustype = SSB_BUSTYPE_PCMCIA; bus->host_pcmcia = pcmcia_dev; bus->ops = &ssb_pcmcia_ops; err = ssb_bus_register(bus, ssb_pcmcia_get_invariants, baseaddr); if (!err) { ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on " "PCMCIA device %s\n", pcmcia_dev->devname); } return err; } EXPORT_SYMBOL(ssb_bus_pcmciabus_register); #endif /* CONFIG_SSB_PCMCIAHOST */ #ifdef CONFIG_SSB_SDIOHOST int __devinit ssb_bus_sdiobus_register(struct ssb_bus *bus, struct sdio_func *func, unsigned int quirks) { int err; bus->bustype = SSB_BUSTYPE_SDIO; bus->host_sdio = func; bus->ops = &ssb_sdio_ops; bus->quirks = quirks; err = ssb_bus_register(bus, ssb_sdio_get_invariants, ~0); if (!err) { ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on " "SDIO device %s\n", sdio_func_id(func)); } return err; } EXPORT_SYMBOL(ssb_bus_sdiobus_register); #endif /* CONFIG_SSB_PCMCIAHOST */ int __devinit ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr, ssb_invariants_func_t get_invariants) { int err; bus->bustype = SSB_BUSTYPE_SSB; bus->ops = &ssb_ssb_ops; err = ssb_bus_register(bus, get_invariants, baseaddr); if (!err) { ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found at " "address 0x%08lX\n", baseaddr); } return err; } int __ssb_driver_register(struct ssb_driver *drv, struct module *owner) { drv->drv.name = drv->name; drv->drv.bus = &ssb_bustype; drv->drv.owner = owner; return driver_register(&drv->drv); } EXPORT_SYMBOL(__ssb_driver_register); void ssb_driver_unregister(struct ssb_driver *drv) { driver_unregister(&drv->drv); } EXPORT_SYMBOL(ssb_driver_unregister); void ssb_set_devtypedata(struct ssb_device *dev, void *data) { struct ssb_bus *bus = dev->bus; struct ssb_device *ent; int i; for (i = 0; i < bus->nr_devices; i++) { ent = &(bus->devices[i]); if (ent->id.vendor != dev->id.vendor) continue; if (ent->id.coreid != dev->id.coreid) continue; ent->devtypedata = data; } } EXPORT_SYMBOL(ssb_set_devtypedata); static u32 clkfactor_f6_resolve(u32 v) { /* map the magic values */ switch (v) { case SSB_CHIPCO_CLK_F6_2: return 2; case SSB_CHIPCO_CLK_F6_3: return 3; case SSB_CHIPCO_CLK_F6_4: return 4; case SSB_CHIPCO_CLK_F6_5: return 5; case SSB_CHIPCO_CLK_F6_6: return 6; case SSB_CHIPCO_CLK_F6_7: return 7; } return 0; } /* Calculate the speed the backplane would run at a given set of clockcontrol values */ u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m) { u32 n1, n2, clock, m1, m2, m3, mc; n1 = (n & SSB_CHIPCO_CLK_N1); n2 = ((n & SSB_CHIPCO_CLK_N2) >> SSB_CHIPCO_CLK_N2_SHIFT); switch (plltype) { case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */ if (m & SSB_CHIPCO_CLK_T6_MMASK) return SSB_CHIPCO_CLK_T6_M1; return SSB_CHIPCO_CLK_T6_M0; case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */ case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */ case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ n1 = clkfactor_f6_resolve(n1); n2 += SSB_CHIPCO_CLK_F5_BIAS; break; case SSB_PLLTYPE_2: /* 48Mhz, 4 dividers */ n1 += SSB_CHIPCO_CLK_T2_BIAS; n2 += SSB_CHIPCO_CLK_T2_BIAS; SSB_WARN_ON(!((n1 >= 2) && (n1 <= 7))); SSB_WARN_ON(!((n2 >= 5) && (n2 <= 23))); break; case SSB_PLLTYPE_5: /* 25Mhz, 4 dividers */ return 100000000; default: SSB_WARN_ON(1); } switch (plltype) { case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ clock = SSB_CHIPCO_CLK_BASE2 * n1 * n2; break; default: clock = SSB_CHIPCO_CLK_BASE1 * n1 * n2; } if (!clock) return 0; m1 = (m & SSB_CHIPCO_CLK_M1); m2 = ((m & SSB_CHIPCO_CLK_M2) >> SSB_CHIPCO_CLK_M2_SHIFT); m3 = ((m & SSB_CHIPCO_CLK_M3) >> SSB_CHIPCO_CLK_M3_SHIFT); mc = ((m & SSB_CHIPCO_CLK_MC) >> SSB_CHIPCO_CLK_MC_SHIFT); switch (plltype) { case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */ case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */ case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ m1 = clkfactor_f6_resolve(m1); if ((plltype == SSB_PLLTYPE_1) || (plltype == SSB_PLLTYPE_3)) m2 += SSB_CHIPCO_CLK_F5_BIAS; else m2 = clkfactor_f6_resolve(m2); m3 = clkfactor_f6_resolve(m3); switch (mc) { case SSB_CHIPCO_CLK_MC_BYPASS: return clock; case SSB_CHIPCO_CLK_MC_M1: return (clock / m1); case SSB_CHIPCO_CLK_MC_M1M2: return (clock / (m1 * m2)); case SSB_CHIPCO_CLK_MC_M1M2M3: return (clock / (m1 * m2 * m3)); case SSB_CHIPCO_CLK_MC_M1M3: return (clock / (m1 * m3)); } return 0; case SSB_PLLTYPE_2: m1 += SSB_CHIPCO_CLK_T2_BIAS; m2 += SSB_CHIPCO_CLK_T2M2_BIAS; m3 += SSB_CHIPCO_CLK_T2_BIAS; SSB_WARN_ON(!((m1 >= 2) && (m1 <= 7))); SSB_WARN_ON(!((m2 >= 3) && (m2 <= 10))); SSB_WARN_ON(!((m3 >= 2) && (m3 <= 7))); if (!(mc & SSB_CHIPCO_CLK_T2MC_M1BYP)) clock /= m1; if (!(mc & SSB_CHIPCO_CLK_T2MC_M2BYP)) clock /= m2; if (!(mc & SSB_CHIPCO_CLK_T2MC_M3BYP)) clock /= m3; return clock; default: SSB_WARN_ON(1); } return 0; } /* Get the current speed the backplane is running at */ u32 ssb_clockspeed(struct ssb_bus *bus) { u32 rate; u32 plltype; u32 clkctl_n, clkctl_m; if (bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU) return ssb_pmu_get_controlclock(&bus->chipco); if (ssb_extif_available(&bus->extif)) ssb_extif_get_clockcontrol(&bus->extif, &plltype, &clkctl_n, &clkctl_m); else if (bus->chipco.dev) ssb_chipco_get_clockcontrol(&bus->chipco, &plltype, &clkctl_n, &clkctl_m); else return 0; if (bus->chip_id == 0x5365) { rate = 100000000; } else { rate = ssb_calc_clock_rate(plltype, clkctl_n, clkctl_m); if (plltype == SSB_PLLTYPE_3) /* 25Mhz, 2 dividers */ rate /= 2; } return rate; } EXPORT_SYMBOL(ssb_clockspeed); static u32 ssb_tmslow_reject_bitmask(struct ssb_device *dev) { u32 rev = ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV; /* The REJECT bit seems to be different for Backplane rev 2.3 */ switch (rev) { case SSB_IDLOW_SSBREV_22: case SSB_IDLOW_SSBREV_24: case SSB_IDLOW_SSBREV_26: return SSB_TMSLOW_REJECT; case SSB_IDLOW_SSBREV_23: return SSB_TMSLOW_REJECT_23; case SSB_IDLOW_SSBREV_25: /* TODO - find the proper REJECT bit */ case SSB_IDLOW_SSBREV_27: /* same here */ return SSB_TMSLOW_REJECT; /* this is a guess */ default: printk(KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev); WARN_ON(1); } return (SSB_TMSLOW_REJECT | SSB_TMSLOW_REJECT_23); } int ssb_device_is_enabled(struct ssb_device *dev) { u32 val; u32 reject; reject = ssb_tmslow_reject_bitmask(dev); val = ssb_read32(dev, SSB_TMSLOW); val &= SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET | reject; return (val == SSB_TMSLOW_CLOCK); } EXPORT_SYMBOL(ssb_device_is_enabled); static void ssb_flush_tmslow(struct ssb_device *dev) { /* Make _really_ sure the device has finished the TMSLOW * register write transaction, as we risk running into * a machine check exception otherwise. * Do this by reading the register back to commit the * PCI write and delay an additional usec for the device * to react to the change. */ ssb_read32(dev, SSB_TMSLOW); udelay(1); } void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags) { u32 val; ssb_device_disable(dev, core_specific_flags); ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_RESET | SSB_TMSLOW_CLOCK | SSB_TMSLOW_FGC | core_specific_flags); ssb_flush_tmslow(dev); /* Clear SERR if set. This is a hw bug workaround. */ if (ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_SERR) ssb_write32(dev, SSB_TMSHIGH, 0); val = ssb_read32(dev, SSB_IMSTATE); if (val & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { val &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); ssb_write32(dev, SSB_IMSTATE, val); } ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_CLOCK | SSB_TMSLOW_FGC | core_specific_flags); ssb_flush_tmslow(dev); ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_CLOCK | core_specific_flags); ssb_flush_tmslow(dev); } EXPORT_SYMBOL(ssb_device_enable); /* Wait for bitmask in a register to get set or cleared. * timeout is in units of ten-microseconds */ static int ssb_wait_bits(struct ssb_device *dev, u16 reg, u32 bitmask, int timeout, int set) { int i; u32 val; for (i = 0; i < timeout; i++) { val = ssb_read32(dev, reg); if (set) { if ((val & bitmask) == bitmask) return 0; } else { if (!(val & bitmask)) return 0; } udelay(10); } printk(KERN_ERR PFX "Timeout waiting for bitmask %08X on " "register %04X to %s.\n", bitmask, reg, (set ? "set" : "clear")); return -ETIMEDOUT; } void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags) { u32 reject, val; if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_RESET) return; reject = ssb_tmslow_reject_bitmask(dev); if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_CLOCK) { ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_CLOCK); ssb_wait_bits(dev, SSB_TMSLOW, reject, 1000, 1); ssb_wait_bits(dev, SSB_TMSHIGH, SSB_TMSHIGH_BUSY, 1000, 0); if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) { val = ssb_read32(dev, SSB_IMSTATE); val |= SSB_IMSTATE_REJECT; ssb_write32(dev, SSB_IMSTATE, val); ssb_wait_bits(dev, SSB_IMSTATE, SSB_IMSTATE_BUSY, 1000, 0); } ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | reject | SSB_TMSLOW_RESET | core_specific_flags); ssb_flush_tmslow(dev); if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) { val = ssb_read32(dev, SSB_IMSTATE); val &= ~SSB_IMSTATE_REJECT; ssb_write32(dev, SSB_IMSTATE, val); } } ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_RESET | core_specific_flags); ssb_flush_tmslow(dev); } EXPORT_SYMBOL(ssb_device_disable); /* Some chipsets need routing known for PCIe and 64-bit DMA */ static bool ssb_dma_translation_special_bit(struct ssb_device *dev) { u16 chip_id = dev->bus->chip_id; if (dev->id.coreid == SSB_DEV_80211) { return (chip_id == 0x4322 || chip_id == 43221 || chip_id == 43231 || chip_id == 43222); } return 0; } u32 ssb_dma_translation(struct ssb_device *dev) { switch (dev->bus->bustype) { case SSB_BUSTYPE_SSB: return 0; case SSB_BUSTYPE_PCI: if (pci_is_pcie(dev->bus->host_pci) && ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64) { return SSB_PCIE_DMA_H32; } else { if (ssb_dma_translation_special_bit(dev)) return SSB_PCIE_DMA_H32; else return SSB_PCI_DMA; } default: __ssb_dma_not_implemented(dev); } return 0; } EXPORT_SYMBOL(ssb_dma_translation); int ssb_bus_may_powerdown(struct ssb_bus *bus) { struct ssb_chipcommon *cc; int err = 0; /* On buses where more than one core may be working * at a time, we must not powerdown stuff if there are * still cores that may want to run. */ if (bus->bustype == SSB_BUSTYPE_SSB) goto out; cc = &bus->chipco; if (!cc->dev) goto out; if (cc->dev->id.revision < 5) goto out; ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW); err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); if (err) goto error; out: #ifdef CONFIG_SSB_DEBUG bus->powered_up = 0; #endif return err; error: ssb_printk(KERN_ERR PFX "Bus powerdown failed\n"); goto out; } EXPORT_SYMBOL(ssb_bus_may_powerdown); int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl) { int err; enum ssb_clkmode mode; err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); if (err) goto error; #ifdef CONFIG_SSB_DEBUG bus->powered_up = 1; #endif mode = dynamic_pctl ? SSB_CLKMODE_DYNAMIC : SSB_CLKMODE_FAST; ssb_chipco_set_clockmode(&bus->chipco, mode); return 0; error: ssb_printk(KERN_ERR PFX "Bus powerup failed\n"); return err; } EXPORT_SYMBOL(ssb_bus_powerup); static void ssb_broadcast_value(struct ssb_device *dev, u32 address, u32 data) { #ifdef CONFIG_SSB_DRIVER_PCICORE /* This is used for both, PCI and ChipCommon core, so be careful. */ BUILD_BUG_ON(SSB_PCICORE_BCAST_ADDR != SSB_CHIPCO_BCAST_ADDR); BUILD_BUG_ON(SSB_PCICORE_BCAST_DATA != SSB_CHIPCO_BCAST_DATA); #endif ssb_write32(dev, SSB_CHIPCO_BCAST_ADDR, address); ssb_read32(dev, SSB_CHIPCO_BCAST_ADDR); /* flush */ ssb_write32(dev, SSB_CHIPCO_BCAST_DATA, data); ssb_read32(dev, SSB_CHIPCO_BCAST_DATA); /* flush */ } void ssb_commit_settings(struct ssb_bus *bus) { struct ssb_device *dev; #ifdef CONFIG_SSB_DRIVER_PCICORE dev = bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev; #else dev = bus->chipco.dev; #endif if (WARN_ON(!dev)) return; /* This forces an update of the cached registers. */ ssb_broadcast_value(dev, 0xFD8, 0); } EXPORT_SYMBOL(ssb_commit_settings); u32 ssb_admatch_base(u32 adm) { u32 base = 0; switch (adm & SSB_ADM_TYPE) { case SSB_ADM_TYPE0: base = (adm & SSB_ADM_BASE0); break; case SSB_ADM_TYPE1: SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ base = (adm & SSB_ADM_BASE1); break; case SSB_ADM_TYPE2: SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ base = (adm & SSB_ADM_BASE2); break; default: SSB_WARN_ON(1); } return base; } EXPORT_SYMBOL(ssb_admatch_base); u32 ssb_admatch_size(u32 adm) { u32 size = 0; switch (adm & SSB_ADM_TYPE) { case SSB_ADM_TYPE0: size = ((adm & SSB_ADM_SZ0) >> SSB_ADM_SZ0_SHIFT); break; case SSB_ADM_TYPE1: SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ size = ((adm & SSB_ADM_SZ1) >> SSB_ADM_SZ1_SHIFT); break; case SSB_ADM_TYPE2: SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ size = ((adm & SSB_ADM_SZ2) >> SSB_ADM_SZ2_SHIFT); break; default: SSB_WARN_ON(1); } size = (1 << (size + 1)); return size; } EXPORT_SYMBOL(ssb_admatch_size); static int __init ssb_modinit(void) { int err; /* See the comment at the ssb_is_early_boot definition */ ssb_is_early_boot = 0; err = bus_register(&ssb_bustype); if (err) return err; /* Maybe we already registered some buses at early boot. * Check for this and attach them */ ssb_buses_lock(); err = ssb_attach_queued_buses(); ssb_buses_unlock(); if (err) { bus_unregister(&ssb_bustype); goto out; } err = b43_pci_ssb_bridge_init(); if (err) { ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge " "initialization failed\n"); /* don't fail SSB init because of this */ err = 0; } err = ssb_gige_init(); if (err) { ssb_printk(KERN_ERR "SSB Broadcom Gigabit Ethernet " "driver initialization failed\n"); /* don't fail SSB init because of this */ err = 0; } out: return err; } /* ssb must be initialized after PCI but before the ssb drivers. * That means we must use some initcall between subsys_initcall * and device_initcall. */ fs_initcall(ssb_modinit); static void __exit ssb_modexit(void) { ssb_gige_exit(); b43_pci_ssb_bridge_exit(); bus_unregister(&ssb_bustype); } module_exit(ssb_modexit) compat-drivers-2012-09-18/drivers/ssb/ssb_private.h0000644000175000017500000001343312026211315021323 0ustar mcgrofmcgrof#ifndef LINUX_SSB_PRIVATE_H_ #define LINUX_SSB_PRIVATE_H_ #include #include #define PFX "ssb: " #ifdef CONFIG_SSB_SILENT # define ssb_printk(fmt, x...) do { /* nothing */ } while (0) #else # define ssb_printk printk #endif /* CONFIG_SSB_SILENT */ /* dprintk: Debugging printk; vanishes for non-debug compilation */ #ifdef CONFIG_SSB_DEBUG # define ssb_dprintk(fmt, x...) ssb_printk(fmt , ##x) #else # define ssb_dprintk(fmt, x...) do { /* nothing */ } while (0) #endif #ifdef CONFIG_SSB_DEBUG # define SSB_WARN_ON(x) WARN_ON(x) # define SSB_BUG_ON(x) BUG_ON(x) #else static inline int __ssb_do_nothing(int x) { return x; } # define SSB_WARN_ON(x) __ssb_do_nothing(unlikely(!!(x))) # define SSB_BUG_ON(x) __ssb_do_nothing(unlikely(!!(x))) #endif /* pci.c */ #ifdef CONFIG_SSB_PCIHOST extern int ssb_pci_switch_core(struct ssb_bus *bus, struct ssb_device *dev); extern int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx); extern int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on); extern int ssb_pci_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv); extern void ssb_pci_exit(struct ssb_bus *bus); extern int ssb_pci_init(struct ssb_bus *bus); extern const struct ssb_bus_ops ssb_pci_ops; #else /* CONFIG_SSB_PCIHOST */ static inline int ssb_pci_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { return 0; } static inline int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { return 0; } static inline int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on) { return 0; } static inline void ssb_pci_exit(struct ssb_bus *bus) { } static inline int ssb_pci_init(struct ssb_bus *bus) { return 0; } #endif /* CONFIG_SSB_PCIHOST */ /* pcmcia.c */ #ifdef CONFIG_SSB_PCMCIAHOST extern int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev); extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, u8 coreidx); extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg); extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv); extern int ssb_pcmcia_hardware_setup(struct ssb_bus *bus); extern void ssb_pcmcia_exit(struct ssb_bus *bus); extern int ssb_pcmcia_init(struct ssb_bus *bus); extern const struct ssb_bus_ops ssb_pcmcia_ops; #else /* CONFIG_SSB_PCMCIAHOST */ static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { return 0; } static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { return 0; } static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) { return 0; } static inline int ssb_pcmcia_hardware_setup(struct ssb_bus *bus) { return 0; } static inline void ssb_pcmcia_exit(struct ssb_bus *bus) { } static inline int ssb_pcmcia_init(struct ssb_bus *bus) { return 0; } #endif /* CONFIG_SSB_PCMCIAHOST */ /* sdio.c */ #ifdef CONFIG_SSB_SDIOHOST extern int ssb_sdio_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv); extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset); extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev); extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx); extern int ssb_sdio_hardware_setup(struct ssb_bus *bus); extern void ssb_sdio_exit(struct ssb_bus *bus); extern int ssb_sdio_init(struct ssb_bus *bus); extern const struct ssb_bus_ops ssb_sdio_ops; #else /* CONFIG_SSB_SDIOHOST */ static inline u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset) { return 0; } static inline int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { return 0; } static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { return 0; } static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus) { return 0; } static inline void ssb_sdio_exit(struct ssb_bus *bus) { } static inline int ssb_sdio_init(struct ssb_bus *bus) { return 0; } #endif /* CONFIG_SSB_SDIOHOST */ /* scan.c */ extern const char *ssb_core_name(u16 coreid); extern int ssb_bus_scan(struct ssb_bus *bus, unsigned long baseaddr); extern void ssb_iounmap(struct ssb_bus *ssb); /* sprom.c */ extern ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, int (*sprom_read)(struct ssb_bus *bus, u16 *sprom)); extern ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, const char *buf, size_t count, int (*sprom_check_crc)(const u16 *sprom, size_t size), int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)); extern int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out); /* core.c */ extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); int ssb_for_each_bus_call(unsigned long data, int (*func)(struct ssb_bus *bus, unsigned long data)); extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); struct ssb_freeze_context { /* Pointer to the bus */ struct ssb_bus *bus; /* Boolean list to indicate whether a device is frozen on this bus. */ bool device_frozen[SSB_MAX_NR_CORES]; }; extern int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx); extern int ssb_devices_thaw(struct ssb_freeze_context *ctx); /* b43_pci_bridge.c */ #ifdef CONFIG_SSB_B43_PCI_BRIDGE extern int __init b43_pci_ssb_bridge_init(void); extern void __exit b43_pci_ssb_bridge_exit(void); #else /* CONFIG_SSB_B43_PCI_BRIDGE */ static inline int b43_pci_ssb_bridge_init(void) { return 0; } static inline void b43_pci_ssb_bridge_exit(void) { } #endif /* CONFIG_SSB_B43_PCI_BRIDGE */ /* driver_chipcommon_pmu.c */ extern u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc); extern u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc); #endif /* LINUX_SSB_PRIVATE_H_ */ compat-drivers-2012-09-18/drivers/ssb/sprom.c0000644000175000017500000001235012026211315020132 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Common SPROM support routines * * Copyright (C) 2005-2008 Michael Buesch * Copyright (C) 2005 Martin Langer * Copyright (C) 2005 Stefano Brivio * Copyright (C) 2005 Danny van Dyk * Copyright (C) 2005 Andreas Jaggi * * Licensed under the GNU/GPL. See COPYING for details. */ #include "ssb_private.h" #include #include static int(*get_fallback_sprom)(struct ssb_bus *dev, struct ssb_sprom *out); static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, size_t sprom_size_words) { int i, pos = 0; for (i = 0; i < sprom_size_words; i++) pos += snprintf(buf + pos, buf_len - pos - 1, "%04X", swab16(sprom[i]) & 0xFFFF); pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); return pos + 1; } static int hex2sprom(u16 *sprom, const char *dump, size_t len, size_t sprom_size_words) { char c, tmp[5] = { 0 }; int err, cnt = 0; unsigned long parsed; /* Strip whitespace at the end. */ while (len) { c = dump[len - 1]; if (!isspace(c) && c != '\0') break; len--; } /* Length must match exactly. */ if (len != sprom_size_words * 4) return -EINVAL; while (cnt < sprom_size_words) { memcpy(tmp, dump, 4); dump += 4; err = strict_strtoul(tmp, 16, &parsed); if (err) return err; sprom[cnt++] = swab16((u16)parsed); } return 0; } /* Common sprom device-attribute show-handler */ ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, int (*sprom_read)(struct ssb_bus *bus, u16 *sprom)) { u16 *sprom; int err = -ENOMEM; ssize_t count = 0; size_t sprom_size_words = bus->sprom_size; sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL); if (!sprom) goto out; /* Use interruptible locking, as the SPROM write might * be holding the lock for several seconds. So allow userspace * to cancel operation. */ err = -ERESTARTSYS; if (mutex_lock_interruptible(&bus->sprom_mutex)) goto out_kfree; err = sprom_read(bus, sprom); mutex_unlock(&bus->sprom_mutex); if (!err) count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words); out_kfree: kfree(sprom); out: return err ? err : count; } /* Common sprom device-attribute store-handler */ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, const char *buf, size_t count, int (*sprom_check_crc)(const u16 *sprom, size_t size), int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)) { u16 *sprom; int res = 0, err = -ENOMEM; size_t sprom_size_words = bus->sprom_size; struct ssb_freeze_context freeze; sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); if (!sprom) goto out; err = hex2sprom(sprom, buf, count, sprom_size_words); if (err) { err = -EINVAL; goto out_kfree; } err = sprom_check_crc(sprom, sprom_size_words); if (err) { err = -EINVAL; goto out_kfree; } /* Use interruptible locking, as the SPROM write might * be holding the lock for several seconds. So allow userspace * to cancel operation. */ err = -ERESTARTSYS; if (mutex_lock_interruptible(&bus->sprom_mutex)) goto out_kfree; err = ssb_devices_freeze(bus, &freeze); if (err) { ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); goto out_unlock; } res = sprom_write(bus, sprom); err = ssb_devices_thaw(&freeze); if (err) ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); out_unlock: mutex_unlock(&bus->sprom_mutex); out_kfree: kfree(sprom); out: if (res) return res; return err ? err : count; } /** * ssb_arch_register_fallback_sprom - Registers a method providing a * fallback SPROM if no SPROM is found. * * @sprom_callback: The callback function. * * With this function the architecture implementation may register a * callback handler which fills the SPROM data structure. The fallback is * only used for PCI based SSB devices, where no valid SPROM can be found * in the shadow registers. * * This function is useful for weird architectures that have a half-assed * SSB device hardwired to their PCI bus. * * Note that it does only work with PCI attached SSB devices. PCMCIA * devices currently don't use this fallback. * Architectures must provide the SPROM for native SSB devices anyway, so * the fallback also isn't used for native devices. * * This function is available for architecture code, only. So it is not * exported. */ int ssb_arch_register_fallback_sprom(int (*sprom_callback)(struct ssb_bus *bus, struct ssb_sprom *out)) { if (get_fallback_sprom) return -EEXIST; get_fallback_sprom = sprom_callback; return 0; } int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out) { if (!get_fallback_sprom) return -ENOENT; return get_fallback_sprom(bus, out); } /* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */ bool ssb_is_sprom_available(struct ssb_bus *bus) { /* status register only exists on chipcomon rev >= 11 and we need check for >= 31 only */ /* this routine differs from specs as we do not access SPROM directly on PCMCIA */ if (bus->bustype == SSB_BUSTYPE_PCI && bus->chipco.dev && /* can be unavailable! */ bus->chipco.dev->id.revision >= 31) return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM; return true; } compat-drivers-2012-09-18/drivers/ssb/sdio.c0000644000175000017500000003714312026211315017737 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * SDIO-Hostbus related functions * * Copyright 2009 Albert Herranz * * Based on drivers/ssb/pcmcia.c * Copyright 2006 Johannes Berg * Copyright 2007-2008 Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. * */ #include #include #include #include #include #include "ssb_private.h" /* Define the following to 1 to enable a printk on each coreswitch. */ #define SSB_VERBOSE_SDIOCORESWITCH_DEBUG 0 /* Hardware invariants CIS tuples */ #define SSB_SDIO_CIS 0x80 #define SSB_SDIO_CIS_SROMREV 0x00 #define SSB_SDIO_CIS_ID 0x01 #define SSB_SDIO_CIS_BOARDREV 0x02 #define SSB_SDIO_CIS_PA 0x03 #define SSB_SDIO_CIS_PA_PA0B0_LO 0 #define SSB_SDIO_CIS_PA_PA0B0_HI 1 #define SSB_SDIO_CIS_PA_PA0B1_LO 2 #define SSB_SDIO_CIS_PA_PA0B1_HI 3 #define SSB_SDIO_CIS_PA_PA0B2_LO 4 #define SSB_SDIO_CIS_PA_PA0B2_HI 5 #define SSB_SDIO_CIS_PA_ITSSI 6 #define SSB_SDIO_CIS_PA_MAXPOW 7 #define SSB_SDIO_CIS_OEMNAME 0x04 #define SSB_SDIO_CIS_CCODE 0x05 #define SSB_SDIO_CIS_ANTENNA 0x06 #define SSB_SDIO_CIS_ANTGAIN 0x07 #define SSB_SDIO_CIS_BFLAGS 0x08 #define SSB_SDIO_CIS_LEDS 0x09 #define CISTPL_FUNCE_LAN_NODE_ID 0x04 /* same as in PCMCIA */ /* * Function 1 miscellaneous registers. * * Definitions match src/include/sbsdio.h from the * Android Open Source Project * http://android.git.kernel.org/?p=platform/system/wlan/broadcom.git * */ #define SBSDIO_FUNC1_SBADDRLOW 0x1000a /* SB Address window Low (b15) */ #define SBSDIO_FUNC1_SBADDRMID 0x1000b /* SB Address window Mid (b23-b16) */ #define SBSDIO_FUNC1_SBADDRHIGH 0x1000c /* SB Address window High (b24-b31) */ /* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ #define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid address bits in SBADDRLOW */ #define SBSDIO_SBADDRMID_MASK 0xff /* Valid address bits in SBADDRMID */ #define SBSDIO_SBADDRHIGH_MASK 0xff /* Valid address bits in SBADDRHIGH */ #define SBSDIO_SB_OFT_ADDR_MASK 0x7FFF /* sb offset addr is <= 15 bits, 32k */ /* REVISIT: this flag doesn't seem to matter */ #define SBSDIO_SB_ACCESS_2_4B_FLAG 0x8000 /* forces 32-bit SB access */ /* * Address map within the SDIO function address space (128K). * * Start End Description * ------- ------- ------------------------------------------ * 0x00000 0x0ffff selected backplane address window (64K) * 0x10000 0x1ffff backplane control registers (max 64K) * * The current address window is configured by writing to registers * SBADDRLOW, SBADDRMID and SBADDRHIGH. * * In order to access the contents of a 32-bit Silicon Backplane address * the backplane address window must be first loaded with the highest * 16 bits of the target address. Then, an access must be done to the * SDIO function address space using the lower 15 bits of the address. * Bit 15 of the address must be set when doing 32 bit accesses. * * 10987654321098765432109876543210 * WWWWWWWWWWWWWWWWW SB Address Window * OOOOOOOOOOOOOOOO Offset within SB Address Window * a 32-bit access flag */ /* * SSB I/O via SDIO. * * NOTE: SDIO address @addr is 17 bits long (SDIO address space is 128K). */ static inline struct device *ssb_sdio_dev(struct ssb_bus *bus) { return &bus->host_sdio->dev; } /* host claimed */ static int ssb_sdio_writeb(struct ssb_bus *bus, unsigned int addr, u8 val) { int error = 0; sdio_writeb(bus->host_sdio, val, addr, &error); if (unlikely(error)) { dev_dbg(ssb_sdio_dev(bus), "%08X <- %02x, error %d\n", addr, val, error); } return error; } #if 0 static u8 ssb_sdio_readb(struct ssb_bus *bus, unsigned int addr) { u8 val; int error = 0; val = sdio_readb(bus->host_sdio, addr, &error); if (unlikely(error)) { dev_dbg(ssb_sdio_dev(bus), "%08X -> %02x, error %d\n", addr, val, error); } return val; } #endif /* host claimed */ static int ssb_sdio_set_sbaddr_window(struct ssb_bus *bus, u32 address) { int error; error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRLOW, (address >> 8) & SBSDIO_SBADDRLOW_MASK); if (error) goto out; error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRMID, (address >> 16) & SBSDIO_SBADDRMID_MASK); if (error) goto out; error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRHIGH, (address >> 24) & SBSDIO_SBADDRHIGH_MASK); if (error) goto out; bus->sdio_sbaddr = address; out: if (error) { dev_dbg(ssb_sdio_dev(bus), "failed to set address window" " to 0x%08x, error %d\n", address, error); } return error; } /* for enumeration use only */ u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset) { u32 val; int error; sdio_claim_host(bus->host_sdio); val = sdio_readl(bus->host_sdio, offset, &error); sdio_release_host(bus->host_sdio); if (unlikely(error)) { dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n", bus->sdio_sbaddr >> 16, offset, val, error); } return val; } /* for enumeration use only */ int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { u32 sbaddr; int error; sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; sdio_claim_host(bus->host_sdio); error = ssb_sdio_set_sbaddr_window(bus, sbaddr); sdio_release_host(bus->host_sdio); if (error) { dev_err(ssb_sdio_dev(bus), "failed to switch to core %u," " error %d\n", coreidx, error); goto out; } out: return error; } /* host must be already claimed */ int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { u8 coreidx = dev->core_index; u32 sbaddr; int error = 0; sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; if (unlikely(bus->sdio_sbaddr != sbaddr)) { #if SSB_VERBOSE_SDIOCORESWITCH_DEBUG dev_info(ssb_sdio_dev(bus), "switching to %s core, index %d\n", ssb_core_name(dev->id.coreid), coreidx); #endif error = ssb_sdio_set_sbaddr_window(bus, sbaddr); if (error) { dev_dbg(ssb_sdio_dev(bus), "failed to switch to" " core %u, error %d\n", coreidx, error); goto out; } bus->mapped_device = dev; } out: return error; } static u8 ssb_sdio_read8(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; u8 val = 0xff; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) goto out; offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; val = sdio_readb(bus->host_sdio, offset, &error); if (error) { dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %02x, error %d\n", bus->sdio_sbaddr >> 16, offset, val, error); } out: sdio_release_host(bus->host_sdio); return val; } static u16 ssb_sdio_read16(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; u16 val = 0xffff; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) goto out; offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; val = sdio_readw(bus->host_sdio, offset, &error); if (error) { dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %04x, error %d\n", bus->sdio_sbaddr >> 16, offset, val, error); } out: sdio_release_host(bus->host_sdio); return val; } static u32 ssb_sdio_read32(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; u32 val = 0xffffffff; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) goto out; offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ val = sdio_readl(bus->host_sdio, offset, &error); if (error) { dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n", bus->sdio_sbaddr >> 16, offset, val, error); } out: sdio_release_host(bus->host_sdio); return val; } #ifdef CONFIG_SSB_BLOCKIO static void ssb_sdio_block_read(struct ssb_device *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { size_t saved_count = count; struct ssb_bus *bus = dev->bus; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) { error = -EIO; memset(buffer, 0xff, count); goto err_out; } offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; switch (reg_width) { case sizeof(u8): { error = sdio_readsb(bus->host_sdio, buffer, offset, count); break; } case sizeof(u16): { SSB_WARN_ON(count & 1); error = sdio_readsb(bus->host_sdio, buffer, offset, count); break; } case sizeof(u32): { SSB_WARN_ON(count & 3); offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ error = sdio_readsb(bus->host_sdio, buffer, offset, count); break; } default: SSB_WARN_ON(1); } if (!error) goto out; err_out: dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n", bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error); out: sdio_release_host(bus->host_sdio); } #endif /* CONFIG_SSB_BLOCKIO */ static void ssb_sdio_write8(struct ssb_device *dev, u16 offset, u8 val) { struct ssb_bus *bus = dev->bus; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) goto out; offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; sdio_writeb(bus->host_sdio, val, offset, &error); if (error) { dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %02x, error %d\n", bus->sdio_sbaddr >> 16, offset, val, error); } out: sdio_release_host(bus->host_sdio); } static void ssb_sdio_write16(struct ssb_device *dev, u16 offset, u16 val) { struct ssb_bus *bus = dev->bus; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) goto out; offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; sdio_writew(bus->host_sdio, val, offset, &error); if (error) { dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %04x, error %d\n", bus->sdio_sbaddr >> 16, offset, val, error); } out: sdio_release_host(bus->host_sdio); } static void ssb_sdio_write32(struct ssb_device *dev, u16 offset, u32 val) { struct ssb_bus *bus = dev->bus; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) goto out; offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ sdio_writel(bus->host_sdio, val, offset, &error); if (error) { dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %08x, error %d\n", bus->sdio_sbaddr >> 16, offset, val, error); } if (bus->quirks & SSB_QUIRK_SDIO_READ_AFTER_WRITE32) sdio_readl(bus->host_sdio, 0, &error); out: sdio_release_host(bus->host_sdio); } #ifdef CONFIG_SSB_BLOCKIO static void ssb_sdio_block_write(struct ssb_device *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { size_t saved_count = count; struct ssb_bus *bus = dev->bus; int error = 0; sdio_claim_host(bus->host_sdio); if (unlikely(ssb_sdio_switch_core(bus, dev))) { error = -EIO; memset((void *)buffer, 0xff, count); goto err_out; } offset |= bus->sdio_sbaddr & 0xffff; offset &= SBSDIO_SB_OFT_ADDR_MASK; switch (reg_width) { case sizeof(u8): error = sdio_writesb(bus->host_sdio, offset, (void *)buffer, count); break; case sizeof(u16): SSB_WARN_ON(count & 1); error = sdio_writesb(bus->host_sdio, offset, (void *)buffer, count); break; case sizeof(u32): SSB_WARN_ON(count & 3); offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ error = sdio_writesb(bus->host_sdio, offset, (void *)buffer, count); break; default: SSB_WARN_ON(1); } if (!error) goto out; err_out: dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n", bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error); out: sdio_release_host(bus->host_sdio); } #endif /* CONFIG_SSB_BLOCKIO */ /* Not "static", as it's used in main.c */ const struct ssb_bus_ops ssb_sdio_ops = { .read8 = ssb_sdio_read8, .read16 = ssb_sdio_read16, .read32 = ssb_sdio_read32, .write8 = ssb_sdio_write8, .write16 = ssb_sdio_write16, .write32 = ssb_sdio_write32, #ifdef CONFIG_SSB_BLOCKIO .block_read = ssb_sdio_block_read, .block_write = ssb_sdio_block_write, #endif }; #define GOTO_ERROR_ON(condition, description) do { \ if (unlikely(condition)) { \ error_description = description; \ goto error; \ } \ } while (0) int ssb_sdio_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv) { struct ssb_sprom *sprom = &iv->sprom; struct ssb_boardinfo *bi = &iv->boardinfo; const char *error_description = "none"; struct sdio_func_tuple *tuple; void *mac; memset(sprom, 0xFF, sizeof(*sprom)); sprom->boardflags_lo = 0; sprom->boardflags_hi = 0; tuple = bus->host_sdio->tuples; while (tuple) { switch (tuple->code) { case 0x22: /* extended function */ switch (tuple->data[0]) { case CISTPL_FUNCE_LAN_NODE_ID: GOTO_ERROR_ON((tuple->size != 7) && (tuple->data[1] != 6), "mac tpl size"); /* fetch the MAC address. */ mac = tuple->data + 2; memcpy(sprom->il0mac, mac, ETH_ALEN); memcpy(sprom->et1mac, mac, ETH_ALEN); break; default: break; } break; case 0x80: /* vendor specific tuple */ switch (tuple->data[0]) { case SSB_SDIO_CIS_SROMREV: GOTO_ERROR_ON(tuple->size != 2, "sromrev tpl size"); sprom->revision = tuple->data[1]; break; case SSB_SDIO_CIS_ID: GOTO_ERROR_ON((tuple->size != 5) && (tuple->size != 7), "id tpl size"); bi->vendor = tuple->data[1] | (tuple->data[2]<<8); break; case SSB_SDIO_CIS_BOARDREV: GOTO_ERROR_ON(tuple->size != 2, "boardrev tpl size"); sprom->board_rev = tuple->data[1]; break; case SSB_SDIO_CIS_PA: GOTO_ERROR_ON((tuple->size != 9) && (tuple->size != 10), "pa tpl size"); sprom->pa0b0 = tuple->data[1] | ((u16)tuple->data[2] << 8); sprom->pa0b1 = tuple->data[3] | ((u16)tuple->data[4] << 8); sprom->pa0b2 = tuple->data[5] | ((u16)tuple->data[6] << 8); sprom->itssi_a = tuple->data[7]; sprom->itssi_bg = tuple->data[7]; sprom->maxpwr_a = tuple->data[8]; sprom->maxpwr_bg = tuple->data[8]; break; case SSB_SDIO_CIS_OEMNAME: /* Not present */ break; case SSB_SDIO_CIS_CCODE: GOTO_ERROR_ON(tuple->size != 2, "ccode tpl size"); sprom->country_code = tuple->data[1]; break; case SSB_SDIO_CIS_ANTENNA: GOTO_ERROR_ON(tuple->size != 2, "ant tpl size"); sprom->ant_available_a = tuple->data[1]; sprom->ant_available_bg = tuple->data[1]; break; case SSB_SDIO_CIS_ANTGAIN: GOTO_ERROR_ON(tuple->size != 2, "antg tpl size"); sprom->antenna_gain.a0 = tuple->data[1]; sprom->antenna_gain.a1 = tuple->data[1]; sprom->antenna_gain.a2 = tuple->data[1]; sprom->antenna_gain.a3 = tuple->data[1]; break; case SSB_SDIO_CIS_BFLAGS: GOTO_ERROR_ON((tuple->size != 3) && (tuple->size != 5), "bfl tpl size"); sprom->boardflags_lo = tuple->data[1] | ((u16)tuple->data[2] << 8); break; case SSB_SDIO_CIS_LEDS: GOTO_ERROR_ON(tuple->size != 5, "leds tpl size"); sprom->gpio0 = tuple->data[1]; sprom->gpio1 = tuple->data[2]; sprom->gpio2 = tuple->data[3]; sprom->gpio3 = tuple->data[4]; break; default: break; } break; default: break; } tuple = tuple->next; } return 0; error: dev_err(ssb_sdio_dev(bus), "failed to fetch device invariants: %s\n", error_description); return -ENODEV; } void ssb_sdio_exit(struct ssb_bus *bus) { if (bus->bustype != SSB_BUSTYPE_SDIO) return; /* Nothing to do here. */ } int ssb_sdio_init(struct ssb_bus *bus) { if (bus->bustype != SSB_BUSTYPE_SDIO) return 0; bus->sdio_sbaddr = ~0; return 0; } compat-drivers-2012-09-18/drivers/ssb/scan.c0000644000175000017500000002467312026211315017731 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Bus scanning * * Copyright (C) 2005-2007 Michael Buesch * Copyright (C) 2005 Martin Langer * Copyright (C) 2005 Stefano Brivio * Copyright (C) 2005 Danny van Dyk * Copyright (C) 2005 Andreas Jaggi * Copyright (C) 2006 Broadcom Corporation. * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #include #include "ssb_private.h" const char *ssb_core_name(u16 coreid) { switch (coreid) { case SSB_DEV_CHIPCOMMON: return "ChipCommon"; case SSB_DEV_ILINE20: return "ILine 20"; case SSB_DEV_SDRAM: return "SDRAM"; case SSB_DEV_PCI: return "PCI"; case SSB_DEV_MIPS: return "MIPS"; case SSB_DEV_ETHERNET: return "Fast Ethernet"; case SSB_DEV_V90: return "V90"; case SSB_DEV_USB11_HOSTDEV: return "USB 1.1 Hostdev"; case SSB_DEV_ADSL: return "ADSL"; case SSB_DEV_ILINE100: return "ILine 100"; case SSB_DEV_IPSEC: return "IPSEC"; case SSB_DEV_PCMCIA: return "PCMCIA"; case SSB_DEV_INTERNAL_MEM: return "Internal Memory"; case SSB_DEV_MEMC_SDRAM: return "MEMC SDRAM"; case SSB_DEV_EXTIF: return "EXTIF"; case SSB_DEV_80211: return "IEEE 802.11"; case SSB_DEV_MIPS_3302: return "MIPS 3302"; case SSB_DEV_USB11_HOST: return "USB 1.1 Host"; case SSB_DEV_USB11_DEV: return "USB 1.1 Device"; case SSB_DEV_USB20_HOST: return "USB 2.0 Host"; case SSB_DEV_USB20_DEV: return "USB 2.0 Device"; case SSB_DEV_SDIO_HOST: return "SDIO Host"; case SSB_DEV_ROBOSWITCH: return "Roboswitch"; case SSB_DEV_PARA_ATA: return "PATA"; case SSB_DEV_SATA_XORDMA: return "SATA XOR-DMA"; case SSB_DEV_ETHERNET_GBIT: return "GBit Ethernet"; case SSB_DEV_PCIE: return "PCI-E"; case SSB_DEV_MIMO_PHY: return "MIMO PHY"; case SSB_DEV_SRAM_CTRLR: return "SRAM Controller"; case SSB_DEV_MINI_MACPHY: return "Mini MACPHY"; case SSB_DEV_ARM_1176: return "ARM 1176"; case SSB_DEV_ARM_7TDMI: return "ARM 7TDMI"; case SSB_DEV_ARM_CM3: return "ARM Cortex M3"; } return "UNKNOWN"; } static u16 pcidev_to_chipid(struct pci_dev *pci_dev) { u16 chipid_fallback = 0; switch (pci_dev->device) { case 0x4301: chipid_fallback = 0x4301; break; case 0x4305 ... 0x4307: chipid_fallback = 0x4307; break; case 0x4403: chipid_fallback = 0x4402; break; case 0x4610 ... 0x4615: chipid_fallback = 0x4610; break; case 0x4710 ... 0x4715: chipid_fallback = 0x4710; break; case 0x4320 ... 0x4325: chipid_fallback = 0x4309; break; case PCI_DEVICE_ID_BCM4401: case PCI_DEVICE_ID_BCM4401B0: case PCI_DEVICE_ID_BCM4401B1: chipid_fallback = 0x4401; break; default: ssb_printk(KERN_ERR PFX "PCI-ID not in fallback list\n"); } return chipid_fallback; } static u8 chipid_to_nrcores(u16 chipid) { switch (chipid) { case 0x5365: return 7; case 0x4306: return 6; case 0x4310: return 8; case 0x4307: case 0x4301: return 5; case 0x4401: case 0x4402: return 3; case 0x4710: case 0x4610: case 0x4704: return 9; default: ssb_printk(KERN_ERR PFX "CHIPID not in nrcores fallback list\n"); } return 1; } static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx, u16 offset) { u32 lo, hi; switch (bus->bustype) { case SSB_BUSTYPE_SSB: offset += current_coreidx * SSB_CORE_SIZE; break; case SSB_BUSTYPE_PCI: break; case SSB_BUSTYPE_PCMCIA: if (offset >= 0x800) { ssb_pcmcia_switch_segment(bus, 1); offset -= 0x800; } else ssb_pcmcia_switch_segment(bus, 0); lo = readw(bus->mmio + offset); hi = readw(bus->mmio + offset + 2); return lo | (hi << 16); case SSB_BUSTYPE_SDIO: offset += current_coreidx * SSB_CORE_SIZE; return ssb_sdio_scan_read32(bus, offset); } return readl(bus->mmio + offset); } static int scan_switchcore(struct ssb_bus *bus, u8 coreidx) { switch (bus->bustype) { case SSB_BUSTYPE_SSB: break; case SSB_BUSTYPE_PCI: return ssb_pci_switch_coreidx(bus, coreidx); case SSB_BUSTYPE_PCMCIA: return ssb_pcmcia_switch_coreidx(bus, coreidx); case SSB_BUSTYPE_SDIO: return ssb_sdio_scan_switch_coreidx(bus, coreidx); } return 0; } void ssb_iounmap(struct ssb_bus *bus) { switch (bus->bustype) { case SSB_BUSTYPE_SSB: case SSB_BUSTYPE_PCMCIA: iounmap(bus->mmio); break; case SSB_BUSTYPE_PCI: #ifdef CONFIG_SSB_PCIHOST pci_iounmap(bus->host_pci, bus->mmio); #else SSB_BUG_ON(1); /* Can't reach this code. */ #endif break; case SSB_BUSTYPE_SDIO: break; } bus->mmio = NULL; bus->mapped_device = NULL; } static void __iomem *ssb_ioremap(struct ssb_bus *bus, unsigned long baseaddr) { void __iomem *mmio = NULL; switch (bus->bustype) { case SSB_BUSTYPE_SSB: /* Only map the first core for now. */ /* fallthrough... */ case SSB_BUSTYPE_PCMCIA: mmio = ioremap(baseaddr, SSB_CORE_SIZE); break; case SSB_BUSTYPE_PCI: #ifdef CONFIG_SSB_PCIHOST mmio = pci_iomap(bus->host_pci, 0, ~0UL); #else SSB_BUG_ON(1); /* Can't reach this code. */ #endif break; case SSB_BUSTYPE_SDIO: /* Nothing to ioremap in the SDIO case, just fake it */ mmio = (void __iomem *)baseaddr; break; } return mmio; } static int we_support_multiple_80211_cores(struct ssb_bus *bus) { /* More than one 802.11 core is only supported by special chips. * There are chips with two 802.11 cores, but with dangling * pins on the second core. Be careful and reject them here. */ #ifdef CONFIG_SSB_PCIHOST if (bus->bustype == SSB_BUSTYPE_PCI) { if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM && ((bus->host_pci->device == 0x4313) || (bus->host_pci->device == 0x431A) || (bus->host_pci->device == 0x4321) || (bus->host_pci->device == 0x4324))) return 1; } #endif /* CONFIG_SSB_PCIHOST */ return 0; } int ssb_bus_scan(struct ssb_bus *bus, unsigned long baseaddr) { int err = -ENOMEM; void __iomem *mmio; u32 idhi, cc, rev, tmp; int dev_i, i; struct ssb_device *dev; int nr_80211_cores = 0; mmio = ssb_ioremap(bus, baseaddr); if (!mmio) goto out; bus->mmio = mmio; err = scan_switchcore(bus, 0); /* Switch to first core */ if (err) goto err_unmap; idhi = scan_read32(bus, 0, SSB_IDHIGH); cc = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; rev = (idhi & SSB_IDHIGH_RCLO); rev |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT; bus->nr_devices = 0; if (cc == SSB_DEV_CHIPCOMMON) { tmp = scan_read32(bus, 0, SSB_CHIPCO_CHIPID); bus->chip_id = (tmp & SSB_CHIPCO_IDMASK); bus->chip_rev = (tmp & SSB_CHIPCO_REVMASK) >> SSB_CHIPCO_REVSHIFT; bus->chip_package = (tmp & SSB_CHIPCO_PACKMASK) >> SSB_CHIPCO_PACKSHIFT; if (rev >= 4) { bus->nr_devices = (tmp & SSB_CHIPCO_NRCORESMASK) >> SSB_CHIPCO_NRCORESSHIFT; } tmp = scan_read32(bus, 0, SSB_CHIPCO_CAP); bus->chipco.capabilities = tmp; } else { if (bus->bustype == SSB_BUSTYPE_PCI) { bus->chip_id = pcidev_to_chipid(bus->host_pci); bus->chip_rev = bus->host_pci->revision; bus->chip_package = 0; } else { bus->chip_id = 0x4710; bus->chip_rev = 0; bus->chip_package = 0; } } ssb_printk(KERN_INFO PFX "Found chip with id 0x%04X, rev 0x%02X and " "package 0x%02X\n", bus->chip_id, bus->chip_rev, bus->chip_package); if (!bus->nr_devices) bus->nr_devices = chipid_to_nrcores(bus->chip_id); if (bus->nr_devices > ARRAY_SIZE(bus->devices)) { ssb_printk(KERN_ERR PFX "More than %d ssb cores found (%d)\n", SSB_MAX_NR_CORES, bus->nr_devices); goto err_unmap; } if (bus->bustype == SSB_BUSTYPE_SSB) { /* Now that we know the number of cores, * remap the whole IO space for all cores. */ err = -ENOMEM; iounmap(mmio); mmio = ioremap(baseaddr, SSB_CORE_SIZE * bus->nr_devices); if (!mmio) goto out; bus->mmio = mmio; } /* Fetch basic information about each core/device */ for (i = 0, dev_i = 0; i < bus->nr_devices; i++) { err = scan_switchcore(bus, i); if (err) goto err_unmap; dev = &(bus->devices[dev_i]); idhi = scan_read32(bus, i, SSB_IDHIGH); dev->id.coreid = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; dev->id.revision = (idhi & SSB_IDHIGH_RCLO); dev->id.revision |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT; dev->id.vendor = (idhi & SSB_IDHIGH_VC) >> SSB_IDHIGH_VC_SHIFT; dev->core_index = i; dev->bus = bus; dev->ops = bus->ops; printk(KERN_DEBUG PFX "Core %d found: %s " "(cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n", i, ssb_core_name(dev->id.coreid), dev->id.coreid, dev->id.revision, dev->id.vendor); switch (dev->id.coreid) { case SSB_DEV_80211: nr_80211_cores++; if (nr_80211_cores > 1) { if (!we_support_multiple_80211_cores(bus)) { ssb_dprintk(KERN_INFO PFX "Ignoring additional " "802.11 core\n"); continue; } } break; case SSB_DEV_EXTIF: #ifdef CONFIG_SSB_DRIVER_EXTIF if (bus->extif.dev) { ssb_printk(KERN_WARNING PFX "WARNING: Multiple EXTIFs found\n"); break; } bus->extif.dev = dev; #endif /* CONFIG_SSB_DRIVER_EXTIF */ break; case SSB_DEV_CHIPCOMMON: if (bus->chipco.dev) { ssb_printk(KERN_WARNING PFX "WARNING: Multiple ChipCommon found\n"); break; } bus->chipco.dev = dev; break; case SSB_DEV_MIPS: case SSB_DEV_MIPS_3302: #ifdef CONFIG_SSB_DRIVER_MIPS if (bus->mipscore.dev) { ssb_printk(KERN_WARNING PFX "WARNING: Multiple MIPS cores found\n"); break; } bus->mipscore.dev = dev; #endif /* CONFIG_SSB_DRIVER_MIPS */ break; case SSB_DEV_PCI: case SSB_DEV_PCIE: #ifdef CONFIG_SSB_DRIVER_PCICORE if (bus->bustype == SSB_BUSTYPE_PCI) { /* Ignore PCI cores on PCI-E cards. * Ignore PCI-E cores on PCI cards. */ if (dev->id.coreid == SSB_DEV_PCI) { if (pci_is_pcie(bus->host_pci)) continue; } else { if (!pci_is_pcie(bus->host_pci)) continue; } } if (bus->pcicore.dev) { ssb_printk(KERN_WARNING PFX "WARNING: Multiple PCI(E) cores found\n"); break; } bus->pcicore.dev = dev; #endif /* CONFIG_SSB_DRIVER_PCICORE */ break; case SSB_DEV_ETHERNET: if (bus->bustype == SSB_BUSTYPE_PCI) { if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM && (bus->host_pci->device & 0xFF00) == 0x4300) { /* This is a dangling ethernet core on a * wireless device. Ignore it. */ continue; } } break; default: break; } dev_i++; } bus->nr_devices = dev_i; err = 0; out: return err; err_unmap: ssb_iounmap(bus); goto out; } compat-drivers-2012-09-18/drivers/ssb/pcmcia.c0000644000175000017500000004701312026211315020232 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * PCMCIA-Hostbus related functions * * Copyright 2006 Johannes Berg * Copyright 2007-2008 Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #include #include #include #include "ssb_private.h" /* Define the following to 1 to enable a printk on each coreswitch. */ #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0 /* PCMCIA configuration registers */ #define SSB_PCMCIA_ADDRESS0 0x2E #define SSB_PCMCIA_ADDRESS1 0x30 #define SSB_PCMCIA_ADDRESS2 0x32 #define SSB_PCMCIA_MEMSEG 0x34 #define SSB_PCMCIA_SPROMCTL 0x36 #define SSB_PCMCIA_SPROMCTL_IDLE 0 #define SSB_PCMCIA_SPROMCTL_WRITE 1 #define SSB_PCMCIA_SPROMCTL_READ 2 #define SSB_PCMCIA_SPROMCTL_WRITEEN 4 #define SSB_PCMCIA_SPROMCTL_WRITEDIS 7 #define SSB_PCMCIA_SPROMCTL_DONE 8 #define SSB_PCMCIA_SPROM_DATALO 0x38 #define SSB_PCMCIA_SPROM_DATAHI 0x3A #define SSB_PCMCIA_SPROM_ADDRLO 0x3C #define SSB_PCMCIA_SPROM_ADDRHI 0x3E /* Hardware invariants CIS tuples */ #define SSB_PCMCIA_CIS 0x80 #define SSB_PCMCIA_CIS_ID 0x01 #define SSB_PCMCIA_CIS_BOARDREV 0x02 #define SSB_PCMCIA_CIS_PA 0x03 #define SSB_PCMCIA_CIS_PA_PA0B0_LO 0 #define SSB_PCMCIA_CIS_PA_PA0B0_HI 1 #define SSB_PCMCIA_CIS_PA_PA0B1_LO 2 #define SSB_PCMCIA_CIS_PA_PA0B1_HI 3 #define SSB_PCMCIA_CIS_PA_PA0B2_LO 4 #define SSB_PCMCIA_CIS_PA_PA0B2_HI 5 #define SSB_PCMCIA_CIS_PA_ITSSI 6 #define SSB_PCMCIA_CIS_PA_MAXPOW 7 #define SSB_PCMCIA_CIS_OEMNAME 0x04 #define SSB_PCMCIA_CIS_CCODE 0x05 #define SSB_PCMCIA_CIS_ANTENNA 0x06 #define SSB_PCMCIA_CIS_ANTGAIN 0x07 #define SSB_PCMCIA_CIS_BFLAGS 0x08 #define SSB_PCMCIA_CIS_LEDS 0x09 /* PCMCIA SPROM size. */ #define SSB_PCMCIA_SPROM_SIZE 256 #define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16)) /* Write to a PCMCIA configuration register. */ static int ssb_pcmcia_cfg_write(struct ssb_bus *bus, u8 offset, u8 value) { int res; res = pcmcia_write_config_byte(bus->host_pcmcia, offset, value); if (unlikely(res != 0)) return -EBUSY; return 0; } /* Read from a PCMCIA configuration register. */ static int ssb_pcmcia_cfg_read(struct ssb_bus *bus, u8 offset, u8 *value) { int res; res = pcmcia_read_config_byte(bus->host_pcmcia, offset, value); if (unlikely(res != 0)) return -EBUSY; return 0; } int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { int err; int attempts = 0; u32 cur_core; u32 addr; u32 read_addr; u8 val; addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; while (1) { err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS0, (addr & 0x0000F000) >> 12); if (err) goto error; err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS1, (addr & 0x00FF0000) >> 16); if (err) goto error; err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS2, (addr & 0xFF000000) >> 24); if (err) goto error; read_addr = 0; err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS0, &val); if (err) goto error; read_addr |= ((u32)(val & 0x0F)) << 12; err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS1, &val); if (err) goto error; read_addr |= ((u32)val) << 16; err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS2, &val); if (err) goto error; read_addr |= ((u32)val) << 24; cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; if (cur_core == coreidx) break; err = -ETIMEDOUT; if (attempts++ > SSB_BAR0_MAX_RETRIES) goto error; udelay(10); } return 0; error: ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); return err; } int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { int err; #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG ssb_printk(KERN_INFO PFX "Switching to %s core, index %d\n", ssb_core_name(dev->id.coreid), dev->core_index); #endif err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); if (!err) bus->mapped_device = dev; return err; } int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) { int attempts = 0; int err; u8 val; SSB_WARN_ON((seg != 0) && (seg != 1)); while (1) { err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_MEMSEG, seg); if (err) goto error; err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_MEMSEG, &val); if (err) goto error; if (val == seg) break; err = -ETIMEDOUT; if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) goto error; udelay(10); } bus->mapped_pcmcia_seg = seg; return 0; error: ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); return err; } static int select_core_and_segment(struct ssb_device *dev, u16 *offset) { struct ssb_bus *bus = dev->bus; int err; u8 need_segment; if (*offset >= 0x800) { *offset -= 0x800; need_segment = 1; } else need_segment = 0; if (unlikely(dev != bus->mapped_device)) { err = ssb_pcmcia_switch_core(bus, dev); if (unlikely(err)) return err; } if (unlikely(need_segment != bus->mapped_pcmcia_seg)) { err = ssb_pcmcia_switch_segment(bus, need_segment); if (unlikely(err)) return err; } return 0; } static u8 ssb_pcmcia_read8(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; unsigned long flags; int err; u8 value = 0xFF; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (likely(!err)) value = readb(bus->mmio + offset); spin_unlock_irqrestore(&bus->bar_lock, flags); return value; } static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; unsigned long flags; int err; u16 value = 0xFFFF; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (likely(!err)) value = readw(bus->mmio + offset); spin_unlock_irqrestore(&bus->bar_lock, flags); return value; } static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; unsigned long flags; int err; u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (likely(!err)) { lo = readw(bus->mmio + offset); hi = readw(bus->mmio + offset + 2); } spin_unlock_irqrestore(&bus->bar_lock, flags); return (lo | (hi << 16)); } #ifdef CONFIG_SSB_BLOCKIO static void ssb_pcmcia_block_read(struct ssb_device *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { struct ssb_bus *bus = dev->bus; unsigned long flags; void __iomem *addr = bus->mmio + offset; int err; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (unlikely(err)) { memset(buffer, 0xFF, count); goto unlock; } switch (reg_width) { case sizeof(u8): { u8 *buf = buffer; while (count) { *buf = __raw_readb(addr); buf++; count--; } break; } case sizeof(u16): { __le16 *buf = buffer; SSB_WARN_ON(count & 1); while (count) { *buf = (__force __le16)__raw_readw(addr); buf++; count -= 2; } break; } case sizeof(u32): { __le16 *buf = buffer; SSB_WARN_ON(count & 3); while (count) { *buf = (__force __le16)__raw_readw(addr); buf++; *buf = (__force __le16)__raw_readw(addr + 2); buf++; count -= 4; } break; } default: SSB_WARN_ON(1); } unlock: spin_unlock_irqrestore(&bus->bar_lock, flags); } #endif /* CONFIG_SSB_BLOCKIO */ static void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value) { struct ssb_bus *bus = dev->bus; unsigned long flags; int err; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (likely(!err)) writeb(value, bus->mmio + offset); mmiowb(); spin_unlock_irqrestore(&bus->bar_lock, flags); } static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) { struct ssb_bus *bus = dev->bus; unsigned long flags; int err; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (likely(!err)) writew(value, bus->mmio + offset); mmiowb(); spin_unlock_irqrestore(&bus->bar_lock, flags); } static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) { struct ssb_bus *bus = dev->bus; unsigned long flags; int err; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (likely(!err)) { writew((value & 0x0000FFFF), bus->mmio + offset); writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2); } mmiowb(); spin_unlock_irqrestore(&bus->bar_lock, flags); } #ifdef CONFIG_SSB_BLOCKIO static void ssb_pcmcia_block_write(struct ssb_device *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { struct ssb_bus *bus = dev->bus; unsigned long flags; void __iomem *addr = bus->mmio + offset; int err; spin_lock_irqsave(&bus->bar_lock, flags); err = select_core_and_segment(dev, &offset); if (unlikely(err)) goto unlock; switch (reg_width) { case sizeof(u8): { const u8 *buf = buffer; while (count) { __raw_writeb(*buf, addr); buf++; count--; } break; } case sizeof(u16): { const __le16 *buf = buffer; SSB_WARN_ON(count & 1); while (count) { __raw_writew((__force u16)(*buf), addr); buf++; count -= 2; } break; } case sizeof(u32): { const __le16 *buf = buffer; SSB_WARN_ON(count & 3); while (count) { __raw_writew((__force u16)(*buf), addr); buf++; __raw_writew((__force u16)(*buf), addr + 2); buf++; count -= 4; } break; } default: SSB_WARN_ON(1); } unlock: mmiowb(); spin_unlock_irqrestore(&bus->bar_lock, flags); } #endif /* CONFIG_SSB_BLOCKIO */ /* Not "static", as it's used in main.c */ const struct ssb_bus_ops ssb_pcmcia_ops = { .read8 = ssb_pcmcia_read8, .read16 = ssb_pcmcia_read16, .read32 = ssb_pcmcia_read32, .write8 = ssb_pcmcia_write8, .write16 = ssb_pcmcia_write16, .write32 = ssb_pcmcia_write32, #ifdef CONFIG_SSB_BLOCKIO .block_read = ssb_pcmcia_block_read, .block_write = ssb_pcmcia_block_write, #endif }; static int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command) { unsigned int i; int err; u8 value; err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROMCTL, command); if (err) return err; for (i = 0; i < 1000; i++) { err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROMCTL, &value); if (err) return err; if (value & SSB_PCMCIA_SPROMCTL_DONE) return 0; udelay(10); } return -ETIMEDOUT; } /* offset is the 16bit word offset */ static int ssb_pcmcia_sprom_read(struct ssb_bus *bus, u16 offset, u16 *value) { int err; u8 lo, hi; offset *= 2; /* Make byte offset */ err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, (offset & 0x00FF)); if (err) return err; err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, (offset & 0xFF00) >> 8); if (err) return err; err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_READ); if (err) return err; err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATALO, &lo); if (err) return err; err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATAHI, &hi); if (err) return err; *value = (lo | (((u16)hi) << 8)); return 0; } /* offset is the 16bit word offset */ static int ssb_pcmcia_sprom_write(struct ssb_bus *bus, u16 offset, u16 value) { int err; offset *= 2; /* Make byte offset */ err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, (offset & 0x00FF)); if (err) return err; err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, (offset & 0xFF00) >> 8); if (err) return err; err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATALO, (value & 0x00FF)); if (err) return err; err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATAHI, (value & 0xFF00) >> 8); if (err) return err; err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITE); if (err) return err; msleep(20); return 0; } /* Read the SPROM image. bufsize is in 16bit words. */ static int ssb_pcmcia_sprom_read_all(struct ssb_bus *bus, u16 *sprom) { int err, i; for (i = 0; i < SSB_PCMCIA_SPROM_SIZE; i++) { err = ssb_pcmcia_sprom_read(bus, i, &sprom[i]); if (err) return err; } return 0; } /* Write the SPROM image. size is in 16bit words. */ static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom) { int i, err; bool failed = 0; size_t size = SSB_PCMCIA_SPROM_SIZE; ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! " "Please stand by...\n"); err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN); if (err) { ssb_printk(KERN_NOTICE PFX "Could not enable SPROM write access.\n"); return -EBUSY; } ssb_printk(KERN_NOTICE PFX "[ 0%%"); msleep(500); for (i = 0; i < size; i++) { if (i == size / 4) ssb_printk("25%%"); else if (i == size / 2) ssb_printk("50%%"); else if (i == (size * 3) / 4) ssb_printk("75%%"); else if (i % 2) ssb_printk("."); err = ssb_pcmcia_sprom_write(bus, i, sprom[i]); if (err) { ssb_printk(KERN_NOTICE PFX "Failed to write to SPROM.\n"); failed = 1; break; } } err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS); if (err) { ssb_printk(KERN_NOTICE PFX "Could not disable SPROM write access.\n"); failed = 1; } msleep(500); if (!failed) { ssb_printk("100%% ]\n"); ssb_printk(KERN_NOTICE PFX "SPROM written.\n"); } return failed ? -EBUSY : 0; } static int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size) { //TODO return 0; } #define GOTO_ERROR_ON(condition, description) do { \ if (unlikely(condition)) { \ error_description = description; \ goto error; \ } \ } while (0) static int ssb_pcmcia_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple, void *priv) { struct ssb_sprom *sprom = priv; if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) return -EINVAL; if (tuple->TupleDataLen != ETH_ALEN + 2) return -EINVAL; if (tuple->TupleData[1] != ETH_ALEN) return -EINVAL; memcpy(sprom->il0mac, &tuple->TupleData[2], ETH_ALEN); return 0; }; static int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev, tuple_t *tuple, void *priv) { struct ssb_init_invariants *iv = priv; struct ssb_sprom *sprom = &iv->sprom; struct ssb_boardinfo *bi = &iv->boardinfo; const char *error_description; GOTO_ERROR_ON(tuple->TupleDataLen < 1, "VEN tpl < 1"); switch (tuple->TupleData[0]) { case SSB_PCMCIA_CIS_ID: GOTO_ERROR_ON((tuple->TupleDataLen != 5) && (tuple->TupleDataLen != 7), "id tpl size"); bi->vendor = tuple->TupleData[1] | ((u16)tuple->TupleData[2] << 8); break; case SSB_PCMCIA_CIS_BOARDREV: GOTO_ERROR_ON(tuple->TupleDataLen != 2, "boardrev tpl size"); sprom->board_rev = tuple->TupleData[1]; break; case SSB_PCMCIA_CIS_PA: GOTO_ERROR_ON((tuple->TupleDataLen != 9) && (tuple->TupleDataLen != 10), "pa tpl size"); sprom->pa0b0 = tuple->TupleData[1] | ((u16)tuple->TupleData[2] << 8); sprom->pa0b1 = tuple->TupleData[3] | ((u16)tuple->TupleData[4] << 8); sprom->pa0b2 = tuple->TupleData[5] | ((u16)tuple->TupleData[6] << 8); sprom->itssi_a = tuple->TupleData[7]; sprom->itssi_bg = tuple->TupleData[7]; sprom->maxpwr_a = tuple->TupleData[8]; sprom->maxpwr_bg = tuple->TupleData[8]; break; case SSB_PCMCIA_CIS_OEMNAME: /* We ignore this. */ break; case SSB_PCMCIA_CIS_CCODE: GOTO_ERROR_ON(tuple->TupleDataLen != 2, "ccode tpl size"); sprom->country_code = tuple->TupleData[1]; break; case SSB_PCMCIA_CIS_ANTENNA: GOTO_ERROR_ON(tuple->TupleDataLen != 2, "ant tpl size"); sprom->ant_available_a = tuple->TupleData[1]; sprom->ant_available_bg = tuple->TupleData[1]; break; case SSB_PCMCIA_CIS_ANTGAIN: GOTO_ERROR_ON(tuple->TupleDataLen != 2, "antg tpl size"); sprom->antenna_gain.a0 = tuple->TupleData[1]; sprom->antenna_gain.a1 = tuple->TupleData[1]; sprom->antenna_gain.a2 = tuple->TupleData[1]; sprom->antenna_gain.a3 = tuple->TupleData[1]; break; case SSB_PCMCIA_CIS_BFLAGS: GOTO_ERROR_ON((tuple->TupleDataLen != 3) && (tuple->TupleDataLen != 5), "bfl tpl size"); sprom->boardflags_lo = tuple->TupleData[1] | ((u16)tuple->TupleData[2] << 8); break; case SSB_PCMCIA_CIS_LEDS: GOTO_ERROR_ON(tuple->TupleDataLen != 5, "leds tpl size"); sprom->gpio0 = tuple->TupleData[1]; sprom->gpio1 = tuple->TupleData[2]; sprom->gpio2 = tuple->TupleData[3]; sprom->gpio3 = tuple->TupleData[4]; break; } return -ENOSPC; /* continue with next entry */ error: ssb_printk(KERN_ERR PFX "PCMCIA: Failed to fetch device invariants: %s\n", error_description); return -ENODEV; } int ssb_pcmcia_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv) { struct ssb_sprom *sprom = &iv->sprom; int res; memset(sprom, 0xFF, sizeof(*sprom)); sprom->revision = 1; sprom->boardflags_lo = 0; sprom->boardflags_hi = 0; /* First fetch the MAC address. */ res = pcmcia_loop_tuple(bus->host_pcmcia, CISTPL_FUNCE, ssb_pcmcia_get_mac, sprom); if (res != 0) { ssb_printk(KERN_ERR PFX "PCMCIA: Failed to fetch MAC address\n"); return -ENODEV; } /* Fetch the vendor specific tuples. */ res = pcmcia_loop_tuple(bus->host_pcmcia, SSB_PCMCIA_CIS, ssb_pcmcia_do_get_invariants, iv); if ((res == 0) || (res == -ENOSPC)) return 0; ssb_printk(KERN_ERR PFX "PCMCIA: Failed to fetch device invariants\n"); return -ENODEV; } static ssize_t ssb_pcmcia_attr_sprom_show(struct device *pcmciadev, struct device_attribute *attr, char *buf) { struct pcmcia_device *pdev = container_of(pcmciadev, struct pcmcia_device, dev); struct ssb_bus *bus; bus = ssb_pcmcia_dev_to_bus(pdev); if (!bus) return -ENODEV; return ssb_attr_sprom_show(bus, buf, ssb_pcmcia_sprom_read_all); } static ssize_t ssb_pcmcia_attr_sprom_store(struct device *pcmciadev, struct device_attribute *attr, const char *buf, size_t count) { struct pcmcia_device *pdev = container_of(pcmciadev, struct pcmcia_device, dev); struct ssb_bus *bus; bus = ssb_pcmcia_dev_to_bus(pdev); if (!bus) return -ENODEV; return ssb_attr_sprom_store(bus, buf, count, ssb_pcmcia_sprom_check_crc, ssb_pcmcia_sprom_write_all); } static DEVICE_ATTR(ssb_sprom, 0600, ssb_pcmcia_attr_sprom_show, ssb_pcmcia_attr_sprom_store); static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor) { u8 val; int err; err = ssb_pcmcia_cfg_read(bus, cor, &val); if (err) return err; val &= ~COR_SOFT_RESET; val |= COR_FUNC_ENA | COR_IREQ_ENA | COR_LEVEL_REQ; err = ssb_pcmcia_cfg_write(bus, cor, val); if (err) return err; msleep(40); return 0; } /* Initialize the PCMCIA hardware. This is called on Init and Resume. */ int ssb_pcmcia_hardware_setup(struct ssb_bus *bus) { int err; if (bus->bustype != SSB_BUSTYPE_PCMCIA) return 0; /* Switch segment to a known state and sync * bus->mapped_pcmcia_seg with hardware state. */ ssb_pcmcia_switch_segment(bus, 0); /* Init the COR register. */ err = ssb_pcmcia_cor_setup(bus, CISREG_COR); if (err) return err; /* Some cards also need this register to get poked. */ err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80); if (err) return err; return 0; } void ssb_pcmcia_exit(struct ssb_bus *bus) { if (bus->bustype != SSB_BUSTYPE_PCMCIA) return; device_remove_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); } int ssb_pcmcia_init(struct ssb_bus *bus) { int err; if (bus->bustype != SSB_BUSTYPE_PCMCIA) return 0; err = ssb_pcmcia_hardware_setup(bus); if (err) goto error; bus->sprom_size = SSB_PCMCIA_SPROM_SIZE; mutex_init(&bus->sprom_mutex); err = device_create_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); if (err) goto error; return 0; error: ssb_printk(KERN_ERR PFX "Failed to initialize PCMCIA host device\n"); return err; } compat-drivers-2012-09-18/drivers/ssb/pcihost_wrapper.c0000644000175000017500000000530712026211315022207 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * PCI Hostdevice wrapper * * Copyright (c) 2005 Martin Langer * Copyright (c) 2005 Stefano Brivio * Copyright (c) 2005 Danny van Dyk * Copyright (c) 2005 Andreas Jaggi * Copyright (c) 2005-2007 Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #ifdef CONFIG_PM static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state) { struct ssb_bus *ssb = pci_get_drvdata(dev); int err; err = ssb_bus_suspend(ssb); if (err) return err; pci_save_state(dev); pci_disable_device(dev); pci_set_power_state(dev, pci_choose_state(dev, state)); return 0; } static int ssb_pcihost_resume(struct pci_dev *dev) { struct ssb_bus *ssb = pci_get_drvdata(dev); int err; pci_set_power_state(dev, 0); err = pci_enable_device(dev); if (err) return err; pci_restore_state(dev); err = ssb_bus_resume(ssb); if (err) return err; return 0; } #else /* CONFIG_PM */ # define ssb_pcihost_suspend NULL # define ssb_pcihost_resume NULL #endif /* CONFIG_PM */ static int __devinit ssb_pcihost_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct ssb_bus *ssb; int err = -ENOMEM; const char *name; u32 val; ssb = kzalloc(sizeof(*ssb), GFP_KERNEL); if (!ssb) goto out; err = pci_enable_device(dev); if (err) goto err_kfree_ssb; name = dev_name(&dev->dev); if (dev->driver && dev->driver->name) name = dev->driver->name; err = pci_request_regions(dev, name); if (err) goto err_pci_disable; pci_set_master(dev); /* Disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_read_config_dword(dev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(dev, 0x40, val & 0xffff00ff); err = ssb_bus_pcibus_register(ssb, dev); if (err) goto err_pci_release_regions; pci_set_drvdata(dev, ssb); out: return err; err_pci_release_regions: pci_release_regions(dev); err_pci_disable: pci_disable_device(dev); err_kfree_ssb: kfree(ssb); return err; } static void ssb_pcihost_remove(struct pci_dev *dev) { struct ssb_bus *ssb = pci_get_drvdata(dev); ssb_bus_unregister(ssb); pci_release_regions(dev); pci_disable_device(dev); kfree(ssb); pci_set_drvdata(dev, NULL); } int __devinit ssb_pcihost_register(struct pci_driver *driver) { driver->probe = ssb_pcihost_probe; driver->remove = ssb_pcihost_remove; driver->suspend = ssb_pcihost_suspend; driver->resume = ssb_pcihost_resume; return pci_register_driver(driver); } EXPORT_SYMBOL(ssb_pcihost_register); compat-drivers-2012-09-18/drivers/ssb/pci.c0000644000175000017500000010427412026211315017554 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane PCI-Hostbus related functions. * * Copyright (C) 2005-2006 Michael Buesch * Copyright (C) 2005 Martin Langer * Copyright (C) 2005 Stefano Brivio * Copyright (C) 2005 Danny van Dyk * Copyright (C) 2005 Andreas Jaggi * * Derived from the Broadcom 4400 device driver. * Copyright (C) 2002 David S. Miller (davem@redhat.com) * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) * Copyright (C) 2006 Broadcom Corporation. * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #include "ssb_private.h" /* Define the following to 1 to enable a printk on each coreswitch. */ #define SSB_VERBOSE_PCICORESWITCH_DEBUG 0 /* Lowlevel coreswitching */ int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { int err; int attempts = 0; u32 cur_core; while (1) { err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN, (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE); if (err) goto error; err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN, &cur_core); if (err) goto error; cur_core = (cur_core - SSB_ENUM_BASE) / SSB_CORE_SIZE; if (cur_core == coreidx) break; if (attempts++ > SSB_BAR0_MAX_RETRIES) goto error; udelay(10); } return 0; error: ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); return -ENODEV; } int ssb_pci_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { int err; unsigned long flags; #if SSB_VERBOSE_PCICORESWITCH_DEBUG ssb_printk(KERN_INFO PFX "Switching to %s core, index %d\n", ssb_core_name(dev->id.coreid), dev->core_index); #endif spin_lock_irqsave(&bus->bar_lock, flags); err = ssb_pci_switch_coreidx(bus, dev->core_index); if (!err) bus->mapped_device = dev; spin_unlock_irqrestore(&bus->bar_lock, flags); return err; } /* Enable/disable the on board crystal oscillator and/or PLL. */ int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on) { int err; u32 in, out, outenable; u16 pci_status; if (bus->bustype != SSB_BUSTYPE_PCI) return 0; err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in); if (err) goto err_pci; err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out); if (err) goto err_pci; err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable); if (err) goto err_pci; outenable |= what; if (turn_on) { /* Avoid glitching the clock if GPRS is already using it. * We can't actually read the state of the PLLPD so we infer it * by the value of XTAL_PU which *is* readable via gpioin. */ if (!(in & SSB_GPIO_XTAL)) { if (what & SSB_GPIO_XTAL) { /* Turn the crystal on */ out |= SSB_GPIO_XTAL; if (what & SSB_GPIO_PLL) out |= SSB_GPIO_PLL; err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); if (err) goto err_pci; err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable); if (err) goto err_pci; msleep(1); } if (what & SSB_GPIO_PLL) { /* Turn the PLL on */ out &= ~SSB_GPIO_PLL; err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); if (err) goto err_pci; msleep(5); } } err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status); if (err) goto err_pci; pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT; err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status); if (err) goto err_pci; } else { if (what & SSB_GPIO_XTAL) { /* Turn the crystal off */ out &= ~SSB_GPIO_XTAL; } if (what & SSB_GPIO_PLL) { /* Turn the PLL off */ out |= SSB_GPIO_PLL; } err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); if (err) goto err_pci; err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable); if (err) goto err_pci; } out: return err; err_pci: printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n"); err = -EBUSY; goto out; } /* Get the word-offset for a SSB_SPROM_XXX define. */ #define SPOFF(offset) ((offset) / sizeof(u16)) /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */ #define SPEX16(_outvar, _offset, _mask, _shift) \ out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift)) #define SPEX32(_outvar, _offset, _mask, _shift) \ out->_outvar = ((((u32)in[SPOFF((_offset)+2)] << 16 | \ in[SPOFF(_offset)]) & (_mask)) >> (_shift)) #define SPEX(_outvar, _offset, _mask, _shift) \ SPEX16(_outvar, _offset, _mask, _shift) #define SPEX_ARRAY8(_field, _offset, _mask, _shift) \ do { \ SPEX(_field[0], _offset + 0, _mask, _shift); \ SPEX(_field[1], _offset + 2, _mask, _shift); \ SPEX(_field[2], _offset + 4, _mask, _shift); \ SPEX(_field[3], _offset + 6, _mask, _shift); \ SPEX(_field[4], _offset + 8, _mask, _shift); \ SPEX(_field[5], _offset + 10, _mask, _shift); \ SPEX(_field[6], _offset + 12, _mask, _shift); \ SPEX(_field[7], _offset + 14, _mask, _shift); \ } while (0) static inline u8 ssb_crc8(u8 crc, u8 data) { /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */ static const u8 t[] = { 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, }; return t[crc ^ data]; } static u8 ssb_sprom_crc(const u16 *sprom, u16 size) { int word; u8 crc = 0xFF; for (word = 0; word < size - 1; word++) { crc = ssb_crc8(crc, sprom[word] & 0x00FF); crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8); } crc = ssb_crc8(crc, sprom[size - 1] & 0x00FF); crc ^= 0xFF; return crc; } static int sprom_check_crc(const u16 *sprom, size_t size) { u8 crc; u8 expected_crc; u16 tmp; crc = ssb_sprom_crc(sprom, size); tmp = sprom[size - 1] & SSB_SPROM_REVISION_CRC; expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; if (crc != expected_crc) return -EPROTO; return 0; } static int sprom_do_read(struct ssb_bus *bus, u16 *sprom) { int i; for (i = 0; i < bus->sprom_size; i++) sprom[i] = ioread16(bus->mmio + bus->sprom_offset + (i * 2)); return 0; } static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) { struct pci_dev *pdev = bus->host_pci; int i, err; u32 spromctl; u16 size = bus->sprom_size; ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); if (err) goto err_ctlreg; spromctl |= SSB_SPROMCTL_WE; err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); if (err) goto err_ctlreg; ssb_printk(KERN_NOTICE PFX "[ 0%%"); msleep(500); for (i = 0; i < size; i++) { if (i == size / 4) ssb_printk("25%%"); else if (i == size / 2) ssb_printk("50%%"); else if (i == (size * 3) / 4) ssb_printk("75%%"); else if (i % 2) ssb_printk("."); writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2)); mmiowb(); msleep(20); } err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); if (err) goto err_ctlreg; spromctl &= ~SSB_SPROMCTL_WE; err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); if (err) goto err_ctlreg; msleep(500); ssb_printk("100%% ]\n"); ssb_printk(KERN_NOTICE PFX "SPROM written.\n"); return 0; err_ctlreg: ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n"); return err; } static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in, u16 mask, u16 shift) { u16 v; u8 gain; v = in[SPOFF(SSB_SPROM1_AGAIN)]; gain = (v & mask) >> shift; if (gain == 0xFF) gain = 2; /* If unset use 2dBm */ if (sprom_revision == 1) { /* Convert to Q5.2 */ gain <<= 2; } else { /* Q5.2 Fractional part is stored in 0xC0 */ gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2); } return (s8)gain; } static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) { int i; u16 v; u16 loc[3]; if (out->revision == 3) /* rev 3 moved MAC */ loc[0] = SSB_SPROM3_IL0MAC; else { loc[0] = SSB_SPROM1_IL0MAC; loc[1] = SSB_SPROM1_ET0MAC; loc[2] = SSB_SPROM1_ET1MAC; } for (i = 0; i < 3; i++) { v = in[SPOFF(loc[0]) + i]; *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); } if (out->revision < 3) { /* only rev 1-2 have et0, et1 */ for (i = 0; i < 3; i++) { v = in[SPOFF(loc[1]) + i]; *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); } for (i = 0; i < 3; i++) { v = in[SPOFF(loc[2]) + i]; *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); } } SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0); SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A, SSB_SPROM1_ETHPHY_ET1A_SHIFT); SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14); SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15); SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0); if (out->revision == 1) SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE, SSB_SPROM1_BINF_CCODE_SHIFT); SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA, SSB_SPROM1_BINF_ANTA_SHIFT); SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG, SSB_SPROM1_BINF_ANTBG_SHIFT); SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0); SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0); SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0); SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0); SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0); SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0); SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0); SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1, SSB_SPROM1_GPIOA_P1_SHIFT); SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0); SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3, SSB_SPROM1_GPIOB_P3_SHIFT); SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A, SSB_SPROM1_MAXPWR_A_SHIFT); SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0); SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A, SSB_SPROM1_ITSSI_A_SHIFT); SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0); SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0); if (out->revision >= 2) SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); SPEX(alpha2[0], SSB_SPROM1_CCODE, 0xff00, 8); SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0); /* Extract the antenna gain values. */ out->antenna_gain.a0 = r123_extract_antgain(out->revision, in, SSB_SPROM1_AGAIN_BG, SSB_SPROM1_AGAIN_BG_SHIFT); out->antenna_gain.a1 = r123_extract_antgain(out->revision, in, SSB_SPROM1_AGAIN_A, SSB_SPROM1_AGAIN_A_SHIFT); } /* Revs 4 5 and 8 have partially shared layout */ static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in) { SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0, SSB_SPROM4_TXPID2G0_SHIFT); SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1, SSB_SPROM4_TXPID2G1_SHIFT); SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2, SSB_SPROM4_TXPID2G2_SHIFT); SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3, SSB_SPROM4_TXPID2G3_SHIFT); SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0, SSB_SPROM4_TXPID5GL0_SHIFT); SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1, SSB_SPROM4_TXPID5GL1_SHIFT); SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2, SSB_SPROM4_TXPID5GL2_SHIFT); SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3, SSB_SPROM4_TXPID5GL3_SHIFT); SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0, SSB_SPROM4_TXPID5G0_SHIFT); SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1, SSB_SPROM4_TXPID5G1_SHIFT); SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2, SSB_SPROM4_TXPID5G2_SHIFT); SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3, SSB_SPROM4_TXPID5G3_SHIFT); SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0, SSB_SPROM4_TXPID5GH0_SHIFT); SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1, SSB_SPROM4_TXPID5GH1_SHIFT); SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2, SSB_SPROM4_TXPID5GH2_SHIFT); SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3, SSB_SPROM4_TXPID5GH3_SHIFT); } static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) { int i; u16 v; u16 il0mac_offset; if (out->revision == 4) il0mac_offset = SSB_SPROM4_IL0MAC; else il0mac_offset = SSB_SPROM5_IL0MAC; /* extract the MAC address */ for (i = 0; i < 3; i++) { v = in[SPOFF(il0mac_offset) + i]; *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); } SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0); SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A, SSB_SPROM4_ETHPHY_ET1A_SHIFT); SPEX(board_rev, SSB_SPROM4_BOARDREV, 0xFFFF, 0); if (out->revision == 4) { SPEX(alpha2[0], SSB_SPROM4_CCODE, 0xff00, 8); SPEX(alpha2[1], SSB_SPROM4_CCODE, 0x00ff, 0); SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0); SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0); SPEX(boardflags2_lo, SSB_SPROM4_BFL2LO, 0xFFFF, 0); SPEX(boardflags2_hi, SSB_SPROM4_BFL2HI, 0xFFFF, 0); } else { SPEX(alpha2[0], SSB_SPROM5_CCODE, 0xff00, 8); SPEX(alpha2[1], SSB_SPROM5_CCODE, 0x00ff, 0); SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0); SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0); SPEX(boardflags2_lo, SSB_SPROM5_BFL2LO, 0xFFFF, 0); SPEX(boardflags2_hi, SSB_SPROM5_BFL2HI, 0xFFFF, 0); } SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A, SSB_SPROM4_ANTAVAIL_A_SHIFT); SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG, SSB_SPROM4_ANTAVAIL_BG_SHIFT); SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0); SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG, SSB_SPROM4_ITSSI_BG_SHIFT); SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0); SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A, SSB_SPROM4_ITSSI_A_SHIFT); if (out->revision == 4) { SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0); SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1, SSB_SPROM4_GPIOA_P1_SHIFT); SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0); SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3, SSB_SPROM4_GPIOB_P3_SHIFT); } else { SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0); SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1, SSB_SPROM5_GPIOA_P1_SHIFT); SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0); SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3, SSB_SPROM5_GPIOB_P3_SHIFT); } /* Extract the antenna gain values. */ SPEX(antenna_gain.a0, SSB_SPROM4_AGAIN01, SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT); SPEX(antenna_gain.a1, SSB_SPROM4_AGAIN01, SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT); SPEX(antenna_gain.a2, SSB_SPROM4_AGAIN23, SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT); SPEX(antenna_gain.a3, SSB_SPROM4_AGAIN23, SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); sprom_extract_r458(out, in); /* TODO - get remaining rev 4 stuff needed */ } static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in) { int i; u16 v, o; u16 pwr_info_offset[] = { SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1, SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3 }; BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != ARRAY_SIZE(out->core_pwr_info)); /* extract the MAC address */ for (i = 0; i < 3; i++) { v = in[SPOFF(SSB_SPROM8_IL0MAC) + i]; *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); } SPEX(board_rev, SSB_SPROM8_BOARDREV, 0xFFFF, 0); SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8); SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0); SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0); SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0); SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0); SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0); SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A, SSB_SPROM8_ANTAVAIL_A_SHIFT); SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG, SSB_SPROM8_ANTAVAIL_BG_SHIFT); SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0); SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG, SSB_SPROM8_ITSSI_BG_SHIFT); SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0); SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A, SSB_SPROM8_ITSSI_A_SHIFT); SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0); SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK, SSB_SPROM8_MAXP_AL_SHIFT); SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0); SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1, SSB_SPROM8_GPIOA_P1_SHIFT); SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0); SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3, SSB_SPROM8_GPIOB_P3_SHIFT); SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0); SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G, SSB_SPROM8_TRI5G_SHIFT); SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0); SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH, SSB_SPROM8_TRI5GH_SHIFT); SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0); SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G, SSB_SPROM8_RXPO5G_SHIFT); SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0); SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G, SSB_SPROM8_RSSISMC2G_SHIFT); SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G, SSB_SPROM8_RSSISAV2G_SHIFT); SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G, SSB_SPROM8_BXA2G_SHIFT); SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0); SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G, SSB_SPROM8_RSSISMC5G_SHIFT); SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G, SSB_SPROM8_RSSISAV5G_SHIFT); SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G, SSB_SPROM8_BXA5G_SHIFT); SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0); SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0); SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0); SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0); SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0); SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0); SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0); SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0); SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0); SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0); SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0); SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0); SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0); SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0); SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0); SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0); SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0); /* Extract the antenna gain values. */ SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01, SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT); SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01, SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT); SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23, SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT); SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23, SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT); /* Extract cores power info info */ for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { o = pwr_info_offset[i]; SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI, SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT); SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI, SSB_SPROM8_2G_MAXP, 0); SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0); SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI, SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT); SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI, SSB_SPROM8_5G_MAXP, 0); SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP, SSB_SPROM8_5GH_MAXP, 0); SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP, SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT); SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0); SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0); SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0); } /* Extract FEM info */ SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT); SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT); SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT); SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT); SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT); SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT); SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT); SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT); SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT); SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT); SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON, SSB_SPROM8_LEDDC_ON_SHIFT); SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF, SSB_SPROM8_LEDDC_OFF_SHIFT); SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN, SSB_SPROM8_TXRXC_TXCHAIN_SHIFT); SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN, SSB_SPROM8_TXRXC_RXCHAIN_SHIFT); SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH, SSB_SPROM8_TXRXC_SWITCH_SHIFT); SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0); SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0); SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0); SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0); SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0); SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP, SSB_SPROM8_RAWTS_RAWTEMP_SHIFT); SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER, SSB_SPROM8_RAWTS_MEASPOWER_SHIFT); SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMP_SLOPE, SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT); SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT); SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMP_OPTION, SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT); SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR, SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT); SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP, SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT); SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT); SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0); SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0); SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0); SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0); SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH, SSB_SPROM8_THERMAL_TRESH_SHIFT); SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET, SSB_SPROM8_THERMAL_OFFSET_SHIFT); SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PHYCAL, SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT); SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD, SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT); SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_HYSTERESIS, SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT); sprom_extract_r458(out, in); /* TODO - get remaining rev 8 stuff needed */ } static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, const u16 *in, u16 size) { memset(out, 0, sizeof(*out)); out->revision = in[size - 1] & 0x00FF; ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision); memset(out->et0mac, 0xFF, 6); /* preset et0 and et1 mac */ memset(out->et1mac, 0xFF, 6); if ((bus->chip_id & 0xFF00) == 0x4400) { /* Workaround: The BCM44XX chip has a stupid revision * number stored in the SPROM. * Always extract r1. */ out->revision = 1; ssb_dprintk(KERN_DEBUG PFX "SPROM treated as revision %d\n", out->revision); } switch (out->revision) { case 1: case 2: case 3: sprom_extract_r123(out, in); break; case 4: case 5: sprom_extract_r45(out, in); break; case 8: sprom_extract_r8(out, in); break; default: ssb_printk(KERN_WARNING PFX "Unsupported SPROM" " revision %d detected. Will extract" " v1\n", out->revision); out->revision = 1; sprom_extract_r123(out, in); } if (out->boardflags_lo == 0xFFFF) out->boardflags_lo = 0; /* per specs */ if (out->boardflags_hi == 0xFFFF) out->boardflags_hi = 0; /* per specs */ return 0; } static int ssb_pci_sprom_get(struct ssb_bus *bus, struct ssb_sprom *sprom) { int err; u16 *buf; if (!ssb_is_sprom_available(bus)) { ssb_printk(KERN_ERR PFX "No SPROM available!\n"); return -ENODEV; } if (bus->chipco.dev) { /* can be unavailable! */ /* * get SPROM offset: SSB_SPROM_BASE1 except for * chipcommon rev >= 31 or chip ID is 0x4312 and * chipcommon status & 3 == 2 */ if (bus->chipco.dev->id.revision >= 31) bus->sprom_offset = SSB_SPROM_BASE31; else if (bus->chip_id == 0x4312 && (bus->chipco.status & 0x03) == 2) bus->sprom_offset = SSB_SPROM_BASE31; else bus->sprom_offset = SSB_SPROM_BASE1; } else { bus->sprom_offset = SSB_SPROM_BASE1; } ssb_dprintk(KERN_INFO PFX "SPROM offset is 0x%x\n", bus->sprom_offset); buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL); if (!buf) return -ENOMEM; bus->sprom_size = SSB_SPROMSIZE_WORDS_R123; sprom_do_read(bus, buf); err = sprom_check_crc(buf, bus->sprom_size); if (err) { /* try for a 440 byte SPROM - revision 4 and higher */ kfree(buf); buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), GFP_KERNEL); if (!buf) return -ENOMEM; bus->sprom_size = SSB_SPROMSIZE_WORDS_R4; sprom_do_read(bus, buf); err = sprom_check_crc(buf, bus->sprom_size); if (err) { /* All CRC attempts failed. * Maybe there is no SPROM on the device? * Now we ask the arch code if there is some sprom * available for this device in some other storage */ err = ssb_fill_sprom_with_fallback(bus, sprom); if (err) { ssb_printk(KERN_WARNING PFX "WARNING: Using" " fallback SPROM failed (err %d)\n", err); } else { ssb_dprintk(KERN_DEBUG PFX "Using SPROM" " revision %d provided by" " platform.\n", sprom->revision); err = 0; goto out_free; } ssb_printk(KERN_WARNING PFX "WARNING: Invalid" " SPROM CRC (corrupt SPROM)\n"); } } err = sprom_extract(bus, sprom, buf, bus->sprom_size); out_free: kfree(buf); return err; } static void ssb_pci_get_boardinfo(struct ssb_bus *bus, struct ssb_boardinfo *bi) { bi->vendor = bus->host_pci->subsystem_vendor; bi->type = bus->host_pci->subsystem_device; } int ssb_pci_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv) { int err; err = ssb_pci_sprom_get(bus, &iv->sprom); if (err) goto out; ssb_pci_get_boardinfo(bus, &iv->boardinfo); out: return err; } #ifdef CONFIG_SSB_DEBUG static int ssb_pci_assert_buspower(struct ssb_bus *bus) { if (likely(bus->powered_up)) return 0; printk(KERN_ERR PFX "FATAL ERROR: Bus powered down " "while accessing PCI MMIO space\n"); if (bus->power_warn_count <= 10) { bus->power_warn_count++; dump_stack(); } return -ENODEV; } #else /* DEBUG */ static inline int ssb_pci_assert_buspower(struct ssb_bus *bus) { return 0; } #endif /* DEBUG */ static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; if (unlikely(ssb_pci_assert_buspower(bus))) return 0xFF; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return 0xFF; } return ioread8(bus->mmio + offset); } static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; if (unlikely(ssb_pci_assert_buspower(bus))) return 0xFFFF; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return 0xFFFF; } return ioread16(bus->mmio + offset); } static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; if (unlikely(ssb_pci_assert_buspower(bus))) return 0xFFFFFFFF; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return 0xFFFFFFFF; } return ioread32(bus->mmio + offset); } #ifdef CONFIG_SSB_BLOCKIO static void ssb_pci_block_read(struct ssb_device *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { struct ssb_bus *bus = dev->bus; void __iomem *addr = bus->mmio + offset; if (unlikely(ssb_pci_assert_buspower(bus))) goto error; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) goto error; } switch (reg_width) { case sizeof(u8): ioread8_rep(addr, buffer, count); break; case sizeof(u16): SSB_WARN_ON(count & 1); ioread16_rep(addr, buffer, count >> 1); break; case sizeof(u32): SSB_WARN_ON(count & 3); ioread32_rep(addr, buffer, count >> 2); break; default: SSB_WARN_ON(1); } return; error: memset(buffer, 0xFF, count); } #endif /* CONFIG_SSB_BLOCKIO */ static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value) { struct ssb_bus *bus = dev->bus; if (unlikely(ssb_pci_assert_buspower(bus))) return; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return; } iowrite8(value, bus->mmio + offset); } static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value) { struct ssb_bus *bus = dev->bus; if (unlikely(ssb_pci_assert_buspower(bus))) return; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return; } iowrite16(value, bus->mmio + offset); } static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value) { struct ssb_bus *bus = dev->bus; if (unlikely(ssb_pci_assert_buspower(bus))) return; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return; } iowrite32(value, bus->mmio + offset); } #ifdef CONFIG_SSB_BLOCKIO static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { struct ssb_bus *bus = dev->bus; void __iomem *addr = bus->mmio + offset; if (unlikely(ssb_pci_assert_buspower(bus))) return; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return; } switch (reg_width) { case sizeof(u8): iowrite8_rep(addr, buffer, count); break; case sizeof(u16): SSB_WARN_ON(count & 1); iowrite16_rep(addr, buffer, count >> 1); break; case sizeof(u32): SSB_WARN_ON(count & 3); iowrite32_rep(addr, buffer, count >> 2); break; default: SSB_WARN_ON(1); } } #endif /* CONFIG_SSB_BLOCKIO */ /* Not "static", as it's used in main.c */ const struct ssb_bus_ops ssb_pci_ops = { .read8 = ssb_pci_read8, .read16 = ssb_pci_read16, .read32 = ssb_pci_read32, .write8 = ssb_pci_write8, .write16 = ssb_pci_write16, .write32 = ssb_pci_write32, #ifdef CONFIG_SSB_BLOCKIO .block_read = ssb_pci_block_read, .block_write = ssb_pci_block_write, #endif }; static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, struct device_attribute *attr, char *buf) { struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); struct ssb_bus *bus; bus = ssb_pci_dev_to_bus(pdev); if (!bus) return -ENODEV; return ssb_attr_sprom_show(bus, buf, sprom_do_read); } static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, struct device_attribute *attr, const char *buf, size_t count) { struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); struct ssb_bus *bus; bus = ssb_pci_dev_to_bus(pdev); if (!bus) return -ENODEV; return ssb_attr_sprom_store(bus, buf, count, sprom_check_crc, sprom_do_write); } static DEVICE_ATTR(ssb_sprom, 0600, ssb_pci_attr_sprom_show, ssb_pci_attr_sprom_store); void ssb_pci_exit(struct ssb_bus *bus) { struct pci_dev *pdev; if (bus->bustype != SSB_BUSTYPE_PCI) return; pdev = bus->host_pci; device_remove_file(&pdev->dev, &dev_attr_ssb_sprom); } int ssb_pci_init(struct ssb_bus *bus) { struct pci_dev *pdev; int err; if (bus->bustype != SSB_BUSTYPE_PCI) return 0; pdev = bus->host_pci; mutex_init(&bus->sprom_mutex); err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); if (err) goto out; out: return err; } compat-drivers-2012-09-18/drivers/ssb/embedded.c0000644000175000017500000001242012026211315020521 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Embedded systems support code * * Copyright 2005-2008, Broadcom Corporation * Copyright 2006-2008, Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #include #include "ssb_private.h" int ssb_watchdog_timer_set(struct ssb_bus *bus, u32 ticks) { if (ssb_chipco_available(&bus->chipco)) { ssb_chipco_watchdog_timer_set(&bus->chipco, ticks); return 0; } if (ssb_extif_available(&bus->extif)) { ssb_extif_watchdog_timer_set(&bus->extif, ticks); return 0; } return -ENODEV; } EXPORT_SYMBOL(ssb_watchdog_timer_set); u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask) { unsigned long flags; u32 res = 0; spin_lock_irqsave(&bus->gpio_lock, flags); if (ssb_chipco_available(&bus->chipco)) res = ssb_chipco_gpio_in(&bus->chipco, mask); else if (ssb_extif_available(&bus->extif)) res = ssb_extif_gpio_in(&bus->extif, mask); else SSB_WARN_ON(1); spin_unlock_irqrestore(&bus->gpio_lock, flags); return res; } EXPORT_SYMBOL(ssb_gpio_in); u32 ssb_gpio_out(struct ssb_bus *bus, u32 mask, u32 value) { unsigned long flags; u32 res = 0; spin_lock_irqsave(&bus->gpio_lock, flags); if (ssb_chipco_available(&bus->chipco)) res = ssb_chipco_gpio_out(&bus->chipco, mask, value); else if (ssb_extif_available(&bus->extif)) res = ssb_extif_gpio_out(&bus->extif, mask, value); else SSB_WARN_ON(1); spin_unlock_irqrestore(&bus->gpio_lock, flags); return res; } EXPORT_SYMBOL(ssb_gpio_out); u32 ssb_gpio_outen(struct ssb_bus *bus, u32 mask, u32 value) { unsigned long flags; u32 res = 0; spin_lock_irqsave(&bus->gpio_lock, flags); if (ssb_chipco_available(&bus->chipco)) res = ssb_chipco_gpio_outen(&bus->chipco, mask, value); else if (ssb_extif_available(&bus->extif)) res = ssb_extif_gpio_outen(&bus->extif, mask, value); else SSB_WARN_ON(1); spin_unlock_irqrestore(&bus->gpio_lock, flags); return res; } EXPORT_SYMBOL(ssb_gpio_outen); u32 ssb_gpio_control(struct ssb_bus *bus, u32 mask, u32 value) { unsigned long flags; u32 res = 0; spin_lock_irqsave(&bus->gpio_lock, flags); if (ssb_chipco_available(&bus->chipco)) res = ssb_chipco_gpio_control(&bus->chipco, mask, value); spin_unlock_irqrestore(&bus->gpio_lock, flags); return res; } EXPORT_SYMBOL(ssb_gpio_control); u32 ssb_gpio_intmask(struct ssb_bus *bus, u32 mask, u32 value) { unsigned long flags; u32 res = 0; spin_lock_irqsave(&bus->gpio_lock, flags); if (ssb_chipco_available(&bus->chipco)) res = ssb_chipco_gpio_intmask(&bus->chipco, mask, value); else if (ssb_extif_available(&bus->extif)) res = ssb_extif_gpio_intmask(&bus->extif, mask, value); else SSB_WARN_ON(1); spin_unlock_irqrestore(&bus->gpio_lock, flags); return res; } EXPORT_SYMBOL(ssb_gpio_intmask); u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value) { unsigned long flags; u32 res = 0; spin_lock_irqsave(&bus->gpio_lock, flags); if (ssb_chipco_available(&bus->chipco)) res = ssb_chipco_gpio_polarity(&bus->chipco, mask, value); else if (ssb_extif_available(&bus->extif)) res = ssb_extif_gpio_polarity(&bus->extif, mask, value); else SSB_WARN_ON(1); spin_unlock_irqrestore(&bus->gpio_lock, flags); return res; } EXPORT_SYMBOL(ssb_gpio_polarity); #ifdef CONFIG_SSB_DRIVER_GIGE static int gige_pci_init_callback(struct ssb_bus *bus, unsigned long data) { struct pci_dev *pdev = (struct pci_dev *)data; struct ssb_device *dev; unsigned int i; int res; for (i = 0; i < bus->nr_devices; i++) { dev = &(bus->devices[i]); if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT) continue; if (!dev->dev || !dev->dev->driver || !device_is_registered(dev->dev)) continue; res = ssb_gige_pcibios_plat_dev_init(dev, pdev); if (res >= 0) return res; } return -ENODEV; } #endif /* CONFIG_SSB_DRIVER_GIGE */ int ssb_pcibios_plat_dev_init(struct pci_dev *dev) { int err; err = ssb_pcicore_plat_dev_init(dev); if (!err) return 0; #ifdef CONFIG_SSB_DRIVER_GIGE err = ssb_for_each_bus_call((unsigned long)dev, gige_pci_init_callback); if (err >= 0) return err; #endif /* This is not a PCI device on any SSB device. */ return -ENODEV; } #ifdef CONFIG_SSB_DRIVER_GIGE static int gige_map_irq_callback(struct ssb_bus *bus, unsigned long data) { const struct pci_dev *pdev = (const struct pci_dev *)data; struct ssb_device *dev; unsigned int i; int res; for (i = 0; i < bus->nr_devices; i++) { dev = &(bus->devices[i]); if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT) continue; if (!dev->dev || !dev->dev->driver || !device_is_registered(dev->dev)) continue; res = ssb_gige_map_irq(dev, pdev); if (res >= 0) return res; } return -ENODEV; } #endif /* CONFIG_SSB_DRIVER_GIGE */ int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { int res; /* Check if this PCI device is a device on a SSB bus or device * and return the IRQ number for it. */ res = ssb_pcicore_pcibios_map_irq(dev, slot, pin); if (res >= 0) return res; #ifdef CONFIG_SSB_DRIVER_GIGE res = ssb_for_each_bus_call((unsigned long)dev, gige_map_irq_callback); if (res >= 0) return res; #endif /* This is not a PCI device on any SSB device. */ return -ENODEV; } compat-drivers-2012-09-18/drivers/ssb/driver_pcicore.c0000644000175000017500000004474412026211315022005 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Broadcom PCI-core driver * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #include "ssb_private.h" static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address); static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data); static u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address); static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device, u8 address, u16 data); static inline u32 pcicore_read32(struct ssb_pcicore *pc, u16 offset) { return ssb_read32(pc->dev, offset); } static inline void pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value) { ssb_write32(pc->dev, offset, value); } static inline u16 pcicore_read16(struct ssb_pcicore *pc, u16 offset) { return ssb_read16(pc->dev, offset); } static inline void pcicore_write16(struct ssb_pcicore *pc, u16 offset, u16 value) { ssb_write16(pc->dev, offset, value); } /************************************************** * Code for hostmode operation. **************************************************/ #ifdef CONFIG_SSB_PCICORE_HOSTMODE #include /* Probe a 32bit value on the bus and catch bus exceptions. * Returns nonzero on a bus exception. * This is MIPS specific */ #define mips_busprobe32(val, addr) get_dbe((val), ((u32 *)(addr))) /* Assume one-hot slot wiring */ #define SSB_PCI_SLOT_MAX 16 /* Global lock is OK, as we won't have more than one extpci anyway. */ static DEFINE_SPINLOCK(cfgspace_lock); /* Core to access the external PCI config space. Can only have one. */ static struct ssb_pcicore *extpci_core; static u32 get_cfgspace_addr(struct ssb_pcicore *pc, unsigned int bus, unsigned int dev, unsigned int func, unsigned int off) { u32 addr = 0; u32 tmp; /* We do only have one cardbus device behind the bridge. */ if (pc->cardbusmode && (dev > 1)) goto out; if (bus == 0) { /* Type 0 transaction */ if (unlikely(dev >= SSB_PCI_SLOT_MAX)) goto out; /* Slide the window */ tmp = SSB_PCICORE_SBTOPCI_CFG0; tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK); pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp); /* Calculate the address */ addr = SSB_PCI_CFG; addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK); addr |= (func << 8); addr |= (off & ~3); } else { /* Type 1 transaction */ pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, SSB_PCICORE_SBTOPCI_CFG1); /* Calculate the address */ addr = SSB_PCI_CFG; addr |= (bus << 16); addr |= (dev << 11); addr |= (func << 8); addr |= (off & ~3); } out: return addr; } static int ssb_extpci_read_config(struct ssb_pcicore *pc, unsigned int bus, unsigned int dev, unsigned int func, unsigned int off, void *buf, int len) { int err = -EINVAL; u32 addr, val; void __iomem *mmio; SSB_WARN_ON(!pc->hostmode); if (unlikely(len != 1 && len != 2 && len != 4)) goto out; addr = get_cfgspace_addr(pc, bus, dev, func, off); if (unlikely(!addr)) goto out; err = -ENOMEM; mmio = ioremap_nocache(addr, len); if (!mmio) goto out; if (mips_busprobe32(val, mmio)) { val = 0xffffffff; goto unmap; } val = readl(mmio); val >>= (8 * (off & 3)); switch (len) { case 1: *((u8 *)buf) = (u8)val; break; case 2: *((u16 *)buf) = (u16)val; break; case 4: *((u32 *)buf) = (u32)val; break; } err = 0; unmap: iounmap(mmio); out: return err; } static int ssb_extpci_write_config(struct ssb_pcicore *pc, unsigned int bus, unsigned int dev, unsigned int func, unsigned int off, const void *buf, int len) { int err = -EINVAL; u32 addr, val = 0; void __iomem *mmio; SSB_WARN_ON(!pc->hostmode); if (unlikely(len != 1 && len != 2 && len != 4)) goto out; addr = get_cfgspace_addr(pc, bus, dev, func, off); if (unlikely(!addr)) goto out; err = -ENOMEM; mmio = ioremap_nocache(addr, len); if (!mmio) goto out; if (mips_busprobe32(val, mmio)) { val = 0xffffffff; goto unmap; } switch (len) { case 1: val = readl(mmio); val &= ~(0xFF << (8 * (off & 3))); val |= *((const u8 *)buf) << (8 * (off & 3)); break; case 2: val = readl(mmio); val &= ~(0xFFFF << (8 * (off & 3))); val |= *((const u16 *)buf) << (8 * (off & 3)); break; case 4: val = *((const u32 *)buf); break; } writel(val, mmio); err = 0; unmap: iounmap(mmio); out: return err; } static int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn, int reg, int size, u32 *val) { unsigned long flags; int err; spin_lock_irqsave(&cfgspace_lock, flags); err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), reg, val, size); spin_unlock_irqrestore(&cfgspace_lock, flags); return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; } static int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn, int reg, int size, u32 val) { unsigned long flags; int err; spin_lock_irqsave(&cfgspace_lock, flags); err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), reg, &val, size); spin_unlock_irqrestore(&cfgspace_lock, flags); return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; } static struct pci_ops ssb_pcicore_pciops = { .read = ssb_pcicore_read_config, .write = ssb_pcicore_write_config, }; static struct resource ssb_pcicore_mem_resource = { .name = "SSB PCIcore external memory", .start = SSB_PCI_DMA, .end = SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1, .flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED, }; static struct resource ssb_pcicore_io_resource = { .name = "SSB PCIcore external I/O", .start = 0x100, .end = 0x7FF, .flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED, }; static struct pci_controller ssb_pcicore_controller = { .pci_ops = &ssb_pcicore_pciops, .io_resource = &ssb_pcicore_io_resource, .mem_resource = &ssb_pcicore_mem_resource, }; /* This function is called when doing a pci_enable_device(). * We must first check if the device is a device on the PCI-core bridge. */ int ssb_pcicore_plat_dev_init(struct pci_dev *d) { if (d->bus->ops != &ssb_pcicore_pciops) { /* This is not a device on the PCI-core bridge. */ return -ENODEV; } ssb_printk(KERN_INFO "PCI: Fixing up device %s\n", pci_name(d)); /* Fix up interrupt lines */ d->irq = ssb_mips_irq(extpci_core->dev) + 2; pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); return 0; } /* Early PCI fixup for a device on the PCI-core bridge. */ static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev) { u8 lat; if (dev->bus->ops != &ssb_pcicore_pciops) { /* This is not a device on the PCI-core bridge. */ return; } if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0) return; ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev)); /* Enable PCI bridge bus mastering and memory space */ pci_set_master(dev); if (pcibios_enable_device(dev, ~0) < 0) { ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n"); return; } /* Enable PCI bridge BAR1 prefetch and burst */ pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3); /* Make sure our latency is high enough to handle the devices behind us */ lat = 168; ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n", pci_name(dev), lat); pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); } DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge); /* PCI device IRQ mapping. */ int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { if (dev->bus->ops != &ssb_pcicore_pciops) { /* This is not a device on the PCI-core bridge. */ return -ENODEV; } return ssb_mips_irq(extpci_core->dev) + 2; } static void __devinit ssb_pcicore_init_hostmode(struct ssb_pcicore *pc) { u32 val; if (WARN_ON(extpci_core)) return; extpci_core = pc; ssb_dprintk(KERN_INFO PFX "PCIcore in host mode found\n"); /* Reset devices on the external PCI bus */ val = SSB_PCICORE_CTL_RST_OE; val |= SSB_PCICORE_CTL_CLK_OE; pcicore_write32(pc, SSB_PCICORE_CTL, val); val |= SSB_PCICORE_CTL_CLK; /* Clock on */ pcicore_write32(pc, SSB_PCICORE_CTL, val); udelay(150); /* Assertion time demanded by the PCI standard */ val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */ pcicore_write32(pc, SSB_PCICORE_CTL, val); val = SSB_PCICORE_ARBCTL_INTERN; pcicore_write32(pc, SSB_PCICORE_ARBCTL, val); udelay(1); /* Assertion time demanded by the PCI standard */ if (pc->dev->bus->has_cardbus_slot) { ssb_dprintk(KERN_INFO PFX "CardBus slot detected\n"); pc->cardbusmode = 1; /* GPIO 1 resets the bridge */ ssb_gpio_out(pc->dev->bus, 1, 1); ssb_gpio_outen(pc->dev->bus, 1, 1); pcicore_write16(pc, SSB_PCICORE_SPROM(0), pcicore_read16(pc, SSB_PCICORE_SPROM(0)) | 0x0400); } /* 64MB I/O window */ pcicore_write32(pc, SSB_PCICORE_SBTOPCI0, SSB_PCICORE_SBTOPCI_IO); /* 64MB config space */ pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, SSB_PCICORE_SBTOPCI_CFG0); /* 1GB memory window */ pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA); /* Enable PCI bridge BAR0 prefetch and burst */ val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2); /* Clear error conditions */ val = 0; ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2); /* Enable PCI interrupts */ pcicore_write32(pc, SSB_PCICORE_IMASK, SSB_PCICORE_IMASK_INTA); /* Ok, ready to run, register it to the system. * The following needs change, if we want to port hostmode * to non-MIPS platform. */ ssb_pcicore_controller.io_map_base = (unsigned long)ioremap_nocache(SSB_PCI_MEM, 0x04000000); set_io_port_base(ssb_pcicore_controller.io_map_base); /* Give some time to the PCI controller to configure itself with the new * values. Not waiting at this point causes crashes of the machine. */ mdelay(10); register_pci_controller(&ssb_pcicore_controller); } static int __devinit pcicore_is_in_hostmode(struct ssb_pcicore *pc) { struct ssb_bus *bus = pc->dev->bus; u16 chipid_top; u32 tmp; chipid_top = (bus->chip_id & 0xFF00); if (chipid_top != 0x4700 && chipid_top != 0x5300) return 0; if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI) return 0; /* The 200-pin BCM4712 package does not bond out PCI. Even when * PCI is bonded out, some boards may leave the pins floating. */ if (bus->chip_id == 0x4712) { if (bus->chip_package == SSB_CHIPPACK_BCM4712S) return 0; if (bus->chip_package == SSB_CHIPPACK_BCM4712M) return 0; } if (bus->chip_id == 0x5350) return 0; return !mips_busprobe32(tmp, (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE))); } #endif /* CONFIG_SSB_PCICORE_HOSTMODE */ /************************************************** * Workarounds. **************************************************/ static void __devinit ssb_pcicore_fix_sprom_core_index(struct ssb_pcicore *pc) { u16 tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(0)); if (((tmp & 0xF000) >> 12) != pc->dev->core_index) { tmp &= ~0xF000; tmp |= (pc->dev->core_index << 12); pcicore_write16(pc, SSB_PCICORE_SPROM(0), tmp); } } static u8 ssb_pcicore_polarity_workaround(struct ssb_pcicore *pc) { return (ssb_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80; } static void ssb_pcicore_serdes_workaround(struct ssb_pcicore *pc) { const u8 serdes_pll_device = 0x1D; const u8 serdes_rx_device = 0x1F; u16 tmp; ssb_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */, ssb_pcicore_polarity_workaround(pc)); tmp = ssb_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */); if (tmp & 0x4000) ssb_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000); } static void ssb_pcicore_pci_setup_workarounds(struct ssb_pcicore *pc) { struct ssb_device *pdev = pc->dev; struct ssb_bus *bus = pdev->bus; u32 tmp; tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2); tmp |= SSB_PCICORE_SBTOPCI_PREF; tmp |= SSB_PCICORE_SBTOPCI_BURST; pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp); if (pdev->id.revision < 5) { tmp = ssb_read32(pdev, SSB_IMCFGLO); tmp &= ~SSB_IMCFGLO_SERTO; tmp |= 2; tmp &= ~SSB_IMCFGLO_REQTO; tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT; ssb_write32(pdev, SSB_IMCFGLO, tmp); ssb_commit_settings(bus); } else if (pdev->id.revision >= 11) { tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2); tmp |= SSB_PCICORE_SBTOPCI_MRM; pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp); } } static void ssb_pcicore_pcie_setup_workarounds(struct ssb_pcicore *pc) { u32 tmp; u8 rev = pc->dev->id.revision; if (rev == 0 || rev == 1) { /* TLP Workaround register. */ tmp = ssb_pcie_read(pc, 0x4); tmp |= 0x8; ssb_pcie_write(pc, 0x4, tmp); } if (rev == 1) { /* DLLP Link Control register. */ tmp = ssb_pcie_read(pc, 0x100); tmp |= 0x40; ssb_pcie_write(pc, 0x100, tmp); } if (rev == 0) { const u8 serdes_rx_device = 0x1F; ssb_pcie_mdio_write(pc, serdes_rx_device, 2 /* Timer */, 0x8128); ssb_pcie_mdio_write(pc, serdes_rx_device, 6 /* CDR */, 0x0100); ssb_pcie_mdio_write(pc, serdes_rx_device, 7 /* CDR BW */, 0x1466); } else if (rev == 3 || rev == 4 || rev == 5) { /* TODO: DLLP Power Management Threshold */ ssb_pcicore_serdes_workaround(pc); /* TODO: ASPM */ } else if (rev == 7) { /* TODO: No PLL down */ } if (rev >= 6) { /* Miscellaneous Configuration Fixup */ tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(5)); if (!(tmp & 0x8000)) pcicore_write16(pc, SSB_PCICORE_SPROM(5), tmp | 0x8000); } } /************************************************** * Generic and Clientmode operation code. **************************************************/ static void __devinit ssb_pcicore_init_clientmode(struct ssb_pcicore *pc) { struct ssb_device *pdev = pc->dev; struct ssb_bus *bus = pdev->bus; if (bus->bustype == SSB_BUSTYPE_PCI) ssb_pcicore_fix_sprom_core_index(pc); /* Disable PCI interrupts. */ ssb_write32(pdev, SSB_INTVEC, 0); /* Additional PCIe always once-executed workarounds */ if (pc->dev->id.coreid == SSB_DEV_PCIE) { ssb_pcicore_serdes_workaround(pc); /* TODO: ASPM */ /* TODO: Clock Request Update */ } } void __devinit ssb_pcicore_init(struct ssb_pcicore *pc) { struct ssb_device *dev = pc->dev; if (!dev) return; if (!ssb_device_is_enabled(dev)) ssb_device_enable(dev, 0); #ifdef CONFIG_SSB_PCICORE_HOSTMODE pc->hostmode = pcicore_is_in_hostmode(pc); if (pc->hostmode) ssb_pcicore_init_hostmode(pc); #endif /* CONFIG_SSB_PCICORE_HOSTMODE */ if (!pc->hostmode) ssb_pcicore_init_clientmode(pc); } static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address) { pcicore_write32(pc, 0x130, address); return pcicore_read32(pc, 0x134); } static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data) { pcicore_write32(pc, 0x130, address); pcicore_write32(pc, 0x134, data); } static void ssb_pcie_mdio_set_phy(struct ssb_pcicore *pc, u8 phy) { const u16 mdio_control = 0x128; const u16 mdio_data = 0x12C; u32 v; int i; v = (1 << 30); /* Start of Transaction */ v |= (1 << 28); /* Write Transaction */ v |= (1 << 17); /* Turnaround */ v |= (0x1F << 18); v |= (phy << 4); pcicore_write32(pc, mdio_data, v); udelay(10); for (i = 0; i < 200; i++) { v = pcicore_read32(pc, mdio_control); if (v & 0x100 /* Trans complete */) break; msleep(1); } } static u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address) { const u16 mdio_control = 0x128; const u16 mdio_data = 0x12C; int max_retries = 10; u16 ret = 0; u32 v; int i; v = 0x80; /* Enable Preamble Sequence */ v |= 0x2; /* MDIO Clock Divisor */ pcicore_write32(pc, mdio_control, v); if (pc->dev->id.revision >= 10) { max_retries = 200; ssb_pcie_mdio_set_phy(pc, device); } v = (1 << 30); /* Start of Transaction */ v |= (1 << 29); /* Read Transaction */ v |= (1 << 17); /* Turnaround */ if (pc->dev->id.revision < 10) v |= (u32)device << 22; v |= (u32)address << 18; pcicore_write32(pc, mdio_data, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { v = pcicore_read32(pc, mdio_control); if (v & 0x100 /* Trans complete */) { udelay(10); ret = pcicore_read32(pc, mdio_data); break; } msleep(1); } pcicore_write32(pc, mdio_control, 0); return ret; } static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device, u8 address, u16 data) { const u16 mdio_control = 0x128; const u16 mdio_data = 0x12C; int max_retries = 10; u32 v; int i; v = 0x80; /* Enable Preamble Sequence */ v |= 0x2; /* MDIO Clock Divisor */ pcicore_write32(pc, mdio_control, v); if (pc->dev->id.revision >= 10) { max_retries = 200; ssb_pcie_mdio_set_phy(pc, device); } v = (1 << 30); /* Start of Transaction */ v |= (1 << 28); /* Write Transaction */ v |= (1 << 17); /* Turnaround */ if (pc->dev->id.revision < 10) v |= (u32)device << 22; v |= (u32)address << 18; v |= data; pcicore_write32(pc, mdio_data, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { v = pcicore_read32(pc, mdio_control); if (v & 0x100 /* Trans complete */) break; msleep(1); } pcicore_write32(pc, mdio_control, 0); } int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, struct ssb_device *dev) { struct ssb_device *pdev = pc->dev; struct ssb_bus *bus; int err = 0; u32 tmp; if (dev->bus->bustype != SSB_BUSTYPE_PCI) { /* This SSB device is not on a PCI host-bus. So the IRQs are * not routed through the PCI core. * So we must not enable routing through the PCI core. */ goto out; } if (!pdev) goto out; bus = pdev->bus; might_sleep_if(pdev->id.coreid != SSB_DEV_PCI); /* Enable interrupts for this device. */ if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) { u32 coremask; /* Calculate the "coremask" for the device. */ coremask = (1 << dev->core_index); SSB_WARN_ON(bus->bustype != SSB_BUSTYPE_PCI); err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp); if (err) goto out; tmp |= coremask << 8; err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp); if (err) goto out; } else { u32 intvec; intvec = ssb_read32(pdev, SSB_INTVEC); tmp = ssb_read32(dev, SSB_TPSFLAG); tmp &= SSB_TPSFLAG_BPFLAG; intvec |= (1 << tmp); ssb_write32(pdev, SSB_INTVEC, intvec); } /* Setup PCIcore operation. */ if (pc->setup_done) goto out; if (pdev->id.coreid == SSB_DEV_PCI) { ssb_pcicore_pci_setup_workarounds(pc); } else { WARN_ON(pdev->id.coreid != SSB_DEV_PCIE); ssb_pcicore_pcie_setup_workarounds(pc); } pc->setup_done = 1; out: return err; } EXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable); compat-drivers-2012-09-18/drivers/ssb/driver_mipscore.c0000644000175000017500000001662712026211315022201 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Broadcom MIPS core driver * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #include "ssb_private.h" static inline u32 mips_read32(struct ssb_mipscore *mcore, u16 offset) { return ssb_read32(mcore->dev, offset); } static inline void mips_write32(struct ssb_mipscore *mcore, u16 offset, u32 value) { ssb_write32(mcore->dev, offset, value); } static const u32 ipsflag_irq_mask[] = { 0, SSB_IPSFLAG_IRQ1, SSB_IPSFLAG_IRQ2, SSB_IPSFLAG_IRQ3, SSB_IPSFLAG_IRQ4, }; static const u32 ipsflag_irq_shift[] = { 0, SSB_IPSFLAG_IRQ1_SHIFT, SSB_IPSFLAG_IRQ2_SHIFT, SSB_IPSFLAG_IRQ3_SHIFT, SSB_IPSFLAG_IRQ4_SHIFT, }; static inline u32 ssb_irqflag(struct ssb_device *dev) { u32 tpsflag = ssb_read32(dev, SSB_TPSFLAG); if (tpsflag) return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG; else /* not irq supported */ return 0x3f; } static struct ssb_device *find_device(struct ssb_device *rdev, int irqflag) { struct ssb_bus *bus = rdev->bus; int i; for (i = 0; i < bus->nr_devices; i++) { struct ssb_device *dev; dev = &(bus->devices[i]); if (ssb_irqflag(dev) == irqflag) return dev; } return NULL; } /* Get the MIPS IRQ assignment for a specified device. * If unassigned, 0 is returned. * If disabled, 5 is returned. * If not supported, 6 is returned. */ unsigned int ssb_mips_irq(struct ssb_device *dev) { struct ssb_bus *bus = dev->bus; struct ssb_device *mdev = bus->mipscore.dev; u32 irqflag; u32 ipsflag; u32 tmp; unsigned int irq; irqflag = ssb_irqflag(dev); if (irqflag == 0x3f) return 6; ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG); for (irq = 1; irq <= 4; irq++) { tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]); if (tmp == irqflag) break; } if (irq == 5) { if ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)) irq = 0; } return irq; } static void clear_irq(struct ssb_bus *bus, unsigned int irq) { struct ssb_device *dev = bus->mipscore.dev; /* Clear the IRQ in the MIPScore backplane registers */ if (irq == 0) { ssb_write32(dev, SSB_INTVEC, 0); } else { ssb_write32(dev, SSB_IPSFLAG, ssb_read32(dev, SSB_IPSFLAG) | ipsflag_irq_mask[irq]); } } static void set_irq(struct ssb_device *dev, unsigned int irq) { unsigned int oldirq = ssb_mips_irq(dev); struct ssb_bus *bus = dev->bus; struct ssb_device *mdev = bus->mipscore.dev; u32 irqflag = ssb_irqflag(dev); BUG_ON(oldirq == 6); dev->irq = irq + 2; /* clear the old irq */ if (oldirq == 0) ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))); else if (oldirq != 5) clear_irq(bus, oldirq); /* assign the new one */ if (irq == 0) { ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) | ssb_read32(mdev, SSB_INTVEC))); } else { u32 ipsflag = ssb_read32(mdev, SSB_IPSFLAG); if ((ipsflag & ipsflag_irq_mask[irq]) != ipsflag_irq_mask[irq]) { u32 oldipsflag = (ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]; struct ssb_device *olddev = find_device(dev, oldipsflag); if (olddev) set_irq(olddev, 0); } irqflag <<= ipsflag_irq_shift[irq]; irqflag |= (ipsflag & ~ipsflag_irq_mask[irq]); ssb_write32(mdev, SSB_IPSFLAG, irqflag); } ssb_dprintk(KERN_INFO PFX "set_irq: core 0x%04x, irq %d => %d\n", dev->id.coreid, oldirq+2, irq+2); } static void print_irq(struct ssb_device *dev, unsigned int irq) { int i; static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"}; ssb_dprintk(KERN_INFO PFX "core 0x%04x, irq :", dev->id.coreid); for (i = 0; i <= 6; i++) { ssb_dprintk(" %s%s", irq_name[i], i==irq?"*":" "); } ssb_dprintk("\n"); } static void dump_irq(struct ssb_bus *bus) { int i; for (i = 0; i < bus->nr_devices; i++) { struct ssb_device *dev; dev = &(bus->devices[i]); print_irq(dev, ssb_mips_irq(dev)); } } static void ssb_mips_serial_init(struct ssb_mipscore *mcore) { struct ssb_bus *bus = mcore->dev->bus; if (bus->extif.dev) mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports); else if (bus->chipco.dev) mcore->nr_serial_ports = ssb_chipco_serial_init(&bus->chipco, mcore->serial_ports); else mcore->nr_serial_ports = 0; } static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) { struct ssb_bus *bus = mcore->dev->bus; /* When there is no chipcommon on the bus there is 4MB flash */ if (!bus->chipco.dev) { mcore->flash_buswidth = 2; mcore->flash_window = SSB_FLASH1; mcore->flash_window_size = SSB_FLASH1_SZ; return; } /* There is ChipCommon, so use it to read info about flash */ switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { case SSB_CHIPCO_FLASHT_STSER: case SSB_CHIPCO_FLASHT_ATSER: pr_err("Serial flash not supported\n"); break; case SSB_CHIPCO_FLASHT_PARA: pr_debug("Found parallel flash\n"); mcore->flash_window = SSB_FLASH2; mcore->flash_window_size = SSB_FLASH2_SZ; if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) & SSB_CHIPCO_CFG_DS16) == 0) mcore->flash_buswidth = 1; else mcore->flash_buswidth = 2; break; } } u32 ssb_cpu_clock(struct ssb_mipscore *mcore) { struct ssb_bus *bus = mcore->dev->bus; u32 pll_type, n, m, rate = 0; if (bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU) return ssb_pmu_get_cpu_clock(&bus->chipco); if (bus->extif.dev) { ssb_extif_get_clockcontrol(&bus->extif, &pll_type, &n, &m); } else if (bus->chipco.dev) { ssb_chipco_get_clockcpu(&bus->chipco, &pll_type, &n, &m); } else return 0; if ((pll_type == SSB_PLLTYPE_5) || (bus->chip_id == 0x5365)) { rate = 200000000; } else { rate = ssb_calc_clock_rate(pll_type, n, m); } if (pll_type == SSB_PLLTYPE_6) { rate *= 2; } return rate; } void ssb_mipscore_init(struct ssb_mipscore *mcore) { struct ssb_bus *bus; struct ssb_device *dev; unsigned long hz, ns; unsigned int irq, i; if (!mcore->dev) return; /* We don't have a MIPS core */ ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n"); bus = mcore->dev->bus; hz = ssb_clockspeed(bus); if (!hz) hz = 100000000; ns = 1000000000 / hz; if (bus->extif.dev) ssb_extif_timing_init(&bus->extif, ns); else if (bus->chipco.dev) ssb_chipco_timing_init(&bus->chipco, ns); /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */ for (irq = 2, i = 0; i < bus->nr_devices; i++) { int mips_irq; dev = &(bus->devices[i]); mips_irq = ssb_mips_irq(dev); if (mips_irq > 4) dev->irq = 0; else dev->irq = mips_irq + 2; if (dev->irq > 5) continue; switch (dev->id.coreid) { case SSB_DEV_USB11_HOST: /* shouldn't need a separate irq line for non-4710, most of them have a proper * external usb controller on the pci */ if ((bus->chip_id == 0x4710) && (irq <= 4)) { set_irq(dev, irq++); } break; case SSB_DEV_PCI: case SSB_DEV_ETHERNET: case SSB_DEV_ETHERNET_GBIT: case SSB_DEV_80211: case SSB_DEV_USB20_HOST: /* These devices get their own IRQ line if available, the rest goes on IRQ0 */ if (irq <= 4) { set_irq(dev, irq++); break; } /* fallthrough */ case SSB_DEV_EXTIF: set_irq(dev, 0); break; } } ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n"); dump_irq(bus); ssb_mips_serial_init(mcore); ssb_mips_flash_detect(mcore); } compat-drivers-2012-09-18/drivers/ssb/driver_gige.c0000644000175000017500000001655112026211315021267 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Broadcom Gigabit Ethernet core driver * * Copyright 2008, Broadcom Corporation * Copyright 2008, Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #include /* MODULE_DESCRIPTION("SSB Broadcom Gigabit Ethernet driver"); MODULE_AUTHOR("Michael Buesch"); MODULE_LICENSE("GPL"); */ static const struct ssb_device_id ssb_gige_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV), SSB_DEVTABLE_END }; /* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */ static inline u8 gige_read8(struct ssb_gige *dev, u16 offset) { return ssb_read8(dev->dev, offset); } static inline u16 gige_read16(struct ssb_gige *dev, u16 offset) { return ssb_read16(dev->dev, offset); } static inline u32 gige_read32(struct ssb_gige *dev, u16 offset) { return ssb_read32(dev->dev, offset); } static inline void gige_write8(struct ssb_gige *dev, u16 offset, u8 value) { ssb_write8(dev->dev, offset, value); } static inline void gige_write16(struct ssb_gige *dev, u16 offset, u16 value) { ssb_write16(dev->dev, offset, value); } static inline void gige_write32(struct ssb_gige *dev, u16 offset, u32 value) { ssb_write32(dev->dev, offset, value); } static inline u8 gige_pcicfg_read8(struct ssb_gige *dev, unsigned int offset) { BUG_ON(offset >= 256); return gige_read8(dev, SSB_GIGE_PCICFG + offset); } static inline u16 gige_pcicfg_read16(struct ssb_gige *dev, unsigned int offset) { BUG_ON(offset >= 256); return gige_read16(dev, SSB_GIGE_PCICFG + offset); } static inline u32 gige_pcicfg_read32(struct ssb_gige *dev, unsigned int offset) { BUG_ON(offset >= 256); return gige_read32(dev, SSB_GIGE_PCICFG + offset); } static inline void gige_pcicfg_write8(struct ssb_gige *dev, unsigned int offset, u8 value) { BUG_ON(offset >= 256); gige_write8(dev, SSB_GIGE_PCICFG + offset, value); } static inline void gige_pcicfg_write16(struct ssb_gige *dev, unsigned int offset, u16 value) { BUG_ON(offset >= 256); gige_write16(dev, SSB_GIGE_PCICFG + offset, value); } static inline void gige_pcicfg_write32(struct ssb_gige *dev, unsigned int offset, u32 value) { BUG_ON(offset >= 256); gige_write32(dev, SSB_GIGE_PCICFG + offset, value); } static int __devinit ssb_gige_pci_read_config(struct pci_bus *bus, unsigned int devfn, int reg, int size, u32 *val) { struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops); unsigned long flags; if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0)) return PCIBIOS_DEVICE_NOT_FOUND; if (reg >= 256) return PCIBIOS_DEVICE_NOT_FOUND; spin_lock_irqsave(&dev->lock, flags); switch (size) { case 1: *val = gige_pcicfg_read8(dev, reg); break; case 2: *val = gige_pcicfg_read16(dev, reg); break; case 4: *val = gige_pcicfg_read32(dev, reg); break; default: WARN_ON(1); } spin_unlock_irqrestore(&dev->lock, flags); return PCIBIOS_SUCCESSFUL; } static int __devinit ssb_gige_pci_write_config(struct pci_bus *bus, unsigned int devfn, int reg, int size, u32 val) { struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops); unsigned long flags; if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0)) return PCIBIOS_DEVICE_NOT_FOUND; if (reg >= 256) return PCIBIOS_DEVICE_NOT_FOUND; spin_lock_irqsave(&dev->lock, flags); switch (size) { case 1: gige_pcicfg_write8(dev, reg, val); break; case 2: gige_pcicfg_write16(dev, reg, val); break; case 4: gige_pcicfg_write32(dev, reg, val); break; default: WARN_ON(1); } spin_unlock_irqrestore(&dev->lock, flags); return PCIBIOS_SUCCESSFUL; } static int __devinit ssb_gige_probe(struct ssb_device *sdev, const struct ssb_device_id *id) { struct ssb_gige *dev; u32 base, tmslow, tmshigh; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->dev = sdev; spin_lock_init(&dev->lock); dev->pci_controller.pci_ops = &dev->pci_ops; dev->pci_controller.io_resource = &dev->io_resource; dev->pci_controller.mem_resource = &dev->mem_resource; dev->pci_controller.io_map_base = 0x800; dev->pci_ops.read = ssb_gige_pci_read_config; dev->pci_ops.write = ssb_gige_pci_write_config; dev->io_resource.name = SSB_GIGE_IO_RES_NAME; dev->io_resource.start = 0x800; dev->io_resource.end = 0x8FF; dev->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED; if (!ssb_device_is_enabled(sdev)) ssb_device_enable(sdev, 0); /* Setup BAR0. This is a 64k MMIO region. */ base = ssb_admatch_base(ssb_read32(sdev, SSB_ADMATCH1)); gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_0, base); gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_1, 0); dev->mem_resource.name = SSB_GIGE_MEM_RES_NAME; dev->mem_resource.start = base; dev->mem_resource.end = base + 0x10000 - 1; dev->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; /* Enable the memory region. */ gige_pcicfg_write16(dev, PCI_COMMAND, gige_pcicfg_read16(dev, PCI_COMMAND) | PCI_COMMAND_MEMORY); /* Write flushing is controlled by the Flush Status Control register. * We want to flush every register write with a timeout and we want * to disable the IRQ mask while flushing to avoid concurrency. * Note that automatic write flushing does _not_ work from * an IRQ handler. The driver must flush manually by reading a register. */ gige_write32(dev, SSB_GIGE_SHIM_FLUSHSTAT, 0x00000068); /* Check if we have an RGMII or GMII PHY-bus. * On RGMII do not bypass the DLLs */ tmslow = ssb_read32(sdev, SSB_TMSLOW); tmshigh = ssb_read32(sdev, SSB_TMSHIGH); if (tmshigh & SSB_GIGE_TMSHIGH_RGMII) { tmslow &= ~SSB_GIGE_TMSLOW_TXBYPASS; tmslow &= ~SSB_GIGE_TMSLOW_RXBYPASS; dev->has_rgmii = 1; } else { tmslow |= SSB_GIGE_TMSLOW_TXBYPASS; tmslow |= SSB_GIGE_TMSLOW_RXBYPASS; dev->has_rgmii = 0; } tmslow |= SSB_GIGE_TMSLOW_DLLEN; ssb_write32(sdev, SSB_TMSLOW, tmslow); ssb_set_drvdata(sdev, dev); register_pci_controller(&dev->pci_controller); return 0; } bool pdev_is_ssb_gige_core(struct pci_dev *pdev) { if (!pdev->resource[0].name) return 0; return (strcmp(pdev->resource[0].name, SSB_GIGE_MEM_RES_NAME) == 0); } EXPORT_SYMBOL(pdev_is_ssb_gige_core); int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, struct pci_dev *pdev) { struct ssb_gige *dev = ssb_get_drvdata(sdev); struct resource *res; if (pdev->bus->ops != &dev->pci_ops) { /* The PCI device is not on this SSB GigE bridge device. */ return -ENODEV; } /* Fixup the PCI resources. */ res = &(pdev->resource[0]); res->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; res->name = dev->mem_resource.name; res->start = dev->mem_resource.start; res->end = dev->mem_resource.end; /* Fixup interrupt lines. */ pdev->irq = ssb_mips_irq(sdev) + 2; pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq); return 0; } int ssb_gige_map_irq(struct ssb_device *sdev, const struct pci_dev *pdev) { struct ssb_gige *dev = ssb_get_drvdata(sdev); if (pdev->bus->ops != &dev->pci_ops) { /* The PCI device is not on this SSB GigE bridge device. */ return -ENODEV; } return ssb_mips_irq(sdev) + 2; } static struct ssb_driver ssb_gige_driver = { .name = "BCM-GigE", .id_table = ssb_gige_tbl, .probe = ssb_gige_probe, }; int ssb_gige_init(void) { return ssb_driver_register(&ssb_gige_driver); } compat-drivers-2012-09-18/drivers/ssb/driver_extif.c0000644000175000017500000000736212026211315021473 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Broadcom EXTIF core driver * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * Copyright 2006, 2007, Felix Fietkau * Copyright 2007, Aurelien Jarno * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include "ssb_private.h" static inline u32 extif_read32(struct ssb_extif *extif, u16 offset) { return ssb_read32(extif->dev, offset); } static inline void extif_write32(struct ssb_extif *extif, u16 offset, u32 value) { ssb_write32(extif->dev, offset, value); } static inline u32 extif_write32_masked(struct ssb_extif *extif, u16 offset, u32 mask, u32 value) { value &= mask; value |= extif_read32(extif, offset) & ~mask; extif_write32(extif, offset, value); return value; } #ifdef CONFIG_SSB_SERIAL static bool serial_exists(u8 *regs) { u8 save_mcr, msr = 0; if (regs) { save_mcr = regs[UART_MCR]; regs[UART_MCR] = (UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS); msr = regs[UART_MSR] & (UART_MSR_DCD | UART_MSR_RI | UART_MSR_CTS | UART_MSR_DSR); regs[UART_MCR] = save_mcr; } return (msr == (UART_MSR_DCD | UART_MSR_CTS)); } int ssb_extif_serial_init(struct ssb_extif *extif, struct ssb_serial_port *ports) { u32 i, nr_ports = 0; /* Disable GPIO interrupt initially */ extif_write32(extif, SSB_EXTIF_GPIO_INTPOL, 0); extif_write32(extif, SSB_EXTIF_GPIO_INTMASK, 0); for (i = 0; i < 2; i++) { void __iomem *uart_regs; uart_regs = ioremap_nocache(SSB_EUART, 16); if (uart_regs) { uart_regs += (i * 8); if (serial_exists(uart_regs) && ports) { extif_write32(extif, SSB_EXTIF_GPIO_INTMASK, 2); nr_ports++; ports[i].regs = uart_regs; ports[i].irq = 2; ports[i].baud_base = 13500000; ports[i].reg_shift = 0; } iounmap(uart_regs); } } return nr_ports; } #endif /* CONFIG_SSB_SERIAL */ void ssb_extif_timing_init(struct ssb_extif *extif, unsigned long ns) { u32 tmp; /* Initialize extif so we can get to the LEDs and external UART */ extif_write32(extif, SSB_EXTIF_PROG_CFG, SSB_EXTCFG_EN); /* Set timing for the flash */ tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT; tmp |= DIV_ROUND_UP(120, ns); extif_write32(extif, SSB_EXTIF_PROG_WAITCNT, tmp); /* Set programmable interface timing for external uart */ tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT; tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT; tmp |= DIV_ROUND_UP(120, ns); extif_write32(extif, SSB_EXTIF_PROG_WAITCNT, tmp); } void ssb_extif_get_clockcontrol(struct ssb_extif *extif, u32 *pll_type, u32 *n, u32 *m) { *pll_type = SSB_PLLTYPE_1; *n = extif_read32(extif, SSB_EXTIF_CLOCK_N); *m = extif_read32(extif, SSB_EXTIF_CLOCK_SB); } void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks) { extif_write32(extif, SSB_EXTIF_WATCHDOG, ticks); } u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask) { return extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask; } u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value) { return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0), mask, value); } u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value) { return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0), mask, value); } u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, u32 value) { return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value); } u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, u32 value) { return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value); } compat-drivers-2012-09-18/drivers/ssb/driver_chipcommon_pmu.c0000644000175000017500000004761612026211315023377 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Broadcom ChipCommon Power Management Unit driver * * Copyright 2009, Michael Buesch * Copyright 2007, Broadcom Corporation * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include #ifdef CONFIG_BCM47XX #include #endif #include "ssb_private.h" static u32 ssb_chipco_pll_read(struct ssb_chipcommon *cc, u32 offset) { chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset); return chipco_read32(cc, SSB_CHIPCO_PLLCTL_DATA); } static void ssb_chipco_pll_write(struct ssb_chipcommon *cc, u32 offset, u32 value) { chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset); chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, value); } static void ssb_chipco_regctl_maskset(struct ssb_chipcommon *cc, u32 offset, u32 mask, u32 set) { u32 value; chipco_read32(cc, SSB_CHIPCO_REGCTL_ADDR); chipco_write32(cc, SSB_CHIPCO_REGCTL_ADDR, offset); chipco_read32(cc, SSB_CHIPCO_REGCTL_ADDR); value = chipco_read32(cc, SSB_CHIPCO_REGCTL_DATA); value &= mask; value |= set; chipco_write32(cc, SSB_CHIPCO_REGCTL_DATA, value); chipco_read32(cc, SSB_CHIPCO_REGCTL_DATA); } struct pmu0_plltab_entry { u16 freq; /* Crystal frequency in kHz.*/ u8 xf; /* Crystal frequency value for PMU control */ u8 wb_int; u32 wb_frac; }; static const struct pmu0_plltab_entry pmu0_plltab[] = { { .freq = 12000, .xf = 1, .wb_int = 73, .wb_frac = 349525, }, { .freq = 13000, .xf = 2, .wb_int = 67, .wb_frac = 725937, }, { .freq = 14400, .xf = 3, .wb_int = 61, .wb_frac = 116508, }, { .freq = 15360, .xf = 4, .wb_int = 57, .wb_frac = 305834, }, { .freq = 16200, .xf = 5, .wb_int = 54, .wb_frac = 336579, }, { .freq = 16800, .xf = 6, .wb_int = 52, .wb_frac = 399457, }, { .freq = 19200, .xf = 7, .wb_int = 45, .wb_frac = 873813, }, { .freq = 19800, .xf = 8, .wb_int = 44, .wb_frac = 466033, }, { .freq = 20000, .xf = 9, .wb_int = 44, .wb_frac = 0, }, { .freq = 25000, .xf = 10, .wb_int = 70, .wb_frac = 419430, }, { .freq = 26000, .xf = 11, .wb_int = 67, .wb_frac = 725937, }, { .freq = 30000, .xf = 12, .wb_int = 58, .wb_frac = 699050, }, { .freq = 38400, .xf = 13, .wb_int = 45, .wb_frac = 873813, }, { .freq = 40000, .xf = 14, .wb_int = 45, .wb_frac = 0, }, }; #define SSB_PMU0_DEFAULT_XTALFREQ 20000 static const struct pmu0_plltab_entry * pmu0_plltab_find_entry(u32 crystalfreq) { const struct pmu0_plltab_entry *e; unsigned int i; for (i = 0; i < ARRAY_SIZE(pmu0_plltab); i++) { e = &pmu0_plltab[i]; if (e->freq == crystalfreq) return e; } return NULL; } /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */ static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon *cc, u32 crystalfreq) { struct ssb_bus *bus = cc->dev->bus; const struct pmu0_plltab_entry *e = NULL; u32 pmuctl, tmp, pllctl; unsigned int i; if (crystalfreq) e = pmu0_plltab_find_entry(crystalfreq); if (!e) e = pmu0_plltab_find_entry(SSB_PMU0_DEFAULT_XTALFREQ); BUG_ON(!e); crystalfreq = e->freq; cc->pmu.crystalfreq = e->freq; /* Check if the PLL already is programmed to this frequency. */ pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) { /* We're already there... */ return; } ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n", (crystalfreq / 1000), (crystalfreq % 1000)); /* First turn the PLL off. */ switch (bus->chip_id) { case 0x4328: chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, ~(1 << SSB_PMURES_4328_BB_PLL_PU)); chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, ~(1 << SSB_PMURES_4328_BB_PLL_PU)); break; case 0x5354: chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, ~(1 << SSB_PMURES_5354_BB_PLL_PU)); chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, ~(1 << SSB_PMURES_5354_BB_PLL_PU)); break; default: SSB_WARN_ON(1); } for (i = 1500; i; i--) { tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)) break; udelay(10); } tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT) ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n"); /* Set PDIV in PLL control 0. */ pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL0); if (crystalfreq >= SSB_PMU0_PLLCTL0_PDIV_FREQ) pllctl |= SSB_PMU0_PLLCTL0_PDIV_MSK; else pllctl &= ~SSB_PMU0_PLLCTL0_PDIV_MSK; ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL0, pllctl); /* Set WILD in PLL control 1. */ pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL1); pllctl &= ~SSB_PMU0_PLLCTL1_STOPMOD; pllctl &= ~(SSB_PMU0_PLLCTL1_WILD_IMSK | SSB_PMU0_PLLCTL1_WILD_FMSK); pllctl |= ((u32)e->wb_int << SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_IMSK; pllctl |= ((u32)e->wb_frac << SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_FMSK; if (e->wb_frac == 0) pllctl |= SSB_PMU0_PLLCTL1_STOPMOD; ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL1, pllctl); /* Set WILD in PLL control 2. */ pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL2); pllctl &= ~SSB_PMU0_PLLCTL2_WILD_IMSKHI; pllctl |= (((u32)e->wb_int >> 4) << SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT) & SSB_PMU0_PLLCTL2_WILD_IMSKHI; ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL2, pllctl); /* Set the crystalfrequency and the divisor. */ pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); pmuctl &= ~SSB_CHIPCO_PMU_CTL_ILP_DIV; pmuctl |= (((crystalfreq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT) & SSB_CHIPCO_PMU_CTL_ILP_DIV; pmuctl &= ~SSB_CHIPCO_PMU_CTL_XTALFREQ; pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ; chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl); } struct pmu1_plltab_entry { u16 freq; /* Crystal frequency in kHz.*/ u8 xf; /* Crystal frequency value for PMU control */ u8 ndiv_int; u32 ndiv_frac; u8 p1div; u8 p2div; }; static const struct pmu1_plltab_entry pmu1_plltab[] = { { .freq = 12000, .xf = 1, .p1div = 3, .p2div = 22, .ndiv_int = 0x9, .ndiv_frac = 0xFFFFEF, }, { .freq = 13000, .xf = 2, .p1div = 1, .p2div = 6, .ndiv_int = 0xb, .ndiv_frac = 0x483483, }, { .freq = 14400, .xf = 3, .p1div = 1, .p2div = 10, .ndiv_int = 0xa, .ndiv_frac = 0x1C71C7, }, { .freq = 15360, .xf = 4, .p1div = 1, .p2div = 5, .ndiv_int = 0xb, .ndiv_frac = 0x755555, }, { .freq = 16200, .xf = 5, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x6E9E06, }, { .freq = 16800, .xf = 6, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x3CF3CF, }, { .freq = 19200, .xf = 7, .p1div = 1, .p2div = 9, .ndiv_int = 0x5, .ndiv_frac = 0x17B425, }, { .freq = 19800, .xf = 8, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0xA57EB, }, { .freq = 20000, .xf = 9, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0, }, { .freq = 24000, .xf = 10, .p1div = 3, .p2div = 11, .ndiv_int = 0xa, .ndiv_frac = 0, }, { .freq = 25000, .xf = 11, .p1div = 5, .p2div = 16, .ndiv_int = 0xb, .ndiv_frac = 0, }, { .freq = 26000, .xf = 12, .p1div = 1, .p2div = 2, .ndiv_int = 0x10, .ndiv_frac = 0xEC4EC4, }, { .freq = 30000, .xf = 13, .p1div = 3, .p2div = 8, .ndiv_int = 0xb, .ndiv_frac = 0, }, { .freq = 38400, .xf = 14, .p1div = 1, .p2div = 5, .ndiv_int = 0x4, .ndiv_frac = 0x955555, }, { .freq = 40000, .xf = 15, .p1div = 1, .p2div = 2, .ndiv_int = 0xb, .ndiv_frac = 0, }, }; #define SSB_PMU1_DEFAULT_XTALFREQ 15360 static const struct pmu1_plltab_entry * pmu1_plltab_find_entry(u32 crystalfreq) { const struct pmu1_plltab_entry *e; unsigned int i; for (i = 0; i < ARRAY_SIZE(pmu1_plltab); i++) { e = &pmu1_plltab[i]; if (e->freq == crystalfreq) return e; } return NULL; } /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */ static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon *cc, u32 crystalfreq) { struct ssb_bus *bus = cc->dev->bus; const struct pmu1_plltab_entry *e = NULL; u32 buffer_strength = 0; u32 tmp, pllctl, pmuctl; unsigned int i; if (bus->chip_id == 0x4312) { /* We do not touch the BCM4312 PLL and assume * the default crystal settings work out-of-the-box. */ cc->pmu.crystalfreq = 20000; return; } if (crystalfreq) e = pmu1_plltab_find_entry(crystalfreq); if (!e) e = pmu1_plltab_find_entry(SSB_PMU1_DEFAULT_XTALFREQ); BUG_ON(!e); crystalfreq = e->freq; cc->pmu.crystalfreq = e->freq; /* Check if the PLL already is programmed to this frequency. */ pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) { /* We're already there... */ return; } ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n", (crystalfreq / 1000), (crystalfreq % 1000)); /* First turn the PLL off. */ switch (bus->chip_id) { case 0x4325: chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) | (1 << SSB_PMURES_4325_HT_AVAIL))); chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) | (1 << SSB_PMURES_4325_HT_AVAIL))); /* Adjust the BBPLL to 2 on all channels later. */ buffer_strength = 0x222222; break; default: SSB_WARN_ON(1); } for (i = 1500; i; i--) { tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)) break; udelay(10); } tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT) ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n"); /* Set p1div and p2div. */ pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL0); pllctl &= ~(SSB_PMU1_PLLCTL0_P1DIV | SSB_PMU1_PLLCTL0_P2DIV); pllctl |= ((u32)e->p1div << SSB_PMU1_PLLCTL0_P1DIV_SHIFT) & SSB_PMU1_PLLCTL0_P1DIV; pllctl |= ((u32)e->p2div << SSB_PMU1_PLLCTL0_P2DIV_SHIFT) & SSB_PMU1_PLLCTL0_P2DIV; ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, pllctl); /* Set ndiv int and ndiv mode */ pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL2); pllctl &= ~(SSB_PMU1_PLLCTL2_NDIVINT | SSB_PMU1_PLLCTL2_NDIVMODE); pllctl |= ((u32)e->ndiv_int << SSB_PMU1_PLLCTL2_NDIVINT_SHIFT) & SSB_PMU1_PLLCTL2_NDIVINT; pllctl |= (1 << SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT) & SSB_PMU1_PLLCTL2_NDIVMODE; ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, pllctl); /* Set ndiv frac */ pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL3); pllctl &= ~SSB_PMU1_PLLCTL3_NDIVFRAC; pllctl |= ((u32)e->ndiv_frac << SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT) & SSB_PMU1_PLLCTL3_NDIVFRAC; ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, pllctl); /* Change the drive strength, if required. */ if (buffer_strength) { pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL5); pllctl &= ~SSB_PMU1_PLLCTL5_CLKDRV; pllctl |= (buffer_strength << SSB_PMU1_PLLCTL5_CLKDRV_SHIFT) & SSB_PMU1_PLLCTL5_CLKDRV; ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, pllctl); } /* Tune the crystalfreq and the divisor. */ pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); pmuctl &= ~(SSB_CHIPCO_PMU_CTL_ILP_DIV | SSB_CHIPCO_PMU_CTL_XTALFREQ); pmuctl |= ((((u32)e->freq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT) & SSB_CHIPCO_PMU_CTL_ILP_DIV; pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ; chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl); } static void ssb_pmu_pll_init(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; u32 crystalfreq = 0; /* in kHz. 0 = keep default freq. */ if (bus->bustype == SSB_BUSTYPE_SSB) { #ifdef CONFIG_BCM47XX char buf[20]; if (nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0) crystalfreq = simple_strtoul(buf, NULL, 0); #endif } switch (bus->chip_id) { case 0x4312: case 0x4325: ssb_pmu1_pllinit_r0(cc, crystalfreq); break; case 0x4328: ssb_pmu0_pllinit_r0(cc, crystalfreq); break; case 0x5354: if (crystalfreq == 0) crystalfreq = 25000; ssb_pmu0_pllinit_r0(cc, crystalfreq); break; case 0x4322: if (cc->pmu.rev == 2) { chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, 0x0000000A); chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, 0x380005C0); } break; default: ssb_printk(KERN_ERR PFX "ERROR: PLL init unknown for device %04X\n", bus->chip_id); } } struct pmu_res_updown_tab_entry { u8 resource; /* The resource number */ u16 updown; /* The updown value */ }; enum pmu_res_depend_tab_task { PMU_RES_DEP_SET = 1, PMU_RES_DEP_ADD, PMU_RES_DEP_REMOVE, }; struct pmu_res_depend_tab_entry { u8 resource; /* The resource number */ u8 task; /* SET | ADD | REMOVE */ u32 depend; /* The depend mask */ }; static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4328a0[] = { { .resource = SSB_PMURES_4328_EXT_SWITCHER_PWM, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_BB_SWITCHER_PWM, .updown = 0x1F01, }, { .resource = SSB_PMURES_4328_BB_SWITCHER_BURST, .updown = 0x010F, }, { .resource = SSB_PMURES_4328_BB_EXT_SWITCHER_BURST, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_ILP_REQUEST, .updown = 0x0202, }, { .resource = SSB_PMURES_4328_RADIO_SWITCHER_PWM, .updown = 0x0F01, }, { .resource = SSB_PMURES_4328_RADIO_SWITCHER_BURST, .updown = 0x0F01, }, { .resource = SSB_PMURES_4328_ROM_SWITCH, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_PA_REF_LDO, .updown = 0x0F01, }, { .resource = SSB_PMURES_4328_RADIO_LDO, .updown = 0x0F01, }, { .resource = SSB_PMURES_4328_AFE_LDO, .updown = 0x0F01, }, { .resource = SSB_PMURES_4328_PLL_LDO, .updown = 0x0F01, }, { .resource = SSB_PMURES_4328_BG_FILTBYP, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_TX_FILTBYP, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_RX_FILTBYP, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_XTAL_PU, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_XTAL_EN, .updown = 0xA001, }, { .resource = SSB_PMURES_4328_BB_PLL_FILTBYP, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_RF_PLL_FILTBYP, .updown = 0x0101, }, { .resource = SSB_PMURES_4328_BB_PLL_PU, .updown = 0x0701, }, }; static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4328a0[] = { { /* Adjust ILP Request to avoid forcing EXT/BB into burst mode. */ .resource = SSB_PMURES_4328_ILP_REQUEST, .task = PMU_RES_DEP_SET, .depend = ((1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) | (1 << SSB_PMURES_4328_BB_SWITCHER_PWM)), }, }; static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4325a0[] = { { .resource = SSB_PMURES_4325_XTAL_PU, .updown = 0x1501, }, }; static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4325a0[] = { { /* Adjust HT-Available dependencies. */ .resource = SSB_PMURES_4325_HT_AVAIL, .task = PMU_RES_DEP_ADD, .depend = ((1 << SSB_PMURES_4325_RX_PWRSW_PU) | (1 << SSB_PMURES_4325_TX_PWRSW_PU) | (1 << SSB_PMURES_4325_LOGEN_PWRSW_PU) | (1 << SSB_PMURES_4325_AFE_PWRSW_PU)), }, }; static void ssb_pmu_resources_init(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; u32 min_msk = 0, max_msk = 0; unsigned int i; const struct pmu_res_updown_tab_entry *updown_tab = NULL; unsigned int updown_tab_size = 0; const struct pmu_res_depend_tab_entry *depend_tab = NULL; unsigned int depend_tab_size = 0; switch (bus->chip_id) { case 0x4312: min_msk = 0xCBB; break; case 0x4322: /* We keep the default settings: * min_msk = 0xCBB * max_msk = 0x7FFFF */ break; case 0x4325: /* Power OTP down later. */ min_msk = (1 << SSB_PMURES_4325_CBUCK_BURST) | (1 << SSB_PMURES_4325_LNLDO2_PU); if (chipco_read32(cc, SSB_CHIPCO_CHIPSTAT) & SSB_CHIPCO_CHST_4325_PMUTOP_2B) min_msk |= (1 << SSB_PMURES_4325_CLDO_CBUCK_BURST); /* The PLL may turn on, if it decides so. */ max_msk = 0xFFFFF; updown_tab = pmu_res_updown_tab_4325a0; updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4325a0); depend_tab = pmu_res_depend_tab_4325a0; depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4325a0); break; case 0x4328: min_msk = (1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) | (1 << SSB_PMURES_4328_BB_SWITCHER_PWM) | (1 << SSB_PMURES_4328_XTAL_EN); /* The PLL may turn on, if it decides so. */ max_msk = 0xFFFFF; updown_tab = pmu_res_updown_tab_4328a0; updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4328a0); depend_tab = pmu_res_depend_tab_4328a0; depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4328a0); break; case 0x5354: /* The PLL may turn on, if it decides so. */ max_msk = 0xFFFFF; break; default: ssb_printk(KERN_ERR PFX "ERROR: PMU resource config unknown for device %04X\n", bus->chip_id); } if (updown_tab) { for (i = 0; i < updown_tab_size; i++) { chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL, updown_tab[i].resource); chipco_write32(cc, SSB_CHIPCO_PMU_RES_UPDNTM, updown_tab[i].updown); } } if (depend_tab) { for (i = 0; i < depend_tab_size; i++) { chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL, depend_tab[i].resource); switch (depend_tab[i].task) { case PMU_RES_DEP_SET: chipco_write32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, depend_tab[i].depend); break; case PMU_RES_DEP_ADD: chipco_set32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, depend_tab[i].depend); break; case PMU_RES_DEP_REMOVE: chipco_mask32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, ~(depend_tab[i].depend)); break; default: SSB_WARN_ON(1); } } } /* Set the resource masks. */ if (min_msk) chipco_write32(cc, SSB_CHIPCO_PMU_MINRES_MSK, min_msk); if (max_msk) chipco_write32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, max_msk); } /* http://bcm-v4.sipsolutions.net/802.11/SSB/PmuInit */ void ssb_pmu_init(struct ssb_chipcommon *cc) { u32 pmucap; if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU)) return; pmucap = chipco_read32(cc, SSB_CHIPCO_PMU_CAP); cc->pmu.rev = (pmucap & SSB_CHIPCO_PMU_CAP_REVISION); ssb_dprintk(KERN_DEBUG PFX "Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev, pmucap); if (cc->pmu.rev == 1) chipco_mask32(cc, SSB_CHIPCO_PMU_CTL, ~SSB_CHIPCO_PMU_CTL_NOILPONW); else chipco_set32(cc, SSB_CHIPCO_PMU_CTL, SSB_CHIPCO_PMU_CTL_NOILPONW); ssb_pmu_pll_init(cc); ssb_pmu_resources_init(cc); } void ssb_pmu_set_ldo_voltage(struct ssb_chipcommon *cc, enum ssb_pmu_ldo_volt_id id, u32 voltage) { struct ssb_bus *bus = cc->dev->bus; u32 addr, shift, mask; switch (bus->chip_id) { case 0x4328: case 0x5354: switch (id) { case LDO_VOLT1: addr = 2; shift = 25; mask = 0xF; break; case LDO_VOLT2: addr = 3; shift = 1; mask = 0xF; break; case LDO_VOLT3: addr = 3; shift = 9; mask = 0xF; break; case LDO_PAREF: addr = 3; shift = 17; mask = 0x3F; break; default: SSB_WARN_ON(1); return; } break; case 0x4312: if (SSB_WARN_ON(id != LDO_PAREF)) return; addr = 0; shift = 21; mask = 0x3F; break; default: return; } ssb_chipco_regctl_maskset(cc, addr, ~(mask << shift), (voltage & mask) << shift); } void ssb_pmu_set_ldo_paref(struct ssb_chipcommon *cc, bool on) { struct ssb_bus *bus = cc->dev->bus; int ldo; switch (bus->chip_id) { case 0x4312: ldo = SSB_PMURES_4312_PA_REF_LDO; break; case 0x4328: ldo = SSB_PMURES_4328_PA_REF_LDO; break; case 0x5354: ldo = SSB_PMURES_5354_PA_REF_LDO; break; default: return; } if (on) chipco_set32(cc, SSB_CHIPCO_PMU_MINRES_MSK, 1 << ldo); else chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, ~(1 << ldo)); chipco_read32(cc, SSB_CHIPCO_PMU_MINRES_MSK); //SPEC FIXME found via mmiotrace - dummy read? } EXPORT_SYMBOL(ssb_pmu_set_ldo_voltage); EXPORT_SYMBOL(ssb_pmu_set_ldo_paref); u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; switch (bus->chip_id) { case 0x5354: /* 5354 chip uses a non programmable PLL of frequency 240MHz */ return 240000000; default: ssb_printk(KERN_ERR PFX "ERROR: PMU cpu clock unknown for device %04X\n", bus->chip_id); return 0; } } u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; switch (bus->chip_id) { case 0x5354: return 120000000; default: ssb_printk(KERN_ERR PFX "ERROR: PMU controlclock unknown for device %04X\n", bus->chip_id); return 0; } } compat-drivers-2012-09-18/drivers/ssb/driver_chipcommon.c0000644000175000017500000003476412026211315022516 0ustar mcgrofmcgrof/* * Sonics Silicon Backplane * Broadcom ChipCommon core driver * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include #include "ssb_private.h" /* Clock sources */ enum ssb_clksrc { /* PCI clock */ SSB_CHIPCO_CLKSRC_PCI, /* Crystal slow clock oscillator */ SSB_CHIPCO_CLKSRC_XTALOS, /* Low power oscillator */ SSB_CHIPCO_CLKSRC_LOPWROS, }; static inline u32 chipco_write32_masked(struct ssb_chipcommon *cc, u16 offset, u32 mask, u32 value) { value &= mask; value |= chipco_read32(cc, offset) & ~mask; chipco_write32(cc, offset, value); return value; } void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc, enum ssb_clkmode mode) { struct ssb_device *ccdev = cc->dev; struct ssb_bus *bus; u32 tmp; if (!ccdev) return; bus = ccdev->bus; /* We support SLOW only on 6..9 */ if (ccdev->id.revision >= 10 && mode == SSB_CLKMODE_SLOW) mode = SSB_CLKMODE_DYNAMIC; if (cc->capabilities & SSB_CHIPCO_CAP_PMU) return; /* PMU controls clockmode, separated function needed */ SSB_WARN_ON(ccdev->id.revision >= 20); /* chipcommon cores prior to rev6 don't support dynamic clock control */ if (ccdev->id.revision < 6) return; /* ChipCommon cores rev10+ need testing */ if (ccdev->id.revision >= 10) return; if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) return; switch (mode) { case SSB_CLKMODE_SLOW: /* For revs 6..9 only */ tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); tmp |= SSB_CHIPCO_SLOWCLKCTL_FSLOW; chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); break; case SSB_CLKMODE_FAST: if (ccdev->id.revision < 10) { ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */ tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW; tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL; chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); } else { chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL, (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) | SSB_CHIPCO_SYSCLKCTL_FORCEHT)); /* udelay(150); TODO: not available in early init */ } break; case SSB_CLKMODE_DYNAMIC: if (ccdev->id.revision < 10) { tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW; tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL; tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL; if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) != SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL) tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL; chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); /* For dynamic control, we have to release our xtal_pu * "force on" */ if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL) ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0); } else { chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL, (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) & ~SSB_CHIPCO_SYSCLKCTL_FORCEHT)); } break; default: SSB_WARN_ON(1); } } /* Get the Slow Clock Source */ static enum ssb_clksrc chipco_pctl_get_slowclksrc(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; u32 uninitialized_var(tmp); if (cc->dev->id.revision < 6) { if (bus->bustype == SSB_BUSTYPE_SSB || bus->bustype == SSB_BUSTYPE_PCMCIA) return SSB_CHIPCO_CLKSRC_XTALOS; if (bus->bustype == SSB_BUSTYPE_PCI) { pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &tmp); if (tmp & 0x10) return SSB_CHIPCO_CLKSRC_PCI; return SSB_CHIPCO_CLKSRC_XTALOS; } } if (cc->dev->id.revision < 10) { tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); tmp &= 0x7; if (tmp == 0) return SSB_CHIPCO_CLKSRC_LOPWROS; if (tmp == 1) return SSB_CHIPCO_CLKSRC_XTALOS; if (tmp == 2) return SSB_CHIPCO_CLKSRC_PCI; } return SSB_CHIPCO_CLKSRC_XTALOS; } /* Get maximum or minimum (depending on get_max flag) slowclock frequency. */ static int chipco_pctl_clockfreqlimit(struct ssb_chipcommon *cc, int get_max) { int uninitialized_var(limit); enum ssb_clksrc clocksrc; int divisor = 1; u32 tmp; clocksrc = chipco_pctl_get_slowclksrc(cc); if (cc->dev->id.revision < 6) { switch (clocksrc) { case SSB_CHIPCO_CLKSRC_PCI: divisor = 64; break; case SSB_CHIPCO_CLKSRC_XTALOS: divisor = 32; break; default: SSB_WARN_ON(1); } } else if (cc->dev->id.revision < 10) { switch (clocksrc) { case SSB_CHIPCO_CLKSRC_LOPWROS: break; case SSB_CHIPCO_CLKSRC_XTALOS: case SSB_CHIPCO_CLKSRC_PCI: tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); divisor = (tmp >> 16) + 1; divisor *= 4; break; } } else { tmp = chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL); divisor = (tmp >> 16) + 1; divisor *= 4; } switch (clocksrc) { case SSB_CHIPCO_CLKSRC_LOPWROS: if (get_max) limit = 43000; else limit = 25000; break; case SSB_CHIPCO_CLKSRC_XTALOS: if (get_max) limit = 20200000; else limit = 19800000; break; case SSB_CHIPCO_CLKSRC_PCI: if (get_max) limit = 34000000; else limit = 25000000; break; } limit /= divisor; return limit; } static void chipco_powercontrol_init(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; if (bus->chip_id == 0x4321) { if (bus->chip_rev == 0) chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0x3A4); else if (bus->chip_rev == 1) chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0xA4); } if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) return; if (cc->dev->id.revision >= 10) { /* Set Idle Power clock rate to 1Mhz */ chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL, (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) & 0x0000FFFF) | 0x00040000); } else { int maxfreq; maxfreq = chipco_pctl_clockfreqlimit(cc, 1); chipco_write32(cc, SSB_CHIPCO_PLLONDELAY, (maxfreq * 150 + 999999) / 1000000); chipco_write32(cc, SSB_CHIPCO_FREFSELDELAY, (maxfreq * 15 + 999999) / 1000000); } } /* http://bcm-v4.sipsolutions.net/802.11/PmuFastPwrupDelay */ static u16 pmu_fast_powerup_delay(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; switch (bus->chip_id) { case 0x4312: case 0x4322: case 0x4328: return 7000; case 0x4325: /* TODO: */ default: return 15000; } } /* http://bcm-v4.sipsolutions.net/802.11/ClkctlFastPwrupDelay */ static void calc_fast_powerup_delay(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; int minfreq; unsigned int tmp; u32 pll_on_delay; if (bus->bustype != SSB_BUSTYPE_PCI) return; if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { cc->fast_pwrup_delay = pmu_fast_powerup_delay(cc); return; } if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) return; minfreq = chipco_pctl_clockfreqlimit(cc, 0); pll_on_delay = chipco_read32(cc, SSB_CHIPCO_PLLONDELAY); tmp = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq; SSB_WARN_ON(tmp & ~0xFFFF); cc->fast_pwrup_delay = tmp; } void ssb_chipcommon_init(struct ssb_chipcommon *cc) { if (!cc->dev) return; /* We don't have a ChipCommon */ if (cc->dev->id.revision >= 11) cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT); ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status); if (cc->dev->id.revision >= 20) { chipco_write32(cc, SSB_CHIPCO_GPIOPULLUP, 0); chipco_write32(cc, SSB_CHIPCO_GPIOPULLDOWN, 0); } ssb_pmu_init(cc); chipco_powercontrol_init(cc); ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); calc_fast_powerup_delay(cc); } void ssb_chipco_suspend(struct ssb_chipcommon *cc) { if (!cc->dev) return; ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW); } void ssb_chipco_resume(struct ssb_chipcommon *cc) { if (!cc->dev) return; chipco_powercontrol_init(cc); ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); } /* Get the processor clock */ void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc, u32 *plltype, u32 *n, u32 *m) { *n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N); *plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); switch (*plltype) { case SSB_PLLTYPE_2: case SSB_PLLTYPE_4: case SSB_PLLTYPE_6: case SSB_PLLTYPE_7: *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS); break; case SSB_PLLTYPE_3: /* 5350 uses m2 to control mips */ *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2); break; default: *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB); break; } } /* Get the bus clock */ void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc, u32 *plltype, u32 *n, u32 *m) { *n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N); *plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); switch (*plltype) { case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */ *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS); break; case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ if (cc->dev->bus->chip_id != 0x5365) { *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2); break; } /* Fallthough */ default: *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB); } } void ssb_chipco_timing_init(struct ssb_chipcommon *cc, unsigned long ns) { struct ssb_device *dev = cc->dev; struct ssb_bus *bus = dev->bus; u32 tmp; /* set register for external IO to control LED. */ chipco_write32(cc, SSB_CHIPCO_PROG_CFG, 0x11); tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */ tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 40ns */ tmp |= DIV_ROUND_UP(240, ns); /* Waitcount-0 = 240ns */ chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */ /* Set timing for the flash */ tmp = DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_3_SHIFT; /* Waitcount-3 = 10nS */ tmp |= DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_1_SHIFT; /* Waitcount-1 = 10nS */ tmp |= DIV_ROUND_UP(120, ns); /* Waitcount-0 = 120nS */ if ((bus->chip_id == 0x5365) || (dev->id.revision < 9)) chipco_write32(cc, SSB_CHIPCO_FLASH_WAITCNT, tmp); if ((bus->chip_id == 0x5365) || (dev->id.revision < 9) || ((bus->chip_id == 0x5350) && (bus->chip_rev == 0))) chipco_write32(cc, SSB_CHIPCO_PCMCIA_MEMWAIT, tmp); if (bus->chip_id == 0x5350) { /* Enable EXTIF */ tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */ tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT; /* Waitcount-2 = 20ns */ tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 100ns */ tmp |= DIV_ROUND_UP(120, ns); /* Waitcount-0 = 120ns */ chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */ } } /* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */ void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks) { /* instant NMI */ chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks); } void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value) { chipco_write32_masked(cc, SSB_CHIPCO_IRQMASK, mask, value); } u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask) { return chipco_read32(cc, SSB_CHIPCO_IRQSTAT) & mask; } u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask) { return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask; } u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value) { return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value); } u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value) { return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value); } u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value) { return chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value); } EXPORT_SYMBOL(ssb_chipco_gpio_control); u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value) { return chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value); } u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value) { return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value); } #ifdef CONFIG_SSB_SERIAL int ssb_chipco_serial_init(struct ssb_chipcommon *cc, struct ssb_serial_port *ports) { struct ssb_bus *bus = cc->dev->bus; int nr_ports = 0; u32 plltype; unsigned int irq; u32 baud_base, div; u32 i, n; unsigned int ccrev = cc->dev->id.revision; plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); irq = ssb_mips_irq(cc->dev); if (plltype == SSB_PLLTYPE_1) { /* PLL clock */ baud_base = ssb_calc_clock_rate(plltype, chipco_read32(cc, SSB_CHIPCO_CLOCK_N), chipco_read32(cc, SSB_CHIPCO_CLOCK_M2)); div = 1; } else { if (ccrev == 20) { /* BCM5354 uses constant 25MHz clock */ baud_base = 25000000; div = 48; /* Set the override bit so we don't divide it */ chipco_write32(cc, SSB_CHIPCO_CORECTL, chipco_read32(cc, SSB_CHIPCO_CORECTL) | SSB_CHIPCO_CORECTL_UARTCLK0); } else if ((ccrev >= 11) && (ccrev != 15)) { /* Fixed ALP clock */ baud_base = 20000000; if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { /* FIXME: baud_base is different for devices with a PMU */ SSB_WARN_ON(1); } div = 1; if (ccrev >= 21) { /* Turn off UART clock before switching clocksource. */ chipco_write32(cc, SSB_CHIPCO_CORECTL, chipco_read32(cc, SSB_CHIPCO_CORECTL) & ~SSB_CHIPCO_CORECTL_UARTCLKEN); } /* Set the override bit so we don't divide it */ chipco_write32(cc, SSB_CHIPCO_CORECTL, chipco_read32(cc, SSB_CHIPCO_CORECTL) | SSB_CHIPCO_CORECTL_UARTCLK0); if (ccrev >= 21) { /* Re-enable the UART clock. */ chipco_write32(cc, SSB_CHIPCO_CORECTL, chipco_read32(cc, SSB_CHIPCO_CORECTL) | SSB_CHIPCO_CORECTL_UARTCLKEN); } } else if (ccrev >= 3) { /* Internal backplane clock */ baud_base = ssb_clockspeed(bus); div = chipco_read32(cc, SSB_CHIPCO_CLKDIV) & SSB_CHIPCO_CLKDIV_UART; } else { /* Fixed internal backplane clock */ baud_base = 88000000; div = 48; } /* Clock source depends on strapping if UartClkOverride is unset */ if ((ccrev > 0) && !(chipco_read32(cc, SSB_CHIPCO_CORECTL) & SSB_CHIPCO_CORECTL_UARTCLK0)) { if ((cc->capabilities & SSB_CHIPCO_CAP_UARTCLK) == SSB_CHIPCO_CAP_UARTCLK_INT) { /* Internal divided backplane clock */ baud_base /= div; } else { /* Assume external clock of 1.8432 MHz */ baud_base = 1843200; } } } /* Determine the registers of the UARTs */ n = (cc->capabilities & SSB_CHIPCO_CAP_NRUART); for (i = 0; i < n; i++) { void __iomem *cc_mmio; void __iomem *uart_regs; cc_mmio = cc->dev->bus->mmio + (cc->dev->core_index * SSB_CORE_SIZE); uart_regs = cc_mmio + SSB_CHIPCO_UART0_DATA; /* Offset changed at after rev 0 */ if (ccrev == 0) uart_regs += (i * 8); else uart_regs += (i * 256); nr_ports++; ports[i].regs = uart_regs; ports[i].irq = irq; ports[i].baud_base = baud_base; ports[i].reg_shift = 0; } return nr_ports; } #endif /* CONFIG_SSB_SERIAL */ compat-drivers-2012-09-18/drivers/ssb/b43_pci_bridge.c0000644000175000017500000000336512026211315021537 0ustar mcgrofmcgrof/* * Broadcom 43xx PCI-SSB bridge module * * This technically is a separate PCI driver module, but * because of its small size we include it in the SSB core * instead of creating a standalone module. * * Copyright 2007 Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include #include "ssb_private.h" static const struct pci_device_id b43_pci_bridge_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4301) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4306) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4307) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4311) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4312) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4315) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4318) }, { PCI_DEVICE(PCI_VENDOR_ID_BCM_GVC, 0x4318) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4319) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4320) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4321) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4322) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43222) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4325) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4328) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4329) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x432b) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x432c) }, { 0, }, }; MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl); static struct pci_driver b43_pci_bridge_driver = { .name = "b43-pci-bridge", .id_table = b43_pci_bridge_tbl, }; int __init b43_pci_ssb_bridge_init(void) { return ssb_pcihost_register(&b43_pci_bridge_driver); } void __exit b43_pci_ssb_bridge_exit(void) { ssb_pcihost_unregister(&b43_pci_bridge_driver); } compat-drivers-2012-09-18/drivers/ssb/Makefile0000644000175000017500000000133012026211315020262 0ustar mcgrofmcgrof# core ssb-y += main.o scan.o ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o ssb-$(CONFIG_SSB_SPROM) += sprom.o # host support ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o ssb-$(CONFIG_SSB_SDIOHOST) += sdio.o # built-in drivers ssb-y += driver_chipcommon.o ssb-y += driver_chipcommon_pmu.o ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o # b43 pci-ssb-bridge driver # Not strictly a part of SSB, but kept here for convenience ssb-$(CONFIG_SSB_B43_PCI_BRIDGE) += b43_pci_bridge.o obj-$(CONFIG_SSB) += ssb.o compat-drivers-2012-09-18/drivers/ssb/Kconfig0000644000175000017500000000666112026211315020141 0ustar mcgrofmcgrofconfig SSB_POSSIBLE bool depends on HAS_IOMEM && HAS_DMA default y menu "Sonics Silicon Backplane" depends on SSB_POSSIBLE config SSB tristate "Sonics Silicon Backplane support" depends on SSB_POSSIBLE help Support for the Sonics Silicon Backplane bus. You only need to enable this option, if you are configuring a kernel for an embedded system with this bus. It will be auto-selected if needed in other environments. The module will be called ssb. If unsure, say N. # Common SPROM support routines config SSB_SPROM bool # Support for Block-I/O. SELECT this from the driver that needs it. config SSB_BLOCKIO bool depends on SSB config SSB_PCIHOST_POSSIBLE bool depends on SSB && (PCI = y || PCI = SSB) default y config SSB_PCIHOST bool "Support for SSB on PCI-bus host" depends on SSB_PCIHOST_POSSIBLE select SSB_SPROM default y help Support for a Sonics Silicon Backplane on top of a PCI device. If unsure, say Y config SSB_B43_PCI_BRIDGE bool depends on SSB_PCIHOST default n config SSB_PCMCIAHOST_POSSIBLE bool depends on SSB && (PCMCIA = y || PCMCIA = SSB) default y config SSB_PCMCIAHOST bool "Support for SSB on PCMCIA-bus host" depends on SSB_PCMCIAHOST_POSSIBLE select SSB_SPROM help Support for a Sonics Silicon Backplane on top of a PCMCIA device. If unsure, say N config SSB_SDIOHOST_POSSIBLE bool depends on SSB && (MMC = y || MMC = SSB) default y config SSB_SDIOHOST bool "Support for SSB on SDIO-bus host" depends on SSB_SDIOHOST_POSSIBLE help Support for a Sonics Silicon Backplane on top of a SDIO device. If unsure, say N config SSB_SILENT bool "No SSB kernel messages" depends on SSB && EXPERT help This option turns off all Sonics Silicon Backplane printks. Note that you won't be able to identify problems, once messages are turned off. This might only be desired for production kernels on embedded devices to reduce the kernel size. Say N config SSB_DEBUG bool "SSB debugging" depends on SSB && !SSB_SILENT help This turns on additional runtime checks and debugging messages. Turn this on for SSB troubleshooting. If unsure, say N config SSB_SERIAL bool depends on SSB # ChipCommon and ExtIf serial support routines. config SSB_DRIVER_PCICORE_POSSIBLE bool depends on SSB_PCIHOST default y config SSB_DRIVER_PCICORE bool "SSB PCI core driver" depends on SSB_DRIVER_PCICORE_POSSIBLE help Driver for the Sonics Silicon Backplane attached Broadcom PCI core. If unsure, say Y config SSB_PCICORE_HOSTMODE bool "Hostmode support for SSB PCI core" depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS help PCIcore hostmode operation (external PCI bus). config SSB_DRIVER_MIPS bool "SSB Broadcom MIPS core driver" depends on SSB && MIPS select SSB_SERIAL help Driver for the Sonics Silicon Backplane attached Broadcom MIPS core. If unsure, say N # Assumption: We are on embedded, if we compile the MIPS core. config SSB_EMBEDDED bool depends on SSB_DRIVER_MIPS default y config SSB_DRIVER_EXTIF bool "SSB Broadcom EXTIF core driver" depends on SSB_DRIVER_MIPS help Driver for the Sonics Silicon Backplane attached Broadcom EXTIF core. If unsure, say N config SSB_DRIVER_GIGE bool "SSB Broadcom Gigabit Ethernet driver" depends on SSB_PCIHOST_POSSIBLE && SSB_EMBEDDED && MIPS help Driver for the Sonics Silicon Backplane attached Broadcom Gigabit Ethernet. If unsure, say N endmenu compat-drivers-2012-09-18/drivers/platform/0000755000175000017500000000000012026211315017662 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/platform/x86/0000755000175000017500000000000012026211315020307 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/0000755000175000017500000000000012026211315016624 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/0000755000175000017500000000000012026211315020461 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/Makefile0000644000175000017500000000175412026211315022130 0ustar mcgrofmcgrof# # Makefile for the Linux Wireless network device drivers. # obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o obj-$(CONFIG_B43) += b43/ obj-$(CONFIG_B43LEGACY) += b43legacy/ obj-$(CONFIG_COMPAT_ZD1211RW) += zd1211rw/ obj-$(CONFIG_RTL8180) += rtl818x/ obj-$(CONFIG_RTL8187) += rtl818x/ obj-$(CONFIG_RTLWIFI) += rtlwifi/ obj-$(CONFIG_USB_NET_COMPAT_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ obj-$(CONFIG_ADM8211) += adm8211.o obj-$(CONFIG_MWL8K) += mwl8k.o obj-$(CONFIG_IWLWIFI) += iwlwifi/ obj-$(CONFIG_IWLEGACY) += iwlegacy/ obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_P54_COMMON) += p54/ obj-$(CONFIG_ATH_COMMON) += ath/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o obj-$(CONFIG_WL_TI) += ti/ obj-$(CONFIG_MWIFIEX) += mwifiex/ obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/ compat-drivers-2012-09-18/drivers/net/wireless/rndis_wlan.c0000644000175000017500000031003612026211315022770 0ustar mcgrofmcgrof/* * Driver for RNDIS based wireless USB devices. * * Copyright (C) 2007 by Bjorge Dijkstra * Copyright (C) 2008-2009 by Jussi Kivilinna * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Portions of this file are based on NDISwrapper project, * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani * http://ndiswrapper.sourceforge.net/ */ // #define DEBUG // error path messages, extra info // #define VERBOSE // more; success messages #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* NOTE: All these are settings for Broadcom chipset */ static char modparam_country[4] = "EU"; module_param_string(country, modparam_country, 4, 0444); MODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU"); static int modparam_frameburst = 1; module_param_named(frameburst, modparam_frameburst, int, 0444); MODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)"); static int modparam_afterburner = 0; module_param_named(afterburner, modparam_afterburner, int, 0444); MODULE_PARM_DESC(afterburner, "enable afterburner aka '125 High Speed Mode' (default: off)"); static int modparam_power_save = 0; module_param_named(power_save, modparam_power_save, int, 0444); MODULE_PARM_DESC(power_save, "set power save mode: 0=off, 1=on, 2=fast (default: off)"); static int modparam_power_output = 3; module_param_named(power_output, modparam_power_output, int, 0444); MODULE_PARM_DESC(power_output, "set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)"); static int modparam_roamtrigger = -70; module_param_named(roamtrigger, modparam_roamtrigger, int, 0444); MODULE_PARM_DESC(roamtrigger, "set roaming dBm trigger: -80=optimize for distance, " "-60=bandwidth (default: -70)"); static int modparam_roamdelta = 1; module_param_named(roamdelta, modparam_roamdelta, int, 0444); MODULE_PARM_DESC(roamdelta, "set roaming tendency: 0=aggressive, 1=moderate, " "2=conservative (default: moderate)"); static int modparam_workaround_interval; module_param_named(workaround_interval, modparam_workaround_interval, int, 0444); MODULE_PARM_DESC(workaround_interval, "set stall workaround interval in msecs (0=disabled) (default: 0)"); /* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */ #define WL_NOISE -96 /* typical noise level in dBm */ #define WL_SIGMAX -32 /* typical maximum signal level in dBm */ /* Assume that Broadcom 4320 (only chipset at time of writing known to be * based on wireless rndis) has default txpower of 13dBm. * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications. * 100% : 20 mW ~ 13dBm * 75% : 15 mW ~ 12dBm * 50% : 10 mW ~ 10dBm * 25% : 5 mW ~ 7dBm */ #define BCM4320_DEFAULT_TXPOWER_DBM_100 13 #define BCM4320_DEFAULT_TXPOWER_DBM_75 12 #define BCM4320_DEFAULT_TXPOWER_DBM_50 10 #define BCM4320_DEFAULT_TXPOWER_DBM_25 7 /* Known device types */ #define RNDIS_UNKNOWN 0 #define RNDIS_BCM4320A 1 #define RNDIS_BCM4320B 2 /* NDIS data structures. Taken from wpa_supplicant driver_ndis.c * slightly modified for datatype endianess, etc */ #define NDIS_802_11_LENGTH_SSID 32 #define NDIS_802_11_LENGTH_RATES 8 #define NDIS_802_11_LENGTH_RATES_EX 16 enum ndis_80211_net_type { NDIS_80211_TYPE_FREQ_HOP, NDIS_80211_TYPE_DIRECT_SEQ, NDIS_80211_TYPE_OFDM_A, NDIS_80211_TYPE_OFDM_G }; enum ndis_80211_net_infra { NDIS_80211_INFRA_ADHOC, NDIS_80211_INFRA_INFRA, NDIS_80211_INFRA_AUTO_UNKNOWN }; enum ndis_80211_auth_mode { NDIS_80211_AUTH_OPEN, NDIS_80211_AUTH_SHARED, NDIS_80211_AUTH_AUTO_SWITCH, NDIS_80211_AUTH_WPA, NDIS_80211_AUTH_WPA_PSK, NDIS_80211_AUTH_WPA_NONE, NDIS_80211_AUTH_WPA2, NDIS_80211_AUTH_WPA2_PSK }; enum ndis_80211_encr_status { NDIS_80211_ENCR_WEP_ENABLED, NDIS_80211_ENCR_DISABLED, NDIS_80211_ENCR_WEP_KEY_ABSENT, NDIS_80211_ENCR_NOT_SUPPORTED, NDIS_80211_ENCR_TKIP_ENABLED, NDIS_80211_ENCR_TKIP_KEY_ABSENT, NDIS_80211_ENCR_CCMP_ENABLED, NDIS_80211_ENCR_CCMP_KEY_ABSENT }; enum ndis_80211_priv_filter { NDIS_80211_PRIV_ACCEPT_ALL, NDIS_80211_PRIV_8021X_WEP }; enum ndis_80211_status_type { NDIS_80211_STATUSTYPE_AUTHENTICATION, NDIS_80211_STATUSTYPE_MEDIASTREAMMODE, NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST, NDIS_80211_STATUSTYPE_RADIOSTATE, }; enum ndis_80211_media_stream_mode { NDIS_80211_MEDIA_STREAM_OFF, NDIS_80211_MEDIA_STREAM_ON }; enum ndis_80211_radio_status { NDIS_80211_RADIO_STATUS_ON, NDIS_80211_RADIO_STATUS_HARDWARE_OFF, NDIS_80211_RADIO_STATUS_SOFTWARE_OFF, }; enum ndis_80211_addkey_bits { NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28), NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29), NDIS_80211_ADDKEY_PAIRWISE_KEY = cpu_to_le32(1 << 30), NDIS_80211_ADDKEY_TRANSMIT_KEY = cpu_to_le32(1 << 31) }; enum ndis_80211_addwep_bits { NDIS_80211_ADDWEP_PERCLIENT_KEY = cpu_to_le32(1 << 30), NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) }; enum ndis_80211_power_mode { NDIS_80211_POWER_MODE_CAM, NDIS_80211_POWER_MODE_MAX_PSP, NDIS_80211_POWER_MODE_FAST_PSP, }; enum ndis_80211_pmkid_cand_list_flag_bits { NDIS_80211_PMKID_CAND_PREAUTH = cpu_to_le32(1 << 0) }; struct ndis_80211_auth_request { __le32 length; u8 bssid[6]; u8 padding[2]; __le32 flags; } __packed; struct ndis_80211_pmkid_candidate { u8 bssid[6]; u8 padding[2]; __le32 flags; } __packed; struct ndis_80211_pmkid_cand_list { __le32 version; __le32 num_candidates; struct ndis_80211_pmkid_candidate candidate_list[0]; } __packed; struct ndis_80211_status_indication { __le32 status_type; union { __le32 media_stream_mode; __le32 radio_status; struct ndis_80211_auth_request auth_request[0]; struct ndis_80211_pmkid_cand_list cand_list; } u; } __packed; struct ndis_80211_ssid { __le32 length; u8 essid[NDIS_802_11_LENGTH_SSID]; } __packed; struct ndis_80211_conf_freq_hop { __le32 length; __le32 hop_pattern; __le32 hop_set; __le32 dwell_time; } __packed; struct ndis_80211_conf { __le32 length; __le32 beacon_period; __le32 atim_window; __le32 ds_config; struct ndis_80211_conf_freq_hop fh_config; } __packed; struct ndis_80211_bssid_ex { __le32 length; u8 mac[6]; u8 padding[2]; struct ndis_80211_ssid ssid; __le32 privacy; __le32 rssi; __le32 net_type; struct ndis_80211_conf config; __le32 net_infra; u8 rates[NDIS_802_11_LENGTH_RATES_EX]; __le32 ie_length; u8 ies[0]; } __packed; struct ndis_80211_bssid_list_ex { __le32 num_items; struct ndis_80211_bssid_ex bssid[0]; } __packed; struct ndis_80211_fixed_ies { u8 timestamp[8]; __le16 beacon_interval; __le16 capabilities; } __packed; struct ndis_80211_wep_key { __le32 size; __le32 index; __le32 length; u8 material[32]; } __packed; struct ndis_80211_key { __le32 size; __le32 index; __le32 length; u8 bssid[6]; u8 padding[6]; u8 rsc[8]; u8 material[32]; } __packed; struct ndis_80211_remove_key { __le32 size; __le32 index; u8 bssid[6]; u8 padding[2]; } __packed; struct ndis_config_param { __le32 name_offs; __le32 name_length; __le32 type; __le32 value_offs; __le32 value_length; } __packed; struct ndis_80211_assoc_info { __le32 length; __le16 req_ies; struct req_ie { __le16 capa; __le16 listen_interval; u8 cur_ap_address[6]; } req_ie; __le32 req_ie_length; __le32 offset_req_ies; __le16 resp_ies; struct resp_ie { __le16 capa; __le16 status_code; __le16 assoc_id; } resp_ie; __le32 resp_ie_length; __le32 offset_resp_ies; } __packed; struct ndis_80211_auth_encr_pair { __le32 auth_mode; __le32 encr_mode; } __packed; struct ndis_80211_capability { __le32 length; __le32 version; __le32 num_pmkids; __le32 num_auth_encr_pair; struct ndis_80211_auth_encr_pair auth_encr_pair[0]; } __packed; struct ndis_80211_bssid_info { u8 bssid[6]; u8 pmkid[16]; } __packed; struct ndis_80211_pmkid { __le32 length; __le32 bssid_info_count; struct ndis_80211_bssid_info bssid_info[0]; } __packed; /* * private data */ #define CAP_MODE_80211A 1 #define CAP_MODE_80211B 2 #define CAP_MODE_80211G 4 #define CAP_MODE_MASK 7 #define WORK_LINK_UP (1<<0) #define WORK_LINK_DOWN (1<<1) #define WORK_SET_MULTICAST_LIST (1<<2) #define RNDIS_WLAN_ALG_NONE 0 #define RNDIS_WLAN_ALG_WEP (1<<0) #define RNDIS_WLAN_ALG_TKIP (1<<1) #define RNDIS_WLAN_ALG_CCMP (1<<2) #define RNDIS_WLAN_NUM_KEYS 4 #define RNDIS_WLAN_KEY_MGMT_NONE 0 #define RNDIS_WLAN_KEY_MGMT_802_1X (1<<0) #define RNDIS_WLAN_KEY_MGMT_PSK (1<<1) #define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set)) static const struct ieee80211_channel rndis_channels[] = { { .center_freq = 2412 }, { .center_freq = 2417 }, { .center_freq = 2422 }, { .center_freq = 2427 }, { .center_freq = 2432 }, { .center_freq = 2437 }, { .center_freq = 2442 }, { .center_freq = 2447 }, { .center_freq = 2452 }, { .center_freq = 2457 }, { .center_freq = 2462 }, { .center_freq = 2467 }, { .center_freq = 2472 }, { .center_freq = 2484 }, }; static const struct ieee80211_rate rndis_rates[] = { { .bitrate = 10 }, { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60 }, { .bitrate = 90 }, { .bitrate = 120 }, { .bitrate = 180 }, { .bitrate = 240 }, { .bitrate = 360 }, { .bitrate = 480 }, { .bitrate = 540 } }; static const u32 rndis_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, }; struct rndis_wlan_encr_key { int len; u32 cipher; u8 material[32]; u8 bssid[ETH_ALEN]; bool pairwise; bool tx_key; }; /* RNDIS device private data */ struct rndis_wlan_private { struct usbnet *usbdev; struct wireless_dev wdev; struct cfg80211_scan_request *scan_request; struct workqueue_struct *workqueue; struct delayed_work dev_poller_work; struct delayed_work scan_work; struct work_struct work; struct mutex command_lock; unsigned long work_pending; int last_qual; s32 cqm_rssi_thold; u32 cqm_rssi_hyst; int last_cqm_event_rssi; struct ieee80211_supported_band band; struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)]; u32 cipher_suites[ARRAY_SIZE(rndis_cipher_suites)]; int device_type; int caps; int multicast_size; /* module parameters */ char param_country[4]; int param_frameburst; int param_afterburner; int param_power_save; int param_power_output; int param_roamtrigger; int param_roamdelta; u32 param_workaround_interval; /* hardware state */ bool radio_on; int power_mode; int infra_mode; bool connected; u8 bssid[ETH_ALEN]; u32 current_command_oid; /* encryption stuff */ u8 encr_tx_key_index; struct rndis_wlan_encr_key encr_keys[RNDIS_WLAN_NUM_KEYS]; int wpa_version; u8 command_buffer[COMMAND_BUFFER_SIZE]; }; /* * cfg80211 ops */ static int rndis_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params); static int rndis_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request); static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); static int rndis_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm); static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm); static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code); static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params); static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev); static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr, struct key_params *params); static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr); static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast); static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo); static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo); static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa); static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa); static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev); static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout); static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst); static const struct cfg80211_ops rndis_config_ops = { .change_virtual_intf = rndis_change_virtual_intf, .scan = rndis_scan, .set_wiphy_params = rndis_set_wiphy_params, .set_tx_power = rndis_set_tx_power, .get_tx_power = rndis_get_tx_power, .connect = rndis_connect, .disconnect = rndis_disconnect, .join_ibss = rndis_join_ibss, .leave_ibss = rndis_leave_ibss, .add_key = rndis_add_key, .del_key = rndis_del_key, .set_default_key = rndis_set_default_key, .get_station = rndis_get_station, .dump_station = rndis_dump_station, .set_pmksa = rndis_set_pmksa, .del_pmksa = rndis_del_pmksa, .flush_pmksa = rndis_flush_pmksa, .set_power_mgmt = rndis_set_power_mgmt, .set_cqm_rssi_config = rndis_set_cqm_rssi_config, }; static void *rndis_wiphy_privid = &rndis_wiphy_privid; static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) { return (struct rndis_wlan_private *)dev->driver_priv; } static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv) { switch (priv->param_power_output) { default: case 3: return BCM4320_DEFAULT_TXPOWER_DBM_100; case 2: return BCM4320_DEFAULT_TXPOWER_DBM_75; case 1: return BCM4320_DEFAULT_TXPOWER_DBM_50; case 0: return BCM4320_DEFAULT_TXPOWER_DBM_25; } } static bool is_wpa_key(struct rndis_wlan_private *priv, u8 idx) { int cipher = priv->encr_keys[idx].cipher; return (cipher == WLAN_CIPHER_SUITE_CCMP || cipher == WLAN_CIPHER_SUITE_TKIP); } static int rndis_cipher_to_alg(u32 cipher) { switch (cipher) { default: return RNDIS_WLAN_ALG_NONE; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: return RNDIS_WLAN_ALG_WEP; case WLAN_CIPHER_SUITE_TKIP: return RNDIS_WLAN_ALG_TKIP; case WLAN_CIPHER_SUITE_CCMP: return RNDIS_WLAN_ALG_CCMP; } } static int rndis_akm_suite_to_key_mgmt(u32 akm_suite) { switch (akm_suite) { default: return RNDIS_WLAN_KEY_MGMT_NONE; case WLAN_AKM_SUITE_8021X: return RNDIS_WLAN_KEY_MGMT_802_1X; case WLAN_AKM_SUITE_PSK: return RNDIS_WLAN_KEY_MGMT_PSK; } } #ifdef DEBUG static const char *oid_to_string(u32 oid) { switch (oid) { #define OID_STR(oid) case oid: return(#oid) /* from rndis_host.h */ OID_STR(RNDIS_OID_802_3_PERMANENT_ADDRESS); OID_STR(RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE); OID_STR(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); OID_STR(RNDIS_OID_GEN_PHYSICAL_MEDIUM); /* from rndis_wlan.c */ OID_STR(RNDIS_OID_GEN_LINK_SPEED); OID_STR(RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER); OID_STR(RNDIS_OID_GEN_XMIT_OK); OID_STR(RNDIS_OID_GEN_RCV_OK); OID_STR(RNDIS_OID_GEN_XMIT_ERROR); OID_STR(RNDIS_OID_GEN_RCV_ERROR); OID_STR(RNDIS_OID_GEN_RCV_NO_BUFFER); OID_STR(RNDIS_OID_802_3_CURRENT_ADDRESS); OID_STR(RNDIS_OID_802_3_MULTICAST_LIST); OID_STR(RNDIS_OID_802_3_MAXIMUM_LIST_SIZE); OID_STR(RNDIS_OID_802_11_BSSID); OID_STR(RNDIS_OID_802_11_SSID); OID_STR(RNDIS_OID_802_11_INFRASTRUCTURE_MODE); OID_STR(RNDIS_OID_802_11_ADD_WEP); OID_STR(RNDIS_OID_802_11_REMOVE_WEP); OID_STR(RNDIS_OID_802_11_DISASSOCIATE); OID_STR(RNDIS_OID_802_11_AUTHENTICATION_MODE); OID_STR(RNDIS_OID_802_11_PRIVACY_FILTER); OID_STR(RNDIS_OID_802_11_BSSID_LIST_SCAN); OID_STR(RNDIS_OID_802_11_ENCRYPTION_STATUS); OID_STR(RNDIS_OID_802_11_ADD_KEY); OID_STR(RNDIS_OID_802_11_REMOVE_KEY); OID_STR(RNDIS_OID_802_11_ASSOCIATION_INFORMATION); OID_STR(RNDIS_OID_802_11_CAPABILITY); OID_STR(RNDIS_OID_802_11_PMKID); OID_STR(RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED); OID_STR(RNDIS_OID_802_11_NETWORK_TYPE_IN_USE); OID_STR(RNDIS_OID_802_11_TX_POWER_LEVEL); OID_STR(RNDIS_OID_802_11_RSSI); OID_STR(RNDIS_OID_802_11_RSSI_TRIGGER); OID_STR(RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD); OID_STR(RNDIS_OID_802_11_RTS_THRESHOLD); OID_STR(RNDIS_OID_802_11_SUPPORTED_RATES); OID_STR(RNDIS_OID_802_11_CONFIGURATION); OID_STR(RNDIS_OID_802_11_POWER_MODE); OID_STR(RNDIS_OID_802_11_BSSID_LIST); #undef OID_STR } return "?"; } #else static const char *oid_to_string(u32 oid) { return "?"; } #endif /* translate error code */ static int rndis_error_status(__le32 rndis_status) { int ret = -EINVAL; switch (le32_to_cpu(rndis_status)) { case RNDIS_STATUS_SUCCESS: ret = 0; break; case RNDIS_STATUS_FAILURE: case RNDIS_STATUS_INVALID_DATA: ret = -EINVAL; break; case RNDIS_STATUS_NOT_SUPPORTED: ret = -EOPNOTSUPP; break; case RNDIS_STATUS_ADAPTER_NOT_READY: case RNDIS_STATUS_ADAPTER_NOT_OPEN: ret = -EBUSY; break; } return ret; } static int rndis_query_oid(struct usbnet *dev, u32 oid, void *data, int *len) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); union { void *buf; struct rndis_msg_hdr *header; struct rndis_query *get; struct rndis_query_c *get_c; } u; int ret, buflen; int resplen, respoffs, copylen; buflen = *len + sizeof(*u.get); if (buflen < CONTROL_BUFFER_SIZE) buflen = CONTROL_BUFFER_SIZE; if (buflen > COMMAND_BUFFER_SIZE) { u.buf = kmalloc(buflen, GFP_KERNEL); if (!u.buf) return -ENOMEM; } else { u.buf = priv->command_buffer; } mutex_lock(&priv->command_lock); memset(u.get, 0, sizeof *u.get); u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY); u.get->msg_len = cpu_to_le32(sizeof *u.get); u.get->oid = cpu_to_le32(oid); priv->current_command_oid = oid; ret = rndis_command(dev, u.header, buflen); priv->current_command_oid = 0; if (ret < 0) netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", __func__, oid_to_string(oid), ret, le32_to_cpu(u.get_c->status)); if (ret == 0) { resplen = le32_to_cpu(u.get_c->len); respoffs = le32_to_cpu(u.get_c->offset) + 8; if (respoffs > buflen) { /* Device returned data offset outside buffer, error. */ netdev_dbg(dev->net, "%s(%s): received invalid " "data offset: %d > %d\n", __func__, oid_to_string(oid), respoffs, buflen); ret = -EINVAL; goto exit_unlock; } if ((resplen + respoffs) > buflen) { /* Device would have returned more data if buffer would * have been big enough. Copy just the bits that we got. */ copylen = buflen - respoffs; } else { copylen = resplen; } if (copylen > *len) copylen = *len; memcpy(data, u.buf + respoffs, copylen); *len = resplen; ret = rndis_error_status(u.get_c->status); if (ret < 0) netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", __func__, oid_to_string(oid), le32_to_cpu(u.get_c->status), ret); } exit_unlock: mutex_unlock(&priv->command_lock); if (u.buf != priv->command_buffer) kfree(u.buf); return ret; } static int rndis_set_oid(struct usbnet *dev, u32 oid, const void *data, int len) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); union { void *buf; struct rndis_msg_hdr *header; struct rndis_set *set; struct rndis_set_c *set_c; } u; int ret, buflen; buflen = len + sizeof(*u.set); if (buflen < CONTROL_BUFFER_SIZE) buflen = CONTROL_BUFFER_SIZE; if (buflen > COMMAND_BUFFER_SIZE) { u.buf = kmalloc(buflen, GFP_KERNEL); if (!u.buf) return -ENOMEM; } else { u.buf = priv->command_buffer; } mutex_lock(&priv->command_lock); memset(u.set, 0, sizeof *u.set); u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len); u.set->oid = cpu_to_le32(oid); u.set->len = cpu_to_le32(len); u.set->offset = cpu_to_le32(sizeof(*u.set) - 8); u.set->handle = cpu_to_le32(0); memcpy(u.buf + sizeof(*u.set), data, len); priv->current_command_oid = oid; ret = rndis_command(dev, u.header, buflen); priv->current_command_oid = 0; if (ret < 0) netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", __func__, oid_to_string(oid), ret, le32_to_cpu(u.set_c->status)); if (ret == 0) { ret = rndis_error_status(u.set_c->status); if (ret < 0) netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", __func__, oid_to_string(oid), le32_to_cpu(u.set_c->status), ret); } mutex_unlock(&priv->command_lock); if (u.buf != priv->command_buffer) kfree(u.buf); return ret; } static int rndis_reset(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct rndis_reset *reset; int ret; mutex_lock(&priv->command_lock); reset = (void *)priv->command_buffer; memset(reset, 0, sizeof(*reset)); reset->msg_type = cpu_to_le32(RNDIS_MSG_RESET); reset->msg_len = cpu_to_le32(sizeof(*reset)); priv->current_command_oid = 0; ret = rndis_command(usbdev, (void *)reset, CONTROL_BUFFER_SIZE); mutex_unlock(&priv->command_lock); if (ret < 0) return ret; return 0; } /* * Specs say that we can only set config parameters only soon after device * initialization. * value_type: 0 = u32, 2 = unicode string */ static int rndis_set_config_parameter(struct usbnet *dev, char *param, int value_type, void *value) { struct ndis_config_param *infobuf; int value_len, info_len, param_len, ret, i; __le16 *unibuf; __le32 *dst_value; if (value_type == 0) value_len = sizeof(__le32); else if (value_type == 2) value_len = strlen(value) * sizeof(__le16); else return -EINVAL; param_len = strlen(param) * sizeof(__le16); info_len = sizeof(*infobuf) + param_len + value_len; #ifdef DEBUG info_len += 12; #endif infobuf = kmalloc(info_len, GFP_KERNEL); if (!infobuf) return -ENOMEM; #ifdef DEBUG info_len -= 12; /* extra 12 bytes are for padding (debug output) */ memset(infobuf, 0xCC, info_len + 12); #endif if (value_type == 2) netdev_dbg(dev->net, "setting config parameter: %s, value: %s\n", param, (u8 *)value); else netdev_dbg(dev->net, "setting config parameter: %s, value: %d\n", param, *(u32 *)value); infobuf->name_offs = cpu_to_le32(sizeof(*infobuf)); infobuf->name_length = cpu_to_le32(param_len); infobuf->type = cpu_to_le32(value_type); infobuf->value_offs = cpu_to_le32(sizeof(*infobuf) + param_len); infobuf->value_length = cpu_to_le32(value_len); /* simple string to unicode string conversion */ unibuf = (void *)infobuf + sizeof(*infobuf); for (i = 0; i < param_len / sizeof(__le16); i++) unibuf[i] = cpu_to_le16(param[i]); if (value_type == 2) { unibuf = (void *)infobuf + sizeof(*infobuf) + param_len; for (i = 0; i < value_len / sizeof(__le16); i++) unibuf[i] = cpu_to_le16(((u8 *)value)[i]); } else { dst_value = (void *)infobuf + sizeof(*infobuf) + param_len; *dst_value = cpu_to_le32(*(u32 *)value); } #ifdef DEBUG netdev_dbg(dev->net, "info buffer (len: %d)\n", info_len); for (i = 0; i < info_len; i += 12) { u32 *tmp = (u32 *)((u8 *)infobuf + i); netdev_dbg(dev->net, "%08X:%08X:%08X\n", cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), cpu_to_be32(tmp[2])); } #endif ret = rndis_set_oid(dev, RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER, infobuf, info_len); if (ret != 0) netdev_dbg(dev->net, "setting rndis config parameter failed, %d\n", ret); kfree(infobuf); return ret; } static int rndis_set_config_parameter_str(struct usbnet *dev, char *param, char *value) { return rndis_set_config_parameter(dev, param, 2, value); } /* * data conversion functions */ static int level_to_qual(int level) { int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE); return qual >= 0 ? (qual <= 100 ? qual : 100) : 0; } /* * common functions */ static int set_infra_mode(struct usbnet *usbdev, int mode); static void restore_keys(struct usbnet *usbdev); static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, bool *matched); static int rndis_start_bssid_list_scan(struct usbnet *usbdev) { __le32 tmp; /* Note: RNDIS_OID_802_11_BSSID_LIST_SCAN clears internal BSS list. */ tmp = cpu_to_le32(1); return rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST_SCAN, &tmp, sizeof(tmp)); } static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int ret; ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_SSID, ssid, sizeof(*ssid)); if (ret < 0) { netdev_warn(usbdev->net, "setting SSID failed (%08X)\n", ret); return ret; } if (ret == 0) { priv->radio_on = true; netdev_dbg(usbdev->net, "%s(): radio_on = true\n", __func__); } return ret; } static int set_bssid(struct usbnet *usbdev, const u8 *bssid) { int ret; ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID, bssid, ETH_ALEN); if (ret < 0) { netdev_warn(usbdev->net, "setting BSSID[%pM] failed (%08X)\n", bssid, ret); return ret; } return ret; } static int clear_bssid(struct usbnet *usbdev) { static const u8 broadcast_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; return set_bssid(usbdev, broadcast_mac); } static int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN]) { int ret, len; len = ETH_ALEN; ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID, bssid, &len); if (ret != 0) memset(bssid, 0, ETH_ALEN); return ret; } static int get_association_info(struct usbnet *usbdev, struct ndis_80211_assoc_info *info, int len) { return rndis_query_oid(usbdev, RNDIS_OID_802_11_ASSOCIATION_INFORMATION, info, &len); } static bool is_associated(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); u8 bssid[ETH_ALEN]; int ret; if (!priv->radio_on) return false; ret = get_bssid(usbdev, bssid); return (ret == 0 && !is_zero_ether_addr(bssid)); } static int disassociate(struct usbnet *usbdev, bool reset_ssid) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_ssid ssid; int i, ret = 0; if (priv->radio_on) { ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_DISASSOCIATE, NULL, 0); if (ret == 0) { priv->radio_on = false; netdev_dbg(usbdev->net, "%s(): radio_on = false\n", __func__); if (reset_ssid) msleep(100); } } /* disassociate causes radio to be turned off; if reset_ssid * is given, set random ssid to enable radio */ if (reset_ssid) { /* Set device to infrastructure mode so we don't get ad-hoc * 'media connect' indications with the random ssid. */ set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); ssid.length = cpu_to_le32(sizeof(ssid.essid)); get_random_bytes(&ssid.essid[2], sizeof(ssid.essid)-2); ssid.essid[0] = 0x1; ssid.essid[1] = 0xff; for (i = 2; i < sizeof(ssid.essid); i++) ssid.essid[i] = 0x1 + (ssid.essid[i] * 0xfe / 0xff); ret = set_essid(usbdev, &ssid); } return ret; } static int set_auth_mode(struct usbnet *usbdev, u32 wpa_version, enum nl80211_auth_type auth_type, int keymgmt) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; int auth_mode, ret; netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x authalg=0x%x keymgmt=0x%x\n", __func__, wpa_version, auth_type, keymgmt); if (wpa_version & NL80211_WPA_VERSION_2) { if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) auth_mode = NDIS_80211_AUTH_WPA2; else auth_mode = NDIS_80211_AUTH_WPA2_PSK; } else if (wpa_version & NL80211_WPA_VERSION_1) { if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) auth_mode = NDIS_80211_AUTH_WPA; else if (keymgmt & RNDIS_WLAN_KEY_MGMT_PSK) auth_mode = NDIS_80211_AUTH_WPA_PSK; else auth_mode = NDIS_80211_AUTH_WPA_NONE; } else if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) auth_mode = NDIS_80211_AUTH_SHARED; else if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) auth_mode = NDIS_80211_AUTH_OPEN; else if (auth_type == NL80211_AUTHTYPE_AUTOMATIC) auth_mode = NDIS_80211_AUTH_AUTO_SWITCH; else return -ENOTSUPP; tmp = cpu_to_le32(auth_mode); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_AUTHENTICATION_MODE, &tmp, sizeof(tmp)); if (ret != 0) { netdev_warn(usbdev->net, "setting auth mode failed (%08X)\n", ret); return ret; } priv->wpa_version = wpa_version; return 0; } static int set_priv_filter(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x\n", __func__, priv->wpa_version); if (priv->wpa_version & NL80211_WPA_VERSION_2 || priv->wpa_version & NL80211_WPA_VERSION_1) tmp = cpu_to_le32(NDIS_80211_PRIV_8021X_WEP); else tmp = cpu_to_le32(NDIS_80211_PRIV_ACCEPT_ALL); return rndis_set_oid(usbdev, RNDIS_OID_802_11_PRIVACY_FILTER, &tmp, sizeof(tmp)); } static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) { __le32 tmp; int encr_mode, ret; netdev_dbg(usbdev->net, "%s(): cipher_pair=0x%x cipher_group=0x%x\n", __func__, pairwise, groupwise); if (pairwise & RNDIS_WLAN_ALG_CCMP) encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; else if (pairwise & RNDIS_WLAN_ALG_TKIP) encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; else if (pairwise & RNDIS_WLAN_ALG_WEP) encr_mode = NDIS_80211_ENCR_WEP_ENABLED; else if (groupwise & RNDIS_WLAN_ALG_CCMP) encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; else if (groupwise & RNDIS_WLAN_ALG_TKIP) encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; else encr_mode = NDIS_80211_ENCR_DISABLED; tmp = cpu_to_le32(encr_mode); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_ENCRYPTION_STATUS, &tmp, sizeof(tmp)); if (ret != 0) { netdev_warn(usbdev->net, "setting encr mode failed (%08X)\n", ret); return ret; } return 0; } static int set_infra_mode(struct usbnet *usbdev, int mode) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; int ret; netdev_dbg(usbdev->net, "%s(): infra_mode=0x%x\n", __func__, priv->infra_mode); tmp = cpu_to_le32(mode); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_INFRASTRUCTURE_MODE, &tmp, sizeof(tmp)); if (ret != 0) { netdev_warn(usbdev->net, "setting infra mode failed (%08X)\n", ret); return ret; } /* NDIS drivers clear keys when infrastructure mode is * changed. But Linux tools assume otherwise. So set the * keys */ restore_keys(usbdev); priv->infra_mode = mode; return 0; } static int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold) { __le32 tmp; netdev_dbg(usbdev->net, "%s(): %i\n", __func__, rts_threshold); if (rts_threshold < 0 || rts_threshold > 2347) rts_threshold = 2347; tmp = cpu_to_le32(rts_threshold); return rndis_set_oid(usbdev, RNDIS_OID_802_11_RTS_THRESHOLD, &tmp, sizeof(tmp)); } static int set_frag_threshold(struct usbnet *usbdev, u32 frag_threshold) { __le32 tmp; netdev_dbg(usbdev->net, "%s(): %i\n", __func__, frag_threshold); if (frag_threshold < 256 || frag_threshold > 2346) frag_threshold = 2346; tmp = cpu_to_le32(frag_threshold); return rndis_set_oid(usbdev, RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD, &tmp, sizeof(tmp)); } static void set_default_iw_params(struct usbnet *usbdev) { set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); set_auth_mode(usbdev, 0, NL80211_AUTHTYPE_OPEN_SYSTEM, RNDIS_WLAN_KEY_MGMT_NONE); set_priv_filter(usbdev); set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); } static int deauthenticate(struct usbnet *usbdev) { int ret; ret = disassociate(usbdev, true); set_default_iw_params(usbdev); return ret; } static int set_channel(struct usbnet *usbdev, int channel) { struct ndis_80211_conf config; unsigned int dsconfig; int len, ret; netdev_dbg(usbdev->net, "%s(%d)\n", __func__, channel); /* this OID is valid only when not associated */ if (is_associated(usbdev)) return 0; dsconfig = ieee80211_dsss_chan_to_freq(channel) * 1000; len = sizeof(config); ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_CONFIGURATION, &config, &len); if (ret < 0) { netdev_dbg(usbdev->net, "%s(): querying configuration failed\n", __func__); return ret; } config.ds_config = cpu_to_le32(dsconfig); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_CONFIGURATION, &config, sizeof(config)); netdev_dbg(usbdev->net, "%s(): %d -> %d\n", __func__, channel, ret); return ret; } static struct ieee80211_channel *get_current_channel(struct usbnet *usbdev, u32 *beacon_period) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ieee80211_channel *channel; struct ndis_80211_conf config; int len, ret; /* Get channel and beacon interval */ len = sizeof(config); ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_CONFIGURATION, &config, &len); netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_CONFIGURATION -> %d\n", __func__, ret); if (ret < 0) return NULL; channel = ieee80211_get_channel(priv->wdev.wiphy, KHZ_TO_MHZ(le32_to_cpu(config.ds_config))); if (!channel) return NULL; if (beacon_period) *beacon_period = le32_to_cpu(config.beacon_period); return channel; } /* index must be 0 - N, as per NDIS */ static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len, u8 index) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_wep_key ndis_key; u32 cipher; int ret; netdev_dbg(usbdev->net, "%s(idx: %d, len: %d)\n", __func__, index, key_len); if (index >= RNDIS_WLAN_NUM_KEYS) return -EINVAL; if (key_len == 5) cipher = WLAN_CIPHER_SUITE_WEP40; else if (key_len == 13) cipher = WLAN_CIPHER_SUITE_WEP104; else return -EINVAL; memset(&ndis_key, 0, sizeof(ndis_key)); ndis_key.size = cpu_to_le32(sizeof(ndis_key)); ndis_key.length = cpu_to_le32(key_len); ndis_key.index = cpu_to_le32(index); memcpy(&ndis_key.material, key, key_len); if (index == priv->encr_tx_key_index) { ndis_key.index |= NDIS_80211_ADDWEP_TRANSMIT_KEY; ret = set_encr_mode(usbdev, RNDIS_WLAN_ALG_WEP, RNDIS_WLAN_ALG_NONE); if (ret) netdev_warn(usbdev->net, "encryption couldn't be enabled (%08X)\n", ret); } ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_ADD_WEP, &ndis_key, sizeof(ndis_key)); if (ret != 0) { netdev_warn(usbdev->net, "adding encryption key %d failed (%08X)\n", index + 1, ret); return ret; } priv->encr_keys[index].len = key_len; priv->encr_keys[index].cipher = cipher; memcpy(&priv->encr_keys[index].material, key, key_len); memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN); return 0; } static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, u8 index, const u8 *addr, const u8 *rx_seq, int seq_len, u32 cipher, __le32 flags) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_key ndis_key; bool is_addr_ok; int ret; if (index >= RNDIS_WLAN_NUM_KEYS) { netdev_dbg(usbdev->net, "%s(): index out of range (%i)\n", __func__, index); return -EINVAL; } if (key_len > sizeof(ndis_key.material) || key_len < 0) { netdev_dbg(usbdev->net, "%s(): key length out of range (%i)\n", __func__, key_len); return -EINVAL; } if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) { if (!rx_seq || seq_len <= 0) { netdev_dbg(usbdev->net, "%s(): recv seq flag without buffer\n", __func__); return -EINVAL; } if (rx_seq && seq_len > sizeof(ndis_key.rsc)) { netdev_dbg(usbdev->net, "%s(): too big recv seq buffer\n", __func__); return -EINVAL; } } is_addr_ok = addr && !is_zero_ether_addr(addr) && !is_broadcast_ether_addr(addr); if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !is_addr_ok) { netdev_dbg(usbdev->net, "%s(): pairwise but bssid invalid (%pM)\n", __func__, addr); return -EINVAL; } netdev_dbg(usbdev->net, "%s(%i): flags:%i%i%i\n", __func__, index, !!(flags & NDIS_80211_ADDKEY_TRANSMIT_KEY), !!(flags & NDIS_80211_ADDKEY_PAIRWISE_KEY), !!(flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ)); memset(&ndis_key, 0, sizeof(ndis_key)); ndis_key.size = cpu_to_le32(sizeof(ndis_key) - sizeof(ndis_key.material) + key_len); ndis_key.length = cpu_to_le32(key_len); ndis_key.index = cpu_to_le32(index) | flags; if (cipher == WLAN_CIPHER_SUITE_TKIP && key_len == 32) { /* wpa_supplicant gives us the Michael MIC RX/TX keys in * different order than NDIS spec, so swap the order here. */ memcpy(ndis_key.material, key, 16); memcpy(ndis_key.material + 16, key + 24, 8); memcpy(ndis_key.material + 24, key + 16, 8); } else memcpy(ndis_key.material, key, key_len); if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) memcpy(ndis_key.rsc, rx_seq, seq_len); if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) { /* pairwise key */ memcpy(ndis_key.bssid, addr, ETH_ALEN); } else { /* group key */ if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) memset(ndis_key.bssid, 0xff, ETH_ALEN); else get_bssid(usbdev, ndis_key.bssid); } ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_ADD_KEY, &ndis_key, le32_to_cpu(ndis_key.size)); netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_ADD_KEY -> %08X\n", __func__, ret); if (ret != 0) return ret; memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index])); priv->encr_keys[index].len = key_len; priv->encr_keys[index].cipher = cipher; memcpy(&priv->encr_keys[index].material, key, key_len); if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) memcpy(&priv->encr_keys[index].bssid, ndis_key.bssid, ETH_ALEN); else memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN); if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY) priv->encr_tx_key_index = index; return 0; } static int restore_key(struct usbnet *usbdev, u8 key_idx) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct rndis_wlan_encr_key key; if (is_wpa_key(priv, key_idx)) return 0; key = priv->encr_keys[key_idx]; netdev_dbg(usbdev->net, "%s(): %i:%i\n", __func__, key_idx, key.len); if (key.len == 0) return 0; return add_wep_key(usbdev, key.material, key.len, key_idx); } static void restore_keys(struct usbnet *usbdev) { int i; for (i = 0; i < 4; i++) restore_key(usbdev, i); } static void clear_key(struct rndis_wlan_private *priv, u8 idx) { memset(&priv->encr_keys[idx], 0, sizeof(priv->encr_keys[idx])); } /* remove_key is for both wep and wpa */ static int remove_key(struct usbnet *usbdev, u8 index, const u8 *bssid) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_remove_key remove_key; __le32 keyindex; bool is_wpa; int ret; if (index >= RNDIS_WLAN_NUM_KEYS) return -ENOENT; if (priv->encr_keys[index].len == 0) return 0; is_wpa = is_wpa_key(priv, index); netdev_dbg(usbdev->net, "%s(): %i:%s:%i\n", __func__, index, is_wpa ? "wpa" : "wep", priv->encr_keys[index].len); clear_key(priv, index); if (is_wpa) { remove_key.size = cpu_to_le32(sizeof(remove_key)); remove_key.index = cpu_to_le32(index); if (bssid) { /* pairwise key */ if (!is_broadcast_ether_addr(bssid)) remove_key.index |= NDIS_80211_ADDKEY_PAIRWISE_KEY; memcpy(remove_key.bssid, bssid, sizeof(remove_key.bssid)); } else memset(remove_key.bssid, 0xff, sizeof(remove_key.bssid)); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_REMOVE_KEY, &remove_key, sizeof(remove_key)); if (ret != 0) return ret; } else { keyindex = cpu_to_le32(index); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_REMOVE_WEP, &keyindex, sizeof(keyindex)); if (ret != 0) { netdev_warn(usbdev->net, "removing encryption key %d failed (%08X)\n", index, ret); return ret; } } /* if it is transmit key, disable encryption */ if (index == priv->encr_tx_key_index) set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); return 0; } static void set_multicast_list(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct netdev_hw_addr *ha; __le32 filter, basefilter; int ret; char *mc_addrs = NULL; int mc_count; basefilter = filter = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST); if (usbdev->net->flags & IFF_PROMISC) { filter |= cpu_to_le32(RNDIS_PACKET_TYPE_PROMISCUOUS | RNDIS_PACKET_TYPE_ALL_LOCAL); } else if (usbdev->net->flags & IFF_ALLMULTI) { filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); } if (filter != basefilter) goto set_filter; /* * mc_list should be accessed holding the lock, so copy addresses to * local buffer first. */ netif_addr_lock_bh(usbdev->net); mc_count = netdev_mc_count(usbdev->net); if (mc_count > priv->multicast_size) { filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); } else if (mc_count) { int i = 0; mc_addrs = kmalloc(mc_count * ETH_ALEN, GFP_ATOMIC); if (!mc_addrs) { netdev_warn(usbdev->net, "couldn't alloc %d bytes of memory\n", mc_count * ETH_ALEN); netif_addr_unlock_bh(usbdev->net); return; } netdev_for_each_mc_addr(ha, usbdev->net) memcpy(mc_addrs + i++ * ETH_ALEN, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr, ETH_ALEN); #else ha->dmi_addr, ETH_ALEN); #endif } netif_addr_unlock_bh(usbdev->net); if (filter != basefilter) goto set_filter; if (mc_count) { ret = rndis_set_oid(usbdev, RNDIS_OID_802_3_MULTICAST_LIST, mc_addrs, mc_count * ETH_ALEN); kfree(mc_addrs); if (ret == 0) filter |= cpu_to_le32(RNDIS_PACKET_TYPE_MULTICAST); else filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); netdev_dbg(usbdev->net, "RNDIS_OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n", mc_count, priv->multicast_size, ret); } set_filter: ret = rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, sizeof(filter)); if (ret < 0) { netdev_warn(usbdev->net, "couldn't set packet filter: %08x\n", le32_to_cpu(filter)); } netdev_dbg(usbdev->net, "RNDIS_OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d\n", le32_to_cpu(filter), ret); } #ifdef DEBUG static void debug_print_pmkids(struct usbnet *usbdev, struct ndis_80211_pmkid *pmkids, const char *func_str) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int i, len, count, max_pmkids, entry_len; max_pmkids = priv->wdev.wiphy->max_num_pmkids; len = le32_to_cpu(pmkids->length); count = le32_to_cpu(pmkids->bssid_info_count); entry_len = (count > 0) ? (len - sizeof(*pmkids)) / count : -1; netdev_dbg(usbdev->net, "%s(): %d PMKIDs (data len: %d, entry len: " "%d)\n", func_str, count, len, entry_len); if (count > max_pmkids) count = max_pmkids; for (i = 0; i < count; i++) { u32 *tmp = (u32 *)pmkids->bssid_info[i].pmkid; netdev_dbg(usbdev->net, "%s(): bssid: %pM, " "pmkid: %08X:%08X:%08X:%08X\n", func_str, pmkids->bssid_info[i].bssid, cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); } } #else static void debug_print_pmkids(struct usbnet *usbdev, struct ndis_80211_pmkid *pmkids, const char *func_str) { return; } #endif static struct ndis_80211_pmkid *get_device_pmkids(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_pmkid *pmkids; int len, ret, max_pmkids; max_pmkids = priv->wdev.wiphy->max_num_pmkids; len = sizeof(*pmkids) + max_pmkids * sizeof(pmkids->bssid_info[0]); pmkids = kzalloc(len, GFP_KERNEL); if (!pmkids) return ERR_PTR(-ENOMEM); pmkids->length = cpu_to_le32(len); pmkids->bssid_info_count = cpu_to_le32(max_pmkids); ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_PMKID, pmkids, &len); if (ret < 0) { netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d)" " -> %d\n", __func__, len, max_pmkids, ret); kfree(pmkids); return ERR_PTR(ret); } if (le32_to_cpu(pmkids->bssid_info_count) > max_pmkids) pmkids->bssid_info_count = cpu_to_le32(max_pmkids); debug_print_pmkids(usbdev, pmkids, __func__); return pmkids; } static int set_device_pmkids(struct usbnet *usbdev, struct ndis_80211_pmkid *pmkids) { int ret, len, num_pmkids; num_pmkids = le32_to_cpu(pmkids->bssid_info_count); len = sizeof(*pmkids) + num_pmkids * sizeof(pmkids->bssid_info[0]); pmkids->length = cpu_to_le32(len); debug_print_pmkids(usbdev, pmkids, __func__); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, pmkids, le32_to_cpu(pmkids->length)); if (ret < 0) { netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d) -> %d" "\n", __func__, len, num_pmkids, ret); } kfree(pmkids); return ret; } static struct ndis_80211_pmkid *remove_pmkid(struct usbnet *usbdev, struct ndis_80211_pmkid *pmkids, struct cfg80211_pmksa *pmksa, int max_pmkids) { int i, newlen, err; unsigned int count; count = le32_to_cpu(pmkids->bssid_info_count); if (count > max_pmkids) count = max_pmkids; for (i = 0; i < count; i++) if (ether_addr_equal(pmkids->bssid_info[i].bssid, pmksa->bssid)) break; /* pmkid not found */ if (i == count) { netdev_dbg(usbdev->net, "%s(): bssid not found (%pM)\n", __func__, pmksa->bssid); err = -ENOENT; goto error; } for (; i + 1 < count; i++) pmkids->bssid_info[i] = pmkids->bssid_info[i + 1]; count--; newlen = sizeof(*pmkids) + count * sizeof(pmkids->bssid_info[0]); pmkids->length = cpu_to_le32(newlen); pmkids->bssid_info_count = cpu_to_le32(count); return pmkids; error: kfree(pmkids); return ERR_PTR(err); } static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev, struct ndis_80211_pmkid *pmkids, struct cfg80211_pmksa *pmksa, int max_pmkids) { struct ndis_80211_pmkid *new_pmkids; int i, err, newlen; unsigned int count; count = le32_to_cpu(pmkids->bssid_info_count); if (count > max_pmkids) count = max_pmkids; /* update with new pmkid */ for (i = 0; i < count; i++) { if (!ether_addr_equal(pmkids->bssid_info[i].bssid, pmksa->bssid)) continue; memcpy(pmkids->bssid_info[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); return pmkids; } /* out of space, return error */ if (i == max_pmkids) { netdev_dbg(usbdev->net, "%s(): out of space\n", __func__); err = -ENOSPC; goto error; } /* add new pmkid */ newlen = sizeof(*pmkids) + (count + 1) * sizeof(pmkids->bssid_info[0]); new_pmkids = krealloc(pmkids, newlen, GFP_KERNEL); if (!new_pmkids) { err = -ENOMEM; goto error; } pmkids = new_pmkids; pmkids->length = cpu_to_le32(newlen); pmkids->bssid_info_count = cpu_to_le32(count + 1); memcpy(pmkids->bssid_info[count].bssid, pmksa->bssid, ETH_ALEN); memcpy(pmkids->bssid_info[count].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); return pmkids; error: kfree(pmkids); return ERR_PTR(err); } /* * cfg80211 ops */ static int rndis_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; int mode; switch (type) { case NL80211_IFTYPE_ADHOC: mode = NDIS_80211_INFRA_ADHOC; break; case NL80211_IFTYPE_STATION: mode = NDIS_80211_INFRA_INFRA; break; default: return -EINVAL; } priv->wdev.iftype = type; return set_infra_mode(usbdev, mode); } static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; int err; if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { err = set_frag_threshold(usbdev, wiphy->frag_threshold); if (err < 0) return err; } if (changed & WIPHY_PARAM_RTS_THRESHOLD) { err = set_rts_threshold(usbdev, wiphy->rts_threshold); if (err < 0) return err; } return 0; } static int rndis_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; netdev_dbg(usbdev->net, "%s(): type:0x%x mbm:%i\n", __func__, type, mbm); if (mbm < 0 || (mbm % 100)) return -ENOTSUPP; /* Device doesn't support changing txpower after initialization, only * turn off/on radio. Support 'auto' mode and setting same dBm that is * currently used. */ if (type == NL80211_TX_POWER_AUTOMATIC || MBM_TO_DBM(mbm) == get_bcm4320_power_dbm(priv)) { if (!priv->radio_on) disassociate(usbdev, true); /* turn on radio */ return 0; } return -ENOTSUPP; } static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; *dbm = get_bcm4320_power_dbm(priv); netdev_dbg(usbdev->net, "%s(): dbm:%i\n", __func__, *dbm); return 0; } #define SCAN_DELAY_JIFFIES (6 * HZ) static int rndis_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct net_device *dev = request->wdev->netdev; struct usbnet *usbdev = netdev_priv(dev); struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int ret; int delay = SCAN_DELAY_JIFFIES; netdev_dbg(usbdev->net, "cfg80211.scan\n"); /* Get current bssid list from device before new scan, as new scan * clears internal bssid list. */ rndis_check_bssid_list(usbdev, NULL, NULL); if (priv->scan_request && priv->scan_request != request) return -EBUSY; priv->scan_request = request; ret = rndis_start_bssid_list_scan(usbdev); if (ret == 0) { if (priv->device_type == RNDIS_BCM4320A) delay = HZ; /* Wait before retrieving scan results from device */ queue_delayed_work(priv->workqueue, &priv->scan_work, delay); } return ret; } static bool rndis_bss_info_update(struct usbnet *usbdev, struct ndis_80211_bssid_ex *bssid) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ieee80211_channel *channel; struct cfg80211_bss *bss; s32 signal; u64 timestamp; u16 capability; u16 beacon_interval; struct ndis_80211_fixed_ies *fixed; int ie_len, bssid_len; u8 *ie; netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM], len: %d\n", bssid->ssid.essid, bssid->mac, le32_to_cpu(bssid->length)); /* parse bssid structure */ bssid_len = le32_to_cpu(bssid->length); if (bssid_len < sizeof(struct ndis_80211_bssid_ex) + sizeof(struct ndis_80211_fixed_ies)) return NULL; fixed = (struct ndis_80211_fixed_ies *)bssid->ies; ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); ie_len = min(bssid_len - (int)sizeof(*bssid), (int)le32_to_cpu(bssid->ie_length)); ie_len -= sizeof(struct ndis_80211_fixed_ies); if (ie_len < 0) return NULL; /* extract data for cfg80211_inform_bss */ channel = ieee80211_get_channel(priv->wdev.wiphy, KHZ_TO_MHZ(le32_to_cpu(bssid->config.ds_config))); if (!channel) return NULL; signal = level_to_qual(le32_to_cpu(bssid->rssi)); timestamp = le64_to_cpu(*(__le64 *)fixed->timestamp); capability = le16_to_cpu(fixed->capabilities); beacon_interval = le16_to_cpu(fixed->beacon_interval); bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac, timestamp, capability, beacon_interval, ie, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(bss); return (bss != NULL); } static struct ndis_80211_bssid_ex *next_bssid_list_item( struct ndis_80211_bssid_ex *bssid, int *bssid_len, void *buf, int len) { void *buf_end, *bssid_end; buf_end = (char *)buf + len; bssid_end = (char *)bssid + *bssid_len; if ((int)(buf_end - bssid_end) < sizeof(bssid->length)) { *bssid_len = 0; return NULL; } else { bssid = (void *)((char *)bssid + *bssid_len); *bssid_len = le32_to_cpu(bssid->length); return bssid; } } static bool check_bssid_list_item(struct ndis_80211_bssid_ex *bssid, int bssid_len, void *buf, int len) { void *buf_end, *bssid_end; if (!bssid || bssid_len <= 0 || bssid_len > len) return false; buf_end = (char *)buf + len; bssid_end = (char *)bssid + bssid_len; return (int)(buf_end - bssid_end) >= 0 && (int)(bssid_end - buf) >= 0; } static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, bool *matched) { void *buf = NULL; struct ndis_80211_bssid_list_ex *bssid_list; struct ndis_80211_bssid_ex *bssid; int ret = -EINVAL, len, count, bssid_len, real_count, new_len; netdev_dbg(usbdev->net, "%s()\n", __func__); len = CONTROL_BUFFER_SIZE; resize_buf: buf = kzalloc(len, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto out; } /* BSSID-list might have got bigger last time we checked, keep * resizing until it won't get any bigger. */ new_len = len; ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST, buf, &new_len); if (ret != 0 || new_len < sizeof(struct ndis_80211_bssid_list_ex)) goto out; if (new_len > len) { len = new_len; kfree(buf); goto resize_buf; } len = new_len; bssid_list = buf; count = le32_to_cpu(bssid_list->num_items); real_count = 0; netdev_dbg(usbdev->net, "%s(): buflen: %d\n", __func__, len); bssid_len = 0; bssid = next_bssid_list_item(bssid_list->bssid, &bssid_len, buf, len); /* Device returns incorrect 'num_items'. Workaround by ignoring the * received 'num_items' and walking through full bssid buffer instead. */ while (check_bssid_list_item(bssid, bssid_len, buf, len)) { if (rndis_bss_info_update(usbdev, bssid) && match_bssid && matched) { if (ether_addr_equal(bssid->mac, match_bssid)) *matched = true; } real_count++; bssid = next_bssid_list_item(bssid, &bssid_len, buf, len); } netdev_dbg(usbdev->net, "%s(): num_items from device: %d, really found:" " %d\n", __func__, count, real_count); out: kfree(buf); return ret; } static void rndis_get_scan_results(struct work_struct *work) { struct rndis_wlan_private *priv = container_of(work, struct rndis_wlan_private, scan_work.work); struct usbnet *usbdev = priv->usbdev; int ret; netdev_dbg(usbdev->net, "get_scan_results\n"); if (!priv->scan_request) return; ret = rndis_check_bssid_list(usbdev, NULL, NULL); cfg80211_scan_done(priv->scan_request, ret < 0); priv->scan_request = NULL; } static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; struct ieee80211_channel *channel = sme->channel; struct ndis_80211_ssid ssid; int pairwise = RNDIS_WLAN_ALG_NONE; int groupwise = RNDIS_WLAN_ALG_NONE; int keymgmt = RNDIS_WLAN_KEY_MGMT_NONE; int length, i, ret, chan = -1; if (channel) chan = ieee80211_frequency_to_channel(channel->center_freq); groupwise = rndis_cipher_to_alg(sme->crypto.cipher_group); for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) pairwise |= rndis_cipher_to_alg(sme->crypto.ciphers_pairwise[i]); if (sme->crypto.n_ciphers_pairwise > 0 && pairwise == RNDIS_WLAN_ALG_NONE) { netdev_err(usbdev->net, "Unsupported pairwise cipher\n"); return -ENOTSUPP; } for (i = 0; i < sme->crypto.n_akm_suites; i++) keymgmt |= rndis_akm_suite_to_key_mgmt(sme->crypto.akm_suites[i]); if (sme->crypto.n_akm_suites > 0 && keymgmt == RNDIS_WLAN_KEY_MGMT_NONE) { netdev_err(usbdev->net, "Invalid keymgmt\n"); return -ENOTSUPP; } netdev_dbg(usbdev->net, "cfg80211.connect('%.32s':[%pM]:%d:[%d,0x%x:0x%x]:[0x%x:0x%x]:0x%x)\n", sme->ssid, sme->bssid, chan, sme->privacy, sme->crypto.wpa_versions, sme->auth_type, groupwise, pairwise, keymgmt); if (is_associated(usbdev)) disassociate(usbdev, false); ret = set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); if (ret < 0) { netdev_dbg(usbdev->net, "connect: set_infra_mode failed, %d\n", ret); goto err_turn_radio_on; } ret = set_auth_mode(usbdev, sme->crypto.wpa_versions, sme->auth_type, keymgmt); if (ret < 0) { netdev_dbg(usbdev->net, "connect: set_auth_mode failed, %d\n", ret); goto err_turn_radio_on; } set_priv_filter(usbdev); ret = set_encr_mode(usbdev, pairwise, groupwise); if (ret < 0) { netdev_dbg(usbdev->net, "connect: set_encr_mode failed, %d\n", ret); goto err_turn_radio_on; } if (channel) { ret = set_channel(usbdev, chan); if (ret < 0) { netdev_dbg(usbdev->net, "connect: set_channel failed, %d\n", ret); goto err_turn_radio_on; } } if (sme->key && ((groupwise | pairwise) & RNDIS_WLAN_ALG_WEP)) { priv->encr_tx_key_index = sme->key_idx; ret = add_wep_key(usbdev, sme->key, sme->key_len, sme->key_idx); if (ret < 0) { netdev_dbg(usbdev->net, "connect: add_wep_key failed, %d (%d, %d)\n", ret, sme->key_len, sme->key_idx); goto err_turn_radio_on; } } if (sme->bssid && !is_zero_ether_addr(sme->bssid) && !is_broadcast_ether_addr(sme->bssid)) { ret = set_bssid(usbdev, sme->bssid); if (ret < 0) { netdev_dbg(usbdev->net, "connect: set_bssid failed, %d\n", ret); goto err_turn_radio_on; } } else clear_bssid(usbdev); length = sme->ssid_len; if (length > NDIS_802_11_LENGTH_SSID) length = NDIS_802_11_LENGTH_SSID; memset(&ssid, 0, sizeof(ssid)); ssid.length = cpu_to_le32(length); memcpy(ssid.essid, sme->ssid, length); /* Pause and purge rx queue, so we don't pass packets before * 'media connect'-indication. */ usbnet_pause_rx(usbdev); usbnet_purge_paused_rxq(usbdev); ret = set_essid(usbdev, &ssid); if (ret < 0) netdev_dbg(usbdev->net, "connect: set_essid failed, %d\n", ret); return ret; err_turn_radio_on: disassociate(usbdev, true); return ret; } static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; netdev_dbg(usbdev->net, "cfg80211.disconnect(%d)\n", reason_code); priv->connected = false; memset(priv->bssid, 0, ETH_ALEN); return deauthenticate(usbdev); } static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; struct ieee80211_channel *channel = params->channel; struct ndis_80211_ssid ssid; enum nl80211_auth_type auth_type; int ret, alg, length, chan = -1; if (channel) chan = ieee80211_frequency_to_channel(channel->center_freq); /* TODO: How to handle ad-hoc encryption? * connect() has *key, join_ibss() doesn't. RNDIS requires key to be * pre-shared for encryption (open/shared/wpa), is key set before * join_ibss? Which auth_type to use (not in params)? What about WPA? */ if (params->privacy) { auth_type = NL80211_AUTHTYPE_SHARED_KEY; alg = RNDIS_WLAN_ALG_WEP; } else { auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; alg = RNDIS_WLAN_ALG_NONE; } netdev_dbg(usbdev->net, "cfg80211.join_ibss('%.32s':[%pM]:%d:%d)\n", params->ssid, params->bssid, chan, params->privacy); if (is_associated(usbdev)) disassociate(usbdev, false); ret = set_infra_mode(usbdev, NDIS_80211_INFRA_ADHOC); if (ret < 0) { netdev_dbg(usbdev->net, "join_ibss: set_infra_mode failed, %d\n", ret); goto err_turn_radio_on; } ret = set_auth_mode(usbdev, 0, auth_type, RNDIS_WLAN_KEY_MGMT_NONE); if (ret < 0) { netdev_dbg(usbdev->net, "join_ibss: set_auth_mode failed, %d\n", ret); goto err_turn_radio_on; } set_priv_filter(usbdev); ret = set_encr_mode(usbdev, alg, RNDIS_WLAN_ALG_NONE); if (ret < 0) { netdev_dbg(usbdev->net, "join_ibss: set_encr_mode failed, %d\n", ret); goto err_turn_radio_on; } if (channel) { ret = set_channel(usbdev, chan); if (ret < 0) { netdev_dbg(usbdev->net, "join_ibss: set_channel failed, %d\n", ret); goto err_turn_radio_on; } } if (params->bssid && !is_zero_ether_addr(params->bssid) && !is_broadcast_ether_addr(params->bssid)) { ret = set_bssid(usbdev, params->bssid); if (ret < 0) { netdev_dbg(usbdev->net, "join_ibss: set_bssid failed, %d\n", ret); goto err_turn_radio_on; } } else clear_bssid(usbdev); length = params->ssid_len; if (length > NDIS_802_11_LENGTH_SSID) length = NDIS_802_11_LENGTH_SSID; memset(&ssid, 0, sizeof(ssid)); ssid.length = cpu_to_le32(length); memcpy(ssid.essid, params->ssid, length); /* Don't need to pause rx queue for ad-hoc. */ usbnet_purge_paused_rxq(usbdev); usbnet_resume_rx(usbdev); ret = set_essid(usbdev, &ssid); if (ret < 0) netdev_dbg(usbdev->net, "join_ibss: set_essid failed, %d\n", ret); return ret; err_turn_radio_on: disassociate(usbdev, true); return ret; } static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; netdev_dbg(usbdev->net, "cfg80211.leave_ibss()\n"); priv->connected = false; memset(priv->bssid, 0, ETH_ALEN); return deauthenticate(usbdev); } static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; __le32 flags; netdev_dbg(usbdev->net, "%s(%i, %pM, %08x)\n", __func__, key_index, mac_addr, params->cipher); switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: return add_wep_key(usbdev, params->key, params->key_len, key_index); case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_CCMP: flags = 0; if (params->seq && params->seq_len > 0) flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ; if (mac_addr) flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY | NDIS_80211_ADDKEY_TRANSMIT_KEY; return add_wpa_key(usbdev, params->key, params->key_len, key_index, mac_addr, params->seq, params->seq_len, params->cipher, flags); default: netdev_dbg(usbdev->net, "%s(): unsupported cipher %08x\n", __func__, params->cipher); return -ENOTSUPP; } } static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; netdev_dbg(usbdev->net, "%s(%i, %pM)\n", __func__, key_index, mac_addr); return remove_key(usbdev, key_index, mac_addr); } static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; struct rndis_wlan_encr_key key; netdev_dbg(usbdev->net, "%s(%i)\n", __func__, key_index); if (key_index >= RNDIS_WLAN_NUM_KEYS) return -ENOENT; priv->encr_tx_key_index = key_index; if (is_wpa_key(priv, key_index)) return 0; key = priv->encr_keys[key_index]; return add_wep_key(usbdev, key.material, key.len, key_index); } static void rndis_fill_station_info(struct usbnet *usbdev, struct station_info *sinfo) { __le32 linkspeed, rssi; int ret, len; memset(sinfo, 0, sizeof(*sinfo)); len = sizeof(linkspeed); ret = rndis_query_oid(usbdev, RNDIS_OID_GEN_LINK_SPEED, &linkspeed, &len); if (ret == 0) { sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000; sinfo->filled |= STATION_INFO_TX_BITRATE; } len = sizeof(rssi); ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, &rssi, &len); if (ret == 0) { sinfo->signal = level_to_qual(le32_to_cpu(rssi)); sinfo->filled |= STATION_INFO_SIGNAL; } } static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; if (!ether_addr_equal(priv->bssid, mac)) return -ENOENT; rndis_fill_station_info(usbdev, sinfo); return 0; } static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; if (idx != 0) return -ENOENT; memcpy(mac, priv->bssid, ETH_ALEN); rndis_fill_station_info(usbdev, sinfo); return 0; } static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; struct ndis_80211_pmkid *pmkids; u32 *tmp = (u32 *)pmksa->pmkid; netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, pmksa->bssid, cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); pmkids = get_device_pmkids(usbdev); if (IS_ERR(pmkids)) { /* couldn't read PMKID cache from device */ return PTR_ERR(pmkids); } pmkids = update_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); if (IS_ERR(pmkids)) { /* not found, list full, etc */ return PTR_ERR(pmkids); } return set_device_pmkids(usbdev, pmkids); } static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; struct ndis_80211_pmkid *pmkids; u32 *tmp = (u32 *)pmksa->pmkid; netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, pmksa->bssid, cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); pmkids = get_device_pmkids(usbdev); if (IS_ERR(pmkids)) { /* Couldn't read PMKID cache from device */ return PTR_ERR(pmkids); } pmkids = remove_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); if (IS_ERR(pmkids)) { /* not found, etc */ return PTR_ERR(pmkids); } return set_device_pmkids(usbdev, pmkids); } static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; struct ndis_80211_pmkid pmkid; netdev_dbg(usbdev->net, "%s()\n", __func__); memset(&pmkid, 0, sizeof(pmkid)); pmkid.length = cpu_to_le32(sizeof(pmkid)); pmkid.bssid_info_count = cpu_to_le32(0); return rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, &pmkid, sizeof(pmkid)); } static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; int power_mode; __le32 mode; int ret; if (priv->device_type != RNDIS_BCM4320B) return -ENOTSUPP; netdev_dbg(usbdev->net, "%s(): %s, %d\n", __func__, enabled ? "enabled" : "disabled", timeout); if (enabled) power_mode = NDIS_80211_POWER_MODE_FAST_PSP; else power_mode = NDIS_80211_POWER_MODE_CAM; if (power_mode == priv->power_mode) return 0; priv->power_mode = power_mode; mode = cpu_to_le32(power_mode); ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_POWER_MODE, &mode, sizeof(mode)); netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_POWER_MODE -> %d\n", __func__, ret); return ret; } static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); priv->cqm_rssi_thold = rssi_thold; priv->cqm_rssi_hyst = rssi_hyst; priv->last_cqm_event_rssi = 0; return 0; } static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, struct ndis_80211_assoc_info *info) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ieee80211_channel *channel; struct ndis_80211_ssid ssid; struct cfg80211_bss *bss; s32 signal; u64 timestamp; u16 capability; u32 beacon_period = 0; __le32 rssi; u8 ie_buf[34]; int len, ret, ie_len; /* Get signal quality, in case of error use rssi=0 and ignore error. */ len = sizeof(rssi); rssi = 0; ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, &rssi, &len); signal = level_to_qual(le32_to_cpu(rssi)); netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_RSSI -> %d, " "rssi:%d, qual: %d\n", __func__, ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); /* Get AP capabilities */ if (info) { capability = le16_to_cpu(info->resp_ie.capa); } else { /* Set atleast ESS/IBSS capability */ capability = (priv->infra_mode == NDIS_80211_INFRA_INFRA) ? WLAN_CAPABILITY_ESS : WLAN_CAPABILITY_IBSS; } /* Get channel and beacon interval */ channel = get_current_channel(usbdev, &beacon_period); if (!channel) { netdev_warn(usbdev->net, "%s(): could not get channel.\n", __func__); return; } /* Get SSID, in case of error, use zero length SSID and ignore error. */ len = sizeof(ssid); memset(&ssid, 0, sizeof(ssid)); ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_SSID, &ssid, &len); netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_SSID -> %d, len: %d, ssid: " "'%.32s'\n", __func__, ret, le32_to_cpu(ssid.length), ssid.essid); if (le32_to_cpu(ssid.length) > 32) ssid.length = cpu_to_le32(32); ie_buf[0] = WLAN_EID_SSID; ie_buf[1] = le32_to_cpu(ssid.length); memcpy(&ie_buf[2], ssid.essid, le32_to_cpu(ssid.length)); ie_len = le32_to_cpu(ssid.length) + 2; /* no tsf */ timestamp = 0; netdev_dbg(usbdev->net, "%s(): channel:%d(freq), bssid:[%pM], tsf:%d, " "capa:%x, beacon int:%d, resp_ie(len:%d, essid:'%.32s'), " "signal:%d\n", __func__, (channel ? channel->center_freq : -1), bssid, (u32)timestamp, capability, beacon_period, ie_len, ssid.essid, signal); bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid, timestamp, capability, beacon_period, ie_buf, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(bss); } /* * workers, indication handlers, device poller */ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_assoc_info *info = NULL; u8 bssid[ETH_ALEN]; unsigned int resp_ie_len, req_ie_len; unsigned int offset; u8 *req_ie, *resp_ie; int ret; bool roamed = false; bool match_bss; if (priv->infra_mode == NDIS_80211_INFRA_INFRA && priv->connected) { /* received media connect indication while connected, either * device reassociated with same AP or roamed to new. */ roamed = true; } req_ie_len = 0; resp_ie_len = 0; req_ie = NULL; resp_ie = NULL; if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { info = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (!info) { /* No memory? Try resume work later */ set_bit(WORK_LINK_UP, &priv->work_pending); queue_work(priv->workqueue, &priv->work); return; } /* Get association info IEs from device. */ ret = get_association_info(usbdev, info, CONTROL_BUFFER_SIZE); if (!ret) { req_ie_len = le32_to_cpu(info->req_ie_length); if (req_ie_len > CONTROL_BUFFER_SIZE) req_ie_len = CONTROL_BUFFER_SIZE; if (req_ie_len != 0) { offset = le32_to_cpu(info->offset_req_ies); if (offset > CONTROL_BUFFER_SIZE) offset = CONTROL_BUFFER_SIZE; req_ie = (u8 *)info + offset; if (offset + req_ie_len > CONTROL_BUFFER_SIZE) req_ie_len = CONTROL_BUFFER_SIZE - offset; } resp_ie_len = le32_to_cpu(info->resp_ie_length); if (resp_ie_len > CONTROL_BUFFER_SIZE) resp_ie_len = CONTROL_BUFFER_SIZE; if (resp_ie_len != 0) { offset = le32_to_cpu(info->offset_resp_ies); if (offset > CONTROL_BUFFER_SIZE) offset = CONTROL_BUFFER_SIZE; resp_ie = (u8 *)info + offset; if (offset + resp_ie_len > CONTROL_BUFFER_SIZE) resp_ie_len = CONTROL_BUFFER_SIZE - offset; } } else { /* Since rndis_wlan_craft_connected_bss() might use info * later and expects info to contain valid data if * non-null, free info and set NULL here. */ kfree(info); info = NULL; } } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC)) return; ret = get_bssid(usbdev, bssid); if (ret < 0) memset(bssid, 0, sizeof(bssid)); netdev_dbg(usbdev->net, "link up work: [%pM]%s\n", bssid, roamed ? " roamed" : ""); /* Internal bss list in device should contain at least the currently * connected bss and we can get it to cfg80211 with * rndis_check_bssid_list(). * * NDIS spec says: "If the device is associated, but the associated * BSSID is not in its BSSID scan list, then the driver must add an * entry for the BSSID at the end of the data that it returns in * response to query of RNDIS_OID_802_11_BSSID_LIST." * * NOTE: Seems to be true for BCM4320b variant, but not BCM4320a. */ match_bss = false; rndis_check_bssid_list(usbdev, bssid, &match_bss); if (!is_zero_ether_addr(bssid) && !match_bss) { /* Couldn't get bss from device, we need to manually craft bss * for cfg80211. */ rndis_wlan_craft_connected_bss(usbdev, bssid, info); } if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { if (!roamed) cfg80211_connect_result(usbdev->net, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, 0, GFP_KERNEL); else cfg80211_roamed(usbdev->net, get_current_channel(usbdev, NULL), bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, GFP_KERNEL); } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL); if (info != NULL) kfree(info); priv->connected = true; memcpy(priv->bssid, bssid, ETH_ALEN); usbnet_resume_rx(usbdev); netif_carrier_on(usbdev->net); } static void rndis_wlan_do_link_down_work(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); if (priv->connected) { priv->connected = false; memset(priv->bssid, 0, ETH_ALEN); deauthenticate(usbdev); cfg80211_disconnected(usbdev->net, 0, NULL, 0, GFP_KERNEL); } netif_carrier_off(usbdev->net); } static void rndis_wlan_worker(struct work_struct *work) { struct rndis_wlan_private *priv = container_of(work, struct rndis_wlan_private, work); struct usbnet *usbdev = priv->usbdev; if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) rndis_wlan_do_link_up_work(usbdev); if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) rndis_wlan_do_link_down_work(usbdev); if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) set_multicast_list(usbdev); } static void rndis_wlan_set_multicast_list(struct net_device *dev) { struct usbnet *usbdev = netdev_priv(dev); struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) return; set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending); queue_work(priv->workqueue, &priv->work); } static void rndis_wlan_auth_indication(struct usbnet *usbdev, struct ndis_80211_status_indication *indication, int len) { u8 *buf; const char *type; int flags, buflen, key_id; bool pairwise_error, group_error; struct ndis_80211_auth_request *auth_req; enum nl80211_key_type key_type; /* must have at least one array entry */ if (len < offsetof(struct ndis_80211_status_indication, u) + sizeof(struct ndis_80211_auth_request)) { netdev_info(usbdev->net, "authentication indication: too short message (%i)\n", len); return; } buf = (void *)&indication->u.auth_request[0]; buflen = len - offsetof(struct ndis_80211_status_indication, u); while (buflen >= sizeof(*auth_req)) { auth_req = (void *)buf; type = "unknown"; flags = le32_to_cpu(auth_req->flags); pairwise_error = false; group_error = false; if (flags & 0x1) type = "reauth request"; if (flags & 0x2) type = "key update request"; if (flags & 0x6) { pairwise_error = true; type = "pairwise_error"; } if (flags & 0xe) { group_error = true; type = "group_error"; } netdev_info(usbdev->net, "authentication indication: %s (0x%08x)\n", type, le32_to_cpu(auth_req->flags)); if (pairwise_error) { key_type = NL80211_KEYTYPE_PAIRWISE; key_id = -1; cfg80211_michael_mic_failure(usbdev->net, auth_req->bssid, key_type, key_id, NULL, GFP_KERNEL); } if (group_error) { key_type = NL80211_KEYTYPE_GROUP; key_id = -1; cfg80211_michael_mic_failure(usbdev->net, auth_req->bssid, key_type, key_id, NULL, GFP_KERNEL); } buflen -= le32_to_cpu(auth_req->length); buf += le32_to_cpu(auth_req->length); } } static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev, struct ndis_80211_status_indication *indication, int len) { struct ndis_80211_pmkid_cand_list *cand_list; int list_len, expected_len, i; if (len < offsetof(struct ndis_80211_status_indication, u) + sizeof(struct ndis_80211_pmkid_cand_list)) { netdev_info(usbdev->net, "pmkid candidate list indication: too short message (%i)\n", len); return; } list_len = le32_to_cpu(indication->u.cand_list.num_candidates) * sizeof(struct ndis_80211_pmkid_candidate); expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len + offsetof(struct ndis_80211_status_indication, u); if (len < expected_len) { netdev_info(usbdev->net, "pmkid candidate list indication: list larger than buffer (%i < %i)\n", len, expected_len); return; } cand_list = &indication->u.cand_list; netdev_info(usbdev->net, "pmkid candidate list indication: version %i, candidates %i\n", le32_to_cpu(cand_list->version), le32_to_cpu(cand_list->num_candidates)); if (le32_to_cpu(cand_list->version) != 1) return; for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) { struct ndis_80211_pmkid_candidate *cand = &cand_list->candidate_list[i]; bool preauth = !!(cand->flags & NDIS_80211_PMKID_CAND_PREAUTH); netdev_dbg(usbdev->net, "cand[%i]: flags: 0x%08x, preauth: %d, bssid: %pM\n", i, le32_to_cpu(cand->flags), preauth, cand->bssid); cfg80211_pmksa_candidate_notify(usbdev->net, i, cand->bssid, preauth, GFP_ATOMIC); } } static void rndis_wlan_media_specific_indication(struct usbnet *usbdev, struct rndis_indicate *msg, int buflen) { struct ndis_80211_status_indication *indication; unsigned int len, offset; offset = offsetof(struct rndis_indicate, status) + le32_to_cpu(msg->offset); len = le32_to_cpu(msg->length); if (len < 8) { netdev_info(usbdev->net, "media specific indication, ignore too short message (%i < 8)\n", len); return; } if (len > buflen || offset > buflen || offset + len > buflen) { netdev_info(usbdev->net, "media specific indication, too large to fit to buffer (%i > %i)\n", offset + len, buflen); return; } indication = (void *)((u8 *)msg + offset); switch (le32_to_cpu(indication->status_type)) { case NDIS_80211_STATUSTYPE_RADIOSTATE: netdev_info(usbdev->net, "radio state indication: %i\n", le32_to_cpu(indication->u.radio_status)); return; case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE: netdev_info(usbdev->net, "media stream mode indication: %i\n", le32_to_cpu(indication->u.media_stream_mode)); return; case NDIS_80211_STATUSTYPE_AUTHENTICATION: rndis_wlan_auth_indication(usbdev, indication, len); return; case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST: rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len); return; default: netdev_info(usbdev->net, "media specific indication: unknown status type 0x%08x\n", le32_to_cpu(indication->status_type)); } } static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct rndis_indicate *msg = ind; switch (le32_to_cpu(msg->status)) { case RNDIS_STATUS_MEDIA_CONNECT: if (priv->current_command_oid == RNDIS_OID_802_11_ADD_KEY) { /* RNDIS_OID_802_11_ADD_KEY causes sometimes extra * "media connect" indications which confuses driver * and userspace to think that device is * roaming/reassociating when it isn't. */ netdev_dbg(usbdev->net, "ignored RNDIS_OID_802_11_ADD_KEY triggered 'media connect'\n"); return; } usbnet_pause_rx(usbdev); netdev_info(usbdev->net, "media connect\n"); /* queue work to avoid recursive calls into rndis_command */ set_bit(WORK_LINK_UP, &priv->work_pending); queue_work(priv->workqueue, &priv->work); break; case RNDIS_STATUS_MEDIA_DISCONNECT: netdev_info(usbdev->net, "media disconnect\n"); /* queue work to avoid recursive calls into rndis_command */ set_bit(WORK_LINK_DOWN, &priv->work_pending); queue_work(priv->workqueue, &priv->work); break; case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION: rndis_wlan_media_specific_indication(usbdev, msg, buflen); break; default: netdev_info(usbdev->net, "indication: 0x%08x\n", le32_to_cpu(msg->status)); break; } } static int rndis_wlan_get_caps(struct usbnet *usbdev, struct wiphy *wiphy) { struct { __le32 num_items; __le32 items[8]; } networks_supported; struct ndis_80211_capability *caps; u8 caps_buf[sizeof(*caps) + sizeof(caps->auth_encr_pair) * 16]; int len, retval, i, n; struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); /* determine supported modes */ len = sizeof(networks_supported); retval = rndis_query_oid(usbdev, RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED, &networks_supported, &len); if (retval >= 0) { n = le32_to_cpu(networks_supported.num_items); if (n > 8) n = 8; for (i = 0; i < n; i++) { switch (le32_to_cpu(networks_supported.items[i])) { case NDIS_80211_TYPE_FREQ_HOP: case NDIS_80211_TYPE_DIRECT_SEQ: priv->caps |= CAP_MODE_80211B; break; case NDIS_80211_TYPE_OFDM_A: priv->caps |= CAP_MODE_80211A; break; case NDIS_80211_TYPE_OFDM_G: priv->caps |= CAP_MODE_80211G; break; } } } /* get device 802.11 capabilities, number of PMKIDs */ caps = (struct ndis_80211_capability *)caps_buf; len = sizeof(caps_buf); retval = rndis_query_oid(usbdev, RNDIS_OID_802_11_CAPABILITY, caps, &len); if (retval >= 0) { netdev_dbg(usbdev->net, "RNDIS_OID_802_11_CAPABILITY -> len %d, " "ver %d, pmkids %d, auth-encr-pairs %d\n", le32_to_cpu(caps->length), le32_to_cpu(caps->version), le32_to_cpu(caps->num_pmkids), le32_to_cpu(caps->num_auth_encr_pair)); wiphy->max_num_pmkids = le32_to_cpu(caps->num_pmkids); } else wiphy->max_num_pmkids = 0; return retval; } static void rndis_do_cqm(struct usbnet *usbdev, s32 rssi) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); enum nl80211_cqm_rssi_threshold_event event; int thold, hyst, last_event; if (priv->cqm_rssi_thold >= 0 || rssi >= 0) return; if (priv->infra_mode != NDIS_80211_INFRA_INFRA) return; last_event = priv->last_cqm_event_rssi; thold = priv->cqm_rssi_thold; hyst = priv->cqm_rssi_hyst; if (rssi < thold && (last_event == 0 || rssi < last_event - hyst)) event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; else if (rssi > thold && (last_event == 0 || rssi > last_event + hyst)) event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; else return; priv->last_cqm_event_rssi = rssi; cfg80211_cqm_rssi_notify(usbdev->net, event, GFP_KERNEL); } #define DEVICE_POLLER_JIFFIES (HZ) static void rndis_device_poller(struct work_struct *work) { struct rndis_wlan_private *priv = container_of(work, struct rndis_wlan_private, dev_poller_work.work); struct usbnet *usbdev = priv->usbdev; __le32 rssi, tmp; int len, ret, j; int update_jiffies = DEVICE_POLLER_JIFFIES; void *buf; /* Only check/do workaround when connected. Calling is_associated() * also polls device with rndis_command() and catches for media link * indications. */ if (!is_associated(usbdev)) { /* Workaround bad scanning in BCM4320a devices with active * background scanning when not associated. */ if (priv->device_type == RNDIS_BCM4320A && priv->radio_on && !priv->scan_request) { /* Get previous scan results */ rndis_check_bssid_list(usbdev, NULL, NULL); /* Initiate new scan */ rndis_start_bssid_list_scan(usbdev); } goto end; } len = sizeof(rssi); ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, &rssi, &len); if (ret == 0) { priv->last_qual = level_to_qual(le32_to_cpu(rssi)); rndis_do_cqm(usbdev, le32_to_cpu(rssi)); } netdev_dbg(usbdev->net, "dev-poller: RNDIS_OID_802_11_RSSI -> %d, rssi:%d, qual: %d\n", ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); /* Workaround transfer stalls on poor quality links. * TODO: find right way to fix these stalls (as stalls do not happen * with ndiswrapper/windows driver). */ if (priv->param_workaround_interval > 0 && priv->last_qual <= 25) { /* Decrease stats worker interval to catch stalls. * faster. Faster than 400-500ms causes packet loss, * Slower doesn't catch stalls fast enough. */ j = msecs_to_jiffies(priv->param_workaround_interval); if (j > DEVICE_POLLER_JIFFIES) j = DEVICE_POLLER_JIFFIES; else if (j <= 0) j = 1; update_jiffies = j; /* Send scan OID. Use of both OIDs is required to get device * working. */ tmp = cpu_to_le32(1); rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST_SCAN, &tmp, sizeof(tmp)); len = CONTROL_BUFFER_SIZE; buf = kmalloc(len, GFP_KERNEL); if (!buf) goto end; rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST, buf, &len); kfree(buf); } end: if (update_jiffies >= HZ) update_jiffies = round_jiffies_relative(update_jiffies); else { j = round_jiffies_relative(update_jiffies); if (abs(j - update_jiffies) <= 10) update_jiffies = j; } queue_delayed_work(priv->workqueue, &priv->dev_poller_work, update_jiffies); } /* * driver/device initialization */ static void rndis_copy_module_params(struct usbnet *usbdev, int device_type) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); priv->device_type = device_type; priv->param_country[0] = modparam_country[0]; priv->param_country[1] = modparam_country[1]; priv->param_country[2] = 0; priv->param_frameburst = modparam_frameburst; priv->param_afterburner = modparam_afterburner; priv->param_power_save = modparam_power_save; priv->param_power_output = modparam_power_output; priv->param_roamtrigger = modparam_roamtrigger; priv->param_roamdelta = modparam_roamdelta; priv->param_country[0] = toupper(priv->param_country[0]); priv->param_country[1] = toupper(priv->param_country[1]); /* doesn't support EU as country code, use FI instead */ if (!strcmp(priv->param_country, "EU")) strcpy(priv->param_country, "FI"); if (priv->param_power_save < 0) priv->param_power_save = 0; else if (priv->param_power_save > 2) priv->param_power_save = 2; if (priv->param_power_output < 0) priv->param_power_output = 0; else if (priv->param_power_output > 3) priv->param_power_output = 3; if (priv->param_roamtrigger < -80) priv->param_roamtrigger = -80; else if (priv->param_roamtrigger > -60) priv->param_roamtrigger = -60; if (priv->param_roamdelta < 0) priv->param_roamdelta = 0; else if (priv->param_roamdelta > 2) priv->param_roamdelta = 2; if (modparam_workaround_interval < 0) priv->param_workaround_interval = 500; else priv->param_workaround_interval = modparam_workaround_interval; } static int unknown_early_init(struct usbnet *usbdev) { /* copy module parameters for unknown so that iwconfig reports txpower * and workaround parameter is copied to private structure correctly. */ rndis_copy_module_params(usbdev, RNDIS_UNKNOWN); /* This is unknown device, so do not try set configuration parameters. */ return 0; } static int bcm4320a_early_init(struct usbnet *usbdev) { /* copy module parameters for bcm4320a so that iwconfig reports txpower * and workaround parameter is copied to private structure correctly. */ rndis_copy_module_params(usbdev, RNDIS_BCM4320A); /* bcm4320a doesn't handle configuration parameters well. Try * set any and you get partially zeroed mac and broken device. */ return 0; } static int bcm4320b_early_init(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); char buf[8]; rndis_copy_module_params(usbdev, RNDIS_BCM4320B); /* Early initialization settings, setting these won't have effect * if called after generic_rndis_bind(). */ rndis_set_config_parameter_str(usbdev, "Country", priv->param_country); rndis_set_config_parameter_str(usbdev, "FrameBursting", priv->param_frameburst ? "1" : "0"); rndis_set_config_parameter_str(usbdev, "Afterburner", priv->param_afterburner ? "1" : "0"); sprintf(buf, "%d", priv->param_power_save); rndis_set_config_parameter_str(usbdev, "PowerSaveMode", buf); sprintf(buf, "%d", priv->param_power_output); rndis_set_config_parameter_str(usbdev, "PwrOut", buf); sprintf(buf, "%d", priv->param_roamtrigger); rndis_set_config_parameter_str(usbdev, "RoamTrigger", buf); sprintf(buf, "%d", priv->param_roamdelta); rndis_set_config_parameter_str(usbdev, "RoamDelta", buf); return 0; } /* same as rndis_netdev_ops but with local multicast handler */ static const struct net_device_ops rndis_wlan_netdev_ops = { .ndo_open = usbnet_open, .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = rndis_wlan_set_multicast_list, }; static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) { struct wiphy *wiphy; struct rndis_wlan_private *priv; int retval, len; __le32 tmp; /* allocate wiphy and rndis private data * NOTE: We only support a single virtual interface, so wiphy * and wireless_dev are somewhat synonymous for this device. */ wiphy = wiphy_new(&rndis_config_ops, sizeof(struct rndis_wlan_private)); if (!wiphy) return -ENOMEM; priv = wiphy_priv(wiphy); usbdev->net->ieee80211_ptr = &priv->wdev; priv->wdev.wiphy = wiphy; priv->wdev.iftype = NL80211_IFTYPE_STATION; /* These have to be initialized before calling generic_rndis_bind(). * Otherwise we'll be in big trouble in rndis_wlan_early_init(). */ usbdev->driver_priv = priv; priv->usbdev = usbdev; mutex_init(&priv->command_lock); /* because rndis_command() sleeps we need to use workqueue */ priv->workqueue = create_singlethread_workqueue("rndis_wlan"); INIT_WORK(&priv->work, rndis_wlan_worker); INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller); INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results); /* try bind rndis_host */ retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS); if (retval < 0) goto fail; /* generic_rndis_bind set packet filter to multicast_all+ * promisc mode which doesn't work well for our devices (device * picks up rssi to closest station instead of to access point). * * rndis_host wants to avoid all OID as much as possible * so do promisc/multicast handling in rndis_wlan. */ netdev_attach_ops(usbdev->net, &rndis_wlan_netdev_ops); tmp = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST); retval = rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &tmp, sizeof(tmp)); len = sizeof(tmp); retval = rndis_query_oid(usbdev, RNDIS_OID_802_3_MAXIMUM_LIST_SIZE, &tmp, &len); priv->multicast_size = le32_to_cpu(tmp); if (retval < 0 || priv->multicast_size < 0) priv->multicast_size = 0; if (priv->multicast_size > 0) usbdev->net->flags |= IFF_MULTICAST; else usbdev->net->flags &= ~IFF_MULTICAST; /* fill-out wiphy structure and register w/ cfg80211 */ memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN); wiphy->privid = rndis_wiphy_privid; wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); wiphy->max_scan_ssids = 1; /* TODO: fill-out band/encr information based on priv->caps */ rndis_wlan_get_caps(usbdev, wiphy); memcpy(priv->channels, rndis_channels, sizeof(rndis_channels)); memcpy(priv->rates, rndis_rates, sizeof(rndis_rates)); priv->band.channels = priv->channels; priv->band.n_channels = ARRAY_SIZE(rndis_channels); priv->band.bitrates = priv->rates; priv->band.n_bitrates = ARRAY_SIZE(rndis_rates); wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; memcpy(priv->cipher_suites, rndis_cipher_suites, sizeof(rndis_cipher_suites)); wiphy->cipher_suites = priv->cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(rndis_cipher_suites); set_wiphy_dev(wiphy, &usbdev->udev->dev); if (wiphy_register(wiphy)) { retval = -ENODEV; goto fail; } set_default_iw_params(usbdev); priv->power_mode = -1; /* set default rts/frag */ rndis_set_wiphy_params(wiphy, WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); /* turn radio off on init */ priv->radio_on = false; disassociate(usbdev, false); netif_carrier_off(usbdev->net); return 0; fail: cancel_delayed_work_sync(&priv->dev_poller_work); cancel_delayed_work_sync(&priv->scan_work); cancel_work_sync(&priv->work); flush_workqueue(priv->workqueue); destroy_workqueue(priv->workqueue); wiphy_free(wiphy); return retval; } static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); /* turn radio off */ disassociate(usbdev, false); cancel_delayed_work_sync(&priv->dev_poller_work); cancel_delayed_work_sync(&priv->scan_work); cancel_work_sync(&priv->work); flush_workqueue(priv->workqueue); destroy_workqueue(priv->workqueue); rndis_unbind(usbdev, intf); wiphy_unregister(priv->wdev.wiphy); wiphy_free(priv->wdev.wiphy); } static int rndis_wlan_reset(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int retval; netdev_dbg(usbdev->net, "%s()\n", __func__); retval = rndis_reset(usbdev); if (retval) netdev_warn(usbdev->net, "rndis_reset failed: %d\n", retval); /* rndis_reset cleared multicast list, so restore here. (set_multicast_list() also turns on current packet filter) */ set_multicast_list(usbdev); queue_delayed_work(priv->workqueue, &priv->dev_poller_work, round_jiffies_relative(DEVICE_POLLER_JIFFIES)); return deauthenticate(usbdev); } static int rndis_wlan_stop(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); int retval; __le32 filter; netdev_dbg(usbdev->net, "%s()\n", __func__); retval = disassociate(usbdev, false); priv->work_pending = 0; cancel_delayed_work_sync(&priv->dev_poller_work); cancel_delayed_work_sync(&priv->scan_work); cancel_work_sync(&priv->work); flush_workqueue(priv->workqueue); if (priv->scan_request) { cfg80211_scan_done(priv->scan_request, true); priv->scan_request = NULL; } /* Set current packet filter zero to block receiving data packets from device. */ filter = 0; rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, sizeof(filter)); return retval; } static const struct driver_info bcm4320b_info = { .description = "Wireless RNDIS device, BCM4320b based", .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | FLAG_AVOID_UNLINK_URBS, .bind = rndis_wlan_bind, .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, .stop = rndis_wlan_stop, .early_init = bcm4320b_early_init, .indication = rndis_wlan_indication, }; static const struct driver_info bcm4320a_info = { .description = "Wireless RNDIS device, BCM4320a based", .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | FLAG_AVOID_UNLINK_URBS, .bind = rndis_wlan_bind, .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, .stop = rndis_wlan_stop, .early_init = bcm4320a_early_init, .indication = rndis_wlan_indication, }; static const struct driver_info rndis_wlan_info = { .description = "Wireless RNDIS device", .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | FLAG_AVOID_UNLINK_URBS, .bind = rndis_wlan_bind, .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, .stop = rndis_wlan_stop, .early_init = unknown_early_init, .indication = rndis_wlan_indication, }; /*-------------------------------------------------------------------------*/ static const struct usb_device_id products [] = { #define RNDIS_MASTER_INTERFACE \ .bInterfaceClass = USB_CLASS_COMM, \ .bInterfaceSubClass = 2 /* ACM */, \ .bInterfaceProtocol = 0x0ff /* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki. */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0411, .idProduct = 0x00bc, /* Buffalo WLI-U2-KG125S */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0baf, .idProduct = 0x011b, /* U.S. Robotics USR5421 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x050d, .idProduct = 0x011b, /* Belkin F5D7051 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x1799, /* Belkin has two vendor ids */ .idProduct = 0x011b, /* Belkin F5D7051 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x13b1, .idProduct = 0x0014, /* Linksys WUSB54GSv2 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x13b1, .idProduct = 0x0026, /* Linksys WUSB54GSC */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0b05, .idProduct = 0x1717, /* Asus WL169gE */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0a5c, .idProduct = 0xd11b, /* Eminent EM4045 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x1690, .idProduct = 0x0715, /* BT Voyager 1055 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320b_info, }, /* These devices have DriverVer < 4.xx.xx.xx and do not have any custom * parameters available, hardware probably contain older firmware version with * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki. */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x13b1, .idProduct = 0x000e, /* Linksys WUSB54GSv1 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320a_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0baf, .idProduct = 0x0111, /* U.S. Robotics USR5420 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320a_info, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0411, .idProduct = 0x004b, /* BUFFALO WLI-USB-G54 */ RNDIS_MASTER_INTERFACE, .driver_info = (unsigned long) &bcm4320a_info, }, /* Generic Wireless RNDIS devices that we don't have exact * idVendor/idProduct/chip yet. */ { /* RNDIS is MSFT's un-official variant of CDC ACM */ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), .driver_info = (unsigned long) &rndis_wlan_info, }, { /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), .driver_info = (unsigned long) &rndis_wlan_info, }, { }, // END }; MODULE_DEVICE_TABLE(usb, products); static struct usb_driver rndis_wlan_driver = { .name = "rndis_wlan", .id_table = products, .probe = usbnet_probe, .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(rndis_wlan_driver); MODULE_AUTHOR("Bjorge Dijkstra"); MODULE_AUTHOR("Jussi Kivilinna"); MODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/at76c50x-usb.c0000644000175000017500000021035712026211315022705 0ustar mcgrofmcgrof/* * at76c503/at76c505 USB driver * * Copyright (c) 2002 - 2003 Oliver Kurth * Copyright (c) 2004 Joerg Albert * Copyright (c) 2004 Nick Jones * Copyright (c) 2004 Balint Seeber * Copyright (c) 2007 Guido Guenther * Copyright (c) 2007 Kalle Valo * Copyright (c) 2010 Sebastian Smolorz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This file is part of the Berlios driver for WLAN USB devices based on the * Atmel AT76C503A/505/505A. * * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed * * TODO list is at the wiki: * * http://wireless.kernel.org/en/users/Drivers/at76c50x-usb#TODO * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "at76c50x-usb.h" /* Version information */ #define DRIVER_NAME "at76c50x-usb" #define DRIVER_VERSION "0.17" #define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver" /* at76_debug bits */ #define DBG_PROGRESS 0x00000001 /* authentication/accociation */ #define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */ #define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ #define DBG_MAC_STATE 0x00000008 /* MAC state transitions */ #define DBG_TX_DATA 0x00000010 /* tx header */ #define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ #define DBG_TX_MGMT 0x00000040 /* tx management */ #define DBG_RX_DATA 0x00000080 /* rx data header */ #define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ #define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */ #define DBG_RX_BEACON 0x00000400 /* rx beacon */ #define DBG_RX_CTRL 0x00000800 /* rx control */ #define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ #define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ #define DBG_DEVSTART 0x00004000 /* fw download, device start */ #define DBG_URB 0x00008000 /* rx urb status, ... */ #define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */ #define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */ #define DBG_PM 0x00040000 /* power management settings */ #define DBG_BSS_MATCH 0x00080000 /* BSS match failures */ #define DBG_PARAMS 0x00100000 /* show configured parameters */ #define DBG_WAIT_COMPLETE 0x00200000 /* command completion */ #define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */ #define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */ #define DBG_MONITOR_MODE 0x01000000 /* monitor mode */ #define DBG_MIB 0x02000000 /* dump all MIBs on startup */ #define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ #define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ #define DBG_FW 0x10000000 /* firmware download */ #define DBG_DFU 0x20000000 /* device firmware upgrade */ #define DBG_CMD 0x40000000 #define DBG_MAC80211 0x80000000 #define DBG_DEFAULTS 0 /* Use our own dbg macro */ #define at76_dbg(bits, format, arg...) \ do { \ if (at76_debug & (bits)) \ printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ } while (0) #define at76_dbg_dump(bits, buf, len, format, arg...) \ do { \ if (at76_debug & (bits)) { \ printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \ } \ } while (0) static uint at76_debug = DBG_DEFAULTS; /* Protect against concurrent firmware loading and parsing */ static struct mutex fw_mutex; static struct fwentry firmwares[] = { [0] = { "" }, [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" }, [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" }, [BOARD_503] = { "atmel_at76c503-rfmd.bin" }, [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" }, [BOARD_505] = { "atmel_at76c505-rfmd.bin" }, [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" }, [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, }; MODULE_FIRMWARE("atmel_at76c503-i3861.bin"); MODULE_FIRMWARE("atmel_at76c503-i3863.bin"); MODULE_FIRMWARE("atmel_at76c503-rfmd.bin"); MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin"); MODULE_FIRMWARE("atmel_at76c505-rfmd.bin"); MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin"); MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin"); MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); #define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) static struct usb_device_id dev_table[] = { /* * at76c503-i3861 */ /* Generic AT76C503/3861 device */ { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Linksys WUSB11 v2.1/v2.6 */ { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Netgear MA101 rev. A */ { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Tekram U300C / Allnet ALL0193 */ { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* HP HN210W J7801A */ { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Sitecom/Z-Com/Zyxel M4Y-750 */ { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Dynalink/Askey WLL013 (intersil) */ { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* BenQ AWL300 */ { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Addtron AWU-120, Compex WLU11 */ { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Intel AP310 AnyPoint II USB */ { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Dynalink L11U */ { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* Arescom WL-210, FCC id 07J-GL2411USB */ { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* I-O DATA WN-B11/USB */ { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* BT Voyager 1010 */ { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) }, /* * at76c503-i3863 */ /* Generic AT76C503/3863 device */ { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) }, /* Samsung SWL-2100U */ { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) }, /* * at76c503-rfmd */ /* Generic AT76C503/RFMD device */ { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) }, /* Dynalink/Askey WLL013 (rfmd) */ { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) }, /* Linksys WUSB11 v2.6 */ { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) }, /* Network Everywhere NWU11B */ { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) }, /* Netgear MA101 rev. B */ { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) }, /* D-Link DWL-120 rev. E */ { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) }, /* Actiontec 802UAT1, HWU01150-01UK */ { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) }, /* AirVast W-Buddie WN210 */ { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) }, /* Dick Smith Electronics XH1153 802.11b USB adapter */ { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) }, /* CNet CNUSB611 */ { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) }, /* FiberLine FL-WL200U */ { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) }, /* BenQ AWL400 USB stick */ { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) }, /* 3Com 3CRSHEW696 */ { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) }, /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) }, /* Belkin F5D6050, version 2 */ { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) }, /* iBlitzz, BWU613 (not *B or *SB) */ { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) }, /* Gigabyte GN-WLBM101 */ { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) }, /* Planex GW-US11S */ { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) }, /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) }, /* Corega Wireless LAN USB-11 mini */ { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) }, /* Corega Wireless LAN USB-11 mini2 */ { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) }, /* Uniden PCW100 */ { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) }, /* * at76c503-rfmd-acc */ /* SMC2664W */ { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) }, /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) }, /* * at76c505-rfmd */ /* Generic AT76C505/RFMD */ { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) }, /* * at76c505-rfmd2958 */ /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, /* Fiberline FL-WL240U */ { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) }, /* CNet CNUSB-611G */ { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) }, /* Linksys WUSB11 v2.8 */ { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) }, /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) }, /* Corega WLAN USB Stick 11 */ { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, /* Microstar MSI Box MS6978 */ { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) }, /* * at76c505a-rfmd2958 */ /* Generic AT76C505A device */ { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) }, /* Generic AT76C505AS device */ { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) }, /* Siemens Gigaset USB WLAN Adapter 11 */ { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) }, /* OQO Model 01+ Internal Wi-Fi */ { USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A) }, /* * at76c505amx-rfmd */ /* Generic AT76C505AMX device */ { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) }, { } }; MODULE_DEVICE_TABLE(usb, dev_table); /* Supported rates of this hardware, bit 7 marks basic rates */ static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; static const char *const preambles[] = { "long", "short", "auto" }; /* Firmware download */ /* DFU states */ #define STATE_IDLE 0x00 #define STATE_DETACH 0x01 #define STATE_DFU_IDLE 0x02 #define STATE_DFU_DOWNLOAD_SYNC 0x03 #define STATE_DFU_DOWNLOAD_BUSY 0x04 #define STATE_DFU_DOWNLOAD_IDLE 0x05 #define STATE_DFU_MANIFEST_SYNC 0x06 #define STATE_DFU_MANIFEST 0x07 #define STATE_DFU_MANIFEST_WAIT_RESET 0x08 #define STATE_DFU_UPLOAD_IDLE 0x09 #define STATE_DFU_ERROR 0x0a /* DFU commands */ #define DFU_DETACH 0 #define DFU_DNLOAD 1 #define DFU_UPLOAD 2 #define DFU_GETSTATUS 3 #define DFU_CLRSTATUS 4 #define DFU_GETSTATE 5 #define DFU_ABORT 6 #define FW_BLOCK_SIZE 1024 struct dfu_status { unsigned char status; unsigned char poll_timeout[3]; unsigned char state; unsigned char string; } __packed; static inline int at76_is_intersil(enum board_type board) { return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); } static inline int at76_is_503rfmd(enum board_type board) { return (board == BOARD_503 || board == BOARD_503_ACC); } static inline int at76_is_505a(enum board_type board) { return (board == BOARD_505A || board == BOARD_505AMX); } /* Load a block of the first (internal) part of the firmware */ static int at76_load_int_fw_block(struct usb_device *udev, int blockno, void *block, int size) { return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, blockno, 0, block, size, USB_CTRL_GET_TIMEOUT); } static int at76_dfu_get_status(struct usb_device *udev, struct dfu_status *status) { int ret; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, 0, 0, status, sizeof(struct dfu_status), USB_CTRL_GET_TIMEOUT); return ret; } static int at76_dfu_get_state(struct usb_device *udev, u8 *state) { int ret; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, 0, 0, state, 1, USB_CTRL_GET_TIMEOUT); return ret; } /* Convert timeout from the DFU status to jiffies */ static inline unsigned long at76_get_timeout(struct dfu_status *s) { return msecs_to_jiffies((s->poll_timeout[2] << 16) | (s->poll_timeout[1] << 8) | (s->poll_timeout[0])); } /* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use * its value in jiffies in the MANIFEST_SYNC state. */ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, int manifest_sync_timeout) { u8 *block; struct dfu_status dfu_stat_buf; int ret = 0; int need_dfu_state = 1; int is_done = 0; u8 dfu_state = 0; u32 dfu_timeout = 0; int bsize = 0; int blockno = 0; at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, manifest_sync_timeout); if (!size) { dev_printk(KERN_ERR, &udev->dev, "FW buffer length invalid!\n"); return -EINVAL; } block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); if (!block) return -ENOMEM; do { if (need_dfu_state) { ret = at76_dfu_get_state(udev, &dfu_state); if (ret < 0) { dev_printk(KERN_ERR, &udev->dev, "cannot get DFU state: %d\n", ret); goto exit; } need_dfu_state = 0; } switch (dfu_state) { case STATE_DFU_DOWNLOAD_SYNC: at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); ret = at76_dfu_get_status(udev, &dfu_stat_buf); if (ret >= 0) { dfu_state = dfu_stat_buf.state; dfu_timeout = at76_get_timeout(&dfu_stat_buf); need_dfu_state = 0; } else dev_printk(KERN_ERR, &udev->dev, "at76_dfu_get_status returned %d\n", ret); break; case STATE_DFU_DOWNLOAD_BUSY: at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY"); need_dfu_state = 1; at76_dbg(DBG_DFU, "DFU: Resetting device"); schedule_timeout_interruptible(dfu_timeout); break; case STATE_DFU_DOWNLOAD_IDLE: at76_dbg(DBG_DFU, "DOWNLOAD..."); /* fall through */ case STATE_DFU_IDLE: at76_dbg(DBG_DFU, "DFU IDLE"); bsize = min_t(int, size, FW_BLOCK_SIZE); memcpy(block, buf, bsize); at76_dbg(DBG_DFU, "int fw, size left = %5d, " "bsize = %4d, blockno = %2d", size, bsize, blockno); ret = at76_load_int_fw_block(udev, blockno, block, bsize); buf += bsize; size -= bsize; blockno++; if (ret != bsize) dev_printk(KERN_ERR, &udev->dev, "at76_load_int_fw_block " "returned %d\n", ret); need_dfu_state = 1; break; case STATE_DFU_MANIFEST_SYNC: at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); ret = at76_dfu_get_status(udev, &dfu_stat_buf); if (ret < 0) break; dfu_state = dfu_stat_buf.state; dfu_timeout = at76_get_timeout(&dfu_stat_buf); need_dfu_state = 0; /* override the timeout from the status response, needed for AT76C505A */ if (manifest_sync_timeout > 0) dfu_timeout = manifest_sync_timeout; at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase"); schedule_timeout_interruptible(dfu_timeout); break; case STATE_DFU_MANIFEST: at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST"); is_done = 1; break; case STATE_DFU_MANIFEST_WAIT_RESET: at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET"); is_done = 1; break; case STATE_DFU_UPLOAD_IDLE: at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE"); break; case STATE_DFU_ERROR: at76_dbg(DBG_DFU, "STATE_DFU_ERROR"); ret = -EPIPE; break; default: at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state); ret = -EINVAL; break; } } while (!is_done && (ret >= 0)); exit: kfree(block); if (ret >= 0) ret = 0; return ret; } /* LED trigger */ static int tx_activity; static void at76_ledtrig_tx_timerfunc(unsigned long data); static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0); DEFINE_LED_TRIGGER(ledtrig_tx); static void at76_ledtrig_tx_timerfunc(unsigned long data) { static int tx_lastactivity; if (tx_lastactivity != tx_activity) { tx_lastactivity = tx_activity; led_trigger_event(ledtrig_tx, LED_FULL); mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); } else led_trigger_event(ledtrig_tx, LED_OFF); } static void at76_ledtrig_tx_activity(void) { tx_activity++; if (!timer_pending(&ledtrig_tx_timer)) mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); } static int at76_remap(struct usb_device *udev) { int ret; ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE, 0, 0, NULL, 0, USB_CTRL_GET_TIMEOUT); if (ret < 0) return ret; return 0; } static int at76_get_op_mode(struct usb_device *udev) { int ret; u8 saved; u8 *op_mode; op_mode = kmalloc(1, GFP_NOIO); if (!op_mode) return -ENOMEM; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1, USB_CTRL_GET_TIMEOUT); saved = *op_mode; kfree(op_mode); if (ret < 0) return ret; else if (ret < 1) return -EIO; else return saved; } /* Load a block of the second ("external") part of the firmware */ static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno, void *block, int size) { return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, 0x0802, blockno, block, size, USB_CTRL_GET_TIMEOUT); } static inline int at76_get_hw_cfg(struct usb_device *udev, union at76_hwcfg *buf, int buf_size) { return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE, 0x0a02, 0, buf, buf_size, USB_CTRL_GET_TIMEOUT); } /* Intersil boards use a different "value" for GetHWConfig requests */ static inline int at76_get_hw_cfg_intersil(struct usb_device *udev, union at76_hwcfg *buf, int buf_size) { return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE, 0x0902, 0, buf, buf_size, USB_CTRL_GET_TIMEOUT); } /* Get the hardware configuration for the adapter and put it to the appropriate * fields of 'priv' (the GetHWConfig request and interpretation of the result * depends on the board type) */ static int at76_get_hw_config(struct at76_priv *priv) { int ret; union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); if (!hwcfg) return -ENOMEM; if (at76_is_intersil(priv->board_type)) { ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg, sizeof(hwcfg->i)); if (ret < 0) goto exit; memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); priv->regulatory_domain = hwcfg->i.regulatory_domain; } else if (at76_is_503rfmd(priv->board_type)) { ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3)); if (ret < 0) goto exit; memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); priv->regulatory_domain = hwcfg->r3.regulatory_domain; } else { ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5)); if (ret < 0) goto exit; memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); priv->regulatory_domain = hwcfg->r5.regulatory_domain; } exit: kfree(hwcfg); if (ret < 0) wiphy_err(priv->hw->wiphy, "cannot get HW Config (error %d)\n", ret); return ret; } static struct reg_domain const *at76_get_reg_domain(u16 code) { int i; static struct reg_domain const fd_tab[] = { { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */ { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */ { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */ { 0x31, "Spain", 0x600 }, /* ch 10-11 */ { 0x32, "France", 0x1e00 }, /* ch 10-13 */ { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */ { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */ { 0x50, "Israel", 0x3fc }, /* ch 3-9 */ { 0x00, "", 0xffffffff } /* ch 1-32 */ }; /* Last entry is fallback for unknown domain code */ for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++) if (code == fd_tab[i].code) break; return &fd_tab[i]; } static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, int buf_size) { int ret; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size, USB_CTRL_GET_TIMEOUT); if (ret >= 0 && ret != buf_size) return -EIO; return ret; } /* Return positive number for status, negative for an error */ static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) { u8 *stat_buf; int ret; stat_buf = kmalloc(40, GFP_NOIO); if (!stat_buf) return -ENOMEM; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE, cmd, 0, stat_buf, 40, USB_CTRL_GET_TIMEOUT); if (ret >= 0) ret = stat_buf[5]; kfree(stat_buf); return ret; } #define MAKE_CMD_CASE(c) case (c): return #c static const char *at76_get_cmd_string(u8 cmd_status) { switch (cmd_status) { MAKE_CMD_CASE(CMD_SET_MIB); MAKE_CMD_CASE(CMD_GET_MIB); MAKE_CMD_CASE(CMD_SCAN); MAKE_CMD_CASE(CMD_JOIN); MAKE_CMD_CASE(CMD_START_IBSS); MAKE_CMD_CASE(CMD_RADIO_ON); MAKE_CMD_CASE(CMD_RADIO_OFF); MAKE_CMD_CASE(CMD_STARTUP); } return "UNKNOWN"; } static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, int buf_size) { int ret; struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + buf_size, GFP_KERNEL); if (!cmd_buf) return -ENOMEM; cmd_buf->cmd = cmd; cmd_buf->reserved = 0; cmd_buf->size = cpu_to_le16(buf_size); memcpy(cmd_buf->data, buf, buf_size); at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size, "issuing command %s (0x%02x)", at76_get_cmd_string(cmd), cmd); ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, 0, 0, cmd_buf, sizeof(struct at76_command) + buf_size, USB_CTRL_GET_TIMEOUT); kfree(cmd_buf); return ret; } #define MAKE_CMD_STATUS_CASE(c) case (c): return #c static const char *at76_get_cmd_status_string(u8 cmd_status) { switch (cmd_status) { MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); } return "UNKNOWN"; } /* Wait until the command is completed */ static int at76_wait_completion(struct at76_priv *priv, int cmd) { int status = 0; unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT; do { status = at76_get_cmd_status(priv->udev, cmd); if (status < 0) { wiphy_err(priv->hw->wiphy, "at76_get_cmd_status failed: %d\n", status); break; } at76_dbg(DBG_WAIT_COMPLETE, "%s: Waiting on cmd %d, status = %d (%s)", wiphy_name(priv->hw->wiphy), cmd, status, at76_get_cmd_status_string(status)); if (status != CMD_STATUS_IN_PROGRESS && status != CMD_STATUS_IDLE) break; schedule_timeout_interruptible(HZ / 10); /* 100 ms */ if (time_after(jiffies, timeout)) { wiphy_err(priv->hw->wiphy, "completion timeout for command %d\n", cmd); status = -ETIMEDOUT; break; } } while (1); return status; } static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) { int ret; ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf, offsetof(struct set_mib_buffer, data) + buf->size); if (ret < 0) return ret; ret = at76_wait_completion(priv, CMD_SET_MIB); if (ret != CMD_STATUS_COMPLETE) { wiphy_info(priv->hw->wiphy, "set_mib: at76_wait_completion failed with %d\n", ret); ret = -EIO; } return ret; } /* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ static int at76_set_radio(struct at76_priv *priv, int enable) { int ret; int cmd; if (priv->radio_on == enable) return 0; cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF; ret = at76_set_card_command(priv->udev, cmd, NULL, 0); if (ret < 0) wiphy_err(priv->hw->wiphy, "at76_set_card_command(%d) failed: %d\n", cmd, ret); else ret = 1; priv->radio_on = enable; return ret; } /* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */ static int at76_set_pm_mode(struct at76_priv *priv) { int ret = 0; priv->mib_buf.type = MIB_MAC_MGMT; priv->mib_buf.size = 1; priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode); priv->mib_buf.data.byte = priv->pm_mode; ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) wiphy_err(priv->hw->wiphy, "set_mib (pm_mode) failed: %d\n", ret); return ret; } static int at76_set_preamble(struct at76_priv *priv, u8 type) { int ret = 0; priv->mib_buf.type = MIB_LOCAL; priv->mib_buf.size = 1; priv->mib_buf.index = offsetof(struct mib_local, preamble_type); priv->mib_buf.data.byte = type; ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) wiphy_err(priv->hw->wiphy, "set_mib (preamble) failed: %d\n", ret); return ret; } static int at76_set_frag(struct at76_priv *priv, u16 size) { int ret = 0; priv->mib_buf.type = MIB_MAC; priv->mib_buf.size = 2; priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold); priv->mib_buf.data.word = cpu_to_le16(size); ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) wiphy_err(priv->hw->wiphy, "set_mib (frag threshold) failed: %d\n", ret); return ret; } static int at76_set_rts(struct at76_priv *priv, u16 size) { int ret = 0; priv->mib_buf.type = MIB_MAC; priv->mib_buf.size = 2; priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold); priv->mib_buf.data.word = cpu_to_le16(size); ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) wiphy_err(priv->hw->wiphy, "set_mib (rts) failed: %d\n", ret); return ret; } static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) { int ret = 0; priv->mib_buf.type = MIB_LOCAL; priv->mib_buf.size = 1; priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback); priv->mib_buf.data.byte = onoff; ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) wiphy_err(priv->hw->wiphy, "set_mib (autorate fallback) failed: %d\n", ret); return ret; } static void at76_dump_mib_mac_addr(struct at76_priv *priv) { int i; int ret; struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr), GFP_KERNEL); if (!m) return; ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m, sizeof(struct mib_mac_addr)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (MAC_ADDR) failed: %d\n", ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x", wiphy_name(priv->hw->wiphy), m->mac_addr, m->res[0], m->res[1]); for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, " "status %d", wiphy_name(priv->hw->wiphy), i, m->group_addr[i], m->group_addr_status[i]); exit: kfree(m); } static void at76_dump_mib_mac_wep(struct at76_priv *priv) { int i; int ret; int key_len; struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); if (!m) return; ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m, sizeof(struct mib_mac_wep)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (MAC_WEP) failed: %d\n", ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " "encr_level %u key %d", wiphy_name(priv->hw->wiphy), m->privacy_invoked, m->wep_default_key_id, m->wep_key_mapping_len, m->exclude_unencrypted, le32_to_cpu(m->wep_icv_error_count), le32_to_cpu(m->wep_excluded_count), m->encryption_level, m->wep_default_key_id); key_len = (m->encryption_level == 1) ? WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; for (i = 0; i < WEP_KEYS; i++) at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %*phD", wiphy_name(priv->hw->wiphy), i, key_len, m->wep_default_keyvalue[i]); exit: kfree(m); } static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) { int ret; struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt), GFP_KERNEL); if (!m) return; ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m, sizeof(struct mib_mac_mgmt)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (MAC_MGMT) failed: %d\n", ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " "current_bssid %pM current_essid %*phD current_bss_type %d " "pm_mode %d ibss_change %d res %d " "multi_domain_capability_implemented %d " "international_roaming %d country_string %.3s", wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period), le16_to_cpu(m->CFP_max_duration), le16_to_cpu(m->medium_occupancy_limit), le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, m->CFP_period, m->current_bssid, IW_ESSID_MAX_SIZE, m->current_essid, m->current_bss_type, m->power_mgmt_mode, m->ibss_change, m->res, m->multi_domain_capability_implemented, m->multi_domain_capability_enabled, m->country_string); exit: kfree(m); } static void at76_dump_mib_mac(struct at76_priv *priv) { int ret; struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); if (!m) return; ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (MAC) failed: %d\n", ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d " "max_rx_lifetime %d frag_threshold %d rts_threshold %d " "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " "scan_type %d scan_channel %d probe_delay %u " "min_channel_time %d max_channel_time %d listen_int %d " "desired_ssid %*phD desired_bssid %pM desired_bsstype %d", wiphy_name(priv->hw->wiphy), le32_to_cpu(m->max_tx_msdu_lifetime), le32_to_cpu(m->max_rx_lifetime), le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), m->short_retry_time, m->long_retry_time, m->scan_type, m->scan_channel, le16_to_cpu(m->probe_delay), le16_to_cpu(m->min_channel_time), le16_to_cpu(m->max_channel_time), le16_to_cpu(m->listen_interval), IW_ESSID_MAX_SIZE, m->desired_ssid, m->desired_bssid, m->desired_bsstype); exit: kfree(m); } static void at76_dump_mib_phy(struct at76_priv *priv) { int ret; struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); if (!m) return; ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (PHY) failed: %d\n", ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d " "sifs_time %d preamble_length %d plcp_header_length %d " "mpdu_max_length %d cca_mode_supported %d operation_rate_set " "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " "phy_type %d current_reg_domain %d", wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold), le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), le16_to_cpu(m->preamble_length), le16_to_cpu(m->plcp_header_length), le16_to_cpu(m->mpdu_max_length), le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0], m->operation_rate_set[1], m->operation_rate_set[2], m->operation_rate_set[3], m->channel_id, m->current_cca_mode, m->phy_type, m->current_reg_domain); exit: kfree(m); } static void at76_dump_mib_local(struct at76_priv *priv) { int ret; struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL); if (!m) return; ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (LOCAL) failed: %d\n", ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " "txautorate_fallback %d ssid_size %d promiscuous_mode %d " "preamble_type %d", wiphy_name(priv->hw->wiphy), m->beacon_enable, m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, m->preamble_type); exit: kfree(m); } static void at76_dump_mib_mdomain(struct at76_priv *priv) { int ret; struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); if (!m) return; ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m, sizeof(struct mib_mdomain)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (MDOMAIN) failed: %d\n", ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %*phD", wiphy_name(priv->hw->wiphy), (int)sizeof(m->channel_list), m->channel_list); at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %*phD", wiphy_name(priv->hw->wiphy), (int)sizeof(m->tx_powerlevel), m->tx_powerlevel); exit: kfree(m); } /* Enable monitor mode */ static int at76_start_monitor(struct at76_priv *priv) { struct at76_req_scan scan; int ret; memset(&scan, 0, sizeof(struct at76_req_scan)); memset(scan.bssid, 0xff, ETH_ALEN); scan.channel = priv->channel; scan.scan_type = SCAN_TYPE_PASSIVE; scan.international_scan = 0; scan.min_channel_time = cpu_to_le16(priv->scan_min_time); scan.max_channel_time = cpu_to_le16(priv->scan_max_time); scan.probe_delay = cpu_to_le16(0); ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); if (ret >= 0) ret = at76_get_cmd_status(priv->udev, CMD_SCAN); return ret; } /* Calculate padding from txbuf->wlength (which excludes the USB TX header), likely to compensate a flaw in the AT76C503A USB part ... */ static inline int at76_calc_padding(int wlen) { /* add the USB TX header */ wlen += AT76_TX_HDRLEN; wlen = wlen % 64; if (wlen < 50) return 50 - wlen; if (wlen >= 61) return 64 + 50 - wlen; return 0; } static void at76_rx_callback(struct urb *urb) { struct at76_priv *priv = urb->context; priv->rx_tasklet.data = (unsigned long)urb; tasklet_schedule(&priv->rx_tasklet); } static int at76_submit_rx_urb(struct at76_priv *priv) { int ret; int size; struct sk_buff *skb = priv->rx_skb; if (!priv->rx_urb) { wiphy_err(priv->hw->wiphy, "%s: priv->rx_urb is NULL\n", __func__); return -EFAULT; } if (!skb) { skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); if (!skb) { wiphy_err(priv->hw->wiphy, "cannot allocate rx skbuff\n"); ret = -ENOMEM; goto exit; } priv->rx_skb = skb; } else { skb_push(skb, skb_headroom(skb)); skb_trim(skb, 0); } size = skb_tailroom(skb); usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, skb_put(skb, size), size, at76_rx_callback, priv); ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); if (ret < 0) { if (ret == -ENODEV) at76_dbg(DBG_DEVSTART, "usb_submit_urb returned -ENODEV"); else wiphy_err(priv->hw->wiphy, "rx, usb_submit_urb failed: %d\n", ret); } exit: if (ret < 0 && ret != -ENODEV) wiphy_err(priv->hw->wiphy, "cannot submit rx urb - please unload the driver and/or power cycle the device\n"); return ret; } /* Download external firmware */ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) { int ret; int op_mode; int blockno = 0; int bsize; u8 *block; u8 *buf = fwe->extfw; int size = fwe->extfw_size; if (!buf || !size) return -ENOENT; op_mode = at76_get_op_mode(udev); at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { dev_printk(KERN_ERR, &udev->dev, "unexpected opmode %d\n", op_mode); return -EINVAL; } block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); if (!block) return -ENOMEM; at76_dbg(DBG_DEVSTART, "downloading external firmware"); /* for fw >= 0.100, the device needs an extra empty block */ do { bsize = min_t(int, size, FW_BLOCK_SIZE); memcpy(block, buf, bsize); at76_dbg(DBG_DEVSTART, "ext fw, size left = %5d, bsize = %4d, blockno = %2d", size, bsize, blockno); ret = at76_load_ext_fw_block(udev, blockno, block, bsize); if (ret != bsize) { dev_printk(KERN_ERR, &udev->dev, "loading %dth firmware block failed: %d\n", blockno, ret); goto exit; } buf += bsize; size -= bsize; blockno++; } while (bsize > 0); if (at76_is_505a(fwe->board_type)) { at76_dbg(DBG_DEVSTART, "200 ms delay for 505a"); schedule_timeout_interruptible(HZ / 5 + 1); } exit: kfree(block); if (ret < 0) dev_printk(KERN_ERR, &udev->dev, "downloading external firmware failed: %d\n", ret); return ret; } /* Download internal firmware */ static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) { int ret; int need_remap = !at76_is_505a(fwe->board_type); ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, need_remap ? 0 : 2 * HZ); if (ret < 0) { dev_printk(KERN_ERR, &udev->dev, "downloading internal fw failed with %d\n", ret); goto exit; } at76_dbg(DBG_DEVSTART, "sending REMAP"); /* no REMAP for 505A (see SF driver) */ if (need_remap) { ret = at76_remap(udev); if (ret < 0) { dev_printk(KERN_ERR, &udev->dev, "sending REMAP failed with %d\n", ret); goto exit; } } at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); schedule_timeout_interruptible(2 * HZ + 1); usb_reset_device(udev); exit: return ret; } static int at76_startup_device(struct at76_priv *priv) { struct at76_card_config *ccfg = &priv->card_config; int ret; at76_dbg(DBG_PARAMS, "%s param: ssid %.*s (%*phD) mode %s ch %d wep %s key %d " "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size, priv->essid, IW_ESSID_MAX_SIZE, priv->essid, priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", priv->channel, priv->wep_enabled ? "enabled" : "disabled", priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); at76_dbg(DBG_PARAMS, "%s param: preamble %s rts %d retry %d frag %d " "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy), preambles[priv->preamble_type], priv->rts_threshold, priv->short_retry_limit, priv->frag_threshold, priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == TX_RATE_2MBIT ? "2MBit" : priv->txrate == TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate == TX_RATE_11MBIT ? "11MBit" : priv->txrate == TX_RATE_AUTO ? "auto" : "", priv->auth_mode); at76_dbg(DBG_PARAMS, "%s param: pm_mode %d pm_period %d auth_mode %s " "scan_times %d %d scan_mode %s", wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period, priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", priv->scan_min_time, priv->scan_max_time, priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); memset(ccfg, 0, sizeof(struct at76_card_config)); ccfg->promiscuous_mode = 0; ccfg->short_retry_limit = priv->short_retry_limit; if (priv->wep_enabled) { if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) ccfg->encryption_type = 2; else ccfg->encryption_type = 1; /* jal: always exclude unencrypted if WEP is active */ ccfg->exclude_unencrypted = 1; } else { ccfg->exclude_unencrypted = 0; ccfg->encryption_type = 0; } ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold); ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold); memcpy(ccfg->basic_rate_set, hw_rates, 4); /* jal: really needed, we do a set_mib for autorate later ??? */ ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0); ccfg->channel = priv->channel; ccfg->privacy_invoked = priv->wep_enabled; memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE); ccfg->ssid_len = priv->essid_size; ccfg->wep_default_key_id = priv->wep_key_id; memcpy(ccfg->wep_default_key_value, priv->wep_keys, sizeof(priv->wep_keys)); ccfg->short_preamble = priv->preamble_type; ccfg->beacon_period = cpu_to_le16(priv->beacon_period); ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config, sizeof(struct at76_card_config)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", ret); return ret; } at76_wait_completion(priv, CMD_STARTUP); /* remove BSSID from previous run */ memset(priv->bssid, 0, ETH_ALEN); if (at76_set_radio(priv, 1) == 1) at76_wait_completion(priv, CMD_RADIO_ON); ret = at76_set_preamble(priv, priv->preamble_type); if (ret < 0) return ret; ret = at76_set_frag(priv, priv->frag_threshold); if (ret < 0) return ret; ret = at76_set_rts(priv, priv->rts_threshold); if (ret < 0) return ret; ret = at76_set_autorate_fallback(priv, priv->txrate == TX_RATE_AUTO ? 1 : 0); if (ret < 0) return ret; ret = at76_set_pm_mode(priv); if (ret < 0) return ret; if (at76_debug & DBG_MIB) { at76_dump_mib_mac(priv); at76_dump_mib_mac_addr(priv); at76_dump_mib_mac_mgmt(priv); at76_dump_mib_mac_wep(priv); at76_dump_mib_mdomain(priv); at76_dump_mib_phy(priv); at76_dump_mib_local(priv); } return 0; } /* Enable or disable promiscuous mode */ static void at76_work_set_promisc(struct work_struct *work) { struct at76_priv *priv = container_of(work, struct at76_priv, work_set_promisc); int ret = 0; if (priv->device_unplugged) return; mutex_lock(&priv->mtx); priv->mib_buf.type = MIB_LOCAL; priv->mib_buf.size = 1; priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); priv->mib_buf.data.byte = priv->promisc ? 1 : 0; ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) wiphy_err(priv->hw->wiphy, "set_mib (promiscuous_mode) failed: %d\n", ret); mutex_unlock(&priv->mtx); } /* Submit Rx urb back to the device */ static void at76_work_submit_rx(struct work_struct *work) { struct at76_priv *priv = container_of(work, struct at76_priv, work_submit_rx); mutex_lock(&priv->mtx); at76_submit_rx_urb(priv); mutex_unlock(&priv->mtx); } static void at76_rx_tasklet(unsigned long param) { struct urb *urb = (struct urb *)param; struct at76_priv *priv = urb->context; struct at76_rx_buffer *buf; struct ieee80211_rx_status rx_status = { 0 }; if (priv->device_unplugged) { at76_dbg(DBG_DEVSTART, "device unplugged"); at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); return; } if (!priv->rx_skb || !priv->rx_skb->data) return; buf = (struct at76_rx_buffer *)priv->rx_skb->data; if (urb->status != 0) { if (urb->status != -ENOENT && urb->status != -ECONNRESET) at76_dbg(DBG_URB, "%s %s: - nonzero Rx bulk status received: %d", __func__, wiphy_name(priv->hw->wiphy), urb->status); return; } at76_dbg(DBG_RX_ATMEL_HDR, "%s: rx frame: rate %d rssi %d noise %d link %d", wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi, buf->noise_level, buf->link_quality); skb_pull(priv->rx_skb, AT76_RX_HDRLEN); skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength)); at76_dbg_dump(DBG_RX_DATA, priv->rx_skb->data, priv->rx_skb->len, "RX: len=%d", priv->rx_skb->len); rx_status.signal = buf->rssi; rx_status.flag |= RX_FLAG_DECRYPTED; rx_status.flag |= RX_FLAG_IV_STRIPPED; at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", priv->rx_skb->len, priv->rx_skb->data_len); memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(priv->hw, priv->rx_skb); /* Use a new skb for the next receive */ priv->rx_skb = NULL; at76_submit_rx_urb(priv); } /* Load firmware into kernel memory and parse it */ static struct fwentry *at76_load_firmware(struct usb_device *udev, enum board_type board_type) { int ret; char *str; struct at76_fw_header *fwh; struct fwentry *fwe = &firmwares[board_type]; mutex_lock(&fw_mutex); if (fwe->loaded) { at76_dbg(DBG_FW, "re-using previously loaded fw"); goto exit; } at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); if (ret < 0) { dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n", fwe->fwname); dev_printk(KERN_ERR, &udev->dev, "you may need to download the firmware from " "http://developer.berlios.de/projects/at76c503a/\n"); goto exit; } at76_dbg(DBG_FW, "got it."); fwh = (struct at76_fw_header *)(fwe->fw->data); if (fwe->fw->size <= sizeof(*fwh)) { dev_printk(KERN_ERR, &udev->dev, "firmware is too short (0x%zx)\n", fwe->fw->size); goto exit; } /* CRC currently not checked */ fwe->board_type = le32_to_cpu(fwh->board_type); if (fwe->board_type != board_type) { dev_printk(KERN_ERR, &udev->dev, "board type mismatch, requested %u, got %u\n", board_type, fwe->board_type); goto exit; } fwe->fw_version.major = fwh->major; fwe->fw_version.minor = fwh->minor; fwe->fw_version.patch = fwh->patch; fwe->fw_version.build = fwh->build; str = (char *)fwh + le32_to_cpu(fwh->str_offset); fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); fwe->loaded = 1; dev_printk(KERN_DEBUG, &udev->dev, "using firmware %s (version %d.%d.%d-%d)\n", fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); at76_dbg(DBG_DEVSTART, "firmware id %s", str); exit: mutex_unlock(&fw_mutex); if (fwe->loaded) return fwe; else return NULL; } static int at76_join(struct at76_priv *priv) { struct at76_req_join join; int ret; memset(&join, 0, sizeof(struct at76_req_join)); memcpy(join.essid, priv->essid, priv->essid_size); join.essid_size = priv->essid_size; memcpy(join.bssid, priv->bssid, ETH_ALEN); join.bss_type = INFRASTRUCTURE_MODE; join.channel = priv->channel; join.timeout = cpu_to_le16(2000); at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__); ret = at76_set_card_command(priv->udev, CMD_JOIN, &join, sizeof(struct at76_req_join)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", ret); return 0; } ret = at76_wait_completion(priv, CMD_JOIN); at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret); if (ret != CMD_STATUS_COMPLETE) { wiphy_err(priv->hw->wiphy, "at76_wait_completion failed: %d\n", ret); return 0; } at76_set_pm_mode(priv); return 0; } static void at76_work_join_bssid(struct work_struct *work) { struct at76_priv *priv = container_of(work, struct at76_priv, work_join_bssid); if (priv->device_unplugged) return; mutex_lock(&priv->mtx); if (is_valid_ether_addr(priv->bssid)) at76_join(priv); mutex_unlock(&priv->mtx); } static void at76_mac80211_tx_callback(struct urb *urb) { struct at76_priv *priv = urb->context; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); at76_dbg(DBG_MAC80211, "%s()", __func__); switch (urb->status) { case 0: /* success */ info->flags |= IEEE80211_TX_STAT_ACK; break; case -ENOENT: case -ECONNRESET: /* fail, urb has been unlinked */ /* FIXME: add error message */ break; default: at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", __func__, urb->status); break; } memset(&info->status, 0, sizeof(info->status)); ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); priv->tx_skb = NULL; ieee80211_wake_queues(priv->hw); } static void at76_mac80211_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct at76_priv *priv = hw->priv; struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; int padding, submit_len, ret; at76_dbg(DBG_MAC80211, "%s()", __func__); if (priv->tx_urb->status == -EINPROGRESS) { wiphy_err(priv->hw->wiphy, "%s called while tx urb is pending\n", __func__); dev_kfree_skb_any(skb); return; } /* The following code lines are important when the device is going to * authenticate with a new bssid. The driver must send CMD_JOIN before * an authentication frame is transmitted. For this to succeed, the * correct bssid of the AP must be known. As mac80211 does not inform * drivers about the bssid prior to the authentication process the * following workaround is necessary. If the TX frame is an * authentication frame extract the bssid and send the CMD_JOIN. */ if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { if (!ether_addr_equal(priv->bssid, mgmt->bssid)) { memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); ieee80211_queue_work(hw, &priv->work_join_bssid); dev_kfree_skb_any(skb); return; } } ieee80211_stop_queues(hw); at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ WARN_ON(priv->tx_skb != NULL); priv->tx_skb = skb; padding = at76_calc_padding(skb->len); submit_len = AT76_TX_HDRLEN + skb->len + padding; /* setup 'Atmel' header */ memset(tx_buffer, 0, sizeof(*tx_buffer)); tx_buffer->padding = padding; tx_buffer->wlength = cpu_to_le16(skb->len); tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value; memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); memcpy(tx_buffer->packet, skb->data, skb->len); at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr", wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength), tx_buffer->padding, tx_buffer->tx_rate); /* send stuff */ at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len, "%s(): tx_buffer %d bytes:", __func__, submit_len); usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, submit_len, at76_mac80211_tx_callback, priv); ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); if (ret) { wiphy_err(priv->hw->wiphy, "error in tx submit urb: %d\n", ret); if (ret == -EINVAL) wiphy_err(priv->hw->wiphy, "-EINVAL: tx urb %p hcpriv %p complete %p\n", priv->tx_urb, priv->tx_urb->hcpriv, priv->tx_urb->complete); } } static int at76_mac80211_start(struct ieee80211_hw *hw) { struct at76_priv *priv = hw->priv; int ret; at76_dbg(DBG_MAC80211, "%s()", __func__); mutex_lock(&priv->mtx); ret = at76_submit_rx_urb(priv); if (ret < 0) { wiphy_err(priv->hw->wiphy, "open: submit_rx_urb failed: %d\n", ret); goto error; } at76_startup_device(priv); at76_start_monitor(priv); error: mutex_unlock(&priv->mtx); return 0; } static void at76_mac80211_stop(struct ieee80211_hw *hw) { struct at76_priv *priv = hw->priv; at76_dbg(DBG_MAC80211, "%s()", __func__); cancel_delayed_work(&priv->dwork_hw_scan); cancel_work_sync(&priv->work_join_bssid); cancel_work_sync(&priv->work_set_promisc); mutex_lock(&priv->mtx); if (!priv->device_unplugged) { /* We are called by "ifconfig ethX down", not because the * device is not available anymore. */ at76_set_radio(priv, 0); /* We unlink rx_urb because at76_open() re-submits it. * If unplugged, at76_delete_device() takes care of it. */ usb_kill_urb(priv->rx_urb); } mutex_unlock(&priv->mtx); } static int at76_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct at76_priv *priv = hw->priv; int ret = 0; at76_dbg(DBG_MAC80211, "%s()", __func__); mutex_lock(&priv->mtx); switch (vif->type) { case NL80211_IFTYPE_STATION: priv->iw_mode = IW_MODE_INFRA; break; default: ret = -EOPNOTSUPP; goto exit; } exit: mutex_unlock(&priv->mtx); return ret; } static void at76_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { at76_dbg(DBG_MAC80211, "%s()", __func__); } static void at76_dwork_hw_scan(struct work_struct *work) { struct at76_priv *priv = container_of(work, struct at76_priv, dwork_hw_scan.work); int ret; if (priv->device_unplugged) return; mutex_lock(&priv->mtx); ret = at76_get_cmd_status(priv->udev, CMD_SCAN); at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret); /* FIXME: add maximum time for scan to complete */ if (ret != CMD_STATUS_COMPLETE) { ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, SCAN_POLL_INTERVAL); mutex_unlock(&priv->mtx); return; } if (is_valid_ether_addr(priv->bssid)) at76_join(priv); mutex_unlock(&priv->mtx); ieee80211_scan_completed(priv->hw, false); ieee80211_wake_queues(priv->hw); } static int at76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct at76_priv *priv = hw->priv; struct at76_req_scan scan; u8 *ssid = NULL; int ret, len = 0; at76_dbg(DBG_MAC80211, "%s():", __func__); if (priv->device_unplugged) return 0; mutex_lock(&priv->mtx); ieee80211_stop_queues(hw); memset(&scan, 0, sizeof(struct at76_req_scan)); memset(scan.bssid, 0xFF, ETH_ALEN); if (req->n_ssids) { scan.scan_type = SCAN_TYPE_ACTIVE; ssid = req->ssids[0].ssid; len = req->ssids[0].ssid_len; } else { scan.scan_type = SCAN_TYPE_PASSIVE; } if (len) { memcpy(scan.essid, ssid, len); scan.essid_size = len; } scan.min_channel_time = cpu_to_le16(priv->scan_min_time); scan.max_channel_time = cpu_to_le16(priv->scan_max_time); scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); scan.international_scan = 0; at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__); ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "CMD_SCAN failed: %d\n", ret); goto exit; } ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, SCAN_POLL_INTERVAL); exit: mutex_unlock(&priv->mtx); return 0; } static int at76_config(struct ieee80211_hw *hw, u32 changed) { struct at76_priv *priv = hw->priv; at76_dbg(DBG_MAC80211, "%s(): channel %d", __func__, hw->conf.channel->hw_value); at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); mutex_lock(&priv->mtx); priv->channel = hw->conf.channel->hw_value; if (is_valid_ether_addr(priv->bssid)) at76_join(priv); else at76_start_monitor(priv); mutex_unlock(&priv->mtx); return 0; } static void at76_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf, u32 changed) { struct at76_priv *priv = hw->priv; at76_dbg(DBG_MAC80211, "%s():", __func__); if (!(changed & BSS_CHANGED_BSSID)) return; at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); mutex_lock(&priv->mtx); memcpy(priv->bssid, conf->bssid, ETH_ALEN); if (is_valid_ether_addr(priv->bssid)) /* mac80211 is joining a bss */ at76_join(priv); mutex_unlock(&priv->mtx); } /* must be atomic */ static void at76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct at76_priv *priv = hw->priv; int flags; at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " "total_flags=0x%08x", __func__, changed_flags, *total_flags); flags = changed_flags & AT76_SUPPORTED_FILTERS; *total_flags = AT76_SUPPORTED_FILTERS; /* Bail out after updating flags to prevent a WARN_ON in mac80211. */ if (priv->device_unplugged) return; /* FIXME: access to priv->promisc should be protected with * priv->mtx, but it's impossible because this function needs to be * atomic */ if (flags && !priv->promisc) { /* mac80211 wants us to enable promiscuous mode */ priv->promisc = 1; } else if (!flags && priv->promisc) { /* we need to disable promiscuous mode */ priv->promisc = 0; } else return; ieee80211_queue_work(hw, &priv->work_set_promisc); } static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct at76_priv *priv = hw->priv; int i; at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d " "key->keylen %d", __func__, cmd, key->cipher, key->keyidx, key->keylen); if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) && (key->cipher != WLAN_CIPHER_SUITE_WEP104)) return -EOPNOTSUPP; key->hw_key_idx = key->keyidx; mutex_lock(&priv->mtx); switch (cmd) { case SET_KEY: memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen); priv->wep_keys_len[key->keyidx] = key->keylen; /* FIXME: find out how to do this properly */ priv->wep_key_id = key->keyidx; break; case DISABLE_KEY: default: priv->wep_keys_len[key->keyidx] = 0; break; } priv->wep_enabled = 0; for (i = 0; i < WEP_KEYS; i++) { if (priv->wep_keys_len[i] != 0) priv->wep_enabled = 1; } at76_startup_device(priv); mutex_unlock(&priv->mtx); return 0; } static const struct ieee80211_ops at76_ops = { .tx = at76_mac80211_tx, .add_interface = at76_add_interface, .remove_interface = at76_remove_interface, .config = at76_config, .bss_info_changed = at76_bss_info_changed, .configure_filter = at76_configure_filter, .start = at76_mac80211_start, .stop = at76_mac80211_stop, .hw_scan = at76_hw_scan, .set_key = at76_set_key, }; /* Allocate network device and initialize private data */ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) { struct ieee80211_hw *hw; struct at76_priv *priv; hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops); if (!hw) { printk(KERN_ERR DRIVER_NAME ": could not register" " ieee80211_hw\n"); return NULL; } priv = hw->priv; priv->hw = hw; priv->udev = udev; mutex_init(&priv->mtx); INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid); INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); tasklet_init(&priv->rx_tasklet, at76_rx_tasklet, 0); priv->pm_mode = AT76_PM_OFF; priv->pm_period = 0; /* unit us */ priv->hw->channel_change_time = 100000; return priv; } static int at76_alloc_urbs(struct at76_priv *priv, struct usb_interface *interface) { struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; int i; int buffer_size; struct usb_host_interface *iface_desc; at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, interface->altsetting[0].desc.bNumEndpoints); ep_in = NULL; ep_out = NULL; iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { endpoint = &iface_desc->endpoint[i].desc; at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", __func__, i, endpoint->bEndpointAddress, endpoint->bmAttributes); if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) ep_in = endpoint; if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) ep_out = endpoint; } if (!ep_in || !ep_out) { dev_printk(KERN_ERR, &interface->dev, "bulk endpoints missing\n"); return -ENXIO; } priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress); priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress); priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!priv->rx_urb || !priv->tx_urb) { dev_printk(KERN_ERR, &interface->dev, "cannot allocate URB\n"); return -ENOMEM; } buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!priv->bulk_out_buffer) { dev_printk(KERN_ERR, &interface->dev, "cannot allocate output buffer\n"); return -ENOMEM; } at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); return 0; } static struct ieee80211_rate at76_rates[] = { { .bitrate = 10, .hw_value = TX_RATE_1MBIT, }, { .bitrate = 20, .hw_value = TX_RATE_2MBIT, }, { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, }, { .bitrate = 110, .hw_value = TX_RATE_11MBIT, }, }; static struct ieee80211_channel at76_channels[] = { { .center_freq = 2412, .hw_value = 1 }, { .center_freq = 2417, .hw_value = 2 }, { .center_freq = 2422, .hw_value = 3 }, { .center_freq = 2427, .hw_value = 4 }, { .center_freq = 2432, .hw_value = 5 }, { .center_freq = 2437, .hw_value = 6 }, { .center_freq = 2442, .hw_value = 7 }, { .center_freq = 2447, .hw_value = 8 }, { .center_freq = 2452, .hw_value = 9 }, { .center_freq = 2457, .hw_value = 10 }, { .center_freq = 2462, .hw_value = 11 }, { .center_freq = 2467, .hw_value = 12 }, { .center_freq = 2472, .hw_value = 13 }, { .center_freq = 2484, .hw_value = 14 } }; static struct ieee80211_supported_band at76_supported_band = { .channels = at76_channels, .n_channels = ARRAY_SIZE(at76_channels), .bitrates = at76_rates, .n_bitrates = ARRAY_SIZE(at76_rates), }; /* Register network device and initialize the hardware */ static int at76_init_new_device(struct at76_priv *priv, struct usb_interface *interface) { struct wiphy *wiphy; size_t len; int ret; /* set up the endpoint information */ /* check out the endpoints */ at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", interface->cur_altsetting->desc.bNumEndpoints); ret = at76_alloc_urbs(priv, interface); if (ret < 0) goto exit; /* MAC address */ ret = at76_get_hw_config(priv); if (ret < 0) { dev_printk(KERN_ERR, &interface->dev, "cannot get MAC address\n"); goto exit; } priv->domain = at76_get_reg_domain(priv->regulatory_domain); priv->channel = DEF_CHANNEL; priv->iw_mode = IW_MODE_INFRA; priv->rts_threshold = DEF_RTS_THRESHOLD; priv->frag_threshold = DEF_FRAG_THRESHOLD; priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT; priv->txrate = TX_RATE_AUTO; priv->preamble_type = PREAMBLE_TYPE_LONG; priv->beacon_period = 100; priv->auth_mode = WLAN_AUTH_OPEN; priv->scan_min_time = DEF_SCAN_MIN_TIME; priv->scan_max_time = DEF_SCAN_MAX_TIME; priv->scan_mode = SCAN_TYPE_ACTIVE; priv->device_unplugged = 0; /* mac80211 initialisation */ wiphy = priv->hw->wiphy; priv->hw->wiphy->max_scan_ssids = 1; priv->hw->wiphy->max_scan_ie_len = 0; priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &at76_supported_band; priv->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_UNSPEC; priv->hw->max_signal = 100; SET_IEEE80211_DEV(priv->hw, &interface->dev); SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); len = sizeof(wiphy->fw_version); snprintf(wiphy->fw_version, len, "%d.%d.%d-%d", priv->fw_version.major, priv->fw_version.minor, priv->fw_version.patch, priv->fw_version.build); wiphy->hw_version = priv->board_type; ret = ieee80211_register_hw(priv->hw); if (ret) { printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", ret); goto exit; } priv->mac80211_registered = 1; wiphy_info(priv->hw->wiphy, "USB %s, MAC %pM, firmware %d.%d.%d-%d\n", dev_name(&interface->dev), priv->mac_addr, priv->fw_version.major, priv->fw_version.minor, priv->fw_version.patch, priv->fw_version.build); wiphy_info(priv->hw->wiphy, "regulatory domain 0x%02x: %s\n", priv->regulatory_domain, priv->domain->name); exit: return ret; } static void at76_delete_device(struct at76_priv *priv) { at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); /* The device is gone, don't bother turning it off */ priv->device_unplugged = 1; tasklet_kill(&priv->rx_tasklet); if (priv->mac80211_registered) ieee80211_unregister_hw(priv->hw); if (priv->tx_urb) { usb_kill_urb(priv->tx_urb); usb_free_urb(priv->tx_urb); } if (priv->rx_urb) { usb_kill_urb(priv->rx_urb); usb_free_urb(priv->rx_urb); } at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__); kfree(priv->bulk_out_buffer); del_timer_sync(&ledtrig_tx_timer); kfree_skb(priv->rx_skb); usb_put_dev(priv->udev); at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw", __func__); ieee80211_free_hw(priv->hw); at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); } static int at76_probe(struct usb_interface *interface, const struct usb_device_id *id) { int ret; struct at76_priv *priv; struct fwentry *fwe; struct usb_device *udev; int op_mode; int need_ext_fw = 0; struct mib_fw_version fwv; int board_type = (int)id->driver_info; udev = usb_get_dev(interface_to_usbdev(interface)); /* Load firmware into kernel memory */ fwe = at76_load_firmware(udev, board_type); if (!fwe) { ret = -ENOENT; goto error; } op_mode = at76_get_op_mode(udev); at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ if (op_mode == OPMODE_HW_CONFIG_MODE) { dev_printk(KERN_ERR, &interface->dev, "cannot handle a device in HW_CONFIG_MODE\n"); ret = -EBUSY; goto error; } if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { /* download internal firmware part */ dev_printk(KERN_DEBUG, &interface->dev, "downloading internal firmware\n"); ret = at76_load_internal_fw(udev, fwe); if (ret < 0) { dev_printk(KERN_ERR, &interface->dev, "error %d downloading internal firmware\n", ret); goto error; } usb_put_dev(udev); return ret; } /* Internal firmware already inside the device. Get firmware * version to test if external firmware is loaded. * This works only for newer firmware, e.g. the Intersil 0.90.x * says "control timeout on ep0in" and subsequent * at76_get_op_mode() fail too :-( */ /* if version >= 0.100.x.y or device with built-in flash we can * query the device for the fw version */ if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); if (ret < 0 || (fwv.major | fwv.minor) == 0) need_ext_fw = 1; } else /* No way to check firmware version, reload to be sure */ need_ext_fw = 1; if (need_ext_fw) { dev_printk(KERN_DEBUG, &interface->dev, "downloading external firmware\n"); ret = at76_load_external_fw(udev, fwe); if (ret) goto error; /* Re-check firmware version */ ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); if (ret < 0) { dev_printk(KERN_ERR, &interface->dev, "error %d getting firmware version\n", ret); goto error; } } priv = at76_alloc_new_device(udev); if (!priv) { ret = -ENOMEM; goto error; } usb_set_intfdata(interface, priv); memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version)); priv->board_type = board_type; ret = at76_init_new_device(priv, interface); if (ret < 0) at76_delete_device(priv); return ret; error: usb_put_dev(udev); return ret; } static void at76_disconnect(struct usb_interface *interface) { struct at76_priv *priv; priv = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); /* Disconnect after loading internal firmware */ if (!priv) return; wiphy_info(priv->hw->wiphy, "disconnecting\n"); at76_delete_device(priv); dev_printk(KERN_INFO, &interface->dev, "disconnected\n"); } /* Structure for registering this driver with the USB subsystem */ static struct usb_driver at76_driver = { .name = DRIVER_NAME, .probe = at76_probe, .disconnect = at76_disconnect, .id_table = dev_table, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; static int __init at76_mod_init(void) { int result; printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n"); mutex_init(&fw_mutex); /* register this driver with the USB subsystem */ result = usb_register(&at76_driver); if (result < 0) printk(KERN_ERR DRIVER_NAME ": usb_register failed (status %d)\n", result); led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); return result; } static void __exit at76_mod_exit(void) { int i; printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n"); usb_deregister(&at76_driver); for (i = 0; i < ARRAY_SIZE(firmwares); i++) release_firmware(firmwares[i].fw); led_trigger_unregister_simple(ledtrig_tx); } module_param_named(debug, at76_debug, uint, 0600); MODULE_PARM_DESC(debug, "Debugging level"); module_init(at76_mod_init); module_exit(at76_mod_exit); MODULE_AUTHOR("Oliver Kurth "); MODULE_AUTHOR("Joerg Albert "); MODULE_AUTHOR("Alex "); MODULE_AUTHOR("Nick Jones"); MODULE_AUTHOR("Balint Seeber "); MODULE_AUTHOR("Pavel Roskin "); MODULE_AUTHOR("Guido Guenther "); MODULE_AUTHOR("Kalle Valo "); MODULE_AUTHOR("Sebastian Smolorz "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/mwl8k.c0000644000175000017500000043524212026211315021701 0ustar mcgrofmcgrof/* * drivers/net/wireless/mwl8k.c * Driver for Marvell TOPDOG 802.11 Wireless cards * * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" #define MWL8K_NAME KBUILD_MODNAME #define MWL8K_VERSION "0.13" /* Module parameters */ static bool ap_mode_default; module_param(ap_mode_default, bool, 0); MODULE_PARM_DESC(ap_mode_default, "Set to 1 to make ap mode the default instead of sta mode"); /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 #define MWL8K_MODE_STA 0x0000005a #define MWL8K_MODE_AP 0x000000a5 #define MWL8K_HIU_INT_CODE 0x00000c14 #define MWL8K_FWSTA_READY 0xf0f1f2f4 #define MWL8K_FWAP_READY 0xf1f2f4a5 #define MWL8K_INT_CODE_CMD_FINISHED 0x00000005 #define MWL8K_HIU_SCRATCH 0x00000c40 /* Host->device communications */ #define MWL8K_HIU_H2A_INTERRUPT_EVENTS 0x00000c18 #define MWL8K_HIU_H2A_INTERRUPT_STATUS 0x00000c1c #define MWL8K_HIU_H2A_INTERRUPT_MASK 0x00000c20 #define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL 0x00000c24 #define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK 0x00000c28 #define MWL8K_H2A_INT_DUMMY (1 << 20) #define MWL8K_H2A_INT_RESET (1 << 15) #define MWL8K_H2A_INT_DOORBELL (1 << 1) #define MWL8K_H2A_INT_PPA_READY (1 << 0) /* Device->host communications */ #define MWL8K_HIU_A2H_INTERRUPT_EVENTS 0x00000c2c #define MWL8K_HIU_A2H_INTERRUPT_STATUS 0x00000c30 #define MWL8K_HIU_A2H_INTERRUPT_MASK 0x00000c34 #define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL 0x00000c38 #define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK 0x00000c3c #define MWL8K_A2H_INT_DUMMY (1 << 20) #define MWL8K_A2H_INT_BA_WATCHDOG (1 << 14) #define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11) #define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10) #define MWL8K_A2H_INT_RADAR_DETECT (1 << 7) #define MWL8K_A2H_INT_RADIO_ON (1 << 6) #define MWL8K_A2H_INT_RADIO_OFF (1 << 5) #define MWL8K_A2H_INT_MAC_EVENT (1 << 3) #define MWL8K_A2H_INT_OPC_DONE (1 << 2) #define MWL8K_A2H_INT_RX_READY (1 << 1) #define MWL8K_A2H_INT_TX_DONE (1 << 0) /* HW micro second timer register * located at offset 0xA600. This * will be used to timestamp tx * packets. */ #define MWL8K_HW_TIMER_REGISTER 0x0000a600 #define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ MWL8K_A2H_INT_CHNL_SWITCHED | \ MWL8K_A2H_INT_QUEUE_EMPTY | \ MWL8K_A2H_INT_RADAR_DETECT | \ MWL8K_A2H_INT_RADIO_ON | \ MWL8K_A2H_INT_RADIO_OFF | \ MWL8K_A2H_INT_MAC_EVENT | \ MWL8K_A2H_INT_OPC_DONE | \ MWL8K_A2H_INT_RX_READY | \ MWL8K_A2H_INT_TX_DONE | \ MWL8K_A2H_INT_BA_WATCHDOG) #define MWL8K_RX_QUEUES 1 #define MWL8K_TX_WMM_QUEUES 4 #define MWL8K_MAX_AMPDU_QUEUES 8 #define MWL8K_MAX_TX_QUEUES (MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES) #define mwl8k_tx_queues(priv) (MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues) struct rxd_ops { int rxd_size; void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); void (*rxd_refill)(void *rxd, dma_addr_t addr, int len); int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status, __le16 *qos, s8 *noise); }; struct mwl8k_device_info { char *part_name; char *helper_image; char *fw_image_sta; char *fw_image_ap; struct rxd_ops *ap_rxd_ops; u32 fw_api_ap; }; struct mwl8k_rx_queue { int rxd_count; /* hw receives here */ int head; /* refill descs here */ int tail; void *rxd; dma_addr_t rxd_dma; struct { struct sk_buff *skb; DEFINE_DMA_UNMAP_ADDR(dma); } *buf; }; struct mwl8k_tx_queue { /* hw transmits here */ int head; /* sw appends here */ int tail; unsigned int len; struct mwl8k_tx_desc *txd; dma_addr_t txd_dma; struct sk_buff **skb; }; enum { AMPDU_NO_STREAM, AMPDU_STREAM_NEW, AMPDU_STREAM_IN_PROGRESS, AMPDU_STREAM_ACTIVE, }; struct mwl8k_ampdu_stream { struct ieee80211_sta *sta; u8 tid; u8 state; u8 idx; u8 txq_idx; /* index of this stream in priv->txq */ }; struct mwl8k_priv { struct ieee80211_hw *hw; struct pci_dev *pdev; int irq; struct mwl8k_device_info *device_info; void __iomem *sram; void __iomem *regs; /* firmware */ const struct firmware *fw_helper; const struct firmware *fw_ucode; /* hardware/firmware parameters */ bool ap_fw; struct rxd_ops *rxd_ops; struct ieee80211_supported_band band_24; struct ieee80211_channel channels_24[14]; struct ieee80211_rate rates_24[14]; struct ieee80211_supported_band band_50; struct ieee80211_channel channels_50[4]; struct ieee80211_rate rates_50[9]; u32 ap_macids_supported; u32 sta_macids_supported; /* Ampdu stream information */ u8 num_ampdu_queues; spinlock_t stream_lock; struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES]; struct work_struct watchdog_ba_handle; /* firmware access */ struct mutex fw_mutex; struct task_struct *fw_mutex_owner; struct task_struct *hw_restart_owner; int fw_mutex_depth; struct completion *hostcmd_wait; /* lock held over TX and TX reap */ spinlock_t tx_lock; /* TX quiesce completion, protected by fw_mutex and tx_lock */ struct completion *tx_wait; /* List of interfaces. */ u32 macids_used; struct list_head vif_list; /* power management status cookie from firmware */ u32 *cookie; dma_addr_t cookie_dma; u16 num_mcaddrs; u8 hw_rev; u32 fw_rev; /* * Running count of TX packets in flight, to avoid * iterating over the transmit rings each time. */ int pending_tx_pkts; struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES]; u32 txq_offset[MWL8K_MAX_TX_QUEUES]; bool radio_on; bool radio_short_preamble; bool sniffer_enabled; bool wmm_enabled; /* XXX need to convert this to handle multiple interfaces */ bool capture_beacon; u8 capture_bssid[ETH_ALEN]; struct sk_buff *beacon_skb; /* * This FJ worker has to be global as it is scheduled from the * RX handler. At this point we don't know which interface it * belongs to until the list of bssids waiting to complete join * is checked. */ struct work_struct finalize_join_worker; /* Tasklet to perform TX reclaim. */ struct tasklet_struct poll_tx_task; /* Tasklet to perform RX. */ struct tasklet_struct poll_rx_task; /* Most recently reported noise in dBm */ s8 noise; /* * preserve the queue configurations so they can be restored if/when * the firmware image is swapped. */ struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES]; /* To perform the task of reloading the firmware */ struct work_struct fw_reload; bool hw_restart_in_progress; /* async firmware loading state */ unsigned fw_state; char *fw_pref; char *fw_alt; struct completion firmware_loading_complete; }; #define MAX_WEP_KEY_LEN 13 #define NUM_WEP_KEYS 4 /* Per interface specific private data */ struct mwl8k_vif { struct list_head list; struct ieee80211_vif *vif; /* Firmware macid for this vif. */ int macid; /* Non AMPDU sequence number assigned by driver. */ u16 seqno; /* Saved WEP keys */ struct { u8 enabled; u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN]; } wep_key_conf[NUM_WEP_KEYS]; /* BSSID */ u8 bssid[ETH_ALEN]; /* A flag to indicate is HW crypto is enabled for this bssid */ bool is_hw_crypto_enabled; }; #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) #define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8)) struct tx_traffic_info { u32 start_time; u32 pkts; }; #define MWL8K_MAX_TID 8 struct mwl8k_sta { /* Index into station database. Returned by UPDATE_STADB. */ u8 peer_id; u8 is_ampdu_allowed; struct tx_traffic_info tx_stats[MWL8K_MAX_TID]; }; #define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) static const struct ieee80211_channel mwl8k_channels_24[] = { { .center_freq = 2412, .hw_value = 1, }, { .center_freq = 2417, .hw_value = 2, }, { .center_freq = 2422, .hw_value = 3, }, { .center_freq = 2427, .hw_value = 4, }, { .center_freq = 2432, .hw_value = 5, }, { .center_freq = 2437, .hw_value = 6, }, { .center_freq = 2442, .hw_value = 7, }, { .center_freq = 2447, .hw_value = 8, }, { .center_freq = 2452, .hw_value = 9, }, { .center_freq = 2457, .hw_value = 10, }, { .center_freq = 2462, .hw_value = 11, }, { .center_freq = 2467, .hw_value = 12, }, { .center_freq = 2472, .hw_value = 13, }, { .center_freq = 2484, .hw_value = 14, }, }; static const struct ieee80211_rate mwl8k_rates_24[] = { { .bitrate = 10, .hw_value = 2, }, { .bitrate = 20, .hw_value = 4, }, { .bitrate = 55, .hw_value = 11, }, { .bitrate = 110, .hw_value = 22, }, { .bitrate = 220, .hw_value = 44, }, { .bitrate = 60, .hw_value = 12, }, { .bitrate = 90, .hw_value = 18, }, { .bitrate = 120, .hw_value = 24, }, { .bitrate = 180, .hw_value = 36, }, { .bitrate = 240, .hw_value = 48, }, { .bitrate = 360, .hw_value = 72, }, { .bitrate = 480, .hw_value = 96, }, { .bitrate = 540, .hw_value = 108, }, { .bitrate = 720, .hw_value = 144, }, }; static const struct ieee80211_channel mwl8k_channels_50[] = { { .center_freq = 5180, .hw_value = 36, }, { .center_freq = 5200, .hw_value = 40, }, { .center_freq = 5220, .hw_value = 44, }, { .center_freq = 5240, .hw_value = 48, }, }; static const struct ieee80211_rate mwl8k_rates_50[] = { { .bitrate = 60, .hw_value = 12, }, { .bitrate = 90, .hw_value = 18, }, { .bitrate = 120, .hw_value = 24, }, { .bitrate = 180, .hw_value = 36, }, { .bitrate = 240, .hw_value = 48, }, { .bitrate = 360, .hw_value = 72, }, { .bitrate = 480, .hw_value = 96, }, { .bitrate = 540, .hw_value = 108, }, { .bitrate = 720, .hw_value = 144, }, }; /* Set or get info from Firmware */ #define MWL8K_CMD_GET 0x0000 #define MWL8K_CMD_SET 0x0001 #define MWL8K_CMD_SET_LIST 0x0002 /* Firmware command codes */ #define MWL8K_CMD_CODE_DNLD 0x0001 #define MWL8K_CMD_GET_HW_SPEC 0x0003 #define MWL8K_CMD_SET_HW_SPEC 0x0004 #define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 #define MWL8K_CMD_GET_STAT 0x0014 #define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_TX_POWER 0x001f #define MWL8K_CMD_RF_ANTENNA 0x0020 #define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */ #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_RF_CHANNEL 0x010a #define MWL8K_CMD_SET_AID 0x010d #define MWL8K_CMD_SET_RATE 0x0110 #define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111 #define MWL8K_CMD_RTS_THRESHOLD 0x0113 #define MWL8K_CMD_SET_SLOT 0x0114 #define MWL8K_CMD_SET_EDCA_PARAMS 0x0115 #define MWL8K_CMD_SET_WMM_MODE 0x0123 #define MWL8K_CMD_MIMO_CONFIG 0x0125 #define MWL8K_CMD_USE_FIXED_RATE 0x0126 #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 #define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 #define MWL8K_CMD_GET_WATCHDOG_BITMAP 0x0205 #define MWL8K_CMD_DEL_MAC_ADDR 0x0206 /* per-vif */ #define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ #define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ #define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ #define MWL8K_CMD_UPDATE_STADB 0x1123 #define MWL8K_CMD_BASTREAM 0x1125 static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize) { u16 command = le16_to_cpu(cmd); #define MWL8K_CMDNAME(x) case MWL8K_CMD_##x: do {\ snprintf(buf, bufsize, "%s", #x);\ return buf;\ } while (0) switch (command & ~0x8000) { MWL8K_CMDNAME(CODE_DNLD); MWL8K_CMDNAME(GET_HW_SPEC); MWL8K_CMDNAME(SET_HW_SPEC); MWL8K_CMDNAME(MAC_MULTICAST_ADR); MWL8K_CMDNAME(GET_STAT); MWL8K_CMDNAME(RADIO_CONTROL); MWL8K_CMDNAME(RF_TX_POWER); MWL8K_CMDNAME(TX_POWER); MWL8K_CMDNAME(RF_ANTENNA); MWL8K_CMDNAME(SET_BEACON); MWL8K_CMDNAME(SET_PRE_SCAN); MWL8K_CMDNAME(SET_POST_SCAN); MWL8K_CMDNAME(SET_RF_CHANNEL); MWL8K_CMDNAME(SET_AID); MWL8K_CMDNAME(SET_RATE); MWL8K_CMDNAME(SET_FINALIZE_JOIN); MWL8K_CMDNAME(RTS_THRESHOLD); MWL8K_CMDNAME(SET_SLOT); MWL8K_CMDNAME(SET_EDCA_PARAMS); MWL8K_CMDNAME(SET_WMM_MODE); MWL8K_CMDNAME(MIMO_CONFIG); MWL8K_CMDNAME(USE_FIXED_RATE); MWL8K_CMDNAME(ENABLE_SNIFFER); MWL8K_CMDNAME(SET_MAC_ADDR); MWL8K_CMDNAME(SET_RATEADAPT_MODE); MWL8K_CMDNAME(BSS_START); MWL8K_CMDNAME(SET_NEW_STN); MWL8K_CMDNAME(UPDATE_ENCRYPTION); MWL8K_CMDNAME(UPDATE_STADB); MWL8K_CMDNAME(BASTREAM); MWL8K_CMDNAME(GET_WATCHDOG_BITMAP); default: snprintf(buf, bufsize, "0x%x", cmd); } #undef MWL8K_CMDNAME return buf; } /* Hardware and firmware reset */ static void mwl8k_hw_reset(struct mwl8k_priv *priv) { iowrite32(MWL8K_H2A_INT_RESET, priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); iowrite32(MWL8K_H2A_INT_RESET, priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); msleep(20); } /* Release fw image */ static void mwl8k_release_fw(const struct firmware **fw) { if (*fw == NULL) return; release_firmware(*fw); *fw = NULL; } static void mwl8k_release_firmware(struct mwl8k_priv *priv) { mwl8k_release_fw(&priv->fw_ucode); mwl8k_release_fw(&priv->fw_helper); } /* states for asynchronous f/w loading */ static void mwl8k_fw_state_machine(const struct firmware *fw, void *context); enum { FW_STATE_INIT = 0, FW_STATE_LOADING_PREF, FW_STATE_LOADING_ALT, FW_STATE_ERROR, }; /* Request fw image */ static int mwl8k_request_fw(struct mwl8k_priv *priv, const char *fname, const struct firmware **fw, bool nowait) { /* release current image */ if (*fw != NULL) mwl8k_release_fw(fw); if (nowait) return request_firmware_nowait(THIS_MODULE, 1, fname, &priv->pdev->dev, GFP_KERNEL, priv, mwl8k_fw_state_machine); else return request_firmware(fw, fname, &priv->pdev->dev); } static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image, bool nowait) { struct mwl8k_device_info *di = priv->device_info; int rc; if (di->helper_image != NULL) { if (nowait) rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper, true); else rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper, false); if (rc) printk(KERN_ERR "%s: Error requesting helper fw %s\n", pci_name(priv->pdev), di->helper_image); if (rc || nowait) return rc; } if (nowait) { /* * if we get here, no helper image is needed. Skip the * FW_STATE_INIT state. */ priv->fw_state = FW_STATE_LOADING_PREF; rc = mwl8k_request_fw(priv, fw_image, &priv->fw_ucode, true); } else rc = mwl8k_request_fw(priv, fw_image, &priv->fw_ucode, false); if (rc) { printk(KERN_ERR "%s: Error requesting firmware file %s\n", pci_name(priv->pdev), fw_image); mwl8k_release_fw(&priv->fw_helper); return rc; } return 0; } struct mwl8k_cmd_pkt { __le16 code; __le16 length; __u8 seq_num; __u8 macid; __le16 result; char payload[0]; } __packed; /* * Firmware loading. */ static int mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) { void __iomem *regs = priv->regs; dma_addr_t dma_addr; int loops; dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(priv->pdev, dma_addr)) return -ENOMEM; iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); iowrite32(0, regs + MWL8K_HIU_INT_CODE); iowrite32(MWL8K_H2A_INT_DOORBELL, regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); iowrite32(MWL8K_H2A_INT_DUMMY, regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); loops = 1000; do { u32 int_code; int_code = ioread32(regs + MWL8K_HIU_INT_CODE); if (int_code == MWL8K_INT_CODE_CMD_FINISHED) { iowrite32(0, regs + MWL8K_HIU_INT_CODE); break; } cond_resched(); udelay(1); } while (--loops); pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE); return loops ? 0 : -ETIMEDOUT; } static int mwl8k_load_fw_image(struct mwl8k_priv *priv, const u8 *data, size_t length) { struct mwl8k_cmd_pkt *cmd; int done; int rc = 0; cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); cmd->seq_num = 0; cmd->macid = 0; cmd->result = 0; done = 0; while (length) { int block_size = length > 256 ? 256 : length; memcpy(cmd->payload, data + done, block_size); cmd->length = cpu_to_le16(block_size); rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd) + block_size); if (rc) break; done += block_size; length -= block_size; } if (!rc) { cmd->length = 0; rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd)); } kfree(cmd); return rc; } static int mwl8k_feed_fw_image(struct mwl8k_priv *priv, const u8 *data, size_t length) { unsigned char *buffer; int may_continue, rc = 0; u32 done, prev_block_size; buffer = kmalloc(1024, GFP_KERNEL); if (buffer == NULL) return -ENOMEM; done = 0; prev_block_size = 0; may_continue = 1000; while (may_continue > 0) { u32 block_size; block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH); if (block_size & 1) { block_size &= ~1; may_continue--; } else { done += prev_block_size; length -= prev_block_size; } if (block_size > 1024 || block_size > length) { rc = -EOVERFLOW; break; } if (length == 0) { rc = 0; break; } if (block_size == 0) { rc = -EPROTO; may_continue--; udelay(1); continue; } prev_block_size = block_size; memcpy(buffer, data + done, block_size); rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size); if (rc) break; } if (!rc && length != 0) rc = -EREMOTEIO; kfree(buffer); return rc; } static int mwl8k_load_firmware(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; const struct firmware *fw = priv->fw_ucode; int rc; int loops; if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) { const struct firmware *helper = priv->fw_helper; if (helper == NULL) { printk(KERN_ERR "%s: helper image needed but none " "given\n", pci_name(priv->pdev)); return -EINVAL; } rc = mwl8k_load_fw_image(priv, helper->data, helper->size); if (rc) { printk(KERN_ERR "%s: unable to load firmware " "helper image\n", pci_name(priv->pdev)); return rc; } msleep(20); rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); } else { rc = mwl8k_load_fw_image(priv, fw->data, fw->size); } if (rc) { printk(KERN_ERR "%s: unable to load firmware image\n", pci_name(priv->pdev)); return rc; } iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); loops = 500000; do { u32 ready_code; ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE); if (ready_code == MWL8K_FWAP_READY) { priv->ap_fw = true; break; } else if (ready_code == MWL8K_FWSTA_READY) { priv->ap_fw = false; break; } cond_resched(); udelay(1); } while (--loops); return loops ? 0 : -ETIMEDOUT; } /* DMA header used by firmware and hardware. */ struct mwl8k_dma_data { __le16 fwlen; struct ieee80211_hdr wh; char data[0]; } __packed; /* Routines to add/remove DMA header from skb. */ static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos) { struct mwl8k_dma_data *tr; int hdrlen; tr = (struct mwl8k_dma_data *)skb->data; hdrlen = ieee80211_hdrlen(tr->wh.frame_control); if (hdrlen != sizeof(tr->wh)) { if (ieee80211_is_data_qos(tr->wh.frame_control)) { memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2); *((__le16 *)(tr->data - 2)) = qos; } else { memmove(tr->data - hdrlen, &tr->wh, hdrlen); } } if (hdrlen != sizeof(*tr)) skb_pull(skb, sizeof(*tr) - hdrlen); } #define REDUCED_TX_HEADROOM 8 static void mwl8k_add_dma_header(struct mwl8k_priv *priv, struct sk_buff *skb, int head_pad, int tail_pad) { struct ieee80211_hdr *wh; int hdrlen; int reqd_hdrlen; struct mwl8k_dma_data *tr; /* * Add a firmware DMA header; the firmware requires that we * present a 2-byte payload length followed by a 4-address * header (without QoS field), followed (optionally) by any * WEP/ExtIV header (but only filled in for CCMP). */ wh = (struct ieee80211_hdr *)skb->data; hdrlen = ieee80211_hdrlen(wh->frame_control); /* * Check if skb_resize is required because of * tx_headroom adjustment. */ if (priv->ap_fw && (hdrlen < (sizeof(struct ieee80211_cts) + REDUCED_TX_HEADROOM))) { if (pskb_expand_head(skb, REDUCED_TX_HEADROOM, 0, GFP_ATOMIC)) { wiphy_err(priv->hw->wiphy, "Failed to reallocate TX buffer\n"); return; } skb->truesize += REDUCED_TX_HEADROOM; } reqd_hdrlen = sizeof(*tr) + head_pad; if (hdrlen != reqd_hdrlen) skb_push(skb, reqd_hdrlen - hdrlen); if (ieee80211_is_data_qos(wh->frame_control)) hdrlen -= IEEE80211_QOS_CTL_LEN; tr = (struct mwl8k_dma_data *)skb->data; if (wh != &tr->wh) memmove(&tr->wh, wh, hdrlen); if (hdrlen != sizeof(tr->wh)) memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen); /* * Firmware length is the length of the fully formed "802.11 * payload". That is, everything except for the 802.11 header. * This includes all crypto material including the MIC. */ tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad); } static void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv, struct sk_buff *skb) { struct ieee80211_hdr *wh; struct ieee80211_tx_info *tx_info; struct ieee80211_key_conf *key_conf; int data_pad; int head_pad = 0; wh = (struct ieee80211_hdr *)skb->data; tx_info = IEEE80211_SKB_CB(skb); key_conf = NULL; if (ieee80211_is_data(wh->frame_control)) key_conf = tx_info->control.hw_key; /* * Make sure the packet header is in the DMA header format (4-address * without QoS), and add head & tail padding when HW crypto is enabled. * * We have the following trailer padding requirements: * - WEP: 4 trailer bytes (ICV) * - TKIP: 12 trailer bytes (8 MIC + 4 ICV) * - CCMP: 8 trailer bytes (MIC) */ data_pad = 0; if (key_conf != NULL) { head_pad = key_conf->iv_len; switch (key_conf->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: data_pad = 4; break; case WLAN_CIPHER_SUITE_TKIP: data_pad = 12; break; case WLAN_CIPHER_SUITE_CCMP: data_pad = 8; break; } } mwl8k_add_dma_header(priv, skb, head_pad, data_pad); } /* * Packet reception for 88w8366 AP firmware. */ struct mwl8k_rxd_8366_ap { __le16 pkt_len; __u8 sq2; __u8 rate; __le32 pkt_phys_addr; __le32 next_rxd_phys_addr; __le16 qos_control; __le16 htsig2; __le32 hw_rssi_info; __le32 hw_noise_floor_info; __u8 noise_floor; __u8 pad0[3]; __u8 rssi; __u8 rx_status; __u8 channel; __u8 rx_ctrl; } __packed; #define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT 0x80 #define MWL8K_8366_AP_RATE_INFO_40MHZ 0x40 #define MWL8K_8366_AP_RATE_INFO_RATEID(x) ((x) & 0x3f) #define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST 0x80 /* 8366 AP rx_status bits */ #define MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK 0x80 #define MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR 0xFF #define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR 0x02 #define MWL8K_8366_AP_RXSTAT_WEP_DECRYPT_ICV_ERR 0x04 #define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR 0x08 static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr) { struct mwl8k_rxd_8366_ap *rxd = _rxd; rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST; } static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len) { struct mwl8k_rxd_8366_ap *rxd = _rxd; rxd->pkt_len = cpu_to_le16(len); rxd->pkt_phys_addr = cpu_to_le32(addr); wmb(); rxd->rx_ctrl = 0; } static int mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, __le16 *qos, s8 *noise) { struct mwl8k_rxd_8366_ap *rxd = _rxd; if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST)) return -1; rmb(); memset(status, 0, sizeof(*status)); status->signal = -rxd->rssi; *noise = -rxd->noise_floor; if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) { status->flag |= RX_FLAG_HT; if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ) status->flag |= RX_FLAG_40MHZ; status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate); } else { int i; for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { if (mwl8k_rates_24[i].hw_value == rxd->rate) { status->rate_idx = i; break; } } } if (rxd->channel > 14) { status->band = IEEE80211_BAND_5GHZ; if (!(status->flag & RX_FLAG_HT)) status->rate_idx -= 5; } else { status->band = IEEE80211_BAND_2GHZ; } status->freq = ieee80211_channel_to_frequency(rxd->channel, status->band); *qos = rxd->qos_control; if ((rxd->rx_status != MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR) && (rxd->rx_status & MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK) && (rxd->rx_status & MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR)) status->flag |= RX_FLAG_MMIC_ERROR; return le16_to_cpu(rxd->pkt_len); } static struct rxd_ops rxd_8366_ap_ops = { .rxd_size = sizeof(struct mwl8k_rxd_8366_ap), .rxd_init = mwl8k_rxd_8366_ap_init, .rxd_refill = mwl8k_rxd_8366_ap_refill, .rxd_process = mwl8k_rxd_8366_ap_process, }; /* * Packet reception for STA firmware. */ struct mwl8k_rxd_sta { __le16 pkt_len; __u8 link_quality; __u8 noise_level; __le32 pkt_phys_addr; __le32 next_rxd_phys_addr; __le16 qos_control; __le16 rate_info; __le32 pad0[4]; __u8 rssi; __u8 channel; __le16 pad1; __u8 rx_ctrl; __u8 rx_status; __u8 pad2[2]; } __packed; #define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000 #define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) #define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) #define MWL8K_STA_RATE_INFO_40MHZ 0x0004 #define MWL8K_STA_RATE_INFO_SHORTGI 0x0002 #define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001 #define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02 #define MWL8K_STA_RX_CTRL_DECRYPT_ERROR 0x04 /* ICV=0 or MIC=1 */ #define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE 0x08 /* Key is uploaded only in failure case */ #define MWL8K_STA_RX_CTRL_KEY_INDEX 0x30 static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr) { struct mwl8k_rxd_sta *rxd = _rxd; rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST; } static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len) { struct mwl8k_rxd_sta *rxd = _rxd; rxd->pkt_len = cpu_to_le16(len); rxd->pkt_phys_addr = cpu_to_le32(addr); wmb(); rxd->rx_ctrl = 0; } static int mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, __le16 *qos, s8 *noise) { struct mwl8k_rxd_sta *rxd = _rxd; u16 rate_info; if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST)) return -1; rmb(); rate_info = le16_to_cpu(rxd->rate_info); memset(status, 0, sizeof(*status)); status->signal = -rxd->rssi; *noise = -rxd->noise_level; status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info); status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info); if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) status->flag |= RX_FLAG_SHORTPRE; if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) status->flag |= RX_FLAG_40MHZ; if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) status->flag |= RX_FLAG_SHORT_GI; if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) status->flag |= RX_FLAG_HT; if (rxd->channel > 14) { status->band = IEEE80211_BAND_5GHZ; if (!(status->flag & RX_FLAG_HT)) status->rate_idx -= 5; } else { status->band = IEEE80211_BAND_2GHZ; } status->freq = ieee80211_channel_to_frequency(rxd->channel, status->band); *qos = rxd->qos_control; if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) && (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE)) status->flag |= RX_FLAG_MMIC_ERROR; return le16_to_cpu(rxd->pkt_len); } static struct rxd_ops rxd_sta_ops = { .rxd_size = sizeof(struct mwl8k_rxd_sta), .rxd_init = mwl8k_rxd_sta_init, .rxd_refill = mwl8k_rxd_sta_refill, .rxd_process = mwl8k_rxd_sta_process, }; #define MWL8K_RX_DESCS 256 #define MWL8K_RX_MAXSZ 3800 static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_rx_queue *rxq = priv->rxq + index; int size; int i; rxq->rxd_count = 0; rxq->head = 0; rxq->tail = 0; size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size; rxq->rxd = pci_alloc_consistent(priv->pdev, size, &rxq->rxd_dma); if (rxq->rxd == NULL) { wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n"); return -ENOMEM; } memset(rxq->rxd, 0, size); rxq->buf = kcalloc(MWL8K_RX_DESCS, sizeof(*rxq->buf), GFP_KERNEL); if (rxq->buf == NULL) { wiphy_err(hw->wiphy, "failed to alloc RX skbuff list\n"); pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma); return -ENOMEM; } for (i = 0; i < MWL8K_RX_DESCS; i++) { int desc_size; void *rxd; int nexti; dma_addr_t next_dma_addr; desc_size = priv->rxd_ops->rxd_size; rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size); nexti = i + 1; if (nexti == MWL8K_RX_DESCS) nexti = 0; next_dma_addr = rxq->rxd_dma + (nexti * desc_size); priv->rxd_ops->rxd_init(rxd, next_dma_addr); } return 0; } static int rxq_refill(struct ieee80211_hw *hw, int index, int limit) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_rx_queue *rxq = priv->rxq + index; int refilled; refilled = 0; while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) { struct sk_buff *skb; dma_addr_t addr; int rx; void *rxd; skb = dev_alloc_skb(MWL8K_RX_MAXSZ); if (skb == NULL) break; addr = pci_map_single(priv->pdev, skb->data, MWL8K_RX_MAXSZ, DMA_FROM_DEVICE); rxq->rxd_count++; rx = rxq->tail++; if (rxq->tail == MWL8K_RX_DESCS) rxq->tail = 0; rxq->buf[rx].skb = skb; dma_unmap_addr_set(&rxq->buf[rx], dma, addr); rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size); priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ); refilled++; } return refilled; } /* Must be called only when the card's reception is completely halted */ static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_rx_queue *rxq = priv->rxq + index; int i; if (rxq->rxd == NULL) return; for (i = 0; i < MWL8K_RX_DESCS; i++) { if (rxq->buf[i].skb != NULL) { pci_unmap_single(priv->pdev, dma_unmap_addr(&rxq->buf[i], dma), MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); dma_unmap_addr_set(&rxq->buf[i], dma, 0); kfree_skb(rxq->buf[i].skb); rxq->buf[i].skb = NULL; } } kfree(rxq->buf); rxq->buf = NULL; pci_free_consistent(priv->pdev, MWL8K_RX_DESCS * priv->rxd_ops->rxd_size, rxq->rxd, rxq->rxd_dma); rxq->rxd = NULL; } /* * Scan a list of BSSIDs to process for finalize join. * Allows for extension to process multiple BSSIDs. */ static inline int mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh) { return priv->capture_beacon && ieee80211_is_beacon(wh->frame_control) && ether_addr_equal(wh->addr3, priv->capture_bssid); } static inline void mwl8k_save_beacon(struct ieee80211_hw *hw, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; priv->capture_beacon = false; memset(priv->capture_bssid, 0, ETH_ALEN); /* * Use GFP_ATOMIC as rxq_process is called from * the primary interrupt handler, memory allocation call * must not sleep. */ priv->beacon_skb = skb_copy(skb, GFP_ATOMIC); if (priv->beacon_skb != NULL) ieee80211_queue_work(hw, &priv->finalize_join_worker); } static inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list, u8 *bssid) { struct mwl8k_vif *mwl8k_vif; list_for_each_entry(mwl8k_vif, vif_list, list) { if (memcmp(bssid, mwl8k_vif->bssid, ETH_ALEN) == 0) return mwl8k_vif; } return NULL; } static int rxq_process(struct ieee80211_hw *hw, int index, int limit) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif = NULL; struct mwl8k_rx_queue *rxq = priv->rxq + index; int processed; processed = 0; while (rxq->rxd_count && limit--) { struct sk_buff *skb; void *rxd; int pkt_len; struct ieee80211_rx_status status; struct ieee80211_hdr *wh; __le16 qos; skb = rxq->buf[rxq->head].skb; if (skb == NULL) break; rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size); pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos, &priv->noise); if (pkt_len < 0) break; rxq->buf[rxq->head].skb = NULL; pci_unmap_single(priv->pdev, dma_unmap_addr(&rxq->buf[rxq->head], dma), MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0); rxq->head++; if (rxq->head == MWL8K_RX_DESCS) rxq->head = 0; rxq->rxd_count--; wh = &((struct mwl8k_dma_data *)skb->data)->wh; /* * Check for a pending join operation. Save a * copy of the beacon and schedule a tasklet to * send a FINALIZE_JOIN command to the firmware. */ if (mwl8k_capture_bssid(priv, (void *)skb->data)) mwl8k_save_beacon(hw, skb); if (ieee80211_has_protected(wh->frame_control)) { /* Check if hw crypto has been enabled for * this bss. If yes, set the status flags * accordingly */ mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list, wh->addr1); if (mwl8k_vif != NULL && mwl8k_vif->is_hw_crypto_enabled) { /* * When MMIC ERROR is encountered * by the firmware, payload is * dropped and only 32 bytes of * mwl8k Firmware header is sent * to the host. * * We need to add four bytes of * key information. In it * MAC80211 expects keyidx set to * 0 for triggering Counter * Measure of MMIC failure. */ if (status.flag & RX_FLAG_MMIC_ERROR) { struct mwl8k_dma_data *tr; tr = (struct mwl8k_dma_data *)skb->data; memset((void *)&(tr->data), 0, 4); pkt_len += 4; } if (!ieee80211_is_auth(wh->frame_control)) status.flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; } } skb_put(skb, pkt_len); mwl8k_remove_dma_header(skb, qos); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); ieee80211_rx_irqsafe(hw, skb); processed++; } return processed; } /* * Packet transmission. */ #define MWL8K_TXD_STATUS_OK 0x00000001 #define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 #define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 #define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008 #define MWL8K_TXD_STATUS_FW_OWNED 0x80000000 #define MWL8K_QOS_QLEN_UNSPEC 0xff00 #define MWL8K_QOS_ACK_POLICY_MASK 0x0060 #define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000 #define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060 #define MWL8K_QOS_EOSP 0x0010 struct mwl8k_tx_desc { __le32 status; __u8 data_rate; __u8 tx_priority; __le16 qos_control; __le32 pkt_phys_addr; __le16 pkt_len; __u8 dest_MAC_addr[ETH_ALEN]; __le32 next_txd_phys_addr; __le32 timestamp; __le16 rate_info; __u8 peer_id; __u8 tx_frag_cnt; } __packed; #define MWL8K_TX_DESCS 128 static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; int size; int i; txq->len = 0; txq->head = 0; txq->tail = 0; size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc); txq->txd = pci_alloc_consistent(priv->pdev, size, &txq->txd_dma); if (txq->txd == NULL) { wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n"); return -ENOMEM; } memset(txq->txd, 0, size); txq->skb = kcalloc(MWL8K_TX_DESCS, sizeof(*txq->skb), GFP_KERNEL); if (txq->skb == NULL) { wiphy_err(hw->wiphy, "failed to alloc TX skbuff list\n"); pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma); return -ENOMEM; } for (i = 0; i < MWL8K_TX_DESCS; i++) { struct mwl8k_tx_desc *tx_desc; int nexti; tx_desc = txq->txd + i; nexti = (i + 1) % MWL8K_TX_DESCS; tx_desc->status = 0; tx_desc->next_txd_phys_addr = cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc)); } return 0; } static inline void mwl8k_tx_start(struct mwl8k_priv *priv) { iowrite32(MWL8K_H2A_INT_PPA_READY, priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); iowrite32(MWL8K_H2A_INT_DUMMY, priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); ioread32(priv->regs + MWL8K_HIU_INT_CODE); } static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; int i; for (i = 0; i < mwl8k_tx_queues(priv); i++) { struct mwl8k_tx_queue *txq = priv->txq + i; int fw_owned = 0; int drv_owned = 0; int unused = 0; int desc; for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { struct mwl8k_tx_desc *tx_desc = txq->txd + desc; u32 status; status = le32_to_cpu(tx_desc->status); if (status & MWL8K_TXD_STATUS_FW_OWNED) fw_owned++; else drv_owned++; if (tx_desc->pkt_len == 0) unused++; } wiphy_err(hw->wiphy, "txq[%d] len=%d head=%d tail=%d " "fw_owned=%d drv_owned=%d unused=%d\n", i, txq->len, txq->head, txq->tail, fw_owned, drv_owned, unused); } } /* * Must be called with priv->fw_mutex held and tx queues stopped. */ #define MWL8K_TX_WAIT_TIMEOUT_MS 5000 static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; DECLARE_COMPLETION_ONSTACK(tx_wait); int retry; int rc; might_sleep(); /* Since fw restart is in progress, allow only the firmware * commands from the restart code and block the other * commands since they are going to fail in any case since * the firmware has crashed */ if (priv->hw_restart_in_progress) { if (priv->hw_restart_owner == current) return 0; else return -EBUSY; } /* * The TX queues are stopped at this point, so this test * doesn't need to take ->tx_lock. */ if (!priv->pending_tx_pkts) return 0; retry = 0; rc = 0; spin_lock_bh(&priv->tx_lock); priv->tx_wait = &tx_wait; while (!rc) { int oldcount; unsigned long timeout; oldcount = priv->pending_tx_pkts; spin_unlock_bh(&priv->tx_lock); timeout = wait_for_completion_timeout(&tx_wait, msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); spin_lock_bh(&priv->tx_lock); if (timeout) { WARN_ON(priv->pending_tx_pkts); if (retry) wiphy_notice(hw->wiphy, "tx rings drained\n"); break; } if (priv->pending_tx_pkts < oldcount) { wiphy_notice(hw->wiphy, "waiting for tx rings to drain (%d -> %d pkts)\n", oldcount, priv->pending_tx_pkts); retry = 1; continue; } priv->tx_wait = NULL; wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n", MWL8K_TX_WAIT_TIMEOUT_MS); mwl8k_dump_tx_rings(hw); priv->hw_restart_in_progress = true; ieee80211_queue_work(hw, &priv->fw_reload); rc = -ETIMEDOUT; } spin_unlock_bh(&priv->tx_lock); return rc; } #define MWL8K_TXD_SUCCESS(status) \ ((status) & (MWL8K_TXD_STATUS_OK | \ MWL8K_TXD_STATUS_OK_RETRY | \ MWL8K_TXD_STATUS_OK_MORE_RETRY)) static int mwl8k_tid_queue_mapping(u8 tid) { BUG_ON(tid > 7); switch (tid) { case 0: case 3: return IEEE80211_AC_BE; break; case 1: case 2: return IEEE80211_AC_BK; break; case 4: case 5: return IEEE80211_AC_VI; break; case 6: case 7: return IEEE80211_AC_VO; break; default: return -1; break; } } /* The firmware will fill in the rate information * for each packet that gets queued in the hardware * and these macros will interpret that info. */ #define RI_FORMAT(a) (a & 0x0001) #define RI_RATE_ID_MCS(a) ((a & 0x01f8) >> 3) static int mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; int processed; processed = 0; while (txq->len > 0 && limit--) { int tx; struct mwl8k_tx_desc *tx_desc; unsigned long addr; int size; struct sk_buff *skb; struct ieee80211_tx_info *info; u32 status; struct ieee80211_sta *sta; struct mwl8k_sta *sta_info = NULL; u16 rate_info; struct ieee80211_hdr *wh; tx = txq->head; tx_desc = txq->txd + tx; status = le32_to_cpu(tx_desc->status); if (status & MWL8K_TXD_STATUS_FW_OWNED) { if (!force) break; tx_desc->status &= ~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED); } txq->head = (tx + 1) % MWL8K_TX_DESCS; BUG_ON(txq->len == 0); txq->len--; priv->pending_tx_pkts--; addr = le32_to_cpu(tx_desc->pkt_phys_addr); size = le16_to_cpu(tx_desc->pkt_len); skb = txq->skb[tx]; txq->skb[tx] = NULL; BUG_ON(skb == NULL); pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE); mwl8k_remove_dma_header(skb, tx_desc->qos_control); wh = (struct ieee80211_hdr *) skb->data; /* Mark descriptor as unused */ tx_desc->pkt_phys_addr = 0; tx_desc->pkt_len = 0; info = IEEE80211_SKB_CB(skb); if (ieee80211_is_data(wh->frame_control)) { rcu_read_lock(); sta = ieee80211_find_sta_by_ifaddr(hw, wh->addr1, wh->addr2); if (sta) { sta_info = MWL8K_STA(sta); BUG_ON(sta_info == NULL); rate_info = le16_to_cpu(tx_desc->rate_info); /* If rate is < 6.5 Mpbs for an ht station * do not form an ampdu. If the station is a * legacy station (format = 0), do not form an * ampdu */ if (RI_RATE_ID_MCS(rate_info) < 1 || RI_FORMAT(rate_info) == 0) { sta_info->is_ampdu_allowed = false; } else { sta_info->is_ampdu_allowed = true; } } rcu_read_unlock(); } ieee80211_tx_info_clear_status(info); /* Rate control is happening in the firmware. * Ensure no tx rate is being reported. */ info->status.rates[0].idx = -1; info->status.rates[0].count = 1; if (MWL8K_TXD_SUCCESS(status)) info->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(hw, skb); processed++; } return processed; } /* must be called only when the card's transmit is completely halted */ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; if (txq->txd == NULL) return; mwl8k_txq_reclaim(hw, index, INT_MAX, 1); kfree(txq->skb); txq->skb = NULL; pci_free_consistent(priv->pdev, MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc), txq->txd, txq->txd_dma); txq->txd = NULL; } /* caller must hold priv->stream_lock when calling the stream functions */ static struct mwl8k_ampdu_stream * mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid) { struct mwl8k_ampdu_stream *stream; struct mwl8k_priv *priv = hw->priv; int i; for (i = 0; i < priv->num_ampdu_queues; i++) { stream = &priv->ampdu[i]; if (stream->state == AMPDU_NO_STREAM) { stream->sta = sta; stream->state = AMPDU_STREAM_NEW; stream->tid = tid; stream->idx = i; stream->txq_idx = MWL8K_TX_WMM_QUEUES + i; wiphy_debug(hw->wiphy, "Added a new stream for %pM %d", sta->addr, tid); return stream; } } return NULL; } static int mwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) { int ret; /* if the stream has already been started, don't start it again */ if (stream->state != AMPDU_STREAM_NEW) return 0; ret = ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0); if (ret) wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: " "%d\n", stream->sta->addr, stream->tid, ret); else wiphy_debug(hw->wiphy, "Started stream for %pM %d\n", stream->sta->addr, stream->tid); return ret; } static void mwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) { wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n", stream->sta->addr, stream->tid); memset(stream, 0, sizeof(*stream)); } static struct mwl8k_ampdu_stream * mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid) { struct mwl8k_priv *priv = hw->priv; int i; for (i = 0 ; i < priv->num_ampdu_queues; i++) { struct mwl8k_ampdu_stream *stream; stream = &priv->ampdu[i]; if (stream->state == AMPDU_NO_STREAM) continue; if (!memcmp(stream->sta->addr, addr, ETH_ALEN) && stream->tid == tid) return stream; } return NULL; } #define MWL8K_AMPDU_PACKET_THRESHOLD 64 static inline bool mwl8k_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) { struct mwl8k_sta *sta_info = MWL8K_STA(sta); struct tx_traffic_info *tx_stats; BUG_ON(tid >= MWL8K_MAX_TID); tx_stats = &sta_info->tx_stats[tid]; return sta_info->is_ampdu_allowed && tx_stats->pkts > MWL8K_AMPDU_PACKET_THRESHOLD; } static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid) { struct mwl8k_sta *sta_info = MWL8K_STA(sta); struct tx_traffic_info *tx_stats; BUG_ON(tid >= MWL8K_MAX_TID); tx_stats = &sta_info->tx_stats[tid]; if (tx_stats->start_time == 0) tx_stats->start_time = jiffies; /* reset the packet count after each second elapses. If the number of * packets ever exceeds the ampdu_min_traffic threshold, we will allow * an ampdu stream to be started. */ if (jiffies - tx_stats->start_time > HZ) { tx_stats->pkts = 0; tx_stats->start_time = 0; } else tx_stats->pkts++; } static void mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct ieee80211_sta *sta, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; struct ieee80211_tx_info *tx_info; struct mwl8k_vif *mwl8k_vif; struct ieee80211_hdr *wh; struct mwl8k_tx_queue *txq; struct mwl8k_tx_desc *tx; dma_addr_t dma; u32 txstatus; u8 txdatarate; u16 qos; int txpriority; u8 tid = 0; struct mwl8k_ampdu_stream *stream = NULL; bool start_ba_session = false; bool mgmtframe = false; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; wh = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_data_qos(wh->frame_control)) qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); else qos = 0; if (ieee80211_is_mgmt(wh->frame_control)) mgmtframe = true; if (priv->ap_fw) mwl8k_encapsulate_tx_frame(priv, skb); else mwl8k_add_dma_header(priv, skb, 0, 0); wh = &((struct mwl8k_dma_data *)skb->data)->wh; tx_info = IEEE80211_SKB_CB(skb); mwl8k_vif = MWL8K_VIF(tx_info->control.vif); if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); mwl8k_vif->seqno += 0x10; } /* Setup firmware control bit fields for each frame type. */ txstatus = 0; txdatarate = 0; if (ieee80211_is_mgmt(wh->frame_control) || ieee80211_is_ctl(wh->frame_control)) { txdatarate = 0; qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP; } else if (ieee80211_is_data(wh->frame_control)) { txdatarate = 1; if (is_multicast_ether_addr(wh->addr1)) txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; qos &= ~MWL8K_QOS_ACK_POLICY_MASK; if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK; else qos |= MWL8K_QOS_ACK_POLICY_NORMAL; } /* Queue ADDBA request in the respective data queue. While setting up * the ampdu stream, mac80211 queues further packets for that * particular ra/tid pair. However, packets piled up in the hardware * for that ra/tid pair will still go out. ADDBA request and the * related data packets going out from different queues asynchronously * will cause a shift in the receiver window which might result in * ampdu packets getting dropped at the receiver after the stream has * been setup. */ if (unlikely(ieee80211_is_action(wh->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK && mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ && priv->ap_fw)) { u16 capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; index = mwl8k_tid_queue_mapping(tid); } txpriority = index; if (priv->ap_fw && sta && sta->ht_cap.ht_supported && skb->protocol != cpu_to_be16(ETH_P_PAE) && ieee80211_is_data_qos(wh->frame_control)) { tid = qos & 0xf; mwl8k_tx_count_packet(sta, tid); spin_lock(&priv->stream_lock); stream = mwl8k_lookup_stream(hw, sta->addr, tid); if (stream != NULL) { if (stream->state == AMPDU_STREAM_ACTIVE) { txpriority = stream->txq_idx; index = stream->txq_idx; } else if (stream->state == AMPDU_STREAM_NEW) { /* We get here if the driver sends us packets * after we've initiated a stream, but before * our ampdu_action routine has been called * with IEEE80211_AMPDU_TX_START to get the SSN * for the ADDBA request. So this packet can * go out with no risk of sequence number * mismatch. No special handling is required. */ } else { /* Drop packets that would go out after the * ADDBA request was sent but before the ADDBA * response is received. If we don't do this, * the recipient would probably receive it * after the ADDBA request with SSN 0. This * will cause the recipient's BA receive window * to shift, which would cause the subsequent * packets in the BA stream to be discarded. * mac80211 queues our packets for us in this * case, so this is really just a safety check. */ wiphy_warn(hw->wiphy, "Cannot send packet while ADDBA " "dialog is underway.\n"); spin_unlock(&priv->stream_lock); dev_kfree_skb(skb); return; } } else { /* Defer calling mwl8k_start_stream so that the current * skb can go out before the ADDBA request. This * prevents sequence number mismatch at the recepient * as described above. */ if (mwl8k_ampdu_allowed(sta, tid)) { stream = mwl8k_add_stream(hw, sta, tid); if (stream != NULL) start_ba_session = true; } } spin_unlock(&priv->stream_lock); } dma = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(priv->pdev, dma)) { wiphy_debug(hw->wiphy, "failed to dma map skb, dropping TX frame.\n"); if (start_ba_session) { spin_lock(&priv->stream_lock); mwl8k_remove_stream(hw, stream); spin_unlock(&priv->stream_lock); } dev_kfree_skb(skb); return; } spin_lock_bh(&priv->tx_lock); txq = priv->txq + index; /* Mgmt frames that go out frequently are probe * responses. Other mgmt frames got out relatively * infrequently. Hence reserve 2 buffers so that * other mgmt frames do not get dropped due to an * already queued probe response in one of the * reserved buffers. */ if (txq->len >= MWL8K_TX_DESCS - 2) { if (!mgmtframe || txq->len == MWL8K_TX_DESCS) { if (start_ba_session) { spin_lock(&priv->stream_lock); mwl8k_remove_stream(hw, stream); spin_unlock(&priv->stream_lock); } spin_unlock_bh(&priv->tx_lock); dev_kfree_skb(skb); return; } } BUG_ON(txq->skb[txq->tail] != NULL); txq->skb[txq->tail] = skb; tx = txq->txd + txq->tail; tx->data_rate = txdatarate; tx->tx_priority = txpriority; tx->qos_control = cpu_to_le16(qos); tx->pkt_phys_addr = cpu_to_le32(dma); tx->pkt_len = cpu_to_le16(skb->len); tx->rate_info = 0; if (!priv->ap_fw && sta != NULL) tx->peer_id = MWL8K_STA(sta)->peer_id; else tx->peer_id = 0; if (priv->ap_fw) tx->timestamp = cpu_to_le32(ioread32(priv->regs + MWL8K_HW_TIMER_REGISTER)); wmb(); tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); txq->len++; priv->pending_tx_pkts++; txq->tail++; if (txq->tail == MWL8K_TX_DESCS) txq->tail = 0; mwl8k_tx_start(priv); spin_unlock_bh(&priv->tx_lock); /* Initiate the ampdu session here */ if (start_ba_session) { spin_lock(&priv->stream_lock); if (mwl8k_start_stream(hw, stream)) mwl8k_remove_stream(hw, stream); spin_unlock(&priv->stream_lock); } } /* * Firmware access. * * We have the following requirements for issuing firmware commands: * - Some commands require that the packet transmit path is idle when * the command is issued. (For simplicity, we'll just quiesce the * transmit path for every command.) * - There are certain sequences of commands that need to be issued to * the hardware sequentially, with no other intervening commands. * * This leads to an implementation of a "firmware lock" as a mutex that * can be taken recursively, and which is taken by both the low-level * command submission function (mwl8k_post_cmd) as well as any users of * that function that require issuing of an atomic sequence of commands, * and quiesces the transmit path whenever it's taken. */ static int mwl8k_fw_lock(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; if (priv->fw_mutex_owner != current) { int rc; mutex_lock(&priv->fw_mutex); ieee80211_stop_queues(hw); rc = mwl8k_tx_wait_empty(hw); if (rc) { if (!priv->hw_restart_in_progress) ieee80211_wake_queues(hw); mutex_unlock(&priv->fw_mutex); return rc; } priv->fw_mutex_owner = current; } priv->fw_mutex_depth++; return 0; } static void mwl8k_fw_unlock(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; if (!--priv->fw_mutex_depth) { if (!priv->hw_restart_in_progress) ieee80211_wake_queues(hw); priv->fw_mutex_owner = NULL; mutex_unlock(&priv->fw_mutex); } } /* * Command processing. */ /* Timeout firmware commands after 10s */ #define MWL8K_CMD_TIMEOUT_MS 10000 static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) { DECLARE_COMPLETION_ONSTACK(cmd_wait); struct mwl8k_priv *priv = hw->priv; void __iomem *regs = priv->regs; dma_addr_t dma_addr; unsigned int dma_size; int rc; unsigned long timeout = 0; u8 buf[32]; cmd->result = (__force __le16) 0xffff; dma_size = le16_to_cpu(cmd->length); dma_addr = pci_map_single(priv->pdev, cmd, dma_size, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(priv->pdev, dma_addr)) return -ENOMEM; rc = mwl8k_fw_lock(hw); if (rc) { pci_unmap_single(priv->pdev, dma_addr, dma_size, PCI_DMA_BIDIRECTIONAL); return rc; } priv->hostcmd_wait = &cmd_wait; iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); iowrite32(MWL8K_H2A_INT_DOORBELL, regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); iowrite32(MWL8K_H2A_INT_DUMMY, regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); timeout = wait_for_completion_timeout(&cmd_wait, msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); priv->hostcmd_wait = NULL; mwl8k_fw_unlock(hw); pci_unmap_single(priv->pdev, dma_addr, dma_size, PCI_DMA_BIDIRECTIONAL); if (!timeout) { wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n", mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), MWL8K_CMD_TIMEOUT_MS); rc = -ETIMEDOUT; } else { int ms; ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout); rc = cmd->result ? -EINVAL : 0; if (rc) wiphy_err(hw->wiphy, "Command %s error 0x%x\n", mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), le16_to_cpu(cmd->result)); else if (ms > 2000) wiphy_notice(hw->wiphy, "Command %s took %d ms\n", mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), ms); } return rc; } static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mwl8k_cmd_pkt *cmd) { if (vif != NULL) cmd->macid = MWL8K_VIF(vif)->macid; return mwl8k_post_cmd(hw, cmd); } /* * Setup code shared between STA and AP firmware images. */ static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24)); memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); priv->band_24.band = IEEE80211_BAND_2GHZ; priv->band_24.channels = priv->channels_24; priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); priv->band_24.bitrates = priv->rates_24; priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; } static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50)); memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50)); BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50)); memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50)); priv->band_50.band = IEEE80211_BAND_5GHZ; priv->band_50.channels = priv->channels_50; priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50); priv->band_50.bitrates = priv->rates_50; priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50); hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50; } /* * CMD_GET_HW_SPEC (STA version). */ struct mwl8k_cmd_get_hw_spec_sta { struct mwl8k_cmd_pkt header; __u8 hw_rev; __u8 host_interface; __le16 num_mcaddrs; __u8 perm_addr[ETH_ALEN]; __le16 region_code; __le32 fw_rev; __le32 ps_cookie; __le32 caps; __u8 mcs_bitmap[16]; __le32 rx_queue_ptr; __le32 num_tx_queues; __le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES]; __le32 caps2; __le32 num_tx_desc_per_queue; __le32 total_rxd; } __packed; #define MWL8K_CAP_MAX_AMSDU 0x20000000 #define MWL8K_CAP_GREENFIELD 0x08000000 #define MWL8K_CAP_AMPDU 0x04000000 #define MWL8K_CAP_RX_STBC 0x01000000 #define MWL8K_CAP_TX_STBC 0x00800000 #define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 #define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 #define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 #define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 #define MWL8K_CAP_DELAY_BA 0x00003000 #define MWL8K_CAP_MIMO 0x00000200 #define MWL8K_CAP_40MHZ 0x00000100 #define MWL8K_CAP_BAND_MASK 0x00000007 #define MWL8K_CAP_5GHZ 0x00000004 #define MWL8K_CAP_2GHZ4 0x00000001 static void mwl8k_set_ht_caps(struct ieee80211_hw *hw, struct ieee80211_supported_band *band, u32 cap) { int rx_streams; int tx_streams; band->ht_cap.ht_supported = 1; if (cap & MWL8K_CAP_MAX_AMSDU) band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; if (cap & MWL8K_CAP_GREENFIELD) band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; if (cap & MWL8K_CAP_AMPDU) { hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; } if (cap & MWL8K_CAP_RX_STBC) band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; if (cap & MWL8K_CAP_TX_STBC) band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; if (cap & MWL8K_CAP_SHORTGI_40MHZ) band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; if (cap & MWL8K_CAP_SHORTGI_20MHZ) band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; if (cap & MWL8K_CAP_DELAY_BA) band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; if (cap & MWL8K_CAP_40MHZ) band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); band->ht_cap.mcs.rx_mask[0] = 0xff; if (rx_streams >= 2) band->ht_cap.mcs.rx_mask[1] = 0xff; if (rx_streams >= 3) band->ht_cap.mcs.rx_mask[2] = 0xff; band->ht_cap.mcs.rx_mask[4] = 0x01; band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; if (rx_streams != tx_streams) { band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; band->ht_cap.mcs.tx_params |= (tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; } } static void mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps) { struct mwl8k_priv *priv = hw->priv; if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) { mwl8k_setup_2ghz_band(hw); if (caps & MWL8K_CAP_MIMO) mwl8k_set_ht_caps(hw, &priv->band_24, caps); } if (caps & MWL8K_CAP_5GHZ) { mwl8k_setup_5ghz_band(hw); if (caps & MWL8K_CAP_MIMO) mwl8k_set_ht_caps(hw, &priv->band_50, caps); } } static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_get_hw_spec_sta *cmd; int rc; int i; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); cmd->header.length = cpu_to_le16(sizeof(*cmd)); memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); for (i = 0; i < mwl8k_tx_queues(priv); i++) cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); rc = mwl8k_post_cmd(hw, &cmd->header); if (!rc) { SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); priv->ap_macids_supported = 0x00000000; priv->sta_macids_supported = 0x00000001; } kfree(cmd); return rc; } /* * CMD_GET_HW_SPEC (AP version). */ struct mwl8k_cmd_get_hw_spec_ap { struct mwl8k_cmd_pkt header; __u8 hw_rev; __u8 host_interface; __le16 num_wcb; __le16 num_mcaddrs; __u8 perm_addr[ETH_ALEN]; __le16 region_code; __le16 num_antenna; __le32 fw_rev; __le32 wcbbase0; __le32 rxwrptr; __le32 rxrdptr; __le32 ps_cookie; __le32 wcbbase1; __le32 wcbbase2; __le32 wcbbase3; __le32 fw_api_version; __le32 caps; __le32 num_of_ampdu_queues; __le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES]; } __packed; static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_get_hw_spec_ap *cmd; int rc, i; u32 api_version; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); cmd->header.length = cpu_to_le16(sizeof(*cmd)); memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); rc = mwl8k_post_cmd(hw, &cmd->header); if (!rc) { int off; api_version = le32_to_cpu(cmd->fw_api_version); if (priv->device_info->fw_api_ap != api_version) { printk(KERN_ERR "%s: Unsupported fw API version for %s." " Expected %d got %d.\n", MWL8K_NAME, priv->device_info->part_name, priv->device_info->fw_api_ap, api_version); rc = -EINVAL; goto done; } SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); priv->ap_macids_supported = 0x000000ff; priv->sta_macids_supported = 0x00000000; priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues); if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) { wiphy_warn(hw->wiphy, "fw reported %d ampdu queues" " but we only support %d.\n", priv->num_ampdu_queues, MWL8K_MAX_AMPDU_QUEUES); priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES; } off = le32_to_cpu(cmd->rxwrptr) & 0xffff; iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); off = le32_to_cpu(cmd->rxrdptr) & 0xffff; iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff; priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff; priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff; priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff; for (i = 0; i < priv->num_ampdu_queues; i++) priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] = le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff; } done: kfree(cmd); return rc; } /* * CMD_SET_HW_SPEC. */ struct mwl8k_cmd_set_hw_spec { struct mwl8k_cmd_pkt header; __u8 hw_rev; __u8 host_interface; __le16 num_mcaddrs; __u8 perm_addr[ETH_ALEN]; __le16 region_code; __le32 fw_rev; __le32 ps_cookie; __le32 caps; __le32 rx_queue_ptr; __le32 num_tx_queues; __le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES]; __le32 flags; __le32 num_tx_desc_per_queue; __le32 total_rxd; } __packed; /* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause * packets to expire 500 ms after the timestamp in the tx descriptor. That is, * the packets that are queued for more than 500ms, will be dropped in the * hardware. This helps minimizing the issues caused due to head-of-line * blocking where a slow client can hog the bandwidth and affect traffic to a * faster client. */ #define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY 0x00000400 #define MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR 0x00000200 #define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_set_hw_spec *cmd; int rc; int i; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); /* * Mac80211 stack has Q0 as highest priority and Q3 as lowest in * that order. Firmware has Q3 as highest priority and Q0 as lowest * in that order. Map Q3 of mac80211 to Q0 of firmware so that the * priority is interpreted the right way in firmware. */ for (i = 0; i < mwl8k_tx_queues(priv); i++) { int j = mwl8k_tx_queues(priv) - 1 - i; cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma); } cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON | MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY | MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR); cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_MAC_MULTICAST_ADR. */ struct mwl8k_cmd_mac_multicast_adr { struct mwl8k_cmd_pkt header; __le16 action; __le16 numaddr; __u8 addr[0][ETH_ALEN]; }; #define MWL8K_ENABLE_RX_DIRECTED 0x0001 #define MWL8K_ENABLE_RX_MULTICAST 0x0002 #define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004 #define MWL8K_ENABLE_RX_BROADCAST 0x0008 static struct mwl8k_cmd_pkt * __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *ha) #endif { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_mac_multicast_adr *cmd; int size; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) int mc_count = 0; if (mc_list) mc_count = netdev_hw_addr_list_count(mc_list); #endif if (allmulti || mc_count > priv->num_mcaddrs) { allmulti = 1; mc_count = 0; } size = sizeof(*cmd) + mc_count * ETH_ALEN; cmd = kzalloc(size, GFP_ATOMIC); if (cmd == NULL) return NULL; cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR); cmd->header.length = cpu_to_le16(size); cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED | MWL8K_ENABLE_RX_BROADCAST); if (allmulti) { cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST); } else if (mc_count) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; int i = 0; #else int i; #endif cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); cmd->numaddr = cpu_to_le16(mc_count); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { memcpy(cmd->addr[i], ha->addr, ETH_ALEN); #else for (i = 0; i < mc_count && ha; i++) { if (ha->da_addrlen != ETH_ALEN) { kfree(cmd); return NULL; } memcpy(cmd->addr[i], ha->da_addr, ETH_ALEN); ha = ha->next; #endif } } return &cmd->header; } /* * CMD_GET_STAT. */ struct mwl8k_cmd_get_stat { struct mwl8k_cmd_pkt header; __le32 stats[64]; } __packed; #define MWL8K_STAT_ACK_FAILURE 9 #define MWL8K_STAT_RTS_FAILURE 12 #define MWL8K_STAT_FCS_ERROR 24 #define MWL8K_STAT_RTS_SUCCESS 11 static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct mwl8k_cmd_get_stat *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT); cmd->header.length = cpu_to_le16(sizeof(*cmd)); rc = mwl8k_post_cmd(hw, &cmd->header); if (!rc) { stats->dot11ACKFailureCount = le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]); stats->dot11RTSFailureCount = le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]); stats->dot11FCSErrorCount = le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]); stats->dot11RTSSuccessCount = le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]); } kfree(cmd); return rc; } /* * CMD_RADIO_CONTROL. */ struct mwl8k_cmd_radio_control { struct mwl8k_cmd_pkt header; __le16 action; __le16 control; __le16 radio_on; } __packed; static int mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_radio_control *cmd; int rc; if (enable == priv->radio_on && !force) return 0; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1); cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); if (!rc) priv->radio_on = enable; return rc; } static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw) { return mwl8k_cmd_radio_control(hw, 0, 0); } static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw) { return mwl8k_cmd_radio_control(hw, 1, 0); } static int mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) { struct mwl8k_priv *priv = hw->priv; priv->radio_short_preamble = short_preamble; return mwl8k_cmd_radio_control(hw, 1, 1); } /* * CMD_RF_TX_POWER. */ #define MWL8K_RF_TX_POWER_LEVEL_TOTAL 8 struct mwl8k_cmd_rf_tx_power { struct mwl8k_cmd_pkt header; __le16 action; __le16 support_level; __le16 current_level; __le16 reserved; __le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL]; } __packed; static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm) { struct mwl8k_cmd_rf_tx_power *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->support_level = cpu_to_le16(dBm); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_TX_POWER. */ #define MWL8K_TX_POWER_LEVEL_TOTAL 12 struct mwl8k_cmd_tx_power { struct mwl8k_cmd_pkt header; __le16 action; __le16 band; __le16 channel; __le16 bw; __le16 sub_ch; __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL]; } __packed; static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw, struct ieee80211_conf *conf, unsigned short pwr) { struct ieee80211_channel *channel = conf->channel; struct mwl8k_cmd_tx_power *cmd; int rc; int i; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST); if (channel->band == IEEE80211_BAND_2GHZ) cmd->band = cpu_to_le16(0x1); else if (channel->band == IEEE80211_BAND_5GHZ) cmd->band = cpu_to_le16(0x4); cmd->channel = cpu_to_le16(channel->hw_value); if (conf->channel_type == NL80211_CHAN_NO_HT || conf->channel_type == NL80211_CHAN_HT20) { cmd->bw = cpu_to_le16(0x2); } else { cmd->bw = cpu_to_le16(0x4); if (conf->channel_type == NL80211_CHAN_HT40MINUS) cmd->sub_ch = cpu_to_le16(0x3); else if (conf->channel_type == NL80211_CHAN_HT40PLUS) cmd->sub_ch = cpu_to_le16(0x1); } for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++) cmd->power_level_list[i] = cpu_to_le16(pwr); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_RF_ANTENNA. */ struct mwl8k_cmd_rf_antenna { struct mwl8k_cmd_pkt header; __le16 antenna; __le16 mode; } __packed; #define MWL8K_RF_ANTENNA_RX 1 #define MWL8K_RF_ANTENNA_TX 2 static int mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) { struct mwl8k_cmd_rf_antenna *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->antenna = cpu_to_le16(antenna); cmd->mode = cpu_to_le16(mask); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_BEACON. */ struct mwl8k_cmd_set_beacon { struct mwl8k_cmd_pkt header; __le16 beacon_len; __u8 beacon[0]; }; static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *beacon, int len) { struct mwl8k_cmd_set_beacon *cmd; int rc; cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); cmd->beacon_len = cpu_to_le16(len); memcpy(cmd->beacon, beacon, len); rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_PRE_SCAN. */ struct mwl8k_cmd_set_pre_scan { struct mwl8k_cmd_pkt header; } __packed; static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw) { struct mwl8k_cmd_set_pre_scan *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_POST_SCAN. */ struct mwl8k_cmd_set_post_scan { struct mwl8k_cmd_pkt header; __le32 isibss; __u8 bssid[ETH_ALEN]; } __packed; static int mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) { struct mwl8k_cmd_set_post_scan *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->isibss = 0; memcpy(cmd->bssid, mac, ETH_ALEN); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_RF_CHANNEL. */ struct mwl8k_cmd_set_rf_channel { struct mwl8k_cmd_pkt header; __le16 action; __u8 current_channel; __le32 channel_flags; } __packed; static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, struct ieee80211_conf *conf) { struct ieee80211_channel *channel = conf->channel; struct mwl8k_cmd_set_rf_channel *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->current_channel = channel->hw_value; if (channel->band == IEEE80211_BAND_2GHZ) cmd->channel_flags |= cpu_to_le32(0x00000001); else if (channel->band == IEEE80211_BAND_5GHZ) cmd->channel_flags |= cpu_to_le32(0x00000004); if (conf->channel_type == NL80211_CHAN_NO_HT || conf->channel_type == NL80211_CHAN_HT20) cmd->channel_flags |= cpu_to_le32(0x00000080); else if (conf->channel_type == NL80211_CHAN_HT40MINUS) cmd->channel_flags |= cpu_to_le32(0x000001900); else if (conf->channel_type == NL80211_CHAN_HT40PLUS) cmd->channel_flags |= cpu_to_le32(0x000000900); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_AID. */ #define MWL8K_FRAME_PROT_DISABLED 0x00 #define MWL8K_FRAME_PROT_11G 0x07 #define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 #define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 struct mwl8k_cmd_update_set_aid { struct mwl8k_cmd_pkt header; __le16 aid; /* AP's MAC address (BSSID) */ __u8 bssid[ETH_ALEN]; __le16 protection_mode; __u8 supp_rates[14]; } __packed; static void legacy_rate_mask_to_array(u8 *rates, u32 mask) { int i; int j; /* * Clear nonstandard rates 4 and 13. */ mask &= 0x1fef; for (i = 0, j = 0; i < 14; i++) { if (mask & (1 << i)) rates[j++] = mwl8k_rates_24[i].hw_value; } } static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 legacy_rate_mask) { struct mwl8k_cmd_update_set_aid *cmd; u16 prot_mode; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->aid = cpu_to_le16(vif->bss_conf.aid); memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); if (vif->bss_conf.use_cts_prot) { prot_mode = MWL8K_FRAME_PROT_11G; } else { switch (vif->bss_conf.ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) { case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; break; case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; break; default: prot_mode = MWL8K_FRAME_PROT_DISABLED; break; } } cmd->protection_mode = cpu_to_le16(prot_mode); legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_RATE. */ struct mwl8k_cmd_set_rate { struct mwl8k_cmd_pkt header; __u8 legacy_rates[14]; /* Bitmap for supported MCS codes. */ __u8 mcs_set[16]; __u8 reserved[16]; } __packed; static int mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 legacy_rate_mask, u8 *mcs_rates) { struct mwl8k_cmd_set_rate *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); memcpy(cmd->mcs_set, mcs_rates, 16); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_FINALIZE_JOIN. */ #define MWL8K_FJ_BEACON_MAXLEN 128 struct mwl8k_cmd_finalize_join { struct mwl8k_cmd_pkt header; __le32 sleep_interval; /* Number of beacon periods to sleep */ __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; } __packed; static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame, int framelen, int dtim) { struct mwl8k_cmd_finalize_join *cmd; struct ieee80211_mgmt *payload = frame; int payload_len; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); payload_len = framelen - ieee80211_hdrlen(payload->frame_control); if (payload_len < 0) payload_len = 0; else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) payload_len = MWL8K_FJ_BEACON_MAXLEN; memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_RTS_THRESHOLD. */ struct mwl8k_cmd_set_rts_threshold { struct mwl8k_cmd_pkt header; __le16 action; __le16 threshold; } __packed; static int mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) { struct mwl8k_cmd_set_rts_threshold *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->threshold = cpu_to_le16(rts_thresh); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_SLOT. */ struct mwl8k_cmd_set_slot { struct mwl8k_cmd_pkt header; __le16 action; __u8 short_slot; } __packed; static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) { struct mwl8k_cmd_set_slot *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->short_slot = short_slot_time; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_EDCA_PARAMS. */ struct mwl8k_cmd_set_edca_params { struct mwl8k_cmd_pkt header; /* See MWL8K_SET_EDCA_XXX below */ __le16 action; /* TX opportunity in units of 32 us */ __le16 txop; union { struct { /* Log exponent of max contention period: 0...15 */ __le32 log_cw_max; /* Log exponent of min contention period: 0...15 */ __le32 log_cw_min; /* Adaptive interframe spacing in units of 32us */ __u8 aifs; /* TX queue to configure */ __u8 txq; } ap; struct { /* Log exponent of max contention period: 0...15 */ __u8 log_cw_max; /* Log exponent of min contention period: 0...15 */ __u8 log_cw_min; /* Adaptive interframe spacing in units of 32us */ __u8 aifs; /* TX queue to configure */ __u8 txq; } sta; }; } __packed; #define MWL8K_SET_EDCA_CW 0x01 #define MWL8K_SET_EDCA_TXOP 0x02 #define MWL8K_SET_EDCA_AIFS 0x04 #define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \ MWL8K_SET_EDCA_TXOP | \ MWL8K_SET_EDCA_AIFS) static int mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, __u16 cw_min, __u16 cw_max, __u8 aifs, __u16 txop) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_set_edca_params *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); cmd->txop = cpu_to_le16(txop); if (priv->ap_fw) { cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1)); cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1)); cmd->ap.aifs = aifs; cmd->ap.txq = qnum; } else { cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1); cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1); cmd->sta.aifs = aifs; cmd->sta.txq = qnum; } rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_SET_WMM_MODE. */ struct mwl8k_cmd_set_wmm_mode { struct mwl8k_cmd_pkt header; __le16 action; } __packed; static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_set_wmm_mode *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(!!enable); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); if (!rc) priv->wmm_enabled = enable; return rc; } /* * CMD_MIMO_CONFIG. */ struct mwl8k_cmd_mimo_config { struct mwl8k_cmd_pkt header; __le32 action; __u8 rx_antenna_map; __u8 tx_antenna_map; } __packed; static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) { struct mwl8k_cmd_mimo_config *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); cmd->rx_antenna_map = rx; cmd->tx_antenna_map = tx; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_USE_FIXED_RATE (STA version). */ struct mwl8k_cmd_use_fixed_rate_sta { struct mwl8k_cmd_pkt header; __le32 action; __le32 allow_rate_drop; __le32 num_rates; struct { __le32 is_ht_rate; __le32 enable_retry; __le32 rate; __le32 retry_count; } rate_entry[8]; __le32 rate_type; __le32 reserved1; __le32 reserved2; } __packed; #define MWL8K_USE_AUTO_RATE 0x0002 #define MWL8K_UCAST_RATE 0 static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) { struct mwl8k_cmd_use_fixed_rate_sta *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_USE_FIXED_RATE (AP version). */ struct mwl8k_cmd_use_fixed_rate_ap { struct mwl8k_cmd_pkt header; __le32 action; __le32 allow_rate_drop; __le32 num_rates; struct mwl8k_rate_entry_ap { __le32 is_ht_rate; __le32 enable_retry; __le32 rate; __le32 retry_count; } rate_entry[4]; u8 multicast_rate; u8 multicast_rate_type; u8 management_rate; } __packed; static int mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) { struct mwl8k_cmd_use_fixed_rate_ap *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); cmd->multicast_rate = mcast; cmd->management_rate = mgmt; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_ENABLE_SNIFFER. */ struct mwl8k_cmd_enable_sniffer { struct mwl8k_cmd_pkt header; __le32 action; } __packed; static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable) { struct mwl8k_cmd_enable_sniffer *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(!!enable); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } struct mwl8k_cmd_update_mac_addr { struct mwl8k_cmd_pkt header; union { struct { __le16 mac_type; __u8 mac_addr[ETH_ALEN]; } mbss; __u8 mac_addr[ETH_ALEN]; }; } __packed; #define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 #define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1 #define MWL8K_MAC_TYPE_PRIMARY_AP 2 #define MWL8K_MAC_TYPE_SECONDARY_AP 3 static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *mac, bool set) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); struct mwl8k_cmd_update_mac_addr *cmd; int mac_type; int rc; mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; else mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported)) mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; else mac_type = MWL8K_MAC_TYPE_SECONDARY_AP; } cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; if (set) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); else cmd->header.code = cpu_to_le16(MWL8K_CMD_DEL_MAC_ADDR); cmd->header.length = cpu_to_le16(sizeof(*cmd)); if (priv->ap_fw) { cmd->mbss.mac_type = cpu_to_le16(mac_type); memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); } else { memcpy(cmd->mac_addr, mac, ETH_ALEN); } rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } /* * MWL8K_CMD_SET_MAC_ADDR. */ static inline int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *mac) { return mwl8k_cmd_update_mac_addr(hw, vif, mac, true); } /* * MWL8K_CMD_DEL_MAC_ADDR. */ static inline int mwl8k_cmd_del_mac_addr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *mac) { return mwl8k_cmd_update_mac_addr(hw, vif, mac, false); } /* * CMD_SET_RATEADAPT_MODE. */ struct mwl8k_cmd_set_rate_adapt_mode { struct mwl8k_cmd_pkt header; __le16 action; __le16 mode; } __packed; static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) { struct mwl8k_cmd_set_rate_adapt_mode *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->mode = cpu_to_le16(mode); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * CMD_GET_WATCHDOG_BITMAP. */ struct mwl8k_cmd_get_watchdog_bitmap { struct mwl8k_cmd_pkt header; u8 bitmap; } __packed; static int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) { struct mwl8k_cmd_get_watchdog_bitmap *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_WATCHDOG_BITMAP); cmd->header.length = cpu_to_le16(sizeof(*cmd)); rc = mwl8k_post_cmd(hw, &cmd->header); if (!rc) *bitmap = cmd->bitmap; kfree(cmd); return rc; } #define INVALID_BA 0xAA static void mwl8k_watchdog_ba_events(struct work_struct *work) { int rc; u8 bitmap = 0, stream_index; struct mwl8k_ampdu_stream *streams; struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, watchdog_ba_handle); rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap); if (rc) return; if (bitmap == INVALID_BA) return; /* the bitmap is the hw queue number. Map it to the ampdu queue. */ stream_index = bitmap - MWL8K_TX_WMM_QUEUES; BUG_ON(stream_index >= priv->num_ampdu_queues); streams = &priv->ampdu[stream_index]; if (streams->state == AMPDU_STREAM_ACTIVE) ieee80211_stop_tx_ba_session(streams->sta, streams->tid); return; } /* * CMD_BSS_START. */ struct mwl8k_cmd_bss_start { struct mwl8k_cmd_pkt header; __le32 enable; } __packed; static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int enable) { struct mwl8k_cmd_bss_start *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->enable = cpu_to_le32(enable); rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } /* * CMD_BASTREAM. */ /* * UPSTREAM is tx direction */ #define BASTREAM_FLAG_DIRECTION_UPSTREAM 0x00 #define BASTREAM_FLAG_IMMEDIATE_TYPE 0x01 enum ba_stream_action_type { MWL8K_BA_CREATE, MWL8K_BA_UPDATE, MWL8K_BA_DESTROY, MWL8K_BA_FLUSH, MWL8K_BA_CHECK, }; struct mwl8k_create_ba_stream { __le32 flags; __le32 idle_thrs; __le32 bar_thrs; __le32 window_size; u8 peer_mac_addr[6]; u8 dialog_token; u8 tid; u8 queue_id; u8 param_info; __le32 ba_context; u8 reset_seq_no_flag; __le16 curr_seq_no; u8 sta_src_mac_addr[6]; } __packed; struct mwl8k_destroy_ba_stream { __le32 flags; __le32 ba_context; } __packed; struct mwl8k_cmd_bastream { struct mwl8k_cmd_pkt header; __le32 action; union { struct mwl8k_create_ba_stream create_params; struct mwl8k_destroy_ba_stream destroy_params; }; } __packed; static int mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) { struct mwl8k_cmd_bastream *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_BA_CHECK); cmd->create_params.queue_id = stream->idx; memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr, ETH_ALEN); cmd->create_params.tid = stream->tid; cmd->create_params.flags = cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) | cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } static int mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, u8 buf_size) { struct mwl8k_cmd_bastream *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_BA_CREATE); cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size); cmd->create_params.window_size = cpu_to_le32((u32)buf_size); cmd->create_params.queue_id = stream->idx; memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN); cmd->create_params.tid = stream->tid; cmd->create_params.curr_seq_no = cpu_to_le16(0); cmd->create_params.reset_seq_no_flag = 1; cmd->create_params.param_info = (stream->sta->ht_cap.ampdu_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) | ((stream->sta->ht_cap.ampdu_density << 2) & IEEE80211_HT_AMPDU_PARM_DENSITY); cmd->create_params.flags = cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE | BASTREAM_FLAG_DIRECTION_UPSTREAM); rc = mwl8k_post_cmd(hw, &cmd->header); wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n", stream->sta->addr, stream->tid); kfree(cmd); return rc; } static void mwl8k_destroy_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) { struct mwl8k_cmd_bastream *cmd; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return; cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_BA_DESTROY); cmd->destroy_params.ba_context = cpu_to_le32(stream->idx); mwl8k_post_cmd(hw, &cmd->header); wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", stream->idx); kfree(cmd); } /* * CMD_SET_NEW_STN. */ struct mwl8k_cmd_set_new_stn { struct mwl8k_cmd_pkt header; __le16 aid; __u8 mac_addr[6]; __le16 stn_id; __le16 action; __le16 rsvd; __le32 legacy_rates; __u8 ht_rates[4]; __le16 cap_info; __le16 ht_capabilities_info; __u8 mac_ht_param_info; __u8 rev; __u8 control_channel; __u8 add_channel; __le16 op_mode; __le16 stbc; __u8 add_qos_info; __u8 is_qos_sta; __le32 fw_sta_ptr; } __packed; #define MWL8K_STA_ACTION_ADD 0 #define MWL8K_STA_ACTION_REMOVE 2 static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mwl8k_cmd_set_new_stn *cmd; u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->aid = cpu_to_le16(sta->aid); memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); cmd->stn_id = cpu_to_le16(sta->aid); cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; else rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; cmd->legacy_rates = cpu_to_le32(rates); if (sta->ht_cap.ht_supported) { cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0]; cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1]; cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2]; cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3]; cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap); cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) | ((sta->ht_cap.ampdu_density & 7) << 2); cmd->is_qos_sta = 1; } rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mwl8k_cmd_set_new_stn *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *addr) { struct mwl8k_cmd_set_new_stn *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); memcpy(cmd->mac_addr, addr, ETH_ALEN); cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } /* * CMD_UPDATE_ENCRYPTION. */ #define MAX_ENCR_KEY_LENGTH 16 #define MIC_KEY_LENGTH 8 struct mwl8k_cmd_update_encryption { struct mwl8k_cmd_pkt header; __le32 action; __le32 reserved; __u8 mac_addr[6]; __u8 encr_type; } __packed; struct mwl8k_cmd_set_key { struct mwl8k_cmd_pkt header; __le32 action; __le32 reserved; __le16 length; __le16 key_type_id; __le32 key_info; __le32 key_id; __le16 key_len; __u8 key_material[MAX_ENCR_KEY_LENGTH]; __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; __le16 tkip_rsc_low; __le32 tkip_rsc_high; __le16 tkip_tsc_low; __le32 tkip_tsc_high; __u8 mac_addr[6]; } __packed; enum { MWL8K_ENCR_ENABLE, MWL8K_ENCR_SET_KEY, MWL8K_ENCR_REMOVE_KEY, MWL8K_ENCR_SET_GROUP_KEY, }; #define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP 0 #define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE 1 #define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP 4 #define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED 7 #define MWL8K_UPDATE_ENCRYPTION_TYPE_AES 8 enum { MWL8K_ALG_WEP, MWL8K_ALG_TKIP, MWL8K_ALG_CCMP, }; #define MWL8K_KEY_FLAG_TXGROUPKEY 0x00000004 #define MWL8K_KEY_FLAG_PAIRWISE 0x00000008 #define MWL8K_KEY_FLAG_TSC_VALID 0x00000040 #define MWL8K_KEY_FLAG_WEP_TXKEY 0x01000000 #define MWL8K_KEY_FLAG_MICKEY_VALID 0x02000000 static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *addr, u8 encr_type) { struct mwl8k_cmd_update_encryption *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE); memcpy(cmd->mac_addr, addr, ETH_ALEN); cmd->encr_type = encr_type; rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd, u8 *addr, struct ieee80211_key_conf *key) { cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->length = cpu_to_le16(sizeof(*cmd) - offsetof(struct mwl8k_cmd_set_key, length)); cmd->key_id = cpu_to_le32(key->keyidx); cmd->key_len = cpu_to_le16(key->keylen); memcpy(cmd->mac_addr, addr, ETH_ALEN); switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP); if (key->keyidx == 0) cmd->key_info = cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY); break; case WLAN_CIPHER_SUITE_TKIP: cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP); cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID | MWL8K_KEY_FLAG_TSC_VALID); break; case WLAN_CIPHER_SUITE_CCMP: cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP); cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); break; default: return -ENOTSUPP; } return 0; } static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *addr, struct ieee80211_key_conf *key) { struct mwl8k_cmd_set_key *cmd; int rc; int keymlen; u32 action; u8 idx; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); if (rc < 0) goto done; idx = key->keyidx; if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) action = MWL8K_ENCR_SET_KEY; else action = MWL8K_ENCR_SET_GROUP_KEY; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: if (!mwl8k_vif->wep_key_conf[idx].enabled) { memcpy(mwl8k_vif->wep_key_conf[idx].key, key, sizeof(*key) + key->keylen); mwl8k_vif->wep_key_conf[idx].enabled = 1; } keymlen = key->keylen; action = MWL8K_ENCR_SET_KEY; break; case WLAN_CIPHER_SUITE_TKIP: keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; break; case WLAN_CIPHER_SUITE_CCMP: keymlen = key->keylen; break; default: rc = -ENOTSUPP; goto done; } memcpy(cmd->key_material, key->key, keymlen); cmd->action = cpu_to_le32(action); rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); done: kfree(cmd); return rc; } static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *addr, struct ieee80211_key_conf *key) { struct mwl8k_cmd_set_key *cmd; int rc; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); if (rc < 0) goto done; if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0; cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY); rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); done: kfree(cmd); return rc; } static int mwl8k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd_param, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { int rc = 0; u8 encr_type; u8 *addr; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); if (vif->type == NL80211_IFTYPE_STATION) return -EOPNOTSUPP; if (sta == NULL) addr = vif->addr; else addr = sta->addr; if (cmd_param == SET_KEY) { rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key); if (rc) goto out; if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) || (key->cipher == WLAN_CIPHER_SUITE_WEP104)) encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP; else encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED; rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr, encr_type); if (rc) goto out; mwl8k_vif->is_hw_crypto_enabled = true; } else { rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key); if (rc) goto out; } out: return rc; } /* * CMD_UPDATE_STADB. */ struct ewc_ht_info { __le16 control1; __le16 control2; __le16 control3; } __packed; struct peer_capability_info { /* Peer type - AP vs. STA. */ __u8 peer_type; /* Basic 802.11 capabilities from assoc resp. */ __le16 basic_caps; /* Set if peer supports 802.11n high throughput (HT). */ __u8 ht_support; /* Valid if HT is supported. */ __le16 ht_caps; __u8 extended_ht_caps; struct ewc_ht_info ewc_info; /* Legacy rate table. Intersection of our rates and peer rates. */ __u8 legacy_rates[12]; /* HT rate table. Intersection of our rates and peer rates. */ __u8 ht_rates[16]; __u8 pad[16]; /* If set, interoperability mode, no proprietary extensions. */ __u8 interop; __u8 pad2; __u8 station_id; __le16 amsdu_enabled; } __packed; struct mwl8k_cmd_update_stadb { struct mwl8k_cmd_pkt header; /* See STADB_ACTION_TYPE */ __le32 action; /* Peer MAC address */ __u8 peer_addr[ETH_ALEN]; __le32 reserved; /* Peer info - valid during add/update. */ struct peer_capability_info peer_info; } __packed; #define MWL8K_STA_DB_MODIFY_ENTRY 1 #define MWL8K_STA_DB_DEL_ENTRY 2 /* Peer Entry flags - used to define the type of the peer node */ #define MWL8K_PEER_TYPE_ACCESSPOINT 2 static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mwl8k_cmd_update_stadb *cmd; struct peer_capability_info *p; u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); p = &cmd->peer_info; p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); p->ht_support = sta->ht_cap.ht_supported; p->ht_caps = cpu_to_le16(sta->ht_cap.cap); p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | ((sta->ht_cap.ampdu_density & 7) << 2); if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; else rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; legacy_rate_mask_to_array(p->legacy_rates, rates); memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16); p->interop = 1; p->amsdu_enabled = 0; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc ? rc : p->station_id; } static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *addr) { struct mwl8k_cmd_update_stadb *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); memcpy(cmd->peer_addr, addr, ETH_ALEN); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; } /* * Interrupt handling. */ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) { struct ieee80211_hw *hw = dev_id; struct mwl8k_priv *priv = hw->priv; u32 status; status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); if (!status) return IRQ_NONE; if (status & MWL8K_A2H_INT_TX_DONE) { status &= ~MWL8K_A2H_INT_TX_DONE; tasklet_schedule(&priv->poll_tx_task); } if (status & MWL8K_A2H_INT_RX_READY) { status &= ~MWL8K_A2H_INT_RX_READY; tasklet_schedule(&priv->poll_rx_task); } if (status & MWL8K_A2H_INT_BA_WATCHDOG) { status &= ~MWL8K_A2H_INT_BA_WATCHDOG; ieee80211_queue_work(hw, &priv->watchdog_ba_handle); } if (status) iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); if (status & MWL8K_A2H_INT_OPC_DONE) { if (priv->hostcmd_wait != NULL) complete(priv->hostcmd_wait); } if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { if (!mutex_is_locked(&priv->fw_mutex) && priv->radio_on && priv->pending_tx_pkts) mwl8k_tx_start(priv); } return IRQ_HANDLED; } static void mwl8k_tx_poll(unsigned long data) { struct ieee80211_hw *hw = (struct ieee80211_hw *)data; struct mwl8k_priv *priv = hw->priv; int limit; int i; limit = 32; spin_lock_bh(&priv->tx_lock); for (i = 0; i < mwl8k_tx_queues(priv); i++) limit -= mwl8k_txq_reclaim(hw, i, limit, 0); if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { complete(priv->tx_wait); priv->tx_wait = NULL; } spin_unlock_bh(&priv->tx_lock); if (limit) { writel(~MWL8K_A2H_INT_TX_DONE, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); } else { tasklet_schedule(&priv->poll_tx_task); } } static void mwl8k_rx_poll(unsigned long data) { struct ieee80211_hw *hw = (struct ieee80211_hw *)data; struct mwl8k_priv *priv = hw->priv; int limit; limit = 32; limit -= rxq_process(hw, 0, limit); limit -= rxq_refill(hw, 0, limit); if (limit) { writel(~MWL8K_A2H_INT_RX_READY, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); } else { tasklet_schedule(&priv->poll_rx_task); } } /* * Core driver operations. */ static void mwl8k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; int index = skb_get_queue_mapping(skb); if (!priv->radio_on) { wiphy_debug(hw->wiphy, "dropped TX frame since radio disabled\n"); dev_kfree_skb(skb); return; } mwl8k_txq_xmit(hw, index, control->sta, skb); } static int mwl8k_start(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; int rc; rc = request_irq(priv->pdev->irq, mwl8k_interrupt, IRQF_SHARED, MWL8K_NAME, hw); if (rc) { priv->irq = -1; wiphy_err(hw->wiphy, "failed to register IRQ handler\n"); return -EIO; } priv->irq = priv->pdev->irq; /* Enable TX reclaim and RX tasklets. */ tasklet_enable(&priv->poll_tx_task); tasklet_enable(&priv->poll_rx_task); /* Enable interrupts */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); rc = mwl8k_fw_lock(hw); if (!rc) { rc = mwl8k_cmd_radio_enable(hw); if (!priv->ap_fw) { if (!rc) rc = mwl8k_cmd_enable_sniffer(hw, 0); if (!rc) rc = mwl8k_cmd_set_pre_scan(hw); if (!rc) rc = mwl8k_cmd_set_post_scan(hw, "\x00\x00\x00\x00\x00\x00"); } if (!rc) rc = mwl8k_cmd_set_rateadapt_mode(hw, 0); if (!rc) rc = mwl8k_cmd_set_wmm_mode(hw, 0); mwl8k_fw_unlock(hw); } if (rc) { iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); priv->irq = -1; tasklet_disable(&priv->poll_tx_task); tasklet_disable(&priv->poll_rx_task); } return rc; } static void mwl8k_stop(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; int i; if (!priv->hw_restart_in_progress) mwl8k_cmd_radio_disable(hw); ieee80211_stop_queues(hw); /* Disable interrupts */ iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); if (priv->irq != -1) { free_irq(priv->pdev->irq, hw); priv->irq = -1; } /* Stop finalize join worker */ cancel_work_sync(&priv->finalize_join_worker); cancel_work_sync(&priv->watchdog_ba_handle); if (priv->beacon_skb != NULL) dev_kfree_skb(priv->beacon_skb); /* Stop TX reclaim and RX tasklets. */ tasklet_disable(&priv->poll_tx_task); tasklet_disable(&priv->poll_rx_task); /* Return all skbs to mac80211 */ for (i = 0; i < mwl8k_tx_queues(priv); i++) mwl8k_txq_reclaim(hw, i, INT_MAX, 1); } static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image); static int mwl8k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif; u32 macids_supported; int macid, rc; struct mwl8k_device_info *di; /* * Reject interface creation if sniffer mode is active, as * STA operation is mutually exclusive with hardware sniffer * mode. (Sniffer mode is only used on STA firmware.) */ if (priv->sniffer_enabled) { wiphy_info(hw->wiphy, "unable to create STA interface because sniffer mode is enabled\n"); return -EINVAL; } di = priv->device_info; switch (vif->type) { case NL80211_IFTYPE_AP: if (!priv->ap_fw && di->fw_image_ap) { /* we must load the ap fw to meet this request */ if (!list_empty(&priv->vif_list)) return -EBUSY; rc = mwl8k_reload_firmware(hw, di->fw_image_ap); if (rc) return rc; } macids_supported = priv->ap_macids_supported; break; case NL80211_IFTYPE_STATION: if (priv->ap_fw && di->fw_image_sta) { /* we must load the sta fw to meet this request */ if (!list_empty(&priv->vif_list)) return -EBUSY; rc = mwl8k_reload_firmware(hw, di->fw_image_sta); if (rc) return rc; } macids_supported = priv->sta_macids_supported; break; default: return -EINVAL; } macid = ffs(macids_supported & ~priv->macids_used); if (!macid--) return -EBUSY; /* Setup driver private area. */ mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); mwl8k_vif->vif = vif; mwl8k_vif->macid = macid; mwl8k_vif->seqno = 0; memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN); mwl8k_vif->is_hw_crypto_enabled = false; /* Set the mac address. */ mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); if (priv->ap_fw) mwl8k_cmd_set_new_stn_add_self(hw, vif); priv->macids_used |= 1 << mwl8k_vif->macid; list_add_tail(&mwl8k_vif->list, &priv->vif_list); return 0; } static void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif) { /* Has ieee80211_restart_hw re-added the removed interfaces? */ if (!priv->macids_used) return; priv->macids_used &= ~(1 << vif->macid); list_del(&vif->list); } static void mwl8k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); if (priv->ap_fw) mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); mwl8k_cmd_del_mac_addr(hw, vif, vif->addr); mwl8k_remove_vif(priv, mwl8k_vif); } static void mwl8k_hw_restart_work(struct work_struct *work) { struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, fw_reload); struct ieee80211_hw *hw = priv->hw; struct mwl8k_device_info *di; int rc; /* If some command is waiting for a response, clear it */ if (priv->hostcmd_wait != NULL) { complete(priv->hostcmd_wait); priv->hostcmd_wait = NULL; } priv->hw_restart_owner = current; di = priv->device_info; mwl8k_fw_lock(hw); if (priv->ap_fw) rc = mwl8k_reload_firmware(hw, di->fw_image_ap); else rc = mwl8k_reload_firmware(hw, di->fw_image_sta); if (rc) goto fail; priv->hw_restart_owner = NULL; priv->hw_restart_in_progress = false; /* * This unlock will wake up the queues and * also opens the command path for other * commands */ mwl8k_fw_unlock(hw); ieee80211_restart_hw(hw); wiphy_err(hw->wiphy, "Firmware restarted successfully\n"); return; fail: mwl8k_fw_unlock(hw); wiphy_err(hw->wiphy, "Firmware restart failed\n"); } static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) { struct ieee80211_conf *conf = &hw->conf; struct mwl8k_priv *priv = hw->priv; int rc; if (conf->flags & IEEE80211_CONF_IDLE) { mwl8k_cmd_radio_disable(hw); return 0; } rc = mwl8k_fw_lock(hw); if (rc) return rc; rc = mwl8k_cmd_radio_enable(hw); if (rc) goto out; rc = mwl8k_cmd_set_rf_channel(hw, conf); if (rc) goto out; if (conf->power_level > 18) conf->power_level = 18; if (priv->ap_fw) { if (conf->flags & IEEE80211_CONF_CHANGE_POWER) { rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level); if (rc) goto out; } rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3); if (rc) wiphy_warn(hw->wiphy, "failed to set # of RX antennas"); rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); if (rc) wiphy_warn(hw->wiphy, "failed to set # of TX antennas"); } else { rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); if (rc) goto out; rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7); } out: mwl8k_fw_unlock(hw); return rc; } static void mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct mwl8k_priv *priv = hw->priv; u32 ap_legacy_rates = 0; u8 ap_mcs_rates[16]; int rc; if (mwl8k_fw_lock(hw)) return; /* * No need to capture a beacon if we're no longer associated. */ if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc) priv->capture_beacon = false; /* * Get the AP's legacy and MCS rates. */ if (vif->bss_conf.assoc) { struct ieee80211_sta *ap; rcu_read_lock(); ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); if (ap == NULL) { rcu_read_unlock(); goto out; } if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) { ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; } else { ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_5GHZ] << 5; } memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16); rcu_read_unlock(); } if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates); if (rc) goto out; rc = mwl8k_cmd_use_fixed_rate_sta(hw); if (rc) goto out; } if (changed & BSS_CHANGED_ERP_PREAMBLE) { rc = mwl8k_set_radio_preamble(hw, vif->bss_conf.use_short_preamble); if (rc) goto out; } if (changed & BSS_CHANGED_ERP_SLOT) { rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); if (rc) goto out; } if (vif->bss_conf.assoc && (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))) { rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); if (rc) goto out; } if (vif->bss_conf.assoc && (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { /* * Finalize the join. Tell rx handler to process * next beacon from our BSSID. */ memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); priv->capture_beacon = true; } out: mwl8k_fw_unlock(hw); } static void mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { int rc; if (mwl8k_fw_lock(hw)) return; if (changed & BSS_CHANGED_ERP_PREAMBLE) { rc = mwl8k_set_radio_preamble(hw, vif->bss_conf.use_short_preamble); if (rc) goto out; } if (changed & BSS_CHANGED_BASIC_RATES) { int idx; int rate; /* * Use lowest supported basic rate for multicasts * and management frames (such as probe responses -- * beacons will always go out at 1 Mb/s). */ idx = ffs(vif->bss_conf.basic_rates); if (idx) idx--; if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) rate = mwl8k_rates_24[idx].hw_value; else rate = mwl8k_rates_50[idx].hw_value; mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); } if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { struct sk_buff *skb; skb = ieee80211_beacon_get(hw, vif); if (skb != NULL) { mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len); kfree_skb(skb); } } if (changed & BSS_CHANGED_BEACON_ENABLED) mwl8k_cmd_bss_start(hw, vif, info->enable_beacon); out: mwl8k_fw_unlock(hw); } static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct mwl8k_priv *priv = hw->priv; if (!priv->ap_fw) mwl8k_bss_info_changed_sta(hw, vif, info, changed); else mwl8k_bss_info_changed_ap(hw, vif, info, changed); } static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *ha) #endif { struct mwl8k_cmd_pkt *cmd; /* * Synthesize and return a command packet that programs the * hardware multicast address filter. At this point we don't * know whether FIF_ALLMULTI is being requested, but if it is, * we'll end up throwing this packet away and creating a new * one in mwl8k_configure_filter(). */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list); #else cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_count, ha); #endif return (unsigned long)cmd; } static int mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags) { struct mwl8k_priv *priv = hw->priv; /* * Hardware sniffer mode is mutually exclusive with STA * operation, so refuse to enable sniffer mode if a STA * interface is active. */ if (!list_empty(&priv->vif_list)) { if (net_ratelimit()) wiphy_info(hw->wiphy, "not enabling sniffer mode because STA interface is active\n"); return 0; } if (!priv->sniffer_enabled) { if (mwl8k_cmd_enable_sniffer(hw, 1)) return 0; priv->sniffer_enabled = true; } *total_flags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL | FIF_OTHER_BSS; return 1; } static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv) { if (!list_empty(&priv->vif_list)) return list_entry(priv->vif_list.next, struct mwl8k_vif, list); return NULL; } static void mwl8k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast; /* * AP firmware doesn't allow fine-grained control over * the receive filter. */ if (priv->ap_fw) { *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; kfree(cmd); return; } /* * Enable hardware sniffer mode if FIF_CONTROL or * FIF_OTHER_BSS is requested. */ if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) && mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) { kfree(cmd); return; } /* Clear unsupported feature flags */ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; if (mwl8k_fw_lock(hw)) { kfree(cmd); return; } if (priv->sniffer_enabled) { mwl8k_cmd_enable_sniffer(hw, 0); priv->sniffer_enabled = false; } if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { if (*total_flags & FIF_BCN_PRBRESP_PROMISC) { /* * Disable the BSS filter. */ mwl8k_cmd_set_pre_scan(hw); } else { struct mwl8k_vif *mwl8k_vif; const u8 *bssid; /* * Enable the BSS filter. * * If there is an active STA interface, use that * interface's BSSID, otherwise use a dummy one * (where the OUI part needs to be nonzero for * the BSSID to be accepted by POST_SCAN). */ mwl8k_vif = mwl8k_first_vif(priv); if (mwl8k_vif != NULL) bssid = mwl8k_vif->vif->bss_conf.bssid; else bssid = "\x01\x00\x00\x00\x00\x00"; mwl8k_cmd_set_post_scan(hw, bssid); } } /* * If FIF_ALLMULTI is being requested, throw away the command * packet that ->prepare_multicast() built and replace it with * a command packet that enables reception of all multicast * packets. */ if (*total_flags & FIF_ALLMULTI) { kfree(cmd); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL); #else cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, 0, NULL); #endif } if (cmd != NULL) { mwl8k_post_cmd(hw, cmd); kfree(cmd); } mwl8k_fw_unlock(hw); } static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { return mwl8k_cmd_set_rts_threshold(hw, value); } static int mwl8k_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mwl8k_priv *priv = hw->priv; if (priv->ap_fw) return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr); else return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr); } static int mwl8k_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mwl8k_priv *priv = hw->priv; int ret; int i; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); struct ieee80211_key_conf *key; if (!priv->ap_fw) { ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); if (ret >= 0) { MWL8K_STA(sta)->peer_id = ret; if (sta->ht_cap.ht_supported) MWL8K_STA(sta)->is_ampdu_allowed = true; ret = 0; } } else { ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta); } for (i = 0; i < NUM_WEP_KEYS; i++) { key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key); if (mwl8k_vif->wep_key_conf[i].enabled) mwl8k_set_key(hw, SET_KEY, vif, sta, key); } return ret; } static int mwl8k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct mwl8k_priv *priv = hw->priv; int rc; rc = mwl8k_fw_lock(hw); if (!rc) { BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1); memcpy(&priv->wmm_params[queue], params, sizeof(*params)); if (!priv->wmm_enabled) rc = mwl8k_cmd_set_wmm_mode(hw, 1); if (!rc) { int q = MWL8K_TX_WMM_QUEUES - 1 - queue; rc = mwl8k_cmd_set_edca_params(hw, q, params->cw_min, params->cw_max, params->aifs, params->txop); } mwl8k_fw_unlock(hw); } return rc; } static int mwl8k_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { return mwl8k_cmd_get_stat(hw, stats); } static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct mwl8k_priv *priv = hw->priv; struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; survey->filled = SURVEY_INFO_NOISE_DBM; survey->noise = priv->noise; return 0; } #define MAX_AMPDU_ATTEMPTS 5 static int mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { int i, rc = 0; struct mwl8k_priv *priv = hw->priv; struct mwl8k_ampdu_stream *stream; u8 *addr = sta->addr; if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) return -ENOTSUPP; spin_lock(&priv->stream_lock); stream = mwl8k_lookup_stream(hw, addr, tid); switch (action) { case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: /* By the time we get here the hw queues may contain outgoing * packets for this RA/TID that are not part of this BA * session. The hw will assign sequence numbers to these * packets as they go out. So if we query the hw for its next * sequence number and use that for the SSN here, it may end up * being wrong, which will lead to sequence number mismatch at * the recipient. To avoid this, we reset the sequence number * to O for the first MPDU in this BA stream. */ *ssn = 0; if (stream == NULL) { /* This means that somebody outside this driver called * ieee80211_start_tx_ba_session. This is unexpected * because we do our own rate control. Just warn and * move on. */ wiphy_warn(hw->wiphy, "Unexpected call to %s. " "Proceeding anyway.\n", __func__); stream = mwl8k_add_stream(hw, sta, tid); } if (stream == NULL) { wiphy_debug(hw->wiphy, "no free AMPDU streams\n"); rc = -EBUSY; break; } stream->state = AMPDU_STREAM_IN_PROGRESS; /* Release the lock before we do the time consuming stuff */ spin_unlock(&priv->stream_lock); for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { rc = mwl8k_check_ba(hw, stream); /* If HW restart is in progress mwl8k_post_cmd will * return -EBUSY. Avoid retrying mwl8k_check_ba in * such cases */ if (!rc || rc == -EBUSY) break; /* * HW queues take time to be flushed, give them * sufficient time */ msleep(1000); } spin_lock(&priv->stream_lock); if (rc) { wiphy_err(hw->wiphy, "Stream for tid %d busy after %d" " attempts\n", tid, MAX_AMPDU_ATTEMPTS); mwl8k_remove_stream(hw, stream); rc = -EBUSY; break; } ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); break; case IEEE80211_AMPDU_TX_STOP: if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { spin_unlock(&priv->stream_lock); mwl8k_destroy_ba(hw, stream); spin_lock(&priv->stream_lock); } mwl8k_remove_stream(hw, stream); } ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: BUG_ON(stream == NULL); BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); spin_unlock(&priv->stream_lock); rc = mwl8k_create_ba(hw, stream, buf_size); spin_lock(&priv->stream_lock); if (!rc) stream->state = AMPDU_STREAM_ACTIVE; else { spin_unlock(&priv->stream_lock); mwl8k_destroy_ba(hw, stream); spin_lock(&priv->stream_lock); wiphy_debug(hw->wiphy, "Failed adding stream for sta %pM tid %d\n", addr, tid); mwl8k_remove_stream(hw, stream); } break; default: rc = -ENOTSUPP; } spin_unlock(&priv->stream_lock); return rc; } static const struct ieee80211_ops mwl8k_ops = { .tx = mwl8k_tx, .start = mwl8k_start, .stop = mwl8k_stop, .add_interface = mwl8k_add_interface, .remove_interface = mwl8k_remove_interface, .config = mwl8k_config, .bss_info_changed = mwl8k_bss_info_changed, .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_key = mwl8k_set_key, .set_rts_threshold = mwl8k_set_rts_threshold, .sta_add = mwl8k_sta_add, .sta_remove = mwl8k_sta_remove, .conf_tx = mwl8k_conf_tx, .get_stats = mwl8k_get_stats, .get_survey = mwl8k_get_survey, .ampdu_action = mwl8k_ampdu_action, }; static void mwl8k_finalize_join_worker(struct work_struct *work) { struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, finalize_join_worker); struct sk_buff *skb = priv->beacon_skb; struct ieee80211_mgmt *mgmt = (void *)skb->data; int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable); const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM, mgmt->u.beacon.variable, len); int dtim_period = 1; if (tim && tim[1] >= 2) dtim_period = tim[3]; mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period); dev_kfree_skb(skb); priv->beacon_skb = NULL; } enum { MWL8363 = 0, MWL8687, MWL8366, }; #define MWL8K_8366_AP_FW_API 2 #define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw" #define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api) static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { [MWL8363] = { .part_name = "88w8363", .helper_image = "mwl8k/helper_8363.fw", .fw_image_sta = "mwl8k/fmimage_8363.fw", }, [MWL8687] = { .part_name = "88w8687", .helper_image = "mwl8k/helper_8687.fw", .fw_image_sta = "mwl8k/fmimage_8687.fw", }, [MWL8366] = { .part_name = "88w8366", .helper_image = "mwl8k/helper_8366.fw", .fw_image_sta = "mwl8k/fmimage_8366.fw", .fw_image_ap = MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API), .fw_api_ap = MWL8K_8366_AP_FW_API, .ap_rxd_ops = &rxd_8366_ap_ops, }, }; MODULE_FIRMWARE("mwl8k/helper_8363.fw"); MODULE_FIRMWARE("mwl8k/fmimage_8363.fw"); MODULE_FIRMWARE("mwl8k/helper_8687.fw"); MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); MODULE_FIRMWARE("mwl8k/helper_8366.fw"); MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API)); static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, }, { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, { }, }; MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); static int mwl8k_request_alt_fw(struct mwl8k_priv *priv) { int rc; printk(KERN_ERR "%s: Error requesting preferred fw %s.\n" "Trying alternative firmware %s\n", pci_name(priv->pdev), priv->fw_pref, priv->fw_alt); rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true); if (rc) { printk(KERN_ERR "%s: Error requesting alt fw %s\n", pci_name(priv->pdev), priv->fw_alt); return rc; } return 0; } static int mwl8k_firmware_load_success(struct mwl8k_priv *priv); static void mwl8k_fw_state_machine(const struct firmware *fw, void *context) { struct mwl8k_priv *priv = context; struct mwl8k_device_info *di = priv->device_info; int rc; switch (priv->fw_state) { case FW_STATE_INIT: if (!fw) { printk(KERN_ERR "%s: Error requesting helper fw %s\n", pci_name(priv->pdev), di->helper_image); goto fail; } priv->fw_helper = fw; rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode, true); if (rc && priv->fw_alt) { rc = mwl8k_request_alt_fw(priv); if (rc) goto fail; priv->fw_state = FW_STATE_LOADING_ALT; } else if (rc) goto fail; else priv->fw_state = FW_STATE_LOADING_PREF; break; case FW_STATE_LOADING_PREF: if (!fw) { if (priv->fw_alt) { rc = mwl8k_request_alt_fw(priv); if (rc) goto fail; priv->fw_state = FW_STATE_LOADING_ALT; } else goto fail; } else { priv->fw_ucode = fw; rc = mwl8k_firmware_load_success(priv); if (rc) goto fail; else complete(&priv->firmware_loading_complete); } break; case FW_STATE_LOADING_ALT: if (!fw) { printk(KERN_ERR "%s: Error requesting alt fw %s\n", pci_name(priv->pdev), di->helper_image); goto fail; } priv->fw_ucode = fw; rc = mwl8k_firmware_load_success(priv); if (rc) goto fail; else complete(&priv->firmware_loading_complete); break; default: printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n", MWL8K_NAME, priv->fw_state); BUG_ON(1); } return; fail: priv->fw_state = FW_STATE_ERROR; complete(&priv->firmware_loading_complete); device_release_driver(&priv->pdev->dev); mwl8k_release_firmware(priv); } #define MAX_RESTART_ATTEMPTS 1 static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image, bool nowait) { struct mwl8k_priv *priv = hw->priv; int rc; int count = MAX_RESTART_ATTEMPTS; retry: /* Reset firmware and hardware */ mwl8k_hw_reset(priv); /* Ask userland hotplug daemon for the device firmware */ rc = mwl8k_request_firmware(priv, fw_image, nowait); if (rc) { wiphy_err(hw->wiphy, "Firmware files not found\n"); return rc; } if (nowait) return rc; /* Load firmware into hardware */ rc = mwl8k_load_firmware(hw); if (rc) wiphy_err(hw->wiphy, "Cannot start firmware\n"); /* Reclaim memory once firmware is successfully loaded */ mwl8k_release_firmware(priv); if (rc && count) { /* FW did not start successfully; * lets try one more time */ count--; wiphy_err(hw->wiphy, "Trying to reload the firmware again\n"); msleep(20); goto retry; } return rc; } static int mwl8k_init_txqs(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; int rc = 0; int i; for (i = 0; i < mwl8k_tx_queues(priv); i++) { rc = mwl8k_txq_init(hw, i); if (rc) break; if (priv->ap_fw) iowrite32(priv->txq[i].txd_dma, priv->sram + priv->txq_offset[i]); } return rc; } /* initialize hw after successfully loading a firmware image */ static int mwl8k_probe_hw(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; int rc = 0; int i; if (priv->ap_fw) { priv->rxd_ops = priv->device_info->ap_rxd_ops; if (priv->rxd_ops == NULL) { wiphy_err(hw->wiphy, "Driver does not have AP firmware image support for this hardware\n"); goto err_stop_firmware; } } else { priv->rxd_ops = &rxd_sta_ops; } priv->sniffer_enabled = false; priv->wmm_enabled = false; priv->pending_tx_pkts = 0; rc = mwl8k_rxq_init(hw, 0); if (rc) goto err_stop_firmware; rxq_refill(hw, 0, INT_MAX); /* For the sta firmware, we need to know the dma addresses of tx queues * before sending MWL8K_CMD_GET_HW_SPEC. So we must initialize them * prior to issuing this command. But for the AP case, we learn the * total number of queues from the result CMD_GET_HW_SPEC, so for this * case we must initialize the tx queues after. */ priv->num_ampdu_queues = 0; if (!priv->ap_fw) { rc = mwl8k_init_txqs(hw); if (rc) goto err_free_queues; } iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); iowrite32(MWL8K_A2H_INT_TX_DONE|MWL8K_A2H_INT_RX_READY| MWL8K_A2H_INT_BA_WATCHDOG, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(MWL8K_A2H_INT_OPC_DONE, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); rc = request_irq(priv->pdev->irq, mwl8k_interrupt, IRQF_SHARED, MWL8K_NAME, hw); if (rc) { wiphy_err(hw->wiphy, "failed to register IRQ handler\n"); goto err_free_queues; } /* * When hw restart is requested, * mac80211 will take care of clearing * the ampdu streams, so do not clear * the ampdu state here */ if (!priv->hw_restart_in_progress) memset(priv->ampdu, 0, sizeof(priv->ampdu)); /* * Temporarily enable interrupts. Initial firmware host * commands use interrupts and avoid polling. Disable * interrupts when done. */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); /* Get config data, mac addrs etc */ if (priv->ap_fw) { rc = mwl8k_cmd_get_hw_spec_ap(hw); if (!rc) rc = mwl8k_init_txqs(hw); if (!rc) rc = mwl8k_cmd_set_hw_spec(hw); } else { rc = mwl8k_cmd_get_hw_spec_sta(hw); } if (rc) { wiphy_err(hw->wiphy, "Cannot initialise firmware\n"); goto err_free_irq; } /* Turn radio off */ rc = mwl8k_cmd_radio_disable(hw); if (rc) { wiphy_err(hw->wiphy, "Cannot disable\n"); goto err_free_irq; } /* Clear MAC address */ rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00"); if (rc) { wiphy_err(hw->wiphy, "Cannot clear MAC address\n"); goto err_free_irq; } /* Disable interrupts */ iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n", priv->device_info->part_name, priv->hw_rev, hw->wiphy->perm_addr, priv->ap_fw ? "AP" : "STA", (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff, (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff); return 0; err_free_irq: iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); err_free_queues: for (i = 0; i < mwl8k_tx_queues(priv); i++) mwl8k_txq_deinit(hw, i); mwl8k_rxq_deinit(hw, 0); err_stop_firmware: mwl8k_hw_reset(priv); return rc; } /* * invoke mwl8k_reload_firmware to change the firmware image after the device * has already been registered */ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image) { int i, rc = 0; struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *vif, *tmp_vif; mwl8k_stop(hw); mwl8k_rxq_deinit(hw, 0); /* * All the existing interfaces are re-added by the ieee80211_reconfig; * which means driver should remove existing interfaces before calling * ieee80211_restart_hw */ if (priv->hw_restart_in_progress) list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list) mwl8k_remove_vif(priv, vif); for (i = 0; i < mwl8k_tx_queues(priv); i++) mwl8k_txq_deinit(hw, i); rc = mwl8k_init_firmware(hw, fw_image, false); if (rc) goto fail; rc = mwl8k_probe_hw(hw); if (rc) goto fail; if (priv->hw_restart_in_progress) return rc; rc = mwl8k_start(hw); if (rc) goto fail; rc = mwl8k_config(hw, ~0); if (rc) goto fail; for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) { rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]); if (rc) goto fail; } return rc; fail: printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n"); return rc; } static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) { struct ieee80211_hw *hw = priv->hw; int i, rc; rc = mwl8k_load_firmware(hw); mwl8k_release_firmware(priv); if (rc) { wiphy_err(hw->wiphy, "Cannot start firmware\n"); return rc; } /* * Extra headroom is the size of the required DMA header * minus the size of the smallest 802.11 frame (CTS frame). */ hw->extra_tx_headroom = sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts); hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; hw->channel_change_time = 10; hw->queues = MWL8K_TX_WMM_QUEUES; /* Set rssi values to dBm */ hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_HAS_RATE_CONTROL; /* * Ask mac80211 to not to trigger PS mode * based on PM bit of incoming frames. */ if (priv->ap_fw) hw->flags |= IEEE80211_HW_AP_LINK_PS; hw->vif_data_size = sizeof(struct mwl8k_vif); hw->sta_data_size = sizeof(struct mwl8k_sta); priv->macids_used = 0; INIT_LIST_HEAD(&priv->vif_list); /* Set default radio state and preamble */ priv->radio_on = false; priv->radio_short_preamble = false; /* Finalize join worker */ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); /* Handle watchdog ba events */ INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events); /* To reload the firmware if it crashes */ INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work); /* TX reclaim and RX tasklets. */ tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); tasklet_disable(&priv->poll_tx_task); tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw); tasklet_disable(&priv->poll_rx_task); /* Power management cookie */ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); if (priv->cookie == NULL) return -ENOMEM; mutex_init(&priv->fw_mutex); priv->fw_mutex_owner = NULL; priv->fw_mutex_depth = 0; priv->hostcmd_wait = NULL; spin_lock_init(&priv->tx_lock); spin_lock_init(&priv->stream_lock); priv->tx_wait = NULL; rc = mwl8k_probe_hw(hw); if (rc) goto err_free_cookie; hw->wiphy->interface_modes = 0; if (priv->ap_macids_supported || priv->device_info->fw_image_ap) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); if (priv->sta_macids_supported || priv->device_info->fw_image_sta) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); rc = ieee80211_register_hw(hw); if (rc) { wiphy_err(hw->wiphy, "Cannot register device\n"); goto err_unprobe_hw; } return 0; err_unprobe_hw: for (i = 0; i < mwl8k_tx_queues(priv); i++) mwl8k_txq_deinit(hw, i); mwl8k_rxq_deinit(hw, 0); err_free_cookie: if (priv->cookie != NULL) pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); return rc; } static int __devinit mwl8k_probe(struct pci_dev *pdev, const struct pci_device_id *id) { static int printed_version; struct ieee80211_hw *hw; struct mwl8k_priv *priv; struct mwl8k_device_info *di; int rc; if (!printed_version) { printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION); printed_version = 1; } rc = pci_enable_device(pdev); if (rc) { printk(KERN_ERR "%s: Cannot enable new PCI device\n", MWL8K_NAME); return rc; } rc = pci_request_regions(pdev, MWL8K_NAME); if (rc) { printk(KERN_ERR "%s: Cannot obtain PCI resources\n", MWL8K_NAME); goto err_disable_device; } pci_set_master(pdev); hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops); if (hw == NULL) { printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME); rc = -ENOMEM; goto err_free_reg; } SET_IEEE80211_DEV(hw, &pdev->dev); pci_set_drvdata(pdev, hw); priv = hw->priv; priv->hw = hw; priv->pdev = pdev; priv->device_info = &mwl8k_info_tbl[id->driver_data]; priv->sram = pci_iomap(pdev, 0, 0x10000); if (priv->sram == NULL) { wiphy_err(hw->wiphy, "Cannot map device SRAM\n"); goto err_iounmap; } /* * If BAR0 is a 32 bit BAR, the register BAR will be BAR1. * If BAR0 is a 64 bit BAR, the register BAR will be BAR2. */ priv->regs = pci_iomap(pdev, 1, 0x10000); if (priv->regs == NULL) { priv->regs = pci_iomap(pdev, 2, 0x10000); if (priv->regs == NULL) { wiphy_err(hw->wiphy, "Cannot map device registers\n"); goto err_iounmap; } } /* * Choose the initial fw image depending on user input. If a second * image is available, make it the alternative image that will be * loaded if the first one fails. */ init_completion(&priv->firmware_loading_complete); di = priv->device_info; if (ap_mode_default && di->fw_image_ap) { priv->fw_pref = di->fw_image_ap; priv->fw_alt = di->fw_image_sta; } else if (!ap_mode_default && di->fw_image_sta) { priv->fw_pref = di->fw_image_sta; priv->fw_alt = di->fw_image_ap; } else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) { printk(KERN_WARNING "AP fw is unavailable. Using STA fw."); priv->fw_pref = di->fw_image_sta; } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) { printk(KERN_WARNING "STA fw is unavailable. Using AP fw."); priv->fw_pref = di->fw_image_ap; } rc = mwl8k_init_firmware(hw, priv->fw_pref, true); if (rc) goto err_stop_firmware; priv->hw_restart_in_progress = false; return rc; err_stop_firmware: mwl8k_hw_reset(priv); err_iounmap: if (priv->regs != NULL) pci_iounmap(pdev, priv->regs); if (priv->sram != NULL) pci_iounmap(pdev, priv->sram); pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); err_free_reg: pci_release_regions(pdev); err_disable_device: pci_disable_device(pdev); return rc; } static void __devexit mwl8k_shutdown(struct pci_dev *pdev) { printk(KERN_ERR "===>%s(%u)\n", __func__, __LINE__); } static void __devexit mwl8k_remove(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct mwl8k_priv *priv; int i; if (hw == NULL) return; priv = hw->priv; wait_for_completion(&priv->firmware_loading_complete); if (priv->fw_state == FW_STATE_ERROR) { mwl8k_hw_reset(priv); goto unmap; } ieee80211_stop_queues(hw); ieee80211_unregister_hw(hw); /* Remove TX reclaim and RX tasklets. */ tasklet_kill(&priv->poll_tx_task); tasklet_kill(&priv->poll_rx_task); /* Stop hardware */ mwl8k_hw_reset(priv); /* Return all skbs to mac80211 */ for (i = 0; i < mwl8k_tx_queues(priv); i++) mwl8k_txq_reclaim(hw, i, INT_MAX, 1); for (i = 0; i < mwl8k_tx_queues(priv); i++) mwl8k_txq_deinit(hw, i); mwl8k_rxq_deinit(hw, 0); pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); unmap: pci_iounmap(pdev, priv->regs); pci_iounmap(pdev, priv->sram); pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); pci_release_regions(pdev); pci_disable_device(pdev); } static struct pci_driver mwl8k_driver = { .name = MWL8K_NAME, .id_table = mwl8k_pci_id_table, .probe = mwl8k_probe, .remove = __devexit_p(mwl8k_remove), .shutdown = __devexit_p(mwl8k_shutdown), }; module_pci_driver(mwl8k_driver); MODULE_DESCRIPTION(MWL8K_DESC); MODULE_VERSION(MWL8K_VERSION); MODULE_AUTHOR("Lennert Buytenhek "); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/adm8211.c0000644000175000017500000015465412026211315021721 0ustar mcgrofmcgrof /* * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP) * * Copyright (c) 2003, Jouni Malinen * Copyright (c) 2004-2007, Michael Wu * Some parts copyright (c) 2003 by David Young * and used with permission. * * Much thanks to Infineon-ADMtek for their support of this driver. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "adm8211.h" MODULE_AUTHOR("Michael Wu "); MODULE_AUTHOR("Jouni Malinen "); MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless cards based on ADMtek ADM8211"); MODULE_SUPPORTED_DEVICE("ADM8211"); MODULE_LICENSE("GPL"); static unsigned int tx_ring_size __read_mostly = 16; static unsigned int rx_ring_size __read_mostly = 16; module_param(tx_ring_size, uint, 0); module_param(rx_ring_size, uint, 0); static DEFINE_PCI_DEVICE_TABLE(adm8211_pci_id_table) = { /* ADMtek ADM8211 */ { PCI_DEVICE(0x10B7, 0x6000) }, /* 3Com 3CRSHPW796 */ { PCI_DEVICE(0x1200, 0x8201) }, /* ? */ { PCI_DEVICE(0x1317, 0x8201) }, /* ADM8211A */ { PCI_DEVICE(0x1317, 0x8211) }, /* ADM8211B/C */ { 0 } }; static struct ieee80211_rate adm8211_rates[] = { { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 220, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, /* XX ?? */ }; static const struct ieee80211_channel adm8211_channels[] = { { .center_freq = 2412}, { .center_freq = 2417}, { .center_freq = 2422}, { .center_freq = 2427}, { .center_freq = 2432}, { .center_freq = 2437}, { .center_freq = 2442}, { .center_freq = 2447}, { .center_freq = 2452}, { .center_freq = 2457}, { .center_freq = 2462}, { .center_freq = 2467}, { .center_freq = 2472}, { .center_freq = 2484}, }; static void adm8211_eeprom_register_read(struct eeprom_93cx6 *eeprom) { struct adm8211_priv *priv = eeprom->data; u32 reg = ADM8211_CSR_READ(SPR); eeprom->reg_data_in = reg & ADM8211_SPR_SDI; eeprom->reg_data_out = reg & ADM8211_SPR_SDO; eeprom->reg_data_clock = reg & ADM8211_SPR_SCLK; eeprom->reg_chip_select = reg & ADM8211_SPR_SCS; } static void adm8211_eeprom_register_write(struct eeprom_93cx6 *eeprom) { struct adm8211_priv *priv = eeprom->data; u32 reg = 0x4000 | ADM8211_SPR_SRS; if (eeprom->reg_data_in) reg |= ADM8211_SPR_SDI; if (eeprom->reg_data_out) reg |= ADM8211_SPR_SDO; if (eeprom->reg_data_clock) reg |= ADM8211_SPR_SCLK; if (eeprom->reg_chip_select) reg |= ADM8211_SPR_SCS; ADM8211_CSR_WRITE(SPR, reg); ADM8211_CSR_READ(SPR); /* eeprom_delay */ } static int adm8211_read_eeprom(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; unsigned int words, i; struct ieee80211_chan_range chan_range; u16 cr49; struct eeprom_93cx6 eeprom = { .data = priv, .register_read = adm8211_eeprom_register_read, .register_write = adm8211_eeprom_register_write }; if (ADM8211_CSR_READ(CSR_TEST0) & ADM8211_CSR_TEST0_EPTYP) { /* 256 * 16-bit = 512 bytes */ eeprom.width = PCI_EEPROM_WIDTH_93C66; words = 256; } else { /* 64 * 16-bit = 128 bytes */ eeprom.width = PCI_EEPROM_WIDTH_93C46; words = 64; } priv->eeprom_len = words * 2; priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL); if (!priv->eeprom) return -ENOMEM; eeprom_93cx6_multiread(&eeprom, 0, (__le16 *)priv->eeprom, words); cr49 = le16_to_cpu(priv->eeprom->cr49); priv->rf_type = (cr49 >> 3) & 0x7; switch (priv->rf_type) { case ADM8211_TYPE_INTERSIL: case ADM8211_TYPE_RFMD: case ADM8211_TYPE_MARVEL: case ADM8211_TYPE_AIROHA: case ADM8211_TYPE_ADMTEK: break; default: if (priv->pdev->revision < ADM8211_REV_CA) priv->rf_type = ADM8211_TYPE_RFMD; else priv->rf_type = ADM8211_TYPE_AIROHA; printk(KERN_WARNING "%s (adm8211): Unknown RFtype %d\n", pci_name(priv->pdev), (cr49 >> 3) & 0x7); } priv->bbp_type = cr49 & 0x7; switch (priv->bbp_type) { case ADM8211_TYPE_INTERSIL: case ADM8211_TYPE_RFMD: case ADM8211_TYPE_MARVEL: case ADM8211_TYPE_AIROHA: case ADM8211_TYPE_ADMTEK: break; default: if (priv->pdev->revision < ADM8211_REV_CA) priv->bbp_type = ADM8211_TYPE_RFMD; else priv->bbp_type = ADM8211_TYPE_ADMTEK; printk(KERN_WARNING "%s (adm8211): Unknown BBPtype: %d\n", pci_name(priv->pdev), cr49 >> 3); } if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) { printk(KERN_WARNING "%s (adm8211): Invalid country code (%d)\n", pci_name(priv->pdev), priv->eeprom->country_code); chan_range = cranges[2]; } else chan_range = cranges[priv->eeprom->country_code]; printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n", pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max); BUILD_BUG_ON(sizeof(priv->channels) != sizeof(adm8211_channels)); memcpy(priv->channels, adm8211_channels, sizeof(priv->channels)); priv->band.channels = priv->channels; priv->band.n_channels = ARRAY_SIZE(adm8211_channels); priv->band.bitrates = adm8211_rates; priv->band.n_bitrates = ARRAY_SIZE(adm8211_rates); for (i = 1; i <= ARRAY_SIZE(adm8211_channels); i++) if (i < chan_range.min || i > chan_range.max) priv->channels[i - 1].flags |= IEEE80211_CHAN_DISABLED; switch (priv->eeprom->specific_bbptype) { case ADM8211_BBP_RFMD3000: case ADM8211_BBP_RFMD3002: case ADM8211_BBP_ADM8011: priv->specific_bbptype = priv->eeprom->specific_bbptype; break; default: if (priv->pdev->revision < ADM8211_REV_CA) priv->specific_bbptype = ADM8211_BBP_RFMD3000; else priv->specific_bbptype = ADM8211_BBP_ADM8011; printk(KERN_WARNING "%s (adm8211): Unknown specific BBP: %d\n", pci_name(priv->pdev), priv->eeprom->specific_bbptype); } switch (priv->eeprom->specific_rftype) { case ADM8211_RFMD2948: case ADM8211_RFMD2958: case ADM8211_RFMD2958_RF3000_CONTROL_POWER: case ADM8211_MAX2820: case ADM8211_AL2210L: priv->transceiver_type = priv->eeprom->specific_rftype; break; default: if (priv->pdev->revision == ADM8211_REV_BA) priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER; else if (priv->pdev->revision == ADM8211_REV_CA) priv->transceiver_type = ADM8211_AL2210L; else if (priv->pdev->revision == ADM8211_REV_AB) priv->transceiver_type = ADM8211_RFMD2948; printk(KERN_WARNING "%s (adm8211): Unknown transceiver: %d\n", pci_name(priv->pdev), priv->eeprom->specific_rftype); break; } printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d " "Transceiver=%d\n", pci_name(priv->pdev), priv->rf_type, priv->bbp_type, priv->specific_bbptype, priv->transceiver_type); return 0; } static inline void adm8211_write_sram(struct ieee80211_hw *dev, u32 addr, u32 data) { struct adm8211_priv *priv = dev->priv; ADM8211_CSR_WRITE(WEPCTL, addr | ADM8211_WEPCTL_TABLE_WR | (priv->pdev->revision < ADM8211_REV_BA ? 0 : ADM8211_WEPCTL_SEL_WEPTABLE )); ADM8211_CSR_READ(WEPCTL); msleep(1); ADM8211_CSR_WRITE(WESK, data); ADM8211_CSR_READ(WESK); msleep(1); } static void adm8211_write_sram_bytes(struct ieee80211_hw *dev, unsigned int addr, u8 *buf, unsigned int len) { struct adm8211_priv *priv = dev->priv; u32 reg = ADM8211_CSR_READ(WEPCTL); unsigned int i; if (priv->pdev->revision < ADM8211_REV_BA) { for (i = 0; i < len; i += 2) { u16 val = buf[i] | (buf[i + 1] << 8); adm8211_write_sram(dev, addr + i / 2, val); } } else { for (i = 0; i < len; i += 4) { u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) | (buf[i + 2] << 16) | (buf[i + 3] << 24); adm8211_write_sram(dev, addr + i / 4, val); } } ADM8211_CSR_WRITE(WEPCTL, reg); } static void adm8211_clear_sram(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; u32 reg = ADM8211_CSR_READ(WEPCTL); unsigned int addr; for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++) adm8211_write_sram(dev, addr, 0); ADM8211_CSR_WRITE(WEPCTL, reg); } static int adm8211_get_stats(struct ieee80211_hw *dev, struct ieee80211_low_level_stats *stats) { struct adm8211_priv *priv = dev->priv; memcpy(stats, &priv->stats, sizeof(*stats)); return 0; } static void adm8211_interrupt_tci(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; unsigned int dirty_tx; spin_lock(&priv->lock); for (dirty_tx = priv->dirty_tx; priv->cur_tx - dirty_tx; dirty_tx++) { unsigned int entry = dirty_tx % priv->tx_ring_size; u32 status = le32_to_cpu(priv->tx_ring[entry].status); struct ieee80211_tx_info *txi; struct adm8211_tx_ring_info *info; struct sk_buff *skb; if (status & TDES0_CONTROL_OWN || !(status & TDES0_CONTROL_DONE)) break; info = &priv->tx_buffers[entry]; skb = info->skb; txi = IEEE80211_SKB_CB(skb); /* TODO: check TDES0_STATUS_TUF and TDES0_STATUS_TRO */ pci_unmap_single(priv->pdev, info->mapping, info->skb->len, PCI_DMA_TODEVICE); ieee80211_tx_info_clear_status(txi); skb_pull(skb, sizeof(struct adm8211_tx_hdr)); memcpy(skb_push(skb, info->hdrlen), skb->cb, info->hdrlen); if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && !(status & TDES0_STATUS_ES)) txi->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(dev, skb); info->skb = NULL; } if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2) ieee80211_wake_queue(dev, 0); priv->dirty_tx = dirty_tx; spin_unlock(&priv->lock); } static void adm8211_interrupt_rci(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; unsigned int entry = priv->cur_rx % priv->rx_ring_size; u32 status; unsigned int pktlen; struct sk_buff *skb, *newskb; unsigned int limit = priv->rx_ring_size; u8 rssi, rate; while (!(priv->rx_ring[entry].status & cpu_to_le32(RDES0_STATUS_OWN))) { if (!limit--) break; status = le32_to_cpu(priv->rx_ring[entry].status); rate = (status & RDES0_STATUS_RXDR) >> 12; rssi = le32_to_cpu(priv->rx_ring[entry].length) & RDES1_STATUS_RSSI; pktlen = status & RDES0_STATUS_FL; if (pktlen > RX_PKT_SIZE) { if (net_ratelimit()) wiphy_debug(dev->wiphy, "frame too long (%d)\n", pktlen); pktlen = RX_PKT_SIZE; } if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) { skb = NULL; /* old buffer will be reused */ /* TODO: update RX error stats */ /* TODO: check RDES0_STATUS_CRC*E */ } else if (pktlen < RX_COPY_BREAK) { skb = dev_alloc_skb(pktlen); if (skb) { pci_dma_sync_single_for_cpu( priv->pdev, priv->rx_buffers[entry].mapping, pktlen, PCI_DMA_FROMDEVICE); memcpy(skb_put(skb, pktlen), skb_tail_pointer(priv->rx_buffers[entry].skb), pktlen); pci_dma_sync_single_for_device( priv->pdev, priv->rx_buffers[entry].mapping, RX_PKT_SIZE, PCI_DMA_FROMDEVICE); } } else { newskb = dev_alloc_skb(RX_PKT_SIZE); if (newskb) { skb = priv->rx_buffers[entry].skb; skb_put(skb, pktlen); pci_unmap_single( priv->pdev, priv->rx_buffers[entry].mapping, RX_PKT_SIZE, PCI_DMA_FROMDEVICE); priv->rx_buffers[entry].skb = newskb; priv->rx_buffers[entry].mapping = pci_map_single(priv->pdev, skb_tail_pointer(newskb), RX_PKT_SIZE, PCI_DMA_FROMDEVICE); } else { skb = NULL; /* TODO: update rx dropped stats */ } priv->rx_ring[entry].buffer1 = cpu_to_le32(priv->rx_buffers[entry].mapping); } priv->rx_ring[entry].status = cpu_to_le32(RDES0_STATUS_OWN | RDES0_STATUS_SQL); priv->rx_ring[entry].length = cpu_to_le32(RX_PKT_SIZE | (entry == priv->rx_ring_size - 1 ? RDES1_CONTROL_RER : 0)); if (skb) { struct ieee80211_rx_status rx_status = {0}; if (priv->pdev->revision < ADM8211_REV_CA) rx_status.signal = rssi; else rx_status.signal = 100 - rssi; rx_status.rate_idx = rate; rx_status.freq = adm8211_channels[priv->channel - 1].center_freq; rx_status.band = IEEE80211_BAND_2GHZ; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(dev, skb); } entry = (++priv->cur_rx) % priv->rx_ring_size; } /* TODO: check LPC and update stats? */ } static irqreturn_t adm8211_interrupt(int irq, void *dev_id) { #define ADM8211_INT(x) \ do { \ if (unlikely(stsr & ADM8211_STSR_ ## x)) \ wiphy_debug(dev->wiphy, "%s\n", #x); \ } while (0) struct ieee80211_hw *dev = dev_id; struct adm8211_priv *priv = dev->priv; u32 stsr = ADM8211_CSR_READ(STSR); ADM8211_CSR_WRITE(STSR, stsr); if (stsr == 0xffffffff) return IRQ_HANDLED; if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS))) return IRQ_HANDLED; if (stsr & ADM8211_STSR_RCI) adm8211_interrupt_rci(dev); if (stsr & ADM8211_STSR_TCI) adm8211_interrupt_tci(dev); ADM8211_INT(PCF); ADM8211_INT(BCNTC); ADM8211_INT(GPINT); ADM8211_INT(ATIMTC); ADM8211_INT(TSFTF); ADM8211_INT(TSCZ); ADM8211_INT(SQL); ADM8211_INT(WEPTD); ADM8211_INT(ATIME); ADM8211_INT(TEIS); ADM8211_INT(FBE); ADM8211_INT(REIS); ADM8211_INT(GPTT); ADM8211_INT(RPS); ADM8211_INT(RDU); ADM8211_INT(TUF); ADM8211_INT(TPS); return IRQ_HANDLED; #undef ADM8211_INT } #define WRITE_SYN(name,v_mask,v_shift,a_mask,a_shift,bits,prewrite,postwrite)\ static void adm8211_rf_write_syn_ ## name (struct ieee80211_hw *dev, \ u16 addr, u32 value) { \ struct adm8211_priv *priv = dev->priv; \ unsigned int i; \ u32 reg, bitbuf; \ \ value &= v_mask; \ addr &= a_mask; \ bitbuf = (value << v_shift) | (addr << a_shift); \ \ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1); \ ADM8211_CSR_READ(SYNRF); \ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0); \ ADM8211_CSR_READ(SYNRF); \ \ if (prewrite) { \ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0); \ ADM8211_CSR_READ(SYNRF); \ } \ \ for (i = 0; i <= bits; i++) { \ if (bitbuf & (1 << (bits - i))) \ reg = ADM8211_SYNRF_WRITE_SYNDATA_1; \ else \ reg = ADM8211_SYNRF_WRITE_SYNDATA_0; \ \ ADM8211_CSR_WRITE(SYNRF, reg); \ ADM8211_CSR_READ(SYNRF); \ \ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1); \ ADM8211_CSR_READ(SYNRF); \ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0); \ ADM8211_CSR_READ(SYNRF); \ } \ \ if (postwrite == 1) { \ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0); \ ADM8211_CSR_READ(SYNRF); \ } \ if (postwrite == 2) { \ ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_1); \ ADM8211_CSR_READ(SYNRF); \ } \ \ ADM8211_CSR_WRITE(SYNRF, 0); \ ADM8211_CSR_READ(SYNRF); \ } WRITE_SYN(max2820, 0x00FFF, 0, 0x0F, 12, 15, 1, 1) WRITE_SYN(al2210l, 0xFFFFF, 4, 0x0F, 0, 23, 1, 1) WRITE_SYN(rfmd2958, 0x3FFFF, 0, 0x1F, 18, 23, 0, 1) WRITE_SYN(rfmd2948, 0x0FFFF, 4, 0x0F, 0, 21, 0, 2) #undef WRITE_SYN static int adm8211_write_bbp(struct ieee80211_hw *dev, u8 addr, u8 data) { struct adm8211_priv *priv = dev->priv; unsigned int timeout; u32 reg; timeout = 10; while (timeout > 0) { reg = ADM8211_CSR_READ(BBPCTL); if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD))) break; timeout--; msleep(2); } if (timeout == 0) { wiphy_debug(dev->wiphy, "adm8211_write_bbp(%d,%d) failed prewrite (reg=0x%08x)\n", addr, data, reg); return -ETIMEDOUT; } switch (priv->bbp_type) { case ADM8211_TYPE_INTERSIL: reg = ADM8211_BBPCTL_MMISEL; /* three wire interface */ break; case ADM8211_TYPE_RFMD: reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | (0x01 << 18); break; case ADM8211_TYPE_ADMTEK: reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | (0x05 << 18); break; } reg |= ADM8211_BBPCTL_WR | (addr << 8) | data; ADM8211_CSR_WRITE(BBPCTL, reg); timeout = 10; while (timeout > 0) { reg = ADM8211_CSR_READ(BBPCTL); if (!(reg & ADM8211_BBPCTL_WR)) break; timeout--; msleep(2); } if (timeout == 0) { ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) & ~ADM8211_BBPCTL_WR); wiphy_debug(dev->wiphy, "adm8211_write_bbp(%d,%d) failed postwrite (reg=0x%08x)\n", addr, data, reg); return -ETIMEDOUT; } return 0; } static int adm8211_rf_set_channel(struct ieee80211_hw *dev, unsigned int chan) { static const u32 adm8211_rfmd2958_reg5[] = {0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340, 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7}; static const u32 adm8211_rfmd2958_reg6[] = {0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000, 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745}; struct adm8211_priv *priv = dev->priv; u8 ant_power = priv->ant_power > 0x3F ? priv->eeprom->antenna_power[chan - 1] : priv->ant_power; u8 tx_power = priv->tx_power > 0x3F ? priv->eeprom->tx_power[chan - 1] : priv->tx_power; u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ? priv->eeprom->lpf_cutoff[chan - 1] : priv->lpf_cutoff; u8 lnags_thresh = priv->lnags_threshold == 0xFF ? priv->eeprom->lnags_threshold[chan - 1] : priv->lnags_threshold; u32 reg; ADM8211_IDLE(); /* Program synthesizer to new channel */ switch (priv->transceiver_type) { case ADM8211_RFMD2958: case ADM8211_RFMD2958_RF3000_CONTROL_POWER: adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007); adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033); adm8211_rf_write_syn_rfmd2958(dev, 0x05, adm8211_rfmd2958_reg5[chan - 1]); adm8211_rf_write_syn_rfmd2958(dev, 0x06, adm8211_rfmd2958_reg6[chan - 1]); break; case ADM8211_RFMD2948: adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, SI4126_MAIN_XINDIV2); adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN, SI4126_POWERDOWN_PDIB | SI4126_POWERDOWN_PDRB); adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0); adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV, (chan == 14 ? 2110 : (2033 + (chan * 5)))); adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496); adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44); adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44); break; case ADM8211_MAX2820: adm8211_rf_write_syn_max2820(dev, 0x3, (chan == 14 ? 0x054 : (0x7 + (chan * 5)))); break; case ADM8211_AL2210L: adm8211_rf_write_syn_al2210l(dev, 0x0, (chan == 14 ? 0x229B4 : (0x22967 + (chan * 5)))); break; default: wiphy_debug(dev->wiphy, "unsupported transceiver type %d\n", priv->transceiver_type); break; } /* write BBP regs */ if (priv->bbp_type == ADM8211_TYPE_RFMD) { /* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */ /* TODO: remove if SMC 2635W doesn't need this */ if (priv->transceiver_type == ADM8211_RFMD2948) { reg = ADM8211_CSR_READ(GPIO); reg &= 0xfffc0000; reg |= ADM8211_CSR_GPIO_EN0; if (chan != 14) reg |= ADM8211_CSR_GPIO_O0; ADM8211_CSR_WRITE(GPIO, reg); } if (priv->transceiver_type == ADM8211_RFMD2958) { /* set PCNT2 */ adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100); /* set PCNT1 P_DESIRED/MID_BIAS */ reg = le16_to_cpu(priv->eeprom->cr49); reg >>= 13; reg <<= 15; reg |= ant_power << 9; adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg); /* set TXRX TX_GAIN */ adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 | (priv->pdev->revision < ADM8211_REV_CA ? tx_power : 0)); } else { reg = ADM8211_CSR_READ(PLCPHD); reg &= 0xff00ffff; reg |= tx_power << 18; ADM8211_CSR_WRITE(PLCPHD, reg); } ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); ADM8211_CSR_READ(SYNRF); msleep(30); /* RF3000 BBP */ if (priv->transceiver_type != ADM8211_RFMD2958) adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, tx_power<<2); adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff); adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh); adm8211_write_bbp(dev, 0x1c, priv->pdev->revision == ADM8211_REV_BA ? priv->eeprom->cr28 : 0); adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); ADM8211_CSR_WRITE(SYNRF, 0); /* Nothing to do for ADMtek BBP */ } else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) wiphy_debug(dev->wiphy, "unsupported BBP type %d\n", priv->bbp_type); ADM8211_RESTORE(); /* update current channel for adhoc (and maybe AP mode) */ reg = ADM8211_CSR_READ(CAP0); reg &= ~0xF; reg |= chan; ADM8211_CSR_WRITE(CAP0, reg); return 0; } static void adm8211_update_mode(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; ADM8211_IDLE(); priv->soft_rx_crc = 0; switch (priv->mode) { case NL80211_IFTYPE_STATION: priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA); priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR; break; case NL80211_IFTYPE_ADHOC: priv->nar &= ~ADM8211_NAR_PR; priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR; /* don't trust the error bits on rev 0x20 and up in adhoc */ if (priv->pdev->revision >= ADM8211_REV_BA) priv->soft_rx_crc = 1; break; case NL80211_IFTYPE_MONITOR: priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST); priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR; break; } ADM8211_RESTORE(); } static void adm8211_hw_init_syn(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; switch (priv->transceiver_type) { case ADM8211_RFMD2958: case ADM8211_RFMD2958_RF3000_CONTROL_POWER: /* comments taken from ADMtek vendor driver */ /* Reset RF2958 after power on */ adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000); /* Initialize RF VCO Core Bias to maximum */ adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F); /* Initialize IF PLL */ adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03); /* Initialize IF PLL Coarse Tuning */ adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F); /* Initialize RF PLL */ adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403); /* Initialize RF PLL Coarse Tuning */ adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F); /* Initialize TX gain and filter BW (R9) */ adm8211_rf_write_syn_rfmd2958(dev, 0x09, (priv->transceiver_type == ADM8211_RFMD2958 ? 0x10050 : 0x00050)); /* Initialize CAL register */ adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8); break; case ADM8211_MAX2820: adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E); adm8211_rf_write_syn_max2820(dev, 0x2, 0x001); adm8211_rf_write_syn_max2820(dev, 0x3, 0x054); adm8211_rf_write_syn_max2820(dev, 0x4, 0x310); adm8211_rf_write_syn_max2820(dev, 0x5, 0x000); break; case ADM8211_AL2210L: adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C); adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB); adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F); adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9); adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280); adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641); adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130); adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000); adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F); adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C); adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000); adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000); break; case ADM8211_RFMD2948: default: break; } } static int adm8211_hw_init_bbp(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; u32 reg; /* write addresses */ if (priv->bbp_type == ADM8211_TYPE_INTERSIL) { ADM8211_CSR_WRITE(MMIWA, 0x100E0C0A); ADM8211_CSR_WRITE(MMIRD0, 0x00007C7E); ADM8211_CSR_WRITE(MMIRD1, 0x00100000); } else if (priv->bbp_type == ADM8211_TYPE_RFMD || priv->bbp_type == ADM8211_TYPE_ADMTEK) { /* check specific BBP type */ switch (priv->specific_bbptype) { case ADM8211_BBP_RFMD3000: case ADM8211_BBP_RFMD3002: ADM8211_CSR_WRITE(MMIWA, 0x00009101); ADM8211_CSR_WRITE(MMIRD0, 0x00000301); break; case ADM8211_BBP_ADM8011: ADM8211_CSR_WRITE(MMIWA, 0x00008903); ADM8211_CSR_WRITE(MMIRD0, 0x00001716); reg = ADM8211_CSR_READ(BBPCTL); reg &= ~ADM8211_BBPCTL_TYPE; reg |= 0x5 << 18; ADM8211_CSR_WRITE(BBPCTL, reg); break; } switch (priv->pdev->revision) { case ADM8211_REV_CA: if (priv->transceiver_type == ADM8211_RFMD2958 || priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || priv->transceiver_type == ADM8211_RFMD2948) ADM8211_CSR_WRITE(SYNCTL, 0x1 << 22); else if (priv->transceiver_type == ADM8211_MAX2820 || priv->transceiver_type == ADM8211_AL2210L) ADM8211_CSR_WRITE(SYNCTL, 0x3 << 22); break; case ADM8211_REV_BA: reg = ADM8211_CSR_READ(MMIRD1); reg &= 0x0000FFFF; reg |= 0x7e100000; ADM8211_CSR_WRITE(MMIRD1, reg); break; case ADM8211_REV_AB: case ADM8211_REV_AF: default: ADM8211_CSR_WRITE(MMIRD1, 0x7e100000); break; } /* For RFMD */ ADM8211_CSR_WRITE(MACTEST, 0x800); } adm8211_hw_init_syn(dev); /* Set RF Power control IF pin to PE1+PHYRST# */ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); ADM8211_CSR_READ(SYNRF); msleep(20); /* write BBP regs */ if (priv->bbp_type == ADM8211_TYPE_RFMD) { /* RF3000 BBP */ /* another set: * 11: c8 * 14: 14 * 15: 50 (chan 1..13; chan 14: d0) * 1c: 00 * 1d: 84 */ adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80); /* antenna selection: diversity */ adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74); adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38); adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40); if (priv->eeprom->major_version < 2) { adm8211_write_bbp(dev, 0x1c, 0x00); adm8211_write_bbp(dev, 0x1d, 0x80); } else { if (priv->pdev->revision == ADM8211_REV_BA) adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28); else adm8211_write_bbp(dev, 0x1c, 0x00); adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); } } else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) { /* reset baseband */ adm8211_write_bbp(dev, 0x00, 0xFF); /* antenna selection: diversity */ adm8211_write_bbp(dev, 0x07, 0x0A); /* TODO: find documentation for this */ switch (priv->transceiver_type) { case ADM8211_RFMD2958: case ADM8211_RFMD2958_RF3000_CONTROL_POWER: adm8211_write_bbp(dev, 0x00, 0x00); adm8211_write_bbp(dev, 0x01, 0x00); adm8211_write_bbp(dev, 0x02, 0x00); adm8211_write_bbp(dev, 0x03, 0x00); adm8211_write_bbp(dev, 0x06, 0x0f); adm8211_write_bbp(dev, 0x09, 0x00); adm8211_write_bbp(dev, 0x0a, 0x00); adm8211_write_bbp(dev, 0x0b, 0x00); adm8211_write_bbp(dev, 0x0c, 0x00); adm8211_write_bbp(dev, 0x0f, 0xAA); adm8211_write_bbp(dev, 0x10, 0x8c); adm8211_write_bbp(dev, 0x11, 0x43); adm8211_write_bbp(dev, 0x18, 0x40); adm8211_write_bbp(dev, 0x20, 0x23); adm8211_write_bbp(dev, 0x21, 0x02); adm8211_write_bbp(dev, 0x22, 0x28); adm8211_write_bbp(dev, 0x23, 0x30); adm8211_write_bbp(dev, 0x24, 0x2d); adm8211_write_bbp(dev, 0x28, 0x35); adm8211_write_bbp(dev, 0x2a, 0x8c); adm8211_write_bbp(dev, 0x2b, 0x81); adm8211_write_bbp(dev, 0x2c, 0x44); adm8211_write_bbp(dev, 0x2d, 0x0A); adm8211_write_bbp(dev, 0x29, 0x40); adm8211_write_bbp(dev, 0x60, 0x08); adm8211_write_bbp(dev, 0x64, 0x01); break; case ADM8211_MAX2820: adm8211_write_bbp(dev, 0x00, 0x00); adm8211_write_bbp(dev, 0x01, 0x00); adm8211_write_bbp(dev, 0x02, 0x00); adm8211_write_bbp(dev, 0x03, 0x00); adm8211_write_bbp(dev, 0x06, 0x0f); adm8211_write_bbp(dev, 0x09, 0x05); adm8211_write_bbp(dev, 0x0a, 0x02); adm8211_write_bbp(dev, 0x0b, 0x00); adm8211_write_bbp(dev, 0x0c, 0x0f); adm8211_write_bbp(dev, 0x0f, 0x55); adm8211_write_bbp(dev, 0x10, 0x8d); adm8211_write_bbp(dev, 0x11, 0x43); adm8211_write_bbp(dev, 0x18, 0x4a); adm8211_write_bbp(dev, 0x20, 0x20); adm8211_write_bbp(dev, 0x21, 0x02); adm8211_write_bbp(dev, 0x22, 0x23); adm8211_write_bbp(dev, 0x23, 0x30); adm8211_write_bbp(dev, 0x24, 0x2d); adm8211_write_bbp(dev, 0x2a, 0x8c); adm8211_write_bbp(dev, 0x2b, 0x81); adm8211_write_bbp(dev, 0x2c, 0x44); adm8211_write_bbp(dev, 0x29, 0x4a); adm8211_write_bbp(dev, 0x60, 0x2b); adm8211_write_bbp(dev, 0x64, 0x01); break; case ADM8211_AL2210L: adm8211_write_bbp(dev, 0x00, 0x00); adm8211_write_bbp(dev, 0x01, 0x00); adm8211_write_bbp(dev, 0x02, 0x00); adm8211_write_bbp(dev, 0x03, 0x00); adm8211_write_bbp(dev, 0x06, 0x0f); adm8211_write_bbp(dev, 0x07, 0x05); adm8211_write_bbp(dev, 0x08, 0x03); adm8211_write_bbp(dev, 0x09, 0x00); adm8211_write_bbp(dev, 0x0a, 0x00); adm8211_write_bbp(dev, 0x0b, 0x00); adm8211_write_bbp(dev, 0x0c, 0x10); adm8211_write_bbp(dev, 0x0f, 0x55); adm8211_write_bbp(dev, 0x10, 0x8d); adm8211_write_bbp(dev, 0x11, 0x43); adm8211_write_bbp(dev, 0x18, 0x4a); adm8211_write_bbp(dev, 0x20, 0x20); adm8211_write_bbp(dev, 0x21, 0x02); adm8211_write_bbp(dev, 0x22, 0x23); adm8211_write_bbp(dev, 0x23, 0x30); adm8211_write_bbp(dev, 0x24, 0x2d); adm8211_write_bbp(dev, 0x2a, 0xaa); adm8211_write_bbp(dev, 0x2b, 0x81); adm8211_write_bbp(dev, 0x2c, 0x44); adm8211_write_bbp(dev, 0x29, 0xfa); adm8211_write_bbp(dev, 0x60, 0x2d); adm8211_write_bbp(dev, 0x64, 0x01); break; case ADM8211_RFMD2948: break; default: wiphy_debug(dev->wiphy, "unsupported transceiver %d\n", priv->transceiver_type); break; } } else wiphy_debug(dev->wiphy, "unsupported BBP %d\n", priv->bbp_type); ADM8211_CSR_WRITE(SYNRF, 0); /* Set RF CAL control source to MAC control */ reg = ADM8211_CSR_READ(SYNCTL); reg |= ADM8211_SYNCTL_SELCAL; ADM8211_CSR_WRITE(SYNCTL, reg); return 0; } /* configures hw beacons/probe responses */ static int adm8211_set_rate(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; u32 reg; int i = 0; u8 rate_buf[12] = {0}; /* write supported rates */ if (priv->pdev->revision != ADM8211_REV_BA) { rate_buf[0] = ARRAY_SIZE(adm8211_rates); for (i = 0; i < ARRAY_SIZE(adm8211_rates); i++) rate_buf[i + 1] = (adm8211_rates[i].bitrate / 5) | 0x80; } else { /* workaround for rev BA specific bug */ rate_buf[0] = 0x04; rate_buf[1] = 0x82; rate_buf[2] = 0x04; rate_buf[3] = 0x0b; rate_buf[4] = 0x16; } adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, ARRAY_SIZE(adm8211_rates) + 1); reg = ADM8211_CSR_READ(PLCPHD) & 0x00FFFFFF; /* keep bits 0-23 */ reg |= 1 << 15; /* short preamble */ reg |= 110 << 24; ADM8211_CSR_WRITE(PLCPHD, reg); /* MTMLT = 512 TU (max TX MSDU lifetime) * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate) * SRTYLIM = 224 (short retry limit, TX header value is default) */ ADM8211_CSR_WRITE(TXLMT, (512 << 16) | (110 << 8) | (224 << 0)); return 0; } static void adm8211_hw_init(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; u32 reg; u8 cline; reg = ADM8211_CSR_READ(PAR); reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME; reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL); if (!pci_set_mwi(priv->pdev)) { reg |= 0x1 << 24; pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cline); switch (cline) { case 0x8: reg |= (0x1 << 14); break; case 0x16: reg |= (0x2 << 14); break; case 0x32: reg |= (0x3 << 14); break; default: reg |= (0x0 << 14); break; } } ADM8211_CSR_WRITE(PAR, reg); reg = ADM8211_CSR_READ(CSR_TEST1); reg &= ~(0xF << 28); reg |= (1 << 28) | (1 << 31); ADM8211_CSR_WRITE(CSR_TEST1, reg); /* lose link after 4 lost beacons */ reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE; ADM8211_CSR_WRITE(WCSR, reg); /* Disable APM, enable receive FIFO threshold, and set drain receive * threshold to store-and-forward */ reg = ADM8211_CSR_READ(CMDR); reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT); reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF; ADM8211_CSR_WRITE(CMDR, reg); adm8211_set_rate(dev); /* 4-bit values: * PWR1UP = 8 * 2 ms * PWR0PAPE = 8 us or 5 us * PWR1PAPE = 1 us or 3 us * PWR0TRSW = 5 us * PWR1TRSW = 12 us * PWR0PE2 = 13 us * PWR1PE2 = 1 us * PWR0TXPE = 8 or 6 */ if (priv->pdev->revision < ADM8211_REV_CA) ADM8211_CSR_WRITE(TOFS2, 0x8815cd18); else ADM8211_CSR_WRITE(TOFS2, 0x8535cd16); /* Enable store and forward for transmit */ priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB; ADM8211_CSR_WRITE(NAR, priv->nar); /* Reset RF */ ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_RADIO); ADM8211_CSR_READ(SYNRF); msleep(10); ADM8211_CSR_WRITE(SYNRF, 0); ADM8211_CSR_READ(SYNRF); msleep(5); /* Set CFP Max Duration to 0x10 TU */ reg = ADM8211_CSR_READ(CFPP); reg &= ~(0xffff << 8); reg |= 0x0010 << 8; ADM8211_CSR_WRITE(CFPP, reg); /* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us * TUCNT = 0x3ff - Tu counter 1024 us */ ADM8211_CSR_WRITE(TOFS0, (0x16 << 24) | 0x3ff); /* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us), * DIFS=50 us, EIFS=100 us */ if (priv->pdev->revision < ADM8211_REV_CA) ADM8211_CSR_WRITE(IFST, (20 << 23) | (110 << 15) | (50 << 9) | 100); else ADM8211_CSR_WRITE(IFST, (20 << 23) | (24 << 15) | (50 << 9) | 100); /* PCNT = 1 (MAC idle time awake/sleep, unit S) * RMRD = 2346 * 8 + 1 us (max RX duration) */ ADM8211_CSR_WRITE(RMD, (1 << 16) | 18769); /* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */ ADM8211_CSR_WRITE(RSPT, 0xffffff00); /* Initialize BBP (and SYN) */ adm8211_hw_init_bbp(dev); /* make sure interrupts are off */ ADM8211_CSR_WRITE(IER, 0); /* ACK interrupts */ ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR)); /* Setup WEP (turns it off for now) */ reg = ADM8211_CSR_READ(MACTEST); reg &= ~(7 << 20); ADM8211_CSR_WRITE(MACTEST, reg); reg = ADM8211_CSR_READ(WEPCTL); reg &= ~ADM8211_WEPCTL_WEPENABLE; reg |= ADM8211_WEPCTL_WEPRXBYP; ADM8211_CSR_WRITE(WEPCTL, reg); /* Clear the missed-packet counter. */ ADM8211_CSR_READ(LPC); } static int adm8211_hw_reset(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; u32 reg, tmp; int timeout = 100; /* Power-on issue */ /* TODO: check if this is necessary */ ADM8211_CSR_WRITE(FRCTL, 0); /* Reset the chip */ tmp = ADM8211_CSR_READ(PAR); ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR); while ((ADM8211_CSR_READ(PAR) & ADM8211_PAR_SWR) && timeout--) msleep(50); if (timeout <= 0) return -ETIMEDOUT; ADM8211_CSR_WRITE(PAR, tmp); if (priv->pdev->revision == ADM8211_REV_BA && (priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || priv->transceiver_type == ADM8211_RFMD2958)) { reg = ADM8211_CSR_READ(CSR_TEST1); reg |= (1 << 4) | (1 << 5); ADM8211_CSR_WRITE(CSR_TEST1, reg); } else if (priv->pdev->revision == ADM8211_REV_CA) { reg = ADM8211_CSR_READ(CSR_TEST1); reg &= ~((1 << 4) | (1 << 5)); ADM8211_CSR_WRITE(CSR_TEST1, reg); } ADM8211_CSR_WRITE(FRCTL, 0); reg = ADM8211_CSR_READ(CSR_TEST0); reg |= ADM8211_CSR_TEST0_EPRLD; /* EEPROM Recall */ ADM8211_CSR_WRITE(CSR_TEST0, reg); adm8211_clear_sram(dev); return 0; } static u64 adm8211_get_tsft(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct adm8211_priv *priv = dev->priv; u32 tsftl; u64 tsft; tsftl = ADM8211_CSR_READ(TSFTL); tsft = ADM8211_CSR_READ(TSFTH); tsft <<= 32; tsft |= tsftl; return tsft; } static void adm8211_set_interval(struct ieee80211_hw *dev, unsigned short bi, unsigned short li) { struct adm8211_priv *priv = dev->priv; u32 reg; /* BP (beacon interval) = data->beacon_interval * LI (listen interval) = data->listen_interval (in beacon intervals) */ reg = (bi << 16) | li; ADM8211_CSR_WRITE(BPLI, reg); } static void adm8211_set_bssid(struct ieee80211_hw *dev, const u8 *bssid) { struct adm8211_priv *priv = dev->priv; u32 reg; ADM8211_CSR_WRITE(BSSID0, le32_to_cpu(*(__le32 *)bssid)); reg = ADM8211_CSR_READ(ABDA1); reg &= 0x0000ffff; reg |= (bssid[4] << 16) | (bssid[5] << 24); ADM8211_CSR_WRITE(ABDA1, reg); } static int adm8211_config(struct ieee80211_hw *dev, u32 changed) { struct adm8211_priv *priv = dev->priv; struct ieee80211_conf *conf = &dev->conf; int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); if (channel != priv->channel) { priv->channel = channel; adm8211_rf_set_channel(dev, priv->channel); } return 0; } static void adm8211_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf, u32 changes) { struct adm8211_priv *priv = dev->priv; if (!(changes & BSS_CHANGED_BSSID)) return; if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { adm8211_set_bssid(dev, conf->bssid); memcpy(priv->bssid, conf->bssid, ETH_ALEN); } } static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *ha) #endif { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) unsigned int bit_nr; struct netdev_hw_addr *ha; #else unsigned int bit_nr, i; #endif u32 mc_filter[2]; mc_filter[1] = mc_filter[0] = 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; #else for (i = 0; i < mc_count; i++) { if (!ha) break; bit_nr = ether_crc(ETH_ALEN, ha->dmi_addr) >> 26; #endif bit_nr &= 0x3F; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) ha = ha->next; #endif } return mc_filter[0] | ((u64)(mc_filter[1]) << 32); } static void adm8211_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; struct adm8211_priv *priv = dev->priv; unsigned int new_flags; u32 mc_filter[2]; mc_filter[0] = multicast; mc_filter[1] = multicast >> 32; new_flags = 0; if (*total_flags & FIF_PROMISC_IN_BSS) { new_flags |= FIF_PROMISC_IN_BSS; priv->nar |= ADM8211_NAR_PR; priv->nar &= ~ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; } else if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) { new_flags |= FIF_ALLMULTI; priv->nar &= ~ADM8211_NAR_PR; priv->nar |= ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; } else { priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); } ADM8211_IDLE_RX(); ADM8211_CSR_WRITE(MAR0, mc_filter[0]); ADM8211_CSR_WRITE(MAR1, mc_filter[1]); ADM8211_CSR_READ(NAR); if (priv->nar & ADM8211_NAR_PR) dev->flags |= IEEE80211_HW_RX_INCLUDES_FCS; else dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS; if (*total_flags & FIF_BCN_PRBRESP_PROMISC) adm8211_set_bssid(dev, bcast); else adm8211_set_bssid(dev, priv->bssid); ADM8211_RESTORE(); *total_flags = new_flags; } static int adm8211_add_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct adm8211_priv *priv = dev->priv; if (priv->mode != NL80211_IFTYPE_MONITOR) return -EOPNOTSUPP; switch (vif->type) { case NL80211_IFTYPE_STATION: priv->mode = vif->type; break; default: return -EOPNOTSUPP; } ADM8211_IDLE(); ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)vif->addr)); ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(vif->addr + 4))); adm8211_update_mode(dev); ADM8211_RESTORE(); return 0; } static void adm8211_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct adm8211_priv *priv = dev->priv; priv->mode = NL80211_IFTYPE_MONITOR; } static int adm8211_init_rings(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; struct adm8211_desc *desc = NULL; struct adm8211_rx_ring_info *rx_info; struct adm8211_tx_ring_info *tx_info; unsigned int i; for (i = 0; i < priv->rx_ring_size; i++) { desc = &priv->rx_ring[i]; desc->status = 0; desc->length = cpu_to_le32(RX_PKT_SIZE); priv->rx_buffers[i].skb = NULL; } /* Mark the end of RX ring; hw returns to base address after this * descriptor */ desc->length |= cpu_to_le32(RDES1_CONTROL_RER); for (i = 0; i < priv->rx_ring_size; i++) { desc = &priv->rx_ring[i]; rx_info = &priv->rx_buffers[i]; rx_info->skb = dev_alloc_skb(RX_PKT_SIZE); if (rx_info->skb == NULL) break; rx_info->mapping = pci_map_single(priv->pdev, skb_tail_pointer(rx_info->skb), RX_PKT_SIZE, PCI_DMA_FROMDEVICE); desc->buffer1 = cpu_to_le32(rx_info->mapping); desc->status = cpu_to_le32(RDES0_STATUS_OWN | RDES0_STATUS_SQL); } /* Setup TX ring. TX buffers descriptors will be filled in as needed */ for (i = 0; i < priv->tx_ring_size; i++) { desc = &priv->tx_ring[i]; tx_info = &priv->tx_buffers[i]; tx_info->skb = NULL; tx_info->mapping = 0; desc->status = 0; } desc->length = cpu_to_le32(TDES1_CONTROL_TER); priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0; ADM8211_CSR_WRITE(RDB, priv->rx_ring_dma); ADM8211_CSR_WRITE(TDBD, priv->tx_ring_dma); return 0; } static void adm8211_free_rings(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; unsigned int i; for (i = 0; i < priv->rx_ring_size; i++) { if (!priv->rx_buffers[i].skb) continue; pci_unmap_single( priv->pdev, priv->rx_buffers[i].mapping, RX_PKT_SIZE, PCI_DMA_FROMDEVICE); dev_kfree_skb(priv->rx_buffers[i].skb); } for (i = 0; i < priv->tx_ring_size; i++) { if (!priv->tx_buffers[i].skb) continue; pci_unmap_single(priv->pdev, priv->tx_buffers[i].mapping, priv->tx_buffers[i].skb->len, PCI_DMA_TODEVICE); dev_kfree_skb(priv->tx_buffers[i].skb); } } static int adm8211_start(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; int retval; /* Power up MAC and RF chips */ retval = adm8211_hw_reset(dev); if (retval) { wiphy_err(dev->wiphy, "hardware reset failed\n"); goto fail; } retval = adm8211_init_rings(dev); if (retval) { wiphy_err(dev->wiphy, "failed to initialize rings\n"); goto fail; } /* Init hardware */ adm8211_hw_init(dev); adm8211_rf_set_channel(dev, priv->channel); retval = request_irq(priv->pdev->irq, adm8211_interrupt, IRQF_SHARED, "adm8211", dev); if (retval) { wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); goto fail; } ADM8211_CSR_WRITE(IER, ADM8211_IER_NIE | ADM8211_IER_AIE | ADM8211_IER_RCIE | ADM8211_IER_TCIE | ADM8211_IER_TDUIE | ADM8211_IER_GPTIE); priv->mode = NL80211_IFTYPE_MONITOR; adm8211_update_mode(dev); ADM8211_CSR_WRITE(RDR, 0); adm8211_set_interval(dev, 100, 10); return 0; fail: return retval; } static void adm8211_stop(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; priv->mode = NL80211_IFTYPE_UNSPECIFIED; priv->nar = 0; ADM8211_CSR_WRITE(NAR, 0); ADM8211_CSR_WRITE(IER, 0); ADM8211_CSR_READ(NAR); free_irq(priv->pdev->irq, dev); adm8211_free_rings(dev); } static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len, int plcp_signal, int short_preamble) { /* Alternative calculation from NetBSD: */ /* IEEE 802.11b durations for DSSS PHY in microseconds */ #define IEEE80211_DUR_DS_LONG_PREAMBLE 144 #define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 #define IEEE80211_DUR_DS_FAST_PLCPHDR 24 #define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 #define IEEE80211_DUR_DS_SLOW_ACK 112 #define IEEE80211_DUR_DS_FAST_ACK 56 #define IEEE80211_DUR_DS_SLOW_CTS 112 #define IEEE80211_DUR_DS_FAST_CTS 56 #define IEEE80211_DUR_DS_SLOT 20 #define IEEE80211_DUR_DS_SIFS 10 int remainder; *dur = (80 * (24 + payload_len) + plcp_signal - 1) / plcp_signal; if (plcp_signal <= PLCP_SIGNAL_2M) /* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */ *dur += 3 * (IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SHORT_PREAMBLE + IEEE80211_DUR_DS_FAST_PLCPHDR) + IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK; else /* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */ *dur += 3 * (IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SHORT_PREAMBLE + IEEE80211_DUR_DS_FAST_PLCPHDR) + IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK; /* lengthen duration if long preamble */ if (!short_preamble) *dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE - IEEE80211_DUR_DS_SHORT_PREAMBLE) + 3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR - IEEE80211_DUR_DS_FAST_PLCPHDR); *plcp = (80 * len) / plcp_signal; remainder = (80 * len) % plcp_signal; if (plcp_signal == PLCP_SIGNAL_11M && remainder <= 30 && remainder > 0) *plcp = (*plcp | 0x8000) + 1; else if (remainder) (*plcp)++; } /* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */ static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, u16 plcp_signal, size_t hdrlen) { struct adm8211_priv *priv = dev->priv; unsigned long flags; dma_addr_t mapping; unsigned int entry; u32 flag; mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); spin_lock_irqsave(&priv->lock, flags); if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2) flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS; else flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS; if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size - 2) ieee80211_stop_queue(dev, 0); entry = priv->cur_tx % priv->tx_ring_size; priv->tx_buffers[entry].skb = skb; priv->tx_buffers[entry].mapping = mapping; priv->tx_buffers[entry].hdrlen = hdrlen; priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping); if (entry == priv->tx_ring_size - 1) flag |= TDES1_CONTROL_TER; priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len); /* Set TX rate (SIGNAL field in PLCP PPDU format) */ flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */; priv->tx_ring[entry].status = cpu_to_le32(flag); priv->cur_tx++; spin_unlock_irqrestore(&priv->lock, flags); /* Trigger transmit poll */ ADM8211_CSR_WRITE(TDR, 0); } /* Put adm8211_tx_hdr on skb and transmit */ static void adm8211_tx(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct adm8211_tx_hdr *txhdr; size_t payload_len, hdrlen; int plcp, dur, len, plcp_signal, short_preamble; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, info); u8 rc_flags; rc_flags = info->control.rates[0].flags; short_preamble = !!(rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE); plcp_signal = txrate->bitrate; hdr = (struct ieee80211_hdr *)skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); memcpy(skb->cb, skb->data, hdrlen); hdr = (struct ieee80211_hdr *)skb->cb; skb_pull(skb, hdrlen); payload_len = skb->len; txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr)); memset(txhdr, 0, sizeof(*txhdr)); memcpy(txhdr->da, ieee80211_get_DA(hdr), ETH_ALEN); txhdr->signal = plcp_signal; txhdr->frame_body_size = cpu_to_le16(payload_len); txhdr->frame_control = hdr->frame_control; len = hdrlen + payload_len + FCS_LEN; txhdr->frag = cpu_to_le16(0x0FFF); adm8211_calc_durations(&dur, &plcp, payload_len, len, plcp_signal, short_preamble); txhdr->plcp_frag_head_len = cpu_to_le16(plcp); txhdr->plcp_frag_tail_len = cpu_to_le16(plcp); txhdr->dur_frag_head = cpu_to_le16(dur); txhdr->dur_frag_tail = cpu_to_le16(dur); txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER); if (short_preamble) txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE); if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS); txhdr->retry_limit = info->control.rates[0].count; adm8211_tx_raw(dev, skb, plcp_signal, hdrlen); } static int adm8211_alloc_rings(struct ieee80211_hw *dev) { struct adm8211_priv *priv = dev->priv; unsigned int ring_size; priv->rx_buffers = kmalloc(sizeof(*priv->rx_buffers) * priv->rx_ring_size + sizeof(*priv->tx_buffers) * priv->tx_ring_size, GFP_KERNEL); if (!priv->rx_buffers) return -ENOMEM; priv->tx_buffers = (void *)priv->rx_buffers + sizeof(*priv->rx_buffers) * priv->rx_ring_size; /* Allocate TX/RX descriptors */ ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->tx_ring_size; priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size, &priv->rx_ring_dma); if (!priv->rx_ring) { kfree(priv->rx_buffers); priv->rx_buffers = NULL; priv->tx_buffers = NULL; return -ENOMEM; } priv->tx_ring = priv->rx_ring + priv->rx_ring_size; priv->tx_ring_dma = priv->rx_ring_dma + sizeof(struct adm8211_desc) * priv->rx_ring_size; return 0; } static const struct ieee80211_ops adm8211_ops = { .tx = adm8211_tx, .start = adm8211_start, .stop = adm8211_stop, .add_interface = adm8211_add_interface, .remove_interface = adm8211_remove_interface, .config = adm8211_config, .bss_info_changed = adm8211_bss_info_changed, .prepare_multicast = adm8211_prepare_multicast, .configure_filter = adm8211_configure_filter, .get_stats = adm8211_get_stats, .get_tsf = adm8211_get_tsft }; static int __devinit adm8211_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ieee80211_hw *dev; struct adm8211_priv *priv; unsigned long mem_addr, mem_len; unsigned int io_addr, io_len; int err; u32 reg; u8 perm_addr[ETH_ALEN]; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", pci_name(pdev)); return err; } io_addr = pci_resource_start(pdev, 0); io_len = pci_resource_len(pdev, 0); mem_addr = pci_resource_start(pdev, 1); mem_len = pci_resource_len(pdev, 1); if (io_len < 256 || mem_len < 1024) { printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", pci_name(pdev)); goto err_disable_pdev; } /* check signature */ pci_read_config_dword(pdev, 0x80 /* CR32 */, ®); if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) { printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", pci_name(pdev), reg); goto err_disable_pdev; } err = pci_request_regions(pdev, "adm8211"); if (err) { printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", pci_name(pdev)); return err; /* someone else grabbed it? don't disable it */ } if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", pci_name(pdev)); goto err_free_reg; } pci_set_master(pdev); dev = ieee80211_alloc_hw(sizeof(*priv), &adm8211_ops); if (!dev) { printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", pci_name(pdev)); err = -ENOMEM; goto err_free_reg; } priv = dev->priv; priv->pdev = pdev; spin_lock_init(&priv->lock); SET_IEEE80211_DEV(dev, &pdev->dev); pci_set_drvdata(pdev, dev); priv->map = pci_iomap(pdev, 1, mem_len); if (!priv->map) priv->map = pci_iomap(pdev, 0, io_len); if (!priv->map) { printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", pci_name(pdev)); goto err_free_dev; } priv->rx_ring_size = rx_ring_size; priv->tx_ring_size = tx_ring_size; if (adm8211_alloc_rings(dev)) { printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", pci_name(pdev)); goto err_iounmap; } *(__le32 *)perm_addr = cpu_to_le32(ADM8211_CSR_READ(PAR0)); *(__le16 *)&perm_addr[4] = cpu_to_le16(ADM8211_CSR_READ(PAR1) & 0xFFFF); if (!is_valid_ether_addr(perm_addr)) { printk(KERN_WARNING "%s (adm8211): Invalid hwaddr in EEPROM!\n", pci_name(pdev)); eth_random_addr(perm_addr); } SET_IEEE80211_PERM_ADDR(dev, perm_addr); dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); /* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS in promisc mode */ dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); dev->channel_change_time = 1000; dev->max_signal = 100; /* FIXME: find better value */ dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ priv->retry_limit = 3; priv->ant_power = 0x40; priv->tx_power = 0x40; priv->lpf_cutoff = 0xFF; priv->lnags_threshold = 0xFF; priv->mode = NL80211_IFTYPE_UNSPECIFIED; /* Power-on issue. EEPROM won't read correctly without */ if (pdev->revision >= ADM8211_REV_BA) { ADM8211_CSR_WRITE(FRCTL, 0); ADM8211_CSR_READ(FRCTL); ADM8211_CSR_WRITE(FRCTL, 1); ADM8211_CSR_READ(FRCTL); msleep(100); } err = adm8211_read_eeprom(dev); if (err) { printk(KERN_ERR "%s (adm8211): Can't alloc eeprom buffer\n", pci_name(pdev)); goto err_free_desc; } priv->channel = 1; dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; err = ieee80211_register_hw(dev); if (err) { printk(KERN_ERR "%s (adm8211): Cannot register device\n", pci_name(pdev)); goto err_free_eeprom; } wiphy_info(dev->wiphy, "hwaddr %pM, Rev 0x%02x\n", dev->wiphy->perm_addr, pdev->revision); return 0; err_free_eeprom: kfree(priv->eeprom); err_free_desc: pci_free_consistent(pdev, sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->tx_ring_size, priv->rx_ring, priv->rx_ring_dma); kfree(priv->rx_buffers); err_iounmap: pci_iounmap(pdev, priv->map); err_free_dev: pci_set_drvdata(pdev, NULL); ieee80211_free_hw(dev); err_free_reg: pci_release_regions(pdev); err_disable_pdev: pci_disable_device(pdev); return err; } static void __devexit adm8211_remove(struct pci_dev *pdev) { struct ieee80211_hw *dev = pci_get_drvdata(pdev); struct adm8211_priv *priv; if (!dev) return; ieee80211_unregister_hw(dev); priv = dev->priv; pci_free_consistent(pdev, sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->tx_ring_size, priv->rx_ring, priv->rx_ring_dma); kfree(priv->rx_buffers); kfree(priv->eeprom); pci_iounmap(pdev, priv->map); pci_release_regions(pdev); pci_disable_device(pdev); ieee80211_free_hw(dev); } #ifdef CONFIG_PM static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state) { pci_save_state(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); return 0; } static int adm8211_resume(struct pci_dev *pdev) { pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); return 0; } #endif /* CONFIG_PM */ MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table); /* TODO: implement enable_wake */ static struct pci_driver adm8211_driver = { .name = "adm8211", .id_table = adm8211_pci_id_table, .probe = adm8211_probe, .remove = __devexit_p(adm8211_remove), #ifdef CONFIG_PM .suspend = adm8211_suspend, .resume = adm8211_resume, #endif /* CONFIG_PM */ }; module_pci_driver(adm8211_driver); compat-drivers-2012-09-18/drivers/net/wireless/mac80211_hwsim.c0000644000175000017500000015536112026211315023203 0ustar mcgrofmcgrof/* * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 * Copyright (c) 2008, Jouni Malinen * Copyright (c) 2011, Javier Lopez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* * TODO: * - Add TSF sync and fix IBSS beacon transmission by adding * competition for "air time" at TBTT * - RX filtering based on filter configuration (data->rx_filter) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mac80211_hwsim.h" #define WARN_QUEUE 100 #define MAX_QUEUE 200 MODULE_AUTHOR("Jouni Malinen"); MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211"); MODULE_LICENSE("GPL"); static u32 wmediumd_portid; static int radios = 2; module_param(radios, int, 0444); MODULE_PARM_DESC(radios, "Number of simulated radios"); static bool fake_hw_scan; module_param(fake_hw_scan, bool, 0444); MODULE_PARM_DESC(fake_hw_scan, "Install fake (no-op) hw-scan handler"); /** * enum hwsim_regtest - the type of regulatory tests we offer * * These are the different values you can use for the regtest * module parameter. This is useful to help test world roaming * and the driver regulatory_hint() call and combinations of these. * If you want to do specific alpha2 regulatory domain tests simply * use the userspace regulatory request as that will be respected as * well without the need of this module parameter. This is designed * only for testing the driver regulatory request, world roaming * and all possible combinations. * * @HWSIM_REGTEST_DISABLED: No regulatory tests are performed, * this is the default value. * @HWSIM_REGTEST_DRIVER_REG_FOLLOW: Used for testing the driver regulatory * hint, only one driver regulatory hint will be sent as such the * secondary radios are expected to follow. * @HWSIM_REGTEST_DRIVER_REG_ALL: Used for testing the driver regulatory * request with all radios reporting the same regulatory domain. * @HWSIM_REGTEST_DIFF_COUNTRY: Used for testing the drivers calling * different regulatory domains requests. Expected behaviour is for * an intersection to occur but each device will still use their * respective regulatory requested domains. Subsequent radios will * use the resulting intersection. * @HWSIM_REGTEST_WORLD_ROAM: Used for testing the world roaming. We accomplish * this by using a custom beacon-capable regulatory domain for the first * radio. All other device world roam. * @HWSIM_REGTEST_CUSTOM_WORLD: Used for testing the custom world regulatory * domain requests. All radios will adhere to this custom world regulatory * domain. * @HWSIM_REGTEST_CUSTOM_WORLD_2: Used for testing 2 custom world regulatory * domain requests. The first radio will adhere to the first custom world * regulatory domain, the second one to the second custom world regulatory * domain. All other devices will world roam. * @HWSIM_REGTEST_STRICT_FOLLOW_: Used for testing strict regulatory domain * settings, only the first radio will send a regulatory domain request * and use strict settings. The rest of the radios are expected to follow. * @HWSIM_REGTEST_STRICT_ALL: Used for testing strict regulatory domain * settings. All radios will adhere to this. * @HWSIM_REGTEST_STRICT_AND_DRIVER_REG: Used for testing strict regulatory * domain settings, combined with secondary driver regulatory domain * settings. The first radio will get a strict regulatory domain setting * using the first driver regulatory request and the second radio will use * non-strict settings using the second driver regulatory request. All * other devices should follow the intersection created between the * first two. * @HWSIM_REGTEST_ALL: Used for testing every possible mix. You will need * at least 6 radios for a complete test. We will test in this order: * 1 - driver custom world regulatory domain * 2 - second custom world regulatory domain * 3 - first driver regulatory domain request * 4 - second driver regulatory domain request * 5 - strict regulatory domain settings using the third driver regulatory * domain request * 6 and on - should follow the intersection of the 3rd, 4rth and 5th radio * regulatory requests. */ enum hwsim_regtest { HWSIM_REGTEST_DISABLED = 0, HWSIM_REGTEST_DRIVER_REG_FOLLOW = 1, HWSIM_REGTEST_DRIVER_REG_ALL = 2, HWSIM_REGTEST_DIFF_COUNTRY = 3, HWSIM_REGTEST_WORLD_ROAM = 4, HWSIM_REGTEST_CUSTOM_WORLD = 5, HWSIM_REGTEST_CUSTOM_WORLD_2 = 6, HWSIM_REGTEST_STRICT_FOLLOW = 7, HWSIM_REGTEST_STRICT_ALL = 8, HWSIM_REGTEST_STRICT_AND_DRIVER_REG = 9, HWSIM_REGTEST_ALL = 10, }; /* Set to one of the HWSIM_REGTEST_* values above */ static int regtest = HWSIM_REGTEST_DISABLED; module_param(regtest, int, 0444); MODULE_PARM_DESC(regtest, "The type of regulatory test we want to run"); static const char *hwsim_alpha2s[] = { "FI", "AL", "US", "DE", "JP", "AL", }; static const struct ieee80211_regdomain hwsim_world_regdom_custom_01 = { .n_reg_rules = 4, .alpha2 = "99", .reg_rules = { REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), REG_RULE(2484-10, 2484+10, 40, 0, 20, 0), REG_RULE(5150-10, 5240+10, 40, 0, 30, 0), REG_RULE(5745-10, 5825+10, 40, 0, 30, 0), } }; static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = { .n_reg_rules = 2, .alpha2 = "99", .reg_rules = { REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), REG_RULE(5725-10, 5850+10, 40, 0, 30, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), } }; struct hwsim_vif_priv { u32 magic; u8 bssid[ETH_ALEN]; bool assoc; u16 aid; }; #define HWSIM_VIF_MAGIC 0x69537748 static inline void hwsim_check_magic(struct ieee80211_vif *vif) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; WARN_ON(vp->magic != HWSIM_VIF_MAGIC); } static inline void hwsim_set_magic(struct ieee80211_vif *vif) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; vp->magic = HWSIM_VIF_MAGIC; } static inline void hwsim_clear_magic(struct ieee80211_vif *vif) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; vp->magic = 0; } struct hwsim_sta_priv { u32 magic; }; #define HWSIM_STA_MAGIC 0x6d537748 static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta) { struct hwsim_sta_priv *sp = (void *)sta->drv_priv; WARN_ON(sp->magic != HWSIM_STA_MAGIC); } static inline void hwsim_set_sta_magic(struct ieee80211_sta *sta) { struct hwsim_sta_priv *sp = (void *)sta->drv_priv; sp->magic = HWSIM_STA_MAGIC; } static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta) { struct hwsim_sta_priv *sp = (void *)sta->drv_priv; sp->magic = 0; } static struct class *hwsim_class; static struct net_device *hwsim_mon; /* global monitor netdev */ #define CHAN2G(_freq) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_freq), \ .max_power = 20, \ } #define CHAN5G(_freq) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = (_freq), \ .hw_value = (_freq), \ .max_power = 20, \ } static const struct ieee80211_channel hwsim_channels_2ghz[] = { CHAN2G(2412), /* Channel 1 */ CHAN2G(2417), /* Channel 2 */ CHAN2G(2422), /* Channel 3 */ CHAN2G(2427), /* Channel 4 */ CHAN2G(2432), /* Channel 5 */ CHAN2G(2437), /* Channel 6 */ CHAN2G(2442), /* Channel 7 */ CHAN2G(2447), /* Channel 8 */ CHAN2G(2452), /* Channel 9 */ CHAN2G(2457), /* Channel 10 */ CHAN2G(2462), /* Channel 11 */ CHAN2G(2467), /* Channel 12 */ CHAN2G(2472), /* Channel 13 */ CHAN2G(2484), /* Channel 14 */ }; static const struct ieee80211_channel hwsim_channels_5ghz[] = { CHAN5G(5180), /* Channel 36 */ CHAN5G(5200), /* Channel 40 */ CHAN5G(5220), /* Channel 44 */ CHAN5G(5240), /* Channel 48 */ CHAN5G(5260), /* Channel 52 */ CHAN5G(5280), /* Channel 56 */ CHAN5G(5300), /* Channel 60 */ CHAN5G(5320), /* Channel 64 */ CHAN5G(5500), /* Channel 100 */ CHAN5G(5520), /* Channel 104 */ CHAN5G(5540), /* Channel 108 */ CHAN5G(5560), /* Channel 112 */ CHAN5G(5580), /* Channel 116 */ CHAN5G(5600), /* Channel 120 */ CHAN5G(5620), /* Channel 124 */ CHAN5G(5640), /* Channel 128 */ CHAN5G(5660), /* Channel 132 */ CHAN5G(5680), /* Channel 136 */ CHAN5G(5700), /* Channel 140 */ CHAN5G(5745), /* Channel 149 */ CHAN5G(5765), /* Channel 153 */ CHAN5G(5785), /* Channel 157 */ CHAN5G(5805), /* Channel 161 */ CHAN5G(5825), /* Channel 165 */ }; static const struct ieee80211_rate hwsim_rates[] = { { .bitrate = 10 }, { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60 }, { .bitrate = 90 }, { .bitrate = 120 }, { .bitrate = 180 }, { .bitrate = 240 }, { .bitrate = 360 }, { .bitrate = 480 }, { .bitrate = 540 } }; static spinlock_t hwsim_radio_lock; static struct list_head hwsim_radios; struct mac80211_hwsim_data { struct list_head list; struct ieee80211_hw *hw; struct device *dev; struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; struct mac_address addresses[2]; struct ieee80211_channel *channel; unsigned long beacon_int; /* in jiffies unit */ unsigned int rx_filter; bool started, idle, scanning; struct mutex mutex; struct timer_list beacon_timer; enum ps_mode { PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL } ps; bool ps_poll_pending; struct dentry *debugfs; struct dentry *debugfs_ps; struct sk_buff_head pending; /* packets pending */ /* * Only radios in the same group can communicate together (the * channel has to match too). Each bit represents a group. A * radio can be in more then one group. */ u64 group; struct dentry *debugfs_group; int power_level; /* difference between this hw's clock and the real clock, in usecs */ u64 tsf_offset; }; struct hwsim_radiotap_hdr { struct ieee80211_radiotap_header hdr; __le64 rt_tsft; u8 rt_flags; u8 rt_rate; __le16 rt_channel; __le16 rt_chbitmask; } __packed; /* MAC80211_HWSIM netlinf family */ static struct genl_family hwsim_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = 0, .name = "MAC80211_HWSIM", .version = 1, .maxattr = HWSIM_ATTR_MAX, }; /* MAC80211_HWSIM netlink policy */ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, .len = 6*sizeof(u8) }, [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, .len = 6*sizeof(u8) }, [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 }, [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 }, [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 }, [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC, .len = IEEE80211_TX_MAX_RATES*sizeof( struct hwsim_tx_rate)}, [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, }; static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, struct net_device *dev) { /* TODO: allow packet injection */ dev_kfree_skb(skb); return NETDEV_TX_OK; } static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) { struct timeval tv = ktime_to_timeval(ktime_get_real()); u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec; return cpu_to_le64(now + data->tsf_offset); } static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mac80211_hwsim_data *data = hw->priv; return le64_to_cpu(__mac80211_hwsim_get_tsf(data)); } static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct mac80211_hwsim_data *data = hw->priv; struct timeval tv = ktime_to_timeval(ktime_get_real()); u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec; data->tsf_offset = tsf - now; } static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, struct sk_buff *tx_skb) { struct mac80211_hwsim_data *data = hw->priv; struct sk_buff *skb; struct hwsim_radiotap_hdr *hdr; u16 flags; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb); struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); if (!netif_running(hwsim_mon)) return; skb = skb_copy_expand(tx_skb, sizeof(*hdr), 0, GFP_ATOMIC); if (skb == NULL) return; hdr = (struct hwsim_radiotap_hdr *) skb_push(skb, sizeof(*hdr)); hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; hdr->hdr.it_pad = 0; hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_RATE) | (1 << IEEE80211_RADIOTAP_TSFT) | (1 << IEEE80211_RADIOTAP_CHANNEL)); hdr->rt_tsft = __mac80211_hwsim_get_tsf(data); hdr->rt_flags = 0; hdr->rt_rate = txrate->bitrate / 5; hdr->rt_channel = cpu_to_le16(data->channel->center_freq); flags = IEEE80211_CHAN_2GHZ; if (txrate->flags & IEEE80211_RATE_ERP_G) flags |= IEEE80211_CHAN_OFDM; else flags |= IEEE80211_CHAN_CCK; hdr->rt_chbitmask = cpu_to_le16(flags); skb->dev = hwsim_mon; skb_set_mac_header(skb, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); memset(skb->cb, 0, sizeof(skb->cb)); netif_rx(skb); } static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr) { struct mac80211_hwsim_data *data = hw->priv; struct sk_buff *skb; struct hwsim_radiotap_hdr *hdr; u16 flags; struct ieee80211_hdr *hdr11; if (!netif_running(hwsim_mon)) return; skb = dev_alloc_skb(100); if (skb == NULL) return; hdr = (struct hwsim_radiotap_hdr *) skb_put(skb, sizeof(*hdr)); hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; hdr->hdr.it_pad = 0; hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_CHANNEL)); hdr->rt_flags = 0; hdr->rt_rate = 0; hdr->rt_channel = cpu_to_le16(data->channel->center_freq); flags = IEEE80211_CHAN_2GHZ; hdr->rt_chbitmask = cpu_to_le16(flags); hdr11 = (struct ieee80211_hdr *) skb_put(skb, 10); hdr11->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); hdr11->duration_id = cpu_to_le16(0); memcpy(hdr11->addr1, addr, ETH_ALEN); skb->dev = hwsim_mon; skb_set_mac_header(skb, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); memset(skb->cb, 0, sizeof(skb->cb)); netif_rx(skb); } static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, struct sk_buff *skb) { switch (data->ps) { case PS_DISABLED: return true; case PS_ENABLED: return false; case PS_AUTO_POLL: /* TODO: accept (some) Beacons by default and other frames only * if pending PS-Poll has been sent */ return true; case PS_MANUAL_POLL: /* Allow unicast frames to own address if there is a pending * PS-Poll */ if (data->ps_poll_pending && memcmp(data->hw->wiphy->perm_addr, skb->data + 4, ETH_ALEN) == 0) { data->ps_poll_pending = false; return true; } return false; } return true; } struct mac80211_hwsim_addr_match_data { bool ret; const u8 *addr; }; static void mac80211_hwsim_addr_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct mac80211_hwsim_addr_match_data *md = data; if (memcmp(mac, md->addr, ETH_ALEN) == 0) md->ret = true; } static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, const u8 *addr) { struct mac80211_hwsim_addr_match_data md; if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0) return true; md.ret = false; md.addr = addr; ieee80211_iterate_active_interfaces_atomic(data->hw, mac80211_hwsim_addr_iter, &md); return md.ret; } static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct sk_buff *my_skb, int dst_portid) { struct sk_buff *skb; struct mac80211_hwsim_data *data = hw->priv; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb); void *msg_head; unsigned int hwsim_flags = 0; int i; struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES]; if (data->idle) { wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n"); dev_kfree_skb(my_skb); return; } if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); /* If the queue contains MAX_QUEUE skb's drop some */ if (skb_queue_len(&data->pending) >= MAX_QUEUE) { /* Droping until WARN_QUEUE level */ while (skb_queue_len(&data->pending) >= WARN_QUEUE) skb_dequeue(&data->pending); } skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (skb == NULL) goto nla_put_failure; msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, HWSIM_CMD_FRAME); if (msg_head == NULL) { printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n"); goto nla_put_failure; } if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, sizeof(struct mac_address), data->addresses[1].addr)) goto nla_put_failure; /* We get the skb->data */ if (nla_put(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data)) goto nla_put_failure; /* We get the flags for this transmission, and we translate them to wmediumd flags */ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS; if (info->flags & IEEE80211_TX_CTL_NO_ACK) hwsim_flags |= HWSIM_TX_CTL_NO_ACK; if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags)) goto nla_put_failure; /* We get the tx control (rate and retries) info*/ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { tx_attempts[i].idx = info->status.rates[i].idx; tx_attempts[i].count = info->status.rates[i].count; } if (nla_put(skb, HWSIM_ATTR_TX_INFO, sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES, tx_attempts)) goto nla_put_failure; /* We create a cookie to identify this skb */ if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb)) goto nla_put_failure; genlmsg_end(skb, msg_head); genlmsg_unicast(&init_net, skb, dst_portid); /* Enqueue the packet */ skb_queue_tail(&data->pending, my_skb); return; nla_put_failure: printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); } static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct sk_buff *skb) { struct mac80211_hwsim_data *data = hw->priv, *data2; bool ack = false; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_rx_status rx_status; struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); if (data->idle) { wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n"); return false; } memset(&rx_status, 0, sizeof(rx_status)); rx_status.flag |= RX_FLAG_MACTIME_MPDU; rx_status.freq = data->channel->center_freq; rx_status.band = data->channel->band; rx_status.rate_idx = info->control.rates[0].idx; if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) rx_status.flag |= RX_FLAG_HT; if (info->control.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) rx_status.flag |= RX_FLAG_40MHZ; if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) rx_status.flag |= RX_FLAG_SHORT_GI; /* TODO: simulate real signal strength (and optional packet loss) */ rx_status.signal = data->power_level - 50; if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); /* release the skb's source info */ skb_orphan(skb); skb_dst_drop(skb); skb->mark = 0; secpath_reset(skb); nf_reset(skb); /* Copy skb to all enabled radios that are on the current frequency */ spin_lock(&hwsim_radio_lock); list_for_each_entry(data2, &hwsim_radios, list) { struct sk_buff *nskb; struct ieee80211_mgmt *mgmt; if (data == data2) continue; if (data2->idle || !data2->started || !hwsim_ps_rx_ok(data2, skb) || !data2->channel || data->channel->center_freq != data2->channel->center_freq || !(data->group & data2->group)) continue; nskb = skb_copy(skb, GFP_ATOMIC); if (nskb == NULL) continue; if (mac80211_hwsim_addr_match(data2, hdr->addr1)) ack = true; /* set bcn timestamp relative to receiver mactime */ rx_status.mactime = le64_to_cpu(__mac80211_hwsim_get_tsf(data2)); mgmt = (struct ieee80211_mgmt *) nskb->data; if (ieee80211_is_beacon(mgmt->frame_control) || ieee80211_is_probe_resp(mgmt->frame_control)) mgmt->u.beacon.timestamp = cpu_to_le64( rx_status.mactime + (data->tsf_offset - data2->tsf_offset) + 24 * 8 * 10 / txrate->bitrate); memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(data2->hw, nskb); } spin_unlock(&hwsim_radio_lock); return ack; } static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { bool ack; struct ieee80211_tx_info *txi; u32 _portid; mac80211_hwsim_monitor_rx(hw, skb); if (skb->len < 10) { /* Should not happen; just a sanity check for addr1 use */ dev_kfree_skb(skb); return; } /* wmediumd mode check */ _portid = ACCESS_ONCE(wmediumd_portid); if (_portid) return mac80211_hwsim_tx_frame_nl(hw, skb, _portid); /* NO wmediumd detected, perfect medium simulation */ ack = mac80211_hwsim_tx_frame_no_nl(hw, skb); if (ack && skb->len >= 16) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; mac80211_hwsim_monitor_ack(hw, hdr->addr2); } txi = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(txi); /* frame was transmitted at most favorable rate at first attempt */ txi->control.rates[0].count = 1; txi->control.rates[1].idx = -1; if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack) txi->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(hw, skb); } static int mac80211_hwsim_start(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *data = hw->priv; wiphy_debug(hw->wiphy, "%s\n", __func__); data->started = true; return 0; } static void mac80211_hwsim_stop(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *data = hw->priv; data->started = false; del_timer(&data->beacon_timer); wiphy_debug(hw->wiphy, "%s\n", __func__); } static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { wiphy_debug(hw->wiphy, "%s (type=%d mac_addr=%pM)\n", __func__, ieee80211_vif_type_p2p(vif), vif->addr); hwsim_set_magic(vif); return 0; } static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p) { newtype = ieee80211_iftype_p2p(newtype, newp2p); wiphy_debug(hw->wiphy, "%s (old type=%d, new type=%d, mac_addr=%pM)\n", __func__, ieee80211_vif_type_p2p(vif), newtype, vif->addr); hwsim_check_magic(vif); return 0; } static void mac80211_hwsim_remove_interface( struct ieee80211_hw *hw, struct ieee80211_vif *vif) { wiphy_debug(hw->wiphy, "%s (type=%d mac_addr=%pM)\n", __func__, ieee80211_vif_type_p2p(vif), vif->addr); hwsim_check_magic(vif); hwsim_clear_magic(vif); } static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, struct ieee80211_vif *vif) { struct ieee80211_hw *hw = arg; struct sk_buff *skb; struct ieee80211_tx_info *info; u32 _portid; hwsim_check_magic(vif); if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_MESH_POINT && vif->type != NL80211_IFTYPE_ADHOC) return; skb = ieee80211_beacon_get(hw, vif); if (skb == NULL) return; info = IEEE80211_SKB_CB(skb); mac80211_hwsim_monitor_rx(hw, skb); /* wmediumd mode check */ _portid = ACCESS_ONCE(wmediumd_portid); if (_portid) return mac80211_hwsim_tx_frame_nl(hw, skb, _portid); mac80211_hwsim_tx_frame_no_nl(hw, skb); dev_kfree_skb(skb); } static void mac80211_hwsim_beacon(unsigned long arg) { struct ieee80211_hw *hw = (struct ieee80211_hw *) arg; struct mac80211_hwsim_data *data = hw->priv; if (!data->started) return; ieee80211_iterate_active_interfaces_atomic( hw, mac80211_hwsim_beacon_tx, hw); data->beacon_timer.expires = jiffies + data->beacon_int; add_timer(&data->beacon_timer); } static const char *hwsim_chantypes[] = { [NL80211_CHAN_NO_HT] = "noht", [NL80211_CHAN_HT20] = "ht20", [NL80211_CHAN_HT40MINUS] = "ht40-", [NL80211_CHAN_HT40PLUS] = "ht40+", }; static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) { struct mac80211_hwsim_data *data = hw->priv; struct ieee80211_conf *conf = &hw->conf; static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { [IEEE80211_SMPS_AUTOMATIC] = "auto", [IEEE80211_SMPS_OFF] = "off", [IEEE80211_SMPS_STATIC] = "static", [IEEE80211_SMPS_DYNAMIC] = "dynamic", }; wiphy_debug(hw->wiphy, "%s (freq=%d/%s idle=%d ps=%d smps=%s)\n", __func__, conf->channel->center_freq, hwsim_chantypes[conf->channel_type], !!(conf->flags & IEEE80211_CONF_IDLE), !!(conf->flags & IEEE80211_CONF_PS), smps_modes[conf->smps_mode]); data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); data->channel = conf->channel; data->power_level = conf->power_level; if (!data->started || !data->beacon_int) del_timer(&data->beacon_timer); else mod_timer(&data->beacon_timer, jiffies + data->beacon_int); return 0; } static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags,u64 multicast) { struct mac80211_hwsim_data *data = hw->priv; wiphy_debug(hw->wiphy, "%s\n", __func__); data->rx_filter = 0; if (*total_flags & FIF_PROMISC_IN_BSS) data->rx_filter |= FIF_PROMISC_IN_BSS; if (*total_flags & FIF_ALLMULTI) data->rx_filter |= FIF_ALLMULTI; *total_flags = data->rx_filter; } static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct mac80211_hwsim_data *data = hw->priv; hwsim_check_magic(vif); wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed); if (changed & BSS_CHANGED_BSSID) { wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n", __func__, info->bssid); memcpy(vp->bssid, info->bssid, ETH_ALEN); } if (changed & BSS_CHANGED_ASSOC) { wiphy_debug(hw->wiphy, " ASSOC: assoc=%d aid=%d\n", info->assoc, info->aid); vp->assoc = info->assoc; vp->aid = info->aid; } if (changed & BSS_CHANGED_BEACON_INT) { wiphy_debug(hw->wiphy, " BCNINT: %d\n", info->beacon_int); data->beacon_int = 1024 * info->beacon_int / 1000 * HZ / 1000; if (WARN_ON(!data->beacon_int)) data->beacon_int = 1; if (data->started) mod_timer(&data->beacon_timer, jiffies + data->beacon_int); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { wiphy_debug(hw->wiphy, " ERP_CTS_PROT: %d\n", info->use_cts_prot); } if (changed & BSS_CHANGED_ERP_PREAMBLE) { wiphy_debug(hw->wiphy, " ERP_PREAMBLE: %d\n", info->use_short_preamble); } if (changed & BSS_CHANGED_ERP_SLOT) { wiphy_debug(hw->wiphy, " ERP_SLOT: %d\n", info->use_short_slot); } if (changed & BSS_CHANGED_HT) { wiphy_debug(hw->wiphy, " HT: op_mode=0x%x, chantype=%s\n", info->ht_operation_mode, hwsim_chantypes[info->channel_type]); } if (changed & BSS_CHANGED_BASIC_RATES) { wiphy_debug(hw->wiphy, " BASIC_RATES: 0x%llx\n", (unsigned long long) info->basic_rates); } } static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { hwsim_check_magic(vif); hwsim_set_sta_magic(sta); return 0; } static int mac80211_hwsim_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { hwsim_check_magic(vif); hwsim_clear_sta_magic(sta); return 0; } static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { hwsim_check_magic(vif); switch (cmd) { case STA_NOTIFY_SLEEP: case STA_NOTIFY_AWAKE: /* TODO: make good use of these flags */ break; default: WARN(1, "Invalid sta notify: %d\n", cmd); break; } } static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { hwsim_check_sta_magic(sta); return 0; } static int mac80211_hwsim_conf_tx( struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { wiphy_debug(hw->wiphy, "%s (queue=%d txop=%d cw_min=%d cw_max=%d aifs=%d)\n", __func__, queue, params->txop, params->cw_min, params->cw_max, params->aifs); return 0; } static int mac80211_hwsim_get_survey( struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct ieee80211_conf *conf = &hw->conf; wiphy_debug(hw->wiphy, "%s (idx=%d)\n", __func__, idx); if (idx != 0) return -ENOENT; /* Current channel */ survey->channel = conf->channel; /* * Magically conjured noise level --- this is only ok for simulated hardware. * * A real driver which cannot determine the real channel noise MUST NOT * report any noise, especially not a magically conjured one :-) */ survey->filled = SURVEY_INFO_NOISE_DBM; survey->noise = -92; return 0; } #ifdef CONFIG_NL80211_TESTMODE /* * This section contains example code for using netlink * attributes with the testmode command in nl80211. */ /* These enums need to be kept in sync with userspace */ enum hwsim_testmode_attr { __HWSIM_TM_ATTR_INVALID = 0, HWSIM_TM_ATTR_CMD = 1, HWSIM_TM_ATTR_PS = 2, /* keep last */ __HWSIM_TM_ATTR_AFTER_LAST, HWSIM_TM_ATTR_MAX = __HWSIM_TM_ATTR_AFTER_LAST - 1 }; enum hwsim_testmode_cmd { HWSIM_TM_CMD_SET_PS = 0, HWSIM_TM_CMD_GET_PS = 1, HWSIM_TM_CMD_STOP_QUEUES = 2, HWSIM_TM_CMD_WAKE_QUEUES = 3, }; static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { [HWSIM_TM_ATTR_CMD] = { .type = NLA_U32 }, [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, }; static int hwsim_fops_ps_write(void *dat, u64 val); static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) { struct mac80211_hwsim_data *hwsim = hw->priv; struct nlattr *tb[HWSIM_TM_ATTR_MAX + 1]; struct sk_buff *skb; int err, ps; err = nla_parse(tb, HWSIM_TM_ATTR_MAX, data, len, hwsim_testmode_policy); if (err) return err; if (!tb[HWSIM_TM_ATTR_CMD]) return -EINVAL; switch (nla_get_u32(tb[HWSIM_TM_ATTR_CMD])) { case HWSIM_TM_CMD_SET_PS: if (!tb[HWSIM_TM_ATTR_PS]) return -EINVAL; ps = nla_get_u32(tb[HWSIM_TM_ATTR_PS]); return hwsim_fops_ps_write(hwsim, ps); case HWSIM_TM_CMD_GET_PS: skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, nla_total_size(sizeof(u32))); if (!skb) return -ENOMEM; if (nla_put_u32(skb, HWSIM_TM_ATTR_PS, hwsim->ps)) goto nla_put_failure; return cfg80211_testmode_reply(skb); case HWSIM_TM_CMD_STOP_QUEUES: ieee80211_stop_queues(hw); return 0; case HWSIM_TM_CMD_WAKE_QUEUES: ieee80211_wake_queues(hw); return 0; default: return -EOPNOTSUPP; } nla_put_failure: kfree_skb(skb); return -ENOBUFS; } #endif static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { switch (action) { case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: break; case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: break; default: return -EOPNOTSUPP; } return 0; } static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop) { /* Not implemented, queues only on kernel side */ } struct hw_scan_done { struct delayed_work w; struct ieee80211_hw *hw; }; static void hw_scan_done(struct work_struct *work) { struct hw_scan_done *hsd = container_of(work, struct hw_scan_done, w.work); ieee80211_scan_completed(hsd->hw, false); kfree(hsd); } static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL); int i; if (!hsd) return -ENOMEM; hsd->hw = hw; INIT_DELAYED_WORK(&hsd->w, hw_scan_done); printk(KERN_DEBUG "hwsim hw_scan request\n"); for (i = 0; i < req->n_channels; i++) printk(KERN_DEBUG "hwsim hw_scan freq %d\n", req->channels[i]->center_freq); print_hex_dump(KERN_DEBUG, "scan IEs: ", DUMP_PREFIX_OFFSET, 16, 1, req->ie, req->ie_len, 1); ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ); return 0; } static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *hwsim = hw->priv; mutex_lock(&hwsim->mutex); if (hwsim->scanning) { printk(KERN_DEBUG "two hwsim sw_scans detected!\n"); goto out; } printk(KERN_DEBUG "hwsim sw_scan request, prepping stuff\n"); hwsim->scanning = true; out: mutex_unlock(&hwsim->mutex); } static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *hwsim = hw->priv; mutex_lock(&hwsim->mutex); printk(KERN_DEBUG "hwsim sw_scan_complete\n"); hwsim->scanning = false; mutex_unlock(&hwsim->mutex); } static struct ieee80211_ops mac80211_hwsim_ops = { .tx = mac80211_hwsim_tx, .start = mac80211_hwsim_start, .stop = mac80211_hwsim_stop, .add_interface = mac80211_hwsim_add_interface, .change_interface = mac80211_hwsim_change_interface, .remove_interface = mac80211_hwsim_remove_interface, .config = mac80211_hwsim_config, .configure_filter = mac80211_hwsim_configure_filter, .bss_info_changed = mac80211_hwsim_bss_info_changed, .sta_add = mac80211_hwsim_sta_add, .sta_remove = mac80211_hwsim_sta_remove, .sta_notify = mac80211_hwsim_sta_notify, .set_tim = mac80211_hwsim_set_tim, .conf_tx = mac80211_hwsim_conf_tx, .get_survey = mac80211_hwsim_get_survey, CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) .ampdu_action = mac80211_hwsim_ampdu_action, .sw_scan_start = mac80211_hwsim_sw_scan, .sw_scan_complete = mac80211_hwsim_sw_scan_complete, .flush = mac80211_hwsim_flush, .get_tsf = mac80211_hwsim_get_tsf, .set_tsf = mac80211_hwsim_set_tsf, }; static void mac80211_hwsim_free(void) { struct list_head tmplist, *i, *tmp; struct mac80211_hwsim_data *data, *tmpdata; INIT_LIST_HEAD(&tmplist); spin_lock_bh(&hwsim_radio_lock); list_for_each_safe(i, tmp, &hwsim_radios) list_move(i, &tmplist); spin_unlock_bh(&hwsim_radio_lock); list_for_each_entry_safe(data, tmpdata, &tmplist, list) { debugfs_remove(data->debugfs_group); debugfs_remove(data->debugfs_ps); debugfs_remove(data->debugfs); ieee80211_unregister_hw(data->hw); device_unregister(data->dev); ieee80211_free_hw(data->hw); } class_destroy(hwsim_class); } static struct device_driver mac80211_hwsim_driver = { .name = "mac80211_hwsim" }; static const struct net_device_ops hwsim_netdev_ops = { .ndo_start_xmit = hwsim_mon_xmit, .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; static void hwsim_mon_setup(struct net_device *dev) { netdev_attach_ops(dev, &hwsim_netdev_ops); dev->destructor = free_netdev; ether_setup(dev); dev->tx_queue_len = 0; dev->type = ARPHRD_IEEE80211_RADIOTAP; memset(dev->dev_addr, 0, ETH_ALEN); dev->dev_addr[0] = 0x12; } static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) { struct mac80211_hwsim_data *data = dat; struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct sk_buff *skb; struct ieee80211_pspoll *pspoll; u32 _portid; if (!vp->assoc) return; wiphy_debug(data->hw->wiphy, "%s: send PS-Poll to %pM for aid %d\n", __func__, vp->bssid, vp->aid); skb = dev_alloc_skb(sizeof(*pspoll)); if (!skb) return; pspoll = (void *) skb_put(skb, sizeof(*pspoll)); pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL | IEEE80211_FCTL_PM); pspoll->aid = cpu_to_le16(0xc000 | vp->aid); memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); memcpy(pspoll->ta, mac, ETH_ALEN); /* wmediumd mode check */ _portid = ACCESS_ONCE(wmediumd_portid); if (_portid) return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid); if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb)) printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__); dev_kfree_skb(skb); } static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, struct ieee80211_vif *vif, int ps) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct sk_buff *skb; struct ieee80211_hdr *hdr; u32 _portid; if (!vp->assoc) return; wiphy_debug(data->hw->wiphy, "%s: send data::nullfunc to %pM ps=%d\n", __func__, vp->bssid, ps); skb = dev_alloc_skb(sizeof(*hdr)); if (!skb) return; hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | (ps ? IEEE80211_FCTL_PM : 0)); hdr->duration_id = cpu_to_le16(0); memcpy(hdr->addr1, vp->bssid, ETH_ALEN); memcpy(hdr->addr2, mac, ETH_ALEN); memcpy(hdr->addr3, vp->bssid, ETH_ALEN); /* wmediumd mode check */ _portid = ACCESS_ONCE(wmediumd_portid); if (_portid) return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid); if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb)) printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__); dev_kfree_skb(skb); } static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, struct ieee80211_vif *vif) { struct mac80211_hwsim_data *data = dat; hwsim_send_nullfunc(data, mac, vif, 1); } static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, struct ieee80211_vif *vif) { struct mac80211_hwsim_data *data = dat; hwsim_send_nullfunc(data, mac, vif, 0); } static int hwsim_fops_ps_read(void *dat, u64 *val) { struct mac80211_hwsim_data *data = dat; *val = data->ps; return 0; } static int hwsim_fops_ps_write(void *dat, u64 val) { struct mac80211_hwsim_data *data = dat; enum ps_mode old_ps; if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && val != PS_MANUAL_POLL) return -EINVAL; old_ps = data->ps; data->ps = val; if (val == PS_MANUAL_POLL) { ieee80211_iterate_active_interfaces(data->hw, hwsim_send_ps_poll, data); data->ps_poll_pending = true; } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { ieee80211_iterate_active_interfaces(data->hw, hwsim_send_nullfunc_ps, data); } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { ieee80211_iterate_active_interfaces(data->hw, hwsim_send_nullfunc_no_ps, data); } return 0; } DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, "%llu\n"); static int hwsim_fops_group_read(void *dat, u64 *val) { struct mac80211_hwsim_data *data = dat; *val = data->group; return 0; } static int hwsim_fops_group_write(void *dat, u64 val) { struct mac80211_hwsim_data *data = dat; data->group = val; return 0; } DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, hwsim_fops_group_read, hwsim_fops_group_write, "%llx\n"); static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr( struct mac_address *addr) { struct mac80211_hwsim_data *data; bool _found = false; spin_lock_bh(&hwsim_radio_lock); list_for_each_entry(data, &hwsim_radios, list) { if (memcmp(data->addresses[1].addr, addr, sizeof(struct mac_address)) == 0) { _found = true; break; } } spin_unlock_bh(&hwsim_radio_lock); if (!_found) return NULL; return data; } static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, struct genl_info *info) { struct ieee80211_hdr *hdr; struct mac80211_hwsim_data *data2; struct ieee80211_tx_info *txi; struct hwsim_tx_rate *tx_attempts; unsigned long ret_skb_ptr; struct sk_buff *skb, *tmp; struct mac_address *src; unsigned int hwsim_flags; int i; bool found = false; if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || !info->attrs[HWSIM_ATTR_FLAGS] || !info->attrs[HWSIM_ATTR_COOKIE] || !info->attrs[HWSIM_ATTR_TX_INFO]) goto out; src = (struct mac_address *)nla_data( info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]); ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); data2 = get_hwsim_data_ref_from_addr(src); if (data2 == NULL) goto out; /* look for the skb matching the cookie passed back from user */ skb_queue_walk_safe(&data2->pending, skb, tmp) { if ((unsigned long)skb == ret_skb_ptr) { skb_unlink(skb, &data2->pending); found = true; break; } } /* not found */ if (!found) goto out; /* Tx info received because the frame was broadcasted on user space, so we get all the necessary info: tx attempts and skb control buff */ tx_attempts = (struct hwsim_tx_rate *)nla_data( info->attrs[HWSIM_ATTR_TX_INFO]); /* now send back TX status */ txi = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(txi); for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { txi->status.rates[i].idx = tx_attempts[i].idx; txi->status.rates[i].count = tx_attempts[i].count; /*txi->status.rates[i].flags = 0;*/ } txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]); if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) && (hwsim_flags & HWSIM_TX_STAT_ACK)) { if (skb->len >= 16) { hdr = (struct ieee80211_hdr *) skb->data; mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2); } txi->flags |= IEEE80211_TX_STAT_ACK; } ieee80211_tx_status_irqsafe(data2->hw, skb); return 0; out: return -EINVAL; } static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, struct genl_info *info) { struct mac80211_hwsim_data *data2; struct ieee80211_rx_status rx_status; struct mac_address *dst; int frame_data_len; char *frame_data; struct sk_buff *skb = NULL; if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || !info->attrs[HWSIM_ATTR_FRAME] || !info->attrs[HWSIM_ATTR_RX_RATE] || !info->attrs[HWSIM_ATTR_SIGNAL]) goto out; dst = (struct mac_address *)nla_data( info->attrs[HWSIM_ATTR_ADDR_RECEIVER]); frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]); frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]); /* Allocate new skb here */ skb = alloc_skb(frame_data_len, GFP_KERNEL); if (skb == NULL) goto err; if (frame_data_len <= IEEE80211_MAX_DATA_LEN) { /* Copy the data */ memcpy(skb_put(skb, frame_data_len), frame_data, frame_data_len); } else goto err; data2 = get_hwsim_data_ref_from_addr(dst); if (data2 == NULL) goto out; /* check if radio is configured properly */ if (data2->idle || !data2->started || !data2->channel) goto out; /*A frame is received from user space*/ memset(&rx_status, 0, sizeof(rx_status)); rx_status.freq = data2->channel->center_freq; rx_status.band = data2->channel->band; rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]); rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]); memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(data2->hw, skb); return 0; err: printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); goto out; out: dev_kfree_skb(skb); return -EINVAL; } static int hwsim_register_received_nl(struct sk_buff *skb_2, struct genl_info *info) { if (info == NULL) goto out; wmediumd_portid = genl_info_snd_portid(info); printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, " "switching to wmediumd mode with pid %d\n", genl_info_snd_portid(info)); return 0; out: printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); return -EINVAL; } /* Generic Netlink operations array */ static struct genl_ops hwsim_ops[] = { { .cmd = HWSIM_CMD_REGISTER, .policy = hwsim_genl_policy, .doit = hwsim_register_received_nl, .flags = GENL_ADMIN_PERM, }, { .cmd = HWSIM_CMD_FRAME, .policy = hwsim_genl_policy, .doit = hwsim_cloned_frame_received_nl, }, { .cmd = HWSIM_CMD_TX_INFO_FRAME, .policy = hwsim_genl_policy, .doit = hwsim_tx_info_frame_received_nl, }, }; static int mac80211_hwsim_netlink_notify(struct notifier_block *nb, unsigned long state, void *_notify) { struct netlink_notify *notify = _notify; if (state != NETLINK_URELEASE) return NOTIFY_DONE; if (netlink_notify_portid(notify) == wmediumd_portid) { printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink" " socket, switching to perfect channel medium\n"); wmediumd_portid = 0; } return NOTIFY_DONE; } static struct notifier_block hwsim_netlink_notifier = { .notifier_call = mac80211_hwsim_netlink_notify, }; static int hwsim_init_netlink(void) { int rc; printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops, ARRAY_SIZE(hwsim_ops)); if (rc) goto failure; rc = netlink_register_notifier(&hwsim_netlink_notifier); if (rc) goto failure; return 0; failure: printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); return -EINVAL; } static void hwsim_exit_netlink(void) { int ret; printk(KERN_INFO "mac80211_hwsim: closing netlink\n"); /* unregister the notifier */ netlink_unregister_notifier(&hwsim_netlink_notifier); /* unregister the family */ ret = genl_unregister_family(&hwsim_genl_family); if (ret) printk(KERN_DEBUG "mac80211_hwsim: " "unregister family %i\n", ret); } static const struct ieee80211_iface_limit hwsim_if_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) | #ifdef CONFIG_MAC80211_MESH BIT(NL80211_IFTYPE_MESH_POINT) | #endif BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) }, { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, }; static const struct ieee80211_iface_combination hwsim_if_comb = { .limits = hwsim_if_limits, .n_limits = ARRAY_SIZE(hwsim_if_limits), .max_interfaces = 2048, .num_different_channels = 1, }; static int __init init_mac80211_hwsim(void) { int i, err = 0; u8 addr[ETH_ALEN]; struct mac80211_hwsim_data *data; struct ieee80211_hw *hw; enum ieee80211_band band; if (radios < 1 || radios > 100) return -EINVAL; if (fake_hw_scan) { mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan; mac80211_hwsim_ops.sw_scan_start = NULL; mac80211_hwsim_ops.sw_scan_complete = NULL; } spin_lock_init(&hwsim_radio_lock); INIT_LIST_HEAD(&hwsim_radios); hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); if (IS_ERR(hwsim_class)) return PTR_ERR(hwsim_class); memset(addr, 0, ETH_ALEN); addr[0] = 0x02; for (i = 0; i < radios; i++) { printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n", i); hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops); if (!hw) { printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw " "failed\n"); err = -ENOMEM; goto failed; } data = hw->priv; data->hw = hw; data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", i); if (IS_ERR(data->dev)) { printk(KERN_DEBUG "mac80211_hwsim: device_create " "failed (%ld)\n", PTR_ERR(data->dev)); err = -ENOMEM; goto failed_drvdata; } data->dev->driver = &mac80211_hwsim_driver; skb_queue_head_init(&data->pending); SET_IEEE80211_DEV(hw, data->dev); addr[3] = i >> 8; addr[4] = i; memcpy(data->addresses[0].addr, addr, ETH_ALEN); memcpy(data->addresses[1].addr, addr, ETH_ALEN); data->addresses[1].addr[0] |= 0x40; hw->wiphy->n_addresses = 2; hw->wiphy->addresses = data->addresses; hw->wiphy->iface_combinations = &hwsim_if_comb; hw->wiphy->n_iface_combinations = 1; if (fake_hw_scan) { hw->wiphy->max_scan_ssids = 255; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; } hw->channel_change_time = 1; hw->queues = 4; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_P2P_DEVICE); hw->flags = IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_STATIC_SMPS | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_WANT_MONITOR_VIF; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; /* ask mac80211 to reserve space for magic */ hw->vif_data_size = sizeof(struct hwsim_vif_priv); hw->sta_data_size = sizeof(struct hwsim_sta_priv); memcpy(data->channels_2ghz, hwsim_channels_2ghz, sizeof(hwsim_channels_2ghz)); memcpy(data->channels_5ghz, hwsim_channels_5ghz, sizeof(hwsim_channels_5ghz)); memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband = &data->bands[band]; switch (band) { case IEEE80211_BAND_2GHZ: sband->channels = data->channels_2ghz; sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); sband->bitrates = data->rates; sband->n_bitrates = ARRAY_SIZE(hwsim_rates); break; case IEEE80211_BAND_5GHZ: sband->channels = data->channels_5ghz; sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); sband->bitrates = data->rates + 4; sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; break; default: continue; } sband->ht_cap.ht_supported = true; sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40; sband->ht_cap.ampdu_factor = 0x3; sband->ht_cap.ampdu_density = 0x6; memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs)); sband->ht_cap.mcs.rx_mask[0] = 0xff; sband->ht_cap.mcs.rx_mask[1] = 0xff; sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; hw->wiphy->bands[band] = sband; } /* By default all radios are belonging to the first group */ data->group = 1; mutex_init(&data->mutex); /* Enable frame retransmissions for lossy channels */ hw->max_rates = 4; hw->max_rate_tries = 11; /* Work to be done prior to ieee80211_register_hw() */ switch (regtest) { case HWSIM_REGTEST_DISABLED: case HWSIM_REGTEST_DRIVER_REG_FOLLOW: case HWSIM_REGTEST_DRIVER_REG_ALL: case HWSIM_REGTEST_DIFF_COUNTRY: /* * Nothing to be done for driver regulatory domain * hints prior to ieee80211_register_hw() */ break; case HWSIM_REGTEST_WORLD_ROAM: if (i == 0) { hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } break; case HWSIM_REGTEST_CUSTOM_WORLD: hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); break; case HWSIM_REGTEST_CUSTOM_WORLD_2: if (i == 0) { hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } else if (i == 1) { hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_02); } break; case HWSIM_REGTEST_STRICT_ALL: hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; break; case HWSIM_REGTEST_STRICT_FOLLOW: case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: if (i == 0) hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; break; case HWSIM_REGTEST_ALL: if (i == 0) { hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } else if (i == 1) { hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_02); } else if (i == 4) hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; break; default: break; } /* give the regulatory workqueue a chance to run */ if (regtest) schedule_timeout_interruptible(1); err = ieee80211_register_hw(hw); if (err < 0) { printk(KERN_DEBUG "mac80211_hwsim: " "ieee80211_register_hw failed (%d)\n", err); goto failed_hw; } /* Work to be done after to ieee80211_register_hw() */ switch (regtest) { case HWSIM_REGTEST_WORLD_ROAM: case HWSIM_REGTEST_DISABLED: break; case HWSIM_REGTEST_DRIVER_REG_FOLLOW: if (!i) regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); break; case HWSIM_REGTEST_DRIVER_REG_ALL: case HWSIM_REGTEST_STRICT_ALL: regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); break; case HWSIM_REGTEST_DIFF_COUNTRY: if (i < ARRAY_SIZE(hwsim_alpha2s)) regulatory_hint(hw->wiphy, hwsim_alpha2s[i]); break; case HWSIM_REGTEST_CUSTOM_WORLD: case HWSIM_REGTEST_CUSTOM_WORLD_2: /* * Nothing to be done for custom world regulatory * domains after to ieee80211_register_hw */ break; case HWSIM_REGTEST_STRICT_FOLLOW: if (i == 0) regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); break; case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: if (i == 0) regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); else if (i == 1) regulatory_hint(hw->wiphy, hwsim_alpha2s[1]); break; case HWSIM_REGTEST_ALL: if (i == 2) regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); else if (i == 3) regulatory_hint(hw->wiphy, hwsim_alpha2s[1]); else if (i == 4) regulatory_hint(hw->wiphy, hwsim_alpha2s[2]); break; default: break; } wiphy_debug(hw->wiphy, "hwaddr %pm registered\n", hw->wiphy->perm_addr); data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); data->debugfs_ps = debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); data->debugfs_group = debugfs_create_file("group", 0666, data->debugfs, data, &hwsim_fops_group); setup_timer(&data->beacon_timer, mac80211_hwsim_beacon, (unsigned long) hw); list_add_tail(&data->list, &hwsim_radios); } hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup); if (hwsim_mon == NULL) goto failed; rtnl_lock(); err = dev_alloc_name(hwsim_mon, hwsim_mon->name); if (err < 0) goto failed_mon; err = register_netdevice(hwsim_mon); if (err < 0) goto failed_mon; rtnl_unlock(); err = hwsim_init_netlink(); if (err < 0) goto failed_nl; return 0; failed_nl: printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n"); return err; failed_mon: rtnl_unlock(); free_netdev(hwsim_mon); mac80211_hwsim_free(); return err; failed_hw: device_unregister(data->dev); failed_drvdata: ieee80211_free_hw(hw); failed: mac80211_hwsim_free(); return err; } static void __exit exit_mac80211_hwsim(void) { printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n"); hwsim_exit_netlink(); mac80211_hwsim_free(); unregister_netdev(hwsim_mon); } module_init(init_mac80211_hwsim); module_exit(exit_mac80211_hwsim); compat-drivers-2012-09-18/drivers/net/wireless/mac80211_hwsim.h0000644000175000017500000001014112026211315023172 0ustar mcgrofmcgrof/* * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 * Copyright (c) 2008, Jouni Malinen * Copyright (c) 2011, Javier Lopez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef __MAC80211_HWSIM_H #define __MAC80211_HWSIM_H /** * enum hwsim_tx_control_flags - flags to describe transmission info/status * * These flags are used to give the wmediumd extra information in order to * modify its behavior for each frame * * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame. * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack * @HWSIM_TX_STAT_ACK: Frame was acknowledged * */ enum hwsim_tx_control_flags { HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0), HWSIM_TX_CTL_NO_ACK = BIT(1), HWSIM_TX_STAT_ACK = BIT(2), }; /** * DOC: Frame transmission/registration support * * Frame transmission and registration support exists to allow userspace * entities such as wmediumd to receive and process all broadcasted * frames from a mac80211_hwsim radio device. * * This allow user space applications to decide if the frame should be * dropped or not and implement a wireless medium simulator at user space. * * Registration is done by sending a register message to the driver and * will be automatically unregistered if the user application doesn't * responds to sent frames. * Once registered the user application has to take responsibility of * broadcasting the frames to all listening mac80211_hwsim radio * interfaces. * * For more technical details, see the corresponding command descriptions * below. */ /** * enum hwsim_commands - supported hwsim commands * * @HWSIM_CMD_UNSPEC: unspecified command to catch errors * * @HWSIM_CMD_REGISTER: request to register and received all broadcasted * frames by any mac80211_hwsim radio device. * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user * space, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER, * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE, * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to * kernel, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS, * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE * @__HWSIM_CMD_MAX: enum limit */ enum { HWSIM_CMD_UNSPEC, HWSIM_CMD_REGISTER, HWSIM_CMD_FRAME, HWSIM_CMD_TX_INFO_FRAME, __HWSIM_CMD_MAX, }; #define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1) /** * enum hwsim_attrs - hwsim netlink attributes * * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors * * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that * the frame is broadcasted to * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that * the frame was broadcasted from * @HWSIM_ATTR_FRAME: Data array * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process properly the frame at user space * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user space * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user space * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame * @__HWSIM_ATTR_MAX: enum limit */ enum { HWSIM_ATTR_UNSPEC, HWSIM_ATTR_ADDR_RECEIVER, HWSIM_ATTR_ADDR_TRANSMITTER, HWSIM_ATTR_FRAME, HWSIM_ATTR_FLAGS, HWSIM_ATTR_RX_RATE, HWSIM_ATTR_SIGNAL, HWSIM_ATTR_TX_INFO, HWSIM_ATTR_COOKIE, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) /** * struct hwsim_tx_rate - rate selection/status * * @idx: rate index to attempt to send with * @count: number of tries in this rate before going to the next rate * * A value of -1 for @idx indicates an invalid rate and, if used * in an array of retry rates, that no more rates should be tried. * * When used for transmit status reporting, the driver should * always report the rate and number of retries used. * */ struct hwsim_tx_rate { s8 idx; u8 count; } __packed; #endif /* __MAC80211_HWSIM_H */ compat-drivers-2012-09-18/drivers/net/wireless/at76c50x-usb.h0000644000175000017500000002607012026211315022707 0ustar mcgrofmcgrof/* * Copyright (c) 2002,2003 Oliver Kurth * (c) 2003,2004 Joerg Albert * (c) 2007 Guido Guenther * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This driver was based on information from the Sourceforge driver * released and maintained by Atmel: * * http://sourceforge.net/projects/atmelwlandriver/ * * Although the code was completely re-written, * it would have been impossible without Atmel's decision to * release an Open Source driver (unfortunately the firmware was * kept binary only). Thanks for that decision to Atmel! */ #ifndef _AT76_USB_H #define _AT76_USB_H /* Board types */ enum board_type { BOARD_503_ISL3861 = 1, BOARD_503_ISL3863 = 2, BOARD_503 = 3, BOARD_503_ACC = 4, BOARD_505 = 5, BOARD_505_2958 = 6, BOARD_505A = 7, BOARD_505AMX = 8 }; #define CMD_STATUS_IDLE 0x00 #define CMD_STATUS_COMPLETE 0x01 #define CMD_STATUS_UNKNOWN 0x02 #define CMD_STATUS_INVALID_PARAMETER 0x03 #define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 #define CMD_STATUS_TIME_OUT 0x07 #define CMD_STATUS_IN_PROGRESS 0x08 #define CMD_STATUS_HOST_FAILURE 0xff #define CMD_STATUS_SCAN_FAILED 0xf0 /* answers to get op mode */ #define OPMODE_NONE 0x00 #define OPMODE_NORMAL_NIC_WITH_FLASH 0x01 #define OPMODE_HW_CONFIG_MODE 0x02 #define OPMODE_DFU_MODE_WITH_FLASH 0x03 #define OPMODE_NORMAL_NIC_WITHOUT_FLASH 0x04 #define CMD_SET_MIB 0x01 #define CMD_GET_MIB 0x02 #define CMD_SCAN 0x03 #define CMD_JOIN 0x04 #define CMD_START_IBSS 0x05 #define CMD_RADIO_ON 0x06 #define CMD_RADIO_OFF 0x07 #define CMD_STARTUP 0x0B #define MIB_LOCAL 0x01 #define MIB_MAC_ADDR 0x02 #define MIB_MAC 0x03 #define MIB_MAC_MGMT 0x05 #define MIB_MAC_WEP 0x06 #define MIB_PHY 0x07 #define MIB_FW_VERSION 0x08 #define MIB_MDOMAIN 0x09 #define ADHOC_MODE 1 #define INFRASTRUCTURE_MODE 2 /* values for struct mib_local, field preamble_type */ #define PREAMBLE_TYPE_LONG 0 #define PREAMBLE_TYPE_SHORT 1 #define PREAMBLE_TYPE_AUTO 2 /* values for tx_rate */ #define TX_RATE_1MBIT 0 #define TX_RATE_2MBIT 1 #define TX_RATE_5_5MBIT 2 #define TX_RATE_11MBIT 3 #define TX_RATE_AUTO 4 /* power management modes */ #define AT76_PM_OFF 1 #define AT76_PM_ON 2 #define AT76_PM_SMART 3 struct hwcfg_r505 { u8 cr39_values[14]; u8 reserved1[14]; u8 bb_cr[14]; u8 pidvid[4]; u8 mac_addr[ETH_ALEN]; u8 regulatory_domain; u8 reserved2[14]; u8 cr15_values[14]; u8 reserved3[3]; } __packed; struct hwcfg_rfmd { u8 cr20_values[14]; u8 cr21_values[14]; u8 bb_cr[14]; u8 pidvid[4]; u8 mac_addr[ETH_ALEN]; u8 regulatory_domain; u8 low_power_values[14]; u8 normal_power_values[14]; u8 reserved1[3]; } __packed; struct hwcfg_intersil { u8 mac_addr[ETH_ALEN]; u8 cr31_values[14]; u8 cr58_values[14]; u8 pidvid[4]; u8 regulatory_domain; u8 reserved[1]; } __packed; union at76_hwcfg { struct hwcfg_intersil i; struct hwcfg_rfmd r3; struct hwcfg_r505 r5; }; #define WEP_SMALL_KEY_LEN (40 / 8) #define WEP_LARGE_KEY_LEN (104 / 8) #define WEP_KEYS (4) struct at76_card_config { u8 exclude_unencrypted; u8 promiscuous_mode; u8 short_retry_limit; u8 encryption_type; __le16 rts_threshold; __le16 fragmentation_threshold; /* 256..2346 */ u8 basic_rate_set[4]; u8 auto_rate_fallback; /* 0,1 */ u8 channel; u8 privacy_invoked; u8 wep_default_key_id; /* 0..3 */ u8 current_ssid[32]; u8 wep_default_key_value[4][WEP_LARGE_KEY_LEN]; u8 ssid_len; u8 short_preamble; __le16 beacon_period; } __packed; struct at76_command { u8 cmd; u8 reserved; __le16 size; u8 data[0]; } __packed; /* Length of Atmel-specific Rx header before 802.11 frame */ #define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet) struct at76_rx_buffer { __le16 wlength; u8 rx_rate; u8 newbss; u8 fragmentation; u8 rssi; u8 link_quality; u8 noise_level; __le32 rx_time; u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; } __packed; /* Length of Atmel-specific Tx header before 802.11 frame */ #define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet) struct at76_tx_buffer { __le16 wlength; u8 tx_rate; u8 padding; u8 reserved[4]; u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; } __packed; /* defines for scan_type below */ #define SCAN_TYPE_ACTIVE 0 #define SCAN_TYPE_PASSIVE 1 struct at76_req_scan { u8 bssid[ETH_ALEN]; u8 essid[32]; u8 scan_type; u8 channel; __le16 probe_delay; __le16 min_channel_time; __le16 max_channel_time; u8 essid_size; u8 international_scan; } __packed; struct at76_req_ibss { u8 bssid[ETH_ALEN]; u8 essid[32]; u8 bss_type; u8 channel; u8 essid_size; u8 reserved[3]; } __packed; struct at76_req_join { u8 bssid[ETH_ALEN]; u8 essid[32]; u8 bss_type; u8 channel; __le16 timeout; u8 essid_size; u8 reserved; } __packed; struct set_mib_buffer { u8 type; u8 size; u8 index; u8 reserved; union { u8 byte; __le16 word; u8 addr[ETH_ALEN]; } data; } __packed; struct mib_local { u16 reserved0; u8 beacon_enable; u8 txautorate_fallback; u8 reserved1; u8 ssid_size; u8 promiscuous_mode; u16 reserved2; u8 preamble_type; u16 reserved3; } __packed; struct mib_mac_addr { u8 mac_addr[ETH_ALEN]; u8 res[2]; /* ??? */ u8 group_addr[4][ETH_ALEN]; u8 group_addr_status[4]; } __packed; struct mib_mac { __le32 max_tx_msdu_lifetime; __le32 max_rx_lifetime; __le16 frag_threshold; __le16 rts_threshold; __le16 cwmin; __le16 cwmax; u8 short_retry_time; u8 long_retry_time; u8 scan_type; /* active or passive */ u8 scan_channel; __le16 probe_delay; /* delay before ProbeReq in active scan, RO */ __le16 min_channel_time; __le16 max_channel_time; __le16 listen_interval; u8 desired_ssid[32]; u8 desired_bssid[ETH_ALEN]; u8 desired_bsstype; /* ad-hoc or infrastructure */ u8 reserved2; } __packed; struct mib_mac_mgmt { __le16 beacon_period; __le16 CFP_max_duration; __le16 medium_occupancy_limit; __le16 station_id; /* assoc id */ __le16 ATIM_window; u8 CFP_mode; u8 privacy_option_implemented; u8 DTIM_period; u8 CFP_period; u8 current_bssid[ETH_ALEN]; u8 current_essid[32]; u8 current_bss_type; u8 power_mgmt_mode; /* rfmd and 505 */ u8 ibss_change; u8 res; u8 multi_domain_capability_implemented; u8 multi_domain_capability_enabled; u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; u8 reserved[3]; } __packed; struct mib_mac_wep { u8 privacy_invoked; /* 0 disable encr., 1 enable encr */ u8 wep_default_key_id; u8 wep_key_mapping_len; u8 exclude_unencrypted; __le32 wep_icv_error_count; __le32 wep_excluded_count; u8 wep_default_keyvalue[WEP_KEYS][WEP_LARGE_KEY_LEN]; u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */ } __packed; struct mib_phy { __le32 ed_threshold; __le16 slot_time; __le16 sifs_time; __le16 preamble_length; __le16 plcp_header_length; __le16 mpdu_max_length; __le16 cca_mode_supported; u8 operation_rate_set[4]; u8 channel_id; u8 current_cca_mode; u8 phy_type; u8 current_reg_domain; } __packed; struct mib_fw_version { u8 major; u8 minor; u8 patch; u8 build; } __packed; struct mib_mdomain { u8 tx_powerlevel[14]; u8 channel_list[14]; /* 0 for invalid channels */ } __packed; struct at76_fw_header { __le32 crc; /* CRC32 of the whole image */ __le32 board_type; /* firmware compatibility code */ u8 build; /* firmware build number */ u8 patch; /* firmware patch level */ u8 minor; /* firmware minor version */ u8 major; /* firmware major version */ __le32 str_offset; /* offset of the copyright string */ __le32 int_fw_offset; /* internal firmware image offset */ __le32 int_fw_len; /* internal firmware image length */ __le32 ext_fw_offset; /* external firmware image offset */ __le32 ext_fw_len; /* external firmware image length */ } __packed; /* a description of a regulatory domain and the allowed channels */ struct reg_domain { u16 code; char const *name; u32 channel_map; /* if bit N is set, channel (N+1) is allowed */ }; /* Data for one loaded firmware file */ struct fwentry { const char *const fwname; const struct firmware *fw; int extfw_size; int intfw_size; /* pointer to loaded firmware, no need to free */ u8 *extfw; /* external firmware, extfw_size bytes long */ u8 *intfw; /* internal firmware, intfw_size bytes long */ enum board_type board_type; /* board type */ struct mib_fw_version fw_version; int loaded; /* Loaded and parsed successfully */ }; struct at76_priv { struct usb_device *udev; /* USB device pointer */ struct sk_buff *rx_skb; /* skbuff for receiving data */ struct sk_buff *tx_skb; /* skbuff for transmitting data */ void *bulk_out_buffer; /* buffer for sending data */ struct urb *tx_urb; /* URB for sending data */ struct urb *rx_urb; /* URB for receiving data */ unsigned int tx_pipe; /* bulk out pipe */ unsigned int rx_pipe; /* bulk in pipe */ struct mutex mtx; /* locks this structure */ /* work queues */ struct work_struct work_set_promisc; struct work_struct work_submit_rx; struct work_struct work_join_bssid; struct delayed_work dwork_hw_scan; struct tasklet_struct rx_tasklet; /* the WEP stuff */ int wep_enabled; /* 1 if WEP is enabled */ int wep_key_id; /* key id to be used */ u8 wep_keys[WEP_KEYS][WEP_LARGE_KEY_LEN]; /* WEP keys */ u8 wep_keys_len[WEP_KEYS]; /* length of WEP keys */ int channel; int iw_mode; u8 bssid[ETH_ALEN]; u8 essid[IW_ESSID_MAX_SIZE]; int essid_size; int radio_on; int promisc; int preamble_type; /* 0 - long, 1 - short, 2 - auto */ int auth_mode; /* authentication type: 0 open, 1 shared key */ int txrate; /* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */ int frag_threshold; /* threshold for fragmentation of tx packets */ int rts_threshold; /* threshold for RTS mechanism */ int short_retry_limit; int scan_min_time; /* scan min channel time */ int scan_max_time; /* scan max channel time */ int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ int scan_need_any; /* if set, need to scan for any ESSID */ u16 assoc_id; /* current association ID, if associated */ u8 pm_mode; /* power management mode */ u32 pm_period; /* power management period in microseconds */ struct reg_domain const *domain; /* reg domain description */ /* These fields contain HW config provided by the device (not all of * these fields are used by all board types) */ u8 mac_addr[ETH_ALEN]; u8 regulatory_domain; struct at76_card_config card_config; enum board_type board_type; struct mib_fw_version fw_version; unsigned int device_unplugged:1; unsigned int netdev_registered:1; struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */ int beacon_period; /* period of mgmt beacons, Kus */ struct ieee80211_hw *hw; int mac80211_registered; }; #define AT76_SUPPORTED_FILTERS FIF_PROMISC_IN_BSS #define SCAN_POLL_INTERVAL (HZ / 4) #define CMD_COMPLETION_TIMEOUT (5 * HZ) #define DEF_RTS_THRESHOLD 1536 #define DEF_FRAG_THRESHOLD 1536 #define DEF_SHORT_RETRY_LIMIT 8 #define DEF_CHANNEL 10 #define DEF_SCAN_MIN_TIME 10 #define DEF_SCAN_MAX_TIME 120 /* the max padding size for tx in bytes (see calc_padding) */ #define MAX_PADDING_SIZE 53 #endif /* _AT76_USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/adm8211.h0000644000175000017500000004317312026211315021717 0ustar mcgrofmcgrof#ifndef ADM8211_H #define ADM8211_H /* ADM8211 Registers */ /* CR32 (SIG) signature */ #define ADM8211_SIG1 0x82011317 /* ADM8211A */ #define ADM8211_SIG2 0x82111317 /* ADM8211B/ADM8211C */ #define ADM8211_CSR_READ(r) ioread32(&priv->map->r) #define ADM8211_CSR_WRITE(r, val) iowrite32((val), &priv->map->r) /* CSR (Host Control and Status Registers) */ struct adm8211_csr { __le32 PAR; /* 0x00 CSR0 */ __le32 FRCTL; /* 0x04 CSR0A */ __le32 TDR; /* 0x08 CSR1 */ __le32 WTDP; /* 0x0C CSR1A */ __le32 RDR; /* 0x10 CSR2 */ __le32 WRDP; /* 0x14 CSR2A */ __le32 RDB; /* 0x18 CSR3 */ __le32 TDBH; /* 0x1C CSR3A */ __le32 TDBD; /* 0x20 CSR4 */ __le32 TDBP; /* 0x24 CSR4A */ __le32 STSR; /* 0x28 CSR5 */ __le32 TDBB; /* 0x2C CSR5A */ __le32 NAR; /* 0x30 CSR6 */ __le32 CSR6A; /* reserved */ __le32 IER; /* 0x38 CSR7 */ __le32 TKIPSCEP; /* 0x3C CSR7A */ __le32 LPC; /* 0x40 CSR8 */ __le32 CSR_TEST1; /* 0x44 CSR8A */ __le32 SPR; /* 0x48 CSR9 */ __le32 CSR_TEST0; /* 0x4C CSR9A */ __le32 WCSR; /* 0x50 CSR10 */ __le32 WPDR; /* 0x54 CSR10A */ __le32 GPTMR; /* 0x58 CSR11 */ __le32 GPIO; /* 0x5C CSR11A */ __le32 BBPCTL; /* 0x60 CSR12 */ __le32 SYNCTL; /* 0x64 CSR12A */ __le32 PLCPHD; /* 0x68 CSR13 */ __le32 MMIWA; /* 0x6C CSR13A */ __le32 MMIRD0; /* 0x70 CSR14 */ __le32 MMIRD1; /* 0x74 CSR14A */ __le32 TXBR; /* 0x78 CSR15 */ __le32 SYNDATA; /* 0x7C CSR15A */ __le32 ALCS; /* 0x80 CSR16 */ __le32 TOFS2; /* 0x84 CSR17 */ __le32 CMDR; /* 0x88 CSR18 */ __le32 PCIC; /* 0x8C CSR19 */ __le32 PMCSR; /* 0x90 CSR20 */ __le32 PAR0; /* 0x94 CSR21 */ __le32 PAR1; /* 0x98 CSR22 */ __le32 MAR0; /* 0x9C CSR23 */ __le32 MAR1; /* 0xA0 CSR24 */ __le32 ATIMDA0; /* 0xA4 CSR25 */ __le32 ABDA1; /* 0xA8 CSR26 */ __le32 BSSID0; /* 0xAC CSR27 */ __le32 TXLMT; /* 0xB0 CSR28 */ __le32 MIBCNT; /* 0xB4 CSR29 */ __le32 BCNT; /* 0xB8 CSR30 */ __le32 TSFTH; /* 0xBC CSR31 */ __le32 TSC; /* 0xC0 CSR32 */ __le32 SYNRF; /* 0xC4 CSR33 */ __le32 BPLI; /* 0xC8 CSR34 */ __le32 CAP0; /* 0xCC CSR35 */ __le32 CAP1; /* 0xD0 CSR36 */ __le32 RMD; /* 0xD4 CSR37 */ __le32 CFPP; /* 0xD8 CSR38 */ __le32 TOFS0; /* 0xDC CSR39 */ __le32 TOFS1; /* 0xE0 CSR40 */ __le32 IFST; /* 0xE4 CSR41 */ __le32 RSPT; /* 0xE8 CSR42 */ __le32 TSFTL; /* 0xEC CSR43 */ __le32 WEPCTL; /* 0xF0 CSR44 */ __le32 WESK; /* 0xF4 CSR45 */ __le32 WEPCNT; /* 0xF8 CSR46 */ __le32 MACTEST; /* 0xFC CSR47 */ __le32 FER; /* 0x100 */ __le32 FEMR; /* 0x104 */ __le32 FPSR; /* 0x108 */ __le32 FFER; /* 0x10C */ } __packed; /* CSR0 - PAR (PCI Address Register) */ #define ADM8211_PAR_MWIE (1 << 24) #define ADM8211_PAR_MRLE (1 << 23) #define ADM8211_PAR_MRME (1 << 21) #define ADM8211_PAR_RAP ((1 << 18) | (1 << 17)) #define ADM8211_PAR_CAL ((1 << 15) | (1 << 14)) #define ADM8211_PAR_PBL 0x00003f00 #define ADM8211_PAR_BLE (1 << 7) #define ADM8211_PAR_DSL 0x0000007c #define ADM8211_PAR_BAR (1 << 1) #define ADM8211_PAR_SWR (1 << 0) /* CSR1 - FRCTL (Frame Control Register) */ #define ADM8211_FRCTL_PWRMGT (1 << 31) #define ADM8211_FRCTL_MAXPSP (1 << 27) #define ADM8211_FRCTL_DRVPRSP (1 << 26) #define ADM8211_FRCTL_DRVBCON (1 << 25) #define ADM8211_FRCTL_AID 0x0000ffff #define ADM8211_FRCTL_AID_ON 0x0000c000 /* CSR5 - STSR (Status Register) */ #define ADM8211_STSR_PCF (1 << 31) #define ADM8211_STSR_BCNTC (1 << 30) #define ADM8211_STSR_GPINT (1 << 29) #define ADM8211_STSR_LinkOff (1 << 28) #define ADM8211_STSR_ATIMTC (1 << 27) #define ADM8211_STSR_TSFTF (1 << 26) #define ADM8211_STSR_TSCZ (1 << 25) #define ADM8211_STSR_LinkOn (1 << 24) #define ADM8211_STSR_SQL (1 << 23) #define ADM8211_STSR_WEPTD (1 << 22) #define ADM8211_STSR_ATIME (1 << 21) #define ADM8211_STSR_TBTT (1 << 20) #define ADM8211_STSR_NISS (1 << 16) #define ADM8211_STSR_AISS (1 << 15) #define ADM8211_STSR_TEIS (1 << 14) #define ADM8211_STSR_FBE (1 << 13) #define ADM8211_STSR_REIS (1 << 12) #define ADM8211_STSR_GPTT (1 << 11) #define ADM8211_STSR_RPS (1 << 8) #define ADM8211_STSR_RDU (1 << 7) #define ADM8211_STSR_RCI (1 << 6) #define ADM8211_STSR_TUF (1 << 5) #define ADM8211_STSR_TRT (1 << 4) #define ADM8211_STSR_TLT (1 << 3) #define ADM8211_STSR_TDU (1 << 2) #define ADM8211_STSR_TPS (1 << 1) #define ADM8211_STSR_TCI (1 << 0) /* CSR6 - NAR (Network Access Register) */ #define ADM8211_NAR_TXCF (1 << 31) #define ADM8211_NAR_HF (1 << 30) #define ADM8211_NAR_UTR (1 << 29) #define ADM8211_NAR_SQ (1 << 28) #define ADM8211_NAR_CFP (1 << 27) #define ADM8211_NAR_SF (1 << 21) #define ADM8211_NAR_TR ((1 << 15) | (1 << 14)) #define ADM8211_NAR_ST (1 << 13) #define ADM8211_NAR_OM ((1 << 11) | (1 << 10)) #define ADM8211_NAR_MM (1 << 7) #define ADM8211_NAR_PR (1 << 6) #define ADM8211_NAR_EA (1 << 5) #define ADM8211_NAR_PB (1 << 3) #define ADM8211_NAR_STPDMA (1 << 2) #define ADM8211_NAR_SR (1 << 1) #define ADM8211_NAR_CTX (1 << 0) #define ADM8211_IDLE() \ do { \ if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) { \ ADM8211_CSR_WRITE(NAR, priv->nar & \ ~(ADM8211_NAR_SR | ADM8211_NAR_ST));\ ADM8211_CSR_READ(NAR); \ msleep(20); \ } \ } while (0) #define ADM8211_IDLE_RX() \ do { \ if (priv->nar & ADM8211_NAR_SR) { \ ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR); \ ADM8211_CSR_READ(NAR); \ mdelay(20); \ } \ } while (0) #define ADM8211_RESTORE() \ do { \ if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) \ ADM8211_CSR_WRITE(NAR, priv->nar); \ } while (0) /* CSR7 - IER (Interrupt Enable Register) */ #define ADM8211_IER_PCFIE (1 << 31) #define ADM8211_IER_BCNTCIE (1 << 30) #define ADM8211_IER_GPIE (1 << 29) #define ADM8211_IER_LinkOffIE (1 << 28) #define ADM8211_IER_ATIMTCIE (1 << 27) #define ADM8211_IER_TSFTFIE (1 << 26) #define ADM8211_IER_TSCZE (1 << 25) #define ADM8211_IER_LinkOnIE (1 << 24) #define ADM8211_IER_SQLIE (1 << 23) #define ADM8211_IER_WEPIE (1 << 22) #define ADM8211_IER_ATIMEIE (1 << 21) #define ADM8211_IER_TBTTIE (1 << 20) #define ADM8211_IER_NIE (1 << 16) #define ADM8211_IER_AIE (1 << 15) #define ADM8211_IER_TEIE (1 << 14) #define ADM8211_IER_FBEIE (1 << 13) #define ADM8211_IER_REIE (1 << 12) #define ADM8211_IER_GPTIE (1 << 11) #define ADM8211_IER_RSIE (1 << 8) #define ADM8211_IER_RUIE (1 << 7) #define ADM8211_IER_RCIE (1 << 6) #define ADM8211_IER_TUIE (1 << 5) #define ADM8211_IER_TRTIE (1 << 4) #define ADM8211_IER_TLTTIE (1 << 3) #define ADM8211_IER_TDUIE (1 << 2) #define ADM8211_IER_TPSIE (1 << 1) #define ADM8211_IER_TCIE (1 << 0) /* CSR9 - SPR (Serial Port Register) */ #define ADM8211_SPR_SRS (1 << 11) #define ADM8211_SPR_SDO (1 << 3) #define ADM8211_SPR_SDI (1 << 2) #define ADM8211_SPR_SCLK (1 << 1) #define ADM8211_SPR_SCS (1 << 0) /* CSR9A - CSR_TEST0 */ #define ADM8211_CSR_TEST0_EPNE (1 << 18) #define ADM8211_CSR_TEST0_EPSNM (1 << 17) #define ADM8211_CSR_TEST0_EPTYP (1 << 16) #define ADM8211_CSR_TEST0_EPRLD (1 << 15) /* CSR10 - WCSR (Wake-up Control/Status Register) */ #define ADM8211_WCSR_CRCT (1 << 30) #define ADM8211_WCSR_TSFTWE (1 << 20) #define ADM8211_WCSR_TIMWE (1 << 19) #define ADM8211_WCSR_ATIMWE (1 << 18) #define ADM8211_WCSR_KEYWE (1 << 17) #define ADM8211_WCSR_MPRE (1 << 9) #define ADM8211_WCSR_LSOE (1 << 8) #define ADM8211_WCSR_KEYUP (1 << 6) #define ADM8211_WCSR_TSFTW (1 << 5) #define ADM8211_WCSR_TIMW (1 << 4) #define ADM8211_WCSR_ATIMW (1 << 3) #define ADM8211_WCSR_MPR (1 << 1) #define ADM8211_WCSR_LSO (1 << 0) /* CSR11A - GPIO */ #define ADM8211_CSR_GPIO_EN5 (1 << 17) #define ADM8211_CSR_GPIO_EN4 (1 << 16) #define ADM8211_CSR_GPIO_EN3 (1 << 15) #define ADM8211_CSR_GPIO_EN2 (1 << 14) #define ADM8211_CSR_GPIO_EN1 (1 << 13) #define ADM8211_CSR_GPIO_EN0 (1 << 12) #define ADM8211_CSR_GPIO_O5 (1 << 11) #define ADM8211_CSR_GPIO_O4 (1 << 10) #define ADM8211_CSR_GPIO_O3 (1 << 9) #define ADM8211_CSR_GPIO_O2 (1 << 8) #define ADM8211_CSR_GPIO_O1 (1 << 7) #define ADM8211_CSR_GPIO_O0 (1 << 6) #define ADM8211_CSR_GPIO_IN 0x0000003f /* CSR12 - BBPCTL (BBP Control port) */ #define ADM8211_BBPCTL_MMISEL (1 << 31) #define ADM8211_BBPCTL_SPICADD (0x7F << 24) #define ADM8211_BBPCTL_RF3000 (0x20 << 24) #define ADM8211_BBPCTL_TXCE (1 << 23) #define ADM8211_BBPCTL_RXCE (1 << 22) #define ADM8211_BBPCTL_CCAP (1 << 21) #define ADM8211_BBPCTL_TYPE 0x001c0000 #define ADM8211_BBPCTL_WR (1 << 17) #define ADM8211_BBPCTL_RD (1 << 16) #define ADM8211_BBPCTL_ADDR 0x0000ff00 #define ADM8211_BBPCTL_DATA 0x000000ff /* CSR12A - SYNCTL (Synthesizer Control port) */ #define ADM8211_SYNCTL_WR (1 << 31) #define ADM8211_SYNCTL_RD (1 << 30) #define ADM8211_SYNCTL_CS0 (1 << 29) #define ADM8211_SYNCTL_CS1 (1 << 28) #define ADM8211_SYNCTL_CAL (1 << 27) #define ADM8211_SYNCTL_SELCAL (1 << 26) #define ADM8211_SYNCTL_RFtype ((1 << 24) | (1 << 23) | (1 << 22)) #define ADM8211_SYNCTL_RFMD (1 << 22) #define ADM8211_SYNCTL_GENERAL (0x7 << 22) /* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */ /* CSR18 - CMDR (Command Register) */ #define ADM8211_CMDR_PM (1 << 19) #define ADM8211_CMDR_APM (1 << 18) #define ADM8211_CMDR_RTE (1 << 4) #define ADM8211_CMDR_DRT ((1 << 3) | (1 << 2)) #define ADM8211_CMDR_DRT_8DW (0x0 << 2) #define ADM8211_CMDR_DRT_16DW (0x1 << 2) #define ADM8211_CMDR_DRT_SF (0x2 << 2) /* CSR33 - SYNRF (SYNRF direct control) */ #define ADM8211_SYNRF_SELSYN (1 << 31) #define ADM8211_SYNRF_SELRF (1 << 30) #define ADM8211_SYNRF_LERF (1 << 29) #define ADM8211_SYNRF_LEIF (1 << 28) #define ADM8211_SYNRF_SYNCLK (1 << 27) #define ADM8211_SYNRF_SYNDATA (1 << 26) #define ADM8211_SYNRF_PE1 (1 << 25) #define ADM8211_SYNRF_PE2 (1 << 24) #define ADM8211_SYNRF_PA_PE (1 << 23) #define ADM8211_SYNRF_TR_SW (1 << 22) #define ADM8211_SYNRF_TR_SWN (1 << 21) #define ADM8211_SYNRF_RADIO (1 << 20) #define ADM8211_SYNRF_CAL_EN (1 << 19) #define ADM8211_SYNRF_PHYRST (1 << 18) #define ADM8211_SYNRF_IF_SELECT_0 (1 << 31) #define ADM8211_SYNRF_IF_SELECT_1 ((1 << 31) | (1 << 28)) #define ADM8211_SYNRF_WRITE_SYNDATA_0 (1 << 31) #define ADM8211_SYNRF_WRITE_SYNDATA_1 ((1 << 31) | (1 << 26)) #define ADM8211_SYNRF_WRITE_CLOCK_0 (1 << 31) #define ADM8211_SYNRF_WRITE_CLOCK_1 ((1 << 31) | (1 << 27)) /* CSR44 - WEPCTL (WEP Control) */ #define ADM8211_WEPCTL_WEPENABLE (1 << 31) #define ADM8211_WEPCTL_WPAENABLE (1 << 30) #define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29) #define ADM8211_WEPCTL_TABLE_WR (1 << 28) #define ADM8211_WEPCTL_TABLE_RD (1 << 27) #define ADM8211_WEPCTL_WEPRXBYP (1 << 25) #define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23) #define ADM8211_WEPCTL_ADDR (0x000001ff) /* CSR45 - WESK (Data Entry for Share/Individual Key) */ #define ADM8211_WESK_DATA (0x0000ffff) /* FER (Function Event Register) */ #define ADM8211_FER_INTR_EV_ENT (1 << 15) /* Si4126 RF Synthesizer - Control Registers */ #define SI4126_MAIN_CONF 0 #define SI4126_PHASE_DET_GAIN 1 #define SI4126_POWERDOWN 2 #define SI4126_RF1_N_DIV 3 /* only Si4136 */ #define SI4126_RF2_N_DIV 4 #define SI4126_IF_N_DIV 5 #define SI4126_RF1_R_DIV 6 /* only Si4136 */ #define SI4126_RF2_R_DIV 7 #define SI4126_IF_R_DIV 8 /* Main Configuration */ #define SI4126_MAIN_XINDIV2 (1 << 6) #define SI4126_MAIN_IFDIV ((1 << 11) | (1 << 10)) /* Powerdown */ #define SI4126_POWERDOWN_PDIB (1 << 1) #define SI4126_POWERDOWN_PDRB (1 << 0) /* RF3000 BBP - Control Port Registers */ /* 0x00 - reserved */ #define RF3000_MODEM_CTRL__RX_STATUS 0x01 #define RF3000_CCA_CTRL 0x02 #define RF3000_DIVERSITY__RSSI 0x03 #define RF3000_RX_SIGNAL_FIELD 0x04 #define RF3000_RX_LEN_MSB 0x05 #define RF3000_RX_LEN_LSB 0x06 #define RF3000_RX_SERVICE_FIELD 0x07 #define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11 #define RF3000_TX_LEN_MSB 0x12 #define RF3000_TX_LEN_LSB 0x13 #define RF3000_LOW_GAIN_CALIB 0x14 #define RF3000_HIGH_GAIN_CALIB 0x15 /* ADM8211 revisions */ #define ADM8211_REV_AB 0x11 #define ADM8211_REV_AF 0x15 #define ADM8211_REV_BA 0x20 #define ADM8211_REV_CA 0x30 struct adm8211_desc { __le32 status; __le32 length; __le32 buffer1; __le32 buffer2; }; #define RDES0_STATUS_OWN (1 << 31) #define RDES0_STATUS_ES (1 << 30) #define RDES0_STATUS_SQL (1 << 29) #define RDES0_STATUS_DE (1 << 28) #define RDES0_STATUS_FS (1 << 27) #define RDES0_STATUS_LS (1 << 26) #define RDES0_STATUS_PCF (1 << 25) #define RDES0_STATUS_SFDE (1 << 24) #define RDES0_STATUS_SIGE (1 << 23) #define RDES0_STATUS_CRC16E (1 << 22) #define RDES0_STATUS_RXTOE (1 << 21) #define RDES0_STATUS_CRC32E (1 << 20) #define RDES0_STATUS_ICVE (1 << 19) #define RDES0_STATUS_DA1 (1 << 17) #define RDES0_STATUS_DA0 (1 << 16) #define RDES0_STATUS_RXDR ((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12)) #define RDES0_STATUS_FL (0x00000fff) #define RDES1_CONTROL_RER (1 << 25) #define RDES1_CONTROL_RCH (1 << 24) #define RDES1_CONTROL_RBS2 (0x00fff000) #define RDES1_CONTROL_RBS1 (0x00000fff) #define RDES1_STATUS_RSSI (0x0000007f) #define TDES0_CONTROL_OWN (1 << 31) #define TDES0_CONTROL_DONE (1 << 30) #define TDES0_CONTROL_TXDR (0x0ff00000) #define TDES0_STATUS_OWN (1 << 31) #define TDES0_STATUS_DONE (1 << 30) #define TDES0_STATUS_ES (1 << 29) #define TDES0_STATUS_TLT (1 << 28) #define TDES0_STATUS_TRT (1 << 27) #define TDES0_STATUS_TUF (1 << 26) #define TDES0_STATUS_TRO (1 << 25) #define TDES0_STATUS_SOFBR (1 << 24) #define TDES0_STATUS_ACR (0x00000fff) #define TDES1_CONTROL_IC (1 << 31) #define TDES1_CONTROL_LS (1 << 30) #define TDES1_CONTROL_FS (1 << 29) #define TDES1_CONTROL_TER (1 << 25) #define TDES1_CONTROL_TCH (1 << 24) #define TDES1_CONTROL_RBS2 (0x00fff000) #define TDES1_CONTROL_RBS1 (0x00000fff) /* SRAM offsets */ #define ADM8211_SRAM(x) (priv->pdev->revision < ADM8211_REV_BA ? \ ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x) #define ADM8211_SRAM_INDIV_KEY 0x0000 #define ADM8211_SRAM_A_SHARE_KEY 0x0160 #define ADM8211_SRAM_B_SHARE_KEY 0x00c0 #define ADM8211_SRAM_A_SSID 0x0180 #define ADM8211_SRAM_B_SSID 0x00d4 #define ADM8211_SRAM_SSID ADM8211_SRAM(SSID) #define ADM8211_SRAM_A_SUPP_RATE 0x0191 #define ADM8211_SRAM_B_SUPP_RATE 0x00dd #define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE) #define ADM8211_SRAM_A_SIZE 0x0200 #define ADM8211_SRAM_B_SIZE 0x01c0 #define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE) struct adm8211_rx_ring_info { struct sk_buff *skb; dma_addr_t mapping; }; struct adm8211_tx_ring_info { struct sk_buff *skb; dma_addr_t mapping; size_t hdrlen; }; #define PLCP_SIGNAL_1M 0x0a #define PLCP_SIGNAL_2M 0x14 #define PLCP_SIGNAL_5M5 0x37 #define PLCP_SIGNAL_11M 0x6e struct adm8211_tx_hdr { u8 da[6]; u8 signal; /* PLCP signal / TX rate in 100 Kbps */ u8 service; __le16 frame_body_size; __le16 frame_control; __le16 plcp_frag_tail_len; __le16 plcp_frag_head_len; __le16 dur_frag_tail; __le16 dur_frag_head; u8 addr4[6]; #define ADM8211_TXHDRCTL_SHORT_PREAMBLE (1 << 0) #define ADM8211_TXHDRCTL_MORE_FRAG (1 << 1) #define ADM8211_TXHDRCTL_MORE_DATA (1 << 2) #define ADM8211_TXHDRCTL_FRAG_NO (1 << 3) /* ? */ #define ADM8211_TXHDRCTL_ENABLE_RTS (1 << 4) #define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE (1 << 5) #define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER (1 << 15) /* ? */ __le16 header_control; __le16 frag; u8 reserved_0; u8 retry_limit; u32 wep2key0; u32 wep2key1; u32 wep2key2; u32 wep2key3; u8 keyid; u8 entry_control; // huh?? u16 reserved_1; u32 reserved_2; } __packed; #define RX_COPY_BREAK 128 #define RX_PKT_SIZE 2500 struct adm8211_eeprom { __le16 signature; /* 0x00 */ u8 major_version; /* 0x02 */ u8 minor_version; /* 0x03 */ u8 reserved_1[4]; /* 0x04 */ u8 hwaddr[6]; /* 0x08 */ u8 reserved_2[8]; /* 0x1E */ __le16 cr49; /* 0x16 */ u8 cr03; /* 0x18 */ u8 cr28; /* 0x19 */ u8 cr29; /* 0x1A */ u8 country_code; /* 0x1B */ /* specific bbp types */ #define ADM8211_BBP_RFMD3000 0x00 #define ADM8211_BBP_RFMD3002 0x01 #define ADM8211_BBP_ADM8011 0x04 u8 specific_bbptype; /* 0x1C */ u8 specific_rftype; /* 0x1D */ u8 reserved_3[2]; /* 0x1E */ __le16 device_id; /* 0x20 */ __le16 vendor_id; /* 0x22 */ __le16 subsystem_id; /* 0x24 */ __le16 subsystem_vendor_id; /* 0x26 */ u8 maxlat; /* 0x28 */ u8 mingnt; /* 0x29 */ __le16 cis_pointer_low; /* 0x2A */ __le16 cis_pointer_high; /* 0x2C */ __le16 csr18; /* 0x2E */ u8 reserved_4[16]; /* 0x30 */ u8 d1_pwrdara; /* 0x40 */ u8 d0_pwrdara; /* 0x41 */ u8 d3_pwrdara; /* 0x42 */ u8 d2_pwrdara; /* 0x43 */ u8 antenna_power[14]; /* 0x44 */ __le16 cis_wordcnt; /* 0x52 */ u8 tx_power[14]; /* 0x54 */ u8 lpf_cutoff[14]; /* 0x62 */ u8 lnags_threshold[14]; /* 0x70 */ __le16 checksum; /* 0x7E */ u8 cis_data[0]; /* 0x80, 384 bytes */ } __packed; struct adm8211_priv { struct pci_dev *pdev; spinlock_t lock; struct adm8211_csr __iomem *map; struct adm8211_desc *rx_ring; struct adm8211_desc *tx_ring; dma_addr_t rx_ring_dma; dma_addr_t tx_ring_dma; struct adm8211_rx_ring_info *rx_buffers; struct adm8211_tx_ring_info *tx_buffers; unsigned int rx_ring_size, tx_ring_size; unsigned int cur_tx, dirty_tx, cur_rx; struct ieee80211_low_level_stats stats; struct ieee80211_supported_band band; struct ieee80211_channel channels[14]; int mode; int channel; u8 bssid[ETH_ALEN]; u8 soft_rx_crc; u8 retry_limit; u8 ant_power; u8 tx_power; u8 lpf_cutoff; u8 lnags_threshold; struct adm8211_eeprom *eeprom; size_t eeprom_len; u32 nar; #define ADM8211_TYPE_INTERSIL 0x00 #define ADM8211_TYPE_RFMD 0x01 #define ADM8211_TYPE_MARVEL 0x02 #define ADM8211_TYPE_AIROHA 0x03 #define ADM8211_TYPE_ADMTEK 0x05 unsigned int rf_type:3; unsigned int bbp_type:3; u8 specific_bbptype; enum { ADM8211_RFMD2948 = 0x0, ADM8211_RFMD2958 = 0x1, ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2, ADM8211_MAX2820 = 0x8, ADM8211_AL2210L = 0xC, /* Airoha */ } transceiver_type; }; struct ieee80211_chan_range { u8 min; u8 max; }; static const struct ieee80211_chan_range cranges[] = { {1, 11}, /* FCC */ {1, 11}, /* IC */ {1, 13}, /* ETSI */ {10, 11}, /* SPAIN */ {10, 13}, /* FRANCE */ {14, 14}, /* MMK */ {1, 14}, /* MMK2 */ }; #endif /* ADM8211_H */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/0000755000175000017500000000000012026211315022131 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/mwifiex/init.c0000644000175000017500000005344312026211315023251 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: HW/FW Initialization * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* * This function adds a BSS priority table to the table list. * * The function allocates a new BSS priority table node and adds it to * the end of BSS priority table list, kept in driver memory. */ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) { struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bss_prio_node *bss_prio; struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; unsigned long flags; bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); if (!bss_prio) { dev_err(adapter->dev, "%s: failed to alloc bss_prio\n", __func__); return -ENOMEM; } bss_prio->priv = priv; INIT_LIST_HEAD(&bss_prio->list); if (!tbl[priv->bss_priority].bss_prio_cur) tbl[priv->bss_priority].bss_prio_cur = bss_prio; spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head); spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); return 0; } static void scan_delay_timer_fn(unsigned long data) { struct mwifiex_private *priv = (struct mwifiex_private *)data; struct mwifiex_adapter *adapter = priv->adapter; struct cmd_ctrl_node *cmd_node, *tmp_node; unsigned long flags; if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { /* * Abort scan operation by cancelling all pending scan * commands */ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); mwifiex_insert_cmd_to_free_q(adapter, cmd_node); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = false; adapter->scan_delay_cnt = 0; adapter->empty_tx_q_cnt = 0; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); if (priv->user_scan_cfg) { dev_dbg(priv->adapter->dev, "info: %s: scan aborted\n", __func__); cfg80211_scan_done(priv->scan_request, 1); priv->scan_request = NULL; kfree(priv->user_scan_cfg); priv->user_scan_cfg = NULL; } if (priv->scan_pending_on_block) { priv->scan_pending_on_block = false; up(&priv->async_sem); } goto done; } if (!atomic_read(&priv->adapter->is_tx_received)) { adapter->empty_tx_q_cnt++; if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) { /* * No Tx traffic for 200msec. Get scan command from * scan pending queue and put to cmd pending queue to * resume scan operation */ adapter->scan_delay_cnt = 0; adapter->empty_tx_q_cnt = 0; spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); cmd_node = list_first_entry(&adapter->scan_pending_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); goto done; } } else { adapter->empty_tx_q_cnt = 0; } /* Delay scan operation further by 20msec */ mod_timer(&priv->scan_delay_timer, jiffies + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); adapter->scan_delay_cnt++; done: if (atomic_read(&priv->adapter->is_tx_received)) atomic_set(&priv->adapter->is_tx_received, false); return; } /* * This function initializes the private structure and sets default * values to the members. * * Additionally, it also initializes all the locks and sets up all the * lists. */ static int mwifiex_init_priv(struct mwifiex_private *priv) { u32 i; priv->media_connected = false; memset(priv->curr_addr, 0xff, ETH_ALEN); priv->pkt_tx_ctrl = 0; priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; priv->data_rate = 0; /* Initially indicate the rate as auto */ priv->is_data_rate_auto = true; priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; priv->sec_info.wep_enabled = 0; priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; priv->sec_info.encryption_mode = 0; for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key)); priv->wep_key_curr_index = 0; priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | HostCmd_ACT_MAC_ETHERNETII_ENABLE; priv->beacon_period = 100; /* beacon interval */ ; priv->attempted_bss_desc = NULL; memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); priv->assoc_rsp_size = 0; priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; priv->atim_window = 0; priv->adhoc_state = ADHOC_IDLE; priv->tx_power_level = 0; priv->max_tx_power_level = 0; priv->min_tx_power_level = 0; priv->tx_rate = 0; priv->rxpd_htinfo = 0; priv->rxpd_rate = 0; priv->rate_bitmap = 0; priv->data_rssi_last = 0; priv->data_rssi_avg = 0; priv->data_nf_avg = 0; priv->data_nf_last = 0; priv->bcn_rssi_last = 0; priv->bcn_rssi_avg = 0; priv->bcn_nf_avg = 0; priv->bcn_nf_last = 0; memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); memset(&priv->aes_key, 0, sizeof(priv->aes_key)); priv->wpa_ie_len = 0; priv->wpa_is_gtk_set = false; memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); priv->assoc_tlv_buf_len = 0; memset(&priv->wps, 0, sizeof(priv->wps)); memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); priv->gen_ie_buf_len = 0; memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); priv->wmm_required = true; priv->wmm_enabled = false; priv->wmm_qosinfo = 0; priv->curr_bcn_buf = NULL; priv->curr_bcn_size = 0; priv->wps_ie = NULL; priv->wps_ie_len = 0; priv->ap_11n_enabled = 0; priv->scan_block = false; setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn, (unsigned long)priv); return mwifiex_add_bss_prio_tbl(priv); } /* * This function allocates buffers for members of the adapter * structure. * * The memory allocated includes scan table, command buffers, and * sleep confirm command buffer. In addition, the queues are * also initialized. */ static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) { int ret; /* Allocate command buffer */ ret = mwifiex_alloc_cmd_buffer(adapter); if (ret) { dev_err(adapter->dev, "%s: failed to alloc cmd buffer\n", __func__); return -1; } adapter->sleep_cfm = dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) + INTF_HEADER_LEN); if (!adapter->sleep_cfm) { dev_err(adapter->dev, "%s: failed to alloc sleep cfm" " cmd buffer\n", __func__); return -1; } skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); return 0; } /* * This function initializes the adapter structure and sets default * values to the members of adapter. * * This also initializes the WMM related parameters in the driver private * structures. */ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) { struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL; skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm)); adapter->cmd_sent = false; if (adapter->iface_type == MWIFIEX_SDIO) adapter->data_sent = true; else adapter->data_sent = false; adapter->cmd_resp_received = false; adapter->event_received = false; adapter->data_received = false; adapter->surprise_removed = false; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; adapter->ps_state = PS_STATE_AWAKE; adapter->need_to_wakeup = false; adapter->scan_mode = HostCmd_BSS_MODE_ANY; adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME; adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME; adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME; adapter->scan_probes = 1; adapter->multiple_dtim = 1; adapter->local_listen_interval = 0; /* default value in firmware will be used */ adapter->is_deep_sleep = false; adapter->delay_null_pkt = false; adapter->delay_to_ps = 1000; adapter->enhanced_ps_mode = PS_MODE_AUTO; adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by default */ adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by default */ adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; adapter->max_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; adapter->is_hs_configured = false; adapter->hs_cfg.conditions = cpu_to_le32(HOST_SLEEP_CFG_COND_DEF); adapter->hs_cfg.gpio = HOST_SLEEP_CFG_GPIO_DEF; adapter->hs_cfg.gap = HOST_SLEEP_CFG_GAP_DEF; adapter->hs_activated = false; memset(adapter->event_body, 0, sizeof(adapter->event_body)); adapter->hw_dot_11n_dev_cap = 0; adapter->hw_dev_mcs_support = 0; adapter->sec_chan_offset = 0; adapter->adhoc_11n_enabled = false; mwifiex_wmm_init(adapter); if (adapter->sleep_cfm) { sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) adapter->sleep_cfm->data; memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len); sleep_cfm_buf->result = 0; sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); } memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); adapter->tx_lock_flag = false; adapter->null_pkt_interval = 0; adapter->fw_bands = 0; adapter->config_bands = 0; adapter->adhoc_start_band = 0; adapter->scan_channels = NULL; adapter->fw_release_number = 0; adapter->fw_cap_info = 0; memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); adapter->event_cause = 0; adapter->region_code = 0; adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; adapter->adhoc_awake_period = 0; memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); adapter->arp_filter_size = 0; adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; adapter->empty_tx_q_cnt = 0; } /* * This function sets trans_start per tx_queue */ void mwifiex_set_trans_start(struct net_device *dev) { int i; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) for (i = 0; i < dev->num_tx_queues; i++) netdev_get_tx_queue(dev, i)->trans_start = jiffies; #endif dev->trans_start = jiffies; } /* * This function wakes up all queues in net_device */ void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { unsigned long dev_queue_flags; spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); netif_tx_wake_all_queues(netdev); spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); } /* * This function stops all queues in net_device */ void mwifiex_stop_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { unsigned long dev_queue_flags; spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); netif_tx_stop_all_queues(netdev); spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); } /* * This function releases the lock variables and frees the locks and * associated locks. */ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; s32 i, j; /* Free lists */ list_del(&adapter->cmd_free_q); list_del(&adapter->cmd_pending_q); list_del(&adapter->scan_pending_q); for (i = 0; i < adapter->priv_num; i++) list_del(&adapter->bss_prio_tbl[i].bss_prio_head); for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { priv = adapter->priv[i]; for (j = 0; j < MAX_NUM_TID; ++j) list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); list_del(&priv->tx_ba_stream_tbl_ptr); list_del(&priv->rx_reorder_tbl_ptr); list_del(&priv->sta_list); } } } /* * This function frees the adapter structure. * * The freeing operation is done recursively, by canceling all * pending commands, freeing the member buffers previously * allocated (command buffers, scan table buffer, sleep confirm * command buffer), stopping the timers and calling the cleanup * routines for every interface, before the actual adapter * structure is freed. */ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) { if (!adapter) { pr_err("%s: adapter is NULL\n", __func__); return; } mwifiex_cancel_all_pending_cmd(adapter); /* Free lock variables */ mwifiex_free_lock_list(adapter); /* Free command buffer */ dev_dbg(adapter->dev, "info: free cmd buffer\n"); mwifiex_free_cmd_buffer(adapter); del_timer(&adapter->cmd_timer); dev_dbg(adapter->dev, "info: free scan table\n"); if (adapter->if_ops.cleanup_if) adapter->if_ops.cleanup_if(adapter); if (adapter->sleep_cfm) dev_kfree_skb_any(adapter->sleep_cfm); } /* * This function intializes the lock variables and * the list heads. */ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; s32 i, j; spin_lock_init(&adapter->mwifiex_lock); spin_lock_init(&adapter->int_lock); spin_lock_init(&adapter->main_proc_lock); spin_lock_init(&adapter->mwifiex_cmd_lock); spin_lock_init(&adapter->queue_lock); for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { priv = adapter->priv[i]; spin_lock_init(&priv->rx_pkt_lock); spin_lock_init(&priv->wmm.ra_list_spinlock); spin_lock_init(&priv->curr_bcn_buf_lock); spin_lock_init(&priv->sta_list_spinlock); } } /* Initialize cmd_free_q */ INIT_LIST_HEAD(&adapter->cmd_free_q); /* Initialize cmd_pending_q */ INIT_LIST_HEAD(&adapter->cmd_pending_q); /* Initialize scan_pending_q */ INIT_LIST_HEAD(&adapter->scan_pending_q); spin_lock_init(&adapter->cmd_free_q_lock); spin_lock_init(&adapter->cmd_pending_q_lock); spin_lock_init(&adapter->scan_pending_q_lock); skb_queue_head_init(&adapter->usb_rx_data_q); for (i = 0; i < adapter->priv_num; ++i) { INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); adapter->bss_prio_tbl[i].bss_prio_cur = NULL; spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); } for (i = 0; i < adapter->priv_num; i++) { if (!adapter->priv[i]) continue; priv = adapter->priv[i]; for (j = 0; j < MAX_NUM_TID; ++j) { INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); spin_lock_init(&priv->wmm.tid_tbl_ptr[j].tid_tbl_lock); } INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); INIT_LIST_HEAD(&priv->sta_list); spin_lock_init(&priv->tx_ba_stream_tbl_lock); spin_lock_init(&priv->rx_reorder_tbl_lock); } return 0; } /* * This function initializes the firmware. * * The following operations are performed sequentially - * - Allocate adapter structure * - Initialize the adapter structure * - Initialize the private structure * - Add BSS priority tables to the adapter structure * - For each interface, send the init commands to firmware * - Send the first command in command pending queue, if available */ int mwifiex_init_fw(struct mwifiex_adapter *adapter) { int ret; struct mwifiex_private *priv; u8 i, first_sta = true; int is_cmd_pend_q_empty; unsigned long flags; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; /* Allocate memory for member of adapter structure */ ret = mwifiex_allocate_adapter(adapter); if (ret) return -1; /* Initialize adapter structure */ mwifiex_init_adapter(adapter); for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { priv = adapter->priv[i]; /* Initialize private structure */ ret = mwifiex_init_priv(priv); if (ret) return -1; } } for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta); if (ret == -1) return -1; first_sta = false; } } spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); if (!is_cmd_pend_q_empty) { /* Send the first command in queue and return */ if (mwifiex_main_process(adapter) != -1) ret = -EINPROGRESS; } else { adapter->hw_status = MWIFIEX_HW_STATUS_READY; } return ret; } /* * This function deletes the BSS priority tables. * * The function traverses through all the allocated BSS priority nodes * in every BSS priority table and frees them. */ static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) { int i; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bss_prio_node *bssprio_node, *tmp_node, **cur; struct list_head *head; spinlock_t *lock; /* bss priority lock */ unsigned long flags; for (i = 0; i < adapter->priv_num; ++i) { head = &adapter->bss_prio_tbl[i].bss_prio_head; cur = &adapter->bss_prio_tbl[i].bss_prio_cur; lock = &adapter->bss_prio_tbl[i].bss_prio_lock; dev_dbg(adapter->dev, "info: delete BSS priority table," " bss_type = %d, bss_num = %d, i = %d," " head = %p, cur = %p\n", priv->bss_type, priv->bss_num, i, head, *cur); if (*cur) { spin_lock_irqsave(lock, flags); if (list_empty(head)) { spin_unlock_irqrestore(lock, flags); continue; } bssprio_node = list_first_entry(head, struct mwifiex_bss_prio_node, list); spin_unlock_irqrestore(lock, flags); list_for_each_entry_safe(bssprio_node, tmp_node, head, list) { if (bssprio_node->priv == priv) { dev_dbg(adapter->dev, "info: Delete " "node %p, next = %p\n", bssprio_node, tmp_node); spin_lock_irqsave(lock, flags); list_del(&bssprio_node->list); spin_unlock_irqrestore(lock, flags); kfree(bssprio_node); } } *cur = (struct mwifiex_bss_prio_node *)head; } } } /* * This function is used to shutdown the driver. * * The following operations are performed sequentially - * - Check if already shut down * - Make sure the main process has stopped * - Clean up the Tx and Rx queues * - Delete BSS priority tables * - Free the adapter * - Notify completion */ int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) { int ret = -EINPROGRESS; struct mwifiex_private *priv; s32 i; unsigned long flags; struct sk_buff *skb; /* mwifiex already shutdown */ if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) return 0; adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING; /* wait for mwifiex_process to complete */ if (adapter->mwifiex_processing) { dev_warn(adapter->dev, "main process is still running\n"); return ret; } /* shut down mwifiex */ dev_dbg(adapter->dev, "info: shutdown mwifiex...\n"); /* Clean up Tx/Rx queues and delete BSS priority table */ for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { priv = adapter->priv[i]; mwifiex_clean_txrx(priv); mwifiex_delete_bss_prio_tbl(priv); } } spin_lock_irqsave(&adapter->mwifiex_lock, flags); if (adapter->if_ops.data_complete) { while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) { struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); priv = adapter->priv[rx_info->bss_num]; if (priv) priv->stats.rx_dropped++; adapter->if_ops.data_complete(adapter, skb); } } /* Free adapter structure */ mwifiex_free_adapter(adapter); spin_unlock_irqrestore(&adapter->mwifiex_lock, flags); /* Notify completion */ ret = mwifiex_shutdown_fw_complete(adapter); return ret; } /* * This function downloads the firmware to the card. * * The actual download is preceded by two sanity checks - * - Check if firmware is already running * - Check if the interface is the winner to download the firmware * * ...and followed by another - * - Check if the firmware is downloaded successfully * * After download is successfully completed, the host interrupts are enabled. */ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *pmfw) { int ret; u32 poll_num = 1; if (adapter->if_ops.check_fw_status) { adapter->winner = 0; /* check if firmware is already running */ ret = adapter->if_ops.check_fw_status(adapter, poll_num); if (!ret) { dev_notice(adapter->dev, "WLAN FW already running! Skip FW dnld\n"); goto done; } poll_num = MAX_FIRMWARE_POLL_TRIES; /* check if we are the winner for downloading FW */ if (!adapter->winner) { dev_notice(adapter->dev, "FW already running! Skip FW dnld\n"); poll_num = MAX_MULTI_INTERFACE_POLL_TRIES; goto poll_fw; } } if (pmfw) { /* Download firmware with helper */ ret = adapter->if_ops.prog_fw(adapter, pmfw); if (ret) { dev_err(adapter->dev, "prog_fw failed ret=%#x\n", ret); return ret; } } poll_fw: /* Check if the firmware is downloaded successfully or not */ ret = adapter->if_ops.check_fw_status(adapter, poll_num); if (ret) { dev_err(adapter->dev, "FW failed to be active in time\n"); return -1; } done: /* re-enable host interrupt for mwifiex after fw dnld is successful */ if (adapter->if_ops.enable_int) adapter->if_ops.enable_int(adapter); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sdio.c0000644000175000017500000013276512026211315023251 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: SDIO specific handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" #include "sdio.h" #define SDIO_VERSION "1.0" /* The mwifiex_sdio_remove() callback function is called when * user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases * differently. * If the user is removing the module, the few commands (FUNC_SHUTDOWN, * HS_CANCEL etc.) are sent to the firmware. * If the card is removed, there is no need to send these command. * * The variable 'user_rmmod' is used to distinguish these two * scenarios. This flag is initialized as FALSE in case the card * is removed, and will be set to TRUE for module removal when * module_exit function is called. */ static u8 user_rmmod; static struct mwifiex_if_ops sdio_ops; static struct semaphore add_remove_card_sem; static int mwifiex_sdio_resume(struct device *dev); /* * SDIO probe. * * This function probes an mwifiex device and registers it. It allocates * the card structure, enables SDIO function number and initiates the * device registration and initialization procedure by adding a logical * interface. */ static int mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int ret; struct sdio_mmc_card *card = NULL; pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", func->vendor, func->device, func->class, func->num); card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); if (!card) return -ENOMEM; card->func = func; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; #endif sdio_claim_host(func); ret = sdio_enable_func(func); sdio_release_host(func); if (ret) { pr_err("%s: failed to enable function\n", __func__); kfree(card); return -EIO; } if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, MWIFIEX_SDIO)) { pr_err("%s: add card failed\n", __func__); kfree(card); sdio_claim_host(func); ret = sdio_disable_func(func); sdio_release_host(func); ret = -1; } return ret; } /* * SDIO remove. * * This function removes the interface and frees up the card structure. */ static void mwifiex_sdio_remove(struct sdio_func *func) { struct sdio_mmc_card *card; struct mwifiex_adapter *adapter; struct mwifiex_private *priv; int i; pr_debug("info: SDIO func num=%d\n", func->num); card = sdio_get_drvdata(func); if (!card) return; adapter = card->adapter; if (!adapter || !adapter->priv_num) return; /* In case driver is removed when asynchronous FW load is in progress */ wait_for_completion(&adapter->fw_load); if (user_rmmod) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); mwifiex_disable_auto_ds(priv); mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); } mwifiex_remove_card(card->adapter, &add_remove_card_sem); kfree(card); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* * SDIO suspend. * * Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not suspended, this function allocates and sends a host * sleep activate request to the firmware and turns off the traffic. */ static int mwifiex_sdio_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct sdio_mmc_card *card; struct mwifiex_adapter *adapter; mmc_pm_flag_t pm_flag = 0; int hs_actived = 0; int i; int ret = 0; if (func) { pm_flag = sdio_get_host_pm_caps(func); pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", sdio_func_id(func), pm_flag); if (!(pm_flag & MMC_PM_KEEP_POWER)) { pr_err("%s: cannot remain alive while host is" " suspended\n", sdio_func_id(func)); return -ENOSYS; } card = sdio_get_drvdata(func); if (!card || !card->adapter) { pr_err("suspend: invalid card or adapter\n"); return 0; } } else { pr_err("suspend: sdio_func is not specified\n"); return 0; } adapter = card->adapter; /* Enable the Host Sleep */ hs_actived = mwifiex_enable_hs(adapter); if (hs_actived) { pr_debug("cmd: suspend with MMC_PM_KEEP_POWER\n"); ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); } /* Indicate device suspended */ adapter->is_suspended = true; for (i = 0; i < adapter->priv_num; i++) netif_carrier_off(adapter->priv[i]->netdev); return ret; } /* * SDIO resume. * * Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not resumed, this function turns on the traffic and * sends a host sleep cancel request to the firmware. */ static int mwifiex_sdio_resume(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct sdio_mmc_card *card; struct mwifiex_adapter *adapter; mmc_pm_flag_t pm_flag = 0; int i; if (func) { pm_flag = sdio_get_host_pm_caps(func); card = sdio_get_drvdata(func); if (!card || !card->adapter) { pr_err("resume: invalid card or adapter\n"); return 0; } } else { pr_err("resume: sdio_func is not specified\n"); return 0; } adapter = card->adapter; if (!adapter->is_suspended) { dev_warn(adapter->dev, "device already resumed\n"); return 0; } adapter->is_suspended = false; for (i = 0; i < adapter->priv_num; i++) if (adapter->priv[i]->media_connected) netif_carrier_on(adapter->priv[i]->netdev); /* Disable Host Sleep */ mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), MWIFIEX_ASYNC_CMD); return 0; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ /* Device ID for SD8786 */ #define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) /* Device ID for SD8787 */ #define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) /* Device ID for SD8797 */ #define SDIO_DEVICE_ID_MARVELL_8797 (0x9129) /* WLAN IDs */ static const struct sdio_device_id mwifiex_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)}, {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)}, {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)}, {}, }; MODULE_DEVICE_TABLE(sdio, mwifiex_ids); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static const struct dev_pm_ops mwifiex_sdio_pm_ops = { .suspend = mwifiex_sdio_suspend, .resume = mwifiex_sdio_resume, }; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ static struct sdio_driver mwifiex_sdio = { .name = "mwifiex_sdio", .id_table = mwifiex_ids, .probe = mwifiex_sdio_probe, .remove = mwifiex_sdio_remove, .drv = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .pm = &mwifiex_sdio_pm_ops, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ } }; /* * This function writes data into SDIO card register. */ static int mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data) { struct sdio_mmc_card *card = adapter->card; int ret = -1; sdio_claim_host(card->func); sdio_writeb(card->func, (u8) data, reg, &ret); sdio_release_host(card->func); return ret; } /* * This function reads data from SDIO card register. */ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data) { struct sdio_mmc_card *card = adapter->card; int ret = -1; u8 val; sdio_claim_host(card->func); val = sdio_readb(card->func, reg, &ret); sdio_release_host(card->func); *data = val; return ret; } /* * This function writes multiple data into SDIO card memory. * * This does not work in suspended mode. */ static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, u32 pkt_len, u32 port) { struct sdio_mmc_card *card = adapter->card; int ret = -1; u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (pkt_len / MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); if (adapter->is_suspended) { dev_err(adapter->dev, "%s: not allowed while suspended\n", __func__); return -1; } sdio_claim_host(card->func); if (!sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size)) ret = 0; sdio_release_host(card->func); return ret; } /* * This function reads multiple data from SDIO card memory. */ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, u32 len, u32 port, u8 claim) { struct sdio_mmc_card *card = adapter->card; int ret = -1; u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) : len; u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); if (claim) sdio_claim_host(card->func); if (!sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size)) ret = 0; if (claim) sdio_release_host(card->func); return ret; } /* * This function wakes up the card. * * A host power up command is written to the card configuration * register to wake up the card. */ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { dev_dbg(adapter->dev, "event: wakeup device...\n"); return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); } /* * This function is called after the card has woken up. * * The card configuration register is reset. */ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) { dev_dbg(adapter->dev, "cmd: wakeup device completed\n"); return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); } /* * This function initializes the IO ports. * * The following operations are performed - * - Read the IO ports (0, 1 and 2) * - Set host interrupt Reset-To-Read to clear * - Set auto re-enable interrupt */ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) { u32 reg; adapter->ioport = 0; /* Read the IO port */ if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, ®)) adapter->ioport |= (reg & 0xff); else return -1; if (!mwifiex_read_reg(adapter, IO_PORT_1_REG, ®)) adapter->ioport |= ((reg & 0xff) << 8); else return -1; if (!mwifiex_read_reg(adapter, IO_PORT_2_REG, ®)) adapter->ioport |= ((reg & 0xff) << 16); else return -1; pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); /* Set Host interrupt reset to read to clear */ if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, ®)) mwifiex_write_reg(adapter, HOST_INT_RSR_REG, reg | SDIO_INT_MASK); else return -1; /* Dnld/Upld ready set to auto reset */ if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, ®)) mwifiex_write_reg(adapter, CARD_MISC_CFG_REG, reg | AUTO_RE_ENABLE_INT); else return -1; return 0; } /* * This function sends data to the card. */ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, u8 *payload, u32 pkt_len, u32 port) { u32 i = 0; int ret; do { ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); if (ret) { i++; dev_err(adapter->dev, "host_to_card, write iomem" " (%d) failed: %d\n", i, ret); if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) dev_err(adapter->dev, "write CFG reg failed\n"); ret = -1; if (i > MAX_WRITE_IOMEM_RETRY) return ret; } } while (ret == -1); return ret; } /* * This function gets the read port. * * If control port bit is set in MP read bitmap, the control port * is returned, otherwise the current read port is returned and * the value is increased (provided it does not reach the maximum * limit, in which case it is reset to 1) */ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) { struct sdio_mmc_card *card = adapter->card; u16 rd_bitmap = card->mp_rd_bitmap; dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap); if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK))) return -1; if (card->mp_rd_bitmap & CTRL_PORT_MASK) { card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK); *port = CTRL_PORT; dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n", *port, card->mp_rd_bitmap); } else { if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) { card->mp_rd_bitmap &= (u16) (~(1 << card->curr_rd_port)); *port = card->curr_rd_port; if (++card->curr_rd_port == MAX_PORT) card->curr_rd_port = 1; } else { return -1; } dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n", *port, rd_bitmap, card->mp_rd_bitmap); } return 0; } /* * This function gets the write port for data. * * The current write port is returned if available and the value is * increased (provided it does not reach the maximum limit, in which * case it is reset to 1) */ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) { struct sdio_mmc_card *card = adapter->card; u16 wr_bitmap = card->mp_wr_bitmap; dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap); if (!(wr_bitmap & card->mp_data_port_mask)) return -1; if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port)); *port = card->curr_wr_port; if (++card->curr_wr_port == card->mp_end_port) card->curr_wr_port = 1; } else { adapter->data_sent = true; return -EBUSY; } if (*port == CTRL_PORT) { dev_err(adapter->dev, "invalid data port=%d cur port=%d" " mp_wr_bitmap=0x%04x -> 0x%04x\n", *port, card->curr_wr_port, wr_bitmap, card->mp_wr_bitmap); return -1; } dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n", *port, wr_bitmap, card->mp_wr_bitmap); return 0; } /* * This function polls the card status. */ static int mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) { u32 tries; u32 cs; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs)) break; else if ((cs & bits) == bits) return 0; usleep_range(10, 20); } dev_err(adapter->dev, "poll card status failed, tries = %d\n", tries); return -1; } /* * This function reads the firmware status. */ static int mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) { u32 fws0, fws1; if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0)) return -1; if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1)) return -1; *dat = (u16) ((fws1 << 8) | fws0); return 0; } /* * This function disables the host interrupt. * * The host interrupt mask is read, the disable bit is reset and * written back to the card host interrupt mask register. */ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) { u32 host_int_mask; /* Read back the host_int_mask register */ if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask)) return -1; /* Update with the mask and write back to the register */ host_int_mask &= ~HOST_INT_DISABLE; if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) { dev_err(adapter->dev, "disable host interrupt failed\n"); return -1; } return 0; } /* * This function enables the host interrupt. * * The host interrupt enable mask is written to the card * host interrupt mask register. */ static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) { /* Simply write the mask to the register */ if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) { dev_err(adapter->dev, "enable host interrupt failed\n"); return -1; } return 0; } /* * This function sends a data buffer to the card. */ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, u32 *type, u8 *buffer, u32 npayload, u32 ioport) { int ret; u32 nb; if (!buffer) { dev_err(adapter->dev, "%s: buffer is NULL\n", __func__); return -1; } ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); if (ret) { dev_err(adapter->dev, "%s: read iomem failed: %d\n", __func__, ret); return -1; } nb = le16_to_cpu(*(__le16 *) (buffer)); if (nb > npayload) { dev_err(adapter->dev, "%s: invalid packet, nb=%d npayload=%d\n", __func__, nb, npayload); return -1; } *type = le16_to_cpu(*(__le16 *) (buffer + 2)); return ret; } /* * This function downloads the firmware to the card. * * Firmware is downloaded to the card in blocks. Every block download * is tested for CRC errors, and retried a number of times before * returning failure. */ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { int ret; u8 *firmware = fw->fw_buf; u32 firmware_len = fw->fw_len; u32 offset = 0; u32 base0, base1; u8 *fwbuf; u16 len = 0; u32 txlen, tx_blocks = 0, tries; u32 i = 0; if (!firmware_len) { dev_err(adapter->dev, "firmware image not found! Terminating download\n"); return -1; } dev_dbg(adapter->dev, "info: downloading FW image (%d bytes)\n", firmware_len); /* Assume that the allocated buffer is 8-byte aligned */ fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); if (!fwbuf) { dev_err(adapter->dev, "unable to alloc buffer for FW. Terminating dnld\n"); return -ENOMEM; } /* Perform firmware data transfer */ do { /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | DN_LD_CARD_RDY); if (ret) { dev_err(adapter->dev, "FW download with helper:" " poll status timeout @ %d\n", offset); goto done; } /* More data? */ if (offset >= firmware_len) break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0, &base0); if (ret) { dev_err(adapter->dev, "dev BASE0 register read failed: " "base0=%#04X(%d). Terminating dnld\n", base0, base0); goto done; } ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1, &base1); if (ret) { dev_err(adapter->dev, "dev BASE1 register read failed: " "base1=%#04X(%d). Terminating dnld\n", base1, base1); goto done; } len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); if (len) break; usleep_range(10, 20); } if (!len) { break; } else if (len > MWIFIEX_UPLD_SIZE) { dev_err(adapter->dev, "FW dnld failed @ %d, invalid length %d\n", offset, len); ret = -1; goto done; } txlen = len; if (len & BIT(0)) { i++; if (i > MAX_WRITE_IOMEM_RETRY) { dev_err(adapter->dev, "FW dnld failed @ %d, over max retry\n", offset); ret = -1; goto done; } dev_err(adapter->dev, "CRC indicated by the helper:" " len = 0x%04X, txlen = %d\n", len, txlen); len &= ~BIT(0); /* Setting this to 0 to resend from same offset */ txlen = 0; } else { i = 0; /* Set blocksize to transfer - checking for last block */ if (firmware_len - offset < txlen) txlen = firmware_len - offset; tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1) / MWIFIEX_SDIO_BLOCK_SIZE; /* Copy payload to buffer */ memmove(fwbuf, &firmware[offset], txlen); } ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * MWIFIEX_SDIO_BLOCK_SIZE, adapter->ioport); if (ret) { dev_err(adapter->dev, "FW download, write iomem (%d) failed @ %d\n", i, offset); if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) dev_err(adapter->dev, "write CFG reg failed\n"); ret = -1; goto done; } offset += txlen; } while (true); dev_dbg(adapter->dev, "info: FW download over, size %d bytes\n", offset); ret = 0; done: kfree(fwbuf); return ret; } /* * This function checks the firmware status in card. * * The winner interface is also determined by this function. */ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) { int ret = 0; u16 firmware_stat; u32 tries; u32 winner_status; /* Wait for firmware initialization event */ for (tries = 0; tries < poll_num; tries++) { ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); if (ret) continue; if (firmware_stat == FIRMWARE_READY_SDIO) { ret = 0; break; } else { mdelay(100); ret = -1; } } if (ret) { if (mwifiex_read_reg (adapter, CARD_FW_STATUS0_REG, &winner_status)) winner_status = 0; if (winner_status) adapter->winner = 0; else adapter->winner = 1; } return ret; } /* * This function reads the interrupt status from card. */ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; u32 sdio_ireg; unsigned long flags; if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS, REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { dev_err(adapter->dev, "read mp_regs failed\n"); return; } sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG]; if (sdio_ireg) { /* * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS * Clear the interrupt status register */ dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg); spin_lock_irqsave(&adapter->int_lock, flags); adapter->int_status |= sdio_ireg; spin_unlock_irqrestore(&adapter->int_lock, flags); } } /* * SDIO interrupt handler. * * This function reads the interrupt status from firmware and assigns * the main process in workqueue which will handle the interrupt. */ static void mwifiex_sdio_interrupt(struct sdio_func *func) { struct mwifiex_adapter *adapter; struct sdio_mmc_card *card; card = sdio_get_drvdata(func); if (!card || !card->adapter) { pr_debug("int: func=%p card=%p adapter=%p\n", func, card, card ? card->adapter : NULL); return; } adapter = card->adapter; if (adapter->surprise_removed) return; if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) adapter->ps_state = PS_STATE_AWAKE; mwifiex_interrupt_status(adapter); queue_work(adapter->workqueue, &adapter->main_work); } /* * This function decodes a received packet. * * Based on the type, the packet is treated as either a data, or * a command response, or an event, and the correct handler * function is invoked. */ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb, u32 upld_typ) { u8 *cmd_buf; skb_pull(skb, INTF_HEADER_LEN); switch (upld_typ) { case MWIFIEX_TYPE_DATA: dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n"); mwifiex_handle_rx_packet(adapter, skb); break; case MWIFIEX_TYPE_CMD: dev_dbg(adapter->dev, "info: --- Rx: Cmd Response ---\n"); /* take care of curr_cmd = NULL case */ if (!adapter->curr_cmd) { cmd_buf = adapter->upld_buf; if (adapter->ps_state == PS_STATE_SLEEP_CFM) mwifiex_process_sleep_confirm_resp(adapter, skb->data, skb->len); memcpy(cmd_buf, skb->data, min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); dev_kfree_skb_any(skb); } else { adapter->cmd_resp_received = true; adapter->curr_cmd->resp_skb = skb; } break; case MWIFIEX_TYPE_EVENT: dev_dbg(adapter->dev, "info: --- Rx: Event ---\n"); adapter->event_cause = *(u32 *) skb->data; if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) memcpy(adapter->event_body, skb->data + MWIFIEX_EVENT_HEADER_LEN, skb->len); /* event cause has been saved to adapter->event_cause */ adapter->event_received = true; adapter->event_skb = skb; break; default: dev_err(adapter->dev, "unknown upload type %#x\n", upld_typ); dev_kfree_skb_any(skb); break; } return 0; } /* * This function transfers received packets from card to driver, performing * aggregation if required. * * For data received on control port, or if aggregation is disabled, the * received buffers are uploaded as separate packets. However, if aggregation * is enabled and required, the buffers are copied onto an aggregation buffer, * provided there is space left, processed and finally uploaded. */ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, struct sk_buff *skb, u8 port) { struct sdio_mmc_card *card = adapter->card; s32 f_do_rx_aggr = 0; s32 f_do_rx_cur = 0; s32 f_aggr_cur = 0; struct sk_buff *skb_deaggr; u32 pind; u32 pkt_len, pkt_type = 0; u8 *curr_ptr; u32 rx_len = skb->len; if (port == CTRL_PORT) { /* Read the command Resp without aggr */ dev_dbg(adapter->dev, "info: %s: no aggregation for cmd " "response\n", __func__); f_do_rx_cur = 1; goto rx_curr_single; } if (!card->mpa_rx.enabled) { dev_dbg(adapter->dev, "info: %s: rx aggregation disabled\n", __func__); f_do_rx_cur = 1; goto rx_curr_single; } if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) { /* Some more data RX pending */ dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__); if (MP_RX_AGGR_IN_PROGRESS(card)) { if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) { f_aggr_cur = 1; } else { /* No room in Aggr buf, do rx aggr now */ f_do_rx_aggr = 1; f_do_rx_cur = 1; } } else { /* Rx aggr not in progress */ f_aggr_cur = 1; } } else { /* No more data RX pending */ dev_dbg(adapter->dev, "info: %s: last packet\n", __func__); if (MP_RX_AGGR_IN_PROGRESS(card)) { f_do_rx_aggr = 1; if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) f_aggr_cur = 1; else /* No room in Aggr buf, do rx aggr now */ f_do_rx_cur = 1; } else { f_do_rx_cur = 1; } } if (f_aggr_cur) { dev_dbg(adapter->dev, "info: current packet aggregation\n"); /* Curr pkt can be aggregated */ MP_RX_AGGR_SETUP(card, skb, port); if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || MP_RX_AGGR_PORT_LIMIT_REACHED(card)) { dev_dbg(adapter->dev, "info: %s: aggregated packet " "limit reached\n", __func__); /* No more pkts allowed in Aggr buf, rx it */ f_do_rx_aggr = 1; } } if (f_do_rx_aggr) { /* do aggr RX now */ dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n", card->mpa_rx.pkt_cnt); if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, card->mpa_rx.buf_len, (adapter->ioport | 0x1000 | (card->mpa_rx.ports << 4)) + card->mpa_rx.start_port, 1)) goto error; curr_ptr = card->mpa_rx.buf; for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { /* get curr PKT len & type */ pkt_len = *(u16 *) &curr_ptr[0]; pkt_type = *(u16 *) &curr_ptr[2]; /* copy pkt to deaggr buf */ skb_deaggr = card->mpa_rx.skb_arr[pind]; if ((pkt_type == MWIFIEX_TYPE_DATA) && (pkt_len <= card->mpa_rx.len_arr[pind])) { memcpy(skb_deaggr->data, curr_ptr, pkt_len); skb_trim(skb_deaggr, pkt_len); /* Process de-aggr packet */ mwifiex_decode_rx_packet(adapter, skb_deaggr, pkt_type); } else { dev_err(adapter->dev, "wrong aggr pkt:" " type=%d len=%d max_len=%d\n", pkt_type, pkt_len, card->mpa_rx.len_arr[pind]); dev_kfree_skb_any(skb_deaggr); } curr_ptr += card->mpa_rx.len_arr[pind]; } MP_RX_AGGR_BUF_RESET(card); } rx_curr_single: if (f_do_rx_cur) { dev_dbg(adapter->dev, "info: RX: port: %d, rx_len: %d\n", port, rx_len); if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, skb->len, adapter->ioport + port)) goto error; mwifiex_decode_rx_packet(adapter, skb, pkt_type); } return 0; error: if (MP_RX_AGGR_IN_PROGRESS(card)) { /* Multiport-aggregation transfer failed - cleanup */ for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { /* copy pkt to deaggr buf */ skb_deaggr = card->mpa_rx.skb_arr[pind]; dev_kfree_skb_any(skb_deaggr); } MP_RX_AGGR_BUF_RESET(card); } if (f_do_rx_cur) /* Single transfer pending. Free curr buff also */ dev_kfree_skb_any(skb); return -1; } /* * This function checks the current interrupt status. * * The following interrupts are checked and handled by this function - * - Data sent * - Command sent * - Packets received * * Since the firmware does not generate download ready interrupt if the * port updated is command port only, command sent interrupt checking * should be done manually, and for every SDIO interrupt. * * In case of Rx packets received, the packets are uploaded from card to * host and processed accordingly. */ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; int ret = 0; u8 sdio_ireg; struct sk_buff *skb; u8 port = CTRL_PORT; u32 len_reg_l, len_reg_u; u32 rx_blocks; u16 rx_len; unsigned long flags; spin_lock_irqsave(&adapter->int_lock, flags); sdio_ireg = adapter->int_status; adapter->int_status = 0; spin_unlock_irqrestore(&adapter->int_lock, flags); if (!sdio_ireg) return ret; if (sdio_ireg & DN_LD_HOST_INT_STATUS) { card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8; card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L]; dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n", card->mp_wr_bitmap); if (adapter->data_sent && (card->mp_wr_bitmap & card->mp_data_port_mask)) { dev_dbg(adapter->dev, "info: <--- Tx DONE Interrupt --->\n"); adapter->data_sent = false; } } /* As firmware will not generate download ready interrupt if the port updated is command port only, cmd_sent should be done for any SDIO interrupt. */ if (adapter->cmd_sent) { /* Check if firmware has attach buffer at command port and update just that in wr_bit_map. */ card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; if (card->mp_wr_bitmap & CTRL_PORT_MASK) adapter->cmd_sent = false; } dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", adapter->cmd_sent, adapter->data_sent); if (sdio_ireg & UP_LD_HOST_INT_STATUS) { card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8; card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L]; dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n", card->mp_rd_bitmap); while (true) { ret = mwifiex_get_rd_port(adapter, &port); if (ret) { dev_dbg(adapter->dev, "info: no more rd_port available\n"); break; } len_reg_l = RD_LEN_P0_L + (port << 1); len_reg_u = RD_LEN_P0_U + (port << 1); rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; rx_len |= (u16) card->mp_regs[len_reg_l]; dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n", port, rx_len); rx_blocks = (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - 1) / MWIFIEX_SDIO_BLOCK_SIZE; if (rx_len <= INTF_HEADER_LEN || (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > MWIFIEX_RX_DATA_BUF_SIZE) { dev_err(adapter->dev, "invalid rx_len=%d\n", rx_len); return -1; } rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); skb = dev_alloc_skb(rx_len); if (!skb) { dev_err(adapter->dev, "%s: failed to alloc skb", __func__); return -1; } skb_put(skb, rx_len); dev_dbg(adapter->dev, "info: rx_len = %d skb->len = %d\n", rx_len, skb->len); if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb, port)) { u32 cr = 0; dev_err(adapter->dev, "card_to_host_mpa failed:" " int status=%#x\n", sdio_ireg); if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) dev_err(adapter->dev, "read CFG reg failed\n"); dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr); if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) dev_err(adapter->dev, "write CFG reg failed\n"); dev_dbg(adapter->dev, "info: write success\n"); if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) dev_err(adapter->dev, "read CFG reg failed\n"); dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr); return -1; } } } return 0; } /* * This function aggregates transmission buffers in driver and downloads * the aggregated packet to card. * * The individual packets are aggregated by copying into an aggregation * buffer and then downloaded to the card. Previous unsent packets in the * aggregation buffer are pre-copied first before new packets are added. * Aggregation is done till there is space left in the aggregation buffer, * or till new packets are available. * * The function will only download the packet to the card when aggregation * stops, otherwise it will just aggregate the packet in aggregation buffer * and return. */ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, u8 *payload, u32 pkt_len, u8 port, u32 next_pkt_len) { struct sdio_mmc_card *card = adapter->card; int ret = 0; s32 f_send_aggr_buf = 0; s32 f_send_cur_buf = 0; s32 f_precopy_cur_buf = 0; s32 f_postcopy_cur_buf = 0; if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) { dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n", __func__); f_send_cur_buf = 1; goto tx_curr_single; } if (next_pkt_len) { /* More pkt in TX queue */ dev_dbg(adapter->dev, "info: %s: more packets in queue.\n", __func__); if (MP_TX_AGGR_IN_PROGRESS(card)) { if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) && MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { f_precopy_cur_buf = 1; if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port)) || !MP_TX_AGGR_BUF_HAS_ROOM( card, pkt_len + next_pkt_len)) f_send_aggr_buf = 1; } else { /* No room in Aggr buf, send it */ f_send_aggr_buf = 1; if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) || !(card->mp_wr_bitmap & (1 << card->curr_wr_port))) f_send_cur_buf = 1; else f_postcopy_cur_buf = 1; } } else { if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && (card->mp_wr_bitmap & (1 << card->curr_wr_port))) f_precopy_cur_buf = 1; else f_send_cur_buf = 1; } } else { /* Last pkt in TX queue */ dev_dbg(adapter->dev, "info: %s: Last packet in Tx Queue.\n", __func__); if (MP_TX_AGGR_IN_PROGRESS(card)) { /* some packs in Aggr buf already */ f_send_aggr_buf = 1; if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) f_precopy_cur_buf = 1; else /* No room in Aggr buf, send it */ f_send_cur_buf = 1; } else { f_send_cur_buf = 1; } } if (f_precopy_cur_buf) { dev_dbg(adapter->dev, "data: %s: precopy current buffer\n", __func__); MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || MP_TX_AGGR_PORT_LIMIT_REACHED(card)) /* No more pkts allowed in Aggr buf, send it */ f_send_aggr_buf = 1; } if (f_send_aggr_buf) { dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n", __func__, card->mpa_tx.start_port, card->mpa_tx.ports); ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, card->mpa_tx.buf_len, (adapter->ioport | 0x1000 | (card->mpa_tx.ports << 4)) + card->mpa_tx.start_port); MP_TX_AGGR_BUF_RESET(card); } tx_curr_single: if (f_send_cur_buf) { dev_dbg(adapter->dev, "data: %s: send current buffer %d\n", __func__, port); ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, adapter->ioport + port); } if (f_postcopy_cur_buf) { dev_dbg(adapter->dev, "data: %s: postcopy current buffer\n", __func__); MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); } return ret; } /* * This function downloads data from driver to card. * * Both commands and data packets are transferred to the card by this * function. * * This function adds the SDIO specific header to the front of the buffer * before transferring. The header contains the length of the packet and * the type. The firmware handles the packets based upon this set type. */ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, u8 type, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { struct sdio_mmc_card *card = adapter->card; int ret; u32 buf_block_len; u32 blk_size; u8 port = CTRL_PORT; u8 *payload = (u8 *)skb->data; u32 pkt_len = skb->len; /* Allocate buffer and copy payload */ blk_size = MWIFIEX_SDIO_BLOCK_SIZE; buf_block_len = (pkt_len + blk_size - 1) / blk_size; *(u16 *) &payload[0] = (u16) pkt_len; *(u16 *) &payload[2] = type; /* * This is SDIO specific header * u16 length, * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, * MWIFIEX_TYPE_EVENT = 3) */ if (type == MWIFIEX_TYPE_DATA) { ret = mwifiex_get_wr_port_data(adapter, &port); if (ret) { dev_err(adapter->dev, "%s: no wr_port available\n", __func__); return ret; } } else { adapter->cmd_sent = true; /* Type must be MWIFIEX_TYPE_CMD */ if (pkt_len <= INTF_HEADER_LEN || pkt_len > MWIFIEX_UPLD_SIZE) dev_err(adapter->dev, "%s: payload=%p, nb=%d\n", __func__, payload, pkt_len); } /* Transfer data to card */ pkt_len = buf_block_len * blk_size; if (tx_param) ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, port, tx_param->next_pkt_len ); else ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, port, 0); if (ret) { if (type == MWIFIEX_TYPE_CMD) adapter->cmd_sent = false; if (type == MWIFIEX_TYPE_DATA) adapter->data_sent = false; } else { if (type == MWIFIEX_TYPE_DATA) { if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) adapter->data_sent = true; else adapter->data_sent = false; } } return ret; } /* * This function allocates the MPA Tx and Rx buffers. */ static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) { struct sdio_mmc_card *card = adapter->card; int ret = 0; card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); if (!card->mpa_tx.buf) { dev_err(adapter->dev, "could not alloc buffer for MP-A TX\n"); ret = -1; goto error; } card->mpa_tx.buf_size = mpa_tx_buf_size; card->mpa_rx.buf = kzalloc(mpa_rx_buf_size, GFP_KERNEL); if (!card->mpa_rx.buf) { dev_err(adapter->dev, "could not alloc buffer for MP-A RX\n"); ret = -1; goto error; } card->mpa_rx.buf_size = mpa_rx_buf_size; error: if (ret) { kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); } return ret; } /* * This function unregisters the SDIO device. * * The SDIO IRQ is released, the function is disabled and driver * data is set to null. */ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; if (adapter->card) { /* Release the SDIO IRQ */ sdio_claim_host(card->func); sdio_release_irq(card->func); sdio_disable_func(card->func); sdio_release_host(card->func); sdio_set_drvdata(card->func, NULL); } } /* * This function registers the SDIO device. * * SDIO IRQ is claimed, block size is set and driver data is initialized. */ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) { int ret = 0; struct sdio_mmc_card *card = adapter->card; struct sdio_func *func = card->func; /* save adapter pointer in card */ card->adapter = adapter; sdio_claim_host(func); /* Request the SDIO IRQ */ ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); if (ret) { pr_err("claim irq failed: ret=%d\n", ret); goto disable_func; } /* Set block size */ ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); if (ret) { pr_err("cannot set SDIO block size\n"); ret = -1; goto release_irq; } sdio_release_host(func); sdio_set_drvdata(func, card); adapter->dev = &func->dev; switch (func->device) { case SDIO_DEVICE_ID_MARVELL_8786: strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME); break; case SDIO_DEVICE_ID_MARVELL_8797: strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME); break; case SDIO_DEVICE_ID_MARVELL_8787: default: strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME); break; } return 0; release_irq: sdio_release_irq(func); disable_func: sdio_disable_func(func); sdio_release_host(func); adapter->card = NULL; return -1; } /* * This function initializes the SDIO driver. * * The following initializations steps are followed - * - Read the Host interrupt status register to acknowledge * the first interrupt got from bootloader * - Disable host interrupt mask register * - Get SDIO port * - Initialize SDIO variables in card * - Allocate MP registers * - Allocate MPA Tx and Rx buffers */ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; int ret; u32 sdio_ireg; /* * Read the HOST_INT_STATUS_REG for ACK the first interrupt got * from the bootloader. If we don't do this we get a interrupt * as soon as we register the irq. */ mwifiex_read_reg(adapter, HOST_INTSTATUS_REG, &sdio_ireg); /* Disable host interrupt mask register for SDIO */ mwifiex_sdio_disable_host_int(adapter); /* Get SDIO ioport */ mwifiex_init_sdio_ioport(adapter); /* Initialize SDIO variables in card */ card->mp_rd_bitmap = 0; card->mp_wr_bitmap = 0; card->curr_rd_port = 1; card->curr_wr_port = 1; card->mp_data_port_mask = DATA_PORT_MASK; card->mpa_tx.buf_len = 0; card->mpa_tx.pkt_cnt = 0; card->mpa_tx.start_port = 0; card->mpa_tx.enabled = 1; card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; card->mpa_rx.buf_len = 0; card->mpa_rx.pkt_cnt = 0; card->mpa_rx.start_port = 0; card->mpa_rx.enabled = 1; card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; /* Allocate buffers for SDIO MP-A */ card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL); if (!card->mp_regs) { dev_err(adapter->dev, "failed to alloc mp_regs\n"); return -ENOMEM; } ret = mwifiex_alloc_sdio_mpa_buffers(adapter, SDIO_MP_TX_AGGR_DEF_BUF_SIZE, SDIO_MP_RX_AGGR_DEF_BUF_SIZE); if (ret) { dev_err(adapter->dev, "failed to alloc sdio mp-a buffers\n"); kfree(card->mp_regs); return -1; } return ret; } /* * This function resets the MPA Tx and Rx buffers. */ static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; MP_TX_AGGR_BUF_RESET(card); MP_RX_AGGR_BUF_RESET(card); } /* * This function cleans up the allocated card buffers. * * The following are freed by this function - * - MP registers * - MPA Tx buffer * - MPA Rx buffer */ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; kfree(card->mp_regs); kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); } /* * This function updates the MP end port in card. */ static void mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) { struct sdio_mmc_card *card = adapter->card; int i; card->mp_end_port = port; card->mp_data_port_mask = DATA_PORT_MASK; for (i = 1; i <= MAX_PORT - card->mp_end_port; i++) card->mp_data_port_mask &= ~(1 << (MAX_PORT - i)); card->curr_wr_port = 1; dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n", port, card->mp_data_port_mask); } static struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, .check_fw_status = mwifiex_check_fw_status, .prog_fw = mwifiex_prog_fw_w_helper, .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, .enable_int = mwifiex_sdio_enable_host_int, .process_int_status = mwifiex_process_int_status, .host_to_card = mwifiex_sdio_host_to_card, .wakeup = mwifiex_pm_wakeup_card, .wakeup_complete = mwifiex_pm_wakeup_card_complete, /* SDIO specific */ .update_mp_end_port = mwifiex_update_mp_end_port, .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, .event_complete = mwifiex_sdio_event_complete, }; /* * This function initializes the SDIO driver. * * This initiates the semaphore and registers the device with * SDIO bus. */ static int mwifiex_sdio_init_module(void) { sema_init(&add_remove_card_sem, 1); /* Clear the flag in case user removes the card. */ user_rmmod = 0; return sdio_register_driver(&mwifiex_sdio); } /* * This function cleans up the SDIO driver. * * The following major steps are followed for cleanup - * - Resume the device if its suspended * - Disconnect the device if connected * - Shutdown the firmware * - Unregister the device from SDIO bus. */ static void mwifiex_sdio_cleanup_module(void) { if (!down_interruptible(&add_remove_card_sem)) up(&add_remove_card_sem); /* Set the flag as user is removing this module. */ user_rmmod = 1; sdio_unregister_driver(&mwifiex_sdio); } module_init(mwifiex_sdio_init_module); module_exit(mwifiex_sdio_cleanup_module); MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); MODULE_VERSION(SDIO_VERSION); MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/debugfs.c0000644000175000017500000005173112026211315023723 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: debugfs * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include #include "main.h" #include "11n.h" static struct dentry *mwifiex_dfs_dir; static char *bss_modes[] = { "Unknown", "Ad-hoc", "Managed", "Auto" }; /* size/addr for mwifiex_debug_info */ #define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) #define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) /* size/addr for struct mwifiex_adapter */ #define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) #define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) struct mwifiex_debug_data { char name[32]; /* variable/array name */ u32 size; /* size of the variable/array */ size_t addr; /* address of the variable/array */ int num; /* number of variables in an array */ }; static struct mwifiex_debug_data items[] = { {"int_counter", item_size(int_counter), item_addr(int_counter), 1}, {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), item_addr(packets_out[WMM_AC_VO]), 1}, {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), item_addr(packets_out[WMM_AC_VI]), 1}, {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), item_addr(packets_out[WMM_AC_BE]), 1}, {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), item_addr(packets_out[WMM_AC_BK]), 1}, {"max_tx_buf_size", item_size(max_tx_buf_size), item_addr(max_tx_buf_size), 1}, {"tx_buf_size", item_size(tx_buf_size), item_addr(tx_buf_size), 1}, {"curr_tx_buf_size", item_size(curr_tx_buf_size), item_addr(curr_tx_buf_size), 1}, {"ps_mode", item_size(ps_mode), item_addr(ps_mode), 1}, {"ps_state", item_size(ps_state), item_addr(ps_state), 1}, {"is_deep_sleep", item_size(is_deep_sleep), item_addr(is_deep_sleep), 1}, {"wakeup_dev_req", item_size(pm_wakeup_card_req), item_addr(pm_wakeup_card_req), 1}, {"wakeup_tries", item_size(pm_wakeup_fw_try), item_addr(pm_wakeup_fw_try), 1}, {"hs_configured", item_size(is_hs_configured), item_addr(is_hs_configured), 1}, {"hs_activated", item_size(hs_activated), item_addr(hs_activated), 1}, {"num_tx_timeout", item_size(num_tx_timeout), item_addr(num_tx_timeout), 1}, {"num_cmd_timeout", item_size(num_cmd_timeout), item_addr(num_cmd_timeout), 1}, {"timeout_cmd_id", item_size(timeout_cmd_id), item_addr(timeout_cmd_id), 1}, {"timeout_cmd_act", item_size(timeout_cmd_act), item_addr(timeout_cmd_act), 1}, {"last_cmd_id", item_size(last_cmd_id), item_addr(last_cmd_id), DBG_CMD_NUM}, {"last_cmd_act", item_size(last_cmd_act), item_addr(last_cmd_act), DBG_CMD_NUM}, {"last_cmd_index", item_size(last_cmd_index), item_addr(last_cmd_index), 1}, {"last_cmd_resp_id", item_size(last_cmd_resp_id), item_addr(last_cmd_resp_id), DBG_CMD_NUM}, {"last_cmd_resp_index", item_size(last_cmd_resp_index), item_addr(last_cmd_resp_index), 1}, {"last_event", item_size(last_event), item_addr(last_event), DBG_CMD_NUM}, {"last_event_index", item_size(last_event_index), item_addr(last_event_index), 1}, {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), item_addr(num_cmd_host_to_card_failure), 1}, {"num_cmd_sleep_cfm_fail", item_size(num_cmd_sleep_cfm_host_to_card_failure), item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), item_addr(num_tx_host_to_card_failure), 1}, {"num_evt_deauth", item_size(num_event_deauth), item_addr(num_event_deauth), 1}, {"num_evt_disassoc", item_size(num_event_disassoc), item_addr(num_event_disassoc), 1}, {"num_evt_link_lost", item_size(num_event_link_lost), item_addr(num_event_link_lost), 1}, {"num_cmd_deauth", item_size(num_cmd_deauth), item_addr(num_cmd_deauth), 1}, {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), item_addr(num_cmd_assoc_success), 1}, {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), item_addr(num_cmd_assoc_failure), 1}, {"cmd_sent", item_size(cmd_sent), item_addr(cmd_sent), 1}, {"data_sent", item_size(data_sent), item_addr(data_sent), 1}, {"cmd_resp_received", item_size(cmd_resp_received), item_addr(cmd_resp_received), 1}, {"event_received", item_size(event_received), item_addr(event_received), 1}, /* variables defined in struct mwifiex_adapter */ {"cmd_pending", adapter_item_size(cmd_pending), adapter_item_addr(cmd_pending), 1}, {"tx_pending", adapter_item_size(tx_pending), adapter_item_addr(tx_pending), 1}, {"rx_pending", adapter_item_size(rx_pending), adapter_item_addr(rx_pending), 1}, }; static int num_of_items = ARRAY_SIZE(items); /* * Proc info file read handler. * * This function is called when the 'info' file is opened for reading. * It prints the following driver related information - * - Driver name * - Driver version * - Driver extended version * - Interface name * - BSS mode * - Media state (connected or disconnected) * - MAC address * - Total number of Tx bytes * - Total number of Rx bytes * - Total number of Tx packets * - Total number of Rx packets * - Total number of dropped Tx packets * - Total number of dropped Rx packets * - Total number of corrupted Tx packets * - Total number of corrupted Rx packets * - Carrier status (on or off) * - Tx queue status (started or stopped) * * For STA mode drivers, it also prints the following extra - * - ESSID * - BSSID * - Channel * - Region code * - Multicast count * - Multicast addresses */ static ssize_t mwifiex_info_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { struct mwifiex_private *priv = (struct mwifiex_private *) file->private_data; struct net_device *netdev = priv->netdev; struct netdev_hw_addr *ha; unsigned long page = get_zeroed_page(GFP_KERNEL); char *p = (char *) page, fmt[64]; struct mwifiex_bss_info info; ssize_t ret; int i = 0; if (!p) return -ENOMEM; memset(&info, 0, sizeof(info)); ret = mwifiex_get_bss_info(priv, &info); if (ret) goto free_and_exit; mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); if (!priv->version_str[0]) mwifiex_get_ver_ext(priv); p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); p += sprintf(p, "driver_version = %s", fmt); p += sprintf(p, "\nverext = %s", priv->version_str); p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); p += sprintf(p, "media_state=\"%s\"\n", (!priv->media_connected ? "Disconnected" : "Connected")); p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { p += sprintf(p, "multicast_count=\"%d\"\n", netdev_mc_count(netdev)); p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); p += sprintf(p, "country_code = \"%s\"\n", info.country_code); netdev_for_each_mc_addr(ha, netdev) p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i++, ha->addr); #else i++, ha->dmi_addr); #endif } p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); p += sprintf(p, "tx queue %s\n", ((netif_queue_stopped(priv->netdev)) ? "stopped" : "started")); ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, (unsigned long) p - page); free_and_exit: free_page(page); return ret; } /* * Proc getlog file read handler. * * This function is called when the 'getlog' file is opened for reading * It prints the following log information - * - Number of multicast Tx frames * - Number of failed packets * - Number of Tx retries * - Number of multicast Tx retries * - Number of duplicate frames * - Number of RTS successes * - Number of RTS failures * - Number of ACK failures * - Number of fragmented Rx frames * - Number of multicast Rx frames * - Number of FCS errors * - Number of Tx frames * - WEP ICV error counts */ static ssize_t mwifiex_getlog_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { struct mwifiex_private *priv = (struct mwifiex_private *) file->private_data; unsigned long page = get_zeroed_page(GFP_KERNEL); char *p = (char *) page; ssize_t ret; struct mwifiex_ds_get_stats stats; if (!p) return -ENOMEM; memset(&stats, 0, sizeof(stats)); ret = mwifiex_get_stats_info(priv, &stats); if (ret) goto free_and_exit; p += sprintf(p, "\n" "mcasttxframe %u\n" "failed %u\n" "retry %u\n" "multiretry %u\n" "framedup %u\n" "rtssuccess %u\n" "rtsfailure %u\n" "ackfailure %u\n" "rxfrag %u\n" "mcastrxframe %u\n" "fcserror %u\n" "txframe %u\n" "wepicverrcnt-1 %u\n" "wepicverrcnt-2 %u\n" "wepicverrcnt-3 %u\n" "wepicverrcnt-4 %u\n", stats.mcast_tx_frame, stats.failed, stats.retry, stats.multi_retry, stats.frame_dup, stats.rts_success, stats.rts_failure, stats.ack_failure, stats.rx_frag, stats.mcast_rx_frame, stats.fcs_error, stats.tx_frame, stats.wep_icv_error[0], stats.wep_icv_error[1], stats.wep_icv_error[2], stats.wep_icv_error[3]); ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, (unsigned long) p - page); free_and_exit: free_page(page); return ret; } static struct mwifiex_debug_info info; /* * Proc debug file read handler. * * This function is called when the 'debug' file is opened for reading * It prints the following log information - * - Interrupt count * - WMM AC VO packets count * - WMM AC VI packets count * - WMM AC BE packets count * - WMM AC BK packets count * - Maximum Tx buffer size * - Tx buffer size * - Current Tx buffer size * - Power Save mode * - Power Save state * - Deep Sleep status * - Device wakeup required status * - Number of wakeup tries * - Host Sleep configured status * - Host Sleep activated status * - Number of Tx timeouts * - Number of command timeouts * - Last timed out command ID * - Last timed out command action * - Last command ID * - Last command action * - Last command index * - Last command response ID * - Last command response index * - Last event * - Last event index * - Number of host to card command failures * - Number of sleep confirm command failures * - Number of host to card data failure * - Number of deauthentication events * - Number of disassociation events * - Number of link lost events * - Number of deauthentication commands * - Number of association success commands * - Number of association failure commands * - Number of commands sent * - Number of data packets sent * - Number of command responses received * - Number of events received * - Tx BA stream table (TID, RA) * - Rx reorder table (TID, TA, Start window, Window size, Buffer) */ static ssize_t mwifiex_debug_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { struct mwifiex_private *priv = (struct mwifiex_private *) file->private_data; struct mwifiex_debug_data *d = &items[0]; unsigned long page = get_zeroed_page(GFP_KERNEL); char *p = (char *) page; ssize_t ret; size_t size, addr; long val; int i, j; if (!p) return -ENOMEM; ret = mwifiex_get_debug_info(priv, &info); if (ret) goto free_and_exit; for (i = 0; i < num_of_items; i++) { p += sprintf(p, "%s=", d[i].name); size = d[i].size / d[i].num; if (i < (num_of_items - 3)) addr = d[i].addr + (size_t) &info; else /* The last 3 items are struct mwifiex_adapter variables */ addr = d[i].addr + (size_t) priv->adapter; for (j = 0; j < d[i].num; j++) { switch (size) { case 1: val = *((u8 *) addr); break; case 2: val = *((u16 *) addr); break; case 4: val = *((u32 *) addr); break; case 8: val = *((long long *) addr); break; default: val = -1; break; } p += sprintf(p, "%#lx ", val); addr += size; } p += sprintf(p, "\n"); } if (info.tx_tbl_num) { p += sprintf(p, "Tx BA stream table:\n"); for (i = 0; i < info.tx_tbl_num; i++) p += sprintf(p, "tid = %d, ra = %pM\n", info.tx_tbl[i].tid, info.tx_tbl[i].ra); } if (info.rx_tbl_num) { p += sprintf(p, "Rx reorder table:\n"); for (i = 0; i < info.rx_tbl_num; i++) { p += sprintf(p, "tid = %d, ta = %pM, " "start_win = %d, " "win_size = %d, buffer: ", info.rx_tbl[i].tid, info.rx_tbl[i].ta, info.rx_tbl[i].start_win, info.rx_tbl[i].win_size); for (j = 0; j < info.rx_tbl[i].win_size; j++) p += sprintf(p, "%c ", info.rx_tbl[i].buffer[j] ? '1' : '0'); p += sprintf(p, "\n"); } } ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, (unsigned long) p - page); free_and_exit: free_page(page); return ret; } static u32 saved_reg_type, saved_reg_offset, saved_reg_value; /* * Proc regrdwr file write handler. * * This function is called when the 'regrdwr' file is opened for writing * * This function can be used to write to a register. */ static ssize_t mwifiex_regrdwr_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1)); int ret; u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; if (!buf) return -ENOMEM; if (copy_from_user(buf, ubuf, buf_size)) { ret = -EFAULT; goto done; } sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); if (reg_type == 0 || reg_offset == 0) { ret = -EINVAL; goto done; } else { saved_reg_type = reg_type; saved_reg_offset = reg_offset; saved_reg_value = reg_value; ret = count; } done: free_page(addr); return ret; } /* * Proc regrdwr file read handler. * * This function is called when the 'regrdwr' file is opened for reading * * This function can be used to read from a register. */ static ssize_t mwifiex_regrdwr_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { struct mwifiex_private *priv = (struct mwifiex_private *) file->private_data; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; int pos = 0, ret = 0; u32 reg_value; if (!buf) return -ENOMEM; if (!saved_reg_type) { /* No command has been given */ pos += snprintf(buf, PAGE_SIZE, "0"); goto done; } /* Set command has been given */ if (saved_reg_value != UINT_MAX) { ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, saved_reg_value); pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, saved_reg_offset, saved_reg_value); ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); goto done; } /* Get command has been given */ ret = mwifiex_reg_read(priv, saved_reg_type, saved_reg_offset, ®_value); if (ret) { ret = -EINVAL; goto done; } pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, saved_reg_offset, reg_value); ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); done: free_page(addr); return ret; } static u32 saved_offset = -1, saved_bytes = -1; /* * Proc rdeeprom file write handler. * * This function is called when the 'rdeeprom' file is opened for writing * * This function can be used to write to a RDEEPROM location. */ static ssize_t mwifiex_rdeeprom_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1)); int ret = 0; int offset = -1, bytes = -1; if (!buf) return -ENOMEM; if (copy_from_user(buf, ubuf, buf_size)) { ret = -EFAULT; goto done; } sscanf(buf, "%d %d", &offset, &bytes); if (offset == -1 || bytes == -1) { ret = -EINVAL; goto done; } else { saved_offset = offset; saved_bytes = bytes; ret = count; } done: free_page(addr); return ret; } /* * Proc rdeeprom read write handler. * * This function is called when the 'rdeeprom' file is opened for reading * * This function can be used to read from a RDEEPROM location. */ static ssize_t mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { struct mwifiex_private *priv = (struct mwifiex_private *) file->private_data; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; int pos = 0, ret = 0, i; u8 value[MAX_EEPROM_DATA]; if (!buf) return -ENOMEM; if (saved_offset == -1) { /* No command has been given */ pos += snprintf(buf, PAGE_SIZE, "0"); goto done; } /* Get command has been given */ ret = mwifiex_eeprom_read(priv, (u16) saved_offset, (u16) saved_bytes, value); if (ret) { ret = -EINVAL; goto done; } pos += snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); for (i = 0; i < saved_bytes; i++) pos += snprintf(buf + strlen(buf), PAGE_SIZE, "%d ", value[i]); ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); done: free_page(addr); return ret; } #define MWIFIEX_DFS_ADD_FILE(name) do { \ if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ priv, &mwifiex_dfs_##name##_fops)) \ return; \ } while (0); #define MWIFIEX_DFS_FILE_OPS(name) \ static const struct file_operations mwifiex_dfs_##name##_fops = { \ .read = mwifiex_##name##_read, \ .write = mwifiex_##name##_write, \ .open = simple_open, \ }; #define MWIFIEX_DFS_FILE_READ_OPS(name) \ static const struct file_operations mwifiex_dfs_##name##_fops = { \ .read = mwifiex_##name##_read, \ .open = simple_open, \ }; #define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ static const struct file_operations mwifiex_dfs_##name##_fops = { \ .write = mwifiex_##name##_write, \ .open = simple_open, \ }; MWIFIEX_DFS_FILE_READ_OPS(info); MWIFIEX_DFS_FILE_READ_OPS(debug); MWIFIEX_DFS_FILE_READ_OPS(getlog); MWIFIEX_DFS_FILE_OPS(regrdwr); MWIFIEX_DFS_FILE_OPS(rdeeprom); /* * This function creates the debug FS directory structure and the files. */ void mwifiex_dev_debugfs_init(struct mwifiex_private *priv) { if (!mwifiex_dfs_dir || !priv) return; priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, mwifiex_dfs_dir); if (!priv->dfs_dev_dir) return; MWIFIEX_DFS_ADD_FILE(info); MWIFIEX_DFS_ADD_FILE(debug); MWIFIEX_DFS_ADD_FILE(getlog); MWIFIEX_DFS_ADD_FILE(regrdwr); MWIFIEX_DFS_ADD_FILE(rdeeprom); } /* * This function removes the debug FS directory structure and the files. */ void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) { if (!priv) return; debugfs_remove_recursive(priv->dfs_dev_dir); } /* * This function creates the top level proc directory. */ void mwifiex_debugfs_init(void) { if (!mwifiex_dfs_dir) mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); } /* * This function removes the top level proc directory. */ void mwifiex_debugfs_remove(void) { if (mwifiex_dfs_dir) debugfs_remove(mwifiex_dfs_dir); } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sta_ioctl.c0000644000175000017500000010573612026211315024272 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: functions for station ioctl * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" #include "cfg80211.h" /* * Copies the multicast address list from device to driver. * * This function does not validate the destination memory for * size, and the calling function must ensure enough memory is * available. */ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, struct net_device *dev) { int i = 0; struct netdev_hw_addr *ha; netdev_for_each_mc_addr(ha, dev) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); #else memcpy(&mlist->mac_list[i++], ha->dmi_addr, ETH_ALEN); #endif return i; } /* * Wait queue completion handler. * * This function waits on a cmd wait queue. It also cancels the pending * request after waking up, in case of errors. */ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) { bool cancel_flag = false; int status; struct cmd_ctrl_node *cmd_queued; if (!adapter->cmd_queued) return 0; cmd_queued = adapter->cmd_queued; adapter->cmd_queued = NULL; dev_dbg(adapter->dev, "cmd pending\n"); atomic_inc(&adapter->cmd_pending); /* Wait for completion */ wait_event_interruptible(adapter->cmd_wait_q.wait, *(cmd_queued->condition)); if (!*(cmd_queued->condition)) cancel_flag = true; if (cancel_flag) { mwifiex_cancel_pending_ioctl(adapter); dev_dbg(adapter->dev, "cmd cancel\n"); } status = adapter->cmd_wait_q.status; adapter->cmd_wait_q.status = 0; return status; } /* * This function prepares the correct firmware command and * issues it to set the multicast list. * * This function can be used to enable promiscuous mode, or enable all * multicast packets, or to enable selective multicast. */ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, struct mwifiex_multicast_list *mcast_list) { int ret = 0; u16 old_pkt_filter; old_pkt_filter = priv->curr_pkt_filter; if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { dev_dbg(priv->adapter->dev, "info: Enable Promiscuous mode\n"); priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; } else { /* Multicast */ priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; if (mcast_list->mode == MWIFIEX_MULTICAST_MODE) { dev_dbg(priv->adapter->dev, "info: Enabling All Multicast!\n"); priv->curr_pkt_filter |= HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; } else { priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; if (mcast_list->num_multicast_addr) { dev_dbg(priv->adapter->dev, "info: Set multicast list=%d\n", mcast_list->num_multicast_addr); /* Set multicast addresses to firmware */ if (old_pkt_filter == priv->curr_pkt_filter) { /* Send request to firmware */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_MULTICAST_ADR, HostCmd_ACT_GEN_SET, 0, mcast_list); } else { /* Send request to firmware */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_MULTICAST_ADR, HostCmd_ACT_GEN_SET, 0, mcast_list); } } } } dev_dbg(priv->adapter->dev, "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", old_pkt_filter, priv->curr_pkt_filter); if (old_pkt_filter != priv->curr_pkt_filter) { ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, HostCmd_ACT_GEN_SET, 0, &priv->curr_pkt_filter); } return ret; } /* * This function fills bss descriptor structure using provided * information. */ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct mwifiex_bssdescriptor *bss_desc) { int ret; u8 *beacon_ie; struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; beacon_ie = kmemdup(bss->information_elements, bss->len_beacon_ies, GFP_KERNEL); if (!beacon_ie) { dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); return -ENOMEM; } memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); bss_desc->rssi = bss->signal; bss_desc->beacon_buf = beacon_ie; bss_desc->beacon_buf_size = bss->len_beacon_ies; bss_desc->beacon_period = bss->beacon_interval; bss_desc->cap_info_bitmap = bss->capability; bss_desc->bss_band = bss_priv->band; bss_desc->fw_tsf = bss_priv->fw_tsf; bss_desc->timestamp = bss->tsf; if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { dev_dbg(priv->adapter->dev, "info: InterpretIE: AP WEP enabled\n"); bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; } else { bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; } if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS) bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; else bss_desc->bss_mode = NL80211_IFTYPE_STATION; ret = mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); kfree(beacon_ie); return ret; } static int mwifiex_process_country_ie(struct mwifiex_private *priv, struct cfg80211_bss *bss) { u8 *country_ie, country_ie_len; struct mwifiex_802_11d_domain_reg *domain_info = &priv->adapter->domain_reg; country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); if (!country_ie) return 0; country_ie_len = country_ie[1]; if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) return 0; domain_info->country_code[0] = country_ie[2]; domain_info->country_code[1] = country_ie[3]; domain_info->country_code[2] = ' '; country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; domain_info->no_of_triplet = country_ie_len / sizeof(struct ieee80211_country_ie_triplet); memcpy((u8 *)domain_info->triplet, &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(priv->adapter->wiphy, "11D: setting domain info in FW\n"); return -1; } return 0; } /* * In Ad-Hoc mode, the IBSS is created if not found in scan list. * In both Ad-Hoc and infra mode, an deauthentication is performed * first. */ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct cfg80211_ssid *req_ssid) { int ret; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bssdescriptor *bss_desc = NULL; priv->scan_block = false; if (bss) { mwifiex_process_country_ie(priv, bss); /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); if (!bss_desc) { dev_err(priv->adapter->dev, " failed to alloc bss_desc\n"); return -ENOMEM; } ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); if (ret) goto done; } if (priv->bss_mode == NL80211_IFTYPE_STATION) { /* Infra mode */ ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; dev_dbg(adapter->dev, "info: SSID found in scan list ... " "associating...\n"); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); /* Clear any past association response stored for * application retrieval */ priv->assoc_rsp_size = 0; ret = mwifiex_associate(priv, bss_desc); /* If auth type is auto and association fails using open mode, * try to connect using shared mode */ if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && priv->sec_info.is_authtype_auto && priv->sec_info.wep_enabled) { priv->sec_info.authentication_mode = NL80211_AUTHTYPE_SHARED_KEY; ret = mwifiex_associate(priv, bss_desc); } if (bss) cfg80211_put_bss(bss); } else { /* Adhoc mode */ /* If the requested SSID matches current SSID, return */ if (bss_desc && bss_desc->ssid.ssid_len && (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. ssid, &bss_desc->ssid))) { kfree(bss_desc); return 0; } /* Exit Adhoc mode first */ dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; priv->adhoc_is_link_sensed = false; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); if (!ret) { dev_dbg(adapter->dev, "info: network found in scan" " list. Joining...\n"); ret = mwifiex_adhoc_join(priv, bss_desc); if (bss) cfg80211_put_bss(bss); } else { dev_dbg(adapter->dev, "info: Network not found in " "the list, creating adhoc with ssid = %s\n", req_ssid->ssid); ret = mwifiex_adhoc_start(priv, req_ssid); } } done: kfree(bss_desc); return ret; } /* * IOCTL request handler to set host sleep configuration. * * This function prepares the correct firmware command and * issues it. */ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) { struct mwifiex_adapter *adapter = priv->adapter; int status = 0; u32 prev_cond = 0; if (!hs_cfg) return -ENOMEM; switch (action) { case HostCmd_ACT_GEN_SET: if (adapter->pps_uapsd_mode) { dev_dbg(adapter->dev, "info: Host Sleep IOCTL" " is blocked in UAPSD/PPS mode\n"); status = -1; break; } if (hs_cfg->is_invoke_hostcmd) { if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) { if (!adapter->is_hs_configured) /* Already cancelled */ break; /* Save previous condition */ prev_cond = le32_to_cpu(adapter->hs_cfg .conditions); adapter->hs_cfg.conditions = cpu_to_le32(hs_cfg->conditions); } else if (hs_cfg->conditions) { adapter->hs_cfg.conditions = cpu_to_le32(hs_cfg->conditions); adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; if (hs_cfg->gap) adapter->hs_cfg.gap = (u8)hs_cfg->gap; } else if (adapter->hs_cfg.conditions == cpu_to_le32(HOST_SLEEP_CFG_CANCEL)) { /* Return failure if no parameters for HS enable */ status = -1; break; } if (cmd_type == MWIFIEX_SYNC_CMD) status = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_HS_CFG_ENH, HostCmd_ACT_GEN_SET, 0, &adapter->hs_cfg); else status = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_HS_CFG_ENH, HostCmd_ACT_GEN_SET, 0, &adapter->hs_cfg); if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) /* Restore previous condition */ adapter->hs_cfg.conditions = cpu_to_le32(prev_cond); } else { adapter->hs_cfg.conditions = cpu_to_le32(hs_cfg->conditions); adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; adapter->hs_cfg.gap = (u8)hs_cfg->gap; } break; case HostCmd_ACT_GEN_GET: hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); hs_cfg->gpio = adapter->hs_cfg.gpio; hs_cfg->gap = adapter->hs_cfg.gap; break; default: status = -1; break; } return status; } /* * Sends IOCTL request to cancel the existing Host Sleep configuration. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type) { struct mwifiex_ds_hs_cfg hscfg; hscfg.conditions = HOST_SLEEP_CFG_CANCEL; hscfg.is_invoke_hostcmd = true; return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, cmd_type, &hscfg); } EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); /* * Sends IOCTL request to cancel the existing Host Sleep configuration. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) { struct mwifiex_ds_hs_cfg hscfg; if (adapter->hs_activated) { dev_dbg(adapter->dev, "cmd: HS Already actived\n"); return true; } adapter->hs_activate_wait_q_woken = false; memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); hscfg.is_invoke_hostcmd = true; if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, &hscfg)) { dev_err(adapter->dev, "IOCTL request HS enable failed\n"); return false; } wait_event_interruptible(adapter->hs_activate_wait_q, adapter->hs_activate_wait_q_woken); return true; } EXPORT_SYMBOL_GPL(mwifiex_enable_hs); /* * IOCTL request handler to get BSS information. * * This function collates the information from different driver structures * to send to the user. */ int mwifiex_get_bss_info(struct mwifiex_private *priv, struct mwifiex_bss_info *info) { struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bssdescriptor *bss_desc; if (!info) return -1; bss_desc = &priv->curr_bss_params.bss_descriptor; info->bss_mode = priv->bss_mode; memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid)); memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); info->bss_chan = bss_desc->channel; memcpy(info->country_code, adapter->country_code, IEEE80211_COUNTRY_STRING_LEN); info->media_connected = priv->media_connected; info->max_power_level = priv->max_tx_power_level; info->min_power_level = priv->min_tx_power_level; info->adhoc_state = priv->adhoc_state; info->bcn_nf_last = priv->bcn_nf_last; if (priv->sec_info.wep_enabled) info->wep_status = true; else info->wep_status = false; info->is_hs_configured = adapter->is_hs_configured; info->is_deep_sleep = adapter->is_deep_sleep; return 0; } /* * The function disables auto deep sleep mode. */ int mwifiex_disable_auto_ds(struct mwifiex_private *priv) { struct mwifiex_ds_auto_ds auto_ds; auto_ds.auto_ds = DEEP_SLEEP_OFF; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH, DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds); } EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds); /* * Sends IOCTL request to get the data rate. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate) { int ret; ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, HostCmd_ACT_GEN_GET, 0, NULL); if (!ret) { if (priv->is_data_rate_auto) *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate, priv->tx_htinfo); else *rate = priv->data_rate; } return ret; } /* * IOCTL request handler to set tx power configuration. * * This function prepares the correct firmware command and * issues it. * * For non-auto power mode, all the following power groups are set - * - Modulation class HR/DSSS * - Modulation class OFDM * - Modulation class HTBW20 * - Modulation class HTBW40 */ int mwifiex_set_tx_power(struct mwifiex_private *priv, struct mwifiex_power_cfg *power_cfg) { int ret; struct host_cmd_ds_txpwr_cfg *txp_cfg; struct mwifiex_types_power_group *pg_tlv; struct mwifiex_power_group *pg; u8 *buf; u16 dbm = 0; if (!power_cfg->is_power_auto) { dbm = (u16) power_cfg->power_level; if ((dbm < priv->min_tx_power_level) || (dbm > priv->max_tx_power_level)) { dev_err(priv->adapter->dev, "txpower value %d dBm" " is out of range (%d dBm-%d dBm)\n", dbm, priv->min_tx_power_level, priv->max_tx_power_level); return -1; } } buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); if (!buf) { dev_err(priv->adapter->dev, "%s: failed to alloc cmd buffer\n", __func__); return -ENOMEM; } txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); if (!power_cfg->is_power_auto) { txp_cfg->mode = cpu_to_le32(1); pg_tlv = (struct mwifiex_types_power_group *) (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); pg_tlv->type = TLV_TYPE_POWER_GROUP; pg_tlv->length = 4 * sizeof(struct mwifiex_power_group); pg = (struct mwifiex_power_group *) (buf + sizeof(struct host_cmd_ds_txpwr_cfg) + sizeof(struct mwifiex_types_power_group)); /* Power group for modulation class HR/DSSS */ pg->first_rate_code = 0x00; pg->last_rate_code = 0x03; pg->modulation_class = MOD_CLASS_HR_DSSS; pg->power_step = 0; pg->power_min = (s8) dbm; pg->power_max = (s8) dbm; pg++; /* Power group for modulation class OFDM */ pg->first_rate_code = 0x00; pg->last_rate_code = 0x07; pg->modulation_class = MOD_CLASS_OFDM; pg->power_step = 0; pg->power_min = (s8) dbm; pg->power_max = (s8) dbm; pg++; /* Power group for modulation class HTBW20 */ pg->first_rate_code = 0x00; pg->last_rate_code = 0x20; pg->modulation_class = MOD_CLASS_HT; pg->power_step = 0; pg->power_min = (s8) dbm; pg->power_max = (s8) dbm; pg->ht_bandwidth = HT_BW_20; pg++; /* Power group for modulation class HTBW40 */ pg->first_rate_code = 0x00; pg->last_rate_code = 0x20; pg->modulation_class = MOD_CLASS_HT; pg->power_step = 0; pg->power_min = (s8) dbm; pg->power_max = (s8) dbm; pg->ht_bandwidth = HT_BW_40; } ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TXPWR_CFG, HostCmd_ACT_GEN_SET, 0, buf); kfree(buf); return ret; } /* * IOCTL request handler to get power save mode. * * This function prepares the correct firmware command and * issues it. */ int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) { int ret; struct mwifiex_adapter *adapter = priv->adapter; u16 sub_cmd; if (*ps_mode) adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; else adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH, sub_cmd, BITMAP_STA_PS, NULL); if ((!ret) && (sub_cmd == DIS_AUTO_PS)) ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_PS_MODE_ENH, GET_PS, 0, NULL); return ret; } /* * IOCTL request handler to set/reset WPA IE. * * The supplied WPA IE is treated as a opaque buffer. Only the first field * is checked to determine WPA version. If buffer length is zero, the existing * WPA IE is reset. */ static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { if (ie_len > sizeof(priv->wpa_ie)) { dev_err(priv->adapter->dev, "failed to copy WPA IE, too big\n"); return -1; } memcpy(priv->wpa_ie, ie_data_ptr, ie_len); priv->wpa_ie_len = (u8) ie_len; dev_dbg(priv->adapter->dev, "cmd: Set Wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, priv->wpa_ie[0]); if (priv->wpa_ie[0] == WLAN_EID_WPA) { priv->sec_info.wpa_enabled = true; } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { priv->sec_info.wpa2_enabled = true; } else { priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; } } else { memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); priv->wpa_ie_len = 0; dev_dbg(priv->adapter->dev, "info: reset wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, priv->wpa_ie[0]); priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; } return 0; } /* * IOCTL request handler to set/reset WAPI IE. * * The supplied WAPI IE is treated as a opaque buffer. Only the first field * is checked to internally enable WAPI. If buffer length is zero, the existing * WAPI IE is reset. */ static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { if (ie_len > sizeof(priv->wapi_ie)) { dev_dbg(priv->adapter->dev, "info: failed to copy WAPI IE, too big\n"); return -1; } memcpy(priv->wapi_ie, ie_data_ptr, ie_len); priv->wapi_ie_len = ie_len; dev_dbg(priv->adapter->dev, "cmd: Set wapi_ie_len=%d IE=%#x\n", priv->wapi_ie_len, priv->wapi_ie[0]); if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) priv->sec_info.wapi_enabled = true; } else { memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); priv->wapi_ie_len = ie_len; dev_dbg(priv->adapter->dev, "info: Reset wapi_ie_len=%d IE=%#x\n", priv->wapi_ie_len, priv->wapi_ie[0]); priv->sec_info.wapi_enabled = false; } return 0; } /* * IOCTL request handler to set/reset WPS IE. * * The supplied WPS IE is treated as a opaque buffer. Only the first field * is checked to internally enable WPS. If buffer length is zero, the existing * WPS IE is reset. */ static int mwifiex_set_wps_ie(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); if (!priv->wps_ie) return -ENOMEM; if (ie_len > sizeof(priv->wps_ie)) { dev_dbg(priv->adapter->dev, "info: failed to copy WPS IE, too big\n"); kfree(priv->wps_ie); return -1; } memcpy(priv->wps_ie, ie_data_ptr, ie_len); priv->wps_ie_len = ie_len; dev_dbg(priv->adapter->dev, "cmd: Set wps_ie_len=%d IE=%#x\n", priv->wps_ie_len, priv->wps_ie[0]); } else { kfree(priv->wps_ie); priv->wps_ie_len = ie_len; dev_dbg(priv->adapter->dev, "info: Reset wps_ie_len=%d\n", priv->wps_ie_len); } return 0; } /* * IOCTL request handler to set WAPI key. * * This function prepares the correct firmware command and * issues it. */ static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, encrypt_key); } /* * IOCTL request handler to set WEP network key. * * This function prepares the correct firmware command and * issues it, after validation checks. */ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { int ret; struct mwifiex_wep_key *wep_key; int index; if (priv->wep_key_curr_index >= NUM_WEP_KEYS) priv->wep_key_curr_index = 0; wep_key = &priv->wep_key[priv->wep_key_curr_index]; index = encrypt_key->key_index; if (encrypt_key->key_disable) { priv->sec_info.wep_enabled = 0; } else if (!encrypt_key->key_len) { /* Copy the required key as the current key */ wep_key = &priv->wep_key[index]; if (!wep_key->key_length) { dev_err(priv->adapter->dev, "key not set, so cannot enable it\n"); return -1; } priv->wep_key_curr_index = (u16) index; priv->sec_info.wep_enabled = 1; } else { wep_key = &priv->wep_key[index]; memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); /* Copy the key in the driver */ memcpy(wep_key->key_material, encrypt_key->key_material, encrypt_key->key_len); wep_key->key_index = index; wep_key->key_length = encrypt_key->key_len; priv->sec_info.wep_enabled = 1; } if (wep_key->key_length) { /* Send request to firmware */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, 0, NULL); if (ret) return ret; } if (priv->sec_info.wep_enabled) priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; else priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, HostCmd_ACT_GEN_SET, 0, &priv->curr_pkt_filter); return ret; } /* * IOCTL request handler to set WPA key. * * This function prepares the correct firmware command and * issues it, after validation checks. * * Current driver only supports key length of up to 32 bytes. * * This function can also be used to disable a currently set key. */ static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { int ret; u8 remove_key = false; struct host_cmd_ds_802_11_key_material *ibss_key; /* Current driver only supports key length of up to 32 bytes */ if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { dev_err(priv->adapter->dev, "key length too long\n"); return -1; } if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { /* * IBSS/WPA-None uses only one key (Group) for both receiving * and sending unicast and multicast packets. */ /* Send the key as PTK to firmware */ encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, encrypt_key); if (ret) return ret; ibss_key = &priv->aes_key; memset(ibss_key, 0, sizeof(struct host_cmd_ds_802_11_key_material)); /* Copy the key in the driver */ memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, encrypt_key->key_len); memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, sizeof(ibss_key->key_param_set.key_len)); ibss_key->key_param_set.key_type_id = cpu_to_le16(KEY_TYPE_ID_TKIP); ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); /* Send the key as GTK to firmware */ encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; } if (!encrypt_key->key_index) encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; if (remove_key) ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, !KEY_INFO_ENABLED, encrypt_key); else ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, encrypt_key); return ret; } /* * IOCTL request handler to set/get network keys. * * This is a generic key handling function which supports WEP, WPA * and WAPI. */ static int mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { int status; if (encrypt_key->is_wapi_key) status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key); else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key); else status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key); return status; } /* * This function returns the driver version. */ int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, int max_len) { union { u32 l; u8 c[4]; } ver; char fw_ver[32]; ver.l = adapter->fw_release_number; sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); snprintf(version, max_len, driver_version, fw_ver); dev_dbg(adapter->dev, "info: MWIFIEX VERSION: %s\n", version); return 0; } /* * Sends IOCTL request to set encoding parameters. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, const u8 *key, int key_len, u8 key_index, const u8 *mac_addr, int disable) { struct mwifiex_ds_encrypt_key encrypt_key; memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); encrypt_key.key_len = key_len; if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) encrypt_key.is_igtk_key = true; if (!disable) { encrypt_key.key_index = key_index; if (key_len) memcpy(encrypt_key.key_material, key, key_len); if (mac_addr) memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); if (kp && kp->seq && kp->seq_len) memcpy(encrypt_key.pn, kp->seq, kp->seq_len); } else { encrypt_key.key_disable = true; if (mac_addr) memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); } return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key); } /* * Sends IOCTL request to get extended version. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_get_ver_ext(struct mwifiex_private *priv) { struct mwifiex_ver_ext ver_ext; memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_VERSION_EXT, HostCmd_ACT_GEN_GET, 0, &ver_ext)) return -1; return 0; } /* * Sends IOCTL request to get statistics information. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_get_stats_info(struct mwifiex_private *priv, struct mwifiex_ds_get_stats *log) { return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_GET_LOG, HostCmd_ACT_GEN_GET, 0, log); } /* * IOCTL request handler to read/write register. * * This function prepares the correct firmware command and * issues it. * * Access to the following registers are supported - * - MAC * - BBP * - RF * - PMIC * - CAU */ static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, struct mwifiex_ds_reg_rw *reg_rw, u16 action) { u16 cmd_no; switch (le32_to_cpu(reg_rw->type)) { case MWIFIEX_REG_MAC: cmd_no = HostCmd_CMD_MAC_REG_ACCESS; break; case MWIFIEX_REG_BBP: cmd_no = HostCmd_CMD_BBP_REG_ACCESS; break; case MWIFIEX_REG_RF: cmd_no = HostCmd_CMD_RF_REG_ACCESS; break; case MWIFIEX_REG_PMIC: cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; break; case MWIFIEX_REG_CAU: cmd_no = HostCmd_CMD_CAU_REG_ACCESS; break; default: return -1; } return mwifiex_send_cmd_sync(priv, cmd_no, action, 0, reg_rw); } /* * Sends IOCTL request to write to a register. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, u32 reg_offset, u32 reg_value) { struct mwifiex_ds_reg_rw reg_rw; reg_rw.type = cpu_to_le32(reg_type); reg_rw.offset = cpu_to_le32(reg_offset); reg_rw.value = cpu_to_le32(reg_value); return mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_SET); } /* * Sends IOCTL request to read from a register. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, u32 reg_offset, u32 *value) { int ret; struct mwifiex_ds_reg_rw reg_rw; reg_rw.type = cpu_to_le32(reg_type); reg_rw.offset = cpu_to_le32(reg_offset); ret = mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_GET); if (ret) goto done; *value = le32_to_cpu(reg_rw.value); done: return ret; } /* * Sends IOCTL request to read from EEPROM. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, u8 *value) { int ret; struct mwifiex_ds_read_eeprom rd_eeprom; rd_eeprom.offset = cpu_to_le16((u16) offset); rd_eeprom.byte_count = cpu_to_le16((u16) bytes); /* Send request to firmware */ ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, HostCmd_ACT_GEN_GET, 0, &rd_eeprom); if (!ret) memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); return ret; } /* * This function sets a generic IE. In addition to generic IE, it can * also handle WPA, WPA2 and WAPI IEs. */ static int mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { int ret = 0; struct ieee_types_vendor_header *pvendor_ie; const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; /* If the passed length is zero, reset the buffer */ if (!ie_len) { priv->gen_ie_buf_len = 0; priv->wps.session_enable = false; return 0; } else if (!ie_data_ptr) { return -1; } pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; /* Test to see if it is a WPA IE, if not, then it is a gen IE */ if (((pvendor_ie->element_id == WLAN_EID_WPA) && (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) || (pvendor_ie->element_id == WLAN_EID_RSN)) { /* IE is a WPA/WPA2 IE so call set_wpa function */ ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); priv->wps.session_enable = false; return ret; } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { /* IE is a WAPI IE so call set_wapi function */ ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); return ret; } /* * Verify that the passed length is not larger than the * available space remaining in the buffer */ if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { /* Test to see if it is a WPS IE, if so, enable * wps session flag */ pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) { priv->wps.session_enable = true; dev_dbg(priv->adapter->dev, "info: WPS Session Enabled.\n"); ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len); } /* Append the passed data to the end of the genIeBuffer */ memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, ie_len); /* Increment the stored buffer length by the size passed */ priv->gen_ie_buf_len += ie_len; } else { /* Passed data does not fit in the remaining buffer space */ ret = -1; } /* Return 0, or -1 for error case */ return ret; } /* * IOCTL request handler to set/get generic IE. * * In addition to various generic IEs, this function can also be * used to set the ARP filter. */ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, struct mwifiex_ds_misc_gen_ie *gen_ie, u16 action) { struct mwifiex_adapter *adapter = priv->adapter; switch (gen_ie->type) { case MWIFIEX_IE_TYPE_GEN_IE: if (action == HostCmd_ACT_GEN_GET) { gen_ie->len = priv->wpa_ie_len; memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); } else { mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, (u16) gen_ie->len); } break; case MWIFIEX_IE_TYPE_ARP_FILTER: memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { adapter->arp_filter_size = 0; dev_err(adapter->dev, "invalid ARP filter size\n"); return -1; } else { memcpy(adapter->arp_filter, gen_ie->ie_data, gen_ie->len); adapter->arp_filter_size = gen_ie->len; } break; default: dev_err(adapter->dev, "invalid IE type\n"); return -1; } return 0; } /* * Sends IOCTL request to set a generic IE. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) { struct mwifiex_ds_misc_gen_ie gen_ie; if (ie_len > IEEE_MAX_IE_SIZE) return -EFAULT; gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; gen_ie.len = ie_len; memcpy(gen_ie.ie_data, ie, ie_len); if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET)) return -EFAULT; return 0; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/main.c0000644000175000017500000006040012026211315023221 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: major functions * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "main.h" #include "wmm.h" #include "cfg80211.h" #include "11n.h" #define VERSION "1.0" const char driver_version[] = "mwifiex " VERSION " (%s) "; /* * This function registers the device and performs all the necessary * initializations. * * The following initialization operations are performed - * - Allocate adapter structure * - Save interface specific operations table in adapter * - Call interface specific initialization routine * - Allocate private structures * - Set default adapter structure parameters * - Initialize locks * * In case of any errors during inittialization, this function also ensures * proper cleanup before exiting. */ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, void **padapter) { struct mwifiex_adapter *adapter; int i; adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL); if (!adapter) return -ENOMEM; *padapter = adapter; adapter->card = card; /* Save interface specific operations in adapter */ memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops)); /* card specific initialization has been deferred until now .. */ if (adapter->if_ops.init_if) if (adapter->if_ops.init_if(adapter)) goto error; adapter->priv_num = 0; for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { /* Allocate memory for private structure */ adapter->priv[i] = kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL); if (!adapter->priv[i]) goto error; adapter->priv[i]->adapter = adapter; adapter->priv_num++; } mwifiex_init_lock_list(adapter); init_timer(&adapter->cmd_timer); adapter->cmd_timer.function = mwifiex_cmd_timeout_func; adapter->cmd_timer.data = (unsigned long) adapter; return 0; error: dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n"); for (i = 0; i < adapter->priv_num; i++) kfree(adapter->priv[i]); kfree(adapter); return -1; } /* * This function unregisters the device and performs all the necessary * cleanups. * * The following cleanup operations are performed - * - Free the timers * - Free beacon buffers * - Free private structures * - Free adapter structure */ static int mwifiex_unregister(struct mwifiex_adapter *adapter) { s32 i; del_timer(&adapter->cmd_timer); /* Free private structures */ for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { mwifiex_free_curr_bcn(adapter->priv[i]); kfree(adapter->priv[i]); } } kfree(adapter); return 0; } /* * The main process. * * This function is the main procedure of the driver and handles various driver * operations. It runs in a loop and provides the core functionalities. * * The main responsibilities of this function are - * - Ensure concurrency control * - Handle pending interrupts and call interrupt handlers * - Wake up the card if required * - Handle command responses and call response handlers * - Handle events and call event handlers * - Execute pending commands * - Transmit pending data packets */ int mwifiex_main_process(struct mwifiex_adapter *adapter) { int ret = 0; unsigned long flags; struct sk_buff *skb; spin_lock_irqsave(&adapter->main_proc_lock, flags); /* Check if already processing */ if (adapter->mwifiex_processing) { spin_unlock_irqrestore(&adapter->main_proc_lock, flags); goto exit_main_proc; } else { adapter->mwifiex_processing = true; spin_unlock_irqrestore(&adapter->main_proc_lock, flags); } process_start: do { if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) || (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) break; /* Handle pending interrupt if any */ if (adapter->int_status) { if (adapter->hs_activated) mwifiex_process_hs_config(adapter); if (adapter->if_ops.process_int_status) adapter->if_ops.process_int_status(adapter); } /* Need to wake up the card ? */ if ((adapter->ps_state == PS_STATE_SLEEP) && (adapter->pm_wakeup_card_req && !adapter->pm_wakeup_fw_try) && (is_command_pending(adapter) || !mwifiex_wmm_lists_empty(adapter))) { adapter->pm_wakeup_fw_try = true; adapter->if_ops.wakeup(adapter); continue; } if (IS_CARD_RX_RCVD(adapter)) { adapter->pm_wakeup_fw_try = false; if (adapter->ps_state == PS_STATE_SLEEP) adapter->ps_state = PS_STATE_AWAKE; } else { /* We have tried to wakeup the card already */ if (adapter->pm_wakeup_fw_try) break; if (adapter->ps_state != PS_STATE_AWAKE || adapter->tx_lock_flag) break; if ((adapter->scan_processing && !adapter->scan_delay_cnt) || adapter->data_sent || mwifiex_wmm_lists_empty(adapter)) { if (adapter->cmd_sent || adapter->curr_cmd || (!is_command_pending(adapter))) break; } } /* Check Rx data for USB */ if (adapter->iface_type == MWIFIEX_USB) while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) mwifiex_handle_rx_packet(adapter, skb); /* Check for Cmd Resp */ if (adapter->cmd_resp_received) { adapter->cmd_resp_received = false; mwifiex_process_cmdresp(adapter); /* call mwifiex back when init_fw is done */ if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { adapter->hw_status = MWIFIEX_HW_STATUS_READY; mwifiex_init_fw_complete(adapter); } } /* Check for event */ if (adapter->event_received) { adapter->event_received = false; mwifiex_process_event(adapter); } /* Check if we need to confirm Sleep Request received previously */ if (adapter->ps_state == PS_STATE_PRE_SLEEP) { if (!adapter->cmd_sent && !adapter->curr_cmd) mwifiex_check_ps_cond(adapter); } /* * The ps_state may have been changed during processing of * Sleep Request event. */ if ((adapter->ps_state == PS_STATE_SLEEP) || (adapter->ps_state == PS_STATE_PRE_SLEEP) || (adapter->ps_state == PS_STATE_SLEEP_CFM) || adapter->tx_lock_flag) continue; if (!adapter->cmd_sent && !adapter->curr_cmd) { if (mwifiex_exec_next_cmd(adapter) == -1) { ret = -1; break; } } if ((!adapter->scan_processing || adapter->scan_delay_cnt) && !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) { mwifiex_wmm_process_tx(adapter); if (adapter->hs_activated) { adapter->is_hs_configured = false; mwifiex_hs_activated_event (mwifiex_get_priv (adapter, MWIFIEX_BSS_ROLE_ANY), false); } } if (adapter->delay_null_pkt && !adapter->cmd_sent && !adapter->curr_cmd && !is_command_pending(adapter) && mwifiex_wmm_lists_empty(adapter)) { if (!mwifiex_send_null_packet (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) { adapter->delay_null_pkt = false; adapter->ps_state = PS_STATE_SLEEP; } break; } } while (true); if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter)) goto process_start; spin_lock_irqsave(&adapter->main_proc_lock, flags); adapter->mwifiex_processing = false; spin_unlock_irqrestore(&adapter->main_proc_lock, flags); exit_main_proc: if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) mwifiex_shutdown_drv(adapter); return ret; } /* * This function frees the adapter structure. * * Additionally, this closes the netlink socket, frees the timers * and private structures. */ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) { if (!adapter) { pr_err("%s: adapter is NULL\n", __func__); return; } mwifiex_unregister(adapter); pr_debug("info: %s: free adapter\n", __func__); } /* * This function gets firmware and initializes it. * * The main initialization steps followed are - * - Download the correct firmware to card * - Issue the init commands to firmware */ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) { int ret; char fmt[64]; struct mwifiex_private *priv; struct mwifiex_adapter *adapter = context; struct mwifiex_fw_image fw; if (!firmware) { dev_err(adapter->dev, "Failed to get firmware %s\n", adapter->fw_name); goto done; } memset(&fw, 0, sizeof(struct mwifiex_fw_image)); adapter->firmware = firmware; fw.fw_buf = (u8 *) adapter->firmware->data; fw.fw_len = adapter->firmware->size; if (adapter->if_ops.dnld_fw) ret = adapter->if_ops.dnld_fw(adapter, &fw); else ret = mwifiex_dnld_fw(adapter, &fw); if (ret == -1) goto done; dev_notice(adapter->dev, "WLAN FW is active\n"); adapter->init_wait_q_woken = false; ret = mwifiex_init_fw(adapter); if (ret == -1) { goto done; } else if (!ret) { adapter->hw_status = MWIFIEX_HW_STATUS_READY; goto done; } /* Wait for mwifiex_init to complete */ wait_event_interruptible(adapter->init_wait_q, adapter->init_wait_q_woken); if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) goto done; priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; if (mwifiex_register_cfg80211(adapter)) { dev_err(adapter->dev, "cannot register with cfg80211\n"); goto err_init_fw; } rtnl_lock(); /* Create station interface by default */ if (!mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NL80211_IFTYPE_STATION, NULL, NULL)) { dev_err(adapter->dev, "cannot create default STA interface\n"); goto err_add_intf; } /* Create AP interface by default */ if (!mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NL80211_IFTYPE_AP, NULL, NULL)) { dev_err(adapter->dev, "cannot create default AP interface\n"); goto err_add_intf; } rtnl_unlock(); mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); dev_notice(adapter->dev, "driver_version = %s\n", fmt); goto done; err_add_intf: mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev); rtnl_unlock(); err_init_fw: pr_debug("info: %s: unregister device\n", __func__); adapter->if_ops.unregister_dev(adapter); done: release_firmware(adapter->firmware); complete(&adapter->fw_load); return; } /* * This function initializes the hardware and gets firmware. */ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) { int ret; init_completion(&adapter->fw_load); ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, adapter->dev, GFP_KERNEL, adapter, mwifiex_fw_dpc); if (ret < 0) dev_err(adapter->dev, "request_firmware_nowait() returned error %d\n", ret); return ret; } /* * This function fills a driver buffer. * * The function associates a given SKB with the provided driver buffer * and also updates some of the SKB parameters, including IP header, * priority and timestamp. */ static void mwifiex_fill_buffer(struct sk_buff *skb) { struct ethhdr *eth; struct iphdr *iph; struct timeval tv; u8 tid = 0; eth = (struct ethhdr *) skb->data; switch (eth->h_proto) { case __constant_htons(ETH_P_IP): iph = ip_hdr(skb); tid = IPTOS_PREC(iph->tos); pr_debug("data: packet type ETH_P_IP: %04x, tid=%#x prio=%#x\n", eth->h_proto, tid, skb->priority); break; case __constant_htons(ETH_P_ARP): pr_debug("data: ARP packet: %04x\n", eth->h_proto); default: break; } /* Offset for TOS field in the IP header */ #define IPTOS_OFFSET 5 tid = (tid >> IPTOS_OFFSET); skb->priority = tid; /* Record the current time the packet was queued; used to determine the amount of time the packet was queued in the driver before it was sent to the firmware. The delay is then sent along with the packet to the firmware for aggregate delay calculation for stats and MSDU lifetime expiry. */ do_gettimeofday(&tv); skb->tstamp = timeval_to_ktime(tv); } /* * CFG802.11 network device handler for open. * * Starts the data queue. */ static int mwifiex_open(struct net_device *dev) { netif_tx_start_all_queues(dev); return 0; } /* * CFG802.11 network device handler for close. */ static int mwifiex_close(struct net_device *dev) { return 0; } /* * CFG802.11 network device handler for data transmission. */ static int mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n", jiffies, priv->bss_type, priv->bss_num); if (priv->adapter->surprise_removed) { kfree_skb(skb); priv->stats.tx_dropped++; return 0; } if (!skb->len || (skb->len > ETH_FRAME_LEN)) { dev_err(priv->adapter->dev, "Tx: bad skb len %d\n", skb->len); kfree_skb(skb); priv->stats.tx_dropped++; return 0; } if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { dev_dbg(priv->adapter->dev, "data: Tx: insufficient skb headroom %d\n", skb_headroom(skb)); /* Insufficient skb headroom - allocate a new skb */ new_skb = skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); if (unlikely(!new_skb)) { dev_err(priv->adapter->dev, "Tx: cannot alloca new_skb\n"); kfree_skb(skb); priv->stats.tx_dropped++; return 0; } kfree_skb(skb); skb = new_skb; dev_dbg(priv->adapter->dev, "info: new skb headroomd %d\n", skb_headroom(skb)); } tx_info = MWIFIEX_SKB_TXCB(skb); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; mwifiex_fill_buffer(skb); mwifiex_wmm_add_buf_txqueue(priv, skb); atomic_inc(&priv->adapter->tx_pending); if (priv->adapter->scan_delay_cnt) atomic_set(&priv->adapter->is_tx_received, true); if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) { mwifiex_set_trans_start(dev); mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); } queue_work(priv->adapter->workqueue, &priv->adapter->main_work); return 0; } /* * CFG802.11 network device handler for setting MAC address. */ static int mwifiex_set_mac_address(struct net_device *dev, void *addr) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct sockaddr *hw_addr = addr; int ret; memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); /* Send request to firmware */ ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_MAC_ADDRESS, HostCmd_ACT_GEN_SET, 0, NULL); if (!ret) memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); else dev_err(priv->adapter->dev, "set mac address failed: ret=%d\n", ret); memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); return ret; } /* * CFG802.11 network device handler for setting multicast list. */ static void mwifiex_set_multicast_list(struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct mwifiex_multicast_list mcast_list; if (dev->flags & IFF_PROMISC) { mcast_list.mode = MWIFIEX_PROMISC_MODE; } else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; } else { mcast_list.mode = MWIFIEX_MULTICAST_MODE; if (netdev_mc_count(dev)) mcast_list.num_multicast_addr = mwifiex_copy_mcast_addr(&mcast_list, dev); } mwifiex_request_set_multicast_list(priv, &mcast_list); } /* * CFG802.11 network device handler for transmission timeout. */ static void mwifiex_tx_timeout(struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); dev_err(priv->adapter->dev, "%lu : Tx timeout, bss_type-num = %d-%d\n", jiffies, priv->bss_type, priv->bss_num); mwifiex_set_trans_start(dev); priv->num_tx_timeout++; } /* * CFG802.11 network device handler for statistics retrieval. */ static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); return &priv->stats; } /* Network device handlers */ static const struct net_device_ops mwifiex_netdev_ops = { .ndo_open = mwifiex_open, .ndo_stop = mwifiex_close, .ndo_start_xmit = mwifiex_hard_start_xmit, .ndo_set_mac_address = mwifiex_set_mac_address, .ndo_tx_timeout = mwifiex_tx_timeout, .ndo_get_stats = mwifiex_get_stats, .ndo_set_rx_mode = mwifiex_set_multicast_list, }; /* * This function initializes the private structure parameters. * * The following wait queues are initialized - * - IOCTL wait queue * - Command wait queue * - Statistics wait queue * * ...and the following default parameters are set - * - Current key index : Set to 0 * - Rate index : Set to auto * - Media connected : Set to disconnected * - Adhoc link sensed : Set to false * - Nick name : Set to null * - Number of Tx timeout : Set to 0 * - Device address : Set to current address * * In addition, the CFG80211 work queue is also created. */ void mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev) { netdev_attach_ops(dev, &mwifiex_netdev_ops); /* Initialize private structure */ priv->current_key_index = 0; priv->media_connected = false; memset(&priv->nick_name, 0, sizeof(priv->nick_name)); memset(priv->mgmt_ie, 0, sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX); priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK; priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK; priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK; priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK; priv->num_tx_timeout = 0; memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); } /* * This function check if command is pending. */ int is_command_pending(struct mwifiex_adapter *adapter) { unsigned long flags; int is_cmd_pend_q_empty; spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); return !is_cmd_pend_q_empty; } /* * This is the main work queue function. * * It handles the main process, which in turn handles the complete * driver operations. */ static void mwifiex_main_work_queue(struct work_struct *work) { struct mwifiex_adapter *adapter = container_of(work, struct mwifiex_adapter, main_work); if (adapter->surprise_removed) return; mwifiex_main_process(adapter); } /* * This function cancels all works in the queue and destroys * the main workqueue. */ static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) { flush_workqueue(adapter->workqueue); destroy_workqueue(adapter->workqueue); adapter->workqueue = NULL; } /* * This function adds the card. * * This function follows the following major steps to set up the device - * - Initialize software. This includes probing the card, registering * the interface operations table, and allocating/initializing the * adapter structure * - Set up the netlink socket * - Create and start the main work queue * - Register the device * - Initialize firmware and hardware * - Add logical interfaces */ int mwifiex_add_card(void *card, struct semaphore *sem, struct mwifiex_if_ops *if_ops, u8 iface_type) { struct mwifiex_adapter *adapter; if (down_interruptible(sem)) goto exit_sem_err; if (mwifiex_register(card, if_ops, (void **)&adapter)) { pr_err("%s: software init failed\n", __func__); goto err_init_sw; } adapter->iface_type = iface_type; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; adapter->surprise_removed = false; init_waitqueue_head(&adapter->init_wait_q); adapter->is_suspended = false; adapter->hs_activated = false; init_waitqueue_head(&adapter->hs_activate_wait_q); adapter->cmd_wait_q_required = false; init_waitqueue_head(&adapter->cmd_wait_q.wait); adapter->cmd_wait_q.status = 0; adapter->scan_wait_q_woken = false; adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE"); if (!adapter->workqueue) goto err_kmalloc; INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); /* Register the device. Fill up the private data structure with relevant information from the card and request for the required IRQ. */ if (adapter->if_ops.register_dev(adapter)) { pr_err("%s: failed to register mwifiex device\n", __func__); goto err_registerdev; } if (mwifiex_init_hw_fw(adapter)) { pr_err("%s: firmware init failed\n", __func__); goto err_init_fw; } up(sem); return 0; err_init_fw: pr_debug("info: %s: unregister device\n", __func__); if (adapter->if_ops.unregister_dev) adapter->if_ops.unregister_dev(adapter); err_registerdev: adapter->surprise_removed = true; mwifiex_terminate_workqueue(adapter); err_kmalloc: if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { pr_debug("info: %s: shutdown mwifiex\n", __func__); adapter->init_wait_q_woken = false; if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) wait_event_interruptible(adapter->init_wait_q, adapter->init_wait_q_woken); } mwifiex_free_adapter(adapter); err_init_sw: up(sem); exit_sem_err: return -1; } EXPORT_SYMBOL_GPL(mwifiex_add_card); /* * This function removes the card. * * This function follows the following major steps to remove the device - * - Stop data traffic * - Shutdown firmware * - Remove the logical interfaces * - Terminate the work queue * - Unregister the device * - Free the adapter structure */ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) { struct mwifiex_private *priv = NULL; int i; if (down_interruptible(sem)) goto exit_sem_err; if (!adapter) goto exit_remove; adapter->surprise_removed = true; /* Stop data */ for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv && priv->netdev) { if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); } } dev_dbg(adapter->dev, "cmd: calling mwifiex_shutdown_drv...\n"); adapter->init_wait_q_woken = false; if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) wait_event_interruptible(adapter->init_wait_q, adapter->init_wait_q_woken); dev_dbg(adapter->dev, "cmd: mwifiex_shutdown_drv done\n"); if (atomic_read(&adapter->rx_pending) || atomic_read(&adapter->tx_pending) || atomic_read(&adapter->cmd_pending)) { dev_err(adapter->dev, "rx_pending=%d, tx_pending=%d, " "cmd_pending=%d\n", atomic_read(&adapter->rx_pending), atomic_read(&adapter->tx_pending), atomic_read(&adapter->cmd_pending)); } for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (!priv) continue; rtnl_lock(); if (priv->wdev && priv->netdev) mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev); rtnl_unlock(); } priv = adapter->priv[0]; if (!priv || !priv->wdev) goto exit_remove; wiphy_unregister(priv->wdev->wiphy); wiphy_free(priv->wdev->wiphy); for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv) kfree(priv->wdev); } mwifiex_terminate_workqueue(adapter); /* Unregister device */ dev_dbg(adapter->dev, "info: unregister device\n"); if (adapter->if_ops.unregister_dev) adapter->if_ops.unregister_dev(adapter); /* Free adapter structure */ dev_dbg(adapter->dev, "info: free adapter\n"); mwifiex_free_adapter(adapter); exit_remove: up(sem); exit_sem_err: return 0; } EXPORT_SYMBOL_GPL(mwifiex_remove_card); /* * This function initializes the module. * * The debug FS is also initialized if configured. */ static int mwifiex_init_module(void) { #ifdef CONFIG_DEBUG_FS mwifiex_debugfs_init(); #endif return 0; } /* * This function cleans up the module. * * The debug FS is removed if available. */ static void mwifiex_cleanup_module(void) { #ifdef CONFIG_DEBUG_FS mwifiex_debugfs_remove(); #endif } module_init(mwifiex_init_module); module_exit(mwifiex_cleanup_module); MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL v2"); compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/wmm.h0000644000175000017500000000615212026211315023106 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: WMM * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_WMM_H_ #define _MWIFIEX_WMM_H_ enum ieee_types_wmm_aciaifsn_bitmasks { MWIFIEX_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), MWIFIEX_ACM = BIT(4), MWIFIEX_ACI = (BIT(5) | BIT(6)), }; enum ieee_types_wmm_ecw_bitmasks { MWIFIEX_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), }; /* * This function retrieves the TID of the given RA list. */ static inline int mwifiex_get_tid(struct mwifiex_ra_list_tbl *ptr) { struct sk_buff *skb; if (skb_queue_empty(&ptr->skb_head)) return 0; skb = skb_peek(&ptr->skb_head); return skb->priority; } /* * This function gets the length of a list. */ static inline int mwifiex_wmm_list_len(struct list_head *head) { struct list_head *pos; int count = 0; list_for_each(pos, head) ++count; return count; } /* * This function checks if a RA list is empty or not. */ static inline u8 mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) { struct mwifiex_ra_list_tbl *ra_list; int is_list_empty; list_for_each_entry(ra_list, ra_list_hhead, list) { is_list_empty = skb_queue_empty(&ra_list->skb_head); if (!is_list_empty) return false; } return true; } void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, struct sk_buff *skb); void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra); int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); int mwifiex_is_ralist_valid(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list, int tid); u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, const struct sk_buff *skb); void mwifiex_wmm_init(struct mwifiex_adapter *adapter); extern u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, u8 **assoc_buf, struct ieee_types_wmm_parameter *wmmie, struct ieee80211_ht_cap *htcap); void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, struct ieee_types_wmm_parameter *wmm_ie); void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); extern int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, const struct host_cmd_ds_command *resp); #endif /* !_MWIFIEX_WMM_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/wmm.c0000644000175000017500000010674112026211315023106 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: WMM * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* Maximum value FW can accept for driver delay in packet transmission */ #define DRV_PKT_DELAY_TO_FW_MAX 512 #define WMM_QUEUED_PACKET_LOWER_LIMIT 180 #define WMM_QUEUED_PACKET_UPPER_LIMIT 200 /* Offset for TOS field in the IP header */ #define IPTOS_OFFSET 5 /* WMM information IE */ static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00 }; static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO }; static u8 tos_to_tid[] = { /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ 0x01, /* 0 1 0 AC_BK */ 0x02, /* 0 0 0 AC_BK */ 0x00, /* 0 0 1 AC_BE */ 0x03, /* 0 1 1 AC_BE */ 0x04, /* 1 0 0 AC_VI */ 0x05, /* 1 0 1 AC_VI */ 0x06, /* 1 1 0 AC_VO */ 0x07 /* 1 1 1 AC_VO */ }; /* * This table inverses the tos_to_tid operation to get a priority * which is in sequential order, and can be compared. * Use this to compare the priority of two different TIDs. */ static u8 tos_to_tid_inv[] = { 0x02, /* from tos_to_tid[2] = 0 */ 0x00, /* from tos_to_tid[0] = 1 */ 0x01, /* from tos_to_tid[1] = 2 */ 0x03, 0x04, 0x05, 0x06, 0x07}; static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; /* * This function debug prints the priority parameters for a WMM AC. */ static void mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) { const char *ac_str[] = { "BK", "BE", "VI", "VO" }; pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5]], (ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5, (ac_param->aci_aifsn_bitmap & MWIFIEX_ACM) >> 4, ac_param->aci_aifsn_bitmap & MWIFIEX_AIFSN, ac_param->ecw_bitmap & MWIFIEX_ECW_MIN, (ac_param->ecw_bitmap & MWIFIEX_ECW_MAX) >> 4, le16_to_cpu(ac_param->tx_op_limit)); } /* * This function allocates a route address list. * * The function also initializes the list with the provided RA. */ static struct mwifiex_ra_list_tbl * mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra) { struct mwifiex_ra_list_tbl *ra_list; ra_list = kzalloc(sizeof(struct mwifiex_ra_list_tbl), GFP_ATOMIC); if (!ra_list) { dev_err(adapter->dev, "%s: failed to alloc ra_list\n", __func__); return NULL; } INIT_LIST_HEAD(&ra_list->list); skb_queue_head_init(&ra_list->skb_head); memcpy(ra_list->ra, ra, ETH_ALEN); ra_list->total_pkts_size = 0; dev_dbg(adapter->dev, "info: allocated ra_list %p\n", ra_list); return ra_list; } /* This function returns random no between 16 and 32 to be used as threshold * for no of packets after which BA setup is initiated. */ static u8 mwifiex_get_random_ba_threshold(void) { u32 sec, usec; struct timeval ba_tstamp; u8 ba_threshold; /* setup ba_packet_threshold here random number between * [BA_SETUP_PACKET_OFFSET, * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] */ do_gettimeofday(&ba_tstamp); sec = (ba_tstamp.tv_sec & 0xFFFF) + (ba_tstamp.tv_sec >> 16); usec = (ba_tstamp.tv_usec & 0xFFFF) + (ba_tstamp.tv_usec >> 16); ba_threshold = (((sec << 16) + usec) % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; return ba_threshold; } /* * This function allocates and adds a RA list for all TIDs * with the given RA. */ void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) { int i; struct mwifiex_ra_list_tbl *ra_list; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_sta_node *node; unsigned long flags; spin_lock_irqsave(&priv->sta_list_spinlock, flags); node = mwifiex_get_sta_entry(priv, ra); spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); for (i = 0; i < MAX_NUM_TID; ++i) { ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra); dev_dbg(adapter->dev, "info: created ra_list %p\n", ra_list); if (!ra_list) break; ra_list->is_11n_enabled = 0; if (!mwifiex_queuing_ra_based(priv)) { ra_list->is_11n_enabled = IS_11N_ENABLED(priv); } else { ra_list->is_11n_enabled = mwifiex_is_sta_11n_enabled(priv, node); if (ra_list->is_11n_enabled) ra_list->max_amsdu = node->max_amsdu; } dev_dbg(adapter->dev, "data: ralist %p: is_11n_enabled=%d\n", ra_list, ra_list->is_11n_enabled); if (ra_list->is_11n_enabled) { ra_list->pkt_count = 0; ra_list->ba_packet_thr = mwifiex_get_random_ba_threshold(); } list_add_tail(&ra_list->list, &priv->wmm.tid_tbl_ptr[i].ra_list); if (!priv->wmm.tid_tbl_ptr[i].ra_list_curr) priv->wmm.tid_tbl_ptr[i].ra_list_curr = ra_list; } } /* * This function sets the WMM queue priorities to their default values. */ static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) { /* Default queue priorities: VO->VI->BE->BK */ priv->wmm.queue_priority[0] = WMM_AC_VO; priv->wmm.queue_priority[1] = WMM_AC_VI; priv->wmm.queue_priority[2] = WMM_AC_BE; priv->wmm.queue_priority[3] = WMM_AC_BK; } /* * This function map ACs to TIDs. */ static void mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm) { u8 *queue_priority = wmm->queue_priority; int i; for (i = 0; i < 4; ++i) { tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1]; tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0]; } for (i = 0; i < MAX_NUM_TID; ++i) tos_to_tid_inv[tos_to_tid[i]] = (u8)i; atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); } /* * This function initializes WMM priority queues. */ void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, struct ieee_types_wmm_parameter *wmm_ie) { u16 cw_min, avg_back_off, tmp[4]; u32 i, j, num_ac; u8 ac_idx; if (!wmm_ie || !priv->wmm_enabled) { /* WMM is not enabled, just set the defaults and return */ mwifiex_wmm_default_queue_priorities(priv); return; } dev_dbg(priv->adapter->dev, "info: WMM Parameter IE: version=%d, " "qos_info Parameter Set Count=%d, Reserved=%#x\n", wmm_ie->vend_hdr.version, wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, wmm_ie->reserved); for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { u8 ecw = wmm_ie->ac_params[num_ac].ecw_bitmap; u8 aci_aifsn = wmm_ie->ac_params[num_ac].aci_aifsn_bitmap; cw_min = (1 << (ecw & MWIFIEX_ECW_MIN)) - 1; avg_back_off = (cw_min >> 1) + (aci_aifsn & MWIFIEX_AIFSN); ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & MWIFIEX_ACI) >> 5]; priv->wmm.queue_priority[ac_idx] = ac_idx; tmp[ac_idx] = avg_back_off; dev_dbg(priv->adapter->dev, "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", (1 << ((ecw & MWIFIEX_ECW_MAX) >> 4)) - 1, cw_min, avg_back_off); mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); } /* Bubble sort */ for (i = 0; i < num_ac; i++) { for (j = 1; j < num_ac - i; j++) { if (tmp[j - 1] > tmp[j]) { swap(tmp[j - 1], tmp[j]); swap(priv->wmm.queue_priority[j - 1], priv->wmm.queue_priority[j]); } else if (tmp[j - 1] == tmp[j]) { if (priv->wmm.queue_priority[j - 1] < priv->wmm.queue_priority[j]) swap(priv->wmm.queue_priority[j - 1], priv->wmm.queue_priority[j]); } } } mwifiex_wmm_queue_priorities_tid(&priv->wmm); } /* * This function evaluates whether or not an AC is to be downgraded. * * In case the AC is not enabled, the highest AC is returned that is * enabled and does not require admission control. */ static enum mwifiex_wmm_ac_e mwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, enum mwifiex_wmm_ac_e eval_ac) { int down_ac; enum mwifiex_wmm_ac_e ret_ac; struct mwifiex_wmm_ac_status *ac_status; ac_status = &priv->wmm.ac_status[eval_ac]; if (!ac_status->disabled) /* Okay to use this AC, its enabled */ return eval_ac; /* Setup a default return value of the lowest priority */ ret_ac = WMM_AC_BK; /* * Find the highest AC that is enabled and does not require * admission control. The spec disallows downgrading to an AC, * which is enabled due to a completed admission control. * Unadmitted traffic is not to be sent on an AC with admitted * traffic. */ for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { ac_status = &priv->wmm.ac_status[down_ac]; if (!ac_status->disabled && !ac_status->flow_required) /* AC is enabled and does not require admission control */ ret_ac = (enum mwifiex_wmm_ac_e) down_ac; } return ret_ac; } /* * This function downgrades WMM priority queue. */ void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) { int ac_val; dev_dbg(priv->adapter->dev, "info: WMM: AC Priorities:" "BK(0), BE(1), VI(2), VO(3)\n"); if (!priv->wmm_enabled) { /* WMM is not enabled, default priorities */ for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) priv->wmm.ac_down_graded_vals[ac_val] = (enum mwifiex_wmm_ac_e) ac_val; } else { for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { priv->wmm.ac_down_graded_vals[ac_val] = mwifiex_wmm_eval_downgrade_ac(priv, (enum mwifiex_wmm_ac_e) ac_val); dev_dbg(priv->adapter->dev, "info: WMM: AC PRIO %d maps to %d\n", ac_val, priv->wmm.ac_down_graded_vals[ac_val]); } } } /* * This function converts the IP TOS field to an WMM AC * Queue assignment. */ static enum mwifiex_wmm_ac_e mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) { /* Map of TOS UP values to WMM AC */ const enum mwifiex_wmm_ac_e tos_to_ac[] = { WMM_AC_BE, WMM_AC_BK, WMM_AC_BK, WMM_AC_BE, WMM_AC_VI, WMM_AC_VI, WMM_AC_VO, WMM_AC_VO }; if (tos >= ARRAY_SIZE(tos_to_ac)) return WMM_AC_BE; return tos_to_ac[tos]; } /* * This function evaluates a given TID and downgrades it to a lower * TID if the WMM Parameter IE received from the AP indicates that the * AP is disabled (due to call admission control (ACM bit). Mapping * of TID to AC is taken care of internally. */ static u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) { enum mwifiex_wmm_ac_e ac, ac_down; u8 new_tid; ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); ac_down = priv->wmm.ac_down_graded_vals[ac]; /* Send the index to tid array, picking from the array will be * taken care by dequeuing function */ new_tid = ac_to_tid[ac_down][tid % 2]; return new_tid; } /* * This function initializes the WMM state information and the * WMM data path queues. */ void mwifiex_wmm_init(struct mwifiex_adapter *adapter) { int i, j; struct mwifiex_private *priv; for (j = 0; j < adapter->priv_num; ++j) { priv = adapter->priv[j]; if (!priv) continue; for (i = 0; i < MAX_NUM_TID; ++i) { priv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i]; priv->aggr_prio_tbl[i].ampdu_ap = tos_to_tid_inv[i]; priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; priv->wmm.tid_tbl_ptr[i].ra_list_curr = NULL; } priv->aggr_prio_tbl[6].amsdu = priv->aggr_prio_tbl[6].ampdu_ap = priv->aggr_prio_tbl[6].ampdu_user = BA_STREAM_NOT_ALLOWED; priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap = priv->aggr_prio_tbl[7].ampdu_user = BA_STREAM_NOT_ALLOWED; priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; priv->add_ba_param.tx_win_size = MWIFIEX_AMPDU_DEF_TXWINSIZE; priv->add_ba_param.rx_win_size = MWIFIEX_AMPDU_DEF_RXWINSIZE; mwifiex_reset_11n_rx_seq_num(priv); atomic_set(&priv->wmm.tx_pkts_queued, 0); atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); } } /* * This function checks if WMM Tx queue is empty. */ int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) { int i; struct mwifiex_private *priv; for (i = 0; i < adapter->priv_num; ++i) { priv = adapter->priv[i]; if (priv && atomic_read(&priv->wmm.tx_pkts_queued)) return false; } return true; } /* * This function deletes all packets in an RA list node. * * The packet sent completion callback handler are called with * status failure, after they are dequeued to ensure proper * cleanup. The RA list node itself is freed at the end. */ static void mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list) { struct mwifiex_adapter *adapter = priv->adapter; struct sk_buff *skb, *tmp; skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) mwifiex_write_data_complete(adapter, skb, -1); } /* * This function deletes all packets in an RA list. * * Each nodes in the RA list are freed individually first, and then * the RA list itself is freed. */ static void mwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, struct list_head *ra_list_head) { struct mwifiex_ra_list_tbl *ra_list; list_for_each_entry(ra_list, ra_list_head, list) mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); } /* * This function deletes all packets in all RA lists. */ static void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) { int i; for (i = 0; i < MAX_NUM_TID; i++) mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. ra_list); atomic_set(&priv->wmm.tx_pkts_queued, 0); atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); } /* * This function deletes all route addresses from all RA lists. */ static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) { struct mwifiex_ra_list_tbl *ra_list, *tmp_node; int i; for (i = 0; i < MAX_NUM_TID; ++i) { dev_dbg(priv->adapter->dev, "info: ra_list: freeing buf for tid %d\n", i); list_for_each_entry_safe(ra_list, tmp_node, &priv->wmm.tid_tbl_ptr[i].ra_list, list) { list_del(&ra_list->list); kfree(ra_list); } INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); priv->wmm.tid_tbl_ptr[i].ra_list_curr = NULL; } } /* * This function cleans up the Tx and Rx queues. * * Cleanup includes - * - All packets in RA lists * - All entries in Rx reorder table * - All entries in Tx BA stream table * - MPA buffer (if required) * - All RA lists */ void mwifiex_clean_txrx(struct mwifiex_private *priv) { unsigned long flags; mwifiex_11n_cleanup_reorder_tbl(priv); spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); mwifiex_wmm_cleanup_queues(priv); mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); if (priv->adapter->if_ops.cleanup_mpa_buf) priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); mwifiex_wmm_delete_all_ralist(priv); memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); } /* * This function retrieves a particular RA list node, matching with the * given TID and RA address. */ static struct mwifiex_ra_list_tbl * mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, list) { if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) return ra_list; } return NULL; } /* * This function retrieves an RA list node for a given TID and * RA address pair. * * If no such node is found, a new node is added first and then * retrieved. */ static struct mwifiex_ra_list_tbl * mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); if (ra_list) return ra_list; mwifiex_ralist_add(priv, ra_addr); return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); } /* * This function checks if a particular RA list node exists in a given TID * table index. */ int mwifiex_is_ralist_valid(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list, int ptr_index) { struct mwifiex_ra_list_tbl *rlist; list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, list) { if (rlist == ra_list) return true; } return false; } /* * This function adds a packet to WMM queue. * * In disconnected state the packet is immediately dropped and the * packet send completion callback is called with status failure. * * Otherwise, the correct RA list node is located and the packet * is queued at the list tail. */ void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, struct sk_buff *skb) { struct mwifiex_adapter *adapter = priv->adapter; u32 tid; struct mwifiex_ra_list_tbl *ra_list; u8 ra[ETH_ALEN], tid_down; unsigned long flags; if (!priv->media_connected) { dev_dbg(adapter->dev, "data: drop packet in disconnect\n"); mwifiex_write_data_complete(adapter, skb, -1); return; } tid = skb->priority; spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); tid_down = mwifiex_wmm_downgrade_tid(priv, tid); /* In case of infra as we have already created the list during association we just don't have to call get_queue_raptr, we will have only 1 raptr for a tid in case of infra */ if (!mwifiex_queuing_ra_based(priv)) { if (!list_empty(&priv->wmm.tid_tbl_ptr[tid_down].ra_list)) ra_list = list_first_entry( &priv->wmm.tid_tbl_ptr[tid_down].ra_list, struct mwifiex_ra_list_tbl, list); else ra_list = NULL; } else { memcpy(ra, skb->data, ETH_ALEN); if (ra[0] & 0x01) memset(ra, 0xff, ETH_ALEN); ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); } if (!ra_list) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); mwifiex_write_data_complete(adapter, skb, -1); return; } skb_queue_tail(&ra_list->skb_head, skb); ra_list->total_pkts_size += skb->len; ra_list->pkt_count++; atomic_inc(&priv->wmm.tx_pkts_queued); if (atomic_read(&priv->wmm.highest_queued_prio) < tos_to_tid_inv[tid_down]) atomic_set(&priv->wmm.highest_queued_prio, tos_to_tid_inv[tid_down]); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); } /* * This function processes the get WMM status command response from firmware. * * The response may contain multiple TLVs - * - AC Queue status TLVs * - Current WMM Parameter IE TLV * - Admission Control action frame TLVs * * This function parses the TLVs and then calls further specific functions * to process any changes in the queue prioritize or state. */ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, const struct host_cmd_ds_command *resp) { u8 *curr = (u8 *) &resp->params.get_wmm_status; uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; int valid = true; struct mwifiex_ie_types_data *tlv_hdr; struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; struct ieee_types_wmm_parameter *wmm_param_ie = NULL; struct mwifiex_wmm_ac_status *ac_status; dev_dbg(priv->adapter->dev, "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", resp_len); while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { tlv_hdr = (struct mwifiex_ie_types_data *) curr; tlv_len = le16_to_cpu(tlv_hdr->header.len); switch (le16_to_cpu(tlv_hdr->header.type)) { case TLV_TYPE_WMMQSTATUS: tlv_wmm_qstatus = (struct mwifiex_ie_types_wmm_queue_status *) tlv_hdr; dev_dbg(priv->adapter->dev, "info: CMD_RESP: WMM_GET_STATUS:" " QSTATUS TLV: %d, %d, %d\n", tlv_wmm_qstatus->queue_index, tlv_wmm_qstatus->flow_required, tlv_wmm_qstatus->disabled); ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> queue_index]; ac_status->disabled = tlv_wmm_qstatus->disabled; ac_status->flow_required = tlv_wmm_qstatus->flow_required; ac_status->flow_created = tlv_wmm_qstatus->flow_created; break; case WLAN_EID_VENDOR_SPECIFIC: /* * Point the regular IEEE IE 2 bytes into the Marvell IE * and setup the IEEE IE type and length byte fields */ wmm_param_ie = (struct ieee_types_wmm_parameter *) (curr + 2); wmm_param_ie->vend_hdr.len = (u8) tlv_len; wmm_param_ie->vend_hdr.element_id = WLAN_EID_VENDOR_SPECIFIC; dev_dbg(priv->adapter->dev, "info: CMD_RESP: WMM_GET_STATUS:" " WMM Parameter Set Count: %d\n", wmm_param_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK); memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. wmm_ie, wmm_param_ie, wmm_param_ie->vend_hdr.len + 2); break; default: valid = false; break; } curr += (tlv_len + sizeof(tlv_hdr->header)); resp_len -= (tlv_len + sizeof(tlv_hdr->header)); } mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); mwifiex_wmm_setup_ac_downgrade(priv); return 0; } /* * Callback handler from the command module to allow insertion of a WMM TLV. * * If the BSS we are associating to supports WMM, this function adds the * required WMM Information IE to the association request command buffer in * the form of a Marvell extended IEEE IE. */ u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, u8 **assoc_buf, struct ieee_types_wmm_parameter *wmm_ie, struct ieee80211_ht_cap *ht_cap) { struct mwifiex_ie_types_wmm_param_set *wmm_tlv; u32 ret_len = 0; /* Null checks */ if (!assoc_buf) return 0; if (!(*assoc_buf)) return 0; if (!wmm_ie) return 0; dev_dbg(priv->adapter->dev, "info: WMM: process assoc req: bss->wmm_ie=%#x\n", wmm_ie->vend_hdr.element_id); if ((priv->wmm_required || (ht_cap && (priv->adapter->config_bands & BAND_GN || priv->adapter->config_bands & BAND_AN))) && wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], le16_to_cpu(wmm_tlv->header.len)); if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) memcpy((u8 *) (wmm_tlv->wmm_ie + le16_to_cpu(wmm_tlv->header.len) - sizeof(priv->wmm_qosinfo)), &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); ret_len = sizeof(wmm_tlv->header) + le16_to_cpu(wmm_tlv->header.len); *assoc_buf += ret_len; } return ret_len; } /* * This function computes the time delay in the driver queues for a * given packet. * * When the packet is received at the OS/Driver interface, the current * time is set in the packet structure. The difference between the present * time and that received time is computed in this function and limited * based on pre-compiled limits in the driver. */ u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, const struct sk_buff *skb) { u8 ret_val; struct timeval out_tstamp, in_tstamp; u32 queue_delay; do_gettimeofday(&out_tstamp); in_tstamp = ktime_to_timeval(skb->tstamp); queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000; queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000; /* * Queue delay is passed as a uint8 in units of 2ms (ms shifted * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. * * Pass max value if queue_delay is beyond the uint8 range */ ret_val = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); dev_dbg(priv->adapter->dev, "data: WMM: Pkt Delay: %d ms," " %d ms sent to FW\n", queue_delay, ret_val); return ret_val; } /* * This function retrieves the highest priority RA list table pointer. */ static struct mwifiex_ra_list_tbl * mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, struct mwifiex_private **priv, int *tid) { struct mwifiex_private *priv_tmp; struct mwifiex_ra_list_tbl *ptr, *head; struct mwifiex_bss_prio_node *bssprio_node, *bssprio_head; struct mwifiex_tid_tbl *tid_ptr; atomic_t *hqp; int is_list_empty; unsigned long flags; int i, j; for (j = adapter->priv_num - 1; j >= 0; --j) { spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock, flags); is_list_empty = list_empty(&adapter->bss_prio_tbl[j] .bss_prio_head); spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock, flags); if (is_list_empty) continue; if (adapter->bss_prio_tbl[j].bss_prio_cur == (struct mwifiex_bss_prio_node *) &adapter->bss_prio_tbl[j].bss_prio_head) { adapter->bss_prio_tbl[j].bss_prio_cur = list_first_entry(&adapter->bss_prio_tbl[j] .bss_prio_head, struct mwifiex_bss_prio_node, list); } bssprio_node = adapter->bss_prio_tbl[j].bss_prio_cur; bssprio_head = bssprio_node; do { priv_tmp = bssprio_node->priv; hqp = &priv_tmp->wmm.highest_queued_prio; for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { tid_ptr = &(priv_tmp)->wmm. tid_tbl_ptr[tos_to_tid[i]]; /* For non-STA ra_list_curr may be NULL */ if (!tid_ptr->ra_list_curr) continue; spin_lock_irqsave(&tid_ptr->tid_tbl_lock, flags); is_list_empty = list_empty(&adapter->bss_prio_tbl[j] .bss_prio_head); spin_unlock_irqrestore(&tid_ptr->tid_tbl_lock, flags); if (is_list_empty) continue; /* * Always choose the next ra we transmitted * last time, this way we pick the ra's in * round robin fashion. */ ptr = list_first_entry( &tid_ptr->ra_list_curr->list, struct mwifiex_ra_list_tbl, list); head = ptr; if (ptr == (struct mwifiex_ra_list_tbl *) &tid_ptr->ra_list) { /* Get next ra */ ptr = list_first_entry(&ptr->list, struct mwifiex_ra_list_tbl, list); head = ptr; } do { is_list_empty = skb_queue_empty(&ptr->skb_head); if (!is_list_empty) goto found; /* Get next ra */ ptr = list_first_entry(&ptr->list, struct mwifiex_ra_list_tbl, list); if (ptr == (struct mwifiex_ra_list_tbl *) &tid_ptr->ra_list) ptr = list_first_entry( &ptr->list, struct mwifiex_ra_list_tbl, list); } while (ptr != head); } /* No packet at any TID for this priv. Mark as such * to skip checking TIDs for this priv (until pkt is * added). */ atomic_set(hqp, NO_PKT_PRIO_TID); /* Get next bss priority node */ bssprio_node = list_first_entry(&bssprio_node->list, struct mwifiex_bss_prio_node, list); if (bssprio_node == (struct mwifiex_bss_prio_node *) &adapter->bss_prio_tbl[j].bss_prio_head) /* Get next bss priority node */ bssprio_node = list_first_entry( &bssprio_node->list, struct mwifiex_bss_prio_node, list); } while (bssprio_node != bssprio_head); } return NULL; found: spin_lock_irqsave(&priv_tmp->wmm.ra_list_spinlock, flags); if (atomic_read(hqp) > i) atomic_set(hqp, i); spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags); *priv = priv_tmp; *tid = tos_to_tid[i]; return ptr; } /* * This function checks if 11n aggregation is possible. */ static int mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr, int max_buf_size) { int count = 0, total_size = 0; struct sk_buff *skb, *tmp; int max_amsdu_size; if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP && priv->ap_11n_enabled && ptr->is_11n_enabled) max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size); else max_amsdu_size = max_buf_size; skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { total_size += skb->len; if (total_size >= max_amsdu_size) break; if (++count >= MIN_NUM_AMSDU) return true; } return false; } /* * This function sends a single packet to firmware for transmission. */ static void mwifiex_send_single_packet(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr, int ptr_index, unsigned long ra_list_flags) __releases(&priv->wmm.ra_list_spinlock) { struct sk_buff *skb, *skb_next; struct mwifiex_tx_param tx_param; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_txinfo *tx_info; if (skb_queue_empty(&ptr->skb_head)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); dev_dbg(adapter->dev, "data: nothing to send\n"); return; } skb = skb_dequeue(&ptr->skb_head); tx_info = MWIFIEX_SKB_TXCB(skb); dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb); ptr->total_pkts_size -= skb->len; if (!skb_queue_empty(&ptr->skb_head)) skb_next = skb_peek(&ptr->skb_head); else skb_next = NULL; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); tx_param.next_pkt_len = ((skb_next) ? skb_next->len + sizeof(struct txpd) : 0); if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { /* Queue the packet back at the head */ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); mwifiex_write_data_complete(adapter, skb, -1); return; } skb_queue_tail(&ptr->skb_head, skb); ptr->total_pkts_size += skb->len; ptr->pkt_count++; tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); } else { spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); if (mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { priv->wmm.packets_out[ptr_index]++; priv->wmm.tid_tbl_ptr[ptr_index].ra_list_curr = ptr; } adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = list_first_entry( &adapter->bss_prio_tbl[priv->bss_priority] .bss_prio_cur->list, struct mwifiex_bss_prio_node, list); atomic_dec(&priv->wmm.tx_pkts_queued); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); } } /* * This function checks if the first packet in the given RA list * is already processed or not. */ static int mwifiex_is_ptr_processed(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr) { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; if (skb_queue_empty(&ptr->skb_head)) return false; skb = skb_peek(&ptr->skb_head); tx_info = MWIFIEX_SKB_TXCB(skb); if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) return true; return false; } /* * This function sends a single processed packet to firmware for * transmission. */ static void mwifiex_send_processed_packet(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr, int ptr_index, unsigned long ra_list_flags) __releases(&priv->wmm.ra_list_spinlock) { struct mwifiex_tx_param tx_param; struct mwifiex_adapter *adapter = priv->adapter; int ret = -1; struct sk_buff *skb, *skb_next; struct mwifiex_txinfo *tx_info; if (skb_queue_empty(&ptr->skb_head)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); return; } skb = skb_dequeue(&ptr->skb_head); if (!skb_queue_empty(&ptr->skb_head)) skb_next = skb_peek(&ptr->skb_head); else skb_next = NULL; tx_info = MWIFIEX_SKB_TXCB(skb); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); if (adapter->iface_type == MWIFIEX_USB) { adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); } else { tx_param.next_pkt_len = ((skb_next) ? skb_next->len + sizeof(struct txpd) : 0); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, skb, &tx_param); } switch (ret) { case -EBUSY: dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); mwifiex_write_data_complete(adapter, skb, -1); return; } skb_queue_tail(&ptr->skb_head, skb); tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); break; case -1: adapter->data_sent = false; dev_err(adapter->dev, "host_to_card failed: %#x\n", ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb, ret); break; case -EINPROGRESS: adapter->data_sent = false; default: break; } if (ret != -EBUSY) { spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); if (mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { priv->wmm.packets_out[ptr_index]++; priv->wmm.tid_tbl_ptr[ptr_index].ra_list_curr = ptr; } adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = list_first_entry( &adapter->bss_prio_tbl[priv->bss_priority] .bss_prio_cur->list, struct mwifiex_bss_prio_node, list); atomic_dec(&priv->wmm.tx_pkts_queued); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); } } /* * This function dequeues a packet from the highest priority list * and transmits it. */ static int mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) { struct mwifiex_ra_list_tbl *ptr; struct mwifiex_private *priv = NULL; int ptr_index = 0; u8 ra[ETH_ALEN]; int tid_del = 0, tid = 0; unsigned long flags; ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); if (!ptr) return -1; tid = mwifiex_get_tid(ptr); dev_dbg(adapter->dev, "data: tid=%d\n", tid); spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); return -1; } if (mwifiex_is_ptr_processed(priv, ptr)) { mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); /* ra_list_spinlock has been freed in mwifiex_send_processed_packet() */ return 0; } if (!ptr->is_11n_enabled || mwifiex_is_ba_stream_setup(priv, ptr, tid) || priv->wps.session_enable || ((priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) && !priv->wpa_is_gtk_set)) { mwifiex_send_single_packet(priv, ptr, ptr_index, flags); /* ra_list_spinlock has been freed in mwifiex_send_single_packet() */ } else { if (mwifiex_is_ampdu_allowed(priv, tid) && ptr->pkt_count > ptr->ba_packet_thr) { if (mwifiex_space_avail_for_new_ba_stream(adapter)) { mwifiex_create_ba_tbl(priv, ptr->ra, tid, BA_SETUP_INPROGRESS); mwifiex_send_addba(priv, tid, ptr->ra); } else if (mwifiex_find_stream_to_delete (priv, tid, &tid_del, ra)) { mwifiex_create_ba_tbl(priv, ptr->ra, tid, BA_SETUP_INPROGRESS); mwifiex_send_delba(priv, tid_del, ra, 1); } } if (mwifiex_is_amsdu_allowed(priv, tid) && mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, ptr_index, flags); /* ra_list_spinlock has been freed in mwifiex_11n_aggregate_pkt() */ else mwifiex_send_single_packet(priv, ptr, ptr_index, flags); /* ra_list_spinlock has been freed in mwifiex_send_single_packet() */ } return 0; } /* * This function transmits the highest priority packet awaiting in the * WMM Queues. */ void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) { do { /* Check if busy */ if (adapter->data_sent || adapter->tx_lock_flag) break; if (mwifiex_dequeue_tx_packet(adapter)) break; } while (!mwifiex_wmm_lists_empty(adapter)); } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/util.h0000644000175000017500000000253112026211315023260 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: utility functions * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_UTIL_H_ #define _MWIFIEX_UTIL_H_ static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) { return (struct mwifiex_rxinfo *)(skb->cb + sizeof(phys_addr_t)); } static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) { return (struct mwifiex_txinfo *)(skb->cb + sizeof(phys_addr_t)); } static inline phys_addr_t *MWIFIEX_SKB_PACB(struct sk_buff *skb) { return (phys_addr_t *)skb->cb; } #endif /* !_MWIFIEX_UTIL_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/util.c0000644000175000017500000001670712026211315023265 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: utility functions * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* * Firmware initialization complete callback handler. * * This function wakes up the function waiting on the init * wait queue for the firmware initialization to complete. */ int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) { adapter->init_wait_q_woken = true; wake_up_interruptible(&adapter->init_wait_q); return 0; } /* * Firmware shutdown complete callback handler. * * This function sets the hardware status to not ready and wakes up * the function waiting on the init wait queue for the firmware * shutdown to complete. */ int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter) { adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY; adapter->init_wait_q_woken = true; wake_up_interruptible(&adapter->init_wait_q); return 0; } /* * This function sends init/shutdown command * to firmware. */ int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown) { u16 cmd; if (func_init_shutdown == MWIFIEX_FUNC_INIT) { cmd = HostCmd_CMD_FUNC_INIT; } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { cmd = HostCmd_CMD_FUNC_SHUTDOWN; } else { dev_err(priv->adapter->dev, "unsupported parameter\n"); return -1; } return mwifiex_send_cmd_sync(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL); } EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); /* * IOCTL request handler to set/get debug information. * * This function collates/sets the information from/to different driver * structures. */ int mwifiex_get_debug_info(struct mwifiex_private *priv, struct mwifiex_debug_info *info) { struct mwifiex_adapter *adapter = priv->adapter; if (info) { memcpy(info->packets_out, priv->wmm.packets_out, sizeof(priv->wmm.packets_out)); info->max_tx_buf_size = (u32) adapter->max_tx_buf_size; info->tx_buf_size = (u32) adapter->tx_buf_size; info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv, info->rx_tbl); info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, info->tx_tbl); info->ps_mode = adapter->ps_mode; info->ps_state = adapter->ps_state; info->is_deep_sleep = adapter->is_deep_sleep; info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; info->is_hs_configured = adapter->is_hs_configured; info->hs_activated = adapter->hs_activated; info->num_cmd_host_to_card_failure = adapter->dbg.num_cmd_host_to_card_failure; info->num_cmd_sleep_cfm_host_to_card_failure = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; info->num_tx_host_to_card_failure = adapter->dbg.num_tx_host_to_card_failure; info->num_event_deauth = adapter->dbg.num_event_deauth; info->num_event_disassoc = adapter->dbg.num_event_disassoc; info->num_event_link_lost = adapter->dbg.num_event_link_lost; info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; info->num_cmd_assoc_success = adapter->dbg.num_cmd_assoc_success; info->num_cmd_assoc_failure = adapter->dbg.num_cmd_assoc_failure; info->num_tx_timeout = adapter->dbg.num_tx_timeout; info->num_cmd_timeout = adapter->dbg.num_cmd_timeout; info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, sizeof(adapter->dbg.last_cmd_id)); memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, sizeof(adapter->dbg.last_cmd_act)); info->last_cmd_index = adapter->dbg.last_cmd_index; memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, sizeof(adapter->dbg.last_cmd_resp_id)); info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; memcpy(info->last_event, adapter->dbg.last_event, sizeof(adapter->dbg.last_event)); info->last_event_index = adapter->dbg.last_event_index; info->data_sent = adapter->data_sent; info->cmd_sent = adapter->cmd_sent; info->cmd_resp_received = adapter->cmd_resp_received; } return 0; } /* * This function processes the received packet before sending it to the * kernel. * * It extracts the SKB from the received buffer and sends it to kernel. * In case the received buffer does not contain the data in SKB format, * the function creates a blank SKB, fills it with the data from the * received buffer and then sends this new SKB to the kernel. */ int mwifiex_recv_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct mwifiex_rxinfo *rx_info; struct mwifiex_private *priv; if (!skb) return -1; rx_info = MWIFIEX_SKB_RXCB(skb); priv = mwifiex_get_priv_by_id(adapter, rx_info->bss_num, rx_info->bss_type); if (!priv) return -1; skb->dev = priv->netdev; skb->protocol = eth_type_trans(skb, priv->netdev); skb->ip_summed = CHECKSUM_NONE; /* This is required only in case of 11n and USB as we alloc * a buffer of 4K only if its 11N (to be able to receive 4K * AMSDU packets). In case of SD we allocate buffers based * on the size of packet and hence this is not needed. * * Modifying the truesize here as our allocation for each * skb is 4K but we only receive 2K packets and this cause * the kernel to start dropping packets in case where * application has allocated buffer based on 2K size i.e. * if there a 64K packet received (in IP fragments and * application allocates 64K to receive this packet but * this packet would almost double up because we allocate * each 1.5K fragment in 4K and pass it up. As soon as the * 64K limit hits kernel will start to drop rest of the * fragments. Currently we fail the Filesndl-ht.scr script * for UDP, hence this fix */ if ((adapter->iface_type == MWIFIEX_USB) && (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); priv->stats.rx_bytes += skb->len; priv->stats.rx_packets++; if (in_interrupt()) netif_rx(skb); else netif_rx_ni(skb); return 0; } /* * IOCTL completion callback handler. * * This function is called when a pending IOCTL is completed. * * If work queue support is enabled, the function wakes up the * corresponding waiting function. Otherwise, it processes the * IOCTL response and frees the response buffer. */ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node) { atomic_dec(&adapter->cmd_pending); dev_dbg(adapter->dev, "cmd completed: status=%d\n", adapter->cmd_wait_q.status); *(cmd_node->condition) = true; if (adapter->cmd_wait_q.status == -ETIMEDOUT) dev_err(adapter->dev, "cmd timeout\n"); else wake_up_interruptible(&adapter->cmd_wait_q.wait); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/usb.h0000644000175000017500000000474212026211315023102 0ustar mcgrofmcgrof/* * This file contains definitions for mwifiex USB interface driver. * * Copyright (C) 2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_USB_H #define _MWIFIEX_USB_H #include #define USB8797_VID 0x1286 #define USB8797_PID_1 0x2043 #define USB8797_PID_2 0x2044 #define USB8797_FW_DNLD 1 #define USB8797_FW_READY 2 #define USB8797_FW_MAX_RETRY 3 #define MWIFIEX_TX_DATA_URB 6 #define MWIFIEX_RX_DATA_URB 6 #define MWIFIEX_USB_TIMEOUT 100 #define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" #define FW_DNLD_TX_BUF_SIZE 620 #define FW_DNLD_RX_BUF_SIZE 2048 #define FW_HAS_LAST_BLOCK 0x00000004 #define FW_DATA_XMIT_SIZE \ (sizeof(struct fw_header) + dlen + sizeof(u32)) struct urb_context { struct mwifiex_adapter *adapter; struct sk_buff *skb; struct urb *urb; u8 ep; }; struct usb_card_rec { struct mwifiex_adapter *adapter; struct usb_device *udev; struct usb_interface *intf; u8 rx_cmd_ep; struct urb_context rx_cmd; atomic_t rx_cmd_urb_pending; struct urb_context rx_data_list[MWIFIEX_RX_DATA_URB]; u8 usb_boot_state; u8 rx_data_ep; atomic_t rx_data_urb_pending; u8 tx_data_ep; u8 tx_cmd_ep; atomic_t tx_data_urb_pending; atomic_t tx_cmd_urb_pending; int bulk_out_maxpktsize; struct urb_context tx_cmd; int tx_data_ix; struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; }; struct fw_header { __le32 dnld_cmd; __le32 base_addr; __le32 data_len; __le32 crc; }; struct fw_sync_header { __le32 cmd; __le32 seq_num; }; struct fw_data { struct fw_header fw_hdr; __le32 seq_num; u8 data[1]; }; /* This function is called after the card has woken up. */ static inline int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) { return 0; } #endif /*_MWIFIEX_USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/usb.c0000644000175000017500000006717612026211315023107 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: USB specific handling * * Copyright (C) 2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "main.h" #include "usb.h" #define USB_VERSION "1.0" static const char usbdriver_name[] = "usb8797"; static u8 user_rmmod; static struct mwifiex_if_ops usb_ops; static struct semaphore add_remove_card_sem; static struct usb_device_id mwifiex_usb_table[] = { {USB_DEVICE(USB8797_VID, USB8797_PID_1)}, {USB_DEVICE_AND_INTERFACE_INFO(USB8797_VID, USB8797_PID_2, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0xff)}, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, mwifiex_usb_table); static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size); /* This function handles received packet. Necessary action is taken based on * cmd/event/data. */ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, struct sk_buff *skb, u8 ep) { struct device *dev = adapter->dev; u32 recv_type; __le32 tmp; int ret; if (adapter->hs_activated) mwifiex_process_hs_config(adapter); if (skb->len < INTF_HEADER_LEN) { dev_err(dev, "%s: invalid skb->len\n", __func__); return -1; } switch (ep) { case MWIFIEX_USB_EP_CMD_EVENT: dev_dbg(dev, "%s: EP_CMD_EVENT\n", __func__); skb_copy_from_linear_data(skb, &tmp, INTF_HEADER_LEN); recv_type = le32_to_cpu(tmp); skb_pull(skb, INTF_HEADER_LEN); switch (recv_type) { case MWIFIEX_USB_TYPE_CMD: if (skb->len > MWIFIEX_SIZE_OF_CMD_BUFFER) { dev_err(dev, "CMD: skb->len too large\n"); ret = -1; goto exit_restore_skb; } else if (!adapter->curr_cmd) { dev_dbg(dev, "CMD: no curr_cmd\n"); if (adapter->ps_state == PS_STATE_SLEEP_CFM) { mwifiex_process_sleep_confirm_resp( adapter, skb->data, skb->len); ret = 0; goto exit_restore_skb; } ret = -1; goto exit_restore_skb; } adapter->curr_cmd->resp_skb = skb; adapter->cmd_resp_received = true; break; case MWIFIEX_USB_TYPE_EVENT: if (skb->len < sizeof(u32)) { dev_err(dev, "EVENT: skb->len too small\n"); ret = -1; goto exit_restore_skb; } skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); adapter->event_cause = le32_to_cpu(tmp); dev_dbg(dev, "event_cause %#x\n", adapter->event_cause); if (skb->len > MAX_EVENT_SIZE) { dev_err(dev, "EVENT: event body too large\n"); ret = -1; goto exit_restore_skb; } memcpy(adapter->event_body, skb->data + MWIFIEX_EVENT_HEADER_LEN, skb->len); adapter->event_received = true; adapter->event_skb = skb; break; default: dev_err(dev, "unknown recv_type %#x\n", recv_type); return -1; } break; case MWIFIEX_USB_EP_DATA: dev_dbg(dev, "%s: EP_DATA\n", __func__); if (skb->len > MWIFIEX_RX_DATA_BUF_SIZE) { dev_err(dev, "DATA: skb->len too large\n"); return -1; } skb_queue_tail(&adapter->usb_rx_data_q, skb); adapter->data_received = true; break; default: dev_err(dev, "%s: unknown endport %#x\n", __func__, ep); return -1; } return -EINPROGRESS; exit_restore_skb: /* The buffer will be reused for further cmds/events */ skb_push(skb, INTF_HEADER_LEN); return ret; } static void mwifiex_usb_rx_complete(struct urb *urb) { struct urb_context *context = (struct urb_context *)urb->context; struct mwifiex_adapter *adapter = context->adapter; struct sk_buff *skb = context->skb; struct usb_card_rec *card; int recv_length = urb->actual_length; int size, status; if (!adapter || !adapter->card) { pr_err("mwifiex adapter or card structure is not valid\n"); return; } card = (struct usb_card_rec *)adapter->card; if (card->rx_cmd_ep == context->ep) atomic_dec(&card->rx_cmd_urb_pending); else atomic_dec(&card->rx_data_urb_pending); if (recv_length) { if (urb->status || (adapter->surprise_removed)) { dev_err(adapter->dev, "URB status is failed: %d\n", urb->status); /* Do not free skb in case of command ep */ if (card->rx_cmd_ep != context->ep) dev_kfree_skb_any(skb); goto setup_for_next; } if (skb->len > recv_length) skb_trim(skb, recv_length); else skb_put(skb, recv_length - skb->len); atomic_inc(&adapter->rx_pending); status = mwifiex_usb_recv(adapter, skb, context->ep); dev_dbg(adapter->dev, "info: recv_length=%d, status=%d\n", recv_length, status); if (status == -EINPROGRESS) { queue_work(adapter->workqueue, &adapter->main_work); /* urb for data_ep is re-submitted now; * urb for cmd_ep will be re-submitted in callback * mwifiex_usb_recv_complete */ if (card->rx_cmd_ep == context->ep) return; } else { atomic_dec(&adapter->rx_pending); if (status == -1) dev_err(adapter->dev, "received data processing failed!\n"); /* Do not free skb in case of command ep */ if (card->rx_cmd_ep != context->ep) dev_kfree_skb_any(skb); } } else if (urb->status) { if (!adapter->is_suspended) { dev_warn(adapter->dev, "Card is removed: %d\n", urb->status); adapter->surprise_removed = true; } dev_kfree_skb_any(skb); return; } else { /* Do not free skb in case of command ep */ if (card->rx_cmd_ep != context->ep) dev_kfree_skb_any(skb); /* fall through setup_for_next */ } setup_for_next: if (card->rx_cmd_ep == context->ep) size = MWIFIEX_RX_CMD_BUF_SIZE; else size = MWIFIEX_RX_DATA_BUF_SIZE; mwifiex_usb_submit_rx_urb(context, size); return; } static void mwifiex_usb_tx_complete(struct urb *urb) { struct urb_context *context = (struct urb_context *)(urb->context); struct mwifiex_adapter *adapter = context->adapter; struct usb_card_rec *card = adapter->card; dev_dbg(adapter->dev, "%s: status: %d\n", __func__, urb->status); if (context->ep == card->tx_cmd_ep) { dev_dbg(adapter->dev, "%s: CMD\n", __func__); atomic_dec(&card->tx_cmd_urb_pending); adapter->cmd_sent = false; } else { dev_dbg(adapter->dev, "%s: DATA\n", __func__); atomic_dec(&card->tx_data_urb_pending); mwifiex_write_data_complete(adapter, context->skb, urb->status ? -1 : 0); } queue_work(adapter->workqueue, &adapter->main_work); return; } static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) { struct mwifiex_adapter *adapter = ctx->adapter; struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; if (card->rx_cmd_ep != ctx->ep) { ctx->skb = dev_alloc_skb(size); if (!ctx->skb) { dev_err(adapter->dev, "%s: dev_alloc_skb failed\n", __func__); return -ENOMEM; } } usb_fill_bulk_urb(ctx->urb, card->udev, usb_rcvbulkpipe(card->udev, ctx->ep), ctx->skb->data, size, mwifiex_usb_rx_complete, (void *)ctx); if (card->rx_cmd_ep == ctx->ep) atomic_inc(&card->rx_cmd_urb_pending); else atomic_inc(&card->rx_data_urb_pending); if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { dev_err(adapter->dev, "usb_submit_urb failed\n"); dev_kfree_skb_any(ctx->skb); ctx->skb = NULL; if (card->rx_cmd_ep == ctx->ep) atomic_dec(&card->rx_cmd_urb_pending); else atomic_dec(&card->rx_data_urb_pending); return -1; } return 0; } static void mwifiex_usb_free(struct usb_card_rec *card) { int i; if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) usb_kill_urb(card->rx_cmd.urb); usb_free_urb(card->rx_cmd.urb); card->rx_cmd.urb = NULL; if (atomic_read(&card->rx_data_urb_pending)) for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) if (card->rx_data_list[i].urb) usb_kill_urb(card->rx_data_list[i].urb); for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { usb_free_urb(card->rx_data_list[i].urb); card->rx_data_list[i].urb = NULL; } for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) { usb_free_urb(card->tx_data_list[i].urb); card->tx_data_list[i].urb = NULL; } usb_free_urb(card->tx_cmd.urb); card->tx_cmd.urb = NULL; return; } /* This function probes an mwifiex device and registers it. It allocates * the card structure, initiates the device registration and initialization * procedure by adding a logical interface. */ static int mwifiex_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *iface_desc = intf->cur_altsetting; struct usb_endpoint_descriptor *epd; int ret, i; struct usb_card_rec *card; u16 id_vendor, id_product, bcd_device, bcd_usb; card = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); if (!card) return -ENOMEM; id_vendor = le16_to_cpu(udev->descriptor.idVendor); id_product = le16_to_cpu(udev->descriptor.idProduct); bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); bcd_usb = le16_to_cpu(udev->descriptor.bcdUSB); pr_debug("info: VID/PID = %X/%X, Boot2 version = %X\n", id_vendor, id_product, bcd_device); /* PID_1 is used for firmware downloading only */ if (id_product == USB8797_PID_1) card->usb_boot_state = USB8797_FW_DNLD; else card->usb_boot_state = USB8797_FW_READY; card->udev = udev; card->intf = intf; pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocl=%#x\n", udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass, udev->descriptor.bDeviceSubClass, udev->descriptor.bDeviceProtocol); for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { epd = &iface_desc->endpoint[i].desc; if (usb_endpoint_dir_in(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && usb_endpoint_xfer_bulk(epd)) { pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", le16_to_cpu(epd->wMaxPacketSize), epd->bEndpointAddress); card->rx_cmd_ep = usb_endpoint_num(epd); atomic_set(&card->rx_cmd_urb_pending, 0); } if (usb_endpoint_dir_in(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && usb_endpoint_xfer_bulk(epd)) { pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", le16_to_cpu(epd->wMaxPacketSize), epd->bEndpointAddress); card->rx_data_ep = usb_endpoint_num(epd); atomic_set(&card->rx_data_urb_pending, 0); } if (usb_endpoint_dir_out(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && usb_endpoint_xfer_bulk(epd)) { pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", le16_to_cpu(epd->wMaxPacketSize), epd->bEndpointAddress); card->tx_data_ep = usb_endpoint_num(epd); atomic_set(&card->tx_data_urb_pending, 0); } if (usb_endpoint_dir_out(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && usb_endpoint_xfer_bulk(epd)) { pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", le16_to_cpu(epd->wMaxPacketSize), epd->bEndpointAddress); card->tx_cmd_ep = usb_endpoint_num(epd); atomic_set(&card->tx_cmd_urb_pending, 0); card->bulk_out_maxpktsize = le16_to_cpu(epd->wMaxPacketSize); } } usb_set_intfdata(intf, card); ret = mwifiex_add_card(card, &add_remove_card_sem, &usb_ops, MWIFIEX_USB); if (ret) { pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); usb_reset_device(udev); kfree(card); return ret; } usb_get_dev(udev); return 0; } /* Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not suspended, this function allocates and sends a * 'host sleep activate' request to the firmware and turns off the traffic. */ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; int i; if (!card || !card->adapter) { pr_err("%s: card or card->adapter is NULL\n", __func__); return 0; } adapter = card->adapter; if (unlikely(adapter->is_suspended)) dev_warn(adapter->dev, "Device already suspended\n"); mwifiex_enable_hs(adapter); /* 'is_suspended' flag indicates device is suspended. * It must be set here before the usb_kill_urb() calls. Reason * is in the complete handlers, urb->status(= -ENOENT) and * this flag is used in combination to distinguish between a * 'suspended' state and a 'disconnect' one. */ adapter->is_suspended = true; for (i = 0; i < adapter->priv_num; i++) netif_carrier_off(adapter->priv[i]->netdev); if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) usb_kill_urb(card->rx_cmd.urb); if (atomic_read(&card->rx_data_urb_pending)) for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) if (card->rx_data_list[i].urb) usb_kill_urb(card->rx_data_list[i].urb); for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) if (card->tx_data_list[i].urb) usb_kill_urb(card->tx_data_list[i].urb); if (card->tx_cmd.urb) usb_kill_urb(card->tx_cmd.urb); return 0; } /* Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not resumed, this function turns on the traffic and * sends a 'host sleep cancel' request to the firmware. */ static int mwifiex_usb_resume(struct usb_interface *intf) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; int i; if (!card || !card->adapter) { pr_err("%s: card or card->adapter is NULL\n", __func__); return 0; } adapter = card->adapter; if (unlikely(!adapter->is_suspended)) { dev_warn(adapter->dev, "Device already resumed\n"); return 0; } /* Indicate device resumed. The netdev queue will be resumed only * after the urbs have been re-submitted */ adapter->is_suspended = false; if (!atomic_read(&card->rx_data_urb_pending)) for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], MWIFIEX_RX_DATA_BUF_SIZE); if (!atomic_read(&card->rx_cmd_urb_pending)) { card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); if (card->rx_cmd.skb) mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE); } for (i = 0; i < adapter->priv_num; i++) if (adapter->priv[i]->media_connected) netif_carrier_on(adapter->priv[i]->netdev); /* Disable Host Sleep */ if (adapter->hs_activated) mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), MWIFIEX_ASYNC_CMD); #ifdef CONFIG_PM /* Resume handler may be called due to remote wakeup, * force to exit suspend anyway */ usb_disable_autosuspend(card->udev); #endif /* CONFIG_PM */ return 0; } static void mwifiex_usb_disconnect(struct usb_interface *intf) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; int i; if (!card || !card->adapter) { pr_err("%s: card or card->adapter is NULL\n", __func__); return; } adapter = card->adapter; if (!adapter->priv_num) return; /* In case driver is removed when asynchronous FW downloading is * in progress */ wait_for_completion(&adapter->fw_load); if (user_rmmod) { #ifdef CONFIG_PM if (adapter->is_suspended) mwifiex_usb_resume(intf); #endif for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), MWIFIEX_FUNC_SHUTDOWN); } mwifiex_usb_free(card); dev_dbg(adapter->dev, "%s: removing card\n", __func__); mwifiex_remove_card(adapter, &add_remove_card_sem); usb_set_intfdata(intf, NULL); usb_put_dev(interface_to_usbdev(intf)); kfree(card); return; } static struct usb_driver mwifiex_usb_driver = { .name = usbdriver_name, .probe = mwifiex_usb_probe, .disconnect = mwifiex_usb_disconnect, .id_table = mwifiex_usb_table, .suspend = mwifiex_usb_suspend, .resume = mwifiex_usb_resume, .supports_autosuspend = 1, }; static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) { struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; int i; card->tx_cmd.adapter = adapter; card->tx_cmd.ep = card->tx_cmd_ep; card->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); if (!card->tx_cmd.urb) { dev_err(adapter->dev, "tx_cmd.urb allocation failed\n"); return -ENOMEM; } card->tx_data_ix = 0; for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) { card->tx_data_list[i].adapter = adapter; card->tx_data_list[i].ep = card->tx_data_ep; card->tx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); if (!card->tx_data_list[i].urb) { dev_err(adapter->dev, "tx_data_list[] urb allocation failed\n"); return -ENOMEM; } } return 0; } static int mwifiex_usb_rx_init(struct mwifiex_adapter *adapter) { struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; int i; card->rx_cmd.adapter = adapter; card->rx_cmd.ep = card->rx_cmd_ep; card->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); if (!card->rx_cmd.urb) { dev_err(adapter->dev, "rx_cmd.urb allocation failed\n"); return -ENOMEM; } card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); if (!card->rx_cmd.skb) { dev_err(adapter->dev, "rx_cmd.skb allocation failed\n"); return -ENOMEM; } if (mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE)) return -1; for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { card->rx_data_list[i].adapter = adapter; card->rx_data_list[i].ep = card->rx_data_ep; card->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); if (!card->rx_data_list[i].urb) { dev_err(adapter->dev, "rx_data_list[] urb allocation failed\n"); return -1; } if (mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], MWIFIEX_RX_DATA_BUF_SIZE)) return -1; } return 0; } static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, u32 *len, u8 ep, u32 timeout) { struct usb_card_rec *card = adapter->card; int actual_length, ret; if (!(*len % card->bulk_out_maxpktsize)) (*len)++; /* Send the data block */ ret = usb_bulk_msg(card->udev, usb_sndbulkpipe(card->udev, ep), pbuf, *len, &actual_length, timeout); if (ret) { dev_err(adapter->dev, "usb_bulk_msg for tx failed: %d\n", ret); ret = -1; } *len = actual_length; return ret; } static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, u32 *len, u8 ep, u32 timeout) { struct usb_card_rec *card = adapter->card; int actual_length, ret; /* Receive the data response */ ret = usb_bulk_msg(card->udev, usb_rcvbulkpipe(card->udev, ep), pbuf, *len, &actual_length, timeout); if (ret) { dev_err(adapter->dev, "usb_bulk_msg for rx failed: %d\n", ret); ret = -1; } *len = actual_length; return ret; } /* This function write a command/data packet to card. */ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { struct usb_card_rec *card = adapter->card; struct urb_context *context; u8 *data = (u8 *)skb->data; struct urb *tx_urb; if (adapter->is_suspended) { dev_err(adapter->dev, "%s: not allowed while suspended\n", __func__); return -1; } if (adapter->surprise_removed) { dev_err(adapter->dev, "%s: device removed\n", __func__); return -1; } if (ep == card->tx_data_ep && atomic_read(&card->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { return -EBUSY; } dev_dbg(adapter->dev, "%s: ep=%d\n", __func__, ep); if (ep == card->tx_cmd_ep) { context = &card->tx_cmd; } else { if (card->tx_data_ix >= MWIFIEX_TX_DATA_URB) card->tx_data_ix = 0; context = &card->tx_data_list[card->tx_data_ix++]; } context->adapter = adapter; context->ep = ep; context->skb = skb; tx_urb = context->urb; usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), data, skb->len, mwifiex_usb_tx_complete, (void *)context); tx_urb->transfer_flags |= URB_ZERO_PACKET; if (ep == card->tx_cmd_ep) atomic_inc(&card->tx_cmd_urb_pending); else atomic_inc(&card->tx_data_urb_pending); if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { dev_err(adapter->dev, "%s: usb_submit_urb failed\n", __func__); if (ep == card->tx_cmd_ep) { atomic_dec(&card->tx_cmd_urb_pending); } else { atomic_dec(&card->tx_data_urb_pending); if (card->tx_data_ix) card->tx_data_ix--; else card->tx_data_ix = MWIFIEX_TX_DATA_URB; } return -1; } else { if (ep == card->tx_data_ep && atomic_read(&card->tx_data_urb_pending) == MWIFIEX_TX_DATA_URB) return -ENOSR; } return -EINPROGRESS; } /* This function register usb device and initialize parameter. */ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) { struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; card->adapter = adapter; adapter->dev = &card->udev->dev; strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); return 0; } /* This function reads one block of firmware data. */ static int mwifiex_get_fw_data(struct mwifiex_adapter *adapter, u32 offset, u32 len, u8 *buf) { if (!buf || !len) return -1; if (offset + len > adapter->firmware->size) return -1; memcpy(buf, adapter->firmware->data + offset, len); return 0; } static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { int ret = 0; u8 *firmware = fw->fw_buf, *recv_buff; u32 retries = USB8797_FW_MAX_RETRY, dlen; u32 fw_seqnum = 0, tlen = 0, dnld_cmd = 0; struct fw_data *fwdata; struct fw_sync_header sync_fw; u8 check_winner = 1; if (!firmware) { dev_err(adapter->dev, "No firmware image found! Terminating download\n"); ret = -1; goto fw_exit; } /* Allocate memory for transmit */ fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL); if (!fwdata) goto fw_exit; /* Allocate memory for receive */ recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL); if (!recv_buff) goto cleanup; do { /* Send pseudo data to check winner status first */ if (check_winner) { memset(&fwdata->fw_hdr, 0, sizeof(struct fw_header)); dlen = 0; } else { /* copy the header of the fw_data to get the length */ if (firmware) memcpy(&fwdata->fw_hdr, &firmware[tlen], sizeof(struct fw_header)); else mwifiex_get_fw_data(adapter, tlen, sizeof(struct fw_header), (u8 *)&fwdata->fw_hdr); dlen = le32_to_cpu(fwdata->fw_hdr.data_len); dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd); tlen += sizeof(struct fw_header); if (firmware) memcpy(fwdata->data, &firmware[tlen], dlen); else mwifiex_get_fw_data(adapter, tlen, dlen, (u8 *)fwdata->data); fwdata->seq_num = cpu_to_le32(fw_seqnum); tlen += dlen; } /* If the send/receive fails or CRC occurs then retry */ while (retries--) { u8 *buf = (u8 *)fwdata; u32 len = FW_DATA_XMIT_SIZE; /* send the firmware block */ ret = mwifiex_write_data_sync(adapter, buf, &len, MWIFIEX_USB_EP_CMD_EVENT, MWIFIEX_USB_TIMEOUT); if (ret) { dev_err(adapter->dev, "write_data_sync: failed: %d\n", ret); continue; } buf = recv_buff; len = FW_DNLD_RX_BUF_SIZE; /* Receive the firmware block response */ ret = mwifiex_read_data_sync(adapter, buf, &len, MWIFIEX_USB_EP_CMD_EVENT, MWIFIEX_USB_TIMEOUT); if (ret) { dev_err(adapter->dev, "read_data_sync: failed: %d\n", ret); continue; } memcpy(&sync_fw, recv_buff, sizeof(struct fw_sync_header)); /* check 1st firmware block resp for highest bit set */ if (check_winner) { if (le32_to_cpu(sync_fw.cmd) & 0x80000000) { dev_warn(adapter->dev, "USB is not the winner %#x\n", sync_fw.cmd); /* returning success */ ret = 0; goto cleanup; } dev_dbg(adapter->dev, "USB is the winner, start to download FW\n"); check_winner = 0; break; } /* check the firmware block response for CRC errors */ if (sync_fw.cmd) { dev_err(adapter->dev, "FW received block with CRC %#x\n", sync_fw.cmd); ret = -1; continue; } retries = USB8797_FW_MAX_RETRY; break; } fw_seqnum++; } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); cleanup: dev_dbg(adapter->dev, "%s: %d bytes downloaded\n", __func__, tlen); kfree(recv_buff); kfree(fwdata); if (retries) ret = 0; fw_exit: return ret; } static int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { int ret; struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; if (card->usb_boot_state == USB8797_FW_DNLD) { ret = mwifiex_prog_fw_w_helper(adapter, fw); if (ret) return -1; /* Boot state changes after successful firmware download */ if (card->usb_boot_state == USB8797_FW_DNLD) return -1; } ret = mwifiex_usb_rx_init(adapter); if (!ret) ret = mwifiex_usb_tx_init(adapter); return ret; } static void mwifiex_submit_rx_urb(struct mwifiex_adapter *adapter, u8 ep) { struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; skb_push(card->rx_cmd.skb, INTF_HEADER_LEN); if ((ep == card->rx_cmd_ep) && (!atomic_read(&card->rx_cmd_urb_pending))) mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE); return; } static int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { atomic_dec(&adapter->rx_pending); mwifiex_submit_rx_urb(adapter, MWIFIEX_USB_EP_CMD_EVENT); return 0; } static int mwifiex_usb_data_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { atomic_dec(&adapter->rx_pending); dev_kfree_skb_any(skb); return 0; } /* This function wakes up the card. */ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { /* Simulation of HS_AWAKE event */ adapter->pm_wakeup_fw_try = false; adapter->pm_wakeup_card_req = false; adapter->ps_state = PS_STATE_AWAKE; return 0; } static struct mwifiex_if_ops usb_ops = { .register_dev = mwifiex_register_dev, .wakeup = mwifiex_pm_wakeup_card, .wakeup_complete = mwifiex_pm_wakeup_card_complete, /* USB specific */ .dnld_fw = mwifiex_usb_dnld_fw, .cmdrsp_complete = mwifiex_usb_cmd_event_complete, .event_complete = mwifiex_usb_cmd_event_complete, .data_complete = mwifiex_usb_data_complete, .host_to_card = mwifiex_usb_host_to_card, }; /* This function initializes the USB driver module. * * This initiates the semaphore and registers the device with * USB bus. */ static int mwifiex_usb_init_module(void) { int ret; pr_debug("Marvell USB8797 Driver\n"); sema_init(&add_remove_card_sem, 1); ret = usb_register(&mwifiex_usb_driver); if (ret) pr_err("Driver register failed!\n"); else pr_debug("info: Driver registered successfully!\n"); return ret; } /* This function cleans up the USB driver. * * The following major steps are followed in .disconnect for cleanup: * - Resume the device if its suspended * - Disconnect the device if connected * - Shutdown the firmware * - Unregister the device from USB bus. */ static void mwifiex_usb_cleanup_module(void) { if (!down_interruptible(&add_remove_card_sem)) up(&add_remove_card_sem); /* set the flag as user is removing this module */ user_rmmod = 1; usb_deregister(&mwifiex_usb_driver); } module_init(mwifiex_usb_init_module); module_exit(mwifiex_usb_cleanup_module); MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION); MODULE_VERSION(USB_VERSION); MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE("mrvl/usb8797_uapsta.bin"); compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/uap_txrx.c0000644000175000017500000002306612026211315024156 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: AP TX and RX data handling * * Copyright (C) 2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "main.h" #include "wmm.h" #include "11n_aggr.h" #include "11n_rxreorder.h" static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, struct sk_buff *skb) { struct mwifiex_adapter *adapter = priv->adapter; struct uap_rxpd *uap_rx_pd; struct rx_packet_hdr *rx_pkt_hdr; struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; int hdr_chop; struct timeval tv; u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; uap_rx_pd = (struct uap_rxpd *)(skb->data); rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); if ((atomic_read(&adapter->pending_bridged_pkts) >= MWIFIEX_BRIDGED_PKTS_THRESHOLD)) { dev_err(priv->adapter->dev, "Tx: Bridge packet limit reached. Drop packet!\n"); kfree_skb(skb); return; } if (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) /* Chop off the rxpd + the excess memory from * 802.2/llc/snap header that was removed. */ hdr_chop = (u8 *)eth_hdr - (u8 *)uap_rx_pd; else /* Chop off the rxpd */ hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd; /* Chop off the leading header bytes so the it points * to the start of either the reconstructed EthII frame * or the 802.2/llc/snap frame. */ skb_pull(skb, hdr_chop); if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { dev_dbg(priv->adapter->dev, "data: Tx: insufficient skb headroom %d\n", skb_headroom(skb)); /* Insufficient skb headroom - allocate a new skb */ new_skb = skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); if (unlikely(!new_skb)) { dev_err(priv->adapter->dev, "Tx: cannot allocate new_skb\n"); kfree_skb(skb); priv->stats.tx_dropped++; return; } kfree_skb(skb); skb = new_skb; dev_dbg(priv->adapter->dev, "info: new skb headroom %d\n", skb_headroom(skb)); } tx_info = MWIFIEX_SKB_TXCB(skb); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT; do_gettimeofday(&tv); skb->tstamp = timeval_to_ktime(tv); mwifiex_wmm_add_buf_txqueue(priv, skb); atomic_inc(&adapter->tx_pending); atomic_inc(&adapter->pending_bridged_pkts); if ((atomic_read(&adapter->tx_pending) >= MAX_TX_PENDING)) { mwifiex_set_trans_start(priv->netdev); mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); } return; } /* * This function contains logic for AP packet forwarding. * * If a packet is multicast/broadcast, it is sent to kernel/upper layer * as well as queued back to AP TX queue so that it can be sent to other * associated stations. * If a packet is unicast and RA is present in associated station list, * it is again requeued into AP TX queue. * If a packet is unicast and RA is not in associated station list, * packet is forwarded to kernel to handle routing logic. */ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, struct sk_buff *skb) { struct mwifiex_adapter *adapter = priv->adapter; struct uap_rxpd *uap_rx_pd; struct rx_packet_hdr *rx_pkt_hdr; u8 ra[ETH_ALEN]; struct sk_buff *skb_uap; uap_rx_pd = (struct uap_rxpd *)(skb->data); rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); /* don't do packet forwarding in disconnected state */ if (!priv->media_connected) { dev_err(adapter->dev, "drop packet in disconnected state.\n"); dev_kfree_skb_any(skb); return 0; } memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN); if (is_multicast_ether_addr(ra)) { skb_uap = skb_copy(skb, GFP_ATOMIC); mwifiex_uap_queue_bridged_pkt(priv, skb_uap); } else { if (mwifiex_get_sta_entry(priv, ra)) { /* Requeue Intra-BSS packet */ mwifiex_uap_queue_bridged_pkt(priv, skb); return 0; } } /* Forward unicat/Inter-BSS packets to kernel. */ return mwifiex_process_rx_packet(adapter, skb); } /* * This function processes the packet received on AP interface. * * The function looks into the RxPD and performs sanity tests on the * received buffer to ensure its a valid packet before processing it * further. If the packet is determined to be aggregated, it is * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic. * * The completion callback is called after processing is complete. */ int mwifiex_process_uap_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) { int ret; struct uap_rxpd *uap_rx_pd; struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); struct rx_packet_hdr *rx_pkt_hdr; u16 rx_pkt_type; u8 ta[ETH_ALEN], pkt_type; struct mwifiex_sta_node *node; struct mwifiex_private *priv = mwifiex_get_priv_by_id(adapter, rx_info->bss_num, rx_info->bss_type); if (!priv) return -1; uap_rx_pd = (struct uap_rxpd *)(skb->data); rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type); rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) + le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) { dev_err(adapter->dev, "wrong rx packet: len=%d, offset=%d, length=%d\n", skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset), le16_to_cpu(uap_rx_pd->rx_pkt_length)); priv->stats.rx_dropped++; if (adapter->if_ops.data_complete) adapter->if_ops.data_complete(adapter, skb); else dev_kfree_skb_any(skb); return 0; } if (le16_to_cpu(uap_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { struct sk_buff_head list; struct sk_buff *rx_skb; __skb_queue_head_init(&list); skb_pull(skb, le16_to_cpu(uap_rx_pd->rx_pkt_offset)); skb_trim(skb, le16_to_cpu(uap_rx_pd->rx_pkt_length)); ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, priv->wdev->iftype, 0, false); while (!skb_queue_empty(&list)) { rx_skb = __skb_dequeue(&list); ret = mwifiex_recv_packet(adapter, rx_skb); if (ret) dev_err(adapter->dev, "AP:Rx A-MSDU failed"); } return 0; } memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { node = mwifiex_get_sta_entry(priv, ta); if (node) node->rx_seq[uap_rx_pd->priority] = le16_to_cpu(uap_rx_pd->seq_num); } if (!priv->ap_11n_enabled || (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) && (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) { ret = mwifiex_handle_uap_rx_forward(priv, skb); return ret; } /* Reorder and send to kernel */ pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type); ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num), uap_rx_pd->priority, ta, pkt_type, skb); if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { if (adapter->if_ops.data_complete) adapter->if_ops.data_complete(adapter, skb); else dev_kfree_skb_any(skb); } if (ret) priv->stats.rx_dropped++; return ret; } /* * This function fills the TxPD for AP tx packets. * * The Tx buffer received by this function should already have the * header space allocated for TxPD. * * This function inserts the TxPD in between interface header and actual * data and adjusts the buffer pointers accordingly. * * The following TxPD fields are set by this function, as required - * - BSS number * - Tx packet length and offset * - Priority * - Packet delay * - Priority specific Tx control * - Flags */ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, struct sk_buff *skb) { struct mwifiex_adapter *adapter = priv->adapter; struct uap_txpd *txpd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); int pad, len; if (!skb->len) { dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); tx_info->status_code = -1; return skb->data; } /* If skb->data is not aligned, add padding */ pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; len = sizeof(*txpd) + pad; BUG_ON(skb_headroom(skb) < len + INTF_HEADER_LEN); skb_push(skb, len); txpd = (struct uap_txpd *)skb->data; memset(txpd, 0, sizeof(*txpd)); txpd->bss_num = priv->bss_num; txpd->bss_type = priv->bss_type; txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len)); txpd->priority = (u8)skb->priority; txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) /* * Set the priority specific tx_control field, setting of 0 will * cause the default value to be used later in this function. */ txpd->tx_control = cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); /* Offset of actual data */ txpd->tx_pkt_offset = cpu_to_le16(len); /* make space for INTF_HEADER_LEN */ skb_push(skb, INTF_HEADER_LEN); if (!txpd->tx_control) /* TxCtrl set by user or default */ txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); return skb->data; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/uap_event.c0000644000175000017500000001753112026211315024272 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: AP event handling * * Copyright (C) 2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "main.h" #include "11n.h" /* * This function will return the pointer to station entry in station list * table which matches specified mac address. * This function should be called after acquiring RA list spinlock. * NULL is returned if station entry is not found in associated STA list. */ struct mwifiex_sta_node * mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) { struct mwifiex_sta_node *node; if (!mac) return NULL; list_for_each_entry(node, &priv->sta_list, list) { if (!memcmp(node->mac_addr, mac, ETH_ALEN)) return node; } return NULL; } /* * This function will add a sta_node entry to associated station list * table with the given mac address. * If entry exist already, existing entry is returned. * If received mac address is NULL, NULL is returned. */ static struct mwifiex_sta_node * mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac) { struct mwifiex_sta_node *node; unsigned long flags; if (!mac) return NULL; spin_lock_irqsave(&priv->sta_list_spinlock, flags); node = mwifiex_get_sta_entry(priv, mac); if (node) goto done; node = kzalloc(sizeof(struct mwifiex_sta_node), GFP_ATOMIC); if (!node) goto done; memcpy(node->mac_addr, mac, ETH_ALEN); list_add_tail(&node->list, &priv->sta_list); done: spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); return node; } /* * This function will search for HT IE in association request IEs * and set station HT parameters accordingly. */ static void mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, int ies_len, struct mwifiex_sta_node *node) { const struct ieee80211_ht_cap *ht_cap; if (!ies) return; ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); if (ht_cap) { node->is_11n_enabled = 1; node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & IEEE80211_HT_CAP_MAX_AMSDU ? MWIFIEX_TX_DATA_BUF_SIZE_8K : MWIFIEX_TX_DATA_BUF_SIZE_4K; } else { node->is_11n_enabled = 0; } return; } /* * This function will delete a station entry from station list */ static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) { struct mwifiex_sta_node *node, *tmp; unsigned long flags; spin_lock_irqsave(&priv->sta_list_spinlock, flags); node = mwifiex_get_sta_entry(priv, mac); if (node) { list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { list_del(&node->list); kfree(node); } } spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); return; } /* * This function will delete all stations from associated station list. */ static void mwifiex_del_all_sta_list(struct mwifiex_private *priv) { struct mwifiex_sta_node *node, *tmp; unsigned long flags; spin_lock_irqsave(&priv->sta_list_spinlock, flags); list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { list_del(&node->list); kfree(node); } INIT_LIST_HEAD(&priv->sta_list); spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); return; } /* * This function handles AP interface specific events generated by firmware. * * Event specific routines are called by this function based * upon the generated event cause. * * * Events supported for AP - * - EVENT_UAP_STA_ASSOC * - EVENT_UAP_STA_DEAUTH * - EVENT_UAP_BSS_ACTIVE * - EVENT_UAP_BSS_START * - EVENT_UAP_BSS_IDLE * - EVENT_UAP_MIC_COUNTERMEASURES: */ int mwifiex_process_uap_event(struct mwifiex_private *priv) { struct mwifiex_adapter *adapter = priv->adapter; int len, i; u32 eventcause = adapter->event_cause; struct station_info sinfo; struct mwifiex_assoc_event *event; struct mwifiex_sta_node *node; u8 *deauth_mac; struct host_cmd_ds_11n_batimeout *ba_timeout; u16 ctrl; switch (eventcause) { case EVENT_UAP_STA_ASSOC: memset(&sinfo, 0, sizeof(sinfo)); event = (struct mwifiex_assoc_event *) (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER); if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) { len = -1; if (ieee80211_is_assoc_req(event->frame_control)) len = 0; else if (ieee80211_is_reassoc_req(event->frame_control)) /* There will be ETH_ALEN bytes of * current_ap_addr before the re-assoc ies. */ len = ETH_ALEN; if (len != -1) { sinfo.filled = STATION_INFO_ASSOC_REQ_IES; sinfo.assoc_req_ies = &event->data[len]; len = (u8 *)sinfo.assoc_req_ies - (u8 *)&event->frame_control; sinfo.assoc_req_ies_len = le16_to_cpu(event->len) - (u16)len; } } cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo, GFP_KERNEL); node = mwifiex_add_sta_entry(priv, event->sta_addr); if (!node) { dev_warn(adapter->dev, "could not create station entry!\n"); return -1; } if (!priv->ap_11n_enabled) break; mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies, sinfo.assoc_req_ies_len, node); for (i = 0; i < MAX_NUM_TID; i++) { if (node->is_11n_enabled) node->ampdu_sta[i] = priv->aggr_prio_tbl[i].ampdu_user; else node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; } memset(node->rx_seq, 0xff, sizeof(node->rx_seq)); break; case EVENT_UAP_STA_DEAUTH: deauth_mac = adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER; cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL); if (priv->ap_11n_enabled) { mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac); mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac); } mwifiex_del_sta_entry(priv, deauth_mac); break; case EVENT_UAP_BSS_IDLE: priv->media_connected = false; mwifiex_clean_txrx(priv); mwifiex_del_all_sta_list(priv); break; case EVENT_UAP_BSS_ACTIVE: priv->media_connected = true; break; case EVENT_UAP_BSS_START: dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause); memcpy(priv->netdev->dev_addr, adapter->event_body + 2, ETH_ALEN); break; case EVENT_UAP_MIC_COUNTERMEASURES: /* For future development */ dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause); break; case EVENT_AMSDU_AGGR_CTRL: ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", ctrl); if (priv->media_connected) { adapter->tx_buf_size = min_t(u16, adapter->curr_tx_buf_size, ctrl); dev_dbg(adapter->dev, "event: tx_buf_size %d\n", adapter->tx_buf_size); } break; case EVENT_ADDBA: dev_dbg(adapter->dev, "event: ADDBA Request\n"); if (priv->media_connected) mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP, HostCmd_ACT_GEN_SET, 0, adapter->event_body); break; case EVENT_DELBA: dev_dbg(adapter->dev, "event: DELBA Request\n"); if (priv->media_connected) mwifiex_11n_delete_ba_stream(priv, adapter->event_body); break; case EVENT_BA_STREAM_TIEMOUT: dev_dbg(adapter->dev, "event: BA Stream timeout\n"); if (priv->media_connected) { ba_timeout = (void *)adapter->event_body; mwifiex_11n_ba_stream_timeout(priv, ba_timeout); } break; default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); break; } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/uap_cmd.c0000644000175000017500000004713512026211315023717 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: AP specific command handling * * Copyright (C) 2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "main.h" /* This function parses security related parameters from cfg80211_ap_settings * and sets into FW understandable bss_config structure. */ int mwifiex_set_secure_params(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_config, struct cfg80211_ap_settings *params) { int i; struct mwifiex_wep_key wep_key; if (!params->privacy) { bss_config->protocol = PROTOCOL_NO_SECURITY; bss_config->key_mgmt = KEY_MGMT_NONE; bss_config->wpa_cfg.length = 0; priv->sec_info.wep_enabled = 0; priv->sec_info.wpa_enabled = 0; priv->sec_info.wpa2_enabled = 0; return 0; } switch (params->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: bss_config->auth_mode = WLAN_AUTH_OPEN; break; case NL80211_AUTHTYPE_SHARED_KEY: bss_config->auth_mode = WLAN_AUTH_SHARED_KEY; break; case NL80211_AUTHTYPE_NETWORK_EAP: bss_config->auth_mode = WLAN_AUTH_LEAP; break; default: bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO; break; } bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST; for (i = 0; i < params->crypto.n_akm_suites; i++) { switch (params->crypto.akm_suites[i]) { case WLAN_AKM_SUITE_8021X: if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) { bss_config->protocol = PROTOCOL_WPA; bss_config->key_mgmt = KEY_MGMT_EAP; } if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) { bss_config->protocol |= PROTOCOL_WPA2; bss_config->key_mgmt = KEY_MGMT_EAP; } break; case WLAN_AKM_SUITE_PSK: if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) { bss_config->protocol = PROTOCOL_WPA; bss_config->key_mgmt = KEY_MGMT_PSK; } if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) { bss_config->protocol |= PROTOCOL_WPA2; bss_config->key_mgmt = KEY_MGMT_PSK; } break; default: break; } } for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) { switch (params->crypto.ciphers_pairwise[i]) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: break; case WLAN_CIPHER_SUITE_TKIP: if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) bss_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_TKIP; if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) bss_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) bss_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_AES_CCMP; if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) bss_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_AES_CCMP; default: break; } } switch (params->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: if (priv->sec_info.wep_enabled) { bss_config->protocol = PROTOCOL_STATIC_WEP; bss_config->key_mgmt = KEY_MGMT_NONE; bss_config->wpa_cfg.length = 0; for (i = 0; i < NUM_WEP_KEYS; i++) { wep_key = priv->wep_key[i]; bss_config->wep_cfg[i].key_index = i; if (priv->wep_key_curr_index == i) bss_config->wep_cfg[i].is_default = 1; else bss_config->wep_cfg[i].is_default = 0; bss_config->wep_cfg[i].length = wep_key.key_length; memcpy(&bss_config->wep_cfg[i].key, &wep_key.key_material, wep_key.key_length); } } break; case WLAN_CIPHER_SUITE_TKIP: bss_config->wpa_cfg.group_cipher = CIPHER_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; break; default: break; } return 0; } /* This function updates 11n related parameters from IE and sets them into * bss_config structure. */ void mwifiex_set_ht_params(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_cfg, struct cfg80211_ap_settings *params) { const u8 *ht_ie; if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) return; ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail, params->beacon.tail_len); if (ht_ie) { memcpy(&bss_cfg->ht_cap, ht_ie + 2, sizeof(struct ieee80211_ht_cap)); priv->ap_11n_enabled = 1; } else { memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP); bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU; } return; } /* This function finds supported rates IE from beacon parameter and sets * these rates into bss_config structure. */ void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, struct cfg80211_ap_settings *params) { struct ieee_types_header *rate_ie; int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); const u8 *var_pos = params->beacon.head + var_offset; int len = params->beacon.head_len - var_offset; rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); if (rate_ie) memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); return; } /* This function initializes some of mwifiex_uap_bss_param variables. * This helps FW in ignoring invalid values. These values may or may not * be get updated to valid ones at later stage. */ void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config) { config->bcast_ssid_ctl = 0x7F; config->radio_ctl = 0x7F; config->dtim_period = 0x7F; config->beacon_period = 0x7FFF; config->auth_mode = 0x7F; config->rts_threshold = 0x7FFF; config->frag_threshold = 0x7FFF; config->retry_limit = 0x7F; } /* This function parses BSS related parameters from structure * and prepares TLVs specific to WPA/WPA2 security. * These TLVs are appended to command buffer. */ static void mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) { struct host_cmd_tlv_pwk_cipher *pwk_cipher; struct host_cmd_tlv_gwk_cipher *gwk_cipher; struct host_cmd_tlv_passphrase *passphrase; struct host_cmd_tlv_akmp *tlv_akmp; struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; u16 cmd_size = *param_size; u8 *tlv = *tlv_buf; tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; tlv_akmp->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); tlv_akmp->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - sizeof(struct host_cmd_tlv)); tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); cmd_size += sizeof(struct host_cmd_tlv_akmp); tlv += sizeof(struct host_cmd_tlv_akmp); if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); pwk_cipher->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - sizeof(struct host_cmd_tlv)); pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); tlv += sizeof(struct host_cmd_tlv_pwk_cipher); } if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); pwk_cipher->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - sizeof(struct host_cmd_tlv)); pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); tlv += sizeof(struct host_cmd_tlv_pwk_cipher); } if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; gwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); gwk_cipher->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - sizeof(struct host_cmd_tlv)); gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); tlv += sizeof(struct host_cmd_tlv_gwk_cipher); } if (bss_cfg->wpa_cfg.length) { passphrase = (struct host_cmd_tlv_passphrase *)tlv; passphrase->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); passphrase->tlv.len = cpu_to_le16(bss_cfg->wpa_cfg.length); memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, bss_cfg->wpa_cfg.length); cmd_size += sizeof(struct host_cmd_tlv) + bss_cfg->wpa_cfg.length; tlv += sizeof(struct host_cmd_tlv) + bss_cfg->wpa_cfg.length; } *param_size = cmd_size; *tlv_buf = tlv; return; } /* This function parses BSS related parameters from structure * and prepares TLVs specific to WEP encryption. * These TLVs are appended to command buffer. */ static void mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) { struct host_cmd_tlv_wep_key *wep_key; u16 cmd_size = *param_size; int i; u8 *tlv = *tlv_buf; struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; for (i = 0; i < NUM_WEP_KEYS; i++) { if (bss_cfg->wep_cfg[i].length && (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { wep_key = (struct host_cmd_tlv_wep_key *)tlv; wep_key->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); wep_key->tlv.len = cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); wep_key->key_index = bss_cfg->wep_cfg[i].key_index; wep_key->is_default = bss_cfg->wep_cfg[i].is_default; memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, bss_cfg->wep_cfg[i].length); cmd_size += sizeof(struct host_cmd_tlv) + 2 + bss_cfg->wep_cfg[i].length; tlv += sizeof(struct host_cmd_tlv) + 2 + bss_cfg->wep_cfg[i].length; } } *param_size = cmd_size; *tlv_buf = tlv; return; } /* This function parses BSS related parameters from structure * and prepares TLVs. These TLVs are appended to command buffer. */ static int mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) { struct host_cmd_tlv_dtim_period *dtim_period; struct host_cmd_tlv_beacon_period *beacon_period; struct host_cmd_tlv_ssid *ssid; struct host_cmd_tlv_bcast_ssid *bcast_ssid; struct host_cmd_tlv_channel_band *chan_band; struct host_cmd_tlv_frag_threshold *frag_threshold; struct host_cmd_tlv_rts_threshold *rts_threshold; struct host_cmd_tlv_retry_limit *retry_limit; struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; struct host_cmd_tlv_auth_type *auth_type; struct host_cmd_tlv_rates *tlv_rates; struct mwifiex_ie_types_htcap *htcap; struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; int i; u16 cmd_size = *param_size; if (bss_cfg->ssid.ssid_len) { ssid = (struct host_cmd_tlv_ssid *)tlv; ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_SSID); ssid->tlv.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); cmd_size += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len; tlv += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len; bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; bcast_ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); bcast_ssid->tlv.len = cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); tlv += sizeof(struct host_cmd_tlv_bcast_ssid); } if (bss_cfg->rates[0]) { tlv_rates = (struct host_cmd_tlv_rates *)tlv; tlv_rates->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RATES); for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i]; i++) tlv_rates->rates[i] = bss_cfg->rates[i]; tlv_rates->tlv.len = cpu_to_le16(i); cmd_size += sizeof(struct host_cmd_tlv_rates) + i; tlv += sizeof(struct host_cmd_tlv_rates) + i; } if (bss_cfg->channel && ((bss_cfg->band_cfg == BAND_CONFIG_BG && bss_cfg->channel <= MAX_CHANNEL_BAND_BG) || (bss_cfg->band_cfg == BAND_CONFIG_A && bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { chan_band = (struct host_cmd_tlv_channel_band *)tlv; chan_band->tlv.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); chan_band->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - sizeof(struct host_cmd_tlv)); chan_band->band_config = bss_cfg->band_cfg; chan_band->channel = bss_cfg->channel; cmd_size += sizeof(struct host_cmd_tlv_channel_band); tlv += sizeof(struct host_cmd_tlv_channel_band); } if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD && bss_cfg->beacon_period <= MAX_BEACON_PERIOD) { beacon_period = (struct host_cmd_tlv_beacon_period *)tlv; beacon_period->tlv.type = cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); beacon_period->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - sizeof(struct host_cmd_tlv)); beacon_period->period = cpu_to_le16(bss_cfg->beacon_period); cmd_size += sizeof(struct host_cmd_tlv_beacon_period); tlv += sizeof(struct host_cmd_tlv_beacon_period); } if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD && bss_cfg->dtim_period <= MAX_DTIM_PERIOD) { dtim_period = (struct host_cmd_tlv_dtim_period *)tlv; dtim_period->tlv.type = cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); dtim_period->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - sizeof(struct host_cmd_tlv)); dtim_period->period = bss_cfg->dtim_period; cmd_size += sizeof(struct host_cmd_tlv_dtim_period); tlv += sizeof(struct host_cmd_tlv_dtim_period); } if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) { rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv; rts_threshold->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); rts_threshold->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - sizeof(struct host_cmd_tlv)); rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold); cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); tlv += sizeof(struct host_cmd_tlv_frag_threshold); } if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) && (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) { frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv; frag_threshold->tlv.type = cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); frag_threshold->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - sizeof(struct host_cmd_tlv)); frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold); cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); tlv += sizeof(struct host_cmd_tlv_frag_threshold); } if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) { retry_limit = (struct host_cmd_tlv_retry_limit *)tlv; retry_limit->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); retry_limit->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - sizeof(struct host_cmd_tlv)); retry_limit->limit = (u8)bss_cfg->retry_limit; cmd_size += sizeof(struct host_cmd_tlv_retry_limit); tlv += sizeof(struct host_cmd_tlv_retry_limit); } if ((bss_cfg->protocol & PROTOCOL_WPA) || (bss_cfg->protocol & PROTOCOL_WPA2) || (bss_cfg->protocol & PROTOCOL_EAP)) mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); else mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size); if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { auth_type = (struct host_cmd_tlv_auth_type *)tlv; auth_type->tlv.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); auth_type->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - sizeof(struct host_cmd_tlv)); auth_type->auth_type = (u8)bss_cfg->auth_mode; cmd_size += sizeof(struct host_cmd_tlv_auth_type); tlv += sizeof(struct host_cmd_tlv_auth_type); } if (bss_cfg->protocol) { encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv; encrypt_protocol->tlv.type = cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); encrypt_protocol->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) - sizeof(struct host_cmd_tlv)); encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol); cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol); tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); } if (bss_cfg->ht_cap.cap_info) { htcap = (struct mwifiex_ie_types_htcap *)tlv; htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); htcap->header.len = cpu_to_le16(sizeof(struct ieee80211_ht_cap)); htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info; htcap->ht_cap.ampdu_params_info = bss_cfg->ht_cap.ampdu_params_info; memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs, sizeof(struct ieee80211_mcs_info)); htcap->ht_cap.extended_ht_cap_info = bss_cfg->ht_cap.extended_ht_cap_info; htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info; htcap->ht_cap.antenna_selection_info = bss_cfg->ht_cap.antenna_selection_info; cmd_size += sizeof(struct mwifiex_ie_types_htcap); tlv += sizeof(struct mwifiex_ie_types_htcap); } *param_size = cmd_size; return 0; } /* This function parses custom IEs from IE list and prepares command buffer */ static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size) { struct mwifiex_ie_list *ap_ie = cmd_buf; struct host_cmd_tlv *tlv_ie = (struct host_cmd_tlv *)tlv; if (!ap_ie || !ap_ie->len || !ap_ie->ie_list) return -1; *ie_size += le16_to_cpu(ap_ie->len) + sizeof(struct host_cmd_tlv); tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); tlv_ie->len = ap_ie->len; tlv += sizeof(struct host_cmd_tlv); memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); return 0; } /* Parse AP config structure and prepare TLV based command structure * to be sent to FW for uAP configuration */ static int mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action, u32 type, void *cmd_buf) { u8 *tlv; u16 cmd_size, param_size, ie_size; struct host_cmd_ds_sys_config *sys_cfg; cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG); cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN); sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config; sys_cfg->action = cpu_to_le16(cmd_action); tlv = sys_cfg->tlv; switch (type) { case UAP_BSS_PARAMS_I: param_size = cmd_size; if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, ¶m_size)) return -1; cmd->size = cpu_to_le16(param_size); break; case UAP_CUSTOM_IE_I: ie_size = cmd_size; if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size)) return -1; cmd->size = cpu_to_le16(ie_size); break; default: return -1; } return 0; } /* This function prepares the AP specific commands before sending them * to the firmware. * This is a generic function which calls specific command preparation * routines based upon the command number. */ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, u16 cmd_action, u32 type, void *data_buf, void *cmd_buf) { struct host_cmd_ds_command *cmd = cmd_buf; switch (cmd_no) { case HostCmd_CMD_UAP_SYS_CONFIG: if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf)) return -1; break; case HostCmd_CMD_UAP_BSS_START: case HostCmd_CMD_UAP_BSS_STOP: cmd->command = cpu_to_le16(cmd_no); cmd->size = cpu_to_le16(S_DS_GEN); break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd %#x\n", cmd_no); return -1; } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/txrx.c0000644000175000017500000001242412026211315023305 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: generic TX/RX data handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" /* * This function processes the received buffer. * * Main responsibility of this function is to parse the RxPD to * identify the correct interface this packet is headed for and * forwarding it to the associated handling function, where the * packet will be further processed and sent to kernel/upper layer * if required. */ int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); struct rxpd *local_rx_pd; struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); local_rx_pd = (struct rxpd *) (skb->data); /* Get the BSS number from rxpd, get corresponding priv */ priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num & BSS_NUM_MASK, local_rx_pd->bss_type); if (!priv) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); rx_info->bss_num = priv->bss_num; rx_info->bss_type = priv->bss_type; if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) return mwifiex_process_uap_rx_packet(adapter, skb); return mwifiex_process_sta_rx_packet(adapter, skb); } EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); /* * This function sends a packet to device. * * It processes the packet to add the TxPD, checks condition and * sends the processed packet to firmware for transmission. * * On successful completion, the function calls the completion callback * and logs the time. */ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { int ret = -1; struct mwifiex_adapter *adapter = priv->adapter; u8 *head_ptr; struct txpd *local_tx_pd = NULL; if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) head_ptr = mwifiex_process_uap_txpd(priv, skb); else head_ptr = mwifiex_process_sta_txpd(priv, skb); if (head_ptr) { if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) local_tx_pd = (struct txpd *) (head_ptr + INTF_HEADER_LEN); if (adapter->iface_type == MWIFIEX_USB) { adapter->data_sent = true; skb_pull(skb, INTF_HEADER_LEN); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); } else { ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, skb, tx_param); } } switch (ret) { case -ENOSR: dev_err(adapter->dev, "data: -ENOSR is returned\n"); break; case -EBUSY: if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && (adapter->pps_uapsd_mode) && (adapter->tx_lock_flag)) { priv->adapter->tx_lock_flag = false; if (local_tx_pd) local_tx_pd->flags = 0; } dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); break; case -1: adapter->data_sent = false; dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n", ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb, ret); break; case -EINPROGRESS: adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb, ret); break; default: break; } return ret; } /* * Packet send completion callback handler. * * It either frees the buffer directly or forwards it to another * completion callback which checks conditions, updates statistics, * wakes up stalled traffic queue if required, and then frees the buffer. */ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb, int status) { struct mwifiex_private *priv, *tpriv; struct mwifiex_txinfo *tx_info; int i; if (!skb) return 0; tx_info = MWIFIEX_SKB_TXCB(skb); priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, tx_info->bss_type); if (!priv) goto done; if (adapter->iface_type == MWIFIEX_USB) adapter->data_sent = false; mwifiex_set_trans_start(priv->netdev); if (!status) { priv->stats.tx_packets++; priv->stats.tx_bytes += skb->len; } else { priv->stats.tx_errors++; } if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) atomic_dec_return(&adapter->pending_bridged_pkts); if (atomic_dec_return(&adapter->tx_pending) >= LOW_TX_PENDING) goto done; for (i = 0; i < adapter->priv_num; i++) { tpriv = adapter->priv[i]; if (tpriv->media_connected && netif_queue_stopped(tpriv->netdev)) mwifiex_wake_up_net_dev_queue(tpriv->netdev, adapter); } done: dev_kfree_skb_any(skb); return 0; } EXPORT_SYMBOL_GPL(mwifiex_write_data_complete); compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sta_tx.c0000644000175000017500000001343212026211315023602 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: station TX data handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" /* * This function fills the TxPD for tx packets. * * The Tx buffer received by this function should already have the * header space allocated for TxPD. * * This function inserts the TxPD in between interface header and actual * data and adjusts the buffer pointers accordingly. * * The following TxPD fields are set by this function, as required - * - BSS number * - Tx packet length and offset * - Priority * - Packet delay * - Priority specific Tx control * - Flags */ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, struct sk_buff *skb) { struct mwifiex_adapter *adapter = priv->adapter; struct txpd *local_tx_pd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); u8 pad; if (!skb->len) { dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); tx_info->status_code = -1; return skb->data; } /* If skb->data is not aligned; add padding */ pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN + pad)); skb_push(skb, sizeof(*local_tx_pd) + pad); local_tx_pd = (struct txpd *) skb->data; memset(local_tx_pd, 0, sizeof(struct txpd)); local_tx_pd->bss_num = priv->bss_num; local_tx_pd->bss_type = priv->bss_type; local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(struct txpd) + pad))); local_tx_pd->priority = (u8) skb->priority; local_tx_pd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); if (local_tx_pd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) /* * Set the priority specific tx_control field, setting of 0 will * cause the default value to be used later in this function */ local_tx_pd->tx_control = cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd-> priority]); if (adapter->pps_uapsd_mode) { if (mwifiex_check_last_packet_indication(priv)) { adapter->tx_lock_flag = true; local_tx_pd->flags = MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; } } /* Offset of actual data */ local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + pad); /* make space for INTF_HEADER_LEN */ skb_push(skb, INTF_HEADER_LEN); if (!local_tx_pd->tx_control) /* TxCtrl set by user or default */ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); return skb->data; } /* * This function tells firmware to send a NULL data packet. * * The function creates a NULL data packet with TxPD and sends to the * firmware for transmission, with highest priority setting. */ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) { struct mwifiex_adapter *adapter = priv->adapter; struct txpd *local_tx_pd; /* sizeof(struct txpd) + Interface specific header */ #define NULL_PACKET_HDR 64 u32 data_len = NULL_PACKET_HDR; struct sk_buff *skb; int ret; struct mwifiex_txinfo *tx_info = NULL; if (adapter->surprise_removed) return -1; if (!priv->media_connected) return -1; if (adapter->data_sent) return -1; skb = dev_alloc_skb(data_len); if (!skb) return -1; tx_info = MWIFIEX_SKB_TXCB(skb); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN); skb_push(skb, sizeof(struct txpd)); local_tx_pd = (struct txpd *) skb->data; local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); local_tx_pd->flags = flags; local_tx_pd->priority = WMM_HIGHEST_PRIORITY; local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); local_tx_pd->bss_num = priv->bss_num; local_tx_pd->bss_type = priv->bss_type; if (adapter->iface_type == MWIFIEX_USB) { ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb, NULL); } else { skb_push(skb, INTF_HEADER_LEN); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, skb, NULL); } switch (ret) { case -EBUSY: adapter->data_sent = true; /* Fall through FAILURE handling */ case -1: dev_kfree_skb_any(skb); dev_err(adapter->dev, "%s: host_to_card failed: ret=%d\n", __func__, ret); adapter->dbg.num_tx_host_to_card_failure++; break; case 0: dev_kfree_skb_any(skb); dev_dbg(adapter->dev, "data: %s: host_to_card succeeded\n", __func__); adapter->tx_lock_flag = true; break; case -EINPROGRESS: break; default: break; } return ret; } /* * This function checks if we need to send last packet indication. */ u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv) { struct mwifiex_adapter *adapter = priv->adapter; u8 ret = false; if (!adapter->sleep_period.period) return ret; if (mwifiex_wmm_lists_empty(adapter)) ret = true; if (ret && !adapter->cmd_sent && !adapter->curr_cmd && !is_command_pending(adapter)) { adapter->delay_null_pkt = false; ret = true; } else { ret = false; adapter->delay_null_pkt = true; } return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sta_rx.c0000644000175000017500000001512012026211315023574 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: station RX data handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "11n_aggr.h" #include "11n_rxreorder.h" /* * This function processes the received packet and forwards it * to kernel/upper layer. * * This function parses through the received packet and determines * if it is a debug packet or normal packet. * * For non-debug packets, the function chops off unnecessary leading * header bytes, reconstructs the packet as an ethernet frame or * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. * * The completion callback is called after processing in complete. */ int mwifiex_process_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) { int ret; struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); struct mwifiex_private *priv = mwifiex_get_priv_by_id(adapter, rx_info->bss_num, rx_info->bss_type); struct rx_packet_hdr *rx_pkt_hdr; struct rxpd *local_rx_pd; int hdr_chop; struct ethhdr *eth_hdr; u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; local_rx_pd = (struct rxpd *) (skb->data); rx_pkt_hdr = (void *)local_rx_pd + le16_to_cpu(local_rx_pd->rx_pkt_offset); if (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) { /* * Replace the 803 header and rfc1042 header (llc/snap) with an * EthernetII header, keep the src/dst and snap_type * (ethertype). * The firmware only passes up SNAP frames converting * all RX Data from 802.11 to 802.2/LLC/SNAP frames. * To create the Ethernet II, just move the src, dst address * right before the snap_type. */ eth_hdr = (struct ethhdr *) ((u8 *) &rx_pkt_hdr->eth803_hdr + sizeof(rx_pkt_hdr->eth803_hdr) + sizeof(rx_pkt_hdr->rfc1042_hdr) - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) - sizeof(rx_pkt_hdr->eth803_hdr.h_source) - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); memcpy(eth_hdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, sizeof(eth_hdr->h_source)); memcpy(eth_hdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, sizeof(eth_hdr->h_dest)); /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header that was removed. */ hdr_chop = (u8 *) eth_hdr - (u8 *) local_rx_pd; } else { /* Chop off the rxpd */ hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - (u8 *) local_rx_pd; } /* Chop off the leading header bytes so the it points to the start of either the reconstructed EthII frame or the 802.2/llc/snap frame */ skb_pull(skb, hdr_chop); priv->rxpd_rate = local_rx_pd->rx_rate; priv->rxpd_htinfo = local_rx_pd->ht_info; ret = mwifiex_recv_packet(adapter, skb); if (ret == -1) dev_err(adapter->dev, "recv packet failed\n"); return ret; } /* * This function processes the received buffer. * * The function looks into the RxPD and performs sanity tests on the * received buffer to ensure its a valid packet, before processing it * further. If the packet is determined to be aggregated, it is * de-aggregated accordingly. Non-unicast packets are sent directly to * the kernel/upper layers. Unicast packets are handed over to the * Rx reordering routine if 11n is enabled. * * The completion callback is called after processing in complete. */ int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) { int ret = 0; struct rxpd *local_rx_pd; struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); struct rx_packet_hdr *rx_pkt_hdr; u8 ta[ETH_ALEN]; u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; struct mwifiex_private *priv = mwifiex_get_priv_by_id(adapter, rx_info->bss_num, rx_info->bss_type); if (!priv) return -1; local_rx_pd = (struct rxpd *) (skb->data); rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset); rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length); seq_num = le16_to_cpu(local_rx_pd->seq_num); rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset; if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) { dev_err(adapter->dev, "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", skb->len, rx_pkt_offset, rx_pkt_length); priv->stats.rx_dropped++; if (adapter->if_ops.data_complete) adapter->if_ops.data_complete(adapter, skb); else dev_kfree_skb_any(skb); return ret; } if (rx_pkt_type == PKT_TYPE_AMSDU) { struct sk_buff_head list; struct sk_buff *rx_skb; __skb_queue_head_init(&list); skb_pull(skb, rx_pkt_offset); skb_trim(skb, rx_pkt_length); ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, priv->wdev->iftype, 0, false); while (!skb_queue_empty(&list)) { rx_skb = __skb_dequeue(&list); ret = mwifiex_recv_packet(adapter, rx_skb); if (ret == -1) dev_err(adapter->dev, "Rx of A-MSDU failed"); } return 0; } /* * If the packet is not an unicast packet then send the packet * directly to os. Don't pass thru rx reordering */ if (!IS_11N_ENABLED(priv) || memcmp(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN)) { mwifiex_process_rx_packet(adapter, skb); return ret; } if (mwifiex_queuing_ra_based(priv)) { memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); } else { if (rx_pkt_type != PKT_TYPE_BAR) priv->rx_seq[local_rx_pd->priority] = seq_num; memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); } /* Reorder and send to OS */ ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, ta, (u8) rx_pkt_type, skb); if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { if (adapter->if_ops.data_complete) adapter->if_ops.data_complete(adapter, skb); else dev_kfree_skb_any(skb); } if (ret) priv->stats.rx_dropped++; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sta_event.c0000644000175000017500000003020612026211315024266 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: station event handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* * This function resets the connection state. * * The function is invoked after receiving a disconnect event from firmware, * and performs the following actions - * - Set media status to disconnected * - Clean up Tx and Rx packets * - Resets SNR/NF/RSSI value in driver * - Resets security configurations in driver * - Enables auto data rate * - Saves the previous SSID and BSSID so that they can * be used for re-association, if required * - Erases current SSID and BSSID information * - Sends a disconnect event to upper layers/applications. */ void mwifiex_reset_connect_state(struct mwifiex_private *priv) { struct mwifiex_adapter *adapter = priv->adapter; if (!priv->media_connected) return; dev_dbg(adapter->dev, "info: handles disconnect event\n"); priv->media_connected = false; priv->scan_block = false; /* Free Tx and Rx packets, report disconnect to upper layer */ mwifiex_clean_txrx(priv); /* Reset SNR/NF/RSSI values */ priv->data_rssi_last = 0; priv->data_nf_last = 0; priv->data_rssi_avg = 0; priv->data_nf_avg = 0; priv->bcn_rssi_last = 0; priv->bcn_nf_last = 0; priv->bcn_rssi_avg = 0; priv->bcn_nf_avg = 0; priv->rxpd_rate = 0; priv->rxpd_htinfo = 0; priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; priv->wpa_ie_len = 0; priv->sec_info.wapi_enabled = false; priv->wapi_ie_len = 0; priv->sec_info.wapi_key_on = false; priv->sec_info.encryption_mode = 0; /* Enable auto data rate */ priv->is_data_rate_auto = true; priv->data_rate = 0; if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { priv->adhoc_state = ADHOC_IDLE; priv->adhoc_is_link_sensed = false; } /* * Memorize the previous SSID and BSSID so * it could be used for re-assoc */ dev_dbg(adapter->dev, "info: previous SSID=%s, SSID len=%u\n", priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); dev_dbg(adapter->dev, "info: current SSID=%s, SSID len=%u\n", priv->curr_bss_params.bss_descriptor.ssid.ssid, priv->curr_bss_params.bss_descriptor.ssid.ssid_len); memcpy(&priv->prev_ssid, &priv->curr_bss_params.bss_descriptor.ssid, sizeof(struct cfg80211_ssid)); memcpy(priv->prev_bssid, priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); /* Need to erase the current SSID and BSSID info */ memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); adapter->tx_lock_flag = false; adapter->pps_uapsd_mode = false; if (adapter->num_cmd_timeout && adapter->curr_cmd) return; priv->media_connected = false; dev_dbg(adapter->dev, "info: successfully disconnected from %pM: reason code %d\n", priv->cfg_bssid, WLAN_REASON_DEAUTH_LEAVING); if (priv->bss_mode == NL80211_IFTYPE_STATION) { cfg80211_disconnected(priv->netdev, WLAN_REASON_DEAUTH_LEAVING, NULL, 0, GFP_KERNEL); } memset(priv->cfg_bssid, 0, ETH_ALEN); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); } /* * This function handles events generated by firmware. * * This is a generic function and handles all events. * * Event specific routines are called by this function based * upon the generated event cause. * * For the following events, the function just forwards them to upper * layers, optionally recording the change - * - EVENT_LINK_SENSED * - EVENT_MIC_ERR_UNICAST * - EVENT_MIC_ERR_MULTICAST * - EVENT_PORT_RELEASE * - EVENT_RSSI_LOW * - EVENT_SNR_LOW * - EVENT_MAX_FAIL * - EVENT_RSSI_HIGH * - EVENT_SNR_HIGH * - EVENT_DATA_RSSI_LOW * - EVENT_DATA_SNR_LOW * - EVENT_DATA_RSSI_HIGH * - EVENT_DATA_SNR_HIGH * - EVENT_LINK_QUALITY * - EVENT_PRE_BEACON_LOST * - EVENT_IBSS_COALESCED * - EVENT_WEP_ICV_ERR * - EVENT_BW_CHANGE * - EVENT_HOSTWAKE_STAIE * * For the following events, no action is taken - * - EVENT_MIB_CHANGED * - EVENT_INIT_DONE * - EVENT_DUMMY_HOST_WAKEUP_SIGNAL * * Rest of the supported events requires driver handling - * - EVENT_DEAUTHENTICATED * - EVENT_DISASSOCIATED * - EVENT_LINK_LOST * - EVENT_PS_SLEEP * - EVENT_PS_AWAKE * - EVENT_DEEP_SLEEP_AWAKE * - EVENT_HS_ACT_REQ * - EVENT_ADHOC_BCN_LOST * - EVENT_BG_SCAN_REPORT * - EVENT_WMM_STATUS_CHANGE * - EVENT_ADDBA * - EVENT_DELBA * - EVENT_BA_STREAM_TIEMOUT * - EVENT_AMSDU_AGGR_CTRL */ int mwifiex_process_sta_event(struct mwifiex_private *priv) { struct mwifiex_adapter *adapter = priv->adapter; int ret = 0; u32 eventcause = adapter->event_cause; u16 ctrl; switch (eventcause) { case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: dev_err(adapter->dev, "invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignore it\n"); break; case EVENT_LINK_SENSED: dev_dbg(adapter->dev, "event: LINK_SENSED\n"); if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); if (netif_queue_stopped(priv->netdev)) mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); break; case EVENT_DEAUTHENTICATED: dev_dbg(adapter->dev, "event: Deauthenticated\n"); adapter->dbg.num_event_deauth++; if (priv->media_connected) mwifiex_reset_connect_state(priv); break; case EVENT_DISASSOCIATED: dev_dbg(adapter->dev, "event: Disassociated\n"); adapter->dbg.num_event_disassoc++; if (priv->media_connected) mwifiex_reset_connect_state(priv); break; case EVENT_LINK_LOST: dev_dbg(adapter->dev, "event: Link lost\n"); adapter->dbg.num_event_link_lost++; if (priv->media_connected) mwifiex_reset_connect_state(priv); break; case EVENT_PS_SLEEP: dev_dbg(adapter->dev, "info: EVENT: SLEEP\n"); adapter->ps_state = PS_STATE_PRE_SLEEP; mwifiex_check_ps_cond(adapter); break; case EVENT_PS_AWAKE: dev_dbg(adapter->dev, "info: EVENT: AWAKE\n"); if (!adapter->pps_uapsd_mode && priv->media_connected && adapter->sleep_period.period) { adapter->pps_uapsd_mode = true; dev_dbg(adapter->dev, "event: PPS/UAPSD mode activated\n"); } adapter->tx_lock_flag = false; if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { if (mwifiex_check_last_packet_indication(priv)) { if (adapter->data_sent) { adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; break; } if (!mwifiex_send_null_packet (priv, MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) adapter->ps_state = PS_STATE_SLEEP; return 0; } } adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; break; case EVENT_DEEP_SLEEP_AWAKE: adapter->if_ops.wakeup_complete(adapter); dev_dbg(adapter->dev, "event: DS_AWAKE\n"); if (adapter->is_deep_sleep) adapter->is_deep_sleep = false; break; case EVENT_HS_ACT_REQ: dev_dbg(adapter->dev, "event: HS_ACT_REQ\n"); ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_HS_CFG_ENH, 0, 0, NULL); break; case EVENT_MIC_ERR_UNICAST: dev_dbg(adapter->dev, "event: UNICAST MIC ERROR\n"); cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, NL80211_KEYTYPE_PAIRWISE, -1, NULL, GFP_KERNEL); break; case EVENT_MIC_ERR_MULTICAST: dev_dbg(adapter->dev, "event: MULTICAST MIC ERROR\n"); cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, NL80211_KEYTYPE_GROUP, -1, NULL, GFP_KERNEL); break; case EVENT_MIB_CHANGED: case EVENT_INIT_DONE: break; case EVENT_ADHOC_BCN_LOST: dev_dbg(adapter->dev, "event: ADHOC_BCN_LOST\n"); priv->adhoc_is_link_sensed = false; mwifiex_clean_txrx(priv); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); break; case EVENT_BG_SCAN_REPORT: dev_dbg(adapter->dev, "event: BGS_REPORT\n"); ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY, HostCmd_ACT_GEN_GET, 0, NULL); break; case EVENT_PORT_RELEASE: dev_dbg(adapter->dev, "event: PORT RELEASE\n"); break; case EVENT_WMM_STATUS_CHANGE: dev_dbg(adapter->dev, "event: WMM status changed\n"); ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_WMM_GET_STATUS, 0, 0, NULL); break; case EVENT_RSSI_LOW: cfg80211_cqm_rssi_notify(priv->netdev, NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, GFP_KERNEL); mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO, HostCmd_ACT_GEN_GET, 0, NULL); priv->subsc_evt_rssi_state = RSSI_LOW_RECVD; dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n"); break; case EVENT_SNR_LOW: dev_dbg(adapter->dev, "event: Beacon SNR_LOW\n"); break; case EVENT_MAX_FAIL: dev_dbg(adapter->dev, "event: MAX_FAIL\n"); break; case EVENT_RSSI_HIGH: cfg80211_cqm_rssi_notify(priv->netdev, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, GFP_KERNEL); mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO, HostCmd_ACT_GEN_GET, 0, NULL); priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD; dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n"); break; case EVENT_SNR_HIGH: dev_dbg(adapter->dev, "event: Beacon SNR_HIGH\n"); break; case EVENT_DATA_RSSI_LOW: dev_dbg(adapter->dev, "event: Data RSSI_LOW\n"); break; case EVENT_DATA_SNR_LOW: dev_dbg(adapter->dev, "event: Data SNR_LOW\n"); break; case EVENT_DATA_RSSI_HIGH: dev_dbg(adapter->dev, "event: Data RSSI_HIGH\n"); break; case EVENT_DATA_SNR_HIGH: dev_dbg(adapter->dev, "event: Data SNR_HIGH\n"); break; case EVENT_LINK_QUALITY: dev_dbg(adapter->dev, "event: Link Quality\n"); break; case EVENT_PRE_BEACON_LOST: dev_dbg(adapter->dev, "event: Pre-Beacon Lost\n"); break; case EVENT_IBSS_COALESCED: dev_dbg(adapter->dev, "event: IBSS_COALESCED\n"); ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, HostCmd_ACT_GEN_GET, 0, NULL); break; case EVENT_ADDBA: dev_dbg(adapter->dev, "event: ADDBA Request\n"); mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP, HostCmd_ACT_GEN_SET, 0, adapter->event_body); break; case EVENT_DELBA: dev_dbg(adapter->dev, "event: DELBA Request\n"); mwifiex_11n_delete_ba_stream(priv, adapter->event_body); break; case EVENT_BA_STREAM_TIEMOUT: dev_dbg(adapter->dev, "event: BA Stream timeout\n"); mwifiex_11n_ba_stream_timeout(priv, (struct host_cmd_ds_11n_batimeout *) adapter->event_body); break; case EVENT_AMSDU_AGGR_CTRL: ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", ctrl); adapter->tx_buf_size = min_t(u16, adapter->curr_tx_buf_size, ctrl); dev_dbg(adapter->dev, "event: tx_buf_size %d\n", adapter->tx_buf_size); break; case EVENT_WEP_ICV_ERR: dev_dbg(adapter->dev, "event: WEP ICV error\n"); break; case EVENT_BW_CHANGE: dev_dbg(adapter->dev, "event: BW Change\n"); break; case EVENT_HOSTWAKE_STAIE: dev_dbg(adapter->dev, "event: HOSTWAKE_STAIE %d\n", eventcause); break; default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); break; } return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sta_cmdresp.c0000644000175000017500000007017212026211315024610 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: station command response handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* * This function handles the command response error case. * * For scan response error, the function cancels all the pending * scan commands and generates an event to inform the applications * of the scan completion. * * For Power Save command failure, we do not retry enter PS * command in case of Ad-hoc mode. * * For all other response errors, the current command buffer is freed * and returned to the free command queue. */ static void mwifiex_process_cmdresp_error(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ps_mode_enh *pm; unsigned long flags; dev_err(adapter->dev, "CMD_RESP: cmd %#x error, result=%#x\n", resp->command, resp->result); if (adapter->curr_cmd->wait_q_enabled) adapter->cmd_wait_q.status = -1; switch (le16_to_cpu(resp->command)) { case HostCmd_CMD_802_11_PS_MODE_ENH: pm = &resp->params.psmode_enh; dev_err(adapter->dev, "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n", resp->result, le16_to_cpu(pm->action)); /* We do not re-try enter-ps command in ad-hoc mode. */ if (le16_to_cpu(pm->action) == EN_AUTO_PS && (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) && priv->bss_mode == NL80211_IFTYPE_ADHOC) adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; break; case HostCmd_CMD_802_11_SCAN: /* Cancel all pending scan command */ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); mwifiex_insert_cmd_to_free_q(adapter, cmd_node); spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); if (priv->report_scan_result) priv->report_scan_result = false; if (priv->scan_pending_on_block) { priv->scan_pending_on_block = false; up(&priv->async_sem); } break; case HostCmd_CMD_MAC_CONTROL: break; default: break; } /* Handling errors here */ mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd = NULL; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); } /* * This function handles the command response of get RSSI info. * * Handling includes changing the header fields into CPU format * and saving the following parameters in driver - * - Last data and beacon RSSI value * - Average data and beacon RSSI value * - Last data and beacon NF value * - Average data and beacon NF value * * The parameters are send to the application as well, along with * calculated SNR values. */ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = &resp->params.rssi_info_rsp; struct mwifiex_ds_misc_subsc_evt *subsc_evt = &priv->async_subsc_evt_storage; priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); if (priv->subsc_evt_rssi_state == EVENT_HANDLED) return 0; memset(subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); /* Resubscribe low and high rssi events with new thresholds */ subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; subsc_evt->action = HostCmd_ACT_BITWISE_SET; if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - priv->cqm_rssi_hyst); subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + priv->cqm_rssi_hyst); } subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; priv->subsc_evt_rssi_state = EVENT_HANDLED; mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, 0, 0, subsc_evt); return 0; } /* * This function handles the command response of set/get SNMP * MIB parameters. * * Handling includes changing the header fields into CPU format * and saving the parameter in driver. * * The following parameters are supported - * - Fragmentation threshold * - RTS threshold * - Short retry limit */ static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, struct host_cmd_ds_command *resp, u32 *data_buf) { struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; u16 oid = le16_to_cpu(smib->oid); u16 query_type = le16_to_cpu(smib->query_type); u32 ul_temp; dev_dbg(priv->adapter->dev, "info: SNMP_RESP: oid value = %#x," " query_type = %#x, buf size = %#x\n", oid, query_type, le16_to_cpu(smib->buf_size)); if (query_type == HostCmd_ACT_GEN_GET) { ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); if (data_buf) *data_buf = ul_temp; switch (oid) { case FRAG_THRESH_I: dev_dbg(priv->adapter->dev, "info: SNMP_RESP: FragThsd =%u\n", ul_temp); break; case RTS_THRESH_I: dev_dbg(priv->adapter->dev, "info: SNMP_RESP: RTSThsd =%u\n", ul_temp); break; case SHORT_RETRY_LIM_I: dev_dbg(priv->adapter->dev, "info: SNMP_RESP: TxRetryCount=%u\n", ul_temp); break; case DTIM_PERIOD_I: dev_dbg(priv->adapter->dev, "info: SNMP_RESP: DTIM period=%u\n", ul_temp); default: break; } } return 0; } /* * This function handles the command response of get log request * * Handling includes changing the header fields into CPU format * and sending the received parameters to application. */ static int mwifiex_ret_get_log(struct mwifiex_private *priv, struct host_cmd_ds_command *resp, struct mwifiex_ds_get_stats *stats) { struct host_cmd_ds_802_11_get_log *get_log = &resp->params.get_log; if (stats) { stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); stats->failed = le32_to_cpu(get_log->failed); stats->retry = le32_to_cpu(get_log->retry); stats->multi_retry = le32_to_cpu(get_log->multi_retry); stats->frame_dup = le32_to_cpu(get_log->frame_dup); stats->rts_success = le32_to_cpu(get_log->rts_success); stats->rts_failure = le32_to_cpu(get_log->rts_failure); stats->ack_failure = le32_to_cpu(get_log->ack_failure); stats->rx_frag = le32_to_cpu(get_log->rx_frag); stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); stats->fcs_error = le32_to_cpu(get_log->fcs_error); stats->tx_frame = le32_to_cpu(get_log->tx_frame); stats->wep_icv_error[0] = le32_to_cpu(get_log->wep_icv_err_cnt[0]); stats->wep_icv_error[1] = le32_to_cpu(get_log->wep_icv_err_cnt[1]); stats->wep_icv_error[2] = le32_to_cpu(get_log->wep_icv_err_cnt[2]); stats->wep_icv_error[3] = le32_to_cpu(get_log->wep_icv_err_cnt[3]); } return 0; } /* * This function handles the command response of set/get Tx rate * configurations. * * Handling includes changing the header fields into CPU format * and saving the following parameters in driver - * - DSSS rate bitmap * - OFDM rate bitmap * - HT MCS rate bitmaps * * Based on the new rate bitmaps, the function re-evaluates if * auto data rate has been activated. If not, it sends another * query to the firmware to get the current Tx data rate. */ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; struct mwifiex_rate_scope *rate_scope; struct mwifiex_ie_types_header *head; u16 tlv, tlv_buf_len; u8 *tlv_buf; u32 i; tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16)); while (tlv_buf && tlv_buf_len > 0) { tlv = (*tlv_buf); tlv = tlv | (*(tlv_buf + 1) << 8); switch (tlv) { case TLV_TYPE_RATE_SCOPE: rate_scope = (struct mwifiex_rate_scope *) tlv_buf; priv->bitmap_rates[0] = le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); priv->bitmap_rates[1] = le16_to_cpu(rate_scope->ofdm_rate_bitmap); for (i = 0; i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); i++) priv->bitmap_rates[2 + i] = le16_to_cpu(rate_scope-> ht_mcs_rate_bitmap[i]); break; /* Add RATE_DROP tlv here */ } head = (struct mwifiex_ie_types_header *) tlv_buf; tlv_buf += le16_to_cpu(head->len) + sizeof(*head); tlv_buf_len -= le16_to_cpu(head->len); } priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); if (priv->is_data_rate_auto) priv->data_rate = 0; else return mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, HostCmd_ACT_GEN_GET, 0, NULL); return 0; } /* * This function handles the command response of get Tx power level. * * Handling includes saving the maximum and minimum Tx power levels * in driver, as well as sending the values to user. */ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) { int length, max_power = -1, min_power = -1; struct mwifiex_types_power_group *pg_tlv_hdr; struct mwifiex_power_group *pg; if (!data_buf) return -1; pg_tlv_hdr = (struct mwifiex_types_power_group *) ((u8 *) data_buf + sizeof(struct host_cmd_ds_txpwr_cfg)); pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); length = pg_tlv_hdr->length; if (length > 0) { max_power = pg->power_max; min_power = pg->power_min; length -= sizeof(struct mwifiex_power_group); } while (length) { pg++; if (max_power < pg->power_max) max_power = pg->power_max; if (min_power > pg->power_min) min_power = pg->power_min; length -= sizeof(struct mwifiex_power_group); } if (pg_tlv_hdr->length > 0) { priv->min_tx_power_level = (u8) min_power; priv->max_tx_power_level = (u8) max_power; } return 0; } /* * This function handles the command response of set/get Tx power * configurations. * * Handling includes changing the header fields into CPU format * and saving the current Tx power level in driver. */ static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; struct mwifiex_types_power_group *pg_tlv_hdr; struct mwifiex_power_group *pg; u16 action = le16_to_cpu(txp_cfg->action); switch (action) { case HostCmd_ACT_GEN_GET: pg_tlv_hdr = (struct mwifiex_types_power_group *) ((u8 *) txp_cfg + sizeof(struct host_cmd_ds_txpwr_cfg)); pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) mwifiex_get_power_level(priv, txp_cfg); priv->tx_power_level = (u16) pg->power_min; break; case HostCmd_ACT_GEN_SET: if (!le32_to_cpu(txp_cfg->mode)) break; pg_tlv_hdr = (struct mwifiex_types_power_group *) ((u8 *) txp_cfg + sizeof(struct host_cmd_ds_txpwr_cfg)); pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); if (pg->power_max == pg->power_min) priv->tx_power_level = (u16) pg->power_min; break; default: dev_err(adapter->dev, "CMD_RESP: unknown cmd action %d\n", action); return 0; } dev_dbg(adapter->dev, "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", priv->tx_power_level, priv->max_tx_power_level, priv->min_tx_power_level); return 0; } /* * This function handles the command response of get RF Tx power. */ static int mwifiex_ret_rf_tx_power(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; u16 action = le16_to_cpu(txp->action); priv->tx_power_level = le16_to_cpu(txp->cur_level); if (action == HostCmd_ACT_GEN_GET) { priv->max_tx_power_level = txp->max_power; priv->min_tx_power_level = txp->min_power; } dev_dbg(priv->adapter->dev, "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", priv->tx_power_level, priv->max_tx_power_level, priv->min_tx_power_level); return 0; } /* * This function handles the command response of set rf antenna */ static int mwifiex_ret_rf_antenna(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; struct mwifiex_adapter *adapter = priv->adapter; if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) dev_dbg(adapter->dev, "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x" " Rx action = 0x%x, Rx Mode = 0x%04x\n", le16_to_cpu(ant_mimo->action_tx), le16_to_cpu(ant_mimo->tx_ant_mode), le16_to_cpu(ant_mimo->action_rx), le16_to_cpu(ant_mimo->rx_ant_mode)); else dev_dbg(adapter->dev, "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", le16_to_cpu(ant_siso->action), le16_to_cpu(ant_siso->ant_mode)); return 0; } /* * This function handles the command response of set/get MAC address. * * Handling includes saving the MAC address in driver. */ static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_mac_address *cmd_mac_addr = &resp->params.mac_addr; memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); dev_dbg(priv->adapter->dev, "info: set mac address: %pM\n", priv->curr_addr); return 0; } /* * This function handles the command response of set/get MAC multicast * address. */ static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { return 0; } /* * This function handles the command response of get Tx rate query. * * Handling includes changing the header fields into CPU format * and saving the Tx rate and HT information parameters in driver. * * Both rate configuration and current data rate can be retrieved * with this request. */ static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { priv->tx_rate = resp->params.tx_rate.tx_rate; priv->tx_htinfo = resp->params.tx_rate.ht_info; if (!priv->is_data_rate_auto) priv->data_rate = mwifiex_index_to_data_rate(priv, priv->tx_rate, priv->tx_htinfo); return 0; } /* * This function handles the command response of a deauthenticate * command. * * If the deauthenticated MAC matches the current BSS MAC, the connection * state is reset. */ static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct mwifiex_adapter *adapter = priv->adapter; adapter->dbg.num_cmd_deauth++; if (!memcmp(resp->params.deauth.mac_addr, &priv->curr_bss_params.bss_descriptor.mac_address, sizeof(resp->params.deauth.mac_addr))) mwifiex_reset_connect_state(priv); return 0; } /* * This function handles the command response of ad-hoc stop. * * The function resets the connection state in driver. */ static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { mwifiex_reset_connect_state(priv); return 0; } /* * This function handles the command response of set/get key material. * * Handling includes updating the driver parameters to reflect the * changes. */ static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_key_material *key = &resp->params.key_material; if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) { if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { dev_dbg(priv->adapter->dev, "info: key: GTK is set\n"); priv->wpa_is_gtk_set = true; priv->scan_block = false; } } memset(priv->aes_key.key_param_set.key, 0, sizeof(key->key_param_set.key)); priv->aes_key.key_param_set.key_len = key->key_param_set.key_len; memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key, le16_to_cpu(priv->aes_key.key_param_set.key_len)); return 0; } /* * This function handles the command response of get 11d domain information. */ static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = &resp->params.domain_info_resp; struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; u16 action = le16_to_cpu(domain_info->action); u8 no_of_triplet; no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) - IEEE80211_COUNTRY_STRING_LEN) / sizeof(struct ieee80211_country_ie_triplet)); dev_dbg(priv->adapter->dev, "info: 11D Domain Info Resp: no_of_triplet=%d\n", no_of_triplet); if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) { dev_warn(priv->adapter->dev, "11D: invalid number of triplets %d returned\n", no_of_triplet); return -1; } switch (action) { case HostCmd_ACT_GEN_SET: /* Proc Set Action */ break; case HostCmd_ACT_GEN_GET: break; default: dev_err(priv->adapter->dev, "11D: invalid action:%d\n", domain_info->action); return -1; } return 0; } /* * This function handles the command response of get extended version. * * Handling includes forming the extended version string and sending it * to application. */ static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, struct host_cmd_ds_command *resp, struct host_cmd_ds_version_ext *version_ext) { struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; if (version_ext) { version_ext->version_str_sel = ver_ext->version_str_sel; memcpy(version_ext->version_str, ver_ext->version_str, sizeof(char) * 128); memcpy(priv->version_str, ver_ext->version_str, 128); } return 0; } /* * This function handles the command response of register access. * * The register value and offset are returned to the user. For EEPROM * access, the byte count is also returned. */ static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, void *data_buf) { struct mwifiex_ds_reg_rw *reg_rw; struct mwifiex_ds_read_eeprom *eeprom; union reg { struct host_cmd_ds_mac_reg_access *mac; struct host_cmd_ds_bbp_reg_access *bbp; struct host_cmd_ds_rf_reg_access *rf; struct host_cmd_ds_pmic_reg_access *pmic; struct host_cmd_ds_802_11_eeprom_access *eeprom; } r; if (!data_buf) return 0; reg_rw = data_buf; eeprom = data_buf; switch (type) { case HostCmd_CMD_MAC_REG_ACCESS: r.mac = &resp->params.mac_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.mac->offset)); reg_rw->value = r.mac->value; break; case HostCmd_CMD_BBP_REG_ACCESS: r.bbp = &resp->params.bbp_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.bbp->offset)); reg_rw->value = cpu_to_le32((u32) r.bbp->value); break; case HostCmd_CMD_RF_REG_ACCESS: r.rf = &resp->params.rf_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); reg_rw->value = cpu_to_le32((u32) r.bbp->value); break; case HostCmd_CMD_PMIC_REG_ACCESS: r.pmic = &resp->params.pmic_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.pmic->offset)); reg_rw->value = cpu_to_le32((u32) r.pmic->value); break; case HostCmd_CMD_CAU_REG_ACCESS: r.rf = &resp->params.rf_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); reg_rw->value = cpu_to_le32((u32) r.rf->value); break; case HostCmd_CMD_802_11_EEPROM_ACCESS: r.eeprom = &resp->params.eeprom; pr_debug("info: EEPROM read len=%x\n", r.eeprom->byte_count); if (le16_to_cpu(eeprom->byte_count) < le16_to_cpu(r.eeprom->byte_count)) { eeprom->byte_count = cpu_to_le16(0); pr_debug("info: EEPROM read length is too big\n"); return -1; } eeprom->offset = r.eeprom->offset; eeprom->byte_count = r.eeprom->byte_count; if (le16_to_cpu(eeprom->byte_count) > 0) memcpy(&eeprom->value, &r.eeprom->value, le16_to_cpu(r.eeprom->byte_count)); break; default: return -1; } return 0; } /* * This function handles the command response of get IBSS coalescing status. * * If the received BSSID is different than the current one, the current BSSID, * beacon interval, ATIM window and ERP information are updated, along with * changing the ad-hoc state accordingly. */ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = &(resp->params.ibss_coalescing); if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) return 0; dev_dbg(priv->adapter->dev, "info: new BSSID %pM\n", ibss_coal_resp->bssid); /* If rsp has NULL BSSID, Just return..... No Action */ if (is_zero_ether_addr(ibss_coal_resp->bssid)) { dev_warn(priv->adapter->dev, "new BSSID is NULL\n"); return 0; } /* If BSSID is diff, modify current BSS parameters */ if (memcmp(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid, ETH_ALEN)) { /* BSSID */ memcpy(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid, ETH_ALEN); /* Beacon Interval */ priv->curr_bss_params.bss_descriptor.beacon_period = le16_to_cpu(ibss_coal_resp->beacon_interval); /* ERP Information */ priv->curr_bss_params.bss_descriptor.erp_flags = (u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect); priv->adhoc_state = ADHOC_COALESCED; } return 0; } /* * This function handles the command response for subscribe event command. */ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = &resp->params.subsc_evt; /* For every subscribe event command (Get/Set/Clear), FW reports the * current set of subscribed events*/ dev_dbg(priv->adapter->dev, "Bitmap of currently subscribed events: %16x\n", le16_to_cpu(cmd_sub_event->events)); return 0; } /* * This function handles the command responses. * * This is a generic function, which calls command specific * response handlers based on the command ID. */ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, struct host_cmd_ds_command *resp) { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; void *data_buf = adapter->curr_cmd->data_buf; /* If the command is not successful, cleanup and return failure */ if (resp->result != HostCmd_RESULT_OK) { mwifiex_process_cmdresp_error(priv, resp); return -1; } /* Command successful, handle response */ switch (cmdresp_no) { case HostCmd_CMD_GET_HW_SPEC: ret = mwifiex_ret_get_hw_spec(priv, resp); break; case HostCmd_CMD_MAC_CONTROL: break; case HostCmd_CMD_802_11_MAC_ADDRESS: ret = mwifiex_ret_802_11_mac_address(priv, resp); break; case HostCmd_CMD_MAC_MULTICAST_ADR: ret = mwifiex_ret_mac_multicast_adr(priv, resp); break; case HostCmd_CMD_TX_RATE_CFG: ret = mwifiex_ret_tx_rate_cfg(priv, resp); break; case HostCmd_CMD_802_11_SCAN: ret = mwifiex_ret_802_11_scan(priv, resp); adapter->curr_cmd->wait_q_enabled = false; break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: ret = mwifiex_ret_802_11_scan(priv, resp); dev_dbg(adapter->dev, "info: CMD_RESP: BG_SCAN result is ready!\n"); break; case HostCmd_CMD_TXPWR_CFG: ret = mwifiex_ret_tx_power_cfg(priv, resp); break; case HostCmd_CMD_RF_TX_PWR: ret = mwifiex_ret_rf_tx_power(priv, resp); break; case HostCmd_CMD_RF_ANTENNA: ret = mwifiex_ret_rf_antenna(priv, resp); break; case HostCmd_CMD_802_11_PS_MODE_ENH: ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); break; case HostCmd_CMD_802_11_HS_CFG_ENH: ret = mwifiex_ret_802_11_hs_cfg(priv, resp); break; case HostCmd_CMD_802_11_ASSOCIATE: ret = mwifiex_ret_802_11_associate(priv, resp); break; case HostCmd_CMD_802_11_DEAUTHENTICATE: ret = mwifiex_ret_802_11_deauthenticate(priv, resp); break; case HostCmd_CMD_802_11_AD_HOC_START: case HostCmd_CMD_802_11_AD_HOC_JOIN: ret = mwifiex_ret_802_11_ad_hoc(priv, resp); break; case HostCmd_CMD_802_11_AD_HOC_STOP: ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp); break; case HostCmd_CMD_802_11_GET_LOG: ret = mwifiex_ret_get_log(priv, resp, data_buf); break; case HostCmd_CMD_RSSI_INFO: ret = mwifiex_ret_802_11_rssi_info(priv, resp); break; case HostCmd_CMD_802_11_SNMP_MIB: ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf); break; case HostCmd_CMD_802_11_TX_RATE_QUERY: ret = mwifiex_ret_802_11_tx_rate_query(priv, resp); break; case HostCmd_CMD_VERSION_EXT: ret = mwifiex_ret_ver_ext(priv, resp, data_buf); break; case HostCmd_CMD_FUNC_INIT: case HostCmd_CMD_FUNC_SHUTDOWN: break; case HostCmd_CMD_802_11_KEY_MATERIAL: ret = mwifiex_ret_802_11_key_material(priv, resp); break; case HostCmd_CMD_802_11D_DOMAIN_INFO: ret = mwifiex_ret_802_11d_domain_info(priv, resp); break; case HostCmd_CMD_11N_ADDBA_REQ: ret = mwifiex_ret_11n_addba_req(priv, resp); break; case HostCmd_CMD_11N_DELBA: ret = mwifiex_ret_11n_delba(priv, resp); break; case HostCmd_CMD_11N_ADDBA_RSP: ret = mwifiex_ret_11n_addba_resp(priv, resp); break; case HostCmd_CMD_RECONFIGURE_TX_BUFF: adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. tx_buf.buff_size); adapter->tx_buf_size = (adapter->tx_buf_size / MWIFIEX_SDIO_BLOCK_SIZE) * MWIFIEX_SDIO_BLOCK_SIZE; adapter->curr_tx_buf_size = adapter->tx_buf_size; dev_dbg(adapter->dev, "cmd: max_tx_buf_size=%d, tx_buf_size=%d\n", adapter->max_tx_buf_size, adapter->tx_buf_size); if (adapter->if_ops.update_mp_end_port) adapter->if_ops.update_mp_end_port(adapter, le16_to_cpu(resp->params.tx_buf.mp_end_port)); break; case HostCmd_CMD_AMSDU_AGGR_CTRL: break; case HostCmd_CMD_WMM_GET_STATUS: ret = mwifiex_ret_wmm_get_status(priv, resp); break; case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: ret = mwifiex_ret_ibss_coalescing_status(priv, resp); break; case HostCmd_CMD_MAC_REG_ACCESS: case HostCmd_CMD_BBP_REG_ACCESS: case HostCmd_CMD_RF_REG_ACCESS: case HostCmd_CMD_PMIC_REG_ACCESS: case HostCmd_CMD_CAU_REG_ACCESS: case HostCmd_CMD_802_11_EEPROM_ACCESS: ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf); break; case HostCmd_CMD_SET_BSS_MODE: break; case HostCmd_CMD_11N_CFG: break; case HostCmd_CMD_PCIE_DESC_DETAILS: break; case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: ret = mwifiex_ret_subsc_evt(priv, resp); break; case HostCmd_CMD_UAP_SYS_CONFIG: break; case HostCmd_CMD_UAP_BSS_START: priv->bss_started = 1; break; case HostCmd_CMD_UAP_BSS_STOP: priv->bss_started = 0; break; default: dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", resp->command); break; } return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sta_cmd.c0000644000175000017500000012547012026211315023720 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: station command handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* * This function prepares command to set/get RSSI information. * * Preparation includes - * - Setting command ID, action and proper size * - Setting data/beacon average factors * - Resetting SNR/NF/RSSI values in private structure * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action) { cmd->command = cpu_to_le16(HostCmd_CMD_RSSI_INFO); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + S_DS_GEN); cmd->params.rssi_info.action = cpu_to_le16(cmd_action); cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); /* Reset SNR/NF/RSSI values in private structure */ priv->data_rssi_last = 0; priv->data_nf_last = 0; priv->data_rssi_avg = 0; priv->data_nf_avg = 0; priv->bcn_rssi_last = 0; priv->bcn_nf_last = 0; priv->bcn_rssi_avg = 0; priv->bcn_nf_avg = 0; return 0; } /* * This function prepares command to set MAC control. * * Preparation includes - * - Setting command ID, action and proper size * - Ensuring correct endian-ness */ static int mwifiex_cmd_mac_control(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, u16 *action) { struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; if (cmd_action != HostCmd_ACT_GEN_SET) { dev_err(priv->adapter->dev, "mac_control: only support set cmd\n"); return -1; } cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); mac_ctrl->action = cpu_to_le16(*action); return 0; } /* * This function prepares command to set/get SNMP MIB. * * Preparation includes - * - Setting command ID, action and proper size * - Setting SNMP MIB OID number and value * (as required) * - Ensuring correct endian-ness * * The following SNMP MIB OIDs are supported - * - FRAG_THRESH_I : Fragmentation threshold * - RTS_THRESH_I : RTS threshold * - SHORT_RETRY_LIM_I : Short retry limit * - DOT11D_I : 11d support */ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, u32 cmd_oid, u16 *ul_temp) { struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; dev_dbg(priv->adapter->dev, "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) - 1 + S_DS_GEN); snmp_mib->oid = cpu_to_le16((u16)cmd_oid); if (cmd_action == HostCmd_ACT_GEN_GET) { snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET); snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); le16_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); } else if (cmd_action == HostCmd_ACT_GEN_SET) { snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); *((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp); le16_add_cpu(&cmd->size, sizeof(u16)); } dev_dbg(priv->adapter->dev, "cmd: SNMP_CMD: Action=0x%x, OID=0x%x, OIDSize=0x%x," " Value=0x%x\n", cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size), le16_to_cpu(*(__le16 *) snmp_mib->value)); return 0; } /* * This function prepares command to get log. * * Preparation includes - * - Setting command ID and proper size * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd) { cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + S_DS_GEN); return 0; } /* * This function prepares command to set/get Tx data rate configuration. * * Preparation includes - * - Setting command ID, action and proper size * - Setting configuration index, rate scope and rate drop pattern * parameters (as required) * - Ensuring correct endian-ness */ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, u16 *pbitmap_rates) { struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; struct mwifiex_rate_scope *rate_scope; struct mwifiex_rate_drop_pattern *rate_drop; u32 i; cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); rate_cfg->action = cpu_to_le16(cmd_action); rate_cfg->cfg_index = 0; rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg + sizeof(struct host_cmd_ds_tx_rate_cfg)); rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); rate_scope->length = cpu_to_le16 (sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header)); if (pbitmap_rates != NULL) { rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); for (i = 0; i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(pbitmap_rates[2 + i]); } else { rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(priv->bitmap_rates[0]); rate_scope->ofdm_rate_bitmap = cpu_to_le16(priv->bitmap_rates[1]); for (i = 0; i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(priv->bitmap_rates[2 + i]); } rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + sizeof(struct mwifiex_rate_scope)); rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); rate_drop->rate_drop_mode = 0; cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + sizeof(struct mwifiex_rate_scope) + sizeof(struct mwifiex_rate_drop_pattern)); return 0; } /* * This function prepares command to set/get Tx power configuration. * * Preparation includes - * - Setting command ID, action and proper size * - Setting Tx power mode, power group TLV * (as required) * - Ensuring correct endian-ness */ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action, struct host_cmd_ds_txpwr_cfg *txp) { struct mwifiex_types_power_group *pg_tlv; struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG); cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); switch (cmd_action) { case HostCmd_ACT_GEN_SET: if (txp->mode) { pg_tlv = (struct mwifiex_types_power_group *) ((unsigned long) txp + sizeof(struct host_cmd_ds_txpwr_cfg)); memmove(cmd_txp_cfg, txp, sizeof(struct host_cmd_ds_txpwr_cfg) + sizeof(struct mwifiex_types_power_group) + pg_tlv->length); pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) cmd_txp_cfg + sizeof(struct host_cmd_ds_txpwr_cfg)); cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + sizeof(struct mwifiex_types_power_group) + pg_tlv->length); } else { memmove(cmd_txp_cfg, txp, sizeof(*txp)); } cmd_txp_cfg->action = cpu_to_le16(cmd_action); break; case HostCmd_ACT_GEN_GET: cmd_txp_cfg->action = cpu_to_le16(cmd_action); break; } return 0; } /* * This function prepares command to get RF Tx power. */ static int mwifiex_cmd_rf_tx_power(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, void *data_buf) { struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) + S_DS_GEN); cmd->command = cpu_to_le16(HostCmd_CMD_RF_TX_PWR); txp->action = cpu_to_le16(cmd_action); return 0; } /* * This function prepares command to set rf antenna. */ static int mwifiex_cmd_rf_antenna(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_ds_ant_cfg *ant_cfg) { struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo; struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso; cmd->command = cpu_to_le16(HostCmd_CMD_RF_ANTENNA); if (cmd_action != HostCmd_ACT_GEN_SET) return 0; if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) + S_DS_GEN); ant_mimo->action_tx = cpu_to_le16(HostCmd_ACT_SET_TX); ant_mimo->tx_ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); ant_mimo->action_rx = cpu_to_le16(HostCmd_ACT_SET_RX); ant_mimo->rx_ant_mode = cpu_to_le16((u16)ant_cfg->rx_ant); } else { cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) + S_DS_GEN); ant_siso->action = cpu_to_le16(HostCmd_ACT_SET_BOTH); ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); } return 0; } /* * This function prepares command to set Host Sleep configuration. * * Preparation includes - * - Setting command ID and proper size * - Setting Host Sleep action, conditions, ARP filters * (as required) * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_hs_config_param *hscfg_param) { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; u16 hs_activate = false; if (!hscfg_param) /* New Activate command */ hs_activate = true; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); if (!hs_activate && (hscfg_param->conditions != cpu_to_le32(HOST_SLEEP_CFG_CANCEL)) && ((adapter->arp_filter_size > 0) && (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { dev_dbg(adapter->dev, "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", adapter->arp_filter_size); memcpy(((u8 *) hs_cfg) + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), adapter->arp_filter, adapter->arp_filter_size); cmd->size = cpu_to_le16 (adapter->arp_filter_size + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + S_DS_GEN); } else { cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh)); } if (hs_activate) { hs_cfg->action = cpu_to_le16(HS_ACTIVATE); hs_cfg->params.hs_activate.resp_ctrl = RESP_NEEDED; } else { hs_cfg->action = cpu_to_le16(HS_CONFIGURE); hs_cfg->params.hs_config.conditions = hscfg_param->conditions; hs_cfg->params.hs_config.gpio = hscfg_param->gpio; hs_cfg->params.hs_config.gap = hscfg_param->gap; dev_dbg(adapter->dev, "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", hs_cfg->params.hs_config.conditions, hs_cfg->params.hs_config.gpio, hs_cfg->params.hs_config.gap); } return 0; } /* * This function prepares command to set/get MAC address. * * Preparation includes - * - Setting command ID, action and proper size * - Setting MAC address (for SET only) * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action) { cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + S_DS_GEN); cmd->result = 0; cmd->params.mac_addr.action = cpu_to_le16(cmd_action); if (cmd_action == HostCmd_ACT_GEN_SET) memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, ETH_ALEN); return 0; } /* * This function prepares command to set MAC multicast address. * * Preparation includes - * - Setting command ID, action and proper size * - Setting MAC multicast address * - Ensuring correct endian-ness */ static int mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_multicast_list *mcast_list) { struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + S_DS_GEN); cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); mcast_addr->action = cpu_to_le16(cmd_action); mcast_addr->num_of_adrs = cpu_to_le16((u16) mcast_list->num_multicast_addr); memcpy(mcast_addr->mac_list, mcast_list->mac_list, mcast_list->num_multicast_addr * ETH_ALEN); return 0; } /* * This function prepares command to deauthenticate. * * Preparation includes - * - Setting command ID and proper size * - Setting AP MAC address and reason code * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u8 *mac) { struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) + S_DS_GEN); /* Set AP MAC address */ memcpy(deauth->mac_addr, mac, ETH_ALEN); dev_dbg(priv->adapter->dev, "cmd: Deauth: %pM\n", deauth->mac_addr); deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); return 0; } /* * This function prepares command to stop Ad-Hoc network. * * Preparation includes - * - Setting command ID and proper size * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11_ad_hoc_stop(struct host_cmd_ds_command *cmd) { cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); cmd->size = cpu_to_le16(S_DS_GEN); return 0; } /* * This function sets WEP key(s) to key parameter TLV(s). * * Multi-key parameter TLVs are supported, so we can send multiple * WEP keys in a single buffer. */ static int mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, struct mwifiex_ie_type_key_param_set *key_param_set, u16 *key_param_len) { int cur_key_param_len; u8 i; /* Multi-key_param_set TLV is supported */ for (i = 0; i < NUM_WEP_KEYS; i++) { if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) || (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) { key_param_set->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); /* Key_param_set WEP fixed length */ #define KEYPARAMSET_WEP_FIXED_LEN 8 key_param_set->length = cpu_to_le16((u16) (priv->wep_key[i]. key_length + KEYPARAMSET_WEP_FIXED_LEN)); key_param_set->key_type_id = cpu_to_le16(KEY_TYPE_ID_WEP); key_param_set->key_info = cpu_to_le16(KEY_ENABLED | KEY_UNICAST | KEY_MCAST); key_param_set->key_len = cpu_to_le16(priv->wep_key[i].key_length); /* Set WEP key index */ key_param_set->key[0] = i; /* Set default Tx key flag */ if (i == (priv-> wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) key_param_set->key[1] = 1; else key_param_set->key[1] = 0; memmove(&key_param_set->key[2], priv->wep_key[i].key_material, priv->wep_key[i].key_length); cur_key_param_len = priv->wep_key[i].key_length + KEYPARAMSET_WEP_FIXED_LEN + sizeof(struct mwifiex_ie_types_header); *key_param_len += (u16) cur_key_param_len; key_param_set = (struct mwifiex_ie_type_key_param_set *) ((u8 *)key_param_set + cur_key_param_len); } else if (!priv->wep_key[i].key_length) { continue; } else { dev_err(priv->adapter->dev, "key%d Length = %d is incorrect\n", (i + 1), priv->wep_key[i].key_length); return -1; } } return 0; } /* * This function prepares command to set/get/reset network key(s). * * Preparation includes - * - Setting command ID, action and proper size * - Setting WEP keys, WAPI keys or WPA keys along with required * encryption (TKIP, AES) (as required) * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, u32 cmd_oid, struct mwifiex_ds_encrypt_key *enc_key) { struct host_cmd_ds_802_11_key_material *key_material = &cmd->params.key_material; struct host_cmd_tlv_mac_addr *tlv_mac; u16 key_param_len = 0, cmd_size; int ret = 0; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); key_material->action = cpu_to_le16(cmd_action); if (cmd_action == HostCmd_ACT_GEN_GET) { cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN); return ret; } if (!enc_key) { memset(&key_material->key_param_set, 0, (NUM_WEP_KEYS * sizeof(struct mwifiex_ie_type_key_param_set))); ret = mwifiex_set_keyparamset_wep(priv, &key_material->key_param_set, &key_param_len); cmd->size = cpu_to_le16(key_param_len + sizeof(key_material->action) + S_DS_GEN); return ret; } else memset(&key_material->key_param_set, 0, sizeof(struct mwifiex_ie_type_key_param_set)); if (enc_key->is_wapi_key) { dev_dbg(priv->adapter->dev, "info: Set WAPI Key\n"); key_material->key_param_set.key_type_id = cpu_to_le16(KEY_TYPE_ID_WAPI); if (cmd_oid == KEY_INFO_ENABLED) key_material->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); else key_material->key_param_set.key_info = cpu_to_le16(!KEY_ENABLED); key_material->key_param_set.key[0] = enc_key->key_index; if (!priv->sec_info.wapi_key_on) key_material->key_param_set.key[1] = 1; else /* set 0 when re-key */ key_material->key_param_set.key[1] = 0; if (!is_broadcast_ether_addr(enc_key->mac_addr)) { /* WAPI pairwise key: unicast */ key_material->key_param_set.key_info |= cpu_to_le16(KEY_UNICAST); } else { /* WAPI group key: multicast */ key_material->key_param_set.key_info |= cpu_to_le16(KEY_MCAST); priv->sec_info.wapi_key_on = true; } key_material->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); key_material->key_param_set.key_len = cpu_to_le16(WAPI_KEY_LEN); memcpy(&key_material->key_param_set.key[2], enc_key->key_material, enc_key->key_len); memcpy(&key_material->key_param_set.key[2 + enc_key->key_len], enc_key->pn, PN_LEN); key_material->key_param_set.length = cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + sizeof(struct mwifiex_ie_types_header); cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN + key_param_len); return ret; } if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { if (enc_key->is_igtk_key) { dev_dbg(priv->adapter->dev, "cmd: CMAC_AES\n"); key_material->key_param_set.key_type_id = cpu_to_le16(KEY_TYPE_ID_AES_CMAC); if (cmd_oid == KEY_INFO_ENABLED) key_material->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); else key_material->key_param_set.key_info = cpu_to_le16(!KEY_ENABLED); key_material->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); } else { dev_dbg(priv->adapter->dev, "cmd: WPA_AES\n"); key_material->key_param_set.key_type_id = cpu_to_le16(KEY_TYPE_ID_AES); if (cmd_oid == KEY_INFO_ENABLED) key_material->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); else key_material->key_param_set.key_info = cpu_to_le16(!KEY_ENABLED); if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) /* AES pairwise key: unicast */ key_material->key_param_set.key_info |= cpu_to_le16(KEY_UNICAST); else /* AES group key: multicast */ key_material->key_param_set.key_info |= cpu_to_le16(KEY_MCAST); } } else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { dev_dbg(priv->adapter->dev, "cmd: WPA_TKIP\n"); key_material->key_param_set.key_type_id = cpu_to_le16(KEY_TYPE_ID_TKIP); key_material->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) /* TKIP pairwise key: unicast */ key_material->key_param_set.key_info |= cpu_to_le16(KEY_UNICAST); else /* TKIP group key: multicast */ key_material->key_param_set.key_info |= cpu_to_le16(KEY_MCAST); } if (key_material->key_param_set.key_type_id) { key_material->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); key_material->key_param_set.key_len = cpu_to_le16((u16) enc_key->key_len); memcpy(key_material->key_param_set.key, enc_key->key_material, enc_key->key_len); key_material->key_param_set.length = cpu_to_le16((u16) enc_key->key_len + KEYPARAMSET_FIXED_LEN); key_param_len = (u16)(enc_key->key_len + KEYPARAMSET_FIXED_LEN) + sizeof(struct mwifiex_ie_types_header); if (le16_to_cpu(key_material->key_param_set.key_type_id) == KEY_TYPE_ID_AES_CMAC) { struct mwifiex_cmac_param *param = (void *)key_material->key_param_set.key; memcpy(param->ipn, enc_key->pn, IGTK_PN_LEN); memcpy(param->key, enc_key->key_material, WLAN_KEY_LEN_AES_CMAC); key_param_len = sizeof(struct mwifiex_cmac_param); key_material->key_param_set.key_len = cpu_to_le16(key_param_len); key_param_len += KEYPARAMSET_FIXED_LEN; key_material->key_param_set.length = cpu_to_le16(key_param_len); key_param_len += sizeof(struct mwifiex_ie_types_header); } cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN + key_param_len); if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { tlv_mac = (void *)((u8 *)&key_material->key_param_set + key_param_len); tlv_mac->tlv.type = cpu_to_le16(TLV_TYPE_STA_MAC_ADDR); tlv_mac->tlv.len = cpu_to_le16(ETH_ALEN); memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN); cmd_size = key_param_len + S_DS_GEN + sizeof(key_material->action) + sizeof(struct host_cmd_tlv_mac_addr); } else { cmd_size = key_param_len + S_DS_GEN + sizeof(key_material->action); } cmd->size = cpu_to_le16(cmd_size); } return ret; } /* * This function prepares command to set/get 11d domain information. * * Preparation includes - * - Setting command ID, action and proper size * - Setting domain information fields (for SET only) * - Ensuring correct endian-ness */ static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action) { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11d_domain_info *domain_info = &cmd->params.domain_info; struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; u8 no_of_triplet = adapter->domain_reg.no_of_triplet; dev_dbg(adapter->dev, "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); domain_info->action = cpu_to_le16(cmd_action); if (cmd_action == HostCmd_ACT_GEN_GET) { cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); return 0; } /* Set domain info fields */ domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); memcpy(domain->country_code, adapter->domain_reg.country_code, sizeof(domain->country_code)); domain->header.len = cpu_to_le16((no_of_triplet * sizeof(struct ieee80211_country_ie_triplet)) + sizeof(domain->country_code)); if (no_of_triplet) { memcpy(domain->triplet, adapter->domain_reg.triplet, no_of_triplet * sizeof(struct ieee80211_country_ie_triplet)); cmd->size = cpu_to_le16(sizeof(domain_info->action) + le16_to_cpu(domain->header.len) + sizeof(struct mwifiex_ie_types_header) + S_DS_GEN); } else { cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); } return 0; } /* * This function prepares command to set/get IBSS coalescing status. * * Preparation includes - * - Setting command ID, action and proper size * - Setting status to enable or disable (for SET only) * - Ensuring correct endian-ness */ static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd, u16 cmd_action, u16 *enable) { struct host_cmd_ds_802_11_ibss_status *ibss_coal = &(cmd->params.ibss_coalescing); cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) + S_DS_GEN); cmd->result = 0; ibss_coal->action = cpu_to_le16(cmd_action); switch (cmd_action) { case HostCmd_ACT_GEN_SET: if (enable) ibss_coal->enable = cpu_to_le16(*enable); else ibss_coal->enable = 0; break; /* In other case.. Nothing to do */ case HostCmd_ACT_GEN_GET: default: break; } return 0; } /* * This function prepares command to set/get register value. * * Preparation includes - * - Setting command ID, action and proper size * - Setting register offset (for both GET and SET) and * register value (for SET only) * - Ensuring correct endian-ness * * The following type of registers can be accessed with this function - * - MAC register * - BBP register * - RF register * - PMIC register * - CAU register * - EEPROM */ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, u16 cmd_action, void *data_buf) { struct mwifiex_ds_reg_rw *reg_rw = data_buf; switch (le16_to_cpu(cmd->command)) { case HostCmd_CMD_MAC_REG_ACCESS: { struct host_cmd_ds_mac_reg_access *mac_reg; cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); mac_reg = &cmd->params.mac_reg; mac_reg->action = cpu_to_le16(cmd_action); mac_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); mac_reg->value = reg_rw->value; break; } case HostCmd_CMD_BBP_REG_ACCESS: { struct host_cmd_ds_bbp_reg_access *bbp_reg; cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); bbp_reg = &cmd->params.bbp_reg; bbp_reg->action = cpu_to_le16(cmd_action); bbp_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); bbp_reg->value = (u8) le32_to_cpu(reg_rw->value); break; } case HostCmd_CMD_RF_REG_ACCESS: { struct host_cmd_ds_rf_reg_access *rf_reg; cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); rf_reg = &cmd->params.rf_reg; rf_reg->action = cpu_to_le16(cmd_action); rf_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); rf_reg->value = (u8) le32_to_cpu(reg_rw->value); break; } case HostCmd_CMD_PMIC_REG_ACCESS: { struct host_cmd_ds_pmic_reg_access *pmic_reg; cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); pmic_reg = &cmd->params.pmic_reg; pmic_reg->action = cpu_to_le16(cmd_action); pmic_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); pmic_reg->value = (u8) le32_to_cpu(reg_rw->value); break; } case HostCmd_CMD_CAU_REG_ACCESS: { struct host_cmd_ds_rf_reg_access *cau_reg; cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); cau_reg = &cmd->params.rf_reg; cau_reg->action = cpu_to_le16(cmd_action); cau_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); cau_reg->value = (u8) le32_to_cpu(reg_rw->value); break; } case HostCmd_CMD_802_11_EEPROM_ACCESS: { struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf; struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = &cmd->params.eeprom; cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); cmd_eeprom->action = cpu_to_le16(cmd_action); cmd_eeprom->offset = rd_eeprom->offset; cmd_eeprom->byte_count = rd_eeprom->byte_count; cmd_eeprom->value = 0; break; } default: return -1; } return 0; } /* * This function prepares command to set PCI-Express * host buffer configuration * * Preparation includes - * - Setting command ID, action and proper size * - Setting host buffer configuration * - Ensuring correct endian-ness */ static int mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 action) { struct host_cmd_ds_pcie_details *host_spec = &cmd->params.pcie_host_spec; struct pcie_service_card *card = priv->adapter->card; phys_addr_t *buf_pa; cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_pcie_details) + S_DS_GEN); cmd->result = 0; memset(host_spec, 0, sizeof(struct host_cmd_ds_pcie_details)); if (action != HostCmd_ACT_GEN_SET) return 0; /* Send the ring base addresses and count to firmware */ host_spec->txbd_addr_lo = (u32)(card->txbd_ring_pbase); host_spec->txbd_addr_hi = (u32)(((u64)card->txbd_ring_pbase)>>32); host_spec->txbd_count = MWIFIEX_MAX_TXRX_BD; host_spec->rxbd_addr_lo = (u32)(card->rxbd_ring_pbase); host_spec->rxbd_addr_hi = (u32)(((u64)card->rxbd_ring_pbase)>>32); host_spec->rxbd_count = MWIFIEX_MAX_TXRX_BD; host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase); host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32); host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD; if (card->sleep_cookie) { buf_pa = MWIFIEX_SKB_PACB(card->sleep_cookie); host_spec->sleep_cookie_addr_lo = (u32) *buf_pa; host_spec->sleep_cookie_addr_hi = (u32) (((u64)*buf_pa) >> 32); dev_dbg(priv->adapter->dev, "sleep_cook_lo phy addr: 0x%x\n", host_spec->sleep_cookie_addr_lo); } return 0; } /* * This function prepares command for event subscription, configuration * and query. Events can be subscribed or unsubscribed. Current subscribed * events can be queried. Also, current subscribed events are reported in * every FW response. */ static int mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg) { struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt; struct mwifiex_ie_types_rssi_threshold *rssi_tlv; u16 event_bitmap; u8 *pos; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + S_DS_GEN); subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action); dev_dbg(priv->adapter->dev, "cmd: action: %d\n", subsc_evt_cfg->action); /*For query requests, no configuration TLV structures are to be added.*/ if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET) return 0; subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events); event_bitmap = subsc_evt_cfg->events; dev_dbg(priv->adapter->dev, "cmd: event bitmap : %16x\n", event_bitmap); if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) || (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) && (event_bitmap == 0)) { dev_dbg(priv->adapter->dev, "Error: No event specified " "for bitwise action type\n"); return -EINVAL; } /* * Append TLV structures for each of the specified events for * subscribing or re-configuring. This is not required for * bitwise unsubscribing request. */ if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) return 0; pos = ((u8 *)subsc_evt) + sizeof(struct host_cmd_ds_802_11_subsc_evt); if (event_bitmap & BITMASK_BCN_RSSI_LOW) { rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW); rssi_tlv->header.len = cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - sizeof(struct mwifiex_ie_types_header)); rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq; dev_dbg(priv->adapter->dev, "Cfg Beacon Low Rssi event, " "RSSI:-%d dBm, Freq:%d\n", subsc_evt_cfg->bcn_l_rssi_cfg.abs_value, subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); pos += sizeof(struct mwifiex_ie_types_rssi_threshold); le16_add_cpu(&cmd->size, sizeof(struct mwifiex_ie_types_rssi_threshold)); } if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); rssi_tlv->header.len = cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - sizeof(struct mwifiex_ie_types_header)); rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq; dev_dbg(priv->adapter->dev, "Cfg Beacon High Rssi event, " "RSSI:-%d dBm, Freq:%d\n", subsc_evt_cfg->bcn_h_rssi_cfg.abs_value, subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); pos += sizeof(struct mwifiex_ie_types_rssi_threshold); le16_add_cpu(&cmd->size, sizeof(struct mwifiex_ie_types_rssi_threshold)); } return 0; } /* * This function prepares the commands before sending them to the firmware. * * This is a generic function which calls specific command preparation * routines based upon the command number. */ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, u16 cmd_action, u32 cmd_oid, void *data_buf, void *cmd_buf) { struct host_cmd_ds_command *cmd_ptr = cmd_buf; int ret = 0; /* Prepare command */ switch (cmd_no) { case HostCmd_CMD_GET_HW_SPEC: ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); break; case HostCmd_CMD_MAC_CONTROL: ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_802_11_MAC_ADDRESS: ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr, cmd_action); break; case HostCmd_CMD_MAC_MULTICAST_ADR: ret = mwifiex_cmd_mac_multicast_adr(cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_TX_RATE_CFG: ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_TXPWR_CFG: ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_RF_TX_PWR: ret = mwifiex_cmd_rf_tx_power(priv, cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_RF_ANTENNA: ret = mwifiex_cmd_rf_antenna(priv, cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_802_11_PS_MODE_ENH: ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, (uint16_t)cmd_oid, data_buf); break; case HostCmd_CMD_802_11_HS_CFG_ENH: ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action, (struct mwifiex_hs_config_param *) data_buf); break; case HostCmd_CMD_802_11_SCAN: ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf); break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr); break; case HostCmd_CMD_802_11_ASSOCIATE: ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf); break; case HostCmd_CMD_802_11_DEAUTHENTICATE: ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr, data_buf); break; case HostCmd_CMD_802_11_AD_HOC_START: ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr, data_buf); break; case HostCmd_CMD_802_11_GET_LOG: ret = mwifiex_cmd_802_11_get_log(cmd_ptr); break; case HostCmd_CMD_802_11_AD_HOC_JOIN: ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr, data_buf); break; case HostCmd_CMD_802_11_AD_HOC_STOP: ret = mwifiex_cmd_802_11_ad_hoc_stop(cmd_ptr); break; case HostCmd_CMD_RSSI_INFO: ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action); break; case HostCmd_CMD_802_11_SNMP_MIB: ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action, cmd_oid, data_buf); break; case HostCmd_CMD_802_11_TX_RATE_QUERY: cmd_ptr->command = cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); cmd_ptr->size = cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + S_DS_GEN); priv->tx_rate = 0; ret = 0; break; case HostCmd_CMD_VERSION_EXT: cmd_ptr->command = cpu_to_le16(cmd_no); cmd_ptr->params.verext.version_str_sel = (u8) (*((u32 *) data_buf)); memcpy(&cmd_ptr->params, data_buf, sizeof(struct host_cmd_ds_version_ext)); cmd_ptr->size = cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + S_DS_GEN); ret = 0; break; case HostCmd_CMD_FUNC_INIT: if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; cmd_ptr->command = cpu_to_le16(cmd_no); cmd_ptr->size = cpu_to_le16(S_DS_GEN); break; case HostCmd_CMD_FUNC_SHUTDOWN: priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET; cmd_ptr->command = cpu_to_le16(cmd_no); cmd_ptr->size = cpu_to_le16(S_DS_GEN); break; case HostCmd_CMD_11N_ADDBA_REQ: ret = mwifiex_cmd_11n_addba_req(cmd_ptr, data_buf); break; case HostCmd_CMD_11N_DELBA: ret = mwifiex_cmd_11n_delba(cmd_ptr, data_buf); break; case HostCmd_CMD_11N_ADDBA_RSP: ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf); break; case HostCmd_CMD_802_11_KEY_MATERIAL: ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr, cmd_action, cmd_oid, data_buf); break; case HostCmd_CMD_802_11D_DOMAIN_INFO: ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr, cmd_action); break; case HostCmd_CMD_RECONFIGURE_TX_BUFF: ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_AMSDU_AGGR_CTRL: ret = mwifiex_cmd_amsdu_aggr_ctrl(cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_11N_CFG: ret = mwifiex_cmd_11n_cfg(cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_WMM_GET_STATUS: dev_dbg(priv->adapter->dev, "cmd: WMM: WMM_GET_STATUS cmd sent\n"); cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); cmd_ptr->size = cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + S_DS_GEN); ret = 0; break; case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_MAC_REG_ACCESS: case HostCmd_CMD_BBP_REG_ACCESS: case HostCmd_CMD_RF_REG_ACCESS: case HostCmd_CMD_PMIC_REG_ACCESS: case HostCmd_CMD_CAU_REG_ACCESS: case HostCmd_CMD_802_11_EEPROM_ACCESS: ret = mwifiex_cmd_reg_access(cmd_ptr, cmd_action, data_buf); break; case HostCmd_CMD_SET_BSS_MODE: cmd_ptr->command = cpu_to_le16(cmd_no); if (priv->bss_mode == NL80211_IFTYPE_ADHOC) cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_ADHOC; else if (priv->bss_mode == NL80211_IFTYPE_STATION) cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_INFRA; cmd_ptr->size = cpu_to_le16(sizeof(struct host_cmd_ds_set_bss_mode) + S_DS_GEN); ret = 0; break; case HostCmd_CMD_PCIE_DESC_DETAILS: ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action); break; case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf); break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd- %#x\n", cmd_no); ret = -1; break; } return ret; } /* * This function issues commands to initialize firmware. * * This is called after firmware download to bring the card to * working state. * * The following commands are issued sequentially - * - Set PCI-Express host buffer configuration (PCIE only) * - Function init (for first interface only) * - Read MAC address (for first interface only) * - Reconfigure Tx buffer size (for first interface only) * - Enable auto deep sleep (for first interface only) * - Get Tx rate * - Get Tx power * - Set IBSS coalescing status * - Set AMSDU aggregation control * - Set 11d control * - Set MAC control (this must be the last command to initialize firmware) */ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) { int ret; u16 enable = true; struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; struct mwifiex_ds_auto_ds auto_ds; enum state_11d_t state_11d; struct mwifiex_ds_11n_tx_cfg tx_cfg; if (first_sta) { if (priv->adapter->iface_type == MWIFIEX_PCIE) { ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_PCIE_DESC_DETAILS, HostCmd_ACT_GEN_SET, 0, NULL); if (ret) return -1; } ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_FUNC_INIT, HostCmd_ACT_GEN_SET, 0, NULL); if (ret) return -1; /* Read MAC address from HW */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_GET_HW_SPEC, HostCmd_ACT_GEN_GET, 0, NULL); if (ret) return -1; /* Reconfigure tx buf size */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, HostCmd_ACT_GEN_SET, 0, &priv->adapter->tx_buf_size); if (ret) return -1; if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Enable IEEE PS by default */ priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; ret = mwifiex_send_cmd_async( priv, HostCmd_CMD_802_11_PS_MODE_ENH, EN_AUTO_PS, BITMAP_STA_PS, NULL); if (ret) return -1; } } /* get tx rate */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_TX_RATE_CFG, HostCmd_ACT_GEN_GET, 0, NULL); if (ret) return -1; priv->data_rate = 0; /* get tx power */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_RF_TX_PWR, HostCmd_ACT_GEN_GET, 0, NULL); if (ret) return -1; if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { /* set ibss coalescing_status */ ret = mwifiex_send_cmd_async( priv, HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, HostCmd_ACT_GEN_SET, 0, &enable); if (ret) return -1; } memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); amsdu_aggr_ctrl.enable = true; /* Send request to firmware */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, HostCmd_ACT_GEN_SET, 0, &amsdu_aggr_ctrl); if (ret) return -1; /* MAC Control must be the last command in init_fw */ /* set MAC Control */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, HostCmd_ACT_GEN_SET, 0, &priv->curr_pkt_filter); if (ret) return -1; if (first_sta && priv->adapter->iface_type != MWIFIEX_USB && priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Enable auto deep sleep */ auto_ds.auto_ds = DEEP_SLEEP_ON; auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_PS_MODE_ENH, EN_AUTO_PS, BITMAP_AUTO_DS, &auto_ds); if (ret) return -1; } if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Send cmd to FW to enable/disable 11D function */ state_11d = ENABLE_11D; ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SNMP_MIB, HostCmd_ACT_GEN_SET, DOT11D_I, &state_11d); if (ret) dev_err(priv->adapter->dev, "11D: failed to enable 11D\n"); } /* Send cmd to FW to configure 11n specific configuration * (Short GI, Channel BW, Green field support etc.) for transmit */ tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG; ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_CFG, HostCmd_ACT_GEN_SET, 0, &tx_cfg); /* set last_init_cmd */ priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; ret = -EINPROGRESS; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/sdio.h0000644000175000017500000002412112026211315023240 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: SDIO specific definitions * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_SDIO_H #define _MWIFIEX_SDIO_H #include #include #include #include #include "main.h" #define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" #define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" #define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" #define BLOCK_MODE 1 #define BYTE_MODE 0 #define REG_PORT 0 #define RD_BITMAP_L 0x04 #define RD_BITMAP_U 0x05 #define WR_BITMAP_L 0x06 #define WR_BITMAP_U 0x07 #define RD_LEN_P0_L 0x08 #define RD_LEN_P0_U 0x09 #define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff #define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 #define DATA_PORT_MASK 0xfffe #define MAX_MP_REGS 64 #define MAX_PORT 16 #define SDIO_MP_AGGR_DEF_PKT_LIMIT 8 #define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */ /* Multi port RX aggregation buffer size */ #define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (16384) /* 16K */ /* Misc. Config Register : Auto Re-enable interrupts */ #define AUTO_RE_ENABLE_INT BIT(4) /* Host Control Registers */ /* Host Control Registers : I/O port 0 */ #define IO_PORT_0_REG 0x78 /* Host Control Registers : I/O port 1 */ #define IO_PORT_1_REG 0x79 /* Host Control Registers : I/O port 2 */ #define IO_PORT_2_REG 0x7A /* Host Control Registers : Configuration */ #define CONFIGURATION_REG 0x00 /* Host Control Registers : Host without Command 53 finish host*/ #define HOST_TO_CARD_EVENT (0x1U << 3) /* Host Control Registers : Host without Command 53 finish host */ #define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) /* Host Control Registers : Host power up */ #define HOST_POWER_UP (0x1U << 1) /* Host Control Registers : Host power down */ #define HOST_POWER_DOWN (0x1U << 0) /* Host Control Registers : Host interrupt mask */ #define HOST_INT_MASK_REG 0x02 /* Host Control Registers : Upload host interrupt mask */ #define UP_LD_HOST_INT_MASK (0x1U) /* Host Control Registers : Download host interrupt mask */ #define DN_LD_HOST_INT_MASK (0x2U) /* Enable Host interrupt mask */ #define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) /* Disable Host interrupt mask */ #define HOST_INT_DISABLE 0xff /* Host Control Registers : Host interrupt status */ #define HOST_INTSTATUS_REG 0x03 /* Host Control Registers : Upload host interrupt status */ #define UP_LD_HOST_INT_STATUS (0x1U) /* Host Control Registers : Download host interrupt status */ #define DN_LD_HOST_INT_STATUS (0x2U) /* Host Control Registers : Host interrupt RSR */ #define HOST_INT_RSR_REG 0x01 /* Host Control Registers : Upload host interrupt RSR */ #define UP_LD_HOST_INT_RSR (0x1U) #define SDIO_INT_MASK 0x3F /* Host Control Registers : Host interrupt status */ #define HOST_INT_STATUS_REG 0x28 /* Host Control Registers : Upload CRC error */ #define UP_LD_CRC_ERR (0x1U << 2) /* Host Control Registers : Upload restart */ #define UP_LD_RESTART (0x1U << 1) /* Host Control Registers : Download restart */ #define DN_LD_RESTART (0x1U << 0) /* Card Control Registers : Card status register */ #define CARD_STATUS_REG 0x30 /* Card Control Registers : Card I/O ready */ #define CARD_IO_READY (0x1U << 3) /* Card Control Registers : CIS card ready */ #define CIS_CARD_RDY (0x1U << 2) /* Card Control Registers : Upload card ready */ #define UP_LD_CARD_RDY (0x1U << 1) /* Card Control Registers : Download card ready */ #define DN_LD_CARD_RDY (0x1U << 0) /* Card Control Registers : Host interrupt mask register */ #define HOST_INTERRUPT_MASK_REG 0x34 /* Card Control Registers : Host power interrupt mask */ #define HOST_POWER_INT_MASK (0x1U << 3) /* Card Control Registers : Abort card interrupt mask */ #define ABORT_CARD_INT_MASK (0x1U << 2) /* Card Control Registers : Upload card interrupt mask */ #define UP_LD_CARD_INT_MASK (0x1U << 1) /* Card Control Registers : Download card interrupt mask */ #define DN_LD_CARD_INT_MASK (0x1U << 0) /* Card Control Registers : Card interrupt status register */ #define CARD_INTERRUPT_STATUS_REG 0x38 /* Card Control Registers : Power up interrupt */ #define POWER_UP_INT (0x1U << 4) /* Card Control Registers : Power down interrupt */ #define POWER_DOWN_INT (0x1U << 3) /* Card Control Registers : Card interrupt RSR register */ #define CARD_INTERRUPT_RSR_REG 0x3c /* Card Control Registers : Power up RSR */ #define POWER_UP_RSR (0x1U << 4) /* Card Control Registers : Power down RSR */ #define POWER_DOWN_RSR (0x1U << 3) /* Card Control Registers : Miscellaneous Configuration Register */ #define CARD_MISC_CFG_REG 0x6C /* Host F1 read base 0 */ #define HOST_F1_RD_BASE_0 0x0040 /* Host F1 read base 1 */ #define HOST_F1_RD_BASE_1 0x0041 /* Host F1 card ready */ #define HOST_F1_CARD_RDY 0x0020 /* Firmware status 0 register */ #define CARD_FW_STATUS0_REG 0x60 /* Firmware status 1 register */ #define CARD_FW_STATUS1_REG 0x61 /* Rx length register */ #define CARD_RX_LEN_REG 0x62 /* Rx unit register */ #define CARD_RX_UNIT_REG 0x63 /* Max retry number of CMD53 write */ #define MAX_WRITE_IOMEM_RETRY 2 /* SDIO Tx aggregation in progress ? */ #define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0) /* SDIO Tx aggregation buffer room for next packet ? */ #define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len) \ <= a->mpa_tx.buf_size) /* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ #define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \ memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len], \ payload, pkt_len); \ a->mpa_tx.buf_len += pkt_len; \ if (!a->mpa_tx.pkt_cnt) \ a->mpa_tx.start_port = port; \ if (a->mpa_tx.start_port <= port) \ a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ else \ a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \ a->mp_end_port))); \ a->mpa_tx.pkt_cnt++; \ } while (0) /* SDIO Tx aggregation limit ? */ #define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) /* SDIO Tx aggregation port limit ? */ #define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \ a->mpa_tx.start_port) && (((MAX_PORT - \ a->mpa_tx.start_port) + a->curr_wr_port) >= \ SDIO_MP_AGGR_DEF_PKT_LIMIT)) /* Reset SDIO Tx aggregation buffer parameters */ #define MP_TX_AGGR_BUF_RESET(a) do { \ a->mpa_tx.pkt_cnt = 0; \ a->mpa_tx.buf_len = 0; \ a->mpa_tx.ports = 0; \ a->mpa_tx.start_port = 0; \ } while (0) /* SDIO Rx aggregation limit ? */ #define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) /* SDIO Tx aggregation port limit ? */ #define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \ a->mpa_rx.start_port) && (((MAX_PORT - \ a->mpa_rx.start_port) + a->curr_rd_port) >= \ SDIO_MP_AGGR_DEF_PKT_LIMIT)) /* SDIO Rx aggregation in progress ? */ #define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) /* SDIO Rx aggregation buffer room for next packet ? */ #define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) /* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ #define MP_RX_AGGR_SETUP(a, skb, port) do { \ a->mpa_rx.buf_len += skb->len; \ if (!a->mpa_rx.pkt_cnt) \ a->mpa_rx.start_port = port; \ if (a->mpa_rx.start_port <= port) \ a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \ else \ a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \ a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \ a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \ a->mpa_rx.pkt_cnt++; \ } while (0) /* Reset SDIO Rx aggregation buffer parameters */ #define MP_RX_AGGR_BUF_RESET(a) do { \ a->mpa_rx.pkt_cnt = 0; \ a->mpa_rx.buf_len = 0; \ a->mpa_rx.ports = 0; \ a->mpa_rx.start_port = 0; \ } while (0) /* data structure for SDIO MPA TX */ struct mwifiex_sdio_mpa_tx { /* multiport tx aggregation buffer pointer */ u8 *buf; u32 buf_len; u32 pkt_cnt; u16 ports; u16 start_port; u8 enabled; u32 buf_size; u32 pkt_aggr_limit; }; struct mwifiex_sdio_mpa_rx { u8 *buf; u32 buf_len; u32 pkt_cnt; u16 ports; u16 start_port; struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; u8 enabled; u32 buf_size; u32 pkt_aggr_limit; }; int mwifiex_bus_register(void); void mwifiex_bus_unregister(void); struct sdio_mmc_card { struct sdio_func *func; struct mwifiex_adapter *adapter; u16 mp_rd_bitmap; u16 mp_wr_bitmap; u16 mp_end_port; u16 mp_data_port_mask; u8 curr_rd_port; u8 curr_wr_port; u8 *mp_regs; struct mwifiex_sdio_mpa_tx mpa_tx; struct mwifiex_sdio_mpa_rx mpa_rx; }; /* * .cmdrsp_complete handler */ static inline int mwifiex_sdio_cmdrsp_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { dev_kfree_skb_any(skb); return 0; } /* * .event_complete handler */ static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { dev_kfree_skb_any(skb); return 0; } #endif /* _MWIFIEX_SDIO_H */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/scan.c0000644000175000017500000016746312026211315023242 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: scan ioctl and command handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "11n.h" #include "cfg80211.h" /* The maximum number of channels the firmware can scan per command */ #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 #define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD 4 #define MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD 15 #define MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD 27 #define MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD 35 /* Memory needed to store a max sized Channel List TLV for a firmware scan */ #define CHAN_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_header) \ + (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN \ *sizeof(struct mwifiex_chan_scan_param_set))) /* Memory needed to store supported rate */ #define RATE_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_rates_param_set) \ + HOSTCMD_SUPPORTED_RATES) /* Memory needed to store a max number/size WildCard SSID TLV for a firmware scan */ #define WILDCARD_SSID_TLV_MAX_SIZE \ (MWIFIEX_MAX_SSID_LIST_LENGTH * \ (sizeof(struct mwifiex_ie_types_wildcard_ssid_params) \ + IEEE80211_MAX_SSID_LEN)) /* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */ #define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config) \ + sizeof(struct mwifiex_ie_types_num_probes) \ + sizeof(struct mwifiex_ie_types_htcap) \ + CHAN_TLV_MAX_SIZE \ + RATE_TLV_MAX_SIZE \ + WILDCARD_SSID_TLV_MAX_SIZE) union mwifiex_scan_cmd_config_tlv { /* Scan configuration (variable length) */ struct mwifiex_scan_cmd_config config; /* Max allocated block */ u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; }; enum cipher_suite { CIPHER_SUITE_TKIP, CIPHER_SUITE_CCMP, CIPHER_SUITE_MAX }; static u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = { { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ }; static u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = { { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ }; /* * This function parses a given IE for a given OUI. * * This is used to parse a WPA/RSN IE to find if it has * a given oui in PTK. */ static u8 mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui) { u8 count; count = iebody->ptk_cnt[0]; /* There could be multiple OUIs for PTK hence 1) Take the length. 2) Check all the OUIs for AES. 3) If one of them is AES then pass success. */ while (count) { if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) return MWIFIEX_OUI_PRESENT; --count; if (count) iebody = (struct ie_body *) ((u8 *) iebody + sizeof(iebody->ptk_body)); } pr_debug("info: %s: OUI is not found in PTK\n", __func__); return MWIFIEX_OUI_NOT_PRESENT; } /* * This function checks if a given OUI is present in a RSN IE. * * The function first checks if a RSN IE is present or not in the * BSS descriptor. It tries to locate the OUI only if such an IE is * present. */ static u8 mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) { u8 *oui; struct ie_body *iebody; u8 ret = MWIFIEX_OUI_NOT_PRESENT; if (((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)). ieee_hdr.element_id == WLAN_EID_RSN))) { iebody = (struct ie_body *) (((u8 *) bss_desc->bcn_rsn_ie->data) + RSN_GTK_OUI_OFFSET); oui = &mwifiex_rsn_oui[cipher][0]; ret = mwifiex_search_oui_in_ie(iebody, oui); if (ret) return ret; } return ret; } /* * This function checks if a given OUI is present in a WPA IE. * * The function first checks if a WPA IE is present or not in the * BSS descriptor. It tries to locate the OUI only if such an IE is * present. */ static u8 mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) { u8 *oui; struct ie_body *iebody; u8 ret = MWIFIEX_OUI_NOT_PRESENT; if (((bss_desc->bcn_wpa_ie) && ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == WLAN_EID_WPA))) { iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; oui = &mwifiex_wpa_oui[cipher][0]; ret = mwifiex_search_oui_in_ie(iebody, oui); if (ret) return ret; } return ret; } /* * This function compares two SSIDs and checks if they match. */ s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2) { if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len)) return -1; return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); } /* * This function checks if wapi is enabled in driver and scanned network is * compatible with it. */ static bool mwifiex_is_bss_wapi(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { if (priv->sec_info.wapi_enabled && (bss_desc->bcn_wapi_ie && ((*(bss_desc->bcn_wapi_ie)).ieee_hdr.element_id == WLAN_EID_BSS_AC_ACCESS_DELAY))) { return true; } return false; } /* * This function checks if driver is configured with no security mode and * scanned network is compatible with it. */ static bool mwifiex_is_bss_no_sec(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != WLAN_EID_WPA)) && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && !priv->sec_info.encryption_mode && !bss_desc->privacy) { return true; } return false; } /* * This function checks if static WEP is enabled in driver and scanned network * is compatible with it. */ static bool mwifiex_is_bss_static_wep(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && bss_desc->privacy) { return true; } return false; } /* * This function checks if wpa is enabled in driver and scanned network is * compatible with it. */ static bool mwifiex_is_bss_wpa(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((bss_desc->bcn_wpa_ie) && ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == WLAN_EID_WPA)) /* * Privacy bit may NOT be set in some APs like * LinkSys WRT54G && bss_desc->privacy */ ) { dev_dbg(priv->adapter->dev, "info: %s: WPA:" " wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " "EncMode=%#x privacy=%#x\n", __func__, (bss_desc->bcn_wpa_ie) ? (*(bss_desc->bcn_wpa_ie)). vend_hdr.element_id : 0, (bss_desc->bcn_rsn_ie) ? (*(bss_desc->bcn_rsn_ie)). ieee_hdr.element_id : 0, (priv->sec_info.wep_enabled) ? "e" : "d", (priv->sec_info.wpa_enabled) ? "e" : "d", (priv->sec_info.wpa2_enabled) ? "e" : "d", priv->sec_info.encryption_mode, bss_desc->privacy); return true; } return false; } /* * This function checks if wpa2 is enabled in driver and scanned network is * compatible with it. */ static bool mwifiex_is_bss_wpa2(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && priv->sec_info.wpa2_enabled && ((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id == WLAN_EID_RSN))) { /* * Privacy bit may NOT be set in some APs like * LinkSys WRT54G && bss_desc->privacy */ dev_dbg(priv->adapter->dev, "info: %s: WPA2: " " wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " "EncMode=%#x privacy=%#x\n", __func__, (bss_desc->bcn_wpa_ie) ? (*(bss_desc->bcn_wpa_ie)). vend_hdr.element_id : 0, (bss_desc->bcn_rsn_ie) ? (*(bss_desc->bcn_rsn_ie)). ieee_hdr.element_id : 0, (priv->sec_info.wep_enabled) ? "e" : "d", (priv->sec_info.wpa_enabled) ? "e" : "d", (priv->sec_info.wpa2_enabled) ? "e" : "d", priv->sec_info.encryption_mode, bss_desc->privacy); return true; } return false; } /* * This function checks if adhoc AES is enabled in driver and scanned network is * compatible with it. */ static bool mwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != WLAN_EID_WPA)) && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && !priv->sec_info.encryption_mode && bss_desc->privacy) { return true; } return false; } /* * This function checks if dynamic WEP is enabled in driver and scanned network * is compatible with it. */ static bool mwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != WLAN_EID_WPA)) && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && priv->sec_info.encryption_mode && bss_desc->privacy) { dev_dbg(priv->adapter->dev, "info: %s: dynamic " "WEP: wpa_ie=%#x wpa2_ie=%#x " "EncMode=%#x privacy=%#x\n", __func__, (bss_desc->bcn_wpa_ie) ? (*(bss_desc->bcn_wpa_ie)). vend_hdr.element_id : 0, (bss_desc->bcn_rsn_ie) ? (*(bss_desc->bcn_rsn_ie)). ieee_hdr.element_id : 0, priv->sec_info.encryption_mode, bss_desc->privacy); return true; } return false; } /* * This function checks if a scanned network is compatible with the driver * settings. * * WEP WPA WPA2 ad-hoc encrypt Network * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible * 0 0 0 0 NONE 0 0 0 yes No security * 0 1 0 0 x 1x 1 x yes WPA (disable * HT if no AES) * 0 0 1 0 x 1x x 1 yes WPA2 (disable * HT if no AES) * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES * 1 0 0 0 NONE 1 0 0 yes Static WEP * (disable HT) * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP * * Compatibility is not matched while roaming, except for mode. */ static s32 mwifiex_is_network_compatible(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc, u32 mode) { struct mwifiex_adapter *adapter = priv->adapter; bss_desc->disable_11n = false; /* Don't check for compatibility if roaming */ if (priv->media_connected && (priv->bss_mode == NL80211_IFTYPE_STATION) && (bss_desc->bss_mode == NL80211_IFTYPE_STATION)) return 0; if (priv->wps.session_enable) { dev_dbg(adapter->dev, "info: return success directly in WPS period\n"); return 0; } if (mwifiex_is_bss_wapi(priv, bss_desc)) { dev_dbg(adapter->dev, "info: return success for WAPI AP\n"); return 0; } if (bss_desc->bss_mode == mode) { if (mwifiex_is_bss_no_sec(priv, bss_desc)) { /* No security */ return 0; } else if (mwifiex_is_bss_static_wep(priv, bss_desc)) { /* Static WEP enabled */ dev_dbg(adapter->dev, "info: Disable 11n in WEP mode.\n"); bss_desc->disable_11n = true; return 0; } else if (mwifiex_is_bss_wpa(priv, bss_desc)) { /* WPA enabled */ if (((priv->adapter->config_bands & BAND_GN || priv->adapter->config_bands & BAND_AN) && bss_desc->bcn_ht_cap) && !mwifiex_is_wpa_oui_present(bss_desc, CIPHER_SUITE_CCMP)) { if (mwifiex_is_wpa_oui_present (bss_desc, CIPHER_SUITE_TKIP)) { dev_dbg(adapter->dev, "info: Disable 11n if AES " "is not supported by AP\n"); bss_desc->disable_11n = true; } else { return -1; } } return 0; } else if (mwifiex_is_bss_wpa2(priv, bss_desc)) { /* WPA2 enabled */ if (((priv->adapter->config_bands & BAND_GN || priv->adapter->config_bands & BAND_AN) && bss_desc->bcn_ht_cap) && !mwifiex_is_rsn_oui_present(bss_desc, CIPHER_SUITE_CCMP)) { if (mwifiex_is_rsn_oui_present (bss_desc, CIPHER_SUITE_TKIP)) { dev_dbg(adapter->dev, "info: Disable 11n if AES " "is not supported by AP\n"); bss_desc->disable_11n = true; } else { return -1; } } return 0; } else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) { /* Ad-hoc AES enabled */ return 0; } else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) { /* Dynamic WEP enabled */ return 0; } /* Security doesn't match */ dev_dbg(adapter->dev, "info: %s: failed: wpa_ie=%#x wpa2_ie=%#x WEP=%s " "WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", __func__, (bss_desc->bcn_wpa_ie) ? (*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id : 0, (bss_desc->bcn_rsn_ie) ? (*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id : 0, (priv->sec_info.wep_enabled) ? "e" : "d", (priv->sec_info.wpa_enabled) ? "e" : "d", (priv->sec_info.wpa2_enabled) ? "e" : "d", priv->sec_info.encryption_mode, bss_desc->privacy); return -1; } /* Mode doesn't match */ return -1; } /* * This function creates a channel list for the driver to scan, based * on region/band information. * * This routine is used for any scan that is not provided with a * specific channel list to scan. */ static int mwifiex_scan_create_channel_list(struct mwifiex_private *priv, const struct mwifiex_user_scan_cfg *user_scan_in, struct mwifiex_chan_scan_param_set *scan_chan_list, u8 filtered_scan) { enum ieee80211_band band; struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; struct mwifiex_adapter *adapter = priv->adapter; int chan_idx = 0, i; for (band = 0; (band < IEEE80211_NUM_BANDS) ; band++) { if (!priv->wdev->wiphy->bands[band]) continue; sband = priv->wdev->wiphy->bands[band]; for (i = 0; (i < sband->n_channels) ; i++) { ch = &sband->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; scan_chan_list[chan_idx].radio_type = band; if (user_scan_in && user_scan_in->chan_list[0].scan_time) scan_chan_list[chan_idx].max_scan_time = cpu_to_le16((u16) user_scan_in-> chan_list[0].scan_time); else if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) scan_chan_list[chan_idx].max_scan_time = cpu_to_le16(adapter->passive_scan_time); else scan_chan_list[chan_idx].max_scan_time = cpu_to_le16(adapter->active_scan_time); if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) scan_chan_list[chan_idx].chan_scan_mode_bitmap |= MWIFIEX_PASSIVE_SCAN; else scan_chan_list[chan_idx].chan_scan_mode_bitmap &= ~MWIFIEX_PASSIVE_SCAN; scan_chan_list[chan_idx].chan_number = (u32) ch->hw_value; if (filtered_scan) { scan_chan_list[chan_idx].max_scan_time = cpu_to_le16(adapter->specific_scan_time); scan_chan_list[chan_idx].chan_scan_mode_bitmap |= MWIFIEX_DISABLE_CHAN_FILT; } chan_idx++; } } return chan_idx; } /* * This function constructs and sends multiple scan config commands to * the firmware. * * Previous routines in the code flow have created a scan command configuration * with any requested TLVs. This function splits the channel TLV into maximum * channels supported per scan lists and sends the portion of the channel TLV, * along with the other TLVs, to the firmware. */ static int mwifiex_scan_channel_list(struct mwifiex_private *priv, u32 max_chan_per_scan, u8 filtered_scan, struct mwifiex_scan_cmd_config *scan_cfg_out, struct mwifiex_ie_types_chan_list_param_set *chan_tlv_out, struct mwifiex_chan_scan_param_set *scan_chan_list) { int ret = 0; struct mwifiex_chan_scan_param_set *tmp_chan_list; struct mwifiex_chan_scan_param_set *start_chan; u32 tlv_idx; u32 total_scan_time; u32 done_early; if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { dev_dbg(priv->adapter->dev, "info: Scan: Null detect: %p, %p, %p\n", scan_cfg_out, chan_tlv_out, scan_chan_list); return -1; } chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); /* Set the temp channel struct pointer to the start of the desired list */ tmp_chan_list = scan_chan_list; /* Loop through the desired channel list, sending a new firmware scan commands for each max_chan_per_scan channels (or for 1,6,11 individually if configured accordingly) */ while (tmp_chan_list->chan_number) { tlv_idx = 0; total_scan_time = 0; chan_tlv_out->header.len = 0; start_chan = tmp_chan_list; done_early = false; /* * Construct the Channel TLV for the scan command. Continue to * insert channel TLVs until: * - the tlv_idx hits the maximum configured per scan command * - the next channel to insert is 0 (end of desired channel * list) * - done_early is set (controlling individual scanning of * 1,6,11) */ while (tlv_idx < max_chan_per_scan && tmp_chan_list->chan_number && !done_early) { dev_dbg(priv->adapter->dev, "info: Scan: Chan(%3d), Radio(%d)," " Mode(%d, %d), Dur(%d)\n", tmp_chan_list->chan_number, tmp_chan_list->radio_type, tmp_chan_list->chan_scan_mode_bitmap & MWIFIEX_PASSIVE_SCAN, (tmp_chan_list->chan_scan_mode_bitmap & MWIFIEX_DISABLE_CHAN_FILT) >> 1, le16_to_cpu(tmp_chan_list->max_scan_time)); /* Copy the current channel TLV to the command being prepared */ memcpy(chan_tlv_out->chan_scan_param + tlv_idx, tmp_chan_list, sizeof(chan_tlv_out->chan_scan_param)); /* Increment the TLV header length by the size appended */ chan_tlv_out->header.len = cpu_to_le16(le16_to_cpu(chan_tlv_out->header.len) + (sizeof(chan_tlv_out->chan_scan_param))); /* * The tlv buffer length is set to the number of bytes * of the between the channel tlv pointer and the start * of the tlv buffer. This compensates for any TLVs * that were appended before the channel list. */ scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - scan_cfg_out->tlv_buf); /* Add the size of the channel tlv header and the data length */ scan_cfg_out->tlv_buf_len += (sizeof(chan_tlv_out->header) + le16_to_cpu(chan_tlv_out->header.len)); /* Increment the index to the channel tlv we are constructing */ tlv_idx++; /* Count the total scan time per command */ total_scan_time += le16_to_cpu(tmp_chan_list->max_scan_time); done_early = false; /* Stop the loop if the *current* channel is in the 1,6,11 set and we are not filtering on a BSSID or SSID. */ if (!filtered_scan && (tmp_chan_list->chan_number == 1 || tmp_chan_list->chan_number == 6 || tmp_chan_list->chan_number == 11)) done_early = true; /* Increment the tmp pointer to the next channel to be scanned */ tmp_chan_list++; /* Stop the loop if the *next* channel is in the 1,6,11 set. This will cause it to be the only channel scanned on the next interation */ if (!filtered_scan && (tmp_chan_list->chan_number == 1 || tmp_chan_list->chan_number == 6 || tmp_chan_list->chan_number == 11)) done_early = true; } /* The total scan time should be less than scan command timeout value */ if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { dev_err(priv->adapter->dev, "total scan time %dms" " is over limit (%dms), scan skipped\n", total_scan_time, MWIFIEX_MAX_TOTAL_SCAN_TIME); ret = -1; break; } priv->adapter->scan_channels = start_chan; /* Send the scan command to the firmware with the specified cfg */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN, HostCmd_ACT_GEN_SET, 0, scan_cfg_out); if (ret) break; } if (ret) return -1; return 0; } /* * This function constructs a scan command configuration structure to use * in scan commands. * * Application layer or other functions can invoke network scanning * with a scan configuration supplied in a user scan configuration structure. * This structure is used as the basis of one or many scan command configuration * commands that are sent to the command processing module and eventually to the * firmware. * * This function creates a scan command configuration structure based on the * following user supplied parameters (if present): * - SSID filter * - BSSID filter * - Number of Probes to be sent * - Channel list * * If the SSID or BSSID filter is not present, the filter is disabled/cleared. * If the number of probes is not set, adapter default setting is used. */ static void mwifiex_config_scan(struct mwifiex_private *priv, const struct mwifiex_user_scan_cfg *user_scan_in, struct mwifiex_scan_cmd_config *scan_cfg_out, struct mwifiex_ie_types_chan_list_param_set **chan_list_out, struct mwifiex_chan_scan_param_set *scan_chan_list, u8 *max_chan_per_scan, u8 *filtered_scan, u8 *scan_current_only) { struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_ie_types_num_probes *num_probes_tlv; struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; struct mwifiex_ie_types_rates_param_set *rates_tlv; u8 *tlv_pos; u32 num_probes; u32 ssid_len; u32 chan_idx; u32 chan_num; u32 scan_type; u16 scan_dur; u8 channel; u8 radio_type; int i; u8 ssid_filter; u8 rates[MWIFIEX_SUPPORTED_RATES]; u32 rates_size; struct mwifiex_ie_types_htcap *ht_cap; /* The tlv_buf_len is calculated for each scan command. The TLVs added in this routine will be preserved since the routine that sends the command will append channelTLVs at *chan_list_out. The difference between the *chan_list_out and the tlv_buf start will be used to calculate the size of anything we add in this routine. */ scan_cfg_out->tlv_buf_len = 0; /* Running tlv pointer. Assigned to chan_list_out at end of function so later routines know where channels can be added to the command buf */ tlv_pos = scan_cfg_out->tlv_buf; /* Initialize the scan as un-filtered; the flag is later set to TRUE below if a SSID or BSSID filter is sent in the command */ *filtered_scan = false; /* Initialize the scan as not being only on the current channel. If the channel list is customized, only contains one channel, and is the active channel, this is set true and data flow is not halted. */ *scan_current_only = false; if (user_scan_in) { /* Default the ssid_filter flag to TRUE, set false under certain wildcard conditions and qualified by the existence of an SSID list before marking the scan as filtered */ ssid_filter = true; /* Set the BSS type scan filter, use Adapter setting if unset */ scan_cfg_out->bss_mode = (user_scan_in->bss_mode ? (u8) user_scan_in-> bss_mode : (u8) adapter->scan_mode); /* Set the number of probes to send, use Adapter setting if unset */ num_probes = (user_scan_in->num_probes ? user_scan_in-> num_probes : adapter->scan_probes); /* * Set the BSSID filter to the incoming configuration, * if non-zero. If not set, it will remain disabled * (all zeros). */ memcpy(scan_cfg_out->specific_bssid, user_scan_in->specific_bssid, sizeof(scan_cfg_out->specific_bssid)); for (i = 0; i < user_scan_in->num_ssids; i++) { ssid_len = user_scan_in->ssid_list[i].ssid_len; wildcard_ssid_tlv = (struct mwifiex_ie_types_wildcard_ssid_params *) tlv_pos; wildcard_ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_WILDCARDSSID); wildcard_ssid_tlv->header.len = cpu_to_le16( (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> max_ssid_length))); /* * max_ssid_length = 0 tells firmware to perform * specific scan for the SSID filled, whereas * max_ssid_length = IEEE80211_MAX_SSID_LEN is for * wildcard scan. */ if (ssid_len) wildcard_ssid_tlv->max_ssid_length = 0; else wildcard_ssid_tlv->max_ssid_length = IEEE80211_MAX_SSID_LEN; memcpy(wildcard_ssid_tlv->ssid, user_scan_in->ssid_list[i].ssid, ssid_len); tlv_pos += (sizeof(wildcard_ssid_tlv->header) + le16_to_cpu(wildcard_ssid_tlv->header.len)); dev_dbg(adapter->dev, "info: scan: ssid[%d]: %s, %d\n", i, wildcard_ssid_tlv->ssid, wildcard_ssid_tlv->max_ssid_length); /* Empty wildcard ssid with a maxlen will match many or potentially all SSIDs (maxlen == 32), therefore do not treat the scan as filtered. */ if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) ssid_filter = false; } /* * The default number of channels sent in the command is low to * ensure the response buffer from the firmware does not * truncate scan results. That is not an issue with an SSID * or BSSID filter applied to the scan results in the firmware. */ if ((i && ssid_filter) || !is_zero_ether_addr(scan_cfg_out->specific_bssid)) *filtered_scan = true; } else { scan_cfg_out->bss_mode = (u8) adapter->scan_mode; num_probes = adapter->scan_probes; } /* * If a specific BSSID or SSID is used, the number of channels in the * scan command will be increased to the absolute maximum. */ if (*filtered_scan) *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; else *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; /* If the input config or adapter has the number of Probes set, add tlv */ if (num_probes) { dev_dbg(adapter->dev, "info: scan: num_probes = %d\n", num_probes); num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); num_probes_tlv->header.len = cpu_to_le16(sizeof(num_probes_tlv->num_probes)); num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); tlv_pos += sizeof(num_probes_tlv->header) + le16_to_cpu(num_probes_tlv->header.len); } /* Append rates tlv */ memset(rates, 0, sizeof(rates)); rates_size = mwifiex_get_supported_rates(priv, rates); rates_tlv = (struct mwifiex_ie_types_rates_param_set *) tlv_pos; rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); rates_tlv->header.len = cpu_to_le16((u16) rates_size); memcpy(rates_tlv->rates, rates, rates_size); tlv_pos += sizeof(rates_tlv->header) + rates_size; dev_dbg(adapter->dev, "info: SCAN_CMD: Rates size = %d\n", rates_size); if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && (priv->adapter->config_bands & BAND_GN || priv->adapter->config_bands & BAND_AN)) { ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); ht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_ht_cap)); radio_type = mwifiex_band_to_radio_type(priv->adapter->config_bands); mwifiex_fill_cap_info(priv, radio_type, ht_cap); tlv_pos += sizeof(struct mwifiex_ie_types_htcap); } /* Append vendor specific IE TLV */ mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); /* * Set the output for the channel TLV to the address in the tlv buffer * past any TLVs that were added in this function (SSID, num_probes). * Channel TLVs will be added past this for each scan command, * preserving the TLVs that were previously added. */ *chan_list_out = (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; if (user_scan_in && user_scan_in->chan_list[0].chan_number) { dev_dbg(adapter->dev, "info: Scan: Using supplied channel list\n"); for (chan_idx = 0; chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX && user_scan_in->chan_list[chan_idx].chan_number; chan_idx++) { channel = user_scan_in->chan_list[chan_idx].chan_number; (scan_chan_list + chan_idx)->chan_number = channel; radio_type = user_scan_in->chan_list[chan_idx].radio_type; (scan_chan_list + chan_idx)->radio_type = radio_type; scan_type = user_scan_in->chan_list[chan_idx].scan_type; if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) (scan_chan_list + chan_idx)->chan_scan_mode_bitmap |= MWIFIEX_PASSIVE_SCAN; else (scan_chan_list + chan_idx)->chan_scan_mode_bitmap &= ~MWIFIEX_PASSIVE_SCAN; if (user_scan_in->chan_list[chan_idx].scan_time) { scan_dur = (u16) user_scan_in-> chan_list[chan_idx].scan_time; } else { if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) scan_dur = adapter->passive_scan_time; else if (*filtered_scan) scan_dur = adapter->specific_scan_time; else scan_dur = adapter->active_scan_time; } (scan_chan_list + chan_idx)->min_scan_time = cpu_to_le16(scan_dur); (scan_chan_list + chan_idx)->max_scan_time = cpu_to_le16(scan_dur); } /* Check if we are only scanning the current channel */ if ((chan_idx == 1) && (user_scan_in->chan_list[0].chan_number == priv->curr_bss_params.bss_descriptor.channel)) { *scan_current_only = true; dev_dbg(adapter->dev, "info: Scan: Scanning current channel only\n"); } chan_num = chan_idx; } else { dev_dbg(adapter->dev, "info: Scan: Creating full region channel list\n"); chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in, scan_chan_list, *filtered_scan); } /* * In associated state we will reduce the number of channels scanned per * scan command to avoid any traffic delay/loss. This number is decided * based on total number of channels to be scanned due to constraints * of command buffers. */ if (priv->media_connected) { if (chan_num < MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD) *max_chan_per_scan = 1; else if (chan_num < MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD) *max_chan_per_scan = 2; else if (chan_num < MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD) *max_chan_per_scan = 3; else *max_chan_per_scan = 4; } } /* * This function inspects the scan response buffer for pointers to * expected TLVs. * * TLVs can be included at the end of the scan response BSS information. * * Data in the buffer is parsed pointers to TLVs that can potentially * be passed back in the response. */ static void mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, struct mwifiex_ie_types_data *tlv, u32 tlv_buf_size, u32 req_tlv_type, struct mwifiex_ie_types_data **tlv_data) { struct mwifiex_ie_types_data *current_tlv; u32 tlv_buf_left; u32 tlv_type; u32 tlv_len; current_tlv = tlv; tlv_buf_left = tlv_buf_size; *tlv_data = NULL; dev_dbg(adapter->dev, "info: SCAN_RESP: tlv_buf_size = %d\n", tlv_buf_size); while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { tlv_type = le16_to_cpu(current_tlv->header.type); tlv_len = le16_to_cpu(current_tlv->header.len); if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { dev_err(adapter->dev, "SCAN_RESP: TLV buffer corrupt\n"); break; } if (req_tlv_type == tlv_type) { switch (tlv_type) { case TLV_TYPE_TSFTIMESTAMP: dev_dbg(adapter->dev, "info: SCAN_RESP: TSF " "timestamp TLV, len = %d\n", tlv_len); *tlv_data = current_tlv; break; case TLV_TYPE_CHANNELBANDLIST: dev_dbg(adapter->dev, "info: SCAN_RESP: channel" " band list TLV, len = %d\n", tlv_len); *tlv_data = current_tlv; break; default: dev_err(adapter->dev, "SCAN_RESP: unhandled TLV = %d\n", tlv_type); /* Give up, this seems corrupted */ return; } } if (*tlv_data) break; tlv_buf_left -= (sizeof(tlv->header) + tlv_len); current_tlv = (struct mwifiex_ie_types_data *) (current_tlv->data + tlv_len); } /* while */ } /* * This function parses provided beacon buffer and updates * respective fields in bss descriptor structure. */ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, struct mwifiex_bssdescriptor *bss_entry) { int ret = 0; u8 element_id; struct ieee_types_fh_param_set *fh_param_set; struct ieee_types_ds_param_set *ds_param_set; struct ieee_types_cf_param_set *cf_param_set; struct ieee_types_ibss_param_set *ibss_param_set; u8 *current_ptr; u8 *rate; u8 element_len; u16 total_ie_len; u8 bytes_to_copy; u8 rate_size; u8 found_data_rate_ie; u32 bytes_left; struct ieee_types_vendor_specific *vendor_ie; const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; found_data_rate_ie = false; rate_size = 0; current_ptr = bss_entry->beacon_buf; bytes_left = bss_entry->beacon_buf_size; /* Process variable IE */ while (bytes_left >= 2) { element_id = *current_ptr; element_len = *(current_ptr + 1); total_ie_len = element_len + sizeof(struct ieee_types_header); if (bytes_left < total_ie_len) { dev_err(adapter->dev, "err: InterpretIE: in processing" " IE, bytes left < IE length\n"); return -1; } switch (element_id) { case WLAN_EID_SSID: bss_entry->ssid.ssid_len = element_len; memcpy(bss_entry->ssid.ssid, (current_ptr + 2), element_len); dev_dbg(adapter->dev, "info: InterpretIE: ssid: %-32s\n", bss_entry->ssid.ssid); break; case WLAN_EID_SUPP_RATES: memcpy(bss_entry->data_rates, current_ptr + 2, element_len); memcpy(bss_entry->supported_rates, current_ptr + 2, element_len); rate_size = element_len; found_data_rate_ie = true; break; case WLAN_EID_FH_PARAMS: fh_param_set = (struct ieee_types_fh_param_set *) current_ptr; memcpy(&bss_entry->phy_param_set.fh_param_set, fh_param_set, sizeof(struct ieee_types_fh_param_set)); break; case WLAN_EID_DS_PARAMS: ds_param_set = (struct ieee_types_ds_param_set *) current_ptr; bss_entry->channel = ds_param_set->current_chan; memcpy(&bss_entry->phy_param_set.ds_param_set, ds_param_set, sizeof(struct ieee_types_ds_param_set)); break; case WLAN_EID_CF_PARAMS: cf_param_set = (struct ieee_types_cf_param_set *) current_ptr; memcpy(&bss_entry->ss_param_set.cf_param_set, cf_param_set, sizeof(struct ieee_types_cf_param_set)); break; case WLAN_EID_IBSS_PARAMS: ibss_param_set = (struct ieee_types_ibss_param_set *) current_ptr; memcpy(&bss_entry->ss_param_set.ibss_param_set, ibss_param_set, sizeof(struct ieee_types_ibss_param_set)); break; case WLAN_EID_ERP_INFO: bss_entry->erp_flags = *(current_ptr + 2); break; case WLAN_EID_EXT_SUPP_RATES: /* * Only process extended supported rate * if data rate is already found. * Data rate IE should come before * extended supported rate IE */ if (found_data_rate_ie) { if ((element_len + rate_size) > MWIFIEX_SUPPORTED_RATES) bytes_to_copy = (MWIFIEX_SUPPORTED_RATES - rate_size); else bytes_to_copy = element_len; rate = (u8 *) bss_entry->data_rates; rate += rate_size; memcpy(rate, current_ptr + 2, bytes_to_copy); rate = (u8 *) bss_entry->supported_rates; rate += rate_size; memcpy(rate, current_ptr + 2, bytes_to_copy); } break; case WLAN_EID_VENDOR_SPECIFIC: vendor_ie = (struct ieee_types_vendor_specific *) current_ptr; if (!memcmp (vendor_ie->vend_hdr.oui, wpa_oui, sizeof(wpa_oui))) { bss_entry->bcn_wpa_ie = (struct ieee_types_vendor_specific *) current_ptr; bss_entry->wpa_offset = (u16) (current_ptr - bss_entry->beacon_buf); } else if (!memcmp(vendor_ie->vend_hdr.oui, wmm_oui, sizeof(wmm_oui))) { if (total_ie_len == sizeof(struct ieee_types_wmm_parameter) || total_ie_len == sizeof(struct ieee_types_wmm_info)) /* * Only accept and copy the WMM IE if * it matches the size expected for the * WMM Info IE or the WMM Parameter IE. */ memcpy((u8 *) &bss_entry->wmm_ie, current_ptr, total_ie_len); } break; case WLAN_EID_RSN: bss_entry->bcn_rsn_ie = (struct ieee_types_generic *) current_ptr; bss_entry->rsn_offset = (u16) (current_ptr - bss_entry->beacon_buf); break; case WLAN_EID_BSS_AC_ACCESS_DELAY: bss_entry->bcn_wapi_ie = (struct ieee_types_generic *) current_ptr; bss_entry->wapi_offset = (u16) (current_ptr - bss_entry->beacon_buf); break; case WLAN_EID_HT_CAPABILITY: bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) (current_ptr + sizeof(struct ieee_types_header)); bss_entry->ht_cap_offset = (u16) (current_ptr + sizeof(struct ieee_types_header) - bss_entry->beacon_buf); break; case WLAN_EID_HT_OPERATION: bss_entry->bcn_ht_oper = (struct ieee80211_ht_operation *)(current_ptr + sizeof(struct ieee_types_header)); bss_entry->ht_info_offset = (u16) (current_ptr + sizeof(struct ieee_types_header) - bss_entry->beacon_buf); break; case WLAN_EID_BSS_COEX_2040: bss_entry->bcn_bss_co_2040 = current_ptr + sizeof(struct ieee_types_header); bss_entry->bss_co_2040_offset = (u16) (current_ptr + sizeof(struct ieee_types_header) - bss_entry->beacon_buf); break; case WLAN_EID_EXT_CAPABILITY: bss_entry->bcn_ext_cap = current_ptr + sizeof(struct ieee_types_header); bss_entry->ext_cap_offset = (u16) (current_ptr + sizeof(struct ieee_types_header) - bss_entry->beacon_buf); break; default: break; } current_ptr += element_len + 2; /* Need to account for IE ID and IE Len */ bytes_left -= (element_len + 2); } /* while (bytes_left > 2) */ return ret; } /* * This function converts radio type scan parameter to a band configuration * to be used in join command. */ static u8 mwifiex_radio_type_to_band(u8 radio_type) { switch (radio_type) { case HostCmd_SCAN_RADIO_TYPE_A: return BAND_A; case HostCmd_SCAN_RADIO_TYPE_BG: default: return BAND_G; } } /* * This is an internal function used to start a scan based on an input * configuration. * * This uses the input user scan configuration information when provided in * order to send the appropriate scan commands to firmware to populate or * update the internal driver scan table. */ int mwifiex_scan_networks(struct mwifiex_private *priv, const struct mwifiex_user_scan_cfg *user_scan_in) { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; struct cmd_ctrl_node *cmd_node; union mwifiex_scan_cmd_config_tlv *scan_cfg_out; struct mwifiex_ie_types_chan_list_param_set *chan_list_out; u32 buf_size; struct mwifiex_chan_scan_param_set *scan_chan_list; u8 filtered_scan; u8 scan_current_chan_only; u8 max_chan_per_scan; unsigned long flags; if (adapter->scan_processing) { dev_dbg(adapter->dev, "cmd: Scan already in process...\n"); return ret; } spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = true; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); if (priv->scan_block) { dev_dbg(adapter->dev, "cmd: Scan is blocked during association...\n"); return ret; } scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), GFP_KERNEL); if (!scan_cfg_out) { dev_err(adapter->dev, "failed to alloc scan_cfg_out\n"); return -ENOMEM; } buf_size = sizeof(struct mwifiex_chan_scan_param_set) * MWIFIEX_USER_SCAN_CHAN_MAX; scan_chan_list = kzalloc(buf_size, GFP_KERNEL); if (!scan_chan_list) { dev_err(adapter->dev, "failed to alloc scan_chan_list\n"); kfree(scan_cfg_out); return -ENOMEM; } mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config, &chan_list_out, scan_chan_list, &max_chan_per_scan, &filtered_scan, &scan_current_chan_only); ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan, &scan_cfg_out->config, chan_list_out, scan_chan_list); /* Get scan command from scan_pending_q and put to cmd_pending_q */ if (!ret) { spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); if (!list_empty(&adapter->scan_pending_q)) { cmd_node = list_first_entry(&adapter->scan_pending_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); adapter->cmd_queued = cmd_node; mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); queue_work(adapter->workqueue, &adapter->main_work); } else { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); } } else { spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = true; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); } kfree(scan_cfg_out); kfree(scan_chan_list); return ret; } /* * This function prepares a scan command to be sent to the firmware. * * This uses the scan command configuration sent to the command processing * module in command preparation stage to configure a scan command structure * to send to firmware. * * The fixed fields specifying the BSS type and BSSID filters as well as a * variable number/length of TLVs are sent in the command to firmware. * * Preparation also includes - * - Setting command ID, and proper size * - Ensuring correct endian-ness */ int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, struct mwifiex_scan_cmd_config *scan_cfg) { struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; /* Set fixed field variables in scan command */ scan_cmd->bss_mode = scan_cfg->bss_mode; memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, sizeof(scan_cmd->bssid)); memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) + sizeof(scan_cmd->bssid) + scan_cfg->tlv_buf_len + S_DS_GEN)); return 0; } /* * This function checks compatibility of requested network with current * driver settings. */ int mwifiex_check_network_compatibility(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { int ret = -1; if (!bss_desc) return -1; if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band, (u16) bss_desc->channel, 0))) { switch (priv->bss_mode) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: ret = mwifiex_is_network_compatible(priv, bss_desc, priv->bss_mode); if (ret) dev_err(priv->adapter->dev, "cannot find ssid " "%s\n", bss_desc->ssid.ssid); break; default: ret = 0; } } return ret; } static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, struct cfg80211_bss *bss) { struct mwifiex_bssdescriptor *bss_desc; int ret; unsigned long flags; /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); if (!bss_desc) { dev_err(priv->adapter->dev, " failed to alloc bss_desc\n"); return -ENOMEM; } ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); if (ret) goto done; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; /* Update current bss descriptor parameters */ spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); priv->curr_bss_params.bss_descriptor.bcn_wpa_ie = NULL; priv->curr_bss_params.bss_descriptor.wpa_offset = 0; priv->curr_bss_params.bss_descriptor.bcn_rsn_ie = NULL; priv->curr_bss_params.bss_descriptor.rsn_offset = 0; priv->curr_bss_params.bss_descriptor.bcn_wapi_ie = NULL; priv->curr_bss_params.bss_descriptor.wapi_offset = 0; priv->curr_bss_params.bss_descriptor.bcn_ht_cap = NULL; priv->curr_bss_params.bss_descriptor.ht_cap_offset = 0; priv->curr_bss_params.bss_descriptor.bcn_ht_oper = NULL; priv->curr_bss_params.bss_descriptor.ht_info_offset = 0; priv->curr_bss_params.bss_descriptor.bcn_bss_co_2040 = NULL; priv->curr_bss_params.bss_descriptor. bss_co_2040_offset = 0; priv->curr_bss_params.bss_descriptor.bcn_ext_cap = NULL; priv->curr_bss_params.bss_descriptor.ext_cap_offset = 0; priv->curr_bss_params.bss_descriptor.beacon_buf = NULL; priv->curr_bss_params.bss_descriptor.beacon_buf_size = 0; /* Make a copy of current BSSID descriptor */ memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, sizeof(priv->curr_bss_params.bss_descriptor)); mwifiex_save_curr_bcn(priv); spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); done: kfree(bss_desc); return 0; } /* * This function handles the command response of scan. * * The response buffer for the scan command has the following * memory layout: * * .-------------------------------------------------------------. * | Header (4 * sizeof(t_u16)): Standard command response hdr | * .-------------------------------------------------------------. * | BufSize (t_u16) : sizeof the BSS Description data | * .-------------------------------------------------------------. * | NumOfSet (t_u8) : Number of BSS Descs returned | * .-------------------------------------------------------------. * | BSSDescription data (variable, size given in BufSize) | * .-------------------------------------------------------------. * | TLV data (variable, size calculated using Header->Size, | * | BufSize and sizeof the fixed fields above) | * .-------------------------------------------------------------. */ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; struct cmd_ctrl_node *cmd_node; struct host_cmd_ds_802_11_scan_rsp *scan_rsp; struct mwifiex_ie_types_data *tlv_data; struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; u8 *bss_info; u32 scan_resp_size; u32 bytes_left; u32 idx; u32 tlv_buf_size; struct mwifiex_chan_freq_power *cfp; struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; struct chan_band_param_set *chan_band; u8 is_bgscan_resp; unsigned long flags; struct cfg80211_bss *bss; is_bgscan_resp = (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_BG_SCAN_QUERY); if (is_bgscan_resp) scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; else scan_rsp = &resp->params.scan_resp; if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) { dev_err(adapter->dev, "SCAN_RESP: too many AP returned (%d)\n", scan_rsp->number_of_sets); ret = -1; goto done; } bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); dev_dbg(adapter->dev, "info: SCAN_RESP: bss_descript_size %d\n", bytes_left); scan_resp_size = le16_to_cpu(resp->size); dev_dbg(adapter->dev, "info: SCAN_RESP: returned %d APs before parsing\n", scan_rsp->number_of_sets); bss_info = scan_rsp->bss_desc_and_tlv_buffer; /* * The size of the TLV buffer is equal to the entire command response * size (scan_resp_size) minus the fixed fields (sizeof()'s), the * BSS Descriptions (bss_descript_size as bytesLef) and the command * response header (S_DS_GEN) */ tlv_buf_size = scan_resp_size - (bytes_left + sizeof(scan_rsp->bss_descript_size) + sizeof(scan_rsp->number_of_sets) + S_DS_GEN); tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> bss_desc_and_tlv_buffer + bytes_left); /* Search the TLV buffer space in the scan response for any valid TLVs */ mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, TLV_TYPE_TSFTIMESTAMP, (struct mwifiex_ie_types_data **) &tsf_tlv); /* Search the TLV buffer space in the scan response for any valid TLVs */ mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, TLV_TYPE_CHANNELBANDLIST, (struct mwifiex_ie_types_data **) &chan_band_tlv); for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { u8 bssid[ETH_ALEN]; s32 rssi; const u8 *ie_buf; size_t ie_len; u16 channel = 0; u64 fw_tsf = 0; u16 beacon_size = 0; u32 curr_bcn_bytes; u32 freq; u16 beacon_period; u16 cap_info_bitmap; u8 *current_ptr; u64 timestamp; struct mwifiex_bcn_param *bcn_param; struct mwifiex_bss_priv *bss_priv; if (bytes_left >= sizeof(beacon_size)) { /* Extract & convert beacon size from command buffer */ memcpy(&beacon_size, bss_info, sizeof(beacon_size)); bytes_left -= sizeof(beacon_size); bss_info += sizeof(beacon_size); } if (!beacon_size || beacon_size > bytes_left) { bss_info += bytes_left; bytes_left = 0; return -1; } /* Initialize the current working beacon pointer for this BSS * iteration */ current_ptr = bss_info; /* Advance the return beacon pointer past the current beacon */ bss_info += beacon_size; bytes_left -= beacon_size; curr_bcn_bytes = beacon_size; /* * First 5 fields are bssid, RSSI, time stamp, beacon interval, * and capability information */ if (curr_bcn_bytes < sizeof(struct mwifiex_bcn_param)) { dev_err(adapter->dev, "InterpretIE: not enough bytes left\n"); continue; } bcn_param = (struct mwifiex_bcn_param *)current_ptr; current_ptr += sizeof(*bcn_param); curr_bcn_bytes -= sizeof(*bcn_param); memcpy(bssid, bcn_param->bssid, ETH_ALEN); rssi = (s32) bcn_param->rssi; rssi = (-rssi) * 100; /* Convert dBm to mBm */ dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi); timestamp = le64_to_cpu(bcn_param->timestamp); beacon_period = le16_to_cpu(bcn_param->beacon_period); cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n", cap_info_bitmap); /* Rest of the current buffer are IE's */ ie_buf = current_ptr; ie_len = curr_bcn_bytes; dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n", curr_bcn_bytes); while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { u8 element_id, element_len; element_id = *current_ptr; element_len = *(current_ptr + 1); if (curr_bcn_bytes < element_len + sizeof(struct ieee_types_header)) { dev_err(priv->adapter->dev, "%s: bytes left < IE length\n", __func__); goto done; } if (element_id == WLAN_EID_DS_PARAMS) { channel = *(current_ptr + sizeof(struct ieee_types_header)); break; } current_ptr += element_len + sizeof(struct ieee_types_header); curr_bcn_bytes -= element_len + sizeof(struct ieee_types_header); } /* * If the TSF TLV was appended to the scan results, save this * entry's TSF value in the fw_tsf field. It is the firmware's * TSF value at the time the beacon or probe response was * received. */ if (tsf_tlv) memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], sizeof(fw_tsf)); if (channel) { struct ieee80211_channel *chan; u8 band; band = BAND_G; if (chan_band_tlv) { chan_band = &chan_band_tlv->chan_band_param[idx]; band = mwifiex_radio_type_to_band( chan_band->radio_type & (BIT(0) | BIT(1))); } cfp = mwifiex_get_cfp(priv, band, channel, 0); freq = cfp ? cfp->freq : 0; chan = ieee80211_get_channel(priv->wdev->wiphy, freq); if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, bssid, timestamp, cap_info_bitmap, beacon_period, ie_buf, ie_len, rssi, GFP_KERNEL); bss_priv = (struct mwifiex_bss_priv *)bss->priv; bss_priv->band = band; bss_priv->fw_tsf = fw_tsf; if (priv->media_connected && !memcmp(bssid, priv->curr_bss_params.bss_descriptor .mac_address, ETH_ALEN)) mwifiex_update_curr_bss_params(priv, bss); cfg80211_put_bss(bss); } } else { dev_dbg(adapter->dev, "missing BSS channel IE\n"); } } spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); if (list_empty(&adapter->scan_pending_q)) { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); /* Need to indicate IOCTL complete */ if (adapter->curr_cmd->wait_q_enabled) { adapter->cmd_wait_q.status = 0; mwifiex_complete_cmd(adapter, adapter->curr_cmd); } if (priv->report_scan_result) priv->report_scan_result = false; if (priv->scan_pending_on_block) { priv->scan_pending_on_block = false; up(&priv->async_sem); } if (priv->user_scan_cfg) { dev_dbg(priv->adapter->dev, "info: %s: sending scan results\n", __func__); cfg80211_scan_done(priv->scan_request, 0); priv->scan_request = NULL; kfree(priv->user_scan_cfg); priv->user_scan_cfg = NULL; } } else { if (!mwifiex_wmm_lists_empty(adapter)) { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); adapter->scan_delay_cnt = 1; mod_timer(&priv->scan_delay_timer, jiffies + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); } else { /* Get scan command from scan_pending_q and put to cmd_pending_q */ cmd_node = list_first_entry(&adapter->scan_pending_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); } } done: return ret; } /* * This function prepares command for background scan query. * * Preparation includes - * - Setting command ID and proper size * - Setting background scan flush parameter * - Ensuring correct endian-ness */ int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) { struct host_cmd_ds_802_11_bg_scan_query *bg_query = &cmd->params.bg_scan_query; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) + S_DS_GEN); bg_query->flush = 1; return 0; } /* * This function inserts scan command node to the scan pending queue. */ void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, struct cmd_ctrl_node *cmd_node) { struct mwifiex_adapter *adapter = priv->adapter; unsigned long flags; cmd_node->wait_q_enabled = true; cmd_node->condition = &adapter->scan_wait_q_woken; spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); list_add_tail(&cmd_node->list, &adapter->scan_pending_q); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); } /* * This function sends a scan command for all available channels to the * firmware, filtered on a specific SSID. */ static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, struct cfg80211_ssid *req_ssid) { struct mwifiex_adapter *adapter = priv->adapter; int ret = 0; struct mwifiex_user_scan_cfg *scan_cfg; if (!req_ssid) return -1; if (adapter->scan_processing) { dev_dbg(adapter->dev, "cmd: Scan already in process...\n"); return ret; } if (priv->scan_block) { dev_dbg(adapter->dev, "cmd: Scan is blocked during association...\n"); return ret; } scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); if (!scan_cfg) { dev_err(adapter->dev, "failed to alloc scan_cfg\n"); return -ENOMEM; } scan_cfg->ssid_list = req_ssid; scan_cfg->num_ssids = 1; ret = mwifiex_scan_networks(priv, scan_cfg); kfree(scan_cfg); return ret; } /* * Sends IOCTL request to start a scan. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. * * Scan command can be issued for both normal scan and specific SSID * scan, depending upon whether an SSID is provided or not. */ int mwifiex_request_scan(struct mwifiex_private *priv, struct cfg80211_ssid *req_ssid) { int ret; if (down_interruptible(&priv->async_sem)) { dev_err(priv->adapter->dev, "%s: acquire semaphore\n", __func__); return -1; } priv->scan_pending_on_block = true; priv->adapter->scan_wait_q_woken = false; if (req_ssid && req_ssid->ssid_len != 0) /* Specific SSID scan */ ret = mwifiex_scan_specific_ssid(priv, req_ssid); else /* Normal scan */ ret = mwifiex_scan_networks(priv, NULL); if (!ret) ret = mwifiex_wait_queue_complete(priv->adapter); if (ret == -1) { priv->scan_pending_on_block = false; up(&priv->async_sem); } return ret; } /* * This function appends the vendor specific IE TLV to a buffer. */ int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, u8 **buffer) { int id, ret_len = 0; struct mwifiex_ie_types_vendor_param_set *vs_param_set; if (!buffer) return 0; if (!(*buffer)) return 0; /* * Traverse through the saved vendor specific IE array and append * the selected(scan/assoc/adhoc) IE as TLV to the command */ for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { if (priv->vs_ie[id].mask & vsie_mask) { vs_param_set = (struct mwifiex_ie_types_vendor_param_set *) *buffer; vs_param_set->header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); vs_param_set->header.len = cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) & 0x00FF) + 2); memcpy(vs_param_set->ie, priv->vs_ie[id].ie, le16_to_cpu(vs_param_set->header.len)); *buffer += le16_to_cpu(vs_param_set->header.len) + sizeof(struct mwifiex_ie_types_header); ret_len += le16_to_cpu(vs_param_set->header.len) + sizeof(struct mwifiex_ie_types_header); } } return ret_len; } /* * This function saves a beacon buffer of the current BSS descriptor. * * The current beacon buffer is saved so that it can be restored in the * following cases that makes the beacon buffer not to contain the current * ssid's beacon buffer. * - The current ssid was not found somehow in the last scan. * - The current ssid was the last entry of the scan table and overloaded. */ void mwifiex_save_curr_bcn(struct mwifiex_private *priv) { struct mwifiex_bssdescriptor *curr_bss = &priv->curr_bss_params.bss_descriptor; if (!curr_bss->beacon_buf_size) return; /* allocate beacon buffer at 1st time; or if it's size has changed */ if (!priv->curr_bcn_buf || priv->curr_bcn_size != curr_bss->beacon_buf_size) { priv->curr_bcn_size = curr_bss->beacon_buf_size; kfree(priv->curr_bcn_buf); priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size, GFP_ATOMIC); if (!priv->curr_bcn_buf) { dev_err(priv->adapter->dev, "failed to alloc curr_bcn_buf\n"); return; } } memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, curr_bss->beacon_buf_size); dev_dbg(priv->adapter->dev, "info: current beacon saved %d\n", priv->curr_bcn_size); curr_bss->beacon_buf = priv->curr_bcn_buf; /* adjust the pointers in the current BSS descriptor */ if (curr_bss->bcn_wpa_ie) curr_bss->bcn_wpa_ie = (struct ieee_types_vendor_specific *) (curr_bss->beacon_buf + curr_bss->wpa_offset); if (curr_bss->bcn_rsn_ie) curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) (curr_bss->beacon_buf + curr_bss->rsn_offset); if (curr_bss->bcn_ht_cap) curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) (curr_bss->beacon_buf + curr_bss->ht_cap_offset); if (curr_bss->bcn_ht_oper) curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) (curr_bss->beacon_buf + curr_bss->ht_info_offset); if (curr_bss->bcn_bss_co_2040) curr_bss->bcn_bss_co_2040 = (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); if (curr_bss->bcn_ext_cap) curr_bss->bcn_ext_cap = curr_bss->beacon_buf + curr_bss->ext_cap_offset; } /* * This function frees the current BSS descriptor beacon buffer. */ void mwifiex_free_curr_bcn(struct mwifiex_private *priv) { kfree(priv->curr_bcn_buf); priv->curr_bcn_buf = NULL; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/pcie.h0000644000175000017500000001123512026211315023224 0ustar mcgrofmcgrof/* @file mwifiex_pcie.h * * @brief This file contains definitions for PCI-E interface. * driver. * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_PCIE_H #define _MWIFIEX_PCIE_H #include #include #include #include "main.h" #define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin" /* Constants for Buffer Descriptor (BD) rings */ #define MWIFIEX_MAX_TXRX_BD 0x20 #define MWIFIEX_TXBD_MASK 0x3F #define MWIFIEX_RXBD_MASK 0x3F #define MWIFIEX_MAX_EVT_BD 0x04 #define MWIFIEX_EVTBD_MASK 0x07 /* PCIE INTERNAL REGISTERS */ #define PCIE_SCRATCH_0_REG 0xC10 #define PCIE_SCRATCH_1_REG 0xC14 #define PCIE_CPU_INT_EVENT 0xC18 #define PCIE_CPU_INT_STATUS 0xC1C #define PCIE_HOST_INT_STATUS 0xC30 #define PCIE_HOST_INT_MASK 0xC34 #define PCIE_HOST_INT_STATUS_MASK 0xC3C #define PCIE_SCRATCH_2_REG 0xC40 #define PCIE_SCRATCH_3_REG 0xC44 #define PCIE_SCRATCH_4_REG 0xCD0 #define PCIE_SCRATCH_5_REG 0xCD4 #define PCIE_SCRATCH_6_REG 0xCD8 #define PCIE_SCRATCH_7_REG 0xCDC #define PCIE_SCRATCH_8_REG 0xCE0 #define PCIE_SCRATCH_9_REG 0xCE4 #define PCIE_SCRATCH_10_REG 0xCE8 #define PCIE_SCRATCH_11_REG 0xCEC #define PCIE_SCRATCH_12_REG 0xCF0 #define CPU_INTR_DNLD_RDY BIT(0) #define CPU_INTR_DOOR_BELL BIT(1) #define CPU_INTR_SLEEP_CFM_DONE BIT(2) #define CPU_INTR_RESET BIT(3) #define HOST_INTR_DNLD_DONE BIT(0) #define HOST_INTR_UPLD_RDY BIT(1) #define HOST_INTR_CMD_DONE BIT(2) #define HOST_INTR_EVENT_RDY BIT(3) #define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \ HOST_INTR_UPLD_RDY | \ HOST_INTR_CMD_DONE | \ HOST_INTR_EVENT_RDY) #define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7) #define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0) #define MWIFIEX_BD_FLAG_LAST_DESC BIT(1) #define REG_CMD_ADDR_LO PCIE_SCRATCH_0_REG #define REG_CMD_ADDR_HI PCIE_SCRATCH_1_REG #define REG_CMD_SIZE PCIE_SCRATCH_2_REG #define REG_CMDRSP_ADDR_LO PCIE_SCRATCH_4_REG #define REG_CMDRSP_ADDR_HI PCIE_SCRATCH_5_REG /* TX buffer description read pointer */ #define REG_TXBD_RDPTR PCIE_SCRATCH_6_REG /* TX buffer description write pointer */ #define REG_TXBD_WRPTR PCIE_SCRATCH_7_REG /* RX buffer description read pointer */ #define REG_RXBD_RDPTR PCIE_SCRATCH_8_REG /* RX buffer description write pointer */ #define REG_RXBD_WRPTR PCIE_SCRATCH_9_REG /* Event buffer description read pointer */ #define REG_EVTBD_RDPTR PCIE_SCRATCH_10_REG /* Event buffer description write pointer */ #define REG_EVTBD_WRPTR PCIE_SCRATCH_11_REG /* Driver ready signature write pointer */ #define REG_DRV_READY PCIE_SCRATCH_12_REG /* Max retry number of command write */ #define MAX_WRITE_IOMEM_RETRY 2 /* Define PCIE block size for firmware download */ #define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256 /* FW awake cookie after FW ready */ #define FW_AWAKE_COOKIE (0xAA55AA55) struct mwifiex_pcie_buf_desc { u64 paddr; u16 len; u16 flags; } __packed; struct pcie_service_card { struct pci_dev *dev; struct mwifiex_adapter *adapter; u32 txbd_wrptr; u32 txbd_rdptr; u32 txbd_ring_size; u8 *txbd_ring_vbase; phys_addr_t txbd_ring_pbase; struct mwifiex_pcie_buf_desc *txbd_ring[MWIFIEX_MAX_TXRX_BD]; struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD]; u32 rxbd_wrptr; u32 rxbd_rdptr; u32 rxbd_ring_size; u8 *rxbd_ring_vbase; phys_addr_t rxbd_ring_pbase; struct mwifiex_pcie_buf_desc *rxbd_ring[MWIFIEX_MAX_TXRX_BD]; struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD]; u32 evtbd_wrptr; u32 evtbd_rdptr; u32 evtbd_ring_size; u8 *evtbd_ring_vbase; phys_addr_t evtbd_ring_pbase; struct mwifiex_pcie_buf_desc *evtbd_ring[MWIFIEX_MAX_EVT_BD]; struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD]; struct sk_buff *cmd_buf; struct sk_buff *cmdrsp_buf; struct sk_buff *sleep_cookie; void __iomem *pci_mmap; void __iomem *pci_mmap1; }; #endif /* _MWIFIEX_PCIE_H */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/pcie.c0000644000175000017500000014545412026211315023232 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: PCIE specific handling * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" #include "pcie.h" #define PCIE_VERSION "1.0" #define DRV_NAME "Marvell mwifiex PCIe" static u8 user_rmmod; static struct mwifiex_if_ops pcie_ops; static struct semaphore add_remove_card_sem; static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter); static int mwifiex_pcie_resume(struct pci_dev *pdev); /* * This function is called after skb allocation to update * "skb->cb" with physical address of data pointer. */ static phys_addr_t *mwifiex_update_sk_buff_pa(struct sk_buff *skb) { phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb); *buf_pa = (phys_addr_t)virt_to_phys(skb->data); return buf_pa; } /* * This function reads sleep cookie and checks if FW is ready */ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) { u32 *cookie_addr; struct pcie_service_card *card = adapter->card; if (card->sleep_cookie) { cookie_addr = (u32 *)card->sleep_cookie->data; dev_dbg(adapter->dev, "info: ACCESS_HW: sleep cookie=0x%x\n", *cookie_addr); if (*cookie_addr == FW_AWAKE_COOKIE) return true; } return false; } /* * This function probes an mwifiex device and registers it. It allocates * the card structure, enables PCIE function number and initiates the * device registration and initialization procedure by adding a logical * interface. */ static int mwifiex_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pcie_service_card *card; pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", pdev->vendor, pdev->device, pdev->revision); card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL); if (!card) return -ENOMEM; card->dev = pdev; if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, MWIFIEX_PCIE)) { pr_err("%s failed\n", __func__); kfree(card); return -1; } return 0; } /* * This function removes the interface and frees up the card structure. */ static void mwifiex_pcie_remove(struct pci_dev *pdev) { struct pcie_service_card *card; struct mwifiex_adapter *adapter; struct mwifiex_private *priv; int i; card = pci_get_drvdata(pdev); if (!card) return; adapter = card->adapter; if (!adapter || !adapter->priv_num) return; /* In case driver is removed when asynchronous FW load is in progress */ wait_for_completion(&adapter->fw_load); if (user_rmmod) { #ifdef CONFIG_PM if (adapter->is_suspended) mwifiex_pcie_resume(pdev); #endif for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); mwifiex_disable_auto_ds(priv); mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); } mwifiex_remove_card(card->adapter, &add_remove_card_sem); kfree(card); } /* * Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not suspended, this function allocates and sends a host * sleep activate request to the firmware and turns off the traffic. */ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state) { struct mwifiex_adapter *adapter; struct pcie_service_card *card; int hs_actived, i; if (pdev) { card = (struct pcie_service_card *) pci_get_drvdata(pdev); if (!card || card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; } } else { pr_err("PCIE device is not specified\n"); return 0; } adapter = card->adapter; hs_actived = mwifiex_enable_hs(adapter); /* Indicate device suspended */ adapter->is_suspended = true; for (i = 0; i < adapter->priv_num; i++) netif_carrier_off(adapter->priv[i]->netdev); return 0; } /* * Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not resumed, this function turns on the traffic and * sends a host sleep cancel request to the firmware. */ static int mwifiex_pcie_resume(struct pci_dev *pdev) { struct mwifiex_adapter *adapter; struct pcie_service_card *card; int i; if (pdev) { card = (struct pcie_service_card *) pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; } } else { pr_err("PCIE device is not specified\n"); return 0; } adapter = card->adapter; if (!adapter->is_suspended) { dev_warn(adapter->dev, "Device already resumed\n"); return 0; } adapter->is_suspended = false; for (i = 0; i < adapter->priv_num; i++) if (adapter->priv[i]->media_connected) netif_carrier_on(adapter->priv[i]->netdev); mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), MWIFIEX_ASYNC_CMD); return 0; } #define PCIE_VENDOR_ID_MARVELL (0x11ab) #define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30) static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = { { PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, {}, }; MODULE_DEVICE_TABLE(pci, mwifiex_ids); /* PCI Device Driver */ static struct pci_driver __refdata mwifiex_pcie = { .name = "mwifiex_pcie", .id_table = mwifiex_ids, .probe = mwifiex_pcie_probe, .remove = mwifiex_pcie_remove, #ifdef CONFIG_PM /* Power Management Hooks */ .suspend = mwifiex_pcie_suspend, .resume = mwifiex_pcie_resume, #endif }; /* * This function writes data into PCIE card register. */ static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) { struct pcie_service_card *card = adapter->card; iowrite32(data, card->pci_mmap1 + reg); return 0; } /* * This function reads data from PCIE card register. */ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) { struct pcie_service_card *card = adapter->card; *data = ioread32(card->pci_mmap1 + reg); return 0; } /* * This function wakes up the card. * * A host power up command is written to the card configuration * register to wake up the card. */ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { int i = 0; while (mwifiex_pcie_ok_to_access_hw(adapter)) { i++; usleep_range(10, 20); /* 50ms max wait */ if (i == 50000) break; } dev_dbg(adapter->dev, "event: Wakeup device...\n"); /* Enable interrupts or any chip access will wakeup device */ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, HOST_INTR_MASK)) { dev_warn(adapter->dev, "Enable host interrupt failed\n"); return -1; } dev_dbg(adapter->dev, "PCIE wakeup: Setting PS_STATE_AWAKE\n"); adapter->ps_state = PS_STATE_AWAKE; return 0; } /* * This function is called after the card has woken up. * * The card configuration register is reset. */ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) { dev_dbg(adapter->dev, "cmd: Wakeup device completed\n"); return 0; } /* * This function disables the host interrupt. * * The host interrupt mask is read, the disable bit is reset and * written back to the card host interrupt mask register. */ static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) { if (mwifiex_pcie_ok_to_access_hw(adapter)) { if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, 0x00000000)) { dev_warn(adapter->dev, "Disable host interrupt failed\n"); return -1; } } return 0; } /* * This function enables the host interrupt. * * The host interrupt enable mask is written to the card * host interrupt mask register. */ static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) { if (mwifiex_pcie_ok_to_access_hw(adapter)) { /* Simply write the mask to the register */ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, HOST_INTR_MASK)) { dev_warn(adapter->dev, "Enable host interrupt failed\n"); return -1; } } return 0; } /* * This function creates buffer descriptor ring for TX */ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; struct sk_buff *skb; int i; phys_addr_t *buf_pa; /* * driver maintaines the write pointer and firmware maintaines the read * pointer. The write pointer starts at 0 (zero) while the read pointer * starts at zero with rollover bit set */ card->txbd_wrptr = 0; card->txbd_rdptr |= MWIFIEX_BD_FLAG_ROLLOVER_IND; /* allocate shared memory for the BD ring and divide the same in to several descriptors */ card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * MWIFIEX_MAX_TXRX_BD; dev_dbg(adapter->dev, "info: txbd_ring: Allocating %d bytes\n", card->txbd_ring_size); card->txbd_ring_vbase = kzalloc(card->txbd_ring_size, GFP_KERNEL); if (!card->txbd_ring_vbase) { dev_err(adapter->dev, "Unable to alloc buffer for txbd ring\n"); return -ENOMEM; } card->txbd_ring_pbase = virt_to_phys(card->txbd_ring_vbase); dev_dbg(adapter->dev, "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n", card->txbd_ring_vbase, (u32)card->txbd_ring_pbase, (u32)((u64)card->txbd_ring_pbase >> 32), card->txbd_ring_size); for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { card->txbd_ring[i] = (struct mwifiex_pcie_buf_desc *) (card->txbd_ring_vbase + (sizeof(struct mwifiex_pcie_buf_desc) * i)); /* Allocate buffer here so that firmware can DMA data from it */ skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); if (!skb) { dev_err(adapter->dev, "Unable to allocate skb for TX ring.\n"); kfree(card->txbd_ring_vbase); return -ENOMEM; } buf_pa = mwifiex_update_sk_buff_pa(skb); skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE); dev_dbg(adapter->dev, "info: TX ring: add new skb base: %p, " "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n", skb, skb->data, (u32)*buf_pa, (u32)(((u64)*buf_pa >> 32)), skb->len); card->tx_buf_list[i] = skb; card->txbd_ring[i]->paddr = *buf_pa; card->txbd_ring[i]->len = (u16)skb->len; card->txbd_ring[i]->flags = 0; } return 0; } static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; int i; for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { if (card->tx_buf_list[i]) dev_kfree_skb_any(card->tx_buf_list[i]); card->tx_buf_list[i] = NULL; card->txbd_ring[i]->paddr = 0; card->txbd_ring[i]->len = 0; card->txbd_ring[i]->flags = 0; card->txbd_ring[i] = NULL; } kfree(card->txbd_ring_vbase); card->txbd_ring_size = 0; card->txbd_wrptr = 0; card->txbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; card->txbd_ring_vbase = NULL; return 0; } /* * This function creates buffer descriptor ring for RX */ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; struct sk_buff *skb; int i; phys_addr_t *buf_pa; /* * driver maintaines the read pointer and firmware maintaines the write * pointer. The write pointer starts at 0 (zero) while the read pointer * starts at zero with rollover bit set */ card->rxbd_wrptr = 0; card->rxbd_rdptr |= MWIFIEX_BD_FLAG_ROLLOVER_IND; card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * MWIFIEX_MAX_TXRX_BD; dev_dbg(adapter->dev, "info: rxbd_ring: Allocating %d bytes\n", card->rxbd_ring_size); card->rxbd_ring_vbase = kzalloc(card->rxbd_ring_size, GFP_KERNEL); if (!card->rxbd_ring_vbase) { dev_err(adapter->dev, "Unable to allocate buffer for " "rxbd_ring.\n"); return -ENOMEM; } card->rxbd_ring_pbase = virt_to_phys(card->rxbd_ring_vbase); dev_dbg(adapter->dev, "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase, (u32)((u64)card->rxbd_ring_pbase >> 32), card->rxbd_ring_size); for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { card->rxbd_ring[i] = (struct mwifiex_pcie_buf_desc *) (card->rxbd_ring_vbase + (sizeof(struct mwifiex_pcie_buf_desc) * i)); /* Allocate skb here so that firmware can DMA data from it */ skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); if (!skb) { dev_err(adapter->dev, "Unable to allocate skb for RX ring.\n"); kfree(card->rxbd_ring_vbase); return -ENOMEM; } buf_pa = mwifiex_update_sk_buff_pa(skb); skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE); dev_dbg(adapter->dev, "info: RX ring: add new skb base: %p, " "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n", skb, skb->data, (u32)*buf_pa, (u32)((u64)*buf_pa >> 32), skb->len); card->rx_buf_list[i] = skb; card->rxbd_ring[i]->paddr = *buf_pa; card->rxbd_ring[i]->len = (u16)skb->len; card->rxbd_ring[i]->flags = 0; } return 0; } /* * This function deletes Buffer descriptor ring for RX */ static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; int i; for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { if (card->rx_buf_list[i]) dev_kfree_skb_any(card->rx_buf_list[i]); card->rx_buf_list[i] = NULL; card->rxbd_ring[i]->paddr = 0; card->rxbd_ring[i]->len = 0; card->rxbd_ring[i]->flags = 0; card->rxbd_ring[i] = NULL; } kfree(card->rxbd_ring_vbase); card->rxbd_ring_size = 0; card->rxbd_wrptr = 0; card->rxbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; card->rxbd_ring_vbase = NULL; return 0; } /* * This function creates buffer descriptor ring for Events */ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; struct sk_buff *skb; int i; phys_addr_t *buf_pa; /* * driver maintaines the read pointer and firmware maintaines the write * pointer. The write pointer starts at 0 (zero) while the read pointer * starts at zero with rollover bit set */ card->evtbd_wrptr = 0; card->evtbd_rdptr |= MWIFIEX_BD_FLAG_ROLLOVER_IND; card->evtbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * MWIFIEX_MAX_EVT_BD; dev_dbg(adapter->dev, "info: evtbd_ring: Allocating %d bytes\n", card->evtbd_ring_size); card->evtbd_ring_vbase = kzalloc(card->evtbd_ring_size, GFP_KERNEL); if (!card->evtbd_ring_vbase) { dev_err(adapter->dev, "Unable to allocate buffer. Terminating download\n"); return -ENOMEM; } card->evtbd_ring_pbase = virt_to_phys(card->evtbd_ring_vbase); dev_dbg(adapter->dev, "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n", card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase, (u32)((u64)card->evtbd_ring_pbase >> 32), card->evtbd_ring_size); for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { card->evtbd_ring[i] = (struct mwifiex_pcie_buf_desc *) (card->evtbd_ring_vbase + (sizeof(struct mwifiex_pcie_buf_desc) * i)); /* Allocate skb here so that firmware can DMA data from it */ skb = dev_alloc_skb(MAX_EVENT_SIZE); if (!skb) { dev_err(adapter->dev, "Unable to allocate skb for EVENT buf.\n"); kfree(card->evtbd_ring_vbase); return -ENOMEM; } buf_pa = mwifiex_update_sk_buff_pa(skb); skb_put(skb, MAX_EVENT_SIZE); dev_dbg(adapter->dev, "info: Evt ring: add new skb. base: %p, " "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n", skb, skb->data, (u32)*buf_pa, (u32)((u64)*buf_pa >> 32), skb->len); card->evt_buf_list[i] = skb; card->evtbd_ring[i]->paddr = *buf_pa; card->evtbd_ring[i]->len = (u16)skb->len; card->evtbd_ring[i]->flags = 0; } return 0; } /* * This function deletes Buffer descriptor ring for Events */ static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; int i; for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { if (card->evt_buf_list[i]) dev_kfree_skb_any(card->evt_buf_list[i]); card->evt_buf_list[i] = NULL; card->evtbd_ring[i]->paddr = 0; card->evtbd_ring[i]->len = 0; card->evtbd_ring[i]->flags = 0; card->evtbd_ring[i] = NULL; } kfree(card->evtbd_ring_vbase); card->evtbd_wrptr = 0; card->evtbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; card->evtbd_ring_size = 0; card->evtbd_ring_vbase = NULL; return 0; } /* * This function allocates a buffer for CMDRSP */ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; struct sk_buff *skb; /* Allocate memory for receiving command response data */ skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); if (!skb) { dev_err(adapter->dev, "Unable to allocate skb for command response data.\n"); return -ENOMEM; } mwifiex_update_sk_buff_pa(skb); skb_put(skb, MWIFIEX_UPLD_SIZE); card->cmdrsp_buf = skb; skb = NULL; /* Allocate memory for sending command to firmware */ skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); if (!skb) { dev_err(adapter->dev, "Unable to allocate skb for command data.\n"); return -ENOMEM; } mwifiex_update_sk_buff_pa(skb); skb_put(skb, MWIFIEX_SIZE_OF_CMD_BUFFER); card->cmd_buf = skb; return 0; } /* * This function deletes a buffer for CMDRSP */ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) { struct pcie_service_card *card; if (!adapter) return 0; card = adapter->card; if (card && card->cmdrsp_buf) dev_kfree_skb_any(card->cmdrsp_buf); if (card && card->cmd_buf) dev_kfree_skb_any(card->cmd_buf); return 0; } /* * This function allocates a buffer for sleep cookie */ static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) { struct sk_buff *skb; struct pcie_service_card *card = adapter->card; /* Allocate memory for sleep cookie */ skb = dev_alloc_skb(sizeof(u32)); if (!skb) { dev_err(adapter->dev, "Unable to allocate skb for sleep cookie!\n"); return -ENOMEM; } mwifiex_update_sk_buff_pa(skb); skb_put(skb, sizeof(u32)); /* Init val of Sleep Cookie */ *(u32 *)skb->data = FW_AWAKE_COOKIE; dev_dbg(adapter->dev, "alloc_scook: sleep cookie=0x%x\n", *((u32 *)skb->data)); /* Save the sleep cookie */ card->sleep_cookie = skb; return 0; } /* * This function deletes buffer for sleep cookie */ static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) { struct pcie_service_card *card; if (!adapter) return 0; card = adapter->card; if (card && card->sleep_cookie) { dev_kfree_skb_any(card->sleep_cookie); card->sleep_cookie = NULL; } return 0; } /* * This function sends data buffer to device */ static int mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; u32 wrindx, rdptr; phys_addr_t *buf_pa; __le16 *tmp; if (!mwifiex_pcie_ok_to_access_hw(adapter)) mwifiex_pm_wakeup_card(adapter); /* Read the TX ring read pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_TXBD_RDPTR, &rdptr)) { dev_err(adapter->dev, "SEND DATA: failed to read REG_TXBD_RDPTR\n"); return -1; } wrindx = card->txbd_wrptr & MWIFIEX_TXBD_MASK; dev_dbg(adapter->dev, "info: SEND DATA: \n", rdptr, card->txbd_wrptr); if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) != (rdptr & MWIFIEX_TXBD_MASK)) || ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) != (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) { struct sk_buff *skb_data; u8 *payload; adapter->data_sent = true; skb_data = card->tx_buf_list[wrindx]; memcpy(skb_data->data, skb->data, skb->len); payload = skb_data->data; tmp = (__le16 *)&payload[0]; *tmp = cpu_to_le16((u16)skb->len); tmp = (__le16 *)&payload[2]; *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); skb_put(skb_data, MWIFIEX_RX_DATA_BUF_SIZE - skb_data->len); skb_trim(skb_data, skb->len); buf_pa = MWIFIEX_SKB_PACB(skb_data); card->txbd_ring[wrindx]->paddr = *buf_pa; card->txbd_ring[wrindx]->len = (u16)skb_data->len; card->txbd_ring[wrindx]->flags = MWIFIEX_BD_FLAG_FIRST_DESC | MWIFIEX_BD_FLAG_LAST_DESC; if ((++card->txbd_wrptr & MWIFIEX_TXBD_MASK) == MWIFIEX_MAX_TXRX_BD) card->txbd_wrptr = ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) ^ MWIFIEX_BD_FLAG_ROLLOVER_IND); /* Write the TX ring write pointer in to REG_TXBD_WRPTR */ if (mwifiex_write_reg(adapter, REG_TXBD_WRPTR, card->txbd_wrptr)) { dev_err(adapter->dev, "SEND DATA: failed to write REG_TXBD_WRPTR\n"); return 0; } /* Send the TX ready interrupt */ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_DNLD_RDY)) { dev_err(adapter->dev, "SEND DATA: failed to assert door-bell intr\n"); return -1; } dev_dbg(adapter->dev, "info: SEND DATA: Updated and sent packet to firmware successfully\n", rdptr, card->txbd_wrptr); } else { dev_dbg(adapter->dev, "info: TX Ring full, can't send packets to fw\n"); adapter->data_sent = true; /* Send the TX ready interrupt */ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_DNLD_RDY)) dev_err(adapter->dev, "SEND DATA: failed to assert door-bell intr\n"); return -EBUSY; } return 0; } /* * This function handles received buffer ring and * dispatches packets to upper */ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; u32 wrptr, rd_index; int ret = 0; struct sk_buff *skb_tmp = NULL; /* Read the RX ring Write pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_RXBD_WRPTR, &wrptr)) { dev_err(adapter->dev, "RECV DATA: failed to read REG_TXBD_RDPTR\n"); ret = -1; goto done; } while (((wrptr & MWIFIEX_RXBD_MASK) != (card->rxbd_rdptr & MWIFIEX_RXBD_MASK)) || ((wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) == (card->rxbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) { struct sk_buff *skb_data; u16 rx_len; rd_index = card->rxbd_rdptr & MWIFIEX_RXBD_MASK; skb_data = card->rx_buf_list[rd_index]; /* Get data length from interface header - first byte is len, second byte is type */ rx_len = *((u16 *)skb_data->data); dev_dbg(adapter->dev, "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", card->rxbd_rdptr, wrptr, rx_len); skb_tmp = dev_alloc_skb(rx_len); if (!skb_tmp) { dev_dbg(adapter->dev, "info: Failed to alloc skb for RX\n"); ret = -EBUSY; goto done; } skb_put(skb_tmp, rx_len); memcpy(skb_tmp->data, skb_data->data + INTF_HEADER_LEN, rx_len); if ((++card->rxbd_rdptr & MWIFIEX_RXBD_MASK) == MWIFIEX_MAX_TXRX_BD) { card->rxbd_rdptr = ((card->rxbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) ^ MWIFIEX_BD_FLAG_ROLLOVER_IND); } dev_dbg(adapter->dev, "info: RECV DATA: \n", card->rxbd_rdptr, wrptr); /* Write the RX ring read pointer in to REG_RXBD_RDPTR */ if (mwifiex_write_reg(adapter, REG_RXBD_RDPTR, card->rxbd_rdptr)) { dev_err(adapter->dev, "RECV DATA: failed to write REG_RXBD_RDPTR\n"); ret = -1; goto done; } /* Read the RX ring Write pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_RXBD_WRPTR, &wrptr)) { dev_err(adapter->dev, "RECV DATA: failed to read REG_TXBD_RDPTR\n"); ret = -1; goto done; } dev_dbg(adapter->dev, "info: RECV DATA: Rcvd packet from fw successfully\n"); mwifiex_handle_rx_packet(adapter, skb_tmp); } done: if (ret && skb_tmp) dev_kfree_skb_any(skb_tmp); return ret; } /* * This function downloads the boot command to device */ static int mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) { phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb); if (!(skb->data && skb->len && *buf_pa)) { dev_err(adapter->dev, "Invalid parameter in %s <%p, %#x:%x, %x>\n", __func__, skb->data, skb->len, (u32)*buf_pa, (u32)((u64)*buf_pa >> 32)); return -1; } /* Write the lower 32bits of the physical address to scratch * register 0 */ if (mwifiex_write_reg(adapter, PCIE_SCRATCH_0_REG, (u32)*buf_pa)) { dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); return -1; } /* Write the upper 32bits of the physical address to scratch * register 1 */ if (mwifiex_write_reg(adapter, PCIE_SCRATCH_1_REG, (u32)((u64)*buf_pa >> 32))) { dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); return -1; } /* Write the command length to scratch register 2 */ if (mwifiex_write_reg(adapter, PCIE_SCRATCH_2_REG, skb->len)) { dev_err(adapter->dev, "%s: failed to write command len to scratch reg 2\n", __func__); return -1; } /* Ring the door bell */ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_DOOR_BELL)) { dev_err(adapter->dev, "%s: failed to assert door-bell intr\n", __func__); return -1; } return 0; } /* * This function downloads commands to the device */ static int mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; int ret = 0; phys_addr_t *cmd_buf_pa; phys_addr_t *cmdrsp_buf_pa; if (!(skb->data && skb->len)) { dev_err(adapter->dev, "Invalid parameter in %s <%p, %#x>\n", __func__, skb->data, skb->len); return -1; } /* Make sure a command response buffer is available */ if (!card->cmdrsp_buf) { dev_err(adapter->dev, "No response buffer available, send command failed\n"); return -EBUSY; } /* Make sure a command buffer is available */ if (!card->cmd_buf) { dev_err(adapter->dev, "Command buffer not available\n"); return -EBUSY; } adapter->cmd_sent = true; /* Copy the given skb in to DMA accessable shared buffer */ skb_put(card->cmd_buf, MWIFIEX_SIZE_OF_CMD_BUFFER - card->cmd_buf->len); skb_trim(card->cmd_buf, skb->len); memcpy(card->cmd_buf->data, skb->data, skb->len); /* To send a command, the driver will: 1. Write the 64bit physical address of the data buffer to SCRATCH1 + SCRATCH0 2. Ring the door bell (i.e. set the door bell interrupt) In response to door bell interrupt, the firmware will perform the DMA of the command packet (first header to obtain the total length and then rest of the command). */ if (card->cmdrsp_buf) { cmdrsp_buf_pa = MWIFIEX_SKB_PACB(card->cmdrsp_buf); /* Write the lower 32bits of the cmdrsp buffer physical address */ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_LO, (u32)*cmdrsp_buf_pa)) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; goto done; } /* Write the upper 32bits of the cmdrsp buffer physical address */ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_HI, (u32)((u64)*cmdrsp_buf_pa >> 32))) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; goto done; } } cmd_buf_pa = MWIFIEX_SKB_PACB(card->cmd_buf); /* Write the lower 32bits of the physical address to REG_CMD_ADDR_LO */ if (mwifiex_write_reg(adapter, REG_CMD_ADDR_LO, (u32)*cmd_buf_pa)) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; goto done; } /* Write the upper 32bits of the physical address to REG_CMD_ADDR_HI */ if (mwifiex_write_reg(adapter, REG_CMD_ADDR_HI, (u32)((u64)*cmd_buf_pa >> 32))) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; goto done; } /* Write the command length to REG_CMD_SIZE */ if (mwifiex_write_reg(adapter, REG_CMD_SIZE, card->cmd_buf->len)) { dev_err(adapter->dev, "Failed to write cmd len to REG_CMD_SIZE\n"); ret = -1; goto done; } /* Ring the door bell */ if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_DOOR_BELL)) { dev_err(adapter->dev, "Failed to assert door-bell intr\n"); ret = -1; goto done; } done: if (ret) adapter->cmd_sent = false; return 0; } /* * This function handles command complete interrupt */ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; struct sk_buff *skb = card->cmdrsp_buf; int count = 0; dev_dbg(adapter->dev, "info: Rx CMD Response\n"); if (!adapter->curr_cmd) { skb_pull(skb, INTF_HEADER_LEN); if (adapter->ps_state == PS_STATE_SLEEP_CFM) { mwifiex_process_sleep_confirm_resp(adapter, skb->data, skb->len); while (mwifiex_pcie_ok_to_access_hw(adapter) && (count++ < 10)) usleep_range(50, 60); } else { dev_err(adapter->dev, "There is no command but got cmdrsp\n"); } memcpy(adapter->upld_buf, skb->data, min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); skb_push(skb, INTF_HEADER_LEN); } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { skb_pull(skb, INTF_HEADER_LEN); adapter->curr_cmd->resp_skb = skb; adapter->cmd_resp_received = true; /* Take the pointer and set it to CMD node and will return in the response complete callback */ card->cmdrsp_buf = NULL; /* Clear the cmd-rsp buffer address in scratch registers. This will prevent firmware from writing to the same response buffer again. */ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_LO, 0)) { dev_err(adapter->dev, "cmd_done: failed to clear cmd_rsp_addr_lo\n"); return -1; } /* Write the upper 32bits of the cmdrsp buffer physical address */ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_HI, 0)) { dev_err(adapter->dev, "cmd_done: failed to clear cmd_rsp_addr_hi\n"); return -1; } } return 0; } /* * Command Response processing complete handler */ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; if (skb) { card->cmdrsp_buf = skb; skb_push(card->cmdrsp_buf, INTF_HEADER_LEN); } return 0; } /* * This function handles firmware event ready interrupt */ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr, event; if (adapter->event_received) { dev_dbg(adapter->dev, "info: Event being processed, " "do not process this interrupt just yet\n"); return 0; } if (rdptr >= MWIFIEX_MAX_EVT_BD) { dev_dbg(adapter->dev, "info: Invalid read pointer...\n"); return -1; } /* Read the event ring write pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_EVTBD_WRPTR, &wrptr)) { dev_err(adapter->dev, "EventReady: failed to read REG_EVTBD_WRPTR\n"); return -1; } dev_dbg(adapter->dev, "info: EventReady: Initial ", card->evtbd_rdptr, wrptr); if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr & MWIFIEX_EVTBD_MASK)) || ((wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) == (card->evtbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) { struct sk_buff *skb_cmd; __le16 data_len = 0; u16 evt_len; dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr); skb_cmd = card->evt_buf_list[rdptr]; /* Take the pointer and set it to event pointer in adapter and will return back after event handling callback */ card->evt_buf_list[rdptr] = NULL; card->evtbd_ring[rdptr]->paddr = 0; card->evtbd_ring[rdptr]->len = 0; card->evtbd_ring[rdptr]->flags = 0; event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN]; adapter->event_cause = event; /* The first 4bytes will be the event transfer header len is 2 bytes followed by type which is 2 bytes */ memcpy(&data_len, skb_cmd->data, sizeof(__le16)); evt_len = le16_to_cpu(data_len); skb_pull(skb_cmd, INTF_HEADER_LEN); dev_dbg(adapter->dev, "info: Event length: %d\n", evt_len); if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE)) memcpy(adapter->event_body, skb_cmd->data + MWIFIEX_EVENT_HEADER_LEN, evt_len - MWIFIEX_EVENT_HEADER_LEN); adapter->event_received = true; adapter->event_skb = skb_cmd; /* Do not update the event read pointer here, wait till the buffer is released. This is just to make things simpler, we need to find a better method of managing these buffers. */ } return 0; } /* * Event processing complete handler */ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; int ret = 0; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr; phys_addr_t *buf_pa; if (!skb) return 0; if (rdptr >= MWIFIEX_MAX_EVT_BD) { dev_err(adapter->dev, "event_complete: Invalid rdptr 0x%x\n", rdptr); return -EINVAL; } /* Read the event ring write pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_EVTBD_WRPTR, &wrptr)) { dev_err(adapter->dev, "event_complete: failed to read REG_EVTBD_WRPTR\n"); return -1; } if (!card->evt_buf_list[rdptr]) { skb_push(skb, INTF_HEADER_LEN); card->evt_buf_list[rdptr] = skb; buf_pa = MWIFIEX_SKB_PACB(skb); card->evtbd_ring[rdptr]->paddr = *buf_pa; card->evtbd_ring[rdptr]->len = (u16)skb->len; card->evtbd_ring[rdptr]->flags = 0; skb = NULL; } else { dev_dbg(adapter->dev, "info: ERROR: buf still valid at index %d, <%p, %p>\n", rdptr, card->evt_buf_list[rdptr], skb); } if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) { card->evtbd_rdptr = ((card->evtbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) ^ MWIFIEX_BD_FLAG_ROLLOVER_IND); } dev_dbg(adapter->dev, "info: Updated ", card->evtbd_rdptr, wrptr); /* Write the event ring read pointer in to REG_EVTBD_RDPTR */ if (mwifiex_write_reg(adapter, REG_EVTBD_RDPTR, card->evtbd_rdptr)) { dev_err(adapter->dev, "event_complete: failed to read REG_EVTBD_RDPTR\n"); return -1; } dev_dbg(adapter->dev, "info: Check Events Again\n"); ret = mwifiex_pcie_process_event_ready(adapter); return ret; } /* * This function downloads the firmware to the card. * * Firmware is downloaded to the card in blocks. Every block download * is tested for CRC errors, and retried a number of times before * returning failure. */ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { int ret; u8 *firmware = fw->fw_buf; u32 firmware_len = fw->fw_len; u32 offset = 0; struct sk_buff *skb; u32 txlen, tx_blocks = 0, tries, len; u32 block_retry_cnt = 0; if (!adapter) { pr_err("adapter structure is not valid\n"); return -1; } if (!firmware || !firmware_len) { dev_err(adapter->dev, "No firmware image found! Terminating download\n"); return -1; } dev_dbg(adapter->dev, "info: Downloading FW image (%d bytes)\n", firmware_len); if (mwifiex_pcie_disable_host_int(adapter)) { dev_err(adapter->dev, "%s: Disabling interrupts failed.\n", __func__); return -1; } skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); if (!skb) { ret = -ENOMEM; goto done; } mwifiex_update_sk_buff_pa(skb); /* Perform firmware data transfer */ do { u32 ireg_intr = 0; /* More data? */ if (offset >= firmware_len) break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { ret = mwifiex_read_reg(adapter, PCIE_SCRATCH_2_REG, &len); if (ret) { dev_warn(adapter->dev, "Failed reading len from boot code\n"); goto done; } if (len) break; usleep_range(10, 20); } if (!len) { break; } else if (len > MWIFIEX_UPLD_SIZE) { pr_err("FW download failure @ %d, invalid length %d\n", offset, len); ret = -1; goto done; } txlen = len; if (len & BIT(0)) { block_retry_cnt++; if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) { pr_err("FW download failure @ %d, over max " "retry count\n", offset); ret = -1; goto done; } dev_err(adapter->dev, "FW CRC error indicated by the " "helper: len = 0x%04X, txlen = %d\n", len, txlen); len &= ~BIT(0); /* Setting this to 0 to resend from same offset */ txlen = 0; } else { block_retry_cnt = 0; /* Set blocksize to transfer - checking for last block */ if (firmware_len - offset < txlen) txlen = firmware_len - offset; dev_dbg(adapter->dev, "."); tx_blocks = (txlen + MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD - 1) / MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD; /* Copy payload to buffer */ memmove(skb->data, &firmware[offset], txlen); } skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); skb_trim(skb, tx_blocks * MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD); /* Send the boot command to device */ if (mwifiex_pcie_send_boot_cmd(adapter, skb)) { dev_err(adapter->dev, "Failed to send firmware download command\n"); ret = -1; goto done; } /* Wait for the command done interrupt */ do { if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, &ireg_intr)) { dev_err(adapter->dev, "%s: Failed to read " "interrupt status during fw dnld.\n", __func__); ret = -1; goto done; } } while ((ireg_intr & CPU_INTR_DOOR_BELL) == CPU_INTR_DOOR_BELL); offset += txlen; } while (true); dev_dbg(adapter->dev, "info:\nFW download over, size %d bytes\n", offset); ret = 0; done: dev_kfree_skb_any(skb); return ret; } /* * This function checks the firmware status in card. * * The winner interface is also determined by this function. */ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) { int ret = 0; u32 firmware_stat, winner_status; u32 tries; /* Mask spurios interrupts */ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK, HOST_INTR_MASK)) { dev_warn(adapter->dev, "Write register failed\n"); return -1; } dev_dbg(adapter->dev, "Setting driver ready signature\n"); if (mwifiex_write_reg(adapter, REG_DRV_READY, FIRMWARE_READY_PCIE)) { dev_err(adapter->dev, "Failed to write driver ready signature\n"); return -1; } /* Wait for firmware initialization event */ for (tries = 0; tries < poll_num; tries++) { if (mwifiex_read_reg(adapter, PCIE_SCRATCH_3_REG, &firmware_stat)) ret = -1; else ret = 0; if (ret) continue; if (firmware_stat == FIRMWARE_READY_PCIE) { ret = 0; break; } else { mdelay(100); ret = -1; } } if (ret) { if (mwifiex_read_reg(adapter, PCIE_SCRATCH_3_REG, &winner_status)) ret = -1; else if (!winner_status) { dev_err(adapter->dev, "PCI-E is the winner\n"); adapter->winner = 1; ret = -1; } else { dev_err(adapter->dev, "PCI-E is not the winner <%#x,%d>, exit dnld\n", ret, adapter->winner); ret = 0; } } return ret; } /* * This function reads the interrupt status from card. */ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) { u32 pcie_ireg; unsigned long flags; if (!mwifiex_pcie_ok_to_access_hw(adapter)) return; if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) { dev_warn(adapter->dev, "Read register failed\n"); return; } if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { mwifiex_pcie_disable_host_int(adapter); /* Clear the pending interrupts */ if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS, ~pcie_ireg)) { dev_warn(adapter->dev, "Write register failed\n"); return; } spin_lock_irqsave(&adapter->int_lock, flags); adapter->int_status |= pcie_ireg; spin_unlock_irqrestore(&adapter->int_lock, flags); if (pcie_ireg & HOST_INTR_CMD_DONE) { if ((adapter->ps_state == PS_STATE_SLEEP_CFM) || (adapter->ps_state == PS_STATE_SLEEP)) { mwifiex_pcie_enable_host_int(adapter); if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_SLEEP_CFM_DONE) ) { dev_warn(adapter->dev, "Write register failed\n"); return; } } } else if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) { /* Potentially for PCIe we could get other * interrupts like shared. Don't change power * state until cookie is set */ if (mwifiex_pcie_ok_to_access_hw(adapter)) adapter->ps_state = PS_STATE_AWAKE; } } } /* * Interrupt handler for PCIe root port * * This function reads the interrupt status from firmware and assigns * the main process in workqueue which will handle the interrupt. */ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) { struct pci_dev *pdev = (struct pci_dev *)context; struct pcie_service_card *card; struct mwifiex_adapter *adapter; if (!pdev) { pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev); goto exit; } card = (struct pcie_service_card *) pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, card ? card->adapter : NULL); goto exit; } adapter = card->adapter; if (adapter->surprise_removed) goto exit; mwifiex_interrupt_status(adapter); queue_work(adapter->workqueue, &adapter->main_work); exit: return IRQ_HANDLED; } /* * This function checks the current interrupt status. * * The following interrupts are checked and handled by this function - * - Data sent * - Command sent * - Command received * - Packets received * - Events received * * In case of Rx packets received, the packets are uploaded from card to * host and processed accordingly. */ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) { int ret; u32 pcie_ireg = 0; unsigned long flags; spin_lock_irqsave(&adapter->int_lock, flags); /* Clear out unused interrupts */ adapter->int_status &= HOST_INTR_MASK; spin_unlock_irqrestore(&adapter->int_lock, flags); while (adapter->int_status & HOST_INTR_MASK) { if (adapter->int_status & HOST_INTR_DNLD_DONE) { adapter->int_status &= ~HOST_INTR_DNLD_DONE; if (adapter->data_sent) { dev_dbg(adapter->dev, "info: DATA sent intr\n"); adapter->data_sent = false; } } if (adapter->int_status & HOST_INTR_UPLD_RDY) { adapter->int_status &= ~HOST_INTR_UPLD_RDY; dev_dbg(adapter->dev, "info: Rx DATA\n"); ret = mwifiex_pcie_process_recv_data(adapter); if (ret) return ret; } if (adapter->int_status & HOST_INTR_EVENT_RDY) { adapter->int_status &= ~HOST_INTR_EVENT_RDY; dev_dbg(adapter->dev, "info: Rx EVENT\n"); ret = mwifiex_pcie_process_event_ready(adapter); if (ret) return ret; } if (adapter->int_status & HOST_INTR_CMD_DONE) { adapter->int_status &= ~HOST_INTR_CMD_DONE; if (adapter->cmd_sent) { dev_dbg(adapter->dev, "info: CMD sent Interrupt\n"); adapter->cmd_sent = false; } /* Handle command response */ ret = mwifiex_pcie_process_cmd_complete(adapter); if (ret) return ret; } if (mwifiex_pcie_ok_to_access_hw(adapter)) { if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) { dev_warn(adapter->dev, "Read register failed\n"); return -1; } if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS, ~pcie_ireg)) { dev_warn(adapter->dev, "Write register failed\n"); return -1; } adapter->int_status |= pcie_ireg; adapter->int_status &= HOST_INTR_MASK; } } } dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", adapter->cmd_sent, adapter->data_sent); mwifiex_pcie_enable_host_int(adapter); return 0; } /* * This function downloads data from driver to card. * * Both commands and data packets are transferred to the card by this * function. * * This function adds the PCIE specific header to the front of the buffer * before transferring. The header contains the length of the packet and * the type. The firmware handles the packets based upon this set type. */ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { if (!skb) { dev_err(adapter->dev, "Passed NULL skb to %s\n", __func__); return -1; } if (type == MWIFIEX_TYPE_DATA) return mwifiex_pcie_send_data(adapter, skb); else if (type == MWIFIEX_TYPE_CMD) return mwifiex_pcie_send_cmd(adapter, skb); return 0; } /* * This function initializes the PCI-E host memory space, WCB rings, etc. * * The following initializations steps are followed - * - Allocate TXBD ring buffers * - Allocate RXBD ring buffers * - Allocate event BD ring buffers * - Allocate command response ring buffer * - Allocate sleep cookie buffer */ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; int ret; struct pci_dev *pdev = card->dev; pci_set_drvdata(pdev, card); ret = pci_enable_device(pdev); if (ret) goto err_enable_dev; pci_set_master(pdev); dev_dbg(adapter->dev, "try set_consistent_dma_mask(32)\n"); ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { dev_err(adapter->dev, "set_dma_mask(32) failed\n"); goto err_set_dma_mask; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { dev_err(adapter->dev, "set_consistent_dma_mask(64) failed\n"); goto err_set_dma_mask; } ret = pci_request_region(pdev, 0, DRV_NAME); if (ret) { dev_err(adapter->dev, "req_reg(0) error\n"); goto err_req_region0; } card->pci_mmap = pci_iomap(pdev, 0, 0); if (!card->pci_mmap) { dev_err(adapter->dev, "iomap(0) error\n"); goto err_iomap0; } ret = pci_request_region(pdev, 2, DRV_NAME); if (ret) { dev_err(adapter->dev, "req_reg(2) error\n"); goto err_req_region2; } card->pci_mmap1 = pci_iomap(pdev, 2, 0); if (!card->pci_mmap1) { dev_err(adapter->dev, "iomap(2) error\n"); goto err_iomap2; } dev_dbg(adapter->dev, "PCI memory map Virt0: %p PCI memory map Virt2: %p\n", card->pci_mmap, card->pci_mmap1); card->cmdrsp_buf = NULL; ret = mwifiex_pcie_create_txbd_ring(adapter); if (ret) goto err_cre_txbd; ret = mwifiex_pcie_create_rxbd_ring(adapter); if (ret) goto err_cre_rxbd; ret = mwifiex_pcie_create_evtbd_ring(adapter); if (ret) goto err_cre_evtbd; ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter); if (ret) goto err_alloc_cmdbuf; ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter); if (ret) goto err_alloc_cookie; return ret; err_alloc_cookie: mwifiex_pcie_delete_cmdrsp_buf(adapter); err_alloc_cmdbuf: mwifiex_pcie_delete_evtbd_ring(adapter); err_cre_evtbd: mwifiex_pcie_delete_rxbd_ring(adapter); err_cre_rxbd: mwifiex_pcie_delete_txbd_ring(adapter); err_cre_txbd: pci_iounmap(pdev, card->pci_mmap1); err_iomap2: pci_release_region(pdev, 2); err_req_region2: pci_iounmap(pdev, card->pci_mmap); err_iomap0: pci_release_region(pdev, 0); err_req_region0: err_set_dma_mask: pci_disable_device(pdev); err_enable_dev: pci_set_drvdata(pdev, NULL); return ret; } /* * This function cleans up the allocated card buffers. * * The following are freed by this function - * - TXBD ring buffers * - RXBD ring buffers * - Event BD ring buffers * - Command response ring buffer * - Sleep cookie buffer */ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; struct pci_dev *pdev = card->dev; mwifiex_pcie_delete_sleep_cookie_buf(adapter); mwifiex_pcie_delete_cmdrsp_buf(adapter); mwifiex_pcie_delete_evtbd_ring(adapter); mwifiex_pcie_delete_rxbd_ring(adapter); mwifiex_pcie_delete_txbd_ring(adapter); card->cmdrsp_buf = NULL; dev_dbg(adapter->dev, "Clearing driver ready signature\n"); if (user_rmmod) { if (mwifiex_write_reg(adapter, REG_DRV_READY, 0x00000000)) dev_err(adapter->dev, "Failed to write driver not-ready signature\n"); } if (pdev) { pci_iounmap(pdev, card->pci_mmap); pci_iounmap(pdev, card->pci_mmap1); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } } /* * This function registers the PCIE device. * * PCIE IRQ is claimed, block size is set and driver data is initialized. */ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) { int ret; struct pcie_service_card *card = adapter->card; struct pci_dev *pdev = card->dev; /* save adapter pointer in card */ card->adapter = adapter; ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, "MRVL_PCIE", pdev); if (ret) { pr_err("request_irq failed: ret=%d\n", ret); adapter->card = NULL; return -1; } adapter->dev = &pdev->dev; strcpy(adapter->fw_name, PCIE8766_DEFAULT_FW_NAME); return 0; } /* * This function unregisters the PCIE device. * * The PCIE IRQ is released, the function is disabled and driver * data is set to null. */ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; if (card) { dev_dbg(adapter->dev, "%s(): calling free_irq()\n", __func__); free_irq(card->dev->irq, card->dev); } } static struct mwifiex_if_ops pcie_ops = { .init_if = mwifiex_pcie_init, .cleanup_if = mwifiex_pcie_cleanup, .check_fw_status = mwifiex_check_fw_status, .prog_fw = mwifiex_prog_fw_w_helper, .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, .enable_int = mwifiex_pcie_enable_host_int, .process_int_status = mwifiex_process_int_status, .host_to_card = mwifiex_pcie_host_to_card, .wakeup = mwifiex_pm_wakeup_card, .wakeup_complete = mwifiex_pm_wakeup_card_complete, /* PCIE specific */ .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete, .event_complete = mwifiex_pcie_event_complete, .update_mp_end_port = NULL, .cleanup_mpa_buf = NULL, }; /* * This function initializes the PCIE driver module. * * This initiates the semaphore and registers the device with * PCIE bus. */ static int mwifiex_pcie_init_module(void) { int ret; pr_debug("Marvell 8766 PCIe Driver\n"); sema_init(&add_remove_card_sem, 1); /* Clear the flag in case user removes the card. */ user_rmmod = 0; ret = pci_register_driver(&mwifiex_pcie); if (ret) pr_err("Driver register failed!\n"); else pr_debug("info: Driver registered successfully!\n"); return ret; } /* * This function cleans up the PCIE driver. * * The following major steps are followed for cleanup - * - Resume the device if its suspended * - Disconnect the device if connected * - Shutdown the firmware * - Unregister the device from PCIE bus. */ static void mwifiex_pcie_cleanup_module(void) { if (!down_interruptible(&add_remove_card_sem)) up(&add_remove_card_sem); /* Set the flag as user is removing this module. */ user_rmmod = 1; pci_unregister_driver(&mwifiex_pcie); } module_init(mwifiex_pcie_init_module); module_exit(mwifiex_pcie_cleanup_module); MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION); MODULE_VERSION(PCIE_VERSION); MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE("mrvl/pcie8766_uapsta.bin"); compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/main.h0000644000175000017500000007376612026211315023251 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: major data structures and prototypes * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_MAIN_H_ #define _MWIFIEX_MAIN_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "pcie.h" extern const char driver_version[]; enum { MWIFIEX_ASYNC_CMD, MWIFIEX_SYNC_CMD }; #define MWIFIEX_MAX_AP 64 #define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) #define MWIFIEX_TIMER_10S 10000 #define MWIFIEX_TIMER_1S 1000 #define MAX_TX_PENDING 100 #define LOW_TX_PENDING 80 #define MWIFIEX_UPLD_SIZE (2312) #define MAX_EVENT_SIZE 1024 #define ARP_FILTER_MAX_BUF_SIZE 68 #define MWIFIEX_KEY_BUFFER_SIZE 16 #define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 #define MWIFIEX_MAX_REGION_CODE 7 #define DEFAULT_BCN_AVG_FACTOR 8 #define DEFAULT_DATA_AVG_FACTOR 8 #define FIRST_VALID_CHANNEL 0xff #define DEFAULT_AD_HOC_CHANNEL 6 #define DEFAULT_AD_HOC_CHANNEL_A 36 #define DEFAULT_BCN_MISS_TIMEOUT 5 #define MAX_SCAN_BEACON_BUFFER 8000 #define SCAN_BEACON_ENTRY_PAD 6 #define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110 #define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30 #define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30 #define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) #define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) #define MWIFIEX_MAX_SCAN_DELAY_CNT 50 #define MWIFIEX_MAX_EMPTY_TX_Q_CNT 10 #define MWIFIEX_SCAN_DELAY_MSEC 20 #define RSN_GTK_OUI_OFFSET 2 #define MWIFIEX_OUI_NOT_PRESENT 0 #define MWIFIEX_OUI_PRESENT 1 /* * Do not check for data_received for USB, as data_received * is handled in mwifiex_usb_recv for USB */ #define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ adapter->event_received || \ ((adapter->iface_type != MWIFIEX_USB) && \ adapter->data_received) || \ ((adapter->iface_type == MWIFIEX_USB) && \ !skb_queue_empty(&adapter->usb_rx_data_q))) #define MWIFIEX_TYPE_CMD 1 #define MWIFIEX_TYPE_DATA 0 #define MWIFIEX_TYPE_EVENT 3 #define DBG_CMD_NUM 5 #define MAX_BITMAP_RATES_SIZE 10 #define MAX_CHANNEL_BAND_BG 14 #define MAX_CHANNEL_BAND_A 165 #define MAX_FREQUENCY_BAND_BG 2484 #define MWIFIEX_EVENT_HEADER_LEN 4 #define MWIFIEX_UAP_EVENT_EXTRA_HEADER 2 #define MWIFIEX_TYPE_LEN 4 #define MWIFIEX_USB_TYPE_CMD 0xF00DFACE #define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE #define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE struct mwifiex_dbg { u32 num_cmd_host_to_card_failure; u32 num_cmd_sleep_cfm_host_to_card_failure; u32 num_tx_host_to_card_failure; u32 num_event_deauth; u32 num_event_disassoc; u32 num_event_link_lost; u32 num_cmd_deauth; u32 num_cmd_assoc_success; u32 num_cmd_assoc_failure; u32 num_tx_timeout; u32 num_cmd_timeout; u16 timeout_cmd_id; u16 timeout_cmd_act; u16 last_cmd_id[DBG_CMD_NUM]; u16 last_cmd_act[DBG_CMD_NUM]; u16 last_cmd_index; u16 last_cmd_resp_id[DBG_CMD_NUM]; u16 last_cmd_resp_index; u16 last_event[DBG_CMD_NUM]; u16 last_event_index; }; enum MWIFIEX_HARDWARE_STATUS { MWIFIEX_HW_STATUS_READY, MWIFIEX_HW_STATUS_INITIALIZING, MWIFIEX_HW_STATUS_FW_READY, MWIFIEX_HW_STATUS_INIT_DONE, MWIFIEX_HW_STATUS_RESET, MWIFIEX_HW_STATUS_CLOSING, MWIFIEX_HW_STATUS_NOT_READY }; enum MWIFIEX_802_11_POWER_MODE { MWIFIEX_802_11_POWER_MODE_CAM, MWIFIEX_802_11_POWER_MODE_PSP }; struct mwifiex_tx_param { u32 next_pkt_len; }; enum MWIFIEX_PS_STATE { PS_STATE_AWAKE, PS_STATE_PRE_SLEEP, PS_STATE_SLEEP_CFM, PS_STATE_SLEEP }; enum mwifiex_iface_type { MWIFIEX_SDIO, MWIFIEX_PCIE, MWIFIEX_USB }; struct mwifiex_add_ba_param { u32 tx_win_size; u32 rx_win_size; u32 timeout; }; struct mwifiex_tx_aggr { u8 ampdu_user; u8 ampdu_ap; u8 amsdu; }; struct mwifiex_ra_list_tbl { struct list_head list; struct sk_buff_head skb_head; u8 ra[ETH_ALEN]; u32 total_pkts_size; u32 is_11n_enabled; u16 max_amsdu; u16 pkt_count; u8 ba_packet_thr; }; struct mwifiex_tid_tbl { struct list_head ra_list; /* spin lock for tid table */ spinlock_t tid_tbl_lock; struct mwifiex_ra_list_tbl *ra_list_curr; }; #define WMM_HIGHEST_PRIORITY 7 #define HIGH_PRIO_TID 7 #define LOW_PRIO_TID 0 #define NO_PKT_PRIO_TID (-1) struct mwifiex_wmm_desc { struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; u32 packets_out[MAX_NUM_TID]; /* spin lock to protect ra_list */ spinlock_t ra_list_spinlock; struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS]; enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS]; u32 drv_pkt_delay_max; u8 queue_priority[IEEE80211_NUM_ACS]; u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ /* Number of transmit packets queued */ atomic_t tx_pkts_queued; /* Tracks highest priority with a packet queued */ atomic_t highest_queued_prio; }; struct mwifiex_802_11_security { u8 wpa_enabled; u8 wpa2_enabled; u8 wapi_enabled; u8 wapi_key_on; u8 wep_enabled; u32 authentication_mode; u8 is_authtype_auto; u32 encryption_mode; }; struct ieee_types_header { u8 element_id; u8 len; } __packed; struct ieee_types_vendor_specific { struct ieee_types_vendor_header vend_hdr; u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; } __packed; struct ieee_types_generic { struct ieee_types_header ieee_hdr; u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; } __packed; struct mwifiex_bssdescriptor { u8 mac_address[ETH_ALEN]; struct cfg80211_ssid ssid; u32 privacy; s32 rssi; u32 channel; u32 freq; u16 beacon_period; u8 erp_flags; u32 bss_mode; u8 supported_rates[MWIFIEX_SUPPORTED_RATES]; u8 data_rates[MWIFIEX_SUPPORTED_RATES]; /* Network band. * BAND_B(0x01): 'b' band * BAND_G(0x02): 'g' band * BAND_A(0X04): 'a' band */ u16 bss_band; u64 fw_tsf; u64 timestamp; union ieee_types_phy_param_set phy_param_set; union ieee_types_ss_param_set ss_param_set; u16 cap_info_bitmap; struct ieee_types_wmm_parameter wmm_ie; u8 disable_11n; struct ieee80211_ht_cap *bcn_ht_cap; u16 ht_cap_offset; struct ieee80211_ht_operation *bcn_ht_oper; u16 ht_info_offset; u8 *bcn_bss_co_2040; u16 bss_co_2040_offset; u8 *bcn_ext_cap; u16 ext_cap_offset; struct ieee_types_vendor_specific *bcn_wpa_ie; u16 wpa_offset; struct ieee_types_generic *bcn_rsn_ie; u16 rsn_offset; struct ieee_types_generic *bcn_wapi_ie; u16 wapi_offset; u8 *beacon_buf; u32 beacon_buf_size; }; struct mwifiex_current_bss_params { struct mwifiex_bssdescriptor bss_descriptor; u8 wmm_enabled; u8 wmm_uapsd_enabled; u8 band; u32 num_of_rates; u8 data_rates[MWIFIEX_SUPPORTED_RATES]; }; struct mwifiex_sleep_params { u16 sp_error; u16 sp_offset; u16 sp_stable_time; u8 sp_cal_control; u8 sp_ext_sleep_clk; u16 sp_reserved; }; struct mwifiex_sleep_period { u16 period; u16 reserved; }; struct mwifiex_wep_key { u32 length; u32 key_index; u32 key_length; u8 key_material[MWIFIEX_KEY_BUFFER_SIZE]; }; #define MAX_REGION_CHANNEL_NUM 2 struct mwifiex_chan_freq_power { u16 channel; u32 freq; u16 max_tx_power; u8 unsupported; }; enum state_11d_t { DISABLE_11D = 0, ENABLE_11D = 1, }; #define MWIFIEX_MAX_TRIPLET_802_11D 83 struct mwifiex_802_11d_domain_reg { u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; u8 no_of_triplet; struct ieee80211_country_ie_triplet triplet[MWIFIEX_MAX_TRIPLET_802_11D]; }; struct mwifiex_vendor_spec_cfg_ie { u16 mask; u16 flag; u8 ie[MWIFIEX_MAX_VSIE_LEN]; }; struct wps { u8 session_enable; }; struct mwifiex_adapter; struct mwifiex_private; struct mwifiex_private { struct mwifiex_adapter *adapter; u8 bss_type; u8 bss_role; u8 bss_priority; u8 bss_num; u8 bss_started; u8 frame_type; u8 curr_addr[ETH_ALEN]; u8 media_connected; u32 num_tx_timeout; struct net_device *netdev; struct net_device_stats stats; u16 curr_pkt_filter; u32 bss_mode; u32 pkt_tx_ctrl; u16 tx_power_level; u8 max_tx_power_level; u8 min_tx_power_level; u8 tx_rate; u8 tx_htinfo; u8 rxpd_htinfo; u8 rxpd_rate; u16 rate_bitmap; u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; u32 data_rate; u8 is_data_rate_auto; u16 bcn_avg_factor; u16 data_avg_factor; s16 data_rssi_last; s16 data_nf_last; s16 data_rssi_avg; s16 data_nf_avg; s16 bcn_rssi_last; s16 bcn_nf_last; s16 bcn_rssi_avg; s16 bcn_nf_avg; struct mwifiex_bssdescriptor *attempted_bss_desc; struct cfg80211_ssid prev_ssid; u8 prev_bssid[ETH_ALEN]; struct mwifiex_current_bss_params curr_bss_params; u16 beacon_period; u8 dtim_period; u16 listen_interval; u16 atim_window; u8 adhoc_channel; u8 adhoc_is_link_sensed; u8 adhoc_state; struct mwifiex_802_11_security sec_info; struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; u16 wep_key_curr_index; u8 wpa_ie[256]; u8 wpa_ie_len; u8 wpa_is_gtk_set; struct host_cmd_ds_802_11_key_material aes_key; u8 wapi_ie[256]; u8 wapi_ie_len; u8 *wps_ie; u8 wps_ie_len; u8 wmm_required; u8 wmm_enabled; u8 wmm_qosinfo; struct mwifiex_wmm_desc wmm; struct list_head sta_list; /* spin lock for associated station list */ spinlock_t sta_list_spinlock; struct list_head tx_ba_stream_tbl_ptr; /* spin lock for tx_ba_stream_tbl_ptr queue */ spinlock_t tx_ba_stream_tbl_lock; struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; struct mwifiex_add_ba_param add_ba_param; u16 rx_seq[MAX_NUM_TID]; struct list_head rx_reorder_tbl_ptr; /* spin lock for rx_reorder_tbl_ptr queue */ spinlock_t rx_reorder_tbl_lock; /* spin lock for Rx packets */ spinlock_t rx_pkt_lock; #define MWIFIEX_ASSOC_RSP_BUF_SIZE 500 u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE]; u32 assoc_rsp_size; #define MWIFIEX_GENIE_BUF_SIZE 256 u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE]; u8 gen_ie_buf_len; struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM]; #define MWIFIEX_ASSOC_TLV_BUF_SIZE 256 u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE]; u8 assoc_tlv_buf_len; u8 *curr_bcn_buf; u32 curr_bcn_size; /* spin lock for beacon buffer */ spinlock_t curr_bcn_buf_lock; struct wireless_dev *wdev; struct mwifiex_chan_freq_power cfp; char version_str[128]; #ifdef CONFIG_DEBUG_FS struct dentry *dfs_dev_dir; #endif u8 nick_name[16]; u16 current_key_index; struct semaphore async_sem; u8 scan_pending_on_block; u8 report_scan_result; struct cfg80211_scan_request *scan_request; struct mwifiex_user_scan_cfg *user_scan_cfg; u8 cfg_bssid[6]; struct wps wps; u8 scan_block; s32 cqm_rssi_thold; u32 cqm_rssi_hyst; u8 subsc_evt_rssi_state; struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage; struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX]; u16 beacon_idx; u16 proberesp_idx; u16 assocresp_idx; u16 rsn_idx; struct timer_list scan_delay_timer; u8 ap_11n_enabled; }; enum mwifiex_ba_status { BA_SETUP_NONE = 0, BA_SETUP_INPROGRESS, BA_SETUP_COMPLETE }; struct mwifiex_tx_ba_stream_tbl { struct list_head list; int tid; u8 ra[ETH_ALEN]; enum mwifiex_ba_status ba_status; }; struct mwifiex_rx_reorder_tbl; struct reorder_tmr_cnxt { struct timer_list timer; struct mwifiex_rx_reorder_tbl *ptr; struct mwifiex_private *priv; }; struct mwifiex_rx_reorder_tbl { struct list_head list; int tid; u8 ta[ETH_ALEN]; int start_win; int win_size; void **rx_reorder_ptr; struct reorder_tmr_cnxt timer_context; }; struct mwifiex_bss_prio_node { struct list_head list; struct mwifiex_private *priv; }; struct mwifiex_bss_prio_tbl { struct list_head bss_prio_head; /* spin lock for bss priority */ spinlock_t bss_prio_lock; struct mwifiex_bss_prio_node *bss_prio_cur; }; struct cmd_ctrl_node { struct list_head list; struct mwifiex_private *priv; u32 cmd_oid; u32 cmd_flag; struct sk_buff *cmd_skb; struct sk_buff *resp_skb; void *data_buf; u32 wait_q_enabled; struct sk_buff *skb; u8 *condition; u8 cmd_wait_q_woken; }; struct mwifiex_bss_priv { u8 band; u64 fw_tsf; }; /* This is AP specific structure which stores information * about associated STA */ struct mwifiex_sta_node { struct list_head list; u8 mac_addr[ETH_ALEN]; u8 is_wmm_enabled; u8 is_11n_enabled; u8 ampdu_sta[MAX_NUM_TID]; u16 rx_seq[MAX_NUM_TID]; u16 max_amsdu; }; struct mwifiex_if_ops { int (*init_if) (struct mwifiex_adapter *); void (*cleanup_if) (struct mwifiex_adapter *); int (*check_fw_status) (struct mwifiex_adapter *, u32); int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); int (*register_dev) (struct mwifiex_adapter *); void (*unregister_dev) (struct mwifiex_adapter *); int (*enable_int) (struct mwifiex_adapter *); int (*process_int_status) (struct mwifiex_adapter *); int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *, struct mwifiex_tx_param *); int (*wakeup) (struct mwifiex_adapter *); int (*wakeup_complete) (struct mwifiex_adapter *); /* Interface specific functions */ void (*update_mp_end_port) (struct mwifiex_adapter *, u16); void (*cleanup_mpa_buf) (struct mwifiex_adapter *); int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*data_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); }; struct mwifiex_adapter { u8 iface_type; struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; u8 priv_num; const struct firmware *firmware; char fw_name[32]; int winner; struct device *dev; struct wiphy *wiphy; bool surprise_removed; u32 fw_release_number; u16 init_wait_q_woken; wait_queue_head_t init_wait_q; void *card; struct mwifiex_if_ops if_ops; atomic_t rx_pending; atomic_t tx_pending; atomic_t cmd_pending; struct workqueue_struct *workqueue; struct work_struct main_work; struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; /* spin lock for init/shutdown */ spinlock_t mwifiex_lock; /* spin lock for main process */ spinlock_t main_proc_lock; u32 mwifiex_processing; u16 max_tx_buf_size; u16 tx_buf_size; u16 curr_tx_buf_size; u32 ioport; enum MWIFIEX_HARDWARE_STATUS hw_status; u16 number_of_antenna; u32 fw_cap_info; /* spin lock for interrupt handling */ spinlock_t int_lock; u8 int_status; u32 event_cause; struct sk_buff *event_skb; u8 upld_buf[MWIFIEX_UPLD_SIZE]; u8 data_sent; u8 cmd_sent; u8 cmd_resp_received; u8 event_received; u8 data_received; u16 seq_num; struct cmd_ctrl_node *cmd_pool; struct cmd_ctrl_node *curr_cmd; /* spin lock for command */ spinlock_t mwifiex_cmd_lock; u32 num_cmd_timeout; u16 last_init_cmd; struct timer_list cmd_timer; struct list_head cmd_free_q; /* spin lock for cmd_free_q */ spinlock_t cmd_free_q_lock; struct list_head cmd_pending_q; /* spin lock for cmd_pending_q */ spinlock_t cmd_pending_q_lock; struct list_head scan_pending_q; /* spin lock for scan_pending_q */ spinlock_t scan_pending_q_lock; struct sk_buff_head usb_rx_data_q; u32 scan_processing; u16 region_code; struct mwifiex_802_11d_domain_reg domain_reg; u16 scan_probes; u32 scan_mode; u16 specific_scan_time; u16 active_scan_time; u16 passive_scan_time; u8 fw_bands; u8 adhoc_start_band; u8 config_bands; struct mwifiex_chan_scan_param_set *scan_channels; u8 tx_lock_flag; struct mwifiex_sleep_params sleep_params; struct mwifiex_sleep_period sleep_period; u16 ps_mode; u32 ps_state; u8 need_to_wakeup; u16 multiple_dtim; u16 local_listen_interval; u16 null_pkt_interval; struct sk_buff *sleep_cfm; u16 bcn_miss_time_out; u16 adhoc_awake_period; u8 is_deep_sleep; u8 delay_null_pkt; u16 delay_to_ps; u16 enhanced_ps_mode; u8 pm_wakeup_card_req; u16 gen_null_pkt; u16 pps_uapsd_mode; u32 pm_wakeup_fw_try; u8 is_hs_configured; struct mwifiex_hs_config_param hs_cfg; u8 hs_activated; u16 hs_activate_wait_q_woken; wait_queue_head_t hs_activate_wait_q; bool is_suspended; u8 event_body[MAX_EVENT_SIZE]; u32 hw_dot_11n_dev_cap; u8 hw_dev_mcs_support; u8 adhoc_11n_enabled; u8 sec_chan_offset; struct mwifiex_dbg dbg; u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; u32 arp_filter_size; u16 cmd_wait_q_required; struct mwifiex_wait_queue cmd_wait_q; u8 scan_wait_q_woken; struct cmd_ctrl_node *cmd_queued; spinlock_t queue_lock; /* lock for tx queues */ struct completion fw_load; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; u16 max_mgmt_ie_index; u8 scan_delay_cnt; u8 empty_tx_q_cnt; atomic_t is_tx_received; atomic_t pending_bridged_pkts; }; int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); void mwifiex_set_trans_start(struct net_device *dev); void mwifiex_stop_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter); void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter); int mwifiex_init_fw(struct mwifiex_adapter *adapter); int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter); int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); int mwifiex_recv_packet(struct mwifiex_adapter *, struct sk_buff *skb); int mwifiex_process_event(struct mwifiex_adapter *adapter); int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node); int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, u16 cmd_action, u32 cmd_oid, void *data_buf); int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no, u16 cmd_action, u32 cmd_oid, void *data_buf); void mwifiex_cmd_timeout_func(unsigned long function_context); int mwifiex_get_debug_info(struct mwifiex_private *, struct mwifiex_debug_info *); int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node); void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node, u32 addtail); int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb); int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, struct mwifiex_tx_param *tx_param); int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb, int status); void mwifiex_clean_txrx(struct mwifiex_private *priv); u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *, u32); int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, uint16_t ps_bitmap, struct mwifiex_ds_auto_ds *auto_ds); int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, struct host_cmd_ds_command *resp, struct mwifiex_ds_pm_cfg *pm_cfg); void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); void mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated); int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_process_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb); int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no, u16 cmd_action, u32 cmd_oid, void *data_buf, void *cmd_buf); int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, u16 cmd_action, u32 cmd_oid, void *data_buf, void *cmd_buf); int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no, struct host_cmd_ds_command *resp); int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *, struct sk_buff *skb); int mwifiex_process_uap_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb); int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, struct sk_buff *skb); int mwifiex_process_sta_event(struct mwifiex_private *); int mwifiex_process_uap_event(struct mwifiex_private *); struct mwifiex_sta_node * mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); void mwifiex_delete_all_station_list(struct mwifiex_private *priv); void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta); int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, struct mwifiex_scan_cmd_config *scan_cfg); void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, struct cmd_ctrl_node *cmd_node); int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2); int mwifiex_associate(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); void mwifiex_reset_connect_state(struct mwifiex_private *priv); u8 mwifiex_band_to_radio_type(u8 band); int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); int mwifiex_adhoc_start(struct mwifiex_private *priv, struct cfg80211_ssid *adhoc_ssid); int mwifiex_adhoc_join(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct cfg80211_ssid *req_ssid); int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd); struct mwifiex_chan_freq_power *mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq); u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info); u32 mwifiex_find_freq_from_band_chan(u8, u8); int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, u8 **buffer); u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates); u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; void mwifiex_save_curr_bcn(struct mwifiex_private *priv); void mwifiex_free_curr_bcn(struct mwifiex_private *priv); int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd); int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int is_command_pending(struct mwifiex_adapter *adapter); void mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev); int mwifiex_set_secure_params(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_config, struct cfg80211_ap_settings *params); void mwifiex_set_ht_params(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_cfg, struct cfg80211_ap_settings *params); void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, struct cfg80211_ap_settings *params); /* * This function checks if the queuing is RA based or not. */ static inline u8 mwifiex_queuing_ra_based(struct mwifiex_private *priv) { /* * Currently we assume if we are in Infra, then DA=RA. This might not be * true in the future */ if ((priv->bss_mode == NL80211_IFTYPE_STATION) && (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)) return false; return true; } /* * This function copies rates. */ static inline u32 mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) { int i; for (i = 0; i < len && src[i]; i++, pos++) { if (pos >= MWIFIEX_SUPPORTED_RATES) break; dest[pos] = src[i]; } return pos; } /* * This function returns the correct private structure pointer based * upon the BSS type and BSS number. */ static inline struct mwifiex_private * mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter, u8 bss_num, u8 bss_type) { int i; for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { if ((adapter->priv[i]->bss_num == bss_num) && (adapter->priv[i]->bss_type == bss_type)) break; } } return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); } /* * This function returns the first available private structure pointer * based upon the BSS role. */ static inline struct mwifiex_private * mwifiex_get_priv(struct mwifiex_adapter *adapter, enum mwifiex_bss_role bss_role) { int i; for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { if (bss_role == MWIFIEX_BSS_ROLE_ANY || GET_BSS_ROLE(adapter->priv[i]) == bss_role) break; } } return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); } /* * This function returns the driver private structure of a network device. */ static inline struct mwifiex_private * mwifiex_netdev_get_priv(struct net_device *dev) { return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); } int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, int maxlen); int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, struct mwifiex_multicast_list *mcast_list); int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, struct net_device *dev); int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter); int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct cfg80211_ssid *req_ssid); int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type); int mwifiex_enable_hs(struct mwifiex_adapter *adapter); int mwifiex_disable_auto_ds(struct mwifiex_private *priv); int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate); int mwifiex_request_scan(struct mwifiex_private *priv, struct cfg80211_ssid *req_ssid); int mwifiex_scan_networks(struct mwifiex_private *priv, const struct mwifiex_user_scan_cfg *user_scan_in); int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, const u8 *key, int key_len, u8 key_index, const u8 *mac_addr, int disable); int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); int mwifiex_get_ver_ext(struct mwifiex_private *priv); int mwifiex_get_stats_info(struct mwifiex_private *priv, struct mwifiex_ds_get_stats *log); int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, u32 reg_offset, u32 reg_value); int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, u32 reg_offset, u32 *value); int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, u8 *value); int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data); int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data); int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index); int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index); int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode); int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, int max_len); int mwifiex_set_tx_power(struct mwifiex_private *priv, struct mwifiex_power_cfg *power_cfg); int mwifiex_main_process(struct mwifiex_adapter *); int mwifiex_get_bss_info(struct mwifiex_private *, struct mwifiex_bss_info *); int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, struct mwifiex_bssdescriptor *bss_entry); int mwifiex_check_network_compatibility(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params); int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config); int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, struct cfg80211_beacon_data *data); int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); u8 *mwifiex_11d_code_2_region(u8 code); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); void mwifiex_debugfs_remove(void); void mwifiex_dev_debugfs_init(struct mwifiex_private *priv); void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv); #endif #endif /* !_MWIFIEX_MAIN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/join.c0000644000175000017500000013027412026211315023243 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: association and ad-hoc start/join * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" #define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9))) /* * Append a generic IE as a pass through TLV to a TLV buffer. * * This function is called from the network join command preparation routine. * * If the IE buffer has been setup by the application, this routine appends * the buffer as a pass through TLV type to the request. */ static int mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer) { int ret_len = 0; struct mwifiex_ie_types_header ie_header; /* Null Checks */ if (!buffer) return 0; if (!(*buffer)) return 0; /* * If there is a generic ie buffer setup, append it to the return * parameter buffer pointer. */ if (priv->gen_ie_buf_len) { dev_dbg(priv->adapter->dev, "info: %s: append generic ie len %d to %p\n", __func__, priv->gen_ie_buf_len, *buffer); /* Wrap the generic IE buffer with a pass through TLV type */ ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); ie_header.len = cpu_to_le16(priv->gen_ie_buf_len); memcpy(*buffer, &ie_header, sizeof(ie_header)); /* Increment the return size and the return buffer pointer param */ *buffer += sizeof(ie_header); ret_len += sizeof(ie_header); /* Copy the generic IE buffer to the output buffer, advance pointer */ memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len); /* Increment the return size and the return buffer pointer param */ *buffer += priv->gen_ie_buf_len; ret_len += priv->gen_ie_buf_len; /* Reset the generic IE buffer */ priv->gen_ie_buf_len = 0; } /* return the length appended to the buffer */ return ret_len; } /* * Append TSF tracking info from the scan table for the target AP. * * This function is called from the network join command preparation routine. * * The TSF table TSF sent to the firmware contains two TSF values: * - The TSF of the target AP from its previous beacon/probe response * - The TSF timestamp of our local MAC at the time we observed the * beacon/probe response. * * The firmware uses the timestamp values to set an initial TSF value * in the MAC for the new association after a reassociation attempt. */ static int mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer, struct mwifiex_bssdescriptor *bss_desc) { struct mwifiex_ie_types_tsf_timestamp tsf_tlv; __le64 tsf_val; /* Null Checks */ if (buffer == NULL) return 0; if (*buffer == NULL) return 0; memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp)); tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val)); memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); *buffer += sizeof(tsf_tlv.header); /* TSF at the time when beacon/probe_response was received */ tsf_val = cpu_to_le64(bss_desc->fw_tsf); memcpy(*buffer, &tsf_val, sizeof(tsf_val)); *buffer += sizeof(tsf_val); tsf_val = cpu_to_le64(bss_desc->timestamp); dev_dbg(priv->adapter->dev, "info: %s: TSF offset calc: %016llx - %016llx\n", __func__, bss_desc->timestamp, bss_desc->fw_tsf); memcpy(*buffer, &tsf_val, sizeof(tsf_val)); *buffer += sizeof(tsf_val); return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); } /* * This function finds out the common rates between rate1 and rate2. * * It will fill common rates in rate1 as output if found. * * NOTE: Setting the MSB of the basic rates needs to be taken * care of, either before or after calling this function. */ static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, u32 rate1_size, u8 *rate2, u32 rate2_size) { int ret; u8 *ptr = rate1, *tmp; u32 i, j; tmp = kmemdup(rate1, rate1_size, GFP_KERNEL); if (!tmp) { dev_err(priv->adapter->dev, "failed to alloc tmp buf\n"); return -ENOMEM; } memset(rate1, 0, rate1_size); for (i = 0; rate2[i] && i < rate2_size; i++) { for (j = 0; tmp[j] && j < rate1_size; j++) { /* Check common rate, excluding the bit for basic rate */ if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { *rate1++ = tmp[j]; break; } } } dev_dbg(priv->adapter->dev, "info: Tx data rate set to %#x\n", priv->data_rate); if (!priv->is_data_rate_auto) { while (*ptr) { if ((*ptr & 0x7f) == priv->data_rate) { ret = 0; goto done; } ptr++; } dev_err(priv->adapter->dev, "previously set fixed data rate %#x" " is not compatible with the network\n", priv->data_rate); ret = -1; goto done; } ret = 0; done: kfree(tmp); return ret; } /* * This function creates the intersection of the rates supported by a * target BSS and our adapter settings for use in an assoc/join command. */ static int mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc, u8 *out_rates, u32 *out_rates_size) { u8 card_rates[MWIFIEX_SUPPORTED_RATES]; u32 card_rates_size; /* Copy AP supported rates */ memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES); /* Get the STA supported rates */ card_rates_size = mwifiex_get_active_data_rates(priv, card_rates); /* Get the common rates between AP and STA supported rates */ if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES, card_rates, card_rates_size)) { *out_rates_size = 0; dev_err(priv->adapter->dev, "%s: cannot get common rates\n", __func__); return -1; } *out_rates_size = min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES); return 0; } /* * This function appends a WPS IE. It is called from the network join command * preparation routine. * * If the IE buffer has been setup by the application, this routine appends * the buffer as a WPS TLV type to the request. */ static int mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer) { int retLen = 0; struct mwifiex_ie_types_header ie_header; if (!buffer || !*buffer) return 0; /* * If there is a wps ie buffer setup, append it to the return * parameter buffer pointer. */ if (priv->wps_ie_len) { dev_dbg(priv->adapter->dev, "cmd: append wps ie %d to %p\n", priv->wps_ie_len, *buffer); /* Wrap the generic IE buffer with a pass through TLV type */ ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE); ie_header.len = cpu_to_le16(priv->wps_ie_len); memcpy(*buffer, &ie_header, sizeof(ie_header)); *buffer += sizeof(ie_header); retLen += sizeof(ie_header); memcpy(*buffer, priv->wps_ie, priv->wps_ie_len); *buffer += priv->wps_ie_len; retLen += priv->wps_ie_len; } kfree(priv->wps_ie); priv->wps_ie_len = 0; return retLen; } /* * This function appends a WAPI IE. * * This function is called from the network join command preparation routine. * * If the IE buffer has been setup by the application, this routine appends * the buffer as a WAPI TLV type to the request. */ static int mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer) { int retLen = 0; struct mwifiex_ie_types_header ie_header; /* Null Checks */ if (buffer == NULL) return 0; if (*buffer == NULL) return 0; /* * If there is a wapi ie buffer setup, append it to the return * parameter buffer pointer. */ if (priv->wapi_ie_len) { dev_dbg(priv->adapter->dev, "cmd: append wapi ie %d to %p\n", priv->wapi_ie_len, *buffer); /* Wrap the generic IE buffer with a pass through TLV type */ ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE); ie_header.len = cpu_to_le16(priv->wapi_ie_len); memcpy(*buffer, &ie_header, sizeof(ie_header)); /* Increment the return size and the return buffer pointer param */ *buffer += sizeof(ie_header); retLen += sizeof(ie_header); /* Copy the wapi IE buffer to the output buffer, advance pointer */ memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len); /* Increment the return size and the return buffer pointer param */ *buffer += priv->wapi_ie_len; retLen += priv->wapi_ie_len; } /* return the length appended to the buffer */ return retLen; } /* * This function appends rsn ie tlv for wpa/wpa2 security modes. * It is called from the network join command preparation routine. */ static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv, u8 **buffer) { struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv; int rsn_ie_len; if (!buffer || !(*buffer)) return 0; rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer); rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]); rsn_ie_tlv->header.type = cpu_to_le16( le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]); rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) & 0x00FF); if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], le16_to_cpu(rsn_ie_tlv->header.len)); else return -1; rsn_ie_len = sizeof(rsn_ie_tlv->header) + le16_to_cpu(rsn_ie_tlv->header.len); *buffer += rsn_ie_len; return rsn_ie_len; } /* * This function prepares command for association. * * This sets the following parameters - * - Peer MAC address * - Listen interval * - Beacon interval * - Capability information * * ...and the following TLVs, as required - * - SSID TLV * - PHY TLV * - SS TLV * - Rates TLV * - Authentication TLV * - Channel TLV * - WPA/WPA2 IE * - 11n TLV * - Vendor specific TLV * - WMM TLV * - WAPI IE * - Generic IE * - TSF TLV * * Preparation also includes - * - Setting command ID and proper size * - Ensuring correct endian-ness */ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct mwifiex_bssdescriptor *bss_desc) { struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; struct mwifiex_ie_types_ssid_param_set *ssid_tlv; struct mwifiex_ie_types_phy_param_set *phy_tlv; struct mwifiex_ie_types_ss_param_set *ss_tlv; struct mwifiex_ie_types_rates_param_set *rates_tlv; struct mwifiex_ie_types_auth_type *auth_tlv; struct mwifiex_ie_types_chan_list_param_set *chan_tlv; u8 rates[MWIFIEX_SUPPORTED_RATES]; u32 rates_size; u16 tmp_cap; u8 *pos; int rsn_ie_len = 0; pos = (u8 *) assoc; mwifiex_cfg_tx_buf(priv, bss_desc); cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); /* Save so we know which BSS Desc to use in the response handler */ priv->attempted_bss_desc = bss_desc; memcpy(assoc->peer_sta_addr, bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); pos += sizeof(assoc->peer_sta_addr); /* Set the listen interval */ assoc->listen_interval = cpu_to_le16(priv->listen_interval); /* Set the beacon period */ assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); pos += sizeof(assoc->cap_info_bitmap); pos += sizeof(assoc->listen_interval); pos += sizeof(assoc->beacon_period); pos += sizeof(assoc->dtim_period); ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos; ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len); memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, le16_to_cpu(ssid_tlv->header.len)); pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); memcpy(&phy_tlv->fh_ds.ds_param_set, &bss_desc->phy_param_set.ds_param_set.current_chan, sizeof(phy_tlv->fh_ds.ds_param_set)); pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); /* Get the common rates supported between the driver and the BSS Desc */ if (mwifiex_setup_rates_from_bssdesc (priv, bss_desc, rates, &rates_size)) return -1; /* Save the data rates into Current BSS state structure */ priv->curr_bss_params.num_of_rates = rates_size; memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); /* Setup the Rates TLV in the association command */ rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos; rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); rates_tlv->header.len = cpu_to_le16((u16) rates_size); memcpy(rates_tlv->rates, rates, rates_size); pos += sizeof(rates_tlv->header) + rates_size; dev_dbg(priv->adapter->dev, "info: ASSOC_CMD: rates size = %d\n", rates_size); /* Add the Authentication type to be used for Auth frames */ auth_tlv = (struct mwifiex_ie_types_auth_type *) pos; auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type)); if (priv->sec_info.wep_enabled) auth_tlv->auth_type = cpu_to_le16( (u16) priv->sec_info.authentication_mode); else auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); if (IS_SUPPORT_MULTI_BANDS(priv->adapter) && !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && (!bss_desc->disable_11n) && (priv->adapter->config_bands & BAND_GN || priv->adapter->config_bands & BAND_AN) && (bss_desc->bcn_ht_cap) ) ) { /* Append a channel TLV for the channel the attempted AP was found on */ chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); chan_tlv->header.len = cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); memset(chan_tlv->chan_scan_param, 0x00, sizeof(struct mwifiex_chan_scan_param_set)); chan_tlv->chan_scan_param[0].chan_number = (bss_desc->phy_param_set.ds_param_set.current_chan); dev_dbg(priv->adapter->dev, "info: Assoc: TLV Chan = %d\n", chan_tlv->chan_scan_param[0].chan_number); chan_tlv->chan_scan_param[0].radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); dev_dbg(priv->adapter->dev, "info: Assoc: TLV Band = %d\n", chan_tlv->chan_scan_param[0].radio_type); pos += sizeof(chan_tlv->header) + sizeof(struct mwifiex_chan_scan_param_set); } if (!priv->wps.session_enable) { if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); if (rsn_ie_len == -1) return -1; } if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && (!bss_desc->disable_11n) && (priv->adapter->config_bands & BAND_GN || priv->adapter->config_bands & BAND_AN)) mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); /* Append vendor specific IE TLV */ mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos); mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, bss_desc->bcn_ht_cap); if (priv->sec_info.wapi_enabled && priv->wapi_ie_len) mwifiex_cmd_append_wapi_ie(priv, &pos); if (priv->wps.session_enable && priv->wps_ie_len) mwifiex_cmd_append_wps_ie(priv, &pos); mwifiex_cmd_append_generic_ie(priv, &pos); mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); /* Set the Capability info at last */ tmp_cap = bss_desc->cap_info_bitmap; if (priv->adapter->config_bands == BAND_B) tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; tmp_cap &= CAPINFO_MASK; dev_dbg(priv->adapter->dev, "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", tmp_cap, CAPINFO_MASK); assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); return 0; } /* * Association firmware command response handler * * The response buffer for the association command has the following * memory layout. * * For cases where an association response was not received (indicated * by the CapInfo and AId field): * * .------------------------------------------------------------. * | Header(4 * sizeof(t_u16)): Standard command response hdr | * .------------------------------------------------------------. * | cap_info/Error Return(t_u16): | * | 0xFFFF(-1): Internal error | * | 0xFFFE(-2): Authentication unhandled message | * | 0xFFFD(-3): Authentication refused | * | 0xFFFC(-4): Timeout waiting for AP response | * .------------------------------------------------------------. * | status_code(t_u16): | * | If cap_info is -1: | * | An internal firmware failure prevented the | * | command from being processed. The status_code | * | will be set to 1. | * | | * | If cap_info is -2: | * | An authentication frame was received but was | * | not handled by the firmware. IEEE Status | * | code for the failure is returned. | * | | * | If cap_info is -3: | * | An authentication frame was received and the | * | status_code is the IEEE Status reported in the | * | response. | * | | * | If cap_info is -4: | * | (1) Association response timeout | * | (2) Authentication response timeout | * .------------------------------------------------------------. * | a_id(t_u16): 0xFFFF | * .------------------------------------------------------------. * * * For cases where an association response was received, the IEEE * standard association response frame is returned: * * .------------------------------------------------------------. * | Header(4 * sizeof(t_u16)): Standard command response hdr | * .------------------------------------------------------------. * | cap_info(t_u16): IEEE Capability | * .------------------------------------------------------------. * | status_code(t_u16): IEEE Status Code | * .------------------------------------------------------------. * | a_id(t_u16): IEEE Association ID | * .------------------------------------------------------------. * | IEEE IEs(variable): Any received IEs comprising the | * | remaining portion of a received | * | association response frame. | * .------------------------------------------------------------. * * For simplistic handling, the status_code field can be used to determine * an association success (0) or failure (non-zero). */ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct mwifiex_adapter *adapter = priv->adapter; int ret = 0; struct ieee_types_assoc_rsp *assoc_rsp; struct mwifiex_bssdescriptor *bss_desc; u8 enable_data = true; assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, sizeof(priv->assoc_rsp_buf)); memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); if (le16_to_cpu(assoc_rsp->status_code)) { priv->adapter->dbg.num_cmd_assoc_failure++; dev_err(priv->adapter->dev, "ASSOC_RESP: failed, status code=%d err=%#x a_id=%#x\n", le16_to_cpu(assoc_rsp->status_code), le16_to_cpu(assoc_rsp->cap_info_bitmap), le16_to_cpu(assoc_rsp->a_id)); ret = le16_to_cpu(assoc_rsp->status_code); goto done; } /* Send a Media Connected event, according to the Spec */ priv->media_connected = true; priv->adapter->ps_state = PS_STATE_AWAKE; priv->adapter->pps_uapsd_mode = false; priv->adapter->tx_lock_flag = false; /* Set the attempted BSSID Index to current */ bss_desc = priv->attempted_bss_desc; dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: %s\n", bss_desc->ssid.ssid); /* Make a copy of current BSSID descriptor */ memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, sizeof(struct mwifiex_bssdescriptor)); /* Update curr_bss_params */ priv->curr_bss_params.bss_descriptor.channel = bss_desc->phy_param_set.ds_param_set.current_chan; priv->curr_bss_params.band = (u8) bss_desc->bss_band; if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) priv->curr_bss_params.wmm_enabled = true; else priv->curr_bss_params.wmm_enabled = false; if ((priv->wmm_required || bss_desc->bcn_ht_cap) && priv->curr_bss_params.wmm_enabled) priv->wmm_enabled = true; else priv->wmm_enabled = false; priv->curr_bss_params.wmm_uapsd_enabled = false; if (priv->wmm_enabled) priv->curr_bss_params.wmm_uapsd_enabled = ((bss_desc->wmm_ie.qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: curr_pkt_filter is %#x\n", priv->curr_pkt_filter); if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) priv->wpa_is_gtk_set = false; if (priv->wmm_enabled) { /* Don't re-enable carrier until we get the WMM_GET_STATUS event */ enable_data = false; } else { /* Since WMM is not enabled, setup the queues with the defaults */ mwifiex_wmm_setup_queue_priorities(priv, NULL); mwifiex_wmm_setup_ac_downgrade(priv); } if (enable_data) dev_dbg(priv->adapter->dev, "info: post association, re-enabling data flow\n"); /* Reset SNR/NF/RSSI values */ priv->data_rssi_last = 0; priv->data_nf_last = 0; priv->data_rssi_avg = 0; priv->data_nf_avg = 0; priv->bcn_rssi_last = 0; priv->bcn_nf_last = 0; priv->bcn_rssi_avg = 0; priv->bcn_nf_avg = 0; priv->rxpd_rate = 0; priv->rxpd_htinfo = 0; mwifiex_save_curr_bcn(priv); priv->adapter->dbg.num_cmd_assoc_success++; dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: associated\n"); /* Add the ra_list here for infra mode as there will be only 1 ra always */ mwifiex_ralist_add(priv, priv->curr_bss_params.bss_descriptor.mac_address); if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); if (netif_queue_stopped(priv->netdev)) netif_wake_queue(priv->netdev); if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) priv->scan_block = true; done: /* Need to indicate IOCTL complete */ if (adapter->curr_cmd->wait_q_enabled) { if (ret) adapter->cmd_wait_q.status = -1; else adapter->cmd_wait_q.status = 0; } return ret; } /* * This function prepares command for ad-hoc start. * * Driver will fill up SSID, BSS mode, IBSS parameters, physical * parameters, probe delay, and capability information. Firmware * will fill up beacon period, basic rates and operational rates. * * In addition, the following TLVs are added - * - Channel TLV * - Vendor specific IE * - WPA/WPA2 IE * - HT Capabilities IE * - HT Information IE * * Preparation also includes - * - Setting command ID and proper size * - Ensuring correct endian-ness */ int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct cfg80211_ssid *req_ssid) { int rsn_ie_len = 0; struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start = &cmd->params.adhoc_start; struct mwifiex_bssdescriptor *bss_desc; u32 cmd_append_size = 0; u32 i; u16 tmp_cap; struct mwifiex_ie_types_chan_list_param_set *chan_tlv; u8 radio_type; struct mwifiex_ie_types_htcap *ht_cap; struct mwifiex_ie_types_htinfo *ht_info; u8 *pos = (u8 *) adhoc_start + sizeof(struct host_cmd_ds_802_11_ad_hoc_start); if (!adapter) return -1; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); bss_desc = &priv->curr_bss_params.bss_descriptor; priv->attempted_bss_desc = bss_desc; /* * Fill in the parameters for 2 data structures: * 1. struct host_cmd_ds_802_11_ad_hoc_start command * 2. bss_desc * Driver will fill up SSID, bss_mode,IBSS param, Physical Param, * probe delay, and Cap info. * Firmware will fill up beacon period, Basic rates * and operational rates. */ memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN); memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len); dev_dbg(adapter->dev, "info: ADHOC_S_CMD: SSID = %s\n", adhoc_start->ssid); memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN); memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len); bss_desc->ssid.ssid_len = req_ssid->ssid_len; /* Set the BSS mode */ adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period); bss_desc->beacon_period = priv->beacon_period; /* Set Physical param set */ /* Parameter IE Id */ #define DS_PARA_IE_ID 3 /* Parameter IE length */ #define DS_PARA_IE_LEN 1 adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; if (!mwifiex_get_cfp(priv, adapter->adhoc_start_band, (u16) priv->adhoc_channel, 0)) { struct mwifiex_chan_freq_power *cfp; cfp = mwifiex_get_cfp(priv, adapter->adhoc_start_band, FIRST_VALID_CHANNEL, 0); if (cfp) priv->adhoc_channel = (u8) cfp->channel; } if (!priv->adhoc_channel) { dev_err(adapter->dev, "ADHOC_S_CMD: adhoc_channel cannot be 0\n"); return -1; } dev_dbg(adapter->dev, "info: ADHOC_S_CMD: creating ADHOC on channel %d\n", priv->adhoc_channel); priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel; priv->curr_bss_params.band = adapter->adhoc_start_band; bss_desc->channel = priv->adhoc_channel; adhoc_start->phy_param_set.ds_param_set.current_chan = priv->adhoc_channel; memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set, sizeof(union ieee_types_phy_param_set)); /* Set IBSS param set */ /* IBSS parameter IE Id */ #define IBSS_PARA_IE_ID 6 /* IBSS parameter IE length */ #define IBSS_PARA_IE_LEN 2 adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; adhoc_start->ss_param_set.ibss_param_set.atim_window = cpu_to_le16(priv->atim_window); memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set, sizeof(union ieee_types_ss_param_set)); /* Set Capability info */ bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS; tmp_cap = le16_to_cpu(adhoc_start->cap_info_bitmap); tmp_cap &= ~WLAN_CAPABILITY_ESS; tmp_cap |= WLAN_CAPABILITY_IBSS; /* Set up privacy in bss_desc */ if (priv->sec_info.encryption_mode) { /* Ad-Hoc capability privacy on */ dev_dbg(adapter->dev, "info: ADHOC_S_CMD: wep_status set privacy to WEP\n"); bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; tmp_cap |= WLAN_CAPABILITY_PRIVACY; } else { dev_dbg(adapter->dev, "info: ADHOC_S_CMD: wep_status NOT set," " setting privacy to ACCEPT ALL\n"); bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; } memset(adhoc_start->data_rate, 0, sizeof(adhoc_start->data_rate)); mwifiex_get_active_data_rates(priv, adhoc_start->data_rate); if ((adapter->adhoc_start_band & BAND_G) && (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, HostCmd_ACT_GEN_SET, 0, &priv->curr_pkt_filter)) { dev_err(adapter->dev, "ADHOC_S_CMD: G Protection config failed\n"); return -1; } } /* Find the last non zero */ for (i = 0; i < sizeof(adhoc_start->data_rate); i++) if (!adhoc_start->data_rate[i]) break; priv->curr_bss_params.num_of_rates = i; /* Copy the ad-hoc creating rates into Current BSS rate structure */ memcpy(&priv->curr_bss_params.data_rates, &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates); dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n", adhoc_start->data_rate[0], adhoc_start->data_rate[1], adhoc_start->data_rate[2], adhoc_start->data_rate[3]); dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); if (IS_SUPPORT_MULTI_BANDS(adapter)) { /* Append a channel TLV */ chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); chan_tlv->header.len = cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); memset(chan_tlv->chan_scan_param, 0x00, sizeof(struct mwifiex_chan_scan_param_set)); chan_tlv->chan_scan_param[0].chan_number = (u8) priv->curr_bss_params.bss_descriptor.channel; dev_dbg(adapter->dev, "info: ADHOC_S_CMD: TLV Chan = %d\n", chan_tlv->chan_scan_param[0].chan_number); chan_tlv->chan_scan_param[0].radio_type = mwifiex_band_to_radio_type(priv->curr_bss_params.band); if (adapter->adhoc_start_band & BAND_GN || adapter->adhoc_start_band & BAND_AN) { if (adapter->sec_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) chan_tlv->chan_scan_param[0].radio_type |= (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4); else if (adapter->sec_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) chan_tlv->chan_scan_param[0].radio_type |= (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4); } dev_dbg(adapter->dev, "info: ADHOC_S_CMD: TLV Band = %d\n", chan_tlv->chan_scan_param[0].radio_type); pos += sizeof(chan_tlv->header) + sizeof(struct mwifiex_chan_scan_param_set); cmd_append_size += sizeof(chan_tlv->header) + sizeof(struct mwifiex_chan_scan_param_set); } /* Append vendor specific IE TLV */ cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ADHOC, &pos); if (priv->sec_info.wpa_enabled) { rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); if (rsn_ie_len == -1) return -1; cmd_append_size += rsn_ie_len; } if (adapter->adhoc_11n_enabled) { /* Fill HT CAPABILITY */ ht_cap = (struct mwifiex_ie_types_htcap *) pos; memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); ht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_ht_cap)); radio_type = mwifiex_band_to_radio_type( priv->adapter->config_bands); mwifiex_fill_cap_info(priv, radio_type, ht_cap); pos += sizeof(struct mwifiex_ie_types_htcap); cmd_append_size += sizeof(struct mwifiex_ie_types_htcap); /* Fill HT INFORMATION */ ht_info = (struct mwifiex_ie_types_htinfo *) pos; memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION); ht_info->header.len = cpu_to_le16(sizeof(struct ieee80211_ht_operation)); ht_info->ht_oper.primary_chan = (u8) priv->curr_bss_params.bss_descriptor.channel; if (adapter->sec_chan_offset) { ht_info->ht_oper.ht_param = adapter->sec_chan_offset; ht_info->ht_oper.ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; } ht_info->ht_oper.operation_mode = cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); ht_info->ht_oper.basic_set[0] = 0xff; pos += sizeof(struct mwifiex_ie_types_htinfo); cmd_append_size += sizeof(struct mwifiex_ie_types_htinfo); } cmd->size = cpu_to_le16((u16)(sizeof(struct host_cmd_ds_802_11_ad_hoc_start) + S_DS_GEN + cmd_append_size)); if (adapter->adhoc_start_band == BAND_B) tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; else tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME; adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap); return 0; } /* * This function prepares command for ad-hoc join. * * Most of the parameters are set up by copying from the target BSS descriptor * from the scan response. * * In addition, the following TLVs are added - * - Channel TLV * - Vendor specific IE * - WPA/WPA2 IE * - 11n IE * * Preparation also includes - * - Setting command ID and proper size * - Ensuring correct endian-ness */ int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct mwifiex_bssdescriptor *bss_desc) { int rsn_ie_len = 0; struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join = &cmd->params.adhoc_join; struct mwifiex_ie_types_chan_list_param_set *chan_tlv; u32 cmd_append_size = 0; u16 tmp_cap; u32 i, rates_size = 0; u16 curr_pkt_filter; u8 *pos = (u8 *) adhoc_join + sizeof(struct host_cmd_ds_802_11_ad_hoc_join); /* Use G protection */ #define USE_G_PROTECTION 0x02 if (bss_desc->erp_flags & USE_G_PROTECTION) { curr_pkt_filter = priv-> curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, HostCmd_ACT_GEN_SET, 0, &curr_pkt_filter)) { dev_err(priv->adapter->dev, "ADHOC_J_CMD: G Protection config failed\n"); return -1; } } priv->attempted_bss_desc = bss_desc; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; adhoc_join->bss_descriptor.beacon_period = cpu_to_le16(bss_desc->beacon_period); memcpy(&adhoc_join->bss_descriptor.bssid, &bss_desc->mac_address, ETH_ALEN); memcpy(&adhoc_join->bss_descriptor.ssid, &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len); memcpy(&adhoc_join->bss_descriptor.phy_param_set, &bss_desc->phy_param_set, sizeof(union ieee_types_phy_param_set)); memcpy(&adhoc_join->bss_descriptor.ss_param_set, &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set)); tmp_cap = bss_desc->cap_info_bitmap; tmp_cap &= CAPINFO_MASK; dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", tmp_cap, CAPINFO_MASK); /* Information on BSSID descriptor passed to FW */ dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: BSSID=%pM, SSID='%s'\n", adhoc_join->bss_descriptor.bssid, adhoc_join->bss_descriptor.ssid); for (i = 0; bss_desc->supported_rates[i] && i < MWIFIEX_SUPPORTED_RATES; i++) ; rates_size = i; /* Copy Data Rates from the Rates recorded in scan response */ memset(adhoc_join->bss_descriptor.data_rates, 0, sizeof(adhoc_join->bss_descriptor.data_rates)); memcpy(adhoc_join->bss_descriptor.data_rates, bss_desc->supported_rates, rates_size); /* Copy the adhoc join rates into Current BSS state structure */ priv->curr_bss_params.num_of_rates = rates_size; memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates, rates_size); /* Copy the channel information */ priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel; priv->curr_bss_params.band = (u8) bss_desc->bss_band; if (priv->sec_info.wep_enabled || priv->sec_info.wpa_enabled) tmp_cap |= WLAN_CAPABILITY_PRIVACY; if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) { /* Append a channel TLV */ chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); chan_tlv->header.len = cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); memset(chan_tlv->chan_scan_param, 0x00, sizeof(struct mwifiex_chan_scan_param_set)); chan_tlv->chan_scan_param[0].chan_number = (bss_desc->phy_param_set.ds_param_set.current_chan); dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: TLV Chan=%d\n", chan_tlv->chan_scan_param[0].chan_number); chan_tlv->chan_scan_param[0].radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: TLV Band=%d\n", chan_tlv->chan_scan_param[0].radio_type); pos += sizeof(chan_tlv->header) + sizeof(struct mwifiex_chan_scan_param_set); cmd_append_size += sizeof(chan_tlv->header) + sizeof(struct mwifiex_chan_scan_param_set); } if (priv->sec_info.wpa_enabled) rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); if (rsn_ie_len == -1) return -1; cmd_append_size += rsn_ie_len; if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) cmd_append_size += mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); /* Append vendor specific IE TLV */ cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ADHOC, &pos); cmd->size = cpu_to_le16 ((u16) (sizeof(struct host_cmd_ds_802_11_ad_hoc_join) + S_DS_GEN + cmd_append_size)); adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap); return 0; } /* * This function handles the command response of ad-hoc start and * ad-hoc join. * * The function generates a device-connected event to notify * the applications, in case of successful ad-hoc start/join, and * saves the beacon buffer. */ int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; struct mwifiex_bssdescriptor *bss_desc; adhoc_result = &resp->params.adhoc_result; bss_desc = priv->attempted_bss_desc; /* Join result code 0 --> SUCCESS */ if (le16_to_cpu(resp->result)) { dev_err(priv->adapter->dev, "ADHOC_RESP: failed\n"); if (priv->media_connected) mwifiex_reset_connect_state(priv); memset(&priv->curr_bss_params.bss_descriptor, 0x00, sizeof(struct mwifiex_bssdescriptor)); ret = -1; goto done; } /* Send a Media Connected event, according to the Spec */ priv->media_connected = true; if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) { dev_dbg(priv->adapter->dev, "info: ADHOC_S_RESP %s\n", bss_desc->ssid.ssid); /* Update the created network descriptor with the new BSSID */ memcpy(bss_desc->mac_address, adhoc_result->bssid, ETH_ALEN); priv->adhoc_state = ADHOC_STARTED; } else { /* * Now the join cmd should be successful. * If BSSID has changed use SSID to compare instead of BSSID */ dev_dbg(priv->adapter->dev, "info: ADHOC_J_RESP %s\n", bss_desc->ssid.ssid); /* * Make a copy of current BSSID descriptor, only needed for * join since the current descriptor is already being used * for adhoc start */ memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, sizeof(struct mwifiex_bssdescriptor)); priv->adhoc_state = ADHOC_JOINED; } dev_dbg(priv->adapter->dev, "info: ADHOC_RESP: channel = %d\n", priv->adhoc_channel); dev_dbg(priv->adapter->dev, "info: ADHOC_RESP: BSSID = %pM\n", priv->curr_bss_params.bss_descriptor.mac_address); if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); if (netif_queue_stopped(priv->netdev)) netif_wake_queue(priv->netdev); mwifiex_save_curr_bcn(priv); done: /* Need to indicate IOCTL complete */ if (adapter->curr_cmd->wait_q_enabled) { if (ret) adapter->cmd_wait_q.status = -1; else adapter->cmd_wait_q.status = 0; } return ret; } /* * This function associates to a specific BSS discovered in a scan. * * It clears any past association response stored for application * retrieval and calls the command preparation routine to send the * command to firmware. */ int mwifiex_associate(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { u8 current_bssid[ETH_ALEN]; /* Return error if the adapter or table entry is not marked as infra */ if ((priv->bss_mode != NL80211_IFTYPE_STATION) || (bss_desc->bss_mode != NL80211_IFTYPE_STATION)) return -1; memcpy(¤t_bssid, &priv->curr_bss_params.bss_descriptor.mac_address, sizeof(current_bssid)); /* Clear any past association response stored for application retrieval */ priv->assoc_rsp_size = 0; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_ASSOCIATE, HostCmd_ACT_GEN_SET, 0, bss_desc); } /* * This function starts an ad-hoc network. * * It calls the command preparation routine to send the command to firmware. */ int mwifiex_adhoc_start(struct mwifiex_private *priv, struct cfg80211_ssid *adhoc_ssid) { dev_dbg(priv->adapter->dev, "info: Adhoc Channel = %d\n", priv->adhoc_channel); dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n", priv->curr_bss_params.bss_descriptor.channel); dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %d\n", priv->curr_bss_params.band); return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_START, HostCmd_ACT_GEN_SET, 0, adhoc_ssid); } /* * This function joins an ad-hoc network found in a previous scan. * * It calls the command preparation routine to send the command to firmware, * if already not connected to the requested SSID. */ int mwifiex_adhoc_join(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { dev_dbg(priv->adapter->dev, "info: adhoc join: curr_bss ssid =%s\n", priv->curr_bss_params.bss_descriptor.ssid.ssid); dev_dbg(priv->adapter->dev, "info: adhoc join: curr_bss ssid_len =%u\n", priv->curr_bss_params.bss_descriptor.ssid.ssid_len); dev_dbg(priv->adapter->dev, "info: adhoc join: ssid =%s\n", bss_desc->ssid.ssid); dev_dbg(priv->adapter->dev, "info: adhoc join: ssid_len =%u\n", bss_desc->ssid.ssid_len); /* Check if the requested SSID is already joined */ if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len && !mwifiex_ssid_cmp(&bss_desc->ssid, &priv->curr_bss_params.bss_descriptor.ssid) && (priv->curr_bss_params.bss_descriptor.bss_mode == NL80211_IFTYPE_ADHOC)) { dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: new ad-hoc SSID" " is the same as current; not attempting to re-join\n"); return -1; } dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n", priv->curr_bss_params.bss_descriptor.channel); dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %c\n", priv->curr_bss_params.band); return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, HostCmd_ACT_GEN_SET, 0, bss_desc); } /* * This function deauthenticates/disconnects from infra network by sending * deauthentication request. */ static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) { u8 mac_address[ETH_ALEN]; int ret; if (!mac || is_zero_ether_addr(mac)) memcpy(mac_address, priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); else memcpy(mac_address, mac, ETH_ALEN); ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, HostCmd_ACT_GEN_SET, 0, mac_address); return ret; } /* * This function deauthenticates/disconnects from a BSS. * * In case of infra made, it sends deauthentication request, and * in case of ad-hoc mode, a stop network request is sent to the firmware. * In AP mode, a command to stop bss is sent to firmware. */ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) { if (!priv->media_connected) return 0; switch (priv->bss_mode) { case NL80211_IFTYPE_STATION: return mwifiex_deauthenticate_infra(priv, mac); case NL80211_IFTYPE_ADHOC: return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_STOP, HostCmd_ACT_GEN_SET, 0, NULL); case NL80211_IFTYPE_AP: return mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, HostCmd_ACT_GEN_SET, 0, NULL); default: break; } return 0; } EXPORT_SYMBOL_GPL(mwifiex_deauthenticate); /* * This function converts band to radio type used in channel TLV. */ u8 mwifiex_band_to_radio_type(u8 band) { switch (band) { case BAND_A: case BAND_AN: case BAND_A | BAND_AN: return HostCmd_SCAN_RADIO_TYPE_A; case BAND_B: case BAND_G: case BAND_B | BAND_G: default: return HostCmd_SCAN_RADIO_TYPE_BG; } } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/ioctl.h0000644000175000017500000001616112026211315023421 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: ioctl data structures & APIs * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_IOCTL_H_ #define _MWIFIEX_IOCTL_H_ #include #include enum { MWIFIEX_SCAN_TYPE_UNCHANGED = 0, MWIFIEX_SCAN_TYPE_ACTIVE, MWIFIEX_SCAN_TYPE_PASSIVE }; struct mwifiex_user_scan { u32 scan_cfg_len; u8 scan_cfg_buf[1]; }; #define MWIFIEX_PROMISC_MODE 1 #define MWIFIEX_MULTICAST_MODE 2 #define MWIFIEX_ALL_MULTI_MODE 4 #define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32 struct mwifiex_multicast_list { u32 mode; u32 num_multicast_addr; u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; }; struct mwifiex_chan_freq { u32 channel; u32 freq; }; struct mwifiex_ssid_bssid { struct cfg80211_ssid ssid; u8 bssid[ETH_ALEN]; }; enum { BAND_B = 1, BAND_G = 2, BAND_A = 4, BAND_GN = 8, BAND_AN = 16, }; #define MWIFIEX_WPA_PASSHPHRASE_LEN 64 struct wpa_param { u8 pairwise_cipher_wpa; u8 pairwise_cipher_wpa2; u8 group_cipher; u32 length; u8 passphrase[MWIFIEX_WPA_PASSHPHRASE_LEN]; }; struct wep_key { u8 key_index; u8 is_default; u16 length; u8 key[WLAN_KEY_LEN_WEP104]; }; #define KEY_MGMT_ON_HOST 0x03 #define MWIFIEX_AUTH_MODE_AUTO 0xFF #define BAND_CONFIG_BG 0x00 #define BAND_CONFIG_A 0x01 #define MWIFIEX_SUPPORTED_RATES 14 #define MWIFIEX_SUPPORTED_RATES_EXT 32 struct mwifiex_uap_bss_param { u8 channel; u8 band_cfg; u16 rts_threshold; u16 frag_threshold; u8 retry_limit; struct mwifiex_802_11_ssid ssid; u8 bcast_ssid_ctl; u8 radio_ctl; u8 dtim_period; u16 beacon_period; u16 auth_mode; u16 protocol; u16 key_mgmt; u16 key_mgmt_operation; struct wpa_param wpa_cfg; struct wep_key wep_cfg[NUM_WEP_KEYS]; struct ieee80211_ht_cap ht_cap; u8 rates[MWIFIEX_SUPPORTED_RATES]; }; enum { ADHOC_IDLE, ADHOC_STARTED, ADHOC_JOINED, ADHOC_COALESCED }; struct mwifiex_ds_get_stats { u32 mcast_tx_frame; u32 failed; u32 retry; u32 multi_retry; u32 frame_dup; u32 rts_success; u32 rts_failure; u32 ack_failure; u32 rx_frag; u32 mcast_rx_frame; u32 fcs_error; u32 tx_frame; u32 wep_icv_error[4]; }; #define MWIFIEX_MAX_VER_STR_LEN 128 struct mwifiex_ver_ext { u32 version_str_sel; char version_str[MWIFIEX_MAX_VER_STR_LEN]; }; struct mwifiex_bss_info { u32 bss_mode; struct cfg80211_ssid ssid; u32 bss_chan; u8 country_code[3]; u32 media_connected; u32 max_power_level; u32 min_power_level; u32 adhoc_state; signed int bcn_nf_last; u32 wep_status; u32 is_hs_configured; u32 is_deep_sleep; u8 bssid[ETH_ALEN]; }; #define MAX_NUM_TID 8 #define MAX_RX_WINSIZE 64 struct mwifiex_ds_rx_reorder_tbl { u16 tid; u8 ta[ETH_ALEN]; u32 start_win; u32 win_size; u32 buffer[MAX_RX_WINSIZE]; }; struct mwifiex_ds_tx_ba_stream_tbl { u16 tid; u8 ra[ETH_ALEN]; }; #define DBG_CMD_NUM 5 struct mwifiex_debug_info { u32 int_counter; u32 packets_out[MAX_NUM_TID]; u32 max_tx_buf_size; u32 tx_buf_size; u32 curr_tx_buf_size; u32 tx_tbl_num; struct mwifiex_ds_tx_ba_stream_tbl tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED]; u32 rx_tbl_num; struct mwifiex_ds_rx_reorder_tbl rx_tbl [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; u16 ps_mode; u32 ps_state; u8 is_deep_sleep; u8 pm_wakeup_card_req; u32 pm_wakeup_fw_try; u8 is_hs_configured; u8 hs_activated; u32 num_cmd_host_to_card_failure; u32 num_cmd_sleep_cfm_host_to_card_failure; u32 num_tx_host_to_card_failure; u32 num_event_deauth; u32 num_event_disassoc; u32 num_event_link_lost; u32 num_cmd_deauth; u32 num_cmd_assoc_success; u32 num_cmd_assoc_failure; u32 num_tx_timeout; u32 num_cmd_timeout; u16 timeout_cmd_id; u16 timeout_cmd_act; u16 last_cmd_id[DBG_CMD_NUM]; u16 last_cmd_act[DBG_CMD_NUM]; u16 last_cmd_index; u16 last_cmd_resp_id[DBG_CMD_NUM]; u16 last_cmd_resp_index; u16 last_event[DBG_CMD_NUM]; u16 last_event_index; u8 data_sent; u8 cmd_sent; u8 cmd_resp_received; u8 event_received; }; #define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 #define PN_LEN 16 struct mwifiex_ds_encrypt_key { u32 key_disable; u32 key_index; u32 key_len; u8 key_material[WLAN_MAX_KEY_LEN]; u8 mac_addr[ETH_ALEN]; u32 is_wapi_key; u8 pn[PN_LEN]; /* packet number */ u8 is_igtk_key; }; struct mwifiex_power_cfg { u32 is_power_auto; u32 power_level; }; struct mwifiex_ds_hs_cfg { u32 is_invoke_hostcmd; /* Bit0: non-unicast data * Bit1: unicast data * Bit2: mac events * Bit3: magic packet */ u32 conditions; u32 gpio; u32 gap; }; #define DEEP_SLEEP_ON 1 #define DEEP_SLEEP_OFF 0 #define DEEP_SLEEP_IDLE_TIME 100 #define PS_MODE_AUTO 1 struct mwifiex_ds_auto_ds { u16 auto_ds; u16 idle_time; }; struct mwifiex_ds_pm_cfg { union { u32 ps_mode; struct mwifiex_ds_hs_cfg hs_cfg; struct mwifiex_ds_auto_ds auto_deep_sleep; u32 sleep_period; } param; }; struct mwifiex_ds_11n_tx_cfg { u16 tx_htcap; u16 tx_htinfo; }; struct mwifiex_ds_11n_amsdu_aggr_ctrl { u16 enable; u16 curr_buf_size; }; struct mwifiex_ds_ant_cfg { u32 tx_ant; u32 rx_ant; }; #define MWIFIEX_NUM_OF_CMD_BUFFER 20 #define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 enum { MWIFIEX_IE_TYPE_GEN_IE = 0, MWIFIEX_IE_TYPE_ARP_FILTER, }; enum { MWIFIEX_REG_MAC = 1, MWIFIEX_REG_BBP, MWIFIEX_REG_RF, MWIFIEX_REG_PMIC, MWIFIEX_REG_CAU, }; struct mwifiex_ds_reg_rw { __le32 type; __le32 offset; __le32 value; }; #define MAX_EEPROM_DATA 256 struct mwifiex_ds_read_eeprom { __le16 offset; __le16 byte_count; u8 value[MAX_EEPROM_DATA]; }; #define IEEE_MAX_IE_SIZE 256 #define MWIFIEX_IE_HDR_SIZE (sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE) struct mwifiex_ds_misc_gen_ie { u32 type; u32 len; u8 ie_data[IEEE_MAX_IE_SIZE]; }; struct mwifiex_ds_misc_cmd { u32 len; u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER]; }; #define BITMASK_BCN_RSSI_LOW BIT(0) #define BITMASK_BCN_RSSI_HIGH BIT(4) enum subsc_evt_rssi_state { EVENT_HANDLED, RSSI_LOW_RECVD, RSSI_HIGH_RECVD }; struct subsc_evt_cfg { u8 abs_value; u8 evt_freq; }; struct mwifiex_ds_misc_subsc_evt { u16 action; u16 events; struct subsc_evt_cfg bcn_l_rssi_cfg; struct subsc_evt_cfg bcn_h_rssi_cfg; }; #define MWIFIEX_MAX_VSIE_LEN (256) #define MWIFIEX_MAX_VSIE_NUM (8) #define MWIFIEX_VSIE_MASK_CLEAR 0x00 #define MWIFIEX_VSIE_MASK_SCAN 0x01 #define MWIFIEX_VSIE_MASK_ASSOC 0x02 #define MWIFIEX_VSIE_MASK_ADHOC 0x04 enum { MWIFIEX_FUNC_INIT = 1, MWIFIEX_FUNC_SHUTDOWN, }; #endif /* !_MWIFIEX_IOCTL_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/ie.c0000644000175000017500000002767212026211315022710 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: management IE handling- setting and * deleting IE. * * Copyright (C) 2012, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "main.h" /* This function checks if current IE index is used by any on other interface. * Return: -1: yes, current IE index is used by someone else. * 0: no, current IE index is NOT used by other interface. */ static int mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx) { int i; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_ie *ie; for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i] != priv) { ie = &adapter->priv[i]->mgmt_ie[idx]; if (ie->mgmt_subtype_mask && ie->ie_length) return -1; } } return 0; } /* Get unused IE index. This index will be used for setting new IE */ static int mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask, struct mwifiex_ie *ie, u16 *index) { u16 mask, len, i; for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) { mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask); len = le16_to_cpu(ie->ie_length); if (mask == MWIFIEX_AUTO_IDX_MASK) continue; if (mask == subtype_mask) { if (len > IEEE_MAX_IE_SIZE) continue; *index = i; return 0; } if (!priv->mgmt_ie[i].ie_length) { if (mwifiex_ie_index_used_by_other_intf(priv, i)) continue; *index = i; return 0; } } return -1; } /* This function prepares IE data buffer for command to be sent to FW */ static int mwifiex_update_autoindex_ies(struct mwifiex_private *priv, struct mwifiex_ie_list *ie_list) { u16 travel_len, index, mask; s16 input_len; struct mwifiex_ie *ie; u8 *tmp; input_len = le16_to_cpu(ie_list->len); travel_len = sizeof(struct host_cmd_tlv); ie_list->len = 0; while (input_len > 0) { ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len); input_len -= le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE; travel_len += le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE; index = le16_to_cpu(ie->ie_index); mask = le16_to_cpu(ie->mgmt_subtype_mask); if (index == MWIFIEX_AUTO_IDX_MASK) { /* automatic addition */ if (mwifiex_ie_get_autoidx(priv, mask, ie, &index)) return -1; if (index == MWIFIEX_AUTO_IDX_MASK) return -1; tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer; memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length)); priv->mgmt_ie[index].ie_length = ie->ie_length; priv->mgmt_ie[index].ie_index = cpu_to_le16(index); priv->mgmt_ie[index].mgmt_subtype_mask = cpu_to_le16(mask); ie->ie_index = cpu_to_le16(index); ie->ie_length = priv->mgmt_ie[index].ie_length; memcpy(&ie->ie_buffer, &priv->mgmt_ie[index].ie_buffer, le16_to_cpu(priv->mgmt_ie[index].ie_length)); } else { if (mask != MWIFIEX_DELETE_MASK) return -1; /* * Check if this index is being used on any * other interface. */ if (mwifiex_ie_index_used_by_other_intf(priv, index)) return -1; ie->ie_length = 0; memcpy(&priv->mgmt_ie[index], ie, sizeof(struct mwifiex_ie)); } le16_add_cpu(&ie_list->len, le16_to_cpu(priv->mgmt_ie[index].ie_length) + MWIFIEX_IE_HDR_SIZE); } if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) return mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG, HostCmd_ACT_GEN_SET, UAP_CUSTOM_IE_I, ie_list); return 0; } /* Copy individual custom IEs for beacon, probe response and assoc response * and prepare single structure for IE setting. * This function also updates allocated IE indices from driver. */ static int mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, struct mwifiex_ie *beacon_ie, u16 *beacon_idx, struct mwifiex_ie *pr_ie, u16 *probe_idx, struct mwifiex_ie *ar_ie, u16 *assoc_idx) { struct mwifiex_ie_list *ap_custom_ie; u8 *pos; u16 len; int ret; ap_custom_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); if (!ap_custom_ie) return -ENOMEM; ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); pos = (u8 *)ap_custom_ie->ie_list; if (beacon_ie) { len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + le16_to_cpu(beacon_ie->ie_length); memcpy(pos, beacon_ie, len); pos += len; le16_add_cpu(&ap_custom_ie->len, len); } if (pr_ie) { len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + le16_to_cpu(pr_ie->ie_length); memcpy(pos, pr_ie, len); pos += len; le16_add_cpu(&ap_custom_ie->len, len); } if (ar_ie) { len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + le16_to_cpu(ar_ie->ie_length); memcpy(pos, ar_ie, len); pos += len; le16_add_cpu(&ap_custom_ie->len, len); } ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie); pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index); if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) { /* save beacon ie index after auto-indexing */ *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index); len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE + le16_to_cpu(beacon_ie->ie_length); pos += len; } if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) { /* save probe resp ie index after auto-indexing */ *probe_idx = *((u16 *)pos); len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE + le16_to_cpu(pr_ie->ie_length); pos += len; } if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) /* save assoc resp ie index after auto-indexing */ *assoc_idx = *((u16 *)pos); kfree(ap_custom_ie); return ret; } /* This function checks if WPS IE is present in passed buffer and copies it to * mwifiex_ie structure. * Function takes pointer to struct mwifiex_ie pointer as argument. * If WPS IE is present memory is allocated for mwifiex_ie pointer and filled * in with WPS IE. Caller should take care of freeing this memory. */ static int mwifiex_update_wps_ie(const u8 *ies, int ies_len, struct mwifiex_ie **ie_ptr, u16 mask) { struct ieee_types_header *wps_ie; struct mwifiex_ie *ie = NULL; const u8 *vendor_ie; vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, ies, ies_len); if (vendor_ie) { ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); if (!ie) return -ENOMEM; wps_ie = (struct ieee_types_header *)vendor_ie; memcpy(ie->ie_buffer, wps_ie, wps_ie->len + 2); ie->ie_length = cpu_to_le16(wps_ie->len + 2); ie->mgmt_subtype_mask = cpu_to_le16(mask); ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK); } *ie_ptr = ie; return 0; } /* This function parses beacon IEs, probe response IEs, association response IEs * from cfg80211_ap_settings->beacon and sets these IE to FW. */ static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv, struct cfg80211_beacon_data *data) { struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL; u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK; u16 ar_idx = MWIFIEX_AUTO_IDX_MASK; int ret = 0; if (data->beacon_ies && data->beacon_ies_len) mwifiex_update_wps_ie(data->beacon_ies, data->beacon_ies_len, &beacon_ie, MGMT_MASK_BEACON); if (data->proberesp_ies && data->proberesp_ies_len) mwifiex_update_wps_ie(data->proberesp_ies, data->proberesp_ies_len, &pr_ie, MGMT_MASK_PROBE_RESP); if (data->assocresp_ies && data->assocresp_ies_len) mwifiex_update_wps_ie(data->assocresp_ies, data->assocresp_ies_len, &ar_ie, MGMT_MASK_ASSOC_RESP | MGMT_MASK_REASSOC_RESP); if (beacon_ie || pr_ie || ar_ie) { ret = mwifiex_update_uap_custom_ie(priv, beacon_ie, &beacon_idx, pr_ie, &pr_idx, ar_ie, &ar_idx); if (ret) goto done; } priv->beacon_idx = beacon_idx; priv->proberesp_idx = pr_idx; priv->assocresp_idx = ar_idx; done: kfree(beacon_ie); kfree(pr_ie); kfree(ar_ie); return ret; } /* This function parses different IEs-tail IEs, beacon IEs, probe response IEs, * association response IEs from cfg80211_ap_settings function and sets these IE * to FW. */ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, struct cfg80211_beacon_data *info) { struct mwifiex_ie *gen_ie; struct ieee_types_header *rsn_ie, *wpa_ie = NULL; u16 rsn_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; const u8 *vendor_ie; if (info->tail && info->tail_len) { gen_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); if (!gen_ie) return -ENOMEM; gen_ie->ie_index = cpu_to_le16(rsn_idx); gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP | MGMT_MASK_ASSOC_RESP); rsn_ie = (void *)cfg80211_find_ie(WLAN_EID_RSN, info->tail, info->tail_len); if (rsn_ie) { memcpy(gen_ie->ie_buffer, rsn_ie, rsn_ie->len + 2); ie_len = rsn_ie->len + 2; gen_ie->ie_length = cpu_to_le16(ie_len); } vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, info->tail, info->tail_len); if (vendor_ie) { wpa_ie = (struct ieee_types_header *)vendor_ie; memcpy(gen_ie->ie_buffer + ie_len, wpa_ie, wpa_ie->len + 2); ie_len += wpa_ie->len + 2; gen_ie->ie_length = cpu_to_le16(ie_len); } if (rsn_ie || wpa_ie) { if (mwifiex_update_uap_custom_ie(priv, gen_ie, &rsn_idx, NULL, NULL, NULL, NULL)) { kfree(gen_ie); return -1; } priv->rsn_idx = rsn_idx; } kfree(gen_ie); } return mwifiex_set_mgmt_beacon_data_ies(priv, info); } /* This function removes management IE set */ int mwifiex_del_mgmt_ies(struct mwifiex_private *priv) { struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL; struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL; int ret = 0; if (priv->rsn_idx != MWIFIEX_AUTO_IDX_MASK) { rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); if (!rsn_ie) return -ENOMEM; rsn_ie->ie_index = cpu_to_le16(priv->rsn_idx); rsn_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); rsn_ie->ie_length = 0; if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &priv->rsn_idx, NULL, &priv->proberesp_idx, NULL, &priv->assocresp_idx)) { ret = -1; goto done; } priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK; } if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) { beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); if (!beacon_ie) { ret = -ENOMEM; goto done; } beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx); beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); beacon_ie->ie_length = 0; } if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) { pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); if (!pr_ie) { ret = -ENOMEM; goto done; } pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx); pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); pr_ie->ie_length = 0; } if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) { ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); if (!ar_ie) { ret = -ENOMEM; goto done; } ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx); ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); ar_ie->ie_length = 0; } if (beacon_ie || pr_ie || ar_ie) ret = mwifiex_update_uap_custom_ie(priv, beacon_ie, &priv->beacon_idx, pr_ie, &priv->proberesp_idx, ar_ie, &priv->assocresp_idx); done: kfree(beacon_ie); kfree(pr_ie); kfree(ar_ie); kfree(rsn_ie); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/fw.h0000644000175000017500000011163312026211315022723 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: Firmware specific macros & structures * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_FW_H_ #define _MWIFIEX_FW_H_ #include #define INTF_HEADER_LEN 4 struct rfc_1042_hdr { u8 llc_dsap; u8 llc_ssap; u8 llc_ctrl; u8 snap_oui[3]; u16 snap_type; }; struct rx_packet_hdr { struct ethhdr eth803_hdr; struct rfc_1042_hdr rfc1042_hdr; }; struct tx_packet_hdr { struct ethhdr eth803_hdr; struct rfc_1042_hdr rfc1042_hdr; }; #define B_SUPPORTED_RATES 5 #define G_SUPPORTED_RATES 9 #define BG_SUPPORTED_RATES 13 #define A_SUPPORTED_RATES 9 #define HOSTCMD_SUPPORTED_RATES 14 #define N_SUPPORTED_RATES 3 #define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN) #define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11)) #define IS_SUPPORT_MULTI_BANDS(adapter) \ (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) #define GET_FW_DEFAULT_BANDS(adapter) \ ((adapter->fw_cap_info >> 8) & ALL_802_11_BANDS) #define HostCmd_WEP_KEY_INDEX_MASK 0x3fff #define KEY_INFO_ENABLED 0x01 enum KEY_TYPE_ID { KEY_TYPE_ID_WEP = 0, KEY_TYPE_ID_TKIP, KEY_TYPE_ID_AES, KEY_TYPE_ID_WAPI, KEY_TYPE_ID_AES_CMAC, }; #define KEY_MCAST BIT(0) #define KEY_UNICAST BIT(1) #define KEY_ENABLED BIT(2) #define KEY_IGTK BIT(10) #define WAPI_KEY_LEN 50 #define MAX_POLL_TRIES 100 #define MAX_MULTI_INTERFACE_POLL_TRIES 1000 #define MAX_FIRMWARE_POLL_TRIES 100 #define FIRMWARE_READY_SDIO 0xfedc #define FIRMWARE_READY_PCIE 0xfedcba00 enum mwifiex_usb_ep { MWIFIEX_USB_EP_CMD_EVENT = 1, MWIFIEX_USB_EP_DATA = 2, }; enum MWIFIEX_802_11_PRIVACY_FILTER { MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, MWIFIEX_802_11_PRIV_FILTER_8021X_WEP }; #define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) #define UAP_BSS_PARAMS_I 0 #define UAP_CUSTOM_IE_I 1 #define MWIFIEX_AUTO_IDX_MASK 0xffff #define MWIFIEX_DELETE_MASK 0x0000 #define MGMT_MASK_ASSOC_REQ 0x01 #define MGMT_MASK_REASSOC_REQ 0x04 #define MGMT_MASK_ASSOC_RESP 0x02 #define MGMT_MASK_REASSOC_RESP 0x08 #define MGMT_MASK_PROBE_REQ 0x10 #define MGMT_MASK_PROBE_RESP 0x20 #define MGMT_MASK_BEACON 0x100 #define TLV_TYPE_UAP_SSID 0x0000 #define TLV_TYPE_UAP_RATES 0x0001 #define PROPRIETARY_TLV_BASE_ID 0x0100 #define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) #define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) #define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) #define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) #define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) #define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) #define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) #define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) #define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) #define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) #define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) #define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) #define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) #define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) #define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) #define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) #define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) #define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65) #define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70) #define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) #define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) #define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) #define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) #define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) #define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) #define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105) #define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) #define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) #define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 #define SSN_MASK 0xfff0 #define BA_RESULT_SUCCESS 0x0 #define BA_RESULT_TIMEOUT 0x2 #define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) #define BA_STREAM_NOT_ALLOWED 0xff #define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \ priv->adapter->config_bands & BAND_AN) && \ priv->curr_bss_params.bss_descriptor.bcn_ht_cap) #define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\ BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) #define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096 #define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) #define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ IEEE80211_HT_CAP_SM_PS) #define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR /* dev_cap bitmap * BIT * 0-16 reserved * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40 * 18-22 reserved * 23 IEEE80211_HT_CAP_SGI_20 * 24 IEEE80211_HT_CAP_SGI_40 * 25 IEEE80211_HT_CAP_TX_STBC * 26 IEEE80211_HT_CAP_RX_STBC * 27-28 reserved * 29 IEEE80211_HT_CAP_GRN_FLD * 30-31 reserved */ #define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17)) #define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23)) #define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24)) #define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25)) #define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26)) #define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) /* httxcfg bitmap * 0 reserved * 1 20/40 Mhz enable(1)/disable(0) * 2-3 reserved * 4 green field enable(1)/disable(0) * 5 short GI in 20 Mhz enable(1)/disable(0) * 6 short GI in 40 Mhz enable(1)/disable(0) * 7-15 reserved */ #define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) #define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) #define SETHT_MCS32(x) (x[4] |= 1) #define HT_STREAM_2X2 0x22 #define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) #define LLC_SNAP_LEN 8 #define MOD_CLASS_HR_DSSS 0x03 #define MOD_CLASS_OFDM 0x07 #define MOD_CLASS_HT 0x08 #define HT_BW_20 0 #define HT_BW_40 1 #define HostCmd_CMD_GET_HW_SPEC 0x0003 #define HostCmd_CMD_802_11_SCAN 0x0006 #define HostCmd_CMD_802_11_GET_LOG 0x000b #define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 #define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 #define HostCmd_CMD_802_11_ASSOCIATE 0x0012 #define HostCmd_CMD_802_11_SNMP_MIB 0x0016 #define HostCmd_CMD_MAC_REG_ACCESS 0x0019 #define HostCmd_CMD_BBP_REG_ACCESS 0x001a #define HostCmd_CMD_RF_REG_ACCESS 0x001b #define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad #define HostCmd_CMD_RF_TX_PWR 0x001e #define HostCmd_CMD_RF_ANTENNA 0x0020 #define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 #define HostCmd_CMD_MAC_CONTROL 0x0028 #define HostCmd_CMD_802_11_AD_HOC_START 0x002b #define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c #define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 #define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D #define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b #define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e #define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c #define HostCmd_CMD_WMM_GET_STATUS 0x0071 #define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 #define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 #define HostCmd_CMD_VERSION_EXT 0x0097 #define HostCmd_CMD_RSSI_INFO 0x00a4 #define HostCmd_CMD_FUNC_INIT 0x00a9 #define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa #define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0 #define HostCmd_CMD_UAP_BSS_START 0x00b1 #define HostCmd_CMD_UAP_BSS_STOP 0x00b2 #define HostCmd_CMD_11N_CFG 0x00cd #define HostCmd_CMD_11N_ADDBA_REQ 0x00ce #define HostCmd_CMD_11N_ADDBA_RSP 0x00cf #define HostCmd_CMD_11N_DELBA 0x00d0 #define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 #define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df #define HostCmd_CMD_TXPWR_CFG 0x00d1 #define HostCmd_CMD_TX_RATE_CFG 0x00d6 #define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 #define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed #define HostCmd_CMD_SET_BSS_MODE 0x00f7 #define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa #define PROTOCOL_NO_SECURITY 0x01 #define PROTOCOL_STATIC_WEP 0x02 #define PROTOCOL_WPA 0x08 #define PROTOCOL_WPA2 0x20 #define PROTOCOL_WPA2_MIXED 0x28 #define PROTOCOL_EAP 0x40 #define KEY_MGMT_NONE 0x04 #define KEY_MGMT_PSK 0x02 #define KEY_MGMT_EAP 0x01 #define CIPHER_TKIP 0x04 #define CIPHER_AES_CCMP 0x08 #define VALID_CIPHER_BITMAP 0x0c enum ENH_PS_MODES { EN_PS = 1, DIS_PS = 2, EN_AUTO_DS = 3, DIS_AUTO_DS = 4, SLEEP_CONFIRM = 5, GET_PS = 0, EN_AUTO_PS = 0xff, DIS_AUTO_PS = 0xfe, }; #define HostCmd_RET_BIT 0x8000 #define HostCmd_ACT_GEN_GET 0x0000 #define HostCmd_ACT_GEN_SET 0x0001 #define HostCmd_ACT_BITWISE_SET 0x0002 #define HostCmd_ACT_BITWISE_CLR 0x0003 #define HostCmd_RESULT_OK 0x0000 #define HostCmd_ACT_MAC_RX_ON 0x0001 #define HostCmd_ACT_MAC_TX_ON 0x0002 #define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 #define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 #define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 #define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 #define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 #define HostCmd_BSS_MODE_IBSS 0x0002 #define HostCmd_BSS_MODE_ANY 0x0003 #define HostCmd_SCAN_RADIO_TYPE_BG 0 #define HostCmd_SCAN_RADIO_TYPE_A 1 #define HOST_SLEEP_CFG_CANCEL 0xffffffff #define HOST_SLEEP_CFG_COND_DEF 0x00000000 #define HOST_SLEEP_CFG_GPIO_DEF 0xff #define HOST_SLEEP_CFG_GAP_DEF 0 #define CMD_F_HOSTCMD (1 << 0) #define CMD_F_CANCELED (1 << 1) #define HostCmd_CMD_ID_MASK 0x0fff #define HostCmd_SEQ_NUM_MASK 0x00ff #define HostCmd_BSS_NUM_MASK 0x0f00 #define HostCmd_BSS_TYPE_MASK 0xf000 #define HostCmd_ACT_SET_RX 0x0001 #define HostCmd_ACT_SET_TX 0x0002 #define HostCmd_ACT_SET_BOTH 0x0003 #define RF_ANTENNA_AUTO 0xFFFF #define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \ (((seq) & 0x00ff) | \ (((num) & 0x000f) << 8)) | \ (((type) & 0x000f) << 12); } #define HostCmd_GET_SEQ_NO(seq) \ ((seq) & HostCmd_SEQ_NUM_MASK) #define HostCmd_GET_BSS_NO(seq) \ (((seq) & HostCmd_BSS_NUM_MASK) >> 8) #define HostCmd_GET_BSS_TYPE(seq) \ (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) #define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 #define EVENT_LINK_LOST 0x00000003 #define EVENT_LINK_SENSED 0x00000004 #define EVENT_MIB_CHANGED 0x00000006 #define EVENT_INIT_DONE 0x00000007 #define EVENT_DEAUTHENTICATED 0x00000008 #define EVENT_DISASSOCIATED 0x00000009 #define EVENT_PS_AWAKE 0x0000000a #define EVENT_PS_SLEEP 0x0000000b #define EVENT_MIC_ERR_MULTICAST 0x0000000d #define EVENT_MIC_ERR_UNICAST 0x0000000e #define EVENT_DEEP_SLEEP_AWAKE 0x00000010 #define EVENT_ADHOC_BCN_LOST 0x00000011 #define EVENT_WMM_STATUS_CHANGE 0x00000017 #define EVENT_BG_SCAN_REPORT 0x00000018 #define EVENT_RSSI_LOW 0x00000019 #define EVENT_SNR_LOW 0x0000001a #define EVENT_MAX_FAIL 0x0000001b #define EVENT_RSSI_HIGH 0x0000001c #define EVENT_SNR_HIGH 0x0000001d #define EVENT_IBSS_COALESCED 0x0000001e #define EVENT_DATA_RSSI_LOW 0x00000024 #define EVENT_DATA_SNR_LOW 0x00000025 #define EVENT_DATA_RSSI_HIGH 0x00000026 #define EVENT_DATA_SNR_HIGH 0x00000027 #define EVENT_LINK_QUALITY 0x00000028 #define EVENT_PORT_RELEASE 0x0000002b #define EVENT_UAP_STA_DEAUTH 0x0000002c #define EVENT_UAP_STA_ASSOC 0x0000002d #define EVENT_UAP_BSS_START 0x0000002e #define EVENT_PRE_BEACON_LOST 0x00000031 #define EVENT_ADDBA 0x00000033 #define EVENT_DELBA 0x00000034 #define EVENT_BA_STREAM_TIEMOUT 0x00000037 #define EVENT_AMSDU_AGGR_CTRL 0x00000042 #define EVENT_UAP_BSS_IDLE 0x00000043 #define EVENT_UAP_BSS_ACTIVE 0x00000044 #define EVENT_WEP_ICV_ERR 0x00000046 #define EVENT_HS_ACT_REQ 0x00000047 #define EVENT_BW_CHANGE 0x00000048 #define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c #define EVENT_HOSTWAKE_STAIE 0x0000004d #define EVENT_ID_MASK 0xffff #define BSS_NUM_MASK 0xf #define EVENT_GET_BSS_NUM(event_cause) \ (((event_cause) >> 16) & BSS_NUM_MASK) #define EVENT_GET_BSS_TYPE(event_cause) \ (((event_cause) >> 24) & 0x00ff) struct mwifiex_ie_types_header { __le16 type; __le16 len; } __packed; struct mwifiex_ie_types_data { struct mwifiex_ie_types_header header; u8 data[1]; } __packed; #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 struct txpd { u8 bss_type; u8 bss_num; __le16 tx_pkt_length; __le16 tx_pkt_offset; __le16 tx_pkt_type; __le32 tx_control; u8 priority; u8 flags; u8 pkt_delay_2ms; u8 reserved1; } __packed; struct rxpd { u8 bss_type; u8 bss_num; __le16 rx_pkt_length; __le16 rx_pkt_offset; __le16 rx_pkt_type; __le16 seq_num; u8 priority; u8 rx_rate; s8 snr; s8 nf; /* Ht Info [Bit 0] RxRate format: LG=0, HT=1 * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ u8 ht_info; u8 reserved; } __packed; struct uap_txpd { u8 bss_type; u8 bss_num; __le16 tx_pkt_length; __le16 tx_pkt_offset; __le16 tx_pkt_type; __le32 tx_control; u8 priority; u8 flags; u8 pkt_delay_2ms; u8 reserved1; __le32 reserved2; }; struct uap_rxpd { u8 bss_type; u8 bss_num; __le16 rx_pkt_length; __le16 rx_pkt_offset; __le16 rx_pkt_type; __le16 seq_num; u8 priority; u8 reserved1; }; enum mwifiex_chan_scan_mode_bitmasks { MWIFIEX_PASSIVE_SCAN = BIT(0), MWIFIEX_DISABLE_CHAN_FILT = BIT(1), }; struct mwifiex_chan_scan_param_set { u8 radio_type; u8 chan_number; u8 chan_scan_mode_bitmap; __le16 min_scan_time; __le16 max_scan_time; } __packed; struct mwifiex_ie_types_chan_list_param_set { struct mwifiex_ie_types_header header; struct mwifiex_chan_scan_param_set chan_scan_param[1]; } __packed; struct chan_band_param_set { u8 radio_type; u8 chan_number; }; struct mwifiex_ie_types_chan_band_list_param_set { struct mwifiex_ie_types_header header; struct chan_band_param_set chan_band_param[1]; } __packed; struct mwifiex_ie_types_rates_param_set { struct mwifiex_ie_types_header header; u8 rates[1]; } __packed; struct mwifiex_ie_types_ssid_param_set { struct mwifiex_ie_types_header header; u8 ssid[1]; } __packed; struct mwifiex_ie_types_num_probes { struct mwifiex_ie_types_header header; __le16 num_probes; } __packed; struct mwifiex_ie_types_wildcard_ssid_params { struct mwifiex_ie_types_header header; u8 max_ssid_length; u8 ssid[1]; } __packed; #define TSF_DATA_SIZE 8 struct mwifiex_ie_types_tsf_timestamp { struct mwifiex_ie_types_header header; u8 tsf_data[1]; } __packed; struct mwifiex_cf_param_set { u8 cfp_cnt; u8 cfp_period; u16 cfp_max_duration; u16 cfp_duration_remaining; } __packed; struct mwifiex_ibss_param_set { u16 atim_window; } __packed; struct mwifiex_ie_types_ss_param_set { struct mwifiex_ie_types_header header; union { struct mwifiex_cf_param_set cf_param_set[1]; struct mwifiex_ibss_param_set ibss_param_set[1]; } cf_ibss; } __packed; struct mwifiex_fh_param_set { u16 dwell_time; u8 hop_set; u8 hop_pattern; u8 hop_index; } __packed; struct mwifiex_ds_param_set { u8 current_chan; } __packed; struct mwifiex_ie_types_phy_param_set { struct mwifiex_ie_types_header header; union { struct mwifiex_fh_param_set fh_param_set[1]; struct mwifiex_ds_param_set ds_param_set[1]; } fh_ds; } __packed; struct mwifiex_ie_types_auth_type { struct mwifiex_ie_types_header header; __le16 auth_type; } __packed; struct mwifiex_ie_types_vendor_param_set { struct mwifiex_ie_types_header header; u8 ie[MWIFIEX_MAX_VSIE_LEN]; }; struct mwifiex_ie_types_rsn_param_set { struct mwifiex_ie_types_header header; u8 rsn_ie[1]; } __packed; #define KEYPARAMSET_FIXED_LEN 6 struct mwifiex_ie_type_key_param_set { __le16 type; __le16 length; __le16 key_type_id; __le16 key_info; __le16 key_len; u8 key[50]; } __packed; #define IGTK_PN_LEN 8 struct mwifiex_cmac_param { u8 ipn[IGTK_PN_LEN]; u8 key[WLAN_KEY_LEN_AES_CMAC]; } __packed; struct host_cmd_ds_802_11_key_material { __le16 action; struct mwifiex_ie_type_key_param_set key_param_set; } __packed; struct host_cmd_ds_gen { u16 command; u16 size; u16 seq_num; u16 result; }; #define S_DS_GEN sizeof(struct host_cmd_ds_gen) enum sleep_resp_ctrl { RESP_NOT_NEEDED = 0, RESP_NEEDED, }; struct mwifiex_ps_param { __le16 null_pkt_interval; __le16 multiple_dtims; __le16 bcn_miss_timeout; __le16 local_listen_interval; __le16 adhoc_wake_period; __le16 mode; __le16 delay_to_ps; }; #define BITMAP_AUTO_DS 0x01 #define BITMAP_STA_PS 0x10 struct mwifiex_ie_types_auto_ds_param { struct mwifiex_ie_types_header header; __le16 deep_sleep_timeout; } __packed; struct mwifiex_ie_types_ps_param { struct mwifiex_ie_types_header header; struct mwifiex_ps_param param; } __packed; struct host_cmd_ds_802_11_ps_mode_enh { __le16 action; union { struct mwifiex_ps_param opt_ps; __le16 ps_bitmap; } params; } __packed; struct host_cmd_ds_get_hw_spec { __le16 hw_if_version; __le16 version; __le16 reserved; __le16 num_of_mcast_adr; u8 permanent_addr[ETH_ALEN]; __le16 region_code; __le16 number_of_antenna; __le32 fw_release_number; __le32 reserved_1; __le32 reserved_2; __le32 reserved_3; __le32 fw_cap_info; __le32 dot_11n_dev_cap; u8 dev_mcs_support; __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ __le16 reserved_4; } __packed; struct host_cmd_ds_802_11_rssi_info { __le16 action; __le16 ndata; __le16 nbcn; __le16 reserved[9]; long long reserved_1; }; struct host_cmd_ds_802_11_rssi_info_rsp { __le16 action; __le16 ndata; __le16 nbcn; __le16 data_rssi_last; __le16 data_nf_last; __le16 data_rssi_avg; __le16 data_nf_avg; __le16 bcn_rssi_last; __le16 bcn_nf_last; __le16 bcn_rssi_avg; __le16 bcn_nf_avg; long long tsf_bcn; }; struct host_cmd_ds_802_11_mac_address { __le16 action; u8 mac_addr[ETH_ALEN]; }; struct host_cmd_ds_mac_control { __le16 action; __le16 reserved; }; struct host_cmd_ds_mac_multicast_adr { __le16 action; __le16 num_of_adrs; u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; } __packed; struct host_cmd_ds_802_11_deauthenticate { u8 mac_addr[ETH_ALEN]; __le16 reason_code; } __packed; struct host_cmd_ds_802_11_associate { u8 peer_sta_addr[ETH_ALEN]; __le16 cap_info_bitmap; __le16 listen_interval; __le16 beacon_period; u8 dtim_period; } __packed; struct ieee_types_assoc_rsp { __le16 cap_info_bitmap; __le16 status_code; __le16 a_id; u8 ie_buffer[1]; } __packed; struct host_cmd_ds_802_11_associate_rsp { struct ieee_types_assoc_rsp assoc_rsp; } __packed; struct ieee_types_cf_param_set { u8 element_id; u8 len; u8 cfp_cnt; u8 cfp_period; u16 cfp_max_duration; u16 cfp_duration_remaining; } __packed; struct ieee_types_ibss_param_set { u8 element_id; u8 len; __le16 atim_window; } __packed; union ieee_types_ss_param_set { struct ieee_types_cf_param_set cf_param_set; struct ieee_types_ibss_param_set ibss_param_set; } __packed; struct ieee_types_fh_param_set { u8 element_id; u8 len; __le16 dwell_time; u8 hop_set; u8 hop_pattern; u8 hop_index; } __packed; struct ieee_types_ds_param_set { u8 element_id; u8 len; u8 current_chan; } __packed; union ieee_types_phy_param_set { struct ieee_types_fh_param_set fh_param_set; struct ieee_types_ds_param_set ds_param_set; } __packed; struct host_cmd_ds_802_11_ad_hoc_start { u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 bss_mode; __le16 beacon_period; u8 dtim_period; union ieee_types_ss_param_set ss_param_set; union ieee_types_phy_param_set phy_param_set; u16 reserved1; __le16 cap_info_bitmap; u8 data_rate[HOSTCMD_SUPPORTED_RATES]; } __packed; struct host_cmd_ds_802_11_ad_hoc_result { u8 pad[3]; u8 bssid[ETH_ALEN]; } __packed; struct adhoc_bss_desc { u8 bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 bss_mode; __le16 beacon_period; u8 dtim_period; u8 time_stamp[8]; u8 local_time[8]; union ieee_types_phy_param_set phy_param_set; union ieee_types_ss_param_set ss_param_set; __le16 cap_info_bitmap; u8 data_rates[HOSTCMD_SUPPORTED_RATES]; /* * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. * It is used in the Adhoc join command and will cause a * binary layout mismatch with the firmware */ } __packed; struct host_cmd_ds_802_11_ad_hoc_join { struct adhoc_bss_desc bss_descriptor; u16 reserved1; u16 reserved2; } __packed; struct host_cmd_ds_802_11_get_log { __le32 mcast_tx_frame; __le32 failed; __le32 retry; __le32 multi_retry; __le32 frame_dup; __le32 rts_success; __le32 rts_failure; __le32 ack_failure; __le32 rx_frag; __le32 mcast_rx_frame; __le32 fcs_error; __le32 tx_frame; __le32 reserved; __le32 wep_icv_err_cnt[4]; }; struct host_cmd_ds_tx_rate_query { u8 tx_rate; /* Ht Info [Bit 0] RxRate format: LG=0, HT=1 * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ u8 ht_info; } __packed; enum Host_Sleep_Action { HS_CONFIGURE = 0x0001, HS_ACTIVATE = 0x0002, }; struct mwifiex_hs_config_param { __le32 conditions; u8 gpio; u8 gap; } __packed; struct hs_activate_param { u16 resp_ctrl; } __packed; struct host_cmd_ds_802_11_hs_cfg_enh { __le16 action; union { struct mwifiex_hs_config_param hs_config; struct hs_activate_param hs_activate; } params; } __packed; enum SNMP_MIB_INDEX { OP_RATE_SET_I = 1, DTIM_PERIOD_I = 3, RTS_THRESH_I = 5, SHORT_RETRY_LIM_I = 6, LONG_RETRY_LIM_I = 7, FRAG_THRESH_I = 8, DOT11D_I = 9, }; #define MAX_SNMP_BUF_SIZE 128 struct host_cmd_ds_802_11_snmp_mib { __le16 query_type; __le16 oid; __le16 buf_size; u8 value[1]; } __packed; struct mwifiex_rate_scope { __le16 type; __le16 length; __le16 hr_dsss_rate_bitmap; __le16 ofdm_rate_bitmap; __le16 ht_mcs_rate_bitmap[8]; } __packed; struct mwifiex_rate_drop_pattern { __le16 type; __le16 length; __le32 rate_drop_mode; } __packed; struct host_cmd_ds_tx_rate_cfg { __le16 action; __le16 cfg_index; } __packed; struct mwifiex_power_group { u8 modulation_class; u8 first_rate_code; u8 last_rate_code; s8 power_step; s8 power_min; s8 power_max; u8 ht_bandwidth; u8 reserved; } __packed; struct mwifiex_types_power_group { u16 type; u16 length; } __packed; struct host_cmd_ds_txpwr_cfg { __le16 action; __le16 cfg_index; __le32 mode; } __packed; struct host_cmd_ds_rf_tx_pwr { __le16 action; __le16 cur_level; u8 max_power; u8 min_power; } __packed; struct host_cmd_ds_rf_ant_mimo { __le16 action_tx; __le16 tx_ant_mode; __le16 action_rx; __le16 rx_ant_mode; }; struct host_cmd_ds_rf_ant_siso { __le16 action; __le16 ant_mode; }; struct mwifiex_bcn_param { u8 bssid[ETH_ALEN]; u8 rssi; __le64 timestamp; __le16 beacon_period; __le16 cap_info_bitmap; } __packed; #define MWIFIEX_USER_SCAN_CHAN_MAX 50 #define MWIFIEX_MAX_SSID_LIST_LENGTH 10 struct mwifiex_scan_cmd_config { /* * BSS mode to be sent in the firmware command */ u8 bss_mode; /* Specific BSSID used to filter scan results in the firmware */ u8 specific_bssid[ETH_ALEN]; /* Length of TLVs sent in command starting at tlvBuffer */ u32 tlv_buf_len; /* * SSID TLV(s) and ChanList TLVs to be sent in the firmware command * * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set */ u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored here */ } __packed; struct mwifiex_user_scan_chan { u8 chan_number; u8 radio_type; u8 scan_type; u8 reserved; u32 scan_time; } __packed; struct mwifiex_user_scan_cfg { /* * BSS mode to be sent in the firmware command */ u8 bss_mode; /* Configure the number of probe requests for active chan scans */ u8 num_probes; u8 reserved; /* BSSID filter sent in the firmware command to limit the results */ u8 specific_bssid[ETH_ALEN]; /* SSID filter list used in the firmware to limit the scan results */ struct cfg80211_ssid *ssid_list; u8 num_ssids; /* Variable number (fixed maximum) of channels to scan up */ struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX]; } __packed; struct ie_body { u8 grp_key_oui[4]; u8 ptk_cnt[2]; u8 ptk_body[4]; } __packed; struct host_cmd_ds_802_11_scan { u8 bss_mode; u8 bssid[ETH_ALEN]; u8 tlv_buffer[1]; } __packed; struct host_cmd_ds_802_11_scan_rsp { __le16 bss_descript_size; u8 number_of_sets; u8 bss_desc_and_tlv_buffer[1]; } __packed; struct host_cmd_ds_802_11_bg_scan_query { u8 flush; } __packed; struct host_cmd_ds_802_11_bg_scan_query_rsp { u32 report_condition; struct host_cmd_ds_802_11_scan_rsp scan_resp; } __packed; struct mwifiex_ietypes_domain_param_set { struct mwifiex_ie_types_header header; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; struct ieee80211_country_ie_triplet triplet[1]; } __packed; struct host_cmd_ds_802_11d_domain_info { __le16 action; struct mwifiex_ietypes_domain_param_set domain; } __packed; struct host_cmd_ds_802_11d_domain_info_rsp { __le16 action; struct mwifiex_ietypes_domain_param_set domain; } __packed; struct host_cmd_ds_11n_addba_req { u8 add_req_result; u8 peer_mac_addr[ETH_ALEN]; u8 dialog_token; __le16 block_ack_param_set; __le16 block_ack_tmo; __le16 ssn; } __packed; struct host_cmd_ds_11n_addba_rsp { u8 add_rsp_result; u8 peer_mac_addr[ETH_ALEN]; u8 dialog_token; __le16 status_code; __le16 block_ack_param_set; __le16 block_ack_tmo; __le16 ssn; } __packed; struct host_cmd_ds_11n_delba { u8 del_result; u8 peer_mac_addr[ETH_ALEN]; __le16 del_ba_param_set; __le16 reason_code; u8 reserved; } __packed; struct host_cmd_ds_11n_batimeout { u8 tid; u8 peer_mac_addr[ETH_ALEN]; u8 origninator; } __packed; struct host_cmd_ds_11n_cfg { __le16 action; __le16 ht_tx_cap; __le16 ht_tx_info; } __packed; struct host_cmd_ds_txbuf_cfg { __le16 action; __le16 buff_size; __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ __le16 reserved3; } __packed; struct host_cmd_ds_amsdu_aggr_ctrl { __le16 action; __le16 enable; __le16 curr_buf_size; } __packed; struct mwifiex_ie_types_wmm_param_set { struct mwifiex_ie_types_header header; u8 wmm_ie[1]; }; struct mwifiex_ie_types_wmm_queue_status { struct mwifiex_ie_types_header header; u8 queue_index; u8 disabled; u16 medium_time; u8 flow_required; u8 flow_created; u32 reserved; }; struct ieee_types_vendor_header { u8 element_id; u8 len; u8 oui[4]; /* 0~2: oui, 3: oui_type */ u8 oui_subtype; u8 version; } __packed; struct ieee_types_wmm_ac_parameters { u8 aci_aifsn_bitmap; u8 ecw_bitmap; __le16 tx_op_limit; } __packed; struct ieee_types_wmm_parameter { /* * WMM Parameter IE - Vendor Specific Header: * element_id [221/0xdd] * Len [24] * Oui [00:50:f2] * OuiType [2] * OuiSubType [1] * Version [1] */ struct ieee_types_vendor_header vend_hdr; u8 qos_info_bitmap; u8 reserved; struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; } __packed; struct ieee_types_wmm_info { /* * WMM Info IE - Vendor Specific Header: * element_id [221/0xdd] * Len [7] * Oui [00:50:f2] * OuiType [2] * OuiSubType [0] * Version [1] */ struct ieee_types_vendor_header vend_hdr; u8 qos_info_bitmap; } __packed; struct host_cmd_ds_wmm_get_status { u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) * IEEE80211_NUM_ACS]; u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2]; } __packed; struct mwifiex_wmm_ac_status { u8 disabled; u8 flow_required; u8 flow_created; }; struct mwifiex_ie_types_htcap { struct mwifiex_ie_types_header header; struct ieee80211_ht_cap ht_cap; } __packed; struct mwifiex_ie_types_htinfo { struct mwifiex_ie_types_header header; struct ieee80211_ht_operation ht_oper; } __packed; struct mwifiex_ie_types_2040bssco { struct mwifiex_ie_types_header header; u8 bss_co_2040; } __packed; struct mwifiex_ie_types_extcap { struct mwifiex_ie_types_header header; u8 ext_cap; } __packed; struct host_cmd_ds_mac_reg_access { __le16 action; __le16 offset; __le32 value; } __packed; struct host_cmd_ds_bbp_reg_access { __le16 action; __le16 offset; u8 value; u8 reserved[3]; } __packed; struct host_cmd_ds_rf_reg_access { __le16 action; __le16 offset; u8 value; u8 reserved[3]; } __packed; struct host_cmd_ds_pmic_reg_access { __le16 action; __le16 offset; u8 value; u8 reserved[3]; } __packed; struct host_cmd_ds_802_11_eeprom_access { __le16 action; __le16 offset; __le16 byte_count; u8 value; } __packed; struct host_cmd_tlv { __le16 type; __le16 len; } __packed; struct mwifiex_assoc_event { u8 sta_addr[ETH_ALEN]; __le16 type; __le16 len; __le16 frame_control; __le16 cap_info; __le16 listen_interval; u8 data[0]; } __packed; struct host_cmd_ds_sys_config { __le16 action; u8 tlv[0]; }; struct host_cmd_tlv_akmp { struct host_cmd_tlv tlv; __le16 key_mgmt; __le16 key_mgmt_operation; } __packed; struct host_cmd_tlv_pwk_cipher { struct host_cmd_tlv tlv; __le16 proto; u8 cipher; u8 reserved; } __packed; struct host_cmd_tlv_gwk_cipher { struct host_cmd_tlv tlv; u8 cipher; u8 reserved; } __packed; struct host_cmd_tlv_passphrase { struct host_cmd_tlv tlv; u8 passphrase[0]; } __packed; struct host_cmd_tlv_wep_key { struct host_cmd_tlv tlv; u8 key_index; u8 is_default; u8 key[1]; }; struct host_cmd_tlv_auth_type { struct host_cmd_tlv tlv; u8 auth_type; } __packed; struct host_cmd_tlv_encrypt_protocol { struct host_cmd_tlv tlv; __le16 proto; } __packed; struct host_cmd_tlv_ssid { struct host_cmd_tlv tlv; u8 ssid[0]; } __packed; struct host_cmd_tlv_rates { struct host_cmd_tlv tlv; u8 rates[0]; } __packed; struct host_cmd_tlv_bcast_ssid { struct host_cmd_tlv tlv; u8 bcast_ctl; } __packed; struct host_cmd_tlv_beacon_period { struct host_cmd_tlv tlv; __le16 period; } __packed; struct host_cmd_tlv_dtim_period { struct host_cmd_tlv tlv; u8 period; } __packed; struct host_cmd_tlv_frag_threshold { struct host_cmd_tlv tlv; __le16 frag_thr; } __packed; struct host_cmd_tlv_rts_threshold { struct host_cmd_tlv tlv; __le16 rts_thr; } __packed; struct host_cmd_tlv_retry_limit { struct host_cmd_tlv tlv; u8 limit; } __packed; struct host_cmd_tlv_mac_addr { struct host_cmd_tlv tlv; u8 mac_addr[ETH_ALEN]; } __packed; struct host_cmd_tlv_channel_band { struct host_cmd_tlv tlv; u8 band_config; u8 channel; } __packed; struct host_cmd_ds_version_ext { u8 version_str_sel; char version_str[128]; } __packed; struct host_cmd_ds_802_11_ibss_status { __le16 action; __le16 enable; u8 bssid[ETH_ALEN]; __le16 beacon_interval; __le16 atim_window; __le16 use_g_rate_protect; } __packed; #define CONNECTION_TYPE_INFRA 0 #define CONNECTION_TYPE_ADHOC 1 struct host_cmd_ds_set_bss_mode { u8 con_type; } __packed; struct host_cmd_ds_pcie_details { /* TX buffer descriptor ring address */ u32 txbd_addr_lo; u32 txbd_addr_hi; /* TX buffer descriptor ring count */ u32 txbd_count; /* RX buffer descriptor ring address */ u32 rxbd_addr_lo; u32 rxbd_addr_hi; /* RX buffer descriptor ring count */ u32 rxbd_count; /* Event buffer descriptor ring address */ u32 evtbd_addr_lo; u32 evtbd_addr_hi; /* Event buffer descriptor ring count */ u32 evtbd_count; /* Sleep cookie buffer physical address */ u32 sleep_cookie_addr_lo; u32 sleep_cookie_addr_hi; } __packed; struct mwifiex_ie_types_rssi_threshold { struct mwifiex_ie_types_header header; u8 abs_value; u8 evt_freq; } __packed; struct host_cmd_ds_802_11_subsc_evt { __le16 action; __le16 events; } __packed; struct mwifiex_ie { __le16 ie_index; __le16 mgmt_subtype_mask; __le16 ie_length; u8 ie_buffer[IEEE_MAX_IE_SIZE]; } __packed; #define MAX_MGMT_IE_INDEX 16 struct mwifiex_ie_list { __le16 type; __le16 len; struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; } __packed; struct host_cmd_ds_command { __le16 command; __le16 size; __le16 seq_num; __le16 result; union { struct host_cmd_ds_get_hw_spec hw_spec; struct host_cmd_ds_mac_control mac_ctrl; struct host_cmd_ds_802_11_mac_address mac_addr; struct host_cmd_ds_mac_multicast_adr mc_addr; struct host_cmd_ds_802_11_get_log get_log; struct host_cmd_ds_802_11_rssi_info rssi_info; struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; struct host_cmd_ds_802_11_snmp_mib smib; struct host_cmd_ds_tx_rate_query tx_rate; struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; struct host_cmd_ds_txpwr_cfg txp_cfg; struct host_cmd_ds_rf_tx_pwr txp; struct host_cmd_ds_rf_ant_mimo ant_mimo; struct host_cmd_ds_rf_ant_siso ant_siso; struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; struct host_cmd_ds_802_11_scan scan; struct host_cmd_ds_802_11_scan_rsp scan_resp; struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; struct host_cmd_ds_802_11_associate associate; struct host_cmd_ds_802_11_associate_rsp associate_rsp; struct host_cmd_ds_802_11_deauthenticate deauth; struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; struct host_cmd_ds_802_11d_domain_info domain_info; struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; struct host_cmd_ds_11n_addba_req add_ba_req; struct host_cmd_ds_11n_addba_rsp add_ba_rsp; struct host_cmd_ds_11n_delba del_ba; struct host_cmd_ds_txbuf_cfg tx_buf; struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; struct host_cmd_ds_11n_cfg htcfg; struct host_cmd_ds_wmm_get_status get_wmm_status; struct host_cmd_ds_802_11_key_material key_material; struct host_cmd_ds_version_ext verext; struct host_cmd_ds_802_11_ibss_status ibss_coalescing; struct host_cmd_ds_mac_reg_access mac_reg; struct host_cmd_ds_bbp_reg_access bbp_reg; struct host_cmd_ds_rf_reg_access rf_reg; struct host_cmd_ds_pmic_reg_access pmic_reg; struct host_cmd_ds_set_bss_mode bss_mode; struct host_cmd_ds_pcie_details pcie_host_spec; struct host_cmd_ds_802_11_eeprom_access eeprom; struct host_cmd_ds_802_11_subsc_evt subsc_evt; struct host_cmd_ds_sys_config uap_sys_config; } params; } __packed; struct mwifiex_opt_sleep_confirm { __le16 command; __le16 size; __le16 seq_num; __le16 result; __le16 action; __le16 resp_ctrl; } __packed; #endif /* !_MWIFIEX_FW_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/decl.h0000644000175000017500000000621612026211315023216 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: generic data structures and APIs * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_DECL_H_ #define _MWIFIEX_DECL_H_ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #define MWIFIEX_MAX_BSS_NUM (2) #define MWIFIEX_MIN_DATA_HEADER_LEN 36 /* sizeof(mwifiex_txpd) * + 4 byte alignment */ #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 #define MWIFIEX_AMPDU_DEF_TXWINSIZE 32 #define MWIFIEX_AMPDU_DEF_RXWINSIZE 16 #define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff #define MWIFIEX_RATE_BITMAP_MCS0 32 #define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024) #define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024) #define MAX_BEACON_PERIOD (4000) #define MIN_BEACON_PERIOD (50) #define MAX_DTIM_PERIOD (100) #define MIN_DTIM_PERIOD (1) #define MWIFIEX_RTS_MIN_VALUE (0) #define MWIFIEX_RTS_MAX_VALUE (2347) #define MWIFIEX_FRAG_MIN_VALUE (256) #define MWIFIEX_FRAG_MAX_VALUE (2346) #define MWIFIEX_RETRY_LIMIT 14 #define MWIFIEX_SDIO_BLOCK_SIZE 256 #define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) #define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) #define MWIFIEX_BRIDGED_PKTS_THRESHOLD 1024 enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, MWIFIEX_BSS_TYPE_ANY = 0xff, }; enum mwifiex_bss_role { MWIFIEX_BSS_ROLE_STA = 0, MWIFIEX_BSS_ROLE_UAP = 1, MWIFIEX_BSS_ROLE_ANY = 0xff, }; #define BSS_ROLE_BIT_MASK BIT(0) #define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) enum mwifiex_data_frame_type { MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0, MWIFIEX_DATA_FRAME_TYPE_802_11, }; struct mwifiex_fw_image { u8 *helper_buf; u32 helper_len; u8 *fw_buf; u32 fw_len; }; struct mwifiex_802_11_ssid { u32 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; }; struct mwifiex_wait_queue { wait_queue_head_t wait; int status; }; struct mwifiex_rxinfo { u8 bss_num; u8 bss_type; struct sk_buff *parent; u8 use_count; }; struct mwifiex_txinfo { u32 status_code; u8 flags; u8 bss_num; u8 bss_type; }; enum mwifiex_wmm_ac_e { WMM_AC_BK, WMM_AC_BE, WMM_AC_VI, WMM_AC_VO } __packed; #endif /* !_MWIFIEX_DECL_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/cmdevt.c0000644000175000017500000013377112026211315023573 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: commands and events * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* * This function initializes a command node. * * The actual allocation of the node is not done by this function. It only * initiates a node by filling it with default parameters. Similarly, * allocation of the different buffers used (IOCTL buffer, data buffer) are * not done by this function either. */ static void mwifiex_init_cmd_node(struct mwifiex_private *priv, struct cmd_ctrl_node *cmd_node, u32 cmd_oid, void *data_buf) { cmd_node->priv = priv; cmd_node->cmd_oid = cmd_oid; if (priv->adapter->cmd_wait_q_required) { cmd_node->wait_q_enabled = priv->adapter->cmd_wait_q_required; priv->adapter->cmd_wait_q_required = false; cmd_node->cmd_wait_q_woken = false; cmd_node->condition = &cmd_node->cmd_wait_q_woken; } cmd_node->data_buf = data_buf; cmd_node->cmd_skb = cmd_node->skb; } /* * This function returns a command node from the free queue depending upon * availability. */ static struct cmd_ctrl_node * mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node; unsigned long flags; spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); if (list_empty(&adapter->cmd_free_q)) { dev_err(adapter->dev, "GET_CMD_NODE: cmd node not available\n"); spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); return NULL; } cmd_node = list_first_entry(&adapter->cmd_free_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); return cmd_node; } /* * This function cleans up a command node. * * The function resets the fields including the buffer pointers. * This function does not try to free the buffers. They must be * freed before calling this function. * * This function will however call the receive completion callback * in case a response buffer is still available before resetting * the pointer. */ static void mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node) { cmd_node->cmd_oid = 0; cmd_node->cmd_flag = 0; cmd_node->data_buf = NULL; cmd_node->wait_q_enabled = false; if (cmd_node->cmd_skb) skb_trim(cmd_node->cmd_skb, 0); if (cmd_node->resp_skb) { adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb); cmd_node->resp_skb = NULL; } } /* * This function sends a host command to the firmware. * * The function copies the host command into the driver command * buffer, which will be transferred to the firmware later by the * main thread. */ static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct mwifiex_ds_misc_cmd *pcmd_ptr) { /* Copy the HOST command to command buffer */ memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len); dev_dbg(priv->adapter->dev, "cmd: host cmd size = %d\n", pcmd_ptr->len); return 0; } /* * This function downloads a command to the firmware. * * The function performs sanity tests, sets the command sequence * number and size, converts the header fields to CPU format before * sending. Afterwards, it logs the command ID and action for debugging * and sets up the command timeout timer. */ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, struct cmd_ctrl_node *cmd_node) { struct mwifiex_adapter *adapter = priv->adapter; int ret; struct host_cmd_ds_command *host_cmd; uint16_t cmd_code; uint16_t cmd_size; struct timeval tstamp; unsigned long flags; __le32 tmp; if (!adapter || !cmd_node) return -1; host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); /* Sanity test */ if (host_cmd == NULL || host_cmd->size == 0) { dev_err(adapter->dev, "DNLD_CMD: host_cmd is null" " or cmd size is 0, not sending\n"); if (cmd_node->wait_q_enabled) adapter->cmd_wait_q.status = -1; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); return -1; } /* Set command sequence number */ adapter->seq_num++; host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO (adapter->seq_num, cmd_node->priv->bss_num, cmd_node->priv->bss_type)); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd = cmd_node; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); cmd_code = le16_to_cpu(host_cmd->command); cmd_size = le16_to_cpu(host_cmd->size); /* Adjust skb length */ if (cmd_node->cmd_skb->len > cmd_size) /* * cmd_size is less than sizeof(struct host_cmd_ds_command). * Trim off the unused portion. */ skb_trim(cmd_node->cmd_skb, cmd_size); else if (cmd_node->cmd_skb->len < cmd_size) /* * cmd_size is larger than sizeof(struct host_cmd_ds_command) * because we have appended custom IE TLV. Increase skb length * accordingly. */ skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); do_gettimeofday(&tstamp); dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d," " seqno %#x\n", tstamp.tv_sec, tstamp.tv_usec, cmd_code, le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)), cmd_size, le16_to_cpu(host_cmd->seq_num)); if (adapter->iface_type == MWIFIEX_USB) { tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); memcpy(cmd_node->cmd_skb->data, &tmp, MWIFIEX_TYPE_LEN); adapter->cmd_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_CMD_EVENT, cmd_node->cmd_skb, NULL); skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); if (ret == -EBUSY) cmd_node->cmd_skb = NULL; } else { skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, cmd_node->cmd_skb, NULL); skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN); } if (ret == -1) { dev_err(adapter->dev, "DNLD_CMD: host to card failed\n"); if (adapter->iface_type == MWIFIEX_USB) adapter->cmd_sent = false; if (cmd_node->wait_q_enabled) adapter->cmd_wait_q.status = -1; mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd = NULL; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); adapter->dbg.num_cmd_host_to_card_failure++; return -1; } /* Save the last command id and action to debug log */ adapter->dbg.last_cmd_index = (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); /* Clear BSS_NO_BITS from HostCmd */ cmd_code &= HostCmd_CMD_ID_MASK; /* Setup the timer after transmit command */ mod_timer(&adapter->cmd_timer, jiffies + (MWIFIEX_TIMER_10S * HZ) / 1000); return 0; } /* * This function downloads a sleep confirm command to the firmware. * * The function performs sanity tests, sets the command sequence * number and size, converts the header fields to CPU format before * sending. * * No responses are needed for sleep confirm command. */ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) { int ret; struct mwifiex_private *priv; struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) adapter->sleep_cfm->data; struct sk_buff *sleep_cfm_tmp; __le32 tmp; priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); sleep_cfm_buf->seq_num = cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO (adapter->seq_num, priv->bss_num, priv->bss_type))); adapter->seq_num++; if (adapter->iface_type == MWIFIEX_USB) { sleep_cfm_tmp = dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) + MWIFIEX_TYPE_LEN); skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm) + MWIFIEX_TYPE_LEN); tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); memcpy(sleep_cfm_tmp->data, &tmp, MWIFIEX_TYPE_LEN); memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN, adapter->sleep_cfm->data, sizeof(struct mwifiex_opt_sleep_confirm)); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_CMD_EVENT, sleep_cfm_tmp, NULL); if (ret != -EBUSY) dev_kfree_skb_any(sleep_cfm_tmp); } else { skb_push(adapter->sleep_cfm, INTF_HEADER_LEN); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, adapter->sleep_cfm, NULL); skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN); } if (ret == -1) { dev_err(adapter->dev, "SLEEP_CFM: failed\n"); adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; return -1; } if (GET_BSS_ROLE(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY)) == MWIFIEX_BSS_ROLE_STA) { if (!sleep_cfm_buf->resp_ctrl) /* Response is not needed for sleep confirm command */ adapter->ps_state = PS_STATE_SLEEP; else adapter->ps_state = PS_STATE_SLEEP_CFM; if (!sleep_cfm_buf->resp_ctrl && (adapter->is_hs_configured && !adapter->sleep_period.period)) { adapter->pm_wakeup_card_req = true; mwifiex_hs_activated_event(mwifiex_get_priv (adapter, MWIFIEX_BSS_ROLE_STA), true); } } return ret; } /* * This function allocates the command buffers and links them to * the command free queue. * * The driver uses a pre allocated number of command buffers, which * are created at driver initializations and freed at driver cleanup. * Every command needs to obtain a command buffer from this pool before * it can be issued. The command free queue lists the command buffers * currently free to use, while the command pending queue lists the * command buffers already in use and awaiting handling. Command buffers * are returned to the free queue after use. */ int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_array; u32 buf_size; u32 i; /* Allocate and initialize struct cmd_ctrl_node */ buf_size = sizeof(struct cmd_ctrl_node) * MWIFIEX_NUM_OF_CMD_BUFFER; cmd_array = kzalloc(buf_size, GFP_KERNEL); if (!cmd_array) { dev_err(adapter->dev, "%s: failed to alloc cmd_array\n", __func__); return -ENOMEM; } adapter->cmd_pool = cmd_array; memset(adapter->cmd_pool, 0, buf_size); /* Allocate and initialize command buffers */ for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); if (!cmd_array[i].skb) { dev_err(adapter->dev, "ALLOC_CMD_BUF: out of memory\n"); return -1; } } for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]); return 0; } /* * This function frees the command buffers. * * The function calls the completion callback for all the command * buffers that still have response buffers associated with them. */ int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_array; u32 i; /* Need to check if cmd pool is allocated or not */ if (!adapter->cmd_pool) { dev_dbg(adapter->dev, "info: FREE_CMD_BUF: cmd_pool is null\n"); return 0; } cmd_array = adapter->cmd_pool; /* Release shared memory buffers */ for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { if (cmd_array[i].skb) { dev_dbg(adapter->dev, "cmd: free cmd buffer %d\n", i); dev_kfree_skb_any(cmd_array[i].skb); } if (!cmd_array[i].resp_skb) continue; if (adapter->iface_type == MWIFIEX_USB) adapter->if_ops.cmdrsp_complete(adapter, cmd_array[i].resp_skb); else dev_kfree_skb_any(cmd_array[i].resp_skb); } /* Release struct cmd_ctrl_node */ if (adapter->cmd_pool) { dev_dbg(adapter->dev, "cmd: free cmd pool\n"); kfree(adapter->cmd_pool); adapter->cmd_pool = NULL; } return 0; } /* * This function handles events generated by firmware. * * Event body of events received from firmware are not used (though they are * saved), only the event ID is used. Some events are re-invoked by * the driver, with a new event body. * * After processing, the function calls the completion callback * for cleanup. */ int mwifiex_process_event(struct mwifiex_adapter *adapter) { int ret; struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); struct sk_buff *skb = adapter->event_skb; u32 eventcause = adapter->event_cause; struct timeval tstamp; struct mwifiex_rxinfo *rx_info; /* Save the last event to debug log */ adapter->dbg.last_event_index = (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; adapter->dbg.last_event[adapter->dbg.last_event_index] = (u16) eventcause; /* Get BSS number and corresponding priv */ priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), EVENT_GET_BSS_TYPE(eventcause)); if (!priv) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); /* Clear BSS_NO_BITS from event */ eventcause &= EVENT_ID_MASK; adapter->event_cause = eventcause; if (skb) { rx_info = MWIFIEX_SKB_RXCB(skb); rx_info->bss_num = priv->bss_num; rx_info->bss_type = priv->bss_type; } if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) { do_gettimeofday(&tstamp); dev_dbg(adapter->dev, "event: %lu.%lu: cause: %#x\n", tstamp.tv_sec, tstamp.tv_usec, eventcause); } else { /* Handle PS_SLEEP/AWAKE events on STA */ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); if (!priv) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); } if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) ret = mwifiex_process_uap_event(priv); else ret = mwifiex_process_sta_event(priv); adapter->event_cause = 0; adapter->event_skb = NULL; adapter->if_ops.event_complete(adapter, skb); return ret; } /* * This function is used to send synchronous command to the firmware. * * it allocates a wait queue for the command and wait for the command * response. */ int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no, u16 cmd_action, u32 cmd_oid, void *data_buf) { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; adapter->cmd_wait_q_required = true; ret = mwifiex_send_cmd_async(priv, cmd_no, cmd_action, cmd_oid, data_buf); if (!ret) ret = mwifiex_wait_queue_complete(adapter); return ret; } /* * This function prepares a command and asynchronously send it to the firmware. * * Preparation includes - * - Sanity tests to make sure the card is still present or the FW * is not reset * - Getting a new command node from the command free queue * - Initializing the command node for default parameters * - Fill up the non-default parameters and buffer pointers * - Add the command to pending queue */ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, u16 cmd_action, u32 cmd_oid, void *data_buf) { int ret; struct mwifiex_adapter *adapter = priv->adapter; struct cmd_ctrl_node *cmd_node; struct host_cmd_ds_command *cmd_ptr; if (!adapter) { pr_err("PREP_CMD: adapter is NULL\n"); return -1; } if (adapter->is_suspended) { dev_err(adapter->dev, "PREP_CMD: device in suspended state\n"); return -1; } if (adapter->surprise_removed) { dev_err(adapter->dev, "PREP_CMD: card is removed\n"); return -1; } if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { if (cmd_no != HostCmd_CMD_FUNC_INIT) { dev_err(adapter->dev, "PREP_CMD: FW in reset state\n"); return -1; } } /* Get a new command node */ cmd_node = mwifiex_get_cmd_node(adapter); if (!cmd_node) { dev_err(adapter->dev, "PREP_CMD: no free cmd node\n"); return -1; } /* Initialize the command node */ mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf); if (!cmd_node->cmd_skb) { dev_err(adapter->dev, "PREP_CMD: no free cmd buf\n"); return -1; } memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)), 0, sizeof(struct host_cmd_ds_command)); cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); cmd_ptr->command = cpu_to_le16(cmd_no); cmd_ptr->result = 0; /* Prepare command */ if (cmd_no) { switch (cmd_no) { case HostCmd_CMD_UAP_SYS_CONFIG: case HostCmd_CMD_UAP_BSS_START: case HostCmd_CMD_UAP_BSS_STOP: ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, cmd_oid, data_buf, cmd_ptr); break; default: ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action, cmd_oid, data_buf, cmd_ptr); break; } } else { ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf); cmd_node->cmd_flag |= CMD_F_HOSTCMD; } /* Return error, since the command preparation failed */ if (ret) { dev_err(adapter->dev, "PREP_CMD: cmd %#x preparation failed\n", cmd_no); mwifiex_insert_cmd_to_free_q(adapter, cmd_node); return -1; } /* Send command */ if (cmd_no == HostCmd_CMD_802_11_SCAN) { mwifiex_queue_scan_cmd(priv, cmd_node); } else { adapter->cmd_queued = cmd_node; mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); queue_work(adapter->workqueue, &adapter->main_work); } return ret; } /* * This function returns a command to the command free queue. * * The function also calls the completion callback if required, before * cleaning the command node and re-inserting it into the free queue. */ void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node) { unsigned long flags; if (!cmd_node) return; if (cmd_node->wait_q_enabled) mwifiex_complete_cmd(adapter, cmd_node); /* Clean the node */ mwifiex_clean_cmd_node(adapter, cmd_node); /* Insert node into cmd_free_q */ spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); list_add_tail(&cmd_node->list, &adapter->cmd_free_q); spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); } /* * This function queues a command to the command pending queue. * * This in effect adds the command to the command list to be executed. * Exit PS command is handled specially, by placing it always to the * front of the command queue. */ void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node, u32 add_tail) { struct host_cmd_ds_command *host_cmd = NULL; u16 command; unsigned long flags; host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); if (!host_cmd) { dev_err(adapter->dev, "QUEUE_CMD: host_cmd is NULL\n"); return; } command = le16_to_cpu(host_cmd->command); /* Exit_PS command needs to be queued in the header always. */ if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { struct host_cmd_ds_802_11_ps_mode_enh *pm = &host_cmd->params.psmode_enh; if ((le16_to_cpu(pm->action) == DIS_PS) || (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { if (adapter->ps_state != PS_STATE_AWAKE) add_tail = false; } } spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); if (add_tail) list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); else list_add(&cmd_node->list, &adapter->cmd_pending_q); spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x is queued\n", command); } /* * This function executes the next command in command pending queue. * * This function will fail if a command is already in processing stage, * otherwise it will dequeue the first command from the command pending * queue and send to the firmware. * * If the device is currently in host sleep mode, any commands, except the * host sleep configuration command will de-activate the host sleep. For PS * mode, the function will put the firmware back to sleep if applicable. */ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; struct cmd_ctrl_node *cmd_node; int ret = 0; struct host_cmd_ds_command *host_cmd; unsigned long cmd_flags; unsigned long cmd_pending_q_flags; /* Check if already in processing */ if (adapter->curr_cmd) { dev_err(adapter->dev, "EXEC_NEXT_CMD: cmd in processing\n"); return -1; } spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); /* Check if any command is pending */ spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); if (list_empty(&adapter->cmd_pending_q)) { spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); return 0; } cmd_node = list_first_entry(&adapter->cmd_pending_q, struct cmd_ctrl_node, list); spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); priv = cmd_node->priv; if (adapter->ps_state != PS_STATE_AWAKE) { dev_err(adapter->dev, "%s: cannot send cmd in sleep state," " this should not happen\n", __func__); spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); return ret; } spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); /* Any command sent to the firmware when host is in sleep * mode should de-configure host sleep. We should skip the * host sleep configuration command itself though */ if (priv && (host_cmd->command != cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { if (adapter->hs_activated) { adapter->is_hs_configured = false; mwifiex_hs_activated_event(priv, false); } } return ret; } /* * This function handles the command response. * * After processing, the function cleans the command node and puts * it back to the command free queue. */ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) { struct host_cmd_ds_command *resp; struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); int ret = 0; uint16_t orig_cmdresp_no; uint16_t cmdresp_no; uint16_t cmdresp_result; struct timeval tstamp; unsigned long flags; /* Now we got response from FW, cancel the command timer */ del_timer(&adapter->cmd_timer); if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { resp = (struct host_cmd_ds_command *) adapter->upld_buf; dev_err(adapter->dev, "CMD_RESP: NULL curr_cmd, %#x\n", le16_to_cpu(resp->command)); return -1; } adapter->num_cmd_timeout = 0; resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) { dev_err(adapter->dev, "CMD_RESP: %#x been canceled\n", le16_to_cpu(resp->command)); mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd = NULL; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); return -1; } if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { /* Copy original response back to response buffer */ struct mwifiex_ds_misc_cmd *hostcmd; uint16_t size = le16_to_cpu(resp->size); dev_dbg(adapter->dev, "info: host cmd resp size = %d\n", size); size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER); if (adapter->curr_cmd->data_buf) { hostcmd = adapter->curr_cmd->data_buf; hostcmd->len = size; memcpy(hostcmd->cmd, resp, size); } } orig_cmdresp_no = le16_to_cpu(resp->command); /* Get BSS number and corresponding priv */ priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)), HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); if (!priv) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); /* Clear RET_BIT from HostCmd */ resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK); cmdresp_no = le16_to_cpu(resp->command); cmdresp_result = le16_to_cpu(resp->result); /* Save the last command response to debug log */ adapter->dbg.last_cmd_resp_index = (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = orig_cmdresp_no; do_gettimeofday(&tstamp); dev_dbg(adapter->dev, "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d," " len %d, seqno 0x%x\n", tstamp.tv_sec, tstamp.tv_usec, orig_cmdresp_no, cmdresp_result, le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { dev_err(adapter->dev, "CMD_RESP: invalid cmd resp\n"); if (adapter->curr_cmd->wait_q_enabled) adapter->cmd_wait_q.status = -1; mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd = NULL; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); return -1; } if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; if ((cmdresp_result == HostCmd_RESULT_OK) && (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) ret = mwifiex_ret_802_11_hs_cfg(priv, resp); } else { /* handle response */ ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp); } /* Check init command response */ if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { if (ret) { dev_err(adapter->dev, "%s: cmd %#x failed during " "initialization\n", __func__, cmdresp_no); mwifiex_init_fw_complete(adapter); return -1; } else if (adapter->last_init_cmd == cmdresp_no) adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; } if (adapter->curr_cmd) { if (adapter->curr_cmd->wait_q_enabled) adapter->cmd_wait_q.status = ret; /* Clean up and put current command back to cmd_free_q */ mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd = NULL; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); } return ret; } /* * This function handles the timeout of command sending. * * It will re-send the same command again. */ void mwifiex_cmd_timeout_func(unsigned long function_context) { struct mwifiex_adapter *adapter = (struct mwifiex_adapter *) function_context; struct cmd_ctrl_node *cmd_node; struct timeval tstamp; adapter->num_cmd_timeout++; adapter->dbg.num_cmd_timeout++; if (!adapter->curr_cmd) { dev_dbg(adapter->dev, "cmd: empty curr_cmd\n"); return; } cmd_node = adapter->curr_cmd; if (cmd_node->wait_q_enabled) adapter->cmd_wait_q.status = -ETIMEDOUT; if (cmd_node) { adapter->dbg.timeout_cmd_id = adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; adapter->dbg.timeout_cmd_act = adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; do_gettimeofday(&tstamp); dev_err(adapter->dev, "%s: Timeout cmd id (%lu.%lu) = %#x, act = %#x\n", __func__, tstamp.tv_sec, tstamp.tv_usec, adapter->dbg.timeout_cmd_id, adapter->dbg.timeout_cmd_act); dev_err(adapter->dev, "num_data_h2c_failure = %d\n", adapter->dbg.num_tx_host_to_card_failure); dev_err(adapter->dev, "num_cmd_h2c_failure = %d\n", adapter->dbg.num_cmd_host_to_card_failure); dev_err(adapter->dev, "num_cmd_timeout = %d\n", adapter->dbg.num_cmd_timeout); dev_err(adapter->dev, "num_tx_timeout = %d\n", adapter->dbg.num_tx_timeout); dev_err(adapter->dev, "last_cmd_index = %d\n", adapter->dbg.last_cmd_index); print_hex_dump_bytes("last_cmd_id: ", DUMP_PREFIX_OFFSET, adapter->dbg.last_cmd_id, DBG_CMD_NUM); print_hex_dump_bytes("last_cmd_act: ", DUMP_PREFIX_OFFSET, adapter->dbg.last_cmd_act, DBG_CMD_NUM); dev_err(adapter->dev, "last_cmd_resp_index = %d\n", adapter->dbg.last_cmd_resp_index); print_hex_dump_bytes("last_cmd_resp_id: ", DUMP_PREFIX_OFFSET, adapter->dbg.last_cmd_resp_id, DBG_CMD_NUM); dev_err(adapter->dev, "last_event_index = %d\n", adapter->dbg.last_event_index); print_hex_dump_bytes("last_event: ", DUMP_PREFIX_OFFSET, adapter->dbg.last_event, DBG_CMD_NUM); dev_err(adapter->dev, "data_sent=%d cmd_sent=%d\n", adapter->data_sent, adapter->cmd_sent); dev_err(adapter->dev, "ps_mode=%d ps_state=%d\n", adapter->ps_mode, adapter->ps_state); } if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) mwifiex_init_fw_complete(adapter); } /* * This function cancels all the pending commands. * * The current command, all commands in command pending queue and all scan * commands in scan pending queue are cancelled. All the completion callbacks * are called with failure status to ensure cleanup. */ void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; unsigned long flags; /* Cancel current cmd */ if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd->wait_q_enabled = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); adapter->cmd_wait_q.status = -1; mwifiex_complete_cmd(adapter, adapter->curr_cmd); } /* Cancel all pending command */ spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->cmd_pending_q, list) { list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); if (cmd_node->wait_q_enabled) { adapter->cmd_wait_q.status = -1; mwifiex_complete_cmd(adapter, cmd_node); cmd_node->wait_q_enabled = false; } mwifiex_insert_cmd_to_free_q(adapter, cmd_node); spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); } spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); /* Cancel all pending scan command */ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); } /* * This function cancels all pending commands that matches with * the given IOCTL request. * * Both the current command buffer and the pending command queue are * searched for matching IOCTL request. The completion callback of * the matched command is called with failure status to ensure cleanup. * In case of scan commands, all pending commands in scan pending queue * are cancelled. */ void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; unsigned long cmd_flags; unsigned long scan_pending_q_flags; uint16_t cancel_scan_cmd = false; if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); cmd_node = adapter->curr_cmd; cmd_node->wait_q_enabled = false; cmd_node->cmd_flag |= CMD_F_CANCELED; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); mwifiex_complete_cmd(adapter, adapter->curr_cmd); adapter->curr_cmd = NULL; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); } /* Cancel all pending scan command */ spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_pending_q_flags); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_pending_q_flags); cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_pending_q_flags); cancel_scan_cmd = true; } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_pending_q_flags); if (cancel_scan_cmd) { spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); adapter->scan_processing = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); } adapter->cmd_wait_q.status = -1; } /* * This function sends the sleep confirm command to firmware, if * possible. * * The sleep confirm command cannot be issued if command response, * data response or event response is awaiting handling, or if we * are in the middle of sending a command, or expecting a command * response. */ void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) { if (!adapter->cmd_sent && !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) mwifiex_dnld_sleep_confirm_cmd(adapter); else dev_dbg(adapter->dev, "cmd: Delay Sleep Confirm (%s%s%s)\n", (adapter->cmd_sent) ? "D" : "", (adapter->curr_cmd) ? "C" : "", (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); } /* * This function sends a Host Sleep activated event to applications. * * This event is generated by the driver, with a blank event body. */ void mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) { if (activated) { if (priv->adapter->is_hs_configured) { priv->adapter->hs_activated = true; dev_dbg(priv->adapter->dev, "event: hs_activated\n"); priv->adapter->hs_activate_wait_q_woken = true; wake_up_interruptible( &priv->adapter->hs_activate_wait_q); } else { dev_dbg(priv->adapter->dev, "event: HS not configured\n"); } } else { dev_dbg(priv->adapter->dev, "event: hs_deactivated\n"); priv->adapter->hs_activated = false; } } /* * This function handles the command response of a Host Sleep configuration * command. * * Handling includes changing the header fields into CPU format * and setting the current host sleep activation status in driver. * * In case host sleep status change, the function generates an event to * notify the applications. */ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = &resp->params.opt_hs_cfg; uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && adapter->iface_type == MWIFIEX_SDIO) { mwifiex_hs_activated_event(priv, true); return 0; } else { dev_dbg(adapter->dev, "cmd: CMD_RESP: HS_CFG cmd reply" " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", resp->result, conditions, phs_cfg->params.hs_config.gpio, phs_cfg->params.hs_config.gap); } if (conditions != HOST_SLEEP_CFG_CANCEL) { adapter->is_hs_configured = true; if (adapter->iface_type == MWIFIEX_USB || adapter->iface_type == MWIFIEX_PCIE) mwifiex_hs_activated_event(priv, true); } else { adapter->is_hs_configured = false; if (adapter->hs_activated) mwifiex_hs_activated_event(priv, false); } return 0; } /* * This function wakes up the adapter and generates a Host Sleep * cancel event on receiving the power up interrupt. */ void mwifiex_process_hs_config(struct mwifiex_adapter *adapter) { dev_dbg(adapter->dev, "info: %s: auto cancelling host sleep" " since there is interrupt from the firmware\n", __func__); adapter->if_ops.wakeup(adapter); adapter->hs_activated = false; adapter->is_hs_configured = false; mwifiex_hs_activated_event(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), false); } EXPORT_SYMBOL_GPL(mwifiex_process_hs_config); /* * This function handles the command response of a sleep confirm command. * * The function sets the card state to SLEEP if the response indicates success. */ void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, u8 *pbuf, u32 upld_len) { struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf; struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); uint16_t result = le16_to_cpu(cmd->result); uint16_t command = le16_to_cpu(cmd->command); uint16_t seq_num = le16_to_cpu(cmd->seq_num); if (!upld_len) { dev_err(adapter->dev, "%s: cmd size is 0\n", __func__); return; } /* Get BSS number and corresponding priv */ priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), HostCmd_GET_BSS_TYPE(seq_num)); if (!priv) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); /* Update sequence number */ seq_num = HostCmd_GET_SEQ_NO(seq_num); /* Clear RET_BIT from HostCmd */ command &= HostCmd_CMD_ID_MASK; if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { dev_err(adapter->dev, "%s: rcvd unexpected resp for cmd %#x, result = %x\n", __func__, command, result); return; } if (result) { dev_err(adapter->dev, "%s: sleep confirm cmd failed\n", __func__); adapter->pm_wakeup_card_req = false; adapter->ps_state = PS_STATE_AWAKE; return; } adapter->pm_wakeup_card_req = true; if (adapter->is_hs_configured) mwifiex_hs_activated_event(mwifiex_get_priv (adapter, MWIFIEX_BSS_ROLE_ANY), true); adapter->ps_state = PS_STATE_SLEEP; cmd->command = cpu_to_le16(command); cmd->seq_num = cpu_to_le16(seq_num); } EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp); /* * This function prepares an enhanced power mode command. * * This function can be used to disable power save or to configure * power save with auto PS or STA PS or auto deep sleep. * * Preparation includes - * - Setting command ID, action and proper size * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV, * auto deep sleep TLV (as required) * - Ensuring correct endian-ness */ int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, uint16_t ps_bitmap, struct mwifiex_ds_auto_ds *auto_ds) { struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = &cmd->params.psmode_enh; u8 *tlv; u16 cmd_size = 0; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); if (cmd_action == DIS_AUTO_PS) { psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + sizeof(psmode_enh->params.ps_bitmap)); } else if (cmd_action == GET_PS) { psmode_enh->action = cpu_to_le16(GET_PS); psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + sizeof(psmode_enh->params.ps_bitmap)); } else if (cmd_action == EN_AUTO_PS) { psmode_enh->action = cpu_to_le16(EN_AUTO_PS); psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + sizeof(psmode_enh->params.ps_bitmap); tlv = (u8 *) cmd + cmd_size; if (ps_bitmap & BITMAP_STA_PS) { struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_ie_types_ps_param *ps_tlv = (struct mwifiex_ie_types_ps_param *) tlv; struct mwifiex_ps_param *ps_mode = &ps_tlv->param; ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - sizeof(struct mwifiex_ie_types_header)); cmd_size += sizeof(*ps_tlv); tlv += sizeof(*ps_tlv); dev_dbg(adapter->dev, "cmd: PS Command: Enter PS\n"); ps_mode->null_pkt_interval = cpu_to_le16(adapter->null_pkt_interval); ps_mode->multiple_dtims = cpu_to_le16(adapter->multiple_dtim); ps_mode->bcn_miss_timeout = cpu_to_le16(adapter->bcn_miss_time_out); ps_mode->local_listen_interval = cpu_to_le16(adapter->local_listen_interval); ps_mode->adhoc_wake_period = cpu_to_le16(adapter->adhoc_awake_period); ps_mode->delay_to_ps = cpu_to_le16(adapter->delay_to_ps); ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode); } if (ps_bitmap & BITMAP_AUTO_DS) { struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv = (struct mwifiex_ie_types_auto_ds_param *) tlv; u16 idletime = 0; auto_ds_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); auto_ds_tlv->header.len = cpu_to_le16(sizeof(*auto_ds_tlv) - sizeof(struct mwifiex_ie_types_header)); cmd_size += sizeof(*auto_ds_tlv); tlv += sizeof(*auto_ds_tlv); if (auto_ds) idletime = auto_ds->idle_time; dev_dbg(priv->adapter->dev, "cmd: PS Command: Enter Auto Deep Sleep\n"); auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); } cmd->size = cpu_to_le16(cmd_size); } return 0; } /* * This function handles the command response of an enhanced power mode * command. * * Handling includes changing the header fields into CPU format * and setting the current enhanced power mode in driver. */ int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, struct host_cmd_ds_command *resp, struct mwifiex_ds_pm_cfg *pm_cfg) { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = &resp->params.psmode_enh; uint16_t action = le16_to_cpu(ps_mode->action); uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); uint16_t auto_ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); dev_dbg(adapter->dev, "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", __func__, resp->result, action); if (action == EN_AUTO_PS) { if (auto_ps_bitmap & BITMAP_AUTO_DS) { dev_dbg(adapter->dev, "cmd: Enabled auto deep sleep\n"); priv->adapter->is_deep_sleep = true; } if (auto_ps_bitmap & BITMAP_STA_PS) { dev_dbg(adapter->dev, "cmd: Enabled STA power save\n"); if (adapter->sleep_period.period) dev_dbg(adapter->dev, "cmd: set to uapsd/pps mode\n"); } } else if (action == DIS_AUTO_PS) { if (ps_bitmap & BITMAP_AUTO_DS) { priv->adapter->is_deep_sleep = false; dev_dbg(adapter->dev, "cmd: Disabled auto deep sleep\n"); } if (ps_bitmap & BITMAP_STA_PS) { dev_dbg(adapter->dev, "cmd: Disabled STA power save\n"); if (adapter->sleep_period.period) { adapter->delay_null_pkt = false; adapter->tx_lock_flag = false; adapter->pps_uapsd_mode = false; } } } else if (action == GET_PS) { if (ps_bitmap & BITMAP_STA_PS) adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; else adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; dev_dbg(adapter->dev, "cmd: ps_bitmap=%#x\n", ps_bitmap); if (pm_cfg) { /* This section is for get power save mode */ if (ps_bitmap & BITMAP_STA_PS) pm_cfg->param.ps_mode = 1; else pm_cfg->param.ps_mode = 0; } } return 0; } /* * This function prepares command to get hardware specifications. * * Preparation includes - * - Setting command ID, action and proper size * - Setting permanent address parameter * - Ensuring correct endian-ness */ int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd) { struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); return 0; } /* * This function handles the command response of get hardware * specifications. * * Handling includes changing the header fields into CPU format * and saving/updating the following parameters in driver - * - Firmware capability information * - Firmware band settings * - Ad-hoc start band and channel * - Ad-hoc 11n activation status * - Firmware release number * - Number of antennas * - Hardware address * - Hardware interface version * - Firmware version * - Region code * - 11n capabilities * - MCS support fields * - MP end port */ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; struct mwifiex_adapter *adapter = priv->adapter; int i; adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); if (IS_SUPPORT_MULTI_BANDS(adapter)) adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter); else adapter->fw_bands = BAND_B; adapter->config_bands = adapter->fw_bands; if (adapter->fw_bands & BAND_A) { if (adapter->fw_bands & BAND_GN) { adapter->config_bands |= BAND_AN; adapter->fw_bands |= BAND_AN; } if (adapter->fw_bands & BAND_AN) { adapter->adhoc_start_band = BAND_A | BAND_AN; adapter->adhoc_11n_enabled = true; } else { adapter->adhoc_start_band = BAND_A; } priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; } else if (adapter->fw_bands & BAND_GN) { adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; adapter->adhoc_11n_enabled = true; } else if (adapter->fw_bands & BAND_G) { adapter->adhoc_start_band = BAND_G | BAND_B; priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; } else if (adapter->fw_bands & BAND_B) { adapter->adhoc_start_band = BAND_B; priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; } adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n", adapter->fw_release_number); dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n", hw_spec->permanent_addr); dev_dbg(adapter->dev, "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", le16_to_cpu(hw_spec->hw_if_version), le16_to_cpu(hw_spec->version)); if (priv->curr_addr[0] == 0xff) memmove(priv->curr_addr, hw_spec->permanent_addr, ETH_ALEN); adapter->region_code = le16_to_cpu(hw_spec->region_code); for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) /* Use the region code to search for the index */ if (adapter->region_code == region_code_index[i]) break; /* If it's unidentified region code, use the default (USA) */ if (i >= MWIFIEX_MAX_REGION_CODE) { adapter->region_code = 0x10; dev_dbg(adapter->dev, "cmd: unknown region code, use default (USA)\n"); } adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; if (adapter->if_ops.update_mp_end_port) adapter->if_ops.update_mp_end_port(adapter, le16_to_cpu(hw_spec->mp_end_port)); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/cfp.c0000644000175000017500000002335012026211315023050 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: Channel, Frequence and Power * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "cfg80211.h" /* 100mW */ #define MWIFIEX_TX_PWR_DEFAULT 20 /* 100mW */ #define MWIFIEX_TX_PWR_US_DEFAULT 20 /* 50mW */ #define MWIFIEX_TX_PWR_JP_DEFAULT 16 /* 100mW */ #define MWIFIEX_TX_PWR_FR_100MW 20 /* 10mW */ #define MWIFIEX_TX_PWR_FR_10MW 10 /* 100mW */ #define MWIFIEX_TX_PWR_EMEA_DEFAULT 20 static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 }; static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0 }; static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0 }; static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0 }; static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0 }; static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90, 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 }; static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 }; static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0 }; static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0 }; u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, 0x32, 0x40, 0x41, 0xff }; static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; struct region_code_mapping { u8 code; u8 region[IEEE80211_COUNTRY_STRING_LEN]; }; static struct region_code_mapping region_code_mapping_t[] = { { 0x10, "US " }, /* US FCC */ { 0x20, "CA " }, /* IC Canada */ { 0x30, "EU " }, /* ETSI */ { 0x31, "ES " }, /* Spain */ { 0x32, "FR " }, /* France */ { 0x40, "JP " }, /* Japan */ { 0x41, "JP " }, /* Japan */ { 0x50, "CN " }, /* China */ }; /* This function converts integer code to region string */ u8 *mwifiex_11d_code_2_region(u8 code) { u8 i; u8 size = sizeof(region_code_mapping_t)/ sizeof(struct region_code_mapping); /* Look for code in mapping table */ for (i = 0; i < size; i++) if (region_code_mapping_t[i].code == code) return region_code_mapping_t[i].region; return NULL; } /* * This function maps an index in supported rates table into * the corresponding data rate. */ u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info) { /* * For every mcs_rate line, the first 8 bytes are for stream 1x1, * and all 16 bytes are for stream 2x2. */ u16 mcs_rate[4][16] = { /* LGI 40M */ { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, /* SGI 40M */ { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, /* LGI 20M */ { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, /* SGI 20M */ { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } }; u32 mcs_num_supp = (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; u32 rate; if (ht_info & BIT(0)) { if (index == MWIFIEX_RATE_BITMAP_MCS0) { if (ht_info & BIT(2)) rate = 0x0D; /* MCS 32 SGI rate */ else rate = 0x0C; /* MCS 32 LGI rate */ } else if (index < mcs_num_supp) { if (ht_info & BIT(1)) { if (ht_info & BIT(2)) /* SGI, 40M */ rate = mcs_rate[1][index]; else /* LGI, 40M */ rate = mcs_rate[0][index]; } else { if (ht_info & BIT(2)) /* SGI, 20M */ rate = mcs_rate[3][index]; else /* LGI, 20M */ rate = mcs_rate[2][index]; } } else rate = mwifiex_data_rates[0]; } else { if (index >= MWIFIEX_SUPPORTED_RATES_EXT) index = 0; rate = mwifiex_data_rates[index]; } return rate; } /* * This function returns the current active data rates. * * The result may vary depending upon connection status. */ u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) { if (!priv->media_connected) return mwifiex_get_supported_rates(priv, rates); else return mwifiex_copy_rates(rates, 0, priv->curr_bss_params.data_rates, priv->curr_bss_params.num_of_rates); } /* * This function locates the Channel-Frequency-Power triplet based upon * band and channel/frequency parameters. */ struct mwifiex_chan_freq_power * mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq) { struct mwifiex_chan_freq_power *cfp = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *ch = NULL; int i; if (!channel && !freq) return cfp; if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; else sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; if (!sband) { dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d\n", __func__, band); return cfp; } for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; if (freq) { if (ch->center_freq == freq) break; } else { /* find by valid channel*/ if (ch->hw_value == channel || channel == FIRST_VALID_CHANNEL) break; } } if (i == sband->n_channels) { dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" " & channel=%d freq=%d\n", __func__, band, channel, freq); } else { if (!ch) return cfp; priv->cfp.channel = ch->hw_value; priv->cfp.freq = ch->center_freq; priv->cfp.max_tx_power = ch->max_power; cfp = &priv->cfp; } return cfp; } /* * This function checks if the data rate is set to auto. */ u8 mwifiex_is_rate_auto(struct mwifiex_private *priv) { u32 i; int rate_num = 0; for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) if (priv->bitmap_rates[i]) rate_num++; if (rate_num > 1) return true; else return false; } /* * This function gets the supported data rates. * * The function works in both Ad-Hoc and infra mode by printing the * band and returning the data rates. */ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) { u32 k = 0; struct mwifiex_adapter *adapter = priv->adapter; if (priv->bss_mode == NL80211_IFTYPE_STATION) { switch (adapter->config_bands) { case BAND_B: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_b\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_b, sizeof(supported_rates_b)); break; case BAND_G: case BAND_G | BAND_GN: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_g\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_g, sizeof(supported_rates_g)); break; case BAND_B | BAND_G: case BAND_A | BAND_B | BAND_G: case BAND_A | BAND_B: case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: case BAND_B | BAND_G | BAND_GN: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_bg\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_bg, sizeof(supported_rates_bg)); break; case BAND_A: case BAND_A | BAND_G: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_a\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_a, sizeof(supported_rates_a)); break; case BAND_A | BAND_AN: case BAND_A | BAND_G | BAND_AN | BAND_GN: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_a\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_a, sizeof(supported_rates_a)); break; case BAND_GN: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_n\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_n, sizeof(supported_rates_n)); break; } } else { /* Ad-hoc mode */ switch (adapter->adhoc_start_band) { case BAND_B: dev_dbg(adapter->dev, "info: adhoc B\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_b, sizeof(adhoc_rates_b)); break; case BAND_G: case BAND_G | BAND_GN: dev_dbg(adapter->dev, "info: adhoc G only\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_g, sizeof(adhoc_rates_g)); break; case BAND_B | BAND_G: case BAND_B | BAND_G | BAND_GN: dev_dbg(adapter->dev, "info: adhoc BG\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, sizeof(adhoc_rates_bg)); break; case BAND_A: case BAND_A | BAND_AN: dev_dbg(adapter->dev, "info: adhoc A\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_a, sizeof(adhoc_rates_a)); break; } } return k; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/cfg80211.h0000644000175000017500000000203712026211315023437 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: CFG80211 * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef __MWIFIEX_CFG80211__ #define __MWIFIEX_CFG80211__ #include #include "main.h" int mwifiex_register_cfg80211(struct mwifiex_adapter *); #endif compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/cfg80211.c0000644000175000017500000015241412026211315023437 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: CFG80211 * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "cfg80211.h" #include "main.h" static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP), }, }; static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = { .limits = mwifiex_ap_sta_limits, .num_different_channels = 1, .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), .max_interfaces = MWIFIEX_MAX_BSS_NUM, .beacon_int_infra_match = true, }; static const struct ieee80211_regdomain mwifiex_world_regdom_custom = { .n_reg_rules = 7, .alpha2 = "99", .reg_rules = { /* Channel 1 - 11 */ REG_RULE(2412-10, 2462+10, 40, 3, 20, 0), /* Channel 12 - 13 */ REG_RULE(2467-10, 2472+10, 20, 3, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), /* Channel 14 */ REG_RULE(2484-10, 2484+10, 20, 3, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | NL80211_RRF_NO_OFDM), /* Channel 36 - 48 */ REG_RULE(5180-10, 5240+10, 40, 3, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), /* Channel 149 - 165 */ REG_RULE(5745-10, 5825+10, 40, 3, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), /* Channel 52 - 64 */ REG_RULE(5260-10, 5320+10, 40, 3, 30, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | NL80211_RRF_DFS), /* Channel 100 - 140 */ REG_RULE(5500-10, 5700+10, 40, 3, 30, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | NL80211_RRF_DFS), } }; /* * This function maps the nl802.11 channel type into driver channel type. * * The mapping is as follows - * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE */ static u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) { switch (chan_type) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: return IEEE80211_HT_PARAM_CHA_SEC_NONE; case NL80211_CHAN_HT40PLUS: return IEEE80211_HT_PARAM_CHA_SEC_ABOVE; case NL80211_CHAN_HT40MINUS: return IEEE80211_HT_PARAM_CHA_SEC_BELOW; default: return IEEE80211_HT_PARAM_CHA_SEC_NONE; } } /* * This function checks whether WEP is set. */ static int mwifiex_is_alg_wep(u32 cipher) { switch (cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: return 1; default: break; } return 0; } /* * This function retrieves the private structure from kernel wiphy structure. */ static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy) { return (void *) (*(unsigned long *) wiphy_priv(wiphy)); } /* * CFG802.11 operation handler to delete a network key. */ static int mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; const u8 *peer_mac = pairwise ? mac_addr : bc_mac; if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) { wiphy_err(wiphy, "deleting the crypto keys\n"); return -EFAULT; } wiphy_dbg(wiphy, "info: crypto keys deleted\n"); return 0; } /* * CFG802.11 operation handler to set Tx power. */ static int mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_private *priv; struct mwifiex_power_cfg power_cfg; int dbm = MBM_TO_DBM(mbm); if (type == NL80211_TX_POWER_FIXED) { power_cfg.is_power_auto = 0; power_cfg.power_level = dbm; } else { power_cfg.is_power_auto = 1; } priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); return mwifiex_set_tx_power(priv, &power_cfg); } /* * CFG802.11 operation handler to set Power Save option. * * The timeout value, if provided, is currently ignored. */ static int mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); u32 ps_mode; if (timeout) wiphy_dbg(wiphy, "info: ignore timeout value for IEEE Power Save\n"); ps_mode = enabled; return mwifiex_drv_set_power(priv, &ps_mode); } /* * CFG802.11 operation handler to set the default network key. */ static int mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); /* Return if WEP key not configured */ if (!priv->sec_info.wep_enabled) return 0; if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { priv->wep_key_curr_index = key_index; } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, NULL, 0)) { wiphy_err(wiphy, "set default Tx key index\n"); return -EFAULT; } return 0; } /* * CFG802.11 operation handler to add a network key. */ static int mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); struct mwifiex_wep_key *wep_key; const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; const u8 *peer_mac = pairwise ? mac_addr : bc_mac; if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && (params->cipher == WLAN_CIPHER_SUITE_WEP40 || params->cipher == WLAN_CIPHER_SUITE_WEP104)) { if (params->key && params->key_len) { wep_key = &priv->wep_key[key_index]; memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); memcpy(wep_key->key_material, params->key, params->key_len); wep_key->key_index = key_index; wep_key->key_length = params->key_len; priv->sec_info.wep_enabled = 1; } return 0; } if (mwifiex_set_encode(priv, params, params->key, params->key_len, key_index, peer_mac, 0)) { wiphy_err(wiphy, "crypto keys added\n"); return -EFAULT; } return 0; } /* * This function sends domain information to the firmware. * * The following information are passed to the firmware - * - Country codes * - Sub bands (first channel, number of channels, maximum Tx power) */ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) { u8 no_of_triplet = 0; struct ieee80211_country_ie_triplet *t; u8 no_of_parsed_chan = 0; u8 first_chan = 0, next_chan = 0, max_pwr = 0; u8 i, flag = 0; enum ieee80211_band band; struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_private *priv; struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; /* Set country code */ domain_info->country_code[0] = adapter->country_code[0]; domain_info->country_code[1] = adapter->country_code[1]; domain_info->country_code[2] = ' '; band = mwifiex_band_to_radio_type(adapter->config_bands); if (!wiphy->bands[band]) { wiphy_err(wiphy, "11D: setting domain info in FW\n"); return -1; } sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels ; i++) { ch = &sband->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; if (!flag) { flag = 1; first_chan = (u32) ch->hw_value; next_chan = first_chan; max_pwr = ch->max_reg_power; no_of_parsed_chan = 1; continue; } if (ch->hw_value == next_chan + 1 && ch->max_reg_power == max_pwr) { next_chan++; no_of_parsed_chan++; } else { t = &domain_info->triplet[no_of_triplet]; t->chans.first_channel = first_chan; t->chans.num_channels = no_of_parsed_chan; t->chans.max_power = max_pwr; no_of_triplet++; first_chan = (u32) ch->hw_value; next_chan = first_chan; max_pwr = ch->max_reg_power; no_of_parsed_chan = 1; } } if (flag) { t = &domain_info->triplet[no_of_triplet]; t->chans.first_channel = first_chan; t->chans.num_channels = no_of_parsed_chan; t->chans.max_power = max_pwr; no_of_triplet++; } domain_info->no_of_triplet = no_of_triplet; priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(wiphy, "11D: setting domain info in FW\n"); return -1; } return 0; } /* * CFG802.11 regulatory domain callback function. * * This function is called when the regulatory domain is changed due to the * following reasons - * - Set by driver * - Set by system core * - Set by user * - Set bt Country IE */ static int mwifiex_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for %c%c\n", request->alpha2[0], request->alpha2[1]); memcpy(adapter->country_code, request->alpha2, sizeof(request->alpha2)); switch (request->initiator) { case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_USER: break; /* Todo: apply driver specific changes in channel flags based on the request initiator if necessary. */ case NL80211_REGDOM_SET_BY_COUNTRY_IE: break; } mwifiex_send_domain_info_cmd_fw(wiphy); return 0; } /* * This function sets the fragmentation threshold. * * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE * and MWIFIEX_FRAG_MAX_VALUE. */ static int mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) { if (frag_thr < MWIFIEX_FRAG_MIN_VALUE || frag_thr > MWIFIEX_FRAG_MAX_VALUE) frag_thr = MWIFIEX_FRAG_MAX_VALUE; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, HostCmd_ACT_GEN_SET, FRAG_THRESH_I, &frag_thr); } /* * This function sets the RTS threshold. * The rts value must lie between MWIFIEX_RTS_MIN_VALUE * and MWIFIEX_RTS_MAX_VALUE. */ static int mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) { if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) rts_thr = MWIFIEX_RTS_MAX_VALUE; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, HostCmd_ACT_GEN_SET, RTS_THRESH_I, &rts_thr); } /* * CFG802.11 operation handler to set wiphy parameters. * * This function can be used to set the RTS threshold and the * Fragmentation threshold of the driver. */ static int mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_private *priv; struct mwifiex_uap_bss_param *bss_cfg; int ret, bss_started, i; for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; switch (priv->bss_role) { case MWIFIEX_BSS_ROLE_UAP: bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); if (!bss_cfg) return -ENOMEM; mwifiex_set_sys_config_invalid_data(bss_cfg); if (changed & WIPHY_PARAM_RTS_THRESHOLD) bss_cfg->rts_threshold = wiphy->rts_threshold; if (changed & WIPHY_PARAM_FRAG_THRESHOLD) bss_cfg->frag_threshold = wiphy->frag_threshold; if (changed & WIPHY_PARAM_RETRY_LONG) bss_cfg->retry_limit = wiphy->retry_long; bss_started = priv->bss_started; ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, HostCmd_ACT_GEN_SET, 0, NULL); if (ret) { wiphy_err(wiphy, "Failed to stop the BSS\n"); kfree(bss_cfg); return ret; } ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG, HostCmd_ACT_GEN_SET, UAP_BSS_PARAMS_I, bss_cfg); kfree(bss_cfg); if (ret) { wiphy_err(wiphy, "Failed to set bss config\n"); return ret; } if (!bss_started) break; ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_BSS_START, HostCmd_ACT_GEN_SET, 0, NULL); if (ret) { wiphy_err(wiphy, "Failed to start BSS\n"); return ret; } break; case MWIFIEX_BSS_ROLE_STA: if (changed & WIPHY_PARAM_RTS_THRESHOLD) { ret = mwifiex_set_rts(priv, wiphy->rts_threshold); if (ret) return ret; } if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { ret = mwifiex_set_frag(priv, wiphy->frag_threshold); if (ret) return ret; } break; } } return 0; } /* * CFG802.11 operation handler to change interface type. */ static int mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { int ret; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_ADHOC: switch (type) { case NL80211_IFTYPE_STATION: break; case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as IBSS\n", dev->name); case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ return 0; case NL80211_IFTYPE_AP: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); return -EOPNOTSUPP; } break; case NL80211_IFTYPE_STATION: switch (type) { case NL80211_IFTYPE_ADHOC: break; case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as STA\n", dev->name); case NL80211_IFTYPE_STATION: /* This shouldn't happen */ return 0; case NL80211_IFTYPE_AP: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); return -EOPNOTSUPP; } break; case NL80211_IFTYPE_AP: switch (type) { case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as AP\n", dev->name); case NL80211_IFTYPE_AP: /* This shouldn't happen */ return 0; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); return -EOPNOTSUPP; } break; default: wiphy_err(wiphy, "%s: unknown iftype: %d\n", dev->name, dev->ieee80211_ptr->iftype); return -EOPNOTSUPP; } dev->ieee80211_ptr->iftype = type; priv->bss_mode = type; mwifiex_deauthenticate(priv, NULL); priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE, HostCmd_ACT_GEN_SET, 0, NULL); return ret; } /* * This function dumps the station information on a buffer. * * The following information are shown - * - Total bytes transmitted * - Total bytes received * - Total packets transmitted * - Total packets received * - Signal quality level * - Transmission rate */ static int mwifiex_dump_station_info(struct mwifiex_private *priv, struct station_info *sinfo) { u32 rate; sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | STATION_INFO_TX_BITRATE | STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; /* Get signal information from the firmware */ if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO, HostCmd_ACT_GEN_GET, 0, NULL)) { dev_err(priv->adapter->dev, "failed to get signal information\n"); return -EFAULT; } if (mwifiex_drv_get_data_rate(priv, &rate)) { dev_err(priv->adapter->dev, "getting data rate\n"); return -EFAULT; } /* Get DTIM period information from firmware */ mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, &priv->dtim_period); /* * Bit 0 in tx_htinfo indicates that current Tx rate is 11n rate. Valid * MCS index values for us are 0 to 15. */ if ((priv->tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) { sinfo->txrate.mcs = priv->tx_rate; sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; /* 40MHz rate */ if (priv->tx_htinfo & BIT(1)) sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; /* SGI enabled */ if (priv->tx_htinfo & BIT(2)) sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; } sinfo->signal_avg = priv->bcn_rssi_avg; sinfo->rx_bytes = priv->stats.rx_bytes; sinfo->tx_bytes = priv->stats.tx_bytes; sinfo->rx_packets = priv->stats.rx_packets; sinfo->tx_packets = priv->stats.tx_packets; sinfo->signal = priv->bcn_rssi_avg; /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ sinfo->txrate.legacy = rate * 5; if (priv->bss_mode == NL80211_IFTYPE_STATION) { sinfo->filled |= STATION_INFO_BSS_PARAM; sinfo->bss_param.flags = 0; if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & WLAN_CAPABILITY_SHORT_PREAMBLE) sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & WLAN_CAPABILITY_SHORT_SLOT_TIME) sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; sinfo->bss_param.dtim_period = priv->dtim_period; sinfo->bss_param.beacon_interval = priv->curr_bss_params.bss_descriptor.beacon_period; } return 0; } /* * CFG802.11 operation handler to get station information. * * This function only works in connected mode, and dumps the * requested station information, if available. */ static int mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (!priv->media_connected) return -ENOENT; if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) return -ENOENT; return mwifiex_dump_station_info(priv, sinfo); } /* * CFG802.11 operation handler to dump station information. */ static int mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (!priv->media_connected || idx) return -ENOENT; memcpy(mac, priv->cfg_bssid, ETH_ALEN); return mwifiex_dump_station_info(priv, sinfo); } /* Supported rates to be advertised to the cfg80211 */ static struct ieee80211_rate mwifiex_rates[] = { {.bitrate = 10, .hw_value = 2, }, {.bitrate = 20, .hw_value = 4, }, {.bitrate = 55, .hw_value = 11, }, {.bitrate = 110, .hw_value = 22, }, {.bitrate = 60, .hw_value = 12, }, {.bitrate = 90, .hw_value = 18, }, {.bitrate = 120, .hw_value = 24, }, {.bitrate = 180, .hw_value = 36, }, {.bitrate = 240, .hw_value = 48, }, {.bitrate = 360, .hw_value = 72, }, {.bitrate = 480, .hw_value = 96, }, {.bitrate = 540, .hw_value = 108, }, }; /* Channel definitions to be advertised to cfg80211 */ static struct ieee80211_channel mwifiex_channels_2ghz[] = { {.center_freq = 2412, .hw_value = 1, }, {.center_freq = 2417, .hw_value = 2, }, {.center_freq = 2422, .hw_value = 3, }, {.center_freq = 2427, .hw_value = 4, }, {.center_freq = 2432, .hw_value = 5, }, {.center_freq = 2437, .hw_value = 6, }, {.center_freq = 2442, .hw_value = 7, }, {.center_freq = 2447, .hw_value = 8, }, {.center_freq = 2452, .hw_value = 9, }, {.center_freq = 2457, .hw_value = 10, }, {.center_freq = 2462, .hw_value = 11, }, {.center_freq = 2467, .hw_value = 12, }, {.center_freq = 2472, .hw_value = 13, }, {.center_freq = 2484, .hw_value = 14, }, }; static struct ieee80211_supported_band mwifiex_band_2ghz = { .channels = mwifiex_channels_2ghz, .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), .bitrates = mwifiex_rates, .n_bitrates = ARRAY_SIZE(mwifiex_rates), }; static struct ieee80211_channel mwifiex_channels_5ghz[] = { {.center_freq = 5040, .hw_value = 8, }, {.center_freq = 5060, .hw_value = 12, }, {.center_freq = 5080, .hw_value = 16, }, {.center_freq = 5170, .hw_value = 34, }, {.center_freq = 5190, .hw_value = 38, }, {.center_freq = 5210, .hw_value = 42, }, {.center_freq = 5230, .hw_value = 46, }, {.center_freq = 5180, .hw_value = 36, }, {.center_freq = 5200, .hw_value = 40, }, {.center_freq = 5220, .hw_value = 44, }, {.center_freq = 5240, .hw_value = 48, }, {.center_freq = 5260, .hw_value = 52, }, {.center_freq = 5280, .hw_value = 56, }, {.center_freq = 5300, .hw_value = 60, }, {.center_freq = 5320, .hw_value = 64, }, {.center_freq = 5500, .hw_value = 100, }, {.center_freq = 5520, .hw_value = 104, }, {.center_freq = 5540, .hw_value = 108, }, {.center_freq = 5560, .hw_value = 112, }, {.center_freq = 5580, .hw_value = 116, }, {.center_freq = 5600, .hw_value = 120, }, {.center_freq = 5620, .hw_value = 124, }, {.center_freq = 5640, .hw_value = 128, }, {.center_freq = 5660, .hw_value = 132, }, {.center_freq = 5680, .hw_value = 136, }, {.center_freq = 5700, .hw_value = 140, }, {.center_freq = 5745, .hw_value = 149, }, {.center_freq = 5765, .hw_value = 153, }, {.center_freq = 5785, .hw_value = 157, }, {.center_freq = 5805, .hw_value = 161, }, {.center_freq = 5825, .hw_value = 165, }, }; static struct ieee80211_supported_band mwifiex_band_5ghz = { .channels = mwifiex_channels_5ghz, .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), .bitrates = mwifiex_rates + 4, .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4, }; /* Supported crypto cipher suits to be advertised to cfg80211 */ static const u32 mwifiex_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WLAN_CIPHER_SUITE_AES_CMAC, }; /* * CFG802.11 operation handler for setting bit rates. * * Function configures data rates to firmware using bitrate mask * provided by cfg80211. */ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, const struct cfg80211_bitrate_mask *mask) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; enum ieee80211_band band; if (!priv->media_connected) { dev_err(priv->adapter->dev, "Can not set Tx data rate in disconnected state\n"); return -EINVAL; } band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); memset(bitmap_rates, 0, sizeof(bitmap_rates)); /* Fill HR/DSSS rates. */ if (band == IEEE80211_BAND_2GHZ) bitmap_rates[0] = mask->control[band].legacy & 0x000f; /* Fill OFDM rates */ if (band == IEEE80211_BAND_2GHZ) bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4; else bitmap_rates[1] = mask->control[band].legacy; /* Fill MCS rates */ bitmap_rates[2] = mask->control[band].mcs[0]; if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) bitmap_rates[2] |= mask->control[band].mcs[1] << 8; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG, HostCmd_ACT_GEN_SET, 0, bitmap_rates); } /* * CFG802.11 operation handler for connection quality monitoring. * * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI * events to FW. */ static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct mwifiex_ds_misc_subsc_evt subsc_evt; priv->cqm_rssi_thold = rssi_thold; priv->cqm_rssi_hyst = rssi_hyst; memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; /* Subscribe/unsubscribe low and high rssi events */ if (rssi_thold && rssi_hyst) { subsc_evt.action = HostCmd_ACT_BITWISE_SET; subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold); subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, 0, 0, &subsc_evt); } else { subsc_evt.action = HostCmd_ACT_BITWISE_CLR; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, 0, 0, &subsc_evt); } return 0; } /* cfg80211 operation handler for change_beacon. * Function retrieves and sets modified management IEs to FW. */ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_beacon_data *data) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { wiphy_err(wiphy, "%s: bss_type mismatched\n", __func__); return -EINVAL; } if (!priv->bss_started) { wiphy_err(wiphy, "%s: bss not started\n", __func__); return -EINVAL; } if (mwifiex_set_mgmt_ies(priv, data)) { wiphy_err(wiphy, "%s: setting mgmt ies failed\n", __func__); return -EFAULT; } return 0; } static int mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); struct mwifiex_ds_ant_cfg ant_cfg; if (!tx_ant || !rx_ant) return -EOPNOTSUPP; if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) { /* Not a MIMO chip. User should provide specific antenna number * for Tx/Rx path or enable all antennas for diversity */ if (tx_ant != rx_ant) return -EOPNOTSUPP; if ((tx_ant & (tx_ant - 1)) && (tx_ant != BIT(adapter->number_of_antenna) - 1)) return -EOPNOTSUPP; if ((tx_ant == BIT(adapter->number_of_antenna) - 1) && (priv->adapter->number_of_antenna > 1)) { tx_ant = RF_ANTENNA_AUTO; rx_ant = RF_ANTENNA_AUTO; } } ant_cfg.tx_ant = tx_ant; ant_cfg.rx_ant = rx_ant; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_RF_ANTENNA, HostCmd_ACT_GEN_SET, 0, &ant_cfg); } /* cfg80211 operation handler for stop ap. * Function stops BSS running at uAP interface. */ static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (mwifiex_del_mgmt_ies(priv)) wiphy_err(wiphy, "Failed to delete mgmt IEs!\n"); priv->ap_11n_enabled = 0; if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(wiphy, "Failed to stop the BSS\n"); return -1; } return 0; } /* cfg80211 operation handler for start_ap. * Function sets beacon period, DTIM period, SSID and security into * AP config structure. * AP is configured with these settings and BSS is started. */ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *params) { struct mwifiex_uap_bss_param *bss_cfg; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); u8 config_bands = 0; if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) return -1; if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) return -1; bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); if (!bss_cfg) return -ENOMEM; mwifiex_set_sys_config_invalid_data(bss_cfg); if (params->beacon_interval) bss_cfg->beacon_period = params->beacon_interval; if (params->dtim_period) bss_cfg->dtim_period = params->dtim_period; if (params->ssid && params->ssid_len) { memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); bss_cfg->ssid.ssid_len = params->ssid_len; } switch (params->hidden_ssid) { case NL80211_HIDDEN_SSID_NOT_IN_USE: bss_cfg->bcast_ssid_ctl = 1; break; case NL80211_HIDDEN_SSID_ZERO_LEN: bss_cfg->bcast_ssid_ctl = 0; break; case NL80211_HIDDEN_SSID_ZERO_CONTENTS: /* firmware doesn't support this type of hidden SSID */ default: kfree(bss_cfg); return -EINVAL; } bss_cfg->channel = (u8)ieee80211_frequency_to_channel(params->channel->center_freq); /* Set appropriate bands */ if (params->channel->band == IEEE80211_BAND_2GHZ) { bss_cfg->band_cfg = BAND_CONFIG_BG; if (params->channel_type == NL80211_CHAN_NO_HT) config_bands = BAND_B | BAND_G; else config_bands = BAND_B | BAND_G | BAND_GN; } else { bss_cfg->band_cfg = BAND_CONFIG_A; if (params->channel_type == NL80211_CHAN_NO_HT) config_bands = BAND_A; else config_bands = BAND_AN | BAND_A; } if (!((config_bands | priv->adapter->fw_bands) & ~priv->adapter->fw_bands)) priv->adapter->config_bands = config_bands; mwifiex_set_uap_rates(bss_cfg, params); mwifiex_send_domain_info_cmd_fw(wiphy); if (mwifiex_set_secure_params(priv, bss_cfg, params)) { kfree(bss_cfg); wiphy_err(wiphy, "Failed to parse secuirty parameters!\n"); return -1; } mwifiex_set_ht_params(priv, bss_cfg, params); if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(wiphy, "Failed to stop the BSS\n"); kfree(bss_cfg); return -1; } if (mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG, HostCmd_ACT_GEN_SET, UAP_BSS_PARAMS_I, bss_cfg)) { wiphy_err(wiphy, "Failed to set the SSID\n"); kfree(bss_cfg); return -1; } kfree(bss_cfg); if (mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_BSS_START, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(wiphy, "Failed to start the BSS\n"); return -1; } if (priv->sec_info.wep_enabled) priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; else priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, HostCmd_ACT_GEN_SET, 0, &priv->curr_pkt_filter)) return -1; return 0; } /* * CFG802.11 operation handler for disconnection request. * * This function does not work when there is already a disconnection * procedure going on. */ static int mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; wiphy_dbg(wiphy, "info: successfully disconnected from %pM:" " reason code %d\n", priv->cfg_bssid, reason_code); memset(priv->cfg_bssid, 0, ETH_ALEN); return 0; } /* * This function informs the CFG802.11 subsystem of a new IBSS. * * The following information are sent to the CFG802.11 subsystem * to register the new IBSS. If we do not register the new IBSS, * a kernel panic will result. * - SSID * - SSID length * - BSSID * - Channel */ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) { struct ieee80211_channel *chan; struct mwifiex_bss_info bss_info; struct cfg80211_bss *bss; int ie_len; u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; enum ieee80211_band band; if (mwifiex_get_bss_info(priv, &bss_info)) return -1; ie_buf[0] = WLAN_EID_SSID; ie_buf[1] = bss_info.ssid.ssid_len; memcpy(&ie_buf[sizeof(struct ieee_types_header)], &bss_info.ssid.ssid, bss_info.ssid.ssid_len); ie_len = ie_buf[1] + sizeof(struct ieee_types_header); band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); chan = __ieee80211_get_channel(priv->wdev->wiphy, ieee80211_channel_to_frequency(bss_info.bss_chan, band)); bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, 0, ie_buf, ie_len, 0, GFP_KERNEL); cfg80211_put_bss(bss); memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); return 0; } /* * This function connects with a BSS. * * This function handles both Infra and Ad-Hoc modes. It also performs * validity checking on the provided parameters, disconnects from the * current BSS (if any), sets up the association/scan parameters, * including security settings, and performs specific SSID scan before * trying to connect. * * For Infra mode, the function returns failure if the specified SSID * is not found in scan table. However, for Ad-Hoc mode, it can create * the IBSS if it does not exist. On successful completion in either case, * the function notifies the CFG802.11 subsystem of the new BSS connection. */ static int mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, u8 *bssid, int mode, struct ieee80211_channel *channel, struct cfg80211_connect_params *sme, bool privacy) { struct cfg80211_ssid req_ssid; int ret, auth_type = 0; struct cfg80211_bss *bss = NULL; u8 is_scanning_required = 0, config_bands = 0; memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); req_ssid.ssid_len = ssid_len; if (ssid_len > IEEE80211_MAX_SSID_LEN) { dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); return -EINVAL; } memcpy(req_ssid.ssid, ssid, ssid_len); if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); return -EINVAL; } /* disconnect before try to associate */ mwifiex_deauthenticate(priv, NULL); if (channel) { if (mode == NL80211_IFTYPE_STATION) { if (channel->band == IEEE80211_BAND_2GHZ) config_bands = BAND_B | BAND_G | BAND_GN; else config_bands = BAND_A | BAND_AN; if (!((config_bands | priv->adapter->fw_bands) & ~priv->adapter->fw_bands)) priv->adapter->config_bands = config_bands; } } /* As this is new association, clear locally stored * keys and security related flags */ priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; priv->wep_key_curr_index = 0; priv->sec_info.encryption_mode = 0; priv->sec_info.is_authtype_auto = 0; ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); if (mode == NL80211_IFTYPE_ADHOC) { /* "privacy" is set only for ad-hoc mode */ if (privacy) { /* * Keep WLAN_CIPHER_SUITE_WEP104 for now so that * the firmware can find a matching network from the * scan. The cfg80211 does not give us the encryption * mode at this stage so just setting it to WEP here. */ priv->sec_info.encryption_mode = WLAN_CIPHER_SUITE_WEP104; priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; } goto done; } /* Now handle infra mode. "sme" is valid for infra mode only */ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; priv->sec_info.is_authtype_auto = 1; } else { auth_type = sme->auth_type; } if (sme->crypto.n_ciphers_pairwise) { priv->sec_info.encryption_mode = sme->crypto.ciphers_pairwise[0]; priv->sec_info.authentication_mode = auth_type; } if (sme->crypto.cipher_group) { priv->sec_info.encryption_mode = sme->crypto.cipher_group; priv->sec_info.authentication_mode = auth_type; } if (sme->ie) ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); if (sme->key) { if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) { dev_dbg(priv->adapter->dev, "info: setting wep encryption" " with key len %d\n", sme->key_len); priv->wep_key_curr_index = sme->key_idx; ret = mwifiex_set_encode(priv, NULL, sme->key, sme->key_len, sme->key_idx, NULL, 0); } } done: /* * Scan entries are valid for some time (15 sec). So we can save one * active scan time if we just try cfg80211_get_bss first. If it fails * then request scan and cfg80211_get_bss() again for final output. */ while (1) { if (is_scanning_required) { /* Do specific SSID scanning */ if (mwifiex_request_scan(priv, &req_ssid)) { dev_err(priv->adapter->dev, "scan error\n"); return -EFAULT; } } /* Find the BSS we want using available scan results */ if (mode == NL80211_IFTYPE_ADHOC) bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid, ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); else bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!bss) { if (is_scanning_required) { dev_warn(priv->adapter->dev, "assoc: requested bss not found in scan results\n"); break; } is_scanning_required = 1; } else { dev_dbg(priv->adapter->dev, "info: trying to associate to '%s' bssid %pM\n", (char *) req_ssid.ssid, bss->bssid); memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); break; } } if (mwifiex_bss_start(priv, bss, &req_ssid)) return -EFAULT; if (mode == NL80211_IFTYPE_ADHOC) { /* Inform the BSS information to kernel, otherwise * kernel will give a panic after successful assoc */ if (mwifiex_cfg80211_inform_ibss_bss(priv)) return -EFAULT; } return ret; } /* * CFG802.11 operation handler for association request. * * This function does not work when the current mode is set to Ad-Hoc, or * when there is already an association procedure going on. The given BSS * information is used to associate. */ static int mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret = 0; if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { wiphy_err(wiphy, "received infra assoc request " "when station is in ibss mode\n"); goto done; } if (priv->bss_mode == NL80211_IFTYPE_AP) { wiphy_err(wiphy, "skip association request for AP interface\n"); goto done; } wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", (char *) sme->ssid, sme->bssid); ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); done: if (!ret) { cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, NULL, 0, WLAN_STATUS_SUCCESS, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: associated to bssid %pM successfully\n", priv->cfg_bssid); } else { dev_dbg(priv->adapter->dev, "info: association to bssid %pM failed\n", priv->cfg_bssid); memset(priv->cfg_bssid, 0, ETH_ALEN); } return ret; } /* * This function sets following parameters for ibss network. * - channel * - start band * - 11n flag * - secondary channel offset */ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, struct cfg80211_ibss_params *params) { struct wiphy *wiphy = priv->wdev->wiphy; struct mwifiex_adapter *adapter = priv->adapter; int index = 0, i; u8 config_bands = 0; if (params->channel->band == IEEE80211_BAND_2GHZ) { if (!params->basic_rates) { config_bands = BAND_B | BAND_G; } else { for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) { /* * Rates below 6 Mbps in the table are CCK * rates; 802.11b and from 6 they are OFDM; * 802.11G */ if (mwifiex_rates[i].bitrate == 60) { index = 1 << i; break; } } if (params->basic_rates < index) { config_bands = BAND_B; } else { config_bands = BAND_G; if (params->basic_rates % index) config_bands |= BAND_B; } } if (params->channel_type != NL80211_CHAN_NO_HT) config_bands |= BAND_GN; } else { if (params->channel_type == NL80211_CHAN_NO_HT) config_bands = BAND_A; else config_bands = BAND_AN | BAND_A; } if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) { adapter->config_bands = config_bands; adapter->adhoc_start_band = config_bands; if ((config_bands & BAND_GN) || (config_bands & BAND_AN)) adapter->adhoc_11n_enabled = true; else adapter->adhoc_11n_enabled = false; } adapter->sec_chan_offset = mwifiex_chan_type_to_sec_chan_offset(params->channel_type); priv->adhoc_channel = ieee80211_frequency_to_channel(params->channel->center_freq); wiphy_dbg(wiphy, "info: set ibss band %d, chan %d, chan offset %d\n", config_bands, priv->adhoc_channel, adapter->sec_chan_offset); return 0; } /* * CFG802.11 operation handler to join an IBSS. * * This function does not work in any mode other than Ad-Hoc, or if * a join operation is already in progress. */ static int mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret = 0; if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { wiphy_err(wiphy, "request to join ibss received " "when station is not in ibss mode\n"); goto done; } wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n", (char *) params->ssid, params->bssid); mwifiex_set_ibss_params(priv, params); ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, params->bssid, priv->bss_mode, params->channel, NULL, params->privacy); done: if (!ret) { cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: joined/created adhoc network with bssid" " %pM successfully\n", priv->cfg_bssid); } else { dev_dbg(priv->adapter->dev, "info: failed creating/joining adhoc network\n"); } return ret; } /* * CFG802.11 operation handler to leave an IBSS. * * This function does not work if a leave operation is * already in progress. */ static int mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n", priv->cfg_bssid); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; memset(priv->cfg_bssid, 0, ETH_ALEN); return 0; } /* * CFG802.11 operation handler for scan request. * * This function issues a scan request to the firmware based upon * the user specified scan configuration. On successfull completion, * it also informs the results. */ static int mwifiex_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct net_device *dev = request->wdev->netdev; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int i; struct ieee80211_channel *chan; wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); priv->scan_request = request; priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); if (!priv->user_scan_cfg) { dev_err(priv->adapter->dev, "failed to alloc scan_req\n"); return -ENOMEM; } priv->user_scan_cfg->num_ssids = request->n_ssids; priv->user_scan_cfg->ssid_list = request->ssids; if (request->ie && request->ie_len) { for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) continue; priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN; memcpy(&priv->vs_ie[i].ie, request->ie, request->ie_len); break; } } for (i = 0; i < request->n_channels; i++) { chan = request->channels[i]; priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value; priv->user_scan_cfg->chan_list[i].radio_type = chan->band; if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) priv->user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_PASSIVE; else priv->user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_ACTIVE; priv->user_scan_cfg->chan_list[i].scan_time = 0; } if (mwifiex_scan_networks(priv, priv->user_scan_cfg)) return -EFAULT; if (request->ie && request->ie_len) { for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) { priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR; memset(&priv->vs_ie[i].ie, 0, MWIFIEX_MAX_VSIE_LEN); } } } return 0; } /* * This function sets up the CFG802.11 specific HT capability fields * with default values. * * The following default values are set - * - HT Supported = True * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE * - HT Capabilities supported by firmware * - MCS information, Rx mask = 0xff * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) */ static void mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, struct mwifiex_private *priv) { int rx_mcs_supp; struct ieee80211_mcs_info mcs_set; u8 *mcs = (u8 *)&mcs_set; struct mwifiex_adapter *adapter = priv->adapter; ht_info->ht_supported = true; ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); /* Fill HT capability information */ if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; else ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= IEEE80211_HT_CAP_SGI_20; else ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20; if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= IEEE80211_HT_CAP_SGI_40; else ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; if (ISSUPP_RXSTBC(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; else ht_info->cap &= ~(3 << IEEE80211_HT_CAP_RX_STBC_SHIFT); if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; else ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; ht_info->cap |= IEEE80211_HT_CAP_SM_PS; rx_mcs_supp = GET_RXMCSSUPP(adapter->hw_dev_mcs_support); /* Set MCS for 1x1 */ memset(mcs, 0xff, rx_mcs_supp); /* Clear all the other values */ memset(&mcs[rx_mcs_supp], 0, sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); if (priv->bss_mode == NL80211_IFTYPE_STATION || ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ SETHT_MCS32(mcs_set.rx_mask); memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; } /* * create a new virtual interface with the given name */ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_private *priv; struct net_device *dev; void *mdev_priv; struct wireless_dev *wdev; if (!adapter) return ERR_PTR(-EFAULT); switch (type) { case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; if (priv->bss_mode) { wiphy_err(wiphy, "cannot create multiple sta/adhoc ifaces\n"); return ERR_PTR(-EINVAL); } wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); if (!wdev) return ERR_PTR(-ENOMEM); wdev->wiphy = wiphy; priv->wdev = wdev; wdev->iftype = NL80211_IFTYPE_STATION; if (type == NL80211_IFTYPE_UNSPECIFIED) priv->bss_mode = NL80211_IFTYPE_STATION; else priv->bss_mode = type; priv->bss_type = MWIFIEX_BSS_TYPE_STA; priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_STA; priv->bss_num = 0; break; case NL80211_IFTYPE_AP: priv = adapter->priv[MWIFIEX_BSS_TYPE_UAP]; if (priv->bss_mode) { wiphy_err(wiphy, "Can't create multiple AP interfaces"); return ERR_PTR(-EINVAL); } wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); if (!wdev) return ERR_PTR(-ENOMEM); priv->wdev = wdev; wdev->wiphy = wiphy; wdev->iftype = NL80211_IFTYPE_AP; priv->bss_type = MWIFIEX_BSS_TYPE_UAP; priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_UAP; priv->bss_started = 0; priv->bss_num = 0; priv->bss_mode = type; break; default: wiphy_err(wiphy, "type not supported\n"); return ERR_PTR(-EINVAL); } dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), name, ether_setup, 1); if (!dev) { wiphy_err(wiphy, "no memory available for netdevice\n"); priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; return ERR_PTR(-ENOMEM); } mwifiex_init_priv_params(priv, dev); priv->netdev = dev; mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); if (adapter->config_bands & BAND_A) mwifiex_setup_ht_caps( &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); dev_net_set(dev, wiphy_net(wiphy)); dev->ieee80211_ptr = priv->wdev; dev->ieee80211_ptr->iftype = priv->bss_mode; memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); memcpy(dev->perm_addr, wiphy->perm_addr, ETH_ALEN); SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); dev->flags |= IFF_BROADCAST | IFF_MULTICAST; dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; mdev_priv = netdev_priv(dev); *((unsigned long *) mdev_priv) = (unsigned long) priv; SET_NETDEV_DEV(dev, adapter->dev); /* Register network device */ if (register_netdevice(dev)) { wiphy_err(wiphy, "cannot register virtual network device\n"); free_netdev(dev); priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; return ERR_PTR(-EFAULT); } sema_init(&priv->async_sem, 1); priv->scan_pending_on_block = false; dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); #ifdef CONFIG_DEBUG_FS mwifiex_dev_debugfs_init(priv); #endif return wdev; } EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); /* * del_virtual_intf: remove the virtual interface determined by dev */ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); #ifdef CONFIG_DEBUG_FS mwifiex_dev_debugfs_remove(priv); #endif if (!netif_queue_stopped(priv->netdev)) netif_stop_queue(priv->netdev); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); if (wdev->netdev->reg_state == NETREG_REGISTERED) unregister_netdevice(wdev->netdev); if (wdev->netdev->reg_state == NETREG_UNREGISTERED) free_netdev(wdev->netdev); /* Clear the priv in adapter */ priv->netdev = NULL; priv->media_connected = false; priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; return 0; } EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { .add_virtual_intf = mwifiex_add_virtual_intf, .del_virtual_intf = mwifiex_del_virtual_intf, .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, .scan = mwifiex_cfg80211_scan, .connect = mwifiex_cfg80211_connect, .disconnect = mwifiex_cfg80211_disconnect, .get_station = mwifiex_cfg80211_get_station, .dump_station = mwifiex_cfg80211_dump_station, .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, .join_ibss = mwifiex_cfg80211_join_ibss, .leave_ibss = mwifiex_cfg80211_leave_ibss, .add_key = mwifiex_cfg80211_add_key, .del_key = mwifiex_cfg80211_del_key, .set_default_key = mwifiex_cfg80211_set_default_key, .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, .set_tx_power = mwifiex_cfg80211_set_tx_power, .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask, .start_ap = mwifiex_cfg80211_start_ap, .stop_ap = mwifiex_cfg80211_stop_ap, .change_beacon = mwifiex_cfg80211_change_beacon, .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, .set_antenna = mwifiex_cfg80211_set_antenna, }; /* * This function registers the device with CFG802.11 subsystem. * * The function creates the wireless device/wiphy, populates it with * default parameters and handler function pointers, and finally * registers the device. */ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) { int ret; void *wdev_priv; struct wiphy *wiphy; struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; u8 *country_code; /* create a new wiphy for use with cfg80211 */ wiphy = wiphy_new(&mwifiex_cfg80211_ops, sizeof(struct mwifiex_adapter *)); if (!wiphy) { dev_err(adapter->dev, "%s: creating new wiphy\n", __func__); return -ENOMEM; } wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; if (adapter->config_bands & BAND_A) wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; else wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta; wiphy->n_iface_combinations = 1; /* Initialize cipher suits */ wiphy->cipher_suites = mwifiex_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); memcpy(wiphy->perm_addr, priv->curr_addr, ETH_ALEN); wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; wiphy->features = NL80211_FEATURE_HT_IBSS; /* Reserve space for mwifiex specific private data for BSS */ wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); wiphy->reg_notifier = mwifiex_reg_notifier; /* Set struct mwifiex_adapter pointer in wiphy_priv */ wdev_priv = wiphy_priv(wiphy); *(unsigned long *)wdev_priv = (unsigned long)adapter; set_wiphy_dev(wiphy, priv->adapter->dev); ret = wiphy_register(wiphy); if (ret < 0) { dev_err(adapter->dev, "%s: wiphy_register failed: %d\n", __func__, ret); wiphy_free(wiphy); return ret; } country_code = mwifiex_11d_code_2_region(priv->adapter->region_code); if (country_code) dev_info(adapter->dev, "ignoring F/W country code %2.2s\n", country_code); adapter->wiphy = wiphy; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/11n_rxreorder.h0000644000175000017500000000552012026211315024777 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: 802.11n RX Re-ordering * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_11N_RXREORDER_H_ #define _MWIFIEX_11N_RXREORDER_H_ #define MIN_FLUSH_TIMER_MS 50 #define PKT_TYPE_BAR 0xE7 #define MAX_TID_VALUE (2 << 11) #define TWOPOW11 (2 << 10) #define BLOCKACKPARAM_TID_POS 2 #define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 #define BLOCKACKPARAM_WINSIZE_POS 6 #define DELBA_TID_POS 12 #define DELBA_INITIATOR_POS 11 #define TYPE_DELBA_SENT 1 #define TYPE_DELBA_RECEIVE 2 #define IMMEDIATE_BLOCK_ACK 0x2 #define ADDBA_RSP_STATUS_ACCEPT 0 #define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff #define BA_SETUP_MAX_PACKET_THRESHOLD 16 #define BA_SETUP_PACKET_OFFSET 16 static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) { memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); } int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *, u16 seqNum, u16 tid, u8 *ta, u8 pkttype, void *payload); void mwifiex_del_ba_tbl(struct mwifiex_private *priv, int Tid, u8 *PeerMACAddr, u8 type, int initiator); void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, struct host_cmd_ds_11n_batimeout *event); int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf); int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct host_cmd_ds_11n_addba_req *cmd_addba_req); int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf); void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv); struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta); struct mwifiex_rx_reorder_tbl * mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta); void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta); #endif /* _MWIFIEX_11N_RXREORDER_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/11n_rxreorder.c0000644000175000017500000004475112026211315025003 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: 802.11n RX Re-ordering * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" #include "11n_rxreorder.h" /* * This function dispatches all packets in the Rx reorder table until the * start window. * * There could be holes in the buffer, which are skipped by the function. * Since the buffer is linear, the function uses rotation to simulate * circular buffer. */ static void mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl, int start_win) { int pkt_to_send, i; void *rx_tmp_ptr; unsigned long flags; pkt_to_send = (start_win > tbl->start_win) ? min((start_win - tbl->start_win), tbl->win_size) : tbl->win_size; for (i = 0; i < pkt_to_send; ++i) { spin_lock_irqsave(&priv->rx_pkt_lock, flags); rx_tmp_ptr = NULL; if (tbl->rx_reorder_ptr[i]) { rx_tmp_ptr = tbl->rx_reorder_ptr[i]; tbl->rx_reorder_ptr[i] = NULL; } spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); if (rx_tmp_ptr) { if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr); else mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr); } } spin_lock_irqsave(&priv->rx_pkt_lock, flags); /* * We don't have a circular buffer, hence use rotation to simulate * circular buffer */ for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; } tbl->start_win = start_win; spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); } /* * This function dispatches all packets in the Rx reorder table until * a hole is found. * * The start window is adjusted automatically when a hole is located. * Since the buffer is linear, the function uses rotation to simulate * circular buffer. */ static void mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl) { int i, j, xchg; void *rx_tmp_ptr; unsigned long flags; for (i = 0; i < tbl->win_size; ++i) { spin_lock_irqsave(&priv->rx_pkt_lock, flags); if (!tbl->rx_reorder_ptr[i]) { spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); break; } rx_tmp_ptr = tbl->rx_reorder_ptr[i]; tbl->rx_reorder_ptr[i] = NULL; spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr); else mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr); } spin_lock_irqsave(&priv->rx_pkt_lock, flags); /* * We don't have a circular buffer, hence use rotation to simulate * circular buffer */ if (i > 0) { xchg = tbl->win_size - i; for (j = 0; j < xchg; ++j) { tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; tbl->rx_reorder_ptr[i + j] = NULL; } } tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); } /* * This function deletes the Rx reorder table and frees the memory. * * The function stops the associated timer and dispatches all the * pending packets in the Rx reorder table before deletion. */ static void mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl) { unsigned long flags; if (!tbl) return; mwifiex_11n_dispatch_pkt(priv, tbl, (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1)); del_timer(&tbl->timer_context.timer); spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_del(&tbl->list); spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); kfree(tbl->rx_reorder_ptr); kfree(tbl); } /* * This function returns the pointer to an entry in Rx reordering * table which matches the given TA/TID pair. */ struct mwifiex_rx_reorder_tbl * mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) { struct mwifiex_rx_reorder_tbl *tbl; unsigned long flags; spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); return tbl; } } spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); return NULL; } /* This function retrieves the pointer to an entry in Rx reordering * table which matches the given TA and deletes it. */ void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) { struct mwifiex_rx_reorder_tbl *tbl, *tmp; unsigned long flags; if (!ta) return; spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { if (!memcmp(tbl->ta, ta, ETH_ALEN)) { spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); mwifiex_del_rx_reorder_entry(priv, tbl); spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); } } spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); return; } /* * This function finds the last sequence number used in the packets * buffered in Rx reordering table. */ static int mwifiex_11n_find_last_seq_num(struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr) { int i; for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i) if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) return i; return -1; } /* * This function flushes all the packets in Rx reordering table. * * The function checks if any packets are currently buffered in the * table or not. In case there are packets available, it dispatches * them and then dumps the Rx reordering table. */ static void mwifiex_flush_data(unsigned long context) { struct reorder_tmr_cnxt *ctx = (struct reorder_tmr_cnxt *) context; int start_win; start_win = mwifiex_11n_find_last_seq_num(ctx->ptr); if (start_win < 0) return; dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", start_win); mwifiex_11n_dispatch_pkt(ctx->priv, ctx->ptr, (ctx->ptr->start_win + start_win + 1) & (MAX_TID_VALUE - 1)); } /* * This function creates an entry in Rx reordering table for the * given TA/TID. * * The function also initializes the entry with sequence number, window * size as well as initializes the timer. * * If the received TA/TID pair is already present, all the packets are * dispatched and the window size is moved until the SSN. */ static void mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, int tid, int win_size, int seq_num) { int i; struct mwifiex_rx_reorder_tbl *tbl, *new_node; u16 last_seq = 0; unsigned long flags; struct mwifiex_sta_node *node; /* * If we get a TID, ta pair which is already present dispatch all the * the packets and move the window size until the ssn */ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); if (tbl) { mwifiex_11n_dispatch_pkt(priv, tbl, seq_num); return; } /* if !tbl then create one */ new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); if (!new_node) { dev_err(priv->adapter->dev, "%s: failed to alloc new_node\n", __func__); return; } INIT_LIST_HEAD(&new_node->list); new_node->tid = tid; memcpy(new_node->ta, ta, ETH_ALEN); new_node->start_win = seq_num; if (mwifiex_queuing_ra_based(priv)) { dev_dbg(priv->adapter->dev, "info: AP/ADHOC:last_seq=%d start_win=%d\n", last_seq, new_node->start_win); if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { node = mwifiex_get_sta_entry(priv, ta); if (node) last_seq = node->rx_seq[tid]; } } else { last_seq = priv->rx_seq[tid]; } if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && last_seq >= new_node->start_win) new_node->start_win = last_seq + 1; new_node->win_size = win_size; new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, GFP_KERNEL); if (!new_node->rx_reorder_ptr) { kfree((u8 *) new_node); dev_err(priv->adapter->dev, "%s: failed to alloc reorder_ptr\n", __func__); return; } new_node->timer_context.ptr = new_node; new_node->timer_context.priv = priv; init_timer(&new_node->timer_context.timer); new_node->timer_context.timer.function = mwifiex_flush_data; new_node->timer_context.timer.data = (unsigned long) &new_node->timer_context; for (i = 0; i < win_size; ++i) new_node->rx_reorder_ptr[i] = NULL; spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); } /* * This function prepares command for adding a BA request. * * Preparation includes - * - Setting command ID and proper size * - Setting add BA request buffer * - Ensuring correct endian-ness */ int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) { struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); return 0; } /* * This function prepares command for adding a BA response. * * Preparation includes - * - Setting command ID and proper size * - Setting add BA response buffer * - Ensuring correct endian-ness */ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, struct host_cmd_ds_11n_addba_req *cmd_addba_req) { struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; u8 tid; int win_size; uint16_t block_ack_param_set; cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, ETH_ALEN); add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; add_ba_rsp->ssn = cmd_addba_req->ssn; block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) >> BLOCKACKPARAM_TID_POS; add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; /* We donot support AMSDU inside AMPDU, hence reset the bit */ block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; block_ack_param_set |= (priv->add_ba_param.rx_win_size << BLOCKACKPARAM_WINSIZE_POS); add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> BLOCKACKPARAM_WINSIZE_POS; cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, tid, win_size, le16_to_cpu(cmd_addba_req->ssn)); return 0; } /* * This function prepares command for deleting a BA request. * * Preparation includes - * - Setting command ID and proper size * - Setting del BA request buffer * - Ensuring correct endian-ness */ int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) { struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); memcpy(del_ba, data_buf, sizeof(*del_ba)); return 0; } /* * This function identifies if Rx reordering is needed for a received packet. * * In case reordering is required, the function will do the reordering * before sending it to kernel. * * The Rx reorder table is checked first with the received TID/TA pair. If * not found, the received packet is dispatched immediately. But if found, * the packet is reordered and all the packets in the updated Rx reordering * table is dispatched until a hole is found. * * For sequence number less than the starting window, the packet is dropped. */ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, u16 seq_num, u16 tid, u8 *ta, u8 pkt_type, void *payload) { struct mwifiex_rx_reorder_tbl *tbl; int start_win, end_win, win_size; u16 pkt_index; tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); if (!tbl) { if (pkt_type != PKT_TYPE_BAR) { if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) mwifiex_handle_uap_rx_forward(priv, payload); else mwifiex_process_rx_packet(priv->adapter, payload); } return 0; } start_win = tbl->start_win; win_size = tbl->win_size; end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); del_timer(&tbl->timer_context.timer); mod_timer(&tbl->timer_context.timer, jiffies + (MIN_FLUSH_TIMER_MS * win_size * HZ) / 1000); /* * If seq_num is less then starting win then ignore and drop the * packet */ if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {/* Wrap */ if (seq_num >= ((start_win + TWOPOW11) & (MAX_TID_VALUE - 1)) && (seq_num < start_win)) return -1; } else if ((seq_num < start_win) || (seq_num > (start_win + TWOPOW11))) { return -1; } /* * If this packet is a BAR we adjust seq_num as * WinStart = seq_num */ if (pkt_type == PKT_TYPE_BAR) seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); if (((end_win < start_win) && (seq_num < (TWOPOW11 - (MAX_TID_VALUE - start_win))) && (seq_num > end_win)) || ((end_win > start_win) && ((seq_num > end_win) || (seq_num < start_win)))) { end_win = seq_num; if (((seq_num - win_size) + 1) >= 0) start_win = (end_win - win_size) + 1; else start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1; mwifiex_11n_dispatch_pkt(priv, tbl, start_win); } if (pkt_type != PKT_TYPE_BAR) { if (seq_num >= start_win) pkt_index = seq_num - start_win; else pkt_index = (seq_num+MAX_TID_VALUE) - start_win; if (tbl->rx_reorder_ptr[pkt_index]) return -1; tbl->rx_reorder_ptr[pkt_index] = payload; } /* * Dispatch all packets sequentially from start_win until a * hole is found and adjust the start_win appropriately */ mwifiex_11n_scan_and_dispatch(priv, tbl); return 0; } /* * This function deletes an entry for a given TID/TA pair. * * The TID/TA are taken from del BA event body. */ void mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, u8 type, int initiator) { struct mwifiex_rx_reorder_tbl *tbl; struct mwifiex_tx_ba_stream_tbl *ptx_tbl; u8 cleanup_rx_reorder_tbl; unsigned long flags; if (type == TYPE_DELBA_RECEIVE) cleanup_rx_reorder_tbl = (initiator) ? true : false; else cleanup_rx_reorder_tbl = (initiator) ? false : true; dev_dbg(priv->adapter->dev, "event: DELBA: %pM tid=%d initiator=%d\n", peer_mac, tid, initiator); if (cleanup_rx_reorder_tbl) { tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, peer_mac); if (!tbl) { dev_dbg(priv->adapter->dev, "event: TID, TA not found in table\n"); return; } mwifiex_del_rx_reorder_entry(priv, tbl); } else { ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac); if (!ptx_tbl) { dev_dbg(priv->adapter->dev, "event: TID, RA not found in table\n"); return; } spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); } } /* * This function handles the command response of an add BA response. * * Handling includes changing the header fields into CPU format and * creating the stream, provided the add BA is accepted. */ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; int tid, win_size; struct mwifiex_rx_reorder_tbl *tbl; uint16_t block_ack_param_set; block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) >> BLOCKACKPARAM_TID_POS; /* * Check if we had rejected the ADDBA, if yes then do not create * the stream */ if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> BLOCKACKPARAM_WINSIZE_POS; dev_dbg(priv->adapter->dev, "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); } else { dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n", add_ba_rsp->peer_mac_addr, tid); tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, add_ba_rsp->peer_mac_addr); if (tbl) mwifiex_del_rx_reorder_entry(priv, tbl); } return 0; } /* * This function handles BA stream timeout event by preparing and sending * a command to the firmware. */ void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, struct host_cmd_ds_11n_batimeout *event) { struct host_cmd_ds_11n_delba delba; memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); delba.del_ba_param_set |= cpu_to_le16((u16) event->tid << DELBA_TID_POS); delba.del_ba_param_set |= cpu_to_le16( (u16) event->origninator << DELBA_INITIATOR_POS); delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba); } /* * This function cleans up the Rx reorder table by deleting all the entries * and re-initializing. */ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) { struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; unsigned long flags; spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_for_each_entry_safe(del_tbl_ptr, tmp_node, &priv->rx_reorder_tbl_ptr, list) { spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); } spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); mwifiex_reset_11n_rx_seq_num(priv); } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/11n.h0000644000175000017500000001301712026211315022703 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: 802.11n * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_11N_H_ #define _MWIFIEX_11N_H_ #include "11n_aggr.h" #include "11n_rxreorder.h" #include "wmm.h" int mwifiex_ret_11n_delba(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_ds_11n_tx_cfg *txcfg); int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc, u8 **buffer); void mwifiex_cfg_tx_buf(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, struct mwifiex_ie_types_htcap *); int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, u16 action, int *htcap_cfg); void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, struct mwifiex_tx_ba_stream_tbl *tx_tbl); void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv); struct mwifiex_tx_ba_stream_tbl *mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra); void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, enum mwifiex_ba_status ba_status); int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac); int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, int initiator); void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba); int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, struct mwifiex_ds_rx_reorder_tbl *buf); int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, struct mwifiex_ds_tx_ba_stream_tbl *buf); int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, int cmd_action, u16 *buf_size); int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, int cmd_action, struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); /* * This function checks whether AMPDU is allowed or not for a particular TID. */ static inline u8 mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, int tid) { return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) ? true : false); } /* * This function checks whether AMSDU is allowed or not for a particular TID. */ static inline u8 mwifiex_is_amsdu_allowed(struct mwifiex_private *priv, int tid) { return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) && (priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03))) ? true : false); } /* * This function checks whether a space is available for new BA stream or not. */ static inline u8 mwifiex_space_avail_for_new_ba_stream( struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; u8 i; u32 ba_stream_num = 0; for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv) ba_stream_num += mwifiex_wmm_list_len( &priv->tx_ba_stream_tbl_ptr); } return ((ba_stream_num < MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) ? true : false); } /* * This function finds the correct Tx BA stream to delete. * * Upon successfully locating, both the TID and the RA are returned. */ static inline u8 mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, int *ptid, u8 *ra) { int tid; u8 ret = false; struct mwifiex_tx_ba_stream_tbl *tx_tbl; unsigned long flags; tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; *ptid = tx_tbl->tid; memcpy(ra, tx_tbl->ra, ETH_ALEN); ret = true; } } spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); return ret; } /* * This function checks whether BA stream is set up or not. */ static inline int mwifiex_is_ba_stream_setup(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr, int tid) { struct mwifiex_tx_ba_stream_tbl *tx_tbl; tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra); if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl)) return true; return false; } /* * This function checks whether associated station is 11n enabled */ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, struct mwifiex_sta_node *node) { if (!node || (priv->bss_role != MWIFIEX_BSS_ROLE_UAP) || !priv->ap_11n_enabled) return 0; return node->is_11n_enabled; } #endif /* !_MWIFIEX_11N_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/11n.c0000644000175000017500000005323112026211315022700 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: 802.11n * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" /* * Fills HT capability information field, AMPDU Parameters field, HT extended * capability field, and supported MCS set fields. * * HT capability information field, AMPDU Parameters field, supported MCS set * fields are retrieved from cfg80211 stack * * RD responder bit to set to clear in the extended capability header. */ void mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, struct mwifiex_ie_types_htcap *ht_cap) { uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info); struct ieee80211_supported_band *sband = priv->wdev->wiphy->bands[radio_type]; ht_cap->ht_cap.ampdu_params_info = (sband->ht_cap.ampdu_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) | ((sband->ht_cap.ampdu_density << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & IEEE80211_HT_AMPDU_PARM_DENSITY); memcpy((u8 *) &ht_cap->ht_cap.mcs, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); if (priv->bss_mode == NL80211_IFTYPE_STATION || sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); /* Clear RD responder bit */ ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; ht_cap->ht_cap.cap_info = cpu_to_le16(sband->ht_cap.cap); ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap); } /* * This function returns the pointer to an entry in BA Stream * table which matches the requested BA status. */ static struct mwifiex_tx_ba_stream_tbl * mwifiex_get_ba_status(struct mwifiex_private *priv, enum mwifiex_ba_status ba_status) { struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; unsigned long flags; spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { if (tx_ba_tsr_tbl->ba_status == ba_status) { spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); return tx_ba_tsr_tbl; } } spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); return NULL; } /* * This function handles the command response of delete a block * ack request. * * The function checks the response success status and takes action * accordingly (send an add BA request in case of success, or recreate * the deleted stream in case of failure, if the add BA was also * initiated by us). */ int mwifiex_ret_11n_delba(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { int tid; struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba; uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); tid = del_ba_param_set >> DELBA_TID_POS; if (del_ba->del_result == BA_RESULT_SUCCESS) { mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr, TYPE_DELBA_SENT, INITIATOR_BIT(del_ba_param_set)); tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); if (tx_ba_tbl) mwifiex_send_addba(priv, tx_ba_tbl->tid, tx_ba_tbl->ra); } else { /* * In case of failure, recreate the deleted stream in case * we initiated the ADDBA */ if (!INITIATOR_BIT(del_ba_param_set)) return 0; mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid, BA_SETUP_INPROGRESS); tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); if (tx_ba_tbl) mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra, TYPE_DELBA_SENT, true); } return 0; } /* * This function handles the command response of add a block * ack request. * * Handling includes changing the header fields to CPU formats, checking * the response success status and taking actions accordingly (delete the * BA stream table in case of failure). */ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { int tid; struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) & SSN_MASK); tid = (le16_to_cpu(add_ba_rsp->block_ack_param_set) & IEEE80211_ADDBA_PARAM_TID_MASK) >> BLOCKACKPARAM_TID_POS; if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr); if (tx_ba_tbl) { dev_dbg(priv->adapter->dev, "info: BA stream complete\n"); tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; } else { dev_err(priv->adapter->dev, "BA stream not created\n"); } } else { mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, TYPE_DELBA_SENT, true); if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) priv->aggr_prio_tbl[tid].ampdu_ap = BA_STREAM_NOT_ALLOWED; } return 0; } /* * This function prepares command of reconfigure Tx buffer. * * Preparation includes - * - Setting command ID, action and proper size * - Setting Tx buffer size (for SET only) * - Ensuring correct endian-ness */ int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, int cmd_action, u16 *buf_size) { struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; u16 action = (u16) cmd_action; cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); tx_buf->action = cpu_to_le16(action); switch (action) { case HostCmd_ACT_GEN_SET: dev_dbg(priv->adapter->dev, "cmd: set tx_buf=%d\n", *buf_size); tx_buf->buff_size = cpu_to_le16(*buf_size); break; case HostCmd_ACT_GEN_GET: default: tx_buf->buff_size = 0; break; } return 0; } /* * This function prepares command of AMSDU aggregation control. * * Preparation includes - * - Setting command ID, action and proper size * - Setting AMSDU control parameters (for SET only) * - Ensuring correct endian-ness */ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, int cmd_action, struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl) { struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = &cmd->params.amsdu_aggr_ctrl; u16 action = (u16) cmd_action; cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) + S_DS_GEN); amsdu_ctrl->action = cpu_to_le16(action); switch (action) { case HostCmd_ACT_GEN_SET: amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); amsdu_ctrl->curr_buf_size = 0; break; case HostCmd_ACT_GEN_GET: default: amsdu_ctrl->curr_buf_size = 0; break; } return 0; } /* * This function prepares 11n configuration command. * * Preparation includes - * - Setting command ID, action and proper size * - Setting HT Tx capability and HT Tx information fields * - Ensuring correct endian-ness */ int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_ds_11n_tx_cfg *txcfg) { struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); htcfg->action = cpu_to_le16(cmd_action); htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); return 0; } /* * This function appends an 11n TLV to a buffer. * * Buffer allocation is responsibility of the calling * function. No size validation is made here. * * The function fills up the following sections, if applicable - * - HT capability IE * - HT information IE (with channel list) * - 20/40 BSS Coexistence IE * - HT Extended Capabilities IE */ int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc, u8 **buffer) { struct mwifiex_ie_types_htcap *ht_cap; struct mwifiex_ie_types_htinfo *ht_info; struct mwifiex_ie_types_chan_list_param_set *chan_list; struct mwifiex_ie_types_2040bssco *bss_co_2040; struct mwifiex_ie_types_extcap *ext_cap; int ret_len = 0; struct ieee80211_supported_band *sband; u8 radio_type; if (!buffer || !*buffer) return ret_len; radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); sband = priv->wdev->wiphy->bands[radio_type]; if (bss_desc->bcn_ht_cap) { ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); ht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_ht_cap)); memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), (u8 *) bss_desc->bcn_ht_cap + sizeof(struct ieee_types_header), le16_to_cpu(ht_cap->header.len)); mwifiex_fill_cap_info(priv, radio_type, ht_cap); *buffer += sizeof(struct mwifiex_ie_types_htcap); ret_len += sizeof(struct mwifiex_ie_types_htcap); } if (bss_desc->bcn_ht_oper) { if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION); ht_info->header.len = cpu_to_le16( sizeof(struct ieee80211_ht_operation)); memcpy((u8 *) ht_info + sizeof(struct mwifiex_ie_types_header), (u8 *) bss_desc->bcn_ht_oper + sizeof(struct ieee_types_header), le16_to_cpu(ht_info->header.len)); if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) ht_info->ht_oper.ht_param &= ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | IEEE80211_HT_PARAM_CHA_SEC_OFFSET); *buffer += sizeof(struct mwifiex_ie_types_htinfo); ret_len += sizeof(struct mwifiex_ie_types_htinfo); } chan_list = (struct mwifiex_ie_types_chan_list_param_set *) *buffer; memset(chan_list, 0, sizeof(struct mwifiex_ie_types_chan_list_param_set)); chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); chan_list->header.len = cpu_to_le16( sizeof(struct mwifiex_ie_types_chan_list_param_set) - sizeof(struct mwifiex_ie_types_header)); chan_list->chan_scan_param[0].chan_number = bss_desc->bcn_ht_oper->primary_chan; chan_list->chan_scan_param[0].radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. radio_type, (bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); } if (bss_desc->bcn_bss_co_2040) { bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer; memset(bss_co_2040, 0, sizeof(struct mwifiex_ie_types_2040bssco)); bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); bss_co_2040->header.len = cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); memcpy((u8 *) bss_co_2040 + sizeof(struct mwifiex_ie_types_header), bss_desc->bcn_bss_co_2040 + sizeof(struct ieee_types_header), le16_to_cpu(bss_co_2040->header.len)); *buffer += sizeof(struct mwifiex_ie_types_2040bssco); ret_len += sizeof(struct mwifiex_ie_types_2040bssco); } if (bss_desc->bcn_ext_cap) { ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap)); memcpy((u8 *)ext_cap + sizeof(struct mwifiex_ie_types_header), bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header), le16_to_cpu(ext_cap->header.len)); *buffer += sizeof(struct mwifiex_ie_types_extcap); ret_len += sizeof(struct mwifiex_ie_types_extcap); } return ret_len; } /* * This function reconfigures the Tx buffer size in firmware. * * This function prepares a firmware command and issues it, if * the current Tx buffer size is different from the one requested. * Maximum configurable Tx buffer size is limited by the HT capability * field value. */ void mwifiex_cfg_tx_buf(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { u16 max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_2K; u16 tx_buf, curr_tx_buf_size = 0; if (bss_desc->bcn_ht_cap) { if (le16_to_cpu(bss_desc->bcn_ht_cap->cap_info) & IEEE80211_HT_CAP_MAX_AMSDU) max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_8K; else max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_4K; } tx_buf = min(priv->adapter->max_tx_buf_size, max_amsdu); dev_dbg(priv->adapter->dev, "info: max_amsdu=%d, max_tx_buf=%d\n", max_amsdu, priv->adapter->max_tx_buf_size); if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_2K) curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_4K) curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_8K) curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_8K; if (curr_tx_buf_size != tx_buf) mwifiex_send_cmd_async(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, HostCmd_ACT_GEN_SET, 0, &tx_buf); } /* * This function checks if the given pointer is valid entry of * Tx BA Stream table. */ static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv, struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr) { struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { if (tx_ba_tsr_tbl == tx_tbl_ptr) return true; } return false; } /* * This function deletes the given entry in Tx BA Stream table. * * The function also performs a validity check on the supplied * pointer before trying to delete. */ void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl) { if (!tx_ba_tsr_tbl && mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl)) return; dev_dbg(priv->adapter->dev, "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl); list_del(&tx_ba_tsr_tbl->list); kfree(tx_ba_tsr_tbl); } /* * This function deletes all the entries in Tx BA Stream table. */ void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) { int i; struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; unsigned long flags; spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_for_each_entry_safe(del_tbl_ptr, tmp_node, &priv->tx_ba_stream_tbl_ptr, list) mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); for (i = 0; i < MAX_NUM_TID; ++i) priv->aggr_prio_tbl[i].ampdu_ap = priv->aggr_prio_tbl[i].ampdu_user; } /* * This function returns the pointer to an entry in BA Stream * table which matches the given RA/TID pair. */ struct mwifiex_tx_ba_stream_tbl * mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra) { struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; unsigned long flags; spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { if (!memcmp(tx_ba_tsr_tbl->ra, ra, ETH_ALEN) && tx_ba_tsr_tbl->tid == tid) { spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); return tx_ba_tsr_tbl; } } spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); return NULL; } /* * This function creates an entry in Tx BA stream table for the * given RA/TID pair. */ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, enum mwifiex_ba_status ba_status) { struct mwifiex_tx_ba_stream_tbl *new_node; unsigned long flags; if (!mwifiex_get_ba_tbl(priv, tid, ra)) { new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl), GFP_ATOMIC); if (!new_node) { dev_err(priv->adapter->dev, "%s: failed to alloc new_node\n", __func__); return; } INIT_LIST_HEAD(&new_node->list); new_node->tid = tid; new_node->ba_status = ba_status; memcpy(new_node->ra, ra, ETH_ALEN); spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); } } /* * This function sends an add BA request to the given TID/RA pair. */ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) { struct host_cmd_ds_11n_addba_req add_ba_req; static u8 dialog_tok; int ret; dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid); add_ba_req.block_ack_param_set = cpu_to_le16( (u16) ((tid << BLOCKACKPARAM_TID_POS) | (priv->add_ba_param. tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | IMMEDIATE_BLOCK_ACK)); add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); ++dialog_tok; if (dialog_tok == 0) dialog_tok = 1; add_ba_req.dialog_token = dialog_tok; memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); /* We don't wait for the response of this command */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_REQ, 0, 0, &add_ba_req); return ret; } /* * This function sends a delete BA request to the given TID/RA pair. */ int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, int initiator) { struct host_cmd_ds_11n_delba delba; int ret; uint16_t del_ba_param_set; memset(&delba, 0, sizeof(delba)); delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS); del_ba_param_set = le16_to_cpu(delba.del_ba_param_set); if (initiator) del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; else del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); /* We don't wait for the response of this command */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, HostCmd_ACT_GEN_SET, 0, &delba); return ret; } /* * This function handles the command response of a delete BA request. */ void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba) { struct host_cmd_ds_11n_delba *cmd_del_ba = (struct host_cmd_ds_11n_delba *) del_ba; uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); int tid; tid = del_ba_param_set >> DELBA_TID_POS; mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr, TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set)); } /* * This function retrieves the Rx reordering table. */ int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, struct mwifiex_ds_rx_reorder_tbl *buf) { int i; struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; int count = 0; unsigned long flags; spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) rx_reo_tbl->buffer[i] = true; else rx_reo_tbl->buffer[i] = false; } rx_reo_tbl++; count++; if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) break; } spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); return count; } /* * This function retrieves the Tx BA stream table. */ int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, struct mwifiex_ds_tx_ba_stream_tbl *buf) { struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; int count = 0; unsigned long flags; spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; dev_dbg(priv->adapter->dev, "data: %s tid=%d\n", __func__, rx_reo_tbl->tid); memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); rx_reo_tbl++; count++; if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) break; } spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); return count; } /* * This function retrieves the entry for specific tx BA stream table by RA and * deletes it. */ void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra) { struct mwifiex_tx_ba_stream_tbl *tbl, *tmp; unsigned long flags; if (!ra) return; spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) { if (!memcmp(tbl->ra, ra, ETH_ALEN)) { spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); } } spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); return; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/11n_aggr.h0000644000175000017500000000247712026211315023713 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: 802.11n Aggregation * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #ifndef _MWIFIEX_11N_AGGR_H_ #define _MWIFIEX_11N_AGGR_H_ #define PKT_TYPE_AMSDU 0xE6 #define MIN_NUM_AMSDU 2 int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, struct sk_buff *skb); int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr, int headroom, int ptr_index, unsigned long flags) __releases(&priv->wmm.ra_list_spinlock); #endif /* !_MWIFIEX_11N_AGGR_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/11n_aggr.c0000644000175000017500000002266312026211315023705 0ustar mcgrofmcgrof/* * Marvell Wireless LAN device driver: 802.11n Aggregation * * Copyright (C) 2011, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. */ #include "decl.h" #include "ioctl.h" #include "util.h" #include "fw.h" #include "main.h" #include "wmm.h" #include "11n.h" #include "11n_aggr.h" /* * Creates an AMSDU subframe for aggregation into one AMSDU packet. * * The resultant AMSDU subframe format is - * * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ * | DA | SA | Length | SNAP header | MSDU | * | data[0..5] | data[6..11] | | | data[14..] | * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> * * This function also computes the amount of padding required to make the * buffer length multiple of 4 bytes. * * Data => |DA|SA|SNAP-TYPE|........ .| * MSDU => |DA|SA|Length|SNAP|...... ..| */ static int mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, struct sk_buff *skb_src, int *pad) { int dt_offset; struct rfc_1042_hdr snap = { 0xaa, /* LLC DSAP */ 0xaa, /* LLC SSAP */ 0x03, /* LLC CTRL */ {0x00, 0x00, 0x00}, /* SNAP OUI */ 0x0000 /* SNAP type */ /* * This field will be overwritten * later with ethertype */ }; struct tx_packet_hdr *tx_header; tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header)); /* Copy DA and SA */ dt_offset = 2 * ETH_ALEN; memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); /* Copy SNAP header */ snap.snap_type = *(u16 *) ((u8 *)skb_src->data + dt_offset); dt_offset += sizeof(u16); memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); skb_pull(skb_src, dt_offset); /* Update Length field */ tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); /* Add payload */ memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len); /* Add padding for new MSDU to start from 4 byte boundary */ *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4; return skb_aggr->len + *pad; } /* * Adds TxPD to AMSDU header. * * Each AMSDU packet will contain one TxPD at the beginning, * followed by multiple AMSDU subframes. */ static void mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, struct sk_buff *skb) { struct txpd *local_tx_pd; skb_push(skb, sizeof(*local_tx_pd)); local_tx_pd = (struct txpd *) skb->data; memset(local_tx_pd, 0, sizeof(struct txpd)); /* Original priority has been overwritten */ local_tx_pd->priority = (u8) skb->priority; local_tx_pd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); local_tx_pd->bss_num = priv->bss_num; local_tx_pd->bss_type = priv->bss_type; /* Always zero as the data is followed by struct txpd */ local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - sizeof(*local_tx_pd)); if (local_tx_pd->tx_control == 0) /* TxCtrl set by user or default */ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && priv->adapter->pps_uapsd_mode) { if (true == mwifiex_check_last_packet_indication(priv)) { priv->adapter->tx_lock_flag = true; local_tx_pd->flags = MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; } } } /* * Create aggregated packet. * * This function creates an aggregated MSDU packet, by combining buffers * from the RA list. Each individual buffer is encapsulated as an AMSDU * subframe and all such subframes are concatenated together to form the * AMSDU packet. * * A TxPD is also added to the front of the resultant AMSDU packets for * transmission. The resultant packets format is - * * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| * | | 1 | 2 | .. | n | * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ */ int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *pra_list, int headroom, int ptrindex, unsigned long ra_list_flags) __releases(&priv->wmm.ra_list_spinlock) { struct mwifiex_adapter *adapter = priv->adapter; struct sk_buff *skb_aggr, *skb_src; struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; int pad = 0, ret; struct mwifiex_tx_param tx_param; struct txpd *ptx_pd = NULL; skb_src = skb_peek(&pra_list->skb_head); if (!skb_src) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); return 0; } tx_info_src = MWIFIEX_SKB_TXCB(skb_src); skb_aggr = dev_alloc_skb(adapter->tx_buf_size); if (!skb_aggr) { dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); return -1; } skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); tx_info_aggr->bss_type = tx_info_src->bss_type; tx_info_aggr->bss_num = tx_info_src->bss_num; skb_aggr->priority = skb_src->priority; do { /* Check if AMSDU can accommodate this MSDU */ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) break; skb_src = skb_dequeue(&pra_list->skb_head); pra_list->total_pkts_size -= skb_src->len; atomic_dec(&priv->wmm.tx_pkts_queued); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); mwifiex_write_data_complete(adapter, skb_src, 0); spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); return -1; } if (skb_tailroom(skb_aggr) < pad) { pad = 0; break; } skb_put(skb_aggr, pad); skb_src = skb_peek(&pra_list->skb_head); } while (skb_src); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); /* Last AMSDU packet does not need padding */ skb_trim(skb_aggr, skb_aggr->len - pad); /* Form AMSDU */ mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) ptx_pd = (struct txpd *)skb_aggr->data; skb_push(skb_aggr, headroom); if (adapter->iface_type == MWIFIEX_USB) { adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb_aggr, NULL); } else { /* * Padding per MSDU will affect the length of next * packet and hence the exact length of next packet * is uncertain here. * * Also, aggregation of transmission buffer, while * downloading the data to the card, wont gain much * on the AMSDU packets as the AMSDU packets utilizes * the transmission buffer space to the maximum * (adapter->tx_buf_size). */ tx_param.next_pkt_len = 0; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, skb_aggr, &tx_param); } switch (ret) { case -EBUSY: spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); mwifiex_write_data_complete(adapter, skb_aggr, -1); return -1; } if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && adapter->pps_uapsd_mode && adapter->tx_lock_flag) { priv->adapter->tx_lock_flag = false; if (ptx_pd) ptx_pd->flags = 0; } skb_queue_tail(&pra_list->skb_head, skb_aggr); pra_list->total_pkts_size += skb_aggr->len; atomic_inc(&priv->wmm.tx_pkts_queued); tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); break; case -1: adapter->data_sent = false; dev_err(adapter->dev, "%s: host_to_card failed: %#x\n", __func__, ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb_aggr, ret); return 0; case -EINPROGRESS: adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb_aggr, ret); break; default: break; } if (ret != -EBUSY) { spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); if (mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { priv->wmm.packets_out[ptrindex]++; priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list; } /* Now bss_prio_cur pointer points to next node */ adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = list_first_entry( &adapter->bss_prio_tbl[priv->bss_priority] .bss_prio_cur->list, struct mwifiex_bss_prio_node, list); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/Makefile0000644000175000017500000000311212026211315023566 0ustar mcgrofmcgrof# # Copyright (C) 2011, Marvell International Ltd. # # This software file (the "File") is distributed by Marvell International # Ltd. under the terms of the GNU General Public License Version 2, June 1991 # (the "License"). You may use, redistribute and/or modify this File in # accordance with the terms and conditions of the License, a copy of which # is available by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the # worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE # IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE # ARE EXPRESSLY DISCLAIMED. The License provides additional details about # this warranty disclaimer. mwifiex-y += main.o mwifiex-y += init.o mwifiex-y += cfp.o mwifiex-y += cmdevt.o mwifiex-y += util.o mwifiex-y += txrx.o mwifiex-y += wmm.o mwifiex-y += 11n.o mwifiex-y += 11n_aggr.o mwifiex-y += 11n_rxreorder.o mwifiex-y += scan.o mwifiex-y += join.o mwifiex-y += sta_ioctl.o mwifiex-y += sta_cmd.o mwifiex-y += uap_cmd.o mwifiex-y += ie.o mwifiex-y += sta_cmdresp.o mwifiex-y += sta_event.o mwifiex-y += uap_event.o mwifiex-y += sta_tx.o mwifiex-y += sta_rx.o mwifiex-y += uap_txrx.o mwifiex-y += cfg80211.o mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_MWIFIEX) += mwifiex.o mwifiex_sdio-y += sdio.o obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o mwifiex_pcie-y += pcie.o obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o mwifiex_usb-y += usb.o obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o compat-drivers-2012-09-18/drivers/net/wireless/mwifiex/Kconfig0000644000175000017500000000224412026211315023436 0ustar mcgrofmcgrofconfig MWIFIEX tristate "Marvell WiFi-Ex Driver" depends on CFG80211 select LIB80211 ---help--- This adds support for wireless adapters based on Marvell 802.11n chipsets. If you choose to build it as a module, it will be called mwifiex. config MWIFIEX_SDIO tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797" depends on MWIFIEX && MMC select FW_LOADER ---help--- This adds support for wireless adapters based on Marvell 8786/8787/8797 chipsets with SDIO interface. If you choose to build it as a module, it will be called mwifiex_sdio. config MWIFIEX_PCIE tristate "Marvell WiFi-Ex Driver for PCIE 8766" depends on MWIFIEX && PCI select FW_LOADER ---help--- This adds support for wireless adapters based on Marvell 8766 chipset with PCIe interface. If you choose to build it as a module, it will be called mwifiex_pcie. config MWIFIEX_USB tristate "Marvell WiFi-Ex Driver for USB8797" depends on MWIFIEX && USB select FW_LOADER ---help--- This adds support for wireless adapters based on Marvell Avastar 88W8797 chipset with USB interface. If you choose to build it as a module, it will be called mwifiex_usb. compat-drivers-2012-09-18/drivers/net/wireless/orinoco/0000755000175000017500000000000012026211315022131 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco_usb.c0000644000175000017500000013143312026211315024623 0ustar mcgrofmcgrof/* * USB Orinoco driver * * Copyright (c) 2003 Manuel Estrada Sainz * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. * * Queueing code based on linux-wlan-ng 0.2.1-pre5 * * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. * * The license is the same as above. * * Initialy based on USB Skeleton driver - 0.7 * * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * NOTE: The original USB Skeleton driver is GPL, but all that code is * gone so MPL/GPL applies. */ #define DRIVER_NAME "orinoco_usb" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mic.h" #include "orinoco.h" #ifndef URB_ASYNC_UNLINK #define URB_ASYNC_UNLINK 0 #endif /* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; #define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) struct header_struct { /* 802.3 */ u8 dest[ETH_ALEN]; u8 src[ETH_ALEN]; __be16 len; /* 802.2 */ u8 dsap; u8 ssap; u8 ctrl; /* SNAP */ u8 oui[3]; __be16 ethertype; } __packed; struct ez_usb_fw { u16 size; const u8 *code; }; static struct ez_usb_fw firmware = { .size = 0, .code = NULL, }; #ifdef CONFIG_USB_DEBUG static int debug = 1; #else static int debug; #endif /* Debugging macros */ #undef dbg #define dbg(format, arg...) \ do { if (debug) printk(KERN_DEBUG PFX "%s: " format "\n", \ __func__ , ## arg); } while (0) #undef err #define err(format, arg...) \ do { printk(KERN_ERR PFX format "\n", ## arg); } while (0) /* Module paramaters */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug enabled or not"); MODULE_FIRMWARE("orinoco_ezusb_fw"); /* * Under some conditions, the card gets stuck and stops paying attention * to the world (i.e. data communication stalls) until we do something to * it. Sending an INQ_TALLIES command seems to be enough and should be * harmless otherwise. This behaviour has been observed when using the * driver on a systemimager client during installation. In the past a * timer was used to send INQ_TALLIES commands when there was no other * activity, but it was troublesome and was removed. */ #define USB_COMPAQ_VENDOR_ID 0x049f /* Compaq Computer Corp. */ #define USB_COMPAQ_WL215_ID 0x001f /* Compaq WL215 USB Adapter */ #define USB_COMPAQ_W200_ID 0x0076 /* Compaq W200 USB Adapter */ #define USB_HP_WL215_ID 0x0082 /* Compaq WL215 USB Adapter */ #define USB_MELCO_VENDOR_ID 0x0411 #define USB_BUFFALO_L11_ID 0x0006 /* BUFFALO WLI-USB-L11 */ #define USB_BUFFALO_L11G_WR_ID 0x000B /* BUFFALO WLI-USB-L11G-WR */ #define USB_BUFFALO_L11G_ID 0x000D /* BUFFALO WLI-USB-L11G */ #define USB_LUCENT_VENDOR_ID 0x047E /* Lucent Technologies */ #define USB_LUCENT_ORINOCO_ID 0x0300 /* Lucent/Agere Orinoco USB Client */ #define USB_AVAYA8_VENDOR_ID 0x0D98 #define USB_AVAYAE_VENDOR_ID 0x0D9E #define USB_AVAYA_WIRELESS_ID 0x0300 /* Avaya Wireless USB Card */ #define USB_AGERE_VENDOR_ID 0x0D4E /* Agere Systems */ #define USB_AGERE_MODEL0801_ID 0x1000 /* Wireless USB Card Model 0801 */ #define USB_AGERE_MODEL0802_ID 0x1001 /* Wireless USB Card Model 0802 */ #define USB_AGERE_REBRANDED_ID 0x047A /* WLAN USB Card */ #define USB_ELSA_VENDOR_ID 0x05CC #define USB_ELSA_AIRLANCER_ID 0x3100 /* ELSA AirLancer USB-11 */ #define USB_LEGEND_VENDOR_ID 0x0E7C #define USB_LEGEND_JOYNET_ID 0x0300 /* Joynet WLAN USB Card */ #define USB_SAMSUNG_VENDOR_ID 0x04E8 #define USB_SAMSUNG_SEW2001U1_ID 0x5002 /* Samsung SEW-2001u Card */ #define USB_SAMSUNG_SEW2001U2_ID 0x5B11 /* Samsung SEW-2001u Card */ #define USB_SAMSUNG_SEW2003U_ID 0x7011 /* Samsung SEW-2003U Card */ #define USB_IGATE_VENDOR_ID 0x0681 #define USB_IGATE_IGATE_11M_ID 0x0012 /* I-GATE 11M USB Card */ #define USB_FUJITSU_VENDOR_ID 0x0BF8 #define USB_FUJITSU_E1100_ID 0x1002 /* connect2AIR WLAN E-1100 USB */ #define USB_2WIRE_VENDOR_ID 0x1630 #define USB_2WIRE_WIRELESS_ID 0xff81 /* 2Wire Wireless USB adapter */ #define EZUSB_REQUEST_FW_TRANS 0xA0 #define EZUSB_REQUEST_TRIGER 0xAA #define EZUSB_REQUEST_TRIG_AC 0xAC #define EZUSB_CPUCS_REG 0x7F92 #define EZUSB_RID_TX 0x0700 #define EZUSB_RID_RX 0x0701 #define EZUSB_RID_INIT1 0x0702 #define EZUSB_RID_ACK 0x0710 #define EZUSB_RID_READ_PDA 0x0800 #define EZUSB_RID_PROG_INIT 0x0852 #define EZUSB_RID_PROG_SET_ADDR 0x0853 #define EZUSB_RID_PROG_BYTES 0x0854 #define EZUSB_RID_PROG_END 0x0855 #define EZUSB_RID_DOCMD 0x0860 /* Recognize info frames */ #define EZUSB_IS_INFO(id) ((id >= 0xF000) && (id <= 0xF2FF)) #define EZUSB_MAGIC 0x0210 #define EZUSB_FRAME_DATA 1 #define EZUSB_FRAME_CONTROL 2 #define DEF_TIMEOUT (3 * HZ) #define BULK_BUF_SIZE 2048 #define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet)) #define FW_BUF_SIZE 64 #define FW_VAR_OFFSET_PTR 0x359 #define FW_VAR_VALUE 0 #define FW_HOLE_START 0x100 #define FW_HOLE_END 0x300 struct ezusb_packet { __le16 magic; /* 0x0210 */ u8 req_reply_count; u8 ans_reply_count; __le16 frame_type; /* 0x01 for data frames, 0x02 otherwise */ __le16 size; /* transport size */ __le16 crc; /* CRC up to here */ __le16 hermes_len; __le16 hermes_rid; u8 data[0]; } __packed; /* Table of devices that work or may work with this driver */ static struct usb_device_id ezusb_table[] = { {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)}, {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)}, {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)}, {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11_ID)}, {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_WR_ID)}, {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_ID)}, {USB_DEVICE(USB_LUCENT_VENDOR_ID, USB_LUCENT_ORINOCO_ID)}, {USB_DEVICE(USB_AVAYA8_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, {USB_DEVICE(USB_AVAYAE_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0801_ID)}, {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0802_ID)}, {USB_DEVICE(USB_ELSA_VENDOR_ID, USB_ELSA_AIRLANCER_ID)}, {USB_DEVICE(USB_LEGEND_VENDOR_ID, USB_LEGEND_JOYNET_ID)}, {USB_DEVICE_VER(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U1_ID, 0, 0)}, {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U2_ID)}, {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2003U_ID)}, {USB_DEVICE(USB_IGATE_VENDOR_ID, USB_IGATE_IGATE_11M_ID)}, {USB_DEVICE(USB_FUJITSU_VENDOR_ID, USB_FUJITSU_E1100_ID)}, {USB_DEVICE(USB_2WIRE_VENDOR_ID, USB_2WIRE_WIRELESS_ID)}, {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_REBRANDED_ID)}, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, ezusb_table); /* Structure to hold all of our device specific stuff */ struct ezusb_priv { struct usb_device *udev; struct net_device *dev; struct mutex mtx; spinlock_t req_lock; struct list_head req_pending; struct list_head req_active; spinlock_t reply_count_lock; u16 hermes_reg_fake[0x40]; u8 *bap_buf; struct urb *read_urb; int read_pipe; int write_pipe; u8 reply_count; }; enum ezusb_state { EZUSB_CTX_START, EZUSB_CTX_QUEUED, EZUSB_CTX_REQ_SUBMITTED, EZUSB_CTX_REQ_COMPLETE, EZUSB_CTX_RESP_RECEIVED, EZUSB_CTX_REQ_TIMEOUT, EZUSB_CTX_REQ_FAILED, EZUSB_CTX_RESP_TIMEOUT, EZUSB_CTX_REQSUBMIT_FAIL, EZUSB_CTX_COMPLETE, }; struct request_context { struct list_head list; atomic_t refcount; struct completion done; /* Signals that CTX is dead */ int killed; struct urb *outurb; /* OUT for req pkt */ struct ezusb_priv *upriv; struct ezusb_packet *buf; int buf_length; struct timer_list timer; /* Timeout handling */ enum ezusb_state state; /* Current state */ /* the RID that we will wait for */ u16 out_rid; u16 in_rid; }; /* Forward declarations */ static void ezusb_ctx_complete(struct request_context *ctx); static void ezusb_req_queue_run(struct ezusb_priv *upriv); static void ezusb_bulk_in_callback(struct urb *urb); static inline u8 ezusb_reply_inc(u8 count) { if (count < 0x7F) return count + 1; else return 1; } static void ezusb_request_context_put(struct request_context *ctx) { if (!atomic_dec_and_test(&ctx->refcount)) return; WARN_ON(!ctx->done.done); BUG_ON(ctx->outurb->status == -EINPROGRESS); BUG_ON(timer_pending(&ctx->timer)); usb_free_urb(ctx->outurb); kfree(ctx->buf); kfree(ctx); } static inline void ezusb_mod_timer(struct ezusb_priv *upriv, struct timer_list *timer, unsigned long expire) { if (!upriv->udev) return; mod_timer(timer, expire); } static void ezusb_request_timerfn(u_long _ctx) { struct request_context *ctx = (void *) _ctx; ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; if (usb_unlink_urb(ctx->outurb) == -EINPROGRESS) { ctx->state = EZUSB_CTX_REQ_TIMEOUT; } else { ctx->state = EZUSB_CTX_RESP_TIMEOUT; dbg("couldn't unlink"); atomic_inc(&ctx->refcount); ctx->killed = 1; ezusb_ctx_complete(ctx); ezusb_request_context_put(ctx); } }; static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv, u16 out_rid, u16 in_rid) { struct request_context *ctx; ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); if (!ctx) return NULL; ctx->buf = kmalloc(BULK_BUF_SIZE, GFP_ATOMIC); if (!ctx->buf) { kfree(ctx); return NULL; } ctx->outurb = usb_alloc_urb(0, GFP_ATOMIC); if (!ctx->outurb) { kfree(ctx->buf); kfree(ctx); return NULL; } ctx->upriv = upriv; ctx->state = EZUSB_CTX_START; ctx->out_rid = out_rid; ctx->in_rid = in_rid; atomic_set(&ctx->refcount, 1); init_completion(&ctx->done); init_timer(&ctx->timer); ctx->timer.function = ezusb_request_timerfn; ctx->timer.data = (u_long) ctx; return ctx; } /* Hopefully the real complete_all will soon be exported, in the mean * while this should work. */ static inline void ezusb_complete_all(struct completion *comp) { complete(comp); complete(comp); complete(comp); complete(comp); } static void ezusb_ctx_complete(struct request_context *ctx) { struct ezusb_priv *upriv = ctx->upriv; unsigned long flags; spin_lock_irqsave(&upriv->req_lock, flags); list_del_init(&ctx->list); if (upriv->udev) { spin_unlock_irqrestore(&upriv->req_lock, flags); ezusb_req_queue_run(upriv); spin_lock_irqsave(&upriv->req_lock, flags); } switch (ctx->state) { case EZUSB_CTX_COMPLETE: case EZUSB_CTX_REQSUBMIT_FAIL: case EZUSB_CTX_REQ_FAILED: case EZUSB_CTX_REQ_TIMEOUT: case EZUSB_CTX_RESP_TIMEOUT: spin_unlock_irqrestore(&upriv->req_lock, flags); if ((ctx->out_rid == EZUSB_RID_TX) && upriv->dev) { struct net_device *dev = upriv->dev; struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; if (ctx->state != EZUSB_CTX_COMPLETE) stats->tx_errors++; else stats->tx_packets++; netif_wake_queue(dev); } ezusb_complete_all(&ctx->done); ezusb_request_context_put(ctx); break; default: spin_unlock_irqrestore(&upriv->req_lock, flags); if (!upriv->udev) { /* This is normal, as all request contexts get flushed * when the device is disconnected */ err("Called, CTX not terminating, but device gone"); ezusb_complete_all(&ctx->done); ezusb_request_context_put(ctx); break; } err("Called, CTX not in terminating state."); /* Things are really bad if this happens. Just leak * the CTX because it may still be linked to the * queue or the OUT urb may still be active. * Just leaking at least prevents an Oops or Panic. */ break; } } /** * ezusb_req_queue_run: * Description: * Note: Only one active CTX at any one time, because there's no * other (reliable) way to match the response URB to the correct * CTX. **/ static void ezusb_req_queue_run(struct ezusb_priv *upriv) { unsigned long flags; struct request_context *ctx; int result; spin_lock_irqsave(&upriv->req_lock, flags); if (!list_empty(&upriv->req_active)) goto unlock; if (list_empty(&upriv->req_pending)) goto unlock; ctx = list_entry(upriv->req_pending.next, struct request_context, list); if (!ctx->upriv->udev) goto unlock; /* We need to split this off to avoid a race condition */ list_move_tail(&ctx->list, &upriv->req_active); if (ctx->state == EZUSB_CTX_QUEUED) { atomic_inc(&ctx->refcount); result = usb_submit_urb(ctx->outurb, GFP_ATOMIC); if (result) { ctx->state = EZUSB_CTX_REQSUBMIT_FAIL; spin_unlock_irqrestore(&upriv->req_lock, flags); err("Fatal, failed to submit command urb." " error=%d\n", result); ezusb_ctx_complete(ctx); ezusb_request_context_put(ctx); goto done; } ctx->state = EZUSB_CTX_REQ_SUBMITTED; ezusb_mod_timer(ctx->upriv, &ctx->timer, jiffies + DEF_TIMEOUT); } unlock: spin_unlock_irqrestore(&upriv->req_lock, flags); done: return; } static void ezusb_req_enqueue_run(struct ezusb_priv *upriv, struct request_context *ctx) { unsigned long flags; spin_lock_irqsave(&upriv->req_lock, flags); if (!ctx->upriv->udev) { spin_unlock_irqrestore(&upriv->req_lock, flags); goto done; } atomic_inc(&ctx->refcount); list_add_tail(&ctx->list, &upriv->req_pending); spin_unlock_irqrestore(&upriv->req_lock, flags); ctx->state = EZUSB_CTX_QUEUED; ezusb_req_queue_run(upriv); done: return; } static void ezusb_request_out_callback(struct urb *urb) { unsigned long flags; enum ezusb_state state; struct request_context *ctx = urb->context; struct ezusb_priv *upriv = ctx->upriv; spin_lock_irqsave(&upriv->req_lock, flags); del_timer(&ctx->timer); if (ctx->killed) { spin_unlock_irqrestore(&upriv->req_lock, flags); pr_warning("interrupt called with dead ctx"); goto out; } state = ctx->state; if (urb->status == 0) { switch (state) { case EZUSB_CTX_REQ_SUBMITTED: if (ctx->in_rid) { ctx->state = EZUSB_CTX_REQ_COMPLETE; /* reply URB still pending */ ezusb_mod_timer(upriv, &ctx->timer, jiffies + DEF_TIMEOUT); spin_unlock_irqrestore(&upriv->req_lock, flags); break; } /* fall through */ case EZUSB_CTX_RESP_RECEIVED: /* IN already received before this OUT-ACK */ ctx->state = EZUSB_CTX_COMPLETE; spin_unlock_irqrestore(&upriv->req_lock, flags); ezusb_ctx_complete(ctx); break; default: spin_unlock_irqrestore(&upriv->req_lock, flags); err("Unexpected state(0x%x, %d) in OUT URB", state, urb->status); break; } } else { /* If someone cancels the OUT URB then its status * should be either -ECONNRESET or -ENOENT. */ switch (state) { case EZUSB_CTX_REQ_SUBMITTED: case EZUSB_CTX_RESP_RECEIVED: ctx->state = EZUSB_CTX_REQ_FAILED; /* fall through */ case EZUSB_CTX_REQ_FAILED: case EZUSB_CTX_REQ_TIMEOUT: spin_unlock_irqrestore(&upriv->req_lock, flags); ezusb_ctx_complete(ctx); break; default: spin_unlock_irqrestore(&upriv->req_lock, flags); err("Unexpected state(0x%x, %d) in OUT URB", state, urb->status); break; } } out: ezusb_request_context_put(ctx); } static void ezusb_request_in_callback(struct ezusb_priv *upriv, struct urb *urb) { struct ezusb_packet *ans = urb->transfer_buffer; struct request_context *ctx = NULL; enum ezusb_state state; unsigned long flags; /* Find the CTX on the active queue that requested this URB */ spin_lock_irqsave(&upriv->req_lock, flags); if (upriv->udev) { struct list_head *item; list_for_each(item, &upriv->req_active) { struct request_context *c; int reply_count; c = list_entry(item, struct request_context, list); reply_count = ezusb_reply_inc(c->buf->req_reply_count); if ((ans->ans_reply_count == reply_count) && (le16_to_cpu(ans->hermes_rid) == c->in_rid)) { ctx = c; break; } dbg("Skipped (0x%x/0x%x) (%d/%d)", le16_to_cpu(ans->hermes_rid), c->in_rid, ans->ans_reply_count, reply_count); } } if (ctx == NULL) { spin_unlock_irqrestore(&upriv->req_lock, flags); err("%s: got unexpected RID: 0x%04X", __func__, le16_to_cpu(ans->hermes_rid)); ezusb_req_queue_run(upriv); return; } /* The data we want is in the in buffer, exchange */ urb->transfer_buffer = ctx->buf; ctx->buf = (void *) ans; ctx->buf_length = urb->actual_length; state = ctx->state; switch (state) { case EZUSB_CTX_REQ_SUBMITTED: /* We have received our response URB before * our request has been acknowledged. Do NOT * destroy our CTX yet, because our OUT URB * is still alive ... */ ctx->state = EZUSB_CTX_RESP_RECEIVED; spin_unlock_irqrestore(&upriv->req_lock, flags); /* Let the machine continue running. */ break; case EZUSB_CTX_REQ_COMPLETE: /* This is the usual path: our request * has already been acknowledged, and * we have now received the reply. */ ctx->state = EZUSB_CTX_COMPLETE; /* Stop the intimer */ del_timer(&ctx->timer); spin_unlock_irqrestore(&upriv->req_lock, flags); /* Call the completion handler */ ezusb_ctx_complete(ctx); break; default: spin_unlock_irqrestore(&upriv->req_lock, flags); pr_warning("Matched IN URB, unexpected context state(0x%x)", state); /* Throw this CTX away and try submitting another */ del_timer(&ctx->timer); ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; usb_unlink_urb(ctx->outurb); ezusb_req_queue_run(upriv); break; } /* switch */ } static void ezusb_req_ctx_wait(struct ezusb_priv *upriv, struct request_context *ctx) { switch (ctx->state) { case EZUSB_CTX_QUEUED: case EZUSB_CTX_REQ_SUBMITTED: case EZUSB_CTX_REQ_COMPLETE: case EZUSB_CTX_RESP_RECEIVED: if (in_softirq()) { /* If we get called from a timer, timeout timers don't * get the chance to run themselves. So we make sure * that we don't sleep for ever */ int msecs = DEF_TIMEOUT * (1000 / HZ); while (!ctx->done.done && msecs--) udelay(1000); } else { wait_event_interruptible(ctx->done.wait, ctx->done.done); } break; default: /* Done or failed - nothing to wait for */ break; } } static inline u16 build_crc(struct ezusb_packet *data) { u16 crc = 0; u8 *bytes = (u8 *)data; int i; for (i = 0; i < 8; i++) crc = (crc << 1) + bytes[i]; return crc; } /** * ezusb_fill_req: * * if data == NULL and length > 0 the data is assumed to be already in * the target buffer and only the header is filled. * */ static int ezusb_fill_req(struct ezusb_packet *req, u16 length, u16 rid, const void *data, u16 frame_type, u8 reply_count) { int total_size = sizeof(*req) + length; BUG_ON(total_size > BULK_BUF_SIZE); req->magic = cpu_to_le16(EZUSB_MAGIC); req->req_reply_count = reply_count; req->ans_reply_count = 0; req->frame_type = cpu_to_le16(frame_type); req->size = cpu_to_le16(length + 4); req->crc = cpu_to_le16(build_crc(req)); req->hermes_len = cpu_to_le16(HERMES_BYTES_TO_RECLEN(length)); req->hermes_rid = cpu_to_le16(rid); if (data) memcpy(req->data, data, length); return total_size; } static int ezusb_submit_in_urb(struct ezusb_priv *upriv) { int retval = 0; void *cur_buf = upriv->read_urb->transfer_buffer; if (upriv->read_urb->status == -EINPROGRESS) { dbg("urb busy, not resubmiting"); retval = -EBUSY; goto exit; } usb_fill_bulk_urb(upriv->read_urb, upriv->udev, upriv->read_pipe, cur_buf, BULK_BUF_SIZE, ezusb_bulk_in_callback, upriv); upriv->read_urb->transfer_flags = 0; retval = usb_submit_urb(upriv->read_urb, GFP_ATOMIC); if (retval) err("%s submit failed %d", __func__, retval); exit: return retval; } static inline int ezusb_8051_cpucs(struct ezusb_priv *upriv, int reset) { u8 res_val = reset; /* avoid argument promotion */ if (!upriv->udev) { err("%s: !upriv->udev", __func__); return -EFAULT; } return usb_control_msg(upriv->udev, usb_sndctrlpipe(upriv->udev, 0), EZUSB_REQUEST_FW_TRANS, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, EZUSB_CPUCS_REG, 0, &res_val, sizeof(res_val), DEF_TIMEOUT); } static int ezusb_firmware_download(struct ezusb_priv *upriv, struct ez_usb_fw *fw) { u8 fw_buffer[FW_BUF_SIZE]; int retval, addr; int variant_offset; /* * This byte is 1 and should be replaced with 0. The offset is * 0x10AD in version 0.0.6. The byte in question should follow * the end of the code pointed to by the jump in the beginning * of the firmware. Also, it is read by code located at 0x358. */ variant_offset = be16_to_cpup((__be16 *) &fw->code[FW_VAR_OFFSET_PTR]); if (variant_offset >= fw->size) { printk(KERN_ERR PFX "Invalid firmware variant offset: " "0x%04x\n", variant_offset); retval = -EINVAL; goto fail; } retval = ezusb_8051_cpucs(upriv, 1); if (retval < 0) goto fail; for (addr = 0; addr < fw->size; addr += FW_BUF_SIZE) { /* 0x100-0x300 should be left alone, it contains card * specific data, like USB enumeration information */ if ((addr >= FW_HOLE_START) && (addr < FW_HOLE_END)) continue; memcpy(fw_buffer, &fw->code[addr], FW_BUF_SIZE); if (variant_offset >= addr && variant_offset < addr + FW_BUF_SIZE) { dbg("Patching card_variant byte at 0x%04X", variant_offset); fw_buffer[variant_offset - addr] = FW_VAR_VALUE; } retval = usb_control_msg(upriv->udev, usb_sndctrlpipe(upriv->udev, 0), EZUSB_REQUEST_FW_TRANS, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, addr, 0x0, fw_buffer, FW_BUF_SIZE, DEF_TIMEOUT); if (retval < 0) goto fail; } retval = ezusb_8051_cpucs(upriv, 0); if (retval < 0) goto fail; goto exit; fail: printk(KERN_ERR PFX "Firmware download failed, error %d\n", retval); exit: return retval; } static int ezusb_access_ltv(struct ezusb_priv *upriv, struct request_context *ctx, u16 length, const void *data, u16 frame_type, void *ans_buff, int ans_size, u16 *ans_length) { int req_size; int retval = 0; enum ezusb_state state; BUG_ON(in_irq()); if (!upriv->udev) { dbg("Device disconnected"); return -ENODEV; } if (upriv->read_urb->status != -EINPROGRESS) err("%s: in urb not pending", __func__); /* protect upriv->reply_count, guarantee sequential numbers */ spin_lock_bh(&upriv->reply_count_lock); req_size = ezusb_fill_req(ctx->buf, length, ctx->out_rid, data, frame_type, upriv->reply_count); usb_fill_bulk_urb(ctx->outurb, upriv->udev, upriv->write_pipe, ctx->buf, req_size, ezusb_request_out_callback, ctx); if (ctx->in_rid) upriv->reply_count = ezusb_reply_inc(upriv->reply_count); ezusb_req_enqueue_run(upriv, ctx); spin_unlock_bh(&upriv->reply_count_lock); if (ctx->in_rid) ezusb_req_ctx_wait(upriv, ctx); state = ctx->state; switch (state) { case EZUSB_CTX_COMPLETE: retval = ctx->outurb->status; break; case EZUSB_CTX_QUEUED: case EZUSB_CTX_REQ_SUBMITTED: if (!ctx->in_rid) break; default: err("%s: Unexpected context state %d", __func__, state); /* fall though */ case EZUSB_CTX_REQ_TIMEOUT: case EZUSB_CTX_REQ_FAILED: case EZUSB_CTX_RESP_TIMEOUT: case EZUSB_CTX_REQSUBMIT_FAIL: printk(KERN_ERR PFX "Access failed, resetting (state %d," " reply_count %d)\n", state, upriv->reply_count); upriv->reply_count = 0; if (state == EZUSB_CTX_REQ_TIMEOUT || state == EZUSB_CTX_RESP_TIMEOUT) { printk(KERN_ERR PFX "ctx timed out\n"); retval = -ETIMEDOUT; } else { printk(KERN_ERR PFX "ctx failed\n"); retval = -EFAULT; } goto exit; break; } if (ctx->in_rid) { struct ezusb_packet *ans = ctx->buf; int exp_len; if (ans->hermes_len != 0) exp_len = le16_to_cpu(ans->hermes_len) * 2 + 12; else exp_len = 14; if (exp_len != ctx->buf_length) { err("%s: length mismatch for RID 0x%04x: " "expected %d, got %d", __func__, ctx->in_rid, exp_len, ctx->buf_length); retval = -EIO; goto exit; } if (ans_buff) memcpy(ans_buff, ans->data, min_t(int, exp_len, ans_size)); if (ans_length) *ans_length = le16_to_cpu(ans->hermes_len); } exit: ezusb_request_context_put(ctx); return retval; } static int ezusb_write_ltv(struct hermes *hw, int bap, u16 rid, u16 length, const void *data) { struct ezusb_priv *upriv = hw->priv; u16 frame_type; struct request_context *ctx; if (length == 0) return -EINVAL; length = HERMES_RECLEN_TO_BYTES(length); /* On memory mapped devices HERMES_RID_CNFGROUPADDRESSES can be * set to be empty, but the USB bridge doesn't like it */ if (length == 0) return 0; ctx = ezusb_alloc_ctx(upriv, rid, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; if (rid == EZUSB_RID_TX) frame_type = EZUSB_FRAME_DATA; else frame_type = EZUSB_FRAME_CONTROL; return ezusb_access_ltv(upriv, ctx, length, data, frame_type, NULL, 0, NULL); } static int ezusb_read_ltv(struct hermes *hw, int bap, u16 rid, unsigned bufsize, u16 *length, void *buf) { struct ezusb_priv *upriv = hw->priv; struct request_context *ctx; if ((bufsize < 0) || (bufsize % 2)) return -EINVAL; ctx = ezusb_alloc_ctx(upriv, rid, rid); if (!ctx) return -ENOMEM; return ezusb_access_ltv(upriv, ctx, 0, NULL, EZUSB_FRAME_CONTROL, buf, bufsize, length); } static int ezusb_doicmd_wait(struct hermes *hw, u16 cmd, u16 parm0, u16 parm1, u16 parm2, struct hermes_response *resp) { struct ezusb_priv *upriv = hw->priv; struct request_context *ctx; __le16 data[4] = { cpu_to_le16(cmd), cpu_to_le16(parm0), cpu_to_le16(parm1), cpu_to_le16(parm2), }; dbg("0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X", cmd, parm0, parm1, parm2); ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, EZUSB_FRAME_CONTROL, NULL, 0, NULL); } static int ezusb_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0, struct hermes_response *resp) { struct ezusb_priv *upriv = hw->priv; struct request_context *ctx; __le16 data[4] = { cpu_to_le16(cmd), cpu_to_le16(parm0), 0, 0, }; dbg("0x%04X, parm0 0x%04X", cmd, parm0); ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, EZUSB_FRAME_CONTROL, NULL, 0, NULL); } static int ezusb_bap_pread(struct hermes *hw, int bap, void *buf, int len, u16 id, u16 offset) { struct ezusb_priv *upriv = hw->priv; struct ezusb_packet *ans = (void *) upriv->read_urb->transfer_buffer; int actual_length = upriv->read_urb->actual_length; if (id == EZUSB_RID_RX) { if ((sizeof(*ans) + offset + len) > actual_length) { printk(KERN_ERR PFX "BAP read beyond buffer end " "in rx frame\n"); return -EINVAL; } memcpy(buf, ans->data + offset, len); return 0; } if (EZUSB_IS_INFO(id)) { /* Include 4 bytes for length/type */ if ((sizeof(*ans) + offset + len - 4) > actual_length) { printk(KERN_ERR PFX "BAP read beyond buffer end " "in info frame\n"); return -EFAULT; } memcpy(buf, ans->data + offset - 4, len); } else { printk(KERN_ERR PFX "Unexpected fid 0x%04x\n", id); return -EINVAL; } return 0; } static int ezusb_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr, u16 pda_len) { struct ezusb_priv *upriv = hw->priv; struct request_context *ctx; __le16 data[] = { cpu_to_le16(pda_addr & 0xffff), cpu_to_le16(pda_len - 4) }; ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA); if (!ctx) return -ENOMEM; /* wl_lkm does not include PDA size in the PDA area. * We will pad the information into pda, so other routines * don't have to be modified */ pda[0] = cpu_to_le16(pda_len - 2); /* Includes CFG_PROD_DATA but not itself */ pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4, NULL); } static int ezusb_program_init(struct hermes *hw, u32 entry_point) { struct ezusb_priv *upriv = hw->priv; struct request_context *ctx; __le32 data = cpu_to_le32(entry_point); ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, EZUSB_FRAME_CONTROL, NULL, 0, NULL); } static int ezusb_program_end(struct hermes *hw) { struct ezusb_priv *upriv = hw->priv; struct request_context *ctx; ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; return ezusb_access_ltv(upriv, ctx, 0, NULL, EZUSB_FRAME_CONTROL, NULL, 0, NULL); } static int ezusb_program_bytes(struct hermes *hw, const char *buf, u32 addr, u32 len) { struct ezusb_priv *upriv = hw->priv; struct request_context *ctx; __le32 data = cpu_to_le32(addr); int err; ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data, EZUSB_FRAME_CONTROL, NULL, 0, NULL); if (err) return err; ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; return ezusb_access_ltv(upriv, ctx, len, buf, EZUSB_FRAME_CONTROL, NULL, 0, NULL); } static int ezusb_program(struct hermes *hw, const char *buf, u32 addr, u32 len) { u32 ch_addr; u32 ch_len; int err = 0; /* We can only send 2048 bytes out of the bulk xmit at a time, * so we have to split any programming into chunks of <2048 * bytes. */ ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE; ch_addr = addr; while (ch_addr < (addr + len)) { pr_debug("Programming subblock of length %d " "to address 0x%08x. Data @ %p\n", ch_len, ch_addr, &buf[ch_addr - addr]); err = ezusb_program_bytes(hw, &buf[ch_addr - addr], ch_addr, ch_len); if (err) break; ch_addr += ch_len; ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ? (addr + len - ch_addr) : MAX_DL_SIZE; } return err; } static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; struct ezusb_priv *upriv = priv->card; u8 mic[MICHAEL_MIC_LEN + 1]; int err = 0; int tx_control; unsigned long flags; struct request_context *ctx; u8 *buf; int tx_size; if (!netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", dev->name); return NETDEV_TX_BUSY; } if (netif_queue_stopped(dev)) { printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", dev->name); return NETDEV_TX_BUSY; } if (orinoco_lock(priv, &flags) != 0) { printk(KERN_ERR "%s: ezusb_xmit() called while hw_unavailable\n", dev->name); return NETDEV_TX_BUSY; } if (!netif_carrier_ok(dev) || (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { /* Oops, the firmware hasn't established a connection, silently drop the packet (this seems to be the safest approach). */ goto drop; } /* Check packet length */ if (skb->len < ETH_HLEN) goto drop; ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0); if (!ctx) goto busy; memset(ctx->buf, 0, BULK_BUF_SIZE); buf = ctx->buf->data; tx_control = 0; err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, &mic[0]); if (err) goto drop; { __le16 *tx_cntl = (__le16 *)buf; *tx_cntl = cpu_to_le16(tx_control); buf += sizeof(*tx_cntl); } memcpy(buf, skb->data, skb->len); buf += skb->len; if (tx_control & HERMES_TXCTRL_MIC) { u8 *m = mic; /* Mic has been offset so it can be copied to an even * address. We're copying eveything anyway, so we * don't need to copy that first byte. */ if (skb->len % 2) m++; memcpy(buf, m, MICHAEL_MIC_LEN); buf += MICHAEL_MIC_LEN; } /* Finally, we actually initiate the send */ netif_stop_queue(dev); /* The card may behave better if we send evenly sized usb transfers */ tx_size = ALIGN(buf - ctx->buf->data, 2); err = ezusb_access_ltv(upriv, ctx, tx_size, NULL, EZUSB_FRAME_DATA, NULL, 0, NULL); if (err) { netif_start_queue(dev); if (net_ratelimit()) printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err); goto busy; } dev->trans_start = jiffies; stats->tx_bytes += skb->len; goto ok; drop: stats->tx_errors++; stats->tx_dropped++; ok: orinoco_unlock(priv, &flags); dev_kfree_skb(skb); return NETDEV_TX_OK; busy: orinoco_unlock(priv, &flags); return NETDEV_TX_BUSY; } static int ezusb_allocate(struct hermes *hw, u16 size, u16 *fid) { *fid = EZUSB_RID_TX; return 0; } static int ezusb_hard_reset(struct orinoco_private *priv) { struct ezusb_priv *upriv = priv->card; int retval = ezusb_8051_cpucs(upriv, 1); if (retval < 0) { err("Failed to reset"); return retval; } retval = ezusb_8051_cpucs(upriv, 0); if (retval < 0) { err("Failed to unreset"); return retval; } dbg("sending control message"); retval = usb_control_msg(upriv->udev, usb_sndctrlpipe(upriv->udev, 0), EZUSB_REQUEST_TRIGER, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 0x0, 0x0, NULL, 0, DEF_TIMEOUT); if (retval < 0) { err("EZUSB_REQUEST_TRIGER failed retval %d", retval); return retval; } #if 0 dbg("Sending EZUSB_REQUEST_TRIG_AC"); retval = usb_control_msg(upriv->udev, usb_sndctrlpipe(upriv->udev, 0), EZUSB_REQUEST_TRIG_AC, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 0x00FA, 0x0, NULL, 0, DEF_TIMEOUT); if (retval < 0) { err("EZUSB_REQUEST_TRIG_AC failed retval %d", retval); return retval; } #endif return 0; } static int ezusb_init(struct hermes *hw) { struct ezusb_priv *upriv = hw->priv; int retval; BUG_ON(in_interrupt()); BUG_ON(!upriv); upriv->reply_count = 0; /* Write the MAGIC number on the simulated registers to keep * orinoco.c happy */ hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); hermes_write_regn(hw, RXFID, EZUSB_RID_RX); usb_kill_urb(upriv->read_urb); ezusb_submit_in_urb(upriv); retval = ezusb_write_ltv(hw, 0, EZUSB_RID_INIT1, HERMES_BYTES_TO_RECLEN(2), "\x10\x00"); if (retval < 0) { printk(KERN_ERR PFX "EZUSB_RID_INIT1 error %d\n", retval); return retval; } retval = ezusb_docmd_wait(hw, HERMES_CMD_INIT, 0, NULL); if (retval < 0) { printk(KERN_ERR PFX "HERMES_CMD_INIT error %d\n", retval); return retval; } return 0; } static void ezusb_bulk_in_callback(struct urb *urb) { struct ezusb_priv *upriv = (struct ezusb_priv *) urb->context; struct ezusb_packet *ans = urb->transfer_buffer; u16 crc; u16 hermes_rid; if (upriv->udev == NULL) { dbg("disconnected"); return; } if (urb->status == -ETIMEDOUT) { /* When a device gets unplugged we get this every time * we resubmit, flooding the logs. Since we don't use * USB timeouts, it shouldn't happen any other time*/ pr_warning("%s: urb timed out, not resubmiting", __func__); return; } if (urb->status == -ECONNABORTED) { pr_warning("%s: connection abort, resubmiting urb", __func__); goto resubmit; } if ((urb->status == -EILSEQ) || (urb->status == -ENOENT) || (urb->status == -ECONNRESET)) { dbg("status %d, not resubmiting", urb->status); return; } if (urb->status) dbg("status: %d length: %d", urb->status, urb->actual_length); if (urb->actual_length < sizeof(*ans)) { err("%s: short read, ignoring", __func__); goto resubmit; } crc = build_crc(ans); if (le16_to_cpu(ans->crc) != crc) { err("CRC error, ignoring packet"); goto resubmit; } hermes_rid = le16_to_cpu(ans->hermes_rid); if ((hermes_rid != EZUSB_RID_RX) && !EZUSB_IS_INFO(hermes_rid)) { ezusb_request_in_callback(upriv, urb); } else if (upriv->dev) { struct net_device *dev = upriv->dev; struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; if (hermes_rid == EZUSB_RID_RX) { __orinoco_ev_rx(dev, hw); } else { hermes_write_regn(hw, INFOFID, le16_to_cpu(ans->hermes_rid)); __orinoco_ev_info(dev, hw); } } resubmit: if (upriv->udev) ezusb_submit_in_urb(upriv); } static inline void ezusb_delete(struct ezusb_priv *upriv) { struct net_device *dev; struct list_head *item; struct list_head *tmp_item; unsigned long flags; BUG_ON(in_interrupt()); BUG_ON(!upriv); dev = upriv->dev; mutex_lock(&upriv->mtx); upriv->udev = NULL; /* No timer will be rearmed from here */ usb_kill_urb(upriv->read_urb); spin_lock_irqsave(&upriv->req_lock, flags); list_for_each_safe(item, tmp_item, &upriv->req_active) { struct request_context *ctx; int err; ctx = list_entry(item, struct request_context, list); atomic_inc(&ctx->refcount); ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; err = usb_unlink_urb(ctx->outurb); spin_unlock_irqrestore(&upriv->req_lock, flags); if (err == -EINPROGRESS) wait_for_completion(&ctx->done); del_timer_sync(&ctx->timer); /* FIXME: there is an slight chance for the irq handler to * be running */ if (!list_empty(&ctx->list)) ezusb_ctx_complete(ctx); ezusb_request_context_put(ctx); spin_lock_irqsave(&upriv->req_lock, flags); } spin_unlock_irqrestore(&upriv->req_lock, flags); list_for_each_safe(item, tmp_item, &upriv->req_pending) ezusb_ctx_complete(list_entry(item, struct request_context, list)); if (upriv->read_urb && upriv->read_urb->status == -EINPROGRESS) printk(KERN_ERR PFX "Some URB in progress\n"); mutex_unlock(&upriv->mtx); if (upriv->read_urb) { kfree(upriv->read_urb->transfer_buffer); usb_free_urb(upriv->read_urb); } kfree(upriv->bap_buf); if (upriv->dev) { struct orinoco_private *priv = ndev_priv(upriv->dev); orinoco_if_del(priv); free_orinocodev(priv); } } static void ezusb_lock_irqsave(spinlock_t *lock, unsigned long *flags) __acquires(lock) { spin_lock_bh(lock); } static void ezusb_unlock_irqrestore(spinlock_t *lock, unsigned long *flags) __releases(lock) { spin_unlock_bh(lock); } static void ezusb_lock_irq(spinlock_t *lock) __acquires(lock) { spin_lock_bh(lock); } static void ezusb_unlock_irq(spinlock_t *lock) __releases(lock) { spin_unlock_bh(lock); } static const struct hermes_ops ezusb_ops = { .init = ezusb_init, .cmd_wait = ezusb_docmd_wait, .init_cmd_wait = ezusb_doicmd_wait, .allocate = ezusb_allocate, .read_ltv = ezusb_read_ltv, .write_ltv = ezusb_write_ltv, .bap_pread = ezusb_bap_pread, .read_pda_h = ezusb_read_pda, .program_init = ezusb_program_init, .program_end = ezusb_program_end, .program = ezusb_program, .lock_irqsave = ezusb_lock_irqsave, .unlock_irqrestore = ezusb_unlock_irqrestore, .lock_irq = ezusb_lock_irq, .unlock_irq = ezusb_unlock_irq, }; static const struct net_device_ops ezusb_netdev_ops = { .ndo_open = orinoco_open, .ndo_stop = orinoco_stop, .ndo_start_xmit = ezusb_xmit, .ndo_set_rx_mode = orinoco_set_multicast_list, .ndo_change_mtu = orinoco_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = orinoco_tx_timeout, .ndo_get_stats = orinoco_get_stats, }; static int ezusb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); struct orinoco_private *priv; struct hermes *hw; struct ezusb_priv *upriv = NULL; struct usb_interface_descriptor *iface_desc; struct usb_endpoint_descriptor *ep; const struct firmware *fw_entry; int retval = 0; int i; priv = alloc_orinocodev(sizeof(*upriv), &udev->dev, ezusb_hard_reset, NULL); if (!priv) { err("Couldn't allocate orinocodev"); goto exit; } hw = &priv->hw; upriv = priv->card; mutex_init(&upriv->mtx); spin_lock_init(&upriv->reply_count_lock); spin_lock_init(&upriv->req_lock); INIT_LIST_HEAD(&upriv->req_pending); INIT_LIST_HEAD(&upriv->req_active); upriv->udev = udev; hw->iobase = (void __force __iomem *) &upriv->hermes_reg_fake; hw->reg_spacing = HERMES_16BIT_REGSPACING; hw->priv = upriv; hw->ops = &ezusb_ops; /* set up the endpoint information */ /* check out the endpoints */ iface_desc = &interface->altsetting[0].desc; for (i = 0; i < iface_desc->bNumEndpoints; ++i) { ep = &interface->altsetting[0].endpoint[i].desc; if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk in endpoint */ if (upriv->read_urb != NULL) { pr_warning("Found a second bulk in ep, ignored"); continue; } upriv->read_urb = usb_alloc_urb(0, GFP_KERNEL); if (!upriv->read_urb) { err("No free urbs available"); goto error; } if (le16_to_cpu(ep->wMaxPacketSize) != 64) pr_warning("bulk in: wMaxPacketSize!= 64"); if (ep->bEndpointAddress != (2 | USB_DIR_IN)) pr_warning("bulk in: bEndpointAddress: %d", ep->bEndpointAddress); upriv->read_pipe = usb_rcvbulkpipe(udev, ep-> bEndpointAddress); upriv->read_urb->transfer_buffer = kmalloc(BULK_BUF_SIZE, GFP_KERNEL); if (!upriv->read_urb->transfer_buffer) { err("Couldn't allocate IN buffer"); goto error; } } if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk out endpoint */ if (upriv->bap_buf != NULL) { pr_warning("Found a second bulk out ep, ignored"); continue; } if (le16_to_cpu(ep->wMaxPacketSize) != 64) pr_warning("bulk out: wMaxPacketSize != 64"); if (ep->bEndpointAddress != 2) pr_warning("bulk out: bEndpointAddress: %d", ep->bEndpointAddress); upriv->write_pipe = usb_sndbulkpipe(udev, ep-> bEndpointAddress); upriv->bap_buf = kmalloc(BULK_BUF_SIZE, GFP_KERNEL); if (!upriv->bap_buf) { err("Couldn't allocate bulk_out_buffer"); goto error; } } } if (!upriv->bap_buf || !upriv->read_urb) { err("Didn't find the required bulk endpoints"); goto error; } if (request_firmware(&fw_entry, "orinoco_ezusb_fw", &interface->dev) == 0) { firmware.size = fw_entry->size; firmware.code = fw_entry->data; } if (firmware.size && firmware.code) { ezusb_firmware_download(upriv, &firmware); } else { err("No firmware to download"); goto error; } if (ezusb_hard_reset(priv) < 0) { err("Cannot reset the device"); goto error; } /* If the firmware is already downloaded orinoco.c will call * ezusb_init but if the firmware is not already there, that will make * the kernel very unstable, so we try initializing here and quit in * case of error */ if (ezusb_init(hw) < 0) { err("Couldn't initialize the device"); err("Firmware may not be downloaded or may be wrong."); goto error; } /* Initialise the main driver */ if (orinoco_init(priv) != 0) { err("orinoco_init() failed\n"); goto error; } if (orinoco_if_add(priv, 0, 0, &ezusb_netdev_ops) != 0) { upriv->dev = NULL; err("%s: orinoco_if_add() failed", __func__); goto error; } upriv->dev = priv->ndev; goto exit; error: ezusb_delete(upriv); if (upriv->dev) { /* upriv->dev was 0, so ezusb_delete() didn't free it */ free_orinocodev(priv); } upriv = NULL; retval = -EFAULT; exit: if (fw_entry) { firmware.code = NULL; firmware.size = 0; release_firmware(fw_entry); } usb_set_intfdata(interface, upriv); return retval; } static void ezusb_disconnect(struct usb_interface *intf) { struct ezusb_priv *upriv = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); ezusb_delete(upriv); printk(KERN_INFO PFX "Disconnected\n"); } /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver orinoco_driver = { .name = DRIVER_NAME, .probe = ezusb_probe, .disconnect = ezusb_disconnect, .id_table = ezusb_table, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(orinoco_driver); MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Driver for Orinoco wireless LAN cards using EZUSB bridge"); MODULE_LICENSE("Dual MPL/GPL"); compat-drivers-2012-09-18/drivers/net/wireless/orinoco/hermes.h0000644000175000017500000003664612026211315023604 0ustar mcgrofmcgrof/* hermes.h * * Driver core for the "Hermes" wireless MAC controller, as used in * the Lucent Orinoco and Cabletron RoamAbout cards. It should also * work on the hfa3841 and hfa3842 MAC controller chips used in the * Prism I & II chipsets. * * This is not a complete driver, just low-level access routines for * the MAC controller itself. * * Based on the prism2 driver from Absolute Value Systems' linux-wlan * project, the Linux wvlan_cs driver, Lucent's HCF-Light * (wvlan_hcf.c) library, and the NetBSD wireless driver. * * Copyright (C) 2000, David Gibson, Linuxcare Australia. * (C) Copyright David Gibson, IBM Corp. 2001-2003. * * Portions taken from hfa384x.h. * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. * * This file distributed under the GPL, version 2. */ #ifndef _HERMES_H #define _HERMES_H /* Notes on locking: * * As a module of low level hardware access routines, there is no * locking. Users of this module should ensure that they serialize * access to the hermes structure, and to the hardware */ #include #include /* * Limits and constants */ #define HERMES_ALLOC_LEN_MIN (4) #define HERMES_ALLOC_LEN_MAX (2400) #define HERMES_LTV_LEN_MAX (34) #define HERMES_BAP_DATALEN_MAX (4096) #define HERMES_BAP_OFFSET_MAX (4096) #define HERMES_PORTID_MAX (7) #define HERMES_NUMPORTS_MAX (HERMES_PORTID_MAX + 1) #define HERMES_PDR_LEN_MAX (260) /* in bytes, from EK */ #define HERMES_PDA_RECS_MAX (200) /* a guess */ #define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */ #define HERMES_SCANRESULT_MAX (35) #define HERMES_CHINFORESULT_MAX (8) #define HERMES_MAX_MULTICAST (16) #define HERMES_MAGIC (0x7d1f) /* * Hermes register offsets */ #define HERMES_CMD (0x00) #define HERMES_PARAM0 (0x02) #define HERMES_PARAM1 (0x04) #define HERMES_PARAM2 (0x06) #define HERMES_STATUS (0x08) #define HERMES_RESP0 (0x0A) #define HERMES_RESP1 (0x0C) #define HERMES_RESP2 (0x0E) #define HERMES_INFOFID (0x10) #define HERMES_RXFID (0x20) #define HERMES_ALLOCFID (0x22) #define HERMES_TXCOMPLFID (0x24) #define HERMES_SELECT0 (0x18) #define HERMES_OFFSET0 (0x1C) #define HERMES_DATA0 (0x36) #define HERMES_SELECT1 (0x1A) #define HERMES_OFFSET1 (0x1E) #define HERMES_DATA1 (0x38) #define HERMES_EVSTAT (0x30) #define HERMES_INTEN (0x32) #define HERMES_EVACK (0x34) #define HERMES_CONTROL (0x14) #define HERMES_SWSUPPORT0 (0x28) #define HERMES_SWSUPPORT1 (0x2A) #define HERMES_SWSUPPORT2 (0x2C) #define HERMES_AUXPAGE (0x3A) #define HERMES_AUXOFFSET (0x3C) #define HERMES_AUXDATA (0x3E) /* * CMD register bitmasks */ #define HERMES_CMD_BUSY (0x8000) #define HERMES_CMD_AINFO (0x7f00) #define HERMES_CMD_MACPORT (0x0700) #define HERMES_CMD_RECL (0x0100) #define HERMES_CMD_WRITE (0x0100) #define HERMES_CMD_PROGMODE (0x0300) #define HERMES_CMD_CMDCODE (0x003f) /* * STATUS register bitmasks */ #define HERMES_STATUS_RESULT (0x7f00) #define HERMES_STATUS_CMDCODE (0x003f) /* * OFFSET register bitmasks */ #define HERMES_OFFSET_BUSY (0x8000) #define HERMES_OFFSET_ERR (0x4000) #define HERMES_OFFSET_DATAOFF (0x0ffe) /* * Event register bitmasks (INTEN, EVSTAT, EVACK) */ #define HERMES_EV_TICK (0x8000) #define HERMES_EV_WTERR (0x4000) #define HERMES_EV_INFDROP (0x2000) #define HERMES_EV_INFO (0x0080) #define HERMES_EV_DTIM (0x0020) #define HERMES_EV_CMD (0x0010) #define HERMES_EV_ALLOC (0x0008) #define HERMES_EV_TXEXC (0x0004) #define HERMES_EV_TX (0x0002) #define HERMES_EV_RX (0x0001) /* * Command codes */ /*--- Controller Commands ----------------------------*/ #define HERMES_CMD_INIT (0x0000) #define HERMES_CMD_ENABLE (0x0001) #define HERMES_CMD_DISABLE (0x0002) #define HERMES_CMD_DIAG (0x0003) /*--- Buffer Mgmt Commands ---------------------------*/ #define HERMES_CMD_ALLOC (0x000A) #define HERMES_CMD_TX (0x000B) /*--- Regulate Commands ------------------------------*/ #define HERMES_CMD_NOTIFY (0x0010) #define HERMES_CMD_INQUIRE (0x0011) /*--- Configure Commands -----------------------------*/ #define HERMES_CMD_ACCESS (0x0021) #define HERMES_CMD_DOWNLD (0x0022) /*--- Serial I/O Commands ----------------------------*/ #define HERMES_CMD_READMIF (0x0030) #define HERMES_CMD_WRITEMIF (0x0031) /*--- Debugging Commands -----------------------------*/ #define HERMES_CMD_TEST (0x0038) /* Test command arguments */ #define HERMES_TEST_SET_CHANNEL 0x0800 #define HERMES_TEST_MONITOR 0x0b00 #define HERMES_TEST_STOP 0x0f00 /* Authentication algorithms */ #define HERMES_AUTH_OPEN 1 #define HERMES_AUTH_SHARED_KEY 2 /* WEP settings */ #define HERMES_WEP_PRIVACY_INVOKED 0x0001 #define HERMES_WEP_EXCL_UNENCRYPTED 0x0002 #define HERMES_WEP_HOST_ENCRYPT 0x0010 #define HERMES_WEP_HOST_DECRYPT 0x0080 /* Symbol hostscan options */ #define HERMES_HOSTSCAN_SYMBOL_5SEC 0x0001 #define HERMES_HOSTSCAN_SYMBOL_ONCE 0x0002 #define HERMES_HOSTSCAN_SYMBOL_PASSIVE 0x0040 #define HERMES_HOSTSCAN_SYMBOL_BCAST 0x0080 /* * Frame structures and constants */ #define HERMES_DESCRIPTOR_OFFSET 0 #define HERMES_802_11_OFFSET (14) #define HERMES_802_3_OFFSET (14 + 32) #define HERMES_802_2_OFFSET (14 + 32 + 14) #define HERMES_TXCNTL2_OFFSET (HERMES_802_3_OFFSET - 2) #define HERMES_RXSTAT_ERR (0x0003) #define HERMES_RXSTAT_BADCRC (0x0001) #define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) #define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */ #define HERMES_RXSTAT_MACPORT (0x0700) #define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */ #define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */ #define HERMES_RXSTAT_MSGTYPE (0xE000) #define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ #define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */ #define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */ /* Shift amount for key ID in RXSTAT and TXCTRL */ #define HERMES_MIC_KEY_ID_SHIFT 11 struct hermes_tx_descriptor { __le16 status; __le16 reserved1; __le16 reserved2; __le32 sw_support; u8 retry_count; u8 tx_rate; __le16 tx_control; } __packed; #define HERMES_TXSTAT_RETRYERR (0x0001) #define HERMES_TXSTAT_AGEDERR (0x0002) #define HERMES_TXSTAT_DISCON (0x0004) #define HERMES_TXSTAT_FORMERR (0x0008) #define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */ #define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */ #define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */ #define HERMES_TXCTRL_MIC (0x0010) /* 802.3 + TKIP */ #define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */ #define HERMES_TXCTRL_ALT_RTRY (0x0020) /* Inquiry constants and data types */ #define HERMES_INQ_TALLIES (0xF100) #define HERMES_INQ_SCAN (0xF101) #define HERMES_INQ_CHANNELINFO (0xF102) #define HERMES_INQ_HOSTSCAN (0xF103) #define HERMES_INQ_HOSTSCAN_SYMBOL (0xF104) #define HERMES_INQ_LINKSTATUS (0xF200) #define HERMES_INQ_SEC_STAT_AGERE (0xF202) struct hermes_tallies_frame { __le16 TxUnicastFrames; __le16 TxMulticastFrames; __le16 TxFragments; __le16 TxUnicastOctets; __le16 TxMulticastOctets; __le16 TxDeferredTransmissions; __le16 TxSingleRetryFrames; __le16 TxMultipleRetryFrames; __le16 TxRetryLimitExceeded; __le16 TxDiscards; __le16 RxUnicastFrames; __le16 RxMulticastFrames; __le16 RxFragments; __le16 RxUnicastOctets; __le16 RxMulticastOctets; __le16 RxFCSErrors; __le16 RxDiscards_NoBuffer; __le16 TxDiscardsWrongSA; __le16 RxWEPUndecryptable; __le16 RxMsgInMsgFragments; __le16 RxMsgInBadMsgFragments; /* Those last are probably not available in very old firmwares */ __le16 RxDiscards_WEPICVError; __le16 RxDiscards_WEPExcluded; } __packed; /* Grabbed from wlan-ng - Thanks Mark... - Jean II * This is the result of a scan inquiry command */ /* Structure describing info about an Access Point */ struct prism2_scan_apinfo { __le16 channel; /* Channel where the AP sits */ __le16 noise; /* Noise level */ __le16 level; /* Signal level */ u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ __le16 beacon_interv; /* Beacon interval */ __le16 capabilities; /* Capabilities */ __le16 essid_len; /* ESSID length */ u8 essid[32]; /* ESSID of the network */ u8 rates[10]; /* Bit rate supported */ __le16 proberesp_rate; /* Data rate of the response frame */ __le16 atim; /* ATIM window time, Kus (hostscan only) */ } __packed; /* Same stuff for the Lucent/Agere card. * Thanks to h1kari - Jean II */ struct agere_scan_apinfo { __le16 channel; /* Channel where the AP sits */ __le16 noise; /* Noise level */ __le16 level; /* Signal level */ u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ __le16 beacon_interv; /* Beacon interval */ __le16 capabilities; /* Capabilities */ /* bits: 0-ess, 1-ibss, 4-privacy [wep] */ __le16 essid_len; /* ESSID length */ u8 essid[32]; /* ESSID of the network */ } __packed; /* Moustafa: Scan structure for Symbol cards */ struct symbol_scan_apinfo { u8 channel; /* Channel where the AP sits */ u8 unknown1; /* 8 in 2.9x and 3.9x f/w, 0 otherwise */ __le16 noise; /* Noise level */ __le16 level; /* Signal level */ u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ __le16 beacon_interv; /* Beacon interval */ __le16 capabilities; /* Capabilities */ /* bits: 0-ess, 1-ibss, 4-privacy [wep] */ __le16 essid_len; /* ESSID length */ u8 essid[32]; /* ESSID of the network */ __le16 rates[5]; /* Bit rate supported */ __le16 basic_rates; /* Basic rates bitmask */ u8 unknown2[6]; /* Always FF:FF:FF:FF:00:00 */ u8 unknown3[8]; /* Always 0, appeared in f/w 3.91-68 */ } __packed; union hermes_scan_info { struct agere_scan_apinfo a; struct prism2_scan_apinfo p; struct symbol_scan_apinfo s; }; /* Extended scan struct for HERMES_INQ_CHANNELINFO. * wl_lkm calls this an ACS scan (Automatic Channel Select). * Keep out of union hermes_scan_info because it is much bigger than * the older scan structures. */ struct agere_ext_scan_info { __le16 reserved0; u8 noise; u8 level; u8 rx_flow; u8 rate; __le16 reserved1[2]; __le16 frame_control; __le16 dur_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 bssid[ETH_ALEN]; __le16 sequence; u8 addr4[ETH_ALEN]; __le16 data_length; /* Next 3 fields do not get filled in. */ u8 daddr[ETH_ALEN]; u8 saddr[ETH_ALEN]; __le16 len_type; __le64 timestamp; __le16 beacon_interval; __le16 capabilities; u8 data[0]; } __packed; #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) #define HERMES_LINKSTATUS_CONNECTED (0x0001) #define HERMES_LINKSTATUS_DISCONNECTED (0x0002) #define HERMES_LINKSTATUS_AP_CHANGE (0x0003) #define HERMES_LINKSTATUS_AP_OUT_OF_RANGE (0x0004) #define HERMES_LINKSTATUS_AP_IN_RANGE (0x0005) #define HERMES_LINKSTATUS_ASSOC_FAILED (0x0006) struct hermes_linkstatus { __le16 linkstatus; /* Link status */ } __packed; struct hermes_response { u16 status, resp0, resp1, resp2; }; /* "ID" structure - used for ESSID and station nickname */ struct hermes_idstring { __le16 len; __le16 val[16]; } __packed; struct hermes_multicast { u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN]; } __packed; /* Timeouts */ #define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */ struct hermes; /* Functions to access hardware */ struct hermes_ops { int (*init)(struct hermes *hw); int (*cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0, struct hermes_response *resp); int (*init_cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0, u16 parm1, u16 parm2, struct hermes_response *resp); int (*allocate)(struct hermes *hw, u16 size, u16 *fid); int (*read_ltv)(struct hermes *hw, int bap, u16 rid, unsigned buflen, u16 *length, void *buf); int (*write_ltv)(struct hermes *hw, int bap, u16 rid, u16 length, const void *value); int (*bap_pread)(struct hermes *hw, int bap, void *buf, int len, u16 id, u16 offset); int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf, int len, u16 id, u16 offset); int (*read_pda_h)(struct hermes *hw, __le16 *pda, u32 pda_addr, u16 pda_len); int (*program_init)(struct hermes *hw, u32 entry_point); int (*program_end)(struct hermes *hw); int (*program)(struct hermes *hw, const char *buf, u32 addr, u32 len); void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags); void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags); void (*lock_irq)(spinlock_t *lock); void (*unlock_irq)(spinlock_t *lock); }; /* Basic control structure */ struct hermes { void __iomem *iobase; int reg_spacing; #define HERMES_16BIT_REGSPACING 0 #define HERMES_32BIT_REGSPACING 1 u16 inten; /* Which interrupts should be enabled? */ bool eeprom_pda; const struct hermes_ops *ops; void *priv; }; /* Register access convenience macros */ #define hermes_read_reg(hw, off) \ (ioread16((hw)->iobase + ((off) << (hw)->reg_spacing))) #define hermes_write_reg(hw, off, val) \ (iowrite16((val), (hw)->iobase + ((off) << (hw)->reg_spacing))) #define hermes_read_regn(hw, name) hermes_read_reg((hw), HERMES_##name) #define hermes_write_regn(hw, name, val) \ hermes_write_reg((hw), HERMES_##name, (val)) /* Function prototypes */ void hermes_struct_init(struct hermes *hw, void __iomem *address, int reg_spacing); /* Inline functions */ static inline int hermes_present(struct hermes *hw) { return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC; } static inline void hermes_set_irqmask(struct hermes *hw, u16 events) { hw->inten = events; hermes_write_regn(hw, INTEN, events); } static inline int hermes_enable_port(struct hermes *hw, int port) { return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), 0, NULL); } static inline int hermes_disable_port(struct hermes *hw, int port) { return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8), 0, NULL); } /* Initiate an INQUIRE command (tallies or scan). The result will come as an * information frame in __orinoco_ev_info() */ static inline int hermes_inquire(struct hermes *hw, u16 rid) { return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL); } #define HERMES_BYTES_TO_RECLEN(n) ((((n) + 1) / 2) + 1) #define HERMES_RECLEN_TO_BYTES(n) (((n) - 1) * 2) /* Note that for the next two, the count is in 16-bit words, not bytes */ static inline void hermes_read_words(struct hermes *hw, int off, void *buf, unsigned count) { off = off << hw->reg_spacing; ioread16_rep(hw->iobase + off, buf, count); } static inline void hermes_write_bytes(struct hermes *hw, int off, const char *buf, unsigned count) { off = off << hw->reg_spacing; iowrite16_rep(hw->iobase + off, buf, count >> 1); if (unlikely(count & 1)) iowrite8(buf[count - 1], hw->iobase + off); } static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count) { unsigned i; off = off << hw->reg_spacing; for (i = 0; i < count; i++) iowrite16(0, hw->iobase + off); } #define HERMES_READ_RECORD(hw, bap, rid, buf) \ (hw->ops->read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf))) #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ (hw->ops->write_ltv((hw), (bap), (rid), \ HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf))) static inline int hermes_read_wordrec(struct hermes *hw, int bap, u16 rid, u16 *word) { __le16 rec; int err; err = HERMES_READ_RECORD(hw, bap, rid, &rec); *word = le16_to_cpu(rec); return err; } static inline int hermes_write_wordrec(struct hermes *hw, int bap, u16 rid, u16 word) { __le16 rec = cpu_to_le16(word); return HERMES_WRITE_RECORD(hw, bap, rid, &rec); } #endif /* _HERMES_H */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/hermes.c0000644000175000017500000004711012026211315023563 0ustar mcgrofmcgrof/* hermes.c * * Driver core for the "Hermes" wireless MAC controller, as used in * the Lucent Orinoco and Cabletron RoamAbout cards. It should also * work on the hfa3841 and hfa3842 MAC controller chips used in the * Prism II chipsets. * * This is not a complete driver, just low-level access routines for * the MAC controller itself. * * Based on the prism2 driver from Absolute Value Systems' linux-wlan * project, the Linux wvlan_cs driver, Lucent's HCF-Light * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no * particular order). * * Copyright (C) 2000, David Gibson, Linuxcare Australia. * (C) Copyright David Gibson, IBM Corp. 2001-2003. * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. */ #include #include #include #include #include "hermes.h" /* These are maximum timeouts. Most often, card wil react much faster */ #define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ #define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ #define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ /* * AUX port access. To unlock the AUX port write the access keys to the * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL * register. Then read it and make sure it's HERMES_AUX_ENABLED. */ #define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ #define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ #define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ #define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ #define HERMES_AUX_PW0 0xFE01 #define HERMES_AUX_PW1 0xDC23 #define HERMES_AUX_PW2 0xBA45 /* HERMES_CMD_DOWNLD */ #define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) #define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) #define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) #define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) /* * Debugging helpers */ #define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %p: " , hw->iobase); \ printk(stuff); } while (0) #undef HERMES_DEBUG #ifdef HERMES_DEBUG #include #define DEBUG(lvl, stuff...) if ((lvl) <= HERMES_DEBUG) DMSG(stuff) #else /* ! HERMES_DEBUG */ #define DEBUG(lvl, stuff...) do { } while (0) #endif /* ! HERMES_DEBUG */ static const struct hermes_ops hermes_ops_local; /* * Internal functions */ /* Issue a command to the chip. Waiting for it to complete is the caller's problem. Returns -EBUSY if the command register is busy, 0 on success. Callable from any context. */ static int hermes_issue_cmd(struct hermes *hw, u16 cmd, u16 param0, u16 param1, u16 param2) { int k = CMD_BUSY_TIMEOUT; u16 reg; /* First wait for the command register to unbusy */ reg = hermes_read_regn(hw, CMD); while ((reg & HERMES_CMD_BUSY) && k) { k--; udelay(1); reg = hermes_read_regn(hw, CMD); } if (reg & HERMES_CMD_BUSY) return -EBUSY; hermes_write_regn(hw, PARAM2, param2); hermes_write_regn(hw, PARAM1, param1); hermes_write_regn(hw, PARAM0, param0); hermes_write_regn(hw, CMD, cmd); return 0; } /* * Function definitions */ /* For doing cmds that wipe the magic constant in SWSUPPORT0 */ static int hermes_doicmd_wait(struct hermes *hw, u16 cmd, u16 parm0, u16 parm1, u16 parm2, struct hermes_response *resp) { int err = 0; int k; u16 status, reg; err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2); if (err) return err; reg = hermes_read_regn(hw, EVSTAT); k = CMD_INIT_TIMEOUT; while ((!(reg & HERMES_EV_CMD)) && k) { k--; udelay(10); reg = hermes_read_regn(hw, EVSTAT); } hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); if (!hermes_present(hw)) { DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n", hw->iobase); err = -ENODEV; goto out; } if (!(reg & HERMES_EV_CMD)) { printk(KERN_ERR "hermes @ %p: " "Timeout waiting for card to reset (reg=0x%04x)!\n", hw->iobase, reg); err = -ETIMEDOUT; goto out; } status = hermes_read_regn(hw, STATUS); if (resp) { resp->status = status; resp->resp0 = hermes_read_regn(hw, RESP0); resp->resp1 = hermes_read_regn(hw, RESP1); resp->resp2 = hermes_read_regn(hw, RESP2); } hermes_write_regn(hw, EVACK, HERMES_EV_CMD); if (status & HERMES_STATUS_RESULT) err = -EIO; out: return err; } void hermes_struct_init(struct hermes *hw, void __iomem *address, int reg_spacing) { hw->iobase = address; hw->reg_spacing = reg_spacing; hw->inten = 0x0; hw->eeprom_pda = false; hw->ops = &hermes_ops_local; } EXPORT_SYMBOL(hermes_struct_init); static int hermes_init(struct hermes *hw) { u16 reg; int err = 0; int k; /* We don't want to be interrupted while resetting the chipset */ hw->inten = 0x0; hermes_write_regn(hw, INTEN, 0); hermes_write_regn(hw, EVACK, 0xffff); /* Normally it's a "can't happen" for the command register to be busy when we go to issue a command because we are serializing all commands. However we want to have some chance of resetting the card even if it gets into a stupid state, so we actually wait to see if the command register will unbusy itself here. */ k = CMD_BUSY_TIMEOUT; reg = hermes_read_regn(hw, CMD); while (k && (reg & HERMES_CMD_BUSY)) { if (reg == 0xffff) /* Special case - the card has probably been removed, so don't wait for the timeout */ return -ENODEV; k--; udelay(1); reg = hermes_read_regn(hw, CMD); } /* No need to explicitly handle the timeout - if we've timed out hermes_issue_cmd() will probably return -EBUSY below */ /* According to the documentation, EVSTAT may contain obsolete event occurrence information. We have to acknowledge it by writing EVACK. */ reg = hermes_read_regn(hw, EVSTAT); hermes_write_regn(hw, EVACK, reg); /* We don't use hermes_docmd_wait here, because the reset wipes the magic constant in SWSUPPORT0 away, and it gets confused */ err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL); return err; } /* Issue a command to the chip, and (busy!) wait for it to * complete. * * Returns: * < 0 on internal error * 0 on success * > 0 on error returned by the firmware * * Callable from any context, but locking is your problem. */ static int hermes_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0, struct hermes_response *resp) { int err; int k; u16 reg; u16 status; err = hermes_issue_cmd(hw, cmd, parm0, 0, 0); if (err) { if (!hermes_present(hw)) { if (net_ratelimit()) printk(KERN_WARNING "hermes @ %p: " "Card removed while issuing command " "0x%04x.\n", hw->iobase, cmd); err = -ENODEV; } else if (net_ratelimit()) printk(KERN_ERR "hermes @ %p: " "Error %d issuing command 0x%04x.\n", hw->iobase, err, cmd); goto out; } reg = hermes_read_regn(hw, EVSTAT); k = CMD_COMPL_TIMEOUT; while ((!(reg & HERMES_EV_CMD)) && k) { k--; udelay(10); reg = hermes_read_regn(hw, EVSTAT); } if (!hermes_present(hw)) { printk(KERN_WARNING "hermes @ %p: Card removed " "while waiting for command 0x%04x completion.\n", hw->iobase, cmd); err = -ENODEV; goto out; } if (!(reg & HERMES_EV_CMD)) { printk(KERN_ERR "hermes @ %p: Timeout waiting for " "command 0x%04x completion.\n", hw->iobase, cmd); err = -ETIMEDOUT; goto out; } status = hermes_read_regn(hw, STATUS); if (resp) { resp->status = status; resp->resp0 = hermes_read_regn(hw, RESP0); resp->resp1 = hermes_read_regn(hw, RESP1); resp->resp2 = hermes_read_regn(hw, RESP2); } hermes_write_regn(hw, EVACK, HERMES_EV_CMD); if (status & HERMES_STATUS_RESULT) err = -EIO; out: return err; } static int hermes_allocate(struct hermes *hw, u16 size, u16 *fid) { int err = 0; int k; u16 reg; if ((size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX)) return -EINVAL; err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL); if (err) return err; reg = hermes_read_regn(hw, EVSTAT); k = ALLOC_COMPL_TIMEOUT; while ((!(reg & HERMES_EV_ALLOC)) && k) { k--; udelay(10); reg = hermes_read_regn(hw, EVSTAT); } if (!hermes_present(hw)) { printk(KERN_WARNING "hermes @ %p: " "Card removed waiting for frame allocation.\n", hw->iobase); return -ENODEV; } if (!(reg & HERMES_EV_ALLOC)) { printk(KERN_ERR "hermes @ %p: " "Timeout waiting for frame allocation\n", hw->iobase); return -ETIMEDOUT; } *fid = hermes_read_regn(hw, ALLOCFID); hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC); return 0; } /* Set up a BAP to read a particular chunk of data from card's internal buffer. * * Returns: * < 0 on internal failure (errno) * 0 on success * > 0 on error * from firmware * * Callable from any context */ static int hermes_bap_seek(struct hermes *hw, int bap, u16 id, u16 offset) { int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; int k; u16 reg; /* Paranoia.. */ if ((offset > HERMES_BAP_OFFSET_MAX) || (offset % 2)) return -EINVAL; k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ((reg & HERMES_OFFSET_BUSY) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg & HERMES_OFFSET_BUSY) return -ETIMEDOUT; /* Now we actually set up the transfer */ hermes_write_reg(hw, sreg, id); hermes_write_reg(hw, oreg, offset); /* Wait for the BAP to be ready */ k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ((reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg != offset) { printk(KERN_ERR "hermes @ %p: BAP%d offset %s: " "reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap, (reg & HERMES_OFFSET_BUSY) ? "timeout" : "error", reg, id, offset); if (reg & HERMES_OFFSET_BUSY) return -ETIMEDOUT; return -EIO; /* error or wrong offset */ } return 0; } /* Read a block of data from the chip's buffer, via the * BAP. Synchronization/serialization is the caller's problem. len * must be even. * * Returns: * < 0 on internal failure (errno) * 0 on success * > 0 on error from firmware */ static int hermes_bap_pread(struct hermes *hw, int bap, void *buf, int len, u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; if ((len < 0) || (len % 2)) return -EINVAL; err = hermes_bap_seek(hw, bap, id, offset); if (err) goto out; /* Actually do the transfer */ hermes_read_words(hw, dreg, buf, len / 2); out: return err; } /* Write a block of data to the chip's buffer, via the * BAP. Synchronization/serialization is the caller's problem. * * Returns: * < 0 on internal failure (errno) * 0 on success * > 0 on error from firmware */ static int hermes_bap_pwrite(struct hermes *hw, int bap, const void *buf, int len, u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; if (len < 0) return -EINVAL; err = hermes_bap_seek(hw, bap, id, offset); if (err) goto out; /* Actually do the transfer */ hermes_write_bytes(hw, dreg, buf, len); out: return err; } /* Read a Length-Type-Value record from the card. * * If length is NULL, we ignore the length read from the card, and * read the entire buffer regardless. This is useful because some of * the configuration records appear to have incorrect lengths in * practice. * * Callable from user or bh context. */ static int hermes_read_ltv(struct hermes *hw, int bap, u16 rid, unsigned bufsize, u16 *length, void *buf) { int err = 0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; u16 rlength, rtype; unsigned nwords; if (bufsize % 2) return -EINVAL; err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); if (err) return err; err = hermes_bap_seek(hw, bap, rid, 0); if (err) return err; rlength = hermes_read_reg(hw, dreg); if (!rlength) return -ENODATA; rtype = hermes_read_reg(hw, dreg); if (length) *length = rlength; if (rtype != rid) printk(KERN_WARNING "hermes @ %p: %s(): " "rid (0x%04x) does not match type (0x%04x)\n", hw->iobase, __func__, rid, rtype); if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) printk(KERN_WARNING "hermes @ %p: " "Truncating LTV record from %d to %d bytes. " "(rid=0x%04x, len=0x%04x)\n", hw->iobase, HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); nwords = min((unsigned)rlength - 1, bufsize / 2); hermes_read_words(hw, dreg, buf, nwords); return 0; } static int hermes_write_ltv(struct hermes *hw, int bap, u16 rid, u16 length, const void *value) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; unsigned count; if (length == 0) return -EINVAL; err = hermes_bap_seek(hw, bap, rid, 0); if (err) return err; hermes_write_reg(hw, dreg, length); hermes_write_reg(hw, dreg, rid); count = length - 1; hermes_write_bytes(hw, dreg, value, count << 1); err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, rid, NULL); return err; } /*** Hermes AUX control ***/ static inline void hermes_aux_setaddr(struct hermes *hw, u32 addr) { hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); } static inline int hermes_aux_control(struct hermes *hw, int enabled) { int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; int i; /* Already open? */ if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) return 0; hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); hermes_write_reg(hw, HERMES_CONTROL, action); for (i = 0; i < 20; i++) { udelay(10); if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) return 0; } return -EBUSY; } /*** Hermes programming ***/ /* About to start programming data (Hermes I) * offset is the entry point * * Spectrum_cs' Symbol fw does not require this * wl_lkm Agere fw does * Don't know about intersil */ static int hermesi_program_init(struct hermes *hw, u32 offset) { int err; /* Disable interrupts?*/ /*hw->inten = 0x0;*/ /*hermes_write_regn(hw, INTEN, 0);*/ /*hermes_set_irqmask(hw, 0);*/ /* Acknowledge any outstanding command */ hermes_write_regn(hw, EVACK, 0xFFFF); /* Using init_cmd_wait rather than cmd_wait */ err = hw->ops->init_cmd_wait(hw, 0x0100 | HERMES_CMD_INIT, 0, 0, 0, NULL); if (err) return err; err = hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT, 0, 0, 0, NULL); if (err) return err; err = hermes_aux_control(hw, 1); pr_debug("AUX enable returned %d\n", err); if (err) return err; pr_debug("Enabling volatile, EP 0x%08x\n", offset); err = hw->ops->init_cmd_wait(hw, HERMES_PROGRAM_ENABLE_VOLATILE, offset & 0xFFFFu, offset >> 16, 0, NULL); pr_debug("PROGRAM_ENABLE returned %d\n", err); return err; } /* Done programming data (Hermes I) * * Spectrum_cs' Symbol fw does not require this * wl_lkm Agere fw does * Don't know about intersil */ static int hermesi_program_end(struct hermes *hw) { struct hermes_response resp; int rc = 0; int err; rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); pr_debug("PROGRAM_DISABLE returned %d, " "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", rc, resp.resp0, resp.resp1, resp.resp2); if ((rc == 0) && ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) rc = -EIO; err = hermes_aux_control(hw, 0); pr_debug("AUX disable returned %d\n", err); /* Acknowledge any outstanding command */ hermes_write_regn(hw, EVACK, 0xFFFF); /* Reinitialise, ignoring return */ (void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT, 0, 0, 0, NULL); return rc ? rc : err; } static int hermes_program_bytes(struct hermes *hw, const char *data, u32 addr, u32 len) { /* wl lkm splits the programming into chunks of 2000 bytes. * This restriction appears to come from USB. The PCMCIA * adapters can program the whole lot in one go */ hermes_aux_setaddr(hw, addr); hermes_write_bytes(hw, HERMES_AUXDATA, data, len); return 0; } /* Read PDA from the adapter */ static int hermes_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr, u16 pda_len) { int ret; u16 pda_size; u16 data_len = pda_len; __le16 *data = pda; if (hw->eeprom_pda) { /* PDA of spectrum symbol is in eeprom */ /* Issue command to read EEPROM */ ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); if (ret) return ret; } else { /* wl_lkm does not include PDA size in the PDA area. * We will pad the information into pda, so other routines * don't have to be modified */ pda[0] = cpu_to_le16(pda_len - 2); /* Includes CFG_PROD_DATA but not itself */ pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ data_len = pda_len - 4; data = pda + 2; } /* Open auxiliary port */ ret = hermes_aux_control(hw, 1); pr_debug("AUX enable returned %d\n", ret); if (ret) return ret; /* Read PDA */ hermes_aux_setaddr(hw, pda_addr); hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); /* Close aux port */ ret = hermes_aux_control(hw, 0); pr_debug("AUX disable returned %d\n", ret); /* Check PDA length */ pda_size = le16_to_cpu(pda[0]); pr_debug("Actual PDA length %d, Max allowed %d\n", pda_size, pda_len); if (pda_size > pda_len) return -EINVAL; return 0; } static void hermes_lock_irqsave(spinlock_t *lock, unsigned long *flags) __acquires(lock) { spin_lock_irqsave(lock, *flags); } static void hermes_unlock_irqrestore(spinlock_t *lock, unsigned long *flags) __releases(lock) { spin_unlock_irqrestore(lock, *flags); } static void hermes_lock_irq(spinlock_t *lock) __acquires(lock) { spin_lock_irq(lock); } static void hermes_unlock_irq(spinlock_t *lock) __releases(lock) { spin_unlock_irq(lock); } /* Hermes operations for local buses */ static const struct hermes_ops hermes_ops_local = { .init = hermes_init, .cmd_wait = hermes_docmd_wait, .init_cmd_wait = hermes_doicmd_wait, .allocate = hermes_allocate, .read_ltv = hermes_read_ltv, .write_ltv = hermes_write_ltv, .bap_pread = hermes_bap_pread, .bap_pwrite = hermes_bap_pwrite, .read_pda_h = hermes_read_pda, .program_init = hermesi_program_init, .program_end = hermesi_program_end, .program = hermes_program_bytes, .lock_irqsave = hermes_lock_irqsave, .unlock_irqrestore = hermes_unlock_irqrestore, .lock_irq = hermes_lock_irq, .unlock_irq = hermes_unlock_irq, }; compat-drivers-2012-09-18/drivers/net/wireless/orinoco/fw.c0000644000175000017500000002321612026211315022715 0ustar mcgrofmcgrof/* Firmware file reading and download helpers * * See copyright notice in main.c */ #include #include #include #include #include #include "hermes.h" #include "hermes_dld.h" #include "orinoco.h" #include "fw.h" /* End markers (for Symbol firmware only) */ #define TEXT_END 0x1A /* End of text header */ struct fw_info { char *pri_fw; char *sta_fw; char *ap_fw; u32 pda_addr; u16 pda_size; }; static const struct fw_info orinoco_fw[] = { { NULL, "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 }, { NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 }, { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 } }; MODULE_FIRMWARE("agere_sta_fw.bin"); MODULE_FIRMWARE("agere_ap_fw.bin"); MODULE_FIRMWARE("prism_sta_fw.bin"); MODULE_FIRMWARE("prism_ap_fw.bin"); MODULE_FIRMWARE("symbol_sp24t_prim_fw"); MODULE_FIRMWARE("symbol_sp24t_sec_fw"); /* Structure used to access fields in FW * Make sure LE decoding macros are used */ struct orinoco_fw_header { char hdr_vers[6]; /* ASCII string for header version */ __le16 headersize; /* Total length of header */ __le32 entry_point; /* NIC entry point */ __le32 blocks; /* Number of blocks to program */ __le32 block_offset; /* Offset of block data from eof header */ __le32 pdr_offset; /* Offset to PDR data from eof header */ __le32 pri_offset; /* Offset to primary plug data */ __le32 compat_offset; /* Offset to compatibility data*/ char signature[0]; /* FW signature length headersize-20 */ } __packed; /* Check the range of various header entries. Return a pointer to a * description of the problem, or NULL if everything checks out. */ static const char *validate_fw(const struct orinoco_fw_header *hdr, size_t len) { u16 hdrsize; if (len < sizeof(*hdr)) return "image too small"; if (memcmp(hdr->hdr_vers, "HFW", 3) != 0) return "format not recognised"; hdrsize = le16_to_cpu(hdr->headersize); if (hdrsize > len) return "bad headersize"; if ((hdrsize + le32_to_cpu(hdr->block_offset)) > len) return "bad block offset"; if ((hdrsize + le32_to_cpu(hdr->pdr_offset)) > len) return "bad PDR offset"; if ((hdrsize + le32_to_cpu(hdr->pri_offset)) > len) return "bad PRI offset"; if ((hdrsize + le32_to_cpu(hdr->compat_offset)) > len) return "bad compat offset"; /* TODO: consider adding a checksum or CRC to the firmware format */ return NULL; } #if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) static inline const struct firmware * orinoco_cached_fw_get(struct orinoco_private *priv, bool primary) { if (primary) return priv->cached_pri_fw; else return priv->cached_fw; } #else #define orinoco_cached_fw_get(priv, primary) (NULL) #endif /* Download either STA or AP firmware into the card. */ static int orinoco_dl_firmware(struct orinoco_private *priv, const struct fw_info *fw, int ap) { /* Plug Data Area (PDA) */ __le16 *pda; struct hermes *hw = &priv->hw; const struct firmware *fw_entry; const struct orinoco_fw_header *hdr; const unsigned char *first_block; const void *end; const char *firmware; const char *fw_err; struct device *dev = priv->dev; int err = 0; pda = kzalloc(fw->pda_size, GFP_KERNEL); if (!pda) return -ENOMEM; if (ap) firmware = fw->ap_fw; else firmware = fw->sta_fw; dev_dbg(dev, "Attempting to download firmware %s\n", firmware); /* Read current plug data */ err = hw->ops->read_pda_h(hw, pda, fw->pda_addr, fw->pda_size); dev_dbg(dev, "Read PDA returned %d\n", err); if (err) goto free; if (!orinoco_cached_fw_get(priv, false)) { err = request_firmware(&fw_entry, firmware, priv->dev); if (err) { dev_err(dev, "Cannot find firmware %s\n", firmware); err = -ENOENT; goto free; } } else fw_entry = orinoco_cached_fw_get(priv, false); hdr = (const struct orinoco_fw_header *) fw_entry->data; fw_err = validate_fw(hdr, fw_entry->size); if (fw_err) { dev_warn(dev, "Invalid firmware image detected (%s). " "Aborting download\n", fw_err); err = -EINVAL; goto abort; } /* Enable aux port to allow programming */ err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point)); dev_dbg(dev, "Program init returned %d\n", err); if (err != 0) goto abort; /* Program data */ first_block = (fw_entry->data + le16_to_cpu(hdr->headersize) + le32_to_cpu(hdr->block_offset)); end = fw_entry->data + fw_entry->size; err = hermes_program(hw, first_block, end); dev_dbg(dev, "Program returned %d\n", err); if (err != 0) goto abort; /* Update production data */ first_block = (fw_entry->data + le16_to_cpu(hdr->headersize) + le32_to_cpu(hdr->pdr_offset)); err = hermes_apply_pda_with_defaults(hw, first_block, end, pda, &pda[fw->pda_size / sizeof(*pda)]); dev_dbg(dev, "Apply PDA returned %d\n", err); if (err) goto abort; /* Tell card we've finished */ err = hw->ops->program_end(hw); dev_dbg(dev, "Program end returned %d\n", err); if (err != 0) goto abort; /* Check if we're running */ dev_dbg(dev, "hermes_present returned %d\n", hermes_present(hw)); abort: /* If we requested the firmware, release it. */ if (!orinoco_cached_fw_get(priv, false)) release_firmware(fw_entry); free: kfree(pda); return err; } /* * Process a firmware image - stop the card, load the firmware, reset * the card and make sure it responds. For the secondary firmware take * care of the PDA - read it and then write it on top of the firmware. */ static int symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, const unsigned char *image, const void *end, int secondary) { struct hermes *hw = &priv->hw; int ret = 0; const unsigned char *ptr; const unsigned char *first_block; /* Plug Data Area (PDA) */ __le16 *pda = NULL; /* Binary block begins after the 0x1A marker */ ptr = image; while (*ptr++ != TEXT_END); first_block = ptr; /* Read the PDA from EEPROM */ if (secondary) { pda = kzalloc(fw->pda_size, GFP_KERNEL); if (!pda) return -ENOMEM; ret = hw->ops->read_pda_h(hw, pda, fw->pda_addr, fw->pda_size); if (ret) goto free; } /* Stop the firmware, so that it can be safely rewritten */ if (priv->stop_fw) { ret = priv->stop_fw(priv, 1); if (ret) goto free; } /* Program the adapter with new firmware */ ret = hermes_program(hw, first_block, end); if (ret) goto free; /* Write the PDA to the adapter */ if (secondary) { size_t len = hermes_blocks_length(first_block, end); ptr = first_block + len; ret = hermes_apply_pda(hw, ptr, end, pda, &pda[fw->pda_size / sizeof(*pda)]); kfree(pda); if (ret) return ret; } /* Run the firmware */ if (priv->stop_fw) { ret = priv->stop_fw(priv, 0); if (ret) return ret; } /* Reset hermes chip and make sure it responds */ ret = hw->ops->init(hw); /* hermes_reset() should return 0 with the secondary firmware */ if (secondary && ret != 0) return -ENODEV; /* And this should work with any firmware */ if (!hermes_present(hw)) return -ENODEV; return 0; free: kfree(pda); return ret; } /* * Download the firmware into the card, this also does a PCMCIA soft * reset on the card, to make sure it's in a sane state. */ static int symbol_dl_firmware(struct orinoco_private *priv, const struct fw_info *fw) { struct device *dev = priv->dev; int ret; const struct firmware *fw_entry; if (!orinoco_cached_fw_get(priv, true)) { if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) { dev_err(dev, "Cannot find firmware: %s\n", fw->pri_fw); return -ENOENT; } } else fw_entry = orinoco_cached_fw_get(priv, true); /* Load primary firmware */ ret = symbol_dl_image(priv, fw, fw_entry->data, fw_entry->data + fw_entry->size, 0); if (!orinoco_cached_fw_get(priv, true)) release_firmware(fw_entry); if (ret) { dev_err(dev, "Primary firmware download failed\n"); return ret; } if (!orinoco_cached_fw_get(priv, false)) { if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) { dev_err(dev, "Cannot find firmware: %s\n", fw->sta_fw); return -ENOENT; } } else fw_entry = orinoco_cached_fw_get(priv, false); /* Load secondary firmware */ ret = symbol_dl_image(priv, fw, fw_entry->data, fw_entry->data + fw_entry->size, 1); if (!orinoco_cached_fw_get(priv, false)) release_firmware(fw_entry); if (ret) dev_err(dev, "Secondary firmware download failed\n"); return ret; } int orinoco_download(struct orinoco_private *priv) { int err = 0; /* Reload firmware */ switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* case FIRMWARE_TYPE_INTERSIL: */ err = orinoco_dl_firmware(priv, &orinoco_fw[priv->firmware_type], 0); break; case FIRMWARE_TYPE_SYMBOL: err = symbol_dl_firmware(priv, &orinoco_fw[priv->firmware_type]); break; case FIRMWARE_TYPE_INTERSIL: break; } /* TODO: if we fail we probably need to reinitialise * the driver */ return err; } #if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) void orinoco_cache_fw(struct orinoco_private *priv, int ap) { const struct firmware *fw_entry = NULL; const char *pri_fw; const char *fw; pri_fw = orinoco_fw[priv->firmware_type].pri_fw; if (ap) fw = orinoco_fw[priv->firmware_type].ap_fw; else fw = orinoco_fw[priv->firmware_type].sta_fw; if (pri_fw) { if (request_firmware(&fw_entry, pri_fw, priv->dev) == 0) priv->cached_pri_fw = fw_entry; } if (fw) { if (request_firmware(&fw_entry, fw, priv->dev) == 0) priv->cached_fw = fw_entry; } } void orinoco_uncache_fw(struct orinoco_private *priv) { release_firmware(priv->cached_pri_fw); release_firmware(priv->cached_fw); priv->cached_pri_fw = NULL; priv->cached_fw = NULL; } #endif compat-drivers-2012-09-18/drivers/net/wireless/orinoco/hw.h0000644000175000017500000000442012026211315022720 0ustar mcgrofmcgrof/* Encapsulate basic setting changes on Hermes hardware * * See copyright notice in main.c */ #ifndef _ORINOCO_HW_H_ #define _ORINOCO_HW_H_ #include #include #include /* Hardware BAPs */ #define USER_BAP 0 #define IRQ_BAP 1 /* WEP key sizes */ #define SMALL_KEY_SIZE 5 #define LARGE_KEY_SIZE 13 /* Number of supported channels */ #define NUM_CHANNELS 14 /* Forward declarations */ struct orinoco_private; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) struct dev_addr_list; #endif int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, size_t fw_name_len, u32 *hw_ver); int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr); int orinoco_hw_allocate_fid(struct orinoco_private *priv); int orinoco_get_bitratemode(int bitrate, int automatic); void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic); int orinoco_hw_program_rids(struct orinoco_private *priv); int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc); int __orinoco_hw_set_bitrate(struct orinoco_private *priv); int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate); int __orinoco_hw_set_wap(struct orinoco_private *priv); int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv); int __orinoco_hw_setup_enc(struct orinoco_private *priv); int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, int set_tx, u8 *key, u8 *rsc, size_t rsc_len, u8 *tsc, size_t tsc_len); int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx); int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct net_device *dev, int mc_count, int promisc); int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, char buf[IW_ESSID_MAX_SIZE + 1]); int orinoco_hw_get_freq(struct orinoco_private *priv); int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, s32 *rates, int max); int orinoco_hw_trigger_scan(struct orinoco_private *priv, const struct cfg80211_ssid *ssid); int orinoco_hw_disassociate(struct orinoco_private *priv, u8 *addr, u16 reason_code); int orinoco_hw_get_current_bssid(struct orinoco_private *priv, u8 *addr); #endif /* _ORINOCO_HW_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/hw.c0000644000175000017500000010400112026211315022707 0ustar mcgrofmcgrof/* Encapsulate basic setting changes and retrieval on Hermes hardware * * See copyright notice in main.c */ #include #include #include #include #include #include #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" #include "hw.h" #define SYMBOL_MAX_VER_LEN (14) /* Symbol firmware has a bug allocating buffers larger than this */ #define TX_NICBUF_SIZE_BUG 1585 /********************************************************************/ /* Data tables */ /********************************************************************/ /* This tables gives the actual meanings of the bitrate IDs returned * by the firmware. */ static const struct { int bitrate; /* in 100s of kilobits */ int automatic; u16 agere_txratectrl; u16 intersil_txratectrl; } bitrate_table[] = { {110, 1, 3, 15}, /* Entry 0 is the default */ {10, 0, 1, 1}, {10, 1, 1, 1}, {20, 0, 2, 2}, {20, 1, 6, 3}, {55, 0, 4, 4}, {55, 1, 7, 7}, {110, 0, 5, 8}, }; #define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) /* Firmware version encoding */ struct comp_id { u16 id, variant, major, minor; } __packed; static inline enum fwtype determine_firmware_type(struct comp_id *nic_id) { if (nic_id->id < 0x8000) return FIRMWARE_TYPE_AGERE; else if (nic_id->id == 0x8000 && nic_id->major == 0) return FIRMWARE_TYPE_SYMBOL; else return FIRMWARE_TYPE_INTERSIL; } /* Set priv->firmware type, determine firmware properties * This function can be called before we have registerred with netdev, * so all errors go out with dev_* rather than printk * * If non-NULL stores a firmware description in fw_name. * If non-NULL stores a HW version in hw_ver * * These are output via generic cfg80211 ethtool support. */ int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, size_t fw_name_len, u32 *hw_ver) { struct device *dev = priv->dev; struct hermes *hw = &priv->hw; int err; struct comp_id nic_id, sta_id; unsigned int firmver; char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2))); /* Get the hardware version */ err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); if (err) { dev_err(dev, "Cannot read hardware identity: error %d\n", err); return err; } le16_to_cpus(&nic_id.id); le16_to_cpus(&nic_id.variant); le16_to_cpus(&nic_id.major); le16_to_cpus(&nic_id.minor); dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n", nic_id.id, nic_id.variant, nic_id.major, nic_id.minor); if (hw_ver) *hw_ver = (((nic_id.id & 0xff) << 24) | ((nic_id.variant & 0xff) << 16) | ((nic_id.major & 0xff) << 8) | (nic_id.minor & 0xff)); priv->firmware_type = determine_firmware_type(&nic_id); /* Get the firmware version */ err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); if (err) { dev_err(dev, "Cannot read station identity: error %d\n", err); return err; } le16_to_cpus(&sta_id.id); le16_to_cpus(&sta_id.variant); le16_to_cpus(&sta_id.major); le16_to_cpus(&sta_id.minor); dev_info(dev, "Station identity %04x:%04x:%04x:%04x\n", sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); switch (sta_id.id) { case 0x15: dev_err(dev, "Primary firmware is active\n"); return -ENODEV; case 0x14b: dev_err(dev, "Tertiary firmware is active\n"); return -ENODEV; case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ case 0x21: /* Symbol Spectrum24 Trilogy */ break; default: dev_notice(dev, "Unknown station ID, please report\n"); break; } /* Default capabilities */ priv->has_sensitivity = 1; priv->has_mwo = 0; priv->has_preamble = 0; priv->has_port3 = 1; priv->has_ibss = 1; priv->has_wep = 0; priv->has_big_wep = 0; priv->has_alt_txcntl = 0; priv->has_ext_scan = 0; priv->has_wpa = 0; priv->do_fw_download = 0; /* Determine capabilities from the firmware version */ switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ if (fw_name) snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor); firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; priv->has_ibss = (firmver >= 0x60006); priv->has_wep = (firmver >= 0x40020); priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell Gold cards from the others? */ priv->has_mwo = (firmver >= 0x60000); priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ priv->ibss_port = 1; priv->has_hostscan = (firmver >= 0x8000a); priv->do_fw_download = 1; priv->broken_monitor = (firmver >= 0x80000); priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ priv->has_wpa = (firmver >= 0x9002a); /* Tested with Agere firmware : * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II * Tested CableTron firmware : 4.32 => Anton */ break; case FIRMWARE_TYPE_SYMBOL: /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ /* Intel MAC : 00:02:B3:* */ /* 3Com MAC : 00:50:DA:* */ memset(tmp, 0, sizeof(tmp)); /* Get the Symbol firmware version */ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SECONDARYVERSION_SYMBOL, SYMBOL_MAX_VER_LEN, NULL, &tmp); if (err) { dev_warn(dev, "Error %d reading Symbol firmware info. " "Wildly guessing capabilities...\n", err); firmver = 0; tmp[0] = '\0'; } else { /* The firmware revision is a string, the format is * something like : "V2.20-01". * Quick and dirty parsing... - Jean II */ firmver = ((tmp[1] - '0') << 16) | ((tmp[3] - '0') << 12) | ((tmp[4] - '0') << 8) | ((tmp[6] - '0') << 4) | (tmp[7] - '0'); tmp[SYMBOL_MAX_VER_LEN] = '\0'; } if (fw_name) snprintf(fw_name, fw_name_len, "Symbol %s", tmp); priv->has_ibss = (firmver >= 0x20000); priv->has_wep = (firmver >= 0x15012); priv->has_big_wep = (firmver >= 0x20000); priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || (firmver >= 0x29000 && firmver < 0x30000) || firmver >= 0x31000; priv->has_preamble = (firmver >= 0x20000); priv->ibss_port = 4; /* Symbol firmware is found on various cards, but * there has been no attempt to check firmware * download on non-spectrum_cs based cards. * * Given that the Agere firmware download works * differently, we should avoid doing a firmware * download with the Symbol algorithm on non-spectrum * cards. * * For now we can identify a spectrum_cs based card * because it has a firmware reset function. */ priv->do_fw_download = (priv->stop_fw != NULL); priv->broken_disableport = (firmver == 0x25013) || (firmver >= 0x30000 && firmver <= 0x31000); priv->has_hostscan = (firmver >= 0x31001) || (firmver >= 0x29057 && firmver < 0x30000); /* Tested with Intel firmware : 0x20015 => Jean II */ /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ break; case FIRMWARE_TYPE_INTERSIL: /* D-Link, Linksys, Adtron, ZoomAir, and many others... * Samsung, Compaq 100/200 and Proxim are slightly * different and less well tested */ /* D-Link MAC : 00:40:05:* */ /* Addtron MAC : 00:90:D1:* */ if (fw_name) snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d", sta_id.major, sta_id.minor, sta_id.variant); firmver = ((unsigned long)sta_id.major << 16) | ((unsigned long)sta_id.minor << 8) | sta_id.variant; priv->has_ibss = (firmver >= 0x000700); /* FIXME */ priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); priv->has_pm = (firmver >= 0x000700); priv->has_hostscan = (firmver >= 0x010301); if (firmver >= 0x000800) priv->ibss_port = 0; else { dev_notice(dev, "Intersil firmware earlier than v0.8.x" " - several features not supported\n"); priv->ibss_port = 1; } break; } if (fw_name) dev_info(dev, "Firmware determined as %s\n", fw_name); #ifndef CONFIG_HERMES_PRISM if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { dev_err(dev, "Support for Prism chipset is not enabled\n"); return -ENODEV; } #endif return 0; } /* Read settings from EEPROM into our private structure. * MAC address gets dropped into callers buffer * Can be called before netdev registration. */ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) { struct device *dev = priv->dev; struct hermes_idstring nickbuf; struct hermes *hw = &priv->hw; int len; int err; u16 reclen; /* Get the MAC address */ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, ETH_ALEN, NULL, dev_addr); if (err) { dev_warn(dev, "Failed to read MAC address!\n"); goto out; } dev_dbg(dev, "MAC address %pM\n", dev_addr); /* Get the station name */ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, sizeof(nickbuf), &reclen, &nickbuf); if (err) { dev_err(dev, "failed to read station name\n"); goto out; } if (nickbuf.len) len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); else len = min(IW_ESSID_MAX_SIZE, 2 * reclen); memcpy(priv->nick, &nickbuf.val, len); priv->nick[len] = '\0'; dev_dbg(dev, "Station name \"%s\"\n", priv->nick); /* Get allowed channels */ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, &priv->channel_mask); if (err) { dev_err(dev, "Failed to read channel list!\n"); goto out; } /* Get initial AP density */ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &priv->ap_density); if (err || priv->ap_density < 1 || priv->ap_density > 3) priv->has_sensitivity = 0; /* Get initial RTS threshold */ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, &priv->rts_thresh); if (err) { dev_err(dev, "Failed to read RTS threshold!\n"); goto out; } /* Get initial fragmentation settings */ if (priv->has_mwo) err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMWOROBUST_AGERE, &priv->mwo_robust); else err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, &priv->frag_thresh); if (err) { dev_err(dev, "Failed to read fragmentation settings!\n"); goto out; } /* Power management setup */ if (priv->has_pm) { priv->pm_on = 0; priv->pm_mcast = 1; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMAXSLEEPDURATION, &priv->pm_period); if (err) { dev_err(dev, "Failed to read power management " "period!\n"); goto out; } err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, &priv->pm_timeout); if (err) { dev_err(dev, "Failed to read power management " "timeout!\n"); goto out; } } /* Preamble setup */ if (priv->has_preamble) { err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL, &priv->preamble); if (err) { dev_err(dev, "Failed to read preamble setup\n"); goto out; } } /* Retry settings */ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, &priv->short_retry_limit); if (err) { dev_err(dev, "Failed to read short retry limit\n"); goto out; } err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, &priv->long_retry_limit); if (err) { dev_err(dev, "Failed to read long retry limit\n"); goto out; } err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, &priv->retry_lifetime); if (err) { dev_err(dev, "Failed to read max retry lifetime\n"); goto out; } out: return err; } /* Can be called before netdev registration */ int orinoco_hw_allocate_fid(struct orinoco_private *priv) { struct device *dev = priv->dev; struct hermes *hw = &priv->hw; int err; err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { /* Try workaround for old Symbol firmware bug */ priv->nicbuf_size = TX_NICBUF_SIZE_BUG; err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); dev_warn(dev, "Firmware ALLOC bug detected " "(old Symbol firmware?). Work around %s\n", err ? "failed!" : "ok."); } return err; } int orinoco_get_bitratemode(int bitrate, int automatic) { int ratemode = -1; int i; if ((bitrate != 10) && (bitrate != 20) && (bitrate != 55) && (bitrate != 110)) return ratemode; for (i = 0; i < BITRATE_TABLE_SIZE; i++) { if ((bitrate_table[i].bitrate == bitrate) && (bitrate_table[i].automatic == automatic)) { ratemode = i; break; } } return ratemode; } void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic) { BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE)); *bitrate = bitrate_table[ratemode].bitrate * 100000; *automatic = bitrate_table[ratemode].automatic; } int orinoco_hw_program_rids(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct wireless_dev *wdev = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; struct hermes_idstring idbuf; /* Set the MAC address */ err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); if (err) { printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err); return err; } /* Set up the link mode */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type); if (err) { printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err); return err; } /* Set the channel/frequency */ if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel); if (err) { printk(KERN_ERR "%s: Error %d setting channel %d\n", dev->name, err, priv->channel); return err; } } if (priv->has_ibss) { u16 createibss; if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { printk(KERN_WARNING "%s: This firmware requires an " "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); /* With wvlan_cs, in this case, we would crash. * hopefully, this driver will behave better... * Jean II */ createibss = 0; } else { createibss = priv->createibss; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS, createibss); if (err) { printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err); return err; } } /* Set the desired BSSID */ err = __orinoco_hw_set_wap(priv); if (err) { printk(KERN_ERR "%s: Error %d setting AP address\n", dev->name, err); return err; } /* Set the desired ESSID */ idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err); return err; } err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err); return err; } /* Set the station name */ idbuf.len = cpu_to_le16(strlen(priv->nick)); memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err); return err; } /* Set AP density */ if (priv->has_sensitivity) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, priv->ap_density); if (err) { printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " "Disabling sensitivity control\n", dev->name, err); priv->has_sensitivity = 0; } } /* Set RTS threshold */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh); if (err) { printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err); return err; } /* Set fragmentation threshold or MWO robustness */ if (priv->has_mwo) err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMWOROBUST_AGERE, priv->mwo_robust); else err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, priv->frag_thresh); if (err) { printk(KERN_ERR "%s: Error %d setting fragmentation\n", dev->name, err); return err; } /* Set bitrate */ err = __orinoco_hw_set_bitrate(priv); if (err) { printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err); return err; } /* Set power management */ if (priv->has_pm) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, priv->pm_on); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, priv->pm_mcast); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMAXSLEEPDURATION, priv->pm_period); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, priv->pm_timeout); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } } /* Set preamble - only for Symbol so far... */ if (priv->has_preamble) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL, priv->preamble); if (err) { printk(KERN_ERR "%s: Error %d setting preamble\n", dev->name, err); return err; } } /* Set up encryption */ if (priv->has_wep || priv->has_wpa) { err = __orinoco_hw_setup_enc(priv); if (err) { printk(KERN_ERR "%s: Error %d activating encryption\n", dev->name, err); return err; } } if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Enable monitor mode */ dev->type = ARPHRD_IEEE80211; err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_MONITOR, 0, NULL); } else { /* Disable monitor mode */ dev->type = ARPHRD_ETHER; err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_STOP, 0, NULL); } if (err) return err; /* Reset promiscuity / multicast*/ priv->promiscuous = 0; priv->mc_count = 0; /* Record mode change */ wdev->iftype = priv->iw_mode; return 0; } /* Get tsc from the firmware */ int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) { struct hermes *hw = &priv->hw; int err = 0; u8 tsc_arr[4][ORINOCO_SEQ_LEN]; if ((key < 0) || (key >= 4)) return -EINVAL; err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, sizeof(tsc_arr), NULL, &tsc_arr); if (!err) memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); return err; } int __orinoco_hw_set_bitrate(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; int ratemode = priv->bitratemode; int err = 0; if (ratemode >= BITRATE_TABLE_SIZE) { printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", priv->ndev->name, ratemode); return -EINVAL; } switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXRATECONTROL, bitrate_table[ratemode].agere_txratectrl); break; case FIRMWARE_TYPE_INTERSIL: case FIRMWARE_TYPE_SYMBOL: err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXRATECONTROL, bitrate_table[ratemode].intersil_txratectrl); break; default: BUG(); } return err; } int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate) { struct hermes *hw = &priv->hw; int i; int err = 0; u16 val; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTTXRATE, &val); if (err) return err; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Lucent style rate */ /* Note : in Lucent firmware, the return value of * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s, * and therefore is totally different from the * encoding of HERMES_RID_CNFTXRATECONTROL. * Don't forget that 6Mb/s is really 5.5Mb/s */ if (val == 6) *bitrate = 5500000; else *bitrate = val * 1000000; break; case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ for (i = 0; i < BITRATE_TABLE_SIZE; i++) if (bitrate_table[i].intersil_txratectrl == val) { *bitrate = bitrate_table[i].bitrate * 100000; break; } if (i >= BITRATE_TABLE_SIZE) { printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n", priv->ndev->name, val); err = -EIO; } break; default: BUG(); } return err; } /* Set fixed AP address */ int __orinoco_hw_set_wap(struct orinoco_private *priv) { int roaming_flag; int err = 0; struct hermes *hw = &priv->hw; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* not supported */ break; case FIRMWARE_TYPE_INTERSIL: if (priv->bssid_fixed) roaming_flag = 2; else roaming_flag = 1; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFROAMINGMODE, roaming_flag); break; case FIRMWARE_TYPE_SYMBOL: err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFMANDATORYBSSID_SYMBOL, &priv->desired_bssid); break; } return err; } /* Change the WEP keys and/or the current keys. Can be called * either from __orinoco_hw_setup_enc() or directly from * orinoco_ioctl_setiwencode(). In the later case the association * with the AP is not broken (if the firmware can handle it), * which is needed for 802.1x implementations. */ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; int err = 0; int i; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: { struct orinoco_key keys[ORINOCO_MAX_KEYS]; memset(&keys, 0, sizeof(keys)); for (i = 0; i < ORINOCO_MAX_KEYS; i++) { int len = min(priv->keys[i].key_len, ORINOCO_MAX_KEY_SIZE); memcpy(&keys[i].data, priv->keys[i].key, len); if (len > SMALL_KEY_SIZE) keys[i].len = cpu_to_le16(LARGE_KEY_SIZE); else if (len > 0) keys[i].len = cpu_to_le16(SMALL_KEY_SIZE); else keys[i].len = cpu_to_le16(0); } err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFWEPKEYS_AGERE, &keys); if (err) return err; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXKEY_AGERE, priv->tx_key); if (err) return err; break; } case FIRMWARE_TYPE_INTERSIL: case FIRMWARE_TYPE_SYMBOL: { int keylen; /* Force uniform key length to work around * firmware bugs */ keylen = priv->keys[priv->tx_key].key_len; if (keylen > LARGE_KEY_SIZE) { printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", priv->ndev->name, priv->tx_key, keylen); return -E2BIG; } else if (keylen > SMALL_KEY_SIZE) keylen = LARGE_KEY_SIZE; else if (keylen > 0) keylen = SMALL_KEY_SIZE; else keylen = 0; /* Write all 4 keys */ for (i = 0; i < ORINOCO_MAX_KEYS; i++) { u8 key[LARGE_KEY_SIZE] = { 0 }; memcpy(key, priv->keys[i].key, priv->keys[i].key_len); err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDEFAULTKEY0 + i, HERMES_BYTES_TO_RECLEN(keylen), key); if (err) return err; } /* Write the index of the key used in transmission */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID, priv->tx_key); if (err) return err; } break; } return 0; } int __orinoco_hw_setup_enc(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; int err = 0; int master_wep_flag; int auth_flag; int enc_flag; /* Setup WEP keys */ if (priv->encode_alg == ORINOCO_ALG_WEP) __orinoco_hw_setup_wepkeys(priv); if (priv->wep_restrict) auth_flag = HERMES_AUTH_SHARED_KEY; else auth_flag = HERMES_AUTH_OPEN; if (priv->wpa_enabled) enc_flag = 2; else if (priv->encode_alg == ORINOCO_ALG_WEP) enc_flag = 1; else enc_flag = 0; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ if (priv->encode_alg == ORINOCO_ALG_WEP) { /* Enable the shared-key authentication. */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFAUTHENTICATION_AGERE, auth_flag); } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPENABLED_AGERE, enc_flag); if (err) return err; if (priv->has_wpa) { /* Set WPA key management */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE, priv->key_mgmt); if (err) return err; } break; case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ if (priv->encode_alg == ORINOCO_ALG_WEP) { if (priv->wep_restrict || (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | HERMES_WEP_EXCL_UNENCRYPTED; else master_wep_flag = HERMES_WEP_PRIVACY_INVOKED; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFAUTHENTICATION, auth_flag); if (err) return err; } else master_wep_flag = 0; if (priv->iw_mode == NL80211_IFTYPE_MONITOR) master_wep_flag |= HERMES_WEP_HOST_DECRYPT; /* Master WEP setting : on/off */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPFLAGS_INTERSIL, master_wep_flag); if (err) return err; break; } return 0; } /* key must be 32 bytes, including the tx and rx MIC keys. * rsc must be NULL or up to 8 bytes * tsc must be NULL or up to 8 bytes */ int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, int set_tx, u8 *key, u8 *rsc, size_t rsc_len, u8 *tsc, size_t tsc_len) { struct { __le16 idx; u8 rsc[ORINOCO_SEQ_LEN]; u8 key[TKIP_KEYLEN]; u8 tx_mic[MIC_KEYLEN]; u8 rx_mic[MIC_KEYLEN]; u8 tsc[ORINOCO_SEQ_LEN]; } __packed buf; struct hermes *hw = &priv->hw; int ret; int err; int k; u16 xmitting; key_idx &= 0x3; if (set_tx) key_idx |= 0x8000; buf.idx = cpu_to_le16(key_idx); memcpy(buf.key, key, sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); if (rsc_len > sizeof(buf.rsc)) rsc_len = sizeof(buf.rsc); if (tsc_len > sizeof(buf.tsc)) tsc_len = sizeof(buf.tsc); memset(buf.rsc, 0, sizeof(buf.rsc)); memset(buf.tsc, 0, sizeof(buf.tsc)); if (rsc != NULL) memcpy(buf.rsc, rsc, rsc_len); if (tsc != NULL) memcpy(buf.tsc, tsc, tsc_len); else buf.tsc[4] = 0x10; /* Wait up to 100ms for tx queue to empty */ for (k = 100; k > 0; k--) { udelay(1000); ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY, &xmitting); if (ret || !xmitting) break; } if (k == 0) ret = -ETIMEDOUT; err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE, &buf); return ret ? ret : err; } int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx) { struct hermes *hw = &priv->hw; int err; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, key_idx); if (err) printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n", priv->ndev->name, err, key_idx); return err; } int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct net_device *dev, int mc_count, int promisc) { struct hermes *hw = &priv->hw; int err = 0; if (promisc != priv->promiscuous) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPROMISCUOUSMODE, promisc); if (err) { printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", priv->ndev->name, err); } else priv->promiscuous = promisc; } /* If we're not in promiscuous mode, then we need to set the * group address if either we want to multicast, or if we were * multicasting and want to stop */ if (!promisc && (mc_count || priv->mc_count)) { struct netdev_hw_addr *ha; struct hermes_multicast mclist; int i = 0; netdev_for_each_mc_addr(ha, dev) { if (i == mc_count) break; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(mclist.addr[i++], ha->addr, ETH_ALEN); #else memcpy(mclist.addr[i++], ha->dmi_addr, ETH_ALEN); #endif } err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), &mclist); if (err) printk(KERN_ERR "%s: Error %d setting multicast list.\n", priv->ndev->name, err); else priv->mc_count = mc_count; } return err; } /* Return : < 0 -> error code ; >= 0 -> length */ int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, char buf[IW_ESSID_MAX_SIZE + 1]) { struct hermes *hw = &priv->hw; int err = 0; struct hermes_idstring essidbuf; char *p = (char *)(&essidbuf.val); int len; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (strlen(priv->desired_essid) > 0) { /* We read the desired SSID from the hardware rather than from priv->desired_essid, just in case the firmware is allowed to change it on us. I'm not sure about this */ /* My guess is that the OWNSSID should always be whatever * we set to the card, whereas CURRENT_SSID is the one that * may change... - Jean II */ u16 rid; *active = 1; rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : HERMES_RID_CNFDESIREDSSID; err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), NULL, &essidbuf); if (err) goto fail_unlock; } else { *active = 0; err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, sizeof(essidbuf), NULL, &essidbuf); if (err) goto fail_unlock; } len = le16_to_cpu(essidbuf.len); BUG_ON(len > IW_ESSID_MAX_SIZE); memset(buf, 0, IW_ESSID_MAX_SIZE); memcpy(buf, p, len); err = len; fail_unlock: orinoco_unlock(priv, &flags); return err; } int orinoco_hw_get_freq(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; int err = 0; u16 channel; int freq = 0; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel); if (err) goto out; /* Intersil firmware 1.3.5 returns 0 when the interface is down */ if (channel == 0) { err = -EBUSY; goto out; } if ((channel < 1) || (channel > NUM_CHANNELS)) { printk(KERN_WARNING "%s: Channel out of range (%d)!\n", priv->ndev->name, channel); err = -EBUSY; goto out; } freq = ieee80211_dsss_chan_to_freq(channel); out: orinoco_unlock(priv, &flags); if (err > 0) err = -EBUSY; return err ? err : freq; } int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, s32 *rates, int max) { struct hermes *hw = &priv->hw; struct hermes_idstring list; unsigned char *p = (unsigned char *)&list.val; int err = 0; int num; int i; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, sizeof(list), NULL, &list); orinoco_unlock(priv, &flags); if (err) return err; num = le16_to_cpu(list.len); *numrates = num; num = min(num, max); for (i = 0; i < num; i++) rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ return 0; } int orinoco_hw_trigger_scan(struct orinoco_private *priv, const struct cfg80211_ssid *ssid) { struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; unsigned long flags; int err = 0; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* Scanning with port 0 disabled would fail */ if (!netif_running(dev)) { err = -ENETDOWN; goto out; } /* In monitor mode, the scan results are always empty. * Probe responses are passed to the driver as received * frames and could be processed in software. */ if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { err = -EOPNOTSUPP; goto out; } if (priv->has_hostscan) { switch (priv->firmware_type) { case FIRMWARE_TYPE_SYMBOL: err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFHOSTSCAN_SYMBOL, HERMES_HOSTSCAN_SYMBOL_ONCE | HERMES_HOSTSCAN_SYMBOL_BCAST); break; case FIRMWARE_TYPE_INTERSIL: { __le16 req[3]; req[0] = cpu_to_le16(0x3fff); /* All channels */ req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ req[2] = 0; /* Any ESSID */ err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFHOSTSCAN, &req); break; } case FIRMWARE_TYPE_AGERE: if (ssid->ssid_len > 0) { struct hermes_idstring idbuf; size_t len = ssid->ssid_len; idbuf.len = cpu_to_le16(len); memcpy(idbuf.val, ssid->ssid, len); err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFSCANSSID_AGERE, HERMES_BYTES_TO_RECLEN(len + 2), &idbuf); } else err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSCANSSID_AGERE, 0); /* Any ESSID */ if (err) break; if (priv->has_ext_scan) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSCANCHANNELS2GHZ, 0x7FFF); if (err) goto out; err = hermes_inquire(hw, HERMES_INQ_CHANNELINFO); } else err = hermes_inquire(hw, HERMES_INQ_SCAN); break; } } else err = hermes_inquire(hw, HERMES_INQ_SCAN); out: orinoco_unlock(priv, &flags); return err; } /* Disassociate from node with BSSID addr */ int orinoco_hw_disassociate(struct orinoco_private *priv, u8 *addr, u16 reason_code) { struct hermes *hw = &priv->hw; int err; struct { u8 addr[ETH_ALEN]; __le16 reason_code; } __packed buf; /* Currently only supported by WPA enabled Agere fw */ if (!priv->has_wpa) return -EOPNOTSUPP; memcpy(buf.addr, addr, ETH_ALEN); buf.reason_code = cpu_to_le16(reason_code); err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFDISASSOCIATE, &buf); return err; } int orinoco_hw_get_current_bssid(struct orinoco_private *priv, u8 *addr) { struct hermes *hw = &priv->hw; int err; err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, ETH_ALEN, NULL, addr); return err; } compat-drivers-2012-09-18/drivers/net/wireless/orinoco/spectrum_cs.c0000644000175000017500000003072412026211315024632 0ustar mcgrofmcgrof/* * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as * Symbol Wireless Networker LA4137, CompactFlash cards by Socket * Communications and Intel PRO/Wireless 2011B. * * The driver implements Symbol firmware download. The rest is handled * in hermes.c and main.c. * * Utilities for downloading the Symbol firmware are available at * http://sourceforge.net/projects/orinoco/ * * Copyright (C) 2002-2005 Pavel Roskin * Portions based on orinoco_cs.c: * Copyright (C) David Gibson, Linuxcare Australia * Portions based on Spectrum24tDnld.c from original spectrum24 driver: * Copyright (C) Symbol Technologies. * * See copyright notice in file main.c. */ #define DRIVER_NAME "spectrum_cs" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include #include #include "orinoco.h" /********************************************************************/ /* Module stuff */ /********************************************************************/ MODULE_AUTHOR("Pavel Roskin "); MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader"); MODULE_LICENSE("Dual MPL/GPL"); /* Module parameters */ /* Some D-Link cards have buggy CIS. They do work at 5v properly, but * don't have any CIS entry for it. This workaround it... */ static int ignore_cis_vcc; /* = 0 */ module_param(ignore_cis_vcc, int, 0); MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket"); /********************************************************************/ /* Data structures */ /********************************************************************/ /* PCMCIA specific device information (goes in the card field of * struct orinoco_private */ struct orinoco_pccard { struct pcmcia_device *p_dev; }; /********************************************************************/ /* Function prototypes */ /********************************************************************/ static int spectrum_cs_config(struct pcmcia_device *link); static void spectrum_cs_release(struct pcmcia_device *link); /* Constants for the CISREG_CCSR register */ #define HCR_RUN 0x07 /* run firmware after reset */ #define HCR_IDLE 0x0E /* don't run firmware after reset */ #define HCR_MEM16 0x10 /* memory width bit, should be preserved */ /* * Reset the card using configuration registers COR and CCSR. * If IDLE is 1, stop the firmware, so that it can be safely rewritten. */ static int spectrum_reset(struct pcmcia_device *link, int idle) { int ret; u8 save_cor; u8 ccsr; /* Doing it if hardware is gone is guaranteed crash */ if (!pcmcia_dev_present(link)) return -ENODEV; /* Save original COR value */ ret = pcmcia_read_config_byte(link, CISREG_COR, &save_cor); if (ret) goto failed; /* Soft-Reset card */ ret = pcmcia_write_config_byte(link, CISREG_COR, (save_cor | COR_SOFT_RESET)); if (ret) goto failed; udelay(1000); /* Read CCSR */ ret = pcmcia_read_config_byte(link, CISREG_CCSR, &ccsr); if (ret) goto failed; /* * Start or stop the firmware. Memory width bit should be * preserved from the value we've just read. */ ccsr = (idle ? HCR_IDLE : HCR_RUN) | (ccsr & HCR_MEM16); ret = pcmcia_write_config_byte(link, CISREG_CCSR, ccsr); if (ret) goto failed; udelay(1000); /* Restore original COR configuration index */ ret = pcmcia_write_config_byte(link, CISREG_COR, (save_cor & ~COR_SOFT_RESET)); if (ret) goto failed; udelay(1000); return 0; failed: return -ENODEV; } /********************************************************************/ /* Device methods */ /********************************************************************/ static int spectrum_cs_hard_reset(struct orinoco_private *priv) { struct orinoco_pccard *card = priv->card; struct pcmcia_device *link = card->p_dev; /* Soft reset using COR and HCR */ spectrum_reset(link, 0); return 0; } static int spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle) { struct orinoco_pccard *card = priv->card; struct pcmcia_device *link = card->p_dev; return spectrum_reset(link, idle); } /********************************************************************/ /* PCMCIA stuff */ /********************************************************************/ static int spectrum_cs_probe(struct pcmcia_device *link) { struct orinoco_private *priv; struct orinoco_pccard *card; priv = alloc_orinocodev(sizeof(*card), &link->dev, spectrum_cs_hard_reset, spectrum_cs_stop_firmware); if (!priv) return -ENOMEM; card = priv->card; /* Link both structures together */ card->p_dev = link; link->priv = priv; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.Handler = orinoco_interrupt; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; #endif return spectrum_cs_config(link); } /* spectrum_cs_attach */ static void spectrum_cs_detach(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; orinoco_if_del(priv); spectrum_cs_release(link); free_orinocodev(priv); } /* spectrum_cs_detach */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int spectrum_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { if (p_dev->config_index == 0) return -EINVAL; return pcmcia_request_io(p_dev); }; #else static int spectrum_cs_config_check(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cfg, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { if (cfg->index == 0) goto next_entry; /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) { DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", __func__, vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000); if (!ignore_cis_vcc) goto next_entry; } } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) { if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) { DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", __func__, vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000); if (!ignore_cis_vcc) goto next_entry; } } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) p_dev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM)) p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) p_dev->resource[0]->end = p_dev->resource[1]->end = 0; #else p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; #endif if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= pcmcia_io_cfg_data_width(io->flags); p_dev->resource[0]->start = io->win[0].base; p_dev->resource[0]->end = io->win[0].len; #else p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; if (!(io->flags & CISTPL_IO_8BIT)) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16; if (!(io->flags & CISTPL_IO_16BIT)) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; p_dev->io.BasePort1 = io->win[0].base; p_dev->io.NumPorts1 = io->win[0].len; #endif if (io->nwin > 1) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) p_dev->resource[1]->flags = p_dev->resource[0]->flags; p_dev->resource[1]->start = io->win[1].base; p_dev->resource[1]->end = io->win[1].len; #else p_dev->io.Attributes2 = p_dev->io.Attributes1; p_dev->io.BasePort2 = io->win[1].base; p_dev->io.NumPorts2 = io->win[1].len; #endif } /* This reserves IO space but doesn't actually enable it */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) if (pcmcia_request_io(p_dev) != 0) #else if (pcmcia_request_io(p_dev, &p_dev->io) != 0) #endif goto next_entry; } return 0; next_entry: pcmcia_disable_device(p_dev); return -ENODEV; }; #endif static int spectrum_cs_config(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; struct hermes *hw = &priv->hw; int ret; void __iomem *mem; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; if (ignore_cis_vcc) link->config_flags &= ~CONF_AUTO_CHECK_VCC; #endif ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL); if (ret) { if (!ignore_cis_vcc) printk(KERN_ERR PFX "GetNextTuple(): No matching " "CIS configuration. Maybe you need the " "ignore_cis_vcc=1 parameter.\n"); goto failed; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) mem = ioport_map(link->resource[0]->start, resource_size(link->resource[0])); #else mem = ioport_map(link->io.BasePort1, link->io.NumPorts1); #endif if (!mem) goto failed; /* We initialize the hermes structure before completing PCMCIA * configuration just in case the interrupt handler gets * called. */ hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); hw->eeprom_pda = true; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = pcmcia_request_irq(link, orinoco_interrupt); #else ret = pcmcia_request_irq(link, &link->irq); #endif if (ret) goto failed; ret = pcmcia_enable_device(link); if (ret) goto failed; /* Reset card */ if (spectrum_cs_hard_reset(priv) != 0) goto failed; /* Initialise the main driver */ if (orinoco_init(priv) != 0) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto failed; } /* Register an interface with the stack */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) if (orinoco_if_add(priv, link->resource[0]->start, link->irq, NULL) != 0) { #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (orinoco_if_add(priv, link->io.BasePort1, link->irq, NULL) != 0) { #else if (orinoco_if_add(priv, link->io.BasePort1, link->irq.AssignedIRQ, NULL) != 0) { #endif printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } return 0; failed: spectrum_cs_release(link); return -ENODEV; } /* spectrum_cs_config */ static void spectrum_cs_release(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; unsigned long flags; /* We're committed to taking the device away now, so mark the * hardware as unavailable */ priv->hw.ops->lock_irqsave(&priv->lock, &flags); priv->hw_unavailable++; priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); pcmcia_disable_device(link); if (priv->hw.iobase) ioport_unmap(priv->hw.iobase); } /* spectrum_cs_release */ static int spectrum_cs_suspend(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; int err = 0; /* Mark the device as stopped, to block IO until later */ orinoco_down(priv); return err; } static int spectrum_cs_resume(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; int err = orinoco_up(priv); return err; } /********************************************************************/ /* Module initialization */ /********************************************************************/ static const struct pcmcia_device_id spectrum_cs_ids[] = { PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */ PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */ PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids); static struct pcmcia_driver orinoco_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = DRIVER_NAME, #else .drv = { .name = DRIVER_NAME, }, #endif .probe = spectrum_cs_probe, .remove = spectrum_cs_detach, .suspend = spectrum_cs_suspend, .resume = spectrum_cs_resume, .id_table = spectrum_cs_ids, }; static int __init init_spectrum_cs(void) { return pcmcia_register_driver(&orinoco_driver); } static void __exit exit_spectrum_cs(void) { pcmcia_unregister_driver(&orinoco_driver); } module_init(init_spectrum_cs); module_exit(exit_spectrum_cs); compat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco_cs.c0000644000175000017500000004362712026211315024446 0ustar mcgrofmcgrof/* orinoco_cs.c (formerly known as dldwd_cs.c) * * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ * EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others). * It should also be usable on various Prism II based cards such as the * Linksys, D-Link and Farallon Skyline. It should also work on Symbol * cards such as the 3Com AirConnect and Ericsson WLAN. * * Copyright notice & release notes in file main.c */ #define DRIVER_NAME "orinoco_cs" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include #include #include "orinoco.h" /********************************************************************/ /* Module stuff */ /********************************************************************/ MODULE_AUTHOR("David Gibson "); MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco," " Prism II based and similar wireless cards"); MODULE_LICENSE("Dual MPL/GPL"); /* Module parameters */ /* Some D-Link cards have buggy CIS. They do work at 5v properly, but * don't have any CIS entry for it. This workaround it... */ static int ignore_cis_vcc; /* = 0 */ module_param(ignore_cis_vcc, int, 0); MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket"); /********************************************************************/ /* Data structures */ /********************************************************************/ /* PCMCIA specific device information (goes in the card field of * struct orinoco_private */ struct orinoco_pccard { struct pcmcia_device *p_dev; /* Used to handle hard reset */ /* yuck, we need this hack to work around the insanity of the * PCMCIA layer */ unsigned long hard_reset_in_progress; }; /********************************************************************/ /* Function prototypes */ /********************************************************************/ static int orinoco_cs_config(struct pcmcia_device *link); static void orinoco_cs_release(struct pcmcia_device *link); static void orinoco_cs_detach(struct pcmcia_device *p_dev); /********************************************************************/ /* Device methods */ /********************************************************************/ static int orinoco_cs_hard_reset(struct orinoco_private *priv) { struct orinoco_pccard *card = priv->card; struct pcmcia_device *link = card->p_dev; int err; /* We need atomic ops here, because we're not holding the lock */ set_bit(0, &card->hard_reset_in_progress); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27) err = pcmcia_reset_card(link, NULL); #else err = pcmcia_reset_card(link->socket); #endif if (err) return err; msleep(100); clear_bit(0, &card->hard_reset_in_progress); return 0; } /********************************************************************/ /* PCMCIA stuff */ /********************************************************************/ static int orinoco_cs_probe(struct pcmcia_device *link) { struct orinoco_private *priv; struct orinoco_pccard *card; priv = alloc_orinocodev(sizeof(*card), &link->dev, orinoco_cs_hard_reset, NULL); if (!priv) return -ENOMEM; card = priv->card; /* Link both structures together */ card->p_dev = link; link->priv = priv; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.Handler = orinoco_interrupt; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; #endif return orinoco_cs_config(link); } /* orinoco_cs_attach */ static void orinoco_cs_detach(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; orinoco_if_del(priv); orinoco_cs_release(link); free_orinocodev(priv); } /* orinoco_cs_detach */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int orinoco_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { if (p_dev->config_index == 0) return -EINVAL; return pcmcia_request_io(p_dev); }; #else static int orinoco_cs_config_check(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cfg, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { if (cfg->index == 0) goto next_entry; /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) { DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", __func__, vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000); if (!ignore_cis_vcc) goto next_entry; } } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) { if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) { DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", __func__, vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000); if (!ignore_cis_vcc) goto next_entry; } } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) p_dev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM)) p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) p_dev->resource[0]->end = p_dev->resource[1]->end = 0; #else p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; #endif if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= pcmcia_io_cfg_data_width(io->flags); p_dev->resource[0]->start = io->win[0].base; p_dev->resource[0]->end = io->win[0].len; #else p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; if (!(io->flags & CISTPL_IO_8BIT)) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16; if (!(io->flags & CISTPL_IO_16BIT)) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; p_dev->io.BasePort1 = io->win[0].base; p_dev->io.NumPorts1 = io->win[0].len; #endif if (io->nwin > 1) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) p_dev->resource[1]->flags = p_dev->resource[0]->flags; p_dev->resource[1]->start = io->win[1].base; p_dev->resource[1]->end = io->win[1].len; #else p_dev->io.Attributes2 = p_dev->io.Attributes1; p_dev->io.BasePort2 = io->win[1].base; p_dev->io.NumPorts2 = io->win[1].len; #endif } /* This reserves IO space but doesn't actually enable it */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) if (pcmcia_request_io(p_dev) != 0) #else if (pcmcia_request_io(p_dev, &p_dev->io) != 0) #endif goto next_entry; } return 0; next_entry: pcmcia_disable_device(p_dev); return -ENODEV; }; #endif static int orinoco_cs_config(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; struct hermes *hw = &priv->hw; int ret; void __iomem *mem; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; if (ignore_cis_vcc) link->config_flags &= ~CONF_AUTO_CHECK_VCC; #endif ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL); if (ret) { if (!ignore_cis_vcc) printk(KERN_ERR PFX "GetNextTuple(): No matching " "CIS configuration. Maybe you need the " "ignore_cis_vcc=1 parameter.\n"); goto failed; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) mem = ioport_map(link->resource[0]->start, resource_size(link->resource[0])); #else mem = ioport_map(link->io.BasePort1, link->io.NumPorts1); #endif if (!mem) goto failed; /* We initialize the hermes structure before completing PCMCIA * configuration just in case the interrupt handler gets * called. */ hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = pcmcia_request_irq(link, orinoco_interrupt); #else ret = pcmcia_request_irq(link, &link->irq); #endif if (ret) goto failed; ret = pcmcia_enable_device(link); if (ret) goto failed; /* Initialise the main driver */ if (orinoco_init(priv) != 0) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto failed; } /* Register an interface with the stack */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) if (orinoco_if_add(priv, link->resource[0]->start, link->irq, NULL) != 0) { #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (orinoco_if_add(priv, link->io.BasePort1, link->irq, NULL) != 0) { #else if (orinoco_if_add(priv, link->io.BasePort1, link->irq.AssignedIRQ, NULL) != 0) { #endif printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } return 0; failed: orinoco_cs_release(link); return -ENODEV; } /* orinoco_cs_config */ static void orinoco_cs_release(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; unsigned long flags; /* We're committed to taking the device away now, so mark the * hardware as unavailable */ priv->hw.ops->lock_irqsave(&priv->lock, &flags); priv->hw_unavailable++; priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); pcmcia_disable_device(link); if (priv->hw.iobase) ioport_unmap(priv->hw.iobase); } /* orinoco_cs_release */ static int orinoco_cs_suspend(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; struct orinoco_pccard *card = priv->card; /* This is probably racy, but I can't think of a better way, short of rewriting the PCMCIA layer to not suck :-( */ if (!test_bit(0, &card->hard_reset_in_progress)) orinoco_down(priv); return 0; } static int orinoco_cs_resume(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; struct orinoco_pccard *card = priv->card; int err = 0; if (!test_bit(0, &card->hard_reset_in_progress)) err = orinoco_up(priv); return err; } /********************************************************************/ /* Module initialization */ /********************************************************************/ static const struct pcmcia_device_id orinoco_cs_ids[] = { PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */ PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */ PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */ PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), /* ARtem Onair */ PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0003), /* ARtem Onair Comcard 11 */ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), /* Buffalo WLI-PCM-S11 */ PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), /* ASUS SpaceLink WL-100 */ PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), /* SpeedStream SS1021 Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x3021), /* SpeedStream Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), /* PLANEX RoadLannerWave GW-NS11H */ PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f), PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e), PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842), PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169), PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb), PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916), PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3), PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c), PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077), PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92), PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a), PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410), PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3), PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a), PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767), PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed), PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9), PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26), PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b), PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e), PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.01", 0xd27deb1a), /* Lucent Orinoco */ #ifdef CONFIG_HERMES_PRISM /* Only entries that certainly identify Prism chipset */ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */ PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */ PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */ PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */ PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */ PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* Airvast WN-100 */ PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), /* Adaptec Ultra Wireless ANW-8030 */ PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), /* CONTEC FLEXSCAN/FX-DDS110-PCC */ PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), /* Conceptronic CON11Cpro, EMTAC A2424i */ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), /* Safeway 802.11b, ZCOMAX AirRunner/XI-300 */ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), /* D-Link DCF660, Sandisk Connect SDWCFB-000 */ PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0), PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5), PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2), PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18), PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3), PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae), PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146), PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac), PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab), PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9), PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18), PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77), PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf), PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395), PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01), PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1), PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1), PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6), PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264), PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178), PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757), PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a), PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee), PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), /* This may be Agere or Intersil Firmware */ PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), #endif PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids); static struct pcmcia_driver orinoco_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = DRIVER_NAME, #else .drv = { .name = DRIVER_NAME, }, #endif .probe = orinoco_cs_probe, .remove = orinoco_cs_detach, .id_table = orinoco_cs_ids, .suspend = orinoco_cs_suspend, .resume = orinoco_cs_resume, }; static int __init init_orinoco_cs(void) { return pcmcia_register_driver(&orinoco_driver); } static void __exit exit_orinoco_cs(void) { pcmcia_unregister_driver(&orinoco_driver); } module_init(init_orinoco_cs); module_exit(exit_orinoco_cs); compat-drivers-2012-09-18/drivers/net/wireless/orinoco/main.c0000644000175000017500000017700112026211315023227 0ustar mcgrofmcgrof/* main.c - (formerly known as dldwd_cs.c, orinoco_cs.c and orinoco.c) * * A driver for Hermes or Prism 2 chipset based PCMCIA wireless * adaptors, with Lucent/Agere, Intersil or Symbol firmware. * * Current maintainers (as of 29 September 2003) are: * Pavel Roskin * and David Gibson * * (C) Copyright David Gibson, IBM Corporation 2001-2003. * Copyright (C) 2000 David Gibson, Linuxcare Australia. * With some help from : * Copyright (C) 2001 Jean Tourrilhes, HP Labs * Copyright (C) 2001 Benjamin Herrenschmidt * * Based on dummy_cs.c 1.27 2000/06/12 21:27:25 * * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus * http://www.stud.fh-dortmund.de/~andy/wvlan/ * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * The initial developer of the original code is David A. Hinds * . Portions created by David * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights * Reserved. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. */ /* * TODO * o Handle de-encapsulation within network layer, provide 802.11 * headers (patch from Thomas 'Dent' Mirlacher) * o Fix possible races in SPY handling. * o Disconnect wireless extensions from fundamental configuration. * o (maybe) Software WEP support (patch from Stano Meduna). * o (maybe) Use multiple Tx buffers - driver handling queue * rather than firmware. */ /* Locking and synchronization: * * The basic principle is that everything is serialized through a * single spinlock, priv->lock. The lock is used in user, bh and irq * context, so when taken outside hardirq context it should always be * taken with interrupts disabled. The lock protects both the * hardware and the struct orinoco_private. * * Another flag, priv->hw_unavailable indicates that the hardware is * unavailable for an extended period of time (e.g. suspended, or in * the middle of a hard reset). This flag is protected by the * spinlock. All code which touches the hardware should check the * flag after taking the lock, and if it is set, give up on whatever * they are doing and drop the lock again. The orinoco_lock() * function handles this (it unlocks and returns -EBUSY if * hw_unavailable is non-zero). */ #define DRIVER_NAME "orinoco" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hermes_rid.h" #include "hermes_dld.h" #include "hw.h" #include "scan.h" #include "mic.h" #include "fw.h" #include "wext.h" #include "cfg.h" #include "main.h" #include "orinoco.h" /********************************************************************/ /* Module information */ /********************************************************************/ MODULE_AUTHOR("Pavel Roskin & " "David Gibson "); MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based " "and similar wireless cards"); MODULE_LICENSE("Dual MPL/GPL"); /* Level of debugging. Used in the macros in orinoco.h */ #ifdef ORINOCO_DEBUG int orinoco_debug = ORINOCO_DEBUG; EXPORT_SYMBOL(orinoco_debug); module_param(orinoco_debug, int, 0644); MODULE_PARM_DESC(orinoco_debug, "Debug level"); #endif static bool suppress_linkstatus; /* = 0 */ module_param(suppress_linkstatus, bool, 0644); MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes"); static int ignore_disconnect; /* = 0 */ module_param(ignore_disconnect, int, 0644); MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer"); int force_monitor; /* = 0 */ module_param(force_monitor, int, 0644); MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions"); /********************************************************************/ /* Internal constants */ /********************************************************************/ /* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; #define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) #define ORINOCO_MIN_MTU 256 #define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD) #define MAX_IRQLOOPS_PER_IRQ 10 #define MAX_IRQLOOPS_PER_JIFFY (20000 / HZ) /* Based on a guestimate of * how many events the * device could * legitimately generate */ #define DUMMY_FID 0xFFFF /*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \ HERMES_MAX_MULTICAST : 0)*/ #define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST) #define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \ | HERMES_EV_TX | HERMES_EV_TXEXC \ | HERMES_EV_WTERR | HERMES_EV_INFO \ | HERMES_EV_INFDROP) /********************************************************************/ /* Data types */ /********************************************************************/ /* Beginning of the Tx descriptor, used in TxExc handling */ struct hermes_txexc_data { struct hermes_tx_descriptor desc; __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; } __packed; /* Rx frame header except compatibility 802.3 header */ struct hermes_rx_descriptor { /* Control */ __le16 status; __le32 time; u8 silence; u8 signal; u8 rate; u8 rxflow; __le32 reserved; /* 802.11 header */ __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; __le16 seq_ctl; u8 addr4[ETH_ALEN]; /* Data length */ __le16 data_len; } __packed; struct orinoco_rx_data { struct hermes_rx_descriptor *desc; struct sk_buff *skb; struct list_head list; }; struct orinoco_scan_data { void *buf; size_t len; int type; struct list_head list; }; /********************************************************************/ /* Function prototypes */ /********************************************************************/ static int __orinoco_set_multicast_list(struct net_device *dev); static int __orinoco_up(struct orinoco_private *priv); static int __orinoco_down(struct orinoco_private *priv); static int __orinoco_commit(struct orinoco_private *priv); /********************************************************************/ /* Internal helper functions */ /********************************************************************/ void set_port_type(struct orinoco_private *priv) { switch (priv->iw_mode) { case NL80211_IFTYPE_STATION: priv->port_type = 1; priv->createibss = 0; break; case NL80211_IFTYPE_ADHOC: if (priv->prefer_port3) { priv->port_type = 3; priv->createibss = 0; } else { priv->port_type = priv->ibss_port; priv->createibss = 1; } break; case NL80211_IFTYPE_MONITOR: priv->port_type = 3; priv->createibss = 0; break; default: printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", priv->ndev->name); } } /********************************************************************/ /* Device methods */ /********************************************************************/ int orinoco_open(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int err; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = __orinoco_up(priv); if (!err) priv->open = 1; orinoco_unlock(priv, &flags); return err; } EXPORT_SYMBOL(orinoco_open); int orinoco_stop(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); int err = 0; /* We mustn't use orinoco_lock() here, because we need to be able to close the interface even if hw_unavailable is set (e.g. as we're released after a PC Card removal) */ orinoco_lock_irq(priv); priv->open = 0; err = __orinoco_down(priv); orinoco_unlock_irq(priv); return err; } EXPORT_SYMBOL(orinoco_stop); struct net_device_stats *orinoco_get_stats(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); return &priv->stats; } EXPORT_SYMBOL(orinoco_get_stats); void orinoco_set_multicast_list(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) { printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " "called when hw_unavailable\n", dev->name); return; } __orinoco_set_multicast_list(dev); orinoco_unlock(priv, &flags); } EXPORT_SYMBOL(orinoco_set_multicast_list); int orinoco_change_mtu(struct net_device *dev, int new_mtu) { struct orinoco_private *priv = ndev_priv(dev); if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU)) return -EINVAL; /* MTU + encapsulation + header length */ if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) > (priv->nicbuf_size - ETH_HLEN)) return -EINVAL; dev->mtu = new_mtu; return 0; } EXPORT_SYMBOL(orinoco_change_mtu); /********************************************************************/ /* Tx path */ /********************************************************************/ /* Add encapsulation and MIC to the existing SKB. * The main xmit routine will then send the whole lot to the card. * Need 8 bytes headroom * Need 8 bytes tailroom * * With encapsulated ethernet II frame * -------- * 803.3 header (14 bytes) * dst[6] * -------- src[6] * 803.3 header (14 bytes) len[2] * dst[6] 803.2 header (8 bytes) * src[6] encaps[6] * len[2] <- leave alone -> len[2] * -------- -------- <-- 0 * Payload Payload * ... ... * * -------- -------- * MIC (8 bytes) * -------- * * returns 0 on success, -ENOMEM on error. */ int orinoco_process_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct orinoco_private *priv, int *tx_control, u8 *mic_buf) { struct orinoco_tkip_key *key; struct ethhdr *eh; int do_mic; key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && (key != NULL)); if (do_mic) *tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | HERMES_TXCTRL_MIC; eh = (struct ethhdr *)skb->data; /* Encapsulate Ethernet-II frames */ if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ struct header_struct { struct ethhdr eth; /* 802.3 header */ u8 encap[6]; /* 802.2 header */ } __packed hdr; int len = skb->len + sizeof(encaps_hdr) - (2 * ETH_ALEN); if (skb_headroom(skb) < ENCAPS_OVERHEAD) { if (net_ratelimit()) printk(KERN_ERR "%s: Not enough headroom for 802.2 headers %d\n", dev->name, skb_headroom(skb)); return -ENOMEM; } /* Fill in new header */ memcpy(&hdr.eth, eh, 2 * ETH_ALEN); hdr.eth.h_proto = htons(len); memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); /* Make room for the new header, and copy it in */ eh = (struct ethhdr *) skb_push(skb, ENCAPS_OVERHEAD); memcpy(eh, &hdr, sizeof(hdr)); } /* Calculate Michael MIC */ if (do_mic) { size_t len = skb->len - ETH_HLEN; u8 *mic = &mic_buf[0]; /* Have to write to an even address, so copy the spare * byte across */ if (skb->len % 2) { *mic = skb->data[skb->len - 1]; mic++; } orinoco_mic(priv->tx_tfm_mic, key->tx_mic, eh->h_dest, eh->h_source, 0 /* priority */, skb->data + ETH_HLEN, len, mic); } return 0; } EXPORT_SYMBOL(orinoco_process_xmit_skb); static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw; int err = 0; u16 txfid = priv->txfid; int tx_control; unsigned long flags; u8 mic_buf[MICHAEL_MIC_LEN + 1]; if (!netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", dev->name); return NETDEV_TX_BUSY; } if (netif_queue_stopped(dev)) { printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", dev->name); return NETDEV_TX_BUSY; } if (orinoco_lock(priv, &flags) != 0) { printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", dev->name); return NETDEV_TX_BUSY; } if (!netif_carrier_ok(dev) || (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { /* Oops, the firmware hasn't established a connection, silently drop the packet (this seems to be the safest approach). */ goto drop; } /* Check packet length */ if (skb->len < ETH_HLEN) goto drop; tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, &mic_buf[0]); if (err) goto drop; if (priv->has_alt_txcntl) { /* WPA enabled firmwares have tx_cntl at the end of * the 802.11 header. So write zeroed descriptor and * 802.11 header at the same time */ char desc[HERMES_802_3_OFFSET]; __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET]; memset(&desc, 0, sizeof(desc)); *txcntl = cpu_to_le16(tx_control); err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0); if (err) { if (net_ratelimit()) printk(KERN_ERR "%s: Error %d writing Tx " "descriptor to BAP\n", dev->name, err); goto busy; } } else { struct hermes_tx_descriptor desc; memset(&desc, 0, sizeof(desc)); desc.tx_control = cpu_to_le16(tx_control); err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0); if (err) { if (net_ratelimit()) printk(KERN_ERR "%s: Error %d writing Tx " "descriptor to BAP\n", dev->name, err); goto busy; } /* Clear the 802.11 header and data length fields - some * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused * if this isn't done. */ hermes_clear_words(hw, HERMES_DATA0, HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); } err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len, txfid, HERMES_802_3_OFFSET); if (err) { printk(KERN_ERR "%s: Error %d writing packet to BAP\n", dev->name, err); goto busy; } if (tx_control & HERMES_TXCTRL_MIC) { size_t offset = HERMES_802_3_OFFSET + skb->len; size_t len = MICHAEL_MIC_LEN; if (offset % 2) { offset--; len++; } err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len, txfid, offset); if (err) { printk(KERN_ERR "%s: Error %d writing MIC to BAP\n", dev->name, err); goto busy; } } /* Finally, we actually initiate the send */ netif_stop_queue(dev); err = hw->ops->cmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, NULL); if (err) { netif_start_queue(dev); if (net_ratelimit()) printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err); goto busy; } stats->tx_bytes += HERMES_802_3_OFFSET + skb->len; goto ok; drop: stats->tx_errors++; stats->tx_dropped++; ok: orinoco_unlock(priv, &flags); dev_kfree_skb(skb); return NETDEV_TX_OK; busy: if (err == -EIO) schedule_work(&priv->reset_work); orinoco_unlock(priv, &flags); return NETDEV_TX_BUSY; } static void __orinoco_ev_alloc(struct net_device *dev, struct hermes *hw) { struct orinoco_private *priv = ndev_priv(dev); u16 fid = hermes_read_regn(hw, ALLOCFID); if (fid != priv->txfid) { if (fid != DUMMY_FID) printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", dev->name, fid); return; } hermes_write_regn(hw, ALLOCFID, DUMMY_FID); } static void __orinoco_ev_tx(struct net_device *dev, struct hermes *hw) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; stats->tx_packets++; netif_wake_queue(dev); hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } static void __orinoco_ev_txexc(struct net_device *dev, struct hermes *hw) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; u16 fid = hermes_read_regn(hw, TXCOMPLFID); u16 status; struct hermes_txexc_data hdr; int err = 0; if (fid == DUMMY_FID) return; /* Nothing's really happened */ /* Read part of the frame header - we need status and addr1 */ err = hw->ops->bap_pread(hw, IRQ_BAP, &hdr, sizeof(struct hermes_txexc_data), fid, 0); hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); stats->tx_errors++; if (err) { printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " "(FID=%04X error %d)\n", dev->name, fid, err); return; } DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name, err, fid); /* We produce a TXDROP event only for retry or lifetime * exceeded, because that's the only status that really mean * that this particular node went away. * Other errors means that *we* screwed up. - Jean II */ status = le16_to_cpu(hdr.desc.status); if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) { union iwreq_data wrqu; /* Copy 802.11 dest address. * We use the 802.11 header because the frame may * not be 802.3 or may be mangled... * In Ad-Hoc mode, it will be the node address. * In managed mode, it will be most likely the AP addr * User space will figure out how to convert it to * whatever it needs (IP address or else). * - Jean II */ memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN); wrqu.addr.sa_family = ARPHRD_ETHER; /* Send event to user space */ wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); } netif_wake_queue(dev); } void orinoco_tx_timeout(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw; printk(KERN_WARNING "%s: Tx timeout! " "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", dev->name, hermes_read_regn(hw, ALLOCFID), hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); stats->tx_errors++; schedule_work(&priv->reset_work); } EXPORT_SYMBOL(orinoco_tx_timeout); /********************************************************************/ /* Rx path (data frames) */ /********************************************************************/ /* Does the frame have a SNAP header indicating it should be * de-encapsulated to Ethernet-II? */ static inline int is_ethersnap(void *_hdr) { u8 *hdr = _hdr; /* We de-encapsulate all packets which, a) have SNAP headers * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header * and where b) the OUI of the SNAP header is 00:00:00 or * 00:00:f8 - we need both because different APs appear to use * different OUIs for some reason */ return (memcmp(hdr, &encaps_hdr, 5) == 0) && ((hdr[5] == 0x00) || (hdr[5] == 0xf8)); } static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac, int level, int noise) { struct iw_quality wstats; wstats.level = level - 0x95; wstats.noise = noise - 0x95; wstats.qual = (level > noise) ? (level - noise) : 0; wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; /* Update spy records */ wireless_spy_update(dev, mac, &wstats); } static void orinoco_stat_gather(struct net_device *dev, struct sk_buff *skb, struct hermes_rx_descriptor *desc) { struct orinoco_private *priv = ndev_priv(dev); /* Using spy support with lots of Rx packets, like in an * infrastructure (AP), will really slow down everything, because * the MAC address must be compared to each entry of the spy list. * If the user really asks for it (set some address in the * spy list), we do it, but he will pay the price. * Note that to get here, you need both WIRELESS_SPY * compiled in AND some addresses in the list !!! */ /* Note : gcc will optimise the whole section away if * WIRELESS_SPY is not defined... - Jean II */ if (SPY_NUMBER(priv)) { orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN, desc->signal, desc->silence); } } /* * orinoco_rx_monitor - handle received monitor frames. * * Arguments: * dev network device * rxfid received FID * desc rx descriptor of the frame * * Call context: interrupt */ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, struct hermes_rx_descriptor *desc) { u32 hdrlen = 30; /* return full header by default */ u32 datalen = 0; u16 fc; int err; int len; struct sk_buff *skb; struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw; len = le16_to_cpu(desc->data_len); /* Determine the size of the header and the data */ fc = le16_to_cpu(desc->frame_ctl); switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_DATA: if ((fc & IEEE80211_FCTL_TODS) && (fc & IEEE80211_FCTL_FROMDS)) hdrlen = 30; else hdrlen = 24; datalen = len; break; case IEEE80211_FTYPE_MGMT: hdrlen = 24; datalen = len; break; case IEEE80211_FTYPE_CTL: switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PSPOLL: case IEEE80211_STYPE_RTS: case IEEE80211_STYPE_CFEND: case IEEE80211_STYPE_CFENDACK: hdrlen = 16; break; case IEEE80211_STYPE_CTS: case IEEE80211_STYPE_ACK: hdrlen = 10; break; } break; default: /* Unknown frame type */ break; } /* sanity check the length */ if (datalen > IEEE80211_MAX_DATA_LEN + 12) { printk(KERN_DEBUG "%s: oversized monitor frame, " "data length = %d\n", dev->name, datalen); stats->rx_length_errors++; goto update_stats; } skb = dev_alloc_skb(hdrlen + datalen); if (!skb) { printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n", dev->name); goto update_stats; } /* Copy the 802.11 header to the skb */ memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen); skb_reset_mac_header(skb); /* If any, copy the data from the card to the skb */ if (datalen > 0) { err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, datalen), ALIGN(datalen, 2), rxfid, HERMES_802_2_OFFSET); if (err) { printk(KERN_ERR "%s: error %d reading monitor frame\n", dev->name, err); goto drop; } } skb->dev = dev; skb->ip_summed = CHECKSUM_NONE; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = cpu_to_be16(ETH_P_802_2); stats->rx_packets++; stats->rx_bytes += skb->len; netif_rx(skb); return; drop: dev_kfree_skb_irq(skb); update_stats: stats->rx_errors++; stats->rx_dropped++; } void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; u16 rxfid, status; int length; struct hermes_rx_descriptor *desc; struct orinoco_rx_data *rx_data; int err; desc = kmalloc(sizeof(*desc), GFP_ATOMIC); if (!desc) { printk(KERN_WARNING "%s: Can't allocate space for RX descriptor\n", dev->name); goto update_stats; } rxfid = hermes_read_regn(hw, RXFID); err = hw->ops->bap_pread(hw, IRQ_BAP, desc, sizeof(*desc), rxfid, 0); if (err) { printk(KERN_ERR "%s: error %d reading Rx descriptor. " "Frame dropped.\n", dev->name, err); goto update_stats; } status = le16_to_cpu(desc->status); if (status & HERMES_RXSTAT_BADCRC) { DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name); stats->rx_crc_errors++; goto update_stats; } /* Handle frames in monitor mode */ if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { orinoco_rx_monitor(dev, rxfid, desc); goto out; } if (status & HERMES_RXSTAT_UNDECRYPTABLE) { DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", dev->name); wstats->discard.code++; goto update_stats; } length = le16_to_cpu(desc->data_len); /* Sanity checks */ if (length < 3) { /* No for even an 802.2 LLC header */ /* At least on Symbol firmware with PCF we get quite a lot of these legitimately - Poll frames with no data. */ goto out; } if (length > IEEE80211_MAX_DATA_LEN) { printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", dev->name, length); stats->rx_length_errors++; goto update_stats; } /* Payload size does not include Michael MIC. Increase payload * size to read it together with the data. */ if (status & HERMES_RXSTAT_MIC) length += MICHAEL_MIC_LEN; /* We need space for the packet data itself, plus an ethernet header, plus 2 bytes so we can align the IP header on a 32bit boundary, plus 1 byte so we can read in odd length packets from the card, which has an IO granularity of 16 bits */ skb = dev_alloc_skb(length + ETH_HLEN + 2 + 1); if (!skb) { printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", dev->name); goto update_stats; } /* We'll prepend the header, so reserve space for it. The worst case is no decapsulation, when 802.3 header is prepended and nothing is removed. 2 is for aligning the IP header. */ skb_reserve(skb, ETH_HLEN + 2); err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, length), ALIGN(length, 2), rxfid, HERMES_802_2_OFFSET); if (err) { printk(KERN_ERR "%s: error %d reading frame. " "Frame dropped.\n", dev->name, err); goto drop; } /* Add desc and skb to rx queue */ rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC); if (!rx_data) goto drop; rx_data->desc = desc; rx_data->skb = skb; list_add_tail(&rx_data->list, &priv->rx_list); tasklet_schedule(&priv->rx_tasklet); return; drop: dev_kfree_skb_irq(skb); update_stats: stats->rx_errors++; stats->rx_dropped++; out: kfree(desc); } EXPORT_SYMBOL(__orinoco_ev_rx); static void orinoco_rx(struct net_device *dev, struct hermes_rx_descriptor *desc, struct sk_buff *skb) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; u16 status, fc; int length; struct ethhdr *hdr; status = le16_to_cpu(desc->status); length = le16_to_cpu(desc->data_len); fc = le16_to_cpu(desc->frame_ctl); /* Calculate and check MIC */ if (status & HERMES_RXSTAT_MIC) { struct orinoco_tkip_key *key; int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >> HERMES_MIC_KEY_ID_SHIFT); u8 mic[MICHAEL_MIC_LEN]; u8 *rxmic; u8 *src = (fc & IEEE80211_FCTL_FROMDS) ? desc->addr3 : desc->addr2; /* Extract Michael MIC from payload */ rxmic = skb->data + skb->len - MICHAEL_MIC_LEN; skb_trim(skb, skb->len - MICHAEL_MIC_LEN); length -= MICHAEL_MIC_LEN; key = (struct orinoco_tkip_key *) priv->keys[key_id].key; if (!key) { printk(KERN_WARNING "%s: Received encrypted frame from " "%pM using key %i, but key is not installed\n", dev->name, src, key_id); goto drop; } orinoco_mic(priv->rx_tfm_mic, key->rx_mic, desc->addr1, src, 0, /* priority or QoS? */ skb->data, skb->len, &mic[0]); if (memcmp(mic, rxmic, MICHAEL_MIC_LEN)) { union iwreq_data wrqu; struct iw_michaelmicfailure wxmic; printk(KERN_WARNING "%s: " "Invalid Michael MIC in data frame from %pM, " "using key %i\n", dev->name, src, key_id); /* TODO: update stats */ /* Notify userspace */ memset(&wxmic, 0, sizeof(wxmic)); wxmic.flags = key_id & IW_MICFAILURE_KEY_ID; wxmic.flags |= (desc->addr1[0] & 1) ? IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE; wxmic.src_addr.sa_family = ARPHRD_ETHER; memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN); (void) orinoco_hw_get_tkip_iv(priv, key_id, &wxmic.tsc[0]); memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = sizeof(wxmic); wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *) &wxmic); goto drop; } } /* Handle decapsulation * In most cases, the firmware tell us about SNAP frames. * For some reason, the SNAP frames sent by LinkSys APs * are not properly recognised by most firmwares. * So, check ourselves */ if (length >= ENCAPS_OVERHEAD && (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || is_ethersnap(skb->data))) { /* These indicate a SNAP within 802.2 LLC within 802.11 frame which we'll need to de-encapsulate to the original EthernetII frame. */ hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD); } else { /* 802.3 frame - prepend 802.3 header as is */ hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN); hdr->h_proto = htons(length); } memcpy(hdr->h_dest, desc->addr1, ETH_ALEN); if (fc & IEEE80211_FCTL_FROMDS) memcpy(hdr->h_source, desc->addr3, ETH_ALEN); else memcpy(hdr->h_source, desc->addr2, ETH_ALEN); skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; if (fc & IEEE80211_FCTL_TODS) skb->pkt_type = PACKET_OTHERHOST; /* Process the wireless stats if needed */ orinoco_stat_gather(dev, skb, desc); /* Pass the packet to the networking stack */ netif_rx(skb); stats->rx_packets++; stats->rx_bytes += length; return; drop: dev_kfree_skb(skb); stats->rx_errors++; stats->rx_dropped++; } static void orinoco_rx_isr_tasklet(unsigned long data) { struct orinoco_private *priv = (struct orinoco_private *) data; struct net_device *dev = priv->ndev; struct orinoco_rx_data *rx_data, *temp; struct hermes_rx_descriptor *desc; struct sk_buff *skb; unsigned long flags; /* orinoco_rx requires the driver lock, and we also need to * protect priv->rx_list, so just hold the lock over the * lot. * * If orinoco_lock fails, we've unplugged the card. In this * case just abort. */ if (orinoco_lock(priv, &flags) != 0) return; /* extract desc and skb from queue */ list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { desc = rx_data->desc; skb = rx_data->skb; list_del(&rx_data->list); kfree(rx_data); orinoco_rx(dev, desc, skb); kfree(desc); } orinoco_unlock(priv, &flags); } /********************************************************************/ /* Rx path (info frames) */ /********************************************************************/ static void print_linkstatus(struct net_device *dev, u16 status) { char *s; if (suppress_linkstatus) return; switch (status) { case HERMES_LINKSTATUS_NOT_CONNECTED: s = "Not Connected"; break; case HERMES_LINKSTATUS_CONNECTED: s = "Connected"; break; case HERMES_LINKSTATUS_DISCONNECTED: s = "Disconnected"; break; case HERMES_LINKSTATUS_AP_CHANGE: s = "AP Changed"; break; case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: s = "AP Out of Range"; break; case HERMES_LINKSTATUS_AP_IN_RANGE: s = "AP In Range"; break; case HERMES_LINKSTATUS_ASSOC_FAILED: s = "Association Failed"; break; default: s = "UNKNOWN"; } printk(KERN_DEBUG "%s: New link status: %s (%04x)\n", dev->name, s, status); } /* Search scan results for requested BSSID, join it if found */ static void orinoco_join_ap(struct work_struct *work) { struct orinoco_private *priv = container_of(work, struct orinoco_private, join_work); struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; unsigned long flags; struct join_req { u8 bssid[ETH_ALEN]; __le16 channel; } __packed req; const int atom_len = offsetof(struct prism2_scan_apinfo, atim); struct prism2_scan_apinfo *atom = NULL; int offset = 4; int found = 0; u8 *buf; u16 len; /* Allocate buffer for scan results */ buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL); if (!buf) return; if (orinoco_lock(priv, &flags) != 0) goto fail_lock; /* Sanity checks in case user changed something in the meantime */ if (!priv->bssid_fixed) goto out; if (strlen(priv->desired_essid) == 0) goto out; /* Read scan results from the firmware */ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SCANRESULTSTABLE, MAX_SCAN_LEN, &len, buf); if (err) { printk(KERN_ERR "%s: Cannot read scan results\n", dev->name); goto out; } len = HERMES_RECLEN_TO_BYTES(len); /* Go through the scan results looking for the channel of the AP * we were requested to join */ for (; offset + atom_len <= len; offset += atom_len) { atom = (struct prism2_scan_apinfo *) (buf + offset); if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) { found = 1; break; } } if (!found) { DEBUG(1, "%s: Requested AP not found in scan results\n", dev->name); goto out; } memcpy(req.bssid, priv->desired_bssid, ETH_ALEN); req.channel = atom->channel; /* both are little-endian */ err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST, &req); if (err) printk(KERN_ERR "%s: Error issuing join request\n", dev->name); out: orinoco_unlock(priv, &flags); fail_lock: kfree(buf); } /* Send new BSSID to userspace */ static void orinoco_send_bssid_wevent(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; union iwreq_data wrqu; int err; err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, ETH_ALEN, NULL, wrqu.ap_addr.sa_data); if (err != 0) return; wrqu.ap_addr.sa_family = ARPHRD_ETHER; /* Send event to user space */ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; union iwreq_data wrqu; int err; u8 buf[88]; u8 *ie; if (!priv->has_wpa) return; err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO, sizeof(buf), NULL, &buf); if (err != 0) return; ie = orinoco_get_wpa_ie(buf, sizeof(buf)); if (ie) { int rem = sizeof(buf) - (ie - &buf[0]); wrqu.data.length = ie[1] + 2; if (wrqu.data.length > rem) wrqu.data.length = rem; if (wrqu.data.length) /* Send event to user space */ wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie); } } static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; union iwreq_data wrqu; int err; u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */ u8 *ie; if (!priv->has_wpa) return; err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO, sizeof(buf), NULL, &buf); if (err != 0) return; ie = orinoco_get_wpa_ie(buf, sizeof(buf)); if (ie) { int rem = sizeof(buf) - (ie - &buf[0]); wrqu.data.length = ie[1] + 2; if (wrqu.data.length > rem) wrqu.data.length = rem; if (wrqu.data.length) /* Send event to user space */ wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie); } } static void orinoco_send_wevents(struct work_struct *work) { struct orinoco_private *priv = container_of(work, struct orinoco_private, wevent_work); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return; orinoco_send_assocreqie_wevent(priv); orinoco_send_assocrespie_wevent(priv); orinoco_send_bssid_wevent(priv); orinoco_unlock(priv, &flags); } static void qbuf_scan(struct orinoco_private *priv, void *buf, int len, int type) { struct orinoco_scan_data *sd; unsigned long flags; sd = kmalloc(sizeof(*sd), GFP_ATOMIC); if (!sd) { printk(KERN_ERR "%s: failed to alloc memory\n", __func__); return; } sd->buf = buf; sd->len = len; sd->type = type; spin_lock_irqsave(&priv->scan_lock, flags); list_add_tail(&sd->list, &priv->scan_list); spin_unlock_irqrestore(&priv->scan_lock, flags); schedule_work(&priv->process_scan); } static void qabort_scan(struct orinoco_private *priv) { struct orinoco_scan_data *sd; unsigned long flags; sd = kmalloc(sizeof(*sd), GFP_ATOMIC); if (!sd) { printk(KERN_ERR "%s: failed to alloc memory\n", __func__); return; } sd->len = -1; /* Abort */ spin_lock_irqsave(&priv->scan_lock, flags); list_add_tail(&sd->list, &priv->scan_list); spin_unlock_irqrestore(&priv->scan_lock, flags); schedule_work(&priv->process_scan); } static void orinoco_process_scan_results(struct work_struct *work) { struct orinoco_private *priv = container_of(work, struct orinoco_private, process_scan); struct orinoco_scan_data *sd, *temp; unsigned long flags; void *buf; int len; int type; spin_lock_irqsave(&priv->scan_lock, flags); list_for_each_entry_safe(sd, temp, &priv->scan_list, list) { buf = sd->buf; len = sd->len; type = sd->type; list_del(&sd->list); spin_unlock_irqrestore(&priv->scan_lock, flags); kfree(sd); if (len > 0) { if (type == HERMES_INQ_CHANNELINFO) orinoco_add_extscan_result(priv, buf, len); else orinoco_add_hostscan_results(priv, buf, len); kfree(buf); } else { /* Either abort or complete the scan */ orinoco_scan_done(priv, (len < 0)); } spin_lock_irqsave(&priv->scan_lock, flags); } spin_unlock_irqrestore(&priv->scan_lock, flags); } void __orinoco_ev_info(struct net_device *dev, struct hermes *hw) { struct orinoco_private *priv = ndev_priv(dev); u16 infofid; struct { __le16 len; __le16 type; } __packed info; int len, type; int err; /* This is an answer to an INQUIRE command that we did earlier, * or an information "event" generated by the card * The controller return to us a pseudo frame containing * the information in question - Jean II */ infofid = hermes_read_regn(hw, INFOFID); /* Read the info frame header - don't try too hard */ err = hw->ops->bap_pread(hw, IRQ_BAP, &info, sizeof(info), infofid, 0); if (err) { printk(KERN_ERR "%s: error %d reading info frame. " "Frame dropped.\n", dev->name, err); return; } len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len)); type = le16_to_cpu(info.type); switch (type) { case HERMES_INQ_TALLIES: { struct hermes_tallies_frame tallies; struct iw_statistics *wstats = &priv->wstats; if (len > sizeof(tallies)) { printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n", dev->name, len); len = sizeof(tallies); } err = hw->ops->bap_pread(hw, IRQ_BAP, &tallies, len, infofid, sizeof(info)); if (err) break; /* Increment our various counters */ /* wstats->discard.nwid - no wrong BSSID stuff */ wstats->discard.code += le16_to_cpu(tallies.RxWEPUndecryptable); if (len == sizeof(tallies)) wstats->discard.code += le16_to_cpu(tallies.RxDiscards_WEPICVError) + le16_to_cpu(tallies.RxDiscards_WEPExcluded); wstats->discard.misc += le16_to_cpu(tallies.TxDiscardsWrongSA); wstats->discard.fragment += le16_to_cpu(tallies.RxMsgInBadMsgFragments); wstats->discard.retries += le16_to_cpu(tallies.TxRetryLimitExceeded); /* wstats->miss.beacon - no match */ } break; case HERMES_INQ_LINKSTATUS: { struct hermes_linkstatus linkstatus; u16 newstatus; int connected; if (priv->iw_mode == NL80211_IFTYPE_MONITOR) break; if (len != sizeof(linkstatus)) { printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", dev->name, len); break; } err = hw->ops->bap_pread(hw, IRQ_BAP, &linkstatus, len, infofid, sizeof(info)); if (err) break; newstatus = le16_to_cpu(linkstatus.linkstatus); /* Symbol firmware uses "out of range" to signal that * the hostscan frame can be requested. */ if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && priv->firmware_type == FIRMWARE_TYPE_SYMBOL && priv->has_hostscan && priv->scan_request) { hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); break; } connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); if (connected) netif_carrier_on(dev); else if (!ignore_disconnect) netif_carrier_off(dev); if (newstatus != priv->last_linkstatus) { priv->last_linkstatus = newstatus; print_linkstatus(dev, newstatus); /* The info frame contains only one word which is the * status (see hermes.h). The status is pretty boring * in itself, that's why we export the new BSSID... * Jean II */ schedule_work(&priv->wevent_work); } } break; case HERMES_INQ_SCAN: if (!priv->scan_request && priv->bssid_fixed && priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { schedule_work(&priv->join_work); break; } /* fall through */ case HERMES_INQ_HOSTSCAN: case HERMES_INQ_HOSTSCAN_SYMBOL: { /* Result of a scanning. Contains information about * cells in the vicinity - Jean II */ unsigned char *buf; /* Sanity check */ if (len > 4096) { printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", dev->name, len); qabort_scan(priv); break; } /* Allocate buffer for results */ buf = kmalloc(len, GFP_ATOMIC); if (buf == NULL) { /* No memory, so can't printk()... */ qabort_scan(priv); break; } /* Read scan data */ err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) buf, len, infofid, sizeof(info)); if (err) { kfree(buf); qabort_scan(priv); break; } #ifdef ORINOCO_DEBUG { int i; printk(KERN_DEBUG "Scan result [%02X", buf[0]); for (i = 1; i < (len * 2); i++) printk(":%02X", buf[i]); printk("]\n"); } #endif /* ORINOCO_DEBUG */ qbuf_scan(priv, buf, len, type); } break; case HERMES_INQ_CHANNELINFO: { struct agere_ext_scan_info *bss; if (!priv->scan_request) { printk(KERN_DEBUG "%s: Got chaninfo without scan, " "len=%d\n", dev->name, len); break; } /* An empty result indicates that the scan is complete */ if (len == 0) { qbuf_scan(priv, NULL, len, type); break; } /* Sanity check */ else if (len < (offsetof(struct agere_ext_scan_info, data) + 2)) { /* Drop this result now so we don't have to * keep checking later */ printk(KERN_WARNING "%s: Ext scan results too short (%d bytes)\n", dev->name, len); break; } bss = kmalloc(len, GFP_ATOMIC); if (bss == NULL) break; /* Read scan data */ err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) bss, len, infofid, sizeof(info)); if (err) kfree(bss); else qbuf_scan(priv, bss, len, type); break; } case HERMES_INQ_SEC_STAT_AGERE: /* Security status (Agere specific) */ /* Ignore this frame for now */ if (priv->firmware_type == FIRMWARE_TYPE_AGERE) break; /* fall through */ default: printk(KERN_DEBUG "%s: Unknown information frame received: " "type 0x%04x, length %d\n", dev->name, type, len); /* We don't actually do anything about it */ break; } } EXPORT_SYMBOL(__orinoco_ev_info); static void __orinoco_ev_infdrop(struct net_device *dev, struct hermes *hw) { if (net_ratelimit()) printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name); } /********************************************************************/ /* Internal hardware control routines */ /********************************************************************/ static int __orinoco_up(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; netif_carrier_off(dev); /* just to make sure */ err = __orinoco_commit(priv); if (err) { printk(KERN_ERR "%s: Error %d configuring card\n", dev->name, err); return err; } /* Fire things up again */ hermes_set_irqmask(hw, ORINOCO_INTEN); err = hermes_enable_port(hw, 0); if (err) { printk(KERN_ERR "%s: Error %d enabling MAC port\n", dev->name, err); return err; } netif_start_queue(dev); return 0; } static int __orinoco_down(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; netif_stop_queue(dev); if (!priv->hw_unavailable) { if (!priv->broken_disableport) { err = hermes_disable_port(hw, 0); if (err) { /* Some firmwares (e.g. Intersil 1.3.x) seem * to have problems disabling the port, oh * well, too bad. */ printk(KERN_WARNING "%s: Error %d disabling MAC port\n", dev->name, err); priv->broken_disableport = 1; } } hermes_set_irqmask(hw, 0); hermes_write_regn(hw, EVACK, 0xffff); } orinoco_scan_done(priv, true); /* firmware will have to reassociate */ netif_carrier_off(dev); priv->last_linkstatus = 0xffff; return 0; } static int orinoco_reinit_firmware(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; int err; err = hw->ops->init(hw); if (priv->do_fw_download && !err) { err = orinoco_download(priv); if (err) priv->do_fw_download = 0; } if (!err) err = orinoco_hw_allocate_fid(priv); return err; } static int __orinoco_set_multicast_list(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); int err = 0; int promisc, mc_count; /* The Hermes doesn't seem to have an allmulti mode, so we go * into promiscuous mode and let the upper levels deal. */ if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > MAX_MULTICAST(priv))) { promisc = 1; mc_count = 0; } else { promisc = 0; mc_count = netdev_mc_count(dev); } err = __orinoco_hw_set_multicast_list(priv, dev, mc_count, promisc); return err; } /* This must be called from user context, without locks held - use * schedule_work() */ void orinoco_reset(struct work_struct *work) { struct orinoco_private *priv = container_of(work, struct orinoco_private, reset_work); struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) /* When the hardware becomes available again, whatever * detects that is responsible for re-initializing * it. So no need for anything further */ return; netif_stop_queue(dev); /* Shut off interrupts. Depending on what state the hardware * is in, this might not work, but we'll try anyway */ hermes_set_irqmask(hw, 0); hermes_write_regn(hw, EVACK, 0xffff); priv->hw_unavailable++; priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ netif_carrier_off(dev); orinoco_unlock(priv, &flags); /* Scanning support: Notify scan cancellation */ orinoco_scan_done(priv, true); if (priv->hard_reset) { err = (*priv->hard_reset)(priv); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d " "performing hard reset\n", dev->name, err); goto disable; } } err = orinoco_reinit_firmware(priv); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", dev->name, err); goto disable; } /* This has to be called from user context */ orinoco_lock_irq(priv); priv->hw_unavailable--; /* priv->open or priv->hw_unavailable might have changed while * we dropped the lock */ if (priv->open && (!priv->hw_unavailable)) { err = __orinoco_up(priv); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", dev->name, err); } else dev->trans_start = jiffies; } orinoco_unlock_irq(priv); return; disable: hermes_set_irqmask(hw, 0); netif_device_detach(dev); printk(KERN_ERR "%s: Device has been disabled!\n", dev->name); } static int __orinoco_commit(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; int err = 0; /* If we've called commit, we are reconfiguring or bringing the * interface up. Maintaining countermeasures across this would * be confusing, so note that we've disabled them. The port will * be enabled later in orinoco_commit or __orinoco_up. */ priv->tkip_cm_active = 0; err = orinoco_hw_program_rids(priv); /* FIXME: what about netif_tx_lock */ (void) __orinoco_set_multicast_list(dev); return err; } /* Ensures configuration changes are applied. May result in a reset. * The caller should hold priv->lock */ int orinoco_commit(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; if (priv->broken_disableport) { schedule_work(&priv->reset_work); return 0; } err = hermes_disable_port(hw, 0); if (err) { printk(KERN_WARNING "%s: Unable to disable port " "while reconfiguring card\n", dev->name); priv->broken_disableport = 1; goto out; } err = __orinoco_commit(priv); if (err) { printk(KERN_WARNING "%s: Unable to reconfigure card\n", dev->name); goto out; } err = hermes_enable_port(hw, 0); if (err) { printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", dev->name); goto out; } out: if (err) { printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); schedule_work(&priv->reset_work); err = 0; } return err; } /********************************************************************/ /* Interrupt handler */ /********************************************************************/ static void __orinoco_ev_tick(struct net_device *dev, struct hermes *hw) { printk(KERN_DEBUG "%s: TICK\n", dev->name); } static void __orinoco_ev_wterr(struct net_device *dev, struct hermes *hw) { /* This seems to happen a fair bit under load, but ignoring it seems to work fine...*/ printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", dev->name); } irqreturn_t orinoco_interrupt(int irq, void *dev_id) { struct orinoco_private *priv = dev_id; struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int count = MAX_IRQLOOPS_PER_IRQ; u16 evstat, events; /* These are used to detect a runaway interrupt situation. * * If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy, * we panic and shut down the hardware */ /* jiffies value the last time we were called */ static int last_irq_jiffy; /* = 0 */ static int loops_this_jiffy; /* = 0 */ unsigned long flags; if (orinoco_lock(priv, &flags) != 0) { /* If hw is unavailable - we don't know if the irq was * for us or not */ return IRQ_HANDLED; } evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; if (!events) { orinoco_unlock(priv, &flags); return IRQ_NONE; } if (jiffies != last_irq_jiffy) loops_this_jiffy = 0; last_irq_jiffy = jiffies; while (events && count--) { if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { printk(KERN_WARNING "%s: IRQ handler is looping too " "much! Resetting.\n", dev->name); /* Disable interrupts for now */ hermes_set_irqmask(hw, 0); schedule_work(&priv->reset_work); break; } /* Check the card hasn't been removed */ if (!hermes_present(hw)) { DEBUG(0, "orinoco_interrupt(): card removed\n"); break; } if (events & HERMES_EV_TICK) __orinoco_ev_tick(dev, hw); if (events & HERMES_EV_WTERR) __orinoco_ev_wterr(dev, hw); if (events & HERMES_EV_INFDROP) __orinoco_ev_infdrop(dev, hw); if (events & HERMES_EV_INFO) __orinoco_ev_info(dev, hw); if (events & HERMES_EV_RX) __orinoco_ev_rx(dev, hw); if (events & HERMES_EV_TXEXC) __orinoco_ev_txexc(dev, hw); if (events & HERMES_EV_TX) __orinoco_ev_tx(dev, hw); if (events & HERMES_EV_ALLOC) __orinoco_ev_alloc(dev, hw); hermes_write_regn(hw, EVACK, evstat); evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; } orinoco_unlock(priv, &flags); return IRQ_HANDLED; } EXPORT_SYMBOL(orinoco_interrupt); /********************************************************************/ /* Power management */ /********************************************************************/ #if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT) static int orinoco_pm_notifier(struct notifier_block *notifier, unsigned long pm_event, void *unused) { struct orinoco_private *priv = container_of(notifier, struct orinoco_private, pm_notifier); /* All we need to do is cache the firmware before suspend, and * release it when we come out. * * Only need to do this if we're downloading firmware. */ if (!priv->do_fw_download) return NOTIFY_DONE; switch (pm_event) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: orinoco_cache_fw(priv, 0); break; case PM_POST_RESTORE: /* Restore from hibernation failed. We need to clean * up in exactly the same way, so fall through. */ case PM_POST_HIBERNATION: case PM_POST_SUSPEND: orinoco_uncache_fw(priv); break; case PM_RESTORE_PREPARE: default: break; } return NOTIFY_DONE; } static void orinoco_register_pm_notifier(struct orinoco_private *priv) { priv->pm_notifier.notifier_call = orinoco_pm_notifier; register_pm_notifier(&priv->pm_notifier); } static void orinoco_unregister_pm_notifier(struct orinoco_private *priv) { unregister_pm_notifier(&priv->pm_notifier); } #else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */ #define orinoco_register_pm_notifier(priv) do { } while (0) #define orinoco_unregister_pm_notifier(priv) do { } while (0) #endif /********************************************************************/ /* Initialization */ /********************************************************************/ int orinoco_init(struct orinoco_private *priv) { struct device *dev = priv->dev; struct wiphy *wiphy = priv_to_wiphy(priv); struct hermes *hw = &priv->hw; int err = 0; /* No need to lock, the hw_unavailable flag is already set in * alloc_orinocodev() */ priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN; /* Initialize the firmware */ err = hw->ops->init(hw); if (err != 0) { dev_err(dev, "Failed to initialize firmware (err = %d)\n", err); goto out; } err = determine_fw_capabilities(priv, wiphy->fw_version, sizeof(wiphy->fw_version), &wiphy->hw_version); if (err != 0) { dev_err(dev, "Incompatible firmware, aborting\n"); goto out; } if (priv->do_fw_download) { #ifdef CONFIG_HERMES_CACHE_FW_ON_INIT orinoco_cache_fw(priv, 0); #endif err = orinoco_download(priv); if (err) priv->do_fw_download = 0; /* Check firmware version again */ err = determine_fw_capabilities(priv, wiphy->fw_version, sizeof(wiphy->fw_version), &wiphy->hw_version); if (err != 0) { dev_err(dev, "Incompatible firmware, aborting\n"); goto out; } } if (priv->has_port3) dev_info(dev, "Ad-hoc demo mode supported\n"); if (priv->has_ibss) dev_info(dev, "IEEE standard IBSS ad-hoc mode supported\n"); if (priv->has_wep) dev_info(dev, "WEP supported, %s-bit key\n", priv->has_big_wep ? "104" : "40"); if (priv->has_wpa) { dev_info(dev, "WPA-PSK supported\n"); if (orinoco_mic_init(priv)) { dev_err(dev, "Failed to setup MIC crypto algorithm. " "Disabling WPA support\n"); priv->has_wpa = 0; } } err = orinoco_hw_read_card_settings(priv, wiphy->perm_addr); if (err) goto out; err = orinoco_hw_allocate_fid(priv); if (err) { dev_err(dev, "Failed to allocate NIC buffer!\n"); goto out; } /* Set up the default configuration */ priv->iw_mode = NL80211_IFTYPE_STATION; /* By default use IEEE/IBSS ad-hoc mode if we have it */ priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss); set_port_type(priv); priv->channel = 0; /* use firmware default */ priv->promiscuous = 0; priv->encode_alg = ORINOCO_ALG_NONE; priv->tx_key = 0; priv->wpa_enabled = 0; priv->tkip_cm_active = 0; priv->key_mgmt = 0; priv->wpa_ie_len = 0; priv->wpa_ie = NULL; if (orinoco_wiphy_register(wiphy)) { err = -ENODEV; goto out; } /* Make the hardware available, as long as it hasn't been * removed elsewhere (e.g. by PCMCIA hot unplug) */ orinoco_lock_irq(priv); priv->hw_unavailable--; orinoco_unlock_irq(priv); dev_dbg(dev, "Ready\n"); out: return err; } EXPORT_SYMBOL(orinoco_init); static const struct net_device_ops orinoco_netdev_ops = { .ndo_open = orinoco_open, .ndo_stop = orinoco_stop, .ndo_start_xmit = orinoco_xmit, .ndo_set_rx_mode = orinoco_set_multicast_list, .ndo_change_mtu = orinoco_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = orinoco_tx_timeout, .ndo_get_stats = orinoco_get_stats, }; /* Allocate private data. * * This driver has a number of structures associated with it * netdev - Net device structure for each network interface * wiphy - structure associated with wireless phy * wireless_dev (wdev) - structure for each wireless interface * hw - structure for hermes chip info * card - card specific structure for use by the card driver * (airport, orinoco_cs) * priv - orinoco private data * device - generic linux device structure * * +---------+ +---------+ * | wiphy | | netdev | * | +-------+ | +-------+ * | | priv | | | wdev | * | | +-----+ +-+-------+ * | | | hw | * | +-+-----+ * | | card | * +-+-------+ * * priv has a link to netdev and device * wdev has a link to wiphy */ struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device, int (*hard_reset)(struct orinoco_private *), int (*stop_fw)(struct orinoco_private *, int)) { struct orinoco_private *priv; struct wiphy *wiphy; /* allocate wiphy * NOTE: We only support a single virtual interface * but this may change when monitor mode is added */ wiphy = wiphy_new(&orinoco_cfg_ops, sizeof(struct orinoco_private) + sizeof_card); if (!wiphy) return NULL; priv = wiphy_priv(wiphy); priv->dev = device; if (sizeof_card) priv->card = (void *)((unsigned long)priv + sizeof(struct orinoco_private)); else priv->card = NULL; orinoco_wiphy_init(wiphy); #ifdef WIRELESS_SPY priv->wireless_data.spy_data = &priv->spy_data; #endif /* Set up default callbacks */ priv->hard_reset = hard_reset; priv->stop_fw = stop_fw; spin_lock_init(&priv->lock); priv->open = 0; priv->hw_unavailable = 1; /* orinoco_init() must clear this * before anything else touches the * hardware */ INIT_WORK(&priv->reset_work, orinoco_reset); INIT_WORK(&priv->join_work, orinoco_join_ap); INIT_WORK(&priv->wevent_work, orinoco_send_wevents); INIT_LIST_HEAD(&priv->rx_list); tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet, (unsigned long) priv); spin_lock_init(&priv->scan_lock); INIT_LIST_HEAD(&priv->scan_list); INIT_WORK(&priv->process_scan, orinoco_process_scan_results); priv->last_linkstatus = 0xffff; #if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) priv->cached_pri_fw = NULL; priv->cached_fw = NULL; #endif /* Register PM notifiers */ orinoco_register_pm_notifier(priv); return priv; } EXPORT_SYMBOL(alloc_orinocodev); /* We can only support a single interface. We provide a separate * function to set it up to distinguish between hardware * initialisation and interface setup. * * The base_addr and irq parameters are passed on to netdev for use * with SIOCGIFMAP. */ int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, unsigned int irq, const struct net_device_ops *ops) { struct wiphy *wiphy = priv_to_wiphy(priv); struct wireless_dev *wdev; struct net_device *dev; int ret; dev = alloc_etherdev(sizeof(struct wireless_dev)); if (!dev) return -ENOMEM; /* Initialise wireless_dev */ wdev = netdev_priv(dev); wdev->wiphy = wiphy; wdev->iftype = NL80211_IFTYPE_STATION; /* Setup / override net_device fields */ dev->ieee80211_ptr = wdev; dev->watchdog_timeo = HZ; /* 1 second timeout */ dev->wireless_handlers = &orinoco_handler_def; #ifdef WIRELESS_SPY dev->wireless_data = &priv->wireless_data; #endif /* Default to standard ops if not set */ if (ops) netdev_attach_ops(dev, ops); else netdev_attach_ops(dev, &orinoco_netdev_ops); /* we use the default eth_mac_addr for setting the MAC addr */ /* Reserve space in skb for the SNAP header */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) dev->needed_headroom = ENCAPS_OVERHEAD; #else dev->hard_header_len += ENCAPS_OVERHEAD; #endif netif_carrier_off(dev); memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); memcpy(dev->perm_addr, wiphy->perm_addr, ETH_ALEN); dev->base_addr = base_addr; dev->irq = irq; SET_NETDEV_DEV(dev, priv->dev); ret = register_netdev(dev); if (ret) goto fail; priv->ndev = dev; /* Report what we've done */ dev_dbg(priv->dev, "Registerred interface %s.\n", dev->name); return 0; fail: free_netdev(dev); return ret; } EXPORT_SYMBOL(orinoco_if_add); void orinoco_if_del(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; unregister_netdev(dev); free_netdev(dev); } EXPORT_SYMBOL(orinoco_if_del); void free_orinocodev(struct orinoco_private *priv) { struct wiphy *wiphy = priv_to_wiphy(priv); struct orinoco_rx_data *rx_data, *temp; struct orinoco_scan_data *sd, *sdtemp; wiphy_unregister(wiphy); /* If the tasklet is scheduled when we call tasklet_kill it * will run one final time. However the tasklet will only * drain priv->rx_list if the hw is still available. */ tasklet_kill(&priv->rx_tasklet); /* Explicitly drain priv->rx_list */ list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { list_del(&rx_data->list); dev_kfree_skb(rx_data->skb); kfree(rx_data->desc); kfree(rx_data); } cancel_work_sync(&priv->process_scan); /* Explicitly drain priv->scan_list */ list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) { list_del(&sd->list); if ((sd->len > 0) && sd->buf) kfree(sd->buf); kfree(sd); } orinoco_unregister_pm_notifier(priv); orinoco_uncache_fw(priv); priv->wpa_ie_len = 0; kfree(priv->wpa_ie); orinoco_mic_free(priv); wiphy_free(wiphy); } EXPORT_SYMBOL(free_orinocodev); int orinoco_up(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; unsigned long flags; int err; priv->hw.ops->lock_irqsave(&priv->lock, &flags); err = orinoco_reinit_firmware(priv); if (err) { printk(KERN_ERR "%s: Error %d re-initializing firmware\n", dev->name, err); goto exit; } netif_device_attach(dev); priv->hw_unavailable--; if (priv->open && !priv->hw_unavailable) { err = __orinoco_up(priv); if (err) printk(KERN_ERR "%s: Error %d restarting card\n", dev->name, err); } exit: priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); return 0; } EXPORT_SYMBOL(orinoco_up); void orinoco_down(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; unsigned long flags; int err; priv->hw.ops->lock_irqsave(&priv->lock, &flags); err = __orinoco_down(priv); if (err) printk(KERN_WARNING "%s: Error %d downing interface\n", dev->name, err); netif_device_detach(dev); priv->hw_unavailable++; priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); } EXPORT_SYMBOL(orinoco_down); /********************************************************************/ /* Module initialization */ /********************************************************************/ /* Can't be declared "const" or the whole __initdata section will * become const */ static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION " (David Gibson , " "Pavel Roskin , et al)"; static int __init init_orinoco(void) { printk(KERN_DEBUG "%s\n", version); return 0; } static void __exit exit_orinoco(void) { } module_init(init_orinoco); module_exit(exit_orinoco); compat-drivers-2012-09-18/drivers/net/wireless/orinoco/wext.c0000644000175000017500000010450312026211315023267 0ustar mcgrofmcgrof/* Wireless extensions support. * * See copyright notice in main.c */ #include #include #include #include #include #include #include #include #include #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" #include "hw.h" #include "mic.h" #include "scan.h" #include "main.h" #include "wext.h" #define MAX_RID_LEN 1024 /* Helper routine to record keys * It is called under orinoco_lock so it may not sleep */ static int orinoco_set_key(struct orinoco_private *priv, int index, enum orinoco_alg alg, const u8 *key, int key_len, const u8 *seq, int seq_len) { #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) int len; if (!unlikely(ZERO_OR_NULL_PTR(priv->keys[index].key))) { len = priv->keys[index].key_len; memset(priv->keys[index].key, 0, len); kfree(priv->keys[index].key); } if (!unlikely(ZERO_OR_NULL_PTR(priv->keys[index].seq))) { len = priv->keys[index].seq_len; memset(priv->keys[index].seq, 0, len); kfree(priv->keys[index].seq); } #else kzfree(priv->keys[index].key); kzfree(priv->keys[index].seq); #endif if (key_len) { priv->keys[index].key = kzalloc(key_len, GFP_ATOMIC); if (!priv->keys[index].key) goto nomem; } else priv->keys[index].key = NULL; if (seq_len) { priv->keys[index].seq = kzalloc(seq_len, GFP_ATOMIC); if (!priv->keys[index].seq) goto free_key; } else priv->keys[index].seq = NULL; priv->keys[index].key_len = key_len; priv->keys[index].seq_len = seq_len; if (key_len) memcpy(priv->keys[index].key, key, key_len); if (seq_len) memcpy(priv->keys[index].seq, seq, seq_len); switch (alg) { case ORINOCO_ALG_TKIP: priv->keys[index].cipher = WLAN_CIPHER_SUITE_TKIP; break; case ORINOCO_ALG_WEP: priv->keys[index].cipher = (key_len > SMALL_KEY_SIZE) ? WLAN_CIPHER_SUITE_WEP104 : WLAN_CIPHER_SUITE_WEP40; break; case ORINOCO_ALG_NONE: default: priv->keys[index].cipher = 0; break; } return 0; free_key: kfree(priv->keys[index].key); priv->keys[index].key = NULL; nomem: priv->keys[index].key_len = 0; priv->keys[index].seq_len = 0; priv->keys[index].cipher = 0; return -ENOMEM; } static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; struct iw_statistics *wstats = &priv->wstats; int err; unsigned long flags; if (!netif_device_present(dev)) { printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", dev->name); return NULL; /* FIXME: Can we do better than this? */ } /* If busy, return the old stats. Returning NULL may cause * the interface to disappear from /proc/net/wireless */ if (orinoco_lock(priv, &flags) != 0) return wstats; /* We can't really wait for the tallies inquiry command to * complete, so we just use the previous results and trigger * a new tallies inquiry command for next time - Jean II */ /* FIXME: Really we should wait for the inquiry to come back - * as it is the stats we give don't make a whole lot of sense. * Unfortunately, it's not clear how to do that within the * wireless extensions framework: I think we're in user * context, but a lock seems to be held by the time we get in * here so we're not safe to sleep here. */ hermes_inquire(hw, HERMES_INQ_TALLIES); if (priv->iw_mode == NL80211_IFTYPE_ADHOC) { memset(&wstats->qual, 0, sizeof(wstats->qual)); /* If a spy address is defined, we report stats of the * first spy address - Jean II */ if (SPY_NUMBER(priv)) { wstats->qual.qual = priv->spy_data.spy_stat[0].qual; wstats->qual.level = priv->spy_data.spy_stat[0].level; wstats->qual.noise = priv->spy_data.spy_stat[0].noise; wstats->qual.updated = priv->spy_data.spy_stat[0].updated; } } else { struct { __le16 qual, signal, noise, unused; } __packed cq; err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_COMMSQUALITY, &cq); if (!err) { wstats->qual.qual = (int)le16_to_cpu(cq.qual); wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; } } orinoco_unlock(priv, &flags); return wstats; } /********************************************************************/ /* Wireless extensions */ /********************************************************************/ static int orinoco_ioctl_setwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* Enable automatic roaming - no sanity checks are needed */ if (is_zero_ether_addr(ap_addr->sa_data) || is_broadcast_ether_addr(ap_addr->sa_data)) { priv->bssid_fixed = 0; memset(priv->desired_bssid, 0, ETH_ALEN); /* "off" means keep existing connection */ if (ap_addr->sa_data[0] == 0) { __orinoco_hw_set_wap(priv); err = 0; } goto out; } if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " "support manual roaming\n", dev->name); err = -EOPNOTSUPP; goto out; } if (priv->iw_mode != NL80211_IFTYPE_STATION) { printk(KERN_WARNING "%s: Manual roaming supported only in " "managed mode\n", dev->name); err = -EOPNOTSUPP; goto out; } /* Intersil firmware hangs without Desired ESSID */ if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && strlen(priv->desired_essid) == 0) { printk(KERN_WARNING "%s: Desired ESSID must be set for " "manual roaming\n", dev->name); err = -EOPNOTSUPP; goto out; } /* Finally, enable manual roaming */ priv->bssid_fixed = 1; memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int err = 0; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; ap_addr->sa_family = ARPHRD_ETHER; err = orinoco_hw_get_current_bssid(priv, ap_addr->sa_data); orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct orinoco_private *priv = ndev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; int setindex = priv->tx_key; enum orinoco_alg encode_alg = priv->encode_alg; int restricted = priv->wep_restrict; int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (!priv->has_wep) return -EOPNOTSUPP; if (erq->pointer) { /* We actually have a key to set - check its length */ if (erq->length > LARGE_KEY_SIZE) return -E2BIG; if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep) return -E2BIG; } if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* Clear any TKIP key we have */ if ((priv->has_wpa) && (priv->encode_alg == ORINOCO_ALG_TKIP)) (void) orinoco_clear_tkip_key(priv, setindex); if (erq->length > 0) { if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; /* Switch on WEP if off */ if (encode_alg != ORINOCO_ALG_WEP) { setindex = index; encode_alg = ORINOCO_ALG_WEP; } } else { /* Important note : if the user do "iwconfig eth0 enc off", * we will arrive there with an index of -1. This is valid * but need to be taken care off... Jean II */ if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { if ((index != -1) || (erq->flags == 0)) { err = -EINVAL; goto out; } } else { /* Set the index : Check that the key is valid */ if (priv->keys[index].key_len == 0) { err = -EINVAL; goto out; } setindex = index; } } if (erq->flags & IW_ENCODE_DISABLED) encode_alg = ORINOCO_ALG_NONE; if (erq->flags & IW_ENCODE_OPEN) restricted = 0; if (erq->flags & IW_ENCODE_RESTRICTED) restricted = 1; if (erq->pointer && erq->length > 0) { err = orinoco_set_key(priv, index, ORINOCO_ALG_WEP, keybuf, erq->length, NULL, 0); } priv->tx_key = setindex; /* Try fast key change if connected and only keys are changed */ if ((priv->encode_alg == encode_alg) && (priv->wep_restrict == restricted) && netif_carrier_ok(dev)) { err = __orinoco_hw_setup_wepkeys(priv); /* No need to commit if successful */ goto out; } priv->encode_alg = encode_alg; priv->wep_restrict = restricted; out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct orinoco_private *priv = ndev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; unsigned long flags; if (!priv->has_wep) return -EOPNOTSUPP; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; erq->flags = 0; if (!priv->encode_alg) erq->flags |= IW_ENCODE_DISABLED; erq->flags |= index + 1; if (priv->wep_restrict) erq->flags |= IW_ENCODE_RESTRICTED; else erq->flags |= IW_ENCODE_OPEN; erq->length = priv->keys[index].key_len; memcpy(keybuf, priv->keys[index].key, erq->length); orinoco_unlock(priv, &flags); return 0; } static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *essidbuf) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it * anyway... - Jean II */ /* Hum... Should not use Wireless Extension constant (may change), * should use our own... - Jean II */ if (erq->length > IW_ESSID_MAX_SIZE) return -E2BIG; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); /* If not ANY, get the new ESSID */ if (erq->flags) memcpy(priv->desired_essid, essidbuf, erq->length); orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *essidbuf) { struct orinoco_private *priv = ndev_priv(dev); int active; int err = 0; unsigned long flags; if (netif_running(dev)) { err = orinoco_hw_get_essid(priv, &active, essidbuf); if (err < 0) return err; erq->length = err; } else { if (orinoco_lock(priv, &flags) != 0) return -EBUSY; memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); erq->length = strlen(priv->desired_essid); orinoco_unlock(priv, &flags); } erq->flags = 1; return 0; } static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *frq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int chan = -1; unsigned long flags; int err = -EINPROGRESS; /* Call commit handler */ /* In infrastructure mode the AP sets the channel */ if (priv->iw_mode == NL80211_IFTYPE_STATION) return -EBUSY; if ((frq->e == 0) && (frq->m <= 1000)) { /* Setting by channel number */ chan = frq->m; } else { /* Setting by frequency */ int denom = 1; int i; /* Calculate denominator to rescale to MHz */ for (i = 0; i < (6 - frq->e); i++) denom *= 10; chan = ieee80211_freq_to_dsss_chan(frq->m / denom); } if ((chan < 1) || (chan > NUM_CHANNELS) || !(priv->channel_mask & (1 << (chan - 1)))) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->channel = chan; if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Fast channel change - no commit if successful */ struct hermes *hw = &priv->hw; err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_SET_CHANNEL, chan, NULL); } orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *frq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int tmp; /* Locking done in there */ tmp = orinoco_hw_get_freq(priv); if (tmp < 0) return tmp; frq->m = tmp * 100000; frq->e = 1; return 0; } static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *srq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; u16 val; int err; unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val); orinoco_unlock(priv, &flags); if (err) return err; srq->value = val; srq->fixed = 0; /* auto */ return 0; } static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *srq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int val = srq->value; unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; if ((val < 1) || (val > 3)) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->ap_density = val; orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int ratemode; int bitrate; /* 100s of kilobits */ unsigned long flags; /* As the user space doesn't know our highest rate, it uses -1 * to ask us to set the highest rate. Test it using "iwconfig * ethX rate auto" - Jean II */ if (rrq->value == -1) bitrate = 110; else { if (rrq->value % 100000) return -EINVAL; bitrate = rrq->value / 100000; } ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed); if (ratemode == -1) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->bitratemode = ratemode; orinoco_unlock(priv, &flags); return -EINPROGRESS; } static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int err = 0; int bitrate, automatic; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic); /* If the interface is running we try to find more about the current mode */ if (netif_running(dev)) { int act_bitrate; int lerr; /* Ignore errors if we can't get the actual bitrate */ lerr = orinoco_hw_get_act_bitrate(priv, &act_bitrate); if (!lerr) bitrate = act_bitrate; } orinoco_unlock(priv, &flags); rrq->value = bitrate; rrq->fixed = !automatic; rrq->disabled = 0; return err; } static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *prq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (prq->disabled) { priv->pm_on = 0; } else { switch (prq->flags & IW_POWER_MODE) { case IW_POWER_UNICAST_R: priv->pm_mcast = 0; priv->pm_on = 1; break; case IW_POWER_ALL_R: priv->pm_mcast = 1; priv->pm_on = 1; break; case IW_POWER_ON: /* No flags : but we may have a value - Jean II */ break; default: err = -EINVAL; goto out; } if (prq->flags & IW_POWER_TIMEOUT) { priv->pm_on = 1; priv->pm_timeout = prq->value / 1000; } if (prq->flags & IW_POWER_PERIOD) { priv->pm_on = 1; priv->pm_period = prq->value / 1000; } /* It's valid to not have a value if we are just toggling * the flags... Jean II */ if (!priv->pm_on) { err = -EINVAL; goto out; } } out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *prq, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; int err = 0; u16 enable, period, timeout, mcast; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable); if (err) goto out; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMAXSLEEPDURATION, &period); if (err) goto out; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, &timeout); if (err) goto out; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, &mcast); if (err) goto out; prq->disabled = !enable; /* Note : by default, display the period */ if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { prq->flags = IW_POWER_TIMEOUT; prq->value = timeout * 1000; } else { prq->flags = IW_POWER_PERIOD; prq->value = period * 1000; } if (mcast) prq->flags |= IW_POWER_ALL_R; else prq->flags |= IW_POWER_UNICAST_R; out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_set_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int idx, alg = ext->alg, set_key = 1; unsigned long flags; int err = -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* Determine and validate the key index */ idx = encoding->flags & IW_ENCODE_INDEX; if (idx) { if ((idx < 1) || (idx > 4)) goto out; idx--; } else idx = priv->tx_key; if (encoding->flags & IW_ENCODE_DISABLED) alg = IW_ENCODE_ALG_NONE; if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { /* Clear any TKIP TX key we had */ (void) orinoco_clear_tkip_key(priv, priv->tx_key); } if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { priv->tx_key = idx; set_key = ((alg == IW_ENCODE_ALG_TKIP) || (ext->key_len > 0)) ? 1 : 0; } if (set_key) { /* Set the requested key first */ switch (alg) { case IW_ENCODE_ALG_NONE: priv->encode_alg = ORINOCO_ALG_NONE; err = orinoco_set_key(priv, idx, ORINOCO_ALG_NONE, NULL, 0, NULL, 0); break; case IW_ENCODE_ALG_WEP: if (ext->key_len <= 0) goto out; priv->encode_alg = ORINOCO_ALG_WEP; err = orinoco_set_key(priv, idx, ORINOCO_ALG_WEP, ext->key, ext->key_len, NULL, 0); break; case IW_ENCODE_ALG_TKIP: { u8 *tkip_iv = NULL; if (!priv->has_wpa || (ext->key_len > sizeof(struct orinoco_tkip_key))) goto out; priv->encode_alg = ORINOCO_ALG_TKIP; if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) tkip_iv = &ext->rx_seq[0]; err = orinoco_set_key(priv, idx, ORINOCO_ALG_TKIP, ext->key, ext->key_len, tkip_iv, ORINOCO_SEQ_LEN); err = __orinoco_hw_set_tkip_key(priv, idx, ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, priv->keys[idx].key, tkip_iv, ORINOCO_SEQ_LEN, NULL, 0); if (err) printk(KERN_ERR "%s: Error %d setting TKIP key" "\n", dev->name, err); goto out; } default: goto out; } } err = -EINPROGRESS; out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_get_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int idx, max_key_len; unsigned long flags; int err; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = -EINVAL; max_key_len = encoding->length - sizeof(*ext); if (max_key_len < 0) goto out; idx = encoding->flags & IW_ENCODE_INDEX; if (idx) { if ((idx < 1) || (idx > 4)) goto out; idx--; } else idx = priv->tx_key; encoding->flags = idx + 1; memset(ext, 0, sizeof(*ext)); switch (priv->encode_alg) { case ORINOCO_ALG_NONE: ext->alg = IW_ENCODE_ALG_NONE; ext->key_len = 0; encoding->flags |= IW_ENCODE_DISABLED; break; case ORINOCO_ALG_WEP: ext->alg = IW_ENCODE_ALG_WEP; ext->key_len = min(priv->keys[idx].key_len, max_key_len); memcpy(ext->key, priv->keys[idx].key, ext->key_len); encoding->flags |= IW_ENCODE_ENABLED; break; case ORINOCO_ALG_TKIP: ext->alg = IW_ENCODE_ALG_TKIP; ext->key_len = min(priv->keys[idx].key_len, max_key_len); memcpy(ext->key, priv->keys[idx].key, ext->key_len); encoding->flags |= IW_ENCODE_ENABLED; break; } err = 0; out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_set_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; struct iw_param *param = &wrqu->param; unsigned long flags; int ret = -EINPROGRESS; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: case IW_AUTH_CIPHER_PAIRWISE: case IW_AUTH_CIPHER_GROUP: case IW_AUTH_RX_UNENCRYPTED_EAPOL: case IW_AUTH_PRIVACY_INVOKED: case IW_AUTH_DROP_UNENCRYPTED: /* * orinoco does not use these parameters */ break; case IW_AUTH_MFP: /* Management Frame Protection not supported. * Only fail if set to required. */ if (param->value == IW_AUTH_MFP_REQUIRED) ret = -EINVAL; break; case IW_AUTH_KEY_MGMT: /* wl_lkm implies value 2 == PSK for Hermes I * which ties in with WEXT * no other hints tho :( */ priv->key_mgmt = param->value; break; case IW_AUTH_TKIP_COUNTERMEASURES: /* When countermeasures are enabled, shut down the * card; when disabled, re-enable the card. This must * take effect immediately. * * TODO: Make sure that the EAPOL message is getting * out before card disabled */ if (param->value) { priv->tkip_cm_active = 1; ret = hermes_disable_port(hw, 0); } else { priv->tkip_cm_active = 0; ret = hermes_enable_port(hw, 0); } break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) priv->wep_restrict = 1; else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) priv->wep_restrict = 0; else ret = -EINVAL; break; case IW_AUTH_WPA_ENABLED: if (priv->has_wpa) { priv->wpa_enabled = param->value ? 1 : 0; } else { if (param->value) ret = -EOPNOTSUPP; /* else silently accept disable of WPA */ priv->wpa_enabled = 0; } break; default: ret = -EOPNOTSUPP; } orinoco_unlock(priv, &flags); return ret; } static int orinoco_ioctl_get_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct iw_param *param = &wrqu->param; unsigned long flags; int ret = 0; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_KEY_MGMT: param->value = priv->key_mgmt; break; case IW_AUTH_TKIP_COUNTERMEASURES: param->value = priv->tkip_cm_active; break; case IW_AUTH_80211_AUTH_ALG: if (priv->wep_restrict) param->value = IW_AUTH_ALG_SHARED_KEY; else param->value = IW_AUTH_ALG_OPEN_SYSTEM; break; case IW_AUTH_WPA_ENABLED: param->value = priv->wpa_enabled; break; default: ret = -EOPNOTSUPP; } orinoco_unlock(priv, &flags); return ret; } static int orinoco_ioctl_set_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); u8 *buf; unsigned long flags; /* cut off at IEEE80211_MAX_DATA_LEN */ if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || (wrqu->data.length && (extra == NULL))) return -EINVAL; if (wrqu->data.length) { buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); if (buf == NULL) return -ENOMEM; } else buf = NULL; if (orinoco_lock(priv, &flags) != 0) { kfree(buf); return -EBUSY; } kfree(priv->wpa_ie); priv->wpa_ie = buf; priv->wpa_ie_len = wrqu->data.length; if (priv->wpa_ie) { /* Looks like wl_lkm wants to check the auth alg, and * somehow pass it to the firmware. * Instead it just calls the key mgmt rid * - we do this in set auth. */ } orinoco_unlock(priv, &flags); return 0; } static int orinoco_ioctl_get_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int err = 0; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { wrqu->data.length = 0; goto out; } if (wrqu->data.length < priv->wpa_ie_len) { err = -E2BIG; goto out; } wrqu->data.length = priv->wpa_ie_len; memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct iw_mlme *mlme = (struct iw_mlme *)extra; unsigned long flags; int ret = 0; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; switch (mlme->cmd) { case IW_MLME_DEAUTH: /* silently ignore */ break; case IW_MLME_DISASSOC: ret = orinoco_hw_disassociate(priv, mlme->addr.sa_data, mlme->reason_code); break; default: ret = -EOPNOTSUPP; } orinoco_unlock(priv, &flags); return ret; } static int orinoco_ioctl_reset(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); if (!capable(CAP_NET_ADMIN)) return -EPERM; if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); /* Firmware reset */ orinoco_reset(&priv->reset_work); } else { printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); schedule_work(&priv->reset_work); } return 0; } static int orinoco_ioctl_setibssport(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int val = *((int *) extra); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->ibss_port = val; /* Actually update the mode we are using */ set_port_type(priv); orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_getibssport(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int *val = (int *) extra; *val = priv->ibss_port; return 0; } static int orinoco_ioctl_setport3(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int val = *((int *) extra); int err = 0; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; switch (val) { case 0: /* Try to do IEEE ad-hoc mode */ if (!priv->has_ibss) { err = -EINVAL; break; } priv->prefer_port3 = 0; break; case 1: /* Try to do Lucent proprietary ad-hoc mode */ if (!priv->has_port3) { err = -EINVAL; break; } priv->prefer_port3 = 1; break; default: err = -EINVAL; } if (!err) { /* Actually update the mode we are using */ set_port_type(priv); err = -EINPROGRESS; } orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getport3(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int *val = (int *) extra; *val = priv->prefer_port3; return 0; } static int orinoco_ioctl_setpreamble(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int val; if (!priv->has_preamble) return -EOPNOTSUPP; /* 802.11b has recently defined some short preamble. * Basically, the Phy header has been reduced in size. * This increase performance, especially at high rates * (the preamble is transmitted at 1Mb/s), unfortunately * this give compatibility troubles... - Jean II */ val = *((int *) extra); if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (val) priv->preamble = 1; else priv->preamble = 0; orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_getpreamble(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); int *val = (int *) extra; if (!priv->has_preamble) return -EOPNOTSUPP; *val = priv->preamble; return 0; } /* ioctl interface to hermes_read_ltv() * To use with iwpriv, pass the RID as the token argument, e.g. * iwpriv get_rid [0xfc00] * At least Wireless Tools 25 is required to use iwpriv. * For Wireless Tools 25 and 26 append "dummy" are the end. */ static int orinoco_ioctl_getrid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { struct orinoco_private *priv = ndev_priv(dev); struct hermes *hw = &priv->hw; int rid = data->flags; u16 length; int err; unsigned long flags; /* It's a "get" function, but we don't want users to access the * WEP key and other raw firmware data */ if (!capable(CAP_NET_ADMIN)) return -EPERM; if (rid < 0xfc00 || rid > 0xffff) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, extra); if (err) goto out; data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), MAX_RID_LEN); out: orinoco_unlock(priv, &flags); return err; } /* Commit handler, called after set operations */ static int orinoco_ioctl_commit(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int err = 0; if (!priv->open) return 0; if (orinoco_lock(priv, &flags) != 0) return err; err = orinoco_commit(priv); orinoco_unlock(priv, &flags); return err; } static const struct iw_priv_args orinoco_privtab[] = { { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_port3" }, { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_port3" }, { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble" }, { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_preamble" }, { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_ibssport" }, { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ibssport" }, { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, "get_rid" }, }; /* * Structures to export the Wireless Handlers */ static const iw_handler orinoco_handler[] = { IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit), IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq), IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq), IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens), IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens), IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap), IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap), IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid), IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid), IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate), IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate), IW_HANDLER(SIOCSIWRTS, (iw_handler)cfg80211_wext_siwrts), IW_HANDLER(SIOCGIWRTS, (iw_handler)cfg80211_wext_giwrts), IW_HANDLER(SIOCSIWFRAG, (iw_handler)cfg80211_wext_siwfrag), IW_HANDLER(SIOCGIWFRAG, (iw_handler)cfg80211_wext_giwfrag), IW_HANDLER(SIOCGIWRETRY, (iw_handler)cfg80211_wext_giwretry), IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode), IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode), IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower), IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), }; /* Added typecasting since we no longer use iwreq_data -- Moustafa */ static const iw_handler orinoco_private_handler[] = { [0] = (iw_handler)orinoco_ioctl_reset, [1] = (iw_handler)orinoco_ioctl_reset, [2] = (iw_handler)orinoco_ioctl_setport3, [3] = (iw_handler)orinoco_ioctl_getport3, [4] = (iw_handler)orinoco_ioctl_setpreamble, [5] = (iw_handler)orinoco_ioctl_getpreamble, [6] = (iw_handler)orinoco_ioctl_setibssport, [7] = (iw_handler)orinoco_ioctl_getibssport, [9] = (iw_handler)orinoco_ioctl_getrid, }; const struct iw_handler_def orinoco_handler_def = { .num_standard = ARRAY_SIZE(orinoco_handler), .num_private = ARRAY_SIZE(orinoco_private_handler), .num_private_args = ARRAY_SIZE(orinoco_privtab), .standard = orinoco_handler, .private = orinoco_private_handler, .private_args = orinoco_privtab, .get_wireless_stats = orinoco_get_wireless_stats, }; compat-drivers-2012-09-18/drivers/net/wireless/orinoco/wext.h0000644000175000017500000000043712026211315023275 0ustar mcgrofmcgrof/* Wireless extensions support. * * See copyright notice in main.c */ #ifndef _ORINOCO_WEXT_H_ #define _ORINOCO_WEXT_H_ #include /* Structure defining all our WEXT handlers */ extern const struct iw_handler_def orinoco_handler_def; #endif /* _ORINOCO_WEXT_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/scan.h0000644000175000017500000000105112026211315023223 0ustar mcgrofmcgrof/* Helpers for managing scan queues * * See copyright notice in main.c */ #ifndef _ORINOCO_SCAN_H_ #define _ORINOCO_SCAN_H_ /* Forward declarations */ struct orinoco_private; struct agere_ext_scan_info; /* Add scan results */ void orinoco_add_extscan_result(struct orinoco_private *priv, struct agere_ext_scan_info *atom, size_t len); void orinoco_add_hostscan_results(struct orinoco_private *dev, unsigned char *buf, size_t len); void orinoco_scan_done(struct orinoco_private *priv, bool abort); #endif /* _ORINOCO_SCAN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/scan.c0000644000175000017500000001337512026211315023232 0ustar mcgrofmcgrof/* Helpers for managing scan queues * * See copyright notice in main.c */ #include #include #include #include #include #include "hermes.h" #include "orinoco.h" #include "main.h" #include "scan.h" #define ZERO_DBM_OFFSET 0x95 #define MAX_SIGNAL_LEVEL 0x8A #define MIN_SIGNAL_LEVEL 0x2F #define SIGNAL_TO_DBM(x) \ (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \ - ZERO_DBM_OFFSET) #define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100) static int symbol_build_supp_rates(u8 *buf, const __le16 *rates) { int i; u8 rate; buf[0] = WLAN_EID_SUPP_RATES; for (i = 0; i < 5; i++) { rate = le16_to_cpu(rates[i]); /* NULL terminated */ if (rate == 0x0) break; buf[i + 2] = rate; } buf[1] = i; return i + 2; } static int prism_build_supp_rates(u8 *buf, const u8 *rates) { int i; buf[0] = WLAN_EID_SUPP_RATES; for (i = 0; i < 8; i++) { /* NULL terminated */ if (rates[i] == 0x0) break; buf[i + 2] = rates[i]; } buf[1] = i; /* We might still have another 2 rates, which need to go in * extended supported rates */ if (i == 8 && rates[i] > 0) { buf[10] = WLAN_EID_EXT_SUPP_RATES; for (; i < 10; i++) { /* NULL terminated */ if (rates[i] == 0x0) break; buf[i + 2] = rates[i]; } buf[11] = i - 8; } return (i < 8) ? i + 2 : i + 4; } static void orinoco_add_hostscan_result(struct orinoco_private *priv, const union hermes_scan_info *bss) { struct wiphy *wiphy = priv_to_wiphy(priv); struct ieee80211_channel *channel; struct cfg80211_bss *cbss; u8 *ie; u8 ie_buf[46]; u64 timestamp; s32 signal; u16 capability; u16 beacon_interval; int ie_len; int freq; int len; len = le16_to_cpu(bss->a.essid_len); /* Reconstruct SSID and bitrate IEs to pass up */ ie_buf[0] = WLAN_EID_SSID; ie_buf[1] = len; memcpy(&ie_buf[2], bss->a.essid, len); ie = ie_buf + len + 2; ie_len = ie_buf[1] + 2; switch (priv->firmware_type) { case FIRMWARE_TYPE_SYMBOL: ie_len += symbol_build_supp_rates(ie, bss->s.rates); break; case FIRMWARE_TYPE_INTERSIL: ie_len += prism_build_supp_rates(ie, bss->p.rates); break; case FIRMWARE_TYPE_AGERE: default: break; } freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel)); channel = ieee80211_get_channel(wiphy, freq); if (!channel) { printk(KERN_DEBUG "Invalid channel designation %04X(%04X)", bss->a.channel, freq); return; /* Then ignore it for now */ } timestamp = 0; capability = le16_to_cpu(bss->a.capabilities); beacon_interval = le16_to_cpu(bss->a.beacon_interv); signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); cbss = cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, capability, beacon_interval, ie_buf, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(cbss); } void orinoco_add_extscan_result(struct orinoco_private *priv, struct agere_ext_scan_info *bss, size_t len) { struct wiphy *wiphy = priv_to_wiphy(priv); struct ieee80211_channel *channel; struct cfg80211_bss *cbss; const u8 *ie; u64 timestamp; s32 signal; u16 capability; u16 beacon_interval; size_t ie_len; int chan, freq; ie_len = len - sizeof(*bss); ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); chan = ie ? ie[2] : 0; freq = ieee80211_dsss_chan_to_freq(chan); channel = ieee80211_get_channel(wiphy, freq); timestamp = le64_to_cpu(bss->timestamp); capability = le16_to_cpu(bss->capabilities); beacon_interval = le16_to_cpu(bss->beacon_interval); ie = bss->data; signal = SIGNAL_TO_MBM(bss->level); cbss = cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, capability, beacon_interval, ie, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(cbss); } void orinoco_add_hostscan_results(struct orinoco_private *priv, unsigned char *buf, size_t len) { int offset; /* In the scan data */ size_t atom_len; bool abort = false; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: atom_len = sizeof(struct agere_scan_apinfo); offset = 0; break; case FIRMWARE_TYPE_SYMBOL: /* Lack of documentation necessitates this hack. * Different firmwares have 68 or 76 byte long atoms. * We try modulo first. If the length divides by both, * we check what would be the channel in the second * frame for a 68-byte atom. 76-byte atoms have 0 there. * Valid channel cannot be 0. */ if (len % 76) atom_len = 68; else if (len % 68) atom_len = 76; else if (len >= 1292 && buf[68] == 0) atom_len = 76; else atom_len = 68; offset = 0; break; case FIRMWARE_TYPE_INTERSIL: offset = 4; if (priv->has_hostscan) { atom_len = le16_to_cpup((__le16 *)buf); /* Sanity check for atom_len */ if (atom_len < sizeof(struct prism2_scan_apinfo)) { printk(KERN_ERR "%s: Invalid atom_len in scan " "data: %zu\n", priv->ndev->name, atom_len); abort = true; goto scan_abort; } } else atom_len = offsetof(struct prism2_scan_apinfo, atim); break; default: abort = true; goto scan_abort; } /* Check that we got an whole number of atoms */ if ((len - offset) % atom_len) { printk(KERN_ERR "%s: Unexpected scan data length %zu, " "atom_len %zu, offset %d\n", priv->ndev->name, len, atom_len, offset); abort = true; goto scan_abort; } /* Process the entries one by one */ for (; offset + atom_len <= len; offset += atom_len) { union hermes_scan_info *atom; atom = (union hermes_scan_info *) (buf + offset); orinoco_add_hostscan_result(priv, atom); } scan_abort: if (priv->scan_request) { cfg80211_scan_done(priv->scan_request, abort); priv->scan_request = NULL; } } void orinoco_scan_done(struct orinoco_private *priv, bool abort) { if (priv->scan_request) { cfg80211_scan_done(priv->scan_request, abort); priv->scan_request = NULL; } } compat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco_tmd.c0000644000175000017500000001461512026211315024620 0ustar mcgrofmcgrof/* orinoco_tmd.c * * Driver for Prism II devices which would usually be driven by orinoco_cs, * but are connected to the PCI bus by a TMD7160. * * Copyright (C) 2003 Joerg Dorchain * based heavily upon orinoco_plx.c Copyright (C) 2001 Daniel Barlow * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. * * The actual driving is done by main.c, this is just resource * allocation stuff. * * This driver is modeled after the orinoco_plx driver. The main * difference is that the TMD chip has only IO port ranges and doesn't * provide access to the PCMCIA attribute space. * * Pheecom sells cards with the TMD chip as "ASIC version" */ #define DRIVER_NAME "orinoco_tmd" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include #include "orinoco.h" #include "orinoco_pci.h" #define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ #define COR_RESET (0x80) /* reset bit in the COR register */ #define TMD_RESET_TIME (500) /* milliseconds */ /* * Do a soft reset of the card using the Configuration Option Register */ static int orinoco_tmd_cor_reset(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; struct orinoco_pci_card *card = priv->card; unsigned long timeout; u16 reg; iowrite8(COR_VALUE | COR_RESET, card->bridge_io); mdelay(1); iowrite8(COR_VALUE, card->bridge_io); mdelay(1); /* Just in case, wait more until the card is no longer busy */ timeout = jiffies + (TMD_RESET_TIME * HZ / 1000); reg = hermes_read_regn(hw, CMD); while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { mdelay(1); reg = hermes_read_regn(hw, CMD); } /* Still busy? */ if (reg & HERMES_CMD_BUSY) { printk(KERN_ERR PFX "Busy timeout\n"); return -ETIMEDOUT; } return 0; } static int orinoco_tmd_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int err; struct orinoco_private *priv; struct orinoco_pci_card *card; void __iomem *hermes_io, *bridge_io; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR PFX "Cannot enable PCI device\n"); return err; } err = pci_request_regions(pdev, DRIVER_NAME); if (err) { printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); goto fail_resources; } bridge_io = pci_iomap(pdev, 1, 0); if (!bridge_io) { printk(KERN_ERR PFX "Cannot map bridge registers\n"); err = -EIO; goto fail_map_bridge; } hermes_io = pci_iomap(pdev, 2, 0); if (!hermes_io) { printk(KERN_ERR PFX "Cannot map chipset registers\n"); err = -EIO; goto fail_map_hermes; } /* Allocate network device */ priv = alloc_orinocodev(sizeof(*card), &pdev->dev, orinoco_tmd_cor_reset, NULL); if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } card = priv->card; card->bridge_io = bridge_io; hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; goto fail_irq; } err = orinoco_tmd_cor_reset(priv); if (err) { printk(KERN_ERR PFX "Initial reset failed\n"); goto fail; } err = orinoco_init(priv); if (err) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; } pci_set_drvdata(pdev, priv); return 0; fail: free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); fail_map_hermes: pci_iounmap(pdev, bridge_io); fail_map_bridge: pci_release_regions(pdev); fail_resources: pci_disable_device(pdev); return err; } static void __devexit orinoco_tmd_remove_one(struct pci_dev *pdev) { struct orinoco_private *priv = pci_get_drvdata(pdev); struct orinoco_pci_card *card = priv->card; orinoco_if_del(priv); free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->bridge_io); pci_release_regions(pdev); pci_disable_device(pdev); } static DEFINE_PCI_DEVICE_TABLE(orinoco_tmd_id_table) = { {0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */ {0,}, }; MODULE_DEVICE_TABLE(pci, orinoco_tmd_id_table); static struct pci_driver orinoco_tmd_driver = { .name = DRIVER_NAME, .id_table = orinoco_tmd_id_table, .probe = orinoco_tmd_init_one, .remove = __devexit_p(orinoco_tmd_remove_one), .suspend = orinoco_pci_suspend, .resume = orinoco_pci_resume, }; static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION " (Joerg Dorchain )"; MODULE_AUTHOR("Joerg Dorchain "); MODULE_DESCRIPTION("Driver for wireless LAN cards using the TMD7160 PCI bridge"); MODULE_LICENSE("Dual MPL/GPL"); static int __init orinoco_tmd_init(void) { printk(KERN_DEBUG "%s\n", version); return pci_register_driver(&orinoco_tmd_driver); } static void __exit orinoco_tmd_exit(void) { pci_unregister_driver(&orinoco_tmd_driver); } module_init(orinoco_tmd_init); module_exit(orinoco_tmd_exit); /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco_plx.c0000644000175000017500000002674012026211315024641 0ustar mcgrofmcgrof/* orinoco_plx.c * * Driver for Prism II devices which would usually be driven by orinoco_cs, * but are connected to the PCI bus by a PLX9052. * * Current maintainers are: * Pavel Roskin * and David Gibson * * (C) Copyright David Gibson, IBM Corp. 2001-2003. * Copyright (C) 2001 Daniel Barlow * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. * * Here's the general details on how the PLX9052 adapter works: * * - Two PCI I/O address spaces, one 0x80 long which contains the * PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA * slot I/O address space. * * - One PCI memory address space, mapped to the PCMCIA attribute space * (containing the CIS). * * Using the later, you can read through the CIS data to make sure the * card is compatible with the driver. Keep in mind that the PCMCIA * spec specifies the CIS as the lower 8 bits of each word read from * the CIS, so to read the bytes of the CIS, read every other byte * (0,2,4,...). Passing that test, you need to enable the I/O address * space on the PCMCIA card via the PCMCIA COR register. This is the * first byte following the CIS. In my case (which may not have any * relation to what's on the PRISM2 cards), COR was at offset 0x800 * within the PCI memory space. Write 0x41 to the COR register to * enable I/O mode and to select level triggered interrupts. To * confirm you actually succeeded, read the COR register back and make * sure it actually got set to 0x41, in case you have an unexpected * card inserted. * * Following that, you can treat the second PCI I/O address space (the * one that's not 0x80 in length) as the PCMCIA I/O space. * * Note that in the Eumitcom's source for their drivers, they register * the interrupt as edge triggered when registering it with the * Windows kernel. I don't recall how to register edge triggered on * Linux (if it can be done at all). But in some experimentation, I * don't see much operational difference between using either * interrupt mode. Don't mess with the interrupt mode in the COR * register though, as the PLX9052 wants level triggers with the way * the serial EEPROM configures it on the WL11000. * * There's some other little quirks related to timing that I bumped * into, but I don't recall right now. Also, there's two variants of * the WL11000 I've seen, revision A1 and T2. These seem to differ * slightly in the timings configured in the wait-state generator in * the PLX9052. There have also been some comments from Eumitcom that * cards shouldn't be hot swapped, apparently due to risk of cooking * the PLX9052. I'm unsure why they believe this, as I can't see * anything in the design that would really cause a problem, except * for crashing drivers not written to expect it. And having developed * drivers for the WL11000, I'd say it's quite tricky to write code * that will successfully deal with a hot unplug. Very odd things * happen on the I/O side of things. But anyway, be warned. Despite * that, I've hot-swapped a number of times during debugging and * driver development for various reasons (stuck WAIT# line after the * radio card's firmware locks up). */ #define DRIVER_NAME "orinoco_plx" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include #include "orinoco.h" #include "orinoco_pci.h" #define COR_OFFSET (0x3e0) /* COR attribute offset of Prism2 PC card */ #define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ #define COR_RESET (0x80) /* reset bit in the COR register */ #define PLX_RESET_TIME (500) /* milliseconds */ #define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */ #define PLX_INTCSR_INTEN (1 << 6) /* Interrupt Enable bit */ /* * Do a soft reset of the card using the Configuration Option Register */ static int orinoco_plx_cor_reset(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; struct orinoco_pci_card *card = priv->card; unsigned long timeout; u16 reg; iowrite8(COR_VALUE | COR_RESET, card->attr_io + COR_OFFSET); mdelay(1); iowrite8(COR_VALUE, card->attr_io + COR_OFFSET); mdelay(1); /* Just in case, wait more until the card is no longer busy */ timeout = jiffies + (PLX_RESET_TIME * HZ / 1000); reg = hermes_read_regn(hw, CMD); while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { mdelay(1); reg = hermes_read_regn(hw, CMD); } /* Still busy? */ if (reg & HERMES_CMD_BUSY) { printk(KERN_ERR PFX "Busy timeout\n"); return -ETIMEDOUT; } return 0; } static int orinoco_plx_hw_init(struct orinoco_pci_card *card) { int i; u32 csr_reg; static const u8 cis_magic[] = { 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67 }; printk(KERN_DEBUG PFX "CIS: "); for (i = 0; i < 16; i++) printk("%02X:", ioread8(card->attr_io + (i << 1))); printk("\n"); /* Verify whether a supported PC card is present */ /* FIXME: we probably need to be smarted about this */ for (i = 0; i < sizeof(cis_magic); i++) { if (cis_magic[i] != ioread8(card->attr_io + (i << 1))) { printk(KERN_ERR PFX "The CIS value of Prism2 PC " "card is unexpected\n"); return -ENODEV; } } /* bjoern: We need to tell the card to enable interrupts, in case the serial eprom didn't do this already. See the PLX9052 data book, p8-1 and 8-24 for reference. */ csr_reg = ioread32(card->bridge_io + PLX_INTCSR); if (!(csr_reg & PLX_INTCSR_INTEN)) { csr_reg |= PLX_INTCSR_INTEN; iowrite32(csr_reg, card->bridge_io + PLX_INTCSR); csr_reg = ioread32(card->bridge_io + PLX_INTCSR); if (!(csr_reg & PLX_INTCSR_INTEN)) { printk(KERN_ERR PFX "Cannot enable interrupts\n"); return -EIO; } } return 0; } static int orinoco_plx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int err; struct orinoco_private *priv; struct orinoco_pci_card *card; void __iomem *hermes_io, *attr_io, *bridge_io; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR PFX "Cannot enable PCI device\n"); return err; } err = pci_request_regions(pdev, DRIVER_NAME); if (err) { printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); goto fail_resources; } bridge_io = pci_iomap(pdev, 1, 0); if (!bridge_io) { printk(KERN_ERR PFX "Cannot map bridge registers\n"); err = -EIO; goto fail_map_bridge; } attr_io = pci_iomap(pdev, 2, 0); if (!attr_io) { printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n"); err = -EIO; goto fail_map_attr; } hermes_io = pci_iomap(pdev, 3, 0); if (!hermes_io) { printk(KERN_ERR PFX "Cannot map chipset registers\n"); err = -EIO; goto fail_map_hermes; } /* Allocate network device */ priv = alloc_orinocodev(sizeof(*card), &pdev->dev, orinoco_plx_cor_reset, NULL); if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } card = priv->card; card->bridge_io = bridge_io; card->attr_io = attr_io; hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; goto fail_irq; } err = orinoco_plx_hw_init(card); if (err) { printk(KERN_ERR PFX "Hardware initialization failed\n"); goto fail; } err = orinoco_plx_cor_reset(priv); if (err) { printk(KERN_ERR PFX "Initial reset failed\n"); goto fail; } err = orinoco_init(priv); if (err) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; } pci_set_drvdata(pdev, priv); return 0; fail: free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); fail_map_hermes: pci_iounmap(pdev, attr_io); fail_map_attr: pci_iounmap(pdev, bridge_io); fail_map_bridge: pci_release_regions(pdev); fail_resources: pci_disable_device(pdev); return err; } static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev) { struct orinoco_private *priv = pci_get_drvdata(pdev); struct orinoco_pci_card *card = priv->card; orinoco_if_del(priv); free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); pci_iounmap(pdev, card->bridge_io); pci_release_regions(pdev); pci_disable_device(pdev); } static DEFINE_PCI_DEVICE_TABLE(orinoco_plx_id_table) = { {0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */ {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */ {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */ {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W, Eumitcom PCI WL11000, Addtron AWA-100 */ {0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* Global Sun Tech GL24110P */ {0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */ {0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */ {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */ {0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 tested by Brendan W. McAdams */ {0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3Com AirConnect PCI tested by Damien Persohn */ {0,}, }; MODULE_DEVICE_TABLE(pci, orinoco_plx_id_table); static struct pci_driver orinoco_plx_driver = { .name = DRIVER_NAME, .id_table = orinoco_plx_id_table, .probe = orinoco_plx_init_one, .remove = __devexit_p(orinoco_plx_remove_one), .suspend = orinoco_pci_suspend, .resume = orinoco_pci_resume, }; static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION " (Pavel Roskin ," " David Gibson ," " Daniel Barlow )"; MODULE_AUTHOR("Daniel Barlow "); MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); MODULE_LICENSE("Dual MPL/GPL"); static int __init orinoco_plx_init(void) { printk(KERN_DEBUG "%s\n", version); return pci_register_driver(&orinoco_plx_driver); } static void __exit orinoco_plx_exit(void) { pci_unregister_driver(&orinoco_plx_driver); } module_init(orinoco_plx_init); module_exit(orinoco_plx_exit); /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco_pci.h0000644000175000017500000000262012026211315024605 0ustar mcgrofmcgrof/* orinoco_pci.h * * Common code for all Orinoco drivers for PCI devices, including * both native PCI and PCMCIA-to-PCI bridges. * * Copyright (C) 2005, Pavel Roskin. * See main.c for license. */ #ifndef _ORINOCO_PCI_H #define _ORINOCO_PCI_H #include /* Driver specific data */ struct orinoco_pci_card { void __iomem *bridge_io; void __iomem *attr_io; }; #ifdef CONFIG_PM static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) { struct orinoco_private *priv = pci_get_drvdata(pdev); orinoco_down(priv); free_irq(pdev->irq, priv); pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); return 0; } static int orinoco_pci_resume(struct pci_dev *pdev) { struct orinoco_private *priv = pci_get_drvdata(pdev); struct net_device *dev = priv->ndev; int err; pci_set_power_state(pdev, 0); err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s: pci_enable_device failed on resume\n", dev->name); return err; } pci_restore_state(pdev); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, dev->name, priv); if (err) { printk(KERN_ERR "%s: cannot re-allocate IRQ on resume\n", dev->name); pci_disable_device(pdev); return -EBUSY; } err = orinoco_up(priv); return err; } #else #define orinoco_pci_suspend NULL #define orinoco_pci_resume NULL #endif #endif /* _ORINOCO_PCI_H */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco_pci.c0000644000175000017500000001656712026211315024617 0ustar mcgrofmcgrof/* orinoco_pci.c * * Driver for Prism 2.5/3 devices that have a direct PCI interface * (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge). * The card contains only one PCI region, which contains all the usual * hermes registers, as well as the COR register. * * Current maintainers are: * Pavel Roskin * and David Gibson * * Some of this code is borrowed from orinoco_plx.c * Copyright (C) 2001 Daniel Barlow * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing * has been copied from it. linux-wlan-ng-0.1.10 is originally : * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. * This file originally written by: * Copyright (C) 2001 Jean Tourrilhes * And is now maintained by: * (C) Copyright David Gibson, IBM Corp. 2002-2003. * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. */ #define DRIVER_NAME "orinoco_pci" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include "orinoco.h" #include "orinoco_pci.h" /* Offset of the COR register of the PCI card */ #define HERMES_PCI_COR (0x26) /* Bitmask to reset the card */ #define HERMES_PCI_COR_MASK (0x0080) /* Magic timeouts for doing the reset. * Those times are straight from wlan-ng, and it is claimed that they * are necessary. Alan will kill me. Take your time and grab a coffee. */ #define HERMES_PCI_COR_ONT (250) /* ms */ #define HERMES_PCI_COR_OFFT (500) /* ms */ #define HERMES_PCI_COR_BUSYT (500) /* ms */ /* * Do a soft reset of the card using the Configuration Option Register * We need this to get going... * This is the part of the code that is strongly inspired from wlan-ng * * Note : This code is done with irq enabled. This mean that many * interrupts will occur while we are there. This is why we use the * jiffies to regulate time instead of a straight mdelay(). Usually we * need only around 245 iteration of the loop to do 250 ms delay. * * Note bis : Don't try to access HERMES_CMD during the reset phase. * It just won't work ! */ static int orinoco_pci_cor_reset(struct orinoco_private *priv) { struct hermes *hw = &priv->hw; unsigned long timeout; u16 reg; /* Assert the reset until the card notices */ hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); mdelay(HERMES_PCI_COR_ONT); /* Give time for the card to recover from this hard effort */ hermes_write_regn(hw, PCI_COR, 0x0000); mdelay(HERMES_PCI_COR_OFFT); /* The card is ready when it's no longer busy */ timeout = jiffies + (HERMES_PCI_COR_BUSYT * HZ / 1000); reg = hermes_read_regn(hw, CMD); while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { mdelay(1); reg = hermes_read_regn(hw, CMD); } /* Still busy? */ if (reg & HERMES_CMD_BUSY) { printk(KERN_ERR PFX "Busy timeout\n"); return -ETIMEDOUT; } return 0; } static int orinoco_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int err; struct orinoco_private *priv; struct orinoco_pci_card *card; void __iomem *hermes_io; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR PFX "Cannot enable PCI device\n"); return err; } err = pci_request_regions(pdev, DRIVER_NAME); if (err) { printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); goto fail_resources; } hermes_io = pci_iomap(pdev, 0, 0); if (!hermes_io) { printk(KERN_ERR PFX "Cannot remap chipset registers\n"); err = -EIO; goto fail_map_hermes; } /* Allocate network device */ priv = alloc_orinocodev(sizeof(*card), &pdev->dev, orinoco_pci_cor_reset, NULL); if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } card = priv->card; hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; goto fail_irq; } err = orinoco_pci_cor_reset(priv); if (err) { printk(KERN_ERR PFX "Initial reset failed\n"); goto fail; } err = orinoco_init(priv); if (err) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; } pci_set_drvdata(pdev, priv); return 0; fail: free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); fail_map_hermes: pci_release_regions(pdev); fail_resources: pci_disable_device(pdev); return err; } static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev) { struct orinoco_private *priv = pci_get_drvdata(pdev); orinoco_if_del(priv); free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_release_regions(pdev); pci_disable_device(pdev); } static DEFINE_PCI_DEVICE_TABLE(orinoco_pci_id_table) = { /* Intersil Prism 3 */ {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, /* Intersil Prism 2.5 */ {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, /* Samsung MagicLAN SWL-2210P */ {0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,}, {0,}, }; MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table); static struct pci_driver orinoco_pci_driver = { .name = DRIVER_NAME, .id_table = orinoco_pci_id_table, .probe = orinoco_pci_init_one, .remove = __devexit_p(orinoco_pci_remove_one), .suspend = orinoco_pci_suspend, .resume = orinoco_pci_resume, }; static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION " (Pavel Roskin ," " David Gibson &" " Jean Tourrilhes )"; MODULE_AUTHOR("Pavel Roskin &" " David Gibson "); MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); MODULE_LICENSE("Dual MPL/GPL"); static int __init orinoco_pci_init(void) { printk(KERN_DEBUG "%s\n", version); return pci_register_driver(&orinoco_pci_driver); } static void __exit orinoco_pci_exit(void) { pci_unregister_driver(&orinoco_pci_driver); } module_init(orinoco_pci_init); module_exit(orinoco_pci_exit); /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco_nortel.c0000644000175000017500000002056112026211315025334 0ustar mcgrofmcgrof/* orinoco_nortel.c * * Driver for Prism II devices which would usually be driven by orinoco_cs, * but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in * Nortel emobility, Symbol LA-4113 and Symbol LA-4123. * * Copyright (C) 2002 Tobias Hoffmann * (C) 2003 Christoph Jungegger * * Some of this code is borrowed from orinoco_plx.c * Copyright (C) 2001 Daniel Barlow * Some of this code is borrowed from orinoco_pci.c * Copyright (C) 2001 Jean Tourrilhes * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing * has been copied from it. linux-wlan-ng-0.1.10 is originally : * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. */ #define DRIVER_NAME "orinoco_nortel" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include #include "orinoco.h" #include "orinoco_pci.h" #define COR_OFFSET (0xe0) /* COR attribute offset of Prism2 PC card */ #define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ /* * Do a soft reset of the card using the Configuration Option Register * We need this to get going... * This is the part of the code that is strongly inspired from wlan-ng * * Note bis : Don't try to access HERMES_CMD during the reset phase. * It just won't work ! */ static int orinoco_nortel_cor_reset(struct orinoco_private *priv) { struct orinoco_pci_card *card = priv->card; /* Assert the reset until the card notices */ iowrite16(8, card->bridge_io + 2); ioread16(card->attr_io + COR_OFFSET); iowrite16(0x80, card->attr_io + COR_OFFSET); mdelay(1); /* Give time for the card to recover from this hard effort */ iowrite16(0, card->attr_io + COR_OFFSET); iowrite16(0, card->attr_io + COR_OFFSET); mdelay(1); /* Set COR as usual */ iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); mdelay(1); iowrite16(0x228, card->bridge_io + 2); return 0; } static int orinoco_nortel_hw_init(struct orinoco_pci_card *card) { int i; u32 reg; /* Setup bridge */ if (ioread16(card->bridge_io) & 1) { printk(KERN_ERR PFX "brg1 answer1 wrong\n"); return -EBUSY; } iowrite16(0x118, card->bridge_io + 2); iowrite16(0x108, card->bridge_io + 2); mdelay(30); iowrite16(0x8, card->bridge_io + 2); for (i = 0; i < 30; i++) { mdelay(30); if (ioread16(card->bridge_io) & 0x10) break; } if (i == 30) { printk(KERN_ERR PFX "brg1 timed out\n"); return -EBUSY; } if (ioread16(card->attr_io + COR_OFFSET) & 1) { printk(KERN_ERR PFX "brg2 answer1 wrong\n"); return -EBUSY; } if (ioread16(card->attr_io + COR_OFFSET + 2) & 1) { printk(KERN_ERR PFX "brg2 answer2 wrong\n"); return -EBUSY; } if (ioread16(card->attr_io + COR_OFFSET + 4) & 1) { printk(KERN_ERR PFX "brg2 answer3 wrong\n"); return -EBUSY; } /* Set the PCMCIA COR register */ iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); mdelay(1); reg = ioread16(card->attr_io + COR_OFFSET); if (reg != COR_VALUE) { printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n", reg); return -EBUSY; } /* Set LEDs */ iowrite16(1, card->bridge_io + 10); return 0; } static int orinoco_nortel_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int err; struct orinoco_private *priv; struct orinoco_pci_card *card; void __iomem *hermes_io, *bridge_io, *attr_io; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR PFX "Cannot enable PCI device\n"); return err; } err = pci_request_regions(pdev, DRIVER_NAME); if (err) { printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); goto fail_resources; } bridge_io = pci_iomap(pdev, 0, 0); if (!bridge_io) { printk(KERN_ERR PFX "Cannot map bridge registers\n"); err = -EIO; goto fail_map_bridge; } attr_io = pci_iomap(pdev, 1, 0); if (!attr_io) { printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n"); err = -EIO; goto fail_map_attr; } hermes_io = pci_iomap(pdev, 2, 0); if (!hermes_io) { printk(KERN_ERR PFX "Cannot map chipset registers\n"); err = -EIO; goto fail_map_hermes; } /* Allocate network device */ priv = alloc_orinocodev(sizeof(*card), &pdev->dev, orinoco_nortel_cor_reset, NULL); if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } card = priv->card; card->bridge_io = bridge_io; card->attr_io = attr_io; hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; goto fail_irq; } err = orinoco_nortel_hw_init(card); if (err) { printk(KERN_ERR PFX "Hardware initialization failed\n"); goto fail; } err = orinoco_nortel_cor_reset(priv); if (err) { printk(KERN_ERR PFX "Initial reset failed\n"); goto fail; } err = orinoco_init(priv); if (err) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; } pci_set_drvdata(pdev, priv); return 0; fail: free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); fail_map_hermes: pci_iounmap(pdev, attr_io); fail_map_attr: pci_iounmap(pdev, bridge_io); fail_map_bridge: pci_release_regions(pdev); fail_resources: pci_disable_device(pdev); return err; } static void __devexit orinoco_nortel_remove_one(struct pci_dev *pdev) { struct orinoco_private *priv = pci_get_drvdata(pdev); struct orinoco_pci_card *card = priv->card; /* Clear LEDs */ iowrite16(0, card->bridge_io + 10); orinoco_if_del(priv); free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); pci_iounmap(pdev, card->bridge_io); pci_release_regions(pdev); pci_disable_device(pdev); } static DEFINE_PCI_DEVICE_TABLE(orinoco_nortel_id_table) = { /* Nortel emobility PCI */ {0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,}, /* Symbol LA-4123 PCI */ {0x1562, 0x0001, PCI_ANY_ID, PCI_ANY_ID,}, {0,}, }; MODULE_DEVICE_TABLE(pci, orinoco_nortel_id_table); static struct pci_driver orinoco_nortel_driver = { .name = DRIVER_NAME, .id_table = orinoco_nortel_id_table, .probe = orinoco_nortel_init_one, .remove = __devexit_p(orinoco_nortel_remove_one), .suspend = orinoco_pci_suspend, .resume = orinoco_pci_resume, }; static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION " (Tobias Hoffmann & Christoph Jungegger )"; MODULE_AUTHOR("Christoph Jungegger "); MODULE_DESCRIPTION("Driver for wireless LAN cards using the Nortel PCI bridge"); MODULE_LICENSE("Dual MPL/GPL"); static int __init orinoco_nortel_init(void) { printk(KERN_DEBUG "%s\n", version); return pci_register_driver(&orinoco_nortel_driver); } static void __exit orinoco_nortel_exit(void) { pci_unregister_driver(&orinoco_nortel_driver); } module_init(orinoco_nortel_init); module_exit(orinoco_nortel_exit); /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/orinoco.h0000644000175000017500000001515412026211315023760 0ustar mcgrofmcgrof/* orinoco.h * * Common definitions to all pieces of the various orinoco * drivers */ #ifndef _ORINOCO_H #define _ORINOCO_H #define DRIVER_VERSION "0.15" #include #include #include #include #include #include #include "hermes.h" /* To enable debug messages */ /*#define ORINOCO_DEBUG 3*/ #define WIRELESS_SPY /* enable iwspy support */ #define MAX_SCAN_LEN 4096 #define ORINOCO_SEQ_LEN 8 #define ORINOCO_MAX_KEY_SIZE 14 #define ORINOCO_MAX_KEYS 4 struct orinoco_key { __le16 len; /* always stored as little-endian */ char data[ORINOCO_MAX_KEY_SIZE]; } __packed; #define TKIP_KEYLEN 16 #define MIC_KEYLEN 8 struct orinoco_tkip_key { u8 tkip[TKIP_KEYLEN]; u8 tx_mic[MIC_KEYLEN]; u8 rx_mic[MIC_KEYLEN]; }; enum orinoco_alg { ORINOCO_ALG_NONE, ORINOCO_ALG_WEP, ORINOCO_ALG_TKIP }; enum fwtype { FIRMWARE_TYPE_AGERE, FIRMWARE_TYPE_INTERSIL, FIRMWARE_TYPE_SYMBOL }; struct firmware; struct orinoco_private { void *card; /* Pointer to card dependent structure */ struct device *dev; int (*hard_reset)(struct orinoco_private *); int (*stop_fw)(struct orinoco_private *, int); struct ieee80211_supported_band band; struct ieee80211_channel channels[14]; u32 cipher_suites[3]; /* Synchronisation stuff */ spinlock_t lock; int hw_unavailable; struct work_struct reset_work; /* Interrupt tasklets */ struct tasklet_struct rx_tasklet; struct list_head rx_list; /* driver state */ int open; u16 last_linkstatus; struct work_struct join_work; struct work_struct wevent_work; /* Net device stuff */ struct net_device *ndev; struct net_device_stats stats; struct iw_statistics wstats; /* Hardware control variables */ struct hermes hw; u16 txfid; /* Capabilities of the hardware/firmware */ enum fwtype firmware_type; int ibss_port; int nicbuf_size; u16 channel_mask; /* Boolean capabilities */ unsigned int has_ibss:1; unsigned int has_port3:1; unsigned int has_wep:1; unsigned int has_big_wep:1; unsigned int has_mwo:1; unsigned int has_pm:1; unsigned int has_preamble:1; unsigned int has_sensitivity:1; unsigned int has_hostscan:1; unsigned int has_alt_txcntl:1; unsigned int has_ext_scan:1; unsigned int has_wpa:1; unsigned int do_fw_download:1; unsigned int broken_disableport:1; unsigned int broken_monitor:1; unsigned int prefer_port3:1; /* Configuration paramaters */ enum nl80211_iftype iw_mode; enum orinoco_alg encode_alg; u16 wep_restrict, tx_key; struct key_params keys[ORINOCO_MAX_KEYS]; int bitratemode; char nick[IW_ESSID_MAX_SIZE + 1]; char desired_essid[IW_ESSID_MAX_SIZE + 1]; char desired_bssid[ETH_ALEN]; int bssid_fixed; u16 frag_thresh, mwo_robust; u16 channel; u16 ap_density, rts_thresh; u16 pm_on, pm_mcast, pm_period, pm_timeout; u16 preamble; u16 short_retry_limit, long_retry_limit; u16 retry_lifetime; #ifdef WIRELESS_SPY struct iw_spy_data spy_data; /* iwspy support */ struct iw_public_data wireless_data; #endif /* Configuration dependent variables */ int port_type, createibss; int promiscuous, mc_count; /* Scanning support */ struct cfg80211_scan_request *scan_request; struct work_struct process_scan; struct list_head scan_list; spinlock_t scan_lock; /* protects the scan list */ /* WPA support */ u8 *wpa_ie; int wpa_ie_len; struct crypto_hash *rx_tfm_mic; struct crypto_hash *tx_tfm_mic; unsigned int wpa_enabled:1; unsigned int tkip_cm_active:1; unsigned int key_mgmt:3; #if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) /* Cached in memory firmware to use during ->resume. */ const struct firmware *cached_pri_fw; const struct firmware *cached_fw; #endif struct notifier_block pm_notifier; }; #ifdef ORINOCO_DEBUG extern int orinoco_debug; #define DEBUG(n, args...) do { \ if (orinoco_debug > (n)) \ printk(KERN_DEBUG args); \ } while (0) #else #define DEBUG(n, args...) do { } while (0) #endif /* ORINOCO_DEBUG */ /********************************************************************/ /* Exported prototypes */ /********************************************************************/ extern struct orinoco_private *alloc_orinocodev( int sizeof_card, struct device *device, int (*hard_reset)(struct orinoco_private *), int (*stop_fw)(struct orinoco_private *, int)); extern void free_orinocodev(struct orinoco_private *priv); extern int orinoco_init(struct orinoco_private *priv); extern int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, unsigned int irq, const struct net_device_ops *ops); extern void orinoco_if_del(struct orinoco_private *priv); extern int orinoco_up(struct orinoco_private *priv); extern void orinoco_down(struct orinoco_private *priv); extern irqreturn_t orinoco_interrupt(int irq, void *dev_id); extern void __orinoco_ev_info(struct net_device *dev, struct hermes *hw); extern void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw); int orinoco_process_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct orinoco_private *priv, int *tx_control, u8 *mic); /* Common ndo functions exported for reuse by orinoco_usb */ int orinoco_open(struct net_device *dev); int orinoco_stop(struct net_device *dev); struct net_device_stats *orinoco_get_stats(struct net_device *dev); void orinoco_set_multicast_list(struct net_device *dev); int orinoco_change_mtu(struct net_device *dev, int new_mtu); void orinoco_tx_timeout(struct net_device *dev); /********************************************************************/ /* Locking and synchronization functions */ /********************************************************************/ static inline int orinoco_lock(struct orinoco_private *priv, unsigned long *flags) { priv->hw.ops->lock_irqsave(&priv->lock, flags); if (priv->hw_unavailable) { DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n", priv->ndev); priv->hw.ops->unlock_irqrestore(&priv->lock, flags); return -EBUSY; } return 0; } static inline void orinoco_unlock(struct orinoco_private *priv, unsigned long *flags) { priv->hw.ops->unlock_irqrestore(&priv->lock, flags); } static inline void orinoco_lock_irq(struct orinoco_private *priv) { priv->hw.ops->lock_irq(&priv->lock); } static inline void orinoco_unlock_irq(struct orinoco_private *priv) { priv->hw.ops->unlock_irq(&priv->lock); } /*** Navigate from net_device to orinoco_private ***/ static inline struct orinoco_private *ndev_priv(struct net_device *dev) { struct wireless_dev *wdev = netdev_priv(dev); return wdev_priv(wdev); } #endif /* _ORINOCO_H */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/mic.h0000644000175000017500000000076312026211315023060 0ustar mcgrofmcgrof/* Orinoco MIC helpers * * See copyright notice in main.c */ #ifndef _ORINOCO_MIC_H_ #define _ORINOCO_MIC_H_ #include #define MICHAEL_MIC_LEN 8 /* Forward declarations */ struct orinoco_private; struct crypto_hash; int orinoco_mic_init(struct orinoco_private *priv); void orinoco_mic_free(struct orinoco_private *priv); int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, u8 *da, u8 *sa, u8 priority, u8 *data, size_t data_len, u8 *mic); #endif /* ORINOCO_MIC_H */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/mic.c0000644000175000017500000000411412026211315023045 0ustar mcgrofmcgrof/* Orinoco MIC helpers * * See copyright notice in main.c */ #include #include #include #include #include #include "orinoco.h" #include "mic.h" /********************************************************************/ /* Michael MIC crypto setup */ /********************************************************************/ int orinoco_mic_init(struct orinoco_private *priv) { priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); if (IS_ERR(priv->tx_tfm_mic)) { printk(KERN_DEBUG "orinoco_mic_init: could not allocate " "crypto API michael_mic\n"); priv->tx_tfm_mic = NULL; return -ENOMEM; } priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); if (IS_ERR(priv->rx_tfm_mic)) { printk(KERN_DEBUG "orinoco_mic_init: could not allocate " "crypto API michael_mic\n"); priv->rx_tfm_mic = NULL; return -ENOMEM; } return 0; } void orinoco_mic_free(struct orinoco_private *priv) { if (priv->tx_tfm_mic) crypto_free_hash(priv->tx_tfm_mic); if (priv->rx_tfm_mic) crypto_free_hash(priv->rx_tfm_mic); } int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, u8 *da, u8 *sa, u8 priority, u8 *data, size_t data_len, u8 *mic) { struct hash_desc desc; struct scatterlist sg[2]; u8 hdr[ETH_HLEN + 2]; /* size of header + padding */ if (tfm_michael == NULL) { printk(KERN_WARNING "orinoco_mic: tfm_michael == NULL\n"); return -1; } /* Copy header into buffer. We need the padding on the end zeroed */ memcpy(&hdr[0], da, ETH_ALEN); memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN); hdr[ETH_ALEN * 2] = priority; hdr[ETH_ALEN * 2 + 1] = 0; hdr[ETH_ALEN * 2 + 2] = 0; hdr[ETH_ALEN * 2 + 3] = 0; /* Use scatter gather to MIC header and data in one go */ sg_init_table(sg, 2); sg_set_buf(&sg[0], hdr, sizeof(hdr)); sg_set_buf(&sg[1], data, data_len); if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN)) return -1; desc.tfm = tfm_michael; desc.flags = 0; return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr), mic); } compat-drivers-2012-09-18/drivers/net/wireless/orinoco/main.h0000644000175000017500000000252612026211315023233 0ustar mcgrofmcgrof/* Exports from main to helper modules * * See copyright notice in main.c */ #ifndef _ORINOCO_MAIN_H_ #define _ORINOCO_MAIN_H_ #include #include "orinoco.h" /********************************************************************/ /* Compile time configuration and compatibility stuff */ /********************************************************************/ /* We do this this way to avoid ifdefs in the actual code */ #ifdef WIRELESS_SPY #define SPY_NUMBER(priv) (priv->spy_data.spy_number) #else #define SPY_NUMBER(priv) 0 #endif /* WIRELESS_SPY */ /********************************************************************/ /* Export module parameter */ extern int force_monitor; /* Forward declarations */ struct net_device; struct work_struct; void set_port_type(struct orinoco_private *priv); int orinoco_commit(struct orinoco_private *priv); void orinoco_reset(struct work_struct *work); /* Information element helpers - find a home for these... */ #define WPA_OUI_TYPE "\x00\x50\xF2\x01" #define WPA_SELECTOR_LEN 4 static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) { u8 *p = data; while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { if ((p[0] == WLAN_EID_GENERIC) && (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) return p; p += p[1] + 2; } return NULL; } #endif /* _ORINOCO_MAIN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/hermes_rid.h0000644000175000017500000001526412026211315024433 0ustar mcgrofmcgrof#ifndef _HERMES_RID_H #define _HERMES_RID_H /* * Configuration RIDs */ #define HERMES_RID_CNFPORTTYPE 0xFC00 #define HERMES_RID_CNFOWNMACADDR 0xFC01 #define HERMES_RID_CNFDESIREDSSID 0xFC02 #define HERMES_RID_CNFOWNCHANNEL 0xFC03 #define HERMES_RID_CNFOWNSSID 0xFC04 #define HERMES_RID_CNFOWNATIMWINDOW 0xFC05 #define HERMES_RID_CNFSYSTEMSCALE 0xFC06 #define HERMES_RID_CNFMAXDATALEN 0xFC07 #define HERMES_RID_CNFWDSADDRESS 0xFC08 #define HERMES_RID_CNFPMENABLED 0xFC09 #define HERMES_RID_CNFPMEPS 0xFC0A #define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B #define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C #define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D #define HERMES_RID_CNFOWNNAME 0xFC0E #define HERMES_RID_CNFOWNDTIMPERIOD 0xFC10 #define HERMES_RID_CNFWDSADDRESS1 0xFC11 #define HERMES_RID_CNFWDSADDRESS2 0xFC12 #define HERMES_RID_CNFWDSADDRESS3 0xFC13 #define HERMES_RID_CNFWDSADDRESS4 0xFC14 #define HERMES_RID_CNFWDSADDRESS5 0xFC15 #define HERMES_RID_CNFWDSADDRESS6 0xFC16 #define HERMES_RID_CNFMULTICASTPMBUFFERING 0xFC17 #define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 #define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21 #define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21 #define HERMES_RID_CNFDROPUNENCRYPTED 0xFC22 #define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 #define HERMES_RID_CNFDEFAULTKEY0 0xFC24 #define HERMES_RID_CNFDEFAULTKEY1 0xFC25 #define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25 #define HERMES_RID_CNFDEFAULTKEY2 0xFC26 #define HERMES_RID_CNFDEFAULTKEY3 0xFC27 #define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28 #define HERMES_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 #define HERMES_RID_CNFAUTHENTICATION 0xFC2A #define HERMES_RID_CNFMAXASSOCSTA 0xFC2B #define HERMES_RID_CNFKEYLENGTH_SYMBOL 0xFC2B #define HERMES_RID_CNFTXCONTROL 0xFC2C #define HERMES_RID_CNFROAMINGMODE 0xFC2D #define HERMES_RID_CNFHOSTAUTHENTICATION 0xFC2E #define HERMES_RID_CNFRCVCRCERROR 0xFC30 #define HERMES_RID_CNFMMLIFE 0xFC31 #define HERMES_RID_CNFALTRETRYCOUNT 0xFC32 #define HERMES_RID_CNFBEACONINT 0xFC33 #define HERMES_RID_CNFAPPCFINFO 0xFC34 #define HERMES_RID_CNFSTAPCFINFO 0xFC35 #define HERMES_RID_CNFPRIORITYQUSAGE 0xFC37 #define HERMES_RID_CNFTIMCTRL 0xFC40 #define HERMES_RID_CNFTHIRTY2TALLY 0xFC42 #define HERMES_RID_CNFENHSECURITY 0xFC43 #define HERMES_RID_CNFGROUPADDRESSES 0xFC80 #define HERMES_RID_CNFCREATEIBSS 0xFC81 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82 #define HERMES_RID_CNFRTSTHRESHOLD 0xFC83 #define HERMES_RID_CNFTXRATECONTROL 0xFC84 #define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85 #define HERMES_RID_CNFBASICRATES_SYMBOL 0xFC8A #define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD0 0xFC90 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD1 0xFC91 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD2 0xFC92 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD3 0xFC93 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD4 0xFC94 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD5 0xFC95 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD6 0xFC96 #define HERMES_RID_CNFRTSTHRESHOLD0 0xFC97 #define HERMES_RID_CNFRTSTHRESHOLD1 0xFC98 #define HERMES_RID_CNFRTSTHRESHOLD2 0xFC99 #define HERMES_RID_CNFRTSTHRESHOLD3 0xFC9A #define HERMES_RID_CNFRTSTHRESHOLD4 0xFC9B #define HERMES_RID_CNFRTSTHRESHOLD5 0xFC9C #define HERMES_RID_CNFRTSTHRESHOLD6 0xFC9D #define HERMES_RID_CNFHOSTSCAN_SYMBOL 0xFCAB #define HERMES_RID_CNFSHORTPREAMBLE 0xFCB0 #define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0 #define HERMES_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 #define HERMES_RID_CNFTXKEY_AGERE 0xFCB1 #define HERMES_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 #define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2 #define HERMES_RID_CNFBASICRATES 0xFCB3 #define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4 #define HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE 0xFCB4 #define HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE 0xFCB5 #define HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE 0xFCB6 #define HERMES_RID_CNFADDMAPPEDTKIPKEY_AGERE 0xFCB7 #define HERMES_RID_CNFREMMAPPEDTKIPKEY_AGERE 0xFCB8 #define HERMES_RID_CNFSETWPACAPABILITIES_AGERE 0xFCB9 #define HERMES_RID_CNFCACHEDPMKADDRESS 0xFCBA #define HERMES_RID_CNFREMOVEPMKADDRESS 0xFCBB #define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2 #define HERMES_RID_CNFDISASSOCIATE 0xFCC8 #define HERMES_RID_CNFTICKTIME 0xFCE0 #define HERMES_RID_CNFSCANREQUEST 0xFCE1 #define HERMES_RID_CNFJOINREQUEST 0xFCE2 #define HERMES_RID_CNFAUTHENTICATESTATION 0xFCE3 #define HERMES_RID_CNFCHANNELINFOREQUEST 0xFCE4 #define HERMES_RID_CNFHOSTSCAN 0xFCE5 /* * Information RIDs */ #define HERMES_RID_MAXLOADTIME 0xFD00 #define HERMES_RID_DOWNLOADBUFFER 0xFD01 #define HERMES_RID_PRIID 0xFD02 #define HERMES_RID_PRISUPRANGE 0xFD03 #define HERMES_RID_CFIACTRANGES 0xFD04 #define HERMES_RID_NICSERNUM 0xFD0A #define HERMES_RID_NICID 0xFD0B #define HERMES_RID_MFISUPRANGE 0xFD0C #define HERMES_RID_CFISUPRANGE 0xFD0D #define HERMES_RID_CHANNELLIST 0xFD10 #define HERMES_RID_REGULATORYDOMAINS 0xFD11 #define HERMES_RID_TEMPTYPE 0xFD12 #define HERMES_RID_CIS 0xFD13 #define HERMES_RID_STAID 0xFD20 #define HERMES_RID_STASUPRANGE 0xFD21 #define HERMES_RID_MFIACTRANGES 0xFD22 #define HERMES_RID_CFIACTRANGES2 0xFD23 #define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24 #define HERMES_RID_PORTSTATUS 0xFD40 #define HERMES_RID_CURRENTSSID 0xFD41 #define HERMES_RID_CURRENTBSSID 0xFD42 #define HERMES_RID_COMMSQUALITY 0xFD43 #define HERMES_RID_CURRENTTXRATE 0xFD44 #define HERMES_RID_CURRENTBEACONINTERVAL 0xFD45 #define HERMES_RID_CURRENTSCALETHRESHOLDS 0xFD46 #define HERMES_RID_PROTOCOLRSPTIME 0xFD47 #define HERMES_RID_SHORTRETRYLIMIT 0xFD48 #define HERMES_RID_LONGRETRYLIMIT 0xFD49 #define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A #define HERMES_RID_MAXRECEIVELIFETIME 0xFD4B #define HERMES_RID_CFPOLLABLE 0xFD4C #define HERMES_RID_AUTHENTICATIONALGORITHMS 0xFD4D #define HERMES_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F #define HERMES_RID_DBMCOMMSQUALITY_INTERSIL 0xFD51 #define HERMES_RID_CURRENTTXRATE1 0xFD80 #define HERMES_RID_CURRENTTXRATE2 0xFD81 #define HERMES_RID_CURRENTTXRATE3 0xFD82 #define HERMES_RID_CURRENTTXRATE4 0xFD83 #define HERMES_RID_CURRENTTXRATE5 0xFD84 #define HERMES_RID_CURRENTTXRATE6 0xFD85 #define HERMES_RID_OWNMACADDR 0xFD86 #define HERMES_RID_SCANRESULTSTABLE 0xFD88 #define HERMES_RID_CURRENT_COUNTRY_INFO 0xFD89 #define HERMES_RID_CURRENT_WPA_IE 0xFD8A #define HERMES_RID_CURRENT_TKIP_IV 0xFD8B #define HERMES_RID_CURRENT_ASSOC_REQ_INFO 0xFD8C #define HERMES_RID_CURRENT_ASSOC_RESP_INFO 0xFD8D #define HERMES_RID_TXQUEUEEMPTY 0xFD91 #define HERMES_RID_PHYTYPE 0xFDC0 #define HERMES_RID_CURRENTCHANNEL 0xFDC1 #define HERMES_RID_CURRENTPOWERSTATE 0xFDC2 #define HERMES_RID_CCAMODE 0xFDC3 #define HERMES_RID_SUPPORTEDDATARATES 0xFDC6 #define HERMES_RID_BUILDSEQ 0xFFFE #define HERMES_RID_FWID 0xFFFF #endif compat-drivers-2012-09-18/drivers/net/wireless/orinoco/hermes_dld.h0000644000175000017500000000366312026211315024420 0ustar mcgrofmcgrof/* * Copyright (C) 2007, David Kilroy * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. */ #ifndef _HERMES_DLD_H #define _HERMES_DLD_H #include "hermes.h" int hermesi_program_init(struct hermes *hw, u32 offset); int hermesi_program_end(struct hermes *hw); int hermes_program(struct hermes *hw, const char *first_block, const void *end); int hermes_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr, u16 pda_len, int use_eeprom); int hermes_apply_pda(struct hermes *hw, const char *first_pdr, const void *pdr_end, const __le16 *pda, const void *pda_end); int hermes_apply_pda_with_defaults(struct hermes *hw, const char *first_pdr, const void *pdr_end, const __le16 *pda, const void *pda_end); size_t hermes_blocks_length(const char *first_block, const void *end); #endif /* _HERMES_DLD_H */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/hermes_dld.c0000644000175000017500000003143112026211315024405 0ustar mcgrofmcgrof/* * Hermes download helper. * * This helper: * - is capable of writing to the volatile area of the hermes device * - is currently not capable of writing to non-volatile areas * - provide helpers to identify and update plugin data * - is not capable of interpreting a fw image directly. That is up to * the main card driver. * - deals with Hermes I devices. It can probably be modified to deal * with Hermes II devices * * Copyright (C) 2007, David Kilroy * * Plug data code slightly modified from spectrum_cs driver * Copyright (C) 2002-2005 Pavel Roskin * Portions based on information in wl_lkm_718 Agere driver * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the MPL or the GPL. */ #include #include #include "hermes.h" #include "hermes_dld.h" #define PFX "hermes_dld: " /* End markers used in dblocks */ #define PDI_END 0x00000000 /* End of PDA */ #define BLOCK_END 0xFFFFFFFF /* Last image block */ #define TEXT_END 0x1A /* End of text header */ /* * The following structures have little-endian fields denoted by * the leading underscore. Don't access them directly - use inline * functions defined below. */ /* * The binary image to be downloaded consists of series of data blocks. * Each block has the following structure. */ struct dblock { __le32 addr; /* adapter address where to write the block */ __le16 len; /* length of the data only, in bytes */ char data[0]; /* data to be written */ } __packed; /* * Plug Data References are located in the image after the last data * block. They refer to areas in the adapter memory where the plug data * items with matching ID should be written. */ struct pdr { __le32 id; /* record ID */ __le32 addr; /* adapter address where to write the data */ __le32 len; /* expected length of the data, in bytes */ char next[0]; /* next PDR starts here */ } __packed; /* * Plug Data Items are located in the EEPROM read from the adapter by * primary firmware. They refer to the device-specific data that should * be plugged into the secondary firmware. */ struct pdi { __le16 len; /* length of ID and data, in words */ __le16 id; /* record ID */ char data[0]; /* plug data */ } __packed; /*** FW data block access functions ***/ static inline u32 dblock_addr(const struct dblock *blk) { return le32_to_cpu(blk->addr); } static inline u32 dblock_len(const struct dblock *blk) { return le16_to_cpu(blk->len); } /*** PDR Access functions ***/ static inline u32 pdr_id(const struct pdr *pdr) { return le32_to_cpu(pdr->id); } static inline u32 pdr_addr(const struct pdr *pdr) { return le32_to_cpu(pdr->addr); } static inline u32 pdr_len(const struct pdr *pdr) { return le32_to_cpu(pdr->len); } /*** PDI Access functions ***/ static inline u32 pdi_id(const struct pdi *pdi) { return le16_to_cpu(pdi->id); } /* Return length of the data only, in bytes */ static inline u32 pdi_len(const struct pdi *pdi) { return 2 * (le16_to_cpu(pdi->len) - 1); } /*** Plug Data Functions ***/ /* * Scan PDR for the record with the specified RECORD_ID. * If it's not found, return NULL. */ static const struct pdr * hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end) { const struct pdr *pdr = first_pdr; end -= sizeof(struct pdr); while (((void *) pdr <= end) && (pdr_id(pdr) != PDI_END)) { /* * PDR area is currently not terminated by PDI_END. * It's followed by CRC records, which have the type * field where PDR has length. The type can be 0 or 1. */ if (pdr_len(pdr) < 2) return NULL; /* If the record ID matches, we are done */ if (pdr_id(pdr) == record_id) return pdr; pdr = (struct pdr *) pdr->next; } return NULL; } /* Scan production data items for a particular entry */ static const struct pdi * hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end) { const struct pdi *pdi = first_pdi; end -= sizeof(struct pdi); while (((void *) pdi <= end) && (pdi_id(pdi) != PDI_END)) { /* If the record ID matches, we are done */ if (pdi_id(pdi) == record_id) return pdi; pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; } return NULL; } /* Process one Plug Data Item - find corresponding PDR and plug it */ static int hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr, const struct pdi *pdi, const void *pdr_end) { const struct pdr *pdr; /* Find the PDR corresponding to this PDI */ pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end); /* No match is found, safe to ignore */ if (!pdr) return 0; /* Lengths of the data in PDI and PDR must match */ if (pdi_len(pdi) != pdr_len(pdr)) return -EINVAL; /* do the actual plugging */ hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi)); return 0; } /* Parse PDA and write the records into the adapter * * Attempt to write every records that is in the specified pda * which also has a valid production data record for the firmware. */ int hermes_apply_pda(struct hermes *hw, const char *first_pdr, const void *pdr_end, const __le16 *pda, const void *pda_end) { int ret; const struct pdi *pdi; const struct pdr *pdr; pdr = (const struct pdr *) first_pdr; pda_end -= sizeof(struct pdi); /* Go through every PDI and plug them into the adapter */ pdi = (const struct pdi *) (pda + 2); while (((void *) pdi <= pda_end) && (pdi_id(pdi) != PDI_END)) { ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end); if (ret) return ret; /* Increment to the next PDI */ pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)]; } return 0; } /* Identify the total number of bytes in all blocks * including the header data. */ size_t hermes_blocks_length(const char *first_block, const void *end) { const struct dblock *blk = (const struct dblock *) first_block; int total_len = 0; int len; end -= sizeof(*blk); /* Skip all blocks to locate Plug Data References * (Spectrum CS) */ while (((void *) blk <= end) && (dblock_addr(blk) != BLOCK_END)) { len = dblock_len(blk); total_len += sizeof(*blk) + len; blk = (struct dblock *) &blk->data[len]; } return total_len; } /*** Hermes programming ***/ /* Program the data blocks */ int hermes_program(struct hermes *hw, const char *first_block, const void *end) { const struct dblock *blk; u32 blkaddr; u32 blklen; int err = 0; blk = (const struct dblock *) first_block; if ((void *) blk > (end - sizeof(*blk))) return -EIO; blkaddr = dblock_addr(blk); blklen = dblock_len(blk); while ((blkaddr != BLOCK_END) && (((void *) blk + blklen) <= end)) { pr_debug(PFX "Programming block of length %d " "to address 0x%08x\n", blklen, blkaddr); err = hw->ops->program(hw, blk->data, blkaddr, blklen); if (err) break; blk = (const struct dblock *) &blk->data[blklen]; if ((void *) blk > (end - sizeof(*blk))) return -EIO; blkaddr = dblock_addr(blk); blklen = dblock_len(blk); } return err; } /*** Default plugging data for Hermes I ***/ /* Values from wl_lkm_718/hcf/dhf.c */ #define DEFINE_DEFAULT_PDR(pid, length, data) \ static const struct { \ __le16 len; \ __le16 id; \ u8 val[length]; \ } __packed default_pdr_data_##pid = { \ cpu_to_le16((sizeof(default_pdr_data_##pid)/ \ sizeof(__le16)) - 1), \ cpu_to_le16(pid), \ data \ } #define DEFAULT_PDR(pid) default_pdr_data_##pid /* HWIF Compatibility */ DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); /* PPPPSign */ DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); /* PPPPProf */ DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); /* Antenna diversity */ DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); /* Modem VCO band Set-up */ DEFINE_DEFAULT_PDR(0x0160, 28, "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00"); /* Modem Rx Gain Table Values */ DEFINE_DEFAULT_PDR(0x0161, 256, "\x3F\x01\x3F\01\x3F\x01\x3F\x01" "\x3F\x01\x3F\01\x3F\x01\x3F\x01" "\x3F\x01\x3F\01\x3F\x01\x3F\x01" "\x3F\x01\x3F\01\x3F\x01\x3F\x01" "\x3F\x01\x3E\01\x3E\x01\x3D\x01" "\x3D\x01\x3C\01\x3C\x01\x3B\x01" "\x3B\x01\x3A\01\x3A\x01\x39\x01" "\x39\x01\x38\01\x38\x01\x37\x01" "\x37\x01\x36\01\x36\x01\x35\x01" "\x35\x01\x34\01\x34\x01\x33\x01" "\x33\x01\x32\x01\x32\x01\x31\x01" "\x31\x01\x30\x01\x30\x01\x7B\x01" "\x7B\x01\x7A\x01\x7A\x01\x79\x01" "\x79\x01\x78\x01\x78\x01\x77\x01" "\x77\x01\x76\x01\x76\x01\x75\x01" "\x75\x01\x74\x01\x74\x01\x73\x01" "\x73\x01\x72\x01\x72\x01\x71\x01" "\x71\x01\x70\x01\x70\x01\x68\x01" "\x68\x01\x67\x01\x67\x01\x66\x01" "\x66\x01\x65\x01\x65\x01\x57\x01" "\x57\x01\x56\x01\x56\x01\x55\x01" "\x55\x01\x54\x01\x54\x01\x53\x01" "\x53\x01\x52\x01\x52\x01\x51\x01" "\x51\x01\x50\x01\x50\x01\x48\x01" "\x48\x01\x47\x01\x47\x01\x46\x01" "\x46\x01\x45\x01\x45\x01\x44\x01" "\x44\x01\x43\x01\x43\x01\x42\x01" "\x42\x01\x41\x01\x41\x01\x40\x01" "\x40\x01\x40\x01\x40\x01\x40\x01" "\x40\x01\x40\x01\x40\x01\x40\x01" "\x40\x01\x40\x01\x40\x01\x40\x01" "\x40\x01\x40\x01\x40\x01\x40\x01"); /* Write PDA according to certain rules. * * For every production data record, look for a previous setting in * the pda, and use that. * * For certain records, use defaults if they are not found in pda. */ int hermes_apply_pda_with_defaults(struct hermes *hw, const char *first_pdr, const void *pdr_end, const __le16 *pda, const void *pda_end) { const struct pdr *pdr = (const struct pdr *) first_pdr; const struct pdi *first_pdi = (const struct pdi *) &pda[2]; const struct pdi *pdi; const struct pdi *default_pdi = NULL; const struct pdi *outdoor_pdi; int record_id; pdr_end -= sizeof(struct pdr); while (((void *) pdr <= pdr_end) && (pdr_id(pdr) != PDI_END)) { /* * For spectrum_cs firmwares, * PDR area is currently not terminated by PDI_END. * It's followed by CRC records, which have the type * field where PDR has length. The type can be 0 or 1. */ if (pdr_len(pdr) < 2) break; record_id = pdr_id(pdr); pdi = hermes_find_pdi(first_pdi, record_id, pda_end); if (pdi) pr_debug(PFX "Found record 0x%04x at %p\n", record_id, pdi); switch (record_id) { case 0x110: /* Modem REFDAC values */ case 0x120: /* Modem VGDAC values */ outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1, pda_end); default_pdi = NULL; if (outdoor_pdi) { pdi = outdoor_pdi; pr_debug(PFX "Using outdoor record 0x%04x at %p\n", record_id + 1, pdi); } break; case 0x5: /* HWIF Compatibility */ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); break; case 0x108: /* PPPPSign */ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108); break; case 0x109: /* PPPPProf */ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109); break; case 0x150: /* Antenna diversity */ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150); break; case 0x160: /* Modem VCO band Set-up */ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160); break; case 0x161: /* Modem Rx Gain Table Values */ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161); break; default: default_pdi = NULL; break; } if (!pdi && default_pdi) { /* Use default */ pdi = default_pdi; pr_debug(PFX "Using default record 0x%04x at %p\n", record_id, pdi); } if (pdi) { /* Lengths of the data in PDI and PDR must match */ if ((pdi_len(pdi) == pdr_len(pdr)) && ((void *) pdi->data + pdi_len(pdi) < pda_end)) { /* do the actual plugging */ hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi)); } } pdr++; } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/orinoco/fw.h0000644000175000017500000000107112026211315022715 0ustar mcgrofmcgrof/* Firmware file reading and download helpers * * See copyright notice in main.c */ #ifndef _ORINOCO_FW_H_ #define _ORINOCO_FW_H_ /* Forward declations */ struct orinoco_private; int orinoco_download(struct orinoco_private *priv); #if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) void orinoco_cache_fw(struct orinoco_private *priv, int ap); void orinoco_uncache_fw(struct orinoco_private *priv); #else #define orinoco_cache_fw(priv, ap) do { } while (0) #define orinoco_uncache_fw(priv) do { } while (0) #endif #endif /* _ORINOCO_FW_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/cfg.h0000644000175000017500000000046412026211315023045 0ustar mcgrofmcgrof/* cfg80211 support. * * See copyright notice in main.c */ #ifndef ORINOCO_CFG_H #define ORINOCO_CFG_H #include extern const struct cfg80211_ops orinoco_cfg_ops; void orinoco_wiphy_init(struct wiphy *wiphy); int orinoco_wiphy_register(struct wiphy *wiphy); #endif /* ORINOCO_CFG_H */ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/cfg.c0000644000175000017500000001476712026211315023053 0ustar mcgrofmcgrof/* cfg80211 support * * See copyright notice in main.c */ #include #include #include "hw.h" #include "main.h" #include "orinoco.h" #include "cfg.h" /* Supported bitrates. Must agree with hw.c */ static struct ieee80211_rate orinoco_rates[] = { { .bitrate = 10 }, { .bitrate = 20 }, { .bitrate = 55 }, { .bitrate = 110 }, }; static const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid; /* Called after orinoco_private is allocated. */ void orinoco_wiphy_init(struct wiphy *wiphy) { struct orinoco_private *priv = wiphy_priv(wiphy); wiphy->privid = orinoco_wiphy_privid; set_wiphy_dev(wiphy, priv->dev); } /* Called after firmware is initialised */ int orinoco_wiphy_register(struct wiphy *wiphy) { struct orinoco_private *priv = wiphy_priv(wiphy); int i, channels = 0; if (priv->firmware_type == FIRMWARE_TYPE_AGERE) wiphy->max_scan_ssids = 1; else wiphy->max_scan_ssids = 0; wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); /* TODO: should we set if we only have demo ad-hoc? * (priv->has_port3) */ if (priv->has_ibss) wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); if (!priv->broken_monitor || force_monitor) wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); priv->band.bitrates = orinoco_rates; priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates); /* Only support channels allowed by the card EEPROM */ for (i = 0; i < NUM_CHANNELS; i++) { if (priv->channel_mask & (1 << i)) { priv->channels[i].center_freq = ieee80211_dsss_chan_to_freq(i + 1); channels++; } } priv->band.channels = priv->channels; priv->band.n_channels = channels; wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; i = 0; if (priv->has_wep) { priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40; i++; if (priv->has_big_wep) { priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104; i++; } } if (priv->has_wpa) { priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP; i++; } wiphy->cipher_suites = priv->cipher_suites; wiphy->n_cipher_suites = i; wiphy->rts_threshold = priv->rts_thresh; if (!priv->has_mwo) wiphy->frag_threshold = priv->frag_thresh + 1; wiphy->retry_short = priv->short_retry_limit; wiphy->retry_long = priv->long_retry_limit; return wiphy_register(wiphy); } static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct orinoco_private *priv = wiphy_priv(wiphy); int err = 0; unsigned long lock; if (orinoco_lock(priv, &lock) != 0) return -EBUSY; switch (type) { case NL80211_IFTYPE_ADHOC: if (!priv->has_ibss && !priv->has_port3) err = -EINVAL; break; case NL80211_IFTYPE_STATION: break; case NL80211_IFTYPE_MONITOR: if (priv->broken_monitor && !force_monitor) { wiphy_warn(wiphy, "Monitor mode support is buggy in this firmware, not enabling\n"); err = -EINVAL; } break; default: err = -EINVAL; } if (!err) { priv->iw_mode = type; set_port_type(priv); err = orinoco_commit(priv); } orinoco_unlock(priv, &lock); return err; } static int orinoco_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct orinoco_private *priv = wiphy_priv(wiphy); int err; if (!request) return -EINVAL; if (priv->scan_request && priv->scan_request != request) return -EBUSY; priv->scan_request = request; err = orinoco_hw_trigger_scan(priv, request->ssids); /* On error the we aren't processing the request */ if (err) priv->scan_request = NULL; return err; } static int orinoco_set_monitor_channel(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct orinoco_private *priv = wiphy_priv(wiphy); int err = 0; unsigned long flags; int channel; if (!chan) return -EINVAL; if (channel_type != NL80211_CHAN_NO_HT) return -EINVAL; if (chan->band != IEEE80211_BAND_2GHZ) return -EINVAL; channel = ieee80211_freq_to_dsss_chan(chan->center_freq); if ((channel < 1) || (channel > NUM_CHANNELS) || !(priv->channel_mask & (1 << (channel - 1)))) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->channel = channel; if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Fast channel change - no commit if successful */ struct hermes *hw = &priv->hw; err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_SET_CHANNEL, channel, NULL); } orinoco_unlock(priv, &flags); return err; } static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct orinoco_private *priv = wiphy_priv(wiphy); int frag_value = -1; int rts_value = -1; int err = 0; if (changed & WIPHY_PARAM_RETRY_SHORT) { /* Setting short retry not supported */ err = -EINVAL; } if (changed & WIPHY_PARAM_RETRY_LONG) { /* Setting long retry not supported */ err = -EINVAL; } if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { /* Set fragmentation */ if (priv->has_mwo) { if (wiphy->frag_threshold < 0) frag_value = 0; else { printk(KERN_WARNING "%s: Fixed fragmentation " "is not supported on this firmware. " "Using MWO robust instead.\n", priv->ndev->name); frag_value = 1; } } else { if (wiphy->frag_threshold < 0) frag_value = 2346; else if ((wiphy->frag_threshold < 257) || (wiphy->frag_threshold > 2347)) err = -EINVAL; else /* cfg80211 value is 257-2347 (odd only) * orinoco rid has range 256-2346 (even only) */ frag_value = wiphy->frag_threshold & ~0x1; } } if (changed & WIPHY_PARAM_RTS_THRESHOLD) { /* Set RTS. * * Prism documentation suggests default of 2432, * and a range of 0-3000. * * Current implementation uses 2347 as the default and * the upper limit. */ if (wiphy->rts_threshold < 0) rts_value = 2347; else if (wiphy->rts_threshold > 2347) err = -EINVAL; else rts_value = wiphy->rts_threshold; } if (!err) { unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (frag_value >= 0) { if (priv->has_mwo) priv->mwo_robust = frag_value; else priv->frag_thresh = frag_value; } if (rts_value >= 0) priv->rts_thresh = rts_value; err = orinoco_commit(priv); orinoco_unlock(priv, &flags); } return err; } const struct cfg80211_ops orinoco_cfg_ops = { .change_virtual_intf = orinoco_change_vif, .set_monitor_channel = orinoco_set_monitor_channel, .scan = orinoco_scan, .set_wiphy_params = orinoco_set_wiphy_params, }; compat-drivers-2012-09-18/drivers/net/wireless/orinoco/airport.c0000644000175000017500000001415412026211315023762 0ustar mcgrofmcgrof/* airport.c * * A driver for "Hermes" chipset based Apple Airport wireless * card. * * Copyright notice & release notes in file main.c * * Note specific to airport stub: * * 0.05 : first version of the new split driver * 0.06 : fix possible hang on powerup, add sleep support */ #define DRIVER_NAME "airport" #define PFX DRIVER_NAME ": " #include #include #include #include #include #include "orinoco.h" #define AIRPORT_IO_LEN (0x1000) /* one page */ struct airport { struct macio_dev *mdev; void __iomem *vaddr; unsigned int irq; int irq_requested; int ndev_registered; }; static int airport_suspend(struct macio_dev *mdev, pm_message_t state) { struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); struct net_device *dev = priv->ndev; struct airport *card = priv->card; unsigned long flags; int err; printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name); err = orinoco_lock(priv, &flags); if (err) { printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n", dev->name); return 0; } orinoco_down(priv); orinoco_unlock(priv, &flags); disable_irq(card->irq); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0); return 0; } static int airport_resume(struct macio_dev *mdev) { struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); struct net_device *dev = priv->ndev; struct airport *card = priv->card; unsigned long flags; int err; printk(KERN_DEBUG "%s: Airport waking up\n", dev->name); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1); msleep(200); enable_irq(card->irq); priv->hw.ops->lock_irqsave(&priv->lock, &flags); err = orinoco_up(priv); priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); return err; } static int airport_detach(struct macio_dev *mdev) { struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); struct airport *card = priv->card; if (card->ndev_registered) orinoco_if_del(priv); card->ndev_registered = 0; if (card->irq_requested) free_irq(card->irq, priv); card->irq_requested = 0; if (card->vaddr) iounmap(card->vaddr); card->vaddr = NULL; macio_release_resource(mdev, 0); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0); ssleep(1); macio_set_drvdata(mdev, NULL); free_orinocodev(priv); return 0; } static int airport_hard_reset(struct orinoco_private *priv) { /* It would be nice to power cycle the Airport for a real hard * reset, but for some reason although it appears to * re-initialize properly, it falls in a screaming heap * shortly afterwards. */ #if 0 struct airport *card = priv->card; /* Vitally important. If we don't do this it seems we get an * interrupt somewhere during the power cycle, since * hw_unavailable is already set it doesn't get ACKed, we get * into an interrupt loop and the PMU decides to turn us * off. */ disable_irq(card->irq); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 0); ssleep(1); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 1); ssleep(1); enable_irq(card->irq); ssleep(1); #endif return 0; } static int airport_attach(struct macio_dev *mdev, const struct of_device_id *match) { struct orinoco_private *priv; struct airport *card; unsigned long phys_addr; struct hermes *hw; if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) { printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n"); return -ENODEV; } /* Allocate space for private device-specific data */ priv = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev, airport_hard_reset, NULL); if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); return -ENODEV; } card = priv->card; hw = &priv->hw; card->mdev = mdev; if (macio_request_resource(mdev, 0, DRIVER_NAME)) { printk(KERN_ERR PFX "can't request IO resource !\n"); free_orinocodev(priv); return -EBUSY; } macio_set_drvdata(mdev, priv); /* Setup interrupts & base address */ card->irq = macio_irq(mdev, 0); phys_addr = macio_resource_start(mdev, 0); /* Physical address */ printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr); card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); if (!card->vaddr) { printk(KERN_ERR PFX "ioremap() failed\n"); goto failed; } hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING); /* Power up card */ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1); ssleep(1); /* Reset it before we get the interrupt */ hw->ops->init(hw); if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) { printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq); goto failed; } card->irq_requested = 1; /* Initialise the main driver */ if (orinoco_init(priv) != 0) { printk(KERN_ERR PFX "orinoco_init() failed\n"); goto failed; } /* Register an interface with the stack */ if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } card->ndev_registered = 1; return 0; failed: airport_detach(mdev); return -ENODEV; } /* airport_attach */ static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION " (Benjamin Herrenschmidt )"; MODULE_AUTHOR("Benjamin Herrenschmidt "); MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); MODULE_LICENSE("Dual MPL/GPL"); static struct of_device_id airport_match[] = { { .name = "radio", }, {}, }; MODULE_DEVICE_TABLE(of, airport_match); static struct macio_driver airport_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = airport_match, }, .probe = airport_attach, .remove = airport_detach, .suspend = airport_suspend, .resume = airport_resume, }; static int __init init_airport(void) { printk(KERN_DEBUG "%s\n", version); return macio_register_driver(&airport_driver); } static void __exit exit_airport(void) { macio_unregister_driver(&airport_driver); } module_init(init_airport); module_exit(exit_airport); compat-drivers-2012-09-18/drivers/net/wireless/orinoco/Makefile0000644000175000017500000000111512026211315023567 0ustar mcgrofmcgrof# # Makefile for the orinoco wireless device drivers. # orinoco-objs := main.o fw.o hw.o mic.o scan.o wext.o hermes_dld.o hermes.o cfg.o obj-$(CONFIG_HERMES) += orinoco.o obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o obj-$(CONFIG_APPLE_AIRPORT) += airport.o obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o obj-$(CONFIG_ORINOCO_USB) += orinoco_usb.o # Orinoco should be endian clean. ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/orinoco/Kconfig0000644000175000017500000001277012026211315023443 0ustar mcgrofmcgrofconfig HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" depends on (PPC_PMAC || PCI || PCMCIA) depends on CFG80211 && CFG80211_WEXT select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV select FW_LOADER select CRYPTO select CRYPTO_MICHAEL_MIC ---help--- A driver for 802.11b wireless cards based on the "Hermes" or Intersil HFA384x (Prism 2) MAC controller. This includes the vast majority of the PCMCIA 802.11b cards (which are nearly all rebadges) - except for the Cisco/Aironet cards. Cards supported include the Apple Airport (not a PCMCIA card), WavelanIEEE/Orinoco, Cabletron/EnteraSys Roamabout, ELSA AirLancer, MELCO Buffalo, Avaya, IBM High Rate Wireless, Farralon Syyline, Samsung MagicLAN, Netgear MA401, LinkSys WPC-11, D-Link DWL-650, 3Com AirConnect, Intel IPW2011, and Symbol Spectrum24 High Rate amongst others. This option includes the guts of the driver, but in order to actually use a card you will also need to enable support for PCMCIA Hermes cards, PLX9052 based PCI adaptors or the Apple Airport below. You will also very likely also need the Wireless Tools in order to configure your card and that /etc/pcmcia/wireless.opts works : config HERMES_PRISM bool "Support Prism 2/2.5 chipset" depends on HERMES ---help--- Say Y to enable support for Prism 2 and 2.5 chipsets. These chipsets are better handled by the hostap driver. This driver would not support WPA or firmware download for Prism chipset. If you are not sure, say N. config HERMES_CACHE_FW_ON_INIT bool "Cache Hermes firmware on driver initialisation" depends on HERMES default y ---help--- Say Y to cache any firmware required by the Hermes drivers on startup. The firmware will remain cached until the driver is unloaded. The cache uses 64K of RAM. Otherwise load the firmware from userspace as required. In this case the driver should be unloaded and restarted whenever the firmware is changed. If you are not sure, say Y. config APPLE_AIRPORT tristate "Apple Airport support (built-in)" depends on PPC_PMAC && HERMES help Say Y here to support the Airport 802.11b wireless Ethernet hardware built into the Macintosh iBook and other recent PowerPC-based Macintosh machines. This is essentially a Lucent Orinoco card with a non-standard interface. This driver does not support the Airport Extreme (802.11b/g). Use the BCM43xx driver for Airport Extreme cards. config PLX_HERMES tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)" depends on PCI && HERMES help Enable support for PCMCIA cards supported by the "Hermes" (aka orinoco) driver when used in PLX9052 based PCI adaptors. These adaptors are not a full PCMCIA controller but act as a more limited PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that 802.11b PCMCIA cards can be used in desktop machines. The Netgear MA301 is such an adaptor. config TMD_HERMES tristate "Hermes in TMD7160 based PCI adaptor support" depends on PCI && HERMES help Enable support for PCMCIA cards supported by the "Hermes" (aka orinoco) driver when used in TMD7160 based PCI adaptors. These adaptors are not a full PCMCIA controller but act as a more limited PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that 802.11b PCMCIA cards can be used in desktop machines. config NORTEL_HERMES tristate "Nortel emobility PCI adaptor support" depends on PCI && HERMES help Enable support for PCMCIA cards supported by the "Hermes" (aka orinoco) driver when used in Nortel emobility PCI adaptors. These adaptors are not full PCMCIA controllers, but act as a more limited PCI <-> PCMCIA bridge. config PCI_HERMES tristate "Prism 2.5 PCI 802.11b adaptor support" depends on PCI && HERMES && HERMES_PRISM help Enable support for PCI and mini-PCI 802.11b wireless NICs based on the Prism 2.5 chipset. These are true PCI cards, not the 802.11b PCMCIA cards bundled with PCI<->PCMCIA adaptors which are also common. Some of the built-in wireless adaptors in laptops are of this variety. config PCMCIA_HERMES tristate "Hermes PCMCIA card support" depends on PCMCIA && HERMES ---help--- A driver for "Hermes" chipset based PCMCIA wireless adaptors, such as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others). It should also be usable on various Prism II based cards such as the Linksys, D-Link and Farallon Skyline. It should also work on Symbol cards such as the 3Com AirConnect and Ericsson WLAN. You will very likely need the Wireless Tools in order to configure your card and that /etc/pcmcia/wireless.opts works: . config PCMCIA_SPECTRUM tristate "Symbol Spectrum24 Trilogy PCMCIA card support" depends on PCMCIA && HERMES ---help--- This is a driver for 802.11b cards using RAM-loadable Symbol firmware, such as Symbol Wireless Networker LA4100, CompactFlash cards by Socket Communications and Intel PRO/Wireless 2011B. This driver requires firmware download on startup. Utilities for downloading Symbol firmware are available at config ORINOCO_USB tristate "Agere Orinoco USB support" depends on USB && HERMES select FW_LOADER ---help--- This driver is for USB versions of the Agere Orinoco card. compat-drivers-2012-09-18/drivers/net/wireless/ti/0000755000175000017500000000000012026211315021075 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ti/Makefile0000644000175000017500000000026612026211315022541 0ustar mcgrofmcgrofobj-$(CONFIG_WLCORE) += wlcore/ obj-$(CONFIG_WL12XX) += wl12xx/ obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wlcore/ obj-$(CONFIG_WL1251) += wl1251/ obj-$(CONFIG_WL18XX) += wl18xx/ compat-drivers-2012-09-18/drivers/net/wireless/ti/Kconfig0000644000175000017500000000071112026211315022377 0ustar mcgrofmcgrofmenuconfig WL_TI bool "TI Wireless LAN support" ---help--- This section contains support for all the wireless drivers for Texas Instruments WLAN chips, such as wl1251 and the wl12xx family. if WL_TI source "drivers/net/wireless/ti/wl1251/Kconfig" source "drivers/net/wireless/ti/wl12xx/Kconfig" source "drivers/net/wireless/ti/wl18xx/Kconfig" # keep last for automatic dependencies source "drivers/net/wireless/ti/wlcore/Kconfig" endif # WL_TI compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/0000755000175000017500000000000012026211315022250 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/wl18xx.h0000644000175000017500000000413512026211315023577 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL18XX_PRIV_H__ #define __WL18XX_PRIV_H__ #include "conf.h" /* minimum FW required for driver */ #define WL18XX_CHIP_VER 8 #define WL18XX_IFTYPE_VER 2 #define WL18XX_MAJOR_VER 0 #define WL18XX_SUBTYPE_VER 0 #define WL18XX_MINOR_VER 100 #define WL18XX_CMD_MAX_SIZE 740 struct wl18xx_priv { /* buffer for sending commands to FW */ u8 cmd_buf[WL18XX_CMD_MAX_SIZE]; struct wl18xx_priv_conf conf; /* Index of last released Tx desc in FW */ u8 last_fw_rls_idx; /* number of VIFs requiring extra spare mem-blocks */ int extra_spare_vif_count; }; #define WL18XX_FW_MAX_TX_STATUS_DESC 33 struct wl18xx_fw_status_priv { /* * Index in released_tx_desc for first byte that holds * released tx host desc */ u8 fw_release_idx; /* * Array of host Tx descriptors, where fw_release_idx * indicated the first released idx. */ u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC]; u8 padding[2]; }; #define WL18XX_PHY_VERSION_MAX_LEN 20 struct wl18xx_static_data_priv { char phy_version[WL18XX_PHY_VERSION_MAX_LEN]; }; struct wl18xx_clk_cfg { u32 n; u32 m; u32 p; u32 q; bool swallow; }; enum { CLOCK_CONFIG_16_2_M = 1, CLOCK_CONFIG_16_368_M, CLOCK_CONFIG_16_8_M, CLOCK_CONFIG_19_2_M, CLOCK_CONFIG_26_M, CLOCK_CONFIG_32_736_M, CLOCK_CONFIG_33_6_M, CLOCK_CONFIG_38_468_M, CLOCK_CONFIG_52_M, NUM_CLOCK_CONFIGS, }; #endif /* __WL18XX_PRIV_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/tx.h0000644000175000017500000000264312026211315023061 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL18XX_TX_H__ #define __WL18XX_TX_H__ #include "../wlcore/wlcore.h" #define WL18XX_TX_HW_BLOCK_SPARE 1 /* for special cases - namely, TKIP and GEM */ #define WL18XX_TX_HW_EXTRA_BLOCK_SPARE 2 #define WL18XX_TX_HW_BLOCK_SIZE 268 #define WL18XX_TX_STATUS_DESC_ID_MASK 0x7F #define WL18XX_TX_STATUS_STAT_BIT_IDX 7 /* Indicates this TX HW frame is not padded to SDIO block size */ #define WL18XX_TX_CTRL_NOT_PADDED BIT(7) /* * The FW uses a special bit to indicate a wide channel should be used in * the rate policy. */ #define CONF_TX_RATE_USE_WIDE_CHAN BIT(31) void wl18xx_tx_immediate_complete(struct wl1271 *wl); #endif /* __WL12XX_TX_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/tx.c0000644000175000017500000000711212026211315023050 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "../wlcore/wlcore.h" #include "../wlcore/cmd.h" #include "../wlcore/debug.h" #include "../wlcore/acx.h" #include "../wlcore/tx.h" #include "wl18xx.h" #include "tx.h" static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) { struct ieee80211_tx_info *info; struct sk_buff *skb; int id = tx_stat_byte & WL18XX_TX_STATUS_DESC_ID_MASK; bool tx_success; /* check for id legality */ if (unlikely(id >= wl->num_tx_desc || wl->tx_frames[id] == NULL)) { wl1271_warning("illegal id in tx completion: %d", id); return; } /* a zero bit indicates Tx success */ tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX)); skb = wl->tx_frames[id]; info = IEEE80211_SKB_CB(skb); if (wl12xx_is_dummy_packet(wl, skb)) { wl1271_free_tx_id(wl, id); return; } /* update the TX status info */ if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_ACK; /* no real data about Tx completion */ info->status.rates[0].idx = -1; info->status.rates[0].count = 0; info->status.rates[0].flags = 0; info->status.ack_signal = -1; if (!tx_success) wl->stats.retry_count++; /* * TODO: update sequence number for encryption? seems to be * unsupported for now. needed for recovery with encryption. */ /* remove private header from packet */ skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); /* remove TKIP header space if present */ if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen); skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); } wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p success %d", id, skb, tx_success); /* return the packet to the stack */ skb_queue_tail(&wl->deferred_tx_queue, skb); queue_work(wl->freezable_wq, &wl->netstack_work); wl1271_free_tx_id(wl, id); } void wl18xx_tx_immediate_complete(struct wl1271 *wl) { struct wl18xx_fw_status_priv *status_priv = (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; struct wl18xx_priv *priv = wl->priv; u8 i; /* nothing to do here */ if (priv->last_fw_rls_idx == status_priv->fw_release_idx) return; /* freed Tx descriptors */ wl1271_debug(DEBUG_TX, "last released desc = %d, current idx = %d", priv->last_fw_rls_idx, status_priv->fw_release_idx); if (status_priv->fw_release_idx >= WL18XX_FW_MAX_TX_STATUS_DESC) { wl1271_error("invalid desc release index %d", status_priv->fw_release_idx); WARN_ON(1); return; } for (i = priv->last_fw_rls_idx; i != status_priv->fw_release_idx; i = (i + 1) % WL18XX_FW_MAX_TX_STATUS_DESC) { wl18xx_tx_complete_packet(wl, status_priv->released_tx_desc[i]); wl->tx_results_count++; } priv->last_fw_rls_idx = status_priv->fw_release_idx; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/reg.h0000644000175000017500000001575212026211315023210 0ustar mcgrofmcgrof/* * This file is part of wlcore * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __REG_H__ #define __REG_H__ #define WL18XX_REGISTERS_BASE 0x00800000 #define WL18XX_CODE_BASE 0x00000000 #define WL18XX_DATA_BASE 0x00400000 #define WL18XX_DOUBLE_BUFFER_BASE 0x00600000 #define WL18XX_MCU_KEY_SEARCH_BASE 0x00700000 #define WL18XX_PHY_BASE 0x00900000 #define WL18XX_TOP_OCP_BASE 0x00A00000 #define WL18XX_PACKET_RAM_BASE 0x00B00000 #define WL18XX_HOST_BASE 0x00C00000 #define WL18XX_REGISTERS_DOWN_SIZE 0x0000B000 #define WL18XX_REG_BOOT_PART_START 0x00802000 #define WL18XX_REG_BOOT_PART_SIZE 0x00014578 #define WL18XX_PHY_INIT_MEM_ADDR 0x80926000 #define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE) #define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000) #define WL18XX_WGCM_REGS_BASE (WL18XX_REGISTERS_BASE + 0x03000) #define WL18XX_ENC_BASE (WL18XX_REGISTERS_BASE + 0x04000) #define WL18XX_INTERRUPT_BASE (WL18XX_REGISTERS_BASE + 0x05000) #define WL18XX_UART_BASE (WL18XX_REGISTERS_BASE + 0x06000) #define WL18XX_WELP_BASE (WL18XX_REGISTERS_BASE + 0x07000) #define WL18XX_TCP_CKSM_BASE (WL18XX_REGISTERS_BASE + 0x08000) #define WL18XX_FIFO_BASE (WL18XX_REGISTERS_BASE + 0x09000) #define WL18XX_OCP_BRIDGE_BASE (WL18XX_REGISTERS_BASE + 0x0A000) #define WL18XX_PMAC_RX_BASE (WL18XX_REGISTERS_BASE + 0x14800) #define WL18XX_PMAC_ACM_BASE (WL18XX_REGISTERS_BASE + 0x14C00) #define WL18XX_PMAC_TX_BASE (WL18XX_REGISTERS_BASE + 0x15000) #define WL18XX_PMAC_CSR_BASE (WL18XX_REGISTERS_BASE + 0x15400) #define WL18XX_REG_ECPU_CONTROL (WL18XX_REGISTERS_BASE + 0x02004) #define WL18XX_REG_INTERRUPT_NO_CLEAR (WL18XX_REGISTERS_BASE + 0x050E8) #define WL18XX_REG_INTERRUPT_ACK (WL18XX_REGISTERS_BASE + 0x050F0) #define WL18XX_REG_INTERRUPT_TRIG (WL18XX_REGISTERS_BASE + 0x5074) #define WL18XX_REG_INTERRUPT_TRIG_H (WL18XX_REGISTERS_BASE + 0x5078) #define WL18XX_REG_INTERRUPT_MASK (WL18XX_REGISTERS_BASE + 0x0050DC) #define WL18XX_REG_CHIP_ID_B (WL18XX_REGISTERS_BASE + 0x01542C) #define WL18XX_SLV_MEM_DATA (WL18XX_HOST_BASE + 0x0018) #define WL18XX_SLV_REG_DATA (WL18XX_HOST_BASE + 0x0008) /* Scratch Pad registers*/ #define WL18XX_SCR_PAD0 (WL18XX_REGISTERS_BASE + 0x0154EC) #define WL18XX_SCR_PAD1 (WL18XX_REGISTERS_BASE + 0x0154F0) #define WL18XX_SCR_PAD2 (WL18XX_REGISTERS_BASE + 0x0154F4) #define WL18XX_SCR_PAD3 (WL18XX_REGISTERS_BASE + 0x0154F8) #define WL18XX_SCR_PAD4 (WL18XX_REGISTERS_BASE + 0x0154FC) #define WL18XX_SCR_PAD4_SET (WL18XX_REGISTERS_BASE + 0x015504) #define WL18XX_SCR_PAD4_CLR (WL18XX_REGISTERS_BASE + 0x015500) #define WL18XX_SCR_PAD5 (WL18XX_REGISTERS_BASE + 0x015508) #define WL18XX_SCR_PAD5_SET (WL18XX_REGISTERS_BASE + 0x015510) #define WL18XX_SCR_PAD5_CLR (WL18XX_REGISTERS_BASE + 0x01550C) #define WL18XX_SCR_PAD6 (WL18XX_REGISTERS_BASE + 0x015514) #define WL18XX_SCR_PAD7 (WL18XX_REGISTERS_BASE + 0x015518) #define WL18XX_SCR_PAD8 (WL18XX_REGISTERS_BASE + 0x01551C) #define WL18XX_SCR_PAD9 (WL18XX_REGISTERS_BASE + 0x015520) /* Spare registers*/ #define WL18XX_SPARE_A1 (WL18XX_REGISTERS_BASE + 0x002194) #define WL18XX_SPARE_A2 (WL18XX_REGISTERS_BASE + 0x002198) #define WL18XX_SPARE_A3 (WL18XX_REGISTERS_BASE + 0x00219C) #define WL18XX_SPARE_A4 (WL18XX_REGISTERS_BASE + 0x0021A0) #define WL18XX_SPARE_A5 (WL18XX_REGISTERS_BASE + 0x0021A4) #define WL18XX_SPARE_A6 (WL18XX_REGISTERS_BASE + 0x0021A8) #define WL18XX_SPARE_A7 (WL18XX_REGISTERS_BASE + 0x0021AC) #define WL18XX_SPARE_A8 (WL18XX_REGISTERS_BASE + 0x0021B0) #define WL18XX_SPARE_B1 (WL18XX_REGISTERS_BASE + 0x015524) #define WL18XX_SPARE_B2 (WL18XX_REGISTERS_BASE + 0x015528) #define WL18XX_SPARE_B3 (WL18XX_REGISTERS_BASE + 0x01552C) #define WL18XX_SPARE_B4 (WL18XX_REGISTERS_BASE + 0x015530) #define WL18XX_SPARE_B5 (WL18XX_REGISTERS_BASE + 0x015534) #define WL18XX_SPARE_B6 (WL18XX_REGISTERS_BASE + 0x015538) #define WL18XX_SPARE_B7 (WL18XX_REGISTERS_BASE + 0x01553C) #define WL18XX_SPARE_B8 (WL18XX_REGISTERS_BASE + 0x015540) #define WL18XX_REG_COMMAND_MAILBOX_PTR (WL18XX_SCR_PAD0) #define WL18XX_REG_EVENT_MAILBOX_PTR (WL18XX_SCR_PAD1) #define WL18XX_EEPROMLESS_IND (WL18XX_SCR_PAD4) #define WL18XX_WELP_ARM_COMMAND (WL18XX_REGISTERS_BASE + 0x7100) #define WL18XX_ENABLE (WL18XX_REGISTERS_BASE + 0x01543C) /* PRCM registers */ #define PLATFORM_DETECTION 0xA0E3E0 #define OCS_EN 0xA02080 #define PRIMARY_CLK_DETECT 0xA020A6 #define PLLSH_WCS_PLL_N 0xA02362 #define PLLSH_WCS_PLL_M 0xA02360 #define PLLSH_WCS_PLL_Q_FACTOR_CFG_1 0xA02364 #define PLLSH_WCS_PLL_Q_FACTOR_CFG_2 0xA02366 #define PLLSH_WCS_PLL_P_FACTOR_CFG_1 0xA02368 #define PLLSH_WCS_PLL_P_FACTOR_CFG_2 0xA0236A #define PLLSH_WCS_PLL_SWALLOW_EN 0xA0236C #define PLLSH_WL_PLL_EN 0xA02392 #define PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK 0xFFFF #define PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK 0x007F #define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK 0xFFFF #define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK 0x000F #define PLLSH_WCS_PLL_SWALLOW_EN_VAL1 0x1 #define PLLSH_WCS_PLL_SWALLOW_EN_VAL2 0x12 #define WL18XX_REG_FUSE_DATA_1_3 0xA0260C #define WL18XX_PG_VER_MASK 0x70 #define WL18XX_PG_VER_OFFSET 4 #define WL18XX_REG_FUSE_BD_ADDR_1 0xA02602 #define WL18XX_REG_FUSE_BD_ADDR_2 0xA02606 #define WL18XX_CMD_MBOX_ADDRESS 0xB007B4 #define WL18XX_FW_STATUS_ADDR 0x50F8 #define CHIP_ID_185x_PG10 (0x06030101) #define CHIP_ID_185x_PG20 (0x06030111) /* * Host Command Interrupt. Setting this bit masks * the interrupt that the host issues to inform * the FW that it has sent a command * to the Wlan hardware Command Mailbox. */ #define WL18XX_INTR_TRIG_CMD BIT(28) /* * Host Event Acknowlegde Interrupt. The host * sets this bit to acknowledge that it received * the unsolicited information from the event * mailbox. */ #define WL18XX_INTR_TRIG_EVENT_ACK BIT(29) /* * To boot the firmware in PLT mode we need to write this value in * SCR_PAD8 before starting. */ #define WL18XX_SCR_PAD8_PLT 0xBABABEBE enum { COMPONENT_NO_SWITCH = 0x0, COMPONENT_2_WAY_SWITCH = 0x1, COMPONENT_3_WAY_SWITCH = 0x2, COMPONENT_MATCHING = 0x3, }; enum { FEM_NONE = 0x0, FEM_VENDOR_1 = 0x1, FEM_VENDOR_2 = 0x2, FEM_VENDOR_3 = 0x3, }; enum { BOARD_TYPE_EVB_18XX = 0, BOARD_TYPE_DVP_18XX = 1, BOARD_TYPE_HDK_18XX = 2, BOARD_TYPE_FPGA_18XX = 3, BOARD_TYPE_COM8_18XX = 4, NUM_BOARD_TYPES, }; #endif /* __REG_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/main.c0000644000175000017500000013603412026211315023347 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" #include "../wlcore/io.h" #include "../wlcore/acx.h" #include "../wlcore/tx.h" #include "../wlcore/rx.h" #include "../wlcore/boot.h" #include "reg.h" #include "conf.h" #include "acx.h" #include "tx.h" #include "wl18xx.h" #include "io.h" #include "debugfs.h" #define WL18XX_RX_CHECKSUM_MASK 0x40 static char *ht_mode_param = NULL; static char *board_type_param = NULL; static bool checksum_param = false; static bool enable_11a_param = true; static int num_rx_desc_param = -1; /* phy paramters */ static int dc2dc_param = -1; static int n_antennas_2_param = -1; static int n_antennas_5_param = -1; static int low_band_component_param = -1; static int low_band_component_type_param = -1; static int high_band_component_param = -1; static int high_band_component_type_param = -1; static int pwr_limit_reference_11_abg_param = -1; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ 14, /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */ 13, /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */ 12, /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */ 11, /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */ 10, /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */ 9, /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */ 8, /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */ 7, /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */ 6, /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */ 5, /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */ 4, /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */ 3, /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */ 2, /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */ 1, /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */ 0, /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */ 11, /* WL18XX_CONF_HW_RXTX_RATE_54 */ 10, /* WL18XX_CONF_HW_RXTX_RATE_48 */ 9, /* WL18XX_CONF_HW_RXTX_RATE_36 */ 8, /* WL18XX_CONF_HW_RXTX_RATE_24 */ /* TI-specific rate */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22 */ 7, /* WL18XX_CONF_HW_RXTX_RATE_18 */ 6, /* WL18XX_CONF_HW_RXTX_RATE_12 */ 3, /* WL18XX_CONF_HW_RXTX_RATE_11 */ 5, /* WL18XX_CONF_HW_RXTX_RATE_9 */ 4, /* WL18XX_CONF_HW_RXTX_RATE_6 */ 2, /* WL18XX_CONF_HW_RXTX_RATE_5_5 */ 1, /* WL18XX_CONF_HW_RXTX_RATE_2 */ 0 /* WL18XX_CONF_HW_RXTX_RATE_1 */ }; static const u8 wl18xx_rate_to_idx_5ghz[] = { /* MCS rates are used only with 11n */ 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ 14, /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */ 13, /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */ 12, /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */ 11, /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */ 10, /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */ 9, /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */ 8, /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */ 7, /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */ 6, /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */ 5, /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */ 4, /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */ 3, /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */ 2, /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */ 1, /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */ 0, /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */ 7, /* WL18XX_CONF_HW_RXTX_RATE_54 */ 6, /* WL18XX_CONF_HW_RXTX_RATE_48 */ 5, /* WL18XX_CONF_HW_RXTX_RATE_36 */ 4, /* WL18XX_CONF_HW_RXTX_RATE_24 */ /* TI-specific rate */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22 */ 3, /* WL18XX_CONF_HW_RXTX_RATE_18 */ 2, /* WL18XX_CONF_HW_RXTX_RATE_12 */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_11 */ 1, /* WL18XX_CONF_HW_RXTX_RATE_9 */ 0, /* WL18XX_CONF_HW_RXTX_RATE_6 */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_5_5 */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_2 */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_1 */ }; static const u8 *wl18xx_band_rate_to_idx[] = { [IEEE80211_BAND_2GHZ] = wl18xx_rate_to_idx_2ghz, [IEEE80211_BAND_5GHZ] = wl18xx_rate_to_idx_5ghz }; enum wl18xx_hw_rates { WL18XX_CONF_HW_RXTX_RATE_MCS15 = 0, WL18XX_CONF_HW_RXTX_RATE_MCS14, WL18XX_CONF_HW_RXTX_RATE_MCS13, WL18XX_CONF_HW_RXTX_RATE_MCS12, WL18XX_CONF_HW_RXTX_RATE_MCS11, WL18XX_CONF_HW_RXTX_RATE_MCS10, WL18XX_CONF_HW_RXTX_RATE_MCS9, WL18XX_CONF_HW_RXTX_RATE_MCS8, WL18XX_CONF_HW_RXTX_RATE_MCS7, WL18XX_CONF_HW_RXTX_RATE_MCS6, WL18XX_CONF_HW_RXTX_RATE_MCS5, WL18XX_CONF_HW_RXTX_RATE_MCS4, WL18XX_CONF_HW_RXTX_RATE_MCS3, WL18XX_CONF_HW_RXTX_RATE_MCS2, WL18XX_CONF_HW_RXTX_RATE_MCS1, WL18XX_CONF_HW_RXTX_RATE_MCS0, WL18XX_CONF_HW_RXTX_RATE_54, WL18XX_CONF_HW_RXTX_RATE_48, WL18XX_CONF_HW_RXTX_RATE_36, WL18XX_CONF_HW_RXTX_RATE_24, WL18XX_CONF_HW_RXTX_RATE_22, WL18XX_CONF_HW_RXTX_RATE_18, WL18XX_CONF_HW_RXTX_RATE_12, WL18XX_CONF_HW_RXTX_RATE_11, WL18XX_CONF_HW_RXTX_RATE_9, WL18XX_CONF_HW_RXTX_RATE_6, WL18XX_CONF_HW_RXTX_RATE_5_5, WL18XX_CONF_HW_RXTX_RATE_2, WL18XX_CONF_HW_RXTX_RATE_1, WL18XX_CONF_HW_RXTX_RATE_MAX, }; static struct wlcore_conf wl18xx_conf = { .sg = { .params = { [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10, [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180, [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10, [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180, [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10, [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80, [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10, [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80, [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8, [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8, [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20, [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50, [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10, [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20, [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75, [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15, [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27, [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17, /* active scan params */ [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, /* passive scan params */ [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800, [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200, [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, /* passive scan in dual antenna params */ [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0, [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0, /* general params */ [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, [CONF_SG_ANTENNA_CONFIGURATION] = 0, [CONF_SG_BEACON_MISS_PERCENT] = 60, [CONF_SG_DHCP_TIME] = 5000, [CONF_SG_RXT] = 1200, [CONF_SG_TXT] = 1000, [CONF_SG_ADAPTIVE_RXT_TXT] = 1, [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, [CONF_SG_HV3_MAX_SERVED] = 6, [CONF_SG_PS_POLL_TIMEOUT] = 10, [CONF_SG_UPSD_TIMEOUT] = 10, [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5, [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30, /* AP params */ [CONF_AP_BEACON_MISS_TX] = 3, [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10, [CONF_AP_BEACON_WINDOW_INTERVAL] = 2, [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, /* CTS Diluting params */ [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, }, .state = CONF_SG_PROTECTIVE, }, .rx = { .rx_msdu_life_time = 512000, .packet_detection_threshold = 0, .ps_poll_timeout = 15, .upsd_timeout = 15, .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, .rx_cca_threshold = 0, .irq_blk_threshold = 0xFFFF, .irq_pkt_threshold = 0, .irq_timeout = 600, .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, }, .tx = { .tx_energy_detection = 0, .sta_rc_conf = { .enabled_rates = 0, .short_retry_limit = 10, .long_retry_limit = 10, .aflags = 0, }, .ac_conf_count = 4, .ac_conf = { [CONF_TX_AC_BE] = { .ac = CONF_TX_AC_BE, .cw_min = 15, .cw_max = 63, .aifsn = 3, .tx_op_limit = 0, }, [CONF_TX_AC_BK] = { .ac = CONF_TX_AC_BK, .cw_min = 15, .cw_max = 63, .aifsn = 7, .tx_op_limit = 0, }, [CONF_TX_AC_VI] = { .ac = CONF_TX_AC_VI, .cw_min = 15, .cw_max = 63, .aifsn = CONF_TX_AIFS_PIFS, .tx_op_limit = 3008, }, [CONF_TX_AC_VO] = { .ac = CONF_TX_AC_VO, .cw_min = 15, .cw_max = 63, .aifsn = CONF_TX_AIFS_PIFS, .tx_op_limit = 1504, }, }, .max_tx_retries = 100, .ap_aging_period = 300, .tid_conf_count = 4, .tid_conf = { [CONF_TX_AC_BE] = { .queue_id = CONF_TX_AC_BE, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_BE, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, [CONF_TX_AC_BK] = { .queue_id = CONF_TX_AC_BK, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_BK, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, [CONF_TX_AC_VI] = { .queue_id = CONF_TX_AC_VI, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_VI, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, [CONF_TX_AC_VO] = { .queue_id = CONF_TX_AC_VO, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_VO, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, }, .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, .tx_compl_timeout = 350, .tx_compl_threshold = 10, .basic_rate = CONF_HW_BIT_RATE_1MBPS, .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, .tmpl_short_retry_limit = 10, .tmpl_long_retry_limit = 10, .tx_watchdog_timeout = 5000, }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .listen_interval = 1, .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, .suspend_listen_interval = 3, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, .bcn_filt_ie_count = 3, .bcn_filt_ie = { [0] = { .ie = WLAN_EID_CHANNEL_SWITCH, .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, }, [1] = { .ie = WLAN_EID_HT_OPERATION, .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, [2] = { .ie = WLAN_EID_ERP_INFO, .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, }, .synch_fail_thold = 12, .bss_lose_timeout = 400, .beacon_rx_timeout = 10000, .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, .ps_poll_threshold = 10, .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 50, .psm_entry_retries = 8, .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, .dynamic_ps_timeout = 1500, .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, .sta_sleep_auth = WL1271_PSM_ILLEGAL, }, .itrim = { .enable = false, .timeout = 50000, }, .pm_config = { .host_clk_settling_time = 5000, .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE, }, .roam_trigger = { .trigger_pacing = 1, .avg_weight_rssi_beacon = 20, .avg_weight_rssi_data = 10, .avg_weight_snr_beacon = 20, .avg_weight_snr_data = 10, }, .scan = { .min_dwell_time_active = 7500, .max_dwell_time_active = 30000, .min_dwell_time_passive = 100000, .max_dwell_time_passive = 100000, .num_probe_reqs = 2, .split_scan_timeout = 50000, }, .sched_scan = { /* * Values are in TU/1000 but since sched scan FW command * params are in TUs rounding up may occur. */ .base_dwell_time = 7500, .max_dwell_time_delta = 22500, /* based on 250bits per probe @1Mbps */ .dwell_time_delta_per_probe = 2000, /* based on 250bits per probe @6Mbps (plus a bit more) */ .dwell_time_delta_per_probe_5 = 350, .dwell_time_passive = 100000, .dwell_time_dfs = 150000, .num_probe_reqs = 2, .rssi_threshold = -90, .snr_threshold = 0, }, .ht = { .rx_ba_win_size = 10, .tx_ba_win_size = 64, .inactivity_timeout = 10000, .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP, }, .mem = { .num_stations = 1, .ssid_profiles = 1, .rx_block_num = 40, .tx_min_block_num = 40, .dynamic_memory = 1, .min_req_tx_blocks = 45, .min_req_rx_blocks = 22, .tx_min = 27, }, .fm_coex = { .enable = true, .swallow_period = 5, .n_divider_fref_set_1 = 0xff, /* default */ .n_divider_fref_set_2 = 12, .m_divider_fref_set_1 = 0xffff, .m_divider_fref_set_2 = 148, /* default */ .coex_pll_stabilization_time = 0xffffffff, /* default */ .ldo_stabilization_time = 0xffff, /* default */ .fm_disturbed_band_margin = 0xff, /* default */ .swallow_clk_diff = 0xff, /* default */ }, .rx_streaming = { .duration = 150, .queues = 0x1, .interval = 20, .always = 0, }, .fwlog = { .mode = WL12XX_FWLOG_ON_DEMAND, .mem_blocks = 2, .severity = 0, .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, .output = WL12XX_FWLOG_OUTPUT_HOST, .threshold = 0, }, .rate = { .rate_retry_score = 32000, .per_add = 8192, .per_th1 = 2048, .per_th2 = 4096, .max_per = 8100, .inverse_curiosity_factor = 5, .tx_fail_low_th = 4, .tx_fail_high_th = 10, .per_alpha_shift = 4, .per_add_shift = 13, .per_beta1_shift = 10, .per_beta2_shift = 8, .rate_check_up = 2, .rate_check_down = 12, .rate_retry_policy = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, .hangover = { .recover_time = 0, .hangover_period = 20, .dynamic_mode = 1, .early_termination_mode = 1, .max_period = 20, .min_period = 1, .increase_delta = 1, .decrease_delta = 2, .quiet_time = 4, .increase_time = 1, .window_size = 16, }, }; static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .ht = { .mode = HT_MODE_DEFAULT, }, .phy = { .phy_standalone = 0x00, .primary_clock_setting_time = 0x05, .clock_valid_on_wake_up = 0x00, .secondary_clock_setting_time = 0x05, .board_type = BOARD_TYPE_HDK_18XX, .rdl = 0x01, .auto_detect = 0x00, .dedicated_fem = FEM_NONE, .low_band_component = COMPONENT_2_WAY_SWITCH, .low_band_component_type = 0x06, .high_band_component = COMPONENT_2_WAY_SWITCH, .high_band_component_type = 0x09, .tcxo_ldo_voltage = 0x00, .xtal_itrim_val = 0x04, .srf_state = 0x00, .io_configuration = 0x01, .sdio_configuration = 0x00, .settings = 0x00, .enable_clpc = 0x00, .enable_tx_low_pwr_on_siso_rdl = 0x00, .rx_profile = 0x00, .pwr_limit_reference_11_abg = 0xc8, .psat = 0, .low_power_val = 0x00, .med_power_val = 0x0a, .high_power_val = 0x1e, .external_pa_dc2dc = 0, .number_of_assembled_ant2_4 = 1, .number_of_assembled_ant5 = 1, }, }; static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { [PART_TOP_PRCM_ELP_SOC] = { .mem = { .start = 0x00A02000, .size = 0x00010000 }, .reg = { .start = 0x00807000, .size = 0x00005000 }, .mem2 = { .start = 0x00800000, .size = 0x0000B000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, [PART_DOWN] = { .mem = { .start = 0x00000000, .size = 0x00014000 }, .reg = { .start = 0x00810000, .size = 0x0000BFFF }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, [PART_BOOT] = { .mem = { .start = 0x00700000, .size = 0x0000030c }, .reg = { .start = 0x00802000, .size = 0x00014578 }, .mem2 = { .start = 0x00B00404, .size = 0x00001000 }, .mem3 = { .start = 0x00C00000, .size = 0x00000400 }, }, [PART_WORK] = { .mem = { .start = 0x00800000, .size = 0x000050FC }, .reg = { .start = 0x00B00404, .size = 0x00001000 }, .mem2 = { .start = 0x00C00000, .size = 0x00000400 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, [PART_PHY_INIT] = { .mem = { .start = 0x80926000, .size = sizeof(struct wl18xx_mac_and_phy_params) }, .reg = { .start = 0x00000000, .size = 0x00000000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, }; static const int wl18xx_rtable[REG_TABLE_LEN] = { [REG_ECPU_CONTROL] = WL18XX_REG_ECPU_CONTROL, [REG_INTERRUPT_NO_CLEAR] = WL18XX_REG_INTERRUPT_NO_CLEAR, [REG_INTERRUPT_ACK] = WL18XX_REG_INTERRUPT_ACK, [REG_COMMAND_MAILBOX_PTR] = WL18XX_REG_COMMAND_MAILBOX_PTR, [REG_EVENT_MAILBOX_PTR] = WL18XX_REG_EVENT_MAILBOX_PTR, [REG_INTERRUPT_TRIG] = WL18XX_REG_INTERRUPT_TRIG_H, [REG_INTERRUPT_MASK] = WL18XX_REG_INTERRUPT_MASK, [REG_PC_ON_RECOVERY] = WL18XX_SCR_PAD4, [REG_CHIP_ID_B] = WL18XX_REG_CHIP_ID_B, [REG_CMD_MBOX_ADDRESS] = WL18XX_CMD_MBOX_ADDRESS, /* data access memory addresses, used with partition translation */ [REG_SLV_MEM_DATA] = WL18XX_SLV_MEM_DATA, [REG_SLV_REG_DATA] = WL18XX_SLV_REG_DATA, /* raw data access memory addresses */ [REG_RAW_FW_STATUS_ADDR] = WL18XX_FW_STATUS_ADDR, }; static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { [CLOCK_CONFIG_16_2_M] = { 7, 104, 801, 4, true }, [CLOCK_CONFIG_16_368_M] = { 9, 132, 3751, 4, true }, [CLOCK_CONFIG_16_8_M] = { 7, 100, 0, 0, false }, [CLOCK_CONFIG_19_2_M] = { 8, 100, 0, 0, false }, [CLOCK_CONFIG_26_M] = { 13, 120, 0, 0, false }, [CLOCK_CONFIG_32_736_M] = { 9, 132, 3751, 4, true }, [CLOCK_CONFIG_33_6_M] = { 7, 100, 0, 0, false }, [CLOCK_CONFIG_38_468_M] = { 8, 100, 0, 0, false }, [CLOCK_CONFIG_52_M] = { 13, 120, 0, 0, false }, }; /* TODO: maybe move to a new header file? */ #define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin" static int wl18xx_identify_chip(struct wl1271 *wl) { int ret = 0; switch (wl->chip.id) { case CHIP_ID_185x_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG20)", wl->chip.id); wl->sr_fw_name = WL18XX_FW_NAME; /* wl18xx uses the same firmware for PLT */ wl->plt_fw_name = WL18XX_FW_NAME; wl->quirks |= WLCORE_QUIRK_NO_ELP | WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN | WLCORE_QUIRK_TX_PAD_LAST_FRAME; wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER, WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER, WL18XX_MINOR_VER); break; case CHIP_ID_185x_PG10: wl1271_warning("chip id 0x%x (185x PG10) is deprecated", wl->chip.id); ret = -ENODEV; goto out; default: wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; goto out; } out: return ret; } static int wl18xx_set_clk(struct wl1271 *wl) { u16 clk_freq; int ret; ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); if (ret < 0) goto out; /* TODO: PG2: apparently we need to read the clk type */ ret = wl18xx_top_reg_read(wl, PRIMARY_CLK_DETECT, &clk_freq); if (ret < 0) goto out; wl1271_debug(DEBUG_BOOT, "clock freq %d (%d, %d, %d, %d, %s)", clk_freq, wl18xx_clk_table[clk_freq].n, wl18xx_clk_table[clk_freq].m, wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q, wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit"); ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, wl18xx_clk_table[clk_freq].n); if (ret < 0) goto out; ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_M, wl18xx_clk_table[clk_freq].m); if (ret < 0) goto out; if (wl18xx_clk_table[clk_freq].swallow) { /* first the 16 lower bits */ ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_1, wl18xx_clk_table[clk_freq].q & PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK); if (ret < 0) goto out; /* then the 16 higher bits, masked out */ ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_2, (wl18xx_clk_table[clk_freq].q >> 16) & PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK); if (ret < 0) goto out; /* first the 16 lower bits */ ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_1, wl18xx_clk_table[clk_freq].p & PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK); if (ret < 0) goto out; /* then the 16 higher bits, masked out */ ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_2, (wl18xx_clk_table[clk_freq].p >> 16) & PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK); } else { ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_SWALLOW_EN, PLLSH_WCS_PLL_SWALLOW_EN_VAL2); } out: return ret; } static int wl18xx_boot_soft_reset(struct wl1271 *wl) { int ret; /* disable Rx/Tx */ ret = wlcore_write32(wl, WL18XX_ENABLE, 0x0); if (ret < 0) goto out; /* disable auto calibration on start*/ ret = wlcore_write32(wl, WL18XX_SPARE_A2, 0xffff); out: return ret; } static int wl18xx_pre_boot(struct wl1271 *wl) { int ret; ret = wl18xx_set_clk(wl); if (ret < 0) goto out; /* Continue the ELP wake up sequence */ ret = wlcore_write32(wl, WL18XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); if (ret < 0) goto out; udelay(500); ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) goto out; /* Disable interrupts */ ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); if (ret < 0) goto out; ret = wl18xx_boot_soft_reset(wl); out: return ret; } static int wl18xx_pre_upload(struct wl1271 *wl) { u32 tmp; int ret; ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) goto out; /* TODO: check if this is all needed */ ret = wlcore_write32(wl, WL18XX_EEPROMLESS_IND, WL18XX_EEPROMLESS_IND); if (ret < 0) goto out; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp); if (ret < 0) goto out; wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp); out: return ret; } static int wl18xx_set_mac_and_phy(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; struct wl18xx_mac_and_phy_params *params; int ret; params = kmemdup(&priv->conf.phy, sizeof(*params), GFP_KERNEL); if (!params) { ret = -ENOMEM; goto out; } ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); if (ret < 0) goto out; ret = wlcore_write(wl, WL18XX_PHY_INIT_MEM_ADDR, params, sizeof(*params), false); out: kfree(params); return ret; } static int wl18xx_enable_interrupts(struct wl1271 *wl) { u32 event_mask, intr_mask; int ret; event_mask = WL18XX_ACX_EVENTS_VECTOR; intr_mask = WL18XX_INTR_MASK; ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, event_mask); if (ret < 0) goto out; wlcore_enable_interrupts(wl); ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL & ~intr_mask); out: return ret; } static int wl18xx_boot(struct wl1271 *wl) { int ret; ret = wl18xx_pre_boot(wl); if (ret < 0) goto out; ret = wl18xx_pre_upload(wl); if (ret < 0) goto out; ret = wlcore_boot_upload_firmware(wl); if (ret < 0) goto out; ret = wl18xx_set_mac_and_phy(wl); if (ret < 0) goto out; ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; ret = wl18xx_enable_interrupts(wl); out: return ret; } static int wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len) { struct wl18xx_priv *priv = wl->priv; memcpy(priv->cmd_buf, buf, len); memset(priv->cmd_buf + len, 0, WL18XX_CMD_MAX_SIZE - len); return wlcore_write(wl, cmd_box_addr, priv->cmd_buf, WL18XX_CMD_MAX_SIZE, false); } static int wl18xx_ack_event(struct wl1271 *wl) { return wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL18XX_INTR_TRIG_EVENT_ACK); } static u32 wl18xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) { u32 blk_size = WL18XX_TX_HW_BLOCK_SIZE; return (len + blk_size - 1) / blk_size + spare_blks; } static void wl18xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, u32 blks, u32 spare_blks) { desc->wl18xx_mem.total_mem_blocks = blks; } static void wl18xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) { desc->length = cpu_to_le16(skb->len); /* if only the last frame is to be padded, we unset this bit on Tx */ if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) desc->wl18xx_mem.ctrl = WL18XX_TX_CTRL_NOT_PADDED; else desc->wl18xx_mem.ctrl = 0; wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d " "len: %d life: %d mem: %d", desc->hlid, le16_to_cpu(desc->length), le16_to_cpu(desc->life_time), desc->wl18xx_mem.total_mem_blocks); } static enum wl_rx_buf_align wl18xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) { if (rx_desc & RX_BUF_PADDED_PAYLOAD) return WLCORE_RX_BUF_PADDED; return WLCORE_RX_BUF_ALIGNED; } static u32 wl18xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, u32 data_len) { struct wl1271_rx_descriptor *desc = rx_data; /* invalid packet */ if (data_len < sizeof(*desc)) return 0; return data_len - sizeof(*desc); } static void wl18xx_tx_immediate_completion(struct wl1271 *wl) { wl18xx_tx_immediate_complete(wl); } static int wl18xx_set_host_cfg_bitmap(struct wl1271 *wl, u32 extra_mem_blk) { int ret; u32 sdio_align_size = 0; u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE | HOST_IF_CFG_ADD_RX_ALIGNMENT; /* Enable Tx SDIO padding */ if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) { host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; sdio_align_size = WL12XX_BUS_BLOCK_SIZE; } /* Enable Rx SDIO padding */ if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN) { host_cfg_bitmap |= HOST_IF_CFG_RX_PAD_TO_SDIO_BLK; sdio_align_size = WL12XX_BUS_BLOCK_SIZE; } ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap, sdio_align_size, extra_mem_blk, WL18XX_HOST_IF_LEN_SIZE_FIELD); if (ret < 0) return ret; return 0; } static int wl18xx_hw_init(struct wl1271 *wl) { int ret; struct wl18xx_priv *priv = wl->priv; /* (re)init private structures. Relevant on recovery as well. */ priv->last_fw_rls_idx = 0; priv->extra_spare_vif_count = 0; /* set the default amount of spare blocks in the bitmap */ ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE); if (ret < 0) return ret; if (checksum_param) { ret = wl18xx_acx_set_checksum_state(wl); if (ret != 0) return ret; } return ret; } static void wl18xx_set_tx_desc_csum(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) { u32 ip_hdr_offset; struct iphdr *ip_hdr; if (!checksum_param) { desc->wl18xx_checksum_data = 0; return; } if (skb->ip_summed != CHECKSUM_PARTIAL) { desc->wl18xx_checksum_data = 0; return; } ip_hdr_offset = skb_network_header(skb) - skb_mac_header(skb); if (WARN_ON(ip_hdr_offset >= (1<<7))) { desc->wl18xx_checksum_data = 0; return; } desc->wl18xx_checksum_data = ip_hdr_offset << 1; /* FW is interested only in the LSB of the protocol TCP=0 UDP=1 */ ip_hdr = (void *)skb_network_header(skb); desc->wl18xx_checksum_data |= (ip_hdr->protocol & 0x01); } static void wl18xx_set_rx_csum(struct wl1271 *wl, struct wl1271_rx_descriptor *desc, struct sk_buff *skb) { if (desc->status & WL18XX_RX_CHECKSUM_MASK) skb->ip_summed = CHECKSUM_UNNECESSARY; } static bool wl18xx_is_mimo_supported(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; return priv->conf.phy.number_of_assembled_ant2_4 >= 2; } /* * TODO: instead of having these two functions to get the rate mask, * we should modify the wlvif->rate_set instead */ static u32 wl18xx_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { u32 hw_rate_set = wlvif->rate_set; if (wlvif->channel_type == NL80211_CHAN_HT40MINUS || wlvif->channel_type == NL80211_CHAN_HT40PLUS) { wl1271_debug(DEBUG_ACX, "using wide channel rate mask"); hw_rate_set |= CONF_TX_RATE_USE_WIDE_CHAN; /* we don't support MIMO in wide-channel mode */ hw_rate_set &= ~CONF_TX_MIMO_RATES; } else if (wl18xx_is_mimo_supported(wl)) { wl1271_debug(DEBUG_ACX, "using MIMO channel rate mask"); hw_rate_set |= CONF_TX_MIMO_RATES; } return hw_rate_set; } static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { if (wlvif->channel_type == NL80211_CHAN_HT40MINUS || wlvif->channel_type == NL80211_CHAN_HT40PLUS) { wl1271_debug(DEBUG_ACX, "using wide channel rate mask"); /* sanity check - we don't support this */ if (WARN_ON(wlvif->band != IEEE80211_BAND_5GHZ)) return 0; return CONF_TX_RATE_USE_WIDE_CHAN; } else if (wl18xx_is_mimo_supported(wl) && wlvif->band == IEEE80211_BAND_2GHZ) { wl1271_debug(DEBUG_ACX, "using MIMO rate mask"); /* * we don't care about HT channel here - if a peer doesn't * support MIMO, we won't enable it in its rates */ return CONF_TX_MIMO_RATES; } else { return 0; } } static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) { u32 fuse; int ret; ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); if (ret < 0) goto out; ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_1_3, &fuse); if (ret < 0) goto out; if (ver) *ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET; ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); out: return ret; } #define WL18XX_CONF_FILE_NAME "ti-connectivity/wl18xx-conf.bin" static int wl18xx_conf_init(struct wl1271 *wl, struct device *dev) { struct wl18xx_priv *priv = wl->priv; struct wlcore_conf_file *conf_file; const struct firmware *fw; int ret; ret = request_firmware(&fw, WL18XX_CONF_FILE_NAME, dev); if (ret < 0) { wl1271_error("could not get configuration binary %s: %d", WL18XX_CONF_FILE_NAME, ret); goto out_fallback; } if (fw->size != WL18XX_CONF_SIZE) { wl1271_error("configuration binary file size is wrong, expected %zu got %zu", WL18XX_CONF_SIZE, fw->size); ret = -EINVAL; goto out; } conf_file = (struct wlcore_conf_file *) fw->data; if (conf_file->header.magic != cpu_to_le32(WL18XX_CONF_MAGIC)) { wl1271_error("configuration binary file magic number mismatch, " "expected 0x%0x got 0x%0x", WL18XX_CONF_MAGIC, conf_file->header.magic); ret = -EINVAL; goto out; } if (conf_file->header.version != cpu_to_le32(WL18XX_CONF_VERSION)) { wl1271_error("configuration binary file version not supported, " "expected 0x%08x got 0x%08x", WL18XX_CONF_VERSION, conf_file->header.version); ret = -EINVAL; goto out; } memcpy(&wl->conf, &conf_file->core, sizeof(wl18xx_conf)); memcpy(&priv->conf, &conf_file->priv, sizeof(priv->conf)); goto out; out_fallback: wl1271_warning("falling back to default config"); /* apply driver default configuration */ memcpy(&wl->conf, &wl18xx_conf, sizeof(wl18xx_conf)); /* apply default private configuration */ memcpy(&priv->conf, &wl18xx_default_priv_conf, sizeof(priv->conf)); /* For now we just fallback */ return 0; out: release_firmware(fw); return ret; } static int wl18xx_plt_init(struct wl1271 *wl) { int ret; /* calibrator based auto/fem detect not supported for 18xx */ if (wl->plt_mode == PLT_FEM_DETECT) { wl1271_error("wl18xx_plt_init: PLT FEM_DETECT not supported"); return -EINVAL; } ret = wlcore_write32(wl, WL18XX_SCR_PAD8, WL18XX_SCR_PAD8_PLT); if (ret < 0) return ret; return wl->ops->boot(wl); } static int wl18xx_get_mac(struct wl1271 *wl) { u32 mac1, mac2; int ret; ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); if (ret < 0) goto out; ret = wlcore_read32(wl, WL18XX_REG_FUSE_BD_ADDR_1, &mac1); if (ret < 0) goto out; ret = wlcore_read32(wl, WL18XX_REG_FUSE_BD_ADDR_2, &mac2); if (ret < 0) goto out; /* these are the two parts of the BD_ADDR */ wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + ((mac1 & 0xff000000) >> 24); wl->fuse_nic_addr = (mac1 & 0xffffff); ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); out: return ret; } static int wl18xx_handle_static_data(struct wl1271 *wl, struct wl1271_static_data *static_data) { struct wl18xx_static_data_priv *static_data_priv = (struct wl18xx_static_data_priv *) static_data->priv; wl1271_info("PHY firmware version: %s", static_data_priv->phy_version); return 0; } static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) { struct wl18xx_priv *priv = wl->priv; /* If we have VIFs requiring extra spare, indulge them */ if (priv->extra_spare_vif_count) return WL18XX_TX_HW_EXTRA_BLOCK_SPARE; return WL18XX_TX_HW_BLOCK_SPARE; } static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct wl18xx_priv *priv = wl->priv; bool change_spare = false; int ret; /* * when adding the first or removing the last GEM/TKIP interface, * we have to adjust the number of spare blocks. */ change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM || key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) && ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) || (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY)); /* no need to change spare - just regular set_key */ if (!change_spare) return wlcore_set_key(wl, cmd, vif, sta, key_conf); /* * stop the queues and flush to ensure the next packets are * in sync with FW spare block accounting */ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK); wl1271_tx_flush(wl); ret = wlcore_set_key(wl, cmd, vif, sta, key_conf); if (ret < 0) goto out; /* key is now set, change the spare blocks */ if (cmd == SET_KEY) { ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_EXTRA_BLOCK_SPARE); if (ret < 0) goto out; priv->extra_spare_vif_count++; } else { ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE); if (ret < 0) goto out; priv->extra_spare_vif_count--; } out: wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK); return ret; } static u32 wl18xx_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) { if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) { struct wl1271_tx_hw_descr *last_desc; /* get the last TX HW descriptor written to the aggr buf */ last_desc = (struct wl1271_tx_hw_descr *)(wl->aggr_buf + buf_offset - last_len); /* the last frame is padded up to an SDIO block */ last_desc->wl18xx_mem.ctrl &= ~WL18XX_TX_CTRL_NOT_PADDED; return ALIGN(buf_offset, WL12XX_BUS_BLOCK_SIZE); } /* no modifications */ return buf_offset; } static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, .plt_init = wl18xx_plt_init, .trigger_cmd = wl18xx_trigger_cmd, .ack_event = wl18xx_ack_event, .calc_tx_blocks = wl18xx_calc_tx_blocks, .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks, .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len, .get_rx_buf_align = wl18xx_get_rx_buf_align, .get_rx_packet_len = wl18xx_get_rx_packet_len, .tx_immediate_compl = wl18xx_tx_immediate_completion, .tx_delayed_compl = NULL, .hw_init = wl18xx_hw_init, .set_tx_desc_csum = wl18xx_set_tx_desc_csum, .get_pg_ver = wl18xx_get_pg_ver, .set_rx_csum = wl18xx_set_rx_csum, .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask, .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, .get_mac = wl18xx_get_mac, .debugfs_init = wl18xx_debugfs_add_files, .handle_static_data = wl18xx_handle_static_data, .get_spare_blocks = wl18xx_get_spare_blocks, .set_key = wl18xx_set_key, .pre_pkt_send = wl18xx_pre_pkt_send, }; /* HT cap appropriate for wide channels in 2Ghz */ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = { .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .mcs = { .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, .rx_highest = cpu_to_le16(150), .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }; /* HT cap appropriate for wide channels in 5Ghz */ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = { .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .mcs = { .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, .rx_highest = cpu_to_le16(150), .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }; /* HT cap appropriate for SISO 20 */ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { .cap = IEEE80211_HT_CAP_SGI_20, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .mcs = { .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, .rx_highest = cpu_to_le16(72), .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }; /* HT cap appropriate for MIMO rates in 20mhz channel */ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = { .cap = IEEE80211_HT_CAP_SGI_20, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .mcs = { .rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, }, .rx_highest = cpu_to_le16(144), .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }; static int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; struct ieee80211_hw *hw; struct wl18xx_priv *priv; int ret; hw = wlcore_alloc_hw(sizeof(*priv)); if (IS_ERR(hw)) { wl1271_error("can't allocate hw"); ret = PTR_ERR(hw); goto out; } wl = hw->priv; priv = wl->priv; wl->ops = &wl18xx_ops; wl->ptable = wl18xx_ptable; wl->rtable = wl18xx_rtable; wl->num_tx_desc = 32; wl->num_rx_desc = 32; wl->band_rate_to_idx = wl18xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); if (num_rx_desc_param != -1) wl->num_rx_desc = num_rx_desc_param; ret = wl18xx_conf_init(wl, &pdev->dev); if (ret < 0) goto out_free; /* If the module param is set, update it in conf */ if (board_type_param) { if (!strcmp(board_type_param, "fpga")) { priv->conf.phy.board_type = BOARD_TYPE_FPGA_18XX; } else if (!strcmp(board_type_param, "hdk")) { priv->conf.phy.board_type = BOARD_TYPE_HDK_18XX; } else if (!strcmp(board_type_param, "dvp")) { priv->conf.phy.board_type = BOARD_TYPE_DVP_18XX; } else if (!strcmp(board_type_param, "evb")) { priv->conf.phy.board_type = BOARD_TYPE_EVB_18XX; } else if (!strcmp(board_type_param, "com8")) { priv->conf.phy.board_type = BOARD_TYPE_COM8_18XX; } else { wl1271_error("invalid board type '%s'", board_type_param); ret = -EINVAL; goto out_free; } } /* HACK! Just for now we hardcode COM8 and HDK to 0x06 */ switch (priv->conf.phy.board_type) { case BOARD_TYPE_HDK_18XX: case BOARD_TYPE_COM8_18XX: priv->conf.phy.low_band_component_type = 0x06; break; case BOARD_TYPE_FPGA_18XX: case BOARD_TYPE_DVP_18XX: case BOARD_TYPE_EVB_18XX: priv->conf.phy.low_band_component_type = 0x05; break; default: wl1271_error("invalid board type '%d'", priv->conf.phy.board_type); ret = -EINVAL; goto out_free; } if (low_band_component_param != -1) priv->conf.phy.low_band_component = low_band_component_param; if (low_band_component_type_param != -1) priv->conf.phy.low_band_component_type = low_band_component_type_param; if (high_band_component_param != -1) priv->conf.phy.high_band_component = high_band_component_param; if (high_band_component_type_param != -1) priv->conf.phy.high_band_component_type = high_band_component_type_param; if (pwr_limit_reference_11_abg_param != -1) priv->conf.phy.pwr_limit_reference_11_abg = pwr_limit_reference_11_abg_param; if (n_antennas_2_param != -1) priv->conf.phy.number_of_assembled_ant2_4 = n_antennas_2_param; if (n_antennas_5_param != -1) priv->conf.phy.number_of_assembled_ant5 = n_antennas_5_param; if (dc2dc_param != -1) priv->conf.phy.external_pa_dc2dc = dc2dc_param; if (ht_mode_param) { if (!strcmp(ht_mode_param, "default")) priv->conf.ht.mode = HT_MODE_DEFAULT; else if (!strcmp(ht_mode_param, "wide")) priv->conf.ht.mode = HT_MODE_WIDE; else if (!strcmp(ht_mode_param, "siso20")) priv->conf.ht.mode = HT_MODE_SISO20; else { wl1271_error("invalid ht_mode '%s'", ht_mode_param); ret = -EINVAL; goto out_free; } } if (priv->conf.ht.mode == HT_MODE_DEFAULT) { /* * Only support mimo with multiple antennas. Fall back to * siso20. */ if (wl18xx_is_mimo_supported(wl)) wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl18xx_mimo_ht_cap_2ghz); else wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl18xx_siso20_ht_cap); /* 5Ghz is always wide */ wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl18xx_siso40_ht_cap_5ghz); } else if (priv->conf.ht.mode == HT_MODE_WIDE) { wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl18xx_siso40_ht_cap_2ghz); wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl18xx_siso40_ht_cap_5ghz); } else if (priv->conf.ht.mode == HT_MODE_SISO20) { wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl18xx_siso20_ht_cap); wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl18xx_siso20_ht_cap); } if (!checksum_param) { wl18xx_ops.set_rx_csum = NULL; wl18xx_ops.init_vif = NULL; } wl->enable_11a = enable_11a_param; return wlcore_probe(wl, pdev); out_free: wlcore_free_hw(wl); out: return ret; } static const struct platform_device_id wl18xx_id_table[] __devinitconst = { { "wl18xx", 0 }, { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(platform, wl18xx_id_table); static struct platform_driver wl18xx_driver = { .probe = wl18xx_probe, .remove = __devexit_p(wlcore_remove), .id_table = wl18xx_id_table, .driver = { .name = "wl18xx_driver", .owner = THIS_MODULE, } }; static int __init wl18xx_init(void) { return platform_driver_register(&wl18xx_driver); } module_init(wl18xx_init); static void __exit wl18xx_exit(void) { platform_driver_unregister(&wl18xx_driver); } module_exit(wl18xx_exit); module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR); MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or siso20"); module_param_named(board_type, board_type_param, charp, S_IRUSR); MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or " "dvp"); module_param_named(checksum, checksum_param, bool, S_IRUSR); MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to false)"); module_param_named(enable_11a, enable_11a_param, bool, S_IRUSR); MODULE_PARM_DESC(enable_11a, "Enable 11a (5GHz): boolean (defaults to true)"); module_param_named(dc2dc, dc2dc_param, int, S_IRUSR); MODULE_PARM_DESC(dc2dc, "External DC2DC: u8 (defaults to 0)"); module_param_named(n_antennas_2, n_antennas_2_param, int, S_IRUSR); MODULE_PARM_DESC(n_antennas_2, "Number of installed 2.4GHz antennas: 1 (default) or 2"); module_param_named(n_antennas_5, n_antennas_5_param, int, S_IRUSR); MODULE_PARM_DESC(n_antennas_5, "Number of installed 5GHz antennas: 1 (default) or 2"); module_param_named(low_band_component, low_band_component_param, int, S_IRUSR); MODULE_PARM_DESC(low_band_component, "Low band component: u8 " "(default is 0x01)"); module_param_named(low_band_component_type, low_band_component_type_param, int, S_IRUSR); MODULE_PARM_DESC(low_band_component_type, "Low band component type: u8 " "(default is 0x05 or 0x06 depending on the board_type)"); module_param_named(high_band_component, high_band_component_param, int, S_IRUSR); MODULE_PARM_DESC(high_band_component, "High band component: u8, " "(default is 0x01)"); module_param_named(high_band_component_type, high_band_component_type_param, int, S_IRUSR); MODULE_PARM_DESC(high_band_component_type, "High band component type: u8 " "(default is 0x09)"); module_param_named(pwr_limit_reference_11_abg, pwr_limit_reference_11_abg_param, int, S_IRUSR); MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 " "(default is 0xc8)"); module_param_named(num_rx_desc, num_rx_desc_param, int, S_IRUSR); MODULE_PARM_DESC(num_rx_desc_param, "Number of Rx descriptors: u8 (default is 32)"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/io.h0000644000175000017500000000171312026211315023032 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL18XX_IO_H__ #define __WL18XX_IO_H__ int __must_check wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val); int __must_check wl18xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out); #endif /* __WL18XX_IO_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/io.c0000644000175000017500000000324712026211315023031 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "../wlcore/wlcore.h" #include "../wlcore/io.h" #include "io.h" int wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) { u32 tmp; int ret; if (WARN_ON(addr % 2)) return -EINVAL; if ((addr % 4) == 0) { ret = wlcore_read32(wl, addr, &tmp); if (ret < 0) goto out; tmp = (tmp & 0xffff0000) | val; ret = wlcore_write32(wl, addr, tmp); } else { ret = wlcore_read32(wl, addr - 2, &tmp); if (ret < 0) goto out; tmp = (tmp & 0xffff) | (val << 16); ret = wlcore_write32(wl, addr - 2, tmp); } out: return ret; } int wl18xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out) { u32 val = 0; int ret; if (WARN_ON(addr % 2)) return -EINVAL; if ((addr % 4) == 0) { /* address is 4-bytes aligned */ ret = wlcore_read32(wl, addr, &val); if (ret >= 0 && out) *out = val & 0xffff; } else { ret = wlcore_read32(wl, addr - 2, &val); if (ret >= 0 && out) *out = (val & 0xffff0000) >> 16; } return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/debugfs.h0000644000175000017500000000164712026211315024050 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2012 Texas Instruments. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL18XX_DEBUGFS_H__ #define __WL18XX_DEBUGFS_H__ int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir); #endif /* __WL18XX_DEBUGFS_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/debugfs.c0000644000175000017500000003650112026211315024040 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2009 Nokia Corporation * Copyright (C) 2011-2012 Texas Instruments * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "../wlcore/debugfs.h" #include "../wlcore/wlcore.h" #include "wl18xx.h" #include "acx.h" #include "debugfs.h" #define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ DEBUGFS_FWSTATS_FILE(a, b, c, wl18xx_acx_statistics) #define WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \ DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, wl18xx_acx_statistics) WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug1, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug2, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug3, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug4, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug5, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug6, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(error, error_null_Frame_tx_start, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(error, error_numll_frame_cts_start, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(error, error_bar_retry, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame_cts_nul_flid, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_prepared_descs, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_cmplt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_programmed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_burst_programmed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_starts, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_imm_resp, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_templates, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_int_templates, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_fw_gen, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_data, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_null_frame, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_frame_checksum, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_checksum_result, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_mpdu_alloc_failed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_init_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_in_process_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_tkip_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_key_not_found, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_need_fragmentation, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_bad_mblk_num, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_beacon_early_term, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mpdu_nodes, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout_wa, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_density_dropped_frame, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_ba_not_expected, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_frame_checksum, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_checksum_result, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_init_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_in_process_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_tkip_called, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_tkip_replays, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread, PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD); WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_timeouts, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_timeouts, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_max_ap_turn, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_max_ap_turn, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_utilization, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_utilization, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, beacon_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, arp_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, mc_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate, AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE); WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size, AGGR_STATS_RX_SIZE_LEN); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, hs_tx_stat_fifo_int, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_tx_stat_fifo_int, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_rx_stat_fifo_int, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_tx_stat_fifo_int, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_rx_stat_fifo_int, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, rx_complete_stat_fifo_int, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_proc_swi, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, post_proc_swi, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, sec_frag_swi, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_to_defrag_swi, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, defrag_to_csum_swi, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, csum_to_rx_xfer_swi, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in_fifo_full, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_in, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_out, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pipeline, pipeline_fifo_full, PIPE_STATS_HW_FIFO); WL18XX_DEBUGFS_FWSTATS_FILE(mem, rx_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, fw_gen_free_mem_blks, "%u"); static ssize_t conf_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl18xx_priv *priv = wl->priv; struct wlcore_conf_header header; char *buf, *pos; size_t len; int ret; len = WL18XX_CONF_SIZE; buf = kmalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; header.magic = cpu_to_le32(WL18XX_CONF_MAGIC); header.version = cpu_to_le32(WL18XX_CONF_VERSION); header.checksum = 0; mutex_lock(&wl->mutex); pos = buf; memcpy(pos, &header, sizeof(header)); pos += sizeof(header); memcpy(pos, &wl->conf, sizeof(wl->conf)); pos += sizeof(wl->conf); memcpy(pos, &priv->conf, sizeof(priv->conf)); mutex_unlock(&wl->mutex); ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret; } static const struct file_operations conf_ops = { .read = conf_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t clear_fw_stats_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; int ret; mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) goto out; ret = wl18xx_acx_clear_statistics(wl); if (ret < 0) { count = ret; goto out; } out: mutex_unlock(&wl->mutex); return count; } static const struct file_operations clear_fw_stats_ops = { .write = clear_fw_stats_write, .open = simple_open, .llseek = default_llseek, }; int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { int ret = 0; struct dentry *entry, *stats, *moddir; moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir); if (!moddir || IS_ERR(moddir)) { entry = moddir; goto err; } stats = debugfs_create_dir("fw_stats", moddir); if (!stats || IS_ERR(stats)) { entry = stats; goto err; } DEBUGFS_ADD(clear_fw_stats, stats); DEBUGFS_FWSTATS_ADD(debug, debug1); DEBUGFS_FWSTATS_ADD(debug, debug2); DEBUGFS_FWSTATS_ADD(debug, debug3); DEBUGFS_FWSTATS_ADD(debug, debug4); DEBUGFS_FWSTATS_ADD(debug, debug5); DEBUGFS_FWSTATS_ADD(debug, debug6); DEBUGFS_FWSTATS_ADD(error, error_frame); DEBUGFS_FWSTATS_ADD(error, error_null_Frame_tx_start); DEBUGFS_FWSTATS_ADD(error, error_numll_frame_cts_start); DEBUGFS_FWSTATS_ADD(error, error_bar_retry); DEBUGFS_FWSTATS_ADD(error, error_frame_cts_nul_flid); DEBUGFS_FWSTATS_ADD(tx, tx_prepared_descs); DEBUGFS_FWSTATS_ADD(tx, tx_cmplt); DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared); DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared); DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed); DEBUGFS_FWSTATS_ADD(tx, tx_data_programmed); DEBUGFS_FWSTATS_ADD(tx, tx_burst_programmed); DEBUGFS_FWSTATS_ADD(tx, tx_starts); DEBUGFS_FWSTATS_ADD(tx, tx_imm_resp); DEBUGFS_FWSTATS_ADD(tx, tx_start_templates); DEBUGFS_FWSTATS_ADD(tx, tx_start_int_templates); DEBUGFS_FWSTATS_ADD(tx, tx_start_fw_gen); DEBUGFS_FWSTATS_ADD(tx, tx_start_data); DEBUGFS_FWSTATS_ADD(tx, tx_start_null_frame); DEBUGFS_FWSTATS_ADD(tx, tx_exch); DEBUGFS_FWSTATS_ADD(tx, tx_retry_template); DEBUGFS_FWSTATS_ADD(tx, tx_retry_data); DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending); DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry); DEBUGFS_FWSTATS_ADD(tx, tx_done_template); DEBUGFS_FWSTATS_ADD(tx, tx_done_data); DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template); DEBUGFS_FWSTATS_ADD(tx, tx_frame_checksum); DEBUGFS_FWSTATS_ADD(tx, tx_checksum_result); DEBUGFS_FWSTATS_ADD(tx, frag_called); DEBUGFS_FWSTATS_ADD(tx, frag_mpdu_alloc_failed); DEBUGFS_FWSTATS_ADD(tx, frag_init_called); DEBUGFS_FWSTATS_ADD(tx, frag_in_process_called); DEBUGFS_FWSTATS_ADD(tx, frag_tkip_called); DEBUGFS_FWSTATS_ADD(tx, frag_key_not_found); DEBUGFS_FWSTATS_ADD(tx, frag_need_fragmentation); DEBUGFS_FWSTATS_ADD(tx, frag_bad_mblk_num); DEBUGFS_FWSTATS_ADD(tx, frag_failed); DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit); DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss); DEBUGFS_FWSTATS_ADD(rx, rx_beacon_early_term); DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mpdu_nodes); DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow); DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame); DEBUGFS_FWSTATS_ADD(rx, rx_done); DEBUGFS_FWSTATS_ADD(rx, rx_defrag); DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end); DEBUGFS_FWSTATS_ADD(rx, rx_cmplt); DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt); DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task); DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr); DEBUGFS_FWSTATS_ADD(rx, rx_timeout); DEBUGFS_FWSTATS_ADD(rx, rx_timeout_wa); DEBUGFS_FWSTATS_ADD(rx, rx_wa_density_dropped_frame); DEBUGFS_FWSTATS_ADD(rx, rx_wa_ba_not_expected); DEBUGFS_FWSTATS_ADD(rx, rx_frame_checksum); DEBUGFS_FWSTATS_ADD(rx, rx_checksum_result); DEBUGFS_FWSTATS_ADD(rx, defrag_called); DEBUGFS_FWSTATS_ADD(rx, defrag_init_called); DEBUGFS_FWSTATS_ADD(rx, defrag_in_process_called); DEBUGFS_FWSTATS_ADD(rx, defrag_tkip_called); DEBUGFS_FWSTATS_ADD(rx, defrag_need_defrag); DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed); DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found); DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt); DEBUGFS_FWSTATS_ADD(rx, rx_tkip_replays); DEBUGFS_FWSTATS_ADD(isr, irqs); DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt); DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt); DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync); DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread); DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt); DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_timeouts); DEBUGFS_FWSTATS_ADD(ps_poll, upsd_timeouts); DEBUGFS_FWSTATS_ADD(ps_poll, upsd_max_ap_turn); DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_max_ap_turn); DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_utilization); DEBUGFS_FWSTATS_ADD(ps_poll, upsd_utilization); DEBUGFS_FWSTATS_ADD(rx_filter, beacon_filter); DEBUGFS_FWSTATS_ADD(rx_filter, arp_filter); DEBUGFS_FWSTATS_ADD(rx_filter, mc_filter); DEBUGFS_FWSTATS_ADD(rx_filter, dup_filter); DEBUGFS_FWSTATS_ADD(rx_filter, data_filter); DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter); DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter); DEBUGFS_FWSTATS_ADD(rx_filter, accum_arp_pend_requests); DEBUGFS_FWSTATS_ADD(rx_filter, max_arp_queue_dep); DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates); DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_vs_rate); DEBUGFS_FWSTATS_ADD(aggr_size, rx_size); DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int); DEBUGFS_FWSTATS_ADD(pipeline, tcp_tx_stat_fifo_int); DEBUGFS_FWSTATS_ADD(pipeline, tcp_rx_stat_fifo_int); DEBUGFS_FWSTATS_ADD(pipeline, enc_tx_stat_fifo_int); DEBUGFS_FWSTATS_ADD(pipeline, enc_rx_stat_fifo_int); DEBUGFS_FWSTATS_ADD(pipeline, rx_complete_stat_fifo_int); DEBUGFS_FWSTATS_ADD(pipeline, pre_proc_swi); DEBUGFS_FWSTATS_ADD(pipeline, post_proc_swi); DEBUGFS_FWSTATS_ADD(pipeline, sec_frag_swi); DEBUGFS_FWSTATS_ADD(pipeline, pre_to_defrag_swi); DEBUGFS_FWSTATS_ADD(pipeline, defrag_to_csum_swi); DEBUGFS_FWSTATS_ADD(pipeline, csum_to_rx_xfer_swi); DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in); DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in_fifo_full); DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out); DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_in); DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_out); DEBUGFS_FWSTATS_ADD(pipeline, pipeline_fifo_full); DEBUGFS_FWSTATS_ADD(mem, rx_free_mem_blks); DEBUGFS_FWSTATS_ADD(mem, tx_free_mem_blks); DEBUGFS_FWSTATS_ADD(mem, fwlog_free_mem_blks); DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks); DEBUGFS_ADD(conf, moddir); return 0; err: if (IS_ERR(entry)) ret = PTR_ERR(entry); else ret = -ENOMEM; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/conf.h0000644000175000017500000000561512026211315023355 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL18XX_CONF_H__ #define __WL18XX_CONF_H__ #define WL18XX_CONF_MAGIC 0x10e100ca #define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0003) #define WL18XX_CONF_MASK 0x0000ffff #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ sizeof(struct wl18xx_priv_conf)) #define NUM_OF_CHANNELS_11_ABG 150 #define NUM_OF_CHANNELS_11_P 7 #define WL18XX_NUM_OF_SUB_BANDS 9 #define SRF_TABLE_LEN 16 #define PIN_MUXING_SIZE 2 struct wl18xx_mac_and_phy_params { u8 phy_standalone; u8 rdl; u8 enable_clpc; u8 enable_tx_low_pwr_on_siso_rdl; u8 auto_detect; u8 dedicated_fem; u8 low_band_component; /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ u8 low_band_component_type; u8 high_band_component; /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ u8 high_band_component_type; u8 number_of_assembled_ant2_4; u8 number_of_assembled_ant5; u8 pin_muxing_platform_options[PIN_MUXING_SIZE]; u8 external_pa_dc2dc; u8 tcxo_ldo_voltage; u8 xtal_itrim_val; u8 srf_state; u8 srf1[SRF_TABLE_LEN]; u8 srf2[SRF_TABLE_LEN]; u8 srf3[SRF_TABLE_LEN]; u8 io_configuration; u8 sdio_configuration; u8 settings; u8 rx_profile; u8 per_chan_pwr_limit_arr_11abg[NUM_OF_CHANNELS_11_ABG]; u8 pwr_limit_reference_11_abg; u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P]; u8 pwr_limit_reference_11p; u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; u8 primary_clock_setting_time; u8 clock_valid_on_wake_up; u8 secondary_clock_setting_time; u8 board_type; /* enable point saturation */ u8 psat; /* low/medium/high Tx power in dBm */ s8 low_power_val; s8 med_power_val; s8 high_power_val; u8 padding[1]; } __packed; enum wl18xx_ht_mode { /* Default - use MIMO, fallback to SISO20 */ HT_MODE_DEFAULT = 0, /* Wide - use SISO40 */ HT_MODE_WIDE = 1, /* Use SISO20 */ HT_MODE_SISO20 = 2, }; struct wl18xx_ht_settings { /* DEFAULT / WIDE / SISO20 */ u8 mode; } __packed; struct wl18xx_priv_conf { /* Module params structures */ struct wl18xx_ht_settings ht; /* this structure is copied wholesale to FW */ struct wl18xx_mac_and_phy_params phy; } __packed; #endif /* __WL18XX_CONF_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/acx.h0000644000175000017500000001527612026211315023207 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL18XX_ACX_H__ #define __WL18XX_ACX_H__ #include "../wlcore/wlcore.h" #include "../wlcore/acx.h" enum { ACX_CLEAR_STATISTICS = 0x0047, }; /* numbers of bits the length field takes (add 1 for the actual number) */ #define WL18XX_HOST_IF_LEN_SIZE_FIELD 15 #define WL18XX_ACX_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \ WL1271_ACX_INTR_INIT_COMPLETE | \ WL1271_ACX_INTR_EVENT_A | \ WL1271_ACX_INTR_EVENT_B | \ WL1271_ACX_INTR_CMD_COMPLETE | \ WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA | \ WL1271_ACX_SW_INTR_WATCHDOG) #define WL18XX_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \ WL1271_ACX_INTR_EVENT_A | \ WL1271_ACX_INTR_EVENT_B | \ WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA | \ WL1271_ACX_SW_INTR_WATCHDOG) struct wl18xx_acx_host_config_bitmap { struct acx_header header; __le32 host_cfg_bitmap; __le32 host_sdio_block_size; /* extra mem blocks per frame in TX. */ __le32 extra_mem_blocks; /* * number of bits of the length field in the first TX word * (up to 15 - for using the entire 16 bits). */ __le32 length_field_size; } __packed; enum { CHECKSUM_OFFLOAD_DISABLED = 0, CHECKSUM_OFFLOAD_ENABLED = 1, CHECKSUM_OFFLOAD_FAKE_RX = 2, CHECKSUM_OFFLOAD_INVALID = 0xFF }; struct wl18xx_acx_checksum_state { struct acx_header header; /* enum acx_checksum_state */ u8 checksum_state; u8 pad[3]; } __packed; struct wl18xx_acx_error_stats { u32 error_frame; u32 error_null_Frame_tx_start; u32 error_numll_frame_cts_start; u32 error_bar_retry; u32 error_frame_cts_nul_flid; } __packed; struct wl18xx_acx_debug_stats { u32 debug1; u32 debug2; u32 debug3; u32 debug4; u32 debug5; u32 debug6; } __packed; struct wl18xx_acx_ring_stats { u32 prepared_descs; u32 tx_cmplt; } __packed; struct wl18xx_acx_tx_stats { u32 tx_prepared_descs; u32 tx_cmplt; u32 tx_template_prepared; u32 tx_data_prepared; u32 tx_template_programmed; u32 tx_data_programmed; u32 tx_burst_programmed; u32 tx_starts; u32 tx_imm_resp; u32 tx_start_templates; u32 tx_start_int_templates; u32 tx_start_fw_gen; u32 tx_start_data; u32 tx_start_null_frame; u32 tx_exch; u32 tx_retry_template; u32 tx_retry_data; u32 tx_exch_pending; u32 tx_exch_expiry; u32 tx_done_template; u32 tx_done_data; u32 tx_done_int_template; u32 tx_frame_checksum; u32 tx_checksum_result; u32 frag_called; u32 frag_mpdu_alloc_failed; u32 frag_init_called; u32 frag_in_process_called; u32 frag_tkip_called; u32 frag_key_not_found; u32 frag_need_fragmentation; u32 frag_bad_mblk_num; u32 frag_failed; u32 frag_cache_hit; u32 frag_cache_miss; } __packed; struct wl18xx_acx_rx_stats { u32 rx_beacon_early_term; u32 rx_out_of_mpdu_nodes; u32 rx_hdr_overflow; u32 rx_dropped_frame; u32 rx_done_stage; u32 rx_done; u32 rx_defrag; u32 rx_defrag_end; u32 rx_cmplt; u32 rx_pre_complt; u32 rx_cmplt_task; u32 rx_phy_hdr; u32 rx_timeout; u32 rx_timeout_wa; u32 rx_wa_density_dropped_frame; u32 rx_wa_ba_not_expected; u32 rx_frame_checksum; u32 rx_checksum_result; u32 defrag_called; u32 defrag_init_called; u32 defrag_in_process_called; u32 defrag_tkip_called; u32 defrag_need_defrag; u32 defrag_decrypt_failed; u32 decrypt_key_not_found; u32 defrag_need_decrypt; u32 rx_tkip_replays; } __packed; struct wl18xx_acx_isr_stats { u32 irqs; } __packed; #define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10 struct wl18xx_acx_pwr_stats { u32 missing_bcns_cnt; u32 rcvd_bcns_cnt; u32 connection_out_of_sync; u32 cont_miss_bcns_spread[PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD]; u32 rcvd_awake_bcns_cnt; } __packed; struct wl18xx_acx_event_stats { u32 calibration; u32 rx_mismatch; u32 rx_mem_empty; } __packed; struct wl18xx_acx_ps_poll_stats { u32 ps_poll_timeouts; u32 upsd_timeouts; u32 upsd_max_ap_turn; u32 ps_poll_max_ap_turn; u32 ps_poll_utilization; u32 upsd_utilization; } __packed; struct wl18xx_acx_rx_filter_stats { u32 beacon_filter; u32 arp_filter; u32 mc_filter; u32 dup_filter; u32 data_filter; u32 ibss_filter; u32 protection_filter; u32 accum_arp_pend_requests; u32 max_arp_queue_dep; } __packed; struct wl18xx_acx_rx_rate_stats { u32 rx_frames_per_rates[50]; } __packed; #define AGGR_STATS_TX_AGG 16 #define AGGR_STATS_TX_RATE 16 #define AGGR_STATS_RX_SIZE_LEN 16 struct wl18xx_acx_aggr_stats { u32 tx_agg_vs_rate[AGGR_STATS_TX_AGG * AGGR_STATS_TX_RATE]; u32 rx_size[AGGR_STATS_RX_SIZE_LEN]; } __packed; #define PIPE_STATS_HW_FIFO 11 struct wl18xx_acx_pipeline_stats { u32 hs_tx_stat_fifo_int; u32 hs_rx_stat_fifo_int; u32 tcp_tx_stat_fifo_int; u32 tcp_rx_stat_fifo_int; u32 enc_tx_stat_fifo_int; u32 enc_rx_stat_fifo_int; u32 rx_complete_stat_fifo_int; u32 pre_proc_swi; u32 post_proc_swi; u32 sec_frag_swi; u32 pre_to_defrag_swi; u32 defrag_to_csum_swi; u32 csum_to_rx_xfer_swi; u32 dec_packet_in; u32 dec_packet_in_fifo_full; u32 dec_packet_out; u32 cs_rx_packet_in; u32 cs_rx_packet_out; u16 pipeline_fifo_full[PIPE_STATS_HW_FIFO]; } __packed; struct wl18xx_acx_mem_stats { u32 rx_free_mem_blks; u32 tx_free_mem_blks; u32 fwlog_free_mem_blks; u32 fw_gen_free_mem_blks; } __packed; struct wl18xx_acx_statistics { struct acx_header header; struct wl18xx_acx_error_stats error; struct wl18xx_acx_debug_stats debug; struct wl18xx_acx_tx_stats tx; struct wl18xx_acx_rx_stats rx; struct wl18xx_acx_isr_stats isr; struct wl18xx_acx_pwr_stats pwr; struct wl18xx_acx_ps_poll_stats ps_poll; struct wl18xx_acx_rx_filter_stats rx_filter; struct wl18xx_acx_rx_rate_stats rx_rate; struct wl18xx_acx_aggr_stats aggr_size; struct wl18xx_acx_pipeline_stats pipeline; struct wl18xx_acx_mem_stats mem; } __packed; struct wl18xx_acx_clear_statistics { struct acx_header header; }; int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); int wl18xx_acx_set_checksum_state(struct wl1271 *wl); int wl18xx_acx_clear_statistics(struct wl1271 *wl); #endif /* __WL18XX_ACX_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/acx.c0000644000175000017500000000530312026211315023170 0ustar mcgrofmcgrof/* * This file is part of wl18xx * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "../wlcore/cmd.h" #include "../wlcore/debug.h" #include "../wlcore/acx.h" #include "acx.h" int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size) { struct wl18xx_acx_host_config_bitmap *bitmap_conf; int ret; wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d", host_cfg_bitmap, sdio_blk_size, extra_mem_blks, len_field_size); bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); if (!bitmap_conf) { ret = -ENOMEM; goto out; } bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size); bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks); bitmap_conf->length_field_size = cpu_to_le32(len_field_size); ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, bitmap_conf, sizeof(*bitmap_conf)); if (ret < 0) { wl1271_warning("wl1271 bitmap config opt failed: %d", ret); goto out; } out: kfree(bitmap_conf); return ret; } int wl18xx_acx_set_checksum_state(struct wl1271 *wl) { struct wl18xx_acx_checksum_state *acx; int ret; wl1271_debug(DEBUG_ACX, "acx checksum state"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED; ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("failed to set Tx checksum state: %d", ret); goto out; } out: kfree(acx); return ret; } int wl18xx_acx_clear_statistics(struct wl1271 *wl) { struct wl18xx_acx_clear_statistics *acx; int ret = 0; wl1271_debug(DEBUG_ACX, "acx clear statistics"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("failed to clear firmware statistics: %d", ret); goto out; } out: kfree(acx); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/Makefile0000644000175000017500000000012212026211315023703 0ustar mcgrofmcgrofwl18xx-objs = main.o acx.o tx.o io.o debugfs.o obj-$(CONFIG_WL18XX) += wl18xx.o compat-drivers-2012-09-18/drivers/net/wireless/ti/wl18xx/Kconfig0000644000175000017500000000026012026211315023551 0ustar mcgrofmcgrofconfig WL18XX tristate "TI wl18xx support" depends on MAC80211 select WLCORE ---help--- This module adds support for wireless adapters based on TI WiLink 8 chipsets. compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/0000755000175000017500000000000012026211315022370 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/main.c0000644000175000017500000043047612026211315023476 0ustar mcgrofmcgrof /* * This file is part of wl1271 * * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "wlcore.h" #include "debug.h" #include "wl12xx_80211.h" #include "io.h" #include "event.h" #include "tx.h" #include "rx.h" #include "ps.h" #include "init.h" #include "debugfs.h" #include "cmd.h" #include "boot.h" #include "testmode.h" #include "scan.h" #include "hw_ops.h" #define WL1271_BOOT_RETRIES 3 #define WL1271_BOOT_RETRIES 3 static char *fwlog_param; static bool bug_on_recovery; static bool no_recovery; static void __wl1271_op_remove_interface(struct wl1271 *wl, struct ieee80211_vif *vif, bool reset_tx_queues); static void wlcore_op_stop_locked(struct wl1271 *wl); static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS)) return -EINVAL; if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) return 0; if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags)) return 0; ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid); if (ret < 0) return ret; wl12xx_croc(wl, wlvif->role_id); wl1271_info("Association completed."); return 0; } static int wl1271_reg_notify(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_supported_band *band; struct ieee80211_channel *ch; int i; band = wiphy->bands[IEEE80211_BAND_5GHZ]; for (i = 0; i < band->n_channels; i++) { ch = &band->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; if (ch->flags & IEEE80211_CHAN_RADAR) ch->flags |= IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN; } return 0; } static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable) { int ret = 0; /* we should hold wl->mutex */ ret = wl1271_acx_ps_rx_streaming(wl, wlvif, enable); if (ret < 0) goto out; if (enable) set_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags); else clear_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags); out: return ret; } /* * this function is being called when the rx_streaming interval * has beed changed or rx_streaming should be disabled */ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret = 0; int period = wl->conf.rx_streaming.interval; /* don't reconfigure if rx_streaming is disabled */ if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags)) goto out; /* reconfigure/disable according to new streaming_period */ if (period && test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && (wl->conf.rx_streaming.always || test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) ret = wl1271_set_rx_streaming(wl, wlvif, true); else { ret = wl1271_set_rx_streaming(wl, wlvif, false); /* don't cancel_work_sync since we might deadlock */ del_timer_sync(&wlvif->rx_streaming_timer); } out: return ret; } static void wl1271_rx_streaming_enable_work(struct work_struct *work) { int ret; struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif, rx_streaming_enable_work); struct wl1271 *wl = wlvif->wl; mutex_lock(&wl->mutex); if (test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags) || !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) || (!wl->conf.rx_streaming.always && !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) goto out; if (!wl->conf.rx_streaming.interval) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_set_rx_streaming(wl, wlvif, true); if (ret < 0) goto out_sleep; /* stop it after some time of inactivity */ mod_timer(&wlvif->rx_streaming_timer, jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration)); out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } static void wl1271_rx_streaming_disable_work(struct work_struct *work) { int ret; struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif, rx_streaming_disable_work); struct wl1271 *wl = wlvif->wl; mutex_lock(&wl->mutex); if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_set_rx_streaming(wl, wlvif, false); if (ret) goto out_sleep; out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } static void wl1271_rx_streaming_timer(unsigned long data) { struct wl12xx_vif *wlvif = (struct wl12xx_vif *)data; struct wl1271 *wl = wlvif->wl; ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work); } /* wl->mutex must be taken */ void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl) { /* if the watchdog is not armed, don't do anything */ if (wl->tx_allocated_blocks == 0) return; cancel_delayed_work(&wl->tx_watchdog_work); ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work, msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout)); } static void wl12xx_tx_watchdog_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, tx_watchdog_work); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; /* Tx went out in the meantime - everything is ok */ if (unlikely(wl->tx_allocated_blocks == 0)) goto out; /* * if a ROC is in progress, we might not have any Tx for a long * time (e.g. pending Tx on the non-ROC channels) */ if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) { wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC", wl->conf.tx.tx_watchdog_timeout); wl12xx_rearm_tx_watchdog_locked(wl); goto out; } /* * if a scan is in progress, we might not have any Tx for a long * time */ if (wl->scan.state != WL1271_SCAN_STATE_IDLE) { wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan", wl->conf.tx.tx_watchdog_timeout); wl12xx_rearm_tx_watchdog_locked(wl); goto out; } /* * AP might cache a frame for a long time for a sleeping station, * so rearm the timer if there's an AP interface with stations. If * Tx is genuinely stuck we will most hopefully discover it when all * stations are removed due to inactivity. */ if (wl->active_sta_count) { wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has " " %d stations", wl->conf.tx.tx_watchdog_timeout, wl->active_sta_count); wl12xx_rearm_tx_watchdog_locked(wl); goto out; } wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery", wl->conf.tx.tx_watchdog_timeout); wl12xx_queue_recovery_work(wl); out: mutex_unlock(&wl->mutex); } static void wlcore_adjust_conf(struct wl1271 *wl) { /* Adjust settings according to optional module parameters */ if (fwlog_param) { if (!strcmp(fwlog_param, "continuous")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; } else if (!strcmp(fwlog_param, "ondemand")) { wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND; } else if (!strcmp(fwlog_param, "dbgpins")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS; } else if (!strcmp(fwlog_param, "disable")) { wl->conf.fwlog.mem_blocks = 0; wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE; } else { wl1271_error("Unknown fwlog parameter %s", fwlog_param); } } } static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid, u8 tx_pkts) { bool fw_ps, single_sta; fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); single_sta = (wl->active_sta_count == 1); /* * Wake up from high level PS if the STA is asleep with too little * packets in FW or if the STA is awake. */ if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_end(wl, wlvif, hlid); /* * Start high-level PS if the STA is asleep with enough blocks in FW. * Make an exception if this is the only connected station. In this * case FW-memory congestion is not a problem. */ else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } static void wl12xx_irq_update_links_status(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct wl_fw_status_2 *status) { struct wl1271_link *lnk; u32 cur_fw_ps_map; u8 hlid, cnt; /* TODO: also use link_fast_bitmap here */ cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); if (wl->ap_fw_ps_map != cur_fw_ps_map) { wl1271_debug(DEBUG_PSM, "link ps prev 0x%x cur 0x%x changed 0x%x", wl->ap_fw_ps_map, cur_fw_ps_map, wl->ap_fw_ps_map ^ cur_fw_ps_map); wl->ap_fw_ps_map = cur_fw_ps_map; } for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) { lnk = &wl->links[hlid]; cnt = status->counters.tx_lnk_free_pkts[hlid] - lnk->prev_freed_pkts; lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid]; lnk->allocated_pkts -= cnt; wl12xx_irq_ps_regulate_link(wl, wlvif, hlid, lnk->allocated_pkts); } } static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status_1 *status_1, struct wl_fw_status_2 *status_2) { struct wl12xx_vif *wlvif; struct timespec ts; u32 old_tx_blk_count = wl->tx_blocks_available; int avail, freed_blocks; int i; size_t status_len; int ret; status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*status_2) + wl->fw_status_priv_len; ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1, status_len, false); if (ret < 0) return ret; wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", status_1->intr, status_1->fw_rx_counter, status_1->drv_rx_counter, status_1->tx_results_counter); for (i = 0; i < NUM_TX_QUEUES; i++) { /* prevent wrap-around in freed-packets counter */ wl->tx_allocated_pkts[i] -= (status_2->counters.tx_released_pkts[i] - wl->tx_pkts_freed[i]) & 0xff; wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i]; } /* prevent wrap-around in total blocks counter */ if (likely(wl->tx_blocks_freed <= le32_to_cpu(status_2->total_released_blks))) freed_blocks = le32_to_cpu(status_2->total_released_blks) - wl->tx_blocks_freed; else freed_blocks = 0x100000000LL - wl->tx_blocks_freed + le32_to_cpu(status_2->total_released_blks); wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks); wl->tx_allocated_blocks -= freed_blocks; /* * If the FW freed some blocks: * If we still have allocated blocks - re-arm the timer, Tx is * not stuck. Otherwise, cancel the timer (no Tx currently). */ if (freed_blocks) { if (wl->tx_allocated_blocks) wl12xx_rearm_tx_watchdog_locked(wl); else cancel_delayed_work(&wl->tx_watchdog_work); } avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks; /* * The FW might change the total number of TX memblocks before * we get a notification about blocks being released. Thus, the * available blocks calculation might yield a temporary result * which is lower than the actual available blocks. Keeping in * mind that only blocks that were allocated can be moved from * TX to RX, tx_blocks_available should never decrease here. */ wl->tx_blocks_available = max((int)wl->tx_blocks_available, avail); /* if more blocks are available now, tx work can be scheduled */ if (wl->tx_blocks_available > old_tx_blk_count) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); /* for AP update num of allocated TX blocks per link and ps status */ wl12xx_for_each_wlvif_ap(wl, wlvif) { wl12xx_irq_update_links_status(wl, wlvif, status_2); } /* update the host-chipset time offset */ getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - (s64)le32_to_cpu(status_2->fw_localtime); return 0; } static void wl1271_flush_deferred_work(struct wl1271 *wl) { struct sk_buff *skb; /* Pass all received frames to the network stack */ while ((skb = skb_dequeue(&wl->deferred_rx_queue))) ieee80211_rx_ni(wl->hw, skb); /* Return sent skbs to the network stack */ while ((skb = skb_dequeue(&wl->deferred_tx_queue))) ieee80211_tx_status_ni(wl->hw, skb); } static void wl1271_netstack_work(struct work_struct *work) { struct wl1271 *wl = container_of(work, struct wl1271, netstack_work); do { wl1271_flush_deferred_work(wl); } while (skb_queue_len(&wl->deferred_rx_queue)); } #define WL1271_IRQ_MAX_LOOPS 256 static int wlcore_irq_locked(struct wl1271 *wl) { int ret = 0; u32 intr; int loopcount = WL1271_IRQ_MAX_LOOPS; bool done = false; unsigned int defer_count; unsigned long flags; /* * In case edge triggered interrupt must be used, we cannot iterate * more than once without introducing race conditions with the hardirq. */ if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) loopcount = 1; wl1271_debug(DEBUG_IRQ, "IRQ work"); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; while (!done && loopcount--) { /* * In order to avoid a race with the hardirq, clear the flag * before acknowledging the chip. Since the mutex is held, * wl1271_ps_elp_wakeup cannot be called concurrently. */ clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); smp_mb__after_clear_bit(); ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); if (ret < 0) goto out; wlcore_hw_tx_immediate_compl(wl); intr = le32_to_cpu(wl->fw_status_1->intr); intr &= WLCORE_ALL_INTR_MASK; if (!intr) { done = true; continue; } if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { wl1271_error("HW watchdog interrupt received! starting recovery."); wl->watchdog_recovery = true; ret = -EIO; /* restarting the chip. ignore any other interrupt. */ goto out; } if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) { wl1271_error("SW watchdog interrupt received! " "starting recovery."); wl->watchdog_recovery = true; ret = -EIO; /* restarting the chip. ignore any other interrupt. */ goto out; } if (likely(intr & WL1271_ACX_INTR_DATA)) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); ret = wlcore_rx(wl, wl->fw_status_1); if (ret < 0) goto out; /* Check if any tx blocks were freed */ spin_lock_irqsave(&wl->wl_lock, flags); if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && wl1271_tx_total_queue_count(wl) > 0) { spin_unlock_irqrestore(&wl->wl_lock, flags); /* * In order to avoid starvation of the TX path, * call the work function directly. */ ret = wlcore_tx_work_locked(wl); if (ret < 0) goto out; } else { spin_unlock_irqrestore(&wl->wl_lock, flags); } /* check for tx results */ ret = wlcore_hw_tx_delayed_compl(wl); if (ret < 0) goto out; /* Make sure the deferred queues don't get too long */ defer_count = skb_queue_len(&wl->deferred_tx_queue) + skb_queue_len(&wl->deferred_rx_queue); if (defer_count > WL1271_DEFERRED_QUEUE_LIMIT) wl1271_flush_deferred_work(wl); } if (intr & WL1271_ACX_INTR_EVENT_A) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); ret = wl1271_event_handle(wl, 0); if (ret < 0) goto out; } if (intr & WL1271_ACX_INTR_EVENT_B) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); ret = wl1271_event_handle(wl, 1); if (ret < 0) goto out; } if (intr & WL1271_ACX_INTR_INIT_COMPLETE) wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_INIT_COMPLETE"); if (intr & WL1271_ACX_INTR_HW_AVAILABLE) wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); } wl1271_ps_elp_sleep(wl); out: return ret; } static irqreturn_t wlcore_irq(int irq, void *cookie) { int ret; unsigned long flags; struct wl1271 *wl = cookie; /* TX might be handled here, avoid redundant work */ set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); cancel_work_sync(&wl->tx_work); mutex_lock(&wl->mutex); ret = wlcore_irq_locked(wl); if (ret) wl12xx_queue_recovery_work(wl); spin_lock_irqsave(&wl->wl_lock, flags); /* In case TX was not handled here, queue TX work */ clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags); if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && wl1271_tx_total_queue_count(wl) > 0) ieee80211_queue_work(wl->hw, &wl->tx_work); spin_unlock_irqrestore(&wl->wl_lock, flags); mutex_unlock(&wl->mutex); return IRQ_HANDLED; } struct vif_counter_data { u8 counter; struct ieee80211_vif *cur_vif; bool cur_vif_running; }; static void wl12xx_vif_count_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct vif_counter_data *counter = data; counter->counter++; if (counter->cur_vif == vif) counter->cur_vif_running = true; } /* caller must not hold wl->mutex, as it might deadlock */ static void wl12xx_get_vif_count(struct ieee80211_hw *hw, struct ieee80211_vif *cur_vif, struct vif_counter_data *data) { memset(data, 0, sizeof(*data)); data->cur_vif = cur_vif; ieee80211_iterate_active_interfaces(hw, wl12xx_vif_count_iter, data); } static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt) { const struct firmware *fw; const char *fw_name; enum wl12xx_fw_type fw_type; int ret; if (plt) { fw_type = WL12XX_FW_TYPE_PLT; fw_name = wl->plt_fw_name; } else { /* * we can't call wl12xx_get_vif_count() here because * wl->mutex is taken, so use the cached last_vif_count value */ if (wl->last_vif_count > 1) { fw_type = WL12XX_FW_TYPE_MULTI; fw_name = wl->mr_fw_name; } else { fw_type = WL12XX_FW_TYPE_NORMAL; fw_name = wl->sr_fw_name; } } if (wl->fw_type == fw_type) return 0; wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name); ret = request_firmware(&fw, fw_name, wl->dev); if (ret < 0) { wl1271_error("could not get firmware %s: %d", fw_name, ret); return ret; } if (fw->size % 4) { wl1271_error("firmware size is not multiple of 32 bits: %zu", fw->size); ret = -EILSEQ; goto out; } vfree(wl->fw); wl->fw_type = WL12XX_FW_TYPE_NONE; wl->fw_len = fw->size; wl->fw = vmalloc(wl->fw_len); if (!wl->fw) { wl1271_error("could not allocate memory for the firmware"); ret = -ENOMEM; goto out; } memcpy(wl->fw, fw->data, wl->fw_len); ret = 0; wl->fw_type = fw_type; out: release_firmware(fw); return ret; } static void wl1271_fetch_nvs(struct wl1271 *wl) { const struct firmware *fw; int ret; ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev); if (ret < 0) { wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d", WL12XX_NVS_NAME, ret); return; } wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); goto out; } wl->nvs_len = fw->size; out: release_firmware(fw); } void wl12xx_queue_recovery_work(struct wl1271 *wl) { WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); /* Avoid a recursive recovery */ if (!test_and_set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { wlcore_disable_interrupts_nosync(wl); ieee80211_queue_work(wl->hw, &wl->recovery_work); } } size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) { size_t len = 0; /* The FW log is a length-value list, find where the log end */ while (len < maxlen) { if (memblock[len] == 0) break; if (len + memblock[len] + 1 > maxlen) break; len += memblock[len] + 1; } /* Make sure we have enough room */ len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size)); /* Fill the FW log file, consumed by the sysfs fwlog entry */ memcpy(wl->fwlog + wl->fwlog_size, memblock, len); wl->fwlog_size += len; return len; } #define WLCORE_FW_LOG_END 0x2000000 static void wl12xx_read_fwlog_panic(struct wl1271 *wl) { u32 addr; u32 offset; u32 end_of_log; u8 *block; int ret; if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) || (wl->conf.fwlog.mem_blocks == 0)) return; wl1271_info("Reading FW panic log"); block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL); if (!block) return; /* * Make sure the chip is awake and the logger isn't active. * Do not send a stop fwlog command if the fw is hanged. */ if (wl1271_ps_elp_wakeup(wl)) goto out; if (!wl->watchdog_recovery) wl12xx_cmd_stop_fwlog(wl); /* Read the first memory block address */ ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); if (ret < 0) goto out; addr = le32_to_cpu(wl->fw_status_2->log_start_addr); if (!addr) goto out; if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) { offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor); end_of_log = WLCORE_FW_LOG_END; } else { offset = sizeof(addr); end_of_log = addr; } /* Traverse the memory blocks linked list */ do { memset(block, 0, WL12XX_HW_BLOCK_SIZE); ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE, false); if (ret < 0) goto out; /* * Memory blocks are linked to one another. The first 4 bytes * of each memory block hold the hardware address of the next * one. The last memory block points to the first one in * on demand mode and is equal to 0x2000000 in continuous mode. */ addr = le32_to_cpup((__le32 *)block); if (!wl12xx_copy_fwlog(wl, block + offset, WL12XX_HW_BLOCK_SIZE - offset)) break; } while (addr && (addr != end_of_log)); wake_up_interruptible(&wl->fwlog_waitq); out: kfree(block); } static void wlcore_print_recovery(struct wl1271 *wl) { u32 pc = 0; u32 hint_sts = 0; int ret; wl1271_info("Hardware recovery in progress. FW ver: %s", wl->chip.fw_ver_str); /* change partitions momentarily so we can read the FW pc */ ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) return; ret = wlcore_read_reg(wl, REG_PC_ON_RECOVERY, &pc); if (ret < 0) return; ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &hint_sts); if (ret < 0) return; wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts); wlcore_set_partition(wl, &wl->ptable[PART_WORK]); } static void wl1271_recovery_work(struct work_struct *work) { struct wl1271 *wl = container_of(work, struct wl1271, recovery_work); struct wl12xx_vif *wlvif; struct ieee80211_vif *vif; mutex_lock(&wl->mutex); if (wl->state != WL1271_STATE_ON || wl->plt) goto out_unlock; if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) { wl12xx_read_fwlog_panic(wl); wlcore_print_recovery(wl); } BUG_ON(bug_on_recovery && !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); if (no_recovery) { wl1271_info("No recovery (chosen on module load). Fw will remain stuck."); goto out_unlock; } /* * Advance security sequence number to overcome potential progress * in the firmware during recovery. This doens't hurt if the network is * not encrypted. */ wl12xx_for_each_wlvif(wl, wlvif) { if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) || test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) wlvif->tx_security_seq += WL1271_TX_SQN_POST_RECOVERY_PADDING; } /* Prevent spurious TX during FW restart */ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); if (wl->sched_scanning) { ieee80211_sched_scan_stopped(wl->hw); wl->sched_scanning = false; } /* reboot the chipset */ while (!list_empty(&wl->wlvif_list)) { wlvif = list_first_entry(&wl->wlvif_list, struct wl12xx_vif, list); vif = wl12xx_wlvif_to_vif(wlvif); __wl1271_op_remove_interface(wl, vif, false); } wlcore_op_stop_locked(wl); ieee80211_restart_hw(wl->hw); /* * Its safe to enable TX now - the queues are stopped after a request * to restart the HW. */ wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); out_unlock: wl->watchdog_recovery = false; clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); mutex_unlock(&wl->mutex); } static int wlcore_fw_wakeup(struct wl1271 *wl) { return wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); } static int wl1271_setup(struct wl1271 *wl) { wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*wl->fw_status_2) + wl->fw_status_priv_len, GFP_KERNEL); if (!wl->fw_status_1) return -ENOMEM; wl->fw_status_2 = (struct wl_fw_status_2 *) (((u8 *) wl->fw_status_1) + WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); if (!wl->tx_res_if) { kfree(wl->fw_status_1); return -ENOMEM; } return 0; } static int wl12xx_set_power_on(struct wl1271 *wl) { int ret; msleep(WL1271_PRE_POWER_ON_SLEEP); ret = wl1271_power_on(wl); if (ret < 0) goto out; msleep(WL1271_POWER_ON_SLEEP); wl1271_io_reset(wl); wl1271_io_init(wl); ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) goto fail; /* ELP module wake up */ ret = wlcore_fw_wakeup(wl); if (ret < 0) goto fail; out: return ret; fail: wl1271_power_off(wl); return ret; } static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt) { int ret = 0; ret = wl12xx_set_power_on(wl); if (ret < 0) goto out; /* * For wl127x based devices we could use the default block * size (512 bytes), but due to a bug in the sdio driver, we * need to set it explicitly after the chip is powered on. To * simplify the code and since the performance impact is * negligible, we use the same block size for all different * chip types. * * Check if the bus supports blocksize alignment and, if it * doesn't, make sure we don't have the quirk. */ if (!wl1271_set_block_size(wl)) wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; /* TODO: make sure the lower driver has set things up correctly */ ret = wl1271_setup(wl); if (ret < 0) goto out; ret = wl12xx_fetch_firmware(wl, plt); if (ret < 0) goto out; out: return ret; } int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode) { int retries = WL1271_BOOT_RETRIES; struct wiphy *wiphy = wl->hw->wiphy; static const char* const PLT_MODE[] = { "PLT_OFF", "PLT_ON", "PLT_FEM_DETECT" }; int ret; mutex_lock(&wl->mutex); wl1271_notice("power up"); if (wl->state != WL1271_STATE_OFF) { wl1271_error("cannot go into PLT state because not " "in off state: %d", wl->state); ret = -EBUSY; goto out; } /* Indicate to lower levels that we are now in PLT mode */ wl->plt = true; wl->plt_mode = plt_mode; while (retries) { retries--; ret = wl12xx_chip_wakeup(wl, true); if (ret < 0) goto power_off; ret = wl->ops->plt_init(wl); if (ret < 0) goto power_off; wl->state = WL1271_STATE_ON; wl1271_notice("firmware booted in PLT mode %s (%s)", PLT_MODE[plt_mode], wl->chip.fw_ver_str); /* update hw/fw version info in wiphy struct */ wiphy->hw_version = wl->chip.id; strncpy(wiphy->fw_version, wl->chip.fw_ver_str, sizeof(wiphy->fw_version)); goto out; power_off: wl1271_power_off(wl); } wl->plt = false; wl->plt_mode = PLT_OFF; wl1271_error("firmware boot in PLT mode failed despite %d retries", WL1271_BOOT_RETRIES); out: mutex_unlock(&wl->mutex); return ret; } int wl1271_plt_stop(struct wl1271 *wl) { int ret = 0; wl1271_notice("power down"); /* * Interrupts must be disabled before setting the state to OFF. * Otherwise, the interrupt handler might be called and exit without * reading the interrupt status. */ wlcore_disable_interrupts(wl); mutex_lock(&wl->mutex); if (!wl->plt) { mutex_unlock(&wl->mutex); /* * This will not necessarily enable interrupts as interrupts * may have been disabled when op_stop was called. It will, * however, balance the above call to disable_interrupts(). */ wlcore_enable_interrupts(wl); wl1271_error("cannot power down because not in PLT " "state: %d", wl->state); ret = -EBUSY; goto out; } mutex_unlock(&wl->mutex); wl1271_flush_deferred_work(wl); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); cancel_delayed_work_sync(&wl->connection_loss_work); mutex_lock(&wl->mutex); wl1271_power_off(wl); wl->flags = 0; wl->sleep_auth = WL1271_PSM_ILLEGAL; wl->state = WL1271_STATE_OFF; wl->plt = false; wl->plt_mode = PLT_OFF; wl->rx_counter = 0; mutex_unlock(&wl->mutex); out: return ret; } static void wl1271_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; struct wl12xx_vif *wlvif = NULL; unsigned long flags; int q, mapping; u8 hlid; if (vif) wlvif = wl12xx_vif_to_data(vif); mapping = skb_get_queue_mapping(skb); q = wl1271_tx_get_queue(mapping); hlid = wl12xx_tx_get_hlid(wl, wlvif, skb, control->sta); spin_lock_irqsave(&wl->wl_lock, flags); /* * drop the packet if the link is invalid or the queue is stopped * for any reason but watermark. Watermark is a "soft"-stop so we * allow these packets through. */ if (hlid == WL12XX_INVALID_LINK_ID || (wlvif && !test_bit(hlid, wlvif->links_map)) || (wlcore_is_queue_stopped(wl, q) && !wlcore_is_queue_stopped_by_reason(wl, q, WLCORE_QUEUE_STOP_REASON_WATERMARK))) { wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q); ieee80211_free_txskb(hw, skb); goto out; } wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d len %d", hlid, q, skb->len); skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); wl->tx_queue_count[q]++; /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK && !wlcore_is_queue_stopped_by_reason(wl, q, WLCORE_QUEUE_STOP_REASON_WATERMARK)) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q); wlcore_stop_queue_locked(wl, q, WLCORE_QUEUE_STOP_REASON_WATERMARK); } /* * The chip specific setup must run before the first TX packet - * before that, the tx_work will not be initialized! */ if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && !test_bit(WL1271_FLAG_TX_PENDING, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); out: spin_unlock_irqrestore(&wl->wl_lock, flags); } int wl1271_tx_dummy_packet(struct wl1271 *wl) { unsigned long flags; int q; /* no need to queue a new dummy packet if one is already pending */ if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) return 0; q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet)); spin_lock_irqsave(&wl->wl_lock, flags); set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); wl->tx_queue_count[q]++; spin_unlock_irqrestore(&wl->wl_lock, flags); /* The FW is low on RX memory blocks, so send the dummy packet asap */ if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) return wlcore_tx_work_locked(wl); /* * If the FW TX is busy, TX work will be scheduled by the threaded * interrupt handler function */ return 0; } /* * The size of the dummy packet should be at least 1400 bytes. However, in * order to minimize the number of bus transactions, aligning it to 512 bytes * boundaries could be beneficial, performance wise */ #define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512)) static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) { struct sk_buff *skb; struct ieee80211_hdr_3addr *hdr; unsigned int dummy_packet_size; dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE - sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr); skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE); if (!skb) { wl1271_warning("Failed to allocate a dummy packet skb"); return NULL; } skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr)); hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS); memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size); /* Dummy packets require the TID to be management */ skb->priority = WL1271_TID_MGMT; /* Initialize all fields that might be used */ skb_set_queue_mapping(skb, 0); memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); return skb; } #ifdef CONFIG_PM static int wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p) { int num_fields = 0, in_field = 0, fields_size = 0; int i, pattern_len = 0; if (!p->mask) { wl1271_warning("No mask in WoWLAN pattern"); return -EINVAL; } /* * The pattern is broken up into segments of bytes at different offsets * that need to be checked by the FW filter. Each segment is called * a field in the FW API. We verify that the total number of fields * required for this pattern won't exceed FW limits (8) * as well as the total fields buffer won't exceed the FW limit. * Note that if there's a pattern which crosses Ethernet/IP header * boundary a new field is required. */ for (i = 0; i < p->pattern_len; i++) { if (test_bit(i, (unsigned long *)p->mask)) { if (!in_field) { in_field = 1; pattern_len = 1; } else { if (i == WL1271_RX_FILTER_ETH_HEADER_SIZE) { num_fields++; fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD; pattern_len = 1; } else pattern_len++; } } else { if (in_field) { in_field = 0; fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD; num_fields++; } } } if (in_field) { fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD; num_fields++; } if (num_fields > WL1271_RX_FILTER_MAX_FIELDS) { wl1271_warning("RX Filter too complex. Too many segments"); return -EINVAL; } if (fields_size > WL1271_RX_FILTER_MAX_FIELDS_SIZE) { wl1271_warning("RX filter pattern is too big"); return -E2BIG; } return 0; } struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void) { return kzalloc(sizeof(struct wl12xx_rx_filter), GFP_KERNEL); } void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter) { int i; if (filter == NULL) return; for (i = 0; i < filter->num_fields; i++) kfree(filter->fields[i].pattern); kfree(filter); } int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, u16 offset, u8 flags, u8 *pattern, u8 len) { struct wl12xx_rx_filter_field *field; if (filter->num_fields == WL1271_RX_FILTER_MAX_FIELDS) { wl1271_warning("Max fields per RX filter. can't alloc another"); return -EINVAL; } field = &filter->fields[filter->num_fields]; field->pattern = kzalloc(len, GFP_KERNEL); if (!field->pattern) { wl1271_warning("Failed to allocate RX filter pattern"); return -ENOMEM; } filter->num_fields++; field->offset = cpu_to_le16(offset); field->flags = flags; field->len = len; memcpy(field->pattern, pattern, len); return 0; } int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter) { int i, fields_size = 0; for (i = 0; i < filter->num_fields; i++) fields_size += filter->fields[i].len + sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *); return fields_size; } void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, u8 *buf) { int i; struct wl12xx_rx_filter_field *field; for (i = 0; i < filter->num_fields; i++) { field = (struct wl12xx_rx_filter_field *)buf; field->offset = filter->fields[i].offset; field->flags = filter->fields[i].flags; field->len = filter->fields[i].len; memcpy(&field->pattern, filter->fields[i].pattern, field->len); buf += sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *) + field->len; } } /* * Allocates an RX filter returned through f * which needs to be freed using rx_filter_free() */ static int wl1271_convert_wowlan_pattern_to_rx_filter( struct cfg80211_wowlan_trig_pkt_pattern *p, struct wl12xx_rx_filter **f) { int i, j, ret = 0; struct wl12xx_rx_filter *filter; u16 offset; u8 flags, len; filter = wl1271_rx_filter_alloc(); if (!filter) { wl1271_warning("Failed to alloc rx filter"); ret = -ENOMEM; goto err; } i = 0; while (i < p->pattern_len) { if (!test_bit(i, (unsigned long *)p->mask)) { i++; continue; } for (j = i; j < p->pattern_len; j++) { if (!test_bit(j, (unsigned long *)p->mask)) break; if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE && j >= WL1271_RX_FILTER_ETH_HEADER_SIZE) break; } if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE) { offset = i; flags = WL1271_RX_FILTER_FLAG_ETHERNET_HEADER; } else { offset = i - WL1271_RX_FILTER_ETH_HEADER_SIZE; flags = WL1271_RX_FILTER_FLAG_IP_HEADER; } len = j - i; ret = wl1271_rx_filter_alloc_field(filter, offset, flags, &p->pattern[i], len); if (ret) goto err; i = j; } filter->action = FILTER_SIGNAL; *f = filter; return 0; err: wl1271_rx_filter_free(filter); *f = NULL; return ret; } static int wl1271_configure_wowlan(struct wl1271 *wl, struct cfg80211_wowlan *wow) { int i, ret; if (!wow || wow->any || !wow->n_patterns) { ret = wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL); if (ret) goto out; ret = wl1271_rx_filter_clear_all(wl); if (ret) goto out; return 0; } if (WARN_ON(wow->n_patterns > WL1271_MAX_RX_FILTERS)) return -EINVAL; /* Validate all incoming patterns before clearing current FW state */ for (i = 0; i < wow->n_patterns; i++) { ret = wl1271_validate_wowlan_pattern(&wow->patterns[i]); if (ret) { wl1271_warning("Bad wowlan pattern %d", i); return ret; } } ret = wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL); if (ret) goto out; ret = wl1271_rx_filter_clear_all(wl); if (ret) goto out; /* Translate WoWLAN patterns into filters */ for (i = 0; i < wow->n_patterns; i++) { struct cfg80211_wowlan_trig_pkt_pattern *p; struct wl12xx_rx_filter *filter = NULL; p = &wow->patterns[i]; ret = wl1271_convert_wowlan_pattern_to_rx_filter(p, &filter); if (ret) { wl1271_warning("Failed to create an RX filter from " "wowlan pattern %d", i); goto out; } ret = wl1271_rx_filter_enable(wl, i, 1, filter); wl1271_rx_filter_free(filter); if (ret) goto out; } ret = wl1271_acx_default_rx_filter_enable(wl, 1, FILTER_DROP); out: return ret; } static int wl1271_configure_suspend_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_wowlan *wow) { int ret = 0; if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) goto out; if ((wl->conf.conn.suspend_wake_up_event == wl->conf.conn.wake_up_event) && (wl->conf.conn.suspend_listen_interval == wl->conf.conn.listen_interval)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_configure_wowlan(wl, wow); if (ret < 0) goto out_sleep; ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.suspend_wake_up_event, wl->conf.conn.suspend_listen_interval); if (ret < 0) wl1271_error("suspend: set wake up conditions failed: %d", ret); out_sleep: wl1271_ps_elp_sleep(wl); out: return ret; } static int wl1271_configure_suspend_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret = 0; if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); wl1271_ps_elp_sleep(wl); out: return ret; } static int wl1271_configure_suspend(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_wowlan *wow) { if (wlvif->bss_type == BSS_TYPE_STA_BSS) return wl1271_configure_suspend_sta(wl, wlvif, wow); if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl1271_configure_suspend_ap(wl, wlvif); return 0; } static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret = 0; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS; if ((!is_ap) && (!is_sta)) return; if (is_sta && ((wl->conf.conn.suspend_wake_up_event == wl->conf.conn.wake_up_event) && (wl->conf.conn.suspend_listen_interval == wl->conf.conn.listen_interval))) return; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) return; if (is_sta) { wl1271_configure_wowlan(wl, NULL); ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.wake_up_event, wl->conf.conn.listen_interval); if (ret < 0) wl1271_error("resume: wake up conditions failed: %d", ret); } else if (is_ap) { ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); } wl1271_ps_elp_sleep(wl); } static int wl1271_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); WARN_ON(!wow); /* we want to perform the recovery before suspending */ if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { wl1271_warning("postponing suspend to perform recovery"); return -EBUSY; } wl1271_tx_flush(wl); mutex_lock(&wl->mutex); wl->wow_enabled = true; wl12xx_for_each_wlvif(wl, wlvif) { ret = wl1271_configure_suspend(wl, wlvif, wow); if (ret < 0) { mutex_unlock(&wl->mutex); wl1271_warning("couldn't prepare device to suspend"); return ret; } } mutex_unlock(&wl->mutex); /* flush any remaining work */ wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); /* * disable and re-enable interrupts in order to flush * the threaded_irq */ wlcore_disable_interrupts(wl); /* * set suspended flag to avoid triggering a new threaded_irq * work. no need for spinlock as interrupts are disabled. */ set_bit(WL1271_FLAG_SUSPENDED, &wl->flags); wlcore_enable_interrupts(wl); flush_work(&wl->tx_work); flush_delayed_work(&wl->elp_work); return 0; } static int wl1271_op_resume(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; unsigned long flags; bool run_irq_work = false, pending_recovery; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", wl->wow_enabled); WARN_ON(!wl->wow_enabled); /* * re-enable irq_work enqueuing, and call irq_work directly if * there is a pending work. */ spin_lock_irqsave(&wl->wl_lock, flags); clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags); if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags)) run_irq_work = true; spin_unlock_irqrestore(&wl->wl_lock, flags); mutex_lock(&wl->mutex); /* test the recovery flag before calling any SDIO functions */ pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); if (run_irq_work) { wl1271_debug(DEBUG_MAC80211, "run postponed irq_work directly"); /* don't talk to the HW if recovery is pending */ if (!pending_recovery) { ret = wlcore_irq_locked(wl); if (ret) wl12xx_queue_recovery_work(wl); } wlcore_enable_interrupts(wl); } if (pending_recovery) { wl1271_warning("queuing forgotten recovery on resume"); ieee80211_queue_work(wl->hw, &wl->recovery_work); goto out; } wl12xx_for_each_wlvif(wl, wlvif) { wl1271_configure_resume(wl, wlvif); } out: wl->wow_enabled = false; mutex_unlock(&wl->mutex); return 0; } #endif static int wl1271_op_start(struct ieee80211_hw *hw) { wl1271_debug(DEBUG_MAC80211, "mac80211 start"); /* * We have to delay the booting of the hardware because * we need to know the local MAC address before downloading and * initializing the firmware. The MAC address cannot be changed * after boot, and without the proper MAC address, the firmware * will not function properly. * * The MAC address is first known when the corresponding interface * is added. That is where we will initialize the hardware. */ return 0; } static void wlcore_op_stop_locked(struct wl1271 *wl) { int i; if (wl->state == WL1271_STATE_OFF) { if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) wlcore_enable_interrupts(wl); return; } /* * this must be before the cancel_work calls below, so that the work * functions don't perform further work. */ wl->state = WL1271_STATE_OFF; /* * Use the nosync variant to disable interrupts, so the mutex could be * held while doing so without deadlocking. */ wlcore_disable_interrupts_nosync(wl); mutex_unlock(&wl->mutex); wlcore_synchronize_interrupts(wl); wl1271_flush_deferred_work(wl); cancel_delayed_work_sync(&wl->scan_complete_work); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); cancel_delayed_work_sync(&wl->connection_loss_work); /* let's notify MAC80211 about the remaining pending TX frames */ wl12xx_tx_reset(wl); mutex_lock(&wl->mutex); wl1271_power_off(wl); /* * In case a recovery was scheduled, interrupts were disabled to avoid * an interrupt storm. Now that the power is down, it is safe to * re-enable interrupts to balance the disable depth */ if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) wlcore_enable_interrupts(wl); wl->band = IEEE80211_BAND_2GHZ; wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->channel_type = NL80211_CHAN_NO_HT; wl->tx_blocks_available = 0; wl->tx_allocated_blocks = 0; wl->tx_results_count = 0; wl->tx_packets_count = 0; wl->time_offset = 0; wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; wl->sched_scanning = false; wl->sleep_auth = WL1271_PSM_ILLEGAL; memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); memset(wl->roc_map, 0, sizeof(wl->roc_map)); wl->active_sta_count = 0; /* The system link is always allocated */ __set_bit(WL12XX_SYSTEM_HLID, wl->links_map); /* * this is performed after the cancel_work calls and the associated * mutex_lock, so that wl1271_op_add_interface does not accidentally * get executed before all these vars have been reset. */ wl->flags = 0; wl->tx_blocks_freed = 0; for (i = 0; i < NUM_TX_QUEUES; i++) { wl->tx_pkts_freed[i] = 0; wl->tx_allocated_pkts[i] = 0; } wl1271_debugfs_reset(wl); kfree(wl->fw_status_1); wl->fw_status_1 = NULL; wl->fw_status_2 = NULL; kfree(wl->tx_res_if); wl->tx_res_if = NULL; kfree(wl->target_mem_map); wl->target_mem_map = NULL; } static void wlcore_op_stop(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); mutex_lock(&wl->mutex); wlcore_op_stop_locked(wl); mutex_unlock(&wl->mutex); } static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx) { u8 policy = find_first_zero_bit(wl->rate_policies_map, WL12XX_MAX_RATE_POLICIES); if (policy >= WL12XX_MAX_RATE_POLICIES) return -EBUSY; __set_bit(policy, wl->rate_policies_map); *idx = policy; return 0; } static void wl12xx_free_rate_policy(struct wl1271 *wl, u8 *idx) { if (WARN_ON(*idx >= WL12XX_MAX_RATE_POLICIES)) return; __clear_bit(*idx, wl->rate_policies_map); *idx = WL12XX_MAX_RATE_POLICIES; } static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif) { switch (wlvif->bss_type) { case BSS_TYPE_AP_BSS: if (wlvif->p2p) return WL1271_ROLE_P2P_GO; else return WL1271_ROLE_AP; case BSS_TYPE_STA_BSS: if (wlvif->p2p) return WL1271_ROLE_P2P_CL; else return WL1271_ROLE_STA; case BSS_TYPE_IBSS: return WL1271_ROLE_IBSS; default: wl1271_error("invalid bss_type: %d", wlvif->bss_type); } return WL12XX_INVALID_ROLE_TYPE; } static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int i; /* clear everything but the persistent data */ memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent)); switch (ieee80211_vif_type_p2p(vif)) { case NL80211_IFTYPE_P2P_CLIENT: wlvif->p2p = 1; /* fall-through */ case NL80211_IFTYPE_STATION: wlvif->bss_type = BSS_TYPE_STA_BSS; break; case NL80211_IFTYPE_ADHOC: wlvif->bss_type = BSS_TYPE_IBSS; break; case NL80211_IFTYPE_P2P_GO: wlvif->p2p = 1; /* fall-through */ case NL80211_IFTYPE_AP: wlvif->bss_type = BSS_TYPE_AP_BSS; break; default: wlvif->bss_type = MAX_BSS_TYPE; return -EOPNOTSUPP; } wlvif->role_id = WL12XX_INVALID_ROLE_ID; wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID; wlvif->dev_hlid = WL12XX_INVALID_LINK_ID; if (wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS) { /* init sta/ibss data */ wlvif->sta.hlid = WL12XX_INVALID_LINK_ID; wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx); wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx); wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx); wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC; wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC; wlvif->rate_set = CONF_TX_RATE_MASK_BASIC; } else { /* init ap data */ wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID; wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID; wl12xx_allocate_rate_policy(wl, &wlvif->ap.mgmt_rate_idx); wl12xx_allocate_rate_policy(wl, &wlvif->ap.bcast_rate_idx); for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++) wl12xx_allocate_rate_policy(wl, &wlvif->ap.ucast_rate_idx[i]); wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES; /* * TODO: check if basic_rate shouldn't be * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); * instead (the same thing for STA above). */ wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES; /* TODO: this seems to be used only for STA, check it */ wlvif->rate_set = CONF_TX_AP_ENABLED_RATES; } wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate; wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5; wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT; /* * mac80211 configures some values globally, while we treat them * per-interface. thus, on init, we have to copy them from wl */ wlvif->band = wl->band; wlvif->channel = wl->channel; wlvif->power_level = wl->power_level; wlvif->channel_type = wl->channel_type; INIT_WORK(&wlvif->rx_streaming_enable_work, wl1271_rx_streaming_enable_work); INIT_WORK(&wlvif->rx_streaming_disable_work, wl1271_rx_streaming_disable_work); INIT_LIST_HEAD(&wlvif->list); setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer, (unsigned long) wlvif); return 0; } static bool wl12xx_init_fw(struct wl1271 *wl) { int retries = WL1271_BOOT_RETRIES; bool booted = false; struct wiphy *wiphy = wl->hw->wiphy; int ret; while (retries) { retries--; ret = wl12xx_chip_wakeup(wl, false); if (ret < 0) goto power_off; ret = wl->ops->boot(wl); if (ret < 0) goto power_off; ret = wl1271_hw_init(wl); if (ret < 0) goto irq_disable; booted = true; break; irq_disable: mutex_unlock(&wl->mutex); /* Unlocking the mutex in the middle of handling is inherently unsafe. In this case we deem it safe to do, because we need to let any possibly pending IRQ out of the system (and while we are WL1271_STATE_OFF the IRQ work function will not do anything.) Also, any other possible concurrent operations will fail due to the current state, hence the wl1271 struct should be safe. */ wlcore_disable_interrupts(wl); wl1271_flush_deferred_work(wl); cancel_work_sync(&wl->netstack_work); mutex_lock(&wl->mutex); power_off: wl1271_power_off(wl); } if (!booted) { wl1271_error("firmware boot failed despite %d retries", WL1271_BOOT_RETRIES); goto out; } wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str); /* update hw/fw version info in wiphy struct */ wiphy->hw_version = wl->chip.id; strncpy(wiphy->fw_version, wl->chip.fw_ver_str, sizeof(wiphy->fw_version)); /* * Now we know if 11a is supported (info from the NVS), so disable * 11a channels if not supported */ if (!wl->enable_11a) wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0; wl1271_debug(DEBUG_MAC80211, "11a is %ssupported", wl->enable_11a ? "" : "not "); wl->state = WL1271_STATE_ON; out: return booted; } static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif) { return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID; } /* * Check whether a fw switch (i.e. moving from one loaded * fw to another) is needed. This function is also responsible * for updating wl->last_vif_count, so it must be called before * loading a non-plt fw (so the correct fw (single-role/multi-role) * will be used). */ static bool wl12xx_need_fw_change(struct wl1271 *wl, struct vif_counter_data vif_counter_data, bool add) { enum wl12xx_fw_type current_fw = wl->fw_type; u8 vif_count = vif_counter_data.counter; if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags)) return false; /* increase the vif count if this is a new vif */ if (add && !vif_counter_data.cur_vif_running) vif_count++; wl->last_vif_count = vif_count; /* no need for fw change if the device is OFF */ if (wl->state == WL1271_STATE_OFF) return false; if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL) return true; if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI) return true; return false; } /* * Enter "forced psm". Make sure the sta is in psm against the ap, * to make the fw switch a bit more disconnection-persistent. */ static void wl12xx_force_active_psm(struct wl1271 *wl) { struct wl12xx_vif *wlvif; wl12xx_for_each_wlvif_sta(wl, wlvif) { wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE); } } static int wl1271_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct vif_counter_data vif_count; int ret = 0; u8 role_type; bool booted = false; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", ieee80211_vif_type_p2p(vif), vif->addr); wl12xx_get_vif_count(hw, vif, &vif_count); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out_unlock; /* * in some very corner case HW recovery scenarios its possible to * get here before __wl1271_op_remove_interface is complete, so * opt out if that is the case. */ if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) || test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) { ret = -EBUSY; goto out; } ret = wl12xx_init_vif_data(wl, vif); if (ret < 0) goto out; wlvif->wl = wl; role_type = wl12xx_get_role_type(wl, wlvif); if (role_type == WL12XX_INVALID_ROLE_TYPE) { ret = -EINVAL; goto out; } if (wl12xx_need_fw_change(wl, vif_count, true)) { wl12xx_force_active_psm(wl); set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags); mutex_unlock(&wl->mutex); wl1271_recovery_work(&wl->recovery_work); return 0; } /* * TODO: after the nvs issue will be solved, move this block * to start(), and make sure here the driver is ON. */ if (wl->state == WL1271_STATE_OFF) { /* * we still need this in order to configure the fw * while uploading the nvs */ memcpy(wl->addresses[0].addr, vif->addr, ETH_ALEN); booted = wl12xx_init_fw(wl); if (!booted) { ret = -EINVAL; goto out; } } if (wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS) { /* * The device role is a special role used for * rx and tx frames prior to association (as * the STA role can get packets only from * its associated bssid) */ ret = wl12xx_cmd_role_enable(wl, vif->addr, WL1271_ROLE_DEVICE, &wlvif->dev_role_id); if (ret < 0) goto out; } ret = wl12xx_cmd_role_enable(wl, vif->addr, role_type, &wlvif->role_id); if (ret < 0) goto out; ret = wl1271_init_vif_specific(wl, vif); if (ret < 0) goto out; list_add(&wlvif->list, &wl->wlvif_list); set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags); if (wlvif->bss_type == BSS_TYPE_AP_BSS) wl->ap_count++; else wl->sta_count++; out: wl1271_ps_elp_sleep(wl); out_unlock: mutex_unlock(&wl->mutex); return ret; } static void __wl1271_op_remove_interface(struct wl1271 *wl, struct ieee80211_vif *vif, bool reset_tx_queues) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int i, ret; bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) return; /* because of hardware recovery, we may get here twice */ if (wl->state != WL1271_STATE_ON) return; wl1271_info("down"); if (wl->scan.state != WL1271_SCAN_STATE_IDLE && wl->scan_vif == vif) { /* * Rearm the tx watchdog just before idling scan. This * prevents just-finished scans from triggering the watchdog */ wl12xx_rearm_tx_watchdog_locked(wl); wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan_vif = NULL; wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); } if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { /* disable active roles */ ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto deinit; if (wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS) { if (wl12xx_dev_role_started(wlvif)) wl12xx_stop_dev(wl, wlvif); ret = wl12xx_cmd_role_disable(wl, &wlvif->dev_role_id); if (ret < 0) goto deinit; } ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id); if (ret < 0) goto deinit; wl1271_ps_elp_sleep(wl); } deinit: /* clear all hlids (except system_hlid) */ wlvif->dev_hlid = WL12XX_INVALID_LINK_ID; if (wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS) { wlvif->sta.hlid = WL12XX_INVALID_LINK_ID; wl12xx_free_rate_policy(wl, &wlvif->sta.basic_rate_idx); wl12xx_free_rate_policy(wl, &wlvif->sta.ap_rate_idx); wl12xx_free_rate_policy(wl, &wlvif->sta.p2p_rate_idx); } else { wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID; wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID; wl12xx_free_rate_policy(wl, &wlvif->ap.mgmt_rate_idx); wl12xx_free_rate_policy(wl, &wlvif->ap.bcast_rate_idx); for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++) wl12xx_free_rate_policy(wl, &wlvif->ap.ucast_rate_idx[i]); wl1271_free_ap_keys(wl, wlvif); } dev_kfree_skb(wlvif->probereq); wlvif->probereq = NULL; wl12xx_tx_reset_wlvif(wl, wlvif); if (wl->last_wlvif == wlvif) wl->last_wlvif = NULL; list_del(&wlvif->list); memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map)); wlvif->role_id = WL12XX_INVALID_ROLE_ID; wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID; if (is_ap) wl->ap_count--; else wl->sta_count--; /* * Last AP, have more stations. Configure sleep auth according to STA. * Don't do thin on unintended recovery. */ if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) && !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) goto unlock; if (wl->ap_count == 0 && is_ap && wl->sta_count) { u8 sta_auth = wl->conf.conn.sta_sleep_auth; /* Configure for power according to debugfs */ if (sta_auth != WL1271_PSM_ILLEGAL) wl1271_acx_sleep_auth(wl, sta_auth); /* Configure for power always on */ else if (wl->quirks & WLCORE_QUIRK_NO_ELP) wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); /* Configure for ELP power saving */ else wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); } unlock: mutex_unlock(&wl->mutex); del_timer_sync(&wlvif->rx_streaming_timer); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); mutex_lock(&wl->mutex); } static void wl1271_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl12xx_vif *iter; struct vif_counter_data vif_count; bool cancel_recovery = true; wl12xx_get_vif_count(hw, vif, &vif_count); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF || !test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) goto out; /* * wl->vif can be null here if someone shuts down the interface * just when hardware recovery has been started. */ wl12xx_for_each_wlvif(wl, iter) { if (iter != wlvif) continue; __wl1271_op_remove_interface(wl, vif, true); break; } WARN_ON(iter != wlvif); if (wl12xx_need_fw_change(wl, vif_count, false)) { wl12xx_force_active_psm(wl); set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags); wl12xx_queue_recovery_work(wl); cancel_recovery = false; } out: mutex_unlock(&wl->mutex); if (cancel_recovery) cancel_work_sync(&wl->recovery_work); } static int wl12xx_op_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype new_type, bool p2p) { struct wl1271 *wl = hw->priv; int ret; set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags); wl1271_op_remove_interface(hw, vif); vif->type = new_type; vif->p2p = p2p; ret = wl1271_op_add_interface(hw, vif); clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags); return ret; } static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool set_assoc) { int ret; bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS); /* * One of the side effects of the JOIN command is that is clears * WPA/WPA2 keys from the chipset. Performing a JOIN while associated * to a WPA/WPA2 access point will therefore kill the data-path. * Currently the only valid scenario for JOIN during association * is on roaming, in which case we will also be given new keys. * Keep the below message for now, unless it starts bothering * users who really like to roam a lot :) */ if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) wl1271_info("JOIN while associated."); /* clear encryption type */ wlvif->encryption_type = KEY_NONE; if (set_assoc) set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); if (is_ibss) ret = wl12xx_cmd_role_start_ibss(wl, wlvif); else ret = wl12xx_cmd_role_start_sta(wl, wlvif); if (ret < 0) goto out; if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) goto out; /* * The join command disable the keep-alive mode, shut down its process, * and also clear the template config, so we need to reset it all after * the join. The acx_aid starts the keep-alive process, and the order * of the commands below is relevant. */ ret = wl1271_acx_keep_alive_mode(wl, wlvif, true); if (ret < 0) goto out; ret = wl1271_acx_aid(wl, wlvif, wlvif->aid); if (ret < 0) goto out; ret = wl12xx_cmd_build_klv_null_data(wl, wlvif); if (ret < 0) goto out; ret = wl1271_acx_keep_alive_config(wl, wlvif, CMD_TEMPL_KLV_IDX_NULL_DATA, ACX_KEEP_ALIVE_TPL_VALID); if (ret < 0) goto out; out: return ret; } static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); wl12xx_cmd_stop_channel_switch(wl); ieee80211_chswitch_done(vif, false); } /* to stop listening to a channel, we disconnect */ ret = wl12xx_cmd_role_stop_sta(wl, wlvif); if (ret < 0) goto out; /* reset TX security counters on a clean disconnect */ wlvif->tx_security_last_seq_lsb = 0; wlvif->tx_security_seq = 0; out: return ret; } static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) { wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band]; wlvif->rate_set = wlvif->basic_rate_set; } static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool idle) { int ret; bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); if (idle == cur_idle) return 0; if (idle) { /* no need to croc if we weren't busy (e.g. during boot) */ if (wl12xx_dev_role_started(wlvif)) { ret = wl12xx_stop_dev(wl, wlvif); if (ret < 0) goto out; } wlvif->rate_set = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) goto out; ret = wl1271_acx_keep_alive_config( wl, wlvif, CMD_TEMPL_KLV_IDX_NULL_DATA, ACX_KEEP_ALIVE_TPL_INVALID); if (ret < 0) goto out; clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); } else { /* The current firmware only supports sched_scan in idle */ if (wl->sched_scanning) { wl1271_scan_sched_scan_stop(wl, wlvif); ieee80211_sched_scan_stopped(wl->hw); } ret = wl12xx_start_dev(wl, wlvif); if (ret < 0) goto out; set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); } out: return ret; } static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_conf *conf, u32 changed) { bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int channel, ret; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); /* if the channel changes while joined, join again */ if (changed & IEEE80211_CONF_CHANGE_CHANNEL && ((wlvif->band != conf->channel->band) || (wlvif->channel != channel) || (wlvif->channel_type != conf->channel_type))) { /* send all pending packets */ ret = wlcore_tx_work_locked(wl); if (ret < 0) return ret; wlvif->band = conf->channel->band; wlvif->channel = channel; wlvif->channel_type = conf->channel_type; if (is_ap) { wl1271_set_band_rate(wl, wlvif); ret = wl1271_init_ap_rates(wl, wlvif); if (ret < 0) wl1271_error("AP rate policy change failed %d", ret); } else { /* * FIXME: the mac80211 should really provide a fixed * rate to use here. for now, just use the smallest * possible rate for the band as a fixed rate for * association frames and other control messages. */ if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) wl1271_set_band_rate(wl, wlvif); wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) wl1271_warning("rate policy for channel " "failed %d", ret); /* * change the ROC channel. do it only if we are * not idle. otherwise, CROC will be called * anyway. */ if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && wl12xx_dev_role_started(wlvif) && !(conf->flags & IEEE80211_CONF_IDLE)) { ret = wl12xx_stop_dev(wl, wlvif); if (ret < 0) return ret; ret = wl12xx_start_dev(wl, wlvif); if (ret < 0) return ret; } } } if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) { if ((conf->flags & IEEE80211_CONF_PS) && test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { int ps_mode; char *ps_mode_str; if (wl->conf.conn.forced_ps) { ps_mode = STATION_POWER_SAVE_MODE; ps_mode_str = "forced"; } else { ps_mode = STATION_AUTO_PS_MODE; ps_mode_str = "auto"; } wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); ret = wl1271_ps_set_mode(wl, wlvif, ps_mode); if (ret < 0) wl1271_warning("enter %s ps failed %d", ps_mode_str, ret); } else if (!(conf->flags & IEEE80211_CONF_PS) && test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { wl1271_debug(DEBUG_PSM, "auto ps disabled"); ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE); if (ret < 0) wl1271_warning("exit auto ps failed %d", ret); } } if (conf->power_level != wlvif->power_level) { ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level); if (ret < 0) return ret; wlvif->power_level = conf->power_level; } return 0; } static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s" " changed 0x%x", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level, conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use", changed); /* * mac80211 will go to idle nearly immediately after transmitting some * frames, such as the deauth. To make sure those frames reach the air, * wait here until the TX queue is fully flushed. */ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || ((changed & IEEE80211_CONF_CHANGE_IDLE) && (conf->flags & IEEE80211_CONF_IDLE))) wl1271_tx_flush(wl); mutex_lock(&wl->mutex); /* we support configuring the channel and band even while off */ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { wl->band = conf->channel->band; wl->channel = channel; wl->channel_type = conf->channel_type; } if (changed & IEEE80211_CONF_CHANGE_POWER) wl->power_level = conf->power_level; if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* configure each interface */ wl12xx_for_each_wlvif(wl, wlvif) { ret = wl12xx_config_vif(wl, wlvif, conf, changed); if (ret < 0) goto out_sleep; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } struct wl1271_filter_params { bool enabled; int mc_list_length; u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; }; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) #else static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *mc_list) #endif { struct wl1271_filter_params *fp; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; #else int i; #endif struct wl1271 *wl = hw->priv; if (unlikely(wl->state == WL1271_STATE_OFF)) return 0; fp = kzalloc(sizeof(*fp), GFP_ATOMIC); if (!fp) { wl1271_error("Out of memory setting filters."); return 0; } /* update multicast filtering parameters */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) fp->mc_list_length = 0; if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) { #else fp->enabled = true; if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) { mc_count = 0; #endif fp->enabled = false; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) } else { fp->enabled = true; netdev_hw_addr_list_for_each(ha, mc_list) { #else } fp->mc_list_length = 0; for (i = 0; i < mc_count; i++) { if (mc_list->da_addrlen == ETH_ALEN) { #endif memcpy(fp->mc_list[fp->mc_list_length], #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr, ETH_ALEN); #else mc_list->da_addr, ETH_ALEN); #endif fp->mc_list_length++; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) } #else } else wl1271_warning("Unknown mc address length."); mc_list = mc_list->next; #endif } return (u64)(unsigned long)fp; } #define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_CONTROL | \ FIF_OTHER_BSS) static void wl1271_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *total, u64 multicast) { struct wl1271_filter_params *fp = (void *)(unsigned long)multicast; struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x" " total %x", changed, *total); mutex_lock(&wl->mutex); *total &= WL1271_SUPPORTED_FILTERS; changed &= WL1271_SUPPORTED_FILTERS; if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl12xx_for_each_wlvif(wl, wlvif) { if (wlvif->bss_type != BSS_TYPE_AP_BSS) { if (*total & FIF_ALLMULTI) ret = wl1271_acx_group_address_tbl(wl, wlvif, false, NULL, 0); else if (fp) ret = wl1271_acx_group_address_tbl(wl, wlvif, fp->enabled, fp->mc_list, fp->mc_list_length); if (ret < 0) goto out_sleep; } } /* * the fw doesn't provide an api to configure the filters. instead, * the filters configuration is based on the active roles / ROC * state. */ out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); kfree(fp); } static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 id, u8 key_type, u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16) { struct wl1271_ap_key *ap_key; int i; wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id); if (key_size > MAX_KEY_SIZE) return -EINVAL; /* * Find next free entry in ap_keys. Also check we are not replacing * an existing key. */ for (i = 0; i < MAX_NUM_KEYS; i++) { if (wlvif->ap.recorded_keys[i] == NULL) break; if (wlvif->ap.recorded_keys[i]->id == id) { wl1271_warning("trying to record key replacement"); return -EINVAL; } } if (i == MAX_NUM_KEYS) return -EBUSY; ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL); if (!ap_key) return -ENOMEM; ap_key->id = id; ap_key->key_type = key_type; ap_key->key_size = key_size; memcpy(ap_key->key, key, key_size); ap_key->hlid = hlid; ap_key->tx_seq_32 = tx_seq_32; ap_key->tx_seq_16 = tx_seq_16; wlvif->ap.recorded_keys[i] = ap_key; return 0; } static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int i; for (i = 0; i < MAX_NUM_KEYS; i++) { kfree(wlvif->ap.recorded_keys[i]); wlvif->ap.recorded_keys[i] = NULL; } } static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int i, ret = 0; struct wl1271_ap_key *key; bool wep_key_added = false; for (i = 0; i < MAX_NUM_KEYS; i++) { u8 hlid; if (wlvif->ap.recorded_keys[i] == NULL) break; key = wlvif->ap.recorded_keys[i]; hlid = key->hlid; if (hlid == WL12XX_INVALID_LINK_ID) hlid = wlvif->ap.bcast_hlid; ret = wl1271_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE, key->id, key->key_type, key->key_size, key->key, hlid, key->tx_seq_32, key->tx_seq_16); if (ret < 0) goto out; if (key->key_type == KEY_WEP) wep_key_added = true; } if (wep_key_added) { ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key, wlvif->ap.bcast_hlid); if (ret < 0) goto out; } out: wl1271_free_ap_keys(wl, wlvif); return ret; } static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, u32 tx_seq_32, u16 tx_seq_16, struct ieee80211_sta *sta) { int ret; bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); if (is_ap) { struct wl1271_station *wl_sta; u8 hlid; if (sta) { wl_sta = (struct wl1271_station *)sta->drv_priv; hlid = wl_sta->hlid; } else { hlid = wlvif->ap.bcast_hlid; } if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { /* * We do not support removing keys after AP shutdown. * Pretend we do to make mac80211 happy. */ if (action != KEY_ADD_OR_REPLACE) return 0; ret = wl1271_record_ap_key(wl, wlvif, id, key_type, key_size, key, hlid, tx_seq_32, tx_seq_16); } else { ret = wl1271_cmd_set_ap_key(wl, wlvif, action, id, key_type, key_size, key, hlid, tx_seq_32, tx_seq_16); } if (ret < 0) return ret; } else { const u8 *addr; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; addr = sta ? sta->addr : bcast_addr; if (is_zero_ether_addr(addr)) { /* We dont support TX only encryption */ return -EOPNOTSUPP; } /* The wl1271 does not allow to remove unicast keys - they will be cleared automatically on next CMD_JOIN. Ignore the request silently, as we dont want the mac80211 to emit an error message. */ if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr)) return 0; /* don't remove key if hlid was already deleted */ if (action == KEY_REMOVE && wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) return 0; ret = wl1271_cmd_set_sta_key(wl, wlvif, action, id, key_type, key_size, key, addr, tx_seq_32, tx_seq_16); if (ret < 0) return ret; /* the default WEP key needs to be configured at least once */ if (key_type == KEY_WEP) { ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key, wlvif->sta.hlid); if (ret < 0) return ret; } } return 0; } static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct wl1271 *wl = hw->priv; return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf); } int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; u32 tx_seq_32 = 0; u16 tx_seq_16 = 0; u8 key_type; wl1271_debug(DEBUG_MAC80211, "mac80211 set key"); wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta); wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", key_conf->cipher, key_conf->keyidx, key_conf->keylen, key_conf->flags); wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) { ret = -EAGAIN; goto out_unlock; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out_unlock; switch (key_conf->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: key_type = KEY_WEP; key_conf->hw_key_idx = key_conf->keyidx; break; case WLAN_CIPHER_SUITE_TKIP: key_type = KEY_TKIP; key_conf->hw_key_idx = key_conf->keyidx; tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq); tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq); break; case WLAN_CIPHER_SUITE_CCMP: key_type = KEY_AES; key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq); tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq); break; case WL1271_CIPHER_SUITE_GEM: key_type = KEY_GEM; tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq); tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq); break; default: wl1271_error("Unknown key algo 0x%x", key_conf->cipher); ret = -EOPNOTSUPP; goto out_sleep; } switch (cmd) { case SET_KEY: ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, tx_seq_32, tx_seq_16, sta); if (ret < 0) { wl1271_error("Could not add or replace key"); goto out_sleep; } /* * reconfiguring arp response if the unicast (or common) * encryption key type was changed */ if (wlvif->bss_type == BSS_TYPE_STA_BSS && (sta || key_type == KEY_WEP) && wlvif->encryption_type != key_type) { wlvif->encryption_type = key_type; ret = wl1271_cmd_build_arp_rsp(wl, wlvif); if (ret < 0) { wl1271_warning("build arp rsp failed: %d", ret); goto out_sleep; } } break; case DISABLE_KEY: ret = wl1271_set_key(wl, wlvif, KEY_REMOVE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, 0, 0, sta); if (ret < 0) { wl1271_error("Could not remove key"); goto out_sleep; } break; default: wl1271_error("Unsupported key cmd 0x%x", cmd); ret = -EOPNOTSUPP; break; } out_sleep: wl1271_ps_elp_sleep(wl); out_unlock: mutex_unlock(&wl->mutex); return ret; } EXPORT_SYMBOL_GPL(wlcore_set_key); static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct wl1271 *wl = hw->priv; int ret; u8 *ssid = NULL; size_t len = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan"); if (req->n_ssids) { ssid = req->ssids[0].ssid; len = req->ssids[0].ssid_len; } mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { /* * We cannot return -EBUSY here because cfg80211 will expect * a call to ieee80211_scan_completed if we do - in this case * there won't be any call. */ ret = -EAGAIN; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* fail if there is any role in ROC */ if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) { /* don't allow scanning right now */ ret = -EBUSY; goto out_sleep; } ret = wl1271_scan(hw->priv, vif, ssid, len, req); out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan"); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) goto out; if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; if (wl->scan.state != WL1271_SCAN_STATE_DONE) { ret = wl1271_scan_stop(wl); if (ret < 0) goto out_sleep; } /* * Rearm the tx watchdog just before idling scan. This * prevents just-finished scans from triggering the watchdog */ wl12xx_rearm_tx_watchdog_locked(wl); wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan_vif = NULL; wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); cancel_delayed_work_sync(&wl->scan_complete_work); } static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start"); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { ret = -EAGAIN; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies); if (ret < 0) goto out_sleep; ret = wl1271_scan_sched_scan_start(wl, wlvif); if (ret < 0) goto out_sleep; wl->sched_scanning = true; out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop"); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1271_scan_sched_scan_stop(wl, wlvif); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) { struct wl1271 *wl = hw->priv; int ret = 0; mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) { ret = -EAGAIN; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_acx_frag_threshold(wl, value); if (ret < 0) wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; int ret = 0; mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) { ret = -EAGAIN; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl12xx_for_each_wlvif(wl, wlvif) { ret = wl1271_acx_rts_threshold(wl, wlvif, value); if (ret < 0) wl1271_warning("set rts threshold failed: %d", ret); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb, int offset) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); u8 ssid_len; const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, skb->len - offset); if (!ptr) { wl1271_error("No SSID in IEs!"); return -ENOENT; } ssid_len = ptr[1]; if (ssid_len > IEEE80211_MAX_SSID_LEN) { wl1271_error("SSID is too long!"); return -EINVAL; } wlvif->ssid_len = ssid_len; memcpy(wlvif->ssid, ptr+2, ssid_len); return 0; } static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset) { int len; const u8 *next, *end = skb->data + skb->len; u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset, skb->len - ieoffset); if (!ie) return; len = ie[1] + 2; next = ie + len; memmove(ie, next, end - next); skb_trim(skb, skb->len - len); } static void wl12xx_remove_vendor_ie(struct sk_buff *skb, unsigned int oui, u8 oui_type, int ieoffset) { int len; const u8 *next, *end = skb->data + skb->len; u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type, skb->data + ieoffset, skb->len - ieoffset); if (!ie) return; len = ie[1] + 2; next = ie + len; memmove(ie, next, end - next); skb_trim(skb, skb->len - len); } static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct sk_buff *skb; int ret; skb = ieee80211_proberesp_get(wl->hw, vif); if (!skb) return -EOPNOTSUPP; ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_AP_PROBE_RESPONSE, skb->data, skb->len, 0, rates); dev_kfree_skb(skb); if (ret < 0) goto out; wl1271_debug(DEBUG_AP, "probe response updated"); set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags); out: return ret; } static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl, struct ieee80211_vif *vif, u8 *probe_rsp_data, size_t probe_rsp_len, u32 rates) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE]; int ssid_ie_offset, ie_offset, templ_len; const u8 *ptr; /* no need to change probe response if the SSID is set correctly */ if (wlvif->ssid_len > 0) return wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_AP_PROBE_RESPONSE, probe_rsp_data, probe_rsp_len, 0, rates); if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) { wl1271_error("probe_rsp template too big"); return -EINVAL; } /* start searching from IE offset */ ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset, probe_rsp_len - ie_offset); if (!ptr) { wl1271_error("No SSID in beacon!"); return -EINVAL; } ssid_ie_offset = ptr - probe_rsp_data; ptr += (ptr[1] + 2); memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset); /* insert SSID from bss_conf */ probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID; probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len; memcpy(probe_rsp_templ + ssid_ie_offset + 2, bss_conf->ssid, bss_conf->ssid_len); templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len; memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len, ptr, probe_rsp_len - (ptr - probe_rsp_data)); templ_len += probe_rsp_len - (ptr - probe_rsp_data); return wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_AP_PROBE_RESPONSE, probe_rsp_templ, templ_len, 0, rates); } static int wl1271_bss_erp_info_changed(struct wl1271 *wl, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret = 0; if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_SHORT); else ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_LONG); if (ret < 0) { wl1271_warning("Set slot time failed %d", ret); goto out; } } if (changed & BSS_CHANGED_ERP_PREAMBLE) { if (bss_conf->use_short_preamble) wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT); else wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { if (bss_conf->use_cts_prot) ret = wl1271_acx_cts_protect(wl, wlvif, CTSPROTECT_ENABLE); else ret = wl1271_acx_cts_protect(wl, wlvif, CTSPROTECT_DISABLE); if (ret < 0) { wl1271_warning("Set ctsprotect failed %d", ret); goto out; } } out: return ret; } static int wlcore_set_beacon_template(struct wl1271 *wl, struct ieee80211_vif *vif, bool is_ap) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct ieee80211_hdr *hdr; u32 min_rate; int ret; int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); u16 tmpl_id; if (!beacon) { ret = -EINVAL; goto out; } wl1271_debug(DEBUG_MASTER, "beacon updated"); ret = wl1271_ssid_set(vif, beacon, ieoffset); if (ret < 0) { dev_kfree_skb(beacon); goto out; } min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON : CMD_TEMPL_BEACON; ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id, beacon->data, beacon->len, 0, min_rate); if (ret < 0) { dev_kfree_skb(beacon); goto out; } /* * In case we already have a probe-resp beacon set explicitly * by usermode, don't use the beacon data. */ if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags)) goto end_bcn; /* remove TIM ie from probe response */ wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset); /* * remove p2p ie from probe response. * the fw reponds to probe requests that don't include * the p2p ie. probe requests with p2p ie will be passed, * and will be responded by the supplicant (the spec * forbids including the p2p ie when responding to probe * requests that didn't include it). */ wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, ieoffset); hdr = (struct ieee80211_hdr *) beacon->data; hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); if (is_ap) ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif, beacon->data, beacon->len, min_rate); else ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_PROBE_RESPONSE, beacon->data, beacon->len, 0, min_rate); end_bcn: dev_kfree_skb(beacon); if (ret < 0) goto out; out: return ret; } static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret = 0; if ((changed & BSS_CHANGED_BEACON_INT)) { wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d", bss_conf->beacon_int); wlvif->beacon_int = bss_conf->beacon_int; } if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) { u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); wl1271_ap_set_probe_resp_tmpl(wl, rate, vif); } if ((changed & BSS_CHANGED_BEACON)) { ret = wlcore_set_beacon_template(wl, vif, is_ap); if (ret < 0) goto out; } out: if (ret != 0) wl1271_error("beacon info change failed: %d", ret); return ret; } /* AP mode changes */ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret = 0; if ((changed & BSS_CHANGED_BASIC_RATES)) { u32 rates = bss_conf->basic_rates; wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates, wlvif->band); wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_init_ap_rates(wl, wlvif); if (ret < 0) { wl1271_error("AP rate policy change failed %d", ret); goto out; } ret = wl1271_ap_init_templates(wl, vif); if (ret < 0) goto out; ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif); if (ret < 0) goto out; ret = wlcore_set_beacon_template(wl, vif, true); if (ret < 0) goto out; } ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed); if (ret < 0) goto out; if ((changed & BSS_CHANGED_BEACON_ENABLED)) { if (bss_conf->enable_beacon) { if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { ret = wl12xx_cmd_role_start_ap(wl, wlvif); if (ret < 0) goto out; ret = wl1271_ap_init_hwenc(wl, wlvif); if (ret < 0) goto out; set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags); wl1271_debug(DEBUG_AP, "started AP"); } } else { if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { ret = wl12xx_cmd_role_stop_ap(wl, wlvif); if (ret < 0) goto out; clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags); clear_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags); wl1271_debug(DEBUG_AP, "stopped AP"); } } } ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed); if (ret < 0) goto out; /* Handle HT information change */ if ((changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { ret = wl1271_acx_set_ht_information(wl, wlvif, bss_conf->ht_operation_mode); if (ret < 0) { wl1271_warning("Set ht information failed %d", ret); goto out; } } out: return; } /* STA/IBSS mode changes */ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); bool do_join = false, set_assoc = false; bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS); bool ibss_joined = false; u32 sta_rate_set = 0; int ret; struct ieee80211_sta *sta; bool sta_exists = false; struct ieee80211_sta_ht_cap sta_ht_cap; if (is_ibss) { ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed); if (ret < 0) goto out; } if (changed & BSS_CHANGED_IBSS) { if (bss_conf->ibss_joined) { set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags); ibss_joined = true; } else { if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) wl1271_unjoin(wl, wlvif); } } if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined) do_join = true; /* Need to update the SSID (for filtering etc) */ if ((changed & BSS_CHANGED_BEACON) && ibss_joined) do_join = true; if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) { wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s", bss_conf->enable_beacon ? "enabled" : "disabled"); do_join = true; } if (changed & BSS_CHANGED_IDLE && !is_ibss) { ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle); if (ret < 0) wl1271_warning("idle mode change failed %d", ret); } if ((changed & BSS_CHANGED_CQM)) { bool enable = false; if (bss_conf->cqm_rssi_thold) enable = true; ret = wl1271_acx_rssi_snr_trigger(wl, wlvif, enable, bss_conf->cqm_rssi_thold, bss_conf->cqm_rssi_hyst); if (ret < 0) goto out; wlvif->rssi_thold = bss_conf->cqm_rssi_thold; } if (changed & BSS_CHANGED_BSSID) if (!is_zero_ether_addr(bss_conf->bssid)) { ret = wl12xx_cmd_build_null_data(wl, wlvif); if (ret < 0) goto out; ret = wl1271_build_qos_null_data(wl, vif); if (ret < 0) goto out; } if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) { rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!sta) goto sta_not_found; /* save the supp_rates of the ap */ sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band]; if (sta->ht_cap.ht_supported) sta_rate_set |= (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) | (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET); sta_ht_cap = sta->ht_cap; sta_exists = true; sta_not_found: rcu_read_unlock(); } if ((changed & BSS_CHANGED_ASSOC)) { if (bss_conf->assoc) { u32 rates; int ieoffset; wlvif->aid = bss_conf->aid; wlvif->channel_type = bss_conf->channel_type; wlvif->beacon_int = bss_conf->beacon_int; do_join = true; set_assoc = true; /* * use basic rates from AP, and determine lowest rate * to use with control frames. */ rates = bss_conf->basic_rates; wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates, wlvif->band); wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); if (sta_rate_set) wlvif->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rate_set, wlvif->band); ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) goto out; /* * with wl1271, we don't need to update the * beacon_int and dtim_period, because the firmware * updates it by itself when the first beacon is * received after a join. */ ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid); if (ret < 0) goto out; /* * Get a template for hardware connection maintenance */ dev_kfree_skb(wlvif->probereq); wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl, wlvif, NULL); ieoffset = offsetof(struct ieee80211_mgmt, u.probe_req.variable); wl1271_ssid_set(vif, wlvif->probereq, ieoffset); /* enable the connection monitoring feature */ ret = wl1271_acx_conn_monit_params(wl, wlvif, true); if (ret < 0) goto out; } else { /* use defaults when not associated */ bool was_assoc = !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); bool was_ifup = !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags); wlvif->aid = 0; /* free probe-request template */ dev_kfree_skb(wlvif->probereq); wlvif->probereq = NULL; /* revert back to minimum rates for the current band */ wl1271_set_band_rate(wl, wlvif); wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) goto out; /* disable connection monitor features */ ret = wl1271_acx_conn_monit_params(wl, wlvif, false); /* Disable the keep-alive feature */ ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); if (ret < 0) goto out; /* restore the bssid filter and go to dummy bssid */ if (was_assoc) { /* * we might have to disable roc, if there was * no IF_OPER_UP notification. */ if (!was_ifup) { ret = wl12xx_croc(wl, wlvif->role_id); if (ret < 0) goto out; } /* * (we also need to disable roc in case of * roaming on the same channel. until we will * have a better flow...) */ if (test_bit(wlvif->dev_role_id, wl->roc_map)) { ret = wl12xx_croc(wl, wlvif->dev_role_id); if (ret < 0) goto out; } wl1271_unjoin(wl, wlvif); if (!bss_conf->idle) wl12xx_start_dev(wl, wlvif); } } } if (changed & BSS_CHANGED_IBSS) { wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d", bss_conf->ibss_joined); if (bss_conf->ibss_joined) { u32 rates = bss_conf->basic_rates; wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates, wlvif->band); wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); /* by default, use 11b + OFDM rates */ wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES; ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) goto out; } } ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed); if (ret < 0) goto out; if (do_join) { ret = wl1271_join(wl, wlvif, set_assoc); if (ret < 0) { wl1271_warning("cmd join failed %d", ret); goto out; } /* ROC until connected (after EAPOL exchange) */ if (!is_ibss) { ret = wl12xx_roc(wl, wlvif, wlvif->role_id); if (ret < 0) goto out; if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags)) wl12xx_set_authorized(wl, wlvif); } /* * stop device role if started (we might already be in * STA/IBSS role). */ if (wl12xx_dev_role_started(wlvif)) { ret = wl12xx_stop_dev(wl, wlvif); if (ret < 0) goto out; } } /* Handle new association with HT. Do this after join. */ if (sta_exists) { if ((changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, true, wlvif->sta.hlid); if (ret < 0) { wl1271_warning("Set ht cap true failed %d", ret); goto out; } } /* handle new association without HT and disassociation */ else if (changed & BSS_CHANGED_ASSOC) { ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, false, wlvif->sta.hlid); if (ret < 0) { wl1271_warning("Set ht cap false failed %d", ret); goto out; } } } /* Handle HT information change. Done after join. */ if ((changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { ret = wl1271_acx_set_ht_information(wl, wlvif, bss_conf->ht_operation_mode); if (ret < 0) { wl1271_warning("Set ht information failed %d", ret); goto out; } } /* Handle arp filtering. Done after join. */ if ((changed & BSS_CHANGED_ARP_FILTER) || (!is_ibss && (changed & BSS_CHANGED_QOS))) { __be32 addr = bss_conf->arp_addr_list[0]; wlvif->sta.qos = bss_conf->qos; WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled) { wlvif->ip_addr = addr; /* * The template should have been configured only upon * association. however, it seems that the correct ip * isn't being set (when sending), so we have to * reconfigure the template upon every ip change. */ ret = wl1271_cmd_build_arp_rsp(wl, wlvif); if (ret < 0) { wl1271_warning("build arp rsp failed: %d", ret); goto out; } ret = wl1271_acx_arp_ip_filter(wl, wlvif, (ACX_ARP_FILTER_ARP_FILTERING | ACX_ARP_FILTER_AUTO_ARP), addr); } else { wlvif->ip_addr = 0; ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr); } if (ret < 0) goto out; } out: return; } static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x", (int)changed); /* * make sure to cancel pending disconnections if our association * state changed */ if (!is_ap && (changed & BSS_CHANGED_ASSOC)) cancel_delayed_work_sync(&wl->connection_loss_work); if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) wl1271_tx_flush(wl); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; if (is_ap) wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed); else wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } static int wl1271_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); u8 ps_scheme; int ret = 0; mutex_lock(&wl->mutex); wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); if (params->uapsd) ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER; else ps_scheme = CONF_PS_SCHEME_LEGACY; if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* * the txop is confed in units of 32us by the mac80211, * we need us */ ret = wl1271_acx_ac_cfg(wl, wlvif, wl1271_tx_get_queue(queue), params->cw_min, params->cw_max, params->aifs, params->txop << 5); if (ret < 0) goto out_sleep; ret = wl1271_acx_tid_cfg(wl, wlvif, wl1271_tx_get_queue(queue), CONF_CHANNEL_TYPE_EDCF, wl1271_tx_get_queue(queue), ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0); out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); u64 mactime = ULLONG_MAX; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf"); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl12xx_acx_tsf_info(wl, wlvif, &mactime); if (ret < 0) goto out_sleep; out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return mactime; } static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; survey->filled = 0; return 0; } static int wl1271_allocate_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta) { struct wl1271_station *wl_sta; int ret; if (wl->active_sta_count >= AP_MAX_STATIONS) { wl1271_warning("could not allocate HLID - too much stations"); return -EBUSY; } wl_sta = (struct wl1271_station *)sta->drv_priv; ret = wl12xx_allocate_link(wl, wlvif, &wl_sta->hlid); if (ret < 0) { wl1271_warning("could not allocate HLID - too many links"); return -EBUSY; } set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map); memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN); wl->active_sta_count++; return 0; } void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) { if (!test_bit(hlid, wlvif->ap.sta_hlid_map)) return; clear_bit(hlid, wlvif->ap.sta_hlid_map); memset(wl->links[hlid].addr, 0, ETH_ALEN); wl->links[hlid].ba_bitmap = 0; __clear_bit(hlid, &wl->ap_ps_map); __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); wl12xx_free_link(wl, wlvif, &hlid); wl->active_sta_count--; /* * rearm the tx watchdog when the last STA is freed - give the FW a * chance to return STA-buffered packets before complaining. */ if (wl->active_sta_count == 0) wl12xx_rearm_tx_watchdog_locked(wl); } static int wl12xx_sta_add(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta) { struct wl1271_station *wl_sta; int ret = 0; u8 hlid; wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid); ret = wl1271_allocate_sta(wl, wlvif, sta); if (ret < 0) return ret; wl_sta = (struct wl1271_station *)sta->drv_priv; hlid = wl_sta->hlid; ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid); if (ret < 0) wl1271_free_sta(wl, wlvif, hlid); return ret; } static int wl12xx_sta_remove(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta) { struct wl1271_station *wl_sta; int ret = 0, id; wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid); wl_sta = (struct wl1271_station *)sta->drv_priv; id = wl_sta->hlid; if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map))) return -EINVAL; ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid); if (ret < 0) return ret; wl1271_free_sta(wl, wlvif, wl_sta->hlid); return ret; } static int wl12xx_update_sta_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state) { struct wl1271_station *wl_sta; u8 hlid; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS; int ret; wl_sta = (struct wl1271_station *)sta->drv_priv; hlid = wl_sta->hlid; /* Add station (AP mode) */ if (is_ap && old_state == IEEE80211_STA_NOTEXIST && new_state == IEEE80211_STA_NONE) return wl12xx_sta_add(wl, wlvif, sta); /* Remove station (AP mode) */ if (is_ap && old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST) { /* must not fail */ wl12xx_sta_remove(wl, wlvif, sta); return 0; } /* Authorize station (AP mode) */ if (is_ap && new_state == IEEE80211_STA_AUTHORIZED) { ret = wl12xx_cmd_set_peer_state(wl, hlid); if (ret < 0) return ret; ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid); return ret; } /* Authorize station */ if (is_sta && new_state == IEEE80211_STA_AUTHORIZED) { set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags); return wl12xx_set_authorized(wl, wlvif); } if (is_sta && old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags); return 0; } return 0; } static int wl12xx_op_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 sta %d state=%d->%d", sta->aid, old_state, new_state); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) { ret = -EBUSY; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); if (new_state < old_state) return 0; return ret; } static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; u8 hlid, *ba_bitmap; wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action, tid); /* sanity check - the fields in FW are only 8bits wide */ if (WARN_ON(tid > 0xFF)) return -ENOTSUPP; mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) { ret = -EAGAIN; goto out; } if (wlvif->bss_type == BSS_TYPE_STA_BSS) { hlid = wlvif->sta.hlid; ba_bitmap = &wlvif->sta.ba_rx_bitmap; } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) { struct wl1271_station *wl_sta; wl_sta = (struct wl1271_station *)sta->drv_priv; hlid = wl_sta->hlid; ba_bitmap = &wl->links[hlid].ba_bitmap; } else { ret = -EINVAL; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d", tid, action); switch (action) { case IEEE80211_AMPDU_RX_START: if (!wlvif->ba_support || !wlvif->ba_allowed) { ret = -ENOTSUPP; break; } if (wl->ba_rx_session_count >= RX_BA_MAX_SESSIONS) { ret = -EBUSY; wl1271_error("exceeded max RX BA sessions"); break; } if (*ba_bitmap & BIT(tid)) { ret = -EINVAL; wl1271_error("cannot enable RX BA session on active " "tid: %d", tid); break; } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true, hlid); if (!ret) { *ba_bitmap |= BIT(tid); wl->ba_rx_session_count++; } break; case IEEE80211_AMPDU_RX_STOP: if (!(*ba_bitmap & BIT(tid))) { /* * this happens on reconfig - so only output a debug * message for now, and don't fail the function. */ wl1271_debug(DEBUG_MAC80211, "no active RX BA session on tid: %d", tid); ret = 0; break; } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false, hlid); if (!ret) { *ba_bitmap &= ~BIT(tid); wl->ba_rx_session_count--; } break; /* * The BA initiator session management in FW independently. * Falling break here on purpose for all TX APDU commands. */ case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_OPERATIONAL: ret = -EINVAL; break; default: wl1271_error("Incorrect ampdu action id=%x\n", action); ret = -EINVAL; } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl1271 *wl = hw->priv; int i, ret = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x", mask->control[NL80211_BAND_2GHZ].legacy, mask->control[NL80211_BAND_5GHZ].legacy); mutex_lock(&wl->mutex); for (i = 0; i < WLCORE_NUM_BANDS; i++) wlvif->bitrate_masks[i] = wl1271_tx_enabled_rates_get(wl, mask->control[i].legacy, i); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; if (wlvif->bss_type == BSS_TYPE_STA_BSS && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1271_set_band_rate(wl, wlvif); wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_acx_sta_rate_policies(wl, wlvif); wl1271_ps_elp_sleep(wl); } out: mutex_unlock(&wl->mutex); return ret; } static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch"); wl1271_tx_flush(wl); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) { wl12xx_for_each_wlvif_sta(wl, wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); ieee80211_chswitch_done(vif, false); } goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* TODO: change mac80211 to pass vif as param */ wl12xx_for_each_wlvif_sta(wl, wlvif) { ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch); if (!ret) set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop) { struct wl1271 *wl = hw->priv; wl1271_tx_flush(wl); } static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; bool ret = false; mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; /* packets are considered pending if in the TX queue or the FW */ ret = (wl1271_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0); out: mutex_unlock(&wl->mutex); return ret; } /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { { .bitrate = 10, .hw_value = CONF_HW_BIT_RATE_1MBPS, .hw_value_short = CONF_HW_BIT_RATE_1MBPS, }, { .bitrate = 20, .hw_value = CONF_HW_BIT_RATE_2MBPS, .hw_value_short = CONF_HW_BIT_RATE_2MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = CONF_HW_BIT_RATE_5_5MBPS, .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .hw_value = CONF_HW_BIT_RATE_11MBPS, .hw_value_short = CONF_HW_BIT_RATE_11MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = CONF_HW_BIT_RATE_6MBPS, .hw_value_short = CONF_HW_BIT_RATE_6MBPS, }, { .bitrate = 90, .hw_value = CONF_HW_BIT_RATE_9MBPS, .hw_value_short = CONF_HW_BIT_RATE_9MBPS, }, { .bitrate = 120, .hw_value = CONF_HW_BIT_RATE_12MBPS, .hw_value_short = CONF_HW_BIT_RATE_12MBPS, }, { .bitrate = 180, .hw_value = CONF_HW_BIT_RATE_18MBPS, .hw_value_short = CONF_HW_BIT_RATE_18MBPS, }, { .bitrate = 240, .hw_value = CONF_HW_BIT_RATE_24MBPS, .hw_value_short = CONF_HW_BIT_RATE_24MBPS, }, { .bitrate = 360, .hw_value = CONF_HW_BIT_RATE_36MBPS, .hw_value_short = CONF_HW_BIT_RATE_36MBPS, }, { .bitrate = 480, .hw_value = CONF_HW_BIT_RATE_48MBPS, .hw_value_short = CONF_HW_BIT_RATE_48MBPS, }, { .bitrate = 540, .hw_value = CONF_HW_BIT_RATE_54MBPS, .hw_value_short = CONF_HW_BIT_RATE_54MBPS, }, }; /* can't be const, mac80211 writes to this */ static struct ieee80211_channel wl1271_channels[] = { { .hw_value = 1, .center_freq = 2412, .max_power = 25 }, { .hw_value = 2, .center_freq = 2417, .max_power = 25 }, { .hw_value = 3, .center_freq = 2422, .max_power = 25 }, { .hw_value = 4, .center_freq = 2427, .max_power = 25 }, { .hw_value = 5, .center_freq = 2432, .max_power = 25 }, { .hw_value = 6, .center_freq = 2437, .max_power = 25 }, { .hw_value = 7, .center_freq = 2442, .max_power = 25 }, { .hw_value = 8, .center_freq = 2447, .max_power = 25 }, { .hw_value = 9, .center_freq = 2452, .max_power = 25 }, { .hw_value = 10, .center_freq = 2457, .max_power = 25 }, { .hw_value = 11, .center_freq = 2462, .max_power = 25 }, { .hw_value = 12, .center_freq = 2467, .max_power = 25 }, { .hw_value = 13, .center_freq = 2472, .max_power = 25 }, { .hw_value = 14, .center_freq = 2484, .max_power = 25 }, }; /* can't be const, mac80211 writes to this */ static struct ieee80211_supported_band wl1271_band_2ghz = { .channels = wl1271_channels, .n_channels = ARRAY_SIZE(wl1271_channels), .bitrates = wl1271_rates, .n_bitrates = ARRAY_SIZE(wl1271_rates), }; /* 5 GHz data rates for WL1273 */ static struct ieee80211_rate wl1271_rates_5ghz[] = { { .bitrate = 60, .hw_value = CONF_HW_BIT_RATE_6MBPS, .hw_value_short = CONF_HW_BIT_RATE_6MBPS, }, { .bitrate = 90, .hw_value = CONF_HW_BIT_RATE_9MBPS, .hw_value_short = CONF_HW_BIT_RATE_9MBPS, }, { .bitrate = 120, .hw_value = CONF_HW_BIT_RATE_12MBPS, .hw_value_short = CONF_HW_BIT_RATE_12MBPS, }, { .bitrate = 180, .hw_value = CONF_HW_BIT_RATE_18MBPS, .hw_value_short = CONF_HW_BIT_RATE_18MBPS, }, { .bitrate = 240, .hw_value = CONF_HW_BIT_RATE_24MBPS, .hw_value_short = CONF_HW_BIT_RATE_24MBPS, }, { .bitrate = 360, .hw_value = CONF_HW_BIT_RATE_36MBPS, .hw_value_short = CONF_HW_BIT_RATE_36MBPS, }, { .bitrate = 480, .hw_value = CONF_HW_BIT_RATE_48MBPS, .hw_value_short = CONF_HW_BIT_RATE_48MBPS, }, { .bitrate = 540, .hw_value = CONF_HW_BIT_RATE_54MBPS, .hw_value_short = CONF_HW_BIT_RATE_54MBPS, }, }; /* 5 GHz band channels for WL1273 */ static struct ieee80211_channel wl1271_channels_5ghz[] = { { .hw_value = 7, .center_freq = 5035, .max_power = 25 }, { .hw_value = 8, .center_freq = 5040, .max_power = 25 }, { .hw_value = 9, .center_freq = 5045, .max_power = 25 }, { .hw_value = 11, .center_freq = 5055, .max_power = 25 }, { .hw_value = 12, .center_freq = 5060, .max_power = 25 }, { .hw_value = 16, .center_freq = 5080, .max_power = 25 }, { .hw_value = 34, .center_freq = 5170, .max_power = 25 }, { .hw_value = 36, .center_freq = 5180, .max_power = 25 }, { .hw_value = 38, .center_freq = 5190, .max_power = 25 }, { .hw_value = 40, .center_freq = 5200, .max_power = 25 }, { .hw_value = 42, .center_freq = 5210, .max_power = 25 }, { .hw_value = 44, .center_freq = 5220, .max_power = 25 }, { .hw_value = 46, .center_freq = 5230, .max_power = 25 }, { .hw_value = 48, .center_freq = 5240, .max_power = 25 }, { .hw_value = 52, .center_freq = 5260, .max_power = 25 }, { .hw_value = 56, .center_freq = 5280, .max_power = 25 }, { .hw_value = 60, .center_freq = 5300, .max_power = 25 }, { .hw_value = 64, .center_freq = 5320, .max_power = 25 }, { .hw_value = 100, .center_freq = 5500, .max_power = 25 }, { .hw_value = 104, .center_freq = 5520, .max_power = 25 }, { .hw_value = 108, .center_freq = 5540, .max_power = 25 }, { .hw_value = 112, .center_freq = 5560, .max_power = 25 }, { .hw_value = 116, .center_freq = 5580, .max_power = 25 }, { .hw_value = 120, .center_freq = 5600, .max_power = 25 }, { .hw_value = 124, .center_freq = 5620, .max_power = 25 }, { .hw_value = 128, .center_freq = 5640, .max_power = 25 }, { .hw_value = 132, .center_freq = 5660, .max_power = 25 }, { .hw_value = 136, .center_freq = 5680, .max_power = 25 }, { .hw_value = 140, .center_freq = 5700, .max_power = 25 }, { .hw_value = 149, .center_freq = 5745, .max_power = 25 }, { .hw_value = 153, .center_freq = 5765, .max_power = 25 }, { .hw_value = 157, .center_freq = 5785, .max_power = 25 }, { .hw_value = 161, .center_freq = 5805, .max_power = 25 }, { .hw_value = 165, .center_freq = 5825, .max_power = 25 }, }; static struct ieee80211_supported_band wl1271_band_5ghz = { .channels = wl1271_channels_5ghz, .n_channels = ARRAY_SIZE(wl1271_channels_5ghz), .bitrates = wl1271_rates_5ghz, .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz), }; static const struct ieee80211_ops wl1271_ops = { .start = wl1271_op_start, .stop = wlcore_op_stop, .add_interface = wl1271_op_add_interface, .remove_interface = wl1271_op_remove_interface, .change_interface = wl12xx_op_change_interface, #ifdef CONFIG_PM .suspend = wl1271_op_suspend, .resume = wl1271_op_resume, #endif .config = wl1271_op_config, .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, .set_key = wlcore_op_set_key, .hw_scan = wl1271_op_hw_scan, .cancel_hw_scan = wl1271_op_cancel_hw_scan, .sched_scan_start = wl1271_op_sched_scan_start, .sched_scan_stop = wl1271_op_sched_scan_stop, .bss_info_changed = wl1271_op_bss_info_changed, .set_frag_threshold = wl1271_op_set_frag_threshold, .set_rts_threshold = wl1271_op_set_rts_threshold, .conf_tx = wl1271_op_conf_tx, .get_tsf = wl1271_op_get_tsf, .get_survey = wl1271_op_get_survey, .sta_state = wl12xx_op_sta_state, .ampdu_action = wl1271_op_ampdu_action, .tx_frames_pending = wl1271_tx_frames_pending, .set_bitrate_mask = wl12xx_set_bitrate_mask, .channel_switch = wl12xx_op_channel_switch, .flush = wlcore_op_flush, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band) { u8 idx; BUG_ON(band >= 2); if (unlikely(rate >= wl->hw_tx_rate_tbl_size)) { wl1271_error("Illegal RX rate from HW: %d", rate); return 0; } idx = wl->band_rate_to_idx[band][rate]; if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) { wl1271_error("Unsupported RX rate from HW: %d", rate); return 0; } return idx; } static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", wl->sg_enabled); mutex_unlock(&wl->mutex); return len; } static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct wl1271 *wl = dev_get_drvdata(dev); unsigned long res; int ret; ret = kstrtoul(buf, 10, &res); if (ret < 0) { wl1271_warning("incorrect value written to bt_coex_mode"); return count; } mutex_lock(&wl->mutex); res = !!res; if (res == wl->sg_enabled) goto out; wl->sg_enabled = res; if (wl->state == WL1271_STATE_OFF) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1271_acx_sg_enable(wl, wl->sg_enabled); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, wl1271_sysfs_show_bt_coex_state, wl1271_sysfs_store_bt_coex_state); static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); if (wl->hw_pg_ver >= 0) len = snprintf(buf, len, "%d\n", wl->hw_pg_ver); else len = snprintf(buf, len, "n/a\n"); mutex_unlock(&wl->mutex); return len; } static DEVICE_ATTR(hw_pg_ver, S_IRUGO, wl1271_sysfs_show_hw_pg_ver, NULL); static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; int ret; ret = mutex_lock_interruptible(&wl->mutex); if (ret < 0) return -ERESTARTSYS; /* Let only one thread read the log at a time, blocking others */ while (wl->fwlog_size == 0) { DEFINE_WAIT(wait); prepare_to_wait_exclusive(&wl->fwlog_waitq, &wait, TASK_INTERRUPTIBLE); if (wl->fwlog_size != 0) { finish_wait(&wl->fwlog_waitq, &wait); break; } mutex_unlock(&wl->mutex); schedule(); finish_wait(&wl->fwlog_waitq, &wait); if (signal_pending(current)) return -ERESTARTSYS; ret = mutex_lock_interruptible(&wl->mutex); if (ret < 0) return -ERESTARTSYS; } /* Check if the fwlog is still valid */ if (wl->fwlog_size < 0) { mutex_unlock(&wl->mutex); return 0; } /* Seeking is not supported - old logs are not kept. Disregard pos. */ len = min(count, (size_t)wl->fwlog_size); wl->fwlog_size -= len; memcpy(buffer, wl->fwlog, len); /* Make room for new messages */ memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); mutex_unlock(&wl->mutex); return len; } static struct bin_attribute fwlog_attr = { .attr = {.name = "fwlog", .mode = S_IRUSR}, .read = wl1271_sysfs_read_fwlog, }; static void wl1271_connection_loss_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, connection_loss_work); wl1271_info("Connection loss work."); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; /* Call mac80211 connection loss */ wl12xx_for_each_wlvif_sta(wl, wlvif) { if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) goto out; vif = wl12xx_wlvif_to_vif(wlvif); ieee80211_connection_loss(vif); } out: mutex_unlock(&wl->mutex); } static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic, int n) { int i; wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x, n %d", oui, nic, n); if (nic + n - 1 > 0xffffff) wl1271_warning("NIC part of the MAC address wraps around!"); for (i = 0; i < n; i++) { wl->addresses[i].addr[0] = (u8)(oui >> 16); wl->addresses[i].addr[1] = (u8)(oui >> 8); wl->addresses[i].addr[2] = (u8) oui; wl->addresses[i].addr[3] = (u8)(nic >> 16); wl->addresses[i].addr[4] = (u8)(nic >> 8); wl->addresses[i].addr[5] = (u8) nic; nic++; } wl->hw->wiphy->n_addresses = n; wl->hw->wiphy->addresses = wl->addresses; } static int wl12xx_get_hw_info(struct wl1271 *wl) { int ret; ret = wl12xx_set_power_on(wl); if (ret < 0) goto out; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id); if (ret < 0) goto out; wl->fuse_oui_addr = 0; wl->fuse_nic_addr = 0; ret = wl->ops->get_pg_ver(wl, &wl->hw_pg_ver); if (ret < 0) goto out; if (wl->ops->get_mac) ret = wl->ops->get_mac(wl); out: wl1271_power_off(wl); return ret; } static int wl1271_register_hw(struct wl1271 *wl) { int ret; u32 oui_addr = 0, nic_addr = 0; if (wl->mac80211_registered) return 0; wl1271_fetch_nvs(wl); if (wl->nvs != NULL) { /* NOTE: The wl->nvs->nvs element must be first, in * order to simplify the casting, we assume it is at * the beginning of the wl->nvs structure. */ u8 *nvs_ptr = (u8 *)wl->nvs; oui_addr = (nvs_ptr[11] << 16) + (nvs_ptr[10] << 8) + nvs_ptr[6]; nic_addr = (nvs_ptr[5] << 16) + (nvs_ptr[4] << 8) + nvs_ptr[3]; } /* if the MAC address is zeroed in the NVS derive from fuse */ if (oui_addr == 0 && nic_addr == 0) { oui_addr = wl->fuse_oui_addr; /* fuse has the BD_ADDR, the WLAN addresses are the next two */ nic_addr = wl->fuse_nic_addr + 1; } wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr, 2); ret = ieee80211_register_hw(wl->hw); if (ret < 0) { wl1271_error("unable to register mac80211 hw: %d", ret); goto out; } wl->mac80211_registered = true; wl1271_debugfs_init(wl); wl1271_notice("loaded"); out: return ret; } static void wl1271_unregister_hw(struct wl1271 *wl) { if (wl->plt) wl1271_plt_stop(wl); ieee80211_unregister_hw(wl->hw); wl->mac80211_registered = false; } static const struct ieee80211_iface_limit wlcore_iface_limits[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT), }, }; static const struct ieee80211_iface_combination wlcore_iface_combinations[] = { { .num_different_channels = 1, .max_interfaces = 2, .limits = wlcore_iface_limits, .n_limits = ARRAY_SIZE(wlcore_iface_limits), }, }; static int wl1271_init_ieee80211(struct wl1271 *wl) { static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WL1271_CIPHER_SUITE_GEM, }; /* The tx descriptor buffer */ wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr); if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP; /* unit us */ /* FIXME: find a proper value */ wl->hw->channel_change_time = 10000; wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | IEEE80211_HW_SCAN_WHILE_IDLE; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->max_sched_scan_ssids = 16; wl->hw->wiphy->max_match_sets = 16; /* * Maximum length of elements in scanning probe request templates * should be the maximum length possible for a template, without * the IEEE80211 header of the template */ wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; /* make sure all our channels fit in the scanned_ch bitmask */ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + ARRAY_SIZE(wl1271_channels_5ghz) > WL1271_MAX_CHANNELS); /* * We keep local copies of the band structs because we need to * modify them on a per-device basis. */ memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz, sizeof(wl1271_band_2ghz)); memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap, &wl->ht_cap[IEEE80211_BAND_2GHZ], sizeof(*wl->ht_cap)); memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz, sizeof(wl1271_band_5ghz)); memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap, &wl->ht_cap[IEEE80211_BAND_5GHZ], sizeof(*wl->ht_cap)); wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl->bands[IEEE80211_BAND_2GHZ]; wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl->bands[IEEE80211_BAND_5GHZ]; wl->hw->queues = 4; wl->hw->max_rates = 1; wl->hw->wiphy->reg_notifier = wl1271_reg_notify; /* the FW answers probe-requests in AP-mode */ wl->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; wl->hw->wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; /* allowed interface combinations */ wl->hw->wiphy->iface_combinations = wlcore_iface_combinations; wl->hw->wiphy->n_iface_combinations = ARRAY_SIZE(wlcore_iface_combinations); SET_IEEE80211_DEV(wl->hw, wl->dev); wl->hw->sta_data_size = sizeof(struct wl1271_station); wl->hw->vif_data_size = sizeof(struct wl12xx_vif); wl->hw->max_rx_aggregation_subframes = wl->conf.ht.rx_ba_win_size; return 0; } #define WL1271_DEFAULT_CHANNEL 0 struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size) { struct ieee80211_hw *hw; struct wl1271 *wl; int i, j, ret; unsigned int order; BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS); hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { wl1271_error("could not alloc ieee80211_hw"); ret = -ENOMEM; goto err_hw_alloc; } wl = hw->priv; memset(wl, 0, sizeof(*wl)); wl->priv = kzalloc(priv_size, GFP_KERNEL); if (!wl->priv) { wl1271_error("could not alloc wl priv"); ret = -ENOMEM; goto err_priv_alloc; } INIT_LIST_HEAD(&wl->wlvif_list); wl->hw = hw; for (i = 0; i < NUM_TX_QUEUES; i++) for (j = 0; j < WL12XX_MAX_LINKS; j++) skb_queue_head_init(&wl->links[j].tx_queue[i]); skb_queue_head_init(&wl->deferred_rx_queue); skb_queue_head_init(&wl->deferred_tx_queue); INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_WORK(&wl->netstack_work, wl1271_netstack_work); INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work); INIT_DELAYED_WORK(&wl->connection_loss_work, wl1271_connection_loss_work); wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); if (!wl->freezable_wq) { ret = -ENOMEM; goto err_hw; } wl->channel = WL1271_DEFAULT_CHANNEL; wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->band = IEEE80211_BAND_2GHZ; wl->channel_type = NL80211_CHAN_NO_HT; wl->flags = 0; wl->sg_enabled = true; wl->sleep_auth = WL1271_PSM_ILLEGAL; wl->hw_pg_ver = -1; wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; wl->platform_quirks = 0; wl->sched_scanning = false; wl->system_hlid = WL12XX_SYSTEM_HLID; wl->active_sta_count = 0; wl->fwlog_size = 0; init_waitqueue_head(&wl->fwlog_waitq); /* The system link is always allocated */ __set_bit(WL12XX_SYSTEM_HLID, wl->links_map); memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < wl->num_tx_desc; i++) wl->tx_frames[i] = NULL; spin_lock_init(&wl->wl_lock); wl->state = WL1271_STATE_OFF; wl->fw_type = WL12XX_FW_TYPE_NONE; mutex_init(&wl->mutex); mutex_init(&wl->flush_mutex); order = get_order(WL1271_AGGR_BUFFER_SIZE); wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); if (!wl->aggr_buf) { ret = -ENOMEM; goto err_wq; } wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); if (!wl->dummy_packet) { ret = -ENOMEM; goto err_aggr; } /* Allocate one page for the FW log */ wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL); if (!wl->fwlog) { ret = -ENOMEM; goto err_dummy_packet; } wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA); if (!wl->mbox) { ret = -ENOMEM; goto err_fwlog; } return hw; err_fwlog: free_page((unsigned long)wl->fwlog); err_dummy_packet: dev_kfree_skb(wl->dummy_packet); err_aggr: free_pages((unsigned long)wl->aggr_buf, order); err_wq: destroy_workqueue(wl->freezable_wq); err_hw: wl1271_debugfs_exit(wl); kfree(wl->priv); err_priv_alloc: ieee80211_free_hw(hw); err_hw_alloc: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(wlcore_alloc_hw); int wlcore_free_hw(struct wl1271 *wl) { /* Unblock any fwlog readers */ mutex_lock(&wl->mutex); wl->fwlog_size = -1; wake_up_interruptible_all(&wl->fwlog_waitq); mutex_unlock(&wl->mutex); device_remove_bin_file(wl->dev, &fwlog_attr); device_remove_file(wl->dev, &dev_attr_hw_pg_ver); device_remove_file(wl->dev, &dev_attr_bt_coex_state); free_page((unsigned long)wl->fwlog); dev_kfree_skb(wl->dummy_packet); free_pages((unsigned long)wl->aggr_buf, get_order(WL1271_AGGR_BUFFER_SIZE)); wl1271_debugfs_exit(wl); vfree(wl->fw); wl->fw = NULL; wl->fw_type = WL12XX_FW_TYPE_NONE; kfree(wl->nvs); wl->nvs = NULL; kfree(wl->fw_status_1); kfree(wl->tx_res_if); destroy_workqueue(wl->freezable_wq); kfree(wl->priv); ieee80211_free_hw(wl->hw); return 0; } EXPORT_SYMBOL_GPL(wlcore_free_hw); static irqreturn_t wl12xx_hardirq(int irq, void *cookie) { struct wl1271 *wl = cookie; unsigned long flags; wl1271_debug(DEBUG_IRQ, "IRQ"); /* complete the ELP completion */ spin_lock_irqsave(&wl->wl_lock, flags); set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); if (wl->elp_compl) { complete(wl->elp_compl); wl->elp_compl = NULL; } if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) { /* don't enqueue a work right now. mark it as pending */ set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags); wl1271_debug(DEBUG_IRQ, "should not enqueue work"); disable_irq_nosync(wl->irq); pm_wakeup_event(wl->dev, 0); spin_unlock_irqrestore(&wl->wl_lock, flags); return IRQ_HANDLED; } spin_unlock_irqrestore(&wl->wl_lock, flags); return IRQ_WAKE_THREAD; } int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) { struct wl12xx_platform_data *pdata = pdev->dev.platform_data; unsigned long irqflags; int ret; if (!wl->ops || !wl->ptable) { ret = -EINVAL; goto out_free_hw; } BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS); /* adjust some runtime configuration parameters */ wlcore_adjust_conf(wl); wl->irq = platform_get_irq(pdev, 0); wl->platform_quirks = pdata->platform_quirks; wl->set_power = pdata->set_power; wl->dev = &pdev->dev; wl->if_ops = pdata->ops; platform_set_drvdata(pdev, wl); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) irqflags = IRQF_TRIGGER_RISING; #else if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) irqflags = IRQF_TRIGGER_RISING; else irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) ret = compat_request_threaded_irq(&wl->irq_compat, wl->irq, wl12xx_hardirq, wlcore_irq, irqflags, pdev->name, wl); #else ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wlcore_irq, irqflags, pdev->name, wl); #endif if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); goto out_free_hw; } #ifdef CONFIG_PM ret = enable_irq_wake(wl->irq); if (!ret) { wl->irq_wake_enabled = true; device_init_wakeup(wl->dev, 1); if (pdata->pwr_in_suspend) { wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; wl->hw->wiphy->wowlan.n_patterns = WL1271_MAX_RX_FILTERS; wl->hw->wiphy->wowlan.pattern_min_len = 1; wl->hw->wiphy->wowlan.pattern_max_len = WL1271_RX_FILTER_MAX_PATTERN_SIZE; } } #endif disable_irq(wl->irq); ret = wl12xx_get_hw_info(wl); if (ret < 0) { wl1271_error("couldn't get hw info"); goto out_irq; } ret = wl->ops->identify_chip(wl); if (ret < 0) goto out_irq; ret = wl1271_init_ieee80211(wl); if (ret) goto out_irq; ret = wl1271_register_hw(wl); if (ret) goto out_irq; /* Create sysfs file to control bt coex state */ ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); if (ret < 0) { wl1271_error("failed to create sysfs file bt_coex_state"); goto out_unreg; } /* Create sysfs file to get HW PG version */ ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver); if (ret < 0) { wl1271_error("failed to create sysfs file hw_pg_ver"); goto out_bt_coex_state; } /* Create sysfs file for the FW log */ ret = device_create_bin_file(wl->dev, &fwlog_attr); if (ret < 0) { wl1271_error("failed to create sysfs file fwlog"); goto out_hw_pg_ver; } goto out; out_hw_pg_ver: device_remove_file(wl->dev, &dev_attr_hw_pg_ver); out_bt_coex_state: device_remove_file(wl->dev, &dev_attr_bt_coex_state); out_unreg: wl1271_unregister_hw(wl); out_irq: #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) compat_free_threaded_irq(&wl->irq_compat); #else free_irq(wl->irq, wl); #endif out_free_hw: wlcore_free_hw(wl); out: return ret; } EXPORT_SYMBOL_GPL(wlcore_probe); int __devexit wlcore_remove(struct platform_device *pdev) { struct wl1271 *wl = platform_get_drvdata(pdev); if (wl->irq_wake_enabled) { device_init_wakeup(wl->dev, 0); disable_irq_wake(wl->irq); } wl1271_unregister_hw(wl); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) compat_free_threaded_irq(&wl->irq_compat); compat_destroy_threaded_irq(&wl->irq_compat); #else free_irq(wl->irq, wl); #endif wlcore_free_hw(wl); return 0; } EXPORT_SYMBOL_GPL(wlcore_remove); u32 wl12xx_debug_level = DEBUG_NONE; EXPORT_SYMBOL_GPL(wl12xx_debug_level); module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); module_param_named(fwlog, fwlog_param, charp, 0); MODULE_PARM_DESC(fwlog, "FW logger options: continuous, ondemand, dbgpins or disable"); module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery"); module_param(no_recovery, bool, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck."); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/wlcore.h0000644000175000017500000003336012026211315024041 0ustar mcgrofmcgrof/* * This file is part of wlcore * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WLCORE_H__ #define __WLCORE_H__ #include #include "wlcore_i.h" #include "event.h" #include "boot.h" /* The maximum number of Tx descriptors in all chip families */ #define WLCORE_MAX_TX_DESCRIPTORS 32 /* forward declaration */ struct wl1271_tx_hw_descr; enum wl_rx_buf_align; struct wl1271_rx_descriptor; struct wlcore_ops { int (*identify_chip)(struct wl1271 *wl); int (*identify_fw)(struct wl1271 *wl); int (*boot)(struct wl1271 *wl); int (*plt_init)(struct wl1271 *wl); int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len); int (*ack_event)(struct wl1271 *wl); u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks); void (*set_tx_desc_blocks)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, u32 blks, u32 spare_blks); void (*set_tx_desc_data_len)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb); enum wl_rx_buf_align (*get_rx_buf_align)(struct wl1271 *wl, u32 rx_desc); int (*prepare_read)(struct wl1271 *wl, u32 rx_desc, u32 len); u32 (*get_rx_packet_len)(struct wl1271 *wl, void *rx_data, u32 data_len); int (*tx_delayed_compl)(struct wl1271 *wl); void (*tx_immediate_compl)(struct wl1271 *wl); int (*hw_init)(struct wl1271 *wl); int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif); u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*get_pg_ver)(struct wl1271 *wl, s8 *ver); int (*get_mac)(struct wl1271 *wl); void (*set_tx_desc_csum)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb); void (*set_rx_csum)(struct wl1271 *wl, struct wl1271_rx_descriptor *desc, struct sk_buff *skb); u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir); int (*handle_static_data)(struct wl1271 *wl, struct wl1271_static_data *static_data); int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem); int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); }; enum wlcore_partitions { PART_DOWN, PART_WORK, PART_BOOT, PART_DRPW, PART_TOP_PRCM_ELP_SOC, PART_PHY_INIT, PART_TABLE_LEN, }; struct wlcore_partition { u32 size; u32 start; }; struct wlcore_partition_set { struct wlcore_partition mem; struct wlcore_partition reg; struct wlcore_partition mem2; struct wlcore_partition mem3; }; enum wlcore_registers { /* register addresses, used with partition translation */ REG_ECPU_CONTROL, REG_INTERRUPT_NO_CLEAR, REG_INTERRUPT_ACK, REG_COMMAND_MAILBOX_PTR, REG_EVENT_MAILBOX_PTR, REG_INTERRUPT_TRIG, REG_INTERRUPT_MASK, REG_PC_ON_RECOVERY, REG_CHIP_ID_B, REG_CMD_MBOX_ADDRESS, /* data access memory addresses, used with partition translation */ REG_SLV_MEM_DATA, REG_SLV_REG_DATA, /* raw data access memory addresses */ REG_RAW_FW_STATUS_ADDR, REG_TABLE_LEN, }; struct wl1271_stats { void *fw_stats; unsigned long fw_stats_update; size_t fw_stats_len; unsigned int retry_count; unsigned int excessive_retries; }; struct wl1271 { struct ieee80211_hw *hw; bool mac80211_registered; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) struct compat_threaded_irq irq_compat; #endif struct device *dev; void *if_priv; struct wl1271_if_operations *if_ops; void (*set_power)(bool enable); int irq; spinlock_t wl_lock; enum wl1271_state state; enum wl12xx_fw_type fw_type; bool plt; enum plt_mode plt_mode; u8 fem_manuf; u8 last_vif_count; struct mutex mutex; unsigned long flags; struct wlcore_partition_set curr_part; struct wl1271_chip chip; int cmd_box_addr; u8 *fw; size_t fw_len; void *nvs; size_t nvs_len; s8 hw_pg_ver; /* address read from the fuse ROM */ u32 fuse_oui_addr; u32 fuse_nic_addr; /* we have up to 2 MAC addresses */ struct mac_address addresses[2]; int channel; u8 system_hlid; unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)]; unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)]; unsigned long rate_policies_map[ BITS_TO_LONGS(WL12XX_MAX_RATE_POLICIES)]; struct list_head wlvif_list; u8 sta_count; u8 ap_count; struct wl1271_acx_mem_map *target_mem_map; /* Accounting for allocated / available TX blocks on HW */ u32 tx_blocks_freed; u32 tx_blocks_available; u32 tx_allocated_blocks; u32 tx_results_count; /* Accounting for allocated / available Tx packets in HW */ u32 tx_pkts_freed[NUM_TX_QUEUES]; u32 tx_allocated_pkts[NUM_TX_QUEUES]; /* Transmitted TX packets counter for chipset interface */ u32 tx_packets_count; /* Time-offset between host and chipset clocks */ s64 time_offset; /* Frames scheduled for transmission, not handled yet */ int tx_queue_count[NUM_TX_QUEUES]; unsigned long queue_stop_reasons[NUM_TX_QUEUES]; /* Frames received, not handled yet by mac80211 */ struct sk_buff_head deferred_rx_queue; /* Frames sent, not returned yet to mac80211 */ struct sk_buff_head deferred_tx_queue; struct work_struct tx_work; struct workqueue_struct *freezable_wq; /* Pending TX frames */ unsigned long tx_frames_map[BITS_TO_LONGS(WLCORE_MAX_TX_DESCRIPTORS)]; struct sk_buff *tx_frames[WLCORE_MAX_TX_DESCRIPTORS]; int tx_frames_cnt; /* FW Rx counter */ u32 rx_counter; /* Intermediate buffer, used for packet aggregation */ u8 *aggr_buf; /* Reusable dummy packet template */ struct sk_buff *dummy_packet; /* Network stack work */ struct work_struct netstack_work; /* FW log buffer */ u8 *fwlog; /* Number of valid bytes in the FW log buffer */ ssize_t fwlog_size; /* Sysfs FW log entry readers wait queue */ wait_queue_head_t fwlog_waitq; /* Hardware recovery work */ struct work_struct recovery_work; bool watchdog_recovery; /* Pointer that holds DMA-friendly block for the mailbox */ struct event_mailbox *mbox; /* The mbox event mask */ u32 event_mask; /* Mailbox pointers */ u32 mbox_ptr[2]; /* Are we currently scanning */ struct ieee80211_vif *scan_vif; struct wl1271_scan scan; struct delayed_work scan_complete_work; /* Connection loss work */ struct delayed_work connection_loss_work; bool sched_scanning; /* The current band */ enum ieee80211_band band; struct completion *elp_compl; struct delayed_work elp_work; /* in dBm */ int power_level; struct wl1271_stats stats; __le32 buffer_32; u32 buffer_cmd; u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; struct wl_fw_status_1 *fw_status_1; struct wl_fw_status_2 *fw_status_2; struct wl1271_tx_hw_res_if *tx_res_if; /* Current chipset configuration */ struct wlcore_conf conf; bool sg_enabled; bool enable_11a; /* Most recently reported noise in dBm */ s8 noise; /* bands supported by this instance of wl12xx */ struct ieee80211_supported_band bands[WLCORE_NUM_BANDS]; /* * wowlan trigger was configured during suspend. * (currently, only "ANY" trigger is supported) */ bool wow_enabled; bool irq_wake_enabled; /* * AP-mode - links indexed by HLID. The global and broadcast links * are always active. */ struct wl1271_link links[WL12XX_MAX_LINKS]; /* AP-mode - a bitmap of links currently in PS mode according to FW */ u32 ap_fw_ps_map; /* AP-mode - a bitmap of links currently in PS mode in mac80211 */ unsigned long ap_ps_map; /* Quirks of specific hardware revisions */ unsigned int quirks; /* Platform limitations */ unsigned int platform_quirks; /* number of currently active RX BA sessions */ int ba_rx_session_count; /* AP-mode - number of currently connected stations */ int active_sta_count; /* last wlvif we transmitted from */ struct wl12xx_vif *last_wlvif; /* work to fire when Tx is stuck */ struct delayed_work tx_watchdog_work; struct wlcore_ops *ops; /* pointer to the lower driver partition table */ const struct wlcore_partition_set *ptable; /* pointer to the lower driver register table */ const int *rtable; /* name of the firmwares to load - for PLT, single role, multi-role */ const char *plt_fw_name; const char *sr_fw_name; const char *mr_fw_name; /* per-chip-family private structure */ void *priv; /* number of TX descriptors the HW supports. */ u32 num_tx_desc; /* number of RX descriptors the HW supports. */ u32 num_rx_desc; /* translate HW Tx rates to standard rate-indices */ const u8 **band_rate_to_idx; /* size of table for HW rates that can be received from chip */ u8 hw_tx_rate_tbl_size; /* this HW rate and below are considered HT rates for this chip */ u8 hw_min_ht_rate; /* HW HT (11n) capabilities */ struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS]; /* size of the private FW status data */ size_t fw_status_priv_len; /* RX Data filter rule state - enabled/disabled */ bool rx_filter_enabled[WL1271_MAX_RX_FILTERS]; /* size of the private static data */ size_t static_data_priv_len; /* the current channel type */ enum nl80211_channel_type channel_type; /* mutex for protecting the tx_flush function */ struct mutex flush_mutex; /* sleep auth value currently configured to FW */ int sleep_auth; /* the minimum FW version required for the driver to work */ unsigned int min_fw_ver[NUM_FW_VER]; }; int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); int __devexit wlcore_remove(struct platform_device *pdev); struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size); int wlcore_free_hw(struct wl1271 *wl); int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); static inline void wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, struct ieee80211_sta_ht_cap *ht_cap) { memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap)); } static inline void wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, unsigned int iftype, unsigned int major, unsigned int subtype, unsigned int minor) { wl->min_fw_ver[FW_VER_CHIP] = chip; wl->min_fw_ver[FW_VER_IF_TYPE] = iftype; wl->min_fw_ver[FW_VER_MAJOR] = major; wl->min_fw_ver[FW_VER_SUBTYPE] = subtype; wl->min_fw_ver[FW_VER_MINOR] = minor; } /* Firmware image load chunk size */ #define CHUNK_SIZE 16384 /* Quirks */ /* Each RX/TX transaction requires an end-of-transaction transfer */ #define WLCORE_QUIRK_END_OF_TRANSACTION BIT(0) /* wl127x and SPI don't support SDIO block size alignment */ #define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2) /* means aggregated Rx packets are aligned to a SDIO block */ #define WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN BIT(3) /* Older firmwares did not implement the FW logger over bus feature */ #define WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED BIT(4) /* Older firmwares use an old NVS format */ #define WLCORE_QUIRK_LEGACY_NVS BIT(5) /* Some firmwares may not support ELP */ #define WLCORE_QUIRK_NO_ELP BIT(6) /* pad only the last frame in the aggregate buffer */ #define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7) /* extra header space is required for TKIP */ #define WLCORE_QUIRK_TKIP_HEADER_SPACE BIT(8) /* Some firmwares not support sched scans while connected */ #define WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN BIT(9) /* separate probe response templates for one-shot and sched scans */ #define WLCORE_QUIRK_DUAL_PROBE_TMPL BIT(10) /* TODO: move to the lower drivers when all usages are abstracted */ #define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG20 (0x4030111) #define CHIP_ID_1283_PG10 (0x05030101) #define CHIP_ID_1283_PG20 (0x05030111) /* TODO: move all these common registers and values elsewhere */ #define HW_ACCESS_ELP_CTRL_REG 0x1FFFC /* ELP register commands */ #define ELPCTRL_WAKE_UP 0x1 #define ELPCTRL_WAKE_UP_WLAN_READY 0x5 #define ELPCTRL_SLEEP 0x0 /* ELP WLAN_READY bit */ #define ELPCTRL_WLAN_READY 0x2 /************************************************************************* Interrupt Trigger Register (Host -> WiLink) **************************************************************************/ /* Hardware to Embedded CPU Interrupts - first 32-bit register set */ /* * The host sets this bit to inform the Wlan * FW that a TX packet is in the XFER * Buffer #0. */ #define INTR_TRIG_TX_PROC0 BIT(2) /* * The host sets this bit to inform the FW * that it read a packet from RX XFER * Buffer #0. */ #define INTR_TRIG_RX_PROC0 BIT(3) #define INTR_TRIG_DEBUG_ACK BIT(4) #define INTR_TRIG_STATE_CHANGED BIT(5) /* Hardware to Embedded CPU Interrupts - second 32-bit register set */ /* * The host sets this bit to inform the FW * that it read a packet from RX XFER * Buffer #1. */ #define INTR_TRIG_RX_PROC1 BIT(17) /* * The host sets this bit to inform the Wlan * hardware that a TX packet is in the XFER * Buffer #1. */ #define INTR_TRIG_TX_PROC1 BIT(18) #define ACX_SLV_SOFT_RESET_BIT BIT(1) #define SOFT_RESET_MAX_TIME 1000000 #define SOFT_RESET_STALL_TIME 1000 #define ECPU_CONTROL_HALT 0x00000101 #define WELP_ARM_COMMAND_VAL 0x4 #endif /* __WLCORE_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/wlcore_i.h0000644000175000017500000003066012026211315024351 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WLCORE_I_H__ #define __WLCORE_I_H__ #include #include #include #include #include #include #include "conf.h" #include "ini.h" /* * wl127x and wl128x are using the same NVS file name. However, the * ini parameters between them are different. The driver validates * the correct NVS size in wl1271_boot_upload_nvs(). */ #define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin" #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) #define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff #define WL1271_CIPHER_SUITE_GEM 0x00147201 #define WL1271_BUSY_WORD_CNT 1 #define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32)) #define WL1271_ELP_HW_STATE_ASLEEP 0 #define WL1271_ELP_HW_STATE_IRQ 1 #define WL1271_DEFAULT_BEACON_INT 100 #define WL1271_DEFAULT_DTIM_PERIOD 1 #define WL12XX_MAX_ROLES 4 #define WL12XX_MAX_LINKS 12 #define WL12XX_INVALID_ROLE_ID 0xff #define WL12XX_INVALID_LINK_ID 0xff /* the driver supports the 2.4Ghz and 5Ghz bands */ #define WLCORE_NUM_BANDS 2 #define WL12XX_MAX_RATE_POLICIES 16 /* Defined by FW as 0. Will not be freed or allocated. */ #define WL12XX_SYSTEM_HLID 0 /* * When in AP-mode, we allow (at least) this number of packets * to be transmitted to FW for a STA in PS-mode. Only when packets are * present in the FW buffers it will wake the sleeping STA. We want to put * enough packets for the driver to transmit all of its buffered data before * the STA goes to sleep again. But we don't want to take too much memory * as it might hurt the throughput of active STAs. */ #define WL1271_PS_STA_MAX_PACKETS 2 #define WL1271_AP_BSS_INDEX 0 #define WL1271_AP_DEF_BEACON_EXP 20 #define WL1271_AGGR_BUFFER_SIZE (5 * PAGE_SIZE) enum wl1271_state { WL1271_STATE_OFF, WL1271_STATE_ON, }; enum wl12xx_fw_type { WL12XX_FW_TYPE_NONE, WL12XX_FW_TYPE_NORMAL, WL12XX_FW_TYPE_MULTI, WL12XX_FW_TYPE_PLT, }; struct wl1271; enum { FW_VER_CHIP, FW_VER_IF_TYPE, FW_VER_MAJOR, FW_VER_SUBTYPE, FW_VER_MINOR, NUM_FW_VER }; #define FW_VER_CHIP_WL127X 6 #define FW_VER_CHIP_WL128X 7 #define FW_VER_IF_TYPE_STA 1 #define FW_VER_IF_TYPE_AP 2 #define FW_VER_MINOR_1_SPARE_STA_MIN 58 #define FW_VER_MINOR_1_SPARE_AP_MIN 47 #define FW_VER_MINOR_FWLOG_STA_MIN 70 struct wl1271_chip { u32 id; char fw_ver_str[ETHTOOL_BUSINFO_LEN]; unsigned int fw_ver[NUM_FW_VER]; }; #define NUM_TX_QUEUES 4 #define AP_MAX_STATIONS 8 struct wl_fw_packet_counters { /* Cumulative counter of released packets per AC */ u8 tx_released_pkts[NUM_TX_QUEUES]; /* Cumulative counter of freed packets per HLID */ u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; /* Cumulative counter of released Voice memory blocks */ u8 tx_voice_released_blks; u8 padding[3]; } __packed; /* FW status registers */ struct wl_fw_status_1 { __le32 intr; u8 fw_rx_counter; u8 drv_rx_counter; u8 reserved; u8 tx_results_counter; __le32 rx_pkt_descs[0]; } __packed; /* * Each HW arch has a different number of Rx descriptors. * The length of the status depends on it, since it holds an array * of descriptors. */ #define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \ (sizeof(struct wl_fw_status_1) + \ (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \ num_rx_desc) struct wl_fw_status_2 { __le32 fw_localtime; /* * A bitmap (where each bit represents a single HLID) * to indicate if the station is in PS mode. */ __le32 link_ps_bitmap; /* * A bitmap (where each bit represents a single HLID) to indicate * if the station is in Fast mode */ __le32 link_fast_bitmap; /* Cumulative counter of total released mem blocks since FW-reset */ __le32 total_released_blks; /* Size (in Memory Blocks) of TX pool */ __le32 tx_total; struct wl_fw_packet_counters counters; __le32 log_start_addr; /* Private status to be used by the lower drivers */ u8 priv[0]; } __packed; #define WL1271_MAX_CHANNELS 64 struct wl1271_scan { struct cfg80211_scan_request *req; unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)]; bool failed; u8 state; u8 ssid[IEEE80211_MAX_SSID_LEN+1]; size_t ssid_len; }; struct wl1271_if_operations { int __must_check (*read)(struct device *child, int addr, void *buf, size_t len, bool fixed); int __must_check (*write)(struct device *child, int addr, void *buf, size_t len, bool fixed); void (*reset)(struct device *child); void (*init)(struct device *child); int (*power)(struct device *child, bool enable); void (*set_block_size) (struct device *child, unsigned int blksz); }; #define MAX_NUM_KEYS 14 #define MAX_KEY_SIZE 32 struct wl1271_ap_key { u8 id; u8 key_type; u8 key_size; u8 key[MAX_KEY_SIZE]; u8 hlid; u32 tx_seq_32; u16 tx_seq_16; }; enum wl12xx_flags { WL1271_FLAG_GPIO_POWER, WL1271_FLAG_TX_QUEUE_STOPPED, WL1271_FLAG_TX_PENDING, WL1271_FLAG_IN_ELP, WL1271_FLAG_ELP_REQUESTED, WL1271_FLAG_IRQ_RUNNING, WL1271_FLAG_FW_TX_BUSY, WL1271_FLAG_DUMMY_PACKET_PENDING, WL1271_FLAG_SUSPENDED, WL1271_FLAG_PENDING_WORK, WL1271_FLAG_SOFT_GEMINI, WL1271_FLAG_RECOVERY_IN_PROGRESS, WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, WL1271_FLAG_INTENDED_FW_RECOVERY, WL1271_FLAG_IO_FAILED, }; enum wl12xx_vif_flags { WLVIF_FLAG_INITIALIZED, WLVIF_FLAG_STA_ASSOCIATED, WLVIF_FLAG_STA_AUTHORIZED, WLVIF_FLAG_IBSS_JOINED, WLVIF_FLAG_AP_STARTED, WLVIF_FLAG_IN_PS, WLVIF_FLAG_STA_STATE_SENT, WLVIF_FLAG_RX_STREAMING_STARTED, WLVIF_FLAG_PSPOLL_FAILURE, WLVIF_FLAG_CS_PROGRESS, WLVIF_FLAG_AP_PROBE_RESP_SET, WLVIF_FLAG_IN_USE, }; struct wl1271_link { /* AP-mode - TX queue per AC in link */ struct sk_buff_head tx_queue[NUM_TX_QUEUES]; /* accounting for allocated / freed packets in FW */ u8 allocated_pkts; u8 prev_freed_pkts; u8 addr[ETH_ALEN]; /* bitmap of TIDs where RX BA sessions are active for this link */ u8 ba_bitmap; }; #define WL1271_MAX_RX_FILTERS 5 #define WL1271_RX_FILTER_MAX_FIELDS 8 #define WL1271_RX_FILTER_ETH_HEADER_SIZE 14 #define WL1271_RX_FILTER_MAX_FIELDS_SIZE 95 #define RX_FILTER_FIELD_OVERHEAD \ (sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *)) #define WL1271_RX_FILTER_MAX_PATTERN_SIZE \ (WL1271_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD) #define WL1271_RX_FILTER_FLAG_MASK BIT(0) #define WL1271_RX_FILTER_FLAG_IP_HEADER 0 #define WL1271_RX_FILTER_FLAG_ETHERNET_HEADER BIT(1) enum rx_filter_action { FILTER_DROP = 0, FILTER_SIGNAL = 1, FILTER_FW_HANDLE = 2 }; enum plt_mode { PLT_OFF = 0, PLT_ON = 1, PLT_FEM_DETECT = 2, }; struct wl12xx_rx_filter_field { __le16 offset; u8 len; u8 flags; u8 *pattern; } __packed; struct wl12xx_rx_filter { u8 action; int num_fields; struct wl12xx_rx_filter_field fields[WL1271_RX_FILTER_MAX_FIELDS]; }; struct wl1271_station { u8 hlid; }; struct wl12xx_vif { struct wl1271 *wl; struct list_head list; unsigned long flags; u8 bss_type; u8 p2p; /* we are using p2p role */ u8 role_id; /* sta/ibss specific */ u8 dev_role_id; u8 dev_hlid; union { struct { u8 hlid; u8 ba_rx_bitmap; u8 basic_rate_idx; u8 ap_rate_idx; u8 p2p_rate_idx; bool qos; } sta; struct { u8 global_hlid; u8 bcast_hlid; /* HLIDs bitmap of associated stations */ unsigned long sta_hlid_map[BITS_TO_LONGS( WL12XX_MAX_LINKS)]; /* recoreded keys - set here before AP startup */ struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS]; u8 mgmt_rate_idx; u8 bcast_rate_idx; u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT]; } ap; }; /* the hlid of the last transmitted skb */ int last_tx_hlid; unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 ssid_len; /* The current band */ enum ieee80211_band band; int channel; enum nl80211_channel_type channel_type; u32 bitrate_masks[WLCORE_NUM_BANDS]; u32 basic_rate_set; /* * currently configured rate set: * bits 0-15 - 802.11abg rates * bits 16-23 - 802.11n MCS index mask * support only 1 stream, thus only 8 bits for the MCS rates (0-7). */ u32 basic_rate; u32 rate_set; /* probe-req template for the current AP */ struct sk_buff *probereq; /* Beaconing interval (needed for ad-hoc) */ u32 beacon_int; /* Default key (for WEP) */ u32 default_key; /* Our association ID */ u16 aid; /* Session counter for the chipset */ int session_counter; /* retry counter for PSM entries */ u8 psm_entry_retry; /* in dBm */ int power_level; int rssi_thold; int last_rssi_event; /* save the current encryption type for auto-arp config */ u8 encryption_type; __be32 ip_addr; /* RX BA constraint value */ bool ba_support; bool ba_allowed; /* Rx Streaming */ struct work_struct rx_streaming_enable_work; struct work_struct rx_streaming_disable_work; struct timer_list rx_streaming_timer; /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) * should be declared in this struct. */ struct { u8 persistent[0]; /* * Security sequence number * bits 0-15: lower 16 bits part of sequence number * bits 16-47: higher 32 bits part of sequence number * bits 48-63: not in use */ u64 tx_security_seq; /* 8 bits of the last sequence number in use */ u8 tx_security_last_seq_lsb; }; }; static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif) { return (struct wl12xx_vif *)vif->drv_priv; } static inline struct ieee80211_vif *wl12xx_wlvif_to_vif(struct wl12xx_vif *wlvif) { return container_of((void *)wlvif, struct ieee80211_vif, drv_priv); } #define wl12xx_for_each_wlvif(wl, wlvif) \ list_for_each_entry(wlvif, &wl->wlvif_list, list) #define wl12xx_for_each_wlvif_continue(wl, wlvif) \ list_for_each_entry_continue(wlvif, &wl->wlvif_list, list) #define wl12xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type) \ wl12xx_for_each_wlvif(wl, wlvif) \ if (wlvif->bss_type == _bss_type) #define wl12xx_for_each_wlvif_sta(wl, wlvif) \ wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS) #define wl12xx_for_each_wlvif_ap(wl, wlvif) \ wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS) int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode); int wl1271_plt_stop(struct wl1271 *wl); int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_queue_recovery_work(struct wl1271 *wl); size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, u16 offset, u8 flags, u8 *pattern, u8 len); void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter); struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void); int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter); void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, u8 *buf); #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ #define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */ #define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */ #define WL1271_DEFAULT_POWER_LEVEL 0 #define WL1271_TX_QUEUE_LOW_WATERMARK 32 #define WL1271_TX_QUEUE_HIGH_WATERMARK 256 #define WL1271_DEFERRED_QUEUE_LIMIT 64 /* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power on in case is has been shut down shortly before */ #define WL1271_PRE_POWER_ON_SLEEP 20 /* in milliseconds */ #define WL1271_POWER_ON_SLEEP 200 /* in milliseconds */ /* Macros to handle wl1271.sta_rate_set */ #define HW_BG_RATES_MASK 0xffff #define HW_HT_RATES_OFFSET 16 #define HW_MIMO_RATES_OFFSET 24 #define WL12XX_HW_BLOCK_SIZE 256 #endif /* __WLCORE_I_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/wl12xx_platform_data.c0000644000175000017500000000244212026211315026600 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2010-2011 Texas Instruments, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include static struct wl12xx_platform_data *platform_data; int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data) { if (platform_data) return -EBUSY; if (!data) return -EINVAL; platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); if (!platform_data) return -ENOMEM; return 0; } struct wl12xx_platform_data *wl12xx_get_platform_data(void) { if (!platform_data) return ERR_PTR(-ENODEV); return platform_data; } EXPORT_SYMBOL(wl12xx_get_platform_data); compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/wl12xx_80211.h0000644000175000017500000000700612026211315024444 0ustar mcgrofmcgrof#ifndef __WL12XX_80211_H__ #define __WL12XX_80211_H__ #include /* ETH_ALEN */ #include /* RATES */ #define IEEE80211_CCK_RATE_1MB 0x02 #define IEEE80211_CCK_RATE_2MB 0x04 #define IEEE80211_CCK_RATE_5MB 0x0B #define IEEE80211_CCK_RATE_11MB 0x16 #define IEEE80211_OFDM_RATE_6MB 0x0C #define IEEE80211_OFDM_RATE_9MB 0x12 #define IEEE80211_OFDM_RATE_12MB 0x18 #define IEEE80211_OFDM_RATE_18MB 0x24 #define IEEE80211_OFDM_RATE_24MB 0x30 #define IEEE80211_OFDM_RATE_36MB 0x48 #define IEEE80211_OFDM_RATE_48MB 0x60 #define IEEE80211_OFDM_RATE_54MB 0x6C #define IEEE80211_BASIC_RATE_MASK 0x80 #define IEEE80211_CCK_RATE_1MB_MASK (1<<0) #define IEEE80211_CCK_RATE_2MB_MASK (1<<1) #define IEEE80211_CCK_RATE_5MB_MASK (1<<2) #define IEEE80211_CCK_RATE_11MB_MASK (1<<3) #define IEEE80211_OFDM_RATE_6MB_MASK (1<<4) #define IEEE80211_OFDM_RATE_9MB_MASK (1<<5) #define IEEE80211_OFDM_RATE_12MB_MASK (1<<6) #define IEEE80211_OFDM_RATE_18MB_MASK (1<<7) #define IEEE80211_OFDM_RATE_24MB_MASK (1<<8) #define IEEE80211_OFDM_RATE_36MB_MASK (1<<9) #define IEEE80211_OFDM_RATE_48MB_MASK (1<<10) #define IEEE80211_OFDM_RATE_54MB_MASK (1<<11) #define IEEE80211_CCK_RATES_MASK 0x0000000F #define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ IEEE80211_CCK_RATE_2MB_MASK) #define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ IEEE80211_CCK_RATE_5MB_MASK | \ IEEE80211_CCK_RATE_11MB_MASK) #define IEEE80211_OFDM_RATES_MASK 0x00000FF0 #define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ IEEE80211_OFDM_RATE_12MB_MASK | \ IEEE80211_OFDM_RATE_24MB_MASK) #define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ IEEE80211_OFDM_RATE_9MB_MASK | \ IEEE80211_OFDM_RATE_18MB_MASK | \ IEEE80211_OFDM_RATE_36MB_MASK | \ IEEE80211_OFDM_RATE_48MB_MASK | \ IEEE80211_OFDM_RATE_54MB_MASK) #define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ IEEE80211_CCK_DEFAULT_RATES_MASK) /* This really should be 8, but not for our firmware */ #define MAX_SUPPORTED_RATES 32 #define MAX_COUNTRY_TRIPLETS 32 /* Headers */ struct ieee80211_header { __le16 frame_ctl; __le16 duration_id; u8 da[ETH_ALEN]; u8 sa[ETH_ALEN]; u8 bssid[ETH_ALEN]; __le16 seq_ctl; u8 payload[0]; } __packed; struct wl12xx_ie_header { u8 id; u8 len; } __packed; /* IEs */ struct wl12xx_ie_ssid { struct wl12xx_ie_header header; char ssid[IEEE80211_MAX_SSID_LEN]; } __packed; struct wl12xx_ie_rates { struct wl12xx_ie_header header; u8 rates[MAX_SUPPORTED_RATES]; } __packed; struct wl12xx_ie_ds_params { struct wl12xx_ie_header header; u8 channel; } __packed; struct country_triplet { u8 channel; u8 num_channels; u8 max_tx_power; } __packed; struct wl12xx_ie_country { struct wl12xx_ie_header header; u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; struct country_triplet triplets[MAX_COUNTRY_TRIPLETS]; } __packed; /* Templates */ struct wl12xx_null_data_template { struct ieee80211_header header; } __packed; struct wl12xx_ps_poll_template { __le16 fc; __le16 aid; u8 bssid[ETH_ALEN]; u8 ta[ETH_ALEN]; } __packed; struct wl12xx_arp_rsp_template { /* not including ieee80211 header */ u8 llc_hdr[sizeof(rfc1042_header)]; __be16 llc_type; struct arphdr arp_hdr; u8 sender_hw[ETH_ALEN]; __be32 sender_ip; u8 target_hw[ETH_ALEN]; __be32 target_ip; } __packed; struct wl12xx_disconn_template { struct ieee80211_header header; __le16 disconn_reason; } __packed; #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/tx.h0000644000175000017500000002010112026211315023166 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __TX_H__ #define __TX_H__ #define TX_HW_MGMT_PKT_LIFETIME_TU 2000 #define TX_HW_AP_MODE_PKT_LIFETIME_TU 8000 #define TX_HW_ATTR_SAVE_RETRIES BIT(0) #define TX_HW_ATTR_HEADER_PAD BIT(1) #define TX_HW_ATTR_SESSION_COUNTER (BIT(2) | BIT(3) | BIT(4)) #define TX_HW_ATTR_RATE_POLICY (BIT(5) | BIT(6) | BIT(7) | \ BIT(8) | BIT(9)) #define TX_HW_ATTR_LAST_WORD_PAD (BIT(10) | BIT(11)) #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) #define TX_HW_ATTR_HOST_ENCRYPT BIT(14) #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_HEADER_PAD 1 #define TX_HW_ATTR_OFST_SESSION_COUNTER 2 #define TX_HW_ATTR_OFST_RATE_POLICY 5 #define TX_HW_ATTR_OFST_LAST_WORD_PAD 10 #define TX_HW_ATTR_OFST_TX_CMPLT_REQ 12 #define TX_HW_RESULT_QUEUE_LEN 16 #define TX_HW_RESULT_QUEUE_LEN_MASK 0xf #define WL1271_TX_ALIGN_TO 4 #define WL1271_EXTRA_SPACE_TKIP 4 #define WL1271_EXTRA_SPACE_AES 8 #define WL1271_EXTRA_SPACE_MAX 8 /* Used for management frames and dummy packets */ #define WL1271_TID_MGMT 7 struct wl127x_tx_mem { /* * Number of extra memory blocks to allocate for this packet * in addition to the number of blocks derived from the packet * length. */ u8 extra_blocks; /* * Total number of memory blocks allocated by the host for * this packet. Must be equal or greater than the actual * blocks number allocated by HW. */ u8 total_mem_blocks; } __packed; struct wl128x_tx_mem { /* * Total number of memory blocks allocated by the host for * this packet. */ u8 total_mem_blocks; /* * Number of extra bytes, at the end of the frame. the host * uses this padding to complete each frame to integer number * of SDIO blocks. */ u8 extra_bytes; } __packed; struct wl18xx_tx_mem { /* * Total number of memory blocks allocated by the host for * this packet. */ u8 total_mem_blocks; /* * control bits */ u8 ctrl; } __packed; /* * On wl128x based devices, when TX packets are aggregated, each packet * size must be aligned to the SDIO block size. The maximum block size * is bounded by the type of the padded bytes field that is sent to the * FW. Currently the type is u8, so the maximum block size is 256 bytes. */ #define WL12XX_BUS_BLOCK_SIZE min(512u, \ (1u << (8 * sizeof(((struct wl128x_tx_mem *) 0)->extra_bytes)))) struct wl1271_tx_hw_descr { /* Length of packet in words, including descriptor+header+data */ __le16 length; union { struct wl127x_tx_mem wl127x_mem; struct wl128x_tx_mem wl128x_mem; struct wl18xx_tx_mem wl18xx_mem; } __packed; /* Device time (in us) when the packet arrived to the driver */ __le32 start_time; /* * Max delay in TUs until transmission. The last device time the * packet can be transmitted is: start_time + (1024 * life_time) */ __le16 life_time; /* Bitwise fields - see TX_ATTR... definitions above. */ __le16 tx_attr; /* Packet identifier used also in the Tx-Result. */ u8 id; /* The packet TID value (as User-Priority) */ u8 tid; /* host link ID (HLID) */ u8 hlid; union { u8 wl12xx_reserved; /* * bit 0 -> 0 = udp, 1 = tcp * bit 1:7 -> IP header offset */ u8 wl18xx_checksum_data; } __packed; } __packed; enum wl1271_tx_hw_res_status { TX_SUCCESS = 0, TX_HW_ERROR = 1, TX_DISABLED = 2, TX_RETRY_EXCEEDED = 3, TX_TIMEOUT = 4, TX_KEY_NOT_FOUND = 5, TX_PEER_NOT_FOUND = 6, TX_SESSION_MISMATCH = 7, TX_LINK_NOT_VALID = 8, }; struct wl1271_tx_hw_res_descr { /* Packet Identifier - same value used in the Tx descriptor.*/ u8 id; /* The status of the transmission, indicating success or one of several possible reasons for failure. */ u8 status; /* Total air access duration including all retrys and overheads.*/ __le16 medium_usage; /* The time passed from host xfer to Tx-complete.*/ __le32 fw_handling_time; /* Total media delay (from 1st EDCA AIFS counter until TX Complete). */ __le32 medium_delay; /* LS-byte of last TKIP seq-num (saved per AC for recovery). */ u8 tx_security_sequence_number_lsb; /* Retry count - number of transmissions without successful ACK.*/ u8 ack_failures; /* The rate that succeeded getting ACK (Valid only if status=SUCCESS). */ u8 rate_class_index; /* for 4-byte alignment. */ u8 spare; } __packed; struct wl1271_tx_hw_res_if { __le32 tx_result_fw_counter; __le32 tx_result_host_counter; struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; } __packed; enum wlcore_queue_stop_reason { WLCORE_QUEUE_STOP_REASON_WATERMARK, WLCORE_QUEUE_STOP_REASON_FW_RESTART, WLCORE_QUEUE_STOP_REASON_FLUSH, WLCORE_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */ }; static inline int wl1271_tx_get_queue(int queue) { switch (queue) { case 0: return CONF_TX_AC_VO; case 1: return CONF_TX_AC_VI; case 2: return CONF_TX_AC_BE; case 3: return CONF_TX_AC_BK; default: return CONF_TX_AC_BE; } } static inline int wl1271_tx_get_mac80211_queue(int queue) { switch (queue) { case CONF_TX_AC_VO: return 0; case CONF_TX_AC_VI: return 1; case CONF_TX_AC_BE: return 2; case CONF_TX_AC_BK: return 3; default: return 2; } } static inline int wl1271_tx_total_queue_count(struct wl1271 *wl) { int i, count = 0; for (i = 0; i < NUM_TX_QUEUES; i++) count += wl->tx_queue_count[i]; return count; } void wl1271_tx_work(struct work_struct *work); int wlcore_tx_work_locked(struct wl1271 *wl); int wlcore_tx_complete(struct wl1271 *wl); void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_tx_reset(struct wl1271 *wl); void wl1271_tx_flush(struct wl1271 *wl); u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, enum ieee80211_band rate_band); u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set); u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, struct ieee80211_sta *sta); void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid); void wl1271_handle_tx_low_watermark(struct wl1271 *wl); bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb); void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, unsigned int packet_length); void wl1271_free_tx_id(struct wl1271 *wl, int id); void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason); void wlcore_stop_queue(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason); void wlcore_wake_queue(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason); void wlcore_stop_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason); void wlcore_wake_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason); void wlcore_reset_stopped_queues(struct wl1271 *wl); bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason); bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue); /* from main.c */ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/tx.c0000644000175000017500000007623012026211315023177 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include "wlcore.h" #include "debug.h" #include "io.h" #include "ps.h" #include "tx.h" #include "event.h" #include "hw_ops.h" /* * TODO: this is here just for now, it must be removed when the data * operations are in place. */ #include "../wl12xx/reg.h" static int wl1271_set_default_wep_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 id) { int ret; bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); if (is_ap) ret = wl12xx_cmd_set_default_wep_key(wl, id, wlvif->ap.bcast_hlid); else ret = wl12xx_cmd_set_default_wep_key(wl, id, wlvif->sta.hlid); if (ret < 0) return ret; wl1271_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id); return 0; } static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb) { int id; id = find_first_zero_bit(wl->tx_frames_map, wl->num_tx_desc); if (id >= wl->num_tx_desc) return -EBUSY; __set_bit(id, wl->tx_frames_map); wl->tx_frames[id] = skb; wl->tx_frames_cnt++; return id; } void wl1271_free_tx_id(struct wl1271 *wl, int id) { if (__test_and_clear_bit(id, wl->tx_frames_map)) { if (unlikely(wl->tx_frames_cnt == wl->num_tx_desc)) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); wl->tx_frames[id] = NULL; wl->tx_frames_cnt--; } } EXPORT_SYMBOL(wl1271_free_tx_id); static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, struct sk_buff *skb) { struct ieee80211_hdr *hdr; /* * add the station to the known list before transmitting the * authentication response. this way it won't get de-authed by FW * when transmitting too soon. */ hdr = (struct ieee80211_hdr *)(skb->data + sizeof(struct wl1271_tx_hw_descr)); if (ieee80211_is_auth(hdr->frame_control)) wl1271_acx_set_inconnection_sta(wl, hdr->addr1); } static void wl1271_tx_regulate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) { bool fw_ps, single_sta; u8 tx_pkts; if (WARN_ON(!test_bit(hlid, wlvif->links_map))) return; fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); tx_pkts = wl->links[hlid].allocated_pkts; single_sta = (wl->active_sta_count == 1); /* * if in FW PS and there is enough data in FW we can put the link * into high-level PS and clean out its TX queues. * Make an exception if this is the only connected station. In this * case FW-memory congestion is not a problem. */ if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) { return wl->dummy_packet == skb; } EXPORT_SYMBOL(wl12xx_is_dummy_packet); static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, struct ieee80211_sta *sta) { if (sta) { struct wl1271_station *wl_sta; wl_sta = (struct wl1271_station *)sta->drv_priv; return wl_sta->hlid; } else { struct ieee80211_hdr *hdr; if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) return wl->system_hlid; hdr = (struct ieee80211_hdr *)skb->data; if (is_multicast_ether_addr(ieee80211_get_DA(hdr))) return wlvif->ap.bcast_hlid; else return wlvif->ap.global_hlid; } } u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, struct ieee80211_sta *sta) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (!wlvif || wl12xx_is_dummy_packet(wl, skb)) return wl->system_hlid; if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta); if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) || test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) && !ieee80211_is_auth(hdr->frame_control) && !ieee80211_is_assoc_req(hdr->frame_control)) return wlvif->sta.hlid; else return wlvif->dev_hlid; } unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, unsigned int packet_length) { if ((wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) || !(wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN)) return ALIGN(packet_length, WL1271_TX_ALIGN_TO); else return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE); } EXPORT_SYMBOL(wlcore_calc_packet_alignment); static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, u32 extra, u32 buf_offset, u8 hlid, bool is_gem) { struct wl1271_tx_hw_descr *desc; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; u32 total_blocks; int id, ret = -EBUSY, ac; u32 spare_blocks; if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE) return -EAGAIN; spare_blocks = wlcore_hw_get_spare_blocks(wl, is_gem); /* allocate free identifier for the packet */ id = wl1271_alloc_tx_id(wl, skb); if (id < 0) return id; total_blocks = wlcore_hw_calc_tx_blocks(wl, total_len, spare_blocks); if (total_blocks <= wl->tx_blocks_available) { desc = (struct wl1271_tx_hw_descr *)skb_push( skb, total_len - skb->len); wlcore_hw_set_tx_desc_blocks(wl, desc, total_blocks, spare_blocks); desc->id = id; wl->tx_blocks_available -= total_blocks; wl->tx_allocated_blocks += total_blocks; /* If the FW was empty before, arm the Tx watchdog */ if (wl->tx_allocated_blocks == total_blocks) wl12xx_rearm_tx_watchdog_locked(wl); ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); wl->tx_allocated_pkts[ac]++; if (!wl12xx_is_dummy_packet(wl, skb) && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS && test_bit(hlid, wlvif->ap.sta_hlid_map)) wl->links[hlid].allocated_pkts++; ret = 0; wl1271_debug(DEBUG_TX, "tx_allocate: size: %d, blocks: %d, id: %d", total_len, total_blocks, id); } else { wl1271_free_tx_id(wl, id); } return ret; } static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, u32 extra, struct ieee80211_tx_info *control, u8 hlid) { struct timespec ts; struct wl1271_tx_hw_descr *desc; int ac, rate_idx; s64 hosttime; u16 tx_attr = 0; __le16 frame_control; struct ieee80211_hdr *hdr; u8 *frame_start; bool is_dummy; desc = (struct wl1271_tx_hw_descr *) skb->data; frame_start = (u8 *)(desc + 1); hdr = (struct ieee80211_hdr *)(frame_start + extra); frame_control = hdr->frame_control; /* relocate space for security header */ if (extra) { int hdrlen = ieee80211_hdrlen(frame_control); memmove(frame_start, hdr, hdrlen); skb_set_network_header(skb, skb_network_offset(skb) + extra); } /* configure packet life time */ getnstimeofday(&ts); hosttime = (timespec_to_ns(&ts) >> 10); desc->start_time = cpu_to_le32(hosttime - wl->time_offset); is_dummy = wl12xx_is_dummy_packet(wl, skb); if (is_dummy || !wlvif || wlvif->bss_type != BSS_TYPE_AP_BSS) desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU); else desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU); /* queue */ ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); desc->tid = skb->priority; if (is_dummy) { /* * FW expects the dummy packet to have an invalid session id - * any session id that is different than the one set in the join */ tx_attr = (SESSION_COUNTER_INVALID << TX_HW_ATTR_OFST_SESSION_COUNTER) & TX_HW_ATTR_SESSION_COUNTER; tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ; } else if (wlvif) { /* configure the tx attributes */ tx_attr = wlvif->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; } desc->hlid = hlid; if (is_dummy || !wlvif) rate_idx = 0; else if (wlvif->bss_type != BSS_TYPE_AP_BSS) { /* * if the packets are data packets * send them with AP rate policies (EAPOLs are an exception), * otherwise use default basic rates */ if (skb->protocol == cpu_to_be16(ETH_P_PAE)) rate_idx = wlvif->sta.basic_rate_idx; else if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE) rate_idx = wlvif->sta.p2p_rate_idx; else if (ieee80211_is_data(frame_control)) rate_idx = wlvif->sta.ap_rate_idx; else rate_idx = wlvif->sta.basic_rate_idx; } else { if (hlid == wlvif->ap.global_hlid) rate_idx = wlvif->ap.mgmt_rate_idx; else if (hlid == wlvif->ap.bcast_hlid || skb->protocol == cpu_to_be16(ETH_P_PAE)) /* send AP bcast and EAPOLs using the min basic rate */ rate_idx = wlvif->ap.bcast_rate_idx; else rate_idx = wlvif->ap.ucast_rate_idx[ac]; } tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; /* for WEP shared auth - no fw encryption is needed */ if (ieee80211_is_auth(frame_control) && ieee80211_has_protected(frame_control)) tx_attr |= TX_HW_ATTR_HOST_ENCRYPT; desc->tx_attr = cpu_to_le16(tx_attr); wlcore_hw_set_tx_desc_csum(wl, desc, skb); wlcore_hw_set_tx_desc_data_len(wl, desc, skb); } /* caller must hold wl->mutex */ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, u32 buf_offset, u8 hlid) { struct ieee80211_tx_info *info; u32 extra = 0; int ret = 0; u32 total_len; bool is_dummy; bool is_gem = false; if (!skb) { wl1271_error("discarding null skb"); return -EINVAL; } if (hlid == WL12XX_INVALID_LINK_ID) { wl1271_error("invalid hlid. dropping skb 0x%p", skb); return -EINVAL; } info = IEEE80211_SKB_CB(skb); is_dummy = wl12xx_is_dummy_packet(wl, skb); if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) extra = WL1271_EXTRA_SPACE_TKIP; if (info->control.hw_key) { bool is_wep; u8 idx = info->control.hw_key->hw_key_idx; u32 cipher = info->control.hw_key->cipher; is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104); if (unlikely(is_wep && wlvif->default_key != idx)) { ret = wl1271_set_default_wep_key(wl, wlvif, idx); if (ret < 0) return ret; wlvif->default_key = idx; } is_gem = (cipher == WL1271_CIPHER_SUITE_GEM); } ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid, is_gem); if (ret < 0) return ret; wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid); if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) { wl1271_tx_ap_update_inconnection_sta(wl, skb); wl1271_tx_regulate_link(wl, wlvif, hlid); } /* * The length of each packet is stored in terms of * words. Thus, we must pad the skb data to make sure its * length is aligned. The number of padding bytes is computed * and set in wl1271_tx_fill_hdr. * In special cases, we want to align to a specific block size * (eg. for wl128x with SDIO we align to 256). */ total_len = wlcore_calc_packet_alignment(wl, skb->len); memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); /* Revert side effects in the dummy packet skb, so it can be reused */ if (is_dummy) skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); return total_len; } u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, enum ieee80211_band rate_band) { struct ieee80211_supported_band *band; u32 enabled_rates = 0; int bit; band = wl->hw->wiphy->bands[rate_band]; for (bit = 0; bit < band->n_bitrates; bit++) { if (rate_set & 0x1) enabled_rates |= band->bitrates[bit].hw_value; rate_set >>= 1; } /* MCS rates indication are on bits 16 - 31 */ rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates; for (bit = 0; bit < 16; bit++) { if (rate_set & 0x1) enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit); rate_set >>= 1; } return enabled_rates; } void wl1271_handle_tx_low_watermark(struct wl1271 *wl) { int i; for (i = 0; i < NUM_TX_QUEUES; i++) { if (wlcore_is_queue_stopped_by_reason(wl, i, WLCORE_QUEUE_STOP_REASON_WATERMARK) && wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) { /* firmware buffer has space, restart queues */ wlcore_wake_queue(wl, i, WLCORE_QUEUE_STOP_REASON_WATERMARK); } } } static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl, struct sk_buff_head *queues) { int i, q = -1, ac; u32 min_pkts = 0xffffffff; /* * Find a non-empty ac where: * 1. There are packets to transmit * 2. The FW has the least allocated blocks * * We prioritize the ACs according to VO>VI>BE>BK */ for (i = 0; i < NUM_TX_QUEUES; i++) { ac = wl1271_tx_get_queue(i); if (!skb_queue_empty(&queues[ac]) && (wl->tx_allocated_pkts[ac] < min_pkts)) { q = ac; min_pkts = wl->tx_allocated_pkts[q]; } } if (q == -1) return NULL; return &queues[q]; } static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl, struct wl1271_link *lnk) { struct sk_buff *skb; unsigned long flags; struct sk_buff_head *queue; queue = wl1271_select_queue(wl, lnk->tx_queue); if (!queue) return NULL; skb = skb_dequeue(queue); if (skb) { int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); spin_lock_irqsave(&wl->wl_lock, flags); WARN_ON_ONCE(wl->tx_queue_count[q] <= 0); wl->tx_queue_count[q]--; spin_unlock_irqrestore(&wl->wl_lock, flags); } return skb; } static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { struct sk_buff *skb = NULL; int i, h, start_hlid; /* start from the link after the last one */ start_hlid = (wlvif->last_tx_hlid + 1) % WL12XX_MAX_LINKS; /* dequeue according to AC, round robin on each link */ for (i = 0; i < WL12XX_MAX_LINKS; i++) { h = (start_hlid + i) % WL12XX_MAX_LINKS; /* only consider connected stations */ if (!test_bit(h, wlvif->links_map)) continue; skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]); if (!skb) continue; wlvif->last_tx_hlid = h; break; } if (!skb) wlvif->last_tx_hlid = 0; *hlid = wlvif->last_tx_hlid; return skb; } static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid) { unsigned long flags; struct wl12xx_vif *wlvif = wl->last_wlvif; struct sk_buff *skb = NULL; /* continue from last wlvif (round robin) */ if (wlvif) { wl12xx_for_each_wlvif_continue(wl, wlvif) { skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid); if (skb) { wl->last_wlvif = wlvif; break; } } } /* dequeue from the system HLID before the restarting wlvif list */ if (!skb) { skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]); *hlid = wl->system_hlid; } /* do a new pass over the wlvif list */ if (!skb) { wl12xx_for_each_wlvif(wl, wlvif) { skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid); if (skb) { wl->last_wlvif = wlvif; break; } /* * No need to continue after last_wlvif. The previous * pass should have found it. */ if (wlvif == wl->last_wlvif) break; } } if (!skb && test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { int q; skb = wl->dummy_packet; *hlid = wl->system_hlid; q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); spin_lock_irqsave(&wl->wl_lock, flags); WARN_ON_ONCE(wl->tx_queue_count[q] <= 0); wl->tx_queue_count[q]--; spin_unlock_irqrestore(&wl->wl_lock, flags); } return skb; } static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, u8 hlid) { unsigned long flags; int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); if (wl12xx_is_dummy_packet(wl, skb)) { set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); } else { skb_queue_head(&wl->links[hlid].tx_queue[q], skb); /* make sure we dequeue the same packet next time */ wlvif->last_tx_hlid = (hlid + WL12XX_MAX_LINKS - 1) % WL12XX_MAX_LINKS; } spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count[q]++; spin_unlock_irqrestore(&wl->wl_lock, flags); } static bool wl1271_tx_is_data_present(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); return ieee80211_is_data_present(hdr->frame_control); } void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids) { struct wl12xx_vif *wlvif; u32 timeout; u8 hlid; if (!wl->conf.rx_streaming.interval) return; if (!wl->conf.rx_streaming.always && !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)) return; timeout = wl->conf.rx_streaming.duration; wl12xx_for_each_wlvif_sta(wl, wlvif) { bool found = false; for_each_set_bit(hlid, active_hlids, WL12XX_MAX_LINKS) { if (test_bit(hlid, wlvif->links_map)) { found = true; break; } } if (!found) continue; /* enable rx streaming */ if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags)) ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_enable_work); mod_timer(&wlvif->rx_streaming_timer, jiffies + msecs_to_jiffies(timeout)); } } /* * Returns failure values only in case of failed bus ops within this function. * wl1271_prepare_tx_frame retvals won't be returned in order to avoid * triggering recovery by higher layers when not necessary. * In case a FW command fails within wl1271_prepare_tx_frame fails a recovery * will be queued in wl1271_cmd_send. -EAGAIN/-EBUSY from prepare_tx_frame * can occur and are legitimate so don't propagate. -EINVAL will emit a WARNING * within prepare_tx_frame code but there's nothing we should do about those * as well. */ int wlcore_tx_work_locked(struct wl1271 *wl) { struct wl12xx_vif *wlvif; struct sk_buff *skb; struct wl1271_tx_hw_descr *desc; u32 buf_offset = 0, last_len = 0; bool sent_packets = false; unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; int ret = 0; int bus_ret = 0; u8 hlid; if (unlikely(wl->state == WL1271_STATE_OFF)) return 0; while ((skb = wl1271_skb_dequeue(wl, &hlid))) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); bool has_data = false; wlvif = NULL; if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif) wlvif = wl12xx_vif_to_data(info->control.vif); else hlid = wl->system_hlid; has_data = wlvif && wl1271_tx_is_data_present(skb); ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset, hlid); if (ret == -EAGAIN) { /* * Aggregation buffer is full. * Flush buffer and try again. */ wl1271_skb_queue_head(wl, wlvif, skb, hlid); buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); bus_ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, buf_offset, true); if (bus_ret < 0) goto out; sent_packets = true; buf_offset = 0; continue; } else if (ret == -EBUSY) { /* * Firmware buffer is full. * Queue back last skb, and stop aggregating. */ wl1271_skb_queue_head(wl, wlvif, skb, hlid); /* No work left, avoid scheduling redundant tx work */ set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); goto out_ack; } else if (ret < 0) { if (wl12xx_is_dummy_packet(wl, skb)) /* * fw still expects dummy packet, * so re-enqueue it */ wl1271_skb_queue_head(wl, wlvif, skb, hlid); else ieee80211_free_txskb(wl->hw, skb); goto out_ack; } last_len = ret; buf_offset += last_len; wl->tx_packets_count++; if (has_data) { desc = (struct wl1271_tx_hw_descr *) skb->data; __set_bit(desc->hlid, active_hlids); } } out_ack: if (buf_offset) { buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); bus_ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, buf_offset, true); if (bus_ret < 0) goto out; sent_packets = true; } if (sent_packets) { /* * Interrupt the firmware with the new packets. This is only * required for older hardware revisions */ if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) { bus_ret = wlcore_write32(wl, WL12XX_HOST_WR_ACCESS, wl->tx_packets_count); if (bus_ret < 0) goto out; } wl1271_handle_tx_low_watermark(wl); } wl12xx_rearm_rx_streaming(wl, active_hlids); out: return bus_ret; } void wl1271_tx_work(struct work_struct *work) { struct wl1271 *wl = container_of(work, struct wl1271, tx_work); int ret; mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wlcore_tx_work_locked(wl); if (ret < 0) { wl12xx_queue_recovery_work(wl); goto out; } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } static u8 wl1271_tx_get_rate_flags(u8 rate_class_index) { u8 flags = 0; /* * TODO: use wl12xx constants when this code is moved to wl12xx, as * only it uses Tx-completion. */ if (rate_class_index <= 8) flags |= IEEE80211_TX_RC_MCS; /* * TODO: use wl12xx constants when this code is moved to wl12xx, as * only it uses Tx-completion. */ if (rate_class_index == 0) flags |= IEEE80211_TX_RC_SHORT_GI; return flags; } static void wl1271_tx_complete_packet(struct wl1271 *wl, struct wl1271_tx_hw_res_descr *result) { struct ieee80211_tx_info *info; struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; struct sk_buff *skb; int id = result->id; int rate = -1; u8 rate_flags = 0; u8 retries = 0; /* check for id legality */ if (unlikely(id >= wl->num_tx_desc || wl->tx_frames[id] == NULL)) { wl1271_warning("TX result illegal id: %d", id); return; } skb = wl->tx_frames[id]; info = IEEE80211_SKB_CB(skb); if (wl12xx_is_dummy_packet(wl, skb)) { wl1271_free_tx_id(wl, id); return; } /* info->control is valid as long as we don't update info->status */ vif = info->control.vif; wlvif = wl12xx_vif_to_data(vif); /* update the TX status info */ if (result->status == TX_SUCCESS) { if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_ACK; rate = wlcore_rate_to_idx(wl, result->rate_class_index, wlvif->band); rate_flags = wl1271_tx_get_rate_flags(result->rate_class_index); retries = result->ack_failures; } else if (result->status == TX_RETRY_EXCEEDED) { wl->stats.excessive_retries++; retries = result->ack_failures; } info->status.rates[0].idx = rate; info->status.rates[0].count = retries; info->status.rates[0].flags = rate_flags; info->status.ack_signal = -1; wl->stats.retry_count += result->ack_failures; /* * update sequence number only when relevant, i.e. only in * sessions of TKIP, AES and GEM (not in open or WEP sessions) */ if (info->control.hw_key && (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP || info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP || info->control.hw_key->cipher == WL1271_CIPHER_SUITE_GEM)) { u8 fw_lsb = result->tx_security_sequence_number_lsb; u8 cur_lsb = wlvif->tx_security_last_seq_lsb; /* * update security sequence number, taking care of potential * wrap-around */ wlvif->tx_security_seq += (fw_lsb - cur_lsb) & 0xff; wlvif->tx_security_last_seq_lsb = fw_lsb; } /* remove private header from packet */ skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); /* remove TKIP header space if present */ if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen); skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); } wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" " status 0x%x", result->id, skb, result->ack_failures, result->rate_class_index, result->status); /* return the packet to the stack */ skb_queue_tail(&wl->deferred_tx_queue, skb); queue_work(wl->freezable_wq, &wl->netstack_work); wl1271_free_tx_id(wl, result->id); } /* Called upon reception of a TX complete interrupt */ int wlcore_tx_complete(struct wl1271 *wl) { struct wl1271_acx_mem_map *memmap = wl->target_mem_map; u32 count, fw_counter; u32 i; int ret; /* read the tx results from the chipset */ ret = wlcore_read(wl, le32_to_cpu(memmap->tx_result), wl->tx_res_if, sizeof(*wl->tx_res_if), false); if (ret < 0) goto out; fw_counter = le32_to_cpu(wl->tx_res_if->tx_result_fw_counter); /* write host counter to chipset (to ack) */ ret = wlcore_write32(wl, le32_to_cpu(memmap->tx_result) + offsetof(struct wl1271_tx_hw_res_if, tx_result_host_counter), fw_counter); if (ret < 0) goto out; count = fw_counter - wl->tx_results_count; wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); /* verify that the result buffer is not getting overrun */ if (unlikely(count > TX_HW_RESULT_QUEUE_LEN)) wl1271_warning("TX result overflow from chipset: %d", count); /* process the results */ for (i = 0; i < count; i++) { struct wl1271_tx_hw_res_descr *result; u8 offset = wl->tx_results_count & TX_HW_RESULT_QUEUE_LEN_MASK; /* process the packet */ result = &(wl->tx_res_if->tx_results_queue[offset]); wl1271_tx_complete_packet(wl, result); wl->tx_results_count++; } out: return ret; } EXPORT_SYMBOL(wlcore_tx_complete); void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) { struct sk_buff *skb; int i; unsigned long flags; struct ieee80211_tx_info *info; int total[NUM_TX_QUEUES]; for (i = 0; i < NUM_TX_QUEUES; i++) { total[i] = 0; while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb); if (!wl12xx_is_dummy_packet(wl, skb)) { info = IEEE80211_SKB_CB(skb); info->status.rates[0].idx = -1; info->status.rates[0].count = 0; ieee80211_tx_status_ni(wl->hw, skb); } total[i]++; } } spin_lock_irqsave(&wl->wl_lock, flags); for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_queue_count[i] -= total[i]; spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); } /* caller must hold wl->mutex and TX must be stopped */ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int i; /* TX failure */ for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) { if (wlvif->bss_type == BSS_TYPE_AP_BSS) wl1271_free_sta(wl, wlvif, i); else wlvif->sta.ba_rx_bitmap = 0; wl->links[i].allocated_pkts = 0; wl->links[i].prev_freed_pkts = 0; } wlvif->last_tx_hlid = 0; } /* caller must hold wl->mutex and TX must be stopped */ void wl12xx_tx_reset(struct wl1271 *wl) { int i; struct sk_buff *skb; struct ieee80211_tx_info *info; /* only reset the queues if something bad happened */ if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) { for (i = 0; i < WL12XX_MAX_LINKS; i++) wl1271_tx_reset_link_queues(wl, i); for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_queue_count[i] = 0; } /* * Make sure the driver is at a consistent state, in case this * function is called from a context other than interface removal. * This call will always wake the TX queues. */ wl1271_handle_tx_low_watermark(wl); for (i = 0; i < wl->num_tx_desc; i++) { if (wl->tx_frames[i] == NULL) continue; skb = wl->tx_frames[i]; wl1271_free_tx_id(wl, i); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); if (!wl12xx_is_dummy_packet(wl, skb)) { /* * Remove private headers before passing the skb to * mac80211 */ info = IEEE80211_SKB_CB(skb); skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen); skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); } info->status.rates[0].idx = -1; info->status.rates[0].count = 0; ieee80211_tx_status_ni(wl->hw, skb); } } } #define WL1271_TX_FLUSH_TIMEOUT 500000 /* caller must *NOT* hold wl->mutex */ void wl1271_tx_flush(struct wl1271 *wl) { unsigned long timeout; int i; timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT); /* only one flush should be in progress, for consistent queue state */ mutex_lock(&wl->flush_mutex); wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH); while (!time_after(jiffies, timeout)) { mutex_lock(&wl->mutex); wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d", wl->tx_frames_cnt, wl1271_tx_total_queue_count(wl)); if ((wl->tx_frames_cnt == 0) && (wl1271_tx_total_queue_count(wl) == 0)) { mutex_unlock(&wl->mutex); goto out; } mutex_unlock(&wl->mutex); msleep(1); } wl1271_warning("Unable to flush all TX buffers, timed out."); /* forcibly flush all Tx buffers on our queues */ mutex_lock(&wl->mutex); for (i = 0; i < WL12XX_MAX_LINKS; i++) wl1271_tx_reset_link_queues(wl, i); mutex_unlock(&wl->mutex); out: wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH); mutex_unlock(&wl->flush_mutex); } EXPORT_SYMBOL_GPL(wl1271_tx_flush); u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) { if (WARN_ON(!rate_set)) return 0; return BIT(__ffs(rate_set)); } void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason) { bool stopped = !!wl->queue_stop_reasons[queue]; /* queue should not be stopped for this reason */ WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue])); if (stopped) return; ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); } void wlcore_stop_queue(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason) { unsigned long flags; spin_lock_irqsave(&wl->wl_lock, flags); wlcore_stop_queue_locked(wl, queue, reason); spin_unlock_irqrestore(&wl->wl_lock, flags); } void wlcore_wake_queue(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason) { unsigned long flags; spin_lock_irqsave(&wl->wl_lock, flags); /* queue should not be clear for this reason */ WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue])); if (wl->queue_stop_reasons[queue]) goto out; ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); out: spin_unlock_irqrestore(&wl->wl_lock, flags); } void wlcore_stop_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason) { int i; for (i = 0; i < NUM_TX_QUEUES; i++) wlcore_stop_queue(wl, i, reason); } EXPORT_SYMBOL_GPL(wlcore_stop_queues); void wlcore_wake_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason) { int i; for (i = 0; i < NUM_TX_QUEUES; i++) wlcore_wake_queue(wl, i, reason); } EXPORT_SYMBOL_GPL(wlcore_wake_queues); void wlcore_reset_stopped_queues(struct wl1271 *wl) { int i; unsigned long flags; spin_lock_irqsave(&wl->wl_lock, flags); for (i = 0; i < NUM_TX_QUEUES; i++) { if (!wl->queue_stop_reasons[i]) continue; wl->queue_stop_reasons[i] = 0; ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(i)); } spin_unlock_irqrestore(&wl->wl_lock, flags); } bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason) { return test_bit(reason, &wl->queue_stop_reasons[queue]); } bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue) { return !!wl->queue_stop_reasons[queue]; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/testmode.h0000644000175000017500000000171212026211315024366 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __TESTMODE_H__ #define __TESTMODE_H__ #include int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len); #endif /* __WL1271_TESTMODE_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/testmode.c0000644000175000017500000002030712026211315024362 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "testmode.h" #include #include #include "wlcore.h" #include "debug.h" #include "acx.h" #include "ps.h" #include "io.h" #define WL1271_TM_MAX_DATA_LENGTH 1024 enum wl1271_tm_commands { WL1271_TM_CMD_UNSPEC, WL1271_TM_CMD_TEST, WL1271_TM_CMD_INTERROGATE, WL1271_TM_CMD_CONFIGURE, WL1271_TM_CMD_NVS_PUSH, /* Not in use. Keep to not break ABI */ WL1271_TM_CMD_SET_PLT_MODE, WL1271_TM_CMD_RECOVER, /* Not in use. Keep to not break ABI */ WL1271_TM_CMD_GET_MAC, __WL1271_TM_CMD_AFTER_LAST }; #define WL1271_TM_CMD_MAX (__WL1271_TM_CMD_AFTER_LAST - 1) enum wl1271_tm_attrs { WL1271_TM_ATTR_UNSPEC, WL1271_TM_ATTR_CMD_ID, WL1271_TM_ATTR_ANSWER, WL1271_TM_ATTR_DATA, WL1271_TM_ATTR_IE_ID, WL1271_TM_ATTR_PLT_MODE, __WL1271_TM_ATTR_AFTER_LAST }; #define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1) static struct nla_policy wl1271_tm_policy[WL1271_TM_ATTR_MAX + 1] = { [WL1271_TM_ATTR_CMD_ID] = { .type = NLA_U32 }, [WL1271_TM_ATTR_ANSWER] = { .type = NLA_U8 }, [WL1271_TM_ATTR_DATA] = { .type = NLA_BINARY, .len = WL1271_TM_MAX_DATA_LENGTH }, [WL1271_TM_ATTR_IE_ID] = { .type = NLA_U32 }, [WL1271_TM_ATTR_PLT_MODE] = { .type = NLA_U32 }, }; static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[]) { int buf_len, ret, len; struct sk_buff *skb; void *buf; u8 answer = 0; wl1271_debug(DEBUG_TESTMODE, "testmode cmd test"); if (!tb[WL1271_TM_ATTR_DATA]) return -EINVAL; buf = nla_data(tb[WL1271_TM_ATTR_DATA]); buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); if (tb[WL1271_TM_ATTR_ANSWER]) answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]); if (buf_len > sizeof(struct wl1271_command)) return -EMSGSIZE; mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { ret = -EINVAL; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_cmd_test(wl, buf, buf_len, answer); if (ret < 0) { wl1271_warning("testmode cmd test failed: %d", ret); goto out_sleep; } if (answer) { /* If we got bip calibration answer print radio status */ struct wl1271_cmd_cal_p2g *params = (struct wl1271_cmd_cal_p2g *) buf; s16 radio_status = (s16) le16_to_cpu(params->radio_status); if (params->test.id == TEST_CMD_P2G_CAL && radio_status < 0) wl1271_warning("testmode cmd: radio status=%d", radio_status); else wl1271_info("testmode cmd: radio status=%d", radio_status); len = nla_total_size(buf_len); skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); if (!skb) { ret = -ENOMEM; goto out_sleep; } if (nla_put(skb, WL1271_TM_ATTR_DATA, buf_len, buf)) { kfree_skb(skb); ret = -EMSGSIZE; goto out_sleep; } ret = cfg80211_testmode_reply(skb); if (ret < 0) goto out_sleep; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) { int ret; struct wl1271_command *cmd; struct sk_buff *skb; u8 ie_id; wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate"); if (!tb[WL1271_TM_ATTR_IE_ID]) return -EINVAL; ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { ret = -EINVAL; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out_sleep; } ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); if (ret < 0) { wl1271_warning("testmode cmd interrogate failed: %d", ret); goto out_free; } skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd)); if (!skb) { ret = -ENOMEM; goto out_free; } if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd)) { kfree_skb(skb); ret = -EMSGSIZE; goto out_free; } ret = cfg80211_testmode_reply(skb); if (ret < 0) goto out_free; out_free: kfree(cmd); out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[]) { int buf_len, ret; void *buf; u8 ie_id; wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure"); if (!tb[WL1271_TM_ATTR_DATA]) return -EINVAL; if (!tb[WL1271_TM_ATTR_IE_ID]) return -EINVAL; ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); buf = nla_data(tb[WL1271_TM_ATTR_DATA]); buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); if (buf_len > sizeof(struct wl1271_command)) return -EMSGSIZE; mutex_lock(&wl->mutex); ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len); mutex_unlock(&wl->mutex); if (ret < 0) { wl1271_warning("testmode cmd configure failed: %d", ret); return ret; } return 0; } static int wl1271_tm_detect_fem(struct wl1271 *wl, struct nlattr *tb[]) { /* return FEM type */ int ret, len; struct sk_buff *skb; ret = wl1271_plt_start(wl, PLT_FEM_DETECT); if (ret < 0) goto out; mutex_lock(&wl->mutex); len = nla_total_size(sizeof(wl->fem_manuf)); skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); if (!skb) { ret = -ENOMEM; goto out_mutex; } if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(wl->fem_manuf), &wl->fem_manuf)) { kfree_skb(skb); ret = -EMSGSIZE; goto out_mutex; } ret = cfg80211_testmode_reply(skb); out_mutex: mutex_unlock(&wl->mutex); /* We always stop plt after DETECT mode */ wl1271_plt_stop(wl); out: return ret; } static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[]) { u32 val; int ret; wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode"); if (!tb[WL1271_TM_ATTR_PLT_MODE]) return -EINVAL; val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]); switch (val) { case PLT_OFF: ret = wl1271_plt_stop(wl); break; case PLT_ON: ret = wl1271_plt_start(wl, PLT_ON); break; case PLT_FEM_DETECT: ret = wl1271_tm_detect_fem(wl, tb); break; default: ret = -EINVAL; break; } return ret; } static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[]) { struct sk_buff *skb; u8 mac_addr[ETH_ALEN]; int ret = 0; mutex_lock(&wl->mutex); if (!wl->plt) { ret = -EINVAL; goto out; } if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) { ret = -EOPNOTSUPP; goto out; } mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16); mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8); mac_addr[2] = (u8) wl->fuse_oui_addr; mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16); mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8); mac_addr[5] = (u8) wl->fuse_nic_addr; skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN); if (!skb) { ret = -ENOMEM; goto out; } if (nla_put(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr)) { kfree_skb(skb); ret = -EMSGSIZE; goto out; } ret = cfg80211_testmode_reply(skb); if (ret < 0) goto out; out: mutex_unlock(&wl->mutex); return ret; } int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) { struct wl1271 *wl = hw->priv; struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; int err; err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); if (err) return err; if (!tb[WL1271_TM_ATTR_CMD_ID]) return -EINVAL; switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) { case WL1271_TM_CMD_TEST: return wl1271_tm_cmd_test(wl, tb); case WL1271_TM_CMD_INTERROGATE: return wl1271_tm_cmd_interrogate(wl, tb); case WL1271_TM_CMD_CONFIGURE: return wl1271_tm_cmd_configure(wl, tb); case WL1271_TM_CMD_SET_PLT_MODE: return wl1271_tm_cmd_set_plt_mode(wl, tb); case WL1271_TM_CMD_GET_MAC: return wl12xx_tm_cmd_get_mac(wl, tb); default: return -EOPNOTSUPP; } } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/spi.c0000644000175000017500000002376112026211315023340 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include "wlcore.h" #include "wl12xx_80211.h" #include "io.h" #define WSPI_CMD_READ 0x40000000 #define WSPI_CMD_WRITE 0x00000000 #define WSPI_CMD_FIXED 0x20000000 #define WSPI_CMD_BYTE_LENGTH 0x1FFE0000 #define WSPI_CMD_BYTE_LENGTH_OFFSET 17 #define WSPI_CMD_BYTE_ADDR 0x0001FFFF #define WSPI_INIT_CMD_CRC_LEN 5 #define WSPI_INIT_CMD_START 0x00 #define WSPI_INIT_CMD_TX 0x40 /* the extra bypass bit is sampled by the TNET as '1' */ #define WSPI_INIT_CMD_BYPASS_BIT 0x80 #define WSPI_INIT_CMD_FIXEDBUSY_LEN 0x07 #define WSPI_INIT_CMD_EN_FIXEDBUSY 0x80 #define WSPI_INIT_CMD_DIS_FIXEDBUSY 0x00 #define WSPI_INIT_CMD_IOD 0x40 #define WSPI_INIT_CMD_IP 0x20 #define WSPI_INIT_CMD_CS 0x10 #define WSPI_INIT_CMD_WS 0x08 #define WSPI_INIT_CMD_WSPI 0x01 #define WSPI_INIT_CMD_END 0x01 #define WSPI_INIT_CMD_LEN 8 #define HW_ACCESS_WSPI_FIXED_BUSY_LEN \ ((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32)) #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 /* HW limitation: maximum possible chunk size is 4095 bytes */ #define WSPI_MAX_CHUNK_SIZE 4092 #define WSPI_MAX_NUM_OF_CHUNKS (WL1271_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) struct wl12xx_spi_glue { struct device *dev; struct platform_device *core; }; static void wl12xx_spi_reset(struct device *child) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); u8 *cmd; struct spi_transfer t; struct spi_message m; cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { dev_err(child->parent, "could not allocate cmd for spi reset\n"); return; } memset(&t, 0, sizeof(t)); spi_message_init(&m); memset(cmd, 0xff, WSPI_INIT_CMD_LEN); t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; spi_message_add_tail(&t, &m); spi_sync(to_spi_device(glue->dev), &m); kfree(cmd); } static void wl12xx_spi_init(struct device *child) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; struct spi_transfer t; struct spi_message m; cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { dev_err(child->parent, "could not allocate cmd for spi init\n"); return; } memset(crc, 0, sizeof(crc)); memset(&t, 0, sizeof(t)); spi_message_init(&m); /* * Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB */ cmd[2] = 0xff; cmd[3] = 0xff; cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; cmd[0] = 0; cmd[7] = 0; cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; else cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; crc[0] = cmd[1]; crc[1] = cmd[0]; crc[2] = cmd[7]; crc[3] = cmd[6]; crc[4] = cmd[5]; cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; cmd[4] |= WSPI_INIT_CMD_END; t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; spi_message_add_tail(&t, &m); spi_sync(to_spi_device(glue->dev), &m); kfree(cmd); } #define WL1271_BUSY_WORD_TIMEOUT 1000 static int wl12xx_spi_read_busy(struct device *child) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); struct wl1271 *wl = dev_get_drvdata(child); struct spi_transfer t[1]; struct spi_message m; u32 *busy_buf; int num_busy_bytes = 0; /* * Read further busy words from SPI until a non-busy word is * encountered, then read the data itself into the buffer. */ num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT; busy_buf = wl->buffer_busyword; while (num_busy_bytes) { num_busy_bytes--; spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].rx_buf = busy_buf; t[0].len = sizeof(u32); t[0].cs_change = true; spi_message_add_tail(&t[0], &m); spi_sync(to_spi_device(glue->dev), &m); if (*busy_buf & 0x1) return 0; } /* The SPI bus is unresponsive, the read failed. */ dev_err(child->parent, "SPI read busy-word timeout!\n"); return -ETIMEDOUT; } static int __must_check wl12xx_spi_raw_read(struct device *child, int addr, void *buf, size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); struct wl1271 *wl = dev_get_drvdata(child); struct spi_transfer t[2]; struct spi_message m; u32 *busy_buf; u32 *cmd; u32 chunk_len; while (len > 0) { chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len); cmd = &wl->buffer_cmd; busy_buf = wl->buffer_busyword; *cmd = 0; *cmd |= WSPI_CMD_READ; *cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; *cmd |= addr & WSPI_CMD_BYTE_ADDR; if (fixed) *cmd |= WSPI_CMD_FIXED; spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].tx_buf = cmd; t[0].len = 4; t[0].cs_change = true; spi_message_add_tail(&t[0], &m); /* Busy and non busy words read */ t[1].rx_buf = busy_buf; t[1].len = WL1271_BUSY_WORD_LEN; t[1].cs_change = true; spi_message_add_tail(&t[1], &m); spi_sync(to_spi_device(glue->dev), &m); if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) && wl12xx_spi_read_busy(child)) { memset(buf, 0, chunk_len); return 0; } spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].rx_buf = buf; t[0].len = chunk_len; t[0].cs_change = true; spi_message_add_tail(&t[0], &m); spi_sync(to_spi_device(glue->dev), &m); if (!fixed) addr += chunk_len; buf += chunk_len; len -= chunk_len; } return 0; } static int __must_check wl12xx_spi_raw_write(struct device *child, int addr, void *buf, size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS]; struct spi_message m; u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; u32 *cmd; u32 chunk_len; int i; WARN_ON(len > WL1271_AGGR_BUFFER_SIZE); spi_message_init(&m); memset(t, 0, sizeof(t)); cmd = &commands[0]; i = 0; while (len > 0) { chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len); *cmd = 0; *cmd |= WSPI_CMD_WRITE; *cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; *cmd |= addr & WSPI_CMD_BYTE_ADDR; if (fixed) *cmd |= WSPI_CMD_FIXED; t[i].tx_buf = cmd; t[i].len = sizeof(*cmd); spi_message_add_tail(&t[i++], &m); t[i].tx_buf = buf; t[i].len = chunk_len; spi_message_add_tail(&t[i++], &m); if (!fixed) addr += chunk_len; buf += chunk_len; len -= chunk_len; cmd++; } spi_sync(to_spi_device(glue->dev), &m); return 0; } static struct wl1271_if_operations spi_ops = { .read = wl12xx_spi_raw_read, .write = wl12xx_spi_raw_write, .reset = wl12xx_spi_reset, .init = wl12xx_spi_init, .set_block_size = NULL, }; static int __devinit wl1271_probe(struct spi_device *spi) { struct wl12xx_spi_glue *glue; struct wl12xx_platform_data *pdata; struct resource res[1]; int ret = -ENOMEM; pdata = spi->dev.platform_data; if (!pdata) { dev_err(&spi->dev, "no platform data\n"); return -ENODEV; } pdata->ops = &spi_ops; glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&spi->dev, "can't allocate glue\n"); goto out; } glue->dev = &spi->dev; spi_set_drvdata(spi, glue); /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ spi->bits_per_word = 32; ret = spi_setup(spi); if (ret < 0) { dev_err(glue->dev, "spi_setup failed\n"); goto out_free_glue; } glue->core = platform_device_alloc("wl12xx", -1); if (!glue->core) { dev_err(glue->dev, "can't allocate platform_device\n"); ret = -ENOMEM; goto out_free_glue; } glue->core->dev.parent = &spi->dev; memset(res, 0x00, sizeof(res)); res[0].start = spi->irq; res[0].flags = IORESOURCE_IRQ; res[0].name = "irq"; ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res)); if (ret) { dev_err(glue->dev, "can't add resources\n"); goto out_dev_put; } ret = platform_device_add_data(glue->core, pdata, sizeof(*pdata)); if (ret) { dev_err(glue->dev, "can't add platform data\n"); goto out_dev_put; } ret = platform_device_add(glue->core); if (ret) { dev_err(glue->dev, "can't register platform device\n"); goto out_dev_put; } return 0; out_dev_put: platform_device_put(glue->core); out_free_glue: kfree(glue); out: return ret; } static int __devexit wl1271_remove(struct spi_device *spi) { struct wl12xx_spi_glue *glue = spi_get_drvdata(spi); platform_device_del(glue->core); platform_device_put(glue->core); kfree(glue); return 0; } static struct spi_driver wl1271_spi_driver = { .driver = { .name = "wl1271_spi", .owner = THIS_MODULE, }, .probe = wl1271_probe, .remove = __devexit_p(wl1271_remove), }; static int __init wl1271_init(void) { return spi_register_driver(&wl1271_spi_driver); } static void __exit wl1271_exit(void) { spi_unregister_driver(&wl1271_spi_driver); } module_init(wl1271_init); module_exit(wl1271_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_ALIAS("spi:wl1271"); compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/sdio.c0000644000175000017500000002371112026211315023476 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wlcore.h" #include "wl12xx_80211.h" #include "io.h" #ifndef SDIO_VENDOR_ID_TI #define SDIO_VENDOR_ID_TI 0x0097 #endif #ifndef SDIO_DEVICE_ID_TI_WL1271 #define SDIO_DEVICE_ID_TI_WL1271 0x4076 #endif static bool dump = false; struct wl12xx_sdio_glue { struct device *dev; struct platform_device *core; }; static const struct sdio_device_id wl1271_devices[] __devinitconst = { { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) }, {} }; MODULE_DEVICE_TABLE(sdio, wl1271_devices); static void wl1271_sdio_set_block_size(struct device *child, unsigned int blksz) { struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); sdio_set_block_size(func, blksz); sdio_release_host(func); } static int __must_check wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, size_t len, bool fixed) { int ret; struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); if (unlikely(dump)) { printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr); print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ", DUMP_PREFIX_OFFSET, 16, 1, buf, len, false); } if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) { ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n", addr, ((u8 *)buf)[0]); } else { if (fixed) ret = sdio_readsb(func, buf, addr, len); else ret = sdio_memcpy_fromio(func, buf, addr, len); dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n", addr, len); } sdio_release_host(func); if (WARN_ON(ret)) dev_err(child->parent, "sdio read failed (%d)\n", ret); return ret; } static int __must_check wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, size_t len, bool fixed) { int ret; struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); if (unlikely(dump)) { printk(KERN_DEBUG "wlcore_sdio: WRITE to 0x%04x\n", addr); print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ", DUMP_PREFIX_OFFSET, 16, 1, buf, len, false); } if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) { sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n", addr, ((u8 *)buf)[0]); } else { dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n", addr, len); if (fixed) ret = sdio_writesb(func, addr, buf, len); else ret = sdio_memcpy_toio(func, addr, buf, len); } sdio_release_host(func); if (WARN_ON(ret)) dev_err(child->parent, "sdio write failed (%d)\n", ret); return ret; } static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue) { int ret; struct sdio_func *func = dev_to_sdio_func(glue->dev); struct mmc_card *card = func->card; ret = pm_runtime_get_sync(&card->dev); if (ret) { /* * Runtime PM might be temporarily disabled, or the device * might have a positive reference counter. Make sure it is * really powered on. */ ret = mmc_power_restore_host(card->host); if (ret < 0) { pm_runtime_put_sync(&card->dev); goto out; } } sdio_claim_host(func); sdio_enable_func(func); sdio_release_host(func); out: return ret; } static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue) { int ret; struct sdio_func *func = dev_to_sdio_func(glue->dev); struct mmc_card *card = func->card; sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); /* Power off the card manually in case it wasn't powered off above */ ret = mmc_power_save_host(card->host); if (ret < 0) goto out; /* Let runtime PM know the card is powered off */ pm_runtime_put_sync(&card->dev); out: return ret; } static int wl12xx_sdio_set_power(struct device *child, bool enable) { struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); if (enable) return wl12xx_sdio_power_on(glue); else return wl12xx_sdio_power_off(glue); } static struct wl1271_if_operations sdio_ops = { .read = wl12xx_sdio_raw_read, .write = wl12xx_sdio_raw_write, .power = wl12xx_sdio_set_power, .set_block_size = wl1271_sdio_set_block_size, }; static int __devinit wl1271_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct wl12xx_platform_data *wlan_data; struct wl12xx_sdio_glue *glue; struct resource res[1]; mmc_pm_flag_t mmcflags; int ret = -ENOMEM; const char *chip_family; /* We are only able to handle the wlan function */ if (func->num != 0x02) return -ENODEV; glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&func->dev, "can't allocate glue\n"); goto out; } glue->dev = &func->dev; /* Grab access to FN0 for ELP reg. */ func->card->quirks |= MMC_QUIRK_LENIENT_FN0; /* Use block mode for transferring over one block size of data */ func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; wlan_data = wl12xx_get_platform_data(); if (IS_ERR(wlan_data)) { ret = PTR_ERR(wlan_data); dev_err(glue->dev, "missing wlan platform data: %d\n", ret); goto out_free_glue; } /* if sdio can keep power while host is suspended, enable wow */ mmcflags = sdio_get_host_pm_caps(func); dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags); if (mmcflags & MMC_PM_KEEP_POWER) wlan_data->pwr_in_suspend = true; wlan_data->ops = &sdio_ops; sdio_set_drvdata(func, glue); /* Tell PM core that we don't need the card to be powered now */ pm_runtime_put_noidle(&func->dev); /* * Due to a hardware bug, we can't differentiate wl18xx from * wl12xx, because both report the same device ID. The only * way to differentiate is by checking the SDIO revision, * which is 3.00 on the wl18xx chips. */ if (func->card->cccr.sdio_vsn == SDIO_SDIO_REV_3_00) chip_family = "wl18xx"; else chip_family = "wl12xx"; glue->core = platform_device_alloc(chip_family, -1); if (!glue->core) { dev_err(glue->dev, "can't allocate platform_device"); ret = -ENOMEM; goto out_free_glue; } glue->core->dev.parent = &func->dev; memset(res, 0x00, sizeof(res)); res[0].start = wlan_data->irq; res[0].flags = IORESOURCE_IRQ; res[0].name = "irq"; ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res)); if (ret) { dev_err(glue->dev, "can't add resources\n"); goto out_dev_put; } ret = platform_device_add_data(glue->core, wlan_data, sizeof(*wlan_data)); if (ret) { dev_err(glue->dev, "can't add platform data\n"); goto out_dev_put; } ret = platform_device_add(glue->core); if (ret) { dev_err(glue->dev, "can't add platform device\n"); goto out_dev_put; } return 0; out_dev_put: platform_device_put(glue->core); out_free_glue: kfree(glue); out: return ret; } static void __devexit wl1271_remove(struct sdio_func *func) { struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func); /* Undo decrement done above in wl1271_probe */ pm_runtime_get_noresume(&func->dev); platform_device_del(glue->core); platform_device_put(glue->core); kfree(glue); } #ifdef CONFIG_PM static int wl1271_suspend(struct device *dev) { /* Tell MMC/SDIO core it's OK to power down the card * (if it isn't already), but not to remove it completely */ struct sdio_func *func = dev_to_sdio_func(dev); struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func); struct wl1271 *wl = platform_get_drvdata(glue->core); mmc_pm_flag_t sdio_flags; int ret = 0; dev_dbg(dev, "wl1271 suspend. wow_enabled: %d\n", wl->wow_enabled); /* check whether sdio should keep power */ if (wl->wow_enabled) { sdio_flags = sdio_get_host_pm_caps(func); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { dev_err(dev, "can't keep power while host " "is suspended\n"); ret = -EINVAL; goto out; } /* keep power while host suspended */ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) { dev_err(dev, "error while trying to keep power\n"); goto out; } } out: return ret; } static int wl1271_resume(struct device *dev) { dev_dbg(dev, "wl1271 resume\n"); return 0; } static const struct dev_pm_ops wl1271_sdio_pm_ops = { .suspend = wl1271_suspend, .resume = wl1271_resume, }; #endif static struct sdio_driver wl1271_sdio_driver = { .name = "wl1271_sdio", .id_table = wl1271_devices, .probe = wl1271_probe, .remove = __devexit_p(wl1271_remove), #ifdef CONFIG_PM .drv = { .pm = &wl1271_sdio_pm_ops, }, #endif }; static int __init wl1271_init(void) { return sdio_register_driver(&wl1271_sdio_driver); } static void __exit wl1271_exit(void) { sdio_unregister_driver(&wl1271_sdio_driver); } module_init(wl1271_init); module_exit(wl1271_exit); module_param(dump, bool, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(dump, "Enable sdio read/write dumps."); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/scan.h0000644000175000017500000001353612026211315023475 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __SCAN_H__ #define __SCAN_H__ #include "wlcore.h" int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, struct cfg80211_scan_request *req); int wl1271_scan_stop(struct wl1271 *wl); int wl1271_scan_build_probe_req(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 band); void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif); void wl1271_scan_complete_work(struct work_struct *work); int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl1271_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_MAX_CHANNELS 24 #define WL1271_SCAN_DEFAULT_TAG 1 #define WL1271_SCAN_CURRENT_TX_PWR 0 #define WL1271_SCAN_OPT_ACTIVE 0 #define WL1271_SCAN_OPT_PASSIVE 1 #define WL1271_SCAN_OPT_SPLIT_SCAN 2 #define WL1271_SCAN_OPT_PRIORITY_HIGH 4 /* scan even if we fail to enter psm */ #define WL1271_SCAN_OPT_FORCE 8 #define WL1271_SCAN_BAND_2_4_GHZ 0 #define WL1271_SCAN_BAND_5_GHZ 1 #define WL1271_SCAN_TIMEOUT 30000 /* msec */ enum { WL1271_SCAN_STATE_IDLE, WL1271_SCAN_STATE_2GHZ_ACTIVE, WL1271_SCAN_STATE_2GHZ_PASSIVE, WL1271_SCAN_STATE_5GHZ_ACTIVE, WL1271_SCAN_STATE_5GHZ_PASSIVE, WL1271_SCAN_STATE_DONE }; struct basic_scan_params { /* Scan option flags (WL1271_SCAN_OPT_*) */ __le16 scan_options; u8 role_id; /* Number of scan channels in the list (maximum 30) */ u8 n_ch; /* This field indicates the number of probe requests to send per channel for an active scan */ u8 n_probe_reqs; u8 tid_trigger; u8 ssid_len; u8 use_ssid_list; /* Rate bit field for sending the probes */ __le32 tx_rate; u8 ssid[IEEE80211_MAX_SSID_LEN]; /* Band to scan */ u8 band; u8 scan_tag; u8 padding2[2]; } __packed; struct basic_scan_channel_params { /* Duration in TU to wait for frames on a channel for active scan */ __le32 min_duration; __le32 max_duration; __le32 bssid_lsb; __le16 bssid_msb; u8 early_termination; u8 tx_power_att; u8 channel; /* FW internal use only! */ u8 dfs_candidate; u8 activity_detected; u8 pad; } __packed; struct wl1271_cmd_scan { struct wl1271_cmd_header header; struct basic_scan_params params; struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS]; /* src mac address */ u8 addr[ETH_ALEN]; u8 padding[2]; } __packed; struct wl1271_cmd_trigger_scan_to { struct wl1271_cmd_header header; __le32 timeout; } __packed; #define MAX_CHANNELS_2GHZ 14 #define MAX_CHANNELS_5GHZ 23 #define MAX_CHANNELS_4GHZ 4 #define SCAN_MAX_CYCLE_INTERVALS 16 #define SCAN_MAX_BANDS 3 enum { SCAN_SSID_FILTER_ANY = 0, SCAN_SSID_FILTER_SPECIFIC = 1, SCAN_SSID_FILTER_LIST = 2, SCAN_SSID_FILTER_DISABLED = 3 }; enum { SCAN_BSS_TYPE_INDEPENDENT, SCAN_BSS_TYPE_INFRASTRUCTURE, SCAN_BSS_TYPE_ANY, }; #define SCAN_CHANNEL_FLAGS_DFS BIT(0) /* channel is passive until an activity is detected on it */ #define SCAN_CHANNEL_FLAGS_DFS_ENABLED BIT(1) struct conn_scan_ch_params { __le16 min_duration; __le16 max_duration; __le16 passive_duration; u8 channel; u8 tx_power_att; /* bit 0: DFS channel; bit 1: DFS enabled */ u8 flags; u8 padding[3]; } __packed; struct wl1271_cmd_sched_scan_config { struct wl1271_cmd_header header; __le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; s8 rssi_threshold; /* for filtering (in dBm) */ s8 snr_threshold; /* for filtering (in dB) */ u8 cycles; /* maximum number of scan cycles */ u8 report_after; /* report when this number of results are received */ u8 terminate; /* stop scanning after reporting */ u8 tag; u8 bss_type; /* for filtering */ u8 filter_type; u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 n_probe_reqs; /* Number of probes requests per channel */ u8 passive[SCAN_MAX_BANDS]; u8 active[SCAN_MAX_BANDS]; u8 dfs; u8 n_pactive_ch; /* number of pactive (passive until fw detects energy) channels in BG band */ u8 role_id; u8 padding[1]; struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; } __packed; #define SCHED_SCAN_MAX_SSIDS 16 enum { SCAN_SSID_TYPE_PUBLIC = 0, SCAN_SSID_TYPE_HIDDEN = 1, }; struct wl1271_ssid { u8 type; u8 len; u8 ssid[IEEE80211_MAX_SSID_LEN]; /* u8 padding[2]; */ } __packed; struct wl1271_cmd_sched_scan_ssid_list { struct wl1271_cmd_header header; u8 n_ssids; struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS]; u8 role_id; u8 padding[2]; } __packed; struct wl1271_cmd_sched_scan_start { struct wl1271_cmd_header header; u8 tag; u8 role_id; u8 padding[2]; } __packed; struct wl1271_cmd_sched_scan_stop { struct wl1271_cmd_header header; u8 tag; u8 role_id; u8 padding[2]; } __packed; #endif /* __WL1271_SCAN_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/scan.c0000644000175000017500000005305112026211315023464 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include "wlcore.h" #include "debug.h" #include "cmd.h" #include "scan.h" #include "acx.h" #include "ps.h" #include "tx.h" void wl1271_scan_complete_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; int ret; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, scan_complete_work); wl1271_debug(DEBUG_SCAN, "Scanning complete"); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) goto out; if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; vif = wl->scan_vif; wlvif = wl12xx_vif_to_data(vif); /* * Rearm the tx watchdog just before idling scan. This * prevents just-finished scans from triggering the watchdog */ wl12xx_rearm_tx_watchdog_locked(wl); wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; wl->scan_vif = NULL; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { /* restore hardware connection monitoring template */ wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq); } wl1271_ps_elp_sleep(wl); if (wl->scan.failed) { wl1271_info("Scan completed due to error."); wl12xx_queue_recovery_work(wl); } ieee80211_scan_completed(wl->hw, false); out: mutex_unlock(&wl->mutex); } static int wl1271_get_scan_channels(struct wl1271 *wl, struct cfg80211_scan_request *req, struct basic_scan_channel_params *channels, enum ieee80211_band band, bool passive) { struct conf_scan_settings *c = &wl->conf.scan; int i, j; u32 flags; for (i = 0, j = 0; i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; i++) { flags = req->channels[i]->flags; if (!test_bit(i, wl->scan.scanned_ch) && !(flags & IEEE80211_CHAN_DISABLED) && (req->channels[i]->band == band) && /* * In passive scans, we scan all remaining * channels, even if not marked as such. * In active scans, we only scan channels not * marked as passive. */ (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) { wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", req->channels[i]->band, req->channels[i]->center_freq); wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", req->channels[i]->hw_value, req->channels[i]->flags); wl1271_debug(DEBUG_SCAN, "max_antenna_gain %d, max_power %d", req->channels[i]->max_antenna_gain, req->channels[i]->max_power); wl1271_debug(DEBUG_SCAN, "beacon_found %d", req->channels[i]->beacon_found); if (!passive) { channels[j].min_duration = cpu_to_le32(c->min_dwell_time_active); channels[j].max_duration = cpu_to_le32(c->max_dwell_time_active); } else { channels[j].min_duration = cpu_to_le32(c->min_dwell_time_passive); channels[j].max_duration = cpu_to_le32(c->max_dwell_time_passive); } channels[j].early_termination = 0; channels[j].tx_power_att = req->channels[i]->max_power; channels[j].channel = req->channels[i]->hw_value; memset(&channels[j].bssid_lsb, 0xff, 4); memset(&channels[j].bssid_msb, 0xff, 2); /* Mark the channels we already used */ set_bit(i, wl->scan.scanned_ch); j++; } } return j; } #define WL1271_NOTHING_TO_SCAN 1 static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, enum ieee80211_band band, bool passive, u32 basic_rate) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl1271_cmd_scan *cmd; struct wl1271_cmd_trigger_scan_to *trigger; int ret; u16 scan_options = 0; /* skip active scans if we don't have SSIDs */ if (!passive && wl->scan.req->n_ssids == 0) return WL1271_NOTHING_TO_SCAN; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); if (!cmd || !trigger) { ret = -ENOMEM; goto out; } if (wl->conf.scan.split_scan_timeout) scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; if (passive) scan_options |= WL1271_SCAN_OPT_PASSIVE; if (wlvif->bss_type == BSS_TYPE_AP_BSS || test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) cmd->params.role_id = wlvif->role_id; else cmd->params.role_id = wlvif->dev_role_id; if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { ret = -EINVAL; goto out; } cmd->params.scan_options = cpu_to_le16(scan_options); cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, cmd->channels, band, passive); if (cmd->params.n_ch == 0) { ret = WL1271_NOTHING_TO_SCAN; goto out; } cmd->params.tx_rate = cpu_to_le32(basic_rate); cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; if (band == IEEE80211_BAND_2GHZ) cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; else cmd->params.band = WL1271_SCAN_BAND_5_GHZ; if (wl->scan.ssid_len && wl->scan.ssid) { cmd->params.ssid_len = wl->scan.ssid_len; memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); } memcpy(cmd->addr, vif->addr, ETH_ALEN); ret = wl12xx_cmd_build_probe_req(wl, wlvif, cmd->params.role_id, band, wl->scan.ssid, wl->scan.ssid_len, wl->scan.req->ie, wl->scan.req->ie_len, false); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; } trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, sizeof(*trigger), 0); if (ret < 0) { wl1271_error("trigger scan to failed for hw scan"); goto out; } wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("SCAN failed"); goto out; } out: kfree(cmd); kfree(trigger); return ret; } void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret = 0; enum ieee80211_band band; u32 rate, mask; switch (wl->scan.state) { case WL1271_SCAN_STATE_IDLE: break; case WL1271_SCAN_STATE_2GHZ_ACTIVE: band = IEEE80211_BAND_2GHZ; mask = wlvif->bitrate_masks[band]; if (wl->scan.req->no_cck) { mask &= ~CONF_TX_CCK_RATES; if (!mask) mask = CONF_TX_RATE_MASK_BASIC_P2P; } rate = wl1271_tx_min_rate_get(wl, mask); ret = wl1271_scan_send(wl, vif, band, false, rate); if (ret == WL1271_NOTHING_TO_SCAN) { wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; wl1271_scan_stm(wl, vif); } break; case WL1271_SCAN_STATE_2GHZ_PASSIVE: band = IEEE80211_BAND_2GHZ; mask = wlvif->bitrate_masks[band]; if (wl->scan.req->no_cck) { mask &= ~CONF_TX_CCK_RATES; if (!mask) mask = CONF_TX_RATE_MASK_BASIC_P2P; } rate = wl1271_tx_min_rate_get(wl, mask); ret = wl1271_scan_send(wl, vif, band, true, rate); if (ret == WL1271_NOTHING_TO_SCAN) { if (wl->enable_11a) wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; else wl->scan.state = WL1271_SCAN_STATE_DONE; wl1271_scan_stm(wl, vif); } break; case WL1271_SCAN_STATE_5GHZ_ACTIVE: band = IEEE80211_BAND_5GHZ; rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); ret = wl1271_scan_send(wl, vif, band, false, rate); if (ret == WL1271_NOTHING_TO_SCAN) { wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; wl1271_scan_stm(wl, vif); } break; case WL1271_SCAN_STATE_5GHZ_PASSIVE: band = IEEE80211_BAND_5GHZ; rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); ret = wl1271_scan_send(wl, vif, band, true, rate); if (ret == WL1271_NOTHING_TO_SCAN) { wl->scan.state = WL1271_SCAN_STATE_DONE; wl1271_scan_stm(wl, vif); } break; case WL1271_SCAN_STATE_DONE: wl->scan.failed = false; cancel_delayed_work(&wl->scan_complete_work); ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, msecs_to_jiffies(0)); break; default: wl1271_error("invalid scan state"); break; } if (ret < 0) { cancel_delayed_work(&wl->scan_complete_work); ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, msecs_to_jiffies(0)); } } int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, struct cfg80211_scan_request *req) { /* * cfg80211 should guarantee that we don't get more channels * than what we have registered. */ BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); if (wl->scan.state != WL1271_SCAN_STATE_IDLE) return -EBUSY; wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE; if (ssid_len && ssid) { wl->scan.ssid_len = ssid_len; memcpy(wl->scan.ssid, ssid, ssid_len); } else { wl->scan.ssid_len = 0; } wl->scan_vif = vif; wl->scan.req = req; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); /* we assume failure so that timeout scenarios are handled correctly */ wl->scan.failed = true; ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); wl1271_scan_stm(wl, vif); return 0; } int wl1271_scan_stop(struct wl1271 *wl) { struct wl1271_cmd_header *cmd = NULL; int ret = 0; if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) return -EINVAL; wl1271_debug(DEBUG_CMD, "cmd scan stop"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("cmd stop_scan failed"); goto out; } out: kfree(cmd); return ret; } static int wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, struct cfg80211_sched_scan_request *req, struct conn_scan_ch_params *channels, u32 band, bool radar, bool passive, int start, int max_channels, u8 *n_pactive_ch) { struct conf_sched_scan_settings *c = &wl->conf.sched_scan; int i, j; u32 flags; bool force_passive = !req->n_ssids; u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe; u32 dwell_time_passive, dwell_time_dfs; if (band == IEEE80211_BAND_5GHZ) delta_per_probe = c->dwell_time_delta_per_probe_5; else delta_per_probe = c->dwell_time_delta_per_probe; min_dwell_time_active = c->base_dwell_time + req->n_ssids * c->num_probe_reqs * delta_per_probe; max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta; min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000); max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000); dwell_time_passive = DIV_ROUND_UP(c->dwell_time_passive, 1000); dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000); for (i = 0, j = start; i < req->n_channels && j < max_channels; i++) { flags = req->channels[i]->flags; if (force_passive) flags |= IEEE80211_CHAN_PASSIVE_SCAN; if ((req->channels[i]->band == band) && !(flags & IEEE80211_CHAN_DISABLED) && (!!(flags & IEEE80211_CHAN_RADAR) == radar) && /* if radar is set, we ignore the passive flag */ (radar || !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) { wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", req->channels[i]->band, req->channels[i]->center_freq); wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", req->channels[i]->hw_value, req->channels[i]->flags); wl1271_debug(DEBUG_SCAN, "max_power %d", req->channels[i]->max_power); wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d", min_dwell_time_active, max_dwell_time_active); if (flags & IEEE80211_CHAN_RADAR) { channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS; channels[j].passive_duration = cpu_to_le16(dwell_time_dfs); } else { channels[j].passive_duration = cpu_to_le16(dwell_time_passive); } channels[j].min_duration = cpu_to_le16(min_dwell_time_active); channels[j].max_duration = cpu_to_le16(max_dwell_time_active); channels[j].tx_power_att = req->channels[i]->max_power; channels[j].channel = req->channels[i]->hw_value; if ((band == IEEE80211_BAND_2GHZ) && (channels[j].channel >= 12) && (channels[j].channel <= 14) && (flags & IEEE80211_CHAN_PASSIVE_SCAN) && !force_passive) { /* pactive channels treated as DFS */ channels[j].flags = SCAN_CHANNEL_FLAGS_DFS; /* * n_pactive_ch is counted down from the end of * the passive channel list */ (*n_pactive_ch)++; wl1271_debug(DEBUG_SCAN, "n_pactive_ch = %d", *n_pactive_ch); } j++; } } return j - start; } static bool wl1271_scan_sched_scan_channels(struct wl1271 *wl, struct cfg80211_sched_scan_request *req, struct wl1271_cmd_sched_scan_config *cfg) { u8 n_pactive_ch = 0; cfg->passive[0] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, IEEE80211_BAND_2GHZ, false, true, 0, MAX_CHANNELS_2GHZ, &n_pactive_ch); cfg->active[0] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, IEEE80211_BAND_2GHZ, false, false, cfg->passive[0], MAX_CHANNELS_2GHZ, &n_pactive_ch); cfg->passive[1] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, IEEE80211_BAND_5GHZ, false, true, 0, MAX_CHANNELS_5GHZ, &n_pactive_ch); cfg->dfs = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, IEEE80211_BAND_5GHZ, true, true, cfg->passive[1], MAX_CHANNELS_5GHZ, &n_pactive_ch); cfg->active[1] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, IEEE80211_BAND_5GHZ, false, false, cfg->passive[1] + cfg->dfs, MAX_CHANNELS_5GHZ, &n_pactive_ch); /* 802.11j channels are not supported yet */ cfg->passive[2] = 0; cfg->active[2] = 0; cfg->n_pactive_ch = n_pactive_ch; wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", cfg->active[0], cfg->passive[0]); wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", cfg->active[1], cfg->passive[1]); wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs); return cfg->passive[0] || cfg->active[0] || cfg->passive[1] || cfg->active[1] || cfg->dfs || cfg->passive[2] || cfg->active[2]; } /* Returns the scan type to be used or a negative value on error */ static int wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req) { struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL; struct cfg80211_match_set *sets = req->match_sets; struct cfg80211_ssid *ssids = req->ssids; int ret = 0, type, i, j, n_match_ssids = 0; wl1271_debug(DEBUG_CMD, "cmd sched scan ssid list"); /* count the match sets that contain SSIDs */ for (i = 0; i < req->n_match_sets; i++) if (sets[i].ssid.ssid_len > 0) n_match_ssids++; /* No filter, no ssids or only bcast ssid */ if (!n_match_ssids && (!req->n_ssids || (req->n_ssids == 1 && req->ssids[0].ssid_len == 0))) { type = SCAN_SSID_FILTER_ANY; goto out; } cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->role_id = wlvif->dev_role_id; if (!n_match_ssids) { /* No filter, with ssids */ type = SCAN_SSID_FILTER_DISABLED; for (i = 0; i < req->n_ssids; i++) { cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ? SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC; cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len; memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid, ssids[i].ssid_len); cmd->n_ssids++; } } else { type = SCAN_SSID_FILTER_LIST; /* Add all SSIDs from the filters */ for (i = 0; i < req->n_match_sets; i++) { /* ignore sets without SSIDs */ if (!sets[i].ssid.ssid_len) continue; cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC; cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len; memcpy(cmd->ssids[cmd->n_ssids].ssid, sets[i].ssid.ssid, sets[i].ssid.ssid_len); cmd->n_ssids++; } if ((req->n_ssids > 1) || (req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) { /* * Mark all the SSIDs passed in the SSID list as HIDDEN, * so they're used in probe requests. */ for (i = 0; i < req->n_ssids; i++) { if (!req->ssids[i].ssid_len) continue; for (j = 0; j < cmd->n_ssids; j++) if ((req->ssids[i].ssid_len == cmd->ssids[j].len) && !memcmp(req->ssids[i].ssid, cmd->ssids[j].ssid, req->ssids[i].ssid_len)) { cmd->ssids[j].type = SCAN_SSID_TYPE_HIDDEN; break; } /* Fail if SSID isn't present in the filters */ if (j == cmd->n_ssids) { ret = -EINVAL; goto out_free; } } } } wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd)); ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("cmd sched scan ssid list failed"); goto out_free; } out_free: kfree(cmd); out: if (ret < 0) return ret; return type; } int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies) { struct wl1271_cmd_sched_scan_config *cfg = NULL; struct conf_sched_scan_settings *c = &wl->conf.sched_scan; int i, ret; bool force_passive = !req->n_ssids; wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); if (!cfg) return -ENOMEM; cfg->role_id = wlvif->dev_role_id; cfg->rssi_threshold = c->rssi_threshold; cfg->snr_threshold = c->snr_threshold; cfg->n_probe_reqs = c->num_probe_reqs; /* cycles set to 0 it means infinite (until manually stopped) */ cfg->cycles = 0; /* report APs when at least 1 is found */ cfg->report_after = 1; /* don't stop scanning automatically when something is found */ cfg->terminate = 0; cfg->tag = WL1271_SCAN_DEFAULT_TAG; /* don't filter on BSS type */ cfg->bss_type = SCAN_BSS_TYPE_ANY; /* currently NL80211 supports only a single interval */ for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) cfg->intervals[i] = cpu_to_le32(req->interval); cfg->ssid_len = 0; ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req); if (ret < 0) goto out; cfg->filter_type = ret; wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) { wl1271_error("scan channel list is empty"); ret = -EINVAL; goto out; } if (!force_passive && cfg->active[0]) { u8 band = IEEE80211_BAND_2GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, wlvif->dev_role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, ies->ie[band], ies->len[band], true); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); goto out; } } if (!force_passive && cfg->active[1]) { u8 band = IEEE80211_BAND_5GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, wlvif->dev_role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, ies->ie[band], ies->len[band], true); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); goto out; } } wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, sizeof(*cfg), 0); if (ret < 0) { wl1271_error("SCAN configuration failed"); goto out; } out: kfree(cfg); return ret; } int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl1271_cmd_sched_scan_start *start; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); if (wlvif->bss_type != BSS_TYPE_STA_BSS) return -EOPNOTSUPP; if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) return -EBUSY; start = kzalloc(sizeof(*start), GFP_KERNEL); if (!start) return -ENOMEM; start->role_id = wlvif->dev_role_id; start->tag = WL1271_SCAN_DEFAULT_TAG; ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, sizeof(*start), 0); if (ret < 0) { wl1271_error("failed to send scan start command"); goto out_free; } out_free: kfree(start); return ret; } void wl1271_scan_sched_scan_results(struct wl1271 *wl) { wl1271_debug(DEBUG_SCAN, "got periodic scan results"); ieee80211_sched_scan_results(wl->hw); } void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl1271_cmd_sched_scan_stop *stop; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); /* FIXME: what to do if alloc'ing to stop fails? */ stop = kzalloc(sizeof(*stop), GFP_KERNEL); if (!stop) { wl1271_error("failed to alloc memory to send sched scan stop"); return; } stop->role_id = wlvif->dev_role_id; stop->tag = WL1271_SCAN_DEFAULT_TAG; ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, sizeof(*stop), 0); if (ret < 0) { wl1271_error("failed to send sched scan stop command"); goto out_free; } out_free: kfree(stop); } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/rx.h0000644000175000017500000001017612026211315023177 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __RX_H__ #define __RX_H__ #include #define WL1271_RX_MAX_RSSI -30 #define WL1271_RX_MIN_RSSI -95 #define SHORT_PREAMBLE_BIT BIT(0) #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) #define PLCP_HEADER_LENGTH 8 #define RX_DESC_PACKETID_SHIFT 11 #define RX_MAX_PACKET_ID 3 #define RX_DESC_VALID_FCS 0x0001 #define RX_DESC_MATCH_RXADDR1 0x0002 #define RX_DESC_MCAST 0x0004 #define RX_DESC_STAINTIM 0x0008 #define RX_DESC_VIRTUAL_BM 0x0010 #define RX_DESC_BCAST 0x0020 #define RX_DESC_MATCH_SSID 0x0040 #define RX_DESC_MATCH_BSSID 0x0080 #define RX_DESC_ENCRYPTION_MASK 0x0300 #define RX_DESC_MEASURMENT 0x0400 #define RX_DESC_SEQNUM_MASK 0x1800 #define RX_DESC_MIC_FAIL 0x2000 #define RX_DESC_DECRYPT_FAIL 0x4000 /* * RX Descriptor flags: * * Bits 0-1 - band * Bit 2 - STBC * Bit 3 - A-MPDU * Bit 4 - HT * Bits 5-7 - encryption */ #define WL1271_RX_DESC_BAND_MASK 0x03 #define WL1271_RX_DESC_ENCRYPT_MASK 0xE0 #define WL1271_RX_DESC_BAND_BG 0x00 #define WL1271_RX_DESC_BAND_J 0x01 #define WL1271_RX_DESC_BAND_A 0x02 #define WL1271_RX_DESC_STBC BIT(2) #define WL1271_RX_DESC_A_MPDU BIT(3) #define WL1271_RX_DESC_HT BIT(4) #define WL1271_RX_DESC_ENCRYPT_WEP 0x20 #define WL1271_RX_DESC_ENCRYPT_TKIP 0x40 #define WL1271_RX_DESC_ENCRYPT_AES 0x60 #define WL1271_RX_DESC_ENCRYPT_GEM 0x80 /* * RX Descriptor status * * Bits 0-2 - error code * Bits 3-5 - process_id tag (AP mode FW) * Bits 6-7 - reserved */ #define WL1271_RX_DESC_STATUS_MASK 0x03 #define WL1271_RX_DESC_SUCCESS 0x00 #define WL1271_RX_DESC_DECRYPT_FAIL 0x01 #define WL1271_RX_DESC_MIC_FAIL 0x02 #define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03 #define RX_MEM_BLOCK_MASK 0xFF #define RX_BUF_SIZE_MASK 0xFFF00 #define RX_BUF_SIZE_SHIFT_DIV 6 #define ALIGNED_RX_BUF_SIZE_MASK 0xFFFF00 #define ALIGNED_RX_BUF_SIZE_SHIFT 8 /* If set, the start of IP payload is not 4 bytes aligned */ #define RX_BUF_UNALIGNED_PAYLOAD BIT(20) /* If set, the buffer was padded by the FW to be 4 bytes aligned */ #define RX_BUF_PADDED_PAYLOAD BIT(30) /* * Account for the padding inserted by the FW in case of RX_ALIGNMENT * or for fixing alignment in case the packet wasn't aligned. */ #define RX_BUF_ALIGN 2 /* Describes the alignment state of a Rx buffer */ enum wl_rx_buf_align { WLCORE_RX_BUF_ALIGNED, WLCORE_RX_BUF_UNALIGNED, WLCORE_RX_BUF_PADDED, }; enum { WL12XX_RX_CLASS_UNKNOWN, WL12XX_RX_CLASS_MANAGEMENT, WL12XX_RX_CLASS_DATA, WL12XX_RX_CLASS_QOS_DATA, WL12XX_RX_CLASS_BCN_PRBRSP, WL12XX_RX_CLASS_EAPOL, WL12XX_RX_CLASS_BA_EVENT, WL12XX_RX_CLASS_AMSDU, WL12XX_RX_CLASS_LOGGER, }; struct wl1271_rx_descriptor { __le16 length; u8 status; u8 flags; u8 rate; u8 channel; s8 rssi; u8 snr; __le32 timestamp; u8 packet_class; u8 hlid; u8 pad_len; u8 reserved; } __packed; int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); int wl1271_rx_filter_enable(struct wl1271 *wl, int index, bool enable, struct wl12xx_rx_filter *filter); int wl1271_rx_filter_clear_all(struct wl1271 *wl); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/rx.c0000644000175000017500000002132212026211315023165 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include "wlcore.h" #include "debug.h" #include "acx.h" #include "rx.h" #include "tx.h" #include "io.h" #include "hw_ops.h" /* * TODO: this is here just for now, it must be removed when the data * operations are in place. */ #include "../wl12xx/reg.h" static u32 wlcore_rx_get_buf_size(struct wl1271 *wl, u32 rx_pkt_desc) { if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN) return (rx_pkt_desc & ALIGNED_RX_BUF_SIZE_MASK) >> ALIGNED_RX_BUF_SIZE_SHIFT; return (rx_pkt_desc & RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV; } static u32 wlcore_rx_get_align_buf_size(struct wl1271 *wl, u32 pkt_len) { if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN) return ALIGN(pkt_len, WL12XX_BUS_BLOCK_SIZE); return pkt_len; } static void wl1271_rx_status(struct wl1271 *wl, struct wl1271_rx_descriptor *desc, struct ieee80211_rx_status *status, u8 beacon) { memset(status, 0, sizeof(struct ieee80211_rx_status)); if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG) status->band = IEEE80211_BAND_2GHZ; else status->band = IEEE80211_BAND_5GHZ; status->rate_idx = wlcore_rate_to_idx(wl, desc->rate, status->band); /* 11n support */ if (desc->rate <= wl->hw_min_ht_rate) status->flag |= RX_FLAG_HT; status->signal = desc->rssi; /* * FIXME: In wl1251, the SNR should be divided by two. In wl1271 we * need to divide by two for now, but TI has been discussing about * changing it. This needs to be rechecked. */ wl->noise = desc->rssi - (desc->snr >> 1); status->freq = ieee80211_channel_to_frequency(desc->channel, status->band); if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { u8 desc_err_code = desc->status & WL1271_RX_DESC_STATUS_MASK; status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED | RX_FLAG_DECRYPTED; if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) { status->flag |= RX_FLAG_MMIC_ERROR; wl1271_warning("Michael MIC error"); } } } static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, enum wl_rx_buf_align rx_align, u8 *hlid) { struct wl1271_rx_descriptor *desc; struct sk_buff *skb; struct ieee80211_hdr *hdr; u8 *buf; u8 beacon = 0; u8 is_data = 0; u8 reserved = 0; u16 seq_num; u32 pkt_data_len; /* * In PLT mode we seem to get frames and mac80211 warns about them, * workaround this by not retrieving them at all. */ if (unlikely(wl->plt)) return -EINVAL; pkt_data_len = wlcore_hw_get_rx_packet_len(wl, data, length); if (!pkt_data_len) { wl1271_error("Invalid packet arrived from HW. length %d", length); return -EINVAL; } if (rx_align == WLCORE_RX_BUF_UNALIGNED) reserved = RX_BUF_ALIGN; /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) data; if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) { size_t len = length - sizeof(*desc); wl12xx_copy_fwlog(wl, data + sizeof(*desc), len); wake_up_interruptible(&wl->fwlog_waitq); return 0; } switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { /* discard corrupted packets */ case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: case WL1271_RX_DESC_DECRYPT_FAIL: wl1271_warning("corrupted packet in RX with status: 0x%x", desc->status & WL1271_RX_DESC_STATUS_MASK); return -EINVAL; case WL1271_RX_DESC_SUCCESS: case WL1271_RX_DESC_MIC_FAIL: break; default: wl1271_error("invalid RX descriptor status: 0x%x", desc->status & WL1271_RX_DESC_STATUS_MASK); return -EINVAL; } /* skb length not including rx descriptor */ skb = __dev_alloc_skb(pkt_data_len + reserved, GFP_KERNEL); if (!skb) { wl1271_error("Couldn't allocate RX frame"); return -ENOMEM; } /* reserve the unaligned payload(if any) */ skb_reserve(skb, reserved); buf = skb_put(skb, pkt_data_len); /* * Copy packets from aggregation buffer to the skbs without rx * descriptor and with packet payload aligned care. In case of unaligned * packets copy the packets in offset of 2 bytes guarantee IP header * payload aligned to 4 bytes. */ memcpy(buf, data + sizeof(*desc), pkt_data_len); if (rx_align == WLCORE_RX_BUF_PADDED) skb_pull(skb, RX_BUF_ALIGN); *hlid = desc->hlid; hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_beacon(hdr->frame_control)) beacon = 1; if (ieee80211_is_data_present(hdr->frame_control)) is_data = 1; wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); wlcore_hw_set_rx_csum(wl, desc, skb); seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb, skb->len - desc->pad_len, beacon ? "beacon" : "", seq_num, *hlid); skb_queue_tail(&wl->deferred_rx_queue, skb); queue_work(wl->freezable_wq, &wl->netstack_work); return is_data; } int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) { unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; u32 buf_size; u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc; u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc; u32 rx_counter; u32 pkt_len, align_pkt_len; u32 pkt_offset, des; u8 hlid; enum wl_rx_buf_align rx_align; int ret = 0; while (drv_rx_counter != fw_rx_counter) { buf_size = 0; rx_counter = drv_rx_counter; while (rx_counter != fw_rx_counter) { des = le32_to_cpu(status->rx_pkt_descs[rx_counter]); pkt_len = wlcore_rx_get_buf_size(wl, des); align_pkt_len = wlcore_rx_get_align_buf_size(wl, pkt_len); if (buf_size + align_pkt_len > WL1271_AGGR_BUFFER_SIZE) break; buf_size += align_pkt_len; rx_counter++; rx_counter %= wl->num_rx_desc; } if (buf_size == 0) { wl1271_warning("received empty data"); break; } /* Read all available packets at once */ des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]); ret = wlcore_hw_prepare_read(wl, des, buf_size); if (ret < 0) goto out; ret = wlcore_read_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, buf_size, true); if (ret < 0) goto out; /* Split data into separate packets */ pkt_offset = 0; while (pkt_offset < buf_size) { des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]); pkt_len = wlcore_rx_get_buf_size(wl, des); rx_align = wlcore_hw_get_rx_buf_align(wl, des); /* * the handle data call can only fail in memory-outage * conditions, in that case the received frame will just * be dropped. */ if (wl1271_rx_handle_data(wl, wl->aggr_buf + pkt_offset, pkt_len, rx_align, &hlid) == 1) { if (hlid < WL12XX_MAX_LINKS) __set_bit(hlid, active_hlids); else WARN(1, "hlid exceeded WL12XX_MAX_LINKS " "(%d)\n", hlid); } wl->rx_counter++; drv_rx_counter++; drv_rx_counter %= wl->num_rx_desc; pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len); } } /* * Write the driver's packet counter to the FW. This is only required * for older hardware revisions */ if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) { ret = wlcore_write32(wl, WL12XX_REG_RX_DRIVER_COUNTER, wl->rx_counter); if (ret < 0) goto out; } wl12xx_rearm_rx_streaming(wl, active_hlids); out: return ret; } #ifdef CONFIG_PM int wl1271_rx_filter_enable(struct wl1271 *wl, int index, bool enable, struct wl12xx_rx_filter *filter) { int ret; if (wl->rx_filter_enabled[index] == enable) { wl1271_warning("Request to enable an already " "enabled rx filter %d", index); return 0; } ret = wl1271_acx_set_rx_filter(wl, index, enable, filter); if (ret) { wl1271_error("Failed to %s rx data filter %d (err=%d)", enable ? "enable" : "disable", index, ret); return ret; } wl->rx_filter_enabled[index] = enable; return 0; } int wl1271_rx_filter_clear_all(struct wl1271 *wl) { int i, ret = 0; for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) { if (!wl->rx_filter_enabled[i]) continue; ret = wl1271_rx_filter_enable(wl, i, 0, NULL); if (ret) goto out; } out: return ret; } #endif /* CONFIG_PM */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/ps.h0000644000175000017500000000253512026211315023170 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __PS_H__ #define __PS_H__ #include "wlcore.h" #include "acx.h" int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum wl1271_cmd_ps_mode mode); void wl1271_ps_elp_sleep(struct wl1271 *wl); int wl1271_ps_elp_wakeup(struct wl1271 *wl); void wl1271_elp_work(struct work_struct *work); void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid, bool clean_queues); void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); #define WL1271_PS_COMPLETE_TIMEOUT 500 #endif /* __WL1271_PS_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/ps.c0000644000175000017500000001757712026211315023177 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ps.h" #include "io.h" #include "tx.h" #include "debug.h" #define WL1271_WAKEUP_TIMEOUT 500 #define ELP_ENTRY_DELAY 5 void wl1271_elp_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; struct wl12xx_vif *wlvif; int ret; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, elp_work); wl1271_debug(DEBUG_PSM, "elp work"); mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; /* our work might have been already cancelled */ if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) goto out; if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) goto out; wl12xx_for_each_wlvif(wl, wlvif) { if (wlvif->bss_type == BSS_TYPE_AP_BSS) goto out; if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) goto out; } wl1271_debug(DEBUG_PSM, "chip to elp"); ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_SLEEP); if (ret < 0) { wl12xx_queue_recovery_work(wl); goto out; } set_bit(WL1271_FLAG_IN_ELP, &wl->flags); out: mutex_unlock(&wl->mutex); } /* Routines to toggle sleep mode while in ELP */ void wl1271_ps_elp_sleep(struct wl1271 *wl) { struct wl12xx_vif *wlvif; u32 timeout; if (wl->sleep_auth != WL1271_PSM_ELP) return; /* we shouldn't get consecutive sleep requests */ if (WARN_ON(test_and_set_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) return; wl12xx_for_each_wlvif(wl, wlvif) { if (wlvif->bss_type == BSS_TYPE_AP_BSS) return; if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) return; } if (wl->conf.conn.forced_ps) timeout = ELP_ENTRY_DELAY; else timeout = wl->conf.conn.dynamic_ps_timeout; ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, msecs_to_jiffies(timeout)); } int wl1271_ps_elp_wakeup(struct wl1271 *wl) { DECLARE_COMPLETION_ONSTACK(compl); unsigned long flags; int ret; u32 start_time = jiffies; bool pending = false; /* * we might try to wake up even if we didn't go to sleep * before (e.g. on boot) */ if (!test_and_clear_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)) return 0; /* don't cancel_sync as it might contend for a mutex and deadlock */ cancel_delayed_work(&wl->elp_work); if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) return 0; wl1271_debug(DEBUG_PSM, "waking up chip from elp"); /* * The spinlock is required here to synchronize both the work and * the completion variable in one entity. */ spin_lock_irqsave(&wl->wl_lock, flags); if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) pending = true; else wl->elp_compl = &compl; spin_unlock_irqrestore(&wl->wl_lock, flags); ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); if (ret < 0) { wl12xx_queue_recovery_work(wl); goto err; } if (!pending) { ret = wait_for_completion_timeout( &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); if (ret == 0) { wl1271_error("ELP wakeup timeout!"); wl12xx_queue_recovery_work(wl); ret = -ETIMEDOUT; goto err; } else if (ret < 0) { wl1271_error("ELP wakeup completion error."); goto err; } } clear_bit(WL1271_FLAG_IN_ELP, &wl->flags); wl1271_debug(DEBUG_PSM, "wakeup time: %u ms", jiffies_to_msecs(jiffies - start_time)); goto out; err: spin_lock_irqsave(&wl->wl_lock, flags); wl->elp_compl = NULL; spin_unlock_irqrestore(&wl->wl_lock, flags); return ret; out: return 0; } int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum wl1271_cmd_ps_mode mode) { int ret; u16 timeout = wl->conf.conn.dynamic_ps_timeout; switch (mode) { case STATION_AUTO_PS_MODE: case STATION_POWER_SAVE_MODE: wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", mode, timeout); ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.wake_up_event, wl->conf.conn.listen_interval); if (ret < 0) { wl1271_error("couldn't set wake up conditions"); return ret; } ret = wl1271_cmd_ps_mode(wl, wlvif, mode, timeout); if (ret < 0) return ret; set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); /* * enable beacon early termination. * Not relevant for 5GHz and for high rates. */ if ((wlvif->band == IEEE80211_BAND_2GHZ) && (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) { ret = wl1271_acx_bet_enable(wl, wlvif, true); if (ret < 0) return ret; } break; case STATION_ACTIVE_MODE: wl1271_debug(DEBUG_PSM, "leaving psm"); /* disable beacon early termination */ if ((wlvif->band == IEEE80211_BAND_2GHZ) && (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) { ret = wl1271_acx_bet_enable(wl, wlvif, false); if (ret < 0) return ret; } ret = wl1271_cmd_ps_mode(wl, wlvif, mode, 0); if (ret < 0) return ret; clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); break; default: wl1271_warning("trying to set ps to unsupported mode %d", mode); ret = -EINVAL; } return ret; } static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) { int i; struct sk_buff *skb; struct ieee80211_tx_info *info; unsigned long flags; int filtered[NUM_TX_QUEUES]; /* filter all frames currently in the low level queues for this hlid */ for (i = 0; i < NUM_TX_QUEUES; i++) { filtered[i] = 0; while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { filtered[i]++; if (WARN_ON(wl12xx_is_dummy_packet(wl, skb))) continue; info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_STAT_TX_FILTERED; info->status.rates[0].idx = -1; ieee80211_tx_status_ni(wl->hw, skb); } } spin_lock_irqsave(&wl->wl_lock, flags); for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_queue_count[i] -= filtered[i]; spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); } void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid, bool clean_queues) { struct ieee80211_sta *sta; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); if (test_bit(hlid, &wl->ap_ps_map)) return; wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d " "clean_queues %d", hlid, wl->links[hlid].allocated_pkts, clean_queues); rcu_read_lock(); sta = ieee80211_find_sta(vif, wl->links[hlid].addr); if (!sta) { wl1271_error("could not find sta %pM for starting ps", wl->links[hlid].addr); rcu_read_unlock(); return; } ieee80211_sta_ps_transition_ni(sta, true); rcu_read_unlock(); /* do we want to filter all frames from this link's queues? */ if (clean_queues) wl1271_ps_filter_frames(wl, hlid); __set_bit(hlid, &wl->ap_ps_map); } void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) { struct ieee80211_sta *sta; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); if (!test_bit(hlid, &wl->ap_ps_map)) return; wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid); __clear_bit(hlid, &wl->ap_ps_map); rcu_read_lock(); sta = ieee80211_find_sta(vif, wl->links[hlid].addr); if (!sta) { wl1271_error("could not find sta %pM for ending ps", wl->links[hlid].addr); goto end; } ieee80211_sta_ps_transition_ni(sta, false); end: rcu_read_unlock(); } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/io.h0000644000175000017500000001421512026211315023153 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __IO_H__ #define __IO_H__ #include #define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 #define HW_PARTITION_REGISTERS_ADDR 0x1FFC0 #define HW_PART0_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR) #define HW_PART0_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 4) #define HW_PART1_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 8) #define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12) #define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16) #define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20) #define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24) #define HW_ACCESS_REGISTER_SIZE 4 #define HW_ACCESS_PRAM_MAX_RANGE 0x3c000 struct wl1271; void wlcore_disable_interrupts(struct wl1271 *wl); void wlcore_disable_interrupts_nosync(struct wl1271 *wl); void wlcore_enable_interrupts(struct wl1271 *wl); void wlcore_synchronize_interrupts(struct wl1271 *wl); void wl1271_io_reset(struct wl1271 *wl); void wl1271_io_init(struct wl1271 *wl); int wlcore_translate_addr(struct wl1271 *wl, int addr); /* Raw target IO, address is not translated */ static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed) { int ret; if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags)) return -EIO; ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed); if (ret && wl->state != WL1271_STATE_OFF) set_bit(WL1271_FLAG_IO_FAILED, &wl->flags); return ret; } static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed) { int ret; if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags)) return -EIO; ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed); if (ret && wl->state != WL1271_STATE_OFF) set_bit(WL1271_FLAG_IO_FAILED, &wl->flags); return ret; } static inline int __must_check wlcore_raw_read_data(struct wl1271 *wl, int reg, void *buf, size_t len, bool fixed) { return wlcore_raw_read(wl, wl->rtable[reg], buf, len, fixed); } static inline int __must_check wlcore_raw_write_data(struct wl1271 *wl, int reg, void *buf, size_t len, bool fixed) { return wlcore_raw_write(wl, wl->rtable[reg], buf, len, fixed); } static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, u32 *val) { int ret; ret = wlcore_raw_read(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32), false); if (ret < 0) return ret; if (val) *val = le32_to_cpu(wl->buffer_32); return 0; } static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr, u32 val) { wl->buffer_32 = cpu_to_le32(val); return wlcore_raw_write(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32), false); } static inline int __must_check wlcore_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed) { int physical; physical = wlcore_translate_addr(wl, addr); return wlcore_raw_read(wl, physical, buf, len, fixed); } static inline int __must_check wlcore_write(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed) { int physical; physical = wlcore_translate_addr(wl, addr); return wlcore_raw_write(wl, physical, buf, len, fixed); } static inline int __must_check wlcore_write_data(struct wl1271 *wl, int reg, void *buf, size_t len, bool fixed) { return wlcore_write(wl, wl->rtable[reg], buf, len, fixed); } static inline int __must_check wlcore_read_data(struct wl1271 *wl, int reg, void *buf, size_t len, bool fixed) { return wlcore_read(wl, wl->rtable[reg], buf, len, fixed); } static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr, void *buf, size_t len, bool fixed) { int physical; int addr; /* Addresses are stored internally as addresses to 32 bytes blocks */ addr = hwaddr << 5; physical = wlcore_translate_addr(wl, addr); return wlcore_raw_read(wl, physical, buf, len, fixed); } static inline int __must_check wlcore_read32(struct wl1271 *wl, int addr, u32 *val) { return wlcore_raw_read32(wl, wlcore_translate_addr(wl, addr), val); } static inline int __must_check wlcore_write32(struct wl1271 *wl, int addr, u32 val) { return wlcore_raw_write32(wl, wlcore_translate_addr(wl, addr), val); } static inline int __must_check wlcore_read_reg(struct wl1271 *wl, int reg, u32 *val) { return wlcore_raw_read32(wl, wlcore_translate_addr(wl, wl->rtable[reg]), val); } static inline int __must_check wlcore_write_reg(struct wl1271 *wl, int reg, u32 val) { return wlcore_raw_write32(wl, wlcore_translate_addr(wl, wl->rtable[reg]), val); } static inline void wl1271_power_off(struct wl1271 *wl) { int ret; if (!test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags)) return; ret = wl->if_ops->power(wl->dev, false); if (!ret) clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); } static inline int wl1271_power_on(struct wl1271 *wl) { int ret = wl->if_ops->power(wl->dev, true); if (ret == 0) set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); return ret; } int wlcore_set_partition(struct wl1271 *wl, const struct wlcore_partition_set *p); bool wl1271_set_block_size(struct wl1271 *wl); /* Functions from wl1271_main.c */ int wl1271_tx_dummy_packet(struct wl1271 *wl); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/io.c0000644000175000017500000001362712026211315023154 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include "wlcore.h" #include "debug.h" #include "wl12xx_80211.h" #include "io.h" #include "tx.h" bool wl1271_set_block_size(struct wl1271 *wl) { if (wl->if_ops->set_block_size) { wl->if_ops->set_block_size(wl->dev, WL12XX_BUS_BLOCK_SIZE); return true; } return false; } void wlcore_disable_interrupts(struct wl1271 *wl) { disable_irq(wl->irq); } EXPORT_SYMBOL_GPL(wlcore_disable_interrupts); void wlcore_disable_interrupts_nosync(struct wl1271 *wl) { disable_irq_nosync(wl->irq); } EXPORT_SYMBOL_GPL(wlcore_disable_interrupts_nosync); void wlcore_enable_interrupts(struct wl1271 *wl) { enable_irq(wl->irq); } EXPORT_SYMBOL_GPL(wlcore_enable_interrupts); void wlcore_synchronize_interrupts(struct wl1271 *wl) { synchronize_irq(wl->irq); } EXPORT_SYMBOL_GPL(wlcore_synchronize_interrupts); int wlcore_translate_addr(struct wl1271 *wl, int addr) { struct wlcore_partition_set *part = &wl->curr_part; /* * To translate, first check to which window of addresses the * particular address belongs. Then subtract the starting address * of that window from the address. Then, add offset of the * translated region. * * The translated regions occur next to each other in physical device * memory, so just add the sizes of the preceding address regions to * get the offset to the new region. */ if ((addr >= part->mem.start) && (addr < part->mem.start + part->mem.size)) return addr - part->mem.start; else if ((addr >= part->reg.start) && (addr < part->reg.start + part->reg.size)) return addr - part->reg.start + part->mem.size; else if ((addr >= part->mem2.start) && (addr < part->mem2.start + part->mem2.size)) return addr - part->mem2.start + part->mem.size + part->reg.size; else if ((addr >= part->mem3.start) && (addr < part->mem3.start + part->mem3.size)) return addr - part->mem3.start + part->mem.size + part->reg.size + part->mem2.size; WARN(1, "HW address 0x%x out of range", addr); return 0; } EXPORT_SYMBOL_GPL(wlcore_translate_addr); /* Set the partitions to access the chip addresses * * To simplify driver code, a fixed (virtual) memory map is defined for * register and memory addresses. Because in the chipset, in different stages * of operation, those addresses will move around, an address translation * mechanism is required. * * There are four partitions (three memory and one register partition), * which are mapped to two different areas of the hardware memory. * * Virtual address * space * * | | * ...+----+--> mem.start * Physical address ... | | * space ... | | [PART_0] * ... | | * 00000000 <--+----+... ...+----+--> mem.start + mem.size * | | ... | | * |MEM | ... | | * | | ... | | * mem.size <--+----+... | | {unused area) * | | ... | | * |REG | ... | | * mem.size | | ... | | * + <--+----+... ...+----+--> reg.start * reg.size | | ... | | * |MEM2| ... | | [PART_1] * | | ... | | * ...+----+--> reg.start + reg.size * | | * */ int wlcore_set_partition(struct wl1271 *wl, const struct wlcore_partition_set *p) { int ret; /* copy partition info */ memcpy(&wl->curr_part, p, sizeof(*p)); wl1271_debug(DEBUG_IO, "mem_start %08X mem_size %08X", p->mem.start, p->mem.size); wl1271_debug(DEBUG_IO, "reg_start %08X reg_size %08X", p->reg.start, p->reg.size); wl1271_debug(DEBUG_IO, "mem2_start %08X mem2_size %08X", p->mem2.start, p->mem2.size); wl1271_debug(DEBUG_IO, "mem3_start %08X mem3_size %08X", p->mem3.start, p->mem3.size); ret = wlcore_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); if (ret < 0) goto out; ret = wlcore_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); if (ret < 0) goto out; ret = wlcore_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); if (ret < 0) goto out; ret = wlcore_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); if (ret < 0) goto out; ret = wlcore_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); if (ret < 0) goto out; ret = wlcore_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); if (ret < 0) goto out; /* * We don't need the size of the last partition, as it is * automatically calculated based on the total memory size and * the sizes of the previous partitions. */ ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); out: return ret; } EXPORT_SYMBOL_GPL(wlcore_set_partition); void wl1271_io_reset(struct wl1271 *wl) { if (wl->if_ops->reset) wl->if_ops->reset(wl->dev); } void wl1271_io_init(struct wl1271 *wl) { if (wl->if_ops->init) wl->if_ops->init(wl->dev); } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/init.h0000644000175000017500000000253312026211315023507 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __INIT_H__ #define __INIT_H__ #include "wlcore.h" int wl1271_hw_init_power_auth(struct wl1271 *wl); int wl1271_init_templates_config(struct wl1271 *wl); int wl1271_init_pta(struct wl1271 *wl); int wl1271_init_energy_detection(struct wl1271 *wl); int wl1271_chip_specific_init(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl); int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif); int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/init.c0000644000175000017500000004174212026211315023507 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include "debug.h" #include "init.h" #include "wl12xx_80211.h" #include "acx.h" #include "cmd.h" #include "tx.h" #include "io.h" #include "hw_ops.h" int wl1271_init_templates_config(struct wl1271 *wl) { int ret, i; size_t max_size; /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_CFG_PROBE_REQ_5, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) { ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_APP_PROBE_REQ_2_4, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_APP_PROBE_REQ_5, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; } ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_NULL_DATA, NULL, sizeof(struct wl12xx_null_data_template), 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_PS_POLL, NULL, sizeof(struct wl12xx_ps_poll_template), 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_QOS_NULL_DATA, NULL, sizeof (struct ieee80211_qos_hdr), 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_PROBE_RESPONSE, NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_BEACON, NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; max_size = sizeof(struct wl12xx_arp_rsp_template) + WL1271_EXTRA_SPACE_MAX; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_ARP_RSP, NULL, max_size, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; /* * Put very large empty placeholders for all templates. These * reserve memory for later. */ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_AP_PROBE_RESPONSE, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_AP_BEACON, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_DEAUTH_AP, NULL, sizeof (struct wl12xx_disconn_template), 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_KLV, NULL, sizeof(struct ieee80211_qos_hdr), i, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; } return 0; } static int wl1271_ap_init_deauth_template(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_disconn_template *tmpl; int ret; u32 rate; tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL); if (!tmpl) { ret = -ENOMEM; goto out; } tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_DEAUTH_AP, tmpl, sizeof(*tmpl), 0, rate); out: kfree(tmpl); return ret; } static int wl1271_ap_init_null_template(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct ieee80211_hdr_3addr *nullfunc; int ret; u32 rate; nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL); if (!nullfunc) { ret = -ENOMEM; goto out; } nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_FROMDS); /* nullfunc->addr1 is filled by FW */ memcpy(nullfunc->addr2, vif->addr, ETH_ALEN); memcpy(nullfunc->addr3, vif->addr, ETH_ALEN); rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_NULL_DATA, nullfunc, sizeof(*nullfunc), 0, rate); out: kfree(nullfunc); return ret; } static int wl1271_ap_init_qos_null_template(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct ieee80211_qos_hdr *qosnull; int ret; u32 rate; qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL); if (!qosnull) { ret = -ENOMEM; goto out; } qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC | IEEE80211_FCTL_FROMDS); /* qosnull->addr1 is filled by FW */ memcpy(qosnull->addr2, vif->addr, ETH_ALEN); memcpy(qosnull->addr3, vif->addr, ETH_ALEN); rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_QOS_NULL_DATA, qosnull, sizeof(*qosnull), 0, rate); out: kfree(qosnull); return ret; } static int wl12xx_init_rx_config(struct wl1271 *wl) { int ret; ret = wl1271_acx_rx_msdu_life_time(wl); if (ret < 0) return ret; return 0; } static int wl12xx_init_phy_vif_config(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; ret = wl1271_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME); if (ret < 0) return ret; ret = wl1271_acx_service_period_timeout(wl, wlvif); if (ret < 0) return ret; ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold); if (ret < 0) return ret; return 0; } static int wl1271_init_sta_beacon_filter(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; ret = wl1271_acx_beacon_filter_table(wl, wlvif); if (ret < 0) return ret; /* enable beacon filtering */ ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); if (ret < 0) return ret; return 0; } int wl1271_init_pta(struct wl1271 *wl) { int ret; ret = wl12xx_acx_sg_cfg(wl); if (ret < 0) return ret; ret = wl1271_acx_sg_enable(wl, wl->sg_enabled); if (ret < 0) return ret; return 0; } int wl1271_init_energy_detection(struct wl1271 *wl) { int ret; ret = wl1271_acx_cca_threshold(wl); if (ret < 0) return ret; return 0; } static int wl1271_init_beacon_broadcast(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; ret = wl1271_acx_bcn_dtim_options(wl, wlvif); if (ret < 0) return ret; return 0; } static int wl12xx_init_fwlog(struct wl1271 *wl) { int ret; if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) return 0; ret = wl12xx_cmd_config_fwlog(wl); if (ret < 0) return ret; return 0; } /* generic sta initialization (non vif-specific) */ static int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; /* PS config */ ret = wl12xx_acx_config_ps(wl, wlvif); if (ret < 0) return ret; /* FM WLAN coexistence */ ret = wl1271_acx_fm_coex(wl); if (ret < 0) return ret; ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) return ret; return 0; } static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret, i; /* disable all keep-alive templates */ for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { ret = wl1271_acx_keep_alive_config(wl, wlvif, i, ACX_KEEP_ALIVE_TPL_INVALID); if (ret < 0) return ret; } /* disable the keep-alive feature */ ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); if (ret < 0) return ret; return 0; } /* generic ap initialization (non vif-specific) */ static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; ret = wl1271_init_ap_rates(wl, wlvif); if (ret < 0) return ret; return 0; } int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; ret = wl1271_ap_init_deauth_template(wl, wlvif); if (ret < 0) return ret; ret = wl1271_ap_init_null_template(wl, vif); if (ret < 0) return ret; ret = wl1271_ap_init_qos_null_template(wl, vif); if (ret < 0) return ret; /* * when operating as AP we want to receive external beacons for * configuring ERP protection. */ ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); if (ret < 0) return ret; return 0; } static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl, struct ieee80211_vif *vif) { return wl1271_ap_init_templates(wl, vif); } int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int i, ret; struct conf_tx_rate_class rc; u32 supported_rates; wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", wlvif->basic_rate_set); if (wlvif->basic_rate_set == 0) return -EINVAL; rc.enabled_rates = wlvif->basic_rate_set; rc.long_retry_limit = 10; rc.short_retry_limit = 10; rc.aflags = 0; ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx); if (ret < 0) return ret; /* use the min basic rate for AP broadcast/multicast */ rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); rc.short_retry_limit = 10; rc.long_retry_limit = 10; rc.aflags = 0; ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx); if (ret < 0) return ret; /* * If the basic rates contain OFDM rates, use OFDM only * rates for unicast TX as well. Else use all supported rates. */ if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) supported_rates = CONF_TX_OFDM_RATES; else supported_rates = CONF_TX_AP_ENABLED_RATES; /* unconditionally enable HT rates */ supported_rates |= CONF_TX_MCS_RATES; /* get extra MIMO or wide-chan rates where the HW supports it */ supported_rates |= wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); /* configure unicast TX rate classes */ for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { rc.enabled_rates = supported_rates; rc.short_retry_limit = 10; rc.long_retry_limit = 10; rc.aflags = 0; ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.ucast_rate_idx[i]); if (ret < 0) return ret; } return 0; } static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif) { /* Reset the BA RX indicators */ wlvif->ba_allowed = true; wl->ba_rx_session_count = 0; /* BA is supported in STA/AP modes */ if (wlvif->bss_type != BSS_TYPE_AP_BSS && wlvif->bss_type != BSS_TYPE_STA_BSS) { wlvif->ba_support = false; return 0; } wlvif->ba_support = true; /* 802.11n initiator BA session setting */ return wl12xx_acx_set_ba_initiator_policy(wl, wlvif); } /* vif-specifc initialization */ static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; ret = wl1271_acx_group_address_tbl(wl, wlvif, true, NULL, 0); if (ret < 0) return ret; /* Initialize connection monitoring thresholds */ ret = wl1271_acx_conn_monit_params(wl, wlvif, false); if (ret < 0) return ret; /* Beacon filtering */ ret = wl1271_init_sta_beacon_filter(wl, wlvif); if (ret < 0) return ret; /* Beacons and broadcast settings */ ret = wl1271_init_beacon_broadcast(wl, wlvif); if (ret < 0) return ret; /* Configure rssi/snr averaging weights */ ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif); if (ret < 0) return ret; return 0; } /* vif-specific intialization */ static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; ret = wl1271_acx_ap_max_tx_retry(wl, wlvif); if (ret < 0) return ret; /* initialize Tx power */ ret = wl1271_acx_tx_power(wl, wlvif, wlvif->power_level); if (ret < 0) return ret; return 0; } int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct conf_tx_ac_category *conf_ac; struct conf_tx_tid *conf_tid; bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret, i; /* consider all existing roles before configuring psm. */ if (wl->ap_count == 0 && is_ap) { /* first AP */ /* Configure for power always on */ ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); if (ret < 0) return ret; /* first STA, no APs */ } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) { u8 sta_auth = wl->conf.conn.sta_sleep_auth; /* Configure for power according to debugfs */ if (sta_auth != WL1271_PSM_ILLEGAL) ret = wl1271_acx_sleep_auth(wl, sta_auth); /* Configure for power always on */ else if (wl->quirks & WLCORE_QUIRK_NO_ELP) ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); /* Configure for ELP power saving */ else ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); if (ret < 0) return ret; } /* Mode specific init */ if (is_ap) { ret = wl1271_ap_hw_init(wl, wlvif); if (ret < 0) return ret; ret = wl12xx_init_ap_role(wl, wlvif); if (ret < 0) return ret; } else { ret = wl1271_sta_hw_init(wl, wlvif); if (ret < 0) return ret; ret = wl12xx_init_sta_role(wl, wlvif); if (ret < 0) return ret; } wl12xx_init_phy_vif_config(wl, wlvif); /* Default TID/AC configuration */ BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count); for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { conf_ac = &wl->conf.tx.ac_conf[i]; ret = wl1271_acx_ac_cfg(wl, wlvif, conf_ac->ac, conf_ac->cw_min, conf_ac->cw_max, conf_ac->aifsn, conf_ac->tx_op_limit); if (ret < 0) return ret; conf_tid = &wl->conf.tx.tid_conf[i]; ret = wl1271_acx_tid_cfg(wl, wlvif, conf_tid->queue_id, conf_tid->channel_type, conf_tid->tsid, conf_tid->ps_scheme, conf_tid->ack_policy, conf_tid->apsd_conf[0], conf_tid->apsd_conf[1]); if (ret < 0) return ret; } /* Configure HW encryption */ ret = wl1271_acx_feature_cfg(wl, wlvif); if (ret < 0) return ret; /* Mode specific init - post mem init */ if (is_ap) ret = wl1271_ap_hw_init_post_mem(wl, vif); else ret = wl1271_sta_hw_init_post_mem(wl, vif); if (ret < 0) return ret; /* Configure initiator BA sessions policies */ ret = wl1271_set_ba_policies(wl, wlvif); if (ret < 0) return ret; ret = wlcore_hw_init_vif(wl, wlvif); if (ret < 0) return ret; return 0; } int wl1271_hw_init(struct wl1271 *wl) { int ret; /* Chip-specific hw init */ ret = wl->ops->hw_init(wl); if (ret < 0) return ret; /* Init templates */ ret = wl1271_init_templates_config(wl); if (ret < 0) return ret; ret = wl12xx_acx_mem_cfg(wl); if (ret < 0) return ret; /* Configure the FW logger */ ret = wl12xx_init_fwlog(wl); if (ret < 0) return ret; /* Bluetooth WLAN coexistence */ ret = wl1271_init_pta(wl); if (ret < 0) return ret; /* Default memory configuration */ ret = wl1271_acx_init_mem_config(wl); if (ret < 0) return ret; /* RX config */ ret = wl12xx_init_rx_config(wl); if (ret < 0) goto out_free_memmap; ret = wl1271_acx_dco_itrim_params(wl); if (ret < 0) goto out_free_memmap; /* Configure TX patch complete interrupt behavior */ ret = wl1271_acx_tx_config_options(wl); if (ret < 0) goto out_free_memmap; /* RX complete interrupt pacing */ ret = wl1271_acx_init_rx_interrupt(wl); if (ret < 0) goto out_free_memmap; /* Energy detection */ ret = wl1271_init_energy_detection(wl); if (ret < 0) goto out_free_memmap; /* Default fragmentation threshold */ ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold); if (ret < 0) goto out_free_memmap; /* Enable data path */ ret = wl1271_cmd_data_path(wl, 1); if (ret < 0) goto out_free_memmap; /* configure PM */ ret = wl1271_acx_pm_config(wl); if (ret < 0) goto out_free_memmap; ret = wl12xx_acx_set_rate_mgmt_params(wl); if (ret < 0) goto out_free_memmap; /* configure hangover */ ret = wl12xx_acx_config_hangover(wl); if (ret < 0) goto out_free_memmap; return 0; out_free_memmap: kfree(wl->target_mem_map); wl->target_mem_map = NULL; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/ini.h0000644000175000017500000001665412026211315023334 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __INI_H__ #define __INI_H__ #define GENERAL_SETTINGS_DRPW_LPD 0xc0 #define SCRATCH_ENABLE_LPD BIT(25) #define WL1271_INI_MAX_SMART_REFLEX_PARAM 16 struct wl1271_ini_general_params { u8 ref_clock; u8 settling_time; u8 clk_valid_on_wakeup; u8 dc2dc_mode; u8 dual_mode_select; u8 tx_bip_fem_auto_detect; u8 tx_bip_fem_manufacturer; u8 general_settings; u8 sr_state; u8 srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM]; u8 srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM]; u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM]; } __packed; #define WL128X_INI_MAX_SETTINGS_PARAM 4 struct wl128x_ini_general_params { u8 ref_clock; u8 settling_time; u8 clk_valid_on_wakeup; u8 tcxo_ref_clock; u8 tcxo_settling_time; u8 tcxo_valid_on_wakeup; u8 tcxo_ldo_voltage; u8 xtal_itrim_val; u8 platform_conf; u8 dual_mode_select; u8 tx_bip_fem_auto_detect; u8 tx_bip_fem_manufacturer; u8 general_settings[WL128X_INI_MAX_SETTINGS_PARAM]; u8 sr_state; u8 srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM]; u8 srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM]; u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM]; } __packed; #define WL1271_INI_RSSI_PROCESS_COMPENS_SIZE 15 struct wl1271_ini_band_params_2 { u8 rx_trace_insertion_loss; u8 tx_trace_loss; u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; } __packed; #define WL1271_INI_CHANNEL_COUNT_2 14 struct wl128x_ini_band_params_2 { u8 rx_trace_insertion_loss; u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_2]; u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; } __packed; #define WL1271_INI_RATE_GROUP_COUNT 6 struct wl1271_ini_fem_params_2 { __le16 tx_bip_ref_pd_voltage; u8 tx_bip_ref_power; u8 tx_bip_ref_offset; u8 tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2]; u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2]; u8 tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_ibias[WL1271_INI_RATE_GROUP_COUNT]; u8 rx_fem_insertion_loss; u8 degraded_low_to_normal_thr; u8 normal_to_degraded_high_thr; } __packed; #define WL128X_INI_RATE_GROUP_COUNT 7 /* low and high temperatures */ #define WL128X_INI_PD_VS_TEMPERATURE_RANGES 2 struct wl128x_ini_fem_params_2 { __le16 tx_bip_ref_pd_voltage; u8 tx_bip_ref_power; u8 tx_bip_ref_offset; u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2]; u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2]; u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT + 1]; u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_2]; u8 tx_pd_vs_temperature[WL128X_INI_PD_VS_TEMPERATURE_RANGES]; u8 rx_fem_insertion_loss; u8 degraded_low_to_normal_thr; u8 normal_to_degraded_high_thr; } __packed; #define WL1271_INI_CHANNEL_COUNT_5 35 #define WL1271_INI_SUB_BAND_COUNT_5 7 struct wl1271_ini_band_params_5 { u8 rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_trace_loss[WL1271_INI_SUB_BAND_COUNT_5]; u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; } __packed; struct wl128x_ini_band_params_5 { u8 rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_5]; u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; } __packed; struct wl1271_ini_fem_params_5 { __le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5]; u8 tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT]; u8 tx_ibias[WL1271_INI_RATE_GROUP_COUNT]; u8 rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; u8 degraded_low_to_normal_thr; u8 normal_to_degraded_high_thr; } __packed; struct wl128x_ini_fem_params_5 { __le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5]; u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5]; u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT]; u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_5]; u8 tx_pd_vs_temperature[WL1271_INI_SUB_BAND_COUNT_5 * WL128X_INI_PD_VS_TEMPERATURE_RANGES]; u8 rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; u8 degraded_low_to_normal_thr; u8 normal_to_degraded_high_thr; } __packed; /* NVS data structure */ #define WL1271_INI_NVS_SECTION_SIZE 468 /* We have four FEM module types: 0-RFMD, 1-TQS, 2-SKW, 3-TQS_HP */ #define WL1271_INI_FEM_MODULE_COUNT 4 /* * In NVS we only store two FEM module entries - * FEM modules 0,2,3 are stored in entry 0 * FEM module 1 is stored in entry 1 */ #define WL12XX_NVS_FEM_MODULE_COUNT 2 #define WL12XX_FEM_TO_NVS_ENTRY(ini_fem_module) \ ((ini_fem_module) == 1 ? 1 : 0) #define WL1271_INI_LEGACY_NVS_FILE_SIZE 800 struct wl1271_nvs_file { /* NVS section - must be first! */ u8 nvs[WL1271_INI_NVS_SECTION_SIZE]; /* INI section */ struct wl1271_ini_general_params general_params; u8 padding1; struct wl1271_ini_band_params_2 stat_radio_params_2; u8 padding2; struct { struct wl1271_ini_fem_params_2 params; u8 padding; } dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT]; struct wl1271_ini_band_params_5 stat_radio_params_5; u8 padding3; struct { struct wl1271_ini_fem_params_5 params; u8 padding; } dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT]; } __packed; struct wl128x_nvs_file { /* NVS section - must be first! */ u8 nvs[WL1271_INI_NVS_SECTION_SIZE]; /* INI section */ struct wl128x_ini_general_params general_params; u8 fem_vendor_and_options; struct wl128x_ini_band_params_2 stat_radio_params_2; u8 padding2; struct { struct wl128x_ini_fem_params_2 params; u8 padding; } dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT]; struct wl128x_ini_band_params_5 stat_radio_params_5; u8 padding3; struct { struct wl128x_ini_fem_params_5 params; u8 padding; } dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT]; } __packed; #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/hw_ops.h0000644000175000017500000001073412026211315024045 0ustar mcgrofmcgrof/* * This file is part of wlcore * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WLCORE_HW_OPS_H__ #define __WLCORE_HW_OPS_H__ #include "wlcore.h" #include "rx.h" static inline u32 wlcore_hw_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) { if (!wl->ops->calc_tx_blocks) BUG_ON(1); return wl->ops->calc_tx_blocks(wl, len, spare_blks); } static inline void wlcore_hw_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, u32 blks, u32 spare_blks) { if (!wl->ops->set_tx_desc_blocks) BUG_ON(1); return wl->ops->set_tx_desc_blocks(wl, desc, blks, spare_blks); } static inline void wlcore_hw_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) { if (!wl->ops->set_tx_desc_data_len) BUG_ON(1); wl->ops->set_tx_desc_data_len(wl, desc, skb); } static inline enum wl_rx_buf_align wlcore_hw_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) { if (!wl->ops->get_rx_buf_align) BUG_ON(1); return wl->ops->get_rx_buf_align(wl, rx_desc); } static inline int wlcore_hw_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) { if (wl->ops->prepare_read) return wl->ops->prepare_read(wl, rx_desc, len); return 0; } static inline u32 wlcore_hw_get_rx_packet_len(struct wl1271 *wl, void *rx_data, u32 data_len) { if (!wl->ops->get_rx_packet_len) BUG_ON(1); return wl->ops->get_rx_packet_len(wl, rx_data, data_len); } static inline int wlcore_hw_tx_delayed_compl(struct wl1271 *wl) { if (wl->ops->tx_delayed_compl) return wl->ops->tx_delayed_compl(wl); return 0; } static inline void wlcore_hw_tx_immediate_compl(struct wl1271 *wl) { if (wl->ops->tx_immediate_compl) wl->ops->tx_immediate_compl(wl); } static inline int wlcore_hw_init_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif) { if (wl->ops->init_vif) return wl->ops->init_vif(wl, wlvif); return 0; } static inline u32 wlcore_hw_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { if (!wl->ops->sta_get_ap_rate_mask) BUG_ON(1); return wl->ops->sta_get_ap_rate_mask(wl, wlvif); } static inline int wlcore_identify_fw(struct wl1271 *wl) { if (wl->ops->identify_fw) return wl->ops->identify_fw(wl); return 0; } static inline void wlcore_hw_set_tx_desc_csum(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) { if (!wl->ops->set_tx_desc_csum) BUG_ON(1); wl->ops->set_tx_desc_csum(wl, desc, skb); } static inline void wlcore_hw_set_rx_csum(struct wl1271 *wl, struct wl1271_rx_descriptor *desc, struct sk_buff *skb) { if (wl->ops->set_rx_csum) wl->ops->set_rx_csum(wl, desc, skb); } static inline u32 wlcore_hw_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { if (wl->ops->ap_get_mimo_wide_rate_mask) return wl->ops->ap_get_mimo_wide_rate_mask(wl, wlvif); return 0; } static inline int wlcore_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) { if (wl->ops->debugfs_init) return wl->ops->debugfs_init(wl, rootdir); return 0; } static inline int wlcore_handle_static_data(struct wl1271 *wl, void *static_data) { if (wl->ops->handle_static_data) return wl->ops->handle_static_data(wl, static_data); return 0; } static inline int wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem) { if (!wl->ops->get_spare_blocks) BUG_ON(1); return wl->ops->get_spare_blocks(wl, is_gem); } static inline int wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { if (!wl->ops->set_key) BUG_ON(1); return wl->ops->set_key(wl, cmd, vif, sta, key_conf); } static inline u32 wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) { if (wl->ops->pre_pkt_send) return wl->ops->pre_pkt_send(wl, buf_offset, last_len); return buf_offset; } #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/event.h0000644000175000017500000001014112026211315023657 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __EVENT_H__ #define __EVENT_H__ /* * Mbox events * * The event mechanism is based on a pair of event buffers (buffers A and * B) at fixed locations in the target's memory. The host processes one * buffer while the other buffer continues to collect events. If the host * is not processing events, an interrupt is issued to signal that a buffer * is ready. Once the host is done with processing events from one buffer, * it signals the target (with an ACK interrupt) that the event buffer is * free. */ enum { RSSI_SNR_TRIGGER_0_EVENT_ID = BIT(0), RSSI_SNR_TRIGGER_1_EVENT_ID = BIT(1), RSSI_SNR_TRIGGER_2_EVENT_ID = BIT(2), RSSI_SNR_TRIGGER_3_EVENT_ID = BIT(3), RSSI_SNR_TRIGGER_4_EVENT_ID = BIT(4), RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5), RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6), RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7), MEASUREMENT_START_EVENT_ID = BIT(8), MEASUREMENT_COMPLETE_EVENT_ID = BIT(9), SCAN_COMPLETE_EVENT_ID = BIT(10), WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11), AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), RESERVED1 = BIT(13), PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), ROLE_STOP_COMPLETE_EVENT_ID = BIT(15), RADAR_DETECTED_EVENT_ID = BIT(16), CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), MAX_TX_RETRY_EVENT_ID = BIT(20), DUMMY_PACKET_EVENT_ID = BIT(21), SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23), SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25), INACTIVE_STA_EVENT_ID = BIT(26), PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27), PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28), PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29), BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30), REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31), EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, }; enum { EVENT_ENTER_POWER_SAVE_FAIL = 0, EVENT_ENTER_POWER_SAVE_SUCCESS, }; #define NUM_OF_RSSI_SNR_TRIGGERS 8 struct event_mailbox { __le32 events_vector; __le32 events_mask; __le32 reserved_1; __le32 reserved_2; u8 number_of_scan_results; u8 scan_tag; u8 completed_scan_status; u8 reserved_3; u8 soft_gemini_sense_info; u8 soft_gemini_protective_info; s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; u8 change_auto_mode_timeout; u8 scheduled_scan_status; u8 reserved4; /* tuned channel (roc) */ u8 roc_channel; __le16 hlid_removed_bitmap; /* bitmap of aged stations (by HLID) */ __le16 sta_aging_status; /* bitmap of stations (by HLID) which exceeded max tx retries */ __le16 sta_tx_retry_exceeded; /* discovery completed results */ u8 discovery_tag; u8 number_of_preq_results; u8 number_of_prsp_results; u8 reserved_5; /* rx ba constraint */ u8 role_id; /* 0xFF means any role. */ u8 rx_ba_allowed; u8 reserved_6[2]; /* Channel switch results */ u8 channel_switch_role_id; u8 channel_switch_status; u8 reserved_7[2]; u8 ps_poll_delivery_failure_role_ids; u8 stopped_role_ids; u8 started_role_ids; u8 reserved_8[9]; } __packed; struct wl1271; int wl1271_event_unmask(struct wl1271 *wl); int wl1271_event_handle(struct wl1271 *wl, u8 mbox); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/event.c0000644000175000017500000002030312026211315023653 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "wlcore.h" #include "debug.h" #include "io.h" #include "event.h" #include "ps.h" #include "scan.h" #include "wl12xx_80211.h" static void wl1271_event_rssi_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct event_mailbox *mbox) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); enum nl80211_cqm_rssi_threshold_event event; s8 metric = mbox->rssi_snr_trigger_metric[0]; wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric); if (metric <= wlvif->rssi_thold) event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; else event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; if (event != wlvif->last_rssi_event) ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL); wlvif->last_rssi_event = event; } static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); if (wlvif->bss_type != BSS_TYPE_AP_BSS) { if (!wlvif->sta.ba_rx_bitmap) return; ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap, vif->bss_conf.bssid); } else { u8 hlid; struct wl1271_link *lnk; for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) { lnk = &wl->links[hlid]; if (!lnk->ba_bitmap) continue; ieee80211_stop_rx_ba_session(vif, lnk->ba_bitmap, lnk->addr); } } } static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) { struct wl12xx_vif *wlvif; if (enable) { set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); } else { clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); wl12xx_for_each_wlvif_sta(wl, wlvif) { wl1271_recalc_rx_streaming(wl, wlvif); } } } static void wl1271_event_mbox_dump(struct event_mailbox *mbox) { wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); } static int wl1271_event_process(struct wl1271 *wl) { struct event_mailbox *mbox = wl->mbox; struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; u32 vector; bool disconnect_sta = false; unsigned long sta_bitmap = 0; int ret; wl1271_event_mbox_dump(mbox); vector = le32_to_cpu(mbox->events_vector); vector &= ~(le32_to_cpu(mbox->events_mask)); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); wl1271_scan_stm(wl, wl->scan_vif); } if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " "(status 0x%0x)", mbox->scheduled_scan_status); wl1271_scan_sched_scan_results(wl); } if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " "(status 0x%0x)", mbox->scheduled_scan_status); if (wl->sched_scanning) { ieee80211_sched_scan_stopped(wl->hw); wl->sched_scanning = false; } } if (vector & SOFT_GEMINI_SENSE_EVENT_ID) wl12xx_event_soft_gemini_sense(wl, mbox->soft_gemini_sense_info); /* * We are HW_MONITOR device. On beacon loss - queue * connection loss work. Cancel it on REGAINED event. */ if (vector & BSS_LOSE_EVENT_ID) { /* TODO: check for multi-role */ int delay = wl->conf.conn.synch_fail_thold * wl->conf.conn.bss_lose_timeout; wl1271_info("Beacon loss detected."); /* * if the work is already queued, it should take place. We * don't want to delay the connection loss indication * any more. */ ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work, msecs_to_jiffies(delay)); wl12xx_for_each_wlvif_sta(wl, wlvif) { vif = wl12xx_wlvif_to_vif(wlvif); ieee80211_cqm_rssi_notify( vif, NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL); } } if (vector & REGAINED_BSS_EVENT_ID) { /* TODO: check for multi-role */ wl1271_info("Beacon regained."); cancel_delayed_work(&wl->connection_loss_work); /* sanity check - we can't lose and gain the beacon together */ WARN(vector & BSS_LOSE_EVENT_ID, "Concurrent beacon loss and gain from FW"); } if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { /* TODO: check actual multi-role support */ wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); wl12xx_for_each_wlvif_sta(wl, wlvif) { wl1271_event_rssi_trigger(wl, wlvif, mbox); } } if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) { u8 role_id = mbox->role_id; wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. " "ba_allowed = 0x%x, role_id=%d", mbox->rx_ba_allowed, role_id); wl12xx_for_each_wlvif(wl, wlvif) { if (role_id != 0xff && role_id != wlvif->role_id) continue; wlvif->ba_allowed = !!mbox->rx_ba_allowed; if (!wlvif->ba_allowed) wl1271_stop_ba_event(wl, wlvif); } } if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. " "status = 0x%x", mbox->channel_switch_status); /* * That event uses for two cases: * 1) channel switch complete with status=0 * 2) channel switch failed status=1 */ /* TODO: configure only the relevant vif */ wl12xx_for_each_wlvif_sta(wl, wlvif) { bool success; if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) continue; success = mbox->channel_switch_status ? false : true; vif = wl12xx_wlvif_to_vif(wlvif); ieee80211_chswitch_done(vif, success); } } if ((vector & DUMMY_PACKET_EVENT_ID)) { wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); ret = wl1271_tx_dummy_packet(wl); if (ret < 0) return ret; } /* * "TX retries exceeded" has a different meaning according to mode. * In AP mode the offending station is disconnected. */ if (vector & MAX_TX_RETRY_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); disconnect_sta = true; } if (vector & INACTIVE_STA_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); disconnect_sta = true; } if (disconnect_sta) { u32 num_packets = wl->conf.tx.max_tx_retries; struct ieee80211_sta *sta; const u8 *addr; int h; for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) { bool found = false; /* find the ap vif connected to this sta */ wl12xx_for_each_wlvif_ap(wl, wlvif) { if (!test_bit(h, wlvif->ap.sta_hlid_map)) continue; found = true; break; } if (!found) continue; vif = wl12xx_wlvif_to_vif(wlvif); addr = wl->links[h].addr; rcu_read_lock(); sta = ieee80211_find_sta(vif, addr); if (sta) { wl1271_debug(DEBUG_EVENT, "remove sta %d", h); ieee80211_report_low_ack(sta, num_packets); } rcu_read_unlock(); } } return 0; } int wl1271_event_unmask(struct wl1271 *wl) { int ret; ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask)); if (ret < 0) return ret; return 0; } int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) { int ret; wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); if (mbox_num > 1) return -EINVAL; /* first we read the mbox descriptor */ ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, sizeof(*wl->mbox), false); if (ret < 0) return ret; /* process the descriptor */ ret = wl1271_event_process(wl); if (ret < 0) return ret; /* * TODO: we just need this because one bit is in a different * place. Is there any better way? */ ret = wl->ops->ack_event(wl); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/debug.h0000644000175000017500000000533412026211315023634 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2011 Texas Instruments. All rights reserved. * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __DEBUG_H__ #define __DEBUG_H__ #include #include #define DRIVER_NAME "wl12xx" #define DRIVER_PREFIX DRIVER_NAME ": " enum { DEBUG_NONE = 0, DEBUG_IRQ = BIT(0), DEBUG_SPI = BIT(1), DEBUG_BOOT = BIT(2), DEBUG_MAILBOX = BIT(3), DEBUG_TESTMODE = BIT(4), DEBUG_EVENT = BIT(5), DEBUG_TX = BIT(6), DEBUG_RX = BIT(7), DEBUG_SCAN = BIT(8), DEBUG_CRYPT = BIT(9), DEBUG_PSM = BIT(10), DEBUG_MAC80211 = BIT(11), DEBUG_CMD = BIT(12), DEBUG_ACX = BIT(13), DEBUG_SDIO = BIT(14), DEBUG_FILTERS = BIT(15), DEBUG_ADHOC = BIT(16), DEBUG_AP = BIT(17), DEBUG_PROBE = BIT(18), DEBUG_IO = BIT(19), DEBUG_MASTER = (DEBUG_ADHOC | DEBUG_AP), DEBUG_ALL = ~0, }; extern u32 wl12xx_debug_level; #define DEBUG_DUMP_LIMIT 1024 #define wl1271_error(fmt, arg...) \ pr_err(DRIVER_PREFIX "ERROR " fmt "\n", ##arg) #define wl1271_warning(fmt, arg...) \ pr_warning(DRIVER_PREFIX "WARNING " fmt "\n", ##arg) #define wl1271_notice(fmt, arg...) \ pr_info(DRIVER_PREFIX fmt "\n", ##arg) #define wl1271_info(fmt, arg...) \ pr_info(DRIVER_PREFIX fmt "\n", ##arg) #define wl1271_debug(level, fmt, arg...) \ do { \ if (level & wl12xx_debug_level) \ pr_debug(DRIVER_PREFIX fmt "\n", ##arg); \ } while (0) /* TODO: use pr_debug_hex_dump when it becomes available */ #define wl1271_dump(level, prefix, buf, len) \ do { \ if (level & wl12xx_debug_level) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ DUMP_PREFIX_OFFSET, 16, 1, \ buf, \ min_t(size_t, len, DEBUG_DUMP_LIMIT), \ 0); \ } while (0) #define wl1271_dump_ascii(level, prefix, buf, len) \ do { \ if (level & wl12xx_debug_level) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ DUMP_PREFIX_OFFSET, 16, 1, \ buf, \ min_t(size_t, len, DEBUG_DUMP_LIMIT), \ true); \ } while (0) #endif /* __DEBUG_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/debugfs.h0000644000175000017500000000723112026211315024163 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __DEBUGFS_H__ #define __DEBUGFS_H__ #include "wlcore.h" int wl1271_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...); int wl1271_debugfs_init(struct wl1271 *wl); void wl1271_debugfs_exit(struct wl1271 *wl); void wl1271_debugfs_reset(struct wl1271 *wl); void wl1271_debugfs_update_stats(struct wl1271 *wl); #define DEBUGFS_FORMAT_BUFFER_SIZE 256 #define DEBUGFS_READONLY_FILE(name, fmt, value...) \ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct wl1271 *wl = file->private_data; \ return wl1271_format_buffer(userbuf, count, ppos, \ fmt "\n", ##value); \ } \ \ static const struct file_operations name## _ops = { \ .read = name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_ADD(name, parent) \ do { \ entry = debugfs_create_file(#name, 0400, parent, \ wl, &name## _ops); \ if (!entry || IS_ERR(entry)) \ goto err; \ } while (0); #define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ do { \ entry = debugfs_create_file(#name, 0400, parent, \ wl, &prefix## _## name## _ops); \ if (!entry || IS_ERR(entry)) \ goto err; \ } while (0); #define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type) \ static ssize_t sub## _ ##name## _read(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct wl1271 *wl = file->private_data; \ struct struct_type *stats = wl->stats.fw_stats; \ \ wl1271_debugfs_update_stats(wl); \ \ return wl1271_format_buffer(userbuf, count, ppos, fmt "\n", \ stats->sub.name); \ } \ \ static const struct file_operations sub## _ ##name## _ops = { \ .read = sub## _ ##name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type) \ static ssize_t sub## _ ##name## _read(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct wl1271 *wl = file->private_data; \ struct struct_type *stats = wl->stats.fw_stats; \ char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = ""; \ int res, i; \ \ wl1271_debugfs_update_stats(wl); \ \ for (i = 0; i < len; i++) \ res = snprintf(buf, sizeof(buf), "%s[%d] = %d\n", \ buf, i, stats->sub.name[i]); \ \ return wl1271_format_buffer(userbuf, count, ppos, "%s", buf); \ } \ \ static const struct file_operations sub## _ ##name## _ops = { \ .read = sub## _ ##name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_FWSTATS_ADD(sub, name) \ DEBUGFS_ADD(sub## _ ##name, stats) #endif /* WL1271_DEBUGFS_H */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/debugfs.c0000644000175000017500000007615412026211315024170 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "debugfs.h" #include #include #include #include "wlcore.h" #include "debug.h" #include "acx.h" #include "ps.h" #include "io.h" #include "tx.h" #include "hw_ops.h" /* ms */ #define WL1271_DEBUGFS_STATS_LIFETIME 1000 #define WLCORE_MAX_BLOCK_SIZE ((size_t)(4*PAGE_SIZE)) /* debugfs macros idea from mac80211 */ int wl1271_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...) { va_list args; char buf[DEBUGFS_FORMAT_BUFFER_SIZE]; int res; va_start(args, fmt); res = vscnprintf(buf, sizeof(buf), fmt, args); va_end(args); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } EXPORT_SYMBOL_GPL(wl1271_format_buffer); void wl1271_debugfs_update_stats(struct wl1271 *wl) { int ret; mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; if (wl->state == WL1271_STATE_ON && !wl->plt && time_after(jiffies, wl->stats.fw_stats_update + msecs_to_jiffies(WL1271_DEBUGFS_STATS_LIFETIME))) { wl1271_acx_statistics(wl, wl->stats.fw_stats); wl->stats.fw_stats_update = jiffies; } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } EXPORT_SYMBOL_GPL(wl1271_debugfs_update_stats); DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count); DEBUGFS_READONLY_FILE(excessive_retries, "%u", wl->stats.excessive_retries); static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; u32 queue_len; char buf[20]; int res; queue_len = wl1271_tx_total_queue_count(wl); res = scnprintf(buf, sizeof(buf), "%u\n", queue_len); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } static const struct file_operations tx_queue_len_ops = { .read = tx_queue_len_read, .open = simple_open, .llseek = default_llseek, }; static void chip_op_handler(struct wl1271 *wl, unsigned long value, void *arg) { int ret; int (*chip_op) (struct wl1271 *wl); if (!arg) { wl1271_warning("debugfs chip_op_handler with no callback"); return; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) return; chip_op = arg; chip_op(wl); wl1271_ps_elp_sleep(wl); } static inline void no_write_handler(struct wl1271 *wl, unsigned long value, unsigned long param) { } #define WL12XX_CONF_DEBUGFS(param, conf_sub_struct, \ min_val, max_val, write_handler_locked, \ write_handler_arg) \ static ssize_t param##_read(struct file *file, \ char __user *user_buf, \ size_t count, loff_t *ppos) \ { \ struct wl1271 *wl = file->private_data; \ return wl1271_format_buffer(user_buf, count, \ ppos, "%d\n", \ wl->conf.conf_sub_struct.param); \ } \ \ static ssize_t param##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos) \ { \ struct wl1271 *wl = file->private_data; \ unsigned long value; \ int ret; \ \ ret = kstrtoul_from_user(user_buf, count, 10, &value); \ if (ret < 0) { \ wl1271_warning("illegal value for " #param); \ return -EINVAL; \ } \ \ if (value < min_val || value > max_val) { \ wl1271_warning(#param " is not in valid range"); \ return -ERANGE; \ } \ \ mutex_lock(&wl->mutex); \ wl->conf.conf_sub_struct.param = value; \ \ write_handler_locked(wl, value, write_handler_arg); \ \ mutex_unlock(&wl->mutex); \ return count; \ } \ \ static const struct file_operations param##_ops = { \ .read = param##_read, \ .write = param##_write, \ .open = simple_open, \ .llseek = default_llseek, \ }; WL12XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535, chip_op_handler, wl1271_acx_init_rx_interrupt) WL12XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535, chip_op_handler, wl1271_acx_init_rx_interrupt) WL12XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100, chip_op_handler, wl1271_acx_init_rx_interrupt) static ssize_t gpio_power_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; bool state = test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); int res; char buf[10]; res = scnprintf(buf, sizeof(buf), "%d\n", state); return simple_read_from_buffer(user_buf, count, ppos, buf, res); } static ssize_t gpio_power_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in gpio_power"); return -EINVAL; } mutex_lock(&wl->mutex); if (value) wl1271_power_on(wl); else wl1271_power_off(wl); mutex_unlock(&wl->mutex); return count; } static const struct file_operations gpio_power_ops = { .read = gpio_power_read, .write = gpio_power_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t start_recovery_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; mutex_lock(&wl->mutex); wl12xx_queue_recovery_work(wl); mutex_unlock(&wl->mutex); return count; } static const struct file_operations start_recovery_ops = { .write = start_recovery_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t dynamic_ps_timeout_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; return wl1271_format_buffer(user_buf, count, ppos, "%d\n", wl->conf.conn.dynamic_ps_timeout); } static ssize_t dynamic_ps_timeout_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in dynamic_ps"); return -EINVAL; } if (value < 1 || value > 65535) { wl1271_warning("dyanmic_ps_timeout is not in valid range"); return -ERANGE; } mutex_lock(&wl->mutex); wl->conf.conn.dynamic_ps_timeout = value; if (wl->state == WL1271_STATE_OFF) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* In case we're already in PSM, trigger it again to set new timeout * immediately without waiting for re-association */ wl12xx_for_each_wlvif_sta(wl, wlvif) { if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static const struct file_operations dynamic_ps_timeout_ops = { .read = dynamic_ps_timeout_read, .write = dynamic_ps_timeout_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t forced_ps_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; return wl1271_format_buffer(user_buf, count, ppos, "%d\n", wl->conf.conn.forced_ps); } static ssize_t forced_ps_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; unsigned long value; int ret, ps_mode; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in forced_ps"); return -EINVAL; } if (value != 1 && value != 0) { wl1271_warning("forced_ps should be either 0 or 1"); return -ERANGE; } mutex_lock(&wl->mutex); if (wl->conf.conn.forced_ps == value) goto out; wl->conf.conn.forced_ps = value; if (wl->state == WL1271_STATE_OFF) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; /* In case we're already in PSM, trigger it again to switch mode * immediately without waiting for re-association */ ps_mode = value ? STATION_POWER_SAVE_MODE : STATION_AUTO_PS_MODE; wl12xx_for_each_wlvif_sta(wl, wlvif) { if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) wl1271_ps_set_mode(wl, wlvif, ps_mode); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static const struct file_operations forced_ps_ops = { .read = forced_ps_read, .write = forced_ps_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t split_scan_timeout_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; return wl1271_format_buffer(user_buf, count, ppos, "%d\n", wl->conf.scan.split_scan_timeout / 1000); } static ssize_t split_scan_timeout_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in split_scan_timeout"); return -EINVAL; } if (value == 0) wl1271_info("split scan will be disabled"); mutex_lock(&wl->mutex); wl->conf.scan.split_scan_timeout = value * 1000; mutex_unlock(&wl->mutex); return count; } static const struct file_operations split_scan_timeout_ops = { .read = split_scan_timeout_read, .write = split_scan_timeout_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t driver_state_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; int res = 0; ssize_t ret; char *buf; #define DRIVER_STATE_BUF_LEN 1024 buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL); if (!buf) return -ENOMEM; mutex_lock(&wl->mutex); #define DRIVER_STATE_PRINT(x, fmt) \ (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\ #x " = " fmt "\n", wl->x)) #define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld") #define DRIVER_STATE_PRINT_INT(x) DRIVER_STATE_PRINT(x, "%d") #define DRIVER_STATE_PRINT_STR(x) DRIVER_STATE_PRINT(x, "%s") #define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx") #define DRIVER_STATE_PRINT_HEX(x) DRIVER_STATE_PRINT(x, "0x%x") DRIVER_STATE_PRINT_INT(tx_blocks_available); DRIVER_STATE_PRINT_INT(tx_allocated_blocks); DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]); DRIVER_STATE_PRINT_INT(tx_allocated_pkts[1]); DRIVER_STATE_PRINT_INT(tx_allocated_pkts[2]); DRIVER_STATE_PRINT_INT(tx_allocated_pkts[3]); DRIVER_STATE_PRINT_INT(tx_frames_cnt); DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]); DRIVER_STATE_PRINT_INT(tx_queue_count[0]); DRIVER_STATE_PRINT_INT(tx_queue_count[1]); DRIVER_STATE_PRINT_INT(tx_queue_count[2]); DRIVER_STATE_PRINT_INT(tx_queue_count[3]); DRIVER_STATE_PRINT_INT(tx_packets_count); DRIVER_STATE_PRINT_INT(tx_results_count); DRIVER_STATE_PRINT_LHEX(flags); DRIVER_STATE_PRINT_INT(tx_blocks_freed); DRIVER_STATE_PRINT_INT(rx_counter); DRIVER_STATE_PRINT_INT(state); DRIVER_STATE_PRINT_INT(channel); DRIVER_STATE_PRINT_INT(band); DRIVER_STATE_PRINT_INT(power_level); DRIVER_STATE_PRINT_INT(sg_enabled); DRIVER_STATE_PRINT_INT(enable_11a); DRIVER_STATE_PRINT_INT(noise); DRIVER_STATE_PRINT_HEX(ap_fw_ps_map); DRIVER_STATE_PRINT_LHEX(ap_ps_map); DRIVER_STATE_PRINT_HEX(quirks); DRIVER_STATE_PRINT_HEX(irq); /* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */ DRIVER_STATE_PRINT_HEX(hw_pg_ver); DRIVER_STATE_PRINT_HEX(platform_quirks); DRIVER_STATE_PRINT_HEX(chip.id); DRIVER_STATE_PRINT_STR(chip.fw_ver_str); DRIVER_STATE_PRINT_INT(sched_scanning); #undef DRIVER_STATE_PRINT_INT #undef DRIVER_STATE_PRINT_LONG #undef DRIVER_STATE_PRINT_HEX #undef DRIVER_STATE_PRINT_LHEX #undef DRIVER_STATE_PRINT_STR #undef DRIVER_STATE_PRINT #undef DRIVER_STATE_BUF_LEN mutex_unlock(&wl->mutex); ret = simple_read_from_buffer(user_buf, count, ppos, buf, res); kfree(buf); return ret; } static const struct file_operations driver_state_ops = { .read = driver_state_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t vifs_state_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; int ret, res = 0; const int buf_size = 4096; char *buf; char tmp_buf[64]; buf = kzalloc(buf_size, GFP_KERNEL); if (!buf) return -ENOMEM; mutex_lock(&wl->mutex); #define VIF_STATE_PRINT(x, fmt) \ (res += scnprintf(buf + res, buf_size - res, \ #x " = " fmt "\n", wlvif->x)) #define VIF_STATE_PRINT_LONG(x) VIF_STATE_PRINT(x, "%ld") #define VIF_STATE_PRINT_INT(x) VIF_STATE_PRINT(x, "%d") #define VIF_STATE_PRINT_STR(x) VIF_STATE_PRINT(x, "%s") #define VIF_STATE_PRINT_LHEX(x) VIF_STATE_PRINT(x, "0x%lx") #define VIF_STATE_PRINT_LLHEX(x) VIF_STATE_PRINT(x, "0x%llx") #define VIF_STATE_PRINT_HEX(x) VIF_STATE_PRINT(x, "0x%x") #define VIF_STATE_PRINT_NSTR(x, len) \ do { \ memset(tmp_buf, 0, sizeof(tmp_buf)); \ memcpy(tmp_buf, wlvif->x, \ min_t(u8, len, sizeof(tmp_buf) - 1)); \ res += scnprintf(buf + res, buf_size - res, \ #x " = %s\n", tmp_buf); \ } while (0) wl12xx_for_each_wlvif(wl, wlvif) { VIF_STATE_PRINT_INT(role_id); VIF_STATE_PRINT_INT(bss_type); VIF_STATE_PRINT_LHEX(flags); VIF_STATE_PRINT_INT(p2p); VIF_STATE_PRINT_INT(dev_role_id); VIF_STATE_PRINT_INT(dev_hlid); if (wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS) { VIF_STATE_PRINT_INT(sta.hlid); VIF_STATE_PRINT_INT(sta.ba_rx_bitmap); VIF_STATE_PRINT_INT(sta.basic_rate_idx); VIF_STATE_PRINT_INT(sta.ap_rate_idx); VIF_STATE_PRINT_INT(sta.p2p_rate_idx); VIF_STATE_PRINT_INT(sta.qos); } else { VIF_STATE_PRINT_INT(ap.global_hlid); VIF_STATE_PRINT_INT(ap.bcast_hlid); VIF_STATE_PRINT_LHEX(ap.sta_hlid_map[0]); VIF_STATE_PRINT_INT(ap.mgmt_rate_idx); VIF_STATE_PRINT_INT(ap.bcast_rate_idx); VIF_STATE_PRINT_INT(ap.ucast_rate_idx[0]); VIF_STATE_PRINT_INT(ap.ucast_rate_idx[1]); VIF_STATE_PRINT_INT(ap.ucast_rate_idx[2]); VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]); } VIF_STATE_PRINT_INT(last_tx_hlid); VIF_STATE_PRINT_LHEX(links_map[0]); VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len); VIF_STATE_PRINT_INT(band); VIF_STATE_PRINT_INT(channel); VIF_STATE_PRINT_HEX(bitrate_masks[0]); VIF_STATE_PRINT_HEX(bitrate_masks[1]); VIF_STATE_PRINT_HEX(basic_rate_set); VIF_STATE_PRINT_HEX(basic_rate); VIF_STATE_PRINT_HEX(rate_set); VIF_STATE_PRINT_INT(beacon_int); VIF_STATE_PRINT_INT(default_key); VIF_STATE_PRINT_INT(aid); VIF_STATE_PRINT_INT(session_counter); VIF_STATE_PRINT_INT(psm_entry_retry); VIF_STATE_PRINT_INT(power_level); VIF_STATE_PRINT_INT(rssi_thold); VIF_STATE_PRINT_INT(last_rssi_event); VIF_STATE_PRINT_INT(ba_support); VIF_STATE_PRINT_INT(ba_allowed); VIF_STATE_PRINT_LLHEX(tx_security_seq); VIF_STATE_PRINT_INT(tx_security_last_seq_lsb); } #undef VIF_STATE_PRINT_INT #undef VIF_STATE_PRINT_LONG #undef VIF_STATE_PRINT_HEX #undef VIF_STATE_PRINT_LHEX #undef VIF_STATE_PRINT_LLHEX #undef VIF_STATE_PRINT_STR #undef VIF_STATE_PRINT_NSTR #undef VIF_STATE_PRINT mutex_unlock(&wl->mutex); ret = simple_read_from_buffer(user_buf, count, ppos, buf, res); kfree(buf); return ret; } static const struct file_operations vifs_state_ops = { .read = vifs_state_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t dtim_interval_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; u8 value; if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_DTIM || wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM) value = wl->conf.conn.listen_interval; else value = 0; return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); } static ssize_t dtim_interval_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value for dtim_interval"); return -EINVAL; } if (value < 1 || value > 10) { wl1271_warning("dtim value is not in valid range"); return -ERANGE; } mutex_lock(&wl->mutex); wl->conf.conn.listen_interval = value; /* for some reason there are different event types for 1 and >1 */ if (value == 1) wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_DTIM; else wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM; /* * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only * take effect on the next time we enter psm. */ mutex_unlock(&wl->mutex); return count; } static const struct file_operations dtim_interval_ops = { .read = dtim_interval_read, .write = dtim_interval_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t suspend_dtim_interval_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; u8 value; if (wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_DTIM || wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM) value = wl->conf.conn.suspend_listen_interval; else value = 0; return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); } static ssize_t suspend_dtim_interval_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value for suspend_dtim_interval"); return -EINVAL; } if (value < 1 || value > 10) { wl1271_warning("suspend_dtim value is not in valid range"); return -ERANGE; } mutex_lock(&wl->mutex); wl->conf.conn.suspend_listen_interval = value; /* for some reason there are different event types for 1 and >1 */ if (value == 1) wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_DTIM; else wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM; mutex_unlock(&wl->mutex); return count; } static const struct file_operations suspend_dtim_interval_ops = { .read = suspend_dtim_interval_read, .write = suspend_dtim_interval_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t beacon_interval_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; u8 value; if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_BEACON || wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_BEACONS) value = wl->conf.conn.listen_interval; else value = 0; return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); } static ssize_t beacon_interval_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value for beacon_interval"); return -EINVAL; } if (value < 1 || value > 255) { wl1271_warning("beacon interval value is not in valid range"); return -ERANGE; } mutex_lock(&wl->mutex); wl->conf.conn.listen_interval = value; /* for some reason there are different event types for 1 and >1 */ if (value == 1) wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_BEACON; else wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_BEACONS; /* * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only * take effect on the next time we enter psm. */ mutex_unlock(&wl->mutex); return count; } static const struct file_operations beacon_interval_ops = { .read = beacon_interval_read, .write = beacon_interval_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t rx_streaming_interval_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in rx_streaming_interval!"); return -EINVAL; } /* valid values: 0, 10-100 */ if (value && (value < 10 || value > 100)) { wl1271_warning("value is not in range!"); return -ERANGE; } mutex_lock(&wl->mutex); wl->conf.rx_streaming.interval = value; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl12xx_for_each_wlvif_sta(wl, wlvif) { wl1271_recalc_rx_streaming(wl, wlvif); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static ssize_t rx_streaming_interval_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; return wl1271_format_buffer(userbuf, count, ppos, "%d\n", wl->conf.rx_streaming.interval); } static const struct file_operations rx_streaming_interval_ops = { .read = rx_streaming_interval_read, .write = rx_streaming_interval_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t rx_streaming_always_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in rx_streaming_write!"); return -EINVAL; } /* valid values: 0, 10-100 */ if (!(value == 0 || value == 1)) { wl1271_warning("value is not in valid!"); return -EINVAL; } mutex_lock(&wl->mutex); wl->conf.rx_streaming.always = value; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl12xx_for_each_wlvif_sta(wl, wlvif) { wl1271_recalc_rx_streaming(wl, wlvif); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static ssize_t rx_streaming_always_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; return wl1271_format_buffer(userbuf, count, ppos, "%d\n", wl->conf.rx_streaming.always); } static const struct file_operations rx_streaming_always_ops = { .read = rx_streaming_always_read, .write = rx_streaming_always_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t beacon_filtering_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; char buf[10]; size_t len; unsigned long value; int ret; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; ret = kstrtoul(buf, 0, &value); if (ret < 0) { wl1271_warning("illegal value for beacon_filtering!"); return -EINVAL; } mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl12xx_for_each_wlvif(wl, wlvif) { ret = wl1271_acx_beacon_filter_opt(wl, wlvif, !!value); } wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static const struct file_operations beacon_filtering_ops = { .write = beacon_filtering_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t fw_stats_raw_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; wl1271_debugfs_update_stats(wl); return simple_read_from_buffer(userbuf, count, ppos, wl->stats.fw_stats, wl->stats.fw_stats_len); } static const struct file_operations fw_stats_raw_ops = { .read = fw_stats_raw_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t sleep_auth_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; return wl1271_format_buffer(user_buf, count, ppos, "%d\n", wl->sleep_auth); } static ssize_t sleep_auth_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 0, &value); if (ret < 0) { wl1271_warning("illegal value in sleep_auth"); return -EINVAL; } if (value < 0 || value > WL1271_PSM_MAX) { wl1271_warning("sleep_auth must be between 0 and %d", WL1271_PSM_MAX); return -ERANGE; } mutex_lock(&wl->mutex); wl->conf.conn.sta_sleep_auth = value; if (wl->state == WL1271_STATE_OFF) { /* this will show up on "read" in case we are off */ wl->sleep_auth = value; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_acx_sleep_auth(wl, value); if (ret < 0) goto out_sleep; out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static const struct file_operations sleep_auth_ops = { .read = sleep_auth_read, .write = sleep_auth_write, .open = simple_open, .llseek = default_llseek, }; static ssize_t dev_mem_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wlcore_partition_set part, old_part; size_t bytes = count; int ret; char *buf; /* only requests of dword-aligned size and offset are supported */ if (bytes % 4) return -EINVAL; if (*ppos % 4) return -EINVAL; /* function should return in reasonable time */ bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE); if (bytes == 0) return -EINVAL; memset(&part, 0, sizeof(part)); part.mem.start = file->f_pos; part.mem.size = bytes; buf = kmalloc(bytes, GFP_KERNEL); if (!buf) return -ENOMEM; mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { ret = -EFAULT; goto skip_read; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto skip_read; /* store current partition and switch partition */ memcpy(&old_part, &wl->curr_part, sizeof(old_part)); ret = wlcore_set_partition(wl, &part); if (ret < 0) goto part_err; ret = wlcore_raw_read(wl, 0, buf, bytes, false); if (ret < 0) goto read_err; read_err: /* recover partition */ ret = wlcore_set_partition(wl, &old_part); if (ret < 0) goto part_err; part_err: wl1271_ps_elp_sleep(wl); skip_read: mutex_unlock(&wl->mutex); if (ret == 0) { ret = copy_to_user(user_buf, buf, bytes); if (ret < bytes) { bytes -= ret; *ppos += bytes; ret = 0; } else { ret = -EFAULT; } } kfree(buf); return ((ret == 0) ? bytes : ret); } static ssize_t dev_mem_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; struct wlcore_partition_set part, old_part; size_t bytes = count; int ret; char *buf; /* only requests of dword-aligned size and offset are supported */ if (bytes % 4) return -EINVAL; if (*ppos % 4) return -EINVAL; /* function should return in reasonable time */ bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE); if (bytes == 0) return -EINVAL; memset(&part, 0, sizeof(part)); part.mem.start = file->f_pos; part.mem.size = bytes; buf = kmalloc(bytes, GFP_KERNEL); if (!buf) return -ENOMEM; ret = copy_from_user(buf, user_buf, bytes); if (ret) { ret = -EFAULT; goto err_out; } mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { ret = -EFAULT; goto skip_write; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto skip_write; /* store current partition and switch partition */ memcpy(&old_part, &wl->curr_part, sizeof(old_part)); ret = wlcore_set_partition(wl, &part); if (ret < 0) goto part_err; ret = wlcore_raw_write(wl, 0, buf, bytes, false); if (ret < 0) goto write_err; write_err: /* recover partition */ ret = wlcore_set_partition(wl, &old_part); if (ret < 0) goto part_err; part_err: wl1271_ps_elp_sleep(wl); skip_write: mutex_unlock(&wl->mutex); if (ret == 0) *ppos += bytes; err_out: kfree(buf); return ((ret == 0) ? bytes : ret); } static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig) { loff_t ret; /* only requests of dword-aligned size and offset are supported */ if (offset % 4) return -EINVAL; switch (orig) { case SEEK_SET: file->f_pos = offset; ret = file->f_pos; break; case SEEK_CUR: file->f_pos += offset; ret = file->f_pos; break; default: ret = -EINVAL; } return ret; } static const struct file_operations dev_mem_ops = { .open = simple_open, .read = dev_mem_read, .write = dev_mem_write, .llseek = dev_mem_seek, }; static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { int ret = 0; struct dentry *entry, *streaming; DEBUGFS_ADD(tx_queue_len, rootdir); DEBUGFS_ADD(retry_count, rootdir); DEBUGFS_ADD(excessive_retries, rootdir); DEBUGFS_ADD(gpio_power, rootdir); DEBUGFS_ADD(start_recovery, rootdir); DEBUGFS_ADD(driver_state, rootdir); DEBUGFS_ADD(vifs_state, rootdir); DEBUGFS_ADD(dtim_interval, rootdir); DEBUGFS_ADD(suspend_dtim_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir); DEBUGFS_ADD(beacon_filtering, rootdir); DEBUGFS_ADD(dynamic_ps_timeout, rootdir); DEBUGFS_ADD(forced_ps, rootdir); DEBUGFS_ADD(split_scan_timeout, rootdir); DEBUGFS_ADD(irq_pkt_threshold, rootdir); DEBUGFS_ADD(irq_blk_threshold, rootdir); DEBUGFS_ADD(irq_timeout, rootdir); DEBUGFS_ADD(fw_stats_raw, rootdir); DEBUGFS_ADD(sleep_auth, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) goto err; DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming); DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming); DEBUGFS_ADD_PREFIX(dev, mem, rootdir); return 0; err: if (IS_ERR(entry)) ret = PTR_ERR(entry); else ret = -ENOMEM; return ret; } void wl1271_debugfs_reset(struct wl1271 *wl) { if (!wl->stats.fw_stats) return; memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len); wl->stats.retry_count = 0; wl->stats.excessive_retries = 0; } int wl1271_debugfs_init(struct wl1271 *wl) { int ret; struct dentry *rootdir; rootdir = debugfs_create_dir(KBUILD_MODNAME, wl->hw->wiphy->debugfsdir); if (IS_ERR(rootdir)) { ret = PTR_ERR(rootdir); goto out; } wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL); if (!wl->stats.fw_stats) { ret = -ENOMEM; goto out_remove; } wl->stats.fw_stats_update = jiffies; ret = wl1271_debugfs_add_files(wl, rootdir); if (ret < 0) goto out_exit; ret = wlcore_debugfs_init(wl, rootdir); if (ret < 0) goto out_exit; goto out; out_exit: wl1271_debugfs_exit(wl); out_remove: debugfs_remove_recursive(rootdir); out: return ret; } void wl1271_debugfs_exit(struct wl1271 *wl) { kfree(wl->stats.fw_stats); wl->stats.fw_stats = NULL; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/conf.h0000644000175000017500000007140412026211315023474 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __CONF_H__ #define __CONF_H__ enum { CONF_HW_BIT_RATE_1MBPS = BIT(0), CONF_HW_BIT_RATE_2MBPS = BIT(1), CONF_HW_BIT_RATE_5_5MBPS = BIT(2), CONF_HW_BIT_RATE_6MBPS = BIT(3), CONF_HW_BIT_RATE_9MBPS = BIT(4), CONF_HW_BIT_RATE_11MBPS = BIT(5), CONF_HW_BIT_RATE_12MBPS = BIT(6), CONF_HW_BIT_RATE_18MBPS = BIT(7), CONF_HW_BIT_RATE_22MBPS = BIT(8), CONF_HW_BIT_RATE_24MBPS = BIT(9), CONF_HW_BIT_RATE_36MBPS = BIT(10), CONF_HW_BIT_RATE_48MBPS = BIT(11), CONF_HW_BIT_RATE_54MBPS = BIT(12), CONF_HW_BIT_RATE_MCS_0 = BIT(13), CONF_HW_BIT_RATE_MCS_1 = BIT(14), CONF_HW_BIT_RATE_MCS_2 = BIT(15), CONF_HW_BIT_RATE_MCS_3 = BIT(16), CONF_HW_BIT_RATE_MCS_4 = BIT(17), CONF_HW_BIT_RATE_MCS_5 = BIT(18), CONF_HW_BIT_RATE_MCS_6 = BIT(19), CONF_HW_BIT_RATE_MCS_7 = BIT(20), CONF_HW_BIT_RATE_MCS_8 = BIT(21), CONF_HW_BIT_RATE_MCS_9 = BIT(22), CONF_HW_BIT_RATE_MCS_10 = BIT(23), CONF_HW_BIT_RATE_MCS_11 = BIT(24), CONF_HW_BIT_RATE_MCS_12 = BIT(25), CONF_HW_BIT_RATE_MCS_13 = BIT(26), CONF_HW_BIT_RATE_MCS_14 = BIT(27), CONF_HW_BIT_RATE_MCS_15 = BIT(28), }; enum { CONF_HW_RATE_INDEX_1MBPS = 0, CONF_HW_RATE_INDEX_2MBPS = 1, CONF_HW_RATE_INDEX_5_5MBPS = 2, CONF_HW_RATE_INDEX_6MBPS = 3, CONF_HW_RATE_INDEX_9MBPS = 4, CONF_HW_RATE_INDEX_11MBPS = 5, CONF_HW_RATE_INDEX_12MBPS = 6, CONF_HW_RATE_INDEX_18MBPS = 7, CONF_HW_RATE_INDEX_22MBPS = 8, CONF_HW_RATE_INDEX_24MBPS = 9, CONF_HW_RATE_INDEX_36MBPS = 10, CONF_HW_RATE_INDEX_48MBPS = 11, CONF_HW_RATE_INDEX_54MBPS = 12, CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, }; #define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff enum { CONF_SG_DISABLE = 0, CONF_SG_PROTECTIVE, CONF_SG_OPPORTUNISTIC }; enum { /* * Configure the min and max time BT gains the antenna * in WLAN / BT master basic rate * * Range: 0 - 255 (ms) */ CONF_SG_ACL_BT_MASTER_MIN_BR = 0, CONF_SG_ACL_BT_MASTER_MAX_BR, /* * Configure the min and max time BT gains the antenna * in WLAN / BT slave basic rate * * Range: 0 - 255 (ms) */ CONF_SG_ACL_BT_SLAVE_MIN_BR, CONF_SG_ACL_BT_SLAVE_MAX_BR, /* * Configure the min and max time BT gains the antenna * in WLAN / BT master EDR * * Range: 0 - 255 (ms) */ CONF_SG_ACL_BT_MASTER_MIN_EDR, CONF_SG_ACL_BT_MASTER_MAX_EDR, /* * Configure the min and max time BT gains the antenna * in WLAN / BT slave EDR * * Range: 0 - 255 (ms) */ CONF_SG_ACL_BT_SLAVE_MIN_EDR, CONF_SG_ACL_BT_SLAVE_MAX_EDR, /* * The maximum time WLAN can gain the antenna * in WLAN PSM / BT master/slave BR * * Range: 0 - 255 (ms) */ CONF_SG_ACL_WLAN_PS_MASTER_BR, CONF_SG_ACL_WLAN_PS_SLAVE_BR, /* * The maximum time WLAN can gain the antenna * in WLAN PSM / BT master/slave EDR * * Range: 0 - 255 (ms) */ CONF_SG_ACL_WLAN_PS_MASTER_EDR, CONF_SG_ACL_WLAN_PS_SLAVE_EDR, /* TODO: explain these values */ CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR, CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR, CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR, CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR, CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR, CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR, CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR, CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR, CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR, CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR, CONF_SG_ACL_PASSIVE_SCAN_BT_BR, CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR, CONF_SG_ACL_PASSIVE_SCAN_BT_EDR, CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR, /* * Compensation percentage of probe requests when scan initiated * during BT voice/ACL link. * * Range: 0 - 255 (%) */ CONF_SG_AUTO_SCAN_PROBE_REQ, /* * Compensation percentage of probe requests when active scan initiated * during BT voice * * Range: 0 - 255 (%) */ CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, /* * Compensation percentage of WLAN active scan window if initiated * during BT A2DP * * Range: 0 - 1000 (%) */ CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP, /* * Compensation percentage of WLAN passive scan window if initiated * during BT A2DP BR * * Range: 0 - 1000 (%) */ CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR, /* * Compensation percentage of WLAN passive scan window if initiated * during BT A2DP EDR * * Range: 0 - 1000 (%) */ CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR, /* * Compensation percentage of WLAN passive scan window if initiated * during BT voice * * Range: 0 - 1000 (%) */ CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3, /* TODO: explain these values */ CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN, CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN, CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN, /* * Defines whether the SG will force WLAN host to enter/exit PSM * * Range: 1 - SG can force, 0 - host handles PSM */ CONF_SG_STA_FORCE_PS_IN_BT_SCO, /* * Defines antenna configuration (single/dual antenna) * * Range: 0 - single antenna, 1 - dual antenna */ CONF_SG_ANTENNA_CONFIGURATION, /* * The threshold (percent) of max consecutive beacon misses before * increasing priority of beacon reception. * * Range: 0 - 100 (%) */ CONF_SG_BEACON_MISS_PERCENT, /* * Protection time of the DHCP procedure. * * Range: 0 - 100000 (ms) */ CONF_SG_DHCP_TIME, /* * RX guard time before the beginning of a new BT voice frame during * which no new WLAN trigger frame is transmitted. * * Range: 0 - 100000 (us) */ CONF_SG_RXT, /* * TX guard time before the beginning of a new BT voice frame during * which no new WLAN frame is transmitted. * * Range: 0 - 100000 (us) */ CONF_SG_TXT, /* * Enable adaptive RXT/TXT algorithm. If disabled, the host values * will be utilized. * * Range: 0 - disable, 1 - enable */ CONF_SG_ADAPTIVE_RXT_TXT, /* TODO: explain this value */ CONF_SG_GENERAL_USAGE_BIT_MAP, /* * Number of consecutive BT voice frames not interrupted by WLAN * * Range: 0 - 100 */ CONF_SG_HV3_MAX_SERVED, /* * The used WLAN legacy service period during active BT ACL link * * Range: 0 - 255 (ms) */ CONF_SG_PS_POLL_TIMEOUT, /* * The used WLAN UPSD service period during active BT ACL link * * Range: 0 - 255 (ms) */ CONF_SG_UPSD_TIMEOUT, CONF_SG_CONSECUTIVE_CTS_THRESHOLD, CONF_SG_STA_RX_WINDOW_AFTER_DTIM, CONF_SG_STA_CONNECTION_PROTECTION_TIME, /* AP params */ CONF_AP_BEACON_MISS_TX, CONF_AP_RX_WINDOW_AFTER_BEACON, CONF_AP_BEACON_WINDOW_INTERVAL, CONF_AP_CONNECTION_PROTECTION_TIME, CONF_AP_BT_ACL_VAL_BT_SERVE_TIME, CONF_AP_BT_ACL_VAL_WL_SERVE_TIME, /* CTS Diluting params */ CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH, CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER, CONF_SG_TEMP_PARAM_1, CONF_SG_TEMP_PARAM_2, CONF_SG_TEMP_PARAM_3, CONF_SG_TEMP_PARAM_4, CONF_SG_TEMP_PARAM_5, CONF_SG_TEMP_PARAM_6, CONF_SG_TEMP_PARAM_7, CONF_SG_TEMP_PARAM_8, CONF_SG_TEMP_PARAM_9, CONF_SG_TEMP_PARAM_10, CONF_SG_PARAMS_MAX, CONF_SG_PARAMS_ALL = 0xff }; struct conf_sg_settings { u32 params[CONF_SG_PARAMS_MAX]; u8 state; } __packed; enum conf_rx_queue_type { CONF_RX_QUEUE_TYPE_LOW_PRIORITY, /* All except the high priority */ CONF_RX_QUEUE_TYPE_HIGH_PRIORITY, /* Management and voice packets */ }; struct conf_rx_settings { /* * The maximum amount of time, in TU, before the * firmware discards the MSDU. * * Range: 0 - 0xFFFFFFFF */ u32 rx_msdu_life_time; /* * Packet detection threshold in the PHY. * * FIXME: details unknown. */ u32 packet_detection_threshold; /* * The longest time the STA will wait to receive traffic from the AP * after a PS-poll has been transmitted. * * Range: 0 - 200000 */ u16 ps_poll_timeout; /* * The longest time the STA will wait to receive traffic from the AP * after a frame has been sent from an UPSD enabled queue. * * Range: 0 - 200000 */ u16 upsd_timeout; /* * The number of octets in an MPDU, below which an RTS/CTS * handshake is not performed. * * Range: 0 - 4096 */ u16 rts_threshold; /* * The RX Clear Channel Assessment threshold in the PHY * (the energy threshold). * * Range: ENABLE_ENERGY_D == 0x140A * DISABLE_ENERGY_D == 0xFFEF */ u16 rx_cca_threshold; /* * Occupied Rx mem-blocks number which requires interrupting the host * (0 = no buffering, 0xffff = disabled). * * Range: u16 */ u16 irq_blk_threshold; /* * Rx packets number which requires interrupting the host * (0 = no buffering). * * Range: u16 */ u16 irq_pkt_threshold; /* * Max time in msec the FW may delay RX-Complete interrupt. * * Range: 1 - 100 */ u16 irq_timeout; /* * The RX queue type. * * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY, */ u8 queue_type; } __packed; #define CONF_TX_MAX_RATE_CLASSES 10 #define CONF_TX_RATE_MASK_UNSPECIFIED 0 #define CONF_TX_RATE_MASK_BASIC (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS) #define CONF_TX_RATE_RETRY_LIMIT 10 /* basic rates for p2p operations (probe req/resp, etc.) */ #define CONF_TX_RATE_MASK_BASIC_P2P (CONF_HW_BIT_RATE_6MBPS | \ CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS) /* * Rates supported for data packets when operating as AP. Note the absence * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop * one. The rate dropped is not mandatory under any operating mode. */ #define CONF_TX_AP_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \ CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \ CONF_HW_BIT_RATE_18MBPS | CONF_HW_BIT_RATE_24MBPS | \ CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ CONF_HW_BIT_RATE_54MBPS) #define CONF_TX_CCK_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ CONF_HW_BIT_RATE_11MBPS) #define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \ CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \ CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ CONF_HW_BIT_RATE_54MBPS) #define CONF_TX_MCS_RATES (CONF_HW_BIT_RATE_MCS_0 | \ CONF_HW_BIT_RATE_MCS_1 | CONF_HW_BIT_RATE_MCS_2 | \ CONF_HW_BIT_RATE_MCS_3 | CONF_HW_BIT_RATE_MCS_4 | \ CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 | \ CONF_HW_BIT_RATE_MCS_7) #define CONF_TX_MIMO_RATES (CONF_HW_BIT_RATE_MCS_8 | \ CONF_HW_BIT_RATE_MCS_9 | CONF_HW_BIT_RATE_MCS_10 | \ CONF_HW_BIT_RATE_MCS_11 | CONF_HW_BIT_RATE_MCS_12 | \ CONF_HW_BIT_RATE_MCS_13 | CONF_HW_BIT_RATE_MCS_14 | \ CONF_HW_BIT_RATE_MCS_15) /* * Default rates for management traffic when operating in AP mode. This * should be configured according to the basic rate set of the AP */ #define CONF_TX_AP_DEFAULT_MGMT_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS) /* default rates for working as IBSS (11b and OFDM) */ #define CONF_TX_IBSS_DEFAULT_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ CONF_HW_BIT_RATE_11MBPS | CONF_TX_OFDM_RATES); struct conf_tx_rate_class { /* * The rates enabled for this rate class. * * Range: CONF_HW_BIT_RATE_* bit mask */ u32 enabled_rates; /* * The dot11 short retry limit used for TX retries. * * Range: u8 */ u8 short_retry_limit; /* * The dot11 long retry limit used for TX retries. * * Range: u8 */ u8 long_retry_limit; /* * Flags controlling the attributes of TX transmission. * * Range: bit 0: Truncate - when set, FW attempts to send a frame stop * when the total valid per-rate attempts have * been exhausted; otherwise transmissions * will continue at the lowest available rate * until the appropriate one of the * short_retry_limit, long_retry_limit, * dot11_max_transmit_msdu_life_time, or * max_tx_life_time, is exhausted. * 1: Preamble Override - indicates if the preamble type * should be used in TX. * 2: Preamble Type - the type of the preamble to be used by * the policy (0 - long preamble, 1 - short preamble. */ u8 aflags; } __packed; #define CONF_TX_MAX_AC_COUNT 4 /* Slot number setting to start transmission at PIFS interval */ #define CONF_TX_AIFS_PIFS 1 /* Slot number setting to start transmission at DIFS interval normal * DCF access */ #define CONF_TX_AIFS_DIFS 2 enum conf_tx_ac { CONF_TX_AC_BE = 0, /* best effort / legacy */ CONF_TX_AC_BK = 1, /* background */ CONF_TX_AC_VI = 2, /* video */ CONF_TX_AC_VO = 3, /* voice */ CONF_TX_AC_CTS2SELF = 4, /* fictitious AC, follows AC_VO */ CONF_TX_AC_ANY_TID = 0xff }; struct conf_tx_ac_category { /* * The AC class identifier. * * Range: enum conf_tx_ac */ u8 ac; /* * The contention window minimum size (in slots) for the access * class. * * Range: u8 */ u8 cw_min; /* * The contention window maximum size (in slots) for the access * class. * * Range: u8 */ u16 cw_max; /* * The AIF value (in slots) for the access class. * * Range: u8 */ u8 aifsn; /* * The TX Op Limit (in microseconds) for the access class. * * Range: u16 */ u16 tx_op_limit; } __packed; #define CONF_TX_MAX_TID_COUNT 8 /* Allow TX BA on all TIDs but 6,7. These are currently reserved in the FW */ #define CONF_TX_BA_ENABLED_TID_BITMAP 0x3F enum { CONF_CHANNEL_TYPE_DCF = 0, /* DC/LEGACY*/ CONF_CHANNEL_TYPE_EDCF = 1, /* EDCA*/ CONF_CHANNEL_TYPE_HCCA = 2, /* HCCA*/ }; enum { CONF_PS_SCHEME_LEGACY = 0, CONF_PS_SCHEME_UPSD_TRIGGER = 1, CONF_PS_SCHEME_LEGACY_PSPOLL = 2, CONF_PS_SCHEME_SAPSD = 3, }; enum { CONF_ACK_POLICY_LEGACY = 0, CONF_ACK_POLICY_NO_ACK = 1, CONF_ACK_POLICY_BLOCK = 2, }; struct conf_tx_tid { u8 queue_id; u8 channel_type; u8 tsid; u8 ps_scheme; u8 ack_policy; u32 apsd_conf[2]; } __packed; struct conf_tx_settings { /* * The TX ED value for TELEC Enable/Disable. * * Range: 0, 1 */ u8 tx_energy_detection; /* * Configuration for rate classes for TX (currently only one * rate class supported). Used in non-AP mode. */ struct conf_tx_rate_class sta_rc_conf; /* * Configuration for access categories for TX rate control. */ u8 ac_conf_count; struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; /* * AP-mode - allow this number of TX retries to a station before an * event is triggered from FW. * In AP-mode the hlids of unreachable stations are given in the * "sta_tx_retry_exceeded" member in the event mailbox. */ u8 max_tx_retries; /* * AP-mode - after this number of seconds a connected station is * considered inactive. */ u16 ap_aging_period; /* * Configuration for TID parameters. */ u8 tid_conf_count; struct conf_tx_tid tid_conf[CONF_TX_MAX_TID_COUNT]; /* * The TX fragmentation threshold. * * Range: u16 */ u16 frag_threshold; /* * Max time in msec the FW may delay frame TX-Complete interrupt. * * Range: u16 */ u16 tx_compl_timeout; /* * Completed TX packet count which requires to issue the TX-Complete * interrupt. * * Range: u16 */ u16 tx_compl_threshold; /* * The rate used for control messages and scanning on the 2.4GHz band * * Range: CONF_HW_BIT_RATE_* bit mask */ u32 basic_rate; /* * The rate used for control messages and scanning on the 5GHz band * * Range: CONF_HW_BIT_RATE_* bit mask */ u32 basic_rate_5; /* * TX retry limits for templates */ u8 tmpl_short_retry_limit; u8 tmpl_long_retry_limit; /* Time in ms for Tx watchdog timer to expire */ u32 tx_watchdog_timeout; } __packed; enum { CONF_WAKE_UP_EVENT_BEACON = 0x01, /* Wake on every Beacon*/ CONF_WAKE_UP_EVENT_DTIM = 0x02, /* Wake on every DTIM*/ CONF_WAKE_UP_EVENT_N_DTIM = 0x04, /* Wake every Nth DTIM */ CONF_WAKE_UP_EVENT_N_BEACONS = 0x08, /* Wake every Nth beacon */ CONF_WAKE_UP_EVENT_BITS_MASK = 0x0F }; #define CONF_MAX_BCN_FILT_IE_COUNT 32 #define CONF_BCN_RULE_PASS_ON_CHANGE BIT(0) #define CONF_BCN_RULE_PASS_ON_APPEARANCE BIT(1) #define CONF_BCN_IE_OUI_LEN 3 #define CONF_BCN_IE_VER_LEN 2 struct conf_bcn_filt_rule { /* * IE number to which to associate a rule. * * Range: u8 */ u8 ie; /* * Rule to associate with the specific ie. * * Range: CONF_BCN_RULE_PASS_ON_* */ u8 rule; /* * OUI for the vendor specifie IE (221) */ u8 oui[CONF_BCN_IE_OUI_LEN]; /* * Type for the vendor specifie IE (221) */ u8 type; /* * Version for the vendor specifie IE (221) */ u8 version[CONF_BCN_IE_VER_LEN]; } __packed; #define CONF_MAX_RSSI_SNR_TRIGGERS 8 enum { CONF_TRIG_METRIC_RSSI_BEACON = 0, CONF_TRIG_METRIC_RSSI_DATA, CONF_TRIG_METRIC_SNR_BEACON, CONF_TRIG_METRIC_SNR_DATA }; enum { CONF_TRIG_EVENT_TYPE_LEVEL = 0, CONF_TRIG_EVENT_TYPE_EDGE }; enum { CONF_TRIG_EVENT_DIR_LOW = 0, CONF_TRIG_EVENT_DIR_HIGH, CONF_TRIG_EVENT_DIR_BIDIR }; struct conf_sig_weights { /* * RSSI from beacons average weight. * * Range: u8 */ u8 rssi_bcn_avg_weight; /* * RSSI from data average weight. * * Range: u8 */ u8 rssi_pkt_avg_weight; /* * SNR from beacons average weight. * * Range: u8 */ u8 snr_bcn_avg_weight; /* * SNR from data average weight. * * Range: u8 */ u8 snr_pkt_avg_weight; } __packed; enum conf_bcn_filt_mode { CONF_BCN_FILT_MODE_DISABLED = 0, CONF_BCN_FILT_MODE_ENABLED = 1 }; enum conf_bet_mode { CONF_BET_MODE_DISABLE = 0, CONF_BET_MODE_ENABLE = 1, }; struct conf_conn_settings { /* * Firmware wakeup conditions configuration. The host may set only * one bit. * * Range: CONF_WAKE_UP_EVENT_* */ u8 wake_up_event; /* * Listen interval for beacons or Dtims. * * Range: 0 for beacon and Dtim wakeup * 1-10 for x Dtims * 1-255 for x beacons */ u8 listen_interval; /* * Firmware wakeup conditions during suspend * Range: CONF_WAKE_UP_EVENT_* */ u8 suspend_wake_up_event; /* * Listen interval during suspend. * Currently will be in DTIMs (1-10) * */ u8 suspend_listen_interval; /* * Enable or disable the beacon filtering. * * Range: CONF_BCN_FILT_MODE_* */ u8 bcn_filt_mode; /* * Configure Beacon filter pass-thru rules. */ u8 bcn_filt_ie_count; struct conf_bcn_filt_rule bcn_filt_ie[CONF_MAX_BCN_FILT_IE_COUNT]; /* * The number of consecutive beacons to lose, before the firmware * becomes out of synch. * * Range: u32 */ u32 synch_fail_thold; /* * After out-of-synch, the number of TU's to wait without a further * received beacon (or probe response) before issuing the BSS_EVENT_LOSE * event. * * Range: u32 */ u32 bss_lose_timeout; /* * Beacon receive timeout. * * Range: u32 */ u32 beacon_rx_timeout; /* * Broadcast receive timeout. * * Range: u32 */ u32 broadcast_timeout; /* * Enable/disable reception of broadcast packets in power save mode * * Range: 1 - enable, 0 - disable */ u8 rx_broadcast_in_ps; /* * Consecutive PS Poll failures before sending event to driver * * Range: u8 */ u8 ps_poll_threshold; /* * Configuration of signal average weights. */ struct conf_sig_weights sig_weights; /* * Specifies if beacon early termination procedure is enabled or * disabled. * * Range: CONF_BET_MODE_* */ u8 bet_enable; /* * Specifies the maximum number of consecutive beacons that may be * early terminated. After this number is reached at least one full * beacon must be correctly received in FW before beacon ET * resumes. * * Range 0 - 255 */ u8 bet_max_consecutive; /* * Specifies the maximum number of times to try PSM entry if it fails * (if sending the appropriate null-func message fails.) * * Range 0 - 255 */ u8 psm_entry_retries; /* * Specifies the maximum number of times to try PSM exit if it fails * (if sending the appropriate null-func message fails.) * * Range 0 - 255 */ u8 psm_exit_retries; /* * Specifies the maximum number of times to try transmit the PSM entry * null-func frame for each PSM entry attempt * * Range 0 - 255 */ u8 psm_entry_nullfunc_retries; /* * Specifies the dynamic PS timeout in ms that will be used * by the FW when in AUTO_PS mode */ u16 dynamic_ps_timeout; /* * Specifies whether dynamic PS should be disabled and PSM forced. * This is required for certain WiFi certification tests. */ u8 forced_ps; /* * * Specifies the interval of the connection keep-alive null-func * frame in ms. * * Range: 1000 - 3600000 */ u32 keep_alive_interval; /* * Maximum listen interval supported by the driver in units of beacons. * * Range: u16 */ u8 max_listen_interval; /* * Default sleep authorization for a new STA interface. This determines * whether we can go to ELP. */ u8 sta_sleep_auth; } __packed; enum { CONF_REF_CLK_19_2_E, CONF_REF_CLK_26_E, CONF_REF_CLK_38_4_E, CONF_REF_CLK_52_E, CONF_REF_CLK_38_4_M_XTAL, CONF_REF_CLK_26_M_XTAL, }; enum single_dual_band_enum { CONF_SINGLE_BAND, CONF_DUAL_BAND }; #define CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE 15 #define CONF_NUMBER_OF_SUB_BANDS_5 7 #define CONF_NUMBER_OF_RATE_GROUPS 6 #define CONF_NUMBER_OF_CHANNELS_2_4 14 #define CONF_NUMBER_OF_CHANNELS_5 35 struct conf_itrim_settings { /* enable dco itrim */ u8 enable; /* moderation timeout in microsecs from the last TX */ u32 timeout; } __packed; enum conf_fast_wakeup { CONF_FAST_WAKEUP_ENABLE, CONF_FAST_WAKEUP_DISABLE, }; struct conf_pm_config_settings { /* * Host clock settling time * * Range: 0 - 30000 us */ u32 host_clk_settling_time; /* * Host fast wakeup support * * Range: enum conf_fast_wakeup */ u8 host_fast_wakeup_support; } __packed; struct conf_roam_trigger_settings { /* * The minimum interval between two trigger events. * * Range: 0 - 60000 ms */ u16 trigger_pacing; /* * The weight for rssi/beacon average calculation * * Range: 0 - 255 */ u8 avg_weight_rssi_beacon; /* * The weight for rssi/data frame average calculation * * Range: 0 - 255 */ u8 avg_weight_rssi_data; /* * The weight for snr/beacon average calculation * * Range: 0 - 255 */ u8 avg_weight_snr_beacon; /* * The weight for snr/data frame average calculation * * Range: 0 - 255 */ u8 avg_weight_snr_data; } __packed; struct conf_scan_settings { /* * The minimum time to wait on each channel for active scans * * Range: u32 tu/1000 */ u32 min_dwell_time_active; /* * The maximum time to wait on each channel for active scans * * Range: u32 tu/1000 */ u32 max_dwell_time_active; /* * The minimum time to wait on each channel for passive scans * * Range: u32 tu/1000 */ u32 min_dwell_time_passive; /* * The maximum time to wait on each channel for passive scans * * Range: u32 tu/1000 */ u32 max_dwell_time_passive; /* * Number of probe requests to transmit on each active scan channel * * Range: u8 */ u16 num_probe_reqs; /* * Scan trigger (split scan) timeout. The FW will split the scan * operation into slices of the given time and allow the FW to schedule * other tasks in between. * * Range: u32 Microsecs */ u32 split_scan_timeout; } __packed; struct conf_sched_scan_settings { /* * The base time to wait on the channel for active scans (in TU/1000). * The minimum dwell time is calculated according to this: * min_dwell_time = base + num_of_probes_to_be_sent * delta_per_probe * The maximum dwell time is calculated according to this: * max_dwell_time = min_dwell_time + max_dwell_time_delta */ u32 base_dwell_time; /* The delta between the min dwell time and max dwell time for * active scans (in TU/1000s). The max dwell time is used by the FW once * traffic is detected on the channel. */ u32 max_dwell_time_delta; /* Delta added to min dwell time per each probe in 2.4 GHz (TU/1000) */ u32 dwell_time_delta_per_probe; /* Delta added to min dwell time per each probe in 5 GHz (TU/1000) */ u32 dwell_time_delta_per_probe_5; /* time to wait on the channel for passive scans (in TU/1000) */ u32 dwell_time_passive; /* time to wait on the channel for DFS scans (in TU/1000) */ u32 dwell_time_dfs; /* number of probe requests to send on each channel in active scans */ u8 num_probe_reqs; /* RSSI threshold to be used for filtering */ s8 rssi_threshold; /* SNR threshold to be used for filtering */ s8 snr_threshold; } __packed; struct conf_ht_setting { u8 rx_ba_win_size; u8 tx_ba_win_size; u16 inactivity_timeout; /* bitmap of enabled TIDs for TX BA sessions */ u8 tx_ba_tid_bitmap; } __packed; struct conf_memory_settings { /* Number of stations supported in IBSS mode */ u8 num_stations; /* Number of ssid profiles used in IBSS mode */ u8 ssid_profiles; /* Number of memory buffers allocated to rx pool */ u8 rx_block_num; /* Minimum number of blocks allocated to tx pool */ u8 tx_min_block_num; /* Disable/Enable dynamic memory */ u8 dynamic_memory; /* * Minimum required free tx memory blocks in order to assure optimum * performance * * Range: 0-120 */ u8 min_req_tx_blocks; /* * Minimum required free rx memory blocks in order to assure optimum * performance * * Range: 0-120 */ u8 min_req_rx_blocks; /* * Minimum number of mem blocks (free+used) guaranteed for TX * * Range: 0-120 */ u8 tx_min; } __packed; struct conf_fm_coex { u8 enable; u8 swallow_period; u8 n_divider_fref_set_1; u8 n_divider_fref_set_2; u16 m_divider_fref_set_1; u16 m_divider_fref_set_2; u32 coex_pll_stabilization_time; u16 ldo_stabilization_time; u8 fm_disturbed_band_margin; u8 swallow_clk_diff; } __packed; struct conf_rx_streaming_settings { /* * RX Streaming duration (in msec) from last tx/rx * * Range: u32 */ u32 duration; /* * Bitmap of tids to be polled during RX streaming. * (Note: it doesn't look like it really matters) * * Range: 0x1-0xff */ u8 queues; /* * RX Streaming interval. * (Note:this value is also used as the rx streaming timeout) * Range: 0 (disabled), 10 - 100 */ u8 interval; /* * enable rx streaming also when there is no coex activity */ u8 always; } __packed; struct conf_fwlog { /* Continuous or on-demand */ u8 mode; /* * Number of memory blocks dedicated for the FW logger * * Range: 1-3, or 0 to disable the FW logger */ u8 mem_blocks; /* Minimum log level threshold */ u8 severity; /* Include/exclude timestamps from the log messages */ u8 timestamp; /* See enum wl1271_fwlogger_output */ u8 output; /* Regulates the frequency of log messages */ u8 threshold; } __packed; #define ACX_RATE_MGMT_NUM_OF_RATES 13 struct conf_rate_policy_settings { u16 rate_retry_score; u16 per_add; u16 per_th1; u16 per_th2; u16 max_per; u8 inverse_curiosity_factor; u8 tx_fail_low_th; u8 tx_fail_high_th; u8 per_alpha_shift; u8 per_add_shift; u8 per_beta1_shift; u8 per_beta2_shift; u8 rate_check_up; u8 rate_check_down; u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES]; } __packed; struct conf_hangover_settings { u32 recover_time; u8 hangover_period; u8 dynamic_mode; u8 early_termination_mode; u8 max_period; u8 min_period; u8 increase_delta; u8 decrease_delta; u8 quiet_time; u8 increase_time; u8 window_size; } __packed; /* * The conf version consists of 4 bytes. The two MSB are the wlcore * version, the two LSB are the lower driver's private conf * version. */ #define WLCORE_CONF_VERSION (0x0002 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) struct wlcore_conf_header { __le32 magic; __le32 version; __le32 checksum; } __packed; struct wlcore_conf { struct conf_sg_settings sg; struct conf_rx_settings rx; struct conf_tx_settings tx; struct conf_conn_settings conn; struct conf_itrim_settings itrim; struct conf_pm_config_settings pm_config; struct conf_roam_trigger_settings roam_trigger; struct conf_scan_settings scan; struct conf_sched_scan_settings sched_scan; struct conf_ht_setting ht; struct conf_memory_settings mem; struct conf_fm_coex fm_coex; struct conf_rx_streaming_settings rx_streaming; struct conf_fwlog fwlog; struct conf_rate_policy_settings rate; struct conf_hangover_settings hangover; } __packed; struct wlcore_conf_file { struct wlcore_conf_header header; struct wlcore_conf core; u8 priv[0]; } __packed; #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/cmd.h0000644000175000017500000004055112026211315023311 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __CMD_H__ #define __CMD_H__ #include "wlcore.h" struct acx_header; int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, size_t res_len); int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type, u8 *role_id); int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id); int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ps_mode, u16 auto_ps_timeout); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, u16 template_id, void *buf, size_t buf_len, int index, u32 rates); int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid); int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool sched_scan); struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb); int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif); int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_set_default_wep_key(struct wl1271 *wl, u8 id, u8 hlid); int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, const u8 *addr, u32 tx_seq_32, u16 tx_seq_16); int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16); int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid); int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id); int wl12xx_croc(struct wl1271 *wl, u8 role_id); int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u8 hlid); int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid); int wl12xx_cmd_config_fwlog(struct wl1271 *wl); int wl12xx_cmd_start_fwlog(struct wl1271 *wl); int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); int wl12xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl); int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); enum wl1271_commands { CMD_INTERROGATE = 1, /* use this to read information elements */ CMD_CONFIGURE = 2, /* use this to write information elements */ CMD_ENABLE_RX = 3, CMD_ENABLE_TX = 4, CMD_DISABLE_RX = 5, CMD_DISABLE_TX = 6, CMD_SCAN = 7, CMD_STOP_SCAN = 8, CMD_SET_KEYS = 9, CMD_READ_MEMORY = 10, CMD_WRITE_MEMORY = 11, CMD_SET_TEMPLATE = 12, CMD_TEST = 13, CMD_NOISE_HIST = 14, CMD_QUIET_ELEMENT_SET_STATE = 15, CMD_SET_BCN_MODE = 16, CMD_MEASUREMENT = 17, CMD_STOP_MEASUREMENT = 18, CMD_SET_PS_MODE = 19, CMD_CHANNEL_SWITCH = 20, CMD_STOP_CHANNEL_SWICTH = 21, CMD_AP_DISCOVERY = 22, CMD_STOP_AP_DISCOVERY = 23, CMD_HEALTH_CHECK = 24, CMD_DEBUG = 25, CMD_TRIGGER_SCAN_TO = 26, CMD_CONNECTION_SCAN_CFG = 27, CMD_CONNECTION_SCAN_SSID_CFG = 28, CMD_START_PERIODIC_SCAN = 29, CMD_STOP_PERIODIC_SCAN = 30, CMD_SET_PEER_STATE = 31, CMD_REMAIN_ON_CHANNEL = 32, CMD_CANCEL_REMAIN_ON_CHANNEL = 33, CMD_CONFIG_FWLOGGER = 34, CMD_START_FWLOGGER = 35, CMD_STOP_FWLOGGER = 36, /* Access point commands */ CMD_ADD_PEER = 37, CMD_REMOVE_PEER = 38, /* Role API */ CMD_ROLE_ENABLE = 39, CMD_ROLE_DISABLE = 40, CMD_ROLE_START = 41, CMD_ROLE_STOP = 42, /* DFS */ CMD_START_RADAR_DETECTION = 43, CMD_STOP_RADAR_DETECTION = 44, /* WIFI Direct */ CMD_WFD_START_DISCOVERY = 45, CMD_WFD_STOP_DISCOVERY = 46, CMD_WFD_ATTRIBUTE_CONFIG = 47, CMD_NOP = 48, CMD_LAST_COMMAND, MAX_COMMAND_ID = 0xFFFF, }; #define MAX_CMD_PARAMS 572 enum { CMD_TEMPL_KLV_IDX_NULL_DATA = 0, CMD_TEMPL_KLV_IDX_MAX = 4 }; enum cmd_templ { CMD_TEMPL_NULL_DATA = 0, CMD_TEMPL_BEACON, CMD_TEMPL_CFG_PROBE_REQ_2_4, CMD_TEMPL_CFG_PROBE_REQ_5, CMD_TEMPL_PROBE_RESPONSE, CMD_TEMPL_QOS_NULL_DATA, CMD_TEMPL_PS_POLL, CMD_TEMPL_KLV, CMD_TEMPL_DISCONNECT, CMD_TEMPL_APP_PROBE_REQ_2_4, CMD_TEMPL_APP_PROBE_REQ_5, CMD_TEMPL_BAR, /* for firmware internal use only */ CMD_TEMPL_CTS, /* * For CTS-to-self (FastCTS) mechanism * for BT/WLAN coexistence (SoftGemini). */ CMD_TEMPL_AP_BEACON, CMD_TEMPL_AP_PROBE_RESPONSE, CMD_TEMPL_ARP_RSP, CMD_TEMPL_DEAUTH_AP, CMD_TEMPL_TEMPORARY, CMD_TEMPL_LINK_MEASUREMENT_REPORT, CMD_TEMPL_MAX = 0xff }; /* unit ms */ #define WL1271_COMMAND_TIMEOUT 2000 #define WL1271_CMD_TEMPL_DFLT_SIZE 252 #define WL1271_CMD_TEMPL_MAX_SIZE 512 #define WL1271_EVENT_TIMEOUT 1500 struct wl1271_cmd_header { __le16 id; __le16 status; /* payload */ u8 data[0]; } __packed; #define WL1271_CMD_MAX_PARAMS 572 struct wl1271_command { struct wl1271_cmd_header header; u8 parameters[WL1271_CMD_MAX_PARAMS]; } __packed; enum { CMD_MAILBOX_IDLE = 0, CMD_STATUS_SUCCESS = 1, CMD_STATUS_UNKNOWN_CMD = 2, CMD_STATUS_UNKNOWN_IE = 3, CMD_STATUS_REJECT_MEAS_SG_ACTIVE = 11, CMD_STATUS_RX_BUSY = 13, CMD_STATUS_INVALID_PARAM = 14, CMD_STATUS_TEMPLATE_TOO_LARGE = 15, CMD_STATUS_OUT_OF_MEMORY = 16, CMD_STATUS_STA_TABLE_FULL = 17, CMD_STATUS_RADIO_ERROR = 18, CMD_STATUS_WRONG_NESTING = 19, CMD_STATUS_TIMEOUT = 21, /* Driver internal use.*/ CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/ CMD_STATUS_TEMPLATE_OOM = 23, CMD_STATUS_NO_RX_BA_SESSION = 24, MAX_COMMAND_STATUS = 0xff }; #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 enum { BSS_TYPE_IBSS = 0, BSS_TYPE_STA_BSS = 2, BSS_TYPE_AP_BSS = 3, MAX_BSS_TYPE = 0xFF }; #define WL1271_JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */ #define WL1271_JOIN_CMD_TX_SESSION_OFFSET 1 #define WL1271_JOIN_CMD_BSS_TYPE_5GHZ 0x10 struct wl12xx_cmd_role_enable { struct wl1271_cmd_header header; u8 role_id; u8 role_type; u8 mac_address[ETH_ALEN]; } __packed; struct wl12xx_cmd_role_disable { struct wl1271_cmd_header header; u8 role_id; u8 padding[3]; } __packed; enum wlcore_band { WLCORE_BAND_2_4GHZ = 0, WLCORE_BAND_5GHZ = 1, WLCORE_BAND_JAPAN_4_9_GHZ = 2, WLCORE_BAND_DEFAULT = WLCORE_BAND_2_4GHZ, WLCORE_BAND_INVALID = 0x7E, WLCORE_BAND_MAX_RADIO = 0x7F, }; enum wlcore_channel_type { WLCORE_CHAN_NO_HT, WLCORE_CHAN_HT20, WLCORE_CHAN_HT40MINUS, WLCORE_CHAN_HT40PLUS }; struct wl12xx_cmd_role_start { struct wl1271_cmd_header header; u8 role_id; u8 band; u8 channel; /* enum wlcore_channel_type */ u8 channel_type; union { struct { u8 hlid; u8 session; u8 padding_1[54]; } __packed device; /* sta & p2p_cli use the same struct */ struct { u8 bssid[ETH_ALEN]; u8 hlid; /* data hlid */ u8 session; __le32 remote_rates; /* remote supported rates */ /* * The target uses this field to determine the rate at * which to transmit control frame responses (such as * ACK or CTS frames). */ __le32 basic_rate_set; __le32 local_rates; /* local supported rates */ u8 ssid_type; u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; __le16 beacon_interval; /* in TBTTs */ } __packed sta; struct { u8 bssid[ETH_ALEN]; u8 hlid; /* data hlid */ u8 dtim_interval; __le32 remote_rates; /* remote supported rates */ __le32 basic_rate_set; __le32 local_rates; /* local supported rates */ u8 ssid_type; u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; __le16 beacon_interval; /* in TBTTs */ u8 padding_1[4]; } __packed ibss; /* ap & p2p_go use the same struct */ struct { __le16 aging_period; /* in secs */ u8 beacon_expiry; /* in ms */ u8 bss_index; /* The host link id for the AP's global queue */ u8 global_hlid; /* The host link id for the AP's broadcast queue */ u8 broadcast_hlid; __le16 beacon_interval; /* in TBTTs */ __le32 basic_rate_set; __le32 local_rates; /* local supported rates */ u8 dtim_interval; u8 ssid_type; u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 reset_tsf; u8 padding_1[4]; } __packed ap; }; } __packed; struct wl12xx_cmd_role_stop { struct wl1271_cmd_header header; u8 role_id; u8 disc_type; /* only STA and P2P_CLI */ __le16 reason; /* only STA and P2P_CLI */ } __packed; struct cmd_enabledisable_path { struct wl1271_cmd_header header; u8 channel; u8 padding[3]; } __packed; #define WL1271_RATE_AUTOMATIC 0 struct wl1271_cmd_template_set { struct wl1271_cmd_header header; u8 role_id; u8 template_type; __le16 len; u8 index; /* relevant only for KLV_TEMPLATE type */ u8 padding[3]; __le32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; u8 reserved; u8 template_data[WL1271_CMD_TEMPL_MAX_SIZE]; } __packed; #define TIM_ELE_ID 5 #define PARTIAL_VBM_MAX 251 struct wl1271_tim { u8 identity; u8 length; u8 dtim_count; u8 dtim_period; u8 bitmap_ctrl; u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */ } __packed; enum wl1271_cmd_ps_mode { STATION_AUTO_PS_MODE, /* Dynamic Power Save */ STATION_ACTIVE_MODE, STATION_POWER_SAVE_MODE }; struct wl1271_cmd_ps_params { struct wl1271_cmd_header header; u8 role_id; u8 ps_mode; /* STATION_* */ u16 auto_ps_timeout; } __packed; /* HW encryption keys */ #define NUM_ACCESS_CATEGORIES_COPY 4 enum wl1271_cmd_key_action { KEY_ADD_OR_REPLACE = 1, KEY_REMOVE = 2, KEY_SET_ID = 3, MAX_KEY_ACTION = 0xffff, }; enum wl1271_cmd_lid_key_type { UNICAST_LID_TYPE = 0, BROADCAST_LID_TYPE = 1, WEP_DEFAULT_LID_TYPE = 2 }; enum wl1271_cmd_key_type { KEY_NONE = 0, KEY_WEP = 1, KEY_TKIP = 2, KEY_AES = 3, KEY_GEM = 4, }; struct wl1271_cmd_set_keys { struct wl1271_cmd_header header; /* * Indicates whether the HLID is a unicast key set * or broadcast key set. A special value 0xFF is * used to indicate that the HLID is on WEP-default * (multi-hlids). of type wl1271_cmd_lid_key_type. */ u8 hlid; /* * In WEP-default network (hlid == 0xFF) used to * indicate which network STA/IBSS/AP role should be * changed */ u8 lid_key_type; /* * Key ID - For TKIP and AES key types, this field * indicates the value that should be inserted into * the KeyID field of frames transmitted using this * key entry. For broadcast keys the index use as a * marker for TX/RX key. * For WEP default network (HLID=0xFF), this field * indicates the ID of the key to add or remove. */ u8 key_id; u8 reserved_1; /* key_action_e */ __le16 key_action; /* key size in bytes */ u8 key_size; /* key_type_e */ u8 key_type; /* This field holds the security key data to add to the STA table */ u8 key[MAX_KEY_SIZE]; __le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; } __packed; struct wl1271_cmd_test_header { u8 id; u8 padding[3]; } __packed; enum wl1271_channel_tune_bands { WL1271_CHANNEL_TUNE_BAND_2_4, WL1271_CHANNEL_TUNE_BAND_5, WL1271_CHANNEL_TUNE_BAND_4_9 }; #define WL1271_PD_REFERENCE_POINT_BAND_B_G 0 /* * There are three types of disconnections: * * DISCONNECT_IMMEDIATE: the fw doesn't send any frames * DISCONNECT_DEAUTH: the fw generates a DEAUTH request with the reason * we have passed * DISCONNECT_DISASSOC: the fw generates a DESASSOC request with the reason * we have passed */ enum wl1271_disconnect_type { DISCONNECT_IMMEDIATE, DISCONNECT_DEAUTH, DISCONNECT_DISASSOC }; #define WL1271_CMD_STA_STATE_CONNECTED 1 struct wl12xx_cmd_set_peer_state { struct wl1271_cmd_header header; u8 hlid; u8 state; u8 padding[2]; } __packed; struct wl12xx_cmd_roc { struct wl1271_cmd_header header; u8 role_id; u8 channel; u8 band; u8 padding; }; struct wl12xx_cmd_croc { struct wl1271_cmd_header header; u8 role_id; u8 padding[3]; }; enum wl12xx_ssid_type { WL12XX_SSID_TYPE_PUBLIC = 0, WL12XX_SSID_TYPE_HIDDEN = 1, WL12XX_SSID_TYPE_ANY = 2, }; enum wl1271_psd_type { WL1271_PSD_LEGACY = 0, WL1271_PSD_UPSD_TRIGGER = 1, WL1271_PSD_LEGACY_PSPOLL = 2, WL1271_PSD_SAPSD = 3 }; struct wl12xx_cmd_add_peer { struct wl1271_cmd_header header; u8 addr[ETH_ALEN]; u8 hlid; u8 aid; u8 psd_type[NUM_ACCESS_CATEGORIES_COPY]; __le32 supported_rates; u8 bss_index; u8 sp_len; u8 wmm; u8 padding1; } __packed; struct wl12xx_cmd_remove_peer { struct wl1271_cmd_header header; u8 hlid; u8 reason_opcode; u8 send_deauth_flag; u8 padding1; } __packed; /* * Continuous mode - packets are transferred to the host periodically * via the data path. * On demand - Log messages are stored in a cyclic buffer in the * firmware, and only transferred to the host when explicitly requested */ enum wl12xx_fwlogger_log_mode { WL12XX_FWLOG_CONTINUOUS, WL12XX_FWLOG_ON_DEMAND }; /* Include/exclude timestamps from the log messages */ enum wl12xx_fwlogger_timestamp { WL12XX_FWLOG_TIMESTAMP_DISABLED, WL12XX_FWLOG_TIMESTAMP_ENABLED }; /* * Logs can be routed to the debug pinouts (where available), to the host bus * (SDIO/SPI), or dropped */ enum wl12xx_fwlogger_output { WL12XX_FWLOG_OUTPUT_NONE, WL12XX_FWLOG_OUTPUT_DBG_PINS, WL12XX_FWLOG_OUTPUT_HOST, }; struct wl12xx_cmd_config_fwlog { struct wl1271_cmd_header header; /* See enum wl12xx_fwlogger_log_mode */ u8 logger_mode; /* Minimum log level threshold */ u8 log_severity; /* Include/exclude timestamps from the log messages */ u8 timestamp; /* See enum wl1271_fwlogger_output */ u8 output; /* Regulates the frequency of log messages */ u8 threshold; u8 padding[3]; } __packed; struct wl12xx_cmd_start_fwlog { struct wl1271_cmd_header header; } __packed; struct wl12xx_cmd_stop_fwlog { struct wl1271_cmd_header header; } __packed; struct wl12xx_cmd_channel_switch { struct wl1271_cmd_header header; u8 role_id; /* The new serving channel */ u8 channel; /* Relative time of the serving channel switch in TBTT units */ u8 switch_time; /* Stop the role TX, should expect it after radar detection */ u8 stop_tx; /* The target channel tx status 1-stopped 0-open*/ u8 post_switch_tx_disable; u8 padding[3]; } __packed; struct wl12xx_cmd_stop_channel_switch { struct wl1271_cmd_header header; } __packed; /* Used to check radio status after calibration */ #define MAX_TLV_LENGTH 500 #define TEST_CMD_P2G_CAL 2 /* TX BiP */ struct wl1271_cmd_cal_p2g { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; __le32 ver; __le16 len; u8 buf[MAX_TLV_LENGTH]; u8 type; u8 padding; __le16 radio_status; u8 sub_band_mask; u8 padding2; } __packed; #endif /* __WL1271_CMD_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/cmd.c0000644000175000017500000012221512026211315023302 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2009-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include #include "wlcore.h" #include "debug.h" #include "io.h" #include "acx.h" #include "wl12xx_80211.h" #include "cmd.h" #include "event.h" #include "tx.h" #include "hw_ops.h" #define WL1271_CMD_FAST_POLL_COUNT 50 #define WL1271_WAIT_EVENT_FAST_POLL_COUNT 20 /* * send command to firmware * * @wl: wl struct * @id: command id * @buf: buffer containing the command, must work with dma * @len: length of the buffer */ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, size_t res_len) { struct wl1271_cmd_header *cmd; unsigned long timeout; u32 intr; int ret = 0; u16 status; u16 poll_count = 0; cmd = buf; cmd->id = cpu_to_le16(id); cmd->status = 0; WARN_ON(len % 4 != 0); WARN_ON(test_bit(WL1271_FLAG_IN_ELP, &wl->flags)); ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false); if (ret < 0) goto fail; /* * TODO: we just need this because one bit is in a different * place. Is there any better way? */ ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len); if (ret < 0) goto fail; timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) goto fail; while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); ret = -ETIMEDOUT; goto fail; } poll_count++; if (poll_count < WL1271_CMD_FAST_POLL_COUNT) udelay(10); else msleep(1); ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) goto fail; } /* read back the status code of the command */ if (res_len == 0) res_len = sizeof(struct wl1271_cmd_header); ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false); if (ret < 0) goto fail; status = le16_to_cpu(cmd->status); if (status != CMD_STATUS_SUCCESS) { wl1271_error("command execute failure %d", status); ret = -EIO; goto fail; } ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); if (ret < 0) goto fail; return 0; fail: wl12xx_queue_recovery_work(wl); return ret; } /* * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1271_EVENT_TIMEOUT in msecs) */ static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask, bool *timeout) { u32 *events_vector; u32 event; unsigned long timeout_time; u16 poll_count = 0; int ret = 0; *timeout = false; events_vector = kmalloc(sizeof(*events_vector), GFP_KERNEL | GFP_DMA); if (!events_vector) return -ENOMEM; timeout_time = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT); do { if (time_after(jiffies, timeout_time)) { wl1271_debug(DEBUG_CMD, "timeout waiting for event %d", (int)mask); *timeout = true; goto out; } poll_count++; if (poll_count < WL1271_WAIT_EVENT_FAST_POLL_COUNT) usleep_range(50, 51); else usleep_range(1000, 5000); /* read from both event fields */ ret = wlcore_read(wl, wl->mbox_ptr[0], events_vector, sizeof(*events_vector), false); if (ret < 0) goto out; event = *events_vector & mask; ret = wlcore_read(wl, wl->mbox_ptr[1], events_vector, sizeof(*events_vector), false); if (ret < 0) goto out; event |= *events_vector & mask; } while (!event); out: kfree(events_vector); return ret; } static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) { int ret; bool timeout = false; ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout); if (ret != 0 || timeout) { wl12xx_queue_recovery_work(wl); return ret; } return 0; } int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type, u8 *role_id) { struct wl12xx_cmd_role_enable *cmd; int ret; wl1271_debug(DEBUG_CMD, "cmd role enable"); if (WARN_ON(*role_id != WL12XX_INVALID_ROLE_ID)) return -EBUSY; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } /* get role id */ cmd->role_id = find_first_zero_bit(wl->roles_map, WL12XX_MAX_ROLES); if (cmd->role_id >= WL12XX_MAX_ROLES) { ret = -EBUSY; goto out_free; } memcpy(cmd->mac_address, addr, ETH_ALEN); cmd->role_type = role_type; ret = wl1271_cmd_send(wl, CMD_ROLE_ENABLE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role enable"); goto out_free; } __set_bit(cmd->role_id, wl->roles_map); *role_id = cmd->role_id; out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id) { struct wl12xx_cmd_role_disable *cmd; int ret; wl1271_debug(DEBUG_CMD, "cmd role disable"); if (WARN_ON(*role_id == WL12XX_INVALID_ROLE_ID)) return -ENOENT; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->role_id = *role_id; ret = wl1271_cmd_send(wl, CMD_ROLE_DISABLE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role disable"); goto out_free; } __clear_bit(*role_id, wl->roles_map); *role_id = WL12XX_INVALID_ROLE_ID; out_free: kfree(cmd); out: return ret; } int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { unsigned long flags; u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS); if (link >= WL12XX_MAX_LINKS) return -EBUSY; /* these bits are used by op_tx */ spin_lock_irqsave(&wl->wl_lock, flags); __set_bit(link, wl->links_map); __set_bit(link, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); *hlid = link; return 0; } void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { unsigned long flags; if (*hlid == WL12XX_INVALID_LINK_ID) return; /* these bits are used by op_tx */ spin_lock_irqsave(&wl->wl_lock, flags); __clear_bit(*hlid, wl->links_map); __clear_bit(*hlid, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); /* * At this point op_tx() will not add more packets to the queues. We * can purge them. */ wl1271_tx_reset_link_queues(wl, *hlid); *hlid = WL12XX_INVALID_LINK_ID; } static int wl12xx_get_new_session_id(struct wl1271 *wl, struct wl12xx_vif *wlvif) { if (wlvif->session_counter >= SESSION_COUNTER_MAX) wlvif->session_counter = 0; wlvif->session_counter++; return wlvif->session_counter; } static u8 wlcore_get_native_channel_type(u8 nl_channel_type) { switch (nl_channel_type) { case NL80211_CHAN_NO_HT: return WLCORE_CHAN_NO_HT; case NL80211_CHAN_HT20: return WLCORE_CHAN_HT20; case NL80211_CHAN_HT40MINUS: return WLCORE_CHAN_HT40MINUS; case NL80211_CHAN_HT40PLUS: return WLCORE_CHAN_HT40PLUS; default: WARN_ON(1); return WLCORE_CHAN_NO_HT; } } static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_role_start *cmd; int ret; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id); cmd->role_id = wlvif->dev_role_id; if (wlvif->band == IEEE80211_BAND_5GHZ) cmd->band = WLCORE_BAND_5GHZ; cmd->channel = wlvif->channel; if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) { ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid); if (ret) goto out_free; } cmd->device.hlid = wlvif->dev_hlid; cmd->device.session = wl12xx_get_new_session_id(wl, wlvif); wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d", cmd->role_id, cmd->device.hlid, cmd->device.session); ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role enable"); goto err_hlid; } goto out_free; err_hlid: /* clear links on error */ wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid); out_free: kfree(cmd); out: return ret; } static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_role_stop *cmd; int ret; if (WARN_ON(wlvif->dev_hlid == WL12XX_INVALID_LINK_ID)) return -EINVAL; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_CMD, "cmd role stop dev"); cmd->role_id = wlvif->dev_role_id; cmd->disc_type = DISCONNECT_IMMEDIATE; cmd->reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED); ret = wl1271_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role stop"); goto out_free; } ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID); if (ret < 0) { wl1271_error("cmd role stop dev event completion error"); goto out_free; } wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid); out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct wl12xx_cmd_role_start *cmd; int ret; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_CMD, "cmd role start sta %d", wlvif->role_id); cmd->role_id = wlvif->role_id; if (wlvif->band == IEEE80211_BAND_5GHZ) cmd->band = WLCORE_BAND_5GHZ; cmd->channel = wlvif->channel; cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set); cmd->sta.beacon_interval = cpu_to_le16(wlvif->beacon_int); cmd->sta.ssid_type = WL12XX_SSID_TYPE_ANY; cmd->sta.ssid_len = wlvif->ssid_len; memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len); memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN); cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set); cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) { ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid); if (ret) goto out_free; } cmd->sta.hlid = wlvif->sta.hlid; cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif); cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set); wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d " "basic_rate_set: 0x%x, remote_rates: 0x%x", wlvif->role_id, cmd->sta.hlid, cmd->sta.session, wlvif->basic_rate_set, wlvif->rate_set); ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role start sta"); goto err_hlid; } goto out_free; err_hlid: /* clear links on error. */ wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid); out_free: kfree(cmd); out: return ret; } /* use this function to stop ibss as well */ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_role_stop *cmd; int ret; bool timeout = false; if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)) return -EINVAL; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_CMD, "cmd role stop sta %d", wlvif->role_id); cmd->role_id = wlvif->role_id; cmd->disc_type = DISCONNECT_IMMEDIATE; cmd->reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED); ret = wl1271_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role stop sta"); goto out_free; } /* * Sometimes the firmware doesn't send this event, so we just * time out without failing. Queue recovery for other * failures. */ ret = wl1271_cmd_wait_for_event_or_timeout(wl, ROLE_STOP_COMPLETE_EVENT_ID, &timeout); if (ret) wl12xx_queue_recovery_work(wl); wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid); out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_role_start *cmd; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; u32 supported_rates; int ret; wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id); /* trying to use hidden SSID with an old hostapd version */ if (wlvif->ssid_len == 0 && !bss_conf->hidden_ssid) { wl1271_error("got a null SSID from beacon/bss"); ret = -EINVAL; goto out; } cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.global_hlid); if (ret < 0) goto out_free; ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.bcast_hlid); if (ret < 0) goto out_free_global; cmd->role_id = wlvif->role_id; cmd->ap.aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period); cmd->ap.bss_index = WL1271_AP_BSS_INDEX; cmd->ap.global_hlid = wlvif->ap.global_hlid; cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid; cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set); cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int); cmd->ap.dtim_interval = bss_conf->dtim_period; cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP; /* FIXME: Change when adding DFS */ cmd->ap.reset_tsf = 1; /* By default reset AP TSF */ cmd->channel = wlvif->channel; cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); if (!bss_conf->hidden_ssid) { /* take the SSID from the beacon for backward compatibility */ cmd->ap.ssid_type = WL12XX_SSID_TYPE_PUBLIC; cmd->ap.ssid_len = wlvif->ssid_len; memcpy(cmd->ap.ssid, wlvif->ssid, wlvif->ssid_len); } else { cmd->ap.ssid_type = WL12XX_SSID_TYPE_HIDDEN; cmd->ap.ssid_len = bss_conf->ssid_len; memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len); } supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES | wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x", supported_rates); cmd->ap.local_rates = cpu_to_le32(supported_rates); switch (wlvif->band) { case IEEE80211_BAND_2GHZ: cmd->band = WLCORE_BAND_2_4GHZ; break; case IEEE80211_BAND_5GHZ: cmd->band = WLCORE_BAND_5GHZ; break; default: wl1271_warning("ap start - unknown band: %d", (int)wlvif->band); cmd->band = WLCORE_BAND_2_4GHZ; break; } ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role start ap"); goto out_free_bcast; } goto out_free; out_free_bcast: wl12xx_free_link(wl, wlvif, &wlvif->ap.bcast_hlid); out_free_global: wl12xx_free_link(wl, wlvif, &wlvif->ap.global_hlid); out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_role_stop *cmd; int ret; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_CMD, "cmd role stop ap %d", wlvif->role_id); cmd->role_id = wlvif->role_id; ret = wl1271_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role stop ap"); goto out_free; } wl12xx_free_link(wl, wlvif, &wlvif->ap.bcast_hlid); wl12xx_free_link(wl, wlvif, &wlvif->ap.global_hlid); out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct wl12xx_cmd_role_start *cmd; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; int ret; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_CMD, "cmd role start ibss %d", wlvif->role_id); cmd->role_id = wlvif->role_id; if (wlvif->band == IEEE80211_BAND_5GHZ) cmd->band = WLCORE_BAND_5GHZ; cmd->channel = wlvif->channel; cmd->ibss.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set); cmd->ibss.beacon_interval = cpu_to_le16(wlvif->beacon_int); cmd->ibss.dtim_interval = bss_conf->dtim_period; cmd->ibss.ssid_type = WL12XX_SSID_TYPE_ANY; cmd->ibss.ssid_len = wlvif->ssid_len; memcpy(cmd->ibss.ssid, wlvif->ssid, wlvif->ssid_len); memcpy(cmd->ibss.bssid, vif->bss_conf.bssid, ETH_ALEN); cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set); if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) { ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid); if (ret) goto out_free; } cmd->ibss.hlid = wlvif->sta.hlid; cmd->ibss.remote_rates = cpu_to_le32(wlvif->rate_set); wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d " "basic_rate_set: 0x%x, remote_rates: 0x%x", wlvif->role_id, cmd->sta.hlid, cmd->sta.session, wlvif->basic_rate_set, wlvif->rate_set); wl1271_debug(DEBUG_CMD, "vif->bss_conf.bssid = %pM", vif->bss_conf.bssid); ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd role enable"); goto err_hlid; } goto out_free; err_hlid: /* clear links on error. */ wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid); out_free: kfree(cmd); out: return ret; } /** * send test command to firmware * * @wl: wl struct * @buf: buffer containing the command, with all headers, must work with dma * @len: length of the buffer * @answer: is answer needed */ int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer) { int ret; size_t res_len = 0; wl1271_debug(DEBUG_CMD, "cmd test"); if (answer) res_len = buf_len; ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len, res_len); if (ret < 0) { wl1271_warning("TEST command failed"); return ret; } return ret; } EXPORT_SYMBOL_GPL(wl1271_cmd_test); /** * read acx from firmware * * @wl: wl struct * @id: acx id * @buf: buffer for the response, including all headers, must work with dma * @len: length of buf */ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) { struct acx_header *acx = buf; int ret; wl1271_debug(DEBUG_CMD, "cmd interrogate"); acx->id = cpu_to_le16(id); /* payload length, does not include any headers */ acx->len = cpu_to_le16(len - sizeof(*acx)); ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len); if (ret < 0) wl1271_error("INTERROGATE command failed"); return ret; } /** * write acx value to firmware * * @wl: wl struct * @id: acx id * @buf: buffer containing acx, including all headers, must work with dma * @len: length of buf */ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) { struct acx_header *acx = buf; int ret; wl1271_debug(DEBUG_CMD, "cmd configure (%d)", id); acx->id = cpu_to_le16(id); /* payload length, does not include any headers */ acx->len = cpu_to_le16(len - sizeof(*acx)); ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0); if (ret < 0) { wl1271_warning("CONFIGURE command NOK"); return ret; } return 0; } EXPORT_SYMBOL_GPL(wl1271_cmd_configure); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable) { struct cmd_enabledisable_path *cmd; int ret; u16 cmd_rx, cmd_tx; wl1271_debug(DEBUG_CMD, "cmd data path"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } /* the channel here is only used for calibration, so hardcoded to 1 */ cmd->channel = 1; if (enable) { cmd_rx = CMD_ENABLE_RX; cmd_tx = CMD_ENABLE_TX; } else { cmd_rx = CMD_DISABLE_RX; cmd_tx = CMD_DISABLE_TX; } ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("rx %s cmd for channel %d failed", enable ? "start" : "stop", cmd->channel); goto out; } wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d", enable ? "start" : "stop", cmd->channel); ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("tx %s cmd for channel %d failed", enable ? "start" : "stop", cmd->channel); goto out; } wl1271_debug(DEBUG_BOOT, "tx %s cmd channel %d", enable ? "start" : "stop", cmd->channel); out: kfree(cmd); return ret; } EXPORT_SYMBOL_GPL(wl1271_cmd_data_path); int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ps_mode, u16 auto_ps_timeout) { struct wl1271_cmd_ps_params *ps_params = NULL; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd set ps mode"); ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); if (!ps_params) { ret = -ENOMEM; goto out; } ps_params->role_id = wlvif->role_id; ps_params->ps_mode = ps_mode; ps_params->auto_ps_timeout = auto_ps_timeout; ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, sizeof(*ps_params), 0); if (ret < 0) { wl1271_error("cmd set_ps_mode failed"); goto out; } out: kfree(ps_params); return ret; } int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, u16 template_id, void *buf, size_t buf_len, int index, u32 rates) { struct wl1271_cmd_template_set *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd template_set %d (role %d)", template_id, role_id); WARN_ON(buf_len > WL1271_CMD_TEMPL_MAX_SIZE); buf_len = min_t(size_t, buf_len, WL1271_CMD_TEMPL_MAX_SIZE); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } /* during initialization wlvif is NULL */ cmd->role_id = role_id; cmd->len = cpu_to_le16(buf_len); cmd->template_type = template_id; cmd->enabled_rates = cpu_to_le32(rates); cmd->short_retry_limit = wl->conf.tx.tmpl_short_retry_limit; cmd->long_retry_limit = wl->conf.tx.tmpl_long_retry_limit; cmd->index = index; if (buf) memcpy(cmd->template_data, buf, buf_len); ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("cmd set_template failed: %d", ret); goto out_free; } out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct sk_buff *skb = NULL; int size; void *ptr; int ret = -ENOMEM; if (wlvif->bss_type == BSS_TYPE_IBSS) { size = sizeof(struct wl12xx_null_data_template); ptr = NULL; } else { skb = ieee80211_nullfunc_get(wl->hw, wl12xx_wlvif_to_vif(wlvif)); if (!skb) goto out; size = skb->len; ptr = skb->data; } ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_NULL_DATA, ptr, size, 0, wlvif->basic_rate); out: dev_kfree_skb(skb); if (ret) wl1271_warning("cmd buld null data failed %d", ret); return ret; } int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb = NULL; int ret = -ENOMEM; skb = ieee80211_nullfunc_get(wl->hw, vif); if (!skb) goto out; ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_KLV, skb->data, skb->len, CMD_TEMPL_KLV_IDX_NULL_DATA, wlvif->basic_rate); out: dev_kfree_skb(skb); if (ret) wl1271_warning("cmd build klv null data failed %d", ret); return ret; } int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; int ret = 0; skb = ieee80211_pspoll_get(wl->hw, vif); if (!skb) goto out; ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_PS_POLL, skb->data, skb->len, 0, wlvif->basic_rate_set); out: dev_kfree_skb(skb); return ret; } int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool sched_scan) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; int ret; u32 rate; u16 template_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, ie, ie_len); if (!skb) { ret = -ENOMEM; goto out; } wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); if (!sched_scan && (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) { template_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4; template_id_5 = CMD_TEMPL_APP_PROBE_REQ_5; } rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); if (band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, role_id, template_id_2_4, skb->data, skb->len, 0, rate); else ret = wl1271_cmd_template_set(wl, role_id, template_id_5, skb->data, skb->len, 0, rate); out: dev_kfree_skb(skb); return ret; } struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); int ret; u32 rate; if (!skb) skb = ieee80211_ap_probereq_get(wl->hw, vif); if (!skb) goto out; wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len); rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]); if (wlvif->band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_CFG_PROBE_REQ_2_4, skb->data, skb->len, 0, rate); else ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_CFG_PROBE_REQ_5, skb->data, skb->len, 0, rate); if (ret < 0) wl1271_error("Unable to set ap probe request template."); out: return skb; } int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret, extra = 0; u16 fc; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; struct wl12xx_arp_rsp_template *tmpl; struct ieee80211_hdr_3addr *hdr; struct arphdr *arp_hdr; skb = dev_alloc_skb(sizeof(*hdr) + sizeof(__le16) + sizeof(*tmpl) + WL1271_EXTRA_SPACE_MAX); if (!skb) { wl1271_error("failed to allocate buffer for arp rsp template"); return -ENOMEM; } skb_reserve(skb, sizeof(*hdr) + WL1271_EXTRA_SPACE_MAX); tmpl = (struct wl12xx_arp_rsp_template *)skb_put(skb, sizeof(*tmpl)); memset(tmpl, 0, sizeof(*tmpl)); /* llc layer */ memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header)); tmpl->llc_type = cpu_to_be16(ETH_P_ARP); /* arp header */ arp_hdr = &tmpl->arp_hdr; arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER); arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP); arp_hdr->ar_hln = ETH_ALEN; arp_hdr->ar_pln = 4; arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY); /* arp payload */ memcpy(tmpl->sender_hw, vif->addr, ETH_ALEN); tmpl->sender_ip = wlvif->ip_addr; /* encryption space */ switch (wlvif->encryption_type) { case KEY_TKIP: if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) extra = WL1271_EXTRA_SPACE_TKIP; break; case KEY_AES: extra = WL1271_EXTRA_SPACE_AES; break; case KEY_NONE: case KEY_WEP: case KEY_GEM: extra = 0; break; default: wl1271_warning("Unknown encryption type: %d", wlvif->encryption_type); ret = -EINVAL; goto out; } if (extra) { u8 *space = skb_push(skb, extra); memset(space, 0, extra); } /* QoS header - BE */ if (wlvif->sta.qos) memset(skb_push(skb, sizeof(__le16)), 0, sizeof(__le16)); /* mac80211 header */ hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS; if (wlvif->sta.qos) fc |= IEEE80211_STYPE_QOS_DATA; else fc |= IEEE80211_STYPE_DATA; if (wlvif->encryption_type != KEY_NONE) fc |= IEEE80211_FCTL_PROTECTED; hdr->frame_control = cpu_to_le16(fc); memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN); memcpy(hdr->addr2, vif->addr, ETH_ALEN); memset(hdr->addr3, 0xff, ETH_ALEN); ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP, skb->data, skb->len, 0, wlvif->basic_rate); out: dev_kfree_skb(skb); return ret; } int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct ieee80211_qos_hdr template; memset(&template, 0, sizeof(template)); memcpy(template.addr1, vif->bss_conf.bssid, ETH_ALEN); memcpy(template.addr2, vif->addr, ETH_ALEN); memcpy(template.addr3, vif->bss_conf.bssid, ETH_ALEN); template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC | IEEE80211_FCTL_TODS); /* FIXME: not sure what priority to use here */ template.qos_ctrl = cpu_to_le16(0); return wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_QOS_NULL_DATA, &template, sizeof(template), 0, wlvif->basic_rate); } int wl12xx_cmd_set_default_wep_key(struct wl1271 *wl, u8 id, u8 hlid) { struct wl1271_cmd_set_keys *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->hlid = hlid; cmd->key_id = id; cmd->lid_key_type = WEP_DEFAULT_LID_TYPE; cmd->key_action = cpu_to_le16(KEY_SET_ID); cmd->key_type = KEY_WEP; ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("cmd set_default_wep_key failed: %d", ret); goto out; } out: kfree(cmd); return ret; } int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, const u8 *addr, u32 tx_seq_32, u16 tx_seq_16) { struct wl1271_cmd_set_keys *cmd; int ret = 0; /* hlid might have already been deleted */ if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) return 0; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->hlid = wlvif->sta.hlid; if (key_type == KEY_WEP) cmd->lid_key_type = WEP_DEFAULT_LID_TYPE; else if (is_broadcast_ether_addr(addr)) cmd->lid_key_type = BROADCAST_LID_TYPE; else cmd->lid_key_type = UNICAST_LID_TYPE; cmd->key_action = cpu_to_le16(action); cmd->key_size = key_size; cmd->key_type = key_type; cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16); cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32); cmd->key_id = id; if (key_type == KEY_TKIP) { /* * We get the key in the following form: * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) * but the target is expecting: * TKIP - RX MIC - TX MIC */ memcpy(cmd->key, key, 16); memcpy(cmd->key + 16, key + 24, 8); memcpy(cmd->key + 24, key + 16, 8); } else { memcpy(cmd->key, key, key_size); } wl1271_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd)); ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("could not set keys"); goto out; } out: kfree(cmd); return ret; } /* * TODO: merge with sta/ibss into 1 set_key function. * note there are slight diffs */ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16) { struct wl1271_cmd_set_keys *cmd; int ret = 0; u8 lid_type; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return -ENOMEM; if (hlid == wlvif->ap.bcast_hlid) { if (key_type == KEY_WEP) lid_type = WEP_DEFAULT_LID_TYPE; else lid_type = BROADCAST_LID_TYPE; } else { lid_type = UNICAST_LID_TYPE; } wl1271_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d" " hlid: %d", (int)action, (int)id, (int)lid_type, (int)key_type, (int)hlid); cmd->lid_key_type = lid_type; cmd->hlid = hlid; cmd->key_action = cpu_to_le16(action); cmd->key_size = key_size; cmd->key_type = key_type; cmd->key_id = id; cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16); cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32); if (key_type == KEY_TKIP) { /* * We get the key in the following form: * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) * but the target is expecting: * TKIP - RX MIC - TX MIC */ memcpy(cmd->key, key, 16); memcpy(cmd->key + 16, key + 24, 8); memcpy(cmd->key + 24, key + 16, 8); } else { memcpy(cmd->key, key, key_size); } wl1271_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd)); ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("could not set ap keys"); goto out; } out: kfree(cmd); return ret; } int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid) { struct wl12xx_cmd_set_peer_state *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd set peer state (hlid=%d)", hlid); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->hlid = hlid; cmd->state = WL1271_CMD_STA_STATE_CONNECTED; ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send set peer state command"); goto out_free; } out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u8 hlid) { struct wl12xx_cmd_add_peer *cmd; int i, ret; u32 sta_rates; wl1271_debug(DEBUG_CMD, "cmd add peer %d", (int)hlid); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } memcpy(cmd->addr, sta->addr, ETH_ALEN); cmd->bss_index = WL1271_AP_BSS_INDEX; cmd->aid = sta->aid; cmd->hlid = hlid; cmd->sp_len = sta->max_sp; cmd->wmm = sta->wme ? 1 : 0; for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++) if (sta->wme && (sta->uapsd_queues & BIT(i))) cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] = WL1271_PSD_UPSD_TRIGGER; else cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] = WL1271_PSD_LEGACY; sta_rates = sta->supp_rates[wlvif->band]; if (sta->ht_cap.ht_supported) sta_rates |= (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) | (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET); cmd->supported_rates = cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates, wlvif->band)); wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x", cmd->supported_rates, sta->uapsd_queues); ret = wl1271_cmd_send(wl, CMD_ADD_PEER, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd add peer"); goto out_free; } out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid) { struct wl12xx_cmd_remove_peer *cmd; int ret; bool timeout = false; wl1271_debug(DEBUG_CMD, "cmd remove peer %d", (int)hlid); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->hlid = hlid; /* We never send a deauth, mac80211 is in charge of this */ cmd->reason_opcode = 0; cmd->send_deauth_flag = 0; ret = wl1271_cmd_send(wl, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to initiate cmd remove peer"); goto out_free; } ret = wl1271_cmd_wait_for_event_or_timeout(wl, PEER_REMOVE_COMPLETE_EVENT_ID, &timeout); /* * We are ok with a timeout here. The event is sometimes not sent * due to a firmware bug. In case of another error (like SDIO timeout) * queue a recovery. */ if (ret) wl12xx_queue_recovery_work(wl); out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_config_fwlog(struct wl1271 *wl) { struct wl12xx_cmd_config_fwlog *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd config firmware logger"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->logger_mode = wl->conf.fwlog.mode; cmd->log_severity = wl->conf.fwlog.severity; cmd->timestamp = wl->conf.fwlog.timestamp; cmd->output = wl->conf.fwlog.output; cmd->threshold = wl->conf.fwlog.threshold; ret = wl1271_cmd_send(wl, CMD_CONFIG_FWLOGGER, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send config firmware logger command"); goto out_free; } out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_start_fwlog(struct wl1271 *wl) { struct wl12xx_cmd_start_fwlog *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd start firmware logger"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } ret = wl1271_cmd_send(wl, CMD_START_FWLOGGER, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send start firmware logger command"); goto out_free; } out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_stop_fwlog(struct wl1271 *wl) { struct wl12xx_cmd_stop_fwlog *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd stop firmware logger"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } ret = wl1271_cmd_send(wl, CMD_STOP_FWLOGGER, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send stop firmware logger command"); goto out_free; } out_free: kfree(cmd); out: return ret; } static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id) { struct wl12xx_cmd_roc *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id); if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID)) return -EINVAL; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->role_id = role_id; cmd->channel = wlvif->channel; switch (wlvif->band) { case IEEE80211_BAND_2GHZ: cmd->band = WLCORE_BAND_2_4GHZ; break; case IEEE80211_BAND_5GHZ: cmd->band = WLCORE_BAND_5GHZ; break; default: wl1271_error("roc - unknown band: %d", (int)wlvif->band); ret = -EINVAL; goto out_free; } ret = wl1271_cmd_send(wl, CMD_REMAIN_ON_CHANNEL, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send ROC command"); goto out_free; } out_free: kfree(cmd); out: return ret; } static int wl12xx_cmd_croc(struct wl1271 *wl, u8 role_id) { struct wl12xx_cmd_croc *cmd; int ret = 0; wl1271_debug(DEBUG_CMD, "cmd croc (%d)", role_id); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->role_id = role_id; ret = wl1271_cmd_send(wl, CMD_CANCEL_REMAIN_ON_CHANNEL, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send ROC command"); goto out_free; } out_free: kfree(cmd); out: return ret; } int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id) { int ret = 0; bool is_first_roc; if (WARN_ON(test_bit(role_id, wl->roc_map))) return 0; is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES); ret = wl12xx_cmd_roc(wl, wlvif, role_id); if (ret < 0) goto out; if (is_first_roc) { ret = wl1271_cmd_wait_for_event(wl, REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID); if (ret < 0) { wl1271_error("cmd roc event completion error"); goto out; } } __set_bit(role_id, wl->roc_map); out: return ret; } int wl12xx_croc(struct wl1271 *wl, u8 role_id) { int ret = 0; if (WARN_ON(!test_bit(role_id, wl->roc_map))) return 0; ret = wl12xx_cmd_croc(wl, role_id); if (ret < 0) goto out; __clear_bit(role_id, wl->roc_map); /* * Rearm the tx watchdog when removing the last ROC. This prevents * recoveries due to just finished ROCs - when Tx hasn't yet had * a chance to get out. */ if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) wl12xx_rearm_tx_watchdog_locked(wl); out: return ret; } int wl12xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch) { struct wl12xx_cmd_channel_switch *cmd; int ret; wl1271_debug(DEBUG_ACX, "cmd channel switch"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->role_id = wlvif->role_id; cmd->channel = ch_switch->channel->hw_value; cmd->switch_time = ch_switch->count; cmd->stop_tx = ch_switch->block_tx; /* FIXME: control from mac80211 in the future */ cmd->post_switch_tx_disable = 0; /* Enable TX on the target channel */ ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send channel switch command"); goto out_free; } out_free: kfree(cmd); out: return ret; } int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl) { struct wl12xx_cmd_stop_channel_switch *cmd; int ret; wl1271_debug(DEBUG_ACX, "cmd stop channel switch"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to stop channel switch command"); goto out_free; } out_free: kfree(cmd); out: return ret; } /* start dev role and roc on its channel */ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS))) return -EINVAL; ret = wl12xx_cmd_role_start_dev(wl, wlvif); if (ret < 0) goto out; ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id); if (ret < 0) goto out_stop; return 0; out_stop: wl12xx_cmd_role_stop_dev(wl, wlvif); out: return ret; } /* croc dev hlid, and stop the role */ int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS))) return -EINVAL; /* flush all pending packets */ ret = wlcore_tx_work_locked(wl); if (ret < 0) goto out; if (test_bit(wlvif->dev_role_id, wl->roc_map)) { ret = wl12xx_croc(wl, wlvif->dev_role_id); if (ret < 0) goto out; } ret = wl12xx_cmd_role_stop_dev(wl, wlvif); if (ret < 0) goto out; out: return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/boot.h0000644000175000017500000000277612026211315023520 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __BOOT_H__ #define __BOOT_H__ #include "wlcore.h" int wlcore_boot_upload_firmware(struct wl1271 *wl); int wlcore_boot_upload_nvs(struct wl1271 *wl); int wlcore_boot_run_firmware(struct wl1271 *wl); #define WL1271_NO_SUBBANDS 8 #define WL1271_NO_POWER_LEVELS 4 #define WL1271_FW_VERSION_MAX_LEN 20 struct wl1271_static_data { u8 mac_address[ETH_ALEN]; u8 padding[2]; u8 fw_version[WL1271_FW_VERSION_MAX_LEN]; u32 hw_version; u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS]; u8 priv[0]; }; /* number of times we try to read the INIT interrupt */ #define INIT_LOOP 20000 /* delay between retries */ #define INIT_LOOP_DELAY 50 #define WU_COUNTER_PAUSE_VAL 0x3FF #define WELP_ARM_COMMAND_VAL 0x4 #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/boot.c0000644000175000017500000003314212026211315023502 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include "debug.h" #include "acx.h" #include "boot.h" #include "io.h" #include "event.h" #include "rx.h" #include "hw_ops.h" static int wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) { u32 cpu_ctrl; int ret; /* 10.5.0 run the firmware (I) */ ret = wlcore_read_reg(wl, REG_ECPU_CONTROL, &cpu_ctrl); if (ret < 0) goto out; /* 10.5.1 run the firmware (II) */ cpu_ctrl |= flag; ret = wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl); out: return ret; } static int wlcore_boot_parse_fw_ver(struct wl1271 *wl, struct wl1271_static_data *static_data) { int ret; strncpy(wl->chip.fw_ver_str, static_data->fw_version, sizeof(wl->chip.fw_ver_str)); /* make sure the string is NULL-terminated */ wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0'; ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u", &wl->chip.fw_ver[0], &wl->chip.fw_ver[1], &wl->chip.fw_ver[2], &wl->chip.fw_ver[3], &wl->chip.fw_ver[4]); if (ret != 5) { wl1271_warning("fw version incorrect value"); memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver)); ret = -EINVAL; goto out; } ret = wlcore_identify_fw(wl); if (ret < 0) goto out; out: return ret; } static int wlcore_validate_fw_ver(struct wl1271 *wl) { unsigned int *fw_ver = wl->chip.fw_ver; unsigned int *min_ver = wl->min_fw_ver; /* the chip must be exactly equal */ if (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]) goto fail; /* always check the next digit if all previous ones are equal */ if (min_ver[FW_VER_IF_TYPE] < fw_ver[FW_VER_IF_TYPE]) goto out; else if (min_ver[FW_VER_IF_TYPE] > fw_ver[FW_VER_IF_TYPE]) goto fail; if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR]) goto out; else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]) goto fail; if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE]) goto out; else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE]) goto fail; if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR]) goto out; else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]) goto fail; out: return 0; fail: wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n" "Please use at least FW %u.%u.%u.%u.%u.\n" "You can get more information at:\n" "http://wireless.kernel.org/en/users/Drivers/wl12xx", fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE], fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE], fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP], min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR], min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]); return -EINVAL; } static int wlcore_boot_static_data(struct wl1271 *wl) { struct wl1271_static_data *static_data; size_t len = sizeof(*static_data) + wl->static_data_priv_len; int ret; static_data = kmalloc(len, GFP_KERNEL); if (!static_data) { ret = -ENOMEM; goto out; } ret = wlcore_read(wl, wl->cmd_box_addr, static_data, len, false); if (ret < 0) goto out_free; ret = wlcore_boot_parse_fw_ver(wl, static_data); if (ret < 0) goto out_free; ret = wlcore_validate_fw_ver(wl); if (ret < 0) goto out_free; ret = wlcore_handle_static_data(wl, static_data); if (ret < 0) goto out_free; out_free: kfree(static_data); out: return ret; } static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, size_t fw_data_len, u32 dest) { struct wlcore_partition_set partition; int addr, chunk_num, partition_limit; u8 *p, *chunk; int ret; /* whal_FwCtrl_LoadFwImageSm() */ wl1271_debug(DEBUG_BOOT, "starting firmware upload"); wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d", fw_data_len, CHUNK_SIZE); if ((fw_data_len % 4) != 0) { wl1271_error("firmware length not multiple of four"); return -EIO; } chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL); if (!chunk) { wl1271_error("allocation for firmware upload chunk failed"); return -ENOMEM; } memcpy(&partition, &wl->ptable[PART_DOWN], sizeof(partition)); partition.mem.start = dest; ret = wlcore_set_partition(wl, &partition); if (ret < 0) goto out; /* 10.1 set partition limit and chunk num */ chunk_num = 0; partition_limit = wl->ptable[PART_DOWN].mem.size; while (chunk_num < fw_data_len / CHUNK_SIZE) { /* 10.2 update partition, if needed */ addr = dest + (chunk_num + 2) * CHUNK_SIZE; if (addr > partition_limit) { addr = dest + chunk_num * CHUNK_SIZE; partition_limit = chunk_num * CHUNK_SIZE + wl->ptable[PART_DOWN].mem.size; partition.mem.start = addr; ret = wlcore_set_partition(wl, &partition); if (ret < 0) goto out; } /* 10.3 upload the chunk */ addr = dest + chunk_num * CHUNK_SIZE; p = buf + chunk_num * CHUNK_SIZE; memcpy(chunk, p, CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); ret = wlcore_write(wl, addr, chunk, CHUNK_SIZE, false); if (ret < 0) goto out; chunk_num++; } /* 10.4 upload the last chunk */ addr = dest + chunk_num * CHUNK_SIZE; p = buf + chunk_num * CHUNK_SIZE; memcpy(chunk, p, fw_data_len % CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x", fw_data_len % CHUNK_SIZE, p, addr); ret = wlcore_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); out: kfree(chunk); return ret; } int wlcore_boot_upload_firmware(struct wl1271 *wl) { u32 chunks, addr, len; int ret = 0; u8 *fw; fw = wl->fw; chunks = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u", chunks); while (chunks--) { addr = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); len = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); if (len > 300000) { wl1271_info("firmware chunk too long: %u", len); return -EINVAL; } wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u", chunks, addr, len); ret = wl1271_boot_upload_firmware_chunk(wl, fw, len, addr); if (ret != 0) break; fw += len; } return ret; } EXPORT_SYMBOL_GPL(wlcore_boot_upload_firmware); int wlcore_boot_upload_nvs(struct wl1271 *wl) { size_t nvs_len, burst_len; int i; u32 dest_addr, val; u8 *nvs_ptr, *nvs_aligned; int ret; if (wl->nvs == NULL) { wl1271_error("NVS file is needed during boot"); return -ENODEV; } if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) { struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs; /* * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz * band configurations) can be removed when those NVS files stop * floating around. */ if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { if (nvs->general_params.dual_mode_select) wl->enable_11a = true; } if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || wl->enable_11a)) { wl1271_error("nvs size is not as expected: %zu != %zu", wl->nvs_len, sizeof(struct wl1271_nvs_file)); kfree(wl->nvs); wl->nvs = NULL; wl->nvs_len = 0; return -EILSEQ; } /* only the first part of the NVS needs to be uploaded */ nvs_len = sizeof(nvs->nvs); nvs_ptr = (u8 *) nvs->nvs; } else { struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { if (nvs->general_params.dual_mode_select) wl->enable_11a = true; } else { wl1271_error("nvs size is not as expected: %zu != %zu", wl->nvs_len, sizeof(struct wl128x_nvs_file)); kfree(wl->nvs); wl->nvs = NULL; wl->nvs_len = 0; return -EILSEQ; } /* only the first part of the NVS needs to be uploaded */ nvs_len = sizeof(nvs->nvs); nvs_ptr = (u8 *)nvs->nvs; } /* update current MAC address to NVS */ nvs_ptr[11] = wl->addresses[0].addr[0]; nvs_ptr[10] = wl->addresses[0].addr[1]; nvs_ptr[6] = wl->addresses[0].addr[2]; nvs_ptr[5] = wl->addresses[0].addr[3]; nvs_ptr[4] = wl->addresses[0].addr[4]; nvs_ptr[3] = wl->addresses[0].addr[5]; /* * Layout before the actual NVS tables: * 1 byte : burst length. * 2 bytes: destination address. * n bytes: data to burst copy. * * This is ended by a 0 length, then the NVS tables. */ /* FIXME: Do we need to check here whether the LSB is 1? */ while (nvs_ptr[0]) { burst_len = nvs_ptr[0]; dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); /* * Due to our new wl1271_translate_reg_addr function, * we need to add the register partition start address * to the destination */ dest_addr += wl->curr_part.reg.start; /* We move our pointer to the data */ nvs_ptr += 3; for (i = 0; i < burst_len; i++) { if (nvs_ptr + 3 >= (u8 *) wl->nvs + nvs_len) goto out_badnvs; val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); wl1271_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); ret = wlcore_write32(wl, dest_addr, val); if (ret < 0) return ret; nvs_ptr += 4; dest_addr += 4; } if (nvs_ptr >= (u8 *) wl->nvs + nvs_len) goto out_badnvs; } /* * We've reached the first zero length, the first NVS table * is located at an aligned offset which is at least 7 bytes further. * NOTE: The wl->nvs->nvs element must be first, in order to * simplify the casting, we assume it is at the beginning of * the wl->nvs structure. */ nvs_ptr = (u8 *)wl->nvs + ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); if (nvs_ptr >= (u8 *) wl->nvs + nvs_len) goto out_badnvs; nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]); if (ret < 0) return ret; /* Copy the NVS tables to a new block to ensure alignment */ nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); if (!nvs_aligned) return -ENOMEM; /* And finally we upload the NVS tables */ ret = wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); kfree(nvs_aligned); return ret; out_badnvs: wl1271_error("nvs data is malformed"); return -EILSEQ; } EXPORT_SYMBOL_GPL(wlcore_boot_upload_nvs); int wlcore_boot_run_firmware(struct wl1271 *wl) { int loop, ret; u32 chip_id, intr; /* Make sure we have the boot partition */ ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) return ret; ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); if (ret < 0) return ret; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &chip_id); if (ret < 0) return ret; wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); if (chip_id != wl->chip.id) { wl1271_error("chip id doesn't match after firmware boot"); return -EIO; } /* wait for init to complete */ loop = 0; while (loop++ < INIT_LOOP) { udelay(INIT_LOOP_DELAY); ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) return ret; if (intr == 0xffffffff) { wl1271_error("error reading hardware complete " "init indication"); return -EIO; } /* check that ACX_INTR_INIT_COMPLETE is enabled */ else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) { ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK, WL1271_ACX_INTR_INIT_COMPLETE); if (ret < 0) return ret; break; } } if (loop > INIT_LOOP) { wl1271_error("timeout waiting for the hardware to " "complete initialization"); return -EIO; } /* get hardware config command mail box */ ret = wlcore_read_reg(wl, REG_COMMAND_MAILBOX_PTR, &wl->cmd_box_addr); if (ret < 0) return ret; wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x", wl->cmd_box_addr); /* get hardware config event mail box */ ret = wlcore_read_reg(wl, REG_EVENT_MAILBOX_PTR, &wl->mbox_ptr[0]); if (ret < 0) return ret; wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); ret = wlcore_boot_static_data(wl); if (ret < 0) { wl1271_error("error getting static data"); return ret; } /* * in case of full asynchronous mode the firmware event must be * ready to receive event from the command mailbox */ /* unmask required mbox events */ wl->event_mask = BSS_LOSE_EVENT_ID | REGAINED_BSS_EVENT_ID | SCAN_COMPLETE_EVENT_ID | ROLE_STOP_COMPLETE_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID | SOFT_GEMINI_SENSE_EVENT_ID | PERIODIC_SCAN_REPORT_EVENT_ID | PERIODIC_SCAN_COMPLETE_EVENT_ID | DUMMY_PACKET_EVENT_ID | PEER_REMOVE_COMPLETE_EVENT_ID | BA_SESSION_RX_CONSTRAINT_EVENT_ID | REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | INACTIVE_STA_EVENT_ID | MAX_TX_RETRY_EVENT_ID | CHANNEL_SWITCH_COMPLETE_EVENT_ID; ret = wl1271_event_unmask(wl); if (ret < 0) { wl1271_error("EVENT mask setting failed"); return ret; } /* set the working partition to its "running" mode offset */ ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]); /* firmware startup completed */ return ret; } EXPORT_SYMBOL_GPL(wlcore_boot_run_firmware); compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/acx.h0000644000175000017500000007070412026211315023324 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __ACX_H__ #define __ACX_H__ #include "wlcore.h" #include "cmd.h" /************************************************************************* Host Interrupt Register (WiLink -> Host) **************************************************************************/ /* HW Initiated interrupt Watchdog timer expiration */ #define WL1271_ACX_INTR_WATCHDOG BIT(0) /* Init sequence is done (masked interrupt, detection through polling only ) */ #define WL1271_ACX_INTR_INIT_COMPLETE BIT(1) /* Event was entered to Event MBOX #A*/ #define WL1271_ACX_INTR_EVENT_A BIT(2) /* Event was entered to Event MBOX #B*/ #define WL1271_ACX_INTR_EVENT_B BIT(3) /* Command processing completion*/ #define WL1271_ACX_INTR_CMD_COMPLETE BIT(4) /* Signaling the host on HW wakeup */ #define WL1271_ACX_INTR_HW_AVAILABLE BIT(5) /* The MISC bit is used for aggregation of RX, TxComplete and TX rate update */ #define WL1271_ACX_INTR_DATA BIT(6) /* Trace message on MBOX #A */ #define WL1271_ACX_INTR_TRACE_A BIT(7) /* Trace message on MBOX #B */ #define WL1271_ACX_INTR_TRACE_B BIT(8) /* SW FW Initiated interrupt Watchdog timer expiration */ #define WL1271_ACX_SW_INTR_WATCHDOG BIT(9) #define WL1271_ACX_INTR_ALL 0xFFFFFFFF /* all possible interrupts - only appropriate ones will be masked in */ #define WLCORE_ALL_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \ WL1271_ACX_INTR_EVENT_A | \ WL1271_ACX_INTR_EVENT_B | \ WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA | \ WL1271_ACX_SW_INTR_WATCHDOG) /* Target's information element */ struct acx_header { struct wl1271_cmd_header cmd; /* acx (or information element) header */ __le16 id; /* payload length (not including headers */ __le16 len; } __packed; struct acx_error_counter { struct acx_header header; /* The number of PLCP errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ __le32 PLCP_error; /* The number of FCS errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ __le32 FCS_error; /* The number of MPDUs without PLCP header errors received*/ /* since the last time this information element was interrogated. */ /* This field is automatically cleared when it is interrogated.*/ __le32 valid_frame; /* the number of missed sequence numbers in the squentially */ /* values of frames seq numbers */ __le32 seq_num_miss; } __packed; enum wl12xx_role { WL1271_ROLE_STA = 0, WL1271_ROLE_IBSS, WL1271_ROLE_AP, WL1271_ROLE_DEVICE, WL1271_ROLE_P2P_CL, WL1271_ROLE_P2P_GO, WL12XX_INVALID_ROLE_TYPE = 0xff }; enum wl1271_psm_mode { /* Active mode */ WL1271_PSM_CAM = 0, /* Power save mode */ WL1271_PSM_PS = 1, /* Extreme low power */ WL1271_PSM_ELP = 2, WL1271_PSM_MAX = WL1271_PSM_ELP, /* illegal out of band value of PSM mode */ WL1271_PSM_ILLEGAL = 0xff }; struct acx_sleep_auth { struct acx_header header; /* The sleep level authorization of the device. */ /* 0 - Always active*/ /* 1 - Power down mode: light / fast sleep*/ /* 2 - ELP mode: Deep / Max sleep*/ u8 sleep_auth; u8 padding[3]; } __packed; enum { HOSTIF_PCI_MASTER_HOST_INDIRECT, HOSTIF_PCI_MASTER_HOST_DIRECT, HOSTIF_SLAVE, HOSTIF_PKT_RING, HOSTIF_DONTCARE = 0xFF }; #define DEFAULT_UCAST_PRIORITY 0 #define DEFAULT_RX_Q_PRIORITY 0 #define DEFAULT_RXQ_PRIORITY 0 /* low 0 .. 15 high */ #define DEFAULT_RXQ_TYPE 0x07 /* All frames, Data/Ctrl/Mgmt */ #define TRACE_BUFFER_MAX_SIZE 256 #define DP_RX_PACKET_RING_CHUNK_SIZE 1600 #define DP_TX_PACKET_RING_CHUNK_SIZE 1600 #define DP_RX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_COMPLETE_TIME_OUT 20 #define TX_MSDU_LIFETIME_MIN 0 #define TX_MSDU_LIFETIME_MAX 3000 #define TX_MSDU_LIFETIME_DEF 512 #define RX_MSDU_LIFETIME_MIN 0 #define RX_MSDU_LIFETIME_MAX 0xFFFFFFFF #define RX_MSDU_LIFETIME_DEF 512000 struct acx_rx_msdu_lifetime { struct acx_header header; /* * The maximum amount of time, in TU, before the * firmware discards the MSDU. */ __le32 lifetime; } __packed; enum acx_slot_type { SLOT_TIME_LONG = 0, SLOT_TIME_SHORT = 1, DEFAULT_SLOT_TIME = SLOT_TIME_SHORT, MAX_SLOT_TIMES = 0xFF }; #define STATION_WONE_INDEX 0 struct acx_slot { struct acx_header header; u8 role_id; u8 wone_index; /* Reserved */ u8 slot_time; u8 reserved[5]; } __packed; #define ACX_MC_ADDRESS_GROUP_MAX (8) #define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX) struct acx_dot11_grp_addr_tbl { struct acx_header header; u8 role_id; u8 enabled; u8 num_groups; u8 pad[1]; u8 mac_table[ADDRESS_GROUP_MAX_LEN]; } __packed; struct acx_rx_timeout { struct acx_header header; u8 role_id; u8 reserved; __le16 ps_poll_timeout; __le16 upsd_timeout; u8 padding[2]; } __packed; struct acx_rts_threshold { struct acx_header header; u8 role_id; u8 reserved; __le16 threshold; } __packed; struct acx_beacon_filter_option { struct acx_header header; u8 role_id; u8 enable; /* * The number of beacons without the unicast TIM * bit set that the firmware buffers before * signaling the host about ready frames. * When set to 0 and the filter is enabled, beacons * without the unicast TIM bit set are dropped. */ u8 max_num_beacons; u8 pad[1]; } __packed; /* * ACXBeaconFilterEntry (not 221) * Byte Offset Size (Bytes) Definition * =========== ============ ========== * 0 1 IE identifier * 1 1 Treatment bit mask * * ACXBeaconFilterEntry (221) * Byte Offset Size (Bytes) Definition * =========== ============ ========== * 0 1 IE identifier * 1 1 Treatment bit mask * 2 3 OUI * 5 1 Type * 6 2 Version * * * Treatment bit mask - The information element handling: * bit 0 - The information element is compared and transferred * in case of change. * bit 1 - The information element is transferred to the host * with each appearance or disappearance. * Note that both bits can be set at the same time. */ #define BEACON_FILTER_TABLE_MAX_IE_NUM (32) #define BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM (6) #define BEACON_FILTER_TABLE_IE_ENTRY_SIZE (2) #define BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE (6) #define BEACON_FILTER_TABLE_MAX_SIZE ((BEACON_FILTER_TABLE_MAX_IE_NUM * \ BEACON_FILTER_TABLE_IE_ENTRY_SIZE) + \ (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \ BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE)) struct acx_beacon_filter_ie_table { struct acx_header header; u8 role_id; u8 num_ie; u8 pad[2]; u8 table[BEACON_FILTER_TABLE_MAX_SIZE]; } __packed; struct acx_conn_monit_params { struct acx_header header; u8 role_id; u8 padding[3]; __le32 synch_fail_thold; /* number of beacons missed */ __le32 bss_lose_timeout; /* number of TU's from synch fail */ } __packed; struct acx_bt_wlan_coex { struct acx_header header; u8 enable; u8 pad[3]; } __packed; struct acx_bt_wlan_coex_param { struct acx_header header; __le32 params[CONF_SG_PARAMS_MAX]; u8 param_idx; u8 padding[3]; } __packed; struct acx_dco_itrim_params { struct acx_header header; u8 enable; u8 padding[3]; __le32 timeout; } __packed; struct acx_energy_detection { struct acx_header header; /* The RX Clear Channel Assessment threshold in the PHY */ __le16 rx_cca_threshold; u8 tx_energy_detection; u8 pad; } __packed; struct acx_beacon_broadcast { struct acx_header header; u8 role_id; /* Enables receiving of broadcast packets in PS mode */ u8 rx_broadcast_in_ps; __le16 beacon_rx_timeout; __le16 broadcast_timeout; /* Consecutive PS Poll failures before updating the host */ u8 ps_poll_threshold; u8 pad[1]; } __packed; struct acx_event_mask { struct acx_header header; __le32 event_mask; __le32 high_event_mask; /* Unused */ } __packed; #define SCAN_PASSIVE BIT(0) #define SCAN_5GHZ_BAND BIT(1) #define SCAN_TRIGGERED BIT(2) #define SCAN_PRIORITY_HIGH BIT(3) /* When set, disable HW encryption */ #define DF_ENCRYPTION_DISABLE 0x01 #define DF_SNIFF_MODE_ENABLE 0x80 struct acx_feature_config { struct acx_header header; u8 role_id; u8 padding[3]; __le32 options; __le32 data_flow_options; } __packed; struct acx_current_tx_power { struct acx_header header; u8 role_id; u8 current_tx_power; u8 padding[2]; } __packed; struct acx_wake_up_condition { struct acx_header header; u8 role_id; u8 wake_up_event; /* Only one bit can be set */ u8 listen_interval; u8 pad[1]; } __packed; struct acx_aid { struct acx_header header; /* * To be set when associated with an AP. */ u8 role_id; u8 reserved; __le16 aid; } __packed; enum acx_preamble_type { ACX_PREAMBLE_LONG = 0, ACX_PREAMBLE_SHORT = 1 }; struct acx_preamble { struct acx_header header; /* * When set, the WiLink transmits the frames with a short preamble and * when cleared, the WiLink transmits the frames with a long preamble. */ u8 role_id; u8 preamble; u8 padding[2]; } __packed; enum acx_ctsprotect_type { CTSPROTECT_DISABLE = 0, CTSPROTECT_ENABLE = 1 }; struct acx_ctsprotect { struct acx_header header; u8 role_id; u8 ctsprotect; u8 padding[2]; } __packed; struct acx_rate_class { __le32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; u8 reserved; }; struct acx_rate_policy { struct acx_header header; __le32 rate_policy_idx; struct acx_rate_class rate_policy; } __packed; struct acx_ac_cfg { struct acx_header header; u8 role_id; u8 ac; u8 aifsn; u8 cw_min; __le16 cw_max; __le16 tx_op_limit; } __packed; struct acx_tid_config { struct acx_header header; u8 role_id; u8 queue_id; u8 channel_type; u8 tsid; u8 ps_scheme; u8 ack_policy; u8 padding[2]; __le32 apsd_conf[2]; } __packed; struct acx_frag_threshold { struct acx_header header; __le16 frag_threshold; u8 padding[2]; } __packed; struct acx_tx_config_options { struct acx_header header; __le16 tx_compl_timeout; /* msec */ __le16 tx_compl_threshold; /* number of packets */ } __packed; struct wl12xx_acx_config_memory { struct acx_header header; u8 rx_mem_block_num; u8 tx_min_mem_block_num; u8 num_stations; u8 num_ssid_profiles; __le32 total_tx_descriptors; u8 dyn_mem_enable; u8 tx_free_req; u8 rx_free_req; u8 tx_min; u8 fwlog_blocks; u8 padding[3]; } __packed; struct wl1271_acx_mem_map { struct acx_header header; __le32 code_start; __le32 code_end; __le32 wep_defkey_start; __le32 wep_defkey_end; __le32 sta_table_start; __le32 sta_table_end; __le32 packet_template_start; __le32 packet_template_end; /* Address of the TX result interface (control block) */ __le32 tx_result; __le32 tx_result_queue_start; __le32 queue_memory_start; __le32 queue_memory_end; __le32 packet_memory_pool_start; __le32 packet_memory_pool_end; __le32 debug_buffer1_start; __le32 debug_buffer1_end; __le32 debug_buffer2_start; __le32 debug_buffer2_end; /* Number of blocks FW allocated for TX packets */ __le32 num_tx_mem_blocks; /* Number of blocks FW allocated for RX packets */ __le32 num_rx_mem_blocks; /* the following 4 fields are valid in SLAVE mode only */ u8 *tx_cbuf; u8 *rx_cbuf; __le32 rx_ctrl; __le32 tx_ctrl; } __packed; struct wl1271_acx_rx_config_opt { struct acx_header header; __le16 mblk_threshold; __le16 threshold; __le16 timeout; u8 queue_type; u8 reserved; } __packed; struct wl1271_acx_bet_enable { struct acx_header header; u8 role_id; u8 enable; u8 max_consecutive; u8 padding[1]; } __packed; #define ACX_IPV4_VERSION 4 #define ACX_IPV6_VERSION 6 #define ACX_IPV4_ADDR_SIZE 4 /* bitmap of enabled arp_filter features */ #define ACX_ARP_FILTER_ARP_FILTERING BIT(0) #define ACX_ARP_FILTER_AUTO_ARP BIT(1) struct wl1271_acx_arp_filter { struct acx_header header; u8 role_id; u8 version; /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */ u8 enable; /* bitmap of enabled ARP filtering features */ u8 padding[1]; u8 address[16]; /* The configured device IP address - all ARP requests directed to this IP address will pass through. For IPv4, the first four bytes are used. */ } __packed; struct wl1271_acx_pm_config { struct acx_header header; __le32 host_clk_settling_time; u8 host_fast_wakeup_support; u8 padding[3]; } __packed; struct wl1271_acx_keep_alive_mode { struct acx_header header; u8 role_id; u8 enabled; u8 padding[2]; } __packed; enum { ACX_KEEP_ALIVE_NO_TX = 0, ACX_KEEP_ALIVE_PERIOD_ONLY }; enum { ACX_KEEP_ALIVE_TPL_INVALID = 0, ACX_KEEP_ALIVE_TPL_VALID }; struct wl1271_acx_keep_alive_config { struct acx_header header; u8 role_id; u8 index; u8 tpl_validation; u8 trigger; __le32 period; } __packed; /* TODO: maybe this needs to be moved somewhere else? */ #define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0) #define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1) #define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3) #define HOST_IF_CFG_RX_PAD_TO_SDIO_BLK BIT(4) #define HOST_IF_CFG_ADD_RX_ALIGNMENT BIT(6) enum { WL1271_ACX_TRIG_TYPE_LEVEL = 0, WL1271_ACX_TRIG_TYPE_EDGE, }; enum { WL1271_ACX_TRIG_DIR_LOW = 0, WL1271_ACX_TRIG_DIR_HIGH, WL1271_ACX_TRIG_DIR_BIDIR, }; enum { WL1271_ACX_TRIG_ENABLE = 1, WL1271_ACX_TRIG_DISABLE, }; enum { WL1271_ACX_TRIG_METRIC_RSSI_BEACON = 0, WL1271_ACX_TRIG_METRIC_RSSI_DATA, WL1271_ACX_TRIG_METRIC_SNR_BEACON, WL1271_ACX_TRIG_METRIC_SNR_DATA, }; enum { WL1271_ACX_TRIG_IDX_RSSI = 0, WL1271_ACX_TRIG_COUNT = 8, }; struct wl1271_acx_rssi_snr_trigger { struct acx_header header; u8 role_id; u8 metric; u8 type; u8 dir; __le16 threshold; __le16 pacing; /* 0 - 60000 ms */ u8 hysteresis; u8 index; u8 enable; u8 padding[1]; }; struct wl1271_acx_rssi_snr_avg_weights { struct acx_header header; u8 role_id; u8 padding[3]; u8 rssi_beacon; u8 rssi_data; u8 snr_beacon; u8 snr_data; }; /* special capability bit (not employed by the 802.11n spec) */ #define WL12XX_HT_CAP_HT_OPERATION BIT(16) /* * ACX_PEER_HT_CAP * Configure HT capabilities - declare the capabilities of the peer * we are connected to. */ struct wl1271_acx_ht_capabilities { struct acx_header header; /* bitmask of capability bits supported by the peer */ __le32 ht_capabilites; /* Indicates to which link these capabilities apply. */ u8 hlid; /* * This the maximum A-MPDU length supported by the AP. The FW may not * exceed this length when sending A-MPDUs */ u8 ampdu_max_length; /* This is the minimal spacing required when sending A-MPDUs to the AP*/ u8 ampdu_min_spacing; u8 padding; } __packed; /* * ACX_HT_BSS_OPERATION * Configure HT capabilities - AP rules for behavior in the BSS. */ struct wl1271_acx_ht_information { struct acx_header header; u8 role_id; /* Values: 0 - RIFS not allowed, 1 - RIFS allowed */ u8 rifs_mode; /* Values: 0 - 3 like in spec */ u8 ht_protection; /* Values: 0 - GF protection not required, 1 - GF protection required */ u8 gf_protection; /*Values: 0 - TX Burst limit not required, 1 - TX Burst Limit required*/ u8 ht_tx_burst_limit; /* * Values: 0 - Dual CTS protection not required, * 1 - Dual CTS Protection required * Note: When this value is set to 1 FW will protect all TXOP with RTS * frame and will not use CTS-to-self regardless of the value of the * ACX_CTS_PROTECTION information element */ u8 dual_cts_protection; u8 padding[2]; } __packed; #define RX_BA_MAX_SESSIONS 3 struct wl1271_acx_ba_initiator_policy { struct acx_header header; /* Specifies role Id, Range 0-7, 0xFF means ANY role. */ u8 role_id; /* * Per TID setting for allowing TX BA. Set a bit to 1 to allow * TX BA sessions for the corresponding TID. */ u8 tid_bitmap; /* Windows size in number of packets */ u8 win_size; u8 padding1[1]; /* As initiator inactivity timeout in time units(TU) of 1024us */ u16 inactivity_timeout; u8 padding[2]; } __packed; struct wl1271_acx_ba_receiver_setup { struct acx_header header; /* Specifies link id, range 0-31 */ u8 hlid; u8 tid; u8 enable; /* Windows size in number of packets */ u8 win_size; /* BA session starting sequence number. RANGE 0-FFF */ u16 ssn; u8 padding[2]; } __packed; struct wl12xx_acx_fw_tsf_information { struct acx_header header; u8 role_id; u8 padding1[3]; __le32 current_tsf_high; __le32 current_tsf_low; __le32 last_bttt_high; __le32 last_tbtt_low; u8 last_dtim_count; u8 padding2[3]; } __packed; struct wl1271_acx_ps_rx_streaming { struct acx_header header; u8 role_id; u8 tid; u8 enable; /* interval between triggers (10-100 msec) */ u8 period; /* timeout before first trigger (0-200 msec) */ u8 timeout; u8 padding[3]; } __packed; struct wl1271_acx_ap_max_tx_retry { struct acx_header header; u8 role_id; u8 padding_1; /* * the number of frames transmission failures before * issuing the aging event. */ __le16 max_tx_retry; } __packed; struct wl1271_acx_config_ps { struct acx_header header; u8 exit_retries; u8 enter_retries; u8 padding[2]; __le32 null_data_rate; } __packed; struct wl1271_acx_inconnection_sta { struct acx_header header; u8 addr[ETH_ALEN]; u8 padding1[2]; } __packed; /* * ACX_FM_COEX_CFG * set the FM co-existence parameters. */ struct wl1271_acx_fm_coex { struct acx_header header; /* enable(1) / disable(0) the FM Coex feature */ u8 enable; /* * Swallow period used in COEX PLL swallowing mechanism. * 0xFF = use FW default */ u8 swallow_period; /* * The N divider used in COEX PLL swallowing mechanism for Fref of * 38.4/19.2 Mhz. 0xFF = use FW default */ u8 n_divider_fref_set_1; /* * The N divider used in COEX PLL swallowing mechanism for Fref of * 26/52 Mhz. 0xFF = use FW default */ u8 n_divider_fref_set_2; /* * The M divider used in COEX PLL swallowing mechanism for Fref of * 38.4/19.2 Mhz. 0xFFFF = use FW default */ __le16 m_divider_fref_set_1; /* * The M divider used in COEX PLL swallowing mechanism for Fref of * 26/52 Mhz. 0xFFFF = use FW default */ __le16 m_divider_fref_set_2; /* * The time duration in uSec required for COEX PLL to stabilize. * 0xFFFFFFFF = use FW default */ __le32 coex_pll_stabilization_time; /* * The time duration in uSec required for LDO to stabilize. * 0xFFFFFFFF = use FW default */ __le16 ldo_stabilization_time; /* * The disturbed frequency band margin around the disturbed frequency * center (single sided). * For example, if 2 is configured, the following channels will be * considered disturbed channel: * 80 +- 0.1 MHz, 91 +- 0.1 MHz, 98 +- 0.1 MHz, 102 +- 0.1 MH * 0xFF = use FW default */ u8 fm_disturbed_band_margin; /* * The swallow clock difference of the swallowing mechanism. * 0xFF = use FW default */ u8 swallow_clk_diff; } __packed; #define ACX_RATE_MGMT_ALL_PARAMS 0xff struct wl12xx_acx_set_rate_mgmt_params { struct acx_header header; u8 index; /* 0xff to configure all params */ u8 padding1; __le16 rate_retry_score; __le16 per_add; __le16 per_th1; __le16 per_th2; __le16 max_per; u8 inverse_curiosity_factor; u8 tx_fail_low_th; u8 tx_fail_high_th; u8 per_alpha_shift; u8 per_add_shift; u8 per_beta1_shift; u8 per_beta2_shift; u8 rate_check_up; u8 rate_check_down; u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES]; u8 padding2[2]; } __packed; struct wl12xx_acx_config_hangover { struct acx_header header; __le32 recover_time; u8 hangover_period; u8 dynamic_mode; u8 early_termination_mode; u8 max_period; u8 min_period; u8 increase_delta; u8 decrease_delta; u8 quiet_time; u8 increase_time; u8 window_size; u8 padding[2]; } __packed; struct acx_default_rx_filter { struct acx_header header; u8 enable; /* action of type FILTER_XXX */ u8 default_action; u8 pad[2]; } __packed; struct acx_rx_filter_cfg { struct acx_header header; u8 enable; /* 0 - WL1271_MAX_RX_FILTERS-1 */ u8 index; u8 action; u8 num_fields; u8 fields[0]; } __packed; enum { ACX_WAKE_UP_CONDITIONS = 0x0000, ACX_MEM_CFG = 0x0001, ACX_SLOT = 0x0002, ACX_AC_CFG = 0x0003, ACX_MEM_MAP = 0x0004, ACX_AID = 0x0005, ACX_MEDIUM_USAGE = 0x0006, ACX_STATISTICS = 0x0007, ACX_PWR_CONSUMPTION_STATISTICS = 0x0008, ACX_TID_CFG = 0x0009, ACX_PS_RX_STREAMING = 0x000A, ACX_BEACON_FILTER_OPT = 0x000B, ACX_NOISE_HIST = 0x000C, ACX_HDK_VERSION = 0x000D, ACX_PD_THRESHOLD = 0x000E, ACX_TX_CONFIG_OPT = 0x000F, ACX_CCA_THRESHOLD = 0x0010, ACX_EVENT_MBOX_MASK = 0x0011, ACX_CONN_MONIT_PARAMS = 0x0012, ACX_DISABLE_BROADCASTS = 0x0013, ACX_BCN_DTIM_OPTIONS = 0x0014, ACX_SG_ENABLE = 0x0015, ACX_SG_CFG = 0x0016, ACX_FM_COEX_CFG = 0x0017, ACX_BEACON_FILTER_TABLE = 0x0018, ACX_ARP_IP_FILTER = 0x0019, ACX_ROAMING_STATISTICS_TBL = 0x001A, ACX_RATE_POLICY = 0x001B, ACX_CTS_PROTECTION = 0x001C, ACX_SLEEP_AUTH = 0x001D, ACX_PREAMBLE_TYPE = 0x001E, ACX_ERROR_CNT = 0x001F, ACX_IBSS_FILTER = 0x0020, ACX_SERVICE_PERIOD_TIMEOUT = 0x0021, ACX_TSF_INFO = 0x0022, ACX_CONFIG_PS_WMM = 0x0023, ACX_ENABLE_RX_DATA_FILTER = 0x0024, ACX_SET_RX_DATA_FILTER = 0x0025, ACX_GET_DATA_FILTER_STATISTICS = 0x0026, ACX_RX_CONFIG_OPT = 0x0027, ACX_FRAG_CFG = 0x0028, ACX_BET_ENABLE = 0x0029, ACX_RSSI_SNR_TRIGGER = 0x002A, ACX_RSSI_SNR_WEIGHTS = 0x002B, ACX_KEEP_ALIVE_MODE = 0x002C, ACX_SET_KEEP_ALIVE_CONFIG = 0x002D, ACX_BA_SESSION_INIT_POLICY = 0x002E, ACX_BA_SESSION_RX_SETUP = 0x002F, ACX_PEER_HT_CAP = 0x0030, ACX_HT_BSS_OPERATION = 0x0031, ACX_COEX_ACTIVITY = 0x0032, ACX_BURST_MODE = 0x0033, ACX_SET_RATE_MGMT_PARAMS = 0x0034, ACX_GET_RATE_MGMT_PARAMS = 0x0035, ACX_SET_RATE_ADAPT_PARAMS = 0x0036, ACX_SET_DCO_ITRIM_PARAMS = 0x0037, ACX_GEN_FW_CMD = 0x0038, ACX_HOST_IF_CFG_BITMAP = 0x0039, ACX_MAX_TX_FAILURE = 0x003A, ACX_UPDATE_INCONNECTION_STA_LIST = 0x003B, DOT11_RX_MSDU_LIFE_TIME = 0x003C, DOT11_CUR_TX_PWR = 0x003D, DOT11_RTS_THRESHOLD = 0x003E, DOT11_GROUP_ADDRESS_TBL = 0x003F, ACX_PM_CONFIG = 0x0040, ACX_CONFIG_PS = 0x0041, ACX_CONFIG_HANGOVER = 0x0042, ACX_FEATURE_CFG = 0x0043, ACX_PROTECTION_CFG = 0x0044, ACX_CHECKSUM_CONFIG = 0x0045, }; int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 wake_up_event, u8 listen_interval); int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth); int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif, int power); int wl1271_acx_feature_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, size_t len); int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl); int wl1271_acx_slot(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_slot_type slot_time); int wl1271_acx_group_address_tbl(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable, void *mc_list, u32 mc_list_len); int wl1271_acx_service_period_timeout(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_rts_threshold(struct wl1271 *wl, struct wl12xx_vif *wlvif, u32 rts_threshold); int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_conn_monit_params(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable); int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable); int wl12xx_acx_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); int wl1271_acx_bcn_dtim_options(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_aid(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid); int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask); int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_preamble_type preamble); int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_ctsprotect_type ctsprotect); int wl1271_acx_statistics(struct wl1271 *wl, void *stats); int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, u8 idx); int wl1271_acx_ac_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop); int wl1271_acx_tid_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue_id, u8 channel_type, u8 tsid, u8 ps_scheme, u8 ack_policy, u32 apsd_conf0, u32 apsd_conf1); int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold); int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl12xx_acx_mem_cfg(struct wl1271 *wl); int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable); int wl1271_acx_arp_ip_filter(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 enable, __be32 address); int wl1271_acx_pm_config(struct wl1271 *wl); int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *vif, bool enable); int wl1271_acx_keep_alive_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 index, u8 tpl_valid); int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable, s16 thold, u8 hyst); int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, u8 hlid); int wl1271_acx_set_ht_information(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 ht_operation_mode); int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable, u8 peer_hlid); int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, u64 *mactime); int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable); int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); int wl1271_acx_fm_coex(struct wl1271 *wl); int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl); int wl12xx_acx_config_hangover(struct wl1271 *wl); #ifdef CONFIG_PM int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable, enum rx_filter_action action); int wl1271_acx_set_rx_filter(struct wl1271 *wl, u8 index, bool enable, struct wl12xx_rx_filter *filter); #endif /* CONFIG_PM */ #endif /* __WL1271_ACX_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/acx.c0000644000175000017500000012157712026211315023324 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "acx.h" #include #include #include #include #include "wlcore.h" #include "debug.h" #include "wl12xx_80211.h" #include "ps.h" #include "hw_ops.h" int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 wake_up_event, u8 listen_interval) { struct acx_wake_up_condition *wake_up; int ret; wl1271_debug(DEBUG_ACX, "acx wake up conditions (wake_up_event %d listen_interval %d)", wake_up_event, listen_interval); wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); if (!wake_up) { ret = -ENOMEM; goto out; } wake_up->role_id = wlvif->role_id; wake_up->wake_up_event = wake_up_event; wake_up->listen_interval = listen_interval; ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, wake_up, sizeof(*wake_up)); if (ret < 0) { wl1271_warning("could not set wake up conditions: %d", ret); goto out; } out: kfree(wake_up); return ret; } int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth) { struct acx_sleep_auth *auth; int ret; wl1271_debug(DEBUG_ACX, "acx sleep auth %d", sleep_auth); auth = kzalloc(sizeof(*auth), GFP_KERNEL); if (!auth) { ret = -ENOMEM; goto out; } auth->sleep_auth = sleep_auth; ret = wl1271_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); if (ret < 0) { wl1271_error("could not configure sleep_auth to %d: %d", sleep_auth, ret); goto out; } wl->sleep_auth = sleep_auth; out: kfree(auth); return ret; } EXPORT_SYMBOL_GPL(wl1271_acx_sleep_auth); int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif, int power) { struct acx_current_tx_power *acx; int ret; wl1271_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr %d", power); if (power < 0 || power > 25) return -EINVAL; acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->current_tx_power = power * 10; ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("configure of tx power failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_feature_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct acx_feature_config *feature; int ret; wl1271_debug(DEBUG_ACX, "acx feature cfg"); feature = kzalloc(sizeof(*feature), GFP_KERNEL); if (!feature) { ret = -ENOMEM; goto out; } /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ feature->role_id = wlvif->role_id; feature->data_flow_options = 0; feature->options = 0; ret = wl1271_cmd_configure(wl, ACX_FEATURE_CFG, feature, sizeof(*feature)); if (ret < 0) { wl1271_error("Couldnt set HW encryption"); goto out; } out: kfree(feature); return ret; } int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, size_t len) { int ret; wl1271_debug(DEBUG_ACX, "acx mem map"); ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len); if (ret < 0) return ret; return 0; } int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl) { struct acx_rx_msdu_lifetime *acx; int ret; wl1271_debug(DEBUG_ACX, "acx rx msdu life time"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->lifetime = cpu_to_le32(wl->conf.rx.rx_msdu_life_time); ret = wl1271_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("failed to set rx msdu life time: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_slot(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_slot_type slot_time) { struct acx_slot *slot; int ret; wl1271_debug(DEBUG_ACX, "acx slot"); slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) { ret = -ENOMEM; goto out; } slot->role_id = wlvif->role_id; slot->wone_index = STATION_WONE_INDEX; slot->slot_time = slot_time; ret = wl1271_cmd_configure(wl, ACX_SLOT, slot, sizeof(*slot)); if (ret < 0) { wl1271_warning("failed to set slot time: %d", ret); goto out; } out: kfree(slot); return ret; } int wl1271_acx_group_address_tbl(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable, void *mc_list, u32 mc_list_len) { struct acx_dot11_grp_addr_tbl *acx; int ret; wl1271_debug(DEBUG_ACX, "acx group address tbl"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } /* MAC filtering */ acx->role_id = wlvif->role_id; acx->enabled = enable; acx->num_groups = mc_list_len; memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN); ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("failed to set group addr table: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_service_period_timeout(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct acx_rx_timeout *rx_timeout; int ret; rx_timeout = kzalloc(sizeof(*rx_timeout), GFP_KERNEL); if (!rx_timeout) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_ACX, "acx service period timeout"); rx_timeout->role_id = wlvif->role_id; rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout); rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout); ret = wl1271_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT, rx_timeout, sizeof(*rx_timeout)); if (ret < 0) { wl1271_warning("failed to set service period timeout: %d", ret); goto out; } out: kfree(rx_timeout); return ret; } int wl1271_acx_rts_threshold(struct wl1271 *wl, struct wl12xx_vif *wlvif, u32 rts_threshold) { struct acx_rts_threshold *rts; int ret; /* * If the RTS threshold is not configured or out of range, use the * default value. */ if (rts_threshold > IEEE80211_MAX_RTS_THRESHOLD) rts_threshold = wl->conf.rx.rts_threshold; wl1271_debug(DEBUG_ACX, "acx rts threshold: %d", rts_threshold); rts = kzalloc(sizeof(*rts), GFP_KERNEL); if (!rts) { ret = -ENOMEM; goto out; } rts->role_id = wlvif->role_id; rts->threshold = cpu_to_le16((u16)rts_threshold); ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); if (ret < 0) { wl1271_warning("failed to set rts threshold: %d", ret); goto out; } out: kfree(rts); return ret; } int wl1271_acx_dco_itrim_params(struct wl1271 *wl) { struct acx_dco_itrim_params *dco; struct conf_itrim_settings *c = &wl->conf.itrim; int ret; wl1271_debug(DEBUG_ACX, "acx dco itrim parameters"); dco = kzalloc(sizeof(*dco), GFP_KERNEL); if (!dco) { ret = -ENOMEM; goto out; } dco->enable = c->enable; dco->timeout = cpu_to_le32(c->timeout); ret = wl1271_cmd_configure(wl, ACX_SET_DCO_ITRIM_PARAMS, dco, sizeof(*dco)); if (ret < 0) { wl1271_warning("failed to set dco itrim parameters: %d", ret); goto out; } out: kfree(dco); return ret; } int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable_filter) { struct acx_beacon_filter_option *beacon_filter = NULL; int ret = 0; wl1271_debug(DEBUG_ACX, "acx beacon filter opt"); if (enable_filter && wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED) goto out; beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); if (!beacon_filter) { ret = -ENOMEM; goto out; } beacon_filter->role_id = wlvif->role_id; beacon_filter->enable = enable_filter; /* * When set to zero, and the filter is enabled, beacons * without the unicast TIM bit set are dropped. */ beacon_filter->max_num_beacons = 0; ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_OPT, beacon_filter, sizeof(*beacon_filter)); if (ret < 0) { wl1271_warning("failed to set beacon filter opt: %d", ret); goto out; } out: kfree(beacon_filter); return ret; } int wl1271_acx_beacon_filter_table(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct acx_beacon_filter_ie_table *ie_table; int i, idx = 0; int ret; bool vendor_spec = false; wl1271_debug(DEBUG_ACX, "acx beacon filter table"); ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL); if (!ie_table) { ret = -ENOMEM; goto out; } /* configure default beacon pass-through rules */ ie_table->role_id = wlvif->role_id; ie_table->num_ie = 0; for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) { struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]); ie_table->table[idx++] = r->ie; ie_table->table[idx++] = r->rule; if (r->ie == WLAN_EID_VENDOR_SPECIFIC) { /* only one vendor specific ie allowed */ if (vendor_spec) continue; /* for vendor specific rules configure the additional fields */ memcpy(&(ie_table->table[idx]), r->oui, CONF_BCN_IE_OUI_LEN); idx += CONF_BCN_IE_OUI_LEN; ie_table->table[idx++] = r->type; memcpy(&(ie_table->table[idx]), r->version, CONF_BCN_IE_VER_LEN); idx += CONF_BCN_IE_VER_LEN; vendor_spec = true; } ie_table->num_ie++; } ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, ie_table, sizeof(*ie_table)); if (ret < 0) { wl1271_warning("failed to set beacon filter table: %d", ret); goto out; } out: kfree(ie_table); return ret; } #define ACX_CONN_MONIT_DISABLE_VALUE 0xffffffff int wl1271_acx_conn_monit_params(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable) { struct acx_conn_monit_params *acx; u32 threshold = ACX_CONN_MONIT_DISABLE_VALUE; u32 timeout = ACX_CONN_MONIT_DISABLE_VALUE; int ret; wl1271_debug(DEBUG_ACX, "acx connection monitor parameters: %s", enable ? "enabled" : "disabled"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } if (enable) { threshold = wl->conf.conn.synch_fail_thold; timeout = wl->conf.conn.bss_lose_timeout; } acx->role_id = wlvif->role_id; acx->synch_fail_thold = cpu_to_le32(threshold); acx->bss_lose_timeout = cpu_to_le32(timeout); ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("failed to set connection monitor " "parameters: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable) { struct acx_bt_wlan_coex *pta; int ret; wl1271_debug(DEBUG_ACX, "acx sg enable"); pta = kzalloc(sizeof(*pta), GFP_KERNEL); if (!pta) { ret = -ENOMEM; goto out; } if (enable) pta->enable = wl->conf.sg.state; else pta->enable = CONF_SG_DISABLE; ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); if (ret < 0) { wl1271_warning("failed to set softgemini enable: %d", ret); goto out; } out: kfree(pta); return ret; } int wl12xx_acx_sg_cfg(struct wl1271 *wl) { struct acx_bt_wlan_coex_param *param; struct conf_sg_settings *c = &wl->conf.sg; int i, ret; wl1271_debug(DEBUG_ACX, "acx sg cfg"); param = kzalloc(sizeof(*param), GFP_KERNEL); if (!param) { ret = -ENOMEM; goto out; } /* BT-WLAN coext parameters */ for (i = 0; i < CONF_SG_PARAMS_MAX; i++) param->params[i] = cpu_to_le32(c->params[i]); param->param_idx = CONF_SG_PARAMS_ALL; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { wl1271_warning("failed to set sg config: %d", ret); goto out; } out: kfree(param); return ret; } int wl1271_acx_cca_threshold(struct wl1271 *wl) { struct acx_energy_detection *detection; int ret; wl1271_debug(DEBUG_ACX, "acx cca threshold"); detection = kzalloc(sizeof(*detection), GFP_KERNEL); if (!detection) { ret = -ENOMEM; goto out; } detection->rx_cca_threshold = cpu_to_le16(wl->conf.rx.rx_cca_threshold); detection->tx_energy_detection = wl->conf.tx.tx_energy_detection; ret = wl1271_cmd_configure(wl, ACX_CCA_THRESHOLD, detection, sizeof(*detection)); if (ret < 0) wl1271_warning("failed to set cca threshold: %d", ret); out: kfree(detection); return ret; } int wl1271_acx_bcn_dtim_options(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct acx_beacon_broadcast *bb; int ret; wl1271_debug(DEBUG_ACX, "acx bcn dtim options"); bb = kzalloc(sizeof(*bb), GFP_KERNEL); if (!bb) { ret = -ENOMEM; goto out; } bb->role_id = wlvif->role_id; bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout); bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout); bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps; bb->ps_poll_threshold = wl->conf.conn.ps_poll_threshold; ret = wl1271_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb)); if (ret < 0) { wl1271_warning("failed to set rx config: %d", ret); goto out; } out: kfree(bb); return ret; } int wl1271_acx_aid(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid) { struct acx_aid *acx_aid; int ret; wl1271_debug(DEBUG_ACX, "acx aid"); acx_aid = kzalloc(sizeof(*acx_aid), GFP_KERNEL); if (!acx_aid) { ret = -ENOMEM; goto out; } acx_aid->role_id = wlvif->role_id; acx_aid->aid = cpu_to_le16(aid); ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid)); if (ret < 0) { wl1271_warning("failed to set aid: %d", ret); goto out; } out: kfree(acx_aid); return ret; } int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask) { struct acx_event_mask *mask; int ret; wl1271_debug(DEBUG_ACX, "acx event mbox mask"); mask = kzalloc(sizeof(*mask), GFP_KERNEL); if (!mask) { ret = -ENOMEM; goto out; } /* high event mask is unused */ mask->high_event_mask = cpu_to_le32(0xffffffff); mask->event_mask = cpu_to_le32(event_mask); ret = wl1271_cmd_configure(wl, ACX_EVENT_MBOX_MASK, mask, sizeof(*mask)); if (ret < 0) { wl1271_warning("failed to set acx_event_mbox_mask: %d", ret); goto out; } out: kfree(mask); return ret; } int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_preamble_type preamble) { struct acx_preamble *acx; int ret; wl1271_debug(DEBUG_ACX, "acx_set_preamble"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->preamble = preamble; ret = wl1271_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of preamble failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_ctsprotect_type ctsprotect) { struct acx_ctsprotect *acx; int ret; wl1271_debug(DEBUG_ACX, "acx_set_ctsprotect"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->ctsprotect = ctsprotect; ret = wl1271_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of ctsprotect failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_statistics(struct wl1271 *wl, void *stats) { int ret; wl1271_debug(DEBUG_ACX, "acx statistics"); ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats, wl->stats.fw_stats_len); if (ret < 0) { wl1271_warning("acx statistics failed: %d", ret); return -ENOMEM; } return 0; } int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct acx_rate_policy *acx; struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf; int ret = 0; wl1271_debug(DEBUG_ACX, "acx rate policies"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } wl1271_debug(DEBUG_ACX, "basic_rate: 0x%x, full_rate: 0x%x", wlvif->basic_rate, wlvif->rate_set); /* configure one basic rate class */ acx->rate_policy_idx = cpu_to_le32(wlvif->sta.basic_rate_idx); acx->rate_policy.enabled_rates = cpu_to_le32(wlvif->basic_rate); acx->rate_policy.short_retry_limit = c->short_retry_limit; acx->rate_policy.long_retry_limit = c->long_retry_limit; acx->rate_policy.aflags = c->aflags; ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of rate policies failed: %d", ret); goto out; } /* configure one AP supported rate class */ acx->rate_policy_idx = cpu_to_le32(wlvif->sta.ap_rate_idx); /* the AP policy is HW specific */ acx->rate_policy.enabled_rates = cpu_to_le32(wlcore_hw_sta_get_ap_rate_mask(wl, wlvif)); acx->rate_policy.short_retry_limit = c->short_retry_limit; acx->rate_policy.long_retry_limit = c->long_retry_limit; acx->rate_policy.aflags = c->aflags; ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of rate policies failed: %d", ret); goto out; } /* * configure one rate class for basic p2p operations. * (p2p packets should always go out with OFDM rates, even * if we are currently connected to 11b AP) */ acx->rate_policy_idx = cpu_to_le32(wlvif->sta.p2p_rate_idx); acx->rate_policy.enabled_rates = cpu_to_le32(CONF_TX_RATE_MASK_BASIC_P2P); acx->rate_policy.short_retry_limit = c->short_retry_limit; acx->rate_policy.long_retry_limit = c->long_retry_limit; acx->rate_policy.aflags = c->aflags; ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of rate policies failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, u8 idx) { struct acx_rate_policy *acx; int ret = 0; wl1271_debug(DEBUG_ACX, "acx ap rate policy %d rates 0x%x", idx, c->enabled_rates); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->rate_policy.enabled_rates = cpu_to_le32(c->enabled_rates); acx->rate_policy.short_retry_limit = c->short_retry_limit; acx->rate_policy.long_retry_limit = c->long_retry_limit; acx->rate_policy.aflags = c->aflags; acx->rate_policy_idx = cpu_to_le32(idx); ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of ap rate policy failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_ac_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop) { struct acx_ac_cfg *acx; int ret = 0; wl1271_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d " "aifs %d txop %d", ac, cw_min, cw_max, aifsn, txop); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->ac = ac; acx->cw_min = cw_min; acx->cw_max = cpu_to_le16(cw_max); acx->aifsn = aifsn; acx->tx_op_limit = cpu_to_le16(txop); ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx ac cfg failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_tid_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue_id, u8 channel_type, u8 tsid, u8 ps_scheme, u8 ack_policy, u32 apsd_conf0, u32 apsd_conf1) { struct acx_tid_config *acx; int ret = 0; wl1271_debug(DEBUG_ACX, "acx tid config"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->queue_id = queue_id; acx->channel_type = channel_type; acx->tsid = tsid; acx->ps_scheme = ps_scheme; acx->ack_policy = ack_policy; acx->apsd_conf[0] = cpu_to_le32(apsd_conf0); acx->apsd_conf[1] = cpu_to_le32(apsd_conf1); ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of tid config failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold) { struct acx_frag_threshold *acx; int ret = 0; /* * If the fragmentation is not configured or out of range, use the * default value. */ if (frag_threshold > IEEE80211_MAX_FRAG_THRESHOLD) frag_threshold = wl->conf.tx.frag_threshold; wl1271_debug(DEBUG_ACX, "acx frag threshold: %d", frag_threshold); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->frag_threshold = cpu_to_le16((u16)frag_threshold); ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of frag threshold failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_tx_config_options(struct wl1271 *wl) { struct acx_tx_config_options *acx; int ret = 0; wl1271_debug(DEBUG_ACX, "acx tx config options"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->tx_compl_timeout = cpu_to_le16(wl->conf.tx.tx_compl_timeout); acx->tx_compl_threshold = cpu_to_le16(wl->conf.tx.tx_compl_threshold); ret = wl1271_cmd_configure(wl, ACX_TX_CONFIG_OPT, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of tx options failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl12xx_acx_mem_cfg(struct wl1271 *wl) { struct wl12xx_acx_config_memory *mem_conf; struct conf_memory_settings *mem; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL); if (!mem_conf) { ret = -ENOMEM; goto out; } mem = &wl->conf.mem; /* memory config */ mem_conf->num_stations = mem->num_stations; mem_conf->rx_mem_block_num = mem->rx_block_num; mem_conf->tx_min_mem_block_num = mem->tx_min_block_num; mem_conf->num_ssid_profiles = mem->ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(wl->num_tx_desc); mem_conf->dyn_mem_enable = mem->dynamic_memory; mem_conf->tx_free_req = mem->min_req_tx_blocks; mem_conf->rx_free_req = mem->min_req_rx_blocks; mem_conf->tx_min = mem->tx_min; mem_conf->fwlog_blocks = wl->conf.fwlog.mem_blocks; ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); if (ret < 0) { wl1271_warning("wl1271 mem config failed: %d", ret); goto out; } out: kfree(mem_conf); return ret; } EXPORT_SYMBOL_GPL(wl12xx_acx_mem_cfg); int wl1271_acx_init_mem_config(struct wl1271 *wl) { int ret; wl->target_mem_map = kzalloc(sizeof(struct wl1271_acx_mem_map), GFP_KERNEL); if (!wl->target_mem_map) { wl1271_error("couldn't allocate target memory map"); return -ENOMEM; } /* we now ask for the firmware built memory map */ ret = wl1271_acx_mem_map(wl, (void *)wl->target_mem_map, sizeof(struct wl1271_acx_mem_map)); if (ret < 0) { wl1271_error("couldn't retrieve firmware memory map"); kfree(wl->target_mem_map); wl->target_mem_map = NULL; return ret; } /* initialize TX block book keeping */ wl->tx_blocks_available = le32_to_cpu(wl->target_mem_map->num_tx_mem_blocks); wl1271_debug(DEBUG_TX, "available tx blocks: %d", wl->tx_blocks_available); return 0; } EXPORT_SYMBOL_GPL(wl1271_acx_init_mem_config); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl) { struct wl1271_acx_rx_config_opt *rx_conf; int ret; wl1271_debug(DEBUG_ACX, "wl1271 rx interrupt config"); rx_conf = kzalloc(sizeof(*rx_conf), GFP_KERNEL); if (!rx_conf) { ret = -ENOMEM; goto out; } rx_conf->threshold = cpu_to_le16(wl->conf.rx.irq_pkt_threshold); rx_conf->timeout = cpu_to_le16(wl->conf.rx.irq_timeout); rx_conf->mblk_threshold = cpu_to_le16(wl->conf.rx.irq_blk_threshold); rx_conf->queue_type = wl->conf.rx.queue_type; ret = wl1271_cmd_configure(wl, ACX_RX_CONFIG_OPT, rx_conf, sizeof(*rx_conf)); if (ret < 0) { wl1271_warning("wl1271 rx config opt failed: %d", ret); goto out; } out: kfree(rx_conf); return ret; } int wl1271_acx_bet_enable(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable) { struct wl1271_acx_bet_enable *acx = NULL; int ret = 0; wl1271_debug(DEBUG_ACX, "acx bet enable"); if (enable && wl->conf.conn.bet_enable == CONF_BET_MODE_DISABLE) goto out; acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE; acx->max_consecutive = wl->conf.conn.bet_max_consecutive; ret = wl1271_cmd_configure(wl, ACX_BET_ENABLE, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx bet enable failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_arp_ip_filter(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 enable, __be32 address) { struct wl1271_acx_arp_filter *acx; int ret; wl1271_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->version = ACX_IPV4_VERSION; acx->enable = enable; if (enable) memcpy(acx->address, &address, ACX_IPV4_ADDR_SIZE); ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("failed to set arp ip filter: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_pm_config(struct wl1271 *wl) { struct wl1271_acx_pm_config *acx = NULL; struct conf_pm_config_settings *c = &wl->conf.pm_config; int ret = 0; wl1271_debug(DEBUG_ACX, "acx pm config"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->host_clk_settling_time = cpu_to_le32(c->host_clk_settling_time); acx->host_fast_wakeup_support = c->host_fast_wakeup_support; ret = wl1271_cmd_configure(wl, ACX_PM_CONFIG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx pm config failed: %d", ret); goto out; } out: kfree(acx); return ret; } EXPORT_SYMBOL_GPL(wl1271_acx_pm_config); int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable) { struct wl1271_acx_keep_alive_mode *acx = NULL; int ret = 0; wl1271_debug(DEBUG_ACX, "acx keep alive mode: %d", enable); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->enabled = enable; ret = wl1271_cmd_configure(wl, ACX_KEEP_ALIVE_MODE, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx keep alive mode failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_keep_alive_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 index, u8 tpl_valid) { struct wl1271_acx_keep_alive_config *acx = NULL; int ret = 0; wl1271_debug(DEBUG_ACX, "acx keep alive config"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->period = cpu_to_le32(wl->conf.conn.keep_alive_interval); acx->index = index; acx->tpl_validation = tpl_valid; acx->trigger = ACX_KEEP_ALIVE_NO_TX; ret = wl1271_cmd_configure(wl, ACX_SET_KEEP_ALIVE_CONFIG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx keep alive config failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable, s16 thold, u8 hyst) { struct wl1271_acx_rssi_snr_trigger *acx = NULL; int ret = 0; wl1271_debug(DEBUG_ACX, "acx rssi snr trigger"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } wlvif->last_rssi_event = -1; acx->role_id = wlvif->role_id; acx->pacing = cpu_to_le16(wl->conf.roam_trigger.trigger_pacing); acx->metric = WL1271_ACX_TRIG_METRIC_RSSI_BEACON; acx->type = WL1271_ACX_TRIG_TYPE_EDGE; if (enable) acx->enable = WL1271_ACX_TRIG_ENABLE; else acx->enable = WL1271_ACX_TRIG_DISABLE; acx->index = WL1271_ACX_TRIG_IDX_RSSI; acx->dir = WL1271_ACX_TRIG_DIR_BIDIR; acx->threshold = cpu_to_le16(thold); acx->hysteresis = hyst; ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_TRIGGER, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx rssi snr trigger setting failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl1271_acx_rssi_snr_avg_weights *acx = NULL; struct conf_roam_trigger_settings *c = &wl->conf.roam_trigger; int ret = 0; wl1271_debug(DEBUG_ACX, "acx rssi snr avg weights"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->rssi_beacon = c->avg_weight_rssi_beacon; acx->rssi_data = c->avg_weight_rssi_data; acx->snr_beacon = c->avg_weight_snr_beacon; acx->snr_data = c->avg_weight_snr_data; ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_WEIGHTS, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx rssi snr trigger weights failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, u8 hlid) { struct wl1271_acx_ht_capabilities *acx; int ret = 0; u32 ht_capabilites = 0; wl1271_debug(DEBUG_ACX, "acx ht capabilities setting " "sta supp: %d sta cap: %d", ht_cap->ht_supported, ht_cap->cap); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } if (allow_ht_operation && ht_cap->ht_supported) { /* no need to translate capabilities - use the spec values */ ht_capabilites = ht_cap->cap; /* * this bit is not employed by the spec but only by FW to * indicate peer HT support */ ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION; /* get data from A-MPDU parameters field */ acx->ampdu_max_length = ht_cap->ampdu_factor; acx->ampdu_min_spacing = ht_cap->ampdu_density; } acx->hlid = hlid; acx->ht_capabilites = cpu_to_le32(ht_capabilites); ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx ht capabilities setting failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_set_ht_information(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 ht_operation_mode) { struct wl1271_acx_ht_information *acx; int ret = 0; wl1271_debug(DEBUG_ACX, "acx ht information setting"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->role_id = wlvif->role_id; acx->ht_protection = (u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION); acx->rifs_mode = 0; acx->gf_protection = !!(ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); acx->ht_tx_burst_limit = 0; acx->dual_cts_protection = 0; ret = wl1271_cmd_configure(wl, ACX_HT_BSS_OPERATION, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx ht information setting failed: %d", ret); goto out; } out: kfree(acx); return ret; } /* Configure BA session initiator/receiver parameters setting in the FW. */ int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl1271_acx_ba_initiator_policy *acx; int ret; wl1271_debug(DEBUG_ACX, "acx ba initiator policy"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } /* set for the current role */ acx->role_id = wlvif->role_id; acx->tid_bitmap = wl->conf.ht.tx_ba_tid_bitmap; acx->win_size = wl->conf.ht.tx_ba_win_size; acx->inactivity_timeout = wl->conf.ht.inactivity_timeout; ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_INIT_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx ba initiator policy failed: %d", ret); goto out; } out: kfree(acx); return ret; } /* setup BA session receiver setting in the FW. */ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable, u8 peer_hlid) { struct wl1271_acx_ba_receiver_setup *acx; int ret; wl1271_debug(DEBUG_ACX, "acx ba receiver session setting"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->hlid = peer_hlid; acx->tid = tid_index; acx->enable = enable; acx->win_size = wl->conf.ht.rx_ba_win_size; acx->ssn = ssn; ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx ba receiver session failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, u64 *mactime) { struct wl12xx_acx_fw_tsf_information *tsf_info; int ret; tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL); if (!tsf_info) { ret = -ENOMEM; goto out; } tsf_info->role_id = wlvif->role_id; ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info, sizeof(*tsf_info)); if (ret < 0) { wl1271_warning("acx tsf info interrogate failed"); goto out; } *mactime = le32_to_cpu(tsf_info->current_tsf_low) | ((u64) le32_to_cpu(tsf_info->current_tsf_high) << 32); out: kfree(tsf_info); return ret; } int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable) { struct wl1271_acx_ps_rx_streaming *rx_streaming; u32 conf_queues, enable_queues; int i, ret = 0; wl1271_debug(DEBUG_ACX, "acx ps rx streaming"); rx_streaming = kzalloc(sizeof(*rx_streaming), GFP_KERNEL); if (!rx_streaming) { ret = -ENOMEM; goto out; } conf_queues = wl->conf.rx_streaming.queues; if (enable) enable_queues = conf_queues; else enable_queues = 0; for (i = 0; i < 8; i++) { /* * Skip non-changed queues, to avoid redundant acxs. * this check assumes conf.rx_streaming.queues can't * be changed while rx_streaming is enabled. */ if (!(conf_queues & BIT(i))) continue; rx_streaming->role_id = wlvif->role_id; rx_streaming->tid = i; rx_streaming->enable = enable_queues & BIT(i); rx_streaming->period = wl->conf.rx_streaming.interval; rx_streaming->timeout = wl->conf.rx_streaming.interval; ret = wl1271_cmd_configure(wl, ACX_PS_RX_STREAMING, rx_streaming, sizeof(*rx_streaming)); if (ret < 0) { wl1271_warning("acx ps rx streaming failed: %d", ret); goto out; } } out: kfree(rx_streaming); return ret; } int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl1271_acx_ap_max_tx_retry *acx = NULL; int ret; wl1271_debug(DEBUG_ACX, "acx ap max tx retry"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; acx->role_id = wlvif->role_id; acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries); ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx ap max tx retry failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl1271_acx_config_ps *config_ps; int ret; wl1271_debug(DEBUG_ACX, "acx config ps"); config_ps = kzalloc(sizeof(*config_ps), GFP_KERNEL); if (!config_ps) { ret = -ENOMEM; goto out; } config_ps->exit_retries = wl->conf.conn.psm_exit_retries; config_ps->enter_retries = wl->conf.conn.psm_entry_retries; config_ps->null_data_rate = cpu_to_le32(wlvif->basic_rate); ret = wl1271_cmd_configure(wl, ACX_CONFIG_PS, config_ps, sizeof(*config_ps)); if (ret < 0) { wl1271_warning("acx config ps failed: %d", ret); goto out; } out: kfree(config_ps); return ret; } int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr) { struct wl1271_acx_inconnection_sta *acx = NULL; int ret; wl1271_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; memcpy(acx->addr, addr, ETH_ALEN); ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx set inconnaction sta failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1271_acx_fm_coex(struct wl1271 *wl) { struct wl1271_acx_fm_coex *acx; int ret; wl1271_debug(DEBUG_ACX, "acx fm coex setting"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->enable = wl->conf.fm_coex.enable; acx->swallow_period = wl->conf.fm_coex.swallow_period; acx->n_divider_fref_set_1 = wl->conf.fm_coex.n_divider_fref_set_1; acx->n_divider_fref_set_2 = wl->conf.fm_coex.n_divider_fref_set_2; acx->m_divider_fref_set_1 = cpu_to_le16(wl->conf.fm_coex.m_divider_fref_set_1); acx->m_divider_fref_set_2 = cpu_to_le16(wl->conf.fm_coex.m_divider_fref_set_2); acx->coex_pll_stabilization_time = cpu_to_le32(wl->conf.fm_coex.coex_pll_stabilization_time); acx->ldo_stabilization_time = cpu_to_le16(wl->conf.fm_coex.ldo_stabilization_time); acx->fm_disturbed_band_margin = wl->conf.fm_coex.fm_disturbed_band_margin; acx->swallow_clk_diff = wl->conf.fm_coex.swallow_clk_diff; ret = wl1271_cmd_configure(wl, ACX_FM_COEX_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx fm coex setting failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl) { struct wl12xx_acx_set_rate_mgmt_params *acx = NULL; struct conf_rate_policy_settings *conf = &wl->conf.rate; int ret; wl1271_debug(DEBUG_ACX, "acx set rate mgmt params"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; acx->index = ACX_RATE_MGMT_ALL_PARAMS; acx->rate_retry_score = cpu_to_le16(conf->rate_retry_score); acx->per_add = cpu_to_le16(conf->per_add); acx->per_th1 = cpu_to_le16(conf->per_th1); acx->per_th2 = cpu_to_le16(conf->per_th2); acx->max_per = cpu_to_le16(conf->max_per); acx->inverse_curiosity_factor = conf->inverse_curiosity_factor; acx->tx_fail_low_th = conf->tx_fail_low_th; acx->tx_fail_high_th = conf->tx_fail_high_th; acx->per_alpha_shift = conf->per_alpha_shift; acx->per_add_shift = conf->per_add_shift; acx->per_beta1_shift = conf->per_beta1_shift; acx->per_beta2_shift = conf->per_beta2_shift; acx->rate_check_up = conf->rate_check_up; acx->rate_check_down = conf->rate_check_down; memcpy(acx->rate_retry_policy, conf->rate_retry_policy, sizeof(acx->rate_retry_policy)); ret = wl1271_cmd_configure(wl, ACX_SET_RATE_MGMT_PARAMS, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx set rate mgmt params failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl12xx_acx_config_hangover(struct wl1271 *wl) { struct wl12xx_acx_config_hangover *acx; struct conf_hangover_settings *conf = &wl->conf.hangover; int ret; wl1271_debug(DEBUG_ACX, "acx config hangover"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->recover_time = cpu_to_le32(conf->recover_time); acx->hangover_period = conf->hangover_period; acx->dynamic_mode = conf->dynamic_mode; acx->early_termination_mode = conf->early_termination_mode; acx->max_period = conf->max_period; acx->min_period = conf->min_period; acx->increase_delta = conf->increase_delta; acx->decrease_delta = conf->decrease_delta; acx->quiet_time = conf->quiet_time; acx->increase_time = conf->increase_time; acx->window_size = acx->window_size; ret = wl1271_cmd_configure(wl, ACX_CONFIG_HANGOVER, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx config hangover failed: %d", ret); goto out; } out: kfree(acx); return ret; } #ifdef CONFIG_PM /* Set the global behaviour of RX filters - On/Off + default action */ int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable, enum rx_filter_action action) { struct acx_default_rx_filter *acx; int ret; wl1271_debug(DEBUG_ACX, "acx default rx filter en: %d act: %d", enable, action); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; acx->enable = enable; acx->default_action = action; ret = wl1271_cmd_configure(wl, ACX_ENABLE_RX_DATA_FILTER, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx default rx filter enable failed: %d", ret); goto out; } out: kfree(acx); return ret; } /* Configure or disable a specific RX filter pattern */ int wl1271_acx_set_rx_filter(struct wl1271 *wl, u8 index, bool enable, struct wl12xx_rx_filter *filter) { struct acx_rx_filter_cfg *acx; int fields_size = 0; int acx_size; int ret; WARN_ON(enable && !filter); WARN_ON(index >= WL1271_MAX_RX_FILTERS); wl1271_debug(DEBUG_ACX, "acx set rx filter idx: %d enable: %d filter: %p", index, enable, filter); if (enable) { fields_size = wl1271_rx_filter_get_fields_size(filter); wl1271_debug(DEBUG_ACX, "act: %d num_fields: %d field_size: %d", filter->action, filter->num_fields, fields_size); } acx_size = ALIGN(sizeof(*acx) + fields_size, 4); acx = kzalloc(acx_size, GFP_KERNEL); if (!acx) return -ENOMEM; acx->enable = enable; acx->index = index; if (enable) { acx->num_fields = filter->num_fields; acx->action = filter->action; wl1271_rx_filter_flatten_fields(filter, acx->fields); } wl1271_dump(DEBUG_ACX, "RX_FILTER: ", acx, acx_size); ret = wl1271_cmd_configure(wl, ACX_SET_RX_DATA_FILTER, acx, acx_size); if (ret < 0) { wl1271_warning("setting rx filter failed: %d", ret); goto out; } out: kfree(acx); return ret; } #endif /* CONFIG_PM */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/Makefile0000644000175000017500000000067412026211315024037 0ustar mcgrofmcgrofwlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ boot.o init.o debugfs.o scan.o wlcore_spi-objs = spi.o wlcore_sdio-objs = sdio.o wlcore-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_WLCORE) += wlcore.o obj-$(CONFIG_WLCORE_SPI) += wlcore_spi.o obj-$(CONFIG_WLCORE_SDIO) += wlcore_sdio.o # small builtin driver bit obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/ti/wlcore/Kconfig0000644000175000017500000000231612026211315023675 0ustar mcgrofmcgrofconfig WLCORE tristate "TI wlcore support" depends on WL_TI && GENERIC_HARDIRQS && MAC80211 select FW_LOADER ---help--- This module contains the main code for TI WLAN chips. It abstracts hardware-specific differences among different chipset families. Each chipset family needs to implement its own lower-level module that will depend on this module for the common code. If you choose to build a module, it will be called wlcore. Say N if unsure. config WLCORE_SPI tristate "TI wlcore SPI support" depends on WLCORE && SPI_MASTER select CRC7 ---help--- This module adds support for the SPI interface of adapters using TI WLAN chipsets. Select this if your platform is using the SPI bus. If you choose to build a module, it'll be called wlcore_spi. Say N if unsure. config WLCORE_SDIO tristate "TI wlcore SDIO support" depends on WLCORE && MMC ---help--- This module adds support for the SDIO interface of adapters using TI WLAN chipsets. Select this if your platform is using the SDIO bus. If you choose to build a module, it'll be called wlcore_sdio. Say N if unsure. config WL12XX_PLATFORM_DATA bool depends on WLCORE_SDIO != n || WL1251_SDIO != n default y compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/0000755000175000017500000000000012026211315022030 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/Makefile0000644000175000017500000000046612026211315023476 0ustar mcgrofmcgrofwl1251-objs = main.o event.o tx.o rx.o ps.o cmd.o \ acx.o boot.o init.o debugfs.o io.o wl1251_spi-objs += spi.o wl1251_sdio-objs += sdio.o obj-$(CONFIG_WL1251) += wl1251.o obj-$(CONFIG_COMPAT_WL1251_SPI)+= wl1251_spi.o obj-$(CONFIG_COMPAT_WL1251_SDIO)+= wl1251_sdio.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/spi.c0000644000175000017500000001664112026211315022777 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,28)) #include #endif #include #include #include "wl1251.h" #include "reg.h" #include "spi.h" static irqreturn_t wl1251_irq(int irq, void *cookie) { struct wl1251 *wl; wl1251_debug(DEBUG_IRQ, "IRQ"); wl = cookie; ieee80211_queue_work(wl->hw, &wl->irq_work); return IRQ_HANDLED; } static struct spi_device *wl_to_spi(struct wl1251 *wl) { return wl->if_priv; } static void wl1251_spi_reset(struct wl1251 *wl) { u8 *cmd; struct spi_transfer t; struct spi_message m; cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { wl1251_error("could not allocate cmd for spi reset"); return; } memset(&t, 0, sizeof(t)); spi_message_init(&m); memset(cmd, 0xff, WSPI_INIT_CMD_LEN); t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; spi_message_add_tail(&t, &m); spi_sync(wl_to_spi(wl), &m); wl1251_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); kfree(cmd); } static void wl1251_spi_wake(struct wl1251 *wl) { u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; struct spi_transfer t; struct spi_message m; cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { wl1251_error("could not allocate cmd for spi init"); return; } memset(crc, 0, sizeof(crc)); memset(&t, 0, sizeof(t)); spi_message_init(&m); /* * Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB */ cmd[2] = 0xff; cmd[3] = 0xff; cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; cmd[0] = 0; cmd[7] = 0; cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; else cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; crc[0] = cmd[1]; crc[1] = cmd[0]; crc[2] = cmd[7]; crc[3] = cmd[6]; crc[4] = cmd[5]; cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; cmd[4] |= WSPI_INIT_CMD_END; t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; spi_message_add_tail(&t, &m); spi_sync(wl_to_spi(wl), &m); wl1251_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); kfree(cmd); } static void wl1251_spi_reset_wake(struct wl1251 *wl) { wl1251_spi_reset(wl); wl1251_spi_wake(wl); } static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, size_t len) { struct spi_transfer t[3]; struct spi_message m; u8 *busy_buf; u32 *cmd; cmd = &wl->buffer_cmd; busy_buf = wl->buffer_busyword; *cmd = 0; *cmd |= WSPI_CMD_READ; *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; *cmd |= addr & WSPI_CMD_BYTE_ADDR; spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].tx_buf = cmd; t[0].len = 4; spi_message_add_tail(&t[0], &m); /* Busy and non busy words read */ t[1].rx_buf = busy_buf; t[1].len = WL1251_BUSY_WORD_LEN; spi_message_add_tail(&t[1], &m); t[2].rx_buf = buf; t[2].len = len; spi_message_add_tail(&t[2], &m); spi_sync(wl_to_spi(wl), &m); /* FIXME: check busy words */ wl1251_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); wl1251_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); } static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf, size_t len) { struct spi_transfer t[2]; struct spi_message m; u32 *cmd; cmd = &wl->buffer_cmd; *cmd = 0; *cmd |= WSPI_CMD_WRITE; *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; *cmd |= addr & WSPI_CMD_BYTE_ADDR; spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].tx_buf = cmd; t[0].len = sizeof(*cmd); spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; t[1].len = len; spi_message_add_tail(&t[1], &m); spi_sync(wl_to_spi(wl), &m); wl1251_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); wl1251_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); } static void wl1251_spi_enable_irq(struct wl1251 *wl) { return enable_irq(wl->irq); } static void wl1251_spi_disable_irq(struct wl1251 *wl) { return disable_irq(wl->irq); } static int wl1251_spi_set_power(struct wl1251 *wl, bool enable) { if (wl->set_power) wl->set_power(enable); return 0; } static const struct wl1251_if_operations wl1251_spi_ops = { .read = wl1251_spi_read, .write = wl1251_spi_write, .reset = wl1251_spi_reset_wake, .enable_irq = wl1251_spi_enable_irq, .disable_irq = wl1251_spi_disable_irq, .power = wl1251_spi_set_power, }; static int __devinit wl1251_spi_probe(struct spi_device *spi) { struct wl12xx_platform_data *pdata; struct ieee80211_hw *hw; struct wl1251 *wl; int ret; pdata = spi->dev.platform_data; if (!pdata) { wl1251_error("no platform data"); return -ENODEV; } hw = wl1251_alloc_hw(); if (IS_ERR(hw)) return PTR_ERR(hw); wl = hw->priv; SET_IEEE80211_DEV(hw, &spi->dev); dev_set_drvdata(&spi->dev, wl); wl->if_priv = spi; wl->if_ops = &wl1251_spi_ops; /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ spi->bits_per_word = 32; ret = spi_setup(spi); if (ret < 0) { wl1251_error("spi_setup failed"); goto out_free; } wl->set_power = pdata->set_power; if (!wl->set_power) { wl1251_error("set power function missing in platform data"); return -ENODEV; } wl->irq = spi->irq; if (wl->irq < 0) { wl1251_error("irq missing in platform data"); return -ENODEV; } wl->use_eeprom = pdata->use_eeprom; irq_set_status_flags(wl->irq, IRQ_NOAUTOEN); ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); if (ret < 0) { wl1251_error("request_irq() failed: %d", ret); goto out_free; } irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); ret = wl1251_init_ieee80211(wl); if (ret) goto out_irq; return 0; out_irq: free_irq(wl->irq, wl); out_free: ieee80211_free_hw(hw); return ret; } static int __devexit wl1251_spi_remove(struct spi_device *spi) { struct wl1251 *wl = dev_get_drvdata(&spi->dev); free_irq(wl->irq, wl); wl1251_free_hw(wl); return 0; } static struct spi_driver wl1251_spi_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, .probe = wl1251_spi_probe, .remove = __devexit_p(wl1251_spi_remove), }; static int __init wl1251_spi_init(void) { int ret; ret = spi_register_driver(&wl1251_spi_driver); if (ret < 0) { wl1251_error("failed to register spi driver: %d", ret); goto out; } out: return ret; } static void __exit wl1251_spi_exit(void) { spi_unregister_driver(&wl1251_spi_driver); wl1251_notice("unloaded"); } module_init(wl1251_spi_init); module_exit(wl1251_spi_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo "); MODULE_ALIAS("spi:wl1251"); compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/main.c0000644000175000017500000010032512026211315023121 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2008-2009 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,28)) #include #endif #include #include #include #include #include "wl1251.h" #include "wl12xx_80211.h" #include "reg.h" #include "io.h" #include "cmd.h" #include "event.h" #include "tx.h" #include "rx.h" #include "ps.h" #include "init.h" #include "debugfs.h" #include "boot.h" void wl1251_enable_interrupts(struct wl1251 *wl) { wl->if_ops->enable_irq(wl); } void wl1251_disable_interrupts(struct wl1251 *wl) { wl->if_ops->disable_irq(wl); } static int wl1251_power_off(struct wl1251 *wl) { return wl->if_ops->power(wl, false); } static int wl1251_power_on(struct wl1251 *wl) { return wl->if_ops->power(wl, true); } static int wl1251_fetch_firmware(struct wl1251 *wl) { const struct firmware *fw; struct device *dev = wiphy_dev(wl->hw->wiphy); int ret; ret = request_firmware(&fw, WL1251_FW_NAME, dev); if (ret < 0) { wl1251_error("could not get firmware: %d", ret); return ret; } if (fw->size % 4) { wl1251_error("firmware size is not multiple of 32 bits: %zu", fw->size); ret = -EILSEQ; goto out; } wl->fw_len = fw->size; wl->fw = vmalloc(wl->fw_len); if (!wl->fw) { wl1251_error("could not allocate memory for the firmware"); ret = -ENOMEM; goto out; } memcpy(wl->fw, fw->data, wl->fw_len); ret = 0; out: release_firmware(fw); return ret; } static int wl1251_fetch_nvs(struct wl1251 *wl) { const struct firmware *fw; struct device *dev = wiphy_dev(wl->hw->wiphy); int ret; ret = request_firmware(&fw, WL1251_NVS_NAME, dev); if (ret < 0) { wl1251_error("could not get nvs file: %d", ret); return ret; } if (fw->size % 4) { wl1251_error("nvs size is not multiple of 32 bits: %zu", fw->size); ret = -EILSEQ; goto out; } wl->nvs_len = fw->size; wl->nvs = kmemdup(fw->data, wl->nvs_len, GFP_KERNEL); if (!wl->nvs) { wl1251_error("could not allocate memory for the nvs file"); ret = -ENOMEM; goto out; } ret = 0; out: release_firmware(fw); return ret; } static void wl1251_fw_wakeup(struct wl1251 *wl) { u32 elp_reg; elp_reg = ELPCTRL_WAKE_UP; wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); if (!(elp_reg & ELPCTRL_WLAN_READY)) wl1251_warning("WLAN not ready"); } static int wl1251_chip_wakeup(struct wl1251 *wl) { int ret; ret = wl1251_power_on(wl); if (ret < 0) return ret; msleep(WL1251_POWER_ON_SLEEP); wl->if_ops->reset(wl); /* We don't need a real memory partition here, because we only want * to use the registers at this point. */ wl1251_set_partition(wl, 0x00000000, 0x00000000, REGISTERS_BASE, REGISTERS_DOWN_SIZE); /* ELP module wake up */ wl1251_fw_wakeup(wl); /* whal_FwCtrl_BootSm() */ /* 0. read chip id from CHIP_ID */ wl->chip_id = wl1251_reg_read32(wl, CHIP_ID_B); /* 1. check if chip id is valid */ switch (wl->chip_id) { case CHIP_ID_1251_PG12: wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", wl->chip_id); break; case CHIP_ID_1251_PG11: wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG11)", wl->chip_id); break; case CHIP_ID_1251_PG10: default: wl1251_error("unsupported chip id: 0x%x", wl->chip_id); ret = -ENODEV; goto out; } if (wl->fw == NULL) { ret = wl1251_fetch_firmware(wl); if (ret < 0) goto out; } if (wl->nvs == NULL && !wl->use_eeprom) { /* No NVS from netlink, try to get it from the filesystem */ ret = wl1251_fetch_nvs(wl); if (ret < 0) goto out; } out: return ret; } #define WL1251_IRQ_LOOP_COUNT 10 static void wl1251_irq_work(struct work_struct *work) { u32 intr, ctr = WL1251_IRQ_LOOP_COUNT; struct wl1251 *wl = container_of(work, struct wl1251, irq_work); int ret; mutex_lock(&wl->mutex); wl1251_debug(DEBUG_IRQ, "IRQ work"); if (wl->state == WL1251_STATE_OFF) goto out; ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1251_ACX_INTR_ALL); intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr); do { if (wl->data_path) { wl->rx_counter = wl1251_mem_read32( wl, wl->data_path->rx_control_addr); /* We handle a frmware bug here */ switch ((wl->rx_counter - wl->rx_handled) & 0xf) { case 0: wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync"); intr &= ~WL1251_ACX_INTR_RX0_DATA; intr &= ~WL1251_ACX_INTR_RX1_DATA; break; case 1: wl1251_debug(DEBUG_IRQ, "RX: FW +1"); intr |= WL1251_ACX_INTR_RX0_DATA; intr &= ~WL1251_ACX_INTR_RX1_DATA; break; case 2: wl1251_debug(DEBUG_IRQ, "RX: FW +2"); intr |= WL1251_ACX_INTR_RX0_DATA; intr |= WL1251_ACX_INTR_RX1_DATA; break; default: wl1251_warning( "RX: FW and host out of sync: %d", wl->rx_counter - wl->rx_handled); break; } wl->rx_handled = wl->rx_counter; wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); } intr &= wl->intr_mask; if (intr == 0) { wl1251_debug(DEBUG_IRQ, "INTR is 0"); goto out_sleep; } if (intr & WL1251_ACX_INTR_RX0_DATA) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); wl1251_rx(wl); } if (intr & WL1251_ACX_INTR_RX1_DATA) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); wl1251_rx(wl); } if (intr & WL1251_ACX_INTR_TX_RESULT) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); wl1251_tx_complete(wl); } if (intr & WL1251_ACX_INTR_EVENT_A) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_A"); wl1251_event_handle(wl, 0); } if (intr & WL1251_ACX_INTR_EVENT_B) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_B"); wl1251_event_handle(wl, 1); } if (intr & WL1251_ACX_INTR_INIT_COMPLETE) wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); if (--ctr == 0) break; intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); } while (intr); out_sleep: wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel, u16 beacon_interval, u8 dtim_period) { int ret; ret = wl1251_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE, DEFAULT_HW_GEN_MODULATION_TYPE, wl->tx_mgmt_frm_rate, wl->tx_mgmt_frm_mod); if (ret < 0) goto out; /* * Join command applies filters, and if we are not associated, * BSSID filter must be disabled for association to work. */ if (is_zero_ether_addr(wl->bssid)) wl->rx_config &= ~CFG_BSSID_FILTER_EN; ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval, dtim_period); if (ret < 0) goto out; ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100); if (ret < 0) wl1251_warning("join timeout"); out: return ret; } static void wl1251_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct wl1251 *wl = hw->priv; unsigned long flags; skb_queue_tail(&wl->tx_queue, skb); /* * The chip specific setup must run before the first TX packet - * before that, the tx_work will not be initialized! */ ieee80211_queue_work(wl->hw, &wl->tx_work); /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_HIGH_WATERMARK) { wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues"); spin_lock_irqsave(&wl->wl_lock, flags); ieee80211_stop_queues(wl->hw); wl->tx_queue_stopped = true; spin_unlock_irqrestore(&wl->wl_lock, flags); } } static int wl1251_op_start(struct ieee80211_hw *hw) { struct wl1251 *wl = hw->priv; struct wiphy *wiphy = hw->wiphy; int ret = 0; wl1251_debug(DEBUG_MAC80211, "mac80211 start"); mutex_lock(&wl->mutex); if (wl->state != WL1251_STATE_OFF) { wl1251_error("cannot start because not in off state: %d", wl->state); ret = -EBUSY; goto out; } ret = wl1251_chip_wakeup(wl); if (ret < 0) goto out; ret = wl1251_boot(wl); if (ret < 0) goto out; ret = wl1251_hw_init(wl); if (ret < 0) goto out; ret = wl1251_acx_station_id(wl); if (ret < 0) goto out; wl->state = WL1251_STATE_ON; wl1251_info("firmware booted (%s)", wl->fw_ver); /* update hw/fw version info in wiphy struct */ wiphy->hw_version = wl->chip_id; strncpy(wiphy->fw_version, wl->fw_ver, sizeof(wiphy->fw_version)); out: if (ret < 0) wl1251_power_off(wl); mutex_unlock(&wl->mutex); return ret; } static void wl1251_op_stop(struct ieee80211_hw *hw) { struct wl1251 *wl = hw->priv; wl1251_info("down"); wl1251_debug(DEBUG_MAC80211, "mac80211 stop"); mutex_lock(&wl->mutex); WARN_ON(wl->state != WL1251_STATE_ON); if (wl->scanning) { ieee80211_scan_completed(wl->hw, true); wl->scanning = false; } wl->state = WL1251_STATE_OFF; wl1251_disable_interrupts(wl); mutex_unlock(&wl->mutex); cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->elp_work); mutex_lock(&wl->mutex); /* let's notify MAC80211 about the remaining pending TX frames */ wl1251_tx_flush(wl); wl1251_power_off(wl); memset(wl->bssid, 0, ETH_ALEN); wl->listen_int = 1; wl->bss_type = MAX_BSS_TYPE; wl->data_in_count = 0; wl->rx_counter = 0; wl->rx_handled = 0; wl->rx_current_buffer = 0; wl->rx_last_id = 0; wl->next_tx_complete = 0; wl->elp = false; wl->station_mode = STATION_ACTIVE_MODE; wl->tx_queue_stopped = false; wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->rssi_thold = 0; wl->channel = WL1251_DEFAULT_CHANNEL; wl1251_debugfs_reset(wl); mutex_unlock(&wl->mutex); } static int wl1251_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1251 *wl = hw->priv; int ret = 0; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", vif->type, vif->addr); mutex_lock(&wl->mutex); if (wl->vif) { ret = -EBUSY; goto out; } wl->vif = vif; switch (vif->type) { case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; break; case NL80211_IFTYPE_ADHOC: wl->bss_type = BSS_TYPE_IBSS; break; default: ret = -EOPNOTSUPP; goto out; } if (memcmp(wl->mac_addr, vif->addr, ETH_ALEN)) { memcpy(wl->mac_addr, vif->addr, ETH_ALEN); SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); ret = wl1251_acx_station_id(wl); if (ret < 0) goto out; } out: mutex_unlock(&wl->mutex); return ret; } static void wl1251_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1251 *wl = hw->priv; mutex_lock(&wl->mutex); wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); wl->vif = NULL; mutex_unlock(&wl->mutex); } static int wl1251_build_qos_null_data(struct wl1251 *wl) { struct ieee80211_qos_hdr template; memset(&template, 0, sizeof(template)); memcpy(template.addr1, wl->bssid, ETH_ALEN); memcpy(template.addr2, wl->mac_addr, ETH_ALEN); memcpy(template.addr3, wl->bssid, ETH_ALEN); template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC | IEEE80211_FCTL_TODS); /* FIXME: not sure what priority to use here */ template.qos_ctrl = cpu_to_le16(0); return wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, &template, sizeof(template)); } static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1251 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); wl1251_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); mutex_lock(&wl->mutex); ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; if (channel != wl->channel) { wl->channel = channel; ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, wl->dtim_period); if (ret < 0) goto out_sleep; } if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm enabled"); wl->psm_requested = true; wl->dtim_period = conf->ps_dtim_period; ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, wl->dtim_period); /* * mac80211 enables PSM only if we're already associated. */ ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); if (ret < 0) goto out_sleep; } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm disabled"); wl->psm_requested = false; if (wl->station_mode != STATION_ACTIVE_MODE) { ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) goto out_sleep; } } if (changed & IEEE80211_CONF_CHANGE_IDLE) { if (conf->flags & IEEE80211_CONF_IDLE) { ret = wl1251_ps_set_mode(wl, STATION_IDLE); if (ret < 0) goto out_sleep; } else { ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) goto out_sleep; ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, wl->dtim_period); if (ret < 0) goto out_sleep; } } if (conf->power_level != wl->power_level) { ret = wl1251_acx_tx_power(wl, conf->power_level); if (ret < 0) goto out_sleep; wl->power_level = conf->power_level; } out_sleep: wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } #define WL1251_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_CONTROL | \ FIF_OTHER_BSS | \ FIF_PROBE_REQ) static void wl1251_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *total,u64 multicast) { struct wl1251 *wl = hw->priv; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 configure filter"); *total &= WL1251_SUPPORTED_FILTERS; changed &= WL1251_SUPPORTED_FILTERS; if (changed == 0) /* no filters which we support changed */ return; mutex_lock(&wl->mutex); wl->rx_config = WL1251_DEFAULT_RX_CONFIG; wl->rx_filter = WL1251_DEFAULT_RX_FILTER; if (*total & FIF_PROMISC_IN_BSS) { wl->rx_config |= CFG_BSSID_FILTER_EN; wl->rx_config |= CFG_RX_ALL_GOOD; } if (*total & FIF_ALLMULTI) /* * CFG_MC_FILTER_EN in rx_config needs to be 0 to receive * all multicast frames */ wl->rx_config &= ~CFG_MC_FILTER_EN; if (*total & FIF_FCSFAIL) wl->rx_filter |= CFG_RX_FCS_ERROR; if (*total & FIF_BCN_PRBRESP_PROMISC) { wl->rx_config &= ~CFG_BSSID_FILTER_EN; wl->rx_config &= ~CFG_SSID_FILTER_EN; } if (*total & FIF_CONTROL) wl->rx_filter |= CFG_RX_CTL_EN; if (*total & FIF_OTHER_BSS || is_zero_ether_addr(wl->bssid)) wl->rx_config &= ~CFG_BSSID_FILTER_EN; if (*total & FIF_PROBE_REQ) wl->rx_filter |= CFG_RX_PREQ_EN; if (wl->state == WL1251_STATE_OFF) goto out; ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; /* send filters to firmware */ wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter); wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } /* HW encryption */ static int wl1251_set_key_type(struct wl1251 *wl, struct wl1251_cmd_set_keys *key, enum set_key_cmd cmd, struct ieee80211_key_conf *mac80211_key, const u8 *addr) { switch (mac80211_key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: if (is_broadcast_ether_addr(addr)) key->key_type = KEY_WEP_DEFAULT; else key->key_type = KEY_WEP_ADDR; mac80211_key->hw_key_idx = mac80211_key->keyidx; break; case WLAN_CIPHER_SUITE_TKIP: if (is_broadcast_ether_addr(addr)) key->key_type = KEY_TKIP_MIC_GROUP; else key->key_type = KEY_TKIP_MIC_PAIRWISE; mac80211_key->hw_key_idx = mac80211_key->keyidx; break; case WLAN_CIPHER_SUITE_CCMP: if (is_broadcast_ether_addr(addr)) key->key_type = KEY_AES_GROUP; else key->key_type = KEY_AES_PAIRWISE; mac80211_key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; break; default: wl1251_error("Unknown key cipher 0x%x", mac80211_key->cipher); return -EOPNOTSUPP; } return 0; } static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct wl1251 *wl = hw->priv; struct wl1251_cmd_set_keys *wl_cmd; const u8 *addr; int ret; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; wl1251_debug(DEBUG_MAC80211, "mac80211 set key"); wl_cmd = kzalloc(sizeof(*wl_cmd), GFP_KERNEL); if (!wl_cmd) { ret = -ENOMEM; goto out; } addr = sta ? sta->addr : bcast_addr; wl1251_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); wl1251_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); wl1251_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", key->cipher, key->keyidx, key->keylen, key->flags); wl1251_dump(DEBUG_CRYPT, "KEY: ", key->key, key->keylen); if (is_zero_ether_addr(addr)) { /* We dont support TX only encryption */ ret = -EOPNOTSUPP; goto out; } mutex_lock(&wl->mutex); ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out_unlock; switch (cmd) { case SET_KEY: wl_cmd->key_action = KEY_ADD_OR_REPLACE; break; case DISABLE_KEY: wl_cmd->key_action = KEY_REMOVE; break; default: wl1251_error("Unsupported key cmd 0x%x", cmd); break; } ret = wl1251_set_key_type(wl, wl_cmd, cmd, key, addr); if (ret < 0) { wl1251_error("Set KEY type failed"); goto out_sleep; } if (wl_cmd->key_type != KEY_WEP_DEFAULT) memcpy(wl_cmd->addr, addr, ETH_ALEN); if ((wl_cmd->key_type == KEY_TKIP_MIC_GROUP) || (wl_cmd->key_type == KEY_TKIP_MIC_PAIRWISE)) { /* * We get the key in the following form: * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) * but the target is expecting: * TKIP - RX MIC - TX MIC */ memcpy(wl_cmd->key, key->key, 16); memcpy(wl_cmd->key + 16, key->key + 24, 8); memcpy(wl_cmd->key + 24, key->key + 16, 8); } else { memcpy(wl_cmd->key, key->key, key->keylen); } wl_cmd->key_size = key->keylen; wl_cmd->id = key->keyidx; wl_cmd->ssid_profile = 0; wl1251_dump(DEBUG_CRYPT, "TARGET KEY: ", wl_cmd, sizeof(*wl_cmd)); ret = wl1251_cmd_send(wl, CMD_SET_KEYS, wl_cmd, sizeof(*wl_cmd)); if (ret < 0) { wl1251_warning("could not set keys"); goto out_sleep; } out_sleep: wl1251_ps_elp_sleep(wl); out_unlock: mutex_unlock(&wl->mutex); out: kfree(wl_cmd); return ret; } static int wl1251_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct wl1251 *wl = hw->priv; struct sk_buff *skb; size_t ssid_len = 0; u8 *ssid = NULL; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); if (req->n_ssids) { ssid = req->ssids[0].ssid; ssid_len = req->ssids[0].ssid_len; } mutex_lock(&wl->mutex); if (wl->scanning) { wl1251_debug(DEBUG_SCAN, "scan already in progress"); ret = -EINVAL; goto out; } ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, req->ie, req->ie_len); if (!skb) { ret = -ENOMEM; goto out; } ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, skb->len); dev_kfree_skb(skb); if (ret < 0) goto out_sleep; ret = wl1251_cmd_trigger_scan_to(wl, 0); if (ret < 0) goto out_sleep; wl->scanning = true; ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels, req->n_channels, WL1251_SCAN_NUM_PROBES); if (ret < 0) { wl->scanning = false; goto out_sleep; } out_sleep: wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static int wl1251_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct wl1251 *wl = hw->priv; int ret; mutex_lock(&wl->mutex); ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1251_acx_rts_threshold(wl, (u16) value); if (ret < 0) wl1251_warning("wl1251_op_set_rts_threshold failed: %d", ret); wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct wl1251 *wl = hw->priv; struct sk_buff *beacon, *skb; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); mutex_lock(&wl->mutex); ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; if (changed & BSS_CHANGED_CQM) { ret = wl1251_acx_low_rssi(wl, bss_conf->cqm_rssi_thold, WL1251_DEFAULT_LOW_RSSI_WEIGHT, WL1251_DEFAULT_LOW_RSSI_DEPTH, WL1251_ACX_LOW_RSSI_TYPE_EDGE); if (ret < 0) goto out; wl->rssi_thold = bss_conf->cqm_rssi_thold; } if (changed & BSS_CHANGED_BSSID) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); skb = ieee80211_nullfunc_get(wl->hw, wl->vif); if (!skb) goto out_sleep; ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, skb->data, skb->len); dev_kfree_skb(skb); if (ret < 0) goto out_sleep; ret = wl1251_build_qos_null_data(wl); if (ret < 0) goto out; if (wl->bss_type != BSS_TYPE_IBSS) { ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, wl->dtim_period); if (ret < 0) goto out_sleep; } } if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->beacon_int = bss_conf->beacon_int; skb = ieee80211_pspoll_get(wl->hw, wl->vif); if (!skb) goto out_sleep; ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, skb->data, skb->len); dev_kfree_skb(skb); if (ret < 0) goto out_sleep; ret = wl1251_acx_aid(wl, bss_conf->aid); if (ret < 0) goto out_sleep; } else { /* use defaults when not associated */ wl->beacon_int = WL1251_DEFAULT_BEACON_INT; wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; } } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ret = wl1251_acx_slot(wl, SLOT_TIME_SHORT); else ret = wl1251_acx_slot(wl, SLOT_TIME_LONG); if (ret < 0) { wl1251_warning("Set slot time failed %d", ret); goto out_sleep; } } if (changed & BSS_CHANGED_ERP_PREAMBLE) { if (bss_conf->use_short_preamble) wl1251_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); else wl1251_acx_set_preamble(wl, ACX_PREAMBLE_LONG); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { if (bss_conf->use_cts_prot) ret = wl1251_acx_cts_protect(wl, CTSPROTECT_ENABLE); else ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE); if (ret < 0) { wl1251_warning("Set ctsprotect failed %d", ret); goto out_sleep; } } if (changed & BSS_CHANGED_BEACON) { beacon = ieee80211_beacon_get(hw, vif); if (!beacon) goto out_sleep; ret = wl1251_cmd_template_set(wl, CMD_BEACON, beacon->data, beacon->len); if (ret < 0) { dev_kfree_skb(beacon); goto out_sleep; } ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, beacon->len); dev_kfree_skb(beacon); if (ret < 0) goto out_sleep; ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, wl->channel, wl->dtim_period); if (ret < 0) goto out_sleep; } out_sleep: wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1251_rates[] = { { .bitrate = 10, .hw_value = 0x1, .hw_value_short = 0x1, }, { .bitrate = 20, .hw_value = 0x2, .hw_value_short = 0x2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = 0x4, .hw_value_short = 0x4, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .hw_value = 0x20, .hw_value_short = 0x20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = 0x8, .hw_value_short = 0x8, }, { .bitrate = 90, .hw_value = 0x10, .hw_value_short = 0x10, }, { .bitrate = 120, .hw_value = 0x40, .hw_value_short = 0x40, }, { .bitrate = 180, .hw_value = 0x80, .hw_value_short = 0x80, }, { .bitrate = 240, .hw_value = 0x200, .hw_value_short = 0x200, }, { .bitrate = 360, .hw_value = 0x400, .hw_value_short = 0x400, }, { .bitrate = 480, .hw_value = 0x800, .hw_value_short = 0x800, }, { .bitrate = 540, .hw_value = 0x1000, .hw_value_short = 0x1000, }, }; /* can't be const, mac80211 writes to this */ static struct ieee80211_channel wl1251_channels[] = { { .hw_value = 1, .center_freq = 2412}, { .hw_value = 2, .center_freq = 2417}, { .hw_value = 3, .center_freq = 2422}, { .hw_value = 4, .center_freq = 2427}, { .hw_value = 5, .center_freq = 2432}, { .hw_value = 6, .center_freq = 2437}, { .hw_value = 7, .center_freq = 2442}, { .hw_value = 8, .center_freq = 2447}, { .hw_value = 9, .center_freq = 2452}, { .hw_value = 10, .center_freq = 2457}, { .hw_value = 11, .center_freq = 2462}, { .hw_value = 12, .center_freq = 2467}, { .hw_value = 13, .center_freq = 2472}, }; static int wl1251_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { enum wl1251_acx_ps_scheme ps_scheme; struct wl1251 *wl = hw->priv; int ret; mutex_lock(&wl->mutex); wl1251_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; /* mac80211 uses units of 32 usec */ ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue), params->cw_min, params->cw_max, params->aifs, params->txop * 32); if (ret < 0) goto out_sleep; if (params->uapsd) ps_scheme = WL1251_ACX_PS_SCHEME_UPSD_TRIGGER; else ps_scheme = WL1251_ACX_PS_SCHEME_LEGACY; ret = wl1251_acx_tid_cfg(wl, wl1251_tx_get_queue(queue), CHANNEL_TYPE_EDCF, wl1251_tx_get_queue(queue), ps_scheme, WL1251_ACX_ACK_POLICY_LEGACY); if (ret < 0) goto out_sleep; out_sleep: wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; } static int wl1251_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct wl1251 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; survey->filled = SURVEY_INFO_NOISE_DBM; survey->noise = wl->noise; return 0; } /* can't be const, mac80211 writes to this */ static struct ieee80211_supported_band wl1251_band_2ghz = { .channels = wl1251_channels, .n_channels = ARRAY_SIZE(wl1251_channels), .bitrates = wl1251_rates, .n_bitrates = ARRAY_SIZE(wl1251_rates), }; static const struct ieee80211_ops wl1251_ops = { .start = wl1251_op_start, .stop = wl1251_op_stop, .add_interface = wl1251_op_add_interface, .remove_interface = wl1251_op_remove_interface, .config = wl1251_op_config, .configure_filter = wl1251_op_configure_filter, .tx = wl1251_op_tx, .set_key = wl1251_op_set_key, .hw_scan = wl1251_op_hw_scan, .bss_info_changed = wl1251_op_bss_info_changed, .set_rts_threshold = wl1251_op_set_rts_threshold, .conf_tx = wl1251_op_conf_tx, .get_survey = wl1251_op_get_survey, }; static int wl1251_read_eeprom_byte(struct wl1251 *wl, off_t offset, u8 *data) { unsigned long timeout; wl1251_reg_write32(wl, EE_ADDR, offset); wl1251_reg_write32(wl, EE_CTL, EE_CTL_READ); /* EE_CTL_READ clears when data is ready */ timeout = jiffies + msecs_to_jiffies(100); while (1) { if (!(wl1251_reg_read32(wl, EE_CTL) & EE_CTL_READ)) break; if (time_after(jiffies, timeout)) return -ETIMEDOUT; msleep(1); } *data = wl1251_reg_read32(wl, EE_DATA); return 0; } static int wl1251_read_eeprom(struct wl1251 *wl, off_t offset, u8 *data, size_t len) { size_t i; int ret; wl1251_reg_write32(wl, EE_START, 0); for (i = 0; i < len; i++) { ret = wl1251_read_eeprom_byte(wl, offset + i, &data[i]); if (ret < 0) return ret; } return 0; } static int wl1251_read_eeprom_mac(struct wl1251 *wl) { u8 mac[ETH_ALEN]; int i, ret; wl1251_set_partition(wl, 0, 0, REGISTERS_BASE, REGISTERS_DOWN_SIZE); ret = wl1251_read_eeprom(wl, 0x1c, mac, sizeof(mac)); if (ret < 0) { wl1251_warning("failed to read MAC address from EEPROM"); return ret; } /* MAC is stored in reverse order */ for (i = 0; i < ETH_ALEN; i++) wl->mac_addr[i] = mac[ETH_ALEN - i - 1]; return 0; } static int wl1251_register_hw(struct wl1251 *wl) { int ret; if (wl->mac80211_registered) return 0; SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); ret = ieee80211_register_hw(wl->hw); if (ret < 0) { wl1251_error("unable to register mac80211 hw: %d", ret); return ret; } wl->mac80211_registered = true; wl1251_notice("loaded"); return 0; } int wl1251_init_ieee80211(struct wl1251 *wl) { int ret; /* The tx descriptor buffer and the TKIP space */ wl->hw->extra_tx_headroom = sizeof(struct tx_double_buffer_desc) + WL1251_TKIP_IV_SPACE; /* unit us */ /* FIXME: find a proper value */ wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_UAPSD; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz; wl->hw->queues = 4; if (wl->use_eeprom) wl1251_read_eeprom_mac(wl); ret = wl1251_register_hw(wl); if (ret) goto out; wl1251_debugfs_init(wl); wl1251_notice("initialized"); ret = 0; out: return ret; } EXPORT_SYMBOL_GPL(wl1251_init_ieee80211); struct ieee80211_hw *wl1251_alloc_hw(void) { struct ieee80211_hw *hw; struct wl1251 *wl; int i; static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1251_ops); if (!hw) { wl1251_error("could not alloc ieee80211_hw"); return ERR_PTR(-ENOMEM); } wl = hw->priv; memset(wl, 0, sizeof(*wl)); wl->hw = hw; wl->data_in_count = 0; skb_queue_head_init(&wl->tx_queue); INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); wl->channel = WL1251_DEFAULT_CHANNEL; wl->scanning = false; wl->default_key = 0; wl->listen_int = 1; wl->rx_counter = 0; wl->rx_handled = 0; wl->rx_current_buffer = 0; wl->rx_last_id = 0; wl->rx_config = WL1251_DEFAULT_RX_CONFIG; wl->rx_filter = WL1251_DEFAULT_RX_FILTER; wl->elp = false; wl->station_mode = STATION_ACTIVE_MODE; wl->psm_requested = false; wl->tx_queue_stopped = false; wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->rssi_thold = 0; wl->beacon_int = WL1251_DEFAULT_BEACON_INT; wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; wl->vif = NULL; for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) wl->tx_frames[i] = NULL; wl->next_tx_complete = 0; INIT_WORK(&wl->irq_work, wl1251_irq_work); INIT_WORK(&wl->tx_work, wl1251_tx_work); /* * In case our MAC address is not correctly set, * we use a random but Nokia MAC. */ memcpy(wl->mac_addr, nokia_oui, 3); get_random_bytes(wl->mac_addr + 3, 3); wl->state = WL1251_STATE_OFF; mutex_init(&wl->mutex); wl->tx_mgmt_frm_rate = DEFAULT_HW_GEN_TX_RATE; wl->tx_mgmt_frm_mod = DEFAULT_HW_GEN_MODULATION_TYPE; wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL); if (!wl->rx_descriptor) { wl1251_error("could not allocate memory for rx descriptor"); ieee80211_free_hw(hw); return ERR_PTR(-ENOMEM); } return hw; } EXPORT_SYMBOL_GPL(wl1251_alloc_hw); int wl1251_free_hw(struct wl1251 *wl) { ieee80211_unregister_hw(wl->hw); wl1251_debugfs_exit(wl); kfree(wl->target_mem_map); kfree(wl->data_path); vfree(wl->fw); wl->fw = NULL; kfree(wl->nvs); wl->nvs = NULL; kfree(wl->rx_descriptor); wl->rx_descriptor = NULL; ieee80211_free_hw(wl->hw); return 0; } EXPORT_SYMBOL_GPL(wl1251_free_hw); MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo "); MODULE_FIRMWARE(WL1251_FW_NAME); compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/wl12xx_80211.h0000644000175000017500000001003112026211315024074 0ustar mcgrofmcgrof#ifndef __WL12XX_80211_H__ #define __WL12XX_80211_H__ #include /* ETH_ALEN */ /* RATES */ #define IEEE80211_CCK_RATE_1MB 0x02 #define IEEE80211_CCK_RATE_2MB 0x04 #define IEEE80211_CCK_RATE_5MB 0x0B #define IEEE80211_CCK_RATE_11MB 0x16 #define IEEE80211_OFDM_RATE_6MB 0x0C #define IEEE80211_OFDM_RATE_9MB 0x12 #define IEEE80211_OFDM_RATE_12MB 0x18 #define IEEE80211_OFDM_RATE_18MB 0x24 #define IEEE80211_OFDM_RATE_24MB 0x30 #define IEEE80211_OFDM_RATE_36MB 0x48 #define IEEE80211_OFDM_RATE_48MB 0x60 #define IEEE80211_OFDM_RATE_54MB 0x6C #define IEEE80211_BASIC_RATE_MASK 0x80 #define IEEE80211_CCK_RATE_1MB_MASK (1<<0) #define IEEE80211_CCK_RATE_2MB_MASK (1<<1) #define IEEE80211_CCK_RATE_5MB_MASK (1<<2) #define IEEE80211_CCK_RATE_11MB_MASK (1<<3) #define IEEE80211_OFDM_RATE_6MB_MASK (1<<4) #define IEEE80211_OFDM_RATE_9MB_MASK (1<<5) #define IEEE80211_OFDM_RATE_12MB_MASK (1<<6) #define IEEE80211_OFDM_RATE_18MB_MASK (1<<7) #define IEEE80211_OFDM_RATE_24MB_MASK (1<<8) #define IEEE80211_OFDM_RATE_36MB_MASK (1<<9) #define IEEE80211_OFDM_RATE_48MB_MASK (1<<10) #define IEEE80211_OFDM_RATE_54MB_MASK (1<<11) #define IEEE80211_CCK_RATES_MASK 0x0000000F #define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ IEEE80211_CCK_RATE_2MB_MASK) #define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ IEEE80211_CCK_RATE_5MB_MASK | \ IEEE80211_CCK_RATE_11MB_MASK) #define IEEE80211_OFDM_RATES_MASK 0x00000FF0 #define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ IEEE80211_OFDM_RATE_12MB_MASK | \ IEEE80211_OFDM_RATE_24MB_MASK) #define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ IEEE80211_OFDM_RATE_9MB_MASK | \ IEEE80211_OFDM_RATE_18MB_MASK | \ IEEE80211_OFDM_RATE_36MB_MASK | \ IEEE80211_OFDM_RATE_48MB_MASK | \ IEEE80211_OFDM_RATE_54MB_MASK) #define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ IEEE80211_CCK_DEFAULT_RATES_MASK) /* This really should be 8, but not for our firmware */ #define MAX_SUPPORTED_RATES 32 #define MAX_COUNTRY_TRIPLETS 32 /* Headers */ struct ieee80211_header { __le16 frame_ctl; __le16 duration_id; u8 da[ETH_ALEN]; u8 sa[ETH_ALEN]; u8 bssid[ETH_ALEN]; __le16 seq_ctl; u8 payload[0]; } __packed; struct wl12xx_ie_header { u8 id; u8 len; } __packed; /* IEs */ struct wl12xx_ie_ssid { struct wl12xx_ie_header header; char ssid[IEEE80211_MAX_SSID_LEN]; } __packed; struct wl12xx_ie_rates { struct wl12xx_ie_header header; u8 rates[MAX_SUPPORTED_RATES]; } __packed; struct wl12xx_ie_ds_params { struct wl12xx_ie_header header; u8 channel; } __packed; struct country_triplet { u8 channel; u8 num_channels; u8 max_tx_power; } __packed; struct wl12xx_ie_country { struct wl12xx_ie_header header; u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; struct country_triplet triplets[MAX_COUNTRY_TRIPLETS]; } __packed; /* Templates */ struct wl12xx_beacon_template { struct ieee80211_header header; __le32 time_stamp[2]; __le16 beacon_interval; __le16 capability; struct wl12xx_ie_ssid ssid; struct wl12xx_ie_rates rates; struct wl12xx_ie_rates ext_rates; struct wl12xx_ie_ds_params ds_params; struct wl12xx_ie_country country; } __packed; struct wl12xx_null_data_template { struct ieee80211_header header; } __packed; struct wl12xx_ps_poll_template { __le16 fc; __le16 aid; u8 bssid[ETH_ALEN]; u8 ta[ETH_ALEN]; } __packed; struct wl12xx_qos_null_data_template { struct ieee80211_header header; __le16 qos_ctl; } __packed; struct wl12xx_probe_req_template { struct ieee80211_header header; struct wl12xx_ie_ssid ssid; struct wl12xx_ie_rates rates; struct wl12xx_ie_rates ext_rates; } __packed; struct wl12xx_probe_resp_template { struct ieee80211_header header; __le32 time_stamp[2]; __le16 beacon_interval; __le16 capability; struct wl12xx_ie_ssid ssid; struct wl12xx_ie_rates rates; struct wl12xx_ie_rates ext_rates; struct wl12xx_ie_ds_params ds_params; struct wl12xx_ie_country country; } __packed; #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/wl1251.h0000644000175000017500000002512312026211315023137 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008-2009 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_H__ #define __WL1251_H__ #include #include #include #include #define DRIVER_NAME "wl1251" #define DRIVER_PREFIX DRIVER_NAME ": " enum { DEBUG_NONE = 0, DEBUG_IRQ = BIT(0), DEBUG_SPI = BIT(1), DEBUG_BOOT = BIT(2), DEBUG_MAILBOX = BIT(3), DEBUG_NETLINK = BIT(4), DEBUG_EVENT = BIT(5), DEBUG_TX = BIT(6), DEBUG_RX = BIT(7), DEBUG_SCAN = BIT(8), DEBUG_CRYPT = BIT(9), DEBUG_PSM = BIT(10), DEBUG_MAC80211 = BIT(11), DEBUG_CMD = BIT(12), DEBUG_ACX = BIT(13), DEBUG_ALL = ~0, }; #define DEBUG_LEVEL (DEBUG_NONE) #define DEBUG_DUMP_LIMIT 1024 #define wl1251_error(fmt, arg...) \ printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg) #define wl1251_warning(fmt, arg...) \ printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg) #define wl1251_notice(fmt, arg...) \ printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg) #define wl1251_info(fmt, arg...) \ printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg) #define wl1251_debug(level, fmt, arg...) \ do { \ if (level & DEBUG_LEVEL) \ printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \ } while (0) #define wl1251_dump(level, prefix, buf, len) \ do { \ if (level & DEBUG_LEVEL) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ DUMP_PREFIX_OFFSET, 16, 1, \ buf, \ min_t(size_t, len, DEBUG_DUMP_LIMIT), \ 0); \ } while (0) #define wl1251_dump_ascii(level, prefix, buf, len) \ do { \ if (level & DEBUG_LEVEL) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ DUMP_PREFIX_OFFSET, 16, 1, \ buf, \ min_t(size_t, len, DEBUG_DUMP_LIMIT), \ true); \ } while (0) #define WL1251_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ CFG_BSSID_FILTER_EN) #define WL1251_DEFAULT_RX_FILTER (CFG_RX_PRSP_EN | \ CFG_RX_MGMT_EN | \ CFG_RX_DATA_EN | \ CFG_RX_CTL_EN | \ CFG_RX_BCN_EN | \ CFG_RX_AUTH_EN | \ CFG_RX_ASSOC_EN) #define WL1251_BUSY_WORD_LEN 8 struct boot_attr { u32 radio_type; u8 mac_clock; u8 arm_clock; int firmware_debug; u32 minor; u32 major; u32 bugfix; }; enum wl1251_state { WL1251_STATE_OFF, WL1251_STATE_ON, WL1251_STATE_PLT, }; enum wl1251_partition_type { PART_DOWN, PART_WORK, PART_DRPW, PART_TABLE_LEN }; enum wl1251_station_mode { STATION_ACTIVE_MODE, STATION_POWER_SAVE_MODE, STATION_IDLE, }; struct wl1251_partition { u32 size; u32 start; }; struct wl1251_partition_set { struct wl1251_partition mem; struct wl1251_partition reg; }; struct wl1251; struct wl1251_stats { struct acx_statistics *fw_stats; unsigned long fw_stats_update; unsigned int retry_count; unsigned int excessive_retries; }; struct wl1251_debugfs { struct dentry *rootdir; struct dentry *fw_statistics; struct dentry *tx_internal_desc_overflow; struct dentry *rx_out_of_mem; struct dentry *rx_hdr_overflow; struct dentry *rx_hw_stuck; struct dentry *rx_dropped; struct dentry *rx_fcs_err; struct dentry *rx_xfr_hint_trig; struct dentry *rx_path_reset; struct dentry *rx_reset_counter; struct dentry *dma_rx_requested; struct dentry *dma_rx_errors; struct dentry *dma_tx_requested; struct dentry *dma_tx_errors; struct dentry *isr_cmd_cmplt; struct dentry *isr_fiqs; struct dentry *isr_rx_headers; struct dentry *isr_rx_mem_overflow; struct dentry *isr_rx_rdys; struct dentry *isr_irqs; struct dentry *isr_tx_procs; struct dentry *isr_decrypt_done; struct dentry *isr_dma0_done; struct dentry *isr_dma1_done; struct dentry *isr_tx_exch_complete; struct dentry *isr_commands; struct dentry *isr_rx_procs; struct dentry *isr_hw_pm_mode_changes; struct dentry *isr_host_acknowledges; struct dentry *isr_pci_pm; struct dentry *isr_wakeups; struct dentry *isr_low_rssi; struct dentry *wep_addr_key_count; struct dentry *wep_default_key_count; /* skipping wep.reserved */ struct dentry *wep_key_not_found; struct dentry *wep_decrypt_fail; struct dentry *wep_packets; struct dentry *wep_interrupt; struct dentry *pwr_ps_enter; struct dentry *pwr_elp_enter; struct dentry *pwr_missing_bcns; struct dentry *pwr_wake_on_host; struct dentry *pwr_wake_on_timer_exp; struct dentry *pwr_tx_with_ps; struct dentry *pwr_tx_without_ps; struct dentry *pwr_rcvd_beacons; struct dentry *pwr_power_save_off; struct dentry *pwr_enable_ps; struct dentry *pwr_disable_ps; struct dentry *pwr_fix_tsf_ps; /* skipping cont_miss_bcns_spread for now */ struct dentry *pwr_rcvd_awake_beacons; struct dentry *mic_rx_pkts; struct dentry *mic_calc_failure; struct dentry *aes_encrypt_fail; struct dentry *aes_decrypt_fail; struct dentry *aes_encrypt_packets; struct dentry *aes_decrypt_packets; struct dentry *aes_encrypt_interrupt; struct dentry *aes_decrypt_interrupt; struct dentry *event_heart_beat; struct dentry *event_calibration; struct dentry *event_rx_mismatch; struct dentry *event_rx_mem_empty; struct dentry *event_rx_pool; struct dentry *event_oom_late; struct dentry *event_phy_transmit_error; struct dentry *event_tx_stuck; struct dentry *ps_pspoll_timeouts; struct dentry *ps_upsd_timeouts; struct dentry *ps_upsd_max_sptime; struct dentry *ps_upsd_max_apturn; struct dentry *ps_pspoll_max_apturn; struct dentry *ps_pspoll_utilization; struct dentry *ps_upsd_utilization; struct dentry *rxpipe_rx_prep_beacon_drop; struct dentry *rxpipe_descr_host_int_trig_rx_data; struct dentry *rxpipe_beacon_buffer_thres_host_int_trig_rx_data; struct dentry *rxpipe_missed_beacon_host_int_trig_rx_data; struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data; struct dentry *tx_queue_len; struct dentry *tx_queue_status; struct dentry *retry_count; struct dentry *excessive_retries; }; struct wl1251_if_operations { void (*read)(struct wl1251 *wl, int addr, void *buf, size_t len); void (*write)(struct wl1251 *wl, int addr, void *buf, size_t len); void (*read_elp)(struct wl1251 *wl, int addr, u32 *val); void (*write_elp)(struct wl1251 *wl, int addr, u32 val); int (*power)(struct wl1251 *wl, bool enable); void (*reset)(struct wl1251 *wl); void (*enable_irq)(struct wl1251 *wl); void (*disable_irq)(struct wl1251 *wl); }; struct wl1251 { struct ieee80211_hw *hw; bool mac80211_registered; void *if_priv; const struct wl1251_if_operations *if_ops; void (*set_power)(bool enable); int irq; bool use_eeprom; spinlock_t wl_lock; enum wl1251_state state; struct mutex mutex; int physical_mem_addr; int physical_reg_addr; int virtual_mem_addr; int virtual_reg_addr; int cmd_box_addr; int event_box_addr; struct boot_attr boot_attr; u8 *fw; size_t fw_len; u8 *nvs; size_t nvs_len; u8 bssid[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; u8 bss_type; u8 listen_int; int channel; void *target_mem_map; struct acx_data_path_params_resp *data_path; /* Number of TX packets transferred to the FW, modulo 16 */ u32 data_in_count; /* Frames scheduled for transmission, not handled yet */ struct sk_buff_head tx_queue; bool tx_queue_stopped; struct work_struct tx_work; /* Pending TX frames */ struct sk_buff *tx_frames[16]; /* * Index pointing to the next TX complete entry * in the cyclic XT complete array we get from * the FW. */ u32 next_tx_complete; /* FW Rx counter */ u32 rx_counter; /* Rx frames handled */ u32 rx_handled; /* Current double buffer */ u32 rx_current_buffer; u32 rx_last_id; /* The target interrupt mask */ u32 intr_mask; struct work_struct irq_work; /* The mbox event mask */ u32 event_mask; /* Mailbox pointers */ u32 mbox_ptr[2]; /* Are we currently scanning */ bool scanning; /* Default key (for WEP) */ u32 default_key; unsigned int tx_mgmt_frm_rate; unsigned int tx_mgmt_frm_mod; unsigned int rx_config; unsigned int rx_filter; /* is firmware in elp mode */ bool elp; struct delayed_work elp_work; enum wl1251_station_mode station_mode; /* PSM mode requested */ bool psm_requested; u16 beacon_int; u8 dtim_period; /* in dBm */ int power_level; int rssi_thold; struct wl1251_stats stats; struct wl1251_debugfs debugfs; __le32 buffer_32; u32 buffer_cmd; u8 buffer_busyword[WL1251_BUSY_WORD_LEN]; struct wl1251_rx_descriptor *rx_descriptor; struct ieee80211_vif *vif; u32 chip_id; char fw_ver[21]; /* Most recently reported noise in dBm */ s8 noise; }; int wl1251_plt_start(struct wl1251 *wl); int wl1251_plt_stop(struct wl1251 *wl); struct ieee80211_hw *wl1251_alloc_hw(void); int wl1251_free_hw(struct wl1251 *wl); int wl1251_init_ieee80211(struct wl1251 *wl); void wl1251_enable_interrupts(struct wl1251 *wl); void wl1251_disable_interrupts(struct wl1251 *wl); #define DEFAULT_HW_GEN_MODULATION_TYPE CCK_LONG /* Long Preamble */ #define DEFAULT_HW_GEN_TX_RATE RATE_2MBPS #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ #define WL1251_DEFAULT_POWER_LEVEL 20 #define WL1251_TX_QUEUE_LOW_WATERMARK 10 #define WL1251_TX_QUEUE_HIGH_WATERMARK 25 #define WL1251_DEFAULT_BEACON_INT 100 #define WL1251_DEFAULT_DTIM_PERIOD 1 #define WL1251_DEFAULT_CHANNEL 0 #define WL1251_DEFAULT_BET_CONSECUTIVE 10 #define CHIP_ID_1251_PG10 (0x7010101) #define CHIP_ID_1251_PG11 (0x7020101) #define CHIP_ID_1251_PG12 (0x7030101) #define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG20 (0x4030111) #define WL1251_FW_NAME "wl1251-fw.bin" #define WL1251_NVS_NAME "wl1251-nvs.bin" #define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */ #define WL1251_PART_DOWN_MEM_START 0x0 #define WL1251_PART_DOWN_MEM_SIZE 0x16800 #define WL1251_PART_DOWN_REG_START REGISTERS_BASE #define WL1251_PART_DOWN_REG_SIZE REGISTERS_DOWN_SIZE #define WL1251_PART_WORK_MEM_START 0x28000 #define WL1251_PART_WORK_MEM_SIZE 0x14000 #define WL1251_PART_WORK_REG_START REGISTERS_BASE #define WL1251_PART_WORK_REG_SIZE REGISTERS_WORK_SIZE #define WL1251_DEFAULT_LOW_RSSI_WEIGHT 10 #define WL1251_DEFAULT_LOW_RSSI_DEPTH 10 #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/tx.h0000644000175000017500000001364212026211315022642 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_TX_H__ #define __WL1251_TX_H__ #include #include "acx.h" /* * * TX PATH * * The Tx path uses a double buffer and a tx_control structure, each located * at a fixed address in the device's memory. On startup, the host retrieves * the pointers to these addresses. A double buffer allows for continuous data * flow towards the device. The host keeps track of which buffer is available * and alternates between these two buffers on a per packet basis. * * The size of each of the two buffers is large enough to hold the longest * 802.3 packet - maximum size Ethernet packet + header + descriptor. * TX complete indication will be received a-synchronously in a TX done cyclic * buffer which is composed of 16 tx_result descriptors structures and is used * in a cyclic manner. * * The TX (HOST) procedure is as follows: * 1. Read the Tx path status, that will give the data_out_count. * 2. goto 1, if not possible. * i.e. if data_in_count - data_out_count >= HwBuffer size (2 for double * buffer). * 3. Copy the packet (preceded by double_buffer_desc), if possible. * i.e. if data_in_count - data_out_count < HwBuffer size (2 for double * buffer). * 4. increment data_in_count. * 5. Inform the firmware by generating a firmware internal interrupt. * 6. FW will increment data_out_count after it reads the buffer. * * The TX Complete procedure: * 1. To get a TX complete indication the host enables the tx_complete flag in * the TX descriptor Structure. * 2. For each packet with a Tx Complete field set, the firmware adds the * transmit results to the cyclic buffer (txDoneRing) and sets both done_1 * and done_2 to 1 to indicate driver ownership. * 3. The firmware sends a Tx Complete interrupt to the host to trigger the * host to process the new data. Note: interrupt will be send per packet if * TX complete indication was requested in tx_control or per crossing * aggregation threshold. * 4. After receiving the Tx Complete interrupt, the host reads the * TxDescriptorDone information in a cyclic manner and clears both done_1 * and done_2 fields. * */ #define TX_COMPLETE_REQUIRED_BIT 0x80 #define TX_STATUS_DATA_OUT_COUNT_MASK 0xf #define WL1251_TX_ALIGN_TO 4 #define WL1251_TX_ALIGN(len) (((len) + WL1251_TX_ALIGN_TO - 1) & \ ~(WL1251_TX_ALIGN_TO - 1)) #define WL1251_TKIP_IV_SPACE 4 struct tx_control { /* Rate Policy (class) index */ unsigned rate_policy:3; /* When set, no ack policy is expected */ unsigned ack_policy:1; /* * Packet type: * 0 -> 802.11 * 1 -> 802.3 * 2 -> IP * 3 -> raw codec */ unsigned packet_type:2; /* If set, this is a QoS-Null or QoS-Data frame */ unsigned qos:1; /* * If set, the target triggers the tx complete INT * upon frame sending completion. */ unsigned tx_complete:1; /* 2 bytes padding before packet header */ unsigned xfer_pad:1; unsigned reserved:7; } __packed; struct tx_double_buffer_desc { /* Length of payload, including headers. */ __le16 length; /* * A bit mask that specifies the initial rate to be used * Possible values are: * 0x0001 - 1Mbits * 0x0002 - 2Mbits * 0x0004 - 5.5Mbits * 0x0008 - 6Mbits * 0x0010 - 9Mbits * 0x0020 - 11Mbits * 0x0040 - 12Mbits * 0x0080 - 18Mbits * 0x0100 - 22Mbits * 0x0200 - 24Mbits * 0x0400 - 36Mbits * 0x0800 - 48Mbits * 0x1000 - 54Mbits */ __le16 rate; /* Time in us that a packet can spend in the target */ __le32 expiry_time; /* index of the TX queue used for this packet */ u8 xmit_queue; /* Used to identify a packet */ u8 id; struct tx_control control; /* * The FW should cut the packet into fragments * of this size. */ __le16 frag_threshold; /* Numbers of HW queue blocks to be allocated */ u8 num_mem_blocks; u8 reserved; } __packed; enum { TX_SUCCESS = 0, TX_DMA_ERROR = BIT(7), TX_DISABLED = BIT(6), TX_RETRY_EXCEEDED = BIT(5), TX_TIMEOUT = BIT(4), TX_KEY_NOT_FOUND = BIT(3), TX_ENCRYPT_FAIL = BIT(2), TX_UNAVAILABLE_PRIORITY = BIT(1), }; struct tx_result { /* * Ownership synchronization between the host and * the firmware. If done_1 and done_2 are cleared, * owned by the FW (no info ready). */ u8 done_1; /* same as double_buffer_desc->id */ u8 id; /* * Total air access duration consumed by this * packet, including all retries and overheads. */ u16 medium_usage; /* Total media delay (from 1st EDCA AIFS counter until TX Complete). */ u32 medium_delay; /* Time between host xfer and tx complete */ u32 fw_hnadling_time; /* The LS-byte of the last TKIP sequence number. */ u8 lsb_seq_num; /* Retry count */ u8 ack_failures; /* At which rate we got a ACK */ u16 rate; u16 reserved; /* TX_* */ u8 status; /* See done_1 */ u8 done_2; } __packed; static inline int wl1251_tx_get_queue(int queue) { switch (queue) { case 0: return QOS_AC_VO; case 1: return QOS_AC_VI; case 2: return QOS_AC_BE; case 3: return QOS_AC_BK; default: return QOS_AC_BE; } } void wl1251_tx_work(struct work_struct *work); void wl1251_tx_complete(struct wl1251 *wl); void wl1251_tx_flush(struct wl1251 *wl); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/tx.c0000644000175000017500000003252112026211315022632 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include "wl1251.h" #include "reg.h" #include "tx.h" #include "ps.h" #include "io.h" static bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count) { int used, data_in_count; data_in_count = wl->data_in_count; if (data_in_count < data_out_count) /* data_in_count has wrapped */ data_in_count += TX_STATUS_DATA_OUT_COUNT_MASK + 1; used = data_in_count - data_out_count; WARN_ON(used < 0); WARN_ON(used > DP_TX_PACKET_RING_CHUNK_NUM); if (used >= DP_TX_PACKET_RING_CHUNK_NUM) return true; else return false; } static int wl1251_tx_path_status(struct wl1251 *wl) { u32 status, addr, data_out_count; bool busy; addr = wl->data_path->tx_control_addr; status = wl1251_mem_read32(wl, addr); data_out_count = status & TX_STATUS_DATA_OUT_COUNT_MASK; busy = wl1251_tx_double_buffer_busy(wl, data_out_count); if (busy) return -EBUSY; return 0; } static int wl1251_tx_id(struct wl1251 *wl, struct sk_buff *skb) { int i; for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) if (wl->tx_frames[i] == NULL) { wl->tx_frames[i] = skb; return i; } return -EBUSY; } static void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr, struct ieee80211_tx_info *control, u16 fc) { *(u16 *)&tx_hdr->control = 0; tx_hdr->control.rate_policy = 0; /* 802.11 packets */ tx_hdr->control.packet_type = 0; if (control->flags & IEEE80211_TX_CTL_NO_ACK) tx_hdr->control.ack_policy = 1; tx_hdr->control.tx_complete = 1; if ((fc & IEEE80211_FTYPE_DATA) && ((fc & IEEE80211_STYPE_QOS_DATA) || (fc & IEEE80211_STYPE_QOS_NULLFUNC))) tx_hdr->control.qos = 1; } /* RSN + MIC = 8 + 8 = 16 bytes (worst case - AES). */ #define MAX_MSDU_SECURITY_LENGTH 16 #define MAX_MPDU_SECURITY_LENGTH 16 #define WLAN_QOS_HDR_LEN 26 #define MAX_MPDU_HEADER_AND_SECURITY (MAX_MPDU_SECURITY_LENGTH + \ WLAN_QOS_HDR_LEN) #define HW_BLOCK_SIZE 252 static void wl1251_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr) { u16 payload_len, frag_threshold, mem_blocks; u16 num_mpdus, mem_blocks_per_frag; frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; tx_hdr->frag_threshold = cpu_to_le16(frag_threshold); payload_len = le16_to_cpu(tx_hdr->length) + MAX_MSDU_SECURITY_LENGTH; if (payload_len > frag_threshold) { mem_blocks_per_frag = ((frag_threshold + MAX_MPDU_HEADER_AND_SECURITY) / HW_BLOCK_SIZE) + 1; num_mpdus = payload_len / frag_threshold; mem_blocks = num_mpdus * mem_blocks_per_frag; payload_len -= num_mpdus * frag_threshold; num_mpdus++; } else { mem_blocks_per_frag = 0; mem_blocks = 0; num_mpdus = 1; } mem_blocks += (payload_len / HW_BLOCK_SIZE) + 1; if (num_mpdus > 1) mem_blocks += min(num_mpdus, mem_blocks_per_frag); tx_hdr->num_mem_blocks = mem_blocks; } static int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb, struct ieee80211_tx_info *control) { struct tx_double_buffer_desc *tx_hdr; struct ieee80211_rate *rate; int id; u16 fc; if (!skb) return -EINVAL; id = wl1251_tx_id(wl, skb); if (id < 0) return id; fc = *(u16 *)skb->data; tx_hdr = (struct tx_double_buffer_desc *) skb_push(skb, sizeof(*tx_hdr)); tx_hdr->length = cpu_to_le16(skb->len - sizeof(*tx_hdr)); rate = ieee80211_get_tx_rate(wl->hw, control); tx_hdr->rate = cpu_to_le16(rate->hw_value); tx_hdr->expiry_time = cpu_to_le32(1 << 16); tx_hdr->id = id; tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb)); wl1251_tx_control(tx_hdr, control, fc); wl1251_tx_frag_block_num(tx_hdr); return 0; } /* We copy the packet to the target */ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, struct ieee80211_tx_info *control) { struct tx_double_buffer_desc *tx_hdr; int len; u32 addr; if (!skb) return -EINVAL; tx_hdr = (struct tx_double_buffer_desc *) skb->data; if (control->control.hw_key && control->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen; __le16 fc; u16 length; u8 *pos; fc = *(__le16 *)(skb->data + sizeof(*tx_hdr)); length = le16_to_cpu(tx_hdr->length) + WL1251_TKIP_IV_SPACE; tx_hdr->length = cpu_to_le16(length); hdrlen = ieee80211_hdrlen(fc); pos = skb_push(skb, WL1251_TKIP_IV_SPACE); memmove(pos, pos + WL1251_TKIP_IV_SPACE, sizeof(*tx_hdr) + hdrlen); } /* Revisit. This is a workaround for getting non-aligned packets. This happens at least with EAPOL packets from the user space. Our DMA requires packets to be aligned on a 4-byte boundary. */ if (unlikely((long)skb->data & 0x03)) { int offset = (4 - (long)skb->data) & 0x03; wl1251_debug(DEBUG_TX, "skb offset %d", offset); /* check whether the current skb can be used */ if (skb_cloned(skb) || (skb_tailroom(skb) < offset)) { struct sk_buff *newskb = skb_copy_expand(skb, 0, 3, GFP_KERNEL); if (unlikely(newskb == NULL)) { wl1251_error("Can't allocate skb!"); return -EINVAL; } tx_hdr = (struct tx_double_buffer_desc *) newskb->data; dev_kfree_skb_any(skb); wl->tx_frames[tx_hdr->id] = skb = newskb; offset = (4 - (long)skb->data) & 0x03; wl1251_debug(DEBUG_TX, "new skb offset %d", offset); } /* align the buffer on a 4-byte boundary */ if (offset) { unsigned char *src = skb->data; skb_reserve(skb, offset); memmove(skb->data, src, skb->len); tx_hdr = (struct tx_double_buffer_desc *) skb->data; } } /* Our skb->data at this point includes the HW header */ len = WL1251_TX_ALIGN(skb->len); if (wl->data_in_count & 0x1) addr = wl->data_path->tx_packet_ring_addr + wl->data_path->tx_packet_ring_chunk_size; else addr = wl->data_path->tx_packet_ring_addr; wl1251_mem_write(wl, addr, skb->data, len); wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x " "queue %d", tx_hdr->id, skb, tx_hdr->length, tx_hdr->rate, tx_hdr->xmit_queue); return 0; } static void wl1251_tx_trigger(struct wl1251 *wl) { u32 data, addr; if (wl->data_in_count & 0x1) { addr = ACX_REG_INTERRUPT_TRIG_H; data = INTR_TRIG_TX_PROC1; } else { addr = ACX_REG_INTERRUPT_TRIG; data = INTR_TRIG_TX_PROC0; } wl1251_reg_write32(wl, addr, data); /* Bumping data in */ wl->data_in_count = (wl->data_in_count + 1) & TX_STATUS_DATA_OUT_COUNT_MASK; } /* caller must hold wl->mutex */ static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) { struct ieee80211_tx_info *info; int ret = 0; u8 idx; info = IEEE80211_SKB_CB(skb); if (info->control.hw_key) { idx = info->control.hw_key->hw_key_idx; if (unlikely(wl->default_key != idx)) { ret = wl1251_acx_default_key(wl, idx); if (ret < 0) return ret; } } ret = wl1251_tx_path_status(wl); if (ret < 0) return ret; ret = wl1251_tx_fill_hdr(wl, skb, info); if (ret < 0) return ret; ret = wl1251_tx_send_packet(wl, skb, info); if (ret < 0) return ret; wl1251_tx_trigger(wl); return ret; } void wl1251_tx_work(struct work_struct *work) { struct wl1251 *wl = container_of(work, struct wl1251, tx_work); struct sk_buff *skb; bool woken_up = false; int ret; mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1251_STATE_OFF)) goto out; while ((skb = skb_dequeue(&wl->tx_queue))) { if (!woken_up) { ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; woken_up = true; } ret = wl1251_tx_frame(wl, skb); if (ret == -EBUSY) { skb_queue_head(&wl->tx_queue, skb); goto out; } else if (ret < 0) { dev_kfree_skb(skb); goto out; } } out: if (woken_up) wl1251_ps_elp_sleep(wl); mutex_unlock(&wl->mutex); } static const char *wl1251_tx_parse_status(u8 status) { /* 8 bit status field, one character per bit plus null */ static char buf[9]; int i = 0; memset(buf, 0, sizeof(buf)); if (status & TX_DMA_ERROR) buf[i++] = 'm'; if (status & TX_DISABLED) buf[i++] = 'd'; if (status & TX_RETRY_EXCEEDED) buf[i++] = 'r'; if (status & TX_TIMEOUT) buf[i++] = 't'; if (status & TX_KEY_NOT_FOUND) buf[i++] = 'k'; if (status & TX_ENCRYPT_FAIL) buf[i++] = 'e'; if (status & TX_UNAVAILABLE_PRIORITY) buf[i++] = 'p'; /* bit 0 is unused apparently */ return buf; } static void wl1251_tx_packet_cb(struct wl1251 *wl, struct tx_result *result) { struct ieee80211_tx_info *info; struct sk_buff *skb; int hdrlen; u8 *frame; skb = wl->tx_frames[result->id]; if (skb == NULL) { wl1251_error("SKB for packet %d is NULL", result->id); return; } info = IEEE80211_SKB_CB(skb); if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && (result->status == TX_SUCCESS)) info->flags |= IEEE80211_TX_STAT_ACK; info->status.rates[0].count = result->ack_failures + 1; wl->stats.retry_count += result->ack_failures; /* * We have to remove our private TX header before pushing * the skb back to mac80211. */ frame = skb_pull(skb, sizeof(struct tx_double_buffer_desc)); if (info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(frame + WL1251_TKIP_IV_SPACE, frame, hdrlen); skb_pull(skb, WL1251_TKIP_IV_SPACE); } wl1251_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" " status 0x%x (%s)", result->id, skb, result->ack_failures, result->rate, result->status, wl1251_tx_parse_status(result->status)); ieee80211_tx_status(wl->hw, skb); wl->tx_frames[result->id] = NULL; } /* Called upon reception of a TX complete interrupt */ void wl1251_tx_complete(struct wl1251 *wl) { int i, result_index, num_complete = 0, queue_len; struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr; unsigned long flags; if (unlikely(wl->state != WL1251_STATE_ON)) return; /* First we read the result */ wl1251_mem_read(wl, wl->data_path->tx_complete_addr, result, sizeof(result)); result_index = wl->next_tx_complete; for (i = 0; i < ARRAY_SIZE(result); i++) { result_ptr = &result[result_index]; if (result_ptr->done_1 == 1 && result_ptr->done_2 == 1) { wl1251_tx_packet_cb(wl, result_ptr); result_ptr->done_1 = 0; result_ptr->done_2 = 0; result_index = (result_index + 1) & (FW_TX_CMPLT_BLOCK_SIZE - 1); num_complete++; } else { break; } } queue_len = skb_queue_len(&wl->tx_queue); if ((num_complete > 0) && (queue_len > 0)) { /* firmware buffer has space, reschedule tx_work */ wl1251_debug(DEBUG_TX, "tx_complete: reschedule tx_work"); ieee80211_queue_work(wl->hw, &wl->tx_work); } if (wl->tx_queue_stopped && queue_len <= WL1251_TX_QUEUE_LOW_WATERMARK) { /* tx_queue has space, restart queues */ wl1251_debug(DEBUG_TX, "tx_complete: waking queues"); spin_lock_irqsave(&wl->wl_lock, flags); ieee80211_wake_queues(wl->hw); wl->tx_queue_stopped = false; spin_unlock_irqrestore(&wl->wl_lock, flags); } /* Every completed frame needs to be acknowledged */ if (num_complete) { /* * If we've wrapped, we have to clear * the results in 2 steps. */ if (result_index > wl->next_tx_complete) { /* Only 1 write is needed */ wl1251_mem_write(wl, wl->data_path->tx_complete_addr + (wl->next_tx_complete * sizeof(struct tx_result)), &result[wl->next_tx_complete], num_complete * sizeof(struct tx_result)); } else if (result_index < wl->next_tx_complete) { /* 2 writes are needed */ wl1251_mem_write(wl, wl->data_path->tx_complete_addr + (wl->next_tx_complete * sizeof(struct tx_result)), &result[wl->next_tx_complete], (FW_TX_CMPLT_BLOCK_SIZE - wl->next_tx_complete) * sizeof(struct tx_result)); wl1251_mem_write(wl, wl->data_path->tx_complete_addr, result, (num_complete - FW_TX_CMPLT_BLOCK_SIZE + wl->next_tx_complete) * sizeof(struct tx_result)); } else { /* We have to write the whole array */ wl1251_mem_write(wl, wl->data_path->tx_complete_addr, result, FW_TX_CMPLT_BLOCK_SIZE * sizeof(struct tx_result)); } } wl->next_tx_complete = result_index; } /* caller must hold wl->mutex */ void wl1251_tx_flush(struct wl1251 *wl) { int i; struct sk_buff *skb; struct ieee80211_tx_info *info; /* TX failure */ /* control->flags = 0; FIXME */ while ((skb = skb_dequeue(&wl->tx_queue))) { info = IEEE80211_SKB_CB(skb); wl1251_debug(DEBUG_TX, "flushing skb 0x%p", skb); if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) continue; ieee80211_tx_status(wl->hw, skb); } for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) if (wl->tx_frames[i] != NULL) { skb = wl->tx_frames[i]; info = IEEE80211_SKB_CB(skb); if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) continue; ieee80211_tx_status(wl->hw, skb); wl->tx_frames[i] = NULL; } } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/spi.h0000644000175000017500000000366212026211315023003 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_SPI_H__ #define __WL1251_SPI_H__ #include "cmd.h" #include "acx.h" #include "reg.h" #define WSPI_CMD_READ 0x40000000 #define WSPI_CMD_WRITE 0x00000000 #define WSPI_CMD_FIXED 0x20000000 #define WSPI_CMD_BYTE_LENGTH 0x1FFE0000 #define WSPI_CMD_BYTE_LENGTH_OFFSET 17 #define WSPI_CMD_BYTE_ADDR 0x0001FFFF #define WSPI_INIT_CMD_CRC_LEN 5 #define WSPI_INIT_CMD_START 0x00 #define WSPI_INIT_CMD_TX 0x40 /* the extra bypass bit is sampled by the TNET as '1' */ #define WSPI_INIT_CMD_BYPASS_BIT 0x80 #define WSPI_INIT_CMD_FIXEDBUSY_LEN 0x07 #define WSPI_INIT_CMD_EN_FIXEDBUSY 0x80 #define WSPI_INIT_CMD_DIS_FIXEDBUSY 0x00 #define WSPI_INIT_CMD_IOD 0x40 #define WSPI_INIT_CMD_IP 0x20 #define WSPI_INIT_CMD_CS 0x10 #define WSPI_INIT_CMD_WS 0x08 #define WSPI_INIT_CMD_WSPI 0x01 #define WSPI_INIT_CMD_END 0x01 #define WSPI_INIT_CMD_LEN 8 #define HW_ACCESS_WSPI_FIXED_BUSY_LEN \ ((WL1251_BUSY_WORD_LEN - 4) / sizeof(u32)) #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 #endif /* __WL1251_SPI_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/sdio.c0000644000175000017500000002063212026211315023135 0ustar mcgrofmcgrof/* * wl12xx SDIO routines * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Copyright (C) 2005 Texas Instruments Incorporated * Copyright (C) 2008 Google Inc * Copyright (C) 2009 Bob Copeland (me@bobcopeland.com) */ #include #include #include #include #include #include #include #include #include #include "wl1251.h" #ifndef SDIO_VENDOR_ID_TI #define SDIO_VENDOR_ID_TI 0x104c #endif #ifndef SDIO_DEVICE_ID_TI_WL1251 #define SDIO_DEVICE_ID_TI_WL1251 0x9066 #endif struct wl1251_sdio { struct sdio_func *func; u32 elp_val; }; static struct sdio_func *wl_to_func(struct wl1251 *wl) { struct wl1251_sdio *wl_sdio = wl->if_priv; return wl_sdio->func; } static void wl1251_sdio_interrupt(struct sdio_func *func) { struct wl1251 *wl = sdio_get_drvdata(func); wl1251_debug(DEBUG_IRQ, "IRQ"); /* FIXME should be synchronous for sdio */ ieee80211_queue_work(wl->hw, &wl->irq_work); } static const struct sdio_device_id wl1251_devices[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1251) }, {} }; MODULE_DEVICE_TABLE(sdio, wl1251_devices); static void wl1251_sdio_read(struct wl1251 *wl, int addr, void *buf, size_t len) { int ret; struct sdio_func *func = wl_to_func(wl); sdio_claim_host(func); ret = sdio_memcpy_fromio(func, buf, addr, len); if (ret) wl1251_error("sdio read failed (%d)", ret); sdio_release_host(func); } static void wl1251_sdio_write(struct wl1251 *wl, int addr, void *buf, size_t len) { int ret; struct sdio_func *func = wl_to_func(wl); sdio_claim_host(func); ret = sdio_memcpy_toio(func, addr, buf, len); if (ret) wl1251_error("sdio write failed (%d)", ret); sdio_release_host(func); } static void wl1251_sdio_read_elp(struct wl1251 *wl, int addr, u32 *val) { int ret = 0; struct wl1251_sdio *wl_sdio = wl->if_priv; struct sdio_func *func = wl_sdio->func; /* * The hardware only supports RAW (read after write) access for * reading, regular sdio_readb won't work here (it interprets * the unused bits of CMD52 as write data even if we send read * request). */ sdio_claim_host(func); *val = sdio_writeb_readb(func, wl_sdio->elp_val, addr, &ret); sdio_release_host(func); if (ret) wl1251_error("sdio_readb failed (%d)", ret); } static void wl1251_sdio_write_elp(struct wl1251 *wl, int addr, u32 val) { int ret = 0; struct wl1251_sdio *wl_sdio = wl->if_priv; struct sdio_func *func = wl_sdio->func; sdio_claim_host(func); sdio_writeb(func, val, addr, &ret); sdio_release_host(func); if (ret) wl1251_error("sdio_writeb failed (%d)", ret); else wl_sdio->elp_val = val; } static void wl1251_sdio_reset(struct wl1251 *wl) { } static void wl1251_sdio_enable_irq(struct wl1251 *wl) { struct sdio_func *func = wl_to_func(wl); sdio_claim_host(func); sdio_claim_irq(func, wl1251_sdio_interrupt); sdio_release_host(func); } static void wl1251_sdio_disable_irq(struct wl1251 *wl) { struct sdio_func *func = wl_to_func(wl); sdio_claim_host(func); sdio_release_irq(func); sdio_release_host(func); } /* Interrupts when using dedicated WLAN_IRQ pin */ static irqreturn_t wl1251_line_irq(int irq, void *cookie) { struct wl1251 *wl = cookie; ieee80211_queue_work(wl->hw, &wl->irq_work); return IRQ_HANDLED; } static void wl1251_enable_line_irq(struct wl1251 *wl) { return enable_irq(wl->irq); } static void wl1251_disable_line_irq(struct wl1251 *wl) { return disable_irq(wl->irq); } static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable) { struct sdio_func *func = wl_to_func(wl); int ret; if (enable) { /* * Power is controlled by runtime PM, but we still call board * callback in case it wants to do any additional setup, * for example enabling clock buffer for the module. */ if (wl->set_power) wl->set_power(true); ret = pm_runtime_get_sync(&func->dev); if (ret < 0) goto out; sdio_claim_host(func); sdio_enable_func(func); sdio_release_host(func); } else { sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); ret = pm_runtime_put_sync(&func->dev); if (ret < 0) goto out; if (wl->set_power) wl->set_power(false); } out: return ret; } static struct wl1251_if_operations wl1251_sdio_ops = { .read = wl1251_sdio_read, .write = wl1251_sdio_write, .write_elp = wl1251_sdio_write_elp, .read_elp = wl1251_sdio_read_elp, .reset = wl1251_sdio_reset, .power = wl1251_sdio_set_power, }; static int wl1251_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int ret; struct wl1251 *wl; struct ieee80211_hw *hw; struct wl1251_sdio *wl_sdio; const struct wl12xx_platform_data *wl12xx_board_data; hw = wl1251_alloc_hw(); if (IS_ERR(hw)) return PTR_ERR(hw); wl = hw->priv; wl_sdio = kzalloc(sizeof(*wl_sdio), GFP_KERNEL); if (wl_sdio == NULL) { ret = -ENOMEM; goto out_free_hw; } sdio_claim_host(func); ret = sdio_enable_func(func); if (ret) goto release; sdio_set_block_size(func, 512); sdio_release_host(func); SET_IEEE80211_DEV(hw, &func->dev); wl_sdio->func = func; wl->if_priv = wl_sdio; wl->if_ops = &wl1251_sdio_ops; wl12xx_board_data = wl12xx_get_platform_data(); if (!IS_ERR(wl12xx_board_data)) { wl->set_power = wl12xx_board_data->set_power; wl->irq = wl12xx_board_data->irq; wl->use_eeprom = wl12xx_board_data->use_eeprom; } if (wl->irq) { irq_set_status_flags(wl->irq, IRQ_NOAUTOEN); ret = request_irq(wl->irq, wl1251_line_irq, 0, "wl1251", wl); if (ret < 0) { wl1251_error("request_irq() failed: %d", ret); goto disable; } irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq; wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq; wl1251_info("using dedicated interrupt line"); } else { wl1251_sdio_ops.enable_irq = wl1251_sdio_enable_irq; wl1251_sdio_ops.disable_irq = wl1251_sdio_disable_irq; wl1251_info("using SDIO interrupt"); } ret = wl1251_init_ieee80211(wl); if (ret) goto out_free_irq; sdio_set_drvdata(func, wl); /* Tell PM core that we don't need the card to be powered now */ pm_runtime_put_noidle(&func->dev); return ret; out_free_irq: if (wl->irq) free_irq(wl->irq, wl); disable: sdio_claim_host(func); sdio_disable_func(func); release: sdio_release_host(func); kfree(wl_sdio); out_free_hw: wl1251_free_hw(wl); return ret; } static void __devexit wl1251_sdio_remove(struct sdio_func *func) { struct wl1251 *wl = sdio_get_drvdata(func); struct wl1251_sdio *wl_sdio = wl->if_priv; /* Undo decrement done above in wl1251_probe */ pm_runtime_get_noresume(&func->dev); if (wl->irq) free_irq(wl->irq, wl); wl1251_free_hw(wl); kfree(wl_sdio); sdio_claim_host(func); sdio_release_irq(func); sdio_disable_func(func); sdio_release_host(func); } static int wl1251_suspend(struct device *dev) { /* * Tell MMC/SDIO core it's OK to power down the card * (if it isn't already), but not to remove it completely. */ return 0; } static int wl1251_resume(struct device *dev) { return 0; } static const struct dev_pm_ops wl1251_sdio_pm_ops = { .suspend = wl1251_suspend, .resume = wl1251_resume, }; static struct sdio_driver wl1251_sdio_driver = { .name = "wl1251_sdio", .id_table = wl1251_devices, .probe = wl1251_sdio_probe, .remove = __devexit_p(wl1251_sdio_remove), .drv.pm = &wl1251_sdio_pm_ops, }; static int __init wl1251_sdio_init(void) { int err; err = sdio_register_driver(&wl1251_sdio_driver); if (err) wl1251_error("failed to register sdio driver: %d", err); return err; } static void __exit wl1251_sdio_exit(void) { sdio_unregister_driver(&wl1251_sdio_driver); wl1251_notice("unloaded"); } module_init(wl1251_sdio_init); module_exit(wl1251_sdio_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo "); compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/rx.h0000644000175000017500000000615712026211315022643 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_RX_H__ #define __WL1251_RX_H__ #include #include "wl1251.h" /* * RX PATH * * The Rx path uses a double buffer and an rx_contro structure, each located * at a fixed address in the device memory. The host keeps track of which * buffer is available and alternates between them on a per packet basis. * The size of each of the two buffers is large enough to hold the longest * 802.3 packet. * The RX path goes like that: * 1) The target generates an interrupt each time a new packet is received. * There are 2 RX interrupts, one for each buffer. * 2) The host reads the received packet from one of the double buffers. * 3) The host triggers a target interrupt. * 4) The target prepares the next RX packet. */ #define WL1251_RX_MAX_RSSI -30 #define WL1251_RX_MIN_RSSI -95 #define WL1251_RX_ALIGN_TO 4 #define WL1251_RX_ALIGN(len) (((len) + WL1251_RX_ALIGN_TO - 1) & \ ~(WL1251_RX_ALIGN_TO - 1)) #define SHORT_PREAMBLE_BIT BIT(0) #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) #define PLCP_HEADER_LENGTH 8 #define RX_DESC_PACKETID_SHIFT 11 #define RX_MAX_PACKET_ID 3 #define RX_DESC_VALID_FCS 0x0001 #define RX_DESC_MATCH_RXADDR1 0x0002 #define RX_DESC_MCAST 0x0004 #define RX_DESC_STAINTIM 0x0008 #define RX_DESC_VIRTUAL_BM 0x0010 #define RX_DESC_BCAST 0x0020 #define RX_DESC_MATCH_SSID 0x0040 #define RX_DESC_MATCH_BSSID 0x0080 #define RX_DESC_ENCRYPTION_MASK 0x0300 #define RX_DESC_MEASURMENT 0x0400 #define RX_DESC_SEQNUM_MASK 0x1800 #define RX_DESC_MIC_FAIL 0x2000 #define RX_DESC_DECRYPT_FAIL 0x4000 struct wl1251_rx_descriptor { u32 timestamp; /* In microseconds */ u16 length; /* Paylod length, including headers */ u16 flags; /* * 0 - 802.11 * 1 - 802.3 * 2 - IP * 3 - Raw Codec */ u8 type; /* * Received Rate: * 0x0A - 1MBPS * 0x14 - 2MBPS * 0x37 - 5_5MBPS * 0x0B - 6MBPS * 0x0F - 9MBPS * 0x6E - 11MBPS * 0x0A - 12MBPS * 0x0E - 18MBPS * 0xDC - 22MBPS * 0x09 - 24MBPS * 0x0D - 36MBPS * 0x08 - 48MBPS * 0x0C - 54MBPS */ u8 rate; u8 mod_pre; /* Modulation and preamble */ u8 channel; /* * 0 - 2.4 Ghz * 1 - 5 Ghz */ u8 band; s8 rssi; /* in dB */ u8 rcpi; /* in dB */ u8 snr; /* in dB */ } __packed; void wl1251_rx(struct wl1251 *wl); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/rx.c0000644000175000017500000001344612026211315022635 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include "wl1251.h" #include "reg.h" #include "io.h" #include "rx.h" #include "cmd.h" #include "acx.h" static void wl1251_rx_header(struct wl1251 *wl, struct wl1251_rx_descriptor *desc) { u32 rx_packet_ring_addr; rx_packet_ring_addr = wl->data_path->rx_packet_ring_addr; if (wl->rx_current_buffer) rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; wl1251_mem_read(wl, rx_packet_ring_addr, desc, sizeof(*desc)); } static void wl1251_rx_status(struct wl1251 *wl, struct wl1251_rx_descriptor *desc, struct ieee80211_rx_status *status, u8 beacon) { u64 mactime; int ret; memset(status, 0, sizeof(struct ieee80211_rx_status)); status->band = IEEE80211_BAND_2GHZ; status->mactime = desc->timestamp; /* * The rx status timestamp is a 32 bits value while the TSF is a * 64 bits one. * For IBSS merging, TSF is mandatory, so we have to get it * somehow, so we ask for ACX_TSF_INFO. * That could be moved to the get_tsf() hook, but unfortunately, * this one must be atomic, while our SPI routines can sleep. */ if ((wl->bss_type == BSS_TYPE_IBSS) && beacon) { ret = wl1251_acx_tsf_info(wl, &mactime); if (ret == 0) status->mactime = mactime; } status->signal = desc->rssi; /* * FIXME: guessing that snr needs to be divided by two, otherwise * the values don't make any sense */ wl->noise = desc->rssi - desc->snr / 2; status->freq = ieee80211_channel_to_frequency(desc->channel, status->band); status->flag |= RX_FLAG_MACTIME_MPDU; if (desc->flags & RX_DESC_ENCRYPTION_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; if (likely(!(desc->flags & RX_DESC_DECRYPT_FAIL))) status->flag |= RX_FLAG_DECRYPTED; if (unlikely(desc->flags & RX_DESC_MIC_FAIL)) status->flag |= RX_FLAG_MMIC_ERROR; } if (unlikely(!(desc->flags & RX_DESC_VALID_FCS))) status->flag |= RX_FLAG_FAILED_FCS_CRC; switch (desc->rate) { /* skip 1 and 12 Mbps because they have same value 0x0a */ case RATE_2MBPS: status->rate_idx = 1; break; case RATE_5_5MBPS: status->rate_idx = 2; break; case RATE_11MBPS: status->rate_idx = 3; break; case RATE_6MBPS: status->rate_idx = 4; break; case RATE_9MBPS: status->rate_idx = 5; break; case RATE_18MBPS: status->rate_idx = 7; break; case RATE_24MBPS: status->rate_idx = 8; break; case RATE_36MBPS: status->rate_idx = 9; break; case RATE_48MBPS: status->rate_idx = 10; break; case RATE_54MBPS: status->rate_idx = 11; break; } /* for 1 and 12 Mbps we have to check the modulation */ if (desc->rate == RATE_1MBPS) { if (!(desc->mod_pre & OFDM_RATE_BIT)) /* CCK -> RATE_1MBPS */ status->rate_idx = 0; else /* OFDM -> RATE_12MBPS */ status->rate_idx = 6; } if (desc->mod_pre & SHORT_PREAMBLE_BIT) status->flag |= RX_FLAG_SHORTPRE; } static void wl1251_rx_body(struct wl1251 *wl, struct wl1251_rx_descriptor *desc) { struct sk_buff *skb; struct ieee80211_rx_status status; u8 *rx_buffer, beacon = 0; u16 length, *fc; u32 curr_id, last_id_inc, rx_packet_ring_addr; length = WL1251_RX_ALIGN(desc->length - PLCP_HEADER_LENGTH); curr_id = (desc->flags & RX_DESC_SEQNUM_MASK) >> RX_DESC_PACKETID_SHIFT; last_id_inc = (wl->rx_last_id + 1) % (RX_MAX_PACKET_ID + 1); if (last_id_inc != curr_id) { wl1251_warning("curr ID:%d, last ID inc:%d", curr_id, last_id_inc); wl->rx_last_id = curr_id; } else { wl->rx_last_id = last_id_inc; } rx_packet_ring_addr = wl->data_path->rx_packet_ring_addr + sizeof(struct wl1251_rx_descriptor) + 20; if (wl->rx_current_buffer) rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1251_error("Couldn't allocate RX frame"); return; } rx_buffer = skb_put(skb, length); wl1251_mem_read(wl, rx_packet_ring_addr, rx_buffer, length); /* The actual length doesn't include the target's alignment */ skb->len = desc->length - PLCP_HEADER_LENGTH; fc = (u16 *)skb->data; if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) beacon = 1; wl1251_rx_status(wl, desc, &status, beacon); wl1251_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, beacon ? "beacon" : ""); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); ieee80211_rx_ni(wl->hw, skb); } static void wl1251_rx_ack(struct wl1251 *wl) { u32 data, addr; if (wl->rx_current_buffer) { addr = ACX_REG_INTERRUPT_TRIG_H; data = INTR_TRIG_RX_PROC1; } else { addr = ACX_REG_INTERRUPT_TRIG; data = INTR_TRIG_RX_PROC0; } wl1251_reg_write32(wl, addr, data); /* Toggle buffer ring */ wl->rx_current_buffer = !wl->rx_current_buffer; } void wl1251_rx(struct wl1251 *wl) { struct wl1251_rx_descriptor *rx_desc; if (wl->state != WL1251_STATE_ON) return; rx_desc = wl->rx_descriptor; /* We first read the frame's header */ wl1251_rx_header(wl, rx_desc); /* Now we can read the body */ wl1251_rx_body(wl, rx_desc); /* Finally, we need to ACK the RX */ wl1251_rx_ack(wl); } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/reg.h0000644000175000017500000005356712026211315022776 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __REG_H__ #define __REG_H__ #include #define REGISTERS_BASE 0x00300000 #define DRPW_BASE 0x00310000 #define REGISTERS_DOWN_SIZE 0x00008800 #define REGISTERS_WORK_SIZE 0x0000b000 #define HW_ACCESS_ELP_CTRL_REG_ADDR 0x1FFFC /* ELP register commands */ #define ELPCTRL_WAKE_UP 0x1 #define ELPCTRL_WAKE_UP_WLAN_READY 0x5 #define ELPCTRL_SLEEP 0x0 /* ELP WLAN_READY bit */ #define ELPCTRL_WLAN_READY 0x2 /* Device Configuration registers*/ #define SOR_CFG (REGISTERS_BASE + 0x0800) #define ECPU_CTRL (REGISTERS_BASE + 0x0804) #define HI_CFG (REGISTERS_BASE + 0x0808) /* EEPROM registers */ #define EE_START (REGISTERS_BASE + 0x080C) #define EE_CTL (REGISTERS_BASE + 0x2000) #define EE_DATA (REGISTERS_BASE + 0x2004) #define EE_ADDR (REGISTERS_BASE + 0x2008) #define EE_CTL_READ 2 #define CHIP_ID_B (REGISTERS_BASE + 0x5674) #define CHIP_ID_1251_PG10 (0x7010101) #define CHIP_ID_1251_PG11 (0x7020101) #define CHIP_ID_1251_PG12 (0x7030101) #define ENABLE (REGISTERS_BASE + 0x5450) /* Power Management registers */ #define ELP_CFG_MODE (REGISTERS_BASE + 0x5804) #define ELP_CMD (REGISTERS_BASE + 0x5808) #define PLL_CAL_TIME (REGISTERS_BASE + 0x5810) #define CLK_REQ_TIME (REGISTERS_BASE + 0x5814) #define CLK_BUF_TIME (REGISTERS_BASE + 0x5818) #define CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820) /* Scratch Pad registers*/ #define SCR_PAD0 (REGISTERS_BASE + 0x5608) #define SCR_PAD1 (REGISTERS_BASE + 0x560C) #define SCR_PAD2 (REGISTERS_BASE + 0x5610) #define SCR_PAD3 (REGISTERS_BASE + 0x5614) #define SCR_PAD4 (REGISTERS_BASE + 0x5618) #define SCR_PAD4_SET (REGISTERS_BASE + 0x561C) #define SCR_PAD4_CLR (REGISTERS_BASE + 0x5620) #define SCR_PAD5 (REGISTERS_BASE + 0x5624) #define SCR_PAD5_SET (REGISTERS_BASE + 0x5628) #define SCR_PAD5_CLR (REGISTERS_BASE + 0x562C) #define SCR_PAD6 (REGISTERS_BASE + 0x5630) #define SCR_PAD7 (REGISTERS_BASE + 0x5634) #define SCR_PAD8 (REGISTERS_BASE + 0x5638) #define SCR_PAD9 (REGISTERS_BASE + 0x563C) /* Spare registers*/ #define SPARE_A1 (REGISTERS_BASE + 0x0994) #define SPARE_A2 (REGISTERS_BASE + 0x0998) #define SPARE_A3 (REGISTERS_BASE + 0x099C) #define SPARE_A4 (REGISTERS_BASE + 0x09A0) #define SPARE_A5 (REGISTERS_BASE + 0x09A4) #define SPARE_A6 (REGISTERS_BASE + 0x09A8) #define SPARE_A7 (REGISTERS_BASE + 0x09AC) #define SPARE_A8 (REGISTERS_BASE + 0x09B0) #define SPARE_B1 (REGISTERS_BASE + 0x5420) #define SPARE_B2 (REGISTERS_BASE + 0x5424) #define SPARE_B3 (REGISTERS_BASE + 0x5428) #define SPARE_B4 (REGISTERS_BASE + 0x542C) #define SPARE_B5 (REGISTERS_BASE + 0x5430) #define SPARE_B6 (REGISTERS_BASE + 0x5434) #define SPARE_B7 (REGISTERS_BASE + 0x5438) #define SPARE_B8 (REGISTERS_BASE + 0x543C) enum wl12xx_acx_int_reg { ACX_REG_INTERRUPT_TRIG, ACX_REG_INTERRUPT_TRIG_H, /*============================================= Host Interrupt Mask Register - 32bit (RW) ------------------------------------------ Setting a bit in this register masks the corresponding interrupt to the host. 0 - RX0 - Rx first dubble buffer Data Interrupt 1 - TXD - Tx Data Interrupt 2 - TXXFR - Tx Transfer Interrupt 3 - RX1 - Rx second dubble buffer Data Interrupt 4 - RXXFR - Rx Transfer Interrupt 5 - EVENT_A - Event Mailbox interrupt 6 - EVENT_B - Event Mailbox interrupt 7 - WNONHST - Wake On Host Interrupt 8 - TRACE_A - Debug Trace interrupt 9 - TRACE_B - Debug Trace interrupt 10 - CDCMP - Command Complete Interrupt 11 - 12 - 13 - 14 - ICOMP - Initialization Complete Interrupt 16 - SG SE - Soft Gemini - Sense enable interrupt 17 - SG SD - Soft Gemini - Sense disable interrupt 18 - - 19 - - 20 - - 21- - Default: 0x0001 *==============================================*/ ACX_REG_INTERRUPT_MASK, /*============================================= Host Interrupt Mask Set 16bit, (Write only) ------------------------------------------ Setting a bit in this register sets the corresponding bin in ACX_HINT_MASK register without effecting the mask state of other bits (0 = no effect). ==============================================*/ ACX_REG_HINT_MASK_SET, /*============================================= Host Interrupt Mask Clear 16bit,(Write only) ------------------------------------------ Setting a bit in this register clears the corresponding bin in ACX_HINT_MASK register without effecting the mask state of other bits (0 = no effect). =============================================*/ ACX_REG_HINT_MASK_CLR, /*============================================= Host Interrupt Status Nondestructive Read 16bit,(Read only) ------------------------------------------ The host can read this register to determine which interrupts are active. Reading this register doesn't effect its content. =============================================*/ ACX_REG_INTERRUPT_NO_CLEAR, /*============================================= Host Interrupt Status Clear on Read Register 16bit,(Read only) ------------------------------------------ The host can read this register to determine which interrupts are active. Reading this register clears it, thus making all interrupts inactive. ==============================================*/ ACX_REG_INTERRUPT_CLEAR, /*============================================= Host Interrupt Acknowledge Register 16bit,(Write only) ------------------------------------------ The host can set individual bits in this register to clear (acknowledge) the corresp. interrupt status bits in the HINT_STS_CLR and HINT_STS_ND registers, thus making the assotiated interrupt inactive. (0-no effect) ==============================================*/ ACX_REG_INTERRUPT_ACK, /*=============================================== Host Software Reset - 32bit RW ------------------------------------------ [31:1] Reserved 0 SOFT_RESET Soft Reset - When this bit is set, it holds the Wlan hardware in a soft reset state. This reset disables all MAC and baseband processor clocks except the CardBus/PCI interface clock. It also initializes all MAC state machines except the host interface. It does not reload the contents of the EEPROM. When this bit is cleared (not self-clearing), the Wlan hardware exits the software reset state. ===============================================*/ ACX_REG_SLV_SOFT_RESET, /*=============================================== EEPROM Burst Read Start - 32bit RW ------------------------------------------ [31:1] Reserved 0 ACX_EE_START - EEPROM Burst Read Start 0 Setting this bit starts a burst read from the external EEPROM. If this bit is set (after reset) before an EEPROM read/write, the burst read starts at EEPROM address 0. Otherwise, it starts at the address following the address of the previous access. TheWlan hardware hardware clears this bit automatically. Default: 0x00000000 *================================================*/ ACX_REG_EE_START, /* Embedded ARM CPU Control */ /*=============================================== Halt eCPU - 32bit RW ------------------------------------------ 0 HALT_ECPU Halt Embedded CPU - This bit is the compliment of bit 1 (MDATA2) in the SOR_CFG register. During a hardware reset, this bit holds the inverse of MDATA2. When downloading firmware from the host, set this bit (pull down MDATA2). The host clears this bit after downloading the firmware into zero-wait-state SSRAM. When loading firmware from Flash, clear this bit (pull up MDATA2) so that the eCPU can run the bootloader code in Flash HALT_ECPU eCPU State -------------------- 1 halt eCPU 0 enable eCPU ===============================================*/ ACX_REG_ECPU_CONTROL, ACX_REG_TABLE_LEN }; #define ACX_SLV_SOFT_RESET_BIT BIT(0) #define ACX_REG_EEPROM_START_BIT BIT(0) /* Command/Information Mailbox Pointers */ /*=============================================== Command Mailbox Pointer - 32bit RW ------------------------------------------ This register holds the start address of the command mailbox located in the Wlan hardware memory. The host must read this pointer after a reset to find the location of the command mailbox. The Wlan hardware initializes the command mailbox pointer with the default address of the command mailbox. The command mailbox pointer is not valid until after the host receives the Init Complete interrupt from the Wlan hardware. ===============================================*/ #define REG_COMMAND_MAILBOX_PTR (SCR_PAD0) /*=============================================== Information Mailbox Pointer - 32bit RW ------------------------------------------ This register holds the start address of the information mailbox located in the Wlan hardware memory. The host must read this pointer after a reset to find the location of the information mailbox. The Wlan hardware initializes the information mailbox pointer with the default address of the information mailbox. The information mailbox pointer is not valid until after the host receives the Init Complete interrupt from the Wlan hardware. ===============================================*/ #define REG_EVENT_MAILBOX_PTR (SCR_PAD1) /* Misc */ #define REG_ENABLE_TX_RX (ENABLE) /* * Rx configuration (filter) information element * --------------------------------------------- */ #define REG_RX_CONFIG (RX_CFG) #define REG_RX_FILTER (RX_FILTER_CFG) #define RX_CFG_ENABLE_PHY_HEADER_PLCP 0x0002 /* promiscuous - receives all valid frames */ #define RX_CFG_PROMISCUOUS 0x0008 /* receives frames from any BSSID */ #define RX_CFG_BSSID 0x0020 /* receives frames destined to any MAC address */ #define RX_CFG_MAC 0x0010 #define RX_CFG_ENABLE_ONLY_MY_DEST_MAC 0x0010 #define RX_CFG_ENABLE_ANY_DEST_MAC 0x0000 #define RX_CFG_ENABLE_ONLY_MY_BSSID 0x0020 #define RX_CFG_ENABLE_ANY_BSSID 0x0000 /* discards all broadcast frames */ #define RX_CFG_DISABLE_BCAST 0x0200 #define RX_CFG_ENABLE_ONLY_MY_SSID 0x0400 #define RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR 0x0800 #define RX_CFG_COPY_RX_STATUS 0x2000 #define RX_CFG_TSF 0x10000 #define RX_CONFIG_OPTION_ANY_DST_MY_BSS (RX_CFG_ENABLE_ANY_DEST_MAC | \ RX_CFG_ENABLE_ONLY_MY_BSSID) #define RX_CONFIG_OPTION_MY_DST_ANY_BSS (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\ | RX_CFG_ENABLE_ANY_BSSID) #define RX_CONFIG_OPTION_ANY_DST_ANY_BSS (RX_CFG_ENABLE_ANY_DEST_MAC | \ RX_CFG_ENABLE_ANY_BSSID) #define RX_CONFIG_OPTION_MY_DST_MY_BSS (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\ | RX_CFG_ENABLE_ONLY_MY_BSSID) #define RX_CONFIG_OPTION_FOR_SCAN (RX_CFG_ENABLE_PHY_HEADER_PLCP \ | RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR \ | RX_CFG_COPY_RX_STATUS | RX_CFG_TSF) #define RX_CONFIG_OPTION_FOR_MEASUREMENT (RX_CFG_ENABLE_ANY_DEST_MAC) #define RX_CONFIG_OPTION_FOR_JOIN (RX_CFG_ENABLE_ONLY_MY_BSSID | \ RX_CFG_ENABLE_ONLY_MY_DEST_MAC) #define RX_CONFIG_OPTION_FOR_IBSS_JOIN (RX_CFG_ENABLE_ONLY_MY_SSID | \ RX_CFG_ENABLE_ONLY_MY_DEST_MAC) #define RX_FILTER_OPTION_DEF (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\ | CFG_RX_CTL_EN | CFG_RX_BCN_EN\ | CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) #define RX_FILTER_OPTION_FILTER_ALL 0 #define RX_FILTER_OPTION_DEF_PRSP_BCN (CFG_RX_PRSP_EN | CFG_RX_MGMT_EN\ | CFG_RX_RCTS_ACK | CFG_RX_BCN_EN) #define RX_FILTER_OPTION_JOIN (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\ | CFG_RX_BCN_EN | CFG_RX_AUTH_EN\ | CFG_RX_ASSOC_EN | CFG_RX_RCTS_ACK\ | CFG_RX_PRSP_EN) /*=============================================== EEPROM Read/Write Request 32bit RW ------------------------------------------ 1 EE_READ - EEPROM Read Request 1 - Setting this bit loads a single byte of data into the EE_DATA register from the EEPROM location specified in the EE_ADDR register. The Wlan hardware hardware clears this bit automatically. EE_DATA is valid when this bit is cleared. 0 EE_WRITE - EEPROM Write Request - Setting this bit writes a single byte of data from the EE_DATA register into the EEPROM location specified in the EE_ADDR register. The Wlan hardware hardware clears this bit automatically. *===============================================*/ #define EE_CTL (REGISTERS_BASE + 0x2000) #define ACX_EE_CTL_REG EE_CTL #define EE_WRITE 0x00000001ul #define EE_READ 0x00000002ul /*=============================================== EEPROM Address - 32bit RW ------------------------------------------ This register specifies the address within the EEPROM from/to which to read/write data. ===============================================*/ #define EE_ADDR (REGISTERS_BASE + 0x2008) #define ACX_EE_ADDR_REG EE_ADDR /*=============================================== EEPROM Data - 32bit RW ------------------------------------------ This register either holds the read 8 bits of data from the EEPROM or the write data to be written to the EEPROM. ===============================================*/ #define EE_DATA (REGISTERS_BASE + 0x2004) #define ACX_EE_DATA_REG EE_DATA #define EEPROM_ACCESS_TO 10000 /* timeout counter */ #define START_EEPROM_MGR 0x00000001 /*=============================================== EEPROM Base Address - 32bit RW ------------------------------------------ This register holds the upper nine bits [23:15] of the 24-bit Wlan hardware memory address for burst reads from EEPROM accesses. The EEPROM provides the lower 15 bits of this address. The MSB of the address from the EEPROM is ignored. ===============================================*/ #define ACX_EE_CFG EE_CFG /*=============================================== GPIO Output Values -32bit, RW ------------------------------------------ [31:16] Reserved [15: 0] Specify the output values (at the output driver inputs) for GPIO[15:0], respectively. ===============================================*/ #define ACX_GPIO_OUT_REG GPIO_OUT #define ACX_MAX_GPIO_LINES 15 /*=============================================== Contention window -32bit, RW ------------------------------------------ [31:26] Reserved [25:16] Max (0x3ff) [15:07] Reserved [06:00] Current contention window value - default is 0x1F ===============================================*/ #define ACX_CONT_WIND_CFG_REG CONT_WIND_CFG #define ACX_CONT_WIND_MIN_MASK 0x0000007f #define ACX_CONT_WIND_MAX 0x03ff0000 /*=============================================== HI_CFG Interface Configuration Register Values ------------------------------------------ ===============================================*/ #define HI_CFG_UART_ENABLE 0x00000004 #define HI_CFG_RST232_ENABLE 0x00000008 #define HI_CFG_CLOCK_REQ_SELECT 0x00000010 #define HI_CFG_HOST_INT_ENABLE 0x00000020 #define HI_CFG_VLYNQ_OUTPUT_ENABLE 0x00000040 #define HI_CFG_HOST_INT_ACTIVE_LOW 0x00000080 #define HI_CFG_UART_TX_OUT_GPIO_15 0x00000100 #define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200 #define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400 /* * NOTE: USE_ACTIVE_HIGH compilation flag should be defined in makefile * for platforms using active high interrupt level */ #ifdef USE_ACTIVE_HIGH #define HI_CFG_DEF_VAL \ (HI_CFG_UART_ENABLE | \ HI_CFG_RST232_ENABLE | \ HI_CFG_CLOCK_REQ_SELECT | \ HI_CFG_HOST_INT_ENABLE) #else #define HI_CFG_DEF_VAL \ (HI_CFG_UART_ENABLE | \ HI_CFG_RST232_ENABLE | \ HI_CFG_CLOCK_REQ_SELECT | \ HI_CFG_HOST_INT_ENABLE) #endif #define REF_FREQ_19_2 0 #define REF_FREQ_26_0 1 #define REF_FREQ_38_4 2 #define REF_FREQ_40_0 3 #define REF_FREQ_33_6 4 #define REF_FREQ_NUM 5 #define LUT_PARAM_INTEGER_DIVIDER 0 #define LUT_PARAM_FRACTIONAL_DIVIDER 1 #define LUT_PARAM_ATTN_BB 2 #define LUT_PARAM_ALPHA_BB 3 #define LUT_PARAM_STOP_TIME_BB 4 #define LUT_PARAM_BB_PLL_LOOP_FILTER 5 #define LUT_PARAM_NUM 6 #define ACX_EEPROMLESS_IND_REG (SCR_PAD4) #define USE_EEPROM 0 #define SOFT_RESET_MAX_TIME 1000000 #define SOFT_RESET_STALL_TIME 1000 #define NVS_DATA_BUNDARY_ALIGNMENT 4 /* Firmware image load chunk size */ #define CHUNK_SIZE 512 /* Firmware image header size */ #define FW_HDR_SIZE 8 #define ECPU_CONTROL_HALT 0x00000101 /****************************************************************************** CHANNELS, BAND & REG DOMAINS definitions ******************************************************************************/ enum { RADIO_BAND_2_4GHZ = 0, /* 2.4 Ghz band */ RADIO_BAND_5GHZ = 1, /* 5 Ghz band */ RADIO_BAND_JAPAN_4_9_GHZ = 2, DEFAULT_BAND = RADIO_BAND_2_4GHZ, INVALID_BAND = 0xFE, MAX_RADIO_BANDS = 0xFF }; enum { NO_RATE = 0, RATE_1MBPS = 0x0A, RATE_2MBPS = 0x14, RATE_5_5MBPS = 0x37, RATE_6MBPS = 0x0B, RATE_9MBPS = 0x0F, RATE_11MBPS = 0x6E, RATE_12MBPS = 0x0A, RATE_18MBPS = 0x0E, RATE_22MBPS = 0xDC, RATE_24MBPS = 0x09, RATE_36MBPS = 0x0D, RATE_48MBPS = 0x08, RATE_54MBPS = 0x0C }; enum { RATE_INDEX_1MBPS = 0, RATE_INDEX_2MBPS = 1, RATE_INDEX_5_5MBPS = 2, RATE_INDEX_6MBPS = 3, RATE_INDEX_9MBPS = 4, RATE_INDEX_11MBPS = 5, RATE_INDEX_12MBPS = 6, RATE_INDEX_18MBPS = 7, RATE_INDEX_22MBPS = 8, RATE_INDEX_24MBPS = 9, RATE_INDEX_36MBPS = 10, RATE_INDEX_48MBPS = 11, RATE_INDEX_54MBPS = 12, RATE_INDEX_MAX = RATE_INDEX_54MBPS, MAX_RATE_INDEX, INVALID_RATE_INDEX = MAX_RATE_INDEX, RATE_INDEX_ENUM_MAX_SIZE = 0x7FFFFFFF }; enum { RATE_MASK_1MBPS = 0x1, RATE_MASK_2MBPS = 0x2, RATE_MASK_5_5MBPS = 0x4, RATE_MASK_11MBPS = 0x20, }; #define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */ #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) enum { CCK_LONG = 0, CCK_SHORT = SHORT_PREAMBLE_BIT, PBCC_LONG = PBCC_RATE_BIT, PBCC_SHORT = PBCC_RATE_BIT | SHORT_PREAMBLE_BIT, OFDM = OFDM_RATE_BIT }; /****************************************************************************** Transmit-Descriptor RATE-SET field definitions... Define a new "Rate-Set" for TX path that incorporates the Rate & Modulation info into a single 16-bit field. TxdRateSet_t: b15 - Indicates Preamble type (1=SHORT, 0=LONG). Notes: Must be LONG (0) for 1Mbps rate. Does not apply (set to 0) for RevG-OFDM rates. b14 - Indicates PBCC encoding (1=PBCC, 0=not). Notes: Does not apply (set to 0) for rates 1 and 2 Mbps. Does not apply (set to 0) for RevG-OFDM rates. b13 - Unused (set to 0). b12-b0 - Supported Rate indicator bits as defined below. ******************************************************************************/ /************************************************************************* Interrupt Trigger Register (Host -> WiLink) **************************************************************************/ /* Hardware to Embedded CPU Interrupts - first 32-bit register set */ /* * Host Command Interrupt. Setting this bit masks * the interrupt that the host issues to inform * the FW that it has sent a command * to the Wlan hardware Command Mailbox. */ #define INTR_TRIG_CMD BIT(0) /* * Host Event Acknowlegde Interrupt. The host * sets this bit to acknowledge that it received * the unsolicited information from the event * mailbox. */ #define INTR_TRIG_EVENT_ACK BIT(1) /* * The host sets this bit to inform the Wlan * FW that a TX packet is in the XFER * Buffer #0. */ #define INTR_TRIG_TX_PROC0 BIT(2) /* * The host sets this bit to inform the FW * that it read a packet from RX XFER * Buffer #0. */ #define INTR_TRIG_RX_PROC0 BIT(3) #define INTR_TRIG_DEBUG_ACK BIT(4) #define INTR_TRIG_STATE_CHANGED BIT(5) /* Hardware to Embedded CPU Interrupts - second 32-bit register set */ /* * The host sets this bit to inform the FW * that it read a packet from RX XFER * Buffer #1. */ #define INTR_TRIG_RX_PROC1 BIT(17) /* * The host sets this bit to inform the Wlan * hardware that a TX packet is in the XFER * Buffer #1. */ #define INTR_TRIG_TX_PROC1 BIT(18) #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/ps.h0000644000175000017500000000214612026211315022626 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_PS_H__ #define __WL1251_PS_H__ #include "wl1251.h" #include "acx.h" int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode); void wl1251_ps_elp_sleep(struct wl1251 *wl); int wl1251_ps_elp_wakeup(struct wl1251 *wl); void wl1251_elp_work(struct work_struct *work); #endif /* __WL1251_PS_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/ps.c0000644000175000017500000001027412026211315022622 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "reg.h" #include "ps.h" #include "cmd.h" #include "io.h" /* in ms */ #define WL1251_WAKEUP_TIMEOUT 100 void wl1251_elp_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1251 *wl; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1251, elp_work); wl1251_debug(DEBUG_PSM, "elp work"); mutex_lock(&wl->mutex); if (wl->elp || wl->station_mode == STATION_ACTIVE_MODE) goto out; wl1251_debug(DEBUG_PSM, "chip to elp"); wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); wl->elp = true; out: mutex_unlock(&wl->mutex); } #define ELP_ENTRY_DELAY 5 /* Routines to toggle sleep mode while in ELP */ void wl1251_ps_elp_sleep(struct wl1251 *wl) { unsigned long delay; if (wl->station_mode != STATION_ACTIVE_MODE) { delay = msecs_to_jiffies(ELP_ENTRY_DELAY); ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay); } } int wl1251_ps_elp_wakeup(struct wl1251 *wl) { unsigned long timeout, start; u32 elp_reg; if (delayed_work_pending(&wl->elp_work)) cancel_delayed_work(&wl->elp_work); if (!wl->elp) return 0; wl1251_debug(DEBUG_PSM, "waking up chip from elp"); start = jiffies; timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT); wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); /* * FIXME: we should wait for irq from chip but, as a temporary * solution to simplify locking, let's poll instead */ while (!(elp_reg & ELPCTRL_WLAN_READY)) { if (time_after(jiffies, timeout)) { wl1251_error("elp wakeup timeout"); return -ETIMEDOUT; } msleep(1); elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); } wl1251_debug(DEBUG_PSM, "wakeup time: %u ms", jiffies_to_msecs(jiffies - start)); wl->elp = false; return 0; } int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode) { int ret; switch (mode) { case STATION_POWER_SAVE_MODE: wl1251_debug(DEBUG_PSM, "entering psm"); /* enable beacon filtering */ ret = wl1251_acx_beacon_filter_opt(wl, true); if (ret < 0) return ret; ret = wl1251_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, wl->listen_int); if (ret < 0) return ret; ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_ENABLE, WL1251_DEFAULT_BET_CONSECUTIVE); if (ret < 0) return ret; ret = wl1251_cmd_ps_mode(wl, CHIP_POWER_SAVE_MODE); if (ret < 0) return ret; ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP); if (ret < 0) return ret; break; case STATION_IDLE: wl1251_debug(DEBUG_PSM, "entering idle"); ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP); if (ret < 0) return ret; ret = wl1251_cmd_template_set(wl, CMD_DISCONNECT, NULL, 0); if (ret < 0) return ret; break; case STATION_ACTIVE_MODE: default: wl1251_debug(DEBUG_PSM, "leaving psm"); ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM); if (ret < 0) return ret; /* disable BET */ ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_DISABLE, WL1251_DEFAULT_BET_CONSECUTIVE); if (ret < 0) return ret; /* disable beacon filtering */ ret = wl1251_acx_beacon_filter_opt(wl, false); if (ret < 0) return ret; ret = wl1251_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, wl->listen_int); if (ret < 0) return ret; ret = wl1251_cmd_ps_mode(wl, CHIP_ACTIVE_MODE); if (ret < 0) return ret; break; } wl->station_mode = mode; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/io.h0000644000175000017500000000473612026211315022622 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_IO_H__ #define __WL1251_IO_H__ #include "wl1251.h" #define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 #define HW_ACCESS_PART0_SIZE_ADDR 0x1FFC0 #define HW_ACCESS_PART0_START_ADDR 0x1FFC4 #define HW_ACCESS_PART1_SIZE_ADDR 0x1FFC8 #define HW_ACCESS_PART1_START_ADDR 0x1FFCC #define HW_ACCESS_REGISTER_SIZE 4 #define HW_ACCESS_PRAM_MAX_RANGE 0x3c000 static inline u32 wl1251_read32(struct wl1251 *wl, int addr) { wl->if_ops->read(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32)); return le32_to_cpu(wl->buffer_32); } static inline void wl1251_write32(struct wl1251 *wl, int addr, u32 val) { wl->buffer_32 = cpu_to_le32(val); wl->if_ops->write(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32)); } static inline u32 wl1251_read_elp(struct wl1251 *wl, int addr) { u32 response; if (wl->if_ops->read_elp) wl->if_ops->read_elp(wl, addr, &response); else wl->if_ops->read(wl, addr, &response, sizeof(u32)); return response; } static inline void wl1251_write_elp(struct wl1251 *wl, int addr, u32 val) { if (wl->if_ops->write_elp) wl->if_ops->write_elp(wl, addr, val); else wl->if_ops->write(wl, addr, &val, sizeof(u32)); } /* Memory target IO, address is translated to partition 0 */ void wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len); void wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len); u32 wl1251_mem_read32(struct wl1251 *wl, int addr); void wl1251_mem_write32(struct wl1251 *wl, int addr, u32 val); /* Registers IO */ u32 wl1251_reg_read32(struct wl1251 *wl, int addr); void wl1251_reg_write32(struct wl1251 *wl, int addr, u32 val); void wl1251_set_partition(struct wl1251 *wl, u32 part_start, u32 part_size, u32 reg_start, u32 reg_size); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/io.c0000644000175000017500000001514412026211315022610 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "wl1251.h" #include "reg.h" #include "io.h" /* FIXME: this is static data nowadays and the table can be removed */ static enum wl12xx_acx_int_reg wl1251_io_reg_table[ACX_REG_TABLE_LEN] = { [ACX_REG_INTERRUPT_TRIG] = (REGISTERS_BASE + 0x0474), [ACX_REG_INTERRUPT_TRIG_H] = (REGISTERS_BASE + 0x0478), [ACX_REG_INTERRUPT_MASK] = (REGISTERS_BASE + 0x0494), [ACX_REG_HINT_MASK_SET] = (REGISTERS_BASE + 0x0498), [ACX_REG_HINT_MASK_CLR] = (REGISTERS_BASE + 0x049C), [ACX_REG_INTERRUPT_NO_CLEAR] = (REGISTERS_BASE + 0x04B0), [ACX_REG_INTERRUPT_CLEAR] = (REGISTERS_BASE + 0x04A4), [ACX_REG_INTERRUPT_ACK] = (REGISTERS_BASE + 0x04A8), [ACX_REG_SLV_SOFT_RESET] = (REGISTERS_BASE + 0x0000), [ACX_REG_EE_START] = (REGISTERS_BASE + 0x080C), [ACX_REG_ECPU_CONTROL] = (REGISTERS_BASE + 0x0804) }; static int wl1251_translate_reg_addr(struct wl1251 *wl, int addr) { /* If the address is lower than REGISTERS_BASE, it means that this is * a chip-specific register address, so look it up in the registers * table */ if (addr < REGISTERS_BASE) { /* Make sure we don't go over the table */ if (addr >= ACX_REG_TABLE_LEN) { wl1251_error("address out of range (%d)", addr); return -EINVAL; } addr = wl1251_io_reg_table[addr]; } return addr - wl->physical_reg_addr + wl->virtual_reg_addr; } static int wl1251_translate_mem_addr(struct wl1251 *wl, int addr) { return addr - wl->physical_mem_addr + wl->virtual_mem_addr; } void wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len) { int physical; physical = wl1251_translate_mem_addr(wl, addr); wl->if_ops->read(wl, physical, buf, len); } void wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len) { int physical; physical = wl1251_translate_mem_addr(wl, addr); wl->if_ops->write(wl, physical, buf, len); } u32 wl1251_mem_read32(struct wl1251 *wl, int addr) { return wl1251_read32(wl, wl1251_translate_mem_addr(wl, addr)); } void wl1251_mem_write32(struct wl1251 *wl, int addr, u32 val) { wl1251_write32(wl, wl1251_translate_mem_addr(wl, addr), val); } u32 wl1251_reg_read32(struct wl1251 *wl, int addr) { return wl1251_read32(wl, wl1251_translate_reg_addr(wl, addr)); } void wl1251_reg_write32(struct wl1251 *wl, int addr, u32 val) { wl1251_write32(wl, wl1251_translate_reg_addr(wl, addr), val); } /* Set the partitions to access the chip addresses. * * There are two VIRTUAL partitions (the memory partition and the * registers partition), which are mapped to two different areas of the * PHYSICAL (hardware) memory. This function also makes other checks to * ensure that the partitions are not overlapping. In the diagram below, the * memory partition comes before the register partition, but the opposite is * also supported. * * PHYSICAL address * space * * | | * ...+----+--> mem_start * VIRTUAL address ... | | * space ... | | [PART_0] * ... | | * 0x00000000 <--+----+... ...+----+--> mem_start + mem_size * | | ... | | * |MEM | ... | | * | | ... | | * part_size <--+----+... | | {unused area) * | | ... | | * |REG | ... | | * part_size | | ... | | * + <--+----+... ...+----+--> reg_start * reg_size ... | | * ... | | [PART_1] * ... | | * ...+----+--> reg_start + reg_size * | | * */ void wl1251_set_partition(struct wl1251 *wl, u32 mem_start, u32 mem_size, u32 reg_start, u32 reg_size) { struct wl1251_partition partition[2]; wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); /* Make sure that the two partitions together don't exceed the * address range */ if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) { wl1251_debug(DEBUG_SPI, "Total size exceeds maximum virtual" " address range. Truncating partition[0]."); mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size; wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); } if ((mem_start < reg_start) && ((mem_start + mem_size) > reg_start)) { /* Guarantee that the memory partition doesn't overlap the * registers partition */ wl1251_debug(DEBUG_SPI, "End of partition[0] is " "overlapping partition[1]. Adjusted."); mem_size = reg_start - mem_start; wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); } else if ((reg_start < mem_start) && ((reg_start + reg_size) > mem_start)) { /* Guarantee that the register partition doesn't overlap the * memory partition */ wl1251_debug(DEBUG_SPI, "End of partition[1] is" " overlapping partition[0]. Adjusted."); reg_size = mem_start - reg_start; wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); } partition[0].start = mem_start; partition[0].size = mem_size; partition[1].start = reg_start; partition[1].size = reg_size; wl->physical_mem_addr = mem_start; wl->physical_reg_addr = reg_start; wl->virtual_mem_addr = 0; wl->virtual_reg_addr = mem_size; wl->if_ops->write(wl, HW_ACCESS_PART0_SIZE_ADDR, partition, sizeof(partition)); } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/init.h0000644000175000017500000000424612026211315023152 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_INIT_H__ #define __WL1251_INIT_H__ #include "wl1251.h" enum { /* best effort/legacy */ AC_BE = 0, /* background */ AC_BK = 1, /* video */ AC_VI = 2, /* voice */ AC_VO = 3, /* broadcast dummy access category */ AC_BCAST = 4, NUM_ACCESS_CATEGORIES = 4 }; /* following are defult values for the IE fields*/ #define CWMIN_BK 15 #define CWMIN_BE 15 #define CWMIN_VI 7 #define CWMIN_VO 3 #define CWMAX_BK 1023 #define CWMAX_BE 63 #define CWMAX_VI 15 #define CWMAX_VO 7 /* slot number setting to start transmission at PIFS interval */ #define AIFS_PIFS 1 /* * slot number setting to start transmission at DIFS interval - normal DCF * access */ #define AIFS_DIFS 2 #define AIFSN_BK 7 #define AIFSN_BE 3 #define AIFSN_VI AIFS_PIFS #define AIFSN_VO AIFS_PIFS #define TXOP_BK 0 #define TXOP_BE 0 #define TXOP_VI 3008 #define TXOP_VO 1504 int wl1251_hw_init_hwenc_config(struct wl1251 *wl); int wl1251_hw_init_templates_config(struct wl1251 *wl); int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter); int wl1251_hw_init_phy_config(struct wl1251 *wl); int wl1251_hw_init_beacon_filter(struct wl1251 *wl); int wl1251_hw_init_pta(struct wl1251 *wl); int wl1251_hw_init_energy_detection(struct wl1251 *wl); int wl1251_hw_init_beacon_broadcast(struct wl1251 *wl); int wl1251_hw_init_power_auth(struct wl1251 *wl); int wl1251_hw_init_mem_config(struct wl1251 *wl); int wl1251_hw_init(struct wl1251 *wl); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/init.c0000644000175000017500000002172112026211315023142 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include "init.h" #include "wl12xx_80211.h" #include "acx.h" #include "cmd.h" #include "reg.h" int wl1251_hw_init_hwenc_config(struct wl1251 *wl) { int ret; ret = wl1251_acx_feature_cfg(wl); if (ret < 0) { wl1251_warning("couldn't set feature config"); return ret; } ret = wl1251_acx_default_key(wl, wl->default_key); if (ret < 0) { wl1251_warning("couldn't set default key"); return ret; } return 0; } int wl1251_hw_init_templates_config(struct wl1251 *wl) { int ret; u8 partial_vbm[PARTIAL_VBM_MAX]; /* send empty templates for fw memory reservation */ ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, NULL, sizeof(struct wl12xx_probe_req_template)); if (ret < 0) return ret; ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, NULL, sizeof(struct wl12xx_null_data_template)); if (ret < 0) return ret; ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, NULL, sizeof(struct wl12xx_ps_poll_template)); if (ret < 0) return ret; ret = wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL, sizeof (struct wl12xx_qos_null_data_template)); if (ret < 0) return ret; ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, NULL, sizeof (struct wl12xx_probe_resp_template)); if (ret < 0) return ret; ret = wl1251_cmd_template_set(wl, CMD_BEACON, NULL, sizeof (struct wl12xx_beacon_template)); if (ret < 0) return ret; /* tim templates, first reserve space then allocate an empty one */ memset(partial_vbm, 0, PARTIAL_VBM_MAX); ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0); if (ret < 0) return ret; ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0); if (ret < 0) return ret; return 0; } int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter) { int ret; ret = wl1251_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); if (ret < 0) return ret; ret = wl1251_acx_rx_config(wl, config, filter); if (ret < 0) return ret; return 0; } int wl1251_hw_init_phy_config(struct wl1251 *wl) { int ret; ret = wl1251_acx_pd_threshold(wl); if (ret < 0) return ret; ret = wl1251_acx_slot(wl, DEFAULT_SLOT_TIME); if (ret < 0) return ret; ret = wl1251_acx_group_address_tbl(wl); if (ret < 0) return ret; ret = wl1251_acx_service_period_timeout(wl); if (ret < 0) return ret; ret = wl1251_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); if (ret < 0) return ret; return 0; } int wl1251_hw_init_beacon_filter(struct wl1251 *wl) { int ret; /* disable beacon filtering at this stage */ ret = wl1251_acx_beacon_filter_opt(wl, false); if (ret < 0) return ret; ret = wl1251_acx_beacon_filter_table(wl); if (ret < 0) return ret; return 0; } int wl1251_hw_init_pta(struct wl1251 *wl) { int ret; ret = wl1251_acx_sg_enable(wl); if (ret < 0) return ret; ret = wl1251_acx_sg_cfg(wl); if (ret < 0) return ret; return 0; } int wl1251_hw_init_energy_detection(struct wl1251 *wl) { int ret; ret = wl1251_acx_cca_threshold(wl); if (ret < 0) return ret; return 0; } int wl1251_hw_init_beacon_broadcast(struct wl1251 *wl) { int ret; ret = wl1251_acx_bcn_dtim_options(wl); if (ret < 0) return ret; return 0; } int wl1251_hw_init_power_auth(struct wl1251 *wl) { return wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM); } int wl1251_hw_init_mem_config(struct wl1251 *wl) { int ret; ret = wl1251_acx_mem_cfg(wl); if (ret < 0) return ret; wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map), GFP_KERNEL); if (!wl->target_mem_map) { wl1251_error("couldn't allocate target memory map"); return -ENOMEM; } /* we now ask for the firmware built memory map */ ret = wl1251_acx_mem_map(wl, wl->target_mem_map, sizeof(struct wl1251_acx_mem_map)); if (ret < 0) { wl1251_error("couldn't retrieve firmware memory map"); kfree(wl->target_mem_map); wl->target_mem_map = NULL; return ret; } return 0; } static int wl1251_hw_init_txq_fill(u8 qid, struct acx_tx_queue_qos_config *config, u32 num_blocks) { config->qid = qid; switch (qid) { case QOS_AC_BE: config->high_threshold = (QOS_TX_HIGH_BE_DEF * num_blocks) / 100; config->low_threshold = (QOS_TX_LOW_BE_DEF * num_blocks) / 100; break; case QOS_AC_BK: config->high_threshold = (QOS_TX_HIGH_BK_DEF * num_blocks) / 100; config->low_threshold = (QOS_TX_LOW_BK_DEF * num_blocks) / 100; break; case QOS_AC_VI: config->high_threshold = (QOS_TX_HIGH_VI_DEF * num_blocks) / 100; config->low_threshold = (QOS_TX_LOW_VI_DEF * num_blocks) / 100; break; case QOS_AC_VO: config->high_threshold = (QOS_TX_HIGH_VO_DEF * num_blocks) / 100; config->low_threshold = (QOS_TX_LOW_VO_DEF * num_blocks) / 100; break; default: wl1251_error("Invalid TX queue id: %d", qid); return -EINVAL; } return 0; } static int wl1251_hw_init_tx_queue_config(struct wl1251 *wl) { struct acx_tx_queue_qos_config *config; struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map; int ret, i; wl1251_debug(DEBUG_ACX, "acx tx queue config"); config = kzalloc(sizeof(*config), GFP_KERNEL); if (!config) { ret = -ENOMEM; goto out; } for (i = 0; i < MAX_NUM_OF_AC; i++) { ret = wl1251_hw_init_txq_fill(i, config, wl_mem_map->num_tx_mem_blocks); if (ret < 0) goto out; ret = wl1251_cmd_configure(wl, ACX_TX_QUEUE_CFG, config, sizeof(*config)); if (ret < 0) goto out; } wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE); wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK); wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI); wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO); out: kfree(config); return ret; } static int wl1251_hw_init_data_path_config(struct wl1251 *wl) { int ret; /* asking for the data path parameters */ wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp), GFP_KERNEL); if (!wl->data_path) { wl1251_error("Couldnt allocate data path parameters"); return -ENOMEM; } ret = wl1251_acx_data_path_params(wl, wl->data_path); if (ret < 0) { kfree(wl->data_path); wl->data_path = NULL; return ret; } return 0; } int wl1251_hw_init(struct wl1251 *wl) { struct wl1251_acx_mem_map *wl_mem_map; int ret; ret = wl1251_hw_init_hwenc_config(wl); if (ret < 0) return ret; /* Template settings */ ret = wl1251_hw_init_templates_config(wl); if (ret < 0) return ret; /* Default memory configuration */ ret = wl1251_hw_init_mem_config(wl); if (ret < 0) return ret; /* Default data path configuration */ ret = wl1251_hw_init_data_path_config(wl); if (ret < 0) goto out_free_memmap; /* RX config */ ret = wl1251_hw_init_rx_config(wl, RX_CFG_PROMISCUOUS | RX_CFG_TSF, RX_FILTER_OPTION_DEF); /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, RX_FILTER_OPTION_FILTER_ALL); */ if (ret < 0) goto out_free_data_path; /* TX queues config */ ret = wl1251_hw_init_tx_queue_config(wl); if (ret < 0) goto out_free_data_path; /* PHY layer config */ ret = wl1251_hw_init_phy_config(wl); if (ret < 0) goto out_free_data_path; /* Initialize connection monitoring thresholds */ ret = wl1251_acx_conn_monit_params(wl); if (ret < 0) goto out_free_data_path; /* Beacon filtering */ ret = wl1251_hw_init_beacon_filter(wl); if (ret < 0) goto out_free_data_path; /* Bluetooth WLAN coexistence */ ret = wl1251_hw_init_pta(wl); if (ret < 0) goto out_free_data_path; /* Energy detection */ ret = wl1251_hw_init_energy_detection(wl); if (ret < 0) goto out_free_data_path; /* Beacons and boradcast settings */ ret = wl1251_hw_init_beacon_broadcast(wl); if (ret < 0) goto out_free_data_path; /* Enable data path */ ret = wl1251_cmd_data_path(wl, wl->channel, 1); if (ret < 0) goto out_free_data_path; /* Default power state */ ret = wl1251_hw_init_power_auth(wl); if (ret < 0) goto out_free_data_path; wl_mem_map = wl->target_mem_map; wl1251_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x", wl_mem_map->num_tx_mem_blocks, wl->data_path->tx_control_addr, wl_mem_map->num_rx_mem_blocks, wl->data_path->rx_control_addr); return 0; out_free_data_path: kfree(wl->data_path); out_free_memmap: kfree(wl->target_mem_map); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/event.h0000644000175000017500000000762212026211315023331 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_EVENT_H__ #define __WL1251_EVENT_H__ /* * Mbox events * * The event mechanism is based on a pair of event buffers (buffers A and * B) at fixed locations in the target's memory. The host processes one * buffer while the other buffer continues to collect events. If the host * is not processing events, an interrupt is issued to signal that a buffer * is ready. Once the host is done with processing events from one buffer, * it signals the target (with an ACK interrupt) that the event buffer is * free. */ enum { RESERVED1_EVENT_ID = BIT(0), RESERVED2_EVENT_ID = BIT(1), MEASUREMENT_START_EVENT_ID = BIT(2), SCAN_COMPLETE_EVENT_ID = BIT(3), CALIBRATION_COMPLETE_EVENT_ID = BIT(4), ROAMING_TRIGGER_LOW_RSSI_EVENT_ID = BIT(5), PS_REPORT_EVENT_ID = BIT(6), SYNCHRONIZATION_TIMEOUT_EVENT_ID = BIT(7), HEALTH_REPORT_EVENT_ID = BIT(8), ACI_DETECTION_EVENT_ID = BIT(9), DEBUG_REPORT_EVENT_ID = BIT(10), MAC_STATUS_EVENT_ID = BIT(11), DISCONNECT_EVENT_COMPLETE_ID = BIT(12), JOIN_EVENT_COMPLETE_ID = BIT(13), CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(14), BSS_LOSE_EVENT_ID = BIT(15), ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(16), MEASUREMENT_COMPLETE_EVENT_ID = BIT(17), AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(18), SCHEDULED_SCAN_COMPLETE_EVENT_ID = BIT(19), PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(20), RESET_BSS_EVENT_ID = BIT(21), REGAINED_BSS_EVENT_ID = BIT(22), ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID = BIT(23), ROAMING_TRIGGER_LOW_SNR_EVENT_ID = BIT(24), ROAMING_TRIGGER_REGAINED_SNR_EVENT_ID = BIT(25), DBG_EVENT_ID = BIT(26), BT_PTA_SENSE_EVENT_ID = BIT(27), BT_PTA_PREDICTION_EVENT_ID = BIT(28), BT_PTA_AVALANCHE_EVENT_ID = BIT(29), PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(30), EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, }; struct event_debug_report { u8 debug_event_id; u8 num_params; u16 pad; u32 report_1; u32 report_2; u32 report_3; } __packed; struct event_mailbox { u32 events_vector; u32 events_mask; u32 reserved_1; u32 reserved_2; char average_rssi_level; u8 ps_status; u8 channel_switch_status; u8 scheduled_scan_status; /* Channels scanned by the scheduled scan */ u16 scheduled_scan_channels; /* If bit 0 is set -> target's fatal error */ u16 health_report; u16 bad_fft_counter; u8 bt_pta_sense_info; u8 bt_pta_protective_info; u32 reserved; u32 debug_report[2]; /* Number of FCS errors since last event */ u32 fcs_err_counter; struct event_debug_report report; u8 average_snr_level; u8 padding[19]; } __packed; int wl1251_event_unmask(struct wl1251 *wl); void wl1251_event_mbox_config(struct wl1251 *wl); int wl1251_event_handle(struct wl1251 *wl, u8 mbox); int wl1251_event_wait(struct wl1251 *wl, u32 mask, int timeout_ms); #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/event.c0000644000175000017500000001115412026211315023317 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "wl1251.h" #include "reg.h" #include "io.h" #include "event.h" #include "ps.h" static int wl1251_event_scan_complete(struct wl1251 *wl, struct event_mailbox *mbox) { wl1251_debug(DEBUG_EVENT, "status: 0x%x, channels: %d", mbox->scheduled_scan_status, mbox->scheduled_scan_channels); if (wl->scanning) { ieee80211_scan_completed(wl->hw, false); wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan completed"); wl->scanning = false; } return 0; } static void wl1251_event_mbox_dump(struct event_mailbox *mbox) { wl1251_debug(DEBUG_EVENT, "MBOX DUMP:"); wl1251_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); wl1251_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); } static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) { int ret; u32 vector; wl1251_event_mbox_dump(mbox); vector = mbox->events_vector & ~(mbox->events_mask); wl1251_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { ret = wl1251_event_scan_complete(wl, mbox); if (ret < 0) return ret; } if (vector & BSS_LOSE_EVENT_ID) { wl1251_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); if (wl->psm_requested && wl->station_mode != STATION_ACTIVE_MODE) { ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) return ret; } } if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) { wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); /* indicate to the stack, that beacons have been lost */ ieee80211_beacon_loss(wl->vif); } if (vector & REGAINED_BSS_EVENT_ID) { if (wl->psm_requested) { ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); if (ret < 0) return ret; } } if (wl->vif && wl->rssi_thold) { if (vector & ROAMING_TRIGGER_LOW_RSSI_EVENT_ID) { wl1251_debug(DEBUG_EVENT, "ROAMING_TRIGGER_LOW_RSSI_EVENT"); ieee80211_cqm_rssi_notify(wl->vif, NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, GFP_KERNEL); } if (vector & ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID) { wl1251_debug(DEBUG_EVENT, "ROAMING_TRIGGER_REGAINED_RSSI_EVENT"); ieee80211_cqm_rssi_notify(wl->vif, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, GFP_KERNEL); } } return 0; } /* * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1251_EVENT_TIMEOUT in msecs) */ int wl1251_event_wait(struct wl1251 *wl, u32 mask, int timeout_ms) { u32 events_vector, event; unsigned long timeout; timeout = jiffies + msecs_to_jiffies(timeout_ms); do { if (time_after(jiffies, timeout)) return -ETIMEDOUT; msleep(1); /* read from both event fields */ wl1251_mem_read(wl, wl->mbox_ptr[0], &events_vector, sizeof(events_vector)); event = events_vector & mask; wl1251_mem_read(wl, wl->mbox_ptr[1], &events_vector, sizeof(events_vector)); event |= events_vector & mask; } while (!event); return 0; } int wl1251_event_unmask(struct wl1251 *wl) { int ret; ret = wl1251_acx_event_mbox_mask(wl, ~(wl->event_mask)); if (ret < 0) return ret; return 0; } void wl1251_event_mbox_config(struct wl1251 *wl) { wl->mbox_ptr[0] = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl1251_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); } int wl1251_event_handle(struct wl1251 *wl, u8 mbox_num) { struct event_mailbox mbox; int ret; wl1251_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); if (mbox_num > 1) return -EINVAL; /* first we read the mbox descriptor */ wl1251_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, sizeof(struct event_mailbox)); /* process the descriptor */ ret = wl1251_event_process(wl, &mbox); if (ret < 0) return ret; /* then we let the firmware know it can go on...*/ wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/debugfs.h0000644000175000017500000000172012026211315023620 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef WL1251_DEBUGFS_H #define WL1251_DEBUGFS_H #include "wl1251.h" int wl1251_debugfs_init(struct wl1251 *wl); void wl1251_debugfs_exit(struct wl1251 *wl); void wl1251_debugfs_reset(struct wl1251 *wl); #endif /* WL1251_DEBUGFS_H */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/debugfs.c0000644000175000017500000004173012026211315023620 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "debugfs.h" #include #include #include "wl1251.h" #include "acx.h" #include "ps.h" /* ms */ #define WL1251_DEBUGFS_STATS_LIFETIME 1000 /* debugfs macros idea from mac80211 */ #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct wl1251 *wl = file->private_data; \ char buf[buflen]; \ int res; \ \ res = scnprintf(buf, buflen, fmt "\n", ##value); \ return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ } \ \ static const struct file_operations name## _ops = { \ .read = name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_ADD(name, parent) \ wl->debugfs.name = debugfs_create_file(#name, 0400, parent, \ wl, &name## _ops); \ if (IS_ERR(wl->debugfs.name)) { \ ret = PTR_ERR(wl->debugfs.name); \ wl->debugfs.name = NULL; \ goto out; \ } #define DEBUGFS_DEL(name) \ do { \ debugfs_remove(wl->debugfs.name); \ wl->debugfs.name = NULL; \ } while (0) #define DEBUGFS_FWSTATS_FILE(sub, name, buflen, fmt) \ static ssize_t sub## _ ##name## _read(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct wl1251 *wl = file->private_data; \ char buf[buflen]; \ int res; \ \ wl1251_debugfs_update_stats(wl); \ \ res = scnprintf(buf, buflen, fmt "\n", \ wl->stats.fw_stats->sub.name); \ return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ } \ \ static const struct file_operations sub## _ ##name## _ops = { \ .read = sub## _ ##name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_FWSTATS_ADD(sub, name) \ DEBUGFS_ADD(sub## _ ##name, wl->debugfs.fw_statistics) #define DEBUGFS_FWSTATS_DEL(sub, name) \ DEBUGFS_DEL(sub## _ ##name) static void wl1251_debugfs_update_stats(struct wl1251 *wl) { int ret; mutex_lock(&wl->mutex); ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; if (wl->state == WL1251_STATE_ON && time_after(jiffies, wl->stats.fw_stats_update + msecs_to_jiffies(WL1251_DEBUGFS_STATS_LIFETIME))) { wl1251_acx_statistics(wl, wl->stats.fw_stats); wl->stats.fw_stats_update = jiffies; } wl1251_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, out_of_mem, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, hw_stuck, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, dropped, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, fcs_err, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, path_reset, 20, "%u"); DEBUGFS_FWSTATS_FILE(rx, reset_counter, 20, "%u"); DEBUGFS_FWSTATS_FILE(dma, rx_requested, 20, "%u"); DEBUGFS_FWSTATS_FILE(dma, rx_errors, 20, "%u"); DEBUGFS_FWSTATS_FILE(dma, tx_requested, 20, "%u"); DEBUGFS_FWSTATS_FILE(dma, tx_errors, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, fiqs, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, rx_headers, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, rx_rdys, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, irqs, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, tx_procs, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, decrypt_done, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, dma0_done, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, dma1_done, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, commands, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, rx_procs, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, pci_pm, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, wakeups, 20, "%u"); DEBUGFS_FWSTATS_FILE(isr, low_rssi, 20, "%u"); DEBUGFS_FWSTATS_FILE(wep, addr_key_count, 20, "%u"); DEBUGFS_FWSTATS_FILE(wep, default_key_count, 20, "%u"); /* skipping wep.reserved */ DEBUGFS_FWSTATS_FILE(wep, key_not_found, 20, "%u"); DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, 20, "%u"); DEBUGFS_FWSTATS_FILE(wep, packets, 20, "%u"); DEBUGFS_FWSTATS_FILE(wep, interrupt, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, ps_enter, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, elp_enter, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, power_save_off, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, enable_ps, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, disable_ps, 20, "%u"); DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, 20, "%u"); /* skipping cont_miss_bcns_spread for now */ DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, 20, "%u"); DEBUGFS_FWSTATS_FILE(mic, rx_pkts, 20, "%u"); DEBUGFS_FWSTATS_FILE(mic, calc_failure, 20, "%u"); DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, 20, "%u"); DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, 20, "%u"); DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, 20, "%u"); DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, 20, "%u"); DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, 20, "%u"); DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, heart_beat, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, calibration, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, rx_mismatch, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, rx_pool, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, oom_late, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, 20, "%u"); DEBUGFS_FWSTATS_FILE(event, tx_stuck, 20, "%u"); DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, 20, "%u"); DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, 20, "%u"); DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, 20, "%u"); DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, 20, "%u"); DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, 20, "%u"); DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, 20, "%u"); DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, 20, "%u"); DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, 20, "%u"); DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, 20, "%u"); DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, 20, "%u"); DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, 20, "%u"); DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, 20, "%u"); DEBUGFS_READONLY_FILE(retry_count, 20, "%u", wl->stats.retry_count); DEBUGFS_READONLY_FILE(excessive_retries, 20, "%u", wl->stats.excessive_retries); static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct wl1251 *wl = file->private_data; u32 queue_len; char buf[20]; int res; queue_len = skb_queue_len(&wl->tx_queue); res = scnprintf(buf, sizeof(buf), "%u\n", queue_len); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } static const struct file_operations tx_queue_len_ops = { .read = tx_queue_len_read, .open = simple_open, .llseek = generic_file_llseek, }; static ssize_t tx_queue_status_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct wl1251 *wl = file->private_data; char buf[3], status; int len; if (wl->tx_queue_stopped) status = 's'; else status = 'r'; len = scnprintf(buf, sizeof(buf), "%c\n", status); return simple_read_from_buffer(userbuf, count, ppos, buf, len); } static const struct file_operations tx_queue_status_ops = { .read = tx_queue_status_read, .open = simple_open, .llseek = generic_file_llseek, }; static void wl1251_debugfs_delete_files(struct wl1251 *wl) { DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); DEBUGFS_FWSTATS_DEL(rx, out_of_mem); DEBUGFS_FWSTATS_DEL(rx, hdr_overflow); DEBUGFS_FWSTATS_DEL(rx, hw_stuck); DEBUGFS_FWSTATS_DEL(rx, dropped); DEBUGFS_FWSTATS_DEL(rx, fcs_err); DEBUGFS_FWSTATS_DEL(rx, xfr_hint_trig); DEBUGFS_FWSTATS_DEL(rx, path_reset); DEBUGFS_FWSTATS_DEL(rx, reset_counter); DEBUGFS_FWSTATS_DEL(dma, rx_requested); DEBUGFS_FWSTATS_DEL(dma, rx_errors); DEBUGFS_FWSTATS_DEL(dma, tx_requested); DEBUGFS_FWSTATS_DEL(dma, tx_errors); DEBUGFS_FWSTATS_DEL(isr, cmd_cmplt); DEBUGFS_FWSTATS_DEL(isr, fiqs); DEBUGFS_FWSTATS_DEL(isr, rx_headers); DEBUGFS_FWSTATS_DEL(isr, rx_mem_overflow); DEBUGFS_FWSTATS_DEL(isr, rx_rdys); DEBUGFS_FWSTATS_DEL(isr, irqs); DEBUGFS_FWSTATS_DEL(isr, tx_procs); DEBUGFS_FWSTATS_DEL(isr, decrypt_done); DEBUGFS_FWSTATS_DEL(isr, dma0_done); DEBUGFS_FWSTATS_DEL(isr, dma1_done); DEBUGFS_FWSTATS_DEL(isr, tx_exch_complete); DEBUGFS_FWSTATS_DEL(isr, commands); DEBUGFS_FWSTATS_DEL(isr, rx_procs); DEBUGFS_FWSTATS_DEL(isr, hw_pm_mode_changes); DEBUGFS_FWSTATS_DEL(isr, host_acknowledges); DEBUGFS_FWSTATS_DEL(isr, pci_pm); DEBUGFS_FWSTATS_DEL(isr, wakeups); DEBUGFS_FWSTATS_DEL(isr, low_rssi); DEBUGFS_FWSTATS_DEL(wep, addr_key_count); DEBUGFS_FWSTATS_DEL(wep, default_key_count); /* skipping wep.reserved */ DEBUGFS_FWSTATS_DEL(wep, key_not_found); DEBUGFS_FWSTATS_DEL(wep, decrypt_fail); DEBUGFS_FWSTATS_DEL(wep, packets); DEBUGFS_FWSTATS_DEL(wep, interrupt); DEBUGFS_FWSTATS_DEL(pwr, ps_enter); DEBUGFS_FWSTATS_DEL(pwr, elp_enter); DEBUGFS_FWSTATS_DEL(pwr, missing_bcns); DEBUGFS_FWSTATS_DEL(pwr, wake_on_host); DEBUGFS_FWSTATS_DEL(pwr, wake_on_timer_exp); DEBUGFS_FWSTATS_DEL(pwr, tx_with_ps); DEBUGFS_FWSTATS_DEL(pwr, tx_without_ps); DEBUGFS_FWSTATS_DEL(pwr, rcvd_beacons); DEBUGFS_FWSTATS_DEL(pwr, power_save_off); DEBUGFS_FWSTATS_DEL(pwr, enable_ps); DEBUGFS_FWSTATS_DEL(pwr, disable_ps); DEBUGFS_FWSTATS_DEL(pwr, fix_tsf_ps); /* skipping cont_miss_bcns_spread for now */ DEBUGFS_FWSTATS_DEL(pwr, rcvd_awake_beacons); DEBUGFS_FWSTATS_DEL(mic, rx_pkts); DEBUGFS_FWSTATS_DEL(mic, calc_failure); DEBUGFS_FWSTATS_DEL(aes, encrypt_fail); DEBUGFS_FWSTATS_DEL(aes, decrypt_fail); DEBUGFS_FWSTATS_DEL(aes, encrypt_packets); DEBUGFS_FWSTATS_DEL(aes, decrypt_packets); DEBUGFS_FWSTATS_DEL(aes, encrypt_interrupt); DEBUGFS_FWSTATS_DEL(aes, decrypt_interrupt); DEBUGFS_FWSTATS_DEL(event, heart_beat); DEBUGFS_FWSTATS_DEL(event, calibration); DEBUGFS_FWSTATS_DEL(event, rx_mismatch); DEBUGFS_FWSTATS_DEL(event, rx_mem_empty); DEBUGFS_FWSTATS_DEL(event, rx_pool); DEBUGFS_FWSTATS_DEL(event, oom_late); DEBUGFS_FWSTATS_DEL(event, phy_transmit_error); DEBUGFS_FWSTATS_DEL(event, tx_stuck); DEBUGFS_FWSTATS_DEL(ps, pspoll_timeouts); DEBUGFS_FWSTATS_DEL(ps, upsd_timeouts); DEBUGFS_FWSTATS_DEL(ps, upsd_max_sptime); DEBUGFS_FWSTATS_DEL(ps, upsd_max_apturn); DEBUGFS_FWSTATS_DEL(ps, pspoll_max_apturn); DEBUGFS_FWSTATS_DEL(ps, pspoll_utilization); DEBUGFS_FWSTATS_DEL(ps, upsd_utilization); DEBUGFS_FWSTATS_DEL(rxpipe, rx_prep_beacon_drop); DEBUGFS_FWSTATS_DEL(rxpipe, descr_host_int_trig_rx_data); DEBUGFS_FWSTATS_DEL(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); DEBUGFS_FWSTATS_DEL(rxpipe, missed_beacon_host_int_trig_rx_data); DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data); DEBUGFS_DEL(tx_queue_len); DEBUGFS_DEL(tx_queue_status); DEBUGFS_DEL(retry_count); DEBUGFS_DEL(excessive_retries); } static int wl1251_debugfs_add_files(struct wl1251 *wl) { int ret = 0; DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow); DEBUGFS_FWSTATS_ADD(rx, out_of_mem); DEBUGFS_FWSTATS_ADD(rx, hdr_overflow); DEBUGFS_FWSTATS_ADD(rx, hw_stuck); DEBUGFS_FWSTATS_ADD(rx, dropped); DEBUGFS_FWSTATS_ADD(rx, fcs_err); DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig); DEBUGFS_FWSTATS_ADD(rx, path_reset); DEBUGFS_FWSTATS_ADD(rx, reset_counter); DEBUGFS_FWSTATS_ADD(dma, rx_requested); DEBUGFS_FWSTATS_ADD(dma, rx_errors); DEBUGFS_FWSTATS_ADD(dma, tx_requested); DEBUGFS_FWSTATS_ADD(dma, tx_errors); DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt); DEBUGFS_FWSTATS_ADD(isr, fiqs); DEBUGFS_FWSTATS_ADD(isr, rx_headers); DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow); DEBUGFS_FWSTATS_ADD(isr, rx_rdys); DEBUGFS_FWSTATS_ADD(isr, irqs); DEBUGFS_FWSTATS_ADD(isr, tx_procs); DEBUGFS_FWSTATS_ADD(isr, decrypt_done); DEBUGFS_FWSTATS_ADD(isr, dma0_done); DEBUGFS_FWSTATS_ADD(isr, dma1_done); DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete); DEBUGFS_FWSTATS_ADD(isr, commands); DEBUGFS_FWSTATS_ADD(isr, rx_procs); DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes); DEBUGFS_FWSTATS_ADD(isr, host_acknowledges); DEBUGFS_FWSTATS_ADD(isr, pci_pm); DEBUGFS_FWSTATS_ADD(isr, wakeups); DEBUGFS_FWSTATS_ADD(isr, low_rssi); DEBUGFS_FWSTATS_ADD(wep, addr_key_count); DEBUGFS_FWSTATS_ADD(wep, default_key_count); /* skipping wep.reserved */ DEBUGFS_FWSTATS_ADD(wep, key_not_found); DEBUGFS_FWSTATS_ADD(wep, decrypt_fail); DEBUGFS_FWSTATS_ADD(wep, packets); DEBUGFS_FWSTATS_ADD(wep, interrupt); DEBUGFS_FWSTATS_ADD(pwr, ps_enter); DEBUGFS_FWSTATS_ADD(pwr, elp_enter); DEBUGFS_FWSTATS_ADD(pwr, missing_bcns); DEBUGFS_FWSTATS_ADD(pwr, wake_on_host); DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp); DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps); DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps); DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons); DEBUGFS_FWSTATS_ADD(pwr, power_save_off); DEBUGFS_FWSTATS_ADD(pwr, enable_ps); DEBUGFS_FWSTATS_ADD(pwr, disable_ps); DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps); /* skipping cont_miss_bcns_spread for now */ DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons); DEBUGFS_FWSTATS_ADD(mic, rx_pkts); DEBUGFS_FWSTATS_ADD(mic, calc_failure); DEBUGFS_FWSTATS_ADD(aes, encrypt_fail); DEBUGFS_FWSTATS_ADD(aes, decrypt_fail); DEBUGFS_FWSTATS_ADD(aes, encrypt_packets); DEBUGFS_FWSTATS_ADD(aes, decrypt_packets); DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt); DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt); DEBUGFS_FWSTATS_ADD(event, heart_beat); DEBUGFS_FWSTATS_ADD(event, calibration); DEBUGFS_FWSTATS_ADD(event, rx_mismatch); DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); DEBUGFS_FWSTATS_ADD(event, rx_pool); DEBUGFS_FWSTATS_ADD(event, oom_late); DEBUGFS_FWSTATS_ADD(event, phy_transmit_error); DEBUGFS_FWSTATS_ADD(event, tx_stuck); DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts); DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts); DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime); DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn); DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn); DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization); DEBUGFS_FWSTATS_ADD(ps, upsd_utilization); DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop); DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir); DEBUGFS_ADD(tx_queue_status, wl->debugfs.rootdir); DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); out: if (ret < 0) wl1251_debugfs_delete_files(wl); return ret; } void wl1251_debugfs_reset(struct wl1251 *wl) { if (wl->stats.fw_stats != NULL) memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); wl->stats.retry_count = 0; wl->stats.excessive_retries = 0; } int wl1251_debugfs_init(struct wl1251 *wl) { int ret; wl->debugfs.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); if (IS_ERR(wl->debugfs.rootdir)) { ret = PTR_ERR(wl->debugfs.rootdir); wl->debugfs.rootdir = NULL; goto err; } wl->debugfs.fw_statistics = debugfs_create_dir("fw-statistics", wl->debugfs.rootdir); if (IS_ERR(wl->debugfs.fw_statistics)) { ret = PTR_ERR(wl->debugfs.fw_statistics); wl->debugfs.fw_statistics = NULL; goto err_root; } wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats), GFP_KERNEL); if (!wl->stats.fw_stats) { ret = -ENOMEM; goto err_fw; } wl->stats.fw_stats_update = jiffies; ret = wl1251_debugfs_add_files(wl); if (ret < 0) goto err_file; return 0; err_file: kfree(wl->stats.fw_stats); wl->stats.fw_stats = NULL; err_fw: debugfs_remove(wl->debugfs.fw_statistics); wl->debugfs.fw_statistics = NULL; err_root: debugfs_remove(wl->debugfs.rootdir); wl->debugfs.rootdir = NULL; err: return ret; } void wl1251_debugfs_exit(struct wl1251 *wl) { wl1251_debugfs_delete_files(wl); kfree(wl->stats.fw_stats); wl->stats.fw_stats = NULL; debugfs_remove(wl->debugfs.fw_statistics); wl->debugfs.fw_statistics = NULL; debugfs_remove(wl->debugfs.rootdir); wl->debugfs.rootdir = NULL; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/cmd.h0000644000175000017500000002444012026211315022750 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_CMD_H__ #define __WL1251_CMD_H__ #include "wl1251.h" #include struct acx_header; int wl1251_cmd_send(struct wl1251 *wl, u16 type, void *buf, size_t buf_len); int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer); int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len); int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len); int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, void *bitmap, u16 bitmap_len, u8 bitmap_control); int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable); int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, u16 beacon_interval, u8 dtim_interval); int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode); int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, size_t len); int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, void *buf, size_t buf_len); int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, struct ieee80211_channel *channels[], unsigned int n_channels, unsigned int n_probes); int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout); /* unit ms */ #define WL1251_COMMAND_TIMEOUT 2000 enum wl1251_commands { CMD_RESET = 0, CMD_INTERROGATE = 1, /*use this to read information elements*/ CMD_CONFIGURE = 2, /*use this to write information elements*/ CMD_ENABLE_RX = 3, CMD_ENABLE_TX = 4, CMD_DISABLE_RX = 5, CMD_DISABLE_TX = 6, CMD_SCAN = 8, CMD_STOP_SCAN = 9, CMD_VBM = 10, CMD_START_JOIN = 11, CMD_SET_KEYS = 12, CMD_READ_MEMORY = 13, CMD_WRITE_MEMORY = 14, CMD_BEACON = 19, CMD_PROBE_RESP = 20, CMD_NULL_DATA = 21, CMD_PROBE_REQ = 22, CMD_TEST = 23, CMD_RADIO_CALIBRATE = 25, /* OBSOLETE */ CMD_ENABLE_RX_PATH = 27, /* OBSOLETE */ CMD_NOISE_HIST = 28, CMD_RX_RESET = 29, CMD_PS_POLL = 30, CMD_QOS_NULL_DATA = 31, CMD_LNA_CONTROL = 32, CMD_SET_BCN_MODE = 33, CMD_MEASUREMENT = 34, CMD_STOP_MEASUREMENT = 35, CMD_DISCONNECT = 36, CMD_SET_PS_MODE = 37, CMD_CHANNEL_SWITCH = 38, CMD_STOP_CHANNEL_SWICTH = 39, CMD_AP_DISCOVERY = 40, CMD_STOP_AP_DISCOVERY = 41, CMD_SPS_SCAN = 42, CMD_STOP_SPS_SCAN = 43, CMD_HEALTH_CHECK = 45, CMD_DEBUG = 46, CMD_TRIGGER_SCAN_TO = 47, NUM_COMMANDS, MAX_COMMAND_ID = 0xFFFF, }; #define MAX_CMD_PARAMS 572 struct wl1251_cmd_header { u16 id; u16 status; /* payload */ u8 data[0]; } __packed; struct wl1251_command { struct wl1251_cmd_header header; u8 parameters[MAX_CMD_PARAMS]; } __packed; enum { CMD_MAILBOX_IDLE = 0, CMD_STATUS_SUCCESS = 1, CMD_STATUS_UNKNOWN_CMD = 2, CMD_STATUS_UNKNOWN_IE = 3, CMD_STATUS_REJECT_MEAS_SG_ACTIVE = 11, CMD_STATUS_RX_BUSY = 13, CMD_STATUS_INVALID_PARAM = 14, CMD_STATUS_TEMPLATE_TOO_LARGE = 15, CMD_STATUS_OUT_OF_MEMORY = 16, CMD_STATUS_STA_TABLE_FULL = 17, CMD_STATUS_RADIO_ERROR = 18, CMD_STATUS_WRONG_NESTING = 19, CMD_STATUS_TIMEOUT = 21, /* Driver internal use.*/ CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/ MAX_COMMAND_STATUS = 0xff }; /* * CMD_READ_MEMORY * * The host issues this command to read the WiLink device memory/registers. * * Note: The Base Band address has special handling (16 bits registers and * addresses). For more information, see the hardware specification. */ /* * CMD_WRITE_MEMORY * * The host issues this command to write the WiLink device memory/registers. * * The Base Band address has special handling (16 bits registers and * addresses). For more information, see the hardware specification. */ #define MAX_READ_SIZE 256 struct cmd_read_write_memory { struct wl1251_cmd_header header; /* The address of the memory to read from or write to.*/ u32 addr; /* The amount of data in bytes to read from or write to the WiLink * device.*/ u32 size; /* The actual value read from or written to the Wilink. The source of this field is the Host in WRITE command or the Wilink in READ command. */ u8 value[MAX_READ_SIZE]; } __packed; #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 #define WL1251_SCAN_MIN_DURATION 30000 #define WL1251_SCAN_MAX_DURATION 60000 #define WL1251_SCAN_NUM_PROBES 3 struct wl1251_scan_parameters { __le32 rx_config_options; __le32 rx_filter_options; /* * Scan options: * bit 0: When this bit is set, passive scan. * bit 1: Band, when this bit is set we scan * in the 5Ghz band. * bit 2: voice mode, 0 for normal scan. * bit 3: scan priority, 1 for high priority. */ __le16 scan_options; /* Number of channels to scan */ u8 num_channels; /* Number opf probe requests to send, per channel */ u8 num_probe_requests; /* Rate and modulation for probe requests */ __le16 tx_rate; u8 tid_trigger; u8 ssid_len; u8 ssid[32]; } __packed; struct wl1251_scan_ch_parameters { __le32 min_duration; /* in TU */ __le32 max_duration; /* in TU */ u32 bssid_lsb; u16 bssid_msb; /* * bits 0-3: Early termination count. * bits 4-5: Early termination condition. */ u8 early_termination; u8 tx_power_att; u8 channel; u8 pad[3]; } __packed; /* SCAN parameters */ #define SCAN_MAX_NUM_OF_CHANNELS 16 struct wl1251_cmd_scan { struct wl1251_cmd_header header; struct wl1251_scan_parameters params; struct wl1251_scan_ch_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; } __packed; enum { BSS_TYPE_IBSS = 0, BSS_TYPE_STA_BSS = 2, BSS_TYPE_AP_BSS = 3, MAX_BSS_TYPE = 0xFF }; #define JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */ #define JOIN_CMD_CTRL_EARLY_WAKEUP_ENABLE 0x01 /* Early wakeup time */ struct cmd_join { struct wl1251_cmd_header header; u32 bssid_lsb; u16 bssid_msb; u16 beacon_interval; /* in TBTTs */ u32 rx_config_options; u32 rx_filter_options; /* * The target uses this field to determine the rate at * which to transmit control frame responses (such as * ACK or CTS frames). */ u16 basic_rate_set; u8 dtim_interval; u8 tx_ctrl_frame_rate; /* OBSOLETE */ u8 tx_ctrl_frame_mod; /* OBSOLETE */ /* * bits 0-2: This bitwise field specifies the type * of BSS to start or join (BSS_TYPE_*). * bit 4: Band - The radio band in which to join * or start. * 0 - 2.4GHz band * 1 - 5GHz band * bits 3, 5-7: Reserved */ u8 bss_type; u8 channel; u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ctrl; /* JOIN_CMD_CTRL_* */ u8 tx_mgt_frame_rate; /* OBSOLETE */ u8 tx_mgt_frame_mod; /* OBSOLETE */ u8 reserved; } __packed; struct cmd_enabledisable_path { struct wl1251_cmd_header header; u8 channel; u8 padding[3]; } __packed; #define WL1251_MAX_TEMPLATE_SIZE 300 struct wl1251_cmd_packet_template { struct wl1251_cmd_header header; __le16 size; u8 data[0]; } __packed; #define TIM_ELE_ID 5 #define PARTIAL_VBM_MAX 251 struct wl1251_tim { u8 identity; u8 length; u8 dtim_count; u8 dtim_period; u8 bitmap_ctrl; u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */ } __packed; /* Virtual Bit Map update */ struct wl1251_cmd_vbm_update { struct wl1251_cmd_header header; __le16 len; u8 padding[2]; struct wl1251_tim tim; } __packed; enum wl1251_cmd_ps_mode { CHIP_ACTIVE_MODE, CHIP_POWER_SAVE_MODE }; struct wl1251_cmd_ps_params { struct wl1251_cmd_header header; u8 ps_mode; /* STATION_* */ u8 send_null_data; /* Do we have to send NULL data packet ? */ u8 retries; /* Number of retires for the initial NULL data packet */ /* * TUs during which the target stays awake after switching * to power save mode. */ u8 hang_over_period; u16 null_data_rate; u8 pad[2]; } __packed; struct wl1251_cmd_trigger_scan_to { struct wl1251_cmd_header header; u32 timeout; } __packed; /* HW encryption keys */ #define NUM_ACCESS_CATEGORIES_COPY 4 #define MAX_KEY_SIZE 32 /* When set, disable HW encryption */ #define DF_ENCRYPTION_DISABLE 0x01 /* When set, disable HW decryption */ #define DF_SNIFF_MODE_ENABLE 0x80 enum wl1251_cmd_key_action { KEY_ADD_OR_REPLACE = 1, KEY_REMOVE = 2, KEY_SET_ID = 3, MAX_KEY_ACTION = 0xffff, }; enum wl1251_cmd_key_type { KEY_WEP_DEFAULT = 0, KEY_WEP_ADDR = 1, KEY_AES_GROUP = 4, KEY_AES_PAIRWISE = 5, KEY_WEP_GROUP = 6, KEY_TKIP_MIC_GROUP = 10, KEY_TKIP_MIC_PAIRWISE = 11, }; /* * * key_type_e key size key format * ---------- --------- ---------- * 0x00 5, 13, 29 Key data * 0x01 5, 13, 29 Key data * 0x04 16 16 bytes of key data * 0x05 16 16 bytes of key data * 0x0a 32 16 bytes of TKIP key data * 8 bytes of RX MIC key data * 8 bytes of TX MIC key data * 0x0b 32 16 bytes of TKIP key data * 8 bytes of RX MIC key data * 8 bytes of TX MIC key data * */ struct wl1251_cmd_set_keys { struct wl1251_cmd_header header; /* Ignored for default WEP key */ u8 addr[ETH_ALEN]; /* key_action_e */ u16 key_action; u16 reserved_1; /* key size in bytes */ u8 key_size; /* key_type_e */ u8 key_type; u8 ssid_profile; /* * TKIP, AES: frame's key id field. * For WEP default key: key id; */ u8 id; u8 reserved_2[6]; u8 key[MAX_KEY_SIZE]; u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; } __packed; #endif /* __WL1251_CMD_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/cmd.c0000644000175000017500000002473712026211315022754 0ustar mcgrofmcgrof#include "cmd.h" #include #include #include #include "wl1251.h" #include "reg.h" #include "io.h" #include "ps.h" #include "acx.h" /** * send command to firmware * * @wl: wl struct * @id: command id * @buf: buffer containing the command, must work with dma * @len: length of the buffer */ int wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len) { struct wl1251_cmd_header *cmd; unsigned long timeout; u32 intr; int ret = 0; cmd = buf; cmd->id = id; cmd->status = 0; WARN_ON(len % 4 != 0); wl1251_mem_write(wl, wl->cmd_box_addr, buf, len); wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); timeout = jiffies + msecs_to_jiffies(WL1251_COMMAND_TIMEOUT); intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); while (!(intr & WL1251_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1251_error("command complete timeout"); ret = -ETIMEDOUT; goto out; } msleep(1); intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); } wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, WL1251_ACX_INTR_CMD_COMPLETE); out: return ret; } /** * send test command to firmware * * @wl: wl struct * @buf: buffer containing the command, with all headers, must work with dma * @len: length of the buffer * @answer: is answer needed */ int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer) { int ret; wl1251_debug(DEBUG_CMD, "cmd test"); ret = wl1251_cmd_send(wl, CMD_TEST, buf, buf_len); if (ret < 0) { wl1251_warning("TEST command failed"); return ret; } if (answer) { struct wl1251_command *cmd_answer; /* * The test command got in, we can read the answer. * The answer would be a wl1251_command, where the * parameter array contains the actual answer. */ wl1251_mem_read(wl, wl->cmd_box_addr, buf, buf_len); cmd_answer = buf; if (cmd_answer->header.status != CMD_STATUS_SUCCESS) wl1251_error("TEST command answer error: %d", cmd_answer->header.status); } return 0; } /** * read acx from firmware * * @wl: wl struct * @id: acx id * @buf: buffer for the response, including all headers, must work with dma * @len: length of buf */ int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len) { struct acx_header *acx = buf; int ret; wl1251_debug(DEBUG_CMD, "cmd interrogate"); acx->id = id; /* payload length, does not include any headers */ acx->len = len - sizeof(*acx); ret = wl1251_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); if (ret < 0) { wl1251_error("INTERROGATE command failed"); goto out; } /* the interrogate command got in, we can read the answer */ wl1251_mem_read(wl, wl->cmd_box_addr, buf, len); acx = buf; if (acx->cmd.status != CMD_STATUS_SUCCESS) wl1251_error("INTERROGATE command error: %d", acx->cmd.status); out: return ret; } /** * write acx value to firmware * * @wl: wl struct * @id: acx id * @buf: buffer containing acx, including all headers, must work with dma * @len: length of buf */ int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len) { struct acx_header *acx = buf; int ret; wl1251_debug(DEBUG_CMD, "cmd configure"); acx->id = id; /* payload length, does not include any headers */ acx->len = len - sizeof(*acx); ret = wl1251_cmd_send(wl, CMD_CONFIGURE, acx, len); if (ret < 0) { wl1251_warning("CONFIGURE command NOK"); return ret; } return 0; } int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, void *bitmap, u16 bitmap_len, u8 bitmap_control) { struct wl1251_cmd_vbm_update *vbm; int ret; wl1251_debug(DEBUG_CMD, "cmd vbm"); vbm = kzalloc(sizeof(*vbm), GFP_KERNEL); if (!vbm) { ret = -ENOMEM; goto out; } /* Count and period will be filled by the target */ vbm->tim.bitmap_ctrl = bitmap_control; if (bitmap_len > PARTIAL_VBM_MAX) { wl1251_warning("cmd vbm len is %d B, truncating to %d", bitmap_len, PARTIAL_VBM_MAX); bitmap_len = PARTIAL_VBM_MAX; } memcpy(vbm->tim.pvb_field, bitmap, bitmap_len); vbm->tim.identity = identity; vbm->tim.length = bitmap_len + 3; vbm->len = cpu_to_le16(bitmap_len + 5); ret = wl1251_cmd_send(wl, CMD_VBM, vbm, sizeof(*vbm)); if (ret < 0) { wl1251_error("VBM command failed"); goto out; } out: kfree(vbm); return ret; } int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable) { struct cmd_enabledisable_path *cmd; int ret; u16 cmd_rx, cmd_tx; wl1251_debug(DEBUG_CMD, "cmd data path"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->channel = channel; if (enable) { cmd_rx = CMD_ENABLE_RX; cmd_tx = CMD_ENABLE_TX; } else { cmd_rx = CMD_DISABLE_RX; cmd_tx = CMD_DISABLE_TX; } ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); if (ret < 0) { wl1251_error("rx %s cmd for channel %d failed", enable ? "start" : "stop", channel); goto out; } wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d", enable ? "start" : "stop", channel); ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); if (ret < 0) { wl1251_error("tx %s cmd for channel %d failed", enable ? "start" : "stop", channel); goto out; } wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d", enable ? "start" : "stop", channel); out: kfree(cmd); return ret; } int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, u16 beacon_interval, u8 dtim_interval) { struct cmd_join *join; int ret, i; u8 *bssid; join = kzalloc(sizeof(*join), GFP_KERNEL); if (!join) { ret = -ENOMEM; goto out; } wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d", bss_type == BSS_TYPE_IBSS ? " ibss" : "", channel, beacon_interval, dtim_interval); /* Reverse order BSSID */ bssid = (u8 *) &join->bssid_lsb; for (i = 0; i < ETH_ALEN; i++) bssid[i] = wl->bssid[ETH_ALEN - i - 1]; join->rx_config_options = wl->rx_config; join->rx_filter_options = wl->rx_filter; join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; join->beacon_interval = beacon_interval; join->dtim_interval = dtim_interval; join->bss_type = bss_type; join->channel = channel; join->ctrl = JOIN_CMD_CTRL_TX_FLUSH; ret = wl1251_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); if (ret < 0) { wl1251_error("failed to initiate cmd join"); goto out; } out: kfree(join); return ret; } int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode) { struct wl1251_cmd_ps_params *ps_params = NULL; int ret = 0; wl1251_debug(DEBUG_CMD, "cmd set ps mode"); ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); if (!ps_params) { ret = -ENOMEM; goto out; } ps_params->ps_mode = ps_mode; ps_params->send_null_data = 1; ps_params->retries = 5; ps_params->hang_over_period = 128; ps_params->null_data_rate = 1; /* 1 Mbps */ ret = wl1251_cmd_send(wl, CMD_SET_PS_MODE, ps_params, sizeof(*ps_params)); if (ret < 0) { wl1251_error("cmd set_ps_mode failed"); goto out; } out: kfree(ps_params); return ret; } int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, size_t len) { struct cmd_read_write_memory *cmd; int ret = 0; wl1251_debug(DEBUG_CMD, "cmd read memory"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } WARN_ON(len > MAX_READ_SIZE); len = min_t(size_t, len, MAX_READ_SIZE); cmd->addr = addr; cmd->size = len; ret = wl1251_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); if (ret < 0) { wl1251_error("read memory command failed: %d", ret); goto out; } /* the read command got in, we can now read the answer */ wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); if (cmd->header.status != CMD_STATUS_SUCCESS) wl1251_error("error in read command result: %d", cmd->header.status); memcpy(answer, cmd->value, len); out: kfree(cmd); return ret; } int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, void *buf, size_t buf_len) { struct wl1251_cmd_packet_template *cmd; size_t cmd_len; int ret = 0; wl1251_debug(DEBUG_CMD, "cmd template %d", cmd_id); WARN_ON(buf_len > WL1251_MAX_TEMPLATE_SIZE); buf_len = min_t(size_t, buf_len, WL1251_MAX_TEMPLATE_SIZE); cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4); cmd = kzalloc(cmd_len, GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->size = cpu_to_le16(buf_len); if (buf) memcpy(cmd->data, buf, buf_len); ret = wl1251_cmd_send(wl, cmd_id, cmd, cmd_len); if (ret < 0) { wl1251_warning("cmd set_template failed: %d", ret); goto out; } out: kfree(cmd); return ret; } int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, struct ieee80211_channel *channels[], unsigned int n_channels, unsigned int n_probes) { struct wl1251_cmd_scan *cmd; int i, ret = 0; wl1251_debug(DEBUG_CMD, "cmd scan"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return -ENOMEM; cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN); cmd->params.scan_options = 0; cmd->params.num_channels = n_channels; cmd->params.num_probe_requests = n_probes; cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ cmd->params.tid_trigger = 0; for (i = 0; i < n_channels; i++) { cmd->channels[i].min_duration = cpu_to_le32(WL1251_SCAN_MIN_DURATION); cmd->channels[i].max_duration = cpu_to_le32(WL1251_SCAN_MAX_DURATION); memset(&cmd->channels[i].bssid_lsb, 0xff, 4); memset(&cmd->channels[i].bssid_msb, 0xff, 2); cmd->channels[i].early_termination = 0; cmd->channels[i].tx_power_att = 0; cmd->channels[i].channel = channels[i]->hw_value; } cmd->params.ssid_len = ssid_len; if (ssid) memcpy(cmd->params.ssid, ssid, ssid_len); ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); if (ret < 0) { wl1251_error("cmd scan failed: %d", ret); goto out; } wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); if (cmd->header.status != CMD_STATUS_SUCCESS) { wl1251_error("cmd scan status wasn't success: %d", cmd->header.status); ret = -EIO; goto out; } out: kfree(cmd); return ret; } int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout) { struct wl1251_cmd_trigger_scan_to *cmd; int ret; wl1251_debug(DEBUG_CMD, "cmd trigger scan to"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return -ENOMEM; cmd->timeout = timeout; ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, cmd, sizeof(*cmd)); if (ret < 0) { wl1251_error("cmd trigger scan to failed: %d", ret); goto out; } out: kfree(cmd); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/boot.h0000644000175000017500000000223612026211315023147 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __BOOT_H__ #define __BOOT_H__ #include "wl1251.h" int wl1251_boot_soft_reset(struct wl1251 *wl); int wl1251_boot_init_seq(struct wl1251 *wl); int wl1251_boot_run_firmware(struct wl1251 *wl); void wl1251_boot_target_enable_interrupts(struct wl1251 *wl); int wl1251_boot(struct wl1251 *wl); /* number of times we try to read the INIT interrupt */ #define INIT_LOOP 20000 /* delay between retries */ #define INIT_LOOP_DELAY 50 #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/boot.c0000644000175000017500000003473612026211315023154 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include "reg.h" #include "boot.h" #include "io.h" #include "spi.h" #include "event.h" #include "acx.h" void wl1251_boot_target_enable_interrupts(struct wl1251 *wl) { wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); wl1251_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); } int wl1251_boot_soft_reset(struct wl1251 *wl) { unsigned long timeout; u32 boot_data; /* perform soft reset */ wl1251_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); while (1) { boot_data = wl1251_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); wl1251_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) break; if (time_after(jiffies, timeout)) { /* 1.2 check pWhalBus->uSelfClearTime if the * timeout was reached */ wl1251_error("soft reset timeout"); return -1; } udelay(SOFT_RESET_STALL_TIME); } /* disable Rx/Tx */ wl1251_reg_write32(wl, ENABLE, 0x0); /* disable auto calibration on start*/ wl1251_reg_write32(wl, SPARE_A2, 0xffff); return 0; } int wl1251_boot_init_seq(struct wl1251 *wl) { u32 scr_pad6, init_data, tmp, elp_cmd, ref_freq; /* * col #1: INTEGER_DIVIDER * col #2: FRACTIONAL_DIVIDER * col #3: ATTN_BB * col #4: ALPHA_BB * col #5: STOP_TIME_BB * col #6: BB_PLL_LOOP_FILTER */ static const u32 LUT[REF_FREQ_NUM][LUT_PARAM_NUM] = { { 83, 87381, 0xB, 5, 0xF00, 3}, /* REF_FREQ_19_2*/ { 61, 141154, 0xB, 5, 0x1450, 2}, /* REF_FREQ_26_0*/ { 41, 174763, 0xC, 6, 0x2D00, 1}, /* REF_FREQ_38_4*/ { 40, 0, 0xC, 6, 0x2EE0, 1}, /* REF_FREQ_40_0*/ { 47, 162280, 0xC, 6, 0x2760, 1} /* REF_FREQ_33_6 */ }; /* read NVS params */ scr_pad6 = wl1251_reg_read32(wl, SCR_PAD6); wl1251_debug(DEBUG_BOOT, "scr_pad6 0x%x", scr_pad6); /* read ELP_CMD */ elp_cmd = wl1251_reg_read32(wl, ELP_CMD); wl1251_debug(DEBUG_BOOT, "elp_cmd 0x%x", elp_cmd); /* set the BB calibration time to be 300 usec (PLL_CAL_TIME) */ ref_freq = scr_pad6 & 0x000000FF; wl1251_debug(DEBUG_BOOT, "ref_freq 0x%x", ref_freq); wl1251_reg_write32(wl, PLL_CAL_TIME, 0x9); /* * PG 1.2: set the clock buffer time to be 210 usec (CLK_BUF_TIME) */ wl1251_reg_write32(wl, CLK_BUF_TIME, 0x6); /* * set the clock detect feature to work in the restart wu procedure * (ELP_CFG_MODE[14]) and Select the clock source type * (ELP_CFG_MODE[13:12]) */ tmp = ((scr_pad6 & 0x0000FF00) << 4) | 0x00004000; wl1251_reg_write32(wl, ELP_CFG_MODE, tmp); /* PG 1.2: enable the BB PLL fix. Enable the PLL_LIMP_CLK_EN_CMD */ elp_cmd |= 0x00000040; wl1251_reg_write32(wl, ELP_CMD, elp_cmd); /* PG 1.2: Set the BB PLL stable time to be 1000usec * (PLL_STABLE_TIME) */ wl1251_reg_write32(wl, CFG_PLL_SYNC_CNT, 0x20); /* PG 1.2: read clock request time */ init_data = wl1251_reg_read32(wl, CLK_REQ_TIME); /* * PG 1.2: set the clock request time to be ref_clk_settling_time - * 1ms = 4ms */ if (init_data > 0x21) tmp = init_data - 0x21; else tmp = 0; wl1251_reg_write32(wl, CLK_REQ_TIME, tmp); /* set BB PLL configurations in RF AFE */ wl1251_reg_write32(wl, 0x003058cc, 0x4B5); /* set RF_AFE_REG_5 */ wl1251_reg_write32(wl, 0x003058d4, 0x50); /* set RF_AFE_CTRL_REG_2 */ wl1251_reg_write32(wl, 0x00305948, 0x11c001); /* * change RF PLL and BB PLL divider for VCO clock and adjust VCO * bais current(RF_AFE_REG_13) */ wl1251_reg_write32(wl, 0x003058f4, 0x1e); /* set BB PLL configurations */ tmp = LUT[ref_freq][LUT_PARAM_INTEGER_DIVIDER] | 0x00017000; wl1251_reg_write32(wl, 0x00305840, tmp); /* set fractional divider according to Appendix C-BB PLL * Calculations */ tmp = LUT[ref_freq][LUT_PARAM_FRACTIONAL_DIVIDER]; wl1251_reg_write32(wl, 0x00305844, tmp); /* set the initial data for the sigma delta */ wl1251_reg_write32(wl, 0x00305848, 0x3039); /* * set the accumulator attenuation value, calibration loop1 * (alpha), calibration loop2 (beta), calibration loop3 (gamma) and * the VCO gain */ tmp = (LUT[ref_freq][LUT_PARAM_ATTN_BB] << 16) | (LUT[ref_freq][LUT_PARAM_ALPHA_BB] << 12) | 0x1; wl1251_reg_write32(wl, 0x00305854, tmp); /* * set the calibration stop time after holdoff time expires and set * settling time HOLD_OFF_TIME_BB */ tmp = LUT[ref_freq][LUT_PARAM_STOP_TIME_BB] | 0x000A0000; wl1251_reg_write32(wl, 0x00305858, tmp); /* * set BB PLL Loop filter capacitor3- BB_C3[2:0] and set BB PLL * constant leakage current to linearize PFD to 0uA - * BB_ILOOPF[7:3] */ tmp = LUT[ref_freq][LUT_PARAM_BB_PLL_LOOP_FILTER] | 0x00000030; wl1251_reg_write32(wl, 0x003058f8, tmp); /* * set regulator output voltage for n divider to * 1.35-BB_REFDIV[1:0], set charge pump current- BB_CPGAIN[4:2], * set BB PLL Loop filter capacitor2- BB_C2[7:5], set gain of BB * PLL auto-call to normal mode- BB_CALGAIN_3DB[8] */ wl1251_reg_write32(wl, 0x003058f0, 0x29); /* enable restart wakeup sequence (ELP_CMD[0]) */ wl1251_reg_write32(wl, ELP_CMD, elp_cmd | 0x1); /* restart sequence completed */ udelay(2000); return 0; } static void wl1251_boot_set_ecpu_ctrl(struct wl1251 *wl, u32 flag) { u32 cpu_ctrl; /* 10.5.0 run the firmware (I) */ cpu_ctrl = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); /* 10.5.1 run the firmware (II) */ cpu_ctrl &= ~flag; wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); } int wl1251_boot_run_firmware(struct wl1251 *wl) { int loop, ret; u32 chip_id, acx_intr; wl1251_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); chip_id = wl1251_reg_read32(wl, CHIP_ID_B); wl1251_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); if (chip_id != wl->chip_id) { wl1251_error("chip id doesn't match after firmware boot"); return -EIO; } /* wait for init to complete */ loop = 0; while (loop++ < INIT_LOOP) { udelay(INIT_LOOP_DELAY); acx_intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); if (acx_intr == 0xffffffff) { wl1251_error("error reading hardware complete " "init indication"); return -EIO; } /* check that ACX_INTR_INIT_COMPLETE is enabled */ else if (acx_intr & WL1251_ACX_INTR_INIT_COMPLETE) { wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, WL1251_ACX_INTR_INIT_COMPLETE); break; } } if (loop > INIT_LOOP) { wl1251_error("timeout waiting for the hardware to " "complete initialization"); return -EIO; } /* get hardware config command mail box */ wl->cmd_box_addr = wl1251_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); /* get hardware config event mail box */ wl->event_box_addr = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); /* set the working partition to its "running" mode offset */ wl1251_set_partition(wl, WL1251_PART_WORK_MEM_START, WL1251_PART_WORK_MEM_SIZE, WL1251_PART_WORK_REG_START, WL1251_PART_WORK_REG_SIZE); wl1251_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", wl->cmd_box_addr, wl->event_box_addr); wl1251_acx_fw_version(wl, wl->fw_ver, sizeof(wl->fw_ver)); /* * in case of full asynchronous mode the firmware event must be * ready to receive event from the command mailbox */ /* enable gpio interrupts */ wl1251_enable_interrupts(wl); /* Enable target's interrupts */ wl->intr_mask = WL1251_ACX_INTR_RX0_DATA | WL1251_ACX_INTR_RX1_DATA | WL1251_ACX_INTR_TX_RESULT | WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B | WL1251_ACX_INTR_INIT_COMPLETE; wl1251_boot_target_enable_interrupts(wl); wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID | SYNCHRONIZATION_TIMEOUT_EVENT_ID | ROAMING_TRIGGER_LOW_RSSI_EVENT_ID | ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID | REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID | BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID; ret = wl1251_event_unmask(wl); if (ret < 0) { wl1251_error("EVENT mask setting failed"); return ret; } wl1251_event_mbox_config(wl); /* firmware startup completed */ return 0; } static int wl1251_boot_upload_firmware(struct wl1251 *wl) { int addr, chunk_num, partition_limit; size_t fw_data_len, len; u8 *p, *buf; /* whal_FwCtrl_LoadFwImageSm() */ wl1251_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x", wl1251_reg_read32(wl, CHIP_ID_B)); /* 10.0 check firmware length and set partition */ fw_data_len = (wl->fw[4] << 24) | (wl->fw[5] << 16) | (wl->fw[6] << 8) | (wl->fw[7]); wl1251_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len, CHUNK_SIZE); if ((fw_data_len % 4) != 0) { wl1251_error("firmware length not multiple of four"); return -EIO; } buf = kmalloc(CHUNK_SIZE, GFP_KERNEL); if (!buf) { wl1251_error("allocation for firmware upload chunk failed"); return -ENOMEM; } wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, WL1251_PART_DOWN_MEM_SIZE, WL1251_PART_DOWN_REG_START, WL1251_PART_DOWN_REG_SIZE); /* 10.1 set partition limit and chunk num */ chunk_num = 0; partition_limit = WL1251_PART_DOWN_MEM_SIZE; while (chunk_num < fw_data_len / CHUNK_SIZE) { /* 10.2 update partition, if needed */ addr = WL1251_PART_DOWN_MEM_START + (chunk_num + 2) * CHUNK_SIZE; if (addr > partition_limit) { addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; partition_limit = chunk_num * CHUNK_SIZE + WL1251_PART_DOWN_MEM_SIZE; wl1251_set_partition(wl, addr, WL1251_PART_DOWN_MEM_SIZE, WL1251_PART_DOWN_REG_START, WL1251_PART_DOWN_REG_SIZE); } /* 10.3 upload the chunk */ addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); /* need to copy the chunk for dma */ len = CHUNK_SIZE; memcpy(buf, p, len); wl1251_mem_write(wl, addr, buf, len); chunk_num++; } /* 10.4 upload the last chunk */ addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; /* need to copy the chunk for dma */ len = fw_data_len % CHUNK_SIZE; memcpy(buf, p, len); wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", len, p, addr); wl1251_mem_write(wl, addr, buf, len); kfree(buf); return 0; } static int wl1251_boot_upload_nvs(struct wl1251 *wl) { size_t nvs_len, nvs_bytes_written, burst_len; int nvs_start, i; u32 dest_addr, val; u8 *nvs_ptr, *nvs; nvs = wl->nvs; if (nvs == NULL) return -ENODEV; nvs_ptr = nvs; nvs_len = wl->nvs_len; nvs_start = wl->fw_len; /* * Layout before the actual NVS tables: * 1 byte : burst length. * 2 bytes: destination address. * n bytes: data to burst copy. * * This is ended by a 0 length, then the NVS tables. */ while (nvs_ptr[0]) { burst_len = nvs_ptr[0]; dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); /* We move our pointer to the data */ nvs_ptr += 3; for (i = 0; i < burst_len; i++) { val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); wl1251_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); wl1251_mem_write32(wl, dest_addr, val); nvs_ptr += 4; dest_addr += 4; } } /* * We've reached the first zero length, the first NVS table * is 7 bytes further. */ nvs_ptr += 7; nvs_len -= nvs_ptr - nvs; nvs_len = ALIGN(nvs_len, 4); /* Now we must set the partition correctly */ wl1251_set_partition(wl, nvs_start, WL1251_PART_DOWN_MEM_SIZE, WL1251_PART_DOWN_REG_START, WL1251_PART_DOWN_REG_SIZE); /* And finally we upload the NVS tables */ nvs_bytes_written = 0; while (nvs_bytes_written < nvs_len) { val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); wl1251_debug(DEBUG_BOOT, "nvs write table 0x%x: 0x%x", nvs_start, val); wl1251_mem_write32(wl, nvs_start, val); nvs_ptr += 4; nvs_bytes_written += 4; nvs_start += 4; } return 0; } int wl1251_boot(struct wl1251 *wl) { int ret = 0, minor_minor_e2_ver; u32 tmp, boot_data; /* halt embedded ARM CPU while loading firmware */ wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, ECPU_CONTROL_HALT); ret = wl1251_boot_soft_reset(wl); if (ret < 0) goto out; /* 2. start processing NVS file */ if (wl->use_eeprom) { wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR); /* Wait for EEPROM NVS burst read to complete */ msleep(40); wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM); } else { ret = wl1251_boot_upload_nvs(wl); if (ret < 0) goto out; /* write firmware's last address (ie. it's length) to * ACX_EEPROMLESS_IND_REG */ wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); } /* 6. read the EEPROM parameters */ tmp = wl1251_reg_read32(wl, SCR_PAD2); /* 7. read bootdata */ wl->boot_attr.radio_type = (tmp & 0x0000FF00) >> 8; wl->boot_attr.major = (tmp & 0x00FF0000) >> 16; tmp = wl1251_reg_read32(wl, SCR_PAD3); /* 8. check bootdata and call restart sequence */ wl->boot_attr.minor = (tmp & 0x00FF0000) >> 16; minor_minor_e2_ver = (tmp & 0xFF000000) >> 24; wl1251_debug(DEBUG_BOOT, "radioType 0x%x majorE2Ver 0x%x " "minorE2Ver 0x%x minor_minor_e2_ver 0x%x", wl->boot_attr.radio_type, wl->boot_attr.major, wl->boot_attr.minor, minor_minor_e2_ver); ret = wl1251_boot_init_seq(wl); if (ret < 0) goto out; /* 9. NVS processing done */ boot_data = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); wl1251_debug(DEBUG_BOOT, "halt boot_data 0x%x", boot_data); /* 10. check that ECPU_CONTROL_HALT bits are set in * pWhalBus->uBootData and start uploading firmware */ if ((boot_data & ECPU_CONTROL_HALT) == 0) { wl1251_error("boot failed, ECPU_CONTROL_HALT not set"); ret = -EIO; goto out; } ret = wl1251_boot_upload_firmware(wl); if (ret < 0) goto out; /* 10.5 start firmware */ ret = wl1251_boot_run_firmware(wl); if (ret < 0) goto out; out: return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/acx.h0000644000175000017500000011351112026211315022756 0ustar mcgrofmcgrof/* * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL1251_ACX_H__ #define __WL1251_ACX_H__ #include "wl1251.h" #include "cmd.h" /* Target's information element */ struct acx_header { struct wl1251_cmd_header cmd; /* acx (or information element) header */ u16 id; /* payload length (not including headers */ u16 len; } __packed; struct acx_error_counter { struct acx_header header; /* The number of PLCP errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ u32 PLCP_error; /* The number of FCS errors since the last time this */ /* information element was interrogated. This field is */ /* automatically cleared when it is interrogated.*/ u32 FCS_error; /* The number of MPDUs without PLCP header errors received*/ /* since the last time this information element was interrogated. */ /* This field is automatically cleared when it is interrogated.*/ u32 valid_frame; /* the number of missed sequence numbers in the squentially */ /* values of frames seq numbers */ u32 seq_num_miss; } __packed; struct acx_revision { struct acx_header header; /* * The WiLink firmware version, an ASCII string x.x.x.x, * that uniquely identifies the current firmware. * The left most digit is incremented each time a * significant change is made to the firmware, such as * code redesign or new platform support. * The second digit is incremented when major enhancements * are added or major fixes are made. * The third digit is incremented for each GA release. * The fourth digit is incremented for each build. * The first two digits identify a firmware release version, * in other words, a unique set of features. * The first three digits identify a GA release. */ char fw_version[20]; /* * This 4 byte field specifies the WiLink hardware version. * bits 0 - 15: Reserved. * bits 16 - 23: Version ID - The WiLink version ID * (1 = first spin, 2 = second spin, and so on). * bits 24 - 31: Chip ID - The WiLink chip ID. */ u32 hw_version; } __packed; enum wl1251_psm_mode { /* Active mode */ WL1251_PSM_CAM = 0, /* Power save mode */ WL1251_PSM_PS = 1, /* Extreme low power */ WL1251_PSM_ELP = 2, }; struct acx_sleep_auth { struct acx_header header; /* The sleep level authorization of the device. */ /* 0 - Always active*/ /* 1 - Power down mode: light / fast sleep*/ /* 2 - ELP mode: Deep / Max sleep*/ u8 sleep_auth; u8 padding[3]; } __packed; enum { HOSTIF_PCI_MASTER_HOST_INDIRECT, HOSTIF_PCI_MASTER_HOST_DIRECT, HOSTIF_SLAVE, HOSTIF_PKT_RING, HOSTIF_DONTCARE = 0xFF }; #define DEFAULT_UCAST_PRIORITY 0 #define DEFAULT_RX_Q_PRIORITY 0 #define DEFAULT_NUM_STATIONS 1 #define DEFAULT_RXQ_PRIORITY 0 /* low 0 .. 15 high */ #define DEFAULT_RXQ_TYPE 0x07 /* All frames, Data/Ctrl/Mgmt */ #define TRACE_BUFFER_MAX_SIZE 256 #define DP_RX_PACKET_RING_CHUNK_SIZE 1600 #define DP_TX_PACKET_RING_CHUNK_SIZE 1600 #define DP_RX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_PACKET_RING_CHUNK_NUM 2 #define DP_TX_COMPLETE_TIME_OUT 20 #define FW_TX_CMPLT_BLOCK_SIZE 16 struct acx_data_path_params { struct acx_header header; u16 rx_packet_ring_chunk_size; u16 tx_packet_ring_chunk_size; u8 rx_packet_ring_chunk_num; u8 tx_packet_ring_chunk_num; /* * Maximum number of packets that can be gathered * in the TX complete ring before an interrupt * is generated. */ u8 tx_complete_threshold; /* Number of pending TX complete entries in cyclic ring.*/ u8 tx_complete_ring_depth; /* * Max num microseconds since a packet enters the TX * complete ring until an interrupt is generated. */ u32 tx_complete_timeout; } __packed; struct acx_data_path_params_resp { struct acx_header header; u16 rx_packet_ring_chunk_size; u16 tx_packet_ring_chunk_size; u8 rx_packet_ring_chunk_num; u8 tx_packet_ring_chunk_num; u8 pad[2]; u32 rx_packet_ring_addr; u32 tx_packet_ring_addr; u32 rx_control_addr; u32 tx_control_addr; u32 tx_complete_addr; } __packed; #define TX_MSDU_LIFETIME_MIN 0 #define TX_MSDU_LIFETIME_MAX 3000 #define TX_MSDU_LIFETIME_DEF 512 #define RX_MSDU_LIFETIME_MIN 0 #define RX_MSDU_LIFETIME_MAX 0xFFFFFFFF #define RX_MSDU_LIFETIME_DEF 512000 struct acx_rx_msdu_lifetime { struct acx_header header; /* * The maximum amount of time, in TU, before the * firmware discards the MSDU. */ u32 lifetime; } __packed; /* * RX Config Options Table * Bit Definition * === ========== * 31:14 Reserved * 13 Copy RX Status - when set, write three receive status words * to top of rx'd MPDUs. * When cleared, do not write three status words (added rev 1.5) * 12 Reserved * 11 RX Complete upon FCS error - when set, give rx complete * interrupt for FCS errors, after the rx filtering, e.g. unicast * frames not to us with FCS error will not generate an interrupt. * 10 SSID Filter Enable - When set, the WiLink discards all beacon, * probe request, and probe response frames with an SSID that does * not match the SSID specified by the host in the START/JOIN * command. * When clear, the WiLink receives frames with any SSID. * 9 Broadcast Filter Enable - When set, the WiLink discards all * broadcast frames. When clear, the WiLink receives all received * broadcast frames. * 8:6 Reserved * 5 BSSID Filter Enable - When set, the WiLink discards any frames * with a BSSID that does not match the BSSID specified by the * host. * When clear, the WiLink receives frames from any BSSID. * 4 MAC Addr Filter - When set, the WiLink discards any frames * with a destination address that does not match the MAC address * of the adaptor. * When clear, the WiLink receives frames destined to any MAC * address. * 3 Promiscuous - When set, the WiLink receives all valid frames * (i.e., all frames that pass the FCS check). * When clear, only frames that pass the other filters specified * are received. * 2 FCS - When set, the WiLink includes the FCS with the received * frame. * When cleared, the FCS is discarded. * 1 PLCP header - When set, write all data from baseband to frame * buffer including PHY header. * 0 Reserved - Always equal to 0. * * RX Filter Options Table * Bit Definition * === ========== * 31:12 Reserved - Always equal to 0. * 11 Association - When set, the WiLink receives all association * related frames (association request/response, reassocation * request/response, and disassociation). When clear, these frames * are discarded. * 10 Auth/De auth - When set, the WiLink receives all authentication * and de-authentication frames. When clear, these frames are * discarded. * 9 Beacon - When set, the WiLink receives all beacon frames. * When clear, these frames are discarded. * 8 Contention Free - When set, the WiLink receives all contention * free frames. * When clear, these frames are discarded. * 7 Control - When set, the WiLink receives all control frames. * When clear, these frames are discarded. * 6 Data - When set, the WiLink receives all data frames. * When clear, these frames are discarded. * 5 FCS Error - When set, the WiLink receives frames that have FCS * errors. * When clear, these frames are discarded. * 4 Management - When set, the WiLink receives all management * frames. * When clear, these frames are discarded. * 3 Probe Request - When set, the WiLink receives all probe request * frames. * When clear, these frames are discarded. * 2 Probe Response - When set, the WiLink receives all probe * response frames. * When clear, these frames are discarded. * 1 RTS/CTS/ACK - When set, the WiLink receives all RTS, CTS and ACK * frames. * When clear, these frames are discarded. * 0 Rsvd Type/Sub Type - When set, the WiLink receives all frames * that have reserved frame types and sub types as defined by the * 802.11 specification. * When clear, these frames are discarded. */ struct acx_rx_config { struct acx_header header; u32 config_options; u32 filter_options; } __packed; enum { QOS_AC_BE = 0, QOS_AC_BK, QOS_AC_VI, QOS_AC_VO, QOS_HIGHEST_AC_INDEX = QOS_AC_VO, }; #define MAX_NUM_OF_AC (QOS_HIGHEST_AC_INDEX+1) #define FIRST_AC_INDEX QOS_AC_BE #define MAX_NUM_OF_802_1d_TAGS 8 #define AC_PARAMS_MAX_TSID 15 #define MAX_APSD_CONF 0xffff #define QOS_TX_HIGH_MIN (0) #define QOS_TX_HIGH_MAX (100) #define QOS_TX_HIGH_BK_DEF (25) #define QOS_TX_HIGH_BE_DEF (35) #define QOS_TX_HIGH_VI_DEF (35) #define QOS_TX_HIGH_VO_DEF (35) #define QOS_TX_LOW_BK_DEF (15) #define QOS_TX_LOW_BE_DEF (25) #define QOS_TX_LOW_VI_DEF (25) #define QOS_TX_LOW_VO_DEF (25) struct acx_tx_queue_qos_config { struct acx_header header; u8 qid; u8 pad[3]; /* Max number of blocks allowd in the queue */ u16 high_threshold; /* Lowest memory blocks guaranteed for this queue */ u16 low_threshold; } __packed; struct acx_packet_detection { struct acx_header header; u32 threshold; } __packed; enum acx_slot_type { SLOT_TIME_LONG = 0, SLOT_TIME_SHORT = 1, DEFAULT_SLOT_TIME = SLOT_TIME_SHORT, MAX_SLOT_TIMES = 0xFF }; #define STATION_WONE_INDEX 0 struct acx_slot { struct acx_header header; u8 wone_index; /* Reserved */ u8 slot_time; u8 reserved[6]; } __packed; #define ADDRESS_GROUP_MAX (8) #define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) struct acx_dot11_grp_addr_tbl { struct acx_header header; u8 enabled; u8 num_groups; u8 pad[2]; u8 mac_table[ADDRESS_GROUP_MAX_LEN]; } __packed; #define RX_TIMEOUT_PS_POLL_MIN 0 #define RX_TIMEOUT_PS_POLL_MAX (200000) #define RX_TIMEOUT_PS_POLL_DEF (15) #define RX_TIMEOUT_UPSD_MIN 0 #define RX_TIMEOUT_UPSD_MAX (200000) #define RX_TIMEOUT_UPSD_DEF (15) struct acx_rx_timeout { struct acx_header header; /* * The longest time the STA will wait to receive * traffic from the AP after a PS-poll has been * transmitted. */ u16 ps_poll_timeout; /* * The longest time the STA will wait to receive * traffic from the AP after a frame has been sent * from an UPSD enabled queue. */ u16 upsd_timeout; } __packed; #define RTS_THRESHOLD_MIN 0 #define RTS_THRESHOLD_MAX 4096 #define RTS_THRESHOLD_DEF 2347 struct acx_rts_threshold { struct acx_header header; u16 threshold; u8 pad[2]; } __packed; enum wl1251_acx_low_rssi_type { /* * The event is a "Level" indication which keeps triggering * as long as the average RSSI is below the threshold. */ WL1251_ACX_LOW_RSSI_TYPE_LEVEL = 0, /* * The event is an "Edge" indication which triggers * only when the RSSI threshold is crossed from above. */ WL1251_ACX_LOW_RSSI_TYPE_EDGE = 1, }; struct acx_low_rssi { struct acx_header header; /* * The threshold (in dBm) below (or above after low rssi * indication) which the firmware generates an interrupt to the * host. This parameter is signed. */ s8 threshold; /* * The weight of the current RSSI sample, before adding the new * sample, that is used to calculate the average RSSI. */ u8 weight; /* * The number of Beacons/Probe response frames that will be * received before issuing the Low or Regained RSSI event. */ u8 depth; /* * Configures how the Low RSSI Event is triggered. Refer to * enum wl1251_acx_low_rssi_type for more. */ u8 type; } __packed; struct acx_beacon_filter_option { struct acx_header header; u8 enable; /* * The number of beacons without the unicast TIM * bit set that the firmware buffers before * signaling the host about ready frames. * When set to 0 and the filter is enabled, beacons * without the unicast TIM bit set are dropped. */ u8 max_num_beacons; u8 pad[2]; } __packed; /* * ACXBeaconFilterEntry (not 221) * Byte Offset Size (Bytes) Definition * =========== ============ ========== * 0 1 IE identifier * 1 1 Treatment bit mask * * ACXBeaconFilterEntry (221) * Byte Offset Size (Bytes) Definition * =========== ============ ========== * 0 1 IE identifier * 1 1 Treatment bit mask * 2 3 OUI * 5 1 Type * 6 2 Version * * * Treatment bit mask - The information element handling: * bit 0 - The information element is compared and transferred * in case of change. * bit 1 - The information element is transferred to the host * with each appearance or disappearance. * Note that both bits can be set at the same time. */ #define BEACON_FILTER_TABLE_MAX_IE_NUM (32) #define BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM (6) #define BEACON_FILTER_TABLE_IE_ENTRY_SIZE (2) #define BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE (6) #define BEACON_FILTER_TABLE_MAX_SIZE ((BEACON_FILTER_TABLE_MAX_IE_NUM * \ BEACON_FILTER_TABLE_IE_ENTRY_SIZE) + \ (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \ BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE)) #define BEACON_RULE_PASS_ON_CHANGE BIT(0) #define BEACON_RULE_PASS_ON_APPEARANCE BIT(1) #define BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN (37) struct acx_beacon_filter_ie_table { struct acx_header header; u8 num_ie; u8 pad[3]; u8 table[BEACON_FILTER_TABLE_MAX_SIZE]; } __packed; #define SYNCH_FAIL_DEFAULT_THRESHOLD 10 /* number of beacons */ #define NO_BEACON_DEFAULT_TIMEOUT (500) /* in microseconds */ struct acx_conn_monit_params { struct acx_header header; u32 synch_fail_thold; /* number of beacons missed */ u32 bss_lose_timeout; /* number of TU's from synch fail */ } __packed; enum { SG_ENABLE = 0, SG_DISABLE, SG_SENSE_NO_ACTIVITY, SG_SENSE_ACTIVE }; struct acx_bt_wlan_coex { struct acx_header header; /* * 0 -> PTA enabled * 1 -> PTA disabled * 2 -> sense no active mode, i.e. * an interrupt is sent upon * BT activity. * 3 -> PTA is switched on in response * to the interrupt sending. */ u8 enable; u8 pad[3]; } __packed; #define PTA_ANTENNA_TYPE_DEF (0) #define PTA_BT_HP_MAXTIME_DEF (2000) #define PTA_WLAN_HP_MAX_TIME_DEF (5000) #define PTA_SENSE_DISABLE_TIMER_DEF (1350) #define PTA_PROTECTIVE_RX_TIME_DEF (1500) #define PTA_PROTECTIVE_TX_TIME_DEF (1500) #define PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF (3000) #define PTA_SIGNALING_TYPE_DEF (1) #define PTA_AFH_LEVERAGE_ON_DEF (0) #define PTA_NUMBER_QUIET_CYCLE_DEF (0) #define PTA_MAX_NUM_CTS_DEF (3) #define PTA_NUMBER_OF_WLAN_PACKETS_DEF (2) #define PTA_NUMBER_OF_BT_PACKETS_DEF (2) #define PTA_PROTECTIVE_RX_TIME_FAST_DEF (1500) #define PTA_PROTECTIVE_TX_TIME_FAST_DEF (3000) #define PTA_CYCLE_TIME_FAST_DEF (8700) #define PTA_RX_FOR_AVALANCHE_DEF (5) #define PTA_ELP_HP_DEF (0) #define PTA_ANTI_STARVE_PERIOD_DEF (500) #define PTA_ANTI_STARVE_NUM_CYCLE_DEF (4) #define PTA_ALLOW_PA_SD_DEF (1) #define PTA_TIME_BEFORE_BEACON_DEF (6300) #define PTA_HPDM_MAX_TIME_DEF (1600) #define PTA_TIME_OUT_NEXT_WLAN_DEF (2550) #define PTA_AUTO_MODE_NO_CTS_DEF (0) #define PTA_BT_HP_RESPECTED_DEF (3) #define PTA_WLAN_RX_MIN_RATE_DEF (24) #define PTA_ACK_MODE_DEF (1) struct acx_bt_wlan_coex_param { struct acx_header header; /* * The minimum rate of a received WLAN packet in the STA, * during protective mode, of which a new BT-HP request * during this Rx will always be respected and gain the antenna. */ u32 min_rate; /* Max time the BT HP will be respected. */ u16 bt_hp_max_time; /* Max time the WLAN HP will be respected. */ u16 wlan_hp_max_time; /* * The time between the last BT activity * and the moment when the sense mode returns * to SENSE_INACTIVE. */ u16 sense_disable_timer; /* Time before the next BT HP instance */ u16 rx_time_bt_hp; u16 tx_time_bt_hp; /* range: 10-20000 default: 1500 */ u16 rx_time_bt_hp_fast; u16 tx_time_bt_hp_fast; /* range: 2000-65535 default: 8700 */ u16 wlan_cycle_fast; /* range: 0 - 15000 (Msec) default: 1000 */ u16 bt_anti_starvation_period; /* range 400-10000(Usec) default: 3000 */ u16 next_bt_lp_packet; /* Deafult: worst case for BT DH5 traffic */ u16 wake_up_beacon; /* range: 0-50000(Usec) default: 1050 */ u16 hp_dm_max_guard_time; /* * This is to prevent both BT & WLAN antenna * starvation. * Range: 100-50000(Usec) default:2550 */ u16 next_wlan_packet; /* 0 -> shared antenna */ u8 antenna_type; /* * 0 -> TI legacy * 1 -> Palau */ u8 signal_type; /* * BT AFH status * 0 -> no AFH * 1 -> from dedicated GPIO * 2 -> AFH on (from host) */ u8 afh_leverage_on; /* * The number of cycles during which no * TX will be sent after 1 cycle of RX * transaction in protective mode */ u8 quiet_cycle_num; /* * The maximum number of CTSs that will * be sent for receiving RX packet in * protective mode */ u8 max_cts; /* * The number of WLAN packets * transferred in common mode before * switching to BT. */ u8 wlan_packets_num; /* * The number of BT packets * transferred in common mode before * switching to WLAN. */ u8 bt_packets_num; /* range: 1-255 default: 5 */ u8 missed_rx_avalanche; /* range: 0-1 default: 1 */ u8 wlan_elp_hp; /* range: 0 - 15 default: 4 */ u8 bt_anti_starvation_cycles; u8 ack_mode_dual_ant; /* * Allow PA_SD assertion/de-assertion * during enabled BT activity. */ u8 pa_sd_enable; /* * Enable/Disable PTA in auto mode: * Support Both Active & P.S modes */ u8 pta_auto_mode_enable; /* range: 0 - 20 default: 1 */ u8 bt_hp_respected_num; } __packed; #define CCA_THRSH_ENABLE_ENERGY_D 0x140A #define CCA_THRSH_DISABLE_ENERGY_D 0xFFEF struct acx_energy_detection { struct acx_header header; /* The RX Clear Channel Assessment threshold in the PHY */ u16 rx_cca_threshold; u8 tx_energy_detection; u8 pad; } __packed; #define BCN_RX_TIMEOUT_DEF_VALUE 10000 #define BROADCAST_RX_TIMEOUT_DEF_VALUE 20000 #define RX_BROADCAST_IN_PS_DEF_VALUE 1 #define CONSECUTIVE_PS_POLL_FAILURE_DEF 4 struct acx_beacon_broadcast { struct acx_header header; u16 beacon_rx_timeout; u16 broadcast_timeout; /* Enables receiving of broadcast packets in PS mode */ u8 rx_broadcast_in_ps; /* Consecutive PS Poll failures before updating the host */ u8 ps_poll_threshold; u8 pad[2]; } __packed; struct acx_event_mask { struct acx_header header; u32 event_mask; u32 high_event_mask; /* Unused */ } __packed; #define CFG_RX_FCS BIT(2) #define CFG_RX_ALL_GOOD BIT(3) #define CFG_UNI_FILTER_EN BIT(4) #define CFG_BSSID_FILTER_EN BIT(5) #define CFG_MC_FILTER_EN BIT(6) #define CFG_MC_ADDR0_EN BIT(7) #define CFG_MC_ADDR1_EN BIT(8) #define CFG_BC_REJECT_EN BIT(9) #define CFG_SSID_FILTER_EN BIT(10) #define CFG_RX_INT_FCS_ERROR BIT(11) #define CFG_RX_INT_ENCRYPTED BIT(12) #define CFG_RX_WR_RX_STATUS BIT(13) #define CFG_RX_FILTER_NULTI BIT(14) #define CFG_RX_RESERVE BIT(15) #define CFG_RX_TIMESTAMP_TSF BIT(16) #define CFG_RX_RSV_EN BIT(0) #define CFG_RX_RCTS_ACK BIT(1) #define CFG_RX_PRSP_EN BIT(2) #define CFG_RX_PREQ_EN BIT(3) #define CFG_RX_MGMT_EN BIT(4) #define CFG_RX_FCS_ERROR BIT(5) #define CFG_RX_DATA_EN BIT(6) #define CFG_RX_CTL_EN BIT(7) #define CFG_RX_CF_EN BIT(8) #define CFG_RX_BCN_EN BIT(9) #define CFG_RX_AUTH_EN BIT(10) #define CFG_RX_ASSOC_EN BIT(11) #define SCAN_PASSIVE BIT(0) #define SCAN_5GHZ_BAND BIT(1) #define SCAN_TRIGGERED BIT(2) #define SCAN_PRIORITY_HIGH BIT(3) struct acx_fw_gen_frame_rates { struct acx_header header; u8 tx_ctrl_frame_rate; /* RATE_* */ u8 tx_ctrl_frame_mod; /* CCK_* or PBCC_* */ u8 tx_mgt_frame_rate; u8 tx_mgt_frame_mod; } __packed; /* STA MAC */ struct acx_dot11_station_id { struct acx_header header; u8 mac[ETH_ALEN]; u8 pad[2]; } __packed; struct acx_feature_config { struct acx_header header; u32 options; u32 data_flow_options; } __packed; struct acx_current_tx_power { struct acx_header header; u8 current_tx_power; u8 padding[3]; } __packed; struct acx_dot11_default_key { struct acx_header header; u8 id; u8 pad[3]; } __packed; struct acx_tsf_info { struct acx_header header; u32 current_tsf_msb; u32 current_tsf_lsb; u32 last_TBTT_msb; u32 last_TBTT_lsb; u8 last_dtim_count; u8 pad[3]; } __packed; enum acx_wake_up_event { WAKE_UP_EVENT_BEACON_BITMAP = 0x01, /* Wake on every Beacon*/ WAKE_UP_EVENT_DTIM_BITMAP = 0x02, /* Wake on every DTIM*/ WAKE_UP_EVENT_N_DTIM_BITMAP = 0x04, /* Wake on every Nth DTIM */ WAKE_UP_EVENT_N_BEACONS_BITMAP = 0x08, /* Wake on every Nth Beacon */ WAKE_UP_EVENT_BITS_MASK = 0x0F }; struct acx_wake_up_condition { struct acx_header header; u8 wake_up_event; /* Only one bit can be set */ u8 listen_interval; u8 pad[2]; } __packed; struct acx_aid { struct acx_header header; /* * To be set when associated with an AP. */ u16 aid; u8 pad[2]; } __packed; enum acx_preamble_type { ACX_PREAMBLE_LONG = 0, ACX_PREAMBLE_SHORT = 1 }; struct acx_preamble { struct acx_header header; /* * When set, the WiLink transmits the frames with a short preamble and * when cleared, the WiLink transmits the frames with a long preamble. */ u8 preamble; u8 padding[3]; } __packed; enum acx_ctsprotect_type { CTSPROTECT_DISABLE = 0, CTSPROTECT_ENABLE = 1 }; struct acx_ctsprotect { struct acx_header header; u8 ctsprotect; u8 padding[3]; } __packed; struct acx_tx_statistics { u32 internal_desc_overflow; } __packed; struct acx_rx_statistics { u32 out_of_mem; u32 hdr_overflow; u32 hw_stuck; u32 dropped; u32 fcs_err; u32 xfr_hint_trig; u32 path_reset; u32 reset_counter; } __packed; struct acx_dma_statistics { u32 rx_requested; u32 rx_errors; u32 tx_requested; u32 tx_errors; } __packed; struct acx_isr_statistics { /* host command complete */ u32 cmd_cmplt; /* fiqisr() */ u32 fiqs; /* (INT_STS_ND & INT_TRIG_RX_HEADER) */ u32 rx_headers; /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */ u32 rx_completes; /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */ u32 rx_mem_overflow; /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */ u32 rx_rdys; /* irqisr() */ u32 irqs; /* (INT_STS_ND & INT_TRIG_TX_PROC) */ u32 tx_procs; /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */ u32 decrypt_done; /* (INT_STS_ND & INT_TRIG_DMA0) */ u32 dma0_done; /* (INT_STS_ND & INT_TRIG_DMA1) */ u32 dma1_done; /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */ u32 tx_exch_complete; /* (INT_STS_ND & INT_TRIG_COMMAND) */ u32 commands; /* (INT_STS_ND & INT_TRIG_RX_PROC) */ u32 rx_procs; /* (INT_STS_ND & INT_TRIG_PM_802) */ u32 hw_pm_mode_changes; /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */ u32 host_acknowledges; /* (INT_STS_ND & INT_TRIG_PM_PCI) */ u32 pci_pm; /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */ u32 wakeups; /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */ u32 low_rssi; } __packed; struct acx_wep_statistics { /* WEP address keys configured */ u32 addr_key_count; /* default keys configured */ u32 default_key_count; u32 reserved; /* number of times that WEP key not found on lookup */ u32 key_not_found; /* number of times that WEP key decryption failed */ u32 decrypt_fail; /* WEP packets decrypted */ u32 packets; /* WEP decrypt interrupts */ u32 interrupt; } __packed; #define ACX_MISSED_BEACONS_SPREAD 10 struct acx_pwr_statistics { /* the amount of enters into power save mode (both PD & ELP) */ u32 ps_enter; /* the amount of enters into ELP mode */ u32 elp_enter; /* the amount of missing beacon interrupts to the host */ u32 missing_bcns; /* the amount of wake on host-access times */ u32 wake_on_host; /* the amount of wake on timer-expire */ u32 wake_on_timer_exp; /* the number of packets that were transmitted with PS bit set */ u32 tx_with_ps; /* the number of packets that were transmitted with PS bit clear */ u32 tx_without_ps; /* the number of received beacons */ u32 rcvd_beacons; /* the number of entering into PowerOn (power save off) */ u32 power_save_off; /* the number of entries into power save mode */ u16 enable_ps; /* * the number of exits from power save, not including failed PS * transitions */ u16 disable_ps; /* * the number of times the TSF counter was adjusted because * of drift */ u32 fix_tsf_ps; /* Gives statistics about the spread continuous missed beacons. * The 16 LSB are dedicated for the PS mode. * The 16 MSB are dedicated for the PS mode. * cont_miss_bcns_spread[0] - single missed beacon. * cont_miss_bcns_spread[1] - two continuous missed beacons. * cont_miss_bcns_spread[2] - three continuous missed beacons. * ... * cont_miss_bcns_spread[9] - ten and more continuous missed beacons. */ u32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; /* the number of beacons in awake mode */ u32 rcvd_awake_beacons; } __packed; struct acx_mic_statistics { u32 rx_pkts; u32 calc_failure; } __packed; struct acx_aes_statistics { u32 encrypt_fail; u32 decrypt_fail; u32 encrypt_packets; u32 decrypt_packets; u32 encrypt_interrupt; u32 decrypt_interrupt; } __packed; struct acx_event_statistics { u32 heart_beat; u32 calibration; u32 rx_mismatch; u32 rx_mem_empty; u32 rx_pool; u32 oom_late; u32 phy_transmit_error; u32 tx_stuck; } __packed; struct acx_ps_statistics { u32 pspoll_timeouts; u32 upsd_timeouts; u32 upsd_max_sptime; u32 upsd_max_apturn; u32 pspoll_max_apturn; u32 pspoll_utilization; u32 upsd_utilization; } __packed; struct acx_rxpipe_statistics { u32 rx_prep_beacon_drop; u32 descr_host_int_trig_rx_data; u32 beacon_buffer_thres_host_int_trig_rx_data; u32 missed_beacon_host_int_trig_rx_data; u32 tx_xfr_host_int_trig_rx_data; } __packed; struct acx_statistics { struct acx_header header; struct acx_tx_statistics tx; struct acx_rx_statistics rx; struct acx_dma_statistics dma; struct acx_isr_statistics isr; struct acx_wep_statistics wep; struct acx_pwr_statistics pwr; struct acx_aes_statistics aes; struct acx_mic_statistics mic; struct acx_event_statistics event; struct acx_ps_statistics ps; struct acx_rxpipe_statistics rxpipe; } __packed; #define ACX_MAX_RATE_CLASSES 8 #define ACX_RATE_MASK_UNSPECIFIED 0 #define ACX_RATE_RETRY_LIMIT 10 struct acx_rate_class { u32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; u8 reserved; } __packed; struct acx_rate_policy { struct acx_header header; u32 rate_class_cnt; struct acx_rate_class rate_class[ACX_MAX_RATE_CLASSES]; } __packed; struct wl1251_acx_memory { __le16 num_stations; /* number of STAs to be supported. */ u16 reserved_1; /* * Nmber of memory buffers for the RX mem pool. * The actual number may be less if there are * not enough blocks left for the minimum num * of TX ones. */ u8 rx_mem_block_num; u8 reserved_2; u8 num_tx_queues; /* From 1 to 16 */ u8 host_if_options; /* HOST_IF* */ u8 tx_min_mem_block_num; u8 num_ssid_profiles; __le16 debug_buffer_size; } __packed; #define ACX_RX_DESC_MIN 1 #define ACX_RX_DESC_MAX 127 #define ACX_RX_DESC_DEF 32 struct wl1251_acx_rx_queue_config { u8 num_descs; u8 pad; u8 type; u8 priority; __le32 dma_address; } __packed; #define ACX_TX_DESC_MIN 1 #define ACX_TX_DESC_MAX 127 #define ACX_TX_DESC_DEF 16 struct wl1251_acx_tx_queue_config { u8 num_descs; u8 pad[2]; u8 attributes; } __packed; #define MAX_TX_QUEUE_CONFIGS 5 #define MAX_TX_QUEUES 4 struct wl1251_acx_config_memory { struct acx_header header; struct wl1251_acx_memory mem_config; struct wl1251_acx_rx_queue_config rx_queue_config; struct wl1251_acx_tx_queue_config tx_queue_config[MAX_TX_QUEUE_CONFIGS]; } __packed; struct wl1251_acx_mem_map { struct acx_header header; void *code_start; void *code_end; void *wep_defkey_start; void *wep_defkey_end; void *sta_table_start; void *sta_table_end; void *packet_template_start; void *packet_template_end; void *queue_memory_start; void *queue_memory_end; void *packet_memory_pool_start; void *packet_memory_pool_end; void *debug_buffer1_start; void *debug_buffer1_end; void *debug_buffer2_start; void *debug_buffer2_end; /* Number of blocks FW allocated for TX packets */ u32 num_tx_mem_blocks; /* Number of blocks FW allocated for RX packets */ u32 num_rx_mem_blocks; } __packed; struct wl1251_acx_wr_tbtt_and_dtim { struct acx_header header; /* Time in TUs between two consecutive beacons */ u16 tbtt; /* * DTIM period * For BSS: Number of TBTTs in a DTIM period (range: 1-10) * For IBSS: value shall be set to 1 */ u8 dtim; u8 padding; } __packed; enum wl1251_acx_bet_mode { WL1251_ACX_BET_DISABLE = 0, WL1251_ACX_BET_ENABLE = 1, }; struct wl1251_acx_bet_enable { struct acx_header header; /* * Specifies if beacon early termination procedure is enabled or * disabled, see enum wl1251_acx_bet_mode. */ u8 enable; /* * Specifies the maximum number of consecutive beacons that may be * early terminated. After this number is reached at least one full * beacon must be correctly received in FW before beacon ET * resumes. Range 0 - 255. */ u8 max_consecutive; u8 padding[2]; } __packed; struct wl1251_acx_ac_cfg { struct acx_header header; /* * Access Category - The TX queue's access category * (refer to AccessCategory_enum) */ u8 ac; /* * The contention window minimum size (in slots) for * the access class. */ u8 cw_min; /* * The contention window maximum size (in slots) for * the access class. */ u16 cw_max; /* The AIF value (in slots) for the access class. */ u8 aifsn; u8 reserved; /* The TX Op Limit (in microseconds) for the access class. */ u16 txop_limit; } __packed; enum wl1251_acx_channel_type { CHANNEL_TYPE_DCF = 0, CHANNEL_TYPE_EDCF = 1, CHANNEL_TYPE_HCCA = 2, }; enum wl1251_acx_ps_scheme { /* regular ps: simple sending of packets */ WL1251_ACX_PS_SCHEME_LEGACY = 0, /* sending a packet triggers a unscheduled apsd downstream */ WL1251_ACX_PS_SCHEME_UPSD_TRIGGER = 1, /* a pspoll packet will be sent before every data packet */ WL1251_ACX_PS_SCHEME_LEGACY_PSPOLL = 2, /* scheduled apsd mode */ WL1251_ACX_PS_SCHEME_SAPSD = 3, }; enum wl1251_acx_ack_policy { WL1251_ACX_ACK_POLICY_LEGACY = 0, WL1251_ACX_ACK_POLICY_NO_ACK = 1, WL1251_ACX_ACK_POLICY_BLOCK = 2, }; struct wl1251_acx_tid_cfg { struct acx_header header; /* tx queue id number (0-7) */ u8 queue; /* channel access type for the queue, enum wl1251_acx_channel_type */ u8 type; /* EDCA: ac index (0-3), HCCA: traffic stream id (8-15) */ u8 tsid; /* ps scheme of the specified queue, enum wl1251_acx_ps_scheme */ u8 ps_scheme; /* the tx queue ack policy, enum wl1251_acx_ack_policy */ u8 ack_policy; u8 padding[3]; /* not supported */ u32 apsdconf[2]; } __packed; /************************************************************************* Host Interrupt Register (WiLink -> Host) **************************************************************************/ /* RX packet is ready in Xfer buffer #0 */ #define WL1251_ACX_INTR_RX0_DATA BIT(0) /* TX result(s) are in the TX complete buffer */ #define WL1251_ACX_INTR_TX_RESULT BIT(1) /* OBSOLETE */ #define WL1251_ACX_INTR_TX_XFR BIT(2) /* RX packet is ready in Xfer buffer #1 */ #define WL1251_ACX_INTR_RX1_DATA BIT(3) /* Event was entered to Event MBOX #A */ #define WL1251_ACX_INTR_EVENT_A BIT(4) /* Event was entered to Event MBOX #B */ #define WL1251_ACX_INTR_EVENT_B BIT(5) /* OBSOLETE */ #define WL1251_ACX_INTR_WAKE_ON_HOST BIT(6) /* Trace message on MBOX #A */ #define WL1251_ACX_INTR_TRACE_A BIT(7) /* Trace message on MBOX #B */ #define WL1251_ACX_INTR_TRACE_B BIT(8) /* Command processing completion */ #define WL1251_ACX_INTR_CMD_COMPLETE BIT(9) /* Init sequence is done */ #define WL1251_ACX_INTR_INIT_COMPLETE BIT(14) #define WL1251_ACX_INTR_ALL 0xFFFFFFFF enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, ACX_SLOT = 0x0004, ACX_QUEUE_HEAD = 0x0005, /* for MASTER mode only */ ACX_AC_CFG = 0x0007, ACX_MEM_MAP = 0x0008, ACX_AID = 0x000A, ACX_RADIO_PARAM = 0x000B, /* Not used */ ACX_CFG = 0x000C, /* Not used */ ACX_FW_REV = 0x000D, ACX_MEDIUM_USAGE = 0x000F, ACX_RX_CFG = 0x0010, ACX_TX_QUEUE_CFG = 0x0011, /* FIXME: only used by wl1251 */ ACX_BSS_IN_PS = 0x0012, /* for AP only */ ACX_STATISTICS = 0x0013, /* Debug API */ ACX_FEATURE_CFG = 0x0015, ACX_MISC_CFG = 0x0017, /* Not used */ ACX_TID_CFG = 0x001A, ACX_BEACON_FILTER_OPT = 0x001F, ACX_LOW_RSSI = 0x0020, ACX_NOISE_HIST = 0x0021, ACX_HDK_VERSION = 0x0022, /* ??? */ ACX_PD_THRESHOLD = 0x0023, ACX_DATA_PATH_PARAMS = 0x0024, /* WO */ ACX_DATA_PATH_RESP_PARAMS = 0x0024, /* RO */ ACX_CCA_THRESHOLD = 0x0025, ACX_EVENT_MBOX_MASK = 0x0026, #ifdef FW_RUNNING_AS_AP ACX_DTIM_PERIOD = 0x0027, /* for AP only */ #else ACX_WR_TBTT_AND_DTIM = 0x0027, /* STA only */ #endif ACX_ACI_OPTION_CFG = 0x0029, /* OBSOLETE (for 1251)*/ ACX_GPIO_CFG = 0x002A, /* Not used */ ACX_GPIO_SET = 0x002B, /* Not used */ ACX_PM_CFG = 0x002C, /* To Be Documented */ ACX_CONN_MONIT_PARAMS = 0x002D, ACX_AVERAGE_RSSI = 0x002E, /* Not used */ ACX_CONS_TX_FAILURE = 0x002F, ACX_BCN_DTIM_OPTIONS = 0x0031, ACX_SG_ENABLE = 0x0032, ACX_SG_CFG = 0x0033, ACX_ANTENNA_DIVERSITY_CFG = 0x0035, /* To Be Documented */ ACX_LOW_SNR = 0x0037, /* To Be Documented */ ACX_BEACON_FILTER_TABLE = 0x0038, ACX_ARP_IP_FILTER = 0x0039, ACX_ROAMING_STATISTICS_TBL = 0x003B, ACX_RATE_POLICY = 0x003D, ACX_CTS_PROTECTION = 0x003E, ACX_SLEEP_AUTH = 0x003F, ACX_PREAMBLE_TYPE = 0x0040, ACX_ERROR_CNT = 0x0041, ACX_FW_GEN_FRAME_RATES = 0x0042, ACX_IBSS_FILTER = 0x0044, ACX_SERVICE_PERIOD_TIMEOUT = 0x0045, ACX_TSF_INFO = 0x0046, ACX_CONFIG_PS_WMM = 0x0049, ACX_ENABLE_RX_DATA_FILTER = 0x004A, ACX_SET_RX_DATA_FILTER = 0x004B, ACX_GET_DATA_FILTER_STATISTICS = 0x004C, ACX_POWER_LEVEL_TABLE = 0x004D, ACX_BET_ENABLE = 0x0050, DOT11_STATION_ID = 0x1001, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_DEFAULT_KEY = 0x1010, DOT11_RX_DOT11_MODE = 0x1012, DOT11_RTS_THRESHOLD = 0x1013, DOT11_GROUP_ADDRESS_TBL = 0x1014, MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL, MAX_IE = 0xFFFF }; int wl1251_acx_frame_rates(struct wl1251 *wl, u8 ctrl_rate, u8 ctrl_mod, u8 mgt_rate, u8 mgt_mod); int wl1251_acx_station_id(struct wl1251 *wl); int wl1251_acx_default_key(struct wl1251 *wl, u8 key_id); int wl1251_acx_wake_up_conditions(struct wl1251 *wl, u8 wake_up_event, u8 listen_interval); int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth); int wl1251_acx_fw_version(struct wl1251 *wl, char *buf, size_t len); int wl1251_acx_tx_power(struct wl1251 *wl, int power); int wl1251_acx_feature_cfg(struct wl1251 *wl); int wl1251_acx_mem_map(struct wl1251 *wl, struct acx_header *mem_map, size_t len); int wl1251_acx_data_path_params(struct wl1251 *wl, struct acx_data_path_params_resp *data_path); int wl1251_acx_rx_msdu_life_time(struct wl1251 *wl, u32 life_time); int wl1251_acx_rx_config(struct wl1251 *wl, u32 config, u32 filter); int wl1251_acx_pd_threshold(struct wl1251 *wl); int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time); int wl1251_acx_group_address_tbl(struct wl1251 *wl); int wl1251_acx_service_period_timeout(struct wl1251 *wl); int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold); int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter); int wl1251_acx_beacon_filter_table(struct wl1251 *wl); int wl1251_acx_conn_monit_params(struct wl1251 *wl); int wl1251_acx_sg_enable(struct wl1251 *wl); int wl1251_acx_sg_cfg(struct wl1251 *wl); int wl1251_acx_cca_threshold(struct wl1251 *wl); int wl1251_acx_bcn_dtim_options(struct wl1251 *wl); int wl1251_acx_aid(struct wl1251 *wl, u16 aid); int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask); int wl1251_acx_low_rssi(struct wl1251 *wl, s8 threshold, u8 weight, u8 depth, enum wl1251_acx_low_rssi_type type); int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble); int wl1251_acx_cts_protect(struct wl1251 *wl, enum acx_ctsprotect_type ctsprotect); int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats); int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime); int wl1251_acx_rate_policies(struct wl1251 *wl); int wl1251_acx_mem_cfg(struct wl1251 *wl); int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim); int wl1251_acx_bet_enable(struct wl1251 *wl, enum wl1251_acx_bet_mode mode, u8 max_consecutive); int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, u8 aifs, u16 txop); int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, enum wl1251_acx_channel_type type, u8 tsid, enum wl1251_acx_ps_scheme ps_scheme, enum wl1251_acx_ack_policy ack_policy); #endif /* __WL1251_ACX_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/acx.c0000644000175000017500000005520712026211315022760 0ustar mcgrofmcgrof#include "acx.h" #include #include #include #include "wl1251.h" #include "reg.h" #include "cmd.h" #include "ps.h" int wl1251_acx_frame_rates(struct wl1251 *wl, u8 ctrl_rate, u8 ctrl_mod, u8 mgt_rate, u8 mgt_mod) { struct acx_fw_gen_frame_rates *rates; int ret; wl1251_debug(DEBUG_ACX, "acx frame rates"); rates = kzalloc(sizeof(*rates), GFP_KERNEL); if (!rates) { ret = -ENOMEM; goto out; } rates->tx_ctrl_frame_rate = ctrl_rate; rates->tx_ctrl_frame_mod = ctrl_mod; rates->tx_mgt_frame_rate = mgt_rate; rates->tx_mgt_frame_mod = mgt_mod; ret = wl1251_cmd_configure(wl, ACX_FW_GEN_FRAME_RATES, rates, sizeof(*rates)); if (ret < 0) { wl1251_error("Failed to set FW rates and modulation"); goto out; } out: kfree(rates); return ret; } int wl1251_acx_station_id(struct wl1251 *wl) { struct acx_dot11_station_id *mac; int ret, i; wl1251_debug(DEBUG_ACX, "acx dot11_station_id"); mac = kzalloc(sizeof(*mac), GFP_KERNEL); if (!mac) { ret = -ENOMEM; goto out; } for (i = 0; i < ETH_ALEN; i++) mac->mac[i] = wl->mac_addr[ETH_ALEN - 1 - i]; ret = wl1251_cmd_configure(wl, DOT11_STATION_ID, mac, sizeof(*mac)); if (ret < 0) goto out; out: kfree(mac); return ret; } int wl1251_acx_default_key(struct wl1251 *wl, u8 key_id) { struct acx_dot11_default_key *default_key; int ret; wl1251_debug(DEBUG_ACX, "acx dot11_default_key (%d)", key_id); default_key = kzalloc(sizeof(*default_key), GFP_KERNEL); if (!default_key) { ret = -ENOMEM; goto out; } default_key->id = key_id; ret = wl1251_cmd_configure(wl, DOT11_DEFAULT_KEY, default_key, sizeof(*default_key)); if (ret < 0) { wl1251_error("Couldn't set default key"); goto out; } wl->default_key = key_id; out: kfree(default_key); return ret; } int wl1251_acx_wake_up_conditions(struct wl1251 *wl, u8 wake_up_event, u8 listen_interval) { struct acx_wake_up_condition *wake_up; int ret; wl1251_debug(DEBUG_ACX, "acx wake up conditions"); wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); if (!wake_up) { ret = -ENOMEM; goto out; } wake_up->wake_up_event = wake_up_event; wake_up->listen_interval = listen_interval; ret = wl1251_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, wake_up, sizeof(*wake_up)); if (ret < 0) { wl1251_warning("could not set wake up conditions: %d", ret); goto out; } out: kfree(wake_up); return ret; } int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth) { struct acx_sleep_auth *auth; int ret; wl1251_debug(DEBUG_ACX, "acx sleep auth"); auth = kzalloc(sizeof(*auth), GFP_KERNEL); if (!auth) { ret = -ENOMEM; goto out; } auth->sleep_auth = sleep_auth; ret = wl1251_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); out: kfree(auth); return ret; } int wl1251_acx_fw_version(struct wl1251 *wl, char *buf, size_t len) { struct acx_revision *rev; int ret; wl1251_debug(DEBUG_ACX, "acx fw rev"); rev = kzalloc(sizeof(*rev), GFP_KERNEL); if (!rev) { ret = -ENOMEM; goto out; } ret = wl1251_cmd_interrogate(wl, ACX_FW_REV, rev, sizeof(*rev)); if (ret < 0) { wl1251_warning("ACX_FW_REV interrogate failed"); goto out; } /* be careful with the buffer sizes */ strncpy(buf, rev->fw_version, min(len, sizeof(rev->fw_version))); /* * if the firmware version string is exactly * sizeof(rev->fw_version) long or fw_len is less than * sizeof(rev->fw_version) it won't be null terminated */ buf[min(len, sizeof(rev->fw_version)) - 1] = '\0'; out: kfree(rev); return ret; } int wl1251_acx_tx_power(struct wl1251 *wl, int power) { struct acx_current_tx_power *acx; int ret; wl1251_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr"); if (power < 0 || power > 25) return -EINVAL; acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->current_tx_power = power * 10; ret = wl1251_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("configure of tx power failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_feature_cfg(struct wl1251 *wl) { struct acx_feature_config *feature; int ret; wl1251_debug(DEBUG_ACX, "acx feature cfg"); feature = kzalloc(sizeof(*feature), GFP_KERNEL); if (!feature) { ret = -ENOMEM; goto out; } /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ feature->data_flow_options = 0; feature->options = 0; ret = wl1251_cmd_configure(wl, ACX_FEATURE_CFG, feature, sizeof(*feature)); if (ret < 0) { wl1251_error("Couldn't set HW encryption"); goto out; } out: kfree(feature); return ret; } int wl1251_acx_mem_map(struct wl1251 *wl, struct acx_header *mem_map, size_t len) { int ret; wl1251_debug(DEBUG_ACX, "acx mem map"); ret = wl1251_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len); if (ret < 0) return ret; return 0; } int wl1251_acx_data_path_params(struct wl1251 *wl, struct acx_data_path_params_resp *resp) { struct acx_data_path_params *params; int ret; wl1251_debug(DEBUG_ACX, "acx data path params"); params = kzalloc(sizeof(*params), GFP_KERNEL); if (!params) { ret = -ENOMEM; goto out; } params->rx_packet_ring_chunk_size = DP_RX_PACKET_RING_CHUNK_SIZE; params->tx_packet_ring_chunk_size = DP_TX_PACKET_RING_CHUNK_SIZE; params->rx_packet_ring_chunk_num = DP_RX_PACKET_RING_CHUNK_NUM; params->tx_packet_ring_chunk_num = DP_TX_PACKET_RING_CHUNK_NUM; params->tx_complete_threshold = 1; params->tx_complete_ring_depth = FW_TX_CMPLT_BLOCK_SIZE; params->tx_complete_timeout = DP_TX_COMPLETE_TIME_OUT; ret = wl1251_cmd_configure(wl, ACX_DATA_PATH_PARAMS, params, sizeof(*params)); if (ret < 0) goto out; /* FIXME: shouldn't this be ACX_DATA_PATH_RESP_PARAMS? */ ret = wl1251_cmd_interrogate(wl, ACX_DATA_PATH_PARAMS, resp, sizeof(*resp)); if (ret < 0) { wl1251_warning("failed to read data path parameters: %d", ret); goto out; } else if (resp->header.cmd.status != CMD_STATUS_SUCCESS) { wl1251_warning("data path parameter acx status failed"); ret = -EIO; goto out; } out: kfree(params); return ret; } int wl1251_acx_rx_msdu_life_time(struct wl1251 *wl, u32 life_time) { struct acx_rx_msdu_lifetime *acx; int ret; wl1251_debug(DEBUG_ACX, "acx rx msdu life time"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->lifetime = life_time; ret = wl1251_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("failed to set rx msdu life time: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_rx_config(struct wl1251 *wl, u32 config, u32 filter) { struct acx_rx_config *rx_config; int ret; wl1251_debug(DEBUG_ACX, "acx rx config"); rx_config = kzalloc(sizeof(*rx_config), GFP_KERNEL); if (!rx_config) { ret = -ENOMEM; goto out; } rx_config->config_options = config; rx_config->filter_options = filter; ret = wl1251_cmd_configure(wl, ACX_RX_CFG, rx_config, sizeof(*rx_config)); if (ret < 0) { wl1251_warning("failed to set rx config: %d", ret); goto out; } out: kfree(rx_config); return ret; } int wl1251_acx_pd_threshold(struct wl1251 *wl) { struct acx_packet_detection *pd; int ret; wl1251_debug(DEBUG_ACX, "acx data pd threshold"); pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { ret = -ENOMEM; goto out; } /* FIXME: threshold value not set */ ret = wl1251_cmd_configure(wl, ACX_PD_THRESHOLD, pd, sizeof(*pd)); if (ret < 0) { wl1251_warning("failed to set pd threshold: %d", ret); goto out; } out: kfree(pd); return ret; } int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time) { struct acx_slot *slot; int ret; wl1251_debug(DEBUG_ACX, "acx slot"); slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) { ret = -ENOMEM; goto out; } slot->wone_index = STATION_WONE_INDEX; slot->slot_time = slot_time; ret = wl1251_cmd_configure(wl, ACX_SLOT, slot, sizeof(*slot)); if (ret < 0) { wl1251_warning("failed to set slot time: %d", ret); goto out; } out: kfree(slot); return ret; } int wl1251_acx_group_address_tbl(struct wl1251 *wl) { struct acx_dot11_grp_addr_tbl *acx; int ret; wl1251_debug(DEBUG_ACX, "acx group address tbl"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } /* MAC filtering */ acx->enabled = 0; acx->num_groups = 0; memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); ret = wl1251_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("failed to set group addr table: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_service_period_timeout(struct wl1251 *wl) { struct acx_rx_timeout *rx_timeout; int ret; rx_timeout = kzalloc(sizeof(*rx_timeout), GFP_KERNEL); if (!rx_timeout) { ret = -ENOMEM; goto out; } wl1251_debug(DEBUG_ACX, "acx service period timeout"); rx_timeout->ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF; rx_timeout->upsd_timeout = RX_TIMEOUT_UPSD_DEF; ret = wl1251_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT, rx_timeout, sizeof(*rx_timeout)); if (ret < 0) { wl1251_warning("failed to set service period timeout: %d", ret); goto out; } out: kfree(rx_timeout); return ret; } int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold) { struct acx_rts_threshold *rts; int ret; wl1251_debug(DEBUG_ACX, "acx rts threshold"); rts = kzalloc(sizeof(*rts), GFP_KERNEL); if (!rts) { ret = -ENOMEM; goto out; } rts->threshold = rts_threshold; ret = wl1251_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); if (ret < 0) { wl1251_warning("failed to set rts threshold: %d", ret); goto out; } out: kfree(rts); return ret; } int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter) { struct acx_beacon_filter_option *beacon_filter; int ret; wl1251_debug(DEBUG_ACX, "acx beacon filter opt"); beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); if (!beacon_filter) { ret = -ENOMEM; goto out; } beacon_filter->enable = enable_filter; beacon_filter->max_num_beacons = 0; ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_OPT, beacon_filter, sizeof(*beacon_filter)); if (ret < 0) { wl1251_warning("failed to set beacon filter opt: %d", ret); goto out; } out: kfree(beacon_filter); return ret; } int wl1251_acx_beacon_filter_table(struct wl1251 *wl) { struct acx_beacon_filter_ie_table *ie_table; int idx = 0; int ret; wl1251_debug(DEBUG_ACX, "acx beacon filter table"); ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL); if (!ie_table) { ret = -ENOMEM; goto out; } /* configure default beacon pass-through rules */ ie_table->num_ie = 1; ie_table->table[idx++] = BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN; ie_table->table[idx++] = BEACON_RULE_PASS_ON_APPEARANCE; ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, ie_table, sizeof(*ie_table)); if (ret < 0) { wl1251_warning("failed to set beacon filter table: %d", ret); goto out; } out: kfree(ie_table); return ret; } int wl1251_acx_conn_monit_params(struct wl1251 *wl) { struct acx_conn_monit_params *acx; int ret; wl1251_debug(DEBUG_ACX, "acx connection monitor parameters"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD; acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT; ret = wl1251_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("failed to set connection monitor " "parameters: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_sg_enable(struct wl1251 *wl) { struct acx_bt_wlan_coex *pta; int ret; wl1251_debug(DEBUG_ACX, "acx sg enable"); pta = kzalloc(sizeof(*pta), GFP_KERNEL); if (!pta) { ret = -ENOMEM; goto out; } pta->enable = SG_ENABLE; ret = wl1251_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); if (ret < 0) { wl1251_warning("failed to set softgemini enable: %d", ret); goto out; } out: kfree(pta); return ret; } int wl1251_acx_sg_cfg(struct wl1251 *wl) { struct acx_bt_wlan_coex_param *param; int ret; wl1251_debug(DEBUG_ACX, "acx sg cfg"); param = kzalloc(sizeof(*param), GFP_KERNEL); if (!param) { ret = -ENOMEM; goto out; } /* BT-WLAN coext parameters */ param->min_rate = RATE_INDEX_24MBPS; param->bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF; param->wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF; param->sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF; param->rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF; param->tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF; param->rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF; param->tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF; param->wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF; param->bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF; param->next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF; param->wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF; param->hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF; param->next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF; param->antenna_type = PTA_ANTENNA_TYPE_DEF; param->signal_type = PTA_SIGNALING_TYPE_DEF; param->afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF; param->quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF; param->max_cts = PTA_MAX_NUM_CTS_DEF; param->wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF; param->bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF; param->missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF; param->wlan_elp_hp = PTA_ELP_HP_DEF; param->bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF; param->ack_mode_dual_ant = PTA_ACK_MODE_DEF; param->pa_sd_enable = PTA_ALLOW_PA_SD_DEF; param->pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF; param->bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF; ret = wl1251_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { wl1251_warning("failed to set sg config: %d", ret); goto out; } out: kfree(param); return ret; } int wl1251_acx_cca_threshold(struct wl1251 *wl) { struct acx_energy_detection *detection; int ret; wl1251_debug(DEBUG_ACX, "acx cca threshold"); detection = kzalloc(sizeof(*detection), GFP_KERNEL); if (!detection) { ret = -ENOMEM; goto out; } detection->rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; detection->tx_energy_detection = 0; ret = wl1251_cmd_configure(wl, ACX_CCA_THRESHOLD, detection, sizeof(*detection)); if (ret < 0) wl1251_warning("failed to set cca threshold: %d", ret); out: kfree(detection); return ret; } int wl1251_acx_bcn_dtim_options(struct wl1251 *wl) { struct acx_beacon_broadcast *bb; int ret; wl1251_debug(DEBUG_ACX, "acx bcn dtim options"); bb = kzalloc(sizeof(*bb), GFP_KERNEL); if (!bb) { ret = -ENOMEM; goto out; } bb->beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; bb->broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; bb->rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE; bb->ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF; ret = wl1251_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb)); if (ret < 0) { wl1251_warning("failed to set rx config: %d", ret); goto out; } out: kfree(bb); return ret; } int wl1251_acx_aid(struct wl1251 *wl, u16 aid) { struct acx_aid *acx_aid; int ret; wl1251_debug(DEBUG_ACX, "acx aid"); acx_aid = kzalloc(sizeof(*acx_aid), GFP_KERNEL); if (!acx_aid) { ret = -ENOMEM; goto out; } acx_aid->aid = aid; ret = wl1251_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid)); if (ret < 0) { wl1251_warning("failed to set aid: %d", ret); goto out; } out: kfree(acx_aid); return ret; } int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask) { struct acx_event_mask *mask; int ret; wl1251_debug(DEBUG_ACX, "acx event mbox mask"); mask = kzalloc(sizeof(*mask), GFP_KERNEL); if (!mask) { ret = -ENOMEM; goto out; } /* high event mask is unused */ mask->high_event_mask = 0xffffffff; mask->event_mask = event_mask; ret = wl1251_cmd_configure(wl, ACX_EVENT_MBOX_MASK, mask, sizeof(*mask)); if (ret < 0) { wl1251_warning("failed to set acx_event_mbox_mask: %d", ret); goto out; } out: kfree(mask); return ret; } int wl1251_acx_low_rssi(struct wl1251 *wl, s8 threshold, u8 weight, u8 depth, enum wl1251_acx_low_rssi_type type) { struct acx_low_rssi *rssi; int ret; wl1251_debug(DEBUG_ACX, "acx low rssi"); rssi = kzalloc(sizeof(*rssi), GFP_KERNEL); if (!rssi) return -ENOMEM; rssi->threshold = threshold; rssi->weight = weight; rssi->depth = depth; rssi->type = type; ret = wl1251_cmd_configure(wl, ACX_LOW_RSSI, rssi, sizeof(*rssi)); if (ret < 0) wl1251_warning("failed to set low rssi threshold: %d", ret); kfree(rssi); return ret; } int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble) { struct acx_preamble *acx; int ret; wl1251_debug(DEBUG_ACX, "acx_set_preamble"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->preamble = preamble; ret = wl1251_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("Setting of preamble failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_cts_protect(struct wl1251 *wl, enum acx_ctsprotect_type ctsprotect) { struct acx_ctsprotect *acx; int ret; wl1251_debug(DEBUG_ACX, "acx_set_ctsprotect"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->ctsprotect = ctsprotect; ret = wl1251_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("Setting of ctsprotect failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime) { struct acx_tsf_info *tsf_info; int ret; tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL); if (!tsf_info) { ret = -ENOMEM; goto out; } ret = wl1251_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info, sizeof(*tsf_info)); if (ret < 0) { wl1251_warning("ACX_FW_REV interrogate failed"); goto out; } *mactime = tsf_info->current_tsf_lsb | ((u64)tsf_info->current_tsf_msb << 32); out: kfree(tsf_info); return ret; } int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats) { int ret; wl1251_debug(DEBUG_ACX, "acx statistics"); ret = wl1251_cmd_interrogate(wl, ACX_STATISTICS, stats, sizeof(*stats)); if (ret < 0) { wl1251_warning("acx statistics failed: %d", ret); return -ENOMEM; } return 0; } int wl1251_acx_rate_policies(struct wl1251 *wl) { struct acx_rate_policy *acx; int ret = 0; wl1251_debug(DEBUG_ACX, "acx rate policies"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } /* configure one default (one-size-fits-all) rate class */ acx->rate_class_cnt = 1; acx->rate_class[0].enabled_rates = ACX_RATE_MASK_UNSPECIFIED; acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT; acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT; acx->rate_class[0].aflags = 0; ret = wl1251_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("Setting of rate policies failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_mem_cfg(struct wl1251 *wl) { struct wl1251_acx_config_memory *mem_conf; int ret, i; wl1251_debug(DEBUG_ACX, "acx mem cfg"); mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL); if (!mem_conf) { ret = -ENOMEM; goto out; } /* memory config */ mem_conf->mem_config.num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); mem_conf->mem_config.rx_mem_block_num = 35; mem_conf->mem_config.tx_min_mem_block_num = 64; mem_conf->mem_config.num_tx_queues = MAX_TX_QUEUES; mem_conf->mem_config.host_if_options = HOSTIF_PKT_RING; mem_conf->mem_config.num_ssid_profiles = 1; mem_conf->mem_config.debug_buffer_size = cpu_to_le16(TRACE_BUFFER_MAX_SIZE); /* RX queue config */ mem_conf->rx_queue_config.dma_address = 0; mem_conf->rx_queue_config.num_descs = ACX_RX_DESC_DEF; mem_conf->rx_queue_config.priority = DEFAULT_RXQ_PRIORITY; mem_conf->rx_queue_config.type = DEFAULT_RXQ_TYPE; /* TX queue config */ for (i = 0; i < MAX_TX_QUEUES; i++) { mem_conf->tx_queue_config[i].num_descs = ACX_TX_DESC_DEF; mem_conf->tx_queue_config[i].attributes = i; } ret = wl1251_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); if (ret < 0) { wl1251_warning("wl1251 mem config failed: %d", ret); goto out; } out: kfree(mem_conf); return ret; } int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim) { struct wl1251_acx_wr_tbtt_and_dtim *acx; int ret; wl1251_debug(DEBUG_ACX, "acx tbtt and dtim"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->tbtt = tbtt; acx->dtim = dtim; ret = wl1251_cmd_configure(wl, ACX_WR_TBTT_AND_DTIM, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("failed to set tbtt and dtim: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_bet_enable(struct wl1251 *wl, enum wl1251_acx_bet_mode mode, u8 max_consecutive) { struct wl1251_acx_bet_enable *acx; int ret; wl1251_debug(DEBUG_ACX, "acx bet enable"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->enable = mode; acx->max_consecutive = max_consecutive; ret = wl1251_cmd_configure(wl, ACX_BET_ENABLE, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("wl1251 acx bet enable failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, u8 aifs, u16 txop) { struct wl1251_acx_ac_cfg *acx; int ret = 0; wl1251_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d " "aifs %d txop %d", ac, cw_min, cw_max, aifs, txop); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->ac = ac; acx->cw_min = cw_min; acx->cw_max = cw_max; acx->aifsn = aifs; acx->txop_limit = txop; ret = wl1251_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("acx ac cfg failed: %d", ret); goto out; } out: kfree(acx); return ret; } int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, enum wl1251_acx_channel_type type, u8 tsid, enum wl1251_acx_ps_scheme ps_scheme, enum wl1251_acx_ack_policy ack_policy) { struct wl1251_acx_tid_cfg *acx; int ret = 0; wl1251_debug(DEBUG_ACX, "acx tid cfg %d type %d tsid %d " "ps_scheme %d ack_policy %d", queue, type, tsid, ps_scheme, ack_policy); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { ret = -ENOMEM; goto out; } acx->queue = queue; acx->type = type; acx->tsid = tsid; acx->ps_scheme = ps_scheme; acx->ack_policy = ack_policy; ret = wl1251_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("acx tid cfg failed: %d", ret); goto out; } out: kfree(acx); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl1251/Kconfig0000644000175000017500000000172712026211315023342 0ustar mcgrofmcgrofmenuconfig WL1251 tristate "TI wl1251 driver support" depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS select FW_LOADER select CRC7 ---help--- This will enable TI wl1251 driver support. The drivers make use of the mac80211 stack. If you choose to build a module, it'll be called wl1251. Say N if unsure. config WL1251_SPI tristate "TI wl1251 SPI support" depends on WL1251 && SPI_MASTER ---help--- This module adds support for the SPI interface of adapters using TI wl1251 chipset. Select this if your platform is using the SPI bus. If you choose to build a module, it'll be called wl1251_spi. Say N if unsure. config WL1251_SDIO tristate "TI wl1251 SDIO support" depends on WL1251 && MMC ---help--- This module adds support for the SDIO interface of adapters using TI wl1251 chipset. Select this if your platform is using the SDIO bus. If you choose to build a module, it'll be called wl1251_sdio. Say N if unsure. compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/0000755000175000017500000000000012026211315022242 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/main.c0000644000175000017500000013461212026211315023341 0ustar mcgrofmcgrof/* * This file is part of wl1271 * * Copyright (C) 2008-2010 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" #include "../wlcore/io.h" #include "../wlcore/acx.h" #include "../wlcore/tx.h" #include "../wlcore/rx.h" #include "../wlcore/io.h" #include "../wlcore/boot.h" #include "wl12xx.h" #include "reg.h" #include "cmd.h" #include "acx.h" #include "debugfs.h" static char *fref_param; static char *tcxo_param; static struct wlcore_conf wl12xx_conf = { .sg = { .params = { [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10, [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180, [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10, [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180, [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10, [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80, [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10, [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80, [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8, [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8, [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20, [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32, [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28, [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50, [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10, [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20, [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75, [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15, [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27, [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17, /* active scan params */ [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, /* passive scan params */ [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800, [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200, [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, /* passive scan in dual antenna params */ [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0, [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0, /* general params */ [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, [CONF_SG_ANTENNA_CONFIGURATION] = 0, [CONF_SG_BEACON_MISS_PERCENT] = 60, [CONF_SG_DHCP_TIME] = 5000, [CONF_SG_RXT] = 1200, [CONF_SG_TXT] = 1000, [CONF_SG_ADAPTIVE_RXT_TXT] = 1, [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, [CONF_SG_HV3_MAX_SERVED] = 6, [CONF_SG_PS_POLL_TIMEOUT] = 10, [CONF_SG_UPSD_TIMEOUT] = 10, [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5, [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30, /* AP params */ [CONF_AP_BEACON_MISS_TX] = 3, [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10, [CONF_AP_BEACON_WINDOW_INTERVAL] = 2, [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, /* CTS Diluting params */ [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, }, .state = CONF_SG_PROTECTIVE, }, .rx = { .rx_msdu_life_time = 512000, .packet_detection_threshold = 0, .ps_poll_timeout = 15, .upsd_timeout = 15, .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, .rx_cca_threshold = 0, .irq_blk_threshold = 0xFFFF, .irq_pkt_threshold = 0, .irq_timeout = 600, .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, }, .tx = { .tx_energy_detection = 0, .sta_rc_conf = { .enabled_rates = 0, .short_retry_limit = 10, .long_retry_limit = 10, .aflags = 0, }, .ac_conf_count = 4, .ac_conf = { [CONF_TX_AC_BE] = { .ac = CONF_TX_AC_BE, .cw_min = 15, .cw_max = 63, .aifsn = 3, .tx_op_limit = 0, }, [CONF_TX_AC_BK] = { .ac = CONF_TX_AC_BK, .cw_min = 15, .cw_max = 63, .aifsn = 7, .tx_op_limit = 0, }, [CONF_TX_AC_VI] = { .ac = CONF_TX_AC_VI, .cw_min = 15, .cw_max = 63, .aifsn = CONF_TX_AIFS_PIFS, .tx_op_limit = 3008, }, [CONF_TX_AC_VO] = { .ac = CONF_TX_AC_VO, .cw_min = 15, .cw_max = 63, .aifsn = CONF_TX_AIFS_PIFS, .tx_op_limit = 1504, }, }, .max_tx_retries = 100, .ap_aging_period = 300, .tid_conf_count = 4, .tid_conf = { [CONF_TX_AC_BE] = { .queue_id = CONF_TX_AC_BE, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_BE, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, [CONF_TX_AC_BK] = { .queue_id = CONF_TX_AC_BK, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_BK, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, [CONF_TX_AC_VI] = { .queue_id = CONF_TX_AC_VI, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_VI, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, [CONF_TX_AC_VO] = { .queue_id = CONF_TX_AC_VO, .channel_type = CONF_CHANNEL_TYPE_EDCF, .tsid = CONF_TX_AC_VO, .ps_scheme = CONF_PS_SCHEME_LEGACY, .ack_policy = CONF_ACK_POLICY_LEGACY, .apsd_conf = {0, 0}, }, }, .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, .tx_compl_timeout = 700, .tx_compl_threshold = 4, .basic_rate = CONF_HW_BIT_RATE_1MBPS, .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, .tmpl_short_retry_limit = 10, .tmpl_long_retry_limit = 10, .tx_watchdog_timeout = 5000, }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .listen_interval = 1, .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, .suspend_listen_interval = 3, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, .bcn_filt_ie_count = 3, .bcn_filt_ie = { [0] = { .ie = WLAN_EID_CHANNEL_SWITCH, .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, }, [1] = { .ie = WLAN_EID_HT_OPERATION, .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, [2] = { .ie = WLAN_EID_ERP_INFO, .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, }, .synch_fail_thold = 12, .bss_lose_timeout = 400, .beacon_rx_timeout = 10000, .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, .ps_poll_threshold = 10, .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 50, .psm_entry_retries = 8, .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, .dynamic_ps_timeout = 1500, .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, .sta_sleep_auth = WL1271_PSM_ILLEGAL, }, .itrim = { .enable = false, .timeout = 50000, }, .pm_config = { .host_clk_settling_time = 5000, .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE, }, .roam_trigger = { .trigger_pacing = 1, .avg_weight_rssi_beacon = 20, .avg_weight_rssi_data = 10, .avg_weight_snr_beacon = 20, .avg_weight_snr_data = 10, }, .scan = { .min_dwell_time_active = 7500, .max_dwell_time_active = 30000, .min_dwell_time_passive = 100000, .max_dwell_time_passive = 100000, .num_probe_reqs = 2, .split_scan_timeout = 50000, }, .sched_scan = { /* * Values are in TU/1000 but since sched scan FW command * params are in TUs rounding up may occur. */ .base_dwell_time = 7500, .max_dwell_time_delta = 22500, /* based on 250bits per probe @1Mbps */ .dwell_time_delta_per_probe = 2000, /* based on 250bits per probe @6Mbps (plus a bit more) */ .dwell_time_delta_per_probe_5 = 350, .dwell_time_passive = 100000, .dwell_time_dfs = 150000, .num_probe_reqs = 2, .rssi_threshold = -90, .snr_threshold = 0, }, .ht = { .rx_ba_win_size = 8, .tx_ba_win_size = 64, .inactivity_timeout = 10000, .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP, }, /* * Memory config for wl127x chips is given in the * wl12xx_default_priv_conf struct. The below configuration is * for wl128x chips. */ .mem = { .num_stations = 1, .ssid_profiles = 1, .rx_block_num = 40, .tx_min_block_num = 40, .dynamic_memory = 1, .min_req_tx_blocks = 45, .min_req_rx_blocks = 22, .tx_min = 27, }, .fm_coex = { .enable = true, .swallow_period = 5, .n_divider_fref_set_1 = 0xff, /* default */ .n_divider_fref_set_2 = 12, .m_divider_fref_set_1 = 0xffff, .m_divider_fref_set_2 = 148, /* default */ .coex_pll_stabilization_time = 0xffffffff, /* default */ .ldo_stabilization_time = 0xffff, /* default */ .fm_disturbed_band_margin = 0xff, /* default */ .swallow_clk_diff = 0xff, /* default */ }, .rx_streaming = { .duration = 150, .queues = 0x1, .interval = 20, .always = 0, }, .fwlog = { .mode = WL12XX_FWLOG_ON_DEMAND, .mem_blocks = 2, .severity = 0, .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, .output = WL12XX_FWLOG_OUTPUT_HOST, .threshold = 0, }, .rate = { .rate_retry_score = 32000, .per_add = 8192, .per_th1 = 2048, .per_th2 = 4096, .max_per = 8100, .inverse_curiosity_factor = 5, .tx_fail_low_th = 4, .tx_fail_high_th = 10, .per_alpha_shift = 4, .per_add_shift = 13, .per_beta1_shift = 10, .per_beta2_shift = 8, .rate_check_up = 2, .rate_check_down = 12, .rate_retry_policy = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, .hangover = { .recover_time = 0, .hangover_period = 20, .dynamic_mode = 1, .early_termination_mode = 1, .max_period = 20, .min_period = 1, .increase_delta = 1, .decrease_delta = 2, .quiet_time = 4, .increase_time = 1, .window_size = 16, }, }; static struct wl12xx_priv_conf wl12xx_default_priv_conf = { .rf = { .tx_per_channel_power_compensation_2 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, .tx_per_channel_power_compensation_5 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, .mem_wl127x = { .num_stations = 1, .ssid_profiles = 1, .rx_block_num = 70, .tx_min_block_num = 40, .dynamic_memory = 1, .min_req_tx_blocks = 100, .min_req_rx_blocks = 22, .tx_min = 27, }, }; #define WL12XX_TX_HW_BLOCK_SPARE_DEFAULT 1 #define WL12XX_TX_HW_BLOCK_GEM_SPARE 2 #define WL12XX_TX_HW_BLOCK_SIZE 252 static const u8 wl12xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */ 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */ 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */ 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */ 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */ 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */ 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */ 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */ 11, /* WL12XX_CONF_HW_RXTX_RATE_54 */ 10, /* WL12XX_CONF_HW_RXTX_RATE_48 */ 9, /* WL12XX_CONF_HW_RXTX_RATE_36 */ 8, /* WL12XX_CONF_HW_RXTX_RATE_24 */ /* TI-specific rate */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */ 7, /* WL12XX_CONF_HW_RXTX_RATE_18 */ 6, /* WL12XX_CONF_HW_RXTX_RATE_12 */ 3, /* WL12XX_CONF_HW_RXTX_RATE_11 */ 5, /* WL12XX_CONF_HW_RXTX_RATE_9 */ 4, /* WL12XX_CONF_HW_RXTX_RATE_6 */ 2, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */ 1, /* WL12XX_CONF_HW_RXTX_RATE_2 */ 0 /* WL12XX_CONF_HW_RXTX_RATE_1 */ }; static const u8 wl12xx_rate_to_idx_5ghz[] = { /* MCS rates are used only with 11n */ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */ 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */ 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */ 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */ 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */ 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */ 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */ 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */ 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */ 7, /* WL12XX_CONF_HW_RXTX_RATE_54 */ 6, /* WL12XX_CONF_HW_RXTX_RATE_48 */ 5, /* WL12XX_CONF_HW_RXTX_RATE_36 */ 4, /* WL12XX_CONF_HW_RXTX_RATE_24 */ /* TI-specific rate */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */ 3, /* WL12XX_CONF_HW_RXTX_RATE_18 */ 2, /* WL12XX_CONF_HW_RXTX_RATE_12 */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_11 */ 1, /* WL12XX_CONF_HW_RXTX_RATE_9 */ 0, /* WL12XX_CONF_HW_RXTX_RATE_6 */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */ CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_2 */ CONF_HW_RXTX_RATE_UNSUPPORTED /* WL12XX_CONF_HW_RXTX_RATE_1 */ }; static const u8 *wl12xx_band_rate_to_idx[] = { [IEEE80211_BAND_2GHZ] = wl12xx_rate_to_idx_2ghz, [IEEE80211_BAND_5GHZ] = wl12xx_rate_to_idx_5ghz }; enum wl12xx_hw_rates { WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI = 0, WL12XX_CONF_HW_RXTX_RATE_MCS7, WL12XX_CONF_HW_RXTX_RATE_MCS6, WL12XX_CONF_HW_RXTX_RATE_MCS5, WL12XX_CONF_HW_RXTX_RATE_MCS4, WL12XX_CONF_HW_RXTX_RATE_MCS3, WL12XX_CONF_HW_RXTX_RATE_MCS2, WL12XX_CONF_HW_RXTX_RATE_MCS1, WL12XX_CONF_HW_RXTX_RATE_MCS0, WL12XX_CONF_HW_RXTX_RATE_54, WL12XX_CONF_HW_RXTX_RATE_48, WL12XX_CONF_HW_RXTX_RATE_36, WL12XX_CONF_HW_RXTX_RATE_24, WL12XX_CONF_HW_RXTX_RATE_22, WL12XX_CONF_HW_RXTX_RATE_18, WL12XX_CONF_HW_RXTX_RATE_12, WL12XX_CONF_HW_RXTX_RATE_11, WL12XX_CONF_HW_RXTX_RATE_9, WL12XX_CONF_HW_RXTX_RATE_6, WL12XX_CONF_HW_RXTX_RATE_5_5, WL12XX_CONF_HW_RXTX_RATE_2, WL12XX_CONF_HW_RXTX_RATE_1, WL12XX_CONF_HW_RXTX_RATE_MAX, }; static struct wlcore_partition_set wl12xx_ptable[PART_TABLE_LEN] = { [PART_DOWN] = { .mem = { .start = 0x00000000, .size = 0x000177c0 }, .reg = { .start = REGISTERS_BASE, .size = 0x00008800 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, [PART_BOOT] = { /* in wl12xx we can use a mix of work and down * partition here */ .mem = { .start = 0x00040000, .size = 0x00014fc0 }, .reg = { .start = REGISTERS_BASE, .size = 0x00008800 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, [PART_WORK] = { .mem = { .start = 0x00040000, .size = 0x00014fc0 }, .reg = { .start = REGISTERS_BASE, .size = 0x0000a000 }, .mem2 = { .start = 0x003004f8, .size = 0x00000004 }, .mem3 = { .start = 0x00040404, .size = 0x00000000 }, }, [PART_DRPW] = { .mem = { .start = 0x00040000, .size = 0x00014fc0 }, .reg = { .start = DRPW_BASE, .size = 0x00006000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 } } }; static const int wl12xx_rtable[REG_TABLE_LEN] = { [REG_ECPU_CONTROL] = WL12XX_REG_ECPU_CONTROL, [REG_INTERRUPT_NO_CLEAR] = WL12XX_REG_INTERRUPT_NO_CLEAR, [REG_INTERRUPT_ACK] = WL12XX_REG_INTERRUPT_ACK, [REG_COMMAND_MAILBOX_PTR] = WL12XX_REG_COMMAND_MAILBOX_PTR, [REG_EVENT_MAILBOX_PTR] = WL12XX_REG_EVENT_MAILBOX_PTR, [REG_INTERRUPT_TRIG] = WL12XX_REG_INTERRUPT_TRIG, [REG_INTERRUPT_MASK] = WL12XX_REG_INTERRUPT_MASK, [REG_PC_ON_RECOVERY] = WL12XX_SCR_PAD4, [REG_CHIP_ID_B] = WL12XX_CHIP_ID_B, [REG_CMD_MBOX_ADDRESS] = WL12XX_CMD_MBOX_ADDRESS, /* data access memory addresses, used with partition translation */ [REG_SLV_MEM_DATA] = WL1271_SLV_MEM_DATA, [REG_SLV_REG_DATA] = WL1271_SLV_REG_DATA, /* raw data access memory addresses */ [REG_RAW_FW_STATUS_ADDR] = FW_STATUS_ADDR, }; /* TODO: maybe move to a new header file? */ #define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-5-mr.bin" #define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-5-sr.bin" #define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-5-plt.bin" #define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-5-mr.bin" #define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-5-sr.bin" #define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-5-plt.bin" static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) { int ret; if (wl->chip.id != CHIP_ID_1283_PG20) { struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; struct wl127x_rx_mem_pool_addr rx_mem_addr; /* * Choose the block we want to read * For aggregated packets, only the first memory block * should be retrieved. The FW takes care of the rest. */ u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK; rx_mem_addr.addr = (mem_block << 8) + le32_to_cpu(wl_mem_map->packet_memory_pool_start); rx_mem_addr.addr_extra = rx_mem_addr.addr + 4; ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr, sizeof(rx_mem_addr), false); if (ret < 0) return ret; } return 0; } static int wl12xx_identify_chip(struct wl1271 *wl) { int ret = 0; switch (wl->chip.id) { case CHIP_ID_1271_PG10: wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", wl->chip.id); wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_TKIP_HEADER_SPACE; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, sizeof(wl->conf.mem)); /* read data preparation is only needed by wl127x */ wl->ops->prepare_read = wl127x_prepare_read; wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, WL127X_MINOR_VER); break; case CHIP_ID_1271_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl->chip.id); wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_TKIP_HEADER_SPACE; wl->plt_fw_name = WL127X_PLT_FW_NAME; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, sizeof(wl->conf.mem)); /* read data preparation is only needed by wl127x */ wl->ops->prepare_read = wl127x_prepare_read; wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, WL127X_MINOR_VER); break; case CHIP_ID_1283_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)", wl->chip.id); wl->plt_fw_name = WL128X_PLT_FW_NAME; wl->sr_fw_name = WL128X_FW_NAME_SINGLE; wl->mr_fw_name = WL128X_FW_NAME_MULTI; /* wl128x requires TX blocksize alignment */ wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_TKIP_HEADER_SPACE; wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER, WL128X_MAJOR_VER, WL128X_SUBTYPE_VER, WL128X_MINOR_VER); break; case CHIP_ID_1283_PG10: default: wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; goto out; } out: return ret; } static int __must_check wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) { int ret; /* write address >> 1 + 0x30000 to OCP_POR_CTR */ addr = (addr >> 1) + 0x30000; ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr); if (ret < 0) goto out; /* write value to OCP_POR_WDATA */ ret = wlcore_write32(wl, WL12XX_OCP_DATA_WRITE, val); if (ret < 0) goto out; /* write 1 to OCP_CMD */ ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE); if (ret < 0) goto out; out: return ret; } static int __must_check wl12xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out) { u32 val; int timeout = OCP_CMD_LOOP; int ret; /* write address >> 1 + 0x30000 to OCP_POR_CTR */ addr = (addr >> 1) + 0x30000; ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr); if (ret < 0) return ret; /* write 2 to OCP_CMD */ ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ); if (ret < 0) return ret; /* poll for data ready */ do { ret = wlcore_read32(wl, WL12XX_OCP_DATA_READ, &val); if (ret < 0) return ret; } while (!(val & OCP_READY_MASK) && --timeout); if (!timeout) { wl1271_warning("Top register access timed out."); return -ETIMEDOUT; } /* check data status and return if OK */ if ((val & OCP_STATUS_MASK) != OCP_STATUS_OK) { wl1271_warning("Top register access returned error."); return -EIO; } if (out) *out = val & 0xffff; return 0; } static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) { u16 spare_reg; int ret; /* Mask bits [2] & [8:4] in the sys_clk_cfg register */ ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg); if (ret < 0) return ret; if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= (BIT(3) | BIT(5) | BIT(6)); ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); if (ret < 0) return ret; /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */ ret = wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG, WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); if (ret < 0) return ret; /* Delay execution for 15msec, to let the HW settle */ mdelay(15); return 0; } static bool wl128x_is_tcxo_valid(struct wl1271 *wl) { u16 tcxo_detection; int ret; ret = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG, &tcxo_detection); if (ret < 0) return false; if (tcxo_detection & TCXO_DET_FAILED) return false; return true; } static bool wl128x_is_fref_valid(struct wl1271 *wl) { u16 fref_detection; int ret; ret = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG, &fref_detection); if (ret < 0) return false; if (fref_detection & FREF_CLK_DETECT_FAIL) return false; return true; } static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl) { int ret; ret = wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); if (ret < 0) goto out; ret = wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); if (ret < 0) goto out; ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL); out: return ret; } static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) { u16 spare_reg; u16 pll_config; u8 input_freq; struct wl12xx_priv *priv = wl->priv; int ret; /* Mask bits [3:1] in the sys_clk_cfg register */ ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg); if (ret < 0) return ret; if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= BIT(2); ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); if (ret < 0) return ret; /* Handle special cases of the TCXO clock */ if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6) return wl128x_manually_configure_mcs_pll(wl); /* Set the input frequency according to the selected clock source */ input_freq = (clk & 1) + 1; ret = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG, &pll_config); if (ret < 0) return ret; if (pll_config == 0xFFFF) return -EFAULT; pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); pll_config |= MCS_PLL_ENABLE_HP; ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); return ret; } /* * WL128x has two clocks input - TCXO and FREF. * TCXO is the main clock of the device, while FREF is used to sync * between the GPS and the cellular modem. * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used * as the WLAN/BT main clock. */ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) { struct wl12xx_priv *priv = wl->priv; u16 sys_clk_cfg; int ret; /* For XTAL-only modes, FREF will be used after switching from TCXO */ if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL || priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; } /* Query the HW, to determine which clock source we should use */ ret = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG, &sys_clk_cfg); if (ret < 0) return ret; if (sys_clk_cfg == 0xFFFF) return -EINVAL; if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF) goto fref_clk; /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */ if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; } /* TCXO clock is selected */ if (!wl128x_is_tcxo_valid(wl)) return -EINVAL; *selected_clock = priv->tcxo_clock; goto config_mcs_pll; fref_clk: /* FREF clock is selected */ if (!wl128x_is_fref_valid(wl)) return -EINVAL; *selected_clock = priv->ref_clock; config_mcs_pll: return wl128x_configure_mcs_pll(wl, *selected_clock); } static int wl127x_boot_clk(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; u32 pause; u32 clk; int ret; if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3) wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION; if (priv->ref_clock == CONF_REF_CLK_19_2_E || priv->ref_clock == CONF_REF_CLK_38_4_E || priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; else if (priv->ref_clock == CONF_REF_CLK_26_E || priv->ref_clock == CONF_REF_CLK_26_M_XTAL || priv->ref_clock == CONF_REF_CLK_52_E) /* ref clk: 26/52 */ clk = 0x5; else return -EINVAL; if (priv->ref_clock != CONF_REF_CLK_19_2_E) { u16 val; /* Set clock type (open drain) */ ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE, &val); if (ret < 0) goto out; val &= FREF_CLK_TYPE_BITS; ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val); if (ret < 0) goto out; /* Set clock pull mode (no pull) */ ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL, &val); if (ret < 0) goto out; val |= NO_PULL; ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val); if (ret < 0) goto out; } else { u16 val; /* Set clock polarity */ ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY, &val); if (ret < 0) goto out; val &= FREF_CLK_POLARITY_BITS; val |= CLK_REQ_OUTN_SEL; ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); if (ret < 0) goto out; } ret = wlcore_write32(wl, WL12XX_PLL_PARAMETERS, clk); if (ret < 0) goto out; ret = wlcore_read32(wl, WL12XX_PLL_PARAMETERS, &pause); if (ret < 0) goto out; wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); pause &= ~(WU_COUNTER_PAUSE_VAL); pause |= WU_COUNTER_PAUSE_VAL; ret = wlcore_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause); out: return ret; } static int wl1271_boot_soft_reset(struct wl1271 *wl) { unsigned long timeout; u32 boot_data; int ret = 0; /* perform soft reset */ ret = wlcore_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); if (ret < 0) goto out; /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); while (1) { ret = wlcore_read32(wl, WL12XX_SLV_SOFT_RESET, &boot_data); if (ret < 0) goto out; wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) break; if (time_after(jiffies, timeout)) { /* 1.2 check pWhalBus->uSelfClearTime if the * timeout was reached */ wl1271_error("soft reset timeout"); return -1; } udelay(SOFT_RESET_STALL_TIME); } /* disable Rx/Tx */ ret = wlcore_write32(wl, WL12XX_ENABLE, 0x0); if (ret < 0) goto out; /* disable auto calibration on start*/ ret = wlcore_write32(wl, WL12XX_SPARE_A2, 0xffff); out: return ret; } static int wl12xx_pre_boot(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; int ret = 0; u32 clk; int selected_clock = -1; if (wl->chip.id == CHIP_ID_1283_PG20) { ret = wl128x_boot_clk(wl, &selected_clock); if (ret < 0) goto out; } else { ret = wl127x_boot_clk(wl); if (ret < 0) goto out; } /* Continue the ELP wake up sequence */ ret = wlcore_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); if (ret < 0) goto out; udelay(500); ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); if (ret < 0) goto out; /* Read-modify-write DRPW_SCRATCH_START register (see next state) to be used by DRPw FW. The RTRIM value will be added by the FW before taking DRPw out of reset */ ret = wlcore_read32(wl, WL12XX_DRPW_SCRATCH_START, &clk); if (ret < 0) goto out; wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); if (wl->chip.id == CHIP_ID_1283_PG20) clk |= ((selected_clock & 0x3) << 1) << 4; else clk |= (priv->ref_clock << 1) << 4; ret = wlcore_write32(wl, WL12XX_DRPW_SCRATCH_START, clk); if (ret < 0) goto out; ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]); if (ret < 0) goto out; /* Disable interrupts */ ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); if (ret < 0) goto out; ret = wl1271_boot_soft_reset(wl); if (ret < 0) goto out; out: return ret; } static int wl12xx_pre_upload(struct wl1271 *wl) { u32 tmp; u16 polarity; int ret; /* write firmware's last address (ie. it's length) to * ACX_EEPROMLESS_IND_REG */ wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); ret = wlcore_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND); if (ret < 0) goto out; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp); if (ret < 0) goto out; wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); /* 6. read the EEPROM parameters */ ret = wlcore_read32(wl, WL12XX_SCR_PAD2, &tmp); if (ret < 0) goto out; /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ if (wl->chip.id == CHIP_ID_1283_PG20) { ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); if (ret < 0) goto out; } /* polarity must be set before the firmware is loaded */ ret = wl12xx_top_reg_read(wl, OCP_REG_POLARITY, &polarity); if (ret < 0) goto out; /* We use HIGH polarity, so unset the LOW bit */ polarity &= ~POLARITY_LOW; ret = wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity); out: return ret; } static int wl12xx_enable_interrupts(struct wl1271 *wl) { int ret; ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL12XX_ACX_ALL_EVENTS_VECTOR); if (ret < 0) goto out; wlcore_enable_interrupts(wl); ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK)); if (ret < 0) goto out; ret = wlcore_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL); out: return ret; } static int wl12xx_boot(struct wl1271 *wl) { int ret; ret = wl12xx_pre_boot(wl); if (ret < 0) goto out; ret = wlcore_boot_upload_nvs(wl); if (ret < 0) goto out; ret = wl12xx_pre_upload(wl); if (ret < 0) goto out; ret = wlcore_boot_upload_firmware(wl); if (ret < 0) goto out; ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; ret = wl12xx_enable_interrupts(wl); out: return ret; } static int wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len) { int ret; ret = wlcore_write(wl, cmd_box_addr, buf, len, false); if (ret < 0) return ret; ret = wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD); return ret; } static int wl12xx_ack_event(struct wl1271 *wl) { return wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_EVENT_ACK); } static u32 wl12xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) { u32 blk_size = WL12XX_TX_HW_BLOCK_SIZE; u32 align_len = wlcore_calc_packet_alignment(wl, len); return (align_len + blk_size - 1) / blk_size + spare_blks; } static void wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, u32 blks, u32 spare_blks) { if (wl->chip.id == CHIP_ID_1283_PG20) { desc->wl128x_mem.total_mem_blocks = blks; } else { desc->wl127x_mem.extra_blocks = spare_blks; desc->wl127x_mem.total_mem_blocks = blks; } } static void wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) { u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len); if (wl->chip.id == CHIP_ID_1283_PG20) { desc->wl128x_mem.extra_bytes = aligned_len - skb->len; desc->length = cpu_to_le16(aligned_len >> 2); wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d extra: %d", desc->hlid, le16_to_cpu(desc->length), le16_to_cpu(desc->life_time), desc->wl128x_mem.total_mem_blocks, desc->wl128x_mem.extra_bytes); } else { /* calculate number of padding bytes */ int pad = aligned_len - skb->len; desc->tx_attr |= cpu_to_le16(pad << TX_HW_ATTR_OFST_LAST_WORD_PAD); /* Store the aligned length in terms of words */ desc->length = cpu_to_le16(aligned_len >> 2); wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d len: %d life: %d mem: %d", pad, desc->hlid, le16_to_cpu(desc->length), le16_to_cpu(desc->life_time), desc->wl127x_mem.total_mem_blocks); } } static enum wl_rx_buf_align wl12xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) { if (rx_desc & RX_BUF_UNALIGNED_PAYLOAD) return WLCORE_RX_BUF_UNALIGNED; return WLCORE_RX_BUF_ALIGNED; } static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, u32 data_len) { struct wl1271_rx_descriptor *desc = rx_data; /* invalid packet */ if (data_len < sizeof(*desc) || data_len < sizeof(*desc) + desc->pad_len) return 0; return data_len - sizeof(*desc) - desc->pad_len; } static int wl12xx_tx_delayed_compl(struct wl1271 *wl) { if (wl->fw_status_1->tx_results_counter == (wl->tx_results_count & 0xff)) return 0; return wlcore_tx_complete(wl); } static int wl12xx_hw_init(struct wl1271 *wl) { int ret; if (wl->chip.id == CHIP_ID_1283_PG20) { u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; ret = wl128x_cmd_general_parms(wl); if (ret < 0) goto out; /* * If we are in calibrator based auto detect then we got the FEM nr * in wl->fem_manuf. No need to continue further */ if (wl->plt_mode == PLT_FEM_DETECT) goto out; ret = wl128x_cmd_radio_parms(wl); if (ret < 0) goto out; if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) /* Enable SDIO padding */ host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; /* Must be before wl1271_acx_init_mem_config() */ ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap); if (ret < 0) goto out; } else { ret = wl1271_cmd_general_parms(wl); if (ret < 0) goto out; /* * If we are in calibrator based auto detect then we got the FEM nr * in wl->fem_manuf. No need to continue further */ if (wl->plt_mode == PLT_FEM_DETECT) goto out; ret = wl1271_cmd_radio_parms(wl); if (ret < 0) goto out; ret = wl1271_cmd_ext_radio_parms(wl); if (ret < 0) goto out; } out: return ret; } static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { return wlvif->rate_set; } static int wl12xx_identify_fw(struct wl1271 *wl) { unsigned int *fw_ver = wl->chip.fw_ver; /* Only new station firmwares support routing fw logs to the host */ if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN)) wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; /* This feature is not yet supported for AP mode */ if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; return 0; } static void wl12xx_conf_init(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; /* apply driver default configuration */ memcpy(&wl->conf, &wl12xx_conf, sizeof(wl12xx_conf)); /* apply default private configuration */ memcpy(&priv->conf, &wl12xx_default_priv_conf, sizeof(priv->conf)); } static bool wl12xx_mac_in_fuse(struct wl1271 *wl) { bool supported = false; u8 major, minor; if (wl->chip.id == CHIP_ID_1283_PG20) { major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver); minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver); /* in wl128x we have the MAC address if the PG is >= (2, 1) */ if (major > 2 || (major == 2 && minor >= 1)) supported = true; } else { major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver); minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver); /* in wl127x we have the MAC address if the PG is >= (3, 1) */ if (major == 3 && minor >= 1) supported = true; } wl1271_debug(DEBUG_PROBE, "PG Ver major = %d minor = %d, MAC %s present", major, minor, supported ? "is" : "is not"); return supported; } static int wl12xx_get_fuse_mac(struct wl1271 *wl) { u32 mac1, mac2; int ret; ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); if (ret < 0) goto out; ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1, &mac1); if (ret < 0) goto out; ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2, &mac2); if (ret < 0) goto out; /* these are the two parts of the BD_ADDR */ wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + ((mac1 & 0xff000000) >> 24); wl->fuse_nic_addr = mac1 & 0xffffff; ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); out: return ret; } static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver) { u16 die_info; int ret; if (wl->chip.id == CHIP_ID_1283_PG20) ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1, &die_info); else ret = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1, &die_info); if (ret >= 0 && ver) *ver = (s8)((die_info & PG_VER_MASK) >> PG_VER_OFFSET); return ret; } static int wl12xx_get_mac(struct wl1271 *wl) { if (wl12xx_mac_in_fuse(wl)) return wl12xx_get_fuse_mac(wl); return 0; } static void wl12xx_set_tx_desc_csum(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) { desc->wl12xx_reserved = 0; } static int wl12xx_plt_init(struct wl1271 *wl) { int ret; ret = wl->ops->boot(wl); if (ret < 0) goto out; ret = wl->ops->hw_init(wl); if (ret < 0) goto out_irq_disable; /* * If we are in calibrator based auto detect then we got the FEM nr * in wl->fem_manuf. No need to continue further */ if (wl->plt_mode == PLT_FEM_DETECT) goto out; ret = wl1271_acx_init_mem_config(wl); if (ret < 0) goto out_irq_disable; ret = wl12xx_acx_mem_cfg(wl); if (ret < 0) goto out_free_memmap; /* Enable data path */ ret = wl1271_cmd_data_path(wl, 1); if (ret < 0) goto out_free_memmap; /* Configure for CAM power saving (ie. always active) */ ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); if (ret < 0) goto out_free_memmap; /* configure PM */ ret = wl1271_acx_pm_config(wl); if (ret < 0) goto out_free_memmap; goto out; out_free_memmap: kfree(wl->target_mem_map); wl->target_mem_map = NULL; out_irq_disable: mutex_unlock(&wl->mutex); /* Unlocking the mutex in the middle of handling is inherently unsafe. In this case we deem it safe to do, because we need to let any possibly pending IRQ out of the system (and while we are WL1271_STATE_OFF the IRQ work function will not do anything.) Also, any other possible concurrent operations will fail due to the current state, hence the wl1271 struct should be safe. */ wlcore_disable_interrupts(wl); mutex_lock(&wl->mutex); out: return ret; } static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) { if (is_gem) return WL12XX_TX_HW_BLOCK_GEM_SPARE; return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT; } static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { return wlcore_set_key(wl, cmd, vif, sta, key_conf); } static struct wlcore_ops wl12xx_ops = { .identify_chip = wl12xx_identify_chip, .identify_fw = wl12xx_identify_fw, .boot = wl12xx_boot, .plt_init = wl12xx_plt_init, .trigger_cmd = wl12xx_trigger_cmd, .ack_event = wl12xx_ack_event, .calc_tx_blocks = wl12xx_calc_tx_blocks, .set_tx_desc_blocks = wl12xx_set_tx_desc_blocks, .set_tx_desc_data_len = wl12xx_set_tx_desc_data_len, .get_rx_buf_align = wl12xx_get_rx_buf_align, .get_rx_packet_len = wl12xx_get_rx_packet_len, .tx_immediate_compl = NULL, .tx_delayed_compl = wl12xx_tx_delayed_compl, .hw_init = wl12xx_hw_init, .init_vif = NULL, .sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask, .get_pg_ver = wl12xx_get_pg_ver, .get_mac = wl12xx_get_mac, .set_tx_desc_csum = wl12xx_set_tx_desc_csum, .set_rx_csum = NULL, .ap_get_mimo_wide_rate_mask = NULL, .debugfs_init = wl12xx_debugfs_add_files, .get_spare_blocks = wl12xx_get_spare_blocks, .set_key = wl12xx_set_key, .pre_pkt_send = NULL, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, .mcs = { .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, .rx_highest = cpu_to_le16(72), .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }; static int __devinit wl12xx_probe(struct platform_device *pdev) { struct wl12xx_platform_data *pdata = pdev->dev.platform_data; struct wl1271 *wl; struct ieee80211_hw *hw; struct wl12xx_priv *priv; hw = wlcore_alloc_hw(sizeof(*priv)); if (IS_ERR(hw)) { wl1271_error("can't allocate hw"); return PTR_ERR(hw); } wl = hw->priv; priv = wl->priv; wl->ops = &wl12xx_ops; wl->ptable = wl12xx_ptable; wl->rtable = wl12xx_rtable; wl->num_tx_desc = 16; wl->num_rx_desc = 8; wl->band_rate_to_idx = wl12xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = 0; wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics); wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap); wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap); wl12xx_conf_init(wl); if (!fref_param) { priv->ref_clock = pdata->board_ref_clock; } else { if (!strcmp(fref_param, "19.2")) priv->ref_clock = WL12XX_REFCLOCK_19; else if (!strcmp(fref_param, "26")) priv->ref_clock = WL12XX_REFCLOCK_26; else if (!strcmp(fref_param, "26x")) priv->ref_clock = WL12XX_REFCLOCK_26_XTAL; else if (!strcmp(fref_param, "38.4")) priv->ref_clock = WL12XX_REFCLOCK_38; else if (!strcmp(fref_param, "38.4x")) priv->ref_clock = WL12XX_REFCLOCK_38_XTAL; else if (!strcmp(fref_param, "52")) priv->ref_clock = WL12XX_REFCLOCK_52; else wl1271_error("Invalid fref parameter %s", fref_param); } if (!tcxo_param) { priv->tcxo_clock = pdata->board_tcxo_clock; } else { if (!strcmp(tcxo_param, "19.2")) priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2; else if (!strcmp(tcxo_param, "26")) priv->tcxo_clock = WL12XX_TCXOCLOCK_26; else if (!strcmp(tcxo_param, "38.4")) priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4; else if (!strcmp(tcxo_param, "52")) priv->tcxo_clock = WL12XX_TCXOCLOCK_52; else if (!strcmp(tcxo_param, "16.368")) priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368; else if (!strcmp(tcxo_param, "32.736")) priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736; else if (!strcmp(tcxo_param, "16.8")) priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8; else if (!strcmp(tcxo_param, "33.6")) priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6; else wl1271_error("Invalid tcxo parameter %s", tcxo_param); } return wlcore_probe(wl, pdev); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) static const struct platform_device_id wl12xx_id_table[] __devinitconst = { { "wl12xx", 0 }, { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(platform, wl12xx_id_table); #endif static struct platform_driver wl12xx_driver = { .probe = wl12xx_probe, .remove = __devexit_p(wlcore_remove), #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) .id_table = wl12xx_id_table, #endif .driver = { .name = "wl12xx_driver", .owner = THIS_MODULE, } }; static int __init wl12xx_init(void) { return platform_driver_register(&wl12xx_driver); } module_init(wl12xx_init); static void __exit wl12xx_exit(void) { platform_driver_unregister(&wl12xx_driver); } module_exit(wl12xx_exit); module_param_named(fref, fref_param, charp, 0); MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52"); module_param_named(tcxo, tcxo_param, charp, 0); MODULE_PARM_DESC(tcxo, "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); MODULE_FIRMWARE(WL127X_FW_NAME_MULTI); MODULE_FIRMWARE(WL127X_PLT_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE); MODULE_FIRMWARE(WL128X_FW_NAME_MULTI); MODULE_FIRMWARE(WL128X_PLT_FW_NAME); compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/wl12xx.h0000644000175000017500000000254512026211315023566 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL12XX_PRIV_H__ #define __WL12XX_PRIV_H__ #include "conf.h" /* minimum FW required for driver for wl127x */ #define WL127X_CHIP_VER 6 #define WL127X_IFTYPE_VER 3 #define WL127X_MAJOR_VER 10 #define WL127X_SUBTYPE_VER 2 #define WL127X_MINOR_VER 115 /* minimum FW required for driver for wl128x */ #define WL128X_CHIP_VER 7 #define WL128X_IFTYPE_VER 3 #define WL128X_MAJOR_VER 10 #define WL128X_SUBTYPE_VER 2 #define WL128X_MINOR_VER 115 struct wl127x_rx_mem_pool_addr { u32 addr; u32 addr_extra; }; struct wl12xx_priv { struct wl12xx_priv_conf conf; int ref_clock; int tcxo_clock; }; #endif /* __WL12XX_PRIV_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/reg.h0000644000175000017500000005011312026211315023170 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __REG_H__ #define __REG_H__ #include #define REGISTERS_BASE 0x00300000 #define DRPW_BASE 0x00310000 #define REGISTERS_DOWN_SIZE 0x00008800 #define REGISTERS_WORK_SIZE 0x0000b000 #define FW_STATUS_ADDR (0x14FC0 + 0xA000) /*=============================================== Host Software Reset - 32bit RW ------------------------------------------ [31:1] Reserved 0 SOFT_RESET Soft Reset - When this bit is set, it holds the Wlan hardware in a soft reset state. This reset disables all MAC and baseband processor clocks except the CardBus/PCI interface clock. It also initializes all MAC state machines except the host interface. It does not reload the contents of the EEPROM. When this bit is cleared (not self-clearing), the Wlan hardware exits the software reset state. ===============================================*/ #define WL12XX_SLV_SOFT_RESET (REGISTERS_BASE + 0x0000) #define WL1271_SLV_REG_DATA (REGISTERS_BASE + 0x0008) #define WL1271_SLV_REG_ADATA (REGISTERS_BASE + 0x000c) #define WL1271_SLV_MEM_DATA (REGISTERS_BASE + 0x0018) #define WL12XX_REG_INTERRUPT_TRIG (REGISTERS_BASE + 0x0474) #define WL12XX_REG_INTERRUPT_TRIG_H (REGISTERS_BASE + 0x0478) /*============================================= Host Interrupt Mask Register - 32bit (RW) ------------------------------------------ Setting a bit in this register masks the corresponding interrupt to the host. 0 - RX0 - Rx first dubble buffer Data Interrupt 1 - TXD - Tx Data Interrupt 2 - TXXFR - Tx Transfer Interrupt 3 - RX1 - Rx second dubble buffer Data Interrupt 4 - RXXFR - Rx Transfer Interrupt 5 - EVENT_A - Event Mailbox interrupt 6 - EVENT_B - Event Mailbox interrupt 7 - WNONHST - Wake On Host Interrupt 8 - TRACE_A - Debug Trace interrupt 9 - TRACE_B - Debug Trace interrupt 10 - CDCMP - Command Complete Interrupt 11 - 12 - 13 - 14 - ICOMP - Initialization Complete Interrupt 16 - SG SE - Soft Gemini - Sense enable interrupt 17 - SG SD - Soft Gemini - Sense disable interrupt 18 - - 19 - - 20 - - 21- - Default: 0x0001 *==============================================*/ #define WL12XX_REG_INTERRUPT_MASK (REGISTERS_BASE + 0x04DC) /*============================================= Host Interrupt Mask Set 16bit, (Write only) ------------------------------------------ Setting a bit in this register sets the corresponding bin in ACX_HINT_MASK register without effecting the mask state of other bits (0 = no effect). ==============================================*/ #define ACX_REG_HINT_MASK_SET (REGISTERS_BASE + 0x04E0) /*============================================= Host Interrupt Mask Clear 16bit,(Write only) ------------------------------------------ Setting a bit in this register clears the corresponding bin in ACX_HINT_MASK register without effecting the mask state of other bits (0 = no effect). =============================================*/ #define ACX_REG_HINT_MASK_CLR (REGISTERS_BASE + 0x04E4) /*============================================= Host Interrupt Status Nondestructive Read 16bit,(Read only) ------------------------------------------ The host can read this register to determine which interrupts are active. Reading this register doesn't effect its content. =============================================*/ #define WL12XX_REG_INTERRUPT_NO_CLEAR (REGISTERS_BASE + 0x04E8) /*============================================= Host Interrupt Status Clear on Read Register 16bit,(Read only) ------------------------------------------ The host can read this register to determine which interrupts are active. Reading this register clears it, thus making all interrupts inactive. ==============================================*/ #define ACX_REG_INTERRUPT_CLEAR (REGISTERS_BASE + 0x04F8) /*============================================= Host Interrupt Acknowledge Register 16bit,(Write only) ------------------------------------------ The host can set individual bits in this register to clear (acknowledge) the corresp. interrupt status bits in the HINT_STS_CLR and HINT_STS_ND registers, thus making the assotiated interrupt inactive. (0-no effect) ==============================================*/ #define WL12XX_REG_INTERRUPT_ACK (REGISTERS_BASE + 0x04F0) #define WL12XX_REG_RX_DRIVER_COUNTER (REGISTERS_BASE + 0x0538) /* Device Configuration registers*/ #define SOR_CFG (REGISTERS_BASE + 0x0800) /* Embedded ARM CPU Control */ /*=============================================== Halt eCPU - 32bit RW ------------------------------------------ 0 HALT_ECPU Halt Embedded CPU - This bit is the compliment of bit 1 (MDATA2) in the SOR_CFG register. During a hardware reset, this bit holds the inverse of MDATA2. When downloading firmware from the host, set this bit (pull down MDATA2). The host clears this bit after downloading the firmware into zero-wait-state SSRAM. When loading firmware from Flash, clear this bit (pull up MDATA2) so that the eCPU can run the bootloader code in Flash HALT_ECPU eCPU State -------------------- 1 halt eCPU 0 enable eCPU ===============================================*/ #define WL12XX_REG_ECPU_CONTROL (REGISTERS_BASE + 0x0804) #define WL12XX_HI_CFG (REGISTERS_BASE + 0x0808) /*=============================================== EEPROM Burst Read Start - 32bit RW ------------------------------------------ [31:1] Reserved 0 ACX_EE_START - EEPROM Burst Read Start 0 Setting this bit starts a burst read from the external EEPROM. If this bit is set (after reset) before an EEPROM read/write, the burst read starts at EEPROM address 0. Otherwise, it starts at the address following the address of the previous access. TheWlan hardware hardware clears this bit automatically. Default: 0x00000000 *================================================*/ #define ACX_REG_EE_START (REGISTERS_BASE + 0x080C) #define WL12XX_OCP_POR_CTR (REGISTERS_BASE + 0x09B4) #define WL12XX_OCP_DATA_WRITE (REGISTERS_BASE + 0x09B8) #define WL12XX_OCP_DATA_READ (REGISTERS_BASE + 0x09BC) #define WL12XX_OCP_CMD (REGISTERS_BASE + 0x09C0) #define WL12XX_HOST_WR_ACCESS (REGISTERS_BASE + 0x09F8) #define WL12XX_CHIP_ID_B (REGISTERS_BASE + 0x5674) #define WL12XX_ENABLE (REGISTERS_BASE + 0x5450) /* Power Management registers */ #define WL12XX_ELP_CFG_MODE (REGISTERS_BASE + 0x5804) #define WL12XX_ELP_CMD (REGISTERS_BASE + 0x5808) #define WL12XX_PLL_CAL_TIME (REGISTERS_BASE + 0x5810) #define WL12XX_CLK_REQ_TIME (REGISTERS_BASE + 0x5814) #define WL12XX_CLK_BUF_TIME (REGISTERS_BASE + 0x5818) #define WL12XX_CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820) /* Scratch Pad registers*/ #define WL12XX_SCR_PAD0 (REGISTERS_BASE + 0x5608) #define WL12XX_SCR_PAD1 (REGISTERS_BASE + 0x560C) #define WL12XX_SCR_PAD2 (REGISTERS_BASE + 0x5610) #define WL12XX_SCR_PAD3 (REGISTERS_BASE + 0x5614) #define WL12XX_SCR_PAD4 (REGISTERS_BASE + 0x5618) #define WL12XX_SCR_PAD4_SET (REGISTERS_BASE + 0x561C) #define WL12XX_SCR_PAD4_CLR (REGISTERS_BASE + 0x5620) #define WL12XX_SCR_PAD5 (REGISTERS_BASE + 0x5624) #define WL12XX_SCR_PAD5_SET (REGISTERS_BASE + 0x5628) #define WL12XX_SCR_PAD5_CLR (REGISTERS_BASE + 0x562C) #define WL12XX_SCR_PAD6 (REGISTERS_BASE + 0x5630) #define WL12XX_SCR_PAD7 (REGISTERS_BASE + 0x5634) #define WL12XX_SCR_PAD8 (REGISTERS_BASE + 0x5638) #define WL12XX_SCR_PAD9 (REGISTERS_BASE + 0x563C) /* Spare registers*/ #define WL12XX_SPARE_A1 (REGISTERS_BASE + 0x0994) #define WL12XX_SPARE_A2 (REGISTERS_BASE + 0x0998) #define WL12XX_SPARE_A3 (REGISTERS_BASE + 0x099C) #define WL12XX_SPARE_A4 (REGISTERS_BASE + 0x09A0) #define WL12XX_SPARE_A5 (REGISTERS_BASE + 0x09A4) #define WL12XX_SPARE_A6 (REGISTERS_BASE + 0x09A8) #define WL12XX_SPARE_A7 (REGISTERS_BASE + 0x09AC) #define WL12XX_SPARE_A8 (REGISTERS_BASE + 0x09B0) #define WL12XX_SPARE_B1 (REGISTERS_BASE + 0x5420) #define WL12XX_SPARE_B2 (REGISTERS_BASE + 0x5424) #define WL12XX_SPARE_B3 (REGISTERS_BASE + 0x5428) #define WL12XX_SPARE_B4 (REGISTERS_BASE + 0x542C) #define WL12XX_SPARE_B5 (REGISTERS_BASE + 0x5430) #define WL12XX_SPARE_B6 (REGISTERS_BASE + 0x5434) #define WL12XX_SPARE_B7 (REGISTERS_BASE + 0x5438) #define WL12XX_SPARE_B8 (REGISTERS_BASE + 0x543C) #define WL12XX_PLL_PARAMETERS (REGISTERS_BASE + 0x6040) #define WL12XX_WU_COUNTER_PAUSE (REGISTERS_BASE + 0x6008) #define WL12XX_WELP_ARM_COMMAND (REGISTERS_BASE + 0x6100) #define WL12XX_DRPW_SCRATCH_START (DRPW_BASE + 0x002C) #define WL12XX_CMD_MBOX_ADDRESS 0x407B4 #define ACX_REG_EEPROM_START_BIT BIT(1) /* Command/Information Mailbox Pointers */ /*=============================================== Command Mailbox Pointer - 32bit RW ------------------------------------------ This register holds the start address of the command mailbox located in the Wlan hardware memory. The host must read this pointer after a reset to find the location of the command mailbox. The Wlan hardware initializes the command mailbox pointer with the default address of the command mailbox. The command mailbox pointer is not valid until after the host receives the Init Complete interrupt from the Wlan hardware. ===============================================*/ #define WL12XX_REG_COMMAND_MAILBOX_PTR (WL12XX_SCR_PAD0) /*=============================================== Information Mailbox Pointer - 32bit RW ------------------------------------------ This register holds the start address of the information mailbox located in the Wlan hardware memory. The host must read this pointer after a reset to find the location of the information mailbox. The Wlan hardware initializes the information mailbox pointer with the default address of the information mailbox. The information mailbox pointer is not valid until after the host receives the Init Complete interrupt from the Wlan hardware. ===============================================*/ #define WL12XX_REG_EVENT_MAILBOX_PTR (WL12XX_SCR_PAD1) /*=============================================== EEPROM Read/Write Request 32bit RW ------------------------------------------ 1 EE_READ - EEPROM Read Request 1 - Setting this bit loads a single byte of data into the EE_DATA register from the EEPROM location specified in the EE_ADDR register. The Wlan hardware hardware clears this bit automatically. EE_DATA is valid when this bit is cleared. 0 EE_WRITE - EEPROM Write Request - Setting this bit writes a single byte of data from the EE_DATA register into the EEPROM location specified in the EE_ADDR register. The Wlan hardware hardware clears this bit automatically. *===============================================*/ #define ACX_EE_CTL_REG EE_CTL #define EE_WRITE 0x00000001ul #define EE_READ 0x00000002ul /*=============================================== EEPROM Address - 32bit RW ------------------------------------------ This register specifies the address within the EEPROM from/to which to read/write data. ===============================================*/ #define ACX_EE_ADDR_REG EE_ADDR /*=============================================== EEPROM Data - 32bit RW ------------------------------------------ This register either holds the read 8 bits of data from the EEPROM or the write data to be written to the EEPROM. ===============================================*/ #define ACX_EE_DATA_REG EE_DATA /*=============================================== EEPROM Base Address - 32bit RW ------------------------------------------ This register holds the upper nine bits [23:15] of the 24-bit Wlan hardware memory address for burst reads from EEPROM accesses. The EEPROM provides the lower 15 bits of this address. The MSB of the address from the EEPROM is ignored. ===============================================*/ #define ACX_EE_CFG EE_CFG /*=============================================== GPIO Output Values -32bit, RW ------------------------------------------ [31:16] Reserved [15: 0] Specify the output values (at the output driver inputs) for GPIO[15:0], respectively. ===============================================*/ #define ACX_GPIO_OUT_REG GPIO_OUT #define ACX_MAX_GPIO_LINES 15 /*=============================================== Contention window -32bit, RW ------------------------------------------ [31:26] Reserved [25:16] Max (0x3ff) [15:07] Reserved [06:00] Current contention window value - default is 0x1F ===============================================*/ #define ACX_CONT_WIND_CFG_REG CONT_WIND_CFG #define ACX_CONT_WIND_MIN_MASK 0x0000007f #define ACX_CONT_WIND_MAX 0x03ff0000 #define REF_FREQ_19_2 0 #define REF_FREQ_26_0 1 #define REF_FREQ_38_4 2 #define REF_FREQ_40_0 3 #define REF_FREQ_33_6 4 #define REF_FREQ_NUM 5 #define LUT_PARAM_INTEGER_DIVIDER 0 #define LUT_PARAM_FRACTIONAL_DIVIDER 1 #define LUT_PARAM_ATTN_BB 2 #define LUT_PARAM_ALPHA_BB 3 #define LUT_PARAM_STOP_TIME_BB 4 #define LUT_PARAM_BB_PLL_LOOP_FILTER 5 #define LUT_PARAM_NUM 6 #define WL12XX_EEPROMLESS_IND (WL12XX_SCR_PAD4) #define USE_EEPROM 0 #define NVS_DATA_BUNDARY_ALIGNMENT 4 /* Firmware image header size */ #define FW_HDR_SIZE 8 /****************************************************************************** CHANNELS, BAND & REG DOMAINS definitions ******************************************************************************/ #define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */ #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) enum { CCK_LONG = 0, CCK_SHORT = SHORT_PREAMBLE_BIT, PBCC_LONG = PBCC_RATE_BIT, PBCC_SHORT = PBCC_RATE_BIT | SHORT_PREAMBLE_BIT, OFDM = OFDM_RATE_BIT }; /****************************************************************************** Transmit-Descriptor RATE-SET field definitions... Define a new "Rate-Set" for TX path that incorporates the Rate & Modulation info into a single 16-bit field. TxdRateSet_t: b15 - Indicates Preamble type (1=SHORT, 0=LONG). Notes: Must be LONG (0) for 1Mbps rate. Does not apply (set to 0) for RevG-OFDM rates. b14 - Indicates PBCC encoding (1=PBCC, 0=not). Notes: Does not apply (set to 0) for rates 1 and 2 Mbps. Does not apply (set to 0) for RevG-OFDM rates. b13 - Unused (set to 0). b12-b0 - Supported Rate indicator bits as defined below. ******************************************************************************/ #define OCP_CMD_LOOP 32 #define OCP_CMD_WRITE 0x1 #define OCP_CMD_READ 0x2 #define OCP_READY_MASK BIT(18) #define OCP_STATUS_MASK (BIT(16) | BIT(17)) #define OCP_STATUS_NO_RESP 0x00000 #define OCP_STATUS_OK 0x10000 #define OCP_STATUS_REQ_FAILED 0x20000 #define OCP_STATUS_RESP_ERROR 0x30000 #define OCP_REG_POLARITY 0x0064 #define OCP_REG_CLK_TYPE 0x0448 #define OCP_REG_CLK_POLARITY 0x0cb2 #define OCP_REG_CLK_PULL 0x0cb4 #define POLARITY_LOW BIT(1) #define NO_PULL (BIT(14) | BIT(15)) #define FREF_CLK_TYPE_BITS 0xfffffe7f #define CLK_REQ_PRCM 0x100 #define FREF_CLK_POLARITY_BITS 0xfffff8ff #define CLK_REQ_OUTN_SEL 0x700 #define WU_COUNTER_PAUSE_VAL 0x3FF /* PLL configuration algorithm for wl128x */ #define SYS_CLK_CFG_REG 0x2200 /* Bit[0] - 0-TCXO, 1-FREF */ #define MCS_PLL_CLK_SEL_FREF BIT(0) /* Bit[3:2] - 01-TCXO, 10-FREF */ #define WL_CLK_REQ_TYPE_FREF BIT(3) #define WL_CLK_REQ_TYPE_PG2 (BIT(3) | BIT(2)) /* Bit[4] - 0-TCXO, 1-FREF */ #define PRCM_CM_EN_MUX_WLAN_FREF BIT(4) #define TCXO_ILOAD_INT_REG 0x2264 #define TCXO_CLK_DETECT_REG 0x2266 #define TCXO_DET_FAILED BIT(4) #define FREF_ILOAD_INT_REG 0x2084 #define FREF_CLK_DETECT_REG 0x2086 #define FREF_CLK_DETECT_FAIL BIT(4) /* Use this reg for masking during driver access */ #define WL_SPARE_REG 0x2320 #define WL_SPARE_VAL BIT(2) /* Bit[6:5:3] - mask wl write SYS_CLK_CFG[8:5:2:4] */ #define WL_SPARE_MASK_8526 (BIT(6) | BIT(5) | BIT(3)) #define PLL_LOCK_COUNTERS_REG 0xD8C #define PLL_LOCK_COUNTERS_COEX 0x0F #define PLL_LOCK_COUNTERS_MCS 0xF0 #define MCS_PLL_OVERRIDE_REG 0xD90 #define MCS_PLL_CONFIG_REG 0xD92 #define MCS_SEL_IN_FREQ_MASK 0x0070 #define MCS_SEL_IN_FREQ_SHIFT 4 #define MCS_PLL_CONFIG_REG_VAL 0x73 #define MCS_PLL_ENABLE_HP (BIT(0) | BIT(1)) #define MCS_PLL_M_REG 0xD94 #define MCS_PLL_N_REG 0xD96 #define MCS_PLL_M_REG_VAL 0xC8 #define MCS_PLL_N_REG_VAL 0x07 #define SDIO_IO_DS 0xd14 /* SDIO/wSPI DS configuration values */ enum { HCI_IO_DS_8MA = 0, HCI_IO_DS_4MA = 1, /* default */ HCI_IO_DS_6MA = 2, HCI_IO_DS_2MA = 3, }; /* end PLL configuration algorithm for wl128x */ /* * Host Command Interrupt. Setting this bit masks * the interrupt that the host issues to inform * the FW that it has sent a command * to the Wlan hardware Command Mailbox. */ #define WL12XX_INTR_TRIG_CMD BIT(0) /* * Host Event Acknowlegde Interrupt. The host * sets this bit to acknowledge that it received * the unsolicited information from the event * mailbox. */ #define WL12XX_INTR_TRIG_EVENT_ACK BIT(1) /*=============================================== HI_CFG Interface Configuration Register Values ------------------------------------------ ===============================================*/ #define HI_CFG_UART_ENABLE 0x00000004 #define HI_CFG_RST232_ENABLE 0x00000008 #define HI_CFG_CLOCK_REQ_SELECT 0x00000010 #define HI_CFG_HOST_INT_ENABLE 0x00000020 #define HI_CFG_VLYNQ_OUTPUT_ENABLE 0x00000040 #define HI_CFG_HOST_INT_ACTIVE_LOW 0x00000080 #define HI_CFG_UART_TX_OUT_GPIO_15 0x00000100 #define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200 #define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400 #define HI_CFG_DEF_VAL \ (HI_CFG_UART_ENABLE | \ HI_CFG_RST232_ENABLE | \ HI_CFG_CLOCK_REQ_SELECT | \ HI_CFG_HOST_INT_ENABLE) #define WL127X_REG_FUSE_DATA_2_1 0x050a #define WL128X_REG_FUSE_DATA_2_1 0x2152 #define PG_VER_MASK 0x3c #define PG_VER_OFFSET 2 #define WL127X_PG_MAJOR_VER_MASK 0x3 #define WL127X_PG_MAJOR_VER_OFFSET 0x0 #define WL127X_PG_MINOR_VER_MASK 0xc #define WL127X_PG_MINOR_VER_OFFSET 0x2 #define WL128X_PG_MAJOR_VER_MASK 0xc #define WL128X_PG_MAJOR_VER_OFFSET 0x2 #define WL128X_PG_MINOR_VER_MASK 0x3 #define WL128X_PG_MINOR_VER_OFFSET 0x0 #define WL127X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL127X_PG_MAJOR_VER_MASK) >> \ WL127X_PG_MAJOR_VER_OFFSET) #define WL127X_PG_GET_MINOR(pg_ver) ((pg_ver & WL127X_PG_MINOR_VER_MASK) >> \ WL127X_PG_MINOR_VER_OFFSET) #define WL128X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL128X_PG_MAJOR_VER_MASK) >> \ WL128X_PG_MAJOR_VER_OFFSET) #define WL128X_PG_GET_MINOR(pg_ver) ((pg_ver & WL128X_PG_MINOR_VER_MASK) >> \ WL128X_PG_MINOR_VER_OFFSET) #define WL12XX_REG_FUSE_BD_ADDR_1 0x00310eb4 #define WL12XX_REG_FUSE_BD_ADDR_2 0x00310eb8 #endif compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/debugfs.h0000644000175000017500000000164712026211315024042 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2012 Texas Instruments. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL12XX_DEBUGFS_H__ #define __WL12XX_DEBUGFS_H__ int wl12xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir); #endif /* __WL12XX_DEBUGFS_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/debugfs.c0000644000175000017500000002200112026211315024020 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2009 Nokia Corporation * Copyright (C) 2011-2012 Texas Instruments * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "../wlcore/debugfs.h" #include "../wlcore/wlcore.h" #include "wl12xx.h" #include "acx.h" #include "debugfs.h" #define WL12XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ DEBUGFS_FWSTATS_FILE(a, b, c, wl12xx_acx_statistics) WL12XX_DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, dropped, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, commands, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u"); /* skipping wep.reserved */ WL12XX_DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(wep, packets, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u"); /* skipping cont_miss_bcns_spread for now */ WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, oom_late, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u"); WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u"); int wl12xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { int ret = 0; struct dentry *entry, *stats, *moddir; moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir); if (!moddir || IS_ERR(moddir)) { entry = moddir; goto err; } stats = debugfs_create_dir("fw_stats", moddir); if (!stats || IS_ERR(stats)) { entry = stats; goto err; } DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow); DEBUGFS_FWSTATS_ADD(rx, out_of_mem); DEBUGFS_FWSTATS_ADD(rx, hdr_overflow); DEBUGFS_FWSTATS_ADD(rx, hw_stuck); DEBUGFS_FWSTATS_ADD(rx, dropped); DEBUGFS_FWSTATS_ADD(rx, fcs_err); DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig); DEBUGFS_FWSTATS_ADD(rx, path_reset); DEBUGFS_FWSTATS_ADD(rx, reset_counter); DEBUGFS_FWSTATS_ADD(dma, rx_requested); DEBUGFS_FWSTATS_ADD(dma, rx_errors); DEBUGFS_FWSTATS_ADD(dma, tx_requested); DEBUGFS_FWSTATS_ADD(dma, tx_errors); DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt); DEBUGFS_FWSTATS_ADD(isr, fiqs); DEBUGFS_FWSTATS_ADD(isr, rx_headers); DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow); DEBUGFS_FWSTATS_ADD(isr, rx_rdys); DEBUGFS_FWSTATS_ADD(isr, irqs); DEBUGFS_FWSTATS_ADD(isr, tx_procs); DEBUGFS_FWSTATS_ADD(isr, decrypt_done); DEBUGFS_FWSTATS_ADD(isr, dma0_done); DEBUGFS_FWSTATS_ADD(isr, dma1_done); DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete); DEBUGFS_FWSTATS_ADD(isr, commands); DEBUGFS_FWSTATS_ADD(isr, rx_procs); DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes); DEBUGFS_FWSTATS_ADD(isr, host_acknowledges); DEBUGFS_FWSTATS_ADD(isr, pci_pm); DEBUGFS_FWSTATS_ADD(isr, wakeups); DEBUGFS_FWSTATS_ADD(isr, low_rssi); DEBUGFS_FWSTATS_ADD(wep, addr_key_count); DEBUGFS_FWSTATS_ADD(wep, default_key_count); /* skipping wep.reserved */ DEBUGFS_FWSTATS_ADD(wep, key_not_found); DEBUGFS_FWSTATS_ADD(wep, decrypt_fail); DEBUGFS_FWSTATS_ADD(wep, packets); DEBUGFS_FWSTATS_ADD(wep, interrupt); DEBUGFS_FWSTATS_ADD(pwr, ps_enter); DEBUGFS_FWSTATS_ADD(pwr, elp_enter); DEBUGFS_FWSTATS_ADD(pwr, missing_bcns); DEBUGFS_FWSTATS_ADD(pwr, wake_on_host); DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp); DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps); DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps); DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons); DEBUGFS_FWSTATS_ADD(pwr, power_save_off); DEBUGFS_FWSTATS_ADD(pwr, enable_ps); DEBUGFS_FWSTATS_ADD(pwr, disable_ps); DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps); /* skipping cont_miss_bcns_spread for now */ DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons); DEBUGFS_FWSTATS_ADD(mic, rx_pkts); DEBUGFS_FWSTATS_ADD(mic, calc_failure); DEBUGFS_FWSTATS_ADD(aes, encrypt_fail); DEBUGFS_FWSTATS_ADD(aes, decrypt_fail); DEBUGFS_FWSTATS_ADD(aes, encrypt_packets); DEBUGFS_FWSTATS_ADD(aes, decrypt_packets); DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt); DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt); DEBUGFS_FWSTATS_ADD(event, heart_beat); DEBUGFS_FWSTATS_ADD(event, calibration); DEBUGFS_FWSTATS_ADD(event, rx_mismatch); DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); DEBUGFS_FWSTATS_ADD(event, rx_pool); DEBUGFS_FWSTATS_ADD(event, oom_late); DEBUGFS_FWSTATS_ADD(event, phy_transmit_error); DEBUGFS_FWSTATS_ADD(event, tx_stuck); DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts); DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts); DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime); DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn); DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn); DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization); DEBUGFS_FWSTATS_ADD(ps, upsd_utilization); DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop); DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); return 0; err: if (IS_ERR(entry)) ret = PTR_ERR(entry); else ret = -ENOMEM; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/conf.h0000644000175000017500000000257212026211315023346 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL12XX_CONF_H__ #define __WL12XX_CONF_H__ /* these are number of channels on the band divided by two, rounded up */ #define CONF_TX_PWR_COMPENSATION_LEN_2 7 #define CONF_TX_PWR_COMPENSATION_LEN_5 18 struct wl12xx_conf_rf { /* * Per channel power compensation for 2.4GHz * * Range: s8 */ u8 tx_per_channel_power_compensation_2[CONF_TX_PWR_COMPENSATION_LEN_2]; /* * Per channel power compensation for 5GHz * * Range: s8 */ u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5]; }; struct wl12xx_priv_conf { struct wl12xx_conf_rf rf; struct conf_memory_settings mem_wl127x; }; #endif /* __WL12XX_CONF_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/cmd.h0000644000175000017500000000573012026211315023163 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 1998-2009, 2011 Texas Instruments. All rights reserved. * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL12XX_CMD_H__ #define __WL12XX_CMD_H__ #include "conf.h" #define TEST_CMD_INI_FILE_RADIO_PARAM 0x19 #define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E struct wl1271_general_parms_cmd { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; struct wl1271_ini_general_params general_params; u8 sr_debug_table[WL1271_INI_MAX_SMART_REFLEX_PARAM]; u8 sr_sen_n_p; u8 sr_sen_n_p_gain; u8 sr_sen_nrn; u8 sr_sen_prn; u8 padding[3]; } __packed; struct wl128x_general_parms_cmd { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; struct wl128x_ini_general_params general_params; u8 sr_debug_table[WL1271_INI_MAX_SMART_REFLEX_PARAM]; u8 sr_sen_n_p; u8 sr_sen_n_p_gain; u8 sr_sen_nrn; u8 sr_sen_prn; u8 padding[3]; } __packed; struct wl1271_radio_parms_cmd { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; /* Static radio parameters */ struct wl1271_ini_band_params_2 static_params_2; struct wl1271_ini_band_params_5 static_params_5; /* Dynamic radio parameters */ struct wl1271_ini_fem_params_2 dyn_params_2; u8 padding2; struct wl1271_ini_fem_params_5 dyn_params_5; u8 padding3[2]; } __packed; struct wl128x_radio_parms_cmd { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; /* Static radio parameters */ struct wl128x_ini_band_params_2 static_params_2; struct wl128x_ini_band_params_5 static_params_5; u8 fem_vendor_and_options; /* Dynamic radio parameters */ struct wl128x_ini_fem_params_2 dyn_params_2; u8 padding2; struct wl128x_ini_fem_params_5 dyn_params_5; } __packed; #define TEST_CMD_INI_FILE_RF_EXTENDED_PARAM 0x26 struct wl1271_ext_radio_parms_cmd { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; u8 tx_per_channel_power_compensation_2[CONF_TX_PWR_COMPENSATION_LEN_2]; u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5]; u8 padding[3]; } __packed; int wl1271_cmd_general_parms(struct wl1271 *wl); int wl128x_cmd_general_parms(struct wl1271 *wl); int wl1271_cmd_radio_parms(struct wl1271 *wl); int wl128x_cmd_radio_parms(struct wl1271 *wl); int wl1271_cmd_ext_radio_parms(struct wl1271 *wl); #endif /* __WL12XX_CMD_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/cmd.c0000644000175000017500000002011612026211315023151 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2009-2010 Nokia Corporation * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "../wlcore/cmd.h" #include "../wlcore/debug.h" #include "wl12xx.h" #include "cmd.h" int wl1271_cmd_ext_radio_parms(struct wl1271 *wl) { struct wl1271_ext_radio_parms_cmd *ext_radio_parms; struct wl12xx_priv *priv = wl->priv; struct wl12xx_conf_rf *rf = &priv->conf.rf; int ret; if (!wl->nvs) return -ENODEV; ext_radio_parms = kzalloc(sizeof(*ext_radio_parms), GFP_KERNEL); if (!ext_radio_parms) return -ENOMEM; ext_radio_parms->test.id = TEST_CMD_INI_FILE_RF_EXTENDED_PARAM; memcpy(ext_radio_parms->tx_per_channel_power_compensation_2, rf->tx_per_channel_power_compensation_2, CONF_TX_PWR_COMPENSATION_LEN_2); memcpy(ext_radio_parms->tx_per_channel_power_compensation_5, rf->tx_per_channel_power_compensation_5, CONF_TX_PWR_COMPENSATION_LEN_5); wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_EXT_RADIO_PARAM: ", ext_radio_parms, sizeof(*ext_radio_parms)); ret = wl1271_cmd_test(wl, ext_radio_parms, sizeof(*ext_radio_parms), 0); if (ret < 0) wl1271_warning("TEST_CMD_INI_FILE_RF_EXTENDED_PARAM failed"); kfree(ext_radio_parms); return ret; } int wl1271_cmd_general_parms(struct wl1271 *wl) { struct wl1271_general_parms_cmd *gen_parms; struct wl1271_ini_general_params *gp = &((struct wl1271_nvs_file *)wl->nvs)->general_params; struct wl12xx_priv *priv = wl->priv; bool answer = false; int ret; if (!wl->nvs) return -ENODEV; if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { wl1271_warning("FEM index from INI out of bounds"); return -EINVAL; } gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); if (!gen_parms) return -ENOMEM; gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM; memcpy(&gen_parms->general_params, gp, sizeof(*gp)); /* If we started in PLT FEM_DETECT mode, force auto detect */ if (wl->plt_mode == PLT_FEM_DETECT) gen_parms->general_params.tx_bip_fem_auto_detect = true; if (gen_parms->general_params.tx_bip_fem_auto_detect) answer = true; /* Override the REF CLK from the NVS with the one from platform data */ gen_parms->general_params.ref_clock = priv->ref_clock; ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); goto out; } gp->tx_bip_fem_manufacturer = gen_parms->general_params.tx_bip_fem_manufacturer; if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { wl1271_warning("FEM index from FW out of bounds"); ret = -EINVAL; goto out; } /* If we are in calibrator based fem auto detect - save fem nr */ if (wl->plt_mode == PLT_FEM_DETECT) wl->fem_manuf = gp->tx_bip_fem_manufacturer; wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n", answer == false ? "manual" : wl->plt_mode == PLT_FEM_DETECT ? "calibrator_fem_detect" : "auto", gp->tx_bip_fem_manufacturer); out: kfree(gen_parms); return ret; } int wl128x_cmd_general_parms(struct wl1271 *wl) { struct wl128x_general_parms_cmd *gen_parms; struct wl128x_ini_general_params *gp = &((struct wl128x_nvs_file *)wl->nvs)->general_params; struct wl12xx_priv *priv = wl->priv; bool answer = false; int ret; if (!wl->nvs) return -ENODEV; if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { wl1271_warning("FEM index from ini out of bounds"); return -EINVAL; } gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); if (!gen_parms) return -ENOMEM; gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM; memcpy(&gen_parms->general_params, gp, sizeof(*gp)); /* If we started in PLT FEM_DETECT mode, force auto detect */ if (wl->plt_mode == PLT_FEM_DETECT) gen_parms->general_params.tx_bip_fem_auto_detect = true; if (gen_parms->general_params.tx_bip_fem_auto_detect) answer = true; /* Replace REF and TCXO CLKs with the ones from platform data */ gen_parms->general_params.ref_clock = priv->ref_clock; gen_parms->general_params.tcxo_ref_clock = priv->tcxo_clock; ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); goto out; } gp->tx_bip_fem_manufacturer = gen_parms->general_params.tx_bip_fem_manufacturer; if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { wl1271_warning("FEM index from FW out of bounds"); ret = -EINVAL; goto out; } /* If we are in calibrator based fem auto detect - save fem nr */ if (wl->plt_mode == PLT_FEM_DETECT) wl->fem_manuf = gp->tx_bip_fem_manufacturer; wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n", answer == false ? "manual" : wl->plt_mode == PLT_FEM_DETECT ? "calibrator_fem_detect" : "auto", gp->tx_bip_fem_manufacturer); out: kfree(gen_parms); return ret; } int wl1271_cmd_radio_parms(struct wl1271 *wl) { struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs; struct wl1271_radio_parms_cmd *radio_parms; struct wl1271_ini_general_params *gp = &nvs->general_params; int ret, fem_idx; if (!wl->nvs) return -ENODEV; radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); if (!radio_parms) return -ENOMEM; radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer); /* 2.4GHz parameters */ memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2, sizeof(struct wl1271_ini_band_params_2)); memcpy(&radio_parms->dyn_params_2, &nvs->dyn_radio_params_2[fem_idx].params, sizeof(struct wl1271_ini_fem_params_2)); /* 5GHz parameters */ memcpy(&radio_parms->static_params_5, &nvs->stat_radio_params_5, sizeof(struct wl1271_ini_band_params_5)); memcpy(&radio_parms->dyn_params_5, &nvs->dyn_radio_params_5[fem_idx].params, sizeof(struct wl1271_ini_fem_params_5)); wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", radio_parms, sizeof(*radio_parms)); ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); if (ret < 0) wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); kfree(radio_parms); return ret; } int wl128x_cmd_radio_parms(struct wl1271 *wl) { struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; struct wl128x_radio_parms_cmd *radio_parms; struct wl128x_ini_general_params *gp = &nvs->general_params; int ret, fem_idx; if (!wl->nvs) return -ENODEV; radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); if (!radio_parms) return -ENOMEM; radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer); /* 2.4GHz parameters */ memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2, sizeof(struct wl128x_ini_band_params_2)); memcpy(&radio_parms->dyn_params_2, &nvs->dyn_radio_params_2[fem_idx].params, sizeof(struct wl128x_ini_fem_params_2)); /* 5GHz parameters */ memcpy(&radio_parms->static_params_5, &nvs->stat_radio_params_5, sizeof(struct wl128x_ini_band_params_5)); memcpy(&radio_parms->dyn_params_5, &nvs->dyn_radio_params_5[fem_idx].params, sizeof(struct wl128x_ini_fem_params_5)); radio_parms->fem_vendor_and_options = nvs->fem_vendor_and_options; wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", radio_parms, sizeof(*radio_parms)); ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); if (ret < 0) wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); kfree(radio_parms); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/acx.h0000644000175000017500000001511512026211315023171 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 1998-2009, 2011 Texas Instruments. All rights reserved. * Copyright (C) 2008-2010 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __WL12XX_ACX_H__ #define __WL12XX_ACX_H__ #include "../wlcore/wlcore.h" #include "../wlcore/acx.h" #define WL12XX_ACX_ALL_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \ WL1271_ACX_INTR_INIT_COMPLETE | \ WL1271_ACX_INTR_EVENT_A | \ WL1271_ACX_INTR_EVENT_B | \ WL1271_ACX_INTR_CMD_COMPLETE | \ WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA) #define WL12XX_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \ WL1271_ACX_INTR_EVENT_A | \ WL1271_ACX_INTR_EVENT_B | \ WL1271_ACX_INTR_HW_AVAILABLE | \ WL1271_ACX_INTR_DATA) struct wl1271_acx_host_config_bitmap { struct acx_header header; __le32 host_cfg_bitmap; } __packed; struct wl12xx_acx_tx_statistics { __le32 internal_desc_overflow; } __packed; struct wl12xx_acx_rx_statistics { __le32 out_of_mem; __le32 hdr_overflow; __le32 hw_stuck; __le32 dropped; __le32 fcs_err; __le32 xfr_hint_trig; __le32 path_reset; __le32 reset_counter; } __packed; struct wl12xx_acx_dma_statistics { __le32 rx_requested; __le32 rx_errors; __le32 tx_requested; __le32 tx_errors; } __packed; struct wl12xx_acx_isr_statistics { /* host command complete */ __le32 cmd_cmplt; /* fiqisr() */ __le32 fiqs; /* (INT_STS_ND & INT_TRIG_RX_HEADER) */ __le32 rx_headers; /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */ __le32 rx_completes; /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */ __le32 rx_mem_overflow; /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */ __le32 rx_rdys; /* irqisr() */ __le32 irqs; /* (INT_STS_ND & INT_TRIG_TX_PROC) */ __le32 tx_procs; /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */ __le32 decrypt_done; /* (INT_STS_ND & INT_TRIG_DMA0) */ __le32 dma0_done; /* (INT_STS_ND & INT_TRIG_DMA1) */ __le32 dma1_done; /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */ __le32 tx_exch_complete; /* (INT_STS_ND & INT_TRIG_COMMAND) */ __le32 commands; /* (INT_STS_ND & INT_TRIG_RX_PROC) */ __le32 rx_procs; /* (INT_STS_ND & INT_TRIG_PM_802) */ __le32 hw_pm_mode_changes; /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */ __le32 host_acknowledges; /* (INT_STS_ND & INT_TRIG_PM_PCI) */ __le32 pci_pm; /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */ __le32 wakeups; /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */ __le32 low_rssi; } __packed; struct wl12xx_acx_wep_statistics { /* WEP address keys configured */ __le32 addr_key_count; /* default keys configured */ __le32 default_key_count; __le32 reserved; /* number of times that WEP key not found on lookup */ __le32 key_not_found; /* number of times that WEP key decryption failed */ __le32 decrypt_fail; /* WEP packets decrypted */ __le32 packets; /* WEP decrypt interrupts */ __le32 interrupt; } __packed; #define ACX_MISSED_BEACONS_SPREAD 10 struct wl12xx_acx_pwr_statistics { /* the amount of enters into power save mode (both PD & ELP) */ __le32 ps_enter; /* the amount of enters into ELP mode */ __le32 elp_enter; /* the amount of missing beacon interrupts to the host */ __le32 missing_bcns; /* the amount of wake on host-access times */ __le32 wake_on_host; /* the amount of wake on timer-expire */ __le32 wake_on_timer_exp; /* the number of packets that were transmitted with PS bit set */ __le32 tx_with_ps; /* the number of packets that were transmitted with PS bit clear */ __le32 tx_without_ps; /* the number of received beacons */ __le32 rcvd_beacons; /* the number of entering into PowerOn (power save off) */ __le32 power_save_off; /* the number of entries into power save mode */ __le16 enable_ps; /* * the number of exits from power save, not including failed PS * transitions */ __le16 disable_ps; /* * the number of times the TSF counter was adjusted because * of drift */ __le32 fix_tsf_ps; /* Gives statistics about the spread continuous missed beacons. * The 16 LSB are dedicated for the PS mode. * The 16 MSB are dedicated for the PS mode. * cont_miss_bcns_spread[0] - single missed beacon. * cont_miss_bcns_spread[1] - two continuous missed beacons. * cont_miss_bcns_spread[2] - three continuous missed beacons. * ... * cont_miss_bcns_spread[9] - ten and more continuous missed beacons. */ __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; /* the number of beacons in awake mode */ __le32 rcvd_awake_beacons; } __packed; struct wl12xx_acx_mic_statistics { __le32 rx_pkts; __le32 calc_failure; } __packed; struct wl12xx_acx_aes_statistics { __le32 encrypt_fail; __le32 decrypt_fail; __le32 encrypt_packets; __le32 decrypt_packets; __le32 encrypt_interrupt; __le32 decrypt_interrupt; } __packed; struct wl12xx_acx_event_statistics { __le32 heart_beat; __le32 calibration; __le32 rx_mismatch; __le32 rx_mem_empty; __le32 rx_pool; __le32 oom_late; __le32 phy_transmit_error; __le32 tx_stuck; } __packed; struct wl12xx_acx_ps_statistics { __le32 pspoll_timeouts; __le32 upsd_timeouts; __le32 upsd_max_sptime; __le32 upsd_max_apturn; __le32 pspoll_max_apturn; __le32 pspoll_utilization; __le32 upsd_utilization; } __packed; struct wl12xx_acx_rxpipe_statistics { __le32 rx_prep_beacon_drop; __le32 descr_host_int_trig_rx_data; __le32 beacon_buffer_thres_host_int_trig_rx_data; __le32 missed_beacon_host_int_trig_rx_data; __le32 tx_xfr_host_int_trig_rx_data; } __packed; struct wl12xx_acx_statistics { struct acx_header header; struct wl12xx_acx_tx_statistics tx; struct wl12xx_acx_rx_statistics rx; struct wl12xx_acx_dma_statistics dma; struct wl12xx_acx_isr_statistics isr; struct wl12xx_acx_wep_statistics wep; struct wl12xx_acx_pwr_statistics pwr; struct wl12xx_acx_aes_statistics aes; struct wl12xx_acx_mic_statistics mic; struct wl12xx_acx_event_statistics event; struct wl12xx_acx_ps_statistics ps; struct wl12xx_acx_rxpipe_statistics rxpipe; } __packed; int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap); #endif /* __WL12XX_ACX_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/acx.c0000644000175000017500000000263712026211315023171 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2008-2009 Nokia Corporation * Copyright (C) 2011 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "../wlcore/cmd.h" #include "../wlcore/debug.h" #include "../wlcore/acx.h" #include "acx.h" int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap) { struct wl1271_acx_host_config_bitmap *bitmap_conf; int ret; bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); if (!bitmap_conf) { ret = -ENOMEM; goto out; } bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, bitmap_conf, sizeof(*bitmap_conf)); if (ret < 0) { wl1271_warning("wl1271 bitmap config opt failed: %d", ret); goto out; } out: kfree(bitmap_conf); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/Makefile0000644000175000017500000000011612026211315023700 0ustar mcgrofmcgrofwl12xx-objs = main.o cmd.o acx.o debugfs.o obj-$(CONFIG_WL12XX) += wl12xx.o compat-drivers-2012-09-18/drivers/net/wireless/ti/wl12xx/Kconfig0000644000175000017500000000052612026211315023550 0ustar mcgrofmcgrofconfig WL12XX tristate "TI wl12xx support" depends on MAC80211 select WLCORE ---help--- This module adds support for wireless adapters based on TI wl1271, wl1273, wl1281 and wl1283 chipsets. This module does *not* include support for wl1251. For wl1251 support, use the separate homonymous driver instead. compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/0000755000175000017500000000000012026211315021672 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/ipw2100.c0000644000175000017500000070526012026211315023152 0ustar mcgrofmcgrof/****************************************************************************** Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 Portions of this file are based on the sample_* files provided by Wireless Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes Portions of this file are based on the Host AP project, Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen Copyright (c) 2002-2003, Jouni Malinen Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox ******************************************************************************/ /* Initial driver on which this is based was developed by Janusz Gorycki, Maciej Urbaniak, and Maciej Sosnowski. Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak. Theory of Operation Tx - Commands and Data Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs) Each TBD contains a pointer to the physical (dma_addr_t) address of data being sent to the firmware as well as the length of the data. The host writes to the TBD queue at the WRITE index. The WRITE index points to the _next_ packet to be written and is advanced when after the TBD has been filled. The firmware pulls from the TBD queue at the READ index. The READ index points to the currently being read entry, and is advanced once the firmware is done with a packet. When data is sent to the firmware, the first TBD is used to indicate to the firmware if a Command or Data is being sent. If it is Command, all of the command information is contained within the physical address referred to by the TBD. If it is Data, the first TBD indicates the type of data packet, number of fragments, etc. The next TBD then refers to the actual packet location. The Tx flow cycle is as follows: 1) ipw2100_tx() is called by kernel with SKB to transmit 2) Packet is move from the tx_free_list and appended to the transmit pending list (tx_pend_list) 3) work is scheduled to move pending packets into the shared circular queue. 4) when placing packet in the circular queue, the incoming SKB is DMA mapped to a physical address. That address is entered into a TBD. Two TBDs are filled out. The first indicating a data packet, the second referring to the actual payload data. 5) the packet is removed from tx_pend_list and placed on the end of the firmware pending list (fw_pend_list) 6) firmware is notified that the WRITE index has 7) Once the firmware has processed the TBD, INTA is triggered. 8) For each Tx interrupt received from the firmware, the READ index is checked to see which TBDs are done being processed. 9) For each TBD that has been processed, the ISR pulls the oldest packet from the fw_pend_list. 10)The packet structure contained in the fw_pend_list is then used to unmap the DMA address and to free the SKB originally passed to the driver from the kernel. 11)The packet structure is placed onto the tx_free_list The above steps are the same for commands, only the msg_free_list/msg_pend_list are used instead of tx_free_list/tx_pend_list ... Critical Sections / Locking : There are two locks utilized. The first is the low level lock (priv->low_lock) that protects the following: - Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows: tx_free_list : Holds pre-allocated Tx buffers. TAIL modified in __ipw2100_tx_process() HEAD modified in ipw2100_tx() tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring TAIL modified ipw2100_tx() HEAD modified by ipw2100_tx_send_data() msg_free_list : Holds pre-allocated Msg (Command) buffers TAIL modified in __ipw2100_tx_process() HEAD modified in ipw2100_hw_send_command() msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring TAIL modified in ipw2100_hw_send_command() HEAD modified in ipw2100_tx_send_commands() The flow of data on the TX side is as follows: MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST The methods that work on the TBD ring are protected via priv->low_lock. - The internal data state of the device itself - Access to the firmware read/write indexes for the BD queues and associated logic All external entry functions are locked with the priv->action_lock to ensure that only one external action is invoked at a time. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipw2100.h" #include "ipw.h" #define IPW2100_VERSION "git-1.2.2" #define DRV_NAME "ipw2100" #define DRV_VERSION IPW2100_VERSION #define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) static struct pm_qos_request_list ipw2100_pm_qos_req; #else static struct pm_qos_request ipw2100_pm_qos_req; #endif /* Debugging stuff */ #ifdef CONFIG_IPW2100_DEBUG #define IPW2100_RX_DEBUG /* Reception debugging */ #endif MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); static int debug = 0; static int network_mode = 0; static int channel = 0; static int associate = 0; static int disable = 0; #ifdef CONFIG_PM static struct ipw2100_fw ipw2100_firmware; #endif #include module_param(debug, int, 0444); module_param_named(mode, network_mode, int, 0444); module_param(channel, int, 0444); module_param(associate, int, 0444); module_param(disable, int, 0444); MODULE_PARM_DESC(debug, "debug level"); MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); MODULE_PARM_DESC(channel, "channel"); MODULE_PARM_DESC(associate, "auto associate when scanning (default off)"); MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); static u32 ipw2100_debug_level = IPW_DL_NONE; #ifdef CONFIG_IPW2100_DEBUG #define IPW_DEBUG(level, message...) \ do { \ if (ipw2100_debug_level & (level)) { \ printk(KERN_DEBUG "ipw2100: %c %s ", \ in_interrupt() ? 'I' : 'U', __func__); \ printk(message); \ } \ } while (0) #else #define IPW_DEBUG(level, message...) do {} while (0) #endif /* CONFIG_IPW2100_DEBUG */ #ifdef CONFIG_IPW2100_DEBUG static const char *command_types[] = { "undefined", "unused", /* HOST_ATTENTION */ "HOST_COMPLETE", "unused", /* SLEEP */ "unused", /* HOST_POWER_DOWN */ "unused", "SYSTEM_CONFIG", "unused", /* SET_IMR */ "SSID", "MANDATORY_BSSID", "AUTHENTICATION_TYPE", "ADAPTER_ADDRESS", "PORT_TYPE", "INTERNATIONAL_MODE", "CHANNEL", "RTS_THRESHOLD", "FRAG_THRESHOLD", "POWER_MODE", "TX_RATES", "BASIC_TX_RATES", "WEP_KEY_INFO", "unused", "unused", "unused", "unused", "WEP_KEY_INDEX", "WEP_FLAGS", "ADD_MULTICAST", "CLEAR_ALL_MULTICAST", "BEACON_INTERVAL", "ATIM_WINDOW", "CLEAR_STATISTICS", "undefined", "undefined", "undefined", "undefined", "TX_POWER_INDEX", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "BROADCAST_SCAN", "CARD_DISABLE", "PREFERRED_BSSID", "SET_SCAN_OPTIONS", "SCAN_DWELL_TIME", "SWEEP_TABLE", "AP_OR_STATION_TABLE", "GROUP_ORDINALS", "SHORT_RETRY_LIMIT", "LONG_RETRY_LIMIT", "unused", /* SAVE_CALIBRATION */ "unused", /* RESTORE_CALIBRATION */ "undefined", "undefined", "undefined", "HOST_PRE_POWER_DOWN", "unused", /* HOST_INTERRUPT_COALESCING */ "undefined", "CARD_DISABLE_PHY_OFF", "MSDU_TX_RATES", "undefined", "SET_STATION_STAT_BITS", "CLEAR_STATIONS_STAT_BITS", "LEAP_ROGUE_MODE", "SET_SECURITY_INFORMATION", "DISASSOCIATION_BSSID", "SET_WPA_ASS_IE" }; #endif static const long ipw2100_frequencies[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; #define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies) static struct ieee80211_rate ipw2100_bg_rates[] = { { .bitrate = 10 }, { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, }; #define RATE_COUNT ARRAY_SIZE(ipw2100_bg_rates) /* Pre-decl until we get the code solid and then we can clean it up */ static void ipw2100_tx_send_commands(struct ipw2100_priv *priv); static void ipw2100_tx_send_data(struct ipw2100_priv *priv); static int ipw2100_adapter_setup(struct ipw2100_priv *priv); static void ipw2100_queues_initialize(struct ipw2100_priv *priv); static void ipw2100_queues_free(struct ipw2100_priv *priv); static int ipw2100_queues_allocate(struct ipw2100_priv *priv); static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw); static int ipw2100_get_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw); static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, size_t max); static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, size_t max); static void ipw2100_release_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw); static int ipw2100_ucode_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw); static void ipw2100_wx_event_work(struct work_struct *work); static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev); static struct iw_handler_def ipw2100_wx_handler_def; static inline void read_register(struct net_device *dev, u32 reg, u32 * val) { struct ipw2100_priv *priv = libipw_priv(dev); *val = ioread32(priv->ioaddr + reg); IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val); } static inline void write_register(struct net_device *dev, u32 reg, u32 val) { struct ipw2100_priv *priv = libipw_priv(dev); iowrite32(val, priv->ioaddr + reg); IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val); } static inline void read_register_word(struct net_device *dev, u32 reg, u16 * val) { struct ipw2100_priv *priv = libipw_priv(dev); *val = ioread16(priv->ioaddr + reg); IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val); } static inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val) { struct ipw2100_priv *priv = libipw_priv(dev); *val = ioread8(priv->ioaddr + reg); IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val); } static inline void write_register_word(struct net_device *dev, u32 reg, u16 val) { struct ipw2100_priv *priv = libipw_priv(dev); iowrite16(val, priv->ioaddr + reg); IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val); } static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val) { struct ipw2100_priv *priv = libipw_priv(dev); iowrite8(val, priv->ioaddr + reg); IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val); } static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val) { write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, addr & IPW_REG_INDIRECT_ADDR_MASK); read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); } static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val) { write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, addr & IPW_REG_INDIRECT_ADDR_MASK); write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); } static inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val) { write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, addr & IPW_REG_INDIRECT_ADDR_MASK); read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); } static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val) { write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, addr & IPW_REG_INDIRECT_ADDR_MASK); write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); } static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val) { write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, addr & IPW_REG_INDIRECT_ADDR_MASK); read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); } static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val) { write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, addr & IPW_REG_INDIRECT_ADDR_MASK); write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); } static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr) { write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, addr & IPW_REG_INDIRECT_ADDR_MASK); } static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val) { write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val); } static void write_nic_memory(struct net_device *dev, u32 addr, u32 len, const u8 * buf) { u32 aligned_addr; u32 aligned_len; u32 dif_len; u32 i; /* read first nibble byte by byte */ aligned_addr = addr & (~0x3); dif_len = addr - aligned_addr; if (dif_len) { /* Start reading at aligned_addr + dif_len */ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); for (i = dif_len; i < 4; i++, buf++) write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, *buf); len -= dif_len; aligned_addr += 4; } /* read DWs through autoincrement registers */ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr); aligned_len = len & (~0x3); for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf); /* copy the last nibble */ dif_len = len - aligned_len; write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); for (i = 0; i < dif_len; i++, buf++) write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, *buf); } static void read_nic_memory(struct net_device *dev, u32 addr, u32 len, u8 * buf) { u32 aligned_addr; u32 aligned_len; u32 dif_len; u32 i; /* read first nibble byte by byte */ aligned_addr = addr & (~0x3); dif_len = addr - aligned_addr; if (dif_len) { /* Start reading at aligned_addr + dif_len */ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); for (i = dif_len; i < 4; i++, buf++) read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf); len -= dif_len; aligned_addr += 4; } /* read DWs through autoincrement registers */ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr); aligned_len = len & (~0x3); for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf); /* copy the last nibble */ dif_len = len - aligned_len; write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); for (i = 0; i < dif_len; i++, buf++) read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf); } static bool ipw2100_hw_is_adapter_in_system(struct net_device *dev) { u32 dbg; read_register(dev, IPW_REG_DOA_DEBUG_AREA_START, &dbg); return dbg == IPW_DATA_DOA_DEBUG_VALUE; } static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord, void *val, u32 * len) { struct ipw2100_ordinals *ordinals = &priv->ordinals; u32 addr; u32 field_info; u16 field_len; u16 field_count; u32 total_length; if (ordinals->table1_addr == 0) { printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals " "before they have been loaded.\n"); return -EINVAL; } if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) { if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) { *len = IPW_ORD_TAB_1_ENTRY_SIZE; printk(KERN_WARNING DRV_NAME ": ordinal buffer length too small, need %zd\n", IPW_ORD_TAB_1_ENTRY_SIZE); return -EINVAL; } read_nic_dword(priv->net_dev, ordinals->table1_addr + (ord << 2), &addr); read_nic_dword(priv->net_dev, addr, val); *len = IPW_ORD_TAB_1_ENTRY_SIZE; return 0; } if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) { ord -= IPW_START_ORD_TAB_2; /* get the address of statistic */ read_nic_dword(priv->net_dev, ordinals->table2_addr + (ord << 3), &addr); /* get the second DW of statistics ; * two 16-bit words - first is length, second is count */ read_nic_dword(priv->net_dev, ordinals->table2_addr + (ord << 3) + sizeof(u32), &field_info); /* get each entry length */ field_len = *((u16 *) & field_info); /* get number of entries */ field_count = *(((u16 *) & field_info) + 1); /* abort if no enough memory */ total_length = field_len * field_count; if (total_length > *len) { *len = total_length; return -EINVAL; } *len = total_length; if (!total_length) return 0; /* read the ordinal data from the SRAM */ read_nic_memory(priv->net_dev, addr, total_length, val); return 0; } printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor " "in table 2\n", ord); return -EINVAL; } static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val, u32 * len) { struct ipw2100_ordinals *ordinals = &priv->ordinals; u32 addr; if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) { if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) { *len = IPW_ORD_TAB_1_ENTRY_SIZE; IPW_DEBUG_INFO("wrong size\n"); return -EINVAL; } read_nic_dword(priv->net_dev, ordinals->table1_addr + (ord << 2), &addr); write_nic_dword(priv->net_dev, addr, *val); *len = IPW_ORD_TAB_1_ENTRY_SIZE; return 0; } IPW_DEBUG_INFO("wrong table\n"); if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) return -EINVAL; return -EINVAL; } static char *snprint_line(char *buf, size_t count, const u8 * data, u32 len, u32 ofs) { int out, i, j, l; char c; out = snprintf(buf, count, "%08X", ofs); for (l = 0, i = 0; i < 2; i++) { out += snprintf(buf + out, count - out, " "); for (j = 0; j < 8 && l < len; j++, l++) out += snprintf(buf + out, count - out, "%02X ", data[(i * 8 + j)]); for (; j < 8; j++) out += snprintf(buf + out, count - out, " "); } out += snprintf(buf + out, count - out, " "); for (l = 0, i = 0; i < 2; i++) { out += snprintf(buf + out, count - out, " "); for (j = 0; j < 8 && l < len; j++, l++) { c = data[(i * 8 + j)]; if (!isascii(c) || !isprint(c)) c = '.'; out += snprintf(buf + out, count - out, "%c", c); } for (; j < 8; j++) out += snprintf(buf + out, count - out, " "); } return buf; } static void printk_buf(int level, const u8 * data, u32 len) { char line[81]; u32 ofs = 0; if (!(ipw2100_debug_level & level)) return; while (len) { printk(KERN_DEBUG "%s\n", snprint_line(line, sizeof(line), &data[ofs], min(len, 16U), ofs)); ofs += 16; len -= min(len, 16U); } } #define MAX_RESET_BACKOFF 10 static void schedule_reset(struct ipw2100_priv *priv) { unsigned long now = get_seconds(); /* If we haven't received a reset request within the backoff period, * then we can reset the backoff interval so this reset occurs * immediately */ if (priv->reset_backoff && (now - priv->last_reset > priv->reset_backoff)) priv->reset_backoff = 0; priv->last_reset = get_seconds(); if (!(priv->status & STATUS_RESET_PENDING)) { IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n", priv->net_dev->name, priv->reset_backoff); netif_carrier_off(priv->net_dev); netif_stop_queue(priv->net_dev); priv->status |= STATUS_RESET_PENDING; if (priv->reset_backoff) schedule_delayed_work(&priv->reset_work, priv->reset_backoff * HZ); else schedule_delayed_work(&priv->reset_work, 0); if (priv->reset_backoff < MAX_RESET_BACKOFF) priv->reset_backoff++; wake_up_interruptible(&priv->wait_command_queue); } else IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n", priv->net_dev->name); } #define HOST_COMPLETE_TIMEOUT (2 * HZ) static int ipw2100_hw_send_command(struct ipw2100_priv *priv, struct host_command *cmd) { struct list_head *element; struct ipw2100_tx_packet *packet; unsigned long flags; int err = 0; IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n", command_types[cmd->host_command], cmd->host_command, cmd->host_command_length); printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters, cmd->host_command_length); spin_lock_irqsave(&priv->low_lock, flags); if (priv->fatal_error) { IPW_DEBUG_INFO ("Attempt to send command while hardware in fatal error condition.\n"); err = -EIO; goto fail_unlock; } if (!(priv->status & STATUS_RUNNING)) { IPW_DEBUG_INFO ("Attempt to send command while hardware is not running.\n"); err = -EIO; goto fail_unlock; } if (priv->status & STATUS_CMD_ACTIVE) { IPW_DEBUG_INFO ("Attempt to send command while another command is pending.\n"); err = -EBUSY; goto fail_unlock; } if (list_empty(&priv->msg_free_list)) { IPW_DEBUG_INFO("no available msg buffers\n"); goto fail_unlock; } priv->status |= STATUS_CMD_ACTIVE; priv->messages_sent++; element = priv->msg_free_list.next; packet = list_entry(element, struct ipw2100_tx_packet, list); packet->jiffy_start = jiffies; /* initialize the firmware command packet */ packet->info.c_struct.cmd->host_command_reg = cmd->host_command; packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1; packet->info.c_struct.cmd->host_command_len_reg = cmd->host_command_length; packet->info.c_struct.cmd->sequence = cmd->host_command_sequence; memcpy(packet->info.c_struct.cmd->host_command_params_reg, cmd->host_command_parameters, sizeof(packet->info.c_struct.cmd->host_command_params_reg)); list_del(element); DEC_STAT(&priv->msg_free_stat); list_add_tail(element, &priv->msg_pend_list); INC_STAT(&priv->msg_pend_stat); ipw2100_tx_send_commands(priv); ipw2100_tx_send_data(priv); spin_unlock_irqrestore(&priv->low_lock, flags); /* * We must wait for this command to complete before another * command can be sent... but if we wait more than 3 seconds * then there is a problem. */ err = wait_event_interruptible_timeout(priv->wait_command_queue, !(priv-> status & STATUS_CMD_ACTIVE), HOST_COMPLETE_TIMEOUT); if (err == 0) { IPW_DEBUG_INFO("Command completion failed out after %dms.\n", 1000 * (HOST_COMPLETE_TIMEOUT / HZ)); priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT; priv->status &= ~STATUS_CMD_ACTIVE; schedule_reset(priv); return -EIO; } if (priv->fatal_error) { printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n", priv->net_dev->name); return -EIO; } /* !!!!! HACK TEST !!!!! * When lots of debug trace statements are enabled, the driver * doesn't seem to have as many firmware restart cycles... * * As a test, we're sticking in a 1/100s delay here */ schedule_timeout_uninterruptible(msecs_to_jiffies(10)); return 0; fail_unlock: spin_unlock_irqrestore(&priv->low_lock, flags); return err; } /* * Verify the values and data access of the hardware * No locks needed or used. No functions called. */ static int ipw2100_verify(struct ipw2100_priv *priv) { u32 data1, data2; u32 address; u32 val1 = 0x76543210; u32 val2 = 0xFEDCBA98; /* Domain 0 check - all values should be DOA_DEBUG */ for (address = IPW_REG_DOA_DEBUG_AREA_START; address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) { read_register(priv->net_dev, address, &data1); if (data1 != IPW_DATA_DOA_DEBUG_VALUE) return -EIO; } /* Domain 1 check - use arbitrary read/write compare */ for (address = 0; address < 5; address++) { /* The memory area is not used now */ write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32, val1); write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36, val2); read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32, &data1); read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36, &data2); if (val1 == data1 && val2 == data2) return 0; } return -EIO; } /* * * Loop until the CARD_DISABLED bit is the same value as the * supplied parameter * * TODO: See if it would be more efficient to do a wait/wake * cycle and have the completion event trigger the wakeup * */ #define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state) { int i; u32 card_state; u32 len = sizeof(card_state); int err; for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) { err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED, &card_state, &len); if (err) { IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal " "failed.\n"); return 0; } /* We'll break out if either the HW state says it is * in the state we want, or if HOST_COMPLETE command * finishes */ if ((card_state == state) || ((priv->status & STATUS_ENABLED) ? IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) { if (state == IPW_HW_STATE_ENABLED) priv->status |= STATUS_ENABLED; else priv->status &= ~STATUS_ENABLED; return 0; } udelay(50); } IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n", state ? "DISABLED" : "ENABLED"); return -EIO; } /********************************************************************* Procedure : sw_reset_and_clock Purpose : Asserts s/w reset, asserts clock initialization and waits for clock stabilization ********************************************************************/ static int sw_reset_and_clock(struct ipw2100_priv *priv) { int i; u32 r; // assert s/w reset write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_SW_RESET); // wait for clock stabilization for (i = 0; i < 1000; i++) { udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY); // check clock ready bit read_register(priv->net_dev, IPW_REG_RESET_REG, &r); if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET) break; } if (i == 1000) return -EIO; // TODO: better error value /* set "initialization complete" bit to move adapter to * D0 state */ write_register(priv->net_dev, IPW_REG_GP_CNTRL, IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE); /* wait for clock stabilization */ for (i = 0; i < 10000; i++) { udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4); /* check clock ready bit */ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r); if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY) break; } if (i == 10000) return -EIO; /* TODO: better error value */ /* set D0 standby bit */ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r); write_register(priv->net_dev, IPW_REG_GP_CNTRL, r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY); return 0; } /********************************************************************* Procedure : ipw2100_download_firmware Purpose : Initiaze adapter after power on. The sequence is: 1. assert s/w reset first! 2. awake clocks & wait for clock stabilization 3. hold ARC (don't ask me why...) 4. load Dino ucode and reset/clock init again 5. zero-out shared mem 6. download f/w *******************************************************************/ static int ipw2100_download_firmware(struct ipw2100_priv *priv) { u32 address; int err; #ifndef CONFIG_PM /* Fetch the firmware and microcode */ struct ipw2100_fw ipw2100_firmware; #endif if (priv->fatal_error) { IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after " "fatal error %d. Interface must be brought down.\n", priv->net_dev->name, priv->fatal_error); return -EINVAL; } #ifdef CONFIG_PM if (!ipw2100_firmware.version) { err = ipw2100_get_firmware(priv, &ipw2100_firmware); if (err) { IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n", priv->net_dev->name, err); priv->fatal_error = IPW2100_ERR_FW_LOAD; goto fail; } } #else err = ipw2100_get_firmware(priv, &ipw2100_firmware); if (err) { IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n", priv->net_dev->name, err); priv->fatal_error = IPW2100_ERR_FW_LOAD; goto fail; } #endif priv->firmware_version = ipw2100_firmware.version; /* s/w reset and clock stabilization */ err = sw_reset_and_clock(priv); if (err) { IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n", priv->net_dev->name, err); goto fail; } err = ipw2100_verify(priv); if (err) { IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n", priv->net_dev->name, err); goto fail; } /* Hold ARC */ write_nic_dword(priv->net_dev, IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000); /* allow ARC to run */ write_register(priv->net_dev, IPW_REG_RESET_REG, 0); /* load microcode */ err = ipw2100_ucode_download(priv, &ipw2100_firmware); if (err) { printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n", priv->net_dev->name, err); goto fail; } /* release ARC */ write_nic_dword(priv->net_dev, IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000); /* s/w reset and clock stabilization (again!!!) */ err = sw_reset_and_clock(priv); if (err) { printk(KERN_ERR DRV_NAME ": %s: sw_reset_and_clock failed: %d\n", priv->net_dev->name, err); goto fail; } /* load f/w */ err = ipw2100_fw_download(priv, &ipw2100_firmware); if (err) { IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n", priv->net_dev->name, err); goto fail; } #ifndef CONFIG_PM /* * When the .resume method of the driver is called, the other * part of the system, i.e. the ide driver could still stay in * the suspend stage. This prevents us from loading the firmware * from the disk. --YZ */ /* free any storage allocated for firmware image */ ipw2100_release_firmware(priv, &ipw2100_firmware); #endif /* zero out Domain 1 area indirectly (Si requirement) */ for (address = IPW_HOST_FW_SHARED_AREA0; address < IPW_HOST_FW_SHARED_AREA0_END; address += 4) write_nic_dword(priv->net_dev, address, 0); for (address = IPW_HOST_FW_SHARED_AREA1; address < IPW_HOST_FW_SHARED_AREA1_END; address += 4) write_nic_dword(priv->net_dev, address, 0); for (address = IPW_HOST_FW_SHARED_AREA2; address < IPW_HOST_FW_SHARED_AREA2_END; address += 4) write_nic_dword(priv->net_dev, address, 0); for (address = IPW_HOST_FW_SHARED_AREA3; address < IPW_HOST_FW_SHARED_AREA3_END; address += 4) write_nic_dword(priv->net_dev, address, 0); for (address = IPW_HOST_FW_INTERRUPT_AREA; address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4) write_nic_dword(priv->net_dev, address, 0); return 0; fail: ipw2100_release_firmware(priv, &ipw2100_firmware); return err; } static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv) { if (priv->status & STATUS_INT_ENABLED) return; priv->status |= STATUS_INT_ENABLED; write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK); } static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv) { if (!(priv->status & STATUS_INT_ENABLED)) return; priv->status &= ~STATUS_INT_ENABLED; write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0); } static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv) { struct ipw2100_ordinals *ord = &priv->ordinals; IPW_DEBUG_INFO("enter\n"); read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1, &ord->table1_addr); read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2, &ord->table2_addr); read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size); read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size); ord->table2_size &= 0x0000FFFF; IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size); IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size); IPW_DEBUG_INFO("exit\n"); } static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv) { u32 reg = 0; /* * Set GPIO 3 writable by FW; GPIO 1 writable * by driver and enable clock */ reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE | IPW_BIT_GPIO_LED_OFF); write_register(priv->net_dev, IPW_REG_GPIO, reg); } static int rf_kill_active(struct ipw2100_priv *priv) { #define MAX_RF_KILL_CHECKS 5 #define RF_KILL_CHECK_DELAY 40 unsigned short value = 0; u32 reg = 0; int i; if (!(priv->hw_features & HW_FEATURE_RFKILL)) { wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); priv->status &= ~STATUS_RF_KILL_HW; return 0; } for (i = 0; i < MAX_RF_KILL_CHECKS; i++) { udelay(RF_KILL_CHECK_DELAY); read_register(priv->net_dev, IPW_REG_GPIO, ®); value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1); } if (value == 0) { wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); priv->status |= STATUS_RF_KILL_HW; } else { wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); priv->status &= ~STATUS_RF_KILL_HW; } return (value == 0); } static int ipw2100_get_hw_features(struct ipw2100_priv *priv) { u32 addr, len; u32 val; /* * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1 */ len = sizeof(addr); if (ipw2100_get_ordinal (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) { IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); return -EIO; } IPW_DEBUG_INFO("EEPROM address: %08X\n", addr); /* * EEPROM version is the byte at offset 0xfd in firmware * We read 4 bytes, then shift out the byte we actually want */ read_nic_dword(priv->net_dev, addr + 0xFC, &val); priv->eeprom_version = (val >> 24) & 0xFF; IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version); /* * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware * * notice that the EEPROM bit is reverse polarity, i.e. * bit = 0 signifies HW RF kill switch is supported * bit = 1 signifies HW RF kill switch is NOT supported */ read_nic_dword(priv->net_dev, addr + 0x20, &val); if (!((val >> 24) & 0x01)) priv->hw_features |= HW_FEATURE_RFKILL; IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n", (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not "); return 0; } /* * Start firmware execution after power on and intialization * The sequence is: * 1. Release ARC * 2. Wait for f/w initialization completes; */ static int ipw2100_start_adapter(struct ipw2100_priv *priv) { int i; u32 inta, inta_mask, gpio; IPW_DEBUG_INFO("enter\n"); if (priv->status & STATUS_RUNNING) return 0; /* * Initialize the hw - drive adapter to DO state by setting * init_done bit. Wait for clk_ready bit and Download * fw & dino ucode */ if (ipw2100_download_firmware(priv)) { printk(KERN_ERR DRV_NAME ": %s: Failed to power on the adapter.\n", priv->net_dev->name); return -EIO; } /* Clear the Tx, Rx and Msg queues and the r/w indexes * in the firmware RBD and TBD ring queue */ ipw2100_queues_initialize(priv); ipw2100_hw_set_gpio(priv); /* TODO -- Look at disabling interrupts here to make sure none * get fired during FW initialization */ /* Release ARC - clear reset bit */ write_register(priv->net_dev, IPW_REG_RESET_REG, 0); /* wait for f/w intialization complete */ IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n"); i = 5000; do { schedule_timeout_uninterruptible(msecs_to_jiffies(40)); /* Todo... wait for sync command ... */ read_register(priv->net_dev, IPW_REG_INTA, &inta); /* check "init done" bit */ if (inta & IPW2100_INTA_FW_INIT_DONE) { /* reset "init done" bit */ write_register(priv->net_dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE); break; } /* check error conditions : we check these after the firmware * check so that if there is an error, the interrupt handler * will see it and the adapter will be reset */ if (inta & (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) { /* clear error conditions */ write_register(priv->net_dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR); } } while (--i); /* Clear out any pending INTAs since we aren't supposed to have * interrupts enabled at this point... */ read_register(priv->net_dev, IPW_REG_INTA, &inta); read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask); inta &= IPW_INTERRUPT_MASK; /* Clear out any pending interrupts */ if (inta & inta_mask) write_register(priv->net_dev, IPW_REG_INTA, inta); IPW_DEBUG_FW("f/w initialization complete: %s\n", i ? "SUCCESS" : "FAILED"); if (!i) { printk(KERN_WARNING DRV_NAME ": %s: Firmware did not initialize.\n", priv->net_dev->name); return -EIO; } /* allow firmware to write to GPIO1 & GPIO3 */ read_register(priv->net_dev, IPW_REG_GPIO, &gpio); gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK); write_register(priv->net_dev, IPW_REG_GPIO, gpio); /* Ready to receive commands */ priv->status |= STATUS_RUNNING; /* The adapter has been reset; we are not associated */ priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED); IPW_DEBUG_INFO("exit\n"); return 0; } static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv) { if (!priv->fatal_error) return; priv->fatal_errors[priv->fatal_index++] = priv->fatal_error; priv->fatal_index %= IPW2100_ERROR_QUEUE; priv->fatal_error = 0; } /* NOTE: Our interrupt is disabled when this method is called */ static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv) { u32 reg; int i; IPW_DEBUG_INFO("Power cycling the hardware.\n"); ipw2100_hw_set_gpio(priv); /* Step 1. Stop Master Assert */ write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_STOP_MASTER); /* Step 2. Wait for stop Master Assert * (not more than 50us, otherwise ret error */ i = 5; do { udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY); read_register(priv->net_dev, IPW_REG_RESET_REG, ®); if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) break; } while (--i); priv->status &= ~STATUS_RESET_PENDING; if (!i) { IPW_DEBUG_INFO ("exit - waited too long for master assert stop\n"); return -EIO; } write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_SW_RESET); /* Reset any fatal_error conditions */ ipw2100_reset_fatalerror(priv); /* At this point, the adapter is now stopped and disabled */ priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING | STATUS_ASSOCIATED | STATUS_ENABLED); return 0; } /* * Send the CARD_DISABLE_PHY_OFF command to the card to disable it * * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent. * * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of * if STATUS_ASSN_LOST is sent. */ static int ipw2100_hw_phy_off(struct ipw2100_priv *priv) { #define HW_PHY_OFF_LOOP_DELAY (HZ / 5000) struct host_command cmd = { .host_command = CARD_DISABLE_PHY_OFF, .host_command_sequence = 0, .host_command_length = 0, }; int err, i; u32 val1, val2; IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n"); /* Turn off the radio */ err = ipw2100_hw_send_command(priv, &cmd); if (err) return err; for (i = 0; i < 2500; i++) { read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1); read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2); if ((val1 & IPW2100_CONTROL_PHY_OFF) && (val2 & IPW2100_COMMAND_PHY_OFF)) return 0; schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY); } return -EIO; } static int ipw2100_enable_adapter(struct ipw2100_priv *priv) { struct host_command cmd = { .host_command = HOST_COMPLETE, .host_command_sequence = 0, .host_command_length = 0 }; int err = 0; IPW_DEBUG_HC("HOST_COMPLETE\n"); if (priv->status & STATUS_ENABLED) return 0; mutex_lock(&priv->adapter_mutex); if (rf_kill_active(priv)) { IPW_DEBUG_HC("Command aborted due to RF kill active.\n"); goto fail_up; } err = ipw2100_hw_send_command(priv, &cmd); if (err) { IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n"); goto fail_up; } err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED); if (err) { IPW_DEBUG_INFO("%s: card not responding to init command.\n", priv->net_dev->name); goto fail_up; } if (priv->stop_hang_check) { priv->stop_hang_check = 0; schedule_delayed_work(&priv->hang_check, HZ / 2); } fail_up: mutex_unlock(&priv->adapter_mutex); return err; } static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv) { #define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100)) struct host_command cmd = { .host_command = HOST_PRE_POWER_DOWN, .host_command_sequence = 0, .host_command_length = 0, }; int err, i; u32 reg; if (!(priv->status & STATUS_RUNNING)) return 0; priv->status |= STATUS_STOPPING; /* We can only shut down the card if the firmware is operational. So, * if we haven't reset since a fatal_error, then we can not send the * shutdown commands. */ if (!priv->fatal_error) { /* First, make sure the adapter is enabled so that the PHY_OFF * command can shut it down */ ipw2100_enable_adapter(priv); err = ipw2100_hw_phy_off(priv); if (err) printk(KERN_WARNING DRV_NAME ": Error disabling radio %d\n", err); /* * If in D0-standby mode going directly to D3 may cause a * PCI bus violation. Therefore we must change out of the D0 * state. * * Sending the PREPARE_FOR_POWER_DOWN will restrict the * hardware from going into standby mode and will transition * out of D0-standby if it is already in that state. * * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the * driver upon completion. Once received, the driver can * proceed to the D3 state. * * Prepare for power down command to fw. This command would * take HW out of D0-standby and prepare it for D3 state. * * Currently FW does not support event notification for this * event. Therefore, skip waiting for it. Just wait a fixed * 100ms */ IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n"); err = ipw2100_hw_send_command(priv, &cmd); if (err) printk(KERN_WARNING DRV_NAME ": " "%s: Power down command failed: Error %d\n", priv->net_dev->name, err); else schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY); } priv->status &= ~STATUS_ENABLED; /* * Set GPIO 3 writable by FW; GPIO 1 writable * by driver and enable clock */ ipw2100_hw_set_gpio(priv); /* * Power down adapter. Sequence: * 1. Stop master assert (RESET_REG[9]=1) * 2. Wait for stop master (RESET_REG[8]==1) * 3. S/w reset assert (RESET_REG[7] = 1) */ /* Stop master assert */ write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_STOP_MASTER); /* wait stop master not more than 50 usec. * Otherwise return error. */ for (i = 5; i > 0; i--) { udelay(10); /* Check master stop bit */ read_register(priv->net_dev, IPW_REG_RESET_REG, ®); if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) break; } if (i == 0) printk(KERN_WARNING DRV_NAME ": %s: Could now power down adapter.\n", priv->net_dev->name); /* assert s/w reset */ write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_SW_RESET); priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING); return 0; } static int ipw2100_disable_adapter(struct ipw2100_priv *priv) { struct host_command cmd = { .host_command = CARD_DISABLE, .host_command_sequence = 0, .host_command_length = 0 }; int err = 0; IPW_DEBUG_HC("CARD_DISABLE\n"); if (!(priv->status & STATUS_ENABLED)) return 0; /* Make sure we clear the associated state */ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); if (!priv->stop_hang_check) { priv->stop_hang_check = 1; cancel_delayed_work(&priv->hang_check); } mutex_lock(&priv->adapter_mutex); err = ipw2100_hw_send_command(priv, &cmd); if (err) { printk(KERN_WARNING DRV_NAME ": exit - failed to send CARD_DISABLE command\n"); goto fail_up; } err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED); if (err) { printk(KERN_WARNING DRV_NAME ": exit - card failed to change to DISABLED\n"); goto fail_up; } IPW_DEBUG_INFO("TODO: implement scan state machine\n"); fail_up: mutex_unlock(&priv->adapter_mutex); return err; } static int ipw2100_set_scan_options(struct ipw2100_priv *priv) { struct host_command cmd = { .host_command = SET_SCAN_OPTIONS, .host_command_sequence = 0, .host_command_length = 8 }; int err; IPW_DEBUG_INFO("enter\n"); IPW_DEBUG_SCAN("setting scan options\n"); cmd.host_command_parameters[0] = 0; if (!(priv->config & CFG_ASSOCIATE)) cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE; if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled) cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL; if (priv->config & CFG_PASSIVE_SCAN) cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE; cmd.host_command_parameters[1] = priv->channel_mask; err = ipw2100_hw_send_command(priv, &cmd); IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n", cmd.host_command_parameters[0]); return err; } static int ipw2100_start_scan(struct ipw2100_priv *priv) { struct host_command cmd = { .host_command = BROADCAST_SCAN, .host_command_sequence = 0, .host_command_length = 4 }; int err; IPW_DEBUG_HC("START_SCAN\n"); cmd.host_command_parameters[0] = 0; /* No scanning if in monitor mode */ if (priv->ieee->iw_mode == IW_MODE_MONITOR) return 1; if (priv->status & STATUS_SCANNING) { IPW_DEBUG_SCAN("Scan requested while already in scan...\n"); return 0; } IPW_DEBUG_INFO("enter\n"); /* Not clearing here; doing so makes iwlist always return nothing... * * We should modify the table logic to use aging tables vs. clearing * the table on each scan start. */ IPW_DEBUG_SCAN("starting scan\n"); priv->status |= STATUS_SCANNING; err = ipw2100_hw_send_command(priv, &cmd); if (err) priv->status &= ~STATUS_SCANNING; IPW_DEBUG_INFO("exit\n"); return err; } static const struct libipw_geo ipw_geos[] = { { /* Restricted */ "---", .bg_channels = 14, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, {2472, 13}, {2484, 14}}, }, }; static int ipw2100_up(struct ipw2100_priv *priv, int deferred) { unsigned long flags; int rc = 0; u32 lock; u32 ord_len = sizeof(lock); /* Age scan list entries found before suspend */ if (priv->suspend_time) { libipw_networks_age(priv->ieee, priv->suspend_time); priv->suspend_time = 0; } /* Quiet if manually disabled. */ if (priv->status & STATUS_RF_KILL_SW) { IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable " "switch\n", priv->net_dev->name); return 0; } /* the ipw2100 hardware really doesn't want power management delays * longer than 175usec */ pm_qos_update_request(&ipw2100_pm_qos_req, 175); /* If the interrupt is enabled, turn it off... */ spin_lock_irqsave(&priv->low_lock, flags); ipw2100_disable_interrupts(priv); /* Reset any fatal_error conditions */ ipw2100_reset_fatalerror(priv); spin_unlock_irqrestore(&priv->low_lock, flags); if (priv->status & STATUS_POWERED || (priv->status & STATUS_RESET_PENDING)) { /* Power cycle the card ... */ if (ipw2100_power_cycle_adapter(priv)) { printk(KERN_WARNING DRV_NAME ": %s: Could not cycle adapter.\n", priv->net_dev->name); rc = 1; goto exit; } } else priv->status |= STATUS_POWERED; /* Load the firmware, start the clocks, etc. */ if (ipw2100_start_adapter(priv)) { printk(KERN_ERR DRV_NAME ": %s: Failed to start the firmware.\n", priv->net_dev->name); rc = 1; goto exit; } ipw2100_initialize_ordinals(priv); /* Determine capabilities of this particular HW configuration */ if (ipw2100_get_hw_features(priv)) { printk(KERN_ERR DRV_NAME ": %s: Failed to determine HW features.\n", priv->net_dev->name); rc = 1; goto exit; } /* Initialize the geo */ if (libipw_set_geo(priv->ieee, &ipw_geos[0])) { printk(KERN_WARNING DRV_NAME "Could not set geo\n"); return 0; } priv->ieee->freq_band = LIBIPW_24GHZ_BAND; lock = LOCK_NONE; if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) { printk(KERN_ERR DRV_NAME ": %s: Failed to clear ordinal lock.\n", priv->net_dev->name); rc = 1; goto exit; } priv->status &= ~STATUS_SCANNING; if (rf_kill_active(priv)) { printk(KERN_INFO "%s: Radio is disabled by RF switch.\n", priv->net_dev->name); if (priv->stop_rf_kill) { priv->stop_rf_kill = 0; schedule_delayed_work(&priv->rf_kill, round_jiffies_relative(HZ)); } deferred = 1; } /* Turn on the interrupt so that commands can be processed */ ipw2100_enable_interrupts(priv); /* Send all of the commands that must be sent prior to * HOST_COMPLETE */ if (ipw2100_adapter_setup(priv)) { printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n", priv->net_dev->name); rc = 1; goto exit; } if (!deferred) { /* Enable the adapter - sends HOST_COMPLETE */ if (ipw2100_enable_adapter(priv)) { printk(KERN_ERR DRV_NAME ": " "%s: failed in call to enable adapter.\n", priv->net_dev->name); ipw2100_hw_stop_adapter(priv); rc = 1; goto exit; } /* Start a scan . . . */ ipw2100_set_scan_options(priv); ipw2100_start_scan(priv); } exit: return rc; } static void ipw2100_down(struct ipw2100_priv *priv) { unsigned long flags; union iwreq_data wrqu = { .ap_addr = { .sa_family = ARPHRD_ETHER} }; int associated = priv->status & STATUS_ASSOCIATED; /* Kill the RF switch timer */ if (!priv->stop_rf_kill) { priv->stop_rf_kill = 1; cancel_delayed_work(&priv->rf_kill); } /* Kill the firmware hang check timer */ if (!priv->stop_hang_check) { priv->stop_hang_check = 1; cancel_delayed_work(&priv->hang_check); } /* Kill any pending resets */ if (priv->status & STATUS_RESET_PENDING) cancel_delayed_work(&priv->reset_work); /* Make sure the interrupt is on so that FW commands will be * processed correctly */ spin_lock_irqsave(&priv->low_lock, flags); ipw2100_enable_interrupts(priv); spin_unlock_irqrestore(&priv->low_lock, flags); if (ipw2100_hw_stop_adapter(priv)) printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n", priv->net_dev->name); /* Do not disable the interrupt until _after_ we disable * the adaptor. Otherwise the CARD_DISABLE command will never * be ack'd by the firmware */ spin_lock_irqsave(&priv->low_lock, flags); ipw2100_disable_interrupts(priv); spin_unlock_irqrestore(&priv->low_lock, flags); pm_qos_update_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); /* We have to signal any supplicant if we are disassociating */ if (associated) wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); netif_carrier_off(priv->net_dev); netif_stop_queue(priv->net_dev); } static int ipw2100_wdev_init(struct net_device *dev) { struct ipw2100_priv *priv = libipw_priv(dev); const struct libipw_geo *geo = libipw_get_geo(priv->ieee); struct wireless_dev *wdev = &priv->ieee->wdev; int i; memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); /* fill-out priv->ieee->bg_band */ if (geo->bg_channels) { struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; bg_band->band = IEEE80211_BAND_2GHZ; bg_band->n_channels = geo->bg_channels; bg_band->channels = kcalloc(geo->bg_channels, sizeof(struct ieee80211_channel), GFP_KERNEL); if (!bg_band->channels) { ipw2100_down(priv); return -ENOMEM; } /* translate geo->bg to bg_band.channels */ for (i = 0; i < geo->bg_channels; i++) { bg_band->channels[i].band = IEEE80211_BAND_2GHZ; bg_band->channels[i].center_freq = geo->bg[i].freq; bg_band->channels[i].hw_value = geo->bg[i].channel; bg_band->channels[i].max_power = geo->bg[i].max_power; if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) bg_band->channels[i].flags |= IEEE80211_CHAN_PASSIVE_SCAN; if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) bg_band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS; if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) bg_band->channels[i].flags |= IEEE80211_CHAN_RADAR; /* No equivalent for LIBIPW_CH_80211H_RULES, LIBIPW_CH_UNIFORM_SPREADING, or LIBIPW_CH_B_ONLY... */ } /* point at bitrate info */ bg_band->bitrates = ipw2100_bg_rates; bg_band->n_bitrates = RATE_COUNT; wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; } wdev->wiphy->cipher_suites = ipw_cipher_suites; wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites); set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); if (wiphy_register(wdev->wiphy)) return -EIO; return 0; } static void ipw2100_reset_adapter(struct work_struct *work) { struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv, reset_work.work); unsigned long flags; union iwreq_data wrqu = { .ap_addr = { .sa_family = ARPHRD_ETHER} }; int associated = priv->status & STATUS_ASSOCIATED; spin_lock_irqsave(&priv->low_lock, flags); IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name); priv->resets++; priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); priv->status |= STATUS_SECURITY_UPDATED; /* Force a power cycle even if interface hasn't been opened * yet */ cancel_delayed_work(&priv->reset_work); priv->status |= STATUS_RESET_PENDING; spin_unlock_irqrestore(&priv->low_lock, flags); mutex_lock(&priv->action_mutex); /* stop timed checks so that they don't interfere with reset */ priv->stop_hang_check = 1; cancel_delayed_work(&priv->hang_check); /* We have to signal any supplicant if we are disassociating */ if (associated) wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); ipw2100_up(priv, 0); mutex_unlock(&priv->action_mutex); } static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status) { #define MAC_ASSOCIATION_READ_DELAY (HZ) int ret; unsigned int len, essid_len; char essid[IW_ESSID_MAX_SIZE]; u32 txrate; u32 chan; char *txratename; u8 bssid[ETH_ALEN]; DECLARE_SSID_BUF(ssid); /* * TBD: BSSID is usually 00:00:00:00:00:00 here and not * an actual MAC of the AP. Seems like FW sets this * address too late. Read it later and expose through * /proc or schedule a later task to query and update */ essid_len = IW_ESSID_MAX_SIZE; ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &essid_len); if (ret) { IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); return; } len = sizeof(u32); ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len); if (ret) { IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); return; } len = sizeof(u32); ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len); if (ret) { IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); return; } len = ETH_ALEN; ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid, &len); if (ret) { IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); return; } memcpy(priv->ieee->bssid, bssid, ETH_ALEN); switch (txrate) { case TX_RATE_1_MBIT: txratename = "1Mbps"; break; case TX_RATE_2_MBIT: txratename = "2Mbsp"; break; case TX_RATE_5_5_MBIT: txratename = "5.5Mbps"; break; case TX_RATE_11_MBIT: txratename = "11Mbps"; break; default: IPW_DEBUG_INFO("Unknown rate: %d\n", txrate); txratename = "unknown rate"; break; } IPW_DEBUG_INFO("%s: Associated with '%s' at %s, channel %d (BSSID=%pM)\n", priv->net_dev->name, print_ssid(ssid, essid, essid_len), txratename, chan, bssid); /* now we copy read ssid into dev */ if (!(priv->config & CFG_STATIC_ESSID)) { priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE); memcpy(priv->essid, essid, priv->essid_len); } priv->channel = chan; memcpy(priv->bssid, bssid, ETH_ALEN); priv->status |= STATUS_ASSOCIATING; priv->connect_start = get_seconds(); schedule_delayed_work(&priv->wx_event_work, HZ / 10); } static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid, int length, int batch_mode) { int ssid_len = min(length, IW_ESSID_MAX_SIZE); struct host_command cmd = { .host_command = SSID, .host_command_sequence = 0, .host_command_length = ssid_len }; int err; DECLARE_SSID_BUF(ssid); IPW_DEBUG_HC("SSID: '%s'\n", print_ssid(ssid, essid, ssid_len)); if (ssid_len) memcpy(cmd.host_command_parameters, essid, ssid_len); if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to * disable auto association -- so we cheat by setting a bogus SSID */ if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) { int i; u8 *bogus = (u8 *) cmd.host_command_parameters; for (i = 0; i < IW_ESSID_MAX_SIZE; i++) bogus[i] = 0x18 + i; cmd.host_command_length = IW_ESSID_MAX_SIZE; } /* NOTE: We always send the SSID command even if the provided ESSID is * the same as what we currently think is set. */ err = ipw2100_hw_send_command(priv, &cmd); if (!err) { memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len); memcpy(priv->essid, essid, ssid_len); priv->essid_len = ssid_len; } if (!batch_mode) { if (ipw2100_enable_adapter(priv)) err = -EIO; } return err; } static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status) { DECLARE_SSID_BUF(ssid); IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "disassociated: '%s' %pM\n", print_ssid(ssid, priv->essid, priv->essid_len), priv->bssid); priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); if (priv->status & STATUS_STOPPING) { IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n"); return; } memset(priv->bssid, 0, ETH_ALEN); memset(priv->ieee->bssid, 0, ETH_ALEN); netif_carrier_off(priv->net_dev); netif_stop_queue(priv->net_dev); if (!(priv->status & STATUS_RUNNING)) return; if (priv->status & STATUS_SECURITY_UPDATED) schedule_delayed_work(&priv->security_work, 0); schedule_delayed_work(&priv->wx_event_work, 0); } static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status) { IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n", priv->net_dev->name); /* RF_KILL is now enabled (else we wouldn't be here) */ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); priv->status |= STATUS_RF_KILL_HW; /* Make sure the RF Kill check timer is running */ priv->stop_rf_kill = 0; mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ)); } static void send_scan_event(void *data) { struct ipw2100_priv *priv = data; union iwreq_data wrqu; wrqu.data.length = 0; wrqu.data.flags = 0; wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL); } static void ipw2100_scan_event_later(struct work_struct *work) { send_scan_event(container_of(work, struct ipw2100_priv, scan_event_later.work)); } static void ipw2100_scan_event_now(struct work_struct *work) { send_scan_event(container_of(work, struct ipw2100_priv, scan_event_now)); } static void isr_scan_complete(struct ipw2100_priv *priv, u32 status) { IPW_DEBUG_SCAN("scan complete\n"); /* Age the scan results... */ priv->ieee->scans++; priv->status &= ~STATUS_SCANNING; /* Only userspace-requested scan completion events go out immediately */ if (!priv->user_requested_scan) { if (!delayed_work_pending(&priv->scan_event_later)) schedule_delayed_work(&priv->scan_event_later, round_jiffies_relative(msecs_to_jiffies(4000))); } else { priv->user_requested_scan = 0; cancel_delayed_work(&priv->scan_event_later); schedule_work(&priv->scan_event_now); } } #ifdef CONFIG_IPW2100_DEBUG #define IPW2100_HANDLER(v, f) { v, f, # v } struct ipw2100_status_indicator { int status; void (*cb) (struct ipw2100_priv * priv, u32 status); char *name; }; #else #define IPW2100_HANDLER(v, f) { v, f } struct ipw2100_status_indicator { int status; void (*cb) (struct ipw2100_priv * priv, u32 status); }; #endif /* CONFIG_IPW2100_DEBUG */ static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status) { IPW_DEBUG_SCAN("Scanning...\n"); priv->status |= STATUS_SCANNING; } static const struct ipw2100_status_indicator status_handlers[] = { IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL), IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL), IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated), IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost), IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL), IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete), IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL), IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL), IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill), IPW2100_HANDLER(IPW_STATE_DISABLED, NULL), IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL), IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning), IPW2100_HANDLER(-1, NULL) }; static void isr_status_change(struct ipw2100_priv *priv, int status) { int i; if (status == IPW_STATE_SCANNING && priv->status & STATUS_ASSOCIATED && !(priv->status & STATUS_SCANNING)) { IPW_DEBUG_INFO("Scan detected while associated, with " "no scan request. Restarting firmware.\n"); /* Wake up any sleeping jobs */ schedule_reset(priv); } for (i = 0; status_handlers[i].status != -1; i++) { if (status == status_handlers[i].status) { IPW_DEBUG_NOTIF("Status change: %s\n", status_handlers[i].name); if (status_handlers[i].cb) status_handlers[i].cb(priv, status); priv->wstats.status = status; return; } } IPW_DEBUG_NOTIF("unknown status received: %04x\n", status); } static void isr_rx_complete_command(struct ipw2100_priv *priv, struct ipw2100_cmd_header *cmd) { #ifdef CONFIG_IPW2100_DEBUG if (cmd->host_command_reg < ARRAY_SIZE(command_types)) { IPW_DEBUG_HC("Command completed '%s (%d)'\n", command_types[cmd->host_command_reg], cmd->host_command_reg); } #endif if (cmd->host_command_reg == HOST_COMPLETE) priv->status |= STATUS_ENABLED; if (cmd->host_command_reg == CARD_DISABLE) priv->status &= ~STATUS_ENABLED; priv->status &= ~STATUS_CMD_ACTIVE; wake_up_interruptible(&priv->wait_command_queue); } #ifdef CONFIG_IPW2100_DEBUG static const char *frame_types[] = { "COMMAND_STATUS_VAL", "STATUS_CHANGE_VAL", "P80211_DATA_VAL", "P8023_DATA_VAL", "HOST_NOTIFICATION_VAL" }; #endif static int ipw2100_alloc_skb(struct ipw2100_priv *priv, struct ipw2100_rx_packet *packet) { packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx)); if (!packet->skb) return -ENOMEM; packet->rxp = (struct ipw2100_rx *)packet->skb->data; packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data, sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); /* NOTE: pci_map_single does not return an error code, and 0 is a valid * dma_addr */ return 0; } #define SEARCH_ERROR 0xffffffff #define SEARCH_FAIL 0xfffffffe #define SEARCH_SUCCESS 0xfffffff0 #define SEARCH_DISCARD 0 #define SEARCH_SNAPSHOT 1 #define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff)) static void ipw2100_snapshot_free(struct ipw2100_priv *priv) { int i; if (!priv->snapshot[0]) return; for (i = 0; i < 0x30; i++) kfree(priv->snapshot[i]); priv->snapshot[0] = NULL; } #ifdef IPW2100_DEBUG_C3 static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv) { int i; if (priv->snapshot[0]) return 1; for (i = 0; i < 0x30; i++) { priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC); if (!priv->snapshot[i]) { IPW_DEBUG_INFO("%s: Error allocating snapshot " "buffer %d\n", priv->net_dev->name, i); while (i > 0) kfree(priv->snapshot[--i]); priv->snapshot[0] = NULL; return 0; } } return 1; } static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf, size_t len, int mode) { u32 i, j; u32 tmp; u8 *s, *d; u32 ret; s = in_buf; if (mode == SEARCH_SNAPSHOT) { if (!ipw2100_snapshot_alloc(priv)) mode = SEARCH_DISCARD; } for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) { read_nic_dword(priv->net_dev, i, &tmp); if (mode == SEARCH_SNAPSHOT) *(u32 *) SNAPSHOT_ADDR(i) = tmp; if (ret == SEARCH_FAIL) { d = (u8 *) & tmp; for (j = 0; j < 4; j++) { if (*s != *d) { s = in_buf; continue; } s++; d++; if ((s - in_buf) == len) ret = (i + j) - len + 1; } } else if (mode == SEARCH_DISCARD) return ret; } return ret; } #endif /* * * 0) Disconnect the SKB from the firmware (just unmap) * 1) Pack the ETH header into the SKB * 2) Pass the SKB to the network stack * * When packet is provided by the firmware, it contains the following: * * . libipw_hdr * . libipw_snap_hdr * * The size of the constructed ethernet * */ #ifdef IPW2100_RX_DEBUG static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH]; #endif static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) { #ifdef IPW2100_DEBUG_C3 struct ipw2100_status *status = &priv->status_queue.drv[i]; u32 match, reg; int j; #endif IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n", i * sizeof(struct ipw2100_status)); #ifdef IPW2100_DEBUG_C3 /* Halt the firmware so we can get a good image */ write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_STOP_MASTER); j = 5; do { udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY); read_register(priv->net_dev, IPW_REG_RESET_REG, ®); if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) break; } while (j--); match = ipw2100_match_buf(priv, (u8 *) status, sizeof(struct ipw2100_status), SEARCH_SNAPSHOT); if (match < SEARCH_SUCCESS) IPW_DEBUG_INFO("%s: DMA status match in Firmware at " "offset 0x%06X, length %d:\n", priv->net_dev->name, match, sizeof(struct ipw2100_status)); else IPW_DEBUG_INFO("%s: No DMA status match in " "Firmware.\n", priv->net_dev->name); printk_buf((u8 *) priv->status_queue.drv, sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH); #endif priv->fatal_error = IPW2100_ERR_C3_CORRUPTION; priv->net_dev->stats.rx_errors++; schedule_reset(priv); } static void isr_rx(struct ipw2100_priv *priv, int i, struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; struct ipw2100_status *status = &priv->status_queue.drv[i]; struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; IPW_DEBUG_RX("Handler...\n"); if (unlikely(status->frame_size > skb_tailroom(packet->skb))) { IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!" " Dropping.\n", dev->name, status->frame_size, skb_tailroom(packet->skb)); dev->stats.rx_errors++; return; } if (unlikely(!netif_running(dev))) { dev->stats.rx_errors++; priv->wstats.discard.misc++; IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); return; } if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR && !(priv->status & STATUS_ASSOCIATED))) { IPW_DEBUG_DROP("Dropping packet while not associated.\n"); priv->wstats.discard.misc++; return; } pci_unmap_single(priv->pci_dev, packet->dma_addr, sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); skb_put(packet->skb, status->frame_size); #ifdef IPW2100_RX_DEBUG /* Make a copy of the frame so we can dump it to the logs if * libipw_rx fails */ skb_copy_from_linear_data(packet->skb, packet_data, min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH)); #endif if (!libipw_rx(priv->ieee, packet->skb, stats)) { #ifdef IPW2100_RX_DEBUG IPW_DEBUG_DROP("%s: Non consumed packet:\n", dev->name); printk_buf(IPW_DL_DROP, packet_data, status->frame_size); #endif dev->stats.rx_errors++; /* libipw_rx failed, so it didn't free the SKB */ dev_kfree_skb_any(packet->skb); packet->skb = NULL; } /* We need to allocate a new SKB and attach it to the RDB. */ if (unlikely(ipw2100_alloc_skb(priv, packet))) { printk(KERN_WARNING DRV_NAME ": " "%s: Unable to allocate SKB onto RBD ring - disabling " "adapter.\n", dev->name); /* TODO: schedule adapter shutdown */ IPW_DEBUG_INFO("TODO: Shutdown adapter...\n"); } /* Update the RDB entry */ priv->rx_queue.drv[i].host_addr = packet->dma_addr; } #ifdef CONFIG_IPW2100_MONITOR static void isr_rx_monitor(struct ipw2100_priv *priv, int i, struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; struct ipw2100_status *status = &priv->status_queue.drv[i]; struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; /* Magic struct that slots into the radiotap header -- no reason * to build this manually element by element, we can write it much * more efficiently than we can parse it. ORDER MATTERS HERE */ struct ipw_rt_hdr { struct ieee80211_radiotap_header rt_hdr; s8 rt_dbmsignal; /* signal in dbM, kluged to signed */ } *ipw_rt; IPW_DEBUG_RX("Handler...\n"); if (unlikely(status->frame_size > skb_tailroom(packet->skb) - sizeof(struct ipw_rt_hdr))) { IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!" " Dropping.\n", dev->name, status->frame_size, skb_tailroom(packet->skb)); dev->stats.rx_errors++; return; } if (unlikely(!netif_running(dev))) { dev->stats.rx_errors++; priv->wstats.discard.misc++; IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); return; } if (unlikely(priv->config & CFG_CRC_CHECK && status->flags & IPW_STATUS_FLAG_CRC_ERROR)) { IPW_DEBUG_RX("CRC error in packet. Dropping.\n"); dev->stats.rx_errors++; return; } pci_unmap_single(priv->pci_dev, packet->dma_addr, sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); memmove(packet->skb->data + sizeof(struct ipw_rt_hdr), packet->skb->data, status->frame_size); ipw_rt = (struct ipw_rt_hdr *) packet->skb->data; ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */ ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM; skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr)); if (!libipw_rx(priv->ieee, packet->skb, stats)) { dev->stats.rx_errors++; /* libipw_rx failed, so it didn't free the SKB */ dev_kfree_skb_any(packet->skb); packet->skb = NULL; } /* We need to allocate a new SKB and attach it to the RDB. */ if (unlikely(ipw2100_alloc_skb(priv, packet))) { IPW_DEBUG_WARNING( "%s: Unable to allocate SKB onto RBD ring - disabling " "adapter.\n", dev->name); /* TODO: schedule adapter shutdown */ IPW_DEBUG_INFO("TODO: Shutdown adapter...\n"); } /* Update the RDB entry */ priv->rx_queue.drv[i].host_addr = packet->dma_addr; } #endif static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i) { struct ipw2100_status *status = &priv->status_queue.drv[i]; struct ipw2100_rx *u = priv->rx_buffers[i].rxp; u16 frame_type = status->status_fields & STATUS_TYPE_MASK; switch (frame_type) { case COMMAND_STATUS_VAL: return (status->frame_size != sizeof(u->rx_data.command)); case STATUS_CHANGE_VAL: return (status->frame_size != sizeof(u->rx_data.status)); case HOST_NOTIFICATION_VAL: return (status->frame_size < sizeof(u->rx_data.notification)); case P80211_DATA_VAL: case P8023_DATA_VAL: #ifdef CONFIG_IPW2100_MONITOR return 0; #else switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) { case IEEE80211_FTYPE_MGMT: case IEEE80211_FTYPE_CTL: return 0; case IEEE80211_FTYPE_DATA: return (status->frame_size > IPW_MAX_802_11_PAYLOAD_LENGTH); } #endif } return 1; } /* * ipw2100 interrupts are disabled at this point, and the ISR * is the only code that calls this method. So, we do not need * to play with any locks. * * RX Queue works as follows: * * Read index - firmware places packet in entry identified by the * Read index and advances Read index. In this manner, * Read index will always point to the next packet to * be filled--but not yet valid. * * Write index - driver fills this entry with an unused RBD entry. * This entry has not filled by the firmware yet. * * In between the W and R indexes are the RBDs that have been received * but not yet processed. * * The process of handling packets will start at WRITE + 1 and advance * until it reaches the READ index. * * The WRITE index is cached in the variable 'priv->rx_queue.next'. * */ static void __ipw2100_rx_process(struct ipw2100_priv *priv) { struct ipw2100_bd_queue *rxq = &priv->rx_queue; struct ipw2100_status_queue *sq = &priv->status_queue; struct ipw2100_rx_packet *packet; u16 frame_type; u32 r, w, i, s; struct ipw2100_rx *u; struct libipw_rx_stats stats = { .mac_time = jiffies, }; read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r); read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w); if (r >= rxq->entries) { IPW_DEBUG_RX("exit - bad read index\n"); return; } i = (rxq->next + 1) % rxq->entries; s = i; while (i != r) { /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n", r, rxq->next, i); */ packet = &priv->rx_buffers[i]; /* Sync the DMA for the RX buffer so CPU is sure to get * the correct values */ pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr, sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); if (unlikely(ipw2100_corruption_check(priv, i))) { ipw2100_corruption_detected(priv, i); goto increment; } u = packet->rxp; frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK; stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM; stats.len = sq->drv[i].frame_size; stats.mask = 0; if (stats.rssi != 0) stats.mask |= LIBIPW_STATMASK_RSSI; stats.freq = LIBIPW_24GHZ_BAND; IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n", priv->net_dev->name, frame_types[frame_type], stats.len); switch (frame_type) { case COMMAND_STATUS_VAL: /* Reset Rx watchdog */ isr_rx_complete_command(priv, &u->rx_data.command); break; case STATUS_CHANGE_VAL: isr_status_change(priv, u->rx_data.status); break; case P80211_DATA_VAL: case P8023_DATA_VAL: #ifdef CONFIG_IPW2100_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) { isr_rx_monitor(priv, i, &stats); break; } #endif if (stats.len < sizeof(struct libipw_hdr_3addr)) break; switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) { case IEEE80211_FTYPE_MGMT: libipw_rx_mgt(priv->ieee, &u->rx_data.header, &stats); break; case IEEE80211_FTYPE_CTL: break; case IEEE80211_FTYPE_DATA: isr_rx(priv, i, &stats); break; } break; } increment: /* clear status field associated with this RBD */ rxq->drv[i].status.info.field = 0; i = (i + 1) % rxq->entries; } if (i != s) { /* backtrack one entry, wrapping to end if at 0 */ rxq->next = (i ? i : rxq->entries) - 1; write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next); } } /* * __ipw2100_tx_process * * This routine will determine whether the next packet on * the fw_pend_list has been processed by the firmware yet. * * If not, then it does nothing and returns. * * If so, then it removes the item from the fw_pend_list, frees * any associated storage, and places the item back on the * free list of its source (either msg_free_list or tx_free_list) * * TX Queue works as follows: * * Read index - points to the next TBD that the firmware will * process. The firmware will read the data, and once * done processing, it will advance the Read index. * * Write index - driver fills this entry with an constructed TBD * entry. The Write index is not advanced until the * packet has been configured. * * In between the W and R indexes are the TBDs that have NOT been * processed. Lagging behind the R index are packets that have * been processed but have not been freed by the driver. * * In order to free old storage, an internal index will be maintained * that points to the next packet to be freed. When all used * packets have been freed, the oldest index will be the same as the * firmware's read index. * * The OLDEST index is cached in the variable 'priv->tx_queue.oldest' * * Because the TBD structure can not contain arbitrary data, the * driver must keep an internal queue of cached allocations such that * it can put that data back into the tx_free_list and msg_free_list * for use by future command and data packets. * */ static int __ipw2100_tx_process(struct ipw2100_priv *priv) { struct ipw2100_bd_queue *txq = &priv->tx_queue; struct ipw2100_bd *tbd; struct list_head *element; struct ipw2100_tx_packet *packet; int descriptors_used; int e, i; u32 r, w, frag_num = 0; if (list_empty(&priv->fw_pend_list)) return 0; element = priv->fw_pend_list.next; packet = list_entry(element, struct ipw2100_tx_packet, list); tbd = &txq->drv[packet->index]; /* Determine how many TBD entries must be finished... */ switch (packet->type) { case COMMAND: /* COMMAND uses only one slot; don't advance */ descriptors_used = 1; e = txq->oldest; break; case DATA: /* DATA uses two slots; advance and loop position. */ descriptors_used = tbd->num_fragments; frag_num = tbd->num_fragments - 1; e = txq->oldest + frag_num; e %= txq->entries; break; default: printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n", priv->net_dev->name); return 0; } /* if the last TBD is not done by NIC yet, then packet is * not ready to be released. * */ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX, &r); read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, &w); if (w != txq->next) printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n", priv->net_dev->name); /* * txq->next is the index of the last packet written txq->oldest is * the index of the r is the index of the next packet to be read by * firmware */ /* * Quick graphic to help you visualize the following * if / else statement * * ===>| s---->|=============== * e>| * | a | b | c | d | e | f | g | h | i | j | k | l * r---->| * w * * w - updated by driver * r - updated by firmware * s - start of oldest BD entry (txq->oldest) * e - end of oldest BD entry * */ if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) { IPW_DEBUG_TX("exit - no processed packets ready to release.\n"); return 0; } list_del(element); DEC_STAT(&priv->fw_pend_stat); #ifdef CONFIG_IPW2100_DEBUG { i = txq->oldest; IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i, &txq->drv[i], (u32) (txq->nic + i * sizeof(struct ipw2100_bd)), txq->drv[i].host_addr, txq->drv[i].buf_length); if (packet->type == DATA) { i = (i + 1) % txq->entries; IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i, &txq->drv[i], (u32) (txq->nic + i * sizeof(struct ipw2100_bd)), (u32) txq->drv[i].host_addr, txq->drv[i].buf_length); } } #endif switch (packet->type) { case DATA: if (txq->drv[txq->oldest].status.info.fields.txType != 0) printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. " "Expecting DATA TBD but pulled " "something else: ids %d=%d.\n", priv->net_dev->name, txq->oldest, packet->index); /* DATA packet; we have to unmap and free the SKB */ for (i = 0; i < frag_num; i++) { tbd = &txq->drv[(packet->index + 1 + i) % txq->entries]; IPW_DEBUG_TX("TX%d P=%08x L=%d\n", (packet->index + 1 + i) % txq->entries, tbd->host_addr, tbd->buf_length); pci_unmap_single(priv->pci_dev, tbd->host_addr, tbd->buf_length, PCI_DMA_TODEVICE); } libipw_txb_free(packet->info.d_struct.txb); packet->info.d_struct.txb = NULL; list_add_tail(element, &priv->tx_free_list); INC_STAT(&priv->tx_free_stat); /* We have a free slot in the Tx queue, so wake up the * transmit layer if it is stopped. */ if (priv->status & STATUS_ASSOCIATED) netif_wake_queue(priv->net_dev); /* A packet was processed by the hardware, so update the * watchdog */ priv->net_dev->trans_start = jiffies; break; case COMMAND: if (txq->drv[txq->oldest].status.info.fields.txType != 1) printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. " "Expecting COMMAND TBD but pulled " "something else: ids %d=%d.\n", priv->net_dev->name, txq->oldest, packet->index); #ifdef CONFIG_IPW2100_DEBUG if (packet->info.c_struct.cmd->host_command_reg < ARRAY_SIZE(command_types)) IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n", command_types[packet->info.c_struct.cmd-> host_command_reg], packet->info.c_struct.cmd-> host_command_reg, packet->info.c_struct.cmd->cmd_status_reg); #endif list_add_tail(element, &priv->msg_free_list); INC_STAT(&priv->msg_free_stat); break; } /* advance oldest used TBD pointer to start of next entry */ txq->oldest = (e + 1) % txq->entries; /* increase available TBDs number */ txq->available += descriptors_used; SET_STAT(&priv->txq_stat, txq->available); IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n", jiffies - packet->jiffy_start); return (!list_empty(&priv->fw_pend_list)); } static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv) { int i = 0; while (__ipw2100_tx_process(priv) && i < 200) i++; if (i == 200) { printk(KERN_WARNING DRV_NAME ": " "%s: Driver is running slow (%d iters).\n", priv->net_dev->name, i); } } static void ipw2100_tx_send_commands(struct ipw2100_priv *priv) { struct list_head *element; struct ipw2100_tx_packet *packet; struct ipw2100_bd_queue *txq = &priv->tx_queue; struct ipw2100_bd *tbd; int next = txq->next; while (!list_empty(&priv->msg_pend_list)) { /* if there isn't enough space in TBD queue, then * don't stuff a new one in. * NOTE: 3 are needed as a command will take one, * and there is a minimum of 2 that must be * maintained between the r and w indexes */ if (txq->available <= 3) { IPW_DEBUG_TX("no room in tx_queue\n"); break; } element = priv->msg_pend_list.next; list_del(element); DEC_STAT(&priv->msg_pend_stat); packet = list_entry(element, struct ipw2100_tx_packet, list); IPW_DEBUG_TX("using TBD at virt=%p, phys=%04X\n", &txq->drv[txq->next], (u32) (txq->nic + txq->next * sizeof(struct ipw2100_bd))); packet->index = txq->next; tbd = &txq->drv[txq->next]; /* initialize TBD */ tbd->host_addr = packet->info.c_struct.cmd_phys; tbd->buf_length = sizeof(struct ipw2100_cmd_header); /* not marking number of fragments causes problems * with f/w debug version */ tbd->num_fragments = 1; tbd->status.info.field = IPW_BD_STATUS_TX_FRAME_COMMAND | IPW_BD_STATUS_TX_INTERRUPT_ENABLE; /* update TBD queue counters */ txq->next++; txq->next %= txq->entries; txq->available--; DEC_STAT(&priv->txq_stat); list_add_tail(element, &priv->fw_pend_list); INC_STAT(&priv->fw_pend_stat); } if (txq->next != next) { /* kick off the DMA by notifying firmware the * write index has moved; make sure TBD stores are sync'd */ wmb(); write_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, txq->next); } } /* * ipw2100_tx_send_data * */ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) { struct list_head *element; struct ipw2100_tx_packet *packet; struct ipw2100_bd_queue *txq = &priv->tx_queue; struct ipw2100_bd *tbd; int next = txq->next; int i = 0; struct ipw2100_data_header *ipw_hdr; struct libipw_hdr_3addr *hdr; while (!list_empty(&priv->tx_pend_list)) { /* if there isn't enough space in TBD queue, then * don't stuff a new one in. * NOTE: 4 are needed as a data will take two, * and there is a minimum of 2 that must be * maintained between the r and w indexes */ element = priv->tx_pend_list.next; packet = list_entry(element, struct ipw2100_tx_packet, list); if (unlikely(1 + packet->info.d_struct.txb->nr_frags > IPW_MAX_BDS)) { /* TODO: Support merging buffers if more than * IPW_MAX_BDS are used */ IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded. " "Increase fragmentation level.\n", priv->net_dev->name); } if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) { IPW_DEBUG_TX("no room in tx_queue\n"); break; } list_del(element); DEC_STAT(&priv->tx_pend_stat); tbd = &txq->drv[txq->next]; packet->index = txq->next; ipw_hdr = packet->info.d_struct.data; hdr = (struct libipw_hdr_3addr *)packet->info.d_struct.txb-> fragments[0]->data; if (priv->ieee->iw_mode == IW_MODE_INFRA) { /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN); memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN); } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) { /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN); memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN); } ipw_hdr->host_command_reg = SEND; ipw_hdr->host_command_reg1 = 0; /* For now we only support host based encryption */ ipw_hdr->needs_encryption = 0; ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted; if (packet->info.d_struct.txb->nr_frags > 1) ipw_hdr->fragment_size = packet->info.d_struct.txb->frag_size - LIBIPW_3ADDR_LEN; else ipw_hdr->fragment_size = 0; tbd->host_addr = packet->info.d_struct.data_phys; tbd->buf_length = sizeof(struct ipw2100_data_header); tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags; tbd->status.info.field = IPW_BD_STATUS_TX_FRAME_802_3 | IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT; txq->next++; txq->next %= txq->entries; IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n", packet->index, tbd->host_addr, tbd->buf_length); #ifdef CONFIG_IPW2100_DEBUG if (packet->info.d_struct.txb->nr_frags > 1) IPW_DEBUG_FRAG("fragment Tx: %d frames\n", packet->info.d_struct.txb->nr_frags); #endif for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) { tbd = &txq->drv[txq->next]; if (i == packet->info.d_struct.txb->nr_frags - 1) tbd->status.info.field = IPW_BD_STATUS_TX_FRAME_802_3 | IPW_BD_STATUS_TX_INTERRUPT_ENABLE; else tbd->status.info.field = IPW_BD_STATUS_TX_FRAME_802_3 | IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT; tbd->buf_length = packet->info.d_struct.txb-> fragments[i]->len - LIBIPW_3ADDR_LEN; tbd->host_addr = pci_map_single(priv->pci_dev, packet->info.d_struct. txb->fragments[i]-> data + LIBIPW_3ADDR_LEN, tbd->buf_length, PCI_DMA_TODEVICE); IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n", txq->next, tbd->host_addr, tbd->buf_length); pci_dma_sync_single_for_device(priv->pci_dev, tbd->host_addr, tbd->buf_length, PCI_DMA_TODEVICE); txq->next++; txq->next %= txq->entries; } txq->available -= 1 + packet->info.d_struct.txb->nr_frags; SET_STAT(&priv->txq_stat, txq->available); list_add_tail(element, &priv->fw_pend_list); INC_STAT(&priv->fw_pend_stat); } if (txq->next != next) { /* kick off the DMA by notifying firmware the * write index has moved; make sure TBD stores are sync'd */ write_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, txq->next); } } static void ipw2100_irq_tasklet(struct ipw2100_priv *priv) { struct net_device *dev = priv->net_dev; unsigned long flags; u32 inta, tmp; spin_lock_irqsave(&priv->low_lock, flags); ipw2100_disable_interrupts(priv); read_register(dev, IPW_REG_INTA, &inta); IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n", (unsigned long)inta & IPW_INTERRUPT_MASK); priv->in_isr++; priv->interrupts++; /* We do not loop and keep polling for more interrupts as this * is frowned upon and doesn't play nicely with other potentially * chained IRQs */ IPW_DEBUG_ISR("INTA: 0x%08lX\n", (unsigned long)inta & IPW_INTERRUPT_MASK); if (inta & IPW2100_INTA_FATAL_ERROR) { printk(KERN_WARNING DRV_NAME ": Fatal interrupt. Scheduling firmware restart.\n"); priv->inta_other++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR); read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error); IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n", priv->net_dev->name, priv->fatal_error); read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp); IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n", priv->net_dev->name, tmp); /* Wake up any sleeping jobs */ schedule_reset(priv); } if (inta & IPW2100_INTA_PARITY_ERROR) { printk(KERN_ERR DRV_NAME ": ***** PARITY ERROR INTERRUPT !!!!\n"); priv->inta_other++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR); } if (inta & IPW2100_INTA_RX_TRANSFER) { IPW_DEBUG_ISR("RX interrupt\n"); priv->rx_interrupts++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER); __ipw2100_rx_process(priv); __ipw2100_tx_complete(priv); } if (inta & IPW2100_INTA_TX_TRANSFER) { IPW_DEBUG_ISR("TX interrupt\n"); priv->tx_interrupts++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER); __ipw2100_tx_complete(priv); ipw2100_tx_send_commands(priv); ipw2100_tx_send_data(priv); } if (inta & IPW2100_INTA_TX_COMPLETE) { IPW_DEBUG_ISR("TX complete\n"); priv->inta_other++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE); __ipw2100_tx_complete(priv); } if (inta & IPW2100_INTA_EVENT_INTERRUPT) { /* ipw2100_handle_event(dev); */ priv->inta_other++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT); } if (inta & IPW2100_INTA_FW_INIT_DONE) { IPW_DEBUG_ISR("FW init done interrupt\n"); priv->inta_other++; read_register(dev, IPW_REG_INTA, &tmp); if (tmp & (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) { write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR); } write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE); } if (inta & IPW2100_INTA_STATUS_CHANGE) { IPW_DEBUG_ISR("Status change interrupt\n"); priv->inta_other++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE); } if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) { IPW_DEBUG_ISR("slave host mode interrupt\n"); priv->inta_other++; write_register(dev, IPW_REG_INTA, IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE); } priv->in_isr--; ipw2100_enable_interrupts(priv); spin_unlock_irqrestore(&priv->low_lock, flags); IPW_DEBUG_ISR("exit\n"); } static irqreturn_t ipw2100_interrupt(int irq, void *data) { struct ipw2100_priv *priv = data; u32 inta, inta_mask; if (!data) return IRQ_NONE; spin_lock(&priv->low_lock); /* We check to see if we should be ignoring interrupts before * we touch the hardware. During ucode load if we try and handle * an interrupt we can cause keyboard problems as well as cause * the ucode to fail to initialize */ if (!(priv->status & STATUS_INT_ENABLED)) { /* Shared IRQ */ goto none; } read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask); read_register(priv->net_dev, IPW_REG_INTA, &inta); if (inta == 0xFFFFFFFF) { /* Hardware disappeared */ printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n"); goto none; } inta &= IPW_INTERRUPT_MASK; if (!(inta & inta_mask)) { /* Shared interrupt */ goto none; } /* We disable the hardware interrupt here just to prevent unneeded * calls to be made. We disable this again within the actual * work tasklet, so if another part of the code re-enables the * interrupt, that is fine */ ipw2100_disable_interrupts(priv); tasklet_schedule(&priv->irq_tasklet); spin_unlock(&priv->low_lock); return IRQ_HANDLED; none: spin_unlock(&priv->low_lock); return IRQ_NONE; } static netdev_tx_t ipw2100_tx(struct libipw_txb *txb, struct net_device *dev, int pri) { struct ipw2100_priv *priv = libipw_priv(dev); struct list_head *element; struct ipw2100_tx_packet *packet; unsigned long flags; spin_lock_irqsave(&priv->low_lock, flags); if (!(priv->status & STATUS_ASSOCIATED)) { IPW_DEBUG_INFO("Can not transmit when not connected.\n"); priv->net_dev->stats.tx_carrier_errors++; netif_stop_queue(dev); goto fail_unlock; } if (list_empty(&priv->tx_free_list)) goto fail_unlock; element = priv->tx_free_list.next; packet = list_entry(element, struct ipw2100_tx_packet, list); packet->info.d_struct.txb = txb; IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len); printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len); packet->jiffy_start = jiffies; list_del(element); DEC_STAT(&priv->tx_free_stat); list_add_tail(element, &priv->tx_pend_list); INC_STAT(&priv->tx_pend_stat); ipw2100_tx_send_data(priv); spin_unlock_irqrestore(&priv->low_lock, flags); return NETDEV_TX_OK; fail_unlock: netif_stop_queue(dev); spin_unlock_irqrestore(&priv->low_lock, flags); return NETDEV_TX_BUSY; } static int ipw2100_msg_allocate(struct ipw2100_priv *priv) { int i, j, err = -EINVAL; void *v; dma_addr_t p; priv->msg_buffers = kmalloc(IPW_COMMAND_POOL_SIZE * sizeof(struct ipw2100_tx_packet), GFP_KERNEL); if (!priv->msg_buffers) return -ENOMEM; for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) { v = pci_alloc_consistent(priv->pci_dev, sizeof(struct ipw2100_cmd_header), &p); if (!v) { printk(KERN_ERR DRV_NAME ": " "%s: PCI alloc failed for msg " "buffers.\n", priv->net_dev->name); err = -ENOMEM; break; } memset(v, 0, sizeof(struct ipw2100_cmd_header)); priv->msg_buffers[i].type = COMMAND; priv->msg_buffers[i].info.c_struct.cmd = (struct ipw2100_cmd_header *)v; priv->msg_buffers[i].info.c_struct.cmd_phys = p; } if (i == IPW_COMMAND_POOL_SIZE) return 0; for (j = 0; j < i; j++) { pci_free_consistent(priv->pci_dev, sizeof(struct ipw2100_cmd_header), priv->msg_buffers[j].info.c_struct.cmd, priv->msg_buffers[j].info.c_struct. cmd_phys); } kfree(priv->msg_buffers); priv->msg_buffers = NULL; return err; } static int ipw2100_msg_initialize(struct ipw2100_priv *priv) { int i; INIT_LIST_HEAD(&priv->msg_free_list); INIT_LIST_HEAD(&priv->msg_pend_list); for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list); SET_STAT(&priv->msg_free_stat, i); return 0; } static void ipw2100_msg_free(struct ipw2100_priv *priv) { int i; if (!priv->msg_buffers) return; for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) { pci_free_consistent(priv->pci_dev, sizeof(struct ipw2100_cmd_header), priv->msg_buffers[i].info.c_struct.cmd, priv->msg_buffers[i].info.c_struct. cmd_phys); } kfree(priv->msg_buffers); priv->msg_buffers = NULL; } static ssize_t show_pci(struct device *d, struct device_attribute *attr, char *buf) { struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev); char *out = buf; int i, j; u32 val; for (i = 0; i < 16; i++) { out += sprintf(out, "[%08X] ", i * 16); for (j = 0; j < 16; j += 4) { pci_read_config_dword(pci_dev, i * 16 + j, &val); out += sprintf(out, "%08X ", val); } out += sprintf(out, "\n"); } return out - buf; } static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL); static ssize_t show_cfg(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *p = dev_get_drvdata(d); return sprintf(buf, "0x%08x\n", (int)p->config); } static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); static ssize_t show_status(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *p = dev_get_drvdata(d); return sprintf(buf, "0x%08x\n", (int)p->status); } static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); static ssize_t show_capability(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *p = dev_get_drvdata(d); return sprintf(buf, "0x%08x\n", (int)p->capability); } static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL); #define IPW2100_REG(x) { IPW_ ##x, #x } static const struct { u32 addr; const char *name; } hw_data[] = { IPW2100_REG(REG_GP_CNTRL), IPW2100_REG(REG_GPIO), IPW2100_REG(REG_INTA), IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),}; #define IPW2100_NIC(x, s) { x, #x, s } static const struct { u32 addr; const char *name; size_t size; } nic_data[] = { IPW2100_NIC(IPW2100_CONTROL_REG, 2), IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),}; #define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d } static const struct { u8 index; const char *name; const char *desc; } ord_data[] = { IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"), IPW2100_ORD(STAT_TX_HOST_COMPLETE, "successful Host Tx's (MSDU)"), IPW2100_ORD(STAT_TX_DIR_DATA, "successful Directed Tx's (MSDU)"), IPW2100_ORD(STAT_TX_DIR_DATA1, "successful Directed Tx's (MSDU) @ 1MB"), IPW2100_ORD(STAT_TX_DIR_DATA2, "successful Directed Tx's (MSDU) @ 2MB"), IPW2100_ORD(STAT_TX_DIR_DATA5_5, "successful Directed Tx's (MSDU) @ 5_5MB"), IPW2100_ORD(STAT_TX_DIR_DATA11, "successful Directed Tx's (MSDU) @ 11MB"), IPW2100_ORD(STAT_TX_NODIR_DATA1, "successful Non_Directed Tx's (MSDU) @ 1MB"), IPW2100_ORD(STAT_TX_NODIR_DATA2, "successful Non_Directed Tx's (MSDU) @ 2MB"), IPW2100_ORD(STAT_TX_NODIR_DATA5_5, "successful Non_Directed Tx's (MSDU) @ 5.5MB"), IPW2100_ORD(STAT_TX_NODIR_DATA11, "successful Non_Directed Tx's (MSDU) @ 11MB"), IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"), IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"), IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"), IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"), IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"), IPW2100_ORD(STAT_TX_ASSN_RESP, "successful Association response Tx's"), IPW2100_ORD(STAT_TX_REASSN, "successful Reassociation Tx's"), IPW2100_ORD(STAT_TX_REASSN_RESP, "successful Reassociation response Tx's"), IPW2100_ORD(STAT_TX_PROBE, "probes successfully transmitted"), IPW2100_ORD(STAT_TX_PROBE_RESP, "probe responses successfully transmitted"), IPW2100_ORD(STAT_TX_BEACON, "tx beacon"), IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"), IPW2100_ORD(STAT_TX_DISASSN, "successful Disassociation TX"), IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"), IPW2100_ORD(STAT_TX_DEAUTH, "successful Deauthentication TX"), IPW2100_ORD(STAT_TX_TOTAL_BYTES, "Total successful Tx data bytes"), IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"), IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"), IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"), IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"), IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"), IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"), IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP, "times max tries in a hop failed"), IPW2100_ORD(STAT_TX_DISASSN_FAIL, "times disassociation failed"), IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"), IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"), IPW2100_ORD(STAT_RX_HOST, "packets passed to host"), IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"), IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"), IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"), IPW2100_ORD(STAT_RX_DIR_DATA5_5, "directed packets at 5.5MB"), IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"), IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"), IPW2100_ORD(STAT_RX_NODIR_DATA1, "nondirected packets at 1MB"), IPW2100_ORD(STAT_RX_NODIR_DATA2, "nondirected packets at 2MB"), IPW2100_ORD(STAT_RX_NODIR_DATA5_5, "nondirected packets at 5.5MB"), IPW2100_ORD(STAT_RX_NODIR_DATA11, "nondirected packets at 11MB"), IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"), IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS, "Rx CTS"), IPW2100_ORD(STAT_RX_ACK, "Rx ACK"), IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"), IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"), IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"), IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"), IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"), IPW2100_ORD(STAT_RX_REASSN_RESP, "Reassociation response Rx's"), IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"), IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"), IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"), IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"), IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"), IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"), IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"), IPW2100_ORD(STAT_RX_TOTAL_BYTES, "Total rx data bytes received"), IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"), IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"), IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"), IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"), IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"), IPW2100_ORD(STAT_RX_DUPLICATE1, "duplicate rx packets at 1MB"), IPW2100_ORD(STAT_RX_DUPLICATE2, "duplicate rx packets at 2MB"), IPW2100_ORD(STAT_RX_DUPLICATE5_5, "duplicate rx packets at 5.5MB"), IPW2100_ORD(STAT_RX_DUPLICATE11, "duplicate rx packets at 11MB"), IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"), IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"), IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"), IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"), IPW2100_ORD(STAT_RX_INVALID_PROTOCOL, "rx frames with invalid protocol"), IPW2100_ORD(SYS_BOOT_TIME, "Boot time"), IPW2100_ORD(STAT_RX_NO_BUFFER, "rx frames rejected due to no buffer"), IPW2100_ORD(STAT_RX_MISSING_FRAG, "rx frames dropped due to missing fragment"), IPW2100_ORD(STAT_RX_ORPHAN_FRAG, "rx frames dropped due to non-sequential fragment"), IPW2100_ORD(STAT_RX_ORPHAN_FRAME, "rx frames dropped due to unmatched 1st frame"), IPW2100_ORD(STAT_RX_FRAG_AGEOUT, "rx frames dropped due to uncompleted frame"), IPW2100_ORD(STAT_RX_ICV_ERRORS, "ICV errors during decryption"), IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"), IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"), IPW2100_ORD(STAT_PSP_POLL_TIMEOUT, "poll response timeouts"), IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT, "timeouts waiting for last {broad,multi}cast pkt"), IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"), IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"), IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"), IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"), IPW2100_ORD(STAT_PERCENT_MISSED_BCNS, "current calculation of % missed beacons"), IPW2100_ORD(STAT_PERCENT_RETRIES, "current calculation of % missed tx retries"), IPW2100_ORD(ASSOCIATED_AP_PTR, "0 if not associated, else pointer to AP table entry"), IPW2100_ORD(AVAILABLE_AP_CNT, "AP's decsribed in the AP table"), IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"), IPW2100_ORD(STAT_AP_ASSNS, "associations"), IPW2100_ORD(STAT_ASSN_FAIL, "association failures"), IPW2100_ORD(STAT_ASSN_RESP_FAIL, "failures due to response fail"), IPW2100_ORD(STAT_FULL_SCANS, "full scans"), IPW2100_ORD(CARD_DISABLED, "Card Disabled"), IPW2100_ORD(STAT_ROAM_INHIBIT, "times roaming was inhibited due to activity"), IPW2100_ORD(RSSI_AT_ASSN, "RSSI of associated AP at time of association"), IPW2100_ORD(STAT_ASSN_CAUSE1, "reassociation: no probe response or TX on hop"), IPW2100_ORD(STAT_ASSN_CAUSE2, "reassociation: poor tx/rx quality"), IPW2100_ORD(STAT_ASSN_CAUSE3, "reassociation: tx/rx quality (excessive AP load"), IPW2100_ORD(STAT_ASSN_CAUSE4, "reassociation: AP RSSI level"), IPW2100_ORD(STAT_ASSN_CAUSE5, "reassociations due to load leveling"), IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"), IPW2100_ORD(STAT_AUTH_RESP_FAIL, "times authentication response failed"), IPW2100_ORD(STATION_TABLE_CNT, "entries in association table"), IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"), IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"), IPW2100_ORD(COUNTRY_CODE, "IEEE country code as recv'd from beacon"), IPW2100_ORD(COUNTRY_CHANNELS, "channels supported by country"), IPW2100_ORD(RESET_CNT, "adapter resets (warm)"), IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"), IPW2100_ORD(ANTENNA_DIVERSITY, "TRUE if antenna diversity is disabled"), IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"), IPW2100_ORD(OUR_FREQ, "current radio freq lower digits - channel ID"), IPW2100_ORD(RTC_TIME, "current RTC time"), IPW2100_ORD(PORT_TYPE, "operating mode"), IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"), IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"), IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"), IPW2100_ORD(BASIC_RATES, "basic tx rates"), IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"), IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"), IPW2100_ORD(CAPABILITIES, "Management frame capability field"), IPW2100_ORD(AUTH_TYPE, "Type of authentication"), IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"), IPW2100_ORD(RTS_THRESHOLD, "Min packet length for RTS handshaking"), IPW2100_ORD(INT_MODE, "International mode"), IPW2100_ORD(FRAGMENTATION_THRESHOLD, "protocol frag threshold"), IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS, "EEPROM offset in SRAM"), IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE, "EEPROM size in SRAM"), IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"), IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS, "EEPROM IBSS 11b channel set"), IPW2100_ORD(MAC_VERSION, "MAC Version"), IPW2100_ORD(MAC_REVISION, "MAC Revision"), IPW2100_ORD(RADIO_VERSION, "Radio Version"), IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"), IPW2100_ORD(UCODE_VERSION, "Ucode Version"),}; static ssize_t show_registers(struct device *d, struct device_attribute *attr, char *buf) { int i; struct ipw2100_priv *priv = dev_get_drvdata(d); struct net_device *dev = priv->net_dev; char *out = buf; u32 val = 0; out += sprintf(out, "%30s [Address ] : Hex\n", "Register"); for (i = 0; i < ARRAY_SIZE(hw_data); i++) { read_register(dev, hw_data[i].addr, &val); out += sprintf(out, "%30s [%08X] : %08X\n", hw_data[i].name, hw_data[i].addr, val); } return out - buf; } static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); static ssize_t show_hardware(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); struct net_device *dev = priv->net_dev; char *out = buf; int i; out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry"); for (i = 0; i < ARRAY_SIZE(nic_data); i++) { u8 tmp8; u16 tmp16; u32 tmp32; switch (nic_data[i].size) { case 1: read_nic_byte(dev, nic_data[i].addr, &tmp8); out += sprintf(out, "%30s [%08X] : %02X\n", nic_data[i].name, nic_data[i].addr, tmp8); break; case 2: read_nic_word(dev, nic_data[i].addr, &tmp16); out += sprintf(out, "%30s [%08X] : %04X\n", nic_data[i].name, nic_data[i].addr, tmp16); break; case 4: read_nic_dword(dev, nic_data[i].addr, &tmp32); out += sprintf(out, "%30s [%08X] : %08X\n", nic_data[i].name, nic_data[i].addr, tmp32); break; } } return out - buf; } static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL); static ssize_t show_memory(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); struct net_device *dev = priv->net_dev; static unsigned long loop = 0; int len = 0; u32 buffer[4]; int i; char line[81]; if (loop >= 0x30000) loop = 0; /* sysfs provides us PAGE_SIZE buffer */ while (len < PAGE_SIZE - 128 && loop < 0x30000) { if (priv->snapshot[0]) for (i = 0; i < 4; i++) buffer[i] = *(u32 *) SNAPSHOT_ADDR(loop + i * 4); else for (i = 0; i < 4; i++) read_nic_dword(dev, loop + i * 4, &buffer[i]); if (priv->dump_raw) len += sprintf(buf + len, "%c%c%c%c" "%c%c%c%c" "%c%c%c%c" "%c%c%c%c", ((u8 *) buffer)[0x0], ((u8 *) buffer)[0x1], ((u8 *) buffer)[0x2], ((u8 *) buffer)[0x3], ((u8 *) buffer)[0x4], ((u8 *) buffer)[0x5], ((u8 *) buffer)[0x6], ((u8 *) buffer)[0x7], ((u8 *) buffer)[0x8], ((u8 *) buffer)[0x9], ((u8 *) buffer)[0xa], ((u8 *) buffer)[0xb], ((u8 *) buffer)[0xc], ((u8 *) buffer)[0xd], ((u8 *) buffer)[0xe], ((u8 *) buffer)[0xf]); else len += sprintf(buf + len, "%s\n", snprint_line(line, sizeof(line), (u8 *) buffer, 16, loop)); loop += 16; } return len; } static ssize_t store_memory(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw2100_priv *priv = dev_get_drvdata(d); struct net_device *dev = priv->net_dev; const char *p = buf; (void)dev; /* kill unused-var warning for debug-only code */ if (count < 1) return count; if (p[0] == '1' || (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) { IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n", dev->name); priv->dump_raw = 1; } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'f')) { IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n", dev->name); priv->dump_raw = 0; } else if (tolower(p[0]) == 'r') { IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name); ipw2100_snapshot_free(priv); } else IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, " "reset = clear memory snapshot\n", dev->name); return count; } static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory); static ssize_t show_ordinals(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); u32 val = 0; int len = 0; u32 val_len; static int loop = 0; if (priv->status & STATUS_RF_KILL_MASK) return 0; if (loop >= ARRAY_SIZE(ord_data)) loop = 0; /* sysfs provides us PAGE_SIZE buffer */ while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) { val_len = sizeof(u32); if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val, &val_len)) len += sprintf(buf + len, "[0x%02X] = ERROR %s\n", ord_data[loop].index, ord_data[loop].desc); else len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n", ord_data[loop].index, val, ord_data[loop].desc); loop++; } return len; } static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL); static ssize_t show_stats(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); char *out = buf; out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n", priv->interrupts, priv->tx_interrupts, priv->rx_interrupts, priv->inta_other); out += sprintf(out, "firmware resets: %d\n", priv->resets); out += sprintf(out, "firmware hangs: %d\n", priv->hangs); #ifdef CONFIG_IPW2100_DEBUG out += sprintf(out, "packet mismatch image: %s\n", priv->snapshot[0] ? "YES" : "NO"); #endif return out - buf; } static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL); static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode) { int err; if (mode == priv->ieee->iw_mode) return 0; err = ipw2100_disable_adapter(priv); if (err) { printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n", priv->net_dev->name, err); return err; } switch (mode) { case IW_MODE_INFRA: priv->net_dev->type = ARPHRD_ETHER; break; case IW_MODE_ADHOC: priv->net_dev->type = ARPHRD_ETHER; break; #ifdef CONFIG_IPW2100_MONITOR case IW_MODE_MONITOR: priv->last_mode = priv->ieee->iw_mode; priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; break; #endif /* CONFIG_IPW2100_MONITOR */ } priv->ieee->iw_mode = mode; #ifdef CONFIG_PM /* Indicate ipw2100_download_firmware download firmware * from disk instead of memory. */ ipw2100_firmware.version = 0; #endif printk(KERN_INFO "%s: Resetting on mode change.\n", priv->net_dev->name); priv->reset_backoff = 0; schedule_reset(priv); return 0; } static ssize_t show_internals(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); int len = 0; #define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x) if (priv->status & STATUS_ASSOCIATED) len += sprintf(buf + len, "connected: %lu\n", get_seconds() - priv->connect_start); else len += sprintf(buf + len, "not connected\n"); DUMP_VAR(ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx], "p"); DUMP_VAR(status, "08lx"); DUMP_VAR(config, "08lx"); DUMP_VAR(capability, "08lx"); len += sprintf(buf + len, "last_rtc: %lu\n", (unsigned long)priv->last_rtc); DUMP_VAR(fatal_error, "d"); DUMP_VAR(stop_hang_check, "d"); DUMP_VAR(stop_rf_kill, "d"); DUMP_VAR(messages_sent, "d"); DUMP_VAR(tx_pend_stat.value, "d"); DUMP_VAR(tx_pend_stat.hi, "d"); DUMP_VAR(tx_free_stat.value, "d"); DUMP_VAR(tx_free_stat.lo, "d"); DUMP_VAR(msg_free_stat.value, "d"); DUMP_VAR(msg_free_stat.lo, "d"); DUMP_VAR(msg_pend_stat.value, "d"); DUMP_VAR(msg_pend_stat.hi, "d"); DUMP_VAR(fw_pend_stat.value, "d"); DUMP_VAR(fw_pend_stat.hi, "d"); DUMP_VAR(txq_stat.value, "d"); DUMP_VAR(txq_stat.lo, "d"); DUMP_VAR(ieee->scans, "d"); DUMP_VAR(reset_backoff, "d"); return len; } static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL); static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); char essid[IW_ESSID_MAX_SIZE + 1]; u8 bssid[ETH_ALEN]; u32 chan = 0; char *out = buf; unsigned int length; int ret; if (priv->status & STATUS_RF_KILL_MASK) return 0; memset(essid, 0, sizeof(essid)); memset(bssid, 0, sizeof(bssid)); length = IW_ESSID_MAX_SIZE; ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length); if (ret) IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); length = sizeof(bssid); ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid, &length); if (ret) IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); length = sizeof(u32); ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length); if (ret) IPW_DEBUG_INFO("failed querying ordinals at line %d\n", __LINE__); out += sprintf(out, "ESSID: %s\n", essid); out += sprintf(out, "BSSID: %pM\n", bssid); out += sprintf(out, "Channel: %d\n", chan); return out - buf; } static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL); #ifdef CONFIG_IPW2100_DEBUG static ssize_t show_debug_level(struct device_driver *d, char *buf) { return sprintf(buf, "0x%08X\n", ipw2100_debug_level); } static ssize_t store_debug_level(struct device_driver *d, const char *buf, size_t count) { char *p = (char *)buf; u32 val; if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { p++; if (p[0] == 'x' || p[0] == 'X') p++; val = simple_strtoul(p, &p, 16); } else val = simple_strtoul(p, &p, 10); if (p == buf) IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf); else ipw2100_debug_level = val; return strnlen(buf, count); } static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level, store_debug_level); #endif /* CONFIG_IPW2100_DEBUG */ static ssize_t show_fatal_error(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); char *out = buf; int i; if (priv->fatal_error) out += sprintf(out, "0x%08X\n", priv->fatal_error); else out += sprintf(out, "0\n"); for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) { if (!priv->fatal_errors[(priv->fatal_index - i) % IPW2100_ERROR_QUEUE]) continue; out += sprintf(out, "%d. 0x%08X\n", i, priv->fatal_errors[(priv->fatal_index - i) % IPW2100_ERROR_QUEUE]); } return out - buf; } static ssize_t store_fatal_error(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw2100_priv *priv = dev_get_drvdata(d); schedule_reset(priv); return count; } static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error, store_fatal_error); static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, char *buf) { struct ipw2100_priv *priv = dev_get_drvdata(d); return sprintf(buf, "%d\n", priv->ieee->scan_age); } static ssize_t store_scan_age(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw2100_priv *priv = dev_get_drvdata(d); struct net_device *dev = priv->net_dev; char buffer[] = "00000000"; unsigned long len = (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1; unsigned long val; char *p = buffer; (void)dev; /* kill unused-var warning for debug-only code */ IPW_DEBUG_INFO("enter\n"); strncpy(buffer, buf, len); buffer[len] = 0; if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { p++; if (p[0] == 'x' || p[0] == 'X') p++; val = simple_strtoul(p, &p, 16); } else val = simple_strtoul(p, &p, 10); if (p == buffer) { IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name); } else { priv->ieee->scan_age = val; IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age); } IPW_DEBUG_INFO("exit\n"); return len; } static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age); static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, char *buf) { /* 0 - RF kill not enabled 1 - SW based RF kill active (sysfs) 2 - HW based RF kill active 3 - Both HW and SW baed RF kill active */ struct ipw2100_priv *priv = dev_get_drvdata(d); int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | (rf_kill_active(priv) ? 0x2 : 0x0); return sprintf(buf, "%i\n", val); } static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio) { if ((disable_radio ? 1 : 0) == (priv->status & STATUS_RF_KILL_SW ? 1 : 0)) return 0; IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n", disable_radio ? "OFF" : "ON"); mutex_lock(&priv->action_mutex); if (disable_radio) { priv->status |= STATUS_RF_KILL_SW; ipw2100_down(priv); } else { priv->status &= ~STATUS_RF_KILL_SW; if (rf_kill_active(priv)) { IPW_DEBUG_RF_KILL("Can not turn radio back on - " "disabled by HW switch\n"); /* Make sure the RF_KILL check timer is running */ priv->stop_rf_kill = 0; mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ)); } else schedule_reset(priv); } mutex_unlock(&priv->action_mutex); return 1; } static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw2100_priv *priv = dev_get_drvdata(d); ipw_radio_kill_sw(priv, buf[0] == '1'); return count; } static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill); static struct attribute *ipw2100_sysfs_entries[] = { &dev_attr_hardware.attr, &dev_attr_registers.attr, &dev_attr_ordinals.attr, &dev_attr_pci.attr, &dev_attr_stats.attr, &dev_attr_internals.attr, &dev_attr_bssinfo.attr, &dev_attr_memory.attr, &dev_attr_scan_age.attr, &dev_attr_fatal_error.attr, &dev_attr_rf_kill.attr, &dev_attr_cfg.attr, &dev_attr_status.attr, &dev_attr_capability.attr, NULL, }; static struct attribute_group ipw2100_attribute_group = { .attrs = ipw2100_sysfs_entries, }; static int status_queue_allocate(struct ipw2100_priv *priv, int entries) { struct ipw2100_status_queue *q = &priv->status_queue; IPW_DEBUG_INFO("enter\n"); q->size = entries * sizeof(struct ipw2100_status); q->drv = (struct ipw2100_status *)pci_alloc_consistent(priv->pci_dev, q->size, &q->nic); if (!q->drv) { IPW_DEBUG_WARNING("Can not allocate status queue.\n"); return -ENOMEM; } memset(q->drv, 0, q->size); IPW_DEBUG_INFO("exit\n"); return 0; } static void status_queue_free(struct ipw2100_priv *priv) { IPW_DEBUG_INFO("enter\n"); if (priv->status_queue.drv) { pci_free_consistent(priv->pci_dev, priv->status_queue.size, priv->status_queue.drv, priv->status_queue.nic); priv->status_queue.drv = NULL; } IPW_DEBUG_INFO("exit\n"); } static int bd_queue_allocate(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q, int entries) { IPW_DEBUG_INFO("enter\n"); memset(q, 0, sizeof(struct ipw2100_bd_queue)); q->entries = entries; q->size = entries * sizeof(struct ipw2100_bd); q->drv = pci_alloc_consistent(priv->pci_dev, q->size, &q->nic); if (!q->drv) { IPW_DEBUG_INFO ("can't allocate shared memory for buffer descriptors\n"); return -ENOMEM; } memset(q->drv, 0, q->size); IPW_DEBUG_INFO("exit\n"); return 0; } static void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q) { IPW_DEBUG_INFO("enter\n"); if (!q) return; if (q->drv) { pci_free_consistent(priv->pci_dev, q->size, q->drv, q->nic); q->drv = NULL; } IPW_DEBUG_INFO("exit\n"); } static void bd_queue_initialize(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q, u32 base, u32 size, u32 r, u32 w) { IPW_DEBUG_INFO("enter\n"); IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv, (u32) q->nic); write_register(priv->net_dev, base, q->nic); write_register(priv->net_dev, size, q->entries); write_register(priv->net_dev, r, q->oldest); write_register(priv->net_dev, w, q->next); IPW_DEBUG_INFO("exit\n"); } static void ipw2100_kill_works(struct ipw2100_priv *priv) { priv->stop_rf_kill = 1; priv->stop_hang_check = 1; cancel_delayed_work_sync(&priv->reset_work); cancel_delayed_work_sync(&priv->security_work); cancel_delayed_work_sync(&priv->wx_event_work); cancel_delayed_work_sync(&priv->hang_check); cancel_delayed_work_sync(&priv->rf_kill); cancel_work_sync(&priv->scan_event_now); cancel_delayed_work_sync(&priv->scan_event_later); } static int ipw2100_tx_allocate(struct ipw2100_priv *priv) { int i, j, err = -EINVAL; void *v; dma_addr_t p; IPW_DEBUG_INFO("enter\n"); err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH); if (err) { IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n", priv->net_dev->name); return err; } priv->tx_buffers = kmalloc(TX_PENDED_QUEUE_LENGTH * sizeof(struct ipw2100_tx_packet), GFP_ATOMIC); if (!priv->tx_buffers) { printk(KERN_ERR DRV_NAME ": %s: alloc failed form tx buffers.\n", priv->net_dev->name); bd_queue_free(priv, &priv->tx_queue); return -ENOMEM; } for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { v = pci_alloc_consistent(priv->pci_dev, sizeof(struct ipw2100_data_header), &p); if (!v) { printk(KERN_ERR DRV_NAME ": %s: PCI alloc failed for tx " "buffers.\n", priv->net_dev->name); err = -ENOMEM; break; } priv->tx_buffers[i].type = DATA; priv->tx_buffers[i].info.d_struct.data = (struct ipw2100_data_header *)v; priv->tx_buffers[i].info.d_struct.data_phys = p; priv->tx_buffers[i].info.d_struct.txb = NULL; } if (i == TX_PENDED_QUEUE_LENGTH) return 0; for (j = 0; j < i; j++) { pci_free_consistent(priv->pci_dev, sizeof(struct ipw2100_data_header), priv->tx_buffers[j].info.d_struct.data, priv->tx_buffers[j].info.d_struct. data_phys); } kfree(priv->tx_buffers); priv->tx_buffers = NULL; return err; } static void ipw2100_tx_initialize(struct ipw2100_priv *priv) { int i; IPW_DEBUG_INFO("enter\n"); /* * reinitialize packet info lists */ INIT_LIST_HEAD(&priv->fw_pend_list); INIT_STAT(&priv->fw_pend_stat); /* * reinitialize lists */ INIT_LIST_HEAD(&priv->tx_pend_list); INIT_LIST_HEAD(&priv->tx_free_list); INIT_STAT(&priv->tx_pend_stat); INIT_STAT(&priv->tx_free_stat); for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { /* We simply drop any SKBs that have been queued for * transmit */ if (priv->tx_buffers[i].info.d_struct.txb) { libipw_txb_free(priv->tx_buffers[i].info.d_struct. txb); priv->tx_buffers[i].info.d_struct.txb = NULL; } list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list); } SET_STAT(&priv->tx_free_stat, i); priv->tx_queue.oldest = 0; priv->tx_queue.available = priv->tx_queue.entries; priv->tx_queue.next = 0; INIT_STAT(&priv->txq_stat); SET_STAT(&priv->txq_stat, priv->tx_queue.available); bd_queue_initialize(priv, &priv->tx_queue, IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE, IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX); IPW_DEBUG_INFO("exit\n"); } static void ipw2100_tx_free(struct ipw2100_priv *priv) { int i; IPW_DEBUG_INFO("enter\n"); bd_queue_free(priv, &priv->tx_queue); if (!priv->tx_buffers) return; for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { if (priv->tx_buffers[i].info.d_struct.txb) { libipw_txb_free(priv->tx_buffers[i].info.d_struct. txb); priv->tx_buffers[i].info.d_struct.txb = NULL; } if (priv->tx_buffers[i].info.d_struct.data) pci_free_consistent(priv->pci_dev, sizeof(struct ipw2100_data_header), priv->tx_buffers[i].info.d_struct. data, priv->tx_buffers[i].info.d_struct. data_phys); } kfree(priv->tx_buffers); priv->tx_buffers = NULL; IPW_DEBUG_INFO("exit\n"); } static int ipw2100_rx_allocate(struct ipw2100_priv *priv) { int i, j, err = -EINVAL; IPW_DEBUG_INFO("enter\n"); err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH); if (err) { IPW_DEBUG_INFO("failed bd_queue_allocate\n"); return err; } err = status_queue_allocate(priv, RX_QUEUE_LENGTH); if (err) { IPW_DEBUG_INFO("failed status_queue_allocate\n"); bd_queue_free(priv, &priv->rx_queue); return err; } /* * allocate packets */ priv->rx_buffers = kmalloc(RX_QUEUE_LENGTH * sizeof(struct ipw2100_rx_packet), GFP_KERNEL); if (!priv->rx_buffers) { IPW_DEBUG_INFO("can't allocate rx packet buffer table\n"); bd_queue_free(priv, &priv->rx_queue); status_queue_free(priv); return -ENOMEM; } for (i = 0; i < RX_QUEUE_LENGTH; i++) { struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; err = ipw2100_alloc_skb(priv, packet); if (unlikely(err)) { err = -ENOMEM; break; } /* The BD holds the cache aligned address */ priv->rx_queue.drv[i].host_addr = packet->dma_addr; priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH; priv->status_queue.drv[i].status_fields = 0; } if (i == RX_QUEUE_LENGTH) return 0; for (j = 0; j < i; j++) { pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr, sizeof(struct ipw2100_rx_packet), PCI_DMA_FROMDEVICE); dev_kfree_skb(priv->rx_buffers[j].skb); } kfree(priv->rx_buffers); priv->rx_buffers = NULL; bd_queue_free(priv, &priv->rx_queue); status_queue_free(priv); return err; } static void ipw2100_rx_initialize(struct ipw2100_priv *priv) { IPW_DEBUG_INFO("enter\n"); priv->rx_queue.oldest = 0; priv->rx_queue.available = priv->rx_queue.entries - 1; priv->rx_queue.next = priv->rx_queue.entries - 1; INIT_STAT(&priv->rxq_stat); SET_STAT(&priv->rxq_stat, priv->rx_queue.available); bd_queue_initialize(priv, &priv->rx_queue, IPW_MEM_HOST_SHARED_RX_BD_BASE, IPW_MEM_HOST_SHARED_RX_BD_SIZE, IPW_MEM_HOST_SHARED_RX_READ_INDEX, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX); /* set up the status queue */ write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE, priv->status_queue.nic); IPW_DEBUG_INFO("exit\n"); } static void ipw2100_rx_free(struct ipw2100_priv *priv) { int i; IPW_DEBUG_INFO("enter\n"); bd_queue_free(priv, &priv->rx_queue); status_queue_free(priv); if (!priv->rx_buffers) return; for (i = 0; i < RX_QUEUE_LENGTH; i++) { if (priv->rx_buffers[i].rxp) { pci_unmap_single(priv->pci_dev, priv->rx_buffers[i].dma_addr, sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); dev_kfree_skb(priv->rx_buffers[i].skb); } } kfree(priv->rx_buffers); priv->rx_buffers = NULL; IPW_DEBUG_INFO("exit\n"); } static int ipw2100_read_mac_address(struct ipw2100_priv *priv) { u32 length = ETH_ALEN; u8 addr[ETH_ALEN]; int err; err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length); if (err) { IPW_DEBUG_INFO("MAC address read failed\n"); return -EIO; } memcpy(priv->net_dev->dev_addr, addr, ETH_ALEN); IPW_DEBUG_INFO("card MAC is %pM\n", priv->net_dev->dev_addr); return 0; } /******************************************************************** * * Firmware Commands * ********************************************************************/ static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode) { struct host_command cmd = { .host_command = ADAPTER_ADDRESS, .host_command_sequence = 0, .host_command_length = ETH_ALEN }; int err; IPW_DEBUG_HC("SET_MAC_ADDRESS\n"); IPW_DEBUG_INFO("enter\n"); if (priv->config & CFG_CUSTOM_MAC) { memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN); memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); } else memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr, ETH_ALEN); err = ipw2100_hw_send_command(priv, &cmd); IPW_DEBUG_INFO("exit\n"); return err; } static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type, int batch_mode) { struct host_command cmd = { .host_command = PORT_TYPE, .host_command_sequence = 0, .host_command_length = sizeof(u32) }; int err; switch (port_type) { case IW_MODE_INFRA: cmd.host_command_parameters[0] = IPW_BSS; break; case IW_MODE_ADHOC: cmd.host_command_parameters[0] = IPW_IBSS; break; } IPW_DEBUG_HC("PORT_TYPE: %s\n", port_type == IPW_IBSS ? "Ad-Hoc" : "Managed"); if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) { printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n", priv->net_dev->name, err); return err; } } /* send cmd to firmware */ err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) ipw2100_enable_adapter(priv); return err; } static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel, int batch_mode) { struct host_command cmd = { .host_command = CHANNEL, .host_command_sequence = 0, .host_command_length = sizeof(u32) }; int err; cmd.host_command_parameters[0] = channel; IPW_DEBUG_HC("CHANNEL: %d\n", channel); /* If BSS then we don't support channel selection */ if (priv->ieee->iw_mode == IW_MODE_INFRA) return 0; if ((channel != 0) && ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL))) return -EINVAL; if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } err = ipw2100_hw_send_command(priv, &cmd); if (err) { IPW_DEBUG_INFO("Failed to set channel to %d", channel); return err; } if (channel) priv->config |= CFG_STATIC_CHANNEL; else priv->config &= ~CFG_STATIC_CHANNEL; priv->channel = channel; if (!batch_mode) { err = ipw2100_enable_adapter(priv); if (err) return err; } return 0; } static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode) { struct host_command cmd = { .host_command = SYSTEM_CONFIG, .host_command_sequence = 0, .host_command_length = 12, }; u32 ibss_mask, len = sizeof(u32); int err; /* Set system configuration */ if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } if (priv->ieee->iw_mode == IW_MODE_ADHOC) cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START; cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK | IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE; if (!(priv->config & CFG_LONG_PREAMBLE)) cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO; err = ipw2100_get_ordinal(priv, IPW_ORD_EEPROM_IBSS_11B_CHANNELS, &ibss_mask, &len); if (err) ibss_mask = IPW_IBSS_11B_DEFAULT_MASK; cmd.host_command_parameters[1] = REG_CHANNEL_MASK; cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask; /* 11b only */ /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */ err = ipw2100_hw_send_command(priv, &cmd); if (err) return err; /* If IPv6 is configured in the kernel then we don't want to filter out all * of the multicast packets as IPv6 needs some. */ #if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) cmd.host_command = ADD_MULTICAST; cmd.host_command_sequence = 0; cmd.host_command_length = 0; ipw2100_hw_send_command(priv, &cmd); #endif if (!batch_mode) { err = ipw2100_enable_adapter(priv); if (err) return err; } return 0; } static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate, int batch_mode) { struct host_command cmd = { .host_command = BASIC_TX_RATES, .host_command_sequence = 0, .host_command_length = 4 }; int err; cmd.host_command_parameters[0] = rate & TX_RATE_MASK; if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } /* Set BASIC TX Rate first */ ipw2100_hw_send_command(priv, &cmd); /* Set TX Rate */ cmd.host_command = TX_RATES; ipw2100_hw_send_command(priv, &cmd); /* Set MSDU TX Rate */ cmd.host_command = MSDU_TX_RATES; ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) { err = ipw2100_enable_adapter(priv); if (err) return err; } priv->tx_rates = rate; return 0; } static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level) { struct host_command cmd = { .host_command = POWER_MODE, .host_command_sequence = 0, .host_command_length = 4 }; int err; cmd.host_command_parameters[0] = power_level; err = ipw2100_hw_send_command(priv, &cmd); if (err) return err; if (power_level == IPW_POWER_MODE_CAM) priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); else priv->power_mode = IPW_POWER_ENABLED | power_level; #ifdef IPW2100_TX_POWER if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) { /* Set beacon interval */ cmd.host_command = TX_POWER_INDEX; cmd.host_command_parameters[0] = (u32) priv->adhoc_power; err = ipw2100_hw_send_command(priv, &cmd); if (err) return err; } #endif return 0; } static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold) { struct host_command cmd = { .host_command = RTS_THRESHOLD, .host_command_sequence = 0, .host_command_length = 4 }; int err; if (threshold & RTS_DISABLED) cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD; else cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED; err = ipw2100_hw_send_command(priv, &cmd); if (err) return err; priv->rts_threshold = threshold; return 0; } #if 0 int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv, u32 threshold, int batch_mode) { struct host_command cmd = { .host_command = FRAG_THRESHOLD, .host_command_sequence = 0, .host_command_length = 4, .host_command_parameters[0] = 0, }; int err; if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } if (threshold == 0) threshold = DEFAULT_FRAG_THRESHOLD; else { threshold = max(threshold, MIN_FRAG_THRESHOLD); threshold = min(threshold, MAX_FRAG_THRESHOLD); } cmd.host_command_parameters[0] = threshold; IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold); err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) ipw2100_enable_adapter(priv); if (!err) priv->frag_threshold = threshold; return err; } #endif static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry) { struct host_command cmd = { .host_command = SHORT_RETRY_LIMIT, .host_command_sequence = 0, .host_command_length = 4 }; int err; cmd.host_command_parameters[0] = retry; err = ipw2100_hw_send_command(priv, &cmd); if (err) return err; priv->short_retry_limit = retry; return 0; } static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry) { struct host_command cmd = { .host_command = LONG_RETRY_LIMIT, .host_command_sequence = 0, .host_command_length = 4 }; int err; cmd.host_command_parameters[0] = retry; err = ipw2100_hw_send_command(priv, &cmd); if (err) return err; priv->long_retry_limit = retry; return 0; } static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid, int batch_mode) { struct host_command cmd = { .host_command = MANDATORY_BSSID, .host_command_sequence = 0, .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN }; int err; #ifdef CONFIG_IPW2100_DEBUG if (bssid != NULL) IPW_DEBUG_HC("MANDATORY_BSSID: %pM\n", bssid); else IPW_DEBUG_HC("MANDATORY_BSSID: \n"); #endif /* if BSSID is empty then we disable mandatory bssid mode */ if (bssid != NULL) memcpy(cmd.host_command_parameters, bssid, ETH_ALEN); if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) ipw2100_enable_adapter(priv); return err; } static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv) { struct host_command cmd = { .host_command = DISASSOCIATION_BSSID, .host_command_sequence = 0, .host_command_length = ETH_ALEN }; int err; int len; IPW_DEBUG_HC("DISASSOCIATION_BSSID\n"); len = ETH_ALEN; /* The Firmware currently ignores the BSSID and just disassociates from * the currently associated AP -- but in the off chance that a future * firmware does use the BSSID provided here, we go ahead and try and * set it to the currently associated AP's BSSID */ memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN); err = ipw2100_hw_send_command(priv, &cmd); return err; } static int ipw2100_set_wpa_ie(struct ipw2100_priv *, struct ipw2100_wpa_assoc_frame *, int) __attribute__ ((unused)); static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv, struct ipw2100_wpa_assoc_frame *wpa_frame, int batch_mode) { struct host_command cmd = { .host_command = SET_WPA_IE, .host_command_sequence = 0, .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame), }; int err; IPW_DEBUG_HC("SET_WPA_IE\n"); if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } memcpy(cmd.host_command_parameters, wpa_frame, sizeof(struct ipw2100_wpa_assoc_frame)); err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) { if (ipw2100_enable_adapter(priv)) err = -EIO; } return err; } struct security_info_params { u32 allowed_ciphers; u16 version; u8 auth_mode; u8 replay_counters_number; u8 unicast_using_group; } __packed; static int ipw2100_set_security_information(struct ipw2100_priv *priv, int auth_mode, int security_level, int unicast_using_group, int batch_mode) { struct host_command cmd = { .host_command = SET_SECURITY_INFORMATION, .host_command_sequence = 0, .host_command_length = sizeof(struct security_info_params) }; struct security_info_params *security = (struct security_info_params *)&cmd.host_command_parameters; int err; memset(security, 0, sizeof(*security)); /* If shared key AP authentication is turned on, then we need to * configure the firmware to try and use it. * * Actual data encryption/decryption is handled by the host. */ security->auth_mode = auth_mode; security->unicast_using_group = unicast_using_group; switch (security_level) { default: case SEC_LEVEL_0: security->allowed_ciphers = IPW_NONE_CIPHER; break; case SEC_LEVEL_1: security->allowed_ciphers = IPW_WEP40_CIPHER | IPW_WEP104_CIPHER; break; case SEC_LEVEL_2: security->allowed_ciphers = IPW_WEP40_CIPHER | IPW_WEP104_CIPHER | IPW_TKIP_CIPHER; break; case SEC_LEVEL_2_CKIP: security->allowed_ciphers = IPW_WEP40_CIPHER | IPW_WEP104_CIPHER | IPW_CKIP_CIPHER; break; case SEC_LEVEL_3: security->allowed_ciphers = IPW_WEP40_CIPHER | IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER; break; } IPW_DEBUG_HC ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n", security->auth_mode, security->allowed_ciphers, security_level); security->replay_counters_number = 0; if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) ipw2100_enable_adapter(priv); return err; } static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power) { struct host_command cmd = { .host_command = TX_POWER_INDEX, .host_command_sequence = 0, .host_command_length = 4 }; int err = 0; u32 tmp = tx_power; if (tx_power != IPW_TX_POWER_DEFAULT) tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 / (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM); cmd.host_command_parameters[0] = tmp; if (priv->ieee->iw_mode == IW_MODE_ADHOC) err = ipw2100_hw_send_command(priv, &cmd); if (!err) priv->tx_power = tx_power; return 0; } static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv, u32 interval, int batch_mode) { struct host_command cmd = { .host_command = BEACON_INTERVAL, .host_command_sequence = 0, .host_command_length = 4 }; int err; cmd.host_command_parameters[0] = interval; IPW_DEBUG_INFO("enter\n"); if (priv->ieee->iw_mode == IW_MODE_ADHOC) { if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) { err = ipw2100_enable_adapter(priv); if (err) return err; } } IPW_DEBUG_INFO("exit\n"); return 0; } static void ipw2100_queues_initialize(struct ipw2100_priv *priv) { ipw2100_tx_initialize(priv); ipw2100_rx_initialize(priv); ipw2100_msg_initialize(priv); } static void ipw2100_queues_free(struct ipw2100_priv *priv) { ipw2100_tx_free(priv); ipw2100_rx_free(priv); ipw2100_msg_free(priv); } static int ipw2100_queues_allocate(struct ipw2100_priv *priv) { if (ipw2100_tx_allocate(priv) || ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv)) goto fail; return 0; fail: ipw2100_tx_free(priv); ipw2100_rx_free(priv); ipw2100_msg_free(priv); return -ENOMEM; } #define IPW_PRIVACY_CAPABLE 0x0008 static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags, int batch_mode) { struct host_command cmd = { .host_command = WEP_FLAGS, .host_command_sequence = 0, .host_command_length = 4 }; int err; cmd.host_command_parameters[0] = flags; IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags); if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) { printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n", priv->net_dev->name, err); return err; } } /* send cmd to firmware */ err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) ipw2100_enable_adapter(priv); return err; } struct ipw2100_wep_key { u8 idx; u8 len; u8 key[13]; }; /* Macros to ease up priting WEP keys */ #define WEP_FMT_64 "%02X%02X%02X%02X-%02X" #define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X" #define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4] #define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10] /** * Set a the wep key * * @priv: struct to work on * @idx: index of the key we want to set * @key: ptr to the key data to set * @len: length of the buffer at @key * @batch_mode: FIXME perform the operation in batch mode, not * disabling the device. * * @returns 0 if OK, < 0 errno code on error. * * Fill out a command structure with the new wep key, length an * index and send it down the wire. */ static int ipw2100_set_key(struct ipw2100_priv *priv, int idx, char *key, int len, int batch_mode) { int keylen = len ? (len <= 5 ? 5 : 13) : 0; struct host_command cmd = { .host_command = WEP_KEY_INFO, .host_command_sequence = 0, .host_command_length = sizeof(struct ipw2100_wep_key), }; struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters; int err; IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n", idx, keylen, len); /* NOTE: We don't check cached values in case the firmware was reset * or some other problem is occurring. If the user is setting the key, * then we push the change */ wep_key->idx = idx; wep_key->len = keylen; if (keylen) { memcpy(wep_key->key, key, len); memset(wep_key->key + len, 0, keylen - len); } /* Will be optimized out on debug not being configured in */ if (keylen == 0) IPW_DEBUG_WEP("%s: Clearing key %d\n", priv->net_dev->name, wep_key->idx); else if (keylen == 5) IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n", priv->net_dev->name, wep_key->idx, wep_key->len, WEP_STR_64(wep_key->key)); else IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128 "\n", priv->net_dev->name, wep_key->idx, wep_key->len, WEP_STR_128(wep_key->key)); if (!batch_mode) { err = ipw2100_disable_adapter(priv); /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */ if (err) { printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n", priv->net_dev->name, err); return err; } } /* send cmd to firmware */ err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) { int err2 = ipw2100_enable_adapter(priv); if (err == 0) err = err2; } return err; } static int ipw2100_set_key_index(struct ipw2100_priv *priv, int idx, int batch_mode) { struct host_command cmd = { .host_command = WEP_KEY_INDEX, .host_command_sequence = 0, .host_command_length = 4, .host_command_parameters = {idx}, }; int err; IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx); if (idx < 0 || idx > 3) return -EINVAL; if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) { printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n", priv->net_dev->name, err); return err; } } /* send cmd to firmware */ err = ipw2100_hw_send_command(priv, &cmd); if (!batch_mode) ipw2100_enable_adapter(priv); return err; } static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode) { int i, err, auth_mode, sec_level, use_group; if (!(priv->status & STATUS_RUNNING)) return 0; if (!batch_mode) { err = ipw2100_disable_adapter(priv); if (err) return err; } if (!priv->ieee->sec.enabled) { err = ipw2100_set_security_information(priv, IPW_AUTH_OPEN, SEC_LEVEL_0, 0, 1); } else { auth_mode = IPW_AUTH_OPEN; if (priv->ieee->sec.flags & SEC_AUTH_MODE) { if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY) auth_mode = IPW_AUTH_SHARED; else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP) auth_mode = IPW_AUTH_LEAP_CISCO_ID; } sec_level = SEC_LEVEL_0; if (priv->ieee->sec.flags & SEC_LEVEL) sec_level = priv->ieee->sec.level; use_group = 0; if (priv->ieee->sec.flags & SEC_UNICAST_GROUP) use_group = priv->ieee->sec.unicast_uses_group; err = ipw2100_set_security_information(priv, auth_mode, sec_level, use_group, 1); } if (err) goto exit; if (priv->ieee->sec.enabled) { for (i = 0; i < 4; i++) { if (!(priv->ieee->sec.flags & (1 << i))) { memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN); priv->ieee->sec.key_sizes[i] = 0; } else { err = ipw2100_set_key(priv, i, priv->ieee->sec.keys[i], priv->ieee->sec. key_sizes[i], 1); if (err) goto exit; } } ipw2100_set_key_index(priv, priv->ieee->crypt_info.tx_keyidx, 1); } /* Always enable privacy so the Host can filter WEP packets if * encrypted data is sent up */ err = ipw2100_set_wep_flags(priv, priv->ieee->sec. enabled ? IPW_PRIVACY_CAPABLE : 0, 1); if (err) goto exit; priv->status &= ~STATUS_SECURITY_UPDATED; exit: if (!batch_mode) ipw2100_enable_adapter(priv); return err; } static void ipw2100_security_work(struct work_struct *work) { struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv, security_work.work); /* If we happen to have reconnected before we get a chance to * process this, then update the security settings--which causes * a disassociation to occur */ if (!(priv->status & STATUS_ASSOCIATED) && priv->status & STATUS_SECURITY_UPDATED) ipw2100_configure_security(priv, 0); } static void shim__set_security(struct net_device *dev, struct libipw_security *sec) { struct ipw2100_priv *priv = libipw_priv(dev); int i, force_update = 0; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) goto done; for (i = 0; i < 4; i++) { if (sec->flags & (1 << i)) { priv->ieee->sec.key_sizes[i] = sec->key_sizes[i]; if (sec->key_sizes[i] == 0) priv->ieee->sec.flags &= ~(1 << i); else memcpy(priv->ieee->sec.keys[i], sec->keys[i], sec->key_sizes[i]); if (sec->level == SEC_LEVEL_1) { priv->ieee->sec.flags |= (1 << i); priv->status |= STATUS_SECURITY_UPDATED; } else priv->ieee->sec.flags &= ~(1 << i); } } if ((sec->flags & SEC_ACTIVE_KEY) && priv->ieee->sec.active_key != sec->active_key) { if (sec->active_key <= 3) { priv->ieee->sec.active_key = sec->active_key; priv->ieee->sec.flags |= SEC_ACTIVE_KEY; } else priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; priv->status |= STATUS_SECURITY_UPDATED; } if ((sec->flags & SEC_AUTH_MODE) && (priv->ieee->sec.auth_mode != sec->auth_mode)) { priv->ieee->sec.auth_mode = sec->auth_mode; priv->ieee->sec.flags |= SEC_AUTH_MODE; priv->status |= STATUS_SECURITY_UPDATED; } if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) { priv->ieee->sec.flags |= SEC_ENABLED; priv->ieee->sec.enabled = sec->enabled; priv->status |= STATUS_SECURITY_UPDATED; force_update = 1; } if (sec->flags & SEC_ENCRYPT) priv->ieee->sec.encrypt = sec->encrypt; if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) { priv->ieee->sec.level = sec->level; priv->ieee->sec.flags |= SEC_LEVEL; priv->status |= STATUS_SECURITY_UPDATED; } IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n", priv->ieee->sec.flags & (1 << 8) ? '1' : '0', priv->ieee->sec.flags & (1 << 7) ? '1' : '0', priv->ieee->sec.flags & (1 << 6) ? '1' : '0', priv->ieee->sec.flags & (1 << 5) ? '1' : '0', priv->ieee->sec.flags & (1 << 4) ? '1' : '0', priv->ieee->sec.flags & (1 << 3) ? '1' : '0', priv->ieee->sec.flags & (1 << 2) ? '1' : '0', priv->ieee->sec.flags & (1 << 1) ? '1' : '0', priv->ieee->sec.flags & (1 << 0) ? '1' : '0'); /* As a temporary work around to enable WPA until we figure out why * wpa_supplicant toggles the security capability of the driver, which * forces a disassocation with force_update... * * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) ipw2100_configure_security(priv, 0); done: mutex_unlock(&priv->action_mutex); } static int ipw2100_adapter_setup(struct ipw2100_priv *priv) { int err; int batch_mode = 1; u8 *bssid; IPW_DEBUG_INFO("enter\n"); err = ipw2100_disable_adapter(priv); if (err) return err; #ifdef CONFIG_IPW2100_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) { err = ipw2100_set_channel(priv, priv->channel, batch_mode); if (err) return err; IPW_DEBUG_INFO("exit\n"); return 0; } #endif /* CONFIG_IPW2100_MONITOR */ err = ipw2100_read_mac_address(priv); if (err) return -EIO; err = ipw2100_set_mac_address(priv, batch_mode); if (err) return err; err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode); if (err) return err; if (priv->ieee->iw_mode == IW_MODE_ADHOC) { err = ipw2100_set_channel(priv, priv->channel, batch_mode); if (err) return err; } err = ipw2100_system_config(priv, batch_mode); if (err) return err; err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode); if (err) return err; /* Default to power mode OFF */ err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM); if (err) return err; err = ipw2100_set_rts_threshold(priv, priv->rts_threshold); if (err) return err; if (priv->config & CFG_STATIC_BSSID) bssid = priv->bssid; else bssid = NULL; err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode); if (err) return err; if (priv->config & CFG_STATIC_ESSID) err = ipw2100_set_essid(priv, priv->essid, priv->essid_len, batch_mode); else err = ipw2100_set_essid(priv, NULL, 0, batch_mode); if (err) return err; err = ipw2100_configure_security(priv, batch_mode); if (err) return err; if (priv->ieee->iw_mode == IW_MODE_ADHOC) { err = ipw2100_set_ibss_beacon_interval(priv, priv->beacon_interval, batch_mode); if (err) return err; err = ipw2100_set_tx_power(priv, priv->tx_power); if (err) return err; } /* err = ipw2100_set_fragmentation_threshold( priv, priv->frag_threshold, batch_mode); if (err) return err; */ IPW_DEBUG_INFO("exit\n"); return 0; } /************************************************************************* * * EXTERNALLY CALLED METHODS * *************************************************************************/ /* This method is called by the network layer -- not to be confused with * ipw2100_set_mac_address() declared above called by this driver (and this * method as well) to talk to the firmware */ static int ipw2100_set_address(struct net_device *dev, void *p) { struct ipw2100_priv *priv = libipw_priv(dev); struct sockaddr *addr = p; int err = 0; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; mutex_lock(&priv->action_mutex); priv->config |= CFG_CUSTOM_MAC; memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); err = ipw2100_set_mac_address(priv, 0); if (err) goto done; priv->reset_backoff = 0; mutex_unlock(&priv->action_mutex); ipw2100_reset_adapter(&priv->reset_work.work); return 0; done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_open(struct net_device *dev) { struct ipw2100_priv *priv = libipw_priv(dev); unsigned long flags; IPW_DEBUG_INFO("dev->open\n"); spin_lock_irqsave(&priv->low_lock, flags); if (priv->status & STATUS_ASSOCIATED) { netif_carrier_on(dev); netif_start_queue(dev); } spin_unlock_irqrestore(&priv->low_lock, flags); return 0; } static int ipw2100_close(struct net_device *dev) { struct ipw2100_priv *priv = libipw_priv(dev); unsigned long flags; struct list_head *element; struct ipw2100_tx_packet *packet; IPW_DEBUG_INFO("enter\n"); spin_lock_irqsave(&priv->low_lock, flags); if (priv->status & STATUS_ASSOCIATED) netif_carrier_off(dev); netif_stop_queue(dev); /* Flush the TX queue ... */ while (!list_empty(&priv->tx_pend_list)) { element = priv->tx_pend_list.next; packet = list_entry(element, struct ipw2100_tx_packet, list); list_del(element); DEC_STAT(&priv->tx_pend_stat); libipw_txb_free(packet->info.d_struct.txb); packet->info.d_struct.txb = NULL; list_add_tail(element, &priv->tx_free_list); INC_STAT(&priv->tx_free_stat); } spin_unlock_irqrestore(&priv->low_lock, flags); IPW_DEBUG_INFO("exit\n"); return 0; } /* * TODO: Fix this function... its just wrong */ static void ipw2100_tx_timeout(struct net_device *dev) { struct ipw2100_priv *priv = libipw_priv(dev); dev->stats.tx_errors++; #ifdef CONFIG_IPW2100_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) return; #endif IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n", dev->name); schedule_reset(priv); } static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value) { /* This is called when wpa_supplicant loads and closes the driver * interface. */ priv->ieee->wpa_enabled = value; return 0; } static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value) { struct libipw_device *ieee = priv->ieee; struct libipw_security sec = { .flags = SEC_AUTH_MODE, }; int ret = 0; if (value & IW_AUTH_ALG_SHARED_KEY) { sec.auth_mode = WLAN_AUTH_SHARED_KEY; ieee->open_wep = 0; } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) { sec.auth_mode = WLAN_AUTH_OPEN; ieee->open_wep = 1; } else if (value & IW_AUTH_ALG_LEAP) { sec.auth_mode = WLAN_AUTH_LEAP; ieee->open_wep = 1; } else return -EINVAL; if (ieee->set_security) ieee->set_security(ieee->dev, &sec); else ret = -EOPNOTSUPP; return ret; } static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv, char *wpa_ie, int wpa_ie_len) { struct ipw2100_wpa_assoc_frame frame; frame.fixed_ie_mask = 0; /* copy WPA IE */ memcpy(frame.var_ie, wpa_ie, wpa_ie_len); frame.var_ie_len = wpa_ie_len; /* make sure WPA is enabled */ ipw2100_wpa_enable(priv, 1); ipw2100_set_wpa_ie(priv, &frame, 0); } static void ipw_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct ipw2100_priv *priv = libipw_priv(dev); char fw_ver[64], ucode_ver[64]; strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver)); ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver)); snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s", fw_ver, priv->eeprom_version, ucode_ver); strlcpy(info->bus_info, pci_name(priv->pci_dev), sizeof(info->bus_info)); } static u32 ipw2100_ethtool_get_link(struct net_device *dev) { struct ipw2100_priv *priv = libipw_priv(dev); return (priv->status & STATUS_ASSOCIATED) ? 1 : 0; } static const struct ethtool_ops ipw2100_ethtool_ops = { .get_link = ipw2100_ethtool_get_link, .get_drvinfo = ipw_ethtool_get_drvinfo, }; static void ipw2100_hang_check(struct work_struct *work) { struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv, hang_check.work); unsigned long flags; u32 rtc = 0xa5a5a5a5; u32 len = sizeof(rtc); int restart = 0; spin_lock_irqsave(&priv->low_lock, flags); if (priv->fatal_error != 0) { /* If fatal_error is set then we need to restart */ IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n", priv->net_dev->name); restart = 1; } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) || (rtc == priv->last_rtc)) { /* Check if firmware is hung */ IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n", priv->net_dev->name); restart = 1; } if (restart) { /* Kill timer */ priv->stop_hang_check = 1; priv->hangs++; /* Restart the NIC */ schedule_reset(priv); } priv->last_rtc = rtc; if (!priv->stop_hang_check) schedule_delayed_work(&priv->hang_check, HZ / 2); spin_unlock_irqrestore(&priv->low_lock, flags); } static void ipw2100_rf_kill(struct work_struct *work) { struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv, rf_kill.work); unsigned long flags; spin_lock_irqsave(&priv->low_lock, flags); if (rf_kill_active(priv)) { IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n"); if (!priv->stop_rf_kill) schedule_delayed_work(&priv->rf_kill, round_jiffies_relative(HZ)); goto exit_unlock; } /* RF Kill is now disabled, so bring the device back up */ if (!(priv->status & STATUS_RF_KILL_MASK)) { IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting " "device\n"); schedule_reset(priv); } else IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " "enabled\n"); exit_unlock: spin_unlock_irqrestore(&priv->low_lock, flags); } static void ipw2100_irq_tasklet(struct ipw2100_priv *priv); static const struct net_device_ops ipw2100_netdev_ops = { .ndo_open = ipw2100_open, .ndo_stop = ipw2100_close, .ndo_start_xmit = libipw_xmit, .ndo_change_mtu = libipw_change_mtu, .ndo_tx_timeout = ipw2100_tx_timeout, .ndo_set_mac_address = ipw2100_set_address, .ndo_validate_addr = eth_validate_addr, }; /* Look into using netdev destructor to shutdown libipw? */ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, void __iomem * ioaddr) { struct ipw2100_priv *priv; struct net_device *dev; dev = alloc_libipw(sizeof(struct ipw2100_priv), 0); if (!dev) return NULL; priv = libipw_priv(dev); priv->ieee = netdev_priv(dev); priv->pci_dev = pci_dev; priv->net_dev = dev; priv->ioaddr = ioaddr; priv->ieee->hard_start_xmit = ipw2100_tx; priv->ieee->set_security = shim__set_security; priv->ieee->perfect_rssi = -20; priv->ieee->worst_rssi = -85; netdev_attach_ops(dev, &ipw2100_netdev_ops); dev->ethtool_ops = &ipw2100_ethtool_ops; dev->wireless_handlers = &ipw2100_wx_handler_def; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) priv->wireless_data.libipw = priv->ieee; #else priv->wireless_data.ieee80211 = (struct ieee80211_device *) priv->ieee; #endif dev->wireless_data = &priv->wireless_data; dev->watchdog_timeo = 3 * HZ; dev->irq = 0; /* NOTE: We don't use the wireless_handlers hook * in dev as the system will start throwing WX requests * to us before we're actually initialized and it just * ends up causing problems. So, we just handle * the WX extensions through the ipw2100_ioctl interface */ /* memset() puts everything to 0, so we only have explicitly set * those values that need to be something else */ /* If power management is turned on, default to AUTO mode */ priv->power_mode = IPW_POWER_AUTO; #ifdef CONFIG_IPW2100_MONITOR priv->config |= CFG_CRC_CHECK; #endif priv->ieee->wpa_enabled = 0; priv->ieee->drop_unencrypted = 0; priv->ieee->privacy_invoked = 0; priv->ieee->ieee802_1x = 1; /* Set module parameters */ switch (network_mode) { case 1: priv->ieee->iw_mode = IW_MODE_ADHOC; break; #ifdef CONFIG_IPW2100_MONITOR case 2: priv->ieee->iw_mode = IW_MODE_MONITOR; break; #endif default: case 0: priv->ieee->iw_mode = IW_MODE_INFRA; break; } if (disable == 1) priv->status |= STATUS_RF_KILL_SW; if (channel != 0 && ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) { priv->config |= CFG_STATIC_CHANNEL; priv->channel = channel; } if (associate) priv->config |= CFG_ASSOCIATE; priv->beacon_interval = DEFAULT_BEACON_INTERVAL; priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT; priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT; priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED; priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED; priv->tx_power = IPW_TX_POWER_DEFAULT; priv->tx_rates = DEFAULT_TX_RATES; strcpy(priv->nick, "ipw2100"); spin_lock_init(&priv->low_lock); mutex_init(&priv->action_mutex); mutex_init(&priv->adapter_mutex); init_waitqueue_head(&priv->wait_command_queue); netif_carrier_off(dev); INIT_LIST_HEAD(&priv->msg_free_list); INIT_LIST_HEAD(&priv->msg_pend_list); INIT_STAT(&priv->msg_free_stat); INIT_STAT(&priv->msg_pend_stat); INIT_LIST_HEAD(&priv->tx_free_list); INIT_LIST_HEAD(&priv->tx_pend_list); INIT_STAT(&priv->tx_free_stat); INIT_STAT(&priv->tx_pend_stat); INIT_LIST_HEAD(&priv->fw_pend_list); INIT_STAT(&priv->fw_pend_stat); INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter); INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work); INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work); INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check); INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill); INIT_WORK(&priv->scan_event_now, ipw2100_scan_event_now); INIT_DELAYED_WORK(&priv->scan_event_later, ipw2100_scan_event_later); tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) ipw2100_irq_tasklet, (unsigned long)priv); /* NOTE: We do not start the deferred work for status checks yet */ priv->stop_rf_kill = 1; priv->stop_hang_check = 1; return dev; } static int ipw2100_pci_init_one(struct pci_dev *pci_dev, const struct pci_device_id *ent) { void __iomem *ioaddr; struct net_device *dev = NULL; struct ipw2100_priv *priv = NULL; int err = 0; int registered = 0; u32 val; IPW_DEBUG_INFO("enter\n"); if (!(pci_resource_flags(pci_dev, 0) & IORESOURCE_MEM)) { IPW_DEBUG_INFO("weird - resource type is not memory\n"); err = -ENODEV; goto out; } ioaddr = pci_iomap(pci_dev, 0, 0); if (!ioaddr) { printk(KERN_WARNING DRV_NAME "Error calling ioremap_nocache.\n"); err = -EIO; goto fail; } /* allocate and initialize our net_device */ dev = ipw2100_alloc_device(pci_dev, ioaddr); if (!dev) { printk(KERN_WARNING DRV_NAME "Error calling ipw2100_alloc_device.\n"); err = -ENOMEM; goto fail; } /* set up PCI mappings for device */ err = pci_enable_device(pci_dev); if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_enable_device.\n"); return err; } priv = libipw_priv(dev); pci_set_master(pci_dev); pci_set_drvdata(pci_dev, priv); err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_set_dma_mask.\n"); pci_disable_device(pci_dev); return err; } err = pci_request_regions(pci_dev, DRV_NAME); if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_request_regions.\n"); pci_disable_device(pci_dev); return err; } /* We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_read_config_dword(pci_dev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); pci_set_power_state(pci_dev, PCI_D0); if (!ipw2100_hw_is_adapter_in_system(dev)) { printk(KERN_WARNING DRV_NAME "Device not found via register read.\n"); err = -ENODEV; goto fail; } SET_NETDEV_DEV(dev, &pci_dev->dev); /* Force interrupts to be shut off on the device */ priv->status |= STATUS_INT_ENABLED; ipw2100_disable_interrupts(priv); /* Allocate and initialize the Tx/Rx queues and lists */ if (ipw2100_queues_allocate(priv)) { printk(KERN_WARNING DRV_NAME "Error calling ipw2100_queues_allocate.\n"); err = -ENOMEM; goto fail; } ipw2100_queues_initialize(priv); err = request_irq(pci_dev->irq, ipw2100_interrupt, IRQF_SHARED, dev->name, priv); if (err) { printk(KERN_WARNING DRV_NAME "Error calling request_irq: %d.\n", pci_dev->irq); goto fail; } dev->irq = pci_dev->irq; IPW_DEBUG_INFO("Attempting to register device...\n"); printk(KERN_INFO DRV_NAME ": Detected Intel PRO/Wireless 2100 Network Connection\n"); err = ipw2100_up(priv, 1); if (err) goto fail; err = ipw2100_wdev_init(dev); if (err) goto fail; registered = 1; /* Bring up the interface. Pre 0.46, after we registered the * network device we would call ipw2100_up. This introduced a race * condition with newer hotplug configurations (network was coming * up and making calls before the device was initialized). */ err = register_netdev(dev); if (err) { printk(KERN_WARNING DRV_NAME "Error calling register_netdev.\n"); goto fail; } registered = 2; mutex_lock(&priv->action_mutex); IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev)); /* perform this after register_netdev so that dev->name is set */ err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); if (err) goto fail_unlock; /* If the RF Kill switch is disabled, go ahead and complete the * startup sequence */ if (!(priv->status & STATUS_RF_KILL_MASK)) { /* Enable the adapter - sends HOST_COMPLETE */ if (ipw2100_enable_adapter(priv)) { printk(KERN_WARNING DRV_NAME ": %s: failed in call to enable adapter.\n", priv->net_dev->name); ipw2100_hw_stop_adapter(priv); err = -EIO; goto fail_unlock; } /* Start a scan . . . */ ipw2100_set_scan_options(priv); ipw2100_start_scan(priv); } IPW_DEBUG_INFO("exit\n"); priv->status |= STATUS_INITIALIZED; mutex_unlock(&priv->action_mutex); out: return err; fail_unlock: mutex_unlock(&priv->action_mutex); fail: if (dev) { if (registered >= 2) unregister_netdev(dev); if (registered) { wiphy_unregister(priv->ieee->wdev.wiphy); kfree(priv->ieee->bg_band.channels); } ipw2100_hw_stop_adapter(priv); ipw2100_disable_interrupts(priv); if (dev->irq) free_irq(dev->irq, priv); ipw2100_kill_works(priv); /* These are safe to call even if they weren't allocated */ ipw2100_queues_free(priv); sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); free_libipw(dev, 0); pci_set_drvdata(pci_dev, NULL); } pci_iounmap(pci_dev, ioaddr); pci_release_regions(pci_dev); pci_disable_device(pci_dev); goto out; } static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev) { struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); struct net_device *dev = priv->net_dev; mutex_lock(&priv->action_mutex); priv->status &= ~STATUS_INITIALIZED; sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); #ifdef CONFIG_PM if (ipw2100_firmware.version) ipw2100_release_firmware(priv, &ipw2100_firmware); #endif /* Take down the hardware */ ipw2100_down(priv); /* Release the mutex so that the network subsystem can * complete any needed calls into the driver... */ mutex_unlock(&priv->action_mutex); /* Unregister the device first - this results in close() * being called if the device is open. If we free storage * first, then close() will crash. * FIXME: remove the comment above. */ unregister_netdev(dev); ipw2100_kill_works(priv); ipw2100_queues_free(priv); /* Free potential debugging firmware snapshot */ ipw2100_snapshot_free(priv); free_irq(dev->irq, priv); pci_iounmap(pci_dev, priv->ioaddr); /* wiphy_unregister needs to be here, before free_libipw */ wiphy_unregister(priv->ieee->wdev.wiphy); kfree(priv->ieee->bg_band.channels); free_libipw(dev, 0); pci_release_regions(pci_dev); pci_disable_device(pci_dev); IPW_DEBUG_INFO("exit\n"); } #ifdef CONFIG_PM static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state) { struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); struct net_device *dev = priv->net_dev; IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name); mutex_lock(&priv->action_mutex); if (priv->status & STATUS_INITIALIZED) { /* Take down the device; powers it off, etc. */ ipw2100_down(priv); } /* Remove the PRESENT state of the device */ netif_device_detach(dev); pci_save_state(pci_dev); pci_disable_device(pci_dev); pci_set_power_state(pci_dev, PCI_D3hot); priv->suspend_at = get_seconds(); mutex_unlock(&priv->action_mutex); return 0; } static int ipw2100_resume(struct pci_dev *pci_dev) { struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); struct net_device *dev = priv->net_dev; int err; u32 val; if (IPW2100_PM_DISABLED) return 0; mutex_lock(&priv->action_mutex); IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name); pci_set_power_state(pci_dev, PCI_D0); err = pci_enable_device(pci_dev); if (err) { printk(KERN_ERR "%s: pci_enable_device failed on resume\n", dev->name); mutex_unlock(&priv->action_mutex); return err; } pci_restore_state(pci_dev); /* * Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries * from interfering with C3 CPU state. pci_restore_state won't help * here since it only restores the first 64 bytes pci config header. */ pci_read_config_dword(pci_dev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); /* Set the device back into the PRESENT state; this will also wake * the queue of needed */ netif_device_attach(dev); priv->suspend_time = get_seconds() - priv->suspend_at; /* Bring the device back up */ if (!(priv->status & STATUS_RF_KILL_SW)) ipw2100_up(priv, 0); mutex_unlock(&priv->action_mutex); return 0; } #endif static void ipw2100_shutdown(struct pci_dev *pci_dev) { struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); /* Take down the device; powers it off, etc. */ ipw2100_down(priv); pci_disable_device(pci_dev); } #define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x } static DEFINE_PCI_DEVICE_TABLE(ipw2100_pci_id_table) = { IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */ IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */ IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */ IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */ IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */ IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */ IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */ IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */ IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */ IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */ IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */ IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */ IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */ IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */ IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */ IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */ IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */ IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */ IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */ IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */ IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */ IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */ IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */ IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */ IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */ IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */ IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */ IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */ IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */ IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */ IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */ IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */ IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */ IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */ IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */ IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */ IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */ IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */ IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */ IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */ {0,}, }; MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table); static struct pci_driver ipw2100_pci_driver = { .name = DRV_NAME, .id_table = ipw2100_pci_id_table, .probe = ipw2100_pci_init_one, .remove = __devexit_p(ipw2100_pci_remove_one), #ifdef CONFIG_PM .suspend = ipw2100_suspend, .resume = ipw2100_resume, #endif .shutdown = ipw2100_shutdown, }; /** * Initialize the ipw2100 driver/module * * @returns 0 if ok, < 0 errno node con error. * * Note: we cannot init the /proc stuff until the PCI driver is there, * or we risk an unlikely race condition on someone accessing * uninitialized data in the PCI dev struct through /proc. */ static int __init ipw2100_init(void) { int ret; printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION); printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT); pm_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); ret = pci_register_driver(&ipw2100_pci_driver); if (ret) goto out; #ifdef CONFIG_IPW2100_DEBUG ipw2100_debug_level = debug; ret = driver_create_file(&ipw2100_pci_driver.driver, &driver_attr_debug_level); #endif out: return ret; } /** * Cleanup ipw2100 driver registration */ static void __exit ipw2100_exit(void) { /* FIXME: IPG: check that we have no instances of the devices open */ #ifdef CONFIG_IPW2100_DEBUG driver_remove_file(&ipw2100_pci_driver.driver, &driver_attr_debug_level); #endif pci_unregister_driver(&ipw2100_pci_driver); pm_qos_remove_request(&ipw2100_pm_qos_req); } module_init(ipw2100_init); module_exit(ipw2100_exit); static int ipw2100_wx_get_name(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); if (!(priv->status & STATUS_ASSOCIATED)) strcpy(wrqu->name, "unassociated"); else snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b"); IPW_DEBUG_WX("Name: %s\n", wrqu->name); return 0; } static int ipw2100_wx_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); struct iw_freq *fwrq = &wrqu->freq; int err = 0; if (priv->ieee->iw_mode == IW_MODE_INFRA) return -EOPNOTSUPP; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } /* if setting by freq convert to channel */ if (fwrq->e == 1) { if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) { int f = fwrq->m / 100000; int c = 0; while ((c < REG_MAX_CHANNEL) && (f != ipw2100_frequencies[c])) c++; /* hack to fall through */ fwrq->e = 0; fwrq->m = c + 1; } } if (fwrq->e > 0 || fwrq->m > 1000) { err = -EOPNOTSUPP; goto done; } else { /* Set the channel */ IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m); err = ipw2100_set_channel(priv, fwrq->m, 0); } done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); wrqu->freq.e = 0; /* If we are associated, trying to associate, or have a statically * configured CHANNEL then return that; otherwise return ANY */ if (priv->config & CFG_STATIC_CHANNEL || priv->status & STATUS_ASSOCIATED) wrqu->freq.m = priv->channel; else wrqu->freq.m = 0; IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel); return 0; } static int ipw2100_wx_set_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; IPW_DEBUG_WX("SET Mode -> %d\n", wrqu->mode); if (wrqu->mode == priv->ieee->iw_mode) return 0; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } switch (wrqu->mode) { #ifdef CONFIG_IPW2100_MONITOR case IW_MODE_MONITOR: err = ipw2100_switch_mode(priv, IW_MODE_MONITOR); break; #endif /* CONFIG_IPW2100_MONITOR */ case IW_MODE_ADHOC: err = ipw2100_switch_mode(priv, IW_MODE_ADHOC); break; case IW_MODE_INFRA: case IW_MODE_AUTO: default: err = ipw2100_switch_mode(priv, IW_MODE_INFRA); break; } done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); wrqu->mode = priv->ieee->iw_mode; IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode); return 0; } #define POWER_MODES 5 /* Values are in microsecond */ static const s32 timeout_duration[POWER_MODES] = { 350000, 250000, 75000, 37000, 25000, }; static const s32 period_duration[POWER_MODES] = { 400000, 700000, 1000000, 1000000, 1000000 }; static int ipw2100_wx_get_range(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); struct iw_range *range = (struct iw_range *)extra; u16 val; int i, level; wrqu->data.length = sizeof(*range); memset(range, 0, sizeof(*range)); /* Let's try to keep this struct in the same order as in * linux/include/wireless.h */ /* TODO: See what values we can set, and remove the ones we can't * set, or fill them with some default data. */ /* ~5 Mb/s real (802.11b) */ range->throughput = 5 * 1000 * 1000; // range->sensitivity; /* signal level threshold range */ range->max_qual.qual = 100; /* TODO: Find real max RSSI and stick here */ range->max_qual.level = 0; range->max_qual.noise = 0; range->max_qual.updated = 7; /* Updated all three */ range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */ /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM; range->avg_qual.noise = 0; range->avg_qual.updated = 7; /* Updated all three */ range->num_bitrates = RATE_COUNT; for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) { range->bitrate[i] = ipw2100_bg_rates[i].bitrate * 100 * 1000; } range->min_rts = MIN_RTS_THRESHOLD; range->max_rts = MAX_RTS_THRESHOLD; range->min_frag = MIN_FRAG_THRESHOLD; range->max_frag = MAX_FRAG_THRESHOLD; range->min_pmp = period_duration[0]; /* Minimal PM period */ range->max_pmp = period_duration[POWER_MODES - 1]; /* Maximal PM period */ range->min_pmt = timeout_duration[POWER_MODES - 1]; /* Minimal PM timeout */ range->max_pmt = timeout_duration[0]; /* Maximal PM timeout */ /* How to decode max/min PM period */ range->pmp_flags = IW_POWER_PERIOD; /* How to decode max/min PM period */ range->pmt_flags = IW_POWER_TIMEOUT; /* What PM options are supported */ range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD; range->encoding_size[0] = 5; range->encoding_size[1] = 13; /* Different token sizes */ range->num_encoding_sizes = 2; /* Number of entry in the list */ range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */ // range->encoding_login_index; /* token index for login token */ if (priv->ieee->iw_mode == IW_MODE_ADHOC) { range->txpower_capa = IW_TXPOW_DBM; range->num_txpower = IW_MAX_TXPOWER; for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16); i < IW_MAX_TXPOWER; i++, level -= ((IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1)) range->txpower[i] = level / 16; } else { range->txpower_capa = 0; range->num_txpower = 0; } /* Set the Wireless Extension versions */ range->we_version_compiled = WIRELESS_EXT; range->we_version_source = 18; // range->retry_capa; /* What retry options are supported */ // range->retry_flags; /* How to decode max/min retry limit */ // range->r_time_flags; /* How to decode max/min retry life */ // range->min_retry; /* Minimal number of retries */ // range->max_retry; /* Maximal number of retries */ // range->min_r_time; /* Minimal retry lifetime */ // range->max_r_time; /* Maximal retry lifetime */ range->num_channels = FREQ_COUNT; val = 0; for (i = 0; i < FREQ_COUNT; i++) { // TODO: Include only legal frequencies for some countries // if (local->channel_mask & (1 << i)) { range->freq[val].i = i + 1; range->freq[val].m = ipw2100_frequencies[i] * 100000; range->freq[val].e = 1; val++; // } if (val == IW_MAX_FREQUENCIES) break; } range->num_frequency = val; /* Event capability (kernel + driver) */ range->event_capa[0] = (IW_EVENT_CAPA_K_0 | IW_EVENT_CAPA_MASK(SIOCGIWAP)); range->event_capa[1] = IW_EVENT_CAPA_K_1; range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; IPW_DEBUG_WX("GET Range\n"); return 0; } static int ipw2100_wx_set_wap(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; // sanity checks if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) return -EINVAL; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) || is_zero_ether_addr(wrqu->ap_addr.sa_data)) { /* we disable mandatory BSSID association */ IPW_DEBUG_WX("exit - disable mandatory BSSID\n"); priv->config &= ~CFG_STATIC_BSSID; err = ipw2100_set_mandatory_bssid(priv, NULL, 0); goto done; } priv->config |= CFG_STATIC_BSSID; memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN); err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0); IPW_DEBUG_WX("SET BSSID -> %pM\n", wrqu->ap_addr.sa_data); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_wap(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); /* If we are associated, trying to associate, or have a statically * configured BSSID then return that; otherwise return ANY */ if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) { wrqu->ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN); } else memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data); return 0; } static int ipw2100_wx_set_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); char *essid = ""; /* ANY */ int length = 0; int err = 0; DECLARE_SSID_BUF(ssid); mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (wrqu->essid.flags && wrqu->essid.length) { length = wrqu->essid.length; essid = extra; } if (length == 0) { IPW_DEBUG_WX("Setting ESSID to ANY\n"); priv->config &= ~CFG_STATIC_ESSID; err = ipw2100_set_essid(priv, NULL, 0, 0); goto done; } length = min(length, IW_ESSID_MAX_SIZE); priv->config |= CFG_STATIC_ESSID; if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) { IPW_DEBUG_WX("ESSID set to current ESSID.\n"); err = 0; goto done; } IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", print_ssid(ssid, essid, length), length); priv->essid_len = length; memcpy(priv->essid, essid, priv->essid_len); err = ipw2100_set_essid(priv, essid, length, 0); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); DECLARE_SSID_BUF(ssid); /* If we are associated, trying to associate, or have a statically * configured ESSID then return that; otherwise return ANY */ if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) { IPW_DEBUG_WX("Getting essid: '%s'\n", print_ssid(ssid, priv->essid, priv->essid_len)); memcpy(extra, priv->essid, priv->essid_len); wrqu->essid.length = priv->essid_len; wrqu->essid.flags = 1; /* active */ } else { IPW_DEBUG_WX("Getting essid: ANY\n"); wrqu->essid.length = 0; wrqu->essid.flags = 0; /* active */ } return 0; } static int ipw2100_wx_set_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); if (wrqu->data.length > IW_ESSID_MAX_SIZE) return -E2BIG; wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick)); memset(priv->nick, 0, sizeof(priv->nick)); memcpy(priv->nick, extra, wrqu->data.length); IPW_DEBUG_WX("SET Nickname -> %s\n", priv->nick); return 0; } static int ipw2100_wx_get_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); wrqu->data.length = strlen(priv->nick); memcpy(extra, priv->nick, wrqu->data.length); wrqu->data.flags = 1; /* active */ IPW_DEBUG_WX("GET Nickname -> %s\n", extra); return 0; } static int ipw2100_wx_set_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); u32 target_rate = wrqu->bitrate.value; u32 rate; int err = 0; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } rate = 0; if (target_rate == 1000000 || (!wrqu->bitrate.fixed && target_rate > 1000000)) rate |= TX_RATE_1_MBIT; if (target_rate == 2000000 || (!wrqu->bitrate.fixed && target_rate > 2000000)) rate |= TX_RATE_2_MBIT; if (target_rate == 5500000 || (!wrqu->bitrate.fixed && target_rate > 5500000)) rate |= TX_RATE_5_5_MBIT; if (target_rate == 11000000 || (!wrqu->bitrate.fixed && target_rate > 11000000)) rate |= TX_RATE_11_MBIT; if (rate == 0) rate = DEFAULT_TX_RATES; err = ipw2100_set_tx_rates(priv, rate, 0); IPW_DEBUG_WX("SET Rate -> %04X\n", rate); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int val; unsigned int len = sizeof(val); int err = 0; if (!(priv->status & STATUS_ENABLED) || priv->status & STATUS_RF_KILL_MASK || !(priv->status & STATUS_ASSOCIATED)) { wrqu->bitrate.value = 0; return 0; } mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len); if (err) { IPW_DEBUG_WX("failed querying ordinals.\n"); goto done; } switch (val & TX_RATE_MASK) { case TX_RATE_1_MBIT: wrqu->bitrate.value = 1000000; break; case TX_RATE_2_MBIT: wrqu->bitrate.value = 2000000; break; case TX_RATE_5_5_MBIT: wrqu->bitrate.value = 5500000; break; case TX_RATE_11_MBIT: wrqu->bitrate.value = 11000000; break; default: wrqu->bitrate.value = 0; } IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_set_rts(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int value, err; /* Auto RTS not yet supported */ if (wrqu->rts.fixed == 0) return -EINVAL; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (wrqu->rts.disabled) value = priv->rts_threshold | RTS_DISABLED; else { if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) { err = -EINVAL; goto done; } value = wrqu->rts.value; } err = ipw2100_set_rts_threshold(priv, value); IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X\n", value); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_rts(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED; wrqu->rts.fixed = 1; /* no auto select */ /* If RTS is set to the default value, then it is disabled */ wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0; IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X\n", wrqu->rts.value); return 0; } static int ipw2100_wx_set_txpow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err = 0, value; if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled)) return -EINPROGRESS; if (priv->ieee->iw_mode != IW_MODE_ADHOC) return 0; if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL; if (wrqu->txpower.fixed == 0) value = IPW_TX_POWER_DEFAULT; else { if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM || wrqu->txpower.value > IPW_TX_POWER_MAX_DBM) return -EINVAL; value = wrqu->txpower.value; } mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } err = ipw2100_set_tx_power(priv, value); IPW_DEBUG_WX("SET TX Power -> %d\n", value); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_txpow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0; if (priv->tx_power == IPW_TX_POWER_DEFAULT) { wrqu->txpower.fixed = 0; wrqu->txpower.value = IPW_TX_POWER_MAX_DBM; } else { wrqu->txpower.fixed = 1; wrqu->txpower.value = priv->tx_power; } wrqu->txpower.flags = IW_TXPOW_DBM; IPW_DEBUG_WX("GET TX Power -> %d\n", wrqu->txpower.value); return 0; } static int ipw2100_wx_set_frag(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); if (!wrqu->frag.fixed) return -EINVAL; if (wrqu->frag.disabled) { priv->frag_threshold |= FRAG_DISABLED; priv->ieee->fts = DEFAULT_FTS; } else { if (wrqu->frag.value < MIN_FRAG_THRESHOLD || wrqu->frag.value > MAX_FRAG_THRESHOLD) return -EINVAL; priv->ieee->fts = wrqu->frag.value & ~0x1; priv->frag_threshold = priv->ieee->fts; } IPW_DEBUG_WX("SET Frag Threshold -> %d\n", priv->ieee->fts); return 0; } static int ipw2100_wx_get_frag(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED; wrqu->frag.fixed = 0; /* no auto select */ wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0; IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value); return 0; } static int ipw2100_wx_set_retry(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) return -EINVAL; if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) return 0; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (wrqu->retry.flags & IW_RETRY_SHORT) { err = ipw2100_set_short_retry(priv, wrqu->retry.value); IPW_DEBUG_WX("SET Short Retry Limit -> %d\n", wrqu->retry.value); goto done; } if (wrqu->retry.flags & IW_RETRY_LONG) { err = ipw2100_set_long_retry(priv, wrqu->retry.value); IPW_DEBUG_WX("SET Long Retry Limit -> %d\n", wrqu->retry.value); goto done; } err = ipw2100_set_short_retry(priv, wrqu->retry.value); if (!err) err = ipw2100_set_long_retry(priv, wrqu->retry.value); IPW_DEBUG_WX("SET Both Retry Limits -> %d\n", wrqu->retry.value); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_retry(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); wrqu->retry.disabled = 0; /* can't be disabled */ if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) return -EINVAL; if (wrqu->retry.flags & IW_RETRY_LONG) { wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; wrqu->retry.value = priv->long_retry_limit; } else { wrqu->retry.flags = (priv->short_retry_limit != priv->long_retry_limit) ? IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT; wrqu->retry.value = priv->short_retry_limit; } IPW_DEBUG_WX("GET Retry -> %d\n", wrqu->retry.value); return 0; } static int ipw2100_wx_set_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } IPW_DEBUG_WX("Initiating scan...\n"); priv->user_requested_scan = 1; if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) { IPW_DEBUG_WX("Start scan failed.\n"); /* TODO: Mark a scan as pending so when hardware initialized * a scan starts */ } done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); } /* * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c */ static int ipw2100_wx_set_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *key) { /* * No check of STATUS_INITIALIZED required */ struct ipw2100_priv *priv = libipw_priv(dev); return libipw_wx_set_encode(priv->ieee, info, wrqu, key); } static int ipw2100_wx_get_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *key) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); return libipw_wx_get_encode(priv->ieee, info, wrqu, key); } static int ipw2100_wx_set_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (wrqu->power.disabled) { priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM); IPW_DEBUG_WX("SET Power Management Mode -> off\n"); goto done; } switch (wrqu->power.flags & IW_POWER_MODE) { case IW_POWER_ON: /* If not specified */ case IW_POWER_MODE: /* If set all mask */ case IW_POWER_ALL_R: /* If explicitly state all */ break; default: /* Otherwise we don't support it */ IPW_DEBUG_WX("SET PM Mode: %X not supported.\n", wrqu->power.flags); err = -EOPNOTSUPP; goto done; } /* If the user hasn't specified a power management mode yet, default * to BATTERY */ priv->power_mode = IPW_POWER_ENABLED | priv->power_mode; err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode)); IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); if (!(priv->power_mode & IPW_POWER_ENABLED)) wrqu->power.disabled = 1; else { wrqu->power.disabled = 0; wrqu->power.flags = 0; } IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode); return 0; } /* * WE-18 WPA support */ /* SIOCSIWGENIE */ static int ipw2100_wx_set_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; u8 *buf; if (!ieee->wpa_enabled) return -EOPNOTSUPP; if (wrqu->data.length > MAX_WPA_IE_LEN || (wrqu->data.length && extra == NULL)) return -EINVAL; if (wrqu->data.length) { buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); if (buf == NULL) return -ENOMEM; kfree(ieee->wpa_ie); ieee->wpa_ie = buf; ieee->wpa_ie_len = wrqu->data.length; } else { kfree(ieee->wpa_ie); ieee->wpa_ie = NULL; ieee->wpa_ie_len = 0; } ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); return 0; } /* SIOCGIWGENIE */ static int ipw2100_wx_get_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { wrqu->data.length = 0; return 0; } if (wrqu->data.length < ieee->wpa_ie_len) return -E2BIG; wrqu->data.length = ieee->wpa_ie_len; memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); return 0; } /* SIOCSIWAUTH */ static int ipw2100_wx_set_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; struct iw_param *param = &wrqu->param; struct lib80211_crypt_data *crypt; unsigned long flags; int ret = 0; switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: case IW_AUTH_CIPHER_PAIRWISE: case IW_AUTH_CIPHER_GROUP: case IW_AUTH_KEY_MGMT: /* * ipw2200 does not use these parameters */ break; case IW_AUTH_TKIP_COUNTERMEASURES: crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) break; flags = crypt->ops->get_flags(crypt->priv); if (param->value) flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; else flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; crypt->ops->set_flags(flags, crypt->priv); break; case IW_AUTH_DROP_UNENCRYPTED:{ /* HACK: * * wpa_supplicant calls set_wpa_enabled when the driver * is loaded and unloaded, regardless of if WPA is being * used. No other calls are made which can be used to * determine if encryption will be used or not prior to * association being expected. If encryption is not being * used, drop_unencrypted is set to false, else true -- we * can use this to determine if the CAP_PRIVACY_ON bit should * be set. */ struct libipw_security sec = { .flags = SEC_ENABLED, .enabled = param->value, }; priv->ieee->drop_unencrypted = param->value; /* We only change SEC_LEVEL for open mode. Others * are set by ipw_wpa_set_encryption. */ if (!param->value) { sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_0; } else { sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_1; } if (priv->ieee->set_security) priv->ieee->set_security(priv->ieee->dev, &sec); break; } case IW_AUTH_80211_AUTH_ALG: ret = ipw2100_wpa_set_auth_algs(priv, param->value); break; case IW_AUTH_WPA_ENABLED: ret = ipw2100_wpa_enable(priv, param->value); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: ieee->ieee802_1x = param->value; break; //case IW_AUTH_ROAMING_CONTROL: case IW_AUTH_PRIVACY_INVOKED: ieee->privacy_invoked = param->value; break; default: return -EOPNOTSUPP; } return ret; } /* SIOCGIWAUTH */ static int ipw2100_wx_get_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; struct lib80211_crypt_data *crypt; struct iw_param *param = &wrqu->param; int ret = 0; switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: case IW_AUTH_CIPHER_PAIRWISE: case IW_AUTH_CIPHER_GROUP: case IW_AUTH_KEY_MGMT: /* * wpa_supplicant will control these internally */ ret = -EOPNOTSUPP; break; case IW_AUTH_TKIP_COUNTERMEASURES: crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; if (!crypt || !crypt->ops->get_flags) { IPW_DEBUG_WARNING("Can't get TKIP countermeasures: " "crypt not set!\n"); break; } param->value = (crypt->ops->get_flags(crypt->priv) & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0; break; case IW_AUTH_DROP_UNENCRYPTED: param->value = ieee->drop_unencrypted; break; case IW_AUTH_80211_AUTH_ALG: param->value = priv->ieee->sec.auth_mode; break; case IW_AUTH_WPA_ENABLED: param->value = ieee->wpa_enabled; break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: param->value = ieee->ieee802_1x; break; case IW_AUTH_ROAMING_CONTROL: case IW_AUTH_PRIVACY_INVOKED: param->value = ieee->privacy_invoked; break; default: return -EOPNOTSUPP; } return 0; } /* SIOCSIWENCODEEXT */ static int ipw2100_wx_set_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCGIWENCODEEXT */ static int ipw2100_wx_get_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCSIWMLME */ static int ipw2100_wx_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); struct iw_mlme *mlme = (struct iw_mlme *)extra; __le16 reason; reason = cpu_to_le16(mlme->reason_code); switch (mlme->cmd) { case IW_MLME_DEAUTH: // silently ignore break; case IW_MLME_DISASSOC: ipw2100_disassociate_bssid(priv); break; default: return -EOPNOTSUPP; } return 0; } /* * * IWPRIV handlers * */ #ifdef CONFIG_IPW2100_MONITOR static int ipw2100_wx_set_promisc(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int *parms = (int *)extra; int enable = (parms[0] > 0); int err = 0; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (enable) { if (priv->ieee->iw_mode == IW_MODE_MONITOR) { err = ipw2100_set_channel(priv, parms[1], 0); goto done; } priv->channel = parms[1]; err = ipw2100_switch_mode(priv, IW_MODE_MONITOR); } else { if (priv->ieee->iw_mode == IW_MODE_MONITOR) err = ipw2100_switch_mode(priv, priv->last_mode); } done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_reset(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); if (priv->status & STATUS_INITIALIZED) schedule_reset(priv); return 0; } #endif static int ipw2100_wx_set_powermode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err = 0, mode = *(int *)extra; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if ((mode < 0) || (mode > POWER_MODES)) mode = IPW_POWER_AUTO; if (IPW_POWER_LEVEL(priv->power_mode) != mode) err = ipw2100_set_power_mode(priv, mode); done: mutex_unlock(&priv->action_mutex); return err; } #define MAX_POWER_STRING 80 static int ipw2100_wx_get_powermode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); int level = IPW_POWER_LEVEL(priv->power_mode); s32 timeout, period; if (!(priv->power_mode & IPW_POWER_ENABLED)) { snprintf(extra, MAX_POWER_STRING, "Power save level: %d (Off)", level); } else { switch (level) { case IPW_POWER_MODE_CAM: snprintf(extra, MAX_POWER_STRING, "Power save level: %d (None)", level); break; case IPW_POWER_AUTO: snprintf(extra, MAX_POWER_STRING, "Power save level: %d (Auto)", level); break; default: timeout = timeout_duration[level - 1] / 1000; period = period_duration[level - 1] / 1000; snprintf(extra, MAX_POWER_STRING, "Power save level: %d " "(Timeout %dms, Period %dms)", level, timeout, period); } } wrqu->data.length = strlen(extra) + 1; return 0; } static int ipw2100_wx_set_preamble(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err, mode = *(int *)extra; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (mode == 1) priv->config |= CFG_LONG_PREAMBLE; else if (mode == 0) priv->config &= ~CFG_LONG_PREAMBLE; else { err = -EINVAL; goto done; } err = ipw2100_system_config(priv, 0); done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_preamble(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); if (priv->config & CFG_LONG_PREAMBLE) snprintf(wrqu->name, IFNAMSIZ, "long (1)"); else snprintf(wrqu->name, IFNAMSIZ, "auto (0)"); return 0; } #ifdef CONFIG_IPW2100_MONITOR static int ipw2100_wx_set_crc_check(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw2100_priv *priv = libipw_priv(dev); int err, mode = *(int *)extra; mutex_lock(&priv->action_mutex); if (!(priv->status & STATUS_INITIALIZED)) { err = -EIO; goto done; } if (mode == 1) priv->config |= CFG_CRC_CHECK; else if (mode == 0) priv->config &= ~CFG_CRC_CHECK; else { err = -EINVAL; goto done; } err = 0; done: mutex_unlock(&priv->action_mutex); return err; } static int ipw2100_wx_get_crc_check(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * This can be called at any time. No action lock required */ struct ipw2100_priv *priv = libipw_priv(dev); if (priv->config & CFG_CRC_CHECK) snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)"); else snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)"); return 0; } #endif /* CONFIG_IPW2100_MONITOR */ static iw_handler ipw2100_wx_handlers[] = { IW_HANDLER(SIOCGIWNAME, ipw2100_wx_get_name), IW_HANDLER(SIOCSIWFREQ, ipw2100_wx_set_freq), IW_HANDLER(SIOCGIWFREQ, ipw2100_wx_get_freq), IW_HANDLER(SIOCSIWMODE, ipw2100_wx_set_mode), IW_HANDLER(SIOCGIWMODE, ipw2100_wx_get_mode), IW_HANDLER(SIOCGIWRANGE, ipw2100_wx_get_range), IW_HANDLER(SIOCSIWAP, ipw2100_wx_set_wap), IW_HANDLER(SIOCGIWAP, ipw2100_wx_get_wap), IW_HANDLER(SIOCSIWMLME, ipw2100_wx_set_mlme), IW_HANDLER(SIOCSIWSCAN, ipw2100_wx_set_scan), IW_HANDLER(SIOCGIWSCAN, ipw2100_wx_get_scan), IW_HANDLER(SIOCSIWESSID, ipw2100_wx_set_essid), IW_HANDLER(SIOCGIWESSID, ipw2100_wx_get_essid), IW_HANDLER(SIOCSIWNICKN, ipw2100_wx_set_nick), IW_HANDLER(SIOCGIWNICKN, ipw2100_wx_get_nick), IW_HANDLER(SIOCSIWRATE, ipw2100_wx_set_rate), IW_HANDLER(SIOCGIWRATE, ipw2100_wx_get_rate), IW_HANDLER(SIOCSIWRTS, ipw2100_wx_set_rts), IW_HANDLER(SIOCGIWRTS, ipw2100_wx_get_rts), IW_HANDLER(SIOCSIWFRAG, ipw2100_wx_set_frag), IW_HANDLER(SIOCGIWFRAG, ipw2100_wx_get_frag), IW_HANDLER(SIOCSIWTXPOW, ipw2100_wx_set_txpow), IW_HANDLER(SIOCGIWTXPOW, ipw2100_wx_get_txpow), IW_HANDLER(SIOCSIWRETRY, ipw2100_wx_set_retry), IW_HANDLER(SIOCGIWRETRY, ipw2100_wx_get_retry), IW_HANDLER(SIOCSIWENCODE, ipw2100_wx_set_encode), IW_HANDLER(SIOCGIWENCODE, ipw2100_wx_get_encode), IW_HANDLER(SIOCSIWPOWER, ipw2100_wx_set_power), IW_HANDLER(SIOCGIWPOWER, ipw2100_wx_get_power), IW_HANDLER(SIOCSIWGENIE, ipw2100_wx_set_genie), IW_HANDLER(SIOCGIWGENIE, ipw2100_wx_get_genie), IW_HANDLER(SIOCSIWAUTH, ipw2100_wx_set_auth), IW_HANDLER(SIOCGIWAUTH, ipw2100_wx_get_auth), IW_HANDLER(SIOCSIWENCODEEXT, ipw2100_wx_set_encodeext), IW_HANDLER(SIOCGIWENCODEEXT, ipw2100_wx_get_encodeext), }; #define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV #define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1 #define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2 #define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3 #define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4 #define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5 #define IPW2100_PRIV_SET_CRC_CHECK SIOCIWFIRSTPRIV+6 #define IPW2100_PRIV_GET_CRC_CHECK SIOCIWFIRSTPRIV+7 static const struct iw_priv_args ipw2100_private_args[] = { #ifdef CONFIG_IPW2100_MONITOR { IPW2100_PRIV_SET_MONITOR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"}, { IPW2100_PRIV_RESET, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"}, #endif /* CONFIG_IPW2100_MONITOR */ { IPW2100_PRIV_SET_POWER, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"}, { IPW2100_PRIV_GET_POWER, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING, "get_power"}, { IPW2100_PRIV_SET_LONGPREAMBLE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"}, { IPW2100_PRIV_GET_LONGPREAMBLE, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"}, #ifdef CONFIG_IPW2100_MONITOR { IPW2100_PRIV_SET_CRC_CHECK, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"}, { IPW2100_PRIV_GET_CRC_CHECK, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"}, #endif /* CONFIG_IPW2100_MONITOR */ }; static iw_handler ipw2100_private_handler[] = { #ifdef CONFIG_IPW2100_MONITOR ipw2100_wx_set_promisc, ipw2100_wx_reset, #else /* CONFIG_IPW2100_MONITOR */ NULL, NULL, #endif /* CONFIG_IPW2100_MONITOR */ ipw2100_wx_set_powermode, ipw2100_wx_get_powermode, ipw2100_wx_set_preamble, ipw2100_wx_get_preamble, #ifdef CONFIG_IPW2100_MONITOR ipw2100_wx_set_crc_check, ipw2100_wx_get_crc_check, #else /* CONFIG_IPW2100_MONITOR */ NULL, NULL, #endif /* CONFIG_IPW2100_MONITOR */ }; /* * Get wireless statistics. * Called by /proc/net/wireless * Also called by SIOCGIWSTATS */ static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev) { enum { POOR = 30, FAIR = 60, GOOD = 80, VERY_GOOD = 90, EXCELLENT = 95, PERFECT = 100 }; int rssi_qual; int tx_qual; int beacon_qual; int quality; struct ipw2100_priv *priv = libipw_priv(dev); struct iw_statistics *wstats; u32 rssi, tx_retries, missed_beacons, tx_failures; u32 ord_len = sizeof(u32); if (!priv) return (struct iw_statistics *)NULL; wstats = &priv->wstats; /* if hw is disabled, then ipw2100_get_ordinal() can't be called. * ipw2100_wx_wireless_stats seems to be called before fw is * initialized. STATUS_ASSOCIATED will only be set if the hw is up * and associated; if not associcated, the values are all meaningless * anyway, so set them all to NULL and INVALID */ if (!(priv->status & STATUS_ASSOCIATED)) { wstats->miss.beacon = 0; wstats->discard.retries = 0; wstats->qual.qual = 0; wstats->qual.level = 0; wstats->qual.noise = 0; wstats->qual.updated = 7; wstats->qual.updated |= IW_QUAL_NOISE_INVALID | IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; return wstats; } if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS, &missed_beacons, &ord_len)) goto fail_get_ordinal; /* If we don't have a connection the quality and level is 0 */ if (!(priv->status & STATUS_ASSOCIATED)) { wstats->qual.qual = 0; wstats->qual.level = 0; } else { if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR, &rssi, &ord_len)) goto fail_get_ordinal; wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM; if (rssi < 10) rssi_qual = rssi * POOR / 10; else if (rssi < 15) rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR; else if (rssi < 20) rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR; else if (rssi < 30) rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) / 10 + GOOD; else rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) / 10 + VERY_GOOD; if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES, &tx_retries, &ord_len)) goto fail_get_ordinal; if (tx_retries > 75) tx_qual = (90 - tx_retries) * POOR / 15; else if (tx_retries > 70) tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; else if (tx_retries > 65) tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; else if (tx_retries > 50) tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / 15 + GOOD; else tx_qual = (50 - tx_retries) * (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; if (missed_beacons > 50) beacon_qual = (60 - missed_beacons) * POOR / 10; else if (missed_beacons > 40) beacon_qual = (50 - missed_beacons) * (FAIR - POOR) / 10 + POOR; else if (missed_beacons > 32) beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) / 18 + FAIR; else if (missed_beacons > 20) beacon_qual = (32 - missed_beacons) * (VERY_GOOD - GOOD) / 20 + GOOD; else beacon_qual = (20 - missed_beacons) * (PERFECT - VERY_GOOD) / 20 + VERY_GOOD; quality = min(tx_qual, rssi_qual); quality = min(beacon_qual, quality); #ifdef CONFIG_IPW2100_DEBUG if (beacon_qual == quality) IPW_DEBUG_WX("Quality clamped by Missed Beacons\n"); else if (tx_qual == quality) IPW_DEBUG_WX("Quality clamped by Tx Retries\n"); else if (quality != 100) IPW_DEBUG_WX("Quality clamped by Signal Strength\n"); else IPW_DEBUG_WX("Quality not clamped.\n"); #endif wstats->qual.qual = quality; wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM; } wstats->qual.noise = 0; wstats->qual.updated = 7; wstats->qual.updated |= IW_QUAL_NOISE_INVALID; /* FIXME: this is percent and not a # */ wstats->miss.beacon = missed_beacons; if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES, &tx_failures, &ord_len)) goto fail_get_ordinal; wstats->discard.retries = tx_failures; return wstats; fail_get_ordinal: IPW_DEBUG_WX("failed querying ordinals.\n"); return (struct iw_statistics *)NULL; } static struct iw_handler_def ipw2100_wx_handler_def = { .standard = ipw2100_wx_handlers, .num_standard = ARRAY_SIZE(ipw2100_wx_handlers), .num_private = ARRAY_SIZE(ipw2100_private_handler), .num_private_args = ARRAY_SIZE(ipw2100_private_args), .private = (iw_handler *) ipw2100_private_handler, .private_args = (struct iw_priv_args *)ipw2100_private_args, .get_wireless_stats = ipw2100_wx_wireless_stats, }; static void ipw2100_wx_event_work(struct work_struct *work) { struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv, wx_event_work.work); union iwreq_data wrqu; unsigned int len = ETH_ALEN; if (priv->status & STATUS_STOPPING) return; mutex_lock(&priv->action_mutex); IPW_DEBUG_WX("enter\n"); mutex_unlock(&priv->action_mutex); wrqu.ap_addr.sa_family = ARPHRD_ETHER; /* Fetch BSSID from the hardware */ if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) || priv->status & STATUS_RF_KILL_MASK || ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, &priv->bssid, &len)) { memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); } else { /* We now have the BSSID, so can finish setting to the full * associated state */ memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN); memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN); priv->status &= ~STATUS_ASSOCIATING; priv->status |= STATUS_ASSOCIATED; netif_carrier_on(priv->net_dev); netif_wake_queue(priv->net_dev); } if (!(priv->status & STATUS_ASSOCIATED)) { IPW_DEBUG_WX("Configuring ESSID\n"); mutex_lock(&priv->action_mutex); /* This is a disassociation event, so kick the firmware to * look for another AP */ if (priv->config & CFG_STATIC_ESSID) ipw2100_set_essid(priv, priv->essid, priv->essid_len, 0); else ipw2100_set_essid(priv, NULL, 0, 0); mutex_unlock(&priv->action_mutex); } wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); } #define IPW2100_FW_MAJOR_VERSION 1 #define IPW2100_FW_MINOR_VERSION 3 #define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8) #define IPW2100_FW_MAJOR(x) (x & 0xff) #define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \ IPW2100_FW_MAJOR_VERSION) #define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \ "." __stringify(IPW2100_FW_MINOR_VERSION) #define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw" /* BINARY FIRMWARE HEADER FORMAT offset length desc 0 2 version 2 2 mode == 0:BSS,1:IBSS,2:MONITOR 4 4 fw_len 8 4 uc_len C fw_len firmware data 12 + fw_len uc_len microcode data */ struct ipw2100_fw_header { short version; short mode; unsigned int fw_size; unsigned int uc_size; } __packed; static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw) { struct ipw2100_fw_header *h = (struct ipw2100_fw_header *)fw->fw_entry->data; if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) { printk(KERN_WARNING DRV_NAME ": Firmware image not compatible " "(detected version id of %u). " "See Documentation/networking/README.ipw2100\n", h->version); return 1; } fw->version = h->version; fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header); fw->fw.size = h->fw_size; fw->uc.data = fw->fw.data + h->fw_size; fw->uc.size = h->uc_size; return 0; } static int ipw2100_get_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw) { char *fw_name; int rc; IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n", priv->net_dev->name); switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: fw_name = IPW2100_FW_NAME("-i"); break; #ifdef CONFIG_IPW2100_MONITOR case IW_MODE_MONITOR: fw_name = IPW2100_FW_NAME("-p"); break; #endif case IW_MODE_INFRA: default: fw_name = IPW2100_FW_NAME(""); break; } rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev); if (rc < 0) { printk(KERN_ERR DRV_NAME ": " "%s: Firmware '%s' not available or load failed.\n", priv->net_dev->name, fw_name); return rc; } IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data, fw->fw_entry->size); ipw2100_mod_firmware_load(fw); return 0; } MODULE_FIRMWARE(IPW2100_FW_NAME("-i")); #ifdef CONFIG_IPW2100_MONITOR MODULE_FIRMWARE(IPW2100_FW_NAME("-p")); #endif MODULE_FIRMWARE(IPW2100_FW_NAME("")); static void ipw2100_release_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw) { fw->version = 0; release_firmware(fw->fw_entry); fw->fw_entry = NULL; } static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, size_t max) { char ver[MAX_FW_VERSION_LEN]; u32 len = MAX_FW_VERSION_LEN; u32 tmp; int i; /* firmware version is an ascii string (max len of 14) */ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len)) return -EIO; tmp = max; if (len >= max) len = max - 1; for (i = 0; i < len; i++) buf[i] = ver[i]; buf[i] = '\0'; return tmp; } static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, size_t max) { u32 ver; u32 len = sizeof(ver); /* microcode version is a 32 bit integer */ if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len)) return -EIO; return snprintf(buf, max, "%08X", ver); } /* * On exit, the firmware will have been freed from the fw list */ static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw) { /* firmware is constructed of N contiguous entries, each entry is * structured as: * * offset sie desc * 0 4 address to write to * 4 2 length of data run * 6 length data */ unsigned int addr; unsigned short len; const unsigned char *firmware_data = fw->fw.data; unsigned int firmware_data_left = fw->fw.size; while (firmware_data_left > 0) { addr = *(u32 *) (firmware_data); firmware_data += 4; firmware_data_left -= 4; len = *(u16 *) (firmware_data); firmware_data += 2; firmware_data_left -= 2; if (len > 32) { printk(KERN_ERR DRV_NAME ": " "Invalid firmware run-length of %d bytes\n", len); return -EINVAL; } write_nic_memory(priv->net_dev, addr, len, firmware_data); firmware_data += len; firmware_data_left -= len; } return 0; } struct symbol_alive_response { u8 cmd_id; u8 seq_num; u8 ucode_rev; u8 eeprom_valid; u16 valid_flags; u8 IEEE_addr[6]; u16 flags; u16 pcb_rev; u16 clock_settle_time; // 1us LSB u16 powerup_settle_time; // 1us LSB u16 hop_settle_time; // 1us LSB u8 date[3]; // month, day, year u8 time[2]; // hours, minutes u8 ucode_valid; }; static int ipw2100_ucode_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw) { struct net_device *dev = priv->net_dev; const unsigned char *microcode_data = fw->uc.data; unsigned int microcode_data_left = fw->uc.size; void __iomem *reg = priv->ioaddr; struct symbol_alive_response response; int i, j; u8 data; /* Symbol control */ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); readl(reg); write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); readl(reg); /* HW config */ write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ readl(reg); write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ readl(reg); /* EN_CS_ACCESS bit to reset control store pointer */ write_nic_byte(dev, 0x210000, 0x40); readl(reg); write_nic_byte(dev, 0x210000, 0x0); readl(reg); write_nic_byte(dev, 0x210000, 0x40); readl(reg); /* copy microcode from buffer into Symbol */ while (microcode_data_left > 0) { write_nic_byte(dev, 0x210010, *microcode_data++); write_nic_byte(dev, 0x210010, *microcode_data++); microcode_data_left -= 2; } /* EN_CS_ACCESS bit to reset the control store pointer */ write_nic_byte(dev, 0x210000, 0x0); readl(reg); /* Enable System (Reg 0) * first enable causes garbage in RX FIFO */ write_nic_byte(dev, 0x210000, 0x0); readl(reg); write_nic_byte(dev, 0x210000, 0x80); readl(reg); /* Reset External Baseband Reg */ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); readl(reg); write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); readl(reg); /* HW Config (Reg 5) */ write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 readl(reg); write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 readl(reg); /* Enable System (Reg 0) * second enable should be OK */ write_nic_byte(dev, 0x210000, 0x00); // clear enable system readl(reg); write_nic_byte(dev, 0x210000, 0x80); // set enable system /* check Symbol is enabled - upped this from 5 as it wasn't always * catching the update */ for (i = 0; i < 10; i++) { udelay(10); /* check Dino is enabled bit */ read_nic_byte(dev, 0x210000, &data); if (data & 0x1) break; } if (i == 10) { printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n", dev->name); return -EIO; } /* Get Symbol alive response */ for (i = 0; i < 30; i++) { /* Read alive response structure */ for (j = 0; j < (sizeof(struct symbol_alive_response) >> 1); j++) read_nic_word(dev, 0x210004, ((u16 *) & response) + j); if ((response.cmd_id == 1) && (response.ucode_valid == 0x1)) break; udelay(10); } if (i == 30) { printk(KERN_ERR DRV_NAME ": %s: No response from Symbol - hw not alive\n", dev->name); printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response)); return -EIO; } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/ipw2200.c0000644000175000017500000121513512026211315023151 0ustar mcgrofmcgrof/****************************************************************************** Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. 802.11 status code portion of this file from ethereal-0.10.6: Copyright 2000, Axis Communications AB Ethereal - Network traffic analyzer By Gerald Combs Copyright 1998 Gerald Combs This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ #include #include #include #include "ipw2200.h" #include "ipw.h" #ifndef KBUILD_EXTMOD #define VK "k" #else #define VK #endif #ifdef CONFIG_IPW2200_DEBUG #define VD "d" #else #define VD #endif #ifdef CONFIG_IPW2200_MONITOR #define VM "m" #else #define VM #endif #ifdef CONFIG_IPW2200_PROMISCUOUS #define VP "p" #else #define VP #endif #ifdef CONFIG_IPW2200_RADIOTAP #define VR "r" #else #define VR #endif #ifdef CONFIG_IPW2200_QOS #define VQ "q" #else #define VQ #endif #define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ #define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" #define DRV_VERSION IPW2200_VERSION #define ETH_P_80211_STATS (ETH_P_80211_RAW + 1) MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("ipw2200-ibss.fw"); #ifdef CONFIG_IPW2200_MONITOR MODULE_FIRMWARE("ipw2200-sniffer.fw"); #endif MODULE_FIRMWARE("ipw2200-bss.fw"); static int cmdlog = 0; static int debug = 0; static int default_channel = 0; static int network_mode = 0; static u32 ipw_debug_level; static int associate; static int auto_create = 1; static int led_support = 1; static int disable = 0; static int bt_coexist = 0; static int hwcrypto = 0; static int roaming = 1; static const char ipw_modes[] = { 'a', 'b', 'g', '?' }; static int antenna = CFG_SYS_ANTENNA_BOTH; #ifdef CONFIG_IPW2200_PROMISCUOUS static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */ #endif static struct ieee80211_rate ipw2200_rates[] = { { .bitrate = 10 }, { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60 }, { .bitrate = 90 }, { .bitrate = 120 }, { .bitrate = 180 }, { .bitrate = 240 }, { .bitrate = 360 }, { .bitrate = 480 }, { .bitrate = 540 } }; #define ipw2200_a_rates (ipw2200_rates + 4) #define ipw2200_num_a_rates 8 #define ipw2200_bg_rates (ipw2200_rates + 0) #define ipw2200_num_bg_rates 12 /* Ugly macro to convert literal channel numbers into their mhz equivalents * There are certianly some conditions that will break this (like feeding it '30') * but they shouldn't arise since nothing talks on channel 30. */ #define ieee80211chan2mhz(x) \ (((x) <= 14) ? \ (((x) == 14) ? 2484 : ((x) * 5) + 2407) : \ ((x) + 1000) * 5) #ifdef CONFIG_IPW2200_QOS static int qos_enable = 0; static int qos_burst_enable = 0; static int qos_no_ack_mask = 0; static int burst_duration_CCK = 0; static int burst_duration_OFDM = 0; static struct libipw_qos_parameters def_qos_parameters_OFDM = { {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM, QOS_TX3_CW_MIN_OFDM}, {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM, QOS_TX3_CW_MAX_OFDM}, {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS}, {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM}, {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM, QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM} }; static struct libipw_qos_parameters def_qos_parameters_CCK = { {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK, QOS_TX3_CW_MIN_CCK}, {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK, QOS_TX3_CW_MAX_CCK}, {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS}, {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM}, {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK, QOS_TX3_TXOP_LIMIT_CCK} }; static struct libipw_qos_parameters def_parameters_OFDM = { {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM, DEF_TX3_CW_MIN_OFDM}, {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM, DEF_TX3_CW_MAX_OFDM}, {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS}, {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM}, {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM, DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM} }; static struct libipw_qos_parameters def_parameters_CCK = { {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK, DEF_TX3_CW_MIN_CCK}, {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK, DEF_TX3_CW_MAX_CCK}, {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS}, {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM}, {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK, DEF_TX3_TXOP_LIMIT_CCK} }; static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; static int from_priority_to_tx_queue[] = { IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1, IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4 }; static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv); static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters *qos_param); static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element *qos_param); #endif /* CONFIG_IPW2200_QOS */ static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev); static void ipw_remove_current_network(struct ipw_priv *priv); static void ipw_rx(struct ipw_priv *priv); static int ipw_queue_tx_reclaim(struct ipw_priv *priv, struct clx2_tx_queue *txq, int qindex); static int ipw_queue_reset(struct ipw_priv *priv); static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, int len, int sync); static void ipw_tx_queue_free(struct ipw_priv *); static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *); static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *); static void ipw_rx_queue_replenish(void *); static int ipw_up(struct ipw_priv *); static void ipw_bg_up(struct work_struct *work); static void ipw_down(struct ipw_priv *); static void ipw_bg_down(struct work_struct *work); static int ipw_config(struct ipw_priv *); static int init_supported_rates(struct ipw_priv *priv, struct ipw_supported_rates *prates); static void ipw_set_hwcrypto_keys(struct ipw_priv *); static void ipw_send_wep_keys(struct ipw_priv *, int); static int snprint_line(char *buf, size_t count, const u8 * data, u32 len, u32 ofs) { int out, i, j, l; char c; out = snprintf(buf, count, "%08X", ofs); for (l = 0, i = 0; i < 2; i++) { out += snprintf(buf + out, count - out, " "); for (j = 0; j < 8 && l < len; j++, l++) out += snprintf(buf + out, count - out, "%02X ", data[(i * 8 + j)]); for (; j < 8; j++) out += snprintf(buf + out, count - out, " "); } out += snprintf(buf + out, count - out, " "); for (l = 0, i = 0; i < 2; i++) { out += snprintf(buf + out, count - out, " "); for (j = 0; j < 8 && l < len; j++, l++) { c = data[(i * 8 + j)]; if (!isascii(c) || !isprint(c)) c = '.'; out += snprintf(buf + out, count - out, "%c", c); } for (; j < 8; j++) out += snprintf(buf + out, count - out, " "); } return out; } static void printk_buf(int level, const u8 * data, u32 len) { char line[81]; u32 ofs = 0; if (!(ipw_debug_level & level)) return; while (len) { snprint_line(line, sizeof(line), &data[ofs], min(len, 16U), ofs); printk(KERN_DEBUG "%s\n", line); ofs += 16; len -= min(len, 16U); } } static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len) { size_t out = size; u32 ofs = 0; int total = 0; while (size && len) { out = snprint_line(output, size, &data[ofs], min_t(size_t, len, 16U), ofs); ofs += 16; output += out; size -= out; len -= min_t(size_t, len, 16U); total += out; } return total; } /* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */ static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg); #define ipw_read_reg32(a, b) _ipw_read_reg32(a, b) /* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */ static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg); #define ipw_read_reg8(a, b) _ipw_read_reg8(a, b) /* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value); static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c) { IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32) (b), (u32) (c)); _ipw_write_reg8(a, b, c); } /* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value); static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c) { IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32) (b), (u32) (c)); _ipw_write_reg16(a, b, c); } /* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value); static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c) { IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32) (b), (u32) (c)); _ipw_write_reg32(a, b, c); } /* 8-bit direct write (low 4K) */ static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs, u8 val) { writeb(val, ipw->hw_base + ofs); } /* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ #define ipw_write8(ipw, ofs, val) do { \ IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, \ __LINE__, (u32)(ofs), (u32)(val)); \ _ipw_write8(ipw, ofs, val); \ } while (0) /* 16-bit direct write (low 4K) */ static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs, u16 val) { writew(val, ipw->hw_base + ofs); } /* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ #define ipw_write16(ipw, ofs, val) do { \ IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, \ __LINE__, (u32)(ofs), (u32)(val)); \ _ipw_write16(ipw, ofs, val); \ } while (0) /* 32-bit direct write (low 4K) */ static inline void _ipw_write32(struct ipw_priv *ipw, unsigned long ofs, u32 val) { writel(val, ipw->hw_base + ofs); } /* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ #define ipw_write32(ipw, ofs, val) do { \ IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, \ __LINE__, (u32)(ofs), (u32)(val)); \ _ipw_write32(ipw, ofs, val); \ } while (0) /* 8-bit direct read (low 4K) */ static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs) { return readb(ipw->hw_base + ofs); } /* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */ #define ipw_read8(ipw, ofs) ({ \ IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", __FILE__, __LINE__, \ (u32)(ofs)); \ _ipw_read8(ipw, ofs); \ }) /* 16-bit direct read (low 4K) */ static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs) { return readw(ipw->hw_base + ofs); } /* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */ #define ipw_read16(ipw, ofs) ({ \ IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", __FILE__, __LINE__, \ (u32)(ofs)); \ _ipw_read16(ipw, ofs); \ }) /* 32-bit direct read (low 4K) */ static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs) { return readl(ipw->hw_base + ofs); } /* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */ #define ipw_read32(ipw, ofs) ({ \ IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", __FILE__, __LINE__, \ (u32)(ofs)); \ _ipw_read32(ipw, ofs); \ }) static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int); /* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */ #define ipw_read_indirect(a, b, c, d) ({ \ IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %u bytes\n", __FILE__, \ __LINE__, (u32)(b), (u32)(d)); \ _ipw_read_indirect(a, b, c, d); \ }) /* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */ static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data, int num); #define ipw_write_indirect(a, b, c, d) do { \ IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %u bytes\n", __FILE__, \ __LINE__, (u32)(b), (u32)(d)); \ _ipw_write_indirect(a, b, c, d); \ } while (0) /* 32-bit indirect write (above 4K) */ static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value) { IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value); _ipw_write32(priv, IPW_INDIRECT_ADDR, reg); _ipw_write32(priv, IPW_INDIRECT_DATA, value); } /* 8-bit indirect write (above 4K) */ static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value) { u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */ u32 dif_len = reg - aligned_addr; IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); _ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value); } /* 16-bit indirect write (above 4K) */ static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value) { u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */ u32 dif_len = (reg - aligned_addr) & (~0x1ul); IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); _ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value); } /* 8-bit indirect read (above 4K) */ static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg) { u32 word; _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK); IPW_DEBUG_IO(" reg = 0x%8X :\n", reg); word = _ipw_read32(priv, IPW_INDIRECT_DATA); return (word >> ((reg & 0x3) * 8)) & 0xff; } /* 32-bit indirect read (above 4K) */ static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg) { u32 value; IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg); _ipw_write32(priv, IPW_INDIRECT_ADDR, reg); value = _ipw_read32(priv, IPW_INDIRECT_DATA); IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x\n", reg, value); return value; } /* General purpose, no alignment requirement, iterative (multi-byte) read, */ /* for area above 1st 4K of SRAM/reg space */ static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, int num) { u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */ u32 dif_len = addr - aligned_addr; u32 i; IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); if (num <= 0) { return; } /* Read the first dword (or portion) byte by byte */ if (unlikely(dif_len)) { _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); /* Start reading at aligned_addr + dif_len */ for (i = dif_len; ((i < 4) && (num > 0)); i++, num--) *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i); aligned_addr += 4; } /* Read all of the middle dwords as dwords, with auto-increment */ _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4) *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA); /* Read the last dword (or portion) byte by byte */ if (unlikely(num)) { _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); for (i = 0; num > 0; i++, num--) *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i); } } /* General purpose, no alignment requirement, iterative (multi-byte) write, */ /* for area above 1st 4K of SRAM/reg space */ static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, int num) { u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */ u32 dif_len = addr - aligned_addr; u32 i; IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); if (num <= 0) { return; } /* Write the first dword (or portion) byte by byte */ if (unlikely(dif_len)) { _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); /* Start writing at aligned_addr + dif_len */ for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++) _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf); aligned_addr += 4; } /* Write all of the middle dwords as dwords, with auto-increment */ _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4) _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf); /* Write the last dword (or portion) byte by byte */ if (unlikely(num)) { _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); for (i = 0; num > 0; i++, num--, buf++) _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf); } } /* General purpose, no alignment requirement, iterative (multi-byte) write, */ /* for 1st 4K of SRAM/regs space */ static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf, int num) { memcpy_toio((priv->hw_base + addr), buf, num); } /* Set bit(s) in low 4K of SRAM/regs */ static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask) { ipw_write32(priv, reg, ipw_read32(priv, reg) | mask); } /* Clear bit(s) in low 4K of SRAM/regs */ static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask) { ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask); } static inline void __ipw_enable_interrupts(struct ipw_priv *priv) { if (priv->status & STATUS_INT_ENABLED) return; priv->status |= STATUS_INT_ENABLED; ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL); } static inline void __ipw_disable_interrupts(struct ipw_priv *priv) { if (!(priv->status & STATUS_INT_ENABLED)) return; priv->status &= ~STATUS_INT_ENABLED; ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); } static inline void ipw_enable_interrupts(struct ipw_priv *priv) { unsigned long flags; spin_lock_irqsave(&priv->irq_lock, flags); __ipw_enable_interrupts(priv); spin_unlock_irqrestore(&priv->irq_lock, flags); } static inline void ipw_disable_interrupts(struct ipw_priv *priv) { unsigned long flags; spin_lock_irqsave(&priv->irq_lock, flags); __ipw_disable_interrupts(priv); spin_unlock_irqrestore(&priv->irq_lock, flags); } static char *ipw_error_desc(u32 val) { switch (val) { case IPW_FW_ERROR_OK: return "ERROR_OK"; case IPW_FW_ERROR_FAIL: return "ERROR_FAIL"; case IPW_FW_ERROR_MEMORY_UNDERFLOW: return "MEMORY_UNDERFLOW"; case IPW_FW_ERROR_MEMORY_OVERFLOW: return "MEMORY_OVERFLOW"; case IPW_FW_ERROR_BAD_PARAM: return "BAD_PARAM"; case IPW_FW_ERROR_BAD_CHECKSUM: return "BAD_CHECKSUM"; case IPW_FW_ERROR_NMI_INTERRUPT: return "NMI_INTERRUPT"; case IPW_FW_ERROR_BAD_DATABASE: return "BAD_DATABASE"; case IPW_FW_ERROR_ALLOC_FAIL: return "ALLOC_FAIL"; case IPW_FW_ERROR_DMA_UNDERRUN: return "DMA_UNDERRUN"; case IPW_FW_ERROR_DMA_STATUS: return "DMA_STATUS"; case IPW_FW_ERROR_DINO_ERROR: return "DINO_ERROR"; case IPW_FW_ERROR_EEPROM_ERROR: return "EEPROM_ERROR"; case IPW_FW_ERROR_SYSASSERT: return "SYSASSERT"; case IPW_FW_ERROR_FATAL_ERROR: return "FATAL_ERROR"; default: return "UNKNOWN_ERROR"; } } static void ipw_dump_error_log(struct ipw_priv *priv, struct ipw_fw_error *error) { u32 i; if (!error) { IPW_ERROR("Error allocating and capturing error log. " "Nothing to dump.\n"); return; } IPW_ERROR("Start IPW Error Log Dump:\n"); IPW_ERROR("Status: 0x%08X, Config: %08X\n", error->status, error->config); for (i = 0; i < error->elem_len; i++) IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", ipw_error_desc(error->elem[i].desc), error->elem[i].time, error->elem[i].blink1, error->elem[i].blink2, error->elem[i].link1, error->elem[i].link2, error->elem[i].data); for (i = 0; i < error->log_len; i++) IPW_ERROR("%i\t0x%08x\t%i\n", error->log[i].time, error->log[i].data, error->log[i].event); } static inline int ipw_is_init(struct ipw_priv *priv) { return (priv->status & STATUS_INIT) ? 1 : 0; } static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len) { u32 addr, field_info, field_len, field_count, total_len; IPW_DEBUG_ORD("ordinal = %i\n", ord); if (!priv || !val || !len) { IPW_DEBUG_ORD("Invalid argument\n"); return -EINVAL; } /* verify device ordinal tables have been initialized */ if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) { IPW_DEBUG_ORD("Access ordinals before initialization\n"); return -EINVAL; } switch (IPW_ORD_TABLE_ID_MASK & ord) { case IPW_ORD_TABLE_0_MASK: /* * TABLE 0: Direct access to a table of 32 bit values * * This is a very simple table with the data directly * read from the table */ /* remove the table id from the ordinal */ ord &= IPW_ORD_TABLE_VALUE_MASK; /* boundary check */ if (ord > priv->table0_len) { IPW_DEBUG_ORD("ordinal value (%i) longer then " "max (%i)\n", ord, priv->table0_len); return -EINVAL; } /* verify we have enough room to store the value */ if (*len < sizeof(u32)) { IPW_DEBUG_ORD("ordinal buffer length too small, " "need %zd\n", sizeof(u32)); return -EINVAL; } IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n", ord, priv->table0_addr + (ord << 2)); *len = sizeof(u32); ord <<= 2; *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord); break; case IPW_ORD_TABLE_1_MASK: /* * TABLE 1: Indirect access to a table of 32 bit values * * This is a fairly large table of u32 values each * representing starting addr for the data (which is * also a u32) */ /* remove the table id from the ordinal */ ord &= IPW_ORD_TABLE_VALUE_MASK; /* boundary check */ if (ord > priv->table1_len) { IPW_DEBUG_ORD("ordinal value too long\n"); return -EINVAL; } /* verify we have enough room to store the value */ if (*len < sizeof(u32)) { IPW_DEBUG_ORD("ordinal buffer length too small, " "need %zd\n", sizeof(u32)); return -EINVAL; } *((u32 *) val) = ipw_read_reg32(priv, (priv->table1_addr + (ord << 2))); *len = sizeof(u32); break; case IPW_ORD_TABLE_2_MASK: /* * TABLE 2: Indirect access to a table of variable sized values * * This table consist of six values, each containing * - dword containing the starting offset of the data * - dword containing the lengh in the first 16bits * and the count in the second 16bits */ /* remove the table id from the ordinal */ ord &= IPW_ORD_TABLE_VALUE_MASK; /* boundary check */ if (ord > priv->table2_len) { IPW_DEBUG_ORD("ordinal value too long\n"); return -EINVAL; } /* get the address of statistic */ addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3)); /* get the second DW of statistics ; * two 16-bit words - first is length, second is count */ field_info = ipw_read_reg32(priv, priv->table2_addr + (ord << 3) + sizeof(u32)); /* get each entry length */ field_len = *((u16 *) & field_info); /* get number of entries */ field_count = *(((u16 *) & field_info) + 1); /* abort if not enough memory */ total_len = field_len * field_count; if (total_len > *len) { *len = total_len; return -EINVAL; } *len = total_len; if (!total_len) return 0; IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, " "field_info = 0x%08x\n", addr, total_len, field_info); ipw_read_indirect(priv, addr, val, total_len); break; default: IPW_DEBUG_ORD("Invalid ordinal!\n"); return -EINVAL; } return 0; } static void ipw_init_ordinals(struct ipw_priv *priv) { priv->table0_addr = IPW_ORDINALS_TABLE_LOWER; priv->table0_len = ipw_read32(priv, priv->table0_addr); IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n", priv->table0_addr, priv->table0_len); priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1); priv->table1_len = ipw_read_reg32(priv, priv->table1_addr); IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n", priv->table1_addr, priv->table1_len); priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2); priv->table2_len = ipw_read_reg32(priv, priv->table2_addr); priv->table2_len &= 0x0000ffff; /* use first two bytes */ IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n", priv->table2_addr, priv->table2_len); } static u32 ipw_register_toggle(u32 reg) { reg &= ~IPW_START_STANDBY; if (reg & IPW_GATE_ODMA) reg &= ~IPW_GATE_ODMA; if (reg & IPW_GATE_IDMA) reg &= ~IPW_GATE_IDMA; if (reg & IPW_GATE_ADMA) reg &= ~IPW_GATE_ADMA; return reg; } /* * LED behavior: * - On radio ON, turn on any LEDs that require to be on during start * - On initialization, start unassociated blink * - On association, disable unassociated blink * - On disassociation, start unassociated blink * - On radio OFF, turn off any LEDs started during radio on * */ #define LD_TIME_LINK_ON msecs_to_jiffies(300) #define LD_TIME_LINK_OFF msecs_to_jiffies(2700) #define LD_TIME_ACT_ON msecs_to_jiffies(250) static void ipw_led_link_on(struct ipw_priv *priv) { unsigned long flags; u32 led; /* If configured to not use LEDs, or nic_type is 1, * then we don't toggle a LINK led */ if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) return; spin_lock_irqsave(&priv->lock, flags); if (!(priv->status & STATUS_RF_KILL_MASK) && !(priv->status & STATUS_LED_LINK_ON)) { IPW_DEBUG_LED("Link LED On\n"); led = ipw_read_reg32(priv, IPW_EVENT_REG); led |= priv->led_association_on; led = ipw_register_toggle(led); IPW_DEBUG_LED("Reg: 0x%08X\n", led); ipw_write_reg32(priv, IPW_EVENT_REG, led); priv->status |= STATUS_LED_LINK_ON; /* If we aren't associated, schedule turning the LED off */ if (!(priv->status & STATUS_ASSOCIATED)) schedule_delayed_work(&priv->led_link_off, LD_TIME_LINK_ON); } spin_unlock_irqrestore(&priv->lock, flags); } static void ipw_bg_led_link_on(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, led_link_on.work); mutex_lock(&priv->mutex); ipw_led_link_on(priv); mutex_unlock(&priv->mutex); } static void ipw_led_link_off(struct ipw_priv *priv) { unsigned long flags; u32 led; /* If configured not to use LEDs, or nic type is 1, * then we don't goggle the LINK led. */ if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) return; spin_lock_irqsave(&priv->lock, flags); if (priv->status & STATUS_LED_LINK_ON) { led = ipw_read_reg32(priv, IPW_EVENT_REG); led &= priv->led_association_off; led = ipw_register_toggle(led); IPW_DEBUG_LED("Reg: 0x%08X\n", led); ipw_write_reg32(priv, IPW_EVENT_REG, led); IPW_DEBUG_LED("Link LED Off\n"); priv->status &= ~STATUS_LED_LINK_ON; /* If we aren't associated and the radio is on, schedule * turning the LED on (blink while unassociated) */ if (!(priv->status & STATUS_RF_KILL_MASK) && !(priv->status & STATUS_ASSOCIATED)) schedule_delayed_work(&priv->led_link_on, LD_TIME_LINK_OFF); } spin_unlock_irqrestore(&priv->lock, flags); } static void ipw_bg_led_link_off(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, led_link_off.work); mutex_lock(&priv->mutex); ipw_led_link_off(priv); mutex_unlock(&priv->mutex); } static void __ipw_led_activity_on(struct ipw_priv *priv) { u32 led; if (priv->config & CFG_NO_LED) return; if (priv->status & STATUS_RF_KILL_MASK) return; if (!(priv->status & STATUS_LED_ACT_ON)) { led = ipw_read_reg32(priv, IPW_EVENT_REG); led |= priv->led_activity_on; led = ipw_register_toggle(led); IPW_DEBUG_LED("Reg: 0x%08X\n", led); ipw_write_reg32(priv, IPW_EVENT_REG, led); IPW_DEBUG_LED("Activity LED On\n"); priv->status |= STATUS_LED_ACT_ON; cancel_delayed_work(&priv->led_act_off); schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON); } else { /* Reschedule LED off for full time period */ cancel_delayed_work(&priv->led_act_off); schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON); } } #if 0 void ipw_led_activity_on(struct ipw_priv *priv) { unsigned long flags; spin_lock_irqsave(&priv->lock, flags); __ipw_led_activity_on(priv); spin_unlock_irqrestore(&priv->lock, flags); } #endif /* 0 */ static void ipw_led_activity_off(struct ipw_priv *priv) { unsigned long flags; u32 led; if (priv->config & CFG_NO_LED) return; spin_lock_irqsave(&priv->lock, flags); if (priv->status & STATUS_LED_ACT_ON) { led = ipw_read_reg32(priv, IPW_EVENT_REG); led &= priv->led_activity_off; led = ipw_register_toggle(led); IPW_DEBUG_LED("Reg: 0x%08X\n", led); ipw_write_reg32(priv, IPW_EVENT_REG, led); IPW_DEBUG_LED("Activity LED Off\n"); priv->status &= ~STATUS_LED_ACT_ON; } spin_unlock_irqrestore(&priv->lock, flags); } static void ipw_bg_led_activity_off(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, led_act_off.work); mutex_lock(&priv->mutex); ipw_led_activity_off(priv); mutex_unlock(&priv->mutex); } static void ipw_led_band_on(struct ipw_priv *priv) { unsigned long flags; u32 led; /* Only nic type 1 supports mode LEDs */ if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network) return; spin_lock_irqsave(&priv->lock, flags); led = ipw_read_reg32(priv, IPW_EVENT_REG); if (priv->assoc_network->mode == IEEE_A) { led |= priv->led_ofdm_on; led &= priv->led_association_off; IPW_DEBUG_LED("Mode LED On: 802.11a\n"); } else if (priv->assoc_network->mode == IEEE_G) { led |= priv->led_ofdm_on; led |= priv->led_association_on; IPW_DEBUG_LED("Mode LED On: 802.11g\n"); } else { led &= priv->led_ofdm_off; led |= priv->led_association_on; IPW_DEBUG_LED("Mode LED On: 802.11b\n"); } led = ipw_register_toggle(led); IPW_DEBUG_LED("Reg: 0x%08X\n", led); ipw_write_reg32(priv, IPW_EVENT_REG, led); spin_unlock_irqrestore(&priv->lock, flags); } static void ipw_led_band_off(struct ipw_priv *priv) { unsigned long flags; u32 led; /* Only nic type 1 supports mode LEDs */ if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1) return; spin_lock_irqsave(&priv->lock, flags); led = ipw_read_reg32(priv, IPW_EVENT_REG); led &= priv->led_ofdm_off; led &= priv->led_association_off; led = ipw_register_toggle(led); IPW_DEBUG_LED("Reg: 0x%08X\n", led); ipw_write_reg32(priv, IPW_EVENT_REG, led); spin_unlock_irqrestore(&priv->lock, flags); } static void ipw_led_radio_on(struct ipw_priv *priv) { ipw_led_link_on(priv); } static void ipw_led_radio_off(struct ipw_priv *priv) { ipw_led_activity_off(priv); ipw_led_link_off(priv); } static void ipw_led_link_up(struct ipw_priv *priv) { /* Set the Link Led on for all nic types */ ipw_led_link_on(priv); } static void ipw_led_link_down(struct ipw_priv *priv) { ipw_led_activity_off(priv); ipw_led_link_off(priv); if (priv->status & STATUS_RF_KILL_MASK) ipw_led_radio_off(priv); } static void ipw_led_init(struct ipw_priv *priv) { priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE]; /* Set the default PINs for the link and activity leds */ priv->led_activity_on = IPW_ACTIVITY_LED; priv->led_activity_off = ~(IPW_ACTIVITY_LED); priv->led_association_on = IPW_ASSOCIATED_LED; priv->led_association_off = ~(IPW_ASSOCIATED_LED); /* Set the default PINs for the OFDM leds */ priv->led_ofdm_on = IPW_OFDM_LED; priv->led_ofdm_off = ~(IPW_OFDM_LED); switch (priv->nic_type) { case EEPROM_NIC_TYPE_1: /* In this NIC type, the LEDs are reversed.... */ priv->led_activity_on = IPW_ASSOCIATED_LED; priv->led_activity_off = ~(IPW_ASSOCIATED_LED); priv->led_association_on = IPW_ACTIVITY_LED; priv->led_association_off = ~(IPW_ACTIVITY_LED); if (!(priv->config & CFG_NO_LED)) ipw_led_band_on(priv); /* And we don't blink link LEDs for this nic, so * just return here */ return; case EEPROM_NIC_TYPE_3: case EEPROM_NIC_TYPE_2: case EEPROM_NIC_TYPE_4: case EEPROM_NIC_TYPE_0: break; default: IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n", priv->nic_type); priv->nic_type = EEPROM_NIC_TYPE_0; break; } if (!(priv->config & CFG_NO_LED)) { if (priv->status & STATUS_ASSOCIATED) ipw_led_link_on(priv); else ipw_led_link_off(priv); } } static void ipw_led_shutdown(struct ipw_priv *priv) { ipw_led_activity_off(priv); ipw_led_link_off(priv); ipw_led_band_off(priv); cancel_delayed_work(&priv->led_link_on); cancel_delayed_work(&priv->led_link_off); cancel_delayed_work(&priv->led_act_off); } /* * The following adds a new attribute to the sysfs representation * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/) * used for controlling the debug level. * * See the level definitions in ipw for details. */ static ssize_t show_debug_level(struct device_driver *d, char *buf) { return sprintf(buf, "0x%08X\n", ipw_debug_level); } static ssize_t store_debug_level(struct device_driver *d, const char *buf, size_t count) { char *p = (char *)buf; u32 val; if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { p++; if (p[0] == 'x' || p[0] == 'X') p++; val = simple_strtoul(p, &p, 16); } else val = simple_strtoul(p, &p, 10); if (p == buf) printk(KERN_INFO DRV_NAME ": %s is not in hex or decimal form.\n", buf); else ipw_debug_level = val; return strnlen(buf, count); } static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level, store_debug_level); static inline u32 ipw_get_event_log_len(struct ipw_priv *priv) { /* length = 1st dword in log */ return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG)); } static void ipw_capture_event_log(struct ipw_priv *priv, u32 log_len, struct ipw_event *log) { u32 base; if (log_len) { base = ipw_read32(priv, IPW_EVENT_LOG); ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32), (u8 *) log, sizeof(*log) * log_len); } } static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv) { struct ipw_fw_error *error; u32 log_len = ipw_get_event_log_len(priv); u32 base = ipw_read32(priv, IPW_ERROR_LOG); u32 elem_len = ipw_read_reg32(priv, base); error = kmalloc(sizeof(*error) + sizeof(*error->elem) * elem_len + sizeof(*error->log) * log_len, GFP_ATOMIC); if (!error) { IPW_ERROR("Memory allocation for firmware error log " "failed.\n"); return NULL; } error->jiffies = jiffies; error->status = priv->status; error->config = priv->config; error->elem_len = elem_len; error->log_len = log_len; error->elem = (struct ipw_error_elem *)error->payload; error->log = (struct ipw_event *)(error->elem + elem_len); ipw_capture_event_log(priv, log_len, error->log); if (elem_len) ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem, sizeof(*error->elem) * elem_len); return error; } static ssize_t show_event_log(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); u32 log_len = ipw_get_event_log_len(priv); u32 log_size; struct ipw_event *log; u32 len = 0, i; /* not using min() because of its strict type checking */ log_size = PAGE_SIZE / sizeof(*log) > log_len ? sizeof(*log) * log_len : PAGE_SIZE; log = kzalloc(log_size, GFP_KERNEL); if (!log) { IPW_ERROR("Unable to allocate memory for log\n"); return 0; } log_len = log_size / sizeof(*log); ipw_capture_event_log(priv, log_len, log); len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len); for (i = 0; i < log_len; i++) len += snprintf(buf + len, PAGE_SIZE - len, "\n%08X%08X%08X", log[i].time, log[i].event, log[i].data); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); kfree(log); return len; } static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL); static ssize_t show_error(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); u32 len = 0, i; if (!priv->error) return 0; len += snprintf(buf + len, PAGE_SIZE - len, "%08lX%08X%08X%08X", priv->error->jiffies, priv->error->status, priv->error->config, priv->error->elem_len); for (i = 0; i < priv->error->elem_len; i++) len += snprintf(buf + len, PAGE_SIZE - len, "\n%08X%08X%08X%08X%08X%08X%08X", priv->error->elem[i].time, priv->error->elem[i].desc, priv->error->elem[i].blink1, priv->error->elem[i].blink2, priv->error->elem[i].link1, priv->error->elem[i].link2, priv->error->elem[i].data); len += snprintf(buf + len, PAGE_SIZE - len, "\n%08X", priv->error->log_len); for (i = 0; i < priv->error->log_len; i++) len += snprintf(buf + len, PAGE_SIZE - len, "\n%08X%08X%08X", priv->error->log[i].time, priv->error->log[i].event, priv->error->log[i].data); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } static ssize_t clear_error(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); kfree(priv->error); priv->error = NULL; return count; } static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error); static ssize_t show_cmd_log(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); u32 len = 0, i; if (!priv->cmdlog) return 0; for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len; (i != priv->cmdlog_pos) && (PAGE_SIZE - len); i = (i + 1) % priv->cmdlog_len) { len += snprintf(buf + len, PAGE_SIZE - len, "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies, priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd, priv->cmdlog[i].cmd.len); len += snprintk_buf(buf + len, PAGE_SIZE - len, (u8 *) priv->cmdlog[i].cmd.param, priv->cmdlog[i].cmd.len); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); } len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL); #ifdef CONFIG_IPW2200_PROMISCUOUS static void ipw_prom_free(struct ipw_priv *priv); static int ipw_prom_alloc(struct ipw_priv *priv); static ssize_t store_rtap_iface(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); int rc = 0; if (count < 1) return -EINVAL; switch (buf[0]) { case '0': if (!rtap_iface) return count; if (netif_running(priv->prom_net_dev)) { IPW_WARNING("Interface is up. Cannot unregister.\n"); return count; } ipw_prom_free(priv); rtap_iface = 0; break; case '1': if (rtap_iface) return count; rc = ipw_prom_alloc(priv); if (!rc) rtap_iface = 1; break; default: return -EINVAL; } if (rc) { IPW_ERROR("Failed to register promiscuous network " "device (error %d).\n", rc); } return count; } static ssize_t show_rtap_iface(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); if (rtap_iface) return sprintf(buf, "%s", priv->prom_net_dev->name); else { buf[0] = '-'; buf[1] = '1'; buf[2] = '\0'; return 3; } } static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface, store_rtap_iface); static ssize_t store_rtap_filter(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); if (!priv->prom_priv) { IPW_ERROR("Attempting to set filter without " "rtap_iface enabled.\n"); return -EPERM; } priv->prom_priv->filter = simple_strtol(buf, NULL, 0); IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n", BIT_ARG16(priv->prom_priv->filter)); return count; } static ssize_t show_rtap_filter(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); return sprintf(buf, "0x%04X", priv->prom_priv ? priv->prom_priv->filter : 0); } static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter, store_rtap_filter); #endif static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); return sprintf(buf, "%d\n", priv->ieee->scan_age); } static ssize_t store_scan_age(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); struct net_device *dev = priv->net_dev; char buffer[] = "00000000"; unsigned long len = (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1; unsigned long val; char *p = buffer; IPW_DEBUG_INFO("enter\n"); strncpy(buffer, buf, len); buffer[len] = 0; if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { p++; if (p[0] == 'x' || p[0] == 'X') p++; val = simple_strtoul(p, &p, 16); } else val = simple_strtoul(p, &p, 10); if (p == buffer) { IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name); } else { priv->ieee->scan_age = val; IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age); } IPW_DEBUG_INFO("exit\n"); return len; } static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age); static ssize_t show_led(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1); } static ssize_t store_led(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); IPW_DEBUG_INFO("enter\n"); if (count == 0) return 0; if (*buf == 0) { IPW_DEBUG_LED("Disabling LED control.\n"); priv->config |= CFG_NO_LED; ipw_led_shutdown(priv); } else { IPW_DEBUG_LED("Enabling LED control.\n"); priv->config &= ~CFG_NO_LED; ipw_led_init(priv); } IPW_DEBUG_INFO("exit\n"); return count; } static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led); static ssize_t show_status(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *p = dev_get_drvdata(d); return sprintf(buf, "0x%08x\n", (int)p->status); } static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); static ssize_t show_cfg(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *p = dev_get_drvdata(d); return sprintf(buf, "0x%08x\n", (int)p->config); } static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); static ssize_t show_nic_type(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); return sprintf(buf, "TYPE: %d\n", priv->nic_type); } static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL); static ssize_t show_ucode_version(struct device *d, struct device_attribute *attr, char *buf) { u32 len = sizeof(u32), tmp = 0; struct ipw_priv *p = dev_get_drvdata(d); if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len)) return 0; return sprintf(buf, "0x%08x\n", tmp); } static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL); static ssize_t show_rtc(struct device *d, struct device_attribute *attr, char *buf) { u32 len = sizeof(u32), tmp = 0; struct ipw_priv *p = dev_get_drvdata(d); if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len)) return 0; return sprintf(buf, "0x%08x\n", tmp); } static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL); /* * Add a device attribute to view/control the delay between eeprom * operations. */ static ssize_t show_eeprom_delay(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *p = dev_get_drvdata(d); int n = p->eeprom_delay; return sprintf(buf, "%i\n", n); } static ssize_t store_eeprom_delay(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *p = dev_get_drvdata(d); sscanf(buf, "%i", &p->eeprom_delay); return strnlen(buf, count); } static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO, show_eeprom_delay, store_eeprom_delay); static ssize_t show_command_event_reg(struct device *d, struct device_attribute *attr, char *buf) { u32 reg = 0; struct ipw_priv *p = dev_get_drvdata(d); reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT); return sprintf(buf, "0x%08x\n", reg); } static ssize_t store_command_event_reg(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { u32 reg; struct ipw_priv *p = dev_get_drvdata(d); sscanf(buf, "%x", ®); ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg); return strnlen(buf, count); } static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO, show_command_event_reg, store_command_event_reg); static ssize_t show_mem_gpio_reg(struct device *d, struct device_attribute *attr, char *buf) { u32 reg = 0; struct ipw_priv *p = dev_get_drvdata(d); reg = ipw_read_reg32(p, 0x301100); return sprintf(buf, "0x%08x\n", reg); } static ssize_t store_mem_gpio_reg(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { u32 reg; struct ipw_priv *p = dev_get_drvdata(d); sscanf(buf, "%x", ®); ipw_write_reg32(p, 0x301100, reg); return strnlen(buf, count); } static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO, show_mem_gpio_reg, store_mem_gpio_reg); static ssize_t show_indirect_dword(struct device *d, struct device_attribute *attr, char *buf) { u32 reg = 0; struct ipw_priv *priv = dev_get_drvdata(d); if (priv->status & STATUS_INDIRECT_DWORD) reg = ipw_read_reg32(priv, priv->indirect_dword); else reg = 0; return sprintf(buf, "0x%08x\n", reg); } static ssize_t store_indirect_dword(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); sscanf(buf, "%x", &priv->indirect_dword); priv->status |= STATUS_INDIRECT_DWORD; return strnlen(buf, count); } static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO, show_indirect_dword, store_indirect_dword); static ssize_t show_indirect_byte(struct device *d, struct device_attribute *attr, char *buf) { u8 reg = 0; struct ipw_priv *priv = dev_get_drvdata(d); if (priv->status & STATUS_INDIRECT_BYTE) reg = ipw_read_reg8(priv, priv->indirect_byte); else reg = 0; return sprintf(buf, "0x%02x\n", reg); } static ssize_t store_indirect_byte(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); sscanf(buf, "%x", &priv->indirect_byte); priv->status |= STATUS_INDIRECT_BYTE; return strnlen(buf, count); } static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO, show_indirect_byte, store_indirect_byte); static ssize_t show_direct_dword(struct device *d, struct device_attribute *attr, char *buf) { u32 reg = 0; struct ipw_priv *priv = dev_get_drvdata(d); if (priv->status & STATUS_DIRECT_DWORD) reg = ipw_read32(priv, priv->direct_dword); else reg = 0; return sprintf(buf, "0x%08x\n", reg); } static ssize_t store_direct_dword(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); sscanf(buf, "%x", &priv->direct_dword); priv->status |= STATUS_DIRECT_DWORD; return strnlen(buf, count); } static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO, show_direct_dword, store_direct_dword); static int rf_kill_active(struct ipw_priv *priv) { if (0 == (ipw_read32(priv, 0x30) & 0x10000)) { priv->status |= STATUS_RF_KILL_HW; wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); } else { priv->status &= ~STATUS_RF_KILL_HW; wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); } return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0; } static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, char *buf) { /* 0 - RF kill not enabled 1 - SW based RF kill active (sysfs) 2 - HW based RF kill active 3 - Both HW and SW baed RF kill active */ struct ipw_priv *priv = dev_get_drvdata(d); int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | (rf_kill_active(priv) ? 0x2 : 0x0); return sprintf(buf, "%i\n", val); } static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio) { if ((disable_radio ? 1 : 0) == ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0)) return 0; IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n", disable_radio ? "OFF" : "ON"); if (disable_radio) { priv->status |= STATUS_RF_KILL_SW; cancel_delayed_work(&priv->request_scan); cancel_delayed_work(&priv->request_direct_scan); cancel_delayed_work(&priv->request_passive_scan); cancel_delayed_work(&priv->scan_event); schedule_work(&priv->down); } else { priv->status &= ~STATUS_RF_KILL_SW; if (rf_kill_active(priv)) { IPW_DEBUG_RF_KILL("Can not turn radio back on - " "disabled by HW switch\n"); /* Make sure the RF_KILL check timer is running */ cancel_delayed_work(&priv->rf_kill); schedule_delayed_work(&priv->rf_kill, round_jiffies_relative(2 * HZ)); } else schedule_work(&priv->up); } return 1; } static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); ipw_radio_kill_sw(priv, buf[0] == '1'); return count; } static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill); static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); int pos = 0, len = 0; if (priv->config & CFG_SPEED_SCAN) { while (priv->speed_scan[pos] != 0) len += sprintf(&buf[len], "%d ", priv->speed_scan[pos++]); return len + sprintf(&buf[len], "\n"); } return sprintf(buf, "0\n"); } static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); int channel, pos = 0; const char *p = buf; /* list of space separated channels to scan, optionally ending with 0 */ while ((channel = simple_strtol(p, NULL, 0))) { if (pos == MAX_SPEED_SCAN - 1) { priv->speed_scan[pos] = 0; break; } if (libipw_is_valid_channel(priv->ieee, channel)) priv->speed_scan[pos++] = channel; else IPW_WARNING("Skipping invalid channel request: %d\n", channel); p = strchr(p, ' '); if (!p) break; while (*p == ' ' || *p == '\t') p++; } if (pos == 0) priv->config &= ~CFG_SPEED_SCAN; else { priv->speed_scan_pos = 0; priv->config |= CFG_SPEED_SCAN; } return count; } static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan, store_speed_scan); static ssize_t show_net_stats(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0'); } static ssize_t store_net_stats(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ipw_priv *priv = dev_get_drvdata(d); if (buf[0] == '1') priv->config |= CFG_NET_STATS; else priv->config &= ~CFG_NET_STATS; return count; } static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO, show_net_stats, store_net_stats); static ssize_t show_channels(struct device *d, struct device_attribute *attr, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); const struct libipw_geo *geo = libipw_get_geo(priv->ieee); int len = 0, i; len = sprintf(&buf[len], "Displaying %d channels in 2.4Ghz band " "(802.11bg):\n", geo->bg_channels); for (i = 0; i < geo->bg_channels; i++) { len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n", geo->bg[i].channel, geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT ? " (radar spectrum)" : "", ((geo->bg[i].flags & LIBIPW_CH_NO_IBSS) || (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)) ? "" : ", IBSS", geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY ? "passive only" : "active/passive", geo->bg[i].flags & LIBIPW_CH_B_ONLY ? "B" : "B/G"); } len += sprintf(&buf[len], "Displaying %d channels in 5.2Ghz band " "(802.11a):\n", geo->a_channels); for (i = 0; i < geo->a_channels; i++) { len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n", geo->a[i].channel, geo->a[i].flags & LIBIPW_CH_RADAR_DETECT ? " (radar spectrum)" : "", ((geo->a[i].flags & LIBIPW_CH_NO_IBSS) || (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT)) ? "" : ", IBSS", geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY ? "passive only" : "active/passive"); } return len; } static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); static void notify_wx_assoc_event(struct ipw_priv *priv) { union iwreq_data wrqu; wrqu.ap_addr.sa_family = ARPHRD_ETHER; if (priv->status & STATUS_ASSOCIATED) memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN); else memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); } static void ipw_irq_tasklet(struct ipw_priv *priv) { u32 inta, inta_mask, handled = 0; unsigned long flags; int rc = 0; spin_lock_irqsave(&priv->irq_lock, flags); inta = ipw_read32(priv, IPW_INTA_RW); inta_mask = ipw_read32(priv, IPW_INTA_MASK_R); if (inta == 0xFFFFFFFF) { /* Hardware disappeared */ IPW_WARNING("TASKLET INTA == 0xFFFFFFFF\n"); /* Only handle the cached INTA values */ inta = 0; } inta &= (IPW_INTA_MASK_ALL & inta_mask); /* Add any cached INTA values that need to be handled */ inta |= priv->isr_inta; spin_unlock_irqrestore(&priv->irq_lock, flags); spin_lock_irqsave(&priv->lock, flags); /* handle all the justifications for the interrupt */ if (inta & IPW_INTA_BIT_RX_TRANSFER) { ipw_rx(priv); handled |= IPW_INTA_BIT_RX_TRANSFER; } if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) { IPW_DEBUG_HC("Command completed.\n"); rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1); priv->status &= ~STATUS_HCMD_ACTIVE; wake_up_interruptible(&priv->wait_command_queue); handled |= IPW_INTA_BIT_TX_CMD_QUEUE; } if (inta & IPW_INTA_BIT_TX_QUEUE_1) { IPW_DEBUG_TX("TX_QUEUE_1\n"); rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0); handled |= IPW_INTA_BIT_TX_QUEUE_1; } if (inta & IPW_INTA_BIT_TX_QUEUE_2) { IPW_DEBUG_TX("TX_QUEUE_2\n"); rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1); handled |= IPW_INTA_BIT_TX_QUEUE_2; } if (inta & IPW_INTA_BIT_TX_QUEUE_3) { IPW_DEBUG_TX("TX_QUEUE_3\n"); rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2); handled |= IPW_INTA_BIT_TX_QUEUE_3; } if (inta & IPW_INTA_BIT_TX_QUEUE_4) { IPW_DEBUG_TX("TX_QUEUE_4\n"); rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3); handled |= IPW_INTA_BIT_TX_QUEUE_4; } if (inta & IPW_INTA_BIT_STATUS_CHANGE) { IPW_WARNING("STATUS_CHANGE\n"); handled |= IPW_INTA_BIT_STATUS_CHANGE; } if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) { IPW_WARNING("TX_PERIOD_EXPIRED\n"); handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED; } if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) { IPW_WARNING("HOST_CMD_DONE\n"); handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE; } if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) { IPW_WARNING("FW_INITIALIZATION_DONE\n"); handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE; } if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) { IPW_WARNING("PHY_OFF_DONE\n"); handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE; } if (inta & IPW_INTA_BIT_RF_KILL_DONE) { IPW_DEBUG_RF_KILL("RF_KILL_DONE\n"); priv->status |= STATUS_RF_KILL_HW; wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); wake_up_interruptible(&priv->wait_command_queue); priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); cancel_delayed_work(&priv->request_scan); cancel_delayed_work(&priv->request_direct_scan); cancel_delayed_work(&priv->request_passive_scan); cancel_delayed_work(&priv->scan_event); schedule_work(&priv->link_down); schedule_delayed_work(&priv->rf_kill, 2 * HZ); handled |= IPW_INTA_BIT_RF_KILL_DONE; } if (inta & IPW_INTA_BIT_FATAL_ERROR) { IPW_WARNING("Firmware error detected. Restarting.\n"); if (priv->error) { IPW_DEBUG_FW("Sysfs 'error' log already exists.\n"); if (ipw_debug_level & IPW_DL_FW_ERRORS) { struct ipw_fw_error *error = ipw_alloc_error_log(priv); ipw_dump_error_log(priv, error); kfree(error); } } else { priv->error = ipw_alloc_error_log(priv); if (priv->error) IPW_DEBUG_FW("Sysfs 'error' log captured.\n"); else IPW_DEBUG_FW("Error allocating sysfs 'error' " "log.\n"); if (ipw_debug_level & IPW_DL_FW_ERRORS) ipw_dump_error_log(priv, priv->error); } /* XXX: If hardware encryption is for WPA/WPA2, * we have to notify the supplicant. */ if (priv->ieee->sec.encrypt) { priv->status &= ~STATUS_ASSOCIATED; notify_wx_assoc_event(priv); } /* Keep the restart process from trying to send host * commands by clearing the INIT status bit */ priv->status &= ~STATUS_INIT; /* Cancel currently queued command. */ priv->status &= ~STATUS_HCMD_ACTIVE; wake_up_interruptible(&priv->wait_command_queue); schedule_work(&priv->adapter_restart); handled |= IPW_INTA_BIT_FATAL_ERROR; } if (inta & IPW_INTA_BIT_PARITY_ERROR) { IPW_ERROR("Parity error\n"); handled |= IPW_INTA_BIT_PARITY_ERROR; } if (handled != inta) { IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled); } spin_unlock_irqrestore(&priv->lock, flags); /* enable all interrupts */ ipw_enable_interrupts(priv); } #define IPW_CMD(x) case IPW_CMD_ ## x : return #x static char *get_cmd_string(u8 cmd) { switch (cmd) { IPW_CMD(HOST_COMPLETE); IPW_CMD(POWER_DOWN); IPW_CMD(SYSTEM_CONFIG); IPW_CMD(MULTICAST_ADDRESS); IPW_CMD(SSID); IPW_CMD(ADAPTER_ADDRESS); IPW_CMD(PORT_TYPE); IPW_CMD(RTS_THRESHOLD); IPW_CMD(FRAG_THRESHOLD); IPW_CMD(POWER_MODE); IPW_CMD(WEP_KEY); IPW_CMD(TGI_TX_KEY); IPW_CMD(SCAN_REQUEST); IPW_CMD(SCAN_REQUEST_EXT); IPW_CMD(ASSOCIATE); IPW_CMD(SUPPORTED_RATES); IPW_CMD(SCAN_ABORT); IPW_CMD(TX_FLUSH); IPW_CMD(QOS_PARAMETERS); IPW_CMD(DINO_CONFIG); IPW_CMD(RSN_CAPABILITIES); IPW_CMD(RX_KEY); IPW_CMD(CARD_DISABLE); IPW_CMD(SEED_NUMBER); IPW_CMD(TX_POWER); IPW_CMD(COUNTRY_INFO); IPW_CMD(AIRONET_INFO); IPW_CMD(AP_TX_POWER); IPW_CMD(CCKM_INFO); IPW_CMD(CCX_VER_INFO); IPW_CMD(SET_CALIBRATION); IPW_CMD(SENSITIVITY_CALIB); IPW_CMD(RETRY_LIMIT); IPW_CMD(IPW_PRE_POWER_DOWN); IPW_CMD(VAP_BEACON_TEMPLATE); IPW_CMD(VAP_DTIM_PERIOD); IPW_CMD(EXT_SUPPORTED_RATES); IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT); IPW_CMD(VAP_QUIET_INTERVALS); IPW_CMD(VAP_CHANNEL_SWITCH); IPW_CMD(VAP_MANDATORY_CHANNELS); IPW_CMD(VAP_CELL_PWR_LIMIT); IPW_CMD(VAP_CF_PARAM_SET); IPW_CMD(VAP_SET_BEACONING_STATE); IPW_CMD(MEASUREMENT); IPW_CMD(POWER_CAPABILITY); IPW_CMD(SUPPORTED_CHANNELS); IPW_CMD(TPC_REPORT); IPW_CMD(WME_INFO); IPW_CMD(PRODUCTION_COMMAND); default: return "UNKNOWN"; } } #define HOST_COMPLETE_TIMEOUT HZ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) { int rc = 0; unsigned long flags; unsigned long now, end; spin_lock_irqsave(&priv->lock, flags); if (priv->status & STATUS_HCMD_ACTIVE) { IPW_ERROR("Failed to send %s: Already sending a command.\n", get_cmd_string(cmd->cmd)); spin_unlock_irqrestore(&priv->lock, flags); return -EAGAIN; } priv->status |= STATUS_HCMD_ACTIVE; if (priv->cmdlog) { priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies; priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd; priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len; memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param, cmd->len); priv->cmdlog[priv->cmdlog_pos].retcode = -1; } IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n", get_cmd_string(cmd->cmd), cmd->cmd, cmd->len, priv->status); #ifndef DEBUG_CMD_WEP_KEY if (cmd->cmd == IPW_CMD_WEP_KEY) IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n"); else #endif printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len); rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0); if (rc) { priv->status &= ~STATUS_HCMD_ACTIVE; IPW_ERROR("Failed to send %s: Reason %d\n", get_cmd_string(cmd->cmd), rc); spin_unlock_irqrestore(&priv->lock, flags); goto exit; } spin_unlock_irqrestore(&priv->lock, flags); now = jiffies; end = now + HOST_COMPLETE_TIMEOUT; again: rc = wait_event_interruptible_timeout(priv->wait_command_queue, !(priv-> status & STATUS_HCMD_ACTIVE), end - now); if (rc < 0) { now = jiffies; if (time_before(now, end)) goto again; rc = 0; } if (rc == 0) { spin_lock_irqsave(&priv->lock, flags); if (priv->status & STATUS_HCMD_ACTIVE) { IPW_ERROR("Failed to send %s: Command timed out.\n", get_cmd_string(cmd->cmd)); priv->status &= ~STATUS_HCMD_ACTIVE; spin_unlock_irqrestore(&priv->lock, flags); rc = -EIO; goto exit; } spin_unlock_irqrestore(&priv->lock, flags); } else rc = 0; if (priv->status & STATUS_RF_KILL_HW) { IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n", get_cmd_string(cmd->cmd)); rc = -EIO; goto exit; } exit: if (priv->cmdlog) { priv->cmdlog[priv->cmdlog_pos++].retcode = rc; priv->cmdlog_pos %= priv->cmdlog_len; } return rc; } static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command) { struct host_cmd cmd = { .cmd = command, }; return __ipw_send_cmd(priv, &cmd); } static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len, void *data) { struct host_cmd cmd = { .cmd = command, .len = len, .param = data, }; return __ipw_send_cmd(priv, &cmd); } static int ipw_send_host_complete(struct ipw_priv *priv) { if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE); } static int ipw_send_system_config(struct ipw_priv *priv) { return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG, sizeof(priv->sys_config), &priv->sys_config); } static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len) { if (!priv || !ssid) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE), ssid); } static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac) { if (!priv || !mac) { IPW_ERROR("Invalid args\n"); return -1; } IPW_DEBUG_INFO("%s: Setting MAC to %pM\n", priv->net_dev->name, mac); return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN, mac); } static void ipw_adapter_restart(void *adapter) { struct ipw_priv *priv = adapter; if (priv->status & STATUS_RF_KILL_MASK) return; ipw_down(priv); if (priv->assoc_network && (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS)) ipw_remove_current_network(priv); if (ipw_up(priv)) { IPW_ERROR("Failed to up device\n"); return; } } static void ipw_bg_adapter_restart(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, adapter_restart); mutex_lock(&priv->mutex); ipw_adapter_restart(priv); mutex_unlock(&priv->mutex); } static void ipw_abort_scan(struct ipw_priv *priv); #define IPW_SCAN_CHECK_WATCHDOG (5 * HZ) static void ipw_scan_check(void *data) { struct ipw_priv *priv = data; if (priv->status & STATUS_SCAN_ABORTING) { IPW_DEBUG_SCAN("Scan completion watchdog resetting " "adapter after (%dms).\n", jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG)); schedule_work(&priv->adapter_restart); } else if (priv->status & STATUS_SCANNING) { IPW_DEBUG_SCAN("Scan completion watchdog aborting scan " "after (%dms).\n", jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG)); ipw_abort_scan(priv); schedule_delayed_work(&priv->scan_check, HZ); } } static void ipw_bg_scan_check(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, scan_check.work); mutex_lock(&priv->mutex); ipw_scan_check(priv); mutex_unlock(&priv->mutex); } static int ipw_send_scan_request_ext(struct ipw_priv *priv, struct ipw_scan_request_ext *request) { return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT, sizeof(*request), request); } static int ipw_send_scan_abort(struct ipw_priv *priv) { if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT); } static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens) { struct ipw_sensitivity_calib calib = { .beacon_rssi_raw = cpu_to_le16(sens), }; return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib), &calib); } static int ipw_send_associate(struct ipw_priv *priv, struct ipw_associate *associate) { if (!priv || !associate) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(*associate), associate); } static int ipw_send_supported_rates(struct ipw_priv *priv, struct ipw_supported_rates *rates) { if (!priv || !rates) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates), rates); } static int ipw_set_random_seed(struct ipw_priv *priv) { u32 val; if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } get_random_bytes(&val, sizeof(val)); return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val); } static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off) { __le32 v = cpu_to_le32(phy_off); if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(v), &v); } static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power) { if (!priv || !power) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power), power); } static int ipw_set_tx_power(struct ipw_priv *priv) { const struct libipw_geo *geo = libipw_get_geo(priv->ieee); struct ipw_tx_power tx_power; s8 max_power; int i; memset(&tx_power, 0, sizeof(tx_power)); /* configure device for 'G' band */ tx_power.ieee_mode = IPW_G_MODE; tx_power.num_channels = geo->bg_channels; for (i = 0; i < geo->bg_channels; i++) { max_power = geo->bg[i].max_power; tx_power.channels_tx_power[i].channel_number = geo->bg[i].channel; tx_power.channels_tx_power[i].tx_power = max_power ? min(max_power, priv->tx_power) : priv->tx_power; } if (ipw_send_tx_power(priv, &tx_power)) return -EIO; /* configure device to also handle 'B' band */ tx_power.ieee_mode = IPW_B_MODE; if (ipw_send_tx_power(priv, &tx_power)) return -EIO; /* configure device to also handle 'A' band */ if (priv->ieee->abg_true) { tx_power.ieee_mode = IPW_A_MODE; tx_power.num_channels = geo->a_channels; for (i = 0; i < tx_power.num_channels; i++) { max_power = geo->a[i].max_power; tx_power.channels_tx_power[i].channel_number = geo->a[i].channel; tx_power.channels_tx_power[i].tx_power = max_power ? min(max_power, priv->tx_power) : priv->tx_power; } if (ipw_send_tx_power(priv, &tx_power)) return -EIO; } return 0; } static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts) { struct ipw_rts_threshold rts_threshold = { .rts_threshold = cpu_to_le16(rts), }; if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD, sizeof(rts_threshold), &rts_threshold); } static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag) { struct ipw_frag_threshold frag_threshold = { .frag_threshold = cpu_to_le16(frag), }; if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD, sizeof(frag_threshold), &frag_threshold); } static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode) { __le32 param; if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } /* If on battery, set to 3, if AC set to CAM, else user * level */ switch (mode) { case IPW_POWER_BATTERY: param = cpu_to_le32(IPW_POWER_INDEX_3); break; case IPW_POWER_AC: param = cpu_to_le32(IPW_POWER_MODE_CAM); break; default: param = cpu_to_le32(mode); break; } return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param), ¶m); } static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit) { struct ipw_retry_limit retry_limit = { .short_retry_limit = slimit, .long_retry_limit = llimit }; if (!priv) { IPW_ERROR("Invalid args\n"); return -1; } return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit), &retry_limit); } /* * The IPW device contains a Microwire compatible EEPROM that stores * various data like the MAC address. Usually the firmware has exclusive * access to the eeprom, but during device initialization (before the * device driver has sent the HostComplete command to the firmware) the * device driver has read access to the EEPROM by way of indirect addressing * through a couple of memory mapped registers. * * The following is a simplified implementation for pulling data out of the * the eeprom, along with some helper functions to find information in * the per device private data's copy of the eeprom. * * NOTE: To better understand how these functions work (i.e what is a chip * select and why do have to keep driving the eeprom clock?), read * just about any data sheet for a Microwire compatible EEPROM. */ /* write a 32 bit value into the indirect accessor register */ static inline void eeprom_write_reg(struct ipw_priv *p, u32 data) { ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data); /* the eeprom requires some time to complete the operation */ udelay(p->eeprom_delay); } /* perform a chip select operation */ static void eeprom_cs(struct ipw_priv *priv) { eeprom_write_reg(priv, 0); eeprom_write_reg(priv, EEPROM_BIT_CS); eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK); eeprom_write_reg(priv, EEPROM_BIT_CS); } /* perform a chip select operation */ static void eeprom_disable_cs(struct ipw_priv *priv) { eeprom_write_reg(priv, EEPROM_BIT_CS); eeprom_write_reg(priv, 0); eeprom_write_reg(priv, EEPROM_BIT_SK); } /* push a single bit down to the eeprom */ static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit) { int d = (bit ? EEPROM_BIT_DI : 0); eeprom_write_reg(p, EEPROM_BIT_CS | d); eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK); } /* push an opcode followed by an address down to the eeprom */ static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr) { int i; eeprom_cs(priv); eeprom_write_bit(priv, 1); eeprom_write_bit(priv, op & 2); eeprom_write_bit(priv, op & 1); for (i = 7; i >= 0; i--) { eeprom_write_bit(priv, addr & (1 << i)); } } /* pull 16 bits off the eeprom, one bit at a time */ static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr) { int i; u16 r = 0; /* Send READ Opcode */ eeprom_op(priv, EEPROM_CMD_READ, addr); /* Send dummy bit */ eeprom_write_reg(priv, EEPROM_BIT_CS); /* Read the byte off the eeprom one bit at a time */ for (i = 0; i < 16; i++) { u32 data = 0; eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK); eeprom_write_reg(priv, EEPROM_BIT_CS); data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS); r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0); } /* Send another dummy bit */ eeprom_write_reg(priv, 0); eeprom_disable_cs(priv); return r; } /* helper function for pulling the mac address out of the private */ /* data's copy of the eeprom data */ static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac) { memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6); } static void ipw_read_eeprom(struct ipw_priv *priv) { int i; __le16 *eeprom = (__le16 *) priv->eeprom; IPW_DEBUG_TRACE(">>\n"); /* read entire contents of eeprom into private buffer */ for (i = 0; i < 128; i++) eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i)); IPW_DEBUG_TRACE("<<\n"); } /* * Either the device driver (i.e. the host) or the firmware can * load eeprom data into the designated region in SRAM. If neither * happens then the FW will shutdown with a fatal error. * * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE * bit needs region of shared SRAM needs to be non-zero. */ static void ipw_eeprom_init_sram(struct ipw_priv *priv) { int i; IPW_DEBUG_TRACE(">>\n"); /* If the data looks correct, then copy it to our private copy. Otherwise let the firmware know to perform the operation on its own. */ if (priv->eeprom[EEPROM_VERSION] != 0) { IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n"); /* write the eeprom data to sram */ for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++) ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]); /* Do not load eeprom data on fatal error or suspend */ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0); } else { IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n"); /* Load eeprom data on fatal error or suspend */ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1); } IPW_DEBUG_TRACE("<<\n"); } static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count) { count >>= 2; if (!count) return; _ipw_write32(priv, IPW_AUTOINC_ADDR, start); while (count--) _ipw_write32(priv, IPW_AUTOINC_DATA, 0); } static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv) { ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL, CB_NUMBER_OF_ELEMENTS_SMALL * sizeof(struct command_block)); } static int ipw_fw_dma_enable(struct ipw_priv *priv) { /* start dma engine but no transfers yet */ IPW_DEBUG_FW(">> :\n"); /* Start the dma */ ipw_fw_dma_reset_command_blocks(priv); /* Write CB base address */ ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL); IPW_DEBUG_FW("<< :\n"); return 0; } static void ipw_fw_dma_abort(struct ipw_priv *priv) { u32 control = 0; IPW_DEBUG_FW(">> :\n"); /* set the Stop and Abort bit */ control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT; ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control); priv->sram_desc.last_cb_index = 0; IPW_DEBUG_FW("<<\n"); } static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index, struct command_block *cb) { u32 address = IPW_SHARED_SRAM_DMA_CONTROL + (sizeof(struct command_block) * index); IPW_DEBUG_FW(">> :\n"); ipw_write_indirect(priv, address, (u8 *) cb, (int)sizeof(struct command_block)); IPW_DEBUG_FW("<< :\n"); return 0; } static int ipw_fw_dma_kick(struct ipw_priv *priv) { u32 control = 0; u32 index = 0; IPW_DEBUG_FW(">> :\n"); for (index = 0; index < priv->sram_desc.last_cb_index; index++) ipw_fw_dma_write_command_block(priv, index, &priv->sram_desc.cb_list[index]); /* Enable the DMA in the CSR register */ ipw_clear_bit(priv, IPW_RESET_REG, IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER); /* Set the Start bit. */ control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START; ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control); IPW_DEBUG_FW("<< :\n"); return 0; } static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv) { u32 address; u32 register_value = 0; u32 cb_fields_address = 0; IPW_DEBUG_FW(">> :\n"); address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB); IPW_DEBUG_FW_INFO("Current CB is 0x%x\n", address); /* Read the DMA Controlor register */ register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL); IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x\n", register_value); /* Print the CB values */ cb_fields_address = address; register_value = ipw_read_reg32(priv, cb_fields_address); IPW_DEBUG_FW_INFO("Current CB Control Field is 0x%x\n", register_value); cb_fields_address += sizeof(u32); register_value = ipw_read_reg32(priv, cb_fields_address); IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x\n", register_value); cb_fields_address += sizeof(u32); register_value = ipw_read_reg32(priv, cb_fields_address); IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x\n", register_value); cb_fields_address += sizeof(u32); register_value = ipw_read_reg32(priv, cb_fields_address); IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x\n", register_value); IPW_DEBUG_FW(">> :\n"); } static int ipw_fw_dma_command_block_index(struct ipw_priv *priv) { u32 current_cb_address = 0; u32 current_cb_index = 0; IPW_DEBUG_FW("<< :\n"); current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB); current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) / sizeof(struct command_block); IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X\n", current_cb_index, current_cb_address); IPW_DEBUG_FW(">> :\n"); return current_cb_index; } static int ipw_fw_dma_add_command_block(struct ipw_priv *priv, u32 src_address, u32 dest_address, u32 length, int interrupt_enabled, int is_last) { u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC | CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG | CB_DEST_SIZE_LONG; struct command_block *cb; u32 last_cb_element = 0; IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n", src_address, dest_address, length); if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL) return -1; last_cb_element = priv->sram_desc.last_cb_index; cb = &priv->sram_desc.cb_list[last_cb_element]; priv->sram_desc.last_cb_index++; /* Calculate the new CB control word */ if (interrupt_enabled) control |= CB_INT_ENABLED; if (is_last) control |= CB_LAST_VALID; control |= length; /* Calculate the CB Element's checksum value */ cb->status = control ^ src_address ^ dest_address; /* Copy the Source and Destination addresses */ cb->dest_addr = dest_address; cb->source_addr = src_address; /* Copy the Control Word last */ cb->control = control; return 0; } static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, dma_addr_t *src_address, int nr, u32 dest_address, u32 len) { int ret, i; u32 size; IPW_DEBUG_FW(">>\n"); IPW_DEBUG_FW_INFO("nr=%d dest_address=0x%x len=0x%x\n", nr, dest_address, len); for (i = 0; i < nr; i++) { size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH); ret = ipw_fw_dma_add_command_block(priv, src_address[i], dest_address + i * CB_MAX_LENGTH, size, 0, 0); if (ret) { IPW_DEBUG_FW_INFO(": Failed\n"); return -1; } else IPW_DEBUG_FW_INFO(": Added new cb\n"); } IPW_DEBUG_FW("<<\n"); return 0; } static int ipw_fw_dma_wait(struct ipw_priv *priv) { u32 current_index = 0, previous_index; u32 watchdog = 0; IPW_DEBUG_FW(">> :\n"); current_index = ipw_fw_dma_command_block_index(priv); IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n", (int)priv->sram_desc.last_cb_index); while (current_index < priv->sram_desc.last_cb_index) { udelay(50); previous_index = current_index; current_index = ipw_fw_dma_command_block_index(priv); if (previous_index < current_index) { watchdog = 0; continue; } if (++watchdog > 400) { IPW_DEBUG_FW_INFO("Timeout\n"); ipw_fw_dma_dump_command_block(priv); ipw_fw_dma_abort(priv); return -1; } } ipw_fw_dma_abort(priv); /*Disable the DMA in the CSR register */ ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER); IPW_DEBUG_FW("<< dmaWaitSync\n"); return 0; } static void ipw_remove_current_network(struct ipw_priv *priv) { struct list_head *element, *safe; struct libipw_network *network = NULL; unsigned long flags; spin_lock_irqsave(&priv->ieee->lock, flags); list_for_each_safe(element, safe, &priv->ieee->network_list) { network = list_entry(element, struct libipw_network, list); if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) { list_del(element); list_add_tail(&network->list, &priv->ieee->network_free_list); } } spin_unlock_irqrestore(&priv->ieee->lock, flags); } /** * Check that card is still alive. * Reads debug register from domain0. * If card is present, pre-defined value should * be found there. * * @param priv * @return 1 if card is present, 0 otherwise */ static inline int ipw_alive(struct ipw_priv *priv) { return ipw_read32(priv, 0x90) == 0xd55555d5; } /* timeout in msec, attempted in 10-msec quanta */ static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask, int timeout) { int i = 0; do { if ((ipw_read32(priv, addr) & mask) == mask) return i; mdelay(10); i += 10; } while (i < timeout); return -ETIME; } /* These functions load the firmware and micro code for the operation of * the ipw hardware. It assumes the buffer has all the bits for the * image and the caller is handling the memory allocation and clean up. */ static int ipw_stop_master(struct ipw_priv *priv) { int rc; IPW_DEBUG_TRACE(">>\n"); /* stop master. typical delay - 0 */ ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER); /* timeout is in msec, polled in 10-msec quanta */ rc = ipw_poll_bit(priv, IPW_RESET_REG, IPW_RESET_REG_MASTER_DISABLED, 100); if (rc < 0) { IPW_ERROR("wait for stop master failed after 100ms\n"); return -1; } IPW_DEBUG_INFO("stop master %dms\n", rc); return rc; } static void ipw_arc_release(struct ipw_priv *priv) { IPW_DEBUG_TRACE(">>\n"); mdelay(5); ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET); /* no one knows timing, for safety add some delay */ mdelay(5); } struct fw_chunk { __le32 address; __le32 length; }; static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len) { int rc = 0, i, addr; u8 cr = 0; __le16 *image; image = (__le16 *) data; IPW_DEBUG_TRACE(">>\n"); rc = ipw_stop_master(priv); if (rc < 0) return rc; for (addr = IPW_SHARED_LOWER_BOUND; addr < IPW_REGISTER_DOMAIN1_END; addr += 4) { ipw_write32(priv, addr, 0); } /* no ucode (yet) */ memset(&priv->dino_alive, 0, sizeof(priv->dino_alive)); /* destroy DMA queues */ /* reset sequence */ ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON); ipw_arc_release(priv); ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF); mdelay(1); /* reset PHY */ ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN); mdelay(1); ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0); mdelay(1); /* enable ucode store */ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0); ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS); mdelay(1); /* write ucode */ /** * @bug * Do NOT set indirect address register once and then * store data to indirect data register in the loop. * It seems very reasonable, but in this case DINO do not * accept ucode. It is essential to set address each time. */ /* load new ipw uCode */ for (i = 0; i < len / 2; i++) ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE, le16_to_cpu(image[i])); /* enable DINO */ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0); ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM); /* this is where the igx / win driver deveates from the VAP driver. */ /* wait for alive response */ for (i = 0; i < 100; i++) { /* poll for incoming data */ cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS); if (cr & DINO_RXFIFO_DATA) break; mdelay(1); } if (cr & DINO_RXFIFO_DATA) { /* alive_command_responce size is NOT multiple of 4 */ __le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4]; for (i = 0; i < ARRAY_SIZE(response_buffer); i++) response_buffer[i] = cpu_to_le32(ipw_read_reg32(priv, IPW_BASEBAND_RX_FIFO_READ)); memcpy(&priv->dino_alive, response_buffer, sizeof(priv->dino_alive)); if (priv->dino_alive.alive_command == 1 && priv->dino_alive.ucode_valid == 1) { rc = 0; IPW_DEBUG_INFO ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) " "of %02d/%02d/%02d %02d:%02d\n", priv->dino_alive.software_revision, priv->dino_alive.software_revision, priv->dino_alive.device_identifier, priv->dino_alive.device_identifier, priv->dino_alive.time_stamp[0], priv->dino_alive.time_stamp[1], priv->dino_alive.time_stamp[2], priv->dino_alive.time_stamp[3], priv->dino_alive.time_stamp[4]); } else { IPW_DEBUG_INFO("Microcode is not alive\n"); rc = -EINVAL; } } else { IPW_DEBUG_INFO("No alive response from DINO\n"); rc = -ETIME; } /* disable DINO, otherwise for some reason firmware have problem getting alive resp. */ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0); return rc; } static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) { int ret = -1; int offset = 0; struct fw_chunk *chunk; int total_nr = 0; int i; struct pci_pool *pool; void **virts; dma_addr_t *phys; IPW_DEBUG_TRACE("<< :\n"); virts = kmalloc(sizeof(void *) * CB_NUMBER_OF_ELEMENTS_SMALL, GFP_KERNEL); if (!virts) return -ENOMEM; phys = kmalloc(sizeof(dma_addr_t) * CB_NUMBER_OF_ELEMENTS_SMALL, GFP_KERNEL); if (!phys) { kfree(virts); return -ENOMEM; } pool = pci_pool_create("ipw2200", priv->pci_dev, CB_MAX_LENGTH, 0, 0); if (!pool) { IPW_ERROR("pci_pool_create failed\n"); kfree(phys); kfree(virts); return -ENOMEM; } /* Start the Dma */ ret = ipw_fw_dma_enable(priv); /* the DMA is already ready this would be a bug. */ BUG_ON(priv->sram_desc.last_cb_index > 0); do { u32 chunk_len; u8 *start; int size; int nr = 0; chunk = (struct fw_chunk *)(data + offset); offset += sizeof(struct fw_chunk); chunk_len = le32_to_cpu(chunk->length); start = data + offset; nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH; for (i = 0; i < nr; i++) { virts[total_nr] = pci_pool_alloc(pool, GFP_KERNEL, &phys[total_nr]); if (!virts[total_nr]) { ret = -ENOMEM; goto out; } size = min_t(u32, chunk_len - i * CB_MAX_LENGTH, CB_MAX_LENGTH); memcpy(virts[total_nr], start, size); start += size; total_nr++; /* We don't support fw chunk larger than 64*8K */ BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL); } /* build DMA packet and queue up for sending */ /* dma to chunk->address, the chunk->length bytes from data + * offeset*/ /* Dma loading */ ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr], nr, le32_to_cpu(chunk->address), chunk_len); if (ret) { IPW_DEBUG_INFO("dmaAddBuffer Failed\n"); goto out; } offset += chunk_len; } while (offset < len); /* Run the DMA and wait for the answer */ ret = ipw_fw_dma_kick(priv); if (ret) { IPW_ERROR("dmaKick Failed\n"); goto out; } ret = ipw_fw_dma_wait(priv); if (ret) { IPW_ERROR("dmaWaitSync Failed\n"); goto out; } out: for (i = 0; i < total_nr; i++) pci_pool_free(pool, virts[i], phys[i]); pci_pool_destroy(pool); kfree(phys); kfree(virts); return ret; } /* stop nic */ static int ipw_stop_nic(struct ipw_priv *priv) { int rc = 0; /* stop */ ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER); rc = ipw_poll_bit(priv, IPW_RESET_REG, IPW_RESET_REG_MASTER_DISABLED, 500); if (rc < 0) { IPW_ERROR("wait for reg master disabled failed after 500ms\n"); return rc; } ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET); return rc; } static void ipw_start_nic(struct ipw_priv *priv) { IPW_DEBUG_TRACE(">>\n"); /* prvHwStartNic release ARC */ ipw_clear_bit(priv, IPW_RESET_REG, IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER | CBD_RESET_REG_PRINCETON_RESET); /* enable power management */ ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY); IPW_DEBUG_TRACE("<<\n"); } static int ipw_init_nic(struct ipw_priv *priv) { int rc; IPW_DEBUG_TRACE(">>\n"); /* reset */ /*prvHwInitNic */ /* set "initialization complete" bit to move adapter to D0 state */ ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE); /* low-level PLL activation */ ipw_write32(priv, IPW_READ_INT_REGISTER, IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER); /* wait for clock stabilization */ rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_CLOCK_READY, 250); if (rc < 0) IPW_DEBUG_INFO("FAILED wait for clock stablization\n"); /* assert SW reset */ ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET); udelay(10); /* set "initialization complete" bit to move adapter to D0 state */ ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE); IPW_DEBUG_TRACE(">>\n"); return 0; } /* Call this function from process context, it will sleep in request_firmware. * Probe is an ok place to call this from. */ static int ipw_reset_nic(struct ipw_priv *priv) { int rc = 0; unsigned long flags; IPW_DEBUG_TRACE(">>\n"); rc = ipw_init_nic(priv); spin_lock_irqsave(&priv->lock, flags); /* Clear the 'host command active' bit... */ priv->status &= ~STATUS_HCMD_ACTIVE; wake_up_interruptible(&priv->wait_command_queue); priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING); wake_up_interruptible(&priv->wait_state); spin_unlock_irqrestore(&priv->lock, flags); IPW_DEBUG_TRACE("<<\n"); return rc; } struct ipw_fw { __le32 ver; __le32 boot_size; __le32 ucode_size; __le32 fw_size; u8 data[0]; }; static int ipw_get_fw(struct ipw_priv *priv, const struct firmware **raw, const char *name) { struct ipw_fw *fw; int rc; /* ask firmware_class module to get the boot firmware off disk */ rc = request_firmware(raw, name, &priv->pci_dev->dev); if (rc < 0) { IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc); return rc; } if ((*raw)->size < sizeof(*fw)) { IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size); return -EINVAL; } fw = (void *)(*raw)->data; if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) + le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) { IPW_ERROR("%s is too small or corrupt (%zd)\n", name, (*raw)->size); return -EINVAL; } IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n", name, le32_to_cpu(fw->ver) >> 16, le32_to_cpu(fw->ver) & 0xff, (*raw)->size - sizeof(*fw)); return 0; } #define IPW_RX_BUF_SIZE (3000) static void ipw_rx_queue_reset(struct ipw_priv *priv, struct ipw_rx_queue *rxq) { unsigned long flags; int i; spin_lock_irqsave(&rxq->lock, flags); INIT_LIST_HEAD(&rxq->rx_free); INIT_LIST_HEAD(&rxq->rx_used); /* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { /* In the reset function, these buffers may have been allocated * to an SKB, so we need to unmap and free potential storage */ if (rxq->pool[i].skb != NULL) { pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr, IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); dev_kfree_skb(rxq->pool[i].skb); rxq->pool[i].skb = NULL; } list_add_tail(&rxq->pool[i].list, &rxq->rx_used); } /* Set us so that we have processed and used all buffers, but have * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); } #ifdef CONFIG_PM static int fw_loaded = 0; static const struct firmware *raw = NULL; static void free_firmware(void) { if (fw_loaded) { release_firmware(raw); raw = NULL; fw_loaded = 0; } } #else #define free_firmware() do {} while (0) #endif static int ipw_load(struct ipw_priv *priv) { #ifndef CONFIG_PM const struct firmware *raw = NULL; #endif struct ipw_fw *fw; u8 *boot_img, *ucode_img, *fw_img; u8 *name = NULL; int rc = 0, retries = 3; switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: name = "ipw2200-ibss.fw"; break; #ifdef CONFIG_IPW2200_MONITOR case IW_MODE_MONITOR: name = "ipw2200-sniffer.fw"; break; #endif case IW_MODE_INFRA: name = "ipw2200-bss.fw"; break; } if (!name) { rc = -EINVAL; goto error; } #ifdef CONFIG_PM if (!fw_loaded) { #endif rc = ipw_get_fw(priv, &raw, name); if (rc < 0) goto error; #ifdef CONFIG_PM } #endif fw = (void *)raw->data; boot_img = &fw->data[0]; ucode_img = &fw->data[le32_to_cpu(fw->boot_size)]; fw_img = &fw->data[le32_to_cpu(fw->boot_size) + le32_to_cpu(fw->ucode_size)]; if (rc < 0) goto error; if (!priv->rxq) priv->rxq = ipw_rx_queue_alloc(priv); else ipw_rx_queue_reset(priv, priv->rxq); if (!priv->rxq) { IPW_ERROR("Unable to initialize Rx queue\n"); goto error; } retry: /* Ensure interrupts are disabled */ ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); priv->status &= ~STATUS_INT_ENABLED; /* ack pending interrupts */ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); ipw_stop_nic(priv); rc = ipw_reset_nic(priv); if (rc < 0) { IPW_ERROR("Unable to reset NIC\n"); goto error; } ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND, IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND); /* DMA the initial boot firmware into the device */ rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size)); if (rc < 0) { IPW_ERROR("Unable to load boot firmware: %d\n", rc); goto error; } /* kick start the device */ ipw_start_nic(priv); /* wait for the device to finish its initial startup sequence */ rc = ipw_poll_bit(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500); if (rc < 0) { IPW_ERROR("device failed to boot initial fw image\n"); goto error; } IPW_DEBUG_INFO("initial device response after %dms\n", rc); /* ack fw init done interrupt */ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); /* DMA the ucode into the device */ rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size)); if (rc < 0) { IPW_ERROR("Unable to load ucode: %d\n", rc); goto error; } /* stop nic */ ipw_stop_nic(priv); /* DMA bss firmware into the device */ rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size)); if (rc < 0) { IPW_ERROR("Unable to load firmware: %d\n", rc); goto error; } #ifdef CONFIG_PM fw_loaded = 1; #endif ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0); rc = ipw_queue_reset(priv); if (rc < 0) { IPW_ERROR("Unable to initialize queues\n"); goto error; } /* Ensure interrupts are disabled */ ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); /* ack pending interrupts */ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); /* kick start the device */ ipw_start_nic(priv); if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) { if (retries > 0) { IPW_WARNING("Parity error. Retrying init.\n"); retries--; goto retry; } IPW_ERROR("TODO: Handle parity error -- schedule restart?\n"); rc = -EIO; goto error; } /* wait for the device */ rc = ipw_poll_bit(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500); if (rc < 0) { IPW_ERROR("device failed to start within 500ms\n"); goto error; } IPW_DEBUG_INFO("device response after %dms\n", rc); /* ack fw init done interrupt */ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); /* read eeprom data */ priv->eeprom_delay = 1; ipw_read_eeprom(priv); /* initialize the eeprom region of sram */ ipw_eeprom_init_sram(priv); /* enable interrupts */ ipw_enable_interrupts(priv); /* Ensure our queue has valid packets */ ipw_rx_queue_replenish(priv); ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read); /* ack pending interrupts */ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); #ifndef CONFIG_PM release_firmware(raw); #endif return 0; error: if (priv->rxq) { ipw_rx_queue_free(priv, priv->rxq); priv->rxq = NULL; } ipw_tx_queue_free(priv); release_firmware(raw); #ifdef CONFIG_PM fw_loaded = 0; raw = NULL; #endif return rc; } /** * DMA services * * Theory of operation * * A queue is a circular buffers with 'Read' and 'Write' pointers. * 2 empty entries always kept in the buffer to protect from overflow. * * For Tx queue, there are low mark and high mark limits. If, after queuing * the packet for Tx, free space become < low mark, Tx queue stopped. When * reclaiming packets (on 'tx done IRQ), if free space become > high mark, * Tx queue resumed. * * The IPW operates with six queues, one receive queue in the device's * sram, one transmit queue for sending commands to the device firmware, * and four transmit queues for data. * * The four transmit queues allow for performing quality of service (qos) * transmissions as per the 802.11 protocol. Currently Linux does not * provide a mechanism to the user for utilizing prioritized queues, so * we only utilize the first data transmit queue (queue1). */ /** * Driver allocates buffers of this size for Rx */ /** * ipw_rx_queue_space - Return number of free slots available in queue. */ static int ipw_rx_queue_space(const struct ipw_rx_queue *q) { int s = q->read - q->write; if (s <= 0) s += RX_QUEUE_SIZE; /* keep some buffer to not confuse full and empty queue */ s -= 2; if (s < 0) s = 0; return s; } static inline int ipw_tx_queue_space(const struct clx2_queue *q) { int s = q->last_used - q->first_empty; if (s <= 0) s += q->n_bd; s -= 2; /* keep some reserve to not confuse empty and full situations */ if (s < 0) s = 0; return s; } static inline int ipw_queue_inc_wrap(int index, int n_bd) { return (++index == n_bd) ? 0 : index; } /** * Initialize common DMA queue structure * * @param q queue to init * @param count Number of BD's to allocate. Should be power of 2 * @param read_register Address for 'read' register * (not offset within BAR, full address) * @param write_register Address for 'write' register * (not offset within BAR, full address) * @param base_register Address for 'base' register * (not offset within BAR, full address) * @param size Address for 'size' register * (not offset within BAR, full address) */ static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q, int count, u32 read, u32 write, u32 base, u32 size) { q->n_bd = count; q->low_mark = q->n_bd / 4; if (q->low_mark < 4) q->low_mark = 4; q->high_mark = q->n_bd / 8; if (q->high_mark < 2) q->high_mark = 2; q->first_empty = q->last_used = 0; q->reg_r = read; q->reg_w = write; ipw_write32(priv, base, q->dma_addr); ipw_write32(priv, size, count); ipw_write32(priv, read, 0); ipw_write32(priv, write, 0); _ipw_read32(priv, 0x90); } static int ipw_queue_tx_init(struct ipw_priv *priv, struct clx2_tx_queue *q, int count, u32 read, u32 write, u32 base, u32 size) { struct pci_dev *dev = priv->pci_dev; q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL); if (!q->txb) { IPW_ERROR("vmalloc for auxiliary BD structures failed\n"); return -ENOMEM; } q->bd = pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr); if (!q->bd) { IPW_ERROR("pci_alloc_consistent(%zd) failed\n", sizeof(q->bd[0]) * count); kfree(q->txb); q->txb = NULL; return -ENOMEM; } ipw_queue_init(priv, &q->q, count, read, write, base, size); return 0; } /** * Free one TFD, those at index [txq->q.last_used]. * Do NOT advance any indexes * * @param dev * @param txq */ static void ipw_queue_tx_free_tfd(struct ipw_priv *priv, struct clx2_tx_queue *txq) { struct tfd_frame *bd = &txq->bd[txq->q.last_used]; struct pci_dev *dev = priv->pci_dev; int i; /* classify bd */ if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE) /* nothing to cleanup after for host commands */ return; /* sanity check */ if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) { IPW_ERROR("Too many chunks: %i\n", le32_to_cpu(bd->u.data.num_chunks)); /** @todo issue fatal error, it is quite serious situation */ return; } /* unmap chunks if any */ for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) { pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]), le16_to_cpu(bd->u.data.chunk_len[i]), PCI_DMA_TODEVICE); if (txq->txb[txq->q.last_used]) { libipw_txb_free(txq->txb[txq->q.last_used]); txq->txb[txq->q.last_used] = NULL; } } } /** * Deallocate DMA queue. * * Empty queue by removing and destroying all BD's. * Free all buffers. * * @param dev * @param q */ static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq) { struct clx2_queue *q = &txq->q; struct pci_dev *dev = priv->pci_dev; if (q->n_bd == 0) return; /* first, empty all BD's */ for (; q->first_empty != q->last_used; q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) { ipw_queue_tx_free_tfd(priv, txq); } /* free buffers belonging to queue itself */ pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd, q->dma_addr); kfree(txq->txb); /* 0 fill whole structure */ memset(txq, 0, sizeof(*txq)); } /** * Destroy all DMA queues and structures * * @param priv */ static void ipw_tx_queue_free(struct ipw_priv *priv) { /* Tx CMD queue */ ipw_queue_tx_free(priv, &priv->txq_cmd); /* Tx queues */ ipw_queue_tx_free(priv, &priv->txq[0]); ipw_queue_tx_free(priv, &priv->txq[1]); ipw_queue_tx_free(priv, &priv->txq[2]); ipw_queue_tx_free(priv, &priv->txq[3]); } static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid) { /* First 3 bytes are manufacturer */ bssid[0] = priv->mac_addr[0]; bssid[1] = priv->mac_addr[1]; bssid[2] = priv->mac_addr[2]; /* Last bytes are random */ get_random_bytes(&bssid[3], ETH_ALEN - 3); bssid[0] &= 0xfe; /* clear multicast bit */ bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */ } static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid) { struct ipw_station_entry entry; int i; for (i = 0; i < priv->num_stations; i++) { if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) { /* Another node is active in network */ priv->missed_adhoc_beacons = 0; if (!(priv->config & CFG_STATIC_CHANNEL)) /* when other nodes drop out, we drop out */ priv->config &= ~CFG_ADHOC_PERSIST; return i; } } if (i == MAX_STATIONS) return IPW_INVALID_STATION; IPW_DEBUG_SCAN("Adding AdHoc station: %pM\n", bssid); entry.reserved = 0; entry.support_mode = 0; memcpy(entry.mac_addr, bssid, ETH_ALEN); memcpy(priv->stations[i], bssid, ETH_ALEN); ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry), &entry, sizeof(entry)); priv->num_stations++; return i; } static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid) { int i; for (i = 0; i < priv->num_stations; i++) if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) return i; return IPW_INVALID_STATION; } static void ipw_send_disassociate(struct ipw_priv *priv, int quiet) { int err; if (priv->status & STATUS_ASSOCIATING) { IPW_DEBUG_ASSOC("Disassociating while associating.\n"); schedule_work(&priv->disassociate); return; } if (!(priv->status & STATUS_ASSOCIATED)) { IPW_DEBUG_ASSOC("Disassociating while not associated.\n"); return; } IPW_DEBUG_ASSOC("Disassocation attempt from %pM " "on channel %d.\n", priv->assoc_request.bssid, priv->assoc_request.channel); priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED); priv->status |= STATUS_DISASSOCIATING; if (quiet) priv->assoc_request.assoc_type = HC_DISASSOC_QUIET; else priv->assoc_request.assoc_type = HC_DISASSOCIATE; err = ipw_send_associate(priv, &priv->assoc_request); if (err) { IPW_DEBUG_HC("Attempt to send [dis]associate command " "failed.\n"); return; } } static int ipw_disassociate(void *data) { struct ipw_priv *priv = data; if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) return 0; ipw_send_disassociate(data, 0); netif_carrier_off(priv->net_dev); return 1; } static void ipw_bg_disassociate(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, disassociate); mutex_lock(&priv->mutex); ipw_disassociate(priv); mutex_unlock(&priv->mutex); } static void ipw_system_config(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, system_config); #ifdef CONFIG_IPW2200_PROMISCUOUS if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) { priv->sys_config.accept_all_data_frames = 1; priv->sys_config.accept_non_directed_frames = 1; priv->sys_config.accept_all_mgmt_bcpr = 1; priv->sys_config.accept_all_mgmt_frames = 1; } #endif ipw_send_system_config(priv); } struct ipw_status_code { u16 status; const char *reason; }; static const struct ipw_status_code ipw_status_codes[] = { {0x00, "Successful"}, {0x01, "Unspecified failure"}, {0x0A, "Cannot support all requested capabilities in the " "Capability information field"}, {0x0B, "Reassociation denied due to inability to confirm that " "association exists"}, {0x0C, "Association denied due to reason outside the scope of this " "standard"}, {0x0D, "Responding station does not support the specified authentication " "algorithm"}, {0x0E, "Received an Authentication frame with authentication sequence " "transaction sequence number out of expected sequence"}, {0x0F, "Authentication rejected because of challenge failure"}, {0x10, "Authentication rejected due to timeout waiting for next " "frame in sequence"}, {0x11, "Association denied because AP is unable to handle additional " "associated stations"}, {0x12, "Association denied due to requesting station not supporting all " "of the datarates in the BSSBasicServiceSet Parameter"}, {0x13, "Association denied due to requesting station not supporting " "short preamble operation"}, {0x14, "Association denied due to requesting station not supporting " "PBCC encoding"}, {0x15, "Association denied due to requesting station not supporting " "channel agility"}, {0x19, "Association denied due to requesting station not supporting " "short slot operation"}, {0x1A, "Association denied due to requesting station not supporting " "DSSS-OFDM operation"}, {0x28, "Invalid Information Element"}, {0x29, "Group Cipher is not valid"}, {0x2A, "Pairwise Cipher is not valid"}, {0x2B, "AKMP is not valid"}, {0x2C, "Unsupported RSN IE version"}, {0x2D, "Invalid RSN IE Capabilities"}, {0x2E, "Cipher suite is rejected per security policy"}, }; static const char *ipw_get_status_code(u16 status) { int i; for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++) if (ipw_status_codes[i].status == (status & 0xff)) return ipw_status_codes[i].reason; return "Unknown status value."; } static void inline average_init(struct average *avg) { memset(avg, 0, sizeof(*avg)); } #define DEPTH_RSSI 8 #define DEPTH_NOISE 16 static s16 exponential_average(s16 prev_avg, s16 val, u8 depth) { return ((depth-1)*prev_avg + val)/depth; } static void average_add(struct average *avg, s16 val) { avg->sum -= avg->entries[avg->pos]; avg->sum += val; avg->entries[avg->pos++] = val; if (unlikely(avg->pos == AVG_ENTRIES)) { avg->init = 1; avg->pos = 0; } } static s16 average_value(struct average *avg) { if (!unlikely(avg->init)) { if (avg->pos) return avg->sum / avg->pos; return 0; } return avg->sum / AVG_ENTRIES; } static void ipw_reset_stats(struct ipw_priv *priv) { u32 len = sizeof(u32); priv->quality = 0; average_init(&priv->average_missed_beacons); priv->exp_avg_rssi = -60; priv->exp_avg_noise = -85 + 0x100; priv->last_rate = 0; priv->last_missed_beacons = 0; priv->last_rx_packets = 0; priv->last_tx_packets = 0; priv->last_tx_failures = 0; /* Firmware managed, reset only when NIC is restarted, so we have to * normalize on the current value */ ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &priv->last_rx_err, &len); ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &priv->last_tx_failures, &len); /* Driver managed, reset with each association */ priv->missed_adhoc_beacons = 0; priv->missed_beacons = 0; priv->tx_packets = 0; priv->rx_packets = 0; } static u32 ipw_get_max_rate(struct ipw_priv *priv) { u32 i = 0x80000000; u32 mask = priv->rates_mask; /* If currently associated in B mode, restrict the maximum * rate match to B rates */ if (priv->assoc_request.ieee_mode == IPW_B_MODE) mask &= LIBIPW_CCK_RATES_MASK; /* TODO: Verify that the rate is supported by the current rates * list. */ while (i && !(mask & i)) i >>= 1; switch (i) { case LIBIPW_CCK_RATE_1MB_MASK: return 1000000; case LIBIPW_CCK_RATE_2MB_MASK: return 2000000; case LIBIPW_CCK_RATE_5MB_MASK: return 5500000; case LIBIPW_OFDM_RATE_6MB_MASK: return 6000000; case LIBIPW_OFDM_RATE_9MB_MASK: return 9000000; case LIBIPW_CCK_RATE_11MB_MASK: return 11000000; case LIBIPW_OFDM_RATE_12MB_MASK: return 12000000; case LIBIPW_OFDM_RATE_18MB_MASK: return 18000000; case LIBIPW_OFDM_RATE_24MB_MASK: return 24000000; case LIBIPW_OFDM_RATE_36MB_MASK: return 36000000; case LIBIPW_OFDM_RATE_48MB_MASK: return 48000000; case LIBIPW_OFDM_RATE_54MB_MASK: return 54000000; } if (priv->ieee->mode == IEEE_B) return 11000000; else return 54000000; } static u32 ipw_get_current_rate(struct ipw_priv *priv) { u32 rate, len = sizeof(rate); int err; if (!(priv->status & STATUS_ASSOCIATED)) return 0; if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) { err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate, &len); if (err) { IPW_DEBUG_INFO("failed querying ordinals.\n"); return 0; } } else return ipw_get_max_rate(priv); switch (rate) { case IPW_TX_RATE_1MB: return 1000000; case IPW_TX_RATE_2MB: return 2000000; case IPW_TX_RATE_5MB: return 5500000; case IPW_TX_RATE_6MB: return 6000000; case IPW_TX_RATE_9MB: return 9000000; case IPW_TX_RATE_11MB: return 11000000; case IPW_TX_RATE_12MB: return 12000000; case IPW_TX_RATE_18MB: return 18000000; case IPW_TX_RATE_24MB: return 24000000; case IPW_TX_RATE_36MB: return 36000000; case IPW_TX_RATE_48MB: return 48000000; case IPW_TX_RATE_54MB: return 54000000; } return 0; } #define IPW_STATS_INTERVAL (2 * HZ) static void ipw_gather_stats(struct ipw_priv *priv) { u32 rx_err, rx_err_delta, rx_packets_delta; u32 tx_failures, tx_failures_delta, tx_packets_delta; u32 missed_beacons_percent, missed_beacons_delta; u32 quality = 0; u32 len = sizeof(u32); s16 rssi; u32 beacon_quality, signal_quality, tx_quality, rx_quality, rate_quality; u32 max_rate; if (!(priv->status & STATUS_ASSOCIATED)) { priv->quality = 0; return; } /* Update the statistics */ ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS, &priv->missed_beacons, &len); missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons; priv->last_missed_beacons = priv->missed_beacons; if (priv->assoc_request.beacon_interval) { missed_beacons_percent = missed_beacons_delta * (HZ * le16_to_cpu(priv->assoc_request.beacon_interval)) / (IPW_STATS_INTERVAL * 10); } else { missed_beacons_percent = 0; } average_add(&priv->average_missed_beacons, missed_beacons_percent); ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len); rx_err_delta = rx_err - priv->last_rx_err; priv->last_rx_err = rx_err; ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len); tx_failures_delta = tx_failures - priv->last_tx_failures; priv->last_tx_failures = tx_failures; rx_packets_delta = priv->rx_packets - priv->last_rx_packets; priv->last_rx_packets = priv->rx_packets; tx_packets_delta = priv->tx_packets - priv->last_tx_packets; priv->last_tx_packets = priv->tx_packets; /* Calculate quality based on the following: * * Missed beacon: 100% = 0, 0% = 70% missed * Rate: 60% = 1Mbs, 100% = Max * Rx and Tx errors represent a straight % of total Rx/Tx * RSSI: 100% = > -50, 0% = < -80 * Rx errors: 100% = 0, 0% = 50% missed * * The lowest computed quality is used. * */ #define BEACON_THRESHOLD 5 beacon_quality = 100 - missed_beacons_percent; if (beacon_quality < BEACON_THRESHOLD) beacon_quality = 0; else beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 / (100 - BEACON_THRESHOLD); IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n", beacon_quality, missed_beacons_percent); priv->last_rate = ipw_get_current_rate(priv); max_rate = ipw_get_max_rate(priv); rate_quality = priv->last_rate * 40 / max_rate + 60; IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n", rate_quality, priv->last_rate / 1000000); if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta) rx_quality = 100 - (rx_err_delta * 100) / (rx_packets_delta + rx_err_delta); else rx_quality = 100; IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n", rx_quality, rx_err_delta, rx_packets_delta); if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta) tx_quality = 100 - (tx_failures_delta * 100) / (tx_packets_delta + tx_failures_delta); else tx_quality = 100; IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n", tx_quality, tx_failures_delta, tx_packets_delta); rssi = priv->exp_avg_rssi; signal_quality = (100 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) - (priv->ieee->perfect_rssi - rssi) * (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) + 62 * (priv->ieee->perfect_rssi - rssi))) / ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi)); if (signal_quality > 100) signal_quality = 100; else if (signal_quality < 1) signal_quality = 0; IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n", signal_quality, rssi); quality = min(rx_quality, signal_quality); quality = min(tx_quality, quality); quality = min(rate_quality, quality); quality = min(beacon_quality, quality); if (quality == beacon_quality) IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n", quality); if (quality == rate_quality) IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n", quality); if (quality == tx_quality) IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n", quality); if (quality == rx_quality) IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n", quality); if (quality == signal_quality) IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n", quality); priv->quality = quality; schedule_delayed_work(&priv->gather_stats, IPW_STATS_INTERVAL); } static void ipw_bg_gather_stats(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, gather_stats.work); mutex_lock(&priv->mutex); ipw_gather_stats(priv); mutex_unlock(&priv->mutex); } /* Missed beacon behavior: * 1st missed -> roaming_threshold, just wait, don't do any scan/roam. * roaming_threshold -> disassociate_threshold, scan and roam for better signal. * Above disassociate threshold, give up and stop scanning. * Roaming is disabled if disassociate_threshold <= roaming_threshold */ static void ipw_handle_missed_beacon(struct ipw_priv *priv, int missed_count) { priv->notif_missed_beacons = missed_count; if (missed_count > priv->disassociate_threshold && priv->status & STATUS_ASSOCIATED) { /* If associated and we've hit the missed * beacon threshold, disassociate, turn * off roaming, and abort any active scans */ IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "Missed beacon: %d - disassociate\n", missed_count); priv->status &= ~STATUS_ROAMING; if (priv->status & STATUS_SCANNING) { IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE, "Aborting scan with missed beacon.\n"); schedule_work(&priv->abort_scan); } schedule_work(&priv->disassociate); return; } if (priv->status & STATUS_ROAMING) { /* If we are currently roaming, then just * print a debug statement... */ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, "Missed beacon: %d - roam in progress\n", missed_count); return; } if (roaming && (missed_count > priv->roaming_threshold && missed_count <= priv->disassociate_threshold)) { /* If we are not already roaming, set the ROAM * bit in the status and kick off a scan. * This can happen several times before we reach * disassociate_threshold. */ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, "Missed beacon: %d - initiate " "roaming\n", missed_count); if (!(priv->status & STATUS_ROAMING)) { priv->status |= STATUS_ROAMING; if (!(priv->status & STATUS_SCANNING)) schedule_delayed_work(&priv->request_scan, 0); } return; } if (priv->status & STATUS_SCANNING && missed_count > IPW_MB_SCAN_CANCEL_THRESHOLD) { /* Stop scan to keep fw from getting * stuck (only if we aren't roaming -- * otherwise we'll never scan more than 2 or 3 * channels..) */ IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE, "Aborting scan with missed beacon.\n"); schedule_work(&priv->abort_scan); } IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count); } static void ipw_scan_event(struct work_struct *work) { union iwreq_data wrqu; struct ipw_priv *priv = container_of(work, struct ipw_priv, scan_event.work); wrqu.data.length = 0; wrqu.data.flags = 0; wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL); } static void handle_scan_event(struct ipw_priv *priv) { /* Only userspace-requested scan completion events go out immediately */ if (!priv->user_requested_scan) { if (!delayed_work_pending(&priv->scan_event)) schedule_delayed_work(&priv->scan_event, round_jiffies_relative(msecs_to_jiffies(4000))); } else { union iwreq_data wrqu; priv->user_requested_scan = 0; cancel_delayed_work(&priv->scan_event); wrqu.data.length = 0; wrqu.data.flags = 0; wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL); } } /** * Handle host notification packet. * Called from interrupt routine */ static void ipw_rx_notification(struct ipw_priv *priv, struct ipw_rx_notification *notif) { DECLARE_SSID_BUF(ssid); u16 size = le16_to_cpu(notif->size); IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size); switch (notif->subtype) { case HOST_NOTIFICATION_STATUS_ASSOCIATED:{ struct notif_association *assoc = ¬if->u.assoc; switch (assoc->state) { case CMAS_ASSOCIATED:{ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "associated: '%s' %pM\n", print_ssid(ssid, priv->essid, priv->essid_len), priv->bssid); switch (priv->ieee->iw_mode) { case IW_MODE_INFRA: memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN); break; case IW_MODE_ADHOC: memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN); /* clear out the station table */ priv->num_stations = 0; IPW_DEBUG_ASSOC ("queueing adhoc check\n"); schedule_delayed_work( &priv->adhoc_check, le16_to_cpu(priv-> assoc_request. beacon_interval)); break; } priv->status &= ~STATUS_ASSOCIATING; priv->status |= STATUS_ASSOCIATED; schedule_work(&priv->system_config); #ifdef CONFIG_IPW2200_QOS #define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \ le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_control)) if ((priv->status & STATUS_AUTH) && (IPW_GET_PACKET_STYPE(¬if->u.raw) == IEEE80211_STYPE_ASSOC_RESP)) { if ((sizeof (struct libipw_assoc_response) <= size) && (size <= 2314)) { struct libipw_rx_stats stats = { .len = size - 1, }; IPW_DEBUG_QOS ("QoS Associate " "size %d\n", size); libipw_rx_mgt(priv-> ieee, (struct libipw_hdr_4addr *) ¬if->u.raw, &stats); } } #endif schedule_work(&priv->link_up); break; } case CMAS_AUTHENTICATED:{ if (priv-> status & (STATUS_ASSOCIATED | STATUS_AUTH)) { struct notif_authenticate *auth = ¬if->u.auth; IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "deauthenticated: '%s' " "%pM" ": (0x%04X) - %s\n", print_ssid(ssid, priv-> essid, priv-> essid_len), priv->bssid, le16_to_cpu(auth->status), ipw_get_status_code (le16_to_cpu (auth->status))); priv->status &= ~(STATUS_ASSOCIATING | STATUS_AUTH | STATUS_ASSOCIATED); schedule_work(&priv->link_down); break; } IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "authenticated: '%s' %pM\n", print_ssid(ssid, priv->essid, priv->essid_len), priv->bssid); break; } case CMAS_INIT:{ if (priv->status & STATUS_AUTH) { struct libipw_assoc_response *resp; resp = (struct libipw_assoc_response *)¬if->u.raw; IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "association failed (0x%04X): %s\n", le16_to_cpu(resp->status), ipw_get_status_code (le16_to_cpu (resp->status))); } IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "disassociated: '%s' %pM\n", print_ssid(ssid, priv->essid, priv->essid_len), priv->bssid); priv->status &= ~(STATUS_DISASSOCIATING | STATUS_ASSOCIATING | STATUS_ASSOCIATED | STATUS_AUTH); if (priv->assoc_network && (priv->assoc_network-> capability & WLAN_CAPABILITY_IBSS)) ipw_remove_current_network (priv); schedule_work(&priv->link_down); break; } case CMAS_RX_ASSOC_RESP: break; default: IPW_ERROR("assoc: unknown (%d)\n", assoc->state); break; } break; } case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{ struct notif_authenticate *auth = ¬if->u.auth; switch (auth->state) { case CMAS_AUTHENTICATED: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, "authenticated: '%s' %pM\n", print_ssid(ssid, priv->essid, priv->essid_len), priv->bssid); priv->status |= STATUS_AUTH; break; case CMAS_INIT: if (priv->status & STATUS_AUTH) { IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "authentication failed (0x%04X): %s\n", le16_to_cpu(auth->status), ipw_get_status_code(le16_to_cpu (auth-> status))); } IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "deauthenticated: '%s' %pM\n", print_ssid(ssid, priv->essid, priv->essid_len), priv->bssid); priv->status &= ~(STATUS_ASSOCIATING | STATUS_AUTH | STATUS_ASSOCIATED); schedule_work(&priv->link_down); break; case CMAS_TX_AUTH_SEQ_1: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "AUTH_SEQ_1\n"); break; case CMAS_RX_AUTH_SEQ_2: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "AUTH_SEQ_2\n"); break; case CMAS_AUTH_SEQ_1_PASS: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n"); break; case CMAS_AUTH_SEQ_1_FAIL: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n"); break; case CMAS_TX_AUTH_SEQ_3: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "AUTH_SEQ_3\n"); break; case CMAS_RX_AUTH_SEQ_4: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n"); break; case CMAS_AUTH_SEQ_2_PASS: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n"); break; case CMAS_AUTH_SEQ_2_FAIL: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n"); break; case CMAS_TX_ASSOC: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "TX_ASSOC\n"); break; case CMAS_RX_ASSOC_RESP: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "RX_ASSOC_RESP\n"); break; case CMAS_ASSOCIATED: IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "ASSOCIATED\n"); break; default: IPW_DEBUG_NOTIF("auth: failure - %d\n", auth->state); break; } break; } case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{ struct notif_channel_result *x = ¬if->u.channel_result; if (size == sizeof(*x)) { IPW_DEBUG_SCAN("Scan result for channel %d\n", x->channel_num); } else { IPW_DEBUG_SCAN("Scan result of wrong size %d " "(should be %zd)\n", size, sizeof(*x)); } break; } case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{ struct notif_scan_complete *x = ¬if->u.scan_complete; if (size == sizeof(*x)) { IPW_DEBUG_SCAN ("Scan completed: type %d, %d channels, " "%d status\n", x->scan_type, x->num_channels, x->status); } else { IPW_ERROR("Scan completed of wrong size %d " "(should be %zd)\n", size, sizeof(*x)); } priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING); wake_up_interruptible(&priv->wait_state); cancel_delayed_work(&priv->scan_check); if (priv->status & STATUS_EXIT_PENDING) break; priv->ieee->scans++; #ifdef CONFIG_IPW2200_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) { priv->status |= STATUS_SCAN_FORCED; schedule_delayed_work(&priv->request_scan, 0); break; } priv->status &= ~STATUS_SCAN_FORCED; #endif /* CONFIG_IPW2200_MONITOR */ /* Do queued direct scans first */ if (priv->status & STATUS_DIRECT_SCAN_PENDING) schedule_delayed_work(&priv->request_direct_scan, 0); if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING | STATUS_ROAMING | STATUS_DISASSOCIATING))) schedule_work(&priv->associate); else if (priv->status & STATUS_ROAMING) { if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) /* If a scan completed and we are in roam mode, then * the scan that completed was the one requested as a * result of entering roam... so, schedule the * roam work */ schedule_work(&priv->roam); else /* Don't schedule if we aborted the scan */ priv->status &= ~STATUS_ROAMING; } else if (priv->status & STATUS_SCAN_PENDING) schedule_delayed_work(&priv->request_scan, 0); else if (priv->config & CFG_BACKGROUND_SCAN && priv->status & STATUS_ASSOCIATED) schedule_delayed_work(&priv->request_scan, round_jiffies_relative(HZ)); /* Send an empty event to user space. * We don't send the received data on the event because * it would require us to do complex transcoding, and * we want to minimise the work done in the irq handler * Use a request to extract the data. * Also, we generate this even for any scan, regardless * on how the scan was initiated. User space can just * sync on periodic scan to get fresh data... * Jean II */ if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) handle_scan_event(priv); break; } case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{ struct notif_frag_length *x = ¬if->u.frag_len; if (size == sizeof(*x)) IPW_ERROR("Frag length: %d\n", le16_to_cpu(x->frag_length)); else IPW_ERROR("Frag length of wrong size %d " "(should be %zd)\n", size, sizeof(*x)); break; } case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{ struct notif_link_deterioration *x = ¬if->u.link_deterioration; if (size == sizeof(*x)) { IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, "link deterioration: type %d, cnt %d\n", x->silence_notification_type, x->silence_count); memcpy(&priv->last_link_deterioration, x, sizeof(*x)); } else { IPW_ERROR("Link Deterioration of wrong size %d " "(should be %zd)\n", size, sizeof(*x)); } break; } case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{ IPW_ERROR("Dino config\n"); if (priv->hcmd && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG) IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n"); break; } case HOST_NOTIFICATION_STATUS_BEACON_STATE:{ struct notif_beacon_state *x = ¬if->u.beacon_state; if (size != sizeof(*x)) { IPW_ERROR ("Beacon state of wrong size %d (should " "be %zd)\n", size, sizeof(*x)); break; } if (le32_to_cpu(x->state) == HOST_NOTIFICATION_STATUS_BEACON_MISSING) ipw_handle_missed_beacon(priv, le32_to_cpu(x-> number)); break; } case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{ struct notif_tgi_tx_key *x = ¬if->u.tgi_tx_key; if (size == sizeof(*x)) { IPW_ERROR("TGi Tx Key: state 0x%02x sec type " "0x%02x station %d\n", x->key_state, x->security_type, x->station_index); break; } IPW_ERROR ("TGi Tx Key of wrong size %d (should be %zd)\n", size, sizeof(*x)); break; } case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{ struct notif_calibration *x = ¬if->u.calibration; if (size == sizeof(*x)) { memcpy(&priv->calib, x, sizeof(*x)); IPW_DEBUG_INFO("TODO: Calibration\n"); break; } IPW_ERROR ("Calibration of wrong size %d (should be %zd)\n", size, sizeof(*x)); break; } case HOST_NOTIFICATION_NOISE_STATS:{ if (size == sizeof(u32)) { priv->exp_avg_noise = exponential_average(priv->exp_avg_noise, (u8) (le32_to_cpu(notif->u.noise.value) & 0xff), DEPTH_NOISE); break; } IPW_ERROR ("Noise stat is wrong size %d (should be %zd)\n", size, sizeof(u32)); break; } default: IPW_DEBUG_NOTIF("Unknown notification: " "subtype=%d,flags=0x%2x,size=%d\n", notif->subtype, notif->flags, size); } } /** * Destroys all DMA structures and initialise them again * * @param priv * @return error code */ static int ipw_queue_reset(struct ipw_priv *priv) { int rc = 0; /** @todo customize queue sizes */ int nTx = 64, nTxCmd = 8; ipw_tx_queue_free(priv); /* Tx CMD queue */ rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd, IPW_TX_CMD_QUEUE_READ_INDEX, IPW_TX_CMD_QUEUE_WRITE_INDEX, IPW_TX_CMD_QUEUE_BD_BASE, IPW_TX_CMD_QUEUE_BD_SIZE); if (rc) { IPW_ERROR("Tx Cmd queue init failed\n"); goto error; } /* Tx queue(s) */ rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx, IPW_TX_QUEUE_0_READ_INDEX, IPW_TX_QUEUE_0_WRITE_INDEX, IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE); if (rc) { IPW_ERROR("Tx 0 queue init failed\n"); goto error; } rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx, IPW_TX_QUEUE_1_READ_INDEX, IPW_TX_QUEUE_1_WRITE_INDEX, IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE); if (rc) { IPW_ERROR("Tx 1 queue init failed\n"); goto error; } rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx, IPW_TX_QUEUE_2_READ_INDEX, IPW_TX_QUEUE_2_WRITE_INDEX, IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE); if (rc) { IPW_ERROR("Tx 2 queue init failed\n"); goto error; } rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx, IPW_TX_QUEUE_3_READ_INDEX, IPW_TX_QUEUE_3_WRITE_INDEX, IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE); if (rc) { IPW_ERROR("Tx 3 queue init failed\n"); goto error; } /* statistics */ priv->rx_bufs_min = 0; priv->rx_pend_max = 0; return rc; error: ipw_tx_queue_free(priv); return rc; } /** * Reclaim Tx queue entries no more used by NIC. * * When FW advances 'R' index, all entries between old and * new 'R' index need to be reclaimed. As result, some free space * forms. If there is enough free space (> low mark), wake Tx queue. * * @note Need to protect against garbage in 'R' index * @param priv * @param txq * @param qindex * @return Number of used entries remains in the queue */ static int ipw_queue_tx_reclaim(struct ipw_priv *priv, struct clx2_tx_queue *txq, int qindex) { u32 hw_tail; int used; struct clx2_queue *q = &txq->q; hw_tail = ipw_read32(priv, q->reg_r); if (hw_tail >= q->n_bd) { IPW_ERROR ("Read index for DMA queue (%d) is out of range [0-%d)\n", hw_tail, q->n_bd); goto done; } for (; q->last_used != hw_tail; q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) { ipw_queue_tx_free_tfd(priv, txq); priv->tx_packets++; } done: if ((ipw_tx_queue_space(q) > q->low_mark) && (qindex >= 0)) netif_wake_queue(priv->net_dev); used = q->first_empty - q->last_used; if (used < 0) used += q->n_bd; return used; } static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, int len, int sync) { struct clx2_tx_queue *txq = &priv->txq_cmd; struct clx2_queue *q = &txq->q; struct tfd_frame *tfd; if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) { IPW_ERROR("No space for Tx\n"); return -EBUSY; } tfd = &txq->bd[q->first_empty]; txq->txb[q->first_empty] = NULL; memset(tfd, 0, sizeof(*tfd)); tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE; tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK; priv->hcmd_seq++; tfd->u.cmd.index = hcmd; tfd->u.cmd.length = len; memcpy(tfd->u.cmd.payload, buf, len); q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); ipw_write32(priv, q->reg_w, q->first_empty); _ipw_read32(priv, 0x90); return 0; } /* * Rx theory of operation * * The host allocates 32 DMA target addresses and passes the host address * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is * 0 to 31 * * Rx Queue Indexes * The host/firmware share two index registers for managing the Rx buffers. * * The READ index maps to the first position that the firmware may be writing * to -- the driver can read up to (but not including) this position and get * good data. * The READ index is managed by the firmware once the card is enabled. * * The WRITE index maps to the last position the driver has read from -- the * position preceding WRITE is the last slot the firmware can place a packet. * * The queue is empty (no good data) if WRITE = READ - 1, and is full if * WRITE = READ. * * During initialization the host sets up the READ queue position to the first * INDEX position, and WRITE to the last (READ - 1 wrapped) * * When the firmware places a packet in a buffer it will advance the READ index * and fire the RX interrupt. The driver can then query the READ index and * process as many packets as possible, moving the WRITE index forward as it * resets the Rx queue buffers with new memory. * * The management in the driver is as follows: * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled * to replensish the ipw->rxq->rx_free. * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the * ipw->rxq is replenished and the READ INDEX is updated (updating the * 'processed' and 'read' driver indexes as well) * + A received packet is processed and handed to the kernel network stack, * detached from the ipw->rxq. The driver 'processed' index is updated. * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there * were enough free buffers and RX_STALLED is set it is cleared. * * * Driver sequence: * * ipw_rx_queue_alloc() Allocates rx_free * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls * ipw_rx_queue_restock * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx * queue, updates firmware pointers, and updates * the WRITE index. If insufficient rx_free buffers * are available, schedules ipw_rx_queue_replenish * * -- enable interrupts -- * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the * READ INDEX, detaching the SKB from the pool. * Moves the packet buffer from queue to rx_used. * Calls ipw_rx_queue_restock to refill any empty * slots. * ... * */ /* * If there are slots in the RX queue that need to be restocked, * and we have free pre-allocated buffers, fill the ranks as much * as we can pulling from rx_free. * * This moves the 'write' index forward to catch up with 'processed', and * also updates the memory address in the firmware to reference the new * target buffer. */ static void ipw_rx_queue_restock(struct ipw_priv *priv) { struct ipw_rx_queue *rxq = priv->rxq; struct list_head *element; struct ipw_rx_mem_buffer *rxb; unsigned long flags; int write; spin_lock_irqsave(&rxq->lock, flags); write = rxq->write; while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) { element = rxq->rx_free.next; rxb = list_entry(element, struct ipw_rx_mem_buffer, list); list_del(element); ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE, rxb->dma_addr); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE; rxq->free_count--; } spin_unlock_irqrestore(&rxq->lock, flags); /* If the pre-allocated buffer pool is dropping low, schedule to * refill it */ if (rxq->free_count <= RX_LOW_WATERMARK) schedule_work(&priv->rx_replenish); /* If we've added more space for the firmware to place data, tell it */ if (write != rxq->write) ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write); } /* * Move all used packet from rx_used to rx_free, allocating a new SKB for each. * Also restock the Rx queue via ipw_rx_queue_restock. * * This is called as a scheduled work item (except for during intialization) */ static void ipw_rx_queue_replenish(void *data) { struct ipw_priv *priv = data; struct ipw_rx_queue *rxq = priv->rxq; struct list_head *element; struct ipw_rx_mem_buffer *rxb; unsigned long flags; spin_lock_irqsave(&rxq->lock, flags); while (!list_empty(&rxq->rx_used)) { element = rxq->rx_used.next; rxb = list_entry(element, struct ipw_rx_mem_buffer, list); rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC); if (!rxb->skb) { printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n", priv->net_dev->name); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ break; } list_del(element); rxb->dma_addr = pci_map_single(priv->pci_dev, rxb->skb->data, IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; } spin_unlock_irqrestore(&rxq->lock, flags); ipw_rx_queue_restock(priv); } static void ipw_bg_rx_queue_replenish(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, rx_replenish); mutex_lock(&priv->mutex); ipw_rx_queue_replenish(priv); mutex_unlock(&priv->mutex); } /* Assumes that the skb field of the buffers in 'pool' is kept accurate. * If an SKB has been detached, the POOL needs to have its SKB set to NULL * This free routine walks the list of POOL entries and if SKB is set to * non NULL it is unmapped and freed */ static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq) { int i; if (!rxq) return; for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { if (rxq->pool[i].skb != NULL) { pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr, IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); dev_kfree_skb(rxq->pool[i].skb); } } kfree(rxq); } static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv) { struct ipw_rx_queue *rxq; int i; rxq = kzalloc(sizeof(*rxq), GFP_KERNEL); if (unlikely(!rxq)) { IPW_ERROR("memory allocation failed\n"); return NULL; } spin_lock_init(&rxq->lock); INIT_LIST_HEAD(&rxq->rx_free); INIT_LIST_HEAD(&rxq->rx_used); /* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) list_add_tail(&rxq->pool[i].list, &rxq->rx_used); /* Set us so that we have processed and used all buffers, but have * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; rxq->free_count = 0; return rxq; } static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate) { rate &= ~LIBIPW_BASIC_RATE_MASK; if (ieee_mode == IEEE_A) { switch (rate) { case LIBIPW_OFDM_RATE_6MB: return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_9MB: return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_12MB: return priv-> rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_18MB: return priv-> rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_24MB: return priv-> rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_36MB: return priv-> rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_48MB: return priv-> rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_54MB: return priv-> rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; default: return 0; } } /* B and G mixed */ switch (rate) { case LIBIPW_CCK_RATE_1MB: return priv->rates_mask & LIBIPW_CCK_RATE_1MB_MASK ? 1 : 0; case LIBIPW_CCK_RATE_2MB: return priv->rates_mask & LIBIPW_CCK_RATE_2MB_MASK ? 1 : 0; case LIBIPW_CCK_RATE_5MB: return priv->rates_mask & LIBIPW_CCK_RATE_5MB_MASK ? 1 : 0; case LIBIPW_CCK_RATE_11MB: return priv->rates_mask & LIBIPW_CCK_RATE_11MB_MASK ? 1 : 0; } /* If we are limited to B modulations, bail at this point */ if (ieee_mode == IEEE_B) return 0; /* G */ switch (rate) { case LIBIPW_OFDM_RATE_6MB: return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_9MB: return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_12MB: return priv->rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_18MB: return priv->rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_24MB: return priv->rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_36MB: return priv->rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_48MB: return priv->rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; case LIBIPW_OFDM_RATE_54MB: return priv->rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; } return 0; } static int ipw_compatible_rates(struct ipw_priv *priv, const struct libipw_network *network, struct ipw_supported_rates *rates) { int num_rates, i; memset(rates, 0, sizeof(*rates)); num_rates = min(network->rates_len, (u8) IPW_MAX_RATES); rates->num_rates = 0; for (i = 0; i < num_rates; i++) { if (!ipw_is_rate_in_mask(priv, network->mode, network->rates[i])) { if (network->rates[i] & LIBIPW_BASIC_RATE_MASK) { IPW_DEBUG_SCAN("Adding masked mandatory " "rate %02X\n", network->rates[i]); rates->supported_rates[rates->num_rates++] = network->rates[i]; continue; } IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n", network->rates[i], priv->rates_mask); continue; } rates->supported_rates[rates->num_rates++] = network->rates[i]; } num_rates = min(network->rates_ex_len, (u8) (IPW_MAX_RATES - num_rates)); for (i = 0; i < num_rates; i++) { if (!ipw_is_rate_in_mask(priv, network->mode, network->rates_ex[i])) { if (network->rates_ex[i] & LIBIPW_BASIC_RATE_MASK) { IPW_DEBUG_SCAN("Adding masked mandatory " "rate %02X\n", network->rates_ex[i]); rates->supported_rates[rates->num_rates++] = network->rates[i]; continue; } IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n", network->rates_ex[i], priv->rates_mask); continue; } rates->supported_rates[rates->num_rates++] = network->rates_ex[i]; } return 1; } static void ipw_copy_rates(struct ipw_supported_rates *dest, const struct ipw_supported_rates *src) { u8 i; for (i = 0; i < src->num_rates; i++) dest->supported_rates[i] = src->supported_rates[i]; dest->num_rates = src->num_rates; } /* TODO: Look at sniffed packets in the air to determine if the basic rate * mask should ever be used -- right now all callers to add the scan rates are * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */ static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates, u8 modulation, u32 rate_mask) { u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? LIBIPW_BASIC_RATE_MASK : 0; if (rate_mask & LIBIPW_CCK_RATE_1MB_MASK) rates->supported_rates[rates->num_rates++] = LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_1MB; if (rate_mask & LIBIPW_CCK_RATE_2MB_MASK) rates->supported_rates[rates->num_rates++] = LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_2MB; if (rate_mask & LIBIPW_CCK_RATE_5MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | LIBIPW_CCK_RATE_5MB; if (rate_mask & LIBIPW_CCK_RATE_11MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | LIBIPW_CCK_RATE_11MB; } static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates, u8 modulation, u32 rate_mask) { u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? LIBIPW_BASIC_RATE_MASK : 0; if (rate_mask & LIBIPW_OFDM_RATE_6MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | LIBIPW_OFDM_RATE_6MB; if (rate_mask & LIBIPW_OFDM_RATE_9MB_MASK) rates->supported_rates[rates->num_rates++] = LIBIPW_OFDM_RATE_9MB; if (rate_mask & LIBIPW_OFDM_RATE_12MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | LIBIPW_OFDM_RATE_12MB; if (rate_mask & LIBIPW_OFDM_RATE_18MB_MASK) rates->supported_rates[rates->num_rates++] = LIBIPW_OFDM_RATE_18MB; if (rate_mask & LIBIPW_OFDM_RATE_24MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | LIBIPW_OFDM_RATE_24MB; if (rate_mask & LIBIPW_OFDM_RATE_36MB_MASK) rates->supported_rates[rates->num_rates++] = LIBIPW_OFDM_RATE_36MB; if (rate_mask & LIBIPW_OFDM_RATE_48MB_MASK) rates->supported_rates[rates->num_rates++] = LIBIPW_OFDM_RATE_48MB; if (rate_mask & LIBIPW_OFDM_RATE_54MB_MASK) rates->supported_rates[rates->num_rates++] = LIBIPW_OFDM_RATE_54MB; } struct ipw_network_match { struct libipw_network *network; struct ipw_supported_rates rates; }; static int ipw_find_adhoc_network(struct ipw_priv *priv, struct ipw_network_match *match, struct libipw_network *network, int roaming) { struct ipw_supported_rates rates; DECLARE_SSID_BUF(ssid); /* Verify that this network's capability is compatible with the * current mode (AdHoc or Infrastructure) */ if ((priv->ieee->iw_mode == IW_MODE_ADHOC && !(network->capability & WLAN_CAPABILITY_IBSS))) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded due to " "capability mismatch.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } if (unlikely(roaming)) { /* If we are roaming, then ensure check if this is a valid * network to try and roam to */ if ((network->ssid_len != match->network->ssid_len) || memcmp(network->ssid, match->network->ssid, network->ssid_len)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of non-network ESSID.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } } else { /* If an ESSID has been configured then compare the broadcast * ESSID to ours */ if ((priv->config & CFG_STATIC_ESSID) && ((network->ssid_len != priv->essid_len) || memcmp(network->ssid, priv->essid, min(network->ssid_len, priv->essid_len)))) { char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; strncpy(escaped, print_ssid(ssid, network->ssid, network->ssid_len), sizeof(escaped)); IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of ESSID mismatch: '%s'.\n", escaped, network->bssid, print_ssid(ssid, priv->essid, priv->essid_len)); return 0; } } /* If the old network rate is better than this one, don't bother * testing everything else. */ if (network->time_stamp[0] < match->network->time_stamp[0]) { IPW_DEBUG_MERGE("Network '%s excluded because newer than " "current network.\n", print_ssid(ssid, match->network->ssid, match->network->ssid_len)); return 0; } else if (network->time_stamp[1] < match->network->time_stamp[1]) { IPW_DEBUG_MERGE("Network '%s excluded because newer than " "current network.\n", print_ssid(ssid, match->network->ssid, match->network->ssid_len)); return 0; } /* Now go through and see if the requested network is valid... */ if (priv->ieee->scan_age != 0 && time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of age: %ums.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, jiffies_to_msecs(jiffies - network->last_scanned)); return 0; } if ((priv->config & CFG_STATIC_CHANNEL) && (network->channel != priv->channel)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of channel mismatch: %d != %d.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, network->channel, priv->channel); return 0; } /* Verify privacy compatibility */ if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) != ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of privacy mismatch: %s != %s.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, priv-> capability & CAP_PRIVACY_ON ? "on" : "off", network-> capability & WLAN_CAPABILITY_PRIVACY ? "on" : "off"); return 0; } if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of the same BSSID match: %pM" ".\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, priv->bssid); return 0; } /* Filter out any incompatible freq / mode combinations */ if (!libipw_is_valid_mode(priv->ieee, network->mode)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of invalid frequency/mode " "combination.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } /* Ensure that the rates supported by the driver are compatible with * this AP, including verification of basic rates (mandatory) */ if (!ipw_compatible_rates(priv, network, &rates)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because configured rate mask excludes " "AP mandatory rate.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } if (rates.num_rates == 0) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of no compatible rates.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } /* TODO: Perform any further minimal comparititive tests. We do not * want to put too much policy logic here; intelligent scan selection * should occur within a generic IEEE 802.11 user space tool. */ /* Set up 'new' AP to this network */ ipw_copy_rates(&match->rates, &rates); match->network = network; IPW_DEBUG_MERGE("Network '%s (%pM)' is a viable match.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 1; } static void ipw_merge_adhoc_network(struct work_struct *work) { DECLARE_SSID_BUF(ssid); struct ipw_priv *priv = container_of(work, struct ipw_priv, merge_networks); struct libipw_network *network = NULL; struct ipw_network_match match = { .network = priv->assoc_network }; if ((priv->status & STATUS_ASSOCIATED) && (priv->ieee->iw_mode == IW_MODE_ADHOC)) { /* First pass through ROAM process -- look for a better * network */ unsigned long flags; spin_lock_irqsave(&priv->ieee->lock, flags); list_for_each_entry(network, &priv->ieee->network_list, list) { if (network != priv->assoc_network) ipw_find_adhoc_network(priv, &match, network, 1); } spin_unlock_irqrestore(&priv->ieee->lock, flags); if (match.network == priv->assoc_network) { IPW_DEBUG_MERGE("No better ADHOC in this network to " "merge to.\n"); return; } mutex_lock(&priv->mutex); if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) { IPW_DEBUG_MERGE("remove network %s\n", print_ssid(ssid, priv->essid, priv->essid_len)); ipw_remove_current_network(priv); } ipw_disassociate(priv); priv->assoc_network = match.network; mutex_unlock(&priv->mutex); return; } } static int ipw_best_network(struct ipw_priv *priv, struct ipw_network_match *match, struct libipw_network *network, int roaming) { struct ipw_supported_rates rates; DECLARE_SSID_BUF(ssid); /* Verify that this network's capability is compatible with the * current mode (AdHoc or Infrastructure) */ if ((priv->ieee->iw_mode == IW_MODE_INFRA && !(network->capability & WLAN_CAPABILITY_ESS)) || (priv->ieee->iw_mode == IW_MODE_ADHOC && !(network->capability & WLAN_CAPABILITY_IBSS))) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded due to " "capability mismatch.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } if (unlikely(roaming)) { /* If we are roaming, then ensure check if this is a valid * network to try and roam to */ if ((network->ssid_len != match->network->ssid_len) || memcmp(network->ssid, match->network->ssid, network->ssid_len)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of non-network ESSID.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } } else { /* If an ESSID has been configured then compare the broadcast * ESSID to ours */ if ((priv->config & CFG_STATIC_ESSID) && ((network->ssid_len != priv->essid_len) || memcmp(network->ssid, priv->essid, min(network->ssid_len, priv->essid_len)))) { char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; strncpy(escaped, print_ssid(ssid, network->ssid, network->ssid_len), sizeof(escaped)); IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of ESSID mismatch: '%s'.\n", escaped, network->bssid, print_ssid(ssid, priv->essid, priv->essid_len)); return 0; } } /* If the old network rate is better than this one, don't bother * testing everything else. */ if (match->network && match->network->stats.rssi > network->stats.rssi) { char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; strncpy(escaped, print_ssid(ssid, network->ssid, network->ssid_len), sizeof(escaped)); IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded because " "'%s (%pM)' has a stronger signal.\n", escaped, network->bssid, print_ssid(ssid, match->network->ssid, match->network->ssid_len), match->network->bssid); return 0; } /* If this network has already had an association attempt within the * last 3 seconds, do not try and associate again... */ if (network->last_associate && time_after(network->last_associate + (HZ * 3UL), jiffies)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of storming (%ums since last " "assoc attempt).\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, jiffies_to_msecs(jiffies - network->last_associate)); return 0; } /* Now go through and see if the requested network is valid... */ if (priv->ieee->scan_age != 0 && time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of age: %ums.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, jiffies_to_msecs(jiffies - network->last_scanned)); return 0; } if ((priv->config & CFG_STATIC_CHANNEL) && (network->channel != priv->channel)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of channel mismatch: %d != %d.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, network->channel, priv->channel); return 0; } /* Verify privacy compatibility */ if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) != ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of privacy mismatch: %s != %s.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, priv->capability & CAP_PRIVACY_ON ? "on" : "off", network->capability & WLAN_CAPABILITY_PRIVACY ? "on" : "off"); return 0; } if ((priv->config & CFG_STATIC_BSSID) && memcmp(network->bssid, priv->bssid, ETH_ALEN)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of BSSID mismatch: %pM.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, priv->bssid); return 0; } /* Filter out any incompatible freq / mode combinations */ if (!libipw_is_valid_mode(priv->ieee, network->mode)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of invalid frequency/mode " "combination.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } /* Filter out invalid channel in current GEO */ if (!libipw_is_valid_channel(priv->ieee, network->channel)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of invalid channel in current GEO\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } /* Ensure that the rates supported by the driver are compatible with * this AP, including verification of basic rates (mandatory) */ if (!ipw_compatible_rates(priv, network, &rates)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because configured rate mask excludes " "AP mandatory rate.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } if (rates.num_rates == 0) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of no compatible rates.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 0; } /* TODO: Perform any further minimal comparititive tests. We do not * want to put too much policy logic here; intelligent scan selection * should occur within a generic IEEE 802.11 user space tool. */ /* Set up 'new' AP to this network */ ipw_copy_rates(&match->rates, &rates); match->network = network; IPW_DEBUG_ASSOC("Network '%s (%pM)' is a viable match.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 1; } static void ipw_adhoc_create(struct ipw_priv *priv, struct libipw_network *network) { const struct libipw_geo *geo = libipw_get_geo(priv->ieee); int i; /* * For the purposes of scanning, we can set our wireless mode * to trigger scans across combinations of bands, but when it * comes to creating a new ad-hoc network, we have tell the FW * exactly which band to use. * * We also have the possibility of an invalid channel for the * chossen band. Attempting to create a new ad-hoc network * with an invalid channel for wireless mode will trigger a * FW fatal error. * */ switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { case LIBIPW_52GHZ_BAND: network->mode = IEEE_A; i = libipw_channel_to_index(priv->ieee, priv->channel); BUG_ON(i == -1); if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) { IPW_WARNING("Overriding invalid channel\n"); priv->channel = geo->a[0].channel; } break; case LIBIPW_24GHZ_BAND: if (priv->ieee->mode & IEEE_G) network->mode = IEEE_G; else network->mode = IEEE_B; i = libipw_channel_to_index(priv->ieee, priv->channel); BUG_ON(i == -1); if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) { IPW_WARNING("Overriding invalid channel\n"); priv->channel = geo->bg[0].channel; } break; default: IPW_WARNING("Overriding invalid channel\n"); if (priv->ieee->mode & IEEE_A) { network->mode = IEEE_A; priv->channel = geo->a[0].channel; } else if (priv->ieee->mode & IEEE_G) { network->mode = IEEE_G; priv->channel = geo->bg[0].channel; } else { network->mode = IEEE_B; priv->channel = geo->bg[0].channel; } break; } network->channel = priv->channel; priv->config |= CFG_ADHOC_PERSIST; ipw_create_bssid(priv, network->bssid); network->ssid_len = priv->essid_len; memcpy(network->ssid, priv->essid, priv->essid_len); memset(&network->stats, 0, sizeof(network->stats)); network->capability = WLAN_CAPABILITY_IBSS; if (!(priv->config & CFG_PREAMBLE_LONG)) network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE; if (priv->capability & CAP_PRIVACY_ON) network->capability |= WLAN_CAPABILITY_PRIVACY; network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH); memcpy(network->rates, priv->rates.supported_rates, network->rates_len); network->rates_ex_len = priv->rates.num_rates - network->rates_len; memcpy(network->rates_ex, &priv->rates.supported_rates[network->rates_len], network->rates_ex_len); network->last_scanned = 0; network->flags = 0; network->last_associate = 0; network->time_stamp[0] = 0; network->time_stamp[1] = 0; network->beacon_interval = 100; /* Default */ network->listen_interval = 10; /* Default */ network->atim_window = 0; /* Default */ network->wpa_ie_len = 0; network->rsn_ie_len = 0; } static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index) { struct ipw_tgi_tx_key key; if (!(priv->ieee->sec.flags & (1 << index))) return; key.key_id = index; memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH); key.security_type = type; key.station_index = 0; /* always 0 for BSS */ key.flags = 0; /* 0 for new key; previous value of counter (after fatal error) */ key.tx_counter[0] = cpu_to_le32(0); key.tx_counter[1] = cpu_to_le32(0); ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key); } static void ipw_send_wep_keys(struct ipw_priv *priv, int type) { struct ipw_wep_key key; int i; key.cmd_id = DINO_CMD_WEP_KEY; key.seq_num = 0; /* Note: AES keys cannot be set for multiple times. * Only set it at the first time. */ for (i = 0; i < 4; i++) { key.key_index = i | type; if (!(priv->ieee->sec.flags & (1 << i))) { key.key_size = 0; continue; } key.key_size = priv->ieee->sec.key_sizes[i]; memcpy(key.key, priv->ieee->sec.keys[i], key.key_size); ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key); } } static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level) { if (priv->ieee->host_encrypt) return; switch (level) { case SEC_LEVEL_3: priv->sys_config.disable_unicast_decryption = 0; priv->ieee->host_decrypt = 0; break; case SEC_LEVEL_2: priv->sys_config.disable_unicast_decryption = 1; priv->ieee->host_decrypt = 1; break; case SEC_LEVEL_1: priv->sys_config.disable_unicast_decryption = 0; priv->ieee->host_decrypt = 0; break; case SEC_LEVEL_0: priv->sys_config.disable_unicast_decryption = 1; break; default: break; } } static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level) { if (priv->ieee->host_encrypt) return; switch (level) { case SEC_LEVEL_3: priv->sys_config.disable_multicast_decryption = 0; break; case SEC_LEVEL_2: priv->sys_config.disable_multicast_decryption = 1; break; case SEC_LEVEL_1: priv->sys_config.disable_multicast_decryption = 0; break; case SEC_LEVEL_0: priv->sys_config.disable_multicast_decryption = 1; break; default: break; } } static void ipw_set_hwcrypto_keys(struct ipw_priv *priv) { switch (priv->ieee->sec.level) { case SEC_LEVEL_3: if (priv->ieee->sec.flags & SEC_ACTIVE_KEY) ipw_send_tgi_tx_key(priv, DCT_FLAG_EXT_SECURITY_CCM, priv->ieee->sec.active_key); if (!priv->ieee->host_mc_decrypt) ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM); break; case SEC_LEVEL_2: if (priv->ieee->sec.flags & SEC_ACTIVE_KEY) ipw_send_tgi_tx_key(priv, DCT_FLAG_EXT_SECURITY_TKIP, priv->ieee->sec.active_key); break; case SEC_LEVEL_1: ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level); ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level); break; case SEC_LEVEL_0: default: break; } } static void ipw_adhoc_check(void *data) { struct ipw_priv *priv = data; if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold && !(priv->config & CFG_ADHOC_PERSIST)) { IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, "Missed beacon: %d - disassociate\n", priv->missed_adhoc_beacons); ipw_remove_current_network(priv); ipw_disassociate(priv); return; } schedule_delayed_work(&priv->adhoc_check, le16_to_cpu(priv->assoc_request.beacon_interval)); } static void ipw_bg_adhoc_check(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, adhoc_check.work); mutex_lock(&priv->mutex); ipw_adhoc_check(priv); mutex_unlock(&priv->mutex); } static void ipw_debug_config(struct ipw_priv *priv) { DECLARE_SSID_BUF(ssid); IPW_DEBUG_INFO("Scan completed, no valid APs matched " "[CFG 0x%08X]\n", priv->config); if (priv->config & CFG_STATIC_CHANNEL) IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel); else IPW_DEBUG_INFO("Channel unlocked.\n"); if (priv->config & CFG_STATIC_ESSID) IPW_DEBUG_INFO("ESSID locked to '%s'\n", print_ssid(ssid, priv->essid, priv->essid_len)); else IPW_DEBUG_INFO("ESSID unlocked.\n"); if (priv->config & CFG_STATIC_BSSID) IPW_DEBUG_INFO("BSSID locked to %pM\n", priv->bssid); else IPW_DEBUG_INFO("BSSID unlocked.\n"); if (priv->capability & CAP_PRIVACY_ON) IPW_DEBUG_INFO("PRIVACY on\n"); else IPW_DEBUG_INFO("PRIVACY off\n"); IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask); } static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode) { /* TODO: Verify that this works... */ struct ipw_fixed_rate fr; u32 reg; u16 mask = 0; u16 new_tx_rates = priv->rates_mask; /* Identify 'current FW band' and match it with the fixed * Tx rates */ switch (priv->ieee->freq_band) { case LIBIPW_52GHZ_BAND: /* A only */ /* IEEE_A */ if (priv->rates_mask & ~LIBIPW_OFDM_RATES_MASK) { /* Invalid fixed rate mask */ IPW_DEBUG_WX ("invalid fixed rate mask in ipw_set_fixed_rate\n"); new_tx_rates = 0; break; } new_tx_rates >>= LIBIPW_OFDM_SHIFT_MASK_A; break; default: /* 2.4Ghz or Mixed */ /* IEEE_B */ if (mode == IEEE_B) { if (new_tx_rates & ~LIBIPW_CCK_RATES_MASK) { /* Invalid fixed rate mask */ IPW_DEBUG_WX ("invalid fixed rate mask in ipw_set_fixed_rate\n"); new_tx_rates = 0; } break; } /* IEEE_G */ if (new_tx_rates & ~(LIBIPW_CCK_RATES_MASK | LIBIPW_OFDM_RATES_MASK)) { /* Invalid fixed rate mask */ IPW_DEBUG_WX ("invalid fixed rate mask in ipw_set_fixed_rate\n"); new_tx_rates = 0; break; } if (LIBIPW_OFDM_RATE_6MB_MASK & new_tx_rates) { mask |= (LIBIPW_OFDM_RATE_6MB_MASK >> 1); new_tx_rates &= ~LIBIPW_OFDM_RATE_6MB_MASK; } if (LIBIPW_OFDM_RATE_9MB_MASK & new_tx_rates) { mask |= (LIBIPW_OFDM_RATE_9MB_MASK >> 1); new_tx_rates &= ~LIBIPW_OFDM_RATE_9MB_MASK; } if (LIBIPW_OFDM_RATE_12MB_MASK & new_tx_rates) { mask |= (LIBIPW_OFDM_RATE_12MB_MASK >> 1); new_tx_rates &= ~LIBIPW_OFDM_RATE_12MB_MASK; } new_tx_rates |= mask; break; } fr.tx_rates = cpu_to_le16(new_tx_rates); reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE); ipw_write_reg32(priv, reg, *(u32 *) & fr); } static void ipw_abort_scan(struct ipw_priv *priv) { int err; if (priv->status & STATUS_SCAN_ABORTING) { IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n"); return; } priv->status |= STATUS_SCAN_ABORTING; err = ipw_send_scan_abort(priv); if (err) IPW_DEBUG_HC("Request to abort scan failed.\n"); } static void ipw_add_scan_channels(struct ipw_priv *priv, struct ipw_scan_request_ext *scan, int scan_type) { int channel_index = 0; const struct libipw_geo *geo; int i; geo = libipw_get_geo(priv->ieee); if (priv->ieee->freq_band & LIBIPW_52GHZ_BAND) { int start = channel_index; for (i = 0; i < geo->a_channels; i++) { if ((priv->status & STATUS_ASSOCIATED) && geo->a[i].channel == priv->channel) continue; channel_index++; scan->channels_list[channel_index] = geo->a[i].channel; ipw_set_scan_type(scan, channel_index, geo->a[i]. flags & LIBIPW_CH_PASSIVE_ONLY ? IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : scan_type); } if (start != channel_index) { scan->channels_list[start] = (u8) (IPW_A_MODE << 6) | (channel_index - start); channel_index++; } } if (priv->ieee->freq_band & LIBIPW_24GHZ_BAND) { int start = channel_index; if (priv->config & CFG_SPEED_SCAN) { int index; u8 channels[LIBIPW_24GHZ_CHANNELS] = { /* nop out the list */ [0] = 0 }; u8 channel; while (channel_index < IPW_SCAN_CHANNELS - 1) { channel = priv->speed_scan[priv->speed_scan_pos]; if (channel == 0) { priv->speed_scan_pos = 0; channel = priv->speed_scan[0]; } if ((priv->status & STATUS_ASSOCIATED) && channel == priv->channel) { priv->speed_scan_pos++; continue; } /* If this channel has already been * added in scan, break from loop * and this will be the first channel * in the next scan. */ if (channels[channel - 1] != 0) break; channels[channel - 1] = 1; priv->speed_scan_pos++; channel_index++; scan->channels_list[channel_index] = channel; index = libipw_channel_to_index(priv->ieee, channel); ipw_set_scan_type(scan, channel_index, geo->bg[index]. flags & LIBIPW_CH_PASSIVE_ONLY ? IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : scan_type); } } else { for (i = 0; i < geo->bg_channels; i++) { if ((priv->status & STATUS_ASSOCIATED) && geo->bg[i].channel == priv->channel) continue; channel_index++; scan->channels_list[channel_index] = geo->bg[i].channel; ipw_set_scan_type(scan, channel_index, geo->bg[i]. flags & LIBIPW_CH_PASSIVE_ONLY ? IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : scan_type); } } if (start != channel_index) { scan->channels_list[start] = (u8) (IPW_B_MODE << 6) | (channel_index - start); } } } static int ipw_passive_dwell_time(struct ipw_priv *priv) { /* staying on passive channels longer than the DTIM interval during a * scan, while associated, causes the firmware to cancel the scan * without notification. Hence, don't stay on passive channels longer * than the beacon interval. */ if (priv->status & STATUS_ASSOCIATED && priv->assoc_network->beacon_interval > 10) return priv->assoc_network->beacon_interval - 10; else return 120; } static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct) { struct ipw_scan_request_ext scan; int err = 0, scan_type; if (!(priv->status & STATUS_INIT) || (priv->status & STATUS_EXIT_PENDING)) return 0; mutex_lock(&priv->mutex); if (direct && (priv->direct_scan_ssid_len == 0)) { IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n"); priv->status &= ~STATUS_DIRECT_SCAN_PENDING; goto done; } if (priv->status & STATUS_SCANNING) { IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n"); priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : STATUS_SCAN_PENDING; goto done; } if (!(priv->status & STATUS_SCAN_FORCED) && priv->status & STATUS_SCAN_ABORTING) { IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n"); priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : STATUS_SCAN_PENDING; goto done; } if (priv->status & STATUS_RF_KILL_MASK) { IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n"); priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : STATUS_SCAN_PENDING; goto done; } memset(&scan, 0, sizeof(scan)); scan.full_scan_index = cpu_to_le32(libipw_get_scans(priv->ieee)); if (type == IW_SCAN_TYPE_PASSIVE) { IPW_DEBUG_WX("use passive scanning\n"); scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN; scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(ipw_passive_dwell_time(priv)); ipw_add_scan_channels(priv, &scan, scan_type); goto send_request; } /* Use active scan by default. */ if (priv->config & CFG_SPEED_SCAN) scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = cpu_to_le16(30); else scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = cpu_to_le16(20); scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = cpu_to_le16(20); scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(ipw_passive_dwell_time(priv)); scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20); #ifdef CONFIG_IPW2200_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) { u8 channel; u8 band = 0; switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { case LIBIPW_52GHZ_BAND: band = (u8) (IPW_A_MODE << 6) | 1; channel = priv->channel; break; case LIBIPW_24GHZ_BAND: band = (u8) (IPW_B_MODE << 6) | 1; channel = priv->channel; break; default: band = (u8) (IPW_B_MODE << 6) | 1; channel = 9; break; } scan.channels_list[0] = band; scan.channels_list[1] = channel; ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN); /* NOTE: The card will sit on this channel for this time * period. Scan aborts are timing sensitive and frequently * result in firmware restarts. As such, it is best to * set a small dwell_time here and just keep re-issuing * scans. Otherwise fast channel hopping will not actually * hop channels. * * TODO: Move SPEED SCAN support to all modes and bands */ scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(2000); } else { #endif /* CONFIG_IPW2200_MONITOR */ /* Honor direct scans first, otherwise if we are roaming make * this a direct scan for the current network. Finally, * ensure that every other scan is a fast channel hop scan */ if (direct) { err = ipw_send_ssid(priv, priv->direct_scan_ssid, priv->direct_scan_ssid_len); if (err) { IPW_DEBUG_HC("Attempt to send SSID command " "failed\n"); goto done; } scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN; } else if ((priv->status & STATUS_ROAMING) || (!(priv->status & STATUS_ASSOCIATED) && (priv->config & CFG_STATIC_ESSID) && (le32_to_cpu(scan.full_scan_index) % 2))) { err = ipw_send_ssid(priv, priv->essid, priv->essid_len); if (err) { IPW_DEBUG_HC("Attempt to send SSID command " "failed.\n"); goto done; } scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN; } else scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN; ipw_add_scan_channels(priv, &scan, scan_type); #ifdef CONFIG_IPW2200_MONITOR } #endif send_request: err = ipw_send_scan_request_ext(priv, &scan); if (err) { IPW_DEBUG_HC("Sending scan command failed: %08X\n", err); goto done; } priv->status |= STATUS_SCANNING; if (direct) { priv->status &= ~STATUS_DIRECT_SCAN_PENDING; priv->direct_scan_ssid_len = 0; } else priv->status &= ~STATUS_SCAN_PENDING; schedule_delayed_work(&priv->scan_check, IPW_SCAN_CHECK_WATCHDOG); done: mutex_unlock(&priv->mutex); return err; } static void ipw_request_passive_scan(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, request_passive_scan.work); ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0); } static void ipw_request_scan(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, request_scan.work); ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0); } static void ipw_request_direct_scan(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, request_direct_scan.work); ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1); } static void ipw_bg_abort_scan(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, abort_scan); mutex_lock(&priv->mutex); ipw_abort_scan(priv); mutex_unlock(&priv->mutex); } static int ipw_wpa_enable(struct ipw_priv *priv, int value) { /* This is called when wpa_supplicant loads and closes the driver * interface. */ priv->ieee->wpa_enabled = value; return 0; } static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value) { struct libipw_device *ieee = priv->ieee; struct libipw_security sec = { .flags = SEC_AUTH_MODE, }; int ret = 0; if (value & IW_AUTH_ALG_SHARED_KEY) { sec.auth_mode = WLAN_AUTH_SHARED_KEY; ieee->open_wep = 0; } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) { sec.auth_mode = WLAN_AUTH_OPEN; ieee->open_wep = 1; } else if (value & IW_AUTH_ALG_LEAP) { sec.auth_mode = WLAN_AUTH_LEAP; ieee->open_wep = 1; } else return -EINVAL; if (ieee->set_security) ieee->set_security(ieee->dev, &sec); else ret = -EOPNOTSUPP; return ret; } static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie, int wpa_ie_len) { /* make sure WPA is enabled */ ipw_wpa_enable(priv, 1); } static int ipw_set_rsn_capa(struct ipw_priv *priv, char *capabilities, int length) { IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n"); return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length, capabilities); } /* * WE-18 support */ /* SIOCSIWGENIE */ static int ipw_wx_set_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; u8 *buf; int err = 0; if (wrqu->data.length > MAX_WPA_IE_LEN || (wrqu->data.length && extra == NULL)) return -EINVAL; if (wrqu->data.length) { buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); if (buf == NULL) { err = -ENOMEM; goto out; } kfree(ieee->wpa_ie); ieee->wpa_ie = buf; ieee->wpa_ie_len = wrqu->data.length; } else { kfree(ieee->wpa_ie); ieee->wpa_ie = NULL; ieee->wpa_ie_len = 0; } ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); out: return err; } /* SIOCGIWGENIE */ static int ipw_wx_get_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; int err = 0; if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { wrqu->data.length = 0; goto out; } if (wrqu->data.length < ieee->wpa_ie_len) { err = -E2BIG; goto out; } wrqu->data.length = ieee->wpa_ie_len; memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); out: return err; } static int wext_cipher2level(int cipher) { switch (cipher) { case IW_AUTH_CIPHER_NONE: return SEC_LEVEL_0; case IW_AUTH_CIPHER_WEP40: case IW_AUTH_CIPHER_WEP104: return SEC_LEVEL_1; case IW_AUTH_CIPHER_TKIP: return SEC_LEVEL_2; case IW_AUTH_CIPHER_CCMP: return SEC_LEVEL_3; default: return -1; } } /* SIOCSIWAUTH */ static int ipw_wx_set_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; struct iw_param *param = &wrqu->param; struct lib80211_crypt_data *crypt; unsigned long flags; int ret = 0; switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: break; case IW_AUTH_CIPHER_PAIRWISE: ipw_set_hw_decrypt_unicast(priv, wext_cipher2level(param->value)); break; case IW_AUTH_CIPHER_GROUP: ipw_set_hw_decrypt_multicast(priv, wext_cipher2level(param->value)); break; case IW_AUTH_KEY_MGMT: /* * ipw2200 does not use these parameters */ break; case IW_AUTH_TKIP_COUNTERMEASURES: crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) break; flags = crypt->ops->get_flags(crypt->priv); if (param->value) flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; else flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; crypt->ops->set_flags(flags, crypt->priv); break; case IW_AUTH_DROP_UNENCRYPTED:{ /* HACK: * * wpa_supplicant calls set_wpa_enabled when the driver * is loaded and unloaded, regardless of if WPA is being * used. No other calls are made which can be used to * determine if encryption will be used or not prior to * association being expected. If encryption is not being * used, drop_unencrypted is set to false, else true -- we * can use this to determine if the CAP_PRIVACY_ON bit should * be set. */ struct libipw_security sec = { .flags = SEC_ENABLED, .enabled = param->value, }; priv->ieee->drop_unencrypted = param->value; /* We only change SEC_LEVEL for open mode. Others * are set by ipw_wpa_set_encryption. */ if (!param->value) { sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_0; } else { sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_1; } if (priv->ieee->set_security) priv->ieee->set_security(priv->ieee->dev, &sec); break; } case IW_AUTH_80211_AUTH_ALG: ret = ipw_wpa_set_auth_algs(priv, param->value); break; case IW_AUTH_WPA_ENABLED: ret = ipw_wpa_enable(priv, param->value); ipw_disassociate(priv); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: ieee->ieee802_1x = param->value; break; case IW_AUTH_PRIVACY_INVOKED: ieee->privacy_invoked = param->value; break; default: return -EOPNOTSUPP; } return ret; } /* SIOCGIWAUTH */ static int ipw_wx_get_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct libipw_device *ieee = priv->ieee; struct lib80211_crypt_data *crypt; struct iw_param *param = &wrqu->param; int ret = 0; switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: case IW_AUTH_CIPHER_PAIRWISE: case IW_AUTH_CIPHER_GROUP: case IW_AUTH_KEY_MGMT: /* * wpa_supplicant will control these internally */ ret = -EOPNOTSUPP; break; case IW_AUTH_TKIP_COUNTERMEASURES: crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; if (!crypt || !crypt->ops->get_flags) break; param->value = (crypt->ops->get_flags(crypt->priv) & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0; break; case IW_AUTH_DROP_UNENCRYPTED: param->value = ieee->drop_unencrypted; break; case IW_AUTH_80211_AUTH_ALG: param->value = ieee->sec.auth_mode; break; case IW_AUTH_WPA_ENABLED: param->value = ieee->wpa_enabled; break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: param->value = ieee->ieee802_1x; break; case IW_AUTH_ROAMING_CONTROL: case IW_AUTH_PRIVACY_INVOKED: param->value = ieee->privacy_invoked; break; default: return -EOPNOTSUPP; } return 0; } /* SIOCSIWENCODEEXT */ static int ipw_wx_set_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; if (hwcrypto) { if (ext->alg == IW_ENCODE_ALG_TKIP) { /* IPW HW can't build TKIP MIC, host decryption still needed */ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) priv->ieee->host_mc_decrypt = 1; else { priv->ieee->host_encrypt = 0; priv->ieee->host_encrypt_msdu = 1; priv->ieee->host_decrypt = 1; } } else { priv->ieee->host_encrypt = 0; priv->ieee->host_encrypt_msdu = 0; priv->ieee->host_decrypt = 0; priv->ieee->host_mc_decrypt = 0; } } return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCGIWENCODEEXT */ static int ipw_wx_get_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCSIWMLME */ static int ipw_wx_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct iw_mlme *mlme = (struct iw_mlme *)extra; __le16 reason; reason = cpu_to_le16(mlme->reason_code); switch (mlme->cmd) { case IW_MLME_DEAUTH: /* silently ignore */ break; case IW_MLME_DISASSOC: ipw_disassociate(priv); break; default: return -EOPNOTSUPP; } return 0; } #ifdef CONFIG_IPW2200_QOS /* QoS */ /* * get the modulation type of the current network or * the card current mode */ static u8 ipw_qos_current_mode(struct ipw_priv * priv) { u8 mode = 0; if (priv->status & STATUS_ASSOCIATED) { unsigned long flags; spin_lock_irqsave(&priv->ieee->lock, flags); mode = priv->assoc_network->mode; spin_unlock_irqrestore(&priv->ieee->lock, flags); } else { mode = priv->ieee->mode; } IPW_DEBUG_QOS("QoS network/card mode %d\n", mode); return mode; } /* * Handle management frame beacon and probe response */ static int ipw_qos_handle_probe_response(struct ipw_priv *priv, int active_network, struct libipw_network *network) { u32 size = sizeof(struct libipw_qos_parameters); if (network->capability & WLAN_CAPABILITY_IBSS) network->qos_data.active = network->qos_data.supported; if (network->flags & NETWORK_HAS_QOS_MASK) { if (active_network && (network->flags & NETWORK_HAS_QOS_PARAMETERS)) network->qos_data.active = network->qos_data.supported; if ((network->qos_data.active == 1) && (active_network == 1) && (network->flags & NETWORK_HAS_QOS_PARAMETERS) && (network->qos_data.old_param_count != network->qos_data.param_count)) { network->qos_data.old_param_count = network->qos_data.param_count; schedule_work(&priv->qos_activate); IPW_DEBUG_QOS("QoS parameters change call " "qos_activate\n"); } } else { if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B)) memcpy(&network->qos_data.parameters, &def_parameters_CCK, size); else memcpy(&network->qos_data.parameters, &def_parameters_OFDM, size); if ((network->qos_data.active == 1) && (active_network == 1)) { IPW_DEBUG_QOS("QoS was disabled call qos_activate\n"); schedule_work(&priv->qos_activate); } network->qos_data.active = 0; network->qos_data.supported = 0; } if ((priv->status & STATUS_ASSOCIATED) && (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) { if (memcmp(network->bssid, priv->bssid, ETH_ALEN)) if (network->capability & WLAN_CAPABILITY_IBSS) if ((network->ssid_len == priv->assoc_network->ssid_len) && !memcmp(network->ssid, priv->assoc_network->ssid, network->ssid_len)) { schedule_work(&priv->merge_networks); } } return 0; } /* * This function set up the firmware to support QoS. It sends * IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO */ static int ipw_qos_activate(struct ipw_priv *priv, struct libipw_qos_data *qos_network_data) { int err; struct libipw_qos_parameters qos_parameters[QOS_QOS_SETS]; struct libipw_qos_parameters *active_one = NULL; u32 size = sizeof(struct libipw_qos_parameters); u32 burst_duration; int i; u8 type; type = ipw_qos_current_mode(priv); active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]); memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size); active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]); memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size); if (qos_network_data == NULL) { if (type == IEEE_B) { IPW_DEBUG_QOS("QoS activate network mode %d\n", type); active_one = &def_parameters_CCK; } else active_one = &def_parameters_OFDM; memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); burst_duration = ipw_qos_get_burst_duration(priv); for (i = 0; i < QOS_QUEUE_NUM; i++) qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] = cpu_to_le16(burst_duration); } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) { if (type == IEEE_B) { IPW_DEBUG_QOS("QoS activate IBSS network mode %d\n", type); if (priv->qos_data.qos_enable == 0) active_one = &def_parameters_CCK; else active_one = priv->qos_data.def_qos_parm_CCK; } else { if (priv->qos_data.qos_enable == 0) active_one = &def_parameters_OFDM; else active_one = priv->qos_data.def_qos_parm_OFDM; } memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); } else { unsigned long flags; int active; spin_lock_irqsave(&priv->ieee->lock, flags); active_one = &(qos_network_data->parameters); qos_network_data->old_param_count = qos_network_data->param_count; memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); active = qos_network_data->supported; spin_unlock_irqrestore(&priv->ieee->lock, flags); if (active == 0) { burst_duration = ipw_qos_get_burst_duration(priv); for (i = 0; i < QOS_QUEUE_NUM; i++) qos_parameters[QOS_PARAM_SET_ACTIVE]. tx_op_limit[i] = cpu_to_le16(burst_duration); } } IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n"); err = ipw_send_qos_params_command(priv, &qos_parameters[0]); if (err) IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n"); return err; } /* * send IPW_CMD_WME_INFO to the firmware */ static int ipw_qos_set_info_element(struct ipw_priv *priv) { int ret = 0; struct libipw_qos_information_element qos_info; if (priv == NULL) return -1; qos_info.elementID = QOS_ELEMENT_ID; qos_info.length = sizeof(struct libipw_qos_information_element) - 2; qos_info.version = QOS_VERSION_1; qos_info.ac_info = 0; memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN); qos_info.qui_type = QOS_OUI_TYPE; qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE; ret = ipw_send_qos_info_command(priv, &qos_info); if (ret != 0) { IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n"); } return ret; } /* * Set the QoS parameter with the association request structure */ static int ipw_qos_association(struct ipw_priv *priv, struct libipw_network *network) { int err = 0; struct libipw_qos_data *qos_data = NULL; struct libipw_qos_data ibss_data = { .supported = 1, .active = 1, }; switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS)); qos_data = &ibss_data; break; case IW_MODE_INFRA: qos_data = &network->qos_data; break; default: BUG(); break; } err = ipw_qos_activate(priv, qos_data); if (err) { priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC; return err; } if (priv->qos_data.qos_enable && qos_data->supported) { IPW_DEBUG_QOS("QoS will be enabled for this association\n"); priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC; return ipw_qos_set_info_element(priv); } return 0; } /* * handling the beaconing responses. if we get different QoS setting * off the network from the associated setting, adjust the QoS * setting */ static int ipw_qos_association_resp(struct ipw_priv *priv, struct libipw_network *network) { int ret = 0; unsigned long flags; u32 size = sizeof(struct libipw_qos_parameters); int set_qos_param = 0; if ((priv == NULL) || (network == NULL) || (priv->assoc_network == NULL)) return ret; if (!(priv->status & STATUS_ASSOCIATED)) return ret; if ((priv->ieee->iw_mode != IW_MODE_INFRA)) return ret; spin_lock_irqsave(&priv->ieee->lock, flags); if (network->flags & NETWORK_HAS_QOS_PARAMETERS) { memcpy(&priv->assoc_network->qos_data, &network->qos_data, sizeof(struct libipw_qos_data)); priv->assoc_network->qos_data.active = 1; if ((network->qos_data.old_param_count != network->qos_data.param_count)) { set_qos_param = 1; network->qos_data.old_param_count = network->qos_data.param_count; } } else { if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B)) memcpy(&priv->assoc_network->qos_data.parameters, &def_parameters_CCK, size); else memcpy(&priv->assoc_network->qos_data.parameters, &def_parameters_OFDM, size); priv->assoc_network->qos_data.active = 0; priv->assoc_network->qos_data.supported = 0; set_qos_param = 1; } spin_unlock_irqrestore(&priv->ieee->lock, flags); if (set_qos_param == 1) schedule_work(&priv->qos_activate); return ret; } static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv) { u32 ret = 0; if ((priv == NULL)) return 0; if (!(priv->ieee->modulation & LIBIPW_OFDM_MODULATION)) ret = priv->qos_data.burst_duration_CCK; else ret = priv->qos_data.burst_duration_OFDM; return ret; } /* * Initialize the setting of QoS global */ static void ipw_qos_init(struct ipw_priv *priv, int enable, int burst_enable, u32 burst_duration_CCK, u32 burst_duration_OFDM) { priv->qos_data.qos_enable = enable; if (priv->qos_data.qos_enable) { priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK; priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM; IPW_DEBUG_QOS("QoS is enabled\n"); } else { priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK; priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM; IPW_DEBUG_QOS("QoS is not enabled\n"); } priv->qos_data.burst_enable = burst_enable; if (burst_enable) { priv->qos_data.burst_duration_CCK = burst_duration_CCK; priv->qos_data.burst_duration_OFDM = burst_duration_OFDM; } else { priv->qos_data.burst_duration_CCK = 0; priv->qos_data.burst_duration_OFDM = 0; } } /* * map the packet priority to the right TX Queue */ static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority) { if (priority > 7 || !priv->qos_data.qos_enable) priority = 0; return from_priority_to_tx_queue[priority] - 1; } static int ipw_is_qos_active(struct net_device *dev, struct sk_buff *skb) { struct ipw_priv *priv = libipw_priv(dev); struct libipw_qos_data *qos_data = NULL; int active, supported; u8 *daddr = skb->data + ETH_ALEN; int unicast = !is_multicast_ether_addr(daddr); if (!(priv->status & STATUS_ASSOCIATED)) return 0; qos_data = &priv->assoc_network->qos_data; if (priv->ieee->iw_mode == IW_MODE_ADHOC) { if (unicast == 0) qos_data->active = 0; else qos_data->active = qos_data->supported; } active = qos_data->active; supported = qos_data->supported; IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d " "unicast %d\n", priv->qos_data.qos_enable, active, supported, unicast); if (active && priv->qos_data.qos_enable) return 1; return 0; } /* * add QoS parameter to the TX command */ static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv, u16 priority, struct tfd_data *tfd) { int tx_queue_id = 0; tx_queue_id = from_priority_to_tx_queue[priority] - 1; tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED; if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) { tfd->tx_flags &= ~DCT_FLAG_ACK_REQD; tfd->tfd.tfd_26.mchdr.qos_ctrl |= cpu_to_le16(CTRL_QOS_NO_ACK); } return 0; } /* * background support to run QoS activate functionality */ static void ipw_bg_qos_activate(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, qos_activate); mutex_lock(&priv->mutex); if (priv->status & STATUS_ASSOCIATED) ipw_qos_activate(priv, &(priv->assoc_network->qos_data)); mutex_unlock(&priv->mutex); } static int ipw_handle_probe_response(struct net_device *dev, struct libipw_probe_response *resp, struct libipw_network *network) { struct ipw_priv *priv = libipw_priv(dev); int active_network = ((priv->status & STATUS_ASSOCIATED) && (network == priv->assoc_network)); ipw_qos_handle_probe_response(priv, active_network, network); return 0; } static int ipw_handle_beacon(struct net_device *dev, struct libipw_beacon *resp, struct libipw_network *network) { struct ipw_priv *priv = libipw_priv(dev); int active_network = ((priv->status & STATUS_ASSOCIATED) && (network == priv->assoc_network)); ipw_qos_handle_probe_response(priv, active_network, network); return 0; } static int ipw_handle_assoc_response(struct net_device *dev, struct libipw_assoc_response *resp, struct libipw_network *network) { struct ipw_priv *priv = libipw_priv(dev); ipw_qos_association_resp(priv, network); return 0; } static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters *qos_param) { return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS, sizeof(*qos_param) * 3, qos_param); } static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element *qos_param) { return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, sizeof(*qos_param), qos_param); } #endif /* CONFIG_IPW2200_QOS */ static int ipw_associate_network(struct ipw_priv *priv, struct libipw_network *network, struct ipw_supported_rates *rates, int roaming) { int err; DECLARE_SSID_BUF(ssid); if (priv->config & CFG_FIXED_RATE) ipw_set_fixed_rate(priv, network->mode); if (!(priv->config & CFG_STATIC_ESSID)) { priv->essid_len = min(network->ssid_len, (u8) IW_ESSID_MAX_SIZE); memcpy(priv->essid, network->ssid, priv->essid_len); } network->last_associate = jiffies; memset(&priv->assoc_request, 0, sizeof(priv->assoc_request)); priv->assoc_request.channel = network->channel; priv->assoc_request.auth_key = 0; if ((priv->capability & CAP_PRIVACY_ON) && (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) { priv->assoc_request.auth_type = AUTH_SHARED_KEY; priv->assoc_request.auth_key = priv->ieee->sec.active_key; if (priv->ieee->sec.level == SEC_LEVEL_1) ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); } else if ((priv->capability & CAP_PRIVACY_ON) && (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)) priv->assoc_request.auth_type = AUTH_LEAP; else priv->assoc_request.auth_type = AUTH_OPEN; if (priv->ieee->wpa_ie_len) { priv->assoc_request.policy_support = cpu_to_le16(0x02); /* RSN active */ ipw_set_rsn_capa(priv, priv->ieee->wpa_ie, priv->ieee->wpa_ie_len); } /* * It is valid for our ieee device to support multiple modes, but * when it comes to associating to a given network we have to choose * just one mode. */ if (network->mode & priv->ieee->mode & IEEE_A) priv->assoc_request.ieee_mode = IPW_A_MODE; else if (network->mode & priv->ieee->mode & IEEE_G) priv->assoc_request.ieee_mode = IPW_G_MODE; else if (network->mode & priv->ieee->mode & IEEE_B) priv->assoc_request.ieee_mode = IPW_B_MODE; priv->assoc_request.capability = cpu_to_le16(network->capability); if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && !(priv->config & CFG_PREAMBLE_LONG)) { priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE; } else { priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE; /* Clear the short preamble if we won't be supporting it */ priv->assoc_request.capability &= ~cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); } /* Clear capability bits that aren't used in Ad Hoc */ if (priv->ieee->iw_mode == IW_MODE_ADHOC) priv->assoc_request.capability &= ~cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME); IPW_DEBUG_ASSOC("%ssociation attempt: '%s', channel %d, " "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n", roaming ? "Rea" : "A", print_ssid(ssid, priv->essid, priv->essid_len), network->channel, ipw_modes[priv->assoc_request.ieee_mode], rates->num_rates, (priv->assoc_request.preamble_length == DCT_FLAG_LONG_PREAMBLE) ? "long" : "short", network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long", priv->capability & CAP_PRIVACY_ON ? "on " : "off", priv->capability & CAP_PRIVACY_ON ? (priv->capability & CAP_SHARED_KEY ? "(shared)" : "(open)") : "", priv->capability & CAP_PRIVACY_ON ? " key=" : "", priv->capability & CAP_PRIVACY_ON ? '1' + priv->ieee->sec.active_key : '.', priv->capability & CAP_PRIVACY_ON ? '.' : ' '); priv->assoc_request.beacon_interval = cpu_to_le16(network->beacon_interval); if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) { priv->assoc_request.assoc_type = HC_IBSS_START; priv->assoc_request.assoc_tsf_msw = 0; priv->assoc_request.assoc_tsf_lsw = 0; } else { if (unlikely(roaming)) priv->assoc_request.assoc_type = HC_REASSOCIATE; else priv->assoc_request.assoc_type = HC_ASSOCIATE; priv->assoc_request.assoc_tsf_msw = cpu_to_le32(network->time_stamp[1]); priv->assoc_request.assoc_tsf_lsw = cpu_to_le32(network->time_stamp[0]); } memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN); if (priv->ieee->iw_mode == IW_MODE_ADHOC) { memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN); priv->assoc_request.atim_window = cpu_to_le16(network->atim_window); } else { memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN); priv->assoc_request.atim_window = 0; } priv->assoc_request.listen_interval = cpu_to_le16(network->listen_interval); err = ipw_send_ssid(priv, priv->essid, priv->essid_len); if (err) { IPW_DEBUG_HC("Attempt to send SSID command failed.\n"); return err; } rates->ieee_mode = priv->assoc_request.ieee_mode; rates->purpose = IPW_RATE_CONNECT; ipw_send_supported_rates(priv, rates); if (priv->assoc_request.ieee_mode == IPW_G_MODE) priv->sys_config.dot11g_auto_detection = 1; else priv->sys_config.dot11g_auto_detection = 0; if (priv->ieee->iw_mode == IW_MODE_ADHOC) priv->sys_config.answer_broadcast_ssid_probe = 1; else priv->sys_config.answer_broadcast_ssid_probe = 0; err = ipw_send_system_config(priv); if (err) { IPW_DEBUG_HC("Attempt to send sys config command failed.\n"); return err; } IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi); err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM); if (err) { IPW_DEBUG_HC("Attempt to send associate command failed.\n"); return err; } /* * If preemption is enabled, it is possible for the association * to complete before we return from ipw_send_associate. Therefore * we have to be sure and update our priviate data first. */ priv->channel = network->channel; memcpy(priv->bssid, network->bssid, ETH_ALEN); priv->status |= STATUS_ASSOCIATING; priv->status &= ~STATUS_SECURITY_UPDATED; priv->assoc_network = network; #ifdef CONFIG_IPW2200_QOS ipw_qos_association(priv, network); #endif err = ipw_send_associate(priv, &priv->assoc_request); if (err) { IPW_DEBUG_HC("Attempt to send associate command failed.\n"); return err; } IPW_DEBUG(IPW_DL_STATE, "associating: '%s' %pM\n", print_ssid(ssid, priv->essid, priv->essid_len), priv->bssid); return 0; } static void ipw_roam(void *data) { struct ipw_priv *priv = data; struct libipw_network *network = NULL; struct ipw_network_match match = { .network = priv->assoc_network }; /* The roaming process is as follows: * * 1. Missed beacon threshold triggers the roaming process by * setting the status ROAM bit and requesting a scan. * 2. When the scan completes, it schedules the ROAM work * 3. The ROAM work looks at all of the known networks for one that * is a better network than the currently associated. If none * found, the ROAM process is over (ROAM bit cleared) * 4. If a better network is found, a disassociation request is * sent. * 5. When the disassociation completes, the roam work is again * scheduled. The second time through, the driver is no longer * associated, and the newly selected network is sent an * association request. * 6. At this point ,the roaming process is complete and the ROAM * status bit is cleared. */ /* If we are no longer associated, and the roaming bit is no longer * set, then we are not actively roaming, so just return */ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING))) return; if (priv->status & STATUS_ASSOCIATED) { /* First pass through ROAM process -- look for a better * network */ unsigned long flags; u8 rssi = priv->assoc_network->stats.rssi; priv->assoc_network->stats.rssi = -128; spin_lock_irqsave(&priv->ieee->lock, flags); list_for_each_entry(network, &priv->ieee->network_list, list) { if (network != priv->assoc_network) ipw_best_network(priv, &match, network, 1); } spin_unlock_irqrestore(&priv->ieee->lock, flags); priv->assoc_network->stats.rssi = rssi; if (match.network == priv->assoc_network) { IPW_DEBUG_ASSOC("No better APs in this network to " "roam to.\n"); priv->status &= ~STATUS_ROAMING; ipw_debug_config(priv); return; } ipw_send_disassociate(priv, 1); priv->assoc_network = match.network; return; } /* Second pass through ROAM process -- request association */ ipw_compatible_rates(priv, priv->assoc_network, &match.rates); ipw_associate_network(priv, priv->assoc_network, &match.rates, 1); priv->status &= ~STATUS_ROAMING; } static void ipw_bg_roam(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, roam); mutex_lock(&priv->mutex); ipw_roam(priv); mutex_unlock(&priv->mutex); } static int ipw_associate(void *data) { struct ipw_priv *priv = data; struct libipw_network *network = NULL; struct ipw_network_match match = { .network = NULL }; struct ipw_supported_rates *rates; struct list_head *element; unsigned long flags; DECLARE_SSID_BUF(ssid); if (priv->ieee->iw_mode == IW_MODE_MONITOR) { IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n"); return 0; } if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { IPW_DEBUG_ASSOC("Not attempting association (already in " "progress)\n"); return 0; } if (priv->status & STATUS_DISASSOCIATING) { IPW_DEBUG_ASSOC("Not attempting association (in " "disassociating)\n "); schedule_work(&priv->associate); return 0; } if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) { IPW_DEBUG_ASSOC("Not attempting association (scanning or not " "initialized)\n"); return 0; } if (!(priv->config & CFG_ASSOCIATE) && !(priv->config & (CFG_STATIC_ESSID | CFG_STATIC_BSSID))) { IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n"); return 0; } /* Protect our use of the network_list */ spin_lock_irqsave(&priv->ieee->lock, flags); list_for_each_entry(network, &priv->ieee->network_list, list) ipw_best_network(priv, &match, network, 0); network = match.network; rates = &match.rates; if (network == NULL && priv->ieee->iw_mode == IW_MODE_ADHOC && priv->config & CFG_ADHOC_CREATE && priv->config & CFG_STATIC_ESSID && priv->config & CFG_STATIC_CHANNEL) { /* Use oldest network if the free list is empty */ if (list_empty(&priv->ieee->network_free_list)) { struct libipw_network *oldest = NULL; struct libipw_network *target; list_for_each_entry(target, &priv->ieee->network_list, list) { if ((oldest == NULL) || (target->last_scanned < oldest->last_scanned)) oldest = target; } /* If there are no more slots, expire the oldest */ list_del(&oldest->list); target = oldest; IPW_DEBUG_ASSOC("Expired '%s' (%pM) from " "network list.\n", print_ssid(ssid, target->ssid, target->ssid_len), target->bssid); list_add_tail(&target->list, &priv->ieee->network_free_list); } element = priv->ieee->network_free_list.next; network = list_entry(element, struct libipw_network, list); ipw_adhoc_create(priv, network); rates = &priv->rates; list_del(element); list_add_tail(&network->list, &priv->ieee->network_list); } spin_unlock_irqrestore(&priv->ieee->lock, flags); /* If we reached the end of the list, then we don't have any valid * matching APs */ if (!network) { ipw_debug_config(priv); if (!(priv->status & STATUS_SCANNING)) { if (!(priv->config & CFG_SPEED_SCAN)) schedule_delayed_work(&priv->request_scan, SCAN_INTERVAL); else schedule_delayed_work(&priv->request_scan, 0); } return 0; } ipw_associate_network(priv, network, rates, 0); return 1; } static void ipw_bg_associate(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, associate); mutex_lock(&priv->mutex); ipw_associate(priv); mutex_unlock(&priv->mutex); } static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv, struct sk_buff *skb) { struct ieee80211_hdr *hdr; u16 fc; hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); if (!(fc & IEEE80211_FCTL_PROTECTED)) return; fc &= ~IEEE80211_FCTL_PROTECTED; hdr->frame_control = cpu_to_le16(fc); switch (priv->ieee->sec.level) { case SEC_LEVEL_3: /* Remove CCMP HDR */ memmove(skb->data + LIBIPW_3ADDR_LEN, skb->data + LIBIPW_3ADDR_LEN + 8, skb->len - LIBIPW_3ADDR_LEN - 8); skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */ break; case SEC_LEVEL_2: break; case SEC_LEVEL_1: /* Remove IV */ memmove(skb->data + LIBIPW_3ADDR_LEN, skb->data + LIBIPW_3ADDR_LEN + 4, skb->len - LIBIPW_3ADDR_LEN - 4); skb_trim(skb, skb->len - 8); /* IV + ICV */ break; case SEC_LEVEL_0: break; default: printk(KERN_ERR "Unknown security level %d\n", priv->ieee->sec.level); break; } } static void ipw_handle_data_packet(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; struct libipw_hdr_4addr *hdr; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; /* We received data from the HW, so stop the watchdog */ dev->trans_start = jiffies; /* We only process data packets if the * interface is open */ if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { dev->stats.rx_errors++; priv->wstats.discard.misc++; IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); return; } else if (unlikely(!netif_running(priv->net_dev))) { dev->stats.rx_dropped++; priv->wstats.discard.misc++; IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); return; } /* Advance skb->data to the start of the actual payload */ skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data)); /* Set the size of the skb to the size of the frame */ skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length)); IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */ hdr = (struct libipw_hdr_4addr *)rxb->skb->data; if (priv->ieee->iw_mode != IW_MODE_MONITOR && (is_multicast_ether_addr(hdr->addr1) ? !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt)) ipw_rebuild_decrypted_skb(priv, rxb->skb); if (!libipw_rx(priv->ieee, rxb->skb, stats)) dev->stats.rx_errors++; else { /* libipw_rx succeeded, so it now owns the SKB */ rxb->skb = NULL; __ipw_led_activity_on(priv); } } #ifdef CONFIG_IPW2200_RADIOTAP static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; struct ipw_rx_frame *frame = &pkt->u.frame; /* initial pull of some data */ u16 received_channel = frame->received_channel; u8 antennaAndPhy = frame->antennaAndPhy; s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */ u16 pktrate = frame->rate; /* Magic struct that slots into the radiotap header -- no reason * to build this manually element by element, we can write it much * more efficiently than we can parse it. ORDER MATTERS HERE */ struct ipw_rt_hdr *ipw_rt; unsigned short len = le16_to_cpu(pkt->u.frame.length); /* We received data from the HW, so stop the watchdog */ dev->trans_start = jiffies; /* We only process data packets if the * interface is open */ if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { dev->stats.rx_errors++; priv->wstats.discard.misc++; IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); return; } else if (unlikely(!netif_running(priv->net_dev))) { dev->stats.rx_dropped++; priv->wstats.discard.misc++; IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); return; } /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use * that now */ if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) { /* FIXME: Should alloc bigger skb instead */ dev->stats.rx_dropped++; priv->wstats.discard.misc++; IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); return; } /* copy the frame itself */ memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr), rxb->skb->data + IPW_RX_FRAME_SIZE, len); ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data; ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */ /* Big bitfield of all the fields we provide in radiotap */ ipw_rt->rt_hdr.it_present = cpu_to_le32( (1 << IEEE80211_RADIOTAP_TSFT) | (1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_RATE) | (1 << IEEE80211_RADIOTAP_CHANNEL) | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | (1 << IEEE80211_RADIOTAP_ANTENNA)); /* Zero the flags, we'll add to them as we go */ ipw_rt->rt_flags = 0; ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 | frame->parent_tsf[2] << 16 | frame->parent_tsf[1] << 8 | frame->parent_tsf[0]); /* Convert signal to DBM */ ipw_rt->rt_dbmsignal = antsignal; ipw_rt->rt_dbmnoise = (s8) le16_to_cpu(frame->noise); /* Convert the channel data and set the flags */ ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel)); if (received_channel > 14) { /* 802.11a */ ipw_rt->rt_chbitmask = cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); } else if (antennaAndPhy & 32) { /* 802.11b */ ipw_rt->rt_chbitmask = cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); } else { /* 802.11g */ ipw_rt->rt_chbitmask = cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ); } /* set the rate in multiples of 500k/s */ switch (pktrate) { case IPW_TX_RATE_1MB: ipw_rt->rt_rate = 2; break; case IPW_TX_RATE_2MB: ipw_rt->rt_rate = 4; break; case IPW_TX_RATE_5MB: ipw_rt->rt_rate = 10; break; case IPW_TX_RATE_6MB: ipw_rt->rt_rate = 12; break; case IPW_TX_RATE_9MB: ipw_rt->rt_rate = 18; break; case IPW_TX_RATE_11MB: ipw_rt->rt_rate = 22; break; case IPW_TX_RATE_12MB: ipw_rt->rt_rate = 24; break; case IPW_TX_RATE_18MB: ipw_rt->rt_rate = 36; break; case IPW_TX_RATE_24MB: ipw_rt->rt_rate = 48; break; case IPW_TX_RATE_36MB: ipw_rt->rt_rate = 72; break; case IPW_TX_RATE_48MB: ipw_rt->rt_rate = 96; break; case IPW_TX_RATE_54MB: ipw_rt->rt_rate = 108; break; default: ipw_rt->rt_rate = 0; break; } /* antenna number */ ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */ /* set the preamble flag if we have it */ if ((antennaAndPhy & 64)) ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; /* Set the size of the skb to the size of the frame */ skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr)); IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); if (!libipw_rx(priv->ieee, rxb->skb, stats)) dev->stats.rx_errors++; else { /* libipw_rx succeeded, so it now owns the SKB */ rxb->skb = NULL; /* no LED during capture */ } } #endif #ifdef CONFIG_IPW2200_PROMISCUOUS #define libipw_is_probe_response(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \ (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP ) #define libipw_is_management(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) #define libipw_is_control(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) #define libipw_is_data(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) #define libipw_is_assoc_request(fc) \ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ) #define libipw_is_reassoc_request(fc) \ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, struct libipw_rx_stats *stats) { struct net_device *dev = priv->prom_net_dev; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; struct ipw_rx_frame *frame = &pkt->u.frame; struct ipw_rt_hdr *ipw_rt; /* First cache any information we need before we overwrite * the information provided in the skb from the hardware */ struct ieee80211_hdr *hdr; u16 channel = frame->received_channel; u8 phy_flags = frame->antennaAndPhy; s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM; s8 noise = (s8) le16_to_cpu(frame->noise); u8 rate = frame->rate; unsigned short len = le16_to_cpu(pkt->u.frame.length); struct sk_buff *skb; int hdr_only = 0; u16 filter = priv->prom_priv->filter; /* If the filter is set to not include Rx frames then return */ if (filter & IPW_PROM_NO_RX) return; /* We received data from the HW, so stop the watchdog */ dev->trans_start = jiffies; if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { dev->stats.rx_errors++; IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); return; } /* We only process data packets if the interface is open */ if (unlikely(!netif_running(dev))) { dev->stats.rx_dropped++; IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); return; } /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use * that now */ if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) { /* FIXME: Should alloc bigger skb instead */ dev->stats.rx_dropped++; IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); return; } hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE; if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_MGMT) return; if (filter & IPW_PROM_MGMT_HEADER_ONLY) hdr_only = 1; } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_CTL) return; if (filter & IPW_PROM_CTL_HEADER_ONLY) hdr_only = 1; } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_DATA) return; if (filter & IPW_PROM_DATA_HEADER_ONLY) hdr_only = 1; } /* Copy the SKB since this is for the promiscuous side */ skb = skb_copy(rxb->skb, GFP_ATOMIC); if (skb == NULL) { IPW_ERROR("skb_clone failed for promiscuous copy.\n"); return; } /* copy the frame data to write after where the radiotap header goes */ ipw_rt = (void *)skb->data; if (hdr_only) len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); memcpy(ipw_rt->payload, hdr, len); ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */ /* Set the size of the skb to the size of the frame */ skb_put(skb, sizeof(*ipw_rt) + len); /* Big bitfield of all the fields we provide in radiotap */ ipw_rt->rt_hdr.it_present = cpu_to_le32( (1 << IEEE80211_RADIOTAP_TSFT) | (1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_RATE) | (1 << IEEE80211_RADIOTAP_CHANNEL) | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | (1 << IEEE80211_RADIOTAP_ANTENNA)); /* Zero the flags, we'll add to them as we go */ ipw_rt->rt_flags = 0; ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 | frame->parent_tsf[2] << 16 | frame->parent_tsf[1] << 8 | frame->parent_tsf[0]); /* Convert to DBM */ ipw_rt->rt_dbmsignal = signal; ipw_rt->rt_dbmnoise = noise; /* Convert the channel data and set the flags */ ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel)); if (channel > 14) { /* 802.11a */ ipw_rt->rt_chbitmask = cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); } else if (phy_flags & (1 << 5)) { /* 802.11b */ ipw_rt->rt_chbitmask = cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); } else { /* 802.11g */ ipw_rt->rt_chbitmask = cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ); } /* set the rate in multiples of 500k/s */ switch (rate) { case IPW_TX_RATE_1MB: ipw_rt->rt_rate = 2; break; case IPW_TX_RATE_2MB: ipw_rt->rt_rate = 4; break; case IPW_TX_RATE_5MB: ipw_rt->rt_rate = 10; break; case IPW_TX_RATE_6MB: ipw_rt->rt_rate = 12; break; case IPW_TX_RATE_9MB: ipw_rt->rt_rate = 18; break; case IPW_TX_RATE_11MB: ipw_rt->rt_rate = 22; break; case IPW_TX_RATE_12MB: ipw_rt->rt_rate = 24; break; case IPW_TX_RATE_18MB: ipw_rt->rt_rate = 36; break; case IPW_TX_RATE_24MB: ipw_rt->rt_rate = 48; break; case IPW_TX_RATE_36MB: ipw_rt->rt_rate = 72; break; case IPW_TX_RATE_48MB: ipw_rt->rt_rate = 96; break; case IPW_TX_RATE_54MB: ipw_rt->rt_rate = 108; break; default: ipw_rt->rt_rate = 0; break; } /* antenna number */ ipw_rt->rt_antenna = (phy_flags & 3); /* set the preamble flag if we have it */ if (phy_flags & (1 << 6)) ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len); if (!libipw_rx(priv->prom_priv->ieee, skb, stats)) { dev->stats.rx_errors++; dev_kfree_skb_any(skb); } } #endif static int is_network_packet(struct ipw_priv *priv, struct libipw_hdr_4addr *header) { /* Filter incoming packets to determine if they are targeted toward * this network, discarding packets coming from ourselves */ switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */ /* packets from our adapter are dropped (echo) */ if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN)) return 0; /* {broad,multi}cast packets to our BSSID go through */ if (is_multicast_ether_addr(header->addr1)) return !memcmp(header->addr3, priv->bssid, ETH_ALEN); /* packets to our adapter go through */ return !memcmp(header->addr1, priv->net_dev->dev_addr, ETH_ALEN); case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */ /* packets from our adapter are dropped (echo) */ if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN)) return 0; /* {broad,multi}cast packets to our BSS go through */ if (is_multicast_ether_addr(header->addr1)) return !memcmp(header->addr2, priv->bssid, ETH_ALEN); /* packets to our adapter go through */ return !memcmp(header->addr1, priv->net_dev->dev_addr, ETH_ALEN); } return 1; } #define IPW_PACKET_RETRY_TIME HZ static int is_duplicate_packet(struct ipw_priv *priv, struct libipw_hdr_4addr *header) { u16 sc = le16_to_cpu(header->seq_ctl); u16 seq = WLAN_GET_SEQ_SEQ(sc); u16 frag = WLAN_GET_SEQ_FRAG(sc); u16 *last_seq, *last_frag; unsigned long *last_time; switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: { struct list_head *p; struct ipw_ibss_seq *entry = NULL; u8 *mac = header->addr2; int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE; __list_for_each(p, &priv->ibss_mac_hash[index]) { entry = list_entry(p, struct ipw_ibss_seq, list); if (!memcmp(entry->mac, mac, ETH_ALEN)) break; } if (p == &priv->ibss_mac_hash[index]) { entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) { IPW_ERROR ("Cannot malloc new mac entry\n"); return 0; } memcpy(entry->mac, mac, ETH_ALEN); entry->seq_num = seq; entry->frag_num = frag; entry->packet_time = jiffies; list_add(&entry->list, &priv->ibss_mac_hash[index]); return 0; } last_seq = &entry->seq_num; last_frag = &entry->frag_num; last_time = &entry->packet_time; break; } case IW_MODE_INFRA: last_seq = &priv->last_seq_num; last_frag = &priv->last_frag_num; last_time = &priv->last_packet_time; break; default: return 0; } if ((*last_seq == seq) && time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) { if (*last_frag == frag) goto drop; if (*last_frag + 1 != frag) /* out-of-order fragment */ goto drop; } else *last_seq = seq; *last_frag = frag; *last_time = jiffies; return 0; drop: /* Comment this line now since we observed the card receives * duplicate packets but the FCTL_RETRY bit is not set in the * IBSS mode with fragmentation enabled. BUG_ON(!(le16_to_cpu(header->frame_control) & IEEE80211_FCTL_RETRY)); */ return 1; } static void ipw_handle_mgmt_packet(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, struct libipw_rx_stats *stats) { struct sk_buff *skb = rxb->skb; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data; struct libipw_hdr_4addr *header = (struct libipw_hdr_4addr *) (skb->data + IPW_RX_FRAME_SIZE); libipw_rx_mgt(priv->ieee, header, stats); if (priv->ieee->iw_mode == IW_MODE_ADHOC && ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == IEEE80211_STYPE_PROBE_RESP) || (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == IEEE80211_STYPE_BEACON))) { if (!memcmp(header->addr3, priv->bssid, ETH_ALEN)) ipw_add_station(priv, header->addr2); } if (priv->config & CFG_NET_STATS) { IPW_DEBUG_HC("sending stat packet\n"); /* Set the size of the skb to the size of the full * ipw header and 802.11 frame */ skb_put(skb, le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE); /* Advance past the ipw packet header to the 802.11 frame */ skb_pull(skb, IPW_RX_FRAME_SIZE); /* Push the libipw_rx_stats before the 802.11 frame */ memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats)); skb->dev = priv->ieee->dev; /* Point raw at the libipw_stats */ skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; skb->protocol = cpu_to_be16(ETH_P_80211_STATS); memset(skb->cb, 0, sizeof(rxb->skb->cb)); netif_rx(skb); rxb->skb = NULL; } } /* * Main entry function for receiving a packet with 80211 headers. This * should be called when ever the FW has notified us that there is a new * skb in the receive queue. */ static void ipw_rx(struct ipw_priv *priv) { struct ipw_rx_mem_buffer *rxb; struct ipw_rx_packet *pkt; struct libipw_hdr_4addr *header; u32 r, w, i; u8 network_packet; u8 fill_rx = 0; r = ipw_read32(priv, IPW_RX_READ_INDEX); w = ipw_read32(priv, IPW_RX_WRITE_INDEX); i = priv->rxq->read; if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2)) fill_rx = 1; while (i != r) { rxb = priv->rxq->queue[i]; if (unlikely(rxb == NULL)) { printk(KERN_CRIT "Queue not allocated!\n"); break; } priv->rxq->queue[i] = NULL; pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr, IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); pkt = (struct ipw_rx_packet *)rxb->skb->data; IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n", pkt->header.message_type, pkt->header.rx_seq_num, pkt->header.control_bits); switch (pkt->header.message_type) { case RX_FRAME_TYPE: /* 802.11 frame */ { struct libipw_rx_stats stats = { .rssi = pkt->u.frame.rssi_dbm - IPW_RSSI_TO_DBM, .signal = pkt->u.frame.rssi_dbm - IPW_RSSI_TO_DBM + 0x100, .noise = le16_to_cpu(pkt->u.frame.noise), .rate = pkt->u.frame.rate, .mac_time = jiffies, .received_channel = pkt->u.frame.received_channel, .freq = (pkt->u.frame. control & (1 << 0)) ? LIBIPW_24GHZ_BAND : LIBIPW_52GHZ_BAND, .len = le16_to_cpu(pkt->u.frame.length), }; if (stats.rssi != 0) stats.mask |= LIBIPW_STATMASK_RSSI; if (stats.signal != 0) stats.mask |= LIBIPW_STATMASK_SIGNAL; if (stats.noise != 0) stats.mask |= LIBIPW_STATMASK_NOISE; if (stats.rate != 0) stats.mask |= LIBIPW_STATMASK_RATE; priv->rx_packets++; #ifdef CONFIG_IPW2200_PROMISCUOUS if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) ipw_handle_promiscuous_rx(priv, rxb, &stats); #endif #ifdef CONFIG_IPW2200_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) { #ifdef CONFIG_IPW2200_RADIOTAP ipw_handle_data_packet_monitor(priv, rxb, &stats); #else ipw_handle_data_packet(priv, rxb, &stats); #endif break; } #endif header = (struct libipw_hdr_4addr *)(rxb->skb-> data + IPW_RX_FRAME_SIZE); /* TODO: Check Ad-Hoc dest/source and make sure * that we are actually parsing these packets * correctly -- we should probably use the * frame control of the packet and disregard * the current iw_mode */ network_packet = is_network_packet(priv, header); if (network_packet && priv->assoc_network) { priv->assoc_network->stats.rssi = stats.rssi; priv->exp_avg_rssi = exponential_average(priv->exp_avg_rssi, stats.rssi, DEPTH_RSSI); } IPW_DEBUG_RX("Frame: len=%u\n", le16_to_cpu(pkt->u.frame.length)); if (le16_to_cpu(pkt->u.frame.length) < libipw_get_hdrlen(le16_to_cpu( header->frame_ctl))) { IPW_DEBUG_DROP ("Received packet is too small. " "Dropping.\n"); priv->net_dev->stats.rx_errors++; priv->wstats.discard.misc++; break; } switch (WLAN_FC_GET_TYPE (le16_to_cpu(header->frame_ctl))) { case IEEE80211_FTYPE_MGMT: ipw_handle_mgmt_packet(priv, rxb, &stats); break; case IEEE80211_FTYPE_CTL: break; case IEEE80211_FTYPE_DATA: if (unlikely(!network_packet || is_duplicate_packet(priv, header))) { IPW_DEBUG_DROP("Dropping: " "%pM, " "%pM, " "%pM\n", header->addr1, header->addr2, header->addr3); break; } ipw_handle_data_packet(priv, rxb, &stats); break; } break; } case RX_HOST_NOTIFICATION_TYPE:{ IPW_DEBUG_RX ("Notification: subtype=%02X flags=%02X size=%d\n", pkt->u.notification.subtype, pkt->u.notification.flags, le16_to_cpu(pkt->u.notification.size)); ipw_rx_notification(priv, &pkt->u.notification); break; } default: IPW_DEBUG_RX("Bad Rx packet of type %d\n", pkt->header.message_type); break; } /* For now we just don't re-use anything. We can tweak this * later to try and re-use notification packets and SKBs that * fail to Rx correctly */ if (rxb->skb != NULL) { dev_kfree_skb_any(rxb->skb); rxb->skb = NULL; } pci_unmap_single(priv->pci_dev, rxb->dma_addr, IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); list_add_tail(&rxb->list, &priv->rxq->rx_used); i = (i + 1) % RX_QUEUE_SIZE; /* If there are a lot of unsued frames, restock the Rx queue * so the ucode won't assert */ if (fill_rx) { priv->rxq->read = i; ipw_rx_queue_replenish(priv); } } /* Backtrack one entry */ priv->rxq->read = i; ipw_rx_queue_restock(priv); } #define DEFAULT_RTS_THRESHOLD 2304U #define MIN_RTS_THRESHOLD 1U #define MAX_RTS_THRESHOLD 2304U #define DEFAULT_BEACON_INTERVAL 100U #define DEFAULT_SHORT_RETRY_LIMIT 7U #define DEFAULT_LONG_RETRY_LIMIT 4U /** * ipw_sw_reset * @option: options to control different reset behaviour * 0 = reset everything except the 'disable' module_param * 1 = reset everything and print out driver info (for probe only) * 2 = reset everything */ static int ipw_sw_reset(struct ipw_priv *priv, int option) { int band, modulation; int old_mode = priv->ieee->iw_mode; /* Initialize module parameter values here */ priv->config = 0; /* We default to disabling the LED code as right now it causes * too many systems to lock up... */ if (!led_support) priv->config |= CFG_NO_LED; if (associate) priv->config |= CFG_ASSOCIATE; else IPW_DEBUG_INFO("Auto associate disabled.\n"); if (auto_create) priv->config |= CFG_ADHOC_CREATE; else IPW_DEBUG_INFO("Auto adhoc creation disabled.\n"); priv->config &= ~CFG_STATIC_ESSID; priv->essid_len = 0; memset(priv->essid, 0, IW_ESSID_MAX_SIZE); if (disable && option) { priv->status |= STATUS_RF_KILL_SW; IPW_DEBUG_INFO("Radio disabled.\n"); } if (default_channel != 0) { priv->config |= CFG_STATIC_CHANNEL; priv->channel = default_channel; IPW_DEBUG_INFO("Bind to static channel %d\n", default_channel); /* TODO: Validate that provided channel is in range */ } #ifdef CONFIG_IPW2200_QOS ipw_qos_init(priv, qos_enable, qos_burst_enable, burst_duration_CCK, burst_duration_OFDM); #endif /* CONFIG_IPW2200_QOS */ switch (network_mode) { case 1: priv->ieee->iw_mode = IW_MODE_ADHOC; priv->net_dev->type = ARPHRD_ETHER; break; #ifdef CONFIG_IPW2200_MONITOR case 2: priv->ieee->iw_mode = IW_MODE_MONITOR; #ifdef CONFIG_IPW2200_RADIOTAP priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; #else priv->net_dev->type = ARPHRD_IEEE80211; #endif break; #endif default: case 0: priv->net_dev->type = ARPHRD_ETHER; priv->ieee->iw_mode = IW_MODE_INFRA; break; } if (hwcrypto) { priv->ieee->host_encrypt = 0; priv->ieee->host_encrypt_msdu = 0; priv->ieee->host_decrypt = 0; priv->ieee->host_mc_decrypt = 0; } IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off"); /* IPW2200/2915 is abled to do hardware fragmentation. */ priv->ieee->host_open_frag = 0; if ((priv->pci_dev->device == 0x4223) || (priv->pci_dev->device == 0x4224)) { if (option == 1) printk(KERN_INFO DRV_NAME ": Detected Intel PRO/Wireless 2915ABG Network " "Connection\n"); priv->ieee->abg_true = 1; band = LIBIPW_52GHZ_BAND | LIBIPW_24GHZ_BAND; modulation = LIBIPW_OFDM_MODULATION | LIBIPW_CCK_MODULATION; priv->adapter = IPW_2915ABG; priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B; } else { if (option == 1) printk(KERN_INFO DRV_NAME ": Detected Intel PRO/Wireless 2200BG Network " "Connection\n"); priv->ieee->abg_true = 0; band = LIBIPW_24GHZ_BAND; modulation = LIBIPW_OFDM_MODULATION | LIBIPW_CCK_MODULATION; priv->adapter = IPW_2200BG; priv->ieee->mode = IEEE_G | IEEE_B; } priv->ieee->freq_band = band; priv->ieee->modulation = modulation; priv->rates_mask = LIBIPW_DEFAULT_RATES_MASK; priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; priv->rts_threshold = DEFAULT_RTS_THRESHOLD; priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT; priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT; /* If power management is turned on, default to AC mode */ priv->power_mode = IPW_POWER_AC; priv->tx_power = IPW_TX_POWER_DEFAULT; return old_mode == priv->ieee->iw_mode; } /* * This file defines the Wireless Extension handlers. It does not * define any methods of hardware manipulation and relies on the * functions defined in ipw_main to provide the HW interaction. * * The exception to this is the use of the ipw_get_ordinal() * function used to poll the hardware vs. making unnecessary calls. * */ static int ipw_set_channel(struct ipw_priv *priv, u8 channel) { if (channel == 0) { IPW_DEBUG_INFO("Setting channel to ANY (0)\n"); priv->config &= ~CFG_STATIC_CHANNEL; IPW_DEBUG_ASSOC("Attempting to associate with new " "parameters.\n"); ipw_associate(priv); return 0; } priv->config |= CFG_STATIC_CHANNEL; if (priv->channel == channel) { IPW_DEBUG_INFO("Request to set channel to current value (%d)\n", channel); return 0; } IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel); priv->channel = channel; #ifdef CONFIG_IPW2200_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) { int i; if (priv->status & STATUS_SCANNING) { IPW_DEBUG_SCAN("Scan abort triggered due to " "channel change.\n"); ipw_abort_scan(priv); } for (i = 1000; i && (priv->status & STATUS_SCANNING); i--) udelay(10); if (priv->status & STATUS_SCANNING) IPW_DEBUG_SCAN("Still scanning...\n"); else IPW_DEBUG_SCAN("Took %dms to abort current scan\n", 1000 - i); return 0; } #endif /* CONFIG_IPW2200_MONITOR */ /* Network configuration changed -- force [re]association */ IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n"); if (!ipw_disassociate(priv)) ipw_associate(priv); return 0; } static int ipw_wx_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); const struct libipw_geo *geo = libipw_get_geo(priv->ieee); struct iw_freq *fwrq = &wrqu->freq; int ret = 0, i; u8 channel, flags; int band; if (fwrq->m == 0) { IPW_DEBUG_WX("SET Freq/Channel -> any\n"); mutex_lock(&priv->mutex); ret = ipw_set_channel(priv, 0); mutex_unlock(&priv->mutex); return ret; } /* if setting by freq convert to channel */ if (fwrq->e == 1) { channel = libipw_freq_to_channel(priv->ieee, fwrq->m); if (channel == 0) return -EINVAL; } else channel = fwrq->m; if (!(band = libipw_is_valid_channel(priv->ieee, channel))) return -EINVAL; if (priv->ieee->iw_mode == IW_MODE_ADHOC) { i = libipw_channel_to_index(priv->ieee, channel); if (i == -1) return -EINVAL; flags = (band == LIBIPW_24GHZ_BAND) ? geo->bg[i].flags : geo->a[i].flags; if (flags & LIBIPW_CH_PASSIVE_ONLY) { IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n"); return -EINVAL; } } IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m); mutex_lock(&priv->mutex); ret = ipw_set_channel(priv, channel); mutex_unlock(&priv->mutex); return ret; } static int ipw_wx_get_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); wrqu->freq.e = 0; /* If we are associated, trying to associate, or have a statically * configured CHANNEL then return that; otherwise return ANY */ mutex_lock(&priv->mutex); if (priv->config & CFG_STATIC_CHANNEL || priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) { int i; i = libipw_channel_to_index(priv->ieee, priv->channel); BUG_ON(i == -1); wrqu->freq.e = 1; switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { case LIBIPW_52GHZ_BAND: wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000; break; case LIBIPW_24GHZ_BAND: wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000; break; default: BUG(); } } else wrqu->freq.m = 0; mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel); return 0; } static int ipw_wx_set_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int err = 0; IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode); switch (wrqu->mode) { #ifdef CONFIG_IPW2200_MONITOR case IW_MODE_MONITOR: #endif case IW_MODE_ADHOC: case IW_MODE_INFRA: break; case IW_MODE_AUTO: wrqu->mode = IW_MODE_INFRA; break; default: return -EINVAL; } if (wrqu->mode == priv->ieee->iw_mode) return 0; mutex_lock(&priv->mutex); ipw_sw_reset(priv, 0); #ifdef CONFIG_IPW2200_MONITOR if (priv->ieee->iw_mode == IW_MODE_MONITOR) priv->net_dev->type = ARPHRD_ETHER; if (wrqu->mode == IW_MODE_MONITOR) #ifdef CONFIG_IPW2200_RADIOTAP priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; #else priv->net_dev->type = ARPHRD_IEEE80211; #endif #endif /* CONFIG_IPW2200_MONITOR */ /* Free the existing firmware and reset the fw_loaded * flag so ipw_load() will bring in the new firmware */ free_firmware(); priv->ieee->iw_mode = wrqu->mode; schedule_work(&priv->adapter_restart); mutex_unlock(&priv->mutex); return err; } static int ipw_wx_get_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->mode = priv->ieee->iw_mode; IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode); mutex_unlock(&priv->mutex); return 0; } /* Values are in microsecond */ static const s32 timeout_duration[] = { 350000, 250000, 75000, 37000, 25000, }; static const s32 period_duration[] = { 400000, 700000, 1000000, 1000000, 1000000 }; static int ipw_wx_get_range(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct iw_range *range = (struct iw_range *)extra; const struct libipw_geo *geo = libipw_get_geo(priv->ieee); int i = 0, j; wrqu->data.length = sizeof(*range); memset(range, 0, sizeof(*range)); /* 54Mbs == ~27 Mb/s real (802.11g) */ range->throughput = 27 * 1000 * 1000; range->max_qual.qual = 100; /* TODO: Find real max RSSI and stick here */ range->max_qual.level = 0; range->max_qual.noise = 0; range->max_qual.updated = 7; /* Updated all three */ range->avg_qual.qual = 70; /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ range->avg_qual.level = 0; /* FIXME to real average level */ range->avg_qual.noise = 0; range->avg_qual.updated = 7; /* Updated all three */ mutex_lock(&priv->mutex); range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES); for (i = 0; i < range->num_bitrates; i++) range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) * 500000; range->max_rts = DEFAULT_RTS_THRESHOLD; range->min_frag = MIN_FRAG_THRESHOLD; range->max_frag = MAX_FRAG_THRESHOLD; range->encoding_size[0] = 5; range->encoding_size[1] = 13; range->num_encoding_sizes = 2; range->max_encoding_tokens = WEP_KEYS; /* Set the Wireless Extension versions */ range->we_version_compiled = WIRELESS_EXT; range->we_version_source = 18; i = 0; if (priv->ieee->mode & (IEEE_B | IEEE_G)) { for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) { if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && (geo->bg[j].flags & LIBIPW_CH_PASSIVE_ONLY)) continue; range->freq[i].i = geo->bg[j].channel; range->freq[i].m = geo->bg[j].freq * 100000; range->freq[i].e = 1; i++; } } if (priv->ieee->mode & IEEE_A) { for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) { if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && (geo->a[j].flags & LIBIPW_CH_PASSIVE_ONLY)) continue; range->freq[i].i = geo->a[j].channel; range->freq[i].m = geo->a[j].freq * 100000; range->freq[i].e = 1; i++; } } range->num_channels = i; range->num_frequency = i; mutex_unlock(&priv->mutex); /* Event capability (kernel + driver) */ range->event_capa[0] = (IW_EVENT_CAPA_K_0 | IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | IW_EVENT_CAPA_MASK(SIOCGIWAP) | IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); range->event_capa[1] = IW_EVENT_CAPA_K_1; range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE; IPW_DEBUG_WX("GET Range\n"); return 0; } static int ipw_wx_set_wap(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) return -EINVAL; mutex_lock(&priv->mutex); if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) || is_zero_ether_addr(wrqu->ap_addr.sa_data)) { /* we disable mandatory BSSID association */ IPW_DEBUG_WX("Setting AP BSSID to ANY\n"); priv->config &= ~CFG_STATIC_BSSID; IPW_DEBUG_ASSOC("Attempting to associate with new " "parameters.\n"); ipw_associate(priv); mutex_unlock(&priv->mutex); return 0; } priv->config |= CFG_STATIC_BSSID; if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) { IPW_DEBUG_WX("BSSID set to current BSSID.\n"); mutex_unlock(&priv->mutex); return 0; } IPW_DEBUG_WX("Setting mandatory BSSID to %pM\n", wrqu->ap_addr.sa_data); memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN); /* Network configuration changed -- force [re]association */ IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n"); if (!ipw_disassociate(priv)) ipw_associate(priv); mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_get_wap(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); /* If we are associated, trying to associate, or have a statically * configured BSSID then return that; otherwise return ANY */ mutex_lock(&priv->mutex); if (priv->config & CFG_STATIC_BSSID || priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { wrqu->ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN); } else memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data); mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_set_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int length; DECLARE_SSID_BUF(ssid); mutex_lock(&priv->mutex); if (!wrqu->essid.flags) { IPW_DEBUG_WX("Setting ESSID to ANY\n"); ipw_disassociate(priv); priv->config &= ~CFG_STATIC_ESSID; ipw_associate(priv); mutex_unlock(&priv->mutex); return 0; } length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE); priv->config |= CFG_STATIC_ESSID; if (priv->essid_len == length && !memcmp(priv->essid, extra, length) && (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) { IPW_DEBUG_WX("ESSID set to current ESSID.\n"); mutex_unlock(&priv->mutex); return 0; } IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", print_ssid(ssid, extra, length), length); priv->essid_len = length; memcpy(priv->essid, extra, priv->essid_len); /* Network configuration changed -- force [re]association */ IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n"); if (!ipw_disassociate(priv)) ipw_associate(priv); mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_get_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); DECLARE_SSID_BUF(ssid); /* If we are associated, trying to associate, or have a statically * configured ESSID then return that; otherwise return ANY */ mutex_lock(&priv->mutex); if (priv->config & CFG_STATIC_ESSID || priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { IPW_DEBUG_WX("Getting essid: '%s'\n", print_ssid(ssid, priv->essid, priv->essid_len)); memcpy(extra, priv->essid, priv->essid_len); wrqu->essid.length = priv->essid_len; wrqu->essid.flags = 1; /* active */ } else { IPW_DEBUG_WX("Getting essid: ANY\n"); wrqu->essid.length = 0; wrqu->essid.flags = 0; /* active */ } mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_set_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); IPW_DEBUG_WX("Setting nick to '%s'\n", extra); if (wrqu->data.length > IW_ESSID_MAX_SIZE) return -E2BIG; mutex_lock(&priv->mutex); wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick)); memset(priv->nick, 0, sizeof(priv->nick)); memcpy(priv->nick, extra, wrqu->data.length); IPW_DEBUG_TRACE("<<\n"); mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_get_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); IPW_DEBUG_WX("Getting nick\n"); mutex_lock(&priv->mutex); wrqu->data.length = strlen(priv->nick); memcpy(extra, priv->nick, wrqu->data.length); wrqu->data.flags = 1; /* active */ mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_set_sens(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int err = 0; IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value); IPW_DEBUG_WX("Setting disassociate threshold to %d\n", 3*wrqu->sens.value); mutex_lock(&priv->mutex); if (wrqu->sens.fixed == 0) { priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; goto out; } if ((wrqu->sens.value > IPW_MB_ROAMING_THRESHOLD_MAX) || (wrqu->sens.value < IPW_MB_ROAMING_THRESHOLD_MIN)) { err = -EINVAL; goto out; } priv->roaming_threshold = wrqu->sens.value; priv->disassociate_threshold = 3*wrqu->sens.value; out: mutex_unlock(&priv->mutex); return err; } static int ipw_wx_get_sens(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->sens.fixed = 1; wrqu->sens.value = priv->roaming_threshold; mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET roaming threshold -> %s %d\n", wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value); return 0; } static int ipw_wx_set_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* TODO: We should use semaphores or locks for access to priv */ struct ipw_priv *priv = libipw_priv(dev); u32 target_rate = wrqu->bitrate.value; u32 fixed, mask; /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */ /* value = X, fixed = 1 means only rate X */ /* value = X, fixed = 0 means all rates lower equal X */ if (target_rate == -1) { fixed = 0; mask = LIBIPW_DEFAULT_RATES_MASK; /* Now we should reassociate */ goto apply; } mask = 0; fixed = wrqu->bitrate.fixed; if (target_rate == 1000000 || !fixed) mask |= LIBIPW_CCK_RATE_1MB_MASK; if (target_rate == 1000000) goto apply; if (target_rate == 2000000 || !fixed) mask |= LIBIPW_CCK_RATE_2MB_MASK; if (target_rate == 2000000) goto apply; if (target_rate == 5500000 || !fixed) mask |= LIBIPW_CCK_RATE_5MB_MASK; if (target_rate == 5500000) goto apply; if (target_rate == 6000000 || !fixed) mask |= LIBIPW_OFDM_RATE_6MB_MASK; if (target_rate == 6000000) goto apply; if (target_rate == 9000000 || !fixed) mask |= LIBIPW_OFDM_RATE_9MB_MASK; if (target_rate == 9000000) goto apply; if (target_rate == 11000000 || !fixed) mask |= LIBIPW_CCK_RATE_11MB_MASK; if (target_rate == 11000000) goto apply; if (target_rate == 12000000 || !fixed) mask |= LIBIPW_OFDM_RATE_12MB_MASK; if (target_rate == 12000000) goto apply; if (target_rate == 18000000 || !fixed) mask |= LIBIPW_OFDM_RATE_18MB_MASK; if (target_rate == 18000000) goto apply; if (target_rate == 24000000 || !fixed) mask |= LIBIPW_OFDM_RATE_24MB_MASK; if (target_rate == 24000000) goto apply; if (target_rate == 36000000 || !fixed) mask |= LIBIPW_OFDM_RATE_36MB_MASK; if (target_rate == 36000000) goto apply; if (target_rate == 48000000 || !fixed) mask |= LIBIPW_OFDM_RATE_48MB_MASK; if (target_rate == 48000000) goto apply; if (target_rate == 54000000 || !fixed) mask |= LIBIPW_OFDM_RATE_54MB_MASK; if (target_rate == 54000000) goto apply; IPW_DEBUG_WX("invalid rate specified, returning error\n"); return -EINVAL; apply: IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n", mask, fixed ? "fixed" : "sub-rates"); mutex_lock(&priv->mutex); if (mask == LIBIPW_DEFAULT_RATES_MASK) { priv->config &= ~CFG_FIXED_RATE; ipw_set_fixed_rate(priv, priv->ieee->mode); } else priv->config |= CFG_FIXED_RATE; if (priv->rates_mask == mask) { IPW_DEBUG_WX("Mask set to current mask.\n"); mutex_unlock(&priv->mutex); return 0; } priv->rates_mask = mask; /* Network configuration changed -- force [re]association */ IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n"); if (!ipw_disassociate(priv)) ipw_associate(priv); mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_get_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->bitrate.value = priv->last_rate; wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0; mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value); return 0; } static int ipw_wx_set_rts(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (wrqu->rts.disabled || !wrqu->rts.fixed) priv->rts_threshold = DEFAULT_RTS_THRESHOLD; else { if (wrqu->rts.value < MIN_RTS_THRESHOLD || wrqu->rts.value > MAX_RTS_THRESHOLD) { mutex_unlock(&priv->mutex); return -EINVAL; } priv->rts_threshold = wrqu->rts.value; } ipw_send_rts_threshold(priv, priv->rts_threshold); mutex_unlock(&priv->mutex); IPW_DEBUG_WX("SET RTS Threshold -> %d\n", priv->rts_threshold); return 0; } static int ipw_wx_get_rts(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->rts.value = priv->rts_threshold; wrqu->rts.fixed = 0; /* no auto select */ wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET RTS Threshold -> %d\n", wrqu->rts.value); return 0; } static int ipw_wx_set_txpow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int err = 0; mutex_lock(&priv->mutex); if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) { err = -EINPROGRESS; goto out; } if (!wrqu->power.fixed) wrqu->power.value = IPW_TX_POWER_DEFAULT; if (wrqu->power.flags != IW_TXPOW_DBM) { err = -EINVAL; goto out; } if ((wrqu->power.value > IPW_TX_POWER_MAX) || (wrqu->power.value < IPW_TX_POWER_MIN)) { err = -EINVAL; goto out; } priv->tx_power = wrqu->power.value; err = ipw_set_tx_power(priv); out: mutex_unlock(&priv->mutex); return err; } static int ipw_wx_get_txpow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->power.value = priv->tx_power; wrqu->power.fixed = 1; wrqu->power.flags = IW_TXPOW_DBM; wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0; mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET TX Power -> %s %d\n", wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value); return 0; } static int ipw_wx_set_frag(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (wrqu->frag.disabled || !wrqu->frag.fixed) priv->ieee->fts = DEFAULT_FTS; else { if (wrqu->frag.value < MIN_FRAG_THRESHOLD || wrqu->frag.value > MAX_FRAG_THRESHOLD) { mutex_unlock(&priv->mutex); return -EINVAL; } priv->ieee->fts = wrqu->frag.value & ~0x1; } ipw_send_frag_threshold(priv, wrqu->frag.value); mutex_unlock(&priv->mutex); IPW_DEBUG_WX("SET Frag Threshold -> %d\n", wrqu->frag.value); return 0; } static int ipw_wx_get_frag(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->frag.value = priv->ieee->fts; wrqu->frag.fixed = 0; /* no auto select */ wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS); mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value); return 0; } static int ipw_wx_set_retry(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) return -EINVAL; if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) return 0; if (wrqu->retry.value < 0 || wrqu->retry.value >= 255) return -EINVAL; mutex_lock(&priv->mutex); if (wrqu->retry.flags & IW_RETRY_SHORT) priv->short_retry_limit = (u8) wrqu->retry.value; else if (wrqu->retry.flags & IW_RETRY_LONG) priv->long_retry_limit = (u8) wrqu->retry.value; else { priv->short_retry_limit = (u8) wrqu->retry.value; priv->long_retry_limit = (u8) wrqu->retry.value; } ipw_send_retry_limit(priv, priv->short_retry_limit, priv->long_retry_limit); mutex_unlock(&priv->mutex); IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n", priv->short_retry_limit, priv->long_retry_limit); return 0; } static int ipw_wx_get_retry(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->retry.disabled = 0; if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { mutex_unlock(&priv->mutex); return -EINVAL; } if (wrqu->retry.flags & IW_RETRY_LONG) { wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; wrqu->retry.value = priv->long_retry_limit; } else if (wrqu->retry.flags & IW_RETRY_SHORT) { wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; wrqu->retry.value = priv->short_retry_limit; } else { wrqu->retry.flags = IW_RETRY_LIMIT; wrqu->retry.value = priv->short_retry_limit; } mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET retry -> %d\n", wrqu->retry.value); return 0; } static int ipw_wx_set_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); struct iw_scan_req *req = (struct iw_scan_req *)extra; struct delayed_work *work = NULL; mutex_lock(&priv->mutex); priv->user_requested_scan = 1; if (wrqu->data.length == sizeof(struct iw_scan_req)) { if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { int len = min((int)req->essid_len, (int)sizeof(priv->direct_scan_ssid)); memcpy(priv->direct_scan_ssid, req->essid, len); priv->direct_scan_ssid_len = len; work = &priv->request_direct_scan; } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) { work = &priv->request_passive_scan; } } else { /* Normal active broadcast scan */ work = &priv->request_scan; } mutex_unlock(&priv->mutex); IPW_DEBUG_WX("Start scan\n"); schedule_delayed_work(work, 0); return 0; } static int ipw_wx_get_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); } static int ipw_wx_set_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *key) { struct ipw_priv *priv = libipw_priv(dev); int ret; u32 cap = priv->capability; mutex_lock(&priv->mutex); ret = libipw_wx_set_encode(priv->ieee, info, wrqu, key); /* In IBSS mode, we need to notify the firmware to update * the beacon info after we changed the capability. */ if (cap != priv->capability && priv->ieee->iw_mode == IW_MODE_ADHOC && priv->status & STATUS_ASSOCIATED) ipw_disassociate(priv); mutex_unlock(&priv->mutex); return ret; } static int ipw_wx_get_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *key) { struct ipw_priv *priv = libipw_priv(dev); return libipw_wx_get_encode(priv->ieee, info, wrqu, key); } static int ipw_wx_set_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int err; mutex_lock(&priv->mutex); if (wrqu->power.disabled) { priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM); if (err) { IPW_DEBUG_WX("failed setting power mode.\n"); mutex_unlock(&priv->mutex); return err; } IPW_DEBUG_WX("SET Power Management Mode -> off\n"); mutex_unlock(&priv->mutex); return 0; } switch (wrqu->power.flags & IW_POWER_MODE) { case IW_POWER_ON: /* If not specified */ case IW_POWER_MODE: /* If set all mask */ case IW_POWER_ALL_R: /* If explicitly state all */ break; default: /* Otherwise we don't support it */ IPW_DEBUG_WX("SET PM Mode: %X not supported.\n", wrqu->power.flags); mutex_unlock(&priv->mutex); return -EOPNOTSUPP; } /* If the user hasn't specified a power management mode yet, default * to BATTERY */ if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC) priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY; else priv->power_mode = IPW_POWER_ENABLED | priv->power_mode; err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode)); if (err) { IPW_DEBUG_WX("failed setting power mode.\n"); mutex_unlock(&priv->mutex); return err; } IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode); mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_get_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (!(priv->power_mode & IPW_POWER_ENABLED)) wrqu->power.disabled = 1; else wrqu->power.disabled = 0; mutex_unlock(&priv->mutex); IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode); return 0; } static int ipw_wx_set_powermode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int mode = *(int *)extra; int err; mutex_lock(&priv->mutex); if ((mode < 1) || (mode > IPW_POWER_LIMIT)) mode = IPW_POWER_AC; if (IPW_POWER_LEVEL(priv->power_mode) != mode) { err = ipw_send_power_mode(priv, mode); if (err) { IPW_DEBUG_WX("failed setting power mode.\n"); mutex_unlock(&priv->mutex); return err; } priv->power_mode = IPW_POWER_ENABLED | mode; } mutex_unlock(&priv->mutex); return 0; } #define MAX_WX_STRING 80 static int ipw_wx_get_powermode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int level = IPW_POWER_LEVEL(priv->power_mode); char *p = extra; p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level); switch (level) { case IPW_POWER_AC: p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)"); break; case IPW_POWER_BATTERY: p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)"); break; default: p += snprintf(p, MAX_WX_STRING - (p - extra), "(Timeout %dms, Period %dms)", timeout_duration[level - 1] / 1000, period_duration[level - 1] / 1000); } if (!(priv->power_mode & IPW_POWER_ENABLED)) p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF"); wrqu->data.length = p - extra + 1; return 0; } static int ipw_wx_set_wireless_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int mode = *(int *)extra; u8 band = 0, modulation = 0; if (mode == 0 || mode & ~IEEE_MODE_MASK) { IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode); return -EINVAL; } mutex_lock(&priv->mutex); if (priv->adapter == IPW_2915ABG) { priv->ieee->abg_true = 1; if (mode & IEEE_A) { band |= LIBIPW_52GHZ_BAND; modulation |= LIBIPW_OFDM_MODULATION; } else priv->ieee->abg_true = 0; } else { if (mode & IEEE_A) { IPW_WARNING("Attempt to set 2200BG into " "802.11a mode\n"); mutex_unlock(&priv->mutex); return -EINVAL; } priv->ieee->abg_true = 0; } if (mode & IEEE_B) { band |= LIBIPW_24GHZ_BAND; modulation |= LIBIPW_CCK_MODULATION; } else priv->ieee->abg_true = 0; if (mode & IEEE_G) { band |= LIBIPW_24GHZ_BAND; modulation |= LIBIPW_OFDM_MODULATION; } else priv->ieee->abg_true = 0; priv->ieee->mode = mode; priv->ieee->freq_band = band; priv->ieee->modulation = modulation; init_supported_rates(priv, &priv->rates); /* Network configuration changed -- force [re]association */ IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n"); if (!ipw_disassociate(priv)) { ipw_send_supported_rates(priv, &priv->rates); ipw_associate(priv); } /* Update the band LEDs */ ipw_led_band_on(priv); IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n", mode & IEEE_A ? 'a' : '.', mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.'); mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_get_wireless_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); switch (priv->ieee->mode) { case IEEE_A: strncpy(extra, "802.11a (1)", MAX_WX_STRING); break; case IEEE_B: strncpy(extra, "802.11b (2)", MAX_WX_STRING); break; case IEEE_A | IEEE_B: strncpy(extra, "802.11ab (3)", MAX_WX_STRING); break; case IEEE_G: strncpy(extra, "802.11g (4)", MAX_WX_STRING); break; case IEEE_A | IEEE_G: strncpy(extra, "802.11ag (5)", MAX_WX_STRING); break; case IEEE_B | IEEE_G: strncpy(extra, "802.11bg (6)", MAX_WX_STRING); break; case IEEE_A | IEEE_B | IEEE_G: strncpy(extra, "802.11abg (7)", MAX_WX_STRING); break; default: strncpy(extra, "unknown", MAX_WX_STRING); break; } IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra); wrqu->data.length = strlen(extra) + 1; mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_set_preamble(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int mode = *(int *)extra; mutex_lock(&priv->mutex); /* Switching from SHORT -> LONG requires a disassociation */ if (mode == 1) { if (!(priv->config & CFG_PREAMBLE_LONG)) { priv->config |= CFG_PREAMBLE_LONG; /* Network configuration changed -- force [re]association */ IPW_DEBUG_ASSOC ("[re]association triggered due to preamble change.\n"); if (!ipw_disassociate(priv)) ipw_associate(priv); } goto done; } if (mode == 0) { priv->config &= ~CFG_PREAMBLE_LONG; goto done; } mutex_unlock(&priv->mutex); return -EINVAL; done: mutex_unlock(&priv->mutex); return 0; } static int ipw_wx_get_preamble(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (priv->config & CFG_PREAMBLE_LONG) snprintf(wrqu->name, IFNAMSIZ, "long (1)"); else snprintf(wrqu->name, IFNAMSIZ, "auto (0)"); mutex_unlock(&priv->mutex); return 0; } #ifdef CONFIG_IPW2200_MONITOR static int ipw_wx_set_monitor(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); int *parms = (int *)extra; int enable = (parms[0] > 0); mutex_lock(&priv->mutex); IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]); if (enable) { if (priv->ieee->iw_mode != IW_MODE_MONITOR) { #ifdef CONFIG_IPW2200_RADIOTAP priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; #else priv->net_dev->type = ARPHRD_IEEE80211; #endif schedule_work(&priv->adapter_restart); } ipw_set_channel(priv, parms[1]); } else { if (priv->ieee->iw_mode != IW_MODE_MONITOR) { mutex_unlock(&priv->mutex); return 0; } priv->net_dev->type = ARPHRD_ETHER; schedule_work(&priv->adapter_restart); } mutex_unlock(&priv->mutex); return 0; } #endif /* CONFIG_IPW2200_MONITOR */ static int ipw_wx_reset(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); IPW_DEBUG_WX("RESET\n"); schedule_work(&priv->adapter_restart); return 0; } static int ipw_wx_sw_reset(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ipw_priv *priv = libipw_priv(dev); union iwreq_data wrqu_sec = { .encoding = { .flags = IW_ENCODE_DISABLED, }, }; int ret; IPW_DEBUG_WX("SW_RESET\n"); mutex_lock(&priv->mutex); ret = ipw_sw_reset(priv, 2); if (!ret) { free_firmware(); ipw_adapter_restart(priv); } /* The SW reset bit might have been toggled on by the 'disable' * module parameter, so take appropriate action */ ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW); mutex_unlock(&priv->mutex); libipw_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL); mutex_lock(&priv->mutex); if (!(priv->status & STATUS_RF_KILL_MASK)) { /* Configuration likely changed -- force [re]association */ IPW_DEBUG_ASSOC("[re]association triggered due to sw " "reset.\n"); if (!ipw_disassociate(priv)) ipw_associate(priv); } mutex_unlock(&priv->mutex); return 0; } /* Rebase the WE IOCTLs to zero for the handler array */ static iw_handler ipw_wx_handlers[] = { IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq), IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq), IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode), IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode), IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens), IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens), IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range), IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap), IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap), IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan), IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan), IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid), IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid), IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick), IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick), IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate), IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate), IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts), IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts), IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag), IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag), IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow), IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow), IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry), IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry), IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode), IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode), IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power), IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power), IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie), IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie), IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme), IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth), IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth), IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext), IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext), }; enum { IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV, IPW_PRIV_GET_POWER, IPW_PRIV_SET_MODE, IPW_PRIV_GET_MODE, IPW_PRIV_SET_PREAMBLE, IPW_PRIV_GET_PREAMBLE, IPW_PRIV_RESET, IPW_PRIV_SW_RESET, #ifdef CONFIG_IPW2200_MONITOR IPW_PRIV_SET_MONITOR, #endif }; static struct iw_priv_args ipw_priv_args[] = { { .cmd = IPW_PRIV_SET_POWER, .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, .name = "set_power"}, { .cmd = IPW_PRIV_GET_POWER, .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, .name = "get_power"}, { .cmd = IPW_PRIV_SET_MODE, .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, .name = "set_mode"}, { .cmd = IPW_PRIV_GET_MODE, .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, .name = "get_mode"}, { .cmd = IPW_PRIV_SET_PREAMBLE, .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, .name = "set_preamble"}, { .cmd = IPW_PRIV_GET_PREAMBLE, .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, .name = "get_preamble"}, { IPW_PRIV_RESET, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"}, { IPW_PRIV_SW_RESET, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"}, #ifdef CONFIG_IPW2200_MONITOR { IPW_PRIV_SET_MONITOR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"}, #endif /* CONFIG_IPW2200_MONITOR */ }; static iw_handler ipw_priv_handler[] = { ipw_wx_set_powermode, ipw_wx_get_powermode, ipw_wx_set_wireless_mode, ipw_wx_get_wireless_mode, ipw_wx_set_preamble, ipw_wx_get_preamble, ipw_wx_reset, ipw_wx_sw_reset, #ifdef CONFIG_IPW2200_MONITOR ipw_wx_set_monitor, #endif }; static struct iw_handler_def ipw_wx_handler_def = { .standard = ipw_wx_handlers, .num_standard = ARRAY_SIZE(ipw_wx_handlers), .num_private = ARRAY_SIZE(ipw_priv_handler), .num_private_args = ARRAY_SIZE(ipw_priv_args), .private = ipw_priv_handler, .private_args = ipw_priv_args, .get_wireless_stats = ipw_get_wireless_stats, }; /* * Get wireless statistics. * Called by /proc/net/wireless * Also called by SIOCGIWSTATS */ static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev) { struct ipw_priv *priv = libipw_priv(dev); struct iw_statistics *wstats; wstats = &priv->wstats; /* if hw is disabled, then ipw_get_ordinal() can't be called. * netdev->get_wireless_stats seems to be called before fw is * initialized. STATUS_ASSOCIATED will only be set if the hw is up * and associated; if not associcated, the values are all meaningless * anyway, so set them all to NULL and INVALID */ if (!(priv->status & STATUS_ASSOCIATED)) { wstats->miss.beacon = 0; wstats->discard.retries = 0; wstats->qual.qual = 0; wstats->qual.level = 0; wstats->qual.noise = 0; wstats->qual.updated = 7; wstats->qual.updated |= IW_QUAL_NOISE_INVALID | IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; return wstats; } wstats->qual.qual = priv->quality; wstats->qual.level = priv->exp_avg_rssi; wstats->qual.noise = priv->exp_avg_noise; wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM; wstats->miss.beacon = average_value(&priv->average_missed_beacons); wstats->discard.retries = priv->last_tx_failures; wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable; /* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len)) goto fail_get_ordinal; wstats->discard.retries += tx_retry; */ return wstats; } /* net device stuff */ static void init_sys_config(struct ipw_sys_config *sys_config) { memset(sys_config, 0, sizeof(struct ipw_sys_config)); sys_config->bt_coexistence = 0; sys_config->answer_broadcast_ssid_probe = 0; sys_config->accept_all_data_frames = 0; sys_config->accept_non_directed_frames = 1; sys_config->exclude_unicast_unencrypted = 0; sys_config->disable_unicast_decryption = 1; sys_config->exclude_multicast_unencrypted = 0; sys_config->disable_multicast_decryption = 1; if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B) antenna = CFG_SYS_ANTENNA_BOTH; sys_config->antenna_diversity = antenna; sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */ sys_config->dot11g_auto_detection = 0; sys_config->enable_cts_to_self = 0; sys_config->bt_coexist_collision_thr = 0; sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */ sys_config->silence_threshold = 0x1e; } static int ipw_net_open(struct net_device *dev) { IPW_DEBUG_INFO("dev->open\n"); netif_start_queue(dev); return 0; } static int ipw_net_stop(struct net_device *dev) { IPW_DEBUG_INFO("dev->close\n"); netif_stop_queue(dev); return 0; } /* todo: modify to send one tfd per fragment instead of using chunking. otherwise we need to heavily modify the libipw_skb_to_txb. */ static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb, int pri) { struct libipw_hdr_3addrqos *hdr = (struct libipw_hdr_3addrqos *) txb->fragments[0]->data; int i = 0; struct tfd_frame *tfd; #ifdef CONFIG_IPW2200_QOS int tx_id = ipw_get_tx_queue_number(priv, pri); struct clx2_tx_queue *txq = &priv->txq[tx_id]; #else struct clx2_tx_queue *txq = &priv->txq[0]; #endif struct clx2_queue *q = &txq->q; u8 id, hdr_len, unicast; int fc; if (!(priv->status & STATUS_ASSOCIATED)) goto drop; hdr_len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: unicast = !is_multicast_ether_addr(hdr->addr1); id = ipw_find_station(priv, hdr->addr1); if (id == IPW_INVALID_STATION) { id = ipw_add_station(priv, hdr->addr1); if (id == IPW_INVALID_STATION) { IPW_WARNING("Attempt to send data to " "invalid cell: %pM\n", hdr->addr1); goto drop; } } break; case IW_MODE_INFRA: default: unicast = !is_multicast_ether_addr(hdr->addr3); id = 0; break; } tfd = &txq->bd[q->first_empty]; txq->txb[q->first_empty] = txb; memset(tfd, 0, sizeof(*tfd)); tfd->u.data.station_number = id; tfd->control_flags.message_type = TX_FRAME_TYPE; tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK; tfd->u.data.cmd_id = DINO_CMD_TX; tfd->u.data.len = cpu_to_le16(txb->payload_size); if (priv->assoc_request.ieee_mode == IPW_B_MODE) tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK; else tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM; if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE) tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE; fc = le16_to_cpu(hdr->frame_ctl); hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS); memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len); if (likely(unicast)) tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD; if (txb->encrypted && !priv->ieee->host_encrypt) { switch (priv->ieee->sec.level) { case SEC_LEVEL_3: tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); /* XXX: ACK flag must be set for CCMP even if it * is a multicast/broadcast packet, because CCMP * group communication encrypted by GTK is * actually done by the AP. */ if (!unicast) tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD; tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP; tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM; tfd->u.data.key_index = 0; tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE; break; case SEC_LEVEL_2: tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP; tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP; tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE; break; case SEC_LEVEL_1: tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); tfd->u.data.key_index = priv->ieee->crypt_info.tx_keyidx; if (priv->ieee->sec.key_sizes[priv->ieee->crypt_info.tx_keyidx] <= 40) tfd->u.data.key_index |= DCT_WEP_KEY_64Bit; else tfd->u.data.key_index |= DCT_WEP_KEY_128Bit; break; case SEC_LEVEL_0: break; default: printk(KERN_ERR "Unknown security level %d\n", priv->ieee->sec.level); break; } } else /* No hardware encryption */ tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP; #ifdef CONFIG_IPW2200_QOS if (fc & IEEE80211_STYPE_QOS_DATA) ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data)); #endif /* CONFIG_IPW2200_QOS */ /* payload */ tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2), txb->nr_frags)); IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n", txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks)); for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) { IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n", i, le32_to_cpu(tfd->u.data.num_chunks), txb->fragments[i]->len - hdr_len); IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n", i, tfd->u.data.num_chunks, txb->fragments[i]->len - hdr_len); printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len, txb->fragments[i]->len - hdr_len); tfd->u.data.chunk_ptr[i] = cpu_to_le32(pci_map_single (priv->pci_dev, txb->fragments[i]->data + hdr_len, txb->fragments[i]->len - hdr_len, PCI_DMA_TODEVICE)); tfd->u.data.chunk_len[i] = cpu_to_le16(txb->fragments[i]->len - hdr_len); } if (i != txb->nr_frags) { struct sk_buff *skb; u16 remaining_bytes = 0; int j; for (j = i; j < txb->nr_frags; j++) remaining_bytes += txb->fragments[j]->len - hdr_len; printk(KERN_INFO "Trying to reallocate for %d bytes\n", remaining_bytes); skb = alloc_skb(remaining_bytes, GFP_ATOMIC); if (skb != NULL) { tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes); for (j = i; j < txb->nr_frags; j++) { int size = txb->fragments[j]->len - hdr_len; printk(KERN_INFO "Adding frag %d %d...\n", j, size); memcpy(skb_put(skb, size), txb->fragments[j]->data + hdr_len, size); } dev_kfree_skb_any(txb->fragments[i]); txb->fragments[i] = skb; tfd->u.data.chunk_ptr[i] = cpu_to_le32(pci_map_single (priv->pci_dev, skb->data, remaining_bytes, PCI_DMA_TODEVICE)); le32_add_cpu(&tfd->u.data.num_chunks, 1); } } /* kick DMA */ q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); ipw_write32(priv, q->reg_w, q->first_empty); if (ipw_tx_queue_space(q) < q->high_mark) netif_stop_queue(priv->net_dev); return NETDEV_TX_OK; drop: IPW_DEBUG_DROP("Silently dropping Tx packet.\n"); libipw_txb_free(txb); return NETDEV_TX_OK; } static int ipw_net_is_queue_full(struct net_device *dev, int pri) { struct ipw_priv *priv = libipw_priv(dev); #ifdef CONFIG_IPW2200_QOS int tx_id = ipw_get_tx_queue_number(priv, pri); struct clx2_tx_queue *txq = &priv->txq[tx_id]; #else struct clx2_tx_queue *txq = &priv->txq[0]; #endif /* CONFIG_IPW2200_QOS */ if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark) return 1; return 0; } #ifdef CONFIG_IPW2200_PROMISCUOUS static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, struct libipw_txb *txb) { struct libipw_rx_stats dummystats; struct ieee80211_hdr *hdr; u8 n; u16 filter = priv->prom_priv->filter; int hdr_only = 0; if (filter & IPW_PROM_NO_TX) return; memset(&dummystats, 0, sizeof(dummystats)); /* Filtering of fragment chains is done against the first fragment */ hdr = (void *)txb->fragments[0]->data; if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_MGMT) return; if (filter & IPW_PROM_MGMT_HEADER_ONLY) hdr_only = 1; } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_CTL) return; if (filter & IPW_PROM_CTL_HEADER_ONLY) hdr_only = 1; } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_DATA) return; if (filter & IPW_PROM_DATA_HEADER_ONLY) hdr_only = 1; } for(n=0; nnr_frags; ++n) { struct sk_buff *src = txb->fragments[n]; struct sk_buff *dst; struct ieee80211_radiotap_header *rt_hdr; int len; if (hdr_only) { hdr = (void *)src->data; len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); } else len = src->len; dst = alloc_skb(len + sizeof(*rt_hdr), GFP_ATOMIC); if (!dst) continue; rt_hdr = (void *)skb_put(dst, sizeof(*rt_hdr)); rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION; rt_hdr->it_pad = 0; rt_hdr->it_present = 0; /* after all, it's just an idea */ rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL); *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16( ieee80211chan2mhz(priv->channel)); if (priv->channel > 14) /* 802.11a */ *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ); else if (priv->ieee->mode == IEEE_B) /* 802.11b */ *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ); else /* 802.11g */ *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ); rt_hdr->it_len = cpu_to_le16(dst->len); skb_copy_from_linear_data(src, skb_put(dst, len), len); if (!libipw_rx(priv->prom_priv->ieee, dst, &dummystats)) dev_kfree_skb_any(dst); } } #endif static netdev_tx_t ipw_net_hard_start_xmit(struct libipw_txb *txb, struct net_device *dev, int pri) { struct ipw_priv *priv = libipw_priv(dev); unsigned long flags; netdev_tx_t ret; IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size); spin_lock_irqsave(&priv->lock, flags); #ifdef CONFIG_IPW2200_PROMISCUOUS if (rtap_iface && netif_running(priv->prom_net_dev)) ipw_handle_promiscuous_tx(priv, txb); #endif ret = ipw_tx_skb(priv, txb, pri); if (ret == NETDEV_TX_OK) __ipw_led_activity_on(priv); spin_unlock_irqrestore(&priv->lock, flags); return ret; } static void ipw_net_set_multicast_list(struct net_device *dev) { } static int ipw_net_set_mac_address(struct net_device *dev, void *p) { struct ipw_priv *priv = libipw_priv(dev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; mutex_lock(&priv->mutex); priv->config |= CFG_CUSTOM_MAC; memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); printk(KERN_INFO "%s: Setting MAC to %pM\n", priv->net_dev->name, priv->mac_addr); schedule_work(&priv->adapter_restart); mutex_unlock(&priv->mutex); return 0; } static void ipw_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct ipw_priv *p = libipw_priv(dev); char vers[64]; char date[32]; u32 len; strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); len = sizeof(vers); ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len); len = sizeof(date); ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len); snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)", vers, date); strlcpy(info->bus_info, pci_name(p->pci_dev), sizeof(info->bus_info)); info->eedump_len = IPW_EEPROM_IMAGE_SIZE; } static u32 ipw_ethtool_get_link(struct net_device *dev) { struct ipw_priv *priv = libipw_priv(dev); return (priv->status & STATUS_ASSOCIATED) != 0; } static int ipw_ethtool_get_eeprom_len(struct net_device *dev) { return IPW_EEPROM_IMAGE_SIZE; } static int ipw_ethtool_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { struct ipw_priv *p = libipw_priv(dev); if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) return -EINVAL; mutex_lock(&p->mutex); memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len); mutex_unlock(&p->mutex); return 0; } static int ipw_ethtool_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { struct ipw_priv *p = libipw_priv(dev); int i; if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) return -EINVAL; mutex_lock(&p->mutex); memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len); for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++) ipw_write8(p, i + IPW_EEPROM_DATA, p->eeprom[i]); mutex_unlock(&p->mutex); return 0; } static const struct ethtool_ops ipw_ethtool_ops = { .get_link = ipw_ethtool_get_link, .get_drvinfo = ipw_ethtool_get_drvinfo, .get_eeprom_len = ipw_ethtool_get_eeprom_len, .get_eeprom = ipw_ethtool_get_eeprom, .set_eeprom = ipw_ethtool_set_eeprom, }; static irqreturn_t ipw_isr(int irq, void *data) { struct ipw_priv *priv = data; u32 inta, inta_mask; if (!priv) return IRQ_NONE; spin_lock(&priv->irq_lock); if (!(priv->status & STATUS_INT_ENABLED)) { /* IRQ is disabled */ goto none; } inta = ipw_read32(priv, IPW_INTA_RW); inta_mask = ipw_read32(priv, IPW_INTA_MASK_R); if (inta == 0xFFFFFFFF) { /* Hardware disappeared */ IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n"); goto none; } if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) { /* Shared interrupt */ goto none; } /* tell the device to stop sending interrupts */ __ipw_disable_interrupts(priv); /* ack current interrupts */ inta &= (IPW_INTA_MASK_ALL & inta_mask); ipw_write32(priv, IPW_INTA_RW, inta); /* Cache INTA value for our tasklet */ priv->isr_inta = inta; tasklet_schedule(&priv->irq_tasklet); spin_unlock(&priv->irq_lock); return IRQ_HANDLED; none: spin_unlock(&priv->irq_lock); return IRQ_NONE; } static void ipw_rf_kill(void *adapter) { struct ipw_priv *priv = adapter; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); if (rf_kill_active(priv)) { IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n"); schedule_delayed_work(&priv->rf_kill, 2 * HZ); goto exit_unlock; } /* RF Kill is now disabled, so bring the device back up */ if (!(priv->status & STATUS_RF_KILL_MASK)) { IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting " "device\n"); /* we can not do an adapter restart while inside an irq lock */ schedule_work(&priv->adapter_restart); } else IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " "enabled\n"); exit_unlock: spin_unlock_irqrestore(&priv->lock, flags); } static void ipw_bg_rf_kill(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, rf_kill.work); mutex_lock(&priv->mutex); ipw_rf_kill(priv); mutex_unlock(&priv->mutex); } static void ipw_link_up(struct ipw_priv *priv) { priv->last_seq_num = -1; priv->last_frag_num = -1; priv->last_packet_time = 0; netif_carrier_on(priv->net_dev); cancel_delayed_work(&priv->request_scan); cancel_delayed_work(&priv->request_direct_scan); cancel_delayed_work(&priv->request_passive_scan); cancel_delayed_work(&priv->scan_event); ipw_reset_stats(priv); /* Ensure the rate is updated immediately */ priv->last_rate = ipw_get_current_rate(priv); ipw_gather_stats(priv); ipw_led_link_up(priv); notify_wx_assoc_event(priv); if (priv->config & CFG_BACKGROUND_SCAN) schedule_delayed_work(&priv->request_scan, HZ); } static void ipw_bg_link_up(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, link_up); mutex_lock(&priv->mutex); ipw_link_up(priv); mutex_unlock(&priv->mutex); } static void ipw_link_down(struct ipw_priv *priv) { ipw_led_link_down(priv); netif_carrier_off(priv->net_dev); notify_wx_assoc_event(priv); /* Cancel any queued work ... */ cancel_delayed_work(&priv->request_scan); cancel_delayed_work(&priv->request_direct_scan); cancel_delayed_work(&priv->request_passive_scan); cancel_delayed_work(&priv->adhoc_check); cancel_delayed_work(&priv->gather_stats); ipw_reset_stats(priv); if (!(priv->status & STATUS_EXIT_PENDING)) { /* Queue up another scan... */ schedule_delayed_work(&priv->request_scan, 0); } else cancel_delayed_work(&priv->scan_event); } static void ipw_bg_link_down(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, link_down); mutex_lock(&priv->mutex); ipw_link_down(priv); mutex_unlock(&priv->mutex); } static int __devinit ipw_setup_deferred_work(struct ipw_priv *priv) { int ret = 0; init_waitqueue_head(&priv->wait_command_queue); init_waitqueue_head(&priv->wait_state); INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check); INIT_WORK(&priv->associate, ipw_bg_associate); INIT_WORK(&priv->disassociate, ipw_bg_disassociate); INIT_WORK(&priv->system_config, ipw_system_config); INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish); INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart); INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill); INIT_WORK(&priv->up, ipw_bg_up); INIT_WORK(&priv->down, ipw_bg_down); INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan); INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan); INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan); INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event); INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats); INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan); INIT_WORK(&priv->roam, ipw_bg_roam); INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check); INIT_WORK(&priv->link_up, ipw_bg_link_up); INIT_WORK(&priv->link_down, ipw_bg_link_down); INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on); INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off); INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off); INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network); #ifdef CONFIG_IPW2200_QOS INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate); #endif /* CONFIG_IPW2200_QOS */ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) ipw_irq_tasklet, (unsigned long)priv); return ret; } static void shim__set_security(struct net_device *dev, struct libipw_security *sec) { struct ipw_priv *priv = libipw_priv(dev); int i; for (i = 0; i < 4; i++) { if (sec->flags & (1 << i)) { priv->ieee->sec.encode_alg[i] = sec->encode_alg[i]; priv->ieee->sec.key_sizes[i] = sec->key_sizes[i]; if (sec->key_sizes[i] == 0) priv->ieee->sec.flags &= ~(1 << i); else { memcpy(priv->ieee->sec.keys[i], sec->keys[i], sec->key_sizes[i]); priv->ieee->sec.flags |= (1 << i); } priv->status |= STATUS_SECURITY_UPDATED; } else if (sec->level != SEC_LEVEL_1) priv->ieee->sec.flags &= ~(1 << i); } if (sec->flags & SEC_ACTIVE_KEY) { if (sec->active_key <= 3) { priv->ieee->sec.active_key = sec->active_key; priv->ieee->sec.flags |= SEC_ACTIVE_KEY; } else priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; priv->status |= STATUS_SECURITY_UPDATED; } else priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; if ((sec->flags & SEC_AUTH_MODE) && (priv->ieee->sec.auth_mode != sec->auth_mode)) { priv->ieee->sec.auth_mode = sec->auth_mode; priv->ieee->sec.flags |= SEC_AUTH_MODE; if (sec->auth_mode == WLAN_AUTH_SHARED_KEY) priv->capability |= CAP_SHARED_KEY; else priv->capability &= ~CAP_SHARED_KEY; priv->status |= STATUS_SECURITY_UPDATED; } if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) { priv->ieee->sec.flags |= SEC_ENABLED; priv->ieee->sec.enabled = sec->enabled; priv->status |= STATUS_SECURITY_UPDATED; if (sec->enabled) priv->capability |= CAP_PRIVACY_ON; else priv->capability &= ~CAP_PRIVACY_ON; } if (sec->flags & SEC_ENCRYPT) priv->ieee->sec.encrypt = sec->encrypt; if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) { priv->ieee->sec.level = sec->level; priv->ieee->sec.flags |= SEC_LEVEL; priv->status |= STATUS_SECURITY_UPDATED; } if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT)) ipw_set_hwcrypto_keys(priv); /* To match current functionality of ipw2100 (which works well w/ * various supplicants, we don't force a disassociate if the * privacy capability changes ... */ #if 0 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) && (((priv->assoc_request.capability & cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && !sec->enabled) || (!(priv->assoc_request.capability & cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && sec->enabled))) { IPW_DEBUG_ASSOC("Disassociating due to capability " "change.\n"); ipw_disassociate(priv); } #endif } static int init_supported_rates(struct ipw_priv *priv, struct ipw_supported_rates *rates) { /* TODO: Mask out rates based on priv->rates_mask */ memset(rates, 0, sizeof(*rates)); /* configure supported rates */ switch (priv->ieee->freq_band) { case LIBIPW_52GHZ_BAND: rates->ieee_mode = IPW_A_MODE; rates->purpose = IPW_RATE_CAPABILITIES; ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, LIBIPW_OFDM_DEFAULT_RATES_MASK); break; default: /* Mixed or 2.4Ghz */ rates->ieee_mode = IPW_G_MODE; rates->purpose = IPW_RATE_CAPABILITIES; ipw_add_cck_scan_rates(rates, LIBIPW_CCK_MODULATION, LIBIPW_CCK_DEFAULT_RATES_MASK); if (priv->ieee->modulation & LIBIPW_OFDM_MODULATION) { ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, LIBIPW_OFDM_DEFAULT_RATES_MASK); } break; } return 0; } static int ipw_config(struct ipw_priv *priv) { /* This is only called from ipw_up, which resets/reloads the firmware so, we don't need to first disable the card before we configure it */ if (ipw_set_tx_power(priv)) goto error; /* initialize adapter address */ if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr)) goto error; /* set basic system config settings */ init_sys_config(&priv->sys_config); /* Support Bluetooth if we have BT h/w on board, and user wants to. * Does not support BT priority yet (don't abort or defer our Tx) */ if (bt_coexist) { unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY]; if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG) priv->sys_config.bt_coexistence |= CFG_BT_COEXISTENCE_SIGNAL_CHNL; if (bt_caps & EEPROM_SKU_CAP_BT_OOB) priv->sys_config.bt_coexistence |= CFG_BT_COEXISTENCE_OOB; } #ifdef CONFIG_IPW2200_PROMISCUOUS if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) { priv->sys_config.accept_all_data_frames = 1; priv->sys_config.accept_non_directed_frames = 1; priv->sys_config.accept_all_mgmt_bcpr = 1; priv->sys_config.accept_all_mgmt_frames = 1; } #endif if (priv->ieee->iw_mode == IW_MODE_ADHOC) priv->sys_config.answer_broadcast_ssid_probe = 1; else priv->sys_config.answer_broadcast_ssid_probe = 0; if (ipw_send_system_config(priv)) goto error; init_supported_rates(priv, &priv->rates); if (ipw_send_supported_rates(priv, &priv->rates)) goto error; /* Set request-to-send threshold */ if (priv->rts_threshold) { if (ipw_send_rts_threshold(priv, priv->rts_threshold)) goto error; } #ifdef CONFIG_IPW2200_QOS IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n"); ipw_qos_activate(priv, NULL); #endif /* CONFIG_IPW2200_QOS */ if (ipw_set_random_seed(priv)) goto error; /* final state transition to the RUN state */ if (ipw_send_host_complete(priv)) goto error; priv->status |= STATUS_INIT; ipw_led_init(priv); ipw_led_radio_on(priv); priv->notif_missed_beacons = 0; /* Set hardware WEP key if it is configured. */ if ((priv->capability & CAP_PRIVACY_ON) && (priv->ieee->sec.level == SEC_LEVEL_1) && !(priv->ieee->host_encrypt || priv->ieee->host_decrypt)) ipw_set_hwcrypto_keys(priv); return 0; error: return -EIO; } /* * NOTE: * * These tables have been tested in conjunction with the * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters. * * Altering this values, using it on other hardware, or in geographies * not intended for resale of the above mentioned Intel adapters has * not been tested. * * Remember to update the table in README.ipw2200 when changing this * table. * */ static const struct libipw_geo ipw_geos[] = { { /* Restricted */ "---", .bg_channels = 11, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, }, { /* Custom US/Canada */ "ZZF", .bg_channels = 11, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, .a_channels = 8, .a = {{5180, 36}, {5200, 40}, {5220, 44}, {5240, 48}, {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, {5320, 64, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Rest of World */ "ZZD", .bg_channels = 13, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, {2472, 13}}, }, { /* Custom USA & Europe & High */ "ZZA", .bg_channels = 11, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, .a_channels = 13, .a = {{5180, 36}, {5200, 40}, {5220, 44}, {5240, 48}, {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, {5745, 149}, {5765, 153}, {5785, 157}, {5805, 161}, {5825, 165}}, }, { /* Custom NA & Europe */ "ZZB", .bg_channels = 11, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, .a_channels = 13, .a = {{5180, 36}, {5200, 40}, {5220, 44}, {5240, 48}, {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Custom Japan */ "ZZC", .bg_channels = 11, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, .a_channels = 4, .a = {{5170, 34}, {5190, 38}, {5210, 42}, {5230, 46}}, }, { /* Custom */ "ZZM", .bg_channels = 11, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, }, { /* Europe */ "ZZE", .bg_channels = 13, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, {2472, 13}}, .a_channels = 19, .a = {{5180, 36}, {5200, 40}, {5220, 44}, {5240, 48}, {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, {5700, 140, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Custom Japan */ "ZZJ", .bg_channels = 14, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY}}, .a_channels = 4, .a = {{5170, 34}, {5190, 38}, {5210, 42}, {5230, 46}}, }, { /* Rest of World */ "ZZR", .bg_channels = 14, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY | LIBIPW_CH_PASSIVE_ONLY}}, }, { /* High Band */ "ZZH", .bg_channels = 13, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, .a_channels = 4, .a = {{5745, 149}, {5765, 153}, {5785, 157}, {5805, 161}}, }, { /* Custom Europe */ "ZZG", .bg_channels = 13, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, {2472, 13}}, .a_channels = 4, .a = {{5180, 36}, {5200, 40}, {5220, 44}, {5240, 48}}, }, { /* Europe */ "ZZK", .bg_channels = 13, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, .a_channels = 24, .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, {5700, 140, LIBIPW_CH_PASSIVE_ONLY}, {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Europe */ "ZZL", .bg_channels = 11, .bg = {{2412, 1}, {2417, 2}, {2422, 3}, {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, .a_channels = 13, .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, } }; #define MAX_HW_RESTARTS 5 static int ipw_up(struct ipw_priv *priv) { int rc, i, j; /* Age scan list entries found before suspend */ if (priv->suspend_time) { libipw_networks_age(priv->ieee, priv->suspend_time); priv->suspend_time = 0; } if (priv->status & STATUS_EXIT_PENDING) return -EIO; if (cmdlog && !priv->cmdlog) { priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog), GFP_KERNEL); if (priv->cmdlog == NULL) { IPW_ERROR("Error allocating %d command log entries.\n", cmdlog); return -ENOMEM; } else { priv->cmdlog_len = cmdlog; } } for (i = 0; i < MAX_HW_RESTARTS; i++) { /* Load the microcode, firmware, and eeprom. * Also start the clocks. */ rc = ipw_load(priv); if (rc) { IPW_ERROR("Unable to load firmware: %d\n", rc); return rc; } ipw_init_ordinals(priv); if (!(priv->config & CFG_CUSTOM_MAC)) eeprom_parse_mac(priv, priv->mac_addr); memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); memcpy(priv->net_dev->perm_addr, priv->mac_addr, ETH_ALEN); for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) { if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE], ipw_geos[j].name, 3)) break; } if (j == ARRAY_SIZE(ipw_geos)) { IPW_WARNING("SKU [%c%c%c] not recognized.\n", priv->eeprom[EEPROM_COUNTRY_CODE + 0], priv->eeprom[EEPROM_COUNTRY_CODE + 1], priv->eeprom[EEPROM_COUNTRY_CODE + 2]); j = 0; } if (libipw_set_geo(priv->ieee, &ipw_geos[j])) { IPW_WARNING("Could not set geography."); return 0; } if (priv->status & STATUS_RF_KILL_SW) { IPW_WARNING("Radio disabled by module parameter.\n"); return 0; } else if (rf_kill_active(priv)) { IPW_WARNING("Radio Frequency Kill Switch is On:\n" "Kill switch must be turned off for " "wireless networking to work.\n"); schedule_delayed_work(&priv->rf_kill, 2 * HZ); return 0; } rc = ipw_config(priv); if (!rc) { IPW_DEBUG_INFO("Configured device on count %i\n", i); /* If configure to try and auto-associate, kick * off a scan. */ schedule_delayed_work(&priv->request_scan, 0); return 0; } IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc); IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n", i, MAX_HW_RESTARTS); /* We had an error bringing up the hardware, so take it * all the way back down so we can try again */ ipw_down(priv); } /* tried to restart and config the device for as long as our * patience could withstand */ IPW_ERROR("Unable to initialize device after %d attempts.\n", i); return -EIO; } static void ipw_bg_up(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, up); mutex_lock(&priv->mutex); ipw_up(priv); mutex_unlock(&priv->mutex); } static void ipw_deinit(struct ipw_priv *priv) { int i; if (priv->status & STATUS_SCANNING) { IPW_DEBUG_INFO("Aborting scan during shutdown.\n"); ipw_abort_scan(priv); } if (priv->status & STATUS_ASSOCIATED) { IPW_DEBUG_INFO("Disassociating during shutdown.\n"); ipw_disassociate(priv); } ipw_led_shutdown(priv); /* Wait up to 1s for status to change to not scanning and not * associated (disassociation can take a while for a ful 802.11 * exchange */ for (i = 1000; i && (priv->status & (STATUS_DISASSOCIATING | STATUS_ASSOCIATED | STATUS_SCANNING)); i--) udelay(10); if (priv->status & (STATUS_DISASSOCIATING | STATUS_ASSOCIATED | STATUS_SCANNING)) IPW_DEBUG_INFO("Still associated or scanning...\n"); else IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i); /* Attempt to disable the card */ ipw_send_card_disable(priv, 0); priv->status &= ~STATUS_INIT; } static void ipw_down(struct ipw_priv *priv) { int exit_pending = priv->status & STATUS_EXIT_PENDING; priv->status |= STATUS_EXIT_PENDING; if (ipw_is_init(priv)) ipw_deinit(priv); /* Wipe out the EXIT_PENDING status bit if we are not actually * exiting the module */ if (!exit_pending) priv->status &= ~STATUS_EXIT_PENDING; /* tell the device to stop sending interrupts */ ipw_disable_interrupts(priv); /* Clear all bits but the RF Kill */ priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING; netif_carrier_off(priv->net_dev); ipw_stop_nic(priv); ipw_led_radio_off(priv); } static void ipw_bg_down(struct work_struct *work) { struct ipw_priv *priv = container_of(work, struct ipw_priv, down); mutex_lock(&priv->mutex); ipw_down(priv); mutex_unlock(&priv->mutex); } static int ipw_wdev_init(struct net_device *dev) { int i, rc = 0; struct ipw_priv *priv = libipw_priv(dev); const struct libipw_geo *geo = libipw_get_geo(priv->ieee); struct wireless_dev *wdev = &priv->ieee->wdev; memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); /* fill-out priv->ieee->bg_band */ if (geo->bg_channels) { struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; bg_band->band = IEEE80211_BAND_2GHZ; bg_band->n_channels = geo->bg_channels; bg_band->channels = kcalloc(geo->bg_channels, sizeof(struct ieee80211_channel), GFP_KERNEL); if (!bg_band->channels) { rc = -ENOMEM; goto out; } /* translate geo->bg to bg_band.channels */ for (i = 0; i < geo->bg_channels; i++) { bg_band->channels[i].band = IEEE80211_BAND_2GHZ; bg_band->channels[i].center_freq = geo->bg[i].freq; bg_band->channels[i].hw_value = geo->bg[i].channel; bg_band->channels[i].max_power = geo->bg[i].max_power; if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) bg_band->channels[i].flags |= IEEE80211_CHAN_PASSIVE_SCAN; if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) bg_band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS; if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) bg_band->channels[i].flags |= IEEE80211_CHAN_RADAR; /* No equivalent for LIBIPW_CH_80211H_RULES, LIBIPW_CH_UNIFORM_SPREADING, or LIBIPW_CH_B_ONLY... */ } /* point at bitrate info */ bg_band->bitrates = ipw2200_bg_rates; bg_band->n_bitrates = ipw2200_num_bg_rates; wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; } /* fill-out priv->ieee->a_band */ if (geo->a_channels) { struct ieee80211_supported_band *a_band = &priv->ieee->a_band; a_band->band = IEEE80211_BAND_5GHZ; a_band->n_channels = geo->a_channels; a_band->channels = kcalloc(geo->a_channels, sizeof(struct ieee80211_channel), GFP_KERNEL); if (!a_band->channels) { rc = -ENOMEM; goto out; } /* translate geo->a to a_band.channels */ for (i = 0; i < geo->a_channels; i++) { a_band->channels[i].band = IEEE80211_BAND_5GHZ; a_band->channels[i].center_freq = geo->a[i].freq; a_band->channels[i].hw_value = geo->a[i].channel; a_band->channels[i].max_power = geo->a[i].max_power; if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) a_band->channels[i].flags |= IEEE80211_CHAN_PASSIVE_SCAN; if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) a_band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS; if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) a_band->channels[i].flags |= IEEE80211_CHAN_RADAR; /* No equivalent for LIBIPW_CH_80211H_RULES, LIBIPW_CH_UNIFORM_SPREADING, or LIBIPW_CH_B_ONLY... */ } /* point at bitrate info */ a_band->bitrates = ipw2200_a_rates; a_band->n_bitrates = ipw2200_num_a_rates; wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band; } wdev->wiphy->cipher_suites = ipw_cipher_suites; wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites); set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); /* With that information in place, we can now register the wiphy... */ if (wiphy_register(wdev->wiphy)) rc = -EIO; out: return rc; } /* PCI driver stuff */ static DEFINE_PCI_DEVICE_TABLE(card_ids) = { {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0}, {PCI_VDEVICE(INTEL, 0x104f), 0}, {PCI_VDEVICE(INTEL, 0x4220), 0}, /* BG */ {PCI_VDEVICE(INTEL, 0x4221), 0}, /* BG */ {PCI_VDEVICE(INTEL, 0x4223), 0}, /* ABG */ {PCI_VDEVICE(INTEL, 0x4224), 0}, /* ABG */ /* required last entry */ {0,} }; MODULE_DEVICE_TABLE(pci, card_ids); static struct attribute *ipw_sysfs_entries[] = { &dev_attr_rf_kill.attr, &dev_attr_direct_dword.attr, &dev_attr_indirect_byte.attr, &dev_attr_indirect_dword.attr, &dev_attr_mem_gpio_reg.attr, &dev_attr_command_event_reg.attr, &dev_attr_nic_type.attr, &dev_attr_status.attr, &dev_attr_cfg.attr, &dev_attr_error.attr, &dev_attr_event_log.attr, &dev_attr_cmd_log.attr, &dev_attr_eeprom_delay.attr, &dev_attr_ucode_version.attr, &dev_attr_rtc.attr, &dev_attr_scan_age.attr, &dev_attr_led.attr, &dev_attr_speed_scan.attr, &dev_attr_net_stats.attr, &dev_attr_channels.attr, #ifdef CONFIG_IPW2200_PROMISCUOUS &dev_attr_rtap_iface.attr, &dev_attr_rtap_filter.attr, #endif NULL }; static struct attribute_group ipw_attribute_group = { .name = NULL, /* put in device directory */ .attrs = ipw_sysfs_entries, }; #ifdef CONFIG_IPW2200_PROMISCUOUS static int ipw_prom_open(struct net_device *dev) { struct ipw_prom_priv *prom_priv = libipw_priv(dev); struct ipw_priv *priv = prom_priv->priv; IPW_DEBUG_INFO("prom dev->open\n"); netif_carrier_off(dev); if (priv->ieee->iw_mode != IW_MODE_MONITOR) { priv->sys_config.accept_all_data_frames = 1; priv->sys_config.accept_non_directed_frames = 1; priv->sys_config.accept_all_mgmt_bcpr = 1; priv->sys_config.accept_all_mgmt_frames = 1; ipw_send_system_config(priv); } return 0; } static int ipw_prom_stop(struct net_device *dev) { struct ipw_prom_priv *prom_priv = libipw_priv(dev); struct ipw_priv *priv = prom_priv->priv; IPW_DEBUG_INFO("prom dev->stop\n"); if (priv->ieee->iw_mode != IW_MODE_MONITOR) { priv->sys_config.accept_all_data_frames = 0; priv->sys_config.accept_non_directed_frames = 0; priv->sys_config.accept_all_mgmt_bcpr = 0; priv->sys_config.accept_all_mgmt_frames = 0; ipw_send_system_config(priv); } return 0; } static netdev_tx_t ipw_prom_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { IPW_DEBUG_INFO("prom dev->xmit\n"); dev_kfree_skb(skb); return NETDEV_TX_OK; } static const struct net_device_ops ipw_prom_netdev_ops = { .ndo_open = ipw_prom_open, .ndo_stop = ipw_prom_stop, .ndo_start_xmit = ipw_prom_hard_start_xmit, .ndo_change_mtu = libipw_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; static int ipw_prom_alloc(struct ipw_priv *priv) { int rc = 0; if (priv->prom_net_dev) return -EPERM; priv->prom_net_dev = alloc_libipw(sizeof(struct ipw_prom_priv), 1); if (priv->prom_net_dev == NULL) return -ENOMEM; priv->prom_priv = libipw_priv(priv->prom_net_dev); priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev); priv->prom_priv->priv = priv; strcpy(priv->prom_net_dev->name, "rtap%d"); memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN); priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP; netdev_attach_ops(priv->prom_net_dev, &ipw_prom_netdev_ops); priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR; SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev); rc = register_netdev(priv->prom_net_dev); if (rc) { free_libipw(priv->prom_net_dev, 1); priv->prom_net_dev = NULL; return rc; } return 0; } static void ipw_prom_free(struct ipw_priv *priv) { if (!priv->prom_net_dev) return; unregister_netdev(priv->prom_net_dev); free_libipw(priv->prom_net_dev, 1); priv->prom_net_dev = NULL; } #endif static const struct net_device_ops ipw_netdev_ops = { .ndo_open = ipw_net_open, .ndo_stop = ipw_net_stop, .ndo_set_rx_mode = ipw_net_set_multicast_list, .ndo_set_mac_address = ipw_net_set_mac_address, .ndo_start_xmit = libipw_xmit, .ndo_change_mtu = libipw_change_mtu, .ndo_validate_addr = eth_validate_addr, }; static int __devinit ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err = 0; struct net_device *net_dev; void __iomem *base; u32 length, val; struct ipw_priv *priv; int i; net_dev = alloc_libipw(sizeof(struct ipw_priv), 0); if (net_dev == NULL) { err = -ENOMEM; goto out; } priv = libipw_priv(net_dev); priv->ieee = netdev_priv(net_dev); priv->net_dev = net_dev; priv->pci_dev = pdev; ipw_debug_level = debug; spin_lock_init(&priv->irq_lock); spin_lock_init(&priv->lock); for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) INIT_LIST_HEAD(&priv->ibss_mac_hash[i]); mutex_init(&priv->mutex); if (pci_enable_device(pdev)) { err = -ENODEV; goto out_free_libipw; } pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (!err) err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n"); goto out_pci_disable_device; } pci_set_drvdata(pdev, priv); err = pci_request_regions(pdev, DRV_NAME); if (err) goto out_pci_disable_device; /* We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); length = pci_resource_len(pdev, 0); priv->hw_len = length; base = pci_ioremap_bar(pdev, 0); if (!base) { err = -ENODEV; goto out_pci_release_regions; } priv->hw_base = base; IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length); IPW_DEBUG_INFO("pci_resource_base = %p\n", base); err = ipw_setup_deferred_work(priv); if (err) { IPW_ERROR("Unable to setup deferred work\n"); goto out_iounmap; } ipw_sw_reset(priv, 1); err = request_irq(pdev->irq, ipw_isr, IRQF_SHARED, DRV_NAME, priv); if (err) { IPW_ERROR("Error allocating IRQ %d\n", pdev->irq); goto out_iounmap; } SET_NETDEV_DEV(net_dev, &pdev->dev); mutex_lock(&priv->mutex); priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit; priv->ieee->set_security = shim__set_security; priv->ieee->is_queue_full = ipw_net_is_queue_full; #ifdef CONFIG_IPW2200_QOS priv->ieee->is_qos_active = ipw_is_qos_active; priv->ieee->handle_probe_response = ipw_handle_beacon; priv->ieee->handle_beacon = ipw_handle_probe_response; priv->ieee->handle_assoc_response = ipw_handle_assoc_response; #endif /* CONFIG_IPW2200_QOS */ priv->ieee->perfect_rssi = -20; priv->ieee->worst_rssi = -85; netdev_attach_ops(net_dev, &ipw_netdev_ops); priv->wireless_data.spy_data = &priv->ieee->spy_data; net_dev->wireless_data = &priv->wireless_data; net_dev->wireless_handlers = &ipw_wx_handler_def; net_dev->ethtool_ops = &ipw_ethtool_ops; err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group); if (err) { IPW_ERROR("failed to create sysfs device attributes\n"); mutex_unlock(&priv->mutex); goto out_release_irq; } if (ipw_up(priv)) { mutex_unlock(&priv->mutex); err = -EIO; goto out_remove_sysfs; } mutex_unlock(&priv->mutex); err = ipw_wdev_init(net_dev); if (err) { IPW_ERROR("failed to register wireless device\n"); goto out_remove_sysfs; } err = register_netdev(net_dev); if (err) { IPW_ERROR("failed to register network device\n"); goto out_unregister_wiphy; } #ifdef CONFIG_IPW2200_PROMISCUOUS if (rtap_iface) { err = ipw_prom_alloc(priv); if (err) { IPW_ERROR("Failed to register promiscuous network " "device (error %d).\n", err); unregister_netdev(priv->net_dev); goto out_unregister_wiphy; } } #endif printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg " "channels, %d 802.11a channels)\n", priv->ieee->geo.name, priv->ieee->geo.bg_channels, priv->ieee->geo.a_channels); return 0; out_unregister_wiphy: wiphy_unregister(priv->ieee->wdev.wiphy); kfree(priv->ieee->a_band.channels); kfree(priv->ieee->bg_band.channels); out_remove_sysfs: sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); out_release_irq: free_irq(pdev->irq, priv); out_iounmap: iounmap(priv->hw_base); out_pci_release_regions: pci_release_regions(pdev); out_pci_disable_device: pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); out_free_libipw: free_libipw(priv->net_dev, 0); out: return err; } static void __devexit ipw_pci_remove(struct pci_dev *pdev) { struct ipw_priv *priv = pci_get_drvdata(pdev); struct list_head *p, *q; int i; if (!priv) return; mutex_lock(&priv->mutex); priv->status |= STATUS_EXIT_PENDING; ipw_down(priv); sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); mutex_unlock(&priv->mutex); unregister_netdev(priv->net_dev); if (priv->rxq) { ipw_rx_queue_free(priv, priv->rxq); priv->rxq = NULL; } ipw_tx_queue_free(priv); if (priv->cmdlog) { kfree(priv->cmdlog); priv->cmdlog = NULL; } /* make sure all works are inactive */ cancel_delayed_work_sync(&priv->adhoc_check); cancel_work_sync(&priv->associate); cancel_work_sync(&priv->disassociate); cancel_work_sync(&priv->system_config); cancel_work_sync(&priv->rx_replenish); cancel_work_sync(&priv->adapter_restart); cancel_delayed_work_sync(&priv->rf_kill); cancel_work_sync(&priv->up); cancel_work_sync(&priv->down); cancel_delayed_work_sync(&priv->request_scan); cancel_delayed_work_sync(&priv->request_direct_scan); cancel_delayed_work_sync(&priv->request_passive_scan); cancel_delayed_work_sync(&priv->scan_event); cancel_delayed_work_sync(&priv->gather_stats); cancel_work_sync(&priv->abort_scan); cancel_work_sync(&priv->roam); cancel_delayed_work_sync(&priv->scan_check); cancel_work_sync(&priv->link_up); cancel_work_sync(&priv->link_down); cancel_delayed_work_sync(&priv->led_link_on); cancel_delayed_work_sync(&priv->led_link_off); cancel_delayed_work_sync(&priv->led_act_off); cancel_work_sync(&priv->merge_networks); /* Free MAC hash list for ADHOC */ for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) { list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) { list_del(p); kfree(list_entry(p, struct ipw_ibss_seq, list)); } } kfree(priv->error); priv->error = NULL; #ifdef CONFIG_IPW2200_PROMISCUOUS ipw_prom_free(priv); #endif free_irq(pdev->irq, priv); iounmap(priv->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); /* wiphy_unregister needs to be here, before free_libipw */ wiphy_unregister(priv->ieee->wdev.wiphy); kfree(priv->ieee->a_band.channels); kfree(priv->ieee->bg_band.channels); free_libipw(priv->net_dev, 0); free_firmware(); } #ifdef CONFIG_PM static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state) { struct ipw_priv *priv = pci_get_drvdata(pdev); struct net_device *dev = priv->net_dev; printk(KERN_INFO "%s: Going into suspend...\n", dev->name); /* Take down the device; powers it off, etc. */ ipw_down(priv); /* Remove the PRESENT state of the device */ netif_device_detach(dev); pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); priv->suspend_at = get_seconds(); return 0; } static int ipw_pci_resume(struct pci_dev *pdev) { struct ipw_priv *priv = pci_get_drvdata(pdev); struct net_device *dev = priv->net_dev; int err; u32 val; printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name); pci_set_power_state(pdev, PCI_D0); err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s: pci_enable_device failed on resume\n", dev->name); return err; } pci_restore_state(pdev); /* * Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries * from interfering with C3 CPU state. pci_restore_state won't help * here since it only restores the first 64 bytes pci config header. */ pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); /* Set the device back into the PRESENT state; this will also wake * the queue of needed */ netif_device_attach(dev); priv->suspend_time = get_seconds() - priv->suspend_at; /* Bring the device back up */ schedule_work(&priv->up); return 0; } #endif static void ipw_pci_shutdown(struct pci_dev *pdev) { struct ipw_priv *priv = pci_get_drvdata(pdev); /* Take down the device; powers it off, etc. */ ipw_down(priv); pci_disable_device(pdev); } /* driver initialization stuff */ static struct pci_driver ipw_driver = { .name = DRV_NAME, .id_table = card_ids, .probe = ipw_pci_probe, .remove = __devexit_p(ipw_pci_remove), #ifdef CONFIG_PM .suspend = ipw_pci_suspend, .resume = ipw_pci_resume, #endif .shutdown = ipw_pci_shutdown, }; static int __init ipw_init(void) { int ret; printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); ret = pci_register_driver(&ipw_driver); if (ret) { IPW_ERROR("Unable to initialize PCI module\n"); return ret; } ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level); if (ret) { IPW_ERROR("Unable to create driver sysfs file\n"); pci_unregister_driver(&ipw_driver); return ret; } return ret; } static void __exit ipw_exit(void) { driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level); pci_unregister_driver(&ipw_driver); } module_param(disable, int, 0444); MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); module_param(associate, int, 0444); MODULE_PARM_DESC(associate, "auto associate when scanning (default off)"); module_param(auto_create, int, 0444); MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)"); module_param_named(led, led_support, int, 0444); MODULE_PARM_DESC(led, "enable led control on some systems (default 1 on)"); module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "debug output mask"); module_param_named(channel, default_channel, int, 0444); MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])"); #ifdef CONFIG_IPW2200_PROMISCUOUS module_param(rtap_iface, int, 0444); MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)"); #endif #ifdef CONFIG_IPW2200_QOS module_param(qos_enable, int, 0444); MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis"); module_param(qos_burst_enable, int, 0444); MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode"); module_param(qos_no_ack_mask, int, 0444); MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack"); module_param(burst_duration_CCK, int, 0444); MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value"); module_param(burst_duration_OFDM, int, 0444); MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value"); #endif /* CONFIG_IPW2200_QOS */ #ifdef CONFIG_IPW2200_MONITOR module_param_named(mode, network_mode, int, 0444); MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); #else module_param_named(mode, network_mode, int, 0444); MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)"); #endif module_param(bt_coexist, int, 0444); MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)"); module_param(hwcrypto, int, 0444); MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default off)"); module_param(cmdlog, int, 0444); MODULE_PARM_DESC(cmdlog, "allocate a ring buffer for logging firmware commands"); module_param(roaming, int, 0444); MODULE_PARM_DESC(roaming, "enable roaming support (default on)"); module_param(antenna, int, 0444); MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)"); module_exit(ipw_exit); module_init(ipw_init); compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/libipw_wx.c0000644000175000017500000004777412026211315024065 0ustar mcgrofmcgrof/****************************************************************************** Copyright(c) 2004-2005 Intel Corporation. All rights reserved. Portions of this file are based on the WEP enablement code provided by the Host AP project hostap-drivers v0.1.3 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen Copyright (c) 2002-2003, Jouni Malinen This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ #include #include #include #include #include #include #include #include "libipw.h" static const char *libipw_modes[] = { "?", "a", "b", "ab", "g", "ag", "bg", "abg" }; static inline unsigned int elapsed_jiffies_msecs(unsigned long start) { unsigned long end = jiffies; if (end >= start) return jiffies_to_msecs(end - start); return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); } #define MAX_CUSTOM_LEN 64 static char *libipw_translate_scan(struct libipw_device *ieee, char *start, char *stop, struct libipw_network *network, struct iw_request_info *info) { char custom[MAX_CUSTOM_LEN]; char *p; struct iw_event iwe; int i, j; char *current_val; /* For rates */ u8 rate; /* First entry *MUST* be the AP MAC address */ iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); /* Remaining entries will be displayed in the order we provide them */ /* Add the ESSID */ iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = min(network->ssid_len, (u8) 32); start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid); /* Add the protocol name */ iwe.cmd = SIOCGIWNAME; snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", libipw_modes[network->mode]); start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); /* Add mode */ iwe.cmd = SIOCGIWMODE; if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { if (network->capability & WLAN_CAPABILITY_ESS) iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); } /* Add channel and frequency */ /* Note : userspace automatically computes channel using iwrange */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel); iwe.u.freq.e = 6; iwe.u.freq.i = 0; start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; if (network->capability & WLAN_CAPABILITY_PRIVACY) iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid); /* Add basic and extended rates */ /* Rate : stuffing multiple values in a single event require a bit * more of magic - Jean II */ current_val = start + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; /* Those two flags are ignored... */ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; for (i = 0, j = 0; i < network->rates_len;) { if (j < network->rates_ex_len && ((network->rates_ex[j] & 0x7F) < (network->rates[i] & 0x7F))) rate = network->rates_ex[j++] & 0x7F; else rate = network->rates[i++] & 0x7F; /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ current_val = iwe_stream_add_value(info, start, current_val, stop, &iwe, IW_EV_PARAM_LEN); } for (; j < network->rates_ex_len; j++) { rate = network->rates_ex[j] & 0x7F; /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ current_val = iwe_stream_add_value(info, start, current_val, stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any rate */ if ((current_val - start) > iwe_stream_lcp_len(info)) start = current_val; /* Add quality statistics */ iwe.cmd = IWEVQUAL; iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED; if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) { iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; iwe.u.qual.qual = 0; } else { if (ieee->perfect_rssi == ieee->worst_rssi) iwe.u.qual.qual = 100; else iwe.u.qual.qual = (100 * (ieee->perfect_rssi - ieee->worst_rssi) * (ieee->perfect_rssi - ieee->worst_rssi) - (ieee->perfect_rssi - network->stats.rssi) * (15 * (ieee->perfect_rssi - ieee->worst_rssi) + 62 * (ieee->perfect_rssi - network->stats.rssi))) / ((ieee->perfect_rssi - ieee->worst_rssi) * (ieee->perfect_rssi - ieee->worst_rssi)); if (iwe.u.qual.qual > 100) iwe.u.qual.qual = 100; else if (iwe.u.qual.qual < 1) iwe.u.qual.qual = 0; } if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) { iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; iwe.u.qual.noise = 0; } else { iwe.u.qual.noise = network->stats.noise; } if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) { iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; iwe.u.qual.level = 0; } else { iwe.u.qual.level = network->stats.signal; } start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); iwe.cmd = IWEVCUSTOM; p = custom; iwe.u.data.length = p - custom; if (iwe.u.data.length) start = iwe_stream_add_point(info, start, stop, &iwe, custom); memset(&iwe, 0, sizeof(iwe)); if (network->wpa_ie_len) { char buf[MAX_WPA_IE_LEN]; memcpy(buf, network->wpa_ie, network->wpa_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->wpa_ie_len; start = iwe_stream_add_point(info, start, stop, &iwe, buf); } memset(&iwe, 0, sizeof(iwe)); if (network->rsn_ie_len) { char buf[MAX_WPA_IE_LEN]; memcpy(buf, network->rsn_ie, network->rsn_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->rsn_ie_len; start = iwe_stream_add_point(info, start, stop, &iwe, buf); } /* Add EXTRA: Age to display seconds since last beacon/probe response * for given network. */ iwe.cmd = IWEVCUSTOM; p = custom; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Last beacon: %ums ago", elapsed_jiffies_msecs(network->last_scanned)); iwe.u.data.length = p - custom; if (iwe.u.data.length) start = iwe_stream_add_point(info, start, stop, &iwe, custom); /* Add spectrum management information */ iwe.cmd = -1; p = custom; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: "); if (libipw_get_channel_flags(ieee, network->channel) & LIBIPW_CH_INVALID) { iwe.cmd = IWEVCUSTOM; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID "); } if (libipw_get_channel_flags(ieee, network->channel) & LIBIPW_CH_RADAR_DETECT) { iwe.cmd = IWEVCUSTOM; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS "); } if (iwe.cmd == IWEVCUSTOM) { iwe.u.data.length = p - custom; start = iwe_stream_add_point(info, start, stop, &iwe, custom); } return start; } #define SCAN_ITEM_SIZE 128 int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct libipw_network *network; unsigned long flags; int err = 0; char *ev = extra; char *stop = ev + wrqu->data.length; int i = 0; DECLARE_SSID_BUF(ssid); LIBIPW_DEBUG_WX("Getting scan\n"); spin_lock_irqsave(&ieee->lock, flags); list_for_each_entry(network, &ieee->network_list, list) { i++; if (stop - ev < SCAN_ITEM_SIZE) { err = -E2BIG; break; } if (ieee->scan_age == 0 || time_after(network->last_scanned + ieee->scan_age, jiffies)) ev = libipw_translate_scan(ieee, ev, stop, network, info); else { LIBIPW_DEBUG_SCAN("Not showing network '%s (" "%pM)' due to age (%ums).\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid, elapsed_jiffies_msecs( network->last_scanned)); } } spin_unlock_irqrestore(&ieee->lock, flags); wrqu->data.length = ev - extra; wrqu->data.flags = 0; LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i); return err; } int libipw_wx_set_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *keybuf) { struct iw_point *erq = &(wrqu->encoding); struct net_device *dev = ieee->dev; struct libipw_security sec = { .flags = 0 }; int i, key, key_provided, len; struct lib80211_crypt_data **crypt; int host_crypto = ieee->host_encrypt || ieee->host_decrypt; DECLARE_SSID_BUF(ssid); LIBIPW_DEBUG_WX("SET_ENCODE\n"); key = erq->flags & IW_ENCODE_INDEX; if (key) { if (key > WEP_KEYS) return -EINVAL; key--; key_provided = 1; } else { key_provided = 0; key = ieee->crypt_info.tx_keyidx; } LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? "provided" : "default"); crypt = &ieee->crypt_info.crypt[key]; if (erq->flags & IW_ENCODE_DISABLED) { if (key_provided && *crypt) { LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n", key); lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); } else LIBIPW_DEBUG_WX("Disabling encryption.\n"); /* Check all the keys to see if any are still configured, * and if no key index was provided, de-init them all */ for (i = 0; i < WEP_KEYS; i++) { if (ieee->crypt_info.crypt[i] != NULL) { if (key_provided) break; lib80211_crypt_delayed_deinit(&ieee->crypt_info, &ieee->crypt_info.crypt[i]); } } if (i == WEP_KEYS) { sec.enabled = 0; sec.encrypt = 0; sec.level = SEC_LEVEL_0; sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT; } goto done; } sec.enabled = 1; sec.encrypt = 1; sec.flags |= SEC_ENABLED | SEC_ENCRYPT; if (*crypt != NULL && (*crypt)->ops != NULL && strcmp((*crypt)->ops->name, "WEP") != 0) { /* changing to use WEP; deinit previously used algorithm * on this key */ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); } if (*crypt == NULL && host_crypto) { struct lib80211_crypt_data *new_crypt; /* take WEP into use */ new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), GFP_KERNEL); if (new_crypt == NULL) return -ENOMEM; new_crypt->ops = lib80211_get_crypto_ops("WEP"); if (!new_crypt->ops) { request_module("lib80211_crypt_wep"); new_crypt->ops = lib80211_get_crypto_ops("WEP"); } if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) new_crypt->priv = new_crypt->ops->init(key); if (!new_crypt->ops || !new_crypt->priv) { kfree(new_crypt); new_crypt = NULL; printk(KERN_WARNING "%s: could not initialize WEP: " "load module lib80211_crypt_wep\n", dev->name); return -EOPNOTSUPP; } *crypt = new_crypt; } /* If a new key was provided, set it up */ if (erq->length > 0) { len = erq->length <= 5 ? 5 : 13; memcpy(sec.keys[key], keybuf, erq->length); if (len > erq->length) memset(sec.keys[key] + erq->length, 0, len - erq->length); LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n", key, print_ssid(ssid, sec.keys[key], len), erq->length, len); sec.key_sizes[key] = len; if (*crypt) (*crypt)->ops->set_key(sec.keys[key], len, NULL, (*crypt)->priv); sec.flags |= (1 << key); /* This ensures a key will be activated if no key is * explicitly set */ if (key == sec.active_key) sec.flags |= SEC_ACTIVE_KEY; } else { if (host_crypto) { len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, NULL, (*crypt)->priv); if (len == 0) { /* Set a default key of all 0 */ LIBIPW_DEBUG_WX("Setting key %d to all " "zero.\n", key); memset(sec.keys[key], 0, 13); (*crypt)->ops->set_key(sec.keys[key], 13, NULL, (*crypt)->priv); sec.key_sizes[key] = 13; sec.flags |= (1 << key); } } /* No key data - just set the default TX key index */ if (key_provided) { LIBIPW_DEBUG_WX("Setting key %d to default Tx " "key.\n", key); ieee->crypt_info.tx_keyidx = key; sec.active_key = key; sec.flags |= SEC_ACTIVE_KEY; } } if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) { ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY; sec.flags |= SEC_AUTH_MODE; LIBIPW_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ? "OPEN" : "SHARED KEY"); } /* For now we just support WEP, so only set that security level... * TODO: When WPA is added this is one place that needs to change */ sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ sec.encode_alg[key] = SEC_ALG_WEP; done: if (ieee->set_security) ieee->set_security(dev, &sec); return 0; } int libipw_wx_get_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *keybuf) { struct iw_point *erq = &(wrqu->encoding); int len, key; struct lib80211_crypt_data *crypt; struct libipw_security *sec = &ieee->sec; LIBIPW_DEBUG_WX("GET_ENCODE\n"); key = erq->flags & IW_ENCODE_INDEX; if (key) { if (key > WEP_KEYS) return -EINVAL; key--; } else key = ieee->crypt_info.tx_keyidx; crypt = ieee->crypt_info.crypt[key]; erq->flags = key + 1; if (!sec->enabled) { erq->length = 0; erq->flags |= IW_ENCODE_DISABLED; return 0; } len = sec->key_sizes[key]; memcpy(keybuf, sec->keys[key], len); erq->length = len; erq->flags |= IW_ENCODE_ENABLED; if (ieee->open_wep) erq->flags |= IW_ENCODE_OPEN; else erq->flags |= IW_ENCODE_RESTRICTED; return 0; } int libipw_wx_set_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct net_device *dev = ieee->dev; struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int i, idx, ret = 0; int group_key = 0; const char *alg, *module; struct lib80211_crypto_ops *ops; struct lib80211_crypt_data **crypt; struct libipw_security sec = { .flags = 0, }; idx = encoding->flags & IW_ENCODE_INDEX; if (idx) { if (idx < 1 || idx > WEP_KEYS) return -EINVAL; idx--; } else idx = ieee->crypt_info.tx_keyidx; if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { crypt = &ieee->crypt_info.crypt[idx]; group_key = 1; } else { /* some Cisco APs use idx>0 for unicast in dynamic WEP */ if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) return -EINVAL; if (ieee->iw_mode == IW_MODE_INFRA) crypt = &ieee->crypt_info.crypt[idx]; else return -EINVAL; } sec.flags |= SEC_ENABLED | SEC_ENCRYPT; if ((encoding->flags & IW_ENCODE_DISABLED) || ext->alg == IW_ENCODE_ALG_NONE) { if (*crypt) lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); for (i = 0; i < WEP_KEYS; i++) if (ieee->crypt_info.crypt[i] != NULL) break; if (i == WEP_KEYS) { sec.enabled = 0; sec.encrypt = 0; sec.level = SEC_LEVEL_0; sec.flags |= SEC_LEVEL; } goto done; } sec.enabled = 1; sec.encrypt = 1; if (group_key ? !ieee->host_mc_decrypt : !(ieee->host_encrypt || ieee->host_decrypt || ieee->host_encrypt_msdu)) goto skip_host_crypt; switch (ext->alg) { case IW_ENCODE_ALG_WEP: alg = "WEP"; module = "lib80211_crypt_wep"; break; case IW_ENCODE_ALG_TKIP: alg = "TKIP"; module = "lib80211_crypt_tkip"; break; case IW_ENCODE_ALG_CCMP: alg = "CCMP"; module = "lib80211_crypt_ccmp"; break; default: LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", dev->name, ext->alg); ret = -EINVAL; goto done; } ops = lib80211_get_crypto_ops(alg); if (ops == NULL) { request_module(module); ops = lib80211_get_crypto_ops(alg); } if (ops == NULL) { LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", dev->name, ext->alg); ret = -EINVAL; goto done; } if (*crypt == NULL || (*crypt)->ops != ops) { struct lib80211_crypt_data *new_crypt; lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); if (new_crypt == NULL) { ret = -ENOMEM; goto done; } new_crypt->ops = ops; if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) new_crypt->priv = new_crypt->ops->init(idx); if (new_crypt->priv == NULL) { kfree(new_crypt); ret = -EINVAL; goto done; } *crypt = new_crypt; } if (ext->key_len > 0 && (*crypt)->ops->set_key && (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, (*crypt)->priv) < 0) { LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name); ret = -EINVAL; goto done; } skip_host_crypt: if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { ieee->crypt_info.tx_keyidx = idx; sec.active_key = idx; sec.flags |= SEC_ACTIVE_KEY; } if (ext->alg != IW_ENCODE_ALG_NONE) { memcpy(sec.keys[idx], ext->key, ext->key_len); sec.key_sizes[idx] = ext->key_len; sec.flags |= (1 << idx); if (ext->alg == IW_ENCODE_ALG_WEP) { sec.encode_alg[idx] = SEC_ALG_WEP; sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_1; } else if (ext->alg == IW_ENCODE_ALG_TKIP) { sec.encode_alg[idx] = SEC_ALG_TKIP; sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_2; } else if (ext->alg == IW_ENCODE_ALG_CCMP) { sec.encode_alg[idx] = SEC_ALG_CCMP; sec.flags |= SEC_LEVEL; sec.level = SEC_LEVEL_3; } /* Don't set sec level for group keys. */ if (group_key) sec.flags &= ~SEC_LEVEL; } done: if (ieee->set_security) ieee->set_security(ieee->dev, &sec); return ret; } int libipw_wx_get_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; struct libipw_security *sec = &ieee->sec; int idx, max_key_len; max_key_len = encoding->length - sizeof(*ext); if (max_key_len < 0) return -EINVAL; idx = encoding->flags & IW_ENCODE_INDEX; if (idx) { if (idx < 1 || idx > WEP_KEYS) return -EINVAL; idx--; } else idx = ieee->crypt_info.tx_keyidx; if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) && ext->alg != IW_ENCODE_ALG_WEP) if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA) return -EINVAL; encoding->flags = idx + 1; memset(ext, 0, sizeof(*ext)); if (!sec->enabled) { ext->alg = IW_ENCODE_ALG_NONE; ext->key_len = 0; encoding->flags |= IW_ENCODE_DISABLED; } else { if (sec->encode_alg[idx] == SEC_ALG_WEP) ext->alg = IW_ENCODE_ALG_WEP; else if (sec->encode_alg[idx] == SEC_ALG_TKIP) ext->alg = IW_ENCODE_ALG_TKIP; else if (sec->encode_alg[idx] == SEC_ALG_CCMP) ext->alg = IW_ENCODE_ALG_CCMP; else return -EINVAL; ext->key_len = sec->key_sizes[idx]; memcpy(ext->key, sec->keys[idx], ext->key_len); encoding->flags |= IW_ENCODE_ENABLED; if (ext->key_len && (ext->alg == IW_ENCODE_ALG_TKIP || ext->alg == IW_ENCODE_ALG_CCMP)) ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; } return 0; } EXPORT_SYMBOL(libipw_wx_set_encodeext); EXPORT_SYMBOL(libipw_wx_get_encodeext); EXPORT_SYMBOL(libipw_wx_get_scan); EXPORT_SYMBOL(libipw_wx_set_encode); EXPORT_SYMBOL(libipw_wx_get_encode); compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/libipw_tx.c0000644000175000017500000003607712026211315024054 0ustar mcgrofmcgrof/****************************************************************************** Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libipw.h" /* 802.11 Data Frame ,-------------------------------------------------------------------. Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | |------|------|---------|---------|---------|------|---------|------| Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs | | | tion | (BSSID) | | | ence | data | | `--------------------------------------------------| |------' Total: 28 non-data bytes `----.----' | .- 'Frame data' expands, if WEP enabled, to <----------' | V ,-----------------------. Bytes | 4 | 0-2296 | 4 | |-----|-----------|-----| Desc. | IV | Encrypted | ICV | | | Packet | | `-----| |-----' `-----.-----' | .- 'Encrypted Packet' expands to | V ,---------------------------------------------------. Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 | |------|------|---------|----------|------|---------| Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | | DSAP | SSAP | | | | Packet | | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | `---------------------------------------------------- Total: 8 non-data bytes 802.3 Ethernet Data Frame ,-----------------------------------------. Bytes | 6 | 6 | 2 | Variable | 4 | |-------|-------|------|-----------|------| Desc. | Dest. | Source| Type | IP Packet | fcs | | MAC | MAC | | | | `-----------------------------------------' Total: 18 non-data bytes In the event that fragmentation is required, the incoming payload is split into N parts of size ieee->fts. The first fragment contains the SNAP header and the remaining packets are just data. If encryption is enabled, each fragment payload size is reduced by enough space to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP) So if you have 1500 bytes of payload with ieee->fts set to 500 without encryption it will take 3 frames. With WEP it will take 4 frames as the payload of each frame is reduced to 492 bytes. * SKB visualization * * ,- skb->data * | * | ETHERNET HEADER ,-<-- PAYLOAD * | | 14 bytes from skb->data * | 2 bytes for Type --> ,T. | (sizeof ethhdr) * | | | | * |,-Dest.--. ,--Src.---. | | | * | 6 bytes| | 6 bytes | | | | * v | | | | | | * 0 | v 1 | v | v 2 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 * ^ | ^ | ^ | * | | | | | | * | | | | `T' <---- 2 bytes for Type * | | | | * | | '---SNAP--' <-------- 6 bytes for SNAP * | | * `-IV--' <-------------------- 4 bytes for IV (WEP) * * SNAP HEADER * */ static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; static int libipw_copy_snap(u8 * data, __be16 h_proto) { struct libipw_snap_hdr *snap; u8 *oui; snap = (struct libipw_snap_hdr *)data; snap->dsap = 0xaa; snap->ssap = 0xaa; snap->ctrl = 0x03; if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX)) oui = P802_1H_OUI; else oui = RFC1042_OUI; snap->oui[0] = oui[0]; snap->oui[1] = oui[1]; snap->oui[2] = oui[2]; memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16)); return SNAP_SIZE + sizeof(u16); } static int libipw_encrypt_fragment(struct libipw_device *ieee, struct sk_buff *frag, int hdr_len) { struct lib80211_crypt_data *crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; int res; if (crypt == NULL) return -1; /* To encrypt, frame format is: * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ atomic_inc(&crypt->refcnt); res = 0; if (crypt->ops && crypt->ops->encrypt_mpdu) res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); atomic_dec(&crypt->refcnt); if (res < 0) { printk(KERN_INFO "%s: Encryption failed: len=%d.\n", ieee->dev->name, frag->len); ieee->ieee_stats.tx_discards++; return -1; } return 0; } void libipw_txb_free(struct libipw_txb *txb) { int i; if (unlikely(!txb)) return; for (i = 0; i < txb->nr_frags; i++) if (txb->fragments[i]) dev_kfree_skb_any(txb->fragments[i]); kfree(txb); } static struct libipw_txb *libipw_alloc_txb(int nr_frags, int txb_size, int headroom, gfp_t gfp_mask) { struct libipw_txb *txb; int i; txb = kmalloc(sizeof(struct libipw_txb) + (sizeof(u8 *) * nr_frags), gfp_mask); if (!txb) return NULL; memset(txb, 0, sizeof(struct libipw_txb)); txb->nr_frags = nr_frags; txb->frag_size = txb_size; for (i = 0; i < nr_frags; i++) { txb->fragments[i] = __dev_alloc_skb(txb_size + headroom, gfp_mask); if (unlikely(!txb->fragments[i])) { i--; break; } skb_reserve(txb->fragments[i], headroom); } if (unlikely(i != nr_frags)) { while (i >= 0) dev_kfree_skb_any(txb->fragments[i--]); kfree(txb); return NULL; } return txb; } static int libipw_classify(struct sk_buff *skb) { struct ethhdr *eth; struct iphdr *ip; eth = (struct ethhdr *)skb->data; if (eth->h_proto != htons(ETH_P_IP)) return 0; ip = ip_hdr(skb); switch (ip->tos & 0xfc) { case 0x20: return 2; case 0x40: return 1; case 0x60: return 3; case 0x80: return 4; case 0xa0: return 5; case 0xc0: return 6; case 0xe0: return 7; default: return 0; } } /* Incoming skb is converted to a txb which consists of * a block of 802.11 fragment packets (stored as skbs) */ netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev) { struct libipw_device *ieee = netdev_priv(dev); struct libipw_txb *txb = NULL; struct libipw_hdr_3addrqos *frag_hdr; int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size, rts_required; unsigned long flags; int encrypt, host_encrypt, host_encrypt_msdu; __be16 ether_type; int bytes, fc, hdr_len; struct sk_buff *skb_frag; struct libipw_hdr_3addrqos header = {/* Ensure zero initialized */ .duration_id = 0, .seq_ctl = 0, .qos_ctl = 0 }; u8 dest[ETH_ALEN], src[ETH_ALEN]; struct lib80211_crypt_data *crypt; int priority = skb->priority; int snapped = 0; if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority)) return NETDEV_TX_BUSY; spin_lock_irqsave(&ieee->lock, flags); /* If there is no driver handler to take the TXB, dont' bother * creating it... */ if (!ieee->hard_start_xmit) { printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name); goto success; } if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { printk(KERN_WARNING "%s: skb too small (%d).\n", ieee->dev->name, skb->len); goto success; } ether_type = ((struct ethhdr *)skb->data)->h_proto; crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) && ieee->sec.encrypt; host_encrypt = ieee->host_encrypt && encrypt && crypt; host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt; if (!encrypt && ieee->ieee802_1x && ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) { dev->stats.tx_dropped++; goto success; } /* Save source and destination addresses */ skb_copy_from_linear_data(skb, dest, ETH_ALEN); skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN); if (host_encrypt) fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | IEEE80211_FCTL_PROTECTED; else fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; if (ieee->iw_mode == IW_MODE_INFRA) { fc |= IEEE80211_FCTL_TODS; /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ memcpy(header.addr1, ieee->bssid, ETH_ALEN); memcpy(header.addr2, src, ETH_ALEN); memcpy(header.addr3, dest, ETH_ALEN); } else if (ieee->iw_mode == IW_MODE_ADHOC) { /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ memcpy(header.addr1, dest, ETH_ALEN); memcpy(header.addr2, src, ETH_ALEN); memcpy(header.addr3, ieee->bssid, ETH_ALEN); } hdr_len = LIBIPW_3ADDR_LEN; if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) { fc |= IEEE80211_STYPE_QOS_DATA; hdr_len += 2; skb->priority = libipw_classify(skb); header.qos_ctl |= cpu_to_le16(skb->priority & LIBIPW_QCTL_TID); } header.frame_ctl = cpu_to_le16(fc); /* Advance the SKB to the start of the payload */ skb_pull(skb, sizeof(struct ethhdr)); /* Determine total amount of storage required for TXB packets */ bytes = skb->len + SNAP_SIZE + sizeof(u16); /* Encrypt msdu first on the whole data packet. */ if ((host_encrypt || host_encrypt_msdu) && crypt && crypt->ops && crypt->ops->encrypt_msdu) { int res = 0; int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len + crypt->ops->extra_msdu_postfix_len; struct sk_buff *skb_new = dev_alloc_skb(len); if (unlikely(!skb_new)) goto failed; skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len); memcpy(skb_put(skb_new, hdr_len), &header, hdr_len); snapped = 1; libipw_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)), ether_type); skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len); res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv); if (res < 0) { LIBIPW_ERROR("msdu encryption failed\n"); dev_kfree_skb_any(skb_new); goto failed; } dev_kfree_skb_any(skb); skb = skb_new; bytes += crypt->ops->extra_msdu_prefix_len + crypt->ops->extra_msdu_postfix_len; skb_pull(skb, hdr_len); } if (host_encrypt || ieee->host_open_frag) { /* Determine fragmentation size based on destination (multicast * and broadcast are not fragmented) */ if (is_multicast_ether_addr(dest) || is_broadcast_ether_addr(dest)) frag_size = MAX_FRAG_THRESHOLD; else frag_size = ieee->fts; /* Determine amount of payload per fragment. Regardless of if * this stack is providing the full 802.11 header, one will * eventually be affixed to this fragment -- so we must account * for it when determining the amount of payload space. */ bytes_per_frag = frag_size - hdr_len; if (ieee->config & (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) bytes_per_frag -= LIBIPW_FCS_LEN; /* Each fragment may need to have room for encryption * pre/postfix */ if (host_encrypt) bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len + crypt->ops->extra_mpdu_postfix_len; /* Number of fragments is the total * bytes_per_frag / payload_per_fragment */ nr_frags = bytes / bytes_per_frag; bytes_last_frag = bytes % bytes_per_frag; if (bytes_last_frag) nr_frags++; else bytes_last_frag = bytes_per_frag; } else { nr_frags = 1; bytes_per_frag = bytes_last_frag = bytes; frag_size = bytes + hdr_len; } rts_required = (frag_size > ieee->rts && ieee->config & CFG_LIBIPW_RTS); if (rts_required) nr_frags++; /* When we allocate the TXB we allocate enough space for the reserve * and full fragment bytes (bytes_per_frag doesn't include prefix, * postfix, header, FCS, etc.) */ txb = libipw_alloc_txb(nr_frags, frag_size, ieee->tx_headroom, GFP_ATOMIC); if (unlikely(!txb)) { printk(KERN_WARNING "%s: Could not allocate TXB\n", ieee->dev->name); goto failed; } txb->encrypted = encrypt; if (host_encrypt) txb->payload_size = frag_size * (nr_frags - 1) + bytes_last_frag; else txb->payload_size = bytes; if (rts_required) { skb_frag = txb->fragments[0]; frag_hdr = (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); /* * Set header frame_ctl to the RTS. */ header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); memcpy(frag_hdr, &header, hdr_len); /* * Restore header frame_ctl to the original data setting. */ header.frame_ctl = cpu_to_le16(fc); if (ieee->config & (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) skb_put(skb_frag, 4); txb->rts_included = 1; i = 1; } else i = 0; for (; i < nr_frags; i++) { skb_frag = txb->fragments[i]; if (host_encrypt) skb_reserve(skb_frag, crypt->ops->extra_mpdu_prefix_len); frag_hdr = (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); memcpy(frag_hdr, &header, hdr_len); /* If this is not the last fragment, then add the MOREFRAGS * bit to the frame control */ if (i != nr_frags - 1) { frag_hdr->frame_ctl = cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS); bytes = bytes_per_frag; } else { /* The last fragment takes the remaining length */ bytes = bytes_last_frag; } if (i == 0 && !snapped) { libipw_copy_snap(skb_put (skb_frag, SNAP_SIZE + sizeof(u16)), ether_type); bytes -= SNAP_SIZE + sizeof(u16); } skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes); /* Advance the SKB... */ skb_pull(skb, bytes); /* Encryption routine will move the header forward in order * to insert the IV between the header and the payload */ if (host_encrypt) libipw_encrypt_fragment(ieee, skb_frag, hdr_len); if (ieee->config & (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) skb_put(skb_frag, 4); } success: spin_unlock_irqrestore(&ieee->lock, flags); dev_kfree_skb_any(skb); if (txb) { netdev_tx_t ret = (*ieee->hard_start_xmit)(txb, dev, priority); if (ret == NETDEV_TX_OK) { dev->stats.tx_packets++; dev->stats.tx_bytes += txb->payload_size; return NETDEV_TX_OK; } libipw_txb_free(txb); } return NETDEV_TX_OK; failed: spin_unlock_irqrestore(&ieee->lock, flags); netif_stop_queue(dev); dev->stats.tx_errors++; return NETDEV_TX_BUSY; } EXPORT_SYMBOL(libipw_xmit); EXPORT_SYMBOL(libipw_txb_free); compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/libipw_rx.c0000644000175000017500000014460012026211315024042 0ustar mcgrofmcgrof/* * Original code based Host AP (software wireless LAN access point) driver * for Intersil Prism2/2.5/3 - hostap.o module, common routines * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen * * Copyright (c) 2002-2003, Jouni Malinen * Copyright (c) 2004-2005, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libipw.h" static void libipw_monitor_rx(struct libipw_device *ieee, struct sk_buff *skb, struct libipw_rx_stats *rx_stats) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; u16 fc = le16_to_cpu(hdr->frame_control); skb->dev = ieee->dev; skb_reset_mac_header(skb); skb_pull(skb, libipw_get_hdrlen(fc)); skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_80211_RAW); memset(skb->cb, 0, sizeof(skb->cb)); netif_rx(skb); } /* Called only as a tasklet (software IRQ) */ static struct libipw_frag_entry *libipw_frag_cache_find(struct libipw_device *ieee, unsigned int seq, unsigned int frag, u8 * src, u8 * dst) { struct libipw_frag_entry *entry; int i; for (i = 0; i < LIBIPW_FRAG_CACHE_LEN; i++) { entry = &ieee->frag_cache[i]; if (entry->skb != NULL && time_after(jiffies, entry->first_frag_time + 2 * HZ)) { LIBIPW_DEBUG_FRAG("expiring fragment cache entry " "seq=%u last_frag=%u\n", entry->seq, entry->last_frag); dev_kfree_skb_any(entry->skb); entry->skb = NULL; } if (entry->skb != NULL && entry->seq == seq && (entry->last_frag + 1 == frag || frag == -1) && ether_addr_equal(entry->src_addr, src) && ether_addr_equal(entry->dst_addr, dst)) return entry; } return NULL; } /* Called only as a tasklet (software IRQ) */ static struct sk_buff *libipw_frag_cache_get(struct libipw_device *ieee, struct libipw_hdr_4addr *hdr) { struct sk_buff *skb = NULL; u16 sc; unsigned int frag, seq; struct libipw_frag_entry *entry; sc = le16_to_cpu(hdr->seq_ctl); frag = WLAN_GET_SEQ_FRAG(sc); seq = WLAN_GET_SEQ_SEQ(sc); if (frag == 0) { /* Reserve enough space to fit maximum frame length */ skb = dev_alloc_skb(ieee->dev->mtu + sizeof(struct libipw_hdr_4addr) + 8 /* LLC */ + 2 /* alignment */ + 8 /* WEP */ + ETH_ALEN /* WDS */ ); if (skb == NULL) return NULL; entry = &ieee->frag_cache[ieee->frag_next_idx]; ieee->frag_next_idx++; if (ieee->frag_next_idx >= LIBIPW_FRAG_CACHE_LEN) ieee->frag_next_idx = 0; if (entry->skb != NULL) dev_kfree_skb_any(entry->skb); entry->first_frag_time = jiffies; entry->seq = seq; entry->last_frag = frag; entry->skb = skb; memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); } else { /* received a fragment of a frame for which the head fragment * should have already been received */ entry = libipw_frag_cache_find(ieee, seq, frag, hdr->addr2, hdr->addr1); if (entry != NULL) { entry->last_frag = frag; skb = entry->skb; } } return skb; } /* Called only as a tasklet (software IRQ) */ static int libipw_frag_cache_invalidate(struct libipw_device *ieee, struct libipw_hdr_4addr *hdr) { u16 sc; unsigned int seq; struct libipw_frag_entry *entry; sc = le16_to_cpu(hdr->seq_ctl); seq = WLAN_GET_SEQ_SEQ(sc); entry = libipw_frag_cache_find(ieee, seq, -1, hdr->addr2, hdr->addr1); if (entry == NULL) { LIBIPW_DEBUG_FRAG("could not invalidate fragment cache " "entry (seq=%u)\n", seq); return -1; } entry->skb = NULL; return 0; } #ifdef NOT_YET /* libipw_rx_frame_mgtmt * * Responsible for handling management control frames * * Called by libipw_rx */ static int libipw_rx_frame_mgmt(struct libipw_device *ieee, struct sk_buff *skb, struct libipw_rx_stats *rx_stats, u16 type, u16 stype) { if (ieee->iw_mode == IW_MODE_MASTER) { printk(KERN_DEBUG "%s: Master mode not yet supported.\n", ieee->dev->name); return 0; /* hostap_update_sta_ps(ieee, (struct hostap_libipw_hdr_4addr *) skb->data);*/ } if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) { if (stype == WLAN_FC_STYPE_BEACON && ieee->iw_mode == IW_MODE_MASTER) { struct sk_buff *skb2; /* Process beacon frames also in kernel driver to * update STA(AP) table statistics */ skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) hostap_rx(skb2->dev, skb2, rx_stats); } /* send management frames to the user space daemon for * processing */ ieee->apdevstats.rx_packets++; ieee->apdevstats.rx_bytes += skb->len; prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); return 0; } if (ieee->iw_mode == IW_MODE_MASTER) { if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { printk(KERN_DEBUG "%s: unknown management frame " "(type=0x%02x, stype=0x%02x) dropped\n", skb->dev->name, type, stype); return -1; } hostap_rx(skb->dev, skb, rx_stats); return 0; } printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame " "received in non-Host AP mode\n", skb->dev->name); return -1; } #endif /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ static unsigned char libipw_rfc1042_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ static unsigned char libipw_bridge_tunnel_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; /* No encapsulation header if EtherType < 0x600 (=length) */ /* Called by libipw_rx_frame_decrypt */ static int libipw_is_eapol_frame(struct libipw_device *ieee, struct sk_buff *skb) { struct net_device *dev = ieee->dev; u16 fc, ethertype; struct libipw_hdr_3addr *hdr; u8 *pos; if (skb->len < 24) return 0; hdr = (struct libipw_hdr_3addr *)skb->data; fc = le16_to_cpu(hdr->frame_ctl); /* check that the frame is unicast frame to us */ if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_TODS && ether_addr_equal(hdr->addr1, dev->dev_addr) && ether_addr_equal(hdr->addr3, dev->dev_addr)) { /* ToDS frame with own addr BSSID and DA */ } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS && ether_addr_equal(hdr->addr1, dev->dev_addr)) { /* FromDS frame with own addr as DA */ } else return 0; if (skb->len < 24 + 8) return 0; /* check for port access entity Ethernet type */ pos = skb->data + 24; ethertype = (pos[6] << 8) | pos[7]; if (ethertype == ETH_P_PAE) return 1; return 0; } /* Called only as a tasklet (software IRQ), by libipw_rx */ static int libipw_rx_frame_decrypt(struct libipw_device *ieee, struct sk_buff *skb, struct lib80211_crypt_data *crypt) { struct libipw_hdr_3addr *hdr; int res, hdrlen; if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) return 0; hdr = (struct libipw_hdr_3addr *)skb->data; hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); atomic_inc(&crypt->refcnt); res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); atomic_dec(&crypt->refcnt); if (res < 0) { LIBIPW_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n", hdr->addr2, res); if (res == -2) LIBIPW_DEBUG_DROP("Decryption failed ICV " "mismatch (key %d)\n", skb->data[hdrlen + 3] >> 6); ieee->ieee_stats.rx_discards_undecryptable++; return -1; } return res; } /* Called only as a tasklet (software IRQ), by libipw_rx */ static int libipw_rx_frame_decrypt_msdu(struct libipw_device *ieee, struct sk_buff *skb, int keyidx, struct lib80211_crypt_data *crypt) { struct libipw_hdr_3addr *hdr; int res, hdrlen; if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) return 0; hdr = (struct libipw_hdr_3addr *)skb->data; hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); atomic_inc(&crypt->refcnt); res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); atomic_dec(&crypt->refcnt); if (res < 0) { printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" " (SA=%pM keyidx=%d)\n", ieee->dev->name, hdr->addr2, keyidx); return -1; } return 0; } /* All received frames are sent to this function. @skb contains the frame in * IEEE 802.11 format, i.e., in the format it was sent over air. * This function is called only as a tasklet (software IRQ). */ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, struct libipw_rx_stats *rx_stats) { struct net_device *dev = ieee->dev; struct libipw_hdr_4addr *hdr; size_t hdrlen; u16 fc, type, stype, sc; unsigned int frag; u8 *payload; u16 ethertype; #ifdef NOT_YET struct net_device *wds = NULL; struct sk_buff *skb2 = NULL; struct net_device *wds = NULL; int frame_authorized = 0; int from_assoc_ap = 0; void *sta = NULL; #endif u8 dst[ETH_ALEN]; u8 src[ETH_ALEN]; struct lib80211_crypt_data *crypt = NULL; int keyidx = 0; int can_be_decrypted = 0; hdr = (struct libipw_hdr_4addr *)skb->data; if (skb->len < 10) { printk(KERN_INFO "%s: SKB length < 10\n", dev->name); goto rx_dropped; } fc = le16_to_cpu(hdr->frame_ctl); type = WLAN_FC_GET_TYPE(fc); stype = WLAN_FC_GET_STYPE(fc); sc = le16_to_cpu(hdr->seq_ctl); frag = WLAN_GET_SEQ_FRAG(sc); hdrlen = libipw_get_hdrlen(fc); if (skb->len < hdrlen) { printk(KERN_INFO "%s: invalid SKB length %d\n", dev->name, skb->len); goto rx_dropped; } /* Put this code here so that we avoid duplicating it in all * Rx paths. - Jean II */ #ifdef CONFIG_WIRELESS_EXT #ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ /* If spy monitoring on */ if (ieee->spy_data.spy_number > 0) { struct iw_quality wstats; wstats.updated = 0; if (rx_stats->mask & LIBIPW_STATMASK_RSSI) { wstats.level = rx_stats->signal; wstats.updated |= IW_QUAL_LEVEL_UPDATED; } else wstats.updated |= IW_QUAL_LEVEL_INVALID; if (rx_stats->mask & LIBIPW_STATMASK_NOISE) { wstats.noise = rx_stats->noise; wstats.updated |= IW_QUAL_NOISE_UPDATED; } else wstats.updated |= IW_QUAL_NOISE_INVALID; if (rx_stats->mask & LIBIPW_STATMASK_SIGNAL) { wstats.qual = rx_stats->signal; wstats.updated |= IW_QUAL_QUAL_UPDATED; } else wstats.updated |= IW_QUAL_QUAL_INVALID; /* Update spy records */ wireless_spy_update(ieee->dev, hdr->addr2, &wstats); } #endif /* IW_WIRELESS_SPY */ #endif /* CONFIG_WIRELESS_EXT */ #ifdef NOT_YET hostap_update_rx_stats(local->ap, hdr, rx_stats); #endif if (ieee->iw_mode == IW_MODE_MONITOR) { dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; libipw_monitor_rx(ieee, skb, rx_stats); return 1; } can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) || is_broadcast_ether_addr(hdr->addr2)) ? ieee->host_mc_decrypt : ieee->host_decrypt; if (can_be_decrypted) { if (skb->len >= hdrlen + 3) { /* Top two-bits of byte 3 are the key index */ keyidx = skb->data[hdrlen + 3] >> 6; } /* ieee->crypt[] is WEP_KEY (4) in length. Given that keyidx * is only allowed 2-bits of storage, no value of keyidx can * be provided via above code that would result in keyidx * being out of range */ crypt = ieee->crypt_info.crypt[keyidx]; #ifdef NOT_YET sta = NULL; /* Use station specific key to override default keys if the * receiver address is a unicast address ("individual RA"). If * bcrx_sta_key parameter is set, station specific key is used * even with broad/multicast targets (this is against IEEE * 802.11, but makes it easier to use different keys with * stations that do not support WEP key mapping). */ if (is_unicast_ether_addr(hdr->addr1) || local->bcrx_sta_key) (void)hostap_handle_sta_crypto(local, hdr, &crypt, &sta); #endif /* allow NULL decrypt to indicate an station specific override * for default encryption */ if (crypt && (crypt->ops == NULL || crypt->ops->decrypt_mpdu == NULL)) crypt = NULL; if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { /* This seems to be triggered by some (multicast?) * frames from other than current BSS, so just drop the * frames silently instead of filling system log with * these reports. */ LIBIPW_DEBUG_DROP("Decryption failed (not set)" " (SA=%pM)\n", hdr->addr2); ieee->ieee_stats.rx_discards_undecryptable++; goto rx_dropped; } } #ifdef NOT_YET if (type != WLAN_FC_TYPE_DATA) { if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt && (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) { printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " "from %pM\n", dev->name, hdr->addr2); /* TODO: could inform hostapd about this so that it * could send auth failure report */ goto rx_dropped; } if (libipw_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) goto rx_dropped; else goto rx_exit; } #endif /* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */ if (sc == ieee->prev_seq_ctl) goto rx_dropped; else ieee->prev_seq_ctl = sc; /* Data frame - extract src/dst addresses */ if (skb->len < LIBIPW_3ADDR_LEN) goto rx_dropped; switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { case IEEE80211_FCTL_FROMDS: memcpy(dst, hdr->addr1, ETH_ALEN); memcpy(src, hdr->addr3, ETH_ALEN); break; case IEEE80211_FCTL_TODS: memcpy(dst, hdr->addr3, ETH_ALEN); memcpy(src, hdr->addr2, ETH_ALEN); break; case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: if (skb->len < LIBIPW_4ADDR_LEN) goto rx_dropped; memcpy(dst, hdr->addr3, ETH_ALEN); memcpy(src, hdr->addr4, ETH_ALEN); break; case 0: memcpy(dst, hdr->addr1, ETH_ALEN); memcpy(src, hdr->addr2, ETH_ALEN); break; } #ifdef NOT_YET if (hostap_rx_frame_wds(ieee, hdr, fc, &wds)) goto rx_dropped; if (wds) { skb->dev = dev = wds; stats = hostap_get_stats(dev); } if (ieee->iw_mode == IW_MODE_MASTER && !wds && (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS && ieee->stadev && ether_addr_equal(hdr->addr2, ieee->assoc_ap_addr)) { /* Frame from BSSID of the AP for which we are a client */ skb->dev = dev = ieee->stadev; stats = hostap_get_stats(dev); from_assoc_ap = 1; } #endif #ifdef NOT_YET if ((ieee->iw_mode == IW_MODE_MASTER || ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) { switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats, wds != NULL)) { case AP_RX_CONTINUE_NOT_AUTHORIZED: frame_authorized = 0; break; case AP_RX_CONTINUE: frame_authorized = 1; break; case AP_RX_DROP: goto rx_dropped; case AP_RX_EXIT: goto rx_exit; } } #endif /* Nullfunc frames may have PS-bit set, so they must be passed to * hostap_handle_sta_rx() before being dropped here. */ stype &= ~IEEE80211_STYPE_QOS_DATA; if (stype != IEEE80211_STYPE_DATA && stype != IEEE80211_STYPE_DATA_CFACK && stype != IEEE80211_STYPE_DATA_CFPOLL && stype != IEEE80211_STYPE_DATA_CFACKPOLL) { if (stype != IEEE80211_STYPE_NULLFUNC) LIBIPW_DEBUG_DROP("RX: dropped data frame " "with no data (type=0x%02x, " "subtype=0x%02x, len=%d)\n", type, stype, skb->len); goto rx_dropped; } /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && (keyidx = libipw_rx_frame_decrypt(ieee, skb, crypt)) < 0) goto rx_dropped; hdr = (struct libipw_hdr_4addr *)skb->data; /* skb: hdr + (possibly fragmented) plaintext payload */ // PR: FIXME: hostap has additional conditions in the "if" below: // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) { int flen; struct sk_buff *frag_skb = libipw_frag_cache_get(ieee, hdr); LIBIPW_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); if (!frag_skb) { LIBIPW_DEBUG(LIBIPW_DL_RX | LIBIPW_DL_FRAG, "Rx cannot get skb from fragment " "cache (morefrag=%d seq=%u frag=%u)\n", (fc & IEEE80211_FCTL_MOREFRAGS) != 0, WLAN_GET_SEQ_SEQ(sc), frag); goto rx_dropped; } flen = skb->len; if (frag != 0) flen -= hdrlen; if (frag_skb->tail + flen > frag_skb->end) { printk(KERN_WARNING "%s: host decrypted and " "reassembled frame did not fit skb\n", dev->name); libipw_frag_cache_invalidate(ieee, hdr); goto rx_dropped; } if (frag == 0) { /* copy first fragment (including full headers) into * beginning of the fragment cache skb */ skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen); } else { /* append frame payload to the end of the fragment * cache skb */ skb_copy_from_linear_data_offset(skb, hdrlen, skb_put(frag_skb, flen), flen); } dev_kfree_skb_any(skb); skb = NULL; if (fc & IEEE80211_FCTL_MOREFRAGS) { /* more fragments expected - leave the skb in fragment * cache for now; it will be delivered to upper layers * after all fragments have been received */ goto rx_exit; } /* this was the last fragment and the frame will be * delivered, so remove skb from fragment cache */ skb = frag_skb; hdr = (struct libipw_hdr_4addr *)skb->data; libipw_frag_cache_invalidate(ieee, hdr); } /* skb: hdr + (possible reassembled) full MSDU payload; possibly still * encrypted/authenticated */ if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && libipw_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) goto rx_dropped; hdr = (struct libipw_hdr_4addr *)skb->data; if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) { if ( /*ieee->ieee802_1x && */ libipw_is_eapol_frame(ieee, skb)) { /* pass unencrypted EAPOL frames even if encryption is * configured */ } else { LIBIPW_DEBUG_DROP("encryption configured, but RX " "frame not encrypted (SA=%pM)\n", hdr->addr2); goto rx_dropped; } } if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep && !libipw_is_eapol_frame(ieee, skb)) { LIBIPW_DEBUG_DROP("dropped unencrypted RX data " "frame from %pM (drop_unencrypted=1)\n", hdr->addr2); goto rx_dropped; } /* If the frame was decrypted in hardware, we may need to strip off * any security data (IV, ICV, etc) that was left behind */ if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) && ieee->host_strip_iv_icv) { int trimlen = 0; /* Top two-bits of byte 3 are the key index */ if (skb->len >= hdrlen + 3) keyidx = skb->data[hdrlen + 3] >> 6; /* To strip off any security data which appears before the * payload, we simply increase hdrlen (as the header gets * chopped off immediately below). For the security data which * appears after the payload, we use skb_trim. */ switch (ieee->sec.encode_alg[keyidx]) { case SEC_ALG_WEP: /* 4 byte IV */ hdrlen += 4; /* 4 byte ICV */ trimlen = 4; break; case SEC_ALG_TKIP: /* 4 byte IV, 4 byte ExtIV */ hdrlen += 8; /* 8 byte MIC, 4 byte ICV */ trimlen = 12; break; case SEC_ALG_CCMP: /* 8 byte CCMP header */ hdrlen += 8; /* 8 byte MIC */ trimlen = 8; break; } if (skb->len < trimlen) goto rx_dropped; __skb_trim(skb, skb->len - trimlen); if (skb->len < hdrlen) goto rx_dropped; } /* skb: hdr + (possible reassembled) full plaintext payload */ payload = skb->data + hdrlen; ethertype = (payload[6] << 8) | payload[7]; #ifdef NOT_YET /* If IEEE 802.1X is used, check whether the port is authorized to send * the received frame. */ if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) { if (ethertype == ETH_P_PAE) { printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n", dev->name); if (ieee->hostapd && ieee->apdev) { /* Send IEEE 802.1X frames to the user * space daemon for processing */ prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); ieee->apdevstats.rx_packets++; ieee->apdevstats.rx_bytes += skb->len; goto rx_exit; } } else if (!frame_authorized) { printk(KERN_DEBUG "%s: dropped frame from " "unauthorized port (IEEE 802.1X): " "ethertype=0x%04x\n", dev->name, ethertype); goto rx_dropped; } } #endif /* convert hdr + possible LLC headers into Ethernet header */ if (skb->len - hdrlen >= 8 && ((memcmp(payload, libipw_rfc1042_header, SNAP_SIZE) == 0 && ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || memcmp(payload, libipw_bridge_tunnel_header, SNAP_SIZE) == 0)) { /* remove RFC1042 or Bridge-Tunnel encapsulation and * replace EtherType */ skb_pull(skb, hdrlen + SNAP_SIZE); memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); } else { __be16 len; /* Leave Ethernet header part of hdr and full payload */ skb_pull(skb, hdrlen); len = htons(skb->len); memcpy(skb_push(skb, 2), &len, 2); memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); } #ifdef NOT_YET if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) { /* Non-standard frame: get addr4 from its bogus location after * the payload */ skb_copy_to_linear_data_offset(skb, ETH_ALEN, skb->data + skb->len - ETH_ALEN, ETH_ALEN); skb_trim(skb, skb->len - ETH_ALEN); } #endif dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; #ifdef NOT_YET if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) { if (is_multicast_ether_addr(dst)) { /* copy multicast frame both to the higher layers and * to the wireless media */ ieee->ap->bridged_multicast++; skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2 == NULL) printk(KERN_DEBUG "%s: skb_clone failed for " "multicast frame\n", dev->name); } else if (hostap_is_sta_assoc(ieee->ap, dst)) { /* send frame directly to the associated STA using * wireless media and not passing to higher layers */ ieee->ap->bridged_unicast++; skb2 = skb; skb = NULL; } } if (skb2 != NULL) { /* send to wireless media */ skb2->dev = dev; skb2->protocol = htons(ETH_P_802_3); skb_reset_mac_header(skb2); skb_reset_network_header(skb2); /* skb2->network_header += ETH_HLEN; */ dev_queue_xmit(skb2); } #endif if (skb) { skb->protocol = eth_type_trans(skb, dev); memset(skb->cb, 0, sizeof(skb->cb)); skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ if (netif_rx(skb) == NET_RX_DROP) { /* netif_rx always succeeds, but it might drop * the packet. If it drops the packet, we log that * in our stats. */ LIBIPW_DEBUG_DROP ("RX: netif_rx dropped the packet\n"); dev->stats.rx_dropped++; } } rx_exit: #ifdef NOT_YET if (sta) hostap_handle_sta_release(sta); #endif return 1; rx_dropped: dev->stats.rx_dropped++; /* Returning 0 indicates to caller that we have not handled the SKB-- * so it is still allocated and can be used again by underlying * hardware as a DMA target */ return 0; } /* Filter out unrelated packets, call libipw_rx[_mgt] * This function takes over the skb, it should not be used again after calling * this function. */ void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb, struct libipw_rx_stats *stats) { struct libipw_hdr_4addr *hdr; int is_packet_for_us; u16 fc; if (ieee->iw_mode == IW_MODE_MONITOR) { if (!libipw_rx(ieee, skb, stats)) dev_kfree_skb_irq(skb); return; } if (skb->len < sizeof(struct ieee80211_hdr)) goto drop_free; hdr = (struct libipw_hdr_4addr *)skb->data; fc = le16_to_cpu(hdr->frame_ctl); if ((fc & IEEE80211_FCTL_VERS) != 0) goto drop_free; switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_MGMT: if (skb->len < sizeof(struct libipw_hdr_3addr)) goto drop_free; libipw_rx_mgt(ieee, hdr, stats); dev_kfree_skb_irq(skb); return; case IEEE80211_FTYPE_DATA: break; case IEEE80211_FTYPE_CTL: return; default: return; } is_packet_for_us = 0; switch (ieee->iw_mode) { case IW_MODE_ADHOC: /* our BSS and not from/to DS */ if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0) if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) { /* promisc: get all */ if (ieee->dev->flags & IFF_PROMISC) is_packet_for_us = 1; /* to us */ else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0) is_packet_for_us = 1; /* mcast */ else if (is_multicast_ether_addr(hdr->addr1)) is_packet_for_us = 1; } break; case IW_MODE_INFRA: /* our BSS (== from our AP) and from DS */ if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0) if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) { /* promisc: get all */ if (ieee->dev->flags & IFF_PROMISC) is_packet_for_us = 1; /* to us */ else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0) is_packet_for_us = 1; /* mcast */ else if (is_multicast_ether_addr(hdr->addr1)) { /* not our own packet bcasted from AP */ if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN)) is_packet_for_us = 1; } } break; default: /* ? */ break; } if (is_packet_for_us) if (!libipw_rx(ieee, skb, stats)) dev_kfree_skb_irq(skb); return; drop_free: dev_kfree_skb_irq(skb); ieee->dev->stats.rx_dropped++; } #define MGMT_FRAME_FIXED_PART_LENGTH 0x24 static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; /* * Make the structure we read from the beacon packet to have * the right values */ static int libipw_verify_qos_info(struct libipw_qos_information_element *info_element, int sub_type) { if (info_element->qui_subtype != sub_type) return -1; if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN)) return -1; if (info_element->qui_type != QOS_OUI_TYPE) return -1; if (info_element->version != QOS_VERSION_1) return -1; return 0; } /* * Parse a QoS parameter element */ static int libipw_read_qos_param_element(struct libipw_qos_parameter_info *element_param, struct libipw_info_element *info_element) { int ret = 0; u16 size = sizeof(struct libipw_qos_parameter_info) - 2; if ((info_element == NULL) || (element_param == NULL)) return -1; if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) { memcpy(element_param->info_element.qui, info_element->data, info_element->len); element_param->info_element.elementID = info_element->id; element_param->info_element.length = info_element->len; } else ret = -1; if (ret == 0) ret = libipw_verify_qos_info(&element_param->info_element, QOS_OUI_PARAM_SUB_TYPE); return ret; } /* * Parse a QoS information element */ static int libipw_read_qos_info_element(struct libipw_qos_information_element *element_info, struct libipw_info_element *info_element) { int ret = 0; u16 size = sizeof(struct libipw_qos_information_element) - 2; if (element_info == NULL) return -1; if (info_element == NULL) return -1; if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) { memcpy(element_info->qui, info_element->data, info_element->len); element_info->elementID = info_element->id; element_info->length = info_element->len; } else ret = -1; if (ret == 0) ret = libipw_verify_qos_info(element_info, QOS_OUI_INFO_SUB_TYPE); return ret; } /* * Write QoS parameters from the ac parameters. */ static int libipw_qos_convert_ac_to_parameters(struct libipw_qos_parameter_info *param_elm, struct libipw_qos_parameters *qos_param) { int rc = 0; int i; struct libipw_qos_ac_parameter *ac_params; u32 txop; u8 cw_min; u8 cw_max; for (i = 0; i < QOS_QUEUE_NUM; i++) { ac_params = &(param_elm->ac_params_record[i]); qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F; qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2; cw_min = ac_params->ecw_min_max & 0x0F; qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1); cw_max = (ac_params->ecw_min_max & 0xF0) >> 4; qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1); qos_param->flag[i] = (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00; txop = le16_to_cpu(ac_params->tx_op_limit) * 32; qos_param->tx_op_limit[i] = cpu_to_le16(txop); } return rc; } /* * we have a generic data element which it may contain QoS information or * parameters element. check the information element length to decide * which type to read */ static int libipw_parse_qos_info_param_IE(struct libipw_info_element *info_element, struct libipw_network *network) { int rc = 0; struct libipw_qos_parameters *qos_param = NULL; struct libipw_qos_information_element qos_info_element; rc = libipw_read_qos_info_element(&qos_info_element, info_element); if (rc == 0) { network->qos_data.param_count = qos_info_element.ac_info & 0x0F; network->flags |= NETWORK_HAS_QOS_INFORMATION; } else { struct libipw_qos_parameter_info param_element; rc = libipw_read_qos_param_element(¶m_element, info_element); if (rc == 0) { qos_param = &(network->qos_data.parameters); libipw_qos_convert_ac_to_parameters(¶m_element, qos_param); network->flags |= NETWORK_HAS_QOS_PARAMETERS; network->qos_data.param_count = param_element.info_element.ac_info & 0x0F; } } if (rc == 0) { LIBIPW_DEBUG_QOS("QoS is supported\n"); network->qos_data.supported = 1; } return rc; } #ifdef CONFIG_LIBIPW_DEBUG #define MFIE_STRING(x) case WLAN_EID_ ##x: return #x static const char *get_info_element_string(u16 id) { switch (id) { MFIE_STRING(SSID); MFIE_STRING(SUPP_RATES); MFIE_STRING(FH_PARAMS); MFIE_STRING(DS_PARAMS); MFIE_STRING(CF_PARAMS); MFIE_STRING(TIM); MFIE_STRING(IBSS_PARAMS); MFIE_STRING(COUNTRY); MFIE_STRING(HP_PARAMS); MFIE_STRING(HP_TABLE); MFIE_STRING(REQUEST); MFIE_STRING(CHALLENGE); MFIE_STRING(PWR_CONSTRAINT); MFIE_STRING(PWR_CAPABILITY); MFIE_STRING(TPC_REQUEST); MFIE_STRING(TPC_REPORT); MFIE_STRING(SUPPORTED_CHANNELS); MFIE_STRING(CHANNEL_SWITCH); MFIE_STRING(MEASURE_REQUEST); MFIE_STRING(MEASURE_REPORT); MFIE_STRING(QUIET); MFIE_STRING(IBSS_DFS); MFIE_STRING(ERP_INFO); MFIE_STRING(RSN); MFIE_STRING(EXT_SUPP_RATES); MFIE_STRING(GENERIC); MFIE_STRING(QOS_PARAMETER); default: return "UNKNOWN"; } } #endif static int libipw_parse_info_param(struct libipw_info_element *info_element, u16 length, struct libipw_network *network) { DECLARE_SSID_BUF(ssid); u8 i; #ifdef CONFIG_LIBIPW_DEBUG char rates_str[64]; char *p; #endif while (length >= sizeof(*info_element)) { if (sizeof(*info_element) + info_element->len > length) { LIBIPW_DEBUG_MGMT("Info elem: parse failed: " "info_element->len + 2 > left : " "info_element->len+2=%zd left=%d, id=%d.\n", info_element->len + sizeof(*info_element), length, info_element->id); /* We stop processing but don't return an error here * because some misbehaviour APs break this rule. ie. * Orinoco AP1000. */ break; } switch (info_element->id) { case WLAN_EID_SSID: network->ssid_len = min(info_element->len, (u8) IW_ESSID_MAX_SIZE); memcpy(network->ssid, info_element->data, network->ssid_len); if (network->ssid_len < IW_ESSID_MAX_SIZE) memset(network->ssid + network->ssid_len, 0, IW_ESSID_MAX_SIZE - network->ssid_len); LIBIPW_DEBUG_MGMT("WLAN_EID_SSID: '%s' len=%d.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->ssid_len); break; case WLAN_EID_SUPP_RATES: #ifdef CONFIG_LIBIPW_DEBUG p = rates_str; #endif network->rates_len = min(info_element->len, MAX_RATES_LENGTH); for (i = 0; i < network->rates_len; i++) { network->rates[i] = info_element->data[i]; #ifdef CONFIG_LIBIPW_DEBUG p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]); #endif if (libipw_is_ofdm_rate (info_element->data[i])) { network->flags |= NETWORK_HAS_OFDM; if (info_element->data[i] & LIBIPW_BASIC_RATE_MASK) network->flags &= ~NETWORK_HAS_CCK; } } LIBIPW_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n", rates_str, network->rates_len); break; case WLAN_EID_EXT_SUPP_RATES: #ifdef CONFIG_LIBIPW_DEBUG p = rates_str; #endif network->rates_ex_len = min(info_element->len, MAX_RATES_EX_LENGTH); for (i = 0; i < network->rates_ex_len; i++) { network->rates_ex[i] = info_element->data[i]; #ifdef CONFIG_LIBIPW_DEBUG p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]); #endif if (libipw_is_ofdm_rate (info_element->data[i])) { network->flags |= NETWORK_HAS_OFDM; if (info_element->data[i] & LIBIPW_BASIC_RATE_MASK) network->flags &= ~NETWORK_HAS_CCK; } } LIBIPW_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n", rates_str, network->rates_ex_len); break; case WLAN_EID_DS_PARAMS: LIBIPW_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n", info_element->data[0]); network->channel = info_element->data[0]; break; case WLAN_EID_FH_PARAMS: LIBIPW_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n"); break; case WLAN_EID_CF_PARAMS: LIBIPW_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n"); break; case WLAN_EID_TIM: network->tim.tim_count = info_element->data[0]; network->tim.tim_period = info_element->data[1]; LIBIPW_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n"); break; case WLAN_EID_ERP_INFO: network->erp_value = info_element->data[0]; network->flags |= NETWORK_HAS_ERP_VALUE; LIBIPW_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n", network->erp_value); break; case WLAN_EID_IBSS_PARAMS: network->atim_window = info_element->data[0]; LIBIPW_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n", network->atim_window); break; case WLAN_EID_CHALLENGE: LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n"); break; case WLAN_EID_GENERIC: LIBIPW_DEBUG_MGMT("WLAN_EID_GENERIC: %d bytes\n", info_element->len); if (!libipw_parse_qos_info_param_IE(info_element, network)) break; if (info_element->len >= 4 && info_element->data[0] == 0x00 && info_element->data[1] == 0x50 && info_element->data[2] == 0xf2 && info_element->data[3] == 0x01) { network->wpa_ie_len = min(info_element->len + 2, MAX_WPA_IE_LEN); memcpy(network->wpa_ie, info_element, network->wpa_ie_len); } break; case WLAN_EID_RSN: LIBIPW_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n", info_element->len); network->rsn_ie_len = min(info_element->len + 2, MAX_WPA_IE_LEN); memcpy(network->rsn_ie, info_element, network->rsn_ie_len); break; case WLAN_EID_QOS_PARAMETER: printk(KERN_ERR "QoS Error need to parse QOS_PARAMETER IE\n"); break; /* 802.11h */ case WLAN_EID_PWR_CONSTRAINT: network->power_constraint = info_element->data[0]; network->flags |= NETWORK_HAS_POWER_CONSTRAINT; break; case WLAN_EID_CHANNEL_SWITCH: network->power_constraint = info_element->data[0]; network->flags |= NETWORK_HAS_CSA; break; case WLAN_EID_QUIET: network->quiet.count = info_element->data[0]; network->quiet.period = info_element->data[1]; network->quiet.duration = info_element->data[2]; network->quiet.offset = info_element->data[3]; network->flags |= NETWORK_HAS_QUIET; break; case WLAN_EID_IBSS_DFS: if (network->ibss_dfs) break; network->ibss_dfs = kmemdup(info_element->data, info_element->len, GFP_ATOMIC); if (!network->ibss_dfs) return 1; network->flags |= NETWORK_HAS_IBSS_DFS; break; case WLAN_EID_TPC_REPORT: network->tpc_report.transmit_power = info_element->data[0]; network->tpc_report.link_margin = info_element->data[1]; network->flags |= NETWORK_HAS_TPC_REPORT; break; default: LIBIPW_DEBUG_MGMT ("Unsupported info element: %s (%d)\n", get_info_element_string(info_element->id), info_element->id); break; } length -= sizeof(*info_element) + info_element->len; info_element = (struct libipw_info_element *)&info_element-> data[info_element->len]; } return 0; } static int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response *frame, struct libipw_rx_stats *stats) { struct libipw_network network_resp = { .ibss_dfs = NULL, }; struct libipw_network *network = &network_resp; struct net_device *dev = ieee->dev; network->flags = 0; network->qos_data.active = 0; network->qos_data.supported = 0; network->qos_data.param_count = 0; network->qos_data.old_param_count = 0; //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF); network->atim_window = le16_to_cpu(frame->aid); network->listen_interval = le16_to_cpu(frame->status); memcpy(network->bssid, frame->header.addr3, ETH_ALEN); network->capability = le16_to_cpu(frame->capability); network->last_scanned = jiffies; network->rates_len = network->rates_ex_len = 0; network->last_associate = 0; network->ssid_len = 0; network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; if (stats->freq == LIBIPW_52GHZ_BAND) { /* for A band (No DS info) */ network->channel = stats->received_channel; } else network->flags |= NETWORK_HAS_CCK; network->wpa_ie_len = 0; network->rsn_ie_len = 0; if (libipw_parse_info_param (frame->info_element, stats->len - sizeof(*frame), network)) return 1; network->mode = 0; if (stats->freq == LIBIPW_52GHZ_BAND) network->mode = IEEE_A; else { if (network->flags & NETWORK_HAS_OFDM) network->mode |= IEEE_G; if (network->flags & NETWORK_HAS_CCK) network->mode |= IEEE_B; } memcpy(&network->stats, stats, sizeof(network->stats)); if (ieee->handle_assoc_response != NULL) ieee->handle_assoc_response(dev, frame, network); return 0; } /***************************************************/ static int libipw_network_init(struct libipw_device *ieee, struct libipw_probe_response *beacon, struct libipw_network *network, struct libipw_rx_stats *stats) { DECLARE_SSID_BUF(ssid); network->qos_data.active = 0; network->qos_data.supported = 0; network->qos_data.param_count = 0; network->qos_data.old_param_count = 0; /* Pull out fixed field data */ memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); network->capability = le16_to_cpu(beacon->capability); network->last_scanned = jiffies; network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]); network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]); network->beacon_interval = le16_to_cpu(beacon->beacon_interval); /* Where to pull this? beacon->listen_interval; */ network->listen_interval = 0x0A; network->rates_len = network->rates_ex_len = 0; network->last_associate = 0; network->ssid_len = 0; network->flags = 0; network->atim_window = 0; network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; if (stats->freq == LIBIPW_52GHZ_BAND) { /* for A band (No DS info) */ network->channel = stats->received_channel; } else network->flags |= NETWORK_HAS_CCK; network->wpa_ie_len = 0; network->rsn_ie_len = 0; if (libipw_parse_info_param (beacon->info_element, stats->len - sizeof(*beacon), network)) return 1; network->mode = 0; if (stats->freq == LIBIPW_52GHZ_BAND) network->mode = IEEE_A; else { if (network->flags & NETWORK_HAS_OFDM) network->mode |= IEEE_G; if (network->flags & NETWORK_HAS_CCK) network->mode |= IEEE_B; } if (network->mode == 0) { LIBIPW_DEBUG_SCAN("Filtered out '%s (%pM)' " "network.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->bssid); return 1; } memcpy(&network->stats, stats, sizeof(network->stats)); return 0; } static inline int is_same_network(struct libipw_network *src, struct libipw_network *dst) { /* A network is only a duplicate if the channel, BSSID, and ESSID * all match. We treat all with the same BSSID and channel * as one network */ return ((src->ssid_len == dst->ssid_len) && (src->channel == dst->channel) && ether_addr_equal(src->bssid, dst->bssid) && !memcmp(src->ssid, dst->ssid, src->ssid_len)); } static void update_network(struct libipw_network *dst, struct libipw_network *src) { int qos_active; u8 old_param; libipw_network_reset(dst); dst->ibss_dfs = src->ibss_dfs; /* We only update the statistics if they were created by receiving * the network information on the actual channel the network is on. * * This keeps beacons received on neighbor channels from bringing * down the signal level of an AP. */ if (dst->channel == src->stats.received_channel) memcpy(&dst->stats, &src->stats, sizeof(struct libipw_rx_stats)); else LIBIPW_DEBUG_SCAN("Network %pM info received " "off channel (%d vs. %d)\n", src->bssid, dst->channel, src->stats.received_channel); dst->capability = src->capability; memcpy(dst->rates, src->rates, src->rates_len); dst->rates_len = src->rates_len; memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); dst->rates_ex_len = src->rates_ex_len; dst->mode = src->mode; dst->flags = src->flags; dst->time_stamp[0] = src->time_stamp[0]; dst->time_stamp[1] = src->time_stamp[1]; dst->beacon_interval = src->beacon_interval; dst->listen_interval = src->listen_interval; dst->atim_window = src->atim_window; dst->erp_value = src->erp_value; dst->tim = src->tim; memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); dst->wpa_ie_len = src->wpa_ie_len; memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); dst->rsn_ie_len = src->rsn_ie_len; dst->last_scanned = jiffies; qos_active = src->qos_data.active; old_param = dst->qos_data.old_param_count; if (dst->flags & NETWORK_HAS_QOS_MASK) memcpy(&dst->qos_data, &src->qos_data, sizeof(struct libipw_qos_data)); else { dst->qos_data.supported = src->qos_data.supported; dst->qos_data.param_count = src->qos_data.param_count; } if (dst->qos_data.supported == 1) { if (dst->ssid_len) LIBIPW_DEBUG_QOS ("QoS the network %s is QoS supported\n", dst->ssid); else LIBIPW_DEBUG_QOS ("QoS the network is QoS supported\n"); } dst->qos_data.active = qos_active; dst->qos_data.old_param_count = old_param; /* dst->last_associate is not overwritten */ } static inline int is_beacon(__le16 fc) { return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON); } static void libipw_process_probe_response(struct libipw_device *ieee, struct libipw_probe_response *beacon, struct libipw_rx_stats *stats) { struct net_device *dev = ieee->dev; struct libipw_network network = { .ibss_dfs = NULL, }; struct libipw_network *target; struct libipw_network *oldest = NULL; #ifdef CONFIG_LIBIPW_DEBUG struct libipw_info_element *info_element = beacon->info_element; #endif unsigned long flags; DECLARE_SSID_BUF(ssid); LIBIPW_DEBUG_SCAN("'%s' (%pM" "): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", print_ssid(ssid, info_element->data, info_element->len), beacon->header.addr3, (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0'); if (libipw_network_init(ieee, beacon, &network, stats)) { LIBIPW_DEBUG_SCAN("Dropped '%s' (%pM) via %s.\n", print_ssid(ssid, info_element->data, info_element->len), beacon->header.addr3, is_beacon(beacon->header.frame_ctl) ? "BEACON" : "PROBE RESPONSE"); return; } /* The network parsed correctly -- so now we scan our known networks * to see if we can find it in our list. * * NOTE: This search is definitely not optimized. Once its doing * the "right thing" we'll optimize it for efficiency if * necessary */ /* Search for this entry in the list and update it if it is * already there. */ spin_lock_irqsave(&ieee->lock, flags); list_for_each_entry(target, &ieee->network_list, list) { if (is_same_network(target, &network)) break; if ((oldest == NULL) || time_before(target->last_scanned, oldest->last_scanned)) oldest = target; } /* If we didn't find a match, then get a new network slot to initialize * with this beacon's information */ if (&target->list == &ieee->network_list) { if (list_empty(&ieee->network_free_list)) { /* If there are no more slots, expire the oldest */ list_del(&oldest->list); target = oldest; LIBIPW_DEBUG_SCAN("Expired '%s' (%pM) from " "network list.\n", print_ssid(ssid, target->ssid, target->ssid_len), target->bssid); libipw_network_reset(target); } else { /* Otherwise just pull from the free list */ target = list_entry(ieee->network_free_list.next, struct libipw_network, list); list_del(ieee->network_free_list.next); } #ifdef CONFIG_LIBIPW_DEBUG LIBIPW_DEBUG_SCAN("Adding '%s' (%pM) via %s.\n", print_ssid(ssid, network.ssid, network.ssid_len), network.bssid, is_beacon(beacon->header.frame_ctl) ? "BEACON" : "PROBE RESPONSE"); #endif memcpy(target, &network, sizeof(*target)); network.ibss_dfs = NULL; list_add_tail(&target->list, &ieee->network_list); } else { LIBIPW_DEBUG_SCAN("Updating '%s' (%pM) via %s.\n", print_ssid(ssid, target->ssid, target->ssid_len), target->bssid, is_beacon(beacon->header.frame_ctl) ? "BEACON" : "PROBE RESPONSE"); update_network(target, &network); network.ibss_dfs = NULL; } spin_unlock_irqrestore(&ieee->lock, flags); if (is_beacon(beacon->header.frame_ctl)) { if (ieee->handle_beacon != NULL) ieee->handle_beacon(dev, beacon, target); } else { if (ieee->handle_probe_response != NULL) ieee->handle_probe_response(dev, beacon, target); } } void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header, struct libipw_rx_stats *stats) { switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) { case IEEE80211_STYPE_ASSOC_RESP: LIBIPW_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); libipw_handle_assoc_resp(ieee, (struct libipw_assoc_response *) header, stats); break; case IEEE80211_STYPE_REASSOC_RESP: LIBIPW_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); break; case IEEE80211_STYPE_PROBE_REQ: LIBIPW_DEBUG_MGMT("received auth (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); if (ieee->handle_probe_request != NULL) ieee->handle_probe_request(ieee->dev, (struct libipw_probe_request *) header, stats); break; case IEEE80211_STYPE_PROBE_RESP: LIBIPW_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); LIBIPW_DEBUG_SCAN("Probe response\n"); libipw_process_probe_response(ieee, (struct libipw_probe_response *) header, stats); break; case IEEE80211_STYPE_BEACON: LIBIPW_DEBUG_MGMT("received BEACON (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); LIBIPW_DEBUG_SCAN("Beacon\n"); libipw_process_probe_response(ieee, (struct libipw_probe_response *) header, stats); break; case IEEE80211_STYPE_AUTH: LIBIPW_DEBUG_MGMT("received auth (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); if (ieee->handle_auth != NULL) ieee->handle_auth(ieee->dev, (struct libipw_auth *)header); break; case IEEE80211_STYPE_DISASSOC: if (ieee->handle_disassoc != NULL) ieee->handle_disassoc(ieee->dev, (struct libipw_disassoc *) header); break; case IEEE80211_STYPE_ACTION: LIBIPW_DEBUG_MGMT("ACTION\n"); if (ieee->handle_action) ieee->handle_action(ieee->dev, (struct libipw_action *) header, stats); break; case IEEE80211_STYPE_REASSOC_REQ: LIBIPW_DEBUG_MGMT("received reassoc (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); LIBIPW_DEBUG_MGMT("%s: LIBIPW_REASSOC_REQ received\n", ieee->dev->name); if (ieee->handle_reassoc_request != NULL) ieee->handle_reassoc_request(ieee->dev, (struct libipw_reassoc_request *) header); break; case IEEE80211_STYPE_ASSOC_REQ: LIBIPW_DEBUG_MGMT("received assoc (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); LIBIPW_DEBUG_MGMT("%s: LIBIPW_ASSOC_REQ received\n", ieee->dev->name); if (ieee->handle_assoc_request != NULL) ieee->handle_assoc_request(ieee->dev); break; case IEEE80211_STYPE_DEAUTH: LIBIPW_DEBUG_MGMT("DEAUTH\n"); if (ieee->handle_deauth != NULL) ieee->handle_deauth(ieee->dev, (struct libipw_deauth *) header); break; default: LIBIPW_DEBUG_MGMT("received UNKNOWN (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); LIBIPW_DEBUG_MGMT("%s: Unknown management packet: %d\n", ieee->dev->name, WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); break; } } EXPORT_SYMBOL_GPL(libipw_rx_any); EXPORT_SYMBOL(libipw_rx_mgt); EXPORT_SYMBOL(libipw_rx); compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/libipw_module.c0000644000175000017500000002140012026211315024666 0ustar mcgrofmcgrof/******************************************************************************* Copyright(c) 2004-2005 Intel Corporation. All rights reserved. Portions of this file are based on the WEP enablement code provided by the Host AP project hostap-drivers v0.1.3 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen Copyright (c) 2002-2003, Jouni Malinen This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libipw.h" #define DRV_DESCRIPTION "802.11 data/management/control stack" #define DRV_NAME "libipw" #define DRV_PROCNAME "ieee80211" #define DRV_VERSION LIBIPW_VERSION #define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation " MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); static struct cfg80211_ops libipw_config_ops = { }; static void *libipw_wiphy_privid = &libipw_wiphy_privid; static int libipw_networks_allocate(struct libipw_device *ieee) { int i, j; for (i = 0; i < MAX_NETWORK_COUNT; i++) { ieee->networks[i] = kzalloc(sizeof(struct libipw_network), GFP_KERNEL); if (!ieee->networks[i]) { LIBIPW_ERROR("Out of memory allocating beacons\n"); for (j = 0; j < i; j++) kfree(ieee->networks[j]); return -ENOMEM; } } return 0; } void libipw_network_reset(struct libipw_network *network) { if (!network) return; if (network->ibss_dfs) { kfree(network->ibss_dfs); network->ibss_dfs = NULL; } } static inline void libipw_networks_free(struct libipw_device *ieee) { int i; for (i = 0; i < MAX_NETWORK_COUNT; i++) { if (ieee->networks[i]->ibss_dfs) kfree(ieee->networks[i]->ibss_dfs); kfree(ieee->networks[i]); } } void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs) { struct libipw_network *network = NULL; unsigned long flags; unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); spin_lock_irqsave(&ieee->lock, flags); list_for_each_entry(network, &ieee->network_list, list) { network->last_scanned -= age_jiffies; } spin_unlock_irqrestore(&ieee->lock, flags); } EXPORT_SYMBOL(libipw_networks_age); static void libipw_networks_initialize(struct libipw_device *ieee) { int i; INIT_LIST_HEAD(&ieee->network_free_list); INIT_LIST_HEAD(&ieee->network_list); for (i = 0; i < MAX_NETWORK_COUNT; i++) list_add_tail(&ieee->networks[i]->list, &ieee->network_free_list); } int libipw_change_mtu(struct net_device *dev, int new_mtu) { if ((new_mtu < 68) || (new_mtu > LIBIPW_DATA_LEN)) return -EINVAL; dev->mtu = new_mtu; return 0; } EXPORT_SYMBOL(libipw_change_mtu); struct net_device *alloc_libipw(int sizeof_priv, int monitor) { struct libipw_device *ieee; struct net_device *dev; int err; LIBIPW_DEBUG_INFO("Initializing...\n"); dev = alloc_etherdev(sizeof(struct libipw_device) + sizeof_priv); if (!dev) goto failed; ieee = netdev_priv(dev); ieee->dev = dev; if (!monitor) { ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0); if (!ieee->wdev.wiphy) { LIBIPW_ERROR("Unable to allocate wiphy.\n"); goto failed_free_netdev; } ieee->dev->ieee80211_ptr = &ieee->wdev; ieee->wdev.iftype = NL80211_IFTYPE_STATION; /* Fill-out wiphy structure bits we know... Not enough info here to call set_wiphy_dev or set MAC address or channel info -- have to do that in ->ndo_init... */ ieee->wdev.wiphy->privid = libipw_wiphy_privid; ieee->wdev.wiphy->max_scan_ssids = 1; ieee->wdev.wiphy->max_scan_ie_len = 0; ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); } err = libipw_networks_allocate(ieee); if (err) { LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err); goto failed_free_wiphy; } libipw_networks_initialize(ieee); /* Default fragmentation threshold is maximum payload size */ ieee->fts = DEFAULT_FTS; ieee->rts = DEFAULT_FTS; ieee->scan_age = DEFAULT_MAX_SCAN_AGE; ieee->open_wep = 1; /* Default to enabling full open WEP with host based encrypt/decrypt */ ieee->host_encrypt = 1; ieee->host_decrypt = 1; ieee->host_mc_decrypt = 1; /* Host fragmentation in Open mode. Default is enabled. * Note: host fragmentation is always enabled if host encryption * is enabled. For cards can do hardware encryption, they must do * hardware fragmentation as well. So we don't need a variable * like host_enc_frag. */ ieee->host_open_frag = 1; ieee->ieee802_1x = 1; /* Default to supporting 802.1x */ spin_lock_init(&ieee->lock); lib80211_crypt_info_init(&ieee->crypt_info, dev->name, &ieee->lock); ieee->wpa_enabled = 0; ieee->drop_unencrypted = 0; ieee->privacy_invoked = 0; return dev; failed_free_wiphy: if (!monitor) wiphy_free(ieee->wdev.wiphy); failed_free_netdev: free_netdev(dev); failed: return NULL; } EXPORT_SYMBOL(alloc_libipw); void free_libipw(struct net_device *dev, int monitor) { struct libipw_device *ieee = netdev_priv(dev); lib80211_crypt_info_free(&ieee->crypt_info); libipw_networks_free(ieee); /* free cfg80211 resources */ if (!monitor) wiphy_free(ieee->wdev.wiphy); free_netdev(dev); } EXPORT_SYMBOL(free_libipw); #ifdef CONFIG_LIBIPW_DEBUG static int debug = 0; u32 libipw_debug_level = 0; EXPORT_SYMBOL_GPL(libipw_debug_level); static struct proc_dir_entry *libipw_proc = NULL; static int debug_level_proc_show(struct seq_file *m, void *v) { seq_printf(m, "0x%08X\n", libipw_debug_level); return 0; } static int debug_level_proc_open(struct inode *inode, struct file *file) { return single_open(file, debug_level_proc_show, NULL); } static ssize_t debug_level_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { char buf[] = "0x00000000\n"; size_t len = min(sizeof(buf) - 1, count); unsigned long val; if (copy_from_user(buf, buffer, len)) return count; buf[len] = 0; if (sscanf(buf, "%li", &val) != 1) printk(KERN_INFO DRV_NAME ": %s is not in hex or decimal form.\n", buf); else libipw_debug_level = val; return strnlen(buf, len); } static const struct file_operations debug_level_proc_fops = { .owner = THIS_MODULE, .open = debug_level_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_level_proc_write, }; #endif /* CONFIG_LIBIPW_DEBUG */ static int __init libipw_init(void) { #ifdef CONFIG_LIBIPW_DEBUG struct proc_dir_entry *e; libipw_debug_level = debug; libipw_proc = proc_mkdir(DRV_PROCNAME, init_net.proc_net); if (libipw_proc == NULL) { LIBIPW_ERROR("Unable to create " DRV_PROCNAME " proc directory\n"); return -EIO; } e = proc_create("debug_level", S_IRUGO | S_IWUSR, libipw_proc, &debug_level_proc_fops); if (!e) { remove_proc_entry(DRV_PROCNAME, init_net.proc_net); libipw_proc = NULL; return -EIO; } #endif /* CONFIG_LIBIPW_DEBUG */ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); return 0; } static void __exit libipw_exit(void) { #ifdef CONFIG_LIBIPW_DEBUG if (libipw_proc) { remove_proc_entry("debug_level", libipw_proc); remove_proc_entry(DRV_PROCNAME, init_net.proc_net); libipw_proc = NULL; } #endif /* CONFIG_LIBIPW_DEBUG */ } #ifdef CONFIG_LIBIPW_DEBUG #include module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "debug output mask"); #endif /* CONFIG_LIBIPW_DEBUG */ module_exit(libipw_exit); module_init(libipw_init); compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/libipw.h0000644000175000017500000006744112026211315023345 0ustar mcgrofmcgrof/* * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 * remains copyright by the original authors * * Portions of the merged code are based on Host AP (software wireless * LAN access point) driver for Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen * * Copyright (c) 2002-2003, Jouni Malinen * * Adaption to a generic IEEE 802.11 stack by James Ketrenos * * Copyright (c) 2004-2005, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. * * API Version History * 1.0.x -- Initial version * 1.1.x -- Added radiotap, QoS, TIM, libipw_geo APIs, * various structure changes, and crypto API init method */ #ifndef LIBIPW_H #define LIBIPW_H #include /* ETH_ALEN */ #include /* ARRAY_SIZE */ #include #include #include #include #define LIBIPW_VERSION "git-1.1.13" #define LIBIPW_DATA_LEN 2304 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section 6.2.1.1.2. The figure in section 7.1.2 suggests a body size of up to 2312 bytes is allowed, which is a bit confusing, I suspect this represents the 2304 bytes of real data, plus a possible 8 bytes of WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ #define LIBIPW_1ADDR_LEN 10 #define LIBIPW_2ADDR_LEN 16 #define LIBIPW_3ADDR_LEN 24 #define LIBIPW_4ADDR_LEN 30 #define LIBIPW_FCS_LEN 4 #define LIBIPW_HLEN (LIBIPW_4ADDR_LEN) #define LIBIPW_FRAME_LEN (LIBIPW_DATA_LEN + LIBIPW_HLEN) #define MIN_FRAG_THRESHOLD 256U #define MAX_FRAG_THRESHOLD 2346U /* QOS control */ #define LIBIPW_QCTL_TID 0x000F /* debug macros */ #ifdef CONFIG_LIBIPW_DEBUG extern u32 libipw_debug_level; #define LIBIPW_DEBUG(level, fmt, args...) \ do { if (libipw_debug_level & (level)) \ printk(KERN_DEBUG "libipw: %c %s " fmt, \ in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #else #define LIBIPW_DEBUG(level, fmt, args...) do {} while (0) #endif /* CONFIG_LIBIPW_DEBUG */ /* * To use the debug system: * * If you are defining a new debug classification, simply add it to the #define * list here in the form of: * * #define LIBIPW_DL_xxxx VALUE * * shifting value to the left one bit from the previous entry. xxxx should be * the name of the classification (for example, WEP) * * You then need to either add a LIBIPW_xxxx_DEBUG() macro definition for your * classification, or use LIBIPW_DEBUG(LIBIPW_DL_xxxx, ...) whenever you want * to send output to that classification. * * To add your debug level to the list of levels seen when you perform * * % cat /proc/net/ieee80211/debug_level * * you simply need to add your entry to the libipw_debug_level array. * * If you do not see debug_level in /proc/net/ieee80211 then you do not have * CONFIG_LIBIPW_DEBUG defined in your kernel configuration * */ #define LIBIPW_DL_INFO (1<<0) #define LIBIPW_DL_WX (1<<1) #define LIBIPW_DL_SCAN (1<<2) #define LIBIPW_DL_STATE (1<<3) #define LIBIPW_DL_MGMT (1<<4) #define LIBIPW_DL_FRAG (1<<5) #define LIBIPW_DL_DROP (1<<7) #define LIBIPW_DL_TX (1<<8) #define LIBIPW_DL_RX (1<<9) #define LIBIPW_DL_QOS (1<<31) #define LIBIPW_ERROR(f, a...) printk(KERN_ERR "libipw: " f, ## a) #define LIBIPW_WARNING(f, a...) printk(KERN_WARNING "libipw: " f, ## a) #define LIBIPW_DEBUG_INFO(f, a...) LIBIPW_DEBUG(LIBIPW_DL_INFO, f, ## a) #define LIBIPW_DEBUG_WX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_WX, f, ## a) #define LIBIPW_DEBUG_SCAN(f, a...) LIBIPW_DEBUG(LIBIPW_DL_SCAN, f, ## a) #define LIBIPW_DEBUG_STATE(f, a...) LIBIPW_DEBUG(LIBIPW_DL_STATE, f, ## a) #define LIBIPW_DEBUG_MGMT(f, a...) LIBIPW_DEBUG(LIBIPW_DL_MGMT, f, ## a) #define LIBIPW_DEBUG_FRAG(f, a...) LIBIPW_DEBUG(LIBIPW_DL_FRAG, f, ## a) #define LIBIPW_DEBUG_DROP(f, a...) LIBIPW_DEBUG(LIBIPW_DL_DROP, f, ## a) #define LIBIPW_DEBUG_TX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_TX, f, ## a) #define LIBIPW_DEBUG_RX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_RX, f, ## a) #define LIBIPW_DEBUG_QOS(f, a...) LIBIPW_DEBUG(LIBIPW_DL_QOS, f, ## a) #include #include /* ARPHRD_ETHER */ #ifndef WIRELESS_SPY #define WIRELESS_SPY /* enable iwspy support */ #endif #include /* new driver API */ #define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ #ifndef ETH_P_80211_RAW #define ETH_P_80211_RAW (ETH_P_ECONET + 1) #endif /* IEEE 802.11 defines */ #define P80211_OUI_LEN 3 struct libipw_snap_hdr { u8 dsap; /* always 0xAA */ u8 ssap; /* always 0xAA */ u8 ctrl; /* always 0x03 */ u8 oui[P80211_OUI_LEN]; /* organizational universal id */ } __packed; #define SNAP_SIZE sizeof(struct libipw_snap_hdr) #define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS) #define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) #define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) #define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) #define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) #define LIBIPW_STATMASK_SIGNAL (1<<0) #define LIBIPW_STATMASK_RSSI (1<<1) #define LIBIPW_STATMASK_NOISE (1<<2) #define LIBIPW_STATMASK_RATE (1<<3) #define LIBIPW_STATMASK_WEMASK 0x7 #define LIBIPW_CCK_MODULATION (1<<0) #define LIBIPW_OFDM_MODULATION (1<<1) #define LIBIPW_24GHZ_BAND (1<<0) #define LIBIPW_52GHZ_BAND (1<<1) #define LIBIPW_CCK_RATE_1MB 0x02 #define LIBIPW_CCK_RATE_2MB 0x04 #define LIBIPW_CCK_RATE_5MB 0x0B #define LIBIPW_CCK_RATE_11MB 0x16 #define LIBIPW_OFDM_RATE_6MB 0x0C #define LIBIPW_OFDM_RATE_9MB 0x12 #define LIBIPW_OFDM_RATE_12MB 0x18 #define LIBIPW_OFDM_RATE_18MB 0x24 #define LIBIPW_OFDM_RATE_24MB 0x30 #define LIBIPW_OFDM_RATE_36MB 0x48 #define LIBIPW_OFDM_RATE_48MB 0x60 #define LIBIPW_OFDM_RATE_54MB 0x6C #define LIBIPW_BASIC_RATE_MASK 0x80 #define LIBIPW_CCK_RATE_1MB_MASK (1<<0) #define LIBIPW_CCK_RATE_2MB_MASK (1<<1) #define LIBIPW_CCK_RATE_5MB_MASK (1<<2) #define LIBIPW_CCK_RATE_11MB_MASK (1<<3) #define LIBIPW_OFDM_RATE_6MB_MASK (1<<4) #define LIBIPW_OFDM_RATE_9MB_MASK (1<<5) #define LIBIPW_OFDM_RATE_12MB_MASK (1<<6) #define LIBIPW_OFDM_RATE_18MB_MASK (1<<7) #define LIBIPW_OFDM_RATE_24MB_MASK (1<<8) #define LIBIPW_OFDM_RATE_36MB_MASK (1<<9) #define LIBIPW_OFDM_RATE_48MB_MASK (1<<10) #define LIBIPW_OFDM_RATE_54MB_MASK (1<<11) #define LIBIPW_CCK_RATES_MASK 0x0000000F #define LIBIPW_CCK_BASIC_RATES_MASK (LIBIPW_CCK_RATE_1MB_MASK | \ LIBIPW_CCK_RATE_2MB_MASK) #define LIBIPW_CCK_DEFAULT_RATES_MASK (LIBIPW_CCK_BASIC_RATES_MASK | \ LIBIPW_CCK_RATE_5MB_MASK | \ LIBIPW_CCK_RATE_11MB_MASK) #define LIBIPW_OFDM_RATES_MASK 0x00000FF0 #define LIBIPW_OFDM_BASIC_RATES_MASK (LIBIPW_OFDM_RATE_6MB_MASK | \ LIBIPW_OFDM_RATE_12MB_MASK | \ LIBIPW_OFDM_RATE_24MB_MASK) #define LIBIPW_OFDM_DEFAULT_RATES_MASK (LIBIPW_OFDM_BASIC_RATES_MASK | \ LIBIPW_OFDM_RATE_9MB_MASK | \ LIBIPW_OFDM_RATE_18MB_MASK | \ LIBIPW_OFDM_RATE_36MB_MASK | \ LIBIPW_OFDM_RATE_48MB_MASK | \ LIBIPW_OFDM_RATE_54MB_MASK) #define LIBIPW_DEFAULT_RATES_MASK (LIBIPW_OFDM_DEFAULT_RATES_MASK | \ LIBIPW_CCK_DEFAULT_RATES_MASK) #define LIBIPW_NUM_OFDM_RATES 8 #define LIBIPW_NUM_CCK_RATES 4 #define LIBIPW_OFDM_SHIFT_MASK_A 4 /* NOTE: This data is for statistical purposes; not all hardware provides this * information for frames received. * For libipw_rx_mgt, you need to set at least the 'len' parameter. */ struct libipw_rx_stats { u32 mac_time; s8 rssi; u8 signal; u8 noise; u16 rate; /* in 100 kbps */ u8 received_channel; u8 control; u8 mask; u8 freq; u16 len; u64 tsf; u32 beacon_time; }; /* IEEE 802.11 requires that STA supports concurrent reception of at least * three fragmented frames. This define can be increased to support more * concurrent frames, but it should be noted that each entry can consume about * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ #define LIBIPW_FRAG_CACHE_LEN 4 struct libipw_frag_entry { unsigned long first_frag_time; unsigned int seq; unsigned int last_frag; struct sk_buff *skb; u8 src_addr[ETH_ALEN]; u8 dst_addr[ETH_ALEN]; }; struct libipw_stats { unsigned int tx_unicast_frames; unsigned int tx_multicast_frames; unsigned int tx_fragments; unsigned int tx_unicast_octets; unsigned int tx_multicast_octets; unsigned int tx_deferred_transmissions; unsigned int tx_single_retry_frames; unsigned int tx_multiple_retry_frames; unsigned int tx_retry_limit_exceeded; unsigned int tx_discards; unsigned int rx_unicast_frames; unsigned int rx_multicast_frames; unsigned int rx_fragments; unsigned int rx_unicast_octets; unsigned int rx_multicast_octets; unsigned int rx_fcs_errors; unsigned int rx_discards_no_buffer; unsigned int tx_discards_wrong_sa; unsigned int rx_discards_undecryptable; unsigned int rx_message_in_msg_fragments; unsigned int rx_message_in_bad_msg_fragments; }; struct libipw_device; #define SEC_KEY_1 (1<<0) #define SEC_KEY_2 (1<<1) #define SEC_KEY_3 (1<<2) #define SEC_KEY_4 (1<<3) #define SEC_ACTIVE_KEY (1<<4) #define SEC_AUTH_MODE (1<<5) #define SEC_UNICAST_GROUP (1<<6) #define SEC_LEVEL (1<<7) #define SEC_ENABLED (1<<8) #define SEC_ENCRYPT (1<<9) #define SEC_LEVEL_0 0 /* None */ #define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ #define SEC_LEVEL_2 2 /* Level 1 + TKIP */ #define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ #define SEC_LEVEL_3 4 /* Level 2 + CCMP */ #define SEC_ALG_NONE 0 #define SEC_ALG_WEP 1 #define SEC_ALG_TKIP 2 #define SEC_ALG_CCMP 3 #define WEP_KEYS 4 #define WEP_KEY_LEN 13 #define SCM_KEY_LEN 32 #define SCM_TEMPORAL_KEY_LENGTH 16 struct libipw_security { u16 active_key:2, enabled:1, unicast_uses_group:1, encrypt:1; u8 auth_mode; u8 encode_alg[WEP_KEYS]; u8 key_sizes[WEP_KEYS]; u8 keys[WEP_KEYS][SCM_KEY_LEN]; u8 level; u16 flags; } __packed; /* 802.11 data frame from AP ,-------------------------------------------------------------------. Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | |------|------|---------|---------|---------|------|---------|------| Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | | | tion | (BSSID) | | | ence | data | | `-------------------------------------------------------------------' Total: 28-2340 bytes */ #define BEACON_PROBE_SSID_ID_POSITION 12 struct libipw_hdr_1addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 payload[0]; } __packed; struct libipw_hdr_2addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 payload[0]; } __packed; struct libipw_hdr_3addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; __le16 seq_ctl; u8 payload[0]; } __packed; struct libipw_hdr_4addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; __le16 seq_ctl; u8 addr4[ETH_ALEN]; u8 payload[0]; } __packed; struct libipw_hdr_3addrqos { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; __le16 seq_ctl; u8 payload[0]; __le16 qos_ctl; } __packed; struct libipw_info_element { u8 id; u8 len; u8 data[0]; } __packed; /* * These are the data types that can make up management packets * u16 auth_algorithm; u16 auth_sequence; u16 beacon_interval; u16 capability; u8 current_ap[ETH_ALEN]; u16 listen_interval; struct { u16 association_id:14, reserved:2; } __packed; u32 time_stamp[2]; u16 reason; u16 status; */ struct libipw_auth { struct libipw_hdr_3addr header; __le16 algorithm; __le16 transaction; __le16 status; /* challenge */ struct libipw_info_element info_element[0]; } __packed; struct libipw_channel_switch { u8 id; u8 len; u8 mode; u8 channel; u8 count; } __packed; struct libipw_action { struct libipw_hdr_3addr header; u8 category; u8 action; union { struct libipw_action_exchange { u8 token; struct libipw_info_element info_element[0]; } exchange; struct libipw_channel_switch channel_switch; } format; } __packed; struct libipw_disassoc { struct libipw_hdr_3addr header; __le16 reason; } __packed; /* Alias deauth for disassoc */ #define libipw_deauth libipw_disassoc struct libipw_probe_request { struct libipw_hdr_3addr header; /* SSID, supported rates */ struct libipw_info_element info_element[0]; } __packed; struct libipw_probe_response { struct libipw_hdr_3addr header; __le32 time_stamp[2]; __le16 beacon_interval; __le16 capability; /* SSID, supported rates, FH params, DS params, * CF params, IBSS params, TIM (if beacon), RSN */ struct libipw_info_element info_element[0]; } __packed; /* Alias beacon for probe_response */ #define libipw_beacon libipw_probe_response struct libipw_assoc_request { struct libipw_hdr_3addr header; __le16 capability; __le16 listen_interval; /* SSID, supported rates, RSN */ struct libipw_info_element info_element[0]; } __packed; struct libipw_reassoc_request { struct libipw_hdr_3addr header; __le16 capability; __le16 listen_interval; u8 current_ap[ETH_ALEN]; struct libipw_info_element info_element[0]; } __packed; struct libipw_assoc_response { struct libipw_hdr_3addr header; __le16 capability; __le16 status; __le16 aid; /* supported rates */ struct libipw_info_element info_element[0]; } __packed; struct libipw_txb { u8 nr_frags; u8 encrypted; u8 rts_included; u8 reserved; u16 frag_size; u16 payload_size; struct sk_buff *fragments[0]; }; /* SWEEP TABLE ENTRIES NUMBER */ #define MAX_SWEEP_TAB_ENTRIES 42 #define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 /* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs * only use 8, and then use extended rates for the remaining supported * rates. Other APs, however, stick all of their supported rates on the * main rates information element... */ #define MAX_RATES_LENGTH ((u8)12) #define MAX_RATES_EX_LENGTH ((u8)16) #define MAX_NETWORK_COUNT 128 #define CRC_LENGTH 4U #define MAX_WPA_IE_LEN 64 #define NETWORK_HAS_OFDM (1<<1) #define NETWORK_HAS_CCK (1<<2) /* QoS structure */ #define NETWORK_HAS_QOS_PARAMETERS (1<<3) #define NETWORK_HAS_QOS_INFORMATION (1<<4) #define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \ NETWORK_HAS_QOS_INFORMATION) /* 802.11h */ #define NETWORK_HAS_POWER_CONSTRAINT (1<<5) #define NETWORK_HAS_CSA (1<<6) #define NETWORK_HAS_QUIET (1<<7) #define NETWORK_HAS_IBSS_DFS (1<<8) #define NETWORK_HAS_TPC_REPORT (1<<9) #define NETWORK_HAS_ERP_VALUE (1<<10) #define QOS_QUEUE_NUM 4 #define QOS_OUI_LEN 3 #define QOS_OUI_TYPE 2 #define QOS_ELEMENT_ID 221 #define QOS_OUI_INFO_SUB_TYPE 0 #define QOS_OUI_PARAM_SUB_TYPE 1 #define QOS_VERSION_1 1 #define QOS_AIFSN_MIN_VALUE 2 struct libipw_qos_information_element { u8 elementID; u8 length; u8 qui[QOS_OUI_LEN]; u8 qui_type; u8 qui_subtype; u8 version; u8 ac_info; } __packed; struct libipw_qos_ac_parameter { u8 aci_aifsn; u8 ecw_min_max; __le16 tx_op_limit; } __packed; struct libipw_qos_parameter_info { struct libipw_qos_information_element info_element; u8 reserved; struct libipw_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; } __packed; struct libipw_qos_parameters { __le16 cw_min[QOS_QUEUE_NUM]; __le16 cw_max[QOS_QUEUE_NUM]; u8 aifs[QOS_QUEUE_NUM]; u8 flag[QOS_QUEUE_NUM]; __le16 tx_op_limit[QOS_QUEUE_NUM]; } __packed; struct libipw_qos_data { struct libipw_qos_parameters parameters; int active; int supported; u8 param_count; u8 old_param_count; }; struct libipw_tim_parameters { u8 tim_count; u8 tim_period; } __packed; /*******************************************************/ struct libipw_tpc_report { u8 transmit_power; u8 link_margin; } __packed; struct libipw_channel_map { u8 channel; u8 map; } __packed; struct libipw_ibss_dfs { struct libipw_info_element ie; u8 owner[ETH_ALEN]; u8 recovery_interval; struct libipw_channel_map channel_map[0]; }; struct libipw_csa { u8 mode; u8 channel; u8 count; } __packed; struct libipw_quiet { u8 count; u8 period; u8 duration; u8 offset; } __packed; struct libipw_network { /* These entries are used to identify a unique network */ u8 bssid[ETH_ALEN]; u8 channel; /* Ensure null-terminated for any debug msgs */ u8 ssid[IW_ESSID_MAX_SIZE + 1]; u8 ssid_len; struct libipw_qos_data qos_data; /* These are network statistics */ struct libipw_rx_stats stats; u16 capability; u8 rates[MAX_RATES_LENGTH]; u8 rates_len; u8 rates_ex[MAX_RATES_EX_LENGTH]; u8 rates_ex_len; unsigned long last_scanned; u8 mode; u32 flags; u32 last_associate; u32 time_stamp[2]; u16 beacon_interval; u16 listen_interval; u16 atim_window; u8 erp_value; u8 wpa_ie[MAX_WPA_IE_LEN]; size_t wpa_ie_len; u8 rsn_ie[MAX_WPA_IE_LEN]; size_t rsn_ie_len; struct libipw_tim_parameters tim; /* 802.11h info */ /* Power Constraint - mandatory if spctrm mgmt required */ u8 power_constraint; /* TPC Report - mandatory if spctrm mgmt required */ struct libipw_tpc_report tpc_report; /* IBSS DFS - mandatory if spctrm mgmt required and IBSS * NOTE: This is variable length and so must be allocated dynamically */ struct libipw_ibss_dfs *ibss_dfs; /* Channel Switch Announcement - optional if spctrm mgmt required */ struct libipw_csa csa; /* Quiet - optional if spctrm mgmt required */ struct libipw_quiet quiet; struct list_head list; }; enum libipw_state { LIBIPW_UNINITIALIZED = 0, LIBIPW_INITIALIZED, LIBIPW_ASSOCIATING, LIBIPW_ASSOCIATED, LIBIPW_AUTHENTICATING, LIBIPW_AUTHENTICATED, LIBIPW_SHUTDOWN }; #define DEFAULT_MAX_SCAN_AGE (15 * HZ) #define DEFAULT_FTS 2346 #define CFG_LIBIPW_RESERVE_FCS (1<<0) #define CFG_LIBIPW_COMPUTE_FCS (1<<1) #define CFG_LIBIPW_RTS (1<<2) #define LIBIPW_24GHZ_MIN_CHANNEL 1 #define LIBIPW_24GHZ_MAX_CHANNEL 14 #define LIBIPW_24GHZ_CHANNELS (LIBIPW_24GHZ_MAX_CHANNEL - \ LIBIPW_24GHZ_MIN_CHANNEL + 1) #define LIBIPW_52GHZ_MIN_CHANNEL 34 #define LIBIPW_52GHZ_MAX_CHANNEL 165 #define LIBIPW_52GHZ_CHANNELS (LIBIPW_52GHZ_MAX_CHANNEL - \ LIBIPW_52GHZ_MIN_CHANNEL + 1) enum { LIBIPW_CH_PASSIVE_ONLY = (1 << 0), LIBIPW_CH_80211H_RULES = (1 << 1), LIBIPW_CH_B_ONLY = (1 << 2), LIBIPW_CH_NO_IBSS = (1 << 3), LIBIPW_CH_UNIFORM_SPREADING = (1 << 4), LIBIPW_CH_RADAR_DETECT = (1 << 5), LIBIPW_CH_INVALID = (1 << 6), }; struct libipw_channel { u32 freq; /* in MHz */ u8 channel; u8 flags; u8 max_power; /* in dBm */ }; struct libipw_geo { u8 name[4]; u8 bg_channels; u8 a_channels; struct libipw_channel bg[LIBIPW_24GHZ_CHANNELS]; struct libipw_channel a[LIBIPW_52GHZ_CHANNELS]; }; struct libipw_device { struct net_device *dev; struct wireless_dev wdev; struct libipw_security sec; /* Bookkeeping structures */ struct libipw_stats ieee_stats; struct libipw_geo geo; struct ieee80211_supported_band bg_band; struct ieee80211_supported_band a_band; /* Probe / Beacon management */ struct list_head network_free_list; struct list_head network_list; struct libipw_network *networks[MAX_NETWORK_COUNT]; int scans; int scan_age; int iw_mode; /* operating mode (IW_MODE_*) */ struct iw_spy_data spy_data; /* iwspy support */ spinlock_t lock; int tx_headroom; /* Set to size of any additional room needed at front * of allocated Tx SKBs */ u32 config; /* WEP and other encryption related settings at the device level */ int open_wep; /* Set to 1 to allow unencrypted frames */ /* If the host performs {en,de}cryption, then set to 1 */ int host_encrypt; int host_encrypt_msdu; int host_decrypt; /* host performs multicast decryption */ int host_mc_decrypt; /* host should strip IV and ICV from protected frames */ /* meaningful only when hardware decryption is being used */ int host_strip_iv_icv; int host_open_frag; int ieee802_1x; /* is IEEE 802.1X used */ /* WPA data */ int wpa_enabled; int drop_unencrypted; int privacy_invoked; size_t wpa_ie_len; u8 *wpa_ie; struct lib80211_crypt_info crypt_info; int bcrx_sta_key; /* use individual keys to override default keys even * with RX of broad/multicast frames */ /* Fragmentation structures */ struct libipw_frag_entry frag_cache[LIBIPW_FRAG_CACHE_LEN]; unsigned int frag_next_idx; u16 fts; /* Fragmentation Threshold */ u16 rts; /* RTS threshold */ /* Association info */ u8 bssid[ETH_ALEN]; enum libipw_state state; int mode; /* A, B, G */ int modulation; /* CCK, OFDM */ int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ int abg_true; /* ABG flag */ int perfect_rssi; int worst_rssi; u16 prev_seq_ctl; /* used to drop duplicate frames */ /* Callback functions */ void (*set_security) (struct net_device * dev, struct libipw_security * sec); netdev_tx_t (*hard_start_xmit) (struct libipw_txb * txb, struct net_device * dev, int pri); int (*is_queue_full) (struct net_device * dev, int pri); int (*handle_management) (struct net_device * dev, struct libipw_network * network, u16 type); int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb); /* Typical STA methods */ int (*handle_auth) (struct net_device * dev, struct libipw_auth * auth); int (*handle_deauth) (struct net_device * dev, struct libipw_deauth * auth); int (*handle_action) (struct net_device * dev, struct libipw_action * action, struct libipw_rx_stats * stats); int (*handle_disassoc) (struct net_device * dev, struct libipw_disassoc * assoc); int (*handle_beacon) (struct net_device * dev, struct libipw_beacon * beacon, struct libipw_network * network); int (*handle_probe_response) (struct net_device * dev, struct libipw_probe_response * resp, struct libipw_network * network); int (*handle_probe_request) (struct net_device * dev, struct libipw_probe_request * req, struct libipw_rx_stats * stats); int (*handle_assoc_response) (struct net_device * dev, struct libipw_assoc_response * resp, struct libipw_network * network); /* Typical AP methods */ int (*handle_assoc_request) (struct net_device * dev); int (*handle_reassoc_request) (struct net_device * dev, struct libipw_reassoc_request * req); /* This must be the last item so that it points to the data * allocated beyond this structure by alloc_libipw */ u8 priv[0]; }; #define IEEE_A (1<<0) #define IEEE_B (1<<1) #define IEEE_G (1<<2) #define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) static inline void *libipw_priv(struct net_device *dev) { return ((struct libipw_device *)netdev_priv(dev))->priv; } static inline int libipw_is_valid_mode(struct libipw_device *ieee, int mode) { /* * It is possible for both access points and our device to support * combinations of modes, so as long as there is one valid combination * of ap/device supported modes, then return success * */ if ((mode & IEEE_A) && (ieee->modulation & LIBIPW_OFDM_MODULATION) && (ieee->freq_band & LIBIPW_52GHZ_BAND)) return 1; if ((mode & IEEE_G) && (ieee->modulation & LIBIPW_OFDM_MODULATION) && (ieee->freq_band & LIBIPW_24GHZ_BAND)) return 1; if ((mode & IEEE_B) && (ieee->modulation & LIBIPW_CCK_MODULATION) && (ieee->freq_band & LIBIPW_24GHZ_BAND)) return 1; return 0; } static inline int libipw_get_hdrlen(u16 fc) { int hdrlen = LIBIPW_3ADDR_LEN; u16 stype = WLAN_FC_GET_STYPE(fc); switch (WLAN_FC_GET_TYPE(fc)) { case IEEE80211_FTYPE_DATA: if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) hdrlen = LIBIPW_4ADDR_LEN; if (stype & IEEE80211_STYPE_QOS_DATA) hdrlen += 2; break; case IEEE80211_FTYPE_CTL: switch (WLAN_FC_GET_STYPE(fc)) { case IEEE80211_STYPE_CTS: case IEEE80211_STYPE_ACK: hdrlen = LIBIPW_1ADDR_LEN; break; default: hdrlen = LIBIPW_2ADDR_LEN; break; } break; } return hdrlen; } static inline u8 *libipw_get_payload(struct ieee80211_hdr *hdr) { switch (libipw_get_hdrlen(le16_to_cpu(hdr->frame_control))) { case LIBIPW_1ADDR_LEN: return ((struct libipw_hdr_1addr *)hdr)->payload; case LIBIPW_2ADDR_LEN: return ((struct libipw_hdr_2addr *)hdr)->payload; case LIBIPW_3ADDR_LEN: return ((struct libipw_hdr_3addr *)hdr)->payload; case LIBIPW_4ADDR_LEN: return ((struct libipw_hdr_4addr *)hdr)->payload; } return NULL; } static inline int libipw_is_ofdm_rate(u8 rate) { switch (rate & ~LIBIPW_BASIC_RATE_MASK) { case LIBIPW_OFDM_RATE_6MB: case LIBIPW_OFDM_RATE_9MB: case LIBIPW_OFDM_RATE_12MB: case LIBIPW_OFDM_RATE_18MB: case LIBIPW_OFDM_RATE_24MB: case LIBIPW_OFDM_RATE_36MB: case LIBIPW_OFDM_RATE_48MB: case LIBIPW_OFDM_RATE_54MB: return 1; } return 0; } static inline int libipw_is_cck_rate(u8 rate) { switch (rate & ~LIBIPW_BASIC_RATE_MASK) { case LIBIPW_CCK_RATE_1MB: case LIBIPW_CCK_RATE_2MB: case LIBIPW_CCK_RATE_5MB: case LIBIPW_CCK_RATE_11MB: return 1; } return 0; } /* libipw.c */ extern void free_libipw(struct net_device *dev, int monitor); extern struct net_device *alloc_libipw(int sizeof_priv, int monitor); extern int libipw_change_mtu(struct net_device *dev, int new_mtu); extern void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); extern int libipw_set_encryption(struct libipw_device *ieee); /* libipw_tx.c */ extern netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev); extern void libipw_txb_free(struct libipw_txb *); /* libipw_rx.c */ extern void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb, struct libipw_rx_stats *stats); extern int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, struct libipw_rx_stats *rx_stats); /* make sure to set stats->len */ extern void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header, struct libipw_rx_stats *stats); extern void libipw_network_reset(struct libipw_network *network); /* libipw_geo.c */ extern const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee); extern int libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo); extern int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel); extern int libipw_channel_to_index(struct libipw_device *ieee, u8 channel); extern u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); extern u8 libipw_get_channel_flags(struct libipw_device *ieee, u8 channel); extern const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee, u8 channel); extern u32 libipw_channel_to_freq(struct libipw_device * ieee, u8 channel); /* libipw_wx.c */ extern int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *key); extern int libipw_wx_set_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *key); extern int libipw_wx_get_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *key); extern int libipw_wx_set_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); extern int libipw_wx_get_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); static inline void libipw_increment_scans(struct libipw_device *ieee) { ieee->scans++; } static inline int libipw_get_scans(struct libipw_device *ieee) { return ieee->scans; } #endif /* LIBIPW_H */ compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/libipw_geo.c0000644000175000017500000001255212026211315024163 0ustar mcgrofmcgrof/****************************************************************************** Copyright(c) 2005 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libipw.h" int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel) { int i; /* Driver needs to initialize the geography map before using * these helper functions */ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) return 0; if (ieee->freq_band & LIBIPW_24GHZ_BAND) for (i = 0; i < ieee->geo.bg_channels; i++) /* NOTE: If G mode is currently supported but * this is a B only channel, we don't see it * as valid. */ if ((ieee->geo.bg[i].channel == channel) && !(ieee->geo.bg[i].flags & LIBIPW_CH_INVALID) && (!(ieee->mode & IEEE_G) || !(ieee->geo.bg[i].flags & LIBIPW_CH_B_ONLY))) return LIBIPW_24GHZ_BAND; if (ieee->freq_band & LIBIPW_52GHZ_BAND) for (i = 0; i < ieee->geo.a_channels; i++) if ((ieee->geo.a[i].channel == channel) && !(ieee->geo.a[i].flags & LIBIPW_CH_INVALID)) return LIBIPW_52GHZ_BAND; return 0; } int libipw_channel_to_index(struct libipw_device *ieee, u8 channel) { int i; /* Driver needs to initialize the geography map before using * these helper functions */ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) return -1; if (ieee->freq_band & LIBIPW_24GHZ_BAND) for (i = 0; i < ieee->geo.bg_channels; i++) if (ieee->geo.bg[i].channel == channel) return i; if (ieee->freq_band & LIBIPW_52GHZ_BAND) for (i = 0; i < ieee->geo.a_channels; i++) if (ieee->geo.a[i].channel == channel) return i; return -1; } u32 libipw_channel_to_freq(struct libipw_device * ieee, u8 channel) { const struct libipw_channel * ch; /* Driver needs to initialize the geography map before using * these helper functions */ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) return 0; ch = libipw_get_channel(ieee, channel); if (!ch->channel) return 0; return ch->freq; } u8 libipw_freq_to_channel(struct libipw_device * ieee, u32 freq) { int i; /* Driver needs to initialize the geography map before using * these helper functions */ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) return 0; freq /= 100000; if (ieee->freq_band & LIBIPW_24GHZ_BAND) for (i = 0; i < ieee->geo.bg_channels; i++) if (ieee->geo.bg[i].freq == freq) return ieee->geo.bg[i].channel; if (ieee->freq_band & LIBIPW_52GHZ_BAND) for (i = 0; i < ieee->geo.a_channels; i++) if (ieee->geo.a[i].freq == freq) return ieee->geo.a[i].channel; return 0; } int libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo) { memcpy(ieee->geo.name, geo->name, 3); ieee->geo.name[3] = '\0'; ieee->geo.bg_channels = geo->bg_channels; ieee->geo.a_channels = geo->a_channels; memcpy(ieee->geo.bg, geo->bg, geo->bg_channels * sizeof(struct libipw_channel)); memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels * sizeof(struct libipw_channel)); return 0; } const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee) { return &ieee->geo; } u8 libipw_get_channel_flags(struct libipw_device * ieee, u8 channel) { int index = libipw_channel_to_index(ieee, channel); if (index == -1) return LIBIPW_CH_INVALID; if (channel <= LIBIPW_24GHZ_CHANNELS) return ieee->geo.bg[index].flags; return ieee->geo.a[index].flags; } static const struct libipw_channel bad_channel = { .channel = 0, .flags = LIBIPW_CH_INVALID, .max_power = 0, }; const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee, u8 channel) { int index = libipw_channel_to_index(ieee, channel); if (index == -1) return &bad_channel; if (channel <= LIBIPW_24GHZ_CHANNELS) return &ieee->geo.bg[index]; return &ieee->geo.a[index]; } EXPORT_SYMBOL(libipw_get_channel); EXPORT_SYMBOL(libipw_get_channel_flags); EXPORT_SYMBOL(libipw_is_valid_channel); EXPORT_SYMBOL(libipw_freq_to_channel); EXPORT_SYMBOL(libipw_channel_to_freq); EXPORT_SYMBOL(libipw_channel_to_index); EXPORT_SYMBOL(libipw_set_geo); EXPORT_SYMBOL(libipw_get_geo); compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/ipw.h0000644000175000017500000000105412026211315022642 0ustar mcgrofmcgrof/* * Intel Pro/Wireless 2100, 2200BG, 2915ABG network connection driver * * Copyright 2012 Stanislav Yakovlev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef __IPW_H__ #define __IPW_H__ #include static const u32 ipw_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, }; #endif compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/ipw2200.h0000644000175000017500000016267512026211315023167 0ustar mcgrofmcgrof/****************************************************************************** Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ #ifndef __ipw2200_h__ #define __ipw2200_h__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRV_NAME "ipw2200" #include #include "libipw.h" /* Authentication and Association States */ enum connection_manager_assoc_states { CMAS_INIT = 0, CMAS_TX_AUTH_SEQ_1, CMAS_RX_AUTH_SEQ_2, CMAS_AUTH_SEQ_1_PASS, CMAS_AUTH_SEQ_1_FAIL, CMAS_TX_AUTH_SEQ_3, CMAS_RX_AUTH_SEQ_4, CMAS_AUTH_SEQ_2_PASS, CMAS_AUTH_SEQ_2_FAIL, CMAS_AUTHENTICATED, CMAS_TX_ASSOC, CMAS_RX_ASSOC_RESP, CMAS_ASSOCIATED, CMAS_LAST }; #define IPW_WAIT (1<<0) #define IPW_QUIET (1<<1) #define IPW_ROAMING (1<<2) #define IPW_POWER_MODE_CAM 0x00 //(always on) #define IPW_POWER_INDEX_1 0x01 #define IPW_POWER_INDEX_2 0x02 #define IPW_POWER_INDEX_3 0x03 #define IPW_POWER_INDEX_4 0x04 #define IPW_POWER_INDEX_5 0x05 #define IPW_POWER_AC 0x06 #define IPW_POWER_BATTERY 0x07 #define IPW_POWER_LIMIT 0x07 #define IPW_POWER_MASK 0x0F #define IPW_POWER_ENABLED 0x10 #define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK) #define IPW_CMD_HOST_COMPLETE 2 #define IPW_CMD_POWER_DOWN 4 #define IPW_CMD_SYSTEM_CONFIG 6 #define IPW_CMD_MULTICAST_ADDRESS 7 #define IPW_CMD_SSID 8 #define IPW_CMD_ADAPTER_ADDRESS 11 #define IPW_CMD_PORT_TYPE 12 #define IPW_CMD_RTS_THRESHOLD 15 #define IPW_CMD_FRAG_THRESHOLD 16 #define IPW_CMD_POWER_MODE 17 #define IPW_CMD_WEP_KEY 18 #define IPW_CMD_TGI_TX_KEY 19 #define IPW_CMD_SCAN_REQUEST 20 #define IPW_CMD_ASSOCIATE 21 #define IPW_CMD_SUPPORTED_RATES 22 #define IPW_CMD_SCAN_ABORT 23 #define IPW_CMD_TX_FLUSH 24 #define IPW_CMD_QOS_PARAMETERS 25 #define IPW_CMD_SCAN_REQUEST_EXT 26 #define IPW_CMD_DINO_CONFIG 30 #define IPW_CMD_RSN_CAPABILITIES 31 #define IPW_CMD_RX_KEY 32 #define IPW_CMD_CARD_DISABLE 33 #define IPW_CMD_SEED_NUMBER 34 #define IPW_CMD_TX_POWER 35 #define IPW_CMD_COUNTRY_INFO 36 #define IPW_CMD_AIRONET_INFO 37 #define IPW_CMD_AP_TX_POWER 38 #define IPW_CMD_CCKM_INFO 39 #define IPW_CMD_CCX_VER_INFO 40 #define IPW_CMD_SET_CALIBRATION 41 #define IPW_CMD_SENSITIVITY_CALIB 42 #define IPW_CMD_RETRY_LIMIT 51 #define IPW_CMD_IPW_PRE_POWER_DOWN 58 #define IPW_CMD_VAP_BEACON_TEMPLATE 60 #define IPW_CMD_VAP_DTIM_PERIOD 61 #define IPW_CMD_EXT_SUPPORTED_RATES 62 #define IPW_CMD_VAP_LOCAL_TX_PWR_CONSTRAINT 63 #define IPW_CMD_VAP_QUIET_INTERVALS 64 #define IPW_CMD_VAP_CHANNEL_SWITCH 65 #define IPW_CMD_VAP_MANDATORY_CHANNELS 66 #define IPW_CMD_VAP_CELL_PWR_LIMIT 67 #define IPW_CMD_VAP_CF_PARAM_SET 68 #define IPW_CMD_VAP_SET_BEACONING_STATE 69 #define IPW_CMD_MEASUREMENT 80 #define IPW_CMD_POWER_CAPABILITY 81 #define IPW_CMD_SUPPORTED_CHANNELS 82 #define IPW_CMD_TPC_REPORT 83 #define IPW_CMD_WME_INFO 84 #define IPW_CMD_PRODUCTION_COMMAND 85 #define IPW_CMD_LINKSYS_EOU_INFO 90 #define RFD_SIZE 4 #define NUM_TFD_CHUNKS 6 #define TX_QUEUE_SIZE 32 #define RX_QUEUE_SIZE 32 #define DINO_CMD_WEP_KEY 0x08 #define DINO_CMD_TX 0x0B #define DCT_ANTENNA_A 0x01 #define DCT_ANTENNA_B 0x02 #define IPW_A_MODE 0 #define IPW_B_MODE 1 #define IPW_G_MODE 2 /* * TX Queue Flag Definitions */ /* tx wep key definition */ #define DCT_WEP_KEY_NOT_IMMIDIATE 0x00 #define DCT_WEP_KEY_64Bit 0x40 #define DCT_WEP_KEY_128Bit 0x80 #define DCT_WEP_KEY_128bitIV 0xC0 #define DCT_WEP_KEY_SIZE_MASK 0xC0 #define DCT_WEP_KEY_INDEX_MASK 0x0F #define DCT_WEP_INDEX_USE_IMMEDIATE 0x20 /* abort attempt if mgmt frame is rx'd */ #define DCT_FLAG_ABORT_MGMT 0x01 /* require CTS */ #define DCT_FLAG_CTS_REQUIRED 0x02 /* use short preamble */ #define DCT_FLAG_LONG_PREAMBLE 0x00 #define DCT_FLAG_SHORT_PREAMBLE 0x04 /* RTS/CTS first */ #define DCT_FLAG_RTS_REQD 0x08 /* dont calculate duration field */ #define DCT_FLAG_DUR_SET 0x10 /* even if MAC WEP set (allows pre-encrypt) */ #define DCT_FLAG_NO_WEP 0x20 /* overwrite TSF field */ #define DCT_FLAG_TSF_REQD 0x40 /* ACK rx is expected to follow */ #define DCT_FLAG_ACK_REQD 0x80 /* TX flags extension */ #define DCT_FLAG_EXT_MODE_CCK 0x01 #define DCT_FLAG_EXT_MODE_OFDM 0x00 #define DCT_FLAG_EXT_SECURITY_WEP 0x00 #define DCT_FLAG_EXT_SECURITY_NO DCT_FLAG_EXT_SECURITY_WEP #define DCT_FLAG_EXT_SECURITY_CKIP 0x04 #define DCT_FLAG_EXT_SECURITY_CCM 0x08 #define DCT_FLAG_EXT_SECURITY_TKIP 0x0C #define DCT_FLAG_EXT_SECURITY_MASK 0x0C #define DCT_FLAG_EXT_QOS_ENABLED 0x10 #define DCT_FLAG_EXT_HC_NO_SIFS_PIFS 0x00 #define DCT_FLAG_EXT_HC_SIFS 0x20 #define DCT_FLAG_EXT_HC_PIFS 0x40 #define TX_RX_TYPE_MASK 0xFF #define TX_FRAME_TYPE 0x00 #define TX_HOST_COMMAND_TYPE 0x01 #define RX_FRAME_TYPE 0x09 #define RX_HOST_NOTIFICATION_TYPE 0x03 #define RX_HOST_CMD_RESPONSE_TYPE 0x04 #define RX_TX_FRAME_RESPONSE_TYPE 0x05 #define TFD_NEED_IRQ_MASK 0x04 #define HOST_CMD_DINO_CONFIG 30 #define HOST_NOTIFICATION_STATUS_ASSOCIATED 10 #define HOST_NOTIFICATION_STATUS_AUTHENTICATE 11 #define HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT 12 #define HOST_NOTIFICATION_STATUS_SCAN_COMPLETED 13 #define HOST_NOTIFICATION_STATUS_FRAG_LENGTH 14 #define HOST_NOTIFICATION_STATUS_LINK_DETERIORATION 15 #define HOST_NOTIFICATION_DINO_CONFIG_RESPONSE 16 #define HOST_NOTIFICATION_STATUS_BEACON_STATE 17 #define HOST_NOTIFICATION_STATUS_TGI_TX_KEY 18 #define HOST_NOTIFICATION_TX_STATUS 19 #define HOST_NOTIFICATION_CALIB_KEEP_RESULTS 20 #define HOST_NOTIFICATION_MEASUREMENT_STARTED 21 #define HOST_NOTIFICATION_MEASUREMENT_ENDED 22 #define HOST_NOTIFICATION_CHANNEL_SWITCHED 23 #define HOST_NOTIFICATION_RX_DURING_QUIET_PERIOD 24 #define HOST_NOTIFICATION_NOISE_STATS 25 #define HOST_NOTIFICATION_S36_MEASUREMENT_ACCEPTED 30 #define HOST_NOTIFICATION_S36_MEASUREMENT_REFUSED 31 #define HOST_NOTIFICATION_STATUS_BEACON_MISSING 1 #define IPW_MB_SCAN_CANCEL_THRESHOLD 3 #define IPW_MB_ROAMING_THRESHOLD_MIN 1 #define IPW_MB_ROAMING_THRESHOLD_DEFAULT 8 #define IPW_MB_ROAMING_THRESHOLD_MAX 30 #define IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT 3*IPW_MB_ROAMING_THRESHOLD_DEFAULT #define IPW_REAL_RATE_RX_PACKET_THRESHOLD 300 #define MACADRR_BYTE_LEN 6 #define DCR_TYPE_AP 0x01 #define DCR_TYPE_WLAP 0x02 #define DCR_TYPE_MU_ESS 0x03 #define DCR_TYPE_MU_IBSS 0x04 #define DCR_TYPE_MU_PIBSS 0x05 #define DCR_TYPE_SNIFFER 0x06 #define DCR_TYPE_MU_BSS DCR_TYPE_MU_ESS /* QoS definitions */ #define CW_MIN_OFDM 15 #define CW_MAX_OFDM 1023 #define CW_MIN_CCK 31 #define CW_MAX_CCK 1023 #define QOS_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) #define QOS_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) #define QOS_TX2_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1) #define QOS_TX3_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/4 - 1) #define QOS_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) #define QOS_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) #define QOS_TX2_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1) #define QOS_TX3_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/4 - 1) #define QOS_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) #define QOS_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) #define QOS_TX2_CW_MAX_OFDM cpu_to_le16(CW_MIN_OFDM) #define QOS_TX3_CW_MAX_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1) #define QOS_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) #define QOS_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) #define QOS_TX2_CW_MAX_CCK cpu_to_le16(CW_MIN_CCK) #define QOS_TX3_CW_MAX_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1) #define QOS_TX0_AIFS (3 - QOS_AIFSN_MIN_VALUE) #define QOS_TX1_AIFS (7 - QOS_AIFSN_MIN_VALUE) #define QOS_TX2_AIFS (2 - QOS_AIFSN_MIN_VALUE) #define QOS_TX3_AIFS (2 - QOS_AIFSN_MIN_VALUE) #define QOS_TX0_ACM 0 #define QOS_TX1_ACM 0 #define QOS_TX2_ACM 0 #define QOS_TX3_ACM 0 #define QOS_TX0_TXOP_LIMIT_CCK 0 #define QOS_TX1_TXOP_LIMIT_CCK 0 #define QOS_TX2_TXOP_LIMIT_CCK cpu_to_le16(6016) #define QOS_TX3_TXOP_LIMIT_CCK cpu_to_le16(3264) #define QOS_TX0_TXOP_LIMIT_OFDM 0 #define QOS_TX1_TXOP_LIMIT_OFDM 0 #define QOS_TX2_TXOP_LIMIT_OFDM cpu_to_le16(3008) #define QOS_TX3_TXOP_LIMIT_OFDM cpu_to_le16(1504) #define DEF_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) #define DEF_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) #define DEF_TX2_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) #define DEF_TX3_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) #define DEF_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) #define DEF_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) #define DEF_TX2_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) #define DEF_TX3_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) #define DEF_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) #define DEF_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) #define DEF_TX2_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) #define DEF_TX3_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) #define DEF_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) #define DEF_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) #define DEF_TX2_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) #define DEF_TX3_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) #define DEF_TX0_AIFS 0 #define DEF_TX1_AIFS 0 #define DEF_TX2_AIFS 0 #define DEF_TX3_AIFS 0 #define DEF_TX0_ACM 0 #define DEF_TX1_ACM 0 #define DEF_TX2_ACM 0 #define DEF_TX3_ACM 0 #define DEF_TX0_TXOP_LIMIT_CCK 0 #define DEF_TX1_TXOP_LIMIT_CCK 0 #define DEF_TX2_TXOP_LIMIT_CCK 0 #define DEF_TX3_TXOP_LIMIT_CCK 0 #define DEF_TX0_TXOP_LIMIT_OFDM 0 #define DEF_TX1_TXOP_LIMIT_OFDM 0 #define DEF_TX2_TXOP_LIMIT_OFDM 0 #define DEF_TX3_TXOP_LIMIT_OFDM 0 #define QOS_QOS_SETS 3 #define QOS_PARAM_SET_ACTIVE 0 #define QOS_PARAM_SET_DEF_CCK 1 #define QOS_PARAM_SET_DEF_OFDM 2 #define CTRL_QOS_NO_ACK (0x0020) #define IPW_TX_QUEUE_1 1 #define IPW_TX_QUEUE_2 2 #define IPW_TX_QUEUE_3 3 #define IPW_TX_QUEUE_4 4 /* QoS sturctures */ struct ipw_qos_info { int qos_enable; struct libipw_qos_parameters *def_qos_parm_OFDM; struct libipw_qos_parameters *def_qos_parm_CCK; u32 burst_duration_CCK; u32 burst_duration_OFDM; u16 qos_no_ack_mask; int burst_enable; }; /**************************************************************/ /** * Generic queue structure * * Contains common data for Rx and Tx queues */ struct clx2_queue { int n_bd; /**< number of BDs in this queue */ int first_empty; /**< 1-st empty entry (index) */ int last_used; /**< last used entry (index) */ u32 reg_w; /**< 'write' reg (queue head), addr in domain 1 */ u32 reg_r; /**< 'read' reg (queue tail), addr in domain 1 */ dma_addr_t dma_addr; /**< physical addr for BD's */ int low_mark; /**< low watermark, resume queue if free space more than this */ int high_mark; /**< high watermark, stop queue if free space less than this */ } __packed; /* XXX */ struct machdr32 { __le16 frame_ctl; __le16 duration; // watch out for endians! u8 addr1[MACADRR_BYTE_LEN]; u8 addr2[MACADRR_BYTE_LEN]; u8 addr3[MACADRR_BYTE_LEN]; __le16 seq_ctrl; // more endians! u8 addr4[MACADRR_BYTE_LEN]; __le16 qos_ctrl; } __packed; struct machdr30 { __le16 frame_ctl; __le16 duration; // watch out for endians! u8 addr1[MACADRR_BYTE_LEN]; u8 addr2[MACADRR_BYTE_LEN]; u8 addr3[MACADRR_BYTE_LEN]; __le16 seq_ctrl; // more endians! u8 addr4[MACADRR_BYTE_LEN]; } __packed; struct machdr26 { __le16 frame_ctl; __le16 duration; // watch out for endians! u8 addr1[MACADRR_BYTE_LEN]; u8 addr2[MACADRR_BYTE_LEN]; u8 addr3[MACADRR_BYTE_LEN]; __le16 seq_ctrl; // more endians! __le16 qos_ctrl; } __packed; struct machdr24 { __le16 frame_ctl; __le16 duration; // watch out for endians! u8 addr1[MACADRR_BYTE_LEN]; u8 addr2[MACADRR_BYTE_LEN]; u8 addr3[MACADRR_BYTE_LEN]; __le16 seq_ctrl; // more endians! } __packed; // TX TFD with 32 byte MAC Header struct tx_tfd_32 { struct machdr32 mchdr; // 32 __le32 uivplaceholder[2]; // 8 } __packed; // TX TFD with 30 byte MAC Header struct tx_tfd_30 { struct machdr30 mchdr; // 30 u8 reserved[2]; // 2 __le32 uivplaceholder[2]; // 8 } __packed; // tx tfd with 26 byte mac header struct tx_tfd_26 { struct machdr26 mchdr; // 26 u8 reserved1[2]; // 2 __le32 uivplaceholder[2]; // 8 u8 reserved2[4]; // 4 } __packed; // tx tfd with 24 byte mac header struct tx_tfd_24 { struct machdr24 mchdr; // 24 __le32 uivplaceholder[2]; // 8 u8 reserved[8]; // 8 } __packed; #define DCT_WEP_KEY_FIELD_LENGTH 16 struct tfd_command { u8 index; u8 length; __le16 reserved; u8 payload[0]; } __packed; struct tfd_data { /* Header */ __le32 work_area_ptr; u8 station_number; /* 0 for BSS */ u8 reserved1; __le16 reserved2; /* Tx Parameters */ u8 cmd_id; u8 seq_num; __le16 len; u8 priority; u8 tx_flags; u8 tx_flags_ext; u8 key_index; u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH]; u8 rate; u8 antenna; __le16 next_packet_duration; __le16 next_frag_len; __le16 back_off_counter; //////txop; u8 retrylimit; __le16 cwcurrent; u8 reserved3; /* 802.11 MAC Header */ union { struct tx_tfd_24 tfd_24; struct tx_tfd_26 tfd_26; struct tx_tfd_30 tfd_30; struct tx_tfd_32 tfd_32; } tfd; /* Payload DMA info */ __le32 num_chunks; __le32 chunk_ptr[NUM_TFD_CHUNKS]; __le16 chunk_len[NUM_TFD_CHUNKS]; } __packed; struct txrx_control_flags { u8 message_type; u8 rx_seq_num; u8 control_bits; u8 reserved; } __packed; #define TFD_SIZE 128 #define TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH (TFD_SIZE - sizeof(struct txrx_control_flags)) struct tfd_frame { struct txrx_control_flags control_flags; union { struct tfd_data data; struct tfd_command cmd; u8 raw[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH]; } u; } __packed; typedef void destructor_func(const void *); /** * Tx Queue for DMA. Queue consists of circular buffer of * BD's and required locking structures. */ struct clx2_tx_queue { struct clx2_queue q; struct tfd_frame *bd; struct libipw_txb **txb; }; /* * RX related structures and functions */ #define RX_FREE_BUFFERS 32 #define RX_LOW_WATERMARK 8 #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 // Used for passing to driver number of successes and failures per rate struct rate_histogram { union { __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; } success; union { __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; } failed; } __packed; /* statistics command response */ struct ipw_cmd_stats { u8 cmd_id; u8 seq_num; __le16 good_sfd; __le16 bad_plcp; __le16 wrong_bssid; __le16 valid_mpdu; __le16 bad_mac_header; __le16 reserved_frame_types; __le16 rx_ina; __le16 bad_crc32; __le16 invalid_cts; __le16 invalid_acks; __le16 long_distance_ina_fina; __le16 dsp_silence_unreachable; __le16 accumulated_rssi; __le16 rx_ovfl_frame_tossed; __le16 rssi_silence_threshold; __le16 rx_ovfl_frame_supplied; __le16 last_rx_frame_signal; __le16 last_rx_frame_noise; __le16 rx_autodetec_no_ofdm; __le16 rx_autodetec_no_barker; __le16 reserved; } __packed; struct notif_channel_result { u8 channel_num; struct ipw_cmd_stats stats; u8 uReserved; } __packed; #define SCAN_COMPLETED_STATUS_COMPLETE 1 #define SCAN_COMPLETED_STATUS_ABORTED 2 struct notif_scan_complete { u8 scan_type; u8 num_channels; u8 status; u8 reserved; } __packed; struct notif_frag_length { __le16 frag_length; __le16 reserved; } __packed; struct notif_beacon_state { __le32 state; __le32 number; } __packed; struct notif_tgi_tx_key { u8 key_state; u8 security_type; u8 station_index; u8 reserved; } __packed; #define SILENCE_OVER_THRESH (1) #define SILENCE_UNDER_THRESH (2) struct notif_link_deterioration { struct ipw_cmd_stats stats; u8 rate; u8 modulation; struct rate_histogram histogram; u8 silence_notification_type; /* SILENCE_OVER/UNDER_THRESH */ __le16 silence_count; } __packed; struct notif_association { u8 state; } __packed; struct notif_authenticate { u8 state; struct machdr24 addr; __le16 status; } __packed; struct notif_calibration { u8 data[104]; } __packed; struct notif_noise { __le32 value; } __packed; struct ipw_rx_notification { u8 reserved[8]; u8 subtype; u8 flags; __le16 size; union { struct notif_association assoc; struct notif_authenticate auth; struct notif_channel_result channel_result; struct notif_scan_complete scan_complete; struct notif_frag_length frag_len; struct notif_beacon_state beacon_state; struct notif_tgi_tx_key tgi_tx_key; struct notif_link_deterioration link_deterioration; struct notif_calibration calibration; struct notif_noise noise; u8 raw[0]; } u; } __packed; struct ipw_rx_frame { __le32 reserved1; u8 parent_tsf[4]; // fw_use[0] is boolean for OUR_TSF_IS_GREATER u8 received_channel; // The channel that this frame was received on. // Note that for .11b this does not have to be // the same as the channel that it was sent. // Filled by LMAC u8 frameStatus; u8 rate; u8 rssi; u8 agc; u8 rssi_dbm; __le16 signal; __le16 noise; u8 antennaAndPhy; u8 control; // control bit should be on in bg u8 rtscts_rate; // rate of rts or cts (in rts cts sequence rate // is identical) u8 rtscts_seen; // 0x1 RTS seen ; 0x2 CTS seen __le16 length; u8 data[0]; } __packed; struct ipw_rx_header { u8 message_type; u8 rx_seq_num; u8 control_bits; u8 reserved; } __packed; struct ipw_rx_packet { struct ipw_rx_header header; union { struct ipw_rx_frame frame; struct ipw_rx_notification notification; } u; } __packed; #define IPW_RX_NOTIFICATION_SIZE sizeof(struct ipw_rx_header) + 12 #define IPW_RX_FRAME_SIZE (unsigned int)(sizeof(struct ipw_rx_header) + \ sizeof(struct ipw_rx_frame)) struct ipw_rx_mem_buffer { dma_addr_t dma_addr; struct sk_buff *skb; struct list_head list; }; /* Not transferred over network, so not __packed */ struct ipw_rx_queue { struct ipw_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; struct ipw_rx_mem_buffer *queue[RX_QUEUE_SIZE]; u32 processed; /* Internal index to last handled Rx packet */ u32 read; /* Shared index to newest available Rx buffer */ u32 write; /* Shared index to oldest written Rx packet */ u32 free_count; /* Number of pre-allocated buffers in rx_free */ /* Each of these lists is used as a FIFO for ipw_rx_mem_buffers */ struct list_head rx_free; /* Own an SKBs */ struct list_head rx_used; /* No SKB allocated */ spinlock_t lock; }; /* Not transferred over network, so not __packed */ struct alive_command_responce { u8 alive_command; u8 sequence_number; __le16 software_revision; u8 device_identifier; u8 reserved1[5]; __le16 reserved2; __le16 reserved3; __le16 clock_settle_time; __le16 powerup_settle_time; __le16 reserved4; u8 time_stamp[5]; /* month, day, year, hours, minutes */ u8 ucode_valid; } __packed; #define IPW_MAX_RATES 12 struct ipw_rates { u8 num_rates; u8 rates[IPW_MAX_RATES]; } __packed; struct command_block { unsigned int control; u32 source_addr; u32 dest_addr; unsigned int status; } __packed; #define CB_NUMBER_OF_ELEMENTS_SMALL 64 struct fw_image_desc { unsigned long last_cb_index; unsigned long current_cb_index; struct command_block cb_list[CB_NUMBER_OF_ELEMENTS_SMALL]; void *v_addr; unsigned long p_addr; unsigned long len; }; struct ipw_sys_config { u8 bt_coexistence; u8 reserved1; u8 answer_broadcast_ssid_probe; u8 accept_all_data_frames; u8 accept_non_directed_frames; u8 exclude_unicast_unencrypted; u8 disable_unicast_decryption; u8 exclude_multicast_unencrypted; u8 disable_multicast_decryption; u8 antenna_diversity; u8 pass_crc_to_host; u8 dot11g_auto_detection; u8 enable_cts_to_self; u8 enable_multicast_filtering; u8 bt_coexist_collision_thr; u8 silence_threshold; u8 accept_all_mgmt_bcpr; u8 accept_all_mgmt_frames; u8 pass_noise_stats_to_host; u8 reserved3; } __packed; struct ipw_multicast_addr { u8 num_of_multicast_addresses; u8 reserved[3]; u8 mac1[6]; u8 mac2[6]; u8 mac3[6]; u8 mac4[6]; } __packed; #define DCW_WEP_KEY_INDEX_MASK 0x03 /* bits [0:1] */ #define DCW_WEP_KEY_SEC_TYPE_MASK 0x30 /* bits [4:5] */ #define DCW_WEP_KEY_SEC_TYPE_WEP 0x00 #define DCW_WEP_KEY_SEC_TYPE_CCM 0x20 #define DCW_WEP_KEY_SEC_TYPE_TKIP 0x30 #define DCW_WEP_KEY_INVALID_SIZE 0x00 /* 0 = Invalid key */ #define DCW_WEP_KEY64Bit_SIZE 0x05 /* 64-bit encryption */ #define DCW_WEP_KEY128Bit_SIZE 0x0D /* 128-bit encryption */ #define DCW_CCM_KEY128Bit_SIZE 0x10 /* 128-bit key */ //#define DCW_WEP_KEY128BitIV_SIZE 0x10 /* 128-bit key and 128-bit IV */ struct ipw_wep_key { u8 cmd_id; u8 seq_num; u8 key_index; u8 key_size; u8 key[16]; } __packed; struct ipw_tgi_tx_key { u8 key_id; u8 security_type; u8 station_index; u8 flags; u8 key[16]; __le32 tx_counter[2]; } __packed; #define IPW_SCAN_CHANNELS 54 struct ipw_scan_request { u8 scan_type; __le16 dwell_time; u8 channels_list[IPW_SCAN_CHANNELS]; u8 channels_reserved[3]; } __packed; enum { IPW_SCAN_PASSIVE_TILL_FIRST_BEACON_SCAN = 0, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN, IPW_SCAN_ACTIVE_DIRECT_SCAN, IPW_SCAN_ACTIVE_BROADCAST_SCAN, IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN, IPW_SCAN_TYPES }; struct ipw_scan_request_ext { __le32 full_scan_index; u8 channels_list[IPW_SCAN_CHANNELS]; u8 scan_type[IPW_SCAN_CHANNELS / 2]; u8 reserved; __le16 dwell_time[IPW_SCAN_TYPES]; } __packed; static inline u8 ipw_get_scan_type(struct ipw_scan_request_ext *scan, u8 index) { if (index % 2) return scan->scan_type[index / 2] & 0x0F; else return (scan->scan_type[index / 2] & 0xF0) >> 4; } static inline void ipw_set_scan_type(struct ipw_scan_request_ext *scan, u8 index, u8 scan_type) { if (index % 2) scan->scan_type[index / 2] = (scan->scan_type[index / 2] & 0xF0) | (scan_type & 0x0F); else scan->scan_type[index / 2] = (scan->scan_type[index / 2] & 0x0F) | ((scan_type & 0x0F) << 4); } struct ipw_associate { u8 channel; #ifdef __LITTLE_ENDIAN_BITFIELD u8 auth_type:4, auth_key:4; #else u8 auth_key:4, auth_type:4; #endif u8 assoc_type; u8 reserved; __le16 policy_support; u8 preamble_length; u8 ieee_mode; u8 bssid[ETH_ALEN]; __le32 assoc_tsf_msw; __le32 assoc_tsf_lsw; __le16 capability; __le16 listen_interval; __le16 beacon_interval; u8 dest[ETH_ALEN]; __le16 atim_window; u8 smr; u8 reserved1; __le16 reserved2; } __packed; struct ipw_supported_rates { u8 ieee_mode; u8 num_rates; u8 purpose; u8 reserved; u8 supported_rates[IPW_MAX_RATES]; } __packed; struct ipw_rts_threshold { __le16 rts_threshold; __le16 reserved; } __packed; struct ipw_frag_threshold { __le16 frag_threshold; __le16 reserved; } __packed; struct ipw_retry_limit { u8 short_retry_limit; u8 long_retry_limit; __le16 reserved; } __packed; struct ipw_dino_config { __le32 dino_config_addr; __le16 dino_config_size; u8 dino_response; u8 reserved; } __packed; struct ipw_aironet_info { u8 id; u8 length; __le16 reserved; } __packed; struct ipw_rx_key { u8 station_index; u8 key_type; u8 key_id; u8 key_flag; u8 key[16]; u8 station_address[6]; u8 key_index; u8 reserved; } __packed; struct ipw_country_channel_info { u8 first_channel; u8 no_channels; s8 max_tx_power; } __packed; struct ipw_country_info { u8 id; u8 length; u8 country_str[IEEE80211_COUNTRY_STRING_LEN]; struct ipw_country_channel_info groups[7]; } __packed; struct ipw_channel_tx_power { u8 channel_number; s8 tx_power; } __packed; #define SCAN_ASSOCIATED_INTERVAL (HZ) #define SCAN_INTERVAL (HZ / 10) #define MAX_A_CHANNELS 37 #define MAX_B_CHANNELS 14 struct ipw_tx_power { u8 num_channels; u8 ieee_mode; struct ipw_channel_tx_power channels_tx_power[MAX_A_CHANNELS]; } __packed; struct ipw_rsn_capabilities { u8 id; u8 length; __le16 version; } __packed; struct ipw_sensitivity_calib { __le16 beacon_rssi_raw; __le16 reserved; } __packed; /** * Host command structure. * * On input, the following fields should be filled: * - cmd * - len * - status_len * - param (if needed) * * On output, * - \a status contains status; * - \a param filled with status parameters. */ struct ipw_cmd { /* XXX */ u32 cmd; /**< Host command */ u32 status;/**< Status */ u32 status_len; /**< How many 32 bit parameters in the status */ u32 len; /**< incoming parameters length, bytes */ /** * command parameters. * There should be enough space for incoming and * outcoming parameters. * Incoming parameters listed 1-st, followed by outcoming params. * nParams=(len+3)/4+status_len */ u32 param[0]; } __packed; #define STATUS_HCMD_ACTIVE (1<<0) /**< host command in progress */ #define STATUS_INT_ENABLED (1<<1) #define STATUS_RF_KILL_HW (1<<2) #define STATUS_RF_KILL_SW (1<<3) #define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) #define STATUS_INIT (1<<5) #define STATUS_AUTH (1<<6) #define STATUS_ASSOCIATED (1<<7) #define STATUS_STATE_MASK (STATUS_INIT | STATUS_AUTH | STATUS_ASSOCIATED) #define STATUS_ASSOCIATING (1<<8) #define STATUS_DISASSOCIATING (1<<9) #define STATUS_ROAMING (1<<10) #define STATUS_EXIT_PENDING (1<<11) #define STATUS_DISASSOC_PENDING (1<<12) #define STATUS_STATE_PENDING (1<<13) #define STATUS_DIRECT_SCAN_PENDING (1<<19) #define STATUS_SCAN_PENDING (1<<20) #define STATUS_SCANNING (1<<21) #define STATUS_SCAN_ABORTING (1<<22) #define STATUS_SCAN_FORCED (1<<23) #define STATUS_LED_LINK_ON (1<<24) #define STATUS_LED_ACT_ON (1<<25) #define STATUS_INDIRECT_BYTE (1<<28) /* sysfs entry configured for access */ #define STATUS_INDIRECT_DWORD (1<<29) /* sysfs entry configured for access */ #define STATUS_DIRECT_DWORD (1<<30) /* sysfs entry configured for access */ #define STATUS_SECURITY_UPDATED (1<<31) /* Security sync needed */ #define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */ #define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */ #define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */ #define CFG_CUSTOM_MAC (1<<3) #define CFG_PREAMBLE_LONG (1<<4) #define CFG_ADHOC_PERSIST (1<<5) #define CFG_ASSOCIATE (1<<6) #define CFG_FIXED_RATE (1<<7) #define CFG_ADHOC_CREATE (1<<8) #define CFG_NO_LED (1<<9) #define CFG_BACKGROUND_SCAN (1<<10) #define CFG_SPEED_SCAN (1<<11) #define CFG_NET_STATS (1<<12) #define CAP_SHARED_KEY (1<<0) /* Off = OPEN */ #define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */ #define MAX_STATIONS 32 #define IPW_INVALID_STATION (0xff) struct ipw_station_entry { u8 mac_addr[ETH_ALEN]; u8 reserved; u8 support_mode; }; #define AVG_ENTRIES 8 struct average { s16 entries[AVG_ENTRIES]; u8 pos; u8 init; s32 sum; }; #define MAX_SPEED_SCAN 100 #define IPW_IBSS_MAC_HASH_SIZE 31 struct ipw_ibss_seq { u8 mac[ETH_ALEN]; u16 seq_num; u16 frag_num; unsigned long packet_time; struct list_head list; }; struct ipw_error_elem { /* XXX */ u32 desc; u32 time; u32 blink1; u32 blink2; u32 link1; u32 link2; u32 data; }; struct ipw_event { /* XXX */ u32 event; u32 time; u32 data; } __packed; struct ipw_fw_error { /* XXX */ unsigned long jiffies; u32 status; u32 config; u32 elem_len; u32 log_len; struct ipw_error_elem *elem; struct ipw_event *log; u8 payload[0]; } __packed; #ifdef CONFIG_IPW2200_PROMISCUOUS enum ipw_prom_filter { IPW_PROM_CTL_HEADER_ONLY = (1 << 0), IPW_PROM_MGMT_HEADER_ONLY = (1 << 1), IPW_PROM_DATA_HEADER_ONLY = (1 << 2), IPW_PROM_ALL_HEADER_ONLY = 0xf, /* bits 0..3 */ IPW_PROM_NO_TX = (1 << 4), IPW_PROM_NO_RX = (1 << 5), IPW_PROM_NO_CTL = (1 << 6), IPW_PROM_NO_MGMT = (1 << 7), IPW_PROM_NO_DATA = (1 << 8), }; struct ipw_priv; struct ipw_prom_priv { struct ipw_priv *priv; struct libipw_device *ieee; enum ipw_prom_filter filter; int tx_packets; int rx_packets; }; #endif #if defined(CONFIG_IPW2200_RADIOTAP) || defined(CONFIG_IPW2200_PROMISCUOUS) /* Magic struct that slots into the radiotap header -- no reason * to build this manually element by element, we can write it much * more efficiently than we can parse it. ORDER MATTERS HERE * * When sent to us via the simulated Rx interface in sysfs, the entire * structure is provided regardless of any bits unset. */ struct ipw_rt_hdr { struct ieee80211_radiotap_header rt_hdr; u64 rt_tsf; /* TSF */ /* XXX */ u8 rt_flags; /* radiotap packet flags */ u8 rt_rate; /* rate in 500kb/s */ __le16 rt_channel; /* channel in mhz */ __le16 rt_chbitmask; /* channel bitfield */ s8 rt_dbmsignal; /* signal in dbM, kluged to signed */ s8 rt_dbmnoise; u8 rt_antenna; /* antenna number */ u8 payload[0]; /* payload... */ } __packed; #endif struct ipw_priv { /* ieee device used by generic ieee processing code */ struct libipw_device *ieee; spinlock_t lock; spinlock_t irq_lock; struct mutex mutex; /* basic pci-network driver stuff */ struct pci_dev *pci_dev; struct net_device *net_dev; #ifdef CONFIG_IPW2200_PROMISCUOUS /* Promiscuous mode */ struct ipw_prom_priv *prom_priv; struct net_device *prom_net_dev; #endif /* pci hardware address support */ void __iomem *hw_base; unsigned long hw_len; struct fw_image_desc sram_desc; /* result of ucode download */ struct alive_command_responce dino_alive; wait_queue_head_t wait_command_queue; wait_queue_head_t wait_state; /* Rx and Tx DMA processing queues */ struct ipw_rx_queue *rxq; struct clx2_tx_queue txq_cmd; struct clx2_tx_queue txq[4]; u32 status; u32 config; u32 capability; struct average average_missed_beacons; s16 exp_avg_rssi; s16 exp_avg_noise; u32 port_type; int rx_bufs_min; /**< minimum number of bufs in Rx queue */ int rx_pend_max; /**< maximum pending buffers for one IRQ */ u32 hcmd_seq; /**< sequence number for hcmd */ u32 disassociate_threshold; u32 roaming_threshold; struct ipw_associate assoc_request; struct libipw_network *assoc_network; unsigned long ts_scan_abort; struct ipw_supported_rates rates; struct ipw_rates phy[3]; /**< PHY restrictions, per band */ struct ipw_rates supp; /**< software defined */ struct ipw_rates extended; /**< use for corresp. IE, AP only */ struct notif_link_deterioration last_link_deterioration; /** for statistics */ struct ipw_cmd *hcmd; /**< host command currently executed */ wait_queue_head_t hcmd_wq; /**< host command waits for execution */ u32 tsf_bcn[2]; /**< TSF from latest beacon */ struct notif_calibration calib; /**< last calibration */ /* ordinal interface with firmware */ u32 table0_addr; u32 table0_len; u32 table1_addr; u32 table1_len; u32 table2_addr; u32 table2_len; /* context information */ u8 essid[IW_ESSID_MAX_SIZE]; u8 essid_len; u8 nick[IW_ESSID_MAX_SIZE]; u16 rates_mask; u8 channel; struct ipw_sys_config sys_config; u32 power_mode; u8 bssid[ETH_ALEN]; u16 rts_threshold; u8 mac_addr[ETH_ALEN]; u8 num_stations; u8 stations[MAX_STATIONS][ETH_ALEN]; u8 short_retry_limit; u8 long_retry_limit; u32 notif_missed_beacons; /* Statistics and counters normalized with each association */ u32 last_missed_beacons; u32 last_tx_packets; u32 last_rx_packets; u32 last_tx_failures; u32 last_rx_err; u32 last_rate; u32 missed_adhoc_beacons; u32 missed_beacons; u32 rx_packets; u32 tx_packets; u32 quality; u8 speed_scan[MAX_SPEED_SCAN]; u8 speed_scan_pos; u16 last_seq_num; u16 last_frag_num; unsigned long last_packet_time; struct list_head ibss_mac_hash[IPW_IBSS_MAC_HASH_SIZE]; /* eeprom */ u8 eeprom[0x100]; /* 256 bytes of eeprom */ u8 country[4]; int eeprom_delay; struct iw_statistics wstats; struct iw_public_data wireless_data; int user_requested_scan; u8 direct_scan_ssid[IW_ESSID_MAX_SIZE]; u8 direct_scan_ssid_len; struct delayed_work adhoc_check; struct work_struct associate; struct work_struct disassociate; struct work_struct system_config; struct work_struct rx_replenish; struct delayed_work request_scan; struct delayed_work request_direct_scan; struct delayed_work request_passive_scan; struct delayed_work scan_event; struct work_struct adapter_restart; struct delayed_work rf_kill; struct work_struct up; struct work_struct down; struct delayed_work gather_stats; struct work_struct abort_scan; struct work_struct roam; struct delayed_work scan_check; struct work_struct link_up; struct work_struct link_down; struct tasklet_struct irq_tasklet; /* LED related variables and work_struct */ u8 nic_type; u32 led_activity_on; u32 led_activity_off; u32 led_association_on; u32 led_association_off; u32 led_ofdm_on; u32 led_ofdm_off; struct delayed_work led_link_on; struct delayed_work led_link_off; struct delayed_work led_act_off; struct work_struct merge_networks; struct ipw_cmd_log *cmdlog; int cmdlog_len; int cmdlog_pos; #define IPW_2200BG 1 #define IPW_2915ABG 2 u8 adapter; s8 tx_power; /* Track time in suspend */ unsigned long suspend_at; unsigned long suspend_time; #ifdef CONFIG_PM u32 pm_state[16]; #endif struct ipw_fw_error *error; /* network state */ /* Used to pass the current INTA value from ISR to Tasklet */ u32 isr_inta; /* QoS */ struct ipw_qos_info qos_data; struct work_struct qos_activate; /*********************************/ /* debugging info */ u32 indirect_dword; u32 direct_dword; u32 indirect_byte; }; /*ipw_priv */ /* debug macros */ /* Debug and printf string expansion helpers for printing bitfields */ #define BIT_FMT8 "%c%c%c%c-%c%c%c%c" #define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8 #define BIT_FMT32 BIT_FMT16 " " BIT_FMT16 #define BITC(x,y) (((x>>y)&1)?'1':'0') #define BIT_ARG8(x) \ BITC(x,7),BITC(x,6),BITC(x,5),BITC(x,4),\ BITC(x,3),BITC(x,2),BITC(x,1),BITC(x,0) #define BIT_ARG16(x) \ BITC(x,15),BITC(x,14),BITC(x,13),BITC(x,12),\ BITC(x,11),BITC(x,10),BITC(x,9),BITC(x,8),\ BIT_ARG8(x) #define BIT_ARG32(x) \ BITC(x,31),BITC(x,30),BITC(x,29),BITC(x,28),\ BITC(x,27),BITC(x,26),BITC(x,25),BITC(x,24),\ BITC(x,23),BITC(x,22),BITC(x,21),BITC(x,20),\ BITC(x,19),BITC(x,18),BITC(x,17),BITC(x,16),\ BIT_ARG16(x) #define IPW_DEBUG(level, fmt, args...) \ do { if (ipw_debug_level & (level)) \ printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #ifdef CONFIG_IPW2200_DEBUG #define IPW_LL_DEBUG(level, fmt, args...) \ do { if (ipw_debug_level & (level)) \ printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #else #define IPW_LL_DEBUG(level, fmt, args...) do {} while (0) #endif /* CONFIG_IPW2200_DEBUG */ /* * To use the debug system; * * If you are defining a new debug classification, simply add it to the #define * list here in the form of: * * #define IPW_DL_xxxx VALUE * * shifting value to the left one bit from the previous entry. xxxx should be * the name of the classification (for example, WEP) * * You then need to either add a IPW_xxxx_DEBUG() macro definition for your * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want * to send output to that classification. * * To add your debug level to the list of levels seen when you perform * * % cat /proc/net/ipw/debug_level * * you simply need to add your entry to the ipw_debug_levels array. * * If you do not see debug_level in /proc/net/ipw then you do not have * CONFIG_IPW2200_DEBUG defined in your kernel configuration * */ #define IPW_DL_ERROR (1<<0) #define IPW_DL_WARNING (1<<1) #define IPW_DL_INFO (1<<2) #define IPW_DL_WX (1<<3) #define IPW_DL_HOST_COMMAND (1<<5) #define IPW_DL_STATE (1<<6) #define IPW_DL_NOTIF (1<<10) #define IPW_DL_SCAN (1<<11) #define IPW_DL_ASSOC (1<<12) #define IPW_DL_DROP (1<<13) #define IPW_DL_IOCTL (1<<14) #define IPW_DL_MANAGE (1<<15) #define IPW_DL_FW (1<<16) #define IPW_DL_RF_KILL (1<<17) #define IPW_DL_FW_ERRORS (1<<18) #define IPW_DL_LED (1<<19) #define IPW_DL_ORD (1<<20) #define IPW_DL_FRAG (1<<21) #define IPW_DL_WEP (1<<22) #define IPW_DL_TX (1<<23) #define IPW_DL_RX (1<<24) #define IPW_DL_ISR (1<<25) #define IPW_DL_FW_INFO (1<<26) #define IPW_DL_IO (1<<27) #define IPW_DL_TRACE (1<<28) #define IPW_DL_STATS (1<<29) #define IPW_DL_MERGE (1<<30) #define IPW_DL_QOS (1<<31) #define IPW_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a) #define IPW_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a) #define IPW_DEBUG_INFO(f, a...) IPW_DEBUG(IPW_DL_INFO, f, ## a) #define IPW_DEBUG_WX(f, a...) IPW_DEBUG(IPW_DL_WX, f, ## a) #define IPW_DEBUG_SCAN(f, a...) IPW_DEBUG(IPW_DL_SCAN, f, ## a) #define IPW_DEBUG_TRACE(f, a...) IPW_LL_DEBUG(IPW_DL_TRACE, f, ## a) #define IPW_DEBUG_RX(f, a...) IPW_LL_DEBUG(IPW_DL_RX, f, ## a) #define IPW_DEBUG_TX(f, a...) IPW_LL_DEBUG(IPW_DL_TX, f, ## a) #define IPW_DEBUG_ISR(f, a...) IPW_LL_DEBUG(IPW_DL_ISR, f, ## a) #define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a) #define IPW_DEBUG_LED(f, a...) IPW_LL_DEBUG(IPW_DL_LED, f, ## a) #define IPW_DEBUG_WEP(f, a...) IPW_LL_DEBUG(IPW_DL_WEP, f, ## a) #define IPW_DEBUG_HC(f, a...) IPW_LL_DEBUG(IPW_DL_HOST_COMMAND, f, ## a) #define IPW_DEBUG_FRAG(f, a...) IPW_LL_DEBUG(IPW_DL_FRAG, f, ## a) #define IPW_DEBUG_FW(f, a...) IPW_LL_DEBUG(IPW_DL_FW, f, ## a) #define IPW_DEBUG_RF_KILL(f, a...) IPW_DEBUG(IPW_DL_RF_KILL, f, ## a) #define IPW_DEBUG_DROP(f, a...) IPW_DEBUG(IPW_DL_DROP, f, ## a) #define IPW_DEBUG_IO(f, a...) IPW_LL_DEBUG(IPW_DL_IO, f, ## a) #define IPW_DEBUG_ORD(f, a...) IPW_LL_DEBUG(IPW_DL_ORD, f, ## a) #define IPW_DEBUG_FW_INFO(f, a...) IPW_LL_DEBUG(IPW_DL_FW_INFO, f, ## a) #define IPW_DEBUG_NOTIF(f, a...) IPW_DEBUG(IPW_DL_NOTIF, f, ## a) #define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) #define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) #define IPW_DEBUG_STATS(f, a...) IPW_LL_DEBUG(IPW_DL_STATS, f, ## a) #define IPW_DEBUG_MERGE(f, a...) IPW_LL_DEBUG(IPW_DL_MERGE, f, ## a) #define IPW_DEBUG_QOS(f, a...) IPW_LL_DEBUG(IPW_DL_QOS, f, ## a) #include /* * Register bit definitions */ #define IPW_INTA_RW 0x00000008 #define IPW_INTA_MASK_R 0x0000000C #define IPW_INDIRECT_ADDR 0x00000010 #define IPW_INDIRECT_DATA 0x00000014 #define IPW_AUTOINC_ADDR 0x00000018 #define IPW_AUTOINC_DATA 0x0000001C #define IPW_RESET_REG 0x00000020 #define IPW_GP_CNTRL_RW 0x00000024 #define IPW_READ_INT_REGISTER 0xFF4 #define IPW_GP_CNTRL_BIT_INIT_DONE 0x00000004 #define IPW_REGISTER_DOMAIN1_END 0x00001000 #define IPW_SRAM_READ_INT_REGISTER 0x00000ff4 #define IPW_SHARED_LOWER_BOUND 0x00000200 #define IPW_INTERRUPT_AREA_LOWER_BOUND 0x00000f80 #define IPW_NIC_SRAM_LOWER_BOUND 0x00000000 #define IPW_NIC_SRAM_UPPER_BOUND 0x00030000 #define IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29) #define IPW_GP_CNTRL_BIT_CLOCK_READY 0x00000001 #define IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002 /* * RESET Register Bit Indexes */ #define CBD_RESET_REG_PRINCETON_RESET (1<<0) #define IPW_START_STANDBY (1<<2) #define IPW_ACTIVITY_LED (1<<4) #define IPW_ASSOCIATED_LED (1<<5) #define IPW_OFDM_LED (1<<6) #define IPW_RESET_REG_SW_RESET (1<<7) #define IPW_RESET_REG_MASTER_DISABLED (1<<8) #define IPW_RESET_REG_STOP_MASTER (1<<9) #define IPW_GATE_ODMA (1<<25) #define IPW_GATE_IDMA (1<<26) #define IPW_ARC_KESHET_CONFIG (1<<27) #define IPW_GATE_ADMA (1<<29) #define IPW_CSR_CIS_UPPER_BOUND 0x00000200 #define IPW_DOMAIN_0_END 0x1000 #define CLX_MEM_BAR_SIZE 0x1000 /* Dino/baseband control registers bits */ #define DINO_ENABLE_SYSTEM 0x80 /* 1 = baseband processor on, 0 = reset */ #define DINO_ENABLE_CS 0x40 /* 1 = enable ucode load */ #define DINO_RXFIFO_DATA 0x01 /* 1 = data available */ #define IPW_BASEBAND_CONTROL_STATUS 0X00200000 #define IPW_BASEBAND_TX_FIFO_WRITE 0X00200004 #define IPW_BASEBAND_RX_FIFO_READ 0X00200004 #define IPW_BASEBAND_CONTROL_STORE 0X00200010 #define IPW_INTERNAL_CMD_EVENT 0X00300004 #define IPW_BASEBAND_POWER_DOWN 0x00000001 #define IPW_MEM_HALT_AND_RESET 0x003000e0 /* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */ #define IPW_BIT_HALT_RESET_ON 0x80000000 #define IPW_BIT_HALT_RESET_OFF 0x00000000 #define CB_LAST_VALID 0x20000000 #define CB_INT_ENABLED 0x40000000 #define CB_VALID 0x80000000 #define CB_SRC_LE 0x08000000 #define CB_DEST_LE 0x04000000 #define CB_SRC_AUTOINC 0x00800000 #define CB_SRC_IO_GATED 0x00400000 #define CB_DEST_AUTOINC 0x00080000 #define CB_SRC_SIZE_LONG 0x00200000 #define CB_DEST_SIZE_LONG 0x00020000 /* DMA DEFINES */ #define DMA_CONTROL_SMALL_CB_CONST_VALUE 0x00540000 #define DMA_CB_STOP_AND_ABORT 0x00000C00 #define DMA_CB_START 0x00000100 #define IPW_SHARED_SRAM_SIZE 0x00030000 #define IPW_SHARED_SRAM_DMA_CONTROL 0x00027000 #define CB_MAX_LENGTH 0x1FFF #define IPW_HOST_EEPROM_DATA_SRAM_SIZE 0xA18 #define IPW_EEPROM_IMAGE_SIZE 0x100 /* DMA defs */ #define IPW_DMA_I_CURRENT_CB 0x003000D0 #define IPW_DMA_O_CURRENT_CB 0x003000D4 #define IPW_DMA_I_DMA_CONTROL 0x003000A4 #define IPW_DMA_I_CB_BASE 0x003000A0 #define IPW_TX_CMD_QUEUE_BD_BASE 0x00000200 #define IPW_TX_CMD_QUEUE_BD_SIZE 0x00000204 #define IPW_TX_QUEUE_0_BD_BASE 0x00000208 #define IPW_TX_QUEUE_0_BD_SIZE (0x0000020C) #define IPW_TX_QUEUE_1_BD_BASE 0x00000210 #define IPW_TX_QUEUE_1_BD_SIZE 0x00000214 #define IPW_TX_QUEUE_2_BD_BASE 0x00000218 #define IPW_TX_QUEUE_2_BD_SIZE (0x0000021C) #define IPW_TX_QUEUE_3_BD_BASE 0x00000220 #define IPW_TX_QUEUE_3_BD_SIZE 0x00000224 #define IPW_RX_BD_BASE 0x00000240 #define IPW_RX_BD_SIZE 0x00000244 #define IPW_RFDS_TABLE_LOWER 0x00000500 #define IPW_TX_CMD_QUEUE_READ_INDEX 0x00000280 #define IPW_TX_QUEUE_0_READ_INDEX 0x00000284 #define IPW_TX_QUEUE_1_READ_INDEX 0x00000288 #define IPW_TX_QUEUE_2_READ_INDEX (0x0000028C) #define IPW_TX_QUEUE_3_READ_INDEX 0x00000290 #define IPW_RX_READ_INDEX (0x000002A0) #define IPW_TX_CMD_QUEUE_WRITE_INDEX (0x00000F80) #define IPW_TX_QUEUE_0_WRITE_INDEX (0x00000F84) #define IPW_TX_QUEUE_1_WRITE_INDEX (0x00000F88) #define IPW_TX_QUEUE_2_WRITE_INDEX (0x00000F8C) #define IPW_TX_QUEUE_3_WRITE_INDEX (0x00000F90) #define IPW_RX_WRITE_INDEX (0x00000FA0) /* * EEPROM Related Definitions */ #define IPW_EEPROM_DATA_SRAM_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x814) #define IPW_EEPROM_DATA_SRAM_SIZE (IPW_SHARED_LOWER_BOUND + 0x818) #define IPW_EEPROM_LOAD_DISABLE (IPW_SHARED_LOWER_BOUND + 0x81C) #define IPW_EEPROM_DATA (IPW_SHARED_LOWER_BOUND + 0x820) #define IPW_EEPROM_UPPER_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x9E0) #define IPW_STATION_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0xA0C) #define IPW_STATION_TABLE_UPPER (IPW_SHARED_LOWER_BOUND + 0xB0C) #define IPW_REQUEST_ATIM (IPW_SHARED_LOWER_BOUND + 0xB0C) #define IPW_ATIM_SENT (IPW_SHARED_LOWER_BOUND + 0xB10) #define IPW_WHO_IS_AWAKE (IPW_SHARED_LOWER_BOUND + 0xB14) #define IPW_DURING_ATIM_WINDOW (IPW_SHARED_LOWER_BOUND + 0xB18) #define MSB 1 #define LSB 0 #define WORD_TO_BYTE(_word) ((_word) * sizeof(u16)) #define GET_EEPROM_ADDR(_wordoffset,_byteoffset) \ ( WORD_TO_BYTE(_wordoffset) + (_byteoffset) ) /* EEPROM access by BYTE */ #define EEPROM_PME_CAPABILITY (GET_EEPROM_ADDR(0x09,MSB)) /* 1 byte */ #define EEPROM_MAC_ADDRESS (GET_EEPROM_ADDR(0x21,LSB)) /* 6 byte */ #define EEPROM_VERSION (GET_EEPROM_ADDR(0x24,MSB)) /* 1 byte */ #define EEPROM_NIC_TYPE (GET_EEPROM_ADDR(0x25,LSB)) /* 1 byte */ #define EEPROM_SKU_CAPABILITY (GET_EEPROM_ADDR(0x25,MSB)) /* 1 byte */ #define EEPROM_COUNTRY_CODE (GET_EEPROM_ADDR(0x26,LSB)) /* 3 bytes */ #define EEPROM_IBSS_CHANNELS_BG (GET_EEPROM_ADDR(0x28,LSB)) /* 2 bytes */ #define EEPROM_IBSS_CHANNELS_A (GET_EEPROM_ADDR(0x29,MSB)) /* 5 bytes */ #define EEPROM_BSS_CHANNELS_BG (GET_EEPROM_ADDR(0x2c,LSB)) /* 2 bytes */ #define EEPROM_HW_VERSION (GET_EEPROM_ADDR(0x72,LSB)) /* 2 bytes */ /* NIC type as found in the one byte EEPROM_NIC_TYPE offset */ #define EEPROM_NIC_TYPE_0 0 #define EEPROM_NIC_TYPE_1 1 #define EEPROM_NIC_TYPE_2 2 #define EEPROM_NIC_TYPE_3 3 #define EEPROM_NIC_TYPE_4 4 /* Bluetooth Coexistence capabilities as found in EEPROM_SKU_CAPABILITY */ #define EEPROM_SKU_CAP_BT_CHANNEL_SIG 0x01 /* we can tell BT our channel # */ #define EEPROM_SKU_CAP_BT_PRIORITY 0x02 /* BT can take priority over us */ #define EEPROM_SKU_CAP_BT_OOB 0x04 /* we can signal BT out-of-band */ #define FW_MEM_REG_LOWER_BOUND 0x00300000 #define FW_MEM_REG_EEPROM_ACCESS (FW_MEM_REG_LOWER_BOUND + 0x40) #define IPW_EVENT_REG (FW_MEM_REG_LOWER_BOUND + 0x04) #define EEPROM_BIT_SK (1<<0) #define EEPROM_BIT_CS (1<<1) #define EEPROM_BIT_DI (1<<2) #define EEPROM_BIT_DO (1<<4) #define EEPROM_CMD_READ 0x2 /* Interrupts masks */ #define IPW_INTA_NONE 0x00000000 #define IPW_INTA_BIT_RX_TRANSFER 0x00000002 #define IPW_INTA_BIT_STATUS_CHANGE 0x00000010 #define IPW_INTA_BIT_BEACON_PERIOD_EXPIRED 0x00000020 //Inta Bits for CF #define IPW_INTA_BIT_TX_CMD_QUEUE 0x00000800 #define IPW_INTA_BIT_TX_QUEUE_1 0x00001000 #define IPW_INTA_BIT_TX_QUEUE_2 0x00002000 #define IPW_INTA_BIT_TX_QUEUE_3 0x00004000 #define IPW_INTA_BIT_TX_QUEUE_4 0x00008000 #define IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE 0x00010000 #define IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN 0x00100000 #define IPW_INTA_BIT_POWER_DOWN 0x00200000 #define IPW_INTA_BIT_FW_INITIALIZATION_DONE 0x01000000 #define IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE 0x02000000 #define IPW_INTA_BIT_RF_KILL_DONE 0x04000000 #define IPW_INTA_BIT_FATAL_ERROR 0x40000000 #define IPW_INTA_BIT_PARITY_ERROR 0x80000000 /* Interrupts enabled at init time. */ #define IPW_INTA_MASK_ALL \ (IPW_INTA_BIT_TX_QUEUE_1 | \ IPW_INTA_BIT_TX_QUEUE_2 | \ IPW_INTA_BIT_TX_QUEUE_3 | \ IPW_INTA_BIT_TX_QUEUE_4 | \ IPW_INTA_BIT_TX_CMD_QUEUE | \ IPW_INTA_BIT_RX_TRANSFER | \ IPW_INTA_BIT_FATAL_ERROR | \ IPW_INTA_BIT_PARITY_ERROR | \ IPW_INTA_BIT_STATUS_CHANGE | \ IPW_INTA_BIT_FW_INITIALIZATION_DONE | \ IPW_INTA_BIT_BEACON_PERIOD_EXPIRED | \ IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \ IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN | \ IPW_INTA_BIT_POWER_DOWN | \ IPW_INTA_BIT_RF_KILL_DONE ) /* FW event log definitions */ #define EVENT_ELEM_SIZE (3 * sizeof(u32)) #define EVENT_START_OFFSET (1 * sizeof(u32) + 2 * sizeof(u16)) /* FW error log definitions */ #define ERROR_ELEM_SIZE (7 * sizeof(u32)) #define ERROR_START_OFFSET (1 * sizeof(u32)) /* TX power level (dbm) */ #define IPW_TX_POWER_MIN -12 #define IPW_TX_POWER_MAX 20 #define IPW_TX_POWER_DEFAULT IPW_TX_POWER_MAX enum { IPW_FW_ERROR_OK = 0, IPW_FW_ERROR_FAIL, IPW_FW_ERROR_MEMORY_UNDERFLOW, IPW_FW_ERROR_MEMORY_OVERFLOW, IPW_FW_ERROR_BAD_PARAM, IPW_FW_ERROR_BAD_CHECKSUM, IPW_FW_ERROR_NMI_INTERRUPT, IPW_FW_ERROR_BAD_DATABASE, IPW_FW_ERROR_ALLOC_FAIL, IPW_FW_ERROR_DMA_UNDERRUN, IPW_FW_ERROR_DMA_STATUS, IPW_FW_ERROR_DINO_ERROR, IPW_FW_ERROR_EEPROM_ERROR, IPW_FW_ERROR_SYSASSERT, IPW_FW_ERROR_FATAL_ERROR }; #define AUTH_OPEN 0 #define AUTH_SHARED_KEY 1 #define AUTH_LEAP 2 #define AUTH_IGNORE 3 #define HC_ASSOCIATE 0 #define HC_REASSOCIATE 1 #define HC_DISASSOCIATE 2 #define HC_IBSS_START 3 #define HC_IBSS_RECONF 4 #define HC_DISASSOC_QUIET 5 #define HC_QOS_SUPPORT_ASSOC cpu_to_le16(0x01) #define IPW_RATE_CAPABILITIES 1 #define IPW_RATE_CONNECT 0 /* * Rate values and masks */ #define IPW_TX_RATE_1MB 0x0A #define IPW_TX_RATE_2MB 0x14 #define IPW_TX_RATE_5MB 0x37 #define IPW_TX_RATE_6MB 0x0D #define IPW_TX_RATE_9MB 0x0F #define IPW_TX_RATE_11MB 0x6E #define IPW_TX_RATE_12MB 0x05 #define IPW_TX_RATE_18MB 0x07 #define IPW_TX_RATE_24MB 0x09 #define IPW_TX_RATE_36MB 0x0B #define IPW_TX_RATE_48MB 0x01 #define IPW_TX_RATE_54MB 0x03 #define IPW_ORD_TABLE_ID_MASK 0x0000FF00 #define IPW_ORD_TABLE_VALUE_MASK 0x000000FF #define IPW_ORD_TABLE_0_MASK 0x0000F000 #define IPW_ORD_TABLE_1_MASK 0x0000F100 #define IPW_ORD_TABLE_2_MASK 0x0000F200 #define IPW_ORD_TABLE_3_MASK 0x0000F300 #define IPW_ORD_TABLE_4_MASK 0x0000F400 #define IPW_ORD_TABLE_5_MASK 0x0000F500 #define IPW_ORD_TABLE_6_MASK 0x0000F600 #define IPW_ORD_TABLE_7_MASK 0x0000F700 /* * Table 0 Entries (all entries are 32 bits) */ enum { IPW_ORD_STAT_TX_CURR_RATE = IPW_ORD_TABLE_0_MASK + 1, IPW_ORD_STAT_FRAG_TRESHOLD, IPW_ORD_STAT_RTS_THRESHOLD, IPW_ORD_STAT_TX_HOST_REQUESTS, IPW_ORD_STAT_TX_HOST_COMPLETE, IPW_ORD_STAT_TX_DIR_DATA, IPW_ORD_STAT_TX_DIR_DATA_B_1, IPW_ORD_STAT_TX_DIR_DATA_B_2, IPW_ORD_STAT_TX_DIR_DATA_B_5_5, IPW_ORD_STAT_TX_DIR_DATA_B_11, /* Hole */ IPW_ORD_STAT_TX_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 19, IPW_ORD_STAT_TX_DIR_DATA_G_2, IPW_ORD_STAT_TX_DIR_DATA_G_5_5, IPW_ORD_STAT_TX_DIR_DATA_G_6, IPW_ORD_STAT_TX_DIR_DATA_G_9, IPW_ORD_STAT_TX_DIR_DATA_G_11, IPW_ORD_STAT_TX_DIR_DATA_G_12, IPW_ORD_STAT_TX_DIR_DATA_G_18, IPW_ORD_STAT_TX_DIR_DATA_G_24, IPW_ORD_STAT_TX_DIR_DATA_G_36, IPW_ORD_STAT_TX_DIR_DATA_G_48, IPW_ORD_STAT_TX_DIR_DATA_G_54, IPW_ORD_STAT_TX_NON_DIR_DATA, IPW_ORD_STAT_TX_NON_DIR_DATA_B_1, IPW_ORD_STAT_TX_NON_DIR_DATA_B_2, IPW_ORD_STAT_TX_NON_DIR_DATA_B_5_5, IPW_ORD_STAT_TX_NON_DIR_DATA_B_11, /* Hole */ IPW_ORD_STAT_TX_NON_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 44, IPW_ORD_STAT_TX_NON_DIR_DATA_G_2, IPW_ORD_STAT_TX_NON_DIR_DATA_G_5_5, IPW_ORD_STAT_TX_NON_DIR_DATA_G_6, IPW_ORD_STAT_TX_NON_DIR_DATA_G_9, IPW_ORD_STAT_TX_NON_DIR_DATA_G_11, IPW_ORD_STAT_TX_NON_DIR_DATA_G_12, IPW_ORD_STAT_TX_NON_DIR_DATA_G_18, IPW_ORD_STAT_TX_NON_DIR_DATA_G_24, IPW_ORD_STAT_TX_NON_DIR_DATA_G_36, IPW_ORD_STAT_TX_NON_DIR_DATA_G_48, IPW_ORD_STAT_TX_NON_DIR_DATA_G_54, IPW_ORD_STAT_TX_RETRY, IPW_ORD_STAT_TX_FAILURE, IPW_ORD_STAT_RX_ERR_CRC, IPW_ORD_STAT_RX_ERR_ICV, IPW_ORD_STAT_RX_NO_BUFFER, IPW_ORD_STAT_FULL_SCANS, IPW_ORD_STAT_PARTIAL_SCANS, IPW_ORD_STAT_TGH_ABORTED_SCANS, IPW_ORD_STAT_TX_TOTAL_BYTES, IPW_ORD_STAT_CURR_RSSI_RAW, IPW_ORD_STAT_RX_BEACON, IPW_ORD_STAT_MISSED_BEACONS, IPW_ORD_TABLE_0_LAST }; #define IPW_RSSI_TO_DBM 112 /* Table 1 Entries */ enum { IPW_ORD_TABLE_1_LAST = IPW_ORD_TABLE_1_MASK | 1, }; /* * Table 2 Entries * * FW_VERSION: 16 byte string * FW_DATE: 16 byte string (only 14 bytes used) * UCODE_VERSION: 4 byte version code * UCODE_DATE: 5 bytes code code * ADDAPTER_MAC: 6 byte MAC address * RTC: 4 byte clock */ enum { IPW_ORD_STAT_FW_VERSION = IPW_ORD_TABLE_2_MASK | 1, IPW_ORD_STAT_FW_DATE, IPW_ORD_STAT_UCODE_VERSION, IPW_ORD_STAT_UCODE_DATE, IPW_ORD_STAT_ADAPTER_MAC, IPW_ORD_STAT_RTC, IPW_ORD_TABLE_2_LAST }; /* Table 3 */ enum { IPW_ORD_STAT_TX_PACKET = IPW_ORD_TABLE_3_MASK | 0, IPW_ORD_STAT_TX_PACKET_FAILURE, IPW_ORD_STAT_TX_PACKET_SUCCESS, IPW_ORD_STAT_TX_PACKET_ABORTED, IPW_ORD_TABLE_3_LAST }; /* Table 4 */ enum { IPW_ORD_TABLE_4_LAST = IPW_ORD_TABLE_4_MASK }; /* Table 5 */ enum { IPW_ORD_STAT_AVAILABLE_AP_COUNT = IPW_ORD_TABLE_5_MASK, IPW_ORD_STAT_AP_ASSNS, IPW_ORD_STAT_ROAM, IPW_ORD_STAT_ROAM_CAUSE_MISSED_BEACONS, IPW_ORD_STAT_ROAM_CAUSE_UNASSOC, IPW_ORD_STAT_ROAM_CAUSE_RSSI, IPW_ORD_STAT_ROAM_CAUSE_LINK_QUALITY, IPW_ORD_STAT_ROAM_CAUSE_AP_LOAD_BALANCE, IPW_ORD_STAT_ROAM_CAUSE_AP_NO_TX, IPW_ORD_STAT_LINK_UP, IPW_ORD_STAT_LINK_DOWN, IPW_ORD_ANTENNA_DIVERSITY, IPW_ORD_CURR_FREQ, IPW_ORD_TABLE_5_LAST }; /* Table 6 */ enum { IPW_ORD_COUNTRY_CODE = IPW_ORD_TABLE_6_MASK, IPW_ORD_CURR_BSSID, IPW_ORD_CURR_SSID, IPW_ORD_TABLE_6_LAST }; /* Table 7 */ enum { IPW_ORD_STAT_PERCENT_MISSED_BEACONS = IPW_ORD_TABLE_7_MASK, IPW_ORD_STAT_PERCENT_TX_RETRIES, IPW_ORD_STAT_PERCENT_LINK_QUALITY, IPW_ORD_STAT_CURR_RSSI_DBM, IPW_ORD_TABLE_7_LAST }; #define IPW_ERROR_LOG (IPW_SHARED_LOWER_BOUND + 0x410) #define IPW_EVENT_LOG (IPW_SHARED_LOWER_BOUND + 0x414) #define IPW_ORDINALS_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0x500) #define IPW_ORDINALS_TABLE_0 (IPW_SHARED_LOWER_BOUND + 0x180) #define IPW_ORDINALS_TABLE_1 (IPW_SHARED_LOWER_BOUND + 0x184) #define IPW_ORDINALS_TABLE_2 (IPW_SHARED_LOWER_BOUND + 0x188) #define IPW_MEM_FIXED_OVERRIDE (IPW_SHARED_LOWER_BOUND + 0x41C) struct ipw_fixed_rate { __le16 tx_rates; __le16 reserved; } __packed; #define IPW_INDIRECT_ADDR_MASK (~0x3ul) struct host_cmd { u8 cmd; u8 len; u16 reserved; u32 *param; } __packed; /* XXX */ struct cmdlog_host_cmd { u8 cmd; u8 len; __le16 reserved; char param[124]; } __packed; struct ipw_cmd_log { unsigned long jiffies; int retcode; struct cmdlog_host_cmd cmd; }; /* SysConfig command parameters ... */ /* bt_coexistence param */ #define CFG_BT_COEXISTENCE_SIGNAL_CHNL 0x01 /* tell BT our chnl # */ #define CFG_BT_COEXISTENCE_DEFER 0x02 /* defer our Tx if BT traffic */ #define CFG_BT_COEXISTENCE_KILL 0x04 /* kill our Tx if BT traffic */ #define CFG_BT_COEXISTENCE_WME_OVER_BT 0x08 /* multimedia extensions */ #define CFG_BT_COEXISTENCE_OOB 0x10 /* signal BT via out-of-band */ /* clear-to-send to self param */ #define CFG_CTS_TO_ITSELF_ENABLED_MIN 0x00 #define CFG_CTS_TO_ITSELF_ENABLED_MAX 0x01 #define CFG_CTS_TO_ITSELF_ENABLED_DEF CFG_CTS_TO_ITSELF_ENABLED_MIN /* Antenna diversity param (h/w can select best antenna, based on signal) */ #define CFG_SYS_ANTENNA_BOTH 0x00 /* NIC selects best antenna */ #define CFG_SYS_ANTENNA_A 0x01 /* force antenna A */ #define CFG_SYS_ANTENNA_B 0x03 /* force antenna B */ #define CFG_SYS_ANTENNA_SLOW_DIV 0x02 /* consider background noise */ #define IPW_MAX_CONFIG_RETRIES 10 #endif /* __ipw2200_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/ipw2100.h0000644000175000017500000012023612026211315023151 0ustar mcgrofmcgrof/****************************************************************************** Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The full GNU General Public License is included in this distribution in the file called LICENSE. Contact Information: Intel Linux Wireless Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ #ifndef _IPW2100_H #define _IPW2100_H #include #include #include #include #include #include #include #include #include #include #include #include // new driver API #ifdef CONFIG_IPW2100_MONITOR #include #endif #include #include #include "libipw.h" struct ipw2100_priv; struct ipw2100_tx_packet; struct ipw2100_rx_packet; #define IPW_DL_UNINIT 0x80000000 #define IPW_DL_NONE 0x00000000 #define IPW_DL_ALL 0x7FFFFFFF /* * To use the debug system; * * If you are defining a new debug classification, simply add it to the #define * list here in the form of: * * #define IPW_DL_xxxx VALUE * * shifting value to the left one bit from the previous entry. xxxx should be * the name of the classification (for example, WEP) * * You then need to either add a IPW2100_xxxx_DEBUG() macro definition for your * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want * to send output to that classification. * * To add your debug level to the list of levels seen when you perform * * % cat /proc/net/ipw2100/debug_level * * you simply need to add your entry to the ipw2100_debug_levels array. * * If you do not see debug_level in /proc/net/ipw2100 then you do not have * CONFIG_IPW2100_DEBUG defined in your kernel configuration * */ #define IPW_DL_ERROR (1<<0) #define IPW_DL_WARNING (1<<1) #define IPW_DL_INFO (1<<2) #define IPW_DL_WX (1<<3) #define IPW_DL_HC (1<<5) #define IPW_DL_STATE (1<<6) #define IPW_DL_NOTIF (1<<10) #define IPW_DL_SCAN (1<<11) #define IPW_DL_ASSOC (1<<12) #define IPW_DL_DROP (1<<13) #define IPW_DL_IOCTL (1<<14) #define IPW_DL_RF_KILL (1<<17) #define IPW_DL_MANAGE (1<<15) #define IPW_DL_FW (1<<16) #define IPW_DL_FRAG (1<<21) #define IPW_DL_WEP (1<<22) #define IPW_DL_TX (1<<23) #define IPW_DL_RX (1<<24) #define IPW_DL_ISR (1<<25) #define IPW_DL_IO (1<<26) #define IPW_DL_TRACE (1<<28) #define IPW_DEBUG_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a) #define IPW_DEBUG_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a) #define IPW_DEBUG_INFO(f...) IPW_DEBUG(IPW_DL_INFO, ## f) #define IPW_DEBUG_WX(f...) IPW_DEBUG(IPW_DL_WX, ## f) #define IPW_DEBUG_SCAN(f...) IPW_DEBUG(IPW_DL_SCAN, ## f) #define IPW_DEBUG_NOTIF(f...) IPW_DEBUG(IPW_DL_NOTIF, ## f) #define IPW_DEBUG_TRACE(f...) IPW_DEBUG(IPW_DL_TRACE, ## f) #define IPW_DEBUG_RX(f...) IPW_DEBUG(IPW_DL_RX, ## f) #define IPW_DEBUG_TX(f...) IPW_DEBUG(IPW_DL_TX, ## f) #define IPW_DEBUG_ISR(f...) IPW_DEBUG(IPW_DL_ISR, ## f) #define IPW_DEBUG_MANAGEMENT(f...) IPW_DEBUG(IPW_DL_MANAGE, ## f) #define IPW_DEBUG_WEP(f...) IPW_DEBUG(IPW_DL_WEP, ## f) #define IPW_DEBUG_HC(f...) IPW_DEBUG(IPW_DL_HC, ## f) #define IPW_DEBUG_FRAG(f...) IPW_DEBUG(IPW_DL_FRAG, ## f) #define IPW_DEBUG_FW(f...) IPW_DEBUG(IPW_DL_FW, ## f) #define IPW_DEBUG_RF_KILL(f...) IPW_DEBUG(IPW_DL_RF_KILL, ## f) #define IPW_DEBUG_DROP(f...) IPW_DEBUG(IPW_DL_DROP, ## f) #define IPW_DEBUG_IO(f...) IPW_DEBUG(IPW_DL_IO, ## f) #define IPW_DEBUG_IOCTL(f...) IPW_DEBUG(IPW_DL_IOCTL, ## f) #define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) #define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) enum { IPW_HW_STATE_DISABLED = 1, IPW_HW_STATE_ENABLED = 0 }; extern const char *port_type_str[]; extern const char *band_str[]; #define NUMBER_OF_BD_PER_COMMAND_PACKET 1 #define NUMBER_OF_BD_PER_DATA_PACKET 2 #define IPW_MAX_BDS 6 #define NUMBER_OF_OVERHEAD_BDS_PER_PACKETR 2 #define NUMBER_OF_BDS_TO_LEAVE_FOR_COMMANDS 1 #define REQUIRED_SPACE_IN_RING_FOR_COMMAND_PACKET \ (IPW_BD_QUEUE_W_R_MIN_SPARE + NUMBER_OF_BD_PER_COMMAND_PACKET) struct bd_status { union { struct { u8 nlf:1, txType:2, intEnabled:1, reserved:4; } fields; u8 field; } info; } __packed; struct ipw2100_bd { u32 host_addr; u32 buf_length; struct bd_status status; /* number of fragments for frame (should be set only for * 1st TBD) */ u8 num_fragments; u8 reserved[6]; } __packed; #define IPW_BD_QUEUE_LENGTH(n) (1<value = (x)->hi = 0; \ (x)->lo = 0x7fffffff; \ } while (0) #define SET_STAT(x,y) do { \ (x)->value = y; \ if ((x)->value > (x)->hi) (x)->hi = (x)->value; \ if ((x)->value < (x)->lo) (x)->lo = (x)->value; \ } while (0) #define INC_STAT(x) do { if (++(x)->value > (x)->hi) (x)->hi = (x)->value; } \ while (0) #define DEC_STAT(x) do { if (--(x)->value < (x)->lo) (x)->lo = (x)->value; } \ while (0) #define IPW2100_ERROR_QUEUE 5 /* Power management code: enable or disable? */ enum { #ifdef CONFIG_PM IPW2100_PM_DISABLED = 0, PM_STATE_SIZE = 16, #else IPW2100_PM_DISABLED = 1, PM_STATE_SIZE = 0, #endif }; #define STATUS_POWERED (1<<0) #define STATUS_CMD_ACTIVE (1<<1) /**< host command in progress */ #define STATUS_RUNNING (1<<2) /* Card initialized, but not enabled */ #define STATUS_ENABLED (1<<3) /* Card enabled -- can scan,Tx,Rx */ #define STATUS_STOPPING (1<<4) /* Card is in shutdown phase */ #define STATUS_INITIALIZED (1<<5) /* Card is ready for external calls */ #define STATUS_ASSOCIATING (1<<9) /* Associated, but no BSSID yet */ #define STATUS_ASSOCIATED (1<<10) /* Associated and BSSID valid */ #define STATUS_INT_ENABLED (1<<11) #define STATUS_RF_KILL_HW (1<<12) #define STATUS_RF_KILL_SW (1<<13) #define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) #define STATUS_EXIT_PENDING (1<<14) #define STATUS_SCAN_PENDING (1<<23) #define STATUS_SCANNING (1<<24) #define STATUS_SCAN_ABORTING (1<<25) #define STATUS_SCAN_COMPLETE (1<<26) #define STATUS_WX_EVENT_PENDING (1<<27) #define STATUS_RESET_PENDING (1<<29) #define STATUS_SECURITY_UPDATED (1<<30) /* Security sync needed */ /* Internal NIC states */ #define IPW_STATE_INITIALIZED (1<<0) #define IPW_STATE_COUNTRY_FOUND (1<<1) #define IPW_STATE_ASSOCIATED (1<<2) #define IPW_STATE_ASSN_LOST (1<<3) #define IPW_STATE_ASSN_CHANGED (1<<4) #define IPW_STATE_SCAN_COMPLETE (1<<5) #define IPW_STATE_ENTERED_PSP (1<<6) #define IPW_STATE_LEFT_PSP (1<<7) #define IPW_STATE_RF_KILL (1<<8) #define IPW_STATE_DISABLED (1<<9) #define IPW_STATE_POWER_DOWN (1<<10) #define IPW_STATE_SCANNING (1<<11) #define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */ #define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */ #define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */ #define CFG_CUSTOM_MAC (1<<3) #define CFG_LONG_PREAMBLE (1<<4) #define CFG_ASSOCIATE (1<<6) #define CFG_FIXED_RATE (1<<7) #define CFG_ADHOC_CREATE (1<<8) #define CFG_PASSIVE_SCAN (1<<10) #ifdef CONFIG_IPW2100_MONITOR #define CFG_CRC_CHECK (1<<11) #endif #define CAP_SHARED_KEY (1<<0) /* Off = OPEN */ #define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */ struct ipw2100_priv { void __iomem *ioaddr; int stop_hang_check; /* Set 1 when shutting down to kill hang_check */ int stop_rf_kill; /* Set 1 when shutting down to kill rf_kill */ struct libipw_device *ieee; unsigned long status; unsigned long config; unsigned long capability; /* Statistics */ int resets; int reset_backoff; /* Context */ u8 essid[IW_ESSID_MAX_SIZE]; u8 essid_len; u8 bssid[ETH_ALEN]; u8 channel; int last_mode; unsigned long connect_start; unsigned long last_reset; u32 channel_mask; u32 fatal_error; u32 fatal_errors[IPW2100_ERROR_QUEUE]; u32 fatal_index; int eeprom_version; int firmware_version; unsigned long hw_features; int hangs; u32 last_rtc; int dump_raw; /* 1 to dump raw bytes in /sys/.../memory */ u8 *snapshot[0x30]; u8 mandatory_bssid_mac[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; int power_mode; int messages_sent; int short_retry_limit; int long_retry_limit; u32 rts_threshold; u32 frag_threshold; int in_isr; u32 tx_rates; int tx_power; u32 beacon_interval; char nick[IW_ESSID_MAX_SIZE + 1]; struct ipw2100_status_queue status_queue; struct statistic txq_stat; struct statistic rxq_stat; struct ipw2100_bd_queue rx_queue; struct ipw2100_bd_queue tx_queue; struct ipw2100_rx_packet *rx_buffers; struct statistic fw_pend_stat; struct list_head fw_pend_list; struct statistic msg_free_stat; struct statistic msg_pend_stat; struct list_head msg_free_list; struct list_head msg_pend_list; struct ipw2100_tx_packet *msg_buffers; struct statistic tx_free_stat; struct statistic tx_pend_stat; struct list_head tx_free_list; struct list_head tx_pend_list; struct ipw2100_tx_packet *tx_buffers; struct ipw2100_ordinals ordinals; struct pci_dev *pci_dev; struct proc_dir_entry *dir_dev; struct net_device *net_dev; struct iw_statistics wstats; struct iw_public_data wireless_data; struct tasklet_struct irq_tasklet; struct delayed_work reset_work; struct delayed_work security_work; struct delayed_work wx_event_work; struct delayed_work hang_check; struct delayed_work rf_kill; struct work_struct scan_event_now; struct delayed_work scan_event_later; int user_requested_scan; /* Track time in suspend */ unsigned long suspend_at; unsigned long suspend_time; u32 interrupts; int tx_interrupts; int rx_interrupts; int inta_other; spinlock_t low_lock; struct mutex action_mutex; struct mutex adapter_mutex; wait_queue_head_t wait_command_queue; }; /********************************************************* * Host Command -> From Driver to FW *********************************************************/ /** * Host command identifiers */ #define HOST_COMPLETE 2 #define SYSTEM_CONFIG 6 #define SSID 8 #define MANDATORY_BSSID 9 #define AUTHENTICATION_TYPE 10 #define ADAPTER_ADDRESS 11 #define PORT_TYPE 12 #define INTERNATIONAL_MODE 13 #define CHANNEL 14 #define RTS_THRESHOLD 15 #define FRAG_THRESHOLD 16 #define POWER_MODE 17 #define TX_RATES 18 #define BASIC_TX_RATES 19 #define WEP_KEY_INFO 20 #define WEP_KEY_INDEX 25 #define WEP_FLAGS 26 #define ADD_MULTICAST 27 #define CLEAR_ALL_MULTICAST 28 #define BEACON_INTERVAL 29 #define ATIM_WINDOW 30 #define CLEAR_STATISTICS 31 #define SEND 33 #define TX_POWER_INDEX 36 #define BROADCAST_SCAN 43 #define CARD_DISABLE 44 #define PREFERRED_BSSID 45 #define SET_SCAN_OPTIONS 46 #define SCAN_DWELL_TIME 47 #define SWEEP_TABLE 48 #define AP_OR_STATION_TABLE 49 #define GROUP_ORDINALS 50 #define SHORT_RETRY_LIMIT 51 #define LONG_RETRY_LIMIT 52 #define HOST_PRE_POWER_DOWN 58 #define CARD_DISABLE_PHY_OFF 61 #define MSDU_TX_RATES 62 /* Rogue AP Detection */ #define SET_STATION_STAT_BITS 64 #define CLEAR_STATIONS_STAT_BITS 65 #define LEAP_ROGUE_MODE 66 //TODO tbw replaced by CFG_LEAP_ROGUE_AP #define SET_SECURITY_INFORMATION 67 #define DISASSOCIATION_BSSID 68 #define SET_WPA_IE 69 /* system configuration bit mask: */ #define IPW_CFG_MONITOR 0x00004 #define IPW_CFG_PREAMBLE_AUTO 0x00010 #define IPW_CFG_IBSS_AUTO_START 0x00020 #define IPW_CFG_LOOPBACK 0x00100 #define IPW_CFG_ANSWER_BCSSID_PROBE 0x00800 #define IPW_CFG_BT_SIDEBAND_SIGNAL 0x02000 #define IPW_CFG_802_1x_ENABLE 0x04000 #define IPW_CFG_BSS_MASK 0x08000 #define IPW_CFG_IBSS_MASK 0x10000 #define IPW_SCAN_NOASSOCIATE (1<<0) #define IPW_SCAN_MIXED_CELL (1<<1) /* RESERVED (1<<2) */ #define IPW_SCAN_PASSIVE (1<<3) #define IPW_NIC_FATAL_ERROR 0x2A7F0 #define IPW_ERROR_ADDR(x) (x & 0x3FFFF) #define IPW_ERROR_CODE(x) ((x & 0xFF000000) >> 24) #define IPW2100_ERR_C3_CORRUPTION (0x10 << 24) #define IPW2100_ERR_MSG_TIMEOUT (0x11 << 24) #define IPW2100_ERR_FW_LOAD (0x12 << 24) #define IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND 0x200 #define IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0D80 #define IPW_MEM_HOST_SHARED_RX_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x40) #define IPW_MEM_HOST_SHARED_RX_STATUS_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x44) #define IPW_MEM_HOST_SHARED_RX_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x48) #define IPW_MEM_HOST_SHARED_RX_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0xa0) #define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x00) #define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x04) #define IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x80) #define IPW_MEM_HOST_SHARED_RX_WRITE_INDEX \ (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x20) #define IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX \ (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND) #define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x180) #define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x184) #define IPW2100_INTA_TX_TRANSFER (0x00000001) // Bit 0 (LSB) #define IPW2100_INTA_RX_TRANSFER (0x00000002) // Bit 1 #define IPW2100_INTA_TX_COMPLETE (0x00000004) // Bit 2 #define IPW2100_INTA_EVENT_INTERRUPT (0x00000008) // Bit 3 #define IPW2100_INTA_STATUS_CHANGE (0x00000010) // Bit 4 #define IPW2100_INTA_BEACON_PERIOD_EXPIRED (0x00000020) // Bit 5 #define IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE (0x00010000) // Bit 16 #define IPW2100_INTA_FW_INIT_DONE (0x01000000) // Bit 24 #define IPW2100_INTA_FW_CALIBRATION_CALC (0x02000000) // Bit 25 #define IPW2100_INTA_FATAL_ERROR (0x40000000) // Bit 30 #define IPW2100_INTA_PARITY_ERROR (0x80000000) // Bit 31 (MSB) #define IPW_AUX_HOST_RESET_REG_PRINCETON_RESET (0x00000001) #define IPW_AUX_HOST_RESET_REG_FORCE_NMI (0x00000002) #define IPW_AUX_HOST_RESET_REG_PCI_HOST_CLUSTER_FATAL_NMI (0x00000004) #define IPW_AUX_HOST_RESET_REG_CORE_FATAL_NMI (0x00000008) #define IPW_AUX_HOST_RESET_REG_SW_RESET (0x00000080) #define IPW_AUX_HOST_RESET_REG_MASTER_DISABLED (0x00000100) #define IPW_AUX_HOST_RESET_REG_STOP_MASTER (0x00000200) #define IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY (0x00000001) // Bit 0 (LSB) #define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY (0x00000002) // Bit 1 #define IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE (0x00000004) // Bit 2 #define IPW_AUX_HOST_GP_CNTRL_BITS_SYS_CONFIG (0x000007c0) // Bits 6-10 #define IPW_AUX_HOST_GP_CNTRL_BIT_BUS_TYPE (0x00000200) // Bit 9 #define IPW_AUX_HOST_GP_CNTRL_BIT_BAR0_BLOCK_SIZE (0x00000400) // Bit 10 #define IPW_AUX_HOST_GP_CNTRL_BIT_USB_MODE (0x20000000) // Bit 29 #define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_FORCES_SYS_CLK (0x40000000) // Bit 30 #define IPW_AUX_HOST_GP_CNTRL_BIT_FW_FORCES_SYS_CLK (0x80000000) // Bit 31 (MSB) #define IPW_BIT_GPIO_GPIO1_MASK 0x0000000C #define IPW_BIT_GPIO_GPIO3_MASK 0x000000C0 #define IPW_BIT_GPIO_GPIO1_ENABLE 0x00000008 #define IPW_BIT_GPIO_RF_KILL 0x00010000 #define IPW_BIT_GPIO_LED_OFF 0x00002000 // Bit 13 = 1 #define IPW_REG_DOMAIN_0_OFFSET 0x0000 #define IPW_REG_DOMAIN_1_OFFSET IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND #define IPW_REG_INTA IPW_REG_DOMAIN_0_OFFSET + 0x0008 #define IPW_REG_INTA_MASK IPW_REG_DOMAIN_0_OFFSET + 0x000C #define IPW_REG_INDIRECT_ACCESS_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0010 #define IPW_REG_INDIRECT_ACCESS_DATA IPW_REG_DOMAIN_0_OFFSET + 0x0014 #define IPW_REG_AUTOINCREMENT_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0018 #define IPW_REG_AUTOINCREMENT_DATA IPW_REG_DOMAIN_0_OFFSET + 0x001C #define IPW_REG_RESET_REG IPW_REG_DOMAIN_0_OFFSET + 0x0020 #define IPW_REG_GP_CNTRL IPW_REG_DOMAIN_0_OFFSET + 0x0024 #define IPW_REG_GPIO IPW_REG_DOMAIN_0_OFFSET + 0x0030 #define IPW_REG_FW_TYPE IPW_REG_DOMAIN_1_OFFSET + 0x0188 #define IPW_REG_FW_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x018C #define IPW_REG_FW_COMPATABILITY_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x0190 #define IPW_REG_INDIRECT_ADDR_MASK 0x00FFFFFC #define IPW_INTERRUPT_MASK 0xC1010013 #define IPW2100_CONTROL_REG 0x220000 #define IPW2100_CONTROL_PHY_OFF 0x8 #define IPW2100_COMMAND 0x00300004 #define IPW2100_COMMAND_PHY_ON 0x0 #define IPW2100_COMMAND_PHY_OFF 0x1 /* in DEBUG_AREA, values of memory always 0xd55555d5 */ #define IPW_REG_DOA_DEBUG_AREA_START IPW_REG_DOMAIN_0_OFFSET + 0x0090 #define IPW_REG_DOA_DEBUG_AREA_END IPW_REG_DOMAIN_0_OFFSET + 0x00FF #define IPW_DATA_DOA_DEBUG_VALUE 0xd55555d5 #define IPW_INTERNAL_REGISTER_HALT_AND_RESET 0x003000e0 #define IPW_WAIT_CLOCK_STABILIZATION_DELAY 50 // micro seconds #define IPW_WAIT_RESET_ARC_COMPLETE_DELAY 10 // micro seconds #define IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY 10 // micro seconds // BD ring queue read/write difference #define IPW_BD_QUEUE_W_R_MIN_SPARE 2 #define IPW_CACHE_LINE_LENGTH_DEFAULT 0x80 #define IPW_CARD_DISABLE_PHY_OFF_COMPLETE_WAIT 100 // 100 milli #define IPW_PREPARE_POWER_DOWN_COMPLETE_WAIT 100 // 100 milli #define IPW_HEADER_802_11_SIZE sizeof(struct libipw_hdr_3addr) #define IPW_MAX_80211_PAYLOAD_SIZE 2304U #define IPW_MAX_802_11_PAYLOAD_LENGTH 2312 #define IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH 1536 #define IPW_MIN_ACCEPTABLE_RX_FRAME_LENGTH 60 #define IPW_MAX_ACCEPTABLE_RX_FRAME_LENGTH \ (IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH + IPW_HEADER_802_11_SIZE - \ sizeof(struct ethhdr)) #define IPW_802_11_FCS_LENGTH 4 #define IPW_RX_NIC_BUFFER_LENGTH \ (IPW_MAX_802_11_PAYLOAD_LENGTH + IPW_HEADER_802_11_SIZE + \ IPW_802_11_FCS_LENGTH) #define IPW_802_11_PAYLOAD_OFFSET \ (sizeof(struct libipw_hdr_3addr) + \ sizeof(struct libipw_snap_hdr)) struct ipw2100_rx { union { unsigned char payload[IPW_RX_NIC_BUFFER_LENGTH]; struct libipw_hdr_4addr header; u32 status; struct ipw2100_notification notification; struct ipw2100_cmd_header command; } rx_data; } __packed; /* Bit 0-7 are for 802.11b tx rates - . Bit 5-7 are reserved */ #define TX_RATE_1_MBIT 0x0001 #define TX_RATE_2_MBIT 0x0002 #define TX_RATE_5_5_MBIT 0x0004 #define TX_RATE_11_MBIT 0x0008 #define TX_RATE_MASK 0x000F #define DEFAULT_TX_RATES 0x000F #define IPW_POWER_MODE_CAM 0x00 //(always on) #define IPW_POWER_INDEX_1 0x01 #define IPW_POWER_INDEX_2 0x02 #define IPW_POWER_INDEX_3 0x03 #define IPW_POWER_INDEX_4 0x04 #define IPW_POWER_INDEX_5 0x05 #define IPW_POWER_AUTO 0x06 #define IPW_POWER_MASK 0x0F #define IPW_POWER_ENABLED 0x10 #define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK) #define IPW_TX_POWER_AUTO 0 #define IPW_TX_POWER_ENHANCED 1 #define IPW_TX_POWER_DEFAULT 32 #define IPW_TX_POWER_MIN 0 #define IPW_TX_POWER_MAX 16 #define IPW_TX_POWER_MIN_DBM (-12) #define IPW_TX_POWER_MAX_DBM 16 #define FW_SCAN_DONOT_ASSOCIATE 0x0001 // Dont Attempt to Associate after Scan #define FW_SCAN_PASSIVE 0x0008 // Force PASSSIVE Scan #define REG_MIN_CHANNEL 0 #define REG_MAX_CHANNEL 14 #define REG_CHANNEL_MASK 0x00003FFF #define IPW_IBSS_11B_DEFAULT_MASK 0x87ff #define DIVERSITY_EITHER 0 // Use both antennas #define DIVERSITY_ANTENNA_A 1 // Use antenna A #define DIVERSITY_ANTENNA_B 2 // Use antenna B #define HOST_COMMAND_WAIT 0 #define HOST_COMMAND_NO_WAIT 1 #define LOCK_NONE 0 #define LOCK_DRIVER 1 #define LOCK_FW 2 #define TYPE_SWEEP_ORD 0x000D #define TYPE_IBSS_STTN_ORD 0x000E #define TYPE_BSS_AP_ORD 0x000F #define TYPE_RAW_BEACON_ENTRY 0x0010 #define TYPE_CALIBRATION_DATA 0x0011 #define TYPE_ROGUE_AP_DATA 0x0012 #define TYPE_ASSOCIATION_REQUEST 0x0013 #define TYPE_REASSOCIATION_REQUEST 0x0014 #define HW_FEATURE_RFKILL 0x0001 #define RF_KILLSWITCH_OFF 1 #define RF_KILLSWITCH_ON 0 #define IPW_COMMAND_POOL_SIZE 40 #define IPW_START_ORD_TAB_1 1 #define IPW_START_ORD_TAB_2 1000 #define IPW_ORD_TAB_1_ENTRY_SIZE sizeof(u32) #define IS_ORDINAL_TABLE_ONE(mgr,id) \ ((id >= IPW_START_ORD_TAB_1) && (id < mgr->table1_size)) #define IS_ORDINAL_TABLE_TWO(mgr,id) \ ((id >= IPW_START_ORD_TAB_2) && (id < (mgr->table2_size + IPW_START_ORD_TAB_2))) #define BSS_ID_LENGTH 6 // Fixed size data: Ordinal Table 1 typedef enum _ORDINAL_TABLE_1 { // NS - means Not Supported by FW // Transmit statistics IPW_ORD_STAT_TX_HOST_REQUESTS = 1, // # of requested Host Tx's (MSDU) IPW_ORD_STAT_TX_HOST_COMPLETE, // # of successful Host Tx's (MSDU) IPW_ORD_STAT_TX_DIR_DATA, // # of successful Directed Tx's (MSDU) IPW_ORD_STAT_TX_DIR_DATA1 = 4, // # of successful Directed Tx's (MSDU) @ 1MB IPW_ORD_STAT_TX_DIR_DATA2, // # of successful Directed Tx's (MSDU) @ 2MB IPW_ORD_STAT_TX_DIR_DATA5_5, // # of successful Directed Tx's (MSDU) @ 5_5MB IPW_ORD_STAT_TX_DIR_DATA11, // # of successful Directed Tx's (MSDU) @ 11MB IPW_ORD_STAT_TX_DIR_DATA22, // # of successful Directed Tx's (MSDU) @ 22MB IPW_ORD_STAT_TX_NODIR_DATA1 = 13, // # of successful Non_Directed Tx's (MSDU) @ 1MB IPW_ORD_STAT_TX_NODIR_DATA2, // # of successful Non_Directed Tx's (MSDU) @ 2MB IPW_ORD_STAT_TX_NODIR_DATA5_5, // # of successful Non_Directed Tx's (MSDU) @ 5.5MB IPW_ORD_STAT_TX_NODIR_DATA11, // # of successful Non_Directed Tx's (MSDU) @ 11MB IPW_ORD_STAT_NULL_DATA = 21, // # of successful NULL data Tx's IPW_ORD_STAT_TX_RTS, // # of successful Tx RTS IPW_ORD_STAT_TX_CTS, // # of successful Tx CTS IPW_ORD_STAT_TX_ACK, // # of successful Tx ACK IPW_ORD_STAT_TX_ASSN, // # of successful Association Tx's IPW_ORD_STAT_TX_ASSN_RESP, // # of successful Association response Tx's IPW_ORD_STAT_TX_REASSN, // # of successful Reassociation Tx's IPW_ORD_STAT_TX_REASSN_RESP, // # of successful Reassociation response Tx's IPW_ORD_STAT_TX_PROBE, // # of probes successfully transmitted IPW_ORD_STAT_TX_PROBE_RESP, // # of probe responses successfully transmitted IPW_ORD_STAT_TX_BEACON, // # of tx beacon IPW_ORD_STAT_TX_ATIM, // # of Tx ATIM IPW_ORD_STAT_TX_DISASSN, // # of successful Disassociation TX IPW_ORD_STAT_TX_AUTH, // # of successful Authentication Tx IPW_ORD_STAT_TX_DEAUTH, // # of successful Deauthentication TX IPW_ORD_STAT_TX_TOTAL_BYTES = 41, // Total successful Tx data bytes IPW_ORD_STAT_TX_RETRIES, // # of Tx retries IPW_ORD_STAT_TX_RETRY1, // # of Tx retries at 1MBPS IPW_ORD_STAT_TX_RETRY2, // # of Tx retries at 2MBPS IPW_ORD_STAT_TX_RETRY5_5, // # of Tx retries at 5.5MBPS IPW_ORD_STAT_TX_RETRY11, // # of Tx retries at 11MBPS IPW_ORD_STAT_TX_FAILURES = 51, // # of Tx Failures IPW_ORD_STAT_TX_ABORT_AT_HOP, //NS // # of Tx's aborted at hop time IPW_ORD_STAT_TX_MAX_TRIES_IN_HOP, // # of times max tries in a hop failed IPW_ORD_STAT_TX_ABORT_LATE_DMA, //NS // # of times tx aborted due to late dma setup IPW_ORD_STAT_TX_ABORT_STX, //NS // # of times backoff aborted IPW_ORD_STAT_TX_DISASSN_FAIL, // # of times disassociation failed IPW_ORD_STAT_TX_ERR_CTS, // # of missed/bad CTS frames IPW_ORD_STAT_TX_BPDU, //NS // # of spanning tree BPDUs sent IPW_ORD_STAT_TX_ERR_ACK, // # of tx err due to acks // Receive statistics IPW_ORD_STAT_RX_HOST = 61, // # of packets passed to host IPW_ORD_STAT_RX_DIR_DATA, // # of directed packets IPW_ORD_STAT_RX_DIR_DATA1, // # of directed packets at 1MB IPW_ORD_STAT_RX_DIR_DATA2, // # of directed packets at 2MB IPW_ORD_STAT_RX_DIR_DATA5_5, // # of directed packets at 5.5MB IPW_ORD_STAT_RX_DIR_DATA11, // # of directed packets at 11MB IPW_ORD_STAT_RX_DIR_DATA22, // # of directed packets at 22MB IPW_ORD_STAT_RX_NODIR_DATA = 71, // # of nondirected packets IPW_ORD_STAT_RX_NODIR_DATA1, // # of nondirected packets at 1MB IPW_ORD_STAT_RX_NODIR_DATA2, // # of nondirected packets at 2MB IPW_ORD_STAT_RX_NODIR_DATA5_5, // # of nondirected packets at 5.5MB IPW_ORD_STAT_RX_NODIR_DATA11, // # of nondirected packets at 11MB IPW_ORD_STAT_RX_NULL_DATA = 80, // # of null data rx's IPW_ORD_STAT_RX_POLL, //NS // # of poll rx IPW_ORD_STAT_RX_RTS, // # of Rx RTS IPW_ORD_STAT_RX_CTS, // # of Rx CTS IPW_ORD_STAT_RX_ACK, // # of Rx ACK IPW_ORD_STAT_RX_CFEND, // # of Rx CF End IPW_ORD_STAT_RX_CFEND_ACK, // # of Rx CF End + CF Ack IPW_ORD_STAT_RX_ASSN, // # of Association Rx's IPW_ORD_STAT_RX_ASSN_RESP, // # of Association response Rx's IPW_ORD_STAT_RX_REASSN, // # of Reassociation Rx's IPW_ORD_STAT_RX_REASSN_RESP, // # of Reassociation response Rx's IPW_ORD_STAT_RX_PROBE, // # of probe Rx's IPW_ORD_STAT_RX_PROBE_RESP, // # of probe response Rx's IPW_ORD_STAT_RX_BEACON, // # of Rx beacon IPW_ORD_STAT_RX_ATIM, // # of Rx ATIM IPW_ORD_STAT_RX_DISASSN, // # of disassociation Rx IPW_ORD_STAT_RX_AUTH, // # of authentication Rx IPW_ORD_STAT_RX_DEAUTH, // # of deauthentication Rx IPW_ORD_STAT_RX_TOTAL_BYTES = 101, // Total rx data bytes received IPW_ORD_STAT_RX_ERR_CRC, // # of packets with Rx CRC error IPW_ORD_STAT_RX_ERR_CRC1, // # of Rx CRC errors at 1MB IPW_ORD_STAT_RX_ERR_CRC2, // # of Rx CRC errors at 2MB IPW_ORD_STAT_RX_ERR_CRC5_5, // # of Rx CRC errors at 5.5MB IPW_ORD_STAT_RX_ERR_CRC11, // # of Rx CRC errors at 11MB IPW_ORD_STAT_RX_DUPLICATE1 = 112, // # of duplicate rx packets at 1MB IPW_ORD_STAT_RX_DUPLICATE2, // # of duplicate rx packets at 2MB IPW_ORD_STAT_RX_DUPLICATE5_5, // # of duplicate rx packets at 5.5MB IPW_ORD_STAT_RX_DUPLICATE11, // # of duplicate rx packets at 11MB IPW_ORD_STAT_RX_DUPLICATE = 119, // # of duplicate rx packets IPW_ORD_PERS_DB_LOCK = 120, // # locking fw permanent db IPW_ORD_PERS_DB_SIZE, // # size of fw permanent db IPW_ORD_PERS_DB_ADDR, // # address of fw permanent db IPW_ORD_STAT_RX_INVALID_PROTOCOL, // # of rx frames with invalid protocol IPW_ORD_SYS_BOOT_TIME, // # Boot time IPW_ORD_STAT_RX_NO_BUFFER, // # of rx frames rejected due to no buffer IPW_ORD_STAT_RX_ABORT_LATE_DMA, //NS // # of rx frames rejected due to dma setup too late IPW_ORD_STAT_RX_ABORT_AT_HOP, //NS // # of rx frames aborted due to hop IPW_ORD_STAT_RX_MISSING_FRAG, // # of rx frames dropped due to missing fragment IPW_ORD_STAT_RX_ORPHAN_FRAG, // # of rx frames dropped due to non-sequential fragment IPW_ORD_STAT_RX_ORPHAN_FRAME, // # of rx frames dropped due to unmatched 1st frame IPW_ORD_STAT_RX_FRAG_AGEOUT, // # of rx frames dropped due to uncompleted frame IPW_ORD_STAT_RX_BAD_SSID, //NS // Bad SSID (unused) IPW_ORD_STAT_RX_ICV_ERRORS, // # of ICV errors during decryption // PSP Statistics IPW_ORD_STAT_PSP_SUSPENSION = 137, // # of times adapter suspended IPW_ORD_STAT_PSP_BCN_TIMEOUT, // # of beacon timeout IPW_ORD_STAT_PSP_POLL_TIMEOUT, // # of poll response timeouts IPW_ORD_STAT_PSP_NONDIR_TIMEOUT, // # of timeouts waiting for last broadcast/muticast pkt IPW_ORD_STAT_PSP_RX_DTIMS, // # of PSP DTIMs received IPW_ORD_STAT_PSP_RX_TIMS, // # of PSP TIMs received IPW_ORD_STAT_PSP_STATION_ID, // PSP Station ID // Association and roaming IPW_ORD_LAST_ASSN_TIME = 147, // RTC time of last association IPW_ORD_STAT_PERCENT_MISSED_BCNS, // current calculation of % missed beacons IPW_ORD_STAT_PERCENT_RETRIES, // current calculation of % missed tx retries IPW_ORD_ASSOCIATED_AP_PTR, // If associated, this is ptr to the associated // AP table entry. set to 0 if not associated IPW_ORD_AVAILABLE_AP_CNT, // # of AP's decsribed in the AP table IPW_ORD_AP_LIST_PTR, // Ptr to list of available APs IPW_ORD_STAT_AP_ASSNS, // # of associations IPW_ORD_STAT_ASSN_FAIL, // # of association failures IPW_ORD_STAT_ASSN_RESP_FAIL, // # of failuresdue to response fail IPW_ORD_STAT_FULL_SCANS, // # of full scans IPW_ORD_CARD_DISABLED, // # Card Disabled IPW_ORD_STAT_ROAM_INHIBIT, // # of times roaming was inhibited due to ongoing activity IPW_FILLER_40, IPW_ORD_RSSI_AT_ASSN = 160, // RSSI of associated AP at time of association IPW_ORD_STAT_ASSN_CAUSE1, // # of reassociations due to no tx from AP in last N // hops or no prob_ responses in last 3 minutes IPW_ORD_STAT_ASSN_CAUSE2, // # of reassociations due to poor tx/rx quality IPW_ORD_STAT_ASSN_CAUSE3, // # of reassociations due to tx/rx quality with excessive // load at the AP IPW_ORD_STAT_ASSN_CAUSE4, // # of reassociations due to AP RSSI level fell below // eligible group IPW_ORD_STAT_ASSN_CAUSE5, // # of reassociations due to load leveling IPW_ORD_STAT_ASSN_CAUSE6, //NS // # of reassociations due to dropped by Ap IPW_FILLER_41, IPW_FILLER_42, IPW_FILLER_43, IPW_ORD_STAT_AUTH_FAIL, // # of times authentication failed IPW_ORD_STAT_AUTH_RESP_FAIL, // # of times authentication response failed IPW_ORD_STATION_TABLE_CNT, // # of entries in association table // Other statistics IPW_ORD_RSSI_AVG_CURR = 173, // Current avg RSSI IPW_ORD_STEST_RESULTS_CURR, //NS // Current self test results word IPW_ORD_STEST_RESULTS_CUM, //NS // Cummulative self test results word IPW_ORD_SELF_TEST_STATUS, //NS // IPW_ORD_POWER_MGMT_MODE, // Power mode - 0=CAM, 1=PSP IPW_ORD_POWER_MGMT_INDEX, //NS // IPW_ORD_COUNTRY_CODE, // IEEE country code as recv'd from beacon IPW_ORD_COUNTRY_CHANNELS, // channels suported by country // IPW_ORD_COUNTRY_CHANNELS: // For 11b the lower 2-byte are used for channels from 1-14 // and the higher 2-byte are not used. IPW_ORD_RESET_CNT, // # of adapter resets (warm) IPW_ORD_BEACON_INTERVAL, // Beacon interval IPW_ORD_PRINCETON_VERSION = 184, //NS // Princeton Version IPW_ORD_ANTENNA_DIVERSITY, // TRUE if antenna diversity is disabled IPW_ORD_CCA_RSSI, //NS // CCA RSSI value (factory programmed) IPW_ORD_STAT_EEPROM_UPDATE, //NS // # of times config EEPROM updated IPW_ORD_DTIM_PERIOD, // # of beacon intervals between DTIMs IPW_ORD_OUR_FREQ, // current radio freq lower digits - channel ID IPW_ORD_RTC_TIME = 190, // current RTC time IPW_ORD_PORT_TYPE, // operating mode IPW_ORD_CURRENT_TX_RATE, // current tx rate IPW_ORD_SUPPORTED_RATES, // Bitmap of supported tx rates IPW_ORD_ATIM_WINDOW, // current ATIM Window IPW_ORD_BASIC_RATES, // bitmap of basic tx rates IPW_ORD_NIC_HIGHEST_RATE, // bitmap of basic tx rates IPW_ORD_AP_HIGHEST_RATE, // bitmap of basic tx rates IPW_ORD_CAPABILITIES, // Management frame capability field IPW_ORD_AUTH_TYPE, // Type of authentication IPW_ORD_RADIO_TYPE, // Adapter card platform type IPW_ORD_RTS_THRESHOLD = 201, // Min length of packet after which RTS handshaking is used IPW_ORD_INT_MODE, // International mode IPW_ORD_FRAGMENTATION_THRESHOLD, // protocol frag threshold IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, // EEPROM offset in SRAM IPW_ORD_EEPROM_SRAM_DB_BLOCK_SIZE, // EEPROM size in SRAM IPW_ORD_EEPROM_SKU_CAPABILITY, // EEPROM SKU Capability 206 = IPW_ORD_EEPROM_IBSS_11B_CHANNELS, // EEPROM IBSS 11b channel set IPW_ORD_MAC_VERSION = 209, // MAC Version IPW_ORD_MAC_REVISION, // MAC Revision IPW_ORD_RADIO_VERSION, // Radio Version IPW_ORD_NIC_MANF_DATE_TIME, // MANF Date/Time STAMP IPW_ORD_UCODE_VERSION, // Ucode Version IPW_ORD_HW_RF_SWITCH_STATE = 214, // HW RF Kill Switch State } ORDINALTABLE1; // ordinal table 2 // Variable length data: #define IPW_FIRST_VARIABLE_LENGTH_ORDINAL 1001 typedef enum _ORDINAL_TABLE_2 { // NS - means Not Supported by FW IPW_ORD_STAT_BASE = 1000, // contains number of variable ORDs IPW_ORD_STAT_ADAPTER_MAC = 1001, // 6 bytes: our adapter MAC address IPW_ORD_STAT_PREFERRED_BSSID = 1002, // 6 bytes: BSSID of the preferred AP IPW_ORD_STAT_MANDATORY_BSSID = 1003, // 6 bytes: BSSID of the mandatory AP IPW_FILL_1, //NS // IPW_ORD_STAT_COUNTRY_TEXT = 1005, // 36 bytes: Country name text, First two bytes are Country code IPW_ORD_STAT_ASSN_SSID = 1006, // 32 bytes: ESSID String IPW_ORD_STATION_TABLE = 1007, // ? bytes: Station/AP table (via Direct SSID Scans) IPW_ORD_STAT_SWEEP_TABLE = 1008, // ? bytes: Sweep/Host Table table (via Broadcast Scans) IPW_ORD_STAT_ROAM_LOG = 1009, // ? bytes: Roaming log IPW_ORD_STAT_RATE_LOG = 1010, //NS // 0 bytes: Rate log IPW_ORD_STAT_FIFO = 1011, //NS // 0 bytes: Fifo buffer data structures IPW_ORD_STAT_FW_VER_NUM = 1012, // 14 bytes: fw version ID string as in (a.bb.ccc; "0.08.011") IPW_ORD_STAT_FW_DATE = 1013, // 14 bytes: fw date string (mmm dd yyyy; "Mar 13 2002") IPW_ORD_STAT_ASSN_AP_BSSID = 1014, // 6 bytes: MAC address of associated AP IPW_ORD_STAT_DEBUG = 1015, //NS // ? bytes: IPW_ORD_STAT_NIC_BPA_NUM = 1016, // 11 bytes: NIC BPA number in ASCII IPW_ORD_STAT_UCODE_DATE = 1017, // 5 bytes: uCode date IPW_ORD_SECURITY_NGOTIATION_RESULT = 1018, } ORDINALTABLE2; // NS - means Not Supported by FW #define IPW_LAST_VARIABLE_LENGTH_ORDINAL 1018 #ifndef WIRELESS_SPY #define WIRELESS_SPY // enable iwspy support #endif #define IPW_HOST_FW_SHARED_AREA0 0x0002f200 #define IPW_HOST_FW_SHARED_AREA0_END 0x0002f510 // 0x310 bytes #define IPW_HOST_FW_SHARED_AREA1 0x0002f610 #define IPW_HOST_FW_SHARED_AREA1_END 0x0002f630 // 0x20 bytes #define IPW_HOST_FW_SHARED_AREA2 0x0002fa00 #define IPW_HOST_FW_SHARED_AREA2_END 0x0002fa20 // 0x20 bytes #define IPW_HOST_FW_SHARED_AREA3 0x0002fc00 #define IPW_HOST_FW_SHARED_AREA3_END 0x0002fc10 // 0x10 bytes #define IPW_HOST_FW_INTERRUPT_AREA 0x0002ff80 #define IPW_HOST_FW_INTERRUPT_AREA_END 0x00030000 // 0x80 bytes struct ipw2100_fw_chunk { unsigned char *buf; long len; long pos; struct list_head list; }; struct ipw2100_fw_chunk_set { const void *data; unsigned long size; }; struct ipw2100_fw { int version; struct ipw2100_fw_chunk_set fw; struct ipw2100_fw_chunk_set uc; const struct firmware *fw_entry; }; #define MAX_FW_VERSION_LEN 14 #endif /* _IPW2100_H */ compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/Makefile0000644000175000017500000000037712026211315023341 0ustar mcgrofmcgrof# # Makefile for the Intel Centrino wireless drivers # obj-$(CONFIG_IPW2100) += ipw2100.o obj-$(CONFIG_IPW2200) += ipw2200.o obj-$(CONFIG_LIBIPW) += libipw.o libipw-objs := \ libipw_module.o \ libipw_tx.o \ libipw_rx.o \ libipw_wx.o \ libipw_geo.o compat-drivers-2012-09-18/drivers/net/wireless/ipw2x00/Kconfig0000644000175000017500000001530512026211315023201 0ustar mcgrofmcgrof# # Intel Centrino wireless drivers # config IPW2100 tristate "Intel PRO/Wireless 2100 Network Connection" depends on PCI && CFG80211 select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV select FW_LOADER select LIB80211 select LIBIPW ---help--- A driver for the Intel PRO/Wireless 2100 Network Connection 802.11b wireless network adapter. See for information on the capabilities currently enabled in this driver and for tips for debugging issues and problems. In order to use this driver, you will need a firmware image for it. You can obtain the firmware from . Once you have the firmware image, you will need to place it in /lib/firmware. You will also very likely need the Wireless Tools in order to configure your card: . It is recommended that you compile this driver as a module (M) rather than built-in (Y). This driver requires firmware at device initialization time, and when built-in this typically happens before the filesystem is accessible (hence firmware will be unavailable and initialization will fail). If you do choose to build this driver into your kernel image, you can avoid this problem by including the firmware and a firmware loader in an initramfs. config IPW2100_MONITOR bool "Enable promiscuous mode" depends on IPW2100 ---help--- Enables promiscuous/monitor mode support for the ipw2100 driver. With this feature compiled into the driver, you can switch to promiscuous mode via the Wireless Tool's Monitor mode. While in this mode, no packets can be sent. config IPW2100_DEBUG bool "Enable full debugging output in IPW2100 module." depends on IPW2100 ---help--- This option will enable debug tracing output for the IPW2100. This will result in the kernel module being ~60k larger. You can control which debug output is sent to the kernel log by setting the value in /sys/bus/pci/drivers/ipw2100/debug_level This entry will only exist if this option is enabled. If you are not trying to debug or develop the IPW2100 driver, you most likely want to say N here. config IPW2200 tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" depends on PCI && CFG80211 && CFG80211_WEXT select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV select FW_LOADER select LIB80211 select LIBIPW ---help--- A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network Connection adapters. See for information on the capabilities currently enabled in this driver and for tips for debugging issues and problems. In order to use this driver, you will need a firmware image for it. You can obtain the firmware from . See the above referenced README.ipw2200 for information on where to install the firmware images. You will also very likely need the Wireless Tools in order to configure your card: . It is recommended that you compile this driver as a module (M) rather than built-in (Y). This driver requires firmware at device initialization time, and when built-in this typically happens before the filesystem is accessible (hence firmware will be unavailable and initialization will fail). If you do choose to build this driver into your kernel image, you can avoid this problem by including the firmware and a firmware loader in an initramfs. config IPW2200_MONITOR bool "Enable promiscuous mode" depends on IPW2200 ---help--- Enables promiscuous/monitor mode support for the ipw2200 driver. With this feature compiled into the driver, you can switch to promiscuous mode via the Wireless Tool's Monitor mode. While in this mode, no packets can be sent. config IPW2200_RADIOTAP bool "Enable radiotap format 802.11 raw packet support" depends on IPW2200_MONITOR config IPW2200_PROMISCUOUS bool "Enable creation of a RF radiotap promiscuous interface" depends on IPW2200_MONITOR select IPW2200_RADIOTAP ---help--- Enables the creation of a second interface prefixed 'rtap'. This second interface will provide every received in radiotap format. This is useful for performing wireless network analysis while maintaining an active association. Example usage: % modprobe ipw2200 rtap_iface=1 % ifconfig rtap0 up % tethereal -i rtap0 If you do not specify 'rtap_iface=1' as a module parameter then the rtap interface will not be created and you will need to turn it on via sysfs: % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface config IPW2200_QOS bool "Enable QoS support" depends on IPW2200 && EXPERIMENTAL config IPW2200_DEBUG bool "Enable full debugging output in IPW2200 module." depends on IPW2200 ---help--- This option will enable low level debug tracing output for IPW2200. Note, normal debug code is already compiled in. This low level debug option enables debug on hot paths (e.g Tx, Rx, ISR) and will result in the kernel module being ~70 larger. Most users will typically not need this high verbosity debug information. If you are not sure, say N here. config LIBIPW tristate depends on PCI && CFG80211 select WIRELESS_EXT select WEXT_SPY select CRYPTO select CRYPTO_ARC4 select CRYPTO_ECB select CRYPTO_AES select CRYPTO_MICHAEL_MIC select CRYPTO_ECB select CRC32 select LIB80211 select LIB80211_CRYPT_WEP select LIB80211_CRYPT_TKIP select LIB80211_CRYPT_CCMP ---help--- This option enables the hardware independent IEEE 802.11 networking stack. This component is deprecated in favor of the mac80211 component. config LIBIPW_DEBUG bool "Full debugging output for the LIBIPW component" depends on LIBIPW ---help--- This option will enable debug tracing output for the libipw component. This will result in the kernel module being ~70k larger. You can control which debug output is sent to the kernel log by setting the value in /proc/net/ieee80211/debug_level For example: % echo 0x00000FFO > /proc/net/ieee80211/debug_level For a list of values you can assign to debug_level, you can look at the bit mask values in ieee80211.h If you are not trying to debug or develop the libipw component, you most likely want to say N here. compat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/0000755000175000017500000000000012026211315022757 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/if_usb.c0000644000175000017500000005563312026211315024406 0ustar mcgrofmcgrof/* * Copyright (C) 2008, cozybit Inc. * Copyright (C) 2003-2006, Marvell International Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #define DRV_NAME "lbtf_usb" #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "libertas_tf.h" #include "if_usb.h" #include #include #include #include #include #include #include #define INSANEDEBUG 0 #define lbtf_deb_usb2(...) do { if (INSANEDEBUG) lbtf_deb_usbd(__VA_ARGS__); } while (0) #define MESSAGE_HEADER_LEN 4 static char *lbtf_fw_name = "lbtf_usb.bin"; module_param_named(fw_name, lbtf_fw_name, charp, 0644); MODULE_FIRMWARE("lbtf_usb.bin"); static struct usb_device_id if_usb_table[] = { /* Enter the device signature inside */ { USB_DEVICE(0x1286, 0x2001) }, { USB_DEVICE(0x05a3, 0x8388) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, if_usb_table); static void if_usb_receive(struct urb *urb); static void if_usb_receive_fwload(struct urb *urb); static int if_usb_prog_firmware(struct if_usb_card *cardp); static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, uint8_t *payload, uint16_t nb); static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb, u8 data); static void if_usb_free(struct if_usb_card *cardp); static int if_usb_submit_rx_urb(struct if_usb_card *cardp); static int if_usb_reset_device(struct if_usb_card *cardp); /** * if_usb_wrike_bulk_callback - call back to handle URB status * * @param urb pointer to urb structure */ static void if_usb_write_bulk_callback(struct urb *urb) { if (urb->status != 0) { /* print the failure status number for debug */ pr_info("URB in failure status: %d\n", urb->status); } else { lbtf_deb_usb2(&urb->dev->dev, "URB status is successful\n"); lbtf_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", urb->actual_length); } } /** * if_usb_free - free tx/rx urb, skb and rx buffer * * @param cardp pointer if_usb_card */ static void if_usb_free(struct if_usb_card *cardp) { lbtf_deb_enter(LBTF_DEB_USB); /* Unlink tx & rx urb */ usb_kill_urb(cardp->tx_urb); usb_kill_urb(cardp->rx_urb); usb_kill_urb(cardp->cmd_urb); usb_free_urb(cardp->tx_urb); cardp->tx_urb = NULL; usb_free_urb(cardp->rx_urb); cardp->rx_urb = NULL; usb_free_urb(cardp->cmd_urb); cardp->cmd_urb = NULL; kfree(cardp->ep_out_buf); cardp->ep_out_buf = NULL; lbtf_deb_leave(LBTF_DEB_USB); } static void if_usb_setup_firmware(struct lbtf_private *priv) { struct if_usb_card *cardp = priv->card; struct cmd_ds_set_boot2_ver b2_cmd; lbtf_deb_enter(LBTF_DEB_USB); if_usb_submit_rx_urb(cardp); b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); b2_cmd.action = 0; b2_cmd.version = cardp->boot2_version; if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) lbtf_deb_usb("Setting boot2 version failed\n"); lbtf_deb_leave(LBTF_DEB_USB); } static void if_usb_fw_timeo(unsigned long priv) { struct if_usb_card *cardp = (void *)priv; lbtf_deb_enter(LBTF_DEB_USB); if (!cardp->fwdnldover) { /* Download timed out */ cardp->priv->surpriseremoved = 1; pr_err("Download timed out\n"); } else { lbtf_deb_usb("Download complete, no event. Assuming success\n"); } wake_up(&cardp->fw_wq); lbtf_deb_leave(LBTF_DEB_USB); } /** * if_usb_probe - sets the configuration values * * @ifnum interface number * @id pointer to usb_device_id * * Returns: 0 on success, error code on failure */ static int if_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; struct lbtf_private *priv; struct if_usb_card *cardp; int i; lbtf_deb_enter(LBTF_DEB_USB); udev = interface_to_usbdev(intf); cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); if (!cardp) goto error; setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); init_waitqueue_head(&cardp->fw_wq); cardp->udev = udev; iface_desc = intf->cur_altsetting; lbtf_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", le16_to_cpu(udev->descriptor.bcdUSB), udev->descriptor.bDeviceClass, udev->descriptor.bDeviceSubClass, udev->descriptor.bDeviceProtocol); for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(endpoint)) { cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); cardp->ep_in = usb_endpoint_num(endpoint); lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); } else if (usb_endpoint_is_bulk_out(endpoint)) { cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); cardp->ep_out = usb_endpoint_num(endpoint); lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); } } if (!cardp->ep_out_size || !cardp->ep_in_size) { lbtf_deb_usbd(&udev->dev, "Endpoints not found\n"); /* Endpoints not found */ goto dealloc; } cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!cardp->rx_urb) { lbtf_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); goto dealloc; } cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!cardp->tx_urb) { lbtf_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); goto dealloc; } cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL); if (!cardp->cmd_urb) { lbtf_deb_usbd(&udev->dev, "Cmd URB allocation failed\n"); goto dealloc; } cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); if (!cardp->ep_out_buf) { lbtf_deb_usbd(&udev->dev, "Could not allocate buffer\n"); goto dealloc; } priv = lbtf_add_card(cardp, &udev->dev); if (!priv) goto dealloc; cardp->priv = priv; priv->hw_host_to_card = if_usb_host_to_card; priv->hw_prog_firmware = if_usb_prog_firmware; priv->hw_reset_device = if_usb_reset_device; cardp->boot2_version = udev->descriptor.bcdDevice; usb_get_dev(udev); usb_set_intfdata(intf, cardp); return 0; dealloc: if_usb_free(cardp); error: lbtf_deb_leave(LBTF_DEB_MAIN); return -ENOMEM; } /** * if_usb_disconnect - free resource and cleanup * * @intf USB interface structure */ static void if_usb_disconnect(struct usb_interface *intf) { struct if_usb_card *cardp = usb_get_intfdata(intf); struct lbtf_private *priv = cardp->priv; lbtf_deb_enter(LBTF_DEB_MAIN); if_usb_reset_device(cardp); if (priv) lbtf_remove_card(priv); /* Unlink and free urb */ if_usb_free(cardp); usb_set_intfdata(intf, NULL); usb_put_dev(interface_to_usbdev(intf)); lbtf_deb_leave(LBTF_DEB_MAIN); } /** * if_usb_send_fw_pkt - This function downloads the FW * * @priv pointer to struct lbtf_private * * Returns: 0 */ static int if_usb_send_fw_pkt(struct if_usb_card *cardp) { struct fwdata *fwdata = cardp->ep_out_buf; u8 *firmware = (u8 *) cardp->fw->data; lbtf_deb_enter(LBTF_DEB_FW); /* If we got a CRC failure on the last block, back up and retry it */ if (!cardp->CRC_OK) { cardp->totalbytes = cardp->fwlastblksent; cardp->fwseqnum--; } lbtf_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", cardp->totalbytes); /* struct fwdata (which we sent to the card) has an extra __le32 field in between the header and the data, which is not in the struct fwheader in the actual firmware binary. Insert the seqnum in the middle... */ memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], sizeof(struct fwheader)); cardp->fwlastblksent = cardp->totalbytes; cardp->totalbytes += sizeof(struct fwheader); memcpy(fwdata->data, &firmware[cardp->totalbytes], le32_to_cpu(fwdata->hdr.datalength)); lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n", le32_to_cpu(fwdata->hdr.datalength)); fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + le32_to_cpu(fwdata->hdr.datalength), 0); if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); lbtf_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, cardp->totalbytes); } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { lbtf_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); /* Host has finished FW downloading * Donwloading FW JUMP BLOCK */ cardp->fwfinalblk = 1; } lbtf_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", cardp->totalbytes); lbtf_deb_leave(LBTF_DEB_FW); return 0; } static int if_usb_reset_device(struct if_usb_card *cardp) { struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4; int ret; lbtf_deb_enter(LBTF_DEB_USB); *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET); cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset)); cmd->hdr.result = cpu_to_le16(0); cmd->hdr.seqnum = cpu_to_le16(0x5a5a); cmd->action = cpu_to_le16(CMD_ACT_HALT); usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_ds_802_11_reset), 0); msleep(100); ret = usb_reset_device(cardp->udev); msleep(100); lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(if_usb_reset_device); /** * usb_tx_block - transfer data to the device * * @priv pointer to struct lbtf_private * @payload pointer to payload data * @nb data length * @data non-zero for data, zero for commands * * Returns: 0 on success, nonzero otherwise. */ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb, u8 data) { int ret = -1; struct urb *urb; lbtf_deb_enter(LBTF_DEB_USB); /* check if device is removed */ if (cardp->priv->surpriseremoved) { lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n"); goto tx_ret; } if (data) urb = cardp->tx_urb; else urb = cardp->cmd_urb; usb_fill_bulk_urb(urb, cardp->udev, usb_sndbulkpipe(cardp->udev, cardp->ep_out), payload, nb, if_usb_write_bulk_callback, cardp); urb->transfer_flags |= URB_ZERO_PACKET; if (usb_submit_urb(urb, GFP_ATOMIC)) { lbtf_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); goto tx_ret; } lbtf_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); ret = 0; tx_ret: lbtf_deb_leave(LBTF_DEB_USB); return ret; } static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, void (*callbackfn)(struct urb *urb)) { struct sk_buff *skb; int ret = -1; lbtf_deb_enter(LBTF_DEB_USB); skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); if (!skb) { pr_err("No free skb\n"); lbtf_deb_leave(LBTF_DEB_USB); return -1; } cardp->rx_skb = skb; /* Fill the receive configuration URB and initialise the Rx call back */ usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, usb_rcvbulkpipe(cardp->udev, cardp->ep_in), skb_tail_pointer(skb), MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC); if (ret) { lbtf_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); kfree_skb(skb); cardp->rx_skb = NULL; lbtf_deb_leave(LBTF_DEB_USB); return -1; } else { lbtf_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); lbtf_deb_leave(LBTF_DEB_USB); return 0; } } static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) { return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); } static int if_usb_submit_rx_urb(struct if_usb_card *cardp) { return __if_usb_submit_rx_urb(cardp, &if_usb_receive); } static void if_usb_receive_fwload(struct urb *urb) { struct if_usb_card *cardp = urb->context; struct sk_buff *skb = cardp->rx_skb; struct fwsyncheader *syncfwheader; struct bootcmdresp bcmdresp; lbtf_deb_enter(LBTF_DEB_USB); if (urb->status) { lbtf_deb_usbd(&cardp->udev->dev, "URB status is failed during fw load\n"); kfree_skb(skb); lbtf_deb_leave(LBTF_DEB_USB); return; } if (cardp->fwdnldover) { __le32 *tmp = (__le32 *)(skb->data); if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { /* Firmware ready event received */ pr_info("Firmware ready event received\n"); wake_up(&cardp->fw_wq); } else { lbtf_deb_usb("Waiting for confirmation; got %x %x\n", le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); if_usb_submit_rx_urb_fwload(cardp); } kfree_skb(skb); lbtf_deb_leave(LBTF_DEB_USB); return; } if (cardp->bootcmdresp <= 0) { memcpy(&bcmdresp, skb->data, sizeof(bcmdresp)); if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { kfree_skb(skb); if_usb_submit_rx_urb_fwload(cardp); cardp->bootcmdresp = 1; /* Received valid boot command response */ lbtf_deb_usbd(&cardp->udev->dev, "Received valid boot command response\n"); lbtf_deb_leave(LBTF_DEB_USB); return; } if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { if (!cardp->bootcmdresp) pr_info("Firmware already seems alive; resetting\n"); cardp->bootcmdresp = -1; } else { pr_info("boot cmd response wrong magic number (0x%x)\n", le32_to_cpu(bcmdresp.magic)); } } else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) { pr_info("boot cmd response cmd_tag error (%d)\n", bcmdresp.cmd); } else if (bcmdresp.result != BOOT_CMD_RESP_OK) { pr_info("boot cmd response result error (%d)\n", bcmdresp.result); } else { cardp->bootcmdresp = 1; lbtf_deb_usbd(&cardp->udev->dev, "Received valid boot command response\n"); } kfree_skb(skb); if_usb_submit_rx_urb_fwload(cardp); lbtf_deb_leave(LBTF_DEB_USB); return; } syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader), GFP_ATOMIC); if (!syncfwheader) { lbtf_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); kfree_skb(skb); lbtf_deb_leave(LBTF_DEB_USB); return; } if (!syncfwheader->cmd) { lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", le32_to_cpu(syncfwheader->seqnum)); cardp->CRC_OK = 1; } else { lbtf_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); cardp->CRC_OK = 0; } kfree_skb(skb); /* reschedule timer for 200ms hence */ mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); if (cardp->fwfinalblk) { cardp->fwdnldover = 1; goto exit; } if_usb_send_fw_pkt(cardp); exit: if_usb_submit_rx_urb_fwload(cardp); kfree(syncfwheader); lbtf_deb_leave(LBTF_DEB_USB); } #define MRVDRV_MIN_PKT_LEN 30 static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, struct if_usb_card *cardp, struct lbtf_private *priv) { if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { lbtf_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); kfree_skb(skb); return; } skb_put(skb, recvlength); skb_pull(skb, MESSAGE_HEADER_LEN); lbtf_rx(priv, skb); } static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, struct sk_buff *skb, struct if_usb_card *cardp, struct lbtf_private *priv) { if (recvlength > LBS_CMD_BUFFER_SIZE) { lbtf_deb_usbd(&cardp->udev->dev, "The receive buffer is too large\n"); kfree_skb(skb); return; } BUG_ON(!in_interrupt()); spin_lock(&priv->driver_lock); memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN, recvlength - MESSAGE_HEADER_LEN); kfree_skb(skb); lbtf_cmd_response_rx(priv); spin_unlock(&priv->driver_lock); } /** * if_usb_receive - read data received from the device. * * @urb pointer to struct urb */ static void if_usb_receive(struct urb *urb) { struct if_usb_card *cardp = urb->context; struct sk_buff *skb = cardp->rx_skb; struct lbtf_private *priv = cardp->priv; int recvlength = urb->actual_length; uint8_t *recvbuff = NULL; uint32_t recvtype = 0; __le32 *pkt = (__le32 *) skb->data; lbtf_deb_enter(LBTF_DEB_USB); if (recvlength) { if (urb->status) { lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", urb->status); kfree_skb(skb); goto setup_for_next; } recvbuff = skb->data; recvtype = le32_to_cpu(pkt[0]); lbtf_deb_usbd(&cardp->udev->dev, "Recv length = 0x%x, Recv type = 0x%X\n", recvlength, recvtype); } else if (urb->status) { kfree_skb(skb); lbtf_deb_leave(LBTF_DEB_USB); return; } switch (recvtype) { case CMD_TYPE_DATA: process_cmdtypedata(recvlength, skb, cardp, priv); break; case CMD_TYPE_REQUEST: process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); break; case CMD_TYPE_INDICATION: { /* Event cause handling */ u32 event_cause = le32_to_cpu(pkt[1]); lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event_cause); /* Icky undocumented magic special case */ if (event_cause & 0xffff0000) { u16 tmp; u8 retrycnt; u8 failure; tmp = event_cause >> 16; retrycnt = tmp & 0x00ff; failure = (tmp & 0xff00) >> 8; lbtf_send_tx_feedback(priv, retrycnt, failure); } else if (event_cause == LBTF_EVENT_BCN_SENT) lbtf_bcn_sent(priv); else lbtf_deb_usbd(&cardp->udev->dev, "Unsupported notification %d received\n", event_cause); kfree_skb(skb); break; } default: lbtf_deb_usbd(&cardp->udev->dev, "libertastf: unknown command type 0x%X\n", recvtype); kfree_skb(skb); break; } setup_for_next: if_usb_submit_rx_urb(cardp); lbtf_deb_leave(LBTF_DEB_USB); } /** * if_usb_host_to_card - Download data to the device * * @priv pointer to struct lbtf_private structure * @type type of data * @buf pointer to data buffer * @len number of bytes * * Returns: 0 on success, nonzero otherwise */ static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, uint8_t *payload, uint16_t nb) { struct if_usb_card *cardp = priv->card; u8 data = 0; lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type); lbtf_deb_usbd(&cardp->udev->dev, "size after = %d\n", nb); if (type == MVMS_CMD) { *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); } else { *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); data = 1; } memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN, data); } /** * if_usb_issue_boot_command - Issue boot command to Boot2. * * @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM. * * Returns: 0 */ static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) { struct bootcmd *bootcmd = cardp->ep_out_buf; /* Prepare command */ bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); bootcmd->cmd = ivalue; memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); /* Issue command */ usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0); return 0; } /** * check_fwfile_format - Check the validity of Boot2/FW image. * * @data pointer to image * @totlen image length * * Returns: 0 if the image is valid, nonzero otherwise. */ static int check_fwfile_format(const u8 *data, u32 totlen) { u32 bincmd, exit; u32 blksize, offset, len; int ret; ret = 1; exit = len = 0; do { struct fwheader *fwh = (void *) data; bincmd = le32_to_cpu(fwh->dnldcmd); blksize = le32_to_cpu(fwh->datalength); switch (bincmd) { case FW_HAS_DATA_TO_RECV: offset = sizeof(struct fwheader) + blksize; data += offset; len += offset; if (len >= totlen) exit = 1; break; case FW_HAS_LAST_BLOCK: exit = 1; ret = 0; break; default: exit = 1; break; } } while (!exit); if (ret) pr_err("firmware file format check FAIL\n"); else lbtf_deb_fw("firmware file format check PASS\n"); return ret; } static int if_usb_prog_firmware(struct if_usb_card *cardp) { int i = 0; static int reset_count = 10; int ret = 0; lbtf_deb_enter(LBTF_DEB_USB); kparam_block_sysfs_write(fw_name); ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev); if (ret < 0) { pr_err("request_firmware() failed with %#x\n", ret); pr_err("firmware %s not found\n", lbtf_fw_name); kparam_unblock_sysfs_write(fw_name); goto done; } kparam_unblock_sysfs_write(fw_name); if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) goto release_fw; restart: if (if_usb_submit_rx_urb_fwload(cardp) < 0) { lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); ret = -1; goto release_fw; } cardp->bootcmdresp = 0; do { int j = 0; i++; /* Issue Boot command = 1, Boot from Download-FW */ if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); /* wait for command response */ do { j++; msleep_interruptible(100); } while (cardp->bootcmdresp == 0 && j < 10); } while (cardp->bootcmdresp == 0 && i < 5); if (cardp->bootcmdresp <= 0) { if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } return -1; } i = 0; cardp->totalbytes = 0; cardp->fwlastblksent = 0; cardp->CRC_OK = 1; cardp->fwdnldover = 0; cardp->fwseqnum = -1; cardp->totalbytes = 0; cardp->fwfinalblk = 0; /* Send the first firmware packet... */ if_usb_send_fw_pkt(cardp); /* ... and wait for the process to complete */ wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved || cardp->fwdnldover); del_timer_sync(&cardp->fw_timeout); usb_kill_urb(cardp->rx_urb); if (!cardp->fwdnldover) { pr_info("failed to load fw, resetting device!\n"); if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } pr_info("FW download failure, time = %d ms\n", i * 100); ret = -1; goto release_fw; } cardp->priv->fw_ready = 1; release_fw: release_firmware(cardp->fw); cardp->fw = NULL; if_usb_setup_firmware(cardp->priv); done: lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(if_usb_prog_firmware); #define if_usb_suspend NULL #define if_usb_resume NULL static struct usb_driver if_usb_driver = { .name = DRV_NAME, .probe = if_usb_probe, .disconnect = if_usb_disconnect, .id_table = if_usb_table, .suspend = if_usb_suspend, .resume = if_usb_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(if_usb_driver); MODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver"); MODULE_AUTHOR("Cozybit Inc."); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/main.c0000644000175000017500000004707212026211315024061 0ustar mcgrofmcgrof/* * Copyright (C) 2008, cozybit Inc. * Copyright (C) 2003-2006, Marvell International Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include "libertas_tf.h" #define DRIVER_RELEASE_VERSION "004.p0" /* thinfirm version: 5.132.X.pX */ #define LBTF_FW_VER_MIN 0x05840300 #define LBTF_FW_VER_MAX 0x0584ffff #define QOS_CONTROL_LEN 2 /* Module parameters */ unsigned int lbtf_debug; EXPORT_SYMBOL_GPL(lbtf_debug); module_param_named(libertas_tf_debug, lbtf_debug, int, 0644); static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION #ifdef DEBUG "-dbg" #endif ""; struct workqueue_struct *lbtf_wq; static const struct ieee80211_channel lbtf_channels[] = { { .center_freq = 2412, .hw_value = 1 }, { .center_freq = 2417, .hw_value = 2 }, { .center_freq = 2422, .hw_value = 3 }, { .center_freq = 2427, .hw_value = 4 }, { .center_freq = 2432, .hw_value = 5 }, { .center_freq = 2437, .hw_value = 6 }, { .center_freq = 2442, .hw_value = 7 }, { .center_freq = 2447, .hw_value = 8 }, { .center_freq = 2452, .hw_value = 9 }, { .center_freq = 2457, .hw_value = 10 }, { .center_freq = 2462, .hw_value = 11 }, { .center_freq = 2467, .hw_value = 12 }, { .center_freq = 2472, .hw_value = 13 }, { .center_freq = 2484, .hw_value = 14 }, }; /* This table contains the hardware specific values for the modulation rates. */ static const struct ieee80211_rate lbtf_rates[] = { { .bitrate = 10, .hw_value = 0, }, { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = 5, .flags = 0 }, { .bitrate = 90, .hw_value = 6, .flags = 0 }, { .bitrate = 120, .hw_value = 7, .flags = 0 }, { .bitrate = 180, .hw_value = 8, .flags = 0 }, { .bitrate = 240, .hw_value = 9, .flags = 0 }, { .bitrate = 360, .hw_value = 10, .flags = 0 }, { .bitrate = 480, .hw_value = 11, .flags = 0 }, { .bitrate = 540, .hw_value = 12, .flags = 0 }, }; static void lbtf_cmd_work(struct work_struct *work) { struct lbtf_private *priv = container_of(work, struct lbtf_private, cmd_work); lbtf_deb_enter(LBTF_DEB_CMD); spin_lock_irq(&priv->driver_lock); /* command response? */ if (priv->cmd_response_rxed) { priv->cmd_response_rxed = 0; spin_unlock_irq(&priv->driver_lock); lbtf_process_rx_command(priv); spin_lock_irq(&priv->driver_lock); } if (priv->cmd_timed_out && priv->cur_cmd) { struct cmd_ctrl_node *cmdnode = priv->cur_cmd; if (++priv->nr_retries > 10) { lbtf_complete_command(priv, cmdnode, -ETIMEDOUT); priv->nr_retries = 0; } else { priv->cur_cmd = NULL; /* Stick it back at the _top_ of the pending * queue for immediate resubmission */ list_add(&cmdnode->list, &priv->cmdpendingq); } } priv->cmd_timed_out = 0; spin_unlock_irq(&priv->driver_lock); if (!priv->fw_ready) { lbtf_deb_leave_args(LBTF_DEB_CMD, "fw not ready"); return; } /* Execute the next command */ if (!priv->cur_cmd) lbtf_execute_next_command(priv); lbtf_deb_leave(LBTF_DEB_CMD); } /** * lbtf_setup_firmware: initialize firmware. * * @priv A pointer to struct lbtf_private structure * * Returns: 0 on success. */ static int lbtf_setup_firmware(struct lbtf_private *priv) { int ret = -1; lbtf_deb_enter(LBTF_DEB_FW); /* * Read priv address from HW */ memset(priv->current_addr, 0xff, ETH_ALEN); ret = lbtf_update_hw_spec(priv); if (ret) { ret = -1; goto done; } lbtf_set_mac_control(priv); lbtf_set_radio_control(priv); ret = 0; done: lbtf_deb_leave_args(LBTF_DEB_FW, "ret: %d", ret); return ret; } /** * This function handles the timeout of command sending. * It will re-send the same command again. */ static void command_timer_fn(unsigned long data) { struct lbtf_private *priv = (struct lbtf_private *)data; unsigned long flags; lbtf_deb_enter(LBTF_DEB_CMD); spin_lock_irqsave(&priv->driver_lock, flags); if (!priv->cur_cmd) { printk(KERN_DEBUG "libertastf: command timer expired; " "no pending command\n"); goto out; } printk(KERN_DEBUG "libertas: command %x timed out\n", le16_to_cpu(priv->cur_cmd->cmdbuf->command)); priv->cmd_timed_out = 1; queue_work(lbtf_wq, &priv->cmd_work); out: spin_unlock_irqrestore(&priv->driver_lock, flags); lbtf_deb_leave(LBTF_DEB_CMD); } static int lbtf_init_adapter(struct lbtf_private *priv) { lbtf_deb_enter(LBTF_DEB_MAIN); memset(priv->current_addr, 0xff, ETH_ALEN); mutex_init(&priv->lock); priv->vif = NULL; setup_timer(&priv->command_timer, command_timer_fn, (unsigned long)priv); INIT_LIST_HEAD(&priv->cmdfreeq); INIT_LIST_HEAD(&priv->cmdpendingq); spin_lock_init(&priv->driver_lock); /* Allocate the command buffers */ if (lbtf_allocate_cmd_buffer(priv)) return -1; lbtf_deb_leave(LBTF_DEB_MAIN); return 0; } static void lbtf_free_adapter(struct lbtf_private *priv) { lbtf_deb_enter(LBTF_DEB_MAIN); lbtf_free_cmd_buffer(priv); del_timer(&priv->command_timer); lbtf_deb_leave(LBTF_DEB_MAIN); } static void lbtf_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct lbtf_private *priv = hw->priv; priv->skb_to_tx = skb; queue_work(lbtf_wq, &priv->tx_work); /* * queue will be restarted when we receive transmission feedback if * there are no buffered multicast frames to send */ ieee80211_stop_queues(priv->hw); } static void lbtf_tx_work(struct work_struct *work) { struct lbtf_private *priv = container_of(work, struct lbtf_private, tx_work); unsigned int len; struct ieee80211_tx_info *info; struct txpd *txpd; struct sk_buff *skb = NULL; int err; lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX); if ((priv->vif->type == NL80211_IFTYPE_AP) && (!skb_queue_empty(&priv->bc_ps_buf))) skb = skb_dequeue(&priv->bc_ps_buf); else if (priv->skb_to_tx) { skb = priv->skb_to_tx; priv->skb_to_tx = NULL; } else { lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); return; } len = skb->len; info = IEEE80211_SKB_CB(skb); txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd)); if (priv->surpriseremoved) { dev_kfree_skb_any(skb); lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); return; } memset(txpd, 0, sizeof(struct txpd)); /* Activate per-packet rate selection */ txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE | ieee80211_get_tx_rate(priv->hw, info)->hw_value); /* copy destination address from 802.11 header */ memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4, ETH_ALEN); txpd->tx_packet_length = cpu_to_le16(len); txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); BUG_ON(priv->tx_skb); spin_lock_irq(&priv->driver_lock); priv->tx_skb = skb; err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len); spin_unlock_irq(&priv->driver_lock); if (err) { dev_kfree_skb_any(skb); priv->tx_skb = NULL; pr_err("TX error: %d", err); } lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); } static int lbtf_op_start(struct ieee80211_hw *hw) { struct lbtf_private *priv = hw->priv; void *card = priv->card; int ret = -1; lbtf_deb_enter(LBTF_DEB_MACOPS); if (!priv->fw_ready) /* Upload firmware */ if (priv->hw_prog_firmware(card)) goto err_prog_firmware; /* poke the firmware */ priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; priv->radioon = RADIO_ON; priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; ret = lbtf_setup_firmware(priv); if (ret) goto err_prog_firmware; if ((priv->fwrelease < LBTF_FW_VER_MIN) || (priv->fwrelease > LBTF_FW_VER_MAX)) { ret = -1; goto err_prog_firmware; } printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n"); lbtf_deb_leave(LBTF_DEB_MACOPS); return 0; err_prog_firmware: priv->hw_reset_device(card); lbtf_deb_leave_args(LBTF_DEB_MACOPS, "error programing fw; ret=%d", ret); return ret; } static void lbtf_op_stop(struct ieee80211_hw *hw) { struct lbtf_private *priv = hw->priv; unsigned long flags; struct sk_buff *skb; struct cmd_ctrl_node *cmdnode; lbtf_deb_enter(LBTF_DEB_MACOPS); /* Flush pending command nodes */ spin_lock_irqsave(&priv->driver_lock, flags); list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { cmdnode->result = -ENOENT; cmdnode->cmdwaitqwoken = 1; wake_up_interruptible(&cmdnode->cmdwait_q); } spin_unlock_irqrestore(&priv->driver_lock, flags); cancel_work_sync(&priv->cmd_work); cancel_work_sync(&priv->tx_work); while ((skb = skb_dequeue(&priv->bc_ps_buf))) dev_kfree_skb_any(skb); priv->radioon = RADIO_OFF; lbtf_set_radio_control(priv); lbtf_deb_leave(LBTF_DEB_MACOPS); } static int lbtf_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct lbtf_private *priv = hw->priv; lbtf_deb_enter(LBTF_DEB_MACOPS); if (priv->vif != NULL) return -EOPNOTSUPP; priv->vif = vif; switch (vif->type) { case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: lbtf_set_mode(priv, LBTF_AP_MODE); break; case NL80211_IFTYPE_STATION: lbtf_set_mode(priv, LBTF_STA_MODE); break; default: priv->vif = NULL; return -EOPNOTSUPP; } lbtf_set_mac_address(priv, (u8 *) vif->addr); lbtf_deb_leave(LBTF_DEB_MACOPS); return 0; } static void lbtf_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct lbtf_private *priv = hw->priv; lbtf_deb_enter(LBTF_DEB_MACOPS); if (priv->vif->type == NL80211_IFTYPE_AP || priv->vif->type == NL80211_IFTYPE_MESH_POINT) lbtf_beacon_ctrl(priv, 0, 0); lbtf_set_mode(priv, LBTF_PASSIVE_MODE); lbtf_set_bssid(priv, 0, NULL); priv->vif = NULL; lbtf_deb_leave(LBTF_DEB_MACOPS); } static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) { struct lbtf_private *priv = hw->priv; struct ieee80211_conf *conf = &hw->conf; lbtf_deb_enter(LBTF_DEB_MACOPS); if (conf->channel->center_freq != priv->cur_freq) { priv->cur_freq = conf->channel->center_freq; lbtf_set_channel(priv, conf->channel->hw_value); } lbtf_deb_leave(LBTF_DEB_MACOPS); return 0; } static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *ha) #endif { struct lbtf_private *priv = hw->priv; int i; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; int mc_count = netdev_hw_addr_list_count(mc_list); #endif if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) return mc_count; priv->nr_of_multicastmacaddr = mc_count; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = 0; netdev_hw_addr_list_for_each(ha, mc_list) memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN); #else for (i = 0; i < mc_count; i++) { if (!ha) break; memcpy(&priv->multicastlist[i], ha->da_addr, ETH_ALEN); ha = ha->next; } #endif return mc_count; } #define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) static void lbtf_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 multicast) { struct lbtf_private *priv = hw->priv; int old_mac_control = priv->mac_control; lbtf_deb_enter(LBTF_DEB_MACOPS); changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; if (!changed_flags) { lbtf_deb_leave(LBTF_DEB_MACOPS); return; } if (*new_flags & (FIF_PROMISC_IN_BSS)) priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; else priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; if (*new_flags & (FIF_ALLMULTI) || multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; } else if (multicast) { priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; lbtf_cmd_set_mac_multicast_addr(priv); } else { priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | CMD_ACT_MAC_ALL_MULTICAST_ENABLE); if (priv->nr_of_multicastmacaddr) { priv->nr_of_multicastmacaddr = 0; lbtf_cmd_set_mac_multicast_addr(priv); } } if (priv->mac_control != old_mac_control) lbtf_set_mac_control(priv); lbtf_deb_leave(LBTF_DEB_MACOPS); } static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct lbtf_private *priv = hw->priv; struct sk_buff *beacon; lbtf_deb_enter(LBTF_DEB_MACOPS); if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { switch (priv->vif->type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: beacon = ieee80211_beacon_get(hw, vif); if (beacon) { lbtf_beacon_set(priv, beacon); kfree_skb(beacon); lbtf_beacon_ctrl(priv, 1, bss_conf->beacon_int); } break; default: break; } } if (changes & BSS_CHANGED_BSSID) { bool activate = !is_zero_ether_addr(bss_conf->bssid); lbtf_set_bssid(priv, activate, bss_conf->bssid); } if (changes & BSS_CHANGED_ERP_PREAMBLE) { if (bss_conf->use_short_preamble) priv->preamble = CMD_TYPE_SHORT_PREAMBLE; else priv->preamble = CMD_TYPE_LONG_PREAMBLE; lbtf_set_radio_control(priv); } lbtf_deb_leave(LBTF_DEB_MACOPS); } static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct lbtf_private *priv = hw->priv; struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; survey->filled = SURVEY_INFO_NOISE_DBM; survey->noise = priv->noise; return 0; } static const struct ieee80211_ops lbtf_ops = { .tx = lbtf_op_tx, .start = lbtf_op_start, .stop = lbtf_op_stop, .add_interface = lbtf_op_add_interface, .remove_interface = lbtf_op_remove_interface, .config = lbtf_op_config, .prepare_multicast = lbtf_op_prepare_multicast, .configure_filter = lbtf_op_configure_filter, .bss_info_changed = lbtf_op_bss_info_changed, .get_survey = lbtf_op_get_survey, }; int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) { struct ieee80211_rx_status stats; struct rxpd *prxpd; int need_padding; unsigned int flags; struct ieee80211_hdr *hdr; lbtf_deb_enter(LBTF_DEB_RX); prxpd = (struct rxpd *) skb->data; memset(&stats, 0, sizeof(stats)); if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) stats.flag |= RX_FLAG_FAILED_FCS_CRC; stats.freq = priv->cur_freq; stats.band = IEEE80211_BAND_2GHZ; stats.signal = prxpd->snr; priv->noise = prxpd->nf; /* Marvell rate index has a hole at value 4 */ if (prxpd->rx_rate > 4) --prxpd->rx_rate; stats.rate_idx = prxpd->rx_rate; skb_pull(skb, sizeof(struct rxpd)); hdr = (struct ieee80211_hdr *)skb->data; flags = le32_to_cpu(*(__le32 *)(skb->data + 4)); need_padding = ieee80211_is_data_qos(hdr->frame_control); need_padding ^= ieee80211_has_a4(hdr->frame_control); need_padding ^= ieee80211_is_data_qos(hdr->frame_control) && (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT); if (need_padding) { memmove(skb->data + 2, skb->data, skb->len); skb_reserve(skb, 2); } memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data, min_t(unsigned int, skb->len, 100)); ieee80211_rx_irqsafe(priv->hw, skb); lbtf_deb_leave(LBTF_DEB_RX); return 0; } EXPORT_SYMBOL_GPL(lbtf_rx); /** * lbtf_add_card: Add and initialize the card, no fw upload yet. * * @card A pointer to card * * Returns: pointer to struct lbtf_priv. */ struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) { struct ieee80211_hw *hw; struct lbtf_private *priv = NULL; lbtf_deb_enter(LBTF_DEB_MAIN); hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops); if (!hw) goto done; priv = hw->priv; if (lbtf_init_adapter(priv)) goto err_init_adapter; priv->hw = hw; priv->card = card; priv->tx_skb = NULL; hw->queues = 1; hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; hw->extra_tx_headroom = sizeof(struct txpd); memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels)); memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates)); priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates); priv->band.bitrates = priv->rates; priv->band.n_channels = ARRAY_SIZE(lbtf_channels); priv->band.channels = priv->channels; hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); skb_queue_head_init(&priv->bc_ps_buf); SET_IEEE80211_DEV(hw, dmdev); INIT_WORK(&priv->cmd_work, lbtf_cmd_work); INIT_WORK(&priv->tx_work, lbtf_tx_work); if (ieee80211_register_hw(hw)) goto err_init_adapter; goto done; err_init_adapter: lbtf_free_adapter(priv); ieee80211_free_hw(hw); priv = NULL; done: lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv); return priv; } EXPORT_SYMBOL_GPL(lbtf_add_card); int lbtf_remove_card(struct lbtf_private *priv) { struct ieee80211_hw *hw = priv->hw; lbtf_deb_enter(LBTF_DEB_MAIN); priv->surpriseremoved = 1; del_timer(&priv->command_timer); lbtf_free_adapter(priv); priv->hw = NULL; ieee80211_unregister_hw(hw); ieee80211_free_hw(hw); lbtf_deb_leave(LBTF_DEB_MAIN); return 0; } EXPORT_SYMBOL_GPL(lbtf_remove_card); void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); ieee80211_tx_info_clear_status(info); /* * Commented out, otherwise we never go beyond 1Mbit/s using mac80211 * default pid rc algorithm. * * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt; */ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail) info->flags |= IEEE80211_TX_STAT_ACK; skb_pull(priv->tx_skb, sizeof(struct txpd)); ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); priv->tx_skb = NULL; if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf)) ieee80211_wake_queues(priv->hw); else queue_work(lbtf_wq, &priv->tx_work); } EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback); void lbtf_bcn_sent(struct lbtf_private *priv) { struct sk_buff *skb = NULL; if (priv->vif->type != NL80211_IFTYPE_AP) return; if (skb_queue_empty(&priv->bc_ps_buf)) { bool tx_buff_bc = false; while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) { skb_queue_tail(&priv->bc_ps_buf, skb); tx_buff_bc = true; } if (tx_buff_bc) { ieee80211_stop_queues(priv->hw); queue_work(lbtf_wq, &priv->tx_work); } } skb = ieee80211_beacon_get(priv->hw, priv->vif); if (skb) { lbtf_beacon_set(priv, skb); kfree_skb(skb); } } EXPORT_SYMBOL_GPL(lbtf_bcn_sent); static int __init lbtf_init_module(void) { lbtf_deb_enter(LBTF_DEB_MAIN); lbtf_wq = create_workqueue("libertastf"); if (lbtf_wq == NULL) { printk(KERN_ERR "libertastf: couldn't create workqueue\n"); return -ENOMEM; } lbtf_deb_leave(LBTF_DEB_MAIN); return 0; } static void __exit lbtf_exit_module(void) { lbtf_deb_enter(LBTF_DEB_MAIN); destroy_workqueue(lbtf_wq); lbtf_deb_leave(LBTF_DEB_MAIN); } module_init(lbtf_init_module); module_exit(lbtf_exit_module); MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library"); MODULE_AUTHOR("Cozybit Inc."); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/cmd.c0000644000175000017500000005023612026211315023674 0ustar mcgrofmcgrof/* * Copyright (C) 2008, cozybit Inc. * Copyright (C) 2003-2006, Marvell International Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include "libertas_tf.h" static const struct channel_range channel_ranges[] = { { LBTF_REGDOMAIN_US, 1, 12 }, { LBTF_REGDOMAIN_CA, 1, 12 }, { LBTF_REGDOMAIN_EU, 1, 14 }, { LBTF_REGDOMAIN_JP, 1, 14 }, { LBTF_REGDOMAIN_SP, 1, 14 }, { LBTF_REGDOMAIN_FR, 1, 14 }, }; static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] = { LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU, LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP, }; static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv); /** * lbtf_cmd_copyback - Simple callback that copies response back into command * * @priv A pointer to struct lbtf_private structure * @extra A pointer to the original command structure for which * 'resp' is a response * @resp A pointer to the command response * * Returns: 0 on success, error on failure */ int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, struct cmd_header *resp) { struct cmd_header *buf = (void *)extra; uint16_t copy_len; copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); memcpy(buf, resp, copy_len); return 0; } EXPORT_SYMBOL_GPL(lbtf_cmd_copyback); #define CHAN_TO_IDX(chan) ((chan) - 1) static void lbtf_geo_init(struct lbtf_private *priv) { const struct channel_range *range = channel_ranges; u8 ch; int i; for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) if (channel_ranges[i].regdomain == priv->regioncode) { range = &channel_ranges[i]; break; } for (ch = priv->range.start; ch < priv->range.end; ch++) priv->channels[CHAN_TO_IDX(ch)].flags = 0; } /** * lbtf_update_hw_spec: Updates the hardware details. * * @priv A pointer to struct lbtf_private structure * * Returns: 0 on success, error on failure */ int lbtf_update_hw_spec(struct lbtf_private *priv) { struct cmd_ds_get_hw_spec cmd; int ret = -1; u32 i; lbtf_deb_enter(LBTF_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); if (ret) goto out; priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); /* The firmware release is in an interesting format: the patch * level is in the most significant nibble ... so fix that: */ priv->fwrelease = le32_to_cpu(cmd.fwrelease); priv->fwrelease = (priv->fwrelease << 8) | (priv->fwrelease >> 24 & 0xff); printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n", cmd.permanentaddr, priv->fwrelease >> 24 & 0xff, priv->fwrelease >> 16 & 0xff, priv->fwrelease >> 8 & 0xff, priv->fwrelease & 0xff, priv->fwcapinfo); lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", cmd.hwifversion, cmd.version); /* Clamp region code to 8-bit since FW spec indicates that it should * only ever be 8-bit, even though the field size is 16-bit. Some * firmware returns non-zero high 8 bits here. */ priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { /* use the region code to search for the index */ if (priv->regioncode == lbtf_region_code_to_index[i]) break; } /* if it's unidentified region code, use the default (USA) */ if (i >= MRVDRV_MAX_REGION_CODE) { priv->regioncode = 0x10; pr_info("unidentified region code; using the default (USA)\n"); } if (priv->current_addr[0] == 0xff) memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr); lbtf_geo_init(priv); out: lbtf_deb_leave(LBTF_DEB_CMD); return ret; } /** * lbtf_set_channel: Set the radio channel * * @priv A pointer to struct lbtf_private structure * @channel The desired channel, or 0 to clear a locked channel * * Returns: 0 on success, error on failure */ int lbtf_set_channel(struct lbtf_private *priv, u8 channel) { int ret = 0; struct cmd_ds_802_11_rf_channel cmd; lbtf_deb_enter(LBTF_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); cmd.channel = cpu_to_le16(channel); ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); return ret; } int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon) { struct cmd_ds_802_11_beacon_set cmd; int size; lbtf_deb_enter(LBTF_DEB_CMD); if (beacon->len > MRVL_MAX_BCN_SIZE) { lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1); return -1; } size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len; cmd.hdr.size = cpu_to_le16(size); cmd.len = cpu_to_le16(beacon->len); memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len); lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size); lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0); return 0; } int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, int beacon_int) { struct cmd_ds_802_11_beacon_control cmd; lbtf_deb_enter(LBTF_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.beacon_enable = cpu_to_le16(beacon_enable); cmd.beacon_period = cpu_to_le16(beacon_int); lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd)); lbtf_deb_leave(LBTF_DEB_CMD); return 0; } static void lbtf_queue_cmd(struct lbtf_private *priv, struct cmd_ctrl_node *cmdnode) { unsigned long flags; lbtf_deb_enter(LBTF_DEB_HOST); if (!cmdnode) { lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n"); goto qcmd_done; } if (!cmdnode->cmdbuf->size) { lbtf_deb_host("DNLD_CMD: cmd size is zero\n"); goto qcmd_done; } cmdnode->result = 0; spin_lock_irqsave(&priv->driver_lock, flags); list_add_tail(&cmdnode->list, &priv->cmdpendingq); spin_unlock_irqrestore(&priv->driver_lock, flags); lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", le16_to_cpu(cmdnode->cmdbuf->command)); qcmd_done: lbtf_deb_leave(LBTF_DEB_HOST); } static void lbtf_submit_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmdnode) { unsigned long flags; struct cmd_header *cmd; uint16_t cmdsize; uint16_t command; int timeo = 5 * HZ; int ret; lbtf_deb_enter(LBTF_DEB_HOST); cmd = cmdnode->cmdbuf; spin_lock_irqsave(&priv->driver_lock, flags); priv->cur_cmd = cmdnode; cmdsize = le16_to_cpu(cmd->size); command = le16_to_cpu(cmd->command); lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", command, le16_to_cpu(cmd->seqnum), cmdsize); lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); spin_unlock_irqrestore(&priv->driver_lock, flags); if (ret) { pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret); /* Let the timer kick in and retry, and potentially reset the whole thing if the condition persists */ timeo = HZ; } /* Setup the timer after transmit command */ mod_timer(&priv->command_timer, jiffies + timeo); lbtf_deb_leave(LBTF_DEB_HOST); } /** * This function inserts command node to cmdfreeq * after cleans it. Requires priv->driver_lock held. */ static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, struct cmd_ctrl_node *cmdnode) { lbtf_deb_enter(LBTF_DEB_HOST); if (!cmdnode) goto cl_ins_out; cmdnode->callback = NULL; cmdnode->callback_arg = 0; memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); list_add_tail(&cmdnode->list, &priv->cmdfreeq); cl_ins_out: lbtf_deb_leave(LBTF_DEB_HOST); } static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, struct cmd_ctrl_node *ptempcmd) { unsigned long flags; spin_lock_irqsave(&priv->driver_lock, flags); __lbtf_cleanup_and_insert_cmd(priv, ptempcmd); spin_unlock_irqrestore(&priv->driver_lock, flags); } void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, int result) { cmd->result = result; cmd->cmdwaitqwoken = 1; wake_up_interruptible(&cmd->cmdwait_q); if (!cmd->callback) __lbtf_cleanup_and_insert_cmd(priv, cmd); priv->cur_cmd = NULL; } int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv) { struct cmd_ds_mac_multicast_addr cmd; lbtf_deb_enter(LBTF_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr); lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs); memcpy(cmd.maclist, priv->multicastlist, priv->nr_of_multicastmacaddr * ETH_ALEN); lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd)); lbtf_deb_leave(LBTF_DEB_CMD); return 0; } void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) { struct cmd_ds_set_mode cmd; lbtf_deb_enter(LBTF_DEB_WEXT); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.mode = cpu_to_le16(mode); lbtf_deb_wext("Switching to mode: 0x%x\n", mode); lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); lbtf_deb_leave(LBTF_DEB_WEXT); } void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid) { struct cmd_ds_set_bssid cmd; lbtf_deb_enter(LBTF_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.activate = activate ? 1 : 0; if (activate) memcpy(cmd.bssid, bssid, ETH_ALEN); lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd)); lbtf_deb_leave(LBTF_DEB_CMD); } int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr) { struct cmd_ds_802_11_mac_address cmd; lbtf_deb_enter(LBTF_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); memcpy(cmd.macadd, mac_addr, ETH_ALEN); lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd)); lbtf_deb_leave(LBTF_DEB_CMD); return 0; } int lbtf_set_radio_control(struct lbtf_private *priv) { int ret = 0; struct cmd_ds_802_11_radio_control cmd; lbtf_deb_enter(LBTF_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); switch (priv->preamble) { case CMD_TYPE_SHORT_PREAMBLE: cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE); break; case CMD_TYPE_LONG_PREAMBLE: cmd.control = cpu_to_le16(SET_LONG_PREAMBLE); break; case CMD_TYPE_AUTO_PREAMBLE: default: cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE); break; } if (priv->radioon) cmd.control |= cpu_to_le16(TURN_ON_RF); else cmd.control &= cpu_to_le16(~TURN_ON_RF); lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon, priv->preamble); ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); return ret; } void lbtf_set_mac_control(struct lbtf_private *priv) { struct cmd_ds_mac_control cmd; lbtf_deb_enter(LBTF_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(priv->mac_control); cmd.reserved = 0; lbtf_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); lbtf_deb_leave(LBTF_DEB_CMD); } /** * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue * * @priv A pointer to struct lbtf_private structure * * Returns: 0 on success. */ int lbtf_allocate_cmd_buffer(struct lbtf_private *priv) { int ret = 0; u32 bufsize; u32 i; struct cmd_ctrl_node *cmdarray; lbtf_deb_enter(LBTF_DEB_HOST); /* Allocate and initialize the command array */ bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; cmdarray = kzalloc(bufsize, GFP_KERNEL); if (!cmdarray) { lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); ret = -1; goto done; } priv->cmd_array = cmdarray; /* Allocate and initialize each command buffer in the command array */ for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); if (!cmdarray[i].cmdbuf) { lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); ret = -1; goto done; } } for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { init_waitqueue_head(&cmdarray[i].cmdwait_q); lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]); } ret = 0; done: lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); return ret; } /** * lbtf_free_cmd_buffer - Frees the cmd buffer. * * @priv A pointer to struct lbtf_private structure * * Returns: 0 */ int lbtf_free_cmd_buffer(struct lbtf_private *priv) { struct cmd_ctrl_node *cmdarray; unsigned int i; lbtf_deb_enter(LBTF_DEB_HOST); /* need to check if cmd array is allocated or not */ if (priv->cmd_array == NULL) { lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); goto done; } cmdarray = priv->cmd_array; /* Release shared memory buffers */ for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { kfree(cmdarray[i].cmdbuf); cmdarray[i].cmdbuf = NULL; } /* Release cmd_ctrl_node */ kfree(priv->cmd_array); priv->cmd_array = NULL; done: lbtf_deb_leave(LBTF_DEB_HOST); return 0; } /** * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue. * * @priv A pointer to struct lbtf_private structure * * Returns: pointer to a struct cmd_ctrl_node or NULL if none available. */ static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv) { struct cmd_ctrl_node *tempnode; unsigned long flags; lbtf_deb_enter(LBTF_DEB_HOST); if (!priv) return NULL; spin_lock_irqsave(&priv->driver_lock, flags); if (!list_empty(&priv->cmdfreeq)) { tempnode = list_first_entry(&priv->cmdfreeq, struct cmd_ctrl_node, list); list_del(&tempnode->list); } else { lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); tempnode = NULL; } spin_unlock_irqrestore(&priv->driver_lock, flags); lbtf_deb_leave(LBTF_DEB_HOST); return tempnode; } /** * lbtf_execute_next_command: execute next command in cmd pending queue. * * @priv A pointer to struct lbtf_private structure * * Returns: 0 on success. */ int lbtf_execute_next_command(struct lbtf_private *priv) { struct cmd_ctrl_node *cmdnode = NULL; struct cmd_header *cmd; unsigned long flags; int ret = 0; /* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the * only caller to us is lbtf_thread() and we get even when a * data packet is received */ lbtf_deb_enter(LBTF_DEB_THREAD); spin_lock_irqsave(&priv->driver_lock, flags); if (priv->cur_cmd) { pr_alert("EXEC_NEXT_CMD: already processing command!\n"); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } if (!list_empty(&priv->cmdpendingq)) { cmdnode = list_first_entry(&priv->cmdpendingq, struct cmd_ctrl_node, list); } if (cmdnode) { cmd = cmdnode->cmdbuf; list_del(&cmdnode->list); lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", le16_to_cpu(cmd->command)); spin_unlock_irqrestore(&priv->driver_lock, flags); lbtf_submit_command(priv, cmdnode); } else spin_unlock_irqrestore(&priv->driver_lock, flags); ret = 0; done: lbtf_deb_leave(LBTF_DEB_THREAD); return ret; } static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbtf_private *, unsigned long, struct cmd_header *), unsigned long callback_arg) { struct cmd_ctrl_node *cmdnode; lbtf_deb_enter(LBTF_DEB_HOST); if (priv->surpriseremoved) { lbtf_deb_host("PREP_CMD: card removed\n"); cmdnode = ERR_PTR(-ENOENT); goto done; } cmdnode = lbtf_get_cmd_ctrl_node(priv); if (cmdnode == NULL) { lbtf_deb_host("PREP_CMD: cmdnode is NULL\n"); /* Wake up main thread to execute next command */ queue_work(lbtf_wq, &priv->cmd_work); cmdnode = ERR_PTR(-ENOBUFS); goto done; } cmdnode->callback = callback; cmdnode->callback_arg = callback_arg; /* Copy the incoming command to the buffer */ memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); /* Set sequence number, clean result, move to buffer */ priv->seqnum++; cmdnode->cmdbuf->command = cpu_to_le16(command); cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum); cmdnode->cmdbuf->result = 0; lbtf_deb_host("PREP_CMD: command 0x%04x\n", command); cmdnode->cmdwaitqwoken = 0; lbtf_queue_cmd(priv, cmdnode); queue_work(lbtf_wq, &priv->cmd_work); done: lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode); return cmdnode; } void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size) { lbtf_deb_enter(LBTF_DEB_CMD); __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0); lbtf_deb_leave(LBTF_DEB_CMD); } int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbtf_private *, unsigned long, struct cmd_header *), unsigned long callback_arg) { struct cmd_ctrl_node *cmdnode; unsigned long flags; int ret = 0; lbtf_deb_enter(LBTF_DEB_HOST); cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, callback, callback_arg); if (IS_ERR(cmdnode)) { ret = PTR_ERR(cmdnode); goto done; } might_sleep(); ret = wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); if (ret) { pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n", command, ret); goto done; } spin_lock_irqsave(&priv->driver_lock, flags); ret = cmdnode->result; if (ret) pr_info("PREP_CMD: command 0x%04x failed: %d\n", command, ret); __lbtf_cleanup_and_insert_cmd(priv, cmdnode); spin_unlock_irqrestore(&priv->driver_lock, flags); done: lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(__lbtf_cmd); /* Call holding driver_lock */ void lbtf_cmd_response_rx(struct lbtf_private *priv) { priv->cmd_response_rxed = 1; queue_work(lbtf_wq, &priv->cmd_work); } EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx); int lbtf_process_rx_command(struct lbtf_private *priv) { uint16_t respcmd, curcmd; struct cmd_header *resp; int ret = 0; unsigned long flags; uint16_t result; lbtf_deb_enter(LBTF_DEB_CMD); mutex_lock(&priv->lock); spin_lock_irqsave(&priv->driver_lock, flags); if (!priv->cur_cmd) { ret = -1; spin_unlock_irqrestore(&priv->driver_lock, flags); goto done; } resp = (void *)priv->cmd_resp_buff; curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); respcmd = le16_to_cpu(resp->command); result = le16_to_cpu(resp->result); if (net_ratelimit()) pr_info("libertastf: cmd response 0x%04x, seq %d, size %d\n", respcmd, le16_to_cpu(resp->seqnum), le16_to_cpu(resp->size)); if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } if (respcmd != CMD_RET(curcmd)) { spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } if (resp->result == cpu_to_le16(0x0004)) { /* 0x0004 means -EAGAIN. Drop the response, let it time out and be resubmitted */ spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } /* Now we got response from FW, cancel the command timer */ del_timer(&priv->command_timer); priv->cmd_timed_out = 0; if (priv->nr_retries) priv->nr_retries = 0; /* If the command is not successful, cleanup and return failure */ if ((result != 0 || !(respcmd & 0x8000))) { /* * Handling errors here */ switch (respcmd) { case CMD_RET(CMD_GET_HW_SPEC): case CMD_RET(CMD_802_11_RESET): pr_info("libertastf: reset failed\n"); break; } lbtf_complete_command(priv, priv->cur_cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } spin_unlock_irqrestore(&priv->driver_lock, flags); if (priv->cur_cmd && priv->cur_cmd->callback) { ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, resp); } spin_lock_irqsave(&priv->driver_lock, flags); if (priv->cur_cmd) { /* Clean up and Put current command back to cmdfreeq */ lbtf_complete_command(priv, priv->cur_cmd, result); } spin_unlock_irqrestore(&priv->driver_lock, flags); done: mutex_unlock(&priv->lock); lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/libertas_tf.h0000644000175000017500000003045212026211315025432 0ustar mcgrofmcgrof/* * Copyright (C) 2008, cozybit Inc. * Copyright (C) 2007, Red Hat, Inc. * Copyright (C) 2003-2006, Marvell International Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #include #include #include #include #include "deb_defs.h" #ifndef DRV_NAME #define DRV_NAME "libertas_tf" #endif #define MRVL_DEFAULT_RETRIES 9 #define MRVL_PER_PACKET_RATE 0x10 #define MRVL_MAX_BCN_SIZE 440 #define CMD_OPTION_WAITFORRSP 0x0002 /* Return command are almost always the same as the host command, but with * bit 15 set high. There are a few exceptions, though... */ #define CMD_RET(cmd) (0x8000 | cmd) /* Command codes */ #define CMD_GET_HW_SPEC 0x0003 #define CMD_802_11_RESET 0x0005 #define CMD_MAC_MULTICAST_ADR 0x0010 #define CMD_802_11_RADIO_CONTROL 0x001c #define CMD_802_11_RF_CHANNEL 0x001d #define CMD_802_11_RF_TX_POWER 0x001e #define CMD_MAC_CONTROL 0x0028 #define CMD_802_11_MAC_ADDRESS 0x004d #define CMD_SET_BOOT2_VER 0x00a5 #define CMD_802_11_BEACON_CTRL 0x00b0 #define CMD_802_11_BEACON_SET 0x00cb #define CMD_802_11_SET_MODE 0x00cc #define CMD_802_11_SET_BSSID 0x00cd #define CMD_ACT_GET 0x0000 #define CMD_ACT_SET 0x0001 /* Define action or option for CMD_802_11_RESET */ #define CMD_ACT_HALT 0x0003 /* Define action or option for CMD_MAC_CONTROL */ #define CMD_ACT_MAC_RX_ON 0x0001 #define CMD_ACT_MAC_TX_ON 0x0002 #define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 #define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 #define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 #define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 /* Define action or option for CMD_802_11_RADIO_CONTROL */ #define CMD_TYPE_AUTO_PREAMBLE 0x0001 #define CMD_TYPE_SHORT_PREAMBLE 0x0002 #define CMD_TYPE_LONG_PREAMBLE 0x0003 #define TURN_ON_RF 0x01 #define RADIO_ON 0x01 #define RADIO_OFF 0x00 #define SET_AUTO_PREAMBLE 0x05 #define SET_SHORT_PREAMBLE 0x03 #define SET_LONG_PREAMBLE 0x01 /* Define action or option for CMD_802_11_RF_CHANNEL */ #define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 #define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 /* Codes for CMD_802_11_SET_MODE */ enum lbtf_mode { LBTF_PASSIVE_MODE, LBTF_STA_MODE, LBTF_AP_MODE, }; /** Card Event definition */ #define MACREG_INT_CODE_FIRMWARE_READY 48 /** Buffer Constants */ /* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas * driver has more local TxPDs. Each TxPD on the host memory is associated * with a Tx control node. The driver maintains 8 RxPD descriptors for * station firmware to store Rx packet information. * * Current version of MAC has a 32x6 multicast address buffer. * * 802.11b can have up to 14 channels, the driver keeps the * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. */ #define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 #define LBS_NUM_CMD_BUFFERS 10 #define LBS_CMD_BUFFER_SIZE (2 * 1024) #define MRVDRV_MAX_CHANNEL_SIZE 14 #define MRVDRV_SNAP_HEADER_LEN 8 #define LBS_UPLD_SIZE 2312 #define DEV_NAME_LEN 32 /** Misc constants */ /* This section defines 802.11 specific contants */ #define MRVDRV_MAX_REGION_CODE 6 /** * the table to keep region code */ #define LBTF_REGDOMAIN_US 0x10 #define LBTF_REGDOMAIN_CA 0x20 #define LBTF_REGDOMAIN_EU 0x30 #define LBTF_REGDOMAIN_SP 0x31 #define LBTF_REGDOMAIN_FR 0x32 #define LBTF_REGDOMAIN_JP 0x40 #define SBI_EVENT_CAUSE_SHIFT 3 /** RxPD status */ #define MRVDRV_RXPD_STATUS_OK 0x0001 /* This is for firmware specific length */ #define EXTRA_LEN 36 #define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) #define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ (ETH_FRAME_LEN + sizeof(struct rxpd) \ + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) #define CMD_F_HOSTCMD (1 << 0) #define FW_CAPINFO_WPA (1 << 0) #define RF_ANTENNA_1 0x1 #define RF_ANTENNA_2 0x2 #define RF_ANTENNA_AUTO 0xFFFF #define LBTF_EVENT_BCN_SENT 55 /** Global Variable Declaration */ /** mv_ms_type */ enum mv_ms_type { MVMS_DAT = 0, MVMS_CMD = 1, MVMS_TXDONE = 2, MVMS_EVENT }; extern struct workqueue_struct *lbtf_wq; struct lbtf_private; struct lbtf_offset_value { u32 offset; u32 value; }; struct channel_range { u8 regdomain; u8 start; u8 end; /* exclusive (channel must be less than end) */ }; struct if_usb_card; /** Private structure for the MV device */ struct lbtf_private { void *card; struct ieee80211_hw *hw; /* Command response buffer */ u8 cmd_resp_buff[LBS_UPLD_SIZE]; /* Download sent: bit0 1/0=data_sent/data_tx_done, bit1 1/0=cmd_sent/cmd_tx_done, all other bits reserved 0 */ struct ieee80211_vif *vif; struct work_struct cmd_work; struct work_struct tx_work; /** Hardware access */ int (*hw_host_to_card) (struct lbtf_private *priv, u8 type, u8 *payload, u16 nb); int (*hw_prog_firmware) (struct if_usb_card *cardp); int (*hw_reset_device) (struct if_usb_card *cardp); /** Wlan adapter data structure*/ /** STATUS variables */ u32 fwrelease; u32 fwcapinfo; /* protected with big lock */ struct mutex lock; /** command-related variables */ u16 seqnum; /* protected by big lock */ struct cmd_ctrl_node *cmd_array; /** Current command */ struct cmd_ctrl_node *cur_cmd; /** command Queues */ /** Free command buffers */ struct list_head cmdfreeq; /** Pending command buffers */ struct list_head cmdpendingq; /** spin locks */ spinlock_t driver_lock; /** Timers */ struct timer_list command_timer; int nr_retries; int cmd_timed_out; u8 cmd_response_rxed; /** capability Info used in Association, start, join */ u16 capability; /** MAC address information */ u8 current_addr[ETH_ALEN]; u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; u32 nr_of_multicastmacaddr; int cur_freq; struct sk_buff *skb_to_tx; struct sk_buff *tx_skb; /** NIC Operation characteristics */ u16 mac_control; u16 regioncode; struct channel_range range; u8 radioon; u32 preamble; struct ieee80211_channel channels[14]; struct ieee80211_rate rates[12]; struct ieee80211_supported_band band; struct lbtf_offset_value offsetvalue; u8 fw_ready; u8 surpriseremoved; struct sk_buff_head bc_ps_buf; /* Most recently reported noise in dBm */ s8 noise; }; /* 802.11-related definitions */ /* TxPD descriptor */ struct txpd { /* Current Tx packet status */ __le32 tx_status; /* Tx control */ __le32 tx_control; __le32 tx_packet_location; /* Tx packet length */ __le16 tx_packet_length; /* First 2 byte of destination MAC address */ u8 tx_dest_addr_high[2]; /* Last 4 byte of destination MAC address */ u8 tx_dest_addr_low[4]; /* Pkt Priority */ u8 priority; /* Pkt Trasnit Power control */ u8 powermgmt; /* Time the packet has been queued in the driver (units = 2ms) */ u8 pktdelay_2ms; /* reserved */ u8 reserved1; }; /* RxPD Descriptor */ struct rxpd { /* Current Rx packet status */ __le16 status; /* SNR */ u8 snr; /* Tx control */ u8 rx_control; /* Pkt length */ __le16 pkt_len; /* Noise Floor */ u8 nf; /* Rx Packet Rate */ u8 rx_rate; /* Pkt addr */ __le32 pkt_ptr; /* Next Rx RxPD addr */ __le32 next_rxpd_ptr; /* Pkt Priority */ u8 priority; u8 reserved[3]; }; struct cmd_header { __le16 command; __le16 size; __le16 seqnum; __le16 result; } __packed; struct cmd_ctrl_node { struct list_head list; int result; /* command response */ int (*callback)(struct lbtf_private *, unsigned long, struct cmd_header *); unsigned long callback_arg; /* command data */ struct cmd_header *cmdbuf; /* wait queue */ u16 cmdwaitqwoken; wait_queue_head_t cmdwait_q; }; /* * Define data structure for CMD_GET_HW_SPEC * This structure defines the response for the GET_HW_SPEC command */ struct cmd_ds_get_hw_spec { struct cmd_header hdr; /* HW Interface version number */ __le16 hwifversion; /* HW version number */ __le16 version; /* Max number of TxPD FW can handle */ __le16 nr_txpd; /* Max no of Multicast address */ __le16 nr_mcast_adr; /* MAC address */ u8 permanentaddr[6]; /* region Code */ __le16 regioncode; /* Number of antenna used */ __le16 nr_antenna; /* FW release number, example 0x01030304 = 2.3.4p1 */ __le32 fwrelease; /* Base Address of TxPD queue */ __le32 wcb_base; /* Read Pointer of RxPd queue */ __le32 rxpd_rdptr; /* Write Pointer of RxPd queue */ __le32 rxpd_wrptr; /*FW/HW capability */ __le32 fwcapinfo; } __packed; struct cmd_ds_mac_control { struct cmd_header hdr; __le16 action; u16 reserved; }; struct cmd_ds_802_11_mac_address { struct cmd_header hdr; __le16 action; uint8_t macadd[ETH_ALEN]; }; struct cmd_ds_mac_multicast_addr { struct cmd_header hdr; __le16 action; __le16 nr_of_adrs; u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; }; struct cmd_ds_set_mode { struct cmd_header hdr; __le16 mode; }; struct cmd_ds_set_bssid { struct cmd_header hdr; u8 bssid[6]; u8 activate; }; struct cmd_ds_802_11_radio_control { struct cmd_header hdr; __le16 action; __le16 control; }; struct cmd_ds_802_11_rf_channel { struct cmd_header hdr; __le16 action; __le16 channel; __le16 rftype; /* unused */ __le16 reserved; /* unused */ u8 channellist[32]; /* unused */ }; struct cmd_ds_set_boot2_ver { struct cmd_header hdr; __le16 action; __le16 version; }; struct cmd_ds_802_11_reset { struct cmd_header hdr; __le16 action; }; struct cmd_ds_802_11_beacon_control { struct cmd_header hdr; __le16 action; __le16 beacon_enable; __le16 beacon_period; }; struct cmd_ds_802_11_beacon_set { struct cmd_header hdr; __le16 len; u8 beacon[MRVL_MAX_BCN_SIZE]; }; struct lbtf_private; struct cmd_ctrl_node; /** Function Prototype Declaration */ void lbtf_set_mac_control(struct lbtf_private *priv); int lbtf_free_cmd_buffer(struct lbtf_private *priv); int lbtf_allocate_cmd_buffer(struct lbtf_private *priv); int lbtf_execute_next_command(struct lbtf_private *priv); int lbtf_set_radio_control(struct lbtf_private *priv); int lbtf_update_hw_spec(struct lbtf_private *priv); int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv); void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode); void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid); int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr); int lbtf_set_channel(struct lbtf_private *priv, u8 channel); int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon); int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, int beacon_int); int lbtf_process_rx_command(struct lbtf_private *priv); void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, int result); void lbtf_cmd_response_rx(struct lbtf_private *priv); /* main.c */ struct chan_freq_power *lbtf_get_region_cfp_table(u8 region, int *cfp_no); struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev); int lbtf_remove_card(struct lbtf_private *priv); int lbtf_start_card(struct lbtf_private *priv); int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb); void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail); void lbtf_bcn_sent(struct lbtf_private *priv); /* support functions for cmd.c */ /* lbtf_cmd() infers the size of the buffer to copy data back into, from the size of the target of the pointer. Since the command to be sent may often be smaller, that size is set in cmd->size by the caller.*/ #define lbtf_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ __lbtf_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ }) #define lbtf_cmd_with_response(priv, cmdnr, cmd) \ lbtf_cmd(priv, cmdnr, cmd, lbtf_cmd_copyback, (unsigned long) (cmd)) void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size); int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbtf_private *, unsigned long, struct cmd_header *), unsigned long callback_arg); int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, struct cmd_header *resp); compat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/if_usb.h0000644000175000017500000000362112026211315024401 0ustar mcgrofmcgrof/* * Copyright (C) 2008, cozybit Inc. * Copyright (C) 2003-2006, Marvell International Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #include #include struct lbtf_private; /** * This file contains definition for USB interface. */ #define CMD_TYPE_REQUEST 0xF00DFACE #define CMD_TYPE_DATA 0xBEADC0DE #define CMD_TYPE_INDICATION 0xBEEFFACE #define BOOT_CMD_FW_BY_USB 0x01 #define BOOT_CMD_FW_IN_EEPROM 0x02 #define BOOT_CMD_UPDATE_BOOT2 0x03 #define BOOT_CMD_UPDATE_FW 0x04 #define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ struct bootcmd { __le32 magic; uint8_t cmd; uint8_t pad[11]; }; #define BOOT_CMD_RESP_OK 0x0001 #define BOOT_CMD_RESP_FAIL 0x0000 struct bootcmdresp { __le32 magic; uint8_t cmd; uint8_t result; uint8_t pad[2]; }; /** USB card description structure*/ struct if_usb_card { struct usb_device *udev; struct urb *rx_urb, *tx_urb, *cmd_urb; struct lbtf_private *priv; struct sk_buff *rx_skb; uint8_t ep_in; uint8_t ep_out; int8_t bootcmdresp; int ep_in_size; void *ep_out_buf; int ep_out_size; const struct firmware *fw; struct timer_list fw_timeout; wait_queue_head_t fw_wq; uint32_t fwseqnum; uint32_t totalbytes; uint32_t fwlastblksent; uint8_t CRC_OK; uint8_t fwdnldover; uint8_t fwfinalblk; __le16 boot2_version; }; /** fwheader */ struct fwheader { __le32 dnldcmd; __le32 baseaddr; __le32 datalength; __le32 CRC; }; #define FW_MAX_DATA_BLK_SIZE 600 /** FWData */ struct fwdata { struct fwheader hdr; __le32 seqnum; uint8_t data[0]; }; /** fwsyncheader */ struct fwsyncheader { __le32 cmd; __le32 seqnum; }; #define FW_HAS_DATA_TO_RECV 0x00000001 #define FW_HAS_LAST_BLOCK 0x00000004 compat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/deb_defs.h0000644000175000017500000001034512026211315024666 0ustar mcgrofmcgrof/** * This header file contains global constant/enum definitions, * global variable declaration. */ #ifndef _LBS_DEB_DEFS_H_ #define _LBS_DEB_DEFS_H_ #ifndef DRV_NAME #define DRV_NAME "libertas_tf" #endif #include #ifdef CONFIG_LIBERTAS_THINFIRM_DEBUG #define DEBUG #define PROC_DEBUG #endif #define LBTF_DEB_ENTER 0x00000001 #define LBTF_DEB_LEAVE 0x00000002 #define LBTF_DEB_MAIN 0x00000004 #define LBTF_DEB_NET 0x00000008 #define LBTF_DEB_MESH 0x00000010 #define LBTF_DEB_WEXT 0x00000020 #define LBTF_DEB_IOCTL 0x00000040 #define LBTF_DEB_SCAN 0x00000080 #define LBTF_DEB_ASSOC 0x00000100 #define LBTF_DEB_JOIN 0x00000200 #define LBTF_DEB_11D 0x00000400 #define LBTF_DEB_DEBUGFS 0x00000800 #define LBTF_DEB_ETHTOOL 0x00001000 #define LBTF_DEB_HOST 0x00002000 #define LBTF_DEB_CMD 0x00004000 #define LBTF_DEB_RX 0x00008000 #define LBTF_DEB_TX 0x00010000 #define LBTF_DEB_USB 0x00020000 #define LBTF_DEB_CS 0x00040000 #define LBTF_DEB_FW 0x00080000 #define LBTF_DEB_THREAD 0x00100000 #define LBTF_DEB_HEX 0x00200000 #define LBTF_DEB_SDIO 0x00400000 #define LBTF_DEB_MACOPS 0x00800000 extern unsigned int lbtf_debug; #ifdef DEBUG #define LBTF_DEB_LL(grp, grpnam, fmt, args...) \ do { if ((lbtf_debug & (grp)) == (grp)) \ printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ in_interrupt() ? " (INT)" : "", ## args); } while (0) #else #define LBTF_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) #endif #define lbtf_deb_enter(grp) \ LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s()\n", __func__); #define lbtf_deb_enter_args(grp, fmt, args...) \ LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); #define lbtf_deb_leave(grp) \ LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s()\n", __func__); #define lbtf_deb_leave_args(grp, fmt, args...) \ LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ __func__, ##args); #define lbtf_deb_main(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MAIN, " main", fmt, ##args) #define lbtf_deb_net(fmt, args...) LBTF_DEB_LL(LBTF_DEB_NET, " net", fmt, ##args) #define lbtf_deb_mesh(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MESH, " mesh", fmt, ##args) #define lbtf_deb_wext(fmt, args...) LBTF_DEB_LL(LBTF_DEB_WEXT, " wext", fmt, ##args) #define lbtf_deb_ioctl(fmt, args...) LBTF_DEB_LL(LBTF_DEB_IOCTL, " ioctl", fmt, ##args) #define lbtf_deb_scan(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SCAN, " scan", fmt, ##args) #define lbtf_deb_assoc(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ASSOC, " assoc", fmt, ##args) #define lbtf_deb_join(fmt, args...) LBTF_DEB_LL(LBTF_DEB_JOIN, " join", fmt, ##args) #define lbtf_deb_11d(fmt, args...) LBTF_DEB_LL(LBTF_DEB_11D, " 11d", fmt, ##args) #define lbtf_deb_debugfs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_DEBUGFS, " debugfs", fmt, ##args) #define lbtf_deb_ethtool(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ETHTOOL, " ethtool", fmt, ##args) #define lbtf_deb_host(fmt, args...) LBTF_DEB_LL(LBTF_DEB_HOST, " host", fmt, ##args) #define lbtf_deb_cmd(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CMD, " cmd", fmt, ##args) #define lbtf_deb_rx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_RX, " rx", fmt, ##args) #define lbtf_deb_tx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_TX, " tx", fmt, ##args) #define lbtf_deb_fw(fmt, args...) LBTF_DEB_LL(LBTF_DEB_FW, " fw", fmt, ##args) #define lbtf_deb_usb(fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usb", fmt, ##args) #define lbtf_deb_usbd(dev, fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) #define lbtf_deb_cs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CS, " cs", fmt, ##args) #define lbtf_deb_thread(fmt, args...) LBTF_DEB_LL(LBTF_DEB_THREAD, " thread", fmt, ##args) #define lbtf_deb_sdio(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SDIO, " thread", fmt, ##args) #define lbtf_deb_macops(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MACOPS, " thread", fmt, ##args) #ifdef DEBUG static inline void lbtf_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) { char newprompt[32]; if (len && (lbtf_debug & LBTF_DEB_HEX) && (lbtf_debug & grp)) { snprintf(newprompt, sizeof(newprompt), DRV_NAME " %s: ", prompt); print_hex_dump_bytes(prompt, DUMP_PREFIX_NONE, buf, len); } } #else #define lbtf_deb_hex(grp, prompt, buf, len) do {} while (0) #endif #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas_tf/Makefile0000644000175000017500000000025612026211315024422 0ustar mcgrofmcgroflibertas_tf-objs := main.o cmd.o libertas_tf_usb-objs += if_usb.o obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf.o obj-$(CONFIG_LIBERTAS_THINFIRM_USB) += libertas_tf_usb.o compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/0000755000175000017500000000000012026211315022141 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/wifi.h0000644000175000017500000015023712026211315023260 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_WIFI_H__ #define __RTL_WIFI_H__ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include "debug.h" #define RF_CHANGE_BY_INIT 0 #define RF_CHANGE_BY_IPS BIT(28) #define RF_CHANGE_BY_PS BIT(29) #define RF_CHANGE_BY_HW BIT(30) #define RF_CHANGE_BY_SW BIT(31) #define IQK_ADDA_REG_NUM 16 #define IQK_MAC_REG_NUM 4 #define MAX_KEY_LEN 61 #define KEY_BUF_SIZE 5 /* QoS related. */ /*aci: 0x00 Best Effort*/ /*aci: 0x01 Background*/ /*aci: 0x10 Video*/ /*aci: 0x11 Voice*/ /*Max: define total number.*/ #define AC0_BE 0 #define AC1_BK 1 #define AC2_VI 2 #define AC3_VO 3 #define AC_MAX 4 #define QOS_QUEUE_NUM 4 #define RTL_MAC80211_NUM_QUEUE 5 #define REALTEK_USB_VENQT_MAX_BUF_SIZE 254 #define RTL_USB_MAX_RX_COUNT 100 #define QBSS_LOAD_SIZE 5 #define MAX_WMMELE_LENGTH 64 #define TOTAL_CAM_ENTRY 32 /*slot time for 11g. */ #define RTL_SLOT_TIME_9 9 #define RTL_SLOT_TIME_20 20 /*related with tcp/ip. */ /*if_ehther.h*/ #define ETH_P_PAE 0x888E /*Port Access Entity (IEEE 802.1X) */ #define ETH_P_IP 0x0800 /*Internet Protocol packet */ #define ETH_P_ARP 0x0806 /*Address Resolution packet */ #define SNAP_SIZE 6 #define PROTOC_TYPE_SIZE 2 /*related with 802.11 frame*/ #define MAC80211_3ADDR_LEN 24 #define MAC80211_4ADDR_LEN 30 #define CHANNEL_MAX_NUMBER (14 + 24 + 21) /* 14 is the max channel no */ #define CHANNEL_GROUP_MAX (3 + 9) /* ch1~3, 4~9, 10~14 = three groups */ #define MAX_PG_GROUP 13 #define CHANNEL_GROUP_MAX_2G 3 #define CHANNEL_GROUP_IDX_5GL 3 #define CHANNEL_GROUP_IDX_5GM 6 #define CHANNEL_GROUP_IDX_5GH 9 #define CHANNEL_GROUP_MAX_5G 9 #define CHANNEL_MAX_NUMBER_2G 14 #define AVG_THERMAL_NUM 8 #define MAX_TID_COUNT 9 /* for early mode */ #define FCS_LEN 4 #define EM_HDR_LEN 8 enum intf_type { INTF_PCI = 0, INTF_USB = 1, }; enum radio_path { RF90_PATH_A = 0, RF90_PATH_B = 1, RF90_PATH_C = 2, RF90_PATH_D = 3, }; enum rt_eeprom_type { EEPROM_93C46, EEPROM_93C56, EEPROM_BOOT_EFUSE, }; enum ttl_status { RTL_STATUS_INTERFACE_START = 0, }; enum hardware_type { HARDWARE_TYPE_RTL8192E, HARDWARE_TYPE_RTL8192U, HARDWARE_TYPE_RTL8192SE, HARDWARE_TYPE_RTL8192SU, HARDWARE_TYPE_RTL8192CE, HARDWARE_TYPE_RTL8192CU, HARDWARE_TYPE_RTL8192DE, HARDWARE_TYPE_RTL8192DU, HARDWARE_TYPE_RTL8723AE, HARDWARE_TYPE_RTL8723U, /* keep it last */ HARDWARE_TYPE_NUM }; #define IS_HARDWARE_TYPE_8192SU(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SU) #define IS_HARDWARE_TYPE_8192SE(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE) #define IS_HARDWARE_TYPE_8192CE(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE) #define IS_HARDWARE_TYPE_8192CU(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CU) #define IS_HARDWARE_TYPE_8192DE(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DE) #define IS_HARDWARE_TYPE_8192DU(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DU) #define IS_HARDWARE_TYPE_8723E(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8723E) #define IS_HARDWARE_TYPE_8723U(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8723U) #define IS_HARDWARE_TYPE_8192S(rtlhal) \ (IS_HARDWARE_TYPE_8192SE(rtlhal) || IS_HARDWARE_TYPE_8192SU(rtlhal)) #define IS_HARDWARE_TYPE_8192C(rtlhal) \ (IS_HARDWARE_TYPE_8192CE(rtlhal) || IS_HARDWARE_TYPE_8192CU(rtlhal)) #define IS_HARDWARE_TYPE_8192D(rtlhal) \ (IS_HARDWARE_TYPE_8192DE(rtlhal) || IS_HARDWARE_TYPE_8192DU(rtlhal)) #define IS_HARDWARE_TYPE_8723(rtlhal) \ (IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal)) #define IS_HARDWARE_TYPE_8723U(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8723U) #define RX_HAL_IS_CCK_RATE(_pdesc)\ (_pdesc->rxmcs == DESC92_RATE1M || \ _pdesc->rxmcs == DESC92_RATE2M || \ _pdesc->rxmcs == DESC92_RATE5_5M || \ _pdesc->rxmcs == DESC92_RATE11M) enum scan_operation_backup_opt { SCAN_OPT_BACKUP = 0, SCAN_OPT_RESTORE, SCAN_OPT_MAX }; /*RF state.*/ enum rf_pwrstate { ERFON, ERFSLEEP, ERFOFF }; struct bb_reg_def { u32 rfintfs; u32 rfintfi; u32 rfintfo; u32 rfintfe; u32 rf3wire_offset; u32 rflssi_select; u32 rftxgain_stage; u32 rfhssi_para1; u32 rfhssi_para2; u32 rfswitch_control; u32 rfagc_control1; u32 rfagc_control2; u32 rfrxiq_imbalance; u32 rfrx_afe; u32 rftxiq_imbalance; u32 rftx_afe; u32 rflssi_readback; u32 rflssi_readbackpi; }; enum io_type { IO_CMD_PAUSE_DM_BY_SCAN = 0, IO_CMD_RESUME_DM_BY_SCAN = 1, }; enum hw_variables { HW_VAR_ETHER_ADDR, HW_VAR_MULTICAST_REG, HW_VAR_BASIC_RATE, HW_VAR_BSSID, HW_VAR_MEDIA_STATUS, HW_VAR_SECURITY_CONF, HW_VAR_BEACON_INTERVAL, HW_VAR_ATIM_WINDOW, HW_VAR_LISTEN_INTERVAL, HW_VAR_CS_COUNTER, HW_VAR_DEFAULTKEY0, HW_VAR_DEFAULTKEY1, HW_VAR_DEFAULTKEY2, HW_VAR_DEFAULTKEY3, HW_VAR_SIFS, HW_VAR_DIFS, HW_VAR_EIFS, HW_VAR_SLOT_TIME, HW_VAR_ACK_PREAMBLE, HW_VAR_CW_CONFIG, HW_VAR_CW_VALUES, HW_VAR_RATE_FALLBACK_CONTROL, HW_VAR_CONTENTION_WINDOW, HW_VAR_RETRY_COUNT, HW_VAR_TR_SWITCH, HW_VAR_COMMAND, HW_VAR_WPA_CONFIG, HW_VAR_AMPDU_MIN_SPACE, HW_VAR_SHORTGI_DENSITY, HW_VAR_AMPDU_FACTOR, HW_VAR_MCS_RATE_AVAILABLE, HW_VAR_AC_PARAM, HW_VAR_ACM_CTRL, HW_VAR_DIS_Req_Qsize, HW_VAR_CCX_CHNL_LOAD, HW_VAR_CCX_NOISE_HISTOGRAM, HW_VAR_CCX_CLM_NHM, HW_VAR_TxOPLimit, HW_VAR_TURBO_MODE, HW_VAR_RF_STATE, HW_VAR_RF_OFF_BY_HW, HW_VAR_BUS_SPEED, HW_VAR_SET_DEV_POWER, HW_VAR_RCR, HW_VAR_RATR_0, HW_VAR_RRSR, HW_VAR_CPU_RST, HW_VAR_CECHK_BSSID, HW_VAR_LBK_MODE, HW_VAR_AES_11N_FIX, HW_VAR_USB_RX_AGGR, HW_VAR_USER_CONTROL_TURBO_MODE, HW_VAR_RETRY_LIMIT, HW_VAR_INIT_TX_RATE, HW_VAR_TX_RATE_REG, HW_VAR_EFUSE_USAGE, HW_VAR_EFUSE_BYTES, HW_VAR_AUTOLOAD_STATUS, HW_VAR_RF_2R_DISABLE, HW_VAR_SET_RPWM, HW_VAR_H2C_FW_PWRMODE, HW_VAR_H2C_FW_JOINBSSRPT, HW_VAR_FW_PSMODE_STATUS, HW_VAR_1X1_RECV_COMBINE, HW_VAR_STOP_SEND_BEACON, HW_VAR_TSF_TIMER, HW_VAR_IO_CMD, HW_VAR_RF_RECOVERY, HW_VAR_H2C_FW_UPDATE_GTK, HW_VAR_WF_MASK, HW_VAR_WF_CRC, HW_VAR_WF_IS_MAC_ADDR, HW_VAR_H2C_FW_OFFLOAD, HW_VAR_RESET_WFCRC, HW_VAR_HANDLE_FW_C2H, HW_VAR_DL_FW_RSVD_PAGE, HW_VAR_AID, HW_VAR_HW_SEQ_ENABLE, HW_VAR_CORRECT_TSF, HW_VAR_BCN_VALID, HW_VAR_FWLPS_RF_ON, HW_VAR_DUAL_TSF_RST, HW_VAR_SWITCH_EPHY_WoWLAN, HW_VAR_INT_MIGRATION, HW_VAR_INT_AC, HW_VAR_RF_TIMING, HW_VAR_MRC, HW_VAR_MGT_FILTER, HW_VAR_CTRL_FILTER, HW_VAR_DATA_FILTER, }; enum _RT_MEDIA_STATUS { RT_MEDIA_DISCONNECT = 0, RT_MEDIA_CONNECT = 1 }; enum rt_oem_id { RT_CID_DEFAULT = 0, RT_CID_8187_ALPHA0 = 1, RT_CID_8187_SERCOMM_PS = 2, RT_CID_8187_HW_LED = 3, RT_CID_8187_NETGEAR = 4, RT_CID_WHQL = 5, RT_CID_819x_CAMEO = 6, RT_CID_819x_RUNTOP = 7, RT_CID_819x_Senao = 8, RT_CID_TOSHIBA = 9, RT_CID_819x_Netcore = 10, RT_CID_Nettronix = 11, RT_CID_DLINK = 12, RT_CID_PRONET = 13, RT_CID_COREGA = 14, RT_CID_819x_ALPHA = 15, RT_CID_819x_Sitecom = 16, RT_CID_CCX = 17, RT_CID_819x_Lenovo = 18, RT_CID_819x_QMI = 19, RT_CID_819x_Edimax_Belkin = 20, RT_CID_819x_Sercomm_Belkin = 21, RT_CID_819x_CAMEO1 = 22, RT_CID_819x_MSI = 23, RT_CID_819x_Acer = 24, RT_CID_819x_HP = 27, RT_CID_819x_CLEVO = 28, RT_CID_819x_Arcadyan_Belkin = 29, RT_CID_819x_SAMSUNG = 30, RT_CID_819x_WNC_COREGA = 31, RT_CID_819x_Foxcoon = 32, RT_CID_819x_DELL = 33, }; enum hw_descs { HW_DESC_OWN, HW_DESC_RXOWN, HW_DESC_TX_NEXTDESC_ADDR, HW_DESC_TXBUFF_ADDR, HW_DESC_RXBUFF_ADDR, HW_DESC_RXPKT_LEN, HW_DESC_RXERO, }; enum prime_sc { PRIME_CHNL_OFFSET_DONT_CARE = 0, PRIME_CHNL_OFFSET_LOWER = 1, PRIME_CHNL_OFFSET_UPPER = 2, }; enum rf_type { RF_1T1R = 0, RF_1T2R = 1, RF_2T2R = 2, RF_2T2R_GREEN = 3, }; enum ht_channel_width { HT_CHANNEL_WIDTH_20 = 0, HT_CHANNEL_WIDTH_20_40 = 1, }; /* Ref: 802.11i sepc D10.0 7.3.2.25.1 Cipher Suites Encryption Algorithms */ enum rt_enc_alg { NO_ENCRYPTION = 0, WEP40_ENCRYPTION = 1, TKIP_ENCRYPTION = 2, RSERVED_ENCRYPTION = 3, AESCCMP_ENCRYPTION = 4, WEP104_ENCRYPTION = 5, AESCMAC_ENCRYPTION = 6, /*IEEE802.11w */ }; enum rtl_hal_state { _HAL_STATE_STOP = 0, _HAL_STATE_START = 1, }; enum rtl_desc92_rate { DESC92_RATE1M = 0x00, DESC92_RATE2M = 0x01, DESC92_RATE5_5M = 0x02, DESC92_RATE11M = 0x03, DESC92_RATE6M = 0x04, DESC92_RATE9M = 0x05, DESC92_RATE12M = 0x06, DESC92_RATE18M = 0x07, DESC92_RATE24M = 0x08, DESC92_RATE36M = 0x09, DESC92_RATE48M = 0x0a, DESC92_RATE54M = 0x0b, DESC92_RATEMCS0 = 0x0c, DESC92_RATEMCS1 = 0x0d, DESC92_RATEMCS2 = 0x0e, DESC92_RATEMCS3 = 0x0f, DESC92_RATEMCS4 = 0x10, DESC92_RATEMCS5 = 0x11, DESC92_RATEMCS6 = 0x12, DESC92_RATEMCS7 = 0x13, DESC92_RATEMCS8 = 0x14, DESC92_RATEMCS9 = 0x15, DESC92_RATEMCS10 = 0x16, DESC92_RATEMCS11 = 0x17, DESC92_RATEMCS12 = 0x18, DESC92_RATEMCS13 = 0x19, DESC92_RATEMCS14 = 0x1a, DESC92_RATEMCS15 = 0x1b, DESC92_RATEMCS15_SG = 0x1c, DESC92_RATEMCS32 = 0x20, }; enum rtl_var_map { /*reg map */ SYS_ISO_CTRL = 0, SYS_FUNC_EN, SYS_CLK, MAC_RCR_AM, MAC_RCR_AB, MAC_RCR_ACRC32, MAC_RCR_ACF, MAC_RCR_AAP, /*efuse map */ EFUSE_TEST, EFUSE_CTRL, EFUSE_CLK, EFUSE_CLK_CTRL, EFUSE_PWC_EV12V, EFUSE_FEN_ELDR, EFUSE_LOADER_CLK_EN, EFUSE_ANA8M, EFUSE_HWSET_MAX_SIZE, EFUSE_MAX_SECTION_MAP, EFUSE_REAL_CONTENT_SIZE, EFUSE_OOB_PROTECT_BYTES_LEN, /*CAM map */ RWCAM, WCAMI, RCAMO, CAMDBG, SECR, SEC_CAM_NONE, SEC_CAM_WEP40, SEC_CAM_TKIP, SEC_CAM_AES, SEC_CAM_WEP104, /*IMR map */ RTL_IMR_BCNDMAINT6, /*Beacon DMA Interrupt 6 */ RTL_IMR_BCNDMAINT5, /*Beacon DMA Interrupt 5 */ RTL_IMR_BCNDMAINT4, /*Beacon DMA Interrupt 4 */ RTL_IMR_BCNDMAINT3, /*Beacon DMA Interrupt 3 */ RTL_IMR_BCNDMAINT2, /*Beacon DMA Interrupt 2 */ RTL_IMR_BCNDMAINT1, /*Beacon DMA Interrupt 1 */ RTL_IMR_BCNDOK8, /*Beacon Queue DMA OK Interrup 8 */ RTL_IMR_BCNDOK7, /*Beacon Queue DMA OK Interrup 7 */ RTL_IMR_BCNDOK6, /*Beacon Queue DMA OK Interrup 6 */ RTL_IMR_BCNDOK5, /*Beacon Queue DMA OK Interrup 5 */ RTL_IMR_BCNDOK4, /*Beacon Queue DMA OK Interrup 4 */ RTL_IMR_BCNDOK3, /*Beacon Queue DMA OK Interrup 3 */ RTL_IMR_BCNDOK2, /*Beacon Queue DMA OK Interrup 2 */ RTL_IMR_BCNDOK1, /*Beacon Queue DMA OK Interrup 1 */ RTL_IMR_TIMEOUT2, /*Timeout interrupt 2 */ RTL_IMR_TIMEOUT1, /*Timeout interrupt 1 */ RTL_IMR_TXFOVW, /*Transmit FIFO Overflow */ RTL_IMR_PSTIMEOUT, /*Power save time out interrupt */ RTL_IMR_BcnInt, /*Beacon DMA Interrupt 0 */ RTL_IMR_RXFOVW, /*Receive FIFO Overflow */ RTL_IMR_RDU, /*Receive Descriptor Unavailable */ RTL_IMR_ATIMEND, /*For 92C,ATIM Window End Interrupt */ RTL_IMR_BDOK, /*Beacon Queue DMA OK Interrup */ RTL_IMR_HIGHDOK, /*High Queue DMA OK Interrupt */ RTL_IMR_COMDOK, /*Command Queue DMA OK Interrupt*/ RTL_IMR_TBDOK, /*Transmit Beacon OK interrup */ RTL_IMR_MGNTDOK, /*Management Queue DMA OK Interrupt */ RTL_IMR_TBDER, /*For 92C,Transmit Beacon Error Interrupt */ RTL_IMR_BKDOK, /*AC_BK DMA OK Interrupt */ RTL_IMR_BEDOK, /*AC_BE DMA OK Interrupt */ RTL_IMR_VIDOK, /*AC_VI DMA OK Interrupt */ RTL_IMR_VODOK, /*AC_VO DMA Interrupt */ RTL_IMR_ROK, /*Receive DMA OK Interrupt */ RTL_IBSS_INT_MASKS, /*(RTL_IMR_BcnInt | RTL_IMR_TBDOK | * RTL_IMR_TBDER) */ /*CCK Rates, TxHT = 0 */ RTL_RC_CCK_RATE1M, RTL_RC_CCK_RATE2M, RTL_RC_CCK_RATE5_5M, RTL_RC_CCK_RATE11M, /*OFDM Rates, TxHT = 0 */ RTL_RC_OFDM_RATE6M, RTL_RC_OFDM_RATE9M, RTL_RC_OFDM_RATE12M, RTL_RC_OFDM_RATE18M, RTL_RC_OFDM_RATE24M, RTL_RC_OFDM_RATE36M, RTL_RC_OFDM_RATE48M, RTL_RC_OFDM_RATE54M, RTL_RC_HT_RATEMCS7, RTL_RC_HT_RATEMCS15, /*keep it last */ RTL_VAR_MAP_MAX, }; /*Firmware PS mode for control LPS.*/ enum _fw_ps_mode { FW_PS_ACTIVE_MODE = 0, FW_PS_MIN_MODE = 1, FW_PS_MAX_MODE = 2, FW_PS_DTIM_MODE = 3, FW_PS_VOIP_MODE = 4, FW_PS_UAPSD_WMM_MODE = 5, FW_PS_UAPSD_MODE = 6, FW_PS_IBSS_MODE = 7, FW_PS_WWLAN_MODE = 8, FW_PS_PM_Radio_Off = 9, FW_PS_PM_Card_Disable = 10, }; enum rt_psmode { EACTIVE, /*Active/Continuous access. */ EMAXPS, /*Max power save mode. */ EFASTPS, /*Fast power save mode. */ EAUTOPS, /*Auto power save mode. */ }; /*LED related.*/ enum led_ctl_mode { LED_CTL_POWER_ON = 1, LED_CTL_LINK = 2, LED_CTL_NO_LINK = 3, LED_CTL_TX = 4, LED_CTL_RX = 5, LED_CTL_SITE_SURVEY = 6, LED_CTL_POWER_OFF = 7, LED_CTL_START_TO_LINK = 8, LED_CTL_START_WPS = 9, LED_CTL_STOP_WPS = 10, }; enum rtl_led_pin { LED_PIN_GPIO0, LED_PIN_LED0, LED_PIN_LED1, LED_PIN_LED2 }; /*QoS related.*/ /*acm implementation method.*/ enum acm_method { eAcmWay0_SwAndHw = 0, eAcmWay1_HW = 1, eAcmWay2_SW = 2, }; enum macphy_mode { SINGLEMAC_SINGLEPHY = 0, DUALMAC_DUALPHY, DUALMAC_SINGLEPHY, }; enum band_type { BAND_ON_2_4G = 0, BAND_ON_5G, BAND_ON_BOTH, BANDMAX }; /*aci/aifsn Field. Ref: WMM spec 2.2.2: WME Parameter Element, p.12.*/ union aci_aifsn { u8 char_data; struct { u8 aifsn:4; u8 acm:1; u8 aci:2; u8 reserved:1; } f; /* Field */ }; /*mlme related.*/ enum wireless_mode { WIRELESS_MODE_UNKNOWN = 0x00, WIRELESS_MODE_A = 0x01, WIRELESS_MODE_B = 0x02, WIRELESS_MODE_G = 0x04, WIRELESS_MODE_AUTO = 0x08, WIRELESS_MODE_N_24G = 0x10, WIRELESS_MODE_N_5G = 0x20 }; #define IS_WIRELESS_MODE_A(wirelessmode) \ (wirelessmode == WIRELESS_MODE_A) #define IS_WIRELESS_MODE_B(wirelessmode) \ (wirelessmode == WIRELESS_MODE_B) #define IS_WIRELESS_MODE_G(wirelessmode) \ (wirelessmode == WIRELESS_MODE_G) #define IS_WIRELESS_MODE_N_24G(wirelessmode) \ (wirelessmode == WIRELESS_MODE_N_24G) #define IS_WIRELESS_MODE_N_5G(wirelessmode) \ (wirelessmode == WIRELESS_MODE_N_5G) enum ratr_table_mode { RATR_INX_WIRELESS_NGB = 0, RATR_INX_WIRELESS_NG = 1, RATR_INX_WIRELESS_NB = 2, RATR_INX_WIRELESS_N = 3, RATR_INX_WIRELESS_GB = 4, RATR_INX_WIRELESS_G = 5, RATR_INX_WIRELESS_B = 6, RATR_INX_WIRELESS_MC = 7, RATR_INX_WIRELESS_A = 8, }; enum rtl_link_state { MAC80211_NOLINK = 0, MAC80211_LINKING = 1, MAC80211_LINKED = 2, MAC80211_LINKED_SCANNING = 3, }; enum act_category { ACT_CAT_QOS = 1, ACT_CAT_DLS = 2, ACT_CAT_BA = 3, ACT_CAT_HT = 7, ACT_CAT_WMM = 17, }; enum ba_action { ACT_ADDBAREQ = 0, ACT_ADDBARSP = 1, ACT_DELBA = 2, }; struct octet_string { u8 *octet; u16 length; }; struct rtl_hdr_3addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; __le16 seq_ctl; u8 payload[0]; } __packed; struct rtl_info_element { u8 id; u8 len; u8 data[0]; } __packed; struct rtl_probe_rsp { struct rtl_hdr_3addr header; u32 time_stamp[2]; __le16 beacon_interval; __le16 capability; /*SSID, supported rates, FH params, DS params, CF params, IBSS params, TIM (if beacon), RSN */ struct rtl_info_element info_element[0]; } __packed; /*LED related.*/ /*ledpin Identify how to implement this SW led.*/ struct rtl_led { void *hw; enum rtl_led_pin ledpin; bool ledon; }; struct rtl_led_ctl { bool led_opendrain; struct rtl_led sw_led0; struct rtl_led sw_led1; }; struct rtl_qos_parameters { __le16 cw_min; __le16 cw_max; u8 aifs; u8 flag; __le16 tx_op; } __packed; struct rt_smooth_data { u32 elements[100]; /*array to store values */ u32 index; /*index to current array to store */ u32 total_num; /*num of valid elements */ u32 total_val; /*sum of valid elements */ }; struct false_alarm_statistics { u32 cnt_parity_fail; u32 cnt_rate_illegal; u32 cnt_crc8_fail; u32 cnt_mcs_fail; u32 cnt_fast_fsync_fail; u32 cnt_sb_search_fail; u32 cnt_ofdm_fail; u32 cnt_cck_fail; u32 cnt_all; }; struct init_gain { u8 xaagccore1; u8 xbagccore1; u8 xcagccore1; u8 xdagccore1; u8 cca; }; struct wireless_stats { unsigned long txbytesunicast; unsigned long txbytesmulticast; unsigned long txbytesbroadcast; unsigned long rxbytesunicast; long rx_snr_db[4]; /*Correct smoothed ss in Dbm, only used in driver to report real power now. */ long recv_signal_power; long signal_quality; long last_sigstrength_inpercent; u32 rssi_calculate_cnt; /*Transformed, in dbm. Beautified signal strength for UI, not correct. */ long signal_strength; u8 rx_rssi_percentage[4]; u8 rx_evm_percentage[2]; struct rt_smooth_data ui_rssi; struct rt_smooth_data ui_link_quality; }; struct rate_adaptive { u8 rate_adaptive_disabled; u8 ratr_state; u16 reserve; u32 high_rssi_thresh_for_ra; u32 high2low_rssi_thresh_for_ra; u8 low2high_rssi_thresh_for_ra40m; u32 low_rssi_thresh_for_ra40M; u8 low2high_rssi_thresh_for_ra20m; u32 low_rssi_thresh_for_ra20M; u32 upper_rssi_threshold_ratr; u32 middleupper_rssi_threshold_ratr; u32 middle_rssi_threshold_ratr; u32 middlelow_rssi_threshold_ratr; u32 low_rssi_threshold_ratr; u32 ultralow_rssi_threshold_ratr; u32 low_rssi_threshold_ratr_40m; u32 low_rssi_threshold_ratr_20m; u8 ping_rssi_enable; u32 ping_rssi_ratr; u32 ping_rssi_thresh_for_ra; u32 last_ratr; u8 pre_ratr_state; }; struct regd_pair_mapping { u16 reg_dmnenum; u16 reg_5ghz_ctl; u16 reg_2ghz_ctl; }; struct rtl_regulatory { char alpha2[2]; u16 country_code; u16 max_power_level; u32 tp_scale; u16 current_rd; u16 current_rd_ext; int16_t power_limit; struct regd_pair_mapping *regpair; }; struct rtl_rfkill { bool rfkill_state; /*0 is off, 1 is on */ }; #define IQK_MATRIX_REG_NUM 8 #define IQK_MATRIX_SETTINGS_NUM (1 + 24 + 21) struct iqk_matrix_regs { bool iqk_done; long value[1][IQK_MATRIX_REG_NUM]; }; struct phy_parameters { u16 length; u32 *pdata; }; enum hw_param_tab_index { PHY_REG_2T, PHY_REG_1T, PHY_REG_PG, RADIOA_2T, RADIOB_2T, RADIOA_1T, RADIOB_1T, MAC_REG, AGCTAB_2T, AGCTAB_1T, MAX_TAB }; struct rtl_phy { struct bb_reg_def phyreg_def[4]; /*Radio A/B/C/D */ struct init_gain initgain_backup; enum io_type current_io_type; u8 rf_mode; u8 rf_type; u8 current_chan_bw; u8 set_bwmode_inprogress; u8 sw_chnl_inprogress; u8 sw_chnl_stage; u8 sw_chnl_step; u8 current_channel; u8 h2c_box_num; u8 set_io_inprogress; u8 lck_inprogress; /* record for power tracking */ s32 reg_e94; s32 reg_e9c; s32 reg_ea4; s32 reg_eac; s32 reg_eb4; s32 reg_ebc; s32 reg_ec4; s32 reg_ecc; u8 rfpienable; u8 reserve_0; u16 reserve_1; u32 reg_c04, reg_c08, reg_874; u32 adda_backup[16]; u32 iqk_mac_backup[IQK_MAC_REG_NUM]; u32 iqk_bb_backup[10]; bool iqk_initialized; /* Dual mac */ bool need_iqk; struct iqk_matrix_regs iqk_matrix_regsetting[IQK_MATRIX_SETTINGS_NUM]; bool rfpi_enable; u8 pwrgroup_cnt; u8 cck_high_power; /* MAX_PG_GROUP groups of pwr diff by rates */ u32 mcs_txpwrlevel_origoffset[MAX_PG_GROUP][16]; u8 default_initialgain[4]; /* the current Tx power level */ u8 cur_cck_txpwridx; u8 cur_ofdm24g_txpwridx; u32 rfreg_chnlval[2]; bool apk_done; u32 reg_rf3c[2]; /* pathA / pathB */ /* bfsync */ u8 framesync; u32 framesync_c34; u8 num_total_rfpath; struct phy_parameters hwparam_tables[MAX_TAB]; u16 rf_pathmap; }; #define MAX_TID_COUNT 9 #define RTL_AGG_STOP 0 #define RTL_AGG_PROGRESS 1 #define RTL_AGG_START 2 #define RTL_AGG_OPERATIONAL 3 #define RTL_AGG_OFF 0 #define RTL_AGG_ON 1 #define RTL_RX_AGG_START 1 #define RTL_RX_AGG_STOP 0 #define RTL_AGG_EMPTYING_HW_QUEUE_ADDBA 2 #define RTL_AGG_EMPTYING_HW_QUEUE_DELBA 3 struct rtl_ht_agg { u16 txq_id; u16 wait_for_ba; u16 start_idx; u64 bitmap; u32 rate_n_flags; u8 agg_state; u8 rx_agg_state; }; struct rtl_tid_data { u16 seq_number; struct rtl_ht_agg agg; }; struct rssi_sta { long undecorated_smoothed_pwdb; }; struct rtl_sta_info { struct list_head list; u8 ratr_index; u8 wireless_mode; u8 mimo_ps; struct rtl_tid_data tids[MAX_TID_COUNT]; /* just used for ap adhoc or mesh*/ struct rssi_sta rssi_stat; } __packed; struct rtl_priv; struct rtl_io { struct device *dev; struct mutex bb_mutex; /*PCI MEM map */ unsigned long pci_mem_end; /*shared mem end */ unsigned long pci_mem_start; /*shared mem start */ /*PCI IO map */ unsigned long pci_base_addr; /*device I/O address */ void (*write8_async) (struct rtl_priv *rtlpriv, u32 addr, u8 val); void (*write16_async) (struct rtl_priv *rtlpriv, u32 addr, u16 val); void (*write32_async) (struct rtl_priv *rtlpriv, u32 addr, u32 val); void (*writeN_sync) (struct rtl_priv *rtlpriv, u32 addr, void *buf, u16 len); u8(*read8_sync) (struct rtl_priv *rtlpriv, u32 addr); u16(*read16_sync) (struct rtl_priv *rtlpriv, u32 addr); u32(*read32_sync) (struct rtl_priv *rtlpriv, u32 addr); }; struct rtl_mac { u8 mac_addr[ETH_ALEN]; u8 mac80211_registered; u8 beacon_enabled; u32 tx_ss_num; u32 rx_ss_num; struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; struct ieee80211_hw *hw; struct ieee80211_vif *vif; enum nl80211_iftype opmode; /*Probe Beacon management */ struct rtl_tid_data tids[MAX_TID_COUNT]; enum rtl_link_state link_state; int n_channels; int n_bitrates; bool offchan_delay; /*filters */ u32 rx_conf; u16 rx_mgt_filter; u16 rx_ctrl_filter; u16 rx_data_filter; bool act_scanning; u8 cnt_after_linked; /* early mode */ /* skb wait queue */ struct sk_buff_head skb_waitq[MAX_TID_COUNT]; u8 earlymode_threshold; /*RDG*/ bool rdg_en; /*AP*/ u8 bssid[6]; u32 vendor; u8 mcs[16]; /* 16 bytes mcs for HT rates. */ u32 basic_rates; /* b/g rates */ u8 ht_enable; u8 sgi_40; u8 sgi_20; u8 bw_40; u8 mode; /* wireless mode */ u8 slot_time; u8 short_preamble; u8 use_cts_protect; u8 cur_40_prime_sc; u8 cur_40_prime_sc_bk; u64 tsf; u8 retry_short; u8 retry_long; u16 assoc_id; /*IBSS*/ int beacon_interval; /*AMPDU*/ u8 min_space_cfg; /*For Min spacing configurations */ u8 max_mss_density; u8 current_ampdu_factor; u8 current_ampdu_density; /*QOS & EDCA */ struct ieee80211_tx_queue_params edca_param[RTL_MAC80211_NUM_QUEUE]; struct rtl_qos_parameters ac[AC_MAX]; }; struct rtl_hal { struct ieee80211_hw *hw; bool up_first_time; bool first_init; bool being_init_adapter; bool bbrf_ready; enum intf_type interface; u16 hw_type; /*92c or 92d or 92s and so on */ u8 ic_class; u8 oem_id; u32 version; /*version of chip */ u8 state; /*stop 0, start 1 */ /*firmware */ u32 fwsize; u8 *pfirmware; u16 fw_version; u16 fw_subversion; bool h2c_setinprogress; u8 last_hmeboxnum; bool fw_ready; /*Reserve page start offset except beacon in TxQ. */ u8 fw_rsvdpage_startoffset; u8 h2c_txcmd_seq; /* FW Cmd IO related */ u16 fwcmd_iomap; u32 fwcmd_ioparam; bool set_fwcmd_inprogress; u8 current_fwcmd_io; /**/ bool driver_going2unload; /*AMPDU init min space*/ u8 minspace_cfg; /*For Min spacing configurations */ /* Dual mac */ enum macphy_mode macphymode; enum band_type current_bandtype; /* 0:2.4G, 1:5G */ enum band_type current_bandtypebackup; enum band_type bandset; /* dual MAC 0--Mac0 1--Mac1 */ u32 interfaceindex; /* just for DualMac S3S4 */ u8 macphyctl_reg; bool earlymode_enable; /* Dual mac*/ bool during_mac0init_radiob; bool during_mac1init_radioa; bool reloadtxpowerindex; /* True if IMR or IQK have done for 2.4G in scan progress */ bool load_imrandiqk_setting_for2g; bool disable_amsdu_8k; bool master_of_dmsp; bool slave_of_dmsp; }; struct rtl_security { /*default 0 */ bool use_sw_sec; bool being_setkey; bool use_defaultkey; /*Encryption Algorithm for Unicast Packet */ enum rt_enc_alg pairwise_enc_algorithm; /*Encryption Algorithm for Brocast/Multicast */ enum rt_enc_alg group_enc_algorithm; /*Cam Entry Bitmap */ u32 hwsec_cam_bitmap; u8 hwsec_cam_sta_addr[TOTAL_CAM_ENTRY][ETH_ALEN]; /*local Key buffer, indx 0 is for pairwise key 1-4 is for agoup key. */ u8 key_buf[KEY_BUF_SIZE][MAX_KEY_LEN]; u8 key_len[KEY_BUF_SIZE]; /*The pointer of Pairwise Key, it always points to KeyBuf[4] */ u8 *pairwise_key; }; struct rtl_dm { /*PHY status for Dynamic Management */ long entry_min_undecoratedsmoothed_pwdb; long undecorated_smoothed_pwdb; /*out dm */ long entry_max_undecoratedsmoothed_pwdb; bool dm_initialgain_enable; bool dynamic_txpower_enable; bool current_turbo_edca; bool is_any_nonbepkts; /*out dm */ bool is_cur_rdlstate; bool txpower_trackinginit; bool disable_framebursting; bool cck_inch14; bool txpower_tracking; bool useramask; bool rfpath_rxenable[4]; bool inform_fw_driverctrldm; bool current_mrc_switch; u8 txpowercount; u8 thermalvalue_rxgain; u8 thermalvalue_iqk; u8 thermalvalue_lck; u8 thermalvalue; u8 last_dtp_lvl; u8 thermalvalue_avg[AVG_THERMAL_NUM]; u8 thermalvalue_avg_index; bool done_txpower; u8 dynamic_txhighpower_lvl; /*Tx high power level */ u8 dm_flag; /*Indicate each dynamic mechanism's status. */ u8 dm_type; u8 txpower_track_control; bool interrupt_migration; bool disable_tx_int; char ofdm_index[2]; char cck_index; /* DMSP */ bool supp_phymode_switch; }; #define EFUSE_MAX_LOGICAL_SIZE 256 struct rtl_efuse { bool autoLoad_ok; bool bootfromefuse; u16 max_physical_size; u8 efuse_map[2][EFUSE_MAX_LOGICAL_SIZE]; u16 efuse_usedbytes; u8 efuse_usedpercentage; #ifdef EFUSE_REPG_WORKAROUND bool efuse_re_pg_sec1flag; u8 efuse_re_pg_data[8]; #endif u8 autoload_failflag; u8 autoload_status; short epromtype; u16 eeprom_vid; u16 eeprom_did; u16 eeprom_svid; u16 eeprom_smid; u8 eeprom_oemid; u16 eeprom_channelplan; u8 eeprom_version; u8 board_type; u8 external_pa; u8 dev_addr[6]; bool txpwr_fromeprom; u8 eeprom_crystalcap; u8 eeprom_tssi[2]; u8 eeprom_tssi_5g[3][2]; /* for 5GL/5GM/5GH band. */ u8 eeprom_pwrlimit_ht20[CHANNEL_GROUP_MAX]; u8 eeprom_pwrlimit_ht40[CHANNEL_GROUP_MAX]; u8 eeprom_chnlarea_txpwr_cck[2][CHANNEL_GROUP_MAX_2G]; u8 eeprom_chnlarea_txpwr_ht40_1s[2][CHANNEL_GROUP_MAX]; u8 eeprom_chnlarea_txpwr_ht40_2sdiif[2][CHANNEL_GROUP_MAX]; u8 txpwrlevel_cck[2][CHANNEL_MAX_NUMBER_2G]; u8 txpwrlevel_ht40_1s[2][CHANNEL_MAX_NUMBER]; /*For HT 40MHZ pwr */ u8 txpwrlevel_ht40_2s[2][CHANNEL_MAX_NUMBER]; /*For HT 40MHZ pwr */ u8 internal_pa_5g[2]; /* pathA / pathB */ u8 eeprom_c9; u8 eeprom_cc; /*For power group */ u8 eeprom_pwrgroup[2][3]; u8 pwrgroup_ht20[2][CHANNEL_MAX_NUMBER]; u8 pwrgroup_ht40[2][CHANNEL_MAX_NUMBER]; char txpwr_ht20diff[2][CHANNEL_MAX_NUMBER]; /*HT 20<->40 Pwr diff */ /*For HT<->legacy pwr diff*/ u8 txpwr_legacyhtdiff[2][CHANNEL_MAX_NUMBER]; u8 txpwr_safetyflag; /* Band edge enable flag */ u16 eeprom_txpowerdiff; u8 legacy_httxpowerdiff; /* Legacy to HT rate power diff */ u8 antenna_txpwdiff[3]; u8 eeprom_regulatory; u8 eeprom_thermalmeter; u8 thermalmeter[2]; /*ThermalMeter, index 0 for RFIC0, 1 for RFIC1 */ u16 tssi_13dbm; u8 crystalcap; /* CrystalCap. */ u8 delta_iqk; u8 delta_lck; u8 legacy_ht_txpowerdiff; /*Legacy to HT rate power diff */ bool apk_thermalmeterignore; bool b1x1_recvcombine; bool b1ss_support; /*channel plan */ u8 channel_plan; }; struct rtl_ps_ctl { bool pwrdomain_protect; bool in_powersavemode; bool rfchange_inprogress; bool swrf_processing; bool hwradiooff; /* * just for PCIE ASPM * If it supports ASPM, Offset[560h] = 0x40, * otherwise Offset[560h] = 0x00. * */ bool support_aspm; bool support_backdoor; /*for LPS */ enum rt_psmode dot11_psmode; /*Power save mode configured. */ bool swctrl_lps; bool leisure_ps; bool fwctrl_lps; u8 fwctrl_psmode; /*For Fw control LPS mode */ u8 reg_fwctrl_lps; /*Record Fw PS mode status. */ bool fw_current_inpsmode; u8 reg_max_lps_awakeintvl; bool report_linked; /*for IPS */ bool inactiveps; u32 rfoff_reason; /*RF OFF Level */ u32 cur_ps_level; u32 reg_rfps_level; /*just for PCIE ASPM */ u8 const_amdpci_aspm; bool pwrdown_mode; enum rf_pwrstate inactive_pwrstate; enum rf_pwrstate rfpwr_state; /*cur power state */ /* for SW LPS*/ bool sw_ps_enabled; bool state; bool state_inap; bool multi_buffered; u16 nullfunc_seq; unsigned int dtim_counter; unsigned int sleep_ms; unsigned long last_sleep_jiffies; unsigned long last_awake_jiffies; unsigned long last_delaylps_stamp_jiffies; unsigned long last_dtim; unsigned long last_beacon; unsigned long last_action; unsigned long last_slept; }; struct rtl_stats { u32 mac_time[2]; s8 rssi; u8 signal; u8 noise; u16 rate; /*in 100 kbps */ u8 received_channel; u8 control; u8 mask; u8 freq; u16 len; u64 tsf; u32 beacon_time; u8 nic_type; u16 length; u8 signalquality; /*in 0-100 index. */ /* * Real power in dBm for this packet, * no beautification and aggregation. * */ s32 recvsignalpower; s8 rxpower; /*in dBm Translate from PWdB */ u8 signalstrength; /*in 0-100 index. */ u16 hwerror:1; u16 crc:1; u16 icv:1; u16 shortpreamble:1; u16 antenna:1; u16 decrypted:1; u16 wakeup:1; u32 timestamp_low; u32 timestamp_high; u8 rx_drvinfo_size; u8 rx_bufshift; bool isampdu; bool isfirst_ampdu; bool rx_is40Mhzpacket; u32 rx_pwdb_all; u8 rx_mimo_signalstrength[4]; /*in 0~100 index */ s8 rx_mimo_signalquality[2]; bool packet_matchbssid; bool is_cck; bool is_ht; bool packet_toself; bool packet_beacon; /*for rssi */ char cck_adc_pwdb[4]; /*for rx path selection */ }; struct rt_link_detect { /* count for roaming */ u32 bcn_rx_inperiod; u32 roam_times; u32 num_tx_in4period[4]; u32 num_rx_in4period[4]; u32 num_tx_inperiod; u32 num_rx_inperiod; bool busytraffic; bool tx_busy_traffic; bool rx_busy_traffic; bool higher_busytraffic; bool higher_busyrxtraffic; u32 tidtx_in4period[MAX_TID_COUNT][4]; u32 tidtx_inperiod[MAX_TID_COUNT]; bool higher_busytxtraffic[MAX_TID_COUNT]; }; struct rtl_tcb_desc { u8 packet_bw:1; u8 multicast:1; u8 broadcast:1; u8 rts_stbc:1; u8 rts_enable:1; u8 cts_enable:1; u8 rts_use_shortpreamble:1; u8 rts_use_shortgi:1; u8 rts_sc:1; u8 rts_bw:1; u8 rts_rate; u8 use_shortgi:1; u8 use_shortpreamble:1; u8 use_driver_rate:1; u8 disable_ratefallback:1; u8 ratr_index; u8 mac_id; u8 hw_rate; u8 last_inipkt:1; u8 cmd_or_init:1; u8 queue_index; /* early mode */ u8 empkt_num; /* The max value by HW */ u32 empkt_len[5]; }; struct rtl_hal_ops { int (*init_sw_vars) (struct ieee80211_hw *hw); void (*deinit_sw_vars) (struct ieee80211_hw *hw); void (*read_chip_version)(struct ieee80211_hw *hw); void (*read_eeprom_info) (struct ieee80211_hw *hw); void (*interrupt_recognized) (struct ieee80211_hw *hw, u32 *p_inta, u32 *p_intb); int (*hw_init) (struct ieee80211_hw *hw); void (*hw_disable) (struct ieee80211_hw *hw); void (*hw_suspend) (struct ieee80211_hw *hw); void (*hw_resume) (struct ieee80211_hw *hw); void (*enable_interrupt) (struct ieee80211_hw *hw); void (*disable_interrupt) (struct ieee80211_hw *hw); int (*set_network_type) (struct ieee80211_hw *hw, enum nl80211_iftype type); void (*set_chk_bssid)(struct ieee80211_hw *hw, bool check_bssid); void (*set_bw_mode) (struct ieee80211_hw *hw, enum nl80211_channel_type ch_type); u8(*switch_channel) (struct ieee80211_hw *hw); void (*set_qos) (struct ieee80211_hw *hw, int aci); void (*set_bcn_reg) (struct ieee80211_hw *hw); void (*set_bcn_intv) (struct ieee80211_hw *hw); void (*update_interrupt_mask) (struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr); void (*get_hw_reg) (struct ieee80211_hw *hw, u8 variable, u8 *val); void (*set_hw_reg) (struct ieee80211_hw *hw, u8 variable, u8 *val); void (*update_rate_tbl) (struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level); void (*update_rate_mask) (struct ieee80211_hw *hw, u8 rssi_level); void (*fill_tx_desc) (struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); void (*fill_fake_txdesc) (struct ieee80211_hw *hw, u8 *pDesc, u32 buffer_len, bool bIsPsPoll); void (*fill_tx_cmddesc) (struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb); bool (*cmd_send_packet)(struct ieee80211_hw *hw, struct sk_buff *skb); bool (*query_rx_desc) (struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); void (*set_channel_access) (struct ieee80211_hw *hw); bool (*radio_onoff_checking) (struct ieee80211_hw *hw, u8 *valid); void (*dm_watchdog) (struct ieee80211_hw *hw); void (*scan_operation_backup) (struct ieee80211_hw *hw, u8 operation); bool (*set_rf_power_state) (struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); void (*led_control) (struct ieee80211_hw *hw, enum led_ctl_mode ledaction); void (*set_desc) (u8 *pdesc, bool istx, u8 desc_name, u8 *val); u32 (*get_desc) (u8 *pdesc, bool istx, u8 desc_name); void (*tx_polling) (struct ieee80211_hw *hw, u8 hw_queue); void (*enable_hw_sec) (struct ieee80211_hw *hw); void (*set_key) (struct ieee80211_hw *hw, u32 key_index, u8 *macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); void (*init_sw_leds) (struct ieee80211_hw *hw); void (*deinit_sw_leds) (struct ieee80211_hw *hw); u32 (*get_bbreg) (struct ieee80211_hw *hw, u32 regaddr, u32 bitmask); void (*set_bbreg) (struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data); u32 (*get_rfreg) (struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); void (*set_rfreg) (struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); void (*allow_all_destaddr)(struct ieee80211_hw *hw, bool allow_all_da, bool write_into_reg); void (*linked_set_reg) (struct ieee80211_hw *hw); void (*check_switch_to_dmdp) (struct ieee80211_hw *hw); void (*dualmac_easy_concurrent) (struct ieee80211_hw *hw); void (*dualmac_switch_to_dmdp) (struct ieee80211_hw *hw); bool (*phy_rf6052_config) (struct ieee80211_hw *hw); void (*phy_rf6052_set_cck_txpower) (struct ieee80211_hw *hw, u8 *powerlevel); void (*phy_rf6052_set_ofdm_txpower) (struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel); bool (*config_bb_with_headerfile) (struct ieee80211_hw *hw, u8 configtype); bool (*config_bb_with_pgheaderfile) (struct ieee80211_hw *hw, u8 configtype); void (*phy_lc_calibrate) (struct ieee80211_hw *hw, bool is2t); void (*phy_set_bw_mode_callback) (struct ieee80211_hw *hw); void (*dm_dynamic_txpower) (struct ieee80211_hw *hw); }; struct rtl_intf_ops { /*com */ void (*read_efuse_byte)(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf); int (*adapter_start) (struct ieee80211_hw *hw); void (*adapter_stop) (struct ieee80211_hw *hw); bool (*check_buddy_priv)(struct ieee80211_hw *hw, struct rtl_priv **buddy_priv); int (*adapter_tx) (struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb, struct rtl_tcb_desc *ptcb_desc); void (*flush)(struct ieee80211_hw *hw, bool drop); int (*reset_trx_ring) (struct ieee80211_hw *hw); bool (*waitq_insert) (struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb); /*pci */ void (*disable_aspm) (struct ieee80211_hw *hw); void (*enable_aspm) (struct ieee80211_hw *hw); /*usb */ }; struct rtl_mod_params { /* default: 0 = using hardware encryption */ bool sw_crypto; /* default: 0 = DBG_EMERG (0)*/ int debug; /* default: 1 = using no linked power save */ bool inactiveps; /* default: 1 = using linked sw power save */ bool swctrl_lps; /* default: 1 = using linked fw power save */ bool fwctrl_lps; }; struct rtl_hal_usbint_cfg { /* data - rx */ u32 in_ep_num; u32 rx_urb_num; u32 rx_max_size; /* op - rx */ void (*usb_rx_hdl)(struct ieee80211_hw *, struct sk_buff *); void (*usb_rx_segregate_hdl)(struct ieee80211_hw *, struct sk_buff *, struct sk_buff_head *); /* tx */ void (*usb_tx_cleanup)(struct ieee80211_hw *, struct sk_buff *); int (*usb_tx_post_hdl)(struct ieee80211_hw *, struct urb *, struct sk_buff *); struct sk_buff *(*usb_tx_aggregate_hdl)(struct ieee80211_hw *, struct sk_buff_head *); /* endpoint mapping */ int (*usb_endpoint_mapping)(struct ieee80211_hw *hw); u16 (*usb_mq_to_hwq)(__le16 fc, u16 mac80211_queue_index); }; struct rtl_hal_cfg { u8 bar_id; bool write_readback; char *name; char *fw_name; struct rtl_hal_ops *ops; struct rtl_mod_params *mod_params; struct rtl_hal_usbint_cfg *usb_interface_cfg; /*this map used for some registers or vars defined int HAL but used in MAIN */ u32 maps[RTL_VAR_MAP_MAX]; }; struct rtl_locks { /* mutex */ struct mutex conf_mutex; struct mutex ps_mutex; /*spin lock */ spinlock_t ips_lock; spinlock_t irq_th_lock; spinlock_t h2c_lock; spinlock_t rf_ps_lock; spinlock_t rf_lock; spinlock_t lps_lock; spinlock_t waitq_lock; spinlock_t entry_list_lock; spinlock_t usb_lock; /*Dual mac*/ spinlock_t cck_and_rw_pagea_lock; /*Easy concurrent*/ spinlock_t check_sendpkt_lock; }; struct rtl_works { struct ieee80211_hw *hw; /*timer */ struct timer_list watchdog_timer; struct timer_list dualmac_easyconcurrent_retrytimer; /*task */ struct tasklet_struct irq_tasklet; struct tasklet_struct irq_prepare_bcn_tasklet; /*work queue */ struct workqueue_struct *rtl_wq; struct delayed_work watchdog_wq; struct delayed_work ips_nic_off_wq; /* For SW LPS */ struct delayed_work ps_work; struct delayed_work ps_rfon_wq; struct work_struct lps_leave_work; }; struct rtl_debug { u32 dbgp_type[DBGP_TYPE_MAX]; u32 global_debuglevel; u64 global_debugcomponents; /* add for proc debug */ struct proc_dir_entry *proc_dir; char proc_name[20]; }; #define MIMO_PS_STATIC 0 #define MIMO_PS_DYNAMIC 1 #define MIMO_PS_NOLIMIT 3 struct rtl_dualmac_easy_concurrent_ctl { enum band_type currentbandtype_backfordmdp; bool close_bbandrf_for_dmsp; bool change_to_dmdp; bool change_to_dmsp; bool switch_in_process; }; struct rtl_dmsp_ctl { bool activescan_for_slaveofdmsp; bool scan_for_anothermac_fordmsp; bool scan_for_itself_fordmsp; bool writedig_for_anothermacofdmsp; u32 curdigvalue_for_anothermacofdmsp; bool changecckpdstate_for_anothermacofdmsp; u8 curcckpdstate_for_anothermacofdmsp; bool changetxhighpowerlvl_for_anothermacofdmsp; u8 curtxhighlvl_for_anothermacofdmsp; long rssivalmin_for_anothermacofdmsp; }; struct ps_t { u8 pre_ccastate; u8 cur_ccasate; u8 pre_rfstate; u8 cur_rfstate; long rssi_val_min; }; struct dig_t { u32 rssi_lowthresh; u32 rssi_highthresh; u32 fa_lowthresh; u32 fa_highthresh; long last_min_undecorated_pwdb_for_dm; long rssi_highpower_lowthresh; long rssi_highpower_highthresh; u32 recover_cnt; u32 pre_igvalue; u32 cur_igvalue; long rssi_val; u8 dig_enable_flag; u8 dig_ext_port_stage; u8 dig_algorithm; u8 dig_twoport_algorithm; u8 dig_dbgmode; u8 dig_slgorithm_switch; u8 cursta_connectstate; u8 presta_connectstate; u8 curmultista_connectstate; char backoff_val; char backoff_val_range_max; char backoff_val_range_min; u8 rx_gain_range_max; u8 rx_gain_range_min; u8 min_undecorated_pwdb_for_dm; u8 rssi_val_min; u8 pre_cck_pd_state; u8 cur_cck_pd_state; u8 pre_cck_fa_state; u8 cur_cck_fa_state; u8 pre_ccastate; u8 cur_ccasate; u8 large_fa_hit; u8 forbidden_igi; u8 dig_state; u8 dig_highpwrstate; u8 cur_sta_connectstate; u8 pre_sta_connectstate; u8 cur_ap_connectstate; u8 pre_ap_connectstate; u8 cur_pd_thstate; u8 pre_pd_thstate; u8 cur_cs_ratiostate; u8 pre_cs_ratiostate; u8 backoff_enable_flag; char backoffval_range_max; char backoffval_range_min; }; struct rtl_global_var { /* from this list we can get * other adapter's rtl_priv */ struct list_head glb_priv_list; spinlock_t glb_list_lock; }; struct rtl_priv { struct completion firmware_loading_complete; struct list_head list; struct rtl_priv *buddy_priv; struct rtl_global_var *glb_var; struct rtl_dualmac_easy_concurrent_ctl easy_concurrent_ctl; struct rtl_dmsp_ctl dmsp_ctl; struct rtl_locks locks; struct rtl_works works; struct rtl_mac mac80211; struct rtl_hal rtlhal; struct rtl_regulatory regd; struct rtl_rfkill rfkill; struct rtl_io io; struct rtl_phy phy; struct rtl_dm dm; struct rtl_security sec; struct rtl_efuse efuse; struct rtl_ps_ctl psc; struct rate_adaptive ra; struct wireless_stats stats; struct rt_link_detect link_info; struct false_alarm_statistics falsealm_cnt; struct rtl_rate_priv *rate_priv; /* sta entry list for ap adhoc or mesh */ struct list_head entry_list; struct rtl_debug dbg; int max_fw_size; /* *hal_cfg : for diff cards *intf_ops : for diff interrface usb/pcie */ struct rtl_hal_cfg *cfg; struct rtl_intf_ops *intf_ops; /*this var will be set by set_bit, and was used to indicate status of interface or hardware */ unsigned long status; /* tables for dm */ struct dig_t dm_digtable; struct ps_t dm_pstable; /* data buffer pointer for USB reads */ __le32 *usb_data; int usb_data_index; /*This must be the last item so that it points to the data allocated beyond this structure like: rtl_pci_priv or rtl_usb_priv */ u8 priv[0]; }; #define rtl_priv(hw) (((struct rtl_priv *)(hw)->priv)) #define rtl_mac(rtlpriv) (&((rtlpriv)->mac80211)) #define rtl_hal(rtlpriv) (&((rtlpriv)->rtlhal)) #define rtl_efuse(rtlpriv) (&((rtlpriv)->efuse)) #define rtl_psc(rtlpriv) (&((rtlpriv)->psc)) /*************************************** Bluetooth Co-existence Related ****************************************/ enum bt_ant_num { ANT_X2 = 0, ANT_X1 = 1, }; enum bt_co_type { BT_2WIRE = 0, BT_ISSC_3WIRE = 1, BT_ACCEL = 2, BT_CSR_BC4 = 3, BT_CSR_BC8 = 4, BT_RTL8756 = 5, }; enum bt_cur_state { BT_OFF = 0, BT_ON = 1, }; enum bt_service_type { BT_SCO = 0, BT_A2DP = 1, BT_HID = 2, BT_HID_IDLE = 3, BT_SCAN = 4, BT_IDLE = 5, BT_OTHER_ACTION = 6, BT_BUSY = 7, BT_OTHERBUSY = 8, BT_PAN = 9, }; enum bt_radio_shared { BT_RADIO_SHARED = 0, BT_RADIO_INDIVIDUAL = 1, }; struct bt_coexist_info { /* EEPROM BT info. */ u8 eeprom_bt_coexist; u8 eeprom_bt_type; u8 eeprom_bt_ant_num; u8 eeprom_bt_ant_isolation; u8 eeprom_bt_radio_shared; u8 bt_coexistence; u8 bt_ant_num; u8 bt_coexist_type; u8 bt_state; u8 bt_cur_state; /* 0:on, 1:off */ u8 bt_ant_isolation; /* 0:good, 1:bad */ u8 bt_pape_ctrl; /* 0:SW, 1:SW/HW dynamic */ u8 bt_service; u8 bt_radio_shared_type; u8 bt_rfreg_origin_1e; u8 bt_rfreg_origin_1f; u8 bt_rssi_state; u32 ratio_tx; u32 ratio_pri; u32 bt_edca_ul; u32 bt_edca_dl; bool init_set; bool bt_busy_traffic; bool bt_traffic_mode_set; bool bt_non_traffic_mode_set; bool fw_coexist_all_off; bool sw_coexist_all_off; u32 current_state; u32 previous_state; u8 bt_pre_rssi_state; u8 reg_bt_iso; u8 reg_bt_sco; }; /**************************************** mem access macro define start Call endian free function when 1. Read/write packet content. 2. Before write integer to IO. 3. After read integer from IO. ****************************************/ /* Convert little data endian to host ordering */ #define EF1BYTE(_val) \ ((u8)(_val)) #define EF2BYTE(_val) \ (le16_to_cpu(_val)) #define EF4BYTE(_val) \ (le32_to_cpu(_val)) /* Read data from memory */ #define READEF1BYTE(_ptr) \ EF1BYTE(*((u8 *)(_ptr))) /* Read le16 data from memory and convert to host ordering */ #define READEF2BYTE(_ptr) \ EF2BYTE(*(_ptr)) #define READEF4BYTE(_ptr) \ EF4BYTE(*(_ptr)) /* Write data to memory */ #define WRITEEF1BYTE(_ptr, _val) \ (*((u8 *)(_ptr))) = EF1BYTE(_val) /* Write le16 data to memory in host ordering */ #define WRITEEF2BYTE(_ptr, _val) \ (*((u16 *)(_ptr))) = EF2BYTE(_val) #define WRITEEF4BYTE(_ptr, _val) \ (*((u32 *)(_ptr))) = EF2BYTE(_val) /* Create a bit mask * Examples: * BIT_LEN_MASK_32(0) => 0x00000000 * BIT_LEN_MASK_32(1) => 0x00000001 * BIT_LEN_MASK_32(2) => 0x00000003 * BIT_LEN_MASK_32(32) => 0xFFFFFFFF */ #define BIT_LEN_MASK_32(__bitlen) \ (0xFFFFFFFF >> (32 - (__bitlen))) #define BIT_LEN_MASK_16(__bitlen) \ (0xFFFF >> (16 - (__bitlen))) #define BIT_LEN_MASK_8(__bitlen) \ (0xFF >> (8 - (__bitlen))) /* Create an offset bit mask * Examples: * BIT_OFFSET_LEN_MASK_32(0, 2) => 0x00000003 * BIT_OFFSET_LEN_MASK_32(16, 2) => 0x00030000 */ #define BIT_OFFSET_LEN_MASK_32(__bitoffset, __bitlen) \ (BIT_LEN_MASK_32(__bitlen) << (__bitoffset)) #define BIT_OFFSET_LEN_MASK_16(__bitoffset, __bitlen) \ (BIT_LEN_MASK_16(__bitlen) << (__bitoffset)) #define BIT_OFFSET_LEN_MASK_8(__bitoffset, __bitlen) \ (BIT_LEN_MASK_8(__bitlen) << (__bitoffset)) /*Description: * Return 4-byte value in host byte ordering from * 4-byte pointer in little-endian system. */ #define LE_P4BYTE_TO_HOST_4BYTE(__pstart) \ (EF4BYTE(*((__le32 *)(__pstart)))) #define LE_P2BYTE_TO_HOST_2BYTE(__pstart) \ (EF2BYTE(*((__le16 *)(__pstart)))) #define LE_P1BYTE_TO_HOST_1BYTE(__pstart) \ (EF1BYTE(*((u8 *)(__pstart)))) /*Description: Translate subfield (continuous bits in little-endian) of 4-byte value to host byte ordering.*/ #define LE_BITS_TO_4BYTE(__pstart, __bitoffset, __bitlen) \ ( \ (LE_P4BYTE_TO_HOST_4BYTE(__pstart) >> (__bitoffset)) & \ BIT_LEN_MASK_32(__bitlen) \ ) #define LE_BITS_TO_2BYTE(__pstart, __bitoffset, __bitlen) \ ( \ (LE_P2BYTE_TO_HOST_2BYTE(__pstart) >> (__bitoffset)) & \ BIT_LEN_MASK_16(__bitlen) \ ) #define LE_BITS_TO_1BYTE(__pstart, __bitoffset, __bitlen) \ ( \ (LE_P1BYTE_TO_HOST_1BYTE(__pstart) >> (__bitoffset)) & \ BIT_LEN_MASK_8(__bitlen) \ ) /* Description: * Mask subfield (continuous bits in little-endian) of 4-byte value * and return the result in 4-byte value in host byte ordering. */ #define LE_BITS_CLEARED_TO_4BYTE(__pstart, __bitoffset, __bitlen) \ ( \ LE_P4BYTE_TO_HOST_4BYTE(__pstart) & \ (~BIT_OFFSET_LEN_MASK_32(__bitoffset, __bitlen)) \ ) #define LE_BITS_CLEARED_TO_2BYTE(__pstart, __bitoffset, __bitlen) \ ( \ LE_P2BYTE_TO_HOST_2BYTE(__pstart) & \ (~BIT_OFFSET_LEN_MASK_16(__bitoffset, __bitlen)) \ ) #define LE_BITS_CLEARED_TO_1BYTE(__pstart, __bitoffset, __bitlen) \ ( \ LE_P1BYTE_TO_HOST_1BYTE(__pstart) & \ (~BIT_OFFSET_LEN_MASK_8(__bitoffset, __bitlen)) \ ) /* Description: * Set subfield of little-endian 4-byte value to specified value. */ #define SET_BITS_TO_LE_4BYTE(__pstart, __bitoffset, __bitlen, __val) \ *((u32 *)(__pstart)) = \ ( \ LE_BITS_CLEARED_TO_4BYTE(__pstart, __bitoffset, __bitlen) | \ ((((u32)__val) & BIT_LEN_MASK_32(__bitlen)) << (__bitoffset)) \ ); #define SET_BITS_TO_LE_2BYTE(__pstart, __bitoffset, __bitlen, __val) \ *((u16 *)(__pstart)) = \ ( \ LE_BITS_CLEARED_TO_2BYTE(__pstart, __bitoffset, __bitlen) | \ ((((u16)__val) & BIT_LEN_MASK_16(__bitlen)) << (__bitoffset)) \ ); #define SET_BITS_TO_LE_1BYTE(__pstart, __bitoffset, __bitlen, __val) \ *((u8 *)(__pstart)) = EF1BYTE \ ( \ LE_BITS_CLEARED_TO_1BYTE(__pstart, __bitoffset, __bitlen) | \ ((((u8)__val) & BIT_LEN_MASK_8(__bitlen)) << (__bitoffset)) \ ); #define N_BYTE_ALIGMENT(__value, __aligment) ((__aligment == 1) ? \ (__value) : (((__value + __aligment - 1) / __aligment) * __aligment)) /**************************************** mem access macro define end ****************************************/ #define byte(x, n) ((x >> (8 * n)) & 0xff) #define packet_get_type(_packet) (EF1BYTE((_packet).octet[0]) & 0xFC) #define RTL_WATCH_DOG_TIME 2000 #define MSECS(t) msecs_to_jiffies(t) #define WLAN_FC_GET_VERS(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_VERS) #define WLAN_FC_GET_TYPE(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) #define WLAN_FC_GET_STYPE(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) #define WLAN_FC_MORE_DATA(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_MOREDATA) #define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) #define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) #define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) #define RT_RF_OFF_LEVL_ASPM BIT(0) /*PCI ASPM */ #define RT_RF_OFF_LEVL_CLK_REQ BIT(1) /*PCI clock request */ #define RT_RF_OFF_LEVL_PCI_D3 BIT(2) /*PCI D3 mode */ /*NIC halt, re-initialize hw parameters*/ #define RT_RF_OFF_LEVL_HALT_NIC BIT(3) #define RT_RF_OFF_LEVL_FREE_FW BIT(4) /*FW free, re-download the FW */ #define RT_RF_OFF_LEVL_FW_32K BIT(5) /*FW in 32k */ /*Always enable ASPM and Clock Req in initialization.*/ #define RT_RF_PS_LEVEL_ALWAYS_ASPM BIT(6) /* no matter RFOFF or SLEEP we set PS_ASPM_LEVL*/ #define RT_PS_LEVEL_ASPM BIT(7) /*When LPS is on, disable 2R if no packet is received or transmittd.*/ #define RT_RF_LPS_DISALBE_2R BIT(30) #define RT_RF_LPS_LEVEL_ASPM BIT(31) /*LPS with ASPM */ #define RT_IN_PS_LEVEL(ppsc, _ps_flg) \ ((ppsc->cur_ps_level & _ps_flg) ? true : false) #define RT_CLEAR_PS_LEVEL(ppsc, _ps_flg) \ (ppsc->cur_ps_level &= (~(_ps_flg))) #define RT_SET_PS_LEVEL(ppsc, _ps_flg) \ (ppsc->cur_ps_level |= _ps_flg) #define container_of_dwork_rtl(x, y, z) \ container_of(container_of(x, struct delayed_work, work), y, z) #define FILL_OCTET_STRING(_os, _octet, _len) \ (_os).octet = (u8 *)(_octet); \ (_os).length = (_len); #define CP_MACADDR(des, src) \ ((des)[0] = (src)[0], (des)[1] = (src)[1],\ (des)[2] = (src)[2], (des)[3] = (src)[3],\ (des)[4] = (src)[4], (des)[5] = (src)[5]) static inline u8 rtl_read_byte(struct rtl_priv *rtlpriv, u32 addr) { return rtlpriv->io.read8_sync(rtlpriv, addr); } static inline u16 rtl_read_word(struct rtl_priv *rtlpriv, u32 addr) { return rtlpriv->io.read16_sync(rtlpriv, addr); } static inline u32 rtl_read_dword(struct rtl_priv *rtlpriv, u32 addr) { return rtlpriv->io.read32_sync(rtlpriv, addr); } static inline void rtl_write_byte(struct rtl_priv *rtlpriv, u32 addr, u8 val8) { rtlpriv->io.write8_async(rtlpriv, addr, val8); if (rtlpriv->cfg->write_readback) rtlpriv->io.read8_sync(rtlpriv, addr); } static inline void rtl_write_word(struct rtl_priv *rtlpriv, u32 addr, u16 val16) { rtlpriv->io.write16_async(rtlpriv, addr, val16); if (rtlpriv->cfg->write_readback) rtlpriv->io.read16_sync(rtlpriv, addr); } static inline void rtl_write_dword(struct rtl_priv *rtlpriv, u32 addr, u32 val32) { rtlpriv->io.write32_async(rtlpriv, addr, val32); if (rtlpriv->cfg->write_readback) rtlpriv->io.read32_sync(rtlpriv, addr); } static inline u32 rtl_get_bbreg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = hw->priv; return rtlpriv->cfg->ops->get_bbreg(hw, regaddr, bitmask); } static inline void rtl_set_bbreg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = hw->priv; rtlpriv->cfg->ops->set_bbreg(hw, regaddr, bitmask, data); } static inline u32 rtl_get_rfreg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = hw->priv; return rtlpriv->cfg->ops->get_rfreg(hw, rfpath, regaddr, bitmask); } static inline void rtl_set_rfreg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = hw->priv; rtlpriv->cfg->ops->set_rfreg(hw, rfpath, regaddr, bitmask, data); } static inline bool is_hal_stop(struct rtl_hal *rtlhal) { return (_HAL_STATE_STOP == rtlhal->state); } static inline void set_hal_start(struct rtl_hal *rtlhal) { rtlhal->state = _HAL_STATE_START; } static inline void set_hal_stop(struct rtl_hal *rtlhal) { rtlhal->state = _HAL_STATE_STOP; } static inline u8 get_rf_type(struct rtl_phy *rtlphy) { return rtlphy->rf_type; } static inline struct ieee80211_hdr *rtl_get_hdr(struct sk_buff *skb) { return (struct ieee80211_hdr *)(skb->data); } static inline __le16 rtl_get_fc(struct sk_buff *skb) { return rtl_get_hdr(skb)->frame_control; } static inline u16 rtl_get_tid_h(struct ieee80211_hdr *hdr) { return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; } static inline u16 rtl_get_tid(struct sk_buff *skb) { return rtl_get_tid_h(rtl_get_hdr(skb)); } static inline struct ieee80211_sta *get_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *bssid) { return ieee80211_find_sta(vif, bssid); } static inline struct ieee80211_sta *rtl_find_sta(struct ieee80211_hw *hw, u8 *mac_addr) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); return ieee80211_find_sta(mac->vif, mac_addr); } #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/base.c0000644000175000017500000012446412026211315023232 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "wifi.h" #include "rc.h" #include "base.h" #include "efuse.h" #include "cam.h" #include "ps.h" #include "regd.h" #include #include #include /* *NOTICE!!!: This file will be very big, we should *keep it clear under following roles: * *This file include following parts, so, if you add new *functions into this file, please check which part it *should includes. or check if you should add new part *for this file: * *1) mac80211 init functions *2) tx information functions *3) functions called by core.c *4) wq & timer callback functions *5) frame process functions *6) IOT functions *7) sysfs functions *8) ... */ /********************************************************* * * mac80211 init functions * *********************************************************/ static struct ieee80211_channel rtl_channeltable_2g[] = { {.center_freq = 2412, .hw_value = 1,}, {.center_freq = 2417, .hw_value = 2,}, {.center_freq = 2422, .hw_value = 3,}, {.center_freq = 2427, .hw_value = 4,}, {.center_freq = 2432, .hw_value = 5,}, {.center_freq = 2437, .hw_value = 6,}, {.center_freq = 2442, .hw_value = 7,}, {.center_freq = 2447, .hw_value = 8,}, {.center_freq = 2452, .hw_value = 9,}, {.center_freq = 2457, .hw_value = 10,}, {.center_freq = 2462, .hw_value = 11,}, {.center_freq = 2467, .hw_value = 12,}, {.center_freq = 2472, .hw_value = 13,}, {.center_freq = 2484, .hw_value = 14,}, }; static struct ieee80211_channel rtl_channeltable_5g[] = { {.center_freq = 5180, .hw_value = 36,}, {.center_freq = 5200, .hw_value = 40,}, {.center_freq = 5220, .hw_value = 44,}, {.center_freq = 5240, .hw_value = 48,}, {.center_freq = 5260, .hw_value = 52,}, {.center_freq = 5280, .hw_value = 56,}, {.center_freq = 5300, .hw_value = 60,}, {.center_freq = 5320, .hw_value = 64,}, {.center_freq = 5500, .hw_value = 100,}, {.center_freq = 5520, .hw_value = 104,}, {.center_freq = 5540, .hw_value = 108,}, {.center_freq = 5560, .hw_value = 112,}, {.center_freq = 5580, .hw_value = 116,}, {.center_freq = 5600, .hw_value = 120,}, {.center_freq = 5620, .hw_value = 124,}, {.center_freq = 5640, .hw_value = 128,}, {.center_freq = 5660, .hw_value = 132,}, {.center_freq = 5680, .hw_value = 136,}, {.center_freq = 5700, .hw_value = 140,}, {.center_freq = 5745, .hw_value = 149,}, {.center_freq = 5765, .hw_value = 153,}, {.center_freq = 5785, .hw_value = 157,}, {.center_freq = 5805, .hw_value = 161,}, {.center_freq = 5825, .hw_value = 165,}, }; static struct ieee80211_rate rtl_ratetable_2g[] = { {.bitrate = 10, .hw_value = 0x00,}, {.bitrate = 20, .hw_value = 0x01,}, {.bitrate = 55, .hw_value = 0x02,}, {.bitrate = 110, .hw_value = 0x03,}, {.bitrate = 60, .hw_value = 0x04,}, {.bitrate = 90, .hw_value = 0x05,}, {.bitrate = 120, .hw_value = 0x06,}, {.bitrate = 180, .hw_value = 0x07,}, {.bitrate = 240, .hw_value = 0x08,}, {.bitrate = 360, .hw_value = 0x09,}, {.bitrate = 480, .hw_value = 0x0a,}, {.bitrate = 540, .hw_value = 0x0b,}, }; static struct ieee80211_rate rtl_ratetable_5g[] = { {.bitrate = 60, .hw_value = 0x04,}, {.bitrate = 90, .hw_value = 0x05,}, {.bitrate = 120, .hw_value = 0x06,}, {.bitrate = 180, .hw_value = 0x07,}, {.bitrate = 240, .hw_value = 0x08,}, {.bitrate = 360, .hw_value = 0x09,}, {.bitrate = 480, .hw_value = 0x0a,}, {.bitrate = 540, .hw_value = 0x0b,}, }; static const struct ieee80211_supported_band rtl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, .channels = rtl_channeltable_2g, .n_channels = ARRAY_SIZE(rtl_channeltable_2g), .bitrates = rtl_ratetable_2g, .n_bitrates = ARRAY_SIZE(rtl_ratetable_2g), .ht_cap = {0}, }; static struct ieee80211_supported_band rtl_band_5ghz = { .band = IEEE80211_BAND_5GHZ, .channels = rtl_channeltable_5g, .n_channels = ARRAY_SIZE(rtl_channeltable_5g), .bitrates = rtl_ratetable_5g, .n_bitrates = ARRAY_SIZE(rtl_ratetable_5g), .ht_cap = {0}, }; static const u8 tid_to_ac[] = { 2, /* IEEE80211_AC_BE */ 3, /* IEEE80211_AC_BK */ 3, /* IEEE80211_AC_BK */ 2, /* IEEE80211_AC_BE */ 1, /* IEEE80211_AC_VI */ 1, /* IEEE80211_AC_VI */ 0, /* IEEE80211_AC_VO */ 0, /* IEEE80211_AC_VO */ }; u8 rtl_tid_to_ac(u8 tid) { return tid_to_ac[tid]; } static void _rtl_init_hw_ht_capab(struct ieee80211_hw *hw, struct ieee80211_sta_ht_cap *ht_cap) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); ht_cap->ht_supported = true; ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_MAX_AMSDU; if (rtlpriv->rtlhal.disable_amsdu_8k) ht_cap->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; /* *Maximum length of AMPDU that the STA can receive. *Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets) */ ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; /*Minimum MPDU start spacing , */ ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; /* *hw->wiphy->bands[IEEE80211_BAND_2GHZ] *base on ant_num *rx_mask: RX mask *if rx_ant =1 rx_mask[0]=0xff;==>MCS0-MCS7 *if rx_ant =2 rx_mask[1]=0xff;==>MCS8-MCS15 *if rx_ant >=3 rx_mask[2]=0xff; *if BW_40 rx_mask[4]=0x01; *highest supported RX rate */ if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_2T2R) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "1T2R or 2T2R\n"); ht_cap->mcs.rx_mask[0] = 0xFF; ht_cap->mcs.rx_mask[1] = 0xFF; ht_cap->mcs.rx_mask[4] = 0x01; ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS15); } else if (get_rf_type(rtlphy) == RF_1T1R) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "1T1R\n"); ht_cap->mcs.rx_mask[0] = 0xFF; ht_cap->mcs.rx_mask[1] = 0x00; ht_cap->mcs.rx_mask[4] = 0x01; ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS7); } } static void _rtl_init_mac80211(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct ieee80211_supported_band *sband; if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY && rtlhal->bandset == BAND_ON_BOTH) { /* 1: 2.4 G bands */ /* <1> use mac->bands as mem for hw->wiphy->bands */ sband = &(rtlmac->bands[IEEE80211_BAND_2GHZ]); /* <2> set hw->wiphy->bands[IEEE80211_BAND_2GHZ] * to default value(1T1R) */ memcpy(&(rtlmac->bands[IEEE80211_BAND_2GHZ]), &rtl_band_2ghz, sizeof(struct ieee80211_supported_band)); /* <3> init ht cap base on ant_num */ _rtl_init_hw_ht_capab(hw, &sband->ht_cap); /* <4> set mac->sband to wiphy->sband */ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; /* 2: 5 G bands */ /* <1> use mac->bands as mem for hw->wiphy->bands */ sband = &(rtlmac->bands[IEEE80211_BAND_5GHZ]); /* <2> set hw->wiphy->bands[IEEE80211_BAND_5GHZ] * to default value(1T1R) */ memcpy(&(rtlmac->bands[IEEE80211_BAND_5GHZ]), &rtl_band_5ghz, sizeof(struct ieee80211_supported_band)); /* <3> init ht cap base on ant_num */ _rtl_init_hw_ht_capab(hw, &sband->ht_cap); /* <4> set mac->sband to wiphy->sband */ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; } else { if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* <1> use mac->bands as mem for hw->wiphy->bands */ sband = &(rtlmac->bands[IEEE80211_BAND_2GHZ]); /* <2> set hw->wiphy->bands[IEEE80211_BAND_2GHZ] * to default value(1T1R) */ memcpy(&(rtlmac->bands[IEEE80211_BAND_2GHZ]), &rtl_band_2ghz, sizeof(struct ieee80211_supported_band)); /* <3> init ht cap base on ant_num */ _rtl_init_hw_ht_capab(hw, &sband->ht_cap); /* <4> set mac->sband to wiphy->sband */ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; } else if (rtlhal->current_bandtype == BAND_ON_5G) { /* <1> use mac->bands as mem for hw->wiphy->bands */ sband = &(rtlmac->bands[IEEE80211_BAND_5GHZ]); /* <2> set hw->wiphy->bands[IEEE80211_BAND_5GHZ] * to default value(1T1R) */ memcpy(&(rtlmac->bands[IEEE80211_BAND_5GHZ]), &rtl_band_5ghz, sizeof(struct ieee80211_supported_band)); /* <3> init ht cap base on ant_num */ _rtl_init_hw_ht_capab(hw, &sband->ht_cap); /* <4> set mac->sband to wiphy->sband */ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Err BAND %d\n", rtlhal->current_bandtype); } } /* <5> set hw caps */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_CONNECTION_MONITOR | /* IEEE80211_HW_SUPPORTS_CQM_RSSI | */ IEEE80211_HW_REPORTS_TX_ACK_STATUS | 0; /* swlps or hwlps has been set in diff chip in init_sw_vars */ if (rtlpriv->psc.swctrl_lps) hw->flags |= IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | /* IEEE80211_HW_SUPPORTS_DYNAMIC_PS | */ 0; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->rts_threshold = 2347; hw->queues = AC_MAX; hw->extra_tx_headroom = RTL_TX_HEADER_SIZE; /* TODO: Correct this value for our hw */ /* TODO: define these hard code value */ hw->channel_change_time = 100; hw->max_listen_interval = 10; hw->max_rate_tries = 4; /* hw->max_rates = 1; */ hw->sta_data_size = sizeof(struct rtl_sta_info); /* <6> mac address */ if (is_valid_ether_addr(rtlefuse->dev_addr)) { SET_IEEE80211_PERM_ADDR(hw, rtlefuse->dev_addr); } else { u8 rtlmac1[] = { 0x00, 0xe0, 0x4c, 0x81, 0x92, 0x00 }; get_random_bytes((rtlmac1 + (ETH_ALEN - 1)), 1); SET_IEEE80211_PERM_ADDR(hw, rtlmac1); } } static void _rtl_init_deferred_work(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /* <1> timer */ init_timer(&rtlpriv->works.watchdog_timer); setup_timer(&rtlpriv->works.watchdog_timer, rtl_watch_dog_timer_callback, (unsigned long)hw); /* <2> work queue */ rtlpriv->works.hw = hw; rtlpriv->works.rtl_wq = alloc_workqueue(rtlpriv->cfg->name, 0, 0); INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq, (void *)rtl_watchdog_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq, (void *)rtl_ips_nic_off_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ps_work, (void *)rtl_swlps_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ps_rfon_wq, (void *)rtl_swlps_rfon_wq_callback); } void rtl_deinit_deferred_work(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); del_timer_sync(&rtlpriv->works.watchdog_timer); cancel_delayed_work(&rtlpriv->works.watchdog_wq); cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); cancel_delayed_work(&rtlpriv->works.ps_work); cancel_delayed_work(&rtlpriv->works.ps_rfon_wq); } void rtl_init_rfkill(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); bool radio_state; bool blocked; u8 valid = 0; /*set init state to on */ rtlpriv->rfkill.rfkill_state = true; wiphy_rfkill_set_hw_state(hw->wiphy, 0); radio_state = rtlpriv->cfg->ops->radio_onoff_checking(hw, &valid); if (valid) { pr_info("wireless switch is %s\n", rtlpriv->rfkill.rfkill_state ? "on" : "off"); rtlpriv->rfkill.rfkill_state = radio_state; blocked = (rtlpriv->rfkill.rfkill_state == 1) ? 0 : 1; wiphy_rfkill_set_hw_state(hw->wiphy, blocked); } wiphy_rfkill_start_polling(hw->wiphy); } EXPORT_SYMBOL(rtl_init_rfkill); void rtl_deinit_rfkill(struct ieee80211_hw *hw) { wiphy_rfkill_stop_polling(hw->wiphy); } int rtl_init_core(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); /* <1> init mac80211 */ _rtl_init_mac80211(hw); rtlmac->hw = hw; /* <2> rate control register */ hw->rate_control_algorithm = "rtl_rc"; /* * <3> init CRDA must come after init * mac80211 hw in _rtl_init_mac80211. */ if (rtl_regd_init(hw, rtl_reg_notifier)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "REGD init failed\n"); return 1; } else { /* CRDA regd hint must after init CRDA */ if (regulatory_hint(hw->wiphy, rtlpriv->regd.alpha2)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "regulatory_hint fail\n"); } } /* <4> locks */ mutex_init(&rtlpriv->locks.conf_mutex); mutex_init(&rtlpriv->locks.ps_mutex); spin_lock_init(&rtlpriv->locks.ips_lock); spin_lock_init(&rtlpriv->locks.irq_th_lock); spin_lock_init(&rtlpriv->locks.h2c_lock); spin_lock_init(&rtlpriv->locks.rf_ps_lock); spin_lock_init(&rtlpriv->locks.rf_lock); spin_lock_init(&rtlpriv->locks.waitq_lock); spin_lock_init(&rtlpriv->locks.cck_and_rw_pagea_lock); rtlmac->link_state = MAC80211_NOLINK; /* <5> init deferred work */ _rtl_init_deferred_work(hw); return 0; } void rtl_deinit_core(struct ieee80211_hw *hw) { } void rtl_init_rx_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *) (&mac->rx_conf)); } /********************************************************* * * tx information functions * *********************************************************/ static void _rtl_qurey_shortpreamble_mode(struct ieee80211_hw *hw, struct rtl_tcb_desc *tcb_desc, struct ieee80211_tx_info *info) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 rate_flag = info->control.rates[0].flags; tcb_desc->use_shortpreamble = false; /* 1M can only use Long Preamble. 11B spec */ if (tcb_desc->hw_rate == rtlpriv->cfg->maps[RTL_RC_CCK_RATE1M]) return; else if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) tcb_desc->use_shortpreamble = true; return; } static void _rtl_query_shortgi(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct rtl_tcb_desc *tcb_desc, struct ieee80211_tx_info *info) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u8 rate_flag = info->control.rates[0].flags; u8 sgi_40 = 0, sgi_20 = 0, bw_40 = 0; tcb_desc->use_shortgi = false; if (sta == NULL) return; sgi_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40; sgi_20 = sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20; if (!(sta->ht_cap.ht_supported)) return; if (!sgi_40 && !sgi_20) return; if (mac->opmode == NL80211_IFTYPE_STATION) bw_40 = mac->bw_40; else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; if (bw_40 && sgi_40) tcb_desc->use_shortgi = true; else if ((bw_40 == false) && sgi_20) tcb_desc->use_shortgi = true; if (!(rate_flag & IEEE80211_TX_RC_SHORT_GI)) tcb_desc->use_shortgi = false; } static void _rtl_query_protection_mode(struct ieee80211_hw *hw, struct rtl_tcb_desc *tcb_desc, struct ieee80211_tx_info *info) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 rate_flag = info->control.rates[0].flags; /* Common Settings */ tcb_desc->rts_stbc = false; tcb_desc->cts_enable = false; tcb_desc->rts_sc = 0; tcb_desc->rts_bw = false; tcb_desc->rts_use_shortpreamble = false; tcb_desc->rts_use_shortgi = false; if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) { /* Use CTS-to-SELF in protection mode. */ tcb_desc->rts_enable = true; tcb_desc->cts_enable = true; tcb_desc->rts_rate = rtlpriv->cfg->maps[RTL_RC_OFDM_RATE24M]; } else if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { /* Use RTS-CTS in protection mode. */ tcb_desc->rts_enable = true; tcb_desc->rts_rate = rtlpriv->cfg->maps[RTL_RC_OFDM_RATE24M]; } } static void _rtl_txrate_selectmode(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct rtl_tcb_desc *tcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_sta_info *sta_entry = NULL; u8 ratr_index = 7; if (sta) { sta_entry = (struct rtl_sta_info *) sta->drv_priv; ratr_index = sta_entry->ratr_index; } if (!tcb_desc->disable_ratefallback || !tcb_desc->use_driver_rate) { if (mac->opmode == NL80211_IFTYPE_STATION) { tcb_desc->ratr_index = 0; } else if (mac->opmode == NL80211_IFTYPE_ADHOC) { if (tcb_desc->multicast || tcb_desc->broadcast) { tcb_desc->hw_rate = rtlpriv->cfg->maps[RTL_RC_CCK_RATE2M]; tcb_desc->use_driver_rate = 1; } else { /* TODO */ } tcb_desc->ratr_index = ratr_index; } else if (mac->opmode == NL80211_IFTYPE_AP) { tcb_desc->ratr_index = ratr_index; } } if (rtlpriv->dm.useramask) { /* TODO we will differentiate adhoc and station futrue */ if (mac->opmode == NL80211_IFTYPE_STATION) { tcb_desc->mac_id = 0; if (mac->mode == WIRELESS_MODE_N_24G) tcb_desc->ratr_index = RATR_INX_WIRELESS_NGB; else if (mac->mode == WIRELESS_MODE_N_5G) tcb_desc->ratr_index = RATR_INX_WIRELESS_NG; else if (mac->mode & WIRELESS_MODE_G) tcb_desc->ratr_index = RATR_INX_WIRELESS_GB; else if (mac->mode & WIRELESS_MODE_B) tcb_desc->ratr_index = RATR_INX_WIRELESS_B; else if (mac->mode & WIRELESS_MODE_A) tcb_desc->ratr_index = RATR_INX_WIRELESS_G; } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (NULL != sta) { if (sta->aid > 0) tcb_desc->mac_id = sta->aid + 1; else tcb_desc->mac_id = 1; } else { tcb_desc->mac_id = 0; } } } } static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct rtl_tcb_desc *tcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); tcb_desc->packet_bw = false; if (!sta) return; if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (!(sta->ht_cap.ht_supported) || !(sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) return; } else if (mac->opmode == NL80211_IFTYPE_STATION) { if (!mac->bw_40 || !(sta->ht_cap.ht_supported)) return; } if (tcb_desc->multicast || tcb_desc->broadcast) return; /*use legency rate, shall use 20MHz */ if (tcb_desc->hw_rate <= rtlpriv->cfg->maps[RTL_RC_OFDM_RATE54M]) return; tcb_desc->packet_bw = true; } static u8 _rtl_get_highest_n_rate(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 hw_rate; if (get_rf_type(rtlphy) == RF_2T2R) hw_rate = rtlpriv->cfg->maps[RTL_RC_HT_RATEMCS15]; else hw_rate = rtlpriv->cfg->maps[RTL_RC_HT_RATEMCS7]; return hw_rate; } /* mac80211's rate_idx is like this: * * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ * * B/G rate: * (rx_status->flag & RX_FLAG_HT) = 0, * DESC92_RATE1M-->DESC92_RATE54M ==> idx is 0-->11, * * N rate: * (rx_status->flag & RX_FLAG_HT) = 1, * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15 * * 5G band:rx_status->band == IEEE80211_BAND_5GHZ * A rate: * (rx_status->flag & RX_FLAG_HT) = 0, * DESC92_RATE6M-->DESC92_RATE54M ==> idx is 0-->7, * * N rate: * (rx_status->flag & RX_FLAG_HT) = 1, * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15 */ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, u8 desc_rate, bool first_ampdu) { int rate_idx; if (false == isht) { if (IEEE80211_BAND_2GHZ == hw->conf.channel->band) { switch (desc_rate) { case DESC92_RATE1M: rate_idx = 0; break; case DESC92_RATE2M: rate_idx = 1; break; case DESC92_RATE5_5M: rate_idx = 2; break; case DESC92_RATE11M: rate_idx = 3; break; case DESC92_RATE6M: rate_idx = 4; break; case DESC92_RATE9M: rate_idx = 5; break; case DESC92_RATE12M: rate_idx = 6; break; case DESC92_RATE18M: rate_idx = 7; break; case DESC92_RATE24M: rate_idx = 8; break; case DESC92_RATE36M: rate_idx = 9; break; case DESC92_RATE48M: rate_idx = 10; break; case DESC92_RATE54M: rate_idx = 11; break; default: rate_idx = 0; break; } } else { switch (desc_rate) { case DESC92_RATE6M: rate_idx = 0; break; case DESC92_RATE9M: rate_idx = 1; break; case DESC92_RATE12M: rate_idx = 2; break; case DESC92_RATE18M: rate_idx = 3; break; case DESC92_RATE24M: rate_idx = 4; break; case DESC92_RATE36M: rate_idx = 5; break; case DESC92_RATE48M: rate_idx = 6; break; case DESC92_RATE54M: rate_idx = 7; break; default: rate_idx = 0; break; } } } else { switch (desc_rate) { case DESC92_RATEMCS0: rate_idx = 0; break; case DESC92_RATEMCS1: rate_idx = 1; break; case DESC92_RATEMCS2: rate_idx = 2; break; case DESC92_RATEMCS3: rate_idx = 3; break; case DESC92_RATEMCS4: rate_idx = 4; break; case DESC92_RATEMCS5: rate_idx = 5; break; case DESC92_RATEMCS6: rate_idx = 6; break; case DESC92_RATEMCS7: rate_idx = 7; break; case DESC92_RATEMCS8: rate_idx = 8; break; case DESC92_RATEMCS9: rate_idx = 9; break; case DESC92_RATEMCS10: rate_idx = 10; break; case DESC92_RATEMCS11: rate_idx = 11; break; case DESC92_RATEMCS12: rate_idx = 12; break; case DESC92_RATEMCS13: rate_idx = 13; break; case DESC92_RATEMCS14: rate_idx = 14; break; case DESC92_RATEMCS15: rate_idx = 15; break; default: rate_idx = 0; break; } } return rate_idx; } EXPORT_SYMBOL(rtlwifi_rate_mapping); void rtl_get_tcb_desc(struct ieee80211_hw *hw, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, struct rtl_tcb_desc *tcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); struct ieee80211_rate *txrate; __le16 fc = hdr->frame_control; txrate = ieee80211_get_tx_rate(hw, info); if (txrate) tcb_desc->hw_rate = txrate->hw_value; else tcb_desc->hw_rate = 0; if (ieee80211_is_data(fc)) { /* *we set data rate INX 0 *in rtl_rc.c if skb is special data or *mgt which need low data rate. */ /* *So tcb_desc->hw_rate is just used for *special data and mgt frames */ if (info->control.rates[0].idx == 0 || ieee80211_is_nullfunc(fc)) { tcb_desc->use_driver_rate = true; tcb_desc->ratr_index = RATR_INX_WIRELESS_MC; tcb_desc->disable_ratefallback = 1; } else { /* *because hw will nerver use hw_rate *when tcb_desc->use_driver_rate = false *so we never set highest N rate here, *and N rate will all be controlled by FW *when tcb_desc->use_driver_rate = false */ if (sta && (sta->ht_cap.ht_supported)) { tcb_desc->hw_rate = _rtl_get_highest_n_rate(hw); } else { if (rtlmac->mode == WIRELESS_MODE_B) { tcb_desc->hw_rate = rtlpriv->cfg->maps[RTL_RC_CCK_RATE11M]; } else { tcb_desc->hw_rate = rtlpriv->cfg->maps[RTL_RC_OFDM_RATE54M]; } } } if (is_multicast_ether_addr(ieee80211_get_DA(hdr))) tcb_desc->multicast = 1; else if (is_broadcast_ether_addr(ieee80211_get_DA(hdr))) tcb_desc->broadcast = 1; _rtl_txrate_selectmode(hw, sta, tcb_desc); _rtl_query_bandwidth_mode(hw, sta, tcb_desc); _rtl_qurey_shortpreamble_mode(hw, tcb_desc, info); _rtl_query_shortgi(hw, sta, tcb_desc, info); _rtl_query_protection_mode(hw, tcb_desc, info); } else { tcb_desc->use_driver_rate = true; tcb_desc->ratr_index = RATR_INX_WIRELESS_MC; tcb_desc->disable_ratefallback = 1; tcb_desc->mac_id = 0; tcb_desc->packet_bw = false; } } EXPORT_SYMBOL(rtl_get_tcb_desc); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); struct rtl_priv *rtlpriv = rtl_priv(hw); __le16 fc = hdr->frame_control; u8 *act = (u8 *)skb->data + MAC80211_3ADDR_LEN; u8 category; if (!ieee80211_is_action(fc)) return true; category = *act; act++; switch (category) { case ACT_CAT_BA: switch (*act) { case ACT_ADDBAREQ: if (mac->act_scanning) return false; RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, "%s ACT_ADDBAREQ From :%pM\n", is_tx ? "Tx" : "Rx", hdr->addr2); break; case ACT_ADDBARSP: RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, "%s ACT_ADDBARSP From :%pM\n", is_tx ? "Tx" : "Rx", hdr->addr2); break; case ACT_DELBA: RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, "ACT_ADDBADEL From :%pM\n", hdr->addr2); break; } break; default: break; } return true; } /*should call before software enc*/ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); __le16 fc = rtl_get_fc(skb); u16 ether_type; u8 mac_hdr_len = ieee80211_get_hdrlen_from_skb(skb); const struct iphdr *ip; if (!ieee80211_is_data(fc)) return false; ip = (struct iphdr *)((u8 *) skb->data + mac_hdr_len + SNAP_SIZE + PROTOC_TYPE_SIZE); ether_type = *(u16 *) ((u8 *) skb->data + mac_hdr_len + SNAP_SIZE); /* ether_type = ntohs(ether_type); */ if (ETH_P_IP == ether_type) { if (IPPROTO_UDP == ip->protocol) { struct udphdr *udp = (struct udphdr *)((u8 *) ip + (ip->ihl << 2)); if (((((u8 *) udp)[1] == 68) && (((u8 *) udp)[3] == 67)) || ((((u8 *) udp)[1] == 67) && (((u8 *) udp)[3] == 68))) { /* * 68 : UDP BOOTP client * 67 : UDP BOOTP server */ RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, "dhcp %s !!\n", is_tx ? "Tx" : "Rx"); if (is_tx) { rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } return true; } } } else if (ETH_P_ARP == ether_type) { if (is_tx) { rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } return true; } else if (ETH_P_PAE == ether_type) { RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, "802.1X %s EAPOL pkt!!\n", is_tx ? "Tx" : "Rx"); if (is_tx) { rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } return true; } else if (ETH_P_IPV6 == ether_type) { /* IPv6 */ return true; } return false; } /********************************************************* * * functions called by core.c * *********************************************************/ int rtl_tx_agg_start(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_tid_data *tid_data; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_sta_info *sta_entry = NULL; if (sta == NULL) return -EINVAL; if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; sta_entry = (struct rtl_sta_info *)sta->drv_priv; if (!sta_entry) return -ENXIO; tid_data = &sta_entry->tids[tid]; RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "on ra = %pM tid = %d seq:%d\n", sta->addr, tid, tid_data->seq_number); *ssn = tid_data->seq_number; tid_data->agg.agg_state = RTL_AGG_START; ieee80211_start_tx_ba_cb_irqsafe(mac->vif, sta->addr, tid); return 0; } int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_sta_info *sta_entry = NULL; if (sta == NULL) return -EINVAL; if (!sta->addr) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ra = NULL\n"); return -EINVAL; } RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "on ra = %pM tid = %d\n", sta->addr, tid); if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; sta_entry = (struct rtl_sta_info *)sta->drv_priv; sta_entry->tids[tid].agg.agg_state = RTL_AGG_STOP; ieee80211_stop_tx_ba_cb_irqsafe(mac->vif, sta->addr, tid); return 0; } int rtl_tx_agg_oper(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_sta_info *sta_entry = NULL; if (sta == NULL) return -EINVAL; if (!sta->addr) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ra = NULL\n"); return -EINVAL; } RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "on ra = %pM tid = %d\n", sta->addr, tid); if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; sta_entry = (struct rtl_sta_info *)sta->drv_priv; sta_entry->tids[tid].agg.agg_state = RTL_AGG_OPERATIONAL; return 0; } /********************************************************* * * wq & timer callback functions * *********************************************************/ void rtl_watchdog_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, struct rtl_works, watchdog_wq); struct ieee80211_hw *hw = rtlworks->hw; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); bool busytraffic = false; bool higher_busytraffic = false; bool higher_busyrxtraffic = false; u8 idx, tid; u32 rx_cnt_inp4eriod = 0; u32 tx_cnt_inp4eriod = 0; u32 aver_rx_cnt_inperiod = 0; u32 aver_tx_cnt_inperiod = 0; u32 aver_tidtx_inperiod[MAX_TID_COUNT] = {0}; u32 tidtx_inp4eriod[MAX_TID_COUNT] = {0}; bool enter_ps = false; if (is_hal_stop(rtlhal)) return; /* <1> Determine if action frame is allowed */ if (mac->link_state > MAC80211_NOLINK) { if (mac->cnt_after_linked < 20) mac->cnt_after_linked++; } else { mac->cnt_after_linked = 0; } /* *<2> to check if traffic busy, if * busytraffic we don't change channel */ if (mac->link_state >= MAC80211_LINKED) { /* (1) get aver_rx_cnt_inperiod & aver_tx_cnt_inperiod */ for (idx = 0; idx <= 2; idx++) { rtlpriv->link_info.num_rx_in4period[idx] = rtlpriv->link_info.num_rx_in4period[idx + 1]; rtlpriv->link_info.num_tx_in4period[idx] = rtlpriv->link_info.num_tx_in4period[idx + 1]; } rtlpriv->link_info.num_rx_in4period[3] = rtlpriv->link_info.num_rx_inperiod; rtlpriv->link_info.num_tx_in4period[3] = rtlpriv->link_info.num_tx_inperiod; for (idx = 0; idx <= 3; idx++) { rx_cnt_inp4eriod += rtlpriv->link_info.num_rx_in4period[idx]; tx_cnt_inp4eriod += rtlpriv->link_info.num_tx_in4period[idx]; } aver_rx_cnt_inperiod = rx_cnt_inp4eriod / 4; aver_tx_cnt_inperiod = tx_cnt_inp4eriod / 4; /* (2) check traffic busy */ if (aver_rx_cnt_inperiod > 100 || aver_tx_cnt_inperiod > 100) busytraffic = true; /* Higher Tx/Rx data. */ if (aver_rx_cnt_inperiod > 4000 || aver_tx_cnt_inperiod > 4000) { higher_busytraffic = true; /* Extremely high Rx data. */ if (aver_rx_cnt_inperiod > 5000) higher_busyrxtraffic = true; } /* check every tid's tx traffic */ for (tid = 0; tid <= 7; tid++) { for (idx = 0; idx <= 2; idx++) rtlpriv->link_info.tidtx_in4period[tid][idx] = rtlpriv->link_info.tidtx_in4period[tid] [idx + 1]; rtlpriv->link_info.tidtx_in4period[tid][3] = rtlpriv->link_info.tidtx_inperiod[tid]; for (idx = 0; idx <= 3; idx++) tidtx_inp4eriod[tid] += rtlpriv->link_info.tidtx_in4period[tid][idx]; aver_tidtx_inperiod[tid] = tidtx_inp4eriod[tid] / 4; if (aver_tidtx_inperiod[tid] > 5000) rtlpriv->link_info.higher_busytxtraffic[tid] = true; else rtlpriv->link_info.higher_busytxtraffic[tid] = false; } if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || (rtlpriv->link_info.num_rx_inperiod > 2)) enter_ps = false; else enter_ps = true; /* LeisurePS only work in infra mode. */ if (enter_ps) rtl_lps_enter(hw); else rtl_lps_leave(hw); } rtlpriv->link_info.num_rx_inperiod = 0; rtlpriv->link_info.num_tx_inperiod = 0; for (tid = 0; tid <= 7; tid++) rtlpriv->link_info.tidtx_inperiod[tid] = 0; rtlpriv->link_info.busytraffic = busytraffic; rtlpriv->link_info.higher_busytraffic = higher_busytraffic; rtlpriv->link_info.higher_busyrxtraffic = higher_busyrxtraffic; /* <3> DM */ rtlpriv->cfg->ops->dm_watchdog(hw); } void rtl_watch_dog_timer_callback(unsigned long data) { struct ieee80211_hw *hw = (struct ieee80211_hw *)data; struct rtl_priv *rtlpriv = rtl_priv(hw); queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.watchdog_wq, 0); mod_timer(&rtlpriv->works.watchdog_timer, jiffies + MSECS(RTL_WATCH_DOG_TIME)); } /********************************************************* * * frame process functions * *********************************************************/ u8 *rtl_find_ie(u8 *data, unsigned int len, u8 ie) { struct ieee80211_mgmt *mgmt = (void *)data; u8 *pos, *end; pos = (u8 *)mgmt->u.beacon.variable; end = data + len; while (pos < end) { if (pos + 2 + pos[1] > end) return NULL; if (pos[0] == ie) return pos; pos += 2 + pos[1]; } return NULL; } /* when we use 2 rx ants we send IEEE80211_SMPS_OFF */ /* when we use 1 rx ant we send IEEE80211_SMPS_STATIC */ static struct sk_buff *rtl_make_smps_action(struct ieee80211_hw *hw, enum ieee80211_smps_mode smps, u8 *da, u8 *bssid) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct sk_buff *skb; struct ieee80211_mgmt *action_frame; /* 27 = header + category + action + smps mode */ skb = dev_alloc_skb(27 + hw->extra_tx_headroom); if (!skb) return NULL; skb_reserve(skb, hw->extra_tx_headroom); action_frame = (void *)skb_put(skb, 27); memset(action_frame, 0, 27); memcpy(action_frame->da, da, ETH_ALEN); memcpy(action_frame->sa, rtlefuse->dev_addr, ETH_ALEN); memcpy(action_frame->bssid, bssid, ETH_ALEN); action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); action_frame->u.action.category = WLAN_CATEGORY_HT; action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS; switch (smps) { case IEEE80211_SMPS_AUTOMATIC:/* 0 */ case IEEE80211_SMPS_NUM_MODES:/* 4 */ WARN_ON(1); case IEEE80211_SMPS_OFF:/* 1 */ /*MIMO_PS_NOLIMIT*/ action_frame->u.action.u.ht_smps.smps_control = WLAN_HT_SMPS_CONTROL_DISABLED;/* 0 */ break; case IEEE80211_SMPS_STATIC:/* 2 */ /*MIMO_PS_STATIC*/ action_frame->u.action.u.ht_smps.smps_control = WLAN_HT_SMPS_CONTROL_STATIC;/* 1 */ break; case IEEE80211_SMPS_DYNAMIC:/* 3 */ /*MIMO_PS_DYNAMIC*/ action_frame->u.action.u.ht_smps.smps_control = WLAN_HT_SMPS_CONTROL_DYNAMIC;/* 3 */ break; } return skb; } int rtl_send_smps_action(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 *da, u8 *bssid, enum ieee80211_smps_mode smps) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct sk_buff *skb = rtl_make_smps_action(hw, smps, da, bssid); struct rtl_tcb_desc tcb_desc; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); if (rtlpriv->mac80211.act_scanning) goto err_free; if (!sta) goto err_free; if (unlikely(is_hal_stop(rtlhal) || ppsc->rfpwr_state != ERFON)) goto err_free; if (!test_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status)) goto err_free; /* this is a type = mgmt * stype = action frame */ if (skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rtl_sta_info *sta_entry = (struct rtl_sta_info *) sta->drv_priv; sta_entry->mimo_ps = smps; rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0); info->control.rates[0].idx = 0; info->band = hw->conf.channel->band; rtlpriv->intf_ops->adapter_tx(hw, sta, skb, &tcb_desc); } err_free: return 0; } /********************************************************* * * IOT functions * *********************************************************/ static bool rtl_chk_vendor_ouisub(struct ieee80211_hw *hw, struct octet_string vendor_ie) { struct rtl_priv *rtlpriv = rtl_priv(hw); bool matched = false; static u8 athcap_1[] = { 0x00, 0x03, 0x7F }; static u8 athcap_2[] = { 0x00, 0x13, 0x74 }; static u8 broadcap_1[] = { 0x00, 0x10, 0x18 }; static u8 broadcap_2[] = { 0x00, 0x0a, 0xf7 }; static u8 broadcap_3[] = { 0x00, 0x05, 0xb5 }; static u8 racap[] = { 0x00, 0x0c, 0x43 }; static u8 ciscocap[] = { 0x00, 0x40, 0x96 }; static u8 marvcap[] = { 0x00, 0x50, 0x43 }; if (memcmp(vendor_ie.octet, athcap_1, 3) == 0 || memcmp(vendor_ie.octet, athcap_2, 3) == 0) { rtlpriv->mac80211.vendor = PEER_ATH; matched = true; } else if (memcmp(vendor_ie.octet, broadcap_1, 3) == 0 || memcmp(vendor_ie.octet, broadcap_2, 3) == 0 || memcmp(vendor_ie.octet, broadcap_3, 3) == 0) { rtlpriv->mac80211.vendor = PEER_BROAD; matched = true; } else if (memcmp(vendor_ie.octet, racap, 3) == 0) { rtlpriv->mac80211.vendor = PEER_RAL; matched = true; } else if (memcmp(vendor_ie.octet, ciscocap, 3) == 0) { rtlpriv->mac80211.vendor = PEER_CISCO; matched = true; } else if (memcmp(vendor_ie.octet, marvcap, 3) == 0) { rtlpriv->mac80211.vendor = PEER_MARV; matched = true; } return matched; } static bool rtl_find_221_ie(struct ieee80211_hw *hw, u8 *data, unsigned int len) { struct ieee80211_mgmt *mgmt = (void *)data; struct octet_string vendor_ie; u8 *pos, *end; pos = (u8 *)mgmt->u.beacon.variable; end = data + len; while (pos < end) { if (pos[0] == 221) { vendor_ie.length = pos[1]; vendor_ie.octet = &pos[2]; if (rtl_chk_vendor_ouisub(hw, vendor_ie)) return true; } if (pos + 2 + pos[1] > end) return false; pos += 2 + pos[1]; } return false; } void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = (void *)data; u32 vendor = PEER_UNKNOWN; static u8 ap3_1[3] = { 0x00, 0x14, 0xbf }; static u8 ap3_2[3] = { 0x00, 0x1a, 0x70 }; static u8 ap3_3[3] = { 0x00, 0x1d, 0x7e }; static u8 ap4_1[3] = { 0x00, 0x90, 0xcc }; static u8 ap4_2[3] = { 0x00, 0x0e, 0x2e }; static u8 ap4_3[3] = { 0x00, 0x18, 0x02 }; static u8 ap4_4[3] = { 0x00, 0x17, 0x3f }; static u8 ap4_5[3] = { 0x00, 0x1c, 0xdf }; static u8 ap5_1[3] = { 0x00, 0x1c, 0xf0 }; static u8 ap5_2[3] = { 0x00, 0x21, 0x91 }; static u8 ap5_3[3] = { 0x00, 0x24, 0x01 }; static u8 ap5_4[3] = { 0x00, 0x15, 0xe9 }; static u8 ap5_5[3] = { 0x00, 0x17, 0x9A }; static u8 ap5_6[3] = { 0x00, 0x18, 0xE7 }; static u8 ap6_1[3] = { 0x00, 0x17, 0x94 }; static u8 ap7_1[3] = { 0x00, 0x14, 0xa4 }; if (mac->opmode != NL80211_IFTYPE_STATION) return; if (mac->link_state == MAC80211_NOLINK) { mac->vendor = PEER_UNKNOWN; return; } if (mac->cnt_after_linked > 2) return; /* check if this really is a beacon */ if (!ieee80211_is_beacon(hdr->frame_control)) return; /* min. beacon length + FCS_LEN */ if (len <= 40 + FCS_LEN) return; /* and only beacons from the associated BSSID, please */ if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) return; if (rtl_find_221_ie(hw, data, len)) vendor = mac->vendor; if ((memcmp(mac->bssid, ap5_1, 3) == 0) || (memcmp(mac->bssid, ap5_2, 3) == 0) || (memcmp(mac->bssid, ap5_3, 3) == 0) || (memcmp(mac->bssid, ap5_4, 3) == 0) || (memcmp(mac->bssid, ap5_5, 3) == 0) || (memcmp(mac->bssid, ap5_6, 3) == 0) || vendor == PEER_ATH) { vendor = PEER_ATH; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>ath find\n"); } else if ((memcmp(mac->bssid, ap4_4, 3) == 0) || (memcmp(mac->bssid, ap4_5, 3) == 0) || (memcmp(mac->bssid, ap4_1, 3) == 0) || (memcmp(mac->bssid, ap4_2, 3) == 0) || (memcmp(mac->bssid, ap4_3, 3) == 0) || vendor == PEER_RAL) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>ral find\n"); vendor = PEER_RAL; } else if (memcmp(mac->bssid, ap6_1, 3) == 0 || vendor == PEER_CISCO) { vendor = PEER_CISCO; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>cisco find\n"); } else if ((memcmp(mac->bssid, ap3_1, 3) == 0) || (memcmp(mac->bssid, ap3_2, 3) == 0) || (memcmp(mac->bssid, ap3_3, 3) == 0) || vendor == PEER_BROAD) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>broad find\n"); vendor = PEER_BROAD; } else if (memcmp(mac->bssid, ap7_1, 3) == 0 || vendor == PEER_MARV) { vendor = PEER_MARV; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "=>marv find\n"); } mac->vendor = vendor; } /********************************************************* * * sysfs functions * *********************************************************/ static ssize_t rtl_show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct ieee80211_hw *hw = dev_get_drvdata(d); struct rtl_priv *rtlpriv = rtl_priv(hw); return sprintf(buf, "0x%08X\n", rtlpriv->dbg.global_debuglevel); } static ssize_t rtl_store_debug_level(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct ieee80211_hw *hw = dev_get_drvdata(d); struct rtl_priv *rtlpriv = rtl_priv(hw); unsigned long val; int ret; ret = strict_strtoul(buf, 0, &val); if (ret) { printk(KERN_DEBUG "%s is not in hex or decimal form.\n", buf); } else { rtlpriv->dbg.global_debuglevel = val; printk(KERN_DEBUG "debuglevel:%x\n", rtlpriv->dbg.global_debuglevel); } return strnlen(buf, count); } static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, rtl_show_debug_level, rtl_store_debug_level); static struct attribute *rtl_sysfs_entries[] = { &dev_attr_debug_level.attr, NULL }; /* * "name" is folder name witch will be * put in device directory like : * sys/devices/pci0000:00/0000:00:1c.4/ * 0000:06:00.0/rtl_sysfs */ struct attribute_group rtl_attribute_group = { .name = "rtlsysfs", .attrs = rtl_sysfs_entries, }; MODULE_AUTHOR("lizhaoming "); MODULE_AUTHOR("Realtek WlanFAE "); MODULE_AUTHOR("Larry Finger "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core"); static int __init rtl_core_module_init(void) { if (rtl_rate_control_register()) pr_err("Unable to register rtl_rc, use default RC !!\n"); return 0; } static void __exit rtl_core_module_exit(void) { /*RC*/ rtl_rate_control_unregister(); } module_init(rtl_core_module_init); module_exit(rtl_core_module_exit); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/usb.h0000644000175000017500000001074212026211315023107 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * *****************************************************************************/ #ifndef __RTL_USB_H__ #define __RTL_USB_H__ #include #define RTL_RX_DESC_SIZE 24 #define RTL_USB_DEVICE(vend, prod, cfg) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \ .idVendor = (vend), \ .idProduct = (prod), \ .driver_info = (kernel_ulong_t)&(cfg) #define USB_HIGH_SPEED_BULK_SIZE 512 #define USB_FULL_SPEED_BULK_SIZE 64 #define RTL_USB_MAX_TXQ_NUM 4 /* max tx queue */ #define RTL_USB_MAX_EP_NUM 6 /* max ep number */ #define RTL_USB_MAX_TX_URBS_NUM 8 enum rtl_txq { /* These definitions shall be consistent with value * returned by skb_get_queue_mapping *------------------------------------*/ RTL_TXQ_BK, RTL_TXQ_BE, RTL_TXQ_VI, RTL_TXQ_VO, /*------------------------------------*/ RTL_TXQ_BCN, RTL_TXQ_MGT, RTL_TXQ_HI, /* Must be last */ __RTL_TXQ_NUM, }; struct rtl_ep_map { u32 ep_mapping[__RTL_TXQ_NUM]; }; struct _trx_info { struct rtl_usb *rtlusb; u32 ep_num; }; static inline void _rtl_install_trx_info(struct rtl_usb *rtlusb, struct sk_buff *skb, u32 ep_num) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); info->rate_driver_data[0] = rtlusb; info->rate_driver_data[1] = (void *)(__kernel_size_t)ep_num; } /* Add suspend/resume later */ enum rtl_usb_state { USB_STATE_STOP = 0, USB_STATE_START = 1, }; #define IS_USB_STOP(rtlusb_ptr) (USB_STATE_STOP == (rtlusb_ptr)->state) #define IS_USB_START(rtlusb_ptr) (USB_STATE_START == (rtlusb_ptr)->state) #define SET_USB_STOP(rtlusb_ptr) \ do { \ (rtlusb_ptr)->state = USB_STATE_STOP; \ } while (0) #define SET_USB_START(rtlusb_ptr) \ do { \ (rtlusb_ptr)->state = USB_STATE_START; \ } while (0) struct rtl_usb { struct usb_device *udev; struct usb_interface *intf; enum rtl_usb_state state; /* Bcn control register setting */ u32 reg_bcn_ctrl_val; /* for 88/92cu card disable */ u8 disableHWSM; /*QOS & EDCA */ enum acm_method acm_method; /* irq . HIMR,HIMR_EX */ u32 irq_mask[2]; bool irq_enabled; u16 (*usb_mq_to_hwq)(__le16 fc, u16 mac80211_queue_index); /* Tx */ u8 out_ep_nums ; u8 out_queue_sel; struct rtl_ep_map ep_map; u32 max_bulk_out_size; u32 tx_submitted_urbs; struct sk_buff_head tx_skb_queue[RTL_USB_MAX_EP_NUM]; struct usb_anchor tx_pending[RTL_USB_MAX_EP_NUM]; struct usb_anchor tx_submitted; struct sk_buff *(*usb_tx_aggregate_hdl)(struct ieee80211_hw *, struct sk_buff_head *); int (*usb_tx_post_hdl)(struct ieee80211_hw *, struct urb *, struct sk_buff *); void (*usb_tx_cleanup)(struct ieee80211_hw *, struct sk_buff *); /* Rx */ u8 in_ep_nums ; u32 in_ep; /* Bulk IN endpoint number */ u32 rx_max_size; /* Bulk IN max buffer size */ u32 rx_urb_num; /* How many Bulk INs are submitted to host. */ struct usb_anchor rx_submitted; void (*usb_rx_segregate_hdl)(struct ieee80211_hw *, struct sk_buff *, struct sk_buff_head *); void (*usb_rx_hdl)(struct ieee80211_hw *, struct sk_buff *); }; struct rtl_usb_priv { struct rtl_usb dev; struct rtl_led_ctl ledctl; }; #define rtl_usbpriv(hw) (((struct rtl_usb_priv *)(rtl_priv(hw))->priv)) #define rtl_usbdev(usbpriv) (&((usbpriv)->dev)) int __devinit rtl_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); void rtl_usb_disconnect(struct usb_interface *intf); int rtl_usb_suspend(struct usb_interface *pusb_intf, pm_message_t message); int rtl_usb_resume(struct usb_interface *pusb_intf); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/usb.c0000644000175000017500000006736112026211315023113 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * *****************************************************************************/ #include "wifi.h" #include "core.h" #include "usb.h" #include "base.h" #include "ps.h" #include "rtl8192c/fw_common.h" #include #define REALTEK_USB_VENQT_READ 0xC0 #define REALTEK_USB_VENQT_WRITE 0x40 #define REALTEK_USB_VENQT_CMD_REQ 0x05 #define REALTEK_USB_VENQT_CMD_IDX 0x00 #define MAX_USBCTRL_VENDORREQ_TIMES 10 static void usbctrl_async_callback(struct urb *urb) { if (urb) kfree(urb->context); } static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request, u16 value, u16 index, void *pdata, u16 len) { int rc; unsigned int pipe; u8 reqtype; struct usb_ctrlrequest *dr; struct urb *urb; struct rtl819x_async_write_data { u8 data[REALTEK_USB_VENQT_MAX_BUF_SIZE]; struct usb_ctrlrequest dr; } *buf; pipe = usb_sndctrlpipe(udev, 0); /* write_out */ reqtype = REALTEK_USB_VENQT_WRITE; buf = kmalloc(sizeof(*buf), GFP_ATOMIC); if (!buf) return -ENOMEM; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { kfree(buf); return -ENOMEM; } dr = &buf->dr; dr->bRequestType = reqtype; dr->bRequest = request; dr->wValue = cpu_to_le16(value); dr->wIndex = cpu_to_le16(index); dr->wLength = cpu_to_le16(len); /* data are already in little-endian order */ memcpy(buf, pdata, len); usb_fill_control_urb(urb, udev, pipe, (unsigned char *)dr, buf, len, usbctrl_async_callback, buf); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc < 0) kfree(buf); usb_free_urb(urb); return rc; } static int _usbctrl_vendorreq_sync_read(struct usb_device *udev, u8 request, u16 value, u16 index, void *pdata, u16 len) { unsigned int pipe; int status; u8 reqtype; int vendorreq_times = 0; static int count; pipe = usb_rcvctrlpipe(udev, 0); /* read_in */ reqtype = REALTEK_USB_VENQT_READ; do { status = usb_control_msg(udev, pipe, request, reqtype, value, index, pdata, len, 0); /*max. timeout*/ if (status < 0) { /* firmware download is checksumed, don't retry */ if ((value >= FW_8192C_START_ADDRESS && value <= FW_8192C_END_ADDRESS)) break; } else { break; } } while (++vendorreq_times < MAX_USBCTRL_VENDORREQ_TIMES); if (status < 0 && count++ < 4) pr_err("reg 0x%x, usbctrl_vendorreq TimeOut! status:0x%x value=0x%x\n", value, status, *(u32 *)pdata); return status; } static u32 _usb_read_sync(struct rtl_priv *rtlpriv, u32 addr, u16 len) { struct device *dev = rtlpriv->io.dev; struct usb_device *udev = to_usb_device(dev); u8 request; u16 wvalue; u16 index; __le32 *data; unsigned long flags; spin_lock_irqsave(&rtlpriv->locks.usb_lock, flags); if (++rtlpriv->usb_data_index >= RTL_USB_MAX_RX_COUNT) rtlpriv->usb_data_index = 0; data = &rtlpriv->usb_data[rtlpriv->usb_data_index]; spin_unlock_irqrestore(&rtlpriv->locks.usb_lock, flags); request = REALTEK_USB_VENQT_CMD_REQ; index = REALTEK_USB_VENQT_CMD_IDX; /* n/a */ wvalue = (u16)addr; _usbctrl_vendorreq_sync_read(udev, request, wvalue, index, data, len); return le32_to_cpu(*data); } static u8 _usb_read8_sync(struct rtl_priv *rtlpriv, u32 addr) { return (u8)_usb_read_sync(rtlpriv, addr, 1); } static u16 _usb_read16_sync(struct rtl_priv *rtlpriv, u32 addr) { return (u16)_usb_read_sync(rtlpriv, addr, 2); } static u32 _usb_read32_sync(struct rtl_priv *rtlpriv, u32 addr) { return _usb_read_sync(rtlpriv, addr, 4); } static void _usb_write_async(struct usb_device *udev, u32 addr, u32 val, u16 len) { u8 request; u16 wvalue; u16 index; __le32 data; request = REALTEK_USB_VENQT_CMD_REQ; index = REALTEK_USB_VENQT_CMD_IDX; /* n/a */ wvalue = (u16)(addr&0x0000ffff); data = cpu_to_le32(val); _usbctrl_vendorreq_async_write(udev, request, wvalue, index, &data, len); } static void _usb_write8_async(struct rtl_priv *rtlpriv, u32 addr, u8 val) { struct device *dev = rtlpriv->io.dev; _usb_write_async(to_usb_device(dev), addr, val, 1); } static void _usb_write16_async(struct rtl_priv *rtlpriv, u32 addr, u16 val) { struct device *dev = rtlpriv->io.dev; _usb_write_async(to_usb_device(dev), addr, val, 2); } static void _usb_write32_async(struct rtl_priv *rtlpriv, u32 addr, u32 val) { struct device *dev = rtlpriv->io.dev; _usb_write_async(to_usb_device(dev), addr, val, 4); } static void _usb_writeN_sync(struct rtl_priv *rtlpriv, u32 addr, void *data, u16 len) { struct device *dev = rtlpriv->io.dev; struct usb_device *udev = to_usb_device(dev); u8 request = REALTEK_USB_VENQT_CMD_REQ; u8 reqtype = REALTEK_USB_VENQT_WRITE; u16 wvalue; u16 index = REALTEK_USB_VENQT_CMD_IDX; int pipe = usb_sndctrlpipe(udev, 0); /* write_out */ u8 *buffer; dma_addr_t dma_addr; wvalue = (u16)(addr&0x0000ffff); buffer = usb_alloc_coherent(udev, (size_t)len, GFP_ATOMIC, &dma_addr); if (!buffer) return; memcpy(buffer, data, len); usb_control_msg(udev, pipe, request, reqtype, wvalue, index, buffer, len, 50); usb_free_coherent(udev, (size_t)len, buffer, dma_addr); } static void _rtl_usb_io_handler_init(struct device *dev, struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->io.dev = dev; mutex_init(&rtlpriv->io.bb_mutex); rtlpriv->io.write8_async = _usb_write8_async; rtlpriv->io.write16_async = _usb_write16_async; rtlpriv->io.write32_async = _usb_write32_async; rtlpriv->io.read8_sync = _usb_read8_sync; rtlpriv->io.read16_sync = _usb_read16_sync; rtlpriv->io.read32_sync = _usb_read32_sync; rtlpriv->io.writeN_sync = _usb_writeN_sync; } static void _rtl_usb_io_handler_release(struct ieee80211_hw *hw) { struct rtl_priv __maybe_unused *rtlpriv = rtl_priv(hw); mutex_destroy(&rtlpriv->io.bb_mutex); } /** * * Default aggregation handler. Do nothing and just return the oldest skb. */ static struct sk_buff *_none_usb_tx_aggregate_hdl(struct ieee80211_hw *hw, struct sk_buff_head *list) { return skb_dequeue(list); } #define IS_HIGH_SPEED_USB(udev) \ ((USB_SPEED_HIGH == (udev)->speed) ? true : false) static int _rtl_usb_init_tx(struct ieee80211_hw *hw) { u32 i; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); rtlusb->max_bulk_out_size = IS_HIGH_SPEED_USB(rtlusb->udev) ? USB_HIGH_SPEED_BULK_SIZE : USB_FULL_SPEED_BULK_SIZE; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "USB Max Bulk-out Size=%d\n", rtlusb->max_bulk_out_size); for (i = 0; i < __RTL_TXQ_NUM; i++) { u32 ep_num = rtlusb->ep_map.ep_mapping[i]; if (!ep_num) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Invalid endpoint map setting!\n"); return -EINVAL; } } rtlusb->usb_tx_post_hdl = rtlpriv->cfg->usb_interface_cfg->usb_tx_post_hdl; rtlusb->usb_tx_cleanup = rtlpriv->cfg->usb_interface_cfg->usb_tx_cleanup; rtlusb->usb_tx_aggregate_hdl = (rtlpriv->cfg->usb_interface_cfg->usb_tx_aggregate_hdl) ? rtlpriv->cfg->usb_interface_cfg->usb_tx_aggregate_hdl : &_none_usb_tx_aggregate_hdl; init_usb_anchor(&rtlusb->tx_submitted); for (i = 0; i < RTL_USB_MAX_EP_NUM; i++) { skb_queue_head_init(&rtlusb->tx_skb_queue[i]); init_usb_anchor(&rtlusb->tx_pending[i]); } return 0; } static int _rtl_usb_init_rx(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); rtlusb->rx_max_size = rtlpriv->cfg->usb_interface_cfg->rx_max_size; rtlusb->rx_urb_num = rtlpriv->cfg->usb_interface_cfg->rx_urb_num; rtlusb->in_ep = rtlpriv->cfg->usb_interface_cfg->in_ep_num; rtlusb->usb_rx_hdl = rtlpriv->cfg->usb_interface_cfg->usb_rx_hdl; rtlusb->usb_rx_segregate_hdl = rtlpriv->cfg->usb_interface_cfg->usb_rx_segregate_hdl; pr_info("rx_max_size %d, rx_urb_num %d, in_ep %d\n", rtlusb->rx_max_size, rtlusb->rx_urb_num, rtlusb->in_ep); init_usb_anchor(&rtlusb->rx_submitted); return 0; } static int _rtl_usb_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); int err; u8 epidx; struct usb_interface *usb_intf = rtlusb->intf; u8 epnums = usb_intf->cur_altsetting->desc.bNumEndpoints; rtlusb->out_ep_nums = rtlusb->in_ep_nums = 0; for (epidx = 0; epidx < epnums; epidx++) { struct usb_endpoint_descriptor *pep_desc; pep_desc = &usb_intf->cur_altsetting->endpoint[epidx].desc; if (usb_endpoint_dir_in(pep_desc)) rtlusb->in_ep_nums++; else if (usb_endpoint_dir_out(pep_desc)) rtlusb->out_ep_nums++; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "USB EP(0x%02x), MaxPacketSize=%d, Interval=%d\n", pep_desc->bEndpointAddress, pep_desc->wMaxPacketSize, pep_desc->bInterval); } if (rtlusb->in_ep_nums < rtlpriv->cfg->usb_interface_cfg->in_ep_num) { pr_err("Too few input end points found\n"); return -EINVAL; } if (rtlusb->out_ep_nums == 0) { pr_err("No output end points found\n"); return -EINVAL; } /* usb endpoint mapping */ err = rtlpriv->cfg->usb_interface_cfg->usb_endpoint_mapping(hw); rtlusb->usb_mq_to_hwq = rtlpriv->cfg->usb_interface_cfg->usb_mq_to_hwq; _rtl_usb_init_tx(hw); _rtl_usb_init_rx(hw); return err; } static void rtl_usb_init_sw(struct ieee80211_hw *hw) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); rtlhal->hw = hw; ppsc->inactiveps = false; ppsc->leisure_ps = false; ppsc->fwctrl_lps = false; ppsc->reg_fwctrl_lps = 3; ppsc->reg_max_lps_awakeintvl = 5; ppsc->fwctrl_psmode = FW_PS_DTIM_MODE; /* IBSS */ mac->beacon_interval = 100; /* AMPDU */ mac->min_space_cfg = 0; mac->max_mss_density = 0; /* set sane AMPDU defaults */ mac->current_ampdu_density = 7; mac->current_ampdu_factor = 3; /* QOS */ rtlusb->acm_method = eAcmWay2_SW; /* IRQ */ /* HIMR - turn all on */ rtlusb->irq_mask[0] = 0xFFFFFFFF; /* HIMR_EX - turn all on */ rtlusb->irq_mask[1] = 0xFFFFFFFF; rtlusb->disableHWSM = true; } #define __RADIO_TAP_SIZE_RSV 32 static void _rtl_rx_completed(struct urb *urb); static struct sk_buff *_rtl_prep_rx_urb(struct ieee80211_hw *hw, struct rtl_usb *rtlusb, struct urb *urb, gfp_t gfp_mask) { struct sk_buff *skb; struct rtl_priv *rtlpriv = rtl_priv(hw); skb = __dev_alloc_skb((rtlusb->rx_max_size + __RADIO_TAP_SIZE_RSV), gfp_mask); if (!skb) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Failed to __dev_alloc_skb!!\n"); return ERR_PTR(-ENOMEM); } /* reserve some space for mac80211's radiotap */ skb_reserve(skb, __RADIO_TAP_SIZE_RSV); usb_fill_bulk_urb(urb, rtlusb->udev, usb_rcvbulkpipe(rtlusb->udev, rtlusb->in_ep), skb->data, min(skb_tailroom(skb), (int)rtlusb->rx_max_size), _rtl_rx_completed, skb); _rtl_install_trx_info(rtlusb, skb, rtlusb->in_ep); return skb; } #undef __RADIO_TAP_SIZE_RSV static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 *rxdesc = skb->data; struct ieee80211_hdr *hdr; bool unicast = false; __le16 fc; struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, .noise = -98, .rate = 0, }; skb_pull(skb, RTL_RX_DESC_SIZE); rtlpriv->cfg->ops->query_rx_desc(hw, &stats, &rx_status, rxdesc, skb); skb_pull(skb, (stats.rx_drvinfo_size + stats.rx_bufshift)); hdr = (struct ieee80211_hdr *)(skb->data); fc = hdr->frame_control; if (!stats.crc) { memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); if (is_broadcast_ether_addr(hdr->addr1)) { /*TODO*/; } else if (is_multicast_ether_addr(hdr->addr1)) { /*TODO*/ } else { unicast = true; rtlpriv->stats.rxbytesunicast += skb->len; } rtl_is_special_data(hw, skb, false); if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); if (unicast) rtlpriv->link_info.num_rx_inperiod++; } } } static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 *rxdesc = skb->data; struct ieee80211_hdr *hdr; bool unicast = false; __le16 fc; struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, .noise = -98, .rate = 0, }; skb_pull(skb, RTL_RX_DESC_SIZE); rtlpriv->cfg->ops->query_rx_desc(hw, &stats, &rx_status, rxdesc, skb); skb_pull(skb, (stats.rx_drvinfo_size + stats.rx_bufshift)); hdr = (struct ieee80211_hdr *)(skb->data); fc = hdr->frame_control; if (!stats.crc) { memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); if (is_broadcast_ether_addr(hdr->addr1)) { /*TODO*/; } else if (is_multicast_ether_addr(hdr->addr1)) { /*TODO*/ } else { unicast = true; rtlpriv->stats.rxbytesunicast += skb->len; } rtl_is_special_data(hw, skb, false); if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); if (unicast) rtlpriv->link_info.num_rx_inperiod++; } if (likely(rtl_action_proc(hw, skb, false))) { struct sk_buff *uskb = NULL; u8 *pdata; uskb = dev_alloc_skb(skb->len + 128); if (uskb) { /* drop packet on allocation failure */ memcpy(IEEE80211_SKB_RXCB(uskb), &rx_status, sizeof(rx_status)); pdata = (u8 *)skb_put(uskb, skb->len); memcpy(pdata, skb->data, skb->len); ieee80211_rx_irqsafe(hw, uskb); } dev_kfree_skb_any(skb); } else { dev_kfree_skb_any(skb); } } } static void _rtl_rx_pre_process(struct ieee80211_hw *hw, struct sk_buff *skb) { struct sk_buff *_skb; struct sk_buff_head rx_queue; struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); skb_queue_head_init(&rx_queue); if (rtlusb->usb_rx_segregate_hdl) rtlusb->usb_rx_segregate_hdl(hw, skb, &rx_queue); WARN_ON(skb_queue_empty(&rx_queue)); while (!skb_queue_empty(&rx_queue)) { _skb = skb_dequeue(&rx_queue); _rtl_usb_rx_process_agg(hw, skb); ieee80211_rx_irqsafe(hw, skb); } } static void _rtl_rx_completed(struct urb *_urb) { struct sk_buff *skb = (struct sk_buff *)_urb->context; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rtl_usb *rtlusb = (struct rtl_usb *)info->rate_driver_data[0]; struct ieee80211_hw *hw = usb_get_intfdata(rtlusb->intf); struct rtl_priv *rtlpriv = rtl_priv(hw); int err = 0; if (unlikely(IS_USB_STOP(rtlusb))) goto free; if (likely(0 == _urb->status)) { /* If this code were moved to work queue, would CPU * utilization be improved? NOTE: We shall allocate another skb * and reuse the original one. */ skb_put(skb, _urb->actual_length); if (likely(!rtlusb->usb_rx_segregate_hdl)) { struct sk_buff *_skb; _rtl_usb_rx_process_noagg(hw, skb); _skb = _rtl_prep_rx_urb(hw, rtlusb, _urb, GFP_ATOMIC); if (IS_ERR(_skb)) { err = PTR_ERR(_skb); RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Can't allocate skb for bulk IN!\n"); return; } skb = _skb; } else{ /* TO DO */ _rtl_rx_pre_process(hw, skb); pr_err("rx agg not supported\n"); } goto resubmit; } switch (_urb->status) { /* disconnect */ case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: goto free; default: break; } resubmit: skb_reset_tail_pointer(skb); skb_trim(skb, 0); usb_anchor_urb(_urb, &rtlusb->rx_submitted); err = usb_submit_urb(_urb, GFP_ATOMIC); if (unlikely(err)) { usb_unanchor_urb(_urb); goto free; } return; free: dev_kfree_skb_irq(skb); } static int _rtl_usb_receive(struct ieee80211_hw *hw) { struct urb *urb; struct sk_buff *skb; int err; int i; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); WARN_ON(0 == rtlusb->rx_urb_num); /* 1600 == 1514 + max WLAN header + rtk info */ WARN_ON(rtlusb->rx_max_size < 1600); for (i = 0; i < rtlusb->rx_urb_num; i++) { err = -ENOMEM; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Failed to alloc URB!!\n"); goto err_out; } skb = _rtl_prep_rx_urb(hw, rtlusb, urb, GFP_KERNEL); if (IS_ERR(skb)) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Failed to prep_rx_urb!!\n"); err = PTR_ERR(skb); goto err_out; } usb_anchor_urb(urb, &rtlusb->rx_submitted); err = usb_submit_urb(urb, GFP_KERNEL); if (err) goto err_out; usb_free_urb(urb); } return 0; err_out: usb_kill_anchored_urbs(&rtlusb->rx_submitted); return err; } static int rtl_usb_start(struct ieee80211_hw *hw) { int err; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); err = rtlpriv->cfg->ops->hw_init(hw); if (!err) { rtl_init_rx_config(hw); /* Enable software */ SET_USB_START(rtlusb); /* should after adapter start and interrupt enable. */ set_hal_start(rtlhal); /* Start bulk IN */ _rtl_usb_receive(hw); } return err; } /** * * */ /*======================= tx =========================================*/ static void rtl_usb_cleanup(struct ieee80211_hw *hw) { u32 i; struct sk_buff *_skb; struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); struct ieee80211_tx_info *txinfo; SET_USB_STOP(rtlusb); /* clean up rx stuff. */ usb_kill_anchored_urbs(&rtlusb->rx_submitted); /* clean up tx stuff */ for (i = 0; i < RTL_USB_MAX_EP_NUM; i++) { while ((_skb = skb_dequeue(&rtlusb->tx_skb_queue[i]))) { rtlusb->usb_tx_cleanup(hw, _skb); txinfo = IEEE80211_SKB_CB(_skb); ieee80211_tx_info_clear_status(txinfo); txinfo->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(hw, _skb); } usb_kill_anchored_urbs(&rtlusb->tx_pending[i]); } usb_kill_anchored_urbs(&rtlusb->tx_submitted); } /** * * We may add some struct into struct rtl_usb later. Do deinit here. * */ static void rtl_usb_deinit(struct ieee80211_hw *hw) { rtl_usb_cleanup(hw); } static void rtl_usb_stop(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); /* should after adapter start and interrupt enable. */ set_hal_stop(rtlhal); /* Enable software */ SET_USB_STOP(rtlusb); rtl_usb_deinit(hw); rtlpriv->cfg->ops->hw_disable(hw); } static void _rtl_submit_tx_urb(struct ieee80211_hw *hw, struct urb *_urb) { int err; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); usb_anchor_urb(_urb, &rtlusb->tx_submitted); err = usb_submit_urb(_urb, GFP_ATOMIC); if (err < 0) { struct sk_buff *skb; RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Failed to submit urb\n"); usb_unanchor_urb(_urb); skb = (struct sk_buff *)_urb->context; kfree_skb(skb); } usb_free_urb(_urb); } static int _usb_tx_post(struct ieee80211_hw *hw, struct urb *urb, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); struct ieee80211_tx_info *txinfo; rtlusb->usb_tx_post_hdl(hw, urb, skb); skb_pull(skb, RTL_TX_HEADER_SIZE); txinfo = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(txinfo); txinfo->flags |= IEEE80211_TX_STAT_ACK; if (urb->status) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Urb has error status 0x%X\n", urb->status); goto out; } /* TODO: statistics */ out: ieee80211_tx_status_irqsafe(hw, skb); return urb->status; } static void _rtl_tx_complete(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *)urb->context; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rtl_usb *rtlusb = (struct rtl_usb *)info->rate_driver_data[0]; struct ieee80211_hw *hw = usb_get_intfdata(rtlusb->intf); int err; if (unlikely(IS_USB_STOP(rtlusb))) return; err = _usb_tx_post(hw, urb, skb); if (err) { /* Ignore error and keep issuiing other urbs */ return; } } static struct urb *_rtl_usb_tx_urb_setup(struct ieee80211_hw *hw, struct sk_buff *skb, u32 ep_num) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); struct urb *_urb; WARN_ON(NULL == skb); _urb = usb_alloc_urb(0, GFP_ATOMIC); if (!_urb) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Can't allocate URB for bulk out!\n"); kfree_skb(skb); return NULL; } _rtl_install_trx_info(rtlusb, skb, ep_num); usb_fill_bulk_urb(_urb, rtlusb->udev, usb_sndbulkpipe(rtlusb->udev, ep_num), skb->data, skb->len, _rtl_tx_complete, skb); _urb->transfer_flags |= URB_ZERO_PACKET; return _urb; } static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb, enum rtl_txq qnum) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); u32 ep_num; struct urb *_urb = NULL; struct sk_buff *_skb = NULL; struct sk_buff_head *skb_list; struct usb_anchor *urb_list; WARN_ON(NULL == rtlusb->usb_tx_aggregate_hdl); if (unlikely(IS_USB_STOP(rtlusb))) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "USB device is stopping...\n"); kfree_skb(skb); return; } ep_num = rtlusb->ep_map.ep_mapping[qnum]; skb_list = &rtlusb->tx_skb_queue[ep_num]; _skb = skb; _urb = _rtl_usb_tx_urb_setup(hw, _skb, ep_num); if (unlikely(!_urb)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't allocate urb. Drop skb!\n"); return; } urb_list = &rtlusb->tx_pending[ep_num]; _rtl_submit_tx_urb(hw, _urb); } static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb, u16 hw_queue) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rtl_tx_desc *pdesc = NULL; struct rtl_tcb_desc tcb_desc; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; u8 *pda_addr = hdr->addr1; /* ssn */ u8 *qc = NULL; u8 tid = 0; u16 seq_number = 0; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); if (ieee80211_is_auth(fc)) { RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "MAC80211_LINKING\n"); rtl_ips_nic_on(hw); } if (rtlpriv->psc.sw_ps_enabled) { if (ieee80211_is_data(fc) && !ieee80211_is_nullfunc(fc) && !ieee80211_has_pm(fc)) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); } rtl_action_proc(hw, skb, true); if (is_multicast_ether_addr(pda_addr)) rtlpriv->stats.txbytesmulticast += skb->len; else if (is_broadcast_ether_addr(pda_addr)) rtlpriv->stats.txbytesbroadcast += skb->len; else rtlpriv->stats.txbytesunicast += skb->len; if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; seq_number += 1; seq_number <<= 4; } rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, info, sta, skb, hw_queue, &tcb_desc); if (!ieee80211_has_morefrags(hdr->frame_control)) { if (qc) mac->tids[tid].seq_number = seq_number; } if (ieee80211_is_data(fc)) rtlpriv->cfg->ops->led_control(hw, LED_CTL_TX); } static int rtl_usb_tx(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb, struct rtl_tcb_desc *dummy) { struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; u16 hw_queue; if (unlikely(is_hal_stop(rtlhal))) goto err_free; hw_queue = rtlusb->usb_mq_to_hwq(fc, skb_get_queue_mapping(skb)); _rtl_usb_tx_preprocess(hw, sta, skb, hw_queue); _rtl_usb_transmit(hw, skb, hw_queue); return NETDEV_TX_OK; err_free: dev_kfree_skb_any(skb); return NETDEV_TX_OK; } static bool rtl_usb_tx_chk_waitq_insert(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb) { return false; } static struct rtl_intf_ops rtl_usb_ops = { .adapter_start = rtl_usb_start, .adapter_stop = rtl_usb_stop, .adapter_tx = rtl_usb_tx, .waitq_insert = rtl_usb_tx_chk_waitq_insert, }; int __devinit rtl_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { int err; struct ieee80211_hw *hw = NULL; struct rtl_priv *rtlpriv = NULL; struct usb_device *udev; struct rtl_usb_priv *usb_priv; hw = ieee80211_alloc_hw(sizeof(struct rtl_priv) + sizeof(struct rtl_usb_priv), &rtl_ops); if (!hw) { RT_ASSERT(false, "ieee80211 alloc failed\n"); return -ENOMEM; } rtlpriv = hw->priv; rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32), GFP_KERNEL); if (!rtlpriv->usb_data) return -ENOMEM; /* this spin lock must be initialized early */ spin_lock_init(&rtlpriv->locks.usb_lock); rtlpriv->usb_data_index = 0; init_completion(&rtlpriv->firmware_loading_complete); SET_IEEE80211_DEV(hw, &intf->dev); udev = interface_to_usbdev(intf); usb_get_dev(udev); usb_priv = rtl_usbpriv(hw); memset(usb_priv, 0, sizeof(*usb_priv)); usb_priv->dev.intf = intf; usb_priv->dev.udev = udev; usb_set_intfdata(intf, hw); /* init cfg & intf_ops */ rtlpriv->rtlhal.interface = INTF_USB; rtlpriv->cfg = (struct rtl_hal_cfg *)(id->driver_info); rtlpriv->intf_ops = &rtl_usb_ops; rtl_dbgp_flag_init(hw); /* Init IO handler */ _rtl_usb_io_handler_init(&udev->dev, hw); rtlpriv->cfg->ops->read_chip_version(hw); /*like read eeprom and so on */ rtlpriv->cfg->ops->read_eeprom_info(hw); err = _rtl_usb_init(hw); if (err) goto error_out; rtl_usb_init_sw(hw); /* Init mac80211 sw */ err = rtl_init_core(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't allocate sw for mac80211\n"); goto error_out; } if (rtlpriv->cfg->ops->init_sw_vars(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n"); goto error_out; } rtlpriv->cfg->ops->init_sw_leds(hw); return 0; error_out: rtl_deinit_core(hw); _rtl_usb_io_handler_release(hw); usb_put_dev(udev); complete(&rtlpriv->firmware_loading_complete); return -ENODEV; } EXPORT_SYMBOL(rtl_usb_probe); void rtl_usb_disconnect(struct usb_interface *intf) { struct ieee80211_hw *hw = usb_get_intfdata(intf); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); if (unlikely(!rtlpriv)) return; /* just in case driver is removed before firmware callback */ wait_for_completion(&rtlpriv->firmware_loading_complete); /*ieee80211_unregister_hw will call ops_stop */ if (rtlmac->mac80211_registered == 1) { ieee80211_unregister_hw(hw); rtlmac->mac80211_registered = 0; } else { rtl_deinit_deferred_work(hw); rtlpriv->intf_ops->adapter_stop(hw); } /*deinit rfkill */ /* rtl_deinit_rfkill(hw); */ rtl_usb_deinit(hw); rtl_deinit_core(hw); kfree(rtlpriv->usb_data); rtlpriv->cfg->ops->deinit_sw_leds(hw); rtlpriv->cfg->ops->deinit_sw_vars(hw); _rtl_usb_io_handler_release(hw); usb_put_dev(rtlusb->udev); usb_set_intfdata(intf, NULL); ieee80211_free_hw(hw); } EXPORT_SYMBOL(rtl_usb_disconnect); int rtl_usb_suspend(struct usb_interface *pusb_intf, pm_message_t message) { return 0; } EXPORT_SYMBOL(rtl_usb_suspend); int rtl_usb_resume(struct usb_interface *pusb_intf) { return 0; } EXPORT_SYMBOL(rtl_usb_resume); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/regd.h0000644000175000017500000000365312026211315023242 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_REGD_H__ #define __RTL_REGD_H__ struct country_code_to_enum_rd { u16 countrycode; const char *iso_name; }; enum country_code_type_t { COUNTRY_CODE_FCC = 0, COUNTRY_CODE_IC = 1, COUNTRY_CODE_ETSI = 2, COUNTRY_CODE_SPAIN = 3, COUNTRY_CODE_FRANCE = 4, COUNTRY_CODE_MKK = 5, COUNTRY_CODE_MKK1 = 6, COUNTRY_CODE_ISRAEL = 7, COUNTRY_CODE_TELEC = 8, COUNTRY_CODE_MIC = 9, COUNTRY_CODE_GLOBAL_DOMAIN = 10, COUNTRY_CODE_WORLD_WIDE_13 = 11, COUNTRY_CODE_TELEC_NETGEAR = 12, /*add new channel plan above this line */ COUNTRY_CODE_MAX }; int rtl_regd_init(struct ieee80211_hw *hw, int (*reg_notifier) (struct wiphy *wiphy, struct regulatory_request *request)); int rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/regd.c0000644000175000017500000002721512026211315023235 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "wifi.h" #include "regd.h" static struct country_code_to_enum_rd allCountries[] = { {COUNTRY_CODE_FCC, "US"}, {COUNTRY_CODE_IC, "US"}, {COUNTRY_CODE_ETSI, "EC"}, {COUNTRY_CODE_SPAIN, "EC"}, {COUNTRY_CODE_FRANCE, "EC"}, {COUNTRY_CODE_MKK, "JP"}, {COUNTRY_CODE_MKK1, "JP"}, {COUNTRY_CODE_ISRAEL, "EC"}, {COUNTRY_CODE_TELEC, "JP"}, {COUNTRY_CODE_MIC, "JP"}, {COUNTRY_CODE_GLOBAL_DOMAIN, "JP"}, {COUNTRY_CODE_WORLD_WIDE_13, "EC"}, {COUNTRY_CODE_TELEC_NETGEAR, "EC"}, }; /* *Only these channels all allow active *scan on all world regulatory domains */ #define RTL819x_2GHZ_CH01_11 \ REG_RULE(2412-10, 2462+10, 40, 0, 20, 0) /* *We enable active scan on these a case *by case basis by regulatory domain */ #define RTL819x_2GHZ_CH12_13 \ REG_RULE(2467-10, 2472+10, 40, 0, 20,\ NL80211_RRF_PASSIVE_SCAN) #define RTL819x_2GHZ_CH14 \ REG_RULE(2484-10, 2484+10, 40, 0, 20, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_NO_OFDM) /* 5G chan 36 - chan 64*/ #define RTL819x_5GHZ_5150_5350 \ REG_RULE(5150-10, 5350+10, 40, 0, 30, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_NO_IBSS) /* 5G chan 100 - chan 165*/ #define RTL819x_5GHZ_5470_5850 \ REG_RULE(5470-10, 5850+10, 40, 0, 30, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_NO_IBSS) /* 5G chan 149 - chan 165*/ #define RTL819x_5GHZ_5725_5850 \ REG_RULE(5725-10, 5850+10, 40, 0, 30, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_NO_IBSS) #define RTL819x_5GHZ_ALL \ (RTL819x_5GHZ_5150_5350, RTL819x_5GHZ_5470_5850) static const struct ieee80211_regdomain rtl_regdom_11 = { .n_reg_rules = 1, .alpha2 = "99", .reg_rules = { RTL819x_2GHZ_CH01_11, } }; static const struct ieee80211_regdomain rtl_regdom_12_13 = { .n_reg_rules = 2, .alpha2 = "99", .reg_rules = { RTL819x_2GHZ_CH01_11, RTL819x_2GHZ_CH12_13, } }; static const struct ieee80211_regdomain rtl_regdom_no_midband = { .n_reg_rules = 3, .alpha2 = "99", .reg_rules = { RTL819x_2GHZ_CH01_11, RTL819x_5GHZ_5150_5350, RTL819x_5GHZ_5725_5850, } }; static const struct ieee80211_regdomain rtl_regdom_60_64 = { .n_reg_rules = 3, .alpha2 = "99", .reg_rules = { RTL819x_2GHZ_CH01_11, RTL819x_2GHZ_CH12_13, RTL819x_5GHZ_5725_5850, } }; static const struct ieee80211_regdomain rtl_regdom_14_60_64 = { .n_reg_rules = 4, .alpha2 = "99", .reg_rules = { RTL819x_2GHZ_CH01_11, RTL819x_2GHZ_CH12_13, RTL819x_2GHZ_CH14, RTL819x_5GHZ_5725_5850, } }; static const struct ieee80211_regdomain rtl_regdom_14 = { .n_reg_rules = 3, .alpha2 = "99", .reg_rules = { RTL819x_2GHZ_CH01_11, RTL819x_2GHZ_CH12_13, RTL819x_2GHZ_CH14, } }; static bool _rtl_is_radar_freq(u16 center_freq) { return (center_freq >= 5260 && center_freq <= 5700); } static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { enum ieee80211_band band; struct ieee80211_supported_band *sband; const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; u32 bandwidth = 0; int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (_rtl_is_radar_freq(ch->center_freq) || (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); if (r) continue; /* *If 11d had a rule for this channel ensure *we enable adhoc/beaconing if it allows us to *use it. Note that we would have disabled it *by applying our static world regdomain by *default during init, prior to calling our *regulatory_hint(). */ if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) ch->flags &= ~IEEE80211_CHAN_NO_IBSS; if (!(reg_rule-> flags & NL80211_RRF_PASSIVE_SCAN)) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } else { if (ch->beacon_found) ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN); } } } } /* Allows active scan scan on Ch 12 and 13 */ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; u32 bandwidth = 0; int r; if (!wiphy->bands[IEEE80211_BAND_2GHZ]) return; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; /* *If no country IE has been received always enable active scan *on these channels. This is only done for specific regulatory SKUs */ if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { ch = &sband->channels[11]; /* CH 12 */ if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; ch = &sband->channels[12]; /* CH 13 */ if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; return; } /* *If a country IE has been received check its rule for this *channel first before enabling active scan. The passive scan *would have been enforced by the initial processing of our *custom regulatory domain. */ ch = &sband->channels[11]; /* CH 12 */ r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } ch = &sband->channels[12]; /* CH 13 */ r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } } /* *Always apply Radar/DFS rules on *freq range 5260 MHz - 5700 MHz */ static void _rtl_reg_apply_radar_flags(struct wiphy *wiphy) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; unsigned int i; if (!wiphy->bands[IEEE80211_BAND_5GHZ]) return; sband = wiphy->bands[IEEE80211_BAND_5GHZ]; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (!_rtl_is_radar_freq(ch->center_freq)) continue; /* *We always enable radar detection/DFS on this *frequency range. Additionally we also apply on *this frequency range: *- If STA mode does not yet have DFS supports disable * active scanning *- If adhoc mode does not support DFS yet then disable * adhoc in the frequency. *- If AP mode does not yet support radar detection/DFS *do not allow AP mode */ if (!(ch->flags & IEEE80211_CHAN_DISABLED)) ch->flags |= IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN; } } static void _rtl_reg_apply_world_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, struct rtl_regulatory *reg) { _rtl_reg_apply_beaconing_flags(wiphy, initiator); _rtl_reg_apply_active_scan_flags(wiphy, initiator); return; } static int _rtl_reg_notifier_apply(struct wiphy *wiphy, struct regulatory_request *request, struct rtl_regulatory *reg) { /* We always apply this */ _rtl_reg_apply_radar_flags(wiphy); switch (request->initiator) { case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_USER: break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: _rtl_reg_apply_world_flags(wiphy, request->initiator, reg); break; } return 0; } static const struct ieee80211_regdomain *_rtl_regdomain_select( struct rtl_regulatory *reg) { switch (reg->country_code) { case COUNTRY_CODE_FCC: return &rtl_regdom_no_midband; case COUNTRY_CODE_IC: return &rtl_regdom_11; case COUNTRY_CODE_ETSI: case COUNTRY_CODE_TELEC_NETGEAR: return &rtl_regdom_60_64; case COUNTRY_CODE_SPAIN: case COUNTRY_CODE_FRANCE: case COUNTRY_CODE_ISRAEL: case COUNTRY_CODE_WORLD_WIDE_13: return &rtl_regdom_12_13; case COUNTRY_CODE_MKK: case COUNTRY_CODE_MKK1: case COUNTRY_CODE_TELEC: case COUNTRY_CODE_MIC: return &rtl_regdom_14_60_64; case COUNTRY_CODE_GLOBAL_DOMAIN: return &rtl_regdom_14; default: return &rtl_regdom_no_midband; } } static int _rtl_regd_init_wiphy(struct rtl_regulatory *reg, struct wiphy *wiphy, int (*reg_notifier) (struct wiphy *wiphy, struct regulatory_request * request)) { const struct ieee80211_regdomain *regd; wiphy->reg_notifier = reg_notifier; wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY; wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS; regd = _rtl_regdomain_select(reg); wiphy_apply_custom_regulatory(wiphy, regd); _rtl_reg_apply_radar_flags(wiphy); _rtl_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); return 0; } static struct country_code_to_enum_rd *_rtl_regd_find_country(u16 countrycode) { int i; for (i = 0; i < ARRAY_SIZE(allCountries); i++) { if (allCountries[i].countrycode == countrycode) return &allCountries[i]; } return NULL; } int rtl_regd_init(struct ieee80211_hw *hw, int (*reg_notifier) (struct wiphy *wiphy, struct regulatory_request *request)) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct wiphy *wiphy = hw->wiphy; struct country_code_to_enum_rd *country = NULL; if (wiphy == NULL || &rtlpriv->regd == NULL) return -EINVAL; /* init country_code from efuse channel plan */ rtlpriv->regd.country_code = rtlpriv->efuse.channel_plan; RT_TRACE(rtlpriv, COMP_REGD, DBG_TRACE, "rtl: EEPROM regdomain: 0x%0x\n", rtlpriv->regd.country_code); if (rtlpriv->regd.country_code >= COUNTRY_CODE_MAX) { RT_TRACE(rtlpriv, COMP_REGD, DBG_DMESG, "rtl: EEPROM indicates invalid contry code, world wide 13 should be used\n"); rtlpriv->regd.country_code = COUNTRY_CODE_WORLD_WIDE_13; } country = _rtl_regd_find_country(rtlpriv->regd.country_code); if (country) { rtlpriv->regd.alpha2[0] = country->iso_name[0]; rtlpriv->regd.alpha2[1] = country->iso_name[1]; } else { rtlpriv->regd.alpha2[0] = '0'; rtlpriv->regd.alpha2[1] = '0'; } RT_TRACE(rtlpriv, COMP_REGD, DBG_TRACE, "rtl: Country alpha2 being used: %c%c\n", rtlpriv->regd.alpha2[0], rtlpriv->regd.alpha2[1]); _rtl_regd_init_wiphy(&rtlpriv->regd, wiphy, reg_notifier); return 0; } int rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_REGD, DBG_LOUD, "\n"); return _rtl_reg_notifier_apply(wiphy, request, &rtlpriv->regd); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rc.h0000644000175000017500000000275612026211315022730 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_RC_H__ #define __RTL_RC_H__ #define B_MODE_MAX_RIX 3 #define G_MODE_MAX_RIX 11 #define A_MODE_MAX_RIX 7 /* in mac80211 mcs0-mcs15 is idx0-idx15*/ #define N_MODE_MCS7_RIX 7 #define N_MODE_MCS15_RIX 15 struct rtl_rate_priv { u8 ht_cap; }; int rtl_rate_control_register(void); void rtl_rate_control_unregister(void); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rc.c0000644000175000017500000001720212026211315022713 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "wifi.h" #include "base.h" #include "rc.h" /* *Finds the highest rate index we can use *if skb is special data like DHCP/EAPOL, we set should *it to lowest rate CCK_1M, otherwise we set rate to *CCK11M or OFDM_54M based on wireless mode. */ static u8 _rtl_rc_get_highest_rix(struct rtl_priv *rtlpriv, struct ieee80211_sta *sta, struct sk_buff *skb, bool not_data) { struct rtl_mac *rtlmac = rtl_mac(rtlpriv); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_sta_info *sta_entry = NULL; u8 wireless_mode = 0; /* *this rate is no use for true rate, firmware *will control rate at all it just used for *1.show in iwconfig in B/G mode *2.in rtl_get_tcb_desc when we check rate is * 1M we will not use FW rate but user rate. */ if (rtlmac->opmode == NL80211_IFTYPE_AP || rtlmac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) { sta_entry = (struct rtl_sta_info *) sta->drv_priv; wireless_mode = sta_entry->wireless_mode; } else { return 0; } } else { wireless_mode = rtlmac->mode; } if (rtl_is_special_data(rtlpriv->mac80211.hw, skb, true) || not_data) { return 0; } else { if (rtlhal->current_bandtype == BAND_ON_2_4G) { if (wireless_mode == WIRELESS_MODE_B) { return B_MODE_MAX_RIX; } else if (wireless_mode == WIRELESS_MODE_G) { return G_MODE_MAX_RIX; } else { if (get_rf_type(rtlphy) != RF_2T2R) return N_MODE_MCS7_RIX; else return N_MODE_MCS15_RIX; } } else { if (wireless_mode == WIRELESS_MODE_A) { return A_MODE_MAX_RIX; } else { if (get_rf_type(rtlphy) != RF_2T2R) return N_MODE_MCS7_RIX; else return N_MODE_MCS15_RIX; } } } } static void _rtl_rc_rate_set_series(struct rtl_priv *rtlpriv, struct ieee80211_sta *sta, struct ieee80211_tx_rate *rate, struct ieee80211_tx_rate_control *txrc, u8 tries, char rix, int rtsctsenable, bool not_data) { struct rtl_mac *mac = rtl_mac(rtlpriv); u8 sgi_20 = 0, sgi_40 = 0; if (sta) { sgi_20 = sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20; sgi_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40; } rate->count = tries; rate->idx = rix >= 0x00 ? rix : 0x00; if (!not_data) { if (txrc->short_preamble) rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta && (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; } else { if (mac->bw_40) rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; } if (sgi_20 || sgi_40) rate->flags |= IEEE80211_TX_RC_SHORT_GI; if (sta && sta->ht_cap.ht_supported) rate->flags |= IEEE80211_TX_RC_MCS; } } static void rtl_get_rate(void *ppriv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct rtl_priv *rtlpriv = ppriv; struct sk_buff *skb = txrc->skb; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *rates = tx_info->control.rates; __le16 fc = rtl_get_fc(skb); u8 try_per_rate, i, rix; bool not_data = !ieee80211_is_data(fc); if (rate_control_send_low(sta, priv_sta, txrc)) return; rix = _rtl_rc_get_highest_rix(rtlpriv, sta, skb, not_data); try_per_rate = 1; _rtl_rc_rate_set_series(rtlpriv, sta, &rates[0], txrc, try_per_rate, rix, 1, not_data); if (!not_data) { for (i = 1; i < 4; i++) _rtl_rc_rate_set_series(rtlpriv, sta, &rates[i], txrc, i, (rix - i), 1, not_data); } } static bool _rtl_tx_aggr_check(struct rtl_priv *rtlpriv, struct rtl_sta_info *sta_entry, u16 tid) { struct rtl_mac *mac = rtl_mac(rtlpriv); if (mac->act_scanning) return false; if (mac->opmode == NL80211_IFTYPE_STATION && mac->cnt_after_linked < 3) return false; if (sta_entry->tids[tid].agg.agg_state == RTL_AGG_STOP) return true; return false; } /*mac80211 Rate Control callbacks*/ static void rtl_tx_status(void *ppriv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { struct rtl_priv *rtlpriv = ppriv; struct rtl_mac *mac = rtl_mac(rtlpriv); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); __le16 fc = rtl_get_fc(skb); struct rtl_sta_info *sta_entry; if (!priv_sta || !ieee80211_is_data(fc)) return; if (rtl_is_special_data(mac->hw, skb, true)) return; if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) || is_broadcast_ether_addr(ieee80211_get_DA(hdr))) return; if (sta) { /* Check if aggregation has to be enabled for this tid */ sta_entry = (struct rtl_sta_info *) sta->drv_priv; if ((sta->ht_cap.ht_supported) && !(skb->protocol == cpu_to_be16(ETH_P_PAE))) { if (ieee80211_is_data_qos(fc)) { u8 tid = rtl_get_tid(skb); if (_rtl_tx_aggr_check(rtlpriv, sta_entry, tid)) { sta_entry->tids[tid].agg.agg_state = RTL_AGG_PROGRESS; ieee80211_start_tx_ba_session(sta, tid, 5000); } } } } } static void rtl_rate_init(void *ppriv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { } static void rtl_rate_update(void *ppriv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, u32 changed) { } static void *rtl_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { struct rtl_priv *rtlpriv = rtl_priv(hw); return rtlpriv; } static void rtl_rate_free(void *rtlpriv) { return; } static void *rtl_rate_alloc_sta(void *ppriv, struct ieee80211_sta *sta, gfp_t gfp) { struct rtl_priv *rtlpriv = ppriv; struct rtl_rate_priv *rate_priv; rate_priv = kzalloc(sizeof(struct rtl_rate_priv), gfp); if (!rate_priv) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Unable to allocate private rc structure\n"); return NULL; } rtlpriv->rate_priv = rate_priv; return rate_priv; } static void rtl_rate_free_sta(void *rtlpriv, struct ieee80211_sta *sta, void *priv_sta) { struct rtl_rate_priv *rate_priv = priv_sta; kfree(rate_priv); } static struct rate_control_ops rtl_rate_ops = { .module = NULL, .name = "rtl_rc", .alloc = rtl_rate_alloc, .free = rtl_rate_free, .alloc_sta = rtl_rate_alloc_sta, .free_sta = rtl_rate_free_sta, .rate_init = rtl_rate_init, .rate_update = rtl_rate_update, .tx_status = rtl_tx_status, .get_rate = rtl_get_rate, }; int rtl_rate_control_register(void) { return ieee80211_rate_control_register(&rtl_rate_ops); } void rtl_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rtl_rate_ops); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/ps.h0000644000175000017500000000367512026211315022747 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __REALTEK_RTL_PCI_PS_H__ #define __REALTEK_RTL_PCI_PS_H__ #define MAX_SW_LPS_SLEEP_INTV 5 bool rtl_ps_set_rf_state(struct ieee80211_hw *hw, enum rf_pwrstate state_toset, u32 changesource); bool rtl_ps_enable_nic(struct ieee80211_hw *hw); bool rtl_ps_disable_nic(struct ieee80211_hw *hw); void rtl_ips_nic_off(struct ieee80211_hw *hw); void rtl_ips_nic_on(struct ieee80211_hw *hw); void rtl_ips_nic_off_wq_callback(void *data); void rtl_lps_enter(struct ieee80211_hw *hw); void rtl_lps_leave(struct ieee80211_hw *hw); void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len); void rtl_swlps_wq_callback(void *data); void rtl_swlps_rfon_wq_callback(void *data); void rtl_swlps_rf_awake(struct ieee80211_hw *hw); void rtl_swlps_rf_sleep(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/ps.c0000644000175000017500000004012312026211315022727 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include #include "wifi.h" #include "base.h" #include "ps.h" bool rtl_ps_enable_nic(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); /*<1> reset trx ring */ if (rtlhal->interface == INTF_PCI) rtlpriv->intf_ops->reset_trx_ring(hw); if (is_hal_stop(rtlhal)) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Driver is already down!\n"); /*<2> Enable Adapter */ if (rtlpriv->cfg->ops->hw_init(hw)) return 1; RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); /*<3> Enable Interrupt */ rtlpriv->cfg->ops->enable_interrupt(hw); /* */ rtl_watch_dog_timer_callback((unsigned long)hw); return true; } EXPORT_SYMBOL(rtl_ps_enable_nic); bool rtl_ps_disable_nic(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /*<1> Stop all timer */ rtl_deinit_deferred_work(hw); /*<2> Disable Interrupt */ rtlpriv->cfg->ops->disable_interrupt(hw); tasklet_kill(&rtlpriv->works.irq_tasklet); /*<3> Disable Adapter */ rtlpriv->cfg->ops->hw_disable(hw); return true; } EXPORT_SYMBOL(rtl_ps_disable_nic); bool rtl_ps_set_rf_state(struct ieee80211_hw *hw, enum rf_pwrstate state_toset, u32 changesource) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool actionallowed = false; switch (state_toset) { case ERFON: ppsc->rfoff_reason &= (~changesource); if ((changesource == RF_CHANGE_BY_HW) && (ppsc->hwradiooff)) { ppsc->hwradiooff = false; } if (!ppsc->rfoff_reason) { ppsc->rfoff_reason = 0; actionallowed = true; } break; case ERFOFF: if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff) { ppsc->hwradiooff = true; } ppsc->rfoff_reason |= changesource; actionallowed = true; break; case ERFSLEEP: ppsc->rfoff_reason |= changesource; actionallowed = true; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } if (actionallowed) rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset); return actionallowed; } EXPORT_SYMBOL(rtl_ps_set_rf_state); static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); ppsc->swrf_processing = true; if (ppsc->inactive_pwrstate == ERFON && rtlhal->interface == INTF_PCI) { if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) && RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && rtlhal->interface == INTF_PCI) { rtlpriv->intf_ops->disable_aspm(hw); RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); } } rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate, RF_CHANGE_BY_IPS); if (ppsc->inactive_pwrstate == ERFOFF && rtlhal->interface == INTF_PCI) { if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { rtlpriv->intf_ops->enable_aspm(hw); RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); } } ppsc->swrf_processing = false; } void rtl_ips_nic_off_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq); struct ieee80211_hw *hw = rtlworks->hw; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); enum rf_pwrstate rtstate; if (mac->opmode != NL80211_IFTYPE_STATION) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "not station return\n"); return; } if (mac->link_state > MAC80211_NOLINK) return; if (is_hal_stop(rtlhal)) return; if (rtlpriv->sec.being_setkey) return; if (ppsc->inactiveps) { rtstate = ppsc->rfpwr_state; /* *Do not enter IPS in the following conditions: *(1) RF is already OFF or Sleep *(2) swrf_processing (indicates the IPS is still under going) *(3) Connectted (only disconnected can trigger IPS) *(4) IBSS (send Beacon) *(5) AP mode (send Beacon) *(6) monitor mode (rcv packet) */ if (rtstate == ERFON && !ppsc->swrf_processing && (mac->link_state == MAC80211_NOLINK) && !mac->act_scanning) { RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "IPSEnter(): Turn off RF\n"); ppsc->inactive_pwrstate = ERFOFF; ppsc->in_powersavemode = true; /*rtl_pci_reset_trx_ring(hw); */ _rtl_ps_inactive_ps(hw); } } } void rtl_ips_nic_off(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /* *because when link with ap, mac80211 will ask us *to disable nic quickly after scan before linking, *this will cause link failed, so we delay 100ms here */ queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ips_nic_off_wq, MSECS(100)); } void rtl_ips_nic_on(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); enum rf_pwrstate rtstate; unsigned long flags; if (mac->opmode != NL80211_IFTYPE_STATION) return; spin_lock_irqsave(&rtlpriv->locks.ips_lock, flags); if (ppsc->inactiveps) { rtstate = ppsc->rfpwr_state; if (rtstate != ERFON && !ppsc->swrf_processing && ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) { ppsc->inactive_pwrstate = ERFON; ppsc->in_powersavemode = false; _rtl_ps_inactive_ps(hw); } } spin_unlock_irqrestore(&rtlpriv->locks.ips_lock, flags); } /*for FW LPS*/ /* *Determine if we can set Fw into PS mode *in current condition.Return TRUE if it *can enter PS mode. */ static bool rtl_get_fwlps_doze(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u32 ps_timediff; ps_timediff = jiffies_to_msecs(jiffies - ppsc->last_delaylps_stamp_jiffies); if (ps_timediff < 2000) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n"); return false; } if (mac->link_state != MAC80211_LINKED) return false; if (mac->opmode == NL80211_IFTYPE_ADHOC) return false; return true; } /* Change current and default preamble mode.*/ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 rpwm_val, fw_pwrmode; if (mac->opmode == NL80211_IFTYPE_ADHOC) return; if (mac->link_state != MAC80211_LINKED) return; if (ppsc->dot11_psmode == rt_psmode) return; /* Update power save mode configured. */ ppsc->dot11_psmode = rt_psmode; /* * *1. Enter PS mode * Set RPWM to Fw to turn RF off and send H2C fw_pwrmode * cmd to set Fw into PS mode. *2. Leave PS mode * Send H2C fw_pwrmode cmd to Fw to set Fw into Active * mode and set RPWM to turn RF on. */ if ((ppsc->fwctrl_lps) && ppsc->report_linked) { bool fw_current_inps; if (ppsc->dot11_psmode == EACTIVE) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "FW LPS leave ps_mode:%x\n", FW_PS_ACTIVE_MODE); rpwm_val = 0x0C; /* RF on */ fw_pwrmode = FW_PS_ACTIVE_MODE; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, &fw_pwrmode); fw_current_inps = false; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *) (&fw_current_inps)); } else { if (rtl_get_fwlps_doze(hw)) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "FW LPS enter ps_mode:%x\n", ppsc->fwctrl_psmode); rpwm_val = 0x02; /* RF off */ fw_current_inps = true; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *) (&fw_current_inps)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, &ppsc->fwctrl_psmode); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val); } else { /* Reset the power save related parameters. */ ppsc->dot11_psmode = EACTIVE; } } } } /*Enter the leisure power save mode.*/ void rtl_lps_enter(struct ieee80211_hw *hw) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); if (!ppsc->fwctrl_lps) return; if (rtlpriv->sec.being_setkey) return; if (rtlpriv->link_info.busytraffic) return; /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ if (mac->cnt_after_linked < 5) return; if (mac->opmode == NL80211_IFTYPE_ADHOC) return; if (mac->link_state != MAC80211_LINKED) return; mutex_lock(&rtlpriv->locks.ps_mutex); /* Idle for a while if we connect to AP a while ago. */ if (mac->cnt_after_linked >= 2) { if (ppsc->dot11_psmode == EACTIVE) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Enter 802.11 power save mode...\n"); rtl_lps_set_psmode(hw, EAUTOPS); } } mutex_unlock(&rtlpriv->locks.ps_mutex); } /*Leave the leisure power save mode.*/ void rtl_lps_leave(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); mutex_lock(&rtlpriv->locks.ps_mutex); if (ppsc->fwctrl_lps) { if (ppsc->dot11_psmode != EACTIVE) { /*FIX ME */ rtlpriv->cfg->ops->enable_interrupt(hw); if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && rtlhal->interface == INTF_PCI) { rtlpriv->intf_ops->disable_aspm(hw); RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); } RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Busy Traffic,Leave 802.11 power save..\n"); rtl_lps_set_psmode(hw, EACTIVE); } } mutex_unlock(&rtlpriv->locks.ps_mutex); } /* For sw LPS*/ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = data; struct ieee80211_tim_ie *tim_ie; u8 *tim; u8 tim_len; bool u_buffed; bool m_buffed; if (mac->opmode != NL80211_IFTYPE_STATION) return; if (!rtlpriv->psc.swctrl_lps) return; if (rtlpriv->mac80211.link_state != MAC80211_LINKED) return; if (!rtlpriv->psc.sw_ps_enabled) return; if (rtlpriv->psc.fwctrl_lps) return; if (likely(!(hw->conf.flags & IEEE80211_CONF_PS))) return; /* check if this really is a beacon */ if (!ieee80211_is_beacon(hdr->frame_control)) return; /* min. beacon length + FCS_LEN */ if (len <= 40 + FCS_LEN) return; /* and only beacons from the associated BSSID, please */ if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) return; rtlpriv->psc.last_beacon = jiffies; tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); if (!tim) return; if (tim[1] < sizeof(*tim_ie)) return; tim_len = tim[1]; tim_ie = (struct ieee80211_tim_ie *) &tim[2]; if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period)) rtlpriv->psc.dtim_counter = tim_ie->dtim_count; /* Check whenever the PHY can be turned off again. */ /* 1. What about buffered unicast traffic for our AID? */ u_buffed = ieee80211_check_tim(tim_ie, tim_len, rtlpriv->mac80211.assoc_id); /* 2. Maybe the AP wants to send multicast/broadcast data? */ m_buffed = tim_ie->bitmap_ctrl & 0x01; rtlpriv->psc.multi_buffered = m_buffed; /* unicast will process by mac80211 through * set ~IEEE80211_CONF_PS, So we just check * multicast frames here */ if (!m_buffed) { /* back to low-power land. and delay is * prevent null power save frame tx fail */ queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_work, MSECS(5)); } else { RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed); } } void rtl_swlps_rf_awake(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); if (!rtlpriv->psc.swctrl_lps) return; if (mac->link_state != MAC80211_LINKED) return; if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { rtlpriv->intf_ops->disable_aspm(hw); RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); } mutex_lock(&rtlpriv->locks.ps_mutex); rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS); mutex_unlock(&rtlpriv->locks.ps_mutex); } void rtl_swlps_rfon_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq); struct ieee80211_hw *hw = rtlworks->hw; rtl_swlps_rf_awake(hw); } void rtl_swlps_rf_sleep(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 sleep_intv; if (!rtlpriv->psc.sw_ps_enabled) return; if ((rtlpriv->sec.being_setkey) || (mac->opmode == NL80211_IFTYPE_ADHOC)) return; /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5)) return; if (rtlpriv->link_info.busytraffic) return; mutex_lock(&rtlpriv->locks.ps_mutex); rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS); mutex_unlock(&rtlpriv->locks.ps_mutex); if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { rtlpriv->intf_ops->enable_aspm(hw); RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); } /* here is power save alg, when this beacon is DTIM * we will set sleep time to dtim_period * n; * when this beacon is not DTIM, we will set sleep * time to sleep_intv = rtlpriv->psc.dtim_counter or * MAX_SW_LPS_SLEEP_INTV(default set to 5) */ if (rtlpriv->psc.dtim_counter == 0) { if (hw->conf.ps_dtim_period == 1) sleep_intv = hw->conf.ps_dtim_period * 2; else sleep_intv = hw->conf.ps_dtim_period; } else { sleep_intv = rtlpriv->psc.dtim_counter; } if (sleep_intv > MAX_SW_LPS_SLEEP_INTV) sleep_intv = MAX_SW_LPS_SLEEP_INTV; /* this print should always be dtim_conter = 0 & * sleep = dtim_period, that meaons, we should * awake before every dtim */ RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "dtim_counter:%x will sleep :%d beacon_intv\n", rtlpriv->psc.dtim_counter, sleep_intv); /* we tested that 40ms is enough for sw & hw sw delay */ queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq, MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40)); } void rtl_swlps_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, struct rtl_works, ps_work); struct ieee80211_hw *hw = rtlworks->hw; struct rtl_priv *rtlpriv = rtl_priv(hw); bool ps = false; ps = (hw->conf.flags & IEEE80211_CONF_PS); /* we can sleep after ps null send ok */ if (rtlpriv->psc.state_inap) { rtl_swlps_rf_sleep(hw); if (rtlpriv->psc.state && !ps) { rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies - rtlpriv->psc.last_action); } if (ps) rtlpriv->psc.last_slept = jiffies; rtlpriv->psc.last_action = jiffies; rtlpriv->psc.state = ps; } } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/pci.h0000644000175000017500000001614412026211315023073 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_PCI_H__ #define __RTL_PCI_H__ #include /* 1: MSDU packet queue, 2: Rx Command Queue */ #define RTL_PCI_RX_MPDU_QUEUE 0 #define RTL_PCI_RX_CMD_QUEUE 1 #define RTL_PCI_MAX_RX_QUEUE 2 #define RTL_PCI_MAX_RX_COUNT 64 #define RTL_PCI_MAX_TX_QUEUE_COUNT 9 #define RT_TXDESC_NUM 128 #define RT_TXDESC_NUM_BE_QUEUE 256 #define BK_QUEUE 0 #define BE_QUEUE 1 #define VI_QUEUE 2 #define VO_QUEUE 3 #define BEACON_QUEUE 4 #define TXCMD_QUEUE 5 #define MGNT_QUEUE 6 #define HIGH_QUEUE 7 #define HCCA_QUEUE 8 #define RTL_PCI_DEVICE(vend, dev, cfg) \ .vendor = (vend), \ .device = (dev), \ .subvendor = PCI_ANY_ID, \ .subdevice = PCI_ANY_ID,\ .driver_data = (kernel_ulong_t)&(cfg) #define PCI_MAX_BRIDGE_NUMBER 255 #define PCI_MAX_DEVICES 32 #define PCI_MAX_FUNCTION 8 #define PCI_CONF_ADDRESS 0x0CF8 /*PCI Configuration Space Address */ #define PCI_CONF_DATA 0x0CFC /*PCI Configuration Space Data */ #define U1DONTCARE 0xFF #define U2DONTCARE 0xFFFF #define U4DONTCARE 0xFFFFFFFF #define RTL_PCI_8192_DID 0x8192 /*8192 PCI-E */ #define RTL_PCI_8192SE_DID 0x8192 /*8192 SE */ #define RTL_PCI_8174_DID 0x8174 /*8192 SE */ #define RTL_PCI_8173_DID 0x8173 /*8191 SE Crab */ #define RTL_PCI_8172_DID 0x8172 /*8191 SE RE */ #define RTL_PCI_8171_DID 0x8171 /*8191 SE Unicron */ #define RTL_PCI_0045_DID 0x0045 /*8190 PCI for Ceraga */ #define RTL_PCI_0046_DID 0x0046 /*8190 Cardbus for Ceraga */ #define RTL_PCI_0044_DID 0x0044 /*8192e PCIE for Ceraga */ #define RTL_PCI_0047_DID 0x0047 /*8192e Express Card for Ceraga */ #define RTL_PCI_700F_DID 0x700F #define RTL_PCI_701F_DID 0x701F #define RTL_PCI_DLINK_DID 0x3304 #define RTL_PCI_8192CET_DID 0x8191 /*8192ce */ #define RTL_PCI_8192CE_DID 0x8178 /*8192ce */ #define RTL_PCI_8191CE_DID 0x8177 /*8192ce */ #define RTL_PCI_8188CE_DID 0x8176 /*8192ce */ #define RTL_PCI_8192CU_DID 0x8191 /*8192ce */ #define RTL_PCI_8192DE_DID 0x8193 /*8192de */ #define RTL_PCI_8192DE_DID2 0x002B /*92DE*/ /*8192 support 16 pages of IO registers*/ #define RTL_MEM_MAPPED_IO_RANGE_8190PCI 0x1000 #define RTL_MEM_MAPPED_IO_RANGE_8192PCIE 0x4000 #define RTL_MEM_MAPPED_IO_RANGE_8192SE 0x4000 #define RTL_MEM_MAPPED_IO_RANGE_8192CE 0x4000 #define RTL_MEM_MAPPED_IO_RANGE_8192DE 0x4000 #define RTL_PCI_REVISION_ID_8190PCI 0x00 #define RTL_PCI_REVISION_ID_8192PCIE 0x01 #define RTL_PCI_REVISION_ID_8192SE 0x10 #define RTL_PCI_REVISION_ID_8192CE 0x1 #define RTL_PCI_REVISION_ID_8192DE 0x0 #define RTL_DEFAULT_HARDWARE_TYPE HARDWARE_TYPE_RTL8192CE enum pci_bridge_vendor { PCI_BRIDGE_VENDOR_INTEL = 0x0, /*0b'0000,0001 */ PCI_BRIDGE_VENDOR_ATI, /*0b'0000,0010*/ PCI_BRIDGE_VENDOR_AMD, /*0b'0000,0100*/ PCI_BRIDGE_VENDOR_SIS, /*0b'0000,1000*/ PCI_BRIDGE_VENDOR_UNKNOWN, /*0b'0100,0000*/ PCI_BRIDGE_VENDOR_MAX, }; struct rtl_pci_capabilities_header { u8 capability_id; u8 next; }; struct rtl_rx_desc { u32 dword[8]; } __packed; struct rtl_tx_desc { u32 dword[16]; } __packed; struct rtl_tx_cmd_desc { u32 dword[16]; } __packed; struct rtl8192_tx_ring { struct rtl_tx_desc *desc; dma_addr_t dma; unsigned int idx; unsigned int entries; struct sk_buff_head queue; }; struct rtl8192_rx_ring { struct rtl_rx_desc *desc; dma_addr_t dma; unsigned int idx; struct sk_buff *rx_buf[RTL_PCI_MAX_RX_COUNT]; }; struct rtl_pci { struct pci_dev *pdev; bool driver_is_goingto_unload; bool up_first_time; bool first_init; bool being_init_adapter; bool init_ready; /*Tx */ struct rtl8192_tx_ring tx_ring[RTL_PCI_MAX_TX_QUEUE_COUNT]; int txringcount[RTL_PCI_MAX_TX_QUEUE_COUNT]; u32 transmit_config; /*Rx */ struct rtl8192_rx_ring rx_ring[RTL_PCI_MAX_RX_QUEUE]; int rxringcount; u16 rxbuffersize; u32 receive_config; /*irq */ u8 irq_alloc; u32 irq_mask[2]; /*Bcn control register setting */ u32 reg_bcn_ctrl_val; /*ASPM*/ u8 const_pci_aspm; u8 const_amdpci_aspm; u8 const_hwsw_rfoff_d3; u8 const_support_pciaspm; /*pci-e bridge */ u8 const_hostpci_aspm_setting; /*pci-e device */ u8 const_devicepci_aspm_setting; /*If it supports ASPM, Offset[560h] = 0x40, otherwise Offset[560h] = 0x00. */ bool support_aspm; bool support_backdoor; /*QOS & EDCA */ enum acm_method acm_method; u16 shortretry_limit; u16 longretry_limit; }; struct mp_adapter { u8 linkctrl_reg; u8 busnumber; u8 devnumber; u8 funcnumber; u8 pcibridge_busnum; u8 pcibridge_devnum; u8 pcibridge_funcnum; u8 pcibridge_vendor; u16 pcibridge_vendorid; u16 pcibridge_deviceid; u8 num4bytes; u8 pcibridge_pciehdr_offset; u8 pcibridge_linkctrlreg; bool amd_l1_patch; }; struct rtl_pci_priv { struct rtl_pci dev; struct mp_adapter ndis_adapter; struct rtl_led_ctl ledctl; struct bt_coexist_info bt_coexist; }; #define rtl_pcipriv(hw) (((struct rtl_pci_priv *)(rtl_priv(hw))->priv)) #define rtl_pcidev(pcipriv) (&((pcipriv)->dev)) int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw); extern struct rtl_intf_ops rtl_pci_ops; int __devinit rtl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); void rtl_pci_disconnect(struct pci_dev *pdev); int rtl_pci_suspend(struct device *dev); int rtl_pci_resume(struct device *dev); static inline u8 pci_read8_sync(struct rtl_priv *rtlpriv, u32 addr) { return readb((u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } static inline u16 pci_read16_sync(struct rtl_priv *rtlpriv, u32 addr) { return readw((u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } static inline u32 pci_read32_sync(struct rtl_priv *rtlpriv, u32 addr) { return readl((u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } static inline void pci_write8_async(struct rtl_priv *rtlpriv, u32 addr, u8 val) { writeb(val, (u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } static inline void pci_write16_async(struct rtl_priv *rtlpriv, u32 addr, u16 val) { writew(val, (u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } static inline void pci_write32_async(struct rtl_priv *rtlpriv, u32 addr, u32 val) { writel(val, (u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/pci.c0000644000175000017500000015215012026211315023064 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "wifi.h" #include "core.h" #include "pci.h" #include "base.h" #include "ps.h" #include "efuse.h" #include #include static const u16 pcibridge_vendors[PCI_BRIDGE_VENDOR_MAX] = { PCI_VENDOR_ID_INTEL, PCI_VENDOR_ID_ATI, PCI_VENDOR_ID_AMD, PCI_VENDOR_ID_SI }; static const u8 ac_to_hwq[] = { VO_QUEUE, VI_QUEUE, BE_QUEUE, BK_QUEUE }; static u8 _rtl_mac_to_hwqueue(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); __le16 fc = rtl_get_fc(skb); u8 queue_index = skb_get_queue_mapping(skb); if (unlikely(ieee80211_is_beacon(fc))) return BEACON_QUEUE; if (ieee80211_is_mgmt(fc)) return MGNT_QUEUE; if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE) if (ieee80211_is_nullfunc(fc)) return HIGH_QUEUE; return ac_to_hwq[queue_index]; } /* Update PCI dependent default settings*/ static void _rtl_pci_update_default_setting(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 pcibridge_vendor = pcipriv->ndis_adapter.pcibridge_vendor; u8 init_aspm; ppsc->reg_rfps_level = 0; ppsc->support_aspm = false; /*Update PCI ASPM setting */ ppsc->const_amdpci_aspm = rtlpci->const_amdpci_aspm; switch (rtlpci->const_pci_aspm) { case 0: /*No ASPM */ break; case 1: /*ASPM dynamically enabled/disable. */ ppsc->reg_rfps_level |= RT_RF_LPS_LEVEL_ASPM; break; case 2: /*ASPM with Clock Req dynamically enabled/disable. */ ppsc->reg_rfps_level |= (RT_RF_LPS_LEVEL_ASPM | RT_RF_OFF_LEVL_CLK_REQ); break; case 3: /* * Always enable ASPM and Clock Req * from initialization to halt. * */ ppsc->reg_rfps_level &= ~(RT_RF_LPS_LEVEL_ASPM); ppsc->reg_rfps_level |= (RT_RF_PS_LEVEL_ALWAYS_ASPM | RT_RF_OFF_LEVL_CLK_REQ); break; case 4: /* * Always enable ASPM without Clock Req * from initialization to halt. * */ ppsc->reg_rfps_level &= ~(RT_RF_LPS_LEVEL_ASPM | RT_RF_OFF_LEVL_CLK_REQ); ppsc->reg_rfps_level |= RT_RF_PS_LEVEL_ALWAYS_ASPM; break; } ppsc->reg_rfps_level |= RT_RF_OFF_LEVL_HALT_NIC; /*Update Radio OFF setting */ switch (rtlpci->const_hwsw_rfoff_d3) { case 1: if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM) ppsc->reg_rfps_level |= RT_RF_OFF_LEVL_ASPM; break; case 2: if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM) ppsc->reg_rfps_level |= RT_RF_OFF_LEVL_ASPM; ppsc->reg_rfps_level |= RT_RF_OFF_LEVL_HALT_NIC; break; case 3: ppsc->reg_rfps_level |= RT_RF_OFF_LEVL_PCI_D3; break; } /*Set HW definition to determine if it supports ASPM. */ switch (rtlpci->const_support_pciaspm) { case 0:{ /*Not support ASPM. */ bool support_aspm = false; ppsc->support_aspm = support_aspm; break; } case 1:{ /*Support ASPM. */ bool support_aspm = true; bool support_backdoor = true; ppsc->support_aspm = support_aspm; /*if (priv->oem_id == RT_CID_TOSHIBA && !priv->ndis_adapter.amd_l1_patch) support_backdoor = false; */ ppsc->support_backdoor = support_backdoor; break; } case 2: /*ASPM value set by chipset. */ if (pcibridge_vendor == PCI_BRIDGE_VENDOR_INTEL) { bool support_aspm = true; ppsc->support_aspm = support_aspm; } break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } /* toshiba aspm issue, toshiba will set aspm selfly * so we should not set aspm in driver */ pci_read_config_byte(rtlpci->pdev, 0x80, &init_aspm); if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8192SE && init_aspm == 0x43) ppsc->support_aspm = false; } static bool _rtl_pci_platform_switch_device_pci_aspm( struct ieee80211_hw *hw, u8 value) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (rtlhal->hw_type != HARDWARE_TYPE_RTL8192SE) value |= 0x40; pci_write_config_byte(rtlpci->pdev, 0x80, value); return false; } /*When we set 0x01 to enable clk request. Set 0x0 to disable clk req.*/ static void _rtl_pci_switch_clk_req(struct ieee80211_hw *hw, u8 value) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); pci_write_config_byte(rtlpci->pdev, 0x81, value); if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE) udelay(100); } /*Disable RTL8192SE ASPM & Disable Pci Bridge ASPM*/ static void rtl_pci_disable_aspm(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 pcibridge_vendor = pcipriv->ndis_adapter.pcibridge_vendor; u8 num4bytes = pcipriv->ndis_adapter.num4bytes; /*Retrieve original configuration settings. */ u8 linkctrl_reg = pcipriv->ndis_adapter.linkctrl_reg; u16 pcibridge_linkctrlreg = pcipriv->ndis_adapter. pcibridge_linkctrlreg; u16 aspmlevel = 0; u8 tmp_u1b = 0; if (!ppsc->support_aspm) return; if (pcibridge_vendor == PCI_BRIDGE_VENDOR_UNKNOWN) { RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "PCI(Bridge) UNKNOWN\n"); return; } if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_CLK_REQ) { RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_CLK_REQ); _rtl_pci_switch_clk_req(hw, 0x0); } /*for promising device will in L0 state after an I/O. */ pci_read_config_byte(rtlpci->pdev, 0x80, &tmp_u1b); /*Set corresponding value. */ aspmlevel |= BIT(0) | BIT(1); linkctrl_reg &= ~aspmlevel; pcibridge_linkctrlreg &= ~(BIT(0) | BIT(1)); _rtl_pci_platform_switch_device_pci_aspm(hw, linkctrl_reg); udelay(50); /*4 Disable Pci Bridge ASPM */ pci_write_config_byte(rtlpci->pdev, (num4bytes << 2), pcibridge_linkctrlreg); udelay(50); } /* *Enable RTL8192SE ASPM & Enable Pci Bridge ASPM for *power saving We should follow the sequence to enable *RTL8192SE first then enable Pci Bridge ASPM *or the system will show bluescreen. */ static void rtl_pci_enable_aspm(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 pcibridge_busnum = pcipriv->ndis_adapter.pcibridge_busnum; u8 pcibridge_devnum = pcipriv->ndis_adapter.pcibridge_devnum; u8 pcibridge_funcnum = pcipriv->ndis_adapter.pcibridge_funcnum; u8 pcibridge_vendor = pcipriv->ndis_adapter.pcibridge_vendor; u8 num4bytes = pcipriv->ndis_adapter.num4bytes; u16 aspmlevel; u8 u_pcibridge_aspmsetting; u8 u_device_aspmsetting; if (!ppsc->support_aspm) return; if (pcibridge_vendor == PCI_BRIDGE_VENDOR_UNKNOWN) { RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "PCI(Bridge) UNKNOWN\n"); return; } /*4 Enable Pci Bridge ASPM */ u_pcibridge_aspmsetting = pcipriv->ndis_adapter.pcibridge_linkctrlreg | rtlpci->const_hostpci_aspm_setting; if (pcibridge_vendor == PCI_BRIDGE_VENDOR_INTEL) u_pcibridge_aspmsetting &= ~BIT(0); pci_write_config_byte(rtlpci->pdev, (num4bytes << 2), u_pcibridge_aspmsetting); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PlatformEnableASPM():PciBridge busnumber[%x], DevNumbe[%x], funcnumber[%x], Write reg[%x] = %x\n", pcibridge_busnum, pcibridge_devnum, pcibridge_funcnum, (pcipriv->ndis_adapter.pcibridge_pciehdr_offset + 0x10), u_pcibridge_aspmsetting); udelay(50); /*Get ASPM level (with/without Clock Req) */ aspmlevel = rtlpci->const_devicepci_aspm_setting; u_device_aspmsetting = pcipriv->ndis_adapter.linkctrl_reg; /*_rtl_pci_platform_switch_device_pci_aspm(dev,*/ /*(priv->ndis_adapter.linkctrl_reg | ASPMLevel)); */ u_device_aspmsetting |= aspmlevel; _rtl_pci_platform_switch_device_pci_aspm(hw, u_device_aspmsetting); if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_CLK_REQ) { _rtl_pci_switch_clk_req(hw, (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_CLK_REQ) ? 1 : 0); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_CLK_REQ); } udelay(100); } static bool rtl_pci_get_amd_l1_patch(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); bool status = false; u8 offset_e0; unsigned offset_e4; pci_write_config_byte(rtlpci->pdev, 0xe0, 0xa0); pci_read_config_byte(rtlpci->pdev, 0xe0, &offset_e0); if (offset_e0 == 0xA0) { pci_read_config_dword(rtlpci->pdev, 0xe4, &offset_e4); if (offset_e4 & BIT(23)) status = true; } return status; } static void rtl_pci_get_linkcontrol_field(struct ieee80211_hw *hw) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_pci *rtlpci = rtl_pcidev(pcipriv); u8 capabilityoffset = pcipriv->ndis_adapter.pcibridge_pciehdr_offset; u8 linkctrl_reg; u8 num4bbytes; num4bbytes = (capabilityoffset + 0x10) / 4; /*Read Link Control Register */ pci_read_config_byte(rtlpci->pdev, (num4bbytes << 2), &linkctrl_reg); pcipriv->ndis_adapter.pcibridge_linkctrlreg = linkctrl_reg; } static void rtl_pci_parse_configuration(struct pci_dev *pdev, struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); u8 tmp; u16 linkctrl_reg; /*Link Control Register */ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &linkctrl_reg); pcipriv->ndis_adapter.linkctrl_reg = (u8)linkctrl_reg; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Link Control Register =%x\n", pcipriv->ndis_adapter.linkctrl_reg); pci_read_config_byte(pdev, 0x98, &tmp); tmp |= BIT(4); pci_write_config_byte(pdev, 0x98, tmp); tmp = 0x17; pci_write_config_byte(pdev, 0x70f, tmp); } static void rtl_pci_init_aspm(struct ieee80211_hw *hw) { struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); _rtl_pci_update_default_setting(hw); if (ppsc->reg_rfps_level & RT_RF_PS_LEVEL_ALWAYS_ASPM) { /*Always enable ASPM & Clock Req. */ rtl_pci_enable_aspm(hw); RT_SET_PS_LEVEL(ppsc, RT_RF_PS_LEVEL_ALWAYS_ASPM); } } static void _rtl_pci_io_handler_init(struct device *dev, struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->io.dev = dev; rtlpriv->io.write8_async = pci_write8_async; rtlpriv->io.write16_async = pci_write16_async; rtlpriv->io.write32_async = pci_write32_async; rtlpriv->io.read8_sync = pci_read8_sync; rtlpriv->io.read16_sync = pci_read16_sync; rtlpriv->io.read32_sync = pci_read32_sync; } static void _rtl_pci_io_handler_release(struct ieee80211_hw *hw) { } static bool _rtl_update_earlymode_info(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_tcb_desc *tcb_desc, u8 tid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 additionlen = FCS_LEN; struct sk_buff *next_skb; /* here open is 4, wep/tkip is 8, aes is 12*/ if (info->control.hw_key) additionlen += info->control.hw_key->icv_len; /* The most skb num is 6 */ tcb_desc->empkt_num = 0; spin_lock_bh(&rtlpriv->locks.waitq_lock); skb_queue_walk(&rtlpriv->mac80211.skb_waitq[tid], next_skb) { struct ieee80211_tx_info *next_info; next_info = IEEE80211_SKB_CB(next_skb); if (next_info->flags & IEEE80211_TX_CTL_AMPDU) { tcb_desc->empkt_len[tcb_desc->empkt_num] = next_skb->len + additionlen; tcb_desc->empkt_num++; } else { break; } if (skb_queue_is_last(&rtlpriv->mac80211.skb_waitq[tid], next_skb)) break; if (tcb_desc->empkt_num >= 5) break; } spin_unlock_bh(&rtlpriv->locks.waitq_lock); return true; } /* just for early mode now */ static void _rtl_pci_tx_chk_waitq(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct sk_buff *skb = NULL; struct ieee80211_tx_info *info = NULL; int tid; if (!rtlpriv->rtlhal.earlymode_enable) return; /* we juse use em for BE/BK/VI/VO */ for (tid = 7; tid >= 0; tid--) { u8 hw_queue = ac_to_hwq[rtl_tid_to_ac(tid)]; struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue]; while (!mac->act_scanning && rtlpriv->psc.rfpwr_state == ERFON) { struct rtl_tcb_desc tcb_desc; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); spin_lock_bh(&rtlpriv->locks.waitq_lock); if (!skb_queue_empty(&mac->skb_waitq[tid]) && (ring->entries - skb_queue_len(&ring->queue) > 5)) { skb = skb_dequeue(&mac->skb_waitq[tid]); } else { spin_unlock_bh(&rtlpriv->locks.waitq_lock); break; } spin_unlock_bh(&rtlpriv->locks.waitq_lock); /* Some macaddr can't do early mode. like * multicast/broadcast/no_qos data */ info = IEEE80211_SKB_CB(skb); if (info->flags & IEEE80211_TX_CTL_AMPDU) _rtl_update_earlymode_info(hw, skb, &tcb_desc, tid); rtlpriv->intf_ops->adapter_tx(hw, NULL, skb, &tcb_desc); } } } static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[prio]; while (skb_queue_len(&ring->queue)) { struct rtl_tx_desc *entry = &ring->desc[ring->idx]; struct sk_buff *skb; struct ieee80211_tx_info *info; __le16 fc; u8 tid; u8 own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) entry, true, HW_DESC_OWN); /* *beacon packet will only use the first *descriptor defautly,and the own may not *be cleared by the hardware */ if (own) return; ring->idx = (ring->idx + 1) % ring->entries; skb = __skb_dequeue(&ring->queue); pci_unmap_single(rtlpci->pdev, rtlpriv->cfg->ops-> get_desc((u8 *) entry, true, HW_DESC_TXBUFF_ADDR), skb->len, PCI_DMA_TODEVICE); /* remove early mode header */ if (rtlpriv->rtlhal.earlymode_enable) skb_pull(skb, EM_HDR_LEN); RT_TRACE(rtlpriv, (COMP_INTR | COMP_SEND), DBG_TRACE, "new ring->idx:%d, free: skb_queue_len:%d, free: seq:%x\n", ring->idx, skb_queue_len(&ring->queue), *(u16 *) (skb->data + 22)); if (prio == TXCMD_QUEUE) { dev_kfree_skb(skb); goto tx_status_ok; } /* for sw LPS, just after NULL skb send out, we can * sure AP kown we are sleeped, our we should not let * rf to sleep*/ fc = rtl_get_fc(skb); if (ieee80211_is_nullfunc(fc)) { if (ieee80211_has_pm(fc)) { rtlpriv->mac80211.offchan_delay = true; rtlpriv->psc.state_inap = true; } else { rtlpriv->psc.state_inap = false; } } /* update tid tx pkt num */ tid = rtl_get_tid(skb); if (tid <= 7) rtlpriv->link_info.tidtx_inperiod[tid]++; info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); info->flags |= IEEE80211_TX_STAT_ACK; /*info->status.rates[0].count = 1; */ ieee80211_tx_status_irqsafe(hw, skb); if ((ring->entries - skb_queue_len(&ring->queue)) == 2) { RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD, "more desc left, wake skb_queue@%d, ring->idx = %d, skb_queue_len = 0x%d\n", prio, ring->idx, skb_queue_len(&ring->queue)); ieee80211_wake_queue(hw, skb_get_queue_mapping (skb)); } tx_status_ok: skb = NULL; } if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || (rtlpriv->link_info.num_rx_inperiod > 2)) { schedule_work(&rtlpriv->works.lps_leave_work); } } static void _rtl_receive_one(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_rx_status rx_status) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); __le16 fc = rtl_get_fc(skb); bool unicast = false; struct sk_buff *uskb = NULL; u8 *pdata; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); if (is_broadcast_ether_addr(hdr->addr1)) { ;/*TODO*/ } else if (is_multicast_ether_addr(hdr->addr1)) { ;/*TODO*/ } else { unicast = true; rtlpriv->stats.rxbytesunicast += skb->len; } rtl_is_special_data(hw, skb, false); if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); if (unicast) rtlpriv->link_info.num_rx_inperiod++; } /* for sw lps */ rtl_swlps_beacon(hw, (void *)skb->data, skb->len); rtl_recognize_peer(hw, (void *)skb->data, skb->len); if ((rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP) && (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) && (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc))) return; if (unlikely(!rtl_action_proc(hw, skb, false))) return; uskb = dev_alloc_skb(skb->len + 128); if (!uskb) return; /* exit if allocation failed */ memcpy(IEEE80211_SKB_RXCB(uskb), &rx_status, sizeof(rx_status)); pdata = (u8 *)skb_put(uskb, skb->len); memcpy(pdata, skb->data, skb->len); ieee80211_rx_irqsafe(hw, uskb); } static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); int rx_queue_idx = RTL_PCI_RX_MPDU_QUEUE; struct ieee80211_rx_status rx_status = { 0 }; unsigned int count = rtlpci->rxringcount; u8 own; u8 tmp_one; u32 bufferaddress; struct rtl_stats stats = { .signal = 0, .noise = -98, .rate = 0, }; int index = rtlpci->rx_ring[rx_queue_idx].idx; /*RX NORMAL PKT */ while (count--) { /*rx descriptor */ struct rtl_rx_desc *pdesc = &rtlpci->rx_ring[rx_queue_idx].desc[ index]; /*rx pkt */ struct sk_buff *skb = rtlpci->rx_ring[rx_queue_idx].rx_buf[ index]; struct sk_buff *new_skb = NULL; own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc, false, HW_DESC_OWN); /*wait data to be filled by hardware */ if (own) break; rtlpriv->cfg->ops->query_rx_desc(hw, &stats, &rx_status, (u8 *) pdesc, skb); if (stats.crc || stats.hwerror) goto done; new_skb = dev_alloc_skb(rtlpci->rxbuffersize); if (unlikely(!new_skb)) { RT_TRACE(rtlpriv, (COMP_INTR | COMP_RECV), DBG_DMESG, "can't alloc skb for rx\n"); goto done; } pci_unmap_single(rtlpci->pdev, *((dma_addr_t *) skb->cb), rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE); skb_put(skb, rtlpriv->cfg->ops->get_desc((u8 *) pdesc, false, HW_DESC_RXPKT_LEN)); skb_reserve(skb, stats.rx_drvinfo_size + stats.rx_bufshift); /* * NOTICE This can not be use for mac80211, * this is done in mac80211 code, * if you done here sec DHCP will fail * skb_trim(skb, skb->len - 4); */ _rtl_receive_one(hw, skb, rx_status); if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || (rtlpriv->link_info.num_rx_inperiod > 2)) { schedule_work(&rtlpriv->works.lps_leave_work); } dev_kfree_skb_any(skb); skb = new_skb; rtlpci->rx_ring[rx_queue_idx].rx_buf[index] = skb; *((dma_addr_t *) skb->cb) = pci_map_single(rtlpci->pdev, skb_tail_pointer(skb), rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE); done: bufferaddress = (*((dma_addr_t *)skb->cb)); tmp_one = 1; rtlpriv->cfg->ops->set_desc((u8 *) pdesc, false, HW_DESC_RXBUFF_ADDR, (u8 *)&bufferaddress); rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXPKT_LEN, (u8 *)&rtlpci->rxbuffersize); if (index == rtlpci->rxringcount - 1) rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXERO, &tmp_one); rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXOWN, &tmp_one); index = (index + 1) % rtlpci->rxringcount; } rtlpci->rx_ring[rx_queue_idx].idx = index; } static irqreturn_t _rtl_pci_interrupt(int irq, void *dev_id) { struct ieee80211_hw *hw = dev_id; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); unsigned long flags; u32 inta = 0; u32 intb = 0; irqreturn_t ret = IRQ_HANDLED; spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); /*read ISR: 4/8bytes */ rtlpriv->cfg->ops->interrupt_recognized(hw, &inta, &intb); /*Shared IRQ or HW disappared */ if (!inta || inta == 0xffff) { ret = IRQ_NONE; goto done; } /*<1> beacon related */ if (inta & rtlpriv->cfg->maps[RTL_IMR_TBDOK]) { RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "beacon ok interrupt!\n"); } if (unlikely(inta & rtlpriv->cfg->maps[RTL_IMR_TBDER])) { RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "beacon err interrupt!\n"); } if (inta & rtlpriv->cfg->maps[RTL_IMR_BDOK]) { RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "beacon interrupt!\n"); } if (inta & rtlpriv->cfg->maps[RTL_IMR_BcnInt]) { RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "prepare beacon for interrupt!\n"); tasklet_schedule(&rtlpriv->works.irq_prepare_bcn_tasklet); } /*<3> Tx related */ if (unlikely(inta & rtlpriv->cfg->maps[RTL_IMR_TXFOVW])) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "IMR_TXFOVW!\n"); if (inta & rtlpriv->cfg->maps[RTL_IMR_MGNTDOK]) { RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "Manage ok interrupt!\n"); _rtl_pci_tx_isr(hw, MGNT_QUEUE); } if (inta & rtlpriv->cfg->maps[RTL_IMR_HIGHDOK]) { RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "HIGH_QUEUE ok interrupt!\n"); _rtl_pci_tx_isr(hw, HIGH_QUEUE); } if (inta & rtlpriv->cfg->maps[RTL_IMR_BKDOK]) { rtlpriv->link_info.num_tx_inperiod++; RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "BK Tx OK interrupt!\n"); _rtl_pci_tx_isr(hw, BK_QUEUE); } if (inta & rtlpriv->cfg->maps[RTL_IMR_BEDOK]) { rtlpriv->link_info.num_tx_inperiod++; RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "BE TX OK interrupt!\n"); _rtl_pci_tx_isr(hw, BE_QUEUE); } if (inta & rtlpriv->cfg->maps[RTL_IMR_VIDOK]) { rtlpriv->link_info.num_tx_inperiod++; RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "VI TX OK interrupt!\n"); _rtl_pci_tx_isr(hw, VI_QUEUE); } if (inta & rtlpriv->cfg->maps[RTL_IMR_VODOK]) { rtlpriv->link_info.num_tx_inperiod++; RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "Vo TX OK interrupt!\n"); _rtl_pci_tx_isr(hw, VO_QUEUE); } if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE) { if (inta & rtlpriv->cfg->maps[RTL_IMR_COMDOK]) { rtlpriv->link_info.num_tx_inperiod++; RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "CMD TX OK interrupt!\n"); _rtl_pci_tx_isr(hw, TXCMD_QUEUE); } } /*<2> Rx related */ if (inta & rtlpriv->cfg->maps[RTL_IMR_ROK]) { RT_TRACE(rtlpriv, COMP_INTR, DBG_TRACE, "Rx ok interrupt!\n"); _rtl_pci_rx_interrupt(hw); } if (unlikely(inta & rtlpriv->cfg->maps[RTL_IMR_RDU])) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "rx descriptor unavailable!\n"); _rtl_pci_rx_interrupt(hw); } if (unlikely(inta & rtlpriv->cfg->maps[RTL_IMR_RXFOVW])) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "rx overflow !\n"); _rtl_pci_rx_interrupt(hw); } if (rtlpriv->rtlhal.earlymode_enable) tasklet_schedule(&rtlpriv->works.irq_tasklet); done: spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); return ret; } static void _rtl_pci_irq_tasklet(struct ieee80211_hw *hw) { _rtl_pci_tx_chk_waitq(hw); } static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl8192_tx_ring *ring = NULL; struct ieee80211_hdr *hdr = NULL; struct ieee80211_tx_info *info = NULL; struct sk_buff *pskb = NULL; struct rtl_tx_desc *pdesc = NULL; struct rtl_tcb_desc tcb_desc; u8 temp_one = 1; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); ring = &rtlpci->tx_ring[BEACON_QUEUE]; pskb = __skb_dequeue(&ring->queue); if (pskb) { struct rtl_tx_desc *entry = &ring->desc[ring->idx]; pci_unmap_single(rtlpci->pdev, rtlpriv->cfg->ops->get_desc( (u8 *) entry, true, HW_DESC_TXBUFF_ADDR), pskb->len, PCI_DMA_TODEVICE); kfree_skb(pskb); } /*NB: the beacon data buffer must be 32-bit aligned. */ pskb = ieee80211_beacon_get(hw, mac->vif); if (pskb == NULL) return; hdr = rtl_get_hdr(pskb); info = IEEE80211_SKB_CB(pskb); pdesc = &ring->desc[0]; rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *) pdesc, info, NULL, pskb, BEACON_QUEUE, &tcb_desc); __skb_queue_tail(&ring->queue, pskb); rtlpriv->cfg->ops->set_desc((u8 *) pdesc, true, HW_DESC_OWN, &temp_one); return; } static void rtl_lps_leave_work_callback(struct work_struct *work) { struct rtl_works *rtlworks = container_of(work, struct rtl_works, lps_leave_work); struct ieee80211_hw *hw = rtlworks->hw; rtl_lps_leave(hw); } static void _rtl_pci_init_trx_var(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 i; for (i = 0; i < RTL_PCI_MAX_TX_QUEUE_COUNT; i++) rtlpci->txringcount[i] = RT_TXDESC_NUM; /* *we just alloc 2 desc for beacon queue, *because we just need first desc in hw beacon. */ rtlpci->txringcount[BEACON_QUEUE] = 2; /* *BE queue need more descriptor for performance *consideration or, No more tx desc will happen, *and may cause mac80211 mem leakage. */ rtlpci->txringcount[BE_QUEUE] = RT_TXDESC_NUM_BE_QUEUE; rtlpci->rxbuffersize = 9100; /*2048/1024; */ rtlpci->rxringcount = RTL_PCI_MAX_RX_COUNT; /*64; */ } static void _rtl_pci_init_struct(struct ieee80211_hw *hw, struct pci_dev *pdev) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); rtlpci->up_first_time = true; rtlpci->being_init_adapter = false; rtlhal->hw = hw; rtlpci->pdev = pdev; /*Tx/Rx related var */ _rtl_pci_init_trx_var(hw); /*IBSS*/ mac->beacon_interval = 100; /*AMPDU*/ mac->min_space_cfg = 0; mac->max_mss_density = 0; /*set sane AMPDU defaults */ mac->current_ampdu_density = 7; mac->current_ampdu_factor = 3; /*QOS*/ rtlpci->acm_method = eAcmWay2_SW; /*task */ tasklet_init(&rtlpriv->works.irq_tasklet, (void (*)(unsigned long))_rtl_pci_irq_tasklet, (unsigned long)hw); tasklet_init(&rtlpriv->works.irq_prepare_bcn_tasklet, (void (*)(unsigned long))_rtl_pci_prepare_bcn_tasklet, (unsigned long)hw); INIT_WORK(&rtlpriv->works.lps_leave_work, rtl_lps_leave_work_callback); } static int _rtl_pci_init_tx_ring(struct ieee80211_hw *hw, unsigned int prio, unsigned int entries) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_tx_desc *ring; dma_addr_t dma; u32 nextdescaddress; int i; ring = pci_alloc_consistent(rtlpci->pdev, sizeof(*ring) * entries, &dma); if (!ring || (unsigned long)ring & 0xFF) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Cannot allocate TX ring (prio = %d)\n", prio); return -ENOMEM; } memset(ring, 0, sizeof(*ring) * entries); rtlpci->tx_ring[prio].desc = ring; rtlpci->tx_ring[prio].dma = dma; rtlpci->tx_ring[prio].idx = 0; rtlpci->tx_ring[prio].entries = entries; skb_queue_head_init(&rtlpci->tx_ring[prio].queue); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "queue:%d, ring_addr:%p\n", prio, ring); for (i = 0; i < entries; i++) { nextdescaddress = (u32) dma + ((i + 1) % entries) * sizeof(*ring); rtlpriv->cfg->ops->set_desc((u8 *)&(ring[i]), true, HW_DESC_TX_NEXTDESC_ADDR, (u8 *)&nextdescaddress); } return 0; } static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_rx_desc *entry = NULL; int i, rx_queue_idx; u8 tmp_one = 1; /* *rx_queue_idx 0:RX_MPDU_QUEUE *rx_queue_idx 1:RX_CMD_QUEUE */ for (rx_queue_idx = 0; rx_queue_idx < RTL_PCI_MAX_RX_QUEUE; rx_queue_idx++) { rtlpci->rx_ring[rx_queue_idx].desc = pci_alloc_consistent(rtlpci->pdev, sizeof(*rtlpci->rx_ring[rx_queue_idx]. desc) * rtlpci->rxringcount, &rtlpci->rx_ring[rx_queue_idx].dma); if (!rtlpci->rx_ring[rx_queue_idx].desc || (unsigned long)rtlpci->rx_ring[rx_queue_idx].desc & 0xFF) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Cannot allocate RX ring\n"); return -ENOMEM; } memset(rtlpci->rx_ring[rx_queue_idx].desc, 0, sizeof(*rtlpci->rx_ring[rx_queue_idx].desc) * rtlpci->rxringcount); rtlpci->rx_ring[rx_queue_idx].idx = 0; /* If amsdu_8k is disabled, set buffersize to 4096. This * change will reduce memory fragmentation. */ if (rtlpci->rxbuffersize > 4096 && rtlpriv->rtlhal.disable_amsdu_8k) rtlpci->rxbuffersize = 4096; for (i = 0; i < rtlpci->rxringcount; i++) { struct sk_buff *skb = dev_alloc_skb(rtlpci->rxbuffersize); u32 bufferaddress; if (!skb) return 0; kmemleak_not_leak(skb); entry = &rtlpci->rx_ring[rx_queue_idx].desc[i]; /*skb->dev = dev; */ rtlpci->rx_ring[rx_queue_idx].rx_buf[i] = skb; /* *just set skb->cb to mapping addr *for pci_unmap_single use */ *((dma_addr_t *) skb->cb) = pci_map_single(rtlpci->pdev, skb_tail_pointer(skb), rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE); bufferaddress = (*((dma_addr_t *)skb->cb)); rtlpriv->cfg->ops->set_desc((u8 *)entry, false, HW_DESC_RXBUFF_ADDR, (u8 *)&bufferaddress); rtlpriv->cfg->ops->set_desc((u8 *)entry, false, HW_DESC_RXPKT_LEN, (u8 *)&rtlpci-> rxbuffersize); rtlpriv->cfg->ops->set_desc((u8 *) entry, false, HW_DESC_RXOWN, &tmp_one); } rtlpriv->cfg->ops->set_desc((u8 *) entry, false, HW_DESC_RXERO, &tmp_one); } return 0; } static void _rtl_pci_free_tx_ring(struct ieee80211_hw *hw, unsigned int prio) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[prio]; while (skb_queue_len(&ring->queue)) { struct rtl_tx_desc *entry = &ring->desc[ring->idx]; struct sk_buff *skb = __skb_dequeue(&ring->queue); pci_unmap_single(rtlpci->pdev, rtlpriv->cfg-> ops->get_desc((u8 *) entry, true, HW_DESC_TXBUFF_ADDR), skb->len, PCI_DMA_TODEVICE); kfree_skb(skb); ring->idx = (ring->idx + 1) % ring->entries; } if (ring->desc) { pci_free_consistent(rtlpci->pdev, sizeof(*ring->desc) * ring->entries, ring->desc, ring->dma); ring->desc = NULL; } } static void _rtl_pci_free_rx_ring(struct rtl_pci *rtlpci) { int i, rx_queue_idx; /*rx_queue_idx 0:RX_MPDU_QUEUE */ /*rx_queue_idx 1:RX_CMD_QUEUE */ for (rx_queue_idx = 0; rx_queue_idx < RTL_PCI_MAX_RX_QUEUE; rx_queue_idx++) { for (i = 0; i < rtlpci->rxringcount; i++) { struct sk_buff *skb = rtlpci->rx_ring[rx_queue_idx].rx_buf[i]; if (!skb) continue; pci_unmap_single(rtlpci->pdev, *((dma_addr_t *) skb->cb), rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE); kfree_skb(skb); } if (rtlpci->rx_ring[rx_queue_idx].desc) { pci_free_consistent(rtlpci->pdev, sizeof(*rtlpci->rx_ring[rx_queue_idx]. desc) * rtlpci->rxringcount, rtlpci->rx_ring[rx_queue_idx].desc, rtlpci->rx_ring[rx_queue_idx].dma); rtlpci->rx_ring[rx_queue_idx].desc = NULL; } } } static int _rtl_pci_init_trx_ring(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); int ret; int i; ret = _rtl_pci_init_rx_ring(hw); if (ret) return ret; for (i = 0; i < RTL_PCI_MAX_TX_QUEUE_COUNT; i++) { ret = _rtl_pci_init_tx_ring(hw, i, rtlpci->txringcount[i]); if (ret) goto err_free_rings; } return 0; err_free_rings: _rtl_pci_free_rx_ring(rtlpci); for (i = 0; i < RTL_PCI_MAX_TX_QUEUE_COUNT; i++) if (rtlpci->tx_ring[i].desc) _rtl_pci_free_tx_ring(hw, i); return 1; } static int _rtl_pci_deinit_trx_ring(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u32 i; /*free rx rings */ _rtl_pci_free_rx_ring(rtlpci); /*free tx rings */ for (i = 0; i < RTL_PCI_MAX_TX_QUEUE_COUNT; i++) _rtl_pci_free_tx_ring(hw, i); return 0; } int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); int i, rx_queue_idx; unsigned long flags; u8 tmp_one = 1; /*rx_queue_idx 0:RX_MPDU_QUEUE */ /*rx_queue_idx 1:RX_CMD_QUEUE */ for (rx_queue_idx = 0; rx_queue_idx < RTL_PCI_MAX_RX_QUEUE; rx_queue_idx++) { /* *force the rx_ring[RX_MPDU_QUEUE/ *RX_CMD_QUEUE].idx to the first one */ if (rtlpci->rx_ring[rx_queue_idx].desc) { struct rtl_rx_desc *entry = NULL; for (i = 0; i < rtlpci->rxringcount; i++) { entry = &rtlpci->rx_ring[rx_queue_idx].desc[i]; rtlpriv->cfg->ops->set_desc((u8 *) entry, false, HW_DESC_RXOWN, &tmp_one); } rtlpci->rx_ring[rx_queue_idx].idx = 0; } } /* *after reset, release previous pending packet, *and force the tx idx to the first one */ for (i = 0; i < RTL_PCI_MAX_TX_QUEUE_COUNT; i++) { if (rtlpci->tx_ring[i].desc) { struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[i]; while (skb_queue_len(&ring->queue)) { struct rtl_tx_desc *entry; struct sk_buff *skb; spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); entry = &ring->desc[ring->idx]; skb = __skb_dequeue(&ring->queue); pci_unmap_single(rtlpci->pdev, rtlpriv->cfg->ops-> get_desc((u8 *) entry, true, HW_DESC_TXBUFF_ADDR), skb->len, PCI_DMA_TODEVICE); ring->idx = (ring->idx + 1) % ring->entries; spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); kfree_skb(skb); } ring->idx = 0; } } return 0; } static bool rtl_pci_tx_chk_waitq_insert(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_sta_info *sta_entry = NULL; u8 tid = rtl_get_tid(skb); if (!sta) return false; sta_entry = (struct rtl_sta_info *)sta->drv_priv; if (!rtlpriv->rtlhal.earlymode_enable) return false; if (sta_entry->tids[tid].agg.agg_state != RTL_AGG_OPERATIONAL) return false; if (_rtl_mac_to_hwqueue(hw, skb) > VO_QUEUE) return false; if (tid > 7) return false; /* maybe every tid should be checked */ if (!rtlpriv->link_info.higher_busytxtraffic[tid]) return false; spin_lock_bh(&rtlpriv->locks.waitq_lock); skb_queue_tail(&rtlpriv->mac80211.skb_waitq[tid], skb); spin_unlock_bh(&rtlpriv->locks.waitq_lock); return true; } static int rtl_pci_tx(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb, struct rtl_tcb_desc *ptcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_sta_info *sta_entry = NULL; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rtl8192_tx_ring *ring; struct rtl_tx_desc *pdesc; u8 idx; u8 hw_queue = _rtl_mac_to_hwqueue(hw, skb); unsigned long flags; struct ieee80211_hdr *hdr = rtl_get_hdr(skb); __le16 fc = rtl_get_fc(skb); u8 *pda_addr = hdr->addr1; struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); /*ssn */ u8 tid = 0; u16 seq_number = 0; u8 own; u8 temp_one = 1; if (ieee80211_is_auth(fc)) { RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "MAC80211_LINKING\n"); rtl_ips_nic_on(hw); } if (rtlpriv->psc.sw_ps_enabled) { if (ieee80211_is_data(fc) && !ieee80211_is_nullfunc(fc) && !ieee80211_has_pm(fc)) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); } rtl_action_proc(hw, skb, true); if (is_multicast_ether_addr(pda_addr)) rtlpriv->stats.txbytesmulticast += skb->len; else if (is_broadcast_ether_addr(pda_addr)) rtlpriv->stats.txbytesbroadcast += skb->len; else rtlpriv->stats.txbytesunicast += skb->len; spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); ring = &rtlpci->tx_ring[hw_queue]; if (hw_queue != BEACON_QUEUE) idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; else idx = 0; pdesc = &ring->desc[idx]; own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc, true, HW_DESC_OWN); if ((own == 1) && (hw_queue != BEACON_QUEUE)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "No more TX desc@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n", hw_queue, ring->idx, idx, skb_queue_len(&ring->queue)); spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); return skb->len; } if (ieee80211_is_data_qos(fc)) { tid = rtl_get_tid(skb); if (sta) { sta_entry = (struct rtl_sta_info *)sta->drv_priv; seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; seq_number += 1; if (!ieee80211_has_morefrags(hdr->frame_control)) sta_entry->tids[tid].seq_number = seq_number; } } if (ieee80211_is_data(fc)) rtlpriv->cfg->ops->led_control(hw, LED_CTL_TX); rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, info, sta, skb, hw_queue, ptcb_desc); __skb_queue_tail(&ring->queue, skb); rtlpriv->cfg->ops->set_desc((u8 *)pdesc, true, HW_DESC_OWN, &temp_one); if ((ring->entries - skb_queue_len(&ring->queue)) < 2 && hw_queue != BEACON_QUEUE) { RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD, "less desc left, stop skb_queue@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n", hw_queue, ring->idx, idx, skb_queue_len(&ring->queue)); ieee80211_stop_queue(hw, skb_get_queue_mapping(skb)); } spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); rtlpriv->cfg->ops->tx_polling(hw, hw_queue); return 0; } static void rtl_pci_flush(struct ieee80211_hw *hw, bool drop) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u16 i = 0; int queue_id; struct rtl8192_tx_ring *ring; for (queue_id = RTL_PCI_MAX_TX_QUEUE_COUNT - 1; queue_id >= 0;) { u32 queue_len; ring = &pcipriv->dev.tx_ring[queue_id]; queue_len = skb_queue_len(&ring->queue); if (queue_len == 0 || queue_id == BEACON_QUEUE || queue_id == TXCMD_QUEUE) { queue_id--; continue; } else { msleep(20); i++; } /* we just wait 1s for all queues */ if (rtlpriv->psc.rfpwr_state == ERFOFF || is_hal_stop(rtlhal) || i >= 200) return; } } static void rtl_pci_deinit(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); _rtl_pci_deinit_trx_ring(hw); synchronize_irq(rtlpci->pdev->irq); tasklet_kill(&rtlpriv->works.irq_tasklet); cancel_work_sync(&rtlpriv->works.lps_leave_work); flush_workqueue(rtlpriv->works.rtl_wq); destroy_workqueue(rtlpriv->works.rtl_wq); } static int rtl_pci_init(struct ieee80211_hw *hw, struct pci_dev *pdev) { struct rtl_priv *rtlpriv = rtl_priv(hw); int err; _rtl_pci_init_struct(hw, pdev); err = _rtl_pci_init_trx_ring(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "tx ring initialization failed\n"); return err; } return 0; } static int rtl_pci_start(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); int err; rtl_pci_reset_trx_ring(hw); rtlpci->driver_is_goingto_unload = false; err = rtlpriv->cfg->ops->hw_init(hw); if (err) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Failed to config hardware!\n"); return err; } rtlpriv->cfg->ops->enable_interrupt(hw); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "enable_interrupt OK\n"); rtl_init_rx_config(hw); /*should be after adapter start and interrupt enable. */ set_hal_start(rtlhal); RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); rtlpci->up_first_time = false; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "OK\n"); return 0; } static void rtl_pci_stop(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); unsigned long flags; u8 RFInProgressTimeOut = 0; /* *should be before disable interrupt&adapter *and will do it immediately. */ set_hal_stop(rtlhal); rtlpriv->cfg->ops->disable_interrupt(hw); cancel_work_sync(&rtlpriv->works.lps_leave_work); spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flags); while (ppsc->rfchange_inprogress) { spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flags); if (RFInProgressTimeOut > 100) { spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flags); break; } mdelay(1); RFInProgressTimeOut++; spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flags); } ppsc->rfchange_inprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flags); rtlpci->driver_is_goingto_unload = true; rtlpriv->cfg->ops->hw_disable(hw); /* some things are not needed if firmware not available */ if (!rtlpriv->max_fw_size) return; rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flags); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flags); rtl_pci_enable_aspm(hw); } static bool _rtl_pci_find_adapter(struct pci_dev *pdev, struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct pci_dev *bridge_pdev = pdev->bus->self; u16 venderid; u16 deviceid; u8 revisionid; u16 irqline; u8 tmp; pcipriv->ndis_adapter.pcibridge_vendor = PCI_BRIDGE_VENDOR_UNKNOWN; venderid = pdev->vendor; deviceid = pdev->device; pci_read_config_byte(pdev, 0x8, &revisionid); pci_read_config_word(pdev, 0x3C, &irqline); /* PCI ID 0x10ec:0x8192 occurs for both RTL8192E, which uses * r8192e_pci, and RTL8192SE, which uses this driver. If the * revision ID is RTL_PCI_REVISION_ID_8192PCIE (0x01), then * the correct driver is r8192e_pci, thus this routine should * return false. */ if (deviceid == RTL_PCI_8192SE_DID && revisionid == RTL_PCI_REVISION_ID_8192PCIE) return false; if (deviceid == RTL_PCI_8192_DID || deviceid == RTL_PCI_0044_DID || deviceid == RTL_PCI_0047_DID || deviceid == RTL_PCI_8192SE_DID || deviceid == RTL_PCI_8174_DID || deviceid == RTL_PCI_8173_DID || deviceid == RTL_PCI_8172_DID || deviceid == RTL_PCI_8171_DID) { switch (revisionid) { case RTL_PCI_REVISION_ID_8192PCIE: RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "8192 PCI-E is found - vid/did=%x/%x\n", venderid, deviceid); rtlhal->hw_type = HARDWARE_TYPE_RTL8192E; break; case RTL_PCI_REVISION_ID_8192SE: RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "8192SE is found - vid/did=%x/%x\n", venderid, deviceid); rtlhal->hw_type = HARDWARE_TYPE_RTL8192SE; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Err: Unknown device - vid/did=%x/%x\n", venderid, deviceid); rtlhal->hw_type = HARDWARE_TYPE_RTL8192SE; break; } } else if (deviceid == RTL_PCI_8192CET_DID || deviceid == RTL_PCI_8192CE_DID || deviceid == RTL_PCI_8191CE_DID || deviceid == RTL_PCI_8188CE_DID) { rtlhal->hw_type = HARDWARE_TYPE_RTL8192CE; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "8192C PCI-E is found - vid/did=%x/%x\n", venderid, deviceid); } else if (deviceid == RTL_PCI_8192DE_DID || deviceid == RTL_PCI_8192DE_DID2) { rtlhal->hw_type = HARDWARE_TYPE_RTL8192DE; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "8192D PCI-E is found - vid/did=%x/%x\n", venderid, deviceid); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Err: Unknown device - vid/did=%x/%x\n", venderid, deviceid); rtlhal->hw_type = RTL_DEFAULT_HARDWARE_TYPE; } if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DE) { if (revisionid == 0 || revisionid == 1) { if (revisionid == 0) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Find 92DE MAC0\n"); rtlhal->interfaceindex = 0; } else if (revisionid == 1) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Find 92DE MAC1\n"); rtlhal->interfaceindex = 1; } } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Unknown device - VendorID/DeviceID=%x/%x, Revision=%x\n", venderid, deviceid, revisionid); rtlhal->interfaceindex = 0; } } /*find bus info */ pcipriv->ndis_adapter.busnumber = pdev->bus->number; pcipriv->ndis_adapter.devnumber = PCI_SLOT(pdev->devfn); pcipriv->ndis_adapter.funcnumber = PCI_FUNC(pdev->devfn); if (bridge_pdev) { /*find bridge info if available */ pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor; for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) { if (bridge_pdev->vendor == pcibridge_vendors[tmp]) { pcipriv->ndis_adapter.pcibridge_vendor = tmp; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Pci Bridge Vendor is found index: %d\n", tmp); break; } } } if (pcipriv->ndis_adapter.pcibridge_vendor != PCI_BRIDGE_VENDOR_UNKNOWN) { pcipriv->ndis_adapter.pcibridge_busnum = bridge_pdev->bus->number; pcipriv->ndis_adapter.pcibridge_devnum = PCI_SLOT(bridge_pdev->devfn); pcipriv->ndis_adapter.pcibridge_funcnum = PCI_FUNC(bridge_pdev->devfn); pcipriv->ndis_adapter.pcibridge_pciehdr_offset = pci_pcie_cap(bridge_pdev); pcipriv->ndis_adapter.num4bytes = (pcipriv->ndis_adapter.pcibridge_pciehdr_offset + 0x10) / 4; rtl_pci_get_linkcontrol_field(hw); if (pcipriv->ndis_adapter.pcibridge_vendor == PCI_BRIDGE_VENDOR_AMD) { pcipriv->ndis_adapter.amd_l1_patch = rtl_pci_get_amd_l1_patch(hw); } } RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "pcidev busnumber:devnumber:funcnumber:vendor:link_ctl %d:%d:%d:%x:%x\n", pcipriv->ndis_adapter.busnumber, pcipriv->ndis_adapter.devnumber, pcipriv->ndis_adapter.funcnumber, pdev->vendor, pcipriv->ndis_adapter.linkctrl_reg); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "pci_bridge busnumber:devnumber:funcnumber:vendor:pcie_cap:link_ctl_reg:amd %d:%d:%d:%x:%x:%x:%x\n", pcipriv->ndis_adapter.pcibridge_busnum, pcipriv->ndis_adapter.pcibridge_devnum, pcipriv->ndis_adapter.pcibridge_funcnum, pcibridge_vendors[pcipriv->ndis_adapter.pcibridge_vendor], pcipriv->ndis_adapter.pcibridge_pciehdr_offset, pcipriv->ndis_adapter.pcibridge_linkctrlreg, pcipriv->ndis_adapter.amd_l1_patch); rtl_pci_parse_configuration(pdev, hw); return true; } int __devinit rtl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ieee80211_hw *hw = NULL; struct rtl_priv *rtlpriv = NULL; struct rtl_pci_priv *pcipriv = NULL; struct rtl_pci *rtlpci; unsigned long pmem_start, pmem_len, pmem_flags; int err; err = pci_enable_device(pdev); if (err) { RT_ASSERT(false, "%s : Cannot enable new PCI device\n", pci_name(pdev)); return err; } if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { RT_ASSERT(false, "Unable to obtain 32bit DMA for consistent allocations\n"); err = -ENOMEM; goto fail1; } } pci_set_master(pdev); hw = ieee80211_alloc_hw(sizeof(struct rtl_pci_priv) + sizeof(struct rtl_priv), &rtl_ops); if (!hw) { RT_ASSERT(false, "%s : ieee80211 alloc failed\n", pci_name(pdev)); err = -ENOMEM; goto fail1; } SET_IEEE80211_DEV(hw, &pdev->dev); pci_set_drvdata(pdev, hw); rtlpriv = hw->priv; pcipriv = (void *)rtlpriv->priv; pcipriv->dev.pdev = pdev; init_completion(&rtlpriv->firmware_loading_complete); /* init cfg & intf_ops */ rtlpriv->rtlhal.interface = INTF_PCI; rtlpriv->cfg = (struct rtl_hal_cfg *)(id->driver_data); rtlpriv->intf_ops = &rtl_pci_ops; /* *init dbgp flags before all *other functions, because we will *use it in other funtions like *RT_TRACE/RT_PRINT/RTL_PRINT_DATA *you can not use these macro *before this */ rtl_dbgp_flag_init(hw); /* MEM map */ err = pci_request_regions(pdev, KBUILD_MODNAME); if (err) { RT_ASSERT(false, "Can't obtain PCI resources\n"); goto fail1; } pmem_start = pci_resource_start(pdev, rtlpriv->cfg->bar_id); pmem_len = pci_resource_len(pdev, rtlpriv->cfg->bar_id); pmem_flags = pci_resource_flags(pdev, rtlpriv->cfg->bar_id); /*shared mem start */ rtlpriv->io.pci_mem_start = (unsigned long)pci_iomap(pdev, rtlpriv->cfg->bar_id, pmem_len); if (rtlpriv->io.pci_mem_start == 0) { RT_ASSERT(false, "Can't map PCI mem\n"); err = -ENOMEM; goto fail2; } RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "mem mapped space: start: 0x%08lx len:%08lx flags:%08lx, after map:0x%08lx\n", pmem_start, pmem_len, pmem_flags, rtlpriv->io.pci_mem_start); /* Disable Clk Request */ pci_write_config_byte(pdev, 0x81, 0); /* leave D3 mode */ pci_write_config_byte(pdev, 0x44, 0); pci_write_config_byte(pdev, 0x04, 0x06); pci_write_config_byte(pdev, 0x04, 0x07); /* find adapter */ if (!_rtl_pci_find_adapter(pdev, hw)) { err = -ENODEV; goto fail3; } /* Init IO handler */ _rtl_pci_io_handler_init(&pdev->dev, hw); /*like read eeprom and so on */ rtlpriv->cfg->ops->read_eeprom_info(hw); /*aspm */ rtl_pci_init_aspm(hw); /* Init mac80211 sw */ err = rtl_init_core(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't allocate sw for mac80211\n"); goto fail3; } /* Init PCI sw */ err = rtl_pci_init(hw, pdev); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to init PCI\n"); goto fail3; } if (rtlpriv->cfg->ops->init_sw_vars(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n"); err = -ENODEV; goto fail3; } rtlpriv->cfg->ops->init_sw_leds(hw); err = sysfs_create_group(&pdev->dev.kobj, &rtl_attribute_group); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "failed to create sysfs device attributes\n"); goto fail3; } rtlpci = rtl_pcidev(pcipriv); err = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt, IRQF_SHARED, KBUILD_MODNAME, hw); if (err) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "%s: failed to register IRQ handler\n", wiphy_name(hw->wiphy)); goto fail3; } rtlpci->irq_alloc = 1; return 0; fail3: rtl_deinit_core(hw); _rtl_pci_io_handler_release(hw); if (rtlpriv->io.pci_mem_start != 0) pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start); fail2: pci_release_regions(pdev); complete(&rtlpriv->firmware_loading_complete); fail1: if (hw) ieee80211_free_hw(hw); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return err; } EXPORT_SYMBOL(rtl_pci_probe); void rtl_pci_disconnect(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(pcipriv); struct rtl_mac *rtlmac = rtl_mac(rtlpriv); /* just in case driver is removed before firmware callback */ wait_for_completion(&rtlpriv->firmware_loading_complete); clear_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status); sysfs_remove_group(&pdev->dev.kobj, &rtl_attribute_group); /*ieee80211_unregister_hw will call ops_stop */ if (rtlmac->mac80211_registered == 1) { ieee80211_unregister_hw(hw); rtlmac->mac80211_registered = 0; } else { rtl_deinit_deferred_work(hw); rtlpriv->intf_ops->adapter_stop(hw); } rtlpriv->cfg->ops->disable_interrupt(hw); /*deinit rfkill */ rtl_deinit_rfkill(hw); rtl_pci_deinit(hw); rtl_deinit_core(hw); _rtl_pci_io_handler_release(hw); rtlpriv->cfg->ops->deinit_sw_vars(hw); if (rtlpci->irq_alloc) { free_irq(rtlpci->pdev->irq, hw); rtlpci->irq_alloc = 0; } if (rtlpriv->io.pci_mem_start != 0) { pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start); pci_release_regions(pdev); } pci_disable_device(pdev); rtl_pci_disable_aspm(hw); pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); } EXPORT_SYMBOL(rtl_pci_disconnect); /*************************************** kernel pci power state define: PCI_D0 ((pci_power_t __force) 0) PCI_D1 ((pci_power_t __force) 1) PCI_D2 ((pci_power_t __force) 2) PCI_D3hot ((pci_power_t __force) 3) PCI_D3cold ((pci_power_t __force) 4) PCI_UNKNOWN ((pci_power_t __force) 5) This function is called when system goes into suspend state mac80211 will call rtl_mac_stop() from the mac80211 suspend function first, So there is no need to call hw_disable here. ****************************************/ int rtl_pci_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->cfg->ops->hw_suspend(hw); rtl_deinit_rfkill(hw); return 0; } EXPORT_SYMBOL(rtl_pci_suspend); int rtl_pci_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->cfg->ops->hw_resume(hw); rtl_init_rfkill(hw); return 0; } EXPORT_SYMBOL(rtl_pci_resume); struct rtl_intf_ops rtl_pci_ops = { .read_efuse_byte = read_efuse_byte, .adapter_start = rtl_pci_start, .adapter_stop = rtl_pci_stop, .adapter_tx = rtl_pci_tx, .flush = rtl_pci_flush, .reset_trx_ring = rtl_pci_reset_trx_ring, .waitq_insert = rtl_pci_tx_chk_waitq_insert, .disable_aspm = rtl_pci_disable_aspm, .enable_aspm = rtl_pci_enable_aspm, }; compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/efuse.h0000644000175000017500000000643512026211315023431 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_EFUSE_H_ #define __RTL_EFUSE_H_ #define EFUSE_IC_ID_OFFSET 506 #define EFUSE_REAL_CONTENT_LEN 512 #define EFUSE_MAP_LEN 128 #define EFUSE_MAX_WORD_UNIT 4 #define EFUSE_INIT_MAP 0 #define EFUSE_MODIFY_MAP 1 #define PG_STATE_HEADER 0x01 #define PG_STATE_WORD_0 0x02 #define PG_STATE_WORD_1 0x04 #define PG_STATE_WORD_2 0x08 #define PG_STATE_WORD_3 0x10 #define PG_STATE_DATA 0x20 #define PG_SWBYTE_H 0x01 #define PG_SWBYTE_L 0x02 #define _POWERON_DELAY_ #define _PRE_EXECUTE_READ_CMD_ #define EFUSE_REPEAT_THRESHOLD_ 3 #define EFUSE_ERROE_HANDLE 1 struct efuse_map { u8 offset; u8 word_start; u8 byte_start; u8 byte_cnts; }; struct pgpkt_struct { u8 offset; u8 word_en; u8 data[8]; }; enum efuse_data_item { EFUSE_CHIP_ID = 0, EFUSE_LDO_SETTING, EFUSE_CLK_SETTING, EFUSE_SDIO_SETTING, EFUSE_CCCR, EFUSE_SDIO_MODE, EFUSE_OCR, EFUSE_F0CIS, EFUSE_F1CIS, EFUSE_MAC_ADDR, EFUSE_EEPROM_VER, EFUSE_CHAN_PLAN, EFUSE_TXPW_TAB }; enum { VOLTAGE_V25 = 0x03, LDOE25_SHIFT = 28, }; struct efuse_priv { u8 id[2]; u8 ldo_setting[2]; u8 clk_setting[2]; u8 cccr; u8 sdio_mode; u8 ocr[3]; u8 cis0[17]; u8 cis1[48]; u8 mac_addr[6]; u8 eeprom_verno; u8 channel_plan; u8 tx_power_b[14]; u8 tx_power_g[14]; }; extern void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf); extern void efuse_initialize(struct ieee80211_hw *hw); extern u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address); extern void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value); extern void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf); extern void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, u16 offset, u32 *value); extern void efuse_shadow_write(struct ieee80211_hw *hw, u8 type, u16 offset, u32 value); extern bool efuse_shadow_update(struct ieee80211_hw *hw); extern bool efuse_shadow_update_chk(struct ieee80211_hw *hw); extern void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw); extern void efuse_force_write_vendor_Id(struct ieee80211_hw *hw); extern void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/efuse.c0000644000175000017500000010037712026211315023424 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * Tmis program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * Tmis 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. * * You should have received a copy of the GNU General Public License along with * tmis program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * Tme full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include #include "wifi.h" #include "efuse.h" static const u8 MAX_PGPKT_SIZE = 9; static const u8 PGPKT_DATA_SIZE = 8; static const int EFUSE_MAX_SIZE = 512; static const u8 EFUSE_OOB_PROTECT_BYTES = 15; static const struct efuse_map RTL8712_SDIO_EFUSE_TABLE[] = { {0, 0, 0, 2}, {0, 1, 0, 2}, {0, 2, 0, 2}, {1, 0, 0, 1}, {1, 0, 1, 1}, {1, 1, 0, 1}, {1, 1, 1, 3}, {1, 3, 0, 17}, {3, 3, 1, 48}, {10, 0, 0, 6}, {10, 3, 0, 1}, {10, 3, 1, 1}, {11, 0, 0, 28} }; static void efuse_shadow_read_1byte(struct ieee80211_hw *hw, u16 offset, u8 *value); static void efuse_shadow_read_2byte(struct ieee80211_hw *hw, u16 offset, u16 *value); static void efuse_shadow_read_4byte(struct ieee80211_hw *hw, u16 offset, u32 *value); static void efuse_shadow_write_1byte(struct ieee80211_hw *hw, u16 offset, u8 value); static void efuse_shadow_write_2byte(struct ieee80211_hw *hw, u16 offset, u16 value); static void efuse_shadow_write_4byte(struct ieee80211_hw *hw, u16 offset, u32 value); static int efuse_one_byte_read(struct ieee80211_hw *hw, u16 addr, u8 *data); static int efuse_one_byte_write(struct ieee80211_hw *hw, u16 addr, u8 data); static void efuse_read_all_map(struct ieee80211_hw *hw, u8 *efuse); static int efuse_pg_packet_read(struct ieee80211_hw *hw, u8 offset, u8 *data); static int efuse_pg_packet_write(struct ieee80211_hw *hw, u8 offset, u8 word_en, u8 *data); static void efuse_word_enable_data_read(u8 word_en, u8 *sourdata, u8 *targetdata); static u8 efuse_word_enable_data_write(struct ieee80211_hw *hw, u16 efuse_addr, u8 word_en, u8 *data); static void efuse_power_switch(struct ieee80211_hw *hw, u8 write, u8 pwrstate); static u16 efuse_get_current_size(struct ieee80211_hw *hw); static u8 efuse_calculate_word_cnts(u8 word_en); void efuse_initialize(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bytetemp; u8 temp; bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[SYS_FUNC_EN] + 1); temp = bytetemp | 0x20; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[SYS_FUNC_EN] + 1, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[SYS_ISO_CTRL] + 1); temp = bytetemp & 0xFE; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[SYS_ISO_CTRL] + 1, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST] + 3); temp = bytetemp | 0x80; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST] + 3, temp); rtl_write_byte(rtlpriv, 0x2F8, 0x3); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3, 0x72); } u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 data; u8 bytetemp; u8 temp; u32 k = 0; const u32 efuse_len = rtlpriv->cfg->maps[EFUSE_REAL_CONTENT_SIZE]; if (address < efuse_len) { temp = address & 0xFF; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 1, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2); temp = ((address >> 8) & 0x03) | (bytetemp & 0xFC); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3); temp = bytetemp & 0x7F; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3); while (!(bytetemp & 0x80)) { bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg-> maps[EFUSE_CTRL] + 3); k++; if (k == 1000) { k = 0; break; } } data = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL]); return data; } else return 0xFF; } EXPORT_SYMBOL(efuse_read_1byte); void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bytetemp; u8 temp; u32 k = 0; const u32 efuse_len = rtlpriv->cfg->maps[EFUSE_REAL_CONTENT_SIZE]; RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "Addr=%x Data =%x\n", address, value); if (address < efuse_len) { rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL], value); temp = address & 0xFF; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 1, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2); temp = ((address >> 8) & 0x03) | (bytetemp & 0xFC); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3); temp = bytetemp | 0x80; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3, temp); bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3); while (bytetemp & 0x80) { bytetemp = rtl_read_byte(rtlpriv, rtlpriv->cfg-> maps[EFUSE_CTRL] + 3); k++; if (k == 100) { k = 0; break; } } } } void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 value32; u8 readbyte; u16 retry; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 1, (_offset & 0xff)); readbyte = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2, ((_offset >> 8) & 0x03) | (readbyte & 0xfc)); readbyte = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3, (readbyte & 0x7f)); retry = 0; value32 = rtl_read_dword(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL]); while (!(((value32 >> 24) & 0xff) & 0x80) && (retry < 10000)) { value32 = rtl_read_dword(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL]); retry++; } udelay(50); value32 = rtl_read_dword(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL]); *pbuf = (u8) (value32 & 0xff); } void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 *efuse_tbl; u8 rtemp8[1]; u16 efuse_addr = 0; u8 offset, wren; u16 i; u16 j; const u16 efuse_max_section = rtlpriv->cfg->maps[EFUSE_MAX_SECTION_MAP]; const u32 efuse_len = rtlpriv->cfg->maps[EFUSE_REAL_CONTENT_SIZE]; u16 **efuse_word; u16 efuse_utilized = 0; u8 efuse_usage; if ((_offset + _size_byte) > rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]) { RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "read_efuse(): Invalid offset(%#x) with read bytes(%#x)!!\n", _offset, _size_byte); return; } /* allocate memory for efuse_tbl and efuse_word */ efuse_tbl = kmalloc(rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE] * sizeof(u8), GFP_ATOMIC); if (!efuse_tbl) return; efuse_word = kmalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC); if (!efuse_word) goto done; for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) { efuse_word[i] = kmalloc(efuse_max_section * sizeof(u16), GFP_ATOMIC); if (!efuse_word[i]) goto done; } for (i = 0; i < efuse_max_section; i++) for (j = 0; j < EFUSE_MAX_WORD_UNIT; j++) efuse_word[j][i] = 0xFFFF; read_efuse_byte(hw, efuse_addr, rtemp8); if (*rtemp8 != 0xFF) { efuse_utilized++; RTPRINT(rtlpriv, FEEPROM, EFUSE_READ_ALL, "Addr=%d\n", efuse_addr); efuse_addr++; } while ((*rtemp8 != 0xFF) && (efuse_addr < efuse_len)) { offset = ((*rtemp8 >> 4) & 0x0f); if (offset < efuse_max_section) { wren = (*rtemp8 & 0x0f); RTPRINT(rtlpriv, FEEPROM, EFUSE_READ_ALL, "offset-%d Worden=%x\n", offset, wren); for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) { if (!(wren & 0x01)) { RTPRINT(rtlpriv, FEEPROM, EFUSE_READ_ALL, "Addr=%d\n", efuse_addr); read_efuse_byte(hw, efuse_addr, rtemp8); efuse_addr++; efuse_utilized++; efuse_word[i][offset] = (*rtemp8 & 0xff); if (efuse_addr >= efuse_len) break; RTPRINT(rtlpriv, FEEPROM, EFUSE_READ_ALL, "Addr=%d\n", efuse_addr); read_efuse_byte(hw, efuse_addr, rtemp8); efuse_addr++; efuse_utilized++; efuse_word[i][offset] |= (((u16)*rtemp8 << 8) & 0xff00); if (efuse_addr >= efuse_len) break; } wren >>= 1; } } RTPRINT(rtlpriv, FEEPROM, EFUSE_READ_ALL, "Addr=%d\n", efuse_addr); read_efuse_byte(hw, efuse_addr, rtemp8); if (*rtemp8 != 0xFF && (efuse_addr < efuse_len)) { efuse_utilized++; efuse_addr++; } } for (i = 0; i < efuse_max_section; i++) { for (j = 0; j < EFUSE_MAX_WORD_UNIT; j++) { efuse_tbl[(i * 8) + (j * 2)] = (efuse_word[j][i] & 0xff); efuse_tbl[(i * 8) + ((j * 2) + 1)] = ((efuse_word[j][i] >> 8) & 0xff); } } for (i = 0; i < _size_byte; i++) pbuf[i] = efuse_tbl[_offset + i]; rtlefuse->efuse_usedbytes = efuse_utilized; efuse_usage = (u8) ((efuse_utilized * 100) / efuse_len); rtlefuse->efuse_usedpercentage = efuse_usage; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_EFUSE_BYTES, (u8 *)&efuse_utilized); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_EFUSE_USAGE, &efuse_usage); done: for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) kfree(efuse_word[i]); kfree(efuse_word); kfree(efuse_tbl); } bool efuse_shadow_update_chk(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 section_idx, i, Base; u16 words_need = 0, hdr_num = 0, totalbytes, efuse_used; bool wordchanged, result = true; for (section_idx = 0; section_idx < 16; section_idx++) { Base = section_idx * 8; wordchanged = false; for (i = 0; i < 8; i = i + 2) { if ((rtlefuse->efuse_map[EFUSE_INIT_MAP][Base + i] != rtlefuse->efuse_map[EFUSE_MODIFY_MAP][Base + i]) || (rtlefuse->efuse_map[EFUSE_INIT_MAP][Base + i + 1] != rtlefuse->efuse_map[EFUSE_MODIFY_MAP][Base + i + 1])) { words_need++; wordchanged = true; } } if (wordchanged) hdr_num++; } totalbytes = hdr_num + words_need * 2; efuse_used = rtlefuse->efuse_usedbytes; if ((totalbytes + efuse_used) >= (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES)) result = false; RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "efuse_shadow_update_chk(): totalbytes(%#x), hdr_num(%#x), words_need(%#x), efuse_used(%d)\n", totalbytes, hdr_num, words_need, efuse_used); return result; } void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, u16 offset, u32 *value) { if (type == 1) efuse_shadow_read_1byte(hw, offset, (u8 *) value); else if (type == 2) efuse_shadow_read_2byte(hw, offset, (u16 *) value); else if (type == 4) efuse_shadow_read_4byte(hw, offset, value); } void efuse_shadow_write(struct ieee80211_hw *hw, u8 type, u16 offset, u32 value) { if (type == 1) efuse_shadow_write_1byte(hw, offset, (u8) value); else if (type == 2) efuse_shadow_write_2byte(hw, offset, (u16) value); else if (type == 4) efuse_shadow_write_4byte(hw, offset, value); } bool efuse_shadow_update(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u16 i, offset, base; u8 word_en = 0x0F; u8 first_pg = false; RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "--->\n"); if (!efuse_shadow_update_chk(hw)) { efuse_read_all_map(hw, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0]); memcpy(&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]); RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "<---efuse out of capacity!!\n"); return false; } efuse_power_switch(hw, true, true); for (offset = 0; offset < 16; offset++) { word_en = 0x0F; base = offset * 8; for (i = 0; i < 8; i++) { if (first_pg) { word_en &= ~(BIT(i / 2)); rtlefuse->efuse_map[EFUSE_INIT_MAP][base + i] = rtlefuse->efuse_map[EFUSE_MODIFY_MAP][base + i]; } else { if (rtlefuse->efuse_map[EFUSE_INIT_MAP][base + i] != rtlefuse->efuse_map[EFUSE_MODIFY_MAP][base + i]) { word_en &= ~(BIT(i / 2)); rtlefuse->efuse_map[EFUSE_INIT_MAP][base + i] = rtlefuse->efuse_map[EFUSE_MODIFY_MAP][base + i]; } } } if (word_en != 0x0F) { u8 tmpdata[8]; memcpy(tmpdata, &rtlefuse->efuse_map[EFUSE_MODIFY_MAP][base], 8); RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_LOUD, "U-efuse", tmpdata, 8); if (!efuse_pg_packet_write(hw, (u8) offset, word_en, tmpdata)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "PG section(%#x) fail!!\n", offset); break; } } } efuse_power_switch(hw, true, false); efuse_read_all_map(hw, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0]); memcpy(&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]); RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "<---\n"); return true; } void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); if (rtlefuse->autoload_failflag) memset(&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], 0xFF, rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]); else efuse_read_all_map(hw, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0]); memcpy(&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]); } EXPORT_SYMBOL(rtl_efuse_shadow_map_update); void efuse_force_write_vendor_Id(struct ieee80211_hw *hw) { u8 tmpdata[8] = { 0xFF, 0xFF, 0xEC, 0x10, 0xFF, 0xFF, 0xFF, 0xFF }; efuse_power_switch(hw, true, true); efuse_pg_packet_write(hw, 1, 0xD, tmpdata); efuse_power_switch(hw, true, false); } void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx) { } static void efuse_shadow_read_1byte(struct ieee80211_hw *hw, u16 offset, u8 *value) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); *value = rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset]; } static void efuse_shadow_read_2byte(struct ieee80211_hw *hw, u16 offset, u16 *value) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); *value = rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset]; *value |= rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 1] << 8; } static void efuse_shadow_read_4byte(struct ieee80211_hw *hw, u16 offset, u32 *value) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); *value = rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset]; *value |= rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 1] << 8; *value |= rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 2] << 16; *value |= rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 3] << 24; } static void efuse_shadow_write_1byte(struct ieee80211_hw *hw, u16 offset, u8 value) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset] = value; } static void efuse_shadow_write_2byte(struct ieee80211_hw *hw, u16 offset, u16 value) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset] = value & 0x00FF; rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 1] = value >> 8; } static void efuse_shadow_write_4byte(struct ieee80211_hw *hw, u16 offset, u32 value) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset] = (u8) (value & 0x000000FF); rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 1] = (u8) ((value >> 8) & 0x0000FF); rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 2] = (u8) ((value >> 16) & 0x00FF); rtlefuse->efuse_map[EFUSE_MODIFY_MAP][offset + 3] = (u8) ((value >> 24) & 0xFF); } static int efuse_one_byte_read(struct ieee80211_hw *hw, u16 addr, u8 *data) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmpidx = 0; int result; rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 1, (u8) (addr & 0xff)); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2, ((u8) ((addr >> 8) & 0x03)) | (rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2) & 0xFC)); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3, 0x72); while (!(0x80 & rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3)) && (tmpidx < 100)) { tmpidx++; } if (tmpidx < 100) { *data = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL]); result = true; } else { *data = 0xff; result = false; } return result; } static int efuse_one_byte_write(struct ieee80211_hw *hw, u16 addr, u8 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmpidx = 0; RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "Addr = %x Data=%x\n", addr, data); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 1, (u8) (addr & 0xff)); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2, (rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 2) & 0xFC) | (u8) ((addr >> 8) & 0x03)); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL], data); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3, 0xF2); while ((0x80 & rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CTRL] + 3)) && (tmpidx < 100)) { tmpidx++; } if (tmpidx < 100) return true; return false; } static void efuse_read_all_map(struct ieee80211_hw *hw, u8 * efuse) { struct rtl_priv *rtlpriv = rtl_priv(hw); efuse_power_switch(hw, false, true); read_efuse(hw, 0, rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE], efuse); efuse_power_switch(hw, false, false); } static void efuse_read_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, u8 efuse_data, u8 offset, u8 *tmpdata, u8 *readstate) { bool dataempty = true; u8 hoffset; u8 tmpidx; u8 hworden; u8 word_cnts; hoffset = (efuse_data >> 4) & 0x0F; hworden = efuse_data & 0x0F; word_cnts = efuse_calculate_word_cnts(hworden); if (hoffset == offset) { for (tmpidx = 0; tmpidx < word_cnts * 2; tmpidx++) { if (efuse_one_byte_read(hw, *efuse_addr + 1 + tmpidx, &efuse_data)) { tmpdata[tmpidx] = efuse_data; if (efuse_data != 0xff) dataempty = true; } } if (dataempty) { *readstate = PG_STATE_DATA; } else { *efuse_addr = *efuse_addr + (word_cnts * 2) + 1; *readstate = PG_STATE_HEADER; } } else { *efuse_addr = *efuse_addr + (word_cnts * 2) + 1; *readstate = PG_STATE_HEADER; } } static int efuse_pg_packet_read(struct ieee80211_hw *hw, u8 offset, u8 *data) { u8 readstate = PG_STATE_HEADER; bool continual = true; u8 efuse_data, word_cnts = 0; u16 efuse_addr = 0; u8 tmpdata[8]; if (data == NULL) return false; if (offset > 15) return false; memset(data, 0xff, PGPKT_DATA_SIZE * sizeof(u8)); memset(tmpdata, 0xff, PGPKT_DATA_SIZE * sizeof(u8)); while (continual && (efuse_addr < EFUSE_MAX_SIZE)) { if (readstate & PG_STATE_HEADER) { if (efuse_one_byte_read(hw, efuse_addr, &efuse_data) && (efuse_data != 0xFF)) efuse_read_data_case1(hw, &efuse_addr, efuse_data, offset, tmpdata, &readstate); else continual = false; } else if (readstate & PG_STATE_DATA) { efuse_word_enable_data_read(0, tmpdata, data); efuse_addr = efuse_addr + (word_cnts * 2) + 1; readstate = PG_STATE_HEADER; } } if ((data[0] == 0xff) && (data[1] == 0xff) && (data[2] == 0xff) && (data[3] == 0xff) && (data[4] == 0xff) && (data[5] == 0xff) && (data[6] == 0xff) && (data[7] == 0xff)) return false; else return true; } static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, u8 efuse_data, u8 offset, int *continual, u8 *write_state, struct pgpkt_struct *target_pkt, int *repeat_times, int *result, u8 word_en) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct pgpkt_struct tmp_pkt; bool dataempty = true; u8 originaldata[8 * sizeof(u8)]; u8 badworden = 0x0F; u8 match_word_en, tmp_word_en; u8 tmpindex; u8 tmp_header = efuse_data; u8 tmp_word_cnts; tmp_pkt.offset = (tmp_header >> 4) & 0x0F; tmp_pkt.word_en = tmp_header & 0x0F; tmp_word_cnts = efuse_calculate_word_cnts(tmp_pkt.word_en); if (tmp_pkt.offset != target_pkt->offset) { *efuse_addr = *efuse_addr + (tmp_word_cnts * 2) + 1; *write_state = PG_STATE_HEADER; } else { for (tmpindex = 0; tmpindex < (tmp_word_cnts * 2); tmpindex++) { u16 address = *efuse_addr + 1 + tmpindex; if (efuse_one_byte_read(hw, address, &efuse_data) && (efuse_data != 0xFF)) dataempty = false; } if (!dataempty) { *efuse_addr = *efuse_addr + (tmp_word_cnts * 2) + 1; *write_state = PG_STATE_HEADER; } else { match_word_en = 0x0F; if (!((target_pkt->word_en & BIT(0)) | (tmp_pkt.word_en & BIT(0)))) match_word_en &= (~BIT(0)); if (!((target_pkt->word_en & BIT(1)) | (tmp_pkt.word_en & BIT(1)))) match_word_en &= (~BIT(1)); if (!((target_pkt->word_en & BIT(2)) | (tmp_pkt.word_en & BIT(2)))) match_word_en &= (~BIT(2)); if (!((target_pkt->word_en & BIT(3)) | (tmp_pkt.word_en & BIT(3)))) match_word_en &= (~BIT(3)); if ((match_word_en & 0x0F) != 0x0F) { badworden = efuse_word_enable_data_write( hw, *efuse_addr + 1, tmp_pkt.word_en, target_pkt->data); if (0x0F != (badworden & 0x0F)) { u8 reorg_offset = offset; u8 reorg_worden = badworden; efuse_pg_packet_write(hw, reorg_offset, reorg_worden, originaldata); } tmp_word_en = 0x0F; if ((target_pkt->word_en & BIT(0)) ^ (match_word_en & BIT(0))) tmp_word_en &= (~BIT(0)); if ((target_pkt->word_en & BIT(1)) ^ (match_word_en & BIT(1))) tmp_word_en &= (~BIT(1)); if ((target_pkt->word_en & BIT(2)) ^ (match_word_en & BIT(2))) tmp_word_en &= (~BIT(2)); if ((target_pkt->word_en & BIT(3)) ^ (match_word_en & BIT(3))) tmp_word_en &= (~BIT(3)); if ((tmp_word_en & 0x0F) != 0x0F) { *efuse_addr = efuse_get_current_size(hw); target_pkt->offset = offset; target_pkt->word_en = tmp_word_en; } else { *continual = false; } *write_state = PG_STATE_HEADER; *repeat_times += 1; if (*repeat_times > EFUSE_REPEAT_THRESHOLD_) { *continual = false; *result = false; } } else { *efuse_addr += (2 * tmp_word_cnts) + 1; target_pkt->offset = offset; target_pkt->word_en = word_en; *write_state = PG_STATE_HEADER; } } } RTPRINT(rtlpriv, FEEPROM, EFUSE_PG, "efuse PG_STATE_HEADER-1\n"); } static void efuse_write_data_case2(struct ieee80211_hw *hw, u16 *efuse_addr, int *continual, u8 *write_state, struct pgpkt_struct target_pkt, int *repeat_times, int *result) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct pgpkt_struct tmp_pkt; u8 pg_header; u8 tmp_header; u8 originaldata[8 * sizeof(u8)]; u8 tmp_word_cnts; u8 badworden = 0x0F; pg_header = ((target_pkt.offset << 4) & 0xf0) | target_pkt.word_en; efuse_one_byte_write(hw, *efuse_addr, pg_header); efuse_one_byte_read(hw, *efuse_addr, &tmp_header); if (tmp_header == pg_header) { *write_state = PG_STATE_DATA; } else if (tmp_header == 0xFF) { *write_state = PG_STATE_HEADER; *repeat_times += 1; if (*repeat_times > EFUSE_REPEAT_THRESHOLD_) { *continual = false; *result = false; } } else { tmp_pkt.offset = (tmp_header >> 4) & 0x0F; tmp_pkt.word_en = tmp_header & 0x0F; tmp_word_cnts = efuse_calculate_word_cnts(tmp_pkt.word_en); memset(originaldata, 0xff, 8 * sizeof(u8)); if (efuse_pg_packet_read(hw, tmp_pkt.offset, originaldata)) { badworden = efuse_word_enable_data_write(hw, *efuse_addr + 1, tmp_pkt.word_en, originaldata); if (0x0F != (badworden & 0x0F)) { u8 reorg_offset = tmp_pkt.offset; u8 reorg_worden = badworden; efuse_pg_packet_write(hw, reorg_offset, reorg_worden, originaldata); *efuse_addr = efuse_get_current_size(hw); } else { *efuse_addr = *efuse_addr + (tmp_word_cnts * 2) + 1; } } else { *efuse_addr = *efuse_addr + (tmp_word_cnts * 2) + 1; } *write_state = PG_STATE_HEADER; *repeat_times += 1; if (*repeat_times > EFUSE_REPEAT_THRESHOLD_) { *continual = false; *result = false; } RTPRINT(rtlpriv, FEEPROM, EFUSE_PG, "efuse PG_STATE_HEADER-2\n"); } } static int efuse_pg_packet_write(struct ieee80211_hw *hw, u8 offset, u8 word_en, u8 *data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct pgpkt_struct target_pkt; u8 write_state = PG_STATE_HEADER; int continual = true, result = true; u16 efuse_addr = 0; u8 efuse_data; u8 target_word_cnts = 0; u8 badworden = 0x0F; static int repeat_times; if (efuse_get_current_size(hw) >= (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES)) { RTPRINT(rtlpriv, FEEPROM, EFUSE_PG, "efuse_pg_packet_write error\n"); return false; } target_pkt.offset = offset; target_pkt.word_en = word_en; memset(target_pkt.data, 0xFF, 8 * sizeof(u8)); efuse_word_enable_data_read(word_en, data, target_pkt.data); target_word_cnts = efuse_calculate_word_cnts(target_pkt.word_en); RTPRINT(rtlpriv, FEEPROM, EFUSE_PG, "efuse Power ON\n"); while (continual && (efuse_addr < (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES))) { if (write_state == PG_STATE_HEADER) { badworden = 0x0F; RTPRINT(rtlpriv, FEEPROM, EFUSE_PG, "efuse PG_STATE_HEADER\n"); if (efuse_one_byte_read(hw, efuse_addr, &efuse_data) && (efuse_data != 0xFF)) efuse_write_data_case1(hw, &efuse_addr, efuse_data, offset, &continual, &write_state, &target_pkt, &repeat_times, &result, word_en); else efuse_write_data_case2(hw, &efuse_addr, &continual, &write_state, target_pkt, &repeat_times, &result); } else if (write_state == PG_STATE_DATA) { RTPRINT(rtlpriv, FEEPROM, EFUSE_PG, "efuse PG_STATE_DATA\n"); badworden = efuse_word_enable_data_write(hw, efuse_addr + 1, target_pkt.word_en, target_pkt.data); if ((badworden & 0x0F) == 0x0F) { continual = false; } else { efuse_addr += (2 * target_word_cnts) + 1; target_pkt.offset = offset; target_pkt.word_en = badworden; target_word_cnts = efuse_calculate_word_cnts(target_pkt. word_en); write_state = PG_STATE_HEADER; repeat_times++; if (repeat_times > EFUSE_REPEAT_THRESHOLD_) { continual = false; result = false; } RTPRINT(rtlpriv, FEEPROM, EFUSE_PG, "efuse PG_STATE_HEADER-3\n"); } } } if (efuse_addr >= (EFUSE_MAX_SIZE - EFUSE_OOB_PROTECT_BYTES)) { RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "efuse_addr(%#x) Out of size!!\n", efuse_addr); } return true; } static void efuse_word_enable_data_read(u8 word_en, u8 *sourdata, u8 *targetdata) { if (!(word_en & BIT(0))) { targetdata[0] = sourdata[0]; targetdata[1] = sourdata[1]; } if (!(word_en & BIT(1))) { targetdata[2] = sourdata[2]; targetdata[3] = sourdata[3]; } if (!(word_en & BIT(2))) { targetdata[4] = sourdata[4]; targetdata[5] = sourdata[5]; } if (!(word_en & BIT(3))) { targetdata[6] = sourdata[6]; targetdata[7] = sourdata[7]; } } static u8 efuse_word_enable_data_write(struct ieee80211_hw *hw, u16 efuse_addr, u8 word_en, u8 *data) { struct rtl_priv *rtlpriv = rtl_priv(hw); u16 tmpaddr; u16 start_addr = efuse_addr; u8 badworden = 0x0F; u8 tmpdata[8]; memset(tmpdata, 0xff, PGPKT_DATA_SIZE); RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, "word_en = %x efuse_addr=%x\n", word_en, efuse_addr); if (!(word_en & BIT(0))) { tmpaddr = start_addr; efuse_one_byte_write(hw, start_addr++, data[0]); efuse_one_byte_write(hw, start_addr++, data[1]); efuse_one_byte_read(hw, tmpaddr, &tmpdata[0]); efuse_one_byte_read(hw, tmpaddr + 1, &tmpdata[1]); if ((data[0] != tmpdata[0]) || (data[1] != tmpdata[1])) badworden &= (~BIT(0)); } if (!(word_en & BIT(1))) { tmpaddr = start_addr; efuse_one_byte_write(hw, start_addr++, data[2]); efuse_one_byte_write(hw, start_addr++, data[3]); efuse_one_byte_read(hw, tmpaddr, &tmpdata[2]); efuse_one_byte_read(hw, tmpaddr + 1, &tmpdata[3]); if ((data[2] != tmpdata[2]) || (data[3] != tmpdata[3])) badworden &= (~BIT(1)); } if (!(word_en & BIT(2))) { tmpaddr = start_addr; efuse_one_byte_write(hw, start_addr++, data[4]); efuse_one_byte_write(hw, start_addr++, data[5]); efuse_one_byte_read(hw, tmpaddr, &tmpdata[4]); efuse_one_byte_read(hw, tmpaddr + 1, &tmpdata[5]); if ((data[4] != tmpdata[4]) || (data[5] != tmpdata[5])) badworden &= (~BIT(2)); } if (!(word_en & BIT(3))) { tmpaddr = start_addr; efuse_one_byte_write(hw, start_addr++, data[6]); efuse_one_byte_write(hw, start_addr++, data[7]); efuse_one_byte_read(hw, tmpaddr, &tmpdata[6]); efuse_one_byte_read(hw, tmpaddr + 1, &tmpdata[7]); if ((data[6] != tmpdata[6]) || (data[7] != tmpdata[7])) badworden &= (~BIT(3)); } return badworden; } static void efuse_power_switch(struct ieee80211_hw *hw, u8 write, u8 pwrstate) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 tempval; u16 tmpV16; if (pwrstate && (rtlhal->hw_type != HARDWARE_TYPE_RTL8192SE)) { tmpV16 = rtl_read_word(rtlpriv, rtlpriv->cfg->maps[SYS_ISO_CTRL]); if (!(tmpV16 & rtlpriv->cfg->maps[EFUSE_PWC_EV12V])) { tmpV16 |= rtlpriv->cfg->maps[EFUSE_PWC_EV12V]; rtl_write_word(rtlpriv, rtlpriv->cfg->maps[SYS_ISO_CTRL], tmpV16); } tmpV16 = rtl_read_word(rtlpriv, rtlpriv->cfg->maps[SYS_FUNC_EN]); if (!(tmpV16 & rtlpriv->cfg->maps[EFUSE_FEN_ELDR])) { tmpV16 |= rtlpriv->cfg->maps[EFUSE_FEN_ELDR]; rtl_write_word(rtlpriv, rtlpriv->cfg->maps[SYS_FUNC_EN], tmpV16); } tmpV16 = rtl_read_word(rtlpriv, rtlpriv->cfg->maps[SYS_CLK]); if ((!(tmpV16 & rtlpriv->cfg->maps[EFUSE_LOADER_CLK_EN])) || (!(tmpV16 & rtlpriv->cfg->maps[EFUSE_ANA8M]))) { tmpV16 |= (rtlpriv->cfg->maps[EFUSE_LOADER_CLK_EN] | rtlpriv->cfg->maps[EFUSE_ANA8M]); rtl_write_word(rtlpriv, rtlpriv->cfg->maps[SYS_CLK], tmpV16); } } if (pwrstate) { if (write) { tempval = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST] + 3); if (rtlhal->hw_type != HARDWARE_TYPE_RTL8192SE) { tempval &= 0x0F; tempval |= (VOLTAGE_V25 << 4); } rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST] + 3, (tempval | 0x80)); } if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE) { rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CLK], 0x03); } } else { if (write) { tempval = rtl_read_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST] + 3); rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST] + 3, (tempval & 0x7F)); } if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192SE) { rtl_write_byte(rtlpriv, rtlpriv->cfg->maps[EFUSE_CLK], 0x02); } } } static u16 efuse_get_current_size(struct ieee80211_hw *hw) { int continual = true; u16 efuse_addr = 0; u8 hworden; u8 efuse_data, word_cnts; while (continual && efuse_one_byte_read(hw, efuse_addr, &efuse_data) && (efuse_addr < EFUSE_MAX_SIZE)) { if (efuse_data != 0xFF) { hworden = efuse_data & 0x0F; word_cnts = efuse_calculate_word_cnts(hworden); efuse_addr = efuse_addr + (word_cnts * 2) + 1; } else { continual = false; } } return efuse_addr; } static u8 efuse_calculate_word_cnts(u8 word_en) { u8 word_cnts = 0; if (!(word_en & BIT(0))) word_cnts++; if (!(word_en & BIT(1))) word_cnts++; if (!(word_en & BIT(2))) word_cnts++; if (!(word_en & BIT(3))) word_cnts++; return word_cnts; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/debug.h0000644000175000017500000001457212026211315023411 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * Tmis program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * Tmis 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. * * You should have received a copy of the GNU General Public License along with * tmis program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * Tme full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger *****************************************************************************/ #ifndef __RTL_DEBUG_H__ #define __RTL_DEBUG_H__ /*-------------------------------------------------------------- Debug level --------------------------------------------------------------*/ /* *Fatal bug. *For example, Tx/Rx/IO locked up, *memory access violation, *resource allocation failed, *unexpected HW behavior, HW BUG *and so on. */ #define DBG_EMERG 0 /* *Abnormal, rare, or unexpeted cases. *For example, Packet/IO Ctl canceled, *device suprisely unremoved and so on. */ #define DBG_WARNING 2 /* *Normal case driver developer should *open, we can see link status like *assoc/AddBA/DHCP/adapter start and *so on basic and useful infromations. */ #define DBG_DMESG 3 /* *Normal case with useful information *about current SW or HW state. *For example, Tx/Rx descriptor to fill, *Tx/Rx descriptor completed status, *SW protocol state change, dynamic *mechanism state change and so on. */ #define DBG_LOUD 4 /* *Normal case with detail execution *flow or information. */ #define DBG_TRACE 5 /*-------------------------------------------------------------- Define the rt_trace components --------------------------------------------------------------*/ #define COMP_ERR BIT(0) #define COMP_FW BIT(1) #define COMP_INIT BIT(2) /*For init/deinit */ #define COMP_RECV BIT(3) /*For Rx. */ #define COMP_SEND BIT(4) /*For Tx. */ #define COMP_MLME BIT(5) /*For MLME. */ #define COMP_SCAN BIT(6) /*For Scan. */ #define COMP_INTR BIT(7) /*For interrupt Related. */ #define COMP_LED BIT(8) /*For LED. */ #define COMP_SEC BIT(9) /*For sec. */ #define COMP_BEACON BIT(10) /*For beacon. */ #define COMP_RATE BIT(11) /*For rate. */ #define COMP_RXDESC BIT(12) /*For rx desc. */ #define COMP_DIG BIT(13) /*For DIG */ #define COMP_TXAGC BIT(14) /*For Tx power */ #define COMP_HIPWR BIT(15) /*For High Power Mechanism */ #define COMP_POWER BIT(16) /*For lps/ips/aspm. */ #define COMP_POWER_TRACKING BIT(17) /*For TX POWER TRACKING */ #define COMP_BB_POWERSAVING BIT(18) #define COMP_SWAS BIT(19) /*For SW Antenna Switch */ #define COMP_RF BIT(20) /*For RF. */ #define COMP_TURBO BIT(21) /*For EDCA TURBO. */ #define COMP_RATR BIT(22) #define COMP_CMD BIT(23) #define COMP_EFUSE BIT(24) #define COMP_QOS BIT(25) #define COMP_MAC80211 BIT(26) #define COMP_REGD BIT(27) #define COMP_CHAN BIT(28) #define COMP_USB BIT(29) /*-------------------------------------------------------------- Define the rt_print components --------------------------------------------------------------*/ /* Define EEPROM and EFUSE check module bit*/ #define EEPROM_W BIT(0) #define EFUSE_PG BIT(1) #define EFUSE_READ_ALL BIT(2) /* Define init check for module bit*/ #define INIT_EEPROM BIT(0) #define INIT_TxPower BIT(1) #define INIT_IQK BIT(2) #define INIT_RF BIT(3) /* Define PHY-BB/RF/MAC check module bit */ #define PHY_BBR BIT(0) #define PHY_BBW BIT(1) #define PHY_RFR BIT(2) #define PHY_RFW BIT(3) #define PHY_MACR BIT(4) #define PHY_MACW BIT(5) #define PHY_ALLR BIT(6) #define PHY_ALLW BIT(7) #define PHY_TXPWR BIT(8) #define PHY_PWRDIFF BIT(9) enum dbgp_flag_e { FQOS = 0, FTX = 1, FRX = 2, FSEC = 3, FMGNT = 4, FMLME = 5, FRESOURCE = 6, FBEACON = 7, FISR = 8, FPHY = 9, FMP = 10, FEEPROM = 11, FPWR = 12, FDM = 13, FDBGCtrl = 14, FC2H = 15, FBT = 16, FINIT = 17, FIOCTL = 18, DBGP_TYPE_MAX }; #ifdef CONFIG_RTLWIFI_DEBUG #define RT_ASSERT(_exp, fmt, ...) \ do { \ if (!(_exp)) { \ printk(KERN_DEBUG KBUILD_MODNAME ":%s(): " fmt, \ __func__, ##__VA_ARGS__); \ } \ } while (0) #define RT_TRACE(rtlpriv, comp, level, fmt, ...) \ do { \ if (unlikely(((comp) & rtlpriv->dbg.global_debugcomponents) && \ ((level) <= rtlpriv->dbg.global_debuglevel))) { \ printk(KERN_DEBUG KBUILD_MODNAME ":%s():<%lx-%x> " fmt, \ __func__, in_interrupt(), in_atomic(), \ ##__VA_ARGS__); \ } \ } while (0) #define RTPRINT(rtlpriv, dbgtype, dbgflag, fmt, ...) \ do { \ if (unlikely(rtlpriv->dbg.dbgp_type[dbgtype] & dbgflag)) { \ printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \ ##__VA_ARGS__); \ } \ } while (0) #define RT_PRINT_DATA(rtlpriv, _comp, _level, _titlestring, _hexdata, \ _hexdatalen) \ do { \ if (unlikely(((_comp) & rtlpriv->dbg.global_debugcomponents) && \ (_level <= rtlpriv->dbg.global_debuglevel))) { \ printk(KERN_DEBUG "%s: In process \"%s\" (pid %i): %s\n", \ KBUILD_MODNAME, current->comm, current->pid, \ _titlestring); \ print_hex_dump_bytes("", DUMP_PREFIX_NONE, \ _hexdata, _hexdatalen); \ } \ } while (0) #else struct rtl_priv; __printf(2, 3) static inline void RT_ASSERT(int exp, const char *fmt, ...) { } __printf(4, 5) static inline void RT_TRACE(struct rtl_priv *rtlpriv, int comp, int level, const char *fmt, ...) { } __printf(4, 5) static inline void RTPRINT(struct rtl_priv *rtlpriv, int dbgtype, int dbgflag, const char *fmt, ...) { } static inline void RT_PRINT_DATA(struct rtl_priv *rtlpriv, int comp, int level, const char *titlestring, const void *hexdata, size_t hexdatalen) { } #endif void rtl_dbgp_flag_init(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/debug.c0000644000175000017500000000351212026211315023374 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * Tmis program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * Tmis 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. * * You should have received a copy of the GNU General Public License along with * tmis program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * Tme full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger *****************************************************************************/ #include "wifi.h" #include void rtl_dbgp_flag_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 i; rtlpriv->dbg.global_debugcomponents = COMP_ERR | COMP_FW | COMP_INIT | COMP_RECV | COMP_SEND | COMP_MLME | COMP_SCAN | COMP_INTR | COMP_LED | COMP_SEC | COMP_BEACON | COMP_RATE | COMP_RXDESC | COMP_DIG | COMP_TXAGC | COMP_POWER | COMP_POWER_TRACKING | COMP_BB_POWERSAVING | COMP_SWAS | COMP_RF | COMP_TURBO | COMP_RATR | COMP_CMD | COMP_EFUSE | COMP_QOS | COMP_MAC80211 | COMP_REGD | COMP_CHAN; for (i = 0; i < DBGP_TYPE_MAX; i++) rtlpriv->dbg.dbgp_type[i] = 0; /*Init Debug flag enable condition */ } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/core.h0000644000175000017500000000277512026211315023255 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * Tmis program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * Tmis 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. * * You should have received a copy of the GNU General Public License along with * tmis program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * Tme full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_CORE_H__ #define __RTL_CORE_H__ #define RTL_SUPPORTED_FILTERS \ (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | FIF_CONTROL | \ FIF_OTHER_BSS | \ FIF_FCSFAIL | \ FIF_BCN_PRBRESP_PROMISC) #define RTL_SUPPORTED_CTRL_FILTER 0xFF extern const struct ieee80211_ops rtl_ops; void rtl_fw_cb(const struct firmware *firmware, void *context); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/core.c0000644000175000017500000010272112026211315023240 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "wifi.h" #include "core.h" #include "cam.h" #include "base.h" #include "pci.h" #include "ps.h" #include void rtl_fw_cb(const struct firmware *firmware, void *context) { struct ieee80211_hw *hw = context; struct rtl_priv *rtlpriv = rtl_priv(hw); int err; RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD, "Firmware callback routine entered!\n"); complete(&rtlpriv->firmware_loading_complete); if (!firmware) { pr_err("Firmware %s not available\n", rtlpriv->cfg->fw_name); rtlpriv->max_fw_size = 0; return; } if (firmware->size > rtlpriv->max_fw_size) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Firmware is too big!\n"); release_firmware(firmware); return; } memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size); rtlpriv->rtlhal.fwsize = firmware->size; release_firmware(firmware); err = ieee80211_register_hw(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't register mac80211 hw\n"); return; } else { rtlpriv->mac80211.mac80211_registered = 1; } set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status); /*init rfkill */ rtl_init_rfkill(hw); } EXPORT_SYMBOL(rtl_fw_cb); /*mutex for start & stop is must here. */ static int rtl_op_start(struct ieee80211_hw *hw) { int err; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (!is_hal_stop(rtlhal)) return 0; if (!test_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status)) return 0; mutex_lock(&rtlpriv->locks.conf_mutex); err = rtlpriv->intf_ops->adapter_start(hw); if (!err) rtl_watch_dog_timer_callback((unsigned long)hw); mutex_unlock(&rtlpriv->locks.conf_mutex); return err; } static void rtl_op_stop(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); if (is_hal_stop(rtlhal)) return; if (unlikely(ppsc->rfpwr_state == ERFOFF)) { rtl_ips_nic_on(hw); mdelay(1); } mutex_lock(&rtlpriv->locks.conf_mutex); mac->link_state = MAC80211_NOLINK; memset(mac->bssid, 0, 6); mac->vendor = PEER_UNKNOWN; /*reset sec info */ rtl_cam_reset_sec_info(hw); rtl_deinit_deferred_work(hw); rtlpriv->intf_ops->adapter_stop(hw); mutex_unlock(&rtlpriv->locks.conf_mutex); } static void rtl_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_tcb_desc tcb_desc; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); if (unlikely(is_hal_stop(rtlhal) || ppsc->rfpwr_state != ERFON)) goto err_free; if (!test_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status)) goto err_free; if (!rtlpriv->intf_ops->waitq_insert(hw, control->sta, skb)) rtlpriv->intf_ops->adapter_tx(hw, control->sta, skb, &tcb_desc); return; err_free: dev_kfree_skb_any(skb); } static int rtl_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); int err = 0; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; if (mac->vif) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "vif has been set!! mac->vif = 0x%p\n", mac->vif); return -EOPNOTSUPP; } rtl_ips_nic_on(hw); mutex_lock(&rtlpriv->locks.conf_mutex); switch (vif->type) { case NL80211_IFTYPE_STATION: if (mac->beacon_enabled == 1) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "NL80211_IFTYPE_STATION\n"); mac->beacon_enabled = 0; rtlpriv->cfg->ops->update_interrupt_mask(hw, 0, rtlpriv->cfg->maps [RTL_IBSS_INT_MASKS]); } break; case NL80211_IFTYPE_ADHOC: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "NL80211_IFTYPE_ADHOC\n"); mac->link_state = MAC80211_LINKED; rtlpriv->cfg->ops->set_bcn_reg(hw); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) mac->basic_rates = 0xfff; else mac->basic_rates = 0xff0; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, (u8 *) (&mac->basic_rates)); break; case NL80211_IFTYPE_AP: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "NL80211_IFTYPE_AP\n"); mac->link_state = MAC80211_LINKED; rtlpriv->cfg->ops->set_bcn_reg(hw); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) mac->basic_rates = 0xfff; else mac->basic_rates = 0xff0; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, (u8 *) (&mac->basic_rates)); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "operation mode %d is not supported!\n", vif->type); err = -EOPNOTSUPP; goto out; } mac->vif = vif; mac->opmode = vif->type; rtlpriv->cfg->ops->set_network_type(hw, vif->type); memcpy(mac->mac_addr, vif->addr, ETH_ALEN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr); out: mutex_unlock(&rtlpriv->locks.conf_mutex); return err; } static void rtl_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); mutex_lock(&rtlpriv->locks.conf_mutex); /* Free beacon resources */ if ((mac->opmode == NL80211_IFTYPE_AP) || (mac->opmode == NL80211_IFTYPE_ADHOC) || (mac->opmode == NL80211_IFTYPE_MESH_POINT)) { if (mac->beacon_enabled == 1) { mac->beacon_enabled = 0; rtlpriv->cfg->ops->update_interrupt_mask(hw, 0, rtlpriv->cfg->maps [RTL_IBSS_INT_MASKS]); } } /* *Note: We assume NL80211_IFTYPE_UNSPECIFIED as *NO LINK for our hardware. */ mac->vif = NULL; mac->link_state = MAC80211_NOLINK; memset(mac->bssid, 0, 6); mac->vendor = PEER_UNKNOWN; mac->opmode = NL80211_IFTYPE_UNSPECIFIED; rtlpriv->cfg->ops->set_network_type(hw, mac->opmode); mutex_unlock(&rtlpriv->locks.conf_mutex); } static int rtl_op_config(struct ieee80211_hw *hw, u32 changed) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct ieee80211_conf *conf = &hw->conf; mutex_lock(&rtlpriv->locks.conf_mutex); if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { /*BIT(2)*/ RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "IEEE80211_CONF_CHANGE_LISTEN_INTERVAL\n"); } /*For IPS */ if (changed & IEEE80211_CONF_CHANGE_IDLE) { if (hw->conf.flags & IEEE80211_CONF_IDLE) rtl_ips_nic_off(hw); else rtl_ips_nic_on(hw); } else { /* *although rfoff may not cause by ips, but we will *check the reason in set_rf_power_state function */ if (unlikely(ppsc->rfpwr_state == ERFOFF)) rtl_ips_nic_on(hw); } /*For LPS */ if (changed & IEEE80211_CONF_CHANGE_PS) { cancel_delayed_work(&rtlpriv->works.ps_work); cancel_delayed_work(&rtlpriv->works.ps_rfon_wq); if (conf->flags & IEEE80211_CONF_PS) { rtlpriv->psc.sw_ps_enabled = true; /* sleep here is must, or we may recv the beacon and * cause mac80211 into wrong ps state, this will cause * power save nullfunc send fail, and further cause * pkt loss, So sleep must quickly but not immediatly * because that will cause nullfunc send by mac80211 * fail, and cause pkt loss, we have tested that 5mA * is worked very well */ if (!rtlpriv->psc.multi_buffered) queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_work, MSECS(5)); } else { rtl_swlps_rf_awake(hw); rtlpriv->psc.sw_ps_enabled = false; } } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "IEEE80211_CONF_CHANGE_RETRY_LIMITS %x\n", hw->conf.long_frame_max_tx_count); mac->retry_long = hw->conf.long_frame_max_tx_count; mac->retry_short = hw->conf.long_frame_max_tx_count; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT, (u8 *) (&hw->conf. long_frame_max_tx_count)); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *channel = hw->conf.channel; u8 wide_chan = (u8) channel->hw_value; /* *because we should back channel to *current_network.chan in in scanning, *So if set_chan == current_network.chan *we should set it. *because mac80211 tell us wrong bw40 *info for cisco1253 bw20, so we modify *it here based on UPPER & LOWER */ switch (hw->conf.channel_type) { case NL80211_CHAN_HT20: case NL80211_CHAN_NO_HT: /* SC */ mac->cur_40_prime_sc = PRIME_CHNL_OFFSET_DONT_CARE; rtlphy->current_chan_bw = HT_CHANNEL_WIDTH_20; mac->bw_40 = false; break; case NL80211_CHAN_HT40MINUS: /* SC */ mac->cur_40_prime_sc = PRIME_CHNL_OFFSET_UPPER; rtlphy->current_chan_bw = HT_CHANNEL_WIDTH_20_40; mac->bw_40 = true; /*wide channel */ wide_chan -= 2; break; case NL80211_CHAN_HT40PLUS: /* SC */ mac->cur_40_prime_sc = PRIME_CHNL_OFFSET_LOWER; rtlphy->current_chan_bw = HT_CHANNEL_WIDTH_20_40; mac->bw_40 = true; /*wide channel */ wide_chan += 2; break; default: mac->bw_40 = false; RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } if (wide_chan <= 0) wide_chan = 1; /* In scanning, before we go offchannel we may send a ps=1 null * to AP, and then we may send a ps = 0 null to AP quickly, but * first null may have caused AP to put lots of packet to hw tx * buffer. These packets must be tx'd before we go off channel * so we must delay more time to let AP flush these packets * before going offchannel, or dis-association or delete BA will * happen by AP */ if (rtlpriv->mac80211.offchan_delay) { rtlpriv->mac80211.offchan_delay = false; mdelay(50); } rtlphy->current_channel = wide_chan; rtlpriv->cfg->ops->switch_channel(hw); rtlpriv->cfg->ops->set_channel_access(hw); rtlpriv->cfg->ops->set_bw_mode(hw, hw->conf.channel_type); } mutex_unlock(&rtlpriv->locks.conf_mutex); return 0; } static void rtl_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 multicast) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); *new_flags &= RTL_SUPPORTED_FILTERS; if (!changed_flags) return; /*TODO: we disable broadcase now, so enable here */ if (changed_flags & FIF_ALLMULTI) { if (*new_flags & FIF_ALLMULTI) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AM] | rtlpriv->cfg->maps[MAC_RCR_AB]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive multicast frame\n"); } else { mac->rx_conf &= ~(rtlpriv->cfg->maps[MAC_RCR_AM] | rtlpriv->cfg->maps[MAC_RCR_AB]); RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive multicast frame\n"); } } if (changed_flags & FIF_FCSFAIL) { if (*new_flags & FIF_FCSFAIL) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACRC32]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive FCS error frame\n"); } else { mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACRC32]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive FCS error frame\n"); } } /* if ssid not set to hw don't check bssid * here just used for linked scanning, & linked * and nolink check bssid is set in set network_type */ if ((changed_flags & FIF_BCN_PRBRESP_PROMISC) && (mac->link_state >= MAC80211_LINKED)) { if (mac->opmode != NL80211_IFTYPE_AP) { if (*new_flags & FIF_BCN_PRBRESP_PROMISC) { rtlpriv->cfg->ops->set_chk_bssid(hw, false); } else { rtlpriv->cfg->ops->set_chk_bssid(hw, true); } } } if (changed_flags & FIF_CONTROL) { if (*new_flags & FIF_CONTROL) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACF]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive control frame\n"); } else { mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACF]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive control frame\n"); } } if (changed_flags & FIF_OTHER_BSS) { if (*new_flags & FIF_OTHER_BSS) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AAP]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive other BSS's frame\n"); } else { mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_AAP]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive other BSS's frame\n"); } } } static int rtl_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_sta_info *sta_entry; if (sta) { sta_entry = (struct rtl_sta_info *) sta->drv_priv; if (rtlhal->current_bandtype == BAND_ON_2_4G) { sta_entry->wireless_mode = WIRELESS_MODE_G; if (sta->supp_rates[0] <= 0xf) sta_entry->wireless_mode = WIRELESS_MODE_B; if (sta->ht_cap.ht_supported) sta_entry->wireless_mode = WIRELESS_MODE_N_24G; } else if (rtlhal->current_bandtype == BAND_ON_5G) { sta_entry->wireless_mode = WIRELESS_MODE_A; if (sta->ht_cap.ht_supported) sta_entry->wireless_mode = WIRELESS_MODE_N_24G; } /* I found some times mac80211 give wrong supp_rates for adhoc*/ if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_ADHOC) sta_entry->wireless_mode = WIRELESS_MODE_G; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG, "Add sta addr is %pM\n", sta->addr); rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0); } return 0; } static int rtl_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_sta_info *sta_entry; if (sta) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG, "Remove sta addr is %pM\n", sta->addr); sta_entry = (struct rtl_sta_info *) sta->drv_priv; sta_entry->wireless_mode = 0; sta_entry->ratr_index = 0; } return 0; } static int _rtl_get_hal_qnum(u16 queue) { int qnum; switch (queue) { case 0: qnum = AC3_VO; break; case 1: qnum = AC2_VI; break; case 2: qnum = AC0_BE; break; case 3: qnum = AC1_BK; break; default: qnum = AC0_BE; break; } return qnum; } /* *for mac80211 VO=0, VI=1, BE=2, BK=3 *for rtl819x BE=0, BK=1, VI=2, VO=3 */ static int rtl_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *param) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); int aci; if (queue >= AC_MAX) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "queue number %d is incorrect!\n", queue); return -EINVAL; } aci = _rtl_get_hal_qnum(queue); mac->ac[aci].aifs = param->aifs; mac->ac[aci].cw_min = cpu_to_le16(param->cw_min); mac->ac[aci].cw_max = cpu_to_le16(param->cw_max); mac->ac[aci].tx_op = cpu_to_le16(param->txop); memcpy(&mac->edca_param[aci], param, sizeof(*param)); rtlpriv->cfg->ops->set_qos(hw, aci); return 0; } static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct ieee80211_sta *sta = NULL; mutex_lock(&rtlpriv->locks.conf_mutex); if ((vif->type == NL80211_IFTYPE_ADHOC) || (vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_MESH_POINT)) { if ((changed & BSS_CHANGED_BEACON) || (changed & BSS_CHANGED_BEACON_ENABLED && bss_conf->enable_beacon)) { if (mac->beacon_enabled == 0) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG, "BSS_CHANGED_BEACON_ENABLED\n"); /*start hw beacon interrupt. */ /*rtlpriv->cfg->ops->set_bcn_reg(hw); */ mac->beacon_enabled = 1; rtlpriv->cfg->ops->update_interrupt_mask(hw, rtlpriv->cfg->maps [RTL_IBSS_INT_MASKS], 0); if (rtlpriv->cfg->ops->linked_set_reg) rtlpriv->cfg->ops->linked_set_reg(hw); } } if ((changed & BSS_CHANGED_BEACON_ENABLED && !bss_conf->enable_beacon)) { if (mac->beacon_enabled == 1) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG, "ADHOC DISABLE BEACON\n"); mac->beacon_enabled = 0; rtlpriv->cfg->ops->update_interrupt_mask(hw, 0, rtlpriv->cfg->maps [RTL_IBSS_INT_MASKS]); } } if (changed & BSS_CHANGED_BEACON_INT) { RT_TRACE(rtlpriv, COMP_BEACON, DBG_TRACE, "BSS_CHANGED_BEACON_INT\n"); mac->beacon_interval = bss_conf->beacon_int; rtlpriv->cfg->ops->set_bcn_intv(hw); } } /*TODO: reference to enum ieee80211_bss_change */ if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { /* we should reset all sec info & cam * before set cam after linked, we should not * reset in disassoc, that will cause tkip->wep * fail because some flag will be wrong */ /* reset sec info */ rtl_cam_reset_sec_info(hw); /* reset cam to fix wep fail issue * when change from wpa to wep */ rtl_cam_reset_all_entry(hw); mac->link_state = MAC80211_LINKED; mac->cnt_after_linked = 0; mac->assoc_id = bss_conf->aid; memcpy(mac->bssid, bss_conf->bssid, 6); if (rtlpriv->cfg->ops->linked_set_reg) rtlpriv->cfg->ops->linked_set_reg(hw); if (mac->opmode == NL80211_IFTYPE_STATION && sta) rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0); RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG, "BSS_CHANGED_ASSOC\n"); } else { if (mac->link_state == MAC80211_LINKED) rtl_lps_leave(hw); mac->link_state = MAC80211_NOLINK; memset(mac->bssid, 0, 6); /* reset sec info */ rtl_cam_reset_sec_info(hw); rtl_cam_reset_all_entry(hw); mac->vendor = PEER_UNKNOWN; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG, "BSS_CHANGED_UN_ASSOC\n"); } } if (changed & BSS_CHANGED_ERP_CTS_PROT) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "BSS_CHANGED_ERP_CTS_PROT\n"); mac->use_cts_protect = bss_conf->use_cts_prot; } if (changed & BSS_CHANGED_ERP_PREAMBLE) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "BSS_CHANGED_ERP_PREAMBLE use short preamble:%x\n", bss_conf->use_short_preamble); mac->short_preamble = bss_conf->use_short_preamble; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACK_PREAMBLE, &mac->short_preamble); } if (changed & BSS_CHANGED_ERP_SLOT) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "BSS_CHANGED_ERP_SLOT\n"); if (bss_conf->use_short_slot) mac->slot_time = RTL_SLOT_TIME_9; else mac->slot_time = RTL_SLOT_TIME_20; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); } if (changed & BSS_CHANGED_HT) { RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "BSS_CHANGED_HT\n"); rcu_read_lock(); sta = get_sta(hw, vif, bss_conf->bssid); if (sta) { if (sta->ht_cap.ampdu_density > mac->current_ampdu_density) mac->current_ampdu_density = sta->ht_cap.ampdu_density; if (sta->ht_cap.ampdu_factor < mac->current_ampdu_factor) mac->current_ampdu_factor = sta->ht_cap.ampdu_factor; } rcu_read_unlock(); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SHORTGI_DENSITY, &mac->max_mss_density); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AMPDU_FACTOR, &mac->current_ampdu_factor); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AMPDU_MIN_SPACE, &mac->current_ampdu_density); } if (changed & BSS_CHANGED_BSSID) { u32 basic_rates; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BSSID, (u8 *) bss_conf->bssid); RT_TRACE(rtlpriv, COMP_MAC80211, DBG_DMESG, "%pM\n", bss_conf->bssid); mac->vendor = PEER_UNKNOWN; memcpy(mac->bssid, bss_conf->bssid, 6); rtlpriv->cfg->ops->set_network_type(hw, vif->type); rcu_read_lock(); sta = get_sta(hw, vif, bss_conf->bssid); if (!sta) { rcu_read_unlock(); goto out; } if (rtlhal->current_bandtype == BAND_ON_5G) { mac->mode = WIRELESS_MODE_A; } else { if (sta->supp_rates[0] <= 0xf) mac->mode = WIRELESS_MODE_B; else mac->mode = WIRELESS_MODE_G; } if (sta->ht_cap.ht_supported) { if (rtlhal->current_bandtype == BAND_ON_2_4G) mac->mode = WIRELESS_MODE_N_24G; else mac->mode = WIRELESS_MODE_N_5G; } /* just station need it, because ibss & ap mode will * set in sta_add, and will be NULL here */ if (mac->opmode == NL80211_IFTYPE_STATION) { struct rtl_sta_info *sta_entry; sta_entry = (struct rtl_sta_info *) sta->drv_priv; sta_entry->wireless_mode = mac->mode; } if (sta->ht_cap.ht_supported) { mac->ht_enable = true; /* * for cisco 1252 bw20 it's wrong * if (ht_cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { * mac->bw_40 = true; * } * */ } if (changed & BSS_CHANGED_BASIC_RATES) { /* for 5G must << RATE_6M_INDEX=4, * because 5G have no cck rate*/ if (rtlhal->current_bandtype == BAND_ON_5G) basic_rates = sta->supp_rates[1] << 4; else basic_rates = sta->supp_rates[0]; mac->basic_rates = basic_rates; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, (u8 *) (&basic_rates)); } rcu_read_unlock(); } /* * For FW LPS: * To tell firmware we have connected * to an AP. For 92SE/CE power save v2. */ if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { if (ppsc->fwctrl_lps) { u8 mstatus = RT_MEDIA_CONNECT; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_JOINBSSRPT, &mstatus); ppsc->report_linked = true; } } else { if (ppsc->fwctrl_lps) { u8 mstatus = RT_MEDIA_DISCONNECT; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_JOINBSSRPT, &mstatus); ppsc->report_linked = false; } } } out: mutex_unlock(&rtlpriv->locks.conf_mutex); } static u64 rtl_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); u64 tsf; rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_CORRECT_TSF, (u8 *) (&tsf)); return tsf; } static void rtl_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u8 bibss = (mac->opmode == NL80211_IFTYPE_ADHOC) ? 1 : 0; mac->tsf = tsf; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_CORRECT_TSF, &bibss); } static void rtl_op_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp = 0; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_DUAL_TSF_RST, &tmp); } static void rtl_op_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { switch (cmd) { case STA_NOTIFY_SLEEP: break; case STA_NOTIFY_AWAKE: break; default: break; } } static int rtl_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct rtl_priv *rtlpriv = rtl_priv(hw); switch (action) { case IEEE80211_AMPDU_TX_START: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "IEEE80211_AMPDU_TX_START: TID:%d\n", tid); return rtl_tx_agg_start(hw, sta, tid, ssn); break; case IEEE80211_AMPDU_TX_STOP: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid); return rtl_tx_agg_stop(hw, sta, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "IEEE80211_AMPDU_TX_OPERATIONAL:TID:%d\n", tid); rtl_tx_agg_oper(hw, sta, tid); break; case IEEE80211_AMPDU_RX_START: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "IEEE80211_AMPDU_RX_START:TID:%d\n", tid); break; case IEEE80211_AMPDU_RX_STOP: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "IEEE80211_AMPDU_RX_STOP:TID:%d\n", tid); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "IEEE80211_AMPDU_ERR!!!!:\n"); return -EOPNOTSUPP; } return 0; } static void rtl_op_sw_scan_start(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); mac->act_scanning = true; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "\n"); if (mac->link_state == MAC80211_LINKED) { rtl_lps_leave(hw); mac->link_state = MAC80211_LINKED_SCANNING; } else { rtl_ips_nic_on(hw); } /* Dual mac */ rtlpriv->rtlhal.load_imrandiqk_setting_for2g = false; rtlpriv->cfg->ops->led_control(hw, LED_CTL_SITE_SURVEY); rtlpriv->cfg->ops->scan_operation_backup(hw, SCAN_OPT_BACKUP); } static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "\n"); mac->act_scanning = false; /* Dual mac */ rtlpriv->rtlhal.load_imrandiqk_setting_for2g = false; if (mac->link_state == MAC80211_LINKED_SCANNING) { mac->link_state = MAC80211_LINKED; if (mac->opmode == NL80211_IFTYPE_STATION) { /* fix fwlps issue */ rtlpriv->cfg->ops->set_network_type(hw, mac->opmode); } } rtlpriv->cfg->ops->scan_operation_backup(hw, SCAN_OPT_RESTORE); } static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u8 key_type = NO_ENCRYPTION; u8 key_idx; bool group_key = false; bool wep_only = false; int err = 0; u8 mac_addr[ETH_ALEN]; u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; u8 zero_addr[ETH_ALEN] = { 0 }; if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "not open hw encryption\n"); return -ENOSPC; /*User disabled HW-crypto */ } RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "%s hardware based encryption for keyidx: %d, mac: %pM\n", cmd == SET_KEY ? "Using" : "Disabling", key->keyidx, sta ? sta->addr : bcast_addr); rtlpriv->sec.being_setkey = true; rtl_ips_nic_on(hw); mutex_lock(&rtlpriv->locks.conf_mutex); /* <1> get encryption alg */ switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: key_type = WEP40_ENCRYPTION; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "alg:WEP40\n"); break; case WLAN_CIPHER_SUITE_WEP104: RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "alg:WEP104\n"); key_type = WEP104_ENCRYPTION; break; case WLAN_CIPHER_SUITE_TKIP: key_type = TKIP_ENCRYPTION; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "alg:TKIP\n"); break; case WLAN_CIPHER_SUITE_CCMP: key_type = AESCCMP_ENCRYPTION; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "alg:CCMP\n"); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "alg_err:%x!!!!\n", key->cipher); goto out_unlock; } if (key_type == WEP40_ENCRYPTION || key_type == WEP104_ENCRYPTION || mac->opmode == NL80211_IFTYPE_ADHOC) rtlpriv->sec.use_defaultkey = true; /* <2> get key_idx */ key_idx = (u8) (key->keyidx); if (key_idx > 3) goto out_unlock; /* <3> if pairwise key enable_hw_sec */ group_key = !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE); /* wep always be group key, but there are two conditions: * 1) wep only: is just for wep enc, in this condition * rtlpriv->sec.pairwise_enc_algorithm == NO_ENCRYPTION * will be true & enable_hw_sec will be set when wep * ke setting. * 2) wep(group) + AES(pairwise): some AP like cisco * may use it, in this condition enable_hw_sec will not * be set when wep key setting */ /* we must reset sec_info after lingked before set key, * or some flag will be wrong*/ if (mac->opmode == NL80211_IFTYPE_AP) { if (!group_key || key_type == WEP40_ENCRYPTION || key_type == WEP104_ENCRYPTION) { if (group_key) wep_only = true; rtlpriv->cfg->ops->enable_hw_sec(hw); } } else { if ((!group_key) || (mac->opmode == NL80211_IFTYPE_ADHOC) || rtlpriv->sec.pairwise_enc_algorithm == NO_ENCRYPTION) { if (rtlpriv->sec.pairwise_enc_algorithm == NO_ENCRYPTION && (key_type == WEP40_ENCRYPTION || key_type == WEP104_ENCRYPTION)) wep_only = true; rtlpriv->sec.pairwise_enc_algorithm = key_type; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set enable_hw_sec, key_type:%x(OPEN:0 WEP40:1 TKIP:2 AES:4 WEP104:5)\n", key_type); rtlpriv->cfg->ops->enable_hw_sec(hw); } } /* <4> set key based on cmd */ switch (cmd) { case SET_KEY: if (wep_only) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set WEP(group/pairwise) key\n"); /* Pairwise key with an assigned MAC address. */ rtlpriv->sec.pairwise_enc_algorithm = key_type; rtlpriv->sec.group_enc_algorithm = key_type; /*set local buf about wep key. */ memcpy(rtlpriv->sec.key_buf[key_idx], key->key, key->keylen); rtlpriv->sec.key_len[key_idx] = key->keylen; memcpy(mac_addr, zero_addr, ETH_ALEN); } else if (group_key) { /* group key */ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set group key\n"); /* group key */ rtlpriv->sec.group_enc_algorithm = key_type; /*set local buf about group key. */ memcpy(rtlpriv->sec.key_buf[key_idx], key->key, key->keylen); rtlpriv->sec.key_len[key_idx] = key->keylen; memcpy(mac_addr, bcast_addr, ETH_ALEN); } else { /* pairwise key */ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set pairwise key\n"); if (!sta) { RT_ASSERT(false, "pairwise key without mac_addr\n"); err = -EOPNOTSUPP; goto out_unlock; } /* Pairwise key with an assigned MAC address. */ rtlpriv->sec.pairwise_enc_algorithm = key_type; /*set local buf about pairwise key. */ memcpy(rtlpriv->sec.key_buf[PAIRWISE_KEYIDX], key->key, key->keylen); rtlpriv->sec.key_len[PAIRWISE_KEYIDX] = key->keylen; rtlpriv->sec.pairwise_key = rtlpriv->sec.key_buf[PAIRWISE_KEYIDX]; memcpy(mac_addr, sta->addr, ETH_ALEN); } rtlpriv->cfg->ops->set_key(hw, key_idx, mac_addr, group_key, key_type, wep_only, false); /* <5> tell mac80211 do something: */ /*must use sw generate IV, or can not work !!!!. */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; key->hw_key_idx = key_idx; if (key_type == TKIP_ENCRYPTION) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; break; case DISABLE_KEY: RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "disable key delete one entry\n"); /*set local buf about wep key. */ if (mac->opmode == NL80211_IFTYPE_AP) { if (sta) rtl_cam_del_entry(hw, sta->addr); } memset(rtlpriv->sec.key_buf[key_idx], 0, key->keylen); rtlpriv->sec.key_len[key_idx] = 0; memcpy(mac_addr, zero_addr, ETH_ALEN); /* *mac80211 will delete entrys one by one, *so don't use rtl_cam_reset_all_entry *or clear all entry here. */ rtl_cam_delete_one_entry(hw, mac_addr, key_idx); rtl_cam_reset_sec_info(hw); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "cmd_err:%x!!!!\n", cmd); } out_unlock: mutex_unlock(&rtlpriv->locks.conf_mutex); rtlpriv->sec.being_setkey = false; return err; } static void rtl_op_rfkill_poll(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); bool radio_state; bool blocked; u8 valid = 0; if (!test_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status)) return; mutex_lock(&rtlpriv->locks.conf_mutex); /*if Radio On return true here */ radio_state = rtlpriv->cfg->ops->radio_onoff_checking(hw, &valid); if (valid) { if (unlikely(radio_state != rtlpriv->rfkill.rfkill_state)) { rtlpriv->rfkill.rfkill_state = radio_state; RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "wireless radio switch turned %s\n", radio_state ? "on" : "off"); blocked = (rtlpriv->rfkill.rfkill_state == 1) ? 0 : 1; wiphy_rfkill_set_hw_state(hw->wiphy, blocked); } } mutex_unlock(&rtlpriv->locks.conf_mutex); } /* this function is called by mac80211 to flush tx buffer * before switch channle or power save, or tx buffer packet * maybe send after offchannel or rf sleep, this may cause * dis-association by AP */ static void rtl_op_flush(struct ieee80211_hw *hw, bool drop) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->intf_ops->flush) rtlpriv->intf_ops->flush(hw, drop); } const struct ieee80211_ops rtl_ops = { .start = rtl_op_start, .stop = rtl_op_stop, .tx = rtl_op_tx, .add_interface = rtl_op_add_interface, .remove_interface = rtl_op_remove_interface, .config = rtl_op_config, .configure_filter = rtl_op_configure_filter, .sta_add = rtl_op_sta_add, .sta_remove = rtl_op_sta_remove, .set_key = rtl_op_set_key, .conf_tx = rtl_op_conf_tx, .bss_info_changed = rtl_op_bss_info_changed, .get_tsf = rtl_op_get_tsf, .set_tsf = rtl_op_set_tsf, .reset_tsf = rtl_op_reset_tsf, .sta_notify = rtl_op_sta_notify, .ampdu_action = rtl_op_ampdu_action, .sw_scan_start = rtl_op_sw_scan_start, .sw_scan_complete = rtl_op_sw_scan_complete, .rfkill_poll = rtl_op_rfkill_poll, .flush = rtl_op_flush, }; compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/cam.h0000644000175000017500000000404412026211315023054 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_CAM_H_ #define __RTL_CAM_H_ #define CAM_CONTENT_COUNT 8 #define CFG_DEFAULT_KEY BIT(5) #define CFG_VALID BIT(15) #define PAIRWISE_KEYIDX 0 #define CAM_PAIRWISE_KEY_POSITION 4 #define CAM_CONFIG_USEDK 1 #define CAM_CONFIG_NO_USEDK 0 extern void rtl_cam_reset_all_entry(struct ieee80211_hw *hw); extern u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg, u32 ul_default_key, u8 *key_content); int rtl_cam_delete_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, u32 ul_key_id); void rtl_cam_mark_invalid(struct ieee80211_hw *hw, u8 uc_index); void rtl_cam_empty_entry(struct ieee80211_hw *hw, u8 uc_index); void rtl_cam_reset_sec_info(struct ieee80211_hw *hw); u8 rtl_cam_get_free_entry(struct ieee80211_hw *hw, u8 *sta_addr); void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/cam.c0000644000175000017500000002406212026211315023051 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include #include "wifi.h" #include "cam.h" void rtl_cam_reset_sec_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->sec.use_defaultkey = false; rtlpriv->sec.pairwise_enc_algorithm = NO_ENCRYPTION; rtlpriv->sec.group_enc_algorithm = NO_ENCRYPTION; memset(rtlpriv->sec.key_buf, 0, KEY_BUF_SIZE * MAX_KEY_LEN); memset(rtlpriv->sec.key_len, 0, KEY_BUF_SIZE); rtlpriv->sec.pairwise_key = NULL; } static void rtl_cam_program_entry(struct ieee80211_hw *hw, u32 entry_no, u8 *mac_addr, u8 *key_cont_128, u16 us_config) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 target_command; u32 target_content = 0; u8 entry_i; RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "key_cont_128:\n %x:%x:%x:%x:%x:%x\n", key_cont_128[0], key_cont_128[1], key_cont_128[2], key_cont_128[3], key_cont_128[4], key_cont_128[5]); for (entry_i = 0; entry_i < CAM_CONTENT_COUNT; entry_i++) { target_command = entry_i + CAM_CONTENT_COUNT * entry_no; target_command = target_command | BIT(31) | BIT(16); if (entry_i == 0) { target_content = (u32) (*(mac_addr + 0)) << 16 | (u32) (*(mac_addr + 1)) << 24 | (u32) us_config; rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], target_content); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], target_command); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "WRITE %x: %x\n", rtlpriv->cfg->maps[WCAMI], target_content); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The Key ID is %d\n", entry_no); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "WRITE %x: %x\n", rtlpriv->cfg->maps[RWCAM], target_command); } else if (entry_i == 1) { target_content = (u32) (*(mac_addr + 5)) << 24 | (u32) (*(mac_addr + 4)) << 16 | (u32) (*(mac_addr + 3)) << 8 | (u32) (*(mac_addr + 2)); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], target_content); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], target_command); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "WRITE A4: %x\n", target_content); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "WRITE A0: %x\n", target_command); } else { target_content = (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 3)) << 24 | (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 2)) << 16 | (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 1)) << 8 | (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 0)); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], target_content); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], target_command); udelay(100); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "WRITE A4: %x\n", target_content); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "WRITE A0: %x\n", target_command); } } RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "after set key, usconfig:%x\n", us_config); } u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg, u32 ul_default_key, u8 *key_content) { u32 us_config; struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "EntryNo:%x, ulKeyId=%x, ulEncAlg=%x, ulUseDK=%x MacAddr %pM\n", ul_entry_idx, ul_key_id, ul_enc_alg, ul_default_key, mac_addr); if (ul_key_id == TOTAL_CAM_ENTRY) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "<=== ulKeyId exceed!\n"); return 0; } if (ul_default_key == 1) { us_config = CFG_VALID | ((u16) (ul_enc_alg) << 2); } else { us_config = CFG_VALID | ((ul_enc_alg) << 2) | ul_key_id; } rtl_cam_program_entry(hw, ul_entry_idx, mac_addr, key_content, us_config); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "<===\n"); return 1; } EXPORT_SYMBOL(rtl_cam_add_one_entry); int rtl_cam_delete_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, u32 ul_key_id) { u32 ul_command; struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "key_idx:%d\n", ul_key_id); ul_command = ul_key_id * CAM_CONTENT_COUNT; ul_command = ul_command | BIT(31) | BIT(16); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], 0); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "rtl_cam_delete_one_entry(): WRITE A4: %x\n", 0); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "rtl_cam_delete_one_entry(): WRITE A0: %x\n", ul_command); return 0; } EXPORT_SYMBOL(rtl_cam_delete_one_entry); void rtl_cam_reset_all_entry(struct ieee80211_hw *hw) { u32 ul_command; struct rtl_priv *rtlpriv = rtl_priv(hw); ul_command = BIT(31) | BIT(30); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command); } EXPORT_SYMBOL(rtl_cam_reset_all_entry); void rtl_cam_mark_invalid(struct ieee80211_hw *hw, u8 uc_index) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 ul_command; u32 ul_content; u32 ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_AES]; switch (rtlpriv->sec.pairwise_enc_algorithm) { case WEP40_ENCRYPTION: ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_WEP40]; break; case WEP104_ENCRYPTION: ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_WEP104]; break; case TKIP_ENCRYPTION: ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_TKIP]; break; case AESCCMP_ENCRYPTION: ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_AES]; break; default: ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_AES]; } ul_content = (uc_index & 3) | ((u16) (ul_enc_algo) << 2); ul_content |= BIT(15); ul_command = CAM_CONTENT_COUNT * uc_index; ul_command = ul_command | BIT(31) | BIT(16); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], ul_content); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "rtl_cam_mark_invalid(): WRITE A4: %x\n", ul_content); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "rtl_cam_mark_invalid(): WRITE A0: %x\n", ul_command); } EXPORT_SYMBOL(rtl_cam_mark_invalid); void rtl_cam_empty_entry(struct ieee80211_hw *hw, u8 uc_index) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 ul_command; u32 ul_content; u32 ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_AES]; u8 entry_i; switch (rtlpriv->sec.pairwise_enc_algorithm) { case WEP40_ENCRYPTION: ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_WEP40]; break; case WEP104_ENCRYPTION: ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_WEP104]; break; case TKIP_ENCRYPTION: ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_TKIP]; break; case AESCCMP_ENCRYPTION: ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_AES]; break; default: ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_AES]; } for (entry_i = 0; entry_i < CAM_CONTENT_COUNT; entry_i++) { if (entry_i == 0) { ul_content = (uc_index & 0x03) | ((u16) (ul_encalgo) << 2); ul_content |= BIT(15); } else { ul_content = 0; } ul_command = CAM_CONTENT_COUNT * uc_index + entry_i; ul_command = ul_command | BIT(31) | BIT(16); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], ul_content); rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "rtl_cam_empty_entry(): WRITE A4: %x\n", ul_content); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "rtl_cam_empty_entry(): WRITE A0: %x\n", ul_command); } } EXPORT_SYMBOL(rtl_cam_empty_entry); u8 rtl_cam_get_free_entry(struct ieee80211_hw *hw, u8 *sta_addr) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 bitmap = (rtlpriv->sec.hwsec_cam_bitmap) >> 4; u8 entry_idx = 0; u8 i, *addr; if (NULL == sta_addr) { RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "sta_addr is NULL\n"); return TOTAL_CAM_ENTRY; } /* Does STA already exist? */ for (i = 4; i < TOTAL_CAM_ENTRY; i++) { addr = rtlpriv->sec.hwsec_cam_sta_addr[i]; if (memcmp(addr, sta_addr, ETH_ALEN) == 0) return i; } /* Get a free CAM entry. */ for (entry_idx = 4; entry_idx < TOTAL_CAM_ENTRY; entry_idx++) { if ((bitmap & BIT(0)) == 0) { RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "-----hwsec_cam_bitmap: 0x%x entry_idx=%d\n", rtlpriv->sec.hwsec_cam_bitmap, entry_idx); rtlpriv->sec.hwsec_cam_bitmap |= BIT(0) << entry_idx; memcpy(rtlpriv->sec.hwsec_cam_sta_addr[entry_idx], sta_addr, ETH_ALEN); return entry_idx; } bitmap = bitmap >> 1; } return TOTAL_CAM_ENTRY; } EXPORT_SYMBOL(rtl_cam_get_free_entry); void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 bitmap; u8 i, *addr; if (NULL == sta_addr) { RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "sta_addr is NULL\n"); } if (is_zero_ether_addr(sta_addr)) { RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "sta_addr is %pM\n", sta_addr); return; } /* Does STA already exist? */ for (i = 4; i < TOTAL_CAM_ENTRY; i++) { addr = rtlpriv->sec.hwsec_cam_sta_addr[i]; bitmap = (rtlpriv->sec.hwsec_cam_bitmap) >> i; if (((bitmap & BIT(0)) == BIT(0)) && (memcmp(addr, sta_addr, ETH_ALEN) == 0)) { /* Remove from HW Security CAM */ memset(rtlpriv->sec.hwsec_cam_sta_addr[i], 0, ETH_ALEN); rtlpriv->sec.hwsec_cam_bitmap &= ~(BIT(0) << i); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "del CAM entry %d\n", i); } } return; } EXPORT_SYMBOL(rtl_cam_del_entry); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/base.h0000644000175000017500000001250112026211315023223 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_BASE_H__ #define __RTL_BASE_H__ enum ap_peer { PEER_UNKNOWN = 0, PEER_RTL = 1, PEER_RTL_92SE = 2, PEER_BROAD = 3, PEER_RAL = 4, PEER_ATH = 5, PEER_CISCO = 6, PEER_MARV = 7, PEER_AIRGO = 9, PEER_MAX = 10, } ; #define RTL_DUMMY_OFFSET 0 #define RTL_DUMMY_UNIT 8 #define RTL_TX_DUMMY_SIZE (RTL_DUMMY_OFFSET * RTL_DUMMY_UNIT) #define RTL_TX_DESC_SIZE 32 #define RTL_TX_HEADER_SIZE (RTL_TX_DESC_SIZE + RTL_TX_DUMMY_SIZE) #define HT_AMSDU_SIZE_4K 3839 #define HT_AMSDU_SIZE_8K 7935 #define MAX_BIT_RATE_40MHZ_MCS15 300 /* Mbps */ #define MAX_BIT_RATE_40MHZ_MCS7 150 /* Mbps */ #define RTL_RATE_COUNT_LEGACY 12 #define RTL_CHANNEL_COUNT 14 #define FRAME_OFFSET_FRAME_CONTROL 0 #define FRAME_OFFSET_DURATION 2 #define FRAME_OFFSET_ADDRESS1 4 #define FRAME_OFFSET_ADDRESS2 10 #define FRAME_OFFSET_ADDRESS3 16 #define FRAME_OFFSET_SEQUENCE 22 #define FRAME_OFFSET_ADDRESS4 24 #define SET_80211_HDR_FRAME_CONTROL(_hdr, _val) \ WRITEEF2BYTE(_hdr, _val) #define SET_80211_HDR_TYPE_AND_SUBTYPE(_hdr, _val) \ WRITEEF1BYTE(_hdr, _val) #define SET_80211_HDR_PWR_MGNT(_hdr, _val) \ SET_BITS_TO_LE_2BYTE(_hdr, 12, 1, _val) #define SET_80211_HDR_TO_DS(_hdr, _val) \ SET_BITS_TO_LE_2BYTE(_hdr, 8, 1, _val) #define SET_80211_PS_POLL_AID(_hdr, _val) \ (*(u16 *)((u8 *)(_hdr) + 2) = _val) #define SET_80211_PS_POLL_BSSID(_hdr, _val) \ memcpy(((u8 *)(_hdr)) + 4, (u8 *)(_val), ETH_ALEN) #define SET_80211_PS_POLL_TA(_hdr, _val) \ memcpy(((u8 *)(_hdr)) + 10, (u8 *)(_val), ETH_ALEN) #define SET_80211_HDR_DURATION(_hdr, _val) \ (*(u16 *)((u8 *)(_hdr) + FRAME_OFFSET_DURATION) = le16_to_cpu(_val)) #define SET_80211_HDR_ADDRESS1(_hdr, _val) \ CP_MACADDR((u8 *)(_hdr)+FRAME_OFFSET_ADDRESS1, (u8 *)(_val)) #define SET_80211_HDR_ADDRESS2(_hdr, _val) \ CP_MACADDR((u8 *)(_hdr)+FRAME_OFFSET_ADDRESS2, (u8 *)(_val)) #define SET_80211_HDR_ADDRESS3(_hdr, _val) \ CP_MACADDR((u8 *)(_hdr)+FRAME_OFFSET_ADDRESS3, (u8 *)(_val)) #define SET_80211_HDR_FRAGMENT_SEQUENCE(_hdr, _val) \ WRITEEF2BYTE((u8 *)(_hdr)+FRAME_OFFSET_SEQUENCE, _val) #define SET_BEACON_PROBE_RSP_TIME_STAMP_LOW(__phdr, __val) \ WRITEEF4BYTE(((u8 *)(__phdr)) + 24, __val) #define SET_BEACON_PROBE_RSP_TIME_STAMP_HIGH(__phdr, __val) \ WRITEEF4BYTE(((u8 *)(__phdr)) + 28, __val) #define SET_BEACON_PROBE_RSP_BEACON_INTERVAL(__phdr, __val) \ WRITEEF2BYTE(((u8 *)(__phdr)) + 32, __val) #define GET_BEACON_PROBE_RSP_CAPABILITY_INFO(__phdr) \ READEF2BYTE(((u8 *)(__phdr)) + 34) #define SET_BEACON_PROBE_RSP_CAPABILITY_INFO(__phdr, __val) \ WRITEEF2BYTE(((u8 *)(__phdr)) + 34, __val) #define MASK_BEACON_PROBE_RSP_CAPABILITY_INFO(__phdr, __val) \ SET_BEACON_PROBE_RSP_CAPABILITY_INFO(__phdr, \ (GET_BEACON_PROBE_RSP_CAPABILITY_INFO(__phdr) & (~(__val)))) int rtl_init_core(struct ieee80211_hw *hw); void rtl_deinit_core(struct ieee80211_hw *hw); void rtl_init_rx_config(struct ieee80211_hw *hw); void rtl_init_rfkill(struct ieee80211_hw *hw); void rtl_deinit_rfkill(struct ieee80211_hw *hw); void rtl_watch_dog_timer_callback(unsigned long data); void rtl_deinit_deferred_work(struct ieee80211_hw *hw); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); void rtl_watch_dog_timer_callback(unsigned long data); int rtl_tx_agg_start(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid, u16 *ssn); int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid); int rtl_tx_agg_oper(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid); void rtl_watchdog_wq_callback(void *data); void rtl_get_tcb_desc(struct ieee80211_hw *hw, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, struct rtl_tcb_desc *tcb_desc); int rtl_send_smps_action(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 *da, u8 *bssid, enum ieee80211_smps_mode smps); u8 *rtl_find_ie(u8 *data, unsigned int len, u8 ie); void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len); u8 rtl_tid_to_ac(u8 tid); extern struct attribute_group rtl_attribute_group; int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, u8 desc_rate, bool first_ampdu); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/Makefile0000644000175000017500000000077712026211315023614 0ustar mcgrofmcgrofobj-$(CONFIG_RTLWIFI) += rtlwifi.o rtlwifi-objs := \ base.o \ cam.o \ core.o \ debug.o \ efuse.o \ ps.o \ rc.o \ regd.o rtl8192c_common-objs += \ ifneq ($(CONFIG_PCI),) rtlwifi-objs += pci.o endif ifneq ($(CONFIG_USB),) rtlwifi-objs += usb.o endif obj-$(CONFIG_RTL8192C_COMMON) += rtl8192c/ obj-$(CONFIG_RTL8192CE) += rtl8192ce/ obj-$(CONFIG_RTL8192CU) += rtl8192cu/ obj-$(CONFIG_RTL8192SE) += rtl8192se/ obj-$(CONFIG_RTL8192DE) += rtl8192de/ ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/Kconfig0000644000175000017500000000315412026211315023447 0ustar mcgrofmcgrofconfig RTL8192CE tristate "Realtek RTL8192CE/RTL8188CE Wireless Network Adapter" depends on MAC80211 && PCI select FW_LOADER select RTLWIFI select RTL8192C_COMMON ---help--- This is the driver for Realtek RTL8192CE/RTL8188CE 802.11n PCIe wireless network adapters. If you choose to build it as a module, it will be called rtl8192ce config RTL8192SE tristate "Realtek RTL8192SE/RTL8191SE PCIe Wireless Network Adapter" depends on MAC80211 && PCI select FW_LOADER select RTLWIFI ---help--- This is the driver for Realtek RTL8192SE/RTL8191SE 802.11n PCIe wireless network adapters. If you choose to build it as a module, it will be called rtl8192se config RTL8192DE tristate "Realtek RTL8192DE/RTL8188DE PCIe Wireless Network Adapter" depends on MAC80211 && PCI select FW_LOADER select RTLWIFI ---help--- This is the driver for Realtek RTL8192DE/RTL8188DE 802.11n PCIe wireless network adapters. If you choose to build it as a module, it will be called rtl8192de config RTL8192CU tristate "Realtek RTL8192CU/RTL8188CU USB Wireless Network Adapter" depends on MAC80211 && USB select FW_LOADER select RTLWIFI select RTL8192C_COMMON ---help--- This is the driver for Realtek RTL8192CU/RTL8188CU 802.11n USB wireless network adapters. If you choose to build it as a module, it will be called rtl8192cu config RTLWIFI tristate depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE default m config RTLWIFI_DEBUG bool "Additional debugging output" depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE default y config RTL8192C_COMMON tristate depends on RTL8192CE || RTL8192CU default m compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/0000755000175000017500000000000012026211315023577 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/sw.c0000644000175000017500000003177112026211315024405 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../core.h" #include "../pci.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "hw.h" #include "sw.h" #include "trx.h" #include "led.h" #include static void rtl92d_init_aspm_vars(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); /*close ASPM for AMD defaultly */ rtlpci->const_amdpci_aspm = 0; /* * ASPM PS mode. * 0 - Disable ASPM, * 1 - Enable ASPM without Clock Req, * 2 - Enable ASPM with Clock Req, * 3 - Alwyas Enable ASPM with Clock Req, * 4 - Always Enable ASPM without Clock Req. * set defult to RTL8192CE:3 RTL8192E:2 * */ rtlpci->const_pci_aspm = 3; /*Setting for PCI-E device */ rtlpci->const_devicepci_aspm_setting = 0x03; /*Setting for PCI-E bridge */ rtlpci->const_hostpci_aspm_setting = 0x02; /* * In Hw/Sw Radio Off situation. * 0 - Default, * 1 - From ASPM setting without low Mac Pwr, * 2 - From ASPM setting with low Mac Pwr, * 3 - Bus D3 * set default to RTL8192CE:0 RTL8192SE:2 */ rtlpci->const_hwsw_rfoff_d3 = 0; /* * This setting works for those device with * backdoor ASPM setting such as EPHY setting. * 0 - Not support ASPM, * 1 - Support ASPM, * 2 - According to chipset. */ rtlpci->const_support_pciaspm = 1; } static int rtl92d_init_sw_vars(struct ieee80211_hw *hw) { int err; u8 tid; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtlpriv->dm.dm_initialgain_enable = true; rtlpriv->dm.dm_flag = 0; rtlpriv->dm.disable_framebursting = false; rtlpriv->dm.thermalvalue = 0; rtlpriv->dm.useramask = true; /* dual mac */ if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G) rtlpriv->phy.current_channel = 36; else rtlpriv->phy.current_channel = 1; if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) { rtlpriv->rtlhal.disable_amsdu_8k = true; /* No long RX - reduce fragmentation */ rtlpci->rxbuffersize = 4096; } rtlpci->transmit_config = CFENDFORM | BIT(12) | BIT(13); rtlpci->receive_config = ( RCR_APPFCS | RCR_AMF | RCR_ADF | RCR_APP_MIC | RCR_APP_ICV | RCR_AICV | RCR_ACRC32 | RCR_AB | RCR_AM | RCR_APM | RCR_APP_PHYST_RXFF | RCR_HTC_LOC_CTRL ); rtlpci->irq_mask[0] = (u32) ( IMR_ROK | IMR_VODOK | IMR_VIDOK | IMR_BEDOK | IMR_BKDOK | IMR_MGNTDOK | IMR_HIGHDOK | IMR_BDOK | IMR_RDU | IMR_RXFOVW ); rtlpci->irq_mask[1] = (u32) (IMR_CPWM | IMR_C2HCMD); /* for debug level */ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug; /* for LPS & IPS */ rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; if (!rtlpriv->psc.inactiveps) pr_info("Power Save off (module option)\n"); if (!rtlpriv->psc.fwctrl_lps) pr_info("FW Power Save off (module option)\n"); rtlpriv->psc.reg_fwctrl_lps = 3; rtlpriv->psc.reg_max_lps_awakeintvl = 5; /* for ASPM, you can close aspm through * set const_support_pciaspm = 0 */ rtl92d_init_aspm_vars(hw); if (rtlpriv->psc.reg_fwctrl_lps == 1) rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE; else if (rtlpriv->psc.reg_fwctrl_lps == 2) rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE; else if (rtlpriv->psc.reg_fwctrl_lps == 3) rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE; /* for early mode */ rtlpriv->rtlhal.earlymode_enable = true; for (tid = 0; tid < 8; tid++) skb_queue_head_init(&rtlpriv->mac80211.skb_waitq[tid]); /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(0x8000); if (!rtlpriv->rtlhal.pfirmware) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't alloc buffer for fw\n"); return 1; } rtlpriv->max_fw_size = 0x8000; pr_info("Driver for Realtek RTL8192DE WLAN interface\n"); pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name); /* request fw */ err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to request firmware!\n"); return 1; } return 0; } static void rtl92d_deinit_sw_vars(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tid; if (rtlpriv->rtlhal.pfirmware) { vfree(rtlpriv->rtlhal.pfirmware); rtlpriv->rtlhal.pfirmware = NULL; } for (tid = 0; tid < 8; tid++) skb_queue_purge(&rtlpriv->mac80211.skb_waitq[tid]); } static struct rtl_hal_ops rtl8192de_hal_ops = { .init_sw_vars = rtl92d_init_sw_vars, .deinit_sw_vars = rtl92d_deinit_sw_vars, .read_eeprom_info = rtl92de_read_eeprom_info, .interrupt_recognized = rtl92de_interrupt_recognized, .hw_init = rtl92de_hw_init, .hw_disable = rtl92de_card_disable, .hw_suspend = rtl92de_suspend, .hw_resume = rtl92de_resume, .enable_interrupt = rtl92de_enable_interrupt, .disable_interrupt = rtl92de_disable_interrupt, .set_network_type = rtl92de_set_network_type, .set_chk_bssid = rtl92de_set_check_bssid, .set_qos = rtl92de_set_qos, .set_bcn_reg = rtl92de_set_beacon_related_registers, .set_bcn_intv = rtl92de_set_beacon_interval, .update_interrupt_mask = rtl92de_update_interrupt_mask, .get_hw_reg = rtl92de_get_hw_reg, .set_hw_reg = rtl92de_set_hw_reg, .update_rate_tbl = rtl92de_update_hal_rate_tbl, .fill_tx_desc = rtl92de_tx_fill_desc, .fill_tx_cmddesc = rtl92de_tx_fill_cmddesc, .query_rx_desc = rtl92de_rx_query_desc, .set_channel_access = rtl92de_update_channel_access_setting, .radio_onoff_checking = rtl92de_gpio_radio_on_off_checking, .set_bw_mode = rtl92d_phy_set_bw_mode, .switch_channel = rtl92d_phy_sw_chnl, .dm_watchdog = rtl92d_dm_watchdog, .scan_operation_backup = rtl92d_phy_scan_operation_backup, .set_rf_power_state = rtl92d_phy_set_rf_power_state, .led_control = rtl92de_led_control, .set_desc = rtl92de_set_desc, .get_desc = rtl92de_get_desc, .tx_polling = rtl92de_tx_polling, .enable_hw_sec = rtl92de_enable_hw_security_config, .set_key = rtl92de_set_key, .init_sw_leds = rtl92de_init_sw_leds, .get_bbreg = rtl92d_phy_query_bb_reg, .set_bbreg = rtl92d_phy_set_bb_reg, .get_rfreg = rtl92d_phy_query_rf_reg, .set_rfreg = rtl92d_phy_set_rf_reg, .linked_set_reg = rtl92d_linked_set_reg, }; static struct rtl_mod_params rtl92de_mod_params = { .sw_crypto = false, .inactiveps = true, .swctrl_lps = true, .fwctrl_lps = false, .debug = DBG_EMERG, }; static struct rtl_hal_cfg rtl92de_hal_cfg = { .bar_id = 2, .write_readback = true, .name = "rtl8192de", .fw_name = "rtlwifi/rtl8192defw.bin", .ops = &rtl8192de_hal_ops, .mod_params = &rtl92de_mod_params, .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN, .maps[SYS_CLK] = REG_SYS_CLKR, .maps[MAC_RCR_AM] = RCR_AM, .maps[MAC_RCR_AB] = RCR_AB, .maps[MAC_RCR_ACRC32] = RCR_ACRC32, .maps[MAC_RCR_ACF] = RCR_ACF, .maps[MAC_RCR_AAP] = RCR_AAP, .maps[EFUSE_TEST] = REG_EFUSE_TEST, .maps[EFUSE_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_CLK] = 0, /* just for 92se */ .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_PWC_EV12V] = PWC_EV12V, .maps[EFUSE_FEN_ELDR] = FEN_ELDR, .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN, .maps[EFUSE_ANA8M] = 0, /* just for 92se */ .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE, .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION, .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN, .maps[RWCAM] = REG_CAMCMD, .maps[WCAMI] = REG_CAMWRITE, .maps[RCAMO] = REG_CAMREAD, .maps[CAMDBG] = REG_CAMDBG, .maps[SECR] = REG_SECCFG, .maps[SEC_CAM_NONE] = CAM_NONE, .maps[SEC_CAM_WEP40] = CAM_WEP40, .maps[SEC_CAM_TKIP] = CAM_TKIP, .maps[SEC_CAM_AES] = CAM_AES, .maps[SEC_CAM_WEP104] = CAM_WEP104, .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6, .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5, .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4, .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3, .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2, .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1, .maps[RTL_IMR_BCNDOK8] = IMR_BCNDOK8, .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7, .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6, .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5, .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4, .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3, .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2, .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1, .maps[RTL_IMR_TIMEOUT2] = IMR_TIMEOUT2, .maps[RTL_IMR_TIMEOUT1] = IMR_TIMEOUT1, .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW, .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT, .maps[RTL_IMR_BcnInt] = IMR_BcnInt, .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW, .maps[RTL_IMR_RDU] = IMR_RDU, .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND, .maps[RTL_IMR_BDOK] = IMR_BDOK, .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK, .maps[RTL_IMR_TBDER] = IMR_TBDER, .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK, .maps[RTL_IMR_TBDOK] = IMR_TBDOK, .maps[RTL_IMR_BKDOK] = IMR_BKDOK, .maps[RTL_IMR_BEDOK] = IMR_BEDOK, .maps[RTL_IMR_VIDOK] = IMR_VIDOK, .maps[RTL_IMR_VODOK] = IMR_VODOK, .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BcnInt | IMR_TBDOK | IMR_TBDER), .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, }; static struct pci_device_id rtl92de_pci_ids[] __devinitdata = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8193, rtl92de_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x002B, rtl92de_hal_cfg)}, {}, }; MODULE_DEVICE_TABLE(pci, rtl92de_pci_ids); MODULE_AUTHOR("lizhaoming "); MODULE_AUTHOR("Realtek WlanFAE "); MODULE_AUTHOR("Larry Finger "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8192DE 802.11n Dual Mac PCI wireless"); MODULE_FIRMWARE("rtlwifi/rtl8192defw.bin"); module_param_named(swenc, rtl92de_mod_params.sw_crypto, bool, 0444); module_param_named(debug, rtl92de_mod_params.debug, int, 0444); module_param_named(ips, rtl92de_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl92de_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92de_mod_params.fwctrl_lps, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); compat_pci_suspend(rtl_pci_suspend) compat_pci_resume(rtl_pci_resume) static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); static struct pci_driver rtl92de_driver = { .name = KBUILD_MODNAME, .id_table = rtl92de_pci_ids, .probe = rtl_pci_probe, .remove = rtl_pci_disconnect, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &rtlwifi_pm_ops, #elif defined(CONFIG_PM) .suspend = rtl_pci_suspend_compat, .resume = rtl_pci_resume_compat, #endif }; /* add global spin lock to solve the problem that * Dul mac register operation on the same time */ spinlock_t globalmutex_power; spinlock_t globalmutex_for_fwdownload; spinlock_t globalmutex_for_power_and_efuse; static int __init rtl92de_module_init(void) { int ret = 0; spin_lock_init(&globalmutex_power); spin_lock_init(&globalmutex_for_fwdownload); spin_lock_init(&globalmutex_for_power_and_efuse); ret = pci_register_driver(&rtl92de_driver); if (ret) RT_ASSERT(false, "No device found\n"); return ret; } static void __exit rtl92de_module_exit(void) { pci_unregister_driver(&rtl92de_driver); } module_init(rtl92de_module_init); module_exit(rtl92de_module_exit); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/trx.h0000644000175000017500000006123612026211315024575 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92DE_TRX_H__ #define __RTL92DE_TRX_H__ #define TX_DESC_SIZE 64 #define TX_DESC_AGGR_SUBFRAME_SIZE 32 #define RX_DESC_SIZE 32 #define RX_DRV_INFO_SIZE_UNIT 8 #define TX_DESC_NEXT_DESC_OFFSET 40 #define USB_HWDESC_HEADER_LEN 32 #define CRCLENGTH 4 /* Define a macro that takes a le32 word, converts it to host ordering, * right shifts by a specified count, creates a mask of the specified * bit count, and extracts that number of bits. */ #define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \ ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \ BIT_LEN_MASK_32(__mask)) /* Define a macro that clears a bit field in an le32 word and * sets the specified value into that bit field. The resulting * value remains in le32 ordering; however, it is properly converted * to host ordering for the clear and set operations before conversion * back to le32. */ #define SET_BITS_OFFSET_LE(__pdesc, __shift, __len, __val) \ (*(__le32 *)(__pdesc) = \ (cpu_to_le32((le32_to_cpu(*((__le32 *)(__pdesc))) & \ (~(BIT_OFFSET_LEN_MASK_32((__shift), __len)))) | \ (((u32)(__val) & BIT_LEN_MASK_32(__len)) << (__shift))))); /* macros to read/write various fields in RX or TX descriptors */ #define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 0, 16, __val) #define SET_TX_DESC_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 16, 8, __val) #define SET_TX_DESC_BMC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 24, 1, __val) #define SET_TX_DESC_HTC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 25, 1, __val) #define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 26, 1, __val) #define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 27, 1, __val) #define SET_TX_DESC_LINIP(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 28, 1, __val) #define SET_TX_DESC_NO_ACM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 29, 1, __val) #define SET_TX_DESC_GF(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) #define SET_TX_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) #define GET_TX_DESC_PKT_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 0, 16) #define GET_TX_DESC_OFFSET(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 16, 8) #define GET_TX_DESC_BMC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 24, 1) #define GET_TX_DESC_HTC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 25, 1) #define GET_TX_DESC_LAST_SEG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 26, 1) #define GET_TX_DESC_FIRST_SEG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 27, 1) #define GET_TX_DESC_LINIP(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 28, 1) #define GET_TX_DESC_NO_ACM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 29, 1) #define GET_TX_DESC_GF(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 30, 1) #define GET_TX_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) #define SET_TX_DESC_MACID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 0, 5, __val) #define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 5, 1, __val) #define SET_TX_DESC_BK(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 6, 1, __val) #define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 7, 1, __val) #define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 8, 5, __val) #define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 13, 1, __val) #define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 14, 1, __val) #define SET_TX_DESC_PIFS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 15, 1, __val) #define SET_TX_DESC_RATE_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 16, 4, __val) #define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 20, 1, __val) #define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 21, 1, __val) #define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 22, 2, __val) #define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 26, 8, __val) #define GET_TX_DESC_MACID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 0, 5) #define GET_TX_DESC_AGG_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 5, 1) #define GET_TX_DESC_AGG_BREAK(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 6, 1) #define GET_TX_DESC_RDG_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 7, 1) #define GET_TX_DESC_QUEUE_SEL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 8, 5) #define GET_TX_DESC_RDG_NAV_EXT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 13, 1) #define GET_TX_DESC_LSIG_TXOP_EN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 14, 1) #define GET_TX_DESC_PIFS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 15, 1) #define GET_TX_DESC_RATE_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 16, 4) #define GET_TX_DESC_NAV_USE_HDR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 20, 1) #define GET_TX_DESC_EN_DESC_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 21, 1) #define GET_TX_DESC_SEC_TYPE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 22, 2) #define GET_TX_DESC_PKT_OFFSET(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 24, 8) #define SET_TX_DESC_RTS_RC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 0, 6, __val) #define SET_TX_DESC_DATA_RC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 6, 6, __val) #define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 14, 2, __val) #define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 17, 1, __val) #define SET_TX_DESC_RAW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 18, 1, __val) #define SET_TX_DESC_CCX(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 19, 1, __val) #define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 20, 3, __val) #define SET_TX_DESC_ANTSEL_A(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 24, 1, __val) #define SET_TX_DESC_ANTSEL_B(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 25, 1, __val) #define SET_TX_DESC_TX_ANT_CCK(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 26, 2, __val) #define SET_TX_DESC_TX_ANTL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 28, 2, __val) #define SET_TX_DESC_TX_ANT_HT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 30, 2, __val) #define GET_TX_DESC_RTS_RC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 0, 6) #define GET_TX_DESC_DATA_RC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 6, 6) #define GET_TX_DESC_BAR_RTY_TH(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 14, 2) #define GET_TX_DESC_MORE_FRAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 17, 1) #define GET_TX_DESC_RAW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 18, 1) #define GET_TX_DESC_CCX(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 19, 1) #define GET_TX_DESC_AMPDU_DENSITY(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 20, 3) #define GET_TX_DESC_ANTSEL_A(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 24, 1) #define GET_TX_DESC_ANTSEL_B(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 25, 1) #define GET_TX_DESC_TX_ANT_CCK(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 26, 2) #define GET_TX_DESC_TX_ANTL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 28, 2) #define GET_TX_DESC_TX_ANT_HT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 30, 2) #define SET_TX_DESC_NEXT_HEAP_PAGE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 0, 8, __val) #define SET_TX_DESC_TAIL_PAGE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 8, 8, __val) #define SET_TX_DESC_SEQ(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 16, 12, __val) #define SET_TX_DESC_PKT_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 28, 4, __val) #define GET_TX_DESC_NEXT_HEAP_PAGE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 0, 8) #define GET_TX_DESC_TAIL_PAGE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 8, 8) #define GET_TX_DESC_SEQ(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 16, 12) #define GET_TX_DESC_PKT_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 28, 4) #define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 0, 5, __val) #define SET_TX_DESC_AP_DCFE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 5, 1, __val) #define SET_TX_DESC_QOS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 6, 1, __val) #define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 7, 1, __val) #define SET_TX_DESC_USE_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 8, 1, __val) #define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 9, 1, __val) #define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 10, 1, __val) #define SET_TX_DESC_CTS2SELF(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 11, 1, __val) #define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 12, 1, __val) #define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 13, 1, __val) #define SET_TX_DESC_PORT_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 14, 1, __val) #define SET_TX_DESC_WAIT_DCTS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 18, 1, __val) #define SET_TX_DESC_CTS2AP_EN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 19, 1, __val) #define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 20, 2, __val) #define SET_TX_DESC_TX_STBC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 22, 2, __val) #define SET_TX_DESC_DATA_SHORT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 24, 1, __val) #define SET_TX_DESC_DATA_BW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 25, 1, __val) #define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 26, 1, __val) #define SET_TX_DESC_RTS_BW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 27, 1, __val) #define SET_TX_DESC_RTS_SC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 28, 2, __val) #define SET_TX_DESC_RTS_STBC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 30, 2, __val) #define GET_TX_DESC_RTS_RATE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 0, 5) #define GET_TX_DESC_AP_DCFE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 5, 1) #define GET_TX_DESC_QOS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 6, 1) #define GET_TX_DESC_HWSEQ_EN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 7, 1) #define GET_TX_DESC_USE_RATE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 8, 1) #define GET_TX_DESC_DISABLE_RTS_FB(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 9, 1) #define GET_TX_DESC_DISABLE_FB(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 10, 1) #define GET_TX_DESC_CTS2SELF(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 11, 1) #define GET_TX_DESC_RTS_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 12, 1) #define GET_TX_DESC_HW_RTS_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 13, 1) #define GET_TX_DESC_PORT_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 14, 1) #define GET_TX_DESC_WAIT_DCTS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 18, 1) #define GET_TX_DESC_CTS2AP_EN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 19, 1) #define GET_TX_DESC_TX_SUB_CARRIER(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 20, 2) #define GET_TX_DESC_TX_STBC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 22, 2) #define GET_TX_DESC_DATA_SHORT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 24, 1) #define GET_TX_DESC_DATA_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 25, 1) #define GET_TX_DESC_RTS_SHORT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 26, 1) #define GET_TX_DESC_RTS_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 27, 1) #define GET_TX_DESC_RTS_SC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 28, 2) #define GET_TX_DESC_RTS_STBC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 30, 2) #define SET_TX_DESC_TX_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 0, 6, __val) #define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 6, 1, __val) #define SET_TX_DESC_CCX_TAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 7, 1, __val) #define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 8, 5, __val) #define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 13, 4, __val) #define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 17, 1, __val) #define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 18, 6, __val) #define SET_TX_DESC_USB_TXAGG_NUM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 24, 8, __val) #define GET_TX_DESC_TX_RATE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 0, 6) #define GET_TX_DESC_DATA_SHORTGI(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 6, 1) #define GET_TX_DESC_CCX_TAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 7, 1) #define GET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 8, 5) #define GET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 13, 4) #define GET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 17, 1) #define GET_TX_DESC_DATA_RETRY_LIMIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 18, 6) #define GET_TX_DESC_USB_TXAGG_NUM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 24, 8) #define SET_TX_DESC_TXAGC_A(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 0, 5, __val) #define SET_TX_DESC_TXAGC_B(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 5, 5, __val) #define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 10, 1, __val) #define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 11, 5, __val) #define SET_TX_DESC_MCSG1_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 16, 4, __val) #define SET_TX_DESC_MCSG2_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 20, 4, __val) #define SET_TX_DESC_MCSG3_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 24, 4, __val) #define SET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 28, 4, __val) #define GET_TX_DESC_TXAGC_A(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 0, 5) #define GET_TX_DESC_TXAGC_B(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 5, 5) #define GET_TX_DESC_USE_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 10, 1) #define GET_TX_DESC_MAX_AGG_NUM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 11, 5) #define GET_TX_DESC_MCSG1_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 16, 4) #define GET_TX_DESC_MCSG2_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 20, 4) #define GET_TX_DESC_MCSG3_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 24, 4) #define GET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 28, 4) #define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 0, 16, __val) #define SET_TX_DESC_MCSG4_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 16, 4, __val) #define SET_TX_DESC_MCSG5_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 20, 4, __val) #define SET_TX_DESC_MCSG6_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 24, 4, __val) #define SET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 28, 4, __val) #define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 0, 16) #define GET_TX_DESC_MCSG4_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 16, 4) #define GET_TX_DESC_MCSG5_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 20, 4) #define GET_TX_DESC_MCSG6_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 24, 4) #define GET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 28, 4) #define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+32, 0, 32, __val) #define SET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+36, 0, 32, __val) #define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+32, 0, 32) #define GET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+36, 0, 32) #define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+40, 0, 32, __val) #define SET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+44, 0, 32, __val) #define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+40, 0, 32) #define GET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+44, 0, 32) #define GET_RX_DESC_PKT_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 0, 14) #define GET_RX_DESC_CRC32(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 14, 1) #define GET_RX_DESC_ICV(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 15, 1) #define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 16, 4) #define GET_RX_DESC_SECURITY(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 20, 3) #define GET_RX_DESC_QOS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 23, 1) #define GET_RX_DESC_SHIFT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 24, 2) #define GET_RX_DESC_PHYST(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 26, 1) #define GET_RX_DESC_SWDEC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 27, 1) #define GET_RX_DESC_LS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 28, 1) #define GET_RX_DESC_FS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 29, 1) #define GET_RX_DESC_EOR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 30, 1) #define GET_RX_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) #define SET_RX_DESC_PKT_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 0, 14, __val) #define SET_RX_DESC_EOR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) #define SET_RX_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) #define GET_RX_DESC_MACID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 0, 5) #define GET_RX_DESC_TID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 5, 4) #define GET_RX_DESC_HWRSVD(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 9, 5) #define GET_RX_DESC_PAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 14, 1) #define GET_RX_DESC_FAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 15, 1) #define GET_RX_DESC_A1_FIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 16, 4) #define GET_RX_DESC_A2_FIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 20, 4) #define GET_RX_DESC_PAM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 24, 1) #define GET_RX_DESC_PWR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 25, 1) #define GET_RX_DESC_MD(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 26, 1) #define GET_RX_DESC_MF(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 27, 1) #define GET_RX_DESC_TYPE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 28, 2) #define GET_RX_DESC_MC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 30, 1) #define GET_RX_DESC_BC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 31, 1) #define GET_RX_DESC_SEQ(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 0, 12) #define GET_RX_DESC_FRAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 12, 4) #define GET_RX_DESC_NEXT_PKT_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 16, 14) #define GET_RX_DESC_NEXT_IND(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 30, 1) #define GET_RX_DESC_RSVD(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 31, 1) #define GET_RX_DESC_RXMCS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 0, 6) #define GET_RX_DESC_RXHT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 6, 1) #define GET_RX_DESC_SPLCP(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 8, 1) #define GET_RX_DESC_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 9, 1) #define GET_RX_DESC_HTC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 10, 1) #define GET_RX_DESC_HWPC_ERR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 14, 1) #define GET_RX_DESC_HWPC_IND(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 15, 1) #define GET_RX_DESC_IV0(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 16, 16) #define GET_RX_DESC_IV1(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 0, 32) #define GET_RX_DESC_TSFL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 0, 32) #define GET_RX_DESC_BUFF_ADDR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 0, 32) #define GET_RX_DESC_BUFF_ADDR64(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 0, 32) #define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 0, 32, __val) #define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 0, 32, __val) #define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ memset((void *)__pdesc, 0, \ min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET)) /* For 92D early mode */ #define SET_EARLYMODE_PKTNUM(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr, 0, 3, __value) #define SET_EARLYMODE_LEN0(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr, 4, 12, __value) #define SET_EARLYMODE_LEN1(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr, 16, 12, __value) #define SET_EARLYMODE_LEN2_1(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr, 28, 4, __value) #define SET_EARLYMODE_LEN2_2(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr+4, 0, 8, __value) #define SET_EARLYMODE_LEN3(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr+4, 8, 12, __value) #define SET_EARLYMODE_LEN4(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr+4, 20, 12, __value) struct rx_fwinfo_92d { u8 gain_trsw[4]; u8 pwdb_all; u8 cfosho[4]; u8 cfotail[4]; char rxevm[2]; char rxsnr[4]; u8 pdsnr[2]; u8 csi_current[2]; u8 csi_target[2]; u8 sigevm; u8 max_ex_pwr; u8 ex_intf_flag:1; u8 sgi_en:1; u8 rxsc:2; u8 reserve:4; } __packed; struct tx_desc_92d { u32 pktsize:16; u32 offset:8; u32 bmc:1; u32 htc:1; u32 lastseg:1; u32 firstseg:1; u32 linip:1; u32 noacm:1; u32 gf:1; u32 own:1; u32 macid:5; u32 agg_en:1; u32 bk:1; u32 rdg_en:1; u32 queuesel:5; u32 rd_nav_ext:1; u32 lsig_txop_en:1; u32 pifs:1; u32 rateid:4; u32 nav_usehdr:1; u32 en_descid:1; u32 sectype:2; u32 pktoffset:8; u32 rts_rc:6; u32 data_rc:6; u32 rsvd0:2; u32 bar_retryht:2; u32 rsvd1:1; u32 morefrag:1; u32 raw:1; u32 ccx:1; u32 ampdudensity:3; u32 rsvd2:1; u32 ant_sela:1; u32 ant_selb:1; u32 txant_cck:2; u32 txant_l:2; u32 txant_ht:2; u32 nextheadpage:8; u32 tailpage:8; u32 seq:12; u32 pktid:4; u32 rtsrate:5; u32 apdcfe:1; u32 qos:1; u32 hwseq_enable:1; u32 userrate:1; u32 dis_rtsfb:1; u32 dis_datafb:1; u32 cts2self:1; u32 rts_en:1; u32 hwrts_en:1; u32 portid:1; u32 rsvd3:3; u32 waitdcts:1; u32 cts2ap_en:1; u32 txsc:2; u32 stbc:2; u32 txshort:1; u32 txbw:1; u32 rtsshort:1; u32 rtsbw:1; u32 rtssc:2; u32 rtsstbc:2; u32 txrate:6; u32 shortgi:1; u32 ccxt:1; u32 txrate_fb_lmt:5; u32 rtsrate_fb_lmt:4; u32 retrylmt_en:1; u32 txretrylmt:6; u32 usb_txaggnum:8; u32 txagca:5; u32 txagcb:5; u32 usemaxlen:1; u32 maxaggnum:5; u32 mcsg1maxlen:4; u32 mcsg2maxlen:4; u32 mcsg3maxlen:4; u32 mcs7sgimaxlen:4; u32 txbuffersize:16; u32 mcsg4maxlen:4; u32 mcsg5maxlen:4; u32 mcsg6maxlen:4; u32 mcsg15sgimaxlen:4; u32 txbuffaddr; u32 txbufferaddr64; u32 nextdescaddress; u32 nextdescaddress64; u32 reserve_pass_pcie_mm_limit[4]; } __packed; struct rx_desc_92d { u32 length:14; u32 crc32:1; u32 icverror:1; u32 drv_infosize:4; u32 security:3; u32 qos:1; u32 shift:2; u32 phystatus:1; u32 swdec:1; u32 lastseg:1; u32 firstseg:1; u32 eor:1; u32 own:1; u32 macid:5; u32 tid:4; u32 hwrsvd:5; u32 paggr:1; u32 faggr:1; u32 a1_fit:4; u32 a2_fit:4; u32 pam:1; u32 pwr:1; u32 moredata:1; u32 morefrag:1; u32 type:2; u32 mc:1; u32 bc:1; u32 seq:12; u32 frag:4; u32 nextpktlen:14; u32 nextind:1; u32 rsvd:1; u32 rxmcs:6; u32 rxht:1; u32 amsdu:1; u32 splcp:1; u32 bandwidth:1; u32 htc:1; u32 tcpchk_rpt:1; u32 ipcchk_rpt:1; u32 tcpchk_valid:1; u32 hwpcerr:1; u32 hwpcind:1; u32 iv0:16; u32 iv1; u32 tsfl; u32 bufferaddress; u32 bufferaddress64; } __packed; void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); u32 rtl92de_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool b_firstseg, bool b_lastseg, struct sk_buff *skb); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/trx.c0000644000175000017500000006412612026211315024571 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" #include "trx.h" #include "led.h" static u8 _rtl92de_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) { __le16 fc = rtl_get_fc(skb); if (unlikely(ieee80211_is_beacon(fc))) return QSLT_BEACON; if (ieee80211_is_mgmt(fc)) return QSLT_MGNT; return skb->priority; } static u8 _rtl92d_query_rxpwrpercentage(char antpower) { if ((antpower <= -100) || (antpower >= 20)) return 0; else if (antpower >= 0) return 100; else return 100 + antpower; } static u8 _rtl92d_evm_db_to_percentage(char value) { char ret_val = value; if (ret_val >= 0) ret_val = 0; if (ret_val <= -33) ret_val = -33; ret_val = 0 - ret_val; ret_val *= 3; if (ret_val == 99) ret_val = 100; return ret_val; } static long _rtl92de_translate_todbm(struct ieee80211_hw *hw, u8 signal_strength_index) { long signal_power; signal_power = (long)((signal_strength_index + 1) >> 1); signal_power -= 95; return signal_power; } static long _rtl92de_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { long retsig; if (currsig >= 61 && currsig <= 100) retsig = 90 + ((currsig - 60) / 4); else if (currsig >= 41 && currsig <= 60) retsig = 78 + ((currsig - 40) / 2); else if (currsig >= 31 && currsig <= 40) retsig = 66 + (currsig - 30); else if (currsig >= 21 && currsig <= 30) retsig = 54 + (currsig - 20); else if (currsig >= 5 && currsig <= 20) retsig = 42 + (((currsig - 5) * 2) / 3); else if (currsig == 4) retsig = 36; else if (currsig == 3) retsig = 27; else if (currsig == 2) retsig = 18; else if (currsig == 1) retsig = 9; else retsig = currsig; return retsig; } static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, struct rx_desc_92d *pdesc, struct rx_fwinfo_92d *p_drvinfo, bool packet_match_bssid, bool packet_toself, bool packet_beacon) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv); struct phy_sts_cck_8192d *cck_buf; s8 rx_pwr_all, rx_pwr[4]; u8 rf_rx_num = 0, evm, pwdb_all; u8 i, max_spatial_stream; u32 rssi, total_rssi = 0; bool is_cck_rate; is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc); pstats->packet_matchbssid = packet_match_bssid; pstats->packet_toself = packet_toself; pstats->packet_beacon = packet_beacon; pstats->is_cck = is_cck_rate; pstats->rx_mimo_signalquality[0] = -1; pstats->rx_mimo_signalquality[1] = -1; if (is_cck_rate) { u8 report, cck_highpwr; cck_buf = (struct phy_sts_cck_8192d *)p_drvinfo; if (ppsc->rfpwr_state == ERFON) cck_highpwr = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BIT(9)); else cck_highpwr = false; if (!cck_highpwr) { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = cck_buf->cck_agc_rpt & 0xc0; report = report >> 6; switch (report) { case 0x3: rx_pwr_all = -46 - (cck_agc_rpt & 0x3e); break; case 0x2: rx_pwr_all = -26 - (cck_agc_rpt & 0x3e); break; case 0x1: rx_pwr_all = -12 - (cck_agc_rpt & 0x3e); break; case 0x0: rx_pwr_all = 16 - (cck_agc_rpt & 0x3e); break; } } else { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = p_drvinfo->cfosho[0] & 0x60; report = report >> 5; switch (report) { case 0x3: rx_pwr_all = -46 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x2: rx_pwr_all = -26 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x1: rx_pwr_all = -12 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x0: rx_pwr_all = 16 - ((cck_agc_rpt & 0x1f) << 1); break; } } pwdb_all = _rtl92d_query_rxpwrpercentage(rx_pwr_all); /* CCK gain is smaller than OFDM/MCS gain, */ /* so we add gain diff by experiences, the val is 6 */ pwdb_all += 6; if (pwdb_all > 100) pwdb_all = 100; /* modify the offset to make the same gain index with OFDM. */ if (pwdb_all > 34 && pwdb_all <= 42) pwdb_all -= 2; else if (pwdb_all > 26 && pwdb_all <= 34) pwdb_all -= 6; else if (pwdb_all > 14 && pwdb_all <= 26) pwdb_all -= 8; else if (pwdb_all > 4 && pwdb_all <= 14) pwdb_all -= 4; pstats->rx_pwdb_all = pwdb_all; pstats->recvsignalpower = rx_pwr_all; if (packet_match_bssid) { u8 sq; if (pstats->rx_pwdb_all > 40) { sq = 100; } else { sq = cck_buf->sq_rpt; if (sq > 64) sq = 0; else if (sq < 20) sq = 100; else sq = ((64 - sq) * 100) / 44; } pstats->signalquality = sq; pstats->rx_mimo_signalquality[0] = sq; pstats->rx_mimo_signalquality[1] = -1; } } else { rtlpriv->dm.rfpath_rxenable[0] = true; rtlpriv->dm.rfpath_rxenable[1] = true; for (i = RF90_PATH_A; i < RF6052_MAX_PATH; i++) { if (rtlpriv->dm.rfpath_rxenable[i]) rf_rx_num++; rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110; rssi = _rtl92d_query_rxpwrpercentage(rx_pwr[i]); total_rssi += rssi; rtlpriv->stats.rx_snr_db[i] = (long)(p_drvinfo->rxsnr[i] / 2); if (packet_match_bssid) pstats->rx_mimo_signalstrength[i] = (u8) rssi; } rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 106; pwdb_all = _rtl92d_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 && pdesc->rxmcs <= DESC92_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; for (i = 0; i < max_spatial_stream; i++) { evm = _rtl92d_evm_db_to_percentage(p_drvinfo->rxevm[i]); if (packet_match_bssid) { if (i == 0) pstats->signalquality = (u8)(evm & 0xff); pstats->rx_mimo_signalquality[i] = (u8)(evm & 0xff); } } } if (is_cck_rate) pstats->signalstrength = (u8)(_rtl92de_signal_scale_mapping(hw, pwdb_all)); else if (rf_rx_num != 0) pstats->signalstrength = (u8)(_rtl92de_signal_scale_mapping(hw, total_rssi /= rf_rx_num)); } static void rtl92d_loop_over_paths(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 rfpath; for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) { rtlpriv->stats.rx_rssi_percentage[rfpath] = pstats->rx_mimo_signalstrength[rfpath]; } if (pstats->rx_mimo_signalstrength[rfpath] > rtlpriv->stats.rx_rssi_percentage[rfpath]) { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats.rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); rtlpriv->stats.rx_rssi_percentage[rfpath] = rtlpriv->stats.rx_rssi_percentage[rfpath] + 1; } else { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats.rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); } } } static void _rtl92de_process_ui_rssi(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 last_rssi, tmpval; if (pstats->packet_toself || pstats->packet_beacon) { rtlpriv->stats.rssi_calculate_cnt++; if (rtlpriv->stats.ui_rssi.total_num++ >= PHY_RSSI_SLID_WIN_MAX) { rtlpriv->stats.ui_rssi.total_num = PHY_RSSI_SLID_WIN_MAX; last_rssi = rtlpriv->stats.ui_rssi.elements[ rtlpriv->stats.ui_rssi.index]; rtlpriv->stats.ui_rssi.total_val -= last_rssi; } rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength; rtlpriv->stats.ui_rssi.elements [rtlpriv->stats.ui_rssi.index++] = pstats->signalstrength; if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX) rtlpriv->stats.ui_rssi.index = 0; tmpval = rtlpriv->stats.ui_rssi.total_val / rtlpriv->stats.ui_rssi.total_num; rtlpriv->stats.signal_strength = _rtl92de_translate_todbm(hw, (u8) tmpval); pstats->rssi = rtlpriv->stats.signal_strength; } if (!pstats->is_cck && pstats->packet_toself) rtl92d_loop_over_paths(hw, pstats); } static void _rtl92de_update_rxsignalstatistics(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); int weighting = 0; if (rtlpriv->stats.recv_signal_power == 0) rtlpriv->stats.recv_signal_power = pstats->recvsignalpower; if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power) weighting = 5; else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power) weighting = (-5); rtlpriv->stats.recv_signal_power = (rtlpriv->stats.recv_signal_power * 5 + pstats->recvsignalpower + weighting) / 6; } static void _rtl92de_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb; if (mac->opmode == NL80211_IFTYPE_ADHOC || mac->opmode == NL80211_IFTYPE_AP) return; else undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; if (pstats->packet_toself || pstats->packet_beacon) { if (undecorated_smoothed_pwdb < 0) undecorated_smoothed_pwdb = pstats->rx_pwdb_all; if (pstats->rx_pwdb_all > (u32) undecorated_smoothed_pwdb) { undecorated_smoothed_pwdb = (((undecorated_smoothed_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); undecorated_smoothed_pwdb = undecorated_smoothed_pwdb + 1; } else { undecorated_smoothed_pwdb = (((undecorated_smoothed_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); } rtlpriv->dm.undecorated_smoothed_pwdb = undecorated_smoothed_pwdb; _rtl92de_update_rxsignalstatistics(hw, pstats); } } static void rtl92d_loop_over_streams(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); int stream; for (stream = 0; stream < 2; stream++) { if (pstats->rx_mimo_signalquality[stream] != -1) { if (rtlpriv->stats.rx_evm_percentage[stream] == 0) { rtlpriv->stats.rx_evm_percentage[stream] = pstats->rx_mimo_signalquality[stream]; } rtlpriv->stats.rx_evm_percentage[stream] = ((rtlpriv->stats.rx_evm_percentage[stream] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalquality[stream] * 1)) / (RX_SMOOTH_FACTOR); } } } static void _rtl92de_process_ui_link_quality(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 last_evm, tmpval; if (pstats->signalquality == 0) return; if (pstats->packet_toself || pstats->packet_beacon) { if (rtlpriv->stats.ui_link_quality.total_num++ >= PHY_LINKQUALITY_SLID_WIN_MAX) { rtlpriv->stats.ui_link_quality.total_num = PHY_LINKQUALITY_SLID_WIN_MAX; last_evm = rtlpriv->stats.ui_link_quality.elements[ rtlpriv->stats.ui_link_quality.index]; rtlpriv->stats.ui_link_quality.total_val -= last_evm; } rtlpriv->stats.ui_link_quality.total_val += pstats->signalquality; rtlpriv->stats.ui_link_quality.elements[ rtlpriv->stats.ui_link_quality.index++] = pstats->signalquality; if (rtlpriv->stats.ui_link_quality.index >= PHY_LINKQUALITY_SLID_WIN_MAX) rtlpriv->stats.ui_link_quality.index = 0; tmpval = rtlpriv->stats.ui_link_quality.total_val / rtlpriv->stats.ui_link_quality.total_num; rtlpriv->stats.signal_quality = tmpval; rtlpriv->stats.last_sigstrength_inpercent = tmpval; rtl92d_loop_over_streams(hw, pstats); } } static void _rtl92de_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer, struct rtl_stats *pcurrent_stats) { if (!pcurrent_stats->packet_matchbssid && !pcurrent_stats->packet_beacon) return; _rtl92de_process_ui_rssi(hw, pcurrent_stats); _rtl92de_process_pwdb(hw, pcurrent_stats); _rtl92de_process_ui_link_quality(hw, pcurrent_stats); } static void _rtl92de_translate_rx_signal_stuff(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_stats *pstats, struct rx_desc_92d *pdesc, struct rx_fwinfo_92d *p_drvinfo) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct ieee80211_hdr *hdr; u8 *tmp_buf; u8 *praddr; u16 type, cfc; __le16 fc; bool packet_matchbssid, packet_toself, packet_beacon; tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift; hdr = (struct ieee80211_hdr *)tmp_buf; fc = hdr->frame_control; cfc = le16_to_cpu(fc); type = WLAN_FC_GET_TYPE(fc); praddr = hdr->addr1; packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) && ether_addr_equal(mac->bssid, (cfc & IEEE80211_FCTL_TODS) ? hdr->addr1 : (cfc & IEEE80211_FCTL_FROMDS) ? hdr->addr2 : hdr->addr3) && (!pstats->hwerror) && (!pstats->crc) && (!pstats->icv)); packet_toself = packet_matchbssid && ether_addr_equal(praddr, rtlefuse->dev_addr); if (ieee80211_is_beacon(fc)) packet_beacon = true; _rtl92de_query_rxphystatus(hw, pstats, pdesc, p_drvinfo, packet_matchbssid, packet_toself, packet_beacon); _rtl92de_process_phyinfo(hw, tmp_buf, pstats); } bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *p_desc, struct sk_buff *skb) { struct rx_fwinfo_92d *p_drvinfo; struct rx_desc_92d *pdesc = (struct rx_desc_92d *)p_desc; u32 phystatus = GET_RX_DESC_PHYST(pdesc); stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc); stats->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) * RX_DRV_INFO_SIZE_UNIT; stats->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03); stats->icv = (u16) GET_RX_DESC_ICV(pdesc); stats->crc = (u16) GET_RX_DESC_CRC32(pdesc); stats->hwerror = (stats->crc | stats->icv); stats->decrypted = !GET_RX_DESC_SWDEC(pdesc); stats->rate = (u8) GET_RX_DESC_RXMCS(pdesc); stats->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc); stats->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1); stats->isfirst_ampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1) && (GET_RX_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); rx_status->freq = hw->conf.channel->center_freq; rx_status->band = hw->conf.channel->band; if (GET_RX_DESC_CRC32(pdesc)) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (!GET_RX_DESC_SWDEC(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(pdesc)) rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RXHT(pdesc)) rx_status->flag |= RX_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, (bool)GET_RX_DESC_RXHT(pdesc), (u8)GET_RX_DESC_RXMCS(pdesc), (bool)GET_RX_DESC_PAGGR(pdesc)); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92d *)(skb->data + stats->rx_bufshift); _rtl92de_translate_rx_signal_stuff(hw, skb, stats, pdesc, p_drvinfo); } /*rx_status->qual = stats->signal; */ rx_status->signal = stats->rssi + 10; /*rx_status->noise = -stats->noise; */ return true; } static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc, u8 *virtualaddress) { memset(virtualaddress, 0, 8); SET_EARLYMODE_PKTNUM(virtualaddress, ptcb_desc->empkt_num); SET_EARLYMODE_LEN0(virtualaddress, ptcb_desc->empkt_len[0]); SET_EARLYMODE_LEN1(virtualaddress, ptcb_desc->empkt_len[1]); SET_EARLYMODE_LEN2_1(virtualaddress, ptcb_desc->empkt_len[2] & 0xF); SET_EARLYMODE_LEN2_2(virtualaddress, ptcb_desc->empkt_len[2] >> 4); SET_EARLYMODE_LEN3(virtualaddress, ptcb_desc->empkt_len[3]); SET_EARLYMODE_LEN4(virtualaddress, ptcb_desc->empkt_len[4]); } void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; unsigned int buf_len = 0; unsigned int skb_len = skb->len; u8 fw_qsel = _rtl92de_map_hwqueue_to_fwqueue(skb, hw_queue); bool firstseg = ((hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0); bool lastseg = ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) == 0); dma_addr_t mapping; u8 bw_40 = 0; if (mac->opmode == NL80211_IFTYPE_STATION) { bw_40 = mac->bw_40; } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; } seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc); /* reserve 8 byte for AMPDU early mode */ if (rtlhal->earlymode_enable) { skb_push(skb, EM_HDR_LEN); memset(skb->data, 0, EM_HDR_LEN); } buf_len = skb->len; mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_92d)); if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) { firstseg = true; lastseg = true; } if (firstseg) { if (rtlhal->earlymode_enable) { SET_TX_DESC_PKT_OFFSET(pdesc, 1); SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN + EM_HDR_LEN); if (ptcb_desc->empkt_num) { RT_TRACE(rtlpriv, COMP_SEND, DBG_LOUD, "Insert 8 byte.pTcb->EMPktNum:%d\n", ptcb_desc->empkt_num); _rtl92de_insert_emcontent(ptcb_desc, (u8 *)(skb->data)); } } else { SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); } /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) if (ptcb_desc->hw_rate < DESC92_RATE6M) ptcb_desc->hw_rate = DESC92_RATE6M; SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate); if (ptcb_desc->use_shortgi || ptcb_desc->use_shortpreamble) SET_TX_DESC_DATA_SHORTGI(pdesc, 1); if (rtlhal->macphymode == DUALMAC_DUALPHY && ptcb_desc->hw_rate == DESC92_RATEMCS7) SET_TX_DESC_DATA_SHORTGI(pdesc, 1); if (info->flags & IEEE80211_TX_CTL_AMPDU) { SET_TX_DESC_AGG_ENABLE(pdesc, 1); SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14); } SET_TX_DESC_SEQ(pdesc, seq_number); SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable && !ptcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_HW_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable || ptcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_CTS2SELF(pdesc, ((ptcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0)); /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) if (ptcb_desc->rts_rate < DESC92_RATE6M) ptcb_desc->rts_rate = DESC92_RATE6M; SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); SET_TX_DESC_RTS_BW(pdesc, 0); SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <= DESC92_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); if (bw_40) { if (ptcb_desc->packet_bw) { SET_TX_DESC_DATA_BW(pdesc, 1); SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3); } else { SET_TX_DESC_DATA_BW(pdesc, 0); SET_TX_DESC_TX_SUB_CARRIER(pdesc, mac->cur_40_prime_sc); } } else { SET_TX_DESC_DATA_BW(pdesc, 0); SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0); } SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb_len); if (sta) { u8 ampdu_density = sta->ht_cap.ampdu_density; SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density); } if (info->control.hw_key) { struct ieee80211_key_conf *keyconf; keyconf = info->control.hw_key; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: SET_TX_DESC_SEC_TYPE(pdesc, 0x1); break; case WLAN_CIPHER_SUITE_CCMP: SET_TX_DESC_SEC_TYPE(pdesc, 0x3); break; default: SET_TX_DESC_SEC_TYPE(pdesc, 0x0); break; } } SET_TX_DESC_PKT_ID(pdesc, 0); SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel); SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F); SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF); SET_TX_DESC_DISABLE_FB(pdesc, ptcb_desc->disable_ratefallback ? 1 : 0); SET_TX_DESC_USE_RATE(pdesc, ptcb_desc->use_driver_rate ? 1 : 0); /* Set TxRate and RTSRate in TxDesc */ /* This prevent Tx initial rate of new-coming packets */ /* from being overwritten by retried packet rate.*/ if (!ptcb_desc->use_driver_rate) { SET_TX_DESC_RTS_RATE(pdesc, 0x08); /* SET_TX_DESC_TX_RATE(pdesc, 0x0b); */ } if (ieee80211_is_data_qos(fc)) { if (mac->rdg_en) { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "Enable RDG function\n"); SET_TX_DESC_RDG_ENABLE(pdesc, 1); SET_TX_DESC_HTC(pdesc, 1); } } } SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0)); SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0)); SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) buf_len); SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); if (rtlpriv->dm.useramask) { SET_TX_DESC_RATE_ID(pdesc, ptcb_desc->ratr_index); SET_TX_DESC_MACID(pdesc, ptcb_desc->mac_id); } else { SET_TX_DESC_RATE_ID(pdesc, 0xC + ptcb_desc->ratr_index); SET_TX_DESC_MACID(pdesc, ptcb_desc->ratr_index); } if (ieee80211_is_data_qos(fc)) SET_TX_DESC_QOS(pdesc, 1); if ((!ieee80211_is_data_qos(fc)) && ppsc->fwctrl_lps) { SET_TX_DESC_HWSEQ_EN(pdesc, 1); SET_TX_DESC_PKT_ID(pdesc, 8); } SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1)); RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n"); } void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); u8 fw_queue = QSLT_BEACON; dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); if (firstseg) SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); /* 5G have no CCK rate * Caution: The macros below are multi-line expansions. * The braces are needed no matter what checkpatch says */ if (rtlhal->current_bandtype == BAND_ON_5G) { SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE6M); } else { SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); } SET_TX_DESC_SEQ(pdesc, 0); SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)skb->len); SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); SET_TX_DESC_RATE_ID(pdesc, 7); SET_TX_DESC_MACID(pdesc, 0); SET_TX_DESC_PKT_SIZE(pdesc, (u16) (skb->len)); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_OFFSET(pdesc, 0x20); SET_TX_DESC_USE_RATE(pdesc, 1); if (!ieee80211_is_data_qos(fc) && ppsc->fwctrl_lps) { SET_TX_DESC_HWSEQ_EN(pdesc, 1); SET_TX_DESC_PKT_ID(pdesc, 8); } RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, "H2C Tx Cmd Content", pdesc, TX_DESC_SIZE); wmb(); SET_TX_DESC_OWN(pdesc, 1); } void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) { if (istx) { switch (desc_name) { case HW_DESC_OWN: wmb(); SET_TX_DESC_OWN(pdesc, 1); break; case HW_DESC_TX_NEXTDESC_ADDR: SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *) val); break; default: RT_ASSERT(false, "ERR txdesc :%d not process\n", desc_name); break; } } else { switch (desc_name) { case HW_DESC_RXOWN: wmb(); SET_RX_DESC_OWN(pdesc, 1); break; case HW_DESC_RXBUFF_ADDR: SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *) val); break; case HW_DESC_RXPKT_LEN: SET_RX_DESC_PKT_LEN(pdesc, *(u32 *) val); break; case HW_DESC_RXERO: SET_RX_DESC_EOR(pdesc, 1); break; default: RT_ASSERT(false, "ERR rxdesc :%d not process\n", desc_name); break; } } } u32 rtl92de_get_desc(u8 *p_desc, bool istx, u8 desc_name) { u32 ret = 0; if (istx) { switch (desc_name) { case HW_DESC_OWN: ret = GET_TX_DESC_OWN(p_desc); break; case HW_DESC_TXBUFF_ADDR: ret = GET_TX_DESC_TX_BUFFER_ADDRESS(p_desc); break; default: RT_ASSERT(false, "ERR txdesc :%d not process\n", desc_name); break; } } else { struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc; switch (desc_name) { case HW_DESC_OWN: ret = GET_RX_DESC_OWN(pdesc); break; case HW_DESC_RXPKT_LEN: ret = GET_RX_DESC_PKT_LEN(pdesc); break; default: RT_ASSERT(false, "ERR rxdesc :%d not process\n", desc_name); break; } } return ret; } void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (hw_queue == BEACON_QUEUE) rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(4)); else rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(0) << (hw_queue)); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/table.h0000644000175000017500000000435512026211315025046 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * * Created on 2010/ 5/18, 1:41 *****************************************************************************/ #ifndef __RTL92DE_TABLE__H_ #define __RTL92DE_TABLE__H_ /*Created on 2011/ 1/14, 1:35*/ #define PHY_REG_2T_ARRAYLENGTH 380 extern u32 rtl8192de_phy_reg_2tarray[PHY_REG_2T_ARRAYLENGTH]; #define PHY_REG_ARRAY_PG_LENGTH 624 extern u32 rtl8192de_phy_reg_array_pg[PHY_REG_ARRAY_PG_LENGTH]; #define RADIOA_2T_ARRAYLENGTH 378 extern u32 rtl8192de_radioa_2tarray[RADIOA_2T_ARRAYLENGTH]; #define RADIOB_2T_ARRAYLENGTH 384 extern u32 rtl8192de_radiob_2tarray[RADIOB_2T_ARRAYLENGTH]; #define RADIOA_2T_INT_PA_ARRAYLENGTH 378 extern u32 rtl8192de_radioa_2t_int_paarray[RADIOA_2T_INT_PA_ARRAYLENGTH]; #define RADIOB_2T_INT_PA_ARRAYLENGTH 384 extern u32 rtl8192de_radiob_2t_int_paarray[RADIOB_2T_INT_PA_ARRAYLENGTH]; #define MAC_2T_ARRAYLENGTH 160 extern u32 rtl8192de_mac_2tarray[MAC_2T_ARRAYLENGTH]; #define AGCTAB_ARRAYLENGTH 386 extern u32 rtl8192de_agctab_array[AGCTAB_ARRAYLENGTH]; #define AGCTAB_5G_ARRAYLENGTH 194 extern u32 rtl8192de_agctab_5garray[AGCTAB_5G_ARRAYLENGTH]; #define AGCTAB_2G_ARRAYLENGTH 194 extern u32 rtl8192de_agctab_2garray[AGCTAB_2G_ARRAYLENGTH]; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/table.c0000644000175000017500000011006312026211315025033 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * * Created on 2010/12/23, 6:38 *****************************************************************************/ #include #include "table.h" u32 rtl8192de_phy_reg_2tarray[PHY_REG_2T_ARRAYLENGTH] = { 0x024, 0x0011800d, 0x028, 0x00ffdb83, 0x014, 0x088ba955, 0x010, 0x49022b03, 0x800, 0x80040002, 0x804, 0x00000003, 0x808, 0x0000fc00, 0x80c, 0x0000000a, 0x810, 0x80706388, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, 0x820, 0x01000100, 0x824, 0x00390004, 0x828, 0x01000100, 0x82c, 0x00390004, 0x830, 0x27272727, 0x834, 0x27272727, 0x838, 0x27272727, 0x83c, 0x27272727, 0x840, 0x00010000, 0x844, 0x00010000, 0x848, 0x27272727, 0x84c, 0x27272727, 0x850, 0x00000000, 0x854, 0x00000000, 0x858, 0x569a569a, 0x85c, 0x0c1b25a4, 0x860, 0x66e60230, 0x864, 0x061f0130, 0x868, 0x27272727, 0x86c, 0x272b2b2b, 0x870, 0x07000700, 0x874, 0x22188000, 0x878, 0x08080808, 0x87c, 0x00007ff8, 0x880, 0xc0083070, 0x884, 0x00000cd5, 0x888, 0x00000000, 0x88c, 0xcc0000c0, 0x890, 0x00000800, 0x894, 0xfffffffe, 0x898, 0x40302010, 0x89c, 0x00706050, 0x900, 0x00000000, 0x904, 0x00000023, 0x908, 0x00000000, 0x90c, 0x81121313, 0xa00, 0x00d047c8, 0xa04, 0x80ff000c, 0xa08, 0x8c838300, 0xa0c, 0x2e68120f, 0xa10, 0x9500bb78, 0xa14, 0x11144028, 0xa18, 0x00881117, 0xa1c, 0x89140f00, 0xa20, 0x1a1b0000, 0xa24, 0x090e1317, 0xa28, 0x00000204, 0xa2c, 0x00d30000, 0xa70, 0x101fbf00, 0xa74, 0x00000007, 0xc00, 0x40071d40, 0xc04, 0x03a05633, 0xc08, 0x001000e4, 0xc0c, 0x6c6c6c6c, 0xc10, 0x08800000, 0xc14, 0x40000100, 0xc18, 0x08800000, 0xc1c, 0x40000100, 0xc20, 0x00000000, 0xc24, 0x00000000, 0xc28, 0x00000000, 0xc2c, 0x00000000, 0xc30, 0x69e9ac44, 0xc34, 0x469652cf, 0xc38, 0x49795994, 0xc3c, 0x0a979718, 0xc40, 0x1f7c403f, 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, 0xc50, 0x69543420, 0xc54, 0x43bc009e, 0xc58, 0x69543420, 0xc5c, 0x433c00a8, 0xc60, 0x00000000, 0xc64, 0x5116848b, 0xc68, 0x47c00bff, 0xc6c, 0x00000036, 0xc70, 0x2c7f000d, 0xc74, 0x058610db, 0xc78, 0x0000001f, 0xc7c, 0x40b95612, 0xc80, 0x40000100, 0xc84, 0x20f60000, 0xc88, 0x40000100, 0xc8c, 0x20e00000, 0xc90, 0x00121820, 0xc94, 0x00000007, 0xc98, 0x00121820, 0xc9c, 0x00007f7f, 0xca0, 0x00000000, 0xca4, 0x00000080, 0xca8, 0x00000000, 0xcac, 0x00000000, 0xcb0, 0x00000000, 0xcb4, 0x00000000, 0xcb8, 0x00000000, 0xcbc, 0x28000000, 0xcc0, 0x00000000, 0xcc4, 0x00000000, 0xcc8, 0x00000000, 0xccc, 0x00000000, 0xcd0, 0x00000000, 0xcd4, 0x00000000, 0xcd8, 0x64b11e20, 0xcdc, 0xe8767533, 0xce0, 0x00222222, 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, 0xd00, 0x00080740, 0xd04, 0x00020403, 0xd08, 0x0000907f, 0xd0c, 0x20010201, 0xd10, 0xa0633333, 0xd14, 0x3333bc43, 0xd18, 0x7a8f5b6b, 0xd2c, 0xcc979975, 0xd30, 0x00000000, 0xd34, 0x80608404, 0xd38, 0x00000000, 0xd3c, 0x00027293, 0xd40, 0x00000000, 0xd44, 0x00000000, 0xd48, 0x00000000, 0xd4c, 0x00000000, 0xd50, 0x6437140a, 0xd54, 0x00000000, 0xd58, 0x00000000, 0xd5c, 0x30032064, 0xd60, 0x4653de68, 0xd64, 0x04518a3c, 0xd68, 0x00002101, 0xd6c, 0x2a201c16, 0xd70, 0x1812362e, 0xd74, 0x322c2220, 0xd78, 0x000e3c24, 0xe00, 0x2a2a2a2a, 0xe04, 0x2a2a2a2a, 0xe08, 0x03902a2a, 0xe10, 0x2a2a2a2a, 0xe14, 0x2a2a2a2a, 0xe18, 0x2a2a2a2a, 0xe1c, 0x2a2a2a2a, 0xe28, 0x00000000, 0xe30, 0x1000dc1f, 0xe34, 0x10008c1f, 0xe38, 0x02140102, 0xe3c, 0x681604c2, 0xe40, 0x01007c00, 0xe44, 0x01004800, 0xe48, 0xfb000000, 0xe4c, 0x000028d1, 0xe50, 0x1000dc1f, 0xe54, 0x10008c1f, 0xe58, 0x02140102, 0xe5c, 0x28160d05, 0xe60, 0x00000010, 0xe68, 0x001b25a4, 0xe6c, 0x63db25a4, 0xe70, 0x63db25a4, 0xe74, 0x0c126da4, 0xe78, 0x0c126da4, 0xe7c, 0x0c126da4, 0xe80, 0x0c126da4, 0xe84, 0x63db25a4, 0xe88, 0x0c126da4, 0xe8c, 0x63db25a4, 0xed0, 0x63db25a4, 0xed4, 0x63db25a4, 0xed8, 0x63db25a4, 0xedc, 0x001b25a4, 0xee0, 0x001b25a4, 0xeec, 0x6fdb25a4, 0xf14, 0x00000003, 0xf1c, 0x00000064, 0xf4c, 0x00000004, 0xf00, 0x00000300, }; u32 rtl8192de_phy_reg_array_pg[PHY_REG_ARRAY_PG_LENGTH] = { 0xe00, 0xffffffff, 0x07090c0c, 0xe04, 0xffffffff, 0x01020405, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x0b0c0c0e, 0xe14, 0xffffffff, 0x01030506, 0xe18, 0xffffffff, 0x0b0c0d0e, 0xe1c, 0xffffffff, 0x01030509, 0x830, 0xffffffff, 0x07090c0c, 0x834, 0xffffffff, 0x01020405, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x0b0c0c0e, 0x848, 0xffffffff, 0x01030506, 0x84c, 0xffffffff, 0x0b0c0d0e, 0x868, 0xffffffff, 0x01030509, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x06060606, 0xe14, 0xffffffff, 0x00020406, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x06060606, 0x848, 0xffffffff, 0x00020406, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x08080808, 0xe14, 0xffffffff, 0x00040408, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x08080808, 0x848, 0xffffffff, 0x00040408, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x08080808, 0xe14, 0xffffffff, 0x00040408, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x08080808, 0x848, 0xffffffff, 0x00040408, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x08080808, 0xe14, 0xffffffff, 0x00040408, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x08080808, 0x848, 0xffffffff, 0x00040408, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x08080808, 0xe14, 0xffffffff, 0x00040408, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x08080808, 0x848, 0xffffffff, 0x00040408, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x08080808, 0xe14, 0xffffffff, 0x00040408, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x08080808, 0x848, 0xffffffff, 0x00040408, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x08080808, 0xe14, 0xffffffff, 0x00040408, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x08080808, 0x848, 0xffffffff, 0x00040408, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, }; u32 rtl8192de_radioa_2tarray[RADIOA_2T_ARRAYLENGTH] = { 0x000, 0x00030000, 0x001, 0x00030000, 0x002, 0x00000000, 0x003, 0x00018c63, 0x004, 0x00018c63, 0x008, 0x00084000, 0x00b, 0x0001c000, 0x00e, 0x00018c67, 0x00f, 0x00000851, 0x014, 0x00021440, 0x018, 0x00017524, 0x019, 0x00000000, 0x01d, 0x000a1290, 0x023, 0x00001558, 0x01a, 0x00030a99, 0x01b, 0x00040b00, 0x01c, 0x000fc339, 0x03a, 0x000a57eb, 0x03b, 0x00020000, 0x03c, 0x000ff454, 0x020, 0x0000aa52, 0x021, 0x00054000, 0x040, 0x0000aa52, 0x041, 0x00014000, 0x025, 0x000803be, 0x026, 0x000fc638, 0x027, 0x00077c18, 0x028, 0x000de471, 0x029, 0x000d7110, 0x02a, 0x0008cb04, 0x02b, 0x0004128b, 0x02c, 0x00001840, 0x043, 0x0002444f, 0x044, 0x0001adb0, 0x045, 0x00056467, 0x046, 0x0008992c, 0x047, 0x0000452c, 0x048, 0x000f9c43, 0x049, 0x00002e0c, 0x04a, 0x000546eb, 0x04b, 0x0008966c, 0x04c, 0x0000dde9, 0x018, 0x00007401, 0x000, 0x00070000, 0x012, 0x000dc000, 0x012, 0x00090000, 0x012, 0x00051000, 0x012, 0x00012000, 0x013, 0x000287b7, 0x013, 0x000247ab, 0x013, 0x0002079f, 0x013, 0x0001c793, 0x013, 0x0001839b, 0x013, 0x00014392, 0x013, 0x0001019a, 0x013, 0x0000c191, 0x013, 0x00008194, 0x013, 0x000040a0, 0x013, 0x00000018, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x016, 0x000e1330, 0x016, 0x000a1330, 0x016, 0x00061330, 0x016, 0x00021330, 0x018, 0x00017524, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bc, 0x013, 0x000247b0, 0x013, 0x000203b4, 0x013, 0x0001c3a8, 0x013, 0x000181b4, 0x013, 0x000141a8, 0x013, 0x000100b0, 0x013, 0x0000c0a4, 0x013, 0x0000b02c, 0x013, 0x00004020, 0x013, 0x00000014, 0x015, 0x0000f4c3, 0x015, 0x0004f4c3, 0x015, 0x0008f4c3, 0x016, 0x000e085f, 0x016, 0x000a085f, 0x016, 0x0006085f, 0x016, 0x0002085f, 0x018, 0x00037524, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bc, 0x013, 0x000247b0, 0x013, 0x000203b4, 0x013, 0x0001c3a8, 0x013, 0x000181b4, 0x013, 0x000141a8, 0x013, 0x000100b0, 0x013, 0x0000c0a4, 0x013, 0x0000b02c, 0x013, 0x00004020, 0x013, 0x00000014, 0x015, 0x0000f4c3, 0x015, 0x0004f4c3, 0x015, 0x0008f4c3, 0x016, 0x000e085f, 0x016, 0x000a085f, 0x016, 0x0006085f, 0x016, 0x0002085f, 0x018, 0x00057568, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bc, 0x013, 0x000247b0, 0x013, 0x000203b4, 0x013, 0x0001c3a8, 0x013, 0x000181b4, 0x013, 0x000141a8, 0x013, 0x000100b0, 0x013, 0x0000c0a4, 0x013, 0x0000b02c, 0x013, 0x00004020, 0x013, 0x00000014, 0x015, 0x0000f4c3, 0x015, 0x0004f4c3, 0x015, 0x0008f4c3, 0x016, 0x000e085f, 0x016, 0x000a085f, 0x016, 0x0006085f, 0x016, 0x0002085f, 0x030, 0x0004470f, 0x031, 0x00044ff0, 0x032, 0x00000070, 0x033, 0x000dd480, 0x034, 0x000ffac0, 0x035, 0x000b80c0, 0x036, 0x00077000, 0x037, 0x00064ff2, 0x038, 0x000e7661, 0x039, 0x00000e90, 0x000, 0x00030000, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00088009, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x01e, 0x00088001, 0x01f, 0x00080000, 0x0fe, 0x00000000, 0x018, 0x00097524, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x02b, 0x00041289, 0x0fe, 0x00000000, 0x02d, 0x0006aaaa, 0x02e, 0x000b4d01, 0x02d, 0x00080000, 0x02e, 0x00004d02, 0x02d, 0x00095555, 0x02e, 0x00054d03, 0x02d, 0x000aaaaa, 0x02e, 0x000b4d04, 0x02d, 0x000c0000, 0x02e, 0x00004d05, 0x02d, 0x000d5555, 0x02e, 0x00054d06, 0x02d, 0x000eaaaa, 0x02e, 0x000b4d07, 0x02d, 0x00000000, 0x02e, 0x00005108, 0x02d, 0x00015555, 0x02e, 0x00055109, 0x02d, 0x0002aaaa, 0x02e, 0x000b510a, 0x02d, 0x00040000, 0x02e, 0x0000510b, 0x02d, 0x00055555, 0x02e, 0x0005510c, }; u32 rtl8192de_radiob_2tarray[RADIOB_2T_ARRAYLENGTH] = { 0x000, 0x00030000, 0x001, 0x00030000, 0x002, 0x00000000, 0x003, 0x00018c63, 0x004, 0x00018c63, 0x008, 0x00084000, 0x00b, 0x0001c000, 0x00e, 0x00018c67, 0x00f, 0x00000851, 0x014, 0x00021440, 0x018, 0x00007401, 0x019, 0x00000060, 0x01d, 0x000a1290, 0x023, 0x00001558, 0x01a, 0x00030a99, 0x01b, 0x00040b00, 0x01c, 0x000fc339, 0x03a, 0x000a57eb, 0x03b, 0x00020000, 0x03c, 0x000ff454, 0x020, 0x0000aa52, 0x021, 0x00054000, 0x040, 0x0000aa52, 0x041, 0x00014000, 0x025, 0x000803be, 0x026, 0x000fc638, 0x027, 0x00077c18, 0x028, 0x000d1c31, 0x029, 0x000d7110, 0x02a, 0x000aeb04, 0x02b, 0x0004128b, 0x02c, 0x00001840, 0x043, 0x0002444f, 0x044, 0x0001adb0, 0x045, 0x00056467, 0x046, 0x0008992c, 0x047, 0x0000452c, 0x048, 0x000f9c43, 0x049, 0x00002e0c, 0x04a, 0x000546eb, 0x04b, 0x0008966c, 0x04c, 0x0000dde9, 0x018, 0x00007401, 0x000, 0x00070000, 0x012, 0x000dc000, 0x012, 0x00090000, 0x012, 0x00051000, 0x012, 0x00012000, 0x013, 0x000287b7, 0x013, 0x000247ab, 0x013, 0x0002079f, 0x013, 0x0001c793, 0x013, 0x0001839b, 0x013, 0x00014392, 0x013, 0x0001019a, 0x013, 0x0000c191, 0x013, 0x00008194, 0x013, 0x000040a0, 0x013, 0x00000018, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x016, 0x000e1330, 0x016, 0x000a1330, 0x016, 0x00061330, 0x016, 0x00021330, 0x018, 0x00017524, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bc, 0x013, 0x000247b0, 0x013, 0x000203b4, 0x013, 0x0001c3a8, 0x013, 0x000181b4, 0x013, 0x000141a8, 0x013, 0x000100b0, 0x013, 0x0000c0a4, 0x013, 0x0000b02c, 0x013, 0x00004020, 0x013, 0x00000014, 0x015, 0x0000f4c3, 0x015, 0x0004f4c3, 0x015, 0x0008f4c3, 0x016, 0x000e085f, 0x016, 0x000a085f, 0x016, 0x0006085f, 0x016, 0x0002085f, 0x018, 0x00037524, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bc, 0x013, 0x000247b0, 0x013, 0x000203b4, 0x013, 0x0001c3a8, 0x013, 0x000181b4, 0x013, 0x000141a8, 0x013, 0x000100b0, 0x013, 0x0000c0a4, 0x013, 0x0000b02c, 0x013, 0x00004020, 0x013, 0x00000014, 0x015, 0x0000f4c3, 0x015, 0x0004f4c3, 0x015, 0x0008f4c3, 0x016, 0x000e085f, 0x016, 0x000a085f, 0x016, 0x0006085f, 0x016, 0x0002085f, 0x018, 0x00057524, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bc, 0x013, 0x000247b0, 0x013, 0x000203b4, 0x013, 0x0001c3a8, 0x013, 0x000181b4, 0x013, 0x000141a8, 0x013, 0x000100b0, 0x013, 0x0000c0a4, 0x013, 0x0000b02c, 0x013, 0x00004020, 0x013, 0x00000014, 0x015, 0x0000f4c3, 0x015, 0x0004f4c3, 0x015, 0x0008f4c3, 0x016, 0x000e085f, 0x016, 0x000a085f, 0x016, 0x0006085f, 0x016, 0x0002085f, 0x030, 0x0004470f, 0x031, 0x00044ff0, 0x032, 0x00000070, 0x033, 0x000dd480, 0x034, 0x000ffac0, 0x035, 0x000b80c0, 0x036, 0x00077000, 0x037, 0x00064ff2, 0x038, 0x000e7661, 0x039, 0x00000e90, 0x000, 0x00030000, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00088009, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x01e, 0x00088001, 0x01f, 0x00080000, 0x0fe, 0x00000000, 0x018, 0x00087401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x02b, 0x00041289, 0x0fe, 0x00000000, 0x02d, 0x00066666, 0x02e, 0x00064001, 0x02d, 0x00091111, 0x02e, 0x00014002, 0x02d, 0x000bbbbb, 0x02e, 0x000b4003, 0x02d, 0x000e6666, 0x02e, 0x00064004, 0x02d, 0x00088888, 0x02e, 0x00084005, 0x02d, 0x0009dddd, 0x02e, 0x000d4006, 0x02d, 0x000b3333, 0x02e, 0x00034007, 0x02d, 0x00048888, 0x02e, 0x00084408, 0x02d, 0x000bbbbb, 0x02e, 0x000b4409, 0x02d, 0x000e6666, 0x02e, 0x0006440a, 0x02d, 0x00011111, 0x02e, 0x0001480b, 0x02d, 0x0003bbbb, 0x02e, 0x000b480c, 0x02d, 0x00066666, 0x02e, 0x0006480d, 0x02d, 0x000ccccc, 0x02e, 0x000c480e, }; u32 rtl8192de_radioa_2t_int_paarray[RADIOA_2T_INT_PA_ARRAYLENGTH] = { 0x000, 0x00030000, 0x001, 0x00030000, 0x002, 0x00000000, 0x003, 0x00018c63, 0x004, 0x00018c63, 0x008, 0x00084000, 0x00b, 0x0001c000, 0x00e, 0x00018c67, 0x00f, 0x00000851, 0x014, 0x00021440, 0x018, 0x00017524, 0x019, 0x00000000, 0x01d, 0x000a1290, 0x023, 0x00001558, 0x01a, 0x00030a99, 0x01b, 0x00040b00, 0x01c, 0x000fc339, 0x03a, 0x000a57eb, 0x03b, 0x00020000, 0x03c, 0x000ff454, 0x020, 0x0000aa52, 0x021, 0x00054000, 0x040, 0x0000aa52, 0x041, 0x00014000, 0x025, 0x000803be, 0x026, 0x000fc638, 0x027, 0x00077c18, 0x028, 0x000de471, 0x029, 0x000d7110, 0x02a, 0x0008eb04, 0x02b, 0x0004128b, 0x02c, 0x00001840, 0x043, 0x0002444f, 0x044, 0x0001adb0, 0x045, 0x00056467, 0x046, 0x0008992c, 0x047, 0x0000452c, 0x048, 0x000c0443, 0x049, 0x00000730, 0x04a, 0x00050f0f, 0x04b, 0x000896ee, 0x04c, 0x0000ddee, 0x018, 0x00007401, 0x000, 0x00070000, 0x012, 0x000dc000, 0x012, 0x00090000, 0x012, 0x00051000, 0x012, 0x00012000, 0x013, 0x000287b7, 0x013, 0x000247ab, 0x013, 0x0002079f, 0x013, 0x0001c793, 0x013, 0x0001839b, 0x013, 0x00014392, 0x013, 0x0001019a, 0x013, 0x0000c191, 0x013, 0x00008194, 0x013, 0x000040a0, 0x013, 0x00000018, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x016, 0x000e1330, 0x016, 0x000a1330, 0x016, 0x00061330, 0x016, 0x00021330, 0x018, 0x00017524, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bf, 0x013, 0x000247b3, 0x013, 0x000207a7, 0x013, 0x0001c79b, 0x013, 0x0001839f, 0x013, 0x00014393, 0x013, 0x00010399, 0x013, 0x0000c38d, 0x013, 0x00008199, 0x013, 0x0000418d, 0x013, 0x00000099, 0x015, 0x0000f495, 0x015, 0x0004f495, 0x015, 0x0008f495, 0x016, 0x000e1874, 0x016, 0x000a1874, 0x016, 0x00061874, 0x016, 0x00021874, 0x018, 0x00037564, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bf, 0x013, 0x000247b3, 0x013, 0x000207a7, 0x013, 0x0001c79b, 0x013, 0x0001839f, 0x013, 0x00014393, 0x013, 0x00010399, 0x013, 0x0000c38d, 0x013, 0x00008199, 0x013, 0x0000418d, 0x013, 0x00000099, 0x015, 0x0000f495, 0x015, 0x0004f495, 0x015, 0x0008f495, 0x016, 0x000e1874, 0x016, 0x000a1874, 0x016, 0x00061874, 0x016, 0x00021874, 0x018, 0x00057595, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bf, 0x013, 0x000247b3, 0x013, 0x000207a7, 0x013, 0x0001c79b, 0x013, 0x0001839f, 0x013, 0x00014393, 0x013, 0x00010399, 0x013, 0x0000c38d, 0x013, 0x00008199, 0x013, 0x0000418d, 0x013, 0x00000099, 0x015, 0x0000f495, 0x015, 0x0004f495, 0x015, 0x0008f495, 0x016, 0x000e1874, 0x016, 0x000a1874, 0x016, 0x00061874, 0x016, 0x00021874, 0x030, 0x0004470f, 0x031, 0x00044ff0, 0x032, 0x00000070, 0x033, 0x000dd480, 0x034, 0x000ffac0, 0x035, 0x000b80c0, 0x036, 0x00077000, 0x037, 0x00064ff2, 0x038, 0x000e7661, 0x039, 0x00000e90, 0x000, 0x00030000, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00088009, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x01e, 0x00088001, 0x01f, 0x00080000, 0x0fe, 0x00000000, 0x018, 0x00097524, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x02b, 0x00041289, 0x0fe, 0x00000000, 0x02d, 0x0006aaaa, 0x02e, 0x000b4d01, 0x02d, 0x00080000, 0x02e, 0x00004d02, 0x02d, 0x00095555, 0x02e, 0x00054d03, 0x02d, 0x000aaaaa, 0x02e, 0x000b4d04, 0x02d, 0x000c0000, 0x02e, 0x00004d05, 0x02d, 0x000d5555, 0x02e, 0x00054d06, 0x02d, 0x000eaaaa, 0x02e, 0x000b4d07, 0x02d, 0x00000000, 0x02e, 0x00005108, 0x02d, 0x00015555, 0x02e, 0x00055109, 0x02d, 0x0002aaaa, 0x02e, 0x000b510a, 0x02d, 0x00040000, 0x02e, 0x0000510b, 0x02d, 0x00055555, 0x02e, 0x0005510c, }; u32 rtl8192de_radiob_2t_int_paarray[RADIOB_2T_INT_PA_ARRAYLENGTH] = { 0x000, 0x00030000, 0x001, 0x00030000, 0x002, 0x00000000, 0x003, 0x00018c63, 0x004, 0x00018c63, 0x008, 0x00084000, 0x00b, 0x0001c000, 0x00e, 0x00018c67, 0x00f, 0x00000851, 0x014, 0x00021440, 0x018, 0x00007401, 0x019, 0x00000060, 0x01d, 0x000a1290, 0x023, 0x00001558, 0x01a, 0x00030a99, 0x01b, 0x00040b00, 0x01c, 0x000fc339, 0x03a, 0x000a57eb, 0x03b, 0x00020000, 0x03c, 0x000ff454, 0x020, 0x0000aa52, 0x021, 0x00054000, 0x040, 0x0000aa52, 0x041, 0x00014000, 0x025, 0x000803be, 0x026, 0x000fc638, 0x027, 0x00077c18, 0x028, 0x000d1c31, 0x029, 0x000d7110, 0x02a, 0x000aeb04, 0x02b, 0x0004128b, 0x02c, 0x00001840, 0x043, 0x0002444f, 0x044, 0x0001adb0, 0x045, 0x00056467, 0x046, 0x0008992c, 0x047, 0x0000452c, 0x048, 0x000c0443, 0x049, 0x00000730, 0x04a, 0x00050f0f, 0x04b, 0x000896ee, 0x04c, 0x0000ddee, 0x018, 0x00007401, 0x000, 0x00070000, 0x012, 0x000dc000, 0x012, 0x00090000, 0x012, 0x00051000, 0x012, 0x00012000, 0x013, 0x000287b7, 0x013, 0x000247ab, 0x013, 0x0002079f, 0x013, 0x0001c793, 0x013, 0x0001839b, 0x013, 0x00014392, 0x013, 0x0001019a, 0x013, 0x0000c191, 0x013, 0x00008194, 0x013, 0x000040a0, 0x013, 0x00000018, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x016, 0x000e1330, 0x016, 0x000a1330, 0x016, 0x00061330, 0x016, 0x00021330, 0x018, 0x00017524, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bf, 0x013, 0x000247b3, 0x013, 0x000207a7, 0x013, 0x0001c79b, 0x013, 0x0001839f, 0x013, 0x00014393, 0x013, 0x00010399, 0x013, 0x0000c38d, 0x013, 0x00008199, 0x013, 0x0000418d, 0x013, 0x00000099, 0x015, 0x0000f495, 0x015, 0x0004f495, 0x015, 0x0008f495, 0x016, 0x000e1874, 0x016, 0x000a1874, 0x016, 0x00061874, 0x016, 0x00021874, 0x018, 0x00037564, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bf, 0x013, 0x000247b3, 0x013, 0x000207a7, 0x013, 0x0001c79b, 0x013, 0x0001839f, 0x013, 0x00014393, 0x013, 0x00010399, 0x013, 0x0000c38d, 0x013, 0x00008199, 0x013, 0x0000418d, 0x013, 0x00000099, 0x015, 0x0000f495, 0x015, 0x0004f495, 0x015, 0x0008f495, 0x016, 0x000e1874, 0x016, 0x000a1874, 0x016, 0x00061874, 0x016, 0x00021874, 0x018, 0x00057595, 0x000, 0x00070000, 0x012, 0x000cf000, 0x012, 0x000bc000, 0x012, 0x00078000, 0x012, 0x00000000, 0x013, 0x000287bf, 0x013, 0x000247b3, 0x013, 0x000207a7, 0x013, 0x0001c79b, 0x013, 0x0001839f, 0x013, 0x00014393, 0x013, 0x00010399, 0x013, 0x0000c38d, 0x013, 0x00008199, 0x013, 0x0000418d, 0x013, 0x00000099, 0x015, 0x0000f495, 0x015, 0x0004f495, 0x015, 0x0008f495, 0x016, 0x000e1874, 0x016, 0x000a1874, 0x016, 0x00061874, 0x016, 0x00021874, 0x030, 0x0004470f, 0x031, 0x00044ff0, 0x032, 0x00000070, 0x033, 0x000dd480, 0x034, 0x000ffac0, 0x035, 0x000b80c0, 0x036, 0x00077000, 0x037, 0x00064ff2, 0x038, 0x000e7661, 0x039, 0x00000e90, 0x000, 0x00030000, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00088009, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x01e, 0x00088001, 0x01f, 0x00080000, 0x0fe, 0x00000000, 0x018, 0x00087401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x02b, 0x00041289, 0x0fe, 0x00000000, 0x02d, 0x00066666, 0x02e, 0x00064001, 0x02d, 0x00091111, 0x02e, 0x00014002, 0x02d, 0x000bbbbb, 0x02e, 0x000b4003, 0x02d, 0x000e6666, 0x02e, 0x00064004, 0x02d, 0x00088888, 0x02e, 0x00084005, 0x02d, 0x0009dddd, 0x02e, 0x000d4006, 0x02d, 0x000b3333, 0x02e, 0x00034007, 0x02d, 0x00048888, 0x02e, 0x00084408, 0x02d, 0x000bbbbb, 0x02e, 0x000b4409, 0x02d, 0x000e6666, 0x02e, 0x0006440a, 0x02d, 0x00011111, 0x02e, 0x0001480b, 0x02d, 0x0003bbbb, 0x02e, 0x000b480c, 0x02d, 0x00066666, 0x02e, 0x0006480d, 0x02d, 0x000ccccc, 0x02e, 0x000c480e, }; u32 rtl8192de_mac_2tarray[MAC_2T_ARRAYLENGTH] = { 0x420, 0x00000080, 0x423, 0x00000000, 0x430, 0x00000000, 0x431, 0x00000000, 0x432, 0x00000000, 0x433, 0x00000001, 0x434, 0x00000004, 0x435, 0x00000005, 0x436, 0x00000006, 0x437, 0x00000007, 0x438, 0x00000000, 0x439, 0x00000000, 0x43a, 0x00000000, 0x43b, 0x00000001, 0x43c, 0x00000004, 0x43d, 0x00000005, 0x43e, 0x00000006, 0x43f, 0x00000007, 0x440, 0x00000050, 0x441, 0x00000001, 0x442, 0x00000000, 0x444, 0x00000015, 0x445, 0x000000f0, 0x446, 0x0000000f, 0x447, 0x00000000, 0x462, 0x00000008, 0x463, 0x00000003, 0x4c8, 0x000000ff, 0x4c9, 0x00000008, 0x4cc, 0x000000ff, 0x4cd, 0x000000ff, 0x4ce, 0x00000001, 0x500, 0x00000026, 0x501, 0x000000a2, 0x502, 0x0000002f, 0x503, 0x00000000, 0x504, 0x00000028, 0x505, 0x000000a3, 0x506, 0x0000005e, 0x507, 0x00000000, 0x508, 0x0000002b, 0x509, 0x000000a4, 0x50a, 0x0000005e, 0x50b, 0x00000000, 0x50c, 0x0000004f, 0x50d, 0x000000a4, 0x50e, 0x00000000, 0x50f, 0x00000000, 0x512, 0x0000001c, 0x514, 0x0000000a, 0x515, 0x00000010, 0x516, 0x0000000a, 0x517, 0x00000010, 0x51a, 0x00000016, 0x524, 0x0000000f, 0x525, 0x0000004f, 0x546, 0x00000040, 0x547, 0x00000000, 0x550, 0x00000010, 0x551, 0x00000010, 0x559, 0x00000002, 0x55a, 0x00000002, 0x55d, 0x000000ff, 0x605, 0x00000030, 0x608, 0x0000000e, 0x609, 0x0000002a, 0x652, 0x00000020, 0x63c, 0x0000000a, 0x63d, 0x0000000a, 0x63e, 0x0000000e, 0x63f, 0x0000000e, 0x66e, 0x00000005, 0x700, 0x00000021, 0x701, 0x00000043, 0x702, 0x00000065, 0x703, 0x00000087, 0x708, 0x00000021, 0x709, 0x00000043, 0x70a, 0x00000065, 0x70b, 0x00000087, }; u32 rtl8192de_agctab_array[AGCTAB_ARRAYLENGTH] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7b020001, 0xc78, 0x7b030001, 0xc78, 0x7b040001, 0xc78, 0x7b050001, 0xc78, 0x7b060001, 0xc78, 0x7a070001, 0xc78, 0x79080001, 0xc78, 0x78090001, 0xc78, 0x770a0001, 0xc78, 0x760b0001, 0xc78, 0x750c0001, 0xc78, 0x740d0001, 0xc78, 0x730e0001, 0xc78, 0x720f0001, 0xc78, 0x71100001, 0xc78, 0x70110001, 0xc78, 0x6f120001, 0xc78, 0x6e130001, 0xc78, 0x6d140001, 0xc78, 0x6c150001, 0xc78, 0x6b160001, 0xc78, 0x6a170001, 0xc78, 0x69180001, 0xc78, 0x68190001, 0xc78, 0x671a0001, 0xc78, 0x661b0001, 0xc78, 0x651c0001, 0xc78, 0x641d0001, 0xc78, 0x631e0001, 0xc78, 0x621f0001, 0xc78, 0x61200001, 0xc78, 0x60210001, 0xc78, 0x49220001, 0xc78, 0x48230001, 0xc78, 0x47240001, 0xc78, 0x46250001, 0xc78, 0x45260001, 0xc78, 0x44270001, 0xc78, 0x43280001, 0xc78, 0x42290001, 0xc78, 0x412a0001, 0xc78, 0x402b0001, 0xc78, 0x262c0001, 0xc78, 0x252d0001, 0xc78, 0x242e0001, 0xc78, 0x232f0001, 0xc78, 0x22300001, 0xc78, 0x21310001, 0xc78, 0x20320001, 0xc78, 0x06330001, 0xc78, 0x05340001, 0xc78, 0x04350001, 0xc78, 0x03360001, 0xc78, 0x02370001, 0xc78, 0x01380001, 0xc78, 0x00390001, 0xc78, 0x003a0001, 0xc78, 0x003b0001, 0xc78, 0x003c0001, 0xc78, 0x003d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x7b400001, 0xc78, 0x7b410001, 0xc78, 0x7a420001, 0xc78, 0x79430001, 0xc78, 0x78440001, 0xc78, 0x77450001, 0xc78, 0x76460001, 0xc78, 0x75470001, 0xc78, 0x74480001, 0xc78, 0x73490001, 0xc78, 0x724a0001, 0xc78, 0x714b0001, 0xc78, 0x704c0001, 0xc78, 0x6f4d0001, 0xc78, 0x6e4e0001, 0xc78, 0x6d4f0001, 0xc78, 0x6c500001, 0xc78, 0x6b510001, 0xc78, 0x6a520001, 0xc78, 0x69530001, 0xc78, 0x68540001, 0xc78, 0x67550001, 0xc78, 0x66560001, 0xc78, 0x65570001, 0xc78, 0x64580001, 0xc78, 0x63590001, 0xc78, 0x625a0001, 0xc78, 0x615b0001, 0xc78, 0x605c0001, 0xc78, 0x485d0001, 0xc78, 0x475e0001, 0xc78, 0x465f0001, 0xc78, 0x45600001, 0xc78, 0x44610001, 0xc78, 0x43620001, 0xc78, 0x42630001, 0xc78, 0x41640001, 0xc78, 0x40650001, 0xc78, 0x27660001, 0xc78, 0x26670001, 0xc78, 0x25680001, 0xc78, 0x24690001, 0xc78, 0x236a0001, 0xc78, 0x226b0001, 0xc78, 0x216c0001, 0xc78, 0x206d0001, 0xc78, 0x206e0001, 0xc78, 0x206f0001, 0xc78, 0x20700001, 0xc78, 0x20710001, 0xc78, 0x20720001, 0xc78, 0x20730001, 0xc78, 0x20740001, 0xc78, 0x20750001, 0xc78, 0x20760001, 0xc78, 0x20770001, 0xc78, 0x20780001, 0xc78, 0x20790001, 0xc78, 0x207a0001, 0xc78, 0x207b0001, 0xc78, 0x207c0001, 0xc78, 0x207d0001, 0xc78, 0x207e0001, 0xc78, 0x207f0001, 0xc78, 0x38000002, 0xc78, 0x38010002, 0xc78, 0x38020002, 0xc78, 0x38030002, 0xc78, 0x38040002, 0xc78, 0x38050002, 0xc78, 0x38060002, 0xc78, 0x38070002, 0xc78, 0x38080002, 0xc78, 0x3c090002, 0xc78, 0x3e0a0002, 0xc78, 0x400b0002, 0xc78, 0x440c0002, 0xc78, 0x480d0002, 0xc78, 0x4c0e0002, 0xc78, 0x500f0002, 0xc78, 0x52100002, 0xc78, 0x56110002, 0xc78, 0x5a120002, 0xc78, 0x5e130002, 0xc78, 0x60140002, 0xc78, 0x60150002, 0xc78, 0x60160002, 0xc78, 0x62170002, 0xc78, 0x62180002, 0xc78, 0x62190002, 0xc78, 0x621a0002, 0xc78, 0x621b0002, 0xc78, 0x621c0002, 0xc78, 0x621d0002, 0xc78, 0x621e0002, 0xc78, 0x621f0002, 0xc78, 0x32000044, 0xc78, 0x32010044, 0xc78, 0x32020044, 0xc78, 0x32030044, 0xc78, 0x32040044, 0xc78, 0x32050044, 0xc78, 0x32060044, 0xc78, 0x32070044, 0xc78, 0x32080044, 0xc78, 0x34090044, 0xc78, 0x350a0044, 0xc78, 0x360b0044, 0xc78, 0x370c0044, 0xc78, 0x380d0044, 0xc78, 0x390e0044, 0xc78, 0x3a0f0044, 0xc78, 0x3e100044, 0xc78, 0x42110044, 0xc78, 0x44120044, 0xc78, 0x46130044, 0xc78, 0x4a140044, 0xc78, 0x4e150044, 0xc78, 0x50160044, 0xc78, 0x55170044, 0xc78, 0x5a180044, 0xc78, 0x5e190044, 0xc78, 0x641a0044, 0xc78, 0x6e1b0044, 0xc78, 0x6e1c0044, 0xc78, 0x6e1d0044, 0xc78, 0x6e1e0044, 0xc78, 0x6e1f0044, 0xc78, 0x6e1f0000, }; u32 rtl8192de_agctab_5garray[AGCTAB_5G_ARRAYLENGTH] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7a020001, 0xc78, 0x79030001, 0xc78, 0x78040001, 0xc78, 0x77050001, 0xc78, 0x76060001, 0xc78, 0x75070001, 0xc78, 0x74080001, 0xc78, 0x73090001, 0xc78, 0x720a0001, 0xc78, 0x710b0001, 0xc78, 0x700c0001, 0xc78, 0x6f0d0001, 0xc78, 0x6e0e0001, 0xc78, 0x6d0f0001, 0xc78, 0x6c100001, 0xc78, 0x6b110001, 0xc78, 0x6a120001, 0xc78, 0x69130001, 0xc78, 0x68140001, 0xc78, 0x67150001, 0xc78, 0x66160001, 0xc78, 0x65170001, 0xc78, 0x64180001, 0xc78, 0x63190001, 0xc78, 0x621a0001, 0xc78, 0x611b0001, 0xc78, 0x601c0001, 0xc78, 0x481d0001, 0xc78, 0x471e0001, 0xc78, 0x461f0001, 0xc78, 0x45200001, 0xc78, 0x44210001, 0xc78, 0x43220001, 0xc78, 0x42230001, 0xc78, 0x41240001, 0xc78, 0x40250001, 0xc78, 0x27260001, 0xc78, 0x26270001, 0xc78, 0x25280001, 0xc78, 0x24290001, 0xc78, 0x232a0001, 0xc78, 0x222b0001, 0xc78, 0x212c0001, 0xc78, 0x202d0001, 0xc78, 0x202e0001, 0xc78, 0x202f0001, 0xc78, 0x20300001, 0xc78, 0x20310001, 0xc78, 0x20320001, 0xc78, 0x20330001, 0xc78, 0x20340001, 0xc78, 0x20350001, 0xc78, 0x20360001, 0xc78, 0x20370001, 0xc78, 0x20380001, 0xc78, 0x20390001, 0xc78, 0x203a0001, 0xc78, 0x203b0001, 0xc78, 0x203c0001, 0xc78, 0x203d0001, 0xc78, 0x203e0001, 0xc78, 0x203f0001, 0xc78, 0x32000044, 0xc78, 0x32010044, 0xc78, 0x32020044, 0xc78, 0x32030044, 0xc78, 0x32040044, 0xc78, 0x32050044, 0xc78, 0x32060044, 0xc78, 0x32070044, 0xc78, 0x32080044, 0xc78, 0x34090044, 0xc78, 0x350a0044, 0xc78, 0x360b0044, 0xc78, 0x370c0044, 0xc78, 0x380d0044, 0xc78, 0x390e0044, 0xc78, 0x3a0f0044, 0xc78, 0x3e100044, 0xc78, 0x42110044, 0xc78, 0x44120044, 0xc78, 0x46130044, 0xc78, 0x4a140044, 0xc78, 0x4e150044, 0xc78, 0x50160044, 0xc78, 0x55170044, 0xc78, 0x5a180044, 0xc78, 0x5e190044, 0xc78, 0x641a0044, 0xc78, 0x6e1b0044, 0xc78, 0x6e1c0044, 0xc78, 0x6e1d0044, 0xc78, 0x6e1e0044, 0xc78, 0x6e1f0044, 0xc78, 0x6e1f0000, }; u32 rtl8192de_agctab_2garray[AGCTAB_2G_ARRAYLENGTH] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7b020001, 0xc78, 0x7b030001, 0xc78, 0x7b040001, 0xc78, 0x7b050001, 0xc78, 0x7b060001, 0xc78, 0x7a070001, 0xc78, 0x79080001, 0xc78, 0x78090001, 0xc78, 0x770a0001, 0xc78, 0x760b0001, 0xc78, 0x750c0001, 0xc78, 0x740d0001, 0xc78, 0x730e0001, 0xc78, 0x720f0001, 0xc78, 0x71100001, 0xc78, 0x70110001, 0xc78, 0x6f120001, 0xc78, 0x6e130001, 0xc78, 0x6d140001, 0xc78, 0x6c150001, 0xc78, 0x6b160001, 0xc78, 0x6a170001, 0xc78, 0x69180001, 0xc78, 0x68190001, 0xc78, 0x671a0001, 0xc78, 0x661b0001, 0xc78, 0x651c0001, 0xc78, 0x641d0001, 0xc78, 0x631e0001, 0xc78, 0x621f0001, 0xc78, 0x61200001, 0xc78, 0x60210001, 0xc78, 0x49220001, 0xc78, 0x48230001, 0xc78, 0x47240001, 0xc78, 0x46250001, 0xc78, 0x45260001, 0xc78, 0x44270001, 0xc78, 0x43280001, 0xc78, 0x42290001, 0xc78, 0x412a0001, 0xc78, 0x402b0001, 0xc78, 0x262c0001, 0xc78, 0x252d0001, 0xc78, 0x242e0001, 0xc78, 0x232f0001, 0xc78, 0x22300001, 0xc78, 0x21310001, 0xc78, 0x20320001, 0xc78, 0x06330001, 0xc78, 0x05340001, 0xc78, 0x04350001, 0xc78, 0x03360001, 0xc78, 0x02370001, 0xc78, 0x01380001, 0xc78, 0x00390001, 0xc78, 0x003a0001, 0xc78, 0x003b0001, 0xc78, 0x003c0001, 0xc78, 0x003d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x38000002, 0xc78, 0x38010002, 0xc78, 0x38020002, 0xc78, 0x38030002, 0xc78, 0x38040002, 0xc78, 0x38050002, 0xc78, 0x38060002, 0xc78, 0x38070002, 0xc78, 0x38080002, 0xc78, 0x3c090002, 0xc78, 0x3e0a0002, 0xc78, 0x400b0002, 0xc78, 0x440c0002, 0xc78, 0x480d0002, 0xc78, 0x4c0e0002, 0xc78, 0x500f0002, 0xc78, 0x52100002, 0xc78, 0x56110002, 0xc78, 0x5a120002, 0xc78, 0x5e130002, 0xc78, 0x60140002, 0xc78, 0x60150002, 0xc78, 0x60160002, 0xc78, 0x62170002, 0xc78, 0x62180002, 0xc78, 0x62190002, 0xc78, 0x621a0002, 0xc78, 0x621b0002, 0xc78, 0x621c0002, 0xc78, 0x621d0002, 0xc78, 0x621e0002, 0xc78, 0x621f0002, 0xc78, 0x6e1f0000, }; compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/sw.h0000644000175000017500000000253312026211315024404 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92DE_SW_H__ #define __RTL92DE_SW_H__ extern spinlock_t globalmutex_power; extern spinlock_t globalmutex_for_fwdownload; extern spinlock_t globalmutex_for_power_and_efuse; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/rf.h0000644000175000017500000000335712026211315024367 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92D_RF_H__ #define __RTL92D_RF_H__ extern void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); extern void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel); extern void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel); extern bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw); extern bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0); extern void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, bool bmac0); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/rf.c0000644000175000017500000004552712026211315024367 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "hw.h" void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 rfpath; switch (bandwidth) { case HT_CHANNEL_WIDTH_20: for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { rtlphy->rfreg_chnlval[rfpath] = ((rtlphy->rfreg_chnlval [rfpath] & 0xfffff3ff) | 0x0400); rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(10) | BIT(11), 0x01); RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "20M RF 0x18 = 0x%x\n", rtlphy->rfreg_chnlval[rfpath]); } break; case HT_CHANNEL_WIDTH_20_40: for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { rtlphy->rfreg_chnlval[rfpath] = ((rtlphy->rfreg_chnlval[rfpath] & 0xfffff3ff)); rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(10) | BIT(11), 0x00); RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "40M RF 0x18 = 0x%x\n", rtlphy->rfreg_chnlval[rfpath]); } break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", bandwidth); break; } } void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 tx_agc[2] = {0, 0}, tmpval; bool turbo_scanoff = false; u8 idx1, idx2; u8 *ptr; if (rtlefuse->eeprom_regulatory != 0) turbo_scanoff = true; if (mac->act_scanning) { tx_agc[RF90_PATH_A] = 0x3f3f3f3f; tx_agc[RF90_PATH_B] = 0x3f3f3f3f; if (turbo_scanoff) { for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { tx_agc[idx1] = ppowerlevel[idx1] | (ppowerlevel[idx1] << 8) | (ppowerlevel[idx1] << 16) | (ppowerlevel[idx1] << 24); } } } else { for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { tx_agc[idx1] = ppowerlevel[idx1] | (ppowerlevel[idx1] << 8) | (ppowerlevel[idx1] << 16) | (ppowerlevel[idx1] << 24); } if (rtlefuse->eeprom_regulatory == 0) { tmpval = (rtlphy->mcs_txpwrlevel_origoffset[0][6]) + (rtlphy->mcs_txpwrlevel_origoffset[0][7] << 8); tx_agc[RF90_PATH_A] += tmpval; tmpval = (rtlphy->mcs_txpwrlevel_origoffset[0][14]) + (rtlphy->mcs_txpwrlevel_origoffset[0][15] << 24); tx_agc[RF90_PATH_B] += tmpval; } } for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { ptr = (u8 *) (&(tx_agc[idx1])); for (idx2 = 0; idx2 < 4; idx2++) { if (*ptr > RF6052_MAX_TX_PWR) *ptr = RF6052_MAX_TX_PWR; ptr++; } } tmpval = tx_agc[RF90_PATH_A] & 0xff; rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, BMASKBYTE1, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_A_CCK1_MCS32); tmpval = tx_agc[RF90_PATH_A] >> 8; rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); tmpval = tx_agc[RF90_PATH_B] >> 24; rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, BMASKBYTE0, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff; rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK1_55_MCS32); } static void _rtl92d_phy_get_power_base(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel, u32 *ofdmbase, u32 *mcsbase) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 powerbase0, powerbase1; u8 legacy_pwrdiff, ht20_pwrdiff; u8 i, powerlevel[2]; for (i = 0; i < 2; i++) { powerlevel[i] = ppowerlevel[i]; legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff[i][channel - 1]; powerbase0 = powerlevel[i] + legacy_pwrdiff; powerbase0 = (powerbase0 << 24) | (powerbase0 << 16) | (powerbase0 << 8) | powerbase0; *(ofdmbase + i) = powerbase0; RTPRINT(rtlpriv, FPHY, PHY_TXPWR, " [OFDM power base index rf(%c) = 0x%x]\n", i == 0 ? 'A' : 'B', *(ofdmbase + i)); } for (i = 0; i < 2; i++) { if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) { ht20_pwrdiff = rtlefuse->txpwr_ht20diff[i][channel - 1]; powerlevel[i] += ht20_pwrdiff; } powerbase1 = powerlevel[i]; powerbase1 = (powerbase1 << 24) | (powerbase1 << 16) | (powerbase1 << 8) | powerbase1; *(mcsbase + i) = powerbase1; RTPRINT(rtlpriv, FPHY, PHY_TXPWR, " [MCS power base index rf(%c) = 0x%x]\n", i == 0 ? 'A' : 'B', *(mcsbase + i)); } } static u8 _rtl92d_phy_get_chnlgroup_bypg(u8 chnlindex) { u8 group; u8 channel_info[59] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 149, 151, 153, 155, 157, 159, 161, 163, 165 }; if (channel_info[chnlindex] <= 3) /* Chanel 1-3 */ group = 0; else if (channel_info[chnlindex] <= 9) /* Channel 4-9 */ group = 1; else if (channel_info[chnlindex] <= 14) /* Channel 10-14 */ group = 2; else if (channel_info[chnlindex] <= 64) group = 6; else if (channel_info[chnlindex] <= 140) group = 7; else group = 8; return group; } static void _rtl92d_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw, u8 channel, u8 index, u32 *powerbase0, u32 *powerbase1, u32 *p_outwriteval) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 i, chnlgroup = 0, pwr_diff_limit[4]; u32 writeval = 0, customer_limit, rf; for (rf = 0; rf < 2; rf++) { switch (rtlefuse->eeprom_regulatory) { case 0: chnlgroup = 0; writeval = rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "RTK better performance, writeval(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeval); break; case 1: if (rtlphy->pwrgroup_cnt == 1) chnlgroup = 0; if (rtlphy->pwrgroup_cnt >= MAX_PG_GROUP) { chnlgroup = _rtl92d_phy_get_chnlgroup_bypg( channel - 1); if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) chnlgroup++; else chnlgroup += 4; writeval = rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Realtek regulatory, 20MHz, writeval(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeval); } break; case 2: writeval = ((index < 2) ? powerbase0[rf] : powerbase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Better regulatory, writeval(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeval); break; case 3: chnlgroup = 0; if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "customer's limit, 40MHz rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', rtlefuse->pwrgroup_ht40[rf] [channel - 1]); } else { RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "customer's limit, 20MHz rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', rtlefuse->pwrgroup_ht20[rf] [channel - 1]); } for (i = 0; i < 4; i++) { pwr_diff_limit[i] = (u8)((rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] & (0x7f << (i * 8))) >> (i * 8)); if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { if (pwr_diff_limit[i] > rtlefuse->pwrgroup_ht40[rf] [channel - 1]) pwr_diff_limit[i] = rtlefuse->pwrgroup_ht40 [rf][channel - 1]; } else { if (pwr_diff_limit[i] > rtlefuse->pwrgroup_ht20[rf][ channel - 1]) pwr_diff_limit[i] = rtlefuse->pwrgroup_ht20[rf] [channel - 1]; } } customer_limit = (pwr_diff_limit[3] << 24) | (pwr_diff_limit[2] << 16) | (pwr_diff_limit[1] << 8) | (pwr_diff_limit[0]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Customer's limit rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', customer_limit); writeval = customer_limit + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Customer, writeval rf(%c)= 0x%x\n", rf == 0 ? 'A' : 'B', writeval); break; default: chnlgroup = 0; writeval = rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "RTK better performance, writeval rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeval); break; } *(p_outwriteval + rf) = writeval; } } static void _rtl92d_write_ofdm_power_reg(struct ieee80211_hw *hw, u8 index, u32 *pvalue) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); static u16 regoffset_a[6] = { RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24, RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04, RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12 }; static u16 regoffset_b[6] = { RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24, RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04, RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12 }; u8 i, rf, pwr_val[4]; u32 writeval; u16 regoffset; for (rf = 0; rf < 2; rf++) { writeval = pvalue[rf]; for (i = 0; i < 4; i++) { pwr_val[i] = (u8) ((writeval & (0x7f << (i * 8))) >> (i * 8)); if (pwr_val[i] > RF6052_MAX_TX_PWR) pwr_val[i] = RF6052_MAX_TX_PWR; } writeval = (pwr_val[3] << 24) | (pwr_val[2] << 16) | (pwr_val[1] << 8) | pwr_val[0]; if (rf == 0) regoffset = regoffset_a[index]; else regoffset = regoffset_b[index]; rtl_set_bbreg(hw, regoffset, BMASKDWORD, writeval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Set 0x%x = %08x\n", regoffset, writeval); if (((get_rf_type(rtlphy) == RF_2T2R) && (regoffset == RTXAGC_A_MCS15_MCS12 || regoffset == RTXAGC_B_MCS15_MCS12)) || ((get_rf_type(rtlphy) != RF_2T2R) && (regoffset == RTXAGC_A_MCS07_MCS04 || regoffset == RTXAGC_B_MCS07_MCS04))) { writeval = pwr_val[3]; if (regoffset == RTXAGC_A_MCS15_MCS12 || regoffset == RTXAGC_A_MCS07_MCS04) regoffset = 0xc90; if (regoffset == RTXAGC_B_MCS15_MCS12 || regoffset == RTXAGC_B_MCS07_MCS04) regoffset = 0xc98; for (i = 0; i < 3; i++) { if (i != 2) writeval = (writeval > 8) ? (writeval - 8) : 0; else writeval = (writeval > 6) ? (writeval - 6) : 0; rtl_write_byte(rtlpriv, (u32) (regoffset + i), (u8) writeval); } } } } void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel) { u32 writeval[2], powerbase0[2], powerbase1[2]; u8 index; _rtl92d_phy_get_power_base(hw, ppowerlevel, channel, &powerbase0[0], &powerbase1[0]); for (index = 0; index < 6; index++) { _rtl92d_get_txpower_writeval_by_regulatory(hw, channel, index, &powerbase0[0], &powerbase1[0], &writeval[0]); _rtl92d_write_ofdm_power_reg(hw, index, &writeval[0]); } } bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u8 u1btmp; u8 direct = bmac0 ? BIT(3) | BIT(2) : BIT(3); u8 mac_reg = bmac0 ? REG_MAC1 : REG_MAC0; u8 mac_on_bit = bmac0 ? MAC1_ON : MAC0_ON; bool bresult = true; /* true: need to enable BB/RF power */ rtlhal->during_mac0init_radiob = false; rtlhal->during_mac1init_radioa = false; RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "===>\n"); /* MAC0 Need PHY1 load radio_b.txt . Driver use DBI to write. */ u1btmp = rtl_read_byte(rtlpriv, mac_reg); if (!(u1btmp & mac_on_bit)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "enable BB & RF\n"); /* Enable BB and RF power */ rtl92de_write_dword_dbi(hw, REG_SYS_ISO_CTRL, rtl92de_read_dword_dbi(hw, REG_SYS_ISO_CTRL, direct) | BIT(29) | BIT(16) | BIT(17), direct); } else { /* We think if MAC1 is ON,then radio_a.txt * and radio_b.txt has been load. */ bresult = false; } RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "<===\n"); return bresult; } void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, bool bmac0) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u8 u1btmp; u8 direct = bmac0 ? BIT(3) | BIT(2) : BIT(3); u8 mac_reg = bmac0 ? REG_MAC1 : REG_MAC0; u8 mac_on_bit = bmac0 ? MAC1_ON : MAC0_ON; rtlhal->during_mac0init_radiob = false; rtlhal->during_mac1init_radioa = false; RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "====>\n"); /* check MAC0 enable or not again now, if * enabled, not power down radio A. */ u1btmp = rtl_read_byte(rtlpriv, mac_reg); if (!(u1btmp & mac_on_bit)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "power down\n"); /* power down RF radio A according to YuNan's advice. */ rtl92de_write_dword_dbi(hw, RFPGA0_XA_LSSIPARAMETER, 0x00000000, direct); } RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "<====\n"); } bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); bool rtstatus = true; struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u32 u4_regvalue = 0; u8 rfpath; struct bb_reg_def *pphyreg; bool mac1_initradioa_first = false, mac0_initradiob_first = false; bool need_pwrdown_radioa = false, need_pwrdown_radiob = false; bool true_bpath = false; if (rtlphy->rf_type == RF_1T1R) rtlphy->num_total_rfpath = 1; else rtlphy->num_total_rfpath = 2; /* Single phy mode: use radio_a radio_b config path_A path_B */ /* seperately by MAC0, and MAC1 needn't configure RF; */ /* Dual PHY mode:MAC0 use radio_a config 1st phy path_A, */ /* MAC1 use radio_b config 2nd PHY path_A. */ /* DMDP,MAC0 on G band,MAC1 on A band. */ if (rtlhal->macphymode == DUALMAC_DUALPHY) { if (rtlhal->current_bandtype == BAND_ON_2_4G && rtlhal->interfaceindex == 0) { /* MAC0 needs PHY1 load radio_b.txt. * Driver use DBI to write. */ if (rtl92d_phy_enable_anotherphy(hw, true)) { rtlphy->num_total_rfpath = 2; mac0_initradiob_first = true; } else { /* We think if MAC1 is ON,then radio_a.txt and * radio_b.txt has been load. */ return rtstatus; } } else if (rtlhal->current_bandtype == BAND_ON_5G && rtlhal->interfaceindex == 1) { /* MAC1 needs PHY0 load radio_a.txt. * Driver use DBI to write. */ if (rtl92d_phy_enable_anotherphy(hw, false)) { rtlphy->num_total_rfpath = 2; mac1_initradioa_first = true; } else { /* We think if MAC0 is ON,then radio_a.txt and * radio_b.txt has been load. */ return rtstatus; } } else if (rtlhal->interfaceindex == 1) { /* MAC0 enabled, only init radia B. */ true_bpath = true; } } for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { /* Mac1 use PHY0 write */ if (mac1_initradioa_first) { if (rfpath == RF90_PATH_A) { rtlhal->during_mac1init_radioa = true; need_pwrdown_radioa = true; } else if (rfpath == RF90_PATH_B) { rtlhal->during_mac1init_radioa = false; mac1_initradioa_first = false; rfpath = RF90_PATH_A; true_bpath = true; rtlphy->num_total_rfpath = 1; } } else if (mac0_initradiob_first) { /* Mac0 use PHY1 write */ if (rfpath == RF90_PATH_A) rtlhal->during_mac0init_radiob = false; if (rfpath == RF90_PATH_B) { rtlhal->during_mac0init_radiob = true; mac0_initradiob_first = false; need_pwrdown_radiob = true; rfpath = RF90_PATH_A; true_bpath = true; rtlphy->num_total_rfpath = 1; } } pphyreg = &rtlphy->phyreg_def[rfpath]; switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV); break; case RF90_PATH_B: case RF90_PATH_D: u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16); break; } rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1); udelay(1); rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); udelay(1); /* Set bit number of Address and Data for RF register */ /* Set 1 to 4 bits for 8255 */ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREADDRESSLENGTH, 0x0); udelay(1); /* Set 0 to 12 bits for 8255 */ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0); udelay(1); switch (rfpath) { case RF90_PATH_A: if (true_bpath) rtstatus = rtl92d_phy_config_rf_with_headerfile( hw, radiob_txt, (enum radio_path)rfpath); else rtstatus = rtl92d_phy_config_rf_with_headerfile( hw, radioa_txt, (enum radio_path)rfpath); break; case RF90_PATH_B: rtstatus = rtl92d_phy_config_rf_with_headerfile(hw, radiob_txt, (enum radio_path) rfpath); break; case RF90_PATH_C: break; case RF90_PATH_D: break; } switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV, u4_regvalue); break; case RF90_PATH_B: case RF90_PATH_D: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16, u4_regvalue); break; } if (!rtstatus) { RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio[%d] Fail!!", rfpath); goto phy_rf_cfg_fail; } } /* check MAC0 enable or not again, if enabled, * not power down radio A. */ /* check MAC1 enable or not again, if enabled, * not power down radio B. */ if (need_pwrdown_radioa) rtl92d_phy_powerdown_anotherphy(hw, false); else if (need_pwrdown_radiob) rtl92d_phy_powerdown_anotherphy(hw, true); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "<---\n"); return rtstatus; phy_rf_cfg_fail: return rtstatus; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/reg.h0000644000175000017500000011271512026211315024534 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92D_REG_H__ #define __RTL92D_REG_H__ /* ----------------------------------------------------- */ /* 0x0000h ~ 0x00FFh System Configuration */ /* ----------------------------------------------------- */ #define REG_SYS_ISO_CTRL 0x0000 #define REG_SYS_FUNC_EN 0x0002 #define REG_APS_FSMCO 0x0004 #define REG_SYS_CLKR 0x0008 #define REG_9346CR 0x000A #define REG_EE_VPD 0x000C #define REG_AFE_MISC 0x0010 #define REG_SPS0_CTRL 0x0011 #define REG_POWER_OFF_IN_PROCESS 0x0017 #define REG_SPS_OCP_CFG 0x0018 #define REG_RSV_CTRL 0x001C #define REG_RF_CTRL 0x001F #define REG_LDOA15_CTRL 0x0020 #define REG_LDOV12D_CTRL 0x0021 #define REG_LDOHCI12_CTRL 0x0022 #define REG_LPLDO_CTRL 0x0023 #define REG_AFE_XTAL_CTRL 0x0024 #define REG_AFE_PLL_CTRL 0x0028 /* for 92d, DMDP,SMSP,DMSP contrl */ #define REG_MAC_PHY_CTRL 0x002c #define REG_EFUSE_CTRL 0x0030 #define REG_EFUSE_TEST 0x0034 #define REG_PWR_DATA 0x0038 #define REG_CAL_TIMER 0x003C #define REG_ACLK_MON 0x003E #define REG_GPIO_MUXCFG 0x0040 #define REG_GPIO_IO_SEL 0x0042 #define REG_MAC_PINMUX_CFG 0x0043 #define REG_GPIO_PIN_CTRL 0x0044 #define REG_GPIO_INTM 0x0048 #define REG_LEDCFG0 0x004C #define REG_LEDCFG1 0x004D #define REG_LEDCFG2 0x004E #define REG_LEDCFG3 0x004F #define REG_FSIMR 0x0050 #define REG_FSISR 0x0054 #define REG_MCUFWDL 0x0080 #define REG_HMEBOX_EXT_0 0x0088 #define REG_HMEBOX_EXT_1 0x008A #define REG_HMEBOX_EXT_2 0x008C #define REG_HMEBOX_EXT_3 0x008E #define REG_BIST_SCAN 0x00D0 #define REG_BIST_RPT 0x00D4 #define REG_BIST_ROM_RPT 0x00D8 #define REG_USB_SIE_INTF 0x00E0 #define REG_PCIE_MIO_INTF 0x00E4 #define REG_PCIE_MIO_INTD 0x00E8 #define REG_HPON_FSM 0x00EC #define REG_SYS_CFG 0x00F0 #define REG_MAC_PHY_CTRL_NORMAL 0x00f8 #define REG_MAC0 0x0081 #define REG_MAC1 0x0053 #define FW_MAC0_READY 0x18 #define FW_MAC1_READY 0x1A #define MAC0_ON BIT(7) #define MAC1_ON BIT(0) #define MAC0_READY BIT(0) #define MAC1_READY BIT(0) /* ----------------------------------------------------- */ /* 0x0100h ~ 0x01FFh MACTOP General Configuration */ /* ----------------------------------------------------- */ #define REG_CR 0x0100 #define REG_PBP 0x0104 #define REG_TRXDMA_CTRL 0x010C #define REG_TRXFF_BNDY 0x0114 #define REG_TRXFF_STATUS 0x0118 #define REG_RXFF_PTR 0x011C #define REG_HIMR 0x0120 #define REG_HISR 0x0124 #define REG_HIMRE 0x0128 #define REG_HISRE 0x012C #define REG_CPWM 0x012F #define REG_FWIMR 0x0130 #define REG_FWISR 0x0134 #define REG_PKTBUF_DBG_CTRL 0x0140 #define REG_PKTBUF_DBG_DATA_L 0x0144 #define REG_PKTBUF_DBG_DATA_H 0x0148 #define REG_TC0_CTRL 0x0150 #define REG_TC1_CTRL 0x0154 #define REG_TC2_CTRL 0x0158 #define REG_TC3_CTRL 0x015C #define REG_TC4_CTRL 0x0160 #define REG_TCUNIT_BASE 0x0164 #define REG_MBIST_START 0x0174 #define REG_MBIST_DONE 0x0178 #define REG_MBIST_FAIL 0x017C #define REG_C2HEVT_MSG_NORMAL 0x01A0 #define REG_C2HEVT_MSG_TEST 0x01B8 #define REG_C2HEVT_CLEAR 0x01BF #define REG_MCUTST_1 0x01c0 #define REG_FMETHR 0x01C8 #define REG_HMETFR 0x01CC #define REG_HMEBOX_0 0x01D0 #define REG_HMEBOX_1 0x01D4 #define REG_HMEBOX_2 0x01D8 #define REG_HMEBOX_3 0x01DC #define REG_LLT_INIT 0x01E0 #define REG_BB_ACCEESS_CTRL 0x01E8 #define REG_BB_ACCESS_DATA 0x01EC /* ----------------------------------------------------- */ /* 0x0200h ~ 0x027Fh TXDMA Configuration */ /* ----------------------------------------------------- */ #define REG_RQPN 0x0200 #define REG_FIFOPAGE 0x0204 #define REG_TDECTRL 0x0208 #define REG_TXDMA_OFFSET_CHK 0x020C #define REG_TXDMA_STATUS 0x0210 #define REG_RQPN_NPQ 0x0214 /* ----------------------------------------------------- */ /* 0x0280h ~ 0x02FFh RXDMA Configuration */ /* ----------------------------------------------------- */ #define REG_RXDMA_AGG_PG_TH 0x0280 #define REG_RXPKT_NUM 0x0284 #define REG_RXDMA_STATUS 0x0288 /* ----------------------------------------------------- */ /* 0x0300h ~ 0x03FFh PCIe */ /* ----------------------------------------------------- */ #define REG_PCIE_CTRL_REG 0x0300 #define REG_INT_MIG 0x0304 #define REG_BCNQ_DESA 0x0308 #define REG_HQ_DESA 0x0310 #define REG_MGQ_DESA 0x0318 #define REG_VOQ_DESA 0x0320 #define REG_VIQ_DESA 0x0328 #define REG_BEQ_DESA 0x0330 #define REG_BKQ_DESA 0x0338 #define REG_RX_DESA 0x0340 #define REG_DBI 0x0348 #define REG_DBI_WDATA 0x0348 #define REG_DBI_RDATA 0x034C #define REG_DBI_CTRL 0x0350 #define REG_DBI_FLAG 0x0352 #define REG_MDIO 0x0354 #define REG_DBG_SEL 0x0360 #define REG_PCIE_HRPWM 0x0361 #define REG_PCIE_HCPWM 0x0363 #define REG_UART_CTRL 0x0364 #define REG_UART_TX_DESA 0x0370 #define REG_UART_RX_DESA 0x0378 /* ----------------------------------------------------- */ /* 0x0400h ~ 0x047Fh Protocol Configuration */ /* ----------------------------------------------------- */ #define REG_VOQ_INFORMATION 0x0400 #define REG_VIQ_INFORMATION 0x0404 #define REG_BEQ_INFORMATION 0x0408 #define REG_BKQ_INFORMATION 0x040C #define REG_MGQ_INFORMATION 0x0410 #define REG_HGQ_INFORMATION 0x0414 #define REG_BCNQ_INFORMATION 0x0418 #define REG_CPU_MGQ_INFORMATION 0x041C #define REG_FWHW_TXQ_CTRL 0x0420 #define REG_HWSEQ_CTRL 0x0423 #define REG_TXPKTBUF_BCNQ_BDNY 0x0424 #define REG_TXPKTBUF_MGQ_BDNY 0x0425 #define REG_MULTI_BCNQ_EN 0x0426 #define REG_MULTI_BCNQ_OFFSET 0x0427 #define REG_SPEC_SIFS 0x0428 #define REG_RL 0x042A #define REG_DARFRC 0x0430 #define REG_RARFRC 0x0438 #define REG_RRSR 0x0440 #define REG_ARFR0 0x0444 #define REG_ARFR1 0x0448 #define REG_ARFR2 0x044C #define REG_ARFR3 0x0450 #define REG_AGGLEN_LMT 0x0458 #define REG_AMPDU_MIN_SPACE 0x045C #define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D #define REG_FAST_EDCA_CTRL 0x0460 #define REG_RD_RESP_PKT_TH 0x0463 #define REG_INIRTS_RATE_SEL 0x0480 #define REG_INIDATA_RATE_SEL 0x0484 #define REG_POWER_STATUS 0x04A4 #define REG_POWER_STAGE1 0x04B4 #define REG_POWER_STAGE2 0x04B8 #define REG_PKT_LIFE_TIME 0x04C0 #define REG_STBC_SETTING 0x04C4 #define REG_PROT_MODE_CTRL 0x04C8 #define REG_MAX_AGGR_NUM 0x04CA #define REG_RTS_MAX_AGGR_NUM 0x04CB #define REG_BAR_MODE_CTRL 0x04CC #define REG_RA_TRY_RATE_AGG_LMT 0x04CF #define REG_EARLY_MODE_CONTROL 0x4D0 #define REG_NQOS_SEQ 0x04DC #define REG_QOS_SEQ 0x04DE #define REG_NEED_CPU_HANDLE 0x04E0 #define REG_PKT_LOSE_RPT 0x04E1 #define REG_PTCL_ERR_STATUS 0x04E2 #define REG_DUMMY 0x04FC /* ----------------------------------------------------- */ /* 0x0500h ~ 0x05FFh EDCA Configuration */ /* ----------------------------------------------------- */ #define REG_EDCA_VO_PARAM 0x0500 #define REG_EDCA_VI_PARAM 0x0504 #define REG_EDCA_BE_PARAM 0x0508 #define REG_EDCA_BK_PARAM 0x050C #define REG_BCNTCFG 0x0510 #define REG_PIFS 0x0512 #define REG_RDG_PIFS 0x0513 #define REG_SIFS_CTX 0x0514 #define REG_SIFS_TRX 0x0516 #define REG_AGGR_BREAK_TIME 0x051A #define REG_SLOT 0x051B #define REG_TX_PTCL_CTRL 0x0520 #define REG_TXPAUSE 0x0522 #define REG_DIS_TXREQ_CLR 0x0523 #define REG_RD_CTRL 0x0524 #define REG_TBTT_PROHIBIT 0x0540 #define REG_RD_NAV_NXT 0x0544 #define REG_NAV_PROT_LEN 0x0546 #define REG_BCN_CTRL 0x0550 #define REG_USTIME_TSF 0x0551 #define REG_MBID_NUM 0x0552 #define REG_DUAL_TSF_RST 0x0553 #define REG_BCN_INTERVAL 0x0554 #define REG_MBSSID_BCN_SPACE 0x0554 #define REG_DRVERLYINT 0x0558 #define REG_BCNDMATIM 0x0559 #define REG_ATIMWND 0x055A #define REG_BCN_MAX_ERR 0x055D #define REG_RXTSF_OFFSET_CCK 0x055E #define REG_RXTSF_OFFSET_OFDM 0x055F #define REG_TSFTR 0x0560 #define REG_INIT_TSFTR 0x0564 #define REG_PSTIMER 0x0580 #define REG_TIMER0 0x0584 #define REG_TIMER1 0x0588 #define REG_ACMHWCTRL 0x05C0 #define REG_ACMRSTCTRL 0x05C1 #define REG_ACMAVG 0x05C2 #define REG_VO_ADMTIME 0x05C4 #define REG_VI_ADMTIME 0x05C6 #define REG_BE_ADMTIME 0x05C8 #define REG_EDCA_RANDOM_GEN 0x05CC #define REG_SCH_TXCMD 0x05D0 /* Dual MAC Co-Existence Register */ #define REG_DMC 0x05F0 /* ----------------------------------------------------- */ /* 0x0600h ~ 0x07FFh WMAC Configuration */ /* ----------------------------------------------------- */ #define REG_APSD_CTRL 0x0600 #define REG_BWOPMODE 0x0603 #define REG_TCR 0x0604 #define REG_RCR 0x0608 #define REG_RX_PKT_LIMIT 0x060C #define REG_RX_DLK_TIME 0x060D #define REG_RX_DRVINFO_SZ 0x060F #define REG_MACID 0x0610 #define REG_BSSID 0x0618 #define REG_MAR 0x0620 #define REG_MBIDCAMCFG 0x0628 #define REG_USTIME_EDCA 0x0638 #define REG_MAC_SPEC_SIFS 0x063A #define REG_RESP_SIFS_CCK 0x063C #define REG_RESP_SIFS_OFDM 0x063E #define REG_ACKTO 0x0640 #define REG_CTS2TO 0x0641 #define REG_EIFS 0x0642 /* WMA, BA, CCX */ #define REG_NAV_CTRL 0x0650 #define REG_BACAMCMD 0x0654 #define REG_BACAMCONTENT 0x0658 #define REG_LBDLY 0x0660 #define REG_FWDLY 0x0661 #define REG_RXERR_RPT 0x0664 #define REG_WMAC_TRXPTCL_CTL 0x0668 /* Security */ #define REG_CAMCMD 0x0670 #define REG_CAMWRITE 0x0674 #define REG_CAMREAD 0x0678 #define REG_CAMDBG 0x067C #define REG_SECCFG 0x0680 /* Power */ #define REG_WOW_CTRL 0x0690 #define REG_PSSTATUS 0x0691 #define REG_PS_RX_INFO 0x0692 #define REG_LPNAV_CTRL 0x0694 #define REG_WKFMCAM_CMD 0x0698 #define REG_WKFMCAM_RWD 0x069C #define REG_RXFLTMAP0 0x06A0 #define REG_RXFLTMAP1 0x06A2 #define REG_RXFLTMAP2 0x06A4 #define REG_BCN_PSR_RPT 0x06A8 #define REG_CALB32K_CTRL 0x06AC #define REG_PKT_MON_CTRL 0x06B4 #define REG_BT_COEX_TABLE 0x06C0 #define REG_WMAC_RESP_TXINFO 0x06D8 /* ----------------------------------------------------- */ /* Redifine 8192C register definition for compatibility */ /* ----------------------------------------------------- */ #define CR9346 REG_9346CR #define MSR (REG_CR + 2) #define ISR REG_HISR #define TSFR REG_TSFTR #define MACIDR0 REG_MACID #define MACIDR4 (REG_MACID + 4) #define PBP REG_PBP #define IDR0 MACIDR0 #define IDR4 MACIDR4 /* ----------------------------------------------------- */ /* 8192C (MSR) Media Status Register(Offset 0x4C, 8 bits)*/ /* ----------------------------------------------------- */ #define MSR_NOLINK 0x00 #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 /* 6. Adaptive Control Registers (Offset: 0x0160 - 0x01CF) */ /* ----------------------------------------------------- */ /* 8192C Response Rate Set Register(offset 0x181, 24bits)*/ /* ----------------------------------------------------- */ #define RRSR_RSC_OFFSET 21 #define RRSR_SHORT_OFFSET 23 #define RRSR_RSC_BW_40M 0x600000 #define RRSR_RSC_UPSUBCHNL 0x400000 #define RRSR_RSC_LOWSUBCHNL 0x200000 #define RRSR_SHORT 0x800000 #define RRSR_1M BIT0 #define RRSR_2M BIT1 #define RRSR_5_5M BIT2 #define RRSR_11M BIT3 #define RRSR_6M BIT4 #define RRSR_9M BIT5 #define RRSR_12M BIT6 #define RRSR_18M BIT7 #define RRSR_24M BIT8 #define RRSR_36M BIT9 #define RRSR_48M BIT10 #define RRSR_54M BIT11 #define RRSR_MCS0 BIT12 #define RRSR_MCS1 BIT13 #define RRSR_MCS2 BIT14 #define RRSR_MCS3 BIT15 #define RRSR_MCS4 BIT16 #define RRSR_MCS5 BIT17 #define RRSR_MCS6 BIT18 #define RRSR_MCS7 BIT19 #define BRSR_ACKSHORTPMB BIT23 /* ----------------------------------------------------- */ /* 8192C Rate Definition */ /* ----------------------------------------------------- */ /* CCK */ #define RATR_1M 0x00000001 #define RATR_2M 0x00000002 #define RATR_55M 0x00000004 #define RATR_11M 0x00000008 /* OFDM */ #define RATR_6M 0x00000010 #define RATR_9M 0x00000020 #define RATR_12M 0x00000040 #define RATR_18M 0x00000080 #define RATR_24M 0x00000100 #define RATR_36M 0x00000200 #define RATR_48M 0x00000400 #define RATR_54M 0x00000800 /* MCS 1 Spatial Stream */ #define RATR_MCS0 0x00001000 #define RATR_MCS1 0x00002000 #define RATR_MCS2 0x00004000 #define RATR_MCS3 0x00008000 #define RATR_MCS4 0x00010000 #define RATR_MCS5 0x00020000 #define RATR_MCS6 0x00040000 #define RATR_MCS7 0x00080000 /* MCS 2 Spatial Stream */ #define RATR_MCS8 0x00100000 #define RATR_MCS9 0x00200000 #define RATR_MCS10 0x00400000 #define RATR_MCS11 0x00800000 #define RATR_MCS12 0x01000000 #define RATR_MCS13 0x02000000 #define RATR_MCS14 0x04000000 #define RATR_MCS15 0x08000000 /* CCK */ #define RATE_1M BIT(0) #define RATE_2M BIT(1) #define RATE_5_5M BIT(2) #define RATE_11M BIT(3) /* OFDM */ #define RATE_6M BIT(4) #define RATE_9M BIT(5) #define RATE_12M BIT(6) #define RATE_18M BIT(7) #define RATE_24M BIT(8) #define RATE_36M BIT(9) #define RATE_48M BIT(10) #define RATE_54M BIT(11) /* MCS 1 Spatial Stream */ #define RATE_MCS0 BIT(12) #define RATE_MCS1 BIT(13) #define RATE_MCS2 BIT(14) #define RATE_MCS3 BIT(15) #define RATE_MCS4 BIT(16) #define RATE_MCS5 BIT(17) #define RATE_MCS6 BIT(18) #define RATE_MCS7 BIT(19) /* MCS 2 Spatial Stream */ #define RATE_MCS8 BIT(20) #define RATE_MCS9 BIT(21) #define RATE_MCS10 BIT(22) #define RATE_MCS11 BIT(23) #define RATE_MCS12 BIT(24) #define RATE_MCS13 BIT(25) #define RATE_MCS14 BIT(26) #define RATE_MCS15 BIT(27) /* ALL CCK Rate */ #define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | \ RATR_11M) #define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | \ RATR_18M | RATR_24M | \ RATR_36M | RATR_48M | RATR_54M) #define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | \ RATR_MCS3 | RATR_MCS4 | RATR_MCS5 | \ RATR_MCS6 | RATR_MCS7) #define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | \ RATR_MCS11 | RATR_MCS12 | RATR_MCS13 | \ RATR_MCS14 | RATR_MCS15) /* ----------------------------------------------------- */ /* 8192C BW_OPMODE bits (Offset 0x203, 8bit) */ /* ----------------------------------------------------- */ #define BW_OPMODE_20MHZ BIT(2) #define BW_OPMODE_5G BIT(1) #define BW_OPMODE_11J BIT(0) /* ----------------------------------------------------- */ /* 8192C CAM Config Setting (offset 0x250, 1 byte) */ /* ----------------------------------------------------- */ #define CAM_VALID BIT(15) #define CAM_NOTVALID 0x0000 #define CAM_USEDK BIT(5) #define CAM_NONE 0x0 #define CAM_WEP40 0x01 #define CAM_TKIP 0x02 #define CAM_AES 0x04 #define CAM_WEP104 0x05 #define CAM_SMS4 0x6 #define TOTAL_CAM_ENTRY 32 #define HALF_CAM_ENTRY 16 #define CAM_WRITE BIT(16) #define CAM_READ 0x00000000 #define CAM_POLLINIG BIT(31) /* 10. Power Save Control Registers (Offset: 0x0260 - 0x02DF) */ #define WOW_PMEN BIT0 /* Power management Enable. */ #define WOW_WOMEN BIT1 /* WoW function on or off. */ #define WOW_MAGIC BIT2 /* Magic packet */ #define WOW_UWF BIT3 /* Unicast Wakeup frame. */ /* 12. Host Interrupt Status Registers (Offset: 0x0300 - 0x030F) */ /* ----------------------------------------------------- */ /* 8190 IMR/ISR bits (offset 0xfd, 8bits) */ /* ----------------------------------------------------- */ #define IMR8190_DISABLED 0x0 #define IMR_BCNDMAINT6 BIT(31) #define IMR_BCNDMAINT5 BIT(30) #define IMR_BCNDMAINT4 BIT(29) #define IMR_BCNDMAINT3 BIT(28) #define IMR_BCNDMAINT2 BIT(27) #define IMR_BCNDMAINT1 BIT(26) #define IMR_BCNDOK8 BIT(25) #define IMR_BCNDOK7 BIT(24) #define IMR_BCNDOK6 BIT(23) #define IMR_BCNDOK5 BIT(22) #define IMR_BCNDOK4 BIT(21) #define IMR_BCNDOK3 BIT(20) #define IMR_BCNDOK2 BIT(19) #define IMR_BCNDOK1 BIT(18) #define IMR_TIMEOUT2 BIT(17) #define IMR_TIMEOUT1 BIT(16) #define IMR_TXFOVW BIT(15) #define IMR_PSTIMEOUT BIT(14) #define IMR_BcnInt BIT(13) #define IMR_RXFOVW BIT(12) #define IMR_RDU BIT(11) #define IMR_ATIMEND BIT(10) #define IMR_BDOK BIT(9) #define IMR_HIGHDOK BIT(8) #define IMR_TBDOK BIT(7) #define IMR_MGNTDOK BIT(6) #define IMR_TBDER BIT(5) #define IMR_BKDOK BIT(4) #define IMR_BEDOK BIT(3) #define IMR_VIDOK BIT(2) #define IMR_VODOK BIT(1) #define IMR_ROK BIT(0) #define IMR_TXERR BIT(11) #define IMR_RXERR BIT(10) #define IMR_C2HCMD BIT(9) #define IMR_CPWM BIT(8) #define IMR_OCPINT BIT(1) #define IMR_WLANOFF BIT(0) /* ----------------------------------------------------- */ /* 8192C EFUSE */ /* ----------------------------------------------------- */ #define HWSET_MAX_SIZE 256 #define EFUSE_MAX_SECTION 32 #define EFUSE_REAL_CONTENT_LEN 512 /* ----------------------------------------------------- */ /* 8192C EEPROM/EFUSE share register definition. */ /* ----------------------------------------------------- */ #define EEPROM_DEFAULT_TSSI 0x0 #define EEPROM_DEFAULT_CRYSTALCAP 0x0 #define EEPROM_DEFAULT_THERMALMETER 0x12 #define EEPROM_DEFAULT_TXPOWERLEVEL_2G 0x2C #define EEPROM_DEFAULT_TXPOWERLEVEL_5G 0x22 #define EEPROM_DEFAULT_HT40_2SDIFF 0x0 /* HT20<->40 default Tx Power Index Difference */ #define EEPROM_DEFAULT_HT20_DIFF 2 /* OFDM Tx Power index diff */ #define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x4 #define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0 #define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0 #define EEPROM_CHANNEL_PLAN_FCC 0x0 #define EEPROM_CHANNEL_PLAN_IC 0x1 #define EEPROM_CHANNEL_PLAN_ETSI 0x2 #define EEPROM_CHANNEL_PLAN_SPAIN 0x3 #define EEPROM_CHANNEL_PLAN_FRANCE 0x4 #define EEPROM_CHANNEL_PLAN_MKK 0x5 #define EEPROM_CHANNEL_PLAN_MKK1 0x6 #define EEPROM_CHANNEL_PLAN_ISRAEL 0x7 #define EEPROM_CHANNEL_PLAN_TELEC 0x8 #define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9 #define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA #define EEPROM_CHANNEL_PLAN_NCC 0xB #define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80 #define EEPROM_CID_DEFAULT 0x0 #define EEPROM_CID_TOSHIBA 0x4 #define EEPROM_CID_CCX 0x10 #define EEPROM_CID_QMI 0x0D #define EEPROM_CID_WHQL 0xFE #define RTL8192_EEPROM_ID 0x8129 #define EEPROM_WAPI_SUPPORT 0x78 #define RTL8190_EEPROM_ID 0x8129 /* 0-1 */ #define EEPROM_HPON 0x02 /* LDO settings.2-5 */ #define EEPROM_CLK 0x06 /* Clock settings.6-7 */ #define EEPROM_MAC_FUNCTION 0x08 /* SE Test mode.8 */ #define EEPROM_VID 0x28 /* SE Vendor ID.A-B */ #define EEPROM_DID 0x2A /* SE Device ID. C-D */ #define EEPROM_SVID 0x2C /* SE Vendor ID.E-F */ #define EEPROM_SMID 0x2E /* SE PCI Subsystem ID. 10-11 */ #define EEPROM_MAC_ADDR 0x16 /* SEMAC Address. 12-17 */ #define EEPROM_MAC_ADDR_MAC0_92D 0x55 #define EEPROM_MAC_ADDR_MAC1_92D 0x5B /* 2.4G band Tx power index setting */ #define EEPROM_CCK_TX_PWR_INX_2G 0x61 #define EEPROM_HT40_1S_TX_PWR_INX_2G 0x67 #define EEPROM_HT40_2S_TX_PWR_INX_DIFF_2G 0x6D #define EEPROM_HT20_TX_PWR_INX_DIFF_2G 0x70 #define EEPROM_OFDM_TX_PWR_INX_DIFF_2G 0x73 #define EEPROM_HT40_MAX_PWR_OFFSET_2G 0x76 #define EEPROM_HT20_MAX_PWR_OFFSET_2G 0x79 /*5GL channel 32-64 */ #define EEPROM_HT40_1S_TX_PWR_INX_5GL 0x7C #define EEPROM_HT40_2S_TX_PWR_INX_DIFF_5GL 0x82 #define EEPROM_HT20_TX_PWR_INX_DIFF_5GL 0x85 #define EEPROM_OFDM_TX_PWR_INX_DIFF_5GL 0x88 #define EEPROM_HT40_MAX_PWR_OFFSET_5GL 0x8B #define EEPROM_HT20_MAX_PWR_OFFSET_5GL 0x8E /* 5GM channel 100-140 */ #define EEPROM_HT40_1S_TX_PWR_INX_5GM 0x91 #define EEPROM_HT40_2S_TX_PWR_INX_DIFF_5GM 0x97 #define EEPROM_HT20_TX_PWR_INX_DIFF_5GM 0x9A #define EEPROM_OFDM_TX_PWR_INX_DIFF_5GM 0x9D #define EEPROM_HT40_MAX_PWR_OFFSET_5GM 0xA0 #define EEPROM_HT20_MAX_PWR_OFFSET_5GM 0xA3 /* 5GH channel 149-165 */ #define EEPROM_HT40_1S_TX_PWR_INX_5GH 0xA6 #define EEPROM_HT40_2S_TX_PWR_INX_DIFF_5GH 0xAC #define EEPROM_HT20_TX_PWR_INX_DIFF_5GH 0xAF #define EEPROM_OFDM_TX_PWR_INX_DIFF_5GH 0xB2 #define EEPROM_HT40_MAX_PWR_OFFSET_5GH 0xB5 #define EEPROM_HT20_MAX_PWR_OFFSET_5GH 0xB8 /* Map of supported channels. */ #define EEPROM_CHANNEL_PLAN 0xBB #define EEPROM_IQK_DELTA 0xBC #define EEPROM_LCK_DELTA 0xBC #define EEPROM_XTAL_K 0xBD /* [7:5] */ #define EEPROM_TSSI_A_5G 0xBE #define EEPROM_TSSI_B_5G 0xBF #define EEPROM_TSSI_AB_5G 0xC0 #define EEPROM_THERMAL_METER 0xC3 /* [4:0] */ #define EEPROM_RF_OPT1 0xC4 #define EEPROM_RF_OPT2 0xC5 #define EEPROM_RF_OPT3 0xC6 #define EEPROM_RF_OPT4 0xC7 #define EEPROM_RF_OPT5 0xC8 #define EEPROM_RF_OPT6 0xC9 #define EEPROM_VERSION 0xCA #define EEPROM_CUSTOMER_ID 0xCB #define EEPROM_RF_OPT7 0xCC #define EEPROM_DEF_PART_NO 0x3FD /* Byte */ #define EEPROME_CHIP_VERSION_L 0x3FF #define EEPROME_CHIP_VERSION_H 0x3FE /* * Current IOREG MAP * 0x0000h ~ 0x00FFh System Configuration (256 Bytes) * 0x0100h ~ 0x01FFh MACTOP General Configuration (256 Bytes) * 0x0200h ~ 0x027Fh TXDMA Configuration (128 Bytes) * 0x0280h ~ 0x02FFh RXDMA Configuration (128 Bytes) * 0x0300h ~ 0x03FFh PCIE EMAC Reserved Region (256 Bytes) * 0x0400h ~ 0x04FFh Protocol Configuration (256 Bytes) * 0x0500h ~ 0x05FFh EDCA Configuration (256 Bytes) * 0x0600h ~ 0x07FFh WMAC Configuration (512 Bytes) * 0x2000h ~ 0x3FFFh 8051 FW Download Region (8196 Bytes) */ /* ----------------------------------------------------- */ /* 8192C (RCR) (Offset 0x608, 32 bits) */ /* ----------------------------------------------------- */ #define RCR_APPFCS BIT(31) #define RCR_APP_MIC BIT(30) #define RCR_APP_ICV BIT(29) #define RCR_APP_PHYST_RXFF BIT(28) #define RCR_APP_BA_SSN BIT(27) #define RCR_ENMBID BIT(24) #define RCR_LSIGEN BIT(23) #define RCR_MFBEN BIT(22) #define RCR_HTC_LOC_CTRL BIT(14) #define RCR_AMF BIT(13) #define RCR_ACF BIT(12) #define RCR_ADF BIT(11) #define RCR_AICV BIT(9) #define RCR_ACRC32 BIT(8) #define RCR_CBSSID_BCN BIT(7) #define RCR_CBSSID_DATA BIT(6) #define RCR_APWRMGT BIT(5) #define RCR_ADD3 BIT(4) #define RCR_AB BIT(3) #define RCR_AM BIT(2) #define RCR_APM BIT(1) #define RCR_AAP BIT(0) #define RCR_MXDMA_OFFSET 8 #define RCR_FIFO_OFFSET 13 /* ----------------------------------------------------- */ /* 8192C Regsiter Bit and Content definition */ /* ----------------------------------------------------- */ /* ----------------------------------------------------- */ /* 0x0000h ~ 0x00FFh System Configuration */ /* ----------------------------------------------------- */ /* SPS0_CTRL */ #define SW18_FPWM BIT(3) /* SYS_ISO_CTRL */ #define ISO_MD2PP BIT(0) #define ISO_UA2USB BIT(1) #define ISO_UD2CORE BIT(2) #define ISO_PA2PCIE BIT(3) #define ISO_PD2CORE BIT(4) #define ISO_IP2MAC BIT(5) #define ISO_DIOP BIT(6) #define ISO_DIOE BIT(7) #define ISO_EB2CORE BIT(8) #define ISO_DIOR BIT(9) #define PWC_EV25V BIT(14) #define PWC_EV12V BIT(15) /* SYS_FUNC_EN */ #define FEN_BBRSTB BIT(0) #define FEN_BB_GLB_RSTn BIT(1) #define FEN_USBA BIT(2) #define FEN_UPLL BIT(3) #define FEN_USBD BIT(4) #define FEN_DIO_PCIE BIT(5) #define FEN_PCIEA BIT(6) #define FEN_PPLL BIT(7) #define FEN_PCIED BIT(8) #define FEN_DIOE BIT(9) #define FEN_CPUEN BIT(10) #define FEN_DCORE BIT(11) #define FEN_ELDR BIT(12) #define FEN_DIO_RF BIT(13) #define FEN_HWPDN BIT(14) #define FEN_MREGEN BIT(15) /* APS_FSMCO */ #define PFM_LDALL BIT(0) #define PFM_ALDN BIT(1) #define PFM_LDKP BIT(2) #define PFM_WOWL BIT(3) #define EnPDN BIT(4) #define PDN_PL BIT(5) #define APFM_ONMAC BIT(8) #define APFM_OFF BIT(9) #define APFM_RSM BIT(10) #define AFSM_HSUS BIT(11) #define AFSM_PCIE BIT(12) #define APDM_MAC BIT(13) #define APDM_HOST BIT(14) #define APDM_HPDN BIT(15) #define RDY_MACON BIT(16) #define SUS_HOST BIT(17) #define ROP_ALD BIT(20) #define ROP_PWR BIT(21) #define ROP_SPS BIT(22) #define SOP_MRST BIT(25) #define SOP_FUSE BIT(26) #define SOP_ABG BIT(27) #define SOP_AMB BIT(28) #define SOP_RCK BIT(29) #define SOP_A8M BIT(30) #define XOP_BTCK BIT(31) /* SYS_CLKR */ #define ANAD16V_EN BIT(0) #define ANA8M BIT(1) #define MACSLP BIT(4) #define LOADER_CLK_EN BIT(5) #define _80M_SSC_DIS BIT(7) #define _80M_SSC_EN_HO BIT(8) #define PHY_SSC_RSTB BIT(9) #define SEC_CLK_EN BIT(10) #define MAC_CLK_EN BIT(11) #define SYS_CLK_EN BIT(12) #define RING_CLK_EN BIT(13) /* 9346CR */ #define BOOT_FROM_EEPROM BIT(4) #define EEPROM_EN BIT(5) /* AFE_MISC */ #define AFE_BGEN BIT(0) #define AFE_MBEN BIT(1) #define MAC_ID_EN BIT(7) /* RSV_CTRL */ #define WLOCK_ALL BIT(0) #define WLOCK_00 BIT(1) #define WLOCK_04 BIT(2) #define WLOCK_08 BIT(3) #define WLOCK_40 BIT(4) #define R_DIS_PRST_0 BIT(5) #define R_DIS_PRST_1 BIT(6) #define LOCK_ALL_EN BIT(7) /* RF_CTRL */ #define RF_EN BIT(0) #define RF_RSTB BIT(1) #define RF_SDMRSTB BIT(2) /* LDOA15_CTRL */ #define LDA15_EN BIT(0) #define LDA15_STBY BIT(1) #define LDA15_OBUF BIT(2) #define LDA15_REG_VOS BIT(3) #define _LDA15_VOADJ(x) (((x) & 0x7) << 4) /* LDOV12D_CTRL */ #define LDV12_EN BIT(0) #define LDV12_SDBY BIT(1) #define LPLDO_HSM BIT(2) #define LPLDO_LSM_DIS BIT(3) #define _LDV12_VADJ(x) (((x) & 0xF) << 4) /* AFE_XTAL_CTRL */ #define XTAL_EN BIT(0) #define XTAL_BSEL BIT(1) #define _XTAL_BOSC(x) (((x) & 0x3) << 2) #define _XTAL_CADJ(x) (((x) & 0xF) << 4) #define XTAL_GATE_USB BIT(8) #define _XTAL_USB_DRV(x) (((x) & 0x3) << 9) #define XTAL_GATE_AFE BIT(11) #define _XTAL_AFE_DRV(x) (((x) & 0x3) << 12) #define XTAL_RF_GATE BIT(14) #define _XTAL_RF_DRV(x) (((x) & 0x3) << 15) #define XTAL_GATE_DIG BIT(17) #define _XTAL_DIG_DRV(x) (((x) & 0x3) << 18) #define XTAL_BT_GATE BIT(20) #define _XTAL_BT_DRV(x) (((x) & 0x3) << 21) #define _XTAL_GPIO(x) (((x) & 0x7) << 23) #define CKDLY_AFE BIT(26) #define CKDLY_USB BIT(27) #define CKDLY_DIG BIT(28) #define CKDLY_BT BIT(29) /* AFE_PLL_CTRL */ #define APLL_EN BIT(0) #define APLL_320_EN BIT(1) #define APLL_FREF_SEL BIT(2) #define APLL_EDGE_SEL BIT(3) #define APLL_WDOGB BIT(4) #define APLL_LPFEN BIT(5) #define APLL_REF_CLK_13MHZ 0x1 #define APLL_REF_CLK_19_2MHZ 0x2 #define APLL_REF_CLK_20MHZ 0x3 #define APLL_REF_CLK_25MHZ 0x4 #define APLL_REF_CLK_26MHZ 0x5 #define APLL_REF_CLK_38_4MHZ 0x6 #define APLL_REF_CLK_40MHZ 0x7 #define APLL_320EN BIT(14) #define APLL_80EN BIT(15) #define APLL_1MEN BIT(24) /* EFUSE_CTRL */ #define ALD_EN BIT(18) #define EF_PD BIT(19) #define EF_FLAG BIT(31) /* EFUSE_TEST */ #define EF_TRPT BIT(7) #define LDOE25_EN BIT(31) /* MCUFWDL */ #define MCUFWDL_EN BIT(0) #define MCUFWDL_RDY BIT(1) #define FWDL_ChkSum_rpt BIT(2) #define MACINI_RDY BIT(3) #define BBINI_RDY BIT(4) #define RFINI_RDY BIT(5) #define WINTINI_RDY BIT(6) #define MAC1_WINTINI_RDY BIT(11) #define CPRST BIT(23) /* REG_SYS_CFG */ #define XCLK_VLD BIT(0) #define ACLK_VLD BIT(1) #define UCLK_VLD BIT(2) #define PCLK_VLD BIT(3) #define PCIRSTB BIT(4) #define V15_VLD BIT(5) #define TRP_B15V_EN BIT(7) #define SIC_IDLE BIT(8) #define BD_MAC2 BIT(9) #define BD_MAC1 BIT(10) #define IC_MACPHY_MODE BIT(11) #define PAD_HWPD_IDN BIT(22) #define TRP_VAUX_EN BIT(23) #define TRP_BT_EN BIT(24) #define BD_PKG_SEL BIT(25) #define BD_HCI_SEL BIT(26) #define TYPE_ID BIT(27) /* LLT_INIT */ #define _LLT_NO_ACTIVE 0x0 #define _LLT_WRITE_ACCESS 0x1 #define _LLT_READ_ACCESS 0x2 #define _LLT_INIT_DATA(x) ((x) & 0xFF) #define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8) #define _LLT_OP(x) (((x) & 0x3) << 30) #define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3) /* ----------------------------------------------------- */ /* 0x0400h ~ 0x047Fh Protocol Configuration */ /* ----------------------------------------------------- */ #define RETRY_LIMIT_SHORT_SHIFT 8 #define RETRY_LIMIT_LONG_SHIFT 0 /* ----------------------------------------------------- */ /* 0x0500h ~ 0x05FFh EDCA Configuration */ /* ----------------------------------------------------- */ /* EDCA setting */ #define AC_PARAM_TXOP_LIMIT_OFFSET 16 #define AC_PARAM_ECW_MAX_OFFSET 12 #define AC_PARAM_ECW_MIN_OFFSET 8 #define AC_PARAM_AIFS_OFFSET 0 /* ACMHWCTRL */ #define ACMHW_HWEN BIT(0) #define ACMHW_BEQEN BIT(1) #define ACMHW_VIQEN BIT(2) #define ACMHW_VOQEN BIT(3) /* ----------------------------------------------------- */ /* 0x0600h ~ 0x07FFh WMAC Configuration */ /* ----------------------------------------------------- */ /* TCR */ #define TSFRST BIT(0) #define DIS_GCLK BIT(1) #define PAD_SEL BIT(2) #define PWR_ST BIT(6) #define PWRBIT_OW_EN BIT(7) #define ACRC BIT(8) #define CFENDFORM BIT(9) #define ICV BIT(10) /* SECCFG */ #define SCR_TXUSEDK BIT(0) #define SCR_RXUSEDK BIT(1) #define SCR_TXENCENABLE BIT(2) #define SCR_RXENCENABLE BIT(3) #define SCR_SKBYA2 BIT(4) #define SCR_NOSKMC BIT(5) #define SCR_TXBCUSEDK BIT(6) #define SCR_RXBCUSEDK BIT(7) /* General definitions */ #define LAST_ENTRY_OF_TX_PKT_BUFFER 255 #define LAST_ENTRY_OF_TX_PKT_BUFFER_DUAL_MAC 127 #define POLLING_LLT_THRESHOLD 20 #define POLLING_READY_TIMEOUT_COUNT 1000 /* Min Spacing related settings. */ #define MAX_MSS_DENSITY_2T 0x13 #define MAX_MSS_DENSITY_1T 0x0A /* BB-PHY register PMAC 0x100 PHY 0x800 - 0xEFF */ /* 1. PMAC duplicate register due to connection: */ /* RF_Mode, TRxRN, NumOf L-STF */ /* 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00 */ /* 3. RF register 0x00-2E */ /* 4. Bit Mask for BB/RF register */ /* 5. Other defintion for BB/RF R/W */ /* 3. Page8(0x800) */ #define RFPGA0_RFMOD 0x800 #define RFPGA0_TXINFO 0x804 #define RFPGA0_PSDFUNCTION 0x808 #define RFPGA0_TXGAINSTAGE 0x80c #define RFPGA0_RFTIMING1 0x810 #define RFPGA0_RFTIMING2 0x814 #define RFPGA0_XA_HSSIPARAMETER1 0x820 #define RFPGA0_XA_HSSIPARAMETER2 0x824 #define RFPGA0_XB_HSSIPARAMETER1 0x828 #define RFPGA0_XB_HSSIPARAMETER2 0x82c #define RFPGA0_XA_LSSIPARAMETER 0x840 #define RFPGA0_XB_LSSIPARAMETER 0x844 #define RFPGA0_RFWAkEUPPARAMETER 0x850 #define RFPGA0_RFSLEEPUPPARAMETER 0x854 #define RFPGA0_XAB_SWITCHCONTROL 0x858 #define RFPGA0_XCD_SWITCHCONTROL 0x85c #define RFPGA0_XA_RFINTERFACEOE 0x860 #define RFPGA0_XB_RFINTERFACEOE 0x864 #define RFPGA0_XAB_RFINTERFACESW 0x870 #define RFPGA0_XCD_RFINTERFACESW 0x874 #define RFPGA0_XAB_RFPARAMETER 0x878 #define RFPGA0_XCD_RFPARAMETER 0x87c #define RFPGA0_ANALOGPARAMETER1 0x880 #define RFPGA0_ANALOGPARAMETER2 0x884 #define RFPGA0_ANALOGPARAMETER3 0x888 #define RFPGA0_ADDALLOCKEN 0x888 #define RFPGA0_ANALOGPARAMETER4 0x88c #define RFPGA0_XA_LSSIREADBACK 0x8a0 #define RFPGA0_XB_LSSIREADBACK 0x8a4 #define RFPGA0_XC_LSSIREADBACK 0x8a8 #define RFPGA0_XD_LSSIREADBACK 0x8ac #define RFPGA0_PSDREPORT 0x8b4 #define TRANSCEIVERA_HSPI_READBACK 0x8b8 #define TRANSCEIVERB_HSPI_READBACK 0x8bc #define RFPGA0_XAB_RFINTERFACERB 0x8e0 #define RFPGA0_XCD_RFINTERFACERB 0x8e4 /* 4. Page9(0x900) */ #define RFPGA1_RFMOD 0x900 #define RFPGA1_TXBLOCK 0x904 #define RFPGA1_DEBUGSELECT 0x908 #define RFPGA1_TXINFO 0x90c /* 5. PageA(0xA00) */ #define RCCK0_SYSTEM 0xa00 #define RCCK0_AFESSTTING 0xa04 #define RCCK0_CCA 0xa08 #define RCCK0_RXAGC1 0xa0c #define RCCK0_RXAGC2 0xa10 #define RCCK0_RXHP 0xa14 #define RCCK0_DSPPARAMETER1 0xa18 #define RCCK0_DSPPARAMETER2 0xa1c #define RCCK0_TXFILTER1 0xa20 #define RCCK0_TXFILTER2 0xa24 #define RCCK0_DEBUGPORT 0xa28 #define RCCK0_FALSEALARMREPORT 0xa2c #define RCCK0_TRSSIREPORT 0xa50 #define RCCK0_RXREPORT 0xa54 #define RCCK0_FACOUNTERLOWER 0xa5c #define RCCK0_FACOUNTERUPPER 0xa58 /* 6. PageC(0xC00) */ #define ROFDM0_LSTF 0xc00 #define ROFDM0_TRXPATHENABLE 0xc04 #define ROFDM0_TRMUXPAR 0xc08 #define ROFDM0_TRSWISOLATION 0xc0c #define ROFDM0_XARXAFE 0xc10 #define ROFDM0_XARXIQIMBALANCE 0xc14 #define ROFDM0_XBRXAFE 0xc18 #define ROFDM0_XBRXIQIMBALANCE 0xc1c #define ROFDM0_XCRXAFE 0xc20 #define ROFDM0_XCRXIQIMBALANCE 0xc24 #define ROFDM0_XDRXAFE 0xc28 #define ROFDM0_XDRXIQIMBALANCE 0xc2c #define ROFDM0_RXDETECTOR1 0xc30 #define ROFDM0_RXDETECTOR2 0xc34 #define ROFDM0_RXDETECTOR3 0xc38 #define ROFDM0_RXDETECTOR4 0xc3c #define ROFDM0_RXDSP 0xc40 #define ROFDM0_CFOANDDAGC 0xc44 #define ROFDM0_CCADROPTHRESHOLD 0xc48 #define ROFDM0_ECCATHRESHOLD 0xc4c #define ROFDM0_XAAGCCORE1 0xc50 #define ROFDM0_XAAGCCORE2 0xc54 #define ROFDM0_XBAGCCORE1 0xc58 #define ROFDM0_XBAGCCORE2 0xc5c #define ROFDM0_XCAGCCORE1 0xc60 #define ROFDM0_XCAGCCORE2 0xc64 #define ROFDM0_XDAGCCORE1 0xc68 #define ROFDM0_XDAGCCORE2 0xc6c #define ROFDM0_AGCPARAMETER1 0xc70 #define ROFDM0_AGCPARAMETER2 0xc74 #define ROFDM0_AGCRSSITABLE 0xc78 #define ROFDM0_HTSTFAGC 0xc7c #define ROFDM0_XATxIQIMBALANCE 0xc80 #define ROFDM0_XATxAFE 0xc84 #define ROFDM0_XBTxIQIMBALANCE 0xc88 #define ROFDM0_XBTxAFE 0xc8c #define ROFDM0_XCTxIQIMBALANCE 0xc90 #define ROFDM0_XCTxAFE 0xc94 #define ROFDM0_XDTxIQIMBALANCE 0xc98 #define ROFDM0_XDTxAFE 0xc9c #define ROFDM0_RXHPPARAMETER 0xce0 #define ROFDM0_TXPSEUDONOISEWGT 0xce4 #define ROFDM0_FRAMESYNC 0xcf0 #define ROFDM0_DFSREPORT 0xcf4 #define ROFDM0_TXCOEFF1 0xca4 #define ROFDM0_TXCOEFF2 0xca8 #define ROFDM0_TXCOEFF3 0xcac #define ROFDM0_TXCOEFF4 0xcb0 #define ROFDM0_TXCOEFF5 0xcb4 #define ROFDM0_TXCOEFF6 0xcb8 /* 7. PageD(0xD00) */ #define ROFDM1_LSTF 0xd00 #define ROFDM1_TRXPATHENABLE 0xd04 #define ROFDM1_CFO 0xd08 #define ROFDM1_CSI1 0xd10 #define ROFDM1_SBD 0xd14 #define ROFDM1_CSI2 0xd18 #define ROFDM1_CFOTRACKING 0xd2c #define ROFDM1_TRXMESAURE1 0xd34 #define ROFDM1_INTFDET 0xd3c #define ROFDM1_PSEUDONOISESTATEAB 0xd50 #define ROFDM1_PSEUDONOISESTATECD 0xd54 #define ROFDM1_RXPSEUDONOISEWGT 0xd58 #define ROFDM_PHYCOUNTER1 0xda0 #define ROFDM_PHYCOUNTER2 0xda4 #define ROFDM_PHYCOUNTER3 0xda8 #define ROFDM_SHORTCFOAB 0xdac #define ROFDM_SHORTCFOCD 0xdb0 #define ROFDM_LONGCFOAB 0xdb4 #define ROFDM_LONGCFOCD 0xdb8 #define ROFDM_TAILCFOAB 0xdbc #define ROFDM_TAILCFOCD 0xdc0 #define ROFDM_PWMEASURE1 0xdc4 #define ROFDM_PWMEASURE2 0xdc8 #define ROFDM_BWREPORT 0xdcc #define ROFDM_AGCREPORT 0xdd0 #define ROFDM_RXSNR 0xdd4 #define ROFDM_RXEVMCSI 0xdd8 #define ROFDM_SIGReport 0xddc /* 8. PageE(0xE00) */ #define RTXAGC_A_RATE18_06 0xe00 #define RTXAGC_A_RATE54_24 0xe04 #define RTXAGC_A_CCK1_MCS32 0xe08 #define RTXAGC_A_MCS03_MCS00 0xe10 #define RTXAGC_A_MCS07_MCS04 0xe14 #define RTXAGC_A_MCS11_MCS08 0xe18 #define RTXAGC_A_MCS15_MCS12 0xe1c #define RTXAGC_B_RATE18_06 0x830 #define RTXAGC_B_RATE54_24 0x834 #define RTXAGC_B_CCK1_55_MCS32 0x838 #define RTXAGC_B_MCS03_MCS00 0x83c #define RTXAGC_B_MCS07_MCS04 0x848 #define RTXAGC_B_MCS11_MCS08 0x84c #define RTXAGC_B_MCS15_MCS12 0x868 #define RTXAGC_B_CCK11_A_CCK2_11 0x86c /* RL6052 Register definition */ #define RF_AC 0x00 #define RF_IQADJ_G1 0x01 #define RF_IQADJ_G2 0x02 #define RF_POW_TRSW 0x05 #define RF_GAIN_RX 0x06 #define RF_GAIN_TX 0x07 #define RF_TXM_IDAC 0x08 #define RF_BS_IQGEN 0x0F #define RF_MODE1 0x10 #define RF_MODE2 0x11 #define RF_RX_AGC_HP 0x12 #define RF_TX_AGC 0x13 #define RF_BIAS 0x14 #define RF_IPA 0x15 #define RF_POW_ABILITY 0x17 #define RF_MODE_AG 0x18 #define rRfChannel 0x18 #define RF_CHNLBW 0x18 #define RF_TOP 0x19 #define RF_RX_G1 0x1A #define RF_RX_G2 0x1B #define RF_RX_BB2 0x1C #define RF_RX_BB1 0x1D #define RF_RCK1 0x1E #define RF_RCK2 0x1F #define RF_TX_G1 0x20 #define RF_TX_G2 0x21 #define RF_TX_G3 0x22 #define RF_TX_BB1 0x23 #define RF_T_METER 0x42 #define RF_SYN_G1 0x25 #define RF_SYN_G2 0x26 #define RF_SYN_G3 0x27 #define RF_SYN_G4 0x28 #define RF_SYN_G5 0x29 #define RF_SYN_G6 0x2A #define RF_SYN_G7 0x2B #define RF_SYN_G8 0x2C #define RF_RCK_OS 0x30 #define RF_TXPA_G1 0x31 #define RF_TXPA_G2 0x32 #define RF_TXPA_G3 0x33 /* Bit Mask */ /* 2. Page8(0x800) */ #define BRFMOD 0x1 #define BCCKTXSC 0x30 #define BCCKEN 0x1000000 #define BOFDMEN 0x2000000 #define B3WIREDATALENGTH 0x800 #define B3WIREADDRESSLENGTH 0x400 #define BRFSI_RFENV 0x10 #define BLSSIREADADDRESS 0x7f800000 #define BLSSIREADEDGE 0x80000000 #define BLSSIREADBACKDATA 0xfffff /* 4. PageA(0xA00) */ #define BCCKSIDEBAND 0x10 /* Other Definition */ #define BBYTE0 0x1 #define BBYTE1 0x2 #define BBYTE2 0x4 #define BBYTE3 0x8 #define BWORD0 0x3 #define BWORD1 0xc #define BDWORD 0xf #define BMASKBYTE0 0xff #define BMASKBYTE1 0xff00 #define BMASKBYTE2 0xff0000 #define BMASKBYTE3 0xff000000 #define BMASKHWORD 0xffff0000 #define BMASKLWORD 0x0000ffff #define BMASKDWORD 0xffffffff #define BMASK12BITS 0xfff #define BMASKH4BITS 0xf0000000 #define BMASKOFDM_D 0xffc00000 #define BMASKCCK 0x3f3f3f3f #define BRFREGOFFSETMASK 0xfffff #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/phy.h0000644000175000017500000001351612026211315024556 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92D_PHY_H__ #define __RTL92D_PHY_H__ #define MAX_PRECMD_CNT 16 #define MAX_RFDEPENDCMD_CNT 16 #define MAX_POSTCMD_CNT 16 #define MAX_DOZE_WAITING_TIMES_9x 64 #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 #define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 #define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 #define APK_CURVE_REG_NUM 4 #define PATH_NUM 2 #define LOOP_LIMIT 5 #define MAX_STALL_TIME 50 #define ANTENNA_DIVERSITY_VALUE 0x80 #define MAX_TXPWR_IDX_NMODE_92S 63 #define RESET_CNT_LIMIT 3 #define IQK_ADDA_REG_NUM 16 #define IQK_BB_REG_NUM 10 #define IQK_BB_REG_NUM_test 6 #define IQK_MAC_REG_NUM 4 #define RX_INDEX_MAPPING_NUM 15 #define IQK_DELAY_TIME 1 #define CT_OFFSET_MAC_ADDR 0X16 #define CT_OFFSET_CCK_TX_PWR_IDX 0x5A #define CT_OFFSET_HT401S_TX_PWR_IDX 0x60 #define CT_OFFSET_HT402S_TX_PWR_IDX_DIFF 0x66 #define CT_OFFSET_HT20_TX_PWR_IDX_DIFF 0x69 #define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF 0x6C #define CT_OFFSET_HT40_MAX_PWR_OFFSET 0x6F #define CT_OFFSET_HT20_MAX_PWR_OFFSET 0x72 #define CT_OFFSET_CHANNEL_PLAH 0x75 #define CT_OFFSET_THERMAL_METER 0x78 #define CT_OFFSET_RF_OPTION 0x79 #define CT_OFFSET_VERSION 0x7E #define CT_OFFSET_CUSTOMER_ID 0x7F enum swchnlcmd_id { CMDID_END, CMDID_SET_TXPOWEROWER_LEVEL, CMDID_BBREGWRITE10, CMDID_WRITEPORT_ULONG, CMDID_WRITEPORT_USHORT, CMDID_WRITEPORT_UCHAR, CMDID_RF_WRITEREG, }; struct swchnlcmd { enum swchnlcmd_id cmdid; u32 para1; u32 para2; u32 msdelay; }; enum baseband_config_type { BASEBAND_CONFIG_PHY_REG = 0, BASEBAND_CONFIG_AGC_TAB = 1, }; enum rf_content { radioa_txt = 0, radiob_txt = 1, radioc_txt = 2, radiod_txt = 3 }; static inline void rtl92d_acquire_cckandrw_pagea_ctl(struct ieee80211_hw *hw, unsigned long *flag) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->rtlhal.interfaceindex == 1) spin_lock_irqsave(&rtlpriv->locks.cck_and_rw_pagea_lock, *flag); } static inline void rtl92d_release_cckandrw_pagea_ctl(struct ieee80211_hw *hw, unsigned long *flag) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->rtlhal.interfaceindex == 1) spin_unlock_irqrestore(&rtlpriv->locks.cck_and_rw_pagea_lock, *flag); } extern u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask); extern void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data); extern u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); extern void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); extern bool rtl92d_phy_mac_config(struct ieee80211_hw *hw); extern bool rtl92d_phy_bb_config(struct ieee80211_hw *hw); extern bool rtl92d_phy_rf_config(struct ieee80211_hw *hw); extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, enum radio_path rfpath); extern void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); extern void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); extern void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation); extern void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type); extern u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw); bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum rf_content content, enum radio_path rfpath); bool rtl92d_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); extern bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); void rtl92d_phy_config_macphymode(struct ieee80211_hw *hw); void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw); u8 rtl92d_get_chnlgroup_fromarray(u8 chnl); void rtl92d_phy_set_poweron(struct ieee80211_hw *hw); void rtl92d_phy_config_maccoexist_rfpage(struct ieee80211_hw *hw); bool rtl92d_phy_check_poweroff(struct ieee80211_hw *hw); void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw); void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw); void rtl92d_phy_reset_iqk_result(struct ieee80211_hw *hw); void rtl92d_release_cckandrw_pagea_ctl(struct ieee80211_hw *hw, unsigned long *flag); void rtl92d_acquire_cckandrw_pagea_ctl(struct ieee80211_hw *hw, unsigned long *flag); u8 rtl92d_get_rightchnlplace_for_iqk(u8 chnl); void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel); void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/phy.c0000644000175000017500000035724512026211315024563 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../ps.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "table.h" #include "sw.h" #include "hw.h" #define MAX_RF_IMR_INDEX 12 #define MAX_RF_IMR_INDEX_NORMAL 13 #define RF_REG_NUM_FOR_C_CUT_5G 6 #define RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA 7 #define RF_REG_NUM_FOR_C_CUT_2G 5 #define RF_CHNL_NUM_5G 19 #define RF_CHNL_NUM_5G_40M 17 #define TARGET_CHNL_NUM_5G 221 #define TARGET_CHNL_NUM_2G 14 #define CV_CURVE_CNT 64 static u32 rf_reg_for_5g_swchnl_normal[MAX_RF_IMR_INDEX_NORMAL] = { 0, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x0 }; static u8 rf_reg_for_c_cut_5g[RF_REG_NUM_FOR_C_CUT_5G] = { RF_SYN_G1, RF_SYN_G2, RF_SYN_G3, RF_SYN_G4, RF_SYN_G5, RF_SYN_G6 }; static u8 rf_reg_for_c_cut_2g[RF_REG_NUM_FOR_C_CUT_2G] = { RF_SYN_G1, RF_SYN_G2, RF_SYN_G3, RF_SYN_G7, RF_SYN_G8 }; static u8 rf_for_c_cut_5g_internal_pa[RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA] = { 0x0B, 0x48, 0x49, 0x4B, 0x03, 0x04, 0x0E }; static u32 rf_reg_mask_for_c_cut_2g[RF_REG_NUM_FOR_C_CUT_2G] = { BIT(19) | BIT(18) | BIT(17) | BIT(14) | BIT(1), BIT(10) | BIT(9), BIT(18) | BIT(17) | BIT(16) | BIT(1), BIT(2) | BIT(1), BIT(15) | BIT(14) | BIT(13) | BIT(12) | BIT(11) }; static u8 rf_chnl_5g[RF_CHNL_NUM_5G] = { 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 }; static u8 rf_chnl_5g_40m[RF_CHNL_NUM_5G_40M] = { 38, 42, 46, 50, 54, 58, 62, 102, 106, 110, 114, 118, 122, 126, 130, 134, 138 }; static u32 rf_reg_pram_c_5g[5][RF_REG_NUM_FOR_C_CUT_5G] = { {0xE43BE, 0xFC638, 0x77C0A, 0xDE471, 0xd7110, 0x8EB04}, {0xE43BE, 0xFC078, 0xF7C1A, 0xE0C71, 0xD7550, 0xAEB04}, {0xE43BF, 0xFF038, 0xF7C0A, 0xDE471, 0xE5550, 0xAEB04}, {0xE43BF, 0xFF079, 0xF7C1A, 0xDE471, 0xE5550, 0xAEB04}, {0xE43BF, 0xFF038, 0xF7C1A, 0xDE471, 0xd7550, 0xAEB04} }; static u32 rf_reg_param_for_c_cut_2g[3][RF_REG_NUM_FOR_C_CUT_2G] = { {0x643BC, 0xFC038, 0x77C1A, 0x41289, 0x01840}, {0x643BC, 0xFC038, 0x07C1A, 0x41289, 0x01840}, {0x243BC, 0xFC438, 0x07C1A, 0x4128B, 0x0FC41} }; static u32 rf_syn_g4_for_c_cut_2g = 0xD1C31 & 0x7FF; static u32 rf_pram_c_5g_int_pa[3][RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA] = { {0x01a00, 0x40443, 0x00eb5, 0x89bec, 0x94a12, 0x94a12, 0x94a12}, {0x01800, 0xc0443, 0x00730, 0x896ee, 0x94a52, 0x94a52, 0x94a52}, {0x01800, 0xc0443, 0x00730, 0x896ee, 0x94a12, 0x94a12, 0x94a12} }; /* [mode][patha+b][reg] */ static u32 rf_imr_param_normal[1][3][MAX_RF_IMR_INDEX_NORMAL] = { { /* channel 1-14. */ { 0x70000, 0x00ff0, 0x4400f, 0x00ff0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64888, 0xe266c, 0x00090, 0x22fff }, /* path 36-64 */ { 0x70000, 0x22880, 0x4470f, 0x55880, 0x00070, 0x88000, 0x0, 0x88080, 0x70000, 0x64a82, 0xe466c, 0x00090, 0x32c9a }, /* 100 -165 */ { 0x70000, 0x44880, 0x4477f, 0x77880, 0x00070, 0x88000, 0x0, 0x880b0, 0x0, 0x64b82, 0xe466c, 0x00090, 0x32c9a } } }; static u32 curveindex_5g[TARGET_CHNL_NUM_5G] = {0}; static u32 curveindex_2g[TARGET_CHNL_NUM_2G] = {0}; static u32 targetchnl_5g[TARGET_CHNL_NUM_5G] = { 25141, 25116, 25091, 25066, 25041, 25016, 24991, 24966, 24941, 24917, 24892, 24867, 24843, 24818, 24794, 24770, 24765, 24721, 24697, 24672, 24648, 24624, 24600, 24576, 24552, 24528, 24504, 24480, 24457, 24433, 24409, 24385, 24362, 24338, 24315, 24291, 24268, 24245, 24221, 24198, 24175, 24151, 24128, 24105, 24082, 24059, 24036, 24013, 23990, 23967, 23945, 23922, 23899, 23876, 23854, 23831, 23809, 23786, 23764, 23741, 23719, 23697, 23674, 23652, 23630, 23608, 23586, 23564, 23541, 23519, 23498, 23476, 23454, 23432, 23410, 23388, 23367, 23345, 23323, 23302, 23280, 23259, 23237, 23216, 23194, 23173, 23152, 23130, 23109, 23088, 23067, 23046, 23025, 23003, 22982, 22962, 22941, 22920, 22899, 22878, 22857, 22837, 22816, 22795, 22775, 22754, 22733, 22713, 22692, 22672, 22652, 22631, 22611, 22591, 22570, 22550, 22530, 22510, 22490, 22469, 22449, 22429, 22409, 22390, 22370, 22350, 22336, 22310, 22290, 22271, 22251, 22231, 22212, 22192, 22173, 22153, 22134, 22114, 22095, 22075, 22056, 22037, 22017, 21998, 21979, 21960, 21941, 21921, 21902, 21883, 21864, 21845, 21826, 21807, 21789, 21770, 21751, 21732, 21713, 21695, 21676, 21657, 21639, 21620, 21602, 21583, 21565, 21546, 21528, 21509, 21491, 21473, 21454, 21436, 21418, 21400, 21381, 21363, 21345, 21327, 21309, 21291, 21273, 21255, 21237, 21219, 21201, 21183, 21166, 21148, 21130, 21112, 21095, 21077, 21059, 21042, 21024, 21007, 20989, 20972, 25679, 25653, 25627, 25601, 25575, 25549, 25523, 25497, 25471, 25446, 25420, 25394, 25369, 25343, 25318, 25292, 25267, 25242, 25216, 25191, 25166 }; /* channel 1~14 */ static u32 targetchnl_2g[TARGET_CHNL_NUM_2G] = { 26084, 26030, 25976, 25923, 25869, 25816, 25764, 25711, 25658, 25606, 25554, 25502, 25451, 25328 }; static u32 _rtl92d_phy_calculate_bit_shift(u32 bitmask) { u32 i; for (i = 0; i <= 31; i++) { if (((bitmask >> i) & 0x1) == 1) break; } return i; } u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); u32 returnvalue, originalvalue, bitshift; u8 dbi_direct; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask); if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) { /* mac1 use phy0 read radio_b. */ /* mac0 use phy1 read radio_b. */ if (rtlhal->during_mac1init_radioa) dbi_direct = BIT(3); else if (rtlhal->during_mac0init_radiob) dbi_direct = BIT(3) | BIT(2); originalvalue = rtl92de_read_dword_dbi(hw, (u16)regaddr, dbi_direct); } else { originalvalue = rtl_read_dword(rtlpriv, regaddr); } bitshift = _rtl92d_phy_calculate_bit_shift(bitmask); returnvalue = (originalvalue & bitmask) >> bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "BBR MASK=0x%x Addr[0x%x]=0x%x\n", bitmask, regaddr, originalvalue); return returnvalue; } void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); u8 dbi_direct = 0; u32 originalvalue, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, bitmask, data); if (rtlhal->during_mac1init_radioa) dbi_direct = BIT(3); else if (rtlhal->during_mac0init_radiob) /* mac0 use phy1 write radio_b. */ dbi_direct = BIT(3) | BIT(2); if (bitmask != BMASKDWORD) { if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) originalvalue = rtl92de_read_dword_dbi(hw, (u16) regaddr, dbi_direct); else originalvalue = rtl_read_dword(rtlpriv, regaddr); bitshift = _rtl92d_phy_calculate_bit_shift(bitmask); data = ((originalvalue & (~bitmask)) | (data << bitshift)); } if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) rtl92de_write_dword_dbi(hw, (u16) regaddr, data, dbi_direct); else rtl_write_dword(rtlpriv, regaddr, data); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, bitmask, data); } static u32 _rtl92d_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; u32 newoffset; u32 tmplong, tmplong2; u8 rfpi_enable = 0; u32 retvalue; newoffset = offset; tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD); if (rfpath == RF90_PATH_A) tmplong2 = tmplong; else tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD); tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) | (newoffset << 23) | BLSSIREADEDGE; rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD, tmplong & (~BLSSIREADEDGE)); udelay(10); rtl_set_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD, tmplong2); udelay(50); udelay(50); rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD, tmplong | BLSSIREADEDGE); udelay(10); if (rfpath == RF90_PATH_A) rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, BIT(8)); else if (rfpath == RF90_PATH_B) rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1, BIT(8)); if (rfpi_enable) retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readbackpi, BLSSIREADBACKDATA); else retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readback, BLSSIREADBACKDATA); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x] = 0x%x\n", rfpath, pphyreg->rflssi_readback, retvalue); return retvalue; } static void _rtl92d_phy_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data) { u32 data_and_addr; u32 newoffset; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; newoffset = offset; /* T65 RF */ data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff; rtl_set_bbreg(hw, pphyreg->rf3wire_offset, BMASKDWORD, data_and_addr); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n", rfpath, pphyreg->rf3wire_offset, data_and_addr); } u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 original_value, readback_value, bitshift; unsigned long flags; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n", regaddr, rfpath, bitmask); spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); original_value = _rtl92d_phy_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92d_phy_calculate_bit_shift(bitmask); readback_value = (original_value & bitmask) >> bitshift; spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x), original_value(%#x)\n", regaddr, rfpath, bitmask, original_value); return readback_value; } void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 original_value, bitshift; unsigned long flags; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); if (bitmask == 0) return; spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); if (rtlphy->rf_mode != RF_OP_BY_FW) { if (bitmask != BRFREGOFFSETMASK) { original_value = _rtl92d_phy_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92d_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } _rtl92d_phy_rf_serial_write(hw, rfpath, regaddr, data); } spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); } bool rtl92d_phy_mac_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; u32 arraylength; u32 *ptrarray; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read Rtl819XMACPHY_Array\n"); arraylength = MAC_2T_ARRAYLENGTH; ptrarray = rtl8192de_mac_2tarray; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Img:Rtl819XMAC_Array\n"); for (i = 0; i < arraylength; i = i + 2) rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]); if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY) { /* improve 2-stream TX EVM */ /* rtl_write_byte(rtlpriv, 0x14,0x71); */ /* AMPDU aggregation number 9 */ /* rtl_write_word(rtlpriv, REG_MAX_AGGR_NUM, MAX_AGGR_NUM); */ rtl_write_byte(rtlpriv, REG_MAX_AGGR_NUM, 0x0B); } else { /* 92D need to test to decide the num. */ rtl_write_byte(rtlpriv, REG_MAX_AGGR_NUM, 0x07); } return true; } static void _rtl92d_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); /* RF Interface Sowrtware Control */ /* 16 LSBs if read 32-bit from 0x870 */ rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW; /* 16 MSBs if read 32-bit from 0x870 (16-bit for 0x872) */ rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW; /* 16 LSBs if read 32-bit from 0x874 */ rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW; /* 16 MSBs if read 32-bit from 0x874 (16-bit for 0x876) */ rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW; /* RF Interface Readback Value */ /* 16 LSBs if read 32-bit from 0x8E0 */ rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB; /* 16 MSBs if read 32-bit from 0x8E0 (16-bit for 0x8E2) */ rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB; /* 16 LSBs if read 32-bit from 0x8E4 */ rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB; /* 16 MSBs if read 32-bit from 0x8E4 (16-bit for 0x8E6) */ rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB; /* RF Interface Output (and Enable) */ /* 16 LSBs if read 32-bit from 0x860 */ rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE; /* 16 LSBs if read 32-bit from 0x864 */ rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE; /* RF Interface (Output and) Enable */ /* 16 MSBs if read 32-bit from 0x860 (16-bit for 0x862) */ rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE; /* 16 MSBs if read 32-bit from 0x864 (16-bit for 0x866) */ rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE; /* Addr of LSSI. Wirte RF register by driver */ /* LSSI Parameter */ rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset = RFPGA0_XA_LSSIPARAMETER; rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset = RFPGA0_XB_LSSIPARAMETER; /* RF parameter */ /* BB Band Select */ rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = RFPGA0_XAB_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = RFPGA0_XAB_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = RFPGA0_XCD_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = RFPGA0_XCD_RFPARAMETER; /* Tx AGC Gain Stage (same for all path. Should we remove this?) */ /* Tx gain stage */ rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE; /* Tx gain stage */ rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE; /* Tx gain stage */ rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE; /* Tx gain stage */ rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE; /* Tranceiver A~D HSSI Parameter-1 */ /* wire control parameter1 */ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1; /* wire control parameter1 */ rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1; /* Tranceiver A~D HSSI Parameter-2 */ /* wire control parameter2 */ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2; /* wire control parameter2 */ rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2; /* RF switch Control */ /* TR/Ant switch control */ rtlphy->phyreg_def[RF90_PATH_A].rfswitch_control = RFPGA0_XAB_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_B].rfswitch_control = RFPGA0_XAB_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_C].rfswitch_control = RFPGA0_XCD_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_D].rfswitch_control = RFPGA0_XCD_SWITCHCONTROL; /* AGC control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1; rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1; rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1; rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1; /* AGC control 2 */ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2; rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2; rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2; rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2; /* RX AFE control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbalance = ROFDM0_XARXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbalance = ROFDM0_XBRXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbalance = ROFDM0_XCRXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbalance = ROFDM0_XDRXIQIMBALANCE; /*RX AFE control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE; rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE; rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE; rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE; /* Tx AFE control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbalance = ROFDM0_XATxIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbalance = ROFDM0_XBTxIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbalance = ROFDM0_XCTxIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbalance = ROFDM0_XDTxIQIMBALANCE; /* Tx AFE control 2 */ rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATxAFE; rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTxAFE; rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTxAFE; rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTxAFE; /* Tranceiver LSSI Readback SI mode */ rtlphy->phyreg_def[RF90_PATH_A].rflssi_readback = RFPGA0_XA_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_B].rflssi_readback = RFPGA0_XB_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_C].rflssi_readback = RFPGA0_XC_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_D].rflssi_readback = RFPGA0_XD_LSSIREADBACK; /* Tranceiver LSSI Readback PI mode */ rtlphy->phyreg_def[RF90_PATH_A].rflssi_readbackpi = TRANSCEIVERA_HSPI_READBACK; rtlphy->phyreg_def[RF90_PATH_B].rflssi_readbackpi = TRANSCEIVERB_HSPI_READBACK; } static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype) { int i; u32 *phy_regarray_table; u32 *agctab_array_table = NULL; u32 *agctab_5garray_table; u16 phy_reg_arraylen, agctab_arraylen = 0, agctab_5garraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); /* Normal chip,Mac0 use AGC_TAB.txt for 2G and 5G band. */ if (rtlhal->interfaceindex == 0) { agctab_arraylen = AGCTAB_ARRAYLENGTH; agctab_array_table = rtl8192de_agctab_array; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, " ===> phy:MAC0, Rtl819XAGCTAB_Array\n"); } else { if (rtlhal->current_bandtype == BAND_ON_2_4G) { agctab_arraylen = AGCTAB_2G_ARRAYLENGTH; agctab_array_table = rtl8192de_agctab_2garray; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, " ===> phy:MAC1, Rtl819XAGCTAB_2GArray\n"); } else { agctab_5garraylen = AGCTAB_5G_ARRAYLENGTH; agctab_5garray_table = rtl8192de_agctab_5garray; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, " ===> phy:MAC1, Rtl819XAGCTAB_5GArray\n"); } } phy_reg_arraylen = PHY_REG_2T_ARRAYLENGTH; phy_regarray_table = rtl8192de_phy_reg_2tarray; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, " ===> phy:Rtl819XPHY_REG_Array_PG\n"); if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_arraylen; i = i + 2) { if (phy_regarray_table[i] == 0xfe) mdelay(50); else if (phy_regarray_table[i] == 0xfd) mdelay(5); else if (phy_regarray_table[i] == 0xfc) mdelay(1); else if (phy_regarray_table[i] == 0xfb) udelay(50); else if (phy_regarray_table[i] == 0xfa) udelay(5); else if (phy_regarray_table[i] == 0xf9) udelay(1); rtl_set_bbreg(hw, phy_regarray_table[i], BMASKDWORD, phy_regarray_table[i + 1]); udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The phy_regarray_table[0] is %x Rtl819XPHY_REGArray[1] is %x\n", phy_regarray_table[i], phy_regarray_table[i + 1]); } } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { if (rtlhal->interfaceindex == 0) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], BMASKDWORD, agctab_array_table[i + 1]); /* Add 1us delay between BB/RF register * setting. */ udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The Rtl819XAGCTAB_Array_Table[0] is %ul Rtl819XPHY_REGArray[1] is %ul\n", agctab_array_table[i], agctab_array_table[i + 1]); } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Normal Chip, MAC0, load Rtl819XAGCTAB_Array\n"); } else { if (rtlhal->current_bandtype == BAND_ON_2_4G) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], BMASKDWORD, agctab_array_table[i + 1]); /* Add 1us delay between BB/RF register * setting. */ udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The Rtl819XAGCTAB_Array_Table[0] is %ul Rtl819XPHY_REGArray[1] is %ul\n", agctab_array_table[i], agctab_array_table[i + 1]); } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Load Rtl819XAGCTAB_2GArray\n"); } else { for (i = 0; i < agctab_5garraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_5garray_table[i], BMASKDWORD, agctab_5garray_table[i + 1]); /* Add 1us delay between BB/RF registeri * setting. */ udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The Rtl819XAGCTAB_5GArray_Table[0] is %ul Rtl819XPHY_REGArray[1] is %ul\n", agctab_5garray_table[i], agctab_5garray_table[i + 1]); } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Load Rtl819XAGCTAB_5GArray\n"); } } } return true; } static void _rtl92d_store_pwrindex_diffrate_offset(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); int index; if (regaddr == RTXAGC_A_RATE18_06) index = 0; else if (regaddr == RTXAGC_A_RATE54_24) index = 1; else if (regaddr == RTXAGC_A_CCK1_MCS32) index = 6; else if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0xffffff00) index = 7; else if (regaddr == RTXAGC_A_MCS03_MCS00) index = 2; else if (regaddr == RTXAGC_A_MCS07_MCS04) index = 3; else if (regaddr == RTXAGC_A_MCS11_MCS08) index = 4; else if (regaddr == RTXAGC_A_MCS15_MCS12) index = 5; else if (regaddr == RTXAGC_B_RATE18_06) index = 8; else if (regaddr == RTXAGC_B_RATE54_24) index = 9; else if (regaddr == RTXAGC_B_CCK1_55_MCS32) index = 14; else if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0x000000ff) index = 15; else if (regaddr == RTXAGC_B_MCS03_MCS00) index = 10; else if (regaddr == RTXAGC_B_MCS07_MCS04) index = 11; else if (regaddr == RTXAGC_B_MCS11_MCS08) index = 12; else if (regaddr == RTXAGC_B_MCS15_MCS12) index = 13; else return; rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][index] = data; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "MCSTxPowerLevelOriginalOffset[%d][%d] = 0x%ulx\n", rtlphy->pwrgroup_cnt, index, rtlphy->mcs_txpwrlevel_origoffset [rtlphy->pwrgroup_cnt][index]); if (index == 13) rtlphy->pwrgroup_cnt++; } static bool _rtl92d_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype) { struct rtl_priv *rtlpriv = rtl_priv(hw); int i; u32 *phy_regarray_table_pg; u16 phy_regarray_pg_len; phy_regarray_pg_len = PHY_REG_ARRAY_PG_LENGTH; phy_regarray_table_pg = rtl8192de_phy_reg_array_pg; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray_pg_len; i = i + 3) { if (phy_regarray_table_pg[i] == 0xfe) mdelay(50); else if (phy_regarray_table_pg[i] == 0xfd) mdelay(5); else if (phy_regarray_table_pg[i] == 0xfc) mdelay(1); else if (phy_regarray_table_pg[i] == 0xfb) udelay(50); else if (phy_regarray_table_pg[i] == 0xfa) udelay(5); else if (phy_regarray_table_pg[i] == 0xf9) udelay(1); _rtl92d_store_pwrindex_diffrate_offset(hw, phy_regarray_table_pg[i], phy_regarray_table_pg[i + 1], phy_regarray_table_pg[i + 2]); } } else { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "configtype != BaseBand_Config_PHY_REG\n"); } return true; } static bool _rtl92d_phy_bb_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); bool rtstatus = true; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "==>\n"); rtstatus = _rtl92d_phy_config_bb_with_headerfile(hw, BASEBAND_CONFIG_PHY_REG); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write BB Reg Fail!!\n"); return false; } /* if (rtlphy->rf_type == RF_1T2R) { * _rtl92c_phy_bb_config_1t(hw); * RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Config to 1T!!\n"); *} */ if (rtlefuse->autoload_failflag == false) { rtlphy->pwrgroup_cnt = 0; rtstatus = _rtl92d_phy_config_bb_with_pgheaderfile(hw, BASEBAND_CONFIG_PHY_REG); } if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "BB_PG Reg Fail!!\n"); return false; } rtstatus = _rtl92d_phy_config_bb_with_headerfile(hw, BASEBAND_CONFIG_AGC_TAB); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "AGC Table Fail\n"); return false; } rtlphy->cck_high_power = (bool) (rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, 0x200)); return true; } bool rtl92d_phy_bb_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u16 regval; u32 regvaldw; u8 value; _rtl92d_phy_init_bb_rf_register_definition(hw); regval = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, regval | BIT(13) | BIT(0) | BIT(1)); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x83); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL + 1, 0xdb); /* 0x1f bit7 bit6 represent for mac0/mac1 driver ready */ value = rtl_read_byte(rtlpriv, REG_RF_CTRL); rtl_write_byte(rtlpriv, REG_RF_CTRL, value | RF_EN | RF_RSTB | RF_SDMRSTB); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE | FEN_BB_GLB_RSTn | FEN_BBRSTB); rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL + 1, 0x80); if (!(IS_92D_SINGLEPHY(rtlpriv->rtlhal.version))) { regvaldw = rtl_read_dword(rtlpriv, REG_LEDCFG0); rtl_write_dword(rtlpriv, REG_LEDCFG0, regvaldw | BIT(23)); } return _rtl92d_phy_bb_config(hw); } bool rtl92d_phy_rf_config(struct ieee80211_hw *hw) { return rtl92d_phy_rf6052_config(hw); } bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum rf_content content, enum radio_path rfpath) { int i; u32 *radioa_array_table; u32 *radiob_array_table; u16 radioa_arraylen, radiob_arraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); radioa_arraylen = RADIOA_2T_ARRAYLENGTH; radioa_array_table = rtl8192de_radioa_2tarray; radiob_arraylen = RADIOB_2T_ARRAYLENGTH; radiob_array_table = rtl8192de_radiob_2tarray; if (rtlpriv->efuse.internal_pa_5g[0]) { radioa_arraylen = RADIOA_2T_INT_PA_ARRAYLENGTH; radioa_array_table = rtl8192de_radioa_2t_int_paarray; } if (rtlpriv->efuse.internal_pa_5g[1]) { radiob_arraylen = RADIOB_2T_INT_PA_ARRAYLENGTH; radiob_array_table = rtl8192de_radiob_2t_int_paarray; } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PHY_ConfigRFWithHeaderFile() Radio_A:Rtl819XRadioA_1TArray\n"); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PHY_ConfigRFWithHeaderFile() Radio_B:Rtl819XRadioB_1TArray\n"); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio No %x\n", rfpath); /* this only happens when DMDP, mac0 start on 2.4G, * mac1 start on 5G, mac 0 has to set phy0&phy1 * pathA or mac1 has to set phy0&phy1 pathA */ if ((content == radiob_txt) && (rfpath == RF90_PATH_A)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, " ===> althougth Path A, we load radiob.txt\n"); radioa_arraylen = radiob_arraylen; radioa_array_table = radiob_array_table; } switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radioa_arraylen; i = i + 2) { if (radioa_array_table[i] == 0xfe) { mdelay(50); } else if (radioa_array_table[i] == 0xfd) { /* delay_ms(5); */ mdelay(5); } else if (radioa_array_table[i] == 0xfc) { /* delay_ms(1); */ mdelay(1); } else if (radioa_array_table[i] == 0xfb) { udelay(50); } else if (radioa_array_table[i] == 0xfa) { udelay(5); } else if (radioa_array_table[i] == 0xf9) { udelay(1); } else { rtl_set_rfreg(hw, rfpath, radioa_array_table[i], BRFREGOFFSETMASK, radioa_array_table[i + 1]); /* Add 1us delay between BB/RF register set. */ udelay(1); } } break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { if (radiob_array_table[i] == 0xfe) { /* Delay specific ms. Only RF configuration * requires delay. */ mdelay(50); } else if (radiob_array_table[i] == 0xfd) { /* delay_ms(5); */ mdelay(5); } else if (radiob_array_table[i] == 0xfc) { /* delay_ms(1); */ mdelay(1); } else if (radiob_array_table[i] == 0xfb) { udelay(50); } else if (radiob_array_table[i] == 0xfa) { udelay(5); } else if (radiob_array_table[i] == 0xf9) { udelay(1); } else { rtl_set_rfreg(hw, rfpath, radiob_array_table[i], BRFREGOFFSETMASK, radiob_array_table[i + 1]); /* Add 1us delay between BB/RF register set. */ udelay(1); } } break; case RF90_PATH_C: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; case RF90_PATH_D: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } return true; } void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); rtlphy->default_initialgain[0] = (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, BMASKBYTE0); rtlphy->default_initialgain[1] = (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, BMASKBYTE0); rtlphy->default_initialgain[2] = (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, BMASKBYTE0); rtlphy->default_initialgain[3] = (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, BMASKBYTE0); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x\n", rtlphy->default_initialgain[0], rtlphy->default_initialgain[1], rtlphy->default_initialgain[2], rtlphy->default_initialgain[3]); rtlphy->framesync = (u8)rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3, BMASKBYTE0); rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2, BMASKDWORD); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Default framesync (0x%x) = 0x%x\n", ROFDM0_RXDETECTOR3, rtlphy->framesync); } static void _rtl92d_get_txpower_index(struct ieee80211_hw *hw, u8 channel, u8 *cckpowerlevel, u8 *ofdmpowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 index = (channel - 1); /* 1. CCK */ if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* RF-A */ cckpowerlevel[RF90_PATH_A] = rtlefuse->txpwrlevel_cck[RF90_PATH_A][index]; /* RF-B */ cckpowerlevel[RF90_PATH_B] = rtlefuse->txpwrlevel_cck[RF90_PATH_B][index]; } else { cckpowerlevel[RF90_PATH_A] = 0; cckpowerlevel[RF90_PATH_B] = 0; } /* 2. OFDM for 1S or 2S */ if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_1T1R) { /* Read HT 40 OFDM TX power */ ofdmpowerlevel[RF90_PATH_A] = rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_A][index]; ofdmpowerlevel[RF90_PATH_B] = rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_B][index]; } else if (rtlphy->rf_type == RF_2T2R) { /* Read HT 40 OFDM TX power */ ofdmpowerlevel[RF90_PATH_A] = rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_A][index]; ofdmpowerlevel[RF90_PATH_B] = rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_B][index]; } } static void _rtl92d_ccxpower_index_check(struct ieee80211_hw *hw, u8 channel, u8 *cckpowerlevel, u8 *ofdmpowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); rtlphy->cur_cck_txpwridx = cckpowerlevel[0]; rtlphy->cur_ofdm24g_txpwridx = ofdmpowerlevel[0]; } static u8 _rtl92c_phy_get_rightchnlplace(u8 chnl) { u8 channel_5g[59] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 149, 151, 153, 155, 157, 159, 161, 163, 165 }; u8 place = chnl; if (chnl > 14) { for (place = 14; place < sizeof(channel_5g); place++) { if (channel_5g[place] == chnl) { place++; break; } } } return place; } void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); u8 cckpowerlevel[2], ofdmpowerlevel[2]; if (!rtlefuse->txpwr_fromeprom) return; channel = _rtl92c_phy_get_rightchnlplace(channel); _rtl92d_get_txpower_index(hw, channel, &cckpowerlevel[0], &ofdmpowerlevel[0]); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) _rtl92d_ccxpower_index_check(hw, channel, &cckpowerlevel[0], &ofdmpowerlevel[0]); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) rtl92d_phy_rf6052_set_cck_txpower(hw, &cckpowerlevel[0]); rtl92d_phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel); } void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); enum io_type iotype; if (!is_hal_stop(rtlhal)) { switch (operation) { case SCAN_OPT_BACKUP: rtlhal->current_bandtypebackup = rtlhal->current_bandtype; iotype = IO_CMD_PAUSE_DM_BY_SCAN; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, (u8 *)&iotype); break; case SCAN_OPT_RESTORE: iotype = IO_CMD_RESUME_DM_BY_SCAN; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, (u8 *)&iotype); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Unknown Scan Backup operation\n"); break; } } } void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); unsigned long flag = 0; u8 reg_prsr_rsc; u8 reg_bw_opmode; if (rtlphy->set_bwmode_inprogress) return; if ((is_hal_stop(rtlhal)) || (RT_CANNOT_IO(hw))) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "FALSE driver sleep or unload\n"); return; } rtlphy->set_bwmode_inprogress = true; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "Switch to %s bandwidth\n", rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ? "20MHz" : "40MHz"); reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE); reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2); switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: reg_bw_opmode |= BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); break; case HT_CHANNEL_WIDTH_20_40: reg_bw_opmode &= ~BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); reg_prsr_rsc = (reg_prsr_rsc & 0x90) | (mac->cur_40_prime_sc << 5); rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0); /* SET BIT10 BIT11 for receive cck */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10) | BIT(11), 3); break; case HT_CHANNEL_WIDTH_20_40: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1); /* Set Control channel to upper or lower. * These settings are required only for 40MHz */ if (rtlhal->current_bandtype == BAND_ON_2_4G) { rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCKSIDEBAND, (mac->cur_40_prime_sc >> 1)); rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc); /* SET BIT10 BIT11 for receive cck */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10) | BIT(11), 0); rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)), (mac->cur_40_prime_sc == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } rtl92d_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); rtlphy->set_bwmode_inprogress = false; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); } static void _rtl92d_phy_stop_trx_before_changeband(struct ieee80211_hw *hw) { rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0); rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x00); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x0); } static void rtl92d_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 value8; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "==>\n"); rtlhal->bandset = band; rtlhal->current_bandtype = band; if (IS_92D_SINGLEPHY(rtlhal->version)) rtlhal->bandset = BAND_ON_BOTH; /* stop RX/Tx */ _rtl92d_phy_stop_trx_before_changeband(hw); /* reconfig BB/RF according to wireless mode */ if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* BB & RF Config */ RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "====>2.4G\n"); if (rtlhal->interfaceindex == 1) _rtl92d_phy_config_bb_with_headerfile(hw, BASEBAND_CONFIG_AGC_TAB); } else { /* 5G band */ RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "====>5G\n"); if (rtlhal->interfaceindex == 1) _rtl92d_phy_config_bb_with_headerfile(hw, BASEBAND_CONFIG_AGC_TAB); } rtl92d_update_bbrf_configuration(hw); if (rtlhal->current_bandtype == BAND_ON_2_4G) rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1); /* 20M BW. */ /* rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); */ rtlhal->reloadtxpowerindex = true; /* notice fw know band status 0x81[1]/0x53[1] = 0: 5G, 1: 2G */ if (rtlhal->current_bandtype == BAND_ON_2_4G) { value8 = rtl_read_byte(rtlpriv, (rtlhal->interfaceindex == 0 ? REG_MAC0 : REG_MAC1)); value8 |= BIT(1); rtl_write_byte(rtlpriv, (rtlhal->interfaceindex == 0 ? REG_MAC0 : REG_MAC1), value8); } else { value8 = rtl_read_byte(rtlpriv, (rtlhal->interfaceindex == 0 ? REG_MAC0 : REG_MAC1)); value8 &= (~BIT(1)); rtl_write_byte(rtlpriv, (rtlhal->interfaceindex == 0 ? REG_MAC0 : REG_MAC1), value8); } mdelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "<==Switch Band OK\n"); } static void _rtl92d_phy_reload_imr_setting(struct ieee80211_hw *hw, u8 channel, u8 rfpath) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 imr_num = MAX_RF_IMR_INDEX; u32 rfmask = BRFREGOFFSETMASK; u8 group, i; unsigned long flag = 0; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "====>path %d\n", rfpath); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "====>5G\n"); rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(25) | BIT(24), 0); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf); /* fc area 0xd2c */ if (channel > 99) rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(13) | BIT(14), 2); else rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(13) | BIT(14), 1); /* leave 0 for channel1-14. */ group = channel <= 64 ? 1 : 2; imr_num = MAX_RF_IMR_INDEX_NORMAL; for (i = 0; i < imr_num; i++) rtl_set_rfreg(hw, (enum radio_path)rfpath, rf_reg_for_5g_swchnl_normal[i], rfmask, rf_imr_param_normal[0][group][i]); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 1); } else { /* G band. */ RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, "Load RF IMR parameters for G band. IMR already setting %d\n", rtlpriv->rtlhal.load_imrandiqk_setting_for2g); RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "====>2.4G\n"); if (!rtlpriv->rtlhal.load_imrandiqk_setting_for2g) { RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, "Load RF IMR parameters for G band. %d\n", rfpath); rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(25) | BIT(24), 0); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf); imr_num = MAX_RF_IMR_INDEX_NORMAL; for (i = 0; i < imr_num; i++) { rtl_set_rfreg(hw, (enum radio_path)rfpath, rf_reg_for_5g_swchnl_normal[i], BRFREGOFFSETMASK, rf_imr_param_normal[0][0][i]); } rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN | BCCKEN, 3); rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } } RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "<====\n"); } static void _rtl92d_phy_enable_rf_env(struct ieee80211_hw *hw, u8 rfpath, u32 *pu4_regval) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "====>\n"); /*----Store original RFENV control type----*/ switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: *pu4_regval = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV); break; case RF90_PATH_B: case RF90_PATH_D: *pu4_regval = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16); break; } /*----Set RF_ENV enable----*/ rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1); udelay(1); /*----Set RF_ENV output high----*/ rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); udelay(1); /* Set bit number of Address and Data for RF register */ /* Set 1 to 4 bits for 8255 */ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREADDRESSLENGTH, 0x0); udelay(1); /*Set 0 to 12 bits for 8255 */ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0); udelay(1); RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "<====\n"); } static void _rtl92d_phy_restore_rf_env(struct ieee80211_hw *hw, u8 rfpath, u32 *pu4_regval) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "=====>\n"); /*----Restore RFENV control type----*/ switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV, *pu4_regval); break; case RF90_PATH_B: case RF90_PATH_D: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16, *pu4_regval); break; } RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "<=====\n"); } static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u8 path = rtlhal->current_bandtype == BAND_ON_5G ? RF90_PATH_A : RF90_PATH_B; u8 index = 0, i = 0, rfpath = RF90_PATH_A; bool need_pwr_down = false, internal_pa = false; u32 u4regvalue, mask = 0x1C000, value = 0, u4tmp, u4tmp2; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "====>\n"); /* config path A for 5G */ if (rtlhal->current_bandtype == BAND_ON_5G) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "====>5G\n"); u4tmp = curveindex_5g[channel - 1]; RTPRINT(rtlpriv, FINIT, INIT_IQK, "ver 1 set RF-A, 5G, 0x28 = 0x%x !!\n", u4tmp); for (i = 0; i < RF_CHNL_NUM_5G; i++) { if (channel == rf_chnl_5g[i] && channel <= 140) index = 0; } for (i = 0; i < RF_CHNL_NUM_5G_40M; i++) { if (channel == rf_chnl_5g_40m[i] && channel <= 140) index = 1; } if (channel == 149 || channel == 155 || channel == 161) index = 2; else if (channel == 151 || channel == 153 || channel == 163 || channel == 165) index = 3; else if (channel == 157 || channel == 159) index = 4; if (rtlhal->macphymode == DUALMAC_DUALPHY && rtlhal->interfaceindex == 1) { need_pwr_down = rtl92d_phy_enable_anotherphy(hw, false); rtlhal->during_mac1init_radioa = true; /* asume no this case */ if (need_pwr_down) _rtl92d_phy_enable_rf_env(hw, path, &u4regvalue); } for (i = 0; i < RF_REG_NUM_FOR_C_CUT_5G; i++) { if (i == 0 && (rtlhal->macphymode == DUALMAC_DUALPHY)) { rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], BRFREGOFFSETMASK, 0xE439D); } else if (rf_reg_for_c_cut_5g[i] == RF_SYN_G4) { u4tmp2 = (rf_reg_pram_c_5g[index][i] & 0x7FF) | (u4tmp << 11); if (channel == 36) u4tmp2 &= ~(BIT(7) | BIT(6)); rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], BRFREGOFFSETMASK, u4tmp2); } else { rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], BRFREGOFFSETMASK, rf_reg_pram_c_5g[index][i]); } RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "offset 0x%x value 0x%x path %d index %d readback 0x%x\n", rf_reg_for_c_cut_5g[i], rf_reg_pram_c_5g[index][i], path, index, rtl_get_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], BRFREGOFFSETMASK)); } if (need_pwr_down) _rtl92d_phy_restore_rf_env(hw, path, &u4regvalue); if (rtlhal->during_mac1init_radioa) rtl92d_phy_powerdown_anotherphy(hw, false); if (channel < 149) value = 0x07; else if (channel >= 149) value = 0x02; if (channel >= 36 && channel <= 64) index = 0; else if (channel >= 100 && channel <= 140) index = 1; else index = 2; for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { if (rtlhal->macphymode == DUALMAC_DUALPHY && rtlhal->interfaceindex == 1) /* MAC 1 5G */ internal_pa = rtlpriv->efuse.internal_pa_5g[1]; else internal_pa = rtlpriv->efuse.internal_pa_5g[rfpath]; if (internal_pa) { for (i = 0; i < RF_REG_NUM_FOR_C_CUT_5G_INTERNALPA; i++) { rtl_set_rfreg(hw, rfpath, rf_for_c_cut_5g_internal_pa[i], BRFREGOFFSETMASK, rf_pram_c_5g_int_pa[index][i]); RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "offset 0x%x value 0x%x path %d index %d\n", rf_for_c_cut_5g_internal_pa[i], rf_pram_c_5g_int_pa[index][i], rfpath, index); } } else { rtl_set_rfreg(hw, (enum radio_path)rfpath, 0x0B, mask, value); } } } else if (rtlhal->current_bandtype == BAND_ON_2_4G) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "====>2.4G\n"); u4tmp = curveindex_2g[channel - 1]; RTPRINT(rtlpriv, FINIT, INIT_IQK, "ver 3 set RF-B, 2G, 0x28 = 0x%x !!\n", u4tmp); if (channel == 1 || channel == 2 || channel == 4 || channel == 9 || channel == 10 || channel == 11 || channel == 12) index = 0; else if (channel == 3 || channel == 13 || channel == 14) index = 1; else if (channel >= 5 && channel <= 8) index = 2; if (rtlhal->macphymode == DUALMAC_DUALPHY) { path = RF90_PATH_A; if (rtlhal->interfaceindex == 0) { need_pwr_down = rtl92d_phy_enable_anotherphy(hw, true); rtlhal->during_mac0init_radiob = true; if (need_pwr_down) _rtl92d_phy_enable_rf_env(hw, path, &u4regvalue); } } for (i = 0; i < RF_REG_NUM_FOR_C_CUT_2G; i++) { if (rf_reg_for_c_cut_2g[i] == RF_SYN_G7) rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_2g[i], BRFREGOFFSETMASK, (rf_reg_param_for_c_cut_2g[index][i] | BIT(17))); else rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_2g[i], BRFREGOFFSETMASK, rf_reg_param_for_c_cut_2g [index][i]); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "offset 0x%x value 0x%x mak 0x%x path %d index %d readback 0x%x\n", rf_reg_for_c_cut_2g[i], rf_reg_param_for_c_cut_2g[index][i], rf_reg_mask_for_c_cut_2g[i], path, index, rtl_get_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_2g[i], BRFREGOFFSETMASK)); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "cosa ver 3 set RF-B, 2G, 0x28 = 0x%x !!\n", rf_syn_g4_for_c_cut_2g | (u4tmp << 11)); rtl_set_rfreg(hw, (enum radio_path)path, RF_SYN_G4, BRFREGOFFSETMASK, rf_syn_g4_for_c_cut_2g | (u4tmp << 11)); if (need_pwr_down) _rtl92d_phy_restore_rf_env(hw, path, &u4regvalue); if (rtlhal->during_mac0init_radiob) rtl92d_phy_powerdown_anotherphy(hw, true); } RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "<====\n"); } u8 rtl92d_get_rightchnlplace_for_iqk(u8 chnl) { u8 channel_all[59] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 149, 151, 153, 155, 157, 159, 161, 163, 165 }; u8 place = chnl; if (chnl > 14) { for (place = 14; place < sizeof(channel_all); place++) { if (channel_all[place] == chnl) return place - 13; } } return 0; } #define MAX_TOLERANCE 5 #define IQK_DELAY_TIME 1 /* ms */ #define MAX_TOLERANCE_92D 3 /* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */ static u8 _rtl92d_phy_patha_iqk(struct ieee80211_hw *hw, bool configpathb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u32 regeac, rege94, rege9c, regea4; u8 result = 0; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK!\n"); /* path-A IQK setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n"); if (rtlhal->interfaceindex == 0) { rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c1f); rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c1f); } else { rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c22); rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c22); } rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140102); rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x28160206); /* path-B IQK setting */ if (configpathb) { rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x10008c22); rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x10008c22); rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140102); rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x28160206); } /* LO calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n"); rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911); /* One shot, path A LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000); rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Delay %d ms for One shot, path A LOK & IQK\n", IQK_DELAY_TIME); mdelay(IQK_DELAY_TIME); /* Check failed */ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe94 = 0x%x\n", rege94); rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe9c = 0x%x\n", rege9c); regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xea4 = 0x%x\n", regea4); if (!(regeac & BIT(28)) && (((rege94 & 0x03FF0000) >> 16) != 0x142) && (((rege9c & 0x03FF0000) >> 16) != 0x42)) result |= 0x01; else /* if Tx not OK, ignore Rx */ return result; /* if Tx is OK, check whether Rx is OK */ if (!(regeac & BIT(27)) && (((regea4 & 0x03FF0000) >> 16) != 0x132) && (((regeac & 0x03FF0000) >> 16) != 0x36)) result |= 0x02; else RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A Rx IQK fail!!\n"); return result; } /* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */ static u8 _rtl92d_phy_patha_iqk_5g_normal(struct ieee80211_hw *hw, bool configpathb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 regeac, rege94, rege9c, regea4; u8 result = 0; u8 i; u8 retrycount = 2; u32 TxOKBit = BIT(28), RxOKBit = BIT(27); if (rtlhal->interfaceindex == 1) { /* PHY1 */ TxOKBit = BIT(31); RxOKBit = BIT(30); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK!\n"); /* path-A IQK setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n"); rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f); rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f); rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140307); rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68160960); /* path-B IQK setting */ if (configpathb) { rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f); rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f); rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82110000); rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68110000); } /* LO calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n"); rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911); /* path-A PA on */ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x07000f60); rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD, 0x66e60e30); for (i = 0; i < retrycount; i++) { /* One shot, path A LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000); rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Delay %d ms for One shot, path A LOK & IQK.\n", IQK_DELAY_TIME); mdelay(IQK_DELAY_TIME * 10); /* Check failed */ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe94 = 0x%x\n", rege94); rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe9c = 0x%x\n", rege9c); regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xea4 = 0x%x\n", regea4); if (!(regeac & TxOKBit) && (((rege94 & 0x03FF0000) >> 16) != 0x142)) { result |= 0x01; } else { /* if Tx not OK, ignore Rx */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A Tx IQK fail!!\n"); continue; } /* if Tx is OK, check whether Rx is OK */ if (!(regeac & RxOKBit) && (((regea4 & 0x03FF0000) >> 16) != 0x132)) { result |= 0x02; break; } else { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A Rx IQK fail!!\n"); } } /* path A PA off */ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, rtlphy->iqk_bb_backup[0]); rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD, rtlphy->iqk_bb_backup[1]); return result; } /* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */ static u8 _rtl92d_phy_pathb_iqk(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 regeac, regeb4, regebc, regec4, regecc; u8 result = 0; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK!\n"); /* One shot, path B LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000002); rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Delay %d ms for One shot, path B LOK & IQK\n", IQK_DELAY_TIME); mdelay(IQK_DELAY_TIME); /* Check failed */ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeb4 = 0x%x\n", regeb4); regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xebc = 0x%x\n", regebc); regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xec4 = 0x%x\n", regec4); regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xecc = 0x%x\n", regecc); if (!(regeac & BIT(31)) && (((regeb4 & 0x03FF0000) >> 16) != 0x142) && (((regebc & 0x03FF0000) >> 16) != 0x42)) result |= 0x01; else return result; if (!(regeac & BIT(30)) && (((regec4 & 0x03FF0000) >> 16) != 0x132) && (((regecc & 0x03FF0000) >> 16) != 0x36)) result |= 0x02; else RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B Rx IQK fail!!\n"); return result; } /* bit0 = 1 => Tx OK, bit1 = 1 => Rx OK */ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 regeac, regeb4, regebc, regec4, regecc; u8 result = 0; u8 i; u8 retrycount = 2; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK!\n"); /* path-A IQK setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n"); rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f); rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f); rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82110000); rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68110000); /* path-B IQK setting */ rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f); rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f); rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140307); rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68160960); /* LO calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n"); rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911); /* path-B PA on */ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x0f600700); rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD, 0x061f0d30); for (i = 0; i < retrycount; i++) { /* One shot, path B LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xfa000000); rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Delay %d ms for One shot, path B LOK & IQK.\n", 10); mdelay(IQK_DELAY_TIME * 10); /* Check failed */ regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeb4 = 0x%x\n", regeb4); regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xebc = 0x%x\n", regebc); regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xec4 = 0x%x\n", regec4); regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xecc = 0x%x\n", regecc); if (!(regeac & BIT(31)) && (((regeb4 & 0x03FF0000) >> 16) != 0x142)) result |= 0x01; else continue; if (!(regeac & BIT(30)) && (((regec4 & 0x03FF0000) >> 16) != 0x132)) { result |= 0x02; break; } else { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B Rx IQK fail!!\n"); } } /* path B PA off */ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, rtlphy->iqk_bb_backup[0]); rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD, rtlphy->iqk_bb_backup[2]); return result; } static void _rtl92d_phy_save_adda_registers(struct ieee80211_hw *hw, u32 *adda_reg, u32 *adda_backup, u32 regnum) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Save ADDA parameters.\n"); for (i = 0; i < regnum; i++) adda_backup[i] = rtl_get_bbreg(hw, adda_reg[i], BMASKDWORD); } static void _rtl92d_phy_save_mac_registers(struct ieee80211_hw *hw, u32 *macreg, u32 *macbackup) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Save MAC parameters.\n"); for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]); macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]); } static void _rtl92d_phy_reload_adda_registers(struct ieee80211_hw *hw, u32 *adda_reg, u32 *adda_backup, u32 regnum) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Reload ADDA power saving parameters !\n"); for (i = 0; i < regnum; i++) rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, adda_backup[i]); } static void _rtl92d_phy_reload_mac_registers(struct ieee80211_hw *hw, u32 *macreg, u32 *macbackup) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Reload MAC parameters !\n"); for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]); rtl_write_byte(rtlpriv, macreg[i], macbackup[i]); } static void _rtl92d_phy_path_adda_on(struct ieee80211_hw *hw, u32 *adda_reg, bool patha_on, bool is2t) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 pathon; u32 i; RTPRINT(rtlpriv, FINIT, INIT_IQK, "ADDA ON.\n"); pathon = patha_on ? 0x04db25a4 : 0x0b1b25a4; if (patha_on) pathon = rtlpriv->rtlhal.interfaceindex == 0 ? 0x04db25a4 : 0x0b1b25a4; for (i = 0; i < IQK_ADDA_REG_NUM; i++) rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, pathon); } static void _rtl92d_phy_mac_setting_calibration(struct ieee80211_hw *hw, u32 *macreg, u32 *macbackup) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; RTPRINT(rtlpriv, FINIT, INIT_IQK, "MAC settings for Calibration.\n"); rtl_write_byte(rtlpriv, macreg[0], 0x3F); for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++) rtl_write_byte(rtlpriv, macreg[i], (u8)(macbackup[i] & (~BIT(3)))); rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5)))); } static void _rtl92d_phy_patha_standby(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A standby mode!\n"); rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x0); rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD, 0x00010000); rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000); } static void _rtl92d_phy_pimode_switch(struct ieee80211_hw *hw, bool pi_mode) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 mode; RTPRINT(rtlpriv, FINIT, INIT_IQK, "BB Switch to %s mode!\n", pi_mode ? "PI" : "SI"); mode = pi_mode ? 0x01000100 : 0x01000000; rtl_set_bbreg(hw, 0x820, BMASKDWORD, mode); rtl_set_bbreg(hw, 0x828, BMASKDWORD, mode); } static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], u8 t, bool is2t) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 i; u8 patha_ok, pathb_ok; static u32 adda_reg[IQK_ADDA_REG_NUM] = { RFPGA0_XCD_SWITCHCONTROL, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec }; static u32 iqk_mac_reg[IQK_MAC_REG_NUM] = { 0x522, 0x550, 0x551, 0x040 }; static u32 iqk_bb_reg[IQK_BB_REG_NUM] = { RFPGA0_XAB_RFINTERFACESW, RFPGA0_XA_RFINTERFACEOE, RFPGA0_XB_RFINTERFACEOE, ROFDM0_TRMUXPAR, RFPGA0_XCD_RFINTERFACESW, ROFDM0_TRXPATHENABLE, RFPGA0_RFMOD, RFPGA0_ANALOGPARAMETER4, ROFDM0_XAAGCCORE1, ROFDM0_XBAGCCORE1 }; const u32 retrycount = 2; u32 bbvalue; RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK for 2.4G :Start!!!\n"); if (t == 0) { bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "==>0x%08x\n", bbvalue); RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQ Calibration for %s\n", is2t ? "2T2R" : "1T1R"); /* Save ADDA parameters, turn Path A ADDA on */ _rtl92d_phy_save_adda_registers(hw, adda_reg, rtlphy->adda_backup, IQK_ADDA_REG_NUM); _rtl92d_phy_save_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); _rtl92d_phy_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, IQK_BB_REG_NUM); } _rtl92d_phy_path_adda_on(hw, adda_reg, true, is2t); if (t == 0) rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, BIT(8)); /* Switch BB to PI mode to do IQ Calibration. */ if (!rtlphy->rfpi_enable) _rtl92d_phy_pimode_switch(hw, true); rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00); rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600); rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4); rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22204000); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f); if (is2t) { rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD, 0x00010000); rtl_set_bbreg(hw, RFPGA0_XB_LSSIPARAMETER, BMASKDWORD, 0x00010000); } /* MAC settings */ _rtl92d_phy_mac_setting_calibration(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); /* Page B init */ rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000); if (is2t) rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000); /* IQ calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK setting!\n"); rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000); rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x01007c00); rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800); for (i = 0; i < retrycount; i++) { patha_ok = _rtl92d_phy_patha_iqk(hw, is2t); if (patha_ok == 0x03) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Success!!\n"); result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & 0x3FF0000) >> 16; result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & 0x3FF0000) >> 16; result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) & 0x3FF0000) >> 16; break; } else if (i == (retrycount - 1) && patha_ok == 0x01) { /* Tx IQK OK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Only Tx Success!!\n"); result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & 0x3FF0000) >> 16; result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & 0x3FF0000) >> 16; } } if (0x00 == patha_ok) RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK failed!!\n"); if (is2t) { _rtl92d_phy_patha_standby(hw); /* Turn Path B ADDA on */ _rtl92d_phy_path_adda_on(hw, adda_reg, false, is2t); for (i = 0; i < retrycount; i++) { pathb_ok = _rtl92d_phy_pathb_iqk(hw); if (pathb_ok == 0x03) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK Success!!\n"); result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) & 0x3FF0000) >> 16; result[t][6] = (rtl_get_bbreg(hw, 0xec4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][7] = (rtl_get_bbreg(hw, 0xecc, BMASKDWORD) & 0x3FF0000) >> 16; break; } else if (i == (retrycount - 1) && pathb_ok == 0x01) { /* Tx IQK OK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B Only Tx IQK Success!!\n"); result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) & 0x3FF0000) >> 16; } } if (0x00 == pathb_ok) RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK failed!!\n"); } /* Back to BB mode, load original value */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK:Back to BB mode, load original value!\n"); rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0); if (t != 0) { /* Switch back BB to SI mode after finish IQ Calibration. */ if (!rtlphy->rfpi_enable) _rtl92d_phy_pimode_switch(hw, false); /* Reload ADDA power saving parameters */ _rtl92d_phy_reload_adda_registers(hw, adda_reg, rtlphy->adda_backup, IQK_ADDA_REG_NUM); /* Reload MAC parameters */ _rtl92d_phy_reload_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); if (is2t) _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, IQK_BB_REG_NUM); else _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, IQK_BB_REG_NUM - 1); /* load 0xe30 IQC default value */ rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x01008c00); rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x01008c00); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "<==\n"); } static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw, long result[][8], u8 t) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u8 patha_ok, pathb_ok; static u32 adda_reg[IQK_ADDA_REG_NUM] = { RFPGA0_XCD_SWITCHCONTROL, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec }; static u32 iqk_mac_reg[IQK_MAC_REG_NUM] = { 0x522, 0x550, 0x551, 0x040 }; static u32 iqk_bb_reg[IQK_BB_REG_NUM] = { RFPGA0_XAB_RFINTERFACESW, RFPGA0_XA_RFINTERFACEOE, RFPGA0_XB_RFINTERFACEOE, ROFDM0_TRMUXPAR, RFPGA0_XCD_RFINTERFACESW, ROFDM0_TRXPATHENABLE, RFPGA0_RFMOD, RFPGA0_ANALOGPARAMETER4, ROFDM0_XAAGCCORE1, ROFDM0_XBAGCCORE1 }; u32 bbvalue; bool is2t = IS_92D_SINGLEPHY(rtlhal->version); /* Note: IQ calibration must be performed after loading * PHY_REG.txt , and radio_a, radio_b.txt */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK for 5G NORMAL:Start!!!\n"); mdelay(IQK_DELAY_TIME * 20); if (t == 0) { bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "==>0x%08x\n", bbvalue); RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQ Calibration for %s\n", is2t ? "2T2R" : "1T1R"); /* Save ADDA parameters, turn Path A ADDA on */ _rtl92d_phy_save_adda_registers(hw, adda_reg, rtlphy->adda_backup, IQK_ADDA_REG_NUM); _rtl92d_phy_save_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); if (is2t) _rtl92d_phy_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, IQK_BB_REG_NUM); else _rtl92d_phy_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, IQK_BB_REG_NUM - 1); } _rtl92d_phy_path_adda_on(hw, adda_reg, true, is2t); /* MAC settings */ _rtl92d_phy_mac_setting_calibration(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); if (t == 0) rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, BIT(8)); /* Switch BB to PI mode to do IQ Calibration. */ if (!rtlphy->rfpi_enable) _rtl92d_phy_pimode_switch(hw, true); rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00); rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600); rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4); rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22208000); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f); /* Page B init */ rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000); if (is2t) rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000); /* IQ calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK setting!\n"); rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000); rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x10007c00); rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800); patha_ok = _rtl92d_phy_patha_iqk_5g_normal(hw, is2t); if (patha_ok == 0x03) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Success!!\n"); result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & 0x3FF0000) >> 16; result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & 0x3FF0000) >> 16; result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) & 0x3FF0000) >> 16; } else if (patha_ok == 0x01) { /* Tx IQK OK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Only Tx Success!!\n"); result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & 0x3FF0000) >> 16; result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & 0x3FF0000) >> 16; } else { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Fail!!\n"); } if (is2t) { /* _rtl92d_phy_patha_standby(hw); */ /* Turn Path B ADDA on */ _rtl92d_phy_path_adda_on(hw, adda_reg, false, is2t); pathb_ok = _rtl92d_phy_pathb_iqk_5g_normal(hw); if (pathb_ok == 0x03) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK Success!!\n"); result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) & 0x3FF0000) >> 16; result[t][6] = (rtl_get_bbreg(hw, 0xec4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][7] = (rtl_get_bbreg(hw, 0xecc, BMASKDWORD) & 0x3FF0000) >> 16; } else if (pathb_ok == 0x01) { /* Tx IQK OK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B Only Tx IQK Success!!\n"); result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) & 0x3FF0000) >> 16; result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) & 0x3FF0000) >> 16; } else { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK failed!!\n"); } } /* Back to BB mode, load original value */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK:Back to BB mode, load original value!\n"); rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0); if (t != 0) { if (is2t) _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, IQK_BB_REG_NUM); else _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, IQK_BB_REG_NUM - 1); /* Reload MAC parameters */ _rtl92d_phy_reload_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); /* Switch back BB to SI mode after finish IQ Calibration. */ if (!rtlphy->rfpi_enable) _rtl92d_phy_pimode_switch(hw, false); /* Reload ADDA power saving parameters */ _rtl92d_phy_reload_adda_registers(hw, adda_reg, rtlphy->adda_backup, IQK_ADDA_REG_NUM); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "<==\n"); } static bool _rtl92d_phy_simularity_compare(struct ieee80211_hw *hw, long result[][8], u8 c1, u8 c2) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u32 i, j, diff, sim_bitmap, bound; u8 final_candidate[2] = {0xFF, 0xFF}; /* for path A and path B */ bool bresult = true; bool is2t = IS_92D_SINGLEPHY(rtlhal->version); if (is2t) bound = 8; else bound = 4; sim_bitmap = 0; for (i = 0; i < bound; i++) { diff = (result[c1][i] > result[c2][i]) ? (result[c1][i] - result[c2][i]) : (result[c2][i] - result[c1][i]); if (diff > MAX_TOLERANCE_92D) { if ((i == 2 || i == 6) && !sim_bitmap) { if (result[c1][i] + result[c1][i + 1] == 0) final_candidate[(i / 4)] = c2; else if (result[c2][i] + result[c2][i + 1] == 0) final_candidate[(i / 4)] = c1; else sim_bitmap = sim_bitmap | (1 << i); } else { sim_bitmap = sim_bitmap | (1 << i); } } } if (sim_bitmap == 0) { for (i = 0; i < (bound / 4); i++) { if (final_candidate[i] != 0xFF) { for (j = i * 4; j < (i + 1) * 4 - 2; j++) result[3][j] = result[final_candidate[i]][j]; bresult = false; } } return bresult; } if (!(sim_bitmap & 0x0F)) { /* path A OK */ for (i = 0; i < 4; i++) result[3][i] = result[c1][i]; } else if (!(sim_bitmap & 0x03)) { /* path A, Tx OK */ for (i = 0; i < 2; i++) result[3][i] = result[c1][i]; } if (!(sim_bitmap & 0xF0) && is2t) { /* path B OK */ for (i = 4; i < 8; i++) result[3][i] = result[c1][i]; } else if (!(sim_bitmap & 0x30)) { /* path B, Tx OK */ for (i = 4; i < 6; i++) result[3][i] = result[c1][i]; } return false; } static void _rtl92d_phy_patha_fill_iqk_matrix(struct ieee80211_hw *hw, bool iqk_ok, long result[][8], u8 final_candidate, bool txonly) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u32 oldval_0, val_x, tx0_a, reg; long val_y, tx0_c; bool is2t = IS_92D_SINGLEPHY(rtlhal->version) || rtlhal->macphymode == DUALMAC_DUALPHY; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQ Calibration %s !\n", iqk_ok ? "Success" : "Failed"); if (final_candidate == 0xFF) { return; } else if (iqk_ok) { oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD) >> 22) & 0x3FF; /* OFDM0_D */ val_x = result[final_candidate][0]; if ((val_x & 0x00000200) != 0) val_x = val_x | 0xFFFFFC00; tx0_a = (val_x * oldval_0) >> 8; RTPRINT(rtlpriv, FINIT, INIT_IQK, "X = 0x%x, tx0_a = 0x%x, oldval_0 0x%x\n", val_x, tx0_a, oldval_0); rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, 0x3FF, tx0_a); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24), ((val_x * oldval_0 >> 7) & 0x1)); val_y = result[final_candidate][1]; if ((val_y & 0x00000200) != 0) val_y = val_y | 0xFFFFFC00; /* path B IQK result + 3 */ if (rtlhal->interfaceindex == 1 && rtlhal->current_bandtype == BAND_ON_5G) val_y += 3; tx0_c = (val_y * oldval_0) >> 8; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Y = 0x%lx, tx0_c = 0x%lx\n", val_y, tx0_c); rtl_set_bbreg(hw, ROFDM0_XCTxAFE, 0xF0000000, ((tx0_c & 0x3C0) >> 6)); rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, 0x003F0000, (tx0_c & 0x3F)); if (is2t) rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(26), ((val_y * oldval_0 >> 7) & 0x1)); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xC80 = 0x%x\n", rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD)); if (txonly) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "only Tx OK\n"); return; } reg = result[final_candidate][2]; rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg); reg = result[final_candidate][3] & 0x3F; rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg); reg = (result[final_candidate][3] >> 6) & 0xF; rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg); } } static void _rtl92d_phy_pathb_fill_iqk_matrix(struct ieee80211_hw *hw, bool iqk_ok, long result[][8], u8 final_candidate, bool txonly) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u32 oldval_1, val_x, tx1_a, reg; long val_y, tx1_c; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQ Calibration %s !\n", iqk_ok ? "Success" : "Failed"); if (final_candidate == 0xFF) { return; } else if (iqk_ok) { oldval_1 = (rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD) >> 22) & 0x3FF; val_x = result[final_candidate][4]; if ((val_x & 0x00000200) != 0) val_x = val_x | 0xFFFFFC00; tx1_a = (val_x * oldval_1) >> 8; RTPRINT(rtlpriv, FINIT, INIT_IQK, "X = 0x%x, tx1_a = 0x%x\n", val_x, tx1_a); rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, 0x3FF, tx1_a); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(28), ((val_x * oldval_1 >> 7) & 0x1)); val_y = result[final_candidate][5]; if ((val_y & 0x00000200) != 0) val_y = val_y | 0xFFFFFC00; if (rtlhal->current_bandtype == BAND_ON_5G) val_y += 3; tx1_c = (val_y * oldval_1) >> 8; RTPRINT(rtlpriv, FINIT, INIT_IQK, "Y = 0x%lx, tx1_c = 0x%lx\n", val_y, tx1_c); rtl_set_bbreg(hw, ROFDM0_XDTxAFE, 0xF0000000, ((tx1_c & 0x3C0) >> 6)); rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, 0x003F0000, (tx1_c & 0x3F)); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(30), ((val_y * oldval_1 >> 7) & 0x1)); if (txonly) return; reg = result[final_candidate][6]; rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, 0x3FF, reg); reg = result[final_candidate][7] & 0x3F; rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, 0xFC00, reg); reg = (result[final_candidate][7] >> 6) & 0xF; rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, 0x0000F000, reg); } } void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); long result[4][8]; u8 i, final_candidate, indexforchannel; bool patha_ok, pathb_ok; long rege94, rege9c, regea4, regeac, regeb4; long regebc, regec4, regecc, regtmp = 0; bool is12simular, is13simular, is23simular; unsigned long flag = 0; RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK:Start!!!channel %d\n", rtlphy->current_channel); for (i = 0; i < 8; i++) { result[0][i] = 0; result[1][i] = 0; result[2][i] = 0; result[3][i] = 0; } final_candidate = 0xff; patha_ok = false; pathb_ok = false; is12simular = false; is23simular = false; is13simular = false; RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK !!!currentband %d\n", rtlhal->current_bandtype); rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); for (i = 0; i < 3; i++) { if (rtlhal->current_bandtype == BAND_ON_5G) { _rtl92d_phy_iq_calibrate_5g_normal(hw, result, i); } else if (rtlhal->current_bandtype == BAND_ON_2_4G) { if (IS_92D_SINGLEPHY(rtlhal->version)) _rtl92d_phy_iq_calibrate(hw, result, i, true); else _rtl92d_phy_iq_calibrate(hw, result, i, false); } if (i == 1) { is12simular = _rtl92d_phy_simularity_compare(hw, result, 0, 1); if (is12simular) { final_candidate = 0; break; } } if (i == 2) { is13simular = _rtl92d_phy_simularity_compare(hw, result, 0, 2); if (is13simular) { final_candidate = 0; break; } is23simular = _rtl92d_phy_simularity_compare(hw, result, 1, 2); if (is23simular) { final_candidate = 1; } else { for (i = 0; i < 8; i++) regtmp += result[3][i]; if (regtmp != 0) final_candidate = 3; else final_candidate = 0xFF; } } } rtl92d_release_cckandrw_pagea_ctl(hw, &flag); for (i = 0; i < 4; i++) { rege94 = result[i][0]; rege9c = result[i][1]; regea4 = result[i][2]; regeac = result[i][3]; regeb4 = result[i][4]; regebc = result[i][5]; regec4 = result[i][6]; regecc = result[i][7]; RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK: rege94=%lx rege9c=%lx regea4=%lx regeac=%lx regeb4=%lx regebc=%lx regec4=%lx regecc=%lx\n", rege94, rege9c, regea4, regeac, regeb4, regebc, regec4, regecc); } if (final_candidate != 0xff) { rtlphy->reg_e94 = rege94 = result[final_candidate][0]; rtlphy->reg_e9c = rege9c = result[final_candidate][1]; regea4 = result[final_candidate][2]; regeac = result[final_candidate][3]; rtlphy->reg_eb4 = regeb4 = result[final_candidate][4]; rtlphy->reg_ebc = regebc = result[final_candidate][5]; regec4 = result[final_candidate][6]; regecc = result[final_candidate][7]; RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK: final_candidate is %x\n", final_candidate); RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK: rege94=%lx rege9c=%lx regea4=%lx regeac=%lx regeb4=%lx regebc=%lx regec4=%lx regecc=%lx\n", rege94, rege9c, regea4, regeac, regeb4, regebc, regec4, regecc); patha_ok = pathb_ok = true; } else { rtlphy->reg_e94 = rtlphy->reg_eb4 = 0x100; /* X default value */ rtlphy->reg_e9c = rtlphy->reg_ebc = 0x0; /* Y default value */ } if ((rege94 != 0) /*&&(regea4 != 0) */) _rtl92d_phy_patha_fill_iqk_matrix(hw, patha_ok, result, final_candidate, (regea4 == 0)); if (IS_92D_SINGLEPHY(rtlhal->version)) { if ((regeb4 != 0) /*&&(regec4 != 0) */) _rtl92d_phy_pathb_fill_iqk_matrix(hw, pathb_ok, result, final_candidate, (regec4 == 0)); } if (final_candidate != 0xFF) { indexforchannel = rtl92d_get_rightchnlplace_for_iqk( rtlphy->current_channel); for (i = 0; i < IQK_MATRIX_REG_NUM; i++) rtlphy->iqk_matrix_regsetting[indexforchannel]. value[0][i] = result[final_candidate][i]; rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done = true; RT_TRACE(rtlpriv, COMP_SCAN | COMP_MLME, DBG_LOUD, "IQK OK indexforchannel %d\n", indexforchannel); } } void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u8 indexforchannel; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "channel %d\n", channel); /*------Do IQK for normal chip and test chip 5G band------- */ indexforchannel = rtl92d_get_rightchnlplace_for_iqk(channel); RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "indexforchannel %d done %d\n", indexforchannel, rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done); if (0 && !rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done && rtlphy->need_iqk) { /* Re Do IQK. */ RT_TRACE(rtlpriv, COMP_SCAN | COMP_INIT, DBG_LOUD, "Do IQK Matrix reg for channel:%d....\n", channel); rtl92d_phy_iq_calibrate(hw); } else { /* Just load the value. */ /* 2G band just load once. */ if (((!rtlhal->load_imrandiqk_setting_for2g) && indexforchannel == 0) || indexforchannel > 0) { RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, "Just Read IQK Matrix reg for channel:%d....\n", channel); if ((rtlphy->iqk_matrix_regsetting[indexforchannel]. value[0] != NULL) /*&&(regea4 != 0) */) _rtl92d_phy_patha_fill_iqk_matrix(hw, true, rtlphy->iqk_matrix_regsetting[ indexforchannel].value, 0, (rtlphy->iqk_matrix_regsetting[ indexforchannel].value[0][2] == 0)); if (IS_92D_SINGLEPHY(rtlhal->version)) { if ((rtlphy->iqk_matrix_regsetting[ indexforchannel].value[0][4] != 0) /*&&(regec4 != 0) */) _rtl92d_phy_pathb_fill_iqk_matrix(hw, true, rtlphy->iqk_matrix_regsetting[ indexforchannel].value, 0, (rtlphy->iqk_matrix_regsetting[ indexforchannel].value[0][6] == 0)); } } } rtlphy->need_iqk = false; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "<====\n"); } static u32 _rtl92d_phy_get_abs(u32 val1, u32 val2) { u32 ret; if (val1 >= val2) ret = val1 - val2; else ret = val2 - val1; return ret; } static bool _rtl92d_is_legal_5g_channel(struct ieee80211_hw *hw, u8 channel) { int i; u8 channel_5g[45] = { 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 149, 151, 153, 155, 157, 159, 161, 163, 165 }; for (i = 0; i < sizeof(channel_5g); i++) if (channel == channel_5g[i]) return true; return false; } static void _rtl92d_phy_calc_curvindex(struct ieee80211_hw *hw, u32 *targetchnl, u32 * curvecount_val, bool is5g, u32 *curveindex) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 smallest_abs_val = 0xffffffff, u4tmp; u8 i, j; u8 chnl_num = is5g ? TARGET_CHNL_NUM_5G : TARGET_CHNL_NUM_2G; for (i = 0; i < chnl_num; i++) { if (is5g && !_rtl92d_is_legal_5g_channel(hw, i + 1)) continue; curveindex[i] = 0; for (j = 0; j < (CV_CURVE_CNT * 2); j++) { u4tmp = _rtl92d_phy_get_abs(targetchnl[i], curvecount_val[j]); if (u4tmp < smallest_abs_val) { curveindex[i] = j; smallest_abs_val = u4tmp; } } smallest_abs_val = 0xffffffff; RTPRINT(rtlpriv, FINIT, INIT_IQK, "curveindex[%d] = %x\n", i, curveindex[i]); } } static void _rtl92d_phy_reload_lck_setting(struct ieee80211_hw *hw, u8 channel) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 erfpath = rtlpriv->rtlhal.current_bandtype == BAND_ON_5G ? RF90_PATH_A : IS_92D_SINGLEPHY(rtlpriv->rtlhal.version) ? RF90_PATH_B : RF90_PATH_A; u32 u4tmp = 0, u4regvalue = 0; bool bneed_powerdown_radio = false; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "path %d\n", erfpath); RTPRINT(rtlpriv, FINIT, INIT_IQK, "band type = %d\n", rtlpriv->rtlhal.current_bandtype); RTPRINT(rtlpriv, FINIT, INIT_IQK, "channel = %d\n", channel); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G) {/* Path-A for 5G */ u4tmp = curveindex_5g[channel-1]; RTPRINT(rtlpriv, FINIT, INIT_IQK, "ver 1 set RF-A, 5G, 0x28 = 0x%ulx !!\n", u4tmp); if (rtlpriv->rtlhal.macphymode == DUALMAC_DUALPHY && rtlpriv->rtlhal.interfaceindex == 1) { bneed_powerdown_radio = rtl92d_phy_enable_anotherphy(hw, false); rtlpriv->rtlhal.during_mac1init_radioa = true; /* asume no this case */ if (bneed_powerdown_radio) _rtl92d_phy_enable_rf_env(hw, erfpath, &u4regvalue); } rtl_set_rfreg(hw, erfpath, RF_SYN_G4, 0x3f800, u4tmp); if (bneed_powerdown_radio) _rtl92d_phy_restore_rf_env(hw, erfpath, &u4regvalue); if (rtlpriv->rtlhal.during_mac1init_radioa) rtl92d_phy_powerdown_anotherphy(hw, false); } else if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) { u4tmp = curveindex_2g[channel-1]; RTPRINT(rtlpriv, FINIT, INIT_IQK, "ver 3 set RF-B, 2G, 0x28 = 0x%ulx !!\n", u4tmp); if (rtlpriv->rtlhal.macphymode == DUALMAC_DUALPHY && rtlpriv->rtlhal.interfaceindex == 0) { bneed_powerdown_radio = rtl92d_phy_enable_anotherphy(hw, true); rtlpriv->rtlhal.during_mac0init_radiob = true; if (bneed_powerdown_radio) _rtl92d_phy_enable_rf_env(hw, erfpath, &u4regvalue); } rtl_set_rfreg(hw, erfpath, RF_SYN_G4, 0x3f800, u4tmp); RTPRINT(rtlpriv, FINIT, INIT_IQK, "ver 3 set RF-B, 2G, 0x28 = 0x%ulx !!\n", rtl_get_rfreg(hw, erfpath, RF_SYN_G4, 0x3f800)); if (bneed_powerdown_radio) _rtl92d_phy_restore_rf_env(hw, erfpath, &u4regvalue); if (rtlpriv->rtlhal.during_mac0init_radiob) rtl92d_phy_powerdown_anotherphy(hw, true); } RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "<====\n"); } static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 tmpreg, index, rf_mode[2]; u8 path = is2t ? 2 : 1; u8 i; u32 u4tmp, offset; u32 curvecount_val[CV_CURVE_CNT * 2] = {0}; u16 timeout = 800, timecount = 0; /* Check continuous TX and Packet TX */ tmpreg = rtl_read_byte(rtlpriv, 0xd03); /* if Deal with contisuous TX case, disable all continuous TX */ /* if Deal with Packet TX case, block all queues */ if ((tmpreg & 0x70) != 0) rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F); else rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xF00000, 0x0F); for (index = 0; index < path; index++) { /* 1. Read original RF mode */ offset = index == 0 ? ROFDM0_XAAGCCORE1 : ROFDM0_XBAGCCORE1; rf_mode[index] = rtl_read_byte(rtlpriv, offset); /* 2. Set RF mode = standby mode */ rtl_set_rfreg(hw, (enum radio_path)index, RF_AC, BRFREGOFFSETMASK, 0x010000); if (rtlpci->init_ready) { /* switch CV-curve control by LC-calibration */ rtl_set_rfreg(hw, (enum radio_path)index, RF_SYN_G7, BIT(17), 0x0); /* 4. Set LC calibration begin */ rtl_set_rfreg(hw, (enum radio_path)index, RF_CHNLBW, 0x08000, 0x01); } u4tmp = rtl_get_rfreg(hw, (enum radio_path)index, RF_SYN_G6, BRFREGOFFSETMASK); while ((!(u4tmp & BIT(11))) && timecount <= timeout) { mdelay(50); timecount += 50; u4tmp = rtl_get_rfreg(hw, (enum radio_path)index, RF_SYN_G6, BRFREGOFFSETMASK); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "PHY_LCK finish delay for %d ms=2\n", timecount); u4tmp = rtl_get_rfreg(hw, index, RF_SYN_G4, BRFREGOFFSETMASK); if (index == 0 && rtlhal->interfaceindex == 0) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "path-A / 5G LCK\n"); } else { RTPRINT(rtlpriv, FINIT, INIT_IQK, "path-B / 2.4G LCK\n"); } memset(&curvecount_val[0], 0, CV_CURVE_CNT * 2); /* Set LC calibration off */ rtl_set_rfreg(hw, (enum radio_path)index, RF_CHNLBW, 0x08000, 0x0); RTPRINT(rtlpriv, FINIT, INIT_IQK, "set RF 0x18[15] = 0\n"); /* save Curve-counting number */ for (i = 0; i < CV_CURVE_CNT; i++) { u32 readval = 0, readval2 = 0; rtl_set_rfreg(hw, (enum radio_path)index, 0x3F, 0x7f, i); rtl_set_rfreg(hw, (enum radio_path)index, 0x4D, BRFREGOFFSETMASK, 0x0); readval = rtl_get_rfreg(hw, (enum radio_path)index, 0x4F, BRFREGOFFSETMASK); curvecount_val[2 * i + 1] = (readval & 0xfffe0) >> 5; /* reg 0x4f [4:0] */ /* reg 0x50 [19:10] */ readval2 = rtl_get_rfreg(hw, (enum radio_path)index, 0x50, 0xffc00); curvecount_val[2 * i] = (((readval & 0x1F) << 10) | readval2); } if (index == 0 && rtlhal->interfaceindex == 0) _rtl92d_phy_calc_curvindex(hw, targetchnl_5g, curvecount_val, true, curveindex_5g); else _rtl92d_phy_calc_curvindex(hw, targetchnl_2g, curvecount_val, false, curveindex_2g); /* switch CV-curve control mode */ rtl_set_rfreg(hw, (enum radio_path)index, RF_SYN_G7, BIT(17), 0x1); } /* Restore original situation */ for (index = 0; index < path; index++) { offset = index == 0 ? ROFDM0_XAAGCCORE1 : ROFDM0_XBAGCCORE1; rtl_write_byte(rtlpriv, offset, 0x50); rtl_write_byte(rtlpriv, offset, rf_mode[index]); } if ((tmpreg & 0x70) != 0) rtl_write_byte(rtlpriv, 0xd03, tmpreg); else /*Deal with Packet TX case */ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xF00000, 0x00); _rtl92d_phy_reload_lck_setting(hw, rtlpriv->phy.current_channel); } static void _rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) { struct rtl_priv *rtlpriv = rtl_priv(hw); RTPRINT(rtlpriv, FINIT, INIT_IQK, "cosa PHY_LCK ver=2\n"); _rtl92d_phy_lc_calibrate_sw(hw, is2t); } void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); u32 timeout = 2000, timecount = 0; while (rtlpriv->mac80211.act_scanning && timecount < timeout) { udelay(50); timecount += 50; } rtlphy->lck_inprogress = true; RTPRINT(rtlpriv, FINIT, INIT_IQK, "LCK:Start!!! currentband %x delay %d ms\n", rtlhal->current_bandtype, timecount); if (IS_92D_SINGLEPHY(rtlhal->version)) { _rtl92d_phy_lc_calibrate(hw, true); } else { /* For 1T1R */ _rtl92d_phy_lc_calibrate(hw, false); } rtlphy->lck_inprogress = false; RTPRINT(rtlpriv, FINIT, INIT_IQK, "LCK:Finish!!!\n"); } void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, char delta) { return; } static bool _rtl92d_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, u32 cmdtableidx, u32 cmdtablesz, enum swchnlcmd_id cmdid, u32 para1, u32 para2, u32 msdelay) { struct swchnlcmd *pcmd; if (cmdtable == NULL) { RT_ASSERT(false, "cmdtable cannot be NULL\n"); return false; } if (cmdtableidx >= cmdtablesz) return false; pcmd = cmdtable + cmdtableidx; pcmd->cmdid = cmdid; pcmd->para1 = para1; pcmd->para2 = para2; pcmd->msdelay = msdelay; return true; } void rtl92d_phy_reset_iqk_result(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 i; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "settings regs %d default regs %d\n", (int)(sizeof(rtlphy->iqk_matrix_regsetting) / sizeof(struct iqk_matrix_regs)), IQK_MATRIX_REG_NUM); /* 0xe94, 0xe9c, 0xea4, 0xeac, 0xeb4, 0xebc, 0xec4, 0xecc */ for (i = 0; i < IQK_MATRIX_SETTINGS_NUM; i++) { rtlphy->iqk_matrix_regsetting[i].value[0][0] = 0x100; rtlphy->iqk_matrix_regsetting[i].value[0][2] = 0x100; rtlphy->iqk_matrix_regsetting[i].value[0][4] = 0x100; rtlphy->iqk_matrix_regsetting[i].value[0][6] = 0x100; rtlphy->iqk_matrix_regsetting[i].value[0][1] = 0x0; rtlphy->iqk_matrix_regsetting[i].value[0][3] = 0x0; rtlphy->iqk_matrix_regsetting[i].value[0][5] = 0x0; rtlphy->iqk_matrix_regsetting[i].value[0][7] = 0x0; rtlphy->iqk_matrix_regsetting[i].iqk_done = false; } } static bool _rtl92d_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, u8 *stage, u8 *step, u32 *delay) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct swchnlcmd precommoncmd[MAX_PRECMD_CNT]; u32 precommoncmdcnt; struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT]; u32 postcommoncmdcnt; struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT]; u32 rfdependcmdcnt; struct swchnlcmd *currentcmd = NULL; u8 rfpath; u8 num_total_rfpath = rtlphy->num_total_rfpath; precommoncmdcnt = 0; _rtl92d_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL, 0, 0, 0); _rtl92d_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, MAX_PRECMD_CNT, CMDID_END, 0, 0, 0); postcommoncmdcnt = 0; _rtl92d_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++, MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0); rfdependcmdcnt = 0; _rtl92d_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG, RF_CHNLBW, channel, 0); _rtl92d_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0); do { switch (*stage) { case 0: currentcmd = &precommoncmd[*step]; break; case 1: currentcmd = &rfdependcmd[*step]; break; case 2: currentcmd = &postcommoncmd[*step]; break; } if (currentcmd->cmdid == CMDID_END) { if ((*stage) == 2) { return true; } else { (*stage)++; (*step) = 0; continue; } } switch (currentcmd->cmdid) { case CMDID_SET_TXPOWEROWER_LEVEL: rtl92d_phy_set_txpower_level(hw, channel); break; case CMDID_WRITEPORT_ULONG: rtl_write_dword(rtlpriv, currentcmd->para1, currentcmd->para2); break; case CMDID_WRITEPORT_USHORT: rtl_write_word(rtlpriv, currentcmd->para1, (u16)currentcmd->para2); break; case CMDID_WRITEPORT_UCHAR: rtl_write_byte(rtlpriv, currentcmd->para1, (u8)currentcmd->para2); break; case CMDID_RF_WRITEREG: for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) { rtlphy->rfreg_chnlval[rfpath] = ((rtlphy->rfreg_chnlval[rfpath] & 0xffffff00) | currentcmd->para2); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G) { if (currentcmd->para2 > 99) rtlphy->rfreg_chnlval[rfpath] = rtlphy->rfreg_chnlval [rfpath] | (BIT(18)); else rtlphy->rfreg_chnlval[rfpath] = rtlphy->rfreg_chnlval [rfpath] & (~BIT(18)); rtlphy->rfreg_chnlval[rfpath] |= (BIT(16) | BIT(8)); } else { rtlphy->rfreg_chnlval[rfpath] &= ~(BIT(8) | BIT(16) | BIT(18)); } rtl_set_rfreg(hw, (enum radio_path)rfpath, currentcmd->para1, BRFREGOFFSETMASK, rtlphy->rfreg_chnlval[rfpath]); _rtl92d_phy_reload_imr_setting(hw, channel, rfpath); } _rtl92d_phy_switch_rf_setting(hw, channel); /* do IQK when all parameters are ready */ rtl92d_phy_reload_iqk_setting(hw, channel); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } break; } while (true); (*delay) = currentcmd->msdelay; (*step)++; return false; } u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u32 delay; u32 timeout = 1000, timecount = 0; u8 channel = rtlphy->current_channel; u32 ret_value; if (rtlphy->sw_chnl_inprogress) return 0; if (rtlphy->set_bwmode_inprogress) return 0; if ((is_hal_stop(rtlhal)) || (RT_CANNOT_IO(hw))) { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, "sw_chnl_inprogress false driver sleep or unload\n"); return 0; } while (rtlphy->lck_inprogress && timecount < timeout) { mdelay(50); timecount += 50; } if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY && rtlhal->bandset == BAND_ON_BOTH) { ret_value = rtl_get_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BMASKDWORD); if (rtlphy->current_channel > 14 && !(ret_value & BIT(0))) rtl92d_phy_switch_wirelessband(hw, BAND_ON_5G); else if (rtlphy->current_channel <= 14 && (ret_value & BIT(0))) rtl92d_phy_switch_wirelessband(hw, BAND_ON_2_4G); } switch (rtlhal->current_bandtype) { case BAND_ON_5G: /* Get first channel error when change between * 5G and 2.4G band. */ if (channel <= 14) return 0; RT_ASSERT((channel > 14), "5G but channel<=14\n"); break; case BAND_ON_2_4G: /* Get first channel error when change between * 5G and 2.4G band. */ if (channel > 14) return 0; RT_ASSERT((channel <= 14), "2G but channel>14\n"); break; default: RT_ASSERT(false, "Invalid WirelessMode(%#x)!!\n", rtlpriv->mac80211.mode); break; } rtlphy->sw_chnl_inprogress = true; if (channel == 0) channel = 1; rtlphy->sw_chnl_stage = 0; rtlphy->sw_chnl_step = 0; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "switch to channel%d\n", rtlphy->current_channel); do { if (!rtlphy->sw_chnl_inprogress) break; if (!_rtl92d_phy_sw_chnl_step_by_step(hw, rtlphy->current_channel, &rtlphy->sw_chnl_stage, &rtlphy->sw_chnl_step, &delay)) { if (delay > 0) mdelay(delay); else continue; } else { rtlphy->sw_chnl_inprogress = false; } break; } while (true); RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); rtlphy->sw_chnl_inprogress = false; return 1; } static void rtl92d_phy_set_io(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *de_digtable = &rtlpriv->dm_digtable; struct rtl_phy *rtlphy = &(rtlpriv->phy); RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "--->Cmd(%#x), set_io_inprogress(%d)\n", rtlphy->current_io_type, rtlphy->set_io_inprogress); switch (rtlphy->current_io_type) { case IO_CMD_RESUME_DM_BY_SCAN: de_digtable->cur_igvalue = rtlphy->initgain_backup.xaagccore1; rtl92d_dm_write_dig(hw); rtl92d_phy_set_txpower_level(hw, rtlphy->current_channel); break; case IO_CMD_PAUSE_DM_BY_SCAN: rtlphy->initgain_backup.xaagccore1 = de_digtable->cur_igvalue; de_digtable->cur_igvalue = 0x37; rtl92d_dm_write_dig(hw); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } rtlphy->set_io_inprogress = false; RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "<---(%#x)\n", rtlphy->current_io_type); } bool rtl92d_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); bool postprocessing = false; RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "-->IO Cmd(%#x), set_io_inprogress(%d)\n", iotype, rtlphy->set_io_inprogress); do { switch (iotype) { case IO_CMD_RESUME_DM_BY_SCAN: RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "[IO CMD] Resume DM after scan\n"); postprocessing = true; break; case IO_CMD_PAUSE_DM_BY_SCAN: RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "[IO CMD] Pause DM before scan\n"); postprocessing = true; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } while (false); if (postprocessing && !rtlphy->set_io_inprogress) { rtlphy->set_io_inprogress = true; rtlphy->current_io_type = iotype; } else { return false; } rtl92d_phy_set_io(hw); RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "<--IO Type(%#x)\n", iotype); return true; } static void _rtl92d_phy_set_rfon(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /* a. SYS_CLKR 0x08[11] = 1 restore MAC clock */ /* b. SPS_CTRL 0x11[7:0] = 0x2b */ if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY) rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b); /* c. For PCIE: SYS_FUNC_EN 0x02[7:0] = 0xE3 enable BB TRX function */ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); /* RF_ON_EXCEP(d~g): */ /* d. APSD_CTRL 0x600[7:0] = 0x00 */ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); /* e. SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB TRX function again */ /* f. SYS_FUNC_EN 0x02[7:0] = 0xE3 enable BB TRX function*/ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); /* g. txpause 0x522[7:0] = 0x00 enable mac tx queue */ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); } static void _rtl92d_phy_set_rfsleep(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 u4btmp; u8 delay = 5; /* a. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue */ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); /* b. RF path 0 offset 0x00 = 0x00 disable RF */ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00); /* c. APSD_CTRL 0x600[7:0] = 0x40 */ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); /* d. APSD_CTRL 0x600[7:0] = 0x00 * APSD_CTRL 0x600[7:0] = 0x00 * RF path 0 offset 0x00 = 0x00 * APSD_CTRL 0x600[7:0] = 0x40 * */ u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK); while (u4btmp != 0 && delay > 0) { rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK); delay--; } if (delay == 0) { /* Jump out the LPS turn off sequence to RF_ON_EXCEP */ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Fail !!! Switch RF timeout\n"); return; } /* e. For PCIE: SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB TRX function */ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); /* f. SPS_CTRL 0x11[7:0] = 0x22 */ if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY) rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22); /* g. SYS_CLKR 0x08[11] = 0 gated MAC clock */ } bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { bool bresult = true; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 i, queue_id; struct rtl8192_tx_ring *ring = NULL; if (rfpwr_state == ppsc->rfpwr_state) return false; switch (rfpwr_state) { case ERFON: if ((ppsc->rfpwr_state == ERFOFF) && RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) { bool rtstatus; u32 InitializeCount = 0; do { InitializeCount++; RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic enable\n"); rtstatus = rtl_ps_enable_nic(hw); } while (!rtstatus && (InitializeCount < 10)); RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "awake, sleeped:%d ms state_inap:%x\n", jiffies_to_msecs(jiffies - ppsc->last_sleep_jiffies), rtlpriv->psc.state_inap); ppsc->last_awake_jiffies = jiffies; _rtl92d_phy_set_rfon(hw); } if (mac->link_state == MAC80211_LINKED) rtlpriv->cfg->ops->led_control(hw, LED_CTL_LINK); else rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); break; case ERFOFF: if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic disable\n"); rtl_ps_disable_nic(hw); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); else rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); } break; case ERFSLEEP: if (ppsc->rfpwr_state == ERFOFF) return false; for (queue_id = 0, i = 0; queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { ring = &pcipriv->dev.tx_ring[queue_id]; if (skb_queue_len(&ring->queue) == 0 || queue_id == BEACON_QUEUE) { queue_id++; continue; } else if (rtlpci->pdev->current_state != PCI_D0) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "eRf Off/Sleep: %d times TcbBusyQueue[%d] !=0 but lower power state!\n", i + 1, queue_id); break; } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "eRf Off/Sleep: %d times TcbBusyQueue[%d] =%d before doze!\n", i + 1, queue_id, skb_queue_len(&ring->queue)); udelay(10); i++; } if (i >= MAX_DOZE_WAITING_TIMES_9x) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "ERFOFF: %d times TcbBusyQueue[%d] = %d !\n", MAX_DOZE_WAITING_TIMES_9x, queue_id, skb_queue_len(&ring->queue)); break; } } RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "Set rfsleep awaked:%d ms\n", jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies)); RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "sleep awaked:%d ms state_inap:%x\n", jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies), rtlpriv->psc.state_inap); ppsc->last_sleep_jiffies = jiffies; _rtl92d_phy_set_rfsleep(hw); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); bresult = false; break; } if (bresult) ppsc->rfpwr_state = rfpwr_state; return bresult; } void rtl92d_phy_config_macphymode(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 offset = REG_MAC_PHY_CTRL_NORMAL; switch (rtlhal->macphymode) { case DUALMAC_DUALPHY: RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "MacPhyMode: DUALMAC_DUALPHY\n"); rtl_write_byte(rtlpriv, offset, 0xF3); break; case SINGLEMAC_SINGLEPHY: RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "MacPhyMode: SINGLEMAC_SINGLEPHY\n"); rtl_write_byte(rtlpriv, offset, 0xF4); break; case DUALMAC_SINGLEPHY: RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "MacPhyMode: DUALMAC_SINGLEPHY\n"); rtl_write_byte(rtlpriv, offset, 0xF1); break; } } void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); switch (rtlhal->macphymode) { case DUALMAC_SINGLEPHY: rtlphy->rf_type = RF_2T2R; rtlhal->version |= RF_TYPE_2T2R; rtlhal->bandset = BAND_ON_BOTH; rtlhal->current_bandtype = BAND_ON_2_4G; break; case SINGLEMAC_SINGLEPHY: rtlphy->rf_type = RF_2T2R; rtlhal->version |= RF_TYPE_2T2R; rtlhal->bandset = BAND_ON_BOTH; rtlhal->current_bandtype = BAND_ON_2_4G; break; case DUALMAC_DUALPHY: rtlphy->rf_type = RF_1T1R; rtlhal->version &= RF_TYPE_1T1R; /* Now we let MAC0 run on 5G band. */ if (rtlhal->interfaceindex == 0) { rtlhal->bandset = BAND_ON_5G; rtlhal->current_bandtype = BAND_ON_5G; } else { rtlhal->bandset = BAND_ON_2_4G; rtlhal->current_bandtype = BAND_ON_2_4G; } break; default: break; } } u8 rtl92d_get_chnlgroup_fromarray(u8 chnl) { u8 group; u8 channel_info[59] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 149, 151, 153, 155, 157, 159, 161, 163, 165 }; if (channel_info[chnl] <= 3) group = 0; else if (channel_info[chnl] <= 9) group = 1; else if (channel_info[chnl] <= 14) group = 2; else if (channel_info[chnl] <= 44) group = 3; else if (channel_info[chnl] <= 54) group = 4; else if (channel_info[chnl] <= 64) group = 5; else if (channel_info[chnl] <= 112) group = 6; else if (channel_info[chnl] <= 126) group = 7; else if (channel_info[chnl] <= 140) group = 8; else if (channel_info[chnl] <= 153) group = 9; else if (channel_info[chnl] <= 159) group = 10; else group = 11; return group; } void rtl92d_phy_set_poweron(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); unsigned long flags; u8 value8; u16 i; u32 mac_reg = (rtlhal->interfaceindex == 0 ? REG_MAC0 : REG_MAC1); /* notice fw know band status 0x81[1]/0x53[1] = 0: 5G, 1: 2G */ if (rtlhal->current_bandtype == BAND_ON_2_4G) { value8 = rtl_read_byte(rtlpriv, mac_reg); value8 |= BIT(1); rtl_write_byte(rtlpriv, mac_reg, value8); } else { value8 = rtl_read_byte(rtlpriv, mac_reg); value8 &= (~BIT(1)); rtl_write_byte(rtlpriv, mac_reg, value8); } if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY) { value8 = rtl_read_byte(rtlpriv, REG_MAC0); rtl_write_byte(rtlpriv, REG_MAC0, value8 | MAC0_ON); } else { spin_lock_irqsave(&globalmutex_power, flags); if (rtlhal->interfaceindex == 0) { value8 = rtl_read_byte(rtlpriv, REG_MAC0); rtl_write_byte(rtlpriv, REG_MAC0, value8 | MAC0_ON); } else { value8 = rtl_read_byte(rtlpriv, REG_MAC1); rtl_write_byte(rtlpriv, REG_MAC1, value8 | MAC1_ON); } value8 = rtl_read_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS); spin_unlock_irqrestore(&globalmutex_power, flags); for (i = 0; i < 200; i++) { if ((value8 & BIT(7)) == 0) { break; } else { udelay(500); spin_lock_irqsave(&globalmutex_power, flags); value8 = rtl_read_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS); spin_unlock_irqrestore(&globalmutex_power, flags); } } if (i == 200) RT_ASSERT(false, "Another mac power off over time\n"); } } void rtl92d_phy_config_maccoexist_rfpage(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); switch (rtlpriv->rtlhal.macphymode) { case DUALMAC_DUALPHY: rtl_write_byte(rtlpriv, REG_DMC, 0x0); rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x08); rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, 0x13ff); break; case DUALMAC_SINGLEPHY: rtl_write_byte(rtlpriv, REG_DMC, 0xf8); rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x08); rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, 0x13ff); break; case SINGLEMAC_SINGLEPHY: rtl_write_byte(rtlpriv, REG_DMC, 0x0); rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x10); rtl_write_word(rtlpriv, (REG_TRXFF_BNDY + 2), 0x27FF); break; default: break; } } void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 rfpath, i; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "==>\n"); /* r_select_5G for path_A/B 0 for 2.4G, 1 for 5G */ if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* r_select_5G for path_A/B,0x878 */ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(0), 0x0); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15), 0x0); if (rtlhal->macphymode != DUALMAC_DUALPHY) { rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(16), 0x0); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(31), 0x0); } /* rssi_table_select:index 0 for 2.4G.1~3 for 5G,0xc78 */ rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, BIT(6) | BIT(7), 0x0); /* fc_area 0xd2c */ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(14) | BIT(13), 0x0); /* 5G LAN ON */ rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0xa); /* TX BB gain shift*1,Just for testchip,0xc80,0xc88 */ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, 0x40000100); rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, 0x40000100); if (rtlhal->macphymode == DUALMAC_DUALPHY) { rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BIT(10) | BIT(6) | BIT(5), ((rtlefuse->eeprom_c9 & BIT(3)) >> 3) | (rtlefuse->eeprom_c9 & BIT(1)) | ((rtlefuse->eeprom_cc & BIT(1)) << 4)); rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(10) | BIT(6) | BIT(5), ((rtlefuse->eeprom_c9 & BIT(2)) >> 2) | ((rtlefuse->eeprom_c9 & BIT(0)) << 1) | ((rtlefuse->eeprom_cc & BIT(0)) << 5)); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15), 0); } else { rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BIT(26) | BIT(22) | BIT(21) | BIT(10) | BIT(6) | BIT(5), ((rtlefuse->eeprom_c9 & BIT(3)) >> 3) | (rtlefuse->eeprom_c9 & BIT(1)) | ((rtlefuse->eeprom_cc & BIT(1)) << 4) | ((rtlefuse->eeprom_c9 & BIT(7)) << 9) | ((rtlefuse->eeprom_c9 & BIT(5)) << 12) | ((rtlefuse->eeprom_cc & BIT(3)) << 18)); rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(10) | BIT(6) | BIT(5), ((rtlefuse->eeprom_c9 & BIT(2)) >> 2) | ((rtlefuse->eeprom_c9 & BIT(0)) << 1) | ((rtlefuse->eeprom_cc & BIT(0)) << 5)); rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BIT(10) | BIT(6) | BIT(5), ((rtlefuse->eeprom_c9 & BIT(6)) >> 6) | ((rtlefuse->eeprom_c9 & BIT(4)) >> 3) | ((rtlefuse->eeprom_cc & BIT(2)) << 3)); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(31) | BIT(15), 0); } /* 1.5V_LDO */ } else { /* r_select_5G for path_A/B */ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(0), 0x1); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15), 0x1); if (rtlhal->macphymode != DUALMAC_DUALPHY) { rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(16), 0x1); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(31), 0x1); } /* rssi_table_select:index 0 for 2.4G.1~3 for 5G */ rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, BIT(6) | BIT(7), 0x1); /* fc_area */ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(14) | BIT(13), 0x1); /* 5G LAN ON */ rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0x0); /* TX BB gain shift,Just for testchip,0xc80,0xc88 */ if (rtlefuse->internal_pa_5g[0]) rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, 0x2d4000b5); else rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, 0x20000080); if (rtlefuse->internal_pa_5g[1]) rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, 0x2d4000b5); else rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, 0x20000080); if (rtlhal->macphymode == DUALMAC_DUALPHY) { rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BIT(10) | BIT(6) | BIT(5), (rtlefuse->eeprom_cc & BIT(5))); rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(10), ((rtlefuse->eeprom_cc & BIT(4)) >> 4)); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(15), (rtlefuse->eeprom_cc & BIT(4)) >> 4); } else { rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BIT(26) | BIT(22) | BIT(21) | BIT(10) | BIT(6) | BIT(5), (rtlefuse->eeprom_cc & BIT(5)) | ((rtlefuse->eeprom_cc & BIT(7)) << 14)); rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BIT(10), ((rtlefuse->eeprom_cc & BIT(4)) >> 4)); rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BIT(10), ((rtlefuse->eeprom_cc & BIT(6)) >> 6)); rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(31) | BIT(15), ((rtlefuse->eeprom_cc & BIT(4)) >> 4) | ((rtlefuse->eeprom_cc & BIT(6)) << 10)); } } /* update IQK related settings */ rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, BMASKDWORD, 0x40000100); rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, BMASKDWORD, 0x40000100); rtl_set_bbreg(hw, ROFDM0_XCTxAFE, 0xF0000000, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(30) | BIT(28) | BIT(26) | BIT(24), 0x00); rtl_set_bbreg(hw, ROFDM0_XDTxAFE, 0xF0000000, 0x00); rtl_set_bbreg(hw, 0xca0, 0xF0000000, 0x00); rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, 0x0000F000, 0x00); /* Update RF */ for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* MOD_AG for RF paht_A 0x18 BIT8,BIT16 */ rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(8) | BIT(16) | BIT(18), 0); /* RF0x0b[16:14] =3b'111 */ rtl_set_rfreg(hw, (enum radio_path)rfpath, 0x0B, 0x1c000, 0x07); } else { /* MOD_AG for RF paht_A 0x18 BIT8,BIT16 */ rtl_set_rfreg(hw, rfpath, RF_CHNLBW, BIT(8) | BIT(16) | BIT(18), (BIT(16) | BIT(8)) >> 8); } } /* Update for all band. */ /* DMDP */ if (rtlphy->rf_type == RF_1T1R) { /* Use antenna 0,0xc04,0xd04 */ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x11); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x1); /* enable ad/da clock1 for dual-phy reg0x888 */ if (rtlhal->interfaceindex == 0) { rtl_set_bbreg(hw, RFPGA0_ADDALLOCKEN, BIT(12) | BIT(13), 0x3); } else { rtl92d_phy_enable_anotherphy(hw, false); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "MAC1 use DBI to update 0x888\n"); /* 0x888 */ rtl92de_write_dword_dbi(hw, RFPGA0_ADDALLOCKEN, rtl92de_read_dword_dbi(hw, RFPGA0_ADDALLOCKEN, BIT(3)) | BIT(12) | BIT(13), BIT(3)); rtl92d_phy_powerdown_anotherphy(hw, false); } } else { /* Single PHY */ /* Use antenna 0 & 1,0xc04,0xd04 */ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x33); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x3); /* disable ad/da clock1,0x888 */ rtl_set_bbreg(hw, RFPGA0_ADDALLOCKEN, BIT(12) | BIT(13), 0); } for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { rtlphy->rfreg_chnlval[rfpath] = rtl_get_rfreg(hw, rfpath, RF_CHNLBW, BRFREGOFFSETMASK); rtlphy->reg_rf3c[rfpath] = rtl_get_rfreg(hw, rfpath, 0x3C, BRFREGOFFSETMASK); } for (i = 0; i < 2; i++) RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "RF 0x18 = 0x%x\n", rtlphy->rfreg_chnlval[i]); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "<==\n"); } bool rtl92d_phy_check_poweroff(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 u1btmp; unsigned long flags; if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY) { u1btmp = rtl_read_byte(rtlpriv, REG_MAC0); rtl_write_byte(rtlpriv, REG_MAC0, u1btmp & (~MAC0_ON)); return true; } spin_lock_irqsave(&globalmutex_power, flags); if (rtlhal->interfaceindex == 0) { u1btmp = rtl_read_byte(rtlpriv, REG_MAC0); rtl_write_byte(rtlpriv, REG_MAC0, u1btmp & (~MAC0_ON)); u1btmp = rtl_read_byte(rtlpriv, REG_MAC1); u1btmp &= MAC1_ON; } else { u1btmp = rtl_read_byte(rtlpriv, REG_MAC1); rtl_write_byte(rtlpriv, REG_MAC1, u1btmp & (~MAC1_ON)); u1btmp = rtl_read_byte(rtlpriv, REG_MAC0); u1btmp &= MAC0_ON; } if (u1btmp) { spin_unlock_irqrestore(&globalmutex_power, flags); return false; } u1btmp = rtl_read_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS); u1btmp |= BIT(7); rtl_write_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS, u1btmp); spin_unlock_irqrestore(&globalmutex_power, flags); return true; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/led.h0000644000175000017500000000275212026211315024522 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CE_LED_H__ #define __RTL92CE_LED_H__ void rtl92de_init_sw_leds(struct ieee80211_hw *hw); void rtl92de_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92de_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/led.c0000644000175000017500000001073312026211315024513 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "reg.h" #include "led.h" static void _rtl92ce_init_led(struct ieee80211_hw *hw, struct rtl_led *pled, enum rtl_led_pin ledpin) { pled->hw = hw; pled->ledpin = ledpin; pled->ledon = false; } void rtl92de_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled) { u8 ledcfg; struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); if ((rtlpriv->efuse.eeprom_did == 0x8176) || (rtlpriv->efuse.eeprom_did == 0x8193)) /* BIT7 of REG_LEDCFG2 should be set to * make sure we could emit the led2. */ rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) | BIT(7) | BIT(5) | BIT(6)); else rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) | BIT(7) | BIT(5)); break; case LED_PIN_LED1: ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1); rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0x0f) | BIT(5)); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = true; } void rtl92de_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); u8 ledcfg; RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: ledcfg &= 0xf0; if (pcipriv->ledctl.led_opendrain) rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(1) | BIT(5) | BIT(6))); else rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3) | BIT(5) | BIT(6))); break; case LED_PIN_LED1: ledcfg &= 0x0f; rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3))); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = false; } void rtl92de_init_sw_leds(struct ieee80211_hw *hw) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0); _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1); } static void _rtl92ce_sw_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); switch (ledaction) { case LED_CTL_POWER_ON: case LED_CTL_LINK: case LED_CTL_NO_LINK: rtl92de_sw_led_on(hw, pLed0); break; case LED_CTL_POWER_OFF: rtl92de_sw_led_off(hw, pLed0); break; default: break; } } void rtl92de_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) && (ledaction == LED_CTL_TX || ledaction == LED_CTL_RX || ledaction == LED_CTL_SITE_SURVEY || ledaction == LED_CTL_LINK || ledaction == LED_CTL_NO_LINK || ledaction == LED_CTL_START_TO_LINK || ledaction == LED_CTL_POWER_ON)) { return; } RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "ledaction %d,\n", ledaction); _rtl92ce_sw_led_control(hw, ledaction); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/hw.h0000644000175000017500000000564212026211315024375 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92DE_HW_H__ #define __RTL92DE_HW_H__ void rtl92de_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92de_read_eeprom_info(struct ieee80211_hw *hw); void rtl92de_interrupt_recognized(struct ieee80211_hw *hw, u32 *p_inta, u32 *p_intb); int rtl92de_hw_init(struct ieee80211_hw *hw); void rtl92de_card_disable(struct ieee80211_hw *hw); void rtl92de_enable_interrupt(struct ieee80211_hw *hw); void rtl92de_disable_interrupt(struct ieee80211_hw *hw); int rtl92de_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type); void rtl92de_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid); void rtl92de_set_qos(struct ieee80211_hw *hw, int aci); void rtl92de_set_beacon_related_registers(struct ieee80211_hw *hw); void rtl92de_set_beacon_interval(struct ieee80211_hw *hw); void rtl92de_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr); void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92de_update_hal_rate_tbl(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level); void rtl92de_update_channel_access_setting(struct ieee80211_hw *hw); bool rtl92de_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid); void rtl92de_enable_hw_security_config(struct ieee80211_hw *hw); void rtl92de_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); extern void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset, u32 value, u8 direct); extern u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, u8 direct); void rtl92de_suspend(struct ieee80211_hw *hw); void rtl92de_resume(struct ieee80211_hw *hw); void rtl92d_linked_set_reg(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/hw.c0000644000175000017500000021210012026211315024355 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../efuse.h" #include "../base.h" #include "../regd.h" #include "../cam.h" #include "../ps.h" #include "../pci.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "fw.h" #include "led.h" #include "sw.h" #include "hw.h" u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, u8 direct) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 value; rtl_write_word(rtlpriv, REG_DBI_CTRL, (offset & 0xFFC)); rtl_write_byte(rtlpriv, REG_DBI_FLAG, BIT(1) | direct); udelay(10); value = rtl_read_dword(rtlpriv, REG_DBI_RDATA); return value; } void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset, u32 value, u8 direct) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_word(rtlpriv, REG_DBI_CTRL, ((offset & 0xFFC) | 0xF000)); rtl_write_dword(rtlpriv, REG_DBI_WDATA, value); rtl_write_byte(rtlpriv, REG_DBI_FLAG, BIT(0) | direct); } static void _rtl92de_set_bcn_ctrl_reg(struct ieee80211_hw *hw, u8 set_bits, u8 clear_bits) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpci->reg_bcn_ctrl_val |= set_bits; rtlpci->reg_bcn_ctrl_val &= ~clear_bits; rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val); } static void _rtl92de_stop_tx_beacon(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp1byte; tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6))); rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0xff); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64); tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); tmp1byte &= ~(BIT(0)); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); } static void _rtl92de_resume_tx_beacon(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp1byte; tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6)); rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0x0a); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); tmp1byte |= BIT(0); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); } static void _rtl92de_enable_bcn_sub_func(struct ieee80211_hw *hw) { _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(1)); } static void _rtl92de_disable_bcn_sub_func(struct ieee80211_hw *hw) { _rtl92de_set_bcn_ctrl_reg(hw, BIT(1), 0); } void rtl92de_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); switch (variable) { case HW_VAR_RCR: *((u32 *) (val)) = rtlpci->receive_config; break; case HW_VAR_RF_STATE: *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state; break; case HW_VAR_FWLPS_RF_ON:{ enum rf_pwrstate rfState; u32 val_rcr; rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE, (u8 *) (&rfState)); if (rfState == ERFOFF) { *((bool *) (val)) = true; } else { val_rcr = rtl_read_dword(rtlpriv, REG_RCR); val_rcr &= 0x00070000; if (val_rcr) *((bool *) (val)) = false; else *((bool *) (val)) = true; } break; } case HW_VAR_FW_PSMODE_STATUS: *((bool *) (val)) = ppsc->fw_current_inpsmode; break; case HW_VAR_CORRECT_TSF:{ u64 tsf; u32 *ptsf_low = (u32 *)&tsf; u32 *ptsf_high = ((u32 *)&tsf) + 1; *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4)); *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR); *((u64 *) (val)) = tsf; break; } case HW_VAR_INT_MIGRATION: *((bool *)(val)) = rtlpriv->dm.interrupt_migration; break; case HW_VAR_INT_AC: *((bool *)(val)) = rtlpriv->dm.disable_tx_int; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 idx; switch (variable) { case HW_VAR_ETHER_ADDR: for (idx = 0; idx < ETH_ALEN; idx++) { rtl_write_byte(rtlpriv, (REG_MACID + idx), val[idx]); } break; case HW_VAR_BASIC_RATE: { u16 rate_cfg = ((u16 *) val)[0]; u8 rate_index = 0; rate_cfg = rate_cfg & 0x15f; if (mac->vendor == PEER_CISCO && ((rate_cfg & 0x150) == 0)) rate_cfg |= 0x01; rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff); rtl_write_byte(rtlpriv, REG_RRSR + 1, (rate_cfg >> 8) & 0xff); while (rate_cfg > 0x1) { rate_cfg = (rate_cfg >> 1); rate_index++; } if (rtlhal->fw_version > 0xe) rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, rate_index); break; } case HW_VAR_BSSID: for (idx = 0; idx < ETH_ALEN; idx++) { rtl_write_byte(rtlpriv, (REG_BSSID + idx), val[idx]); } break; case HW_VAR_SIFS: rtl_write_byte(rtlpriv, REG_SIFS_CTX + 1, val[0]); rtl_write_byte(rtlpriv, REG_SIFS_TRX + 1, val[1]); rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]); rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]); if (!mac->ht_enable) rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, 0x0e0e); else rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, *((u16 *) val)); break; case HW_VAR_SLOT_TIME: { u8 e_aci; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "HW_VAR_SLOT_TIME %x\n", val[0]); rtl_write_byte(rtlpriv, REG_SLOT, val[0]); for (e_aci = 0; e_aci < AC_MAX; e_aci++) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, (&e_aci)); break; } case HW_VAR_ACK_PREAMBLE: { u8 reg_tmp; u8 short_preamble = (bool) (*val); reg_tmp = (mac->cur_40_prime_sc) << 5; if (short_preamble) reg_tmp |= 0x80; rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_tmp); break; } case HW_VAR_AMPDU_MIN_SPACE: { u8 min_spacing_to_set; u8 sec_min_space; min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { sec_min_space = 0; if (min_spacing_to_set < sec_min_space) min_spacing_to_set = sec_min_space; mac->min_space_cfg = ((mac->min_space_cfg & 0xf8) | min_spacing_to_set); *val = min_spacing_to_set; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, mac->min_space_cfg); } break; } case HW_VAR_SHORTGI_DENSITY: { u8 density_to_set; density_to_set = *val; mac->min_space_cfg = rtlpriv->rtlhal.minspace_cfg; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_SHORTGI_DENSITY: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, mac->min_space_cfg); break; } case HW_VAR_AMPDU_FACTOR: { u8 factor_toset; u32 regtoSet; u8 *ptmp_byte = NULL; u8 index; if (rtlhal->macphymode == DUALMAC_DUALPHY) regtoSet = 0xb9726641; else if (rtlhal->macphymode == DUALMAC_SINGLEPHY) regtoSet = 0x66626641; else regtoSet = 0xb972a841; factor_toset = *val; if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) factor_toset = 0xf; for (index = 0; index < 4; index++) { ptmp_byte = (u8 *) (®toSet) + index; if ((*ptmp_byte & 0xf0) > (factor_toset << 4)) *ptmp_byte = (*ptmp_byte & 0x0f) | (factor_toset << 4); if ((*ptmp_byte & 0x0f) > factor_toset) *ptmp_byte = (*ptmp_byte & 0xf0) | (factor_toset); } rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, regtoSet); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_FACTOR: %#x\n", factor_toset); } break; } case HW_VAR_AC_PARAM: { u8 e_aci = *val; rtl92d_dm_init_edca_turbo(hw); if (rtlpci->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, &e_aci); break; } case HW_VAR_ACM_CTRL: { u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL); acm_ctrl = acm_ctrl | ((rtlpci->acm_method == 2) ? 0x0 : 0x1); if (acm) { switch (e_aci) { case AC0_BE: acm_ctrl |= ACMHW_BEQEN; break; case AC2_VI: acm_ctrl |= ACMHW_VIQEN; break; case AC3_VO: acm_ctrl |= ACMHW_VOQEN; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "HW_VAR_ACM_CTRL acm set failed: eACI is %d\n", acm); break; } } else { switch (e_aci) { case AC0_BE: acm_ctrl &= (~ACMHW_BEQEN); break; case AC2_VI: acm_ctrl &= (~ACMHW_VIQEN); break; case AC3_VO: acm_ctrl &= (~ACMHW_VOQEN); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE, "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] Write 0x%X\n", acm_ctrl); rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl); break; } case HW_VAR_RCR: rtl_write_dword(rtlpriv, REG_RCR, ((u32 *) (val))[0]); rtlpci->receive_config = ((u32 *) (val))[0]; break; case HW_VAR_RETRY_LIMIT: { u8 retry_limit = val[0]; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | retry_limit << RETRY_LIMIT_LONG_SHIFT); break; } case HW_VAR_DUAL_TSF_RST: rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1))); break; case HW_VAR_EFUSE_BYTES: rtlefuse->efuse_usedbytes = *((u16 *) val); break; case HW_VAR_EFUSE_USAGE: rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl92d_phy_set_io_cmd(hw, (*(enum io_type *)val)); break; case HW_VAR_WPA_CONFIG: rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_SET_RPWM: rtl92d_fill_h2c_cmd(hw, H2C_PWRM, 1, (val)); break; case HW_VAR_H2C_FW_PWRMODE: break; case HW_VAR_FW_PSMODE_STATUS: ppsc->fw_current_inpsmode = *((bool *) val); break; case HW_VAR_H2C_FW_JOINBSSRPT: { u8 mstatus = (*val); u8 tmp_regcr, tmp_reg422; bool recover = false; if (mstatus == RT_MEDIA_CONNECT) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AID, NULL); tmp_regcr = rtl_read_byte(rtlpriv, REG_CR + 1); rtl_write_byte(rtlpriv, REG_CR + 1, (tmp_regcr | BIT(0))); _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(3)); _rtl92de_set_bcn_ctrl_reg(hw, BIT(4), 0); tmp_reg422 = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); if (tmp_reg422 & BIT(6)) recover = true; rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp_reg422 & (~BIT(6))); rtl92d_set_fw_rsvdpagepkt(hw, 0); _rtl92de_set_bcn_ctrl_reg(hw, BIT(3), 0); _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(4)); if (recover) rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp_reg422); rtl_write_byte(rtlpriv, REG_CR + 1, (tmp_regcr & ~(BIT(0)))); } rtl92d_set_fw_joinbss_report_cmd(hw, (*val)); break; } case HW_VAR_AID: { u16 u2btmp; u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT); u2btmp &= 0xC000; rtl_write_word(rtlpriv, REG_BCN_PSR_RPT, (u2btmp | mac->assoc_id)); break; } case HW_VAR_CORRECT_TSF: { u8 btype_ibss = val[0]; if (btype_ibss) _rtl92de_stop_tx_beacon(hw); _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(3)); rtl_write_dword(rtlpriv, REG_TSFTR, (u32) (mac->tsf & 0xffffffff)); rtl_write_dword(rtlpriv, REG_TSFTR + 4, (u32) ((mac->tsf >> 32) & 0xffffffff)); _rtl92de_set_bcn_ctrl_reg(hw, BIT(3), 0); if (btype_ibss) _rtl92de_resume_tx_beacon(hw); break; } case HW_VAR_INT_MIGRATION: { bool int_migration = *(bool *) (val); if (int_migration) { /* Set interrupt migration timer and * corresponding Tx/Rx counter. * timer 25ns*0xfa0=100us for 0xf packets. * 0x306:Rx, 0x307:Tx */ rtl_write_dword(rtlpriv, REG_INT_MIG, 0xfe000fa0); rtlpriv->dm.interrupt_migration = int_migration; } else { /* Reset all interrupt migration settings. */ rtl_write_dword(rtlpriv, REG_INT_MIG, 0); rtlpriv->dm.interrupt_migration = int_migration; } break; } case HW_VAR_INT_AC: { bool disable_ac_int = *((bool *) val); /* Disable four ACs interrupts. */ if (disable_ac_int) { /* Disable VO, VI, BE and BK four AC interrupts * to gain more efficient CPU utilization. * When extremely highly Rx OK occurs, * we will disable Tx interrupts. */ rtlpriv->cfg->ops->update_interrupt_mask(hw, 0, RT_AC_INT_MASKS); rtlpriv->dm.disable_tx_int = disable_ac_int; /* Enable four ACs interrupts. */ } else { rtlpriv->cfg->ops->update_interrupt_mask(hw, RT_AC_INT_MASKS, 0); rtlpriv->dm.disable_tx_int = disable_ac_int; } break; } default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } static bool _rtl92de_llt_write(struct ieee80211_hw *hw, u32 address, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); bool status = true; long count = 0; u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) | _LLT_OP(_LLT_WRITE_ACCESS); rtl_write_dword(rtlpriv, REG_LLT_INIT, value); do { value = rtl_read_dword(rtlpriv, REG_LLT_INIT); if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value)) break; if (count > POLLING_LLT_THRESHOLD) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to polling write LLT done at address %d!\n", address); status = false; break; } } while (++count); return status; } static bool _rtl92de_llt_table_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); unsigned short i; u8 txpktbuf_bndy; u8 maxPage; bool status; u32 value32; /* High+low page number */ u8 value8; /* normal page number */ if (rtlpriv->rtlhal.macphymode == SINGLEMAC_SINGLEPHY) { maxPage = 255; txpktbuf_bndy = 246; value8 = 0; value32 = 0x80bf0d29; } else if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) { maxPage = 127; txpktbuf_bndy = 123; value8 = 0; value32 = 0x80750005; } /* Set reserved page for each queue */ /* 11. RQPN 0x200[31:0] = 0x80BD1C1C */ /* load RQPN */ rtl_write_byte(rtlpriv, REG_RQPN_NPQ, value8); rtl_write_dword(rtlpriv, REG_RQPN, value32); /* 12. TXRKTBUG_PG_BNDY 0x114[31:0] = 0x27FF00F6 */ /* TXRKTBUG_PG_BNDY */ rtl_write_dword(rtlpriv, REG_TRXFF_BNDY, (rtl_read_word(rtlpriv, REG_TRXFF_BNDY + 2) << 16 | txpktbuf_bndy)); /* 13. TDECTRL[15:8] 0x209[7:0] = 0xF6 */ /* Beacon Head for TXDMA */ rtl_write_byte(rtlpriv, REG_TDECTRL + 1, txpktbuf_bndy); /* 14. BCNQ_PGBNDY 0x424[7:0] = 0xF6 */ /* BCNQ_PGBNDY */ rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy); /* 15. WMAC_LBK_BF_HD 0x45D[7:0] = 0xF6 */ /* WMAC_LBK_BF_HD */ rtl_write_byte(rtlpriv, 0x45D, txpktbuf_bndy); /* Set Tx/Rx page size (Tx must be 128 Bytes, */ /* Rx can be 64,128,256,512,1024 bytes) */ /* 16. PBP [7:0] = 0x11 */ /* TRX page size */ rtl_write_byte(rtlpriv, REG_PBP, 0x11); /* 17. DRV_INFO_SZ = 0x04 */ rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, 0x4); /* 18. LLT_table_init(Adapter); */ for (i = 0; i < (txpktbuf_bndy - 1); i++) { status = _rtl92de_llt_write(hw, i, i + 1); if (true != status) return status; } /* end of list */ status = _rtl92de_llt_write(hw, (txpktbuf_bndy - 1), 0xFF); if (true != status) return status; /* Make the other pages as ring buffer */ /* This ring buffer is used as beacon buffer if we */ /* config this MAC as two MAC transfer. */ /* Otherwise used as local loopback buffer. */ for (i = txpktbuf_bndy; i < maxPage; i++) { status = _rtl92de_llt_write(hw, i, (i + 1)); if (true != status) return status; } /* Let last entry point to the start entry of ring buffer */ status = _rtl92de_llt_write(hw, maxPage, txpktbuf_bndy); if (true != status) return status; return true; } static void _rtl92de_gen_refresh_led_state(struct ieee80211_hw *hw) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); if (rtlpci->up_first_time) return; if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) rtl92de_sw_led_on(hw, pLed0); else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT) rtl92de_sw_led_on(hw, pLed0); else rtl92de_sw_led_off(hw, pLed0); } static bool _rtl92de_init_mac(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); unsigned char bytetmp; unsigned short wordtmp; u16 retry; rtl92d_phy_set_poweron(hw); /* Add for resume sequence of power domain according * to power document V11. Chapter V.11.... */ /* 0. RSV_CTRL 0x1C[7:0] = 0x00 */ /* unlock ISO/CLK/Power control register */ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00); rtl_write_byte(rtlpriv, REG_LDOA15_CTRL, 0x05); /* 1. AFE_XTAL_CTRL [7:0] = 0x0F enable XTAL */ /* 2. SPS0_CTRL 0x11[7:0] = 0x2b enable SPS into PWM mode */ /* 3. delay (1ms) this is not necessary when initially power on */ /* C. Resume Sequence */ /* a. SPS0_CTRL 0x11[7:0] = 0x2b */ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b); /* b. AFE_XTAL_CTRL [7:0] = 0x0F */ rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL, 0x0F); /* c. DRV runs power on init flow */ /* auto enable WLAN */ /* 4. APS_FSMCO 0x04[8] = 1; wait till 0x04[8] = 0 */ /* Power On Reset for MAC Block */ bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1) | BIT(0); udelay(2); rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, bytetmp); udelay(2); /* 5. Wait while 0x04[8] == 0 goto 2, otherwise goto 1 */ bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1); udelay(50); retry = 0; while ((bytetmp & BIT(0)) && retry < 1000) { retry++; bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1); udelay(50); } /* Enable Radio off, GPIO, and LED function */ /* 6. APS_FSMCO 0x04[15:0] = 0x0012 when enable HWPDN */ rtl_write_word(rtlpriv, REG_APS_FSMCO, 0x1012); /* release RF digital isolation */ /* 7. SYS_ISO_CTRL 0x01[1] = 0x0; */ /*Set REG_SYS_ISO_CTRL 0x1=0x82 to prevent wake# problem. */ rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, 0x82); udelay(2); /* make sure that BB reset OK. */ /* rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); */ /* Disable REG_CR before enable it to assure reset */ rtl_write_word(rtlpriv, REG_CR, 0x0); /* Release MAC IO register reset */ rtl_write_word(rtlpriv, REG_CR, 0x2ff); /* clear stopping tx/rx dma */ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0x0); /* rtl_write_word(rtlpriv,REG_CR+2, 0x2); */ /* System init */ /* 18. LLT_table_init(Adapter); */ if (!_rtl92de_llt_table_init(hw)) return false; /* Clear interrupt and enable interrupt */ /* 19. HISR 0x124[31:0] = 0xffffffff; */ /* HISRE 0x12C[7:0] = 0xFF */ rtl_write_dword(rtlpriv, REG_HISR, 0xffffffff); rtl_write_byte(rtlpriv, REG_HISRE, 0xff); /* 20. HIMR 0x120[31:0] |= [enable INT mask bit map]; */ /* 21. HIMRE 0x128[7:0] = [enable INT mask bit map] */ /* The IMR should be enabled later after all init sequence * is finished. */ /* 22. PCIE configuration space configuration */ /* 23. Ensure PCIe Device 0x80[15:0] = 0x0143 (ASPM+CLKREQ), */ /* and PCIe gated clock function is enabled. */ /* PCIE configuration space will be written after * all init sequence.(Or by BIOS) */ rtl92d_phy_config_maccoexist_rfpage(hw); /* THe below section is not related to power document Vxx . */ /* This is only useful for driver and OS setting. */ /* -------------------Software Relative Setting---------------------- */ wordtmp = rtl_read_word(rtlpriv, REG_TRXDMA_CTRL); wordtmp &= 0xf; wordtmp |= 0xF771; rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, wordtmp); /* Reported Tx status from HW for rate adaptive. */ /* This should be realtive to power on step 14. But in document V11 */ /* still not contain the description.!!! */ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 1, 0x1F); /* Set Tx/Rx page size (Tx must be 128 Bytes, * Rx can be 64,128,256,512,1024 bytes) */ /* rtl_write_byte(rtlpriv,REG_PBP, 0x11); */ /* Set RCR register */ rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); /* rtl_write_byte(rtlpriv,REG_RX_DRVINFO_SZ, 4); */ /* Set TCR register */ rtl_write_dword(rtlpriv, REG_TCR, rtlpci->transmit_config); /* disable earlymode */ rtl_write_byte(rtlpriv, 0x4d0, 0x0); /* Set TX/RX descriptor physical address(from OS API). */ rtl_write_dword(rtlpriv, REG_BCNQ_DESA, rtlpci->tx_ring[BEACON_QUEUE].dma); rtl_write_dword(rtlpriv, REG_MGQ_DESA, rtlpci->tx_ring[MGNT_QUEUE].dma); rtl_write_dword(rtlpriv, REG_VOQ_DESA, rtlpci->tx_ring[VO_QUEUE].dma); rtl_write_dword(rtlpriv, REG_VIQ_DESA, rtlpci->tx_ring[VI_QUEUE].dma); rtl_write_dword(rtlpriv, REG_BEQ_DESA, rtlpci->tx_ring[BE_QUEUE].dma); rtl_write_dword(rtlpriv, REG_BKQ_DESA, rtlpci->tx_ring[BK_QUEUE].dma); rtl_write_dword(rtlpriv, REG_HQ_DESA, rtlpci->tx_ring[HIGH_QUEUE].dma); /* Set RX Desc Address */ rtl_write_dword(rtlpriv, REG_RX_DESA, rtlpci->rx_ring[RX_MPDU_QUEUE].dma); /* if we want to support 64 bit DMA, we should set it here, * but now we do not support 64 bit DMA*/ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 3, 0x33); /* Reset interrupt migration setting when initialization */ rtl_write_dword(rtlpriv, REG_INT_MIG, 0); /* Reconsider when to do this operation after asking HWSD. */ bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); rtl_write_byte(rtlpriv, REG_APSD_CTRL, bytetmp & ~BIT(6)); do { retry++; bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); } while ((retry < 200) && !(bytetmp & BIT(7))); /* After MACIO reset,we must refresh LED state. */ _rtl92de_gen_refresh_led_state(hw); /* Reset H2C protection register */ rtl_write_dword(rtlpriv, REG_MCUTST_1, 0x0); return true; } static void _rtl92de_hw_configure(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 reg_bw_opmode = BW_OPMODE_20MHZ; u32 reg_rrsr; reg_rrsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG; rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8); rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); rtl_write_dword(rtlpriv, REG_RRSR, reg_rrsr); rtl_write_byte(rtlpriv, REG_SLOT, 0x09); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, 0x0); rtl_write_word(rtlpriv, REG_FWHW_TXQ_CTRL, 0x1F80); rtl_write_word(rtlpriv, REG_RL, 0x0707); rtl_write_dword(rtlpriv, REG_BAR_MODE_CTRL, 0x02012802); rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF); rtl_write_dword(rtlpriv, REG_DARFRC, 0x01000000); rtl_write_dword(rtlpriv, REG_DARFRC + 4, 0x07060504); rtl_write_dword(rtlpriv, REG_RARFRC, 0x01000000); rtl_write_dword(rtlpriv, REG_RARFRC + 4, 0x07060504); /* Aggregation threshold */ if (rtlhal->macphymode == DUALMAC_DUALPHY) rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0xb9726641); else if (rtlhal->macphymode == DUALMAC_SINGLEPHY) rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0x66626641); else rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0xb972a841); rtl_write_byte(rtlpriv, REG_ATIMWND, 0x2); rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0x0a); rtlpci->reg_bcn_ctrl_val = 0x1f; rtl_write_byte(rtlpriv, REG_BCN_CTRL, rtlpci->reg_bcn_ctrl_val); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); rtl_write_byte(rtlpriv, REG_PIFS, 0x1C); rtl_write_byte(rtlpriv, REG_AGGR_BREAK_TIME, 0x16); rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020); /* For throughput */ rtl_write_word(rtlpriv, REG_FAST_EDCA_CTRL, 0x6666); /* ACKTO for IOT issue. */ rtl_write_byte(rtlpriv, REG_ACKTO, 0x40); /* Set Spec SIFS (used in NAV) */ rtl_write_word(rtlpriv, REG_SPEC_SIFS, 0x1010); rtl_write_word(rtlpriv, REG_MAC_SPEC_SIFS, 0x1010); /* Set SIFS for CCK */ rtl_write_word(rtlpriv, REG_SIFS_CTX, 0x1010); /* Set SIFS for OFDM */ rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x1010); /* Set Multicast Address. */ rtl_write_dword(rtlpriv, REG_MAR, 0xffffffff); rtl_write_dword(rtlpriv, REG_MAR + 4, 0xffffffff); switch (rtlpriv->phy.rf_type) { case RF_1T2R: case RF_1T1R: rtlhal->minspace_cfg = (MAX_MSS_DENSITY_1T << 3); break; case RF_2T2R: case RF_2T2R_GREEN: rtlhal->minspace_cfg = (MAX_MSS_DENSITY_2T << 3); break; } } static void _rtl92de_enable_aspm_back_door(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); rtl_write_byte(rtlpriv, 0x34b, 0x93); rtl_write_word(rtlpriv, 0x350, 0x870c); rtl_write_byte(rtlpriv, 0x352, 0x1); if (ppsc->support_backdoor) rtl_write_byte(rtlpriv, 0x349, 0x1b); else rtl_write_byte(rtlpriv, 0x349, 0x03); rtl_write_word(rtlpriv, 0x350, 0x2718); rtl_write_byte(rtlpriv, 0x352, 0x1); } void rtl92de_enable_hw_security_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 sec_reg_value; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n", rtlpriv->sec.pairwise_enc_algorithm, rtlpriv->sec.group_enc_algorithm); if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "not open hw encryption\n"); return; } sec_reg_value = SCR_TXENCENABLE | SCR_RXENCENABLE; if (rtlpriv->sec.use_defaultkey) { sec_reg_value |= SCR_TXUSEDK; sec_reg_value |= SCR_RXUSEDK; } sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK); rtl_write_byte(rtlpriv, REG_CR + 1, 0x02); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The SECR-value %x\n", sec_reg_value); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); } int rtl92de_hw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool rtstatus = true; u8 tmp_u1b; int i; int err; unsigned long flags; rtlpci->being_init_adapter = true; rtlpci->init_ready = false; spin_lock_irqsave(&globalmutex_for_power_and_efuse, flags); /* we should do iqk after disable/enable */ rtl92d_phy_reset_iqk_result(hw); /* rtlpriv->intf_ops->disable_aspm(hw); */ rtstatus = _rtl92de_init_mac(hw); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n"); err = 1; spin_unlock_irqrestore(&globalmutex_for_power_and_efuse, flags); return err; } err = rtl92d_download_fw(hw); spin_unlock_irqrestore(&globalmutex_for_power_and_efuse, flags); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW..\n"); return 1; } rtlhal->last_hmeboxnum = 0; rtlpriv->psc.fw_current_inpsmode = false; tmp_u1b = rtl_read_byte(rtlpriv, 0x605); tmp_u1b = tmp_u1b | 0x30; rtl_write_byte(rtlpriv, 0x605, tmp_u1b); if (rtlhal->earlymode_enable) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EarlyMode Enabled!!!\n"); tmp_u1b = rtl_read_byte(rtlpriv, 0x4d0); tmp_u1b = tmp_u1b | 0x1f; rtl_write_byte(rtlpriv, 0x4d0, tmp_u1b); rtl_write_byte(rtlpriv, 0x4d3, 0x80); tmp_u1b = rtl_read_byte(rtlpriv, 0x605); tmp_u1b = tmp_u1b | 0x40; rtl_write_byte(rtlpriv, 0x605, tmp_u1b); } if (mac->rdg_en) { rtl_write_byte(rtlpriv, REG_RD_CTRL, 0xff); rtl_write_word(rtlpriv, REG_RD_NAV_NXT, 0x200); rtl_write_byte(rtlpriv, REG_RD_RESP_PKT_TH, 0x05); } rtl92d_phy_mac_config(hw); /* because last function modify RCR, so we update * rcr var here, or TP will unstable for receive_config * is wrong, RX RCR_ACRC32 will cause TP unstabel & Rx * RCR_APP_ICV will cause mac80211 unassoc for cisco 1252*/ rtlpci->receive_config = rtl_read_dword(rtlpriv, REG_RCR); rtlpci->receive_config &= ~(RCR_ACRC32 | RCR_AICV); rtl92d_phy_bb_config(hw); rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; /* set before initialize RF */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf); /* config RF */ rtl92d_phy_rf_config(hw); /* After read predefined TXT, we must set BB/MAC/RF * register as our requirement */ /* After load BB,RF params,we need do more for 92D. */ rtl92d_update_bbrf_configuration(hw); /* set default value after initialize RF, */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0); rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0, RF_CHNLBW, BRFREGOFFSETMASK); rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1, RF_CHNLBW, BRFREGOFFSETMASK); /*---- Set CCK and OFDM Block "ON"----*/ if (rtlhal->current_bandtype == BAND_ON_2_4G) rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1); if (rtlhal->interfaceindex == 0) { /* RFPGA0_ANALOGPARAMETER2: cck clock select, * set to 20MHz by default */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10) | BIT(11), 3); } else { /* Mac1 */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(11) | BIT(10), 3); } _rtl92de_hw_configure(hw); /* reset hw sec */ rtl_cam_reset_all_entry(hw); rtl92de_enable_hw_security_config(hw); /* Read EEPROM TX power index and PHY_REG_PG.txt to capture correct */ /* TX power index for different rate set. */ rtl92d_phy_get_hw_reg_originalvalue(hw); rtl92d_phy_set_txpower_level(hw, rtlphy->current_channel); ppsc->rfpwr_state = ERFON; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr); _rtl92de_enable_aspm_back_door(hw); /* rtlpriv->intf_ops->enable_aspm(hw); */ rtl92d_dm_init(hw); rtlpci->being_init_adapter = false; if (ppsc->rfpwr_state == ERFON) { rtl92d_phy_lc_calibrate(hw); /* 5G and 2.4G must wait sometime to let RF LO ready */ if (rtlhal->macphymode == DUALMAC_DUALPHY) { u32 tmp_rega; for (i = 0; i < 10000; i++) { udelay(MAX_STALL_TIME); tmp_rega = rtl_get_rfreg(hw, (enum radio_path)RF90_PATH_A, 0x2a, BMASKDWORD); if (((tmp_rega & BIT(11)) == BIT(11))) break; } /* check that loop was successful. If not, exit now */ if (i == 10000) { rtlpci->init_ready = false; return 1; } } } rtlpci->init_ready = true; return err; } static enum version_8192d _rtl92de_read_chip_version(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); enum version_8192d version = VERSION_NORMAL_CHIP_92D_SINGLEPHY; u32 value32; value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG); if (!(value32 & 0x000f0000)) { version = VERSION_TEST_CHIP_92D_SINGLEPHY; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "TEST CHIP!!!\n"); } else { version = VERSION_NORMAL_CHIP_92D_SINGLEPHY; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Normal CHIP!!!\n"); } return version; } static int _rtl92de_set_media_status(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bt_msr = rtl_read_byte(rtlpriv, MSR); enum led_ctl_mode ledaction = LED_CTL_NO_LINK; u8 bcnfunc_enable; bt_msr &= 0xfc; if (type == NL80211_IFTYPE_UNSPECIFIED || type == NL80211_IFTYPE_STATION) { _rtl92de_stop_tx_beacon(hw); _rtl92de_enable_bcn_sub_func(hw); } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP) { _rtl92de_resume_tx_beacon(hw); _rtl92de_disable_bcn_sub_func(hw); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Set HW_VAR_MEDIA_STATUS: No such media status(%x)\n", type); } bcnfunc_enable = rtl_read_byte(rtlpriv, REG_BCN_CTRL); switch (type) { case NL80211_IFTYPE_UNSPECIFIED: bt_msr |= MSR_NOLINK; ledaction = LED_CTL_LINK; bcnfunc_enable &= 0xF7; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: bt_msr |= MSR_ADHOC; bcnfunc_enable |= 0x08; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: bt_msr |= MSR_INFRA; ledaction = LED_CTL_LINK; bcnfunc_enable &= 0xF7; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: bt_msr |= MSR_AP; bcnfunc_enable |= 0x08; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to AP!\n"); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Network type %d not supported!\n", type); return 1; break; } rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); if ((bt_msr & 0xfc) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); return 0; } void rtl92de_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u32 reg_rcr = rtlpci->receive_config; if (rtlpriv->psc.rfpwr_state != ERFON) return; if (check_bssid) { reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); _rtl92de_set_bcn_ctrl_reg(hw, 0, BIT(4)); } else if (!check_bssid) { reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN)); _rtl92de_set_bcn_ctrl_reg(hw, BIT(4), 0); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); } } int rtl92de_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (_rtl92de_set_media_status(hw, type)) return -EOPNOTSUPP; /* check bssid */ if (rtlpriv->mac80211.link_state == MAC80211_LINKED) { if (type != NL80211_IFTYPE_AP) rtl92de_set_check_bssid(hw, true); } else { rtl92de_set_check_bssid(hw, false); } return 0; } /* do iqk or reload iqk */ /* windows just rtl92d_phy_reload_iqk_setting in set channel, * but it's very strict for time sequence so we add * rtl92d_phy_reload_iqk_setting here */ void rtl92d_linked_set_reg(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 indexforchannel; u8 channel = rtlphy->current_channel; indexforchannel = rtl92d_get_rightchnlplace_for_iqk(channel); if (!rtlphy->iqk_matrix_regsetting[indexforchannel].iqk_done) { RT_TRACE(rtlpriv, COMP_SCAN | COMP_INIT, DBG_DMESG, "Do IQK for channel:%d\n", channel); rtl92d_phy_iq_calibrate(hw); } } /* don't set REG_EDCA_BE_PARAM here because * mac80211 will send pkt when scan */ void rtl92de_set_qos(struct ieee80211_hw *hw, int aci) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl92d_dm_init_edca_turbo(hw); return; switch (aci) { case AC1_BK: rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f); break; case AC0_BE: break; case AC2_VI: rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322); break; case AC3_VO: rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222); break; default: RT_ASSERT(false, "invalid aci: %d !\n", aci); break; } } void rtl92de_enable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF); } void rtl92de_disable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED); rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED); synchronize_irq(rtlpci->pdev->irq); } static void _rtl92de_poweroff_adapter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 u1b_tmp; unsigned long flags; rtlpriv->intf_ops->enable_aspm(hw); rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00); rtl_set_bbreg(hw, RFPGA0_XCD_RFPARAMETER, BIT(3), 0); rtl_set_bbreg(hw, RFPGA0_XCD_RFPARAMETER, BIT(15), 0); /* 0x20:value 05-->04 */ rtl_write_byte(rtlpriv, REG_LDOA15_CTRL, 0x04); /* ==== Reset digital sequence ====== */ rtl92d_firmware_selfreset(hw); /* f. SYS_FUNC_EN 0x03[7:0]=0x51 reset MCU, MAC register, DCORE */ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, 0x51); /* g. MCUFWDL 0x80[1:0]=0 reset MCU ready status */ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); /* ==== Pull GPIO PIN to balance level and LED control ====== */ /* h. GPIO_PIN_CTRL 0x44[31:0]=0x000 */ rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00000000); /* i. Value = GPIO_PIN_CTRL[7:0] */ u1b_tmp = rtl_read_byte(rtlpriv, REG_GPIO_PIN_CTRL); /* j. GPIO_PIN_CTRL 0x44[31:0] = 0x00FF0000 | (value <<8); */ /* write external PIN level */ rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00FF0000 | (u1b_tmp << 8)); /* k. GPIO_MUXCFG 0x42 [15:0] = 0x0780 */ rtl_write_word(rtlpriv, REG_GPIO_IO_SEL, 0x0790); /* l. LEDCFG 0x4C[15:0] = 0x8080 */ rtl_write_word(rtlpriv, REG_LEDCFG0, 0x8080); /* ==== Disable analog sequence === */ /* m. AFE_PLL_CTRL[7:0] = 0x80 disable PLL */ rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x80); /* n. SPS0_CTRL 0x11[7:0] = 0x22 enter PFM mode */ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x23); /* o. AFE_XTAL_CTRL 0x24[7:0] = 0x0E disable XTAL, if No BT COEX */ rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL, 0x0e); /* p. RSV_CTRL 0x1C[7:0] = 0x0E lock ISO/CLK/Power control register */ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0e); /* ==== interface into suspend === */ /* q. APS_FSMCO[15:8] = 0x58 PCIe suspend mode */ /* According to power document V11, we need to set this */ /* value as 0x18. Otherwise, we may not L0s sometimes. */ /* This indluences power consumption. Bases on SD1's test, */ /* set as 0x00 do not affect power current. And if it */ /* is set as 0x18, they had ever met auto load fail problem. */ rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, 0x10); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "In PowerOff,reg0x%x=%X\n", REG_SPS0_CTRL, rtl_read_byte(rtlpriv, REG_SPS0_CTRL)); /* r. Note: for PCIe interface, PON will not turn */ /* off m-bias and BandGap in PCIe suspend mode. */ /* 0x17[7] 1b': power off in process 0b' : power off over */ if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) { spin_lock_irqsave(&globalmutex_power, flags); u1b_tmp = rtl_read_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS); u1b_tmp &= (~BIT(7)); rtl_write_byte(rtlpriv, REG_POWER_OFF_IN_PROCESS, u1b_tmp); spin_unlock_irqrestore(&globalmutex_power, flags); } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "<=======\n"); } void rtl92de_card_disable(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); enum nl80211_iftype opmode; mac->link_state = MAC80211_NOLINK; opmode = NL80211_IFTYPE_UNSPECIFIED; _rtl92de_set_media_status(hw, opmode); if (rtlpci->driver_is_goingto_unload || ppsc->rfoff_reason > RF_CHANGE_BY_PS) rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); /* Power sequence for each MAC. */ /* a. stop tx DMA */ /* b. close RF */ /* c. clear rx buf */ /* d. stop rx DMA */ /* e. reset MAC */ /* a. stop tx DMA */ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0xFE); udelay(50); /* b. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue */ /* c. ========RF OFF sequence========== */ /* 0x88c[23:20] = 0xf. */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00); /* APSD_CTRL 0x600[7:0] = 0x40 */ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); /* Close antenna 0,0xc04,0xd04 */ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0); /* SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB state machine */ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); /* Mac0 can not do Global reset. Mac1 can do. */ /* SYS_FUNC_EN 0x02[7:0] = 0xE0 reset BB state machine */ if (rtlpriv->rtlhal.interfaceindex == 1) rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE0); udelay(50); /* d. stop tx/rx dma before disable REG_CR (0x100) to fix */ /* dma hang issue when disable/enable device. */ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0xff); udelay(50); rtl_write_byte(rtlpriv, REG_CR, 0x0); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "==> Do power off.......\n"); if (rtl92d_phy_check_poweroff(hw)) _rtl92de_poweroff_adapter(hw); return; } void rtl92de_interrupt_recognized(struct ieee80211_hw *hw, u32 *p_inta, u32 *p_intb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); *p_inta = rtl_read_dword(rtlpriv, ISR) & rtlpci->irq_mask[0]; rtl_write_dword(rtlpriv, ISR, *p_inta); /* * *p_intb = rtl_read_dword(rtlpriv, REG_HISRE) & rtlpci->irq_mask[1]; * rtl_write_dword(rtlpriv, ISR + 4, *p_intb); */ } void rtl92de_set_beacon_related_registers(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcn_interval, atim_window; bcn_interval = mac->beacon_interval; atim_window = 2; /*rtl92de_disable_interrupt(hw); */ rtl_write_word(rtlpriv, REG_ATIMWND, atim_window); rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f); rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x20); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_5G) rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x30); else rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x20); rtl_write_byte(rtlpriv, 0x606, 0x30); } void rtl92de_set_beacon_interval(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcn_interval = mac->beacon_interval; RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG, "beacon_interval:%d\n", bcn_interval); /* rtl92de_disable_interrupt(hw); */ rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); /* rtl92de_enable_interrupt(hw); */ } void rtl92de_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, "add_msr:%x, rm_msr:%x\n", add_msr, rm_msr); if (add_msr) rtlpci->irq_mask[0] |= add_msr; if (rm_msr) rtlpci->irq_mask[0] &= (~rm_msr); rtl92de_disable_interrupt(hw); rtl92de_enable_interrupt(hw); } static void _rtl92de_readpowervalue_fromprom(struct txpower_info *pwrinfo, u8 *rom_content, bool autoLoadfail) { u32 rfpath, eeaddr, group, offset1, offset2; u8 i; memset(pwrinfo, 0, sizeof(struct txpower_info)); if (autoLoadfail) { for (group = 0; group < CHANNEL_GROUP_MAX; group++) { for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) { if (group < CHANNEL_GROUP_MAX_2G) { pwrinfo->cck_index[rfpath][group] = EEPROM_DEFAULT_TXPOWERLEVEL_2G; pwrinfo->ht40_1sindex[rfpath][group] = EEPROM_DEFAULT_TXPOWERLEVEL_2G; } else { pwrinfo->ht40_1sindex[rfpath][group] = EEPROM_DEFAULT_TXPOWERLEVEL_5G; } pwrinfo->ht40_2sindexdiff[rfpath][group] = EEPROM_DEFAULT_HT40_2SDIFF; pwrinfo->ht20indexdiff[rfpath][group] = EEPROM_DEFAULT_HT20_DIFF; pwrinfo->ofdmindexdiff[rfpath][group] = EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF; pwrinfo->ht40maxoffset[rfpath][group] = EEPROM_DEFAULT_HT40_PWRMAXOFFSET; pwrinfo->ht20maxoffset[rfpath][group] = EEPROM_DEFAULT_HT20_PWRMAXOFFSET; } } for (i = 0; i < 3; i++) { pwrinfo->tssi_a[i] = EEPROM_DEFAULT_TSSI; pwrinfo->tssi_b[i] = EEPROM_DEFAULT_TSSI; } return; } /* Maybe autoload OK,buf the tx power index value is not filled. * If we find it, we set it to default value. */ for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) { for (group = 0; group < CHANNEL_GROUP_MAX_2G; group++) { eeaddr = EEPROM_CCK_TX_PWR_INX_2G + (rfpath * 3) + group; pwrinfo->cck_index[rfpath][group] = (rom_content[eeaddr] == 0xFF) ? (eeaddr > 0x7B ? EEPROM_DEFAULT_TXPOWERLEVEL_5G : EEPROM_DEFAULT_TXPOWERLEVEL_2G) : rom_content[eeaddr]; } } for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) { for (group = 0; group < CHANNEL_GROUP_MAX; group++) { offset1 = group / 3; offset2 = group % 3; eeaddr = EEPROM_HT40_1S_TX_PWR_INX_2G + (rfpath * 3) + offset2 + offset1 * 21; pwrinfo->ht40_1sindex[rfpath][group] = (rom_content[eeaddr] == 0xFF) ? (eeaddr > 0x7B ? EEPROM_DEFAULT_TXPOWERLEVEL_5G : EEPROM_DEFAULT_TXPOWERLEVEL_2G) : rom_content[eeaddr]; } } /* These just for 92D efuse offset. */ for (group = 0; group < CHANNEL_GROUP_MAX; group++) { for (rfpath = 0; rfpath < RF6052_MAX_PATH; rfpath++) { int base1 = EEPROM_HT40_2S_TX_PWR_INX_DIFF_2G; offset1 = group / 3; offset2 = group % 3; if (rom_content[base1 + offset2 + offset1 * 21] != 0xFF) pwrinfo->ht40_2sindexdiff[rfpath][group] = (rom_content[base1 + offset2 + offset1 * 21] >> (rfpath * 4)) & 0xF; else pwrinfo->ht40_2sindexdiff[rfpath][group] = EEPROM_DEFAULT_HT40_2SDIFF; if (rom_content[EEPROM_HT20_TX_PWR_INX_DIFF_2G + offset2 + offset1 * 21] != 0xFF) pwrinfo->ht20indexdiff[rfpath][group] = (rom_content[EEPROM_HT20_TX_PWR_INX_DIFF_2G + offset2 + offset1 * 21] >> (rfpath * 4)) & 0xF; else pwrinfo->ht20indexdiff[rfpath][group] = EEPROM_DEFAULT_HT20_DIFF; if (rom_content[EEPROM_OFDM_TX_PWR_INX_DIFF_2G + offset2 + offset1 * 21] != 0xFF) pwrinfo->ofdmindexdiff[rfpath][group] = (rom_content[EEPROM_OFDM_TX_PWR_INX_DIFF_2G + offset2 + offset1 * 21] >> (rfpath * 4)) & 0xF; else pwrinfo->ofdmindexdiff[rfpath][group] = EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF; if (rom_content[EEPROM_HT40_MAX_PWR_OFFSET_2G + offset2 + offset1 * 21] != 0xFF) pwrinfo->ht40maxoffset[rfpath][group] = (rom_content[EEPROM_HT40_MAX_PWR_OFFSET_2G + offset2 + offset1 * 21] >> (rfpath * 4)) & 0xF; else pwrinfo->ht40maxoffset[rfpath][group] = EEPROM_DEFAULT_HT40_PWRMAXOFFSET; if (rom_content[EEPROM_HT20_MAX_PWR_OFFSET_2G + offset2 + offset1 * 21] != 0xFF) pwrinfo->ht20maxoffset[rfpath][group] = (rom_content[EEPROM_HT20_MAX_PWR_OFFSET_2G + offset2 + offset1 * 21] >> (rfpath * 4)) & 0xF; else pwrinfo->ht20maxoffset[rfpath][group] = EEPROM_DEFAULT_HT20_PWRMAXOFFSET; } } if (rom_content[EEPROM_TSSI_A_5G] != 0xFF) { /* 5GL */ pwrinfo->tssi_a[0] = rom_content[EEPROM_TSSI_A_5G] & 0x3F; pwrinfo->tssi_b[0] = rom_content[EEPROM_TSSI_B_5G] & 0x3F; /* 5GM */ pwrinfo->tssi_a[1] = rom_content[EEPROM_TSSI_AB_5G] & 0x3F; pwrinfo->tssi_b[1] = (rom_content[EEPROM_TSSI_AB_5G] & 0xC0) >> 6 | (rom_content[EEPROM_TSSI_AB_5G + 1] & 0x0F) << 2; /* 5GH */ pwrinfo->tssi_a[2] = (rom_content[EEPROM_TSSI_AB_5G + 1] & 0xF0) >> 4 | (rom_content[EEPROM_TSSI_AB_5G + 2] & 0x03) << 4; pwrinfo->tssi_b[2] = (rom_content[EEPROM_TSSI_AB_5G + 2] & 0xFC) >> 2; } else { for (i = 0; i < 3; i++) { pwrinfo->tssi_a[i] = EEPROM_DEFAULT_TSSI; pwrinfo->tssi_b[i] = EEPROM_DEFAULT_TSSI; } } } static void _rtl92de_read_txpower_info(struct ieee80211_hw *hw, bool autoload_fail, u8 *hwinfo) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct txpower_info pwrinfo; u8 tempval[2], i, pwr, diff; u32 ch, rfPath, group; _rtl92de_readpowervalue_fromprom(&pwrinfo, hwinfo, autoload_fail); if (!autoload_fail) { /* bit0~2 */ rtlefuse->eeprom_regulatory = (hwinfo[EEPROM_RF_OPT1] & 0x7); rtlefuse->eeprom_thermalmeter = hwinfo[EEPROM_THERMAL_METER] & 0x1f; rtlefuse->crystalcap = hwinfo[EEPROM_XTAL_K]; tempval[0] = hwinfo[EEPROM_IQK_DELTA] & 0x03; tempval[1] = (hwinfo[EEPROM_LCK_DELTA] & 0x0C) >> 2; rtlefuse->txpwr_fromeprom = true; if (IS_92D_D_CUT(rtlpriv->rtlhal.version) || IS_92D_E_CUT(rtlpriv->rtlhal.version)) { rtlefuse->internal_pa_5g[0] = !((hwinfo[EEPROM_TSSI_A_5G] & BIT(6)) >> 6); rtlefuse->internal_pa_5g[1] = !((hwinfo[EEPROM_TSSI_B_5G] & BIT(6)) >> 6); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Is D cut,Internal PA0 %d Internal PA1 %d\n", rtlefuse->internal_pa_5g[0], rtlefuse->internal_pa_5g[1]); } rtlefuse->eeprom_c9 = hwinfo[EEPROM_RF_OPT6]; rtlefuse->eeprom_cc = hwinfo[EEPROM_RF_OPT7]; } else { rtlefuse->eeprom_regulatory = 0; rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER; rtlefuse->crystalcap = EEPROM_DEFAULT_CRYSTALCAP; tempval[0] = tempval[1] = 3; } /* Use default value to fill parameters if * efuse is not filled on some place. */ /* ThermalMeter from EEPROM */ if (rtlefuse->eeprom_thermalmeter < 0x06 || rtlefuse->eeprom_thermalmeter > 0x1c) rtlefuse->eeprom_thermalmeter = 0x12; rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter; /* check XTAL_K */ if (rtlefuse->crystalcap == 0xFF) rtlefuse->crystalcap = 0; if (rtlefuse->eeprom_regulatory > 3) rtlefuse->eeprom_regulatory = 0; for (i = 0; i < 2; i++) { switch (tempval[i]) { case 0: tempval[i] = 5; break; case 1: tempval[i] = 4; break; case 2: tempval[i] = 3; break; case 3: default: tempval[i] = 0; break; } } rtlefuse->delta_iqk = tempval[0]; if (tempval[1] > 0) rtlefuse->delta_lck = tempval[1] - 1; if (rtlefuse->eeprom_c9 == 0xFF) rtlefuse->eeprom_c9 = 0x00; RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, "EEPROMRegulatory = 0x%x\n", rtlefuse->eeprom_regulatory); RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, "ThermalMeter = 0x%x\n", rtlefuse->eeprom_thermalmeter); RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, "CrystalCap = 0x%x\n", rtlefuse->crystalcap); RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, "Delta_IQK = 0x%x Delta_LCK = 0x%x\n", rtlefuse->delta_iqk, rtlefuse->delta_lck); for (rfPath = 0; rfPath < RF6052_MAX_PATH; rfPath++) { for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++) { group = rtl92d_get_chnlgroup_fromarray((u8) ch); if (ch < CHANNEL_MAX_NUMBER_2G) rtlefuse->txpwrlevel_cck[rfPath][ch] = pwrinfo.cck_index[rfPath][group]; rtlefuse->txpwrlevel_ht40_1s[rfPath][ch] = pwrinfo.ht40_1sindex[rfPath][group]; rtlefuse->txpwr_ht20diff[rfPath][ch] = pwrinfo.ht20indexdiff[rfPath][group]; rtlefuse->txpwr_legacyhtdiff[rfPath][ch] = pwrinfo.ofdmindexdiff[rfPath][group]; rtlefuse->pwrgroup_ht20[rfPath][ch] = pwrinfo.ht20maxoffset[rfPath][group]; rtlefuse->pwrgroup_ht40[rfPath][ch] = pwrinfo.ht40maxoffset[rfPath][group]; pwr = pwrinfo.ht40_1sindex[rfPath][group]; diff = pwrinfo.ht40_2sindexdiff[rfPath][group]; rtlefuse->txpwrlevel_ht40_2s[rfPath][ch] = (pwr > diff) ? (pwr - diff) : 0; } } } static void _rtl92de_read_macphymode_from_prom(struct ieee80211_hw *hw, u8 *content) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 macphy_crvalue = content[EEPROM_MAC_FUNCTION]; if (macphy_crvalue & BIT(3)) { rtlhal->macphymode = SINGLEMAC_SINGLEPHY; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "MacPhyMode SINGLEMAC_SINGLEPHY\n"); } else { rtlhal->macphymode = DUALMAC_DUALPHY; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "MacPhyMode DUALMAC_DUALPHY\n"); } } static void _rtl92de_read_macphymode_and_bandtype(struct ieee80211_hw *hw, u8 *content) { _rtl92de_read_macphymode_from_prom(hw, content); rtl92d_phy_config_macphymode(hw); rtl92d_phy_config_macphymode_info(hw); } static void _rtl92de_efuse_update_chip_version(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); enum version_8192d chipver = rtlpriv->rtlhal.version; u8 cutvalue[2]; u16 chipvalue; rtlpriv->intf_ops->read_efuse_byte(hw, EEPROME_CHIP_VERSION_H, &cutvalue[1]); rtlpriv->intf_ops->read_efuse_byte(hw, EEPROME_CHIP_VERSION_L, &cutvalue[0]); chipvalue = (cutvalue[1] << 8) | cutvalue[0]; switch (chipvalue) { case 0xAA55: chipver |= CHIP_92D_C_CUT; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "C-CUT!!!\n"); break; case 0x9966: chipver |= CHIP_92D_D_CUT; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "D-CUT!!!\n"); break; case 0xCC33: chipver |= CHIP_92D_E_CUT; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "E-CUT!!!\n"); break; default: chipver |= CHIP_92D_D_CUT; RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Unknown CUT!\n"); break; } rtlpriv->rtlhal.version = chipver; } static void _rtl92de_read_adapter_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u16 i, usvalue; u8 hwinfo[HWSET_MAX_SIZE]; u16 eeprom_id; unsigned long flags; if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) { spin_lock_irqsave(&globalmutex_for_power_and_efuse, flags); rtl_efuse_shadow_map_update(hw); _rtl92de_efuse_update_chip_version(hw); spin_unlock_irqrestore(&globalmutex_for_power_and_efuse, flags); memcpy((void *)hwinfo, (void *)&rtlefuse->efuse_map [EFUSE_INIT_MAP][0], HWSET_MAX_SIZE); } else if (rtlefuse->epromtype == EEPROM_93C46) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "RTL819X Not boot from eeprom, check it !!\n"); } RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, "MAP", hwinfo, HWSET_MAX_SIZE); eeprom_id = *((u16 *)&hwinfo[0]); if (eeprom_id != RTL8190_EEPROM_ID) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "EEPROM ID(%#x) is invalid!!\n", eeprom_id); rtlefuse->autoload_failflag = true; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); rtlefuse->autoload_failflag = false; } if (rtlefuse->autoload_failflag) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "RTL819X Not boot from eeprom, check it !!\n"); return; } rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID]; _rtl92de_read_macphymode_and_bandtype(hw, hwinfo); /* VID, DID SE 0xA-D */ rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID]; rtlefuse->eeprom_did = *(u16 *)&hwinfo[EEPROM_DID]; rtlefuse->eeprom_svid = *(u16 *)&hwinfo[EEPROM_SVID]; rtlefuse->eeprom_smid = *(u16 *)&hwinfo[EEPROM_SMID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROMId = 0x%4x\n", eeprom_id); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid); /* Read Permanent MAC address */ if (rtlhal->interfaceindex == 0) { for (i = 0; i < 6; i += 2) { usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR_MAC0_92D + i]; *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue; } } else { for (i = 0; i < 6; i += 2) { usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR_MAC1_92D + i]; *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue; } } rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, rtlefuse->dev_addr); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "%pM\n", rtlefuse->dev_addr); _rtl92de_read_txpower_info(hw, rtlefuse->autoload_failflag, hwinfo); /* Read Channel Plan */ switch (rtlhal->bandset) { case BAND_ON_2_4G: rtlefuse->channel_plan = COUNTRY_CODE_TELEC; break; case BAND_ON_5G: rtlefuse->channel_plan = COUNTRY_CODE_FCC; break; case BAND_ON_BOTH: rtlefuse->channel_plan = COUNTRY_CODE_FCC; break; default: rtlefuse->channel_plan = COUNTRY_CODE_FCC; break; } rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION]; rtlefuse->txpwr_fromeprom = true; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); } void rtl92de_read_eeprom_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 tmp_u1b; rtlhal->version = _rtl92de_read_chip_version(hw); tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR); rtlefuse->autoload_status = tmp_u1b; if (tmp_u1b & BIT(4)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EEPROM\n"); rtlefuse->epromtype = EEPROM_93C46; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EFUSE\n"); rtlefuse->epromtype = EEPROM_BOOT_EFUSE; } if (tmp_u1b & BIT(5)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); rtlefuse->autoload_failflag = false; _rtl92de_read_adapter_info(hw); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Autoload ERR!!\n"); } return; } static void rtl92de_update_hal_rate_table(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u32 ratr_value; u8 ratr_index = 0; u8 nmode = mac->ht_enable; u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate; u32 tmp_ratr_value; u8 curtxbw_40mhz = mac->bw_40; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = mac->mode; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_value = sta->supp_rates[1] << 4; else ratr_value = sta->supp_rates[0]; ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 | sta->ht_cap.mcs.rx_mask[0] << 12); switch (wirelessmode) { case WIRELESS_MODE_A: ratr_value &= 0x00000FF0; break; case WIRELESS_MODE_B: if (ratr_value & 0x0000000c) ratr_value &= 0x0000000d; else ratr_value &= 0x0000000f; break; case WIRELESS_MODE_G: ratr_value &= 0x00000FF5; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: nmode = 1; if (mimo_ps == IEEE80211_SMPS_STATIC) { ratr_value &= 0x0007F005; } else { u32 ratr_mask; if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_1T1R) { ratr_mask = 0x000ff005; } else { ratr_mask = 0x0f0ff005; } ratr_value &= ratr_mask; } break; default: if (rtlphy->rf_type == RF_1T2R) ratr_value &= 0x000ff0ff; else ratr_value &= 0x0f0ff0ff; break; } ratr_value &= 0x0FFFFFFF; if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz))) { ratr_value |= 0x10000000; tmp_ratr_value = (ratr_value >> 12); for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) { if ((1 << shortgi_rate) & tmp_ratr_value) break; } shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) | (shortgi_rate << 4) | (shortgi_rate); } rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value); RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0)); } static void rtl92de_update_hal_rate_mask(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index; u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = 0; bool shortgi = false; u32 value[2]; u8 macid = 0; u8 mimo_ps = IEEE80211_SMPS_OFF; sta_entry = (struct rtl_sta_info *) sta->drv_priv; mimo_ps = sta_entry->mimo_ps; wirelessmode = sta_entry->wireless_mode; if (mac->opmode == NL80211_IFTYPE_STATION) curtxbw_40mhz = mac->bw_40; else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) macid = sta->aid + 1; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_bitmap = sta->supp_rates[1] << 4; else ratr_bitmap = sta->supp_rates[0]; ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 | sta->ht_cap.mcs.rx_mask[0] << 12); switch (wirelessmode) { case WIRELESS_MODE_B: ratr_index = RATR_INX_WIRELESS_B; if (ratr_bitmap & 0x0000000c) ratr_bitmap &= 0x0000000d; else ratr_bitmap &= 0x0000000f; break; case WIRELESS_MODE_G: ratr_index = RATR_INX_WIRELESS_GB; if (rssi_level == 1) ratr_bitmap &= 0x00000f00; else if (rssi_level == 2) ratr_bitmap &= 0x00000ff0; else ratr_bitmap &= 0x00000ff5; break; case WIRELESS_MODE_A: ratr_index = RATR_INX_WIRELESS_G; ratr_bitmap &= 0x00000ff0; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: if (wirelessmode == WIRELESS_MODE_N_24G) ratr_index = RATR_INX_WIRELESS_NGB; else ratr_index = RATR_INX_WIRELESS_NG; if (mimo_ps == IEEE80211_SMPS_STATIC) { if (rssi_level == 1) ratr_bitmap &= 0x00070000; else if (rssi_level == 2) ratr_bitmap &= 0x0007f000; else ratr_bitmap &= 0x0007f005; } else { if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_1T1R) { if (curtxbw_40mhz) { if (rssi_level == 1) ratr_bitmap &= 0x000f0000; else if (rssi_level == 2) ratr_bitmap &= 0x000ff000; else ratr_bitmap &= 0x000ff015; } else { if (rssi_level == 1) ratr_bitmap &= 0x000f0000; else if (rssi_level == 2) ratr_bitmap &= 0x000ff000; else ratr_bitmap &= 0x000ff005; } } else { if (curtxbw_40mhz) { if (rssi_level == 1) ratr_bitmap &= 0x0f0f0000; else if (rssi_level == 2) ratr_bitmap &= 0x0f0ff000; else ratr_bitmap &= 0x0f0ff015; } else { if (rssi_level == 1) ratr_bitmap &= 0x0f0f0000; else if (rssi_level == 2) ratr_bitmap &= 0x0f0ff000; else ratr_bitmap &= 0x0f0ff005; } } } if ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz)) { if (macid == 0) shortgi = true; else if (macid == 1) shortgi = false; } break; default: ratr_index = RATR_INX_WIRELESS_NGB; if (rtlphy->rf_type == RF_1T2R) ratr_bitmap &= 0x000ff0ff; else ratr_bitmap &= 0x0f0ff0ff; break; } value[0] = (ratr_bitmap & 0x0fffffff) | (ratr_index << 28); value[1] = macid | (shortgi ? 0x20 : 0x00) | 0x80; RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "ratr_bitmap :%x value0:%x value1:%x\n", ratr_bitmap, value[0], value[1]); rtl92d_fill_h2c_cmd(hw, H2C_RA_MASK, 5, (u8 *) value); if (macid != 0) sta_entry->ratr_index = ratr_index; } void rtl92de_update_hal_rate_tbl(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->dm.useramask) rtl92de_update_hal_rate_mask(hw, sta, rssi_level); else rtl92de_update_hal_rate_table(hw, sta); } void rtl92de_update_channel_access_setting(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else sifs_timer = 0x1010; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer); } bool rtl92de_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); enum rf_pwrstate e_rfpowerstate_toset; u8 u1tmp; bool actuallyset = false; unsigned long flag; if (rtlpci->being_init_adapter) return false; if (ppsc->swrf_processing) return false; spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); if (ppsc->rfchange_inprogress) { spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); return false; } else { ppsc->rfchange_inprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG) & ~(BIT(3))); u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL); e_rfpowerstate_toset = (u1tmp & BIT(3)) ? ERFON : ERFOFF; if (ppsc->hwradiooff && (e_rfpowerstate_toset == ERFON)) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "GPIOChangeRF - HW Radio ON, RF ON\n"); e_rfpowerstate_toset = ERFON; ppsc->hwradiooff = false; actuallyset = true; } else if (!ppsc->hwradiooff && (e_rfpowerstate_toset == ERFOFF)) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "GPIOChangeRF - HW Radio OFF, RF OFF\n"); e_rfpowerstate_toset = ERFOFF; ppsc->hwradiooff = true; actuallyset = true; } if (actuallyset) { spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } else { if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } *valid = 1; return !ppsc->hwradiooff; } void rtl92de_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 *macaddr = p_macaddr; u32 entry_id; bool is_pairwise = false; static u8 cam_const_addr[4][6] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} }; static u8 cam_const_broad[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (clear_all) { u8 idx; u8 cam_offset = 0; u8 clear_number = 5; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n"); for (idx = 0; idx < clear_number; idx++) { rtl_cam_mark_invalid(hw, cam_offset + idx); rtl_cam_empty_entry(hw, cam_offset + idx); if (idx < 5) { memset(rtlpriv->sec.key_buf[idx], 0, MAX_KEY_LEN); rtlpriv->sec.key_len[idx] = 0; } } } else { switch (enc_algo) { case WEP40_ENCRYPTION: enc_algo = CAM_WEP40; break; case WEP104_ENCRYPTION: enc_algo = CAM_WEP104; break; case TKIP_ENCRYPTION: enc_algo = CAM_TKIP; break; case AESCCMP_ENCRYPTION: enc_algo = CAM_AES; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); enc_algo = CAM_TKIP; break; } if (is_wepkey || rtlpriv->sec.use_defaultkey) { macaddr = cam_const_addr[key_index]; entry_id = key_index; } else { if (is_group) { macaddr = cam_const_broad; entry_id = key_index; } else { if (mac->opmode == NL80211_IFTYPE_AP) { entry_id = rtl_cam_get_free_entry(hw, p_macaddr); if (entry_id >= TOTAL_CAM_ENTRY) { RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "Can not find free hw security cam entry\n"); return; } } else { entry_id = CAM_PAIRWISE_KEY_POSITION; } key_index = PAIRWISE_KEYIDX; is_pairwise = true; } } if (rtlpriv->sec.key_len[key_index] == 0) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "delete one entry, entry_id is %d\n", entry_id); if (mac->opmode == NL80211_IFTYPE_AP) rtl_cam_del_entry(hw, p_macaddr); rtl_cam_delete_one_entry(hw, p_macaddr, entry_id); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY length is %d\n", rtlpriv->sec.key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY is %x %x\n", rtlpriv->sec.key_buf[0][0], rtlpriv->sec.key_buf[0][1]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "add one entry\n"); if (is_pairwise) { RT_PRINT_DATA(rtlpriv, COMP_SEC, DBG_LOUD, "Pairwise Key content", rtlpriv->sec.pairwise_key, rtlpriv-> sec.key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set Pairwise key\n"); rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv-> sec.key_buf[key_index]); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set group key\n"); if (mac->opmode == NL80211_IFTYPE_ADHOC) { rtl_cam_add_one_entry(hw, rtlefuse->dev_addr, PAIRWISE_KEYIDX, CAM_PAIRWISE_KEY_POSITION, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf[entry_id]); } rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf [entry_id]); } } } } void rtl92de_suspend(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->rtlhal.macphyctl_reg = rtl_read_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL); } void rtl92de_resume(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL, rtlpriv->rtlhal.macphyctl_reg); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/fw.h0000644000175000017500000001233712026211315024372 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92D__FW__H__ #define __RTL92D__FW__H__ #define FW_8192D_START_ADDRESS 0x1000 #define FW_8192D_PAGE_SIZE 4096 #define FW_8192D_POLLING_TIMEOUT_COUNT 1000 #define IS_FW_HEADER_EXIST(_pfwhdr) \ ((GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFF0) == 0x92C0 || \ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFF0) == 0x88C0 || \ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D0 || \ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D1 || \ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D2 || \ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D3) /* Define a macro that takes an le32 word, converts it to host ordering, * right shifts by a specified count, creates a mask of the specified * bit count, and extracts that number of bits. */ #define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \ ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \ BIT_LEN_MASK_32(__mask)) /* Firmware Header(8-byte alinment required) */ /* --- LONG WORD 0 ---- */ #define GET_FIRMWARE_HDR_SIGNATURE(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr, 0, 16) #define GET_FIRMWARE_HDR_CATEGORY(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr, 16, 8) #define GET_FIRMWARE_HDR_FUNCTION(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr, 24, 8) #define GET_FIRMWARE_HDR_VERSION(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 4, 0, 16) #define GET_FIRMWARE_HDR_SUB_VER(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 4, 16, 8) #define GET_FIRMWARE_HDR_RSVD1(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 4, 24, 8) /* --- LONG WORD 1 ---- */ #define GET_FIRMWARE_HDR_MONTH(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 8, 0, 8) #define GET_FIRMWARE_HDR_DATE(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 8, 8, 8) #define GET_FIRMWARE_HDR_HOUR(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 8, 16, 8) #define GET_FIRMWARE_HDR_MINUTE(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 8, 24, 8) #define GET_FIRMWARE_HDR_ROMCODE_SIZE(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 12, 0, 16) #define GET_FIRMWARE_HDR_RSVD2(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 12, 16, 16) /* --- LONG WORD 2 ---- */ #define GET_FIRMWARE_HDR_SVN_IDX(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 16, 0, 32) #define GET_FIRMWARE_HDR_RSVD3(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 20, 0, 32) /* --- LONG WORD 3 ---- */ #define GET_FIRMWARE_HDR_RSVD4(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 24, 0, 32) #define GET_FIRMWARE_HDR_RSVD5(__fwhdr) \ SHIFT_AND_MASK_LE(__fwhdr + 28, 0, 32) #define pagenum_128(_len) \ (u32)(((_len) >> 7) + ((_len) & 0x7F ? 1 : 0)) #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 1, 0, 8, __val) #define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 2, 0, 8, __val) #define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 1, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 2, 0, 8, __val) struct rtl92d_firmware_header { u16 signature; u8 category; u8 function; u16 version; u8 subversion; u8 rsvd1; u8 month; u8 date; u8 hour; u8 minute; u16 ramcodeSize; u16 rsvd2; u32 svnindex; u32 rsvd3; u32 rsvd4; u32 rsvd5; }; enum rtl8192d_h2c_cmd { H2C_AP_OFFLOAD = 0, H2C_SETPWRMODE = 1, H2C_JOINBSSRPT = 2, H2C_RSVDPAGE = 3, H2C_RSSI_REPORT = 5, H2C_RA_MASK = 6, H2C_MAC_MODE_SEL = 9, H2C_PWRM = 15, MAX_H2CCMD }; int rtl92d_download_fw(struct ieee80211_hw *hw); void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); void rtl92d_firmware_selfreset(struct ieee80211_hw *hw); void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); void rtl92d_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished); void rtl92d_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/fw.c0000644000175000017500000006201412026211315024362 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../base.h" #include "reg.h" #include "def.h" #include "fw.h" #include "sw.h" static bool _rtl92d_is_fw_downloaded(struct rtl_priv *rtlpriv) { return (rtl_read_dword(rtlpriv, REG_MCUFWDL) & MCUFWDL_RDY) ? true : false; } static void _rtl92d_enable_fw_download(struct ieee80211_hw *hw, bool enable) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp; if (enable) { tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04); tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01); tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2); rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7); } else { tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe); /* Reserved for fw extension. * 0x81[7] is used for mac0 status , * so don't write this reg here * rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);*/ } } static void _rtl92d_fw_block_write(struct ieee80211_hw *hw, const u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 blocksize = sizeof(u32); u8 *bufferptr = (u8 *) buffer; u32 *pu4BytePtr = (u32 *) buffer; u32 i, offset, blockCount, remainSize; blockCount = size / blocksize; remainSize = size % blocksize; for (i = 0; i < blockCount; i++) { offset = i * blocksize; rtl_write_dword(rtlpriv, (FW_8192D_START_ADDRESS + offset), *(pu4BytePtr + i)); } if (remainSize) { offset = blockCount * blocksize; bufferptr += offset; for (i = 0; i < remainSize; i++) { rtl_write_byte(rtlpriv, (FW_8192D_START_ADDRESS + offset + i), *(bufferptr + i)); } } } static void _rtl92d_fw_page_write(struct ieee80211_hw *hw, u32 page, const u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 value8; u8 u8page = (u8) (page & 0x07); value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page; rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8); _rtl92d_fw_block_write(hw, buffer, size); } static void _rtl92d_fill_dummy(u8 *pfwbuf, u32 *pfwlen) { u32 fwlen = *pfwlen; u8 remain = (u8) (fwlen % 4); remain = (remain == 0) ? 0 : (4 - remain); while (remain > 0) { pfwbuf[fwlen] = 0; fwlen++; remain--; } *pfwlen = fwlen; } static void _rtl92d_write_fw(struct ieee80211_hw *hw, enum version_8192d version, u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 *bufferPtr = buffer; u32 pagenums, remainSize; u32 page, offset; RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size); if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DE) _rtl92d_fill_dummy(bufferPtr, &size); pagenums = size / FW_8192D_PAGE_SIZE; remainSize = size % FW_8192D_PAGE_SIZE; if (pagenums > 8) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Page numbers should not greater then 8\n"); } for (page = 0; page < pagenums; page++) { offset = page * FW_8192D_PAGE_SIZE; _rtl92d_fw_page_write(hw, page, (bufferPtr + offset), FW_8192D_PAGE_SIZE); } if (remainSize) { offset = pagenums * FW_8192D_PAGE_SIZE; page = pagenums; _rtl92d_fw_page_write(hw, page, (bufferPtr + offset), remainSize); } } static int _rtl92d_fw_free_to_go(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 counter = 0; u32 value32; do { value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); } while ((counter++ < FW_8192D_POLLING_TIMEOUT_COUNT) && (!(value32 & FWDL_ChkSum_rpt))); if (counter >= FW_8192D_POLLING_TIMEOUT_COUNT) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "chksum report faill ! REG_MCUFWDL:0x%08x\n", value32); return -EIO; } RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "Checksum report OK ! REG_MCUFWDL:0x%08x\n", value32); value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); value32 |= MCUFWDL_RDY; rtl_write_dword(rtlpriv, REG_MCUFWDL, value32); return 0; } void rtl92d_firmware_selfreset(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 u1b_tmp; u8 delay = 100; /* Set (REG_HMETFR + 3) to 0x20 is reset 8051 */ rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20); u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); while (u1b_tmp & BIT(2)) { delay--; if (delay == 0) break; udelay(50); u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); } RT_ASSERT((delay > 0), "8051 reset failed!\n"); RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "=====> 8051 reset success (%d)\n", delay); } static int _rtl92d_fw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u32 counter; RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "FW already have download\n"); /* polling for FW ready */ counter = 0; do { if (rtlhal->interfaceindex == 0) { if (rtl_read_byte(rtlpriv, FW_MAC0_READY) & MAC0_READY) { RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "Polling FW ready success!! REG_MCUFWDL: 0x%x\n", rtl_read_byte(rtlpriv, FW_MAC0_READY)); return 0; } udelay(5); } else { if (rtl_read_byte(rtlpriv, FW_MAC1_READY) & MAC1_READY) { RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "Polling FW ready success!! REG_MCUFWDL: 0x%x\n", rtl_read_byte(rtlpriv, FW_MAC1_READY)); return 0; } udelay(5); } } while (counter++ < POLLING_READY_TIMEOUT_COUNT); if (rtlhal->interfaceindex == 0) { RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "Polling FW ready fail!! MAC0 FW init not ready: 0x%x\n", rtl_read_byte(rtlpriv, FW_MAC0_READY)); } else { RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "Polling FW ready fail!! MAC1 FW init not ready: 0x%x\n", rtl_read_byte(rtlpriv, FW_MAC1_READY)); } RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "Polling FW ready fail!! REG_MCUFWDL:0x%08ul\n", rtl_read_dword(rtlpriv, REG_MCUFWDL)); return -1; } int rtl92d_download_fw(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 *pfwheader; u8 *pfwdata; u32 fwsize; int err; enum version_8192d version = rtlhal->version; u8 value; u32 count; bool fw_downloaded = false, fwdl_in_process = false; unsigned long flags; if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware) return 1; fwsize = rtlhal->fwsize; pfwheader = rtlhal->pfirmware; pfwdata = rtlhal->pfirmware; rtlhal->fw_version = (u16) GET_FIRMWARE_HDR_VERSION(pfwheader); rtlhal->fw_subversion = (u16) GET_FIRMWARE_HDR_SUB_VER(pfwheader); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "FirmwareVersion(%d), FirmwareSubVersion(%d), Signature(%#x)\n", rtlhal->fw_version, rtlhal->fw_subversion, GET_FIRMWARE_HDR_SIGNATURE(pfwheader)); if (IS_FW_HEADER_EXIST(pfwheader)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Shift 32 bytes for FW header!!\n"); pfwdata = pfwdata + 32; fwsize = fwsize - 32; } spin_lock_irqsave(&globalmutex_for_fwdownload, flags); fw_downloaded = _rtl92d_is_fw_downloaded(rtlpriv); if ((rtl_read_byte(rtlpriv, 0x1f) & BIT(5)) == BIT(5)) fwdl_in_process = true; else fwdl_in_process = false; if (fw_downloaded) { spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags); goto exit; } else if (fwdl_in_process) { spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags); for (count = 0; count < 5000; count++) { udelay(500); spin_lock_irqsave(&globalmutex_for_fwdownload, flags); fw_downloaded = _rtl92d_is_fw_downloaded(rtlpriv); if ((rtl_read_byte(rtlpriv, 0x1f) & BIT(5)) == BIT(5)) fwdl_in_process = true; else fwdl_in_process = false; spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags); if (fw_downloaded) goto exit; else if (!fwdl_in_process) break; else RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "Wait for another mac download fw\n"); } spin_lock_irqsave(&globalmutex_for_fwdownload, flags); value = rtl_read_byte(rtlpriv, 0x1f); value |= BIT(5); rtl_write_byte(rtlpriv, 0x1f, value); spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags); } else { value = rtl_read_byte(rtlpriv, 0x1f); value |= BIT(5); rtl_write_byte(rtlpriv, 0x1f, value); spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags); } /* If 8051 is running in RAM code, driver should * inform Fw to reset by itself, or it will cause * download Fw fail.*/ /* 8051 RAM code */ if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) { rtl92d_firmware_selfreset(hw); rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); } _rtl92d_enable_fw_download(hw, true); _rtl92d_write_fw(hw, version, pfwdata, fwsize); _rtl92d_enable_fw_download(hw, false); spin_lock_irqsave(&globalmutex_for_fwdownload, flags); err = _rtl92d_fw_free_to_go(hw); /* download fw over,clear 0x1f[5] */ value = rtl_read_byte(rtlpriv, 0x1f); value &= (~BIT(5)); rtl_write_byte(rtlpriv, 0x1f, value); spin_unlock_irqrestore(&globalmutex_for_fwdownload, flags); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "fw is not ready to run!\n"); goto exit; } else { RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "fw is ready to run!\n"); } exit: err = _rtl92d_fw_init(hw); return err; } static bool _rtl92d_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 val_hmetfr; bool result = false; val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR); if (((val_hmetfr >> boxnum) & BIT(0)) == 0) result = true; return result; } static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *cmdbuffer) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 boxnum; u16 box_reg = 0, box_extreg = 0; u8 u1b_tmp; bool isfw_read = false; u8 buf_index = 0; bool bwrite_success = false; u8 wait_h2c_limmit = 100; u8 wait_writeh2c_limmit = 100; u8 boxcontent[4], boxextcontent[2]; u32 h2c_waitcounter = 0; unsigned long flag; u8 idx; if (ppsc->rfpwr_state == ERFOFF || ppsc->inactive_pwrstate == ERFOFF) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Return as RF is off!!!\n"); return; } RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n"); while (true) { spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); if (rtlhal->h2c_setinprogress) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "H2C set in progress! Wait to set..element_id(%d)\n", element_id); while (rtlhal->h2c_setinprogress) { spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); h2c_waitcounter++; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Wait 100 us (%d times)...\n", h2c_waitcounter); udelay(100); if (h2c_waitcounter > 1000) return; spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); } spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); } else { rtlhal->h2c_setinprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); break; } } while (!bwrite_success) { wait_writeh2c_limmit--; if (wait_writeh2c_limmit == 0) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write H2C fail because no trigger for FW INT!\n"); break; } boxnum = rtlhal->last_hmeboxnum; switch (boxnum) { case 0: box_reg = REG_HMEBOX_0; box_extreg = REG_HMEBOX_EXT_0; break; case 1: box_reg = REG_HMEBOX_1; box_extreg = REG_HMEBOX_EXT_1; break; case 2: box_reg = REG_HMEBOX_2; box_extreg = REG_HMEBOX_EXT_2; break; case 3: box_reg = REG_HMEBOX_3; box_extreg = REG_HMEBOX_EXT_3; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } isfw_read = _rtl92d_check_fw_read_last_h2c(hw, boxnum); while (!isfw_read) { wait_h2c_limmit--; if (wait_h2c_limmit == 0) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Waiting too long for FW read clear HMEBox(%d)!\n", boxnum); break; } udelay(10); isfw_read = _rtl92d_check_fw_read_last_h2c(hw, boxnum); u1b_tmp = rtl_read_byte(rtlpriv, 0x1BF); RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Waiting for FW read clear HMEBox(%d)!!! 0x1BF = %2x\n", boxnum, u1b_tmp); } if (!isfw_read) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Write H2C register BOX[%d] fail!!!!! Fw do not read.\n", boxnum); break; } memset(boxcontent, 0, sizeof(boxcontent)); memset(boxextcontent, 0, sizeof(boxextcontent)); boxcontent[0] = element_id; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Write element_id box_reg(%4x) = %2x\n", box_reg, element_id); switch (cmd_len) { case 1: boxcontent[0] &= ~(BIT(7)); memcpy(boxcontent + 1, cmdbuffer + buf_index, 1); for (idx = 0; idx < 4; idx++) rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); break; case 2: boxcontent[0] &= ~(BIT(7)); memcpy(boxcontent + 1, cmdbuffer + buf_index, 2); for (idx = 0; idx < 4; idx++) rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); break; case 3: boxcontent[0] &= ~(BIT(7)); memcpy(boxcontent + 1, cmdbuffer + buf_index, 3); for (idx = 0; idx < 4; idx++) rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); break; case 4: boxcontent[0] |= (BIT(7)); memcpy(boxextcontent, cmdbuffer + buf_index, 2); memcpy(boxcontent + 1, cmdbuffer + buf_index + 2, 2); for (idx = 0; idx < 2; idx++) rtl_write_byte(rtlpriv, box_extreg + idx, boxextcontent[idx]); for (idx = 0; idx < 4; idx++) rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); break; case 5: boxcontent[0] |= (BIT(7)); memcpy(boxextcontent, cmdbuffer + buf_index, 2); memcpy(boxcontent + 1, cmdbuffer + buf_index + 2, 3); for (idx = 0; idx < 2; idx++) rtl_write_byte(rtlpriv, box_extreg + idx, boxextcontent[idx]); for (idx = 0; idx < 4; idx++) rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } bwrite_success = true; rtlhal->last_hmeboxnum = boxnum + 1; if (rtlhal->last_hmeboxnum == 4) rtlhal->last_hmeboxnum = 0; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "pHalData->last_hmeboxnum = %d\n", rtlhal->last_hmeboxnum); } spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); rtlhal->h2c_setinprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n"); } void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *cmdbuffer) { u32 tmp_cmdbuf[2]; memset(tmp_cmdbuf, 0, 8); memcpy(tmp_cmdbuf, cmdbuffer, cmd_len); _rtl92d_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf); return; } void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 u1_h2c_set_pwrmode[3] = { 0 }; struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode); SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1); SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode, ppsc->reg_max_lps_awakeintvl); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "rtl92d_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode", u1_h2c_set_pwrmode, 3); rtl92d_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); } static bool _rtl92d_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl8192_tx_ring *ring; struct rtl_tx_desc *pdesc; u8 idx = 0; unsigned long flags; struct sk_buff *pskb; ring = &rtlpci->tx_ring[BEACON_QUEUE]; pskb = __skb_dequeue(&ring->queue); kfree_skb(pskb); spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); pdesc = &ring->desc[idx]; /* discard output from call below */ rtlpriv->cfg->ops->get_desc((u8 *) pdesc, true, HW_DESC_OWN); rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *) pdesc, 1, 1, skb); __skb_queue_tail(&ring->queue, skb); spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE); return true; } #define BEACON_PG 0 /*->1 */ #define PSPOLL_PG 2 #define NULL_PG 3 #define PROBERSP_PG 4 /*->5 */ #define TOTAL_RESERVED_PKT_LEN 768 static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { /* page 0 beacon */ 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x50, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69, 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C, 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96, 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C, 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 1 beacon */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 2 ps-poll */ 0xA4, 0x10, 0x01, 0xC0, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 3 null */ 0x48, 0x01, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 4 probe_resp */ 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, 0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00, 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69, 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C, 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96, 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C, 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 5 probe_resp */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; void rtl92d_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct sk_buff *skb = NULL; u32 totalpacketlen; bool rtstatus; u8 u1RsvdPageLoc[3] = { 0 }; bool dlok = false; u8 *beacon; u8 *p_pspoll; u8 *nullfunc; u8 *p_probersp; /*--------------------------------------------------------- (1) beacon ---------------------------------------------------------*/ beacon = &reserved_page_packet[BEACON_PG * 128]; SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr); SET_80211_HDR_ADDRESS3(beacon, mac->bssid); /*------------------------------------------------------- (2) ps-poll --------------------------------------------------------*/ p_pspoll = &reserved_page_packet[PSPOLL_PG * 128]; SET_80211_PS_POLL_AID(p_pspoll, (mac->assoc_id | 0xc000)); SET_80211_PS_POLL_BSSID(p_pspoll, mac->bssid); SET_80211_PS_POLL_TA(p_pspoll, mac->mac_addr); SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1RsvdPageLoc, PSPOLL_PG); /*-------------------------------------------------------- (3) null data ---------------------------------------------------------*/ nullfunc = &reserved_page_packet[NULL_PG * 128]; SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid); SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr); SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid); SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1RsvdPageLoc, NULL_PG); /*--------------------------------------------------------- (4) probe response ----------------------------------------------------------*/ p_probersp = &reserved_page_packet[PROBERSP_PG * 128]; SET_80211_HDR_ADDRESS1(p_probersp, mac->bssid); SET_80211_HDR_ADDRESS2(p_probersp, mac->mac_addr); SET_80211_HDR_ADDRESS3(p_probersp, mac->bssid); SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1RsvdPageLoc, PROBERSP_PG); totalpacketlen = TOTAL_RESERVED_PKT_LEN; RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, "rtl92d_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL", &reserved_page_packet[0], totalpacketlen); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "rtl92d_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL", u1RsvdPageLoc, 3); skb = dev_alloc_skb(totalpacketlen); if (!skb) { dlok = false; } else { memcpy((u8 *) skb_put(skb, totalpacketlen), &reserved_page_packet, totalpacketlen); rtstatus = _rtl92d_cmd_send_packet(hw, skb); if (rtstatus) dlok = true; } if (dlok) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Set RSVD page location to Fw\n"); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "H2C_RSVDPAGE", u1RsvdPageLoc, 3); rtl92d_fill_h2c_cmd(hw, H2C_RSVDPAGE, sizeof(u1RsvdPageLoc), u1RsvdPageLoc); } else RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Set RSVD page location to Fw FAIL!!!!!!\n"); } void rtl92d_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus) { u8 u1_joinbssrpt_parm[1] = {0}; SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus); rtl92d_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/dm.h0000644000175000017500000000773712026211315024366 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_DM_H__ #define __RTL92C_DM_H__ #define HAL_DM_DIG_DISABLE BIT(0) #define HAL_DM_HIPWR_DISABLE BIT(1) #define OFDM_TABLE_LENGTH 37 #define OFDM_TABLE_SIZE_92D 43 #define CCK_TABLE_LENGTH 33 #define CCK_TABLE_SIZE 33 #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 #define DM_DIG_THRESH_HIGH 40 #define DM_DIG_THRESH_LOW 35 #define DM_FALSEALARM_THRESH_LOW 400 #define DM_FALSEALARM_THRESH_HIGH 1000 #define DM_DIG_MAX 0x3e #define DM_DIG_MIN 0x1c #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x100 #define DM_DIG_FA_TH1 0x400 #define DM_DIG_FA_TH2 0x600 #define DM_DIG_BACKOFF_MAX 12 #define DM_DIG_BACKOFF_MIN -4 #define DM_DIG_BACKOFF_DEFAULT 10 #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 #define DM_RATR_STA_INIT 0 #define DM_RATR_STA_HIGH 1 #define DM_RATR_STA_MIDDLE 2 #define DM_RATR_STA_LOW 3 #define CTS2SELF_THVAL 30 #define REGC38_TH 20 #define WAIOTTHVAL 25 #define TXHIGHPWRLEVEL_NORMAL 0 #define TXHIGHPWRLEVEL_LEVEL1 1 #define TXHIGHPWRLEVEL_LEVEL2 2 #define TXHIGHPWRLEVEL_BT1 3 #define TXHIGHPWRLEVEL_BT2 4 #define DM_TYPE_BYFW 0 #define DM_TYPE_BYDRIVER 1 #define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 #define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 #define INDEX_MAPPING_NUM 13 struct swat { u8 failure_cnt; u8 try_flag; u8 stop_trying; long pre_rssi; long trying_threshold; u8 cur_antenna; u8 pre_antenna; }; enum tag_dynamic_init_gain_operation_type_definition { DIG_TYPE_THRESH_HIGH = 0, DIG_TYPE_THRESH_LOW = 1, DIG_TYPE_BACKOFF = 2, DIG_TYPE_RX_GAIN_MIN = 3, DIG_TYPE_RX_GAIN_MAX = 4, DIG_TYPE_ENABLE = 5, DIG_TYPE_DISABLE = 6, DIG_OP_TYPE_MAX }; enum tag_cck_packet_detection_threshold_type_definition { CCK_PD_STAGE_LOWRSSI = 0, CCK_PD_STAGE_HIGHRSSI = 1, CCK_FA_STAGE_LOW = 2, CCK_FA_STAGE_HIGH = 3, CCK_PD_STAGE_MAX = 4, }; enum dm_1r_cca { CCA_1R = 0, CCA_2R = 1, CCA_MAX = 2, }; enum dm_rf { RF_SAVE = 0, RF_NORMAL = 1, RF_MAX = 2, }; enum dm_sw_ant_switch { ANS_ANTENNA_B = 1, ANS_ANTENNA_A = 2, ANS_ANTENNA_MAX = 3, }; enum dm_dig_ext_port_alg { DIG_EXT_PORT_STAGE_0 = 0, DIG_EXT_PORT_STAGE_1 = 1, DIG_EXT_PORT_STAGE_2 = 2, DIG_EXT_PORT_STAGE_3 = 3, DIG_EXT_PORT_STAGE_MAX = 4, }; enum dm_dig_connect { DIG_STA_DISCONNECT = 0, DIG_STA_CONNECT = 1, DIG_STA_BEFORE_CONNECT = 2, DIG_MULTISTA_DISCONNECT = 3, DIG_MULTISTA_CONNECT = 4, DIG_CONNECT_MAX }; void rtl92d_dm_init(struct ieee80211_hw *hw); void rtl92d_dm_watchdog(struct ieee80211_hw *hw); void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw); void rtl92d_dm_write_dig(struct ieee80211_hw *hw); void rtl92d_dm_check_txpower_tracking_thermal_meter(struct ieee80211_hw *hw); void rtl92d_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/dm.c0000644000175000017500000013565212026211315024357 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "fw.h" #define UNDEC_SM_PWDB entry_min_undecoratedsmoothed_pwdb static const u32 ofdmswing_table[OFDM_TABLE_SIZE_92D] = { 0x7f8001fe, /* 0, +6.0dB */ 0x788001e2, /* 1, +5.5dB */ 0x71c001c7, /* 2, +5.0dB */ 0x6b8001ae, /* 3, +4.5dB */ 0x65400195, /* 4, +4.0dB */ 0x5fc0017f, /* 5, +3.5dB */ 0x5a400169, /* 6, +3.0dB */ 0x55400155, /* 7, +2.5dB */ 0x50800142, /* 8, +2.0dB */ 0x4c000130, /* 9, +1.5dB */ 0x47c0011f, /* 10, +1.0dB */ 0x43c0010f, /* 11, +0.5dB */ 0x40000100, /* 12, +0dB */ 0x3c8000f2, /* 13, -0.5dB */ 0x390000e4, /* 14, -1.0dB */ 0x35c000d7, /* 15, -1.5dB */ 0x32c000cb, /* 16, -2.0dB */ 0x300000c0, /* 17, -2.5dB */ 0x2d4000b5, /* 18, -3.0dB */ 0x2ac000ab, /* 19, -3.5dB */ 0x288000a2, /* 20, -4.0dB */ 0x26000098, /* 21, -4.5dB */ 0x24000090, /* 22, -5.0dB */ 0x22000088, /* 23, -5.5dB */ 0x20000080, /* 24, -6.0dB */ 0x1e400079, /* 25, -6.5dB */ 0x1c800072, /* 26, -7.0dB */ 0x1b00006c, /* 27. -7.5dB */ 0x19800066, /* 28, -8.0dB */ 0x18000060, /* 29, -8.5dB */ 0x16c0005b, /* 30, -9.0dB */ 0x15800056, /* 31, -9.5dB */ 0x14400051, /* 32, -10.0dB */ 0x1300004c, /* 33, -10.5dB */ 0x12000048, /* 34, -11.0dB */ 0x11000044, /* 35, -11.5dB */ 0x10000040, /* 36, -12.0dB */ 0x0f00003c, /* 37, -12.5dB */ 0x0e400039, /* 38, -13.0dB */ 0x0d800036, /* 39, -13.5dB */ 0x0cc00033, /* 40, -14.0dB */ 0x0c000030, /* 41, -14.5dB */ 0x0b40002d, /* 42, -15.0dB */ }; static const u8 cckswing_table_ch1ch13[CCK_TABLE_SIZE][8] = { {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, /* 0, +0dB */ {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, /* 1, -0.5dB */ {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, /* 2, -1.0dB */ {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, /* 3, -1.5dB */ {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, /* 4, -2.0dB */ {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, /* 5, -2.5dB */ {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, /* 6, -3.0dB */ {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, /* 7, -3.5dB */ {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, /* 8, -4.0dB */ {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, /* 9, -4.5dB */ {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, /* 10, -5.0dB */ {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, /* 11, -5.5dB */ {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, /* 12, -6.0dB */ {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, /* 13, -6.5dB */ {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, /* 14, -7.0dB */ {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, /* 15, -7.5dB */ {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, /* 16, -8.0dB */ {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, /* 17, -8.5dB */ {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, /* 18, -9.0dB */ {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 19, -9.5dB */ {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 20, -10.0dB */ {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 21, -10.5dB */ {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 22, -11.0dB */ {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, /* 23, -11.5dB */ {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, /* 24, -12.0dB */ {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, /* 25, -12.5dB */ {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, /* 26, -13.0dB */ {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 27, -13.5dB */ {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 28, -14.0dB */ {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 29, -14.5dB */ {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 30, -15.0dB */ {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, /* 31, -15.5dB */ {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01} /* 32, -16.0dB */ }; static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, /* 0, +0dB */ {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, /* 1, -0.5dB */ {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, /* 2, -1.0dB */ {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, /* 3, -1.5dB */ {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, /* 4, -2.0dB */ {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, /* 5, -2.5dB */ {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, /* 6, -3.0dB */ {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, /* 7, -3.5dB */ {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, /* 8, -4.0dB */ {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, /* 9, -4.5dB */ {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, /* 10, -5.0dB */ {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 11, -5.5dB */ {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 12, -6.0dB */ {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, /* 13, -6.5dB */ {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, /* 14, -7.0dB */ {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 15, -7.5dB */ {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 16, -8.0dB */ {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 17, -8.5dB */ {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 18, -9.0dB */ {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 19, -9.5dB */ {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 20, -10.0dB */ {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 21, -10.5dB */ {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 22, -11.0dB */ {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 23, -11.5dB */ {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 24, -12.0dB */ {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 25, -12.5dB */ {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 26, -13.0dB */ {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 27, -13.5dB */ {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 28, -14.0dB */ {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 29, -14.5dB */ {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 30, -15.0dB */ {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 31, -15.5dB */ {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */ }; static void rtl92d_dm_diginit(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *de_digtable = &rtlpriv->dm_digtable; de_digtable->dig_enable_flag = true; de_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; de_digtable->cur_igvalue = 0x20; de_digtable->pre_igvalue = 0x0; de_digtable->cursta_connectstate = DIG_STA_DISCONNECT; de_digtable->presta_connectstate = DIG_STA_DISCONNECT; de_digtable->curmultista_connectstate = DIG_MULTISTA_DISCONNECT; de_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; de_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; de_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; de_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; de_digtable->rx_gain_range_max = DM_DIG_FA_UPPER; de_digtable->rx_gain_range_min = DM_DIG_FA_LOWER; de_digtable->backoff_val = DM_DIG_BACKOFF_DEFAULT; de_digtable->backoff_val_range_max = DM_DIG_BACKOFF_MAX; de_digtable->backoff_val_range_min = DM_DIG_BACKOFF_MIN; de_digtable->pre_cck_pd_state = CCK_PD_STAGE_LOWRSSI; de_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; de_digtable->large_fa_hit = 0; de_digtable->recover_cnt = 0; de_digtable->forbidden_igi = DM_DIG_FA_LOWER; } static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) { u32 ret_value; struct rtl_priv *rtlpriv = rtl_priv(hw); struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); unsigned long flag = 0; /* hold ofdm counter */ rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 1); /* hold page C counter */ rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 1); /*hold page D counter */ ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, BMASKDWORD); falsealm_cnt->cnt_fast_fsync_fail = (ret_value & 0xffff); falsealm_cnt->cnt_sb_search_fail = ((ret_value & 0xffff0000) >> 16); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, BMASKDWORD); falsealm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, BMASKDWORD); falsealm_cnt->cnt_rate_illegal = (ret_value & 0xffff); falsealm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, BMASKDWORD); falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff); falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail + falsealm_cnt->cnt_rate_illegal + falsealm_cnt->cnt_crc8_fail + falsealm_cnt->cnt_mcs_fail + falsealm_cnt->cnt_fast_fsync_fail + falsealm_cnt->cnt_sb_search_fail; if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G) { /* hold cck counter */ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, BMASKBYTE0); falsealm_cnt->cnt_cck_fail = ret_value; ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, BMASKBYTE3); falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8; rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } else { falsealm_cnt->cnt_cck_fail = 0; } /* reset false alarm counter registers */ falsealm_cnt->cnt_all = falsealm_cnt->cnt_fast_fsync_fail + falsealm_cnt->cnt_sb_search_fail + falsealm_cnt->cnt_parity_fail + falsealm_cnt->cnt_rate_illegal + falsealm_cnt->cnt_crc8_fail + falsealm_cnt->cnt_mcs_fail + falsealm_cnt->cnt_cck_fail; rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 1); /* update ofdm counter */ rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 0); /* update page C counter */ rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 0); /* update page D counter */ rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 0); if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G) { /* reset cck counter */ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 0); /* enable cck counter */ rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 2); rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Cnt_Fast_Fsync_fail = %x, Cnt_SB_Search_fail = %x\n", falsealm_cnt->cnt_fast_fsync_fail, falsealm_cnt->cnt_sb_search_fail); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Cnt_Parity_Fail = %x, Cnt_Rate_Illegal = %x, Cnt_Crc8_fail = %x, Cnt_Mcs_fail = %x\n", falsealm_cnt->cnt_parity_fail, falsealm_cnt->cnt_rate_illegal, falsealm_cnt->cnt_crc8_fail, falsealm_cnt->cnt_mcs_fail); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Cnt_Ofdm_fail = %x, Cnt_Cck_fail = %x, Cnt_all = %x\n", falsealm_cnt->cnt_ofdm_fail, falsealm_cnt->cnt_cck_fail, falsealm_cnt->cnt_all); } static void rtl92d_dm_find_minimum_rssi(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *de_digtable = &rtlpriv->dm_digtable; struct rtl_mac *mac = rtl_mac(rtlpriv); /* Determine the minimum RSSI */ if ((mac->link_state < MAC80211_LINKED) && (rtlpriv->dm.UNDEC_SM_PWDB == 0)) { de_digtable->min_undecorated_pwdb_for_dm = 0; RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, "Not connected to any\n"); } if (mac->link_state >= MAC80211_LINKED) { if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { de_digtable->min_undecorated_pwdb_for_dm = rtlpriv->dm.UNDEC_SM_PWDB; RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, "AP Client PWDB = 0x%lx\n", rtlpriv->dm.UNDEC_SM_PWDB); } else { de_digtable->min_undecorated_pwdb_for_dm = rtlpriv->dm.undecorated_smoothed_pwdb; RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, "STA Default Port PWDB = 0x%x\n", de_digtable->min_undecorated_pwdb_for_dm); } } else { de_digtable->min_undecorated_pwdb_for_dm = rtlpriv->dm.UNDEC_SM_PWDB; RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, "AP Ext Port or disconnect PWDB = 0x%x\n", de_digtable->min_undecorated_pwdb_for_dm); } RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "MinUndecoratedPWDBForDM =%d\n", de_digtable->min_undecorated_pwdb_for_dm); } static void rtl92d_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *de_digtable = &rtlpriv->dm_digtable; unsigned long flag = 0; if (de_digtable->cursta_connectstate == DIG_STA_CONNECT) { if (de_digtable->pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { if (de_digtable->min_undecorated_pwdb_for_dm <= 25) de_digtable->cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI; else de_digtable->cur_cck_pd_state = CCK_PD_STAGE_HIGHRSSI; } else { if (de_digtable->min_undecorated_pwdb_for_dm <= 20) de_digtable->cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI; else de_digtable->cur_cck_pd_state = CCK_PD_STAGE_HIGHRSSI; } } else { de_digtable->cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI; } if (de_digtable->pre_cck_pd_state != de_digtable->cur_cck_pd_state) { if (de_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0x83); rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } else { rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0xcd); rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } de_digtable->pre_cck_pd_state = de_digtable->cur_cck_pd_state; } RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "CurSTAConnectState=%s\n", de_digtable->cursta_connectstate == DIG_STA_CONNECT ? "DIG_STA_CONNECT " : "DIG_STA_DISCONNECT"); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "CCKPDStage=%s\n", de_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI ? "Low RSSI " : "High RSSI "); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "is92d single phy =%x\n", IS_92D_SINGLEPHY(rtlpriv->rtlhal.version)); } void rtl92d_dm_write_dig(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *de_digtable = &rtlpriv->dm_digtable; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "cur_igvalue = 0x%x, pre_igvalue = 0x%x, backoff_val = %d\n", de_digtable->cur_igvalue, de_digtable->pre_igvalue, de_digtable->backoff_val); if (de_digtable->dig_enable_flag == false) { RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "DIG is disabled\n"); de_digtable->pre_igvalue = 0x17; return; } if (de_digtable->pre_igvalue != de_digtable->cur_igvalue) { rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f, de_digtable->cur_igvalue); rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, 0x7f, de_digtable->cur_igvalue); de_digtable->pre_igvalue = de_digtable->cur_igvalue; } } static void rtl92d_early_mode_enabled(struct rtl_priv *rtlpriv) { struct dig_t *de_digtable = &rtlpriv->dm_digtable; if ((rtlpriv->mac80211.link_state >= MAC80211_LINKED) && (rtlpriv->mac80211.vendor == PEER_CISCO)) { RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "IOT_PEER = CISCO\n"); if (de_digtable->last_min_undecorated_pwdb_for_dm >= 50 && de_digtable->min_undecorated_pwdb_for_dm < 50) { rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL, 0x00); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Early Mode Off\n"); } else if (de_digtable->last_min_undecorated_pwdb_for_dm <= 55 && de_digtable->min_undecorated_pwdb_for_dm > 55) { rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL, 0x0f); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Early Mode On\n"); } } else if (!(rtl_read_byte(rtlpriv, REG_EARLY_MODE_CONTROL) & 0xf)) { rtl_write_byte(rtlpriv, REG_EARLY_MODE_CONTROL, 0x0f); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Early Mode On\n"); } } static void rtl92d_dm_dig(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *de_digtable = &rtlpriv->dm_digtable; u8 value_igi = de_digtable->cur_igvalue; struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "==>\n"); if (rtlpriv->rtlhal.earlymode_enable) { rtl92d_early_mode_enabled(rtlpriv); de_digtable->last_min_undecorated_pwdb_for_dm = de_digtable->min_undecorated_pwdb_for_dm; } if (!rtlpriv->dm.dm_initialgain_enable) return; /* because we will send data pkt when scanning * this will cause some ap like gear-3700 wep TP * lower if we retrun here, this is the diff of * mac80211 driver vs ieee80211 driver */ /* if (rtlpriv->mac80211.act_scanning) * return; */ /* Not STA mode return tmp */ if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION) return; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "progress\n"); /* Decide the current status and if modify initial gain or not */ if (rtlpriv->mac80211.link_state >= MAC80211_LINKED) de_digtable->cursta_connectstate = DIG_STA_CONNECT; else de_digtable->cursta_connectstate = DIG_STA_DISCONNECT; /* adjust initial gain according to false alarm counter */ if (falsealm_cnt->cnt_all < DM_DIG_FA_TH0) value_igi--; else if (falsealm_cnt->cnt_all < DM_DIG_FA_TH1) value_igi += 0; else if (falsealm_cnt->cnt_all < DM_DIG_FA_TH2) value_igi++; else if (falsealm_cnt->cnt_all >= DM_DIG_FA_TH2) value_igi += 2; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "dm_DIG() Before: large_fa_hit=%d, forbidden_igi=%x\n", de_digtable->large_fa_hit, de_digtable->forbidden_igi); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "dm_DIG() Before: Recover_cnt=%d, rx_gain_range_min=%x\n", de_digtable->recover_cnt, de_digtable->rx_gain_range_min); /* deal with abnorally large false alarm */ if (falsealm_cnt->cnt_all > 10000) { RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "dm_DIG(): Abnormally false alarm case\n"); de_digtable->large_fa_hit++; if (de_digtable->forbidden_igi < de_digtable->cur_igvalue) { de_digtable->forbidden_igi = de_digtable->cur_igvalue; de_digtable->large_fa_hit = 1; } if (de_digtable->large_fa_hit >= 3) { if ((de_digtable->forbidden_igi + 1) > DM_DIG_MAX) de_digtable->rx_gain_range_min = DM_DIG_MAX; else de_digtable->rx_gain_range_min = (de_digtable->forbidden_igi + 1); de_digtable->recover_cnt = 3600; /* 3600=2hr */ } } else { /* Recovery mechanism for IGI lower bound */ if (de_digtable->recover_cnt != 0) { de_digtable->recover_cnt--; } else { if (de_digtable->large_fa_hit == 0) { if ((de_digtable->forbidden_igi - 1) < DM_DIG_FA_LOWER) { de_digtable->forbidden_igi = DM_DIG_FA_LOWER; de_digtable->rx_gain_range_min = DM_DIG_FA_LOWER; } else { de_digtable->forbidden_igi--; de_digtable->rx_gain_range_min = (de_digtable->forbidden_igi + 1); } } else if (de_digtable->large_fa_hit == 3) { de_digtable->large_fa_hit = 0; } } } RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "dm_DIG() After: large_fa_hit=%d, forbidden_igi=%x\n", de_digtable->large_fa_hit, de_digtable->forbidden_igi); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "dm_DIG() After: recover_cnt=%d, rx_gain_range_min=%x\n", de_digtable->recover_cnt, de_digtable->rx_gain_range_min); if (value_igi > DM_DIG_MAX) value_igi = DM_DIG_MAX; else if (value_igi < de_digtable->rx_gain_range_min) value_igi = de_digtable->rx_gain_range_min; de_digtable->cur_igvalue = value_igi; rtl92d_dm_write_dig(hw); if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G) rtl92d_dm_cck_packet_detection_thresh(hw); RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "<<==\n"); } static void rtl92d_dm_init_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dynamic_txpower_enable = true; rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; } static void rtl92d_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb; if ((!rtlpriv->dm.dynamic_txpower_enable) || rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if ((mac->link_state < MAC80211_LINKED) && (rtlpriv->dm.UNDEC_SM_PWDB == 0)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "Not connected to any\n"); rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if (mac->link_state >= MAC80211_LINKED) { if (mac->opmode == NL80211_IFTYPE_ADHOC) { undecorated_smoothed_pwdb = rtlpriv->dm.UNDEC_SM_PWDB; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "IBSS Client PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } else { undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "STA Default Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } } else { undecorated_smoothed_pwdb = rtlpriv->dm.UNDEC_SM_PWDB; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Ext Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } if (rtlhal->current_bandtype == BAND_ON_5G) { if (undecorated_smoothed_pwdb >= 0x33) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL2; RT_TRACE(rtlpriv, COMP_HIPWR, DBG_LOUD, "5G:TxHighPwrLevel_Level2 (TxPwr=0x0)\n"); } else if ((undecorated_smoothed_pwdb < 0x33) && (undecorated_smoothed_pwdb >= 0x2b)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_HIPWR, DBG_LOUD, "5G:TxHighPwrLevel_Level1 (TxPwr=0x10)\n"); } else if (undecorated_smoothed_pwdb < 0x2b) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; RT_TRACE(rtlpriv, COMP_HIPWR, DBG_LOUD, "5G:TxHighPwrLevel_Normal\n"); } } else { if (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL2) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL2; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x0)\n"); } else if ((undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3)) && (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL1)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x10)\n"); } else if (undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL1 - 5)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_NORMAL\n"); } } if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "PHY_SetTxPowerLevel8192S() Channel = %d\n", rtlphy->current_channel); rtl92d_phy_set_txpower_level(hw, rtlphy->current_channel); } rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; } static void rtl92d_dm_pwdb_monitor(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /* AP & ADHOC & MESH will return tmp */ if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION) return; /* Indicate Rx signal strength to FW. */ if (rtlpriv->dm.useramask) { u32 temp = rtlpriv->dm.undecorated_smoothed_pwdb; temp <<= 16; temp |= 0x100; /* fw v12 cmdid 5:use max macid ,for nic , * default macid is 0 ,max macid is 1 */ rtl92d_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, (u8 *) (&temp)); } else { rtl_write_byte(rtlpriv, 0x4fe, (u8) rtlpriv->dm.undecorated_smoothed_pwdb); } } void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.current_turbo_edca = false; rtlpriv->dm.is_any_nonbepkts = false; rtlpriv->dm.is_cur_rdlstate = false; } static void rtl92d_dm_check_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); static u64 last_txok_cnt; static u64 last_rxok_cnt; u64 cur_txok_cnt; u64 cur_rxok_cnt; u32 edca_be_ul = 0x5ea42b; u32 edca_be_dl = 0x5ea42b; if (mac->link_state != MAC80211_LINKED) { rtlpriv->dm.current_turbo_edca = false; goto exit; } /* Enable BEQ TxOP limit configuration in wireless G-mode. */ /* To check whether we shall force turn on TXOP configuration. */ if ((!rtlpriv->dm.disable_framebursting) && (rtlpriv->sec.pairwise_enc_algorithm == WEP40_ENCRYPTION || rtlpriv->sec.pairwise_enc_algorithm == WEP104_ENCRYPTION || rtlpriv->sec.pairwise_enc_algorithm == TKIP_ENCRYPTION)) { /* Force TxOP limit to 0x005e for UL. */ if (!(edca_be_ul & 0xffff0000)) edca_be_ul |= 0x005e0000; /* Force TxOP limit to 0x005e for DL. */ if (!(edca_be_dl & 0xffff0000)) edca_be_dl |= 0x005e0000; } if ((!rtlpriv->dm.is_any_nonbepkts) && (!rtlpriv->dm.disable_framebursting)) { cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok_cnt; cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rxok_cnt; if (cur_rxok_cnt > 4 * cur_txok_cnt) { if (!rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, edca_be_dl); rtlpriv->dm.is_cur_rdlstate = true; } } else { if (rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, edca_be_ul); rtlpriv->dm.is_cur_rdlstate = false; } } rtlpriv->dm.current_turbo_edca = true; } else { if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, &tmp); rtlpriv->dm.current_turbo_edca = false; } } exit: rtlpriv->dm.is_any_nonbepkts = false; last_txok_cnt = rtlpriv->stats.txbytesunicast; last_rxok_cnt = rtlpriv->stats.rxbytesunicast; } static void rtl92d_dm_rxgain_tracking_thermalmeter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 index_mapping[RX_INDEX_MAPPING_NUM] = { 0x0f, 0x0f, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x04, 0x03, 0x02 }; int i; u32 u4tmp; u4tmp = (index_mapping[(rtlpriv->efuse.eeprom_thermalmeter - rtlpriv->dm.thermalvalue_rxgain)]) << 12; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "===> Rx Gain %x\n", u4tmp); for (i = RF90_PATH_A; i < rtlpriv->phy.num_total_rfpath; i++) rtl_set_rfreg(hw, i, 0x3C, BRFREGOFFSETMASK, (rtlpriv->phy.reg_rf3c[i] & (~(0xF000))) | u4tmp); } static void rtl92d_bandtype_2_4G(struct ieee80211_hw *hw, long *temp_cckg, u8 *cck_index_old) { struct rtl_priv *rtlpriv = rtl_priv(hw); int i; unsigned long flag = 0; long temp_cck; /* Query CCK default setting From 0xa24 */ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); temp_cck = rtl_get_bbreg(hw, RCCK0_TXFILTER2, BMASKDWORD) & BMASKCCK; rtl92d_release_cckandrw_pagea_ctl(hw, &flag); for (i = 0; i < CCK_TABLE_LENGTH; i++) { if (rtlpriv->dm.cck_inch14) { if (!memcmp((void *)&temp_cck, (void *)&cckswing_table_ch14[i][2], 4)) { *cck_index_old = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial reg0x%x = 0x%lx, cck_index=0x%x, ch 14 %d\n", RCCK0_TXFILTER2, temp_cck, *cck_index_old, rtlpriv->dm.cck_inch14); break; } } else { if (!memcmp((void *) &temp_cck, &cckswing_table_ch1ch13[i][2], 4)) { *cck_index_old = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial reg0x%x = 0x%lx, cck_index = 0x%x, ch14 %d\n", RCCK0_TXFILTER2, temp_cck, *cck_index_old, rtlpriv->dm.cck_inch14); break; } } } *temp_cckg = temp_cck; } static void rtl92d_bandtype_5G(struct rtl_hal *rtlhal, u8 *ofdm_index, bool *internal_pa, u8 thermalvalue, u8 delta, u8 rf, struct rtl_efuse *rtlefuse, struct rtl_priv *rtlpriv, struct rtl_phy *rtlphy, u8 index_mapping[5][INDEX_MAPPING_NUM], u8 index_mapping_pa[8][INDEX_MAPPING_NUM]) { int i; u8 index; u8 offset = 0; for (i = 0; i < rf; i++) { if (rtlhal->macphymode == DUALMAC_DUALPHY && rtlhal->interfaceindex == 1) /* MAC 1 5G */ *internal_pa = rtlefuse->internal_pa_5g[1]; else *internal_pa = rtlefuse->internal_pa_5g[i]; if (*internal_pa) { if (rtlhal->interfaceindex == 1 || i == rf) offset = 4; else offset = 0; if (rtlphy->current_channel >= 100 && rtlphy->current_channel <= 165) offset += 2; } else { if (rtlhal->interfaceindex == 1 || i == rf) offset = 2; else offset = 0; } if (thermalvalue > rtlefuse->eeprom_thermalmeter) offset++; if (*internal_pa) { if (delta > INDEX_MAPPING_NUM - 1) index = index_mapping_pa[offset] [INDEX_MAPPING_NUM - 1]; else index = index_mapping_pa[offset][delta]; } else { if (delta > INDEX_MAPPING_NUM - 1) index = index_mapping[offset][INDEX_MAPPING_NUM - 1]; else index = index_mapping[offset][delta]; } if (thermalvalue > rtlefuse->eeprom_thermalmeter) { if (*internal_pa && thermalvalue > 0x12) { ofdm_index[i] = rtlpriv->dm.ofdm_index[i] - ((delta / 2) * 3 + (delta % 2)); } else { ofdm_index[i] -= index; } } else { ofdm_index[i] += index; } } } static void rtl92d_dm_txpower_tracking_callback_thermalmeter( struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 thermalvalue, delta, delta_lck, delta_iqk, delta_rxgain; u8 offset, thermalvalue_avg_count = 0; u32 thermalvalue_avg = 0; bool internal_pa = false; long ele_a = 0, ele_d, temp_cck, val_x, value32; long val_y, ele_c = 0; u8 ofdm_index[2]; u8 cck_index = 0; u8 ofdm_index_old[2]; u8 cck_index_old = 0; u8 index; int i; bool is2t = IS_92D_SINGLEPHY(rtlhal->version); u8 ofdm_min_index = 6, ofdm_min_index_internal_pa = 3, rf; u8 indexforchannel = rtl92d_get_rightchnlplace_for_iqk(rtlphy->current_channel); u8 index_mapping[5][INDEX_MAPPING_NUM] = { /* 5G, path A/MAC 0, decrease power */ {0, 1, 3, 6, 8, 9, 11, 13, 14, 16, 17, 18, 18}, /* 5G, path A/MAC 0, increase power */ {0, 2, 4, 5, 7, 10, 12, 14, 16, 18, 18, 18, 18}, /* 5G, path B/MAC 1, decrease power */ {0, 2, 3, 6, 8, 9, 11, 13, 14, 16, 17, 18, 18}, /* 5G, path B/MAC 1, increase power */ {0, 2, 4, 5, 7, 10, 13, 16, 16, 18, 18, 18, 18}, /* 2.4G, for decreas power */ {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10}, }; u8 index_mapping_internal_pa[8][INDEX_MAPPING_NUM] = { /* 5G, path A/MAC 0, ch36-64, decrease power */ {0, 1, 2, 4, 6, 7, 9, 11, 12, 14, 15, 16, 16}, /* 5G, path A/MAC 0, ch36-64, increase power */ {0, 2, 4, 5, 7, 10, 12, 14, 16, 18, 18, 18, 18}, /* 5G, path A/MAC 0, ch100-165, decrease power */ {0, 1, 2, 3, 5, 6, 8, 10, 11, 13, 14, 15, 15}, /* 5G, path A/MAC 0, ch100-165, increase power */ {0, 2, 4, 5, 7, 10, 12, 14, 16, 18, 18, 18, 18}, /* 5G, path B/MAC 1, ch36-64, decrease power */ {0, 1, 2, 4, 6, 7, 9, 11, 12, 14, 15, 16, 16}, /* 5G, path B/MAC 1, ch36-64, increase power */ {0, 2, 4, 5, 7, 10, 13, 16, 16, 18, 18, 18, 18}, /* 5G, path B/MAC 1, ch100-165, decrease power */ {0, 1, 2, 3, 5, 6, 8, 9, 10, 12, 13, 14, 14}, /* 5G, path B/MAC 1, ch100-165, increase power */ {0, 2, 4, 5, 7, 10, 13, 16, 16, 18, 18, 18, 18}, }; rtlpriv->dm.txpower_trackinginit = true; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "\n"); thermalvalue = (u8) rtl_get_rfreg(hw, RF90_PATH_A, RF_T_METER, 0xf800); RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Readback Thermal Meter = 0x%x pre thermal meter 0x%x eeprom_thermalmeter 0x%x\n", thermalvalue, rtlpriv->dm.thermalvalue, rtlefuse->eeprom_thermalmeter); rtl92d_phy_ap_calibrate(hw, (thermalvalue - rtlefuse->eeprom_thermalmeter)); if (is2t) rf = 2; else rf = 1; if (thermalvalue) { ele_d = rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD) & BMASKOFDM_D; for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) { if (ele_d == (ofdmswing_table[i] & BMASKOFDM_D)) { ofdm_index_old[0] = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial pathA ele_d reg0x%x = 0x%lx, ofdm_index=0x%x\n", ROFDM0_XATxIQIMBALANCE, ele_d, ofdm_index_old[0]); break; } } if (is2t) { ele_d = rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD) & BMASKOFDM_D; for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) { if (ele_d == (ofdmswing_table[i] & BMASKOFDM_D)) { ofdm_index_old[1] = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial pathB ele_d reg 0x%x = 0x%lx, ofdm_index = 0x%x\n", ROFDM0_XBTxIQIMBALANCE, ele_d, ofdm_index_old[1]); break; } } } if (rtlhal->current_bandtype == BAND_ON_2_4G) { rtl92d_bandtype_2_4G(hw, &temp_cck, &cck_index_old); } else { temp_cck = 0x090e1317; cck_index_old = 12; } if (!rtlpriv->dm.thermalvalue) { rtlpriv->dm.thermalvalue = rtlefuse->eeprom_thermalmeter; rtlpriv->dm.thermalvalue_lck = thermalvalue; rtlpriv->dm.thermalvalue_iqk = thermalvalue; rtlpriv->dm.thermalvalue_rxgain = rtlefuse->eeprom_thermalmeter; for (i = 0; i < rf; i++) rtlpriv->dm.ofdm_index[i] = ofdm_index_old[i]; rtlpriv->dm.cck_index = cck_index_old; } if (rtlhal->reloadtxpowerindex) { for (i = 0; i < rf; i++) rtlpriv->dm.ofdm_index[i] = ofdm_index_old[i]; rtlpriv->dm.cck_index = cck_index_old; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "reload ofdm index for band switch\n"); } rtlpriv->dm.thermalvalue_avg [rtlpriv->dm.thermalvalue_avg_index] = thermalvalue; rtlpriv->dm.thermalvalue_avg_index++; if (rtlpriv->dm.thermalvalue_avg_index == AVG_THERMAL_NUM) rtlpriv->dm.thermalvalue_avg_index = 0; for (i = 0; i < AVG_THERMAL_NUM; i++) { if (rtlpriv->dm.thermalvalue_avg[i]) { thermalvalue_avg += rtlpriv->dm.thermalvalue_avg[i]; thermalvalue_avg_count++; } } if (thermalvalue_avg_count) thermalvalue = (u8) (thermalvalue_avg / thermalvalue_avg_count); if (rtlhal->reloadtxpowerindex) { delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ? (thermalvalue - rtlefuse->eeprom_thermalmeter) : (rtlefuse->eeprom_thermalmeter - thermalvalue); rtlhal->reloadtxpowerindex = false; rtlpriv->dm.done_txpower = false; } else if (rtlpriv->dm.done_txpower) { delta = (thermalvalue > rtlpriv->dm.thermalvalue) ? (thermalvalue - rtlpriv->dm.thermalvalue) : (rtlpriv->dm.thermalvalue - thermalvalue); } else { delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ? (thermalvalue - rtlefuse->eeprom_thermalmeter) : (rtlefuse->eeprom_thermalmeter - thermalvalue); } delta_lck = (thermalvalue > rtlpriv->dm.thermalvalue_lck) ? (thermalvalue - rtlpriv->dm.thermalvalue_lck) : (rtlpriv->dm.thermalvalue_lck - thermalvalue); delta_iqk = (thermalvalue > rtlpriv->dm.thermalvalue_iqk) ? (thermalvalue - rtlpriv->dm.thermalvalue_iqk) : (rtlpriv->dm.thermalvalue_iqk - thermalvalue); delta_rxgain = (thermalvalue > rtlpriv->dm.thermalvalue_rxgain) ? (thermalvalue - rtlpriv->dm.thermalvalue_rxgain) : (rtlpriv->dm.thermalvalue_rxgain - thermalvalue); RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Readback Thermal Meter = 0x%x pre thermal meter 0x%x eeprom_thermalmeter 0x%x delta 0x%x delta_lck 0x%x delta_iqk 0x%x\n", thermalvalue, rtlpriv->dm.thermalvalue, rtlefuse->eeprom_thermalmeter, delta, delta_lck, delta_iqk); if ((delta_lck > rtlefuse->delta_lck) && (rtlefuse->delta_lck != 0)) { rtlpriv->dm.thermalvalue_lck = thermalvalue; rtl92d_phy_lc_calibrate(hw); } if (delta > 0 && rtlpriv->dm.txpower_track_control) { rtlpriv->dm.done_txpower = true; delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ? (thermalvalue - rtlefuse->eeprom_thermalmeter) : (rtlefuse->eeprom_thermalmeter - thermalvalue); if (rtlhal->current_bandtype == BAND_ON_2_4G) { offset = 4; if (delta > INDEX_MAPPING_NUM - 1) index = index_mapping[offset] [INDEX_MAPPING_NUM - 1]; else index = index_mapping[offset][delta]; if (thermalvalue > rtlpriv->dm.thermalvalue) { for (i = 0; i < rf; i++) ofdm_index[i] -= delta; cck_index -= delta; } else { for (i = 0; i < rf; i++) ofdm_index[i] += index; cck_index += index; } } else if (rtlhal->current_bandtype == BAND_ON_5G) { rtl92d_bandtype_5G(rtlhal, ofdm_index, &internal_pa, thermalvalue, delta, rf, rtlefuse, rtlpriv, rtlphy, index_mapping, index_mapping_internal_pa); } if (is2t) { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "temp OFDM_A_index=0x%x, OFDM_B_index = 0x%x,cck_index=0x%x\n", rtlpriv->dm.ofdm_index[0], rtlpriv->dm.ofdm_index[1], rtlpriv->dm.cck_index); } else { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "temp OFDM_A_index=0x%x,cck_index = 0x%x\n", rtlpriv->dm.ofdm_index[0], rtlpriv->dm.cck_index); } for (i = 0; i < rf; i++) { if (ofdm_index[i] > OFDM_TABLE_SIZE_92D - 1) ofdm_index[i] = OFDM_TABLE_SIZE_92D - 1; else if (ofdm_index[i] < ofdm_min_index) ofdm_index[i] = ofdm_min_index; } if (rtlhal->current_bandtype == BAND_ON_2_4G) { if (cck_index > CCK_TABLE_SIZE - 1) { cck_index = CCK_TABLE_SIZE - 1; } else if (internal_pa || rtlhal->current_bandtype == BAND_ON_2_4G) { if (ofdm_index[i] < ofdm_min_index_internal_pa) ofdm_index[i] = ofdm_min_index_internal_pa; } else if (cck_index < 0) { cck_index = 0; } } if (is2t) { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "new OFDM_A_index=0x%x, OFDM_B_index = 0x%x, cck_index=0x%x\n", ofdm_index[0], ofdm_index[1], cck_index); } else { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "new OFDM_A_index=0x%x,cck_index = 0x%x\n", ofdm_index[0], cck_index); } ele_d = (ofdmswing_table[(u8) ofdm_index[0]] & 0xFFC00000) >> 22; val_x = rtlphy->iqk_matrix_regsetting [indexforchannel].value[0][0]; val_y = rtlphy->iqk_matrix_regsetting [indexforchannel].value[0][1]; if (val_x != 0) { if ((val_x & 0x00000200) != 0) val_x = val_x | 0xFFFFFC00; ele_a = ((val_x * ele_d) >> 8) & 0x000003FF; /* new element C = element D x Y */ if ((val_y & 0x00000200) != 0) val_y = val_y | 0xFFFFFC00; ele_c = ((val_y * ele_d) >> 8) & 0x000003FF; /* wirte new elements A, C, D to regC80 and * regC94, element B is always 0 */ value32 = (ele_d << 22) | ((ele_c & 0x3F) << 16) | ele_a; rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, value32); value32 = (ele_c & 0x000003C0) >> 6; rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS, value32); value32 = ((val_x * ele_d) >> 7) & 0x01; rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24), value32); } else { rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, ofdmswing_table [(u8)ofdm_index[0]]); rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24), 0x00); } RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "TxPwrTracking for interface %d path A: X = 0x%lx, Y = 0x%lx ele_A = 0x%lx ele_C = 0x%lx ele_D = 0x%lx 0xe94 = 0x%lx 0xe9c = 0x%lx\n", rtlhal->interfaceindex, val_x, val_y, ele_a, ele_c, ele_d, val_x, val_y); if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* Adjust CCK according to IQK result */ if (!rtlpriv->dm.cck_inch14) { rtl_write_byte(rtlpriv, 0xa22, cckswing_table_ch1ch13 [(u8)cck_index][0]); rtl_write_byte(rtlpriv, 0xa23, cckswing_table_ch1ch13 [(u8)cck_index][1]); rtl_write_byte(rtlpriv, 0xa24, cckswing_table_ch1ch13 [(u8)cck_index][2]); rtl_write_byte(rtlpriv, 0xa25, cckswing_table_ch1ch13 [(u8)cck_index][3]); rtl_write_byte(rtlpriv, 0xa26, cckswing_table_ch1ch13 [(u8)cck_index][4]); rtl_write_byte(rtlpriv, 0xa27, cckswing_table_ch1ch13 [(u8)cck_index][5]); rtl_write_byte(rtlpriv, 0xa28, cckswing_table_ch1ch13 [(u8)cck_index][6]); rtl_write_byte(rtlpriv, 0xa29, cckswing_table_ch1ch13 [(u8)cck_index][7]); } else { rtl_write_byte(rtlpriv, 0xa22, cckswing_table_ch14 [(u8)cck_index][0]); rtl_write_byte(rtlpriv, 0xa23, cckswing_table_ch14 [(u8)cck_index][1]); rtl_write_byte(rtlpriv, 0xa24, cckswing_table_ch14 [(u8)cck_index][2]); rtl_write_byte(rtlpriv, 0xa25, cckswing_table_ch14 [(u8)cck_index][3]); rtl_write_byte(rtlpriv, 0xa26, cckswing_table_ch14 [(u8)cck_index][4]); rtl_write_byte(rtlpriv, 0xa27, cckswing_table_ch14 [(u8)cck_index][5]); rtl_write_byte(rtlpriv, 0xa28, cckswing_table_ch14 [(u8)cck_index][6]); rtl_write_byte(rtlpriv, 0xa29, cckswing_table_ch14 [(u8)cck_index][7]); } } if (is2t) { ele_d = (ofdmswing_table[(u8) ofdm_index[1]] & 0xFFC00000) >> 22; val_x = rtlphy->iqk_matrix_regsetting [indexforchannel].value[0][4]; val_y = rtlphy->iqk_matrix_regsetting [indexforchannel].value[0][5]; if (val_x != 0) { if ((val_x & 0x00000200) != 0) /* consider minus */ val_x = val_x | 0xFFFFFC00; ele_a = ((val_x * ele_d) >> 8) & 0x000003FF; /* new element C = element D x Y */ if ((val_y & 0x00000200) != 0) val_y = val_y | 0xFFFFFC00; ele_c = ((val_y * ele_d) >> 8) & 0x00003FF; /* write new elements A, C, D to regC88 * and regC9C, element B is always 0 */ value32 = (ele_d << 22) | ((ele_c & 0x3F) << 16) | ele_a; rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, value32); value32 = (ele_c & 0x000003C0) >> 6; rtl_set_bbreg(hw, ROFDM0_XDTxAFE, BMASKH4BITS, value32); value32 = ((val_x * ele_d) >> 7) & 0x01; rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(28), value32); } else { rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, ofdmswing_table [(u8) ofdm_index[1]]); rtl_set_bbreg(hw, ROFDM0_XDTxAFE, BMASKH4BITS, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(28), 0x00); } RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "TxPwrTracking path B: X = 0x%lx, Y = 0x%lx ele_A = 0x%lx ele_C = 0x%lx ele_D = 0x%lx 0xeb4 = 0x%lx 0xebc = 0x%lx\n", val_x, val_y, ele_a, ele_c, ele_d, val_x, val_y); } RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "TxPwrTracking 0xc80 = 0x%x, 0xc94 = 0x%x RF 0x24 = 0x%x\n", rtl_get_bbreg(hw, 0xc80, BMASKDWORD), rtl_get_bbreg(hw, 0xc94, BMASKDWORD), rtl_get_rfreg(hw, RF90_PATH_A, 0x24, BRFREGOFFSETMASK)); } if ((delta_iqk > rtlefuse->delta_iqk) && (rtlefuse->delta_iqk != 0)) { rtl92d_phy_reset_iqk_result(hw); rtlpriv->dm.thermalvalue_iqk = thermalvalue; rtl92d_phy_iq_calibrate(hw); } if (delta_rxgain > 0 && rtlhal->current_bandtype == BAND_ON_5G && thermalvalue <= rtlefuse->eeprom_thermalmeter) { rtlpriv->dm.thermalvalue_rxgain = thermalvalue; rtl92d_dm_rxgain_tracking_thermalmeter(hw); } if (rtlpriv->dm.txpower_track_control) rtlpriv->dm.thermalvalue = thermalvalue; } RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "<===\n"); } static void rtl92d_dm_initialize_txpower_tracking(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.txpower_tracking = true; rtlpriv->dm.txpower_trackinginit = false; rtlpriv->dm.txpower_track_control = true; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "pMgntInfo->txpower_tracking = %d\n", rtlpriv->dm.txpower_tracking); } void rtl92d_dm_check_txpower_tracking_thermal_meter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); static u8 tm_trigger; if (!rtlpriv->dm.txpower_tracking) return; if (!tm_trigger) { rtl_set_rfreg(hw, RF90_PATH_A, RF_T_METER, BIT(17) | BIT(16), 0x03); RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Trigger 92S Thermal Meter!!\n"); tm_trigger = 1; return; } else { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Schedule TxPowerTracking direct call!!\n"); rtl92d_dm_txpower_tracking_callback_thermalmeter(hw); tm_trigger = 0; } } void rtl92d_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rate_adaptive *ra = &(rtlpriv->ra); ra->ratr_state = DM_RATR_STA_INIT; ra->pre_ratr_state = DM_RATR_STA_INIT; if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER) rtlpriv->dm.useramask = true; else rtlpriv->dm.useramask = false; } void rtl92d_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; rtl92d_dm_diginit(hw); rtl92d_dm_init_dynamic_txpower(hw); rtl92d_dm_init_edca_turbo(hw); rtl92d_dm_init_rate_adaptive_mask(hw); rtl92d_dm_initialize_txpower_tracking(hw); } void rtl92d_dm_watchdog(struct ieee80211_hw *hw) { struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool fw_current_inpsmode = false; bool fwps_awake = true; /* 1. RF is OFF. (No need to do DM.) * 2. Fw is under power saving mode for FwLPS. * (Prevent from SW/FW I/O racing.) * 3. IPS workitem is scheduled. (Prevent from IPS sequence * to be swapped with DM. * 4. RFChangeInProgress is TRUE. * (Prevent from broken by IPS/HW/SW Rf off.) */ if ((ppsc->rfpwr_state == ERFON) && ((!fw_current_inpsmode) && fwps_awake) && (!ppsc->rfchange_inprogress)) { rtl92d_dm_pwdb_monitor(hw); rtl92d_dm_false_alarm_counter_statistics(hw); rtl92d_dm_find_minimum_rssi(hw); rtl92d_dm_dig(hw); /* rtl92d_dm_dynamic_bb_powersaving(hw); */ rtl92d_dm_dynamic_txpower(hw); /* rtl92d_dm_check_txpower_tracking_thermal_meter(hw); */ /* rtl92d_dm_refresh_rate_adaptive_mask(hw); */ /* rtl92d_dm_interrupt_migration(hw); */ rtl92d_dm_check_edca_turbo(hw); } } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/def.h0000644000175000017500000002136412026211315024514 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92D_DEF_H__ #define __RTL92D_DEF_H__ /* Min Spacing related settings. */ #define MAX_MSS_DENSITY_2T 0x13 #define MAX_MSS_DENSITY_1T 0x0A #define RF6052_MAX_TX_PWR 0x3F #define RF6052_MAX_REG 0x3F #define RF6052_MAX_PATH 2 #define HAL_RETRY_LIMIT_INFRA 48 #define HAL_RETRY_LIMIT_AP_ADHOC 7 #define PHY_RSSI_SLID_WIN_MAX 100 #define PHY_LINKQUALITY_SLID_WIN_MAX 20 #define PHY_BEACON_RSSI_SLID_WIN_MAX 10 #define RESET_DELAY_8185 20 #define RT_IBSS_INT_MASKS (IMR_BCNINT | IMR_TBDOK | IMR_TBDER) #define RT_AC_INT_MASKS (IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK) #define NUM_OF_FIRMWARE_QUEUE 10 #define NUM_OF_PAGES_IN_FW 0x100 #define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0x0 #define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x0 #define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x02 #define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0x02 #define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x2 #define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0xA1 #define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM 0x026 #define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM 0x048 #define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM 0x048 #define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM 0x026 #define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM 0x00 #define MAX_LINES_HWCONFIG_TXT 1000 #define MAX_BYTES_LINE_HWCONFIG_TXT 256 #define SW_THREE_WIRE 0 #define HW_THREE_WIRE 2 #define BT_DEMO_BOARD 0 #define BT_QA_BOARD 1 #define BT_FPGA 2 #define RX_SMOOTH_FACTOR 20 #define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0 #define HAL_PRIME_CHNL_OFFSET_LOWER 1 #define HAL_PRIME_CHNL_OFFSET_UPPER 2 #define MAX_H2C_QUEUE_NUM 10 #define RX_MPDU_QUEUE 0 #define RX_CMD_QUEUE 1 #define RX_MAX_QUEUE 2 #define C2H_RX_CMD_HDR_LEN 8 #define GET_C2H_CMD_CMD_LEN(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 0, 16) #define GET_C2H_CMD_ELEMENT_ID(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 16, 8) #define GET_C2H_CMD_CMD_SEQ(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 24, 7) #define GET_C2H_CMD_CONTINUE(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 31, 1) #define GET_C2H_CMD_CONTENT(__prxhdr) \ ((u8 *)(__prxhdr) + C2H_RX_CMD_HDR_LEN) #define GET_C2H_CMD_FEEDBACK_ELEMENT_ID(__pcmdfbhdr) \ LE_BITS_TO_4BYTE((__pcmdfbhdr), 0, 8) #define GET_C2H_CMD_FEEDBACK_CCX_LEN(__pcmdfbhdr) \ LE_BITS_TO_4BYTE((__pcmdfbhdr), 8, 8) #define GET_C2H_CMD_FEEDBACK_CCX_CMD_CNT(__pcmdfbhdr) \ LE_BITS_TO_4BYTE((__pcmdfbhdr), 16, 16) #define GET_C2H_CMD_FEEDBACK_CCX_MAC_ID(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 0, 5) #define GET_C2H_CMD_FEEDBACK_CCX_VALID(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 7, 1) #define GET_C2H_CMD_FEEDBACK_CCX_RETRY_CNT(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 8, 5) #define GET_C2H_CMD_FEEDBACK_CCX_TOK(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 15, 1) #define GET_C2H_CMD_FEEDBACK_CCX_QSEL(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4) #define GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12) enum version_8192d { VERSION_TEST_CHIP_88C = 0x0000, VERSION_TEST_CHIP_92C = 0x0020, VERSION_TEST_UMC_CHIP_8723 = 0x0081, VERSION_NORMAL_TSMC_CHIP_88C = 0x0008, VERSION_NORMAL_TSMC_CHIP_92C = 0x0028, VERSION_NORMAL_TSMC_CHIP_92C_1T2R = 0x0018, VERSION_NORMAL_UMC_CHIP_88C_A_CUT = 0x0088, VERSION_NORMAL_UMC_CHIP_92C_A_CUT = 0x00a8, VERSION_NORMAL_UMC_CHIP_92C_1T2R_A_CUT = 0x0098, VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT = 0x0089, VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT = 0x1089, VERSION_NORMAL_UMC_CHIP_88C_B_CUT = 0x1088, VERSION_NORMAL_UMC_CHIP_92C_B_CUT = 0x10a8, VERSION_NORMAL_UMC_CHIP_92C_1T2R_B_CUT = 0x1090, VERSION_TEST_CHIP_92D_SINGLEPHY = 0x0022, VERSION_TEST_CHIP_92D_DUALPHY = 0x0002, VERSION_NORMAL_CHIP_92D_SINGLEPHY = 0x002a, VERSION_NORMAL_CHIP_92D_DUALPHY = 0x000a, VERSION_NORMAL_CHIP_92D_C_CUT_SINGLEPHY = 0x202a, VERSION_NORMAL_CHIP_92D_C_CUT_DUALPHY = 0x200a, VERSION_NORMAL_CHIP_92D_D_CUT_SINGLEPHY = 0x302a, VERSION_NORMAL_CHIP_92D_D_CUT_DUALPHY = 0x300a, VERSION_NORMAL_CHIP_92D_E_CUT_SINGLEPHY = 0x402a, VERSION_NORMAL_CHIP_92D_E_CUT_DUALPHY = 0x400a, }; /* for 92D */ #define CHIP_92D_SINGLEPHY BIT(9) /* Chip specific */ #define CHIP_BONDING_IDENTIFIER(_value) (((_value)>>22)&0x3) #define CHIP_BONDING_92C_1T2R 0x1 #define CHIP_BONDING_88C_USB_MCARD 0x2 #define CHIP_BONDING_88C_USB_HP 0x1 /* [15:12] IC version(CUT): A-cut=0, B-cut=1, C-cut=2, D-cut=3 */ /* [7] Manufacturer: TSMC=0, UMC=1 */ /* [6:4] RF type: 1T1R=0, 1T2R=1, 2T2R=2 */ /* [3] Chip type: TEST=0, NORMAL=1 */ /* [2:0] IC type: 81xxC=0, 8723=1, 92D=2 */ #define CHIP_8723 BIT(0) #define CHIP_92D BIT(1) #define NORMAL_CHIP BIT(3) #define RF_TYPE_1T1R (~(BIT(4)|BIT(5)|BIT(6))) #define RF_TYPE_1T2R BIT(4) #define RF_TYPE_2T2R BIT(5) #define CHIP_VENDOR_UMC BIT(7) #define CHIP_92D_B_CUT BIT(12) #define CHIP_92D_C_CUT BIT(13) #define CHIP_92D_D_CUT (BIT(13)|BIT(12)) #define CHIP_92D_E_CUT BIT(14) /* MASK */ #define IC_TYPE_MASK (BIT(0)|BIT(1)|BIT(2)) #define CHIP_TYPE_MASK BIT(3) #define RF_TYPE_MASK (BIT(4)|BIT(5)|BIT(6)) #define MANUFACTUER_MASK BIT(7) #define ROM_VERSION_MASK (BIT(11)|BIT(10)|BIT(9)|BIT(8)) #define CUT_VERSION_MASK (BIT(15)|BIT(14)|BIT(13)|BIT(12)) /* Get element */ #define GET_CVID_IC_TYPE(version) ((version) & IC_TYPE_MASK) #define GET_CVID_CHIP_TYPE(version) ((version) & CHIP_TYPE_MASK) #define GET_CVID_RF_TYPE(version) ((version) & RF_TYPE_MASK) #define GET_CVID_MANUFACTUER(version) ((version) & MANUFACTUER_MASK) #define GET_CVID_ROM_VERSION(version) ((version) & ROM_VERSION_MASK) #define GET_CVID_CUT_VERSION(version) ((version) & CUT_VERSION_MASK) #define IS_1T1R(version) ((GET_CVID_RF_TYPE(version)) ? \ false : true) #define IS_1T2R(version) ((GET_CVID_RF_TYPE(version) == \ RF_TYPE_1T2R) ? true : false) #define IS_2T2R(version) ((GET_CVID_RF_TYPE(version) == \ RF_TYPE_2T2R) ? true : false) #define IS_92D_SINGLEPHY(version) ((IS_92D(version)) ? \ (IS_2T2R(version) ? true : false) : false) #define IS_92D(version) ((GET_CVID_IC_TYPE(version) == \ CHIP_92D) ? true : false) #define IS_92D_C_CUT(version) ((IS_92D(version)) ? \ ((GET_CVID_CUT_VERSION(version) == \ CHIP_92D_C_CUT) ? true : false) : false) #define IS_92D_D_CUT(version) ((IS_92D(version)) ? \ ((GET_CVID_CUT_VERSION(version) == \ CHIP_92D_D_CUT) ? true : false) : false) #define IS_92D_E_CUT(version) ((IS_92D(version)) ? \ ((GET_CVID_CUT_VERSION(version) == \ CHIP_92D_E_CUT) ? true : false) : false) enum rf_optype { RF_OP_BY_SW_3WIRE = 0, RF_OP_BY_FW, RF_OP_MAX }; enum rtl_desc_qsel { QSLT_BK = 0x2, QSLT_BE = 0x0, QSLT_VI = 0x5, QSLT_VO = 0x7, QSLT_BEACON = 0x10, QSLT_HIGH = 0x11, QSLT_MGNT = 0x12, QSLT_CMD = 0x13, }; enum channel_plan { CHPL_FCC = 0, CHPL_IC = 1, CHPL_ETSI = 2, CHPL_SPAIN = 3, CHPL_FRANCE = 4, CHPL_MKK = 5, CHPL_MKK1 = 6, CHPL_ISRAEL = 7, CHPL_TELEC = 8, CHPL_GLOBAL = 9, CHPL_WORLD = 10, }; struct phy_sts_cck_8192d { u8 adc_pwdb_X[4]; u8 sq_rpt; u8 cck_agc_rpt; }; struct h2c_cmd_8192c { u8 element_id; u32 cmd_len; u8 *p_cmdbuffer; }; struct txpower_info { u8 cck_index[RF6052_MAX_PATH][CHANNEL_GROUP_MAX]; u8 ht40_1sindex[RF6052_MAX_PATH][CHANNEL_GROUP_MAX]; u8 ht40_2sindexdiff[RF6052_MAX_PATH][CHANNEL_GROUP_MAX]; u8 ht20indexdiff[RF6052_MAX_PATH][CHANNEL_GROUP_MAX]; u8 ofdmindexdiff[RF6052_MAX_PATH][CHANNEL_GROUP_MAX]; u8 ht40maxoffset[RF6052_MAX_PATH][CHANNEL_GROUP_MAX]; u8 ht20maxoffset[RF6052_MAX_PATH][CHANNEL_GROUP_MAX]; u8 tssi_a[3]; /* 5GL/5GM/5GH */ u8 tssi_b[3]; }; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192de/Makefile0000644000175000017500000000027312026211315025241 0ustar mcgrofmcgrofrtl8192de-objs := \ dm.o \ fw.o \ hw.o \ led.o \ phy.o \ rf.o \ sw.o \ table.o \ trx.o obj-$(CONFIG_RTL8192DE) += rtl8192de.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/0000755000175000017500000000000012026211315023616 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/sw.c0000644000175000017500000003411612026211315024420 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../core.h" #include "../base.h" #include "../pci.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "fw.h" #include "hw.h" #include "sw.h" #include "trx.h" #include "led.h" #include static void rtl92s_init_aspm_vars(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); /*close ASPM for AMD defaultly */ rtlpci->const_amdpci_aspm = 0; /* * ASPM PS mode. * 0 - Disable ASPM, * 1 - Enable ASPM without Clock Req, * 2 - Enable ASPM with Clock Req, * 3 - Alwyas Enable ASPM with Clock Req, * 4 - Always Enable ASPM without Clock Req. * set defult to RTL8192CE:3 RTL8192E:2 * */ rtlpci->const_pci_aspm = 2; /*Setting for PCI-E device */ rtlpci->const_devicepci_aspm_setting = 0x03; /*Setting for PCI-E bridge */ rtlpci->const_hostpci_aspm_setting = 0x02; /* * In Hw/Sw Radio Off situation. * 0 - Default, * 1 - From ASPM setting without low Mac Pwr, * 2 - From ASPM setting with low Mac Pwr, * 3 - Bus D3 * set default to RTL8192CE:0 RTL8192SE:2 */ rtlpci->const_hwsw_rfoff_d3 = 2; /* * This setting works for those device with * backdoor ASPM setting such as EPHY setting. * 0 - Not support ASPM, * 1 - Support ASPM, * 2 - According to chipset. */ rtlpci->const_support_pciaspm = 2; } static void rtl92se_fw_cb(const struct firmware *firmware, void *context) { struct ieee80211_hw *hw = context; struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(pcipriv); struct rt_firmware *pfirmware = NULL; int err; RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD, "Firmware callback routine entered!\n"); complete(&rtlpriv->firmware_loading_complete); if (!firmware) { pr_err("Firmware %s not available\n", rtlpriv->cfg->fw_name); rtlpriv->max_fw_size = 0; return; } if (firmware->size > rtlpriv->max_fw_size) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Firmware is too big!\n"); rtlpriv->max_fw_size = 0; release_firmware(firmware); return; } pfirmware = (struct rt_firmware *)rtlpriv->rtlhal.pfirmware; memcpy(pfirmware->sz_fw_tmpbuffer, firmware->data, firmware->size); pfirmware->sz_fw_tmpbufferlen = firmware->size; release_firmware(firmware); err = ieee80211_register_hw(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't register mac80211 hw\n"); return; } else { rtlpriv->mac80211.mac80211_registered = 1; } rtlpci->irq_alloc = 1; set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status); /*init rfkill */ rtl_init_rfkill(hw); } static int rtl92s_init_sw_vars(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); int err = 0; u16 earlyrxthreshold = 7; rtlpriv->dm.dm_initialgain_enable = true; rtlpriv->dm.dm_flag = 0; rtlpriv->dm.disable_framebursting = false; rtlpriv->dm.thermalvalue = 0; rtlpriv->dm.useramask = true; /* compatible 5G band 91se just 2.4G band & smsp */ rtlpriv->rtlhal.current_bandtype = BAND_ON_2_4G; rtlpriv->rtlhal.bandset = BAND_ON_2_4G; rtlpriv->rtlhal.macphymode = SINGLEMAC_SINGLEPHY; rtlpci->transmit_config = 0; rtlpci->receive_config = RCR_APPFCS | RCR_APWRMGT | /*RCR_ADD3 |*/ RCR_AMF | RCR_ADF | RCR_APP_MIC | RCR_APP_ICV | RCR_AICV | /* Accept ICV error, CRC32 Error */ RCR_ACRC32 | RCR_AB | /* Accept Broadcast, Multicast */ RCR_AM | /* Accept Physical match */ RCR_APM | /* Accept Destination Address packets */ /*RCR_AAP |*/ RCR_APP_PHYST_STAFF | /* Accept PHY status */ RCR_APP_PHYST_RXFF | (earlyrxthreshold << RCR_FIFO_OFFSET); rtlpci->irq_mask[0] = (u32) (IMR_ROK | IMR_VODOK | IMR_VIDOK | IMR_BEDOK | IMR_BKDOK | IMR_HCCADOK | IMR_MGNTDOK | IMR_COMDOK | IMR_HIGHDOK | IMR_BDOK | IMR_RXCMDOK | /*IMR_TIMEOUT0 |*/ IMR_RDU | IMR_RXFOVW | IMR_BCNINT /*| IMR_TXFOVW*/ /*| IMR_TBDOK | IMR_TBDER*/); rtlpci->irq_mask[1] = (u32) 0; rtlpci->shortretry_limit = 0x30; rtlpci->longretry_limit = 0x30; rtlpci->first_init = true; /* for debug level */ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug; /* for LPS & IPS */ rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; if (!rtlpriv->psc.inactiveps) pr_info("Power Save off (module option)\n"); if (!rtlpriv->psc.fwctrl_lps) pr_info("FW Power Save off (module option)\n"); rtlpriv->psc.reg_fwctrl_lps = 3; rtlpriv->psc.reg_max_lps_awakeintvl = 5; /* for ASPM, you can close aspm through * set const_support_pciaspm = 0 */ rtl92s_init_aspm_vars(hw); if (rtlpriv->psc.reg_fwctrl_lps == 1) rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE; else if (rtlpriv->psc.reg_fwctrl_lps == 2) rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE; else if (rtlpriv->psc.reg_fwctrl_lps == 3) rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE; /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(sizeof(struct rt_firmware)); if (!rtlpriv->rtlhal.pfirmware) return 1; rtlpriv->max_fw_size = RTL8190_MAX_RAW_FIRMWARE_CODE_SIZE; pr_info("Driver for Realtek RTL8192SE/RTL8191SE\n" "Loading firmware %s\n", rtlpriv->cfg->fw_name); /* request fw */ err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl92se_fw_cb); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to request firmware!\n"); return 1; } return err; } static void rtl92s_deinit_sw_vars(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->rtlhal.pfirmware) { vfree(rtlpriv->rtlhal.pfirmware); rtlpriv->rtlhal.pfirmware = NULL; } } static struct rtl_hal_ops rtl8192se_hal_ops = { .init_sw_vars = rtl92s_init_sw_vars, .deinit_sw_vars = rtl92s_deinit_sw_vars, .read_eeprom_info = rtl92se_read_eeprom_info, .interrupt_recognized = rtl92se_interrupt_recognized, .hw_init = rtl92se_hw_init, .hw_disable = rtl92se_card_disable, .hw_suspend = rtl92se_suspend, .hw_resume = rtl92se_resume, .enable_interrupt = rtl92se_enable_interrupt, .disable_interrupt = rtl92se_disable_interrupt, .set_network_type = rtl92se_set_network_type, .set_chk_bssid = rtl92se_set_check_bssid, .set_qos = rtl92se_set_qos, .set_bcn_reg = rtl92se_set_beacon_related_registers, .set_bcn_intv = rtl92se_set_beacon_interval, .update_interrupt_mask = rtl92se_update_interrupt_mask, .get_hw_reg = rtl92se_get_hw_reg, .set_hw_reg = rtl92se_set_hw_reg, .update_rate_tbl = rtl92se_update_hal_rate_tbl, .fill_tx_desc = rtl92se_tx_fill_desc, .fill_tx_cmddesc = rtl92se_tx_fill_cmddesc, .query_rx_desc = rtl92se_rx_query_desc, .set_channel_access = rtl92se_update_channel_access_setting, .radio_onoff_checking = rtl92se_gpio_radio_on_off_checking, .set_bw_mode = rtl92s_phy_set_bw_mode, .switch_channel = rtl92s_phy_sw_chnl, .dm_watchdog = rtl92s_dm_watchdog, .scan_operation_backup = rtl92s_phy_scan_operation_backup, .set_rf_power_state = rtl92s_phy_set_rf_power_state, .led_control = rtl92se_led_control, .set_desc = rtl92se_set_desc, .get_desc = rtl92se_get_desc, .tx_polling = rtl92se_tx_polling, .enable_hw_sec = rtl92se_enable_hw_security_config, .set_key = rtl92se_set_key, .init_sw_leds = rtl92se_init_sw_leds, .get_bbreg = rtl92s_phy_query_bb_reg, .set_bbreg = rtl92s_phy_set_bb_reg, .get_rfreg = rtl92s_phy_query_rf_reg, .set_rfreg = rtl92s_phy_set_rf_reg, }; static struct rtl_mod_params rtl92se_mod_params = { .sw_crypto = false, .inactiveps = true, .swctrl_lps = true, .fwctrl_lps = false, .debug = DBG_EMERG, }; /* Because memory R/W bursting will cause system hang/crash * for 92se, so we don't read back after every write action */ static struct rtl_hal_cfg rtl92se_hal_cfg = { .bar_id = 1, .write_readback = false, .name = "rtl92s_pci", .fw_name = "rtlwifi/rtl8192sefw.bin", .ops = &rtl8192se_hal_ops, .mod_params = &rtl92se_mod_params, .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN, .maps[SYS_CLK] = SYS_CLKR, .maps[MAC_RCR_AM] = RCR_AM, .maps[MAC_RCR_AB] = RCR_AB, .maps[MAC_RCR_ACRC32] = RCR_ACRC32, .maps[MAC_RCR_ACF] = RCR_ACF, .maps[MAC_RCR_AAP] = RCR_AAP, .maps[EFUSE_TEST] = REG_EFUSE_TEST, .maps[EFUSE_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_CLK] = REG_EFUSE_CLK, .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_PWC_EV12V] = 0, /* nouse for 8192se */ .maps[EFUSE_FEN_ELDR] = 0, /* nouse for 8192se */ .maps[EFUSE_LOADER_CLK_EN] = 0,/* nouse for 8192se */ .maps[EFUSE_ANA8M] = EFUSE_ANA8M, .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE_92S, .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION, .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN, .maps[EFUSE_OOB_PROTECT_BYTES_LEN] = EFUSE_OOB_PROTECT_BYTES, .maps[RWCAM] = REG_RWCAM, .maps[WCAMI] = REG_WCAMI, .maps[RCAMO] = REG_RCAMO, .maps[CAMDBG] = REG_CAMDBG, .maps[SECR] = REG_SECR, .maps[SEC_CAM_NONE] = CAM_NONE, .maps[SEC_CAM_WEP40] = CAM_WEP40, .maps[SEC_CAM_TKIP] = CAM_TKIP, .maps[SEC_CAM_AES] = CAM_AES, .maps[SEC_CAM_WEP104] = CAM_WEP104, .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6, .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5, .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4, .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3, .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2, .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1, .maps[RTL_IMR_BCNDOK8] = IMR_BCNDOK8, .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7, .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6, .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5, .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4, .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3, .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2, .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1, .maps[RTL_IMR_TIMEOUT2] = IMR_TIMEOUT2, .maps[RTL_IMR_TIMEOUT1] = IMR_TIMEOUT1, .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW, .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT, .maps[RTL_IMR_BcnInt] = IMR_BCNINT, .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW, .maps[RTL_IMR_RDU] = IMR_RDU, .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND, .maps[RTL_IMR_BDOK] = IMR_BDOK, .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK, .maps[RTL_IMR_TBDER] = IMR_TBDER, .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK, .maps[RTL_IMR_COMDOK] = IMR_COMDOK, .maps[RTL_IMR_TBDOK] = IMR_TBDOK, .maps[RTL_IMR_BKDOK] = IMR_BKDOK, .maps[RTL_IMR_BEDOK] = IMR_BEDOK, .maps[RTL_IMR_VIDOK] = IMR_VIDOK, .maps[RTL_IMR_VODOK] = IMR_VODOK, .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, }; static struct pci_device_id rtl92se_pci_ids[] __devinitdata = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8192, rtl92se_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8171, rtl92se_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8172, rtl92se_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8173, rtl92se_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8174, rtl92se_hal_cfg)}, {}, }; MODULE_DEVICE_TABLE(pci, rtl92se_pci_ids); MODULE_AUTHOR("lizhaoming "); MODULE_AUTHOR("Realtek WlanFAE "); MODULE_AUTHOR("Larry Finger "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8192S/8191S 802.11n PCI wireless"); MODULE_FIRMWARE("rtlwifi/rtl8192sefw.bin"); module_param_named(swenc, rtl92se_mod_params.sw_crypto, bool, 0444); module_param_named(debug, rtl92se_mod_params.debug, int, 0444); module_param_named(ips, rtl92se_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl92se_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92se_mod_params.fwctrl_lps, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); compat_pci_suspend(rtl_pci_suspend) compat_pci_resume(rtl_pci_resume) static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); static struct pci_driver rtl92se_driver = { .name = KBUILD_MODNAME, .id_table = rtl92se_pci_ids, .probe = rtl_pci_probe, .remove = rtl_pci_disconnect, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &rtlwifi_pm_ops, #elif defined(CONFIG_PM) .suspend = rtl_pci_suspend_compat, .resume = rtl_pci_resume_compat, #endif }; module_pci_driver(rtl92se_driver); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/trx.h0000644000175000017500000000365312026211315024613 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __REALTEK_PCI92SE_TRX_H__ #define __REALTEK_PCI92SE_TRX_H__ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb); bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); u32 rtl92se_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl92se_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/trx.c0000644000175000017500000006054412026211315024610 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" #include "fw.h" #include "trx.h" #include "led.h" static u8 _rtl92se_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 skb_queue) { __le16 fc = rtl_get_fc(skb); if (unlikely(ieee80211_is_beacon(fc))) return QSLT_BEACON; if (ieee80211_is_mgmt(fc)) return QSLT_MGNT; if (ieee80211_is_nullfunc(fc)) return QSLT_HIGH; return skb->priority; } static u8 _rtl92s_query_rxpwrpercentage(char antpower) { if ((antpower <= -100) || (antpower >= 20)) return 0; else if (antpower >= 0) return 100; else return 100 + antpower; } static u8 _rtl92s_evm_db_to_percentage(char value) { char ret_val; ret_val = value; if (ret_val >= 0) ret_val = 0; if (ret_val <= -33) ret_val = -33; ret_val = 0 - ret_val; ret_val *= 3; if (ret_val == 99) ret_val = 100; return ret_val; } static long _rtl92se_translate_todbm(struct ieee80211_hw *hw, u8 signal_strength_index) { long signal_power; signal_power = (long)((signal_strength_index + 1) >> 1); signal_power -= 95; return signal_power; } static long _rtl92se_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { long retsig = 0; /* Step 1. Scale mapping. */ if (currsig > 47) retsig = 100; else if (currsig > 14 && currsig <= 47) retsig = 100 - ((47 - currsig) * 3) / 2; else if (currsig > 2 && currsig <= 14) retsig = 48 - ((14 - currsig) * 15) / 7; else if (currsig >= 0) retsig = currsig * 9 + 1; return retsig; } static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, u8 *pdesc, struct rx_fwinfo *p_drvinfo, bool packet_match_bssid, bool packet_toself, bool packet_beacon) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct phy_sts_cck_8192s_t *cck_buf; s8 rx_pwr_all = 0, rx_pwr[4]; u8 rf_rx_num = 0, evm, pwdb_all; u8 i, max_spatial_stream; u32 rssi, total_rssi = 0; bool in_powersavemode = false; bool is_cck = pstats->is_cck; pstats->packet_matchbssid = packet_match_bssid; pstats->packet_toself = packet_toself; pstats->packet_beacon = packet_beacon; pstats->rx_mimo_signalquality[0] = -1; pstats->rx_mimo_signalquality[1] = -1; if (is_cck) { u8 report, cck_highpwr; cck_buf = (struct phy_sts_cck_8192s_t *)p_drvinfo; if (!in_powersavemode) cck_highpwr = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, 0x200); else cck_highpwr = false; if (!cck_highpwr) { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = cck_buf->cck_agc_rpt & 0xc0; report = report >> 6; switch (report) { case 0x3: rx_pwr_all = -40 - (cck_agc_rpt & 0x3e); break; case 0x2: rx_pwr_all = -20 - (cck_agc_rpt & 0x3e); break; case 0x1: rx_pwr_all = -2 - (cck_agc_rpt & 0x3e); break; case 0x0: rx_pwr_all = 14 - (cck_agc_rpt & 0x3e); break; } } else { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = p_drvinfo->cfosho[0] & 0x60; report = report >> 5; switch (report) { case 0x3: rx_pwr_all = -40 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x2: rx_pwr_all = -20 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x1: rx_pwr_all = -2 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x0: rx_pwr_all = 14 - ((cck_agc_rpt & 0x1f) << 1); break; } } pwdb_all = _rtl92s_query_rxpwrpercentage(rx_pwr_all); /* CCK gain is smaller than OFDM/MCS gain, */ /* so we add gain diff by experiences, the val is 6 */ pwdb_all += 6; if (pwdb_all > 100) pwdb_all = 100; /* modify the offset to make the same gain index with OFDM. */ if (pwdb_all > 34 && pwdb_all <= 42) pwdb_all -= 2; else if (pwdb_all > 26 && pwdb_all <= 34) pwdb_all -= 6; else if (pwdb_all > 14 && pwdb_all <= 26) pwdb_all -= 8; else if (pwdb_all > 4 && pwdb_all <= 14) pwdb_all -= 4; pstats->rx_pwdb_all = pwdb_all; pstats->recvsignalpower = rx_pwr_all; if (packet_match_bssid) { u8 sq; if (pstats->rx_pwdb_all > 40) { sq = 100; } else { sq = cck_buf->sq_rpt; if (sq > 64) sq = 0; else if (sq < 20) sq = 100; else sq = ((64 - sq) * 100) / 44; } pstats->signalquality = sq; pstats->rx_mimo_signalquality[0] = sq; pstats->rx_mimo_signalquality[1] = -1; } } else { rtlpriv->dm.rfpath_rxenable[0] = rtlpriv->dm.rfpath_rxenable[1] = true; for (i = RF90_PATH_A; i < RF90_PATH_MAX; i++) { if (rtlpriv->dm.rfpath_rxenable[i]) rf_rx_num++; rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110; rssi = _rtl92s_query_rxpwrpercentage(rx_pwr[i]); total_rssi += rssi; rtlpriv->stats.rx_snr_db[i] = (long)(p_drvinfo->rxsnr[i] / 2); if (packet_match_bssid) pstats->rx_mimo_signalstrength[i] = (u8) rssi; } rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110; pwdb_all = _rtl92s_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 && pstats->rate <= DESC92_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; for (i = 0; i < max_spatial_stream; i++) { evm = _rtl92s_evm_db_to_percentage(p_drvinfo->rxevm[i]); if (packet_match_bssid) { if (i == 0) pstats->signalquality = (u8)(evm & 0xff); pstats->rx_mimo_signalquality[i] = (u8) (evm & 0xff); } } } if (is_cck) pstats->signalstrength = (u8)(_rtl92se_signal_scale_mapping(hw, pwdb_all)); else if (rf_rx_num != 0) pstats->signalstrength = (u8) (_rtl92se_signal_scale_mapping(hw, total_rssi /= rf_rx_num)); } static void _rtl92se_process_ui_rssi(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 rfpath; u32 last_rssi, tmpval; if (pstats->packet_toself || pstats->packet_beacon) { rtlpriv->stats.rssi_calculate_cnt++; if (rtlpriv->stats.ui_rssi.total_num++ >= PHY_RSSI_SLID_WIN_MAX) { rtlpriv->stats.ui_rssi.total_num = PHY_RSSI_SLID_WIN_MAX; last_rssi = rtlpriv->stats.ui_rssi.elements[ rtlpriv->stats.ui_rssi.index]; rtlpriv->stats.ui_rssi.total_val -= last_rssi; } rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength; rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi.index++] = pstats->signalstrength; if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX) rtlpriv->stats.ui_rssi.index = 0; tmpval = rtlpriv->stats.ui_rssi.total_val / rtlpriv->stats.ui_rssi.total_num; rtlpriv->stats.signal_strength = _rtl92se_translate_todbm(hw, (u8) tmpval); pstats->rssi = rtlpriv->stats.signal_strength; } if (!pstats->is_cck && pstats->packet_toself) { for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) { rtlpriv->stats.rx_rssi_percentage[rfpath] = pstats->rx_mimo_signalstrength[rfpath]; } if (pstats->rx_mimo_signalstrength[rfpath] > rtlpriv->stats.rx_rssi_percentage[rfpath]) { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats.rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); rtlpriv->stats.rx_rssi_percentage[rfpath] = rtlpriv->stats.rx_rssi_percentage[rfpath] + 1; } else { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats.rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); } } } } static void _rtl92se_update_rxsignalstatistics(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); int weighting = 0; if (rtlpriv->stats.recv_signal_power == 0) rtlpriv->stats.recv_signal_power = pstats->recvsignalpower; if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power) weighting = 5; else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power) weighting = (-5); rtlpriv->stats.recv_signal_power = (rtlpriv->stats.recv_signal_power * 5 + pstats->recvsignalpower + weighting) / 6; } static void _rtl92se_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undec_sm_pwdb = 0; if (mac->opmode == NL80211_IFTYPE_ADHOC) { return; } else { undec_sm_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; } if (pstats->packet_toself || pstats->packet_beacon) { if (undec_sm_pwdb < 0) undec_sm_pwdb = pstats->rx_pwdb_all; if (pstats->rx_pwdb_all > (u32) undec_sm_pwdb) { undec_sm_pwdb = (((undec_sm_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); undec_sm_pwdb = undec_sm_pwdb + 1; } else { undec_sm_pwdb = (((undec_sm_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); } rtlpriv->dm.undecorated_smoothed_pwdb = undec_sm_pwdb; _rtl92se_update_rxsignalstatistics(hw, pstats); } } static void rtl_92s_process_streams(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 stream; for (stream = 0; stream < 2; stream++) { if (pstats->rx_mimo_signalquality[stream] != -1) { if (rtlpriv->stats.rx_evm_percentage[stream] == 0) { rtlpriv->stats.rx_evm_percentage[stream] = pstats->rx_mimo_signalquality[stream]; } rtlpriv->stats.rx_evm_percentage[stream] = ((rtlpriv->stats.rx_evm_percentage[stream] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalquality[stream] * 1)) / (RX_SMOOTH_FACTOR); } } } static void _rtl92se_process_ui_link_quality(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 last_evm = 0, tmpval; if (pstats->signalquality != 0) { if (pstats->packet_toself || pstats->packet_beacon) { if (rtlpriv->stats.ui_link_quality.total_num++ >= PHY_LINKQUALITY_SLID_WIN_MAX) { rtlpriv->stats.ui_link_quality.total_num = PHY_LINKQUALITY_SLID_WIN_MAX; last_evm = rtlpriv->stats.ui_link_quality.elements[ rtlpriv->stats.ui_link_quality.index]; rtlpriv->stats.ui_link_quality.total_val -= last_evm; } rtlpriv->stats.ui_link_quality.total_val += pstats->signalquality; rtlpriv->stats.ui_link_quality.elements[ rtlpriv->stats.ui_link_quality.index++] = pstats->signalquality; if (rtlpriv->stats.ui_link_quality.index >= PHY_LINKQUALITY_SLID_WIN_MAX) rtlpriv->stats.ui_link_quality.index = 0; tmpval = rtlpriv->stats.ui_link_quality.total_val / rtlpriv->stats.ui_link_quality.total_num; rtlpriv->stats.signal_quality = tmpval; rtlpriv->stats.last_sigstrength_inpercent = tmpval; rtl_92s_process_streams(hw, pstats); } } } static void _rtl92se_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer, struct rtl_stats *pcurrent_stats) { if (!pcurrent_stats->packet_matchbssid && !pcurrent_stats->packet_beacon) return; _rtl92se_process_ui_rssi(hw, pcurrent_stats); _rtl92se_process_pwdb(hw, pcurrent_stats); _rtl92se_process_ui_link_quality(hw, pcurrent_stats); } static void _rtl92se_translate_rx_signal_stuff(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_stats *pstats, u8 *pdesc, struct rx_fwinfo *p_drvinfo) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct ieee80211_hdr *hdr; u8 *tmp_buf; u8 *praddr; __le16 fc; u16 type, cfc; bool packet_matchbssid, packet_toself, packet_beacon; tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift; hdr = (struct ieee80211_hdr *)tmp_buf; fc = hdr->frame_control; cfc = le16_to_cpu(fc); type = WLAN_FC_GET_TYPE(fc); praddr = hdr->addr1; packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) && ether_addr_equal(mac->bssid, (cfc & IEEE80211_FCTL_TODS) ? hdr->addr1 : (cfc & IEEE80211_FCTL_FROMDS) ? hdr->addr2 : hdr->addr3) && (!pstats->hwerror) && (!pstats->crc) && (!pstats->icv)); packet_toself = packet_matchbssid && ether_addr_equal(praddr, rtlefuse->dev_addr); if (ieee80211_is_beacon(fc)) packet_beacon = true; _rtl92se_query_rxphystatus(hw, pstats, pdesc, p_drvinfo, packet_matchbssid, packet_toself, packet_beacon); _rtl92se_process_phyinfo(hw, tmp_buf, pstats); } bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb) { struct rx_fwinfo *p_drvinfo; u32 phystatus = (u32)GET_RX_STATUS_DESC_PHY_STATUS(pdesc); struct ieee80211_hdr *hdr; bool first_ampdu = false; stats->length = (u16)GET_RX_STATUS_DESC_PKT_LEN(pdesc); stats->rx_drvinfo_size = (u8)GET_RX_STATUS_DESC_DRVINFO_SIZE(pdesc) * 8; stats->rx_bufshift = (u8)(GET_RX_STATUS_DESC_SHIFT(pdesc) & 0x03); stats->icv = (u16)GET_RX_STATUS_DESC_ICV(pdesc); stats->crc = (u16)GET_RX_STATUS_DESC_CRC32(pdesc); stats->hwerror = (u16)(stats->crc | stats->icv); stats->decrypted = !GET_RX_STATUS_DESC_SWDEC(pdesc); stats->rate = (u8)GET_RX_STATUS_DESC_RX_MCS(pdesc); stats->shortpreamble = (u16)GET_RX_STATUS_DESC_SPLCP(pdesc); stats->isampdu = (bool)(GET_RX_STATUS_DESC_PAGGR(pdesc) == 1); stats->isfirst_ampdu = (bool) ((GET_RX_STATUS_DESC_PAGGR(pdesc) == 1) && (GET_RX_STATUS_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_STATUS_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool)GET_RX_STATUS_DESC_BW(pdesc); stats->is_ht = (bool)GET_RX_STATUS_DESC_RX_HT(pdesc); stats->is_cck = SE_RX_HAL_IS_CCK_RATE(pdesc); if (stats->hwerror) return false; rx_status->freq = hw->conf.channel->center_freq; rx_status->band = hw->conf.channel->band; hdr = (struct ieee80211_hdr *)(skb->data + stats->rx_drvinfo_size + stats->rx_bufshift); if (stats->crc) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (stats->rx_is40Mhzpacket) rx_status->flag |= RX_FLAG_40MHZ; if (stats->is_ht) rx_status->flag |= RX_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_MPDU; /* hw will set stats->decrypted true, if it finds the * frame is open data frame or mgmt frame, * hw will not decrypt robust managment frame * for IEEE80211w but still set stats->decrypted * true, so here we should set it back to undecrypted * for IEEE80211w frame, and mac80211 sw will help * to decrypt it */ if (stats->decrypted) { if ((ieee80211_is_robust_mgmt_frame(hdr)) && (ieee80211_has_protected(hdr->frame_control))) rx_status->flag &= ~RX_FLAG_DECRYPTED; else rx_status->flag |= RX_FLAG_DECRYPTED; } rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, stats->rate, first_ampdu); rx_status->mactime = stats->timestamp_low; if (phystatus) { p_drvinfo = (struct rx_fwinfo *)(skb->data + stats->rx_bufshift); _rtl92se_translate_rx_signal_stuff(hw, skb, stats, pdesc, p_drvinfo); } /*rx_status->qual = stats->signal; */ rx_status->signal = stats->rssi + 10; /*rx_status->noise = -stats->noise; */ return true; } void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; u8 reserved_macid = 0; u8 fw_qsel = _rtl92se_map_hwqueue_to_fwqueue(skb, hw_queue); bool firstseg = (!(hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG))); bool lastseg = (!(hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS))); dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); u8 bw_40 = 0; if (mac->opmode == NL80211_IFTYPE_STATION) { bw_40 = mac->bw_40; } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; } seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc); CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE_RTL8192S); if (firstseg) { if (rtlpriv->dm.useramask) { /* set txdesc macId */ if (ptcb_desc->mac_id < 32) { SET_TX_DESC_MACID(pdesc, ptcb_desc->mac_id); reserved_macid |= ptcb_desc->mac_id; } } SET_TX_DESC_RSVD_MACID(pdesc, reserved_macid); SET_TX_DESC_TXHT(pdesc, ((ptcb_desc->hw_rate >= DESC92_RATEMCS0) ? 1 : 0)); if (rtlhal->version == VERSION_8192S_ACUT) { if (ptcb_desc->hw_rate == DESC92_RATE1M || ptcb_desc->hw_rate == DESC92_RATE2M || ptcb_desc->hw_rate == DESC92_RATE5_5M || ptcb_desc->hw_rate == DESC92_RATE11M) { ptcb_desc->hw_rate = DESC92_RATE12M; } } SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate); if (ptcb_desc->use_shortgi || ptcb_desc->use_shortpreamble) SET_TX_DESC_TX_SHORT(pdesc, 0); /* Aggregation related */ if (info->flags & IEEE80211_TX_CTL_AMPDU) SET_TX_DESC_AGG_ENABLE(pdesc, 1); /* For AMPDU, we must insert SSN into TX_DESC */ SET_TX_DESC_SEQ(pdesc, seq_number); /* Protection mode related */ /* For 92S, if RTS/CTS are set, HW will execute RTS. */ /* We choose only one protection mode to execute */ SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable && !ptcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_CTS_ENABLE(pdesc, ((ptcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0)); SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); SET_TX_DESC_RTS_BANDWIDTH(pdesc, 0); SET_TX_DESC_RTS_SUB_CARRIER(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <= DESC92_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); /* Set Bandwidth and sub-channel settings. */ if (bw_40) { if (ptcb_desc->packet_bw) { SET_TX_DESC_TX_BANDWIDTH(pdesc, 1); /* use duplicated mode */ SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0); } else { SET_TX_DESC_TX_BANDWIDTH(pdesc, 0); SET_TX_DESC_TX_SUB_CARRIER(pdesc, mac->cur_40_prime_sc); } } else { SET_TX_DESC_TX_BANDWIDTH(pdesc, 0); SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0); } /* 3 Fill necessary field in First Descriptor */ /*DWORD 0*/ SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_OFFSET(pdesc, 32); SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb->len); /*DWORD 1*/ SET_TX_DESC_RA_BRSR_ID(pdesc, ptcb_desc->ratr_index); /* Fill security related */ if (info->control.hw_key) { struct ieee80211_key_conf *keyconf; keyconf = info->control.hw_key; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: SET_TX_DESC_SEC_TYPE(pdesc, 0x1); break; case WLAN_CIPHER_SUITE_TKIP: SET_TX_DESC_SEC_TYPE(pdesc, 0x2); break; case WLAN_CIPHER_SUITE_CCMP: SET_TX_DESC_SEC_TYPE(pdesc, 0x3); break; default: SET_TX_DESC_SEC_TYPE(pdesc, 0x0); break; } } /* Set Packet ID */ SET_TX_DESC_PACKET_ID(pdesc, 0); /* We will assign magement queue to BK. */ SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel); /* Alwasy enable all rate fallback range */ SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F); /* Fix: I don't kown why hw use 6.5M to tx when set it */ SET_TX_DESC_USER_RATE(pdesc, ptcb_desc->use_driver_rate ? 1 : 0); /* Set NON_QOS bit. */ if (!ieee80211_is_data_qos(fc)) SET_TX_DESC_NON_QOS(pdesc, 1); } /* Fill fields that are required to be initialized * in all of the descriptors */ /*DWORD 0 */ SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0)); SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0)); /* DWORD 7 */ SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) skb->len); /* DOWRD 8 */ SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n"); } void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_tcb_desc *tcb_desc = (struct rtl_tcb_desc *)(skb->cb); dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); /* Clear all status */ CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_CMDDESC_SIZE_RTL8192S); /* This bit indicate this packet is used for FW download. */ if (tcb_desc->cmd_or_init == DESC_PACKET_TYPE_INIT) { /* For firmware downlaod we only need to set LINIP */ SET_TX_DESC_LINIP(pdesc, tcb_desc->last_inipkt); /* 92SE must set as 1 for firmware download HW DMA error */ SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); /* 92SE need not to set TX packet size when firmware download */ SET_TX_DESC_PKT_SIZE(pdesc, (u16)(skb->len)); SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)(skb->len)); SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); wmb(); SET_TX_DESC_OWN(pdesc, 1); } else { /* H2C Command Desc format (Host TXCMD) */ /* 92SE must set as 1 for firmware download HW DMA error */ SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_OFFSET(pdesc, 0x20); /* Buffer size + command header */ SET_TX_DESC_PKT_SIZE(pdesc, (u16)(skb->len)); /* Fixed queue of H2C command */ SET_TX_DESC_QUEUE_SEL(pdesc, 0x13); SET_BITS_TO_LE_4BYTE(skb->data, 24, 7, rtlhal->h2c_txcmd_seq); SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)(skb->len)); SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); wmb(); SET_TX_DESC_OWN(pdesc, 1); } } void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) { if (istx) { switch (desc_name) { case HW_DESC_OWN: wmb(); SET_TX_DESC_OWN(pdesc, 1); break; case HW_DESC_TX_NEXTDESC_ADDR: SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *) val); break; default: RT_ASSERT(false, "ERR txdesc :%d not process\n", desc_name); break; } } else { switch (desc_name) { case HW_DESC_RXOWN: wmb(); SET_RX_STATUS_DESC_OWN(pdesc, 1); break; case HW_DESC_RXBUFF_ADDR: SET_RX_STATUS__DESC_BUFF_ADDR(pdesc, *(u32 *) val); break; case HW_DESC_RXPKT_LEN: SET_RX_STATUS_DESC_PKT_LEN(pdesc, *(u32 *) val); break; case HW_DESC_RXERO: SET_RX_STATUS_DESC_EOR(pdesc, 1); break; default: RT_ASSERT(false, "ERR rxdesc :%d not process\n", desc_name); break; } } } u32 rtl92se_get_desc(u8 *desc, bool istx, u8 desc_name) { u32 ret = 0; if (istx) { switch (desc_name) { case HW_DESC_OWN: ret = GET_TX_DESC_OWN(desc); break; case HW_DESC_TXBUFF_ADDR: ret = GET_TX_DESC_TX_BUFFER_ADDRESS(desc); break; default: RT_ASSERT(false, "ERR txdesc :%d not process\n", desc_name); break; } } else { switch (desc_name) { case HW_DESC_OWN: ret = GET_RX_STATUS_DESC_OWN(desc); break; case HW_DESC_RXPKT_LEN: ret = GET_RX_STATUS_DESC_PKT_LEN(desc); break; default: RT_ASSERT(false, "ERR rxdesc :%d not process\n", desc_name); break; } } return ret; } void rtl92se_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_word(rtlpriv, TP_POLL, BIT(0) << (hw_queue)); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/table.h0000644000175000017500000000356512026211315025067 0ustar mcgrofmcgrof/****************************************************************************** * Copyright(c) 2008 - 2012 Realtek Corporation. All rights reserved. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * * Larry Finger * ******************************************************************************/ #ifndef __INC_HAL8192SE_FW_IMG_H #define __INC_HAL8192SE_FW_IMG_H #include /*Created on 2010/ 4/12, 5:56*/ #define PHY_REG_2T2RARRAYLENGTH 372 extern u32 rtl8192sephy_reg_2t2rarray[PHY_REG_2T2RARRAYLENGTH]; #define PHY_CHANGETO_1T1RARRAYLENGTH 48 extern u32 rtl8192sephy_changeto_1t1rarray[PHY_CHANGETO_1T1RARRAYLENGTH]; #define PHY_CHANGETO_1T2RARRAYLENGTH 45 extern u32 rtl8192sephy_changeto_1t2rarray[PHY_CHANGETO_1T2RARRAYLENGTH]; #define PHY_REG_ARRAY_PGLENGTH 84 extern u32 rtl8192sephy_reg_array_pg[PHY_REG_ARRAY_PGLENGTH]; #define RADIOA_1T_ARRAYLENGTH 202 extern u32 rtl8192seradioa_1t_array[RADIOA_1T_ARRAYLENGTH]; #define RADIOB_ARRAYLENGTH 22 extern u32 rtl8192seradiob_array[RADIOB_ARRAYLENGTH]; #define RADIOB_GM_ARRAYLENGTH 10 extern u32 rtl8192seradiob_gm_array[RADIOB_GM_ARRAYLENGTH]; #define MAC_2T_ARRAYLENGTH 106 extern u32 rtl8192semac_2t_array[MAC_2T_ARRAYLENGTH]; #define AGCTAB_ARRAYLENGTH 320 extern u32 rtl8192seagctab_array[AGCTAB_ARRAYLENGTH]; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/table.c0000644000175000017500000003325312026211315025057 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * * Created on 2010/ 5/18, 1:41 *****************************************************************************/ #include "table.h" u32 rtl8192sephy_reg_2t2rarray[PHY_REG_2T2RARRAYLENGTH] = { 0x01c, 0x07000000, 0x800, 0x00040000, 0x804, 0x00008003, 0x808, 0x0000fc00, 0x80c, 0x0000000a, 0x810, 0x10005088, 0x814, 0x020c3d10, 0x818, 0x00200185, 0x81c, 0x00000000, 0x820, 0x01000000, 0x824, 0x00390004, 0x828, 0x01000000, 0x82c, 0x00390004, 0x830, 0x00000004, 0x834, 0x00690200, 0x838, 0x00000004, 0x83c, 0x00690200, 0x840, 0x00010000, 0x844, 0x00010000, 0x848, 0x00000000, 0x84c, 0x00000000, 0x850, 0x00000000, 0x854, 0x00000000, 0x858, 0x48484848, 0x85c, 0x65a965a9, 0x860, 0x0f7f0130, 0x864, 0x0f7f0130, 0x868, 0x0f7f0130, 0x86c, 0x0f7f0130, 0x870, 0x03000700, 0x874, 0x03000300, 0x878, 0x00020002, 0x87c, 0x004f0201, 0x880, 0xa8300ac1, 0x884, 0x00000058, 0x888, 0x00000008, 0x88c, 0x00000004, 0x890, 0x00000000, 0x894, 0xfffffffe, 0x898, 0x40302010, 0x89c, 0x00706050, 0x8b0, 0x00000000, 0x8e0, 0x00000000, 0x8e4, 0x00000000, 0xe00, 0x30333333, 0xe04, 0x2a2d2e2f, 0xe08, 0x00003232, 0xe10, 0x30333333, 0xe14, 0x2a2d2e2f, 0xe18, 0x30333333, 0xe1c, 0x2a2d2e2f, 0xe30, 0x01007c00, 0xe34, 0x01004800, 0xe38, 0x1000dc1f, 0xe3c, 0x10008c1f, 0xe40, 0x021400a0, 0xe44, 0x281600a0, 0xe48, 0xf8000001, 0xe4c, 0x00002910, 0xe50, 0x01007c00, 0xe54, 0x01004800, 0xe58, 0x1000dc1f, 0xe5c, 0x10008c1f, 0xe60, 0x021400a0, 0xe64, 0x281600a0, 0xe6c, 0x00002910, 0xe70, 0x31ed92fb, 0xe74, 0x361536fb, 0xe78, 0x361536fb, 0xe7c, 0x361536fb, 0xe80, 0x361536fb, 0xe84, 0x000d92fb, 0xe88, 0x000d92fb, 0xe8c, 0x31ed92fb, 0xed0, 0x31ed92fb, 0xed4, 0x31ed92fb, 0xed8, 0x000d92fb, 0xedc, 0x000d92fb, 0xee0, 0x000d92fb, 0xee4, 0x015e5448, 0xee8, 0x21555448, 0x900, 0x00000000, 0x904, 0x00000023, 0x908, 0x00000000, 0x90c, 0x01121313, 0xa00, 0x00d047c8, 0xa04, 0x80ff0008, 0xa08, 0x8ccd8300, 0xa0c, 0x2e62120f, 0xa10, 0x9500bb78, 0xa14, 0x11144028, 0xa18, 0x00881117, 0xa1c, 0x89140f00, 0xa20, 0x1a1b0000, 0xa24, 0x090e1317, 0xa28, 0x00000204, 0xa2c, 0x10d30000, 0xc00, 0x40071d40, 0xc04, 0x00a05633, 0xc08, 0x000000e4, 0xc0c, 0x6c6c6c6c, 0xc10, 0x08800000, 0xc14, 0x40000100, 0xc18, 0x08000000, 0xc1c, 0x40000100, 0xc20, 0x08000000, 0xc24, 0x40000100, 0xc28, 0x08000000, 0xc2c, 0x40000100, 0xc30, 0x6de9ac44, 0xc34, 0x469652cf, 0xc38, 0x49795994, 0xc3c, 0x0a979764, 0xc40, 0x1f7c403f, 0xc44, 0x000100b7, 0xc48, 0xec020000, 0xc4c, 0x007f037f, 0xc50, 0x69543420, 0xc54, 0x433c0094, 0xc58, 0x69543420, 0xc5c, 0x433c0094, 0xc60, 0x69543420, 0xc64, 0x433c0094, 0xc68, 0x69543420, 0xc6c, 0x433c0094, 0xc70, 0x2c7f000d, 0xc74, 0x0186155b, 0xc78, 0x0000001f, 0xc7c, 0x00b91612, 0xc80, 0x40000100, 0xc84, 0x20f60000, 0xc88, 0x20000080, 0xc8c, 0x20200000, 0xc90, 0x40000100, 0xc94, 0x00000000, 0xc98, 0x40000100, 0xc9c, 0x00000000, 0xca0, 0x00492492, 0xca4, 0x00000000, 0xca8, 0x00000000, 0xcac, 0x00000000, 0xcb0, 0x00000000, 0xcb4, 0x00000000, 0xcb8, 0x00000000, 0xcbc, 0x28000000, 0xcc0, 0x00000000, 0xcc4, 0x00000000, 0xcc8, 0x00000000, 0xccc, 0x00000000, 0xcd0, 0x00000000, 0xcd4, 0x00000000, 0xcd8, 0x64b22427, 0xcdc, 0x00766932, 0xce0, 0x00222222, 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, 0xd00, 0x00000750, 0xd04, 0x00000403, 0xd08, 0x0000907f, 0xd0c, 0x00000001, 0xd10, 0xa0633333, 0xd14, 0x33333c63, 0xd18, 0x6a8f5b6b, 0xd1c, 0x00000000, 0xd20, 0x00000000, 0xd24, 0x00000000, 0xd28, 0x00000000, 0xd2c, 0xcc979975, 0xd30, 0x00000000, 0xd34, 0x00000000, 0xd38, 0x00000000, 0xd3c, 0x00027293, 0xd40, 0x00000000, 0xd44, 0x00000000, 0xd48, 0x00000000, 0xd50, 0x6437140a, 0xd54, 0x024dbd02, 0xd58, 0x00000000, 0xd5c, 0x30032064, 0xd60, 0x4653de68, 0xd64, 0x00518a3c, 0xd68, 0x00002101, 0xf14, 0x00000003, 0xf4c, 0x00000000, 0xf00, 0x00000300, }; u32 rtl8192sephy_changeto_1t1rarray[PHY_CHANGETO_1T1RARRAYLENGTH] = { 0x844, 0xffffffff, 0x00010000, 0x804, 0x0000000f, 0x00000001, 0x824, 0x00f0000f, 0x00300004, 0x82c, 0x00f0000f, 0x00100002, 0x870, 0x04000000, 0x00000001, 0x864, 0x00000400, 0x00000000, 0x878, 0x000f000f, 0x00000002, 0xe74, 0x0f000000, 0x00000002, 0xe78, 0x0f000000, 0x00000002, 0xe7c, 0x0f000000, 0x00000002, 0xe80, 0x0f000000, 0x00000002, 0x90c, 0x000000ff, 0x00000011, 0xc04, 0x000000ff, 0x00000011, 0xd04, 0x0000000f, 0x00000001, 0x1f4, 0xffff0000, 0x00007777, 0x234, 0xf8000000, 0x0000000a, }; u32 rtl8192sephy_changeto_1t2rarray[PHY_CHANGETO_1T2RARRAYLENGTH] = { 0x804, 0x0000000f, 0x00000003, 0x824, 0x00f0000f, 0x00300004, 0x82c, 0x00f0000f, 0x00300002, 0x870, 0x04000000, 0x00000001, 0x864, 0x00000400, 0x00000000, 0x878, 0x000f000f, 0x00000002, 0xe74, 0x0f000000, 0x00000002, 0xe78, 0x0f000000, 0x00000002, 0xe7c, 0x0f000000, 0x00000002, 0xe80, 0x0f000000, 0x00000002, 0x90c, 0x000000ff, 0x00000011, 0xc04, 0x000000ff, 0x00000033, 0xd04, 0x0000000f, 0x00000003, 0x1f4, 0xffff0000, 0x00007777, 0x234, 0xf8000000, 0x0000000a, }; u32 rtl8192sephy_reg_array_pg[PHY_REG_ARRAY_PGLENGTH] = { 0xe00, 0xffffffff, 0x06090909, 0xe04, 0xffffffff, 0x00030406, 0xe08, 0x0000ff00, 0x00000000, 0xe10, 0xffffffff, 0x0a0c0d0e, 0xe14, 0xffffffff, 0x04070809, 0xe18, 0xffffffff, 0x0a0c0d0e, 0xe1c, 0xffffffff, 0x04070809, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0xe10, 0xffffffff, 0x02040404, 0xe14, 0xffffffff, 0x00000002, 0xe18, 0xffffffff, 0x02040404, 0xe1c, 0xffffffff, 0x00000002, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0xe10, 0xffffffff, 0x02040404, 0xe14, 0xffffffff, 0x00000002, 0xe18, 0xffffffff, 0x02040404, 0xe1c, 0xffffffff, 0x00000002, 0xe00, 0xffffffff, 0x02020202, 0xe04, 0xffffffff, 0x00020202, 0xe08, 0x0000ff00, 0x00000000, 0xe10, 0xffffffff, 0x02020202, 0xe14, 0xffffffff, 0x00000002, 0xe18, 0xffffffff, 0x02020202, 0xe1c, 0xffffffff, 0x00000002, }; u32 rtl8192seradioa_1t_array[RADIOA_1T_ARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00030250, 0x002, 0x00010000, 0x010, 0x0008000f, 0x011, 0x000231fc, 0x010, 0x000c000f, 0x011, 0x0003f9f8, 0x010, 0x0002000f, 0x011, 0x00020101, 0x014, 0x0001093e, 0x014, 0x0009093e, 0x015, 0x0000f8f4, 0x017, 0x000f6500, 0x01a, 0x00013056, 0x01b, 0x00060000, 0x01c, 0x00000300, 0x01e, 0x00031059, 0x021, 0x00054000, 0x022, 0x0000083c, 0x023, 0x00001558, 0x024, 0x00000060, 0x025, 0x00022583, 0x026, 0x0000f200, 0x027, 0x000eacf1, 0x028, 0x0009bd54, 0x029, 0x00004582, 0x02a, 0x00000001, 0x02b, 0x00021334, 0x02a, 0x00000000, 0x02b, 0x0000000a, 0x02a, 0x00000001, 0x02b, 0x00000808, 0x02b, 0x00053333, 0x02c, 0x0000000c, 0x02a, 0x00000002, 0x02b, 0x00000808, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000003, 0x02b, 0x00000808, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000004, 0x02b, 0x00000808, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x00000005, 0x02b, 0x00000709, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x00000006, 0x02b, 0x00000709, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000007, 0x02b, 0x00000709, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000008, 0x02b, 0x00000709, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x00000009, 0x02b, 0x0000060a, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x0000000a, 0x02b, 0x0000060a, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x0000000b, 0x02b, 0x0000060a, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x0000000c, 0x02b, 0x0000060a, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x0000000d, 0x02b, 0x0000050b, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x0000000e, 0x02b, 0x0000050b, 0x02b, 0x00066623, 0x02c, 0x0000001a, 0x02a, 0x000e4000, 0x030, 0x00020000, 0x031, 0x000b9631, 0x032, 0x0000130d, 0x033, 0x00000187, 0x013, 0x00019e6c, 0x013, 0x00015e94, 0x000, 0x00010159, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x01e, 0x0003105b, 0x0fe, 0x00000000, 0x000, 0x00030159, 0x010, 0x0004000f, 0x011, 0x000203f9, }; u32 rtl8192seradiob_array[RADIOB_ARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00001041, 0x002, 0x00011000, 0x005, 0x00080fc0, 0x007, 0x000fc803, 0x013, 0x00017cb0, 0x013, 0x00011cc0, 0x013, 0x0000dc60, 0x013, 0x00008c60, 0x013, 0x00004450, 0x013, 0x00000020, }; u32 rtl8192seradiob_gm_array[RADIOB_GM_ARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00001041, 0x002, 0x00011000, 0x005, 0x00080fc0, 0x007, 0x000fc803, }; u32 rtl8192semac_2t_array[MAC_2T_ARRAYLENGTH] = { 0x020, 0x00000035, 0x048, 0x0000000e, 0x049, 0x000000f0, 0x04a, 0x00000077, 0x04b, 0x00000083, 0x0b5, 0x00000021, 0x0dc, 0x000000ff, 0x0dd, 0x000000ff, 0x0de, 0x000000ff, 0x0df, 0x000000ff, 0x116, 0x00000000, 0x117, 0x00000000, 0x118, 0x00000000, 0x119, 0x00000000, 0x11a, 0x00000000, 0x11b, 0x00000000, 0x11c, 0x00000000, 0x11d, 0x00000000, 0x160, 0x0000000b, 0x161, 0x0000000b, 0x162, 0x0000000b, 0x163, 0x0000000b, 0x164, 0x0000000b, 0x165, 0x0000000b, 0x166, 0x0000000b, 0x167, 0x0000000b, 0x168, 0x0000000b, 0x169, 0x0000000b, 0x16a, 0x0000000b, 0x16b, 0x0000000b, 0x16c, 0x0000000b, 0x16d, 0x0000000b, 0x16e, 0x0000000b, 0x16f, 0x0000000b, 0x170, 0x0000000b, 0x171, 0x0000000b, 0x172, 0x0000000b, 0x173, 0x0000000b, 0x174, 0x0000000b, 0x175, 0x0000000b, 0x176, 0x0000000b, 0x177, 0x0000000b, 0x178, 0x0000000b, 0x179, 0x0000000b, 0x17a, 0x0000000b, 0x17b, 0x0000000b, 0x17c, 0x0000000b, 0x17d, 0x0000000b, 0x17e, 0x0000000b, 0x17f, 0x0000000b, 0x236, 0x0000000c, 0x503, 0x00000022, 0x560, 0x00000000, }; u32 rtl8192seagctab_array[AGCTAB_ARRAYLENGTH] = { 0xc78, 0x7f000001, 0xc78, 0x7f010001, 0xc78, 0x7e020001, 0xc78, 0x7d030001, 0xc78, 0x7c040001, 0xc78, 0x7b050001, 0xc78, 0x7a060001, 0xc78, 0x79070001, 0xc78, 0x78080001, 0xc78, 0x77090001, 0xc78, 0x760a0001, 0xc78, 0x750b0001, 0xc78, 0x740c0001, 0xc78, 0x730d0001, 0xc78, 0x720e0001, 0xc78, 0x710f0001, 0xc78, 0x70100001, 0xc78, 0x6f110001, 0xc78, 0x6f120001, 0xc78, 0x6e130001, 0xc78, 0x6d140001, 0xc78, 0x6d150001, 0xc78, 0x6c160001, 0xc78, 0x6b170001, 0xc78, 0x6a180001, 0xc78, 0x6a190001, 0xc78, 0x691a0001, 0xc78, 0x681b0001, 0xc78, 0x671c0001, 0xc78, 0x661d0001, 0xc78, 0x651e0001, 0xc78, 0x641f0001, 0xc78, 0x63200001, 0xc78, 0x4c210001, 0xc78, 0x4b220001, 0xc78, 0x4a230001, 0xc78, 0x49240001, 0xc78, 0x48250001, 0xc78, 0x47260001, 0xc78, 0x46270001, 0xc78, 0x45280001, 0xc78, 0x44290001, 0xc78, 0x2c2a0001, 0xc78, 0x2b2b0001, 0xc78, 0x2a2c0001, 0xc78, 0x292d0001, 0xc78, 0x282e0001, 0xc78, 0x272f0001, 0xc78, 0x26300001, 0xc78, 0x25310001, 0xc78, 0x24320001, 0xc78, 0x23330001, 0xc78, 0x22340001, 0xc78, 0x09350001, 0xc78, 0x08360001, 0xc78, 0x07370001, 0xc78, 0x06380001, 0xc78, 0x05390001, 0xc78, 0x043a0001, 0xc78, 0x033b0001, 0xc78, 0x023c0001, 0xc78, 0x013d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x7f400001, 0xc78, 0x7f410001, 0xc78, 0x7e420001, 0xc78, 0x7d430001, 0xc78, 0x7c440001, 0xc78, 0x7b450001, 0xc78, 0x7a460001, 0xc78, 0x79470001, 0xc78, 0x78480001, 0xc78, 0x77490001, 0xc78, 0x764a0001, 0xc78, 0x754b0001, 0xc78, 0x744c0001, 0xc78, 0x734d0001, 0xc78, 0x724e0001, 0xc78, 0x714f0001, 0xc78, 0x70500001, 0xc78, 0x6f510001, 0xc78, 0x6f520001, 0xc78, 0x6e530001, 0xc78, 0x6d540001, 0xc78, 0x6d550001, 0xc78, 0x6c560001, 0xc78, 0x6b570001, 0xc78, 0x6a580001, 0xc78, 0x6a590001, 0xc78, 0x695a0001, 0xc78, 0x685b0001, 0xc78, 0x675c0001, 0xc78, 0x665d0001, 0xc78, 0x655e0001, 0xc78, 0x645f0001, 0xc78, 0x63600001, 0xc78, 0x4c610001, 0xc78, 0x4b620001, 0xc78, 0x4a630001, 0xc78, 0x49640001, 0xc78, 0x48650001, 0xc78, 0x47660001, 0xc78, 0x46670001, 0xc78, 0x45680001, 0xc78, 0x44690001, 0xc78, 0x2c6a0001, 0xc78, 0x2b6b0001, 0xc78, 0x2a6c0001, 0xc78, 0x296d0001, 0xc78, 0x286e0001, 0xc78, 0x276f0001, 0xc78, 0x26700001, 0xc78, 0x25710001, 0xc78, 0x24720001, 0xc78, 0x23730001, 0xc78, 0x22740001, 0xc78, 0x09750001, 0xc78, 0x08760001, 0xc78, 0x07770001, 0xc78, 0x06780001, 0xc78, 0x05790001, 0xc78, 0x047a0001, 0xc78, 0x037b0001, 0xc78, 0x027c0001, 0xc78, 0x017d0001, 0xc78, 0x007e0001, 0xc78, 0x007f0001, 0xc78, 0x3000001e, 0xc78, 0x3001001e, 0xc78, 0x3002001e, 0xc78, 0x3003001e, 0xc78, 0x3004001e, 0xc78, 0x3405001e, 0xc78, 0x3806001e, 0xc78, 0x3e07001e, 0xc78, 0x3e08001e, 0xc78, 0x4409001e, 0xc78, 0x460a001e, 0xc78, 0x480b001e, 0xc78, 0x480c001e, 0xc78, 0x4e0d001e, 0xc78, 0x560e001e, 0xc78, 0x5a0f001e, 0xc78, 0x5e10001e, 0xc78, 0x6211001e, 0xc78, 0x6c12001e, 0xc78, 0x7213001e, 0xc78, 0x7214001e, 0xc78, 0x7215001e, 0xc78, 0x7216001e, 0xc78, 0x7217001e, 0xc78, 0x7218001e, 0xc78, 0x7219001e, 0xc78, 0x721a001e, 0xc78, 0x721b001e, 0xc78, 0x721c001e, 0xc78, 0x721d001e, 0xc78, 0x721e001e, 0xc78, 0x721f001e, }; compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/sw.h0000644000175000017500000000254612026211315024427 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * *****************************************************************************/ #ifndef __REALTEK_PCI92SE_SW_H__ #define __REALTEK_PCI92SE_SW_H__ #define EFUSE_MAX_SECTION 16 int rtl92se_init_sw(struct ieee80211_hw *hw); void rtl92se_deinit_sw(struct ieee80211_hw *hw); void rtl92se_init_var_map(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/rf.h0000644000175000017500000000310412026211315024374 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __INC_RTL92S_RF_H #define __INC_RTL92S_RF_H #define RF6052_MAX_TX_PWR 0x3F void rtl92s_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); bool rtl92s_phy_rf6052_config(struct ieee80211_hw *hw) ; void rtl92s_phy_rf6052_set_ccktxpower(struct ieee80211_hw *hw, u8 powerlevel); void rtl92s_phy_rf6052_set_ofdmtxpower(struct ieee80211_hw *hw, u8 *p_pwrlevel, u8 chnl); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/rf.c0000644000175000017500000003514612026211315024402 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" static void _rtl92s_get_powerbase(struct ieee80211_hw *hw, u8 *p_pwrlevel, u8 chnl, u32 *ofdmbase, u32 *mcsbase, u8 *p_final_pwridx) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 pwrbase0, pwrbase1; u8 legacy_pwrdiff = 0, ht20_pwrdiff = 0; u8 i, pwrlevel[4]; for (i = 0; i < 2; i++) pwrlevel[i] = p_pwrlevel[i]; /* We only care about the path A for legacy. */ if (rtlefuse->eeprom_version < 2) { pwrbase0 = pwrlevel[0] + (rtlefuse->legacy_httxpowerdiff & 0xf); } else if (rtlefuse->eeprom_version >= 2) { legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff [RF90_PATH_A][chnl - 1]; /* For legacy OFDM, tx pwr always > HT OFDM pwr. * We do not care Path B * legacy OFDM pwr diff. NO BB register * to notify HW. */ pwrbase0 = pwrlevel[0] + legacy_pwrdiff; } pwrbase0 = (pwrbase0 << 24) | (pwrbase0 << 16) | (pwrbase0 << 8) | pwrbase0; *ofdmbase = pwrbase0; /* MCS rates */ if (rtlefuse->eeprom_version >= 2) { /* Check HT20 to HT40 diff */ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) { for (i = 0; i < 2; i++) { /* rf-A, rf-B */ /* HT 20<->40 pwr diff */ ht20_pwrdiff = rtlefuse->txpwr_ht20diff [i][chnl - 1]; if (ht20_pwrdiff < 8) /* 0~+7 */ pwrlevel[i] += ht20_pwrdiff; else /* index8-15=-8~-1 */ pwrlevel[i] -= (16 - ht20_pwrdiff); } } } /* use index of rf-A */ pwrbase1 = pwrlevel[0]; pwrbase1 = (pwrbase1 << 24) | (pwrbase1 << 16) | (pwrbase1 << 8) | pwrbase1; *mcsbase = pwrbase1; /* The following is for Antenna * diff from Ant-B to Ant-A */ p_final_pwridx[0] = pwrlevel[0]; p_final_pwridx[1] = pwrlevel[1]; switch (rtlefuse->eeprom_regulatory) { case 3: /* The following is for calculation * of the power diff for Ant-B to Ant-A. */ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { p_final_pwridx[0] += rtlefuse->pwrgroup_ht40 [RF90_PATH_A][ chnl - 1]; p_final_pwridx[1] += rtlefuse->pwrgroup_ht40 [RF90_PATH_B][ chnl - 1]; } else { p_final_pwridx[0] += rtlefuse->pwrgroup_ht20 [RF90_PATH_A][ chnl - 1]; p_final_pwridx[1] += rtlefuse->pwrgroup_ht20 [RF90_PATH_B][ chnl - 1]; } break; default: break; } if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "40MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n", p_final_pwridx[0], p_final_pwridx[1]); } else { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "20MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n", p_final_pwridx[0], p_final_pwridx[1]); } } static void _rtl92s_set_antennadiff(struct ieee80211_hw *hw, u8 *p_final_pwridx) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); char ant_pwr_diff = 0; u32 u4reg_val = 0; if (rtlphy->rf_type == RF_2T2R) { ant_pwr_diff = p_final_pwridx[1] - p_final_pwridx[0]; /* range is from 7~-8, * index = 0x0~0xf */ if (ant_pwr_diff > 7) ant_pwr_diff = 7; if (ant_pwr_diff < -8) ant_pwr_diff = -8; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Antenna Diff from RF-B to RF-A = %d (0x%x)\n", ant_pwr_diff, ant_pwr_diff & 0xf); ant_pwr_diff &= 0xf; } /* Antenna TX power difference */ rtlefuse->antenna_txpwdiff[2] = 0;/* RF-D, don't care */ rtlefuse->antenna_txpwdiff[1] = 0;/* RF-C, don't care */ rtlefuse->antenna_txpwdiff[0] = (u8)(ant_pwr_diff); /* RF-B */ u4reg_val = rtlefuse->antenna_txpwdiff[2] << 8 | rtlefuse->antenna_txpwdiff[1] << 4 | rtlefuse->antenna_txpwdiff[0]; rtl_set_bbreg(hw, RFPGA0_TXGAINSTAGE, (BXBTXAGC | BXCTXAGC | BXDTXAGC), u4reg_val); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Write BCD-Diff(0x%x) = 0x%x\n", RFPGA0_TXGAINSTAGE, u4reg_val); } static void _rtl92s_get_txpower_writeval_byregulatory(struct ieee80211_hw *hw, u8 chnl, u8 index, u32 pwrbase0, u32 pwrbase1, u32 *p_outwrite_val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 i, chnlgroup, pwrdiff_limit[4]; u32 writeval, customer_limit; /* Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */ switch (rtlefuse->eeprom_regulatory) { case 0: /* Realtek better performance increase power diff * defined by Realtek for large power */ chnlgroup = 0; writeval = rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index] + ((index < 2) ? pwrbase0 : pwrbase1); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "RTK better performance, writeval = 0x%x\n", writeval); break; case 1: /* Realtek regulatory increase power diff defined * by Realtek for regulatory */ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { writeval = ((index < 2) ? pwrbase0 : pwrbase1); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Realtek regulatory, 40MHz, writeval = 0x%x\n", writeval); } else { if (rtlphy->pwrgroup_cnt == 1) chnlgroup = 0; if (rtlphy->pwrgroup_cnt >= 3) { if (chnl <= 3) chnlgroup = 0; else if (chnl >= 4 && chnl <= 8) chnlgroup = 1; else if (chnl > 8) chnlgroup = 2; if (rtlphy->pwrgroup_cnt == 4) chnlgroup++; } writeval = rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index] + ((index < 2) ? pwrbase0 : pwrbase1); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Realtek regulatory, 20MHz, writeval = 0x%x\n", writeval); } break; case 2: /* Better regulatory don't increase any power diff */ writeval = ((index < 2) ? pwrbase0 : pwrbase1); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Better regulatory, writeval = 0x%x\n", writeval); break; case 3: /* Customer defined power diff. increase power diff defined by customer. */ chnlgroup = 0; if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "customer's limit, 40MHz = 0x%x\n", rtlefuse->pwrgroup_ht40 [RF90_PATH_A][chnl - 1]); } else { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "customer's limit, 20MHz = 0x%x\n", rtlefuse->pwrgroup_ht20 [RF90_PATH_A][chnl - 1]); } for (i = 0; i < 4; i++) { pwrdiff_limit[i] = (u8)((rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index] & (0x7f << (i * 8))) >> (i * 8)); if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { if (pwrdiff_limit[i] > rtlefuse->pwrgroup_ht40 [RF90_PATH_A][chnl - 1]) { pwrdiff_limit[i] = rtlefuse->pwrgroup_ht20 [RF90_PATH_A][chnl - 1]; } } else { if (pwrdiff_limit[i] > rtlefuse->pwrgroup_ht20 [RF90_PATH_A][chnl - 1]) { pwrdiff_limit[i] = rtlefuse->pwrgroup_ht20 [RF90_PATH_A][chnl - 1]; } } } customer_limit = (pwrdiff_limit[3] << 24) | (pwrdiff_limit[2] << 16) | (pwrdiff_limit[1] << 8) | (pwrdiff_limit[0]); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Customer's limit = 0x%x\n", customer_limit); writeval = customer_limit + ((index < 2) ? pwrbase0 : pwrbase1); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Customer, writeval = 0x%x\n", writeval); break; default: chnlgroup = 0; writeval = rtlphy->mcs_txpwrlevel_origoffset[chnlgroup][index] + ((index < 2) ? pwrbase0 : pwrbase1); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "RTK better performance, writeval = 0x%x\n", writeval); break; } if (rtlpriv->dm.dynamic_txhighpower_lvl == TX_HIGH_PWR_LEVEL_LEVEL1) writeval = 0x10101010; else if (rtlpriv->dm.dynamic_txhighpower_lvl == TX_HIGH_PWR_LEVEL_LEVEL2) writeval = 0x0; *p_outwrite_val = writeval; } static void _rtl92s_write_ofdm_powerreg(struct ieee80211_hw *hw, u8 index, u32 val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u16 regoffset[6] = {0xe00, 0xe04, 0xe10, 0xe14, 0xe18, 0xe1c}; u8 i, rfa_pwr[4]; u8 rfa_lower_bound = 0, rfa_upper_bound = 0, rf_pwr_diff = 0; u32 writeval = val; /* If path A and Path B coexist, we must limit Path A tx power. * Protect Path B pwr over or under flow. We need to calculate * upper and lower bound of path A tx power. */ if (rtlphy->rf_type == RF_2T2R) { rf_pwr_diff = rtlefuse->antenna_txpwdiff[0]; /* Diff=-8~-1 */ if (rf_pwr_diff >= 8) { /* Prevent underflow!! */ rfa_lower_bound = 0x10 - rf_pwr_diff; /* if (rf_pwr_diff >= 0) Diff = 0-7 */ } else { rfa_upper_bound = RF6052_MAX_TX_PWR - rf_pwr_diff; } } for (i = 0; i < 4; i++) { rfa_pwr[i] = (u8)((writeval & (0x7f << (i * 8))) >> (i * 8)); if (rfa_pwr[i] > RF6052_MAX_TX_PWR) rfa_pwr[i] = RF6052_MAX_TX_PWR; /* If path A and Path B coexist, we must limit Path A tx power. * Protect Path B pwr over or under flow. We need to calculate * upper and lower bound of path A tx power. */ if (rtlphy->rf_type == RF_2T2R) { /* Diff=-8~-1 */ if (rf_pwr_diff >= 8) { /* Prevent underflow!! */ if (rfa_pwr[i] < rfa_lower_bound) rfa_pwr[i] = rfa_lower_bound; /* Diff = 0-7 */ } else if (rf_pwr_diff >= 1) { /* Prevent overflow */ if (rfa_pwr[i] > rfa_upper_bound) rfa_pwr[i] = rfa_upper_bound; } } } writeval = (rfa_pwr[3] << 24) | (rfa_pwr[2] << 16) | (rfa_pwr[1] << 8) | rfa_pwr[0]; rtl_set_bbreg(hw, regoffset[index], 0x7f7f7f7f, writeval); } void rtl92s_phy_rf6052_set_ofdmtxpower(struct ieee80211_hw *hw, u8 *p_pwrlevel, u8 chnl) { u32 writeval, pwrbase0, pwrbase1; u8 index = 0; u8 finalpwr_idx[4]; _rtl92s_get_powerbase(hw, p_pwrlevel, chnl, &pwrbase0, &pwrbase1, &finalpwr_idx[0]); _rtl92s_set_antennadiff(hw, &finalpwr_idx[0]); for (index = 0; index < 6; index++) { _rtl92s_get_txpower_writeval_byregulatory(hw, chnl, index, pwrbase0, pwrbase1, &writeval); _rtl92s_write_ofdm_powerreg(hw, index, writeval); } } void rtl92s_phy_rf6052_set_ccktxpower(struct ieee80211_hw *hw, u8 pwrlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 txagc = 0; bool dont_inc_cck_or_turboscanoff = false; if (((rtlefuse->eeprom_version >= 2) && (rtlefuse->txpwr_safetyflag == 1)) || ((rtlefuse->eeprom_version >= 2) && (rtlefuse->eeprom_regulatory != 0))) dont_inc_cck_or_turboscanoff = true; if (mac->act_scanning) { txagc = 0x3f; if (dont_inc_cck_or_turboscanoff) txagc = pwrlevel; } else { txagc = pwrlevel; if (rtlpriv->dm.dynamic_txhighpower_lvl == TX_HIGH_PWR_LEVEL_LEVEL1) txagc = 0x10; else if (rtlpriv->dm.dynamic_txhighpower_lvl == TX_HIGH_PWR_LEVEL_LEVEL2) txagc = 0x0; } if (txagc > RF6052_MAX_TX_PWR) txagc = RF6052_MAX_TX_PWR; rtl_set_bbreg(hw, RTXAGC_CCK_MCS32, BTX_AGCRATECCK, txagc); } bool rtl92s_phy_rf6052_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 u4reg_val = 0; u8 rfpath; bool rtstatus = true; struct bb_reg_def *pphyreg; /* Initialize RF */ for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { pphyreg = &rtlphy->phyreg_def[rfpath]; /* Store original RFENV control type */ switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: u4reg_val = rtl92s_phy_query_bb_reg(hw, pphyreg->rfintfs, BRFSI_RFENV); break; case RF90_PATH_B: case RF90_PATH_D: u4reg_val = rtl92s_phy_query_bb_reg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16); break; } /* Set RF_ENV enable */ rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1); /* Set RF_ENV output high */ rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); /* Set bit number of Address and Data for RF register */ rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2, B3WIRE_ADDRESSLENGTH, 0x0); rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2, B3WIRE_DATALENGTH, 0x0); /* Initialize RF fom connfiguration file */ switch (rfpath) { case RF90_PATH_A: rtstatus = rtl92s_phy_config_rf(hw, (enum radio_path)rfpath); break; case RF90_PATH_B: rtstatus = rtl92s_phy_config_rf(hw, (enum radio_path)rfpath); break; case RF90_PATH_C: break; case RF90_PATH_D: break; } /* Restore RFENV control type */ switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs, BRFSI_RFENV, u4reg_val); break; case RF90_PATH_B: case RF90_PATH_D: rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16, u4reg_val); break; } if (!rtstatus) { pr_err("Radio[%d] Fail!!\n", rfpath); goto fail; } } return rtstatus; fail: return rtstatus; } void rtl92s_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); switch (bandwidth) { case HT_CHANNEL_WIDTH_20: rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 0xfffff3ff) | 0x0400); rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[0]); break; case HT_CHANNEL_WIDTH_20_40: rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 0xfffff3ff)); rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[0]); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", bandwidth); break; } } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/reg.h0000644000175000017500000010072512026211315024551 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __REALTEK_92S_REG_H__ #define __REALTEK_92S_REG_H__ /* 1. System Configuration Registers */ #define REG_SYS_ISO_CTRL 0x0000 #define REG_SYS_FUNC_EN 0x0002 #define PMC_FSM 0x0004 #define SYS_CLKR 0x0008 #define EPROM_CMD 0x000A #define EE_VPD 0x000C #define AFE_MISC 0x0010 #define SPS0_CTRL 0x0011 #define SPS1_CTRL 0x0018 #define RF_CTRL 0x001F #define LDOA15_CTRL 0x0020 #define LDOV12D_CTRL 0x0021 #define LDOHCI12_CTRL 0x0022 #define LDO_USB_SDIO 0x0023 #define LPLDO_CTRL 0x0024 #define AFE_XTAL_CTRL 0x0026 #define AFE_PLL_CTRL 0x0028 #define REG_EFUSE_CTRL 0x0030 #define REG_EFUSE_TEST 0x0034 #define PWR_DATA 0x0038 #define DBG_PORT 0x003A #define DPS_TIMER 0x003C #define RCLK_MON 0x003E /* 2. Command Control Registers */ #define CMDR 0x0040 #define TXPAUSE 0x0042 #define LBKMD_SEL 0x0043 #define TCR 0x0044 #define RCR 0x0048 #define MSR 0x004C #define SYSF_CFG 0x004D #define RX_PKY_LIMIT 0x004E #define MBIDCTRL 0x004F /* 3. MACID Setting Registers */ #define MACIDR 0x0050 #define MACIDR0 0x0050 #define MACIDR4 0x0054 #define BSSIDR 0x0058 #define HWVID 0x005E #define MAR 0x0060 #define MBIDCAMCONTENT 0x0068 #define MBIDCAMCFG 0x0070 #define BUILDTIME 0x0074 #define BUILDUSER 0x0078 #define IDR0 MACIDR0 #define IDR4 MACIDR4 /* 4. Timing Control Registers */ #define TSFR 0x0080 #define SLOT_TIME 0x0089 #define USTIME 0x008A #define SIFS_CCK 0x008C #define SIFS_OFDM 0x008E #define PIFS_TIME 0x0090 #define ACK_TIMEOUT 0x0091 #define EIFSTR 0x0092 #define BCN_INTERVAL 0x0094 #define ATIMWND 0x0096 #define BCN_DRV_EARLY_INT 0x0098 #define BCN_DMATIME 0x009A #define BCN_ERR_THRESH 0x009C #define MLT 0x009D #define RSVD_MAC_TUNE_US 0x009E /* 5. FIFO Control Registers */ #define RQPN 0x00A0 #define RQPN1 0x00A0 #define RQPN2 0x00A1 #define RQPN3 0x00A2 #define RQPN4 0x00A3 #define RQPN5 0x00A4 #define RQPN6 0x00A5 #define RQPN7 0x00A6 #define RQPN8 0x00A7 #define RQPN9 0x00A8 #define RQPN10 0x00A9 #define LD_RQPN 0x00AB #define RXFF_BNDY 0x00AC #define RXRPT_BNDY 0x00B0 #define TXPKTBUF_PGBNDY 0x00B4 #define PBP 0x00B5 #define RXDRVINFO_SZ 0x00B6 #define TXFF_STATUS 0x00B7 #define RXFF_STATUS 0x00B8 #define TXFF_EMPTY_TH 0x00B9 #define SDIO_RX_BLKSZ 0x00BC #define RXDMA 0x00BD #define RXPKT_NUM 0x00BE #define C2HCMD_UDT_SIZE 0x00C0 #define C2HCMD_UDT_ADDR 0x00C2 #define FIFOPAGE1 0x00C4 #define FIFOPAGE2 0x00C8 #define FIFOPAGE3 0x00CC #define FIFOPAGE4 0x00D0 #define FIFOPAGE5 0x00D4 #define FW_RSVD_PG_CRTL 0x00D8 #define RXDMA_AGG_PG_TH 0x00D9 #define TXDESC_MSK 0x00DC #define TXRPTFF_RDPTR 0x00E0 #define TXRPTFF_WTPTR 0x00E4 #define C2HFF_RDPTR 0x00E8 #define C2HFF_WTPTR 0x00EC #define RXFF0_RDPTR 0x00F0 #define RXFF0_WTPTR 0x00F4 #define RXFF1_RDPTR 0x00F8 #define RXFF1_WTPTR 0x00FC #define RXRPT0_RDPTR 0x0100 #define RXRPT0_WTPTR 0x0104 #define RXRPT1_RDPTR 0x0108 #define RXRPT1_WTPTR 0x010C #define RX0_UDT_SIZE 0x0110 #define RX1PKTNUM 0x0114 #define RXFILTERMAP 0x0116 #define RXFILTERMAP_GP1 0x0118 #define RXFILTERMAP_GP2 0x011A #define RXFILTERMAP_GP3 0x011C #define BCNQ_CTRL 0x0120 #define MGTQ_CTRL 0x0124 #define HIQ_CTRL 0x0128 #define VOTID7_CTRL 0x012c #define VOTID6_CTRL 0x0130 #define VITID5_CTRL 0x0134 #define VITID4_CTRL 0x0138 #define BETID3_CTRL 0x013c #define BETID0_CTRL 0x0140 #define BKTID2_CTRL 0x0144 #define BKTID1_CTRL 0x0148 #define CMDQ_CTRL 0x014c #define TXPKT_NUM_CTRL 0x0150 #define TXQ_PGADD 0x0152 #define TXFF_PG_NUM 0x0154 #define TRXDMA_STATUS 0x0156 /* 6. Adaptive Control Registers */ #define INIMCS_SEL 0x0160 #define TX_RATE_REG INIMCS_SEL #define INIRTSMCS_SEL 0x0180 #define RRSR 0x0181 #define ARFR0 0x0184 #define ARFR1 0x0188 #define ARFR2 0x018C #define ARFR3 0x0190 #define ARFR4 0x0194 #define ARFR5 0x0198 #define ARFR6 0x019C #define ARFR7 0x01A0 #define AGGLEN_LMT_H 0x01A7 #define AGGLEN_LMT_L 0x01A8 #define DARFRC 0x01B0 #define RARFRC 0x01B8 #define MCS_TXAGC 0x01C0 #define CCK_TXAGC 0x01C8 /* 7. EDCA Setting Registers */ #define EDCAPARA_VO 0x01D0 #define EDCAPARA_VI 0x01D4 #define EDCAPARA_BE 0x01D8 #define EDCAPARA_BK 0x01DC #define BCNTCFG 0x01E0 #define CWRR 0x01E2 #define ACMAVG 0x01E4 #define AcmHwCtrl 0x01E7 #define VO_ADMTM 0x01E8 #define VI_ADMTM 0x01EC #define BE_ADMTM 0x01F0 #define RETRY_LIMIT 0x01F4 #define SG_RATE 0x01F6 /* 8. WMAC, BA and CCX related Register. */ #define NAV_CTRL 0x0200 #define BW_OPMODE 0x0203 #define BACAMCMD 0x0204 #define BACAMCONTENT 0x0208 /* the 0x2xx register WMAC definition */ #define LBDLY 0x0210 #define FWDLY 0x0211 #define HWPC_RX_CTRL 0x0218 #define MQIR 0x0220 #define MAIR 0x0222 #define MSIR 0x0224 #define CLM_RESULT 0x0227 #define NHM_RPI_CNT 0x0228 #define RXERR_RPT 0x0230 #define NAV_PROT_LEN 0x0234 #define CFEND_TH 0x0236 #define AMPDU_MIN_SPACE 0x0237 #define TXOP_STALL_CTRL 0x0238 /* 9. Security Control Registers */ #define REG_RWCAM 0x0240 #define REG_WCAMI 0x0244 #define REG_RCAMO 0x0248 #define REG_CAMDBG 0x024C #define REG_SECR 0x0250 /* 10. Power Save Control Registers */ #define WOW_CTRL 0x0260 #define PSSTATUS 0x0261 #define PSSWITCH 0x0262 #define MIMOPS_WAIT_PERIOD 0x0263 #define LPNAV_CTRL 0x0264 #define WFM0 0x0270 #define WFM1 0x0280 #define WFM2 0x0290 #define WFM3 0x02A0 #define WFM4 0x02B0 #define WFM5 0x02C0 #define WFCRC 0x02D0 #define FW_RPT_REG 0x02c4 /* 11. General Purpose Registers */ #define PSTIME 0x02E0 #define TIMER0 0x02E4 #define TIMER1 0x02E8 #define GPIO_IN_SE 0x02EC #define GPIO_IO_SEL 0x02EE #define MAC_PINMUX_CFG 0x02F1 #define LEDCFG 0x02F2 #define PHY_REG 0x02F3 #define PHY_REG_DATA 0x02F4 #define REG_EFUSE_CLK 0x02F8 /* 12. Host Interrupt Status Registers */ #define INTA_MASK 0x0300 #define ISR 0x0308 /* 13. Test Mode and Debug Control Registers */ #define DBG_PORT_SWITCH 0x003A #define BIST 0x0310 #define DBS 0x0314 #define CPUINST 0x0318 #define CPUCAUSE 0x031C #define LBUS_ERR_ADDR 0x0320 #define LBUS_ERR_CMD 0x0324 #define LBUS_ERR_DATA_L 0x0328 #define LBUS_ERR_DATA_H 0x032C #define LX_EXCEPTION_ADDR 0x0330 #define WDG_CTRL 0x0334 #define INTMTU 0x0338 #define INTM 0x033A #define FDLOCKTURN0 0x033C #define FDLOCKTURN1 0x033D #define TRXPKTBUF_DBG_DATA 0x0340 #define TRXPKTBUF_DBG_CTRL 0x0348 #define DPLL 0x034A #define CBUS_ERR_ADDR 0x0350 #define CBUS_ERR_CMD 0x0354 #define CBUS_ERR_DATA_L 0x0358 #define CBUS_ERR_DATA_H 0x035C #define USB_SIE_INTF_ADDR 0x0360 #define USB_SIE_INTF_WD 0x0361 #define USB_SIE_INTF_RD 0x0362 #define USB_SIE_INTF_CTRL 0x0363 #define LBUS_MON_ADDR 0x0364 #define LBUS_ADDR_MASK 0x0368 /* Boundary is 0x37F */ /* 14. PCIE config register */ #define TP_POLL 0x0500 #define PM_CTRL 0x0502 #define PCIF 0x0503 #define THPDA 0x0514 #define TMDA 0x0518 #define TCDA 0x051C #define HDA 0x0520 #define TVODA 0x0524 #define TVIDA 0x0528 #define TBEDA 0x052C #define TBKDA 0x0530 #define TBDA 0x0534 #define RCDA 0x0538 #define RDQDA 0x053C #define DBI_WDATA 0x0540 #define DBI_RDATA 0x0544 #define DBI_CTRL 0x0548 #define MDIO_DATA 0x0550 #define MDIO_CTRL 0x0554 #define PCI_RPWM 0x0561 #define PCI_CPWM 0x0563 /* Config register (Offset 0x800-) */ #define PHY_CCA 0x803 /* Min Spacing related settings. */ #define MAX_MSS_DENSITY_2T 0x13 #define MAX_MSS_DENSITY_1T 0x0A /* Rx DMA Control related settings */ #define RXDMA_AGG_EN BIT(7) #define RPWM PCI_RPWM /* Regsiter Bit and Content definition */ #define ISO_MD2PP BIT(0) #define ISO_PA2PCIE BIT(3) #define ISO_PLL2MD BIT(4) #define ISO_PWC_DV2RP BIT(11) #define ISO_PWC_RV2RP BIT(12) #define FEN_MREGEN BIT(15) #define FEN_DCORE BIT(11) #define FEN_CPUEN BIT(10) #define PAD_HWPD_IDN BIT(22) #define SYS_CLKSEL_80M BIT(0) #define SYS_PS_CLKSEL BIT(1) #define SYS_CPU_CLKSEL BIT(2) #define SYS_MAC_CLK_EN BIT(11) #define SYS_SWHW_SEL BIT(14) #define SYS_FWHW_SEL BIT(15) #define CmdEEPROM_En BIT(5) #define CmdEERPOMSEL BIT(4) #define Cmd9346CR_9356SEL BIT(4) #define AFE_MBEN BIT(1) #define AFE_BGEN BIT(0) #define SPS1_SWEN BIT(1) #define SPS1_LDEN BIT(0) #define RF_EN BIT(0) #define RF_RSTB BIT(1) #define RF_SDMRSTB BIT(2) #define LDA15_EN BIT(0) #define LDV12_EN BIT(0) #define LDV12_SDBY BIT(1) #define XTAL_GATE_AFE BIT(10) #define APLL_EN BIT(0) #define AFR_CardBEn BIT(0) #define AFR_CLKRUN_SEL BIT(1) #define AFR_FuncRegEn BIT(2) #define APSDOFF_STATUS BIT(15) #define APSDOFF BIT(14) #define BBRSTN BIT(13) #define BB_GLB_RSTN BIT(12) #define SCHEDULE_EN BIT(10) #define MACRXEN BIT(9) #define MACTXEN BIT(8) #define DDMA_EN BIT(7) #define FW2HW_EN BIT(6) #define RXDMA_EN BIT(5) #define TXDMA_EN BIT(4) #define HCI_RXDMA_EN BIT(3) #define HCI_TXDMA_EN BIT(2) #define StopHCCA BIT(6) #define StopHigh BIT(5) #define StopMgt BIT(4) #define StopVO BIT(3) #define StopVI BIT(2) #define StopBE BIT(1) #define StopBK BIT(0) #define LBK_NORMAL 0x00 #define LBK_MAC_LB (BIT(0) | BIT(1) | BIT(3)) #define LBK_MAC_DLB (BIT(0) | BIT(1)) #define LBK_DMA_LB (BIT(0) | BIT(1) | BIT(2)) #define TCP_OFDL_EN BIT(25) #define HWPC_TX_EN BIT(24) #define TXDMAPRE2FULL BIT(23) #define DISCW BIT(20) #define TCRICV BIT(19) #define CfendForm BIT(17) #define TCRCRC BIT(16) #define FAKE_IMEM_EN BIT(15) #define TSFRST BIT(9) #define TSFEN BIT(8) #define FWALLRDY (BIT(0) | BIT(1) | BIT(2) | \ BIT(3) | BIT(4) | BIT(5) | \ BIT(6) | BIT(7)) #define FWRDY BIT(7) #define BASECHG BIT(6) #define IMEM BIT(5) #define DMEM_CODE_DONE BIT(4) #define EXT_IMEM_CHK_RPT BIT(3) #define EXT_IMEM_CODE_DONE BIT(2) #define IMEM_CHK_RPT BIT(1) #define IMEM_CODE_DONE BIT(0) #define IMEM_CODE_DONE BIT(0) #define IMEM_CHK_RPT BIT(1) #define EMEM_CODE_DONE BIT(2) #define EMEM_CHK_RPT BIT(3) #define DMEM_CODE_DONE BIT(4) #define IMEM_RDY BIT(5) #define BASECHG BIT(6) #define FWRDY BIT(7) #define LOAD_FW_READY (IMEM_CODE_DONE | \ IMEM_CHK_RPT | \ EMEM_CODE_DONE | \ EMEM_CHK_RPT | \ DMEM_CODE_DONE | \ IMEM_RDY | \ BASECHG | \ FWRDY) #define TCR_TSFEN BIT(8) #define TCR_TSFRST BIT(9) #define TCR_FAKE_IMEM_EN BIT(15) #define TCR_CRC BIT(16) #define TCR_ICV BIT(19) #define TCR_DISCW BIT(20) #define TCR_HWPC_TX_EN BIT(24) #define TCR_TCP_OFDL_EN BIT(25) #define TXDMA_INIT_VALUE (IMEM_CHK_RPT | \ EXT_IMEM_CHK_RPT) #define RCR_APPFCS BIT(31) #define RCR_DIS_ENC_2BYTE BIT(30) #define RCR_DIS_AES_2BYTE BIT(29) #define RCR_HTC_LOC_CTRL BIT(28) #define RCR_ENMBID BIT(27) #define RCR_RX_TCPOFDL_EN BIT(26) #define RCR_APP_PHYST_RXFF BIT(25) #define RCR_APP_PHYST_STAFF BIT(24) #define RCR_CBSSID BIT(23) #define RCR_APWRMGT BIT(22) #define RCR_ADD3 BIT(21) #define RCR_AMF BIT(20) #define RCR_ACF BIT(19) #define RCR_ADF BIT(18) #define RCR_APP_MIC BIT(17) #define RCR_APP_ICV BIT(16) #define RCR_RXFTH BIT(13) #define RCR_AICV BIT(12) #define RCR_RXDESC_LK_EN BIT(11) #define RCR_APP_BA_SSN BIT(6) #define RCR_ACRC32 BIT(5) #define RCR_RXSHFT_EN BIT(4) #define RCR_AB BIT(3) #define RCR_AM BIT(2) #define RCR_APM BIT(1) #define RCR_AAP BIT(0) #define RCR_MXDMA_OFFSET 8 #define RCR_FIFO_OFFSET 13 #define MSR_LINK_MASK ((1 << 0) | (1 << 1)) #define MSR_LINK_MANAGED 2 #define MSR_LINK_NONE 0 #define MSR_LINK_SHIFT 0 #define MSR_LINK_ADHOC 1 #define MSR_LINK_MASTER 3 #define MSR_NOLINK 0x00 #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 #define ENUART BIT(7) #define ENJTAG BIT(3) #define BTMODE (BIT(2) | BIT(1)) #define ENBT BIT(0) #define ENMBID BIT(7) #define BCNUM (BIT(6) | BIT(5) | BIT(4)) #define USTIME_EDCA 0xFF00 #define USTIME_TSF 0x00FF #define SIFS_TRX 0xFF00 #define SIFS_CTX 0x00FF #define ENSWBCN BIT(15) #define DRVERLY_TU 0x0FF0 #define DRVERLY_US 0x000F #define BCN_TCFG_CW_SHIFT 8 #define BCN_TCFG_IFS 0 #define RRSR_RSC_OFFSET 21 #define RRSR_SHORT_OFFSET 23 #define RRSR_RSC_BW_40M 0x600000 #define RRSR_RSC_UPSUBCHNL 0x400000 #define RRSR_RSC_LOWSUBCHNL 0x200000 #define RRSR_SHORT 0x800000 #define RRSR_1M BIT(0) #define RRSR_2M BIT(1) #define RRSR_5_5M BIT(2) #define RRSR_11M BIT(3) #define RRSR_6M BIT(4) #define RRSR_9M BIT(5) #define RRSR_12M BIT(6) #define RRSR_18M BIT(7) #define RRSR_24M BIT(8) #define RRSR_36M BIT(9) #define RRSR_48M BIT(10) #define RRSR_54M BIT(11) #define RRSR_MCS0 BIT(12) #define RRSR_MCS1 BIT(13) #define RRSR_MCS2 BIT(14) #define RRSR_MCS3 BIT(15) #define RRSR_MCS4 BIT(16) #define RRSR_MCS5 BIT(17) #define RRSR_MCS6 BIT(18) #define RRSR_MCS7 BIT(19) #define BRSR_AckShortPmb BIT(23) #define RATR_1M 0x00000001 #define RATR_2M 0x00000002 #define RATR_55M 0x00000004 #define RATR_11M 0x00000008 #define RATR_6M 0x00000010 #define RATR_9M 0x00000020 #define RATR_12M 0x00000040 #define RATR_18M 0x00000080 #define RATR_24M 0x00000100 #define RATR_36M 0x00000200 #define RATR_48M 0x00000400 #define RATR_54M 0x00000800 #define RATR_MCS0 0x00001000 #define RATR_MCS1 0x00002000 #define RATR_MCS2 0x00004000 #define RATR_MCS3 0x00008000 #define RATR_MCS4 0x00010000 #define RATR_MCS5 0x00020000 #define RATR_MCS6 0x00040000 #define RATR_MCS7 0x00080000 #define RATR_MCS8 0x00100000 #define RATR_MCS9 0x00200000 #define RATR_MCS10 0x00400000 #define RATR_MCS11 0x00800000 #define RATR_MCS12 0x01000000 #define RATR_MCS13 0x02000000 #define RATR_MCS14 0x04000000 #define RATR_MCS15 0x08000000 #define RATE_ALL_CCK (RATR_1M | RATR_2M | \ RATR_55M | RATR_11M) #define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | \ RATR_12M | RATR_18M | \ RATR_24M | RATR_36M | \ RATR_48M | RATR_54M) #define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | \ RATR_MCS2 | RATR_MCS3 | \ RATR_MCS4 | RATR_MCS5 | \ RATR_MCS6 | RATR_MCS7) #define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | \ RATR_MCS10 | RATR_MCS11 | \ RATR_MCS12 | RATR_MCS13 | \ RATR_MCS14 | RATR_MCS15) #define AC_PARAM_TXOP_LIMIT_OFFSET 16 #define AC_PARAM_ECW_MAX_OFFSET 12 #define AC_PARAM_ECW_MIN_OFFSET 8 #define AC_PARAM_AIFS_OFFSET 0 #define AcmHw_HwEn BIT(0) #define AcmHw_BeqEn BIT(1) #define AcmHw_ViqEn BIT(2) #define AcmHw_VoqEn BIT(3) #define AcmHw_BeqStatus BIT(4) #define AcmHw_ViqStatus BIT(5) #define AcmHw_VoqStatus BIT(6) #define RETRY_LIMIT_SHORT_SHIFT 8 #define RETRY_LIMIT_LONG_SHIFT 0 #define NAV_UPPER_EN BIT(16) #define NAV_UPPER 0xFF00 #define NAV_RTSRST 0xFF #define BW_OPMODE_20MHZ BIT(2) #define BW_OPMODE_5G BIT(1) #define BW_OPMODE_11J BIT(0) #define RXERR_RPT_RST BIT(27) #define RXERR_OFDM_PPDU 0 #define RXERR_OFDM_FALSE_ALARM 1 #define RXERR_OFDM_MPDU_OK 2 #define RXERR_OFDM_MPDU_FAIL 3 #define RXERR_CCK_PPDU 4 #define RXERR_CCK_FALSE_ALARM 5 #define RXERR_CCK_MPDU_OK 6 #define RXERR_CCK_MPDU_FAIL 7 #define RXERR_HT_PPDU 8 #define RXERR_HT_FALSE_ALARM 9 #define RXERR_HT_MPDU_TOTAL 10 #define RXERR_HT_MPDU_OK 11 #define RXERR_HT_MPDU_FAIL 12 #define RXERR_RX_FULL_DROP 15 #define SCR_TXUSEDK BIT(0) #define SCR_RXUSEDK BIT(1) #define SCR_TXENCENABLE BIT(2) #define SCR_RXENCENABLE BIT(3) #define SCR_SKBYA2 BIT(4) #define SCR_NOSKMC BIT(5) #define CAM_VALID BIT(15) #define CAM_NOTVALID 0x0000 #define CAM_USEDK BIT(5) #define CAM_NONE 0x0 #define CAM_WEP40 0x01 #define CAM_TKIP 0x02 #define CAM_AES 0x04 #define CAM_WEP104 0x05 #define TOTAL_CAM_ENTRY 32 #define HALF_CAM_ENTRY 16 #define CAM_WRITE BIT(16) #define CAM_READ 0x00000000 #define CAM_POLLINIG BIT(31) #define WOW_PMEN BIT(0) #define WOW_WOMEN BIT(1) #define WOW_MAGIC BIT(2) #define WOW_UWF BIT(3) #define GPIOMUX_EN BIT(3) #define GPIOSEL_GPIO 0 #define GPIOSEL_PHYDBG 1 #define GPIOSEL_BT 2 #define GPIOSEL_WLANDBG 3 #define GPIOSEL_GPIO_MASK (~(BIT(0)|BIT(1))) #define HST_RDBUSY BIT(0) #define CPU_WTBUSY BIT(1) #define IMR8190_DISABLED 0x0 #define IMR_CPUERR BIT(5) #define IMR_ATIMEND BIT(4) #define IMR_TBDOK BIT(3) #define IMR_TBDER BIT(2) #define IMR_BCNDMAINT8 BIT(1) #define IMR_BCNDMAINT7 BIT(0) #define IMR_BCNDMAINT6 BIT(31) #define IMR_BCNDMAINT5 BIT(30) #define IMR_BCNDMAINT4 BIT(29) #define IMR_BCNDMAINT3 BIT(28) #define IMR_BCNDMAINT2 BIT(27) #define IMR_BCNDMAINT1 BIT(26) #define IMR_BCNDOK8 BIT(25) #define IMR_BCNDOK7 BIT(24) #define IMR_BCNDOK6 BIT(23) #define IMR_BCNDOK5 BIT(22) #define IMR_BCNDOK4 BIT(21) #define IMR_BCNDOK3 BIT(20) #define IMR_BCNDOK2 BIT(19) #define IMR_BCNDOK1 BIT(18) #define IMR_TIMEOUT2 BIT(17) #define IMR_TIMEOUT1 BIT(16) #define IMR_TXFOVW BIT(15) #define IMR_PSTIMEOUT BIT(14) #define IMR_BCNINT BIT(13) #define IMR_RXFOVW BIT(12) #define IMR_RDU BIT(11) #define IMR_RXCMDOK BIT(10) #define IMR_BDOK BIT(9) #define IMR_HIGHDOK BIT(8) #define IMR_COMDOK BIT(7) #define IMR_MGNTDOK BIT(6) #define IMR_HCCADOK BIT(5) #define IMR_BKDOK BIT(4) #define IMR_BEDOK BIT(3) #define IMR_VIDOK BIT(2) #define IMR_VODOK BIT(1) #define IMR_ROK BIT(0) #define TPPOLL_BKQ BIT(0) #define TPPOLL_BEQ BIT(1) #define TPPOLL_VIQ BIT(2) #define TPPOLL_VOQ BIT(3) #define TPPOLL_BQ BIT(4) #define TPPOLL_CQ BIT(5) #define TPPOLL_MQ BIT(6) #define TPPOLL_HQ BIT(7) #define TPPOLL_HCCAQ BIT(8) #define TPPOLL_STOPBK BIT(9) #define TPPOLL_STOPBE BIT(10) #define TPPOLL_STOPVI BIT(11) #define TPPOLL_STOPVO BIT(12) #define TPPOLL_STOPMGT BIT(13) #define TPPOLL_STOPHIGH BIT(14) #define TPPOLL_STOPHCCA BIT(15) #define TPPOLL_SHIFT 8 #define CCX_CMD_CLM_ENABLE BIT(0) #define CCX_CMD_NHM_ENABLE BIT(1) #define CCX_CMD_FUNCTION_ENABLE BIT(8) #define CCX_CMD_IGNORE_CCA BIT(9) #define CCX_CMD_IGNORE_TXON BIT(10) #define CCX_CLM_RESULT_READY BIT(16) #define CCX_NHM_RESULT_READY BIT(16) #define CCX_CMD_RESET 0x0 #define HWSET_MAX_SIZE_92S 128 #define EFUSE_MAX_SECTION 16 #define EFUSE_REAL_CONTENT_LEN 512 #define EFUSE_OOB_PROTECT_BYTES 15 #define RTL8190_EEPROM_ID 0x8129 #define EEPROM_HPON 0x02 #define EEPROM_CLK 0x06 #define EEPROM_TESTR 0x08 #define EEPROM_VID 0x0A #define EEPROM_DID 0x0C #define EEPROM_SVID 0x0E #define EEPROM_SMID 0x10 #define EEPROM_MAC_ADDR 0x12 #define EEPROM_NODE_ADDRESS_BYTE_0 0x12 #define EEPROM_PWDIFF 0x54 #define EEPROM_TXPOWERBASE 0x50 #define EEPROM_TX_PWR_INDEX_RANGE 28 #define EEPROM_TX_PWR_HT20_DIFF 0x62 #define DEFAULT_HT20_TXPWR_DIFF 2 #define EEPROM_TX_PWR_OFDM_DIFF 0x65 #define EEPROM_TXPWRGROUP 0x67 #define EEPROM_REGULATORY 0x6D #define TX_PWR_SAFETY_CHK 0x6D #define EEPROM_TXPWINDEX_CCK_24G 0x5D #define EEPROM_TXPWINDEX_OFDM_24G 0x6B #define EEPROM_HT2T_CH1_A 0x6c #define EEPROM_HT2T_CH7_A 0x6d #define EEPROM_HT2T_CH13_A 0x6e #define EEPROM_HT2T_CH1_B 0x6f #define EEPROM_HT2T_CH7_B 0x70 #define EEPROM_HT2T_CH13_B 0x71 #define EEPROM_TSSI_A 0x74 #define EEPROM_TSSI_B 0x75 #define EEPROM_RFIND_POWERDIFF 0x76 #define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 #define EEPROM_THERMALMETER 0x77 #define EEPROM_BLUETOOTH_COEXIST 0x78 #define EEPROM_BLUETOOTH_TYPE 0x4f #define EEPROM_OPTIONAL 0x78 #define EEPROM_WOWLAN 0x78 #define EEPROM_CRYSTALCAP 0x79 #define EEPROM_CHANNELPLAN 0x7B #define EEPROM_VERSION 0x7C #define EEPROM_CUSTOMID 0x7A #define EEPROM_BOARDTYPE 0x7E #define EEPROM_CHANNEL_PLAN_FCC 0x0 #define EEPROM_CHANNEL_PLAN_IC 0x1 #define EEPROM_CHANNEL_PLAN_ETSI 0x2 #define EEPROM_CHANNEL_PLAN_SPAIN 0x3 #define EEPROM_CHANNEL_PLAN_FRANCE 0x4 #define EEPROM_CHANNEL_PLAN_MKK 0x5 #define EEPROM_CHANNEL_PLAN_MKK1 0x6 #define EEPROM_CHANNEL_PLAN_ISRAEL 0x7 #define EEPROM_CHANNEL_PLAN_TELEC 0x8 #define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9 #define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA #define EEPROM_CHANNEL_PLAN_NCC 0xB #define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80 #define FW_DIG_DISABLE 0xfd00cc00 #define FW_DIG_ENABLE 0xfd000000 #define FW_DIG_HALT 0xfd000001 #define FW_DIG_RESUME 0xfd000002 #define FW_HIGH_PWR_DISABLE 0xfd000008 #define FW_HIGH_PWR_ENABLE 0xfd000009 #define FW_ADD_A2_ENTRY 0xfd000016 #define FW_TXPWR_TRACK_ENABLE 0xfd000017 #define FW_TXPWR_TRACK_DISABLE 0xfd000018 #define FW_TXPWR_TRACK_THERMAL 0xfd000019 #define FW_TXANT_SWITCH_ENABLE 0xfd000023 #define FW_TXANT_SWITCH_DISABLE 0xfd000024 #define FW_RA_INIT 0xfd000026 #define FW_CTRL_DM_BY_DRIVER 0Xfd00002a #define FW_RA_IOT_BG_COMB 0xfd000030 #define FW_RA_IOT_N_COMB 0xfd000031 #define FW_RA_REFRESH 0xfd0000a0 #define FW_RA_UPDATE_MASK 0xfd0000a2 #define FW_RA_DISABLE 0xfd0000a4 #define FW_RA_ACTIVE 0xfd0000a6 #define FW_RA_DISABLE_RSSI_MASK 0xfd0000ac #define FW_RA_ENABLE_RSSI_MASK 0xfd0000ad #define FW_RA_RESET 0xfd0000af #define FW_DM_DISABLE 0xfd00aa00 #define FW_IQK_ENABLE 0xf0000020 #define FW_IQK_SUCCESS 0x0000dddd #define FW_IQK_FAIL 0x0000ffff #define FW_OP_FAILURE 0xffffffff #define FW_TX_FEEDBACK_NONE 0xfb000000 #define FW_TX_FEEDBACK_DTM_ENABLE (FW_TX_FEEDBACK_NONE | 0x1) #define FW_TX_FEEDBACK_CCX_ENABL (FW_TX_FEEDBACK_NONE | 0x2) #define FW_BB_RESET_ENABLE 0xff00000d #define FW_BB_RESET_DISABLE 0xff00000e #define FW_CCA_CHK_ENABLE 0xff000011 #define FW_CCK_RESET_CNT 0xff000013 #define FW_LPS_ENTER 0xfe000010 #define FW_LPS_LEAVE 0xfe000011 #define FW_INDIRECT_READ 0xf2000000 #define FW_INDIRECT_WRITE 0xf2000001 #define FW_CHAN_SET 0xf3000001 #define RFPC 0x5F #define RCR_9356SEL BIT(6) #define TCR_LRL_OFFSET 0 #define TCR_SRL_OFFSET 8 #define TCR_MXDMA_OFFSET 21 #define TCR_SAT BIT(24) #define RCR_MXDMA_OFFSET 8 #define RCR_FIFO_OFFSET 13 #define RCR_OnlyErlPkt BIT(31) #define CWR 0xDC #define RETRYCTR 0xDE #define CPU_GEN_SYSTEM_RESET 0x00000001 #define CCX_COMMAND_REG 0x890 #define CLM_PERIOD_REG 0x894 #define NHM_PERIOD_REG 0x896 #define NHM_THRESHOLD0 0x898 #define NHM_THRESHOLD1 0x899 #define NHM_THRESHOLD2 0x89A #define NHM_THRESHOLD3 0x89B #define NHM_THRESHOLD4 0x89C #define NHM_THRESHOLD5 0x89D #define NHM_THRESHOLD6 0x89E #define CLM_RESULT_REG 0x8D0 #define NHM_RESULT_REG 0x8D4 #define NHM_RPI_COUNTER0 0x8D8 #define NHM_RPI_COUNTER1 0x8D9 #define NHM_RPI_COUNTER2 0x8DA #define NHM_RPI_COUNTER3 0x8DB #define NHM_RPI_COUNTER4 0x8DC #define NHM_RPI_COUNTER5 0x8DD #define NHM_RPI_COUNTER6 0x8DE #define NHM_RPI_COUNTER7 0x8DF #define HAL_8192S_HW_GPIO_OFF_BIT BIT(3) #define HAL_8192S_HW_GPIO_OFF_MASK 0xF7 #define HAL_8192S_HW_GPIO_WPS_BIT BIT(4) #define RPMAC_RESET 0x100 #define RPMAC_TXSTART 0x104 #define RPMAC_TXLEGACYSIG 0x108 #define RPMAC_TXHTSIG1 0x10c #define RPMAC_TXHTSIG2 0x110 #define RPMAC_PHYDEBUG 0x114 #define RPMAC_TXPACKETNNM 0x118 #define RPMAC_TXIDLE 0x11c #define RPMAC_TXMACHEADER0 0x120 #define RPMAC_TXMACHEADER1 0x124 #define RPMAC_TXMACHEADER2 0x128 #define RPMAC_TXMACHEADER3 0x12c #define RPMAC_TXMACHEADER4 0x130 #define RPMAC_TXMACHEADER5 0x134 #define RPMAC_TXDATATYPE 0x138 #define RPMAC_TXRANDOMSEED 0x13c #define RPMAC_CCKPLCPPREAMBLE 0x140 #define RPMAC_CCKPLCPHEADER 0x144 #define RPMAC_CCKCRC16 0x148 #define RPMAC_OFDMRXCRC32OK 0x170 #define RPMAC_OFDMRXCRC32ER 0x174 #define RPMAC_OFDMRXPARITYER 0x178 #define RPMAC_OFDMRXCRC8ER 0x17c #define RPMAC_CCKCRXRC16ER 0x180 #define RPMAC_CCKCRXRC32ER 0x184 #define RPMAC_CCKCRXRC32OK 0x188 #define RPMAC_TXSTATUS 0x18c #define RF_BB_CMD_ADDR 0x02c0 #define RF_BB_CMD_DATA 0x02c4 #define RFPGA0_RFMOD 0x800 #define RFPGA0_TXINFO 0x804 #define RFPGA0_PSDFUNCTION 0x808 #define RFPGA0_TXGAINSTAGE 0x80c #define RFPGA0_RFTIMING1 0x810 #define RFPGA0_RFTIMING2 0x814 #define RFPGA0_XA_HSSIPARAMETER1 0x820 #define RFPGA0_XA_HSSIPARAMETER2 0x824 #define RFPGA0_XB_HSSIPARAMETER1 0x828 #define RFPGA0_XB_HSSIPARAMETER2 0x82c #define RFPGA0_XC_HSSIPARAMETER1 0x830 #define RFPGA0_XC_HSSIPARAMETER2 0x834 #define RFPGA0_XD_HSSIPARAMETER1 0x838 #define RFPGA0_XD_HSSIPARAMETER2 0x83c #define RFPGA0_XA_LSSIPARAMETER 0x840 #define RFPGA0_XB_LSSIPARAMETER 0x844 #define RFPGA0_XC_LSSIPARAMETER 0x848 #define RFPGA0_XD_LSSIPARAMETER 0x84c #define RFPGA0_RFWAKEUP_PARAMETER 0x850 #define RFPGA0_RFSLEEPUP_PARAMETER 0x854 #define RFPGA0_XAB_SWITCHCONTROL 0x858 #define RFPGA0_XCD_SWITCHCONTROL 0x85c #define RFPGA0_XA_RFINTERFACEOE 0x860 #define RFPGA0_XB_RFINTERFACEOE 0x864 #define RFPGA0_XC_RFINTERFACEOE 0x868 #define RFPGA0_XD_RFINTERFACEOE 0x86c #define RFPGA0_XAB_RFINTERFACESW 0x870 #define RFPGA0_XCD_RFINTERFACESW 0x874 #define RFPGA0_XAB_RFPARAMETER 0x878 #define RFPGA0_XCD_RFPARAMETER 0x87c #define RFPGA0_ANALOGPARAMETER1 0x880 #define RFPGA0_ANALOGPARAMETER2 0x884 #define RFPGA0_ANALOGPARAMETER3 0x888 #define RFPGA0_ANALOGPARAMETER4 0x88c #define RFPGA0_XA_LSSIREADBACK 0x8a0 #define RFPGA0_XB_LSSIREADBACK 0x8a4 #define RFPGA0_XC_LSSIREADBACK 0x8a8 #define RFPGA0_XD_LSSIREADBACK 0x8ac #define RFPGA0_PSDREPORT 0x8b4 #define TRANSCEIVERA_HSPI_READBACK 0x8b8 #define TRANSCEIVERB_HSPI_READBACK 0x8bc #define RFPGA0_XAB_RFINTERFACERB 0x8e0 #define RFPGA0_XCD_RFINTERFACERB 0x8e4 #define RFPGA1_RFMOD 0x900 #define RFPGA1_TXBLOCK 0x904 #define RFPGA1_DEBUGSELECT 0x908 #define RFPGA1_TXINFO 0x90c #define RCCK0_SYSTEM 0xa00 #define RCCK0_AFESETTING 0xa04 #define RCCK0_CCA 0xa08 #define RCCK0_RXAGC1 0xa0c #define RCCK0_RXAGC2 0xa10 #define RCCK0_RXHP 0xa14 #define RCCK0_DSPPARAMETER1 0xa18 #define RCCK0_DSPPARAMETER2 0xa1c #define RCCK0_TXFILTER1 0xa20 #define RCCK0_TXFILTER2 0xa24 #define RCCK0_DEBUGPORT 0xa28 #define RCCK0_FALSEALARMREPORT 0xa2c #define RCCK0_TRSSIREPORT 0xa50 #define RCCK0_RXREPORT 0xa54 #define RCCK0_FACOUNTERLOWER 0xa5c #define RCCK0_FACOUNTERUPPER 0xa58 #define ROFDM0_LSTF 0xc00 #define ROFDM0_TRXPATHENABLE 0xc04 #define ROFDM0_TRMUXPAR 0xc08 #define ROFDM0_TRSWISOLATION 0xc0c #define ROFDM0_XARXAFE 0xc10 #define ROFDM0_XARXIQIMBALANCE 0xc14 #define ROFDM0_XBRXAFE 0xc18 #define ROFDM0_XBRXIQIMBALANCE 0xc1c #define ROFDM0_XCRXAFE 0xc20 #define ROFDM0_XCRXIQIMBALANCE 0xc24 #define ROFDM0_XDRXAFE 0xc28 #define ROFDM0_XDRXIQIMBALANCE 0xc2c #define ROFDM0_RXDETECTOR1 0xc30 #define ROFDM0_RXDETECTOR2 0xc34 #define ROFDM0_RXDETECTOR3 0xc38 #define ROFDM0_RXDETECTOR4 0xc3c #define ROFDM0_RXDSP 0xc40 #define ROFDM0_CFO_AND_DAGC 0xc44 #define ROFDM0_CCADROP_THRESHOLD 0xc48 #define ROFDM0_ECCA_THRESHOLD 0xc4c #define ROFDM0_XAAGCCORE1 0xc50 #define ROFDM0_XAAGCCORE2 0xc54 #define ROFDM0_XBAGCCORE1 0xc58 #define ROFDM0_XBAGCCORE2 0xc5c #define ROFDM0_XCAGCCORE1 0xc60 #define ROFDM0_XCAGCCORE2 0xc64 #define ROFDM0_XDAGCCORE1 0xc68 #define ROFDM0_XDAGCCORE2 0xc6c #define ROFDM0_AGCPARAMETER1 0xc70 #define ROFDM0_AGCPARAMETER2 0xc74 #define ROFDM0_AGCRSSITABLE 0xc78 #define ROFDM0_HTSTFAGC 0xc7c #define ROFDM0_XATXIQIMBALANCE 0xc80 #define ROFDM0_XATXAFE 0xc84 #define ROFDM0_XBTXIQIMBALANCE 0xc88 #define ROFDM0_XBTXAFE 0xc8c #define ROFDM0_XCTXIQIMBALANCE 0xc90 #define ROFDM0_XCTXAFE 0xc94 #define ROFDM0_XDTXIQIMBALANCE 0xc98 #define ROFDM0_XDTXAFE 0xc9c #define ROFDM0_RXHP_PARAMETER 0xce0 #define ROFDM0_TXPSEUDO_NOISE_WGT 0xce4 #define ROFDM0_FRAME_SYNC 0xcf0 #define ROFDM0_DFSREPORT 0xcf4 #define ROFDM0_TXCOEFF1 0xca4 #define ROFDM0_TXCOEFF2 0xca8 #define ROFDM0_TXCOEFF3 0xcac #define ROFDM0_TXCOEFF4 0xcb0 #define ROFDM0_TXCOEFF5 0xcb4 #define ROFDM0_TXCOEFF6 0xcb8 #define ROFDM1_LSTF 0xd00 #define ROFDM1_TRXPATHENABLE 0xd04 #define ROFDM1_CFO 0xd08 #define ROFDM1_CSI1 0xd10 #define ROFDM1_SBD 0xd14 #define ROFDM1_CSI2 0xd18 #define ROFDM1_CFOTRACKING 0xd2c #define ROFDM1_TRXMESAURE1 0xd34 #define ROFDM1_INTF_DET 0xd3c #define ROFDM1_PSEUDO_NOISESTATEAB 0xd50 #define ROFDM1_PSEUDO_NOISESTATECD 0xd54 #define ROFDM1_RX_PSEUDO_NOISE_WGT 0xd58 #define ROFDM_PHYCOUNTER1 0xda0 #define ROFDM_PHYCOUNTER2 0xda4 #define ROFDM_PHYCOUNTER3 0xda8 #define ROFDM_SHORT_CFOAB 0xdac #define ROFDM_SHORT_CFOCD 0xdb0 #define ROFDM_LONG_CFOAB 0xdb4 #define ROFDM_LONG_CFOCD 0xdb8 #define ROFDM_TAIL_CFOAB 0xdbc #define ROFDM_TAIL_CFOCD 0xdc0 #define ROFDM_PW_MEASURE1 0xdc4 #define ROFDM_PW_MEASURE2 0xdc8 #define ROFDM_BW_REPORT 0xdcc #define ROFDM_AGC_REPORT 0xdd0 #define ROFDM_RXSNR 0xdd4 #define ROFDM_RXEVMCSI 0xdd8 #define ROFDM_SIG_REPORT 0xddc #define RTXAGC_RATE18_06 0xe00 #define RTXAGC_RATE54_24 0xe04 #define RTXAGC_CCK_MCS32 0xe08 #define RTXAGC_MCS03_MCS00 0xe10 #define RTXAGC_MCS07_MCS04 0xe14 #define RTXAGC_MCS11_MCS08 0xe18 #define RTXAGC_MCS15_MCS12 0xe1c #define RF_AC 0x00 #define RF_IQADJ_G1 0x01 #define RF_IQADJ_G2 0x02 #define RF_POW_TRSW 0x05 #define RF_GAIN_RX 0x06 #define RF_GAIN_TX 0x07 #define RF_TXM_IDAC 0x08 #define RF_BS_IQGEN 0x0F #define RF_MODE1 0x10 #define RF_MODE2 0x11 #define RF_RX_AGC_HP 0x12 #define RF_TX_AGC 0x13 #define RF_BIAS 0x14 #define RF_IPA 0x15 #define RF_POW_ABILITY 0x17 #define RF_MODE_AG 0x18 #define RF_CHANNEL 0x18 #define RF_CHNLBW 0x18 #define RF_TOP 0x19 #define RF_RX_G1 0x1A #define RF_RX_G2 0x1B #define RF_RX_BB2 0x1C #define RF_RX_BB1 0x1D #define RF_RCK1 0x1E #define RF_RCK2 0x1F #define RF_TX_G1 0x20 #define RF_TX_G2 0x21 #define RF_TX_G3 0x22 #define RF_TX_BB1 0x23 #define RF_T_METER 0x24 #define RF_SYN_G1 0x25 #define RF_SYN_G2 0x26 #define RF_SYN_G3 0x27 #define RF_SYN_G4 0x28 #define RF_SYN_G5 0x29 #define RF_SYN_G6 0x2A #define RF_SYN_G7 0x2B #define RF_SYN_G8 0x2C #define RF_RCK_OS 0x30 #define RF_TXPA_G1 0x31 #define RF_TXPA_G2 0x32 #define RF_TXPA_G3 0x33 #define BRFMOD 0x1 #define BCCKEN 0x1000000 #define BOFDMEN 0x2000000 #define BXBTXAGC 0xf00 #define BXCTXAGC 0xf000 #define BXDTXAGC 0xf0000 #define B3WIRE_DATALENGTH 0x800 #define B3WIRE_ADDRESSLENGTH 0x400 #define BRFSI_RFENV 0x10 #define BLSSI_READADDRESS 0x7f800000 #define BLSSI_READEDGE 0x80000000 #define BLSSI_READBACK_DATA 0xfffff #define BADCLKPHASE 0x4000000 #define BCCK_SIDEBAND 0x10 #define BTX_AGCRATECCK 0x7f00 #define MASKBYTE0 0xff #define MASKBYTE1 0xff00 #define MASKBYTE2 0xff0000 #define MASKBYTE3 0xff000000 #define MASKHWORD 0xffff0000 #define MASKLWORD 0x0000ffff #define MASKDWORD 0xffffffff #define MAKS12BITS 0xfffff #define MASK20BITS 0xfffff #define RFREG_OFFSET_MASK 0xfffff #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/phy.h0000644000175000017500000000662312026211315024576 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92S_PHY_H__ #define __RTL92S_PHY_H__ #define MAX_TXPWR_IDX_NMODE_92S 63 #define MAX_DOZE_WAITING_TIMES_9x 64 /* Channel switch:The size of * command tables for switch channel */ #define MAX_PRECMD_CNT 16 #define MAX_RFDEPENDCMD_CNT 16 #define MAX_POSTCMD_CNT 16 #define RF90_PATH_MAX 4 enum version_8192s { VERSION_8192S_ACUT, VERSION_8192S_BCUT, VERSION_8192S_CCUT }; enum swchnlcmd_id { CMDID_END, CMDID_SET_TXPOWEROWER_LEVEL, CMDID_BBREGWRITE10, CMDID_WRITEPORT_ULONG, CMDID_WRITEPORT_USHORT, CMDID_WRITEPORT_UCHAR, CMDID_RF_WRITEREG, }; struct swchnlcmd { enum swchnlcmd_id cmdid; u32 para1; u32 para2; u32 msdelay; }; enum baseband_config_type { /* Radio Path A */ BASEBAND_CONFIG_PHY_REG = 0, /* Radio Path B */ BASEBAND_CONFIG_AGC_TAB = 1, }; #define hal_get_firmwareversion(rtlpriv) \ (((struct rt_firmware *)(rtlpriv->rtlhal.pfirmware))->firmwareversion) u32 rtl92s_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask); void rtl92s_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data); void rtl92s_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation); u32 rtl92s_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); void rtl92s_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); void rtl92s_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type); u8 rtl92s_phy_sw_chnl(struct ieee80211_hw *hw); bool rtl92s_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpower_state); bool rtl92s_phy_mac_config(struct ieee80211_hw *hw); void rtl92s_phy_switch_ephy_parameter(struct ieee80211_hw *hw); bool rtl92s_phy_bb_config(struct ieee80211_hw *hw); bool rtl92s_phy_rf_config(struct ieee80211_hw *hw); void rtl92s_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); void rtl92s_phy_set_txpower(struct ieee80211_hw *hw, u8 channel); bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fwcmd_io); void rtl92s_phy_chk_fwcmd_iodone(struct ieee80211_hw *hw); void rtl92s_phy_set_beacon_hwreg(struct ieee80211_hw *hw, u16 beaconinterval); u8 rtl92s_phy_config_rf(struct ieee80211_hw *hw, enum radio_path rfpath) ; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/phy.c0000644000175000017500000014064612026211315024575 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../ps.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "fw.h" #include "hw.h" #include "table.h" static u32 _rtl92s_phy_calculate_bit_shift(u32 bitmask) { u32 i; for (i = 0; i <= 31; i++) { if (((bitmask >> i) & 0x1) == 1) break; } return i; } u32 rtl92s_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 returnvalue = 0, originalvalue, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask); originalvalue = rtl_read_dword(rtlpriv, regaddr); bitshift = _rtl92s_phy_calculate_bit_shift(bitmask); returnvalue = (originalvalue & bitmask) >> bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "BBR MASK=0x%x Addr[0x%x]=0x%x\n", bitmask, regaddr, originalvalue); return returnvalue; } void rtl92s_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 originalvalue, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, bitmask, data); if (bitmask != MASKDWORD) { originalvalue = rtl_read_dword(rtlpriv, regaddr); bitshift = _rtl92s_phy_calculate_bit_shift(bitmask); data = ((originalvalue & (~bitmask)) | (data << bitshift)); } rtl_write_dword(rtlpriv, regaddr, data); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, bitmask, data); } static u32 _rtl92s_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; u32 newoffset; u32 tmplong, tmplong2; u8 rfpi_enable = 0; u32 retvalue = 0; offset &= 0x3f; newoffset = offset; tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD); if (rfpath == RF90_PATH_A) tmplong2 = tmplong; else tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD); tmplong2 = (tmplong2 & (~BLSSI_READADDRESS)) | (newoffset << 23) | BLSSI_READEDGE; rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, tmplong & (~BLSSI_READEDGE)); mdelay(1); rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2); mdelay(1); rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, tmplong | BLSSI_READEDGE); mdelay(1); if (rfpath == RF90_PATH_A) rfpi_enable = (u8)rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, BIT(8)); else if (rfpath == RF90_PATH_B) rfpi_enable = (u8)rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1, BIT(8)); if (rfpi_enable) retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readbackpi, BLSSI_READBACK_DATA); else retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readback, BLSSI_READBACK_DATA); retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readback, BLSSI_READBACK_DATA); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x]=0x%x\n", rfpath, pphyreg->rflssi_readback, retvalue); return retvalue; } static void _rtl92s_phy_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; u32 data_and_addr = 0; u32 newoffset; offset &= 0x3f; newoffset = offset; data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff; rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n", rfpath, pphyreg->rf3wire_offset, data_and_addr); } u32 rtl92s_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 original_value, readback_value, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n", regaddr, rfpath, bitmask); spin_lock(&rtlpriv->locks.rf_lock); original_value = _rtl92s_phy_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92s_phy_calculate_bit_shift(bitmask); readback_value = (original_value & bitmask) >> bitshift; spin_unlock(&rtlpriv->locks.rf_lock); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x), original_value(%#x)\n", regaddr, rfpath, bitmask, original_value); return readback_value; } void rtl92s_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 original_value, bitshift; if (!((rtlphy->rf_pathmap >> rfpath) & 0x1)) return; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); spin_lock(&rtlpriv->locks.rf_lock); if (bitmask != RFREG_OFFSET_MASK) { original_value = _rtl92s_phy_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92s_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } _rtl92s_phy_rf_serial_write(hw, rfpath, regaddr, data); spin_unlock(&rtlpriv->locks.rf_lock); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); } void rtl92s_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (!is_hal_stop(rtlhal)) { switch (operation) { case SCAN_OPT_BACKUP: rtl92s_phy_set_fw_cmd(hw, FW_CMD_PAUSE_DM_BY_SCAN); break; case SCAN_OPT_RESTORE: rtl92s_phy_set_fw_cmd(hw, FW_CMD_RESUME_DM_BY_SCAN); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Unknown operation\n"); break; } } } void rtl92s_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u8 reg_bw_opmode; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "Switch to %s bandwidth\n", rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ? "20MHz" : "40MHz"); if (rtlphy->set_bwmode_inprogress) return; if (is_hal_stop(rtlhal)) return; rtlphy->set_bwmode_inprogress = true; reg_bw_opmode = rtl_read_byte(rtlpriv, BW_OPMODE); /* dummy read */ rtl_read_byte(rtlpriv, RRSR + 2); switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: reg_bw_opmode |= BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, BW_OPMODE, reg_bw_opmode); break; case HT_CHANNEL_WIDTH_20_40: reg_bw_opmode &= ~BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, BW_OPMODE, reg_bw_opmode); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0); if (rtlhal->version >= VERSION_8192S_BCUT) rtl_write_byte(rtlpriv, RFPGA0_ANALOGPARAMETER2, 0x58); break; case HT_CHANNEL_WIDTH_20_40: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1); rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCK_SIDEBAND, (mac->cur_40_prime_sc >> 1)); rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc); if (rtlhal->version >= VERSION_8192S_BCUT) rtl_write_byte(rtlpriv, RFPGA0_ANALOGPARAMETER2, 0x18); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } rtl92s_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); rtlphy->set_bwmode_inprogress = false; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); } static bool _rtl92s_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, u32 cmdtableidx, u32 cmdtablesz, enum swchnlcmd_id cmdid, u32 para1, u32 para2, u32 msdelay) { struct swchnlcmd *pcmd; if (cmdtable == NULL) { RT_ASSERT(false, "cmdtable cannot be NULL\n"); return false; } if (cmdtableidx >= cmdtablesz) return false; pcmd = cmdtable + cmdtableidx; pcmd->cmdid = cmdid; pcmd->para1 = para1; pcmd->para2 = para2; pcmd->msdelay = msdelay; return true; } static bool _rtl92s_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, u8 *stage, u8 *step, u32 *delay) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct swchnlcmd precommoncmd[MAX_PRECMD_CNT]; u32 precommoncmdcnt; struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT]; u32 postcommoncmdcnt; struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT]; u32 rfdependcmdcnt; struct swchnlcmd *currentcmd = NULL; u8 rfpath; u8 num_total_rfpath = rtlphy->num_total_rfpath; precommoncmdcnt = 0; _rtl92s_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL, 0, 0, 0); _rtl92s_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, MAX_PRECMD_CNT, CMDID_END, 0, 0, 0); postcommoncmdcnt = 0; _rtl92s_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++, MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0); rfdependcmdcnt = 0; RT_ASSERT((channel >= 1 && channel <= 14), "invalid channel for Zebra: %d\n", channel); _rtl92s_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG, RF_CHNLBW, channel, 10); _rtl92s_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0); do { switch (*stage) { case 0: currentcmd = &precommoncmd[*step]; break; case 1: currentcmd = &rfdependcmd[*step]; break; case 2: currentcmd = &postcommoncmd[*step]; break; } if (currentcmd->cmdid == CMDID_END) { if ((*stage) == 2) { return true; } else { (*stage)++; (*step) = 0; continue; } } switch (currentcmd->cmdid) { case CMDID_SET_TXPOWEROWER_LEVEL: rtl92s_phy_set_txpower(hw, channel); break; case CMDID_WRITEPORT_ULONG: rtl_write_dword(rtlpriv, currentcmd->para1, currentcmd->para2); break; case CMDID_WRITEPORT_USHORT: rtl_write_word(rtlpriv, currentcmd->para1, (u16)currentcmd->para2); break; case CMDID_WRITEPORT_UCHAR: rtl_write_byte(rtlpriv, currentcmd->para1, (u8)currentcmd->para2); break; case CMDID_RF_WRITEREG: for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) { rtlphy->rfreg_chnlval[rfpath] = ((rtlphy->rfreg_chnlval[rfpath] & 0xfffffc00) | currentcmd->para2); rtl_set_rfreg(hw, (enum radio_path)rfpath, currentcmd->para1, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[rfpath]); } break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } break; } while (true); (*delay) = currentcmd->msdelay; (*step)++; return false; } u8 rtl92s_phy_sw_chnl(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 delay; bool ret; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "switch to channel%d\n", rtlphy->current_channel); if (rtlphy->sw_chnl_inprogress) return 0; if (rtlphy->set_bwmode_inprogress) return 0; if (is_hal_stop(rtlhal)) return 0; rtlphy->sw_chnl_inprogress = true; rtlphy->sw_chnl_stage = 0; rtlphy->sw_chnl_step = 0; do { if (!rtlphy->sw_chnl_inprogress) break; ret = _rtl92s_phy_sw_chnl_step_by_step(hw, rtlphy->current_channel, &rtlphy->sw_chnl_stage, &rtlphy->sw_chnl_step, &delay); if (!ret) { if (delay > 0) mdelay(delay); else continue; } else { rtlphy->sw_chnl_inprogress = false; } break; } while (true); rtlphy->sw_chnl_inprogress = false; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); return 1; } static void _rtl92se_phy_set_rf_sleep(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 u1btmp; u1btmp = rtl_read_byte(rtlpriv, LDOV12D_CTRL); u1btmp |= BIT(0); rtl_write_byte(rtlpriv, LDOV12D_CTRL, u1btmp); rtl_write_byte(rtlpriv, SPS1_CTRL, 0x0); rtl_write_byte(rtlpriv, TXPAUSE, 0xFF); rtl_write_word(rtlpriv, CMDR, 0x57FC); udelay(100); rtl_write_word(rtlpriv, CMDR, 0x77FC); rtl_write_byte(rtlpriv, PHY_CCA, 0x0); udelay(10); rtl_write_word(rtlpriv, CMDR, 0x37FC); udelay(10); rtl_write_word(rtlpriv, CMDR, 0x77FC); udelay(10); rtl_write_word(rtlpriv, CMDR, 0x57FC); /* we should chnge GPIO to input mode * this will drop away current about 25mA*/ rtl8192se_gpiobit3_cfg_inputmode(hw); } bool rtl92s_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool bresult = true; u8 i, queue_id; struct rtl8192_tx_ring *ring = NULL; if (rfpwr_state == ppsc->rfpwr_state) return false; switch (rfpwr_state) { case ERFON:{ if ((ppsc->rfpwr_state == ERFOFF) && RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) { bool rtstatus; u32 InitializeCount = 0; do { InitializeCount++; RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic enable\n"); rtstatus = rtl_ps_enable_nic(hw); } while (!rtstatus && (InitializeCount < 10)); RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "awake, sleeped:%d ms state_inap:%x\n", jiffies_to_msecs(jiffies - ppsc-> last_sleep_jiffies), rtlpriv->psc.state_inap); ppsc->last_awake_jiffies = jiffies; rtl_write_word(rtlpriv, CMDR, 0x37FC); rtl_write_byte(rtlpriv, TXPAUSE, 0x00); rtl_write_byte(rtlpriv, PHY_CCA, 0x3); } if (mac->link_state == MAC80211_LINKED) rtlpriv->cfg->ops->led_control(hw, LED_CTL_LINK); else rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); break; } case ERFOFF:{ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic disable\n"); rtl_ps_disable_nic(hw); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); else rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); } break; } case ERFSLEEP: if (ppsc->rfpwr_state == ERFOFF) return false; for (queue_id = 0, i = 0; queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { ring = &pcipriv->dev.tx_ring[queue_id]; if (skb_queue_len(&ring->queue) == 0 || queue_id == BEACON_QUEUE) { queue_id++; continue; } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "eRf Off/Sleep: %d times TcbBusyQueue[%d] = %d before doze!\n", i + 1, queue_id, skb_queue_len(&ring->queue)); udelay(10); i++; } if (i >= MAX_DOZE_WAITING_TIMES_9x) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "ERFOFF: %d times TcbBusyQueue[%d] = %d !\n", MAX_DOZE_WAITING_TIMES_9x, queue_id, skb_queue_len(&ring->queue)); break; } } RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "Set ERFSLEEP awaked:%d ms\n", jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies)); RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "sleep awaked:%d ms state_inap:%x\n", jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies), rtlpriv->psc.state_inap); ppsc->last_sleep_jiffies = jiffies; _rtl92se_phy_set_rf_sleep(hw); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); bresult = false; break; } if (bresult) ppsc->rfpwr_state = rfpwr_state; return bresult; } static bool _rtl92s_phy_config_rfpa_bias_current(struct ieee80211_hw *hw, enum radio_path rfpath) { struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool rtstatus = true; u32 tmpval = 0; /* If inferiority IC, we have to increase the PA bias current */ if (rtlhal->ic_class != IC_INFERIORITY_A) { tmpval = rtl92s_phy_query_rf_reg(hw, rfpath, RF_IPA, 0xf); rtl92s_phy_set_rf_reg(hw, rfpath, RF_IPA, 0xf, tmpval + 1); } return rtstatus; } static void _rtl92s_store_pwrindex_diffrate_offset(struct ieee80211_hw *hw, u32 reg_addr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); int index; if (reg_addr == RTXAGC_RATE18_06) index = 0; else if (reg_addr == RTXAGC_RATE54_24) index = 1; else if (reg_addr == RTXAGC_CCK_MCS32) index = 6; else if (reg_addr == RTXAGC_MCS03_MCS00) index = 2; else if (reg_addr == RTXAGC_MCS07_MCS04) index = 3; else if (reg_addr == RTXAGC_MCS11_MCS08) index = 4; else if (reg_addr == RTXAGC_MCS15_MCS12) index = 5; else return; rtlphy->mcs_txpwrlevel_origoffset[rtlphy->pwrgroup_cnt][index] = data; if (index == 5) rtlphy->pwrgroup_cnt++; } static void _rtl92s_phy_init_register_definition(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); /*RF Interface Sowrtware Control */ rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW; rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW; rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW; rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW; /* RF Interface Readback Value */ rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB; rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB; rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB; rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB; /* RF Interface Output (and Enable) */ rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_C].rfintfo = RFPGA0_XC_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_D].rfintfo = RFPGA0_XD_RFINTERFACEOE; /* RF Interface (Output and) Enable */ rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_C].rfintfe = RFPGA0_XC_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_D].rfintfe = RFPGA0_XD_RFINTERFACEOE; /* Addr of LSSI. Wirte RF register by driver */ rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset = RFPGA0_XA_LSSIPARAMETER; rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset = RFPGA0_XB_LSSIPARAMETER; rtlphy->phyreg_def[RF90_PATH_C].rf3wire_offset = RFPGA0_XC_LSSIPARAMETER; rtlphy->phyreg_def[RF90_PATH_D].rf3wire_offset = RFPGA0_XD_LSSIPARAMETER; /* RF parameter */ rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = RFPGA0_XAB_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = RFPGA0_XAB_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = RFPGA0_XCD_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = RFPGA0_XCD_RFPARAMETER; /* Tx AGC Gain Stage (same for all path. Should we remove this?) */ rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE; rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE; rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE; rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE; /* Tranceiver A~D HSSI Parameter-1 */ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1; rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1; rtlphy->phyreg_def[RF90_PATH_C].rfhssi_para1 = RFPGA0_XC_HSSIPARAMETER1; rtlphy->phyreg_def[RF90_PATH_D].rfhssi_para1 = RFPGA0_XD_HSSIPARAMETER1; /* Tranceiver A~D HSSI Parameter-2 */ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2; rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2; rtlphy->phyreg_def[RF90_PATH_C].rfhssi_para2 = RFPGA0_XC_HSSIPARAMETER2; rtlphy->phyreg_def[RF90_PATH_D].rfhssi_para2 = RFPGA0_XD_HSSIPARAMETER2; /* RF switch Control */ rtlphy->phyreg_def[RF90_PATH_A].rfswitch_control = RFPGA0_XAB_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_B].rfswitch_control = RFPGA0_XAB_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_C].rfswitch_control = RFPGA0_XCD_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_D].rfswitch_control = RFPGA0_XCD_SWITCHCONTROL; /* AGC control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1; rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1; rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1; rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1; /* AGC control 2 */ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2; rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2; rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2; rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2; /* RX AFE control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbalance = ROFDM0_XARXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbalance = ROFDM0_XBRXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbalance = ROFDM0_XCRXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbalance = ROFDM0_XDRXIQIMBALANCE; /* RX AFE control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE; rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE; rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE; rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE; /* Tx AFE control 1 */ rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbalance = ROFDM0_XATXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbalance = ROFDM0_XBTXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbalance = ROFDM0_XCTXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbalance = ROFDM0_XDTXIQIMBALANCE; /* Tx AFE control 2 */ rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATXAFE; rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTXAFE; rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTXAFE; rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTXAFE; /* Tranceiver LSSI Readback */ rtlphy->phyreg_def[RF90_PATH_A].rflssi_readback = RFPGA0_XA_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_B].rflssi_readback = RFPGA0_XB_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_C].rflssi_readback = RFPGA0_XC_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_D].rflssi_readback = RFPGA0_XD_LSSIREADBACK; /* Tranceiver LSSI Readback PI mode */ rtlphy->phyreg_def[RF90_PATH_A].rflssi_readbackpi = TRANSCEIVERA_HSPI_READBACK; rtlphy->phyreg_def[RF90_PATH_B].rflssi_readbackpi = TRANSCEIVERB_HSPI_READBACK; } static bool _rtl92s_phy_config_bb(struct ieee80211_hw *hw, u8 configtype) { int i; u32 *phy_reg_table; u32 *agc_table; u16 phy_reg_len, agc_len; agc_len = AGCTAB_ARRAYLENGTH; agc_table = rtl8192seagctab_array; /* Default RF_type: 2T2R */ phy_reg_len = PHY_REG_2T2RARRAYLENGTH; phy_reg_table = rtl8192sephy_reg_2t2rarray; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_len; i = i + 2) { if (phy_reg_table[i] == 0xfe) mdelay(50); else if (phy_reg_table[i] == 0xfd) mdelay(5); else if (phy_reg_table[i] == 0xfc) mdelay(1); else if (phy_reg_table[i] == 0xfb) udelay(50); else if (phy_reg_table[i] == 0xfa) udelay(5); else if (phy_reg_table[i] == 0xf9) udelay(1); /* Add delay for ECS T20 & LG malow platform, */ udelay(1); rtl92s_phy_set_bb_reg(hw, phy_reg_table[i], MASKDWORD, phy_reg_table[i + 1]); } } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { for (i = 0; i < agc_len; i = i + 2) { rtl92s_phy_set_bb_reg(hw, agc_table[i], MASKDWORD, agc_table[i + 1]); /* Add delay for ECS T20 & LG malow platform */ udelay(1); } } return true; } static bool _rtl92s_phy_set_bb_to_diff_rf(struct ieee80211_hw *hw, u8 configtype) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 *phy_regarray2xtxr_table; u16 phy_regarray2xtxr_len; int i; if (rtlphy->rf_type == RF_1T1R) { phy_regarray2xtxr_table = rtl8192sephy_changeto_1t1rarray; phy_regarray2xtxr_len = PHY_CHANGETO_1T1RARRAYLENGTH; } else if (rtlphy->rf_type == RF_1T2R) { phy_regarray2xtxr_table = rtl8192sephy_changeto_1t2rarray; phy_regarray2xtxr_len = PHY_CHANGETO_1T2RARRAYLENGTH; } else { return false; } if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray2xtxr_len; i = i + 3) { if (phy_regarray2xtxr_table[i] == 0xfe) mdelay(50); else if (phy_regarray2xtxr_table[i] == 0xfd) mdelay(5); else if (phy_regarray2xtxr_table[i] == 0xfc) mdelay(1); else if (phy_regarray2xtxr_table[i] == 0xfb) udelay(50); else if (phy_regarray2xtxr_table[i] == 0xfa) udelay(5); else if (phy_regarray2xtxr_table[i] == 0xf9) udelay(1); rtl92s_phy_set_bb_reg(hw, phy_regarray2xtxr_table[i], phy_regarray2xtxr_table[i + 1], phy_regarray2xtxr_table[i + 2]); } } return true; } static bool _rtl92s_phy_config_bb_with_pg(struct ieee80211_hw *hw, u8 configtype) { int i; u32 *phy_table_pg; u16 phy_pg_len; phy_pg_len = PHY_REG_ARRAY_PGLENGTH; phy_table_pg = rtl8192sephy_reg_array_pg; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_pg_len; i = i + 3) { if (phy_table_pg[i] == 0xfe) mdelay(50); else if (phy_table_pg[i] == 0xfd) mdelay(5); else if (phy_table_pg[i] == 0xfc) mdelay(1); else if (phy_table_pg[i] == 0xfb) udelay(50); else if (phy_table_pg[i] == 0xfa) udelay(5); else if (phy_table_pg[i] == 0xf9) udelay(1); _rtl92s_store_pwrindex_diffrate_offset(hw, phy_table_pg[i], phy_table_pg[i + 1], phy_table_pg[i + 2]); rtl92s_phy_set_bb_reg(hw, phy_table_pg[i], phy_table_pg[i + 1], phy_table_pg[i + 2]); } } return true; } static bool _rtl92s_phy_bb_config_parafile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); bool rtstatus = true; /* 1. Read PHY_REG.TXT BB INIT!! */ /* We will separate as 1T1R/1T2R/1T2R_GREEN/2T2R */ if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_2T2R || rtlphy->rf_type == RF_1T1R || rtlphy->rf_type == RF_2T2R_GREEN) { rtstatus = _rtl92s_phy_config_bb(hw, BASEBAND_CONFIG_PHY_REG); if (rtlphy->rf_type != RF_2T2R && rtlphy->rf_type != RF_2T2R_GREEN) /* so we should reconfig BB reg with the right * PHY parameters. */ rtstatus = _rtl92s_phy_set_bb_to_diff_rf(hw, BASEBAND_CONFIG_PHY_REG); } else { rtstatus = false; } if (!rtstatus) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Write BB Reg Fail!!\n"); goto phy_BB8190_Config_ParaFile_Fail; } /* 2. If EEPROM or EFUSE autoload OK, We must config by * PHY_REG_PG.txt */ if (rtlefuse->autoload_failflag == false) { rtlphy->pwrgroup_cnt = 0; rtstatus = _rtl92s_phy_config_bb_with_pg(hw, BASEBAND_CONFIG_PHY_REG); } if (!rtstatus) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "_rtl92s_phy_bb_config_parafile(): BB_PG Reg Fail!!\n"); goto phy_BB8190_Config_ParaFile_Fail; } /* 3. BB AGC table Initialization */ rtstatus = _rtl92s_phy_config_bb(hw, BASEBAND_CONFIG_AGC_TAB); if (!rtstatus) { pr_err("%s(): AGC Table Fail\n", __func__); goto phy_BB8190_Config_ParaFile_Fail; } /* Check if the CCK HighPower is turned ON. */ /* This is used to calculate PWDB. */ rtlphy->cck_high_power = (bool)(rtl92s_phy_query_bb_reg(hw, RFPGA0_XA_HSSIPARAMETER2, 0x200)); phy_BB8190_Config_ParaFile_Fail: return rtstatus; } u8 rtl92s_phy_config_rf(struct ieee80211_hw *hw, enum radio_path rfpath) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); int i; bool rtstatus = true; u32 *radio_a_table; u32 *radio_b_table; u16 radio_a_tblen, radio_b_tblen; radio_a_tblen = RADIOA_1T_ARRAYLENGTH; radio_a_table = rtl8192seradioa_1t_array; /* Using Green mode array table for RF_2T2R_GREEN */ if (rtlphy->rf_type == RF_2T2R_GREEN) { radio_b_table = rtl8192seradiob_gm_array; radio_b_tblen = RADIOB_GM_ARRAYLENGTH; } else { radio_b_table = rtl8192seradiob_array; radio_b_tblen = RADIOB_ARRAYLENGTH; } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Radio No %x\n", rfpath); rtstatus = true; switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radio_a_tblen; i = i + 2) { if (radio_a_table[i] == 0xfe) /* Delay specific ms. Only RF configuration * requires delay. */ mdelay(50); else if (radio_a_table[i] == 0xfd) mdelay(5); else if (radio_a_table[i] == 0xfc) mdelay(1); else if (radio_a_table[i] == 0xfb) udelay(50); else if (radio_a_table[i] == 0xfa) udelay(5); else if (radio_a_table[i] == 0xf9) udelay(1); else rtl92s_phy_set_rf_reg(hw, rfpath, radio_a_table[i], MASK20BITS, radio_a_table[i + 1]); /* Add delay for ECS T20 & LG malow platform */ udelay(1); } /* PA Bias current for inferiority IC */ _rtl92s_phy_config_rfpa_bias_current(hw, rfpath); break; case RF90_PATH_B: for (i = 0; i < radio_b_tblen; i = i + 2) { if (radio_b_table[i] == 0xfe) /* Delay specific ms. Only RF configuration * requires delay.*/ mdelay(50); else if (radio_b_table[i] == 0xfd) mdelay(5); else if (radio_b_table[i] == 0xfc) mdelay(1); else if (radio_b_table[i] == 0xfb) udelay(50); else if (radio_b_table[i] == 0xfa) udelay(5); else if (radio_b_table[i] == 0xf9) udelay(1); else rtl92s_phy_set_rf_reg(hw, rfpath, radio_b_table[i], MASK20BITS, radio_b_table[i + 1]); /* Add delay for ECS T20 & LG malow platform */ udelay(1); } break; case RF90_PATH_C: ; break; case RF90_PATH_D: ; break; default: break; } return rtstatus; } bool rtl92s_phy_mac_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; u32 arraylength; u32 *ptraArray; arraylength = MAC_2T_ARRAYLENGTH; ptraArray = rtl8192semac_2t_array; for (i = 0; i < arraylength; i = i + 2) rtl_write_byte(rtlpriv, ptraArray[i], (u8)ptraArray[i + 1]); return true; } bool rtl92s_phy_bb_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); bool rtstatus = true; u8 pathmap, index, rf_num = 0; u8 path1, path2; _rtl92s_phy_init_register_definition(hw); /* Config BB and AGC */ rtstatus = _rtl92s_phy_bb_config_parafile(hw); /* Check BB/RF confiuration setting. */ /* We only need to configure RF which is turned on. */ path1 = (u8)(rtl92s_phy_query_bb_reg(hw, RFPGA0_TXINFO, 0xf)); mdelay(10); path2 = (u8)(rtl92s_phy_query_bb_reg(hw, ROFDM0_TRXPATHENABLE, 0xf)); pathmap = path1 | path2; rtlphy->rf_pathmap = pathmap; for (index = 0; index < 4; index++) { if ((pathmap >> index) & 0x1) rf_num++; } if ((rtlphy->rf_type == RF_1T1R && rf_num != 1) || (rtlphy->rf_type == RF_1T2R && rf_num != 2) || (rtlphy->rf_type == RF_2T2R && rf_num != 2) || (rtlphy->rf_type == RF_2T2R_GREEN && rf_num != 2)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "RF_Type(%x) does not match RF_Num(%x)!!\n", rtlphy->rf_type, rf_num); RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "path1 0x%x, path2 0x%x, pathmap 0x%x\n", path1, path2, pathmap); } return rtstatus; } bool rtl92s_phy_rf_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); /* Initialize general global value */ if (rtlphy->rf_type == RF_1T1R) rtlphy->num_total_rfpath = 1; else rtlphy->num_total_rfpath = 2; /* Config BB and RF */ return rtl92s_phy_rf6052_config(hw); } void rtl92s_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); /* read rx initial gain */ rtlphy->default_initialgain[0] = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[1] = rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[2] = rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[3] = rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x)\n", rtlphy->default_initialgain[0], rtlphy->default_initialgain[1], rtlphy->default_initialgain[2], rtlphy->default_initialgain[3]); /* read framesync */ rtlphy->framesync = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3, MASKBYTE0); rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2, MASKDWORD); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Default framesync (0x%x) = 0x%x\n", ROFDM0_RXDETECTOR3, rtlphy->framesync); } static void _rtl92s_phy_get_txpower_index(struct ieee80211_hw *hw, u8 channel, u8 *cckpowerlevel, u8 *ofdmpowerLevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 index = (channel - 1); /* 1. CCK */ /* RF-A */ cckpowerlevel[0] = rtlefuse->txpwrlevel_cck[0][index]; /* RF-B */ cckpowerlevel[1] = rtlefuse->txpwrlevel_cck[1][index]; /* 2. OFDM for 1T or 2T */ if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_1T1R) { /* Read HT 40 OFDM TX power */ ofdmpowerLevel[0] = rtlefuse->txpwrlevel_ht40_1s[0][index]; ofdmpowerLevel[1] = rtlefuse->txpwrlevel_ht40_1s[1][index]; } else if (rtlphy->rf_type == RF_2T2R) { /* Read HT 40 OFDM TX power */ ofdmpowerLevel[0] = rtlefuse->txpwrlevel_ht40_2s[0][index]; ofdmpowerLevel[1] = rtlefuse->txpwrlevel_ht40_2s[1][index]; } else { ofdmpowerLevel[0] = 0; ofdmpowerLevel[1] = 0; } } static void _rtl92s_phy_ccxpower_indexcheck(struct ieee80211_hw *hw, u8 channel, u8 *cckpowerlevel, u8 *ofdmpowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); rtlphy->cur_cck_txpwridx = cckpowerlevel[0]; rtlphy->cur_ofdm24g_txpwridx = ofdmpowerlevel[0]; } void rtl92s_phy_set_txpower(struct ieee80211_hw *hw, u8 channel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); /* [0]:RF-A, [1]:RF-B */ u8 cckpowerlevel[2], ofdmpowerLevel[2]; if (!rtlefuse->txpwr_fromeprom) return; /* Mainly we use RF-A Tx Power to write the Tx Power registers, * but the RF-B Tx Power must be calculated by the antenna diff. * So we have to rewrite Antenna gain offset register here. * Please refer to BB register 0x80c * 1. For CCK. * 2. For OFDM 1T or 2T */ _rtl92s_phy_get_txpower_index(hw, channel, &cckpowerlevel[0], &ofdmpowerLevel[0]); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Channel-%d, cckPowerLevel (A / B) = 0x%x / 0x%x, ofdmPowerLevel (A / B) = 0x%x / 0x%x\n", channel, cckpowerlevel[0], cckpowerlevel[1], ofdmpowerLevel[0], ofdmpowerLevel[1]); _rtl92s_phy_ccxpower_indexcheck(hw, channel, &cckpowerlevel[0], &ofdmpowerLevel[0]); rtl92s_phy_rf6052_set_ccktxpower(hw, cckpowerlevel[0]); rtl92s_phy_rf6052_set_ofdmtxpower(hw, &ofdmpowerLevel[0], channel); } void rtl92s_phy_chk_fwcmd_iodone(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u16 pollingcnt = 10000; u32 tmpvalue; /* Make sure that CMD IO has be accepted by FW. */ do { udelay(10); tmpvalue = rtl_read_dword(rtlpriv, WFM5); if (tmpvalue == 0) break; } while (--pollingcnt); if (pollingcnt == 0) RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Set FW Cmd fail!!\n"); } static void _rtl92s_phy_set_fwcmd_io(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 input, current_aid = 0; if (is_hal_stop(rtlhal)) return; /* We re-map RA related CMD IO to combinational ones */ /* if FW version is v.52 or later. */ switch (rtlhal->current_fwcmd_io) { case FW_CMD_RA_REFRESH_N: rtlhal->current_fwcmd_io = FW_CMD_RA_REFRESH_N_COMB; break; case FW_CMD_RA_REFRESH_BG: rtlhal->current_fwcmd_io = FW_CMD_RA_REFRESH_BG_COMB; break; default: break; } switch (rtlhal->current_fwcmd_io) { case FW_CMD_RA_RESET: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_RA_RESET\n"); rtl_write_dword(rtlpriv, WFM5, FW_RA_RESET); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_RA_ACTIVE: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_RA_ACTIVE\n"); rtl_write_dword(rtlpriv, WFM5, FW_RA_ACTIVE); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_RA_REFRESH_N: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_RA_REFRESH_N\n"); input = FW_RA_REFRESH; rtl_write_dword(rtlpriv, WFM5, input); rtl92s_phy_chk_fwcmd_iodone(hw); rtl_write_dword(rtlpriv, WFM5, FW_RA_ENABLE_RSSI_MASK); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_RA_REFRESH_BG: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_RA_REFRESH_BG\n"); rtl_write_dword(rtlpriv, WFM5, FW_RA_REFRESH); rtl92s_phy_chk_fwcmd_iodone(hw); rtl_write_dword(rtlpriv, WFM5, FW_RA_DISABLE_RSSI_MASK); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_RA_REFRESH_N_COMB: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_RA_REFRESH_N_COMB\n"); input = FW_RA_IOT_N_COMB; rtl_write_dword(rtlpriv, WFM5, input); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_RA_REFRESH_BG_COMB: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_RA_REFRESH_BG_COMB\n"); input = FW_RA_IOT_BG_COMB; rtl_write_dword(rtlpriv, WFM5, input); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_IQK_ENABLE: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_IQK_ENABLE\n"); rtl_write_dword(rtlpriv, WFM5, FW_IQK_ENABLE); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_PAUSE_DM_BY_SCAN: /* Lower initial gain */ rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0, 0x17); rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0, 0x17); /* CCA threshold */ rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x40); break; case FW_CMD_RESUME_DM_BY_SCAN: /* CCA threshold */ rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd); rtl92s_phy_set_txpower(hw, rtlphy->current_channel); break; case FW_CMD_HIGH_PWR_DISABLE: if (rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) break; /* Lower initial gain */ rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0, 0x17); rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0, 0x17); /* CCA threshold */ rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x40); break; case FW_CMD_HIGH_PWR_ENABLE: if ((rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) || rtlpriv->dm.dynamic_txpower_enable) break; /* CCA threshold */ rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd); break; case FW_CMD_LPS_ENTER: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_LPS_ENTER\n"); current_aid = rtlpriv->mac80211.assoc_id; rtl_write_dword(rtlpriv, WFM5, (FW_LPS_ENTER | ((current_aid | 0xc000) << 8))); rtl92s_phy_chk_fwcmd_iodone(hw); /* FW set TXOP disable here, so disable EDCA * turbo mode until driver leave LPS */ break; case FW_CMD_LPS_LEAVE: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_LPS_LEAVE\n"); rtl_write_dword(rtlpriv, WFM5, FW_LPS_LEAVE); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_ADD_A2_ENTRY: RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "FW_CMD_ADD_A2_ENTRY\n"); rtl_write_dword(rtlpriv, WFM5, FW_ADD_A2_ENTRY); rtl92s_phy_chk_fwcmd_iodone(hw); break; case FW_CMD_CTRL_DM_BY_DRIVER: RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "FW_CMD_CTRL_DM_BY_DRIVER\n"); rtl_write_dword(rtlpriv, WFM5, FW_CTRL_DM_BY_DRIVER); rtl92s_phy_chk_fwcmd_iodone(hw); break; default: break; } rtl92s_phy_chk_fwcmd_iodone(hw); /* Clear FW CMD operation flag. */ rtlhal->set_fwcmd_inprogress = false; } bool rtl92s_phy_set_fw_cmd(struct ieee80211_hw *hw, enum fwcmd_iotype fw_cmdio) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *digtable = &rtlpriv->dm_digtable; struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 fw_param = FW_CMD_IO_PARA_QUERY(rtlpriv); u16 fw_cmdmap = FW_CMD_IO_QUERY(rtlpriv); bool bPostProcessing = false; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Set FW Cmd(%#x), set_fwcmd_inprogress(%d)\n", fw_cmdio, rtlhal->set_fwcmd_inprogress); do { /* We re-map to combined FW CMD ones if firmware version */ /* is v.53 or later. */ switch (fw_cmdio) { case FW_CMD_RA_REFRESH_N: fw_cmdio = FW_CMD_RA_REFRESH_N_COMB; break; case FW_CMD_RA_REFRESH_BG: fw_cmdio = FW_CMD_RA_REFRESH_BG_COMB; break; default: break; } /* If firmware version is v.62 or later, * use FW_CMD_IO_SET for FW_CMD_CTRL_DM_BY_DRIVER */ if (hal_get_firmwareversion(rtlpriv) >= 0x3E) { if (fw_cmdio == FW_CMD_CTRL_DM_BY_DRIVER) fw_cmdio = FW_CMD_CTRL_DM_BY_DRIVER_NEW; } /* We shall revise all FW Cmd IO into Reg0x364 * DM map table in the future. */ switch (fw_cmdio) { case FW_CMD_RA_INIT: RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "RA init!!\n"); fw_cmdmap |= FW_RA_INIT_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); /* Clear control flag to sync with FW. */ FW_CMD_IO_CLR(rtlpriv, FW_RA_INIT_CTL); break; case FW_CMD_DIG_DISABLE: RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Set DIG disable!!\n"); fw_cmdmap &= ~FW_DIG_ENABLE_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); break; case FW_CMD_DIG_ENABLE: case FW_CMD_DIG_RESUME: if (!(rtlpriv->dm.dm_flag & HAL_DM_DIG_DISABLE)) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Set DIG enable or resume!!\n"); fw_cmdmap |= (FW_DIG_ENABLE_CTL | FW_SS_CTL); FW_CMD_IO_SET(rtlpriv, fw_cmdmap); } break; case FW_CMD_DIG_HALT: RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Set DIG halt!!\n"); fw_cmdmap &= ~(FW_DIG_ENABLE_CTL | FW_SS_CTL); FW_CMD_IO_SET(rtlpriv, fw_cmdmap); break; case FW_CMD_TXPWR_TRACK_THERMAL: { u8 thermalval = 0; fw_cmdmap |= FW_PWR_TRK_CTL; /* Clear FW parameter in terms of thermal parts. */ fw_param &= FW_PWR_TRK_PARAM_CLR; thermalval = rtlpriv->dm.thermalvalue; fw_param |= ((thermalval << 24) | (rtlefuse->thermalmeter[0] << 16)); RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Set TxPwr tracking!! FwCmdMap(%#x), FwParam(%#x)\n", fw_cmdmap, fw_param); FW_CMD_PARA_SET(rtlpriv, fw_param); FW_CMD_IO_SET(rtlpriv, fw_cmdmap); /* Clear control flag to sync with FW. */ FW_CMD_IO_CLR(rtlpriv, FW_PWR_TRK_CTL); } break; /* The following FW CMDs are only compatible to * v.53 or later. */ case FW_CMD_RA_REFRESH_N_COMB: fw_cmdmap |= FW_RA_N_CTL; /* Clear RA BG mode control. */ fw_cmdmap &= ~(FW_RA_BG_CTL | FW_RA_INIT_CTL); /* Clear FW parameter in terms of RA parts. */ fw_param &= FW_RA_PARAM_CLR; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "[FW CMD] [New Version] Set RA/IOT Comb in n mode!! FwCmdMap(%#x), FwParam(%#x)\n", fw_cmdmap, fw_param); FW_CMD_PARA_SET(rtlpriv, fw_param); FW_CMD_IO_SET(rtlpriv, fw_cmdmap); /* Clear control flag to sync with FW. */ FW_CMD_IO_CLR(rtlpriv, FW_RA_N_CTL); break; case FW_CMD_RA_REFRESH_BG_COMB: fw_cmdmap |= FW_RA_BG_CTL; /* Clear RA n-mode control. */ fw_cmdmap &= ~(FW_RA_N_CTL | FW_RA_INIT_CTL); /* Clear FW parameter in terms of RA parts. */ fw_param &= FW_RA_PARAM_CLR; FW_CMD_PARA_SET(rtlpriv, fw_param); FW_CMD_IO_SET(rtlpriv, fw_cmdmap); /* Clear control flag to sync with FW. */ FW_CMD_IO_CLR(rtlpriv, FW_RA_BG_CTL); break; case FW_CMD_IQK_ENABLE: fw_cmdmap |= FW_IQK_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); /* Clear control flag to sync with FW. */ FW_CMD_IO_CLR(rtlpriv, FW_IQK_CTL); break; /* The following FW CMD is compatible to v.62 or later. */ case FW_CMD_CTRL_DM_BY_DRIVER_NEW: fw_cmdmap |= FW_DRIVER_CTRL_DM_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); break; /* The followed FW Cmds needs post-processing later. */ case FW_CMD_RESUME_DM_BY_SCAN: fw_cmdmap |= (FW_DIG_ENABLE_CTL | FW_HIGH_PWR_ENABLE_CTL | FW_SS_CTL); if (rtlpriv->dm.dm_flag & HAL_DM_DIG_DISABLE || !digtable->dig_enable_flag) fw_cmdmap &= ~FW_DIG_ENABLE_CTL; if ((rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) || rtlpriv->dm.dynamic_txpower_enable) fw_cmdmap &= ~FW_HIGH_PWR_ENABLE_CTL; if ((digtable->dig_ext_port_stage == DIG_EXT_PORT_STAGE_0) || (digtable->dig_ext_port_stage == DIG_EXT_PORT_STAGE_1)) fw_cmdmap &= ~FW_DIG_ENABLE_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); bPostProcessing = true; break; case FW_CMD_PAUSE_DM_BY_SCAN: fw_cmdmap &= ~(FW_DIG_ENABLE_CTL | FW_HIGH_PWR_ENABLE_CTL | FW_SS_CTL); FW_CMD_IO_SET(rtlpriv, fw_cmdmap); bPostProcessing = true; break; case FW_CMD_HIGH_PWR_DISABLE: fw_cmdmap &= ~FW_HIGH_PWR_ENABLE_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); bPostProcessing = true; break; case FW_CMD_HIGH_PWR_ENABLE: if (!(rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) && !rtlpriv->dm.dynamic_txpower_enable) { fw_cmdmap |= (FW_HIGH_PWR_ENABLE_CTL | FW_SS_CTL); FW_CMD_IO_SET(rtlpriv, fw_cmdmap); bPostProcessing = true; } break; case FW_CMD_DIG_MODE_FA: fw_cmdmap |= FW_FA_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); break; case FW_CMD_DIG_MODE_SS: fw_cmdmap &= ~FW_FA_CTL; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); break; case FW_CMD_PAPE_CONTROL: RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "[FW CMD] Set PAPE Control\n"); fw_cmdmap &= ~FW_PAPE_CTL_BY_SW_HW; FW_CMD_IO_SET(rtlpriv, fw_cmdmap); break; default: /* Pass to original FW CMD processing callback * routine. */ bPostProcessing = true; break; } } while (false); /* We shall post processing these FW CMD if * variable bPostProcessing is set. */ if (bPostProcessing && !rtlhal->set_fwcmd_inprogress) { rtlhal->set_fwcmd_inprogress = true; /* Update current FW Cmd for callback use. */ rtlhal->current_fwcmd_io = fw_cmdio; } else { return false; } _rtl92s_phy_set_fwcmd_io(hw); return true; } static void _rtl92s_phy_check_ephy_switchready(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 delay = 100; u8 regu1; regu1 = rtl_read_byte(rtlpriv, 0x554); while ((regu1 & BIT(5)) && (delay > 0)) { regu1 = rtl_read_byte(rtlpriv, 0x554); delay--; /* We delay only 50us to prevent * being scheduled out. */ udelay(50); } } void rtl92s_phy_switch_ephy_parameter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); /* The way to be capable to switch clock request * when the PG setting does not support clock request. * This is the backdoor solution to switch clock * request before ASPM or D3. */ rtl_write_dword(rtlpriv, 0x540, 0x73c11); rtl_write_dword(rtlpriv, 0x548, 0x2407c); /* Switch EPHY parameter!!!! */ rtl_write_word(rtlpriv, 0x550, 0x1000); rtl_write_byte(rtlpriv, 0x554, 0x20); _rtl92s_phy_check_ephy_switchready(hw); rtl_write_word(rtlpriv, 0x550, 0xa0eb); rtl_write_byte(rtlpriv, 0x554, 0x3e); _rtl92s_phy_check_ephy_switchready(hw); rtl_write_word(rtlpriv, 0x550, 0xff80); rtl_write_byte(rtlpriv, 0x554, 0x39); _rtl92s_phy_check_ephy_switchready(hw); /* Delay L1 enter time */ if (ppsc->support_aspm && !ppsc->support_backdoor) rtl_write_byte(rtlpriv, 0x560, 0x40); else rtl_write_byte(rtlpriv, 0x560, 0x00); } void rtl92s_phy_set_beacon_hwreg(struct ieee80211_hw *hw, u16 BeaconInterval) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_dword(rtlpriv, WFM5, 0xF1000000 | (BeaconInterval << 8)); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/led.h0000644000175000017500000000277112026211315024542 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __REALTEK_PCI92SE_LED_H__ #define __REALTEK_PCI92SE_LED_H__ void rtl92se_init_sw_leds(struct ieee80211_hw *hw); void rtl92se_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92se_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92se_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/led.c0000644000175000017500000001006612026211315024531 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "reg.h" #include "led.h" static void _rtl92se_init_led(struct ieee80211_hw *hw, struct rtl_led *pled, enum rtl_led_pin ledpin) { pled->hw = hw; pled->ledpin = ledpin; pled->ledon = false; } void rtl92se_init_sw_leds(struct ieee80211_hw *hw) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); _rtl92se_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0); _rtl92se_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1); } void rtl92se_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled) { u8 ledcfg; struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", LEDCFG, pled->ledpin); ledcfg = rtl_read_byte(rtlpriv, LEDCFG); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: rtl_write_byte(rtlpriv, LEDCFG, ledcfg & 0xf0); break; case LED_PIN_LED1: rtl_write_byte(rtlpriv, LEDCFG, ledcfg & 0x0f); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = true; } void rtl92se_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled) { struct rtl_priv *rtlpriv; struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); u8 ledcfg; rtlpriv = rtl_priv(hw); if (!rtlpriv || rtlpriv->max_fw_size) return; RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", LEDCFG, pled->ledpin); ledcfg = rtl_read_byte(rtlpriv, LEDCFG); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: ledcfg &= 0xf0; if (pcipriv->ledctl.led_opendrain) rtl_write_byte(rtlpriv, LEDCFG, (ledcfg | BIT(1))); else rtl_write_byte(rtlpriv, LEDCFG, (ledcfg | BIT(3))); break; case LED_PIN_LED1: ledcfg &= 0x0f; rtl_write_byte(rtlpriv, LEDCFG, (ledcfg | BIT(3))); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = false; } static void _rtl92se_sw_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); switch (ledaction) { case LED_CTL_POWER_ON: case LED_CTL_LINK: case LED_CTL_NO_LINK: rtl92se_sw_led_on(hw, pLed0); break; case LED_CTL_POWER_OFF: rtl92se_sw_led_off(hw, pLed0); break; default: break; } } void rtl92se_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) && (ledaction == LED_CTL_TX || ledaction == LED_CTL_RX || ledaction == LED_CTL_SITE_SURVEY || ledaction == LED_CTL_LINK || ledaction == LED_CTL_NO_LINK || ledaction == LED_CTL_START_TO_LINK || ledaction == LED_CTL_POWER_ON)) { return; } RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "ledaction %d\n", ledaction); _rtl92se_sw_led_control(hw, ledaction); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/hw.h0000644000175000017500000000610212026211315024404 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __REALTEK_PCI92SE_HW_H__ #define __REALTEK_PCI92SE_HW_H__ #define MSR_LINK_MANAGED 2 #define MSR_LINK_NONE 0 #define MSR_LINK_SHIFT 0 #define MSR_LINK_ADHOC 1 #define MSR_LINK_MASTER 3 enum WIRELESS_NETWORK_TYPE { WIRELESS_11B = 1, WIRELESS_11G = 2, WIRELESS_11A = 4, WIRELESS_11N = 8 }; void rtl92se_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92se_read_eeprom_info(struct ieee80211_hw *hw); void rtl92se_interrupt_recognized(struct ieee80211_hw *hw, u32 *inta, u32 *intb); int rtl92se_hw_init(struct ieee80211_hw *hw); void rtl92se_card_disable(struct ieee80211_hw *hw); void rtl92se_enable_interrupt(struct ieee80211_hw *hw); void rtl92se_disable_interrupt(struct ieee80211_hw *hw); int rtl92se_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type); void rtl92se_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid); void rtl92se_set_mac_addr(struct rtl_io *io, const u8 * addr); void rtl92se_set_qos(struct ieee80211_hw *hw, int aci); void rtl92se_set_beacon_related_registers(struct ieee80211_hw *hw); void rtl92se_set_beacon_interval(struct ieee80211_hw *hw); void rtl92se_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr); void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92se_update_hal_rate_tbl(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level); void rtl92se_update_channel_access_setting(struct ieee80211_hw *hw); bool rtl92se_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid); void rtl8192se_gpiobit3_cfg_inputmode(struct ieee80211_hw *hw); void rtl92se_enable_hw_security_config(struct ieee80211_hw *hw); void rtl92se_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); void rtl92se_suspend(struct ieee80211_hw *hw); void rtl92se_resume(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/hw.c0000644000175000017500000021227112026211315024405 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../efuse.h" #include "../base.h" #include "../regd.h" #include "../cam.h" #include "../ps.h" #include "../pci.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "fw.h" #include "led.h" #include "hw.h" void rtl92se_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); switch (variable) { case HW_VAR_RCR: { *((u32 *) (val)) = rtlpci->receive_config; break; } case HW_VAR_RF_STATE: { *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state; break; } case HW_VAR_FW_PSMODE_STATUS: { *((bool *) (val)) = ppsc->fw_current_inpsmode; break; } case HW_VAR_CORRECT_TSF: { u64 tsf; u32 *ptsf_low = (u32 *)&tsf; u32 *ptsf_high = ((u32 *)&tsf) + 1; *ptsf_high = rtl_read_dword(rtlpriv, (TSFR + 4)); *ptsf_low = rtl_read_dword(rtlpriv, TSFR); *((u64 *) (val)) = tsf; break; } case HW_VAR_MRC: { *((bool *)(val)) = rtlpriv->dm.current_mrc_switch; break; } default: { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } } void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); switch (variable) { case HW_VAR_ETHER_ADDR:{ rtl_write_dword(rtlpriv, IDR0, ((u32 *)(val))[0]); rtl_write_word(rtlpriv, IDR4, ((u16 *)(val + 4))[0]); break; } case HW_VAR_BASIC_RATE:{ u16 rate_cfg = ((u16 *) val)[0]; u8 rate_index = 0; if (rtlhal->version == VERSION_8192S_ACUT) rate_cfg = rate_cfg & 0x150; else rate_cfg = rate_cfg & 0x15f; rate_cfg |= 0x01; rtl_write_byte(rtlpriv, RRSR, rate_cfg & 0xff); rtl_write_byte(rtlpriv, RRSR + 1, (rate_cfg >> 8) & 0xff); while (rate_cfg > 0x1) { rate_cfg = (rate_cfg >> 1); rate_index++; } rtl_write_byte(rtlpriv, INIRTSMCS_SEL, rate_index); break; } case HW_VAR_BSSID:{ rtl_write_dword(rtlpriv, BSSIDR, ((u32 *)(val))[0]); rtl_write_word(rtlpriv, BSSIDR + 4, ((u16 *)(val + 4))[0]); break; } case HW_VAR_SIFS:{ rtl_write_byte(rtlpriv, SIFS_OFDM, val[0]); rtl_write_byte(rtlpriv, SIFS_OFDM + 1, val[1]); break; } case HW_VAR_SLOT_TIME:{ u8 e_aci; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "HW_VAR_SLOT_TIME %x\n", val[0]); rtl_write_byte(rtlpriv, SLOT_TIME, val[0]); for (e_aci = 0; e_aci < AC_MAX; e_aci++) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, (&e_aci)); } break; } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; u8 short_preamble = (bool) (*val); reg_tmp = (mac->cur_40_prime_sc) << 5; if (short_preamble) reg_tmp |= 0x80; rtl_write_byte(rtlpriv, RRSR + 2, reg_tmp); break; } case HW_VAR_AMPDU_MIN_SPACE:{ u8 min_spacing_to_set; u8 sec_min_space; min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { if (rtlpriv->sec.pairwise_enc_algorithm == NO_ENCRYPTION) sec_min_space = 0; else sec_min_space = 1; if (min_spacing_to_set < sec_min_space) min_spacing_to_set = sec_min_space; if (min_spacing_to_set > 5) min_spacing_to_set = 5; mac->min_space_cfg = ((mac->min_space_cfg & 0xf8) | min_spacing_to_set); *val = min_spacing_to_set; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, AMPDU_MIN_SPACE, mac->min_space_cfg); } break; } case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; density_to_set = *val; mac->min_space_cfg = rtlpriv->rtlhal.minspace_cfg; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_SHORTGI_DENSITY: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, AMPDU_MIN_SPACE, mac->min_space_cfg); break; } case HW_VAR_AMPDU_FACTOR:{ u8 factor_toset; u8 regtoset; u8 factorlevel[18] = { 2, 4, 4, 7, 7, 13, 13, 13, 2, 7, 7, 13, 13, 15, 15, 15, 15, 0}; u8 index = 0; factor_toset = *val; if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) factor_toset = 0xf; for (index = 0; index < 17; index++) { if (factorlevel[index] > factor_toset) factorlevel[index] = factor_toset; } for (index = 0; index < 8; index++) { regtoset = ((factorlevel[index * 2]) | (factorlevel[index * 2 + 1] << 4)); rtl_write_byte(rtlpriv, AGGLEN_LMT_L + index, regtoset); } regtoset = ((factorlevel[16]) | (factorlevel[17] << 4)); rtl_write_byte(rtlpriv, AGGLEN_LMT_H, regtoset); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_FACTOR: %#x\n", factor_toset); } break; } case HW_VAR_AC_PARAM:{ u8 e_aci = *val; rtl92s_dm_init_edca_turbo(hw); if (rtlpci->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, &e_aci); break; } case HW_VAR_ACM_CTRL:{ u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&( mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; u8 acm_ctrl = rtl_read_byte(rtlpriv, AcmHwCtrl); acm_ctrl = acm_ctrl | ((rtlpci->acm_method == 2) ? 0x0 : 0x1); if (acm) { switch (e_aci) { case AC0_BE: acm_ctrl |= AcmHw_BeqEn; break; case AC2_VI: acm_ctrl |= AcmHw_ViqEn; break; case AC3_VO: acm_ctrl |= AcmHw_VoqEn; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "HW_VAR_ACM_CTRL acm set failed: eACI is %d\n", acm); break; } } else { switch (e_aci) { case AC0_BE: acm_ctrl &= (~AcmHw_BeqEn); break; case AC2_VI: acm_ctrl &= (~AcmHw_ViqEn); break; case AC3_VO: acm_ctrl &= (~AcmHw_BeqEn); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE, "HW_VAR_ACM_CTRL Write 0x%X\n", acm_ctrl); rtl_write_byte(rtlpriv, AcmHwCtrl, acm_ctrl); break; } case HW_VAR_RCR:{ rtl_write_dword(rtlpriv, RCR, ((u32 *) (val))[0]); rtlpci->receive_config = ((u32 *) (val))[0]; break; } case HW_VAR_RETRY_LIMIT:{ u8 retry_limit = val[0]; rtl_write_word(rtlpriv, RETRY_LIMIT, retry_limit << RETRY_LIMIT_SHORT_SHIFT | retry_limit << RETRY_LIMIT_LONG_SHIFT); break; } case HW_VAR_DUAL_TSF_RST: { break; } case HW_VAR_EFUSE_BYTES: { rtlefuse->efuse_usedbytes = *((u16 *) val); break; } case HW_VAR_EFUSE_USAGE: { rtlefuse->efuse_usedpercentage = *val; break; } case HW_VAR_IO_CMD: { break; } case HW_VAR_WPA_CONFIG: { rtl_write_byte(rtlpriv, REG_SECR, *val); break; } case HW_VAR_SET_RPWM:{ break; } case HW_VAR_H2C_FW_PWRMODE:{ break; } case HW_VAR_FW_PSMODE_STATUS: { ppsc->fw_current_inpsmode = *((bool *) val); break; } case HW_VAR_H2C_FW_JOINBSSRPT:{ break; } case HW_VAR_AID:{ break; } case HW_VAR_CORRECT_TSF:{ break; } case HW_VAR_MRC: { bool bmrc_toset = *((bool *)val); u8 u1bdata = 0; if (bmrc_toset) { rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x33); u1bdata = (u8)rtl_get_bbreg(hw, ROFDM1_TRXPATHENABLE, MASKBYTE0); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, MASKBYTE0, ((u1bdata & 0xf0) | 0x03)); u1bdata = (u8)rtl_get_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE1); rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE1, (u1bdata | 0x04)); /* Update current settings. */ rtlpriv->dm.current_mrc_switch = bmrc_toset; } else { rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x13); u1bdata = (u8)rtl_get_bbreg(hw, ROFDM1_TRXPATHENABLE, MASKBYTE0); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, MASKBYTE0, ((u1bdata & 0xf0) | 0x01)); u1bdata = (u8)rtl_get_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE1); rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE1, (u1bdata & 0xfb)); /* Update current settings. */ rtlpriv->dm.current_mrc_switch = bmrc_toset; } break; } default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } void rtl92se_enable_hw_security_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 sec_reg_value = 0x0; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n", rtlpriv->sec.pairwise_enc_algorithm, rtlpriv->sec.group_enc_algorithm); if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "not open hw encryption\n"); return; } sec_reg_value = SCR_TXENCENABLE | SCR_RXENCENABLE; if (rtlpriv->sec.use_defaultkey) { sec_reg_value |= SCR_TXUSEDK; sec_reg_value |= SCR_RXUSEDK; } RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The SECR-value %x\n", sec_reg_value); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); } static u8 _rtl92ce_halset_sysclk(struct ieee80211_hw *hw, u8 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 waitcount = 100; bool bresult = false; u8 tmpvalue; rtl_write_byte(rtlpriv, SYS_CLKR + 1, data); /* Wait the MAC synchronized. */ udelay(400); /* Check if it is set ready. */ tmpvalue = rtl_read_byte(rtlpriv, SYS_CLKR + 1); bresult = ((tmpvalue & BIT(7)) == (data & BIT(7))); if ((data & (BIT(6) | BIT(7))) == false) { waitcount = 100; tmpvalue = 0; while (1) { waitcount--; tmpvalue = rtl_read_byte(rtlpriv, SYS_CLKR + 1); if ((tmpvalue & BIT(6))) break; pr_err("wait for BIT(6) return value %x\n", tmpvalue); if (waitcount == 0) break; udelay(10); } if (waitcount == 0) bresult = false; else bresult = true; } return bresult; } void rtl8192se_gpiobit3_cfg_inputmode(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 u1tmp; /* The following config GPIO function */ rtl_write_byte(rtlpriv, MAC_PINMUX_CFG, (GPIOMUX_EN | GPIOSEL_GPIO)); u1tmp = rtl_read_byte(rtlpriv, GPIO_IO_SEL); /* config GPIO3 to input */ u1tmp &= HAL_8192S_HW_GPIO_OFF_MASK; rtl_write_byte(rtlpriv, GPIO_IO_SEL, u1tmp); } static u8 _rtl92se_rf_onoff_detect(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 u1tmp; u8 retval = ERFON; /* The following config GPIO function */ rtl_write_byte(rtlpriv, MAC_PINMUX_CFG, (GPIOMUX_EN | GPIOSEL_GPIO)); u1tmp = rtl_read_byte(rtlpriv, GPIO_IO_SEL); /* config GPIO3 to input */ u1tmp &= HAL_8192S_HW_GPIO_OFF_MASK; rtl_write_byte(rtlpriv, GPIO_IO_SEL, u1tmp); /* On some of the platform, driver cannot read correct * value without delay between Write_GPIO_SEL and Read_GPIO_IN */ mdelay(10); /* check GPIO3 */ u1tmp = rtl_read_byte(rtlpriv, GPIO_IN_SE); retval = (u1tmp & HAL_8192S_HW_GPIO_OFF_BIT) ? ERFON : ERFOFF; return retval; } static void _rtl92se_macconfig_before_fwdownload(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 i; u8 tmpu1b; u16 tmpu2b; u8 pollingcnt = 20; if (rtlpci->first_init) { /* Reset PCIE Digital */ tmpu1b = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); tmpu1b &= 0xFE; rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmpu1b); udelay(1); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmpu1b | BIT(0)); } /* Switch to SW IO control */ tmpu1b = rtl_read_byte(rtlpriv, (SYS_CLKR + 1)); if (tmpu1b & BIT(7)) { tmpu1b &= ~(BIT(6) | BIT(7)); /* Set failed, return to prevent hang. */ if (!_rtl92ce_halset_sysclk(hw, tmpu1b)) return; } rtl_write_byte(rtlpriv, AFE_PLL_CTRL, 0x0); udelay(50); rtl_write_byte(rtlpriv, LDOA15_CTRL, 0x34); udelay(50); /* Clear FW RPWM for FW control LPS.*/ rtl_write_byte(rtlpriv, RPWM, 0x0); /* Reset MAC-IO and CPU and Core Digital BIT(10)/11/15 */ tmpu1b = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); tmpu1b &= 0x73; rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmpu1b); /* wait for BIT 10/11/15 to pull high automatically!! */ mdelay(1); rtl_write_byte(rtlpriv, CMDR, 0); rtl_write_byte(rtlpriv, TCR, 0); /* Data sheet not define 0x562!!! Copy from WMAC!!!!! */ tmpu1b = rtl_read_byte(rtlpriv, 0x562); tmpu1b |= 0x08; rtl_write_byte(rtlpriv, 0x562, tmpu1b); tmpu1b &= ~(BIT(3)); rtl_write_byte(rtlpriv, 0x562, tmpu1b); /* Enable AFE clock source */ tmpu1b = rtl_read_byte(rtlpriv, AFE_XTAL_CTRL); rtl_write_byte(rtlpriv, AFE_XTAL_CTRL, (tmpu1b | 0x01)); /* Delay 1.5ms */ mdelay(2); tmpu1b = rtl_read_byte(rtlpriv, AFE_XTAL_CTRL + 1); rtl_write_byte(rtlpriv, AFE_XTAL_CTRL + 1, (tmpu1b & 0xfb)); /* Enable AFE Macro Block's Bandgap */ tmpu1b = rtl_read_byte(rtlpriv, AFE_MISC); rtl_write_byte(rtlpriv, AFE_MISC, (tmpu1b | BIT(0))); mdelay(1); /* Enable AFE Mbias */ tmpu1b = rtl_read_byte(rtlpriv, AFE_MISC); rtl_write_byte(rtlpriv, AFE_MISC, (tmpu1b | 0x02)); mdelay(1); /* Enable LDOA15 block */ tmpu1b = rtl_read_byte(rtlpriv, LDOA15_CTRL); rtl_write_byte(rtlpriv, LDOA15_CTRL, (tmpu1b | BIT(0))); /* Set Digital Vdd to Retention isolation Path. */ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_ISO_CTRL); rtl_write_word(rtlpriv, REG_SYS_ISO_CTRL, (tmpu2b | BIT(11))); /* For warm reboot NIC disappera bug. */ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(13))); rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, 0x68); /* Enable AFE PLL Macro Block */ /* We need to delay 100u before enabling PLL. */ udelay(200); tmpu1b = rtl_read_byte(rtlpriv, AFE_PLL_CTRL); rtl_write_byte(rtlpriv, AFE_PLL_CTRL, (tmpu1b | BIT(0) | BIT(4))); /* for divider reset */ udelay(100); rtl_write_byte(rtlpriv, AFE_PLL_CTRL, (tmpu1b | BIT(0) | BIT(4) | BIT(6))); udelay(10); rtl_write_byte(rtlpriv, AFE_PLL_CTRL, (tmpu1b | BIT(0) | BIT(4))); udelay(10); /* Enable MAC 80MHZ clock */ tmpu1b = rtl_read_byte(rtlpriv, AFE_PLL_CTRL + 1); rtl_write_byte(rtlpriv, AFE_PLL_CTRL + 1, (tmpu1b | BIT(0))); mdelay(1); /* Release isolation AFE PLL & MD */ rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL, 0xA6); /* Enable MAC clock */ tmpu2b = rtl_read_word(rtlpriv, SYS_CLKR); rtl_write_word(rtlpriv, SYS_CLKR, (tmpu2b | BIT(12) | BIT(11))); /* Enable Core digital and enable IOREG R/W */ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(11))); tmpu1b = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmpu1b & ~(BIT(7))); /* enable REG_EN */ rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(11) | BIT(15))); /* Switch the control path. */ tmpu2b = rtl_read_word(rtlpriv, SYS_CLKR); rtl_write_word(rtlpriv, SYS_CLKR, (tmpu2b & (~BIT(2)))); tmpu1b = rtl_read_byte(rtlpriv, (SYS_CLKR + 1)); tmpu1b = ((tmpu1b | BIT(7)) & (~BIT(6))); if (!_rtl92ce_halset_sysclk(hw, tmpu1b)) return; /* Set failed, return to prevent hang. */ rtl_write_word(rtlpriv, CMDR, 0x07FC); /* MH We must enable the section of code to prevent load IMEM fail. */ /* Load MAC register from WMAc temporarily We simulate macreg. */ /* txt HW will provide MAC txt later */ rtl_write_byte(rtlpriv, 0x6, 0x30); rtl_write_byte(rtlpriv, 0x49, 0xf0); rtl_write_byte(rtlpriv, 0x4b, 0x81); rtl_write_byte(rtlpriv, 0xb5, 0x21); rtl_write_byte(rtlpriv, 0xdc, 0xff); rtl_write_byte(rtlpriv, 0xdd, 0xff); rtl_write_byte(rtlpriv, 0xde, 0xff); rtl_write_byte(rtlpriv, 0xdf, 0xff); rtl_write_byte(rtlpriv, 0x11a, 0x00); rtl_write_byte(rtlpriv, 0x11b, 0x00); for (i = 0; i < 32; i++) rtl_write_byte(rtlpriv, INIMCS_SEL + i, 0x1b); rtl_write_byte(rtlpriv, 0x236, 0xff); rtl_write_byte(rtlpriv, 0x503, 0x22); if (ppsc->support_aspm && !ppsc->support_backdoor) rtl_write_byte(rtlpriv, 0x560, 0x40); else rtl_write_byte(rtlpriv, 0x560, 0x00); rtl_write_byte(rtlpriv, DBG_PORT, 0x91); /* Set RX Desc Address */ rtl_write_dword(rtlpriv, RDQDA, rtlpci->rx_ring[RX_MPDU_QUEUE].dma); rtl_write_dword(rtlpriv, RCDA, rtlpci->rx_ring[RX_CMD_QUEUE].dma); /* Set TX Desc Address */ rtl_write_dword(rtlpriv, TBKDA, rtlpci->tx_ring[BK_QUEUE].dma); rtl_write_dword(rtlpriv, TBEDA, rtlpci->tx_ring[BE_QUEUE].dma); rtl_write_dword(rtlpriv, TVIDA, rtlpci->tx_ring[VI_QUEUE].dma); rtl_write_dword(rtlpriv, TVODA, rtlpci->tx_ring[VO_QUEUE].dma); rtl_write_dword(rtlpriv, TBDA, rtlpci->tx_ring[BEACON_QUEUE].dma); rtl_write_dword(rtlpriv, TCDA, rtlpci->tx_ring[TXCMD_QUEUE].dma); rtl_write_dword(rtlpriv, TMDA, rtlpci->tx_ring[MGNT_QUEUE].dma); rtl_write_dword(rtlpriv, THPDA, rtlpci->tx_ring[HIGH_QUEUE].dma); rtl_write_dword(rtlpriv, HDA, rtlpci->tx_ring[HCCA_QUEUE].dma); rtl_write_word(rtlpriv, CMDR, 0x37FC); /* To make sure that TxDMA can ready to download FW. */ /* We should reset TxDMA if IMEM RPT was not ready. */ do { tmpu1b = rtl_read_byte(rtlpriv, TCR); if ((tmpu1b & TXDMA_INIT_VALUE) == TXDMA_INIT_VALUE) break; udelay(5); } while (pollingcnt--); if (pollingcnt <= 0) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Polling TXDMA_INIT_VALUE timeout!! Current TCR(%#x)\n", tmpu1b); tmpu1b = rtl_read_byte(rtlpriv, CMDR); rtl_write_byte(rtlpriv, CMDR, tmpu1b & (~TXDMA_EN)); udelay(2); /* Reset TxDMA */ rtl_write_byte(rtlpriv, CMDR, tmpu1b | TXDMA_EN); } /* After MACIO reset,we must refresh LED state. */ if ((ppsc->rfoff_reason == RF_CHANGE_BY_IPS) || (ppsc->rfoff_reason == 0)) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); enum rf_pwrstate rfpwr_state_toset; rfpwr_state_toset = _rtl92se_rf_onoff_detect(hw); if (rfpwr_state_toset == ERFON) rtl92se_sw_led_on(hw, pLed0); } } static void _rtl92se_macconfig_after_fwdownload(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 i; u16 tmpu2b; /* 1. System Configure Register (Offset: 0x0000 - 0x003F) */ /* 2. Command Control Register (Offset: 0x0040 - 0x004F) */ /* Turn on 0x40 Command register */ rtl_write_word(rtlpriv, CMDR, (BBRSTN | BB_GLB_RSTN | SCHEDULE_EN | MACRXEN | MACTXEN | DDMA_EN | FW2HW_EN | RXDMA_EN | TXDMA_EN | HCI_RXDMA_EN | HCI_TXDMA_EN)); /* Set TCR TX DMA pre 2 FULL enable bit */ rtl_write_dword(rtlpriv, TCR, rtl_read_dword(rtlpriv, TCR) | TXDMAPRE2FULL); /* Set RCR */ rtl_write_dword(rtlpriv, RCR, rtlpci->receive_config); /* 3. MACID Setting Register (Offset: 0x0050 - 0x007F) */ /* 4. Timing Control Register (Offset: 0x0080 - 0x009F) */ /* Set CCK/OFDM SIFS */ /* CCK SIFS shall always be 10us. */ rtl_write_word(rtlpriv, SIFS_CCK, 0x0a0a); rtl_write_word(rtlpriv, SIFS_OFDM, 0x1010); /* Set AckTimeout */ rtl_write_byte(rtlpriv, ACK_TIMEOUT, 0x40); /* Beacon related */ rtl_write_word(rtlpriv, BCN_INTERVAL, 100); rtl_write_word(rtlpriv, ATIMWND, 2); /* 5. FIFO Control Register (Offset: 0x00A0 - 0x015F) */ /* 5.1 Initialize Number of Reserved Pages in Firmware Queue */ /* Firmware allocate now, associate with FW internal setting.!!! */ /* 5.2 Setting TX/RX page size 0/1/2/3/4=64/128/256/512/1024 */ /* 5.3 Set driver info, we only accept PHY status now. */ /* 5.4 Set RXDMA arbitration to control RXDMA/MAC/FW R/W for RXFIFO */ rtl_write_byte(rtlpriv, RXDMA, rtl_read_byte(rtlpriv, RXDMA) | BIT(6)); /* 6. Adaptive Control Register (Offset: 0x0160 - 0x01CF) */ /* Set RRSR to all legacy rate and HT rate * CCK rate is supported by default. * CCK rate will be filtered out only when associated * AP does not support it. * Only enable ACK rate to OFDM 24M * Disable RRSR for CCK rate in A-Cut */ if (rtlhal->version == VERSION_8192S_ACUT) rtl_write_byte(rtlpriv, RRSR, 0xf0); else if (rtlhal->version == VERSION_8192S_BCUT) rtl_write_byte(rtlpriv, RRSR, 0xff); rtl_write_byte(rtlpriv, RRSR + 1, 0x01); rtl_write_byte(rtlpriv, RRSR + 2, 0x00); /* A-Cut IC do not support CCK rate. We forbid ARFR to */ /* fallback to CCK rate */ for (i = 0; i < 8; i++) { /*Disable RRSR for CCK rate in A-Cut */ if (rtlhal->version == VERSION_8192S_ACUT) rtl_write_dword(rtlpriv, ARFR0 + i * 4, 0x1f0ff0f0); } /* Different rate use different AMPDU size */ /* MCS32/ MCS15_SG use max AMPDU size 15*2=30K */ rtl_write_byte(rtlpriv, AGGLEN_LMT_H, 0x0f); /* MCS0/1/2/3 use max AMPDU size 4*2=8K */ rtl_write_word(rtlpriv, AGGLEN_LMT_L, 0x7442); /* MCS4/5 use max AMPDU size 8*2=16K 6/7 use 10*2=20K */ rtl_write_word(rtlpriv, AGGLEN_LMT_L + 2, 0xddd7); /* MCS8/9 use max AMPDU size 8*2=16K 10/11 use 10*2=20K */ rtl_write_word(rtlpriv, AGGLEN_LMT_L + 4, 0xd772); /* MCS12/13/14/15 use max AMPDU size 15*2=30K */ rtl_write_word(rtlpriv, AGGLEN_LMT_L + 6, 0xfffd); /* Set Data / Response auto rate fallack retry count */ rtl_write_dword(rtlpriv, DARFRC, 0x04010000); rtl_write_dword(rtlpriv, DARFRC + 4, 0x09070605); rtl_write_dword(rtlpriv, RARFRC, 0x04010000); rtl_write_dword(rtlpriv, RARFRC + 4, 0x09070605); /* 7. EDCA Setting Register (Offset: 0x01D0 - 0x01FF) */ /* Set all rate to support SG */ rtl_write_word(rtlpriv, SG_RATE, 0xFFFF); /* 8. WMAC, BA, and CCX related Register (Offset: 0x0200 - 0x023F) */ /* Set NAV protection length */ rtl_write_word(rtlpriv, NAV_PROT_LEN, 0x0080); /* CF-END Threshold */ rtl_write_byte(rtlpriv, CFEND_TH, 0xFF); /* Set AMPDU minimum space */ rtl_write_byte(rtlpriv, AMPDU_MIN_SPACE, 0x07); /* Set TXOP stall control for several queue/HI/BCN/MGT/ */ rtl_write_byte(rtlpriv, TXOP_STALL_CTRL, 0x00); /* 9. Security Control Register (Offset: 0x0240 - 0x025F) */ /* 10. Power Save Control Register (Offset: 0x0260 - 0x02DF) */ /* 11. General Purpose Register (Offset: 0x02E0 - 0x02FF) */ /* 12. Host Interrupt Status Register (Offset: 0x0300 - 0x030F) */ /* 13. Test Mode and Debug Control Register (Offset: 0x0310 - 0x034F) */ /* 14. Set driver info, we only accept PHY status now. */ rtl_write_byte(rtlpriv, RXDRVINFO_SZ, 4); /* 15. For EEPROM R/W Workaround */ /* 16. For EFUSE to share REG_SYS_FUNC_EN with EEPROM!!! */ tmpu2b = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, tmpu2b | BIT(13)); tmpu2b = rtl_read_byte(rtlpriv, REG_SYS_ISO_CTRL); rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL, tmpu2b & (~BIT(8))); /* 17. For EFUSE */ /* We may R/W EFUSE in EEPROM mode */ if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) { u8 tempval; tempval = rtl_read_byte(rtlpriv, REG_SYS_ISO_CTRL + 1); tempval &= 0xFE; rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, tempval); /* Change Program timing */ rtl_write_byte(rtlpriv, REG_EFUSE_CTRL + 3, 0x72); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "EFUSE CONFIG OK\n"); } RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "OK\n"); } static void _rtl92se_hw_configure(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 reg_bw_opmode = 0; u32 reg_rrsr = 0; u8 regtmp = 0; reg_bw_opmode = BW_OPMODE_20MHZ; reg_rrsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG; regtmp = rtl_read_byte(rtlpriv, INIRTSMCS_SEL); reg_rrsr = ((reg_rrsr & 0x000fffff) << 8) | regtmp; rtl_write_dword(rtlpriv, INIRTSMCS_SEL, reg_rrsr); rtl_write_byte(rtlpriv, BW_OPMODE, reg_bw_opmode); /* Set Retry Limit here */ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT, (u8 *)(&rtlpci->shortretry_limit)); rtl_write_byte(rtlpriv, MLT, 0x8f); /* For Min Spacing configuration. */ switch (rtlphy->rf_type) { case RF_1T2R: case RF_1T1R: rtlhal->minspace_cfg = (MAX_MSS_DENSITY_1T << 3); break; case RF_2T2R: case RF_2T2R_GREEN: rtlhal->minspace_cfg = (MAX_MSS_DENSITY_2T << 3); break; } rtl_write_byte(rtlpriv, AMPDU_MIN_SPACE, rtlhal->minspace_cfg); } int rtl92se_hw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 tmp_byte = 0; bool rtstatus = true; u8 tmp_u1b; int err = false; u8 i; int wdcapra_add[] = { EDCAPARA_BE, EDCAPARA_BK, EDCAPARA_VI, EDCAPARA_VO}; u8 secr_value = 0x0; rtlpci->being_init_adapter = true; rtlpriv->intf_ops->disable_aspm(hw); /* 1. MAC Initialize */ /* Before FW download, we have to set some MAC register */ _rtl92se_macconfig_before_fwdownload(hw); rtlhal->version = (enum version_8192s)((rtl_read_dword(rtlpriv, PMC_FSM) >> 16) & 0xF); rtl8192se_gpiobit3_cfg_inputmode(hw); /* 2. download firmware */ rtstatus = rtl92s_download_fw(hw); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now... " "Please copy FW into /lib/firmware/rtlwifi\n"); return 1; } /* After FW download, we have to reset MAC register */ _rtl92se_macconfig_after_fwdownload(hw); /*Retrieve default FW Cmd IO map. */ rtlhal->fwcmd_iomap = rtl_read_word(rtlpriv, LBUS_MON_ADDR); rtlhal->fwcmd_ioparam = rtl_read_dword(rtlpriv, LBUS_ADDR_MASK); /* 3. Initialize MAC/PHY Config by MACPHY_reg.txt */ if (!rtl92s_phy_mac_config(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "MAC Config failed\n"); return rtstatus; } /* Make sure BB/RF write OK. We should prevent enter IPS. radio off. */ /* We must set flag avoid BB/RF config period later!! */ rtl_write_dword(rtlpriv, CMDR, 0x37FC); /* 4. Initialize BB After MAC Config PHY_reg.txt, AGC_Tab.txt */ if (!rtl92s_phy_bb_config(hw)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "BB Config failed\n"); return rtstatus; } /* 5. Initiailze RF RAIO_A.txt RF RAIO_B.txt */ /* Before initalizing RF. We can not use FW to do RF-R/W. */ rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; /* RF Power Save */ #if 0 /* H/W or S/W RF OFF before sleep. */ if (rtlpriv->psc.rfoff_reason > RF_CHANGE_BY_PS) { u32 rfoffreason = rtlpriv->psc.rfoff_reason; rtlpriv->psc.rfoff_reason = RF_CHANGE_BY_INIT; rtlpriv->psc.rfpwr_state = ERFON; /* FIXME: check spinlocks if this block is uncommented */ rtl_ps_set_rf_state(hw, ERFOFF, rfoffreason); } else { /* gpio radio on/off is out of adapter start */ if (rtlpriv->psc.hwradiooff == false) { rtlpriv->psc.rfpwr_state = ERFON; rtlpriv->psc.rfoff_reason = 0; } } #endif /* Before RF-R/W we must execute the IO from Scott's suggestion. */ rtl_write_byte(rtlpriv, AFE_XTAL_CTRL + 1, 0xDB); if (rtlhal->version == VERSION_8192S_ACUT) rtl_write_byte(rtlpriv, SPS1_CTRL + 3, 0x07); else rtl_write_byte(rtlpriv, RF_CTRL, 0x07); if (!rtl92s_phy_rf_config(hw)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "RF Config failed\n"); return rtstatus; } /* After read predefined TXT, we must set BB/MAC/RF * register as our requirement */ rtlphy->rfreg_chnlval[0] = rtl92s_phy_query_rf_reg(hw, (enum radio_path)0, RF_CHNLBW, RFREG_OFFSET_MASK); rtlphy->rfreg_chnlval[1] = rtl92s_phy_query_rf_reg(hw, (enum radio_path)1, RF_CHNLBW, RFREG_OFFSET_MASK); /*---- Set CCK and OFDM Block "ON"----*/ rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1); /*3 Set Hardware(Do nothing now) */ _rtl92se_hw_configure(hw); /* Read EEPROM TX power index and PHY_REG_PG.txt to capture correct */ /* TX power index for different rate set. */ /* Get original hw reg values */ rtl92s_phy_get_hw_reg_originalvalue(hw); /* Write correct tx power index */ rtl92s_phy_set_txpower(hw, rtlphy->current_channel); /* We must set MAC address after firmware download. */ for (i = 0; i < 6; i++) rtl_write_byte(rtlpriv, MACIDR0 + i, rtlefuse->dev_addr[i]); /* EEPROM R/W workaround */ tmp_u1b = rtl_read_byte(rtlpriv, MAC_PINMUX_CFG); rtl_write_byte(rtlpriv, MAC_PINMUX_CFG, tmp_u1b & (~BIT(3))); rtl_write_byte(rtlpriv, 0x4d, 0x0); if (hal_get_firmwareversion(rtlpriv) >= 0x49) { tmp_byte = rtl_read_byte(rtlpriv, FW_RSVD_PG_CRTL) & (~BIT(4)); tmp_byte = tmp_byte | BIT(5); rtl_write_byte(rtlpriv, FW_RSVD_PG_CRTL, tmp_byte); rtl_write_dword(rtlpriv, TXDESC_MSK, 0xFFFFCFFF); } /* We enable high power and RA related mechanism after NIC * initialized. */ rtl92s_phy_set_fw_cmd(hw, FW_CMD_RA_INIT); /* Add to prevent ASPM bug. */ /* Always enable hst and NIC clock request. */ rtl92s_phy_switch_ephy_parameter(hw); /* Security related * 1. Clear all H/W keys. * 2. Enable H/W encryption/decryption. */ rtl_cam_reset_all_entry(hw); secr_value |= SCR_TXENCENABLE; secr_value |= SCR_RXENCENABLE; secr_value |= SCR_NOSKMC; rtl_write_byte(rtlpriv, REG_SECR, secr_value); for (i = 0; i < 4; i++) rtl_write_dword(rtlpriv, wdcapra_add[i], 0x5e4322); if (rtlphy->rf_type == RF_1T2R) { bool mrc2set = true; /* Turn on B-Path */ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_MRC, (u8 *)&mrc2set); } rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_ON); rtl92s_dm_init(hw); rtlpci->being_init_adapter = false; return err; } void rtl92se_set_mac_addr(struct rtl_io *io, const u8 * addr) { } void rtl92se_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u32 reg_rcr = rtlpci->receive_config; if (rtlpriv->psc.rfpwr_state != ERFON) return; if (check_bssid) { reg_rcr |= (RCR_CBSSID); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); } else if (!check_bssid) { reg_rcr &= (~RCR_CBSSID); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); } } static int _rtl92se_set_media_status(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bt_msr = rtl_read_byte(rtlpriv, MSR); u32 temp; bt_msr &= ~MSR_LINK_MASK; switch (type) { case NL80211_IFTYPE_UNSPECIFIED: bt_msr |= (MSR_LINK_NONE << MSR_LINK_SHIFT); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: bt_msr |= (MSR_LINK_ADHOC << MSR_LINK_SHIFT); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: bt_msr |= (MSR_LINK_MANAGED << MSR_LINK_SHIFT); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: bt_msr |= (MSR_LINK_MASTER << MSR_LINK_SHIFT); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to AP!\n"); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Network type %d not supported!\n", type); return 1; break; } rtl_write_byte(rtlpriv, (MSR), bt_msr); temp = rtl_read_dword(rtlpriv, TCR); rtl_write_dword(rtlpriv, TCR, temp & (~BIT(8))); rtl_write_dword(rtlpriv, TCR, temp | BIT(8)); return 0; } /* HW_VAR_MEDIA_STATUS & HW_VAR_CECHK_BSSID */ int rtl92se_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (_rtl92se_set_media_status(hw, type)) return -EOPNOTSUPP; if (rtlpriv->mac80211.link_state == MAC80211_LINKED) { if (type != NL80211_IFTYPE_AP) rtl92se_set_check_bssid(hw, true); } else { rtl92se_set_check_bssid(hw, false); } return 0; } /* don't set REG_EDCA_BE_PARAM here because mac80211 will send pkt when scan */ void rtl92se_set_qos(struct ieee80211_hw *hw, int aci) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl92s_dm_init_edca_turbo(hw); switch (aci) { case AC1_BK: rtl_write_dword(rtlpriv, EDCAPARA_BK, 0xa44f); break; case AC0_BE: /* rtl_write_dword(rtlpriv, EDCAPARA_BE, u4b_ac_param); */ break; case AC2_VI: rtl_write_dword(rtlpriv, EDCAPARA_VI, 0x5e4322); break; case AC3_VO: rtl_write_dword(rtlpriv, EDCAPARA_VO, 0x2f3222); break; default: RT_ASSERT(false, "invalid aci: %d !\n", aci); break; } } void rtl92se_enable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]); /* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */ rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F); } void rtl92se_disable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv; struct rtl_pci *rtlpci; rtlpriv = rtl_priv(hw); /* if firmware not available, no interrupts */ if (!rtlpriv || !rtlpriv->max_fw_size) return; rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtl_write_dword(rtlpriv, INTA_MASK, 0); rtl_write_dword(rtlpriv, INTA_MASK + 4, 0); synchronize_irq(rtlpci->pdev->irq); } static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 waitcnt = 100; bool result = false; u8 tmp; rtl_write_byte(rtlpriv, SYS_CLKR + 1, data); /* Wait the MAC synchronized. */ udelay(400); /* Check if it is set ready. */ tmp = rtl_read_byte(rtlpriv, SYS_CLKR + 1); result = ((tmp & BIT(7)) == (data & BIT(7))); if ((data & (BIT(6) | BIT(7))) == false) { waitcnt = 100; tmp = 0; while (1) { waitcnt--; tmp = rtl_read_byte(rtlpriv, SYS_CLKR + 1); if ((tmp & BIT(6))) break; pr_err("wait for BIT(6) return value %x\n", tmp); if (waitcnt == 0) break; udelay(10); } if (waitcnt == 0) result = false; else result = true; } return result; } static void _rtl92s_phy_set_rfhalt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 u1btmp; if (rtlhal->driver_going2unload) rtl_write_byte(rtlpriv, 0x560, 0x0); /* Power save for BB/RF */ u1btmp = rtl_read_byte(rtlpriv, LDOV12D_CTRL); u1btmp |= BIT(0); rtl_write_byte(rtlpriv, LDOV12D_CTRL, u1btmp); rtl_write_byte(rtlpriv, SPS1_CTRL, 0x0); rtl_write_byte(rtlpriv, TXPAUSE, 0xFF); rtl_write_word(rtlpriv, CMDR, 0x57FC); udelay(100); rtl_write_word(rtlpriv, CMDR, 0x77FC); rtl_write_byte(rtlpriv, PHY_CCA, 0x0); udelay(10); rtl_write_word(rtlpriv, CMDR, 0x37FC); udelay(10); rtl_write_word(rtlpriv, CMDR, 0x77FC); udelay(10); rtl_write_word(rtlpriv, CMDR, 0x57FC); rtl_write_word(rtlpriv, CMDR, 0x0000); if (rtlhal->driver_going2unload) { u1btmp = rtl_read_byte(rtlpriv, (REG_SYS_FUNC_EN + 1)); u1btmp &= ~(BIT(0)); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, u1btmp); } u1btmp = rtl_read_byte(rtlpriv, (SYS_CLKR + 1)); /* Add description. After switch control path. register * after page1 will be invisible. We can not do any IO * for register>0x40. After resume&MACIO reset, we need * to remember previous reg content. */ if (u1btmp & BIT(7)) { u1btmp &= ~(BIT(6) | BIT(7)); if (!_rtl92s_set_sysclk(hw, u1btmp)) { pr_err("Switch ctrl path fail\n"); return; } } /* Power save for MAC */ if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS && !rtlhal->driver_going2unload) { /* enable LED function */ rtl_write_byte(rtlpriv, 0x03, 0xF9); /* SW/HW radio off or halt adapter!! For example S3/S4 */ } else { /* LED function disable. Power range is about 8mA now. */ /* if write 0xF1 disconnet_pci power * ifconfig wlan0 down power are both high 35:70 */ /* if write oxF9 disconnet_pci power * ifconfig wlan0 down power are both low 12:45*/ rtl_write_byte(rtlpriv, 0x03, 0xF9); } rtl_write_byte(rtlpriv, SYS_CLKR + 1, 0x70); rtl_write_byte(rtlpriv, AFE_PLL_CTRL + 1, 0x68); rtl_write_byte(rtlpriv, AFE_PLL_CTRL, 0x00); rtl_write_byte(rtlpriv, LDOA15_CTRL, 0x34); rtl_write_byte(rtlpriv, AFE_XTAL_CTRL, 0x0E); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } static void _rtl92se_gen_refreshledstate(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); if (rtlpci->up_first_time == 1) return; if (rtlpriv->psc.rfoff_reason == RF_CHANGE_BY_IPS) rtl92se_sw_led_on(hw, pLed0); else rtl92se_sw_led_off(hw, pLed0); } static void _rtl92se_power_domain_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u16 tmpu2b; u8 tmpu1b; rtlpriv->psc.pwrdomain_protect = true; tmpu1b = rtl_read_byte(rtlpriv, (SYS_CLKR + 1)); if (tmpu1b & BIT(7)) { tmpu1b &= ~(BIT(6) | BIT(7)); if (!_rtl92s_set_sysclk(hw, tmpu1b)) { rtlpriv->psc.pwrdomain_protect = false; return; } } rtl_write_byte(rtlpriv, AFE_PLL_CTRL, 0x0); rtl_write_byte(rtlpriv, LDOA15_CTRL, 0x34); /* Reset MAC-IO and CPU and Core Digital BIT10/11/15 */ tmpu1b = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); /* If IPS we need to turn LED on. So we not * not disable BIT 3/7 of reg3. */ if (rtlpriv->psc.rfoff_reason & (RF_CHANGE_BY_IPS | RF_CHANGE_BY_HW)) tmpu1b &= 0xFB; else tmpu1b &= 0x73; rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmpu1b); /* wait for BIT 10/11/15 to pull high automatically!! */ mdelay(1); rtl_write_byte(rtlpriv, CMDR, 0); rtl_write_byte(rtlpriv, TCR, 0); /* Data sheet not define 0x562!!! Copy from WMAC!!!!! */ tmpu1b = rtl_read_byte(rtlpriv, 0x562); tmpu1b |= 0x08; rtl_write_byte(rtlpriv, 0x562, tmpu1b); tmpu1b &= ~(BIT(3)); rtl_write_byte(rtlpriv, 0x562, tmpu1b); /* Enable AFE clock source */ tmpu1b = rtl_read_byte(rtlpriv, AFE_XTAL_CTRL); rtl_write_byte(rtlpriv, AFE_XTAL_CTRL, (tmpu1b | 0x01)); /* Delay 1.5ms */ udelay(1500); tmpu1b = rtl_read_byte(rtlpriv, AFE_XTAL_CTRL + 1); rtl_write_byte(rtlpriv, AFE_XTAL_CTRL + 1, (tmpu1b & 0xfb)); /* Enable AFE Macro Block's Bandgap */ tmpu1b = rtl_read_byte(rtlpriv, AFE_MISC); rtl_write_byte(rtlpriv, AFE_MISC, (tmpu1b | BIT(0))); mdelay(1); /* Enable AFE Mbias */ tmpu1b = rtl_read_byte(rtlpriv, AFE_MISC); rtl_write_byte(rtlpriv, AFE_MISC, (tmpu1b | 0x02)); mdelay(1); /* Enable LDOA15 block */ tmpu1b = rtl_read_byte(rtlpriv, LDOA15_CTRL); rtl_write_byte(rtlpriv, LDOA15_CTRL, (tmpu1b | BIT(0))); /* Set Digital Vdd to Retention isolation Path. */ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_ISO_CTRL); rtl_write_word(rtlpriv, REG_SYS_ISO_CTRL, (tmpu2b | BIT(11))); /* For warm reboot NIC disappera bug. */ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(13))); rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, 0x68); /* Enable AFE PLL Macro Block */ tmpu1b = rtl_read_byte(rtlpriv, AFE_PLL_CTRL); rtl_write_byte(rtlpriv, AFE_PLL_CTRL, (tmpu1b | BIT(0) | BIT(4))); /* Enable MAC 80MHZ clock */ tmpu1b = rtl_read_byte(rtlpriv, AFE_PLL_CTRL + 1); rtl_write_byte(rtlpriv, AFE_PLL_CTRL + 1, (tmpu1b | BIT(0))); mdelay(1); /* Release isolation AFE PLL & MD */ rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL, 0xA6); /* Enable MAC clock */ tmpu2b = rtl_read_word(rtlpriv, SYS_CLKR); rtl_write_word(rtlpriv, SYS_CLKR, (tmpu2b | BIT(12) | BIT(11))); /* Enable Core digital and enable IOREG R/W */ tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(11))); /* enable REG_EN */ rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | BIT(11) | BIT(15))); /* Switch the control path. */ tmpu2b = rtl_read_word(rtlpriv, SYS_CLKR); rtl_write_word(rtlpriv, SYS_CLKR, (tmpu2b & (~BIT(2)))); tmpu1b = rtl_read_byte(rtlpriv, (SYS_CLKR + 1)); tmpu1b = ((tmpu1b | BIT(7)) & (~BIT(6))); if (!_rtl92s_set_sysclk(hw, tmpu1b)) { rtlpriv->psc.pwrdomain_protect = false; return; } rtl_write_word(rtlpriv, CMDR, 0x37FC); /* After MACIO reset,we must refresh LED state. */ _rtl92se_gen_refreshledstate(hw); rtlpriv->psc.pwrdomain_protect = false; } void rtl92se_card_disable(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); enum nl80211_iftype opmode; u8 wait = 30; rtlpriv->intf_ops->enable_aspm(hw); if (rtlpci->driver_is_goingto_unload || ppsc->rfoff_reason > RF_CHANGE_BY_PS) rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); /* we should chnge GPIO to input mode * this will drop away current about 25mA*/ rtl8192se_gpiobit3_cfg_inputmode(hw); /* this is very important for ips power save */ while (wait-- >= 10 && rtlpriv->psc.pwrdomain_protect) { if (rtlpriv->psc.pwrdomain_protect) mdelay(20); else break; } mac->link_state = MAC80211_NOLINK; opmode = NL80211_IFTYPE_UNSPECIFIED; _rtl92se_set_media_status(hw, opmode); _rtl92s_phy_set_rfhalt(hw); udelay(100); } void rtl92se_interrupt_recognized(struct ieee80211_hw *hw, u32 *p_inta, u32 *p_intb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); *p_inta = rtl_read_dword(rtlpriv, ISR) & rtlpci->irq_mask[0]; rtl_write_dword(rtlpriv, ISR, *p_inta); *p_intb = rtl_read_dword(rtlpriv, ISR + 4) & rtlpci->irq_mask[1]; rtl_write_dword(rtlpriv, ISR + 4, *p_intb); } void rtl92se_set_beacon_related_registers(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcntime_cfg = 0; u16 bcn_cw = 6, bcn_ifs = 0xf; u16 atim_window = 2; /* ATIM Window (in unit of TU). */ rtl_write_word(rtlpriv, ATIMWND, atim_window); /* Beacon interval (in unit of TU). */ rtl_write_word(rtlpriv, BCN_INTERVAL, mac->beacon_interval); /* DrvErlyInt (in unit of TU). (Time to send * interrupt to notify driver to change * beacon content) */ rtl_write_word(rtlpriv, BCN_DRV_EARLY_INT, 10 << 4); /* BcnDMATIM(in unit of us). Indicates the * time before TBTT to perform beacon queue DMA */ rtl_write_word(rtlpriv, BCN_DMATIME, 256); /* Force beacon frame transmission even * after receiving beacon frame from * other ad hoc STA */ rtl_write_byte(rtlpriv, BCN_ERR_THRESH, 100); /* Beacon Time Configuration */ if (mac->opmode == NL80211_IFTYPE_ADHOC) bcntime_cfg |= (bcn_cw << BCN_TCFG_CW_SHIFT); /* TODO: bcn_ifs may required to be changed on ASIC */ bcntime_cfg |= bcn_ifs << BCN_TCFG_IFS; /*for beacon changed */ rtl92s_phy_set_beacon_hwreg(hw, mac->beacon_interval); } void rtl92se_set_beacon_interval(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcn_interval = mac->beacon_interval; /* Beacon interval (in unit of TU). */ rtl_write_word(rtlpriv, BCN_INTERVAL, bcn_interval); /* 2008.10.24 added by tynli for beacon changed. */ rtl92s_phy_set_beacon_hwreg(hw, bcn_interval); } void rtl92se_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, "add_msr:%x, rm_msr:%x\n", add_msr, rm_msr); if (add_msr) rtlpci->irq_mask[0] |= add_msr; if (rm_msr) rtlpci->irq_mask[0] &= (~rm_msr); rtl92se_disable_interrupt(hw); rtl92se_enable_interrupt(hw); } static void _rtl8192se_get_IC_Inferiority(struct ieee80211_hw *hw) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 efuse_id; rtlhal->ic_class = IC_INFERIORITY_A; /* Only retrieving while using EFUSE. */ if ((rtlefuse->epromtype == EEPROM_BOOT_EFUSE) && !rtlefuse->autoload_failflag) { efuse_id = efuse_read_1byte(hw, EFUSE_IC_ID_OFFSET); if (efuse_id == 0xfe) rtlhal->ic_class = IC_INFERIORITY_B; } } static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); u16 i, usvalue; u16 eeprom_id; u8 tempval; u8 hwinfo[HWSET_MAX_SIZE_92S]; u8 rf_path, index; if (rtlefuse->epromtype == EEPROM_93C46) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "RTL819X Not boot from eeprom, check it !!\n"); } else if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) { rtl_efuse_shadow_map_update(hw); memcpy((void *)hwinfo, (void *) &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], HWSET_MAX_SIZE_92S); } RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, "MAP", hwinfo, HWSET_MAX_SIZE_92S); eeprom_id = *((u16 *)&hwinfo[0]); if (eeprom_id != RTL8190_EEPROM_ID) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "EEPROM ID(%#x) is invalid!!\n", eeprom_id); rtlefuse->autoload_failflag = true; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); rtlefuse->autoload_failflag = false; } if (rtlefuse->autoload_failflag) return; _rtl8192se_get_IC_Inferiority(hw); /* Read IC Version && Channel Plan */ /* VID, DID SE 0xA-D */ rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID]; rtlefuse->eeprom_did = *(u16 *)&hwinfo[EEPROM_DID]; rtlefuse->eeprom_svid = *(u16 *)&hwinfo[EEPROM_SVID]; rtlefuse->eeprom_smid = *(u16 *)&hwinfo[EEPROM_SMID]; rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROMId = 0x%4x\n", eeprom_id); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid); for (i = 0; i < 6; i += 2) { usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i]; *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue; } for (i = 0; i < 6; i++) rtl_write_byte(rtlpriv, MACIDR0 + i, rtlefuse->dev_addr[i]); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "%pM\n", rtlefuse->dev_addr); /* Get Tx Power Level by Channel */ /* Read Tx power of Channel 1 ~ 14 from EEPROM. */ /* 92S suupport RF A & B */ for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 3; i++) { /* Read CCK RF A & B Tx power */ rtlefuse->eeprom_chnlarea_txpwr_cck[rf_path][i] = hwinfo[EEPROM_TXPOWERBASE + rf_path * 3 + i]; /* Read OFDM RF A & B Tx power for 1T */ rtlefuse->eeprom_chnlarea_txpwr_ht40_1s[rf_path][i] = hwinfo[EEPROM_TXPOWERBASE + 6 + rf_path * 3 + i]; /* Read OFDM RF A & B Tx power for 2T */ rtlefuse->eeprom_chnlarea_txpwr_ht40_2sdiif[rf_path][i] = hwinfo[EEPROM_TXPOWERBASE + 12 + rf_path * 3 + i]; } } for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM CCK Area(%d) = 0x%x\n", rf_path, i, rtlefuse->eeprom_chnlarea_txpwr_cck [rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM HT40 1S Area(%d) = 0x%x\n", rf_path, i, rtlefuse->eeprom_chnlarea_txpwr_ht40_1s [rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM HT40 2S Diff Area(%d) = 0x%x\n", rf_path, i, rtlefuse->eeprom_chnlarea_txpwr_ht40_2sdiif [rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) { /* Assign dedicated channel tx power */ for (i = 0; i < 14; i++) { /* channel 1~3 use the same Tx Power Level. */ if (i < 3) index = 0; /* Channel 4-8 */ else if (i < 8) index = 1; /* Channel 9-14 */ else index = 2; /* Record A & B CCK /OFDM - 1T/2T Channel area * tx power */ rtlefuse->txpwrlevel_cck[rf_path][i] = rtlefuse->eeprom_chnlarea_txpwr_cck [rf_path][index]; rtlefuse->txpwrlevel_ht40_1s[rf_path][i] = rtlefuse->eeprom_chnlarea_txpwr_ht40_1s [rf_path][index]; rtlefuse->txpwrlevel_ht40_2s[rf_path][i] = rtlefuse->eeprom_chnlarea_txpwr_ht40_2sdiif [rf_path][index]; } for (i = 0; i < 14; i++) { RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = [0x%x / 0x%x / 0x%x]\n", rf_path, i, rtlefuse->txpwrlevel_cck[rf_path][i], rtlefuse->txpwrlevel_ht40_1s[rf_path][i], rtlefuse->txpwrlevel_ht40_2s[rf_path][i]); } } for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 3; i++) { /* Read Power diff limit. */ rtlefuse->eeprom_pwrgroup[rf_path][i] = hwinfo[EEPROM_TXPWRGROUP + rf_path * 3 + i]; } } for (rf_path = 0; rf_path < 2; rf_path++) { /* Fill Pwr group */ for (i = 0; i < 14; i++) { /* Chanel 1-3 */ if (i < 3) index = 0; /* Channel 4-8 */ else if (i < 8) index = 1; /* Channel 9-13 */ else index = 2; rtlefuse->pwrgroup_ht20[rf_path][i] = (rtlefuse->eeprom_pwrgroup[rf_path][index] & 0xf); rtlefuse->pwrgroup_ht40[rf_path][i] = ((rtlefuse->eeprom_pwrgroup[rf_path][index] & 0xf0) >> 4); RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-%d pwrgroup_ht20[%d] = 0x%x\n", rf_path, i, rtlefuse->pwrgroup_ht20[rf_path][i]); RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-%d pwrgroup_ht40[%d] = 0x%x\n", rf_path, i, rtlefuse->pwrgroup_ht40[rf_path][i]); } } for (i = 0; i < 14; i++) { /* Read tx power difference between HT OFDM 20/40 MHZ */ /* channel 1-3 */ if (i < 3) index = 0; /* Channel 4-8 */ else if (i < 8) index = 1; /* Channel 9-14 */ else index = 2; tempval = hwinfo[EEPROM_TX_PWR_HT20_DIFF + index] & 0xff; rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); /* Read OFDM<->HT tx power diff */ /* Channel 1-3 */ if (i < 3) index = 0; /* Channel 4-8 */ else if (i < 8) index = 0x11; /* Channel 9-14 */ else index = 1; tempval = hwinfo[EEPROM_TX_PWR_OFDM_DIFF + index] & 0xff; rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); tempval = hwinfo[TX_PWR_SAFETY_CHK]; rtlefuse->txpwr_safetyflag = (tempval & 0x01); } rtlefuse->eeprom_regulatory = 0; if (rtlefuse->eeprom_version >= 2) { /* BIT(0)~2 */ if (rtlefuse->eeprom_version >= 4) rtlefuse->eeprom_regulatory = (hwinfo[EEPROM_REGULATORY] & 0x7); else /* BIT(0) */ rtlefuse->eeprom_regulatory = (hwinfo[EEPROM_REGULATORY] & 0x1); } RTPRINT(rtlpriv, FINIT, INIT_TxPower, "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-B Legacy to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]); RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TxPwrSafetyFlag = %d\n", rtlefuse->txpwr_safetyflag); /* Read RF-indication and Tx Power gain * index diff of legacy to HT OFDM rate. */ tempval = hwinfo[EEPROM_RFIND_POWERDIFF] & 0xff; rtlefuse->eeprom_txpowerdiff = tempval; rtlefuse->legacy_httxpowerdiff = rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][0]; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TxPowerDiff = %#x\n", rtlefuse->eeprom_txpowerdiff); /* Get TSSI value for each path. */ usvalue = *(u16 *)&hwinfo[EEPROM_TSSI_A]; rtlefuse->eeprom_tssi[RF90_PATH_A] = (u8)((usvalue & 0xff00) >> 8); usvalue = hwinfo[EEPROM_TSSI_B]; rtlefuse->eeprom_tssi[RF90_PATH_B] = (u8)(usvalue & 0xff); RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TSSI_A = 0x%x, TSSI_B = 0x%x\n", rtlefuse->eeprom_tssi[RF90_PATH_A], rtlefuse->eeprom_tssi[RF90_PATH_B]); /* Read antenna tx power offset of B/C/D to A from EEPROM */ /* and read ThermalMeter from EEPROM */ tempval = hwinfo[EEPROM_THERMALMETER]; rtlefuse->eeprom_thermalmeter = tempval; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter); /* ThermalMeter, BIT(0)~3 for RFIC1, BIT(4)~7 for RFIC2 */ rtlefuse->thermalmeter[0] = (rtlefuse->eeprom_thermalmeter & 0x1f); rtlefuse->tssi_13dbm = rtlefuse->eeprom_thermalmeter * 100; /* Read CrystalCap from EEPROM */ tempval = hwinfo[EEPROM_CRYSTALCAP] >> 4; rtlefuse->eeprom_crystalcap = tempval; /* CrystalCap, BIT(12)~15 */ rtlefuse->crystalcap = rtlefuse->eeprom_crystalcap; /* Read IC Version && Channel Plan */ /* Version ID, Channel plan */ rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN]; rtlefuse->txpwr_fromeprom = true; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "EEPROM ChannelPlan = 0x%4x\n", rtlefuse->eeprom_channelplan); /* Read Customer ID or Board Type!!! */ tempval = hwinfo[EEPROM_BOARDTYPE]; /* Change RF type definition */ if (tempval == 0) rtlphy->rf_type = RF_2T2R; else if (tempval == 1) rtlphy->rf_type = RF_1T2R; else if (tempval == 2) rtlphy->rf_type = RF_1T2R; else if (tempval == 3) rtlphy->rf_type = RF_1T1R; /* 1T2R but 1SS (1x1 receive combining) */ rtlefuse->b1x1_recvcombine = false; if (rtlphy->rf_type == RF_1T2R) { tempval = rtl_read_byte(rtlpriv, 0x07); if (!(tempval & BIT(0))) { rtlefuse->b1x1_recvcombine = true; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "RF_TYPE=1T2R but only 1SS\n"); } } rtlefuse->b1ss_support = rtlefuse->b1x1_recvcombine; rtlefuse->eeprom_oemid = *&hwinfo[EEPROM_CUSTOMID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x", rtlefuse->eeprom_oemid); /* set channel paln to world wide 13 */ rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13; } void rtl92se_read_eeprom_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 tmp_u1b = 0; tmp_u1b = rtl_read_byte(rtlpriv, EPROM_CMD); if (tmp_u1b & BIT(4)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EEPROM\n"); rtlefuse->epromtype = EEPROM_93C46; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EFUSE\n"); rtlefuse->epromtype = EEPROM_BOOT_EFUSE; } if (tmp_u1b & BIT(5)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); rtlefuse->autoload_failflag = false; _rtl92se_read_adapter_info(hw); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Autoload ERR!!\n"); rtlefuse->autoload_failflag = true; } } static void rtl92se_update_hal_rate_table(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u32 ratr_value; u8 ratr_index = 0; u8 nmode = mac->ht_enable; u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate = 0; u32 tmp_ratr_value = 0; u8 curtxbw_40mhz = mac->bw_40; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = mac->mode; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_value = sta->supp_rates[1] << 4; else ratr_value = sta->supp_rates[0]; ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 | sta->ht_cap.mcs.rx_mask[0] << 12); switch (wirelessmode) { case WIRELESS_MODE_B: ratr_value &= 0x0000000D; break; case WIRELESS_MODE_G: ratr_value &= 0x00000FF5; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: nmode = 1; if (mimo_ps == IEEE80211_SMPS_STATIC) { ratr_value &= 0x0007F005; } else { u32 ratr_mask; if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_1T1R) { if (curtxbw_40mhz) ratr_mask = 0x000ff015; else ratr_mask = 0x000ff005; } else { if (curtxbw_40mhz) ratr_mask = 0x0f0ff015; else ratr_mask = 0x0f0ff005; } ratr_value &= ratr_mask; } break; default: if (rtlphy->rf_type == RF_1T2R) ratr_value &= 0x000ff0ff; else ratr_value &= 0x0f0ff0ff; break; } if (rtlpriv->rtlhal.version >= VERSION_8192S_BCUT) ratr_value &= 0x0FFFFFFF; else if (rtlpriv->rtlhal.version == VERSION_8192S_ACUT) ratr_value &= 0x0FFFFFF0; if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz))) { ratr_value |= 0x10000000; tmp_ratr_value = (ratr_value >> 12); for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) { if ((1 << shortgi_rate) & tmp_ratr_value) break; } shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) | (shortgi_rate << 4) | (shortgi_rate); rtl_write_byte(rtlpriv, SG_RATE, shortgi_rate); } rtl_write_dword(rtlpriv, ARFR0 + ratr_index * 4, ratr_value); if (ratr_value & 0xfffff000) rtl92s_phy_set_fw_cmd(hw, FW_CMD_RA_REFRESH_N); else rtl92s_phy_set_fw_cmd(hw, FW_CMD_RA_REFRESH_BG); RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "%x\n", rtl_read_dword(rtlpriv, ARFR0)); } static void rtl92se_update_hal_rate_mask(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index = 0; u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = 0; bool shortgi = false; u32 ratr_value = 0; u8 shortgi_rate = 0; u32 mask = 0; u32 band = 0; bool bmulticast = false; u8 macid = 0; u8 mimo_ps = IEEE80211_SMPS_OFF; sta_entry = (struct rtl_sta_info *) sta->drv_priv; wirelessmode = sta_entry->wireless_mode; if (mac->opmode == NL80211_IFTYPE_STATION) curtxbw_40mhz = mac->bw_40; else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) macid = sta->aid + 1; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_bitmap = sta->supp_rates[1] << 4; else ratr_bitmap = sta->supp_rates[0]; ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 | sta->ht_cap.mcs.rx_mask[0] << 12); switch (wirelessmode) { case WIRELESS_MODE_B: band |= WIRELESS_11B; ratr_index = RATR_INX_WIRELESS_B; if (ratr_bitmap & 0x0000000c) ratr_bitmap &= 0x0000000d; else ratr_bitmap &= 0x0000000f; break; case WIRELESS_MODE_G: band |= (WIRELESS_11G | WIRELESS_11B); ratr_index = RATR_INX_WIRELESS_GB; if (rssi_level == 1) ratr_bitmap &= 0x00000f00; else if (rssi_level == 2) ratr_bitmap &= 0x00000ff0; else ratr_bitmap &= 0x00000ff5; break; case WIRELESS_MODE_A: band |= WIRELESS_11A; ratr_index = RATR_INX_WIRELESS_A; ratr_bitmap &= 0x00000ff0; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: band |= (WIRELESS_11N | WIRELESS_11G | WIRELESS_11B); ratr_index = RATR_INX_WIRELESS_NGB; if (mimo_ps == IEEE80211_SMPS_STATIC) { if (rssi_level == 1) ratr_bitmap &= 0x00070000; else if (rssi_level == 2) ratr_bitmap &= 0x0007f000; else ratr_bitmap &= 0x0007f005; } else { if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_1T1R) { if (rssi_level == 1) { ratr_bitmap &= 0x000f0000; } else if (rssi_level == 3) { ratr_bitmap &= 0x000fc000; } else if (rssi_level == 5) { ratr_bitmap &= 0x000ff000; } else { if (curtxbw_40mhz) ratr_bitmap &= 0x000ff015; else ratr_bitmap &= 0x000ff005; } } else { if (rssi_level == 1) { ratr_bitmap &= 0x0f8f0000; } else if (rssi_level == 3) { ratr_bitmap &= 0x0f8fc000; } else if (rssi_level == 5) { ratr_bitmap &= 0x0f8ff000; } else { if (curtxbw_40mhz) ratr_bitmap &= 0x0f8ff015; else ratr_bitmap &= 0x0f8ff005; } } } if ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz)) { if (macid == 0) shortgi = true; else if (macid == 1) shortgi = false; } break; default: band |= (WIRELESS_11N | WIRELESS_11G | WIRELESS_11B); ratr_index = RATR_INX_WIRELESS_NGB; if (rtlphy->rf_type == RF_1T2R) ratr_bitmap &= 0x000ff0ff; else ratr_bitmap &= 0x0f8ff0ff; break; } if (rtlpriv->rtlhal.version >= VERSION_8192S_BCUT) ratr_bitmap &= 0x0FFFFFFF; else if (rtlpriv->rtlhal.version == VERSION_8192S_ACUT) ratr_bitmap &= 0x0FFFFFF0; if (shortgi) { ratr_bitmap |= 0x10000000; /* Get MAX MCS available. */ ratr_value = (ratr_bitmap >> 12); for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) { if ((1 << shortgi_rate) & ratr_value) break; } shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) | (shortgi_rate << 4) | (shortgi_rate); rtl_write_byte(rtlpriv, SG_RATE, shortgi_rate); } mask |= (bmulticast ? 1 : 0) << 9 | (macid & 0x1f) << 4 | (band & 0xf); RT_TRACE(rtlpriv, COMP_RATR, DBG_TRACE, "mask = %x, bitmap = %x\n", mask, ratr_bitmap); rtl_write_dword(rtlpriv, 0x2c4, ratr_bitmap); rtl_write_dword(rtlpriv, WFM5, (FW_RA_UPDATE_MASK | (mask << 8))); if (macid != 0) sta_entry->ratr_index = ratr_index; } void rtl92se_update_hal_rate_tbl(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->dm.useramask) rtl92se_update_hal_rate_mask(hw, sta, rssi_level); else rtl92se_update_hal_rate_table(hw, sta); } void rtl92se_update_channel_access_setting(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); sifs_timer = 0x0e0e; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer); } /* this ifunction is for RFKILL, it's different with windows, * because UI will disable wireless when GPIO Radio Off. * And here we not check or Disable/Enable ASPM like windows*/ bool rtl92se_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); enum rf_pwrstate rfpwr_toset /*, cur_rfstate */; unsigned long flag = 0; bool actuallyset = false; bool turnonbypowerdomain = false; /* just 8191se can check gpio before firstup, 92c/92d have fixed it */ if ((rtlpci->up_first_time == 1) || (rtlpci->being_init_adapter)) return false; if (ppsc->swrf_processing) return false; spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); if (ppsc->rfchange_inprogress) { spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); return false; } else { ppsc->rfchange_inprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } /* cur_rfstate = ppsc->rfpwr_state;*/ /* because after _rtl92s_phy_set_rfhalt, all power * closed, so we must open some power for GPIO check, * or we will always check GPIO RFOFF here, * And we should close power after GPIO check */ if (RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) { _rtl92se_power_domain_init(hw); turnonbypowerdomain = true; } rfpwr_toset = _rtl92se_rf_onoff_detect(hw); if ((ppsc->hwradiooff) && (rfpwr_toset == ERFON)) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "RFKILL-HW Radio ON, RF ON\n"); rfpwr_toset = ERFON; ppsc->hwradiooff = false; actuallyset = true; } else if ((!ppsc->hwradiooff) && (rfpwr_toset == ERFOFF)) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "RFKILL-HW Radio OFF, RF OFF\n"); rfpwr_toset = ERFOFF; ppsc->hwradiooff = true; actuallyset = true; } if (actuallyset) { spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); /* this not include ifconfig wlan0 down case */ /* } else if (rfpwr_toset == ERFOFF || cur_rfstate == ERFOFF) { */ } else { /* because power_domain_init may be happen when * _rtl92s_phy_set_rfhalt, this will open some powers * and cause current increasing about 40 mA for ips, * rfoff and ifconfig down, so we set * _rtl92s_phy_set_rfhalt again here */ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC && turnonbypowerdomain) { _rtl92s_phy_set_rfhalt(hw); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } *valid = 1; return !ppsc->hwradiooff; } /* Is_wepkey just used for WEP used as group & pairwise key * if pairwise is AES ang group is WEP Is_wepkey == false.*/ void rtl92se_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 *macaddr = p_macaddr; u32 entry_id = 0; bool is_pairwise = false; static u8 cam_const_addr[4][6] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} }; static u8 cam_const_broad[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (clear_all) { u8 idx = 0; u8 cam_offset = 0; u8 clear_number = 5; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n"); for (idx = 0; idx < clear_number; idx++) { rtl_cam_mark_invalid(hw, cam_offset + idx); rtl_cam_empty_entry(hw, cam_offset + idx); if (idx < 5) { memset(rtlpriv->sec.key_buf[idx], 0, MAX_KEY_LEN); rtlpriv->sec.key_len[idx] = 0; } } } else { switch (enc_algo) { case WEP40_ENCRYPTION: enc_algo = CAM_WEP40; break; case WEP104_ENCRYPTION: enc_algo = CAM_WEP104; break; case TKIP_ENCRYPTION: enc_algo = CAM_TKIP; break; case AESCCMP_ENCRYPTION: enc_algo = CAM_AES; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); enc_algo = CAM_TKIP; break; } if (is_wepkey || rtlpriv->sec.use_defaultkey) { macaddr = cam_const_addr[key_index]; entry_id = key_index; } else { if (is_group) { macaddr = cam_const_broad; entry_id = key_index; } else { if (mac->opmode == NL80211_IFTYPE_AP) { entry_id = rtl_cam_get_free_entry(hw, p_macaddr); if (entry_id >= TOTAL_CAM_ENTRY) { RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "Can not find free hw security cam entry\n"); return; } } else { entry_id = CAM_PAIRWISE_KEY_POSITION; } key_index = PAIRWISE_KEYIDX; is_pairwise = true; } } if (rtlpriv->sec.key_len[key_index] == 0) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "delete one entry, entry_id is %d\n", entry_id); if (mac->opmode == NL80211_IFTYPE_AP) rtl_cam_del_entry(hw, p_macaddr); rtl_cam_delete_one_entry(hw, p_macaddr, entry_id); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY length is %d\n", rtlpriv->sec.key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY is %x %x\n", rtlpriv->sec.key_buf[0][0], rtlpriv->sec.key_buf[0][1]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "add one entry\n"); if (is_pairwise) { RT_PRINT_DATA(rtlpriv, COMP_SEC, DBG_LOUD, "Pairwise Key content", rtlpriv->sec.pairwise_key, rtlpriv->sec. key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set Pairwise key\n"); rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf[key_index]); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set group key\n"); if (mac->opmode == NL80211_IFTYPE_ADHOC) { rtl_cam_add_one_entry(hw, rtlefuse->dev_addr, PAIRWISE_KEYIDX, CAM_PAIRWISE_KEY_POSITION, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf[entry_id]); } rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf[entry_id]); } } } } void rtl92se_suspend(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtlpci->up_first_time = true; } void rtl92se_resume(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u32 val; pci_read_config_dword(rtlpci->pdev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(rtlpci->pdev, 0x40, val & 0xffff00ff); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/fw.h0000644000175000017500000002204112026211315024402 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __REALTEK_FIRMWARE92S_H__ #define __REALTEK_FIRMWARE92S_H__ #define RTL8190_MAX_FIRMWARE_CODE_SIZE 64000 #define RTL8190_MAX_RAW_FIRMWARE_CODE_SIZE 90000 #define RTL8190_CPU_START_OFFSET 0x80 /* Firmware Local buffer size. 64k */ #define MAX_FIRMWARE_CODE_SIZE 0xFF00 #define RT_8192S_FIRMWARE_HDR_SIZE 80 #define RT_8192S_FIRMWARE_HDR_EXCLUDE_PRI_SIZE 32 /* support till 64 bit bus width OS */ #define MAX_DEV_ADDR_SIZE 8 #define MAX_FIRMWARE_INFORMATION_SIZE 32 #define MAX_802_11_HEADER_LENGTH (40 + \ MAX_FIRMWARE_INFORMATION_SIZE) #define ENCRYPTION_MAX_OVERHEAD 128 #define MAX_FRAGMENT_COUNT 8 #define MAX_TRANSMIT_BUFFER_SIZE (1600 + \ (MAX_802_11_HEADER_LENGTH + \ ENCRYPTION_MAX_OVERHEAD) *\ MAX_FRAGMENT_COUNT) #define H2C_TX_CMD_HDR_LEN 8 /* The following DM control code are for Reg0x364, */ #define FW_DIG_ENABLE_CTL BIT(0) #define FW_HIGH_PWR_ENABLE_CTL BIT(1) #define FW_SS_CTL BIT(2) #define FW_RA_INIT_CTL BIT(3) #define FW_RA_BG_CTL BIT(4) #define FW_RA_N_CTL BIT(5) #define FW_PWR_TRK_CTL BIT(6) #define FW_IQK_CTL BIT(7) #define FW_FA_CTL BIT(8) #define FW_DRIVER_CTRL_DM_CTL BIT(9) #define FW_PAPE_CTL_BY_SW_HW BIT(10) #define FW_DISABLE_ALL_DM 0 #define FW_PWR_TRK_PARAM_CLR 0x0000ffff #define FW_RA_PARAM_CLR 0xffff0000 enum desc_packet_type { DESC_PACKET_TYPE_INIT = 0, DESC_PACKET_TYPE_NORMAL = 1, }; /* 8-bytes alignment required */ struct fw_priv { /* --- long word 0 ---- */ /* 0x12: CE product, 0x92: IT product */ u8 signature_0; /* 0x87: CE product, 0x81: IT product */ u8 signature_1; /* 0x81: PCI-AP, 01:PCIe, 02: 92S-U, * 0x82: USB-AP, 0x12: 72S-U, 03:SDIO */ u8 hci_sel; /* the same value as reigster value */ u8 chip_version; /* customer ID low byte */ u8 customer_id_0; /* customer ID high byte */ u8 customer_id_1; /* 0x11: 1T1R, 0x12: 1T2R, * 0x92: 1T2R turbo, 0x22: 2T2R */ u8 rf_config; /* 4: 4EP, 6: 6EP, 11: 11EP */ u8 usb_ep_num; /* --- long word 1 ---- */ /* regulatory class bit map 0 */ u8 regulatory_class_0; /* regulatory class bit map 1 */ u8 regulatory_class_1; /* regulatory class bit map 2 */ u8 regulatory_class_2; /* regulatory class bit map 3 */ u8 regulatory_class_3; /* 0:SWSI, 1:HWSI, 2:HWPI */ u8 rfintfs; u8 def_nettype; u8 rsvd010; u8 rsvd011; /* --- long word 2 ---- */ /* 0x00: normal, 0x03: MACLBK, 0x01: PHYLBK */ u8 lbk_mode; /* 1: for MP use, 0: for normal * driver (to be discussed) */ u8 mp_mode; u8 rsvd020; u8 rsvd021; u8 rsvd022; u8 rsvd023; u8 rsvd024; u8 rsvd025; /* --- long word 3 ---- */ /* QoS enable */ u8 qos_en; /* 40MHz BW enable */ /* 4181 convert AMSDU to AMPDU, 0: disable */ u8 bw_40mhz_en; u8 amsdu2ampdu_en; /* 11n AMPDU enable */ u8 ampdu_en; /* FW offloads, 0: driver handles */ u8 rate_control_offload; /* FW offloads, 0: driver handles */ u8 aggregation_offload; u8 rsvd030; u8 rsvd031; /* --- long word 4 ---- */ /* 1. FW offloads, 0: driver handles */ u8 beacon_offload; /* 2. FW offloads, 0: driver handles */ u8 mlme_offload; /* 3. FW offloads, 0: driver handles */ u8 hwpc_offload; /* 4. FW offloads, 0: driver handles */ u8 tcp_checksum_offload; /* 5. FW offloads, 0: driver handles */ u8 tcp_offload; /* 6. FW offloads, 0: driver handles */ u8 ps_control_offload; /* 7. FW offloads, 0: driver handles */ u8 wwlan_offload; u8 rsvd040; /* --- long word 5 ---- */ /* tcp tx packet length low byte */ u8 tcp_tx_frame_len_L; /* tcp tx packet length high byte */ u8 tcp_tx_frame_len_H; /* tcp rx packet length low byte */ u8 tcp_rx_frame_len_L; /* tcp rx packet length high byte */ u8 tcp_rx_frame_len_H; u8 rsvd050; u8 rsvd051; u8 rsvd052; u8 rsvd053; }; /* 8-byte alinment required */ struct fw_hdr { /* --- LONG WORD 0 ---- */ u16 signature; /* 0x8000 ~ 0x8FFF for FPGA version, * 0x0000 ~ 0x7FFF for ASIC version, */ u16 version; /* define the size of boot loader */ u32 dmem_size; /* --- LONG WORD 1 ---- */ /* define the size of FW in IMEM */ u32 img_imem_size; /* define the size of FW in SRAM */ u32 img_sram_size; /* --- LONG WORD 2 ---- */ /* define the size of DMEM variable */ u32 fw_priv_size; u32 rsvd0; /* --- LONG WORD 3 ---- */ u32 rsvd1; u32 rsvd2; struct fw_priv fwpriv; } ; enum fw_status { FW_STATUS_INIT = 0, FW_STATUS_LOAD_IMEM = 1, FW_STATUS_LOAD_EMEM = 2, FW_STATUS_LOAD_DMEM = 3, FW_STATUS_READY = 4, }; struct rt_firmware { struct fw_hdr *pfwheader; enum fw_status fwstatus; u16 firmwareversion; u8 fw_imem[RTL8190_MAX_FIRMWARE_CODE_SIZE]; u8 fw_emem[RTL8190_MAX_FIRMWARE_CODE_SIZE]; u32 fw_imem_len; u32 fw_emem_len; u8 sz_fw_tmpbuffer[RTL8190_MAX_RAW_FIRMWARE_CODE_SIZE]; u32 sz_fw_tmpbufferlen; u16 cmdpacket_fragthresold; }; struct h2c_set_pwrmode_parm { u8 mode; u8 flag_low_traffic_en; u8 flag_lpnav_en; u8 flag_rf_low_snr_en; /* 1: dps, 0: 32k */ u8 flag_dps_en; u8 bcn_rx_en; u8 bcn_pass_cnt; /* beacon TO (ms). ¡§=0¡¨ no limit. */ u8 bcn_to; u16 bcn_itv; /* only for VOIP mode. */ u8 app_itv; u8 awake_bcn_itvl; u8 smart_ps; /* unit: 100 ms */ u8 bcn_pass_period; }; struct h2c_joinbss_rpt_parm { u8 opmode; u8 ps_qos_info; u8 bssid[6]; u16 bcnitv; u16 aid; } ; struct h2c_wpa_ptk { /* EAPOL-Key Key Confirmation Key (KCK) */ u8 kck[16]; /* EAPOL-Key Key Encryption Key (KEK) */ u8 kek[16]; /* Temporal Key 1 (TK1) */ u8 tk1[16]; union { /* Temporal Key 2 (TK2) */ u8 tk2[16]; struct { u8 tx_mic_key[8]; u8 rx_mic_key[8]; } athu; } u; }; struct h2c_wpa_two_way_parm { /* algorithm TKIP or AES */ u8 pairwise_en_alg; u8 group_en_alg; struct h2c_wpa_ptk wpa_ptk_value; } ; enum h2c_cmd { FW_H2C_SETPWRMODE = 0, FW_H2C_JOINBSSRPT = 1, FW_H2C_WOWLAN_UPDATE_GTK = 2, FW_H2C_WOWLAN_UPDATE_IV = 3, FW_H2C_WOWLAN_OFFLOAD = 4, }; enum fw_h2c_cmd { H2C_READ_MACREG_CMD, /*0*/ H2C_WRITE_MACREG_CMD, H2C_READBB_CMD, H2C_WRITEBB_CMD, H2C_READRF_CMD, H2C_WRITERF_CMD, /*5*/ H2C_READ_EEPROM_CMD, H2C_WRITE_EEPROM_CMD, H2C_READ_EFUSE_CMD, H2C_WRITE_EFUSE_CMD, H2C_READ_CAM_CMD, /*10*/ H2C_WRITE_CAM_CMD, H2C_SETBCNITV_CMD, H2C_SETMBIDCFG_CMD, H2C_JOINBSS_CMD, H2C_DISCONNECT_CMD, /*15*/ H2C_CREATEBSS_CMD, H2C_SETOPMode_CMD, H2C_SITESURVEY_CMD, H2C_SETAUTH_CMD, H2C_SETKEY_CMD, /*20*/ H2C_SETSTAKEY_CMD, H2C_SETASSOCSTA_CMD, H2C_DELASSOCSTA_CMD, H2C_SETSTAPWRSTATE_CMD, H2C_SETBASICRATE_CMD, /*25*/ H2C_GETBASICRATE_CMD, H2C_SETDATARATE_CMD, H2C_GETDATARATE_CMD, H2C_SETPHYINFO_CMD, H2C_GETPHYINFO_CMD, /*30*/ H2C_SETPHY_CMD, H2C_GETPHY_CMD, H2C_READRSSI_CMD, H2C_READGAIN_CMD, H2C_SETATIM_CMD, /*35*/ H2C_SETPWRMODE_CMD, H2C_JOINBSSRPT_CMD, H2C_SETRATABLE_CMD, H2C_GETRATABLE_CMD, H2C_GETCCXREPORT_CMD, /*40*/ H2C_GETDTMREPORT_CMD, H2C_GETTXRATESTATICS_CMD, H2C_SETUSBSUSPEND_CMD, H2C_SETH2CLBK_CMD, H2C_TMP1, /*45*/ H2C_WOWLAN_UPDATE_GTK_CMD, H2C_WOWLAN_FW_OFFLOAD, H2C_TMP2, H2C_TMP3, H2C_WOWLAN_UPDATE_IV_CMD, /*50*/ H2C_TMP4, MAX_H2CCMD /*52*/ }; /* The following macros are used for FW * CMD map and parameter updated. */ #define FW_CMD_IO_CLR(rtlpriv, _Bit) \ do { \ udelay(1000); \ rtlpriv->rtlhal.fwcmd_iomap &= (~_Bit); \ } while (0) #define FW_CMD_IO_UPDATE(rtlpriv, _val) \ rtlpriv->rtlhal.fwcmd_iomap = _val; #define FW_CMD_IO_SET(rtlpriv, _val) \ do { \ rtl_write_word(rtlpriv, LBUS_MON_ADDR, (u16)_val); \ FW_CMD_IO_UPDATE(rtlpriv, _val); \ } while (0) #define FW_CMD_PARA_SET(rtlpriv, _val) \ do { \ rtl_write_dword(rtlpriv, LBUS_ADDR_MASK, _val); \ rtlpriv->rtlhal.fwcmd_ioparam = _val; \ } while (0) #define FW_CMD_IO_QUERY(rtlpriv) \ (u16)(rtlpriv->rtlhal.fwcmd_iomap) #define FW_CMD_IO_PARA_QUERY(rtlpriv) \ ((u32)(rtlpriv->rtlhal.fwcmd_ioparam)) int rtl92s_download_fw(struct ieee80211_hw *hw); void rtl92s_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); void rtl92s_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus, u8 ps_qosinfo); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/fw.c0000644000175000017500000004253412026211315024406 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../base.h" #include "reg.h" #include "def.h" #include "fw.h" static void _rtl92s_fw_set_rqpn(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_dword(rtlpriv, RQPN, 0xffffffff); rtl_write_dword(rtlpriv, RQPN + 4, 0xffffffff); rtl_write_byte(rtlpriv, RQPN + 8, 0xff); rtl_write_byte(rtlpriv, RQPN + 0xB, 0x80); } static bool _rtl92s_firmware_enable_cpu(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 ichecktime = 200; u16 tmpu2b; u8 tmpu1b, cpustatus = 0; _rtl92s_fw_set_rqpn(hw); /* Enable CPU. */ tmpu1b = rtl_read_byte(rtlpriv, SYS_CLKR); /* AFE source */ rtl_write_byte(rtlpriv, SYS_CLKR, (tmpu1b | SYS_CPU_CLKSEL)); tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | FEN_CPUEN)); /* Polling IMEM Ready after CPU has refilled. */ do { cpustatus = rtl_read_byte(rtlpriv, TCR); if (cpustatus & IMEM_RDY) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "IMEM Ready after CPU has refilled\n"); break; } udelay(100); } while (ichecktime--); if (!(cpustatus & IMEM_RDY)) return false; return true; } static enum fw_status _rtl92s_firmware_get_nextstatus( enum fw_status fw_currentstatus) { enum fw_status next_fwstatus = 0; switch (fw_currentstatus) { case FW_STATUS_INIT: next_fwstatus = FW_STATUS_LOAD_IMEM; break; case FW_STATUS_LOAD_IMEM: next_fwstatus = FW_STATUS_LOAD_EMEM; break; case FW_STATUS_LOAD_EMEM: next_fwstatus = FW_STATUS_LOAD_DMEM; break; case FW_STATUS_LOAD_DMEM: next_fwstatus = FW_STATUS_READY; break; default: break; } return next_fwstatus; } static u8 _rtl92s_firmware_header_map_rftype(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); switch (rtlphy->rf_type) { case RF_1T1R: return 0x11; break; case RF_1T2R: return 0x12; break; case RF_2T2R: return 0x22; break; default: RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Unknown RF type(%x)\n", rtlphy->rf_type); break; } return 0x22; } static void _rtl92s_firmwareheader_priveupdate(struct ieee80211_hw *hw, struct fw_priv *pfw_priv) { /* Update RF types for RATR settings. */ pfw_priv->rf_config = _rtl92s_firmware_header_map_rftype(hw); } static bool _rtl92s_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb, u8 last) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl8192_tx_ring *ring; struct rtl_tx_desc *pdesc; unsigned long flags; u8 idx = 0; ring = &rtlpci->tx_ring[TXCMD_QUEUE]; spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; pdesc = &ring->desc[idx]; rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb); __skb_queue_tail(&ring->queue, skb); spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); return true; } static bool _rtl92s_firmware_downloadcode(struct ieee80211_hw *hw, u8 *code_virtual_address, u32 buffer_len) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct sk_buff *skb; struct rtl_tcb_desc *tcb_desc; unsigned char *seg_ptr; u16 frag_threshold = MAX_FIRMWARE_CODE_SIZE; u16 frag_length, frag_offset = 0; u16 extra_descoffset = 0; u8 last_inipkt = 0; _rtl92s_fw_set_rqpn(hw); if (buffer_len >= MAX_FIRMWARE_CODE_SIZE) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Size over FIRMWARE_CODE_SIZE!\n"); return false; } extra_descoffset = 0; do { if ((buffer_len - frag_offset) > frag_threshold) { frag_length = frag_threshold + extra_descoffset; } else { frag_length = (u16)(buffer_len - frag_offset + extra_descoffset); last_inipkt = 1; } /* Allocate skb buffer to contain firmware */ /* info and tx descriptor info. */ skb = dev_alloc_skb(frag_length); if (!skb) return false; skb_reserve(skb, extra_descoffset); seg_ptr = (u8 *)skb_put(skb, (u32)(frag_length - extra_descoffset)); memcpy(seg_ptr, code_virtual_address + frag_offset, (u32)(frag_length - extra_descoffset)); tcb_desc = (struct rtl_tcb_desc *)(skb->cb); tcb_desc->queue_index = TXCMD_QUEUE; tcb_desc->cmd_or_init = DESC_PACKET_TYPE_INIT; tcb_desc->last_inipkt = last_inipkt; _rtl92s_cmd_send_packet(hw, skb, last_inipkt); frag_offset += (frag_length - extra_descoffset); } while (frag_offset < buffer_len); rtl_write_byte(rtlpriv, TP_POLL, TPPOLL_CQ); return true ; } static bool _rtl92s_firmware_checkready(struct ieee80211_hw *hw, u8 loadfw_status) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rt_firmware *firmware = (struct rt_firmware *)rtlhal->pfirmware; u32 tmpu4b; u8 cpustatus = 0; short pollingcnt = 1000; bool rtstatus = true; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "LoadStaus(%d)\n", loadfw_status); firmware->fwstatus = (enum fw_status)loadfw_status; switch (loadfw_status) { case FW_STATUS_LOAD_IMEM: /* Polling IMEM code done. */ do { cpustatus = rtl_read_byte(rtlpriv, TCR); if (cpustatus & IMEM_CODE_DONE) break; udelay(5); } while (pollingcnt--); if (!(cpustatus & IMEM_CHK_RPT) || (pollingcnt <= 0)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "FW_STATUS_LOAD_IMEM FAIL CPU, Status=%x\n", cpustatus); goto status_check_fail; } break; case FW_STATUS_LOAD_EMEM: /* Check Put Code OK and Turn On CPU */ /* Polling EMEM code done. */ do { cpustatus = rtl_read_byte(rtlpriv, TCR); if (cpustatus & EMEM_CODE_DONE) break; udelay(5); } while (pollingcnt--); if (!(cpustatus & EMEM_CHK_RPT) || (pollingcnt <= 0)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "FW_STATUS_LOAD_EMEM FAIL CPU, Status=%x\n", cpustatus); goto status_check_fail; } /* Turn On CPU */ rtstatus = _rtl92s_firmware_enable_cpu(hw); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Enable CPU fail!\n"); goto status_check_fail; } break; case FW_STATUS_LOAD_DMEM: /* Polling DMEM code done */ do { cpustatus = rtl_read_byte(rtlpriv, TCR); if (cpustatus & DMEM_CODE_DONE) break; udelay(5); } while (pollingcnt--); if (!(cpustatus & DMEM_CODE_DONE) || (pollingcnt <= 0)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Polling DMEM code done fail ! cpustatus(%#x)\n", cpustatus); goto status_check_fail; } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "DMEM code download success, cpustatus(%#x)\n", cpustatus); /* Prevent Delay too much and being scheduled out */ /* Polling Load Firmware ready */ pollingcnt = 2000; do { cpustatus = rtl_read_byte(rtlpriv, TCR); if (cpustatus & FWRDY) break; udelay(40); } while (pollingcnt--); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Polling Load Firmware ready, cpustatus(%x)\n", cpustatus); if (((cpustatus & LOAD_FW_READY) != LOAD_FW_READY) || (pollingcnt <= 0)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Polling Load Firmware ready fail ! cpustatus(%x)\n", cpustatus); goto status_check_fail; } /* If right here, we can set TCR/RCR to desired value */ /* and config MAC lookback mode to normal mode */ tmpu4b = rtl_read_dword(rtlpriv, TCR); rtl_write_dword(rtlpriv, TCR, (tmpu4b & (~TCR_ICV))); tmpu4b = rtl_read_dword(rtlpriv, RCR); rtl_write_dword(rtlpriv, RCR, (tmpu4b | RCR_APPFCS | RCR_APP_ICV | RCR_APP_MIC)); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Current RCR settings(%#x)\n", tmpu4b); /* Set to normal mode. */ rtl_write_byte(rtlpriv, LBKMD_SEL, LBK_NORMAL); break; default: RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Unknown status check!\n"); rtstatus = false; break; } status_check_fail: RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "loadfw_status(%d), rtstatus(%x)\n", loadfw_status, rtstatus); return rtstatus; } int rtl92s_download_fw(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rt_firmware *firmware = NULL; struct fw_hdr *pfwheader; struct fw_priv *pfw_priv = NULL; u8 *puc_mappedfile = NULL; u32 ul_filelength = 0; u8 fwhdr_size = RT_8192S_FIRMWARE_HDR_SIZE; u8 fwstatus = FW_STATUS_INIT; bool rtstatus = true; if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware) return 1; firmware = (struct rt_firmware *)rtlhal->pfirmware; firmware->fwstatus = FW_STATUS_INIT; puc_mappedfile = firmware->sz_fw_tmpbuffer; /* 1. Retrieve FW header. */ firmware->pfwheader = (struct fw_hdr *) puc_mappedfile; pfwheader = firmware->pfwheader; firmware->firmwareversion = byte(pfwheader->version, 0); firmware->pfwheader->fwpriv.hci_sel = 1;/* pcie */ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "signature:%x, version:%x, size:%x, imemsize:%x, sram size:%x\n", pfwheader->signature, pfwheader->version, pfwheader->dmem_size, pfwheader->img_imem_size, pfwheader->img_sram_size); /* 2. Retrieve IMEM image. */ if ((pfwheader->img_imem_size == 0) || (pfwheader->img_imem_size > sizeof(firmware->fw_imem))) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "memory for data image is less than IMEM required\n"); goto fail; } else { puc_mappedfile += fwhdr_size; memcpy(firmware->fw_imem, puc_mappedfile, pfwheader->img_imem_size); firmware->fw_imem_len = pfwheader->img_imem_size; } /* 3. Retriecve EMEM image. */ if (pfwheader->img_sram_size > sizeof(firmware->fw_emem)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "memory for data image is less than EMEM required\n"); goto fail; } else { puc_mappedfile += firmware->fw_imem_len; memcpy(firmware->fw_emem, puc_mappedfile, pfwheader->img_sram_size); firmware->fw_emem_len = pfwheader->img_sram_size; } /* 4. download fw now */ fwstatus = _rtl92s_firmware_get_nextstatus(firmware->fwstatus); while (fwstatus != FW_STATUS_READY) { /* Image buffer redirection. */ switch (fwstatus) { case FW_STATUS_LOAD_IMEM: puc_mappedfile = firmware->fw_imem; ul_filelength = firmware->fw_imem_len; break; case FW_STATUS_LOAD_EMEM: puc_mappedfile = firmware->fw_emem; ul_filelength = firmware->fw_emem_len; break; case FW_STATUS_LOAD_DMEM: /* Partial update the content of header private. */ pfwheader = firmware->pfwheader; pfw_priv = &pfwheader->fwpriv; _rtl92s_firmwareheader_priveupdate(hw, pfw_priv); puc_mappedfile = (u8 *)(firmware->pfwheader) + RT_8192S_FIRMWARE_HDR_EXCLUDE_PRI_SIZE; ul_filelength = fwhdr_size - RT_8192S_FIRMWARE_HDR_EXCLUDE_PRI_SIZE; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Unexpected Download step!!\n"); goto fail; break; } /* <2> Download image file */ rtstatus = _rtl92s_firmware_downloadcode(hw, puc_mappedfile, ul_filelength); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "fail!\n"); goto fail; } /* <3> Check whether load FW process is ready */ rtstatus = _rtl92s_firmware_checkready(hw, fwstatus); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "fail!\n"); goto fail; } fwstatus = _rtl92s_firmware_get_nextstatus(firmware->fwstatus); } return rtstatus; fail: return 0; } static u32 _rtl92s_fill_h2c_cmd(struct sk_buff *skb, u32 h2cbufferlen, u32 cmd_num, u32 *pelement_id, u32 *pcmd_len, u8 **pcmb_buffer, u8 *cmd_start_seq) { u32 totallen = 0, len = 0, tx_desclen = 0; u32 pre_continueoffset = 0; u8 *ph2c_buffer; u8 i = 0; do { /* 8 - Byte aligment */ len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8); /* Buffer length is not enough */ if (h2cbufferlen < totallen + len + tx_desclen) break; /* Clear content */ ph2c_buffer = (u8 *)skb_put(skb, (u32)len); memset((ph2c_buffer + totallen + tx_desclen), 0, len); /* CMD len */ SET_BITS_TO_LE_4BYTE((ph2c_buffer + totallen + tx_desclen), 0, 16, pcmd_len[i]); /* CMD ID */ SET_BITS_TO_LE_4BYTE((ph2c_buffer + totallen + tx_desclen), 16, 8, pelement_id[i]); /* CMD Sequence */ *cmd_start_seq = *cmd_start_seq % 0x80; SET_BITS_TO_LE_4BYTE((ph2c_buffer + totallen + tx_desclen), 24, 7, *cmd_start_seq); ++*cmd_start_seq; /* Copy memory */ memcpy((ph2c_buffer + totallen + tx_desclen + H2C_TX_CMD_HDR_LEN), pcmb_buffer[i], pcmd_len[i]); /* CMD continue */ /* set the continue in prevoius cmd. */ if (i < cmd_num - 1) SET_BITS_TO_LE_4BYTE((ph2c_buffer + pre_continueoffset), 31, 1, 1); pre_continueoffset = totallen; totallen += len; } while (++i < cmd_num); return totallen; } static u32 _rtl92s_get_h2c_cmdlen(u32 h2cbufferlen, u32 cmd_num, u32 *pcmd_len) { u32 totallen = 0, len = 0, tx_desclen = 0; u8 i = 0; do { /* 8 - Byte aligment */ len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8); /* Buffer length is not enough */ if (h2cbufferlen < totallen + len + tx_desclen) break; totallen += len; } while (++i < cmd_num); return totallen + tx_desclen; } static bool _rtl92s_firmware_set_h2c_cmd(struct ieee80211_hw *hw, u8 h2c_cmd, u8 *pcmd_buffer) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_tcb_desc *cb_desc; struct sk_buff *skb; u32 element_id = 0; u32 cmd_len = 0; u32 len; switch (h2c_cmd) { case FW_H2C_SETPWRMODE: element_id = H2C_SETPWRMODE_CMD ; cmd_len = sizeof(struct h2c_set_pwrmode_parm); break; case FW_H2C_JOINBSSRPT: element_id = H2C_JOINBSSRPT_CMD; cmd_len = sizeof(struct h2c_joinbss_rpt_parm); break; case FW_H2C_WOWLAN_UPDATE_GTK: element_id = H2C_WOWLAN_UPDATE_GTK_CMD; cmd_len = sizeof(struct h2c_wpa_two_way_parm); break; case FW_H2C_WOWLAN_UPDATE_IV: element_id = H2C_WOWLAN_UPDATE_IV_CMD; cmd_len = sizeof(unsigned long long); break; case FW_H2C_WOWLAN_OFFLOAD: element_id = H2C_WOWLAN_FW_OFFLOAD; cmd_len = sizeof(u8); break; default: break; } len = _rtl92s_get_h2c_cmdlen(MAX_TRANSMIT_BUFFER_SIZE, 1, &cmd_len); skb = dev_alloc_skb(len); if (!skb) return false; cb_desc = (struct rtl_tcb_desc *)(skb->cb); cb_desc->queue_index = TXCMD_QUEUE; cb_desc->cmd_or_init = DESC_PACKET_TYPE_NORMAL; cb_desc->last_inipkt = false; _rtl92s_fill_h2c_cmd(skb, MAX_TRANSMIT_BUFFER_SIZE, 1, &element_id, &cmd_len, &pcmd_buffer, &rtlhal->h2c_txcmd_seq); _rtl92s_cmd_send_packet(hw, skb, false); rtlpriv->cfg->ops->tx_polling(hw, TXCMD_QUEUE); return true; } void rtl92s_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 Mode) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct h2c_set_pwrmode_parm pwrmode; u16 max_wakeup_period = 0; pwrmode.mode = Mode; pwrmode.flag_low_traffic_en = 0; pwrmode.flag_lpnav_en = 0; pwrmode.flag_rf_low_snr_en = 0; pwrmode.flag_dps_en = 0; pwrmode.bcn_rx_en = 0; pwrmode.bcn_to = 0; SET_BITS_TO_LE_2BYTE((u8 *)(&pwrmode) + 8, 0, 16, mac->vif->bss_conf.beacon_int); pwrmode.app_itv = 0; pwrmode.awake_bcn_itvl = ppsc->reg_max_lps_awakeintvl; pwrmode.smart_ps = 1; pwrmode.bcn_pass_period = 10; /* Set beacon pass count */ if (pwrmode.mode == FW_PS_MIN_MODE) max_wakeup_period = mac->vif->bss_conf.beacon_int; else if (pwrmode.mode == FW_PS_MAX_MODE) max_wakeup_period = mac->vif->bss_conf.beacon_int * mac->vif->bss_conf.dtim_period; if (max_wakeup_period >= 500) pwrmode.bcn_pass_cnt = 1; else if ((max_wakeup_period >= 300) && (max_wakeup_period < 500)) pwrmode.bcn_pass_cnt = 2; else if ((max_wakeup_period >= 200) && (max_wakeup_period < 300)) pwrmode.bcn_pass_cnt = 3; else if ((max_wakeup_period >= 20) && (max_wakeup_period < 200)) pwrmode.bcn_pass_cnt = 5; else pwrmode.bcn_pass_cnt = 1; _rtl92s_firmware_set_h2c_cmd(hw, FW_H2C_SETPWRMODE, (u8 *)&pwrmode); } void rtl92s_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus, u8 ps_qosinfo) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct h2c_joinbss_rpt_parm joinbss_rpt; joinbss_rpt.opmode = mstatus; joinbss_rpt.ps_qos_info = ps_qosinfo; joinbss_rpt.bssid[0] = mac->bssid[0]; joinbss_rpt.bssid[1] = mac->bssid[1]; joinbss_rpt.bssid[2] = mac->bssid[2]; joinbss_rpt.bssid[3] = mac->bssid[3]; joinbss_rpt.bssid[4] = mac->bssid[4]; joinbss_rpt.bssid[5] = mac->bssid[5]; SET_BITS_TO_LE_2BYTE((u8 *)(&joinbss_rpt) + 8, 0, 16, mac->vif->bss_conf.beacon_int); SET_BITS_TO_LE_2BYTE((u8 *)(&joinbss_rpt) + 10, 0, 16, mac->assoc_id); _rtl92s_firmware_set_h2c_cmd(hw, FW_H2C_JOINBSSRPT, (u8 *)&joinbss_rpt); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/dm.h0000644000175000017500000000620112026211315024366 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL_92S_DM_H__ #define __RTL_92S_DM_H__ enum dm_dig_alg { DIG_ALGO_BY_FALSE_ALARM = 0, DIG_ALGO_BY_RSSI = 1, DIG_ALGO_BEFORE_CONNECT_BY_RSSI_AND_ALARM = 2, DIG_ALGO_BY_TOW_PORT = 3, DIG_ALGO_MAX }; enum dm_dig_two_port_alg { DIG_TWO_PORT_ALGO_RSSI = 0, DIG_TWO_PORT_ALGO_FALSE_ALARM = 1, }; enum dm_dig_dbg { DM_DBG_OFF = 0, DM_DBG_ON = 1, DM_DBG_MAX }; enum dm_dig_sta { DM_STA_DIG_OFF = 0, DM_STA_DIG_ON, DM_STA_DIG_MAX }; enum dm_dig_connect { DIG_STA_DISCONNECT = 0, DIG_STA_CONNECT = 1, DIG_STA_BEFORE_CONNECT = 2, DIG_AP_DISCONNECT = 3, DIG_AP_CONNECT = 4, DIG_AP_ADD_STATION = 5, DIG_CONNECT_MAX }; enum dm_dig_ext_port_alg { DIG_EXT_PORT_STAGE_0 = 0, DIG_EXT_PORT_STAGE_1 = 1, DIG_EXT_PORT_STAGE_2 = 2, DIG_EXT_PORT_STAGE_3 = 3, DIG_EXT_PORT_STAGE_MAX = 4, }; enum dm_ratr_sta { DM_RATR_STA_HIGH = 0, DM_RATR_STA_MIDDLEHIGH = 1, DM_RATR_STA_MIDDLE = 2, DM_RATR_STA_MIDDLELOW = 3, DM_RATR_STA_LOW = 4, DM_RATR_STA_ULTRALOW = 5, DM_RATR_STA_MAX }; #define DM_TYPE_BYFW 0 #define DM_TYPE_BYDRIVER 1 #define TX_HIGH_PWR_LEVEL_NORMAL 0 #define TX_HIGH_PWR_LEVEL_LEVEL1 1 #define TX_HIGH_PWR_LEVEL_LEVEL2 2 #define HAL_DM_DIG_DISABLE BIT(0) /* Disable Dig */ #define HAL_DM_HIPWR_DISABLE BIT(1) /* Disable High Power */ #define TX_HIGHPWR_LEVEL_NORMAL 0 #define TX_HIGHPWR_LEVEL_NORMAL1 1 #define TX_HIGHPWR_LEVEL_NORMAL2 2 #define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 #define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 #define DM_DIG_THRESH_HIGH 40 #define DM_DIG_THRESH_LOW 35 #define DM_FALSEALARM_THRESH_LOW 40 #define DM_FALSEALARM_THRESH_HIGH 1000 #define DM_DIG_HIGH_PWR_THRESH_HIGH 75 #define DM_DIG_HIGH_PWR_THRESH_LOW 70 #define DM_DIG_BACKOFF 12 #define DM_DIG_MAX 0x3e #define DM_DIG_MIN 0x1c #define DM_DIG_MIN_Netcore 0x12 #define DM_DIG_BACKOFF_MAX 12 #define DM_DIG_BACKOFF_MIN -4 void rtl92s_dm_watchdog(struct ieee80211_hw *hw); void rtl92s_dm_init(struct ieee80211_hw *hw); void rtl92s_dm_init_edca_turbo(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/dm.c0000644000175000017500000005214212026211315024366 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "fw.h" static const u32 edca_setting_dl[PEER_MAX] = { 0xa44f, /* 0 UNKNOWN */ 0x5ea44f, /* 1 REALTEK_90 */ 0x5ea44f, /* 2 REALTEK_92SE */ 0xa630, /* 3 BROAD */ 0xa44f, /* 4 RAL */ 0xa630, /* 5 ATH */ 0xa630, /* 6 CISCO */ 0xa42b, /* 7 MARV */ }; static const u32 edca_setting_dl_gmode[PEER_MAX] = { 0x4322, /* 0 UNKNOWN */ 0xa44f, /* 1 REALTEK_90 */ 0x5ea44f, /* 2 REALTEK_92SE */ 0xa42b, /* 3 BROAD */ 0x5e4322, /* 4 RAL */ 0x4322, /* 5 ATH */ 0xa430, /* 6 CISCO */ 0x5ea44f, /* 7 MARV */ }; static const u32 edca_setting_ul[PEER_MAX] = { 0x5e4322, /* 0 UNKNOWN */ 0xa44f, /* 1 REALTEK_90 */ 0x5ea44f, /* 2 REALTEK_92SE */ 0x5ea322, /* 3 BROAD */ 0x5ea422, /* 4 RAL */ 0x5ea322, /* 5 ATH */ 0x3ea44f, /* 6 CISCO */ 0x5ea44f, /* 7 MARV */ }; static void _rtl92s_dm_check_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); static u64 last_txok_cnt; static u64 last_rxok_cnt; u64 cur_txok_cnt = 0; u64 cur_rxok_cnt = 0; u32 edca_be_ul = edca_setting_ul[mac->vendor]; u32 edca_be_dl = edca_setting_dl[mac->vendor]; u32 edca_gmode = edca_setting_dl_gmode[mac->vendor]; if (mac->link_state != MAC80211_LINKED) { rtlpriv->dm.current_turbo_edca = false; goto dm_checkedcaturbo_exit; } if ((!rtlpriv->dm.is_any_nonbepkts) && (!rtlpriv->dm.disable_framebursting)) { cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok_cnt; cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rxok_cnt; if (rtlpriv->phy.rf_type == RF_1T2R) { if (cur_txok_cnt > 4 * cur_rxok_cnt) { /* Uplink TP is present. */ if (rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { rtl_write_dword(rtlpriv, EDCAPARA_BE, edca_be_ul); rtlpriv->dm.is_cur_rdlstate = false; } } else {/* Balance TP is present. */ if (!rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { if (mac->mode == WIRELESS_MODE_G || mac->mode == WIRELESS_MODE_B) rtl_write_dword(rtlpriv, EDCAPARA_BE, edca_gmode); else rtl_write_dword(rtlpriv, EDCAPARA_BE, edca_be_dl); rtlpriv->dm.is_cur_rdlstate = true; } } rtlpriv->dm.current_turbo_edca = true; } else { if (cur_rxok_cnt > 4 * cur_txok_cnt) { if (!rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { if (mac->mode == WIRELESS_MODE_G || mac->mode == WIRELESS_MODE_B) rtl_write_dword(rtlpriv, EDCAPARA_BE, edca_gmode); else rtl_write_dword(rtlpriv, EDCAPARA_BE, edca_be_dl); rtlpriv->dm.is_cur_rdlstate = true; } } else { if (rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { rtl_write_dword(rtlpriv, EDCAPARA_BE, edca_be_ul); rtlpriv->dm.is_cur_rdlstate = false; } } rtlpriv->dm.current_turbo_edca = true; } } else { if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, &tmp); rtlpriv->dm.current_turbo_edca = false; } } dm_checkedcaturbo_exit: rtlpriv->dm.is_any_nonbepkts = false; last_txok_cnt = rtlpriv->stats.txbytesunicast; last_rxok_cnt = rtlpriv->stats.rxbytesunicast; } static void _rtl92s_dm_txpowertracking_callback_thermalmeter( struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 thermalvalue = 0; rtlpriv->dm.txpower_trackinginit = true; thermalvalue = (u8)rtl_get_rfreg(hw, RF90_PATH_A, RF_T_METER, 0x1f); RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Readback Thermal Meter = 0x%x pre thermal meter 0x%x eeprom_thermal meter 0x%x\n", thermalvalue, rtlpriv->dm.thermalvalue, rtlefuse->eeprom_thermalmeter); if (thermalvalue) { rtlpriv->dm.thermalvalue = thermalvalue; rtl92s_phy_set_fw_cmd(hw, FW_CMD_TXPWR_TRACK_THERMAL); } rtlpriv->dm.txpowercount = 0; } static void _rtl92s_dm_check_txpowertracking_thermalmeter( struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); static u8 tm_trigger; u8 tx_power_checkcnt = 5; /* 2T2R TP issue */ if (rtlphy->rf_type == RF_2T2R) return; if (!rtlpriv->dm.txpower_tracking) return; if (rtlpriv->dm.txpowercount <= tx_power_checkcnt) { rtlpriv->dm.txpowercount++; return; } if (!tm_trigger) { rtl_set_rfreg(hw, RF90_PATH_A, RF_T_METER, RFREG_OFFSET_MASK, 0x60); tm_trigger = 1; } else { _rtl92s_dm_txpowertracking_callback_thermalmeter(hw); tm_trigger = 0; } } static void _rtl92s_dm_refresh_rateadaptive_mask(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rate_adaptive *ra = &(rtlpriv->ra); u32 low_rssi_thresh = 0; u32 middle_rssi_thresh = 0; u32 high_rssi_thresh = 0; struct ieee80211_sta *sta = NULL; if (is_hal_stop(rtlhal)) return; if (!rtlpriv->dm.useramask) return; if (!rtlpriv->dm.inform_fw_driverctrldm) { rtl92s_phy_set_fw_cmd(hw, FW_CMD_CTRL_DM_BY_DRIVER); rtlpriv->dm.inform_fw_driverctrldm = true; } rcu_read_lock(); if (mac->opmode == NL80211_IFTYPE_STATION) sta = get_sta(hw, mac->vif, mac->bssid); if ((mac->link_state == MAC80211_LINKED) && (mac->opmode == NL80211_IFTYPE_STATION)) { switch (ra->pre_ratr_state) { case DM_RATR_STA_HIGH: high_rssi_thresh = 40; middle_rssi_thresh = 30; low_rssi_thresh = 20; break; case DM_RATR_STA_MIDDLE: high_rssi_thresh = 44; middle_rssi_thresh = 30; low_rssi_thresh = 20; break; case DM_RATR_STA_LOW: high_rssi_thresh = 44; middle_rssi_thresh = 34; low_rssi_thresh = 20; break; case DM_RATR_STA_ULTRALOW: high_rssi_thresh = 44; middle_rssi_thresh = 34; low_rssi_thresh = 24; break; default: high_rssi_thresh = 44; middle_rssi_thresh = 34; low_rssi_thresh = 24; break; } if (rtlpriv->dm.undecorated_smoothed_pwdb > (long)high_rssi_thresh) { ra->ratr_state = DM_RATR_STA_HIGH; } else if (rtlpriv->dm.undecorated_smoothed_pwdb > (long)middle_rssi_thresh) { ra->ratr_state = DM_RATR_STA_LOW; } else if (rtlpriv->dm.undecorated_smoothed_pwdb > (long)low_rssi_thresh) { ra->ratr_state = DM_RATR_STA_LOW; } else { ra->ratr_state = DM_RATR_STA_ULTRALOW; } if (ra->pre_ratr_state != ra->ratr_state) { RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, "RSSI = %ld RSSI_LEVEL = %d PreState = %d, CurState = %d\n", rtlpriv->dm.undecorated_smoothed_pwdb, ra->ratr_state, ra->pre_ratr_state, ra->ratr_state); rtlpriv->cfg->ops->update_rate_tbl(hw, sta, ra->ratr_state); ra->pre_ratr_state = ra->ratr_state; } } rcu_read_unlock(); } static void _rtl92s_dm_switch_baseband_mrc(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); bool current_mrc; bool enable_mrc = true; long tmpentry_maxpwdb = 0; u8 rssi_a = 0; u8 rssi_b = 0; if (is_hal_stop(rtlhal)) return; if ((rtlphy->rf_type == RF_1T1R) || (rtlphy->rf_type == RF_2T2R)) return; rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_MRC, (u8 *)(¤t_mrc)); if (mac->link_state >= MAC80211_LINKED) { if (rtlpriv->dm.undecorated_smoothed_pwdb > tmpentry_maxpwdb) { rssi_a = rtlpriv->stats.rx_rssi_percentage[RF90_PATH_A]; rssi_b = rtlpriv->stats.rx_rssi_percentage[RF90_PATH_B]; } } /* MRC settings would NOT affect TP on Wireless B mode. */ if (mac->mode != WIRELESS_MODE_B) { if ((rssi_a == 0) && (rssi_b == 0)) { enable_mrc = true; } else if (rssi_b > 30) { /* Turn on B-Path */ enable_mrc = true; } else if (rssi_b < 5) { /* Turn off B-path */ enable_mrc = false; /* Take care of RSSI differentiation. */ } else if (rssi_a > 15 && (rssi_a >= rssi_b)) { if ((rssi_a - rssi_b) > 15) /* Turn off B-path */ enable_mrc = false; else if ((rssi_a - rssi_b) < 10) /* Turn on B-Path */ enable_mrc = true; else enable_mrc = current_mrc; } else { /* Turn on B-Path */ enable_mrc = true; } } /* Update MRC settings if needed. */ if (enable_mrc != current_mrc) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_MRC, (u8 *)&enable_mrc); } void rtl92s_dm_init_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.current_turbo_edca = false; rtlpriv->dm.is_any_nonbepkts = false; rtlpriv->dm.is_cur_rdlstate = false; } static void _rtl92s_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rate_adaptive *ra = &(rtlpriv->ra); ra->ratr_state = DM_RATR_STA_MAX; ra->pre_ratr_state = DM_RATR_STA_MAX; if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER) rtlpriv->dm.useramask = true; else rtlpriv->dm.useramask = false; rtlpriv->dm.useramask = false; rtlpriv->dm.inform_fw_driverctrldm = false; } static void _rtl92s_dm_init_txpowertracking_thermalmeter( struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.txpower_tracking = true; rtlpriv->dm.txpowercount = 0; rtlpriv->dm.txpower_trackinginit = false; } static void _rtl92s_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); u32 ret_value; ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, MASKDWORD); falsealm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, MASKDWORD); falsealm_cnt->cnt_rate_illegal = (ret_value & 0xffff); falsealm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, MASKDWORD); falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff); falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail + falsealm_cnt->cnt_rate_illegal + falsealm_cnt->cnt_crc8_fail + falsealm_cnt->cnt_mcs_fail; /* read CCK false alarm */ ret_value = rtl_get_bbreg(hw, 0xc64, MASKDWORD); falsealm_cnt->cnt_cck_fail = (ret_value & 0xffff); falsealm_cnt->cnt_all = falsealm_cnt->cnt_ofdm_fail + falsealm_cnt->cnt_cck_fail; } static void rtl92s_backoff_enable_flag(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *digtable = &rtlpriv->dm_digtable; struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); if (falsealm_cnt->cnt_all > digtable->fa_highthresh) { if ((digtable->backoff_val - 6) < digtable->backoffval_range_min) digtable->backoff_val = digtable->backoffval_range_min; else digtable->backoff_val -= 6; } else if (falsealm_cnt->cnt_all < digtable->fa_lowthresh) { if ((digtable->backoff_val + 6) > digtable->backoffval_range_max) digtable->backoff_val = digtable->backoffval_range_max; else digtable->backoff_val += 6; } } static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *digtable = &rtlpriv->dm_digtable; struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); static u8 initialized, force_write; u8 initial_gain = 0; if ((digtable->pre_sta_connectstate == digtable->cur_sta_connectstate) || (digtable->cur_sta_connectstate == DIG_STA_BEFORE_CONNECT)) { if (digtable->cur_sta_connectstate == DIG_STA_BEFORE_CONNECT) { if (rtlpriv->psc.rfpwr_state != ERFON) return; if (digtable->backoff_enable_flag) rtl92s_backoff_enable_flag(hw); else digtable->backoff_val = DM_DIG_BACKOFF; if ((digtable->rssi_val + 10 - digtable->backoff_val) > digtable->rx_gain_range_max) digtable->cur_igvalue = digtable->rx_gain_range_max; else if ((digtable->rssi_val + 10 - digtable->backoff_val) < digtable->rx_gain_range_min) digtable->cur_igvalue = digtable->rx_gain_range_min; else digtable->cur_igvalue = digtable->rssi_val + 10 - digtable->backoff_val; if (falsealm_cnt->cnt_all > 10000) digtable->cur_igvalue = (digtable->cur_igvalue > 0x33) ? digtable->cur_igvalue : 0x33; if (falsealm_cnt->cnt_all > 16000) digtable->cur_igvalue = digtable->rx_gain_range_max; /* connected -> connected or disconnected -> disconnected */ } else { /* Firmware control DIG, do nothing in driver dm */ return; } /* disconnected -> connected or connected -> * disconnected or beforeconnect->(dis)connected */ } else { /* Enable FW DIG */ digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; rtl92s_phy_set_fw_cmd(hw, FW_CMD_DIG_ENABLE); digtable->backoff_val = DM_DIG_BACKOFF; digtable->cur_igvalue = rtlpriv->phy.default_initialgain[0]; digtable->pre_igvalue = 0; return; } /* Forced writing to prevent from fw-dig overwriting. */ if (digtable->pre_igvalue != rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0)) force_write = 1; if ((digtable->pre_igvalue != digtable->cur_igvalue) || !initialized || force_write) { /* Disable FW DIG */ rtl92s_phy_set_fw_cmd(hw, FW_CMD_DIG_DISABLE); initial_gain = (u8)digtable->cur_igvalue; /* Set initial gain. */ rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0, initial_gain); rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0, initial_gain); digtable->pre_igvalue = digtable->cur_igvalue; initialized = 1; force_write = 0; } } static void _rtl92s_dm_ctrl_initgain_bytwoport(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *digtable = &rtlpriv->dm_digtable; if (rtlpriv->mac80211.act_scanning) return; /* Decide the current status and if modify initial gain or not */ if (rtlpriv->mac80211.link_state >= MAC80211_LINKED || rtlpriv->mac80211.opmode == NL80211_IFTYPE_ADHOC) digtable->cur_sta_connectstate = DIG_STA_CONNECT; else digtable->cur_sta_connectstate = DIG_STA_DISCONNECT; digtable->rssi_val = rtlpriv->dm.undecorated_smoothed_pwdb; /* Change dig mode to rssi */ if (digtable->cur_sta_connectstate != DIG_STA_DISCONNECT) { if (digtable->dig_twoport_algorithm == DIG_TWO_PORT_ALGO_FALSE_ALARM) { digtable->dig_twoport_algorithm = DIG_TWO_PORT_ALGO_RSSI; rtl92s_phy_set_fw_cmd(hw, FW_CMD_DIG_MODE_SS); } } _rtl92s_dm_false_alarm_counter_statistics(hw); _rtl92s_dm_initial_gain_sta_beforeconnect(hw); digtable->pre_sta_connectstate = digtable->cur_sta_connectstate; } static void _rtl92s_dm_ctrl_initgain_byrssi(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct dig_t *digtable = &rtlpriv->dm_digtable; /* 2T2R TP issue */ if (rtlphy->rf_type == RF_2T2R) return; if (!rtlpriv->dm.dm_initialgain_enable) return; if (digtable->dig_enable_flag == false) return; _rtl92s_dm_ctrl_initgain_bytwoport(hw); } static void _rtl92s_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb; long txpwr_threshold_lv1, txpwr_threshold_lv2; /* 2T2R TP issue */ if (rtlphy->rf_type == RF_2T2R) return; if (!rtlpriv->dm.dynamic_txpower_enable || rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) { rtlpriv->dm.dynamic_txhighpower_lvl = TX_HIGHPWR_LEVEL_NORMAL; return; } if ((mac->link_state < MAC80211_LINKED) && (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb == 0)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "Not connected to any\n"); rtlpriv->dm.dynamic_txhighpower_lvl = TX_HIGHPWR_LEVEL_NORMAL; rtlpriv->dm.last_dtp_lvl = TX_HIGHPWR_LEVEL_NORMAL; return; } if (mac->link_state >= MAC80211_LINKED) { if (mac->opmode == NL80211_IFTYPE_ADHOC) { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Client PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } else { undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "STA Default Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } } else { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Ext Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } txpwr_threshold_lv2 = TX_POWER_NEAR_FIELD_THRESH_LVL2; txpwr_threshold_lv1 = TX_POWER_NEAR_FIELD_THRESH_LVL1; if (rtl_get_bbreg(hw, 0xc90, MASKBYTE0) == 1) rtlpriv->dm.dynamic_txhighpower_lvl = TX_HIGHPWR_LEVEL_NORMAL; else if (undecorated_smoothed_pwdb >= txpwr_threshold_lv2) rtlpriv->dm.dynamic_txhighpower_lvl = TX_HIGHPWR_LEVEL_NORMAL2; else if ((undecorated_smoothed_pwdb < (txpwr_threshold_lv2 - 3)) && (undecorated_smoothed_pwdb >= txpwr_threshold_lv1)) rtlpriv->dm.dynamic_txhighpower_lvl = TX_HIGHPWR_LEVEL_NORMAL1; else if (undecorated_smoothed_pwdb < (txpwr_threshold_lv1 - 3)) rtlpriv->dm.dynamic_txhighpower_lvl = TX_HIGHPWR_LEVEL_NORMAL; if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) rtl92s_phy_set_txpower(hw, rtlphy->current_channel); rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; } static void _rtl92s_dm_init_dig(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *digtable = &rtlpriv->dm_digtable; /* Disable DIG scheme now.*/ digtable->dig_enable_flag = true; digtable->backoff_enable_flag = true; if ((rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER) && (hal_get_firmwareversion(rtlpriv) >= 0x3c)) digtable->dig_algorithm = DIG_ALGO_BY_TOW_PORT; else digtable->dig_algorithm = DIG_ALGO_BEFORE_CONNECT_BY_RSSI_AND_ALARM; digtable->dig_twoport_algorithm = DIG_TWO_PORT_ALGO_RSSI; digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; /* off=by real rssi value, on=by digtable->rssi_val for new dig */ digtable->dig_dbgmode = DM_DBG_OFF; digtable->dig_slgorithm_switch = 0; /* 2007/10/04 MH Define init gain threshol. */ digtable->dig_state = DM_STA_DIG_MAX; digtable->dig_highpwrstate = DM_STA_DIG_MAX; digtable->cur_sta_connectstate = DIG_STA_DISCONNECT; digtable->pre_sta_connectstate = DIG_STA_DISCONNECT; digtable->cur_ap_connectstate = DIG_AP_DISCONNECT; digtable->pre_ap_connectstate = DIG_AP_DISCONNECT; digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; digtable->rssi_highpower_lowthresh = DM_DIG_HIGH_PWR_THRESH_LOW; digtable->rssi_highpower_highthresh = DM_DIG_HIGH_PWR_THRESH_HIGH; /* for dig debug rssi value */ digtable->rssi_val = 50; digtable->backoff_val = DM_DIG_BACKOFF; digtable->rx_gain_range_max = DM_DIG_MAX; digtable->rx_gain_range_min = DM_DIG_MIN; digtable->backoffval_range_max = DM_DIG_BACKOFF_MAX; digtable->backoffval_range_min = DM_DIG_BACKOFF_MIN; } static void _rtl92s_dm_init_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); if ((hal_get_firmwareversion(rtlpriv) >= 60) && (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER)) rtlpriv->dm.dynamic_txpower_enable = true; else rtlpriv->dm.dynamic_txpower_enable = false; rtlpriv->dm.last_dtp_lvl = TX_HIGHPWR_LEVEL_NORMAL; rtlpriv->dm.dynamic_txhighpower_lvl = TX_HIGHPWR_LEVEL_NORMAL; } void rtl92s_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; rtlpriv->dm.undecorated_smoothed_pwdb = -1; _rtl92s_dm_init_dynamic_txpower(hw); rtl92s_dm_init_edca_turbo(hw); _rtl92s_dm_init_rate_adaptive_mask(hw); _rtl92s_dm_init_txpowertracking_thermalmeter(hw); _rtl92s_dm_init_dig(hw); rtl_write_dword(rtlpriv, WFM5, FW_CCA_CHK_ENABLE); } void rtl92s_dm_watchdog(struct ieee80211_hw *hw) { _rtl92s_dm_check_edca_turbo(hw); _rtl92s_dm_check_txpowertracking_thermalmeter(hw); _rtl92s_dm_ctrl_initgain_byrssi(hw); _rtl92s_dm_dynamic_txpower(hw); _rtl92s_dm_refresh_rateadaptive_mask(hw); _rtl92s_dm_switch_baseband_mrc(hw); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/def.h0000644000175000017500000005111512026211315024530 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __REALTEK_92S_DEF_H__ #define __REALTEK_92S_DEF_H__ #define RX_MPDU_QUEUE 0 #define RX_CMD_QUEUE 1 #define RX_MAX_QUEUE 2 #define SHORT_SLOT_TIME 9 #define NON_SHORT_SLOT_TIME 20 /* Rx smooth factor */ #define RX_SMOOTH_FACTOR 20 /* Queue Select Value in TxDesc */ #define QSLT_BK 0x2 #define QSLT_BE 0x0 #define QSLT_VI 0x5 #define QSLT_VO 0x6 #define QSLT_BEACON 0x10 #define QSLT_HIGH 0x11 #define QSLT_MGNT 0x12 #define QSLT_CMD 0x13 #define PHY_RSSI_SLID_WIN_MAX 100 #define PHY_LINKQUALITY_SLID_WIN_MAX 20 #define PHY_BEACON_RSSI_SLID_WIN_MAX 10 /* Tx Desc */ #define TX_DESC_SIZE_RTL8192S (16 * 4) #define TX_CMDDESC_SIZE_RTL8192S (16 * 4) /* Define a macro that takes a le32 word, converts it to host ordering, * right shifts by a specified count, creates a mask of the specified * bit count, and extracts that number of bits. */ #define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \ ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \ BIT_LEN_MASK_32(__mask)) /* Define a macro that clears a bit field in an le32 word and * sets the specified value into that bit field. The resulting * value remains in le32 ordering; however, it is properly converted * to host ordering for the clear and set operations before conversion * back to le32. */ #define SET_BITS_OFFSET_LE(__pdesc, __shift, __len, __val) \ (*(__le32 *)(__pdesc) = \ (cpu_to_le32((le32_to_cpu(*((__le32 *)(__pdesc))) & \ (~(BIT_OFFSET_LEN_MASK_32((__shift), __len)))) | \ (((u32)(__val) & BIT_LEN_MASK_32(__len)) << (__shift))))); /* macros to read/write various fields in RX or TX descriptors */ /* Dword 0 */ #define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 0, 16, __val) #define SET_TX_DESC_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 16, 8, __val) #define SET_TX_DESC_TYPE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 24, 2, __val) #define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 26, 1, __val) #define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 27, 1, __val) #define SET_TX_DESC_LINIP(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 28, 1, __val) #define SET_TX_DESC_AMSDU(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 29, 1, __val) #define SET_TX_DESC_GREEN_FIELD(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) #define SET_TX_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) #define GET_TX_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) /* Dword 1 */ #define SET_TX_DESC_MACID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 0, 5, __val) #define SET_TX_DESC_MORE_DATA(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 5, 1, __val) #define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 6, 1, __val) #define SET_TX_DESC_PIFS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 7, 1, __val) #define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 8, 5, __val) #define SET_TX_DESC_ACK_POLICY(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 13, 2, __val) #define SET_TX_DESC_NO_ACM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 15, 1, __val) #define SET_TX_DESC_NON_QOS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 16, 1, __val) #define SET_TX_DESC_KEY_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 17, 2, __val) #define SET_TX_DESC_OUI(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 19, 1, __val) #define SET_TX_DESC_PKT_TYPE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 20, 1, __val) #define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 21, 1, __val) #define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 22, 2, __val) #define SET_TX_DESC_WDS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 24, 1, __val) #define SET_TX_DESC_HTC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 25, 1, __val) #define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 26, 5, __val) #define SET_TX_DESC_HWPC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 27, 1, __val) /* Dword 2 */ #define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 0, 6, __val) #define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 6, 1, __val) #define SET_TX_DESC_TSFL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 7, 5, __val) #define SET_TX_DESC_RTS_RETRY_COUNT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 12, 6, __val) #define SET_TX_DESC_DATA_RETRY_COUNT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 18, 6, __val) #define SET_TX_DESC_RSVD_MACID(__pdesc, __val) \ SET_BITS_OFFSET_LE(((__pdesc) + 8), 24, 5, __val) #define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 29, 1, __val) #define SET_TX_DESC_AGG_BREAK(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 30, 1, __val) #define SET_TX_DESC_OWN_MAC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 31, 1, __val) /* Dword 3 */ #define SET_TX_DESC_NEXT_HEAP_PAGE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 0, 8, __val) #define SET_TX_DESC_TAIL_PAGE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 8, 8, __val) #define SET_TX_DESC_SEQ(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 16, 12, __val) #define SET_TX_DESC_FRAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 28, 4, __val) /* Dword 4 */ #define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 0, 6, __val) #define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 6, 1, __val) #define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 7, 4, __val) #define SET_TX_DESC_CTS_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 11, 1, __val) #define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 12, 1, __val) #define SET_TX_DESC_RA_BRSR_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 13, 3, __val) #define SET_TX_DESC_TXHT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 16, 1, __val) #define SET_TX_DESC_TX_SHORT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 17, 1, __val) #define SET_TX_DESC_TX_BANDWIDTH(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 18, 1, __val) #define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 19, 2, __val) #define SET_TX_DESC_TX_STBC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 21, 2, __val) #define SET_TX_DESC_TX_REVERSE_DIRECTION(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 23, 1, __val) #define SET_TX_DESC_RTS_HT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 24, 1, __val) #define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 25, 1, __val) #define SET_TX_DESC_RTS_BANDWIDTH(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 26, 1, __val) #define SET_TX_DESC_RTS_SUB_CARRIER(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 27, 2, __val) #define SET_TX_DESC_RTS_STBC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 29, 2, __val) #define SET_TX_DESC_USER_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 31, 1, __val) /* Dword 5 */ #define SET_TX_DESC_PACKET_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 20, 0, 9, __val) #define SET_TX_DESC_TX_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 20, 9, 6, __val) #define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 20, 15, 1, __val) #define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 20, 16, 5, __val) #define SET_TX_DESC_TX_AGC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 20, 21, 11, __val) /* Dword 6 */ #define SET_TX_DESC_IP_CHECK_SUM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 24, 0, 16, __val) #define SET_TX_DESC_TCP_CHECK_SUM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 24, 16, 16, __val) /* Dword 7 */ #define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 28, 0, 16, __val) #define SET_TX_DESC_IP_HEADER_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 28, 16, 8, __val) #define SET_TX_DESC_TCP_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 28, 31, 1, __val) /* Dword 8 */ #define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 32, 0, 32, __val) #define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 32, 0, 32) /* Dword 9 */ #define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 36, 0, 32, __val) /* Because the PCI Tx descriptors are chaied at the * initialization and all the NextDescAddresses in * these descriptors cannot not be cleared (,or * driver/HW cannot find the next descriptor), the * offset 36 (NextDescAddresses) is reserved when * the desc is cleared. */ #define TX_DESC_NEXT_DESC_OFFSET 36 #define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ memset(__pdesc, 0, min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET)) /* Rx Desc */ #define RX_STATUS_DESC_SIZE 24 #define RX_DRV_INFO_SIZE_UNIT 8 /* DWORD 0 */ #define SET_RX_STATUS_DESC_PKT_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 0, 14, __val) #define SET_RX_STATUS_DESC_CRC32(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 14, 1, __val) #define SET_RX_STATUS_DESC_ICV(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 15, 1, __val) #define SET_RX_STATUS_DESC_DRVINFO_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 16, 4, __val) #define SET_RX_STATUS_DESC_SECURITY(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 20, 3, __val) #define SET_RX_STATUS_DESC_QOS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 23, 1, __val) #define SET_RX_STATUS_DESC_SHIFT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 24, 2, __val) #define SET_RX_STATUS_DESC_PHY_STATUS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 26, 1, __val) #define SET_RX_STATUS_DESC_SWDEC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 27, 1, __val) #define SET_RX_STATUS_DESC_LAST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 28, 1, __val) #define SET_RX_STATUS_DESC_FIRST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 29, 1, __val) #define SET_RX_STATUS_DESC_EOR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) #define SET_RX_STATUS_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) #define GET_RX_STATUS_DESC_PKT_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 0, 14) #define GET_RX_STATUS_DESC_CRC32(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 14, 1) #define GET_RX_STATUS_DESC_ICV(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 15, 1) #define GET_RX_STATUS_DESC_DRVINFO_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 16, 4) #define GET_RX_STATUS_DESC_SECURITY(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 20, 3) #define GET_RX_STATUS_DESC_QOS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 23, 1) #define GET_RX_STATUS_DESC_SHIFT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 24, 2) #define GET_RX_STATUS_DESC_PHY_STATUS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 26, 1) #define GET_RX_STATUS_DESC_SWDEC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 27, 1) #define GET_RX_STATUS_DESC_LAST_SEG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 28, 1) #define GET_RX_STATUS_DESC_FIRST_SEG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 29, 1) #define GET_RX_STATUS_DESC_EOR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 30, 1) #define GET_RX_STATUS_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) /* DWORD 1 */ #define SET_RX_STATUS_DESC_MACID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 0, 5, __val) #define SET_RX_STATUS_DESC_TID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 5, 4, __val) #define SET_RX_STATUS_DESC_PAGGR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 14, 1, __val) #define SET_RX_STATUS_DESC_FAGGR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 15, 1, __val) #define SET_RX_STATUS_DESC_A1_FIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 16, 4, __val) #define SET_RX_STATUS_DESC_A2_FIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 20, 4, __val) #define SET_RX_STATUS_DESC_PAM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 24, 1, __val) #define SET_RX_STATUS_DESC_PWR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 25, 1, __val) #define SET_RX_STATUS_DESC_MOREDATA(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 26, 1, __val) #define SET_RX_STATUS_DESC_MOREFRAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 27, 1, __val) #define SET_RX_STATUS_DESC_TYPE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 28, 2, __val) #define SET_RX_STATUS_DESC_MC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 30, 1, __val) #define SET_RX_STATUS_DESC_BC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 4, 31, 1, __val) #define GET_RX_STATUS_DEC_MACID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 0, 5) #define GET_RX_STATUS_DESC_TID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 5, 4) #define GET_RX_STATUS_DESC_PAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 14, 1) #define GET_RX_STATUS_DESC_FAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 15, 1) #define GET_RX_STATUS_DESC_A1_FIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 16, 4) #define GET_RX_STATUS_DESC_A2_FIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 20, 4) #define GET_RX_STATUS_DESC_PAM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 24, 1) #define GET_RX_STATUS_DESC_PWR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 25, 1) #define GET_RX_STATUS_DESC_MORE_DATA(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 26, 1) #define GET_RX_STATUS_DESC_MORE_FRAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 27, 1) #define GET_RX_STATUS_DESC_TYPE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 28, 2) #define GET_RX_STATUS_DESC_MC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 30, 1) #define GET_RX_STATUS_DESC_BC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 4, 31, 1) /* DWORD 2 */ #define SET_RX_STATUS_DESC_SEQ(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 0, 12, __val) #define SET_RX_STATUS_DESC_FRAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 12, 4, __val) #define SET_RX_STATUS_DESC_NEXT_PKTLEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 16, 8, __val) #define SET_RX_STATUS_DESC_NEXT_IND(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 8, 30, 1, __val) #define GET_RX_STATUS_DESC_SEQ(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 8, 0, 12) #define GET_RX_STATUS_DESC_FRAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 8, 12, 4) #define GET_RX_STATUS_DESC_NEXT_PKTLEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 8, 16, 8) #define GET_RX_STATUS_DESC_NEXT_IND(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 8, 30, 1) /* DWORD 3 */ #define SET_RX_STATUS_DESC_RX_MCS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 0, 6, __val) #define SET_RX_STATUS_DESC_RX_HT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 6, 1, __val) #define SET_RX_STATUS_DESC_AMSDU(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 7, 1, __val) #define SET_RX_STATUS_DESC_SPLCP(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 8, 1, __val) #define SET_RX_STATUS_DESC_BW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 9, 1, __val) #define SET_RX_STATUS_DESC_HTC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 10, 1, __val) #define SET_RX_STATUS_DESC_TCP_CHK_RPT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 11, 1, __val) #define SET_RX_STATUS_DESC_IP_CHK_RPT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 12, 1, __val) #define SET_RX_STATUS_DESC_TCP_CHK_VALID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 13, 1, __val) #define SET_RX_STATUS_DESC_HWPC_ERR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 14, 1, __val) #define SET_RX_STATUS_DESC_HWPC_IND(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 15, 1, __val) #define SET_RX_STATUS_DESC_IV0(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 12, 16, 16, __val) #define GET_RX_STATUS_DESC_RX_MCS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 0, 6) #define GET_RX_STATUS_DESC_RX_HT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 6, 1) #define GET_RX_STATUS_DESC_AMSDU(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 7, 1) #define GET_RX_STATUS_DESC_SPLCP(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 8, 1) #define GET_RX_STATUS_DESC_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 9, 1) #define GET_RX_STATUS_DESC_HTC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 10, 1) #define GET_RX_STATUS_DESC_TCP_CHK_RPT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 11, 1) #define GET_RX_STATUS_DESC_IP_CHK_RPT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 12, 1) #define GET_RX_STATUS_DESC_TCP_CHK_VALID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 13, 1) #define GET_RX_STATUS_DESC_HWPC_ERR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 14, 1) #define GET_RX_STATUS_DESC_HWPC_IND(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 15, 1) #define GET_RX_STATUS_DESC_IV0(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 12, 16, 16) /* DWORD 4 */ #define SET_RX_STATUS_DESC_IV1(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 16, 0, 32, __val) #define GET_RX_STATUS_DESC_IV1(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 16, 0, 32) /* DWORD 5 */ #define SET_RX_STATUS_DESC_TSFL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 20, 0, 32, __val) #define GET_RX_STATUS_DESC_TSFL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc + 20, 0, 32) /* DWORD 6 */ #define SET_RX_STATUS__DESC_BUFF_ADDR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 24, 0, 32, __val) #define SE_RX_HAL_IS_CCK_RATE(_pdesc)\ (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M || \ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE2M || \ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE5_5M ||\ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE11M) enum rf_optype { RF_OP_BY_SW_3WIRE = 0, RF_OP_BY_FW, RF_OP_MAX }; enum ic_inferiority { IC_INFERIORITY_A = 0, IC_INFERIORITY_B = 1, }; enum fwcmd_iotype { /* For DIG DM */ FW_CMD_DIG_ENABLE = 0, FW_CMD_DIG_DISABLE = 1, FW_CMD_DIG_HALT = 2, FW_CMD_DIG_RESUME = 3, /* For High Power DM */ FW_CMD_HIGH_PWR_ENABLE = 4, FW_CMD_HIGH_PWR_DISABLE = 5, /* For Rate adaptive DM */ FW_CMD_RA_RESET = 6, FW_CMD_RA_ACTIVE = 7, FW_CMD_RA_REFRESH_N = 8, FW_CMD_RA_REFRESH_BG = 9, FW_CMD_RA_INIT = 10, /* For FW supported IQK */ FW_CMD_IQK_INIT = 11, /* Tx power tracking switch, * MP driver only */ FW_CMD_TXPWR_TRACK_ENABLE = 12, /* Tx power tracking switch, * MP driver only */ FW_CMD_TXPWR_TRACK_DISABLE = 13, /* Tx power tracking with thermal * indication, for Normal driver */ FW_CMD_TXPWR_TRACK_THERMAL = 14, FW_CMD_PAUSE_DM_BY_SCAN = 15, FW_CMD_RESUME_DM_BY_SCAN = 16, FW_CMD_RA_REFRESH_N_COMB = 17, FW_CMD_RA_REFRESH_BG_COMB = 18, FW_CMD_ANTENNA_SW_ENABLE = 19, FW_CMD_ANTENNA_SW_DISABLE = 20, /* Tx Status report for CCX from FW */ FW_CMD_TX_FEEDBACK_CCX_ENABLE = 21, /* Indifate firmware that driver * enters LPS, For PS-Poll issue */ FW_CMD_LPS_ENTER = 22, /* Indicate firmware that driver * leave LPS*/ FW_CMD_LPS_LEAVE = 23, /* Set DIG mode to signal strength */ FW_CMD_DIG_MODE_SS = 24, /* Set DIG mode to false alarm. */ FW_CMD_DIG_MODE_FA = 25, FW_CMD_ADD_A2_ENTRY = 26, FW_CMD_CTRL_DM_BY_DRIVER = 27, FW_CMD_CTRL_DM_BY_DRIVER_NEW = 28, FW_CMD_PAPE_CONTROL = 29, FW_CMD_IQK_ENABLE = 30, }; /* * Driver info contain PHY status * and other variabel size info * PHY Status content as below */ struct rx_fwinfo { /* DWORD 0 */ u8 gain_trsw[4]; /* DWORD 1 */ u8 pwdb_all; u8 cfosho[4]; /* DWORD 2 */ u8 cfotail[4]; /* DWORD 3 */ s8 rxevm[2]; s8 rxsnr[4]; /* DWORD 4 */ u8 pdsnr[2]; /* DWORD 5 */ u8 csi_current[2]; u8 csi_target[2]; /* DWORD 6 */ u8 sigevm; u8 max_ex_pwr; u8 ex_intf_flag:1; u8 sgi_en:1; u8 rxsc:2; u8 reserve:4; }; struct phy_sts_cck_8192s_t { u8 adc_pwdb_x[4]; u8 sq_rpt; u8 cck_agc_rpt; }; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192se/Makefile0000644000175000017500000000027412026211315025261 0ustar mcgrofmcgrofrtl8192se-objs := \ dm.o \ fw.o \ hw.o \ led.o \ phy.o \ rf.o \ sw.o \ table.o \ trx.o obj-$(CONFIG_RTL8192SE) += rtl8192se.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/0000755000175000017500000000000012026211315023616 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c0000644000175000017500000003500112026211315024412 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../core.h" #include "../usb.h" #include "../efuse.h" #include "reg.h" #include "def.h" #include "phy.h" #include "mac.h" #include "dm.h" #include "rf.h" #include "sw.h" #include "trx.h" #include "led.h" #include "hw.h" #include MODULE_AUTHOR("Georgia "); MODULE_AUTHOR("Ziv Huang "); MODULE_AUTHOR("Larry Finger "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8192C/8188C 802.11n USB wireless"); MODULE_FIRMWARE("rtlwifi/rtl8192cufw.bin"); static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); int err; rtlpriv->dm.dm_initialgain_enable = true; rtlpriv->dm.dm_flag = 0; rtlpriv->dm.disable_framebursting = false; rtlpriv->dm.thermalvalue = 0; rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug; /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(0x4000); if (!rtlpriv->rtlhal.pfirmware) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't alloc buffer for fw\n"); return 1; } pr_info("Loading firmware %s\n", rtlpriv->cfg->fw_name); rtlpriv->max_fw_size = 0x4000; err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); return 0; } static void rtl92cu_deinit_sw_vars(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->rtlhal.pfirmware) { vfree(rtlpriv->rtlhal.pfirmware); rtlpriv->rtlhal.pfirmware = NULL; } } static struct rtl_hal_ops rtl8192cu_hal_ops = { .init_sw_vars = rtl92cu_init_sw_vars, .deinit_sw_vars = rtl92cu_deinit_sw_vars, .read_chip_version = rtl92c_read_chip_version, .read_eeprom_info = rtl92cu_read_eeprom_info, .enable_interrupt = rtl92c_enable_interrupt, .disable_interrupt = rtl92c_disable_interrupt, .hw_init = rtl92cu_hw_init, .hw_disable = rtl92cu_card_disable, .set_network_type = rtl92cu_set_network_type, .set_chk_bssid = rtl92cu_set_check_bssid, .set_qos = rtl92c_set_qos, .set_bcn_reg = rtl92cu_set_beacon_related_registers, .set_bcn_intv = rtl92cu_set_beacon_interval, .update_interrupt_mask = rtl92cu_update_interrupt_mask, .get_hw_reg = rtl92cu_get_hw_reg, .set_hw_reg = rtl92cu_set_hw_reg, .update_rate_tbl = rtl92cu_update_hal_rate_table, .update_rate_mask = rtl92cu_update_hal_rate_mask, .fill_tx_desc = rtl92cu_tx_fill_desc, .fill_fake_txdesc = rtl92cu_fill_fake_txdesc, .fill_tx_cmddesc = rtl92cu_tx_fill_cmddesc, .cmd_send_packet = rtl92cu_cmd_send_packet, .query_rx_desc = rtl92cu_rx_query_desc, .set_channel_access = rtl92cu_update_channel_access_setting, .radio_onoff_checking = rtl92cu_gpio_radio_on_off_checking, .set_bw_mode = rtl92c_phy_set_bw_mode, .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, .scan_operation_backup = rtl92c_phy_scan_operation_backup, .set_rf_power_state = rtl92cu_phy_set_rf_power_state, .led_control = rtl92cu_led_control, .enable_hw_sec = rtl92cu_enable_hw_security_config, .set_key = rtl92c_set_key, .init_sw_leds = rtl92cu_init_sw_leds, .deinit_sw_leds = rtl92cu_deinit_sw_leds, .get_bbreg = rtl92c_phy_query_bb_reg, .set_bbreg = rtl92c_phy_set_bb_reg, .get_rfreg = rtl92cu_phy_query_rf_reg, .set_rfreg = rtl92cu_phy_set_rf_reg, .phy_rf6052_config = rtl92cu_phy_rf6052_config, .phy_rf6052_set_cck_txpower = rtl92cu_phy_rf6052_set_cck_txpower, .phy_rf6052_set_ofdm_txpower = rtl92cu_phy_rf6052_set_ofdm_txpower, .config_bb_with_headerfile = _rtl92cu_phy_config_bb_with_headerfile, .config_bb_with_pgheaderfile = _rtl92cu_phy_config_bb_with_pgheaderfile, .phy_lc_calibrate = _rtl92cu_phy_lc_calibrate, .phy_set_bw_mode_callback = rtl92cu_phy_set_bw_mode_callback, .dm_dynamic_txpower = rtl92cu_dm_dynamic_txpower, }; static struct rtl_mod_params rtl92cu_mod_params = { .sw_crypto = 0, .debug = DBG_EMERG, }; module_param_named(swenc, rtl92cu_mod_params.sw_crypto, bool, 0444); module_param_named(debug, rtl92cu_mod_params.debug, int, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); static struct rtl_hal_usbint_cfg rtl92cu_interface_cfg = { /* rx */ .in_ep_num = RTL92C_USB_BULK_IN_NUM, .rx_urb_num = RTL92C_NUM_RX_URBS, .rx_max_size = RTL92C_SIZE_MAX_RX_BUFFER, .usb_rx_hdl = rtl8192cu_rx_hdl, .usb_rx_segregate_hdl = NULL, /* rtl8192c_rx_segregate_hdl; */ /* tx */ .usb_tx_cleanup = rtl8192c_tx_cleanup, .usb_tx_post_hdl = rtl8192c_tx_post_hdl, .usb_tx_aggregate_hdl = rtl8192c_tx_aggregate_hdl, /* endpoint mapping */ .usb_endpoint_mapping = rtl8192cu_endpoint_mapping, .usb_mq_to_hwq = rtl8192cu_mq_to_hwq, }; static struct rtl_hal_cfg rtl92cu_hal_cfg = { .name = "rtl92c_usb", .fw_name = "rtlwifi/rtl8192cufw.bin", .ops = &rtl8192cu_hal_ops, .mod_params = &rtl92cu_mod_params, .usb_interface_cfg = &rtl92cu_interface_cfg, .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN, .maps[SYS_CLK] = REG_SYS_CLKR, .maps[MAC_RCR_AM] = AM, .maps[MAC_RCR_AB] = AB, .maps[MAC_RCR_ACRC32] = ACRC32, .maps[MAC_RCR_ACF] = ACF, .maps[MAC_RCR_AAP] = AAP, .maps[EFUSE_TEST] = REG_EFUSE_TEST, .maps[EFUSE_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_CLK] = 0, .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_PWC_EV12V] = PWC_EV12V, .maps[EFUSE_FEN_ELDR] = FEN_ELDR, .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN, .maps[EFUSE_ANA8M] = EFUSE_ANA8M, .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE, .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION, .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN, .maps[RWCAM] = REG_CAMCMD, .maps[WCAMI] = REG_CAMWRITE, .maps[RCAMO] = REG_CAMREAD, .maps[CAMDBG] = REG_CAMDBG, .maps[SECR] = REG_SECCFG, .maps[SEC_CAM_NONE] = CAM_NONE, .maps[SEC_CAM_WEP40] = CAM_WEP40, .maps[SEC_CAM_TKIP] = CAM_TKIP, .maps[SEC_CAM_AES] = CAM_AES, .maps[SEC_CAM_WEP104] = CAM_WEP104, .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6, .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5, .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4, .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3, .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2, .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1, .maps[RTL_IMR_BCNDOK8] = IMR_BCNDOK8, .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7, .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6, .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5, .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4, .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3, .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2, .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1, .maps[RTL_IMR_TIMEOUT2] = IMR_TIMEOUT2, .maps[RTL_IMR_TIMEOUT1] = IMR_TIMEOUT1, .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW, .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT, .maps[RTL_IMR_BcnInt] = IMR_BCNINT, .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW, .maps[RTL_IMR_RDU] = IMR_RDU, .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND, .maps[RTL_IMR_BDOK] = IMR_BDOK, .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK, .maps[RTL_IMR_TBDER] = IMR_TBDER, .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK, .maps[RTL_IMR_TBDOK] = IMR_TBDOK, .maps[RTL_IMR_BKDOK] = IMR_BKDOK, .maps[RTL_IMR_BEDOK] = IMR_BEDOK, .maps[RTL_IMR_VIDOK] = IMR_VIDOK, .maps[RTL_IMR_VODOK] = IMR_VODOK, .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, }; #define USB_VENDER_ID_REALTEK 0x0bda /* 2010-10-19 DID_USB_V3.4 */ static struct usb_device_id rtl8192c_usb_ids[] = { /*=== Realtek demoboard ===*/ /* Default ID */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8191, rtl92cu_hal_cfg)}, /****** 8188CU ********/ /* RTL8188CTV */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x018a, rtl92cu_hal_cfg)}, /* 8188CE-VAU USB minCard */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8170, rtl92cu_hal_cfg)}, /* 8188cu 1*1 dongle */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8176, rtl92cu_hal_cfg)}, /* 8188cu 1*1 dongle, (b/g mode only) */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8177, rtl92cu_hal_cfg)}, /* 8188cu Slim Solo */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817a, rtl92cu_hal_cfg)}, /* 8188cu Slim Combo */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817b, rtl92cu_hal_cfg)}, /* 8188RU High-power USB Dongle */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817d, rtl92cu_hal_cfg)}, /* 8188CE-VAU USB minCard (b/g mode only) */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817e, rtl92cu_hal_cfg)}, /* 8188RU in Alfa AWUS036NHR */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817f, rtl92cu_hal_cfg)}, /* RTL8188CUS-VL */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x818a, rtl92cu_hal_cfg)}, /* 8188 Combo for BC4 */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8754, rtl92cu_hal_cfg)}, /****** 8192CU ********/ /* 8192cu 2*2 */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8178, rtl92cu_hal_cfg)}, /* 8192CE-VAU USB minCard */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817c, rtl92cu_hal_cfg)}, /*=== Customer ID ===*/ /****** 8188CU ********/ {RTL_USB_DEVICE(0x050d, 0x1102, rtl92cu_hal_cfg)}, /*Belkin - Edimax*/ {RTL_USB_DEVICE(0x06f8, 0xe033, rtl92cu_hal_cfg)}, /*Hercules - Edimax*/ {RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/ {RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/ {RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/ {RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/ {RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0eb0, 0x9071, rtl92cu_hal_cfg)}, /*NO Brand - Etop*/ {RTL_USB_DEVICE(0x4856, 0x0091, rtl92cu_hal_cfg)}, /*NetweeN - Feixun*/ /* HP - Lite-On ,8188CUS Slim Combo */ {RTL_USB_DEVICE(0x103c, 0x1629, rtl92cu_hal_cfg)}, {RTL_USB_DEVICE(0x13d3, 0x3357, rtl92cu_hal_cfg)}, /* AzureWave */ {RTL_USB_DEVICE(0x2001, 0x3308, rtl92cu_hal_cfg)}, /*D-Link - Alpha*/ {RTL_USB_DEVICE(0x2019, 0x4902, rtl92cu_hal_cfg)}, /*Planex - Etop*/ {RTL_USB_DEVICE(0x2019, 0xab2a, rtl92cu_hal_cfg)}, /*Planex - Abocom*/ /*SW-WF02-AD15 -Abocom*/ {RTL_USB_DEVICE(0x2019, 0xab2e, rtl92cu_hal_cfg)}, {RTL_USB_DEVICE(0x2019, 0xed17, rtl92cu_hal_cfg)}, /*PCI - Edimax*/ {RTL_USB_DEVICE(0x20f4, 0x648b, rtl92cu_hal_cfg)}, /*TRENDnet - Cameo*/ {RTL_USB_DEVICE(0x7392, 0x7811, rtl92cu_hal_cfg)}, /*Edimax - Edimax*/ {RTL_USB_DEVICE(0x13d3, 0x3358, rtl92cu_hal_cfg)}, /*Azwave 8188CE-VAU*/ /* Russian customer -Azwave (8188CE-VAU b/g mode only) */ {RTL_USB_DEVICE(0x13d3, 0x3359, rtl92cu_hal_cfg)}, {RTL_USB_DEVICE(0x4855, 0x0090, rtl92cu_hal_cfg)}, /* Feixun */ {RTL_USB_DEVICE(0x4855, 0x0091, rtl92cu_hal_cfg)}, /* NetweeN-Feixun */ {RTL_USB_DEVICE(0x9846, 0x9041, rtl92cu_hal_cfg)}, /* Netgear Cameo */ /****** 8188 RU ********/ /* Netcore */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x317f, rtl92cu_hal_cfg)}, /****** 8188CUS Slim Solo********/ {RTL_USB_DEVICE(0x04f2, 0xaff7, rtl92cu_hal_cfg)}, /*Xavi*/ {RTL_USB_DEVICE(0x04f2, 0xaff9, rtl92cu_hal_cfg)}, /*Xavi*/ {RTL_USB_DEVICE(0x04f2, 0xaffa, rtl92cu_hal_cfg)}, /*Xavi*/ /****** 8188CUS Slim Combo ********/ {RTL_USB_DEVICE(0x04f2, 0xaff8, rtl92cu_hal_cfg)}, /*Xavi*/ {RTL_USB_DEVICE(0x04f2, 0xaffb, rtl92cu_hal_cfg)}, /*Xavi*/ {RTL_USB_DEVICE(0x04f2, 0xaffc, rtl92cu_hal_cfg)}, /*Xavi*/ {RTL_USB_DEVICE(0x2019, 0x1201, rtl92cu_hal_cfg)}, /*Planex-Vencer*/ /****** 8192CU ********/ {RTL_USB_DEVICE(0x050d, 0x1004, rtl92cu_hal_cfg)}, /*Belcom-SurfN300*/ {RTL_USB_DEVICE(0x050d, 0x2102, rtl92cu_hal_cfg)}, /*Belcom-Sercomm*/ {RTL_USB_DEVICE(0x050d, 0x2103, rtl92cu_hal_cfg)}, /*Belcom-Edimax*/ {RTL_USB_DEVICE(0x0586, 0x341f, rtl92cu_hal_cfg)}, /*Zyxel -Abocom*/ {RTL_USB_DEVICE(0x07aa, 0x0056, rtl92cu_hal_cfg)}, /*ATKK-Gemtek*/ {RTL_USB_DEVICE(0x07b8, 0x8178, rtl92cu_hal_cfg)}, /*Funai -Abocom*/ {RTL_USB_DEVICE(0x0846, 0x9021, rtl92cu_hal_cfg)}, /*Netgear-Sercomm*/ {RTL_USB_DEVICE(0x0b05, 0x17ab, rtl92cu_hal_cfg)}, /*ASUS-Edimax*/ {RTL_USB_DEVICE(0x0bda, 0x8186, rtl92cu_hal_cfg)}, /*Realtek 92CE-VAU*/ {RTL_USB_DEVICE(0x0df6, 0x0061, rtl92cu_hal_cfg)}, /*Sitecom-Edimax*/ {RTL_USB_DEVICE(0x0e66, 0x0019, rtl92cu_hal_cfg)}, /*Hawking-Edimax*/ {RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/ {RTL_USB_DEVICE(0x2001, 0x3309, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/ {RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/ {RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/ {RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/ {RTL_USB_DEVICE(0x7392, 0x7822, rtl92cu_hal_cfg)}, /*Edimax -Edimax*/ {} }; MODULE_DEVICE_TABLE(usb, rtl8192c_usb_ids); static struct usb_driver rtl8192cu_driver = { .name = "rtl8192cu", .probe = rtl_usb_probe, .disconnect = rtl_usb_disconnect, .id_table = rtl8192c_usb_ids, #ifdef CONFIG_PM /* .suspend = rtl_usb_suspend, */ /* .resume = rtl_usb_resume, */ /* .reset_resume = rtl8192c_resume, */ #endif /* CONFIG_PM */ #ifdef CONFIG_AUTOSUSPEND .supports_autosuspend = 1, #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(rtl8192cu_driver); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h0000644000175000017500000004020412026211315024604 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CU_TRX_H__ #define __RTL92CU_TRX_H__ #define RTL92C_USB_BULK_IN_NUM 1 #define RTL92C_NUM_RX_URBS 8 #define RTL92C_NUM_TX_URBS 32 #define RTL92C_SIZE_MAX_RX_BUFFER 15360 /* 8192 */ #define RX_DRV_INFO_SIZE_UNIT 8 #define RTL_AGG_ON 1 enum usb_rx_agg_mode { USB_RX_AGG_DISABLE, USB_RX_AGG_DMA, USB_RX_AGG_USB, USB_RX_AGG_DMA_USB }; #define TX_SELE_HQ BIT(0) /* High Queue */ #define TX_SELE_LQ BIT(1) /* Low Queue */ #define TX_SELE_NQ BIT(2) /* Normal Queue */ #define RTL_USB_TX_AGG_NUM_DESC 5 #define RTL_USB_RX_AGG_PAGE_NUM 4 #define RTL_USB_RX_AGG_PAGE_TIMEOUT 3 #define RTL_USB_RX_AGG_BLOCK_NUM 5 #define RTL_USB_RX_AGG_BLOCK_TIMEOUT 3 /*======================== rx status =========================================*/ struct rx_drv_info_92c { /* * Driver info contain PHY status and other variabel size info * PHY Status content as below */ /* DWORD 0 */ u8 gain_trsw[4]; /* DWORD 1 */ u8 pwdb_all; u8 cfosho[4]; /* DWORD 2 */ u8 cfotail[4]; /* DWORD 3 */ s8 rxevm[2]; s8 rxsnr[4]; /* DWORD 4 */ u8 pdsnr[2]; /* DWORD 5 */ u8 csi_current[2]; u8 csi_target[2]; /* DWORD 6 */ u8 sigevm; u8 max_ex_pwr; u8 ex_intf_flag:1; u8 sgi_en:1; u8 rxsc:2; u8 reserve:4; } __packed; /* Define a macro that takes a le32 word, converts it to host ordering, * right shifts by a specified count, creates a mask of the specified * bit count, and extracts that number of bits. */ #define SHIFT_AND_MASK_LE(__pdesc, __shift, __bits) \ ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \ BIT_LEN_MASK_32(__bits)) /* Define a macro that clears a bit field in an le32 word and * sets the specified value into that bit field. The resulting * value remains in le32 ordering; however, it is properly converted * to host ordering for the clear and set operations before conversion * back to le32. */ #define SET_BITS_OFFSET_LE(__pdesc, __shift, __len, __val) \ (*(__le32 *)(__pdesc) = \ (cpu_to_le32((le32_to_cpu(*((__le32 *)(__pdesc))) & \ (~(BIT_OFFSET_LEN_MASK_32((__shift), __len)))) | \ (((u32)(__val) & BIT_LEN_MASK_32(__len)) << (__shift))))); /* macros to read various fields in RX descriptor */ /* DWORD 0 */ #define GET_RX_DESC_PKT_LEN(__rxdesc) \ SHIFT_AND_MASK_LE((__rxdesc), 0, 14) #define GET_RX_DESC_CRC32(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 14, 1) #define GET_RX_DESC_ICV(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 15, 1) #define GET_RX_DESC_DRVINFO_SIZE(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 16, 4) #define GET_RX_DESC_SECURITY(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 20, 3) #define GET_RX_DESC_QOS(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 23, 1) #define GET_RX_DESC_SHIFT(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 24, 2) #define GET_RX_DESC_PHY_STATUS(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 26, 1) #define GET_RX_DESC_SWDEC(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 27, 1) #define GET_RX_DESC_LAST_SEG(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 28, 1) #define GET_RX_DESC_FIRST_SEG(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 29, 1) #define GET_RX_DESC_EOR(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 30, 1) #define GET_RX_DESC_OWN(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc, 31, 1) /* DWORD 1 */ #define GET_RX_DESC_MACID(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 0, 5) #define GET_RX_DESC_TID(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 5, 4) #define GET_RX_DESC_PAGGR(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 14, 1) #define GET_RX_DESC_FAGGR(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 15, 1) #define GET_RX_DESC_A1_FIT(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 16, 4) #define GET_RX_DESC_A2_FIT(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 20, 4) #define GET_RX_DESC_PAM(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 24, 1) #define GET_RX_DESC_PWR(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 25, 1) #define GET_RX_DESC_MORE_DATA(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 26, 1) #define GET_RX_DESC_MORE_FRAG(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 27, 1) #define GET_RX_DESC_TYPE(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 28, 2) #define GET_RX_DESC_MC(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 30, 1) #define GET_RX_DESC_BC(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+4, 31, 1) /* DWORD 2 */ #define GET_RX_DESC_SEQ(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+8, 0, 12) #define GET_RX_DESC_FRAG(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+8, 12, 4) #define GET_RX_DESC_USB_AGG_PKTNUM(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+8, 16, 8) #define GET_RX_DESC_NEXT_IND(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+8, 30, 1) /* DWORD 3 */ #define GET_RX_DESC_RX_MCS(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 0, 6) #define GET_RX_DESC_RX_HT(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 6, 1) #define GET_RX_DESC_AMSDU(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 7, 1) #define GET_RX_DESC_SPLCP(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 8, 1) #define GET_RX_DESC_BW(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 9, 1) #define GET_RX_DESC_HTC(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 10, 1) #define GET_RX_DESC_TCP_CHK_RPT(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 11, 1) #define GET_RX_DESC_IP_CHK_RPT(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 12, 1) #define GET_RX_DESC_TCP_CHK_VALID(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 13, 1) #define GET_RX_DESC_HWPC_ERR(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 14, 1) #define GET_RX_DESC_HWPC_IND(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 15, 1) #define GET_RX_DESC_IV0(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+12, 16, 16) /* DWORD 4 */ #define GET_RX_DESC_IV1(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+16, 0, 32) /* DWORD 5 */ #define GET_RX_DESC_TSFL(__rxdesc) \ SHIFT_AND_MASK_LE(__rxdesc+20, 0, 32) /*======================= tx desc ============================================*/ /* macros to set various fields in TX descriptor */ /* Dword 0 */ #define SET_TX_DESC_PKT_SIZE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 0, 16, __value) #define SET_TX_DESC_OFFSET(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 16, 8, __value) #define SET_TX_DESC_BMC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 24, 1, __value) #define SET_TX_DESC_HTC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 25, 1, __value) #define SET_TX_DESC_LAST_SEG(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 26, 1, __value) #define SET_TX_DESC_FIRST_SEG(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 27, 1, __value) #define SET_TX_DESC_LINIP(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 28, 1, __value) #define SET_TX_DESC_NO_ACM(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 29, 1, __value) #define SET_TX_DESC_GF(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 30, 1, __value) #define SET_TX_DESC_OWN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc, 31, 1, __value) /* Dword 1 */ #define SET_TX_DESC_MACID(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 0, 5, __value) #define SET_TX_DESC_AGG_ENABLE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 5, 1, __value) #define SET_TX_DESC_AGG_BREAK(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 6, 1, __value) #define SET_TX_DESC_RDG_ENABLE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 7, 1, __value) #define SET_TX_DESC_QUEUE_SEL(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 8, 5, __value) #define SET_TX_DESC_RDG_NAV_EXT(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 13, 1, __value) #define SET_TX_DESC_LSIG_TXOP_EN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 14, 1, __value) #define SET_TX_DESC_PIFS(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 15, 1, __value) #define SET_TX_DESC_RATE_ID(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 16, 4, __value) #define SET_TX_DESC_RA_BRSR_ID(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 16, 4, __value) #define SET_TX_DESC_NAV_USE_HDR(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 20, 1, __value) #define SET_TX_DESC_EN_DESC_ID(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 21, 1, __value) #define SET_TX_DESC_SEC_TYPE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 22, 2, __value) #define SET_TX_DESC_PKT_OFFSET(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+4, 26, 5, __value) /* Dword 2 */ #define SET_TX_DESC_RTS_RC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 0, 6, __value) #define SET_TX_DESC_DATA_RC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 6, 6, __value) #define SET_TX_DESC_BAR_RTY_TH(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 14, 2, __value) #define SET_TX_DESC_MORE_FRAG(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 17, 1, __value) #define SET_TX_DESC_RAW(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 18, 1, __value) #define SET_TX_DESC_CCX(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 19, 1, __value) #define SET_TX_DESC_AMPDU_DENSITY(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 20, 3, __value) #define SET_TX_DESC_ANTSEL_A(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 24, 1, __value) #define SET_TX_DESC_ANTSEL_B(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 25, 1, __value) #define SET_TX_DESC_TX_ANT_CCK(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 26, 2, __value) #define SET_TX_DESC_TX_ANTL(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 28, 2, __value) #define SET_TX_DESC_TX_ANT_HT(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+8, 30, 2, __value) /* Dword 3 */ #define SET_TX_DESC_NEXT_HEAP_PAGE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+12, 0, 8, __value) #define SET_TX_DESC_TAIL_PAGE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+12, 8, 8, __value) #define SET_TX_DESC_SEQ(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+12, 16, 12, __value) #define SET_TX_DESC_PKT_ID(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+12, 28, 4, __value) /* Dword 4 */ #define SET_TX_DESC_RTS_RATE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 0, 5, __value) #define SET_TX_DESC_AP_DCFE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 5, 1, __value) #define SET_TX_DESC_QOS(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 6, 1, __value) #define SET_TX_DESC_HWSEQ_EN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 7, 1, __value) #define SET_TX_DESC_USE_RATE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 8, 1, __value) #define SET_TX_DESC_DISABLE_RTS_FB(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 9, 1, __value) #define SET_TX_DESC_DISABLE_FB(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 10, 1, __value) #define SET_TX_DESC_CTS2SELF(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 11, 1, __value) #define SET_TX_DESC_RTS_ENABLE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 12, 1, __value) #define SET_TX_DESC_HW_RTS_ENABLE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 13, 1, __value) #define SET_TX_DESC_WAIT_DCTS(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 18, 1, __value) #define SET_TX_DESC_CTS2AP_EN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 19, 1, __value) #define SET_TX_DESC_DATA_SC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 20, 2, __value) #define SET_TX_DESC_DATA_STBC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 22, 2, __value) #define SET_TX_DESC_DATA_SHORT(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 24, 1, __value) #define SET_TX_DESC_DATA_BW(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 25, 1, __value) #define SET_TX_DESC_RTS_SHORT(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 26, 1, __value) #define SET_TX_DESC_RTS_BW(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 27, 1, __value) #define SET_TX_DESC_RTS_SC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 28, 2, __value) #define SET_TX_DESC_RTS_STBC(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+16, 30, 2, __value) /* Dword 5 */ #define SET_TX_DESC_TX_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 0, 6, __val) #define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 6, 1, __val) #define SET_TX_DESC_CCX_TAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 7, 1, __val) #define SET_TX_DESC_DATA_RATE_FB_LIMIT(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+20, 8, 5, __value) #define SET_TX_DESC_RTS_RATE_FB_LIMIT(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+20, 13, 4, __value) #define SET_TX_DESC_RETRY_LIMIT_ENABLE(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+20, 17, 1, __value) #define SET_TX_DESC_DATA_RETRY_LIMIT(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+20, 18, 6, __value) #define SET_TX_DESC_USB_TXAGG_NUM(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+20, 24, 8, __value) /* Dword 6 */ #define SET_TX_DESC_TXAGC_A(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 0, 5, __value) #define SET_TX_DESC_TXAGC_B(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 5, 5, __value) #define SET_TX_DESC_USB_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 10, 1, __value) #define SET_TX_DESC_MAX_AGG_NUM(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 11, 5, __value) #define SET_TX_DESC_MCSG1_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 16, 4, __value) #define SET_TX_DESC_MCSG2_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 20, 4, __value) #define SET_TX_DESC_MCSG3_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 24, 4, __value) #define SET_TX_DESC_MCSG7_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+24, 28, 4, __value) /* Dword 7 */ #define SET_TX_DESC_TX_DESC_CHECKSUM(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+28, 0, 16, __value) #define SET_TX_DESC_MCSG4_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+28, 16, 4, __value) #define SET_TX_DESC_MCSG5_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+28, 20, 4, __value) #define SET_TX_DESC_MCSG6_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+28, 24, 4, __value) #define SET_TX_DESC_MCSG15_MAX_LEN(__txdesc, __value) \ SET_BITS_OFFSET_LE(__txdesc+28, 28, 4, __value) int rtl8192cu_endpoint_mapping(struct ieee80211_hw *hw); u16 rtl8192cu_mq_to_hwq(__le16 fc, u16 mac80211_queue_index); bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *p_desc, struct sk_buff *skb); void rtl8192cu_rx_hdl(struct ieee80211_hw *hw, struct sk_buff * skb); void rtl8192c_rx_segregate_hdl(struct ieee80211_hw *, struct sk_buff *, struct sk_buff_head *); void rtl8192c_tx_cleanup(struct ieee80211_hw *hw, struct sk_buff *skb); int rtl8192c_tx_post_hdl(struct ieee80211_hw *hw, struct urb *urb, struct sk_buff *skb); struct sk_buff *rtl8192c_tx_aggregate_hdl(struct ieee80211_hw *, struct sk_buff_head *); void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 queue_index, struct rtl_tcb_desc *tcb_desc); void rtl92cu_fill_fake_txdesc(struct ieee80211_hw *hw, u8 * pDesc, u32 buffer_len, bool bIsPsPoll); void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool b_firstseg, bool b_lastseg, struct sk_buff *skb); bool rtl92cu_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c0000644000175000017500000005171412026211315024607 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../usb.h" #include "../ps.h" #include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "mac.h" #include "trx.h" static int _ConfigVerTOutEP(struct ieee80211_hw *hw) { u8 ep_cfg, txqsele; u8 ep_nums = 0; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); rtlusb->out_queue_sel = 0; ep_cfg = rtl_read_byte(rtlpriv, REG_TEST_SIE_OPTIONAL); ep_cfg = (ep_cfg & USB_TEST_EP_MASK) >> USB_TEST_EP_SHIFT; switch (ep_cfg) { case 0: /* 2 bulk OUT, 1 bulk IN */ case 3: rtlusb->out_queue_sel = TX_SELE_HQ | TX_SELE_LQ; ep_nums = 2; break; case 1: /* 1 bulk IN/OUT => map all endpoint to Low queue */ case 2: /* 1 bulk IN, 1 bulk OUT => map all endpoint to High queue */ txqsele = rtl_read_byte(rtlpriv, REG_TEST_USB_TXQS); if (txqsele & 0x0F) /* /map all endpoint to High queue */ rtlusb->out_queue_sel = TX_SELE_HQ; else if (txqsele&0xF0) /* map all endpoint to Low queue */ rtlusb->out_queue_sel = TX_SELE_LQ; ep_nums = 1; break; default: break; } return (rtlusb->out_ep_nums == ep_nums) ? 0 : -EINVAL; } static int _ConfigVerNOutEP(struct ieee80211_hw *hw) { u8 ep_cfg; u8 ep_nums = 0; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); rtlusb->out_queue_sel = 0; /* Normal and High queue */ ep_cfg = rtl_read_byte(rtlpriv, (REG_NORMAL_SIE_EP + 1)); if (ep_cfg & USB_NORMAL_SIE_EP_MASK) { rtlusb->out_queue_sel |= TX_SELE_HQ; ep_nums++; } if ((ep_cfg >> USB_NORMAL_SIE_EP_SHIFT) & USB_NORMAL_SIE_EP_MASK) { rtlusb->out_queue_sel |= TX_SELE_NQ; ep_nums++; } /* Low queue */ ep_cfg = rtl_read_byte(rtlpriv, (REG_NORMAL_SIE_EP + 2)); if (ep_cfg & USB_NORMAL_SIE_EP_MASK) { rtlusb->out_queue_sel |= TX_SELE_LQ; ep_nums++; } return (rtlusb->out_ep_nums == ep_nums) ? 0 : -EINVAL; } static void _TwoOutEpMapping(struct ieee80211_hw *hw, bool bIsChipB, bool bwificfg, struct rtl_ep_map *ep_map) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (bwificfg) { /* for WMM */ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "USB Chip-B & WMM Setting.....\n"); ep_map->ep_mapping[RTL_TXQ_BE] = 2; ep_map->ep_mapping[RTL_TXQ_BK] = 3; ep_map->ep_mapping[RTL_TXQ_VI] = 3; ep_map->ep_mapping[RTL_TXQ_VO] = 2; ep_map->ep_mapping[RTL_TXQ_MGT] = 2; ep_map->ep_mapping[RTL_TXQ_BCN] = 2; ep_map->ep_mapping[RTL_TXQ_HI] = 2; } else { /* typical setting */ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "USB typical Setting.....\n"); ep_map->ep_mapping[RTL_TXQ_BE] = 3; ep_map->ep_mapping[RTL_TXQ_BK] = 3; ep_map->ep_mapping[RTL_TXQ_VI] = 2; ep_map->ep_mapping[RTL_TXQ_VO] = 2; ep_map->ep_mapping[RTL_TXQ_MGT] = 2; ep_map->ep_mapping[RTL_TXQ_BCN] = 2; ep_map->ep_mapping[RTL_TXQ_HI] = 2; } } static void _ThreeOutEpMapping(struct ieee80211_hw *hw, bool bwificfg, struct rtl_ep_map *ep_map) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (bwificfg) { /* for WMM */ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "USB 3EP Setting for WMM.....\n"); ep_map->ep_mapping[RTL_TXQ_BE] = 5; ep_map->ep_mapping[RTL_TXQ_BK] = 3; ep_map->ep_mapping[RTL_TXQ_VI] = 3; ep_map->ep_mapping[RTL_TXQ_VO] = 2; ep_map->ep_mapping[RTL_TXQ_MGT] = 2; ep_map->ep_mapping[RTL_TXQ_BCN] = 2; ep_map->ep_mapping[RTL_TXQ_HI] = 2; } else { /* typical setting */ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "USB 3EP Setting for typical.....\n"); ep_map->ep_mapping[RTL_TXQ_BE] = 5; ep_map->ep_mapping[RTL_TXQ_BK] = 5; ep_map->ep_mapping[RTL_TXQ_VI] = 3; ep_map->ep_mapping[RTL_TXQ_VO] = 2; ep_map->ep_mapping[RTL_TXQ_MGT] = 2; ep_map->ep_mapping[RTL_TXQ_BCN] = 2; ep_map->ep_mapping[RTL_TXQ_HI] = 2; } } static void _OneOutEpMapping(struct ieee80211_hw *hw, struct rtl_ep_map *ep_map) { ep_map->ep_mapping[RTL_TXQ_BE] = 2; ep_map->ep_mapping[RTL_TXQ_BK] = 2; ep_map->ep_mapping[RTL_TXQ_VI] = 2; ep_map->ep_mapping[RTL_TXQ_VO] = 2; ep_map->ep_mapping[RTL_TXQ_MGT] = 2; ep_map->ep_mapping[RTL_TXQ_BCN] = 2; ep_map->ep_mapping[RTL_TXQ_HI] = 2; } static int _out_ep_mapping(struct ieee80211_hw *hw) { int err = 0; bool bIsChipN, bwificfg = false; struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); struct rtl_ep_map *ep_map = &(rtlusb->ep_map); bIsChipN = IS_NORMAL_CHIP(rtlhal->version); switch (rtlusb->out_ep_nums) { case 2: _TwoOutEpMapping(hw, bIsChipN, bwificfg, ep_map); break; case 3: /* Test chip doesn't support three out EPs. */ if (!bIsChipN) { err = -EINVAL; goto err_out; } _ThreeOutEpMapping(hw, bIsChipN, ep_map); break; case 1: _OneOutEpMapping(hw, ep_map); break; default: err = -EINVAL; break; } err_out: return err; } /* endpoint mapping */ int rtl8192cu_endpoint_mapping(struct ieee80211_hw *hw) { struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); int error = 0; if (likely(IS_NORMAL_CHIP(rtlhal->version))) error = _ConfigVerNOutEP(hw); else error = _ConfigVerTOutEP(hw); if (error) goto err_out; error = _out_ep_mapping(hw); if (error) goto err_out; err_out: return error; } u16 rtl8192cu_mq_to_hwq(__le16 fc, u16 mac80211_queue_index) { u16 hw_queue_index; if (unlikely(ieee80211_is_beacon(fc))) { hw_queue_index = RTL_TXQ_BCN; goto out; } if (ieee80211_is_mgmt(fc)) { hw_queue_index = RTL_TXQ_MGT; goto out; } switch (mac80211_queue_index) { case 0: hw_queue_index = RTL_TXQ_VO; break; case 1: hw_queue_index = RTL_TXQ_VI; break; case 2: hw_queue_index = RTL_TXQ_BE; break; case 3: hw_queue_index = RTL_TXQ_BK; break; default: hw_queue_index = RTL_TXQ_BE; RT_ASSERT(false, "QSLT_BE queue, skb_queue:%d\n", mac80211_queue_index); break; } out: return hw_queue_index; } static enum rtl_desc_qsel _rtl8192cu_mq_to_descq(struct ieee80211_hw *hw, __le16 fc, u16 mac80211_queue_index) { enum rtl_desc_qsel qsel; struct rtl_priv *rtlpriv = rtl_priv(hw); if (unlikely(ieee80211_is_beacon(fc))) { qsel = QSLT_BEACON; goto out; } if (ieee80211_is_mgmt(fc)) { qsel = QSLT_MGNT; goto out; } switch (mac80211_queue_index) { case 0: /* VO */ qsel = QSLT_VO; RT_TRACE(rtlpriv, COMP_USB, DBG_DMESG, "VO queue, set qsel = 0x%x\n", QSLT_VO); break; case 1: /* VI */ qsel = QSLT_VI; RT_TRACE(rtlpriv, COMP_USB, DBG_DMESG, "VI queue, set qsel = 0x%x\n", QSLT_VI); break; case 3: /* BK */ qsel = QSLT_BK; RT_TRACE(rtlpriv, COMP_USB, DBG_DMESG, "BK queue, set qsel = 0x%x\n", QSLT_BK); break; case 2: /* BE */ default: qsel = QSLT_BE; RT_TRACE(rtlpriv, COMP_USB, DBG_DMESG, "BE queue, set qsel = 0x%x\n", QSLT_BE); break; } out: return qsel; } /* =============================================================== */ /*---------------------------------------------------------------------- * * Rx handler * *---------------------------------------------------------------------- */ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *p_desc, struct sk_buff *skb) { struct rx_fwinfo_92c *p_drvinfo; struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc; u32 phystatus = GET_RX_DESC_PHY_STATUS(pdesc); stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc); stats->rx_drvinfo_size = (u8)GET_RX_DESC_DRVINFO_SIZE(pdesc) * RX_DRV_INFO_SIZE_UNIT; stats->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03); stats->icv = (u16) GET_RX_DESC_ICV(pdesc); stats->crc = (u16) GET_RX_DESC_CRC32(pdesc); stats->hwerror = (stats->crc | stats->icv); stats->decrypted = !GET_RX_DESC_SWDEC(pdesc); stats->rate = (u8) GET_RX_DESC_RX_MCS(pdesc); stats->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc); stats->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1); stats->isampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1) && (GET_RX_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); rx_status->freq = hw->conf.channel->center_freq; rx_status->band = hw->conf.channel->band; if (GET_RX_DESC_CRC32(pdesc)) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (!GET_RX_DESC_SWDEC(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(pdesc)) rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RX_HT(pdesc)) rx_status->flag |= RX_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, (bool)GET_RX_DESC_RX_HT(pdesc), (u8)GET_RX_DESC_RX_MCS(pdesc), (bool)GET_RX_DESC_PAGGR(pdesc)); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92c *)(pdesc + RTL_RX_DESC_SIZE); rtl92c_translate_rx_signal_stuff(hw, skb, stats, pdesc, p_drvinfo); } /*rx_status->qual = stats->signal; */ rx_status->signal = stats->rssi + 10; /*rx_status->noise = -stats->noise; */ return true; } #define RTL_RX_DRV_INFO_UNIT 8 static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status = (struct ieee80211_rx_status *)IEEE80211_SKB_RXCB(skb); u32 skb_len, pkt_len, drvinfo_len; struct rtl_priv *rtlpriv = rtl_priv(hw); u8 *rxdesc; struct rtl_stats stats = { .signal = 0, .noise = -98, .rate = 0, }; struct rx_fwinfo_92c *p_drvinfo; bool bv; __le16 fc; struct ieee80211_hdr *hdr; memset(rx_status, 0, sizeof(*rx_status)); rxdesc = skb->data; skb_len = skb->len; drvinfo_len = (GET_RX_DESC_DRVINFO_SIZE(rxdesc) * RTL_RX_DRV_INFO_UNIT); pkt_len = GET_RX_DESC_PKT_LEN(rxdesc); /* TODO: Error recovery. drop this skb or something. */ WARN_ON(skb_len < (pkt_len + RTL_RX_DESC_SIZE + drvinfo_len)); stats.length = (u16) GET_RX_DESC_PKT_LEN(rxdesc); stats.rx_drvinfo_size = (u8)GET_RX_DESC_DRVINFO_SIZE(rxdesc) * RX_DRV_INFO_SIZE_UNIT; stats.rx_bufshift = (u8) (GET_RX_DESC_SHIFT(rxdesc) & 0x03); stats.icv = (u16) GET_RX_DESC_ICV(rxdesc); stats.crc = (u16) GET_RX_DESC_CRC32(rxdesc); stats.hwerror = (stats.crc | stats.icv); stats.decrypted = !GET_RX_DESC_SWDEC(rxdesc); stats.rate = (u8) GET_RX_DESC_RX_MCS(rxdesc); stats.shortpreamble = (u16) GET_RX_DESC_SPLCP(rxdesc); stats.isampdu = (bool) ((GET_RX_DESC_PAGGR(rxdesc) == 1) && (GET_RX_DESC_FAGGR(rxdesc) == 1)); stats.timestamp_low = GET_RX_DESC_TSFL(rxdesc); stats.rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(rxdesc); /* TODO: is center_freq changed when doing scan? */ /* TODO: Shall we add protection or just skip those two step? */ rx_status->freq = hw->conf.channel->center_freq; rx_status->band = hw->conf.channel->band; if (GET_RX_DESC_CRC32(rxdesc)) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (!GET_RX_DESC_SWDEC(rxdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(rxdesc)) rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RX_HT(rxdesc)) rx_status->flag |= RX_FLAG_HT; /* Data rate */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, (bool)GET_RX_DESC_RX_HT(rxdesc), (u8)GET_RX_DESC_RX_MCS(rxdesc), (bool)GET_RX_DESC_PAGGR(rxdesc)); /* There is a phy status after this rx descriptor. */ if (GET_RX_DESC_PHY_STATUS(rxdesc)) { p_drvinfo = (struct rx_fwinfo_92c *)(rxdesc + RTL_RX_DESC_SIZE); rtl92c_translate_rx_signal_stuff(hw, skb, &stats, (struct rx_desc_92c *)rxdesc, p_drvinfo); } skb_pull(skb, (drvinfo_len + RTL_RX_DESC_SIZE)); hdr = (struct ieee80211_hdr *)(skb->data); fc = hdr->frame_control; bv = ieee80211_is_probe_resp(fc); if (bv) RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Got probe response frame\n"); if (ieee80211_is_beacon(fc)) RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Got beacon frame\n"); if (ieee80211_is_data(fc)) RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Got data frame\n"); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Fram: fc = 0x%X addr1 = 0x%02X:0x%02X:0x%02X:0x%02X:0x%02X:0x%02X\n", fc, (u32)hdr->addr1[0], (u32)hdr->addr1[1], (u32)hdr->addr1[2], (u32)hdr->addr1[3], (u32)hdr->addr1[4], (u32)hdr->addr1[5]); memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status)); ieee80211_rx_irqsafe(hw, skb); } void rtl8192cu_rx_hdl(struct ieee80211_hw *hw, struct sk_buff * skb) { _rtl_rx_process(hw, skb); } void rtl8192c_rx_segregate_hdl( struct ieee80211_hw *hw, struct sk_buff *skb, struct sk_buff_head *skb_list) { } /*---------------------------------------------------------------------- * * Tx handler * *---------------------------------------------------------------------- */ void rtl8192c_tx_cleanup(struct ieee80211_hw *hw, struct sk_buff *skb) { } int rtl8192c_tx_post_hdl(struct ieee80211_hw *hw, struct urb *urb, struct sk_buff *skb) { return 0; } struct sk_buff *rtl8192c_tx_aggregate_hdl(struct ieee80211_hw *hw, struct sk_buff_head *list) { return skb_dequeue(list); } /*======================================== trx ===============================*/ static void _rtl_fill_usb_tx_desc(u8 *txdesc) { SET_TX_DESC_OWN(txdesc, 1); SET_TX_DESC_LAST_SEG(txdesc, 1); SET_TX_DESC_FIRST_SEG(txdesc, 1); } /** * For HW recovery information */ static void _rtl_tx_desc_checksum(u8 *txdesc) { u16 *ptr = (u16 *)txdesc; u16 checksum = 0; u32 index; /* Clear first */ SET_TX_DESC_TX_DESC_CHECKSUM(txdesc, 0); for (index = 0; index < 16; index++) checksum = checksum ^ (*(ptr + index)); SET_TX_DESC_TX_DESC_CHECKSUM(txdesc, checksum); } void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 queue_index, struct rtl_tcb_desc *tcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; u8 *qc = ieee80211_get_qos_ctl(hdr); u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; u16 seq_number; __le16 fc = hdr->frame_control; u8 rate_flag = info->control.rates[0].flags; u16 pktlen = skb->len; enum rtl_desc_qsel fw_qsel = _rtl8192cu_mq_to_descq(hw, fc, skb_get_queue_mapping(skb)); u8 *txdesc; seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; rtl_get_tcb_desc(hw, info, sta, skb, tcb_desc); txdesc = (u8 *)skb_push(skb, RTL_TX_HEADER_SIZE); memset(txdesc, 0, RTL_TX_HEADER_SIZE); SET_TX_DESC_PKT_SIZE(txdesc, pktlen); SET_TX_DESC_LINIP(txdesc, 0); SET_TX_DESC_PKT_OFFSET(txdesc, RTL_DUMMY_OFFSET); SET_TX_DESC_OFFSET(txdesc, RTL_TX_HEADER_SIZE); SET_TX_DESC_TX_RATE(txdesc, tcb_desc->hw_rate); if (tcb_desc->use_shortgi || tcb_desc->use_shortpreamble) SET_TX_DESC_DATA_SHORTGI(txdesc, 1); if (mac->tids[tid].agg.agg_state == RTL_AGG_ON && info->flags & IEEE80211_TX_CTL_AMPDU) { SET_TX_DESC_AGG_ENABLE(txdesc, 1); SET_TX_DESC_MAX_AGG_NUM(txdesc, 0x14); } else { SET_TX_DESC_AGG_BREAK(txdesc, 1); } SET_TX_DESC_SEQ(txdesc, seq_number); SET_TX_DESC_RTS_ENABLE(txdesc, ((tcb_desc->rts_enable && !tcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_HW_RTS_ENABLE(txdesc, ((tcb_desc->rts_enable || tcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_CTS2SELF(txdesc, ((tcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_RTS_STBC(txdesc, ((tcb_desc->rts_stbc) ? 1 : 0)); SET_TX_DESC_RTS_RATE(txdesc, tcb_desc->rts_rate); SET_TX_DESC_RTS_BW(txdesc, 0); SET_TX_DESC_RTS_SC(txdesc, tcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(txdesc, ((tcb_desc->rts_rate <= DESC92_RATE54M) ? (tcb_desc->rts_use_shortpreamble ? 1 : 0) : (tcb_desc->rts_use_shortgi ? 1 : 0))); if (mac->bw_40) { if (rate_flag & IEEE80211_TX_RC_DUP_DATA) { SET_TX_DESC_DATA_BW(txdesc, 1); SET_TX_DESC_DATA_SC(txdesc, 3); } else if(rate_flag & IEEE80211_TX_RC_40_MHZ_WIDTH){ SET_TX_DESC_DATA_BW(txdesc, 1); SET_TX_DESC_DATA_SC(txdesc, mac->cur_40_prime_sc); } else { SET_TX_DESC_DATA_BW(txdesc, 0); SET_TX_DESC_DATA_SC(txdesc, 0); } } else { SET_TX_DESC_DATA_BW(txdesc, 0); SET_TX_DESC_DATA_SC(txdesc, 0); } rcu_read_lock(); sta = ieee80211_find_sta(mac->vif, mac->bssid); if (sta) { u8 ampdu_density = sta->ht_cap.ampdu_density; SET_TX_DESC_AMPDU_DENSITY(txdesc, ampdu_density); } rcu_read_unlock(); if (info->control.hw_key) { struct ieee80211_key_conf *keyconf = info->control.hw_key; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: SET_TX_DESC_SEC_TYPE(txdesc, 0x1); break; case WLAN_CIPHER_SUITE_CCMP: SET_TX_DESC_SEC_TYPE(txdesc, 0x3); break; default: SET_TX_DESC_SEC_TYPE(txdesc, 0x0); break; } } SET_TX_DESC_PKT_ID(txdesc, 0); SET_TX_DESC_QUEUE_SEL(txdesc, fw_qsel); SET_TX_DESC_DATA_RATE_FB_LIMIT(txdesc, 0x1F); SET_TX_DESC_RTS_RATE_FB_LIMIT(txdesc, 0xF); SET_TX_DESC_DISABLE_FB(txdesc, 0); SET_TX_DESC_USE_RATE(txdesc, tcb_desc->use_driver_rate ? 1 : 0); if (ieee80211_is_data_qos(fc)) { if (mac->rdg_en) { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "Enable RDG function\n"); SET_TX_DESC_RDG_ENABLE(txdesc, 1); SET_TX_DESC_HTC(txdesc, 1); } } if (rtlpriv->dm.useramask) { SET_TX_DESC_RATE_ID(txdesc, tcb_desc->ratr_index); SET_TX_DESC_MACID(txdesc, tcb_desc->mac_id); } else { SET_TX_DESC_RATE_ID(txdesc, 0xC + tcb_desc->ratr_index); SET_TX_DESC_MACID(txdesc, tcb_desc->ratr_index); } if ((!ieee80211_is_data_qos(fc)) && ppsc->leisure_ps && ppsc->fwctrl_lps) { SET_TX_DESC_HWSEQ_EN(txdesc, 1); SET_TX_DESC_PKT_ID(txdesc, 8); if (!defaultadapter) SET_TX_DESC_QOS(txdesc, 1); } if (ieee80211_has_morefrags(fc)) SET_TX_DESC_MORE_FRAG(txdesc, 1); if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) || is_broadcast_ether_addr(ieee80211_get_DA(hdr))) SET_TX_DESC_BMC(txdesc, 1); _rtl_fill_usb_tx_desc(txdesc); _rtl_tx_desc_checksum(txdesc); RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "==>\n"); } void rtl92cu_fill_fake_txdesc(struct ieee80211_hw *hw, u8 * pDesc, u32 buffer_len, bool bIsPsPoll) { /* Clear all status */ memset(pDesc, 0, RTL_TX_HEADER_SIZE); SET_TX_DESC_FIRST_SEG(pDesc, 1); /* bFirstSeg; */ SET_TX_DESC_LAST_SEG(pDesc, 1); /* bLastSeg; */ SET_TX_DESC_OFFSET(pDesc, RTL_TX_HEADER_SIZE); /* Offset = 32 */ SET_TX_DESC_PKT_SIZE(pDesc, buffer_len); /* Buffer size + command hdr */ SET_TX_DESC_QUEUE_SEL(pDesc, QSLT_MGNT); /* Fixed queue of Mgnt queue */ /* Set NAVUSEHDR to prevent Ps-poll AId filed to be changed to error * vlaue by Hw. */ if (bIsPsPoll) { SET_TX_DESC_NAV_USE_HDR(pDesc, 1); } else { SET_TX_DESC_HWSEQ_EN(pDesc, 1); /* Hw set sequence number */ SET_TX_DESC_PKT_ID(pDesc, 0x100); /* set bit3 to 1. */ } SET_TX_DESC_USE_RATE(pDesc, 1); /* use data rate which is set by Sw */ SET_TX_DESC_OWN(pDesc, 1); SET_TX_DESC_TX_RATE(pDesc, DESC92_RATE1M); _rtl_tx_desc_checksum(pDesc); } void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 fw_queue = QSLT_BEACON; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; memset((void *)pdesc, 0, RTL_TX_HEADER_SIZE); if (firstseg) SET_TX_DESC_OFFSET(pdesc, RTL_TX_HEADER_SIZE); SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_RATE_ID(pdesc, 7); SET_TX_DESC_MACID(pdesc, 0); SET_TX_DESC_OWN(pdesc, 1); SET_TX_DESC_PKT_SIZE(pdesc, (u16)skb->len); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_OFFSET(pdesc, 0x20); SET_TX_DESC_USE_RATE(pdesc, 1); if (!ieee80211_is_data_qos(fc)) { SET_TX_DESC_HWSEQ_EN(pdesc, 1); SET_TX_DESC_PKT_ID(pdesc, 8); } RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, "H2C Tx Cmd Content", pdesc, RTL_TX_DESC_SIZE); } bool rtl92cu_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { return true; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/table.h0000644000175000017500000000550212026211315025060 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CU_TABLE__H_ #define __RTL92CU_TABLE__H_ #include #define RTL8192CUPHY_REG_2TARRAY_LENGTH 374 extern u32 RTL8192CUPHY_REG_2TARRAY[RTL8192CUPHY_REG_2TARRAY_LENGTH]; #define RTL8192CUPHY_REG_1TARRAY_LENGTH 374 extern u32 RTL8192CUPHY_REG_1TARRAY[RTL8192CUPHY_REG_1TARRAY_LENGTH]; #define RTL8192CUPHY_REG_ARRAY_PGLENGTH 336 extern u32 RTL8192CUPHY_REG_ARRAY_PG[RTL8192CUPHY_REG_ARRAY_PGLENGTH]; #define RTL8192CURADIOA_2TARRAYLENGTH 282 extern u32 RTL8192CURADIOA_2TARRAY[RTL8192CURADIOA_2TARRAYLENGTH]; #define RTL8192CURADIOB_2TARRAYLENGTH 78 extern u32 RTL8192CU_RADIOB_2TARRAY[RTL8192CURADIOB_2TARRAYLENGTH]; #define RTL8192CURADIOA_1TARRAYLENGTH 282 extern u32 RTL8192CU_RADIOA_1TARRAY[RTL8192CURADIOA_1TARRAYLENGTH]; #define RTL8192CURADIOB_1TARRAYLENGTH 1 extern u32 RTL8192CU_RADIOB_1TARRAY[RTL8192CURADIOB_1TARRAYLENGTH]; #define RTL8192CUMAC_2T_ARRAYLENGTH 172 extern u32 RTL8192CUMAC_2T_ARRAY[RTL8192CUMAC_2T_ARRAYLENGTH]; #define RTL8192CUAGCTAB_2TARRAYLENGTH 320 extern u32 RTL8192CUAGCTAB_2TARRAY[RTL8192CUAGCTAB_2TARRAYLENGTH]; #define RTL8192CUAGCTAB_1TARRAYLENGTH 320 extern u32 RTL8192CUAGCTAB_1TARRAY[RTL8192CUAGCTAB_1TARRAYLENGTH]; #define RTL8192CUPHY_REG_1T_HPArrayLength 378 extern u32 RTL8192CUPHY_REG_1T_HPArray[RTL8192CUPHY_REG_1T_HPArrayLength]; #define RTL8192CUPHY_REG_Array_PG_HPLength 336 extern u32 RTL8192CUPHY_REG_Array_PG_HP[RTL8192CUPHY_REG_Array_PG_HPLength]; #define RTL8192CURadioA_1T_HPArrayLength 282 extern u32 RTL8192CURadioA_1T_HPArray[RTL8192CURadioA_1T_HPArrayLength]; #define RTL8192CUAGCTAB_1T_HPArrayLength 320 extern u32 Rtl8192CUAGCTAB_1T_HPArray[RTL8192CUAGCTAB_1T_HPArrayLength]; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/table.c0000644000175000017500000012032012026211315025047 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "table.h" u32 RTL8192CUPHY_REG_2TARRAY[RTL8192CUPHY_REG_2TARRAY_LENGTH] = { 0x024, 0x0011800f, 0x028, 0x00ffdb83, 0x800, 0x80040002, 0x804, 0x00000003, 0x808, 0x0000fc00, 0x80c, 0x0000000a, 0x810, 0x10005388, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, 0x820, 0x01000100, 0x824, 0x00390004, 0x828, 0x01000100, 0x82c, 0x00390004, 0x830, 0x27272727, 0x834, 0x27272727, 0x838, 0x27272727, 0x83c, 0x27272727, 0x840, 0x00010000, 0x844, 0x00010000, 0x848, 0x27272727, 0x84c, 0x27272727, 0x850, 0x00000000, 0x854, 0x00000000, 0x858, 0x569a569a, 0x85c, 0x0c1b25a4, 0x860, 0x66e60230, 0x864, 0x061f0130, 0x868, 0x27272727, 0x86c, 0x2b2b2b27, 0x870, 0x07000700, 0x874, 0x22184000, 0x878, 0x08080808, 0x87c, 0x00000000, 0x880, 0xc0083070, 0x884, 0x000004d5, 0x888, 0x00000000, 0x88c, 0xcc0000c0, 0x890, 0x00000800, 0x894, 0xfffffffe, 0x898, 0x40302010, 0x89c, 0x00706050, 0x900, 0x00000000, 0x904, 0x00000023, 0x908, 0x00000000, 0x90c, 0x81121313, 0xa00, 0x00d047c8, 0xa04, 0x80ff000c, 0xa08, 0x8c838300, 0xa0c, 0x2e68120f, 0xa10, 0x9500bb78, 0xa14, 0x11144028, 0xa18, 0x00881117, 0xa1c, 0x89140f00, 0xa20, 0x1a1b0000, 0xa24, 0x090e1317, 0xa28, 0x00000204, 0xa2c, 0x00d30000, 0xa70, 0x101fbf00, 0xa74, 0x00000007, 0xc00, 0x48071d40, 0xc04, 0x03a05633, 0xc08, 0x000000e4, 0xc0c, 0x6c6c6c6c, 0xc10, 0x08800000, 0xc14, 0x40000100, 0xc18, 0x08800000, 0xc1c, 0x40000100, 0xc20, 0x00000000, 0xc24, 0x00000000, 0xc28, 0x00000000, 0xc2c, 0x00000000, 0xc30, 0x69e9ac44, 0xc34, 0x469652cf, 0xc38, 0x49795994, 0xc3c, 0x0a97971c, 0xc40, 0x1f7c403f, 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, 0xc50, 0x6954341e, 0xc54, 0x43bc0094, 0xc58, 0x6954341e, 0xc5c, 0x433c0094, 0xc60, 0x00000000, 0xc64, 0x5116848b, 0xc68, 0x47c00bff, 0xc6c, 0x00000036, 0xc70, 0x2c7f000d, 0xc74, 0x0186115b, 0xc78, 0x0000001f, 0xc7c, 0x00b99612, 0xc80, 0x40000100, 0xc84, 0x20f60000, 0xc88, 0x40000100, 0xc8c, 0x20200000, 0xc90, 0x00121820, 0xc94, 0x00000000, 0xc98, 0x00121820, 0xc9c, 0x00007f7f, 0xca0, 0x00000000, 0xca4, 0x00000080, 0xca8, 0x00000000, 0xcac, 0x00000000, 0xcb0, 0x00000000, 0xcb4, 0x00000000, 0xcb8, 0x00000000, 0xcbc, 0x28000000, 0xcc0, 0x00000000, 0xcc4, 0x00000000, 0xcc8, 0x00000000, 0xccc, 0x00000000, 0xcd0, 0x00000000, 0xcd4, 0x00000000, 0xcd8, 0x64b22427, 0xcdc, 0x00766932, 0xce0, 0x00222222, 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, 0xd00, 0x00080740, 0xd04, 0x00020403, 0xd08, 0x0000907f, 0xd0c, 0x20010201, 0xd10, 0xa0633333, 0xd14, 0x3333bc43, 0xd18, 0x7a8f5b6b, 0xd2c, 0xcc979975, 0xd30, 0x00000000, 0xd34, 0x80608000, 0xd38, 0x00000000, 0xd3c, 0x00027293, 0xd40, 0x00000000, 0xd44, 0x00000000, 0xd48, 0x00000000, 0xd4c, 0x00000000, 0xd50, 0x6437140a, 0xd54, 0x00000000, 0xd58, 0x00000000, 0xd5c, 0x30032064, 0xd60, 0x4653de68, 0xd64, 0x04518a3c, 0xd68, 0x00002101, 0xd6c, 0x2a201c16, 0xd70, 0x1812362e, 0xd74, 0x322c2220, 0xd78, 0x000e3c24, 0xe00, 0x2a2a2a2a, 0xe04, 0x2a2a2a2a, 0xe08, 0x03902a2a, 0xe10, 0x2a2a2a2a, 0xe14, 0x2a2a2a2a, 0xe18, 0x2a2a2a2a, 0xe1c, 0x2a2a2a2a, 0xe28, 0x00000000, 0xe30, 0x1000dc1f, 0xe34, 0x10008c1f, 0xe38, 0x02140102, 0xe3c, 0x681604c2, 0xe40, 0x01007c00, 0xe44, 0x01004800, 0xe48, 0xfb000000, 0xe4c, 0x000028d1, 0xe50, 0x1000dc1f, 0xe54, 0x10008c1f, 0xe58, 0x02140102, 0xe5c, 0x28160d05, 0xe60, 0x00000010, 0xe68, 0x001b25a4, 0xe6c, 0x63db25a4, 0xe70, 0x63db25a4, 0xe74, 0x0c1b25a4, 0xe78, 0x0c1b25a4, 0xe7c, 0x0c1b25a4, 0xe80, 0x0c1b25a4, 0xe84, 0x63db25a4, 0xe88, 0x0c1b25a4, 0xe8c, 0x63db25a4, 0xed0, 0x63db25a4, 0xed4, 0x63db25a4, 0xed8, 0x63db25a4, 0xedc, 0x001b25a4, 0xee0, 0x001b25a4, 0xeec, 0x6fdb25a4, 0xf14, 0x00000003, 0xf4c, 0x00000000, 0xf00, 0x00000300, }; u32 RTL8192CUPHY_REG_1TARRAY[RTL8192CUPHY_REG_1TARRAY_LENGTH] = { 0x024, 0x0011800f, 0x028, 0x00ffdb83, 0x800, 0x80040000, 0x804, 0x00000001, 0x808, 0x0000fc00, 0x80c, 0x0000000a, 0x810, 0x10005388, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, 0x820, 0x01000100, 0x824, 0x00390004, 0x828, 0x00000000, 0x82c, 0x00000000, 0x830, 0x00000000, 0x834, 0x00000000, 0x838, 0x00000000, 0x83c, 0x00000000, 0x840, 0x00010000, 0x844, 0x00000000, 0x848, 0x00000000, 0x84c, 0x00000000, 0x850, 0x00000000, 0x854, 0x00000000, 0x858, 0x569a569a, 0x85c, 0x001b25a4, 0x860, 0x66e60230, 0x864, 0x061f0130, 0x868, 0x00000000, 0x86c, 0x32323200, 0x870, 0x07000700, 0x874, 0x22004000, 0x878, 0x00000808, 0x87c, 0x00000000, 0x880, 0xc0083070, 0x884, 0x000004d5, 0x888, 0x00000000, 0x88c, 0xccc000c0, 0x890, 0x00000800, 0x894, 0xfffffffe, 0x898, 0x40302010, 0x89c, 0x00706050, 0x900, 0x00000000, 0x904, 0x00000023, 0x908, 0x00000000, 0x90c, 0x81121111, 0xa00, 0x00d047c8, 0xa04, 0x80ff000c, 0xa08, 0x8c838300, 0xa0c, 0x2e68120f, 0xa10, 0x9500bb78, 0xa14, 0x11144028, 0xa18, 0x00881117, 0xa1c, 0x89140f00, 0xa20, 0x1a1b0000, 0xa24, 0x090e1317, 0xa28, 0x00000204, 0xa2c, 0x00d30000, 0xa70, 0x101fbf00, 0xa74, 0x00000007, 0xc00, 0x48071d40, 0xc04, 0x03a05611, 0xc08, 0x000000e4, 0xc0c, 0x6c6c6c6c, 0xc10, 0x08800000, 0xc14, 0x40000100, 0xc18, 0x08800000, 0xc1c, 0x40000100, 0xc20, 0x00000000, 0xc24, 0x00000000, 0xc28, 0x00000000, 0xc2c, 0x00000000, 0xc30, 0x69e9ac44, 0xc34, 0x469652cf, 0xc38, 0x49795994, 0xc3c, 0x0a97971c, 0xc40, 0x1f7c403f, 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, 0xc50, 0x6954341e, 0xc54, 0x43bc0094, 0xc58, 0x6954341e, 0xc5c, 0x433c0094, 0xc60, 0x00000000, 0xc64, 0x5116848b, 0xc68, 0x47c00bff, 0xc6c, 0x00000036, 0xc70, 0x2c7f000d, 0xc74, 0x018610db, 0xc78, 0x0000001f, 0xc7c, 0x00b91612, 0xc80, 0x40000100, 0xc84, 0x20f60000, 0xc88, 0x40000100, 0xc8c, 0x20200000, 0xc90, 0x00121820, 0xc94, 0x00000000, 0xc98, 0x00121820, 0xc9c, 0x00007f7f, 0xca0, 0x00000000, 0xca4, 0x00000080, 0xca8, 0x00000000, 0xcac, 0x00000000, 0xcb0, 0x00000000, 0xcb4, 0x00000000, 0xcb8, 0x00000000, 0xcbc, 0x28000000, 0xcc0, 0x00000000, 0xcc4, 0x00000000, 0xcc8, 0x00000000, 0xccc, 0x00000000, 0xcd0, 0x00000000, 0xcd4, 0x00000000, 0xcd8, 0x64b22427, 0xcdc, 0x00766932, 0xce0, 0x00222222, 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, 0xd00, 0x00080740, 0xd04, 0x00020401, 0xd08, 0x0000907f, 0xd0c, 0x20010201, 0xd10, 0xa0633333, 0xd14, 0x3333bc43, 0xd18, 0x7a8f5b6b, 0xd2c, 0xcc979975, 0xd30, 0x00000000, 0xd34, 0x80608000, 0xd38, 0x00000000, 0xd3c, 0x00027293, 0xd40, 0x00000000, 0xd44, 0x00000000, 0xd48, 0x00000000, 0xd4c, 0x00000000, 0xd50, 0x6437140a, 0xd54, 0x00000000, 0xd58, 0x00000000, 0xd5c, 0x30032064, 0xd60, 0x4653de68, 0xd64, 0x04518a3c, 0xd68, 0x00002101, 0xd6c, 0x2a201c16, 0xd70, 0x1812362e, 0xd74, 0x322c2220, 0xd78, 0x000e3c24, 0xe00, 0x2a2a2a2a, 0xe04, 0x2a2a2a2a, 0xe08, 0x03902a2a, 0xe10, 0x2a2a2a2a, 0xe14, 0x2a2a2a2a, 0xe18, 0x2a2a2a2a, 0xe1c, 0x2a2a2a2a, 0xe28, 0x00000000, 0xe30, 0x1000dc1f, 0xe34, 0x10008c1f, 0xe38, 0x02140102, 0xe3c, 0x681604c2, 0xe40, 0x01007c00, 0xe44, 0x01004800, 0xe48, 0xfb000000, 0xe4c, 0x000028d1, 0xe50, 0x1000dc1f, 0xe54, 0x10008c1f, 0xe58, 0x02140102, 0xe5c, 0x28160d05, 0xe60, 0x00000008, 0xe68, 0x001b25a4, 0xe6c, 0x631b25a0, 0xe70, 0x631b25a0, 0xe74, 0x081b25a0, 0xe78, 0x081b25a0, 0xe7c, 0x081b25a0, 0xe80, 0x081b25a0, 0xe84, 0x631b25a0, 0xe88, 0x081b25a0, 0xe8c, 0x631b25a0, 0xed0, 0x631b25a0, 0xed4, 0x631b25a0, 0xed8, 0x631b25a0, 0xedc, 0x001b25a0, 0xee0, 0x001b25a0, 0xeec, 0x6b1b25a0, 0xf14, 0x00000003, 0xf4c, 0x00000000, 0xf00, 0x00000300, }; u32 RTL8192CUPHY_REG_ARRAY_PG[RTL8192CUPHY_REG_ARRAY_PGLENGTH] = { 0xe00, 0xffffffff, 0x07090c0c, 0xe04, 0xffffffff, 0x01020405, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x0b0c0c0e, 0xe14, 0xffffffff, 0x01030506, 0xe18, 0xffffffff, 0x0b0c0d0e, 0xe1c, 0xffffffff, 0x01030509, 0x830, 0xffffffff, 0x07090c0c, 0x834, 0xffffffff, 0x01020405, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x0b0c0d0e, 0x848, 0xffffffff, 0x01030509, 0x84c, 0xffffffff, 0x0b0c0d0e, 0x868, 0xffffffff, 0x01030509, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x06060606, 0xe14, 0xffffffff, 0x00020406, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x06060606, 0x848, 0xffffffff, 0x00020406, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, }; u32 RTL8192CURADIOA_2TARRAY[RTL8192CURADIOA_2TARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00031284, 0x002, 0x00098000, 0x003, 0x00018c63, 0x004, 0x000210e7, 0x009, 0x0002044f, 0x00a, 0x0001adb1, 0x00b, 0x00054867, 0x00c, 0x0008992e, 0x00d, 0x0000e52c, 0x00e, 0x00039ce7, 0x00f, 0x00000451, 0x019, 0x00000000, 0x01a, 0x00010255, 0x01b, 0x00060a00, 0x01c, 0x000fc378, 0x01d, 0x000a1250, 0x01e, 0x0004445f, 0x01f, 0x00080001, 0x020, 0x0000b614, 0x021, 0x0006c000, 0x022, 0x00000000, 0x023, 0x00001558, 0x024, 0x00000060, 0x025, 0x00000483, 0x026, 0x0004f000, 0x027, 0x000ec7d9, 0x028, 0x000577c0, 0x029, 0x00004783, 0x02a, 0x00000001, 0x02b, 0x00021334, 0x02a, 0x00000000, 0x02b, 0x00000054, 0x02a, 0x00000001, 0x02b, 0x00000808, 0x02b, 0x00053333, 0x02c, 0x0000000c, 0x02a, 0x00000002, 0x02b, 0x00000808, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000003, 0x02b, 0x00000808, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000004, 0x02b, 0x00000808, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x00000005, 0x02b, 0x00000808, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x00000006, 0x02b, 0x00000709, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000007, 0x02b, 0x00000709, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000008, 0x02b, 0x0000060a, 0x02b, 0x0004b333, 0x02c, 0x0000000d, 0x02a, 0x00000009, 0x02b, 0x0000060a, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x0000000a, 0x02b, 0x0000060a, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x0000000b, 0x02b, 0x0000060a, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x0000000c, 0x02b, 0x0000060a, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x0000000d, 0x02b, 0x0000060a, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x0000000e, 0x02b, 0x0000050b, 0x02b, 0x00066666, 0x02c, 0x0000001a, 0x02a, 0x000e0000, 0x010, 0x0004000f, 0x011, 0x000e31fc, 0x010, 0x0006000f, 0x011, 0x000ff9f8, 0x010, 0x0002000f, 0x011, 0x000203f9, 0x010, 0x0003000f, 0x011, 0x000ff500, 0x010, 0x00000000, 0x011, 0x00000000, 0x010, 0x0008000f, 0x011, 0x0003f100, 0x010, 0x0009000f, 0x011, 0x00023100, 0x012, 0x00032000, 0x012, 0x00071000, 0x012, 0x000b0000, 0x012, 0x000fc000, 0x013, 0x000287af, 0x013, 0x000244b7, 0x013, 0x000204ab, 0x013, 0x0001c49f, 0x013, 0x00018493, 0x013, 0x00014297, 0x013, 0x00010295, 0x013, 0x0000c298, 0x013, 0x0000819c, 0x013, 0x000040a8, 0x013, 0x0000001c, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, 0x014, 0x000d9444, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x015, 0x000cf424, 0x016, 0x000e0330, 0x016, 0x000a0330, 0x016, 0x00060330, 0x016, 0x00020330, 0x000, 0x00010159, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00044457, 0x01f, 0x00080000, 0x000, 0x00030159, }; u32 RTL8192CU_RADIOB_2TARRAY[RTL8192CURADIOB_2TARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00031284, 0x002, 0x00098000, 0x003, 0x00018c63, 0x004, 0x000210e7, 0x009, 0x0002044f, 0x00a, 0x0001adb1, 0x00b, 0x00054867, 0x00c, 0x0008992e, 0x00d, 0x0000e52c, 0x00e, 0x00039ce7, 0x00f, 0x00000451, 0x012, 0x00032000, 0x012, 0x00071000, 0x012, 0x000b0000, 0x012, 0x000fc000, 0x013, 0x000287af, 0x013, 0x000244b7, 0x013, 0x000204ab, 0x013, 0x0001c49f, 0x013, 0x00018493, 0x013, 0x00014297, 0x013, 0x00010295, 0x013, 0x0000c298, 0x013, 0x0000819c, 0x013, 0x000040a8, 0x013, 0x0000001c, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, 0x014, 0x000d9444, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x015, 0x000cf424, 0x016, 0x000e0330, 0x016, 0x000a0330, 0x016, 0x00060330, 0x016, 0x00020330, }; u32 RTL8192CU_RADIOA_1TARRAY[RTL8192CURADIOA_1TARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00031284, 0x002, 0x00098000, 0x003, 0x00018c63, 0x004, 0x000210e7, 0x009, 0x0002044f, 0x00a, 0x0001adb1, 0x00b, 0x00054867, 0x00c, 0x0008992e, 0x00d, 0x0000e52c, 0x00e, 0x00039ce7, 0x00f, 0x00000451, 0x019, 0x00000000, 0x01a, 0x00010255, 0x01b, 0x00060a00, 0x01c, 0x000fc378, 0x01d, 0x000a1250, 0x01e, 0x0004445f, 0x01f, 0x00080001, 0x020, 0x0000b614, 0x021, 0x0006c000, 0x022, 0x00000000, 0x023, 0x00001558, 0x024, 0x00000060, 0x025, 0x00000483, 0x026, 0x0004f000, 0x027, 0x000ec7d9, 0x028, 0x000577c0, 0x029, 0x00004783, 0x02a, 0x00000001, 0x02b, 0x00021334, 0x02a, 0x00000000, 0x02b, 0x00000054, 0x02a, 0x00000001, 0x02b, 0x00000808, 0x02b, 0x00053333, 0x02c, 0x0000000c, 0x02a, 0x00000002, 0x02b, 0x00000808, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000003, 0x02b, 0x00000808, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000004, 0x02b, 0x00000808, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x00000005, 0x02b, 0x00000808, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x00000006, 0x02b, 0x00000709, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000007, 0x02b, 0x00000709, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000008, 0x02b, 0x0000060a, 0x02b, 0x0004b333, 0x02c, 0x0000000d, 0x02a, 0x00000009, 0x02b, 0x0000060a, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x0000000a, 0x02b, 0x0000060a, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x0000000b, 0x02b, 0x0000060a, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x0000000c, 0x02b, 0x0000060a, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x0000000d, 0x02b, 0x0000060a, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x0000000e, 0x02b, 0x0000050b, 0x02b, 0x00066666, 0x02c, 0x0000001a, 0x02a, 0x000e0000, 0x010, 0x0004000f, 0x011, 0x000e31fc, 0x010, 0x0006000f, 0x011, 0x000ff9f8, 0x010, 0x0002000f, 0x011, 0x000203f9, 0x010, 0x0003000f, 0x011, 0x000ff500, 0x010, 0x00000000, 0x011, 0x00000000, 0x010, 0x0008000f, 0x011, 0x0003f100, 0x010, 0x0009000f, 0x011, 0x00023100, 0x012, 0x00032000, 0x012, 0x00071000, 0x012, 0x000b0000, 0x012, 0x000fc000, 0x013, 0x000287b3, 0x013, 0x000244b7, 0x013, 0x000204ab, 0x013, 0x0001c49f, 0x013, 0x00018493, 0x013, 0x0001429b, 0x013, 0x00010299, 0x013, 0x0000c29c, 0x013, 0x000081a0, 0x013, 0x000040ac, 0x013, 0x00000020, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, 0x014, 0x000d9444, 0x015, 0x0000f405, 0x015, 0x0004f405, 0x015, 0x0008f405, 0x015, 0x000cf405, 0x016, 0x000e0330, 0x016, 0x000a0330, 0x016, 0x00060330, 0x016, 0x00020330, 0x000, 0x00010159, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00044457, 0x01f, 0x00080000, 0x000, 0x00030159, }; u32 RTL8192CU_RADIOB_1TARRAY[RTL8192CURADIOB_1TARRAYLENGTH] = { 0x0, }; u32 RTL8192CUMAC_2T_ARRAY[RTL8192CUMAC_2T_ARRAYLENGTH] = { 0x420, 0x00000080, 0x423, 0x00000000, 0x430, 0x00000000, 0x431, 0x00000000, 0x432, 0x00000000, 0x433, 0x00000001, 0x434, 0x00000004, 0x435, 0x00000005, 0x436, 0x00000006, 0x437, 0x00000007, 0x438, 0x00000000, 0x439, 0x00000000, 0x43a, 0x00000000, 0x43b, 0x00000001, 0x43c, 0x00000004, 0x43d, 0x00000005, 0x43e, 0x00000006, 0x43f, 0x00000007, 0x440, 0x0000005d, 0x441, 0x00000001, 0x442, 0x00000000, 0x444, 0x00000015, 0x445, 0x000000f0, 0x446, 0x0000000f, 0x447, 0x00000000, 0x458, 0x00000041, 0x459, 0x000000a8, 0x45a, 0x00000072, 0x45b, 0x000000b9, 0x460, 0x00000066, 0x461, 0x00000066, 0x462, 0x00000008, 0x463, 0x00000003, 0x4c8, 0x000000ff, 0x4c9, 0x00000008, 0x4cc, 0x000000ff, 0x4cd, 0x000000ff, 0x4ce, 0x00000001, 0x500, 0x00000026, 0x501, 0x000000a2, 0x502, 0x0000002f, 0x503, 0x00000000, 0x504, 0x00000028, 0x505, 0x000000a3, 0x506, 0x0000005e, 0x507, 0x00000000, 0x508, 0x0000002b, 0x509, 0x000000a4, 0x50a, 0x0000005e, 0x50b, 0x00000000, 0x50c, 0x0000004f, 0x50d, 0x000000a4, 0x50e, 0x00000000, 0x50f, 0x00000000, 0x512, 0x0000001c, 0x514, 0x0000000a, 0x515, 0x00000010, 0x516, 0x0000000a, 0x517, 0x00000010, 0x51a, 0x00000016, 0x524, 0x0000000f, 0x525, 0x0000004f, 0x546, 0x00000040, 0x547, 0x00000000, 0x550, 0x00000010, 0x551, 0x00000010, 0x559, 0x00000002, 0x55a, 0x00000002, 0x55d, 0x000000ff, 0x605, 0x00000030, 0x608, 0x0000000e, 0x609, 0x0000002a, 0x652, 0x00000020, 0x63c, 0x0000000a, 0x63d, 0x0000000e, 0x63e, 0x0000000a, 0x63f, 0x0000000e, 0x66e, 0x00000005, 0x700, 0x00000021, 0x701, 0x00000043, 0x702, 0x00000065, 0x703, 0x00000087, 0x708, 0x00000021, 0x709, 0x00000043, 0x70a, 0x00000065, 0x70b, 0x00000087, }; u32 RTL8192CUAGCTAB_2TARRAY[RTL8192CUAGCTAB_2TARRAYLENGTH] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7b020001, 0xc78, 0x7b030001, 0xc78, 0x7b040001, 0xc78, 0x7b050001, 0xc78, 0x7a060001, 0xc78, 0x79070001, 0xc78, 0x78080001, 0xc78, 0x77090001, 0xc78, 0x760a0001, 0xc78, 0x750b0001, 0xc78, 0x740c0001, 0xc78, 0x730d0001, 0xc78, 0x720e0001, 0xc78, 0x710f0001, 0xc78, 0x70100001, 0xc78, 0x6f110001, 0xc78, 0x6e120001, 0xc78, 0x6d130001, 0xc78, 0x6c140001, 0xc78, 0x6b150001, 0xc78, 0x6a160001, 0xc78, 0x69170001, 0xc78, 0x68180001, 0xc78, 0x67190001, 0xc78, 0x661a0001, 0xc78, 0x651b0001, 0xc78, 0x641c0001, 0xc78, 0x631d0001, 0xc78, 0x621e0001, 0xc78, 0x611f0001, 0xc78, 0x60200001, 0xc78, 0x49210001, 0xc78, 0x48220001, 0xc78, 0x47230001, 0xc78, 0x46240001, 0xc78, 0x45250001, 0xc78, 0x44260001, 0xc78, 0x43270001, 0xc78, 0x42280001, 0xc78, 0x41290001, 0xc78, 0x402a0001, 0xc78, 0x262b0001, 0xc78, 0x252c0001, 0xc78, 0x242d0001, 0xc78, 0x232e0001, 0xc78, 0x222f0001, 0xc78, 0x21300001, 0xc78, 0x20310001, 0xc78, 0x06320001, 0xc78, 0x05330001, 0xc78, 0x04340001, 0xc78, 0x03350001, 0xc78, 0x02360001, 0xc78, 0x01370001, 0xc78, 0x00380001, 0xc78, 0x00390001, 0xc78, 0x003a0001, 0xc78, 0x003b0001, 0xc78, 0x003c0001, 0xc78, 0x003d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x7b400001, 0xc78, 0x7b410001, 0xc78, 0x7b420001, 0xc78, 0x7b430001, 0xc78, 0x7b440001, 0xc78, 0x7b450001, 0xc78, 0x7a460001, 0xc78, 0x79470001, 0xc78, 0x78480001, 0xc78, 0x77490001, 0xc78, 0x764a0001, 0xc78, 0x754b0001, 0xc78, 0x744c0001, 0xc78, 0x734d0001, 0xc78, 0x724e0001, 0xc78, 0x714f0001, 0xc78, 0x70500001, 0xc78, 0x6f510001, 0xc78, 0x6e520001, 0xc78, 0x6d530001, 0xc78, 0x6c540001, 0xc78, 0x6b550001, 0xc78, 0x6a560001, 0xc78, 0x69570001, 0xc78, 0x68580001, 0xc78, 0x67590001, 0xc78, 0x665a0001, 0xc78, 0x655b0001, 0xc78, 0x645c0001, 0xc78, 0x635d0001, 0xc78, 0x625e0001, 0xc78, 0x615f0001, 0xc78, 0x60600001, 0xc78, 0x49610001, 0xc78, 0x48620001, 0xc78, 0x47630001, 0xc78, 0x46640001, 0xc78, 0x45650001, 0xc78, 0x44660001, 0xc78, 0x43670001, 0xc78, 0x42680001, 0xc78, 0x41690001, 0xc78, 0x406a0001, 0xc78, 0x266b0001, 0xc78, 0x256c0001, 0xc78, 0x246d0001, 0xc78, 0x236e0001, 0xc78, 0x226f0001, 0xc78, 0x21700001, 0xc78, 0x20710001, 0xc78, 0x06720001, 0xc78, 0x05730001, 0xc78, 0x04740001, 0xc78, 0x03750001, 0xc78, 0x02760001, 0xc78, 0x01770001, 0xc78, 0x00780001, 0xc78, 0x00790001, 0xc78, 0x007a0001, 0xc78, 0x007b0001, 0xc78, 0x007c0001, 0xc78, 0x007d0001, 0xc78, 0x007e0001, 0xc78, 0x007f0001, 0xc78, 0x3800001e, 0xc78, 0x3801001e, 0xc78, 0x3802001e, 0xc78, 0x3803001e, 0xc78, 0x3804001e, 0xc78, 0x3805001e, 0xc78, 0x3806001e, 0xc78, 0x3807001e, 0xc78, 0x3808001e, 0xc78, 0x3c09001e, 0xc78, 0x3e0a001e, 0xc78, 0x400b001e, 0xc78, 0x440c001e, 0xc78, 0x480d001e, 0xc78, 0x4c0e001e, 0xc78, 0x500f001e, 0xc78, 0x5210001e, 0xc78, 0x5611001e, 0xc78, 0x5a12001e, 0xc78, 0x5e13001e, 0xc78, 0x6014001e, 0xc78, 0x6015001e, 0xc78, 0x6016001e, 0xc78, 0x6217001e, 0xc78, 0x6218001e, 0xc78, 0x6219001e, 0xc78, 0x621a001e, 0xc78, 0x621b001e, 0xc78, 0x621c001e, 0xc78, 0x621d001e, 0xc78, 0x621e001e, 0xc78, 0x621f001e, }; u32 RTL8192CUAGCTAB_1TARRAY[RTL8192CUAGCTAB_1TARRAYLENGTH] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7b020001, 0xc78, 0x7b030001, 0xc78, 0x7b040001, 0xc78, 0x7b050001, 0xc78, 0x7a060001, 0xc78, 0x79070001, 0xc78, 0x78080001, 0xc78, 0x77090001, 0xc78, 0x760a0001, 0xc78, 0x750b0001, 0xc78, 0x740c0001, 0xc78, 0x730d0001, 0xc78, 0x720e0001, 0xc78, 0x710f0001, 0xc78, 0x70100001, 0xc78, 0x6f110001, 0xc78, 0x6e120001, 0xc78, 0x6d130001, 0xc78, 0x6c140001, 0xc78, 0x6b150001, 0xc78, 0x6a160001, 0xc78, 0x69170001, 0xc78, 0x68180001, 0xc78, 0x67190001, 0xc78, 0x661a0001, 0xc78, 0x651b0001, 0xc78, 0x641c0001, 0xc78, 0x631d0001, 0xc78, 0x621e0001, 0xc78, 0x611f0001, 0xc78, 0x60200001, 0xc78, 0x49210001, 0xc78, 0x48220001, 0xc78, 0x47230001, 0xc78, 0x46240001, 0xc78, 0x45250001, 0xc78, 0x44260001, 0xc78, 0x43270001, 0xc78, 0x42280001, 0xc78, 0x41290001, 0xc78, 0x402a0001, 0xc78, 0x262b0001, 0xc78, 0x252c0001, 0xc78, 0x242d0001, 0xc78, 0x232e0001, 0xc78, 0x222f0001, 0xc78, 0x21300001, 0xc78, 0x20310001, 0xc78, 0x06320001, 0xc78, 0x05330001, 0xc78, 0x04340001, 0xc78, 0x03350001, 0xc78, 0x02360001, 0xc78, 0x01370001, 0xc78, 0x00380001, 0xc78, 0x00390001, 0xc78, 0x003a0001, 0xc78, 0x003b0001, 0xc78, 0x003c0001, 0xc78, 0x003d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x7b400001, 0xc78, 0x7b410001, 0xc78, 0x7b420001, 0xc78, 0x7b430001, 0xc78, 0x7b440001, 0xc78, 0x7b450001, 0xc78, 0x7a460001, 0xc78, 0x79470001, 0xc78, 0x78480001, 0xc78, 0x77490001, 0xc78, 0x764a0001, 0xc78, 0x754b0001, 0xc78, 0x744c0001, 0xc78, 0x734d0001, 0xc78, 0x724e0001, 0xc78, 0x714f0001, 0xc78, 0x70500001, 0xc78, 0x6f510001, 0xc78, 0x6e520001, 0xc78, 0x6d530001, 0xc78, 0x6c540001, 0xc78, 0x6b550001, 0xc78, 0x6a560001, 0xc78, 0x69570001, 0xc78, 0x68580001, 0xc78, 0x67590001, 0xc78, 0x665a0001, 0xc78, 0x655b0001, 0xc78, 0x645c0001, 0xc78, 0x635d0001, 0xc78, 0x625e0001, 0xc78, 0x615f0001, 0xc78, 0x60600001, 0xc78, 0x49610001, 0xc78, 0x48620001, 0xc78, 0x47630001, 0xc78, 0x46640001, 0xc78, 0x45650001, 0xc78, 0x44660001, 0xc78, 0x43670001, 0xc78, 0x42680001, 0xc78, 0x41690001, 0xc78, 0x406a0001, 0xc78, 0x266b0001, 0xc78, 0x256c0001, 0xc78, 0x246d0001, 0xc78, 0x236e0001, 0xc78, 0x226f0001, 0xc78, 0x21700001, 0xc78, 0x20710001, 0xc78, 0x06720001, 0xc78, 0x05730001, 0xc78, 0x04740001, 0xc78, 0x03750001, 0xc78, 0x02760001, 0xc78, 0x01770001, 0xc78, 0x00780001, 0xc78, 0x00790001, 0xc78, 0x007a0001, 0xc78, 0x007b0001, 0xc78, 0x007c0001, 0xc78, 0x007d0001, 0xc78, 0x007e0001, 0xc78, 0x007f0001, 0xc78, 0x3800001e, 0xc78, 0x3801001e, 0xc78, 0x3802001e, 0xc78, 0x3803001e, 0xc78, 0x3804001e, 0xc78, 0x3805001e, 0xc78, 0x3806001e, 0xc78, 0x3807001e, 0xc78, 0x3808001e, 0xc78, 0x3c09001e, 0xc78, 0x3e0a001e, 0xc78, 0x400b001e, 0xc78, 0x440c001e, 0xc78, 0x480d001e, 0xc78, 0x4c0e001e, 0xc78, 0x500f001e, 0xc78, 0x5210001e, 0xc78, 0x5611001e, 0xc78, 0x5a12001e, 0xc78, 0x5e13001e, 0xc78, 0x6014001e, 0xc78, 0x6015001e, 0xc78, 0x6016001e, 0xc78, 0x6217001e, 0xc78, 0x6218001e, 0xc78, 0x6219001e, 0xc78, 0x621a001e, 0xc78, 0x621b001e, 0xc78, 0x621c001e, 0xc78, 0x621d001e, 0xc78, 0x621e001e, 0xc78, 0x621f001e, }; u32 RTL8192CUPHY_REG_1T_HPArray[RTL8192CUPHY_REG_1T_HPArrayLength] = { 0x024, 0x0011800f, 0x028, 0x00ffdb83, 0x040, 0x000c0004, 0x800, 0x80040000, 0x804, 0x00000001, 0x808, 0x0000fc00, 0x80c, 0x0000000a, 0x810, 0x10005388, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, 0x820, 0x01000100, 0x824, 0x00390204, 0x828, 0x00000000, 0x82c, 0x00000000, 0x830, 0x00000000, 0x834, 0x00000000, 0x838, 0x00000000, 0x83c, 0x00000000, 0x840, 0x00010000, 0x844, 0x00000000, 0x848, 0x00000000, 0x84c, 0x00000000, 0x850, 0x00000000, 0x854, 0x00000000, 0x858, 0x569a569a, 0x85c, 0x001b25a4, 0x860, 0x66e60230, 0x864, 0x061f0130, 0x868, 0x00000000, 0x86c, 0x20202000, 0x870, 0x03000300, 0x874, 0x22004000, 0x878, 0x00000808, 0x87c, 0x00ffc3f1, 0x880, 0xc0083070, 0x884, 0x000004d5, 0x888, 0x00000000, 0x88c, 0xccc000c0, 0x890, 0x00000800, 0x894, 0xfffffffe, 0x898, 0x40302010, 0x89c, 0x00706050, 0x900, 0x00000000, 0x904, 0x00000023, 0x908, 0x00000000, 0x90c, 0x81121111, 0xa00, 0x00d047c8, 0xa04, 0x80ff000c, 0xa08, 0x8c838300, 0xa0c, 0x2e68120f, 0xa10, 0x9500bb78, 0xa14, 0x11144028, 0xa18, 0x00881117, 0xa1c, 0x89140f00, 0xa20, 0x15160000, 0xa24, 0x070b0f12, 0xa28, 0x00000104, 0xa2c, 0x00d30000, 0xa70, 0x101fbf00, 0xa74, 0x00000007, 0xc00, 0x48071d40, 0xc04, 0x03a05611, 0xc08, 0x000000e4, 0xc0c, 0x6c6c6c6c, 0xc10, 0x08800000, 0xc14, 0x40000100, 0xc18, 0x08800000, 0xc1c, 0x40000100, 0xc20, 0x00000000, 0xc24, 0x00000000, 0xc28, 0x00000000, 0xc2c, 0x00000000, 0xc30, 0x69e9ac44, 0xc34, 0x469652cf, 0xc38, 0x49795994, 0xc3c, 0x0a97971c, 0xc40, 0x1f7c403f, 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, 0xc50, 0x6954342e, 0xc54, 0x43bc0094, 0xc58, 0x6954342f, 0xc5c, 0x433c0094, 0xc60, 0x00000000, 0xc64, 0x5116848b, 0xc68, 0x47c00bff, 0xc6c, 0x00000036, 0xc70, 0x2c46000d, 0xc74, 0x018610db, 0xc78, 0x0000001f, 0xc7c, 0x00b91612, 0xc80, 0x24000090, 0xc84, 0x20f60000, 0xc88, 0x24000090, 0xc8c, 0x20200000, 0xc90, 0x00121820, 0xc94, 0x00000000, 0xc98, 0x00121820, 0xc9c, 0x00007f7f, 0xca0, 0x00000000, 0xca4, 0x00000080, 0xca8, 0x00000000, 0xcac, 0x00000000, 0xcb0, 0x00000000, 0xcb4, 0x00000000, 0xcb8, 0x00000000, 0xcbc, 0x28000000, 0xcc0, 0x00000000, 0xcc4, 0x00000000, 0xcc8, 0x00000000, 0xccc, 0x00000000, 0xcd0, 0x00000000, 0xcd4, 0x00000000, 0xcd8, 0x64b22427, 0xcdc, 0x00766932, 0xce0, 0x00222222, 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, 0xd00, 0x00080740, 0xd04, 0x00020401, 0xd08, 0x0000907f, 0xd0c, 0x20010201, 0xd10, 0xa0633333, 0xd14, 0x3333bc43, 0xd18, 0x7a8f5b6b, 0xd2c, 0xcc979975, 0xd30, 0x00000000, 0xd34, 0x80608000, 0xd38, 0x00000000, 0xd3c, 0x00027293, 0xd40, 0x00000000, 0xd44, 0x00000000, 0xd48, 0x00000000, 0xd4c, 0x00000000, 0xd50, 0x6437140a, 0xd54, 0x00000000, 0xd58, 0x00000000, 0xd5c, 0x30032064, 0xd60, 0x4653de68, 0xd64, 0x04518a3c, 0xd68, 0x00002101, 0xd6c, 0x2a201c16, 0xd70, 0x1812362e, 0xd74, 0x322c2220, 0xd78, 0x000e3c24, 0xe00, 0x24242424, 0xe04, 0x24242424, 0xe08, 0x03902024, 0xe10, 0x24242424, 0xe14, 0x24242424, 0xe18, 0x24242424, 0xe1c, 0x24242424, 0xe28, 0x00000000, 0xe30, 0x1000dc1f, 0xe34, 0x10008c1f, 0xe38, 0x02140102, 0xe3c, 0x681604c2, 0xe40, 0x01007c00, 0xe44, 0x01004800, 0xe48, 0xfb000000, 0xe4c, 0x000028d1, 0xe50, 0x1000dc1f, 0xe54, 0x10008c1f, 0xe58, 0x02140102, 0xe5c, 0x28160d05, 0xe60, 0x00000008, 0xe68, 0x001b25a4, 0xe6c, 0x631b25a0, 0xe70, 0x631b25a0, 0xe74, 0x081b25a0, 0xe78, 0x081b25a0, 0xe7c, 0x081b25a0, 0xe80, 0x081b25a0, 0xe84, 0x631b25a0, 0xe88, 0x081b25a0, 0xe8c, 0x631b25a0, 0xed0, 0x631b25a0, 0xed4, 0x631b25a0, 0xed8, 0x631b25a0, 0xedc, 0x001b25a0, 0xee0, 0x001b25a0, 0xeec, 0x6b1b25a0, 0xee8, 0x31555448, 0xf14, 0x00000003, 0xf4c, 0x00000000, 0xf00, 0x00000300, }; u32 RTL8192CUPHY_REG_Array_PG_HP[RTL8192CUPHY_REG_Array_PG_HPLength] = { 0xe00, 0xffffffff, 0x06080808, 0xe04, 0xffffffff, 0x00040406, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x04060608, 0xe14, 0xffffffff, 0x00020204, 0xe18, 0xffffffff, 0x04060608, 0xe1c, 0xffffffff, 0x00020204, 0x830, 0xffffffff, 0x06080808, 0x834, 0xffffffff, 0x00040406, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x04060608, 0x848, 0xffffffff, 0x00020204, 0x84c, 0xffffffff, 0x04060608, 0x868, 0xffffffff, 0x00020204, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, }; u32 RTL8192CURadioA_1T_HPArray[RTL8192CURadioA_1T_HPArrayLength] = { 0x000, 0x00030159, 0x001, 0x00031284, 0x002, 0x00098000, 0x003, 0x00018c63, 0x004, 0x000210e7, 0x009, 0x0002044f, 0x00a, 0x0001adb0, 0x00b, 0x00054867, 0x00c, 0x0008992e, 0x00d, 0x0000e529, 0x00e, 0x00039ce7, 0x00f, 0x00000451, 0x019, 0x00000000, 0x01a, 0x00000255, 0x01b, 0x00060a00, 0x01c, 0x000fc378, 0x01d, 0x000a1250, 0x01e, 0x0004445f, 0x01f, 0x00080001, 0x020, 0x0000b614, 0x021, 0x0006c000, 0x022, 0x0000083c, 0x023, 0x00001558, 0x024, 0x00000060, 0x025, 0x00000483, 0x026, 0x0004f000, 0x027, 0x000ec7d9, 0x028, 0x000977c0, 0x029, 0x00004783, 0x02a, 0x00000001, 0x02b, 0x00021334, 0x02a, 0x00000000, 0x02b, 0x00000054, 0x02a, 0x00000001, 0x02b, 0x00000808, 0x02b, 0x00053333, 0x02c, 0x0000000c, 0x02a, 0x00000002, 0x02b, 0x00000808, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000003, 0x02b, 0x00000808, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000004, 0x02b, 0x00000808, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x00000005, 0x02b, 0x00000808, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x00000006, 0x02b, 0x00000709, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000007, 0x02b, 0x00000709, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000008, 0x02b, 0x0000060a, 0x02b, 0x0004b333, 0x02c, 0x0000000d, 0x02a, 0x00000009, 0x02b, 0x0000060a, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x0000000a, 0x02b, 0x0000060a, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x0000000b, 0x02b, 0x0000060a, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x0000000c, 0x02b, 0x0000060a, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x0000000d, 0x02b, 0x0000060a, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x0000000e, 0x02b, 0x0000050b, 0x02b, 0x00066666, 0x02c, 0x0000001a, 0x02a, 0x000e0000, 0x010, 0x0004000f, 0x011, 0x000e31fc, 0x010, 0x0006000f, 0x011, 0x000ff9f8, 0x010, 0x0002000f, 0x011, 0x000203f9, 0x010, 0x0003000f, 0x011, 0x000ff500, 0x010, 0x00000000, 0x011, 0x00000000, 0x010, 0x0008000f, 0x011, 0x0003f100, 0x010, 0x0009000f, 0x011, 0x00023100, 0x012, 0x000d8000, 0x012, 0x00090000, 0x012, 0x00051000, 0x012, 0x00012000, 0x013, 0x00028fb4, 0x013, 0x00024fa8, 0x013, 0x000207a4, 0x013, 0x0001c798, 0x013, 0x000183a4, 0x013, 0x00014398, 0x013, 0x000101a4, 0x013, 0x0000c198, 0x013, 0x000080a4, 0x013, 0x00004098, 0x013, 0x00000000, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, 0x014, 0x000d9444, 0x015, 0x0000f405, 0x015, 0x0004f405, 0x015, 0x0008f405, 0x015, 0x000cf405, 0x016, 0x000e0330, 0x016, 0x000a0330, 0x016, 0x00060330, 0x016, 0x00020330, 0x000, 0x00010159, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00044457, 0x01f, 0x00080000, 0x000, 0x00030159, }; u32 Rtl8192CUAGCTAB_1T_HPArray[RTL8192CUAGCTAB_1T_HPArrayLength] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7b020001, 0xc78, 0x7b030001, 0xc78, 0x7b040001, 0xc78, 0x7b050001, 0xc78, 0x7b060001, 0xc78, 0x7b070001, 0xc78, 0x7b080001, 0xc78, 0x7a090001, 0xc78, 0x790a0001, 0xc78, 0x780b0001, 0xc78, 0x770c0001, 0xc78, 0x760d0001, 0xc78, 0x750e0001, 0xc78, 0x740f0001, 0xc78, 0x73100001, 0xc78, 0x72110001, 0xc78, 0x71120001, 0xc78, 0x70130001, 0xc78, 0x6f140001, 0xc78, 0x6e150001, 0xc78, 0x6d160001, 0xc78, 0x6c170001, 0xc78, 0x6b180001, 0xc78, 0x6a190001, 0xc78, 0x691a0001, 0xc78, 0x681b0001, 0xc78, 0x671c0001, 0xc78, 0x661d0001, 0xc78, 0x651e0001, 0xc78, 0x641f0001, 0xc78, 0x63200001, 0xc78, 0x62210001, 0xc78, 0x61220001, 0xc78, 0x60230001, 0xc78, 0x46240001, 0xc78, 0x45250001, 0xc78, 0x44260001, 0xc78, 0x43270001, 0xc78, 0x42280001, 0xc78, 0x41290001, 0xc78, 0x402a0001, 0xc78, 0x262b0001, 0xc78, 0x252c0001, 0xc78, 0x242d0001, 0xc78, 0x232e0001, 0xc78, 0x222f0001, 0xc78, 0x21300001, 0xc78, 0x20310001, 0xc78, 0x06320001, 0xc78, 0x05330001, 0xc78, 0x04340001, 0xc78, 0x03350001, 0xc78, 0x02360001, 0xc78, 0x01370001, 0xc78, 0x00380001, 0xc78, 0x00390001, 0xc78, 0x003a0001, 0xc78, 0x003b0001, 0xc78, 0x003c0001, 0xc78, 0x003d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x7b400001, 0xc78, 0x7b410001, 0xc78, 0x7b420001, 0xc78, 0x7b430001, 0xc78, 0x7b440001, 0xc78, 0x7b450001, 0xc78, 0x7b460001, 0xc78, 0x7b470001, 0xc78, 0x7b480001, 0xc78, 0x7a490001, 0xc78, 0x794a0001, 0xc78, 0x784b0001, 0xc78, 0x774c0001, 0xc78, 0x764d0001, 0xc78, 0x754e0001, 0xc78, 0x744f0001, 0xc78, 0x73500001, 0xc78, 0x72510001, 0xc78, 0x71520001, 0xc78, 0x70530001, 0xc78, 0x6f540001, 0xc78, 0x6e550001, 0xc78, 0x6d560001, 0xc78, 0x6c570001, 0xc78, 0x6b580001, 0xc78, 0x6a590001, 0xc78, 0x695a0001, 0xc78, 0x685b0001, 0xc78, 0x675c0001, 0xc78, 0x665d0001, 0xc78, 0x655e0001, 0xc78, 0x645f0001, 0xc78, 0x63600001, 0xc78, 0x62610001, 0xc78, 0x61620001, 0xc78, 0x60630001, 0xc78, 0x46640001, 0xc78, 0x45650001, 0xc78, 0x44660001, 0xc78, 0x43670001, 0xc78, 0x42680001, 0xc78, 0x41690001, 0xc78, 0x406a0001, 0xc78, 0x266b0001, 0xc78, 0x256c0001, 0xc78, 0x246d0001, 0xc78, 0x236e0001, 0xc78, 0x226f0001, 0xc78, 0x21700001, 0xc78, 0x20710001, 0xc78, 0x06720001, 0xc78, 0x05730001, 0xc78, 0x04740001, 0xc78, 0x03750001, 0xc78, 0x02760001, 0xc78, 0x01770001, 0xc78, 0x00780001, 0xc78, 0x00790001, 0xc78, 0x007a0001, 0xc78, 0x007b0001, 0xc78, 0x007c0001, 0xc78, 0x007d0001, 0xc78, 0x007e0001, 0xc78, 0x007f0001, 0xc78, 0x3800001e, 0xc78, 0x3801001e, 0xc78, 0x3802001e, 0xc78, 0x3803001e, 0xc78, 0x3804001e, 0xc78, 0x3805001e, 0xc78, 0x3806001e, 0xc78, 0x3807001e, 0xc78, 0x3808001e, 0xc78, 0x3c09001e, 0xc78, 0x3e0a001e, 0xc78, 0x400b001e, 0xc78, 0x440c001e, 0xc78, 0x480d001e, 0xc78, 0x4c0e001e, 0xc78, 0x500f001e, 0xc78, 0x5210001e, 0xc78, 0x5611001e, 0xc78, 0x5a12001e, 0xc78, 0x5e13001e, 0xc78, 0x6014001e, 0xc78, 0x6015001e, 0xc78, 0x6016001e, 0xc78, 0x6217001e, 0xc78, 0x6218001e, 0xc78, 0x6219001e, 0xc78, 0x621a001e, 0xc78, 0x621b001e, 0xc78, 0x621c001e, 0xc78, 0x621d001e, 0xc78, 0x621e001e, 0xc78, 0x621f001e, }; compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h0000644000175000017500000000413012026211315024416 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CU_SW_H__ #define __RTL92CU_SW_H__ #define EFUSE_MAX_SECTION 16 void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *powerlevel); void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel); bool _rtl92cu_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype); bool _rtl92cu_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype); void _rtl92cu_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); void rtl92cu_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h0000644000175000017500000000367712026211315024413 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CU_RF_H__ #define __RTL92CU_RF_H__ #define RF6052_MAX_TX_PWR 0x3F #define RF6052_MAX_REG 0x3F #define RF6052_MAX_PATH 2 extern void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); extern void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel); extern void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel); bool rtl92cu_phy_rf6052_config(struct ieee80211_hw *hw); bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel); void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c0000644000175000017500000003524712026211315024404 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" static bool _rtl92c_phy_rf6052_config_parafile(struct ieee80211_hw *hw); void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); switch (bandwidth) { case HT_CHANNEL_WIDTH_20: rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 0xfffff3ff) | 0x0400); rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[0]); break; case HT_CHANNEL_WIDTH_20_40: rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 0xfffff3ff)); rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[0]); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", bandwidth); break; } } void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 tx_agc[2] = { 0, 0 }, tmpval = 0; bool turbo_scanoff = false; u8 idx1, idx2; u8 *ptr; if (rtlhal->interface == INTF_PCI) { if (rtlefuse->eeprom_regulatory != 0) turbo_scanoff = true; } else { if ((rtlefuse->eeprom_regulatory != 0) || (rtlefuse->external_pa)) turbo_scanoff = true; } if (mac->act_scanning) { tx_agc[RF90_PATH_A] = 0x3f3f3f3f; tx_agc[RF90_PATH_B] = 0x3f3f3f3f; if (turbo_scanoff) { for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { tx_agc[idx1] = ppowerlevel[idx1] | (ppowerlevel[idx1] << 8) | (ppowerlevel[idx1] << 16) | (ppowerlevel[idx1] << 24); if (rtlhal->interface == INTF_USB) { if (tx_agc[idx1] > 0x20 && rtlefuse->external_pa) tx_agc[idx1] = 0x20; } } } } else { if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_LEVEL1) { tx_agc[RF90_PATH_A] = 0x10101010; tx_agc[RF90_PATH_B] = 0x10101010; } else if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_LEVEL1) { tx_agc[RF90_PATH_A] = 0x00000000; tx_agc[RF90_PATH_B] = 0x00000000; } else{ for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { tx_agc[idx1] = ppowerlevel[idx1] | (ppowerlevel[idx1] << 8) | (ppowerlevel[idx1] << 16) | (ppowerlevel[idx1] << 24); } if (rtlefuse->eeprom_regulatory == 0) { tmpval = (rtlphy->mcs_txpwrlevel_origoffset [0][6]) + (rtlphy->mcs_txpwrlevel_origoffset [0][7] << 8); tx_agc[RF90_PATH_A] += tmpval; tmpval = (rtlphy->mcs_txpwrlevel_origoffset [0][14]) + (rtlphy->mcs_txpwrlevel_origoffset [0][15] << 24); tx_agc[RF90_PATH_B] += tmpval; } } } for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { ptr = (u8 *) (&(tx_agc[idx1])); for (idx2 = 0; idx2 < 4; idx2++) { if (*ptr > RF6052_MAX_TX_PWR) *ptr = RF6052_MAX_TX_PWR; ptr++; } } tmpval = tx_agc[RF90_PATH_A] & 0xff; rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_A_CCK1_MCS32); tmpval = tx_agc[RF90_PATH_A] >> 8; if (mac->mode == WIRELESS_MODE_B) tmpval = tmpval & 0xff00ffff; rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); tmpval = tx_agc[RF90_PATH_B] >> 24; rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff; rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK1_55_MCS32); } static void rtl92c_phy_get_power_base(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel, u32 *ofdmbase, u32 *mcsbase) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 powerBase0, powerBase1; u8 legacy_pwrdiff = 0, ht20_pwrdiff = 0; u8 i, powerlevel[2]; for (i = 0; i < 2; i++) { powerlevel[i] = ppowerlevel[i]; legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff[i][channel - 1]; powerBase0 = powerlevel[i] + legacy_pwrdiff; powerBase0 = (powerBase0 << 24) | (powerBase0 << 16) | (powerBase0 << 8) | powerBase0; *(ofdmbase + i) = powerBase0; RTPRINT(rtlpriv, FPHY, PHY_TXPWR, " [OFDM power base index rf(%c) = 0x%x]\n", i == 0 ? 'A' : 'B', *(ofdmbase + i)); } for (i = 0; i < 2; i++) { if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) { ht20_pwrdiff = rtlefuse->txpwr_ht20diff[i][channel - 1]; powerlevel[i] += ht20_pwrdiff; } powerBase1 = powerlevel[i]; powerBase1 = (powerBase1 << 24) | (powerBase1 << 16) | (powerBase1 << 8) | powerBase1; *(mcsbase + i) = powerBase1; RTPRINT(rtlpriv, FPHY, PHY_TXPWR, " [MCS power base index rf(%c) = 0x%x]\n", i == 0 ? 'A' : 'B', *(mcsbase + i)); } } static void _rtl92c_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw, u8 channel, u8 index, u32 *powerBase0, u32 *powerBase1, u32 *p_outwriteval) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 i, chnlgroup = 0, pwr_diff_limit[4]; u32 writeVal, customer_limit, rf; for (rf = 0; rf < 2; rf++) { switch (rtlefuse->eeprom_regulatory) { case 0: chnlgroup = 0; writeVal = rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "RTK better performance,writeVal(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; case 1: if (rtlphy->pwrgroup_cnt == 1) chnlgroup = 0; if (rtlphy->pwrgroup_cnt >= 3) { if (channel <= 3) chnlgroup = 0; else if (channel >= 4 && channel <= 9) chnlgroup = 1; else if (channel > 9) chnlgroup = 2; if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) chnlgroup++; else chnlgroup += 4; } writeVal = rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Realtek regulatory, 20MHz, writeVal(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; case 2: writeVal = ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Better regulatory,writeVal(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; case 3: chnlgroup = 0; if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "customer's limit, 40MHzrf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', rtlefuse->pwrgroup_ht40[rf] [channel - 1]); } else { RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "customer's limit, 20MHz rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', rtlefuse->pwrgroup_ht20[rf] [channel - 1]); } for (i = 0; i < 4; i++) { pwr_diff_limit[i] = (u8) ((rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] & (0x7f << (i * 8))) >> (i * 8)); if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { if (pwr_diff_limit[i] > rtlefuse->pwrgroup_ht40[rf] [channel - 1]) pwr_diff_limit[i] = rtlefuse-> pwrgroup_ht40[rf] [channel - 1]; } else { if (pwr_diff_limit[i] > rtlefuse->pwrgroup_ht20[rf] [channel - 1]) pwr_diff_limit[i] = rtlefuse->pwrgroup_ht20[rf] [channel - 1]; } } customer_limit = (pwr_diff_limit[3] << 24) | (pwr_diff_limit[2] << 16) | (pwr_diff_limit[1] << 8) | (pwr_diff_limit[0]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Customer's limit rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', customer_limit); writeVal = customer_limit + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Customer, writeVal rf(%c)= 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; default: chnlgroup = 0; writeVal = rtlphy->mcs_txpwrlevel_origoffset[chnlgroup] [index + (rf ? 8 : 0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "RTK better performance, writeValrf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; } if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_LEVEL1) writeVal = 0x14141414; else if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_LEVEL2) writeVal = 0x00000000; if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1) writeVal = writeVal - 0x06060606; else if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT2) writeVal = writeVal; *(p_outwriteval + rf) = writeVal; } } static void _rtl92c_write_ofdm_power_reg(struct ieee80211_hw *hw, u8 index, u32 *pValue) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u16 regoffset_a[6] = { RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24, RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04, RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12 }; u16 regoffset_b[6] = { RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24, RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04, RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12 }; u8 i, rf, pwr_val[4]; u32 writeVal; u16 regoffset; for (rf = 0; rf < 2; rf++) { writeVal = pValue[rf]; for (i = 0; i < 4; i++) { pwr_val[i] = (u8)((writeVal & (0x7f << (i * 8))) >> (i * 8)); if (pwr_val[i] > RF6052_MAX_TX_PWR) pwr_val[i] = RF6052_MAX_TX_PWR; } writeVal = (pwr_val[3] << 24) | (pwr_val[2] << 16) | (pwr_val[1] << 8) | pwr_val[0]; if (rf == 0) regoffset = regoffset_a[index]; else regoffset = regoffset_b[index]; rtl_set_bbreg(hw, regoffset, MASKDWORD, writeVal); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Set 0x%x = %08x\n", regoffset, writeVal); if (((get_rf_type(rtlphy) == RF_2T2R) && (regoffset == RTXAGC_A_MCS15_MCS12 || regoffset == RTXAGC_B_MCS15_MCS12)) || ((get_rf_type(rtlphy) != RF_2T2R) && (regoffset == RTXAGC_A_MCS07_MCS04 || regoffset == RTXAGC_B_MCS07_MCS04))) { writeVal = pwr_val[3]; if (regoffset == RTXAGC_A_MCS15_MCS12 || regoffset == RTXAGC_A_MCS07_MCS04) regoffset = 0xc90; if (regoffset == RTXAGC_B_MCS15_MCS12 || regoffset == RTXAGC_B_MCS07_MCS04) regoffset = 0xc98; for (i = 0; i < 3; i++) { writeVal = (writeVal > 6) ? (writeVal - 6) : 0; rtl_write_byte(rtlpriv, (u32)(regoffset + i), (u8)writeVal); } } } } void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel) { u32 writeVal[2], powerBase0[2], powerBase1[2]; u8 index = 0; rtl92c_phy_get_power_base(hw, ppowerlevel, channel, &powerBase0[0], &powerBase1[0]); for (index = 0; index < 6; index++) { _rtl92c_get_txpower_writeval_by_regulatory(hw, channel, index, &powerBase0[0], &powerBase1[0], &writeVal[0]); _rtl92c_write_ofdm_power_reg(hw, index, &writeVal[0]); } } bool rtl92cu_phy_rf6052_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); bool rtstatus = true; u8 b_reg_hwparafile = 1; if (rtlphy->rf_type == RF_1T1R) rtlphy->num_total_rfpath = 1; else rtlphy->num_total_rfpath = 2; if (b_reg_hwparafile == 1) rtstatus = _rtl92c_phy_rf6052_config_parafile(hw); return rtstatus; } static bool _rtl92c_phy_rf6052_config_parafile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 u4_regvalue = 0; u8 rfpath; bool rtstatus = true; struct bb_reg_def *pphyreg; for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { pphyreg = &rtlphy->phyreg_def[rfpath]; switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV); break; case RF90_PATH_B: case RF90_PATH_D: u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16); break; } rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1); udelay(1); rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); udelay(1); rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREADDREAALENGTH, 0x0); udelay(1); rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0); udelay(1); switch (rfpath) { case RF90_PATH_A: rtstatus = rtl92cu_phy_config_rf_with_headerfile(hw, (enum radio_path) rfpath); break; case RF90_PATH_B: rtstatus = rtl92cu_phy_config_rf_with_headerfile(hw, (enum radio_path) rfpath); break; case RF90_PATH_C: break; case RF90_PATH_D: break; } switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV, u4_regvalue); break; case RF90_PATH_B: case RF90_PATH_D: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16, u4_regvalue); break; } if (!rtstatus) { RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio[%d] Fail!!", rfpath); goto phy_rf_cfg_fail; } } RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "<---\n"); return rtstatus; phy_rf_cfg_fail: return rtstatus; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/reg.h0000644000175000017500000000227012026211315024545 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../rtl8192ce/reg.h" compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/phy.h0000644000175000017500000000426512026211315024576 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../rtl8192ce/phy.h" void rtl92cu_bb_block_on(struct ieee80211_hw *hw); bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, u32 rfpath); void rtl92c_phy_set_io(struct ieee80211_hw *hw); bool _rtl92cu_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); bool rtl92cu_phy_bb_config(struct ieee80211_hw *hw); u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); void rtl92cu_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw); bool _rtl92cu_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype); void _rtl92cu_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); bool _rtl92cu_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype); void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw); bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c0000644000175000017500000004446512026211315024577 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../ps.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "table.h" u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 original_value, readback_value, bitshift; struct rtl_phy *rtlphy = &(rtlpriv->phy); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n", regaddr, rfpath, bitmask); if (rtlphy->rf_mode != RF_OP_BY_FW) { original_value = _rtl92c_phy_rf_serial_read(hw, rfpath, regaddr); } else { original_value = _rtl92c_phy_fw_rf_serial_read(hw, rfpath, regaddr); } bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); readback_value = (original_value & bitmask) >> bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x), original_value(%#x)\n", regaddr, rfpath, bitmask, original_value); return readback_value; } void rtl92cu_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 original_value, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); if (rtlphy->rf_mode != RF_OP_BY_FW) { if (bitmask != RFREG_OFFSET_MASK) { original_value = _rtl92c_phy_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } _rtl92c_phy_rf_serial_write(hw, rfpath, regaddr, data); } else { if (bitmask != RFREG_OFFSET_MASK) { original_value = _rtl92c_phy_fw_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } _rtl92c_phy_fw_rf_serial_write(hw, rfpath, regaddr, data); } RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); } bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw) { bool rtstatus; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool is92c = IS_92C_SERIAL(rtlhal->version); rtstatus = _rtl92cu_phy_config_mac_with_headerfile(hw); if (is92c && IS_HARDWARE_TYPE_8192CE(rtlhal)) rtl_write_byte(rtlpriv, 0x14, 0x71); return rtstatus; } bool rtl92cu_phy_bb_config(struct ieee80211_hw *hw) { bool rtstatus = true; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u16 regval; u8 b_reg_hwparafile = 1; _rtl92c_phy_init_bb_rf_register_definition(hw); regval = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, regval | BIT(13) | BIT(0) | BIT(1)); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x83); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL + 1, 0xdb); rtl_write_byte(rtlpriv, REG_RF_CTRL, RF_EN | RF_RSTB | RF_SDMRSTB); if (IS_HARDWARE_TYPE_8192CE(rtlhal)) { rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE | FEN_BB_GLB_RSTn | FEN_BBRSTB); } else if (IS_HARDWARE_TYPE_8192CU(rtlhal)) { rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, FEN_USBA | FEN_USBD | FEN_BB_GLB_RSTn | FEN_BBRSTB); rtl_write_byte(rtlpriv, REG_LDOHCI12_CTRL, 0x0f); } rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL + 1, 0x80); if (b_reg_hwparafile == 1) rtstatus = _rtl92c_phy_bb8192c_config_parafile(hw); return rtstatus; } bool _rtl92cu_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 i; u32 arraylength; u32 *ptrarray; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read Rtl819XMACPHY_Array\n"); arraylength = rtlphy->hwparam_tables[MAC_REG].length ; ptrarray = rtlphy->hwparam_tables[MAC_REG].pdata; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Img:RTL8192CEMAC_2T_ARRAY\n"); for (i = 0; i < arraylength; i = i + 2) rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]); return true; } bool _rtl92cu_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype) { int i; u32 *phy_regarray_table; u32 *agctab_array_table; u16 phy_reg_arraylen, agctab_arraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); if (IS_92C_SERIAL(rtlhal->version)) { agctab_arraylen = rtlphy->hwparam_tables[AGCTAB_2T].length; agctab_array_table = rtlphy->hwparam_tables[AGCTAB_2T].pdata; phy_reg_arraylen = rtlphy->hwparam_tables[PHY_REG_2T].length; phy_regarray_table = rtlphy->hwparam_tables[PHY_REG_2T].pdata; } else { agctab_arraylen = rtlphy->hwparam_tables[AGCTAB_1T].length; agctab_array_table = rtlphy->hwparam_tables[AGCTAB_1T].pdata; phy_reg_arraylen = rtlphy->hwparam_tables[PHY_REG_1T].length; phy_regarray_table = rtlphy->hwparam_tables[PHY_REG_1T].pdata; } if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_arraylen; i = i + 2) { if (phy_regarray_table[i] == 0xfe) mdelay(50); else if (phy_regarray_table[i] == 0xfd) mdelay(5); else if (phy_regarray_table[i] == 0xfc) mdelay(1); else if (phy_regarray_table[i] == 0xfb) udelay(50); else if (phy_regarray_table[i] == 0xfa) udelay(5); else if (phy_regarray_table[i] == 0xf9) udelay(1); rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD, phy_regarray_table[i + 1]); udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The phy_regarray_table[0] is %x Rtl819XPHY_REGArray[1] is %x\n", phy_regarray_table[i], phy_regarray_table[i + 1]); } } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], MASKDWORD, agctab_array_table[i + 1]); udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The agctab_array_table[0] is %x Rtl819XPHY_REGArray[1] is %x\n", agctab_array_table[i], agctab_array_table[i + 1]); } } return true; } bool _rtl92cu_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); int i; u32 *phy_regarray_table_pg; u16 phy_regarray_pg_len; rtlphy->pwrgroup_cnt = 0; phy_regarray_pg_len = rtlphy->hwparam_tables[PHY_REG_PG].length; phy_regarray_table_pg = rtlphy->hwparam_tables[PHY_REG_PG].pdata; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray_pg_len; i = i + 3) { if (phy_regarray_table_pg[i] == 0xfe) mdelay(50); else if (phy_regarray_table_pg[i] == 0xfd) mdelay(5); else if (phy_regarray_table_pg[i] == 0xfc) mdelay(1); else if (phy_regarray_table_pg[i] == 0xfb) udelay(50); else if (phy_regarray_table_pg[i] == 0xfa) udelay(5); else if (phy_regarray_table_pg[i] == 0xf9) udelay(1); _rtl92c_store_pwrIndex_diffrate_offset(hw, phy_regarray_table_pg[i], phy_regarray_table_pg[i + 1], phy_regarray_table_pg[i + 2]); } } else { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "configtype != BaseBand_Config_PHY_REG\n"); } return true; } bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath) { int i; u32 *radioa_array_table; u32 *radiob_array_table; u16 radioa_arraylen, radiob_arraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); if (IS_92C_SERIAL(rtlhal->version)) { radioa_arraylen = rtlphy->hwparam_tables[RADIOA_2T].length; radioa_array_table = rtlphy->hwparam_tables[RADIOA_2T].pdata; radiob_arraylen = rtlphy->hwparam_tables[RADIOB_2T].length; radiob_array_table = rtlphy->hwparam_tables[RADIOB_2T].pdata; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_A:RTL8192CERADIOA_2TARRAY\n"); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_B:RTL8192CE_RADIOB_2TARRAY\n"); } else { radioa_arraylen = rtlphy->hwparam_tables[RADIOA_1T].length; radioa_array_table = rtlphy->hwparam_tables[RADIOA_1T].pdata; radiob_arraylen = rtlphy->hwparam_tables[RADIOB_1T].length; radiob_array_table = rtlphy->hwparam_tables[RADIOB_1T].pdata; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_A:RTL8192CE_RADIOA_1TARRAY\n"); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_B:RTL8192CE_RADIOB_1TARRAY\n"); } RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio No %x\n", rfpath); switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radioa_arraylen; i = i + 2) { if (radioa_array_table[i] == 0xfe) mdelay(50); else if (radioa_array_table[i] == 0xfd) mdelay(5); else if (radioa_array_table[i] == 0xfc) mdelay(1); else if (radioa_array_table[i] == 0xfb) udelay(50); else if (radioa_array_table[i] == 0xfa) udelay(5); else if (radioa_array_table[i] == 0xf9) udelay(1); else { rtl_set_rfreg(hw, rfpath, radioa_array_table[i], RFREG_OFFSET_MASK, radioa_array_table[i + 1]); udelay(1); } } break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { if (radiob_array_table[i] == 0xfe) { mdelay(50); } else if (radiob_array_table[i] == 0xfd) mdelay(5); else if (radiob_array_table[i] == 0xfc) mdelay(1); else if (radiob_array_table[i] == 0xfb) udelay(50); else if (radiob_array_table[i] == 0xfa) udelay(5); else if (radiob_array_table[i] == 0xf9) udelay(1); else { rtl_set_rfreg(hw, rfpath, radiob_array_table[i], RFREG_OFFSET_MASK, radiob_array_table[i + 1]); udelay(1); } } break; case RF90_PATH_C: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; case RF90_PATH_D: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } return true; } void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u8 reg_bw_opmode; u8 reg_prsr_rsc; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "Switch to %s bandwidth\n", rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ? "20MHz" : "40MHz"); if (is_hal_stop(rtlhal)) { rtlphy->set_bwmode_inprogress = false; return; } reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE); reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2); switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: reg_bw_opmode |= BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); break; case HT_CHANNEL_WIDTH_20_40: reg_bw_opmode &= ~BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); reg_prsr_rsc = (reg_prsr_rsc & 0x90) | (mac->cur_40_prime_sc << 5); rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); break; case HT_CHANNEL_WIDTH_20_40: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1); rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCK_SIDEBAND, (mac->cur_40_prime_sc >> 1)); rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 0); rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)), (mac->cur_40_prime_sc == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } rtl92cu_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); rtlphy->set_bwmode_inprogress = false; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); } void rtl92cu_bb_block_on(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); mutex_lock(&rtlpriv->io.bb_mutex); rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1); mutex_unlock(&rtlpriv->io.bb_mutex); } void _rtl92cu_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) { u8 tmpreg; u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal; struct rtl_priv *rtlpriv = rtl_priv(hw); tmpreg = rtl_read_byte(rtlpriv, 0xd03); if ((tmpreg & 0x70) != 0) rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F); else rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); if ((tmpreg & 0x70) != 0) { rf_a_mode = rtl_get_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS); if (is2t) rf_b_mode = rtl_get_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, (rf_a_mode & 0x8FFFF) | 0x10000); if (is2t) rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS, (rf_b_mode & 0x8FFFF) | 0x10000); } lc_cal = rtl_get_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS); rtl_set_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS, lc_cal | 0x08000); mdelay(100); if ((tmpreg & 0x70) != 0) { rtl_write_byte(rtlpriv, 0xd03, tmpreg); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, rf_a_mode); if (is2t) rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS, rf_b_mode); } else { rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); } } static bool _rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool bresult = true; u8 i, queue_id; struct rtl8192_tx_ring *ring = NULL; switch (rfpwr_state) { case ERFON: if ((ppsc->rfpwr_state == ERFOFF) && RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) { bool rtstatus; u32 InitializeCount = 0; do { InitializeCount++; RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic enable\n"); rtstatus = rtl_ps_enable_nic(hw); } while (!rtstatus && (InitializeCount < 10)); RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "Set ERFON sleeped:%d ms\n", jiffies_to_msecs(jiffies - ppsc->last_sleep_jiffies)); ppsc->last_awake_jiffies = jiffies; rtl92ce_phy_set_rf_on(hw); } if (mac->link_state == MAC80211_LINKED) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_LINK); } else { rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); } break; case ERFOFF: for (queue_id = 0, i = 0; queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { ring = &pcipriv->dev.tx_ring[queue_id]; if (skb_queue_len(&ring->queue) == 0 || queue_id == BEACON_QUEUE) { queue_id++; continue; } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "eRf Off/Sleep: %d times TcbBusyQueue[%d] =%d before doze!\n", i + 1, queue_id, skb_queue_len(&ring->queue)); udelay(10); i++; } if (i >= MAX_DOZE_WAITING_TIMES_9x) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "ERFOFF: %d times TcbBusyQueue[%d] = %d !\n", MAX_DOZE_WAITING_TIMES_9x, queue_id, skb_queue_len(&ring->queue)); break; } } if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic disable\n"); rtl_ps_disable_nic(hw); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); } else { rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); } } break; case ERFSLEEP: if (ppsc->rfpwr_state == ERFOFF) return false; for (queue_id = 0, i = 0; queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { ring = &pcipriv->dev.tx_ring[queue_id]; if (skb_queue_len(&ring->queue) == 0) { queue_id++; continue; } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "eRf Off/Sleep: %d times TcbBusyQueue[%d] =%d before doze!\n", i + 1, queue_id, skb_queue_len(&ring->queue)); udelay(10); i++; } if (i >= MAX_DOZE_WAITING_TIMES_9x) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "ERFSLEEP: %d times TcbBusyQueue[%d] = %d !\n", MAX_DOZE_WAITING_TIMES_9x, queue_id, skb_queue_len(&ring->queue)); break; } } RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "Set ERFSLEEP awaked:%d ms\n", jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies)); ppsc->last_sleep_jiffies = jiffies; _rtl92c_phy_set_rf_sleep(hw); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); bresult = false; break; } if (bresult) ppsc->rfpwr_state = rfpwr_state; return bresult; } bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool bresult = false; if (rfpwr_state == ppsc->rfpwr_state) return bresult; bresult = _rtl92cu_phy_set_rf_power_state(hw, rfpwr_state); return bresult; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h0000644000175000017500000001206712026211315024535 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_MAC_H__ #define __RTL92C_MAC_H__ #define LLT_LAST_ENTRY_OF_TX_PKT_BUFFER 255 #define DRIVER_EARLY_INT_TIME 0x05 #define BCN_DMA_ATIME_INT_TIME 0x02 void rtl92c_read_chip_version(struct ieee80211_hw *hw); bool rtl92c_llt_write(struct ieee80211_hw *hw, u32 address, u32 data); bool rtl92c_init_llt_table(struct ieee80211_hw *hw, u32 boundary); void rtl92c_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); void rtl92c_enable_interrupt(struct ieee80211_hw *hw); void rtl92c_disable_interrupt(struct ieee80211_hw *hw); void rtl92c_set_qos(struct ieee80211_hw *hw, int aci); /*--------------------------------------------------------------- * Hardware init functions *---------------------------------------------------------------*/ void rtl92c_set_mac_addr(struct ieee80211_hw *hw, const u8 *addr); void rtl92c_init_interrupt(struct ieee80211_hw *hw); void rtl92c_init_driver_info_size(struct ieee80211_hw *hw, u8 size); int rtl92c_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type); void rtl92c_init_network_type(struct ieee80211_hw *hw); void rtl92c_init_adaptive_ctrl(struct ieee80211_hw *hw); void rtl92c_init_rate_fallback(struct ieee80211_hw *hw); void rtl92c_init_edca_param(struct ieee80211_hw *hw, u16 queue, u16 txop, u8 ecwmax, u8 ecwmin, u8 aifs); void rtl92c_init_edca(struct ieee80211_hw *hw); void rtl92c_init_ampdu_aggregation(struct ieee80211_hw *hw); void rtl92c_init_beacon_max_error(struct ieee80211_hw *hw, bool infra_mode); void rtl92c_init_rdg_setting(struct ieee80211_hw *hw); void rtl92c_init_retry_function(struct ieee80211_hw *hw); void rtl92c_init_beacon_parameters(struct ieee80211_hw *hw, enum version_8192c version); void rtl92c_disable_fast_edca(struct ieee80211_hw *hw); void rtl92c_set_min_space(struct ieee80211_hw *hw, bool is2T); /* For filter */ u16 rtl92c_get_mgt_filter(struct ieee80211_hw *hw); void rtl92c_set_mgt_filter(struct ieee80211_hw *hw, u16 filter); u16 rtl92c_get_ctrl_filter(struct ieee80211_hw *hw); void rtl92c_set_ctrl_filter(struct ieee80211_hw *hw, u16 filter); u16 rtl92c_get_data_filter(struct ieee80211_hw *hw); void rtl92c_set_data_filter(struct ieee80211_hw *hw, u16 filter); u32 rtl92c_get_txdma_status(struct ieee80211_hw *hw); struct rx_fwinfo_92c { u8 gain_trsw[4]; u8 pwdb_all; u8 cfosho[4]; u8 cfotail[4]; char rxevm[2]; char rxsnr[4]; u8 pdsnr[2]; u8 csi_current[2]; u8 csi_target[2]; u8 sigevm; u8 max_ex_pwr; u8 ex_intf_flag:1; u8 sgi_en:1; u8 rxsc:2; u8 reserve:4; } __packed; struct rx_desc_92c { u32 length:14; u32 crc32:1; u32 icverror:1; u32 drv_infosize:4; u32 security:3; u32 qos:1; u32 shift:2; u32 phystatus:1; u32 swdec:1; u32 lastseg:1; u32 firstseg:1; u32 eor:1; u32 own:1; u32 macid:5; /* word 1 */ u32 tid:4; u32 hwrsvd:5; u32 paggr:1; u32 faggr:1; u32 a1_fit:4; u32 a2_fit:4; u32 pam:1; u32 pwr:1; u32 moredata:1; u32 morefrag:1; u32 type:2; u32 mc:1; u32 bc:1; u32 seq:12; /* word 2 */ u32 frag:4; u32 nextpktlen:14; u32 nextind:1; u32 rsvd:1; u32 rxmcs:6; /* word 3 */ u32 rxht:1; u32 amsdu:1; u32 splcp:1; u32 bandwidth:1; u32 htc:1; u32 tcpchk_rpt:1; u32 ipcchk_rpt:1; u32 tcpchk_valid:1; u32 hwpcerr:1; u32 hwpcind:1; u32 iv0:16; u32 iv1; /* word 4 */ u32 tsfl; /* word 5 */ u32 bufferaddress; /* word 6 */ u32 bufferaddress64; /* word 7 */ } __packed; enum rtl_desc_qsel rtl92c_map_hwqueue_to_fwqueue(u16 fc, unsigned int skb_queue); void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_stats *pstats, struct rx_desc_92c *pdesc, struct rx_fwinfo_92c *p_drvinfo); /*--------------------------------------------------------------- * Card disable functions *---------------------------------------------------------------*/ #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c0000644000175000017500000007675212026211315024543 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * ****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../usb.h" #include "../ps.h" #include "../cam.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "mac.h" #include "trx.h" #include /* macro to shorten lines */ #define LINK_Q ui_link_quality #define RX_EVM rx_evm_percentage #define RX_SIGQ rx_mimo_signalquality void rtl92c_read_chip_version(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); enum version_8192c chip_version = VERSION_UNKNOWN; const char *versionid; u32 value32; value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG); if (value32 & TRP_VAUX_EN) { chip_version = (value32 & TYPE_ID) ? VERSION_TEST_CHIP_92C : VERSION_TEST_CHIP_88C; } else { /* Normal mass production chip. */ chip_version = NORMAL_CHIP; chip_version |= ((value32 & TYPE_ID) ? CHIP_92C : 0); chip_version |= ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0); /* RTL8723 with BT function. */ chip_version |= ((value32 & BT_FUNC) ? CHIP_8723 : 0); if (IS_VENDOR_UMC(chip_version)) chip_version |= ((value32 & CHIP_VER_RTL_MASK) ? CHIP_VENDOR_UMC_B_CUT : 0); if (IS_92C_SERIAL(chip_version)) { value32 = rtl_read_dword(rtlpriv, REG_HPON_FSM); chip_version |= ((CHIP_BONDING_IDENTIFIER(value32) == CHIP_BONDING_92C_1T2R) ? CHIP_92C_1T2R : 0); } else if (IS_8723_SERIES(chip_version)) { value32 = rtl_read_dword(rtlpriv, REG_GPIO_OUTSTS); chip_version |= ((value32 & RF_RL_ID) ? CHIP_8723_DRV_REV : 0); } } rtlhal->version = (enum version_8192c)chip_version; pr_info("Chip version 0x%x\n", chip_version); switch (rtlhal->version) { case VERSION_NORMAL_TSMC_CHIP_92C_1T2R: versionid = "NORMAL_B_CHIP_92C"; break; case VERSION_NORMAL_TSMC_CHIP_92C: versionid = "NORMAL_TSMC_CHIP_92C"; break; case VERSION_NORMAL_TSMC_CHIP_88C: versionid = "NORMAL_TSMC_CHIP_88C"; break; case VERSION_NORMAL_UMC_CHIP_92C_1T2R_A_CUT: versionid = "NORMAL_UMC_CHIP_i92C_1T2R_A_CUT"; break; case VERSION_NORMAL_UMC_CHIP_92C_A_CUT: versionid = "NORMAL_UMC_CHIP_92C_A_CUT"; break; case VERSION_NORMAL_UMC_CHIP_88C_A_CUT: versionid = "NORMAL_UMC_CHIP_88C_A_CUT"; break; case VERSION_NORMAL_UMC_CHIP_92C_1T2R_B_CUT: versionid = "NORMAL_UMC_CHIP_92C_1T2R_B_CUT"; break; case VERSION_NORMAL_UMC_CHIP_92C_B_CUT: versionid = "NORMAL_UMC_CHIP_92C_B_CUT"; break; case VERSION_NORMAL_UMC_CHIP_88C_B_CUT: versionid = "NORMAL_UMC_CHIP_88C_B_CUT"; break; case VERSION_NORMA_UMC_CHIP_8723_1T1R_A_CUT: versionid = "NORMAL_UMC_CHIP_8723_1T1R_A_CUT"; break; case VERSION_NORMA_UMC_CHIP_8723_1T1R_B_CUT: versionid = "NORMAL_UMC_CHIP_8723_1T1R_B_CUT"; break; case VERSION_TEST_CHIP_92C: versionid = "TEST_CHIP_92C"; break; case VERSION_TEST_CHIP_88C: versionid = "TEST_CHIP_88C"; break; default: versionid = "UNKNOWN"; break; } RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Chip Version ID: %s\n", versionid); if (IS_92C_SERIAL(rtlhal->version)) rtlphy->rf_type = (IS_92C_1T2R(rtlhal->version)) ? RF_1T2R : RF_2T2R; else rtlphy->rf_type = RF_1T1R; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Chip RF Type: %s\n", rtlphy->rf_type == RF_2T2R ? "RF_2T2R" : "RF_1T1R"); if (get_rf_type(rtlphy) == RF_1T1R) rtlpriv->dm.rfpath_rxenable[0] = true; else rtlpriv->dm.rfpath_rxenable[0] = rtlpriv->dm.rfpath_rxenable[1] = true; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "VersionID = 0x%4x\n", rtlhal->version); } /** * writeLLT - LLT table write access * @io: io callback * @address: LLT logical address. * @data: LLT data content * * Realtek hardware access function. * */ bool rtl92c_llt_write(struct ieee80211_hw *hw, u32 address, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); bool status = true; long count = 0; u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) | _LLT_OP(_LLT_WRITE_ACCESS); rtl_write_dword(rtlpriv, REG_LLT_INIT, value); do { value = rtl_read_dword(rtlpriv, REG_LLT_INIT); if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value)) break; if (count > POLLING_LLT_THRESHOLD) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to polling write LLT done at address %d! _LLT_OP_VALUE(%x)\n", address, _LLT_OP_VALUE(value)); status = false; break; } } while (++count); return status; } /** * rtl92c_init_LLT_table - Init LLT table * @io: io callback * @boundary: * * Realtek hardware access function. * */ bool rtl92c_init_llt_table(struct ieee80211_hw *hw, u32 boundary) { bool rst = true; u32 i; for (i = 0; i < (boundary - 1); i++) { rst = rtl92c_llt_write(hw, i , i + 1); if (true != rst) { pr_err("===> %s #1 fail\n", __func__); return rst; } } /* end of list */ rst = rtl92c_llt_write(hw, (boundary - 1), 0xFF); if (true != rst) { pr_err("===> %s #2 fail\n", __func__); return rst; } /* Make the other pages as ring buffer * This ring buffer is used as beacon buffer if we config this MAC * as two MAC transfer. * Otherwise used as local loopback buffer. */ for (i = boundary; i < LLT_LAST_ENTRY_OF_TX_PKT_BUFFER; i++) { rst = rtl92c_llt_write(hw, i, (i + 1)); if (true != rst) { pr_err("===> %s #3 fail\n", __func__); return rst; } } /* Let last entry point to the start entry of ring buffer */ rst = rtl92c_llt_write(hw, LLT_LAST_ENTRY_OF_TX_PKT_BUFFER, boundary); if (true != rst) { pr_err("===> %s #4 fail\n", __func__); return rst; } return rst; } void rtl92c_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 *macaddr = p_macaddr; u32 entry_id = 0; bool is_pairwise = false; static u8 cam_const_addr[4][6] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} }; static u8 cam_const_broad[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (clear_all) { u8 idx = 0; u8 cam_offset = 0; u8 clear_number = 5; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n"); for (idx = 0; idx < clear_number; idx++) { rtl_cam_mark_invalid(hw, cam_offset + idx); rtl_cam_empty_entry(hw, cam_offset + idx); if (idx < 5) { memset(rtlpriv->sec.key_buf[idx], 0, MAX_KEY_LEN); rtlpriv->sec.key_len[idx] = 0; } } } else { switch (enc_algo) { case WEP40_ENCRYPTION: enc_algo = CAM_WEP40; break; case WEP104_ENCRYPTION: enc_algo = CAM_WEP104; break; case TKIP_ENCRYPTION: enc_algo = CAM_TKIP; break; case AESCCMP_ENCRYPTION: enc_algo = CAM_AES; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "illegal switch case\n"); enc_algo = CAM_TKIP; break; } if (is_wepkey || rtlpriv->sec.use_defaultkey) { macaddr = cam_const_addr[key_index]; entry_id = key_index; } else { if (is_group) { macaddr = cam_const_broad; entry_id = key_index; } else { key_index = PAIRWISE_KEYIDX; entry_id = CAM_PAIRWISE_KEY_POSITION; is_pairwise = true; } } if (rtlpriv->sec.key_len[key_index] == 0) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "delete one entry\n"); rtl_cam_delete_one_entry(hw, p_macaddr, entry_id); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY length is %d\n", rtlpriv->sec.key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY is %x %x\n", rtlpriv->sec.key_buf[0][0], rtlpriv->sec.key_buf[0][1]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "add one entry\n"); if (is_pairwise) { RT_PRINT_DATA(rtlpriv, COMP_SEC, DBG_LOUD, "Pairwise Key content", rtlpriv->sec.pairwise_key, rtlpriv->sec. key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set Pairwise key\n"); rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec. key_buf[key_index]); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set group key\n"); if (mac->opmode == NL80211_IFTYPE_ADHOC) { rtl_cam_add_one_entry(hw, rtlefuse->dev_addr, PAIRWISE_KEYIDX, CAM_PAIRWISE_KEY_POSITION, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf [entry_id]); } rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf[entry_id]); } } } } u32 rtl92c_get_txdma_status(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); return rtl_read_dword(rtlpriv, REG_TXDMA_STATUS); } void rtl92c_enable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); if (IS_HARDWARE_TYPE_8192CE(rtlhal)) { rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF); } else { rtl_write_dword(rtlpriv, REG_HIMR, rtlusb->irq_mask[0] & 0xFFFFFFFF); rtl_write_dword(rtlpriv, REG_HIMRE, rtlusb->irq_mask[1] & 0xFFFFFFFF); } } void rtl92c_init_interrupt(struct ieee80211_hw *hw) { rtl92c_enable_interrupt(hw); } void rtl92c_disable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED); rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED); } void rtl92c_set_qos(struct ieee80211_hw *hw, int aci) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u32 u4b_ac_param; rtl92c_dm_init_edca_turbo(hw); u4b_ac_param = (u32) mac->ac[aci].aifs; u4b_ac_param |= ((u32) le16_to_cpu(mac->ac[aci].cw_min) & 0xF) << AC_PARAM_ECW_MIN_OFFSET; u4b_ac_param |= ((u32) le16_to_cpu(mac->ac[aci].cw_max) & 0xF) << AC_PARAM_ECW_MAX_OFFSET; u4b_ac_param |= (u32) le16_to_cpu(mac->ac[aci].tx_op) << AC_PARAM_TXOP_OFFSET; RT_TRACE(rtlpriv, COMP_QOS, DBG_LOUD, "queue:%x, ac_param:%x\n", aci, u4b_ac_param); switch (aci) { case AC1_BK: rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, u4b_ac_param); break; case AC0_BE: rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, u4b_ac_param); break; case AC2_VI: rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, u4b_ac_param); break; case AC3_VO: rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, u4b_ac_param); break; default: RT_ASSERT(false, "invalid aci: %d !\n", aci); break; } } /*------------------------------------------------------------------------- * HW MAC Address *-------------------------------------------------------------------------*/ void rtl92c_set_mac_addr(struct ieee80211_hw *hw, const u8 *addr) { u32 i; struct rtl_priv *rtlpriv = rtl_priv(hw); for (i = 0 ; i < ETH_ALEN ; i++) rtl_write_byte(rtlpriv, (REG_MACID + i), *(addr+i)); RT_TRACE(rtlpriv, COMP_CMD, DBG_DMESG, "MAC Address: %02X-%02X-%02X-%02X-%02X-%02X\n", rtl_read_byte(rtlpriv, REG_MACID), rtl_read_byte(rtlpriv, REG_MACID+1), rtl_read_byte(rtlpriv, REG_MACID+2), rtl_read_byte(rtlpriv, REG_MACID+3), rtl_read_byte(rtlpriv, REG_MACID+4), rtl_read_byte(rtlpriv, REG_MACID+5)); } void rtl92c_init_driver_info_size(struct ieee80211_hw *hw, u8 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, size); } int rtl92c_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type) { u8 value; struct rtl_priv *rtlpriv = rtl_priv(hw); switch (type) { case NL80211_IFTYPE_UNSPECIFIED: value = NT_NO_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: value = NT_LINK_AD_HOC; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: value = NT_LINK_AP; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: value = NT_AS_AP; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Set Network type to AP!\n"); break; default: RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Network type %d not supported!\n", type); return -EOPNOTSUPP; } rtl_write_byte(rtlpriv, (REG_CR + 2), value); return 0; } void rtl92c_init_network_type(struct ieee80211_hw *hw) { rtl92c_set_network_type(hw, NL80211_IFTYPE_UNSPECIFIED); } void rtl92c_init_adaptive_ctrl(struct ieee80211_hw *hw) { u16 value16; u32 value32; struct rtl_priv *rtlpriv = rtl_priv(hw); /* Response Rate Set */ value32 = rtl_read_dword(rtlpriv, REG_RRSR); value32 &= ~RATE_BITMAP_ALL; value32 |= RATE_RRSR_CCK_ONLY_1M; rtl_write_dword(rtlpriv, REG_RRSR, value32); /* SIFS (used in NAV) */ value16 = _SPEC_SIFS_CCK(0x10) | _SPEC_SIFS_OFDM(0x10); rtl_write_word(rtlpriv, REG_SPEC_SIFS, value16); /* Retry Limit */ value16 = _LRL(0x30) | _SRL(0x30); rtl_write_dword(rtlpriv, REG_RL, value16); } void rtl92c_init_rate_fallback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /* Set Data Auto Rate Fallback Retry Count register. */ rtl_write_dword(rtlpriv, REG_DARFRC, 0x00000000); rtl_write_dword(rtlpriv, REG_DARFRC+4, 0x10080404); rtl_write_dword(rtlpriv, REG_RARFRC, 0x04030201); rtl_write_dword(rtlpriv, REG_RARFRC+4, 0x08070605); } static void rtl92c_set_cck_sifs(struct ieee80211_hw *hw, u8 trx_sifs, u8 ctx_sifs) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_SIFS_CCK, trx_sifs); rtl_write_byte(rtlpriv, (REG_SIFS_CCK + 1), ctx_sifs); } static void rtl92c_set_ofdm_sifs(struct ieee80211_hw *hw, u8 trx_sifs, u8 ctx_sifs) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_SIFS_OFDM, trx_sifs); rtl_write_byte(rtlpriv, (REG_SIFS_OFDM + 1), ctx_sifs); } void rtl92c_init_edca_param(struct ieee80211_hw *hw, u16 queue, u16 txop, u8 cw_min, u8 cw_max, u8 aifs) { /* sequence: VO, VI, BE, BK ==> the same as 92C hardware design. * referenc : enum nl80211_txq_q or ieee80211_set_wmm_default function. */ u32 value; struct rtl_priv *rtlpriv = rtl_priv(hw); value = (u32)aifs; value |= ((u32)cw_min & 0xF) << 8; value |= ((u32)cw_max & 0xF) << 12; value |= (u32)txop << 16; /* 92C hardware register sequence is the same as queue number. */ rtl_write_dword(rtlpriv, (REG_EDCA_VO_PARAM + (queue * 4)), value); } void rtl92c_init_edca(struct ieee80211_hw *hw) { u16 value16; struct rtl_priv *rtlpriv = rtl_priv(hw); /* disable EDCCA count down, to reduce collison and retry */ value16 = rtl_read_word(rtlpriv, REG_RD_CTRL); value16 |= DIS_EDCA_CNT_DWN; rtl_write_word(rtlpriv, REG_RD_CTRL, value16); /* Update SIFS timing. ?????????? * pHalData->SifsTime = 0x0e0e0a0a; */ rtl92c_set_cck_sifs(hw, 0xa, 0xa); rtl92c_set_ofdm_sifs(hw, 0xe, 0xe); /* Set CCK/OFDM SIFS to be 10us. */ rtl_write_word(rtlpriv, REG_SIFS_CCK, 0x0a0a); rtl_write_word(rtlpriv, REG_SIFS_OFDM, 0x1010); rtl_write_word(rtlpriv, REG_PROT_MODE_CTRL, 0x0204); rtl_write_dword(rtlpriv, REG_BAR_MODE_CTRL, 0x014004); /* TXOP */ rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, 0x005EA42B); rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0x0000A44F); rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x005EA324); rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x002FA226); /* PIFS */ rtl_write_byte(rtlpriv, REG_PIFS, 0x1C); /* AGGR BREAK TIME Register */ rtl_write_byte(rtlpriv, REG_AGGR_BREAK_TIME, 0x16); rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0040); rtl_write_byte(rtlpriv, REG_BCNDMATIM, 0x02); rtl_write_byte(rtlpriv, REG_ATIMWND, 0x02); } void rtl92c_init_ampdu_aggregation(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0x99997631); rtl_write_byte(rtlpriv, REG_AGGR_BREAK_TIME, 0x16); /* init AMPDU aggregation number, tuning for Tx's TP, */ rtl_write_word(rtlpriv, 0x4CA, 0x0708); } void rtl92c_init_beacon_max_error(struct ieee80211_hw *hw, bool infra_mode) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0xFF); } void rtl92c_init_rdg_setting(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_RD_CTRL, 0xFF); rtl_write_word(rtlpriv, REG_RD_NAV_NXT, 0x200); rtl_write_byte(rtlpriv, REG_RD_RESP_PKT_TH, 0x05); } void rtl92c_init_retry_function(struct ieee80211_hw *hw) { u8 value8; struct rtl_priv *rtlpriv = rtl_priv(hw); value8 = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL); value8 |= EN_AMPDU_RTY_NEW; rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL, value8); /* Set ACK timeout */ rtl_write_byte(rtlpriv, REG_ACKTO, 0x40); } void rtl92c_init_beacon_parameters(struct ieee80211_hw *hw, enum version_8192c version) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); rtl_write_word(rtlpriv, REG_TBTT_PROHIBIT, 0x6404);/* ms */ rtl_write_byte(rtlpriv, REG_DRVERLYINT, DRIVER_EARLY_INT_TIME);/*ms*/ rtl_write_byte(rtlpriv, REG_BCNDMATIM, BCN_DMA_ATIME_INT_TIME); if (IS_NORMAL_CHIP(rtlhal->version)) rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660F); else rtl_write_word(rtlpriv, REG_BCNTCFG, 0x66FF); } void rtl92c_disable_fast_edca(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_word(rtlpriv, REG_FAST_EDCA_CTRL, 0); } void rtl92c_set_min_space(struct ieee80211_hw *hw, bool is2T) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 value = is2T ? MAX_MSS_DENSITY_2T : MAX_MSS_DENSITY_1T; rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, value); } u16 rtl92c_get_mgt_filter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); return rtl_read_word(rtlpriv, REG_RXFLTMAP0); } void rtl92c_set_mgt_filter(struct ieee80211_hw *hw, u16 filter) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_word(rtlpriv, REG_RXFLTMAP0, filter); } u16 rtl92c_get_ctrl_filter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); return rtl_read_word(rtlpriv, REG_RXFLTMAP1); } void rtl92c_set_ctrl_filter(struct ieee80211_hw *hw, u16 filter) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_word(rtlpriv, REG_RXFLTMAP1, filter); } u16 rtl92c_get_data_filter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); return rtl_read_word(rtlpriv, REG_RXFLTMAP2); } void rtl92c_set_data_filter(struct ieee80211_hw *hw, u16 filter) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_word(rtlpriv, REG_RXFLTMAP2, filter); } /*==============================================================*/ static u8 _rtl92c_query_rxpwrpercentage(char antpower) { if ((antpower <= -100) || (antpower >= 20)) return 0; else if (antpower >= 0) return 100; else return 100 + antpower; } static u8 _rtl92c_evm_db_to_percentage(char value) { char ret_val; ret_val = value; if (ret_val >= 0) ret_val = 0; if (ret_val <= -33) ret_val = -33; ret_val = 0 - ret_val; ret_val *= 3; if (ret_val == 99) ret_val = 100; return ret_val; } static long _rtl92c_translate_todbm(struct ieee80211_hw *hw, u8 signal_strength_index) { long signal_power; signal_power = (long)((signal_strength_index + 1) >> 1); signal_power -= 95; return signal_power; } static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { long retsig; if (currsig >= 61 && currsig <= 100) retsig = 90 + ((currsig - 60) / 4); else if (currsig >= 41 && currsig <= 60) retsig = 78 + ((currsig - 40) / 2); else if (currsig >= 31 && currsig <= 40) retsig = 66 + (currsig - 30); else if (currsig >= 21 && currsig <= 30) retsig = 54 + (currsig - 20); else if (currsig >= 5 && currsig <= 20) retsig = 42 + (((currsig - 5) * 2) / 3); else if (currsig == 4) retsig = 36; else if (currsig == 3) retsig = 27; else if (currsig == 2) retsig = 18; else if (currsig == 1) retsig = 9; else retsig = currsig; return retsig; } static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, struct rx_desc_92c *pdesc, struct rx_fwinfo_92c *p_drvinfo, bool packet_match_bssid, bool packet_toself, bool packet_beacon) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct phy_sts_cck_8192s_t *cck_buf; s8 rx_pwr_all = 0, rx_pwr[4]; u8 rf_rx_num = 0, evm, pwdb_all; u8 i, max_spatial_stream; u32 rssi, total_rssi = 0; bool in_powersavemode = false; bool is_cck_rate; is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc); pstats->packet_matchbssid = packet_match_bssid; pstats->packet_toself = packet_toself; pstats->is_cck = is_cck_rate; pstats->packet_beacon = packet_beacon; pstats->is_cck = is_cck_rate; pstats->RX_SIGQ[0] = -1; pstats->RX_SIGQ[1] = -1; if (is_cck_rate) { u8 report, cck_highpwr; cck_buf = (struct phy_sts_cck_8192s_t *)p_drvinfo; if (!in_powersavemode) cck_highpwr = rtlphy->cck_high_power; else cck_highpwr = false; if (!cck_highpwr) { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = cck_buf->cck_agc_rpt & 0xc0; report = report >> 6; switch (report) { case 0x3: rx_pwr_all = -46 - (cck_agc_rpt & 0x3e); break; case 0x2: rx_pwr_all = -26 - (cck_agc_rpt & 0x3e); break; case 0x1: rx_pwr_all = -12 - (cck_agc_rpt & 0x3e); break; case 0x0: rx_pwr_all = 16 - (cck_agc_rpt & 0x3e); break; } } else { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = p_drvinfo->cfosho[0] & 0x60; report = report >> 5; switch (report) { case 0x3: rx_pwr_all = -46 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x2: rx_pwr_all = -26 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x1: rx_pwr_all = -12 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x0: rx_pwr_all = 16 - ((cck_agc_rpt & 0x1f) << 1); break; } } pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->recvsignalpower = rx_pwr_all; if (packet_match_bssid) { u8 sq; if (pstats->rx_pwdb_all > 40) sq = 100; else { sq = cck_buf->sq_rpt; if (sq > 64) sq = 0; else if (sq < 20) sq = 100; else sq = ((64 - sq) * 100) / 44; } pstats->signalquality = sq; pstats->RX_SIGQ[0] = sq; pstats->RX_SIGQ[1] = -1; } } else { rtlpriv->dm.rfpath_rxenable[0] = rtlpriv->dm.rfpath_rxenable[1] = true; for (i = RF90_PATH_A; i < RF90_PATH_MAX; i++) { if (rtlpriv->dm.rfpath_rxenable[i]) rf_rx_num++; rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110; rssi = _rtl92c_query_rxpwrpercentage(rx_pwr[i]); total_rssi += rssi; rtlpriv->stats.rx_snr_db[i] = (long)(p_drvinfo->rxsnr[i] / 2); if (packet_match_bssid) pstats->rx_mimo_signalstrength[i] = (u8) rssi; } rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110; pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; if (GET_RX_DESC_RX_MCS(pdesc) && GET_RX_DESC_RX_MCS(pdesc) >= DESC92_RATEMCS8 && GET_RX_DESC_RX_MCS(pdesc) <= DESC92_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; for (i = 0; i < max_spatial_stream; i++) { evm = _rtl92c_evm_db_to_percentage(p_drvinfo->rxevm[i]); if (packet_match_bssid) { if (i == 0) pstats->signalquality = (u8) (evm & 0xff); pstats->RX_SIGQ[i] = (u8) (evm & 0xff); } } } if (is_cck_rate) pstats->signalstrength = (u8) (_rtl92c_signal_scale_mapping(hw, pwdb_all)); else if (rf_rx_num != 0) pstats->signalstrength = (u8) (_rtl92c_signal_scale_mapping (hw, total_rssi /= rf_rx_num)); } static void _rtl92c_process_ui_rssi(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 rfpath; u32 last_rssi, tmpval; if (pstats->packet_toself || pstats->packet_beacon) { rtlpriv->stats.rssi_calculate_cnt++; if (rtlpriv->stats.ui_rssi.total_num++ >= PHY_RSSI_SLID_WIN_MAX) { rtlpriv->stats.ui_rssi.total_num = PHY_RSSI_SLID_WIN_MAX; last_rssi = rtlpriv->stats.ui_rssi.elements[rtlpriv-> stats.ui_rssi.index]; rtlpriv->stats.ui_rssi.total_val -= last_rssi; } rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength; rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi. index++] = pstats->signalstrength; if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX) rtlpriv->stats.ui_rssi.index = 0; tmpval = rtlpriv->stats.ui_rssi.total_val / rtlpriv->stats.ui_rssi.total_num; rtlpriv->stats.signal_strength = _rtl92c_translate_todbm(hw, (u8) tmpval); pstats->rssi = rtlpriv->stats.signal_strength; } if (!pstats->is_cck && pstats->packet_toself) { for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { if (!rtl8192_phy_check_is_legal_rfpath(hw, rfpath)) continue; if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) { rtlpriv->stats.rx_rssi_percentage[rfpath] = pstats->rx_mimo_signalstrength[rfpath]; } if (pstats->rx_mimo_signalstrength[rfpath] > rtlpriv->stats.rx_rssi_percentage[rfpath]) { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats. rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); rtlpriv->stats.rx_rssi_percentage[rfpath] = rtlpriv->stats.rx_rssi_percentage[rfpath] + 1; } else { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats. rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); } } } } static void _rtl92c_update_rxsignalstatistics(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); int weighting = 0; if (rtlpriv->stats.recv_signal_power == 0) rtlpriv->stats.recv_signal_power = pstats->recvsignalpower; if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power) weighting = 5; else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power) weighting = (-5); rtlpriv->stats.recv_signal_power = (rtlpriv->stats.recv_signal_power * 5 + pstats->recvsignalpower + weighting) / 6; } static void _rtl92c_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb = 0; if (mac->opmode == NL80211_IFTYPE_ADHOC) { return; } else { undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; } if (pstats->packet_toself || pstats->packet_beacon) { if (undecorated_smoothed_pwdb < 0) undecorated_smoothed_pwdb = pstats->rx_pwdb_all; if (pstats->rx_pwdb_all > (u32) undecorated_smoothed_pwdb) { undecorated_smoothed_pwdb = (((undecorated_smoothed_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); undecorated_smoothed_pwdb = undecorated_smoothed_pwdb + 1; } else { undecorated_smoothed_pwdb = (((undecorated_smoothed_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); } rtlpriv->dm.undecorated_smoothed_pwdb = undecorated_smoothed_pwdb; _rtl92c_update_rxsignalstatistics(hw, pstats); } } static void _rtl92c_process_LINK_Q(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 last_evm = 0, n_stream, tmpval; if (pstats->signalquality != 0) { if (pstats->packet_toself || pstats->packet_beacon) { if (rtlpriv->stats.LINK_Q.total_num++ >= PHY_LINKQUALITY_SLID_WIN_MAX) { rtlpriv->stats.LINK_Q.total_num = PHY_LINKQUALITY_SLID_WIN_MAX; last_evm = rtlpriv->stats.LINK_Q.elements [rtlpriv->stats.LINK_Q.index]; rtlpriv->stats.LINK_Q.total_val -= last_evm; } rtlpriv->stats.LINK_Q.total_val += pstats->signalquality; rtlpriv->stats.LINK_Q.elements [rtlpriv->stats.LINK_Q.index++] = pstats->signalquality; if (rtlpriv->stats.LINK_Q.index >= PHY_LINKQUALITY_SLID_WIN_MAX) rtlpriv->stats.LINK_Q.index = 0; tmpval = rtlpriv->stats.LINK_Q.total_val / rtlpriv->stats.LINK_Q.total_num; rtlpriv->stats.signal_quality = tmpval; rtlpriv->stats.last_sigstrength_inpercent = tmpval; for (n_stream = 0; n_stream < 2; n_stream++) { if (pstats->RX_SIGQ[n_stream] != -1) { if (!rtlpriv->stats.RX_EVM[n_stream]) { rtlpriv->stats.RX_EVM[n_stream] = pstats->RX_SIGQ[n_stream]; } rtlpriv->stats.RX_EVM[n_stream] = ((rtlpriv->stats.RX_EVM [n_stream] * (RX_SMOOTH_FACTOR - 1)) + (pstats->RX_SIGQ [n_stream] * 1)) / (RX_SMOOTH_FACTOR); } } } } else { ; } } static void _rtl92c_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer, struct rtl_stats *pcurrent_stats) { if (!pcurrent_stats->packet_matchbssid && !pcurrent_stats->packet_beacon) return; _rtl92c_process_ui_rssi(hw, pcurrent_stats); _rtl92c_process_pwdb(hw, pcurrent_stats); _rtl92c_process_LINK_Q(hw, pcurrent_stats); } void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_stats *pstats, struct rx_desc_92c *pdesc, struct rx_fwinfo_92c *p_drvinfo) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct ieee80211_hdr *hdr; u8 *tmp_buf; u8 *praddr; __le16 fc; u16 type, cpu_fc; bool packet_matchbssid, packet_toself, packet_beacon; tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift; hdr = (struct ieee80211_hdr *)tmp_buf; fc = hdr->frame_control; cpu_fc = le16_to_cpu(fc); type = WLAN_FC_GET_TYPE(fc); praddr = hdr->addr1; packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) && ether_addr_equal(mac->bssid, (cpu_fc & IEEE80211_FCTL_TODS) ? hdr->addr1 : (cpu_fc & IEEE80211_FCTL_FROMDS) ? hdr->addr2 : hdr->addr3) && (!pstats->hwerror) && (!pstats->crc) && (!pstats->icv)); packet_toself = packet_matchbssid && ether_addr_equal(praddr, rtlefuse->dev_addr); if (ieee80211_is_beacon(fc)) packet_beacon = true; _rtl92c_query_rxphystatus(hw, pstats, pdesc, p_drvinfo, packet_matchbssid, packet_toself, packet_beacon); _rtl92c_process_phyinfo(hw, tmp_buf, pstats); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/led.h0000644000175000017500000000300612026211315024532 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * *****************************************************************************/ #ifndef __RTL92CU_LED_H__ #define __RTL92CU_LED_H__ void rtl92cu_init_sw_leds(struct ieee80211_hw *hw); void rtl92cu_deinit_sw_leds(struct ieee80211_hw *hw); void rtl92cu_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92cu_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92cu_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/led.c0000644000175000017500000001007512026211315024531 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * *****************************************************************************/ #include "../wifi.h" #include "../usb.h" #include "reg.h" #include "led.h" static void _rtl92cu_init_led(struct ieee80211_hw *hw, struct rtl_led *pled, enum rtl_led_pin ledpin) { pled->hw = hw; pled->ledpin = ledpin; pled->ledon = false; } static void _rtl92cu_deInit_led(struct rtl_led *pled) { } void rtl92cu_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled) { u8 ledcfg; struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) | BIT(5) | BIT(6)); break; case LED_PIN_LED1: rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0x0f) | BIT(5)); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = true; } void rtl92cu_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb_priv *usbpriv = rtl_usbpriv(hw); u8 ledcfg; RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: ledcfg &= 0xf0; if (usbpriv->ledctl.led_opendrain) rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(1) | BIT(5) | BIT(6))); else rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3) | BIT(5) | BIT(6))); break; case LED_PIN_LED1: ledcfg &= 0x0f; rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3))); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = false; } void rtl92cu_init_sw_leds(struct ieee80211_hw *hw) { struct rtl_usb_priv *usbpriv = rtl_usbpriv(hw); _rtl92cu_init_led(hw, &(usbpriv->ledctl.sw_led0), LED_PIN_LED0); _rtl92cu_init_led(hw, &(usbpriv->ledctl.sw_led1), LED_PIN_LED1); } void rtl92cu_deinit_sw_leds(struct ieee80211_hw *hw) { struct rtl_usb_priv *usbpriv = rtl_usbpriv(hw); _rtl92cu_deInit_led(&(usbpriv->ledctl.sw_led0)); _rtl92cu_deInit_led(&(usbpriv->ledctl.sw_led1)); } static void _rtl92cu_sw_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { } void rtl92cu_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) && (ledaction == LED_CTL_TX || ledaction == LED_CTL_RX || ledaction == LED_CTL_SITE_SURVEY || ledaction == LED_CTL_LINK || ledaction == LED_CTL_NO_LINK || ledaction == LED_CTL_START_TO_LINK || ledaction == LED_CTL_POWER_ON)) { return; } RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "ledaction %d\n", ledaction); _rtl92cu_sw_led_control(hw, ledaction); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h0000644000175000017500000001046112026211315024407 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CU_HW_H__ #define __RTL92CU_HW_H__ #define H2C_RA_MASK 6 #define LLT_POLLING_LLT_THRESHOLD 20 #define LLT_POLLING_READY_TIMEOUT_COUNT 100 #define LLT_LAST_ENTRY_OF_TX_PKT_BUFFER 255 #define RX_PAGE_SIZE_REG_VALUE PBP_128 /* Note: We will divide number of page equally for each queue * other than public queue! */ #define TX_TOTAL_PAGE_NUMBER 0xF8 #define TX_PAGE_BOUNDARY (TX_TOTAL_PAGE_NUMBER + 1) #define CHIP_B_PAGE_NUM_PUBQ 0xE7 /* For Test Chip Setting * (HPQ + LPQ + PUBQ) shall be TX_TOTAL_PAGE_NUMBER */ #define CHIP_A_PAGE_NUM_PUBQ 0x7E /* For Chip A Setting */ #define WMM_CHIP_A_TX_TOTAL_PAGE_NUMBER 0xF5 #define WMM_CHIP_A_TX_PAGE_BOUNDARY \ (WMM_CHIP_A_TX_TOTAL_PAGE_NUMBER + 1) /* F6 */ #define WMM_CHIP_A_PAGE_NUM_PUBQ 0xA3 #define WMM_CHIP_A_PAGE_NUM_HPQ 0x29 #define WMM_CHIP_A_PAGE_NUM_LPQ 0x29 /* Note: For Chip B Setting ,modify later */ #define WMM_CHIP_B_TX_TOTAL_PAGE_NUMBER 0xF5 #define WMM_CHIP_B_TX_PAGE_BOUNDARY \ (WMM_CHIP_B_TX_TOTAL_PAGE_NUMBER + 1) /* F6 */ #define WMM_CHIP_B_PAGE_NUM_PUBQ 0xB0 #define WMM_CHIP_B_PAGE_NUM_HPQ 0x29 #define WMM_CHIP_B_PAGE_NUM_LPQ 0x1C #define WMM_CHIP_B_PAGE_NUM_NPQ 0x1C #define BOARD_TYPE_NORMAL_MASK 0xE0 #define BOARD_TYPE_TEST_MASK 0x0F /* should be renamed and moved to another file */ enum _BOARD_TYPE_8192CUSB { BOARD_USB_DONGLE = 0, /* USB dongle */ BOARD_USB_High_PA = 1, /* USB dongle - high power PA */ BOARD_MINICARD = 2, /* Minicard */ BOARD_USB_SOLO = 3, /* USB solo-Slim module */ BOARD_USB_COMBO = 4, /* USB Combo-Slim module */ }; #define IS_HIGHT_PA(boardtype) \ ((boardtype == BOARD_USB_High_PA) ? true : false) #define RTL92C_DRIVER_INFO_SIZE 4 void rtl92cu_read_eeprom_info(struct ieee80211_hw *hw); void rtl92cu_enable_hw_security_config(struct ieee80211_hw *hw); int rtl92cu_hw_init(struct ieee80211_hw *hw); void rtl92cu_card_disable(struct ieee80211_hw *hw); int rtl92cu_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type); void rtl92cu_set_beacon_related_registers(struct ieee80211_hw *hw); void rtl92cu_set_beacon_interval(struct ieee80211_hw *hw); void rtl92cu_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr); void rtl92cu_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92cu_update_hal_rate_table(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level); void rtl92cu_update_hal_rate_mask(struct ieee80211_hw *hw, u8 rssi_level); void rtl92cu_update_channel_access_setting(struct ieee80211_hw *hw); bool rtl92cu_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 * valid); void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid); int rtl92c_download_fw(struct ieee80211_hw *hw); void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished); void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c0000644000175000017500000021261112026211315024403 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../efuse.h" #include "../base.h" #include "../cam.h" #include "../ps.h" #include "../usb.h" #include "reg.h" #include "def.h" #include "phy.h" #include "mac.h" #include "dm.h" #include "hw.h" #include "../rtl8192ce/hw.h" #include "trx.h" #include "led.h" #include "table.h" static void _rtl92cu_phy_param_tab_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtlpriv); rtlphy->hwparam_tables[MAC_REG].length = RTL8192CUMAC_2T_ARRAYLENGTH; rtlphy->hwparam_tables[MAC_REG].pdata = RTL8192CUMAC_2T_ARRAY; if (IS_HIGHT_PA(rtlefuse->board_type)) { rtlphy->hwparam_tables[PHY_REG_PG].length = RTL8192CUPHY_REG_Array_PG_HPLength; rtlphy->hwparam_tables[PHY_REG_PG].pdata = RTL8192CUPHY_REG_Array_PG_HP; } else { rtlphy->hwparam_tables[PHY_REG_PG].length = RTL8192CUPHY_REG_ARRAY_PGLENGTH; rtlphy->hwparam_tables[PHY_REG_PG].pdata = RTL8192CUPHY_REG_ARRAY_PG; } /* 2T */ rtlphy->hwparam_tables[PHY_REG_2T].length = RTL8192CUPHY_REG_2TARRAY_LENGTH; rtlphy->hwparam_tables[PHY_REG_2T].pdata = RTL8192CUPHY_REG_2TARRAY; rtlphy->hwparam_tables[RADIOA_2T].length = RTL8192CURADIOA_2TARRAYLENGTH; rtlphy->hwparam_tables[RADIOA_2T].pdata = RTL8192CURADIOA_2TARRAY; rtlphy->hwparam_tables[RADIOB_2T].length = RTL8192CURADIOB_2TARRAYLENGTH; rtlphy->hwparam_tables[RADIOB_2T].pdata = RTL8192CU_RADIOB_2TARRAY; rtlphy->hwparam_tables[AGCTAB_2T].length = RTL8192CUAGCTAB_2TARRAYLENGTH; rtlphy->hwparam_tables[AGCTAB_2T].pdata = RTL8192CUAGCTAB_2TARRAY; /* 1T */ if (IS_HIGHT_PA(rtlefuse->board_type)) { rtlphy->hwparam_tables[PHY_REG_1T].length = RTL8192CUPHY_REG_1T_HPArrayLength; rtlphy->hwparam_tables[PHY_REG_1T].pdata = RTL8192CUPHY_REG_1T_HPArray; rtlphy->hwparam_tables[RADIOA_1T].length = RTL8192CURadioA_1T_HPArrayLength; rtlphy->hwparam_tables[RADIOA_1T].pdata = RTL8192CURadioA_1T_HPArray; rtlphy->hwparam_tables[RADIOB_1T].length = RTL8192CURADIOB_1TARRAYLENGTH; rtlphy->hwparam_tables[RADIOB_1T].pdata = RTL8192CU_RADIOB_1TARRAY; rtlphy->hwparam_tables[AGCTAB_1T].length = RTL8192CUAGCTAB_1T_HPArrayLength; rtlphy->hwparam_tables[AGCTAB_1T].pdata = Rtl8192CUAGCTAB_1T_HPArray; } else { rtlphy->hwparam_tables[PHY_REG_1T].length = RTL8192CUPHY_REG_1TARRAY_LENGTH; rtlphy->hwparam_tables[PHY_REG_1T].pdata = RTL8192CUPHY_REG_1TARRAY; rtlphy->hwparam_tables[RADIOA_1T].length = RTL8192CURADIOA_1TARRAYLENGTH; rtlphy->hwparam_tables[RADIOA_1T].pdata = RTL8192CU_RADIOA_1TARRAY; rtlphy->hwparam_tables[RADIOB_1T].length = RTL8192CURADIOB_1TARRAYLENGTH; rtlphy->hwparam_tables[RADIOB_1T].pdata = RTL8192CU_RADIOB_1TARRAY; rtlphy->hwparam_tables[AGCTAB_1T].length = RTL8192CUAGCTAB_1TARRAYLENGTH; rtlphy->hwparam_tables[AGCTAB_1T].pdata = RTL8192CUAGCTAB_1TARRAY; } } static void _rtl92cu_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, bool autoload_fail, u8 *hwinfo) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 rf_path, index, tempval; u16 i; for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 3; i++) { if (!autoload_fail) { rtlefuse-> eeprom_chnlarea_txpwr_cck[rf_path][i] = hwinfo[EEPROM_TXPOWERCCK + rf_path * 3 + i]; rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][i] = hwinfo[EEPROM_TXPOWERHT40_1S + rf_path * 3 + i]; } else { rtlefuse-> eeprom_chnlarea_txpwr_cck[rf_path][i] = EEPROM_DEFAULT_TXPOWERLEVEL; rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][i] = EEPROM_DEFAULT_TXPOWERLEVEL; } } } for (i = 0; i < 3; i++) { if (!autoload_fail) tempval = hwinfo[EEPROM_TXPOWERHT40_2SDIFF + i]; else tempval = EEPROM_DEFAULT_HT40_2SDIFF; rtlefuse->eeprom_chnlarea_txpwr_ht40_2sdiif[RF90_PATH_A][i] = (tempval & 0xf); rtlefuse->eeprom_chnlarea_txpwr_ht40_2sdiif[RF90_PATH_B][i] = ((tempval & 0xf0) >> 4); } for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM CCK Area(%d) = 0x%x\n", rf_path, i, rtlefuse-> eeprom_chnlarea_txpwr_cck[rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM HT40 1S Area(%d) = 0x%x\n", rf_path, i, rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM HT40 2S Diff Area(%d) = 0x%x\n", rf_path, i, rtlefuse-> eeprom_chnlarea_txpwr_ht40_2sdiif[rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 14; i++) { index = _rtl92c_get_chnl_group((u8) i); rtlefuse->txpwrlevel_cck[rf_path][i] = rtlefuse->eeprom_chnlarea_txpwr_cck[rf_path][index]; rtlefuse->txpwrlevel_ht40_1s[rf_path][i] = rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][index]; if ((rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][index] - rtlefuse-> eeprom_chnlarea_txpwr_ht40_2sdiif[rf_path][index]) > 0) { rtlefuse->txpwrlevel_ht40_2s[rf_path][i] = rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path] [index] - rtlefuse-> eeprom_chnlarea_txpwr_ht40_2sdiif[rf_path] [index]; } else { rtlefuse->txpwrlevel_ht40_2s[rf_path][i] = 0; } } for (i = 0; i < 14; i++) { RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = [0x%x / 0x%x / 0x%x]\n", rf_path, i, rtlefuse->txpwrlevel_cck[rf_path][i], rtlefuse->txpwrlevel_ht40_1s[rf_path][i], rtlefuse->txpwrlevel_ht40_2s[rf_path][i]); } } for (i = 0; i < 3; i++) { if (!autoload_fail) { rtlefuse->eeprom_pwrlimit_ht40[i] = hwinfo[EEPROM_TXPWR_GROUP + i]; rtlefuse->eeprom_pwrlimit_ht20[i] = hwinfo[EEPROM_TXPWR_GROUP + 3 + i]; } else { rtlefuse->eeprom_pwrlimit_ht40[i] = 0; rtlefuse->eeprom_pwrlimit_ht20[i] = 0; } } for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 14; i++) { index = _rtl92c_get_chnl_group((u8) i); if (rf_path == RF90_PATH_A) { rtlefuse->pwrgroup_ht20[rf_path][i] = (rtlefuse->eeprom_pwrlimit_ht20[index] & 0xf); rtlefuse->pwrgroup_ht40[rf_path][i] = (rtlefuse->eeprom_pwrlimit_ht40[index] & 0xf); } else if (rf_path == RF90_PATH_B) { rtlefuse->pwrgroup_ht20[rf_path][i] = ((rtlefuse->eeprom_pwrlimit_ht20[index] & 0xf0) >> 4); rtlefuse->pwrgroup_ht40[rf_path][i] = ((rtlefuse->eeprom_pwrlimit_ht40[index] & 0xf0) >> 4); } RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-%d pwrgroup_ht20[%d] = 0x%x\n", rf_path, i, rtlefuse->pwrgroup_ht20[rf_path][i]); RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-%d pwrgroup_ht40[%d] = 0x%x\n", rf_path, i, rtlefuse->pwrgroup_ht40[rf_path][i]); } } for (i = 0; i < 14; i++) { index = _rtl92c_get_chnl_group((u8) i); if (!autoload_fail) tempval = hwinfo[EEPROM_TXPOWERHT20DIFF + index]; else tempval = EEPROM_DEFAULT_HT20_DIFF; rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); if (rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] & BIT(3)) rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] |= 0xF0; if (rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] & BIT(3)) rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] |= 0xF0; index = _rtl92c_get_chnl_group((u8) i); if (!autoload_fail) tempval = hwinfo[EEPROM_TXPOWER_OFDMDIFF + index]; else tempval = EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF; rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); } rtlefuse->legacy_ht_txpowerdiff = rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][7]; for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-B Legacy to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]); if (!autoload_fail) rtlefuse->eeprom_regulatory = (hwinfo[RF_OPTION1] & 0x7); else rtlefuse->eeprom_regulatory = 0; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory); if (!autoload_fail) { rtlefuse->eeprom_tssi[RF90_PATH_A] = hwinfo[EEPROM_TSSI_A]; rtlefuse->eeprom_tssi[RF90_PATH_B] = hwinfo[EEPROM_TSSI_B]; } else { rtlefuse->eeprom_tssi[RF90_PATH_A] = EEPROM_DEFAULT_TSSI; rtlefuse->eeprom_tssi[RF90_PATH_B] = EEPROM_DEFAULT_TSSI; } RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TSSI_A = 0x%x, TSSI_B = 0x%x\n", rtlefuse->eeprom_tssi[RF90_PATH_A], rtlefuse->eeprom_tssi[RF90_PATH_B]); if (!autoload_fail) tempval = hwinfo[EEPROM_THERMAL_METER]; else tempval = EEPROM_DEFAULT_THERMALMETER; rtlefuse->eeprom_thermalmeter = (tempval & 0x1f); if (rtlefuse->eeprom_thermalmeter < 0x06 || rtlefuse->eeprom_thermalmeter > 0x1c) rtlefuse->eeprom_thermalmeter = 0x12; if (rtlefuse->eeprom_thermalmeter == 0x1f || autoload_fail) rtlefuse->apk_thermalmeterignore = true; rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter); } static void _rtl92cu_read_board_type(struct ieee80211_hw *hw, u8 *contents) { struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 boardType; if (IS_NORMAL_CHIP(rtlhal->version)) { boardType = ((contents[EEPROM_RF_OPT1]) & BOARD_TYPE_NORMAL_MASK) >> 5; /*bit[7:5]*/ } else { boardType = contents[EEPROM_RF_OPT4]; boardType &= BOARD_TYPE_TEST_MASK; } rtlefuse->board_type = boardType; if (IS_HIGHT_PA(rtlefuse->board_type)) rtlefuse->external_pa = 1; pr_info("Board Type %x\n", rtlefuse->board_type); } static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u16 i, usvalue; u8 hwinfo[HWSET_MAX_SIZE] = {0}; u16 eeprom_id; if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) { rtl_efuse_shadow_map_update(hw); memcpy((void *)hwinfo, (void *)&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], HWSET_MAX_SIZE); } else if (rtlefuse->epromtype == EEPROM_93C46) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "RTL819X Not boot from eeprom, check it !!\n"); } RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_LOUD, "MAP", hwinfo, HWSET_MAX_SIZE); eeprom_id = le16_to_cpu(*((__le16 *)&hwinfo[0])); if (eeprom_id != RTL8190_EEPROM_ID) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "EEPROM ID(%#x) is invalid!!\n", eeprom_id); rtlefuse->autoload_failflag = true; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); rtlefuse->autoload_failflag = false; } if (rtlefuse->autoload_failflag) return; for (i = 0; i < 6; i += 2) { usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i]; *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue; } pr_info("MAC address: %pM\n", rtlefuse->dev_addr); _rtl92cu_read_txpower_info_from_hwpg(hw, rtlefuse->autoload_failflag, hwinfo); rtlefuse->eeprom_vid = le16_to_cpu(*(__le16 *)&hwinfo[EEPROM_VID]); rtlefuse->eeprom_did = le16_to_cpu(*(__le16 *)&hwinfo[EEPROM_DID]); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, " VID = 0x%02x PID = 0x%02x\n", rtlefuse->eeprom_vid, rtlefuse->eeprom_did); rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN]; rtlefuse->eeprom_version = le16_to_cpu(*(__le16 *)&hwinfo[EEPROM_VERSION]); rtlefuse->txpwr_fromeprom = true; rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); if (rtlhal->oem_id == RT_CID_DEFAULT) { switch (rtlefuse->eeprom_oemid) { case EEPROM_CID_DEFAULT: if (rtlefuse->eeprom_did == 0x8176) { if ((rtlefuse->eeprom_svid == 0x103C && rtlefuse->eeprom_smid == 0x1629)) rtlhal->oem_id = RT_CID_819x_HP; else rtlhal->oem_id = RT_CID_DEFAULT; } else { rtlhal->oem_id = RT_CID_DEFAULT; } break; case EEPROM_CID_TOSHIBA: rtlhal->oem_id = RT_CID_TOSHIBA; break; case EEPROM_CID_QMI: rtlhal->oem_id = RT_CID_819x_QMI; break; case EEPROM_CID_WHQL: default: rtlhal->oem_id = RT_CID_DEFAULT; break; } } _rtl92cu_read_board_type(hw, hwinfo); } static void _rtl92cu_hal_customized_behavior(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); switch (rtlhal->oem_id) { case RT_CID_819x_HP: usb_priv->ledctl.led_opendrain = true; break; case RT_CID_819x_Lenovo: case RT_CID_DEFAULT: case RT_CID_TOSHIBA: case RT_CID_CCX: case RT_CID_819x_Acer: case RT_CID_WHQL: default: break; } RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "RT Customized ID: 0x%02X\n", rtlhal->oem_id); } void rtl92cu_read_eeprom_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 tmp_u1b; if (!IS_NORMAL_CHIP(rtlhal->version)) return; tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR); rtlefuse->epromtype = (tmp_u1b & BOOT_FROM_EEPROM) ? EEPROM_93C46 : EEPROM_BOOT_EFUSE; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from %s\n", tmp_u1b & BOOT_FROM_EEPROM ? "EERROM" : "EFUSE"); rtlefuse->autoload_failflag = (tmp_u1b & EEPROM_EN) ? false : true; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload %s\n", tmp_u1b & EEPROM_EN ? "OK!!" : "ERR!!"); _rtl92cu_read_adapter_info(hw); _rtl92cu_hal_customized_behavior(hw); return; } static int _rtl92cu_init_power_on(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); int status = 0; u16 value16; u8 value8; /* polling autoload done. */ u32 pollingCount = 0; do { if (rtl_read_byte(rtlpriv, REG_APS_FSMCO) & PFM_ALDN) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Autoload Done!\n"); break; } if (pollingCount++ > 100) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Failed to polling REG_APS_FSMCO[PFM_ALDN] done!\n"); return -ENODEV; } } while (true); /* 0. RSV_CTRL 0x1C[7:0] = 0 unlock ISO/CLK/Power control register */ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0); /* Power on when re-enter from IPS/Radio off/card disable */ /* enable SPS into PWM mode */ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b); udelay(100); value8 = rtl_read_byte(rtlpriv, REG_LDOV12D_CTRL); if (0 == (value8 & LDV12_EN)) { value8 |= LDV12_EN; rtl_write_byte(rtlpriv, REG_LDOV12D_CTRL, value8); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, " power-on :REG_LDOV12D_CTRL Reg0x21:0x%02x\n", value8); udelay(100); value8 = rtl_read_byte(rtlpriv, REG_SYS_ISO_CTRL); value8 &= ~ISO_MD2PP; rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL, value8); } /* auto enable WLAN */ pollingCount = 0; value16 = rtl_read_word(rtlpriv, REG_APS_FSMCO); value16 |= APFM_ONMAC; rtl_write_word(rtlpriv, REG_APS_FSMCO, value16); do { if (!(rtl_read_word(rtlpriv, REG_APS_FSMCO) & APFM_ONMAC)) { pr_info("MAC auto ON okay!\n"); break; } if (pollingCount++ > 100) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Failed to polling REG_APS_FSMCO[APFM_ONMAC] done!\n"); return -ENODEV; } } while (true); /* Enable Radio ,GPIO ,and LED function */ rtl_write_word(rtlpriv, REG_APS_FSMCO, 0x0812); /* release RF digital isolation */ value16 = rtl_read_word(rtlpriv, REG_SYS_ISO_CTRL); value16 &= ~ISO_DIOR; rtl_write_word(rtlpriv, REG_SYS_ISO_CTRL, value16); /* Reconsider when to do this operation after asking HWSD. */ pollingCount = 0; rtl_write_byte(rtlpriv, REG_APSD_CTRL, (rtl_read_byte(rtlpriv, REG_APSD_CTRL) & ~BIT(6))); do { pollingCount++; } while ((pollingCount < 200) && (rtl_read_byte(rtlpriv, REG_APSD_CTRL) & BIT(7))); /* Enable MAC DMA/WMAC/SCHEDULE/SEC block */ value16 = rtl_read_word(rtlpriv, REG_CR); value16 |= (HCI_TXDMA_EN | HCI_RXDMA_EN | TXDMA_EN | RXDMA_EN | PROTOCOL_EN | SCHEDULE_EN | MACTXEN | MACRXEN | ENSEC); rtl_write_word(rtlpriv, REG_CR, value16); return status; } static void _rtl92cu_init_queue_reserved_page(struct ieee80211_hw *hw, bool wmm_enable, u8 out_ep_num, u8 queue_sel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool isChipN = IS_NORMAL_CHIP(rtlhal->version); u32 outEPNum = (u32)out_ep_num; u32 numHQ = 0; u32 numLQ = 0; u32 numNQ = 0; u32 numPubQ; u32 value32; u8 value8; u32 txQPageNum, txQPageUnit, txQRemainPage; if (!wmm_enable) { numPubQ = (isChipN) ? CHIP_B_PAGE_NUM_PUBQ : CHIP_A_PAGE_NUM_PUBQ; txQPageNum = TX_TOTAL_PAGE_NUMBER - numPubQ; txQPageUnit = txQPageNum/outEPNum; txQRemainPage = txQPageNum % outEPNum; if (queue_sel & TX_SELE_HQ) numHQ = txQPageUnit; if (queue_sel & TX_SELE_LQ) numLQ = txQPageUnit; /* HIGH priority queue always present in the configuration of * 2 out-ep. Remainder pages have assigned to High queue */ if ((outEPNum > 1) && (txQRemainPage)) numHQ += txQRemainPage; /* NOTE: This step done before writting REG_RQPN. */ if (isChipN) { if (queue_sel & TX_SELE_NQ) numNQ = txQPageUnit; value8 = (u8)_NPQ(numNQ); rtl_write_byte(rtlpriv, REG_RQPN_NPQ, value8); } } else { /* for WMM ,number of out-ep must more than or equal to 2! */ numPubQ = isChipN ? WMM_CHIP_B_PAGE_NUM_PUBQ : WMM_CHIP_A_PAGE_NUM_PUBQ; if (queue_sel & TX_SELE_HQ) { numHQ = isChipN ? WMM_CHIP_B_PAGE_NUM_HPQ : WMM_CHIP_A_PAGE_NUM_HPQ; } if (queue_sel & TX_SELE_LQ) { numLQ = isChipN ? WMM_CHIP_B_PAGE_NUM_LPQ : WMM_CHIP_A_PAGE_NUM_LPQ; } /* NOTE: This step done before writting REG_RQPN. */ if (isChipN) { if (queue_sel & TX_SELE_NQ) numNQ = WMM_CHIP_B_PAGE_NUM_NPQ; value8 = (u8)_NPQ(numNQ); rtl_write_byte(rtlpriv, REG_RQPN_NPQ, value8); } } /* TX DMA */ value32 = _HPQ(numHQ) | _LPQ(numLQ) | _PUBQ(numPubQ) | LD_RQPN; rtl_write_dword(rtlpriv, REG_RQPN, value32); } static void _rtl92c_init_trx_buffer(struct ieee80211_hw *hw, bool wmm_enable) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 txpktbuf_bndy; u8 value8; if (!wmm_enable) txpktbuf_bndy = TX_PAGE_BOUNDARY; else /* for WMM */ txpktbuf_bndy = (IS_NORMAL_CHIP(rtlhal->version)) ? WMM_CHIP_B_TX_PAGE_BOUNDARY : WMM_CHIP_A_TX_PAGE_BOUNDARY; rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_TXPKTBUF_WMAC_LBK_BF_HD, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_TRXFF_BNDY, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_TDECTRL+1, txpktbuf_bndy); rtl_write_word(rtlpriv, (REG_TRXFF_BNDY + 2), 0x27FF); value8 = _PSRX(RX_PAGE_SIZE_REG_VALUE) | _PSTX(PBP_128); rtl_write_byte(rtlpriv, REG_PBP, value8); } static void _rtl92c_init_chipN_reg_priority(struct ieee80211_hw *hw, u16 beQ, u16 bkQ, u16 viQ, u16 voQ, u16 mgtQ, u16 hiQ) { struct rtl_priv *rtlpriv = rtl_priv(hw); u16 value16 = (rtl_read_word(rtlpriv, REG_TRXDMA_CTRL) & 0x7); value16 |= _TXDMA_BEQ_MAP(beQ) | _TXDMA_BKQ_MAP(bkQ) | _TXDMA_VIQ_MAP(viQ) | _TXDMA_VOQ_MAP(voQ) | _TXDMA_MGQ_MAP(mgtQ) | _TXDMA_HIQ_MAP(hiQ); rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, value16); } static void _rtl92cu_init_chipN_one_out_ep_priority(struct ieee80211_hw *hw, bool wmm_enable, u8 queue_sel) { u16 uninitialized_var(value); switch (queue_sel) { case TX_SELE_HQ: value = QUEUE_HIGH; break; case TX_SELE_LQ: value = QUEUE_LOW; break; case TX_SELE_NQ: value = QUEUE_NORMAL; break; default: WARN_ON(1); /* Shall not reach here! */ break; } _rtl92c_init_chipN_reg_priority(hw, value, value, value, value, value, value); pr_info("Tx queue select: 0x%02x\n", queue_sel); } static void _rtl92cu_init_chipN_two_out_ep_priority(struct ieee80211_hw *hw, bool wmm_enable, u8 queue_sel) { u16 beQ, bkQ, viQ, voQ, mgtQ, hiQ; u16 uninitialized_var(valueHi); u16 uninitialized_var(valueLow); switch (queue_sel) { case (TX_SELE_HQ | TX_SELE_LQ): valueHi = QUEUE_HIGH; valueLow = QUEUE_LOW; break; case (TX_SELE_NQ | TX_SELE_LQ): valueHi = QUEUE_NORMAL; valueLow = QUEUE_LOW; break; case (TX_SELE_HQ | TX_SELE_NQ): valueHi = QUEUE_HIGH; valueLow = QUEUE_NORMAL; break; default: WARN_ON(1); break; } if (!wmm_enable) { beQ = valueLow; bkQ = valueLow; viQ = valueHi; voQ = valueHi; mgtQ = valueHi; hiQ = valueHi; } else {/* for WMM ,CONFIG_OUT_EP_WIFI_MODE */ beQ = valueHi; bkQ = valueLow; viQ = valueLow; voQ = valueHi; mgtQ = valueHi; hiQ = valueHi; } _rtl92c_init_chipN_reg_priority(hw, beQ, bkQ, viQ, voQ, mgtQ, hiQ); pr_info("Tx queue select: 0x%02x\n", queue_sel); } static void _rtl92cu_init_chipN_three_out_ep_priority(struct ieee80211_hw *hw, bool wmm_enable, u8 queue_sel) { u16 beQ, bkQ, viQ, voQ, mgtQ, hiQ; struct rtl_priv *rtlpriv = rtl_priv(hw); if (!wmm_enable) { /* typical setting */ beQ = QUEUE_LOW; bkQ = QUEUE_LOW; viQ = QUEUE_NORMAL; voQ = QUEUE_HIGH; mgtQ = QUEUE_HIGH; hiQ = QUEUE_HIGH; } else { /* for WMM */ beQ = QUEUE_LOW; bkQ = QUEUE_NORMAL; viQ = QUEUE_NORMAL; voQ = QUEUE_HIGH; mgtQ = QUEUE_HIGH; hiQ = QUEUE_HIGH; } _rtl92c_init_chipN_reg_priority(hw, beQ, bkQ, viQ, voQ, mgtQ, hiQ); RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Tx queue select :0x%02x..\n", queue_sel); } static void _rtl92cu_init_chipN_queue_priority(struct ieee80211_hw *hw, bool wmm_enable, u8 out_ep_num, u8 queue_sel) { switch (out_ep_num) { case 1: _rtl92cu_init_chipN_one_out_ep_priority(hw, wmm_enable, queue_sel); break; case 2: _rtl92cu_init_chipN_two_out_ep_priority(hw, wmm_enable, queue_sel); break; case 3: _rtl92cu_init_chipN_three_out_ep_priority(hw, wmm_enable, queue_sel); break; default: WARN_ON(1); /* Shall not reach here! */ break; } } static void _rtl92cu_init_chipT_queue_priority(struct ieee80211_hw *hw, bool wmm_enable, u8 out_ep_num, u8 queue_sel) { u8 hq_sele = 0; struct rtl_priv *rtlpriv = rtl_priv(hw); switch (out_ep_num) { case 2: /* (TX_SELE_HQ|TX_SELE_LQ) */ if (!wmm_enable) /* typical setting */ hq_sele = HQSEL_VOQ | HQSEL_VIQ | HQSEL_MGTQ | HQSEL_HIQ; else /* for WMM */ hq_sele = HQSEL_VOQ | HQSEL_BEQ | HQSEL_MGTQ | HQSEL_HIQ; break; case 1: if (TX_SELE_LQ == queue_sel) { /* map all endpoint to Low queue */ hq_sele = 0; } else if (TX_SELE_HQ == queue_sel) { /* map all endpoint to High queue */ hq_sele = HQSEL_VOQ | HQSEL_VIQ | HQSEL_BEQ | HQSEL_BKQ | HQSEL_MGTQ | HQSEL_HIQ; } break; default: WARN_ON(1); /* Shall not reach here! */ break; } rtl_write_byte(rtlpriv, (REG_TRXDMA_CTRL+1), hq_sele); RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Tx queue select :0x%02x..\n", hq_sele); } static void _rtl92cu_init_queue_priority(struct ieee80211_hw *hw, bool wmm_enable, u8 out_ep_num, u8 queue_sel) { struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (IS_NORMAL_CHIP(rtlhal->version)) _rtl92cu_init_chipN_queue_priority(hw, wmm_enable, out_ep_num, queue_sel); else _rtl92cu_init_chipT_queue_priority(hw, wmm_enable, out_ep_num, queue_sel); } static void _rtl92cu_init_usb_aggregation(struct ieee80211_hw *hw) { } static void _rtl92cu_init_wmac_setting(struct ieee80211_hw *hw) { u16 value16; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); mac->rx_conf = (RCR_APM | RCR_AM | RCR_ADF | RCR_AB | RCR_APPFCS | RCR_APP_ICV | RCR_AMF | RCR_HTC_LOC_CTRL | RCR_APP_MIC | RCR_APP_PHYSTS | RCR_ACRC32); rtl_write_dword(rtlpriv, REG_RCR, mac->rx_conf); /* Accept all multicast address */ rtl_write_dword(rtlpriv, REG_MAR, 0xFFFFFFFF); rtl_write_dword(rtlpriv, REG_MAR + 4, 0xFFFFFFFF); /* Accept all management frames */ value16 = 0xFFFF; rtl92c_set_mgt_filter(hw, value16); /* Reject all control frame - default value is 0 */ rtl92c_set_ctrl_filter(hw, 0x0); /* Accept all data frames */ value16 = 0xFFFF; rtl92c_set_data_filter(hw, value16); } static int _rtl92cu_init_mac(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_usb_priv *usb_priv = rtl_usbpriv(hw); struct rtl_usb *rtlusb = rtl_usbdev(usb_priv); int err = 0; u32 boundary = 0; u8 wmm_enable = false; /* TODO */ u8 out_ep_nums = rtlusb->out_ep_nums; u8 queue_sel = rtlusb->out_queue_sel; err = _rtl92cu_init_power_on(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to init power on!\n"); return err; } if (!wmm_enable) { boundary = TX_PAGE_BOUNDARY; } else { /* for WMM */ boundary = (IS_NORMAL_CHIP(rtlhal->version)) ? WMM_CHIP_B_TX_PAGE_BOUNDARY : WMM_CHIP_A_TX_PAGE_BOUNDARY; } if (false == rtl92c_init_llt_table(hw, boundary)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to init LLT Table!\n"); return -EINVAL; } _rtl92cu_init_queue_reserved_page(hw, wmm_enable, out_ep_nums, queue_sel); _rtl92c_init_trx_buffer(hw, wmm_enable); _rtl92cu_init_queue_priority(hw, wmm_enable, out_ep_nums, queue_sel); /* Get Rx PHY status in order to report RSSI and others. */ rtl92c_init_driver_info_size(hw, RTL92C_DRIVER_INFO_SIZE); rtl92c_init_interrupt(hw); rtl92c_init_network_type(hw); _rtl92cu_init_wmac_setting(hw); rtl92c_init_adaptive_ctrl(hw); rtl92c_init_edca(hw); rtl92c_init_rate_fallback(hw); rtl92c_init_retry_function(hw); _rtl92cu_init_usb_aggregation(hw); rtlpriv->cfg->ops->set_bw_mode(hw, NL80211_CHAN_HT20); rtl92c_set_min_space(hw, IS_92C_SERIAL(rtlhal->version)); rtl92c_init_beacon_parameters(hw, rtlhal->version); rtl92c_init_ampdu_aggregation(hw); rtl92c_init_beacon_max_error(hw, true); return err; } void rtl92cu_enable_hw_security_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 sec_reg_value = 0x0; struct rtl_hal *rtlhal = rtl_hal(rtlpriv); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n", rtlpriv->sec.pairwise_enc_algorithm, rtlpriv->sec.group_enc_algorithm); if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "not open sw encryption\n"); return; } sec_reg_value = SCR_TxEncEnable | SCR_RxDecEnable; if (rtlpriv->sec.use_defaultkey) { sec_reg_value |= SCR_TxUseDK; sec_reg_value |= SCR_RxUseDK; } if (IS_NORMAL_CHIP(rtlhal->version)) sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK); rtl_write_byte(rtlpriv, REG_CR + 1, 0x02); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The SECR-value %x\n", sec_reg_value); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); } static void _rtl92cu_hw_configure(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); /* To Fix MAC loopback mode fail. */ rtl_write_byte(rtlpriv, REG_LDOHCI12_CTRL, 0x0f); rtl_write_byte(rtlpriv, 0x15, 0xe9); /* HW SEQ CTRL */ /* set 0x0 to 0xFF by tynli. Default enable HW SEQ NUM. */ rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF); /* fixed USB interface interference issue */ rtl_write_byte(rtlpriv, 0xfe40, 0xe0); rtl_write_byte(rtlpriv, 0xfe41, 0x8d); rtl_write_byte(rtlpriv, 0xfe42, 0x80); rtlusb->reg_bcn_ctrl_val = 0x18; rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8)rtlusb->reg_bcn_ctrl_val); } static void _InitPABias(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 pa_setting; /* FIXED PA current issue */ pa_setting = efuse_read_1byte(hw, 0x1FA); if (!(pa_setting & BIT(0))) { rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0FFFFF, 0x0F406); rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0FFFFF, 0x4F406); rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0FFFFF, 0x8F406); rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0FFFFF, 0xCF406); } if (!(pa_setting & BIT(1)) && IS_NORMAL_CHIP(rtlhal->version) && IS_92C_SERIAL(rtlhal->version)) { rtl_set_rfreg(hw, RF90_PATH_B, 0x15, 0x0FFFFF, 0x0F406); rtl_set_rfreg(hw, RF90_PATH_B, 0x15, 0x0FFFFF, 0x4F406); rtl_set_rfreg(hw, RF90_PATH_B, 0x15, 0x0FFFFF, 0x8F406); rtl_set_rfreg(hw, RF90_PATH_B, 0x15, 0x0FFFFF, 0xCF406); } if (!(pa_setting & BIT(4))) { pa_setting = rtl_read_byte(rtlpriv, 0x16); pa_setting &= 0x0F; rtl_write_byte(rtlpriv, 0x16, pa_setting | 0x90); } } static void _update_mac_setting(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); mac->rx_conf = rtl_read_dword(rtlpriv, REG_RCR); mac->rx_mgt_filter = rtl_read_word(rtlpriv, REG_RXFLTMAP0); mac->rx_ctrl_filter = rtl_read_word(rtlpriv, REG_RXFLTMAP1); mac->rx_data_filter = rtl_read_word(rtlpriv, REG_RXFLTMAP2); } int rtl92cu_hw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); int err = 0; static bool iqk_initialized; rtlhal->hw_type = HARDWARE_TYPE_RTL8192CU; err = _rtl92cu_init_mac(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "init mac failed!\n"); return err; } err = rtl92c_download_fw(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now..\n"); err = 1; return err; } rtlhal->last_hmeboxnum = 0; /* h2c */ _rtl92cu_phy_param_tab_init(hw); rtl92cu_phy_mac_config(hw); rtl92cu_phy_bb_config(hw); rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; rtl92c_phy_rf_config(hw); if (IS_VENDOR_UMC_A_CUT(rtlhal->version) && !IS_92C_SERIAL(rtlhal->version)) { rtl_set_rfreg(hw, RF90_PATH_A, RF_RX_G1, MASKDWORD, 0x30255); rtl_set_rfreg(hw, RF90_PATH_A, RF_RX_G2, MASKDWORD, 0x50a00); } rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0, RF_CHNLBW, RFREG_OFFSET_MASK); rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1, RF_CHNLBW, RFREG_OFFSET_MASK); rtl92cu_bb_block_on(hw); rtl_cam_reset_all_entry(hw); rtl92cu_enable_hw_security_config(hw); ppsc->rfpwr_state = ERFON; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr); if (ppsc->rfpwr_state == ERFON) { rtl92c_phy_set_rfpath_switch(hw, 1); if (iqk_initialized) { rtl92c_phy_iq_calibrate(hw, false); } else { rtl92c_phy_iq_calibrate(hw, false); iqk_initialized = true; } rtl92c_dm_check_txpower_tracking(hw); rtl92c_phy_lc_calibrate(hw); } _rtl92cu_hw_configure(hw); _InitPABias(hw); _update_mac_setting(hw); rtl92c_dm_init(hw); return err; } static void _DisableRFAFEAndResetBB(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /************************************** a. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue b. RF path 0 offset 0x00 = 0x00 disable RF c. APSD_CTRL 0x600[7:0] = 0x40 d. SYS_FUNC_EN 0x02[7:0] = 0x16 reset BB state machine e. SYS_FUNC_EN 0x02[7:0] = 0x14 reset BB state machine ***************************************/ u8 eRFPath = 0, value8 = 0; rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); rtl_set_rfreg(hw, (enum radio_path)eRFPath, 0x0, MASKBYTE0, 0x0); value8 |= APSDOFF; rtl_write_byte(rtlpriv, REG_APSD_CTRL, value8); /*0x40*/ value8 = 0; value8 |= (FEN_USBD | FEN_USBA | FEN_BB_GLB_RSTn); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, value8);/*0x16*/ value8 &= (~FEN_BB_GLB_RSTn); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, value8); /*0x14*/ } static void _ResetDigitalProcedure1(struct ieee80211_hw *hw, bool bWithoutHWSM) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (rtlhal->fw_version <= 0x20) { /***************************** f. MCUFWDL 0x80[7:0]=0 reset MCU ready status g. SYS_FUNC_EN 0x02[10]= 0 reset MCU reg, (8051 reset) h. SYS_FUNC_EN 0x02[15-12]= 5 reset MAC reg, DCORE i. SYS_FUNC_EN 0x02[10]= 1 enable MCU reg, (8051 enable) ******************************/ u16 valu16 = 0; rtl_write_byte(rtlpriv, REG_MCUFWDL, 0); valu16 = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (valu16 & (~FEN_CPUEN))); /* reset MCU ,8051 */ valu16 = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN)&0x0FFF; rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (valu16 | (FEN_HWPDN|FEN_ELDR))); /* reset MAC */ valu16 = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (valu16 | FEN_CPUEN)); /* enable MCU ,8051 */ } else { u8 retry_cnts = 0; /* IF fw in RAM code, do reset */ if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(1)) { /* reset MCU ready status */ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0); /* 8051 reset by self */ rtl_write_byte(rtlpriv, REG_HMETFR+3, 0x20); while ((retry_cnts++ < 100) && (FEN_CPUEN & rtl_read_word(rtlpriv, REG_SYS_FUNC_EN))) { udelay(50); } if (retry_cnts >= 100) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "#####=> 8051 reset failed!.........................\n"); /* if 8051 reset fail, reset MAC. */ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, 0x50); udelay(100); } } /* Reset MAC and Enable 8051 */ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, 0x54); rtl_write_byte(rtlpriv, REG_MCUFWDL, 0); } if (bWithoutHWSM) { /***************************** Without HW auto state machine g.SYS_CLKR 0x08[15:0] = 0x30A3 disable MAC clock h.AFE_PLL_CTRL 0x28[7:0] = 0x80 disable AFE PLL i.AFE_XTAL_CTRL 0x24[15:0] = 0x880F gated AFE DIG_CLOCK j.SYS_ISu_CTRL 0x00[7:0] = 0xF9 isolated digital to PON ******************************/ rtl_write_word(rtlpriv, REG_SYS_CLKR, 0x70A3); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x80); rtl_write_word(rtlpriv, REG_AFE_XTAL_CTRL, 0x880F); rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL, 0xF9); } } static void _ResetDigitalProcedure2(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /***************************** k. SYS_FUNC_EN 0x03[7:0] = 0x44 disable ELDR runction l. SYS_CLKR 0x08[15:0] = 0x3083 disable ELDR clock m. SYS_ISO_CTRL 0x01[7:0] = 0x83 isolated ELDR to PON ******************************/ rtl_write_word(rtlpriv, REG_SYS_CLKR, 0x70A3); rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL+1, 0x82); } static void _DisableGPIO(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); /*************************************** j. GPIO_PIN_CTRL 0x44[31:0]=0x000 k. Value = GPIO_PIN_CTRL[7:0] l. GPIO_PIN_CTRL 0x44[31:0] = 0x00FF0000 | (value <<8); write ext PIN level m. GPIO_MUXCFG 0x42 [15:0] = 0x0780 n. LEDCFG 0x4C[15:0] = 0x8080 ***************************************/ u8 value8; u16 value16; u32 value32; /* 1. Disable GPIO[7:0] */ rtl_write_word(rtlpriv, REG_GPIO_PIN_CTRL+2, 0x0000); value32 = rtl_read_dword(rtlpriv, REG_GPIO_PIN_CTRL) & 0xFFFF00FF; value8 = (u8) (value32&0x000000FF); value32 |= ((value8<<8) | 0x00FF0000); rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, value32); /* 2. Disable GPIO[10:8] */ rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG+3, 0x00); value16 = rtl_read_word(rtlpriv, REG_GPIO_MUXCFG+2) & 0xFF0F; value8 = (u8) (value16&0x000F); value16 |= ((value8<<4) | 0x0780); rtl_write_word(rtlpriv, REG_GPIO_PIN_CTRL+2, value16); /* 3. Disable LED0 & 1 */ rtl_write_word(rtlpriv, REG_LEDCFG0, 0x8080); } static void _DisableAnalog(struct ieee80211_hw *hw, bool bWithoutHWSM) { struct rtl_priv *rtlpriv = rtl_priv(hw); u16 value16 = 0; u8 value8 = 0; if (bWithoutHWSM) { /***************************** n. LDOA15_CTRL 0x20[7:0] = 0x04 disable A15 power o. LDOV12D_CTRL 0x21[7:0] = 0x54 disable digital core power r. When driver call disable, the ASIC will turn off remaining clock automatically ******************************/ rtl_write_byte(rtlpriv, REG_LDOA15_CTRL, 0x04); value8 = rtl_read_byte(rtlpriv, REG_LDOV12D_CTRL); value8 &= (~LDV12_EN); rtl_write_byte(rtlpriv, REG_LDOV12D_CTRL, value8); } /***************************** h. SPS0_CTRL 0x11[7:0] = 0x23 enter PFM mode i. APS_FSMCO 0x04[15:0] = 0x4802 set USB suspend ******************************/ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x23); value16 |= (APDM_HOST | AFSM_HSUS | PFM_ALDN); rtl_write_word(rtlpriv, REG_APS_FSMCO, (u16)value16); rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0E); } static void _CardDisableHWSM(struct ieee80211_hw *hw) { /* ==== RF Off Sequence ==== */ _DisableRFAFEAndResetBB(hw); /* ==== Reset digital sequence ====== */ _ResetDigitalProcedure1(hw, false); /* ==== Pull GPIO PIN to balance level and LED control ====== */ _DisableGPIO(hw); /* ==== Disable analog sequence === */ _DisableAnalog(hw, false); } static void _CardDisableWithoutHWSM(struct ieee80211_hw *hw) { /*==== RF Off Sequence ==== */ _DisableRFAFEAndResetBB(hw); /* ==== Reset digital sequence ====== */ _ResetDigitalProcedure1(hw, true); /* ==== Pull GPIO PIN to balance level and LED control ====== */ _DisableGPIO(hw); /* ==== Reset digital sequence ====== */ _ResetDigitalProcedure2(hw); /* ==== Disable analog sequence === */ _DisableAnalog(hw, true); } static void _rtl92cu_set_bcn_ctrl_reg(struct ieee80211_hw *hw, u8 set_bits, u8 clear_bits) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); rtlusb->reg_bcn_ctrl_val |= set_bits; rtlusb->reg_bcn_ctrl_val &= ~clear_bits; rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlusb->reg_bcn_ctrl_val); } static void _rtl92cu_stop_tx_beacon(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); u8 tmp1byte = 0; if (IS_NORMAL_CHIP(rtlhal->version)) { tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6))); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64); tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); tmp1byte &= ~(BIT(0)); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); } else { rtl_write_byte(rtlpriv, REG_TXPAUSE, rtl_read_byte(rtlpriv, REG_TXPAUSE) | BIT(6)); } } static void _rtl92cu_resume_tx_beacon(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); u8 tmp1byte = 0; if (IS_NORMAL_CHIP(rtlhal->version)) { tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6)); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); tmp1byte |= BIT(0); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); } else { rtl_write_byte(rtlpriv, REG_TXPAUSE, rtl_read_byte(rtlpriv, REG_TXPAUSE) & (~BIT(6))); } } static void _rtl92cu_enable_bcn_sub_func(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); if (IS_NORMAL_CHIP(rtlhal->version)) _rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(1)); else _rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(4)); } static void _rtl92cu_disable_bcn_sub_func(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); if (IS_NORMAL_CHIP(rtlhal->version)) _rtl92cu_set_bcn_ctrl_reg(hw, BIT(1), 0); else _rtl92cu_set_bcn_ctrl_reg(hw, BIT(4), 0); } static int _rtl92cu_set_media_status(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bt_msr = rtl_read_byte(rtlpriv, MSR); enum led_ctl_mode ledaction = LED_CTL_NO_LINK; bt_msr &= 0xfc; rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0xFF); if (type == NL80211_IFTYPE_UNSPECIFIED || type == NL80211_IFTYPE_STATION) { _rtl92cu_stop_tx_beacon(hw); _rtl92cu_enable_bcn_sub_func(hw); } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP) { _rtl92cu_resume_tx_beacon(hw); _rtl92cu_disable_bcn_sub_func(hw); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Set HW_VAR_MEDIA_STATUS:No such media status(%x)\n", type); } switch (type) { case NL80211_IFTYPE_UNSPECIFIED: bt_msr |= MSR_NOLINK; ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: bt_msr |= MSR_ADHOC; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: bt_msr |= MSR_INFRA; ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: bt_msr |= MSR_AP; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to AP!\n"); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Network type %d not supported!\n", type); goto error_out; } rtl_write_byte(rtlpriv, (MSR), bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); if ((bt_msr & 0xfc) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); return 0; error_out: return 1; } void rtl92cu_card_disable(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); enum nl80211_iftype opmode; mac->link_state = MAC80211_NOLINK; opmode = NL80211_IFTYPE_UNSPECIFIED; _rtl92cu_set_media_status(hw, opmode); rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); if (rtlusb->disableHWSM) _CardDisableHWSM(hw); else _CardDisableWithoutHWSM(hw); } void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { /* dummy routine needed for callback from rtl_op_configure_filter() */ } /*========================================================================== */ static void _rtl92cu_set_check_bssid(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 filterout_non_associated_bssid = false; switch (type) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: filterout_non_associated_bssid = true; break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_AP: default: break; } if (filterout_non_associated_bssid) { if (IS_NORMAL_CHIP(rtlhal->version)) { switch (rtlphy->current_io_type) { case IO_CMD_RESUME_DM_BY_SCAN: reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); /* enable update TSF */ _rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(4)); break; case IO_CMD_PAUSE_DM_BY_SCAN: reg_rcr &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); /* disable update TSF */ _rtl92cu_set_bcn_ctrl_reg(hw, BIT(4), 0); break; } } else { reg_rcr |= (RCR_CBSSID); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); _rtl92cu_set_bcn_ctrl_reg(hw, 0, (BIT(4)|BIT(5))); } } else if (filterout_non_associated_bssid == false) { if (IS_NORMAL_CHIP(rtlhal->version)) { reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); _rtl92cu_set_bcn_ctrl_reg(hw, BIT(4), 0); } else { reg_rcr &= (~RCR_CBSSID); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); _rtl92cu_set_bcn_ctrl_reg(hw, (BIT(4)|BIT(5)), 0); } } } int rtl92cu_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type) { if (_rtl92cu_set_media_status(hw, type)) return -EOPNOTSUPP; _rtl92cu_set_check_bssid(hw, type); return 0; } static void _InitBeaconParameters(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); rtl_write_word(rtlpriv, REG_BCN_CTRL, 0x1010); /* TODO: Remove these magic number */ rtl_write_word(rtlpriv, REG_TBTT_PROHIBIT, 0x6404); rtl_write_byte(rtlpriv, REG_DRVERLYINT, DRIVER_EARLY_INT_TIME); rtl_write_byte(rtlpriv, REG_BCNDMATIM, BCN_DMA_ATIME_INT_TIME); /* Change beacon AIFS to the largest number * beacause test chip does not contension before sending beacon. */ if (IS_NORMAL_CHIP(rtlhal->version)) rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660F); else rtl_write_word(rtlpriv, REG_BCNTCFG, 0x66FF); } static void _beacon_function_enable(struct ieee80211_hw *hw, bool Enable, bool Linked) { struct rtl_priv *rtlpriv = rtl_priv(hw); _rtl92cu_set_bcn_ctrl_reg(hw, (BIT(4) | BIT(3) | BIT(1)), 0x00); rtl_write_byte(rtlpriv, REG_RD_CTRL+1, 0x6F); } void rtl92cu_set_beacon_related_registers(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcn_interval, atim_window; u32 value32; bcn_interval = mac->beacon_interval; atim_window = 2; /*FIX MERGE */ rtl_write_word(rtlpriv, REG_ATIMWND, atim_window); rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); _InitBeaconParameters(hw); rtl_write_byte(rtlpriv, REG_SLOT, 0x09); /* * Force beacon frame transmission even after receiving beacon frame * from other ad hoc STA * * * Reset TSF Timer to zero, added by Roger. 2008.06.24 */ value32 = rtl_read_dword(rtlpriv, REG_TCR); value32 &= ~TSFRST; rtl_write_dword(rtlpriv, REG_TCR, value32); value32 |= TSFRST; rtl_write_dword(rtlpriv, REG_TCR, value32); RT_TRACE(rtlpriv, COMP_INIT|COMP_BEACON, DBG_LOUD, "SetBeaconRelatedRegisters8192CUsb(): Set TCR(%x)\n", value32); /* TODO: Modify later (Find the right parameters) * NOTE: Fix test chip's bug (about contention windows's randomness) */ if ((mac->opmode == NL80211_IFTYPE_ADHOC) || (mac->opmode == NL80211_IFTYPE_AP)) { rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x50); rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x50); } _beacon_function_enable(hw, true, true); } void rtl92cu_set_beacon_interval(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcn_interval = mac->beacon_interval; RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG, "beacon_interval:%d\n", bcn_interval); rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); } void rtl92cu_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr) { } void rtl92cu_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); switch (variable) { case HW_VAR_RCR: *((u32 *)(val)) = mac->rx_conf; break; case HW_VAR_RF_STATE: *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state; break; case HW_VAR_FWLPS_RF_ON:{ enum rf_pwrstate rfState; u32 val_rcr; rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE, (u8 *)(&rfState)); if (rfState == ERFOFF) { *((bool *) (val)) = true; } else { val_rcr = rtl_read_dword(rtlpriv, REG_RCR); val_rcr &= 0x00070000; if (val_rcr) *((bool *) (val)) = false; else *((bool *) (val)) = true; } break; } case HW_VAR_FW_PSMODE_STATUS: *((bool *) (val)) = ppsc->fw_current_inpsmode; break; case HW_VAR_CORRECT_TSF:{ u64 tsf; u32 *ptsf_low = (u32 *)&tsf; u32 *ptsf_high = ((u32 *)&tsf) + 1; *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4)); *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR); *((u64 *)(val)) = tsf; break; } case HW_VAR_MGT_FILTER: *((u16 *) (val)) = rtl_read_word(rtlpriv, REG_RXFLTMAP0); break; case HW_VAR_CTRL_FILTER: *((u16 *) (val)) = rtl_read_word(rtlpriv, REG_RXFLTMAP1); break; case HW_VAR_DATA_FILTER: *((u16 *) (val)) = rtl_read_word(rtlpriv, REG_RXFLTMAP2); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); enum wireless_mode wirelessmode = mac->mode; u8 idx = 0; switch (variable) { case HW_VAR_ETHER_ADDR:{ for (idx = 0; idx < ETH_ALEN; idx++) { rtl_write_byte(rtlpriv, (REG_MACID + idx), val[idx]); } break; } case HW_VAR_BASIC_RATE:{ u16 rate_cfg = ((u16 *) val)[0]; u8 rate_index = 0; rate_cfg &= 0x15f; /* TODO */ /* if (mac->current_network.vender == HT_IOT_PEER_CISCO * && ((rate_cfg & 0x150) == 0)) { * rate_cfg |= 0x010; * } */ rate_cfg |= 0x01; rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff); rtl_write_byte(rtlpriv, REG_RRSR + 1, (rate_cfg >> 8) & 0xff); while (rate_cfg > 0x1) { rate_cfg >>= 1; rate_index++; } rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, rate_index); break; } case HW_VAR_BSSID:{ for (idx = 0; idx < ETH_ALEN; idx++) { rtl_write_byte(rtlpriv, (REG_BSSID + idx), val[idx]); } break; } case HW_VAR_SIFS:{ rtl_write_byte(rtlpriv, REG_SIFS_CCK + 1, val[0]); rtl_write_byte(rtlpriv, REG_SIFS_OFDM + 1, val[1]); rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]); rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]); rtl_write_byte(rtlpriv, REG_R2T_SIFS+1, val[0]); rtl_write_byte(rtlpriv, REG_T2T_SIFS+1, val[0]); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "HW_VAR_SIFS\n"); break; } case HW_VAR_SLOT_TIME:{ u8 e_aci; u8 QOS_MODE = 1; rtl_write_byte(rtlpriv, REG_SLOT, val[0]); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "HW_VAR_SLOT_TIME %x\n", val[0]); if (QOS_MODE) { for (e_aci = 0; e_aci < AC_MAX; e_aci++) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, &e_aci); } else { u8 sifstime = 0; u8 u1bAIFS; if (IS_WIRELESS_MODE_A(wirelessmode) || IS_WIRELESS_MODE_N_24G(wirelessmode) || IS_WIRELESS_MODE_N_5G(wirelessmode)) sifstime = 16; else sifstime = 10; u1bAIFS = sifstime + (2 * val[0]); rtl_write_byte(rtlpriv, REG_EDCA_VO_PARAM, u1bAIFS); rtl_write_byte(rtlpriv, REG_EDCA_VI_PARAM, u1bAIFS); rtl_write_byte(rtlpriv, REG_EDCA_BE_PARAM, u1bAIFS); rtl_write_byte(rtlpriv, REG_EDCA_BK_PARAM, u1bAIFS); } break; } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; u8 short_preamble = (bool)*val; reg_tmp = 0; if (short_preamble) reg_tmp |= 0x80; rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_tmp); break; } case HW_VAR_AMPDU_MIN_SPACE:{ u8 min_spacing_to_set; u8 sec_min_space; min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { switch (rtlpriv->sec.pairwise_enc_algorithm) { case NO_ENCRYPTION: case AESCCMP_ENCRYPTION: sec_min_space = 0; break; case WEP40_ENCRYPTION: case WEP104_ENCRYPTION: case TKIP_ENCRYPTION: sec_min_space = 6; break; default: sec_min_space = 7; break; } if (min_spacing_to_set < sec_min_space) min_spacing_to_set = sec_min_space; mac->min_space_cfg = ((mac->min_space_cfg & 0xf8) | min_spacing_to_set); *val = min_spacing_to_set; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, mac->min_space_cfg); } break; } case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; density_to_set = *val; density_to_set &= 0x1f; mac->min_space_cfg &= 0x07; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_SHORTGI_DENSITY: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, mac->min_space_cfg); break; } case HW_VAR_AMPDU_FACTOR:{ u8 regtoset_normal[4] = {0x41, 0xa8, 0x72, 0xb9}; u8 factor_toset; u8 *p_regtoset = NULL; u8 index = 0; p_regtoset = regtoset_normal; factor_toset = *val; if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) factor_toset = 0xf; for (index = 0; index < 4; index++) { if ((p_regtoset[index] & 0xf0) > (factor_toset << 4)) p_regtoset[index] = (p_regtoset[index] & 0x0f) | (factor_toset << 4); if ((p_regtoset[index] & 0x0f) > factor_toset) p_regtoset[index] = (p_regtoset[index] & 0xf0) | (factor_toset); rtl_write_byte(rtlpriv, (REG_AGGLEN_LMT + index), p_regtoset[index]); } RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_FACTOR: %#x\n", factor_toset); } break; } case HW_VAR_AC_PARAM:{ u8 e_aci = *val; u32 u4b_ac_param; u16 cw_min = le16_to_cpu(mac->ac[e_aci].cw_min); u16 cw_max = le16_to_cpu(mac->ac[e_aci].cw_max); u16 tx_op = le16_to_cpu(mac->ac[e_aci].tx_op); u4b_ac_param = (u32) mac->ac[e_aci].aifs; u4b_ac_param |= (u32) ((cw_min & 0xF) << AC_PARAM_ECW_MIN_OFFSET); u4b_ac_param |= (u32) ((cw_max & 0xF) << AC_PARAM_ECW_MAX_OFFSET); u4b_ac_param |= (u32) tx_op << AC_PARAM_TXOP_OFFSET; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "queue:%x, ac_param:%x\n", e_aci, u4b_ac_param); switch (e_aci) { case AC1_BK: rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, u4b_ac_param); break; case AC0_BE: rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, u4b_ac_param); break; case AC2_VI: rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, u4b_ac_param); break; case AC3_VO: rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, u4b_ac_param); break; default: RT_ASSERT(false, "SetHwReg8185(): invalid aci: %d !\n", e_aci); break; } if (rtlusb->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, &e_aci); break; } case HW_VAR_ACM_CTRL:{ u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *) (&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL); acm_ctrl = acm_ctrl | ((rtlusb->acm_method == 2) ? 0x0 : 0x1); if (acm) { switch (e_aci) { case AC0_BE: acm_ctrl |= AcmHw_BeqEn; break; case AC2_VI: acm_ctrl |= AcmHw_ViqEn; break; case AC3_VO: acm_ctrl |= AcmHw_VoqEn; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "HW_VAR_ACM_CTRL acm set failed: eACI is %d\n", acm); break; } } else { switch (e_aci) { case AC0_BE: acm_ctrl &= (~AcmHw_BeqEn); break; case AC2_VI: acm_ctrl &= (~AcmHw_ViqEn); break; case AC3_VO: acm_ctrl &= (~AcmHw_BeqEn); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE, "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] Write 0x%X\n", acm_ctrl); rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl); break; } case HW_VAR_RCR:{ rtl_write_dword(rtlpriv, REG_RCR, ((u32 *) (val))[0]); mac->rx_conf = ((u32 *) (val))[0]; RT_TRACE(rtlpriv, COMP_RECV, DBG_DMESG, "### Set RCR(0x%08x) ###\n", mac->rx_conf); break; } case HW_VAR_RETRY_LIMIT:{ u8 retry_limit = val[0]; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | retry_limit << RETRY_LIMIT_LONG_SHIFT); RT_TRACE(rtlpriv, COMP_MLME, DBG_DMESG, "Set HW_VAR_RETRY_LIMIT(0x%08x)\n", retry_limit); break; } case HW_VAR_DUAL_TSF_RST: rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1))); break; case HW_VAR_EFUSE_BYTES: rtlefuse->efuse_usedbytes = *((u16 *) val); break; case HW_VAR_EFUSE_USAGE: rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl92c_phy_set_io_cmd(hw, (*(enum io_type *)val)); break; case HW_VAR_WPA_CONFIG: rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_SET_RPWM:{ u8 rpwm_val = rtl_read_byte(rtlpriv, REG_USB_HRPWM); if (rpwm_val & BIT(7)) rtl_write_byte(rtlpriv, REG_USB_HRPWM, *val); else rtl_write_byte(rtlpriv, REG_USB_HRPWM, *val | BIT(7)); break; } case HW_VAR_H2C_FW_PWRMODE:{ u8 psmode = *val; if ((psmode != FW_PS_ACTIVE_MODE) && (!IS_92C_SERIAL(rtlhal->version))) rtl92c_dm_rf_saving(hw, true); rtl92c_set_fw_pwrmode_cmd(hw, (*val)); break; } case HW_VAR_FW_PSMODE_STATUS: ppsc->fw_current_inpsmode = *((bool *) val); break; case HW_VAR_H2C_FW_JOINBSSRPT:{ u8 mstatus = *val; u8 tmp_reg422; bool recover = false; if (mstatus == RT_MEDIA_CONNECT) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AID, NULL); rtl_write_byte(rtlpriv, REG_CR + 1, 0x03); _rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(3)); _rtl92cu_set_bcn_ctrl_reg(hw, BIT(4), 0); tmp_reg422 = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); if (tmp_reg422 & BIT(6)) recover = true; rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp_reg422 & (~BIT(6))); rtl92c_set_fw_rsvdpagepkt(hw, 0); _rtl92cu_set_bcn_ctrl_reg(hw, BIT(3), 0); _rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(4)); if (recover) rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp_reg422 | BIT(6)); rtl_write_byte(rtlpriv, REG_CR + 1, 0x02); } rtl92c_set_fw_joinbss_report_cmd(hw, (*val)); break; } case HW_VAR_AID:{ u16 u2btmp; u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT); u2btmp &= 0xC000; rtl_write_word(rtlpriv, REG_BCN_PSR_RPT, (u2btmp | mac->assoc_id)); break; } case HW_VAR_CORRECT_TSF:{ u8 btype_ibss = val[0]; if (btype_ibss) _rtl92cu_stop_tx_beacon(hw); _rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(3)); rtl_write_dword(rtlpriv, REG_TSFTR, (u32)(mac->tsf & 0xffffffff)); rtl_write_dword(rtlpriv, REG_TSFTR + 4, (u32)((mac->tsf >> 32) & 0xffffffff)); _rtl92cu_set_bcn_ctrl_reg(hw, BIT(3), 0); if (btype_ibss) _rtl92cu_resume_tx_beacon(hw); break; } case HW_VAR_MGT_FILTER: rtl_write_word(rtlpriv, REG_RXFLTMAP0, *(u16 *)val); break; case HW_VAR_CTRL_FILTER: rtl_write_word(rtlpriv, REG_RXFLTMAP1, *(u16 *)val); break; case HW_VAR_DATA_FILTER: rtl_write_word(rtlpriv, REG_RXFLTMAP2, *(u16 *)val); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } void rtl92cu_update_hal_rate_table(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u32 ratr_value = (u32) mac->basic_rates; u8 *mcsrate = mac->mcs; u8 ratr_index = 0; u8 nmode = mac->ht_enable; u8 mimo_ps = 1; u16 shortgi_rate = 0; u32 tmp_ratr_value = 0; u8 curtxbw_40mhz = mac->bw_40; u8 curshortgi_40mhz = mac->sgi_40; u8 curshortgi_20mhz = mac->sgi_20; enum wireless_mode wirelessmode = mac->mode; ratr_value |= ((*(u16 *) (mcsrate))) << 12; switch (wirelessmode) { case WIRELESS_MODE_B: if (ratr_value & 0x0000000c) ratr_value &= 0x0000000d; else ratr_value &= 0x0000000f; break; case WIRELESS_MODE_G: ratr_value &= 0x00000FF5; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: nmode = 1; if (mimo_ps == 0) { ratr_value &= 0x0007F005; } else { u32 ratr_mask; if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_1T1R) ratr_mask = 0x000ff005; else ratr_mask = 0x0f0ff005; if (curtxbw_40mhz) ratr_mask |= 0x00000010; ratr_value &= ratr_mask; } break; default: if (rtlphy->rf_type == RF_1T2R) ratr_value &= 0x000ff0ff; else ratr_value &= 0x0f0ff0ff; break; } ratr_value &= 0x0FFFFFFF; if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz))) { ratr_value |= 0x10000000; tmp_ratr_value = (ratr_value >> 12); for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) { if ((1 << shortgi_rate) & tmp_ratr_value) break; } shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) | (shortgi_rate << 4) | (shortgi_rate); } rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value); RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0)); } void rtl92cu_update_hal_rate_mask(struct ieee80211_hw *hw, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u32 ratr_bitmap = (u32) mac->basic_rates; u8 *p_mcsrate = mac->mcs; u8 ratr_index = 0; u8 curtxbw_40mhz = mac->bw_40; u8 curshortgi_40mhz = mac->sgi_40; u8 curshortgi_20mhz = mac->sgi_20; enum wireless_mode wirelessmode = mac->mode; bool shortgi = false; u8 rate_mask[5]; u8 macid = 0; u8 mimops = 1; ratr_bitmap |= (p_mcsrate[1] << 20) | (p_mcsrate[0] << 12); switch (wirelessmode) { case WIRELESS_MODE_B: ratr_index = RATR_INX_WIRELESS_B; if (ratr_bitmap & 0x0000000c) ratr_bitmap &= 0x0000000d; else ratr_bitmap &= 0x0000000f; break; case WIRELESS_MODE_G: ratr_index = RATR_INX_WIRELESS_GB; if (rssi_level == 1) ratr_bitmap &= 0x00000f00; else if (rssi_level == 2) ratr_bitmap &= 0x00000ff0; else ratr_bitmap &= 0x00000ff5; break; case WIRELESS_MODE_A: ratr_index = RATR_INX_WIRELESS_A; ratr_bitmap &= 0x00000ff0; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: ratr_index = RATR_INX_WIRELESS_NGB; if (mimops == 0) { if (rssi_level == 1) ratr_bitmap &= 0x00070000; else if (rssi_level == 2) ratr_bitmap &= 0x0007f000; else ratr_bitmap &= 0x0007f005; } else { if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_1T1R) { if (curtxbw_40mhz) { if (rssi_level == 1) ratr_bitmap &= 0x000f0000; else if (rssi_level == 2) ratr_bitmap &= 0x000ff000; else ratr_bitmap &= 0x000ff015; } else { if (rssi_level == 1) ratr_bitmap &= 0x000f0000; else if (rssi_level == 2) ratr_bitmap &= 0x000ff000; else ratr_bitmap &= 0x000ff005; } } else { if (curtxbw_40mhz) { if (rssi_level == 1) ratr_bitmap &= 0x0f0f0000; else if (rssi_level == 2) ratr_bitmap &= 0x0f0ff000; else ratr_bitmap &= 0x0f0ff015; } else { if (rssi_level == 1) ratr_bitmap &= 0x0f0f0000; else if (rssi_level == 2) ratr_bitmap &= 0x0f0ff000; else ratr_bitmap &= 0x0f0ff005; } } } if ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz)) { if (macid == 0) shortgi = true; else if (macid == 1) shortgi = false; } break; default: ratr_index = RATR_INX_WIRELESS_NGB; if (rtlphy->rf_type == RF_1T2R) ratr_bitmap &= 0x000ff0ff; else ratr_bitmap &= 0x0f0ff0ff; break; } RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "ratr_bitmap :%x\n", ratr_bitmap); *(u32 *)&rate_mask = ((ratr_bitmap & 0x0fffffff) | ratr_index << 28); rate_mask[4] = macid | (shortgi ? 0x20 : 0x00) | 0x80; RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "Rate_index:%x, ratr_val:%x, %x:%x:%x:%x:%x\n", ratr_index, ratr_bitmap, rate_mask[0], rate_mask[1], rate_mask[2], rate_mask[3], rate_mask[4]); rtl92c_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask); } void rtl92cu_update_channel_access_setting(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else sifs_timer = 0x0e0e; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer); } bool rtl92cu_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 * valid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); enum rf_pwrstate e_rfpowerstate_toset, cur_rfstate; u8 u1tmp = 0; bool actuallyset = false; unsigned long flag = 0; /* to do - usb autosuspend */ u8 usb_autosuspend = 0; if (ppsc->swrf_processing) return false; spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); if (ppsc->rfchange_inprogress) { spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); return false; } else { ppsc->rfchange_inprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } cur_rfstate = ppsc->rfpwr_state; if (usb_autosuspend) { /* to do................... */ } else { if (ppsc->pwrdown_mode) { u1tmp = rtl_read_byte(rtlpriv, REG_HSISR); e_rfpowerstate_toset = (u1tmp & BIT(7)) ? ERFOFF : ERFON; RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "pwrdown, 0x5c(BIT7)=%02x\n", u1tmp); } else { rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG) & ~(BIT(3))); u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL); e_rfpowerstate_toset = (u1tmp & BIT(3)) ? ERFON : ERFOFF; RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, "GPIO_IN=%02x\n", u1tmp); } RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "N-SS RF =%x\n", e_rfpowerstate_toset); } if ((ppsc->hwradiooff) && (e_rfpowerstate_toset == ERFON)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "GPIOChangeRF - HW Radio ON, RF ON\n"); ppsc->hwradiooff = false; actuallyset = true; } else if ((!ppsc->hwradiooff) && (e_rfpowerstate_toset == ERFOFF)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "GPIOChangeRF - HW Radio OFF\n"); ppsc->hwradiooff = true; actuallyset = true; } else { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "pHalData->bHwRadioOff and eRfPowerStateToSet do not match: pHalData->bHwRadioOff %x, eRfPowerStateToSet %x\n", ppsc->hwradiooff, e_rfpowerstate_toset); } if (actuallyset) { ppsc->hwradiooff = true; if (e_rfpowerstate_toset == ERFON) { if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) && RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_ASPM)) RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_ASPM); else if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_PCI_D3) && RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_PCI_D3)) RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_PCI_D3); } spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); /* For power down module, we need to enable register block * contrl reg at 0x1c. Then enable power down control bit * of register 0x04 BIT4 and BIT15 as 1. */ if (ppsc->pwrdown_mode && e_rfpowerstate_toset == ERFOFF) { /* Enable register area 0x0-0xc. */ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0); if (IS_HARDWARE_TYPE_8723U(rtlhal)) { /* * We should configure HW PDn source for WiFi * ONLY, and then our HW will be set in * power-down mode if PDn source from all * functions are configured. */ u1tmp = rtl_read_byte(rtlpriv, REG_MULTI_FUNC_CTRL); rtl_write_byte(rtlpriv, REG_MULTI_FUNC_CTRL, (u1tmp|WL_HWPDN_EN)); } else { rtl_write_word(rtlpriv, REG_APS_FSMCO, 0x8812); } } if (e_rfpowerstate_toset == ERFOFF) { if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_ASPM); else if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_PCI_D3) RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_PCI_D3); } } else if (e_rfpowerstate_toset == ERFOFF || cur_rfstate == ERFOFF) { /* Enter D3 or ASPM after GPIO had been done. */ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_ASPM); else if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_PCI_D3) RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_PCI_D3); spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } else { spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } *valid = 1; return !ppsc->hwradiooff; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h0000644000175000017500000000236212026211315024372 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../rtl8192ce/dm.h" void rtl92cu_dm_dynamic_txpower(struct ieee80211_hw *hw); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c0000644000175000017500000000731712026211315024372 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" void rtl92cu_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb; if (!rtlpriv->dm.dynamic_txpower_enable) return; if (rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if ((mac->link_state < MAC80211_LINKED) && (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb == 0)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "Not connected to any\n"); rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if (mac->link_state >= MAC80211_LINKED) { if (mac->opmode == NL80211_IFTYPE_ADHOC) { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Client PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } else { undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "STA Default Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } } else { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Ext Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } if (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL2) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x0)\n"); } else if ((undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3)) && (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL1)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x10)\n"); } else if (undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL1 - 5)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_NORMAL\n"); } if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "PHY_SetTxPowerLevel8192S() Channel = %d\n", rtlphy->current_channel); rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); } rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/def.h0000644000175000017500000000415612026211315024533 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../rtl8192ce/def.h" /*------------------------------------------------------------------------- * Chip specific *-------------------------------------------------------------------------*/ #define CHIP_8723 BIT(2) /* RTL8723 With BT feature */ #define CHIP_8723_DRV_REV BIT(3) /* RTL8723 Driver Revised */ #define NORMAL_CHIP BIT(4) #define CHIP_VENDOR_UMC BIT(5) #define CHIP_VENDOR_UMC_B_CUT BIT(6) #define IS_NORMAL_CHIP(version) \ (((version) & NORMAL_CHIP) ? true : false) #define IS_8723_SERIES(version) \ (((version) & CHIP_8723) ? true : false) #define IS_92C_1T2R(version) \ (((version) & CHIP_92C) && ((version) & CHIP_92C_1T2R)) #define IS_VENDOR_UMC(version) \ (((version) & CHIP_VENDOR_UMC) ? true : false) #define IS_VENDOR_8723_A_CUT(version) \ (((version) & CHIP_VENDOR_UMC) ? (((version) & (BIT(6))) ? \ false : true) : false) #define CHIP_BONDING_92C_1T2R 0x1 #define CHIP_BONDING_IDENTIFIER(_value) (((_value) >> 22) & 0x3) compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192cu/Makefile0000644000175000017500000000027412026211315025261 0ustar mcgrofmcgrofrtl8192cu-objs := \ dm.o \ hw.o \ led.o \ mac.o \ phy.o \ rf.o \ sw.o \ table.o \ trx.o obj-$(CONFIG_RTL8192CU) += rtl8192cu.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/0000755000175000017500000000000012026211315023576 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c0000644000175000017500000003170612026211315024402 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../core.h" #include "../pci.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "hw.h" #include "rf.h" #include "sw.h" #include "trx.h" #include "led.h" #include static void rtl92c_init_aspm_vars(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); /*close ASPM for AMD defaultly */ rtlpci->const_amdpci_aspm = 0; /* * ASPM PS mode. * 0 - Disable ASPM, * 1 - Enable ASPM without Clock Req, * 2 - Enable ASPM with Clock Req, * 3 - Alwyas Enable ASPM with Clock Req, * 4 - Always Enable ASPM without Clock Req. * set defult to RTL8192CE:3 RTL8192E:2 * */ rtlpci->const_pci_aspm = 3; /*Setting for PCI-E device */ rtlpci->const_devicepci_aspm_setting = 0x03; /*Setting for PCI-E bridge */ rtlpci->const_hostpci_aspm_setting = 0x02; /* * In Hw/Sw Radio Off situation. * 0 - Default, * 1 - From ASPM setting without low Mac Pwr, * 2 - From ASPM setting with low Mac Pwr, * 3 - Bus D3 * set default to RTL8192CE:0 RTL8192SE:2 */ rtlpci->const_hwsw_rfoff_d3 = 0; /* * This setting works for those device with * backdoor ASPM setting such as EPHY setting. * 0 - Not support ASPM, * 1 - Support ASPM, * 2 - According to chipset. */ rtlpci->const_support_pciaspm = 1; } int rtl92c_init_sw_vars(struct ieee80211_hw *hw) { int err; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); rtl8192ce_bt_reg_init(hw); rtlpriv->dm.dm_initialgain_enable = true; rtlpriv->dm.dm_flag = 0; rtlpriv->dm.disable_framebursting = false; rtlpriv->dm.thermalvalue = 0; rtlpci->transmit_config = CFENDFORM | BIT(12) | BIT(13); /* compatible 5G band 88ce just 2.4G band & smsp */ rtlpriv->rtlhal.current_bandtype = BAND_ON_2_4G; rtlpriv->rtlhal.bandset = BAND_ON_2_4G; rtlpriv->rtlhal.macphymode = SINGLEMAC_SINGLEPHY; rtlpci->receive_config = (RCR_APPFCS | RCR_AMF | RCR_ADF | RCR_APP_MIC | RCR_APP_ICV | RCR_AICV | RCR_ACRC32 | RCR_AB | RCR_AM | RCR_APM | RCR_APP_PHYST_RXFF | RCR_HTC_LOC_CTRL | 0); rtlpci->irq_mask[0] = (u32) (IMR_ROK | IMR_VODOK | IMR_VIDOK | IMR_BEDOK | IMR_BKDOK | IMR_MGNTDOK | IMR_HIGHDOK | IMR_BDOK | IMR_RDU | IMR_RXFOVW | 0); rtlpci->irq_mask[1] = (u32) (IMR_CPWM | IMR_C2HCMD | 0); /* for debug level */ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug; /* for LPS & IPS */ rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; if (!rtlpriv->psc.inactiveps) pr_info("rtl8192ce: Power Save off (module option)\n"); if (!rtlpriv->psc.fwctrl_lps) pr_info("rtl8192ce: FW Power Save off (module option)\n"); rtlpriv->psc.reg_fwctrl_lps = 3; rtlpriv->psc.reg_max_lps_awakeintvl = 5; /* for ASPM, you can close aspm through * set const_support_pciaspm = 0 */ rtl92c_init_aspm_vars(hw); if (rtlpriv->psc.reg_fwctrl_lps == 1) rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE; else if (rtlpriv->psc.reg_fwctrl_lps == 2) rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE; else if (rtlpriv->psc.reg_fwctrl_lps == 3) rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE; /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(0x4000); if (!rtlpriv->rtlhal.pfirmware) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't alloc buffer for fw\n"); return 1; } /* request fw */ if (IS_VENDOR_UMC_A_CUT(rtlhal->version) && !IS_92C_SERIAL(rtlhal->version)) { rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU.bin"; } else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) { rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU_B.bin"; pr_info("****** This B_CUT device may not work with kernels 3.6 and earlier\n"); } rtlpriv->max_fw_size = 0x4000; pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name); err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to request firmware!\n"); return 1; } return 0; } void rtl92c_deinit_sw_vars(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->rtlhal.pfirmware) { vfree(rtlpriv->rtlhal.pfirmware); rtlpriv->rtlhal.pfirmware = NULL; } } static struct rtl_hal_ops rtl8192ce_hal_ops = { .init_sw_vars = rtl92c_init_sw_vars, .deinit_sw_vars = rtl92c_deinit_sw_vars, .read_eeprom_info = rtl92ce_read_eeprom_info, .interrupt_recognized = rtl92ce_interrupt_recognized, .hw_init = rtl92ce_hw_init, .hw_disable = rtl92ce_card_disable, .hw_suspend = rtl92ce_suspend, .hw_resume = rtl92ce_resume, .enable_interrupt = rtl92ce_enable_interrupt, .disable_interrupt = rtl92ce_disable_interrupt, .set_network_type = rtl92ce_set_network_type, .set_chk_bssid = rtl92ce_set_check_bssid, .set_qos = rtl92ce_set_qos, .set_bcn_reg = rtl92ce_set_beacon_related_registers, .set_bcn_intv = rtl92ce_set_beacon_interval, .update_interrupt_mask = rtl92ce_update_interrupt_mask, .get_hw_reg = rtl92ce_get_hw_reg, .set_hw_reg = rtl92ce_set_hw_reg, .update_rate_tbl = rtl92ce_update_hal_rate_tbl, .fill_tx_desc = rtl92ce_tx_fill_desc, .fill_tx_cmddesc = rtl92ce_tx_fill_cmddesc, .query_rx_desc = rtl92ce_rx_query_desc, .set_channel_access = rtl92ce_update_channel_access_setting, .radio_onoff_checking = rtl92ce_gpio_radio_on_off_checking, .set_bw_mode = rtl92c_phy_set_bw_mode, .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, .scan_operation_backup = rtl92c_phy_scan_operation_backup, .set_rf_power_state = rtl92c_phy_set_rf_power_state, .led_control = rtl92ce_led_control, .set_desc = rtl92ce_set_desc, .get_desc = rtl92ce_get_desc, .tx_polling = rtl92ce_tx_polling, .enable_hw_sec = rtl92ce_enable_hw_security_config, .set_key = rtl92ce_set_key, .init_sw_leds = rtl92ce_init_sw_leds, .get_bbreg = rtl92c_phy_query_bb_reg, .set_bbreg = rtl92c_phy_set_bb_reg, .set_rfreg = rtl92ce_phy_set_rf_reg, .get_rfreg = rtl92c_phy_query_rf_reg, .phy_rf6052_config = rtl92ce_phy_rf6052_config, .phy_rf6052_set_cck_txpower = rtl92ce_phy_rf6052_set_cck_txpower, .phy_rf6052_set_ofdm_txpower = rtl92ce_phy_rf6052_set_ofdm_txpower, .config_bb_with_headerfile = _rtl92ce_phy_config_bb_with_headerfile, .config_bb_with_pgheaderfile = _rtl92ce_phy_config_bb_with_pgheaderfile, .phy_lc_calibrate = _rtl92ce_phy_lc_calibrate, .phy_set_bw_mode_callback = rtl92ce_phy_set_bw_mode_callback, .dm_dynamic_txpower = rtl92ce_dm_dynamic_txpower, }; static struct rtl_mod_params rtl92ce_mod_params = { .sw_crypto = false, .inactiveps = true, .swctrl_lps = false, .fwctrl_lps = true, .debug = DBG_EMERG, }; static struct rtl_hal_cfg rtl92ce_hal_cfg = { .bar_id = 2, .write_readback = true, .name = "rtl92c_pci", .fw_name = "rtlwifi/rtl8192cfw.bin", .ops = &rtl8192ce_hal_ops, .mod_params = &rtl92ce_mod_params, .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN, .maps[SYS_CLK] = REG_SYS_CLKR, .maps[MAC_RCR_AM] = AM, .maps[MAC_RCR_AB] = AB, .maps[MAC_RCR_ACRC32] = ACRC32, .maps[MAC_RCR_ACF] = ACF, .maps[MAC_RCR_AAP] = AAP, .maps[EFUSE_TEST] = REG_EFUSE_TEST, .maps[EFUSE_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_CLK] = 0, .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL, .maps[EFUSE_PWC_EV12V] = PWC_EV12V, .maps[EFUSE_FEN_ELDR] = FEN_ELDR, .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN, .maps[EFUSE_ANA8M] = EFUSE_ANA8M, .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE, .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION, .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN, .maps[RWCAM] = REG_CAMCMD, .maps[WCAMI] = REG_CAMWRITE, .maps[RCAMO] = REG_CAMREAD, .maps[CAMDBG] = REG_CAMDBG, .maps[SECR] = REG_SECCFG, .maps[SEC_CAM_NONE] = CAM_NONE, .maps[SEC_CAM_WEP40] = CAM_WEP40, .maps[SEC_CAM_TKIP] = CAM_TKIP, .maps[SEC_CAM_AES] = CAM_AES, .maps[SEC_CAM_WEP104] = CAM_WEP104, .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6, .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5, .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4, .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3, .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2, .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1, .maps[RTL_IMR_BCNDOK8] = IMR_BCNDOK8, .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7, .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6, .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5, .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4, .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3, .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2, .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1, .maps[RTL_IMR_TIMEOUT2] = IMR_TIMEOUT2, .maps[RTL_IMR_TIMEOUT1] = IMR_TIMEOUT1, .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW, .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT, .maps[RTL_IMR_BcnInt] = IMR_BCNINT, .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW, .maps[RTL_IMR_RDU] = IMR_RDU, .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND, .maps[RTL_IMR_BDOK] = IMR_BDOK, .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK, .maps[RTL_IMR_TBDER] = IMR_TBDER, .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK, .maps[RTL_IMR_TBDOK] = IMR_TBDOK, .maps[RTL_IMR_BKDOK] = IMR_BKDOK, .maps[RTL_IMR_BEDOK] = IMR_BEDOK, .maps[RTL_IMR_VIDOK] = IMR_VIDOK, .maps[RTL_IMR_VODOK] = IMR_VODOK, .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, }; static DEFINE_PCI_DEVICE_TABLE(rtl92ce_pci_ids) = { {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8191, rtl92ce_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8178, rtl92ce_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8177, rtl92ce_hal_cfg)}, {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8176, rtl92ce_hal_cfg)}, {}, }; MODULE_DEVICE_TABLE(pci, rtl92ce_pci_ids); MODULE_AUTHOR("lizhaoming "); MODULE_AUTHOR("Realtek WlanFAE "); MODULE_AUTHOR("Larry Finger "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8192C/8188C 802.11n PCI wireless"); MODULE_FIRMWARE("rtlwifi/rtl8192cfw.bin"); MODULE_FIRMWARE("rtlwifi/rtl8192cfwU.bin"); MODULE_FIRMWARE("rtlwifi/rtl8192cfwU_B.bin"); module_param_named(swenc, rtl92ce_mod_params.sw_crypto, bool, 0444); module_param_named(debug, rtl92ce_mod_params.debug, int, 0444); module_param_named(ips, rtl92ce_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl92ce_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92ce_mod_params.fwctrl_lps, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); compat_pci_suspend(rtl_pci_suspend) compat_pci_resume(rtl_pci_resume) static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); static struct pci_driver rtl92ce_driver = { .name = KBUILD_MODNAME, .id_table = rtl92ce_pci_ids, .probe = rtl_pci_probe, .remove = rtl_pci_disconnect, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &rtlwifi_pm_ops, #elif defined(CONFIG_PM) .suspend = rtl_pci_suspend_compat, .resume = rtl_pci_resume_compat, #endif }; module_pci_driver(rtl92ce_driver); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h0000644000175000017500000005772312026211315024602 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CE_TRX_H__ #define __RTL92CE_TRX_H__ #define TX_DESC_SIZE 64 #define TX_DESC_AGGR_SUBFRAME_SIZE 32 #define RX_DESC_SIZE 32 #define RX_DRV_INFO_SIZE_UNIT 8 #define TX_DESC_NEXT_DESC_OFFSET 40 #define USB_HWDESC_HEADER_LEN 32 #define CRCLENGTH 4 /* Define a macro that takes a le32 word, converts it to host ordering, * right shifts by a specified count, creates a mask of the specified * bit count, and extracts that number of bits. */ #define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \ ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \ BIT_LEN_MASK_32(__mask)) /* Define a macro that clears a bit field in an le32 word and * sets the specified value into that bit field. The resulting * value remains in le32 ordering; however, it is properly converted * to host ordering for the clear and set operations before conversion * back to le32. */ #define SET_BITS_OFFSET_LE(__pdesc, __shift, __len, __val) \ (*(__le32 *)(__pdesc) = \ (cpu_to_le32((le32_to_cpu(*((__le32 *)(__pdesc))) & \ (~(BIT_OFFSET_LEN_MASK_32((__shift), __len)))) | \ (((u32)(__val) & BIT_LEN_MASK_32(__len)) << (__shift))))); /* macros to read/write various fields in RX or TX descriptors */ #define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 0, 16, __val) #define SET_TX_DESC_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 16, 8, __val) #define SET_TX_DESC_BMC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 24, 1, __val) #define SET_TX_DESC_HTC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 25, 1, __val) #define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 26, 1, __val) #define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 27, 1, __val) #define SET_TX_DESC_LINIP(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 28, 1, __val) #define SET_TX_DESC_NO_ACM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 29, 1, __val) #define SET_TX_DESC_GF(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) #define SET_TX_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) #define GET_TX_DESC_PKT_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 0, 16) #define GET_TX_DESC_OFFSET(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 16, 8) #define GET_TX_DESC_BMC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 24, 1) #define GET_TX_DESC_HTC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 25, 1) #define GET_TX_DESC_LAST_SEG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 26, 1) #define GET_TX_DESC_FIRST_SEG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 27, 1) #define GET_TX_DESC_LINIP(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 28, 1) #define GET_TX_DESC_NO_ACM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 29, 1) #define GET_TX_DESC_GF(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 30, 1) #define GET_TX_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) #define SET_TX_DESC_MACID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 0, 5, __val) #define SET_TX_DESC_AGG_BREAK(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 5, 1, __val) #define SET_TX_DESC_BK(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 6, 1, __val) #define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 7, 1, __val) #define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 8, 5, __val) #define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 13, 1, __val) #define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 14, 1, __val) #define SET_TX_DESC_PIFS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 15, 1, __val) #define SET_TX_DESC_RATE_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 16, 4, __val) #define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 20, 1, __val) #define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 21, 1, __val) #define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 22, 2, __val) #define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 24, 8, __val) #define GET_TX_DESC_MACID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 0, 5) #define GET_TX_DESC_AGG_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 5, 1) #define GET_TX_DESC_AGG_BREAK(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 6, 1) #define GET_TX_DESC_RDG_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 7, 1) #define GET_TX_DESC_QUEUE_SEL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 8, 5) #define GET_TX_DESC_RDG_NAV_EXT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 13, 1) #define GET_TX_DESC_LSIG_TXOP_EN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 14, 1) #define GET_TX_DESC_PIFS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 15, 1) #define GET_TX_DESC_RATE_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 16, 4) #define GET_TX_DESC_NAV_USE_HDR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 20, 1) #define GET_TX_DESC_EN_DESC_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 21, 1) #define GET_TX_DESC_SEC_TYPE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 22, 2) #define GET_TX_DESC_PKT_OFFSET(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 24, 8) #define SET_TX_DESC_RTS_RC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 0, 6, __val) #define SET_TX_DESC_DATA_RC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 6, 6, __val) #define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 14, 2, __val) #define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 17, 1, __val) #define SET_TX_DESC_RAW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 18, 1, __val) #define SET_TX_DESC_CCX(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 19, 1, __val) #define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 20, 3, __val) #define SET_TX_DESC_ANTSEL_A(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 24, 1, __val) #define SET_TX_DESC_ANTSEL_B(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 25, 1, __val) #define SET_TX_DESC_TX_ANT_CCK(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 26, 2, __val) #define SET_TX_DESC_TX_ANTL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 28, 2, __val) #define SET_TX_DESC_TX_ANT_HT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 30, 2, __val) #define GET_TX_DESC_RTS_RC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 0, 6) #define GET_TX_DESC_DATA_RC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 6, 6) #define GET_TX_DESC_BAR_RTY_TH(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 14, 2) #define GET_TX_DESC_MORE_FRAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 17, 1) #define GET_TX_DESC_RAW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 18, 1) #define GET_TX_DESC_CCX(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 19, 1) #define GET_TX_DESC_AMPDU_DENSITY(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 20, 3) #define GET_TX_DESC_ANTSEL_A(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 24, 1) #define GET_TX_DESC_ANTSEL_B(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 25, 1) #define GET_TX_DESC_TX_ANT_CCK(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 26, 2) #define GET_TX_DESC_TX_ANTL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 28, 2) #define GET_TX_DESC_TX_ANT_HT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 30, 2) #define SET_TX_DESC_NEXT_HEAP_PAGE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 0, 8, __val) #define SET_TX_DESC_TAIL_PAGE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 8, 8, __val) #define SET_TX_DESC_SEQ(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 16, 12, __val) #define SET_TX_DESC_PKT_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 28, 4, __val) #define GET_TX_DESC_NEXT_HEAP_PAGE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 0, 8) #define GET_TX_DESC_TAIL_PAGE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 8, 8) #define GET_TX_DESC_SEQ(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 16, 12) #define GET_TX_DESC_PKT_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 28, 4) #define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 0, 5, __val) #define SET_TX_DESC_AP_DCFE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 5, 1, __val) #define SET_TX_DESC_QOS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 6, 1, __val) #define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 7, 1, __val) #define SET_TX_DESC_USE_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 8, 1, __val) #define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 9, 1, __val) #define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 10, 1, __val) #define SET_TX_DESC_CTS2SELF(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 11, 1, __val) #define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 12, 1, __val) #define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 13, 1, __val) #define SET_TX_DESC_PORT_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 14, 1, __val) #define SET_TX_DESC_WAIT_DCTS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 18, 1, __val) #define SET_TX_DESC_CTS2AP_EN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 19, 1, __val) #define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 20, 2, __val) #define SET_TX_DESC_TX_STBC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 22, 2, __val) #define SET_TX_DESC_DATA_SHORT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 24, 1, __val) #define SET_TX_DESC_DATA_BW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 25, 1, __val) #define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 26, 1, __val) #define SET_TX_DESC_RTS_BW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 27, 1, __val) #define SET_TX_DESC_RTS_SC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 28, 2, __val) #define SET_TX_DESC_RTS_STBC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 30, 2, __val) #define GET_TX_DESC_RTS_RATE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 0, 5) #define GET_TX_DESC_AP_DCFE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 5, 1) #define GET_TX_DESC_QOS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 6, 1) #define GET_TX_DESC_HWSEQ_EN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 7, 1) #define GET_TX_DESC_USE_RATE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 8, 1) #define GET_TX_DESC_DISABLE_RTS_FB(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 9, 1) #define GET_TX_DESC_DISABLE_FB(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 10, 1) #define GET_TX_DESC_CTS2SELF(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 11, 1) #define GET_TX_DESC_RTS_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 12, 1) #define GET_TX_DESC_HW_RTS_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 13, 1) #define GET_TX_DESC_PORT_ID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 14, 1) #define GET_TX_DESC_WAIT_DCTS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 18, 1) #define GET_TX_DESC_CTS2AP_EN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 19, 1) #define GET_TX_DESC_TX_SUB_CARRIER(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 20, 2) #define GET_TX_DESC_TX_STBC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 22, 2) #define GET_TX_DESC_DATA_SHORT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 24, 1) #define GET_TX_DESC_DATA_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 25, 1) #define GET_TX_DESC_RTS_SHORT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 26, 1) #define GET_TX_DESC_RTS_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 27, 1) #define GET_TX_DESC_RTS_SC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 28, 2) #define GET_TX_DESC_RTS_STBC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 30, 2) #define SET_TX_DESC_TX_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 0, 6, __val) #define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 6, 1, __val) #define SET_TX_DESC_CCX_TAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 7, 1, __val) #define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 8, 5, __val) #define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 13, 4, __val) #define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 17, 1, __val) #define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 18, 6, __val) #define SET_TX_DESC_USB_TXAGG_NUM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 24, 8, __val) #define GET_TX_DESC_TX_RATE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 0, 6) #define GET_TX_DESC_DATA_SHORTGI(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 6, 1) #define GET_TX_DESC_CCX_TAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 7, 1) #define GET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 8, 5) #define GET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 13, 4) #define GET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 17, 1) #define GET_TX_DESC_DATA_RETRY_LIMIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 18, 6) #define GET_TX_DESC_USB_TXAGG_NUM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 24, 8) #define SET_TX_DESC_TXAGC_A(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 0, 5, __val) #define SET_TX_DESC_TXAGC_B(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 5, 5, __val) #define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 10, 1, __val) #define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 11, 5, __val) #define SET_TX_DESC_MCSG1_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 16, 4, __val) #define SET_TX_DESC_MCSG2_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 20, 4, __val) #define SET_TX_DESC_MCSG3_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 24, 4, __val) #define SET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 28, 4, __val) #define GET_TX_DESC_TXAGC_A(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 0, 5) #define GET_TX_DESC_TXAGC_B(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 5, 5) #define GET_TX_DESC_USE_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 10, 1) #define GET_TX_DESC_MAX_AGG_NUM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 11, 5) #define GET_TX_DESC_MCSG1_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 16, 4) #define GET_TX_DESC_MCSG2_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 20, 4) #define GET_TX_DESC_MCSG3_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 24, 4) #define GET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 28, 4) #define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 0, 16, __val) #define SET_TX_DESC_MCSG4_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 16, 4, __val) #define SET_TX_DESC_MCSG5_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 20, 4, __val) #define SET_TX_DESC_MCSG6_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 24, 4, __val) #define SET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 28, 4, __val) #define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 0, 16) #define GET_TX_DESC_MCSG4_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 16, 4) #define GET_TX_DESC_MCSG5_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 20, 4) #define GET_TX_DESC_MCSG6_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 24, 4) #define GET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 28, 4) #define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+32, 0, 32, __val) #define SET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+36, 0, 32, __val) #define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+32, 0, 32) #define GET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+36, 0, 32) #define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+40, 0, 32, __val) #define SET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+44, 0, 32, __val) #define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+40, 0, 32) #define GET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+44, 0, 32) #define GET_RX_DESC_PKT_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 0, 14) #define GET_RX_DESC_CRC32(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 14, 1) #define GET_RX_DESC_ICV(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 15, 1) #define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 16, 4) #define GET_RX_DESC_SECURITY(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 20, 3) #define GET_RX_DESC_QOS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 23, 1) #define GET_RX_DESC_SHIFT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 24, 2) #define GET_RX_DESC_PHYST(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 26, 1) #define GET_RX_DESC_SWDEC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 27, 1) #define GET_RX_DESC_LS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 28, 1) #define GET_RX_DESC_FS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 29, 1) #define GET_RX_DESC_EOR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 30, 1) #define GET_RX_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) #define SET_RX_DESC_PKT_LEN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 0, 14, __val) #define SET_RX_DESC_EOR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) #define SET_RX_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) #define GET_RX_DESC_MACID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 0, 5) #define GET_RX_DESC_TID(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 5, 4) #define GET_RX_DESC_HWRSVD(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 9, 5) #define GET_RX_DESC_PAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 14, 1) #define GET_RX_DESC_FAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 15, 1) #define GET_RX_DESC_A1_FIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 16, 4) #define GET_RX_DESC_A2_FIT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 20, 4) #define GET_RX_DESC_PAM(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 24, 1) #define GET_RX_DESC_PWR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 25, 1) #define GET_RX_DESC_MD(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 26, 1) #define GET_RX_DESC_MF(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 27, 1) #define GET_RX_DESC_TYPE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 28, 2) #define GET_RX_DESC_MC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 30, 1) #define GET_RX_DESC_BC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 31, 1) #define GET_RX_DESC_SEQ(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 0, 12) #define GET_RX_DESC_FRAG(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 12, 4) #define GET_RX_DESC_NEXT_PKT_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 16, 14) #define GET_RX_DESC_NEXT_IND(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 30, 1) #define GET_RX_DESC_RSVD(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+8, 31, 1) #define GET_RX_DESC_RXMCS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 0, 6) #define GET_RX_DESC_RXHT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 6, 1) #define GET_RX_DESC_SPLCP(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 8, 1) #define GET_RX_DESC_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 9, 1) #define GET_RX_DESC_HTC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 10, 1) #define GET_RX_DESC_HWPC_ERR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 14, 1) #define GET_RX_DESC_HWPC_IND(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 15, 1) #define GET_RX_DESC_IV0(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 16, 16) #define GET_RX_DESC_IV1(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+16, 0, 32) #define GET_RX_DESC_TSFL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 0, 32) #define GET_RX_DESC_BUFF_ADDR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 0, 32) #define GET_RX_DESC_BUFF_ADDR64(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+28, 0, 32) #define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 0, 32, __val) #define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 0, 32, __val) #define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ memset(__pdesc, 0, min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET)) struct rx_fwinfo_92c { u8 gain_trsw[4]; u8 pwdb_all; u8 cfosho[4]; u8 cfotail[4]; char rxevm[2]; char rxsnr[4]; u8 pdsnr[2]; u8 csi_current[2]; u8 csi_target[2]; u8 sigevm; u8 max_ex_pwr; u8 ex_intf_flag:1; u8 sgi_en:1; u8 rxsc:2; u8 reserve:4; } __packed; struct tx_desc_92c { u32 pktsize:16; u32 offset:8; u32 bmc:1; u32 htc:1; u32 lastseg:1; u32 firstseg:1; u32 linip:1; u32 noacm:1; u32 gf:1; u32 own:1; u32 macid:5; u32 agg_en:1; u32 bk:1; u32 rdg_en:1; u32 queuesel:5; u32 rd_nav_ext:1; u32 lsig_txop_en:1; u32 pifs:1; u32 rateid:4; u32 nav_usehdr:1; u32 en_descid:1; u32 sectype:2; u32 pktoffset:8; u32 rts_rc:6; u32 data_rc:6; u32 rsvd0:2; u32 bar_retryht:2; u32 rsvd1:1; u32 morefrag:1; u32 raw:1; u32 ccx:1; u32 ampdudensity:3; u32 rsvd2:1; u32 ant_sela:1; u32 ant_selb:1; u32 txant_cck:2; u32 txant_l:2; u32 txant_ht:2; u32 nextheadpage:8; u32 tailpage:8; u32 seq:12; u32 pktid:4; u32 rtsrate:5; u32 apdcfe:1; u32 qos:1; u32 hwseq_enable:1; u32 userrate:1; u32 dis_rtsfb:1; u32 dis_datafb:1; u32 cts2self:1; u32 rts_en:1; u32 hwrts_en:1; u32 portid:1; u32 rsvd3:3; u32 waitdcts:1; u32 cts2ap_en:1; u32 txsc:2; u32 stbc:2; u32 txshort:1; u32 txbw:1; u32 rtsshort:1; u32 rtsbw:1; u32 rtssc:2; u32 rtsstbc:2; u32 txrate:6; u32 shortgi:1; u32 ccxt:1; u32 txrate_fb_lmt:5; u32 rtsrate_fb_lmt:4; u32 retrylmt_en:1; u32 txretrylmt:6; u32 usb_txaggnum:8; u32 txagca:5; u32 txagcb:5; u32 usemaxlen:1; u32 maxaggnum:5; u32 mcsg1maxlen:4; u32 mcsg2maxlen:4; u32 mcsg3maxlen:4; u32 mcs7sgimaxlen:4; u32 txbuffersize:16; u32 mcsg4maxlen:4; u32 mcsg5maxlen:4; u32 mcsg6maxlen:4; u32 mcsg15sgimaxlen:4; u32 txbuffaddr; u32 txbufferaddr64; u32 nextdescaddress; u32 nextdescaddress64; u32 reserve_pass_pcie_mm_limit[4]; } __packed; struct rx_desc_92c { u32 length:14; u32 crc32:1; u32 icverror:1; u32 drv_infosize:4; u32 security:3; u32 qos:1; u32 shift:2; u32 phystatus:1; u32 swdec:1; u32 lastseg:1; u32 firstseg:1; u32 eor:1; u32 own:1; u32 macid:5; u32 tid:4; u32 hwrsvd:5; u32 paggr:1; u32 faggr:1; u32 a1_fit:4; u32 a2_fit:4; u32 pam:1; u32 pwr:1; u32 moredata:1; u32 morefrag:1; u32 type:2; u32 mc:1; u32 bc:1; u32 seq:12; u32 frag:4; u32 nextpktlen:14; u32 nextind:1; u32 rsvd:1; u32 rxmcs:6; u32 rxht:1; u32 amsdu:1; u32 splcp:1; u32 bandwidth:1; u32 htc:1; u32 tcpchk_rpt:1; u32 ipcchk_rpt:1; u32 tcpchk_valid:1; u32 hwpcerr:1; u32 hwpcind:1; u32 iv0:16; u32 iv1; u32 tsfl; u32 bufferaddress; u32 bufferaddress64; } __packed; void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); u32 rtl92ce_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool b_firstseg, bool b_lastseg, struct sk_buff *skb); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c0000644000175000017500000005737012026211315024573 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" #include "trx.h" #include "led.h" static u8 _rtl92ce_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) { __le16 fc = rtl_get_fc(skb); if (unlikely(ieee80211_is_beacon(fc))) return QSLT_BEACON; if (ieee80211_is_mgmt(fc)) return QSLT_MGNT; return skb->priority; } static u8 _rtl92c_query_rxpwrpercentage(char antpower) { if ((antpower <= -100) || (antpower >= 20)) return 0; else if (antpower >= 0) return 100; else return 100 + antpower; } static u8 _rtl92c_evm_db_to_percentage(char value) { char ret_val; ret_val = value; if (ret_val >= 0) ret_val = 0; if (ret_val <= -33) ret_val = -33; ret_val = 0 - ret_val; ret_val *= 3; if (ret_val == 99) ret_val = 100; return ret_val; } static long _rtl92ce_translate_todbm(struct ieee80211_hw *hw, u8 signal_strength_index) { long signal_power; signal_power = (long)((signal_strength_index + 1) >> 1); signal_power -= 95; return signal_power; } static long _rtl92ce_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { long retsig; if (currsig >= 61 && currsig <= 100) retsig = 90 + ((currsig - 60) / 4); else if (currsig >= 41 && currsig <= 60) retsig = 78 + ((currsig - 40) / 2); else if (currsig >= 31 && currsig <= 40) retsig = 66 + (currsig - 30); else if (currsig >= 21 && currsig <= 30) retsig = 54 + (currsig - 20); else if (currsig >= 5 && currsig <= 20) retsig = 42 + (((currsig - 5) * 2) / 3); else if (currsig == 4) retsig = 36; else if (currsig == 3) retsig = 27; else if (currsig == 2) retsig = 18; else if (currsig == 1) retsig = 9; else retsig = currsig; return retsig; } static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, struct rx_desc_92c *pdesc, struct rx_fwinfo_92c *p_drvinfo, bool packet_match_bssid, bool packet_toself, bool packet_beacon) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct phy_sts_cck_8192s_t *cck_buf; s8 rx_pwr_all = 0, rx_pwr[4]; u8 evm, pwdb_all, rf_rx_num = 0; u8 i, max_spatial_stream; u32 rssi, total_rssi = 0; bool in_powersavemode = false; bool is_cck_rate; is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc); pstats->packet_matchbssid = packet_match_bssid; pstats->packet_toself = packet_toself; pstats->is_cck = is_cck_rate; pstats->packet_beacon = packet_beacon; pstats->is_cck = is_cck_rate; pstats->rx_mimo_signalquality[0] = -1; pstats->rx_mimo_signalquality[1] = -1; if (is_cck_rate) { u8 report, cck_highpwr; cck_buf = (struct phy_sts_cck_8192s_t *)p_drvinfo; if (!in_powersavemode) cck_highpwr = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BIT(9)); else cck_highpwr = false; if (!cck_highpwr) { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = cck_buf->cck_agc_rpt & 0xc0; report = report >> 6; switch (report) { case 0x3: rx_pwr_all = -46 - (cck_agc_rpt & 0x3e); break; case 0x2: rx_pwr_all = -26 - (cck_agc_rpt & 0x3e); break; case 0x1: rx_pwr_all = -12 - (cck_agc_rpt & 0x3e); break; case 0x0: rx_pwr_all = 16 - (cck_agc_rpt & 0x3e); break; } } else { u8 cck_agc_rpt = cck_buf->cck_agc_rpt; report = p_drvinfo->cfosho[0] & 0x60; report = report >> 5; switch (report) { case 0x3: rx_pwr_all = -46 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x2: rx_pwr_all = -26 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x1: rx_pwr_all = -12 - ((cck_agc_rpt & 0x1f) << 1); break; case 0x0: rx_pwr_all = 16 - ((cck_agc_rpt & 0x1f) << 1); break; } } pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->recvsignalpower = rx_pwr_all; if (packet_match_bssid) { u8 sq; if (pstats->rx_pwdb_all > 40) sq = 100; else { sq = cck_buf->sq_rpt; if (sq > 64) sq = 0; else if (sq < 20) sq = 100; else sq = ((64 - sq) * 100) / 44; } pstats->signalquality = sq; pstats->rx_mimo_signalquality[0] = sq; pstats->rx_mimo_signalquality[1] = -1; } } else { rtlpriv->dm.rfpath_rxenable[0] = rtlpriv->dm.rfpath_rxenable[1] = true; for (i = RF90_PATH_A; i < RF90_PATH_MAX; i++) { if (rtlpriv->dm.rfpath_rxenable[i]) rf_rx_num++; rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110; rssi = _rtl92c_query_rxpwrpercentage(rx_pwr[i]); total_rssi += rssi; rtlpriv->stats.rx_snr_db[i] = (long)(p_drvinfo->rxsnr[i] / 2); if (packet_match_bssid) pstats->rx_mimo_signalstrength[i] = (u8) rssi; } rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110; pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 && pdesc->rxmcs <= DESC92_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; for (i = 0; i < max_spatial_stream; i++) { evm = _rtl92c_evm_db_to_percentage(p_drvinfo->rxevm[i]); if (packet_match_bssid) { if (i == 0) pstats->signalquality = (u8) (evm & 0xff); pstats->rx_mimo_signalquality[i] = (u8) (evm & 0xff); } } } if (is_cck_rate) pstats->signalstrength = (u8) (_rtl92ce_signal_scale_mapping(hw, pwdb_all)); else if (rf_rx_num != 0) pstats->signalstrength = (u8) (_rtl92ce_signal_scale_mapping (hw, total_rssi /= rf_rx_num)); } static void _rtl92ce_process_ui_rssi(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u8 rfpath; u32 last_rssi, tmpval; if (pstats->packet_toself || pstats->packet_beacon) { rtlpriv->stats.rssi_calculate_cnt++; if (rtlpriv->stats.ui_rssi.total_num++ >= PHY_RSSI_SLID_WIN_MAX) { rtlpriv->stats.ui_rssi.total_num = PHY_RSSI_SLID_WIN_MAX; last_rssi = rtlpriv->stats.ui_rssi.elements[rtlpriv-> stats.ui_rssi.index]; rtlpriv->stats.ui_rssi.total_val -= last_rssi; } rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength; rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi. index++] = pstats->signalstrength; if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX) rtlpriv->stats.ui_rssi.index = 0; tmpval = rtlpriv->stats.ui_rssi.total_val / rtlpriv->stats.ui_rssi.total_num; rtlpriv->stats.signal_strength = _rtl92ce_translate_todbm(hw, (u8) tmpval); pstats->rssi = rtlpriv->stats.signal_strength; } if (!pstats->is_cck && pstats->packet_toself) { for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) { rtlpriv->stats.rx_rssi_percentage[rfpath] = pstats->rx_mimo_signalstrength[rfpath]; } if (pstats->rx_mimo_signalstrength[rfpath] > rtlpriv->stats.rx_rssi_percentage[rfpath]) { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats. rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); rtlpriv->stats.rx_rssi_percentage[rfpath] = rtlpriv->stats.rx_rssi_percentage[rfpath] + 1; } else { rtlpriv->stats.rx_rssi_percentage[rfpath] = ((rtlpriv->stats. rx_rssi_percentage[rfpath] * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_mimo_signalstrength[rfpath])) / (RX_SMOOTH_FACTOR); } } } } static void _rtl92ce_update_rxsignalstatistics(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); int weighting = 0; if (rtlpriv->stats.recv_signal_power == 0) rtlpriv->stats.recv_signal_power = pstats->recvsignalpower; if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power) weighting = 5; else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power) weighting = (-5); rtlpriv->stats.recv_signal_power = (rtlpriv->stats.recv_signal_power * 5 + pstats->recvsignalpower + weighting) / 6; } static void _rtl92ce_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb; if (mac->opmode == NL80211_IFTYPE_ADHOC) { return; } else { undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; } if (pstats->packet_toself || pstats->packet_beacon) { if (undecorated_smoothed_pwdb < 0) undecorated_smoothed_pwdb = pstats->rx_pwdb_all; if (pstats->rx_pwdb_all > (u32) undecorated_smoothed_pwdb) { undecorated_smoothed_pwdb = (((undecorated_smoothed_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); undecorated_smoothed_pwdb = undecorated_smoothed_pwdb + 1; } else { undecorated_smoothed_pwdb = (((undecorated_smoothed_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); } rtlpriv->dm.undecorated_smoothed_pwdb = undecorated_smoothed_pwdb; _rtl92ce_update_rxsignalstatistics(hw, pstats); } } static void _rtl92ce_process_ui_link_quality(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 last_evm, n_spatialstream, tmpval; if (pstats->signalquality != 0) { if (pstats->packet_toself || pstats->packet_beacon) { if (rtlpriv->stats.ui_link_quality.total_num++ >= PHY_LINKQUALITY_SLID_WIN_MAX) { rtlpriv->stats.ui_link_quality.total_num = PHY_LINKQUALITY_SLID_WIN_MAX; last_evm = rtlpriv->stats. ui_link_quality.elements[rtlpriv-> stats.ui_link_quality. index]; rtlpriv->stats.ui_link_quality.total_val -= last_evm; } rtlpriv->stats.ui_link_quality.total_val += pstats->signalquality; rtlpriv->stats.ui_link_quality.elements[rtlpriv->stats. ui_link_quality. index++] = pstats->signalquality; if (rtlpriv->stats.ui_link_quality.index >= PHY_LINKQUALITY_SLID_WIN_MAX) rtlpriv->stats.ui_link_quality.index = 0; tmpval = rtlpriv->stats.ui_link_quality.total_val / rtlpriv->stats.ui_link_quality.total_num; rtlpriv->stats.signal_quality = tmpval; rtlpriv->stats.last_sigstrength_inpercent = tmpval; for (n_spatialstream = 0; n_spatialstream < 2; n_spatialstream++) { if (pstats-> rx_mimo_signalquality[n_spatialstream] != -1) { if (rtlpriv->stats. rx_evm_percentage[n_spatialstream] == 0) { rtlpriv->stats. rx_evm_percentage [n_spatialstream] = pstats->rx_mimo_signalquality [n_spatialstream]; } rtlpriv->stats. rx_evm_percentage[n_spatialstream] = ((rtlpriv-> stats.rx_evm_percentage [n_spatialstream] * (RX_SMOOTH_FACTOR - 1)) + (pstats-> rx_mimo_signalquality [n_spatialstream] * 1)) / (RX_SMOOTH_FACTOR); } } } } else { ; } } static void _rtl92ce_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer, struct rtl_stats *pcurrent_stats) { if (!pcurrent_stats->packet_matchbssid && !pcurrent_stats->packet_beacon) return; _rtl92ce_process_ui_rssi(hw, pcurrent_stats); _rtl92ce_process_pwdb(hw, pcurrent_stats); _rtl92ce_process_ui_link_quality(hw, pcurrent_stats); } static void _rtl92ce_translate_rx_signal_stuff(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_stats *pstats, struct rx_desc_92c *pdesc, struct rx_fwinfo_92c *p_drvinfo) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct ieee80211_hdr *hdr; u8 *tmp_buf; u8 *praddr; __le16 fc; u16 type, c_fc; bool packet_matchbssid, packet_toself, packet_beacon; tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift; hdr = (struct ieee80211_hdr *)tmp_buf; fc = hdr->frame_control; c_fc = le16_to_cpu(fc); type = WLAN_FC_GET_TYPE(fc); praddr = hdr->addr1; packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) && ether_addr_equal(mac->bssid, (c_fc & IEEE80211_FCTL_TODS) ? hdr->addr1 : (c_fc & IEEE80211_FCTL_FROMDS) ? hdr->addr2 : hdr->addr3) && (!pstats->hwerror) && (!pstats->crc) && (!pstats->icv)); packet_toself = packet_matchbssid && ether_addr_equal(praddr, rtlefuse->dev_addr); if (ieee80211_is_beacon(fc)) packet_beacon = true; _rtl92ce_query_rxphystatus(hw, pstats, pdesc, p_drvinfo, packet_matchbssid, packet_toself, packet_beacon); _rtl92ce_process_phyinfo(hw, tmp_buf, pstats); } bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *p_desc, struct sk_buff *skb) { struct rx_fwinfo_92c *p_drvinfo; struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc; u32 phystatus = GET_RX_DESC_PHYST(pdesc); stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc); stats->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) * RX_DRV_INFO_SIZE_UNIT; stats->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03); stats->icv = (u16) GET_RX_DESC_ICV(pdesc); stats->crc = (u16) GET_RX_DESC_CRC32(pdesc); stats->hwerror = (stats->crc | stats->icv); stats->decrypted = !GET_RX_DESC_SWDEC(pdesc); stats->rate = (u8) GET_RX_DESC_RXMCS(pdesc); stats->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc); stats->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1); stats->isampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1) && (GET_RX_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); rx_status->freq = hw->conf.channel->center_freq; rx_status->band = hw->conf.channel->band; if (GET_RX_DESC_CRC32(pdesc)) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (!GET_RX_DESC_SWDEC(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(pdesc)) rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RXHT(pdesc)) rx_status->flag |= RX_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, (bool)GET_RX_DESC_RXHT(pdesc), (u8)GET_RX_DESC_RXMCS(pdesc), (bool)GET_RX_DESC_PAGGR(pdesc)); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92c *)(skb->data + stats->rx_bufshift); _rtl92ce_translate_rx_signal_stuff(hw, skb, stats, pdesc, p_drvinfo); } /*rx_status->qual = stats->signal; */ rx_status->signal = stats->rssi + 10; /*rx_status->noise = -stats->noise; */ return true; } void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *tcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; u8 fw_qsel = _rtl92ce_map_hwqueue_to_fwqueue(skb, hw_queue); bool firstseg = ((hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0); bool lastseg = ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) == 0); dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); u8 bw_40 = 0; rcu_read_lock(); sta = get_sta(hw, mac->vif, mac->bssid); if (mac->opmode == NL80211_IFTYPE_STATION) { bw_40 = mac->bw_40; } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; } seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; rtl_get_tcb_desc(hw, info, sta, skb, tcb_desc); CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_92c)); if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) { firstseg = true; lastseg = true; } if (firstseg) { SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); SET_TX_DESC_TX_RATE(pdesc, tcb_desc->hw_rate); if (tcb_desc->use_shortgi || tcb_desc->use_shortpreamble) SET_TX_DESC_DATA_SHORTGI(pdesc, 1); if (info->flags & IEEE80211_TX_CTL_AMPDU) { SET_TX_DESC_AGG_BREAK(pdesc, 1); SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14); } SET_TX_DESC_SEQ(pdesc, seq_number); SET_TX_DESC_RTS_ENABLE(pdesc, ((tcb_desc->rts_enable && !tcb_desc-> cts_enable) ? 1 : 0)); SET_TX_DESC_HW_RTS_ENABLE(pdesc, ((tcb_desc->rts_enable || tcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_CTS2SELF(pdesc, ((tcb_desc->cts_enable) ? 1 : 0)); SET_TX_DESC_RTS_STBC(pdesc, ((tcb_desc->rts_stbc) ? 1 : 0)); SET_TX_DESC_RTS_RATE(pdesc, tcb_desc->rts_rate); SET_TX_DESC_RTS_BW(pdesc, 0); SET_TX_DESC_RTS_SC(pdesc, tcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, ((tcb_desc->rts_rate <= DESC92_RATE54M) ? (tcb_desc->rts_use_shortpreamble ? 1 : 0) : (tcb_desc->rts_use_shortgi ? 1 : 0))); if (bw_40) { if (tcb_desc->packet_bw) { SET_TX_DESC_DATA_BW(pdesc, 1); SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3); } else { SET_TX_DESC_DATA_BW(pdesc, 0); SET_TX_DESC_TX_SUB_CARRIER(pdesc, mac->cur_40_prime_sc); } } else { SET_TX_DESC_DATA_BW(pdesc, 0); SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0); } SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb->len); if (sta) { u8 ampdu_density = sta->ht_cap.ampdu_density; SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density); } if (info->control.hw_key) { struct ieee80211_key_conf *keyconf = info->control.hw_key; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: SET_TX_DESC_SEC_TYPE(pdesc, 0x1); break; case WLAN_CIPHER_SUITE_CCMP: SET_TX_DESC_SEC_TYPE(pdesc, 0x3); break; default: SET_TX_DESC_SEC_TYPE(pdesc, 0x0); break; } } SET_TX_DESC_PKT_ID(pdesc, 0); SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel); SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F); SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF); SET_TX_DESC_DISABLE_FB(pdesc, 0); SET_TX_DESC_USE_RATE(pdesc, tcb_desc->use_driver_rate ? 1 : 0); if (ieee80211_is_data_qos(fc)) { if (mac->rdg_en) { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "Enable RDG function\n"); SET_TX_DESC_RDG_ENABLE(pdesc, 1); SET_TX_DESC_HTC(pdesc, 1); } } } rcu_read_unlock(); SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0)); SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0)); SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) skb->len); SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); if (rtlpriv->dm.useramask) { SET_TX_DESC_RATE_ID(pdesc, tcb_desc->ratr_index); SET_TX_DESC_MACID(pdesc, tcb_desc->mac_id); } else { SET_TX_DESC_RATE_ID(pdesc, 0xC + tcb_desc->ratr_index); SET_TX_DESC_MACID(pdesc, tcb_desc->ratr_index); } if ((!ieee80211_is_data_qos(fc)) && ppsc->fwctrl_lps) { SET_TX_DESC_HWSEQ_EN(pdesc, 1); SET_TX_DESC_PKT_ID(pdesc, 8); if (!defaultadapter) SET_TX_DESC_QOS(pdesc, 1); } SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1)); if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) || is_broadcast_ether_addr(ieee80211_get_DA(hdr))) { SET_TX_DESC_BMC(pdesc, 1); } RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n"); } void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); u8 fw_queue = QSLT_BEACON; dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); if (firstseg) SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) (skb->len)); SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); SET_TX_DESC_RATE_ID(pdesc, 7); SET_TX_DESC_MACID(pdesc, 0); SET_TX_DESC_OWN(pdesc, 1); SET_TX_DESC_PKT_SIZE(pdesc, (u16) (skb->len)); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_OFFSET(pdesc, 0x20); SET_TX_DESC_USE_RATE(pdesc, 1); if (!ieee80211_is_data_qos(fc)) { SET_TX_DESC_HWSEQ_EN(pdesc, 1); SET_TX_DESC_PKT_ID(pdesc, 8); } RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, "H2C Tx Cmd Content", pdesc, TX_DESC_SIZE); } void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) { if (istx) { switch (desc_name) { case HW_DESC_OWN: wmb(); SET_TX_DESC_OWN(pdesc, 1); break; case HW_DESC_TX_NEXTDESC_ADDR: SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *) val); break; default: RT_ASSERT(false, "ERR txdesc :%d not process\n", desc_name); break; } } else { switch (desc_name) { case HW_DESC_RXOWN: wmb(); SET_RX_DESC_OWN(pdesc, 1); break; case HW_DESC_RXBUFF_ADDR: SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *) val); break; case HW_DESC_RXPKT_LEN: SET_RX_DESC_PKT_LEN(pdesc, *(u32 *) val); break; case HW_DESC_RXERO: SET_RX_DESC_EOR(pdesc, 1); break; default: RT_ASSERT(false, "ERR rxdesc :%d not process\n", desc_name); break; } } } u32 rtl92ce_get_desc(u8 *p_desc, bool istx, u8 desc_name) { u32 ret = 0; if (istx) { switch (desc_name) { case HW_DESC_OWN: ret = GET_TX_DESC_OWN(p_desc); break; case HW_DESC_TXBUFF_ADDR: ret = GET_TX_DESC_TX_BUFFER_ADDRESS(p_desc); break; default: RT_ASSERT(false, "ERR txdesc :%d not process\n", desc_name); break; } } else { struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc; switch (desc_name) { case HW_DESC_OWN: ret = GET_RX_DESC_OWN(pdesc); break; case HW_DESC_RXPKT_LEN: ret = GET_RX_DESC_PKT_LEN(pdesc); break; default: RT_ASSERT(false, "ERR rxdesc :%d not process\n", desc_name); break; } } return ret; } void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (hw_queue == BEACON_QUEUE) { rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(4)); } else { rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(0) << (hw_queue)); } } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/table.h0000644000175000017500000000426112026211315025041 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Created on 2010/ 5/18, 1:41 * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CE_TABLE__H_ #define __RTL92CE_TABLE__H_ #include #define PHY_REG_2TARRAY_LENGTH 374 extern u32 RTL8192CEPHY_REG_2TARRAY[PHY_REG_2TARRAY_LENGTH]; #define PHY_REG_1TARRAY_LENGTH 374 extern u32 RTL8192CEPHY_REG_1TARRAY[PHY_REG_1TARRAY_LENGTH]; #define PHY_REG_ARRAY_PGLENGTH 192 extern u32 RTL8192CEPHY_REG_ARRAY_PG[PHY_REG_ARRAY_PGLENGTH]; #define RADIOA_2TARRAYLENGTH 282 extern u32 RTL8192CERADIOA_2TARRAY[RADIOA_2TARRAYLENGTH]; #define RADIOB_2TARRAYLENGTH 78 extern u32 RTL8192CE_RADIOB_2TARRAY[RADIOB_2TARRAYLENGTH]; #define RADIOA_1TARRAYLENGTH 282 extern u32 RTL8192CE_RADIOA_1TARRAY[RADIOA_1TARRAYLENGTH]; #define RADIOB_1TARRAYLENGTH 1 extern u32 RTL8192CE_RADIOB_1TARRAY[RADIOB_1TARRAYLENGTH]; #define MAC_2T_ARRAYLENGTH 162 extern u32 RTL8192CEMAC_2T_ARRAY[MAC_2T_ARRAYLENGTH]; #define AGCTAB_2TARRAYLENGTH 320 extern u32 RTL8192CEAGCTAB_2TARRAY[AGCTAB_2TARRAYLENGTH]; #define AGCTAB_1TARRAYLENGTH 320 extern u32 RTL8192CEAGCTAB_1TARRAY[AGCTAB_1TARRAYLENGTH]; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/table.c0000644000175000017500000006230512026211315025037 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Created on 2010/ 5/18, 1:41 * * Larry Finger * *****************************************************************************/ #include "table.h" u32 RTL8192CEPHY_REG_2TARRAY[PHY_REG_2TARRAY_LENGTH] = { 0x024, 0x0011800f, 0x028, 0x00ffdb83, 0x800, 0x80040002, 0x804, 0x00000003, 0x808, 0x0000fc00, 0x80c, 0x0000000a, 0x810, 0x10005388, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, 0x820, 0x01000100, 0x824, 0x00390004, 0x828, 0x01000100, 0x82c, 0x00390004, 0x830, 0x27272727, 0x834, 0x27272727, 0x838, 0x27272727, 0x83c, 0x27272727, 0x840, 0x00010000, 0x844, 0x00010000, 0x848, 0x27272727, 0x84c, 0x27272727, 0x850, 0x00000000, 0x854, 0x00000000, 0x858, 0x569a569a, 0x85c, 0x0c1b25a4, 0x860, 0x66e60230, 0x864, 0x061f0130, 0x868, 0x27272727, 0x86c, 0x2b2b2b27, 0x870, 0x07000700, 0x874, 0x22184000, 0x878, 0x08080808, 0x87c, 0x00000000, 0x880, 0xc0083070, 0x884, 0x000004d5, 0x888, 0x00000000, 0x88c, 0xcc0000c0, 0x890, 0x00000800, 0x894, 0xfffffffe, 0x898, 0x40302010, 0x89c, 0x00706050, 0x900, 0x00000000, 0x904, 0x00000023, 0x908, 0x00000000, 0x90c, 0x81121313, 0xa00, 0x00d047c8, 0xa04, 0x80ff000c, 0xa08, 0x8c838300, 0xa0c, 0x2e68120f, 0xa10, 0x9500bb78, 0xa14, 0x11144028, 0xa18, 0x00881117, 0xa1c, 0x89140f00, 0xa20, 0x1a1b0000, 0xa24, 0x090e1317, 0xa28, 0x00000204, 0xa2c, 0x00d30000, 0xa70, 0x101fbf00, 0xa74, 0x00000007, 0xc00, 0x48071d40, 0xc04, 0x03a05633, 0xc08, 0x000000e4, 0xc0c, 0x6c6c6c6c, 0xc10, 0x08800000, 0xc14, 0x40000100, 0xc18, 0x08800000, 0xc1c, 0x40000100, 0xc20, 0x00000000, 0xc24, 0x00000000, 0xc28, 0x00000000, 0xc2c, 0x00000000, 0xc30, 0x69e9ac44, 0xc34, 0x469652cf, 0xc38, 0x49795994, 0xc3c, 0x0a97971c, 0xc40, 0x1f7c403f, 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, 0xc50, 0x69543420, 0xc54, 0x43bc0094, 0xc58, 0x69543420, 0xc5c, 0x433c0094, 0xc60, 0x00000000, 0xc64, 0x5116848b, 0xc68, 0x47c00bff, 0xc6c, 0x00000036, 0xc70, 0x2c7f000d, 0xc74, 0x018610db, 0xc78, 0x0000001f, 0xc7c, 0x00b91612, 0xc80, 0x40000100, 0xc84, 0x20f60000, 0xc88, 0x40000100, 0xc8c, 0x20200000, 0xc90, 0x00121820, 0xc94, 0x00000000, 0xc98, 0x00121820, 0xc9c, 0x00007f7f, 0xca0, 0x00000000, 0xca4, 0x00000080, 0xca8, 0x00000000, 0xcac, 0x00000000, 0xcb0, 0x00000000, 0xcb4, 0x00000000, 0xcb8, 0x00000000, 0xcbc, 0x28000000, 0xcc0, 0x00000000, 0xcc4, 0x00000000, 0xcc8, 0x00000000, 0xccc, 0x00000000, 0xcd0, 0x00000000, 0xcd4, 0x00000000, 0xcd8, 0x64b22427, 0xcdc, 0x00766932, 0xce0, 0x00222222, 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, 0xd00, 0x00080740, 0xd04, 0x00020403, 0xd08, 0x0000907f, 0xd0c, 0x20010201, 0xd10, 0xa0633333, 0xd14, 0x3333bc43, 0xd18, 0x7a8f5b6b, 0xd2c, 0xcc979975, 0xd30, 0x00000000, 0xd34, 0x80608000, 0xd38, 0x00000000, 0xd3c, 0x00027293, 0xd40, 0x00000000, 0xd44, 0x00000000, 0xd48, 0x00000000, 0xd4c, 0x00000000, 0xd50, 0x6437140a, 0xd54, 0x00000000, 0xd58, 0x00000000, 0xd5c, 0x30032064, 0xd60, 0x4653de68, 0xd64, 0x04518a3c, 0xd68, 0x00002101, 0xd6c, 0x2a201c16, 0xd70, 0x1812362e, 0xd74, 0x322c2220, 0xd78, 0x000e3c24, 0xe00, 0x2a2a2a2a, 0xe04, 0x2a2a2a2a, 0xe08, 0x03902a2a, 0xe10, 0x2a2a2a2a, 0xe14, 0x2a2a2a2a, 0xe18, 0x2a2a2a2a, 0xe1c, 0x2a2a2a2a, 0xe28, 0x00000000, 0xe30, 0x1000dc1f, 0xe34, 0x10008c1f, 0xe38, 0x02140102, 0xe3c, 0x681604c2, 0xe40, 0x01007c00, 0xe44, 0x01004800, 0xe48, 0xfb000000, 0xe4c, 0x000028d1, 0xe50, 0x1000dc1f, 0xe54, 0x10008c1f, 0xe58, 0x02140102, 0xe5c, 0x28160d05, 0xe60, 0x00000010, 0xe68, 0x001b25a4, 0xe6c, 0x63db25a4, 0xe70, 0x63db25a4, 0xe74, 0x0c1b25a4, 0xe78, 0x0c1b25a4, 0xe7c, 0x0c1b25a4, 0xe80, 0x0c1b25a4, 0xe84, 0x63db25a4, 0xe88, 0x0c1b25a4, 0xe8c, 0x63db25a4, 0xed0, 0x63db25a4, 0xed4, 0x63db25a4, 0xed8, 0x63db25a4, 0xedc, 0x001b25a4, 0xee0, 0x001b25a4, 0xeec, 0x6fdb25a4, 0xf14, 0x00000003, 0xf4c, 0x00000000, 0xf00, 0x00000300, }; u32 RTL8192CEPHY_REG_1TARRAY[PHY_REG_1TARRAY_LENGTH] = { 0x024, 0x0011800f, 0x028, 0x00ffdb83, 0x800, 0x80040000, 0x804, 0x00000001, 0x808, 0x0000fc00, 0x80c, 0x0000000a, 0x810, 0x10005388, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, 0x820, 0x01000100, 0x824, 0x00390004, 0x828, 0x00000000, 0x82c, 0x00000000, 0x830, 0x00000000, 0x834, 0x00000000, 0x838, 0x00000000, 0x83c, 0x00000000, 0x840, 0x00010000, 0x844, 0x00000000, 0x848, 0x00000000, 0x84c, 0x00000000, 0x850, 0x00000000, 0x854, 0x00000000, 0x858, 0x569a569a, 0x85c, 0x001b25a4, 0x860, 0x66e60230, 0x864, 0x061f0130, 0x868, 0x00000000, 0x86c, 0x32323200, 0x870, 0x07000700, 0x874, 0x22004000, 0x878, 0x00000808, 0x87c, 0x00000000, 0x880, 0xc0083070, 0x884, 0x000004d5, 0x888, 0x00000000, 0x88c, 0xccc000c0, 0x890, 0x00000800, 0x894, 0xfffffffe, 0x898, 0x40302010, 0x89c, 0x00706050, 0x900, 0x00000000, 0x904, 0x00000023, 0x908, 0x00000000, 0x90c, 0x81121111, 0xa00, 0x00d047c8, 0xa04, 0x80ff000c, 0xa08, 0x8c838300, 0xa0c, 0x2e68120f, 0xa10, 0x9500bb78, 0xa14, 0x11144028, 0xa18, 0x00881117, 0xa1c, 0x89140f00, 0xa20, 0x1a1b0000, 0xa24, 0x090e1317, 0xa28, 0x00000204, 0xa2c, 0x00d30000, 0xa70, 0x101fbf00, 0xa74, 0x00000007, 0xc00, 0x48071d40, 0xc04, 0x03a05611, 0xc08, 0x000000e4, 0xc0c, 0x6c6c6c6c, 0xc10, 0x08800000, 0xc14, 0x40000100, 0xc18, 0x08800000, 0xc1c, 0x40000100, 0xc20, 0x00000000, 0xc24, 0x00000000, 0xc28, 0x00000000, 0xc2c, 0x00000000, 0xc30, 0x69e9ac44, 0xc34, 0x469652cf, 0xc38, 0x49795994, 0xc3c, 0x0a97971c, 0xc40, 0x1f7c403f, 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, 0xc50, 0x69543420, 0xc54, 0x43bc0094, 0xc58, 0x69543420, 0xc5c, 0x433c0094, 0xc60, 0x00000000, 0xc64, 0x5116848b, 0xc68, 0x47c00bff, 0xc6c, 0x00000036, 0xc70, 0x2c7f000d, 0xc74, 0x018610db, 0xc78, 0x0000001f, 0xc7c, 0x00b91612, 0xc80, 0x40000100, 0xc84, 0x20f60000, 0xc88, 0x40000100, 0xc8c, 0x20200000, 0xc90, 0x00121820, 0xc94, 0x00000000, 0xc98, 0x00121820, 0xc9c, 0x00007f7f, 0xca0, 0x00000000, 0xca4, 0x00000080, 0xca8, 0x00000000, 0xcac, 0x00000000, 0xcb0, 0x00000000, 0xcb4, 0x00000000, 0xcb8, 0x00000000, 0xcbc, 0x28000000, 0xcc0, 0x00000000, 0xcc4, 0x00000000, 0xcc8, 0x00000000, 0xccc, 0x00000000, 0xcd0, 0x00000000, 0xcd4, 0x00000000, 0xcd8, 0x64b22427, 0xcdc, 0x00766932, 0xce0, 0x00222222, 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, 0xd00, 0x00080740, 0xd04, 0x00020401, 0xd08, 0x0000907f, 0xd0c, 0x20010201, 0xd10, 0xa0633333, 0xd14, 0x3333bc43, 0xd18, 0x7a8f5b6b, 0xd2c, 0xcc979975, 0xd30, 0x00000000, 0xd34, 0x80608000, 0xd38, 0x00000000, 0xd3c, 0x00027293, 0xd40, 0x00000000, 0xd44, 0x00000000, 0xd48, 0x00000000, 0xd4c, 0x00000000, 0xd50, 0x6437140a, 0xd54, 0x00000000, 0xd58, 0x00000000, 0xd5c, 0x30032064, 0xd60, 0x4653de68, 0xd64, 0x04518a3c, 0xd68, 0x00002101, 0xd6c, 0x2a201c16, 0xd70, 0x1812362e, 0xd74, 0x322c2220, 0xd78, 0x000e3c24, 0xe00, 0x2a2a2a2a, 0xe04, 0x2a2a2a2a, 0xe08, 0x03902a2a, 0xe10, 0x2a2a2a2a, 0xe14, 0x2a2a2a2a, 0xe18, 0x2a2a2a2a, 0xe1c, 0x2a2a2a2a, 0xe28, 0x00000000, 0xe30, 0x1000dc1f, 0xe34, 0x10008c1f, 0xe38, 0x02140102, 0xe3c, 0x681604c2, 0xe40, 0x01007c00, 0xe44, 0x01004800, 0xe48, 0xfb000000, 0xe4c, 0x000028d1, 0xe50, 0x1000dc1f, 0xe54, 0x10008c1f, 0xe58, 0x02140102, 0xe5c, 0x28160d05, 0xe60, 0x00000010, 0xe68, 0x001b25a4, 0xe6c, 0x631b25a0, 0xe70, 0x631b25a0, 0xe74, 0x081b25a0, 0xe78, 0x081b25a0, 0xe7c, 0x081b25a0, 0xe80, 0x081b25a0, 0xe84, 0x631b25a0, 0xe88, 0x081b25a0, 0xe8c, 0x631b25a0, 0xed0, 0x631b25a0, 0xed4, 0x631b25a0, 0xed8, 0x631b25a0, 0xedc, 0x001b25a0, 0xee0, 0x001b25a0, 0xeec, 0x6b1b25a0, 0xf14, 0x00000003, 0xf4c, 0x00000000, 0xf00, 0x00000300, }; u32 RTL8192CEPHY_REG_ARRAY_PG[PHY_REG_ARRAY_PGLENGTH] = { 0xe00, 0xffffffff, 0x0a0c0c0c, 0xe04, 0xffffffff, 0x02040608, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x0a0c0d0e, 0xe14, 0xffffffff, 0x02040608, 0xe18, 0xffffffff, 0x0a0c0d0e, 0xe1c, 0xffffffff, 0x02040608, 0x830, 0xffffffff, 0x0a0c0c0c, 0x834, 0xffffffff, 0x02040608, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x0a0c0d0e, 0x848, 0xffffffff, 0x02040608, 0x84c, 0xffffffff, 0x0a0c0d0e, 0x868, 0xffffffff, 0x02040608, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, 0xe00, 0xffffffff, 0x04040404, 0xe04, 0xffffffff, 0x00020204, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x06060606, 0xe14, 0xffffffff, 0x00020406, 0xe18, 0xffffffff, 0x06060606, 0xe1c, 0xffffffff, 0x00020406, 0x830, 0xffffffff, 0x04040404, 0x834, 0xffffffff, 0x00020204, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x06060606, 0x848, 0xffffffff, 0x00020406, 0x84c, 0xffffffff, 0x06060606, 0x868, 0xffffffff, 0x00020406, 0xe00, 0xffffffff, 0x00000000, 0xe04, 0xffffffff, 0x00000000, 0xe08, 0x0000ff00, 0x00000000, 0x86c, 0xffffff00, 0x00000000, 0xe10, 0xffffffff, 0x00000000, 0xe14, 0xffffffff, 0x00000000, 0xe18, 0xffffffff, 0x00000000, 0xe1c, 0xffffffff, 0x00000000, 0x830, 0xffffffff, 0x00000000, 0x834, 0xffffffff, 0x00000000, 0x838, 0xffffff00, 0x00000000, 0x86c, 0x000000ff, 0x00000000, 0x83c, 0xffffffff, 0x00000000, 0x848, 0xffffffff, 0x00000000, 0x84c, 0xffffffff, 0x00000000, 0x868, 0xffffffff, 0x00000000, }; u32 RTL8192CERADIOA_2TARRAY[RADIOA_2TARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00031284, 0x002, 0x00098000, 0x003, 0x00018c63, 0x004, 0x000210e7, 0x009, 0x0002044f, 0x00a, 0x0001adb0, 0x00b, 0x00054867, 0x00c, 0x0008992e, 0x00d, 0x0000e52c, 0x00e, 0x00039ce7, 0x00f, 0x00000451, 0x019, 0x00000000, 0x01a, 0x00010255, 0x01b, 0x00060a00, 0x01c, 0x000fc378, 0x01d, 0x000a1250, 0x01e, 0x0004445f, 0x01f, 0x00080001, 0x020, 0x0000b614, 0x021, 0x0006c000, 0x022, 0x00000000, 0x023, 0x00001558, 0x024, 0x00000060, 0x025, 0x00000483, 0x026, 0x0004f000, 0x027, 0x000ec7d9, 0x028, 0x000977c0, 0x029, 0x00004783, 0x02a, 0x00000001, 0x02b, 0x00021334, 0x02a, 0x00000000, 0x02b, 0x00000054, 0x02a, 0x00000001, 0x02b, 0x00000808, 0x02b, 0x00053333, 0x02c, 0x0000000c, 0x02a, 0x00000002, 0x02b, 0x00000808, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000003, 0x02b, 0x00000808, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000004, 0x02b, 0x00000808, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x00000005, 0x02b, 0x00000808, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x00000006, 0x02b, 0x00000709, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000007, 0x02b, 0x00000709, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000008, 0x02b, 0x0000060a, 0x02b, 0x0004b333, 0x02c, 0x0000000d, 0x02a, 0x00000009, 0x02b, 0x0000060a, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x0000000a, 0x02b, 0x0000060a, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x0000000b, 0x02b, 0x0000060a, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x0000000c, 0x02b, 0x0000060a, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x0000000d, 0x02b, 0x0000060a, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x0000000e, 0x02b, 0x0000050b, 0x02b, 0x00066666, 0x02c, 0x0000001a, 0x02a, 0x000e0000, 0x010, 0x0004000f, 0x011, 0x000e31fc, 0x010, 0x0006000f, 0x011, 0x000ff9f8, 0x010, 0x0002000f, 0x011, 0x000203f9, 0x010, 0x0003000f, 0x011, 0x000ff500, 0x010, 0x00000000, 0x011, 0x00000000, 0x010, 0x0008000f, 0x011, 0x0003f100, 0x010, 0x0009000f, 0x011, 0x00023100, 0x012, 0x00032000, 0x012, 0x00071000, 0x012, 0x000b0000, 0x012, 0x000fc000, 0x013, 0x000287af, 0x013, 0x000244b7, 0x013, 0x000204ab, 0x013, 0x0001c49f, 0x013, 0x00018493, 0x013, 0x00014297, 0x013, 0x00010295, 0x013, 0x0000c298, 0x013, 0x0000819c, 0x013, 0x000040a8, 0x013, 0x0000001c, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, 0x014, 0x000d9444, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x015, 0x000cf424, 0x016, 0x000e0330, 0x016, 0x000a0330, 0x016, 0x00060330, 0x016, 0x00020330, 0x000, 0x00010159, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00044457, 0x01f, 0x00080000, 0x000, 0x00030159, }; u32 RTL8192CE_RADIOB_2TARRAY[RADIOB_2TARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00031284, 0x002, 0x00098000, 0x003, 0x00018c63, 0x004, 0x000210e7, 0x009, 0x0002044f, 0x00a, 0x0001adb0, 0x00b, 0x00054867, 0x00c, 0x0008992e, 0x00d, 0x0000e52c, 0x00e, 0x00039ce7, 0x00f, 0x00000451, 0x012, 0x00032000, 0x012, 0x00071000, 0x012, 0x000b0000, 0x012, 0x000fc000, 0x013, 0x000287af, 0x013, 0x000244b7, 0x013, 0x000204ab, 0x013, 0x0001c49f, 0x013, 0x00018493, 0x013, 0x00014297, 0x013, 0x00010295, 0x013, 0x0000c298, 0x013, 0x0000819c, 0x013, 0x000040a8, 0x013, 0x0000001c, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, 0x014, 0x000d9444, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x015, 0x000cf424, 0x016, 0x000e0330, 0x016, 0x000a0330, 0x016, 0x00060330, 0x016, 0x00020330, }; u32 RTL8192CE_RADIOA_1TARRAY[RADIOA_1TARRAYLENGTH] = { 0x000, 0x00030159, 0x001, 0x00031284, 0x002, 0x00098000, 0x003, 0x00018c63, 0x004, 0x000210e7, 0x009, 0x0002044f, 0x00a, 0x0001adb0, 0x00b, 0x00054867, 0x00c, 0x0008992e, 0x00d, 0x0000e52c, 0x00e, 0x00039ce7, 0x00f, 0x00000451, 0x019, 0x00000000, 0x01a, 0x00010255, 0x01b, 0x00060a00, 0x01c, 0x000fc378, 0x01d, 0x000a1250, 0x01e, 0x0004445f, 0x01f, 0x00080001, 0x020, 0x0000b614, 0x021, 0x0006c000, 0x022, 0x00000000, 0x023, 0x00001558, 0x024, 0x00000060, 0x025, 0x00000483, 0x026, 0x0004f000, 0x027, 0x000ec7d9, 0x028, 0x000977c0, 0x029, 0x00004783, 0x02a, 0x00000001, 0x02b, 0x00021334, 0x02a, 0x00000000, 0x02b, 0x00000054, 0x02a, 0x00000001, 0x02b, 0x00000808, 0x02b, 0x00053333, 0x02c, 0x0000000c, 0x02a, 0x00000002, 0x02b, 0x00000808, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000003, 0x02b, 0x00000808, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000004, 0x02b, 0x00000808, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x00000005, 0x02b, 0x00000808, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x00000006, 0x02b, 0x00000709, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x00000007, 0x02b, 0x00000709, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x00000008, 0x02b, 0x0000060a, 0x02b, 0x0004b333, 0x02c, 0x0000000d, 0x02a, 0x00000009, 0x02b, 0x0000060a, 0x02b, 0x00053333, 0x02c, 0x0000000d, 0x02a, 0x0000000a, 0x02b, 0x0000060a, 0x02b, 0x0005b333, 0x02c, 0x0000000d, 0x02a, 0x0000000b, 0x02b, 0x0000060a, 0x02b, 0x00063333, 0x02c, 0x0000000d, 0x02a, 0x0000000c, 0x02b, 0x0000060a, 0x02b, 0x0006b333, 0x02c, 0x0000000d, 0x02a, 0x0000000d, 0x02b, 0x0000060a, 0x02b, 0x00073333, 0x02c, 0x0000000d, 0x02a, 0x0000000e, 0x02b, 0x0000050b, 0x02b, 0x00066666, 0x02c, 0x0000001a, 0x02a, 0x000e0000, 0x010, 0x0004000f, 0x011, 0x000e31fc, 0x010, 0x0006000f, 0x011, 0x000ff9f8, 0x010, 0x0002000f, 0x011, 0x000203f9, 0x010, 0x0003000f, 0x011, 0x000ff500, 0x010, 0x00000000, 0x011, 0x00000000, 0x010, 0x0008000f, 0x011, 0x0003f100, 0x010, 0x0009000f, 0x011, 0x00023100, 0x012, 0x00032000, 0x012, 0x00071000, 0x012, 0x000b0000, 0x012, 0x000fc000, 0x013, 0x000287af, 0x013, 0x000244b7, 0x013, 0x000204ab, 0x013, 0x0001c49f, 0x013, 0x00018493, 0x013, 0x00014297, 0x013, 0x00010295, 0x013, 0x0000c298, 0x013, 0x0000819c, 0x013, 0x000040a8, 0x013, 0x0000001c, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, 0x014, 0x000d9444, 0x015, 0x0000f424, 0x015, 0x0004f424, 0x015, 0x0008f424, 0x015, 0x000cf424, 0x016, 0x000e0330, 0x016, 0x000a0330, 0x016, 0x00060330, 0x016, 0x00020330, 0x000, 0x00010159, 0x018, 0x0000f401, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01f, 0x00080003, 0x0fe, 0x00000000, 0x0fe, 0x00000000, 0x01e, 0x00044457, 0x01f, 0x00080000, 0x000, 0x00030159, }; u32 RTL8192CE_RADIOB_1TARRAY[RADIOB_1TARRAYLENGTH] = { 0x0, }; u32 RTL8192CEMAC_2T_ARRAY[MAC_2T_ARRAYLENGTH] = { 0x420, 0x00000080, 0x423, 0x00000000, 0x430, 0x00000000, 0x431, 0x00000000, 0x432, 0x00000000, 0x433, 0x00000001, 0x434, 0x00000004, 0x435, 0x00000005, 0x436, 0x00000006, 0x437, 0x00000007, 0x438, 0x00000000, 0x439, 0x00000000, 0x43a, 0x00000000, 0x43b, 0x00000001, 0x43c, 0x00000004, 0x43d, 0x00000005, 0x43e, 0x00000006, 0x43f, 0x00000007, 0x440, 0x0000005d, 0x441, 0x00000001, 0x442, 0x00000000, 0x444, 0x00000015, 0x445, 0x000000f0, 0x446, 0x0000000f, 0x447, 0x00000000, 0x458, 0x00000041, 0x459, 0x000000a8, 0x45a, 0x00000072, 0x45b, 0x000000b9, 0x460, 0x00000088, 0x461, 0x00000088, 0x462, 0x00000006, 0x463, 0x00000003, 0x4c8, 0x00000004, 0x4c9, 0x00000008, 0x4cc, 0x00000002, 0x4cd, 0x00000028, 0x4ce, 0x00000001, 0x500, 0x00000026, 0x501, 0x000000a2, 0x502, 0x0000002f, 0x503, 0x00000000, 0x504, 0x00000028, 0x505, 0x000000a3, 0x506, 0x0000005e, 0x507, 0x00000000, 0x508, 0x0000002b, 0x509, 0x000000a4, 0x50a, 0x0000005e, 0x50b, 0x00000000, 0x50c, 0x0000004f, 0x50d, 0x000000a4, 0x50e, 0x00000000, 0x50f, 0x00000000, 0x512, 0x0000001c, 0x514, 0x0000000a, 0x515, 0x00000010, 0x516, 0x0000000a, 0x517, 0x00000010, 0x51a, 0x00000016, 0x524, 0x0000000f, 0x525, 0x0000004f, 0x546, 0x00000020, 0x547, 0x00000000, 0x559, 0x00000002, 0x55a, 0x00000002, 0x55d, 0x000000ff, 0x605, 0x00000030, 0x608, 0x0000000e, 0x609, 0x0000002a, 0x652, 0x00000020, 0x63c, 0x0000000a, 0x63d, 0x0000000a, 0x700, 0x00000021, 0x701, 0x00000043, 0x702, 0x00000065, 0x703, 0x00000087, 0x708, 0x00000021, 0x709, 0x00000043, 0x70a, 0x00000065, 0x70b, 0x00000087, }; u32 RTL8192CEAGCTAB_2TARRAY[AGCTAB_2TARRAYLENGTH] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7b020001, 0xc78, 0x7b030001, 0xc78, 0x7b040001, 0xc78, 0x7b050001, 0xc78, 0x7a060001, 0xc78, 0x79070001, 0xc78, 0x78080001, 0xc78, 0x77090001, 0xc78, 0x760a0001, 0xc78, 0x750b0001, 0xc78, 0x740c0001, 0xc78, 0x730d0001, 0xc78, 0x720e0001, 0xc78, 0x710f0001, 0xc78, 0x70100001, 0xc78, 0x6f110001, 0xc78, 0x6e120001, 0xc78, 0x6d130001, 0xc78, 0x6c140001, 0xc78, 0x6b150001, 0xc78, 0x6a160001, 0xc78, 0x69170001, 0xc78, 0x68180001, 0xc78, 0x67190001, 0xc78, 0x661a0001, 0xc78, 0x651b0001, 0xc78, 0x641c0001, 0xc78, 0x631d0001, 0xc78, 0x621e0001, 0xc78, 0x611f0001, 0xc78, 0x60200001, 0xc78, 0x49210001, 0xc78, 0x48220001, 0xc78, 0x47230001, 0xc78, 0x46240001, 0xc78, 0x45250001, 0xc78, 0x44260001, 0xc78, 0x43270001, 0xc78, 0x42280001, 0xc78, 0x41290001, 0xc78, 0x402a0001, 0xc78, 0x262b0001, 0xc78, 0x252c0001, 0xc78, 0x242d0001, 0xc78, 0x232e0001, 0xc78, 0x222f0001, 0xc78, 0x21300001, 0xc78, 0x20310001, 0xc78, 0x06320001, 0xc78, 0x05330001, 0xc78, 0x04340001, 0xc78, 0x03350001, 0xc78, 0x02360001, 0xc78, 0x01370001, 0xc78, 0x00380001, 0xc78, 0x00390001, 0xc78, 0x003a0001, 0xc78, 0x003b0001, 0xc78, 0x003c0001, 0xc78, 0x003d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x7b400001, 0xc78, 0x7b410001, 0xc78, 0x7b420001, 0xc78, 0x7b430001, 0xc78, 0x7b440001, 0xc78, 0x7b450001, 0xc78, 0x7a460001, 0xc78, 0x79470001, 0xc78, 0x78480001, 0xc78, 0x77490001, 0xc78, 0x764a0001, 0xc78, 0x754b0001, 0xc78, 0x744c0001, 0xc78, 0x734d0001, 0xc78, 0x724e0001, 0xc78, 0x714f0001, 0xc78, 0x70500001, 0xc78, 0x6f510001, 0xc78, 0x6e520001, 0xc78, 0x6d530001, 0xc78, 0x6c540001, 0xc78, 0x6b550001, 0xc78, 0x6a560001, 0xc78, 0x69570001, 0xc78, 0x68580001, 0xc78, 0x67590001, 0xc78, 0x665a0001, 0xc78, 0x655b0001, 0xc78, 0x645c0001, 0xc78, 0x635d0001, 0xc78, 0x625e0001, 0xc78, 0x615f0001, 0xc78, 0x60600001, 0xc78, 0x49610001, 0xc78, 0x48620001, 0xc78, 0x47630001, 0xc78, 0x46640001, 0xc78, 0x45650001, 0xc78, 0x44660001, 0xc78, 0x43670001, 0xc78, 0x42680001, 0xc78, 0x41690001, 0xc78, 0x406a0001, 0xc78, 0x266b0001, 0xc78, 0x256c0001, 0xc78, 0x246d0001, 0xc78, 0x236e0001, 0xc78, 0x226f0001, 0xc78, 0x21700001, 0xc78, 0x20710001, 0xc78, 0x06720001, 0xc78, 0x05730001, 0xc78, 0x04740001, 0xc78, 0x03750001, 0xc78, 0x02760001, 0xc78, 0x01770001, 0xc78, 0x00780001, 0xc78, 0x00790001, 0xc78, 0x007a0001, 0xc78, 0x007b0001, 0xc78, 0x007c0001, 0xc78, 0x007d0001, 0xc78, 0x007e0001, 0xc78, 0x007f0001, 0xc78, 0x3800001e, 0xc78, 0x3801001e, 0xc78, 0x3802001e, 0xc78, 0x3803001e, 0xc78, 0x3804001e, 0xc78, 0x3805001e, 0xc78, 0x3806001e, 0xc78, 0x3807001e, 0xc78, 0x3808001e, 0xc78, 0x3c09001e, 0xc78, 0x3e0a001e, 0xc78, 0x400b001e, 0xc78, 0x440c001e, 0xc78, 0x480d001e, 0xc78, 0x4c0e001e, 0xc78, 0x500f001e, 0xc78, 0x5210001e, 0xc78, 0x5611001e, 0xc78, 0x5a12001e, 0xc78, 0x5e13001e, 0xc78, 0x6014001e, 0xc78, 0x6015001e, 0xc78, 0x6016001e, 0xc78, 0x6217001e, 0xc78, 0x6218001e, 0xc78, 0x6219001e, 0xc78, 0x621a001e, 0xc78, 0x621b001e, 0xc78, 0x621c001e, 0xc78, 0x621d001e, 0xc78, 0x621e001e, 0xc78, 0x621f001e, }; u32 RTL8192CEAGCTAB_1TARRAY[AGCTAB_1TARRAYLENGTH] = { 0xc78, 0x7b000001, 0xc78, 0x7b010001, 0xc78, 0x7b020001, 0xc78, 0x7b030001, 0xc78, 0x7b040001, 0xc78, 0x7b050001, 0xc78, 0x7a060001, 0xc78, 0x79070001, 0xc78, 0x78080001, 0xc78, 0x77090001, 0xc78, 0x760a0001, 0xc78, 0x750b0001, 0xc78, 0x740c0001, 0xc78, 0x730d0001, 0xc78, 0x720e0001, 0xc78, 0x710f0001, 0xc78, 0x70100001, 0xc78, 0x6f110001, 0xc78, 0x6e120001, 0xc78, 0x6d130001, 0xc78, 0x6c140001, 0xc78, 0x6b150001, 0xc78, 0x6a160001, 0xc78, 0x69170001, 0xc78, 0x68180001, 0xc78, 0x67190001, 0xc78, 0x661a0001, 0xc78, 0x651b0001, 0xc78, 0x641c0001, 0xc78, 0x631d0001, 0xc78, 0x621e0001, 0xc78, 0x611f0001, 0xc78, 0x60200001, 0xc78, 0x49210001, 0xc78, 0x48220001, 0xc78, 0x47230001, 0xc78, 0x46240001, 0xc78, 0x45250001, 0xc78, 0x44260001, 0xc78, 0x43270001, 0xc78, 0x42280001, 0xc78, 0x41290001, 0xc78, 0x402a0001, 0xc78, 0x262b0001, 0xc78, 0x252c0001, 0xc78, 0x242d0001, 0xc78, 0x232e0001, 0xc78, 0x222f0001, 0xc78, 0x21300001, 0xc78, 0x20310001, 0xc78, 0x06320001, 0xc78, 0x05330001, 0xc78, 0x04340001, 0xc78, 0x03350001, 0xc78, 0x02360001, 0xc78, 0x01370001, 0xc78, 0x00380001, 0xc78, 0x00390001, 0xc78, 0x003a0001, 0xc78, 0x003b0001, 0xc78, 0x003c0001, 0xc78, 0x003d0001, 0xc78, 0x003e0001, 0xc78, 0x003f0001, 0xc78, 0x7b400001, 0xc78, 0x7b410001, 0xc78, 0x7b420001, 0xc78, 0x7b430001, 0xc78, 0x7b440001, 0xc78, 0x7b450001, 0xc78, 0x7a460001, 0xc78, 0x79470001, 0xc78, 0x78480001, 0xc78, 0x77490001, 0xc78, 0x764a0001, 0xc78, 0x754b0001, 0xc78, 0x744c0001, 0xc78, 0x734d0001, 0xc78, 0x724e0001, 0xc78, 0x714f0001, 0xc78, 0x70500001, 0xc78, 0x6f510001, 0xc78, 0x6e520001, 0xc78, 0x6d530001, 0xc78, 0x6c540001, 0xc78, 0x6b550001, 0xc78, 0x6a560001, 0xc78, 0x69570001, 0xc78, 0x68580001, 0xc78, 0x67590001, 0xc78, 0x665a0001, 0xc78, 0x655b0001, 0xc78, 0x645c0001, 0xc78, 0x635d0001, 0xc78, 0x625e0001, 0xc78, 0x615f0001, 0xc78, 0x60600001, 0xc78, 0x49610001, 0xc78, 0x48620001, 0xc78, 0x47630001, 0xc78, 0x46640001, 0xc78, 0x45650001, 0xc78, 0x44660001, 0xc78, 0x43670001, 0xc78, 0x42680001, 0xc78, 0x41690001, 0xc78, 0x406a0001, 0xc78, 0x266b0001, 0xc78, 0x256c0001, 0xc78, 0x246d0001, 0xc78, 0x236e0001, 0xc78, 0x226f0001, 0xc78, 0x21700001, 0xc78, 0x20710001, 0xc78, 0x06720001, 0xc78, 0x05730001, 0xc78, 0x04740001, 0xc78, 0x03750001, 0xc78, 0x02760001, 0xc78, 0x01770001, 0xc78, 0x00780001, 0xc78, 0x00790001, 0xc78, 0x007a0001, 0xc78, 0x007b0001, 0xc78, 0x007c0001, 0xc78, 0x007d0001, 0xc78, 0x007e0001, 0xc78, 0x007f0001, 0xc78, 0x3800001e, 0xc78, 0x3801001e, 0xc78, 0x3802001e, 0xc78, 0x3803001e, 0xc78, 0x3804001e, 0xc78, 0x3805001e, 0xc78, 0x3806001e, 0xc78, 0x3807001e, 0xc78, 0x3808001e, 0xc78, 0x3c09001e, 0xc78, 0x3e0a001e, 0xc78, 0x400b001e, 0xc78, 0x440c001e, 0xc78, 0x480d001e, 0xc78, 0x4c0e001e, 0xc78, 0x500f001e, 0xc78, 0x5210001e, 0xc78, 0x5611001e, 0xc78, 0x5a12001e, 0xc78, 0x5e13001e, 0xc78, 0x6014001e, 0xc78, 0x6015001e, 0xc78, 0x6016001e, 0xc78, 0x6217001e, 0xc78, 0x6218001e, 0xc78, 0x6219001e, 0xc78, 0x621a001e, 0xc78, 0x621b001e, 0xc78, 0x621c001e, 0xc78, 0x621d001e, 0xc78, 0x621e001e, 0xc78, 0x621f001e, }; compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/sw.h0000644000175000017500000000305712026211315024405 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CE_SW_H__ #define __RTL92CE_SW_H__ int rtl92c_init_sw_vars(struct ieee80211_hw *hw); void rtl92c_deinit_sw_vars(struct ieee80211_hw *hw); void rtl92c_init_var_map(struct ieee80211_hw *hw); bool _rtl92ce_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype); bool _rtl92ce_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h0000644000175000017500000000323712026211315024363 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_RF_H__ #define __RTL92C_RF_H__ #define RF6052_MAX_TX_PWR 0x3F #define RF6052_MAX_REG 0x3F #define RF6052_MAX_PATH 2 extern void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); extern void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel); extern void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel); extern bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c0000644000175000017500000003374512026211315024365 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" static bool _rtl92ce_phy_rf6052_config_parafile(struct ieee80211_hw *hw); void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); switch (bandwidth) { case HT_CHANNEL_WIDTH_20: rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 0xfffff3ff) | 0x0400); rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[0]); break; case HT_CHANNEL_WIDTH_20_40: rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 0xfffff3ff)); rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[0]); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", bandwidth); break; } } void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 tx_agc[2] = {0, 0}, tmpval; bool turbo_scanoff = false; u8 idx1, idx2; u8 *ptr; if (rtlefuse->eeprom_regulatory != 0) turbo_scanoff = true; if (mac->act_scanning) { tx_agc[RF90_PATH_A] = 0x3f3f3f3f; tx_agc[RF90_PATH_B] = 0x3f3f3f3f; if (turbo_scanoff) { for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { tx_agc[idx1] = ppowerlevel[idx1] | (ppowerlevel[idx1] << 8) | (ppowerlevel[idx1] << 16) | (ppowerlevel[idx1] << 24); } } } else { for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { tx_agc[idx1] = ppowerlevel[idx1] | (ppowerlevel[idx1] << 8) | (ppowerlevel[idx1] << 16) | (ppowerlevel[idx1] << 24); } if (rtlefuse->eeprom_regulatory == 0) { tmpval = (rtlphy->mcs_txpwrlevel_origoffset[0][6]) + (rtlphy->mcs_txpwrlevel_origoffset[0][7] << 8); tx_agc[RF90_PATH_A] += tmpval; tmpval = (rtlphy->mcs_txpwrlevel_origoffset[0][14]) + (rtlphy->mcs_txpwrlevel_origoffset[0][15] << 24); tx_agc[RF90_PATH_B] += tmpval; } } for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { ptr = (u8 *) (&(tx_agc[idx1])); for (idx2 = 0; idx2 < 4; idx2++) { if (*ptr > RF6052_MAX_TX_PWR) *ptr = RF6052_MAX_TX_PWR; ptr++; } } tmpval = tx_agc[RF90_PATH_A] & 0xff; rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_A_CCK1_MCS32); tmpval = tx_agc[RF90_PATH_A] >> 8; tmpval = tmpval & 0xff00ffff; rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); tmpval = tx_agc[RF90_PATH_B] >> 24; rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff; rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK1_55_MCS32); } static void rtl92c_phy_get_power_base(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel, u32 *ofdmbase, u32 *mcsbase) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 powerBase0, powerBase1; u8 legacy_pwrdiff, ht20_pwrdiff; u8 i, powerlevel[2]; for (i = 0; i < 2; i++) { powerlevel[i] = ppowerlevel[i]; legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff[i][channel - 1]; powerBase0 = powerlevel[i] + legacy_pwrdiff; powerBase0 = (powerBase0 << 24) | (powerBase0 << 16) | (powerBase0 << 8) | powerBase0; *(ofdmbase + i) = powerBase0; RTPRINT(rtlpriv, FPHY, PHY_TXPWR, " [OFDM power base index rf(%c) = 0x%x]\n", i == 0 ? 'A' : 'B', *(ofdmbase + i)); } for (i = 0; i < 2; i++) { if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) { ht20_pwrdiff = rtlefuse->txpwr_ht20diff[i][channel - 1]; powerlevel[i] += ht20_pwrdiff; } powerBase1 = powerlevel[i]; powerBase1 = (powerBase1 << 24) | (powerBase1 << 16) | (powerBase1 << 8) | powerBase1; *(mcsbase + i) = powerBase1; RTPRINT(rtlpriv, FPHY, PHY_TXPWR, " [MCS power base index rf(%c) = 0x%x]\n", i == 0 ? 'A' : 'B', *(mcsbase + i)); } } static void _rtl92c_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw, u8 channel, u8 index, u32 *powerBase0, u32 *powerBase1, u32 *p_outwriteval) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 i, chnlgroup = 0, pwr_diff_limit[4]; u32 writeVal, customer_limit, rf; for (rf = 0; rf < 2; rf++) { switch (rtlefuse->eeprom_regulatory) { case 0: chnlgroup = 0; writeVal = rtlphy->mcs_txpwrlevel_origoffset[chnlgroup][index + (rf ? 8 : 0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "RTK better performance, writeVal(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; case 1: if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { writeVal = ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Realtek regulatory, 40MHz, writeVal(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); } else { if (rtlphy->pwrgroup_cnt == 1) chnlgroup = 0; if (rtlphy->pwrgroup_cnt >= 3) { if (channel <= 3) chnlgroup = 0; else if (channel >= 4 && channel <= 9) chnlgroup = 1; else if (channel > 9) chnlgroup = 2; if (rtlphy->pwrgroup_cnt == 4) chnlgroup++; } writeVal = rtlphy->mcs_txpwrlevel_origoffset[chnlgroup] [index + (rf ? 8 : 0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Realtek regulatory, 20MHz, writeVal(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); } break; case 2: writeVal = ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Better regulatory, writeVal(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; case 3: chnlgroup = 0; if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "customer's limit, 40MHz rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', rtlefuse->pwrgroup_ht40[rf][channel - 1]); } else { RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "customer's limit, 20MHz rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', rtlefuse->pwrgroup_ht20[rf][channel - 1]); } for (i = 0; i < 4; i++) { pwr_diff_limit[i] = (u8) ((rtlphy->mcs_txpwrlevel_origoffset [chnlgroup][index + (rf ? 8 : 0)] & (0x7f << (i * 8))) >> (i * 8)); if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { if (pwr_diff_limit[i] > rtlefuse-> pwrgroup_ht40[rf][channel - 1]) pwr_diff_limit[i] = rtlefuse->pwrgroup_ht40[rf] [channel - 1]; } else { if (pwr_diff_limit[i] > rtlefuse-> pwrgroup_ht20[rf][channel - 1]) pwr_diff_limit[i] = rtlefuse->pwrgroup_ht20[rf] [channel - 1]; } } customer_limit = (pwr_diff_limit[3] << 24) | (pwr_diff_limit[2] << 16) | (pwr_diff_limit[1] << 8) | (pwr_diff_limit[0]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Customer's limit rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', customer_limit); writeVal = customer_limit + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Customer, writeVal rf(%c)= 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; default: chnlgroup = 0; writeVal = rtlphy->mcs_txpwrlevel_origoffset[chnlgroup] [index + (rf ? 8 : 0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "RTK better performance, writeVal rf(%c) = 0x%x\n", rf == 0 ? 'A' : 'B', writeVal); break; } if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1) writeVal = writeVal - 0x06060606; else if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT2) writeVal = writeVal - 0x0c0c0c0c; *(p_outwriteval + rf) = writeVal; } } static void _rtl92c_write_ofdm_power_reg(struct ieee80211_hw *hw, u8 index, u32 *pValue) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u16 regoffset_a[6] = { RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24, RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04, RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12 }; u16 regoffset_b[6] = { RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24, RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04, RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12 }; u8 i, rf, pwr_val[4]; u32 writeVal; u16 regoffset; for (rf = 0; rf < 2; rf++) { writeVal = pValue[rf]; for (i = 0; i < 4; i++) { pwr_val[i] = (u8) ((writeVal & (0x7f << (i * 8))) >> (i * 8)); if (pwr_val[i] > RF6052_MAX_TX_PWR) pwr_val[i] = RF6052_MAX_TX_PWR; } writeVal = (pwr_val[3] << 24) | (pwr_val[2] << 16) | (pwr_val[1] << 8) | pwr_val[0]; if (rf == 0) regoffset = regoffset_a[index]; else regoffset = regoffset_b[index]; rtl_set_bbreg(hw, regoffset, MASKDWORD, writeVal); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Set 0x%x = %08x\n", regoffset, writeVal); if (((get_rf_type(rtlphy) == RF_2T2R) && (regoffset == RTXAGC_A_MCS15_MCS12 || regoffset == RTXAGC_B_MCS15_MCS12)) || ((get_rf_type(rtlphy) != RF_2T2R) && (regoffset == RTXAGC_A_MCS07_MCS04 || regoffset == RTXAGC_B_MCS07_MCS04))) { writeVal = pwr_val[3]; if (regoffset == RTXAGC_A_MCS15_MCS12 || regoffset == RTXAGC_A_MCS07_MCS04) regoffset = 0xc90; if (regoffset == RTXAGC_B_MCS15_MCS12 || regoffset == RTXAGC_B_MCS07_MCS04) regoffset = 0xc98; for (i = 0; i < 3; i++) { writeVal = (writeVal > 6) ? (writeVal - 6) : 0; rtl_write_byte(rtlpriv, (u32) (regoffset + i), (u8) writeVal); } } } } void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel) { u32 writeVal[2], powerBase0[2], powerBase1[2]; u8 index; rtl92c_phy_get_power_base(hw, ppowerlevel, channel, &powerBase0[0], &powerBase1[0]); for (index = 0; index < 6; index++) { _rtl92c_get_txpower_writeval_by_regulatory(hw, channel, index, &powerBase0[0], &powerBase1[0], &writeVal[0]); _rtl92c_write_ofdm_power_reg(hw, index, &writeVal[0]); } } bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); if (rtlphy->rf_type == RF_1T1R) rtlphy->num_total_rfpath = 1; else rtlphy->num_total_rfpath = 2; return _rtl92ce_phy_rf6052_config_parafile(hw); } static bool _rtl92ce_phy_rf6052_config_parafile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 u4_regvalue = 0; u8 rfpath; bool rtstatus = true; struct bb_reg_def *pphyreg; for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { pphyreg = &rtlphy->phyreg_def[rfpath]; switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV); break; case RF90_PATH_B: case RF90_PATH_D: u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16); break; } rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1); udelay(1); rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); udelay(1); rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREADDREAALENGTH, 0x0); udelay(1); rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0); udelay(1); switch (rfpath) { case RF90_PATH_A: rtstatus = rtl92c_phy_config_rf_with_headerfile(hw, (enum radio_path)rfpath); break; case RF90_PATH_B: rtstatus = rtl92c_phy_config_rf_with_headerfile(hw, (enum radio_path)rfpath); break; case RF90_PATH_C: break; case RF90_PATH_D: break; } switch (rfpath) { case RF90_PATH_A: case RF90_PATH_C: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV, u4_regvalue); break; case RF90_PATH_B: case RF90_PATH_D: rtl_set_bbreg(hw, pphyreg->rfintfs, BRFSI_RFENV << 16, u4_regvalue); break; } if (!rtstatus) { RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio[%d] Fail!!\n", rfpath); return false; } } RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "<---\n"); return rtstatus; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h0000644000175000017500000016563312026211315024542 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_REG_H__ #define __RTL92C_REG_H__ #define REG_SYS_ISO_CTRL 0x0000 #define REG_SYS_FUNC_EN 0x0002 #define REG_APS_FSMCO 0x0004 #define REG_SYS_CLKR 0x0008 #define REG_9346CR 0x000A #define REG_EE_VPD 0x000C #define REG_AFE_MISC 0x0010 #define REG_SPS0_CTRL 0x0011 #define REG_SPS_OCP_CFG 0x0018 #define REG_RSV_CTRL 0x001C #define REG_RF_CTRL 0x001F #define REG_LDOA15_CTRL 0x0020 #define REG_LDOV12D_CTRL 0x0021 #define REG_LDOHCI12_CTRL 0x0022 #define REG_LPLDO_CTRL 0x0023 #define REG_AFE_XTAL_CTRL 0x0024 #define REG_AFE_PLL_CTRL 0x0028 #define REG_EFUSE_CTRL 0x0030 #define REG_EFUSE_TEST 0x0034 #define REG_PWR_DATA 0x0038 #define REG_CAL_TIMER 0x003C #define REG_ACLK_MON 0x003E #define REG_GPIO_MUXCFG 0x0040 #define REG_GPIO_IO_SEL 0x0042 #define REG_MAC_PINMUX_CFG 0x0043 #define REG_GPIO_PIN_CTRL 0x0044 #define REG_GPIO_INTM 0x0048 #define REG_LEDCFG0 0x004C #define REG_LEDCFG1 0x004D #define REG_LEDCFG2 0x004E #define REG_LEDCFG3 0x004F #define REG_FSIMR 0x0050 #define REG_FSISR 0x0054 #define REG_HSIMR 0x0058 #define REG_HSISR 0x005c /* RTL8723 WIFI/BT/GPS Multi-Function GPIO Pin Control. */ #define REG_GPIO_PIN_CTRL_2 0x0060 /* RTL8723 WIFI/BT/GPS Multi-Function GPIO Select. */ #define REG_GPIO_IO_SEL_2 0x0062 /* RTL8723 WIFI/BT/GPS Multi-Function control source. */ #define REG_MULTI_FUNC_CTRL 0x0068 #define REG_MCUFWDL 0x0080 #define REG_HMEBOX_EXT_0 0x0088 #define REG_HMEBOX_EXT_1 0x008A #define REG_HMEBOX_EXT_2 0x008C #define REG_HMEBOX_EXT_3 0x008E #define REG_BIST_SCAN 0x00D0 #define REG_BIST_RPT 0x00D4 #define REG_BIST_ROM_RPT 0x00D8 #define REG_USB_SIE_INTF 0x00E0 #define REG_PCIE_MIO_INTF 0x00E4 #define REG_PCIE_MIO_INTD 0x00E8 #define REG_HPON_FSM 0x00EC #define REG_SYS_CFG 0x00F0 #define REG_GPIO_OUTSTS 0x00F4 /* For RTL8723 only.*/ #define REG_CR 0x0100 #define REG_PBP 0x0104 #define REG_TRXDMA_CTRL 0x010C #define REG_TRXFF_BNDY 0x0114 #define REG_TRXFF_STATUS 0x0118 #define REG_RXFF_PTR 0x011C #define REG_HIMR 0x0120 #define REG_HISR 0x0124 #define REG_HIMRE 0x0128 #define REG_HISRE 0x012C #define REG_CPWM 0x012F #define REG_FWIMR 0x0130 #define REG_FWISR 0x0134 #define REG_PKTBUF_DBG_CTRL 0x0140 #define REG_PKTBUF_DBG_DATA_L 0x0144 #define REG_PKTBUF_DBG_DATA_H 0x0148 #define REG_TC0_CTRL 0x0150 #define REG_TC1_CTRL 0x0154 #define REG_TC2_CTRL 0x0158 #define REG_TC3_CTRL 0x015C #define REG_TC4_CTRL 0x0160 #define REG_TCUNIT_BASE 0x0164 #define REG_MBIST_START 0x0174 #define REG_MBIST_DONE 0x0178 #define REG_MBIST_FAIL 0x017C #define REG_C2HEVT_MSG_NORMAL 0x01A0 #define REG_C2HEVT_MSG_TEST 0x01B8 #define REG_C2HEVT_CLEAR 0x01BF #define REG_MCUTST_1 0x01c0 #define REG_FMETHR 0x01C8 #define REG_HMETFR 0x01CC #define REG_HMEBOX_0 0x01D0 #define REG_HMEBOX_1 0x01D4 #define REG_HMEBOX_2 0x01D8 #define REG_HMEBOX_3 0x01DC #define REG_LLT_INIT 0x01E0 #define REG_BB_ACCEESS_CTRL 0x01E8 #define REG_BB_ACCESS_DATA 0x01EC #define REG_RQPN 0x0200 #define REG_FIFOPAGE 0x0204 #define REG_TDECTRL 0x0208 #define REG_TXDMA_OFFSET_CHK 0x020C #define REG_TXDMA_STATUS 0x0210 #define REG_RQPN_NPQ 0x0214 #define REG_RXDMA_AGG_PG_TH 0x0280 #define REG_RXPKT_NUM 0x0284 #define REG_RXDMA_STATUS 0x0288 #define REG_PCIE_CTRL_REG 0x0300 #define REG_INT_MIG 0x0304 #define REG_BCNQ_DESA 0x0308 #define REG_HQ_DESA 0x0310 #define REG_MGQ_DESA 0x0318 #define REG_VOQ_DESA 0x0320 #define REG_VIQ_DESA 0x0328 #define REG_BEQ_DESA 0x0330 #define REG_BKQ_DESA 0x0338 #define REG_RX_DESA 0x0340 #define REG_DBI 0x0348 #define REG_MDIO 0x0354 #define REG_DBG_SEL 0x0360 #define REG_PCIE_HRPWM 0x0361 #define REG_PCIE_HCPWM 0x0363 #define REG_UART_CTRL 0x0364 #define REG_UART_TX_DESA 0x0370 #define REG_UART_RX_DESA 0x0378 #define REG_HDAQ_DESA_NODEF 0x0000 #define REG_CMDQ_DESA_NODEF 0x0000 #define REG_VOQ_INFORMATION 0x0400 #define REG_VIQ_INFORMATION 0x0404 #define REG_BEQ_INFORMATION 0x0408 #define REG_BKQ_INFORMATION 0x040C #define REG_MGQ_INFORMATION 0x0410 #define REG_HGQ_INFORMATION 0x0414 #define REG_BCNQ_INFORMATION 0x0418 #define REG_CPU_MGQ_INFORMATION 0x041C #define REG_FWHW_TXQ_CTRL 0x0420 #define REG_HWSEQ_CTRL 0x0423 #define REG_TXPKTBUF_BCNQ_BDNY 0x0424 #define REG_TXPKTBUF_MGQ_BDNY 0x0425 #define REG_MULTI_BCNQ_EN 0x0426 #define REG_MULTI_BCNQ_OFFSET 0x0427 #define REG_SPEC_SIFS 0x0428 #define REG_RL 0x042A #define REG_DARFRC 0x0430 #define REG_RARFRC 0x0438 #define REG_RRSR 0x0440 #define REG_ARFR0 0x0444 #define REG_ARFR1 0x0448 #define REG_ARFR2 0x044C #define REG_ARFR3 0x0450 #define REG_AGGLEN_LMT 0x0458 #define REG_AMPDU_MIN_SPACE 0x045C #define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D #define REG_FAST_EDCA_CTRL 0x0460 #define REG_RD_RESP_PKT_TH 0x0463 #define REG_INIRTS_RATE_SEL 0x0480 #define REG_INIDATA_RATE_SEL 0x0484 #define REG_POWER_STATUS 0x04A4 #define REG_POWER_STAGE1 0x04B4 #define REG_POWER_STAGE2 0x04B8 #define REG_PKT_LIFE_TIME 0x04C0 #define REG_STBC_SETTING 0x04C4 #define REG_PROT_MODE_CTRL 0x04C8 #define REG_BAR_MODE_CTRL 0x04CC #define REG_RA_TRY_RATE_AGG_LMT 0x04CF #define REG_NQOS_SEQ 0x04DC #define REG_QOS_SEQ 0x04DE #define REG_NEED_CPU_HANDLE 0x04E0 #define REG_PKT_LOSE_RPT 0x04E1 #define REG_PTCL_ERR_STATUS 0x04E2 #define REG_DUMMY 0x04FC #define REG_EDCA_VO_PARAM 0x0500 #define REG_EDCA_VI_PARAM 0x0504 #define REG_EDCA_BE_PARAM 0x0508 #define REG_EDCA_BK_PARAM 0x050C #define REG_BCNTCFG 0x0510 #define REG_PIFS 0x0512 #define REG_RDG_PIFS 0x0513 #define REG_SIFS_CTX 0x0514 #define REG_SIFS_TRX 0x0516 #define REG_SIFS_CCK 0x0514 #define REG_SIFS_OFDM 0x0516 #define REG_AGGR_BREAK_TIME 0x051A #define REG_SLOT 0x051B #define REG_TX_PTCL_CTRL 0x0520 #define REG_TXPAUSE 0x0522 #define REG_DIS_TXREQ_CLR 0x0523 #define REG_RD_CTRL 0x0524 #define REG_TBTT_PROHIBIT 0x0540 #define REG_RD_NAV_NXT 0x0544 #define REG_NAV_PROT_LEN 0x0546 #define REG_BCN_CTRL 0x0550 #define REG_USTIME_TSF 0x0551 #define REG_MBID_NUM 0x0552 #define REG_DUAL_TSF_RST 0x0553 #define REG_BCN_INTERVAL 0x0554 #define REG_MBSSID_BCN_SPACE 0x0554 #define REG_DRVERLYINT 0x0558 #define REG_BCNDMATIM 0x0559 #define REG_ATIMWND 0x055A #define REG_BCN_MAX_ERR 0x055D #define REG_RXTSF_OFFSET_CCK 0x055E #define REG_RXTSF_OFFSET_OFDM 0x055F #define REG_TSFTR 0x0560 #define REG_INIT_TSFTR 0x0564 #define REG_PSTIMER 0x0580 #define REG_TIMER0 0x0584 #define REG_TIMER1 0x0588 #define REG_ACMHWCTRL 0x05C0 #define REG_ACMRSTCTRL 0x05C1 #define REG_ACMAVG 0x05C2 #define REG_VO_ADMTIME 0x05C4 #define REG_VI_ADMTIME 0x05C6 #define REG_BE_ADMTIME 0x05C8 #define REG_EDCA_RANDOM_GEN 0x05CC #define REG_SCH_TXCMD 0x05D0 #define REG_APSD_CTRL 0x0600 #define REG_BWOPMODE 0x0603 #define REG_TCR 0x0604 #define REG_RCR 0x0608 #define REG_RX_PKT_LIMIT 0x060C #define REG_RX_DLK_TIME 0x060D #define REG_RX_DRVINFO_SZ 0x060F #define REG_MACID 0x0610 #define REG_BSSID 0x0618 #define REG_MAR 0x0620 #define REG_MBIDCAMCFG 0x0628 #define REG_USTIME_EDCA 0x0638 #define REG_MAC_SPEC_SIFS 0x063A #define REG_RESP_SIFS_CCK 0x063C #define REG_RESP_SIFS_OFDM 0x063E /* [15:8]SIFS_R2T_OFDM, [7:0]SIFS_R2T_CCK */ #define REG_R2T_SIFS 0x063C /* [15:8]SIFS_T2T_OFDM, [7:0]SIFS_T2T_CCK */ #define REG_T2T_SIFS 0x063E #define REG_ACKTO 0x0640 #define REG_CTS2TO 0x0641 #define REG_EIFS 0x0642 #define REG_NAV_CTRL 0x0650 #define REG_BACAMCMD 0x0654 #define REG_BACAMCONTENT 0x0658 #define REG_LBDLY 0x0660 #define REG_FWDLY 0x0661 #define REG_RXERR_RPT 0x0664 #define REG_WMAC_TRXPTCL_CTL 0x0668 #define REG_CAMCMD 0x0670 #define REG_CAMWRITE 0x0674 #define REG_CAMREAD 0x0678 #define REG_CAMDBG 0x067C #define REG_SECCFG 0x0680 #define REG_WOW_CTRL 0x0690 #define REG_PSSTATUS 0x0691 #define REG_PS_RX_INFO 0x0692 #define REG_LPNAV_CTRL 0x0694 #define REG_WKFMCAM_CMD 0x0698 #define REG_WKFMCAM_RWD 0x069C #define REG_RXFLTMAP0 0x06A0 #define REG_RXFLTMAP1 0x06A2 #define REG_RXFLTMAP2 0x06A4 #define REG_BCN_PSR_RPT 0x06A8 #define REG_CALB32K_CTRL 0x06AC #define REG_PKT_MON_CTRL 0x06B4 #define REG_BT_COEX_TABLE 0x06C0 #define REG_WMAC_RESP_TXINFO 0x06D8 #define REG_USB_INFO 0xFE17 #define REG_USB_SPECIAL_OPTION 0xFE55 #define REG_USB_DMA_AGG_TO 0xFE5B #define REG_USB_AGG_TO 0xFE5C #define REG_USB_AGG_TH 0xFE5D #define REG_TEST_USB_TXQS 0xFE48 #define REG_TEST_SIE_VID 0xFE60 #define REG_TEST_SIE_PID 0xFE62 #define REG_TEST_SIE_OPTIONAL 0xFE64 #define REG_TEST_SIE_CHIRP_K 0xFE65 #define REG_TEST_SIE_PHY 0xFE66 #define REG_TEST_SIE_MAC_ADDR 0xFE70 #define REG_TEST_SIE_STRING 0xFE80 #define REG_NORMAL_SIE_VID 0xFE60 #define REG_NORMAL_SIE_PID 0xFE62 #define REG_NORMAL_SIE_OPTIONAL 0xFE64 #define REG_NORMAL_SIE_EP 0xFE65 #define REG_NORMAL_SIE_PHY 0xFE68 #define REG_NORMAL_SIE_MAC_ADDR 0xFE70 #define REG_NORMAL_SIE_STRING 0xFE80 #define CR9346 REG_9346CR #define MSR (REG_CR + 2) #define ISR REG_HISR #define TSFR REG_TSFTR #define MACIDR0 REG_MACID #define MACIDR4 (REG_MACID + 4) #define PBP REG_PBP #define IDR0 MACIDR0 #define IDR4 MACIDR4 #define UNUSED_REGISTER 0x1BF #define DCAM UNUSED_REGISTER #define PSR UNUSED_REGISTER #define BBADDR UNUSED_REGISTER #define PHYDATAR UNUSED_REGISTER #define INVALID_BBRF_VALUE 0x12345678 #define MAX_MSS_DENSITY_2T 0x13 #define MAX_MSS_DENSITY_1T 0x0A #define CMDEEPROM_EN BIT(5) #define CMDEEPROM_SEL BIT(4) #define CMD9346CR_9356SEL BIT(4) #define AUTOLOAD_EEPROM (CMDEEPROM_EN|CMDEEPROM_SEL) #define AUTOLOAD_EFUSE CMDEEPROM_EN #define GPIOSEL_GPIO 0 #define GPIOSEL_ENBT BIT(5) #define GPIO_IN REG_GPIO_PIN_CTRL #define GPIO_OUT (REG_GPIO_PIN_CTRL+1) #define GPIO_IO_SEL (REG_GPIO_PIN_CTRL+2) #define GPIO_MOD (REG_GPIO_PIN_CTRL+3) #define MSR_NOLINK 0x00 #define MSR_ADHOC 0x01 #define MSR_INFRA 0x02 #define MSR_AP 0x03 #define RRSR_RSC_OFFSET 21 #define RRSR_SHORT_OFFSET 23 #define RRSR_RSC_BW_40M 0x600000 #define RRSR_RSC_UPSUBCHNL 0x400000 #define RRSR_RSC_LOWSUBCHNL 0x200000 #define RRSR_SHORT 0x800000 #define RRSR_1M BIT(0) #define RRSR_2M BIT(1) #define RRSR_5_5M BIT(2) #define RRSR_11M BIT(3) #define RRSR_6M BIT(4) #define RRSR_9M BIT(5) #define RRSR_12M BIT(6) #define RRSR_18M BIT(7) #define RRSR_24M BIT(8) #define RRSR_36M BIT(9) #define RRSR_48M BIT(10) #define RRSR_54M BIT(11) #define RRSR_MCS0 BIT(12) #define RRSR_MCS1 BIT(13) #define RRSR_MCS2 BIT(14) #define RRSR_MCS3 BIT(15) #define RRSR_MCS4 BIT(16) #define RRSR_MCS5 BIT(17) #define RRSR_MCS6 BIT(18) #define RRSR_MCS7 BIT(19) #define BRSR_ACKSHORTPMB BIT(23) #define RATR_1M 0x00000001 #define RATR_2M 0x00000002 #define RATR_55M 0x00000004 #define RATR_11M 0x00000008 #define RATR_6M 0x00000010 #define RATR_9M 0x00000020 #define RATR_12M 0x00000040 #define RATR_18M 0x00000080 #define RATR_24M 0x00000100 #define RATR_36M 0x00000200 #define RATR_48M 0x00000400 #define RATR_54M 0x00000800 #define RATR_MCS0 0x00001000 #define RATR_MCS1 0x00002000 #define RATR_MCS2 0x00004000 #define RATR_MCS3 0x00008000 #define RATR_MCS4 0x00010000 #define RATR_MCS5 0x00020000 #define RATR_MCS6 0x00040000 #define RATR_MCS7 0x00080000 #define RATR_MCS8 0x00100000 #define RATR_MCS9 0x00200000 #define RATR_MCS10 0x00400000 #define RATR_MCS11 0x00800000 #define RATR_MCS12 0x01000000 #define RATR_MCS13 0x02000000 #define RATR_MCS14 0x04000000 #define RATR_MCS15 0x08000000 #define RATE_1M BIT(0) #define RATE_2M BIT(1) #define RATE_5_5M BIT(2) #define RATE_11M BIT(3) #define RATE_6M BIT(4) #define RATE_9M BIT(5) #define RATE_12M BIT(6) #define RATE_18M BIT(7) #define RATE_24M BIT(8) #define RATE_36M BIT(9) #define RATE_48M BIT(10) #define RATE_54M BIT(11) #define RATE_MCS0 BIT(12) #define RATE_MCS1 BIT(13) #define RATE_MCS2 BIT(14) #define RATE_MCS3 BIT(15) #define RATE_MCS4 BIT(16) #define RATE_MCS5 BIT(17) #define RATE_MCS6 BIT(18) #define RATE_MCS7 BIT(19) #define RATE_MCS8 BIT(20) #define RATE_MCS9 BIT(21) #define RATE_MCS10 BIT(22) #define RATE_MCS11 BIT(23) #define RATE_MCS12 BIT(24) #define RATE_MCS13 BIT(25) #define RATE_MCS14 BIT(26) #define RATE_MCS15 BIT(27) #define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | RATR_11M) #define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | RATR_18M \ | RATR_24M | RATR_36M | RATR_48M | RATR_54M) #define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | \ RATR_MCS3 | RATR_MCS4 | RATR_MCS5 | \ RATR_MCS6 | RATR_MCS7) #define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | \ RATR_MCS11 | RATR_MCS12 | RATR_MCS13 | \ RATR_MCS14 | RATR_MCS15) #define BW_OPMODE_20MHZ BIT(2) #define BW_OPMODE_5G BIT(1) #define BW_OPMODE_11J BIT(0) #define CAM_VALID BIT(15) #define CAM_NOTVALID 0x0000 #define CAM_USEDK BIT(5) #define CAM_NONE 0x0 #define CAM_WEP40 0x01 #define CAM_TKIP 0x02 #define CAM_AES 0x04 #define CAM_WEP104 0x05 #define TOTAL_CAM_ENTRY 32 #define HALF_CAM_ENTRY 16 #define CAM_WRITE BIT(16) #define CAM_READ 0x00000000 #define CAM_POLLINIG BIT(31) #define SCR_USEDK 0x01 #define SCR_TXSEC_ENABLE 0x02 #define SCR_RXSEC_ENABLE 0x04 #define WOW_PMEN BIT(0) #define WOW_WOMEN BIT(1) #define WOW_MAGIC BIT(2) #define WOW_UWF BIT(3) #define IMR8190_DISABLED 0x0 #define IMR_BCNDMAINT6 BIT(31) #define IMR_BCNDMAINT5 BIT(30) #define IMR_BCNDMAINT4 BIT(29) #define IMR_BCNDMAINT3 BIT(28) #define IMR_BCNDMAINT2 BIT(27) #define IMR_BCNDMAINT1 BIT(26) #define IMR_BCNDOK8 BIT(25) #define IMR_BCNDOK7 BIT(24) #define IMR_BCNDOK6 BIT(23) #define IMR_BCNDOK5 BIT(22) #define IMR_BCNDOK4 BIT(21) #define IMR_BCNDOK3 BIT(20) #define IMR_BCNDOK2 BIT(19) #define IMR_BCNDOK1 BIT(18) #define IMR_TIMEOUT2 BIT(17) #define IMR_TIMEOUT1 BIT(16) #define IMR_TXFOVW BIT(15) #define IMR_PSTIMEOUT BIT(14) #define IMR_BCNINT BIT(13) #define IMR_RXFOVW BIT(12) #define IMR_RDU BIT(11) #define IMR_ATIMEND BIT(10) #define IMR_BDOK BIT(9) #define IMR_HIGHDOK BIT(8) #define IMR_TBDOK BIT(7) #define IMR_MGNTDOK BIT(6) #define IMR_TBDER BIT(5) #define IMR_BKDOK BIT(4) #define IMR_BEDOK BIT(3) #define IMR_VIDOK BIT(2) #define IMR_VODOK BIT(1) #define IMR_ROK BIT(0) #define IMR_TXERR BIT(11) #define IMR_RXERR BIT(10) #define IMR_C2HCMD BIT(9) #define IMR_CPWM BIT(8) #define IMR_OCPINT BIT(1) #define IMR_WLANOFF BIT(0) #define EFUSE_REAL_CONTENT_LEN 512 #define EEPROM_DEFAULT_TSSI 0x0 #define EEPROM_DEFAULT_TXPOWERDIFF 0x0 #define EEPROM_DEFAULT_CRYSTALCAP 0x5 #define EEPROM_DEFAULT_BOARDTYPE 0x02 #define EEPROM_DEFAULT_TXPOWER 0x1010 #define EEPROM_DEFAULT_HT2T_TXPWR 0x10 #define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 #define EEPROM_DEFAULT_THERMALMETER 0x12 #define EEPROM_DEFAULT_ANTTXPOWERDIFF 0x0 #define EEPROM_DEFAULT_TXPWDIFF_CRYSTALCAP 0x5 #define EEPROM_DEFAULT_TXPOWERLEVEL 0x22 #define EEPROM_DEFAULT_HT40_2SDIFF 0x0 #define EEPROM_DEFAULT_HT20_DIFF 2 #define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 #define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0 #define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0 #define RF_OPTION1 0x79 #define RF_OPTION2 0x7A #define RF_OPTION3 0x7B #define RF_OPTION4 0x7C #define EEPROM_DEFAULT_PID 0x1234 #define EEPROM_DEFAULT_VID 0x5678 #define EEPROM_DEFAULT_CUSTOMERID 0xAB #define EEPROM_DEFAULT_SUBCUSTOMERID 0xCD #define EEPROM_DEFAULT_VERSION 0 #define EEPROM_CHANNEL_PLAN_FCC 0x0 #define EEPROM_CHANNEL_PLAN_IC 0x1 #define EEPROM_CHANNEL_PLAN_ETSI 0x2 #define EEPROM_CHANNEL_PLAN_SPAIN 0x3 #define EEPROM_CHANNEL_PLAN_FRANCE 0x4 #define EEPROM_CHANNEL_PLAN_MKK 0x5 #define EEPROM_CHANNEL_PLAN_MKK1 0x6 #define EEPROM_CHANNEL_PLAN_ISRAEL 0x7 #define EEPROM_CHANNEL_PLAN_TELEC 0x8 #define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9 #define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA #define EEPROM_CHANNEL_PLAN_NCC 0xB #define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80 #define EEPROM_CID_DEFAULT 0x0 #define EEPROM_CID_TOSHIBA 0x4 #define EEPROM_CID_CCX 0x10 #define EEPROM_CID_QMI 0x0D #define EEPROM_CID_WHQL 0xFE #define RTL8192_EEPROM_ID 0x8129 #define RTL8190_EEPROM_ID 0x8129 #define EEPROM_HPON 0x02 #define EEPROM_CLK 0x06 #define EEPROM_TESTR 0x08 #define EEPROM_VID 0x0A #define EEPROM_DID 0x0C #define EEPROM_SVID 0x0E #define EEPROM_SMID 0x10 #define EEPROM_MAC_ADDR 0x16 #define EEPROM_CCK_TX_PWR_INX 0x5A #define EEPROM_HT40_1S_TX_PWR_INX 0x60 #define EEPROM_HT40_2S_TX_PWR_INX_DIFF 0x66 #define EEPROM_HT20_TX_PWR_INX_DIFF 0x69 #define EEPROM_OFDM_TX_PWR_INX_DIFF 0x6C #define EEPROM_HT40_MAX_PWR_OFFSET 0x6F #define EEPROM_HT20_MAX_PWR_OFFSET 0x72 #define EEPROM_TSSI_A 0x76 #define EEPROM_TSSI_B 0x77 #define EEPROM_THERMAL_METER 0x78 #define EEPROM_XTAL_K 0x78 #define EEPROM_RF_OPT1 0x79 #define EEPROM_RF_OPT2 0x7A #define EEPROM_RF_OPT3 0x7B #define EEPROM_RF_OPT4 0x7C #define EEPROM_CHANNEL_PLAN 0x7D #define EEPROM_VERSION 0x7E #define EEPROM_CUSTOMER_ID 0x7F #define EEPROM_PWRDIFF 0x54 #define EEPROM_TXPOWERCCK 0x5A #define EEPROM_TXPOWERHT40_1S 0x60 #define EEPROM_TXPOWERHT40_2SDIFF 0x66 #define EEPROM_TXPOWERHT20DIFF 0x69 #define EEPROM_TXPOWER_OFDMDIFF 0x6C #define EEPROM_TXPWR_GROUP 0x6F #define EEPROM_TSSI_A 0x76 #define EEPROM_TSSI_B 0x77 #define EEPROM_THERMAL_METER 0x78 #define EEPROM_CHANNELPLAN 0x75 #define RF_OPTION1 0x79 #define RF_OPTION2 0x7A #define RF_OPTION3 0x7B #define RF_OPTION4 0x7C #define STOPBECON BIT(6) #define STOPHIGHT BIT(5) #define STOPMGT BIT(4) #define STOPVO BIT(3) #define STOPVI BIT(2) #define STOPBE BIT(1) #define STOPBK BIT(0) #define RCR_APPFCS BIT(31) #define RCR_APP_FCS BIT(31) #define RCR_APP_MIC BIT(30) #define RCR_APP_ICV BIT(29) #define RCR_APP_PHYSTS BIT(28) #define RCR_APP_PHYST_RXFF BIT(28) #define RCR_APP_BA_SSN BIT(27) #define RCR_ENMBID BIT(24) #define RCR_LSIGEN BIT(23) #define RCR_MFBEN BIT(22) #define RCR_HTC_LOC_CTRL BIT(14) #define RCR_AMF BIT(13) #define RCR_ACF BIT(12) #define RCR_ADF BIT(11) #define RCR_AICV BIT(9) #define RCR_ACRC32 BIT(8) #define RCR_CBSSID_BCN BIT(7) #define RCR_CBSSID_DATA BIT(6) #define RCR_CBSSID RCR_CBSSID_DATA #define RCR_APWRMGT BIT(5) #define RCR_ADD3 BIT(4) #define RCR_AB BIT(3) #define RCR_AM BIT(2) #define RCR_APM BIT(1) #define RCR_AAP BIT(0) #define RCR_MXDMA_OFFSET 8 #define RCR_FIFO_OFFSET 13 #define RSV_CTRL 0x001C #define RD_CTRL 0x0524 #define REG_USB_INFO 0xFE17 #define REG_USB_SPECIAL_OPTION 0xFE55 #define REG_USB_DMA_AGG_TO 0xFE5B #define REG_USB_AGG_TO 0xFE5C #define REG_USB_AGG_TH 0xFE5D #define REG_USB_VID 0xFE60 #define REG_USB_PID 0xFE62 #define REG_USB_OPTIONAL 0xFE64 #define REG_USB_CHIRP_K 0xFE65 #define REG_USB_PHY 0xFE66 #define REG_USB_MAC_ADDR 0xFE70 #define REG_USB_HRPWM 0xFE58 #define REG_USB_HCPWM 0xFE57 #define SW18_FPWM BIT(3) #define ISO_MD2PP BIT(0) #define ISO_UA2USB BIT(1) #define ISO_UD2CORE BIT(2) #define ISO_PA2PCIE BIT(3) #define ISO_PD2CORE BIT(4) #define ISO_IP2MAC BIT(5) #define ISO_DIOP BIT(6) #define ISO_DIOE BIT(7) #define ISO_EB2CORE BIT(8) #define ISO_DIOR BIT(9) #define PWC_EV25V BIT(14) #define PWC_EV12V BIT(15) #define FEN_BBRSTB BIT(0) #define FEN_BB_GLB_RSTn BIT(1) #define FEN_USBA BIT(2) #define FEN_UPLL BIT(3) #define FEN_USBD BIT(4) #define FEN_DIO_PCIE BIT(5) #define FEN_PCIEA BIT(6) #define FEN_PPLL BIT(7) #define FEN_PCIED BIT(8) #define FEN_DIOE BIT(9) #define FEN_CPUEN BIT(10) #define FEN_DCORE BIT(11) #define FEN_ELDR BIT(12) #define FEN_DIO_RF BIT(13) #define FEN_HWPDN BIT(14) #define FEN_MREGEN BIT(15) #define PFM_LDALL BIT(0) #define PFM_ALDN BIT(1) #define PFM_LDKP BIT(2) #define PFM_WOWL BIT(3) #define EnPDN BIT(4) #define PDN_PL BIT(5) #define APFM_ONMAC BIT(8) #define APFM_OFF BIT(9) #define APFM_RSM BIT(10) #define AFSM_HSUS BIT(11) #define AFSM_PCIE BIT(12) #define APDM_MAC BIT(13) #define APDM_HOST BIT(14) #define APDM_HPDN BIT(15) #define RDY_MACON BIT(16) #define SUS_HOST BIT(17) #define ROP_ALD BIT(20) #define ROP_PWR BIT(21) #define ROP_SPS BIT(22) #define SOP_MRST BIT(25) #define SOP_FUSE BIT(26) #define SOP_ABG BIT(27) #define SOP_AMB BIT(28) #define SOP_RCK BIT(29) #define SOP_A8M BIT(30) #define XOP_BTCK BIT(31) #define ANAD16V_EN BIT(0) #define ANA8M BIT(1) #define MACSLP BIT(4) #define LOADER_CLK_EN BIT(5) #define _80M_SSC_DIS BIT(7) #define _80M_SSC_EN_HO BIT(8) #define PHY_SSC_RSTB BIT(9) #define SEC_CLK_EN BIT(10) #define MAC_CLK_EN BIT(11) #define SYS_CLK_EN BIT(12) #define RING_CLK_EN BIT(13) #define BOOT_FROM_EEPROM BIT(4) #define EEPROM_EN BIT(5) #define AFE_BGEN BIT(0) #define AFE_MBEN BIT(1) #define MAC_ID_EN BIT(7) #define WLOCK_ALL BIT(0) #define WLOCK_00 BIT(1) #define WLOCK_04 BIT(2) #define WLOCK_08 BIT(3) #define WLOCK_40 BIT(4) #define R_DIS_PRST_0 BIT(5) #define R_DIS_PRST_1 BIT(6) #define LOCK_ALL_EN BIT(7) #define RF_EN BIT(0) #define RF_RSTB BIT(1) #define RF_SDMRSTB BIT(2) #define LDA15_EN BIT(0) #define LDA15_STBY BIT(1) #define LDA15_OBUF BIT(2) #define LDA15_REG_VOS BIT(3) #define _LDA15_VOADJ(x) (((x) & 0x7) << 4) #define LDV12_EN BIT(0) #define LDV12_SDBY BIT(1) #define LPLDO_HSM BIT(2) #define LPLDO_LSM_DIS BIT(3) #define _LDV12_VADJ(x) (((x) & 0xF) << 4) #define XTAL_EN BIT(0) #define XTAL_BSEL BIT(1) #define _XTAL_BOSC(x) (((x) & 0x3) << 2) #define _XTAL_CADJ(x) (((x) & 0xF) << 4) #define XTAL_GATE_USB BIT(8) #define _XTAL_USB_DRV(x) (((x) & 0x3) << 9) #define XTAL_GATE_AFE BIT(11) #define _XTAL_AFE_DRV(x) (((x) & 0x3) << 12) #define XTAL_RF_GATE BIT(14) #define _XTAL_RF_DRV(x) (((x) & 0x3) << 15) #define XTAL_GATE_DIG BIT(17) #define _XTAL_DIG_DRV(x) (((x) & 0x3) << 18) #define XTAL_BT_GATE BIT(20) #define _XTAL_BT_DRV(x) (((x) & 0x3) << 21) #define _XTAL_GPIO(x) (((x) & 0x7) << 23) #define CKDLY_AFE BIT(26) #define CKDLY_USB BIT(27) #define CKDLY_DIG BIT(28) #define CKDLY_BT BIT(29) #define APLL_EN BIT(0) #define APLL_320_EN BIT(1) #define APLL_FREF_SEL BIT(2) #define APLL_EDGE_SEL BIT(3) #define APLL_WDOGB BIT(4) #define APLL_LPFEN BIT(5) #define APLL_REF_CLK_13MHZ 0x1 #define APLL_REF_CLK_19_2MHZ 0x2 #define APLL_REF_CLK_20MHZ 0x3 #define APLL_REF_CLK_25MHZ 0x4 #define APLL_REF_CLK_26MHZ 0x5 #define APLL_REF_CLK_38_4MHZ 0x6 #define APLL_REF_CLK_40MHZ 0x7 #define APLL_320EN BIT(14) #define APLL_80EN BIT(15) #define APLL_1MEN BIT(24) #define ALD_EN BIT(18) #define EF_PD BIT(19) #define EF_FLAG BIT(31) #define EF_TRPT BIT(7) #define LDOE25_EN BIT(31) #define RSM_EN BIT(0) #define Timer_EN BIT(4) #define TRSW0EN BIT(2) #define TRSW1EN BIT(3) #define EROM_EN BIT(4) #define EnBT BIT(5) #define EnUart BIT(8) #define Uart_910 BIT(9) #define EnPMAC BIT(10) #define SIC_SWRST BIT(11) #define EnSIC BIT(12) #define SIC_23 BIT(13) #define EnHDP BIT(14) #define SIC_LBK BIT(15) #define LED0PL BIT(4) #define LED1PL BIT(12) #define LED0DIS BIT(7) #define MCUFWDL_EN BIT(0) #define MCUFWDL_RDY BIT(1) #define FWDL_ChkSum_rpt BIT(2) #define MACINI_RDY BIT(3) #define BBINI_RDY BIT(4) #define RFINI_RDY BIT(5) #define WINTINI_RDY BIT(6) #define CPRST BIT(23) #define XCLK_VLD BIT(0) #define ACLK_VLD BIT(1) #define UCLK_VLD BIT(2) #define PCLK_VLD BIT(3) #define PCIRSTB BIT(4) #define V15_VLD BIT(5) #define TRP_B15V_EN BIT(7) #define SIC_IDLE BIT(8) #define BD_MAC2 BIT(9) #define BD_MAC1 BIT(10) #define IC_MACPHY_MODE BIT(11) #define BT_FUNC BIT(16) #define VENDOR_ID BIT(19) #define PAD_HWPD_IDN BIT(22) #define TRP_VAUX_EN BIT(23) #define TRP_BT_EN BIT(24) #define BD_PKG_SEL BIT(25) #define BD_HCI_SEL BIT(26) #define TYPE_ID BIT(27) #define RF_RL_ID (BIT(31) | BIT(30) | BIT(29) | BIT(28)) #define CHIP_VER_RTL_MASK 0xF000 #define CHIP_VER_RTL_SHIFT 12 #define REG_LBMODE (REG_CR + 3) #define HCI_TXDMA_EN BIT(0) #define HCI_RXDMA_EN BIT(1) #define TXDMA_EN BIT(2) #define RXDMA_EN BIT(3) #define PROTOCOL_EN BIT(4) #define SCHEDULE_EN BIT(5) #define MACTXEN BIT(6) #define MACRXEN BIT(7) #define ENSWBCN BIT(8) #define ENSEC BIT(9) #define _NETTYPE(x) (((x) & 0x3) << 16) #define MASK_NETTYPE 0x30000 #define NT_NO_LINK 0x0 #define NT_LINK_AD_HOC 0x1 #define NT_LINK_AP 0x2 #define NT_AS_AP 0x3 #define _LBMODE(x) (((x) & 0xF) << 24) #define MASK_LBMODE 0xF000000 #define LOOPBACK_NORMAL 0x0 #define LOOPBACK_IMMEDIATELY 0xB #define LOOPBACK_MAC_DELAY 0x3 #define LOOPBACK_PHY 0x1 #define LOOPBACK_DMA 0x7 #define GET_RX_PAGE_SIZE(value) ((value) & 0xF) #define GET_TX_PAGE_SIZE(value) (((value) & 0xF0) >> 4) #define _PSRX_MASK 0xF #define _PSTX_MASK 0xF0 #define _PSRX(x) (x) #define _PSTX(x) ((x) << 4) #define PBP_64 0x0 #define PBP_128 0x1 #define PBP_256 0x2 #define PBP_512 0x3 #define PBP_1024 0x4 #define RXDMA_ARBBW_EN BIT(0) #define RXSHFT_EN BIT(1) #define RXDMA_AGG_EN BIT(2) #define QS_VO_QUEUE BIT(8) #define QS_VI_QUEUE BIT(9) #define QS_BE_QUEUE BIT(10) #define QS_BK_QUEUE BIT(11) #define QS_MANAGER_QUEUE BIT(12) #define QS_HIGH_QUEUE BIT(13) #define HQSEL_VOQ BIT(0) #define HQSEL_VIQ BIT(1) #define HQSEL_BEQ BIT(2) #define HQSEL_BKQ BIT(3) #define HQSEL_MGTQ BIT(4) #define HQSEL_HIQ BIT(5) #define _TXDMA_HIQ_MAP(x) (((x)&0x3) << 14) #define _TXDMA_MGQ_MAP(x) (((x)&0x3) << 12) #define _TXDMA_BKQ_MAP(x) (((x)&0x3) << 10) #define _TXDMA_BEQ_MAP(x) (((x)&0x3) << 8) #define _TXDMA_VIQ_MAP(x) (((x)&0x3) << 6) #define _TXDMA_VOQ_MAP(x) (((x)&0x3) << 4) #define QUEUE_LOW 1 #define QUEUE_NORMAL 2 #define QUEUE_HIGH 3 #define _LLT_NO_ACTIVE 0x0 #define _LLT_WRITE_ACCESS 0x1 #define _LLT_READ_ACCESS 0x2 #define _LLT_INIT_DATA(x) ((x) & 0xFF) #define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8) #define _LLT_OP(x) (((x) & 0x3) << 30) #define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3) #define BB_WRITE_READ_MASK (BIT(31) | BIT(30)) #define BB_WRITE_EN BIT(30) #define BB_READ_EN BIT(31) #define _HPQ(x) ((x) & 0xFF) #define _LPQ(x) (((x) & 0xFF) << 8) #define _PUBQ(x) (((x) & 0xFF) << 16) #define _NPQ(x) ((x) & 0xFF) #define HPQ_PUBLIC_DIS BIT(24) #define LPQ_PUBLIC_DIS BIT(25) #define LD_RQPN BIT(31) #define BCN_VALID BIT(16) #define BCN_HEAD(x) (((x) & 0xFF) << 8) #define BCN_HEAD_MASK 0xFF00 #define BLK_DESC_NUM_SHIFT 4 #define BLK_DESC_NUM_MASK 0xF #define DROP_DATA_EN BIT(9) #define EN_AMPDU_RTY_NEW BIT(7) #define _INIRTSMCS_SEL(x) ((x) & 0x3F) #define _SPEC_SIFS_CCK(x) ((x) & 0xFF) #define _SPEC_SIFS_OFDM(x) (((x) & 0xFF) << 8) #define RATE_REG_BITMAP_ALL 0xFFFFF #define _RRSC_BITMAP(x) ((x) & 0xFFFFF) #define _RRSR_RSC(x) (((x) & 0x3) << 21) #define RRSR_RSC_RESERVED 0x0 #define RRSR_RSC_UPPER_SUBCHANNEL 0x1 #define RRSR_RSC_LOWER_SUBCHANNEL 0x2 #define RRSR_RSC_DUPLICATE_MODE 0x3 #define USE_SHORT_G1 BIT(20) #define _AGGLMT_MCS0(x) ((x) & 0xF) #define _AGGLMT_MCS1(x) (((x) & 0xF) << 4) #define _AGGLMT_MCS2(x) (((x) & 0xF) << 8) #define _AGGLMT_MCS3(x) (((x) & 0xF) << 12) #define _AGGLMT_MCS4(x) (((x) & 0xF) << 16) #define _AGGLMT_MCS5(x) (((x) & 0xF) << 20) #define _AGGLMT_MCS6(x) (((x) & 0xF) << 24) #define _AGGLMT_MCS7(x) (((x) & 0xF) << 28) #define RETRY_LIMIT_SHORT_SHIFT 8 #define RETRY_LIMIT_LONG_SHIFT 0 #define _DARF_RC1(x) ((x) & 0x1F) #define _DARF_RC2(x) (((x) & 0x1F) << 8) #define _DARF_RC3(x) (((x) & 0x1F) << 16) #define _DARF_RC4(x) (((x) & 0x1F) << 24) #define _DARF_RC5(x) ((x) & 0x1F) #define _DARF_RC6(x) (((x) & 0x1F) << 8) #define _DARF_RC7(x) (((x) & 0x1F) << 16) #define _DARF_RC8(x) (((x) & 0x1F) << 24) #define _RARF_RC1(x) ((x) & 0x1F) #define _RARF_RC2(x) (((x) & 0x1F) << 8) #define _RARF_RC3(x) (((x) & 0x1F) << 16) #define _RARF_RC4(x) (((x) & 0x1F) << 24) #define _RARF_RC5(x) ((x) & 0x1F) #define _RARF_RC6(x) (((x) & 0x1F) << 8) #define _RARF_RC7(x) (((x) & 0x1F) << 16) #define _RARF_RC8(x) (((x) & 0x1F) << 24) #define AC_PARAM_TXOP_OFFSET 16 #define AC_PARAM_TXOP_LIMIT_OFFSET 16 #define AC_PARAM_ECW_MAX_OFFSET 12 #define AC_PARAM_ECW_MIN_OFFSET 8 #define AC_PARAM_AIFS_OFFSET 0 #define _AIFS(x) (x) #define _ECW_MAX_MIN(x) ((x) << 8) #define _TXOP_LIMIT(x) ((x) << 16) #define _BCNIFS(x) ((x) & 0xFF) #define _BCNECW(x) ((((x) & 0xF)) << 8) #define _LRL(x) ((x) & 0x3F) #define _SRL(x) (((x) & 0x3F) << 8) #define _SIFS_CCK_CTX(x) ((x) & 0xFF) #define _SIFS_CCK_TRX(x) (((x) & 0xFF) << 8) #define _SIFS_OFDM_CTX(x) ((x) & 0xFF) #define _SIFS_OFDM_TRX(x) (((x) & 0xFF) << 8) #define _TBTT_PROHIBIT_HOLD(x) (((x) & 0xFF) << 8) #define DIS_EDCA_CNT_DWN BIT(11) #define EN_MBSSID BIT(1) #define EN_TXBCN_RPT BIT(2) #define EN_BCN_FUNCTION BIT(3) #define TSFTR_RST BIT(0) #define TSFTR1_RST BIT(1) #define STOP_BCNQ BIT(6) #define DIS_TSF_UDT0_NORMAL_CHIP BIT(4) #define DIS_TSF_UDT0_TEST_CHIP BIT(5) #define AcmHw_HwEn BIT(0) #define AcmHw_BeqEn BIT(1) #define AcmHw_ViqEn BIT(2) #define AcmHw_VoqEn BIT(3) #define AcmHw_BeqStatus BIT(4) #define AcmHw_ViqStatus BIT(5) #define AcmHw_VoqStatus BIT(6) #define APSDOFF BIT(6) #define APSDOFF_STATUS BIT(7) #define BW_20MHZ BIT(2) #define RATE_BITMAP_ALL 0xFFFFF #define RATE_RRSR_CCK_ONLY_1M 0xFFFF1 #define TSFRST BIT(0) #define DIS_GCLK BIT(1) #define PAD_SEL BIT(2) #define PWR_ST BIT(6) #define PWRBIT_OW_EN BIT(7) #define ACRC BIT(8) #define CFENDFORM BIT(9) #define ICV BIT(10) #define AAP BIT(0) #define APM BIT(1) #define AM BIT(2) #define AB BIT(3) #define ADD3 BIT(4) #define APWRMGT BIT(5) #define CBSSID BIT(6) #define CBSSID_DATA BIT(6) #define CBSSID_BCN BIT(7) #define ACRC32 BIT(8) #define AICV BIT(9) #define ADF BIT(11) #define ACF BIT(12) #define AMF BIT(13) #define HTC_LOC_CTRL BIT(14) #define UC_DATA_EN BIT(16) #define BM_DATA_EN BIT(17) #define MFBEN BIT(22) #define LSIGEN BIT(23) #define EnMBID BIT(24) #define APP_BASSN BIT(27) #define APP_PHYSTS BIT(28) #define APP_ICV BIT(29) #define APP_MIC BIT(30) #define APP_FCS BIT(31) #define _MIN_SPACE(x) ((x) & 0x7) #define _SHORT_GI_PADDING(x) (((x) & 0x1F) << 3) #define RXERR_TYPE_OFDM_PPDU 0 #define RXERR_TYPE_OFDM_FALSE_ALARM 1 #define RXERR_TYPE_OFDM_MPDU_OK 2 #define RXERR_TYPE_OFDM_MPDU_FAIL 3 #define RXERR_TYPE_CCK_PPDU 4 #define RXERR_TYPE_CCK_FALSE_ALARM 5 #define RXERR_TYPE_CCK_MPDU_OK 6 #define RXERR_TYPE_CCK_MPDU_FAIL 7 #define RXERR_TYPE_HT_PPDU 8 #define RXERR_TYPE_HT_FALSE_ALARM 9 #define RXERR_TYPE_HT_MPDU_TOTAL 10 #define RXERR_TYPE_HT_MPDU_OK 11 #define RXERR_TYPE_HT_MPDU_FAIL 12 #define RXERR_TYPE_RX_FULL_DROP 15 #define RXERR_COUNTER_MASK 0xFFFFF #define RXERR_RPT_RST BIT(27) #define _RXERR_RPT_SEL(type) ((type) << 28) #define SCR_TxUseDK BIT(0) #define SCR_RxUseDK BIT(1) #define SCR_TxEncEnable BIT(2) #define SCR_RxDecEnable BIT(3) #define SCR_SKByA2 BIT(4) #define SCR_NoSKMC BIT(5) #define SCR_TXBCUSEDK BIT(6) #define SCR_RXBCUSEDK BIT(7) #define USB_IS_HIGH_SPEED 0 #define USB_IS_FULL_SPEED 1 #define USB_SPEED_MASK BIT(5) #define USB_NORMAL_SIE_EP_MASK 0xF #define USB_NORMAL_SIE_EP_SHIFT 4 #define USB_TEST_EP_MASK 0x30 #define USB_TEST_EP_SHIFT 4 #define USB_AGG_EN BIT(3) #define LAST_ENTRY_OF_TX_PKT_BUFFER 255 #define POLLING_LLT_THRESHOLD 20 #define POLLING_READY_TIMEOUT_COUNT 1000 #define MAX_MSS_DENSITY_2T 0x13 #define MAX_MSS_DENSITY_1T 0x0A #define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6)) #define EPROM_CMD_CONFIG 0x3 #define EPROM_CMD_LOAD 1 #define HWSET_MAX_SIZE 128 #define HWSET_MAX_SIZE_92S HWSET_MAX_SIZE #define EFUSE_MAX_SECTION 16 #define WL_HWPDN_EN BIT(0) #define HAL_8192C_HW_GPIO_WPS_BIT BIT(2) #define RPMAC_RESET 0x100 #define RPMAC_TXSTART 0x104 #define RPMAC_TXLEGACYSIG 0x108 #define RPMAC_TXHTSIG1 0x10c #define RPMAC_TXHTSIG2 0x110 #define RPMAC_PHYDEBUG 0x114 #define RPMAC_TXPACKETNUM 0x118 #define RPMAC_TXIDLE 0x11c #define RPMAC_TXMACHEADER0 0x120 #define RPMAC_TXMACHEADER1 0x124 #define RPMAC_TXMACHEADER2 0x128 #define RPMAC_TXMACHEADER3 0x12c #define RPMAC_TXMACHEADER4 0x130 #define RPMAC_TXMACHEADER5 0x134 #define RPMAC_TXDADATYPE 0x138 #define RPMAC_TXRANDOMSEED 0x13c #define RPMAC_CCKPLCPPREAMBLE 0x140 #define RPMAC_CCKPLCPHEADER 0x144 #define RPMAC_CCKCRC16 0x148 #define RPMAC_OFDMRXCRC32OK 0x170 #define RPMAC_OFDMRXCRC32Er 0x174 #define RPMAC_OFDMRXPARITYER 0x178 #define RPMAC_OFDMRXCRC8ER 0x17c #define RPMAC_CCKCRXRC16ER 0x180 #define RPMAC_CCKCRXRC32ER 0x184 #define RPMAC_CCKCRXRC32OK 0x188 #define RPMAC_TXSTATUS 0x18c #define RFPGA0_RFMOD 0x800 #define RFPGA0_TXINFO 0x804 #define RFPGA0_PSDFUNCTION 0x808 #define RFPGA0_TXGAINSTAGE 0x80c #define RFPGA0_RFTIMING1 0x810 #define RFPGA0_RFTIMING2 0x814 #define RFPGA0_XA_HSSIPARAMETER1 0x820 #define RFPGA0_XA_HSSIPARAMETER2 0x824 #define RFPGA0_XB_HSSIPARAMETER1 0x828 #define RFPGA0_XB_HSSIPARAMETER2 0x82c #define RFPGA0_XA_LSSIPARAMETER 0x840 #define RFPGA0_XB_LSSIPARAMETER 0x844 #define RFPGA0_RFWAKEUPPARAMETER 0x850 #define RFPGA0_RFSLEEPUPPARAMETER 0x854 #define RFPGA0_XAB_SWITCHCONTROL 0x858 #define RFPGA0_XCD_SWITCHCONTROL 0x85c #define RFPGA0_XA_RFINTERFACEOE 0x860 #define RFPGA0_XB_RFINTERFACEOE 0x864 #define RFPGA0_XAB_RFINTERFACESW 0x870 #define RFPGA0_XCD_RFINTERFACESW 0x874 #define rFPGA0_XAB_RFPARAMETER 0x878 #define rFPGA0_XCD_RFPARAMETER 0x87c #define RFPGA0_ANALOGPARAMETER1 0x880 #define RFPGA0_ANALOGPARAMETER2 0x884 #define RFPGA0_ANALOGPARAMETER3 0x888 #define RFPGA0_ANALOGPARAMETER4 0x88c #define RFPGA0_XA_LSSIREADBACK 0x8a0 #define RFPGA0_XB_LSSIREADBACK 0x8a4 #define RFPGA0_XC_LSSIREADBACK 0x8a8 #define RFPGA0_XD_LSSIREADBACK 0x8ac #define RFPGA0_PSDREPORT 0x8b4 #define TRANSCEIVEA_HSPI_READBACK 0x8b8 #define TRANSCEIVEB_HSPI_READBACK 0x8bc #define RFPGA0_XAB_RFINTERFACERB 0x8e0 #define RFPGA0_XCD_RFINTERFACERB 0x8e4 #define RFPGA1_RFMOD 0x900 #define RFPGA1_TXBLOCK 0x904 #define RFPGA1_DEBUGSELECT 0x908 #define RFPGA1_TXINFO 0x90c #define RCCK0_SYSTEM 0xa00 #define RCCK0_AFESETTING 0xa04 #define RCCK0_CCA 0xa08 #define RCCK0_RXAGC1 0xa0c #define RCCK0_RXAGC2 0xa10 #define RCCK0_RXHP 0xa14 #define RCCK0_DSPPARAMETER1 0xa18 #define RCCK0_DSPPARAMETER2 0xa1c #define RCCK0_TXFILTER1 0xa20 #define RCCK0_TXFILTER2 0xa24 #define RCCK0_DEBUGPORT 0xa28 #define RCCK0_FALSEALARMREPORT 0xa2c #define RCCK0_TRSSIREPORT 0xa50 #define RCCK0_RXREPORT 0xa54 #define RCCK0_FACOUNTERLOWER 0xa5c #define RCCK0_FACOUNTERUPPER 0xa58 #define ROFDM0_LSTF 0xc00 #define ROFDM0_TRXPATHENABLE 0xc04 #define ROFDM0_TRMUXPAR 0xc08 #define ROFDM0_TRSWISOLATION 0xc0c #define ROFDM0_XARXAFE 0xc10 #define ROFDM0_XARXIQIMBALANCE 0xc14 #define ROFDM0_XBRXAFE 0xc18 #define ROFDM0_XBRXIQIMBALANCE 0xc1c #define ROFDM0_XCRXAFE 0xc20 #define ROFDM0_XCRXIQIMBANLANCE 0xc24 #define ROFDM0_XDRXAFE 0xc28 #define ROFDM0_XDRXIQIMBALANCE 0xc2c #define ROFDM0_RXDETECTOR1 0xc30 #define ROFDM0_RXDETECTOR2 0xc34 #define ROFDM0_RXDETECTOR3 0xc38 #define ROFDM0_RXDETECTOR4 0xc3c #define ROFDM0_RXDSP 0xc40 #define ROFDM0_CFOANDDAGC 0xc44 #define ROFDM0_CCADROPTHRESHOLD 0xc48 #define ROFDM0_ECCATHRESHOLD 0xc4c #define ROFDM0_XAAGCCORE1 0xc50 #define ROFDM0_XAAGCCORE2 0xc54 #define ROFDM0_XBAGCCORE1 0xc58 #define ROFDM0_XBAGCCORE2 0xc5c #define ROFDM0_XCAGCCORE1 0xc60 #define ROFDM0_XCAGCCORE2 0xc64 #define ROFDM0_XDAGCCORE1 0xc68 #define ROFDM0_XDAGCCORE2 0xc6c #define ROFDM0_AGCPARAMETER1 0xc70 #define ROFDM0_AGCPARAMETER2 0xc74 #define ROFDM0_AGCRSSITABLE 0xc78 #define ROFDM0_HTSTFAGC 0xc7c #define ROFDM0_XATXIQIMBALANCE 0xc80 #define ROFDM0_XATXAFE 0xc84 #define ROFDM0_XBTXIQIMBALANCE 0xc88 #define ROFDM0_XBTXAFE 0xc8c #define ROFDM0_XCTXIQIMBALANCE 0xc90 #define ROFDM0_XCTXAFE 0xc94 #define ROFDM0_XDTXIQIMBALANCE 0xc98 #define ROFDM0_XDTXAFE 0xc9c #define ROFDM0_RXIQEXTANTA 0xca0 #define ROFDM0_RXHPPARAMETER 0xce0 #define ROFDM0_TXPSEUDONOISEWGT 0xce4 #define ROFDM0_FRAMESYNC 0xcf0 #define ROFDM0_DFSREPORT 0xcf4 #define ROFDM0_TXCOEFF1 0xca4 #define ROFDM0_TXCOEFF2 0xca8 #define ROFDM0_TXCOEFF3 0xcac #define ROFDM0_TXCOEFF4 0xcb0 #define ROFDM0_TXCOEFF5 0xcb4 #define ROFDM0_TXCOEFF6 0xcb8 #define ROFDM1_LSTF 0xd00 #define ROFDM1_TRXPATHENABLE 0xd04 #define ROFDM1_CF0 0xd08 #define ROFDM1_CSI1 0xd10 #define ROFDM1_SBD 0xd14 #define ROFDM1_CSI2 0xd18 #define ROFDM1_CFOTRACKING 0xd2c #define ROFDM1_TRXMESAURE1 0xd34 #define ROFDM1_INTFDET 0xd3c #define ROFDM1_PSEUDONOISESTATEAB 0xd50 #define ROFDM1_PSEUDONOISESTATECD 0xd54 #define ROFDM1_RXPSEUDONOISEWGT 0xd58 #define ROFDM_PHYCOUNTER1 0xda0 #define ROFDM_PHYCOUNTER2 0xda4 #define ROFDM_PHYCOUNTER3 0xda8 #define ROFDM_SHORTCFOAB 0xdac #define ROFDM_SHORTCFOCD 0xdb0 #define ROFDM_LONGCFOAB 0xdb4 #define ROFDM_LONGCFOCD 0xdb8 #define ROFDM_TAILCF0AB 0xdbc #define ROFDM_TAILCF0CD 0xdc0 #define ROFDM_PWMEASURE1 0xdc4 #define ROFDM_PWMEASURE2 0xdc8 #define ROFDM_BWREPORT 0xdcc #define ROFDM_AGCREPORT 0xdd0 #define ROFDM_RXSNR 0xdd4 #define ROFDM_RXEVMCSI 0xdd8 #define ROFDM_SIGREPORT 0xddc #define RTXAGC_A_RATE18_06 0xe00 #define RTXAGC_A_RATE54_24 0xe04 #define RTXAGC_A_CCK1_MCS32 0xe08 #define RTXAGC_A_MCS03_MCS00 0xe10 #define RTXAGC_A_MCS07_MCS04 0xe14 #define RTXAGC_A_MCS11_MCS08 0xe18 #define RTXAGC_A_MCS15_MCS12 0xe1c #define RTXAGC_B_RATE18_06 0x830 #define RTXAGC_B_RATE54_24 0x834 #define RTXAGC_B_CCK1_55_MCS32 0x838 #define RTXAGC_B_MCS03_MCS00 0x83c #define RTXAGC_B_MCS07_MCS04 0x848 #define RTXAGC_B_MCS11_MCS08 0x84c #define RTXAGC_B_MCS15_MCS12 0x868 #define RTXAGC_B_CCK11_A_CCK2_11 0x86c #define RZEBRA1_HSSIENABLE 0x0 #define RZEBRA1_TRXENABLE1 0x1 #define RZEBRA1_TRXENABLE2 0x2 #define RZEBRA1_AGC 0x4 #define RZEBRA1_CHARGEPUMP 0x5 #define RZEBRA1_CHANNEL 0x7 #define RZEBRA1_TXGAIN 0x8 #define RZEBRA1_TXLPF 0x9 #define RZEBRA1_RXLPF 0xb #define RZEBRA1_RXHPFCORNER 0xc #define RGLOBALCTRL 0 #define RRTL8256_TXLPF 19 #define RRTL8256_RXLPF 11 #define RRTL8258_TXLPF 0x11 #define RRTL8258_RXLPF 0x13 #define RRTL8258_RSSILPF 0xa #define RF_AC 0x00 #define RF_IQADJ_G1 0x01 #define RF_IQADJ_G2 0x02 #define RF_POW_TRSW 0x05 #define RF_GAIN_RX 0x06 #define RF_GAIN_TX 0x07 #define RF_TXM_IDAC 0x08 #define RF_BS_IQGEN 0x0F #define RF_MODE1 0x10 #define RF_MODE2 0x11 #define RF_RX_AGC_HP 0x12 #define RF_TX_AGC 0x13 #define RF_BIAS 0x14 #define RF_IPA 0x15 #define RF_POW_ABILITY 0x17 #define RF_MODE_AG 0x18 #define RRFCHANNEL 0x18 #define RF_CHNLBW 0x18 #define RF_TOP 0x19 #define RF_RX_G1 0x1A #define RF_RX_G2 0x1B #define RF_RX_BB2 0x1C #define RF_RX_BB1 0x1D #define RF_RCK1 0x1E #define RF_RCK2 0x1F #define RF_TX_G1 0x20 #define RF_TX_G2 0x21 #define RF_TX_G3 0x22 #define RF_TX_BB1 0x23 #define RF_T_METER 0x24 #define RF_SYN_G1 0x25 #define RF_SYN_G2 0x26 #define RF_SYN_G3 0x27 #define RF_SYN_G4 0x28 #define RF_SYN_G5 0x29 #define RF_SYN_G6 0x2A #define RF_SYN_G7 0x2B #define RF_SYN_G8 0x2C #define RF_RCK_OS 0x30 #define RF_TXPA_G1 0x31 #define RF_TXPA_G2 0x32 #define RF_TXPA_G3 0x33 #define BBBRESETB 0x100 #define BGLOBALRESETB 0x200 #define BOFDMTXSTART 0x4 #define BCCKTXSTART 0x8 #define BCRC32DEBUG 0x100 #define BPMACLOOPBACK 0x10 #define BTXLSIG 0xffffff #define BOFDMTXRATE 0xf #define BOFDMTXRESERVED 0x10 #define BOFDMTXLENGTH 0x1ffe0 #define BOFDMTXPARITY 0x20000 #define BTXHTSIG1 0xffffff #define BTXHTMCSRATE 0x7f #define BTXHTBW 0x80 #define BTXHTLENGTH 0xffff00 #define BTXHTSIG2 0xffffff #define BTXHTSMOOTHING 0x1 #define BTXHTSOUNDING 0x2 #define BTXHTRESERVED 0x4 #define BTXHTAGGREATION 0x8 #define BTXHTSTBC 0x30 #define BTXHTADVANCECODING 0x40 #define BTXHTSHORTGI 0x80 #define BTXHTNUMBERHT_LTF 0x300 #define BTXHTCRC8 0x3fc00 #define BCOUNTERRESET 0x10000 #define BNUMOFOFDMTX 0xffff #define BNUMOFCCKTX 0xffff0000 #define BTXIDLEINTERVAL 0xffff #define BOFDMSERVICE 0xffff0000 #define BTXMACHEADER 0xffffffff #define BTXDATAINIT 0xff #define BTXHTMODE 0x100 #define BTXDATATYPE 0x30000 #define BTXRANDOMSEED 0xffffffff #define BCCKTXPREAMBLE 0x1 #define BCCKTXSFD 0xffff0000 #define BCCKTXSIG 0xff #define BCCKTXSERVICE 0xff00 #define BCCKLENGTHEXT 0x8000 #define BCCKTXLENGHT 0xffff0000 #define BCCKTXCRC16 0xffff #define BCCKTXSTATUS 0x1 #define BOFDMTXSTATUS 0x2 #define IS_BB_REG_OFFSET_92S(_Offset) \ ((_Offset >= 0x800) && (_Offset <= 0xfff)) #define BRFMOD 0x1 #define BJAPANMODE 0x2 #define BCCKTXSC 0x30 #define BCCKEN 0x1000000 #define BOFDMEN 0x2000000 #define BOFDMRXADCPHASE 0x10000 #define BOFDMTXDACPHASE 0x40000 #define BXATXAGC 0x3f #define BXBTXAGC 0xf00 #define BXCTXAGC 0xf000 #define BXDTXAGC 0xf0000 #define BPASTART 0xf0000000 #define BTRSTART 0x00f00000 #define BRFSTART 0x0000f000 #define BBBSTART 0x000000f0 #define BBBCCKSTART 0x0000000f #define BPAEND 0xf #define BTREND 0x0f000000 #define BRFEND 0x000f0000 #define BCCAMASK 0x000000f0 #define BR2RCCAMASK 0x00000f00 #define BHSSI_R2TDELAY 0xf8000000 #define BHSSI_T2RDELAY 0xf80000 #define BCONTXHSSI 0x400 #define BIGFROMCCK 0x200 #define BAGCADDRESS 0x3f #define BRXHPTX 0x7000 #define BRXHP2RX 0x38000 #define BRXHPCCKINI 0xc0000 #define BAGCTXCODE 0xc00000 #define BAGCRXCODE 0x300000 #define B3WIREDATALENGTH 0x800 #define B3WIREADDREAALENGTH 0x400 #define B3WIRERFPOWERDOWN 0x1 #define B5GPAPEPOLARITY 0x40000000 #define B2GPAPEPOLARITY 0x80000000 #define BRFSW_TXDEFAULTANT 0x3 #define BRFSW_TXOPTIONANT 0x30 #define BRFSW_RXDEFAULTANT 0x300 #define BRFSW_RXOPTIONANT 0x3000 #define BRFSI_3WIREDATA 0x1 #define BRFSI_3WIRECLOCK 0x2 #define BRFSI_3WIRELOAD 0x4 #define BRFSI_3WIRERW 0x8 #define BRFSI_3WIRE 0xf #define BRFSI_RFENV 0x10 #define BRFSI_TRSW 0x20 #define BRFSI_TRSWB 0x40 #define BRFSI_ANTSW 0x100 #define BRFSI_ANTSWB 0x200 #define BRFSI_PAPE 0x400 #define BRFSI_PAPE5G 0x800 #define BBANDSELECT 0x1 #define BHTSIG2_GI 0x80 #define BHTSIG2_SMOOTHING 0x01 #define BHTSIG2_SOUNDING 0x02 #define BHTSIG2_AGGREATON 0x08 #define BHTSIG2_STBC 0x30 #define BHTSIG2_ADVCODING 0x40 #define BHTSIG2_NUMOFHTLTF 0x300 #define BHTSIG2_CRC8 0x3fc #define BHTSIG1_MCS 0x7f #define BHTSIG1_BANDWIDTH 0x80 #define BHTSIG1_HTLENGTH 0xffff #define BLSIG_RATE 0xf #define BLSIG_RESERVED 0x10 #define BLSIG_LENGTH 0x1fffe #define BLSIG_PARITY 0x20 #define BCCKRXPHASE 0x4 #define BLSSIREADADDRESS 0x7f800000 #define BLSSIREADEDGE 0x80000000 #define BLSSIREADBACKDATA 0xfffff #define BLSSIREADOKFLAG 0x1000 #define BCCKSAMPLERATE 0x8 #define BREGULATOR0STANDBY 0x1 #define BREGULATORPLLSTANDBY 0x2 #define BREGULATOR1STANDBY 0x4 #define BPLLPOWERUP 0x8 #define BDPLLPOWERUP 0x10 #define BDA10POWERUP 0x20 #define BAD7POWERUP 0x200 #define BDA6POWERUP 0x2000 #define BXTALPOWERUP 0x4000 #define B40MDCLKPOWERUP 0x8000 #define BDA6DEBUGMODE 0x20000 #define BDA6SWING 0x380000 #define BADCLKPHASE 0x4000000 #define B80MCLKDELAY 0x18000000 #define BAFEWATCHDOGENABLE 0x20000000 #define BXTALCAP01 0xc0000000 #define BXTALCAP23 0x3 #define BXTALCAP92X 0x0f000000 #define BXTALCAP 0x0f000000 #define BINTDIFCLKENABLE 0x400 #define BEXTSIGCLKENABLE 0x800 #define BBANDGAP_MBIAS_POWERUP 0x10000 #define BAD11SH_GAIN 0xc0000 #define BAD11NPUT_RANGE 0x700000 #define BAD110P_CURRENT 0x3800000 #define BLPATH_LOOPBACK 0x4000000 #define BQPATH_LOOPBACK 0x8000000 #define BAFE_LOOPBACK 0x10000000 #define BDA10_SWING 0x7e0 #define BDA10_REVERSE 0x800 #define BDA_CLK_SOURCE 0x1000 #define BDA7INPUT_RANGE 0x6000 #define BDA7_GAIN 0x38000 #define BDA7OUTPUT_CM_MODE 0x40000 #define BDA7INPUT_CM_MODE 0x380000 #define BDA7CURRENT 0xc00000 #define BREGULATOR_ADJUST 0x7000000 #define BAD11POWERUP_ATTX 0x1 #define BDA10PS_ATTX 0x10 #define BAD11POWERUP_ATRX 0x100 #define BDA10PS_ATRX 0x1000 #define BCCKRX_AGC_FORMAT 0x200 #define BPSDFFT_SAMPLE_POINT 0xc000 #define BPSD_AVERAGE_NUM 0x3000 #define BIQPATH_CONTROL 0xc00 #define BPSD_FREQ 0x3ff #define BPSD_ANTENNA_PATH 0x30 #define BPSD_IQ_SWITCH 0x40 #define BPSD_RX_TRIGGER 0x400000 #define BPSD_TX_TRIGGER 0x80000000 #define BPSD_SINE_TONE_SCALE 0x7f000000 #define BPSD_REPORT 0xffff #define BOFDM_TXSC 0x30000000 #define BCCK_TXON 0x1 #define BOFDM_TXON 0x2 #define BDEBUG_PAGE 0xfff #define BDEBUG_ITEM 0xff #define BANTL 0x10 #define BANT_NONHT 0x100 #define BANT_HT1 0x1000 #define BANT_HT2 0x10000 #define BANT_HT1S1 0x100000 #define BANT_NONHTS1 0x1000000 #define BCCK_BBMODE 0x3 #define BCCK_TXPOWERSAVING 0x80 #define BCCK_RXPOWERSAVING 0x40 #define BCCK_SIDEBAND 0x10 #define BCCK_SCRAMBLE 0x8 #define BCCK_ANTDIVERSITY 0x8000 #define BCCK_CARRIER_RECOVERY 0x4000 #define BCCK_TXRATE 0x3000 #define BCCK_DCCANCEL 0x0800 #define BCCK_ISICANCEL 0x0400 #define BCCK_MATCH_FILTER 0x0200 #define BCCK_EQUALIZER 0x0100 #define BCCK_PREAMBLE_DETECT 0x800000 #define BCCK_FAST_FALSECCA 0x400000 #define BCCK_CH_ESTSTART 0x300000 #define BCCK_CCA_COUNT 0x080000 #define BCCK_CS_LIM 0x070000 #define BCCK_BIST_MODE 0x80000000 #define BCCK_CCAMASK 0x40000000 #define BCCK_TX_DAC_PHASE 0x4 #define BCCK_RX_ADC_PHASE 0x20000000 #define BCCKR_CP_MODE 0x0100 #define BCCK_TXDC_OFFSET 0xf0 #define BCCK_RXDC_OFFSET 0xf #define BCCK_CCA_MODE 0xc000 #define BCCK_FALSECS_LIM 0x3f00 #define BCCK_CS_RATIO 0xc00000 #define BCCK_CORGBIT_SEL 0x300000 #define BCCK_PD_LIM 0x0f0000 #define BCCK_NEWCCA 0x80000000 #define BCCK_RXHP_OF_IG 0x8000 #define BCCK_RXIG 0x7f00 #define BCCK_LNA_POLARITY 0x800000 #define BCCK_RX1ST_BAIN 0x7f0000 #define BCCK_RF_EXTEND 0x20000000 #define BCCK_RXAGC_SATLEVEL 0x1f000000 #define BCCK_RXAGC_SATCOUNT 0xe0 #define bCCKRxRFSettle 0x1f #define BCCK_FIXED_RXAGC 0x8000 #define BCCK_ANTENNA_POLARITY 0x2000 #define BCCK_TXFILTER_TYPE 0x0c00 #define BCCK_RXAGC_REPORTTYPE 0x0300 #define BCCK_RXDAGC_EN 0x80000000 #define BCCK_RXDAGC_PERIOD 0x20000000 #define BCCK_RXDAGC_SATLEVEL 0x1f000000 #define BCCK_TIMING_RECOVERY 0x800000 #define BCCK_TXC0 0x3f0000 #define BCCK_TXC1 0x3f000000 #define BCCK_TXC2 0x3f #define BCCK_TXC3 0x3f00 #define BCCK_TXC4 0x3f0000 #define BCCK_TXC5 0x3f000000 #define BCCK_TXC6 0x3f #define BCCK_TXC7 0x3f00 #define BCCK_DEBUGPORT 0xff0000 #define BCCK_DAC_DEBUG 0x0f000000 #define BCCK_FALSEALARM_ENABLE 0x8000 #define BCCK_FALSEALARM_READ 0x4000 #define BCCK_TRSSI 0x7f #define BCCK_RXAGC_REPORT 0xfe #define BCCK_RXREPORT_ANTSEL 0x80000000 #define BCCK_RXREPORT_MFOFF 0x40000000 #define BCCK_RXREPORT_SQLOSS 0x20000000 #define BCCK_RXREPORT_PKTLOSS 0x10000000 #define BCCK_RXREPORT_LOCKEDBIT 0x08000000 #define BCCK_RXREPORT_RATEERROR 0x04000000 #define BCCK_RXREPORT_RXRATE 0x03000000 #define BCCK_RXFA_COUNTER_LOWER 0xff #define BCCK_RXFA_COUNTER_UPPER 0xff000000 #define BCCK_RXHPAGC_START 0xe000 #define BCCK_RXHPAGC_FINAL 0x1c00 #define BCCK_RXFALSEALARM_ENABLE 0x8000 #define BCCK_FACOUNTER_FREEZE 0x4000 #define BCCK_TXPATH_SEL 0x10000000 #define BCCK_DEFAULT_RXPATH 0xc000000 #define BCCK_OPTION_RXPATH 0x3000000 #define BNUM_OFSTF 0x3 #define BSHIFT_L 0xc0 #define BGI_TH 0xc #define BRXPATH_A 0x1 #define BRXPATH_B 0x2 #define BRXPATH_C 0x4 #define BRXPATH_D 0x8 #define BTXPATH_A 0x1 #define BTXPATH_B 0x2 #define BTXPATH_C 0x4 #define BTXPATH_D 0x8 #define BTRSSI_FREQ 0x200 #define BADC_BACKOFF 0x3000 #define BDFIR_BACKOFF 0xc000 #define BTRSSI_LATCH_PHASE 0x10000 #define BRX_LDC_OFFSET 0xff #define BRX_QDC_OFFSET 0xff00 #define BRX_DFIR_MODE 0x1800000 #define BRX_DCNF_TYPE 0xe000000 #define BRXIQIMB_A 0x3ff #define BRXIQIMB_B 0xfc00 #define BRXIQIMB_C 0x3f0000 #define BRXIQIMB_D 0xffc00000 #define BDC_DC_NOTCH 0x60000 #define BRXNB_NOTCH 0x1f000000 #define BPD_TH 0xf #define BPD_TH_OPT2 0xc000 #define BPWED_TH 0x700 #define BIFMF_WIN_L 0x800 #define BPD_OPTION 0x1000 #define BMF_WIN_L 0xe000 #define BBW_SEARCH_L 0x30000 #define BWIN_ENH_L 0xc0000 #define BBW_TH 0x700000 #define BED_TH2 0x3800000 #define BBW_OPTION 0x4000000 #define BRADIO_TH 0x18000000 #define BWINDOW_L 0xe0000000 #define BSBD_OPTION 0x1 #define BFRAME_TH 0x1c #define BFS_OPTION 0x60 #define BDC_SLOPE_CHECK 0x80 #define BFGUARD_COUNTER_DC_L 0xe00 #define BFRAME_WEIGHT_SHORT 0x7000 #define BSUB_TUNE 0xe00000 #define BFRAME_DC_LENGTH 0xe000000 #define BSBD_START_OFFSET 0x30000000 #define BFRAME_TH_2 0x7 #define BFRAME_GI2_TH 0x38 #define BGI2_SYNC_EN 0x40 #define BSARCH_SHORT_EARLY 0x300 #define BSARCH_SHORT_LATE 0xc00 #define BSARCH_GI2_LATE 0x70000 #define BCFOANTSUM 0x1 #define BCFOACC 0x2 #define BCFOSTARTOFFSET 0xc #define BCFOLOOPBACK 0x70 #define BCFOSUMWEIGHT 0x80 #define BDAGCENABLE 0x10000 #define BTXIQIMB_A 0x3ff #define BTXIQIMB_b 0xfc00 #define BTXIQIMB_C 0x3f0000 #define BTXIQIMB_D 0xffc00000 #define BTXIDCOFFSET 0xff #define BTXIQDCOFFSET 0xff00 #define BTXDFIRMODE 0x10000 #define BTXPESUDO_NOISEON 0x4000000 #define BTXPESUDO_NOISE_A 0xff #define BTXPESUDO_NOISE_B 0xff00 #define BTXPESUDO_NOISE_C 0xff0000 #define BTXPESUDO_NOISE_D 0xff000000 #define BCCA_DROPOPTION 0x20000 #define BCCA_DROPTHRES 0xfff00000 #define BEDCCA_H 0xf #define BEDCCA_L 0xf0 #define BLAMBDA_ED 0x300 #define BRX_INITIALGAIN 0x7f #define BRX_ANTDIV_EN 0x80 #define BRX_AGC_ADDRESS_FOR_LNA 0x7f00 #define BRX_HIGHPOWER_FLOW 0x8000 #define BRX_AGC_FREEZE_THRES 0xc0000 #define BRX_FREEZESTEP_AGC1 0x300000 #define BRX_FREEZESTEP_AGC2 0xc00000 #define BRX_FREEZESTEP_AGC3 0x3000000 #define BRX_FREEZESTEP_AGC0 0xc000000 #define BRXRSSI_CMP_EN 0x10000000 #define BRXQUICK_AGCEN 0x20000000 #define BRXAGC_FREEZE_THRES_MODE 0x40000000 #define BRX_OVERFLOW_CHECKTYPE 0x80000000 #define BRX_AGCSHIFT 0x7f #define BTRSW_TRI_ONLY 0x80 #define BPOWER_THRES 0x300 #define BRXAGC_EN 0x1 #define BRXAGC_TOGETHER_EN 0x2 #define BRXAGC_MIN 0x4 #define BRXHP_INI 0x7 #define BRXHP_TRLNA 0x70 #define BRXHP_RSSI 0x700 #define BRXHP_BBP1 0x7000 #define BRXHP_BBP2 0x70000 #define BRXHP_BBP3 0x700000 #define BRSSI_H 0x7f0000 #define BRSSI_GEN 0x7f000000 #define BRXSETTLE_TRSW 0x7 #define BRXSETTLE_LNA 0x38 #define BRXSETTLE_RSSI 0x1c0 #define BRXSETTLE_BBP 0xe00 #define BRXSETTLE_RXHP 0x7000 #define BRXSETTLE_ANTSW_RSSI 0x38000 #define BRXSETTLE_ANTSW 0xc0000 #define BRXPROCESS_TIME_DAGC 0x300000 #define BRXSETTLE_HSSI 0x400000 #define BRXPROCESS_TIME_BBPPW 0x800000 #define BRXANTENNA_POWER_SHIFT 0x3000000 #define BRSSI_TABLE_SELECT 0xc000000 #define BRXHP_FINAL 0x7000000 #define BRXHPSETTLE_BBP 0x7 #define BRXHTSETTLE_HSSI 0x8 #define BRXHTSETTLE_RXHP 0x70 #define BRXHTSETTLE_BBPPW 0x80 #define BRXHTSETTLE_IDLE 0x300 #define BRXHTSETTLE_RESERVED 0x1c00 #define BRXHT_RXHP_EN 0x8000 #define BRXAGC_FREEZE_THRES 0x30000 #define BRXAGC_TOGETHEREN 0x40000 #define BRXHTAGC_MIN 0x80000 #define BRXHTAGC_EN 0x100000 #define BRXHTDAGC_EN 0x200000 #define BRXHT_RXHP_BBP 0x1c00000 #define BRXHT_RXHP_FINAL 0xe0000000 #define BRXPW_RADIO_TH 0x3 #define BRXPW_RADIO_EN 0x4 #define BRXMF_HOLD 0x3800 #define BRXPD_DELAY_TH1 0x38 #define BRXPD_DELAY_TH2 0x1c0 #define BRXPD_DC_COUNT_MAX 0x600 #define BRXPD_DELAY_TH 0x8000 #define BRXPROCESS_DELAY 0xf0000 #define BRXSEARCHRANGE_GI2_EARLY 0x700000 #define BRXFRAME_FUARD_COUNTER_L 0x3800000 #define BRXSGI_GUARD_L 0xc000000 #define BRXSGI_SEARCH_L 0x30000000 #define BRXSGI_TH 0xc0000000 #define BDFSCNT0 0xff #define BDFSCNT1 0xff00 #define BDFSFLAG 0xf0000 #define BMF_WEIGHT_SUM 0x300000 #define BMINIDX_TH 0x7f000000 #define BDAFORMAT 0x40000 #define BTXCH_EMU_ENABLE 0x01000000 #define BTRSW_ISOLATION_A 0x7f #define BTRSW_ISOLATION_B 0x7f00 #define BTRSW_ISOLATION_C 0x7f0000 #define BTRSW_ISOLATION_D 0x7f000000 #define BEXT_LNA_GAIN 0x7c00 #define BSTBC_EN 0x4 #define BANTENNA_MAPPING 0x10 #define BNSS 0x20 #define BCFO_ANTSUM_ID 0x200 #define BPHY_COUNTER_RESET 0x8000000 #define BCFO_REPORT_GET 0x4000000 #define BOFDM_CONTINUE_TX 0x10000000 #define BOFDM_SINGLE_CARRIER 0x20000000 #define BOFDM_SINGLE_TONE 0x40000000 #define BHT_DETECT 0x100 #define BCFOEN 0x10000 #define BCFOVALUE 0xfff00000 #define BSIGTONE_RE 0x3f #define BSIGTONE_IM 0x7f00 #define BCOUNTER_CCA 0xffff #define BCOUNTER_PARITYFAIL 0xffff0000 #define BCOUNTER_RATEILLEGAL 0xffff #define BCOUNTER_CRC8FAIL 0xffff0000 #define BCOUNTER_MCSNOSUPPORT 0xffff #define BCOUNTER_FASTSYNC 0xffff #define BSHORTCFO 0xfff #define BSHORTCFOT_LENGTH 12 #define BSHORTCFOF_LENGTH 11 #define BLONGCFO 0x7ff #define BLONGCFOT_LENGTH 11 #define BLONGCFOF_LENGTH 11 #define BTAILCFO 0x1fff #define BTAILCFOT_LENGTH 13 #define BTAILCFOF_LENGTH 12 #define BNOISE_EN_PWDB 0xffff #define BCC_POWER_DB 0xffff0000 #define BMOISE_PWDB 0xffff #define BPOWERMEAST_LENGTH 10 #define BPOWERMEASF_LENGTH 3 #define BRX_HT_BW 0x1 #define BRXSC 0x6 #define BRX_HT 0x8 #define BNB_INTF_DET_ON 0x1 #define BINTF_WIN_LEN_CFG 0x30 #define BNB_INTF_TH_CFG 0x1c0 #define BRFGAIN 0x3f #define BTABLESEL 0x40 #define BTRSW 0x80 #define BRXSNR_A 0xff #define BRXSNR_B 0xff00 #define BRXSNR_C 0xff0000 #define BRXSNR_D 0xff000000 #define BSNR_EVMT_LENGTH 8 #define BSNR_EVMF_LENGTH 1 #define BCSI1ST 0xff #define BCSI2ND 0xff00 #define BRXEVM1ST 0xff0000 #define BRXEVM2ND 0xff000000 #define BSIGEVM 0xff #define BPWDB 0xff00 #define BSGIEN 0x10000 #define BSFACTOR_QMA1 0xf #define BSFACTOR_QMA2 0xf0 #define BSFACTOR_QMA3 0xf00 #define BSFACTOR_QMA4 0xf000 #define BSFACTOR_QMA5 0xf0000 #define BSFACTOR_QMA6 0xf0000 #define BSFACTOR_QMA7 0xf00000 #define BSFACTOR_QMA8 0xf000000 #define BSFACTOR_QMA9 0xf0000000 #define BCSI_SCHEME 0x100000 #define BNOISE_LVL_TOP_SET 0x3 #define BCHSMOOTH 0x4 #define BCHSMOOTH_CFG1 0x38 #define BCHSMOOTH_CFG2 0x1c0 #define BCHSMOOTH_CFG3 0xe00 #define BCHSMOOTH_CFG4 0x7000 #define BMRCMODE 0x800000 #define BTHEVMCFG 0x7000000 #define BLOOP_FIT_TYPE 0x1 #define BUPD_CFO 0x40 #define BUPD_CFO_OFFDATA 0x80 #define BADV_UPD_CFO 0x100 #define BADV_TIME_CTRL 0x800 #define BUPD_CLKO 0x1000 #define BFC 0x6000 #define BTRACKING_MODE 0x8000 #define BPHCMP_ENABLE 0x10000 #define BUPD_CLKO_LTF 0x20000 #define BCOM_CH_CFO 0x40000 #define BCSI_ESTI_MODE 0x80000 #define BADV_UPD_EQZ 0x100000 #define BUCHCFG 0x7000000 #define BUPDEQZ 0x8000000 #define BRX_PESUDO_NOISE_ON 0x20000000 #define BRX_PESUDO_NOISE_A 0xff #define BRX_PESUDO_NOISE_B 0xff00 #define BRX_PESUDO_NOISE_C 0xff0000 #define BRX_PESUDO_NOISE_D 0xff000000 #define BRX_PESUDO_NOISESTATE_A 0xffff #define BRX_PESUDO_NOISESTATE_B 0xffff0000 #define BRX_PESUDO_NOISESTATE_C 0xffff #define BRX_PESUDO_NOISESTATE_D 0xffff0000 #define BZEBRA1_HSSIENABLE 0x8 #define BZEBRA1_TRXCONTROL 0xc00 #define BZEBRA1_TRXGAINSETTING 0x07f #define BZEBRA1_RXCOUNTER 0xc00 #define BZEBRA1_TXCHANGEPUMP 0x38 #define BZEBRA1_RXCHANGEPUMP 0x7 #define BZEBRA1_CHANNEL_NUM 0xf80 #define BZEBRA1_TXLPFBW 0x400 #define BZEBRA1_RXLPFBW 0x600 #define BRTL8256REG_MODE_CTRL1 0x100 #define BRTL8256REG_MODE_CTRL0 0x40 #define BRTL8256REG_TXLPFBW 0x18 #define BRTL8256REG_RXLPFBW 0x600 #define BRTL8258_TXLPFBW 0xc #define BRTL8258_RXLPFBW 0xc00 #define BRTL8258_RSSILPFBW 0xc0 #define BBYTE0 0x1 #define BBYTE1 0x2 #define BBYTE2 0x4 #define BBYTE3 0x8 #define BWORD0 0x3 #define BWORD1 0xc #define BWORD 0xf #define MASKBYTE0 0xff #define MASKBYTE1 0xff00 #define MASKBYTE2 0xff0000 #define MASKBYTE3 0xff000000 #define MASKHWORD 0xffff0000 #define MASKLWORD 0x0000ffff #define MASKDWORD 0xffffffff #define MASK12BITS 0xfff #define MASKH4BITS 0xf0000000 #define MASKOFDM_D 0xffc00000 #define MASKCCK 0x3f3f3f3f #define MASK4BITS 0x0f #define MASK20BITS 0xfffff #define RFREG_OFFSET_MASK 0xfffff #define BENABLE 0x1 #define BDISABLE 0x0 #define LEFT_ANTENNA 0x0 #define RIGHT_ANTENNA 0x1 #define TCHECK_TXSTATUS 500 #define TUPDATE_RXCOUNTER 100 #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h0000644000175000017500000001763512026211315024563 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_PHY_H__ #define __RTL92C_PHY_H__ #define MAX_PRECMD_CNT 16 #define MAX_RFDEPENDCMD_CNT 16 #define MAX_POSTCMD_CNT 16 #define MAX_DOZE_WAITING_TIMES_9x 64 #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 #define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 #define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 #define APK_CURVE_REG_NUM 4 #define PATH_NUM 2 #define LOOP_LIMIT 5 #define MAX_STALL_TIME 50 #define AntennaDiversityValue 0x80 #define MAX_TXPWR_IDX_NMODE_92S 63 #define Reset_Cnt_Limit 3 #define IQK_ADDA_REG_NUM 16 #define IQK_MAC_REG_NUM 4 #define IQK_DELAY_TIME 1 #define RF90_PATH_MAX 2 #define CT_OFFSET_MAC_ADDR 0X16 #define CT_OFFSET_CCK_TX_PWR_IDX 0x5A #define CT_OFFSET_HT401S_TX_PWR_IDX 0x60 #define CT_OFFSET_HT402S_TX_PWR_IDX_DIF 0x66 #define CT_OFFSET_HT20_TX_PWR_IDX_DIFF 0x69 #define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF 0x6C #define CT_OFFSET_HT40_MAX_PWR_OFFSET 0x6F #define CT_OFFSET_HT20_MAX_PWR_OFFSET 0x72 #define CT_OFFSET_CHANNEL_PLAH 0x75 #define CT_OFFSET_THERMAL_METER 0x78 #define CT_OFFSET_RF_OPTION 0x79 #define CT_OFFSET_VERSION 0x7E #define CT_OFFSET_CUSTOMER_ID 0x7F #define RTL92C_MAX_PATH_NUM 2 enum swchnlcmd_id { CMDID_END, CMDID_SET_TXPOWEROWER_LEVEL, CMDID_BBREGWRITE10, CMDID_WRITEPORT_ULONG, CMDID_WRITEPORT_USHORT, CMDID_WRITEPORT_UCHAR, CMDID_RF_WRITEREG, }; struct swchnlcmd { enum swchnlcmd_id cmdid; u32 para1; u32 para2; u32 msdelay; }; enum hw90_block_e { HW90_BLOCK_MAC = 0, HW90_BLOCK_PHY0 = 1, HW90_BLOCK_PHY1 = 2, HW90_BLOCK_RF = 3, HW90_BLOCK_MAXIMUM = 4, }; enum baseband_config_type { BASEBAND_CONFIG_PHY_REG = 0, BASEBAND_CONFIG_AGC_TAB = 1, }; enum ra_offset_area { RA_OFFSET_LEGACY_OFDM1, RA_OFFSET_LEGACY_OFDM2, RA_OFFSET_HT_OFDM1, RA_OFFSET_HT_OFDM2, RA_OFFSET_HT_OFDM3, RA_OFFSET_HT_OFDM4, RA_OFFSET_HT_CCK, }; enum antenna_path { ANTENNA_NONE, ANTENNA_D, ANTENNA_C, ANTENNA_CD, ANTENNA_B, ANTENNA_BD, ANTENNA_BC, ANTENNA_BCD, ANTENNA_A, ANTENNA_AD, ANTENNA_AC, ANTENNA_ACD, ANTENNA_AB, ANTENNA_ABD, ANTENNA_ABC, ANTENNA_ABCD }; struct r_antenna_select_ofdm { u32 r_tx_antenna:4; u32 r_ant_l:4; u32 r_ant_non_ht:4; u32 r_ant_ht1:4; u32 r_ant_ht2:4; u32 r_ant_ht_s1:4; u32 r_ant_non_ht_s1:4; u32 ofdm_txsc:2; u32 reserved:2; }; struct r_antenna_select_cck { u8 r_cckrx_enable_2:2; u8 r_cckrx_enable:2; u8 r_ccktx_enable:4; }; struct efuse_contents { u8 mac_addr[ETH_ALEN]; u8 cck_tx_power_idx[6]; u8 ht40_1s_tx_power_idx[6]; u8 ht40_2s_tx_power_idx_diff[3]; u8 ht20_tx_power_idx_diff[3]; u8 ofdm_tx_power_idx_diff[3]; u8 ht40_max_power_offset[3]; u8 ht20_max_power_offset[3]; u8 channel_plan; u8 thermal_meter; u8 rf_option[5]; u8 version; u8 oem_id; u8 regulatory; }; struct tx_power_struct { u8 cck[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 ht40_1s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 ht40_2s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 ht20_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 legacy_ht_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 legacy_ht_txpowerdiff; u8 groupht20[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 groupht40[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 pwrgroup_cnt; u32 mcs_original_offset[4][16]; }; bool rtl92c_phy_bb_config(struct ieee80211_hw *hw); u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask); void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data); u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); extern void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); bool rtl92c_phy_mac_config(struct ieee80211_hw *hw); bool rtl92ce_phy_bb_config(struct ieee80211_hw *hw); bool rtl92c_phy_rf_config(struct ieee80211_hw *hw); bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, enum radio_path rfpath); void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel); void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm); void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw); u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw); void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, u16 beaconinterval); void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, u32 rfpath); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); bool rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); void rtl92c_phy_set_io(struct ieee80211_hw *hw); void rtl92c_bb_block_on(struct ieee80211_hw *hw); u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset); u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset); u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask); void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data); void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data); void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data); bool _rtl92ce_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw); bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw); void _rtl92c_phy_set_rf_sleep(struct ieee80211_hw *hw); bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); bool _rtl92ce_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype); bool _rtl92ce_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype); void rtl92ce_phy_set_bw_mode_callback(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c0000644000175000017500000004402212026211315024544 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../ps.h" #include "reg.h" #include "def.h" #include "hw.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "table.h" static bool _rtl92c_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 original_value, readback_value, bitshift; struct rtl_phy *rtlphy = &(rtlpriv->phy); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n", regaddr, rfpath, bitmask); spin_lock(&rtlpriv->locks.rf_lock); if (rtlphy->rf_mode != RF_OP_BY_FW) { original_value = _rtl92c_phy_rf_serial_read(hw, rfpath, regaddr); } else { original_value = _rtl92c_phy_fw_rf_serial_read(hw, rfpath, regaddr); } bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); readback_value = (original_value & bitmask) >> bitshift; spin_unlock(&rtlpriv->locks.rf_lock); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), rfpath(%#x), bitmask(%#x), original_value(%#x)\n", regaddr, rfpath, bitmask, original_value); return readback_value; } bool rtl92c_phy_mac_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool is92c = IS_92C_SERIAL(rtlhal->version); bool rtstatus = _rtl92c_phy_config_mac_with_headerfile(hw); if (is92c) rtl_write_byte(rtlpriv, 0x14, 0x71); return rtstatus; } bool rtl92c_phy_bb_config(struct ieee80211_hw *hw) { bool rtstatus = true; struct rtl_priv *rtlpriv = rtl_priv(hw); u16 regval; u32 regvaldw; u8 reg_hwparafile = 1; _rtl92c_phy_init_bb_rf_register_definition(hw); regval = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, regval | BIT(13) | BIT(0) | BIT(1)); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x83); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL + 1, 0xdb); rtl_write_byte(rtlpriv, REG_RF_CTRL, RF_EN | RF_RSTB | RF_SDMRSTB); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE | FEN_BB_GLB_RSTn | FEN_BBRSTB); rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL + 1, 0x80); regvaldw = rtl_read_dword(rtlpriv, REG_LEDCFG0); rtl_write_dword(rtlpriv, REG_LEDCFG0, regvaldw | BIT(23)); if (reg_hwparafile == 1) rtstatus = _rtl92c_phy_bb8192c_config_parafile(hw); return rtstatus; } void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 original_value, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); spin_lock(&rtlpriv->locks.rf_lock); if (rtlphy->rf_mode != RF_OP_BY_FW) { if (bitmask != RFREG_OFFSET_MASK) { original_value = _rtl92c_phy_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } _rtl92c_phy_rf_serial_write(hw, rfpath, regaddr, data); } else { if (bitmask != RFREG_OFFSET_MASK) { original_value = _rtl92c_phy_fw_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } _rtl92c_phy_fw_rf_serial_write(hw, rfpath, regaddr, data); } spin_unlock(&rtlpriv->locks.rf_lock); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", regaddr, bitmask, data, rfpath); } static bool _rtl92c_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; u32 arraylength; u32 *ptrarray; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read Rtl819XMACPHY_Array\n"); arraylength = MAC_2T_ARRAYLENGTH; ptrarray = RTL8192CEMAC_2T_ARRAY; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Img:RTL8192CEMAC_2T_ARRAY\n"); for (i = 0; i < arraylength; i = i + 2) rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]); return true; } bool _rtl92ce_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype) { int i; u32 *phy_regarray_table; u32 *agctab_array_table; u16 phy_reg_arraylen, agctab_arraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (IS_92C_SERIAL(rtlhal->version)) { agctab_arraylen = AGCTAB_2TARRAYLENGTH; agctab_array_table = RTL8192CEAGCTAB_2TARRAY; phy_reg_arraylen = PHY_REG_2TARRAY_LENGTH; phy_regarray_table = RTL8192CEPHY_REG_2TARRAY; } else { agctab_arraylen = AGCTAB_1TARRAYLENGTH; agctab_array_table = RTL8192CEAGCTAB_1TARRAY; phy_reg_arraylen = PHY_REG_1TARRAY_LENGTH; phy_regarray_table = RTL8192CEPHY_REG_1TARRAY; } if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_arraylen; i = i + 2) { if (phy_regarray_table[i] == 0xfe) mdelay(50); else if (phy_regarray_table[i] == 0xfd) mdelay(5); else if (phy_regarray_table[i] == 0xfc) mdelay(1); else if (phy_regarray_table[i] == 0xfb) udelay(50); else if (phy_regarray_table[i] == 0xfa) udelay(5); else if (phy_regarray_table[i] == 0xf9) udelay(1); rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD, phy_regarray_table[i + 1]); udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The phy_regarray_table[0] is %x Rtl819XPHY_REGArray[1] is %x\n", phy_regarray_table[i], phy_regarray_table[i + 1]); } } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], MASKDWORD, agctab_array_table[i + 1]); udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "The agctab_array_table[0] is %x Rtl819XPHY_REGArray[1] is %x\n", agctab_array_table[i], agctab_array_table[i + 1]); } } return true; } bool _rtl92ce_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype) { struct rtl_priv *rtlpriv = rtl_priv(hw); int i; u32 *phy_regarray_table_pg; u16 phy_regarray_pg_len; phy_regarray_pg_len = PHY_REG_ARRAY_PGLENGTH; phy_regarray_table_pg = RTL8192CEPHY_REG_ARRAY_PG; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray_pg_len; i = i + 3) { if (phy_regarray_table_pg[i] == 0xfe) mdelay(50); else if (phy_regarray_table_pg[i] == 0xfd) mdelay(5); else if (phy_regarray_table_pg[i] == 0xfc) mdelay(1); else if (phy_regarray_table_pg[i] == 0xfb) udelay(50); else if (phy_regarray_table_pg[i] == 0xfa) udelay(5); else if (phy_regarray_table_pg[i] == 0xf9) udelay(1); _rtl92c_store_pwrIndex_diffrate_offset(hw, phy_regarray_table_pg[i], phy_regarray_table_pg[i + 1], phy_regarray_table_pg[i + 2]); } } else { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "configtype != BaseBand_Config_PHY_REG\n"); } return true; } bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath) { int i; u32 *radioa_array_table; u32 *radiob_array_table; u16 radioa_arraylen, radiob_arraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (IS_92C_SERIAL(rtlhal->version)) { radioa_arraylen = RADIOA_2TARRAYLENGTH; radioa_array_table = RTL8192CERADIOA_2TARRAY; radiob_arraylen = RADIOB_2TARRAYLENGTH; radiob_array_table = RTL8192CE_RADIOB_2TARRAY; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_A:RTL8192CERADIOA_2TARRAY\n"); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_B:RTL8192CE_RADIOB_2TARRAY\n"); } else { radioa_arraylen = RADIOA_1TARRAYLENGTH; radioa_array_table = RTL8192CE_RADIOA_1TARRAY; radiob_arraylen = RADIOB_1TARRAYLENGTH; radiob_array_table = RTL8192CE_RADIOB_1TARRAY; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_A:RTL8192CE_RADIOA_1TARRAY\n"); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio_B:RTL8192CE_RADIOB_1TARRAY\n"); } RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Radio No %x\n", rfpath); switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radioa_arraylen; i = i + 2) { if (radioa_array_table[i] == 0xfe) mdelay(50); else if (radioa_array_table[i] == 0xfd) mdelay(5); else if (radioa_array_table[i] == 0xfc) mdelay(1); else if (radioa_array_table[i] == 0xfb) udelay(50); else if (radioa_array_table[i] == 0xfa) udelay(5); else if (radioa_array_table[i] == 0xf9) udelay(1); else { rtl_set_rfreg(hw, rfpath, radioa_array_table[i], RFREG_OFFSET_MASK, radioa_array_table[i + 1]); udelay(1); } } break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { if (radiob_array_table[i] == 0xfe) { mdelay(50); } else if (radiob_array_table[i] == 0xfd) mdelay(5); else if (radiob_array_table[i] == 0xfc) mdelay(1); else if (radiob_array_table[i] == 0xfb) udelay(50); else if (radiob_array_table[i] == 0xfa) udelay(5); else if (radiob_array_table[i] == 0xf9) udelay(1); else { rtl_set_rfreg(hw, rfpath, radiob_array_table[i], RFREG_OFFSET_MASK, radiob_array_table[i + 1]); udelay(1); } } break; case RF90_PATH_C: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; case RF90_PATH_D: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } return true; } void rtl92ce_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u8 reg_bw_opmode; u8 reg_prsr_rsc; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "Switch to %s bandwidth\n", rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ? "20MHz" : "40MHz"); if (is_hal_stop(rtlhal)) { rtlphy->set_bwmode_inprogress = false; return; } reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE); reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2); switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: reg_bw_opmode |= BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); break; case HT_CHANNEL_WIDTH_20_40: reg_bw_opmode &= ~BW_OPMODE_20MHZ; rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); reg_prsr_rsc = (reg_prsr_rsc & 0x90) | (mac->cur_40_prime_sc << 5); rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } switch (rtlphy->current_chan_bw) { case HT_CHANNEL_WIDTH_20: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); break; case HT_CHANNEL_WIDTH_20_40: rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1); rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1); rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCK_SIDEBAND, (mac->cur_40_prime_sc >> 1)); rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 0); rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)), (mac->cur_40_prime_sc == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); break; } rtl92ce_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); rtlphy->set_bwmode_inprogress = false; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); } void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) { u8 tmpreg; u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal; struct rtl_priv *rtlpriv = rtl_priv(hw); tmpreg = rtl_read_byte(rtlpriv, 0xd03); if ((tmpreg & 0x70) != 0) rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F); else rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); if ((tmpreg & 0x70) != 0) { rf_a_mode = rtl_get_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS); if (is2t) rf_b_mode = rtl_get_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, (rf_a_mode & 0x8FFFF) | 0x10000); if (is2t) rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS, (rf_b_mode & 0x8FFFF) | 0x10000); } lc_cal = rtl_get_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS); rtl_set_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS, lc_cal | 0x08000); mdelay(100); if ((tmpreg & 0x70) != 0) { rtl_write_byte(rtlpriv, 0xd03, tmpreg); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, rf_a_mode); if (is2t) rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS, rf_b_mode); } else { rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); } } static void _rtl92ce_phy_set_rf_sleep(struct ieee80211_hw *hw) { u32 u4b_tmp; u8 delay = 5; struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); while (u4b_tmp != 0 && delay > 0) { rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); delay--; } if (delay == 0) { rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "Switch RF timeout !!!\n"); return; } rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22); } static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool bresult = true; u8 i, queue_id; struct rtl8192_tx_ring *ring = NULL; switch (rfpwr_state) { case ERFON:{ if ((ppsc->rfpwr_state == ERFOFF) && RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) { bool rtstatus; u32 InitializeCount = 0; do { InitializeCount++; RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic enable\n"); rtstatus = rtl_ps_enable_nic(hw); } while (!rtstatus && (InitializeCount < 10)); RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "Set ERFON sleeped:%d ms\n", jiffies_to_msecs(jiffies - ppsc-> last_sleep_jiffies)); ppsc->last_awake_jiffies = jiffies; rtl92ce_phy_set_rf_on(hw); } if (mac->link_state == MAC80211_LINKED) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_LINK); } else { rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); } break; } case ERFOFF:{ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "IPS Set eRf nic disable\n"); rtl_ps_disable_nic(hw); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); } else { if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); } else { rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); } } break; } case ERFSLEEP:{ if (ppsc->rfpwr_state == ERFOFF) return false; for (queue_id = 0, i = 0; queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { ring = &pcipriv->dev.tx_ring[queue_id]; if (skb_queue_len(&ring->queue) == 0) { queue_id++; continue; } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "eRf Off/Sleep: %d times TcbBusyQueue[%d] =%d before doze!\n", i + 1, queue_id, skb_queue_len(&ring->queue)); udelay(10); i++; } if (i >= MAX_DOZE_WAITING_TIMES_9x) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "ERFSLEEP: %d times TcbBusyQueue[%d] = %d !\n", MAX_DOZE_WAITING_TIMES_9x, queue_id, skb_queue_len(&ring->queue)); break; } } RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "Set ERFSLEEP awaked:%d ms\n", jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies)); ppsc->last_sleep_jiffies = jiffies; _rtl92ce_phy_set_rf_sleep(hw); break; } default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); bresult = false; break; } if (bresult) ppsc->rfpwr_state = rfpwr_state; return bresult; } bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool bresult = false; if (rfpwr_state == ppsc->rfpwr_state) return bresult; bresult = _rtl92ce_phy_set_rf_power_state(hw, rfpwr_state); return bresult; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/led.h0000644000175000017500000000275212026211315024521 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CE_LED_H__ #define __RTL92CE_LED_H__ void rtl92ce_init_sw_leds(struct ieee80211_hw *hw); void rtl92ce_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92ce_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled); void rtl92ce_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/led.c0000644000175000017500000001022512026211315024506 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "reg.h" #include "led.h" static void _rtl92ce_init_led(struct ieee80211_hw *hw, struct rtl_led *pled, enum rtl_led_pin ledpin) { pled->hw = hw; pled->ledpin = ledpin; pled->ledon = false; } void rtl92ce_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled) { u8 ledcfg; struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) | BIT(5) | BIT(6)); break; case LED_PIN_LED1: rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0x0f) | BIT(5)); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = true; } void rtl92ce_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); u8 ledcfg; RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); switch (pled->ledpin) { case LED_PIN_GPIO0: break; case LED_PIN_LED0: ledcfg &= 0xf0; if (pcipriv->ledctl.led_opendrain) rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(1) | BIT(5) | BIT(6))); else rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3) | BIT(5) | BIT(6))); break; case LED_PIN_LED1: ledcfg &= 0x0f; rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3))); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } pled->ledon = false; } void rtl92ce_init_sw_leds(struct ieee80211_hw *hw) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0); _rtl92ce_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1); } static void _rtl92ce_sw_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); switch (ledaction) { case LED_CTL_POWER_ON: case LED_CTL_LINK: case LED_CTL_NO_LINK: rtl92ce_sw_led_on(hw, pLed0); break; case LED_CTL_POWER_OFF: rtl92ce_sw_led_off(hw, pLed0); break; default: break; } } void rtl92ce_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) && (ledaction == LED_CTL_TX || ledaction == LED_CTL_RX || ledaction == LED_CTL_SITE_SURVEY || ledaction == LED_CTL_LINK || ledaction == LED_CTL_NO_LINK || ledaction == LED_CTL_START_TO_LINK || ledaction == LED_CTL_POWER_ON)) { return; } RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "ledaction %d\n", ledaction); _rtl92ce_sw_led_control(hw, ledaction); } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h0000644000175000017500000000604112026211315024366 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92CE_HW_H__ #define __RTL92CE_HW_H__ static inline u8 _rtl92c_get_chnl_group(u8 chnl) { u8 group; if (chnl < 3) group = 0; else if (chnl < 9) group = 1; else group = 2; return group; } void rtl92ce_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92ce_read_eeprom_info(struct ieee80211_hw *hw); void rtl92ce_interrupt_recognized(struct ieee80211_hw *hw, u32 *p_inta, u32 *p_intb); int rtl92ce_hw_init(struct ieee80211_hw *hw); void rtl92ce_card_disable(struct ieee80211_hw *hw); void rtl92ce_enable_interrupt(struct ieee80211_hw *hw); void rtl92ce_disable_interrupt(struct ieee80211_hw *hw); int rtl92ce_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type); void rtl92ce_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid); void rtl92ce_set_qos(struct ieee80211_hw *hw, int aci); void rtl92ce_set_beacon_related_registers(struct ieee80211_hw *hw); void rtl92ce_set_beacon_interval(struct ieee80211_hw *hw); void rtl92ce_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr); void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92ce_update_hal_rate_tbl(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level); void rtl92ce_update_channel_access_setting(struct ieee80211_hw *hw); bool rtl92ce_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid); void rtl92ce_enable_hw_security_config(struct ieee80211_hw *hw); void rtl92ce_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); void rtl8192ce_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, bool autoload_fail, u8 *hwinfo); void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw); void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw); void rtl92ce_suspend(struct ieee80211_hw *hw); void rtl92ce_resume(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c0000644000175000017500000017457512026211315024403 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../efuse.h" #include "../base.h" #include "../regd.h" #include "../cam.h" #include "../ps.h" #include "../pci.h" #include "reg.h" #include "def.h" #include "phy.h" #include "../rtl8192c/fw_common.h" #include "dm.h" #include "led.h" #include "hw.h" #define LLT_CONFIG 5 static void _rtl92ce_set_bcn_ctrl_reg(struct ieee80211_hw *hw, u8 set_bits, u8 clear_bits) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpci->reg_bcn_ctrl_val |= set_bits; rtlpci->reg_bcn_ctrl_val &= ~clear_bits; rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val); } static void _rtl92ce_stop_tx_beacon(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp1byte; tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6))); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64); tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); tmp1byte &= ~(BIT(0)); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); } static void _rtl92ce_resume_tx_beacon(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp1byte; tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6)); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); tmp1byte |= BIT(0); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); } static void _rtl92ce_enable_bcn_sub_func(struct ieee80211_hw *hw) { _rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(1)); } static void _rtl92ce_disable_bcn_sub_func(struct ieee80211_hw *hw) { _rtl92ce_set_bcn_ctrl_reg(hw, BIT(1), 0); } void rtl92ce_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); switch (variable) { case HW_VAR_RCR: *((u32 *) (val)) = rtlpci->receive_config; break; case HW_VAR_RF_STATE: *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state; break; case HW_VAR_FWLPS_RF_ON:{ enum rf_pwrstate rfState; u32 val_rcr; rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE, (u8 *) (&rfState)); if (rfState == ERFOFF) { *((bool *) (val)) = true; } else { val_rcr = rtl_read_dword(rtlpriv, REG_RCR); val_rcr &= 0x00070000; if (val_rcr) *((bool *) (val)) = false; else *((bool *) (val)) = true; } break; } case HW_VAR_FW_PSMODE_STATUS: *((bool *) (val)) = ppsc->fw_current_inpsmode; break; case HW_VAR_CORRECT_TSF:{ u64 tsf; u32 *ptsf_low = (u32 *)&tsf; u32 *ptsf_high = ((u32 *)&tsf) + 1; *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4)); *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR); *((u64 *) (val)) = tsf; break; } default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); u8 idx; switch (variable) { case HW_VAR_ETHER_ADDR:{ for (idx = 0; idx < ETH_ALEN; idx++) { rtl_write_byte(rtlpriv, (REG_MACID + idx), val[idx]); } break; } case HW_VAR_BASIC_RATE:{ u16 rate_cfg = ((u16 *) val)[0]; u8 rate_index = 0; rate_cfg &= 0x15f; rate_cfg |= 0x01; rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff); rtl_write_byte(rtlpriv, REG_RRSR + 1, (rate_cfg >> 8) & 0xff); while (rate_cfg > 0x1) { rate_cfg = (rate_cfg >> 1); rate_index++; } rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, rate_index); break; } case HW_VAR_BSSID:{ for (idx = 0; idx < ETH_ALEN; idx++) { rtl_write_byte(rtlpriv, (REG_BSSID + idx), val[idx]); } break; } case HW_VAR_SIFS:{ rtl_write_byte(rtlpriv, REG_SIFS_CTX + 1, val[0]); rtl_write_byte(rtlpriv, REG_SIFS_TRX + 1, val[1]); rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]); rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]); if (!mac->ht_enable) rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, 0x0e0e); else rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, *((u16 *) val)); break; } case HW_VAR_SLOT_TIME:{ u8 e_aci; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "HW_VAR_SLOT_TIME %x\n", val[0]); rtl_write_byte(rtlpriv, REG_SLOT, val[0]); for (e_aci = 0; e_aci < AC_MAX; e_aci++) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, &e_aci); } break; } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; u8 short_preamble = (bool)*val; reg_tmp = (mac->cur_40_prime_sc) << 5; if (short_preamble) reg_tmp |= 0x80; rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_tmp); break; } case HW_VAR_AMPDU_MIN_SPACE:{ u8 min_spacing_to_set; u8 sec_min_space; min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { sec_min_space = 0; if (min_spacing_to_set < sec_min_space) min_spacing_to_set = sec_min_space; mac->min_space_cfg = ((mac->min_space_cfg & 0xf8) | min_spacing_to_set); *val = min_spacing_to_set; RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, mac->min_space_cfg); } break; } case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; density_to_set = *val; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_SHORTGI_DENSITY: %#x\n", mac->min_space_cfg); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, mac->min_space_cfg); break; } case HW_VAR_AMPDU_FACTOR:{ u8 regtoset_normal[4] = {0x41, 0xa8, 0x72, 0xb9}; u8 regtoset_bt[4] = {0x31, 0x74, 0x42, 0x97}; u8 factor_toset; u8 *p_regtoset = NULL; u8 index = 0; if ((rtlpcipriv->bt_coexist.bt_coexistence) && (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) p_regtoset = regtoset_bt; else p_regtoset = regtoset_normal; factor_toset = *(val); if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) factor_toset = 0xf; for (index = 0; index < 4; index++) { if ((p_regtoset[index] & 0xf0) > (factor_toset << 4)) p_regtoset[index] = (p_regtoset[index] & 0x0f) | (factor_toset << 4); if ((p_regtoset[index] & 0x0f) > factor_toset) p_regtoset[index] = (p_regtoset[index] & 0xf0) | (factor_toset); rtl_write_byte(rtlpriv, (REG_AGGLEN_LMT + index), p_regtoset[index]); } RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, "Set HW_VAR_AMPDU_FACTOR: %#x\n", factor_toset); } break; } case HW_VAR_AC_PARAM:{ u8 e_aci = *(val); rtl92c_dm_init_edca_turbo(hw); if (rtlpci->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, (&e_aci)); break; } case HW_VAR_ACM_CTRL:{ u8 e_aci = *(val); union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL); acm_ctrl = acm_ctrl | ((rtlpci->acm_method == 2) ? 0x0 : 0x1); if (acm) { switch (e_aci) { case AC0_BE: acm_ctrl |= AcmHw_BeqEn; break; case AC2_VI: acm_ctrl |= AcmHw_ViqEn; break; case AC3_VO: acm_ctrl |= AcmHw_VoqEn; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "HW_VAR_ACM_CTRL acm set failed: eACI is %d\n", acm); break; } } else { switch (e_aci) { case AC0_BE: acm_ctrl &= (~AcmHw_BeqEn); break; case AC2_VI: acm_ctrl &= (~AcmHw_ViqEn); break; case AC3_VO: acm_ctrl &= (~AcmHw_BeqEn); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE, "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] Write 0x%X\n", acm_ctrl); rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl); break; } case HW_VAR_RCR:{ rtl_write_dword(rtlpriv, REG_RCR, ((u32 *) (val))[0]); rtlpci->receive_config = ((u32 *) (val))[0]; break; } case HW_VAR_RETRY_LIMIT:{ u8 retry_limit = val[0]; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | retry_limit << RETRY_LIMIT_LONG_SHIFT); break; } case HW_VAR_DUAL_TSF_RST: rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1))); break; case HW_VAR_EFUSE_BYTES: rtlefuse->efuse_usedbytes = *((u16 *) val); break; case HW_VAR_EFUSE_USAGE: rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl92c_phy_set_io_cmd(hw, (*(enum io_type *)val)); break; case HW_VAR_WPA_CONFIG: rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_SET_RPWM:{ u8 rpwm_val; rpwm_val = rtl_read_byte(rtlpriv, REG_PCIE_HRPWM); udelay(1); if (rpwm_val & BIT(7)) { rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val); } else { rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val | BIT(7)); } break; } case HW_VAR_H2C_FW_PWRMODE:{ u8 psmode = *val; if ((psmode != FW_PS_ACTIVE_MODE) && (!IS_92C_SERIAL(rtlhal->version))) { rtl92c_dm_rf_saving(hw, true); } rtl92c_set_fw_pwrmode_cmd(hw, *val); break; } case HW_VAR_FW_PSMODE_STATUS: ppsc->fw_current_inpsmode = *((bool *) val); break; case HW_VAR_H2C_FW_JOINBSSRPT:{ u8 mstatus = *val; u8 tmp_regcr, tmp_reg422; bool recover = false; if (mstatus == RT_MEDIA_CONNECT) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AID, NULL); tmp_regcr = rtl_read_byte(rtlpriv, REG_CR + 1); rtl_write_byte(rtlpriv, REG_CR + 1, (tmp_regcr | BIT(0))); _rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(3)); _rtl92ce_set_bcn_ctrl_reg(hw, BIT(4), 0); tmp_reg422 = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); if (tmp_reg422 & BIT(6)) recover = true; rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp_reg422 & (~BIT(6))); rtl92c_set_fw_rsvdpagepkt(hw, 0); _rtl92ce_set_bcn_ctrl_reg(hw, BIT(3), 0); _rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(4)); if (recover) { rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp_reg422); } rtl_write_byte(rtlpriv, REG_CR + 1, (tmp_regcr & ~(BIT(0)))); } rtl92c_set_fw_joinbss_report_cmd(hw, *val); break; } case HW_VAR_AID:{ u16 u2btmp; u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT); u2btmp &= 0xC000; rtl_write_word(rtlpriv, REG_BCN_PSR_RPT, (u2btmp | mac->assoc_id)); break; } case HW_VAR_CORRECT_TSF:{ u8 btype_ibss = val[0]; if (btype_ibss) _rtl92ce_stop_tx_beacon(hw); _rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(3)); rtl_write_dword(rtlpriv, REG_TSFTR, (u32) (mac->tsf & 0xffffffff)); rtl_write_dword(rtlpriv, REG_TSFTR + 4, (u32) ((mac->tsf >> 32) & 0xffffffff)); _rtl92ce_set_bcn_ctrl_reg(hw, BIT(3), 0); if (btype_ibss) _rtl92ce_resume_tx_beacon(hw); break; } default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } static bool _rtl92ce_llt_write(struct ieee80211_hw *hw, u32 address, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); bool status = true; long count = 0; u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) | _LLT_OP(_LLT_WRITE_ACCESS); rtl_write_dword(rtlpriv, REG_LLT_INIT, value); do { value = rtl_read_dword(rtlpriv, REG_LLT_INIT); if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value)) break; if (count > POLLING_LLT_THRESHOLD) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Failed to polling write LLT done at address %d!\n", address); status = false; break; } } while (++count); return status; } static bool _rtl92ce_llt_table_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); unsigned short i; u8 txpktbuf_bndy; u8 maxPage; bool status; #if LLT_CONFIG == 1 maxPage = 255; txpktbuf_bndy = 252; #elif LLT_CONFIG == 2 maxPage = 127; txpktbuf_bndy = 124; #elif LLT_CONFIG == 3 maxPage = 255; txpktbuf_bndy = 174; #elif LLT_CONFIG == 4 maxPage = 255; txpktbuf_bndy = 246; #elif LLT_CONFIG == 5 maxPage = 255; txpktbuf_bndy = 246; #endif #if LLT_CONFIG == 1 rtl_write_byte(rtlpriv, REG_RQPN_NPQ, 0x1c); rtl_write_dword(rtlpriv, REG_RQPN, 0x80a71c1c); #elif LLT_CONFIG == 2 rtl_write_dword(rtlpriv, REG_RQPN, 0x845B1010); #elif LLT_CONFIG == 3 rtl_write_dword(rtlpriv, REG_RQPN, 0x84838484); #elif LLT_CONFIG == 4 rtl_write_dword(rtlpriv, REG_RQPN, 0x80bd1c1c); #elif LLT_CONFIG == 5 rtl_write_word(rtlpriv, REG_RQPN_NPQ, 0x0000); rtl_write_dword(rtlpriv, REG_RQPN, 0x80b01c29); #endif rtl_write_dword(rtlpriv, REG_TRXFF_BNDY, (0x27FF0000 | txpktbuf_bndy)); rtl_write_byte(rtlpriv, REG_TDECTRL + 1, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy); rtl_write_byte(rtlpriv, 0x45D, txpktbuf_bndy); rtl_write_byte(rtlpriv, REG_PBP, 0x11); rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, 0x4); for (i = 0; i < (txpktbuf_bndy - 1); i++) { status = _rtl92ce_llt_write(hw, i, i + 1); if (true != status) return status; } status = _rtl92ce_llt_write(hw, (txpktbuf_bndy - 1), 0xFF); if (true != status) return status; for (i = txpktbuf_bndy; i < maxPage; i++) { status = _rtl92ce_llt_write(hw, i, (i + 1)); if (true != status) return status; } status = _rtl92ce_llt_write(hw, maxPage, txpktbuf_bndy); if (true != status) return status; return true; } static void _rtl92ce_gen_refresh_led_state(struct ieee80211_hw *hw) { struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); if (rtlpci->up_first_time) return; if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) rtl92ce_sw_led_on(hw, pLed0); else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT) rtl92ce_sw_led_on(hw, pLed0); else rtl92ce_sw_led_off(hw, pLed0); } static bool _rtl92ce_init_mac(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); unsigned char bytetmp; unsigned short wordtmp; u16 retry; rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00); if (rtlpcipriv->bt_coexist.bt_coexistence) { u32 value32; value32 = rtl_read_dword(rtlpriv, REG_APS_FSMCO); value32 |= (SOP_ABG | SOP_AMB | XOP_BTCK); rtl_write_dword(rtlpriv, REG_APS_FSMCO, value32); } rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b); rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL, 0x0F); if (rtlpcipriv->bt_coexist.bt_coexistence) { u32 u4b_tmp = rtl_read_dword(rtlpriv, REG_AFE_XTAL_CTRL); u4b_tmp &= (~0x00024800); rtl_write_dword(rtlpriv, REG_AFE_XTAL_CTRL, u4b_tmp); } bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1) | BIT(0); udelay(2); rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, bytetmp); udelay(2); bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1); udelay(2); retry = 0; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "reg0xec:%x:%x\n", rtl_read_dword(rtlpriv, 0xEC), bytetmp); while ((bytetmp & BIT(0)) && retry < 1000) { retry++; udelay(50); bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "reg0xec:%x:%x\n", rtl_read_dword(rtlpriv, 0xEC), bytetmp); udelay(50); } rtl_write_word(rtlpriv, REG_APS_FSMCO, 0x1012); rtl_write_byte(rtlpriv, REG_SYS_ISO_CTRL + 1, 0x82); udelay(2); if (rtlpcipriv->bt_coexist.bt_coexistence) { bytetmp = rtl_read_byte(rtlpriv, REG_AFE_XTAL_CTRL+2) & 0xfd; rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL+2, bytetmp); } rtl_write_word(rtlpriv, REG_CR, 0x2ff); if (!_rtl92ce_llt_table_init(hw)) return false; rtl_write_dword(rtlpriv, REG_HISR, 0xffffffff); rtl_write_byte(rtlpriv, REG_HISRE, 0xff); rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, 0x27ff); wordtmp = rtl_read_word(rtlpriv, REG_TRXDMA_CTRL); wordtmp &= 0xf; wordtmp |= 0xF771; rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, wordtmp); rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 1, 0x1F); rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); rtl_write_dword(rtlpriv, REG_TCR, rtlpci->transmit_config); rtl_write_byte(rtlpriv, 0x4d0, 0x0); rtl_write_dword(rtlpriv, REG_BCNQ_DESA, ((u64) rtlpci->tx_ring[BEACON_QUEUE].dma) & DMA_BIT_MASK(32)); rtl_write_dword(rtlpriv, REG_MGQ_DESA, (u64) rtlpci->tx_ring[MGNT_QUEUE].dma & DMA_BIT_MASK(32)); rtl_write_dword(rtlpriv, REG_VOQ_DESA, (u64) rtlpci->tx_ring[VO_QUEUE].dma & DMA_BIT_MASK(32)); rtl_write_dword(rtlpriv, REG_VIQ_DESA, (u64) rtlpci->tx_ring[VI_QUEUE].dma & DMA_BIT_MASK(32)); rtl_write_dword(rtlpriv, REG_BEQ_DESA, (u64) rtlpci->tx_ring[BE_QUEUE].dma & DMA_BIT_MASK(32)); rtl_write_dword(rtlpriv, REG_BKQ_DESA, (u64) rtlpci->tx_ring[BK_QUEUE].dma & DMA_BIT_MASK(32)); rtl_write_dword(rtlpriv, REG_HQ_DESA, (u64) rtlpci->tx_ring[HIGH_QUEUE].dma & DMA_BIT_MASK(32)); rtl_write_dword(rtlpriv, REG_RX_DESA, (u64) rtlpci->rx_ring[RX_MPDU_QUEUE].dma & DMA_BIT_MASK(32)); if (IS_92C_SERIAL(rtlhal->version)) rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 3, 0x77); else rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 3, 0x22); rtl_write_dword(rtlpriv, REG_INT_MIG, 0); bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); rtl_write_byte(rtlpriv, REG_APSD_CTRL, bytetmp & ~BIT(6)); do { retry++; bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); } while ((retry < 200) && (bytetmp & BIT(7))); _rtl92ce_gen_refresh_led_state(hw); rtl_write_dword(rtlpriv, REG_MCUTST_1, 0x0); return true; } static void _rtl92ce_hw_configure(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); u8 reg_bw_opmode; u32 reg_prsr; reg_bw_opmode = BW_OPMODE_20MHZ; reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG; rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8); rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); rtl_write_dword(rtlpriv, REG_RRSR, reg_prsr); rtl_write_byte(rtlpriv, REG_SLOT, 0x09); rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, 0x0); rtl_write_word(rtlpriv, REG_FWHW_TXQ_CTRL, 0x1F80); rtl_write_word(rtlpriv, REG_RL, 0x0707); rtl_write_dword(rtlpriv, REG_BAR_MODE_CTRL, 0x02012802); rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF); rtl_write_dword(rtlpriv, REG_DARFRC, 0x01000000); rtl_write_dword(rtlpriv, REG_DARFRC + 4, 0x07060504); rtl_write_dword(rtlpriv, REG_RARFRC, 0x01000000); rtl_write_dword(rtlpriv, REG_RARFRC + 4, 0x07060504); if ((rtlpcipriv->bt_coexist.bt_coexistence) && (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0x97427431); else rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0xb972a841); rtl_write_byte(rtlpriv, REG_ATIMWND, 0x2); rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0xff); rtlpci->reg_bcn_ctrl_val = 0x1f; rtl_write_byte(rtlpriv, REG_BCN_CTRL, rtlpci->reg_bcn_ctrl_val); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); rtl_write_byte(rtlpriv, REG_PIFS, 0x1C); rtl_write_byte(rtlpriv, REG_AGGR_BREAK_TIME, 0x16); if ((rtlpcipriv->bt_coexist.bt_coexistence) && (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) { rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020); rtl_write_word(rtlpriv, REG_PROT_MODE_CTRL, 0x0402); } else { rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020); rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020); } if ((rtlpcipriv->bt_coexist.bt_coexistence) && (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) rtl_write_dword(rtlpriv, REG_FAST_EDCA_CTRL, 0x03086666); else rtl_write_dword(rtlpriv, REG_FAST_EDCA_CTRL, 0x086666); rtl_write_byte(rtlpriv, REG_ACKTO, 0x40); rtl_write_word(rtlpriv, REG_SPEC_SIFS, 0x1010); rtl_write_word(rtlpriv, REG_MAC_SPEC_SIFS, 0x1010); rtl_write_word(rtlpriv, REG_SIFS_CTX, 0x1010); rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x1010); rtl_write_dword(rtlpriv, REG_MAR, 0xffffffff); rtl_write_dword(rtlpriv, REG_MAR + 4, 0xffffffff); } static void _rtl92ce_enable_aspm_back_door(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); rtl_write_byte(rtlpriv, 0x34b, 0x93); rtl_write_word(rtlpriv, 0x350, 0x870c); rtl_write_byte(rtlpriv, 0x352, 0x1); if (ppsc->support_backdoor) rtl_write_byte(rtlpriv, 0x349, 0x1b); else rtl_write_byte(rtlpriv, 0x349, 0x03); rtl_write_word(rtlpriv, 0x350, 0x2718); rtl_write_byte(rtlpriv, 0x352, 0x1); } void rtl92ce_enable_hw_security_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 sec_reg_value; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n", rtlpriv->sec.pairwise_enc_algorithm, rtlpriv->sec.group_enc_algorithm); if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "not open hw encryption\n"); return; } sec_reg_value = SCR_TxEncEnable | SCR_RxDecEnable; if (rtlpriv->sec.use_defaultkey) { sec_reg_value |= SCR_TxUseDK; sec_reg_value |= SCR_RxUseDK; } sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK); rtl_write_byte(rtlpriv, REG_CR + 1, 0x02); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The SECR-value %x\n", sec_reg_value); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); } int rtl92ce_hw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); static bool iqk_initialized; /* initialized to false */ bool rtstatus = true; bool is92c; int err; u8 tmp_u1b; rtlpci->being_init_adapter = true; rtlpriv->intf_ops->disable_aspm(hw); rtstatus = _rtl92ce_init_mac(hw); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n"); err = 1; return err; } err = rtl92c_download_fw(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now..\n"); err = 1; return err; } rtlhal->last_hmeboxnum = 0; rtl92c_phy_mac_config(hw); rtl92c_phy_bb_config(hw); rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; rtl92c_phy_rf_config(hw); rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0, RF_CHNLBW, RFREG_OFFSET_MASK); rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1, RF_CHNLBW, RFREG_OFFSET_MASK); rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); _rtl92ce_hw_configure(hw); rtl_cam_reset_all_entry(hw); rtl92ce_enable_hw_security_config(hw); ppsc->rfpwr_state = ERFON; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr); _rtl92ce_enable_aspm_back_door(hw); rtlpriv->intf_ops->enable_aspm(hw); rtl8192ce_bt_hw_init(hw); if (ppsc->rfpwr_state == ERFON) { rtl92c_phy_set_rfpath_switch(hw, 1); if (iqk_initialized) { rtl92c_phy_iq_calibrate(hw, true); } else { rtl92c_phy_iq_calibrate(hw, false); iqk_initialized = true; } rtl92c_dm_check_txpower_tracking(hw); rtl92c_phy_lc_calibrate(hw); } is92c = IS_92C_SERIAL(rtlhal->version); tmp_u1b = efuse_read_1byte(hw, 0x1FA); if (!(tmp_u1b & BIT(0))) { rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0F, 0x05); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "PA BIAS path A\n"); } if (!(tmp_u1b & BIT(1)) && is92c) { rtl_set_rfreg(hw, RF90_PATH_B, 0x15, 0x0F, 0x05); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "PA BIAS path B\n"); } if (!(tmp_u1b & BIT(4))) { tmp_u1b = rtl_read_byte(rtlpriv, 0x16); tmp_u1b &= 0x0F; rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x80); udelay(10); rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x90); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "under 1.5V\n"); } rtl92c_dm_init(hw); rtlpci->being_init_adapter = false; return err; } static enum version_8192c _rtl92ce_read_chip_version(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); enum version_8192c version = VERSION_UNKNOWN; u32 value32; const char *versionid; value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG); if (value32 & TRP_VAUX_EN) { version = (value32 & TYPE_ID) ? VERSION_A_CHIP_92C : VERSION_A_CHIP_88C; } else { version = (enum version_8192c) (CHIP_VER_B | ((value32 & TYPE_ID) ? CHIP_92C_BITMASK : 0) | ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0)); if ((!IS_CHIP_VENDOR_UMC(version)) && (value32 & CHIP_VER_RTL_MASK)) { version = (enum version_8192c)(version | ((((value32 & CHIP_VER_RTL_MASK) == BIT(12)) ? CHIP_VENDOR_UMC_B_CUT : CHIP_UNKNOWN) | CHIP_VENDOR_UMC)); } } switch (version) { case VERSION_B_CHIP_92C: versionid = "B_CHIP_92C"; break; case VERSION_B_CHIP_88C: versionid = "B_CHIP_88C"; break; case VERSION_A_CHIP_92C: versionid = "A_CHIP_92C"; break; case VERSION_A_CHIP_88C: versionid = "A_CHIP_88C"; break; default: versionid = "Unknown. Bug?"; break; } RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Chip Version ID: %s\n", versionid); switch (version & 0x3) { case CHIP_88C: rtlphy->rf_type = RF_1T1R; break; case CHIP_92C: rtlphy->rf_type = RF_2T2R; break; case CHIP_92C_1T2R: rtlphy->rf_type = RF_1T2R; break; default: rtlphy->rf_type = RF_1T1R; RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ERROR RF_Type is set!!\n"); break; } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Chip RF Type: %s\n", rtlphy->rf_type == RF_2T2R ? "RF_2T2R" : "RF_1T1R"); return version; } static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bt_msr = rtl_read_byte(rtlpriv, MSR); enum led_ctl_mode ledaction = LED_CTL_NO_LINK; bt_msr &= 0xfc; if (type == NL80211_IFTYPE_UNSPECIFIED || type == NL80211_IFTYPE_STATION) { _rtl92ce_stop_tx_beacon(hw); _rtl92ce_enable_bcn_sub_func(hw); } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP) { _rtl92ce_resume_tx_beacon(hw); _rtl92ce_disable_bcn_sub_func(hw); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Set HW_VAR_MEDIA_STATUS: No such media status(%x)\n", type); } switch (type) { case NL80211_IFTYPE_UNSPECIFIED: bt_msr |= MSR_NOLINK; ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: bt_msr |= MSR_ADHOC; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: bt_msr |= MSR_INFRA; ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: bt_msr |= MSR_AP; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to AP!\n"); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Network type %d not supported!\n", type); return 1; break; } rtl_write_byte(rtlpriv, (MSR), bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); if ((bt_msr & 0xfc) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); return 0; } void rtl92ce_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR); if (rtlpriv->psc.rfpwr_state != ERFON) return; if (check_bssid) { reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *) (®_rcr)); _rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(4)); } else if (!check_bssid) { reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN)); _rtl92ce_set_bcn_ctrl_reg(hw, BIT(4), 0); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *) (®_rcr)); } } int rtl92ce_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (_rtl92ce_set_media_status(hw, type)) return -EOPNOTSUPP; if (rtlpriv->mac80211.link_state == MAC80211_LINKED) { if (type != NL80211_IFTYPE_AP) rtl92ce_set_check_bssid(hw, true); } else { rtl92ce_set_check_bssid(hw, false); } return 0; } /* don't set REG_EDCA_BE_PARAM here because mac80211 will send pkt when scan */ void rtl92ce_set_qos(struct ieee80211_hw *hw, int aci) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl92c_dm_init_edca_turbo(hw); switch (aci) { case AC1_BK: rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f); break; case AC0_BE: /* rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, u4b_ac_param); */ break; case AC2_VI: rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322); break; case AC3_VO: rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222); break; default: RT_ASSERT(false, "invalid aci: %d !\n", aci); break; } } void rtl92ce_enable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF); } void rtl92ce_disable_interrupt(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED); rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED); synchronize_irq(rtlpci->pdev->irq); } static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); u8 u1b_tmp; u32 u4b_tmp; rtlpriv->intf_ops->enable_aspm(hw); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE0); if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) rtl92c_firmware_selfreset(hw); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, 0x51); rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00000000); u1b_tmp = rtl_read_byte(rtlpriv, REG_GPIO_PIN_CTRL); if ((rtlpcipriv->bt_coexist.bt_coexistence) && ((rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) || (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC8))) { rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00F30000 | (u1b_tmp << 8)); } else { rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x00FF0000 | (u1b_tmp << 8)); } rtl_write_word(rtlpriv, REG_GPIO_IO_SEL, 0x0790); rtl_write_word(rtlpriv, REG_LEDCFG0, 0x8080); rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, 0x80); rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x23); if (rtlpcipriv->bt_coexist.bt_coexistence) { u4b_tmp = rtl_read_dword(rtlpriv, REG_AFE_XTAL_CTRL); u4b_tmp |= 0x03824800; rtl_write_dword(rtlpriv, REG_AFE_XTAL_CTRL, u4b_tmp); } else { rtl_write_dword(rtlpriv, REG_AFE_XTAL_CTRL, 0x0e); } rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0e); rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, 0x10); } void rtl92ce_card_disable(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); enum nl80211_iftype opmode; mac->link_state = MAC80211_NOLINK; opmode = NL80211_IFTYPE_UNSPECIFIED; _rtl92ce_set_media_status(hw, opmode); if (rtlpci->driver_is_goingto_unload || ppsc->rfoff_reason > RF_CHANGE_BY_PS) rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); _rtl92ce_poweroff_adapter(hw); } void rtl92ce_interrupt_recognized(struct ieee80211_hw *hw, u32 *p_inta, u32 *p_intb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); *p_inta = rtl_read_dword(rtlpriv, ISR) & rtlpci->irq_mask[0]; rtl_write_dword(rtlpriv, ISR, *p_inta); /* * *p_intb = rtl_read_dword(rtlpriv, REG_HISRE) & rtlpci->irq_mask[1]; * rtl_write_dword(rtlpriv, ISR + 4, *p_intb); */ } void rtl92ce_set_beacon_related_registers(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcn_interval, atim_window; bcn_interval = mac->beacon_interval; atim_window = 2; /*FIX MERGE */ rtl92ce_disable_interrupt(hw); rtl_write_word(rtlpriv, REG_ATIMWND, atim_window); rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f); rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x18); rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x18); rtl_write_byte(rtlpriv, 0x606, 0x30); rtl92ce_enable_interrupt(hw); } void rtl92ce_set_beacon_interval(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 bcn_interval = mac->beacon_interval; RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG, "beacon_interval:%d\n", bcn_interval); rtl92ce_disable_interrupt(hw); rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); rtl92ce_enable_interrupt(hw); } void rtl92ce_update_interrupt_mask(struct ieee80211_hw *hw, u32 add_msr, u32 rm_msr) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, "add_msr:%x, rm_msr:%x\n", add_msr, rm_msr); if (add_msr) rtlpci->irq_mask[0] |= add_msr; if (rm_msr) rtlpci->irq_mask[0] &= (~rm_msr); rtl92ce_disable_interrupt(hw); rtl92ce_enable_interrupt(hw); } static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, bool autoload_fail, u8 *hwinfo) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 rf_path, index, tempval; u16 i; for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 3; i++) { if (!autoload_fail) { rtlefuse-> eeprom_chnlarea_txpwr_cck[rf_path][i] = hwinfo[EEPROM_TXPOWERCCK + rf_path * 3 + i]; rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][i] = hwinfo[EEPROM_TXPOWERHT40_1S + rf_path * 3 + i]; } else { rtlefuse-> eeprom_chnlarea_txpwr_cck[rf_path][i] = EEPROM_DEFAULT_TXPOWERLEVEL; rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][i] = EEPROM_DEFAULT_TXPOWERLEVEL; } } } for (i = 0; i < 3; i++) { if (!autoload_fail) tempval = hwinfo[EEPROM_TXPOWERHT40_2SDIFF + i]; else tempval = EEPROM_DEFAULT_HT40_2SDIFF; rtlefuse->eeprom_chnlarea_txpwr_ht40_2sdiif[RF90_PATH_A][i] = (tempval & 0xf); rtlefuse->eeprom_chnlarea_txpwr_ht40_2sdiif[RF90_PATH_B][i] = ((tempval & 0xf0) >> 4); } for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM CCK Area(%d) = 0x%x\n", rf_path, i, rtlefuse-> eeprom_chnlarea_txpwr_cck[rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM HT40 1S Area(%d) = 0x%x\n", rf_path, i, rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) for (i = 0; i < 3; i++) RTPRINT(rtlpriv, FINIT, INIT_EEPROM, "RF(%d) EEPROM HT40 2S Diff Area(%d) = 0x%x\n", rf_path, i, rtlefuse-> eeprom_chnlarea_txpwr_ht40_2sdiif[rf_path][i]); for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 14; i++) { index = _rtl92c_get_chnl_group((u8) i); rtlefuse->txpwrlevel_cck[rf_path][i] = rtlefuse->eeprom_chnlarea_txpwr_cck[rf_path][index]; rtlefuse->txpwrlevel_ht40_1s[rf_path][i] = rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][index]; if ((rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path][index] - rtlefuse-> eeprom_chnlarea_txpwr_ht40_2sdiif[rf_path][index]) > 0) { rtlefuse->txpwrlevel_ht40_2s[rf_path][i] = rtlefuse-> eeprom_chnlarea_txpwr_ht40_1s[rf_path] [index] - rtlefuse-> eeprom_chnlarea_txpwr_ht40_2sdiif[rf_path] [index]; } else { rtlefuse->txpwrlevel_ht40_2s[rf_path][i] = 0; } } for (i = 0; i < 14; i++) { RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = [0x%x / 0x%x / 0x%x]\n", rf_path, i, rtlefuse->txpwrlevel_cck[rf_path][i], rtlefuse->txpwrlevel_ht40_1s[rf_path][i], rtlefuse->txpwrlevel_ht40_2s[rf_path][i]); } } for (i = 0; i < 3; i++) { if (!autoload_fail) { rtlefuse->eeprom_pwrlimit_ht40[i] = hwinfo[EEPROM_TXPWR_GROUP + i]; rtlefuse->eeprom_pwrlimit_ht20[i] = hwinfo[EEPROM_TXPWR_GROUP + 3 + i]; } else { rtlefuse->eeprom_pwrlimit_ht40[i] = 0; rtlefuse->eeprom_pwrlimit_ht20[i] = 0; } } for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 14; i++) { index = _rtl92c_get_chnl_group((u8) i); if (rf_path == RF90_PATH_A) { rtlefuse->pwrgroup_ht20[rf_path][i] = (rtlefuse->eeprom_pwrlimit_ht20[index] & 0xf); rtlefuse->pwrgroup_ht40[rf_path][i] = (rtlefuse->eeprom_pwrlimit_ht40[index] & 0xf); } else if (rf_path == RF90_PATH_B) { rtlefuse->pwrgroup_ht20[rf_path][i] = ((rtlefuse->eeprom_pwrlimit_ht20[index] & 0xf0) >> 4); rtlefuse->pwrgroup_ht40[rf_path][i] = ((rtlefuse->eeprom_pwrlimit_ht40[index] & 0xf0) >> 4); } RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-%d pwrgroup_ht20[%d] = 0x%x\n", rf_path, i, rtlefuse->pwrgroup_ht20[rf_path][i]); RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-%d pwrgroup_ht40[%d] = 0x%x\n", rf_path, i, rtlefuse->pwrgroup_ht40[rf_path][i]); } } for (i = 0; i < 14; i++) { index = _rtl92c_get_chnl_group((u8) i); if (!autoload_fail) tempval = hwinfo[EEPROM_TXPOWERHT20DIFF + index]; else tempval = EEPROM_DEFAULT_HT20_DIFF; rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); if (rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] & BIT(3)) rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] |= 0xF0; if (rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] & BIT(3)) rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] |= 0xF0; index = _rtl92c_get_chnl_group((u8) i); if (!autoload_fail) tempval = hwinfo[EEPROM_TXPOWER_OFDMDIFF + index]; else tempval = EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF; rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); } rtlefuse->legacy_ht_txpowerdiff = rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][7]; for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]); for (i = 0; i < 14; i++) RTPRINT(rtlpriv, FINIT, INIT_TxPower, "RF-B Legacy to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]); if (!autoload_fail) rtlefuse->eeprom_regulatory = (hwinfo[RF_OPTION1] & 0x7); else rtlefuse->eeprom_regulatory = 0; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory); if (!autoload_fail) { rtlefuse->eeprom_tssi[RF90_PATH_A] = hwinfo[EEPROM_TSSI_A]; rtlefuse->eeprom_tssi[RF90_PATH_B] = hwinfo[EEPROM_TSSI_B]; } else { rtlefuse->eeprom_tssi[RF90_PATH_A] = EEPROM_DEFAULT_TSSI; rtlefuse->eeprom_tssi[RF90_PATH_B] = EEPROM_DEFAULT_TSSI; } RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TSSI_A = 0x%x, TSSI_B = 0x%x\n", rtlefuse->eeprom_tssi[RF90_PATH_A], rtlefuse->eeprom_tssi[RF90_PATH_B]); if (!autoload_fail) tempval = hwinfo[EEPROM_THERMAL_METER]; else tempval = EEPROM_DEFAULT_THERMALMETER; rtlefuse->eeprom_thermalmeter = (tempval & 0x1f); if (rtlefuse->eeprom_thermalmeter == 0x1f || autoload_fail) rtlefuse->apk_thermalmeterignore = true; rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter); } static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u16 i, usvalue; u8 hwinfo[HWSET_MAX_SIZE]; u16 eeprom_id; if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) { rtl_efuse_shadow_map_update(hw); memcpy((void *)hwinfo, (void *)&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], HWSET_MAX_SIZE); } else if (rtlefuse->epromtype == EEPROM_93C46) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "RTL819X Not boot from eeprom, check it !!"); } RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, "MAP", hwinfo, HWSET_MAX_SIZE); eeprom_id = *((u16 *)&hwinfo[0]); if (eeprom_id != RTL8190_EEPROM_ID) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "EEPROM ID(%#x) is invalid!!\n", eeprom_id); rtlefuse->autoload_failflag = true; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); rtlefuse->autoload_failflag = false; } if (rtlefuse->autoload_failflag) return; for (i = 0; i < 6; i += 2) { usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i]; *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue; } RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "%pM\n", rtlefuse->dev_addr); _rtl92ce_read_txpower_info_from_hwpg(hw, rtlefuse->autoload_failflag, hwinfo); rtl8192ce_read_bt_coexist_info_from_hwpg(hw, rtlefuse->autoload_failflag, hwinfo); rtlefuse->eeprom_channelplan = *&hwinfo[EEPROM_CHANNELPLAN]; rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION]; rtlefuse->txpwr_fromeprom = true; rtlefuse->eeprom_oemid = *&hwinfo[EEPROM_CUSTOMER_ID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); /* set channel paln to world wide 13 */ rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13; if (rtlhal->oem_id == RT_CID_DEFAULT) { switch (rtlefuse->eeprom_oemid) { case EEPROM_CID_DEFAULT: if (rtlefuse->eeprom_did == 0x8176) { if ((rtlefuse->eeprom_svid == 0x103C && rtlefuse->eeprom_smid == 0x1629)) rtlhal->oem_id = RT_CID_819x_HP; else rtlhal->oem_id = RT_CID_DEFAULT; } else { rtlhal->oem_id = RT_CID_DEFAULT; } break; case EEPROM_CID_TOSHIBA: rtlhal->oem_id = RT_CID_TOSHIBA; break; case EEPROM_CID_QMI: rtlhal->oem_id = RT_CID_819x_QMI; break; case EEPROM_CID_WHQL: default: rtlhal->oem_id = RT_CID_DEFAULT; break; } } } static void _rtl92ce_hal_customized_behavior(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); switch (rtlhal->oem_id) { case RT_CID_819x_HP: pcipriv->ledctl.led_opendrain = true; break; case RT_CID_819x_Lenovo: case RT_CID_DEFAULT: case RT_CID_TOSHIBA: case RT_CID_CCX: case RT_CID_819x_Acer: case RT_CID_WHQL: default: break; } RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "RT Customized ID: 0x%02X\n", rtlhal->oem_id); } void rtl92ce_read_eeprom_info(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 tmp_u1b; rtlhal->version = _rtl92ce_read_chip_version(hw); if (get_rf_type(rtlphy) == RF_1T1R) rtlpriv->dm.rfpath_rxenable[0] = true; else rtlpriv->dm.rfpath_rxenable[0] = rtlpriv->dm.rfpath_rxenable[1] = true; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "VersionID = 0x%4x\n", rtlhal->version); tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR); if (tmp_u1b & BIT(4)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EEPROM\n"); rtlefuse->epromtype = EEPROM_93C46; } else { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EFUSE\n"); rtlefuse->epromtype = EEPROM_BOOT_EFUSE; } if (tmp_u1b & BIT(5)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); rtlefuse->autoload_failflag = false; _rtl92ce_read_adapter_info(hw); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Autoload ERR!!\n"); } _rtl92ce_hal_customized_behavior(hw); } static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u32 ratr_value; u8 ratr_index = 0; u8 nmode = mac->ht_enable; u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate; u32 tmp_ratr_value; u8 curtxbw_40mhz = mac->bw_40; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = mac->mode; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_value = sta->supp_rates[1] << 4; else ratr_value = sta->supp_rates[0]; ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 | sta->ht_cap.mcs.rx_mask[0] << 12); switch (wirelessmode) { case WIRELESS_MODE_B: if (ratr_value & 0x0000000c) ratr_value &= 0x0000000d; else ratr_value &= 0x0000000f; break; case WIRELESS_MODE_G: ratr_value &= 0x00000FF5; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: nmode = 1; if (mimo_ps == IEEE80211_SMPS_STATIC) { ratr_value &= 0x0007F005; } else { u32 ratr_mask; if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_1T1R) ratr_mask = 0x000ff005; else ratr_mask = 0x0f0ff005; ratr_value &= ratr_mask; } break; default: if (rtlphy->rf_type == RF_1T2R) ratr_value &= 0x000ff0ff; else ratr_value &= 0x0f0ff0ff; break; } if ((rtlpcipriv->bt_coexist.bt_coexistence) && (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) && (rtlpcipriv->bt_coexist.bt_cur_state) && (rtlpcipriv->bt_coexist.bt_ant_isolation) && ((rtlpcipriv->bt_coexist.bt_service == BT_SCO) || (rtlpcipriv->bt_coexist.bt_service == BT_BUSY))) ratr_value &= 0x0fffcfc0; else ratr_value &= 0x0FFFFFFF; if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz))) { ratr_value |= 0x10000000; tmp_ratr_value = (ratr_value >> 12); for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) { if ((1 << shortgi_rate) & tmp_ratr_value) break; } shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) | (shortgi_rate << 4) | (shortgi_rate); } rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value); RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0)); } static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index; u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = 0; bool shortgi = false; u8 rate_mask[5]; u8 macid = 0; u8 mimo_ps = IEEE80211_SMPS_OFF; sta_entry = (struct rtl_sta_info *) sta->drv_priv; wirelessmode = sta_entry->wireless_mode; if (mac->opmode == NL80211_IFTYPE_STATION) curtxbw_40mhz = mac->bw_40; else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) macid = sta->aid + 1; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_bitmap = sta->supp_rates[1] << 4; else ratr_bitmap = sta->supp_rates[0]; ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 | sta->ht_cap.mcs.rx_mask[0] << 12); switch (wirelessmode) { case WIRELESS_MODE_B: ratr_index = RATR_INX_WIRELESS_B; if (ratr_bitmap & 0x0000000c) ratr_bitmap &= 0x0000000d; else ratr_bitmap &= 0x0000000f; break; case WIRELESS_MODE_G: ratr_index = RATR_INX_WIRELESS_GB; if (rssi_level == 1) ratr_bitmap &= 0x00000f00; else if (rssi_level == 2) ratr_bitmap &= 0x00000ff0; else ratr_bitmap &= 0x00000ff5; break; case WIRELESS_MODE_A: ratr_index = RATR_INX_WIRELESS_A; ratr_bitmap &= 0x00000ff0; break; case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: ratr_index = RATR_INX_WIRELESS_NGB; if (mimo_ps == IEEE80211_SMPS_STATIC) { if (rssi_level == 1) ratr_bitmap &= 0x00070000; else if (rssi_level == 2) ratr_bitmap &= 0x0007f000; else ratr_bitmap &= 0x0007f005; } else { if (rtlphy->rf_type == RF_1T2R || rtlphy->rf_type == RF_1T1R) { if (curtxbw_40mhz) { if (rssi_level == 1) ratr_bitmap &= 0x000f0000; else if (rssi_level == 2) ratr_bitmap &= 0x000ff000; else ratr_bitmap &= 0x000ff015; } else { if (rssi_level == 1) ratr_bitmap &= 0x000f0000; else if (rssi_level == 2) ratr_bitmap &= 0x000ff000; else ratr_bitmap &= 0x000ff005; } } else { if (curtxbw_40mhz) { if (rssi_level == 1) ratr_bitmap &= 0x0f0f0000; else if (rssi_level == 2) ratr_bitmap &= 0x0f0ff000; else ratr_bitmap &= 0x0f0ff015; } else { if (rssi_level == 1) ratr_bitmap &= 0x0f0f0000; else if (rssi_level == 2) ratr_bitmap &= 0x0f0ff000; else ratr_bitmap &= 0x0f0ff005; } } } if ((curtxbw_40mhz && curshortgi_40mhz) || (!curtxbw_40mhz && curshortgi_20mhz)) { if (macid == 0) shortgi = true; else if (macid == 1) shortgi = false; } break; default: ratr_index = RATR_INX_WIRELESS_NGB; if (rtlphy->rf_type == RF_1T2R) ratr_bitmap &= 0x000ff0ff; else ratr_bitmap &= 0x0f0ff0ff; break; } RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "ratr_bitmap :%x\n", ratr_bitmap); *(u32 *)&rate_mask = (ratr_bitmap & 0x0fffffff) | (ratr_index << 28); rate_mask[4] = macid | (shortgi ? 0x20 : 0x00) | 0x80; RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "Rate_index:%x, ratr_val:%x, %x:%x:%x:%x:%x\n", ratr_index, ratr_bitmap, rate_mask[0], rate_mask[1], rate_mask[2], rate_mask[3], rate_mask[4]); rtl92c_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask); if (macid != 0) sta_entry->ratr_index = ratr_index; } void rtl92ce_update_hal_rate_tbl(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->dm.useramask) rtl92ce_update_hal_rate_mask(hw, sta, rssi_level); else rtl92ce_update_hal_rate_table(hw, sta); } void rtl92ce_update_channel_access_setting(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else sifs_timer = 0x1010; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer); } bool rtl92ce_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); enum rf_pwrstate e_rfpowerstate_toset; u8 u1tmp; bool actuallyset = false; unsigned long flag; if (rtlpci->being_init_adapter) return false; if (ppsc->swrf_processing) return false; spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); if (ppsc->rfchange_inprogress) { spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); return false; } else { ppsc->rfchange_inprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG)&~(BIT(3))); u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL); e_rfpowerstate_toset = (u1tmp & BIT(3)) ? ERFON : ERFOFF; if ((ppsc->hwradiooff) && (e_rfpowerstate_toset == ERFON)) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "GPIOChangeRF - HW Radio ON, RF ON\n"); e_rfpowerstate_toset = ERFON; ppsc->hwradiooff = false; actuallyset = true; } else if (!ppsc->hwradiooff && (e_rfpowerstate_toset == ERFOFF)) { RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, "GPIOChangeRF - HW Radio OFF, RF OFF\n"); e_rfpowerstate_toset = ERFOFF; ppsc->hwradiooff = true; actuallyset = true; } if (actuallyset) { spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } else { if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); ppsc->rfchange_inprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); } *valid = 1; return !ppsc->hwradiooff; } void rtl92ce_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 *macaddr = p_macaddr; u32 entry_id = 0; bool is_pairwise = false; static u8 cam_const_addr[4][6] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} }; static u8 cam_const_broad[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (clear_all) { u8 idx = 0; u8 cam_offset = 0; u8 clear_number = 5; RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n"); for (idx = 0; idx < clear_number; idx++) { rtl_cam_mark_invalid(hw, cam_offset + idx); rtl_cam_empty_entry(hw, cam_offset + idx); if (idx < 5) { memset(rtlpriv->sec.key_buf[idx], 0, MAX_KEY_LEN); rtlpriv->sec.key_len[idx] = 0; } } } else { switch (enc_algo) { case WEP40_ENCRYPTION: enc_algo = CAM_WEP40; break; case WEP104_ENCRYPTION: enc_algo = CAM_WEP104; break; case TKIP_ENCRYPTION: enc_algo = CAM_TKIP; break; case AESCCMP_ENCRYPTION: enc_algo = CAM_AES; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); enc_algo = CAM_TKIP; break; } if (is_wepkey || rtlpriv->sec.use_defaultkey) { macaddr = cam_const_addr[key_index]; entry_id = key_index; } else { if (is_group) { macaddr = cam_const_broad; entry_id = key_index; } else { if (mac->opmode == NL80211_IFTYPE_AP) { entry_id = rtl_cam_get_free_entry(hw, p_macaddr); if (entry_id >= TOTAL_CAM_ENTRY) { RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "Can not find free hw security cam entry\n"); return; } } else { entry_id = CAM_PAIRWISE_KEY_POSITION; } key_index = PAIRWISE_KEYIDX; is_pairwise = true; } } if (rtlpriv->sec.key_len[key_index] == 0) { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "delete one entry, entry_id is %d\n", entry_id); if (mac->opmode == NL80211_IFTYPE_AP) rtl_cam_del_entry(hw, p_macaddr); rtl_cam_delete_one_entry(hw, p_macaddr, entry_id); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY length is %d\n", rtlpriv->sec.key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "The insert KEY is %x %x\n", rtlpriv->sec.key_buf[0][0], rtlpriv->sec.key_buf[0][1]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "add one entry\n"); if (is_pairwise) { RT_PRINT_DATA(rtlpriv, COMP_SEC, DBG_LOUD, "Pairwise Key content", rtlpriv->sec.pairwise_key, rtlpriv->sec. key_len[PAIRWISE_KEYIDX]); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set Pairwise key\n"); rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec. key_buf[key_index]); } else { RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set group key\n"); if (mac->opmode == NL80211_IFTYPE_ADHOC) { rtl_cam_add_one_entry(hw, rtlefuse->dev_addr, PAIRWISE_KEYIDX, CAM_PAIRWISE_KEY_POSITION, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf [entry_id]); } rtl_cam_add_one_entry(hw, macaddr, key_index, entry_id, enc_algo, CAM_CONFIG_NO_USEDK, rtlpriv->sec.key_buf[entry_id]); } } } } static void rtl8192ce_bt_var_init(struct ieee80211_hw *hw) { struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); rtlpcipriv->bt_coexist.bt_coexistence = rtlpcipriv->bt_coexist.eeprom_bt_coexist; rtlpcipriv->bt_coexist.bt_ant_num = rtlpcipriv->bt_coexist.eeprom_bt_ant_num; rtlpcipriv->bt_coexist.bt_coexist_type = rtlpcipriv->bt_coexist.eeprom_bt_type; if (rtlpcipriv->bt_coexist.reg_bt_iso == 2) rtlpcipriv->bt_coexist.bt_ant_isolation = rtlpcipriv->bt_coexist.eeprom_bt_ant_isolation; else rtlpcipriv->bt_coexist.bt_ant_isolation = rtlpcipriv->bt_coexist.reg_bt_iso; rtlpcipriv->bt_coexist.bt_radio_shared_type = rtlpcipriv->bt_coexist.eeprom_bt_radio_shared; if (rtlpcipriv->bt_coexist.bt_coexistence) { if (rtlpcipriv->bt_coexist.reg_bt_sco == 1) rtlpcipriv->bt_coexist.bt_service = BT_OTHER_ACTION; else if (rtlpcipriv->bt_coexist.reg_bt_sco == 2) rtlpcipriv->bt_coexist.bt_service = BT_SCO; else if (rtlpcipriv->bt_coexist.reg_bt_sco == 4) rtlpcipriv->bt_coexist.bt_service = BT_BUSY; else if (rtlpcipriv->bt_coexist.reg_bt_sco == 5) rtlpcipriv->bt_coexist.bt_service = BT_OTHERBUSY; else rtlpcipriv->bt_coexist.bt_service = BT_IDLE; rtlpcipriv->bt_coexist.bt_edca_ul = 0; rtlpcipriv->bt_coexist.bt_edca_dl = 0; rtlpcipriv->bt_coexist.bt_rssi_state = 0xff; } } void rtl8192ce_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, bool auto_load_fail, u8 *hwinfo) { struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); u8 value; if (!auto_load_fail) { rtlpcipriv->bt_coexist.eeprom_bt_coexist = ((hwinfo[RF_OPTION1] & 0xe0) >> 5); value = hwinfo[RF_OPTION4]; rtlpcipriv->bt_coexist.eeprom_bt_type = ((value & 0xe) >> 1); rtlpcipriv->bt_coexist.eeprom_bt_ant_num = (value & 0x1); rtlpcipriv->bt_coexist.eeprom_bt_ant_isolation = ((value & 0x10) >> 4); rtlpcipriv->bt_coexist.eeprom_bt_radio_shared = ((value & 0x20) >> 5); } else { rtlpcipriv->bt_coexist.eeprom_bt_coexist = 0; rtlpcipriv->bt_coexist.eeprom_bt_type = BT_2WIRE; rtlpcipriv->bt_coexist.eeprom_bt_ant_num = ANT_X2; rtlpcipriv->bt_coexist.eeprom_bt_ant_isolation = 0; rtlpcipriv->bt_coexist.eeprom_bt_radio_shared = BT_RADIO_SHARED; } rtl8192ce_bt_var_init(hw); } void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw) { struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); /* 0:Low, 1:High, 2:From Efuse. */ rtlpcipriv->bt_coexist.reg_bt_iso = 2; /* 0:Idle, 1:None-SCO, 2:SCO, 3:From Counter. */ rtlpcipriv->bt_coexist.reg_bt_sco = 3; /* 0:Disable BT control A-MPDU, 1:Enable BT control A-MPDU. */ rtlpcipriv->bt_coexist.reg_bt_sco = 0; } void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); u8 u1_tmp; if (rtlpcipriv->bt_coexist.bt_coexistence && ((rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) || rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC8)) { if (rtlpcipriv->bt_coexist.bt_ant_isolation) rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0); u1_tmp = rtl_read_byte(rtlpriv, 0x4fd) & BIT_OFFSET_LEN_MASK_32(0, 1); u1_tmp = u1_tmp | ((rtlpcipriv->bt_coexist.bt_ant_isolation == 1) ? 0 : BIT_OFFSET_LEN_MASK_32(1, 1)) | ((rtlpcipriv->bt_coexist.bt_service == BT_SCO) ? 0 : BIT_OFFSET_LEN_MASK_32(2, 1)); rtl_write_byte(rtlpriv, 0x4fd, u1_tmp); rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+4, 0xaaaa9aaa); rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+8, 0xffbd0040); rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+0xc, 0x40000010); /* Config to 1T1R. */ if (rtlphy->rf_type == RF_1T1R) { u1_tmp = rtl_read_byte(rtlpriv, ROFDM0_TRXPATHENABLE); u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1)); rtl_write_byte(rtlpriv, ROFDM0_TRXPATHENABLE, u1_tmp); u1_tmp = rtl_read_byte(rtlpriv, ROFDM1_TRXPATHENABLE); u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1)); rtl_write_byte(rtlpriv, ROFDM1_TRXPATHENABLE, u1_tmp); } } } void rtl92ce_suspend(struct ieee80211_hw *hw) { } void rtl92ce_resume(struct ieee80211_hw *hw) { } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h0000644000175000017500000001016012026211315024345 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_DM_H__ #define __RTL92C_DM_H__ #define HAL_DM_DIG_DISABLE BIT(0) #define HAL_DM_HIPWR_DISABLE BIT(1) #define OFDM_TABLE_LENGTH 37 #define CCK_TABLE_LENGTH 33 #define OFDM_TABLE_SIZE 37 #define CCK_TABLE_SIZE 33 #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 #define DM_DIG_THRESH_HIGH 40 #define DM_DIG_THRESH_LOW 35 #define DM_FALSEALARM_THRESH_LOW 400 #define DM_FALSEALARM_THRESH_HIGH 1000 #define DM_DIG_MAX 0x3e #define DM_DIG_MIN 0x1e #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 #define DM_DIG_BACKOFF_MAX 12 #define DM_DIG_BACKOFF_MIN -4 #define DM_DIG_BACKOFF_DEFAULT 10 #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 #define DM_RATR_STA_INIT 0 #define DM_RATR_STA_HIGH 1 #define DM_RATR_STA_MIDDLE 2 #define DM_RATR_STA_LOW 3 #define CTS2SELF_THVAL 30 #define REGC38_TH 20 #define WAIOTTHVal 25 #define TXHIGHPWRLEVEL_NORMAL 0 #define TXHIGHPWRLEVEL_LEVEL1 1 #define TXHIGHPWRLEVEL_LEVEL2 2 #define TXHIGHPWRLEVEL_BT1 3 #define TXHIGHPWRLEVEL_BT2 4 #define DM_TYPE_BYFW 0 #define DM_TYPE_BYDRIVER 1 #define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 #define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 struct swat_t { u8 failure_cnt; u8 try_flag; u8 stop_trying; long pre_rssi; long trying_threshold; u8 cur_antenna; u8 pre_antenna; }; enum tag_dynamic_init_gain_operation_type_definition { DIG_TYPE_THRESH_HIGH = 0, DIG_TYPE_THRESH_LOW = 1, DIG_TYPE_BACKOFF = 2, DIG_TYPE_RX_GAIN_MIN = 3, DIG_TYPE_RX_GAIN_MAX = 4, DIG_TYPE_ENABLE = 5, DIG_TYPE_DISABLE = 6, DIG_OP_TYPE_MAX }; enum tag_cck_packet_detection_threshold_type_definition { CCK_PD_STAGE_LowRssi = 0, CCK_PD_STAGE_HighRssi = 1, CCK_FA_STAGE_Low = 2, CCK_FA_STAGE_High = 3, CCK_PD_STAGE_MAX = 4, }; enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, CCA_MAX = 2, }; enum dm_rf_e { RF_SAVE = 0, RF_NORMAL = 1, RF_MAX = 2, }; enum dm_sw_ant_switch_e { ANS_ANTENNA_B = 1, ANS_ANTENNA_A = 2, ANS_ANTENNA_MAX = 3, }; enum dm_dig_ext_port_alg_e { DIG_EXT_PORT_STAGE_0 = 0, DIG_EXT_PORT_STAGE_1 = 1, DIG_EXT_PORT_STAGE_2 = 2, DIG_EXT_PORT_STAGE_3 = 3, DIG_EXT_PORT_STAGE_MAX = 4, }; enum dm_dig_connect_e { DIG_STA_DISCONNECT = 0, DIG_STA_CONNECT = 1, DIG_STA_BEFORE_CONNECT = 2, DIG_MULTISTA_DISCONNECT = 3, DIG_MULTISTA_CONNECT = 4, DIG_CONNECT_MAX }; void rtl92c_dm_init(struct ieee80211_hw *hw); void rtl92c_dm_watchdog(struct ieee80211_hw *hw); void rtl92c_dm_write_dig(struct ieee80211_hw *hw); void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw); void rtl92c_dm_check_txpower_tracking(struct ieee80211_hw *hw); void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal); void rtl92c_dm_bt_coexist(struct ieee80211_hw *hw); void rtl92ce_dm_dynamic_txpower(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c0000644000175000017500000000740612026211315024351 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../base.h" #include "../pci.h" #include "reg.h" #include "def.h" #include "phy.h" #include "dm.h" #include "../rtl8192c/fw_common.h" void rtl92ce_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb; if (!rtlpriv->dm.dynamic_txpower_enable) return; if (rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if ((mac->link_state < MAC80211_LINKED) && (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb == 0)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "Not connected to any\n"); rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if (mac->link_state >= MAC80211_LINKED) { if (mac->opmode == NL80211_IFTYPE_ADHOC) { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Client PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } else { undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "STA Default Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } } else { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Ext Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } if (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL2) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x0)\n"); } else if ((undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3)) && (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL1)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x10)\n"); } else if (undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL1 - 5)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_NORMAL\n"); } if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "PHY_SetTxPowerLevel8192S() Channel = %d\n", rtlphy->current_channel); rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); } rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; } compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/def.h0000644000175000017500000001662512026211315024517 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_DEF_H__ #define __RTL92C_DEF_H__ #define HAL_RETRY_LIMIT_INFRA 48 #define HAL_RETRY_LIMIT_AP_ADHOC 7 #define PHY_RSSI_SLID_WIN_MAX 100 #define PHY_LINKQUALITY_SLID_WIN_MAX 20 #define PHY_BEACON_RSSI_SLID_WIN_MAX 10 #define RESET_DELAY_8185 20 #define RT_IBSS_INT_MASKS (IMR_BCNINT | IMR_TBDOK | IMR_TBDER) #define RT_AC_INT_MASKS (IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK) #define NUM_OF_FIRMWARE_QUEUE 10 #define NUM_OF_PAGES_IN_FW 0x100 #define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x07 #define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0x0 #define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x0 #define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x02 #define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0x02 #define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x2 #define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0xA1 #define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM 0x026 #define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM 0x048 #define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM 0x048 #define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM 0x026 #define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM 0x00 #define MAX_LINES_HWCONFIG_TXT 1000 #define MAX_BYTES_LINE_HWCONFIG_TXT 256 #define SW_THREE_WIRE 0 #define HW_THREE_WIRE 2 #define BT_DEMO_BOARD 0 #define BT_QA_BOARD 1 #define BT_FPGA 2 #define RX_SMOOTH_FACTOR 20 #define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0 #define HAL_PRIME_CHNL_OFFSET_LOWER 1 #define HAL_PRIME_CHNL_OFFSET_UPPER 2 #define MAX_H2C_QUEUE_NUM 10 #define RX_MPDU_QUEUE 0 #define RX_CMD_QUEUE 1 #define RX_MAX_QUEUE 2 #define AC2QUEUEID(_AC) (_AC) #define C2H_RX_CMD_HDR_LEN 8 #define GET_C2H_CMD_CMD_LEN(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 0, 16) #define GET_C2H_CMD_ELEMENT_ID(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 16, 8) #define GET_C2H_CMD_CMD_SEQ(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 24, 7) #define GET_C2H_CMD_CONTINUE(__prxhdr) \ LE_BITS_TO_4BYTE((__prxhdr), 31, 1) #define GET_C2H_CMD_CONTENT(__prxhdr) \ ((u8 *)(__prxhdr) + C2H_RX_CMD_HDR_LEN) #define GET_C2H_CMD_FEEDBACK_ELEMENT_ID(__pcmdfbhdr) \ LE_BITS_TO_4BYTE((__pcmdfbhdr), 0, 8) #define GET_C2H_CMD_FEEDBACK_CCX_LEN(__pcmdfbhdr) \ LE_BITS_TO_4BYTE((__pcmdfbhdr), 8, 8) #define GET_C2H_CMD_FEEDBACK_CCX_CMD_CNT(__pcmdfbhdr) \ LE_BITS_TO_4BYTE((__pcmdfbhdr), 16, 16) #define GET_C2H_CMD_FEEDBACK_CCX_MAC_ID(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 0, 5) #define GET_C2H_CMD_FEEDBACK_CCX_VALID(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 7, 1) #define GET_C2H_CMD_FEEDBACK_CCX_RETRY_CNT(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 8, 5) #define GET_C2H_CMD_FEEDBACK_CCX_TOK(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 15, 1) #define GET_C2H_CMD_FEEDBACK_CCX_QSEL(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4) #define GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr) \ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12) #define CHIP_VER_B BIT(4) #define CHIP_92C_BITMASK BIT(0) #define CHIP_UNKNOWN BIT(7) #define CHIP_92C_1T2R 0x03 #define CHIP_92C 0x01 #define CHIP_88C 0x00 enum version_8192c { VERSION_A_CHIP_92C = 0x01, VERSION_A_CHIP_88C = 0x00, VERSION_B_CHIP_92C = 0x11, VERSION_B_CHIP_88C = 0x10, VERSION_TEST_CHIP_88C = 0x00, VERSION_TEST_CHIP_92C = 0x01, VERSION_NORMAL_TSMC_CHIP_88C = 0x10, VERSION_NORMAL_TSMC_CHIP_92C = 0x11, VERSION_NORMAL_TSMC_CHIP_92C_1T2R = 0x13, VERSION_NORMAL_UMC_CHIP_88C_A_CUT = 0x30, VERSION_NORMAL_UMC_CHIP_92C_A_CUT = 0x31, VERSION_NORMAL_UMC_CHIP_92C_1T2R_A_CUT = 0x33, VERSION_NORMA_UMC_CHIP_8723_1T1R_A_CUT = 0x34, VERSION_NORMA_UMC_CHIP_8723_1T1R_B_CUT = 0x3c, VERSION_NORMAL_UMC_CHIP_88C_B_CUT = 0x70, VERSION_NORMAL_UMC_CHIP_92C_B_CUT = 0x71, VERSION_NORMAL_UMC_CHIP_92C_1T2R_B_CUT = 0x73, VERSION_UNKNOWN = 0x88, }; #define CUT_VERSION_MASK (BIT(6)|BIT(7)) #define CHIP_VENDOR_UMC BIT(5) #define CHIP_VENDOR_UMC_B_CUT BIT(6) /* Chip version for ECO */ #define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \ ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) #define IS_CHIP_VER_B(version) ((version & CHIP_VER_B) ? true : false) #define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \ ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) #define IS_92C_SERIAL(version) ((version & CHIP_92C_BITMASK) ? true : false) #define IS_CHIP_VENDOR_UMC(version) \ ((version & CHIP_VENDOR_UMC) ? true : false) #define GET_CVID_CUT_VERSION(version) ((version) & CUT_VERSION_MASK) #define IS_81xxC_VENDOR_UMC_B_CUT(version) \ ((IS_CHIP_VENDOR_UMC(version)) ? \ ((GET_CVID_CUT_VERSION(version) == CHIP_VENDOR_UMC_B_CUT) ? \ true : false) : false) enum rtl819x_loopback_e { RTL819X_NO_LOOPBACK = 0, RTL819X_MAC_LOOPBACK = 1, RTL819X_DMA_LOOPBACK = 2, RTL819X_CCK_LOOPBACK = 3, }; enum rf_optype { RF_OP_BY_SW_3WIRE = 0, RF_OP_BY_FW, RF_OP_MAX }; enum rf_power_state { RF_ON, RF_OFF, RF_SLEEP, RF_SHUT_DOWN, }; enum power_save_mode { POWER_SAVE_MODE_ACTIVE, POWER_SAVE_MODE_SAVE, }; enum power_polocy_config { POWERCFG_MAX_POWER_SAVINGS, POWERCFG_GLOBAL_POWER_SAVINGS, POWERCFG_LOCAL_POWER_SAVINGS, POWERCFG_LENOVO, }; enum interface_select_pci { INTF_SEL1_MINICARD = 0, INTF_SEL0_PCIE = 1, INTF_SEL2_RSV = 2, INTF_SEL3_RSV = 3, }; enum hal_fw_c2h_cmd_id { HAL_FW_C2H_CMD_Read_MACREG = 0, HAL_FW_C2H_CMD_Read_BBREG = 1, HAL_FW_C2H_CMD_Read_RFREG = 2, HAL_FW_C2H_CMD_Read_EEPROM = 3, HAL_FW_C2H_CMD_Read_EFUSE = 4, HAL_FW_C2H_CMD_Read_CAM = 5, HAL_FW_C2H_CMD_Get_BasicRate = 6, HAL_FW_C2H_CMD_Get_DataRate = 7, HAL_FW_C2H_CMD_Survey = 8, HAL_FW_C2H_CMD_SurveyDone = 9, HAL_FW_C2H_CMD_JoinBss = 10, HAL_FW_C2H_CMD_AddSTA = 11, HAL_FW_C2H_CMD_DelSTA = 12, HAL_FW_C2H_CMD_AtimDone = 13, HAL_FW_C2H_CMD_TX_Report = 14, HAL_FW_C2H_CMD_CCX_Report = 15, HAL_FW_C2H_CMD_DTM_Report = 16, HAL_FW_C2H_CMD_TX_Rate_Statistics = 17, HAL_FW_C2H_CMD_C2HLBK = 18, HAL_FW_C2H_CMD_C2HDBG = 19, HAL_FW_C2H_CMD_C2HFEEDBACK = 20, HAL_FW_C2H_CMD_MAX }; enum rtl_desc_qsel { QSLT_BK = 0x2, QSLT_BE = 0x0, QSLT_VI = 0x5, QSLT_VO = 0x7, QSLT_BEACON = 0x10, QSLT_HIGH = 0x11, QSLT_MGNT = 0x12, QSLT_CMD = 0x13, }; struct phy_sts_cck_8192s_t { u8 adc_pwdb_X[4]; u8 sq_rpt; u8 cck_agc_rpt; }; struct h2c_cmd_8192c { u8 element_id; u32 cmd_len; u8 *p_cmdbuffer; }; #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192ce/Makefile0000644000175000017500000000026112026211315025235 0ustar mcgrofmcgrofrtl8192ce-objs := \ dm.o \ hw.o \ led.o \ phy.o \ rf.o \ sw.o \ table.o \ trx.o obj-$(CONFIG_RTL8192CE) += rtl8192ce.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/0000755000175000017500000000000012026211315023431 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h0000644000175000017500000001720712026211315025761 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C_PHY_COMMON_H__ #define __RTL92C_PHY_COMMON_H__ #define MAX_PRECMD_CNT 16 #define MAX_RFDEPENDCMD_CNT 16 #define MAX_POSTCMD_CNT 16 #define MAX_DOZE_WAITING_TIMES_9x 64 #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 #define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 #define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 #define APK_CURVE_REG_NUM 4 #define PATH_NUM 2 #define LOOP_LIMIT 5 #define MAX_STALL_TIME 50 #define AntennaDiversityValue 0x80 #define MAX_TXPWR_IDX_NMODE_92S 63 #define Reset_Cnt_Limit 3 #define IQK_ADDA_REG_NUM 16 #define IQK_MAC_REG_NUM 4 #define IQK_DELAY_TIME 1 #define RF90_PATH_MAX 2 #define CT_OFFSET_MAC_ADDR 0X16 #define CT_OFFSET_CCK_TX_PWR_IDX 0x5A #define CT_OFFSET_HT401S_TX_PWR_IDX 0x60 #define CT_OFFSET_HT402S_TX_PWR_IDX_DIF 0x66 #define CT_OFFSET_HT20_TX_PWR_IDX_DIFF 0x69 #define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF 0x6C #define CT_OFFSET_HT40_MAX_PWR_OFFSET 0x6F #define CT_OFFSET_HT20_MAX_PWR_OFFSET 0x72 #define CT_OFFSET_CHANNEL_PLAH 0x75 #define CT_OFFSET_THERMAL_METER 0x78 #define CT_OFFSET_RF_OPTION 0x79 #define CT_OFFSET_VERSION 0x7E #define CT_OFFSET_CUSTOMER_ID 0x7F #define RTL92C_MAX_PATH_NUM 2 #define LLT_LAST_ENTRY_OF_TX_PKT_BUFFER 255 enum swchnlcmd_id { CMDID_END, CMDID_SET_TXPOWEROWER_LEVEL, CMDID_BBREGWRITE10, CMDID_WRITEPORT_ULONG, CMDID_WRITEPORT_USHORT, CMDID_WRITEPORT_UCHAR, CMDID_RF_WRITEREG, }; struct swchnlcmd { enum swchnlcmd_id cmdid; u32 para1; u32 para2; u32 msdelay; }; enum hw90_block_e { HW90_BLOCK_MAC = 0, HW90_BLOCK_PHY0 = 1, HW90_BLOCK_PHY1 = 2, HW90_BLOCK_RF = 3, HW90_BLOCK_MAXIMUM = 4, }; enum baseband_config_type { BASEBAND_CONFIG_PHY_REG = 0, BASEBAND_CONFIG_AGC_TAB = 1, }; enum ra_offset_area { RA_OFFSET_LEGACY_OFDM1, RA_OFFSET_LEGACY_OFDM2, RA_OFFSET_HT_OFDM1, RA_OFFSET_HT_OFDM2, RA_OFFSET_HT_OFDM3, RA_OFFSET_HT_OFDM4, RA_OFFSET_HT_CCK, }; enum antenna_path { ANTENNA_NONE, ANTENNA_D, ANTENNA_C, ANTENNA_CD, ANTENNA_B, ANTENNA_BD, ANTENNA_BC, ANTENNA_BCD, ANTENNA_A, ANTENNA_AD, ANTENNA_AC, ANTENNA_ACD, ANTENNA_AB, ANTENNA_ABD, ANTENNA_ABC, ANTENNA_ABCD }; struct r_antenna_select_ofdm { u32 r_tx_antenna:4; u32 r_ant_l:4; u32 r_ant_non_ht:4; u32 r_ant_ht1:4; u32 r_ant_ht2:4; u32 r_ant_ht_s1:4; u32 r_ant_non_ht_s1:4; u32 ofdm_txsc:2; u32 reserved:2; }; struct r_antenna_select_cck { u8 r_cckrx_enable_2:2; u8 r_cckrx_enable:2; u8 r_ccktx_enable:4; }; struct efuse_contents { u8 mac_addr[ETH_ALEN]; u8 cck_tx_power_idx[6]; u8 ht40_1s_tx_power_idx[6]; u8 ht40_2s_tx_power_idx_diff[3]; u8 ht20_tx_power_idx_diff[3]; u8 ofdm_tx_power_idx_diff[3]; u8 ht40_max_power_offset[3]; u8 ht20_max_power_offset[3]; u8 channel_plan; u8 thermal_meter; u8 rf_option[5]; u8 version; u8 oem_id; u8 regulatory; }; struct tx_power_struct { u8 cck[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 ht40_1s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 ht40_2s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 ht20_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 legacy_ht_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 legacy_ht_txpowerdiff; u8 groupht20[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 groupht40[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; u8 pwrgroup_cnt; u32 mcs_original_offset[4][16]; }; u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask); void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data); u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); bool rtl92c_phy_mac_config(struct ieee80211_hw *hw); bool rtl92c_phy_bb_config(struct ieee80211_hw *hw); bool rtl92c_phy_rf_config(struct ieee80211_hw *hw); bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, enum radio_path rfpath); void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel); void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm); void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw); u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw); void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, u16 beaconinterval); void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, u32 rfpath); bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw); void rtl92c_phy_set_io(struct ieee80211_hw *hw); void rtl92c_bb_block_on(struct ieee80211_hw *hw); u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask); long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, enum wireless_mode wirelessmode, u8 txpwridx); u8 _rtl92c_phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, enum wireless_mode wirelessmode, long power_indbm); void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw); void _rtl92c_phy_set_rf_sleep(struct ieee80211_hw *hw); bool _rtl92c_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, u8 *stage, u8 *step, u32 *delay); u8 rtl92c_bt_rssi_state_change(struct ieee80211_hw *hw); u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset); void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data); u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset); void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data); bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw); void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c0000644000175000017500000015670412026211315025762 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include #include "../wifi.h" #include "../rtl8192ce/reg.h" #include "../rtl8192ce/def.h" #include "dm_common.h" #include "phy_common.h" /* Define macro to shorten lines */ #define MCS_TXPWR mcs_txpwrlevel_origoffset u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 returnvalue, originalvalue, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask); originalvalue = rtl_read_dword(rtlpriv, regaddr); bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); returnvalue = (originalvalue & bitmask) >> bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "BBR MASK=0x%x Addr[0x%x]=0x%x\n", bitmask, regaddr, originalvalue); return returnvalue; } EXPORT_SYMBOL(rtl92c_phy_query_bb_reg); void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 originalvalue, bitshift; RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, bitmask, data); if (bitmask != MASKDWORD) { originalvalue = rtl_read_dword(rtlpriv, regaddr); bitshift = _rtl92c_phy_calculate_bit_shift(bitmask); data = ((originalvalue & (~bitmask)) | (data << bitshift)); } rtl_write_dword(rtlpriv, regaddr, data); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, bitmask, data); } EXPORT_SYMBOL(rtl92c_phy_set_bb_reg); u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset) { RT_ASSERT(false, "deprecated!\n"); return 0; } EXPORT_SYMBOL(_rtl92c_phy_fw_rf_serial_read); void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data) { RT_ASSERT(false, "deprecated!\n"); } EXPORT_SYMBOL(_rtl92c_phy_fw_rf_serial_write); u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; u32 newoffset; u32 tmplong, tmplong2; u8 rfpi_enable = 0; u32 retvalue; offset &= 0x3f; newoffset = offset; if (RT_CANNOT_IO(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "return all one\n"); return 0xFFFFFFFF; } tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD); if (rfpath == RF90_PATH_A) tmplong2 = tmplong; else tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD); tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) | (newoffset << 23) | BLSSIREADEDGE; rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, tmplong & (~BLSSIREADEDGE)); mdelay(1); rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2); mdelay(1); rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, tmplong | BLSSIREADEDGE); mdelay(1); if (rfpath == RF90_PATH_A) rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, BIT(8)); else if (rfpath == RF90_PATH_B) rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1, BIT(8)); if (rfpi_enable) retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readbackpi, BLSSIREADBACKDATA); else retvalue = rtl_get_bbreg(hw, pphyreg->rflssi_readback, BLSSIREADBACKDATA); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x]=0x%x\n", rfpath, pphyreg->rflssi_readback, retvalue); return retvalue; } EXPORT_SYMBOL(_rtl92c_phy_rf_serial_read); void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data) { u32 data_and_addr; u32 newoffset; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; if (RT_CANNOT_IO(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "stop\n"); return; } offset &= 0x3f; newoffset = offset; data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff; rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n", rfpath, pphyreg->rf3wire_offset, data_and_addr); } EXPORT_SYMBOL(_rtl92c_phy_rf_serial_write); u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask) { u32 i; for (i = 0; i <= 31; i++) { if ((bitmask >> i) & 0x1) break; } return i; } EXPORT_SYMBOL(_rtl92c_phy_calculate_bit_shift); static void _rtl92c_phy_bb_config_1t(struct ieee80211_hw *hw) { rtl_set_bbreg(hw, RFPGA0_TXINFO, 0x3, 0x2); rtl_set_bbreg(hw, RFPGA1_TXINFO, 0x300033, 0x200022); rtl_set_bbreg(hw, RCCK0_AFESETTING, MASKBYTE3, 0x45); rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x23); rtl_set_bbreg(hw, ROFDM0_AGCPARAMETER1, 0x30, 0x1); rtl_set_bbreg(hw, 0xe74, 0x0c000000, 0x2); rtl_set_bbreg(hw, 0xe78, 0x0c000000, 0x2); rtl_set_bbreg(hw, 0xe7c, 0x0c000000, 0x2); rtl_set_bbreg(hw, 0xe80, 0x0c000000, 0x2); rtl_set_bbreg(hw, 0xe88, 0x0c000000, 0x2); } bool rtl92c_phy_rf_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); return rtlpriv->cfg->ops->phy_rf6052_config(hw); } EXPORT_SYMBOL(rtl92c_phy_rf_config); bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); bool rtstatus; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "==>\n"); rtstatus = rtlpriv->cfg->ops->config_bb_with_headerfile(hw, BASEBAND_CONFIG_PHY_REG); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write BB Reg Fail!!\n"); return false; } if (rtlphy->rf_type == RF_1T2R) { _rtl92c_phy_bb_config_1t(hw); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Config to 1T!!\n"); } if (rtlefuse->autoload_failflag == false) { rtlphy->pwrgroup_cnt = 0; rtstatus = rtlpriv->cfg->ops->config_bb_with_pgheaderfile(hw, BASEBAND_CONFIG_PHY_REG); } if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "BB_PG Reg Fail!!\n"); return false; } rtstatus = rtlpriv->cfg->ops->config_bb_with_headerfile(hw, BASEBAND_CONFIG_AGC_TAB); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "AGC Table Fail\n"); return false; } rtlphy->cck_high_power = (bool) (rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, 0x200)); return true; } EXPORT_SYMBOL(_rtl92c_phy_bb8192c_config_parafile); void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); int index; if (regaddr == RTXAGC_A_RATE18_06) index = 0; else if (regaddr == RTXAGC_A_RATE54_24) index = 1; else if (regaddr == RTXAGC_A_CCK1_MCS32) index = 6; else if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0xffffff00) index = 7; else if (regaddr == RTXAGC_A_MCS03_MCS00) index = 2; else if (regaddr == RTXAGC_A_MCS07_MCS04) index = 3; else if (regaddr == RTXAGC_A_MCS11_MCS08) index = 4; else if (regaddr == RTXAGC_A_MCS15_MCS12) index = 5; else if (regaddr == RTXAGC_B_RATE18_06) index = 8; else if (regaddr == RTXAGC_B_RATE54_24) index = 9; else if (regaddr == RTXAGC_B_CCK1_55_MCS32) index = 14; else if (regaddr == RTXAGC_B_CCK11_A_CCK2_11 && bitmask == 0x000000ff) index = 15; else if (regaddr == RTXAGC_B_MCS03_MCS00) index = 10; else if (regaddr == RTXAGC_B_MCS07_MCS04) index = 11; else if (regaddr == RTXAGC_B_MCS11_MCS08) index = 12; else if (regaddr == RTXAGC_B_MCS15_MCS12) index = 13; else return; rtlphy->MCS_TXPWR[rtlphy->pwrgroup_cnt][index] = data; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "MCSTxPowerLevelOriginalOffset[%d][%d] = 0x%x\n", rtlphy->pwrgroup_cnt, index, rtlphy->MCS_TXPWR[rtlphy->pwrgroup_cnt][index]); if (index == 13) rtlphy->pwrgroup_cnt++; } EXPORT_SYMBOL(_rtl92c_store_pwrIndex_diffrate_offset); void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); rtlphy->default_initialgain[0] = (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[1] = (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[2] = (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[3] = (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x\n", rtlphy->default_initialgain[0], rtlphy->default_initialgain[1], rtlphy->default_initialgain[2], rtlphy->default_initialgain[3]); rtlphy->framesync = (u8) rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3, MASKBYTE0); rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2, MASKDWORD); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Default framesync (0x%x) = 0x%x\n", ROFDM0_RXDETECTOR3, rtlphy->framesync); } void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW; rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW; rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW; rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW; rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB; rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB; rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB; rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB; rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE; rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset = RFPGA0_XA_LSSIPARAMETER; rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset = RFPGA0_XB_LSSIPARAMETER; rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = rFPGA0_XAB_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = rFPGA0_XAB_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = rFPGA0_XCD_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = rFPGA0_XCD_RFPARAMETER; rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE; rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE; rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE; rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE; rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1; rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1; rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2; rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2; rtlphy->phyreg_def[RF90_PATH_A].rfswitch_control = RFPGA0_XAB_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_B].rfswitch_control = RFPGA0_XAB_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_C].rfswitch_control = RFPGA0_XCD_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_D].rfswitch_control = RFPGA0_XCD_SWITCHCONTROL; rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1; rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1; rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1; rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1; rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2; rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2; rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2; rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2; rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbalance = ROFDM0_XARXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbalance = ROFDM0_XBRXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbalance = ROFDM0_XCRXIQIMBANLANCE; rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbalance = ROFDM0_XDRXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE; rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE; rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE; rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE; rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbalance = ROFDM0_XATXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbalance = ROFDM0_XBTXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbalance = ROFDM0_XCTXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbalance = ROFDM0_XDTXIQIMBALANCE; rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATXAFE; rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTXAFE; rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTXAFE; rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTXAFE; rtlphy->phyreg_def[RF90_PATH_A].rflssi_readback = RFPGA0_XA_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_B].rflssi_readback = RFPGA0_XB_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_C].rflssi_readback = RFPGA0_XC_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_D].rflssi_readback = RFPGA0_XD_LSSIREADBACK; rtlphy->phyreg_def[RF90_PATH_A].rflssi_readbackpi = TRANSCEIVEA_HSPI_READBACK; rtlphy->phyreg_def[RF90_PATH_B].rflssi_readbackpi = TRANSCEIVEB_HSPI_READBACK; } EXPORT_SYMBOL(_rtl92c_phy_init_bb_rf_register_definition); void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 txpwr_level; long txpwr_dbm; txpwr_level = rtlphy->cur_cck_txpwridx; txpwr_dbm = _rtl92c_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, txpwr_level); txpwr_level = rtlphy->cur_ofdm24g_txpwridx + rtlefuse->legacy_ht_txpowerdiff; if (_rtl92c_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > txpwr_dbm) txpwr_dbm = _rtl92c_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level); txpwr_level = rtlphy->cur_ofdm24g_txpwridx; if (_rtl92c_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, txpwr_level) > txpwr_dbm) txpwr_dbm = _rtl92c_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, txpwr_level); *powerlevel = txpwr_dbm; } static void _rtl92c_get_txpower_index(struct ieee80211_hw *hw, u8 channel, u8 *cckpowerlevel, u8 *ofdmpowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 index = (channel - 1); cckpowerlevel[RF90_PATH_A] = rtlefuse->txpwrlevel_cck[RF90_PATH_A][index]; cckpowerlevel[RF90_PATH_B] = rtlefuse->txpwrlevel_cck[RF90_PATH_B][index]; if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_1T1R) { ofdmpowerlevel[RF90_PATH_A] = rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_A][index]; ofdmpowerlevel[RF90_PATH_B] = rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_B][index]; } else if (get_rf_type(rtlphy) == RF_2T2R) { ofdmpowerlevel[RF90_PATH_A] = rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_A][index]; ofdmpowerlevel[RF90_PATH_B] = rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_B][index]; } } static void _rtl92c_ccxpower_index_check(struct ieee80211_hw *hw, u8 channel, u8 *cckpowerlevel, u8 *ofdmpowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); rtlphy->cur_cck_txpwridx = cckpowerlevel[0]; rtlphy->cur_ofdm24g_txpwridx = ofdmpowerlevel[0]; } void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtlpriv); u8 cckpowerlevel[2], ofdmpowerlevel[2]; if (!rtlefuse->txpwr_fromeprom) return; _rtl92c_get_txpower_index(hw, channel, &cckpowerlevel[0], &ofdmpowerlevel[0]); _rtl92c_ccxpower_index_check(hw, channel, &cckpowerlevel[0], &ofdmpowerlevel[0]); rtlpriv->cfg->ops->phy_rf6052_set_cck_txpower(hw, &cckpowerlevel[0]); rtlpriv->cfg->ops->phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel); } EXPORT_SYMBOL(rtl92c_phy_set_txpower_level); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 idx; u8 rf_path; u8 ccktxpwridx = _rtl92c_phy_dbm_to_txpwr_Idx(hw, WIRELESS_MODE_B, power_indbm); u8 ofdmtxpwridx = _rtl92c_phy_dbm_to_txpwr_Idx(hw, WIRELESS_MODE_N_24G, power_indbm); if (ofdmtxpwridx - rtlefuse->legacy_ht_txpowerdiff > 0) ofdmtxpwridx -= rtlefuse->legacy_ht_txpowerdiff; else ofdmtxpwridx = 0; RT_TRACE(rtlpriv, COMP_TXAGC, DBG_TRACE, "%lx dBm, ccktxpwridx = %d, ofdmtxpwridx = %d\n", power_indbm, ccktxpwridx, ofdmtxpwridx); for (idx = 0; idx < 14; idx++) { for (rf_path = 0; rf_path < 2; rf_path++) { rtlefuse->txpwrlevel_cck[rf_path][idx] = ccktxpwridx; rtlefuse->txpwrlevel_ht40_1s[rf_path][idx] = ofdmtxpwridx; rtlefuse->txpwrlevel_ht40_2s[rf_path][idx] = ofdmtxpwridx; } } rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); return true; } EXPORT_SYMBOL(rtl92c_phy_update_txpower_dbm); u8 _rtl92c_phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, enum wireless_mode wirelessmode, long power_indbm) { u8 txpwridx; long offset; switch (wirelessmode) { case WIRELESS_MODE_B: offset = -7; break; case WIRELESS_MODE_G: case WIRELESS_MODE_N_24G: offset = -8; break; default: offset = -8; break; } if ((power_indbm - offset) > 0) txpwridx = (u8) ((power_indbm - offset) * 2); else txpwridx = 0; if (txpwridx > MAX_TXPWR_IDX_NMODE_92S) txpwridx = MAX_TXPWR_IDX_NMODE_92S; return txpwridx; } EXPORT_SYMBOL(_rtl92c_phy_dbm_to_txpwr_Idx); long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, enum wireless_mode wirelessmode, u8 txpwridx) { long offset; long pwrout_dbm; switch (wirelessmode) { case WIRELESS_MODE_B: offset = -7; break; case WIRELESS_MODE_G: case WIRELESS_MODE_N_24G: offset = -8; break; default: offset = -8; break; } pwrout_dbm = txpwridx / 2 + offset; return pwrout_dbm; } EXPORT_SYMBOL(_rtl92c_phy_txpwr_idx_to_dbm); void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); enum io_type iotype; if (!is_hal_stop(rtlhal)) { switch (operation) { case SCAN_OPT_BACKUP: iotype = IO_CMD_PAUSE_DM_BY_SCAN; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, (u8 *)&iotype); break; case SCAN_OPT_RESTORE: iotype = IO_CMD_RESUME_DM_BY_SCAN; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, (u8 *)&iotype); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Unknown Scan Backup operation\n"); break; } } } EXPORT_SYMBOL(rtl92c_phy_scan_operation_backup); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 tmp_bw = rtlphy->current_chan_bw; if (rtlphy->set_bwmode_inprogress) return; rtlphy->set_bwmode_inprogress = true; if ((!is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtlpriv->cfg->ops->phy_set_bw_mode_callback(hw); } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "FALSE driver sleep or unload\n"); rtlphy->set_bwmode_inprogress = false; rtlphy->current_chan_bw = tmp_bw; } } EXPORT_SYMBOL(rtl92c_phy_set_bw_mode); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 delay; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "switch to channel%d\n", rtlphy->current_channel); if (is_hal_stop(rtlhal)) return; do { if (!rtlphy->sw_chnl_inprogress) break; if (!_rtl92c_phy_sw_chnl_step_by_step (hw, rtlphy->current_channel, &rtlphy->sw_chnl_stage, &rtlphy->sw_chnl_step, &delay)) { if (delay > 0) mdelay(delay); else continue; } else { rtlphy->sw_chnl_inprogress = false; } break; } while (true); RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); } EXPORT_SYMBOL(rtl92c_phy_sw_chnl_callback); u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (rtlphy->sw_chnl_inprogress) return 0; if (rtlphy->set_bwmode_inprogress) return 0; RT_ASSERT((rtlphy->current_channel <= 14), "WIRELESS_MODE_G but channel>14\n"); rtlphy->sw_chnl_inprogress = true; rtlphy->sw_chnl_stage = 0; rtlphy->sw_chnl_step = 0; if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtl92c_phy_sw_chnl_callback(hw); RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, "sw_chnl_inprogress false schdule workitem\n"); rtlphy->sw_chnl_inprogress = false; } else { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, "sw_chnl_inprogress false driver sleep or unload\n"); rtlphy->sw_chnl_inprogress = false; } return 1; } EXPORT_SYMBOL(rtl92c_phy_sw_chnl); static bool _rtl92c_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, u32 cmdtableidx, u32 cmdtablesz, enum swchnlcmd_id cmdid, u32 para1, u32 para2, u32 msdelay) { struct swchnlcmd *pcmd; if (cmdtable == NULL) { RT_ASSERT(false, "cmdtable cannot be NULL\n"); return false; } if (cmdtableidx >= cmdtablesz) return false; pcmd = cmdtable + cmdtableidx; pcmd->cmdid = cmdid; pcmd->para1 = para1; pcmd->para2 = para2; pcmd->msdelay = msdelay; return true; } bool _rtl92c_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, u8 *stage, u8 *step, u32 *delay) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct swchnlcmd precommoncmd[MAX_PRECMD_CNT]; u32 precommoncmdcnt; struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT]; u32 postcommoncmdcnt; struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT]; u32 rfdependcmdcnt; struct swchnlcmd *currentcmd = NULL; u8 rfpath; u8 num_total_rfpath = rtlphy->num_total_rfpath; precommoncmdcnt = 0; _rtl92c_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL, 0, 0, 0); _rtl92c_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, MAX_PRECMD_CNT, CMDID_END, 0, 0, 0); postcommoncmdcnt = 0; _rtl92c_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++, MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0); rfdependcmdcnt = 0; RT_ASSERT((channel >= 1 && channel <= 14), "invalid channel for Zebra: %d\n", channel); _rtl92c_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG, RF_CHNLBW, channel, 10); _rtl92c_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0); do { switch (*stage) { case 0: currentcmd = &precommoncmd[*step]; break; case 1: currentcmd = &rfdependcmd[*step]; break; case 2: currentcmd = &postcommoncmd[*step]; break; } if (currentcmd->cmdid == CMDID_END) { if ((*stage) == 2) { return true; } else { (*stage)++; (*step) = 0; continue; } } switch (currentcmd->cmdid) { case CMDID_SET_TXPOWEROWER_LEVEL: rtl92c_phy_set_txpower_level(hw, channel); break; case CMDID_WRITEPORT_ULONG: rtl_write_dword(rtlpriv, currentcmd->para1, currentcmd->para2); break; case CMDID_WRITEPORT_USHORT: rtl_write_word(rtlpriv, currentcmd->para1, (u16) currentcmd->para2); break; case CMDID_WRITEPORT_UCHAR: rtl_write_byte(rtlpriv, currentcmd->para1, (u8) currentcmd->para2); break; case CMDID_RF_WRITEREG: for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) { rtlphy->rfreg_chnlval[rfpath] = ((rtlphy->rfreg_chnlval[rfpath] & 0xfffffc00) | currentcmd->para2); rtl_set_rfreg(hw, (enum radio_path)rfpath, currentcmd->para1, RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[rfpath]); } break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } break; } while (true); (*delay) = currentcmd->msdelay; (*step)++; return false; } bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, u32 rfpath) { return true; } EXPORT_SYMBOL(rtl8192_phy_check_is_legal_rfpath); static u8 _rtl92c_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb) { u32 reg_eac, reg_e94, reg_e9c, reg_ea4; u8 result = 0x00; rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c1f); rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x10008c1f); rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82140102); rtl_set_bbreg(hw, 0xe3c, MASKDWORD, config_pathb ? 0x28160202 : 0x28160502); if (config_pathb) { rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x10008c22); rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x10008c22); rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82140102); rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x28160202); } rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x001028d1); rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000); rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000); mdelay(IQK_DELAY_TIME); reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); reg_e94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD); reg_e9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD); reg_ea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD); if (!(reg_eac & BIT(28)) && (((reg_e94 & 0x03FF0000) >> 16) != 0x142) && (((reg_e9c & 0x03FF0000) >> 16) != 0x42)) result |= 0x01; else return result; if (!(reg_eac & BIT(27)) && (((reg_ea4 & 0x03FF0000) >> 16) != 0x132) && (((reg_eac & 0x03FF0000) >> 16) != 0x36)) result |= 0x02; return result; } static u8 _rtl92c_phy_path_b_iqk(struct ieee80211_hw *hw) { u32 reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc; u8 result = 0x00; rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000002); rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000000); mdelay(IQK_DELAY_TIME); reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); reg_eb4 = rtl_get_bbreg(hw, 0xeb4, MASKDWORD); reg_ebc = rtl_get_bbreg(hw, 0xebc, MASKDWORD); reg_ec4 = rtl_get_bbreg(hw, 0xec4, MASKDWORD); reg_ecc = rtl_get_bbreg(hw, 0xecc, MASKDWORD); if (!(reg_eac & BIT(31)) && (((reg_eb4 & 0x03FF0000) >> 16) != 0x142) && (((reg_ebc & 0x03FF0000) >> 16) != 0x42)) result |= 0x01; else return result; if (!(reg_eac & BIT(30)) && (((reg_ec4 & 0x03FF0000) >> 16) != 0x132) && (((reg_ecc & 0x03FF0000) >> 16) != 0x36)) result |= 0x02; return result; } static void _rtl92c_phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw, bool iqk_ok, long result[][8], u8 final_candidate, bool btxonly) { u32 oldval_0, x, tx0_a, reg; long y, tx0_c; if (final_candidate == 0xFF) { return; } else if (iqk_ok) { oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD) >> 22) & 0x3FF; x = result[final_candidate][0]; if ((x & 0x00000200) != 0) x = x | 0xFFFFFC00; tx0_a = (x * oldval_0) >> 8; rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x3FF, tx0_a); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31), ((x * oldval_0 >> 7) & 0x1)); y = result[final_candidate][1]; if ((y & 0x00000200) != 0) y = y | 0xFFFFFC00; tx0_c = (y * oldval_0) >> 8; rtl_set_bbreg(hw, ROFDM0_XCTXAFE, 0xF0000000, ((tx0_c & 0x3C0) >> 6)); rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x003F0000, (tx0_c & 0x3F)); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(29), ((y * oldval_0 >> 7) & 0x1)); if (btxonly) return; reg = result[final_candidate][2]; rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg); reg = result[final_candidate][3] & 0x3F; rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg); reg = (result[final_candidate][3] >> 6) & 0xF; rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg); } } static void _rtl92c_phy_path_b_fill_iqk_matrix(struct ieee80211_hw *hw, bool iqk_ok, long result[][8], u8 final_candidate, bool btxonly) { u32 oldval_1, x, tx1_a, reg; long y, tx1_c; if (final_candidate == 0xFF) { return; } else if (iqk_ok) { oldval_1 = (rtl_get_bbreg(hw, ROFDM0_XBTXIQIMBALANCE, MASKDWORD) >> 22) & 0x3FF; x = result[final_candidate][4]; if ((x & 0x00000200) != 0) x = x | 0xFFFFFC00; tx1_a = (x * oldval_1) >> 8; rtl_set_bbreg(hw, ROFDM0_XBTXIQIMBALANCE, 0x3FF, tx1_a); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(27), ((x * oldval_1 >> 7) & 0x1)); y = result[final_candidate][5]; if ((y & 0x00000200) != 0) y = y | 0xFFFFFC00; tx1_c = (y * oldval_1) >> 8; rtl_set_bbreg(hw, ROFDM0_XDTXAFE, 0xF0000000, ((tx1_c & 0x3C0) >> 6)); rtl_set_bbreg(hw, ROFDM0_XBTXIQIMBALANCE, 0x003F0000, (tx1_c & 0x3F)); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(25), ((y * oldval_1 >> 7) & 0x1)); if (btxonly) return; reg = result[final_candidate][6]; rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, 0x3FF, reg); reg = result[final_candidate][7] & 0x3F; rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, 0xFC00, reg); reg = (result[final_candidate][7] >> 6) & 0xF; rtl_set_bbreg(hw, ROFDM0_AGCRSSITABLE, 0x0000F000, reg); } } static void _rtl92c_phy_save_adda_registers(struct ieee80211_hw *hw, u32 *addareg, u32 *addabackup, u32 registernum) { u32 i; for (i = 0; i < registernum; i++) addabackup[i] = rtl_get_bbreg(hw, addareg[i], MASKDWORD); } static void _rtl92c_phy_save_mac_registers(struct ieee80211_hw *hw, u32 *macreg, u32 *macbackup) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]); macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]); } static void _rtl92c_phy_reload_adda_registers(struct ieee80211_hw *hw, u32 *addareg, u32 *addabackup, u32 regiesternum) { u32 i; for (i = 0; i < regiesternum; i++) rtl_set_bbreg(hw, addareg[i], MASKDWORD, addabackup[i]); } static void _rtl92c_phy_reload_mac_registers(struct ieee80211_hw *hw, u32 *macreg, u32 *macbackup) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]); rtl_write_dword(rtlpriv, macreg[i], macbackup[i]); } static void _rtl92c_phy_path_adda_on(struct ieee80211_hw *hw, u32 *addareg, bool is_patha_on, bool is2t) { u32 pathOn; u32 i; pathOn = is_patha_on ? 0x04db25a4 : 0x0b1b25a4; if (false == is2t) { pathOn = 0x0bdb25a0; rtl_set_bbreg(hw, addareg[0], MASKDWORD, 0x0b1b25a0); } else { rtl_set_bbreg(hw, addareg[0], MASKDWORD, pathOn); } for (i = 1; i < IQK_ADDA_REG_NUM; i++) rtl_set_bbreg(hw, addareg[i], MASKDWORD, pathOn); } static void _rtl92c_phy_mac_setting_calibration(struct ieee80211_hw *hw, u32 *macreg, u32 *macbackup) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; rtl_write_byte(rtlpriv, macreg[0], 0x3F); for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++) rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(3)))); rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5)))); } static void _rtl92c_phy_path_a_standby(struct ieee80211_hw *hw) { rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0); rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); } static void _rtl92c_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode) { u32 mode; mode = pi_mode ? 0x01000100 : 0x01000000; rtl_set_bbreg(hw, 0x820, MASKDWORD, mode); rtl_set_bbreg(hw, 0x828, MASKDWORD, mode); } static bool _rtl92c_phy_simularity_compare(struct ieee80211_hw *hw, long result[][8], u8 c1, u8 c2) { u32 i, j, diff, simularity_bitmap, bound; struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 final_candidate[2] = { 0xFF, 0xFF }; bool bresult = true, is2t = IS_92C_SERIAL(rtlhal->version); if (is2t) bound = 8; else bound = 4; simularity_bitmap = 0; for (i = 0; i < bound; i++) { diff = (result[c1][i] > result[c2][i]) ? (result[c1][i] - result[c2][i]) : (result[c2][i] - result[c1][i]); if (diff > MAX_TOLERANCE) { if ((i == 2 || i == 6) && !simularity_bitmap) { if (result[c1][i] + result[c1][i + 1] == 0) final_candidate[(i / 4)] = c2; else if (result[c2][i] + result[c2][i + 1] == 0) final_candidate[(i / 4)] = c1; else simularity_bitmap = simularity_bitmap | (1 << i); } else simularity_bitmap = simularity_bitmap | (1 << i); } } if (simularity_bitmap == 0) { for (i = 0; i < (bound / 4); i++) { if (final_candidate[i] != 0xFF) { for (j = i * 4; j < (i + 1) * 4 - 2; j++) result[3][j] = result[final_candidate[i]][j]; bresult = false; } } return bresult; } else if (!(simularity_bitmap & 0x0F)) { for (i = 0; i < 4; i++) result[3][i] = result[c1][i]; return false; } else if (!(simularity_bitmap & 0xF0) && is2t) { for (i = 4; i < 8; i++) result[3][i] = result[c1][i]; return false; } else { return false; } } static void _rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], u8 t, bool is2t) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); u32 i; u8 patha_ok, pathb_ok; u32 adda_reg[IQK_ADDA_REG_NUM] = { 0x85c, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec }; u32 iqk_mac_reg[IQK_MAC_REG_NUM] = { 0x522, 0x550, 0x551, 0x040 }; const u32 retrycount = 2; if (t == 0) { /* dummy read */ rtl_get_bbreg(hw, 0x800, MASKDWORD); _rtl92c_phy_save_adda_registers(hw, adda_reg, rtlphy->adda_backup, 16); _rtl92c_phy_save_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); } _rtl92c_phy_path_adda_on(hw, adda_reg, true, is2t); if (t == 0) { rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, BIT(8)); } if (!rtlphy->rfpi_enable) _rtl92c_phy_pi_mode_switch(hw, true); if (t == 0) { rtlphy->reg_c04 = rtl_get_bbreg(hw, 0xc04, MASKDWORD); rtlphy->reg_c08 = rtl_get_bbreg(hw, 0xc08, MASKDWORD); rtlphy->reg_874 = rtl_get_bbreg(hw, 0x874, MASKDWORD); } rtl_set_bbreg(hw, 0xc04, MASKDWORD, 0x03a05600); rtl_set_bbreg(hw, 0xc08, MASKDWORD, 0x000800e4); rtl_set_bbreg(hw, 0x874, MASKDWORD, 0x22204000); if (is2t) { rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00010000); } _rtl92c_phy_mac_setting_calibration(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x00080000); if (is2t) rtl_set_bbreg(hw, 0xb6c, MASKDWORD, 0x00080000); rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x01007c00); rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x01004800); for (i = 0; i < retrycount; i++) { patha_ok = _rtl92c_phy_path_a_iqk(hw, is2t); if (patha_ok == 0x03) { result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & 0x3FF0000) >> 16; result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & 0x3FF0000) >> 16; result[t][2] = (rtl_get_bbreg(hw, 0xea4, MASKDWORD) & 0x3FF0000) >> 16; result[t][3] = (rtl_get_bbreg(hw, 0xeac, MASKDWORD) & 0x3FF0000) >> 16; break; } else if (i == (retrycount - 1) && patha_ok == 0x01) result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & 0x3FF0000) >> 16; result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & 0x3FF0000) >> 16; } if (is2t) { _rtl92c_phy_path_a_standby(hw); _rtl92c_phy_path_adda_on(hw, adda_reg, false, is2t); for (i = 0; i < retrycount; i++) { pathb_ok = _rtl92c_phy_path_b_iqk(hw); if (pathb_ok == 0x03) { result[t][4] = (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) & 0x3FF0000) >> 16; result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) & 0x3FF0000) >> 16; result[t][6] = (rtl_get_bbreg(hw, 0xec4, MASKDWORD) & 0x3FF0000) >> 16; result[t][7] = (rtl_get_bbreg(hw, 0xecc, MASKDWORD) & 0x3FF0000) >> 16; break; } else if (i == (retrycount - 1) && pathb_ok == 0x01) { result[t][4] = (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) & 0x3FF0000) >> 16; } result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) & 0x3FF0000) >> 16; } } rtl_set_bbreg(hw, 0xc04, MASKDWORD, rtlphy->reg_c04); rtl_set_bbreg(hw, 0x874, MASKDWORD, rtlphy->reg_874); rtl_set_bbreg(hw, 0xc08, MASKDWORD, rtlphy->reg_c08); rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0); rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00032ed3); if (is2t) rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00032ed3); if (t != 0) { if (!rtlphy->rfpi_enable) _rtl92c_phy_pi_mode_switch(hw, false); _rtl92c_phy_reload_adda_registers(hw, adda_reg, rtlphy->adda_backup, 16); _rtl92c_phy_reload_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); } } static void _rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta, bool is2t) { #if 0 /* This routine is deliberately dummied out for later fixes */ struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u32 reg_d[PATH_NUM]; u32 tmpreg, index, offset, path, i, pathbound = PATH_NUM, apkbound; u32 bb_backup[APK_BB_REG_NUM]; u32 bb_reg[APK_BB_REG_NUM] = { 0x904, 0xc04, 0x800, 0xc08, 0x874 }; u32 bb_ap_mode[APK_BB_REG_NUM] = { 0x00000020, 0x00a05430, 0x02040000, 0x000800e4, 0x00204000 }; u32 bb_normal_ap_mode[APK_BB_REG_NUM] = { 0x00000020, 0x00a05430, 0x02040000, 0x000800e4, 0x22204000 }; u32 afe_backup[APK_AFE_REG_NUM]; u32 afe_reg[APK_AFE_REG_NUM] = { 0x85c, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec }; u32 mac_backup[IQK_MAC_REG_NUM]; u32 mac_reg[IQK_MAC_REG_NUM] = { 0x522, 0x550, 0x551, 0x040 }; u32 apk_rf_init_value[PATH_NUM][APK_BB_REG_NUM] = { {0x0852c, 0x1852c, 0x5852c, 0x1852c, 0x5852c}, {0x2852e, 0x0852e, 0x3852e, 0x0852e, 0x0852e} }; u32 apk_normal_rf_init_value[PATH_NUM][APK_BB_REG_NUM] = { {0x0852c, 0x0a52c, 0x3a52c, 0x5a52c, 0x5a52c}, {0x0852c, 0x0a52c, 0x5a52c, 0x5a52c, 0x5a52c} }; u32 apk_rf_value_0[PATH_NUM][APK_BB_REG_NUM] = { {0x52019, 0x52014, 0x52013, 0x5200f, 0x5208d}, {0x5201a, 0x52019, 0x52016, 0x52033, 0x52050} }; u32 apk_normal_rf_value_0[PATH_NUM][APK_BB_REG_NUM] = { {0x52019, 0x52017, 0x52010, 0x5200d, 0x5206a}, {0x52019, 0x52017, 0x52010, 0x5200d, 0x5206a} }; u32 afe_on_off[PATH_NUM] = { 0x04db25a4, 0x0b1b25a4 }; const u32 apk_offset[PATH_NUM] = { 0xb68, 0xb6c }; u32 apk_normal_offset[PATH_NUM] = { 0xb28, 0xb98 }; u32 apk_value[PATH_NUM] = { 0x92fc0000, 0x12fc0000 }; u32 apk_normal_value[PATH_NUM] = { 0x92680000, 0x12680000 }; const char apk_delta_mapping[APK_BB_REG_NUM][13] = { {-4, -3, -2, -2, -1, -1, 0, 1, 2, 3, 4, 5, 6}, {-4, -3, -2, -2, -1, -1, 0, 1, 2, 3, 4, 5, 6}, {-6, -4, -2, -2, -1, -1, 0, 1, 2, 3, 4, 5, 6}, {-1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6}, {-11, -9, -7, -5, -3, -1, 0, 0, 0, 0, 0, 0, 0} }; const u32 apk_normal_setting_value_1[13] = { 0x01017018, 0xf7ed8f84, 0x1b1a1816, 0x2522201e, 0x322e2b28, 0x433f3a36, 0x5b544e49, 0x7b726a62, 0xa69a8f84, 0xdfcfc0b3, 0x12680000, 0x00880000, 0x00880000 }; const u32 apk_normal_setting_value_2[16] = { 0x01c7021d, 0x01670183, 0x01000123, 0x00bf00e2, 0x008d00a3, 0x0068007b, 0x004d0059, 0x003a0042, 0x002b0031, 0x001f0025, 0x0017001b, 0x00110014, 0x000c000f, 0x0009000b, 0x00070008, 0x00050006 }; u32 apk_result[PATH_NUM][APK_BB_REG_NUM]; long bb_offset, delta_v, delta_offset; if (!is2t) pathbound = 1; return; for (index = 0; index < PATH_NUM; index++) { apk_offset[index] = apk_normal_offset[index]; apk_value[index] = apk_normal_value[index]; afe_on_off[index] = 0x6fdb25a4; } for (index = 0; index < APK_BB_REG_NUM; index++) { for (path = 0; path < pathbound; path++) { apk_rf_init_value[path][index] = apk_normal_rf_init_value[path][index]; apk_rf_value_0[path][index] = apk_normal_rf_value_0[path][index]; } bb_ap_mode[index] = bb_normal_ap_mode[index]; apkbound = 6; } for (index = 0; index < APK_BB_REG_NUM; index++) { if (index == 0) continue; bb_backup[index] = rtl_get_bbreg(hw, bb_reg[index], MASKDWORD); } _rtl92c_phy_save_mac_registers(hw, mac_reg, mac_backup); _rtl92c_phy_save_adda_registers(hw, afe_reg, afe_backup, 16); for (path = 0; path < pathbound; path++) { if (path == RF90_PATH_A) { offset = 0xb00; for (index = 0; index < 11; index++) { rtl_set_bbreg(hw, offset, MASKDWORD, apk_normal_setting_value_1 [index]); offset += 0x04; } rtl_set_bbreg(hw, 0xb98, MASKDWORD, 0x12680000); offset = 0xb68; for (; index < 13; index++) { rtl_set_bbreg(hw, offset, MASKDWORD, apk_normal_setting_value_1 [index]); offset += 0x04; } rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x40000000); offset = 0xb00; for (index = 0; index < 16; index++) { rtl_set_bbreg(hw, offset, MASKDWORD, apk_normal_setting_value_2 [index]); offset += 0x04; } rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x00000000); } else if (path == RF90_PATH_B) { offset = 0xb70; for (index = 0; index < 10; index++) { rtl_set_bbreg(hw, offset, MASKDWORD, apk_normal_setting_value_1 [index]); offset += 0x04; } rtl_set_bbreg(hw, 0xb28, MASKDWORD, 0x12680000); rtl_set_bbreg(hw, 0xb98, MASKDWORD, 0x12680000); offset = 0xb68; index = 11; for (; index < 13; index++) { rtl_set_bbreg(hw, offset, MASKDWORD, apk_normal_setting_value_1 [index]); offset += 0x04; } rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x40000000); offset = 0xb60; for (index = 0; index < 16; index++) { rtl_set_bbreg(hw, offset, MASKDWORD, apk_normal_setting_value_2 [index]); offset += 0x04; } rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x00000000); } reg_d[path] = rtl_get_rfreg(hw, (enum radio_path)path, 0xd, MASKDWORD); for (index = 0; index < APK_AFE_REG_NUM; index++) rtl_set_bbreg(hw, afe_reg[index], MASKDWORD, afe_on_off[path]); if (path == RF90_PATH_A) { for (index = 0; index < APK_BB_REG_NUM; index++) { if (index == 0) continue; rtl_set_bbreg(hw, bb_reg[index], MASKDWORD, bb_ap_mode[index]); } } _rtl92c_phy_mac_setting_calibration(hw, mac_reg, mac_backup); if (path == 0) { rtl_set_rfreg(hw, RF90_PATH_B, 0x0, MASKDWORD, 0x10000); } else { rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASKDWORD, 0x10000); rtl_set_rfreg(hw, RF90_PATH_A, 0x10, MASKDWORD, 0x1000f); rtl_set_rfreg(hw, RF90_PATH_A, 0x11, MASKDWORD, 0x20103); } delta_offset = ((delta + 14) / 2); if (delta_offset < 0) delta_offset = 0; else if (delta_offset > 12) delta_offset = 12; for (index = 0; index < APK_BB_REG_NUM; index++) { if (index != 1) continue; tmpreg = apk_rf_init_value[path][index]; if (!rtlefuse->apk_thermalmeterignore) { bb_offset = (tmpreg & 0xF0000) >> 16; if (!(tmpreg & BIT(15))) bb_offset = -bb_offset; delta_v = apk_delta_mapping[index][delta_offset]; bb_offset += delta_v; if (bb_offset < 0) { tmpreg = tmpreg & (~BIT(15)); bb_offset = -bb_offset; } else { tmpreg = tmpreg | BIT(15); } tmpreg = (tmpreg & 0xFFF0FFFF) | (bb_offset << 16); } rtl_set_rfreg(hw, (enum radio_path)path, 0xc, MASKDWORD, 0x8992e); rtl_set_rfreg(hw, (enum radio_path)path, 0x0, MASKDWORD, apk_rf_value_0[path][index]); rtl_set_rfreg(hw, (enum radio_path)path, 0xd, MASKDWORD, tmpreg); i = 0; do { rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80000000); rtl_set_bbreg(hw, apk_offset[path], MASKDWORD, apk_value[0]); RTPRINT(rtlpriv, FINIT, INIT_IQK, ("PHY_APCalibrate() offset 0x%x " "value 0x%x\n", apk_offset[path], rtl_get_bbreg(hw, apk_offset[path], MASKDWORD))); mdelay(3); rtl_set_bbreg(hw, apk_offset[path], MASKDWORD, apk_value[1]); RTPRINT(rtlpriv, FINIT, INIT_IQK, ("PHY_APCalibrate() offset 0x%x " "value 0x%x\n", apk_offset[path], rtl_get_bbreg(hw, apk_offset[path], MASKDWORD))); mdelay(20); rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x00000000); if (path == RF90_PATH_A) tmpreg = rtl_get_bbreg(hw, 0xbd8, 0x03E00000); else tmpreg = rtl_get_bbreg(hw, 0xbd8, 0xF8000000); RTPRINT(rtlpriv, FINIT, INIT_IQK, ("PHY_APCalibrate() offset " "0xbd8[25:21] %x\n", tmpreg)); i++; } while (tmpreg > apkbound && i < 4); apk_result[path][index] = tmpreg; } } _rtl92c_phy_reload_mac_registers(hw, mac_reg, mac_backup); for (index = 0; index < APK_BB_REG_NUM; index++) { if (index == 0) continue; rtl_set_bbreg(hw, bb_reg[index], MASKDWORD, bb_backup[index]); } _rtl92c_phy_reload_adda_registers(hw, afe_reg, afe_backup, 16); for (path = 0; path < pathbound; path++) { rtl_set_rfreg(hw, (enum radio_path)path, 0xd, MASKDWORD, reg_d[path]); if (path == RF90_PATH_B) { rtl_set_rfreg(hw, RF90_PATH_A, 0x10, MASKDWORD, 0x1000f); rtl_set_rfreg(hw, RF90_PATH_A, 0x11, MASKDWORD, 0x20101); } if (apk_result[path][1] > 6) apk_result[path][1] = 6; } for (path = 0; path < pathbound; path++) { rtl_set_rfreg(hw, (enum radio_path)path, 0x3, MASKDWORD, ((apk_result[path][1] << 15) | (apk_result[path][1] << 10) | (apk_result[path][1] << 5) | apk_result[path][1])); if (path == RF90_PATH_A) rtl_set_rfreg(hw, (enum radio_path)path, 0x4, MASKDWORD, ((apk_result[path][1] << 15) | (apk_result[path][1] << 10) | (0x00 << 5) | 0x05)); else rtl_set_rfreg(hw, (enum radio_path)path, 0x4, MASKDWORD, ((apk_result[path][1] << 15) | (apk_result[path][1] << 10) | (0x02 << 5) | 0x05)); rtl_set_rfreg(hw, (enum radio_path)path, 0xe, MASKDWORD, ((0x08 << 15) | (0x08 << 10) | (0x08 << 5) | 0x08)); } rtlphy->b_apk_done = true; #endif } static void _rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain, bool is2t) { struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (is_hal_stop(rtlhal)) { rtl_set_bbreg(hw, REG_LEDCFG0, BIT(23), 0x01); rtl_set_bbreg(hw, rFPGA0_XAB_RFPARAMETER, BIT(13), 0x01); } if (is2t) { if (bmain) rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BIT(5) | BIT(6), 0x1); else rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BIT(5) | BIT(6), 0x2); } else { if (bmain) rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, 0x300, 0x2); else rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, 0x300, 0x1); } } #undef IQK_ADDA_REG_NUM #undef IQK_DELAY_TIME void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); long result[4][8]; u8 i, final_candidate; bool patha_ok, pathb_ok; long reg_e94, reg_e9c, reg_ea4, reg_eb4, reg_ebc, reg_ec4, reg_tmp = 0; bool is12simular, is13simular, is23simular; bool start_conttx = false, singletone = false; u32 iqk_bb_reg[10] = { ROFDM0_XARXIQIMBALANCE, ROFDM0_XBRXIQIMBALANCE, ROFDM0_ECCATHRESHOLD, ROFDM0_AGCRSSITABLE, ROFDM0_XATXIQIMBALANCE, ROFDM0_XBTXIQIMBALANCE, ROFDM0_XCTXIQIMBALANCE, ROFDM0_XCTXAFE, ROFDM0_XDTXAFE, ROFDM0_RXIQEXTANTA }; if (recovery) { _rtl92c_phy_reload_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); return; } if (start_conttx || singletone) return; for (i = 0; i < 8; i++) { result[0][i] = 0; result[1][i] = 0; result[2][i] = 0; result[3][i] = 0; } final_candidate = 0xff; patha_ok = false; pathb_ok = false; is12simular = false; is23simular = false; is13simular = false; for (i = 0; i < 3; i++) { if (IS_92C_SERIAL(rtlhal->version)) _rtl92c_phy_iq_calibrate(hw, result, i, true); else _rtl92c_phy_iq_calibrate(hw, result, i, false); if (i == 1) { is12simular = _rtl92c_phy_simularity_compare(hw, result, 0, 1); if (is12simular) { final_candidate = 0; break; } } if (i == 2) { is13simular = _rtl92c_phy_simularity_compare(hw, result, 0, 2); if (is13simular) { final_candidate = 0; break; } is23simular = _rtl92c_phy_simularity_compare(hw, result, 1, 2); if (is23simular) final_candidate = 1; else { for (i = 0; i < 8; i++) reg_tmp += result[3][i]; if (reg_tmp != 0) final_candidate = 3; else final_candidate = 0xFF; } } } for (i = 0; i < 4; i++) { reg_e94 = result[i][0]; reg_e9c = result[i][1]; reg_ea4 = result[i][2]; reg_eb4 = result[i][4]; reg_ebc = result[i][5]; reg_ec4 = result[i][6]; } if (final_candidate != 0xff) { rtlphy->reg_e94 = reg_e94 = result[final_candidate][0]; rtlphy->reg_e9c = reg_e9c = result[final_candidate][1]; reg_ea4 = result[final_candidate][2]; rtlphy->reg_eb4 = reg_eb4 = result[final_candidate][4]; rtlphy->reg_ebc = reg_ebc = result[final_candidate][5]; reg_ec4 = result[final_candidate][6]; patha_ok = pathb_ok = true; } else { rtlphy->reg_e94 = rtlphy->reg_eb4 = 0x100; rtlphy->reg_e9c = rtlphy->reg_ebc = 0x0; } if (reg_e94 != 0) /*&&(reg_ea4 != 0) */ _rtl92c_phy_path_a_fill_iqk_matrix(hw, patha_ok, result, final_candidate, (reg_ea4 == 0)); if (IS_92C_SERIAL(rtlhal->version)) { if (reg_eb4 != 0) /*&&(reg_ec4 != 0) */ _rtl92c_phy_path_b_fill_iqk_matrix(hw, pathb_ok, result, final_candidate, (reg_ec4 == 0)); } _rtl92c_phy_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); } EXPORT_SYMBOL(rtl92c_phy_iq_calibrate); void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool start_conttx = false, singletone = false; if (start_conttx || singletone) return; if (IS_92C_SERIAL(rtlhal->version)) rtlpriv->cfg->ops->phy_lc_calibrate(hw, true); else rtlpriv->cfg->ops->phy_lc_calibrate(hw, false); } EXPORT_SYMBOL(rtl92c_phy_lc_calibrate); void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (rtlphy->apk_done) return; if (IS_92C_SERIAL(rtlhal->version)) _rtl92c_phy_ap_calibrate(hw, delta, true); else _rtl92c_phy_ap_calibrate(hw, delta, false); } EXPORT_SYMBOL(rtl92c_phy_ap_calibrate); void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain) { struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (IS_92C_SERIAL(rtlhal->version)) _rtl92c_phy_set_rfpath_switch(hw, bmain, true); else _rtl92c_phy_set_rfpath_switch(hw, bmain, false); } EXPORT_SYMBOL(rtl92c_phy_set_rfpath_switch); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); bool postprocessing = false; RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "-->IO Cmd(%#x), set_io_inprogress(%d)\n", iotype, rtlphy->set_io_inprogress); do { switch (iotype) { case IO_CMD_RESUME_DM_BY_SCAN: RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "[IO CMD] Resume DM after scan\n"); postprocessing = true; break; case IO_CMD_PAUSE_DM_BY_SCAN: RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "[IO CMD] Pause DM before scan\n"); postprocessing = true; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } } while (false); if (postprocessing && !rtlphy->set_io_inprogress) { rtlphy->set_io_inprogress = true; rtlphy->current_io_type = iotype; } else { return false; } rtl92c_phy_set_io(hw); RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "<--IO Type(%#x)\n", iotype); return true; } EXPORT_SYMBOL(rtl92c_phy_set_io_cmd); void rtl92c_phy_set_io(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct dig_t dm_digtable = rtlpriv->dm_digtable; RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "--->Cmd(%#x), set_io_inprogress(%d)\n", rtlphy->current_io_type, rtlphy->set_io_inprogress); switch (rtlphy->current_io_type) { case IO_CMD_RESUME_DM_BY_SCAN: dm_digtable.cur_igvalue = rtlphy->initgain_backup.xaagccore1; rtl92c_dm_write_dig(hw); rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); break; case IO_CMD_PAUSE_DM_BY_SCAN: rtlphy->initgain_backup.xaagccore1 = dm_digtable.cur_igvalue; dm_digtable.cur_igvalue = 0x37; rtl92c_dm_write_dig(hw); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } rtlphy->set_io_inprogress = false; RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "<---(%#x)\n", rtlphy->current_io_type); } EXPORT_SYMBOL(rtl92c_phy_set_io); void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); } EXPORT_SYMBOL(rtl92ce_phy_set_rf_on); void _rtl92c_phy_set_rf_sleep(struct ieee80211_hw *hw) { u32 u4b_tmp; u8 delay = 5; struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); while (u4b_tmp != 0 && delay > 0) { rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0); rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); delay--; } if (delay == 0) { rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "Switch RF timeout !!!\n"); return; } rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22); } EXPORT_SYMBOL(_rtl92c_phy_set_rf_sleep); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/main.c0000644000175000017500000000306412026211315024524 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include MODULE_AUTHOR("lizhaoming "); MODULE_AUTHOR("Realtek WlanFAE "); MODULE_AUTHOR("Georgia "); MODULE_AUTHOR("Ziv Huang "); MODULE_AUTHOR("Larry Finger "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8192C/8188C 802.11n PCI wireless"); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h0000644000175000017500000000652112026211315025572 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92C__FW__COMMON__H__ #define __RTL92C__FW__COMMON__H__ #define FW_8192C_SIZE 0x3000 #define FW_8192C_START_ADDRESS 0x1000 #define FW_8192C_END_ADDRESS 0x1FFF #define FW_8192C_PAGE_SIZE 4096 #define FW_8192C_POLLING_DELAY 5 #define FW_8192C_POLLING_TIMEOUT_COUNT 100 #define IS_FW_HEADER_EXIST(_pfwhdr) \ ((le16_to_cpu(_pfwhdr->signature)&0xFFF0) == 0x92C0 ||\ (le16_to_cpu(_pfwhdr->signature)&0xFFF0) == 0x88C0) struct rtl92c_firmware_header { __le16 signature; u8 category; u8 function; __le16 version; u8 subversion; u8 rsvd1; u8 month; u8 date; u8 hour; u8 minute; __le16 ramcodeSize; __le16 rsvd2; __le32 svnindex; __le32 rsvd3; __le32 rsvd4; __le32 rsvd5; }; enum rtl8192c_h2c_cmd { H2C_AP_OFFLOAD = 0, H2C_SETPWRMODE = 1, H2C_JOINBSSRPT = 2, H2C_RSVDPAGE = 3, H2C_RSSI_REPORT = 5, H2C_RA_MASK = 6, MAX_H2CCMD }; #define pagenum_128(_len) (u32)(((_len)>>7) + ((_len)&0x7F ? 1 : 0)) #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) #define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) #define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) int rtl92c_download_fw(struct ieee80211_hw *hw); void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); void rtl92c_firmware_selfreset(struct ieee80211_hw *hw); void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished); void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); void usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, void *data, u16 len); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c0000644000175000017500000005646512026211315025601 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include "../wifi.h" #include "../pci.h" #include "../base.h" #include "../rtl8192ce/reg.h" #include "../rtl8192ce/def.h" #include "fw_common.h" #include #include static void _rtl92c_enable_fw_download(struct ieee80211_hw *hw, bool enable) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CU) { u32 value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); if (enable) value32 |= MCUFWDL_EN; else value32 &= ~MCUFWDL_EN; rtl_write_dword(rtlpriv, REG_MCUFWDL, value32); } else if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE) { u8 tmp; if (enable) { tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04); tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01); tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2); rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7); } else { tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe); rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00); } } } static void rtl_block_fw_writeN(struct ieee80211_hw *hw, const u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 blockSize = REALTEK_USB_VENQT_MAX_BUF_SIZE - 20; u8 *bufferPtr = (u8 *) buffer; u32 i, offset, blockCount, remainSize; blockCount = size / blockSize; remainSize = size % blockSize; for (i = 0; i < blockCount; i++) { offset = i * blockSize; rtlpriv->io.writeN_sync(rtlpriv, (FW_8192C_START_ADDRESS + offset), (void *)(bufferPtr + offset), blockSize); } if (remainSize) { offset = blockCount * blockSize; rtlpriv->io.writeN_sync(rtlpriv, (FW_8192C_START_ADDRESS + offset), (void *)(bufferPtr + offset), remainSize); } } static void _rtl92c_fw_block_write(struct ieee80211_hw *hw, const u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 blockSize = sizeof(u32); u8 *bufferPtr = (u8 *) buffer; u32 *pu4BytePtr = (u32 *) buffer; u32 i, offset, blockCount, remainSize; u32 data; if (rtlpriv->io.writeN_sync) { rtl_block_fw_writeN(hw, buffer, size); return; } blockCount = size / blockSize; remainSize = size % blockSize; if (remainSize) { /* the last word is < 4 bytes - pad it with zeros */ for (i = 0; i < 4 - remainSize; i++) *(bufferPtr + size + i) = 0; blockCount++; } for (i = 0; i < blockCount; i++) { offset = i * blockSize; /* for big-endian platforms, the firmware data need to be byte * swapped as it was read as a byte string and will be written * as 32-bit dwords and byte swapped when written */ data = le32_to_cpu(*(__le32 *)(pu4BytePtr + i)); rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset), data); } } static void _rtl92c_fw_page_write(struct ieee80211_hw *hw, u32 page, const u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 value8; u8 u8page = (u8) (page & 0x07); value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page; rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8); _rtl92c_fw_block_write(hw, buffer, size); } static void _rtl92c_fill_dummy(u8 *pfwbuf, u32 *pfwlen) { u32 fwlen = *pfwlen; u8 remain = (u8) (fwlen % 4); remain = (remain == 0) ? 0 : (4 - remain); while (remain > 0) { pfwbuf[fwlen] = 0; fwlen++; remain--; } *pfwlen = fwlen; } static void _rtl92c_write_fw(struct ieee80211_hw *hw, enum version_8192c version, u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 *bufferPtr = buffer; RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes\n", size); if (IS_CHIP_VER_B(version)) { u32 pageNums, remainSize; u32 page, offset; if (IS_HARDWARE_TYPE_8192CE(rtlhal)) _rtl92c_fill_dummy(bufferPtr, &size); pageNums = size / FW_8192C_PAGE_SIZE; remainSize = size % FW_8192C_PAGE_SIZE; if (pageNums > 4) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Page numbers should not greater then 4\n"); } for (page = 0; page < pageNums; page++) { offset = page * FW_8192C_PAGE_SIZE; _rtl92c_fw_page_write(hw, page, (bufferPtr + offset), FW_8192C_PAGE_SIZE); } if (remainSize) { offset = pageNums * FW_8192C_PAGE_SIZE; page = pageNums; _rtl92c_fw_page_write(hw, page, (bufferPtr + offset), remainSize); } } else { _rtl92c_fw_block_write(hw, buffer, size); } } static int _rtl92c_fw_free_to_go(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 counter = 0; u32 value32; do { value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); } while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) && (!(value32 & FWDL_ChkSum_rpt))); if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "chksum report faill ! REG_MCUFWDL:0x%08x\n", value32); return -EIO; } RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "Checksum report OK ! REG_MCUFWDL:0x%08x\n", value32); value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); value32 |= MCUFWDL_RDY; value32 &= ~WINTINI_RDY; rtl_write_dword(rtlpriv, REG_MCUFWDL, value32); counter = 0; do { value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); if (value32 & WINTINI_RDY) { RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "Polling FW ready success!! REG_MCUFWDL:0x%08x\n", value32); return 0; } mdelay(FW_8192C_POLLING_DELAY); } while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT); RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Polling FW ready fail!! REG_MCUFWDL:0x%08x\n", value32); return -EIO; } int rtl92c_download_fw(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl92c_firmware_header *pfwheader; u8 *pfwdata; u32 fwsize; enum version_8192c version = rtlhal->version; if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware) return 1; pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware; pfwdata = rtlhal->pfirmware; fwsize = rtlhal->fwsize; if (IS_FW_HEADER_EXIST(pfwheader)) { RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "Firmware Version(%d), Signature(%#x),Size(%d)\n", le16_to_cpu(pfwheader->version), le16_to_cpu(pfwheader->signature), (uint)sizeof(struct rtl92c_firmware_header)); pfwdata = pfwdata + sizeof(struct rtl92c_firmware_header); fwsize = fwsize - sizeof(struct rtl92c_firmware_header); } _rtl92c_enable_fw_download(hw, true); _rtl92c_write_fw(hw, version, pfwdata, fwsize); _rtl92c_enable_fw_download(hw, false); if (_rtl92c_fw_free_to_go(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Firmware is not ready to run!\n"); } else { RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "Firmware is ready to run!\n"); } return 0; } EXPORT_SYMBOL(rtl92c_download_fw); static bool _rtl92c_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 val_hmetfr, val_mcutst_1; bool result = false; val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR); val_mcutst_1 = rtl_read_byte(rtlpriv, (REG_MCUTST_1 + boxnum)); if (((val_hmetfr >> boxnum) & BIT(0)) == 0 && val_mcutst_1 == 0) result = true; return result; } static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u8 boxnum; u16 box_reg = 0, box_extreg = 0; u8 u1b_tmp; bool isfw_read = false; bool bwrite_success = false; u8 wait_h2c_limmit = 100; u8 wait_writeh2c_limmit = 100; u8 boxcontent[4], boxextcontent[2]; u32 h2c_waitcounter = 0; unsigned long flag; u8 idx; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n"); while (true) { spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); if (rtlhal->h2c_setinprogress) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "H2C set in progress! Wait to set..element_id(%d)\n", element_id); while (rtlhal->h2c_setinprogress) { spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); h2c_waitcounter++; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Wait 100 us (%d times)...\n", h2c_waitcounter); udelay(100); if (h2c_waitcounter > 1000) return; spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); } spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); } else { rtlhal->h2c_setinprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); break; } } while (!bwrite_success) { wait_writeh2c_limmit--; if (wait_writeh2c_limmit == 0) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write H2C fail because no trigger for FW INT!\n"); break; } boxnum = rtlhal->last_hmeboxnum; switch (boxnum) { case 0: box_reg = REG_HMEBOX_0; box_extreg = REG_HMEBOX_EXT_0; break; case 1: box_reg = REG_HMEBOX_1; box_extreg = REG_HMEBOX_EXT_1; break; case 2: box_reg = REG_HMEBOX_2; box_extreg = REG_HMEBOX_EXT_2; break; case 3: box_reg = REG_HMEBOX_3; box_extreg = REG_HMEBOX_EXT_3; break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } isfw_read = _rtl92c_check_fw_read_last_h2c(hw, boxnum); while (!isfw_read) { wait_h2c_limmit--; if (wait_h2c_limmit == 0) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Waiting too long for FW read clear HMEBox(%d)!\n", boxnum); break; } udelay(10); isfw_read = _rtl92c_check_fw_read_last_h2c(hw, boxnum); u1b_tmp = rtl_read_byte(rtlpriv, 0x1BF); RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Waiting for FW read clear HMEBox(%d)!!! 0x1BF = %2x\n", boxnum, u1b_tmp); } if (!isfw_read) { RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Write H2C register BOX[%d] fail!!!!! Fw do not read\n", boxnum); break; } memset(boxcontent, 0, sizeof(boxcontent)); memset(boxextcontent, 0, sizeof(boxextcontent)); boxcontent[0] = element_id; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "Write element_id box_reg(%4x) = %2x\n", box_reg, element_id); switch (cmd_len) { case 1: boxcontent[0] &= ~(BIT(7)); memcpy((u8 *) (boxcontent) + 1, p_cmdbuffer, 1); for (idx = 0; idx < 4; idx++) { rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); } break; case 2: boxcontent[0] &= ~(BIT(7)); memcpy((u8 *) (boxcontent) + 1, p_cmdbuffer, 2); for (idx = 0; idx < 4; idx++) { rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); } break; case 3: boxcontent[0] &= ~(BIT(7)); memcpy((u8 *) (boxcontent) + 1, p_cmdbuffer, 3); for (idx = 0; idx < 4; idx++) { rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); } break; case 4: boxcontent[0] |= (BIT(7)); memcpy((u8 *) (boxextcontent), p_cmdbuffer, 2); memcpy((u8 *) (boxcontent) + 1, p_cmdbuffer + 2, 2); for (idx = 0; idx < 2; idx++) { rtl_write_byte(rtlpriv, box_extreg + idx, boxextcontent[idx]); } for (idx = 0; idx < 4; idx++) { rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); } break; case 5: boxcontent[0] |= (BIT(7)); memcpy((u8 *) (boxextcontent), p_cmdbuffer, 2); memcpy((u8 *) (boxcontent) + 1, p_cmdbuffer + 2, 3); for (idx = 0; idx < 2; idx++) { rtl_write_byte(rtlpriv, box_extreg + idx, boxextcontent[idx]); } for (idx = 0; idx < 4; idx++) { rtl_write_byte(rtlpriv, box_reg + idx, boxcontent[idx]); } break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; } bwrite_success = true; rtlhal->last_hmeboxnum = boxnum + 1; if (rtlhal->last_hmeboxnum == 4) rtlhal->last_hmeboxnum = 0; RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "pHalData->last_hmeboxnum = %d\n", rtlhal->last_hmeboxnum); } spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); rtlhal->h2c_setinprogress = false; spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n"); } void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer) { u32 tmp_cmdbuf[2]; memset(tmp_cmdbuf, 0, 8); memcpy(tmp_cmdbuf, p_cmdbuffer, cmd_len); _rtl92c_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf); return; } EXPORT_SYMBOL(rtl92c_fill_h2c_cmd); void rtl92c_firmware_selfreset(struct ieee80211_hw *hw) { u8 u1b_tmp; u8 delay = 100; struct rtl_priv *rtlpriv = rtl_priv(hw); rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20); u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); while (u1b_tmp & BIT(2)) { delay--; if (delay == 0) { RT_ASSERT(false, "8051 reset fail\n"); break; } udelay(50); u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); } } EXPORT_SYMBOL(rtl92c_firmware_selfreset); void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 u1_h2c_set_pwrmode[3] = {0}; struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode); SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1); SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode, ppsc->reg_max_lps_awakeintvl); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "rtl92c_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode", u1_h2c_set_pwrmode, 3); rtl92c_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); } EXPORT_SYMBOL(rtl92c_set_fw_pwrmode_cmd); static bool _rtl92c_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl8192_tx_ring *ring; struct rtl_tx_desc *pdesc; unsigned long flags; struct sk_buff *pskb = NULL; ring = &rtlpci->tx_ring[BEACON_QUEUE]; pskb = __skb_dequeue(&ring->queue); kfree_skb(pskb); spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); pdesc = &ring->desc[0]; rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *) pdesc, 1, 1, skb); __skb_queue_tail(&ring->queue, skb); spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE); return true; } #define BEACON_PG 0 /*->1*/ #define PSPOLL_PG 2 #define NULL_PG 3 #define PROBERSP_PG 4 /*->5*/ #define TOTAL_RESERVED_PKT_LEN 768 static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { /* page 0 beacon */ 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x50, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69, 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C, 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96, 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C, 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 1 beacon */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 2 ps-poll */ 0xA4, 0x10, 0x01, 0xC0, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 3 null */ 0x48, 0x01, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 4 probe_resp */ 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, 0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00, 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69, 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C, 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96, 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C, 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* page 5 probe_resp */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct sk_buff *skb = NULL; u32 totalpacketlen; bool rtstatus; u8 u1RsvdPageLoc[3] = {0}; bool dlok = false; u8 *beacon; u8 *pspoll; u8 *nullfunc; u8 *probersp; /*--------------------------------------------------------- (1) beacon ---------------------------------------------------------*/ beacon = &reserved_page_packet[BEACON_PG * 128]; SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr); SET_80211_HDR_ADDRESS3(beacon, mac->bssid); /*------------------------------------------------------- (2) ps-poll --------------------------------------------------------*/ pspoll = &reserved_page_packet[PSPOLL_PG * 128]; SET_80211_PS_POLL_AID(pspoll, (mac->assoc_id | 0xc000)); SET_80211_PS_POLL_BSSID(pspoll, mac->bssid); SET_80211_PS_POLL_TA(pspoll, mac->mac_addr); SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1RsvdPageLoc, PSPOLL_PG); /*-------------------------------------------------------- (3) null data ---------------------------------------------------------*/ nullfunc = &reserved_page_packet[NULL_PG * 128]; SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid); SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr); SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid); SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1RsvdPageLoc, NULL_PG); /*--------------------------------------------------------- (4) probe response ----------------------------------------------------------*/ probersp = &reserved_page_packet[PROBERSP_PG * 128]; SET_80211_HDR_ADDRESS1(probersp, mac->bssid); SET_80211_HDR_ADDRESS2(probersp, mac->mac_addr); SET_80211_HDR_ADDRESS3(probersp, mac->bssid); SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1RsvdPageLoc, PROBERSP_PG); totalpacketlen = TOTAL_RESERVED_PKT_LEN; RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, "rtl92c_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL", &reserved_page_packet[0], totalpacketlen); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "rtl92c_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL", u1RsvdPageLoc, 3); skb = dev_alloc_skb(totalpacketlen); if (!skb) return; kmemleak_not_leak(skb); memcpy((u8 *) skb_put(skb, totalpacketlen), &reserved_page_packet, totalpacketlen); rtstatus = _rtl92c_cmd_send_packet(hw, skb); if (rtstatus) dlok = true; if (dlok) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Set RSVD page location to Fw\n"); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "H2C_RSVDPAGE", u1RsvdPageLoc, 3); rtl92c_fill_h2c_cmd(hw, H2C_RSVDPAGE, sizeof(u1RsvdPageLoc), u1RsvdPageLoc); } else RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Set RSVD page location to Fw FAIL!!!!!!\n"); } EXPORT_SYMBOL(rtl92c_set_fw_rsvdpagepkt); void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus) { u8 u1_joinbssrpt_parm[1] = {0}; SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus); rtl92c_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm); } EXPORT_SYMBOL(rtl92c_set_fw_joinbss_report_cmd); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h0000644000175000017500000001064212026211315025555 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #ifndef __RTL92COMMON_DM_H__ #define __RTL92COMMON_DM_H__ #include "../wifi.h" #include "../rtl8192ce/def.h" #include "../rtl8192ce/reg.h" #include "fw_common.h" #define HAL_DM_DIG_DISABLE BIT(0) #define HAL_DM_HIPWR_DISABLE BIT(1) #define OFDM_TABLE_LENGTH 37 #define CCK_TABLE_LENGTH 33 #define OFDM_TABLE_SIZE 37 #define CCK_TABLE_SIZE 33 #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 #define DM_DIG_THRESH_HIGH 40 #define DM_DIG_THRESH_LOW 35 #define DM_FALSEALARM_THRESH_LOW 400 #define DM_FALSEALARM_THRESH_HIGH 1000 #define DM_DIG_MAX 0x3e #define DM_DIG_MIN 0x1e #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 #define DM_DIG_BACKOFF_MAX 12 #define DM_DIG_BACKOFF_MIN -4 #define DM_DIG_BACKOFF_DEFAULT 10 #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 #define DM_RATR_STA_INIT 0 #define DM_RATR_STA_HIGH 1 #define DM_RATR_STA_MIDDLE 2 #define DM_RATR_STA_LOW 3 #define CTS2SELF_THVAL 30 #define REGC38_TH 20 #define WAIOTTHVal 25 #define TXHIGHPWRLEVEL_NORMAL 0 #define TXHIGHPWRLEVEL_LEVEL1 1 #define TXHIGHPWRLEVEL_LEVEL2 2 #define TXHIGHPWRLEVEL_BT1 3 #define TXHIGHPWRLEVEL_BT2 4 #define DM_TYPE_BYFW 0 #define DM_TYPE_BYDRIVER 1 #define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 #define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 struct swat_t { u8 failure_cnt; u8 try_flag; u8 stop_trying; long pre_rssi; long trying_threshold; u8 cur_antenna; u8 pre_antenna; }; enum tag_dynamic_init_gain_operation_type_definition { DIG_TYPE_THRESH_HIGH = 0, DIG_TYPE_THRESH_LOW = 1, DIG_TYPE_BACKOFF = 2, DIG_TYPE_RX_GAIN_MIN = 3, DIG_TYPE_RX_GAIN_MAX = 4, DIG_TYPE_ENABLE = 5, DIG_TYPE_DISABLE = 6, DIG_OP_TYPE_MAX }; enum tag_cck_packet_detection_threshold_type_definition { CCK_PD_STAGE_LowRssi = 0, CCK_PD_STAGE_HighRssi = 1, CCK_FA_STAGE_Low = 2, CCK_FA_STAGE_High = 3, CCK_PD_STAGE_MAX = 4, }; enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, CCA_MAX = 2, }; enum dm_rf_e { RF_SAVE = 0, RF_NORMAL = 1, RF_MAX = 2, }; enum dm_sw_ant_switch_e { ANS_ANTENNA_B = 1, ANS_ANTENNA_A = 2, ANS_ANTENNA_MAX = 3, }; enum dm_dig_ext_port_alg_e { DIG_EXT_PORT_STAGE_0 = 0, DIG_EXT_PORT_STAGE_1 = 1, DIG_EXT_PORT_STAGE_2 = 2, DIG_EXT_PORT_STAGE_3 = 3, DIG_EXT_PORT_STAGE_MAX = 4, }; enum dm_dig_connect_e { DIG_STA_DISCONNECT = 0, DIG_STA_CONNECT = 1, DIG_STA_BEFORE_CONNECT = 2, DIG_MULTISTA_DISCONNECT = 3, DIG_MULTISTA_CONNECT = 4, DIG_CONNECT_MAX }; void rtl92c_dm_init(struct ieee80211_hw *hw); void rtl92c_dm_watchdog(struct ieee80211_hw *hw); void rtl92c_dm_write_dig(struct ieee80211_hw *hw); void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw); void rtl92c_dm_check_txpower_tracking(struct ieee80211_hw *hw); void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal); void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw); void rtl92c_dm_bt_coexist(struct ieee80211_hw *hw); #endif compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c0000644000175000017500000015255212026211315025557 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger * *****************************************************************************/ #include #include "dm_common.h" #include "phy_common.h" #include "../pci.h" #include "../base.h" #define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) #define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) #define BT_RSSI_STATE_SPECIAL_LOW BIT_OFFSET_LEN_MASK_32(2, 1) #define BT_RSSI_STATE_BG_EDCA_LOW BIT_OFFSET_LEN_MASK_32(3, 1) #define BT_RSSI_STATE_TXPOWER_LOW BIT_OFFSET_LEN_MASK_32(4, 1) #define RTLPRIV (struct rtl_priv *) #define GET_UNDECORATED_AVERAGE_RSSI(_priv) \ ((RTLPRIV(_priv))->mac80211.opmode == \ NL80211_IFTYPE_ADHOC) ? \ ((RTLPRIV(_priv))->dm.entry_min_undecoratedsmoothed_pwdb) : \ ((RTLPRIV(_priv))->dm.undecorated_smoothed_pwdb) static const u32 ofdmswing_table[OFDM_TABLE_SIZE] = { 0x7f8001fe, 0x788001e2, 0x71c001c7, 0x6b8001ae, 0x65400195, 0x5fc0017f, 0x5a400169, 0x55400155, 0x50800142, 0x4c000130, 0x47c0011f, 0x43c0010f, 0x40000100, 0x3c8000f2, 0x390000e4, 0x35c000d7, 0x32c000cb, 0x300000c0, 0x2d4000b5, 0x2ac000ab, 0x288000a2, 0x26000098, 0x24000090, 0x22000088, 0x20000080, 0x1e400079, 0x1c800072, 0x1b00006c, 0x19800066, 0x18000060, 0x16c0005b, 0x15800056, 0x14400051, 0x1300004c, 0x12000048, 0x11000044, 0x10000040, }; static const u8 cckswing_table_ch1ch13[CCK_TABLE_SIZE][8] = { {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01} }; static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} }; static void rtl92c_dm_diginit(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; dm_digtable->dig_enable_flag = true; dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; dm_digtable->cur_igvalue = 0x20; dm_digtable->pre_igvalue = 0x0; dm_digtable->cursta_connectstate = DIG_STA_DISCONNECT; dm_digtable->presta_connectstate = DIG_STA_DISCONNECT; dm_digtable->curmultista_connectstate = DIG_MULTISTA_DISCONNECT; dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; dm_digtable->rx_gain_range_max = DM_DIG_MAX; dm_digtable->rx_gain_range_min = DM_DIG_MIN; dm_digtable->backoff_val = DM_DIG_BACKOFF_DEFAULT; dm_digtable->backoff_val_range_max = DM_DIG_BACKOFF_MAX; dm_digtable->backoff_val_range_min = DM_DIG_BACKOFF_MIN; dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; } static u8 rtl92c_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; long rssi_val_min = 0; if ((dm_digtable->curmultista_connectstate == DIG_MULTISTA_CONNECT) && (dm_digtable->cursta_connectstate == DIG_STA_CONNECT)) { if (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb != 0) rssi_val_min = (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb > rtlpriv->dm.undecorated_smoothed_pwdb) ? rtlpriv->dm.undecorated_smoothed_pwdb : rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; else rssi_val_min = rtlpriv->dm.undecorated_smoothed_pwdb; } else if (dm_digtable->cursta_connectstate == DIG_STA_CONNECT || dm_digtable->cursta_connectstate == DIG_STA_BEFORE_CONNECT) { rssi_val_min = rtlpriv->dm.undecorated_smoothed_pwdb; } else if (dm_digtable->curmultista_connectstate == DIG_MULTISTA_CONNECT) { rssi_val_min = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; } return (u8) rssi_val_min; } static void rtl92c_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) { u32 ret_value; struct rtl_priv *rtlpriv = rtl_priv(hw); struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, MASKDWORD); falsealm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, MASKDWORD); falsealm_cnt->cnt_rate_illegal = (ret_value & 0xffff); falsealm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16); ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, MASKDWORD); falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff); falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail + falsealm_cnt->cnt_rate_illegal + falsealm_cnt->cnt_crc8_fail + falsealm_cnt->cnt_mcs_fail; rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(14), 1); ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, MASKBYTE0); falsealm_cnt->cnt_cck_fail = ret_value; ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, MASKBYTE3); falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8; falsealm_cnt->cnt_all = (falsealm_cnt->cnt_parity_fail + falsealm_cnt->cnt_rate_illegal + falsealm_cnt->cnt_crc8_fail + falsealm_cnt->cnt_mcs_fail + falsealm_cnt->cnt_cck_fail); rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 1); rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 0); rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 0); rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 2); RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "cnt_parity_fail = %d, cnt_rate_illegal = %d, cnt_crc8_fail = %d, cnt_mcs_fail = %d\n", falsealm_cnt->cnt_parity_fail, falsealm_cnt->cnt_rate_illegal, falsealm_cnt->cnt_crc8_fail, falsealm_cnt->cnt_mcs_fail); RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "cnt_ofdm_fail = %x, cnt_cck_fail = %x, cnt_all = %x\n", falsealm_cnt->cnt_ofdm_fail, falsealm_cnt->cnt_cck_fail, falsealm_cnt->cnt_all); } static void rtl92c_dm_ctrl_initgain_by_fa(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; u8 value_igi = dm_digtable->cur_igvalue; if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH0) value_igi--; else if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH1) value_igi += 0; else if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH2) value_igi++; else if (rtlpriv->falsealm_cnt.cnt_all >= DM_DIG_FA_TH2) value_igi += 2; if (value_igi > DM_DIG_FA_UPPER) value_igi = DM_DIG_FA_UPPER; else if (value_igi < DM_DIG_FA_LOWER) value_igi = DM_DIG_FA_LOWER; if (rtlpriv->falsealm_cnt.cnt_all > 10000) value_igi = 0x32; dm_digtable->cur_igvalue = value_igi; rtl92c_dm_write_dig(hw); } static void rtl92c_dm_ctrl_initgain_by_rssi(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; if (rtlpriv->falsealm_cnt.cnt_all > dm_digtable->fa_highthresh) { if ((dm_digtable->backoff_val - 2) < dm_digtable->backoff_val_range_min) dm_digtable->backoff_val = dm_digtable->backoff_val_range_min; else dm_digtable->backoff_val -= 2; } else if (rtlpriv->falsealm_cnt.cnt_all < dm_digtable->fa_lowthresh) { if ((dm_digtable->backoff_val + 2) > dm_digtable->backoff_val_range_max) dm_digtable->backoff_val = dm_digtable->backoff_val_range_max; else dm_digtable->backoff_val += 2; } if ((dm_digtable->rssi_val_min + 10 - dm_digtable->backoff_val) > dm_digtable->rx_gain_range_max) dm_digtable->cur_igvalue = dm_digtable->rx_gain_range_max; else if ((dm_digtable->rssi_val_min + 10 - dm_digtable->backoff_val) < dm_digtable->rx_gain_range_min) dm_digtable->cur_igvalue = dm_digtable->rx_gain_range_min; else dm_digtable->cur_igvalue = dm_digtable->rssi_val_min + 10 - dm_digtable->backoff_val; RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "rssi_val_min = %x backoff_val %x\n", dm_digtable->rssi_val_min, dm_digtable->backoff_val); rtl92c_dm_write_dig(hw); } static void rtl92c_dm_initial_gain_multi_sta(struct ieee80211_hw *hw) { static u8 initialized; /* initialized to false */ struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long rssi_strength = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; bool multi_sta = false; if (mac->opmode == NL80211_IFTYPE_ADHOC) multi_sta = true; if (!multi_sta || dm_digtable->cursta_connectstate != DIG_STA_DISCONNECT) { initialized = false; dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; return; } else if (initialized == false) { initialized = true; dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_0; dm_digtable->cur_igvalue = 0x20; rtl92c_dm_write_dig(hw); } if (dm_digtable->curmultista_connectstate == DIG_MULTISTA_CONNECT) { if ((rssi_strength < dm_digtable->rssi_lowthresh) && (dm_digtable->dig_ext_port_stage != DIG_EXT_PORT_STAGE_1)) { if (dm_digtable->dig_ext_port_stage == DIG_EXT_PORT_STAGE_2) { dm_digtable->cur_igvalue = 0x20; rtl92c_dm_write_dig(hw); } dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_1; } else if (rssi_strength > dm_digtable->rssi_highthresh) { dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_2; rtl92c_dm_ctrl_initgain_by_fa(hw); } } else if (dm_digtable->dig_ext_port_stage != DIG_EXT_PORT_STAGE_0) { dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_0; dm_digtable->cur_igvalue = 0x20; rtl92c_dm_write_dig(hw); } RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "curmultista_connectstate = %x dig_ext_port_stage %x\n", dm_digtable->curmultista_connectstate, dm_digtable->dig_ext_port_stage); } static void rtl92c_dm_initial_gain_sta(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "presta_connectstate = %x, cursta_connectstate = %x\n", dm_digtable->presta_connectstate, dm_digtable->cursta_connectstate); if (dm_digtable->presta_connectstate == dm_digtable->cursta_connectstate || dm_digtable->cursta_connectstate == DIG_STA_BEFORE_CONNECT || dm_digtable->cursta_connectstate == DIG_STA_CONNECT) { if (dm_digtable->cursta_connectstate != DIG_STA_DISCONNECT) { dm_digtable->rssi_val_min = rtl92c_dm_initial_gain_min_pwdb(hw); rtl92c_dm_ctrl_initgain_by_rssi(hw); } } else { dm_digtable->rssi_val_min = 0; dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; dm_digtable->backoff_val = DM_DIG_BACKOFF_DEFAULT; dm_digtable->cur_igvalue = 0x20; dm_digtable->pre_igvalue = 0; rtl92c_dm_write_dig(hw); } } static void rtl92c_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; if (dm_digtable->cursta_connectstate == DIG_STA_CONNECT) { dm_digtable->rssi_val_min = rtl92c_dm_initial_gain_min_pwdb(hw); if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) { if (dm_digtable->rssi_val_min <= 25) dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LowRssi; else dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_HighRssi; } else { if (dm_digtable->rssi_val_min <= 20) dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LowRssi; else dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_HighRssi; } } else { dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; } if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) { if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) { if (rtlpriv->falsealm_cnt.cnt_cck_fail > 800) dm_digtable->cur_cck_fa_state = CCK_FA_STAGE_High; else dm_digtable->cur_cck_fa_state = CCK_FA_STAGE_Low; if (dm_digtable->pre_cck_fa_state != dm_digtable->cur_cck_fa_state) { if (dm_digtable->cur_cck_fa_state == CCK_FA_STAGE_Low) rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x83); else rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd); dm_digtable->pre_cck_fa_state = dm_digtable->cur_cck_fa_state; } rtl_set_bbreg(hw, RCCK0_SYSTEM, MASKBYTE1, 0x40); if (IS_92C_SERIAL(rtlhal->version)) rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, MASKBYTE2, 0xd7); } else { rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd); rtl_set_bbreg(hw, RCCK0_SYSTEM, MASKBYTE1, 0x47); if (IS_92C_SERIAL(rtlhal->version)) rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, MASKBYTE2, 0xd3); } dm_digtable->pre_cck_pd_state = dm_digtable->cur_cck_pd_state; } RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "CCKPDStage=%x\n", dm_digtable->cur_cck_pd_state); RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "is92C=%x\n", IS_92C_SERIAL(rtlhal->version)); } static void rtl92c_dm_ctrl_initgain_by_twoport(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); if (mac->act_scanning) return; if (mac->link_state >= MAC80211_LINKED) dm_digtable->cursta_connectstate = DIG_STA_CONNECT; else dm_digtable->cursta_connectstate = DIG_STA_DISCONNECT; rtl92c_dm_initial_gain_sta(hw); rtl92c_dm_initial_gain_multi_sta(hw); rtl92c_dm_cck_packet_detection_thresh(hw); dm_digtable->presta_connectstate = dm_digtable->cursta_connectstate; } static void rtl92c_dm_dig(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; if (rtlpriv->dm.dm_initialgain_enable == false) return; if (dm_digtable->dig_enable_flag == false) return; rtl92c_dm_ctrl_initgain_by_twoport(hw); } static void rtl92c_dm_init_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dynamic_txpower_enable = false; rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; } void rtl92c_dm_write_dig(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "cur_igvalue = 0x%x, pre_igvalue = 0x%x, backoff_val = %d\n", dm_digtable->cur_igvalue, dm_digtable->pre_igvalue, dm_digtable->backoff_val); dm_digtable->cur_igvalue += 2; if (dm_digtable->cur_igvalue > 0x3f) dm_digtable->cur_igvalue = 0x3f; if (dm_digtable->pre_igvalue != dm_digtable->cur_igvalue) { rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f, dm_digtable->cur_igvalue); rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, 0x7f, dm_digtable->cur_igvalue); dm_digtable->pre_igvalue = dm_digtable->cur_igvalue; } } EXPORT_SYMBOL(rtl92c_dm_write_dig); static void rtl92c_dm_pwdb_monitor(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); long tmpentry_max_pwdb = 0, tmpentry_min_pwdb = 0xff; u8 h2c_parameter[3] = { 0 }; return; if (tmpentry_max_pwdb != 0) { rtlpriv->dm.entry_max_undecoratedsmoothed_pwdb = tmpentry_max_pwdb; } else { rtlpriv->dm.entry_max_undecoratedsmoothed_pwdb = 0; } if (tmpentry_min_pwdb != 0xff) { rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb = tmpentry_min_pwdb; } else { rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb = 0; } h2c_parameter[2] = (u8) (rtlpriv->dm.undecorated_smoothed_pwdb & 0xFF); h2c_parameter[0] = 0; rtl92c_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, h2c_parameter); } void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.current_turbo_edca = false; rtlpriv->dm.is_any_nonbepkts = false; rtlpriv->dm.is_cur_rdlstate = false; } EXPORT_SYMBOL(rtl92c_dm_init_edca_turbo); static void rtl92c_dm_check_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); static u64 last_txok_cnt; static u64 last_rxok_cnt; static u32 last_bt_edca_ul; static u32 last_bt_edca_dl; u64 cur_txok_cnt = 0; u64 cur_rxok_cnt = 0; u32 edca_be_ul = 0x5ea42b; u32 edca_be_dl = 0x5ea42b; bool bt_change_edca = false; if ((last_bt_edca_ul != rtlpcipriv->bt_coexist.bt_edca_ul) || (last_bt_edca_dl != rtlpcipriv->bt_coexist.bt_edca_dl)) { rtlpriv->dm.current_turbo_edca = false; last_bt_edca_ul = rtlpcipriv->bt_coexist.bt_edca_ul; last_bt_edca_dl = rtlpcipriv->bt_coexist.bt_edca_dl; } if (rtlpcipriv->bt_coexist.bt_edca_ul != 0) { edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_ul; bt_change_edca = true; } if (rtlpcipriv->bt_coexist.bt_edca_dl != 0) { edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_dl; bt_change_edca = true; } if (mac->link_state != MAC80211_LINKED) { rtlpriv->dm.current_turbo_edca = false; return; } if ((!mac->ht_enable) && (!rtlpcipriv->bt_coexist.bt_coexistence)) { if (!(edca_be_ul & 0xffff0000)) edca_be_ul |= 0x005e0000; if (!(edca_be_dl & 0xffff0000)) edca_be_dl |= 0x005e0000; } if ((bt_change_edca) || ((!rtlpriv->dm.is_any_nonbepkts) && (!rtlpriv->dm.disable_framebursting))) { cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok_cnt; cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rxok_cnt; if (cur_rxok_cnt > 4 * cur_txok_cnt) { if (!rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, edca_be_dl); rtlpriv->dm.is_cur_rdlstate = true; } } else { if (rtlpriv->dm.is_cur_rdlstate || !rtlpriv->dm.current_turbo_edca) { rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, edca_be_ul); rtlpriv->dm.is_cur_rdlstate = false; } } rtlpriv->dm.current_turbo_edca = true; } else { if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, &tmp); rtlpriv->dm.current_turbo_edca = false; } } rtlpriv->dm.is_any_nonbepkts = false; last_txok_cnt = rtlpriv->stats.txbytesunicast; last_rxok_cnt = rtlpriv->stats.rxbytesunicast; } static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 thermalvalue, delta, delta_lck, delta_iqk; long ele_a, ele_d, temp_cck, val_x, value32; long val_y, ele_c = 0; u8 ofdm_index[2], cck_index = 0, ofdm_index_old[2], cck_index_old = 0; int i; bool is2t = IS_92C_SERIAL(rtlhal->version); s8 txpwr_level[2] = {0, 0}; u8 ofdm_min_index = 6, rf; rtlpriv->dm.txpower_trackinginit = true; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "rtl92c_dm_txpower_tracking_callback_thermalmeter\n"); thermalvalue = (u8) rtl_get_rfreg(hw, RF90_PATH_A, RF_T_METER, 0x1f); RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Readback Thermal Meter = 0x%x pre thermal meter 0x%x eeprom_thermalmeter 0x%x\n", thermalvalue, rtlpriv->dm.thermalvalue, rtlefuse->eeprom_thermalmeter); rtl92c_phy_ap_calibrate(hw, (thermalvalue - rtlefuse->eeprom_thermalmeter)); if (is2t) rf = 2; else rf = 1; if (thermalvalue) { ele_d = rtl_get_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD) & MASKOFDM_D; for (i = 0; i < OFDM_TABLE_LENGTH; i++) { if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) { ofdm_index_old[0] = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial pathA ele_d reg0x%x = 0x%lx, ofdm_index=0x%x\n", ROFDM0_XATXIQIMBALANCE, ele_d, ofdm_index_old[0]); break; } } if (is2t) { ele_d = rtl_get_bbreg(hw, ROFDM0_XBTXIQIMBALANCE, MASKDWORD) & MASKOFDM_D; for (i = 0; i < OFDM_TABLE_LENGTH; i++) { if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial pathB ele_d reg0x%x = 0x%lx, ofdm_index=0x%x\n", ROFDM0_XBTXIQIMBALANCE, ele_d, ofdm_index_old[1]); break; } } } temp_cck = rtl_get_bbreg(hw, RCCK0_TXFILTER2, MASKDWORD) & MASKCCK; for (i = 0; i < CCK_TABLE_LENGTH; i++) { if (rtlpriv->dm.cck_inch14) { if (memcmp((void *)&temp_cck, (void *)&cckswing_table_ch14[i][2], 4) == 0) { cck_index_old = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial reg0x%x = 0x%lx, cck_index=0x%x, ch 14 %d\n", RCCK0_TXFILTER2, temp_cck, cck_index_old, rtlpriv->dm.cck_inch14); break; } } else { if (memcmp((void *)&temp_cck, (void *) &cckswing_table_ch1ch13[i][2], 4) == 0) { cck_index_old = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial reg0x%x = 0x%lx, cck_index=0x%x, ch14 %d\n", RCCK0_TXFILTER2, temp_cck, cck_index_old, rtlpriv->dm.cck_inch14); break; } } } if (!rtlpriv->dm.thermalvalue) { rtlpriv->dm.thermalvalue = rtlefuse->eeprom_thermalmeter; rtlpriv->dm.thermalvalue_lck = thermalvalue; rtlpriv->dm.thermalvalue_iqk = thermalvalue; for (i = 0; i < rf; i++) rtlpriv->dm.ofdm_index[i] = ofdm_index_old[i]; rtlpriv->dm.cck_index = cck_index_old; } delta = (thermalvalue > rtlpriv->dm.thermalvalue) ? (thermalvalue - rtlpriv->dm.thermalvalue) : (rtlpriv->dm.thermalvalue - thermalvalue); delta_lck = (thermalvalue > rtlpriv->dm.thermalvalue_lck) ? (thermalvalue - rtlpriv->dm.thermalvalue_lck) : (rtlpriv->dm.thermalvalue_lck - thermalvalue); delta_iqk = (thermalvalue > rtlpriv->dm.thermalvalue_iqk) ? (thermalvalue - rtlpriv->dm.thermalvalue_iqk) : (rtlpriv->dm.thermalvalue_iqk - thermalvalue); RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Readback Thermal Meter = 0x%x pre thermal meter 0x%x eeprom_thermalmeter 0x%x delta 0x%x delta_lck 0x%x delta_iqk 0x%x\n", thermalvalue, rtlpriv->dm.thermalvalue, rtlefuse->eeprom_thermalmeter, delta, delta_lck, delta_iqk); if (delta_lck > 1) { rtlpriv->dm.thermalvalue_lck = thermalvalue; rtl92c_phy_lc_calibrate(hw); } if (delta > 0 && rtlpriv->dm.txpower_track_control) { if (thermalvalue > rtlpriv->dm.thermalvalue) { for (i = 0; i < rf; i++) rtlpriv->dm.ofdm_index[i] -= delta; rtlpriv->dm.cck_index -= delta; } else { for (i = 0; i < rf; i++) rtlpriv->dm.ofdm_index[i] += delta; rtlpriv->dm.cck_index += delta; } if (is2t) { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "temp OFDM_A_index=0x%x, OFDM_B_index=0x%x, cck_index=0x%x\n", rtlpriv->dm.ofdm_index[0], rtlpriv->dm.ofdm_index[1], rtlpriv->dm.cck_index); } else { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "temp OFDM_A_index=0x%x, cck_index=0x%x\n", rtlpriv->dm.ofdm_index[0], rtlpriv->dm.cck_index); } if (thermalvalue > rtlefuse->eeprom_thermalmeter) { for (i = 0; i < rf; i++) ofdm_index[i] = rtlpriv->dm.ofdm_index[i] + 1; cck_index = rtlpriv->dm.cck_index + 1; } else { for (i = 0; i < rf; i++) ofdm_index[i] = rtlpriv->dm.ofdm_index[i]; cck_index = rtlpriv->dm.cck_index; } for (i = 0; i < rf; i++) { if (txpwr_level[i] >= 0 && txpwr_level[i] <= 26) { if (thermalvalue > rtlefuse->eeprom_thermalmeter) { if (delta < 5) ofdm_index[i] -= 1; else ofdm_index[i] -= 2; } else if (delta > 5 && thermalvalue < rtlefuse-> eeprom_thermalmeter) { ofdm_index[i] += 1; } } else if (txpwr_level[i] >= 27 && txpwr_level[i] <= 32 && thermalvalue > rtlefuse->eeprom_thermalmeter) { if (delta < 5) ofdm_index[i] -= 1; else ofdm_index[i] -= 2; } else if (txpwr_level[i] >= 32 && txpwr_level[i] <= 38 && thermalvalue > rtlefuse->eeprom_thermalmeter && delta > 5) { ofdm_index[i] -= 1; } } if (txpwr_level[i] >= 0 && txpwr_level[i] <= 26) { if (thermalvalue > rtlefuse->eeprom_thermalmeter) { if (delta < 5) cck_index -= 1; else cck_index -= 2; } else if (delta > 5 && thermalvalue < rtlefuse->eeprom_thermalmeter) { cck_index += 1; } } else if (txpwr_level[i] >= 27 && txpwr_level[i] <= 32 && thermalvalue > rtlefuse->eeprom_thermalmeter) { if (delta < 5) cck_index -= 1; else cck_index -= 2; } else if (txpwr_level[i] >= 32 && txpwr_level[i] <= 38 && thermalvalue > rtlefuse->eeprom_thermalmeter && delta > 5) { cck_index -= 1; } for (i = 0; i < rf; i++) { if (ofdm_index[i] > OFDM_TABLE_SIZE - 1) ofdm_index[i] = OFDM_TABLE_SIZE - 1; else if (ofdm_index[i] < ofdm_min_index) ofdm_index[i] = ofdm_min_index; } if (cck_index > CCK_TABLE_SIZE - 1) cck_index = CCK_TABLE_SIZE - 1; else if (cck_index < 0) cck_index = 0; if (is2t) { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "new OFDM_A_index=0x%x, OFDM_B_index=0x%x, cck_index=0x%x\n", ofdm_index[0], ofdm_index[1], cck_index); } else { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "new OFDM_A_index=0x%x, cck_index=0x%x\n", ofdm_index[0], cck_index); } } if (rtlpriv->dm.txpower_track_control && delta != 0) { ele_d = (ofdmswing_table[ofdm_index[0]] & 0xFFC00000) >> 22; val_x = rtlphy->reg_e94; val_y = rtlphy->reg_e9c; if (val_x != 0) { if ((val_x & 0x00000200) != 0) val_x = val_x | 0xFFFFFC00; ele_a = ((val_x * ele_d) >> 8) & 0x000003FF; if ((val_y & 0x00000200) != 0) val_y = val_y | 0xFFFFFC00; ele_c = ((val_y * ele_d) >> 8) & 0x000003FF; value32 = (ele_d << 22) | ((ele_c & 0x3F) << 16) | ele_a; rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD, value32); value32 = (ele_c & 0x000003C0) >> 6; rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, value32); value32 = ((val_x * ele_d) >> 7) & 0x01; rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31), value32); value32 = ((val_y * ele_d) >> 7) & 0x01; rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(29), value32); } else { rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD, ofdmswing_table[ofdm_index[0]]); rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31) | BIT(29), 0x00); } if (!rtlpriv->dm.cck_inch14) { rtl_write_byte(rtlpriv, 0xa22, cckswing_table_ch1ch13[cck_index] [0]); rtl_write_byte(rtlpriv, 0xa23, cckswing_table_ch1ch13[cck_index] [1]); rtl_write_byte(rtlpriv, 0xa24, cckswing_table_ch1ch13[cck_index] [2]); rtl_write_byte(rtlpriv, 0xa25, cckswing_table_ch1ch13[cck_index] [3]); rtl_write_byte(rtlpriv, 0xa26, cckswing_table_ch1ch13[cck_index] [4]); rtl_write_byte(rtlpriv, 0xa27, cckswing_table_ch1ch13[cck_index] [5]); rtl_write_byte(rtlpriv, 0xa28, cckswing_table_ch1ch13[cck_index] [6]); rtl_write_byte(rtlpriv, 0xa29, cckswing_table_ch1ch13[cck_index] [7]); } else { rtl_write_byte(rtlpriv, 0xa22, cckswing_table_ch14[cck_index] [0]); rtl_write_byte(rtlpriv, 0xa23, cckswing_table_ch14[cck_index] [1]); rtl_write_byte(rtlpriv, 0xa24, cckswing_table_ch14[cck_index] [2]); rtl_write_byte(rtlpriv, 0xa25, cckswing_table_ch14[cck_index] [3]); rtl_write_byte(rtlpriv, 0xa26, cckswing_table_ch14[cck_index] [4]); rtl_write_byte(rtlpriv, 0xa27, cckswing_table_ch14[cck_index] [5]); rtl_write_byte(rtlpriv, 0xa28, cckswing_table_ch14[cck_index] [6]); rtl_write_byte(rtlpriv, 0xa29, cckswing_table_ch14[cck_index] [7]); } if (is2t) { ele_d = (ofdmswing_table[ofdm_index[1]] & 0xFFC00000) >> 22; val_x = rtlphy->reg_eb4; val_y = rtlphy->reg_ebc; if (val_x != 0) { if ((val_x & 0x00000200) != 0) val_x = val_x | 0xFFFFFC00; ele_a = ((val_x * ele_d) >> 8) & 0x000003FF; if ((val_y & 0x00000200) != 0) val_y = val_y | 0xFFFFFC00; ele_c = ((val_y * ele_d) >> 8) & 0x00003FF; value32 = (ele_d << 22) | ((ele_c & 0x3F) << 16) | ele_a; rtl_set_bbreg(hw, ROFDM0_XBTXIQIMBALANCE, MASKDWORD, value32); value32 = (ele_c & 0x000003C0) >> 6; rtl_set_bbreg(hw, ROFDM0_XDTXAFE, MASKH4BITS, value32); value32 = ((val_x * ele_d) >> 7) & 0x01; rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(27), value32); value32 = ((val_y * ele_d) >> 7) & 0x01; rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(25), value32); } else { rtl_set_bbreg(hw, ROFDM0_XBTXIQIMBALANCE, MASKDWORD, ofdmswing_table[ofdm_index [1]]); rtl_set_bbreg(hw, ROFDM0_XDTXAFE, MASKH4BITS, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(27) | BIT(25), 0x00); } } } if (delta_iqk > 3) { rtlpriv->dm.thermalvalue_iqk = thermalvalue; rtl92c_phy_iq_calibrate(hw, false); } if (rtlpriv->dm.txpower_track_control) rtlpriv->dm.thermalvalue = thermalvalue; } RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "<===\n"); } static void rtl92c_dm_initialize_txpower_tracking_thermalmeter( struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.txpower_tracking = true; rtlpriv->dm.txpower_trackinginit = false; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "pMgntInfo->txpower_tracking = %d\n", rtlpriv->dm.txpower_tracking); } static void rtl92c_dm_initialize_txpower_tracking(struct ieee80211_hw *hw) { rtl92c_dm_initialize_txpower_tracking_thermalmeter(hw); } static void rtl92c_dm_txpower_tracking_directcall(struct ieee80211_hw *hw) { rtl92c_dm_txpower_tracking_callback_thermalmeter(hw); } static void rtl92c_dm_check_txpower_tracking_thermal_meter( struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); static u8 tm_trigger; if (!rtlpriv->dm.txpower_tracking) return; if (!tm_trigger) { rtl_set_rfreg(hw, RF90_PATH_A, RF_T_METER, RFREG_OFFSET_MASK, 0x60); RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Trigger 92S Thermal Meter!!\n"); tm_trigger = 1; return; } else { RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Schedule TxPowerTracking direct call!!\n"); rtl92c_dm_txpower_tracking_directcall(hw); tm_trigger = 0; } } void rtl92c_dm_check_txpower_tracking(struct ieee80211_hw *hw) { rtl92c_dm_check_txpower_tracking_thermal_meter(hw); } EXPORT_SYMBOL(rtl92c_dm_check_txpower_tracking); void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rate_adaptive *p_ra = &(rtlpriv->ra); p_ra->ratr_state = DM_RATR_STA_INIT; p_ra->pre_ratr_state = DM_RATR_STA_INIT; if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER) rtlpriv->dm.useramask = true; else rtlpriv->dm.useramask = false; } EXPORT_SYMBOL(rtl92c_dm_init_rate_adaptive_mask); static void rtl92c_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rate_adaptive *p_ra = &(rtlpriv->ra); u32 low_rssithresh_for_ra, high_rssithresh_for_ra; struct ieee80211_sta *sta = NULL; if (is_hal_stop(rtlhal)) { RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, "<---- driver is going to unload\n"); return; } if (!rtlpriv->dm.useramask) { RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, "<---- driver does not control rate adaptive mask\n"); return; } if (mac->link_state == MAC80211_LINKED && mac->opmode == NL80211_IFTYPE_STATION) { switch (p_ra->pre_ratr_state) { case DM_RATR_STA_HIGH: high_rssithresh_for_ra = 50; low_rssithresh_for_ra = 20; break; case DM_RATR_STA_MIDDLE: high_rssithresh_for_ra = 55; low_rssithresh_for_ra = 20; break; case DM_RATR_STA_LOW: high_rssithresh_for_ra = 50; low_rssithresh_for_ra = 25; break; default: high_rssithresh_for_ra = 50; low_rssithresh_for_ra = 20; break; } if (rtlpriv->dm.undecorated_smoothed_pwdb > (long)high_rssithresh_for_ra) p_ra->ratr_state = DM_RATR_STA_HIGH; else if (rtlpriv->dm.undecorated_smoothed_pwdb > (long)low_rssithresh_for_ra) p_ra->ratr_state = DM_RATR_STA_MIDDLE; else p_ra->ratr_state = DM_RATR_STA_LOW; if (p_ra->pre_ratr_state != p_ra->ratr_state) { RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, "RSSI = %ld\n", rtlpriv->dm.undecorated_smoothed_pwdb); RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, "RSSI_LEVEL = %d\n", p_ra->ratr_state); RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, "PreState = %d, CurState = %d\n", p_ra->pre_ratr_state, p_ra->ratr_state); rcu_read_lock(); sta = ieee80211_find_sta(mac->vif, mac->bssid); rtlpriv->cfg->ops->update_rate_tbl(hw, sta, p_ra->ratr_state); p_ra->pre_ratr_state = p_ra->ratr_state; rcu_read_unlock(); } } } static void rtl92c_dm_init_dynamic_bb_powersaving(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct ps_t *dm_pstable = &rtlpriv->dm_pstable; dm_pstable->pre_ccastate = CCA_MAX; dm_pstable->cur_ccasate = CCA_MAX; dm_pstable->pre_rfstate = RF_MAX; dm_pstable->cur_rfstate = RF_MAX; dm_pstable->rssi_val_min = 0; } void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct ps_t *dm_pstable = &rtlpriv->dm_pstable; static u8 initialize; static u32 reg_874, reg_c70, reg_85c, reg_a74; if (initialize == 0) { reg_874 = (rtl_get_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, MASKDWORD) & 0x1CC000) >> 14; reg_c70 = (rtl_get_bbreg(hw, ROFDM0_AGCPARAMETER1, MASKDWORD) & BIT(3)) >> 3; reg_85c = (rtl_get_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, MASKDWORD) & 0xFF000000) >> 24; reg_a74 = (rtl_get_bbreg(hw, 0xa74, MASKDWORD) & 0xF000) >> 12; initialize = 1; } if (!bforce_in_normal) { if (dm_pstable->rssi_val_min != 0) { if (dm_pstable->pre_rfstate == RF_NORMAL) { if (dm_pstable->rssi_val_min >= 30) dm_pstable->cur_rfstate = RF_SAVE; else dm_pstable->cur_rfstate = RF_NORMAL; } else { if (dm_pstable->rssi_val_min <= 25) dm_pstable->cur_rfstate = RF_NORMAL; else dm_pstable->cur_rfstate = RF_SAVE; } } else { dm_pstable->cur_rfstate = RF_MAX; } } else { dm_pstable->cur_rfstate = RF_NORMAL; } if (dm_pstable->pre_rfstate != dm_pstable->cur_rfstate) { if (dm_pstable->cur_rfstate == RF_SAVE) { rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, 0x1C0000, 0x2); rtl_set_bbreg(hw, ROFDM0_AGCPARAMETER1, BIT(3), 0); rtl_set_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, 0xFF000000, 0x63); rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, 0xC000, 0x2); rtl_set_bbreg(hw, 0xa74, 0xF000, 0x3); rtl_set_bbreg(hw, 0x818, BIT(28), 0x0); rtl_set_bbreg(hw, 0x818, BIT(28), 0x1); } else { rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, 0x1CC000, reg_874); rtl_set_bbreg(hw, ROFDM0_AGCPARAMETER1, BIT(3), reg_c70); rtl_set_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, 0xFF000000, reg_85c); rtl_set_bbreg(hw, 0xa74, 0xF000, reg_a74); rtl_set_bbreg(hw, 0x818, BIT(28), 0x0); } dm_pstable->pre_rfstate = dm_pstable->cur_rfstate; } } EXPORT_SYMBOL(rtl92c_dm_rf_saving); static void rtl92c_dm_dynamic_bb_powersaving(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct ps_t *dm_pstable = &rtlpriv->dm_pstable; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); if (((mac->link_state == MAC80211_NOLINK)) && (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb == 0)) { dm_pstable->rssi_val_min = 0; RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, "Not connected to any\n"); } if (mac->link_state == MAC80211_LINKED) { if (mac->opmode == NL80211_IFTYPE_ADHOC) { dm_pstable->rssi_val_min = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, "AP Client PWDB = 0x%lx\n", dm_pstable->rssi_val_min); } else { dm_pstable->rssi_val_min = rtlpriv->dm.undecorated_smoothed_pwdb; RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, "STA Default Port PWDB = 0x%lx\n", dm_pstable->rssi_val_min); } } else { dm_pstable->rssi_val_min = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, "AP Ext Port PWDB = 0x%lx\n", dm_pstable->rssi_val_min); } if (IS_92C_SERIAL(rtlhal->version)) ;/* rtl92c_dm_1r_cca(hw); */ else rtl92c_dm_rf_saving(hw, false); } void rtl92c_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; rtl92c_dm_diginit(hw); rtl92c_dm_init_dynamic_txpower(hw); rtl92c_dm_init_edca_turbo(hw); rtl92c_dm_init_rate_adaptive_mask(hw); rtl92c_dm_initialize_txpower_tracking(hw); rtl92c_dm_init_dynamic_bb_powersaving(hw); } EXPORT_SYMBOL(rtl92c_dm_init); void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long undecorated_smoothed_pwdb; if (!rtlpriv->dm.dynamic_txpower_enable) return; if (rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if ((mac->link_state < MAC80211_LINKED) && (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb == 0)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, "Not connected to any\n"); rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; return; } if (mac->link_state >= MAC80211_LINKED) { if (mac->opmode == NL80211_IFTYPE_ADHOC) { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Client PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } else { undecorated_smoothed_pwdb = rtlpriv->dm.undecorated_smoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "STA Default Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } } else { undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "AP Ext Port PWDB = 0x%lx\n", undecorated_smoothed_pwdb); } if (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL2) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x0)\n"); } else if ((undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3)) && (undecorated_smoothed_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL1)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x10)\n"); } else if (undecorated_smoothed_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL1 - 5)) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_NORMAL\n"); } if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "PHY_SetTxPowerLevel8192S() Channel = %d\n", rtlphy->current_channel); rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); } rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; } void rtl92c_dm_watchdog(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool fw_current_inpsmode = false; bool fw_ps_awake = true; rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *) (&fw_current_inpsmode)); rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON, (u8 *) (&fw_ps_awake)); if ((ppsc->rfpwr_state == ERFON) && ((!fw_current_inpsmode) && fw_ps_awake) && (!ppsc->rfchange_inprogress)) { rtl92c_dm_pwdb_monitor(hw); rtl92c_dm_dig(hw); rtl92c_dm_false_alarm_counter_statistics(hw); rtl92c_dm_dynamic_bb_powersaving(hw); rtl92c_dm_dynamic_txpower(hw); rtl92c_dm_check_txpower_tracking(hw); rtl92c_dm_refresh_rate_adaptive_mask(hw); rtl92c_dm_bt_coexist(hw); rtl92c_dm_check_edca_turbo(hw); } } EXPORT_SYMBOL(rtl92c_dm_watchdog); u8 rtl92c_bt_rssi_state_change(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); long undecorated_smoothed_pwdb; u8 curr_bt_rssi_state = 0x00; if (rtlpriv->mac80211.link_state == MAC80211_LINKED) { undecorated_smoothed_pwdb = GET_UNDECORATED_AVERAGE_RSSI(rtlpriv); } else { if (rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb == 0) undecorated_smoothed_pwdb = 100; else undecorated_smoothed_pwdb = rtlpriv->dm.entry_min_undecoratedsmoothed_pwdb; } /* Check RSSI to determine HighPower/NormalPower state for * BT coexistence. */ if (undecorated_smoothed_pwdb >= 67) curr_bt_rssi_state &= (~BT_RSSI_STATE_NORMAL_POWER); else if (undecorated_smoothed_pwdb < 62) curr_bt_rssi_state |= BT_RSSI_STATE_NORMAL_POWER; /* Check RSSI to determine AMPDU setting for BT coexistence. */ if (undecorated_smoothed_pwdb >= 40) curr_bt_rssi_state &= (~BT_RSSI_STATE_AMDPU_OFF); else if (undecorated_smoothed_pwdb <= 32) curr_bt_rssi_state |= BT_RSSI_STATE_AMDPU_OFF; /* Marked RSSI state. It will be used to determine BT coexistence * setting later. */ if (undecorated_smoothed_pwdb < 35) curr_bt_rssi_state |= BT_RSSI_STATE_SPECIAL_LOW; else curr_bt_rssi_state &= (~BT_RSSI_STATE_SPECIAL_LOW); /* Set Tx Power according to BT status. */ if (undecorated_smoothed_pwdb >= 30) curr_bt_rssi_state |= BT_RSSI_STATE_TXPOWER_LOW; else if (undecorated_smoothed_pwdb < 25) curr_bt_rssi_state &= (~BT_RSSI_STATE_TXPOWER_LOW); /* Check BT state related to BT_Idle in B/G mode. */ if (undecorated_smoothed_pwdb < 15) curr_bt_rssi_state |= BT_RSSI_STATE_BG_EDCA_LOW; else curr_bt_rssi_state &= (~BT_RSSI_STATE_BG_EDCA_LOW); if (curr_bt_rssi_state != rtlpcipriv->bt_coexist.bt_rssi_state) { rtlpcipriv->bt_coexist.bt_rssi_state = curr_bt_rssi_state; return true; } else { return false; } } EXPORT_SYMBOL(rtl92c_bt_rssi_state_change); static bool rtl92c_bt_state_change(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); u32 polling, ratio_tx, ratio_pri; u32 bt_tx, bt_pri; u8 bt_state; u8 cur_service_type; if (rtlpriv->mac80211.link_state < MAC80211_LINKED) return false; bt_state = rtl_read_byte(rtlpriv, 0x4fd); bt_tx = rtl_read_dword(rtlpriv, 0x488); bt_tx = bt_tx & 0x00ffffff; bt_pri = rtl_read_dword(rtlpriv, 0x48c); bt_pri = bt_pri & 0x00ffffff; polling = rtl_read_dword(rtlpriv, 0x490); if (bt_tx == 0xffffffff && bt_pri == 0xffffffff && polling == 0xffffffff && bt_state == 0xff) return false; bt_state &= BIT_OFFSET_LEN_MASK_32(0, 1); if (bt_state != rtlpcipriv->bt_coexist.bt_cur_state) { rtlpcipriv->bt_coexist.bt_cur_state = bt_state; if (rtlpcipriv->bt_coexist.reg_bt_sco == 3) { rtlpcipriv->bt_coexist.bt_service = BT_IDLE; bt_state = bt_state | ((rtlpcipriv->bt_coexist.bt_ant_isolation == 1) ? 0 : BIT_OFFSET_LEN_MASK_32(1, 1)) | BIT_OFFSET_LEN_MASK_32(2, 1); rtl_write_byte(rtlpriv, 0x4fd, bt_state); } return true; } ratio_tx = bt_tx * 1000 / polling; ratio_pri = bt_pri * 1000 / polling; rtlpcipriv->bt_coexist.ratio_tx = ratio_tx; rtlpcipriv->bt_coexist.ratio_pri = ratio_pri; if (bt_state && rtlpcipriv->bt_coexist.reg_bt_sco == 3) { if ((ratio_tx < 30) && (ratio_pri < 30)) cur_service_type = BT_IDLE; else if ((ratio_pri > 110) && (ratio_pri < 250)) cur_service_type = BT_SCO; else if ((ratio_tx >= 200) && (ratio_pri >= 200)) cur_service_type = BT_BUSY; else if ((ratio_tx >= 350) && (ratio_tx < 500)) cur_service_type = BT_OTHERBUSY; else if (ratio_tx >= 500) cur_service_type = BT_PAN; else cur_service_type = BT_OTHER_ACTION; if (cur_service_type != rtlpcipriv->bt_coexist.bt_service) { rtlpcipriv->bt_coexist.bt_service = cur_service_type; bt_state = bt_state | ((rtlpcipriv->bt_coexist.bt_ant_isolation == 1) ? 0 : BIT_OFFSET_LEN_MASK_32(1, 1)) | ((rtlpcipriv->bt_coexist.bt_service != BT_IDLE) ? 0 : BIT_OFFSET_LEN_MASK_32(2, 1)); /* Add interrupt migration when bt is not ini * idle state (no traffic). */ if (rtlpcipriv->bt_coexist.bt_service != BT_IDLE) { rtl_write_word(rtlpriv, 0x504, 0x0ccc); rtl_write_byte(rtlpriv, 0x506, 0x54); rtl_write_byte(rtlpriv, 0x507, 0x54); } else { rtl_write_byte(rtlpriv, 0x506, 0x00); rtl_write_byte(rtlpriv, 0x507, 0x00); } rtl_write_byte(rtlpriv, 0x4fd, bt_state); return true; } } return false; } static bool rtl92c_bt_wifi_connect_change(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); static bool media_connect; if (rtlpriv->mac80211.link_state < MAC80211_LINKED) { media_connect = false; } else { if (!media_connect) { media_connect = true; return true; } media_connect = true; } return false; } static void rtl92c_bt_set_normal(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); if (rtlpcipriv->bt_coexist.bt_service == BT_OTHERBUSY) { rtlpcipriv->bt_coexist.bt_edca_ul = 0x5ea72b; rtlpcipriv->bt_coexist.bt_edca_dl = 0x5ea72b; } else if (rtlpcipriv->bt_coexist.bt_service == BT_BUSY) { rtlpcipriv->bt_coexist.bt_edca_ul = 0x5eb82f; rtlpcipriv->bt_coexist.bt_edca_dl = 0x5eb82f; } else if (rtlpcipriv->bt_coexist.bt_service == BT_SCO) { if (rtlpcipriv->bt_coexist.ratio_tx > 160) { rtlpcipriv->bt_coexist.bt_edca_ul = 0x5ea72f; rtlpcipriv->bt_coexist.bt_edca_dl = 0x5ea72f; } else { rtlpcipriv->bt_coexist.bt_edca_ul = 0x5ea32b; rtlpcipriv->bt_coexist.bt_edca_dl = 0x5ea42b; } } else { rtlpcipriv->bt_coexist.bt_edca_ul = 0; rtlpcipriv->bt_coexist.bt_edca_dl = 0; } if ((rtlpcipriv->bt_coexist.bt_service != BT_IDLE) && (rtlpriv->mac80211.mode == WIRELESS_MODE_G || (rtlpriv->mac80211.mode == (WIRELESS_MODE_G | WIRELESS_MODE_B))) && (rtlpcipriv->bt_coexist.bt_rssi_state & BT_RSSI_STATE_BG_EDCA_LOW)) { rtlpcipriv->bt_coexist.bt_edca_ul = 0x5eb82b; rtlpcipriv->bt_coexist.bt_edca_dl = 0x5eb82b; } } static void rtl92c_bt_ant_isolation(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); /* Only enable HW BT coexist when BT in "Busy" state. */ if (rtlpriv->mac80211.vendor == PEER_CISCO && rtlpcipriv->bt_coexist.bt_service == BT_OTHER_ACTION) { rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0); } else { if ((rtlpcipriv->bt_coexist.bt_service == BT_BUSY) && (rtlpcipriv->bt_coexist.bt_rssi_state & BT_RSSI_STATE_NORMAL_POWER)) { rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0); } else if ((rtlpcipriv->bt_coexist.bt_service == BT_OTHER_ACTION) && (rtlpriv->mac80211.mode < WIRELESS_MODE_N_24G) && (rtlpcipriv->bt_coexist.bt_rssi_state & BT_RSSI_STATE_SPECIAL_LOW)) { rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0); } else if (rtlpcipriv->bt_coexist.bt_service == BT_PAN) { rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0x00); } else { rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0x00); } } if (rtlpcipriv->bt_coexist.bt_service == BT_PAN) rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x10100); else rtl_write_dword(rtlpriv, REG_GPIO_PIN_CTRL, 0x0); if (rtlpcipriv->bt_coexist.bt_rssi_state & BT_RSSI_STATE_NORMAL_POWER) { rtl92c_bt_set_normal(hw); } else { rtlpcipriv->bt_coexist.bt_edca_ul = 0; rtlpcipriv->bt_coexist.bt_edca_dl = 0; } if (rtlpcipriv->bt_coexist.bt_service != BT_IDLE) { rtlpriv->cfg->ops->set_rfreg(hw, RF90_PATH_A, 0x1e, 0xf0, 0xf); } else { rtlpriv->cfg->ops->set_rfreg(hw, RF90_PATH_A, 0x1e, 0xf0, rtlpcipriv->bt_coexist.bt_rfreg_origin_1e); } if (!rtlpriv->dm.dynamic_txpower_enable) { if (rtlpcipriv->bt_coexist.bt_service != BT_IDLE) { if (rtlpcipriv->bt_coexist.bt_rssi_state & BT_RSSI_STATE_TXPOWER_LOW) { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_BT2; } else { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_BT1; } } else { rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; } rtl92c_phy_set_txpower_level(hw, rtlpriv->phy.current_channel); } } static void rtl92c_check_bt_change(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); if (rtlpcipriv->bt_coexist.bt_cur_state) { if (rtlpcipriv->bt_coexist.bt_ant_isolation) rtl92c_bt_ant_isolation(hw); } else { rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0x00); rtlpriv->cfg->ops->set_rfreg(hw, RF90_PATH_A, 0x1e, 0xf0, rtlpcipriv->bt_coexist.bt_rfreg_origin_1e); rtlpcipriv->bt_coexist.bt_edca_ul = 0; rtlpcipriv->bt_coexist.bt_edca_dl = 0; } } void rtl92c_dm_bt_coexist(struct ieee80211_hw *hw) { struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); bool wifi_connect_change; bool bt_state_change; bool rssi_state_change; if ((rtlpcipriv->bt_coexist.bt_coexistence) && (rtlpcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) { wifi_connect_change = rtl92c_bt_wifi_connect_change(hw); bt_state_change = rtl92c_bt_state_change(hw); rssi_state_change = rtl92c_bt_rssi_state_change(hw); if (wifi_connect_change || bt_state_change || rssi_state_change) rtl92c_check_bt_change(hw); } } EXPORT_SYMBOL(rtl92c_dm_bt_coexist); compat-drivers-2012-09-18/drivers/net/wireless/rtlwifi/rtl8192c/Makefile0000644000175000017500000000025312026211315025071 0ustar mcgrofmcgrofrtl8192c-common-objs := \ main.o \ dm_common.o \ fw_common.o \ phy_common.o obj-$(CONFIG_RTL8192C_COMMON) += rtl8192c-common.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/0000755000175000017500000000000012026211315021713 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl818x.h0000644000175000017500000001630312026211315023321 0ustar mcgrofmcgrof/* * Definitions for RTL818x hardware * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8187 driver, which is: * Copyright 2005 Andrea Merello , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef RTL818X_H #define RTL818X_H struct rtl818x_csr { u8 MAC[6]; u8 reserved_0[2]; __le32 MAR[2]; u8 RX_FIFO_COUNT; u8 reserved_1; u8 TX_FIFO_COUNT; u8 BQREQ; u8 reserved_2[4]; __le32 TSFT[2]; __le32 TLPDA; __le32 TNPDA; __le32 THPDA; __le16 BRSR; u8 BSSID[6]; u8 RESP_RATE; u8 EIFS; u8 reserved_3[1]; u8 CMD; #define RTL818X_CMD_TX_ENABLE (1 << 2) #define RTL818X_CMD_RX_ENABLE (1 << 3) #define RTL818X_CMD_RESET (1 << 4) u8 reserved_4[4]; __le16 INT_MASK; __le16 INT_STATUS; #define RTL818X_INT_RX_OK (1 << 0) #define RTL818X_INT_RX_ERR (1 << 1) #define RTL818X_INT_TXL_OK (1 << 2) #define RTL818X_INT_TXL_ERR (1 << 3) #define RTL818X_INT_RX_DU (1 << 4) #define RTL818X_INT_RX_FO (1 << 5) #define RTL818X_INT_TXN_OK (1 << 6) #define RTL818X_INT_TXN_ERR (1 << 7) #define RTL818X_INT_TXH_OK (1 << 8) #define RTL818X_INT_TXH_ERR (1 << 9) #define RTL818X_INT_TXB_OK (1 << 10) #define RTL818X_INT_TXB_ERR (1 << 11) #define RTL818X_INT_ATIM (1 << 12) #define RTL818X_INT_BEACON (1 << 13) #define RTL818X_INT_TIME_OUT (1 << 14) #define RTL818X_INT_TX_FO (1 << 15) __le32 TX_CONF; #define RTL818X_TX_CONF_LOOPBACK_MAC (1 << 17) #define RTL818X_TX_CONF_LOOPBACK_CONT (3 << 17) #define RTL818X_TX_CONF_NO_ICV (1 << 19) #define RTL818X_TX_CONF_DISCW (1 << 20) #define RTL818X_TX_CONF_SAT_HWPLCP (1 << 24) #define RTL818X_TX_CONF_R8180_ABCD (2 << 25) #define RTL818X_TX_CONF_R8180_F (3 << 25) #define RTL818X_TX_CONF_R8185_ABC (4 << 25) #define RTL818X_TX_CONF_R8185_D (5 << 25) #define RTL818X_TX_CONF_R8187vD (5 << 25) #define RTL818X_TX_CONF_R8187vD_B (6 << 25) #define RTL818X_TX_CONF_HWVER_MASK (7 << 25) #define RTL818X_TX_CONF_DISREQQSIZE (1 << 28) #define RTL818X_TX_CONF_PROBE_DTS (1 << 29) #define RTL818X_TX_CONF_HW_SEQNUM (1 << 30) #define RTL818X_TX_CONF_CW_MIN (1 << 31) __le32 RX_CONF; #define RTL818X_RX_CONF_MONITOR (1 << 0) #define RTL818X_RX_CONF_NICMAC (1 << 1) #define RTL818X_RX_CONF_MULTICAST (1 << 2) #define RTL818X_RX_CONF_BROADCAST (1 << 3) #define RTL818X_RX_CONF_FCS (1 << 5) #define RTL818X_RX_CONF_DATA (1 << 18) #define RTL818X_RX_CONF_CTRL (1 << 19) #define RTL818X_RX_CONF_MGMT (1 << 20) #define RTL818X_RX_CONF_ADDR3 (1 << 21) #define RTL818X_RX_CONF_PM (1 << 22) #define RTL818X_RX_CONF_BSSID (1 << 23) #define RTL818X_RX_CONF_RX_AUTORESETPHY (1 << 28) #define RTL818X_RX_CONF_CSDM1 (1 << 29) #define RTL818X_RX_CONF_CSDM2 (1 << 30) #define RTL818X_RX_CONF_ONLYERLPKT (1 << 31) __le32 INT_TIMEOUT; __le32 TBDA; u8 EEPROM_CMD; #define RTL818X_EEPROM_CMD_READ (1 << 0) #define RTL818X_EEPROM_CMD_WRITE (1 << 1) #define RTL818X_EEPROM_CMD_CK (1 << 2) #define RTL818X_EEPROM_CMD_CS (1 << 3) #define RTL818X_EEPROM_CMD_NORMAL (0 << 6) #define RTL818X_EEPROM_CMD_LOAD (1 << 6) #define RTL818X_EEPROM_CMD_PROGRAM (2 << 6) #define RTL818X_EEPROM_CMD_CONFIG (3 << 6) u8 CONFIG0; u8 CONFIG1; u8 CONFIG2; #define RTL818X_CONFIG2_ANTENNA_DIV (1 << 6) __le32 ANAPARAM; u8 MSR; #define RTL818X_MSR_NO_LINK (0 << 2) #define RTL818X_MSR_ADHOC (1 << 2) #define RTL818X_MSR_INFRA (2 << 2) #define RTL818X_MSR_MASTER (3 << 2) #define RTL818X_MSR_ENEDCA (4 << 2) u8 CONFIG3; #define RTL818X_CONFIG3_ANAPARAM_WRITE (1 << 6) #define RTL818X_CONFIG3_GNT_SELECT (1 << 7) u8 CONFIG4; #define RTL818X_CONFIG4_POWEROFF (1 << 6) #define RTL818X_CONFIG4_VCOOFF (1 << 7) u8 TESTR; u8 reserved_9[2]; u8 PGSELECT; u8 SECURITY; __le32 ANAPARAM2; u8 reserved_10[12]; __le16 BEACON_INTERVAL; __le16 ATIM_WND; __le16 BEACON_INTERVAL_TIME; __le16 ATIMTR_INTERVAL; u8 PHY_DELAY; u8 CARRIER_SENSE_COUNTER; u8 reserved_11[2]; u8 PHY[4]; __le16 RFPinsOutput; __le16 RFPinsEnable; __le16 RFPinsSelect; __le16 RFPinsInput; __le32 RF_PARA; __le32 RF_TIMING; u8 GP_ENABLE; u8 GPIO0; u8 GPIO1; u8 reserved_12; __le32 HSSI_PARA; u8 reserved_13[4]; u8 TX_AGC_CTL; #define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0) #define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1) #define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2) u8 TX_GAIN_CCK; u8 TX_GAIN_OFDM; u8 TX_ANTENNA; u8 reserved_14[16]; u8 WPA_CONF; u8 reserved_15[3]; u8 SIFS; u8 DIFS; u8 SLOT; u8 reserved_16[5]; u8 CW_CONF; #define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0) #define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1) u8 CW_VAL; u8 RATE_FALLBACK; #define RTL818X_RATE_FALLBACK_ENABLE (1 << 7) u8 ACM_CONTROL; u8 reserved_17[24]; u8 CONFIG5; u8 TX_DMA_POLLING; u8 reserved_18[2]; __le16 CWR; u8 RETRY_CTR; u8 reserved_19[3]; __le16 INT_MIG; /* RTL818X_R8187B_*: magic numbers from ioregisters */ #define RTL818X_R8187B_B 0 #define RTL818X_R8187B_D 1 #define RTL818X_R8187B_E 2 __le32 RDSAR; __le16 TID_AC_MAP; u8 reserved_20[4]; u8 ANAPARAM3; u8 reserved_21[5]; __le16 FEMR; u8 reserved_22[4]; __le16 TALLY_CNT; u8 TALLY_SEL; } __packed; struct rtl818x_rf_ops { char *name; void (*init)(struct ieee80211_hw *); void (*stop)(struct ieee80211_hw *); void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *); void (*conf_erp)(struct ieee80211_hw *, struct ieee80211_bss_conf *); u8 (*calc_rssi)(u8 agc, u8 sq); }; /** * enum rtl818x_tx_desc_flags - Tx/Rx flags are common between RTL818X chips * * @RTL818X_TX_DESC_FLAG_NO_ENC: Disable hardware based encryption. * @RTL818X_TX_DESC_FLAG_TX_OK: TX frame was ACKed. * @RTL818X_TX_DESC_FLAG_SPLCP: Use short preamble. * @RTL818X_TX_DESC_FLAG_MOREFRAG: More fragments follow. * @RTL818X_TX_DESC_FLAG_CTS: Use CTS-to-self protection. * @RTL818X_TX_DESC_FLAG_RTS: Use RTS/CTS protection. * @RTL818X_TX_DESC_FLAG_LS: Last segment of the frame. * @RTL818X_TX_DESC_FLAG_FS: First segment of the frame. */ enum rtl818x_tx_desc_flags { RTL818X_TX_DESC_FLAG_NO_ENC = (1 << 15), RTL818X_TX_DESC_FLAG_TX_OK = (1 << 15), RTL818X_TX_DESC_FLAG_SPLCP = (1 << 16), RTL818X_TX_DESC_FLAG_RX_UNDER = (1 << 16), RTL818X_TX_DESC_FLAG_MOREFRAG = (1 << 17), RTL818X_TX_DESC_FLAG_CTS = (1 << 18), RTL818X_TX_DESC_FLAG_RTS = (1 << 23), RTL818X_TX_DESC_FLAG_LS = (1 << 28), RTL818X_TX_DESC_FLAG_FS = (1 << 29), RTL818X_TX_DESC_FLAG_DMA = (1 << 30), RTL818X_TX_DESC_FLAG_OWN = (1 << 31) }; enum rtl818x_rx_desc_flags { RTL818X_RX_DESC_FLAG_ICV_ERR = (1 << 12), RTL818X_RX_DESC_FLAG_CRC32_ERR = (1 << 13), RTL818X_RX_DESC_FLAG_PM = (1 << 14), RTL818X_RX_DESC_FLAG_RX_ERR = (1 << 15), RTL818X_RX_DESC_FLAG_BCAST = (1 << 16), RTL818X_RX_DESC_FLAG_PAM = (1 << 17), RTL818X_RX_DESC_FLAG_MCAST = (1 << 18), RTL818X_RX_DESC_FLAG_QOS = (1 << 19), /* RTL8187(B) only */ RTL818X_RX_DESC_FLAG_TRSW = (1 << 24), /* RTL8187(B) only */ RTL818X_RX_DESC_FLAG_SPLCP = (1 << 25), RTL818X_RX_DESC_FLAG_FOF = (1 << 26), RTL818X_RX_DESC_FLAG_DMA_FAIL = (1 << 27), RTL818X_RX_DESC_FLAG_LS = (1 << 28), RTL818X_RX_DESC_FLAG_FS = (1 << 29), RTL818X_RX_DESC_FLAG_EOR = (1 << 30), RTL818X_RX_DESC_FLAG_OWN = (1 << 31) }; #endif /* RTL818X_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/Makefile0000644000175000017500000000010412026211315023346 0ustar mcgrofmcgrofobj-$(CONFIG_RTL8180) += rtl8180/ obj-$(CONFIG_RTL8187) += rtl8187/ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/Kconfig0000644000175000017500000000401312026211315023214 0ustar mcgrofmcgrof# # RTL818X Wireless LAN device configuration # config RTL8180 tristate "Realtek 8180/8185 PCI support" depends on MAC80211 && PCI && EXPERIMENTAL select EEPROM_93CX6 ---help--- This is a driver for RTL8180 and RTL8185 based cards. These are PCI based chips found in cards such as: (RTL8185 802.11g) A-Link WL54PC (RTL8180 802.11b) Belkin F5D6020 v3 Belkin F5D6020 v3 Dlink DWL-610 Dlink DWL-510 Netgear MA521 Level-One WPC-0101 Acer Aspire 1357 LMi VCTnet PC-11B1 Ovislink AirLive WL-1120PCM Mentor WL-PCI Linksys WPC11 v4 TrendNET TEW-288PI D-Link DWL-520 Rev D Repotec RP-WP7126 TP-Link TL-WN250/251 Zonet ZEW1000 Longshine LCS-8031-R HomeLine HLW-PCC200 GigaFast WF721-AEX Planet WL-3553 Encore ENLWI-PCI1-NT TrendNET TEW-266PC Gigabyte GN-WLMR101 Siemens-fujitsu Amilo D1840W Edimax EW-7126 PheeNet WL-11PCIR Tonze PC-2100T Planet WL-8303 Dlink DWL-650 v M1 Edimax EW-7106 Q-Tec 770WC Topcom Skyr@cer 4011b Roper FreeLan 802.11b (edition 2004) Wistron Neweb Corp CB-200B Pentagram HorNET QTec 775WC TwinMOS Booming B Series Micronet SP906BB Sweex LC700010 Surecom EP-9428 Safecom SWLCR-1100 Thanks to Realtek for their support! config RTL8187 tristate "Realtek 8187 and 8187B USB support" depends on MAC80211 && USB select EEPROM_93CX6 ---help--- This is a driver for RTL8187 and RTL8187B based cards. These are USB based chips found in devices such as: Netgear WG111v2 Level 1 WNC-0301USB Micronet SP907GK V5 Encore ENUWI-G2 Trendnet TEW-424UB ASUS P5B Deluxe/P5K Premium motherboards Toshiba Satellite Pro series of laptops Asus Wireless Link Linksys WUSB54GC-EU v2 (v1 = rt73usb; v3 is rt2070-based, use staging/rt3070 or try rt2800usb) Thanks to Realtek for their support! # If possible, automatically enable LEDs for RTL8187. config RTL8187_LEDS bool depends on RTL8187 && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = RTL8187) default y compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/0000755000175000017500000000000012026211315023044 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/dev.c0000644000175000017500000014105212026211315023771 0ustar mcgrofmcgrof/* * Linux device driver for RTL8187 * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8187 driver, which is: * Copyright 2005 Andrea Merello , et al. * * The driver was extended to the RTL8187B in 2008 by: * Herton Ronaldo Krzesinski * Hin-Tak Leung * Larry Finger * * Magic delays and register offsets below are taken from the original * r8187 driver sources. Thanks to Realtek for their support! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include "rtl8187.h" #include "rtl8225.h" #ifdef CONFIG_RTL8187_LEDS #include "leds.h" #endif #include "rfkill.h" MODULE_AUTHOR("Michael Wu "); MODULE_AUTHOR("Andrea Merello "); MODULE_AUTHOR("Herton Ronaldo Krzesinski "); MODULE_AUTHOR("Hin-Tak Leung "); MODULE_AUTHOR("Larry Finger "); MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); MODULE_LICENSE("GPL"); static struct usb_device_id rtl8187_table[] = { /* Asus */ {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, /* Belkin */ {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B}, /* Realtek */ {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187}, {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B}, {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B}, {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B}, /* Surecom */ {USB_DEVICE(0x0769, 0x11F2), .driver_info = DEVICE_RTL8187}, /* Logitech */ {USB_DEVICE(0x0789, 0x010C), .driver_info = DEVICE_RTL8187}, /* Netgear */ {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187}, {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187}, {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B}, /* HP */ {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, /* Sitecom */ {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, {USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B}, /* Sphairon Access Systems GmbH */ {USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187}, /* Dick Smith Electronics */ {USB_DEVICE(0x1371, 0x9401), .driver_info = DEVICE_RTL8187}, /* Abocom */ {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, /* Qcom */ {USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187}, /* AirLive */ {USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187}, /* Linksys */ {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B}, {} }; MODULE_DEVICE_TABLE(usb, rtl8187_table); static const struct ieee80211_rate rtl818x_rates[] = { { .bitrate = 10, .hw_value = 0, }, { .bitrate = 20, .hw_value = 1, }, { .bitrate = 55, .hw_value = 2, }, { .bitrate = 110, .hw_value = 3, }, { .bitrate = 60, .hw_value = 4, }, { .bitrate = 90, .hw_value = 5, }, { .bitrate = 120, .hw_value = 6, }, { .bitrate = 180, .hw_value = 7, }, { .bitrate = 240, .hw_value = 8, }, { .bitrate = 360, .hw_value = 9, }, { .bitrate = 480, .hw_value = 10, }, { .bitrate = 540, .hw_value = 11, }, }; static const struct ieee80211_channel rtl818x_channels[] = { { .center_freq = 2412 }, { .center_freq = 2417 }, { .center_freq = 2422 }, { .center_freq = 2427 }, { .center_freq = 2432 }, { .center_freq = 2437 }, { .center_freq = 2442 }, { .center_freq = 2447 }, { .center_freq = 2452 }, { .center_freq = 2457 }, { .center_freq = 2462 }, { .center_freq = 2467 }, { .center_freq = 2472 }, { .center_freq = 2484 }, }; static void rtl8187_iowrite_async_cb(struct urb *urb) { kfree(urb->context); } static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, void *data, u16 len) { struct usb_ctrlrequest *dr; struct urb *urb; struct rtl8187_async_write_data { u8 data[4]; struct usb_ctrlrequest dr; } *buf; int rc; buf = kmalloc(sizeof(*buf), GFP_ATOMIC); if (!buf) return; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { kfree(buf); return; } dr = &buf->dr; dr->bRequestType = RTL8187_REQT_WRITE; dr->bRequest = RTL8187_REQ_SET_REG; dr->wValue = addr; dr->wIndex = 0; dr->wLength = cpu_to_le16(len); memcpy(buf, data, len); usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), (unsigned char *)dr, buf, len, rtl8187_iowrite_async_cb, buf); usb_anchor_urb(urb, &priv->anchored); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc < 0) { kfree(buf); usb_unanchor_urb(urb); } usb_free_urb(urb); } static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, __le32 *addr, u32 val) { __le32 buf = cpu_to_le32(val); rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr), &buf, sizeof(buf)); } void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) { struct rtl8187_priv *priv = dev->priv; data <<= 8; data |= addr | 0x80; rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF); rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF); rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF); rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF); } static void rtl8187_tx_cb(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *)urb->context; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hw *hw = info->rate_driver_data[0]; struct rtl8187_priv *priv = hw->priv; skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : sizeof(struct rtl8187_tx_hdr)); ieee80211_tx_info_clear_status(info); if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { if (priv->is_rtl8187b) { skb_queue_tail(&priv->b_tx_status.queue, skb); /* queue is "full", discard last items */ while (skb_queue_len(&priv->b_tx_status.queue) > 5) { struct sk_buff *old_skb; dev_dbg(&priv->udev->dev, "transmit status queue full\n"); old_skb = skb_dequeue(&priv->b_tx_status.queue); ieee80211_tx_status_irqsafe(hw, old_skb); } return; } else { info->flags |= IEEE80211_TX_STAT_ACK; } } if (priv->is_rtl8187b) ieee80211_tx_status_irqsafe(hw, skb); else { /* Retry information for the RTI8187 is only available by * reading a register in the device. We are in interrupt mode * here, thus queue the skb and finish on a work queue. */ skb_queue_tail(&priv->b_tx_status.queue, skb); ieee80211_queue_delayed_work(hw, &priv->work, 0); } } static void rtl8187_tx(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct rtl8187_priv *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *tx_hdr = (struct ieee80211_hdr *)(skb->data); unsigned int ep; void *buf; struct urb *urb; __le16 rts_dur = 0; u32 flags; int rc; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { kfree_skb(skb); return; } flags = skb->len; flags |= RTL818X_TX_DESC_FLAG_NO_ENC; flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; if (ieee80211_has_morefrags(tx_hdr->frame_control)) flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { flags |= RTL818X_TX_DESC_FLAG_RTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; rts_dur = ieee80211_rts_duration(dev, priv->vif, skb->len, info); } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { flags |= RTL818X_TX_DESC_FLAG_CTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; } if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) priv->seqno += 0x10; tx_hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); tx_hdr->seq_ctrl |= cpu_to_le16(priv->seqno); } if (!priv->is_rtl8187b) { struct rtl8187_tx_hdr *hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr)); hdr->flags = cpu_to_le32(flags); hdr->len = 0; hdr->rts_duration = rts_dur; hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); buf = hdr; ep = 2; } else { /* fc needs to be calculated before skb_push() */ unsigned int epmap[4] = { 6, 7, 5, 4 }; u16 fc = le16_to_cpu(tx_hdr->frame_control); struct rtl8187b_tx_hdr *hdr = (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr)); struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, info); memset(hdr, 0, sizeof(*hdr)); hdr->flags = cpu_to_le32(flags); hdr->rts_duration = rts_dur; hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); hdr->tx_duration = ieee80211_generic_frame_duration(dev, priv->vif, info->band, skb->len, txrate); buf = hdr; if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) ep = 12; else ep = epmap[skb_get_queue_mapping(skb)]; } info->rate_driver_data[0] = dev; info->rate_driver_data[1] = urb; usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), buf, skb->len, rtl8187_tx_cb, skb); urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(urb, &priv->anchored); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc < 0) { usb_unanchor_urb(urb); kfree_skb(skb); } usb_free_urb(urb); } static void rtl8187_rx_cb(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *)urb->context; struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb; struct ieee80211_hw *dev = info->dev; struct rtl8187_priv *priv = dev->priv; struct ieee80211_rx_status rx_status = { 0 }; int rate, signal; u32 flags; unsigned long f; spin_lock_irqsave(&priv->rx_queue.lock, f); __skb_unlink(skb, &priv->rx_queue); spin_unlock_irqrestore(&priv->rx_queue.lock, f); skb_put(skb, urb->actual_length); if (unlikely(urb->status)) { dev_kfree_skb_irq(skb); return; } if (!priv->is_rtl8187b) { struct rtl8187_rx_hdr *hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); flags = le32_to_cpu(hdr->flags); /* As with the RTL8187B below, the AGC is used to calculate * signal strength. In this case, the scaling * constants are derived from the output of p54usb. */ signal = -4 - ((27 * hdr->agc) >> 6); rx_status.antenna = (hdr->signal >> 7) & 1; rx_status.mactime = le64_to_cpu(hdr->mac_time); } else { struct rtl8187b_rx_hdr *hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); /* The Realtek datasheet for the RTL8187B shows that the RX * header contains the following quantities: signal quality, * RSSI, AGC, the received power in dB, and the measured SNR. * In testing, none of these quantities show qualitative * agreement with AP signal strength, except for the AGC, * which is inversely proportional to the strength of the * signal. In the following, the signal strength * is derived from the AGC. The arbitrary scaling constants * are chosen to make the results close to the values obtained * for a BCM4312 using b43 as the driver. The noise is ignored * for now. */ flags = le32_to_cpu(hdr->flags); signal = 14 - hdr->agc / 2; rx_status.antenna = (hdr->rssi >> 7) & 1; rx_status.mactime = le64_to_cpu(hdr->mac_time); } rx_status.signal = signal; priv->signal = signal; rate = (flags >> 20) & 0xF; skb_trim(skb, flags & 0x0FFF); rx_status.rate_idx = rate; rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; rx_status.flag |= RX_FLAG_MACTIME_MPDU; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(dev, skb); skb = dev_alloc_skb(RTL8187_MAX_RX); if (unlikely(!skb)) { /* TODO check rx queue length and refill *somewhere* */ return; } info = (struct rtl8187_rx_info *)skb->cb; info->urb = urb; info->dev = dev; urb->transfer_buffer = skb_tail_pointer(skb); urb->context = skb; skb_queue_tail(&priv->rx_queue, skb); usb_anchor_urb(urb, &priv->anchored); if (usb_submit_urb(urb, GFP_ATOMIC)) { usb_unanchor_urb(urb); skb_unlink(skb, &priv->rx_queue); dev_kfree_skb_irq(skb); } } static int rtl8187_init_urbs(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; struct urb *entry = NULL; struct sk_buff *skb; struct rtl8187_rx_info *info; int ret = 0; while (skb_queue_len(&priv->rx_queue) < 16) { skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); if (!skb) { ret = -ENOMEM; goto err; } entry = usb_alloc_urb(0, GFP_KERNEL); if (!entry) { ret = -ENOMEM; goto err; } usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, priv->is_rtl8187b ? 3 : 1), skb_tail_pointer(skb), RTL8187_MAX_RX, rtl8187_rx_cb, skb); info = (struct rtl8187_rx_info *)skb->cb; info->urb = entry; info->dev = dev; skb_queue_tail(&priv->rx_queue, skb); usb_anchor_urb(entry, &priv->anchored); ret = usb_submit_urb(entry, GFP_KERNEL); if (ret) { skb_unlink(skb, &priv->rx_queue); usb_unanchor_urb(entry); goto err; } usb_free_urb(entry); } return ret; err: usb_free_urb(entry); kfree_skb(skb); usb_kill_anchored_urbs(&priv->anchored); return ret; } static void rtl8187b_status_cb(struct urb *urb) { struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; struct rtl8187_priv *priv = hw->priv; u64 val; unsigned int cmd_type; if (unlikely(urb->status)) return; /* * Read from status buffer: * * bits [30:31] = cmd type: * - 0 indicates tx beacon interrupt * - 1 indicates tx close descriptor * * In the case of tx beacon interrupt: * [0:9] = Last Beacon CW * [10:29] = reserved * [30:31] = 00b * [32:63] = Last Beacon TSF * * If it's tx close descriptor: * [0:7] = Packet Retry Count * [8:14] = RTS Retry Count * [15] = TOK * [16:27] = Sequence No * [28] = LS * [29] = FS * [30:31] = 01b * [32:47] = unused (reserved?) * [48:63] = MAC Used Time */ val = le64_to_cpu(priv->b_tx_status.buf); cmd_type = (val >> 30) & 0x3; if (cmd_type == 1) { unsigned int pkt_rc, seq_no; bool tok; struct sk_buff *skb; struct ieee80211_hdr *ieee80211hdr; unsigned long flags; pkt_rc = val & 0xFF; tok = val & (1 << 15); seq_no = (val >> 16) & 0xFFF; spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) { ieee80211hdr = (struct ieee80211_hdr *)skb->data; /* * While testing, it was discovered that the seq_no * doesn't actually contains the sequence number. * Instead of returning just the 12 bits of sequence * number, hardware is returning entire sequence control * (fragment number plus sequence number) in a 12 bit * only field overflowing after some time. As a * workaround, just consider the lower bits, and expect * it's unlikely we wrongly ack some sent data */ if ((le16_to_cpu(ieee80211hdr->seq_ctrl) & 0xFFF) == seq_no) break; } if (skb != (struct sk_buff *) &priv->b_tx_status.queue) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); __skb_unlink(skb, &priv->b_tx_status.queue); if (tok) info->flags |= IEEE80211_TX_STAT_ACK; info->status.rates[0].count = pkt_rc + 1; ieee80211_tx_status_irqsafe(hw, skb); } spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); } usb_anchor_urb(urb, &priv->anchored); if (usb_submit_urb(urb, GFP_ATOMIC)) usb_unanchor_urb(urb); } static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; struct urb *entry; int ret = 0; entry = usb_alloc_urb(0, GFP_KERNEL); if (!entry) return -ENOMEM; usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), rtl8187b_status_cb, dev); usb_anchor_urb(entry, &priv->anchored); ret = usb_submit_urb(entry, GFP_KERNEL); if (ret) usb_unanchor_urb(entry); usb_free_urb(entry); return ret; } static void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon) { u32 anaparam, anaparam2; u8 anaparam3, reg; if (!priv->is_rtl8187b) { if (rfon) { anaparam = RTL8187_RTL8225_ANAPARAM_ON; anaparam2 = RTL8187_RTL8225_ANAPARAM2_ON; } else { anaparam = RTL8187_RTL8225_ANAPARAM_OFF; anaparam2 = RTL8187_RTL8225_ANAPARAM2_OFF; } } else { if (rfon) { anaparam = RTL8187B_RTL8225_ANAPARAM_ON; anaparam2 = RTL8187B_RTL8225_ANAPARAM2_ON; anaparam3 = RTL8187B_RTL8225_ANAPARAM3_ON; } else { anaparam = RTL8187B_RTL8225_ANAPARAM_OFF; anaparam2 = RTL8187B_RTL8225_ANAPARAM2_OFF; anaparam3 = RTL8187B_RTL8225_ANAPARAM3_OFF; } } rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); reg |= RTL818X_CONFIG3_ANAPARAM_WRITE; rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); if (priv->is_rtl8187b) rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, anaparam3); reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE; rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } static int rtl8187_cmd_reset(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; u8 reg; int i; reg = rtl818x_ioread8(priv, &priv->map->CMD); reg &= (1 << 1); reg |= RTL818X_CMD_RESET; rtl818x_iowrite8(priv, &priv->map->CMD, reg); i = 10; do { msleep(2); if (!(rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET)) break; } while (--i); if (!i) { wiphy_err(dev->wiphy, "Reset timeout!\n"); return -ETIMEDOUT; } /* reload registers from eeprom */ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); i = 10; do { msleep(4); if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) & RTL818X_EEPROM_CMD_CONFIG)) break; } while (--i); if (!i) { wiphy_err(dev->wiphy, "eeprom reset timeout!\n"); return -ETIMEDOUT; } return 0; } static int rtl8187_init_hw(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; u8 reg; int res; /* reset */ rtl8187_set_anaparam(priv, true); rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); msleep(200); rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10); rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11); rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00); msleep(200); res = rtl8187_cmd_reset(dev); if (res) return res; rtl8187_set_anaparam(priv, true); /* setup card */ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF); reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); reg &= 0x3F; reg |= 0x80; rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); // TODO: set RESP_RATE and BRSR properly rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); /* host_usb_init */ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); reg = rtl818x_ioread8(priv, (u8 *)0xFE53); rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80); msleep(100); rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); msleep(100); priv->rf->init(dev); rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10); rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80); rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); return 0; } static const u8 rtl8187b_reg_table[][3] = { {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0}, {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0}, {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0}, {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0}, {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1}, {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1}, {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, {0xF2, 0x02, 1}, {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, {0xF6, 0x06, 1}, {0xF7, 0x07, 1}, {0xF8, 0x08, 1}, {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2}, {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2}, {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2}, {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2}, {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2}, {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2}, {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, {0x5B, 0x40, 0}, {0x84, 0x88, 0}, {0x85, 0x24, 0}, {0x88, 0x54, 0}, {0x8B, 0xB8, 0}, {0x8C, 0x07, 0}, {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, {0x95, 0x12, 0}, {0x96, 0x00, 0}, {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, {0x9F, 0x10, 0}, {0xB4, 0x22, 0}, {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, {0xEE, 0x00, 0}, {0x4C, 0x00, 2}, {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0}, {0x8E, 0x08, 0}, {0x8F, 0x00, 0} }; static int rtl8187b_init_hw(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; int res, i; u8 reg; rtl8187_set_anaparam(priv, true); /* Reset PLL sequence on 8187B. Realtek note: reduces power * consumption about 30 mA */ rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10); reg = rtl818x_ioread8(priv, (u8 *)0xFF62); rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5)); rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5)); res = rtl8187_cmd_reset(dev); if (res) return res; rtl8187_set_anaparam(priv, true); /* BRSR (Basic Rate Set Register) on 8187B looks to be the same as * RESP_RATE on 8187L in Realtek sources: each bit should be each * one of the 12 rates, all are enabled */ rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF); reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); rtl818x_iowrite8_idx(priv, (u8 *)0xFFE2, 0x00, 1); rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) { rtl818x_iowrite8_idx(priv, (u8 *)(uintptr_t) (rtl8187b_reg_table[i][0] | 0xFF00), rtl8187b_reg_table[i][1], rtl8187b_reg_table[i][2]); } rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50); rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0); rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1); rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1); rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1); rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001); /* RFSW_CTRL register */ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); msleep(100); priv->rf->init(dev); reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4); rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00); rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F); rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); reg = rtl818x_ioread8(priv, (u8 *)0xFFDB); rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2)); rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3); rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3); rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3); rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3); rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3); rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3); rtl818x_iowrite8(priv, (u8 *)0xFF61, 0); rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1); rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1); rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10); rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2); rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B); rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1); priv->slot_time = 0x9; priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); /* ENEDCA flag must always be set, transmit issues? */ rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA); return 0; } static void rtl8187_work(struct work_struct *work) { /* The RTL8187 returns the retry count through register 0xFFFA. In * addition, it appears to be a cumulative retry count, not the * value for the current TX packet. When multiple TX entries are * waiting in the queue, the retry count will be the total for all. * The "error" may matter for purposes of rate setting, but there is * no other choice with this hardware. */ struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, work.work); struct ieee80211_tx_info *info; struct ieee80211_hw *dev = priv->dev; static u16 retry; u16 tmp; u16 avg_retry; int length; mutex_lock(&priv->conf_mutex); tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); length = skb_queue_len(&priv->b_tx_status.queue); if (unlikely(!length)) length = 1; if (unlikely(tmp < retry)) tmp = retry; avg_retry = (tmp - retry) / length; while (skb_queue_len(&priv->b_tx_status.queue) > 0) { struct sk_buff *old_skb; old_skb = skb_dequeue(&priv->b_tx_status.queue); info = IEEE80211_SKB_CB(old_skb); info->status.rates[0].count = avg_retry + 1; if (info->status.rates[0].count > RETRY_COUNT) info->flags &= ~IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(dev, old_skb); } retry = tmp; mutex_unlock(&priv->conf_mutex); } static int rtl8187_start(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; u32 reg; int ret; mutex_lock(&priv->conf_mutex); ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : rtl8187b_init_hw(dev); if (ret) goto rtl8187_start_exit; init_usb_anchor(&priv->anchored); priv->dev = dev; if (priv->is_rtl8187b) { reg = RTL818X_RX_CONF_MGMT | RTL818X_RX_CONF_DATA | RTL818X_RX_CONF_BROADCAST | RTL818X_RX_CONF_NICMAC | RTL818X_RX_CONF_BSSID | (7 << 13 /* RX FIFO threshold NONE */) | (7 << 10 /* MAX RX DMA */) | RTL818X_RX_CONF_RX_AUTORESETPHY | RTL818X_RX_CONF_ONLYERLPKT | RTL818X_RX_CONF_MULTICAST; priv->rx_conf = reg; rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); rtl818x_iowrite32(priv, &priv->map->TX_CONF, RTL818X_TX_CONF_HW_SEQNUM | RTL818X_TX_CONF_DISREQQSIZE | (RETRY_COUNT << 8 /* short retry limit */) | (RETRY_COUNT << 0 /* long retry limit */) | (7 << 21 /* MAX TX DMA */)); rtl8187_init_urbs(dev); rtl8187b_init_status_urb(dev); goto rtl8187_start_exit; } rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); rtl8187_init_urbs(dev); reg = RTL818X_RX_CONF_ONLYERLPKT | RTL818X_RX_CONF_RX_AUTORESETPHY | RTL818X_RX_CONF_BSSID | RTL818X_RX_CONF_MGMT | RTL818X_RX_CONF_DATA | (7 << 13 /* RX FIFO threshold NONE */) | (7 << 10 /* MAX RX DMA */) | RTL818X_RX_CONF_BROADCAST | RTL818X_RX_CONF_NICMAC; priv->rx_conf = reg; rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); reg = RTL818X_TX_CONF_CW_MIN | (7 << 21 /* MAX TX DMA */) | RTL818X_TX_CONF_NO_ICV; rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->CMD); reg |= RTL818X_CMD_TX_ENABLE; reg |= RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); INIT_DELAYED_WORK(&priv->work, rtl8187_work); rtl8187_start_exit: mutex_unlock(&priv->conf_mutex); return ret; } static void rtl8187_stop(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; struct sk_buff *skb; u32 reg; mutex_lock(&priv->conf_mutex); rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); reg = rtl818x_ioread8(priv, &priv->map->CMD); reg &= ~RTL818X_CMD_TX_ENABLE; reg &= ~RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); priv->rf->stop(dev); rtl8187_set_anaparam(priv, false); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); while ((skb = skb_dequeue(&priv->b_tx_status.queue))) dev_kfree_skb_any(skb); usb_kill_anchored_urbs(&priv->anchored); mutex_unlock(&priv->conf_mutex); if (!priv->is_rtl8187b) cancel_delayed_work_sync(&priv->work); } static u64 rtl8187_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8187_priv *priv = dev->priv; return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; } static void rtl8187_beacon_work(struct work_struct *work) { struct rtl8187_vif *vif_priv = container_of(work, struct rtl8187_vif, beacon_work.work); struct ieee80211_vif *vif = container_of((void *)vif_priv, struct ieee80211_vif, drv_priv); struct ieee80211_hw *dev = vif_priv->dev; struct ieee80211_mgmt *mgmt; struct sk_buff *skb; /* don't overflow the tx ring */ if (ieee80211_queue_stopped(dev, 0)) goto resched; /* grab a fresh beacon */ skb = ieee80211_beacon_get(dev, vif); if (!skb) goto resched; /* * update beacon timestamp w/ TSF value * TODO: make hardware update beacon timestamp */ mgmt = (struct ieee80211_mgmt *)skb->data; mgmt->u.beacon.timestamp = cpu_to_le64(rtl8187_get_tsf(dev, vif)); /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); rtl8187_tx(dev, NULL, skb); resched: /* * schedule next beacon * TODO: use hardware support for beacon timing */ schedule_delayed_work(&vif_priv->beacon_work, usecs_to_jiffies(1024 * vif->bss_conf.beacon_int)); } static int rtl8187_add_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8187_priv *priv = dev->priv; struct rtl8187_vif *vif_priv; int i; int ret = -EOPNOTSUPP; mutex_lock(&priv->conf_mutex); if (priv->vif) goto exit; switch (vif->type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: break; default: goto exit; } ret = 0; priv->vif = vif; /* Initialize driver private area */ vif_priv = (struct rtl8187_vif *)&vif->drv_priv; vif_priv->dev = dev; INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8187_beacon_work); vif_priv->enable_beacon = false; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); for (i = 0; i < ETH_ALEN; i++) rtl818x_iowrite8(priv, &priv->map->MAC[i], ((u8 *)vif->addr)[i]); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); exit: mutex_unlock(&priv->conf_mutex); return ret; } static void rtl8187_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8187_priv *priv = dev->priv; mutex_lock(&priv->conf_mutex); priv->vif = NULL; mutex_unlock(&priv->conf_mutex); } static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) { struct rtl8187_priv *priv = dev->priv; struct ieee80211_conf *conf = &dev->conf; u32 reg; mutex_lock(&priv->conf_mutex); reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); /* Enable TX loopback on MAC level to avoid TX during channel * changes, as this has be seen to causes problems and the * card will stop work until next reset */ rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg | RTL818X_TX_CONF_LOOPBACK_MAC); priv->rf->set_chan(dev, conf); msleep(10); rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); mutex_unlock(&priv->conf_mutex); return 0; } /* * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for * example. Thus we have to use raw values for AC_*_PARAM register addresses. */ static __le32 *rtl8187b_ac_addr[4] = { (__le32 *) 0xFFF0, /* AC_VO */ (__le32 *) 0xFFF4, /* AC_VI */ (__le32 *) 0xFFFC, /* AC_BK */ (__le32 *) 0xFFF8, /* AC_BE */ }; #define SIFS_TIME 0xa static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, bool use_short_preamble) { if (priv->is_rtl8187b) { u8 difs, eifs; u16 ack_timeout; int queue; if (use_short_slot) { priv->slot_time = 0x9; difs = 0x1c; eifs = 0x53; } else { priv->slot_time = 0x14; difs = 0x32; eifs = 0x5b; } rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); rtl818x_iowrite8(priv, &priv->map->DIFS, difs); /* * BRSR+1 on 8187B is in fact EIFS register * Value in units of 4 us */ rtl818x_iowrite8(priv, (u8 *)&priv->map->BRSR + 1, eifs); /* * For 8187B, CARRIER_SENSE_COUNTER is in fact ack timeout * register. In units of 4 us like eifs register * ack_timeout = ack duration + plcp + difs + preamble */ ack_timeout = 112 + 48 + difs; if (use_short_preamble) ack_timeout += 72; else ack_timeout += 144; rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, DIV_ROUND_UP(ack_timeout, 4)); for (queue = 0; queue < 4; queue++) rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue], priv->aifsn[queue] * priv->slot_time + SIFS_TIME); } else { rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); if (use_short_slot) { rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); } else { rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); } } } static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct rtl8187_priv *priv = dev->priv; struct rtl8187_vif *vif_priv; int i; u8 reg; vif_priv = (struct rtl8187_vif *)&vif->drv_priv; if (changed & BSS_CHANGED_BSSID) { mutex_lock(&priv->conf_mutex); for (i = 0; i < ETH_ALEN; i++) rtl818x_iowrite8(priv, &priv->map->BSSID[i], info->bssid[i]); if (priv->is_rtl8187b) reg = RTL818X_MSR_ENEDCA; else reg = 0; if (is_valid_ether_addr(info->bssid)) { if (vif->type == NL80211_IFTYPE_ADHOC) reg |= RTL818X_MSR_ADHOC; else reg |= RTL818X_MSR_INFRA; } else reg |= RTL818X_MSR_NO_LINK; rtl818x_iowrite8(priv, &priv->map->MSR, reg); mutex_unlock(&priv->conf_mutex); } if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) rtl8187_conf_erp(priv, info->use_short_slot, info->use_short_preamble); if (changed & BSS_CHANGED_BEACON_ENABLED) vif_priv->enable_beacon = info->enable_beacon; if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) { cancel_delayed_work_sync(&vif_priv->beacon_work); if (vif_priv->enable_beacon) schedule_work(&vif_priv->beacon_work.work); } } static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *mc_list) #endif { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) return netdev_hw_addr_list_count(mc_list); #else return mc_count; #endif } static void rtl8187_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct rtl8187_priv *priv = dev->priv; if (changed_flags & FIF_FCSFAIL) priv->rx_conf ^= RTL818X_RX_CONF_FCS; if (changed_flags & FIF_CONTROL) priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; *total_flags = 0; if (priv->rx_conf & RTL818X_RX_CONF_FCS) *total_flags |= FIF_FCSFAIL; if (priv->rx_conf & RTL818X_RX_CONF_CTRL) *total_flags |= FIF_CONTROL; if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) *total_flags |= FIF_OTHER_BSS; if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) *total_flags |= FIF_ALLMULTI; rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); } static int rtl8187_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct rtl8187_priv *priv = dev->priv; u8 cw_min, cw_max; if (queue > 3) return -EINVAL; cw_min = fls(params->cw_min); cw_max = fls(params->cw_max); if (priv->is_rtl8187b) { priv->aifsn[queue] = params->aifs; /* * This is the structure of AC_*_PARAM registers in 8187B: * - TXOP limit field, bit offset = 16 * - ECWmax, bit offset = 12 * - ECWmin, bit offset = 8 * - AIFS, bit offset = 0 */ rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue], (params->txop << 16) | (cw_max << 12) | (cw_min << 8) | (params->aifs * priv->slot_time + SIFS_TIME)); } else { if (queue != 0) return -EINVAL; rtl818x_iowrite8(priv, &priv->map->CW_VAL, cw_min | (cw_max << 4)); } return 0; } static const struct ieee80211_ops rtl8187_ops = { .tx = rtl8187_tx, .start = rtl8187_start, .stop = rtl8187_stop, .add_interface = rtl8187_add_interface, .remove_interface = rtl8187_remove_interface, .config = rtl8187_config, .bss_info_changed = rtl8187_bss_info_changed, .prepare_multicast = rtl8187_prepare_multicast, .configure_filter = rtl8187_configure_filter, .conf_tx = rtl8187_conf_tx, .rfkill_poll = rtl8187_rfkill_poll, .get_tsf = rtl8187_get_tsf, }; static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) { struct ieee80211_hw *dev = eeprom->data; struct rtl8187_priv *priv = dev->priv; u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; } static void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom) { struct ieee80211_hw *dev = eeprom->data; struct rtl8187_priv *priv = dev->priv; u8 reg = RTL818X_EEPROM_CMD_PROGRAM; if (eeprom->reg_data_in) reg |= RTL818X_EEPROM_CMD_WRITE; if (eeprom->reg_data_out) reg |= RTL818X_EEPROM_CMD_READ; if (eeprom->reg_data_clock) reg |= RTL818X_EEPROM_CMD_CK; if (eeprom->reg_chip_select) reg |= RTL818X_EEPROM_CMD_CS; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); udelay(10); } static int __devinit rtl8187_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct ieee80211_hw *dev; struct rtl8187_priv *priv; struct eeprom_93cx6 eeprom; struct ieee80211_channel *channel; const char *chip_name; u16 txpwr, reg; u16 product_id = le16_to_cpu(udev->descriptor.idProduct); int err, i; u8 mac_addr[ETH_ALEN]; dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); if (!dev) { printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n"); return -ENOMEM; } priv = dev->priv; priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B); /* allocate "DMA aware" buffer for register accesses */ priv->io_dmabuf = kmalloc(sizeof(*priv->io_dmabuf), GFP_KERNEL); if (!priv->io_dmabuf) { err = -ENOMEM; goto err_free_dev; } mutex_init(&priv->io_mutex); SET_IEEE80211_DEV(dev, &intf->dev); usb_set_intfdata(intf, dev); priv->udev = udev; usb_get_dev(udev); skb_queue_head_init(&priv->rx_queue); BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); priv->map = (struct rtl818x_csr *)0xFF00; priv->band.band = IEEE80211_BAND_2GHZ; priv->band.channels = priv->channels; priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); priv->band.bitrates = priv->rates; priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_RX_INCLUDES_FCS; /* Initialize rate-control variables */ dev->max_rates = 1; dev->max_rate_tries = RETRY_COUNT; eeprom.data = dev; eeprom.register_read = rtl8187_eeprom_register_read; eeprom.register_write = rtl8187_eeprom_register_write; if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) eeprom.width = PCI_EEPROM_WIDTH_93C66; else eeprom.width = PCI_EEPROM_WIDTH_93C46; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); udelay(10); eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR, (__le16 __force *)mac_addr, 3); if (!is_valid_ether_addr(mac_addr)) { printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly " "generated MAC address\n"); eth_random_addr(mac_addr); } SET_IEEE80211_PERM_ADDR(dev, mac_addr); channel = priv->channels; for (i = 0; i < 3; i++) { eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i, &txpwr); (*channel++).hw_value = txpwr & 0xFF; (*channel++).hw_value = txpwr >> 8; } for (i = 0; i < 2; i++) { eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i, &txpwr); (*channel++).hw_value = txpwr & 0xFF; (*channel++).hw_value = txpwr >> 8; } eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE, &priv->txpwr_base); reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); /* 0 means asic B-cut, we should use SW 3 wire * bit-by-bit banging for radio. 1 means we can use * USB specific request to write radio registers */ priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3; rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); if (!priv->is_rtl8187b) { u32 reg32; reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF); reg32 &= RTL818X_TX_CONF_HWVER_MASK; switch (reg32) { case RTL818X_TX_CONF_R8187vD_B: /* Some RTL8187B devices have a USB ID of 0x8187 * detect them here */ chip_name = "RTL8187BvB(early)"; priv->is_rtl8187b = 1; priv->hw_rev = RTL8187BvB; break; case RTL818X_TX_CONF_R8187vD: chip_name = "RTL8187vD"; break; default: chip_name = "RTL8187vB (default)"; } } else { /* * Force USB request to write radio registers for 8187B, Realtek * only uses it in their sources */ /*if (priv->asic_rev == 0) { printk(KERN_WARNING "rtl8187: Forcing use of USB " "requests to write to radio registers\n"); priv->asic_rev = 1; }*/ switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) { case RTL818X_R8187B_B: chip_name = "RTL8187BvB"; priv->hw_rev = RTL8187BvB; break; case RTL818X_R8187B_D: chip_name = "RTL8187BvD"; priv->hw_rev = RTL8187BvD; break; case RTL818X_R8187B_E: chip_name = "RTL8187BvE"; priv->hw_rev = RTL8187BvE; break; default: chip_name = "RTL8187BvB (default)"; priv->hw_rev = RTL8187BvB; } } if (!priv->is_rtl8187b) { for (i = 0; i < 2; i++) { eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6 + i, &txpwr); (*channel++).hw_value = txpwr & 0xFF; (*channel++).hw_value = txpwr >> 8; } } else { eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6, &txpwr); (*channel++).hw_value = txpwr & 0xFF; eeprom_93cx6_read(&eeprom, 0x0A, &txpwr); (*channel++).hw_value = txpwr & 0xFF; eeprom_93cx6_read(&eeprom, 0x1C, &txpwr); (*channel++).hw_value = txpwr & 0xFF; (*channel++).hw_value = txpwr >> 8; } /* Handle the differing rfkill GPIO bit in different models */ priv->rfkill_mask = RFKILL_MASK_8187_89_97; if (product_id == 0x8197 || product_id == 0x8198) { eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, ®); if (reg & 0xFF00) priv->rfkill_mask = RFKILL_MASK_8198; } dev->vif_data_size = sizeof(struct rtl8187_vif); dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) ; if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) printk(KERN_INFO "rtl8187: inconsistency between id with OEM" " info!\n"); priv->rf = rtl8187_detect_rf(dev); dev->extra_tx_headroom = (!priv->is_rtl8187b) ? sizeof(struct rtl8187_tx_hdr) : sizeof(struct rtl8187b_tx_hdr); if (!priv->is_rtl8187b) dev->queues = 1; else dev->queues = 4; err = ieee80211_register_hw(dev); if (err) { printk(KERN_ERR "rtl8187: Cannot register device\n"); goto err_free_dmabuf; } mutex_init(&priv->conf_mutex); skb_queue_head_init(&priv->b_tx_status.queue); wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n", mac_addr, chip_name, priv->asic_rev, priv->rf->name, priv->rfkill_mask); #ifdef CONFIG_RTL8187_LEDS eeprom_93cx6_read(&eeprom, 0x3F, ®); reg &= 0xFF; rtl8187_leds_init(dev, reg); #endif rtl8187_rfkill_init(dev); return 0; err_free_dmabuf: kfree(priv->io_dmabuf); err_free_dev: ieee80211_free_hw(dev); usb_set_intfdata(intf, NULL); usb_put_dev(udev); return err; } static void __devexit rtl8187_disconnect(struct usb_interface *intf) { struct ieee80211_hw *dev = usb_get_intfdata(intf); struct rtl8187_priv *priv; if (!dev) return; #ifdef CONFIG_RTL8187_LEDS rtl8187_leds_exit(dev); #endif rtl8187_rfkill_exit(dev); ieee80211_unregister_hw(dev); priv = dev->priv; usb_reset_device(priv->udev); usb_put_dev(interface_to_usbdev(intf)); kfree(priv->io_dmabuf); ieee80211_free_hw(dev); } static struct usb_driver rtl8187_driver = { .name = KBUILD_MODNAME, .id_table = rtl8187_table, .probe = rtl8187_probe, .disconnect = __devexit_p(rtl8187_disconnect), #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(rtl8187_driver); compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/Makefile0000644000175000017500000000015712026211315024507 0ustar mcgrofmcgrofrtl8187-objs := dev.o rtl8225.o leds.o rfkill.o obj-$(CONFIG_RTL8187) += rtl8187.o ccflags-y += -I$(obj)/.. compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h0000644000175000017500000000255012026211315024341 0ustar mcgrofmcgrof/* * Radio tuning definitions for RTL8225 on RTL8187 * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8187 driver, which is: * Copyright 2005 Andrea Merello , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef RTL8187_RTL8225_H #define RTL8187_RTL8225_H #define RTL8187_RTL8225_ANAPARAM_ON 0xa0000a59 #define RTL8187_RTL8225_ANAPARAM2_ON 0x860c7312 #define RTL8187_RTL8225_ANAPARAM_OFF 0xa00beb59 #define RTL8187_RTL8225_ANAPARAM2_OFF 0x840dec11 #define RTL8187B_RTL8225_ANAPARAM_ON 0x45090658 #define RTL8187B_RTL8225_ANAPARAM2_ON 0x727f3f52 #define RTL8187B_RTL8225_ANAPARAM3_ON 0x00 #define RTL8187B_RTL8225_ANAPARAM_OFF 0x55480658 #define RTL8187B_RTL8225_ANAPARAM2_OFF 0x72003f50 #define RTL8187B_RTL8225_ANAPARAM3_OFF 0x00 const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *); static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, u8 addr, u32 data) { rtl8187_write_phy(dev, addr, data); } static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, u8 addr, u32 data) { rtl8187_write_phy(dev, addr, data | 0x10000); } #endif /* RTL8187_RTL8225_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c0000644000175000017500000007315212026211315024342 0ustar mcgrofmcgrof/* * Radio tuning for RTL8225 on RTL8187 * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8187 driver, which is: * Copyright 2005 Andrea Merello , et al. * * Magic delays, register offsets, and phy value tables below are * taken from the original r8187 driver sources. Thanks to Realtek * for their support! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "rtl8187.h" #include "rtl8225.h" static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data) { struct rtl8187_priv *priv = dev->priv; u16 reg80, reg84, reg82; u32 bangdata; int i; bangdata = (data << 4) | (addr & 0xf); reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7); udelay(10); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); udelay(10); for (i = 15; i >= 0; i--) { u16 reg = reg80 | (bangdata & (1 << i)) >> i; if (i & 1) rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); if (!(i & 1)) rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); } rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); udelay(10); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); } static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data) { struct rtl8187_priv *priv = dev->priv; u16 reg80, reg82, reg84; reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); reg80 &= ~(0x3 << 2); reg84 &= ~0xF; rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007); udelay(10); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); udelay(10); mutex_lock(&priv->io_mutex); priv->io_dmabuf->bits16 = data; usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, addr, 0x8225, &priv->io_dmabuf->bits16, sizeof(data), HZ / 2); mutex_unlock(&priv->io_mutex); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); udelay(10); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); } static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) { struct rtl8187_priv *priv = dev->priv; if (priv->asic_rev) rtl8225_write_8051(dev, addr, cpu_to_le16(data)); else rtl8225_write_bitbang(dev, addr, data); } static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) { struct rtl8187_priv *priv = dev->priv; u16 reg80, reg82, reg84, out; int i; reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); reg80 &= ~0xF; rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); udelay(4); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); udelay(5); for (i = 4; i >= 0; i--) { u16 reg = reg80 | ((addr >> i) & 1); if (!(i & 1)) { rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); udelay(1); } rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); udelay(2); if (i & 1) { rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); udelay(1); } } rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); udelay(2); out = 0; for (i = 11; i >= 0; i--) { rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); udelay(1); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); udelay(2); if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) out |= 1 << i; rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); udelay(2); } rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 2)); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); return out; } static const u16 rtl8225bcd_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb }; static const u8 rtl8225_agc[] = { 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static const u8 rtl8225_gain[] = { 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */ 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */ 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */ 0x33, 0x80, 0x79, 0xc5, /* -78dBm */ 0x43, 0x78, 0x76, 0xc5, /* -74dBm */ 0x53, 0x60, 0x73, 0xc5, /* -70dBm */ 0x63, 0x58, 0x70, 0xc5, /* -66dBm */ }; static const u8 rtl8225_threshold[] = { 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd }; static const u8 rtl8225_tx_gain_cck_ofdm[] = { 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e }; static const u8 rtl8225_tx_power_cck[] = { 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 }; static const u8 rtl8225_tx_power_cck_ch14[] = { 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 }; static const u8 rtl8225_tx_power_ofdm[] = { 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 }; static const u32 rtl8225_chan[] = { 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 }; static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) { struct rtl8187_priv *priv = dev->priv; u8 cck_power, ofdm_power; const u8 *tmp; u32 reg; int i; cck_power = priv->channels[channel - 1].hw_value & 0xF; ofdm_power = priv->channels[channel - 1].hw_value >> 4; cck_power = min(cck_power, (u8)11); if (ofdm_power > (u8)15) ofdm_power = 25; else ofdm_power += 10; rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); if (channel == 14) tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; else tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; for (i = 0; i < 8; i++) rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); msleep(1); // FIXME: optional? /* anaparam2 on */ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8187_RTL8225_ANAPARAM2_ON); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl8225_write_phy_ofdm(dev, 2, 0x42); rtl8225_write_phy_ofdm(dev, 6, 0x00); rtl8225_write_phy_ofdm(dev, 8, 0x00); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1); tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; rtl8225_write_phy_ofdm(dev, 5, *tmp); rtl8225_write_phy_ofdm(dev, 7, *tmp); msleep(1); } static void rtl8225_rf_init(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; int i; rtl8225_write(dev, 0x0, 0x067); rtl8225_write(dev, 0x1, 0xFE0); rtl8225_write(dev, 0x2, 0x44D); rtl8225_write(dev, 0x3, 0x441); rtl8225_write(dev, 0x4, 0x486); rtl8225_write(dev, 0x5, 0xBC0); rtl8225_write(dev, 0x6, 0xAE6); rtl8225_write(dev, 0x7, 0x82A); rtl8225_write(dev, 0x8, 0x01F); rtl8225_write(dev, 0x9, 0x334); rtl8225_write(dev, 0xA, 0xFD4); rtl8225_write(dev, 0xB, 0x391); rtl8225_write(dev, 0xC, 0x050); rtl8225_write(dev, 0xD, 0x6DB); rtl8225_write(dev, 0xE, 0x029); rtl8225_write(dev, 0xF, 0x914); msleep(100); rtl8225_write(dev, 0x2, 0xC4D); msleep(200); rtl8225_write(dev, 0x2, 0x44D); msleep(200); if (!(rtl8225_read(dev, 6) & (1 << 7))) { rtl8225_write(dev, 0x02, 0x0c4d); msleep(200); rtl8225_write(dev, 0x02, 0x044d); msleep(100); if (!(rtl8225_read(dev, 6) & (1 << 7))) wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", rtl8225_read(dev, 6)); } rtl8225_write(dev, 0x0, 0x127); for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { rtl8225_write(dev, 0x1, i + 1); rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); } rtl8225_write(dev, 0x0, 0x027); rtl8225_write(dev, 0x0, 0x22F); for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); } msleep(1); rtl8225_write_phy_ofdm(dev, 0x00, 0x01); rtl8225_write_phy_ofdm(dev, 0x01, 0x02); rtl8225_write_phy_ofdm(dev, 0x02, 0x42); rtl8225_write_phy_ofdm(dev, 0x03, 0x00); rtl8225_write_phy_ofdm(dev, 0x04, 0x00); rtl8225_write_phy_ofdm(dev, 0x05, 0x00); rtl8225_write_phy_ofdm(dev, 0x06, 0x40); rtl8225_write_phy_ofdm(dev, 0x07, 0x00); rtl8225_write_phy_ofdm(dev, 0x08, 0x40); rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); rtl8225_write_phy_ofdm(dev, 0x10, 0x84); rtl8225_write_phy_ofdm(dev, 0x11, 0x06); rtl8225_write_phy_ofdm(dev, 0x12, 0x20); rtl8225_write_phy_ofdm(dev, 0x13, 0x20); rtl8225_write_phy_ofdm(dev, 0x14, 0x00); rtl8225_write_phy_ofdm(dev, 0x15, 0x40); rtl8225_write_phy_ofdm(dev, 0x16, 0x00); rtl8225_write_phy_ofdm(dev, 0x17, 0x40); rtl8225_write_phy_ofdm(dev, 0x18, 0xef); rtl8225_write_phy_ofdm(dev, 0x19, 0x19); rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); rtl8225_write_phy_ofdm(dev, 0x21, 0x27); rtl8225_write_phy_ofdm(dev, 0x22, 0x16); rtl8225_write_phy_ofdm(dev, 0x24, 0x46); rtl8225_write_phy_ofdm(dev, 0x25, 0x20); rtl8225_write_phy_ofdm(dev, 0x26, 0x90); rtl8225_write_phy_ofdm(dev, 0x27, 0x88); rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); rtl8225_write_phy_cck(dev, 0x00, 0x98); rtl8225_write_phy_cck(dev, 0x03, 0x20); rtl8225_write_phy_cck(dev, 0x04, 0x7e); rtl8225_write_phy_cck(dev, 0x05, 0x12); rtl8225_write_phy_cck(dev, 0x06, 0xfc); rtl8225_write_phy_cck(dev, 0x07, 0x78); rtl8225_write_phy_cck(dev, 0x08, 0x2e); rtl8225_write_phy_cck(dev, 0x10, 0x9b); rtl8225_write_phy_cck(dev, 0x11, 0x88); rtl8225_write_phy_cck(dev, 0x12, 0x47); rtl8225_write_phy_cck(dev, 0x13, 0xd0); rtl8225_write_phy_cck(dev, 0x19, 0x00); rtl8225_write_phy_cck(dev, 0x1a, 0xa0); rtl8225_write_phy_cck(dev, 0x1b, 0x08); rtl8225_write_phy_cck(dev, 0x40, 0x86); rtl8225_write_phy_cck(dev, 0x41, 0x8d); rtl8225_write_phy_cck(dev, 0x42, 0x15); rtl8225_write_phy_cck(dev, 0x43, 0x18); rtl8225_write_phy_cck(dev, 0x44, 0x1f); rtl8225_write_phy_cck(dev, 0x45, 0x1e); rtl8225_write_phy_cck(dev, 0x46, 0x1a); rtl8225_write_phy_cck(dev, 0x47, 0x15); rtl8225_write_phy_cck(dev, 0x48, 0x10); rtl8225_write_phy_cck(dev, 0x49, 0x0a); rtl8225_write_phy_cck(dev, 0x4a, 0x05); rtl8225_write_phy_cck(dev, 0x4b, 0x02); rtl8225_write_phy_cck(dev, 0x4c, 0x05); rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); rtl8225_rf_set_tx_power(dev, 1); /* RX antenna default to A */ rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ msleep(1); rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); /* set sensitivity */ rtl8225_write(dev, 0x0c, 0x50); rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]); } static const u8 rtl8225z2_agc[] = { 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 }; static const u8 rtl8225z2_ofdm[] = { 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26, 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3, 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f, 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00, 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e, 0x6d, 0x3c, 0xfb, 0x07 }; static const u8 rtl8225z2_tx_power_cck_ch14[] = { 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 }; static const u8 rtl8225z2_tx_power_cck[] = { 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 }; static const u8 rtl8225z2_tx_power_ofdm[] = { 0x42, 0x00, 0x40, 0x00, 0x40 }; static const u8 rtl8225z2_tx_gain_cck_ofdm[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 }; static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) { struct rtl8187_priv *priv = dev->priv; u8 cck_power, ofdm_power; const u8 *tmp; u32 reg; int i; cck_power = priv->channels[channel - 1].hw_value & 0xF; ofdm_power = priv->channels[channel - 1].hw_value >> 4; cck_power = min(cck_power, (u8)15); cck_power += priv->txpwr_base & 0xF; cck_power = min(cck_power, (u8)35); if (ofdm_power > (u8)15) ofdm_power = 25; else ofdm_power += 10; ofdm_power += priv->txpwr_base >> 4; ofdm_power = min(ofdm_power, (u8)35); if (channel == 14) tmp = rtl8225z2_tx_power_cck_ch14; else tmp = rtl8225z2_tx_power_cck; for (i = 0; i < 8; i++) rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, rtl8225z2_tx_gain_cck_ofdm[cck_power]); msleep(1); /* anaparam2 on */ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8187_RTL8225_ANAPARAM2_ON); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl8225_write_phy_ofdm(dev, 2, 0x42); rtl8225_write_phy_ofdm(dev, 5, 0x00); rtl8225_write_phy_ofdm(dev, 6, 0x40); rtl8225_write_phy_ofdm(dev, 7, 0x00); rtl8225_write_phy_ofdm(dev, 8, 0x40); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, rtl8225z2_tx_gain_cck_ofdm[ofdm_power]); msleep(1); } static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel) { struct rtl8187_priv *priv = dev->priv; u8 cck_power, ofdm_power; const u8 *tmp; int i; cck_power = priv->channels[channel - 1].hw_value & 0xF; ofdm_power = priv->channels[channel - 1].hw_value >> 4; if (cck_power > 15) cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22; else cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7; cck_power += priv->txpwr_base & 0xF; cck_power = min(cck_power, (u8)35); if (ofdm_power > 15) ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25; else ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10; ofdm_power += (priv->txpwr_base >> 4) & 0xF; ofdm_power = min(ofdm_power, (u8)35); if (channel == 14) tmp = rtl8225z2_tx_power_cck_ch14; else tmp = rtl8225z2_tx_power_cck; if (priv->hw_rev == RTL8187BvB) { if (cck_power <= 6) ; /* do nothing */ else if (cck_power <= 11) tmp += 8; else tmp += 16; } else { if (cck_power <= 5) ; /* do nothing */ else if (cck_power <= 11) tmp += 8; else if (cck_power <= 17) tmp += 16; else tmp += 24; } for (i = 0; i < 8; i++) rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, rtl8225z2_tx_gain_cck_ofdm[cck_power] << 1); msleep(1); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1); if (priv->hw_rev == RTL8187BvB) { if (ofdm_power <= 11) { rtl8225_write_phy_ofdm(dev, 0x87, 0x60); rtl8225_write_phy_ofdm(dev, 0x89, 0x60); } else { rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); } } else { if (ofdm_power <= 11) { rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); } else if (ofdm_power <= 17) { rtl8225_write_phy_ofdm(dev, 0x87, 0x54); rtl8225_write_phy_ofdm(dev, 0x89, 0x54); } else { rtl8225_write_phy_ofdm(dev, 0x87, 0x50); rtl8225_write_phy_ofdm(dev, 0x89, 0x50); } } msleep(1); } static const u16 rtl8225z2_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb }; static const u8 rtl8225z2_gain_bg[] = { 0x23, 0x15, 0xa5, /* -82-1dBm */ 0x23, 0x15, 0xb5, /* -82-2dBm */ 0x23, 0x15, 0xc5, /* -82-3dBm */ 0x33, 0x15, 0xc5, /* -78dBm */ 0x43, 0x15, 0xc5, /* -74dBm */ 0x53, 0x15, 0xc5, /* -70dBm */ 0x63, 0x15, 0xc5 /* -66dBm */ }; static void rtl8225z2_rf_init(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; int i; rtl8225_write(dev, 0x0, 0x2BF); rtl8225_write(dev, 0x1, 0xEE0); rtl8225_write(dev, 0x2, 0x44D); rtl8225_write(dev, 0x3, 0x441); rtl8225_write(dev, 0x4, 0x8C3); rtl8225_write(dev, 0x5, 0xC72); rtl8225_write(dev, 0x6, 0x0E6); rtl8225_write(dev, 0x7, 0x82A); rtl8225_write(dev, 0x8, 0x03F); rtl8225_write(dev, 0x9, 0x335); rtl8225_write(dev, 0xa, 0x9D4); rtl8225_write(dev, 0xb, 0x7BB); rtl8225_write(dev, 0xc, 0x850); rtl8225_write(dev, 0xd, 0xCDF); rtl8225_write(dev, 0xe, 0x02B); rtl8225_write(dev, 0xf, 0x114); msleep(100); rtl8225_write(dev, 0x0, 0x1B7); for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { rtl8225_write(dev, 0x1, i + 1); rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); } rtl8225_write(dev, 0x3, 0x080); rtl8225_write(dev, 0x5, 0x004); rtl8225_write(dev, 0x0, 0x0B7); rtl8225_write(dev, 0x2, 0xc4D); msleep(200); rtl8225_write(dev, 0x2, 0x44D); msleep(100); if (!(rtl8225_read(dev, 6) & (1 << 7))) { rtl8225_write(dev, 0x02, 0x0C4D); msleep(200); rtl8225_write(dev, 0x02, 0x044D); msleep(100); if (!(rtl8225_read(dev, 6) & (1 << 7))) wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", rtl8225_read(dev, 6)); } msleep(200); rtl8225_write(dev, 0x0, 0x2BF); for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); } msleep(1); rtl8225_write_phy_ofdm(dev, 0x00, 0x01); rtl8225_write_phy_ofdm(dev, 0x01, 0x02); rtl8225_write_phy_ofdm(dev, 0x02, 0x42); rtl8225_write_phy_ofdm(dev, 0x03, 0x00); rtl8225_write_phy_ofdm(dev, 0x04, 0x00); rtl8225_write_phy_ofdm(dev, 0x05, 0x00); rtl8225_write_phy_ofdm(dev, 0x06, 0x40); rtl8225_write_phy_ofdm(dev, 0x07, 0x00); rtl8225_write_phy_ofdm(dev, 0x08, 0x40); rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); rtl8225_write_phy_ofdm(dev, 0x0a, 0x08); rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); rtl8225_write_phy_ofdm(dev, 0x10, 0x84); rtl8225_write_phy_ofdm(dev, 0x11, 0x07); rtl8225_write_phy_ofdm(dev, 0x12, 0x20); rtl8225_write_phy_ofdm(dev, 0x13, 0x20); rtl8225_write_phy_ofdm(dev, 0x14, 0x00); rtl8225_write_phy_ofdm(dev, 0x15, 0x40); rtl8225_write_phy_ofdm(dev, 0x16, 0x00); rtl8225_write_phy_ofdm(dev, 0x17, 0x40); rtl8225_write_phy_ofdm(dev, 0x18, 0xef); rtl8225_write_phy_ofdm(dev, 0x19, 0x19); rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); rtl8225_write_phy_ofdm(dev, 0x1b, 0x15); rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); rtl8225_write_phy_ofdm(dev, 0x21, 0x17); rtl8225_write_phy_ofdm(dev, 0x22, 0x16); rtl8225_write_phy_ofdm(dev, 0x23, 0x80); rtl8225_write_phy_ofdm(dev, 0x24, 0x46); rtl8225_write_phy_ofdm(dev, 0x25, 0x00); rtl8225_write_phy_ofdm(dev, 0x26, 0x90); rtl8225_write_phy_ofdm(dev, 0x27, 0x88); rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]); rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]); rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]); rtl8225_write_phy_ofdm(dev, 0x21, 0x37); rtl8225_write_phy_cck(dev, 0x00, 0x98); rtl8225_write_phy_cck(dev, 0x03, 0x20); rtl8225_write_phy_cck(dev, 0x04, 0x7e); rtl8225_write_phy_cck(dev, 0x05, 0x12); rtl8225_write_phy_cck(dev, 0x06, 0xfc); rtl8225_write_phy_cck(dev, 0x07, 0x78); rtl8225_write_phy_cck(dev, 0x08, 0x2e); rtl8225_write_phy_cck(dev, 0x10, 0x9b); rtl8225_write_phy_cck(dev, 0x11, 0x88); rtl8225_write_phy_cck(dev, 0x12, 0x47); rtl8225_write_phy_cck(dev, 0x13, 0xd0); rtl8225_write_phy_cck(dev, 0x19, 0x00); rtl8225_write_phy_cck(dev, 0x1a, 0xa0); rtl8225_write_phy_cck(dev, 0x1b, 0x08); rtl8225_write_phy_cck(dev, 0x40, 0x86); rtl8225_write_phy_cck(dev, 0x41, 0x8d); rtl8225_write_phy_cck(dev, 0x42, 0x15); rtl8225_write_phy_cck(dev, 0x43, 0x18); rtl8225_write_phy_cck(dev, 0x44, 0x36); rtl8225_write_phy_cck(dev, 0x45, 0x35); rtl8225_write_phy_cck(dev, 0x46, 0x2e); rtl8225_write_phy_cck(dev, 0x47, 0x25); rtl8225_write_phy_cck(dev, 0x48, 0x1c); rtl8225_write_phy_cck(dev, 0x49, 0x12); rtl8225_write_phy_cck(dev, 0x4a, 0x09); rtl8225_write_phy_cck(dev, 0x4b, 0x04); rtl8225_write_phy_cck(dev, 0x4c, 0x05); rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1); rtl8225z2_rf_set_tx_power(dev, 1); /* RX antenna default to A */ rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ msleep(1); rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); } static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; int i; rtl8225_write(dev, 0x0, 0x0B7); rtl8225_write(dev, 0x1, 0xEE0); rtl8225_write(dev, 0x2, 0x44D); rtl8225_write(dev, 0x3, 0x441); rtl8225_write(dev, 0x4, 0x8C3); rtl8225_write(dev, 0x5, 0xC72); rtl8225_write(dev, 0x6, 0x0E6); rtl8225_write(dev, 0x7, 0x82A); rtl8225_write(dev, 0x8, 0x03F); rtl8225_write(dev, 0x9, 0x335); rtl8225_write(dev, 0xa, 0x9D4); rtl8225_write(dev, 0xb, 0x7BB); rtl8225_write(dev, 0xc, 0x850); rtl8225_write(dev, 0xd, 0xCDF); rtl8225_write(dev, 0xe, 0x02B); rtl8225_write(dev, 0xf, 0x114); rtl8225_write(dev, 0x0, 0x1B7); for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { rtl8225_write(dev, 0x1, i + 1); rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); } rtl8225_write(dev, 0x3, 0x080); rtl8225_write(dev, 0x5, 0x004); rtl8225_write(dev, 0x0, 0x0B7); rtl8225_write(dev, 0x2, 0xC4D); rtl8225_write(dev, 0x2, 0x44D); rtl8225_write(dev, 0x0, 0x2BF); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07); rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); rtl8225_write_phy_ofdm(dev, 0x80, 0x12); for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) { rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]); rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i); rtl8225_write_phy_ofdm(dev, 0xE, 0); } rtl8225_write_phy_ofdm(dev, 0x80, 0x10); for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++) rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]); rtl8225_write_phy_ofdm(dev, 0x97, 0x46); rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); rtl8225_write_phy_cck(dev, 0xc1, 0x88); } static void rtl8225_rf_stop(struct ieee80211_hw *dev) { rtl8225_write(dev, 0x4, 0x1f); } static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, struct ieee80211_conf *conf) { struct rtl8187_priv *priv = dev->priv; int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); if (priv->rf->init == rtl8225_rf_init) rtl8225_rf_set_tx_power(dev, chan); else if (priv->rf->init == rtl8225z2_rf_init) rtl8225z2_rf_set_tx_power(dev, chan); else rtl8225z2_b_rf_set_tx_power(dev, chan); rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); msleep(10); } static const struct rtl818x_rf_ops rtl8225_ops = { .name = "rtl8225", .init = rtl8225_rf_init, .stop = rtl8225_rf_stop, .set_chan = rtl8225_rf_set_channel }; static const struct rtl818x_rf_ops rtl8225z2_ops = { .name = "rtl8225z2", .init = rtl8225z2_rf_init, .stop = rtl8225_rf_stop, .set_chan = rtl8225_rf_set_channel }; static const struct rtl818x_rf_ops rtl8225z2_b_ops = { .name = "rtl8225z2", .init = rtl8225z2_b_rf_init, .stop = rtl8225_rf_stop, .set_chan = rtl8225_rf_set_channel }; const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev) { u16 reg8, reg9; struct rtl8187_priv *priv = dev->priv; if (!priv->is_rtl8187b) { rtl8225_write(dev, 0, 0x1B7); reg8 = rtl8225_read(dev, 8); reg9 = rtl8225_read(dev, 9); rtl8225_write(dev, 0, 0x0B7); if (reg8 != 0x588 || reg9 != 0x700) return &rtl8225_ops; return &rtl8225z2_ops; } else return &rtl8225z2_b_ops; } compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h0000644000175000017500000001452312026211315024353 0ustar mcgrofmcgrof/* * Definitions for RTL8187 hardware * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8187 driver, which is: * Copyright 2005 Andrea Merello , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef RTL8187_H #define RTL8187_H #include "rtl818x.h" #include "leds.h" #define RTL8187_EEPROM_TXPWR_BASE 0x05 #define RTL8187_EEPROM_MAC_ADDR 0x07 #define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */ #define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */ #define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */ #define RTL8187_EEPROM_SELECT_GPIO 0x3B #define RTL8187_REQT_READ 0xC0 #define RTL8187_REQT_WRITE 0x40 #define RTL8187_REQ_GET_REG 0x05 #define RTL8187_REQ_SET_REG 0x05 #define RTL8187_MAX_RX 0x9C4 #define RFKILL_MASK_8187_89_97 0x2 #define RFKILL_MASK_8198 0x4 #define RETRY_COUNT 7 struct rtl8187_rx_info { struct urb *urb; struct ieee80211_hw *dev; }; struct rtl8187_rx_hdr { __le32 flags; u8 noise; u8 signal; u8 agc; u8 reserved; __le64 mac_time; } __packed; struct rtl8187b_rx_hdr { __le32 flags; __le64 mac_time; u8 sq; u8 rssi; u8 agc; u8 flags2; __le16 snr_long2end; s8 pwdb_g12; u8 fot; } __packed; /* {rtl8187,rtl8187b}_tx_info is in skb */ struct rtl8187_tx_hdr { __le32 flags; __le16 rts_duration; __le16 len; __le32 retry; } __packed; struct rtl8187b_tx_hdr { __le32 flags; __le16 rts_duration; __le16 len; __le32 unused_1; __le16 unused_2; __le16 tx_duration; __le32 unused_3; __le32 retry; __le32 unused_4[2]; } __packed; enum { DEVICE_RTL8187, DEVICE_RTL8187B }; struct rtl8187_vif { struct ieee80211_hw *dev; /* beaconing */ struct delayed_work beacon_work; bool enable_beacon; }; struct rtl8187_priv { /* common between rtl818x drivers */ struct rtl818x_csr *map; const struct rtl818x_rf_ops *rf; struct ieee80211_vif *vif; /* The mutex protects the TX loopback state. * Any attempt to set channels concurrently locks the device. */ struct mutex conf_mutex; /* rtl8187 specific */ struct ieee80211_channel channels[14]; struct ieee80211_rate rates[12]; struct ieee80211_supported_band band; struct usb_device *udev; u32 rx_conf; struct usb_anchor anchored; struct delayed_work work; struct ieee80211_hw *dev; #ifdef CONFIG_RTL8187_LEDS struct rtl8187_led led_radio; struct rtl8187_led led_tx; struct rtl8187_led led_rx; struct delayed_work led_on; struct delayed_work led_off; #endif u16 txpwr_base; u8 asic_rev; u8 is_rtl8187b; enum { RTL8187BvB, RTL8187BvD, RTL8187BvE } hw_rev; struct sk_buff_head rx_queue; u8 signal; u8 noise; u8 slot_time; u8 aifsn[4]; u8 rfkill_mask; struct { __le64 buf; struct sk_buff_head queue; } b_tx_status; /* This queue is used by both -b and non-b devices */ struct mutex io_mutex; union { u8 bits8; __le16 bits16; __le32 bits32; } *io_dmabuf; bool rfkill_off; u16 seqno; }; void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv, u8 *addr, u8 idx) { u8 val; mutex_lock(&priv->io_mutex); usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), RTL8187_REQ_GET_REG, RTL8187_REQT_READ, (unsigned long)addr, idx & 0x03, &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); val = priv->io_dmabuf->bits8; mutex_unlock(&priv->io_mutex); return val; } static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr) { return rtl818x_ioread8_idx(priv, addr, 0); } static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv, __le16 *addr, u8 idx) { __le16 val; mutex_lock(&priv->io_mutex); usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), RTL8187_REQ_GET_REG, RTL8187_REQT_READ, (unsigned long)addr, idx & 0x03, &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); val = priv->io_dmabuf->bits16; mutex_unlock(&priv->io_mutex); return le16_to_cpu(val); } static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr) { return rtl818x_ioread16_idx(priv, addr, 0); } static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv, __le32 *addr, u8 idx) { __le32 val; mutex_lock(&priv->io_mutex); usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), RTL8187_REQ_GET_REG, RTL8187_REQT_READ, (unsigned long)addr, idx & 0x03, &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); val = priv->io_dmabuf->bits32; mutex_unlock(&priv->io_mutex); return le32_to_cpu(val); } static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr) { return rtl818x_ioread32_idx(priv, addr, 0); } static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv, u8 *addr, u8 val, u8 idx) { mutex_lock(&priv->io_mutex); priv->io_dmabuf->bits8 = val; usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, (unsigned long)addr, idx & 0x03, &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); mutex_unlock(&priv->io_mutex); } static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val) { rtl818x_iowrite8_idx(priv, addr, val, 0); } static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv, __le16 *addr, u16 val, u8 idx) { mutex_lock(&priv->io_mutex); priv->io_dmabuf->bits16 = cpu_to_le16(val); usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, (unsigned long)addr, idx & 0x03, &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); mutex_unlock(&priv->io_mutex); } static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr, u16 val) { rtl818x_iowrite16_idx(priv, addr, val, 0); } static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv, __le32 *addr, u32 val, u8 idx) { mutex_lock(&priv->io_mutex); priv->io_dmabuf->bits32 = cpu_to_le32(val); usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, (unsigned long)addr, idx & 0x03, &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); mutex_unlock(&priv->io_mutex); } static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr, u32 val) { rtl818x_iowrite32_idx(priv, addr, val, 0); } #endif /* RTL8187_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/rfkill.h0000644000175000017500000000035312026211315024501 0ustar mcgrofmcgrof#ifndef RTL8187_RFKILL_H #define RTL8187_RFKILL_H void rtl8187_rfkill_init(struct ieee80211_hw *hw); void rtl8187_rfkill_poll(struct ieee80211_hw *hw); void rtl8187_rfkill_exit(struct ieee80211_hw *hw); #endif /* RTL8187_RFKILL_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/rfkill.c0000644000175000017500000000335112026211315024475 0ustar mcgrofmcgrof/* * Linux RFKILL support for RTL8187 * * Copyright (c) 2009 Herton Ronaldo Krzesinski * * Based on the RFKILL handling in the r8187 driver, which is: * Copyright (c) Realtek Semiconductor Corp. All rights reserved. * * Thanks to Realtek for their support! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "rtl8187.h" #include "rfkill.h" static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) { u8 gpio; gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~priv->rfkill_mask); gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); return gpio & priv->rfkill_mask; } void rtl8187_rfkill_init(struct ieee80211_hw *hw) { struct rtl8187_priv *priv = hw->priv; priv->rfkill_off = rtl8187_is_radio_enabled(priv); printk(KERN_INFO "rtl8187: wireless switch is %s\n", priv->rfkill_off ? "on" : "off"); wiphy_rfkill_set_hw_state(hw->wiphy, !priv->rfkill_off); wiphy_rfkill_start_polling(hw->wiphy); } void rtl8187_rfkill_poll(struct ieee80211_hw *hw) { bool enabled; struct rtl8187_priv *priv = hw->priv; mutex_lock(&priv->conf_mutex); enabled = rtl8187_is_radio_enabled(priv); if (unlikely(enabled != priv->rfkill_off)) { priv->rfkill_off = enabled; printk(KERN_INFO "rtl8187: wireless radio switch turned %s\n", enabled ? "on" : "off"); wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); } mutex_unlock(&priv->conf_mutex); } void rtl8187_rfkill_exit(struct ieee80211_hw *hw) { wiphy_rfkill_stop_polling(hw->wiphy); } compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/leds.h0000644000175000017500000000251712026211315024151 0ustar mcgrofmcgrof/* * Definitions for RTL8187 leds * * Copyright 2009 Larry Finger * * Based on the LED handling in the r8187 driver, which is: * Copyright (c) Realtek Semiconductor Corp. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef RTL8187_LED_H #define RTL8187_LED_H #ifdef CONFIG_RTL8187_LEDS #define RTL8187_LED_MAX_NAME_LEN 21 #include #include enum { LED_PIN_LED0, LED_PIN_LED1, LED_PIN_GPIO0, LED_PIN_HW }; enum { EEPROM_CID_RSVD0 = 0x00, EEPROM_CID_RSVD1 = 0xFF, EEPROM_CID_ALPHA0 = 0x01, EEPROM_CID_SERCOMM_PS = 0x02, EEPROM_CID_HW = 0x03, EEPROM_CID_TOSHIBA = 0x04, EEPROM_CID_QMI = 0x07, EEPROM_CID_DELL = 0x08 }; struct rtl8187_led { struct ieee80211_hw *dev; /* The LED class device */ struct led_classdev led_dev; /* The pin/method used to control the led */ u8 ledpin; /* The unique name string for this LED device. */ char name[RTL8187_LED_MAX_NAME_LEN + 1]; /* If the LED is radio or tx/rx */ bool is_radio; }; void rtl8187_leds_init(struct ieee80211_hw *dev, u16 code); void rtl8187_leds_exit(struct ieee80211_hw *dev); #endif /* def CONFIG_RTL8187_LEDS */ #endif /* RTL8187_LED_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8187/leds.c0000644000175000017500000001465012026211315024145 0ustar mcgrofmcgrof/* * Linux LED driver for RTL8187 * * Copyright 2009 Larry Finger * * Based on the LED handling in the r8187 driver, which is: * Copyright (c) Realtek Semiconductor Corp. All rights reserved. * * Thanks to Realtek for their support! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifdef CONFIG_RTL8187_LEDS #include #include #include #include "rtl8187.h" #include "leds.h" static void led_turn_on(struct work_struct *work) { /* As this routine does read/write operations on the hardware, it must * be run from a work queue. */ u8 reg; struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, led_on.work); struct rtl8187_led *led = &priv->led_tx; /* Don't change the LED, when the device is down. */ if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) return ; /* Skip if the LED is not registered. */ if (!led->dev) return; mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); break; case LED_PIN_LED0: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_LED1: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_HW: default: break; } mutex_unlock(&priv->conf_mutex); } static void led_turn_off(struct work_struct *work) { /* As this routine does read/write operations on the hardware, it must * be run from a work queue. */ u8 reg; struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, led_off.work); struct rtl8187_led *led = &priv->led_tx; /* Don't change the LED, when the device is down. */ if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) return ; /* Skip if the LED is not registered. */ if (!led->dev) return; mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); break; case LED_PIN_LED0: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_LED1: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_HW: default: break; } mutex_unlock(&priv->conf_mutex); } /* Callback from the LED subsystem. */ static void rtl8187_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, led_dev); struct ieee80211_hw *hw = led->dev; struct rtl8187_priv *priv; static bool radio_on; if (!hw) return; priv = hw->priv; if (led->is_radio) { if (brightness == LED_FULL) { ieee80211_queue_delayed_work(hw, &priv->led_on, 0); radio_on = true; } else if (radio_on) { radio_on = false; cancel_delayed_work(&priv->led_on); ieee80211_queue_delayed_work(hw, &priv->led_off, 0); } } else if (radio_on) { if (brightness == LED_OFF) { ieee80211_queue_delayed_work(hw, &priv->led_off, 0); /* The LED is off for 1/20 sec - it just blinks. */ ieee80211_queue_delayed_work(hw, &priv->led_on, HZ / 20); } else ieee80211_queue_delayed_work(hw, &priv->led_on, 0); } } static int rtl8187_register_led(struct ieee80211_hw *dev, struct rtl8187_led *led, const char *name, const char *default_trigger, u8 ledpin, bool is_radio) { int err; struct rtl8187_priv *priv = dev->priv; if (led->dev) return -EEXIST; if (!default_trigger) return -EINVAL; led->dev = dev; led->ledpin = ledpin; led->is_radio = is_radio; strncpy(led->name, name, sizeof(led->name)); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; led->led_dev.brightness_set = rtl8187_led_brightness_set; err = led_classdev_register(&priv->udev->dev, &led->led_dev); if (err) { printk(KERN_INFO "LEDs: Failed to register %s\n", name); led->dev = NULL; return err; } return 0; } static void rtl8187_unregister_led(struct rtl8187_led *led) { struct ieee80211_hw *hw = led->dev; struct rtl8187_priv *priv = hw->priv; led_classdev_unregister(&led->led_dev); flush_delayed_work(&priv->led_off); led->dev = NULL; } void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) { struct rtl8187_priv *priv = dev->priv; char name[RTL8187_LED_MAX_NAME_LEN + 1]; u8 ledpin; int err; /* According to the vendor driver, the LED operation depends on the * customer ID encoded in the EEPROM */ printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid); switch (custid) { case EEPROM_CID_RSVD0: case EEPROM_CID_RSVD1: case EEPROM_CID_SERCOMM_PS: case EEPROM_CID_QMI: case EEPROM_CID_DELL: case EEPROM_CID_TOSHIBA: ledpin = LED_PIN_GPIO0; break; case EEPROM_CID_ALPHA0: ledpin = LED_PIN_LED0; break; case EEPROM_CID_HW: ledpin = LED_PIN_HW; break; default: ledpin = LED_PIN_GPIO0; } INIT_DELAYED_WORK(&priv->led_on, led_turn_on); INIT_DELAYED_WORK(&priv->led_off, led_turn_off); snprintf(name, sizeof(name), "rtl8187-%s::radio", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_radio, name, ieee80211_get_radio_led_name(dev), ledpin, true); if (err) return; snprintf(name, sizeof(name), "rtl8187-%s::tx", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_tx, name, ieee80211_get_tx_led_name(dev), ledpin, false); if (err) goto err_tx; snprintf(name, sizeof(name), "rtl8187-%s::rx", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_rx, name, ieee80211_get_rx_led_name(dev), ledpin, false); if (!err) return; /* registration of RX LED failed - unregister */ rtl8187_unregister_led(&priv->led_tx); err_tx: rtl8187_unregister_led(&priv->led_radio); } void rtl8187_leds_exit(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; rtl8187_unregister_led(&priv->led_radio); rtl8187_unregister_led(&priv->led_rx); rtl8187_unregister_led(&priv->led_tx); cancel_delayed_work_sync(&priv->led_off); cancel_delayed_work_sync(&priv->led_on); } #endif /* def CONFIG_RTL8187_LEDS */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/0000755000175000017500000000000012026211315023035 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/Makefile0000644000175000017500000000017412026211315024477 0ustar mcgrofmcgrofrtl8180-objs := dev.o rtl8225.o sa2400.o max2820.o grf5101.o obj-$(CONFIG_RTL8180) += rtl8180.o ccflags-y += -I$(obj)/.. compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/dev.c0000644000175000017500000010103512026211315023757 0ustar mcgrofmcgrof /* * Linux device driver for RTL8180 / RTL8185 * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8180 driver, which is: * Copyright 2004-2005 Andrea Merello , et al. * * Thanks to Realtek for their support! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "rtl8180.h" #include "rtl8225.h" #include "sa2400.h" #include "max2820.h" #include "grf5101.h" MODULE_AUTHOR("Michael Wu "); MODULE_AUTHOR("Andrea Merello "); MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); MODULE_LICENSE("GPL"); static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = { /* rtl8185 */ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) }, { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) }, { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) }, /* rtl8180 */ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8180) }, { PCI_DEVICE(0x1799, 0x6001) }, { PCI_DEVICE(0x1799, 0x6020) }, { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) }, { PCI_DEVICE(0x1186, 0x3301) }, { PCI_DEVICE(0x1432, 0x7106) }, { } }; MODULE_DEVICE_TABLE(pci, rtl8180_table); static const struct ieee80211_rate rtl818x_rates[] = { { .bitrate = 10, .hw_value = 0, }, { .bitrate = 20, .hw_value = 1, }, { .bitrate = 55, .hw_value = 2, }, { .bitrate = 110, .hw_value = 3, }, { .bitrate = 60, .hw_value = 4, }, { .bitrate = 90, .hw_value = 5, }, { .bitrate = 120, .hw_value = 6, }, { .bitrate = 180, .hw_value = 7, }, { .bitrate = 240, .hw_value = 8, }, { .bitrate = 360, .hw_value = 9, }, { .bitrate = 480, .hw_value = 10, }, { .bitrate = 540, .hw_value = 11, }, }; static const struct ieee80211_channel rtl818x_channels[] = { { .center_freq = 2412 }, { .center_freq = 2417 }, { .center_freq = 2422 }, { .center_freq = 2427 }, { .center_freq = 2432 }, { .center_freq = 2437 }, { .center_freq = 2442 }, { .center_freq = 2447 }, { .center_freq = 2452 }, { .center_freq = 2457 }, { .center_freq = 2462 }, { .center_freq = 2467 }, { .center_freq = 2472 }, { .center_freq = 2484 }, }; void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) { struct rtl8180_priv *priv = dev->priv; int i = 10; u32 buf; buf = (data << 8) | addr; rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf | 0x80); while (i--) { rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf); if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF)) return; } } static void rtl8180_handle_rx(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; unsigned int count = 32; u8 signal, agc, sq; while (count--) { struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx]; struct sk_buff *skb = priv->rx_buf[priv->rx_idx]; u32 flags = le32_to_cpu(entry->flags); if (flags & RTL818X_RX_DESC_FLAG_OWN) return; if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL | RTL818X_RX_DESC_FLAG_FOF | RTL818X_RX_DESC_FLAG_RX_ERR))) goto done; else { u32 flags2 = le32_to_cpu(entry->flags2); struct ieee80211_rx_status rx_status = {0}; struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE); if (unlikely(!new_skb)) goto done; pci_unmap_single(priv->pdev, *((dma_addr_t *)skb->cb), MAX_RX_SIZE, PCI_DMA_FROMDEVICE); skb_put(skb, flags & 0xFFF); rx_status.antenna = (flags2 >> 15) & 1; rx_status.rate_idx = (flags >> 20) & 0xF; agc = (flags2 >> 17) & 0x7F; if (priv->r8185) { if (rx_status.rate_idx > 3) signal = 90 - clamp_t(u8, agc, 25, 90); else signal = 95 - clamp_t(u8, agc, 30, 95); } else { sq = flags2 & 0xff; signal = priv->rf->calc_rssi(agc, sq); } rx_status.signal = signal; rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; rx_status.mactime = le64_to_cpu(entry->tsft); rx_status.flag |= RX_FLAG_MACTIME_MPDU; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(dev, skb); skb = new_skb; priv->rx_buf[priv->rx_idx] = skb; *((dma_addr_t *) skb->cb) = pci_map_single(priv->pdev, skb_tail_pointer(skb), MAX_RX_SIZE, PCI_DMA_FROMDEVICE); } done: entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE); if (priv->rx_idx == 31) entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); priv->rx_idx = (priv->rx_idx + 1) % 32; } } static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) { struct rtl8180_priv *priv = dev->priv; struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; while (skb_queue_len(&ring->queue)) { struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; struct sk_buff *skb; struct ieee80211_tx_info *info; u32 flags = le32_to_cpu(entry->flags); if (flags & RTL818X_TX_DESC_FLAG_OWN) return; ring->idx = (ring->idx + 1) % ring->entries; skb = __skb_dequeue(&ring->queue); pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), skb->len, PCI_DMA_TODEVICE); info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && (flags & RTL818X_TX_DESC_FLAG_TX_OK)) info->flags |= IEEE80211_TX_STAT_ACK; info->status.rates[0].count = (flags & 0xFF) + 1; info->status.rates[1].idx = -1; ieee80211_tx_status_irqsafe(dev, skb); if (ring->entries - skb_queue_len(&ring->queue) == 2) ieee80211_wake_queue(dev, prio); } } static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) { struct ieee80211_hw *dev = dev_id; struct rtl8180_priv *priv = dev->priv; u16 reg; spin_lock(&priv->lock); reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS); if (unlikely(reg == 0xFFFF)) { spin_unlock(&priv->lock); return IRQ_HANDLED; } rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg); if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR)) rtl8180_handle_tx(dev, 3); if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR)) rtl8180_handle_tx(dev, 2); if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR)) rtl8180_handle_tx(dev, 1); if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR)) rtl8180_handle_tx(dev, 0); if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR)) rtl8180_handle_rx(dev); spin_unlock(&priv->lock); return IRQ_HANDLED; } static void rtl8180_tx(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct rtl8180_priv *priv = dev->priv; struct rtl8180_tx_ring *ring; struct rtl8180_tx_desc *entry; unsigned long flags; unsigned int idx, prio; dma_addr_t mapping; u32 tx_flags; u8 rc_flags; u16 plcp_len = 0; __le16 rts_duration = 0; prio = skb_get_queue_mapping(skb); ring = &priv->tx_ring[prio]; mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | RTL818X_TX_DESC_FLAG_LS | (ieee80211_get_tx_rate(dev, info)->hw_value << 24) | skb->len; if (priv->r8185) tx_flags |= RTL818X_TX_DESC_FLAG_DMA | RTL818X_TX_DESC_FLAG_NO_ENC; rc_flags = info->control.rates[0].flags; if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { tx_flags |= RTL818X_TX_DESC_FLAG_RTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { tx_flags |= RTL818X_TX_DESC_FLAG_CTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; } if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len, info); if (!priv->r8185) { unsigned int remainder; plcp_len = DIV_ROUND_UP(16 * (skb->len + 4), (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); remainder = (16 * (skb->len + 4)) % ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); if (remainder <= 6) plcp_len |= 1 << 15; } spin_lock_irqsave(&priv->lock, flags); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) priv->seqno += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(priv->seqno); } idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; entry = &ring->desc[idx]; entry->rts_duration = rts_duration; entry->plcp_len = cpu_to_le16(plcp_len); entry->tx_buf = cpu_to_le32(mapping); entry->frame_len = cpu_to_le32(skb->len); entry->flags2 = info->control.rates[1].idx >= 0 ? ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; entry->retry_limit = info->control.rates[0].count; entry->flags = cpu_to_le32(tx_flags); __skb_queue_tail(&ring->queue, skb); if (ring->entries - skb_queue_len(&ring->queue) < 2) ieee80211_stop_queue(dev, prio); spin_unlock_irqrestore(&priv->lock, flags); rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); } void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) { u8 reg; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } static int rtl8180_init_hw(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; u16 reg; rtl818x_iowrite8(priv, &priv->map->CMD, 0); rtl818x_ioread8(priv, &priv->map->CMD); msleep(10); /* reset */ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); rtl818x_ioread8(priv, &priv->map->CMD); reg = rtl818x_ioread8(priv, &priv->map->CMD); reg &= (1 << 1); reg |= RTL818X_CMD_RESET; rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET); rtl818x_ioread8(priv, &priv->map->CMD); msleep(200); /* check success of reset */ if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) { wiphy_err(dev->wiphy, "reset timeout!\n"); return -ETIMEDOUT; } rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); rtl818x_ioread8(priv, &priv->map->CMD); msleep(200); if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) { /* For cardbus */ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); reg |= 1 << 1; rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); reg = rtl818x_ioread16(priv, &priv->map->FEMR); reg |= (1 << 15) | (1 << 14) | (1 << 4); rtl818x_iowrite16(priv, &priv->map->FEMR, reg); } rtl818x_iowrite8(priv, &priv->map->MSR, 0); if (!priv->r8185) rtl8180_set_anaparam(priv, priv->anaparam); rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); /* TODO: necessary? specs indicate not */ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3)); if (priv->r8185) { reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4)); } rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); /* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */ /* TODO: turn off hw wep on rtl8180 */ rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); if (priv->r8185) { rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); /* TODO: set ClkRun enable? necessary? */ reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6)); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } else { rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1); rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6); rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C); } priv->rf->init(dev); if (priv->r8185) rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); return 0; } static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; struct rtl8180_rx_desc *entry; int i; priv->rx_ring = pci_alloc_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32, &priv->rx_ring_dma); if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) { wiphy_err(dev->wiphy, "Cannot allocate RX ring\n"); return -ENOMEM; } memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32); priv->rx_idx = 0; for (i = 0; i < 32; i++) { struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE); dma_addr_t *mapping; entry = &priv->rx_ring[i]; if (!skb) return 0; priv->rx_buf[i] = skb; mapping = (dma_addr_t *)skb->cb; *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), MAX_RX_SIZE, PCI_DMA_FROMDEVICE); entry->rx_buf = cpu_to_le32(*mapping); entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE); } entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); return 0; } static void rtl8180_free_rx_ring(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; int i; for (i = 0; i < 32; i++) { struct sk_buff *skb = priv->rx_buf[i]; if (!skb) continue; pci_unmap_single(priv->pdev, *((dma_addr_t *)skb->cb), MAX_RX_SIZE, PCI_DMA_FROMDEVICE); kfree_skb(skb); } pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32, priv->rx_ring, priv->rx_ring_dma); priv->rx_ring = NULL; } static int rtl8180_init_tx_ring(struct ieee80211_hw *dev, unsigned int prio, unsigned int entries) { struct rtl8180_priv *priv = dev->priv; struct rtl8180_tx_desc *ring; dma_addr_t dma; int i; ring = pci_alloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma); if (!ring || (unsigned long)ring & 0xFF) { wiphy_err(dev->wiphy, "Cannot allocate TX ring (prio = %d)\n", prio); return -ENOMEM; } memset(ring, 0, sizeof(*ring)*entries); priv->tx_ring[prio].desc = ring; priv->tx_ring[prio].dma = dma; priv->tx_ring[prio].idx = 0; priv->tx_ring[prio].entries = entries; skb_queue_head_init(&priv->tx_ring[prio].queue); for (i = 0; i < entries; i++) ring[i].next_tx_desc = cpu_to_le32((u32)dma + ((i + 1) % entries) * sizeof(*ring)); return 0; } static void rtl8180_free_tx_ring(struct ieee80211_hw *dev, unsigned int prio) { struct rtl8180_priv *priv = dev->priv; struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; while (skb_queue_len(&ring->queue)) { struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; struct sk_buff *skb = __skb_dequeue(&ring->queue); pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), skb->len, PCI_DMA_TODEVICE); kfree_skb(skb); ring->idx = (ring->idx + 1) % ring->entries; } pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries, ring->desc, ring->dma); ring->desc = NULL; } static int rtl8180_start(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; int ret, i; u32 reg; ret = rtl8180_init_rx_ring(dev); if (ret) return ret; for (i = 0; i < 4; i++) if ((ret = rtl8180_init_tx_ring(dev, i, 16))) goto err_free_rings; ret = rtl8180_init_hw(dev); if (ret) goto err_free_rings; rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); ret = request_irq(priv->pdev->irq, rtl8180_interrupt, IRQF_SHARED, KBUILD_MODNAME, dev); if (ret) { wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); goto err_free_rings; } rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); reg = RTL818X_RX_CONF_ONLYERLPKT | RTL818X_RX_CONF_RX_AUTORESETPHY | RTL818X_RX_CONF_MGMT | RTL818X_RX_CONF_DATA | (7 << 8 /* MAX RX DMA */) | RTL818X_RX_CONF_BROADCAST | RTL818X_RX_CONF_NICMAC; if (priv->r8185) reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2; else { reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1) ? RTL818X_RX_CONF_CSDM1 : 0; reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2) ? RTL818X_RX_CONF_CSDM2 : 0; } priv->rx_conf = reg; rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); if (priv->r8185) { reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT; rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); /* disable early TX */ rtl818x_iowrite8(priv, (u8 __iomem *)priv->map + 0xec, 0x3f); } reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); reg |= (6 << 21 /* MAX TX DMA */) | RTL818X_TX_CONF_NO_ICV; if (priv->r8185) reg &= ~RTL818X_TX_CONF_PROBE_DTS; else reg &= ~RTL818X_TX_CONF_HW_SEQNUM; /* different meaning, same value on both rtl8185 and rtl8180 */ reg &= ~RTL818X_TX_CONF_SAT_HWPLCP; rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->CMD); reg |= RTL818X_CMD_RX_ENABLE; reg |= RTL818X_CMD_TX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); return 0; err_free_rings: rtl8180_free_rx_ring(dev); for (i = 0; i < 4; i++) if (priv->tx_ring[i].desc) rtl8180_free_tx_ring(dev, i); return ret; } static void rtl8180_stop(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; u8 reg; int i; rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); reg = rtl818x_ioread8(priv, &priv->map->CMD); reg &= ~RTL818X_CMD_TX_ENABLE; reg &= ~RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); priv->rf->stop(dev); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); free_irq(priv->pdev->irq, dev); rtl8180_free_rx_ring(dev); for (i = 0; i < 4; i++) rtl8180_free_tx_ring(dev, i); } static u64 rtl8180_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8180_priv *priv = dev->priv; return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; } static void rtl8180_beacon_work(struct work_struct *work) { struct rtl8180_vif *vif_priv = container_of(work, struct rtl8180_vif, beacon_work.work); struct ieee80211_vif *vif = container_of((void *)vif_priv, struct ieee80211_vif, drv_priv); struct ieee80211_hw *dev = vif_priv->dev; struct ieee80211_mgmt *mgmt; struct sk_buff *skb; /* don't overflow the tx ring */ if (ieee80211_queue_stopped(dev, 0)) goto resched; /* grab a fresh beacon */ skb = ieee80211_beacon_get(dev, vif); if (!skb) goto resched; /* * update beacon timestamp w/ TSF value * TODO: make hardware update beacon timestamp */ mgmt = (struct ieee80211_mgmt *)skb->data; mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev, vif)); /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); rtl8180_tx(dev, NULL, skb); resched: /* * schedule next beacon * TODO: use hardware support for beacon timing */ schedule_delayed_work(&vif_priv->beacon_work, usecs_to_jiffies(1024 * vif->bss_conf.beacon_int)); } static int rtl8180_add_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8180_priv *priv = dev->priv; struct rtl8180_vif *vif_priv; /* * We only support one active interface at a time. */ if (priv->vif) return -EBUSY; switch (vif->type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: break; default: return -EOPNOTSUPP; } priv->vif = vif; /* Initialize driver private area */ vif_priv = (struct rtl8180_vif *)&vif->drv_priv; vif_priv->dev = dev; INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8180_beacon_work); vif_priv->enable_beacon = false; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0], le32_to_cpu(*(__le32 *)vif->addr)); rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4], le16_to_cpu(*(__le16 *)(vif->addr + 4))); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); return 0; } static void rtl8180_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8180_priv *priv = dev->priv; priv->vif = NULL; } static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) { struct rtl8180_priv *priv = dev->priv; struct ieee80211_conf *conf = &dev->conf; priv->rf->set_chan(dev, conf); return 0; } static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct rtl8180_priv *priv = dev->priv; struct rtl8180_vif *vif_priv; int i; u8 reg; vif_priv = (struct rtl8180_vif *)&vif->drv_priv; if (changed & BSS_CHANGED_BSSID) { for (i = 0; i < ETH_ALEN; i++) rtl818x_iowrite8(priv, &priv->map->BSSID[i], info->bssid[i]); if (is_valid_ether_addr(info->bssid)) { if (vif->type == NL80211_IFTYPE_ADHOC) reg = RTL818X_MSR_ADHOC; else reg = RTL818X_MSR_INFRA; } else reg = RTL818X_MSR_NO_LINK; rtl818x_iowrite8(priv, &priv->map->MSR, reg); } if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) priv->rf->conf_erp(dev, info); if (changed & BSS_CHANGED_BEACON_ENABLED) vif_priv->enable_beacon = info->enable_beacon; if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) { cancel_delayed_work_sync(&vif_priv->beacon_work); if (vif_priv->enable_beacon) schedule_work(&vif_priv->beacon_work.work); } } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, struct netdev_hw_addr_list *mc_list) #else static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, int mc_count, struct dev_addr_list *mc_list) #endif { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) return netdev_hw_addr_list_count(mc_list); #else return mc_count; #endif } static void rtl8180_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct rtl8180_priv *priv = dev->priv; if (changed_flags & FIF_FCSFAIL) priv->rx_conf ^= RTL818X_RX_CONF_FCS; if (changed_flags & FIF_CONTROL) priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; *total_flags = 0; if (priv->rx_conf & RTL818X_RX_CONF_FCS) *total_flags |= FIF_FCSFAIL; if (priv->rx_conf & RTL818X_RX_CONF_CTRL) *total_flags |= FIF_CONTROL; if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) *total_flags |= FIF_OTHER_BSS; if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) *total_flags |= FIF_ALLMULTI; rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf); } static const struct ieee80211_ops rtl8180_ops = { .tx = rtl8180_tx, .start = rtl8180_start, .stop = rtl8180_stop, .add_interface = rtl8180_add_interface, .remove_interface = rtl8180_remove_interface, .config = rtl8180_config, .bss_info_changed = rtl8180_bss_info_changed, .prepare_multicast = rtl8180_prepare_multicast, .configure_filter = rtl8180_configure_filter, .get_tsf = rtl8180_get_tsf, }; static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) { struct ieee80211_hw *dev = eeprom->data; struct rtl8180_priv *priv = dev->priv; u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; } static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) { struct ieee80211_hw *dev = eeprom->data; struct rtl8180_priv *priv = dev->priv; u8 reg = 2 << 6; if (eeprom->reg_data_in) reg |= RTL818X_EEPROM_CMD_WRITE; if (eeprom->reg_data_out) reg |= RTL818X_EEPROM_CMD_READ; if (eeprom->reg_data_clock) reg |= RTL818X_EEPROM_CMD_CK; if (eeprom->reg_chip_select) reg |= RTL818X_EEPROM_CMD_CS; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(10); } static int __devinit rtl8180_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ieee80211_hw *dev; struct rtl8180_priv *priv; unsigned long mem_addr, mem_len; unsigned int io_addr, io_len; int err, i; struct eeprom_93cx6 eeprom; const char *chip_name, *rf_name = NULL; u32 reg; u16 eeprom_val; u8 mac_addr[ETH_ALEN]; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s (rtl8180): Cannot enable new PCI device\n", pci_name(pdev)); return err; } err = pci_request_regions(pdev, KBUILD_MODNAME); if (err) { printk(KERN_ERR "%s (rtl8180): Cannot obtain PCI resources\n", pci_name(pdev)); return err; } io_addr = pci_resource_start(pdev, 0); io_len = pci_resource_len(pdev, 0); mem_addr = pci_resource_start(pdev, 1); mem_len = pci_resource_len(pdev, 1); if (mem_len < sizeof(struct rtl818x_csr) || io_len < sizeof(struct rtl818x_csr)) { printk(KERN_ERR "%s (rtl8180): Too short PCI resources\n", pci_name(pdev)); err = -ENOMEM; goto err_free_reg; } if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) || (err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))) { printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n", pci_name(pdev)); goto err_free_reg; } pci_set_master(pdev); dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8180_ops); if (!dev) { printk(KERN_ERR "%s (rtl8180): ieee80211 alloc failed\n", pci_name(pdev)); err = -ENOMEM; goto err_free_reg; } priv = dev->priv; priv->pdev = pdev; dev->max_rates = 2; SET_IEEE80211_DEV(dev, &pdev->dev); pci_set_drvdata(pdev, dev); priv->map = pci_iomap(pdev, 1, mem_len); if (!priv->map) priv->map = pci_iomap(pdev, 0, io_len); if (!priv->map) { printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n", pci_name(pdev)); goto err_free_dev; } BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); priv->band.band = IEEE80211_BAND_2GHZ; priv->band.channels = priv->channels; priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); priv->band.bitrates = priv->rates; priv->band.n_bitrates = 4; dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_UNSPEC; dev->vif_data_size = sizeof(struct rtl8180_vif); dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); dev->queues = 1; dev->max_signal = 65; reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); reg &= RTL818X_TX_CONF_HWVER_MASK; switch (reg) { case RTL818X_TX_CONF_R8180_ABCD: chip_name = "RTL8180"; break; case RTL818X_TX_CONF_R8180_F: chip_name = "RTL8180vF"; break; case RTL818X_TX_CONF_R8185_ABC: chip_name = "RTL8185"; break; case RTL818X_TX_CONF_R8185_D: chip_name = "RTL8185vD"; break; default: printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", pci_name(pdev), reg >> 25); goto err_iounmap; } priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC; if (priv->r8185) { priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); pci_try_set_mwi(pdev); } eeprom.data = dev; eeprom.register_read = rtl8180_eeprom_register_read; eeprom.register_write = rtl8180_eeprom_register_write; if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) eeprom.width = PCI_EEPROM_WIDTH_93C66; else eeprom.width = PCI_EEPROM_WIDTH_93C46; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(10); eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val); eeprom_val &= 0xFF; switch (eeprom_val) { case 1: rf_name = "Intersil"; break; case 2: rf_name = "RFMD"; break; case 3: priv->rf = &sa2400_rf_ops; break; case 4: priv->rf = &max2820_rf_ops; break; case 5: priv->rf = &grf5101_rf_ops; break; case 9: priv->rf = rtl8180_detect_rf(dev); break; case 10: rf_name = "RTL8255"; break; default: printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n", pci_name(pdev), eeprom_val); goto err_iounmap; } if (!priv->rf) { printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n", pci_name(pdev), rf_name); goto err_iounmap; } eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val); priv->csthreshold = eeprom_val >> 8; if (!priv->r8185) { __le32 anaparam; eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2); priv->anaparam = le32_to_cpu(anaparam); eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam); } eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3); if (!is_valid_ether_addr(mac_addr)) { printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using" " randomly generated MAC addr\n", pci_name(pdev)); eth_random_addr(mac_addr); } SET_IEEE80211_PERM_ADDR(dev, mac_addr); /* CCK TX power */ for (i = 0; i < 14; i += 2) { u16 txpwr; eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr); priv->channels[i].hw_value = txpwr & 0xFF; priv->channels[i + 1].hw_value = txpwr >> 8; } /* OFDM TX power */ if (priv->r8185) { for (i = 0; i < 14; i += 2) { u16 txpwr; eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr); priv->channels[i].hw_value |= (txpwr & 0xFF) << 8; priv->channels[i + 1].hw_value |= txpwr & 0xFF00; } } rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); spin_lock_init(&priv->lock); err = ieee80211_register_hw(dev); if (err) { printk(KERN_ERR "%s (rtl8180): Cannot register device\n", pci_name(pdev)); goto err_iounmap; } wiphy_info(dev->wiphy, "hwaddr %pm, %s + %s\n", mac_addr, chip_name, priv->rf->name); return 0; err_iounmap: iounmap(priv->map); err_free_dev: pci_set_drvdata(pdev, NULL); ieee80211_free_hw(dev); err_free_reg: pci_release_regions(pdev); pci_disable_device(pdev); return err; } static void __devexit rtl8180_remove(struct pci_dev *pdev) { struct ieee80211_hw *dev = pci_get_drvdata(pdev); struct rtl8180_priv *priv; if (!dev) return; ieee80211_unregister_hw(dev); priv = dev->priv; pci_iounmap(pdev, priv->map); pci_release_regions(pdev); pci_disable_device(pdev); ieee80211_free_hw(dev); } #ifdef CONFIG_PM static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state) { pci_save_state(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); return 0; } static int rtl8180_resume(struct pci_dev *pdev) { pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); return 0; } #endif /* CONFIG_PM */ static struct pci_driver rtl8180_driver = { .name = KBUILD_MODNAME, .id_table = rtl8180_table, .probe = rtl8180_probe, .remove = __devexit_p(rtl8180_remove), #ifdef CONFIG_PM .suspend = rtl8180_suspend, .resume = rtl8180_resume, #endif /* CONFIG_PM */ }; module_pci_driver(rtl8180_driver); compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/sa2400.h0000644000175000017500000000202512026211315024116 0ustar mcgrofmcgrof#ifndef RTL8180_SA2400_H #define RTL8180_SA2400_H /* * Radio tuning for Philips SA2400 on RTL8180 * * Copyright 2007 Andrea Merello * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things * * I want to thanks the Authors of such projects and the Ndiswrapper * project Authors. * * A special Big Thanks also is for all people who donated me cards, * making possible the creation of the original rtl8180 driver * from which this code is derived! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define SA2400_ANTENNA 0x91 #define SA2400_DIG_ANAPARAM_PWR1_ON 0x8 #define SA2400_ANA_ANAPARAM_PWR1_ON 0x28 #define SA2400_ANAPARAM_PWR0_ON 0x3 /* RX sensitivity in dbm */ #define SA2400_MAX_SENS 85 #define SA2400_REG4_FIRDAC_SHIFT 7 extern const struct rtl818x_rf_ops sa2400_rf_ops; #endif /* RTL8180_SA2400_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/sa2400.c0000644000175000017500000001354612026211315024123 0ustar mcgrofmcgrof /* * Radio tuning for Philips SA2400 on RTL8180 * * Copyright 2007 Andrea Merello * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things * * I want to thanks the Authors of such projects and the Ndiswrapper * project Authors. * * A special Big Thanks also is for all people who donated me cards, * making possible the creation of the original rtl8180 driver * from which this code is derived! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "rtl8180.h" #include "sa2400.h" static const u32 sa2400_chan[] = { 0x00096c, /* ch1 */ 0x080970, 0x100974, 0x180978, 0x000980, 0x080984, 0x100988, 0x18098c, 0x000994, 0x080998, 0x10099c, 0x1809a0, 0x0009a8, 0x0009b4, /* ch 14 */ }; static void write_sa2400(struct ieee80211_hw *dev, u8 addr, u32 data) { struct rtl8180_priv *priv = dev->priv; u32 phy_config; /* MAC will bang bits to the sa2400. sw 3-wire is NOT used */ phy_config = 0xb0000000; phy_config |= ((u32)(addr & 0xf)) << 24; phy_config |= data & 0xffffff; rtl818x_iowrite32(priv, (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); msleep(3); } static void sa2400_write_phy_antenna(struct ieee80211_hw *dev, short chan) { struct rtl8180_priv *priv = dev->priv; u8 ant = SA2400_ANTENNA; if (priv->rfparam & RF_PARAM_ANTBDEFAULT) ant |= BB_ANTENNA_B; if (chan == 14) ant |= BB_ANTATTEN_CHAN14; rtl8180_write_phy(dev, 0x10, ant); } static u8 sa2400_rf_rssi_map[] = { 0x64, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x57, 0x54, 0x52, 0x50, 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f, 0x3c, 0x3a, 0x37, 0x36, 0x36, 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, }; static u8 sa2400_rf_calc_rssi(u8 agc, u8 sq) { if (sq == 0x80) return 1; if (sq > 78) return 32; /* TODO: recalc sa2400_rf_rssi_map to avoid mult / div */ return 65 * sa2400_rf_rssi_map[sq] / 100; } static void sa2400_rf_set_channel(struct ieee80211_hw *dev, struct ieee80211_conf *conf) { struct rtl8180_priv *priv = dev->priv; int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; u32 chan = sa2400_chan[channel - 1]; write_sa2400(dev, 7, txpw); sa2400_write_phy_antenna(dev, channel); write_sa2400(dev, 0, chan); write_sa2400(dev, 1, 0xbb50); write_sa2400(dev, 2, 0x80); write_sa2400(dev, 3, 0); } static void sa2400_rf_stop(struct ieee80211_hw *dev) { write_sa2400(dev, 4, 0); } static void sa2400_rf_init(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; u32 anaparam, txconf; u8 firdac; int analogphy = priv->rfparam & RF_PARAM_ANALOGPHY; anaparam = priv->anaparam; anaparam &= ~(1 << ANAPARAM_TXDACOFF_SHIFT); anaparam &= ~ANAPARAM_PWR1_MASK; anaparam &= ~ANAPARAM_PWR0_MASK; if (analogphy) { anaparam |= SA2400_ANA_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT; firdac = 0; } else { anaparam |= (SA2400_DIG_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT); anaparam |= (SA2400_ANAPARAM_PWR0_ON << ANAPARAM_PWR0_SHIFT); firdac = 1 << SA2400_REG4_FIRDAC_SHIFT; } rtl8180_set_anaparam(priv, anaparam); write_sa2400(dev, 0, sa2400_chan[0]); write_sa2400(dev, 1, 0xbb50); write_sa2400(dev, 2, 0x80); write_sa2400(dev, 3, 0); write_sa2400(dev, 4, 0x19340 | firdac); write_sa2400(dev, 5, 0x1dfb | (SA2400_MAX_SENS - 54) << 15); write_sa2400(dev, 4, 0x19348 | firdac); /* calibrate VCO */ if (!analogphy) write_sa2400(dev, 4, 0x1938c); /*???*/ write_sa2400(dev, 4, 0x19340 | firdac); write_sa2400(dev, 0, sa2400_chan[0]); write_sa2400(dev, 1, 0xbb50); write_sa2400(dev, 2, 0x80); write_sa2400(dev, 3, 0); write_sa2400(dev, 4, 0x19344 | firdac); /* calibrate filter */ /* new from rtl8180 embedded driver (rtl8181 project) */ write_sa2400(dev, 6, 0x13ff | (1 << 23)); /* MANRX */ write_sa2400(dev, 8, 0); /* VCO */ if (analogphy) { rtl8180_set_anaparam(priv, anaparam | (1 << ANAPARAM_TXDACOFF_SHIFT)); txconf = rtl818x_ioread32(priv, &priv->map->TX_CONF); rtl818x_iowrite32(priv, &priv->map->TX_CONF, txconf | RTL818X_TX_CONF_LOOPBACK_CONT); write_sa2400(dev, 4, 0x19341); /* calibrates DC */ /* a 5us sleep is required here, * we rely on the 3ms delay introduced in write_sa2400 */ write_sa2400(dev, 4, 0x19345); /* a 20us sleep is required here, * we rely on the 3ms delay introduced in write_sa2400 */ rtl818x_iowrite32(priv, &priv->map->TX_CONF, txconf); rtl8180_set_anaparam(priv, anaparam); } /* end new code */ write_sa2400(dev, 4, 0x19341 | firdac); /* RTX MODE */ /* baseband configuration */ rtl8180_write_phy(dev, 0, 0x98); rtl8180_write_phy(dev, 3, 0x38); rtl8180_write_phy(dev, 4, 0xe0); rtl8180_write_phy(dev, 5, 0x90); rtl8180_write_phy(dev, 6, 0x1a); rtl8180_write_phy(dev, 7, 0x64); sa2400_write_phy_antenna(dev, 1); rtl8180_write_phy(dev, 0x11, 0x80); if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & RTL818X_CONFIG2_ANTENNA_DIV) rtl8180_write_phy(dev, 0x12, 0xc7); /* enable ant diversity */ else rtl8180_write_phy(dev, 0x12, 0x47); /* disable ant diversity */ rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); rtl8180_write_phy(dev, 0x19, 0x0); rtl8180_write_phy(dev, 0x1a, 0xa0); } const struct rtl818x_rf_ops sa2400_rf_ops = { .name = "Philips", .init = sa2400_rf_init, .stop = sa2400_rf_stop, .set_chan = sa2400_rf_set_channel, .calc_rssi = sa2400_rf_calc_rssi, }; compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/rtl8225.h0000644000175000017500000000112412026211315024326 0ustar mcgrofmcgrof#ifndef RTL8180_RTL8225_H #define RTL8180_RTL8225_H #define RTL8225_ANAPARAM_ON 0xa0000b59 #define RTL8225_ANAPARAM2_ON 0x860dec11 #define RTL8225_ANAPARAM_OFF 0xa00beb59 #define RTL8225_ANAPARAM2_OFF 0x840dec11 const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *); static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, u8 addr, u8 data) { rtl8180_write_phy(dev, addr, data); } static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, u8 addr, u8 data) { rtl8180_write_phy(dev, addr, data | 0x10000); } #endif /* RTL8180_RTL8225_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c0000644000175000017500000006670412026211315024340 0ustar mcgrofmcgrof /* * Radio tuning for RTL8225 on RTL8180 * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8180 driver, which is: * Copyright 2005 Andrea Merello , et al. * * Thanks to Realtek for their support! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "rtl8180.h" #include "rtl8225.h" static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) { struct rtl8180_priv *priv = dev->priv; u16 reg80, reg84, reg82; u32 bangdata; int i; bangdata = (data << 4) | (addr & 0xf); reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(10); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(10); for (i = 15; i >= 0; i--) { u16 reg = reg80; if (bangdata & (1 << i)) reg |= 1; if (i & 1) rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); if (!(i & 1)) rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); } rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(10); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); } static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) { struct rtl8180_priv *priv = dev->priv; u16 reg80, reg82, reg84, out; int i; reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400; reg80 &= ~0xF; rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(4); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(5); for (i = 4; i >= 0; i--) { u16 reg = reg80 | ((addr >> i) & 1); if (!(i & 1)) { rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(1); } rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); if (i & 1) { rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(1); } } rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); out = 0; for (i = 11; i >= 0; i--) { rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(1); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 1)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) out |= 1 << i; rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); } rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 3) | (1 << 2)); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); udelay(2); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); return out; } static const u16 rtl8225bcd_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb }; static const u8 rtl8225_agc[] = { 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static const u8 rtl8225_gain[] = { 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ }; static const u8 rtl8225_threshold[] = { 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd }; static const u8 rtl8225_tx_gain_cck_ofdm[] = { 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e }; static const u8 rtl8225_tx_power_cck[] = { 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 }; static const u8 rtl8225_tx_power_cck_ch14[] = { 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 }; static const u8 rtl8225_tx_power_ofdm[] = { 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 }; static const u32 rtl8225_chan[] = { 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 }; static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) { struct rtl8180_priv *priv = dev->priv; u8 cck_power, ofdm_power; const u8 *tmp; u32 reg; int i; cck_power = priv->channels[channel - 1].hw_value & 0xFF; ofdm_power = priv->channels[channel - 1].hw_value >> 8; cck_power = min(cck_power, (u8)35); ofdm_power = min(ofdm_power, (u8)35); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); if (channel == 14) tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; else tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; for (i = 0; i < 8; i++) rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); msleep(1); /* FIXME: optional? */ /* anaparam2 on */ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1); tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; rtl8225_write_phy_ofdm(dev, 5, *tmp); rtl8225_write_phy_ofdm(dev, 7, *tmp); msleep(1); } static void rtl8225_rf_init(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; int i; rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); /* host_pci_init */ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); msleep(200); /* FIXME: ehh?? */ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); /* TODO: check if we need really to change BRSR to do RF config */ rtl818x_ioread16(priv, &priv->map->BRSR); rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl8225_write(dev, 0x0, 0x067); rtl8225_write(dev, 0x1, 0xFE0); rtl8225_write(dev, 0x2, 0x44D); rtl8225_write(dev, 0x3, 0x441); rtl8225_write(dev, 0x4, 0x8BE); rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */ rtl8225_write(dev, 0x6, 0xAE6); rtl8225_write(dev, 0x7, rtl8225_chan[0]); rtl8225_write(dev, 0x8, 0x01F); rtl8225_write(dev, 0x9, 0x334); rtl8225_write(dev, 0xA, 0xFD4); rtl8225_write(dev, 0xB, 0x391); rtl8225_write(dev, 0xC, 0x050); rtl8225_write(dev, 0xD, 0x6DB); rtl8225_write(dev, 0xE, 0x029); rtl8225_write(dev, 0xF, 0x914); msleep(1); rtl8225_write(dev, 0x2, 0xC4D); msleep(100); rtl8225_write(dev, 0x0, 0x127); for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { rtl8225_write(dev, 0x1, i + 1); rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); } rtl8225_write(dev, 0x0, 0x027); rtl8225_write(dev, 0x0, 0x22F); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); msleep(1); rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); msleep(1); } msleep(1); rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1); rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); rtl8225_write_phy_cck(dev, 0x13, 0xd0); rtl8225_write_phy_cck(dev, 0x19, 0x00); rtl8225_write_phy_cck(dev, 0x1a, 0xa0); rtl8225_write_phy_cck(dev, 0x1b, 0x08); rtl8225_write_phy_cck(dev, 0x40, 0x86); rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1); rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1); rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1); rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1); rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1); rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1); rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1); rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1); rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1); rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1); rtl8225_rf_set_tx_power(dev, 1); /* RX antenna default to A */ rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ msleep(1); rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); rtl8225_write(dev, 0x0c, 0x50); /* set OFDM initial gain */ rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]); rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]); rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]); rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]); /* set CCK threshold */ rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]); } static const u8 rtl8225z2_tx_power_cck_ch14[] = { 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 }; static const u8 rtl8225z2_tx_power_cck_B[] = { 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04 }; static const u8 rtl8225z2_tx_power_cck_A[] = { 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04 }; static const u8 rtl8225z2_tx_power_cck[] = { 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 }; static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) { struct rtl8180_priv *priv = dev->priv; u8 cck_power, ofdm_power; const u8 *tmp; int i; cck_power = priv->channels[channel - 1].hw_value & 0xFF; ofdm_power = priv->channels[channel - 1].hw_value >> 8; if (channel == 14) tmp = rtl8225z2_tx_power_cck_ch14; else if (cck_power == 12) tmp = rtl8225z2_tx_power_cck_B; else if (cck_power == 13) tmp = rtl8225z2_tx_power_cck_A; else tmp = rtl8225z2_tx_power_cck; for (i = 0; i < 8; i++) rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); cck_power = min(cck_power, (u8)35); if (cck_power == 13 || cck_power == 14) cck_power = 12; if (cck_power >= 15) cck_power -= 2; rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power); rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK); msleep(1); ofdm_power = min(ofdm_power, (u8)35); rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power); rtl8225_write_phy_ofdm(dev, 2, 0x62); rtl8225_write_phy_ofdm(dev, 5, 0x00); rtl8225_write_phy_ofdm(dev, 6, 0x40); rtl8225_write_phy_ofdm(dev, 7, 0x00); rtl8225_write_phy_ofdm(dev, 8, 0x40); msleep(1); } static const u16 rtl8225z2_rxgain[] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb }; static void rtl8225z2_rf_init(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; int i; rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); /* host_pci_init */ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); msleep(200); /* FIXME: ehh?? */ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008); /* TODO: check if we need really to change BRSR to do RF config */ rtl818x_ioread16(priv, &priv->map->BRSR); rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); rtl8225_write(dev, 0x0, 0x0B7); msleep(1); rtl8225_write(dev, 0x1, 0xEE0); msleep(1); rtl8225_write(dev, 0x2, 0x44D); msleep(1); rtl8225_write(dev, 0x3, 0x441); msleep(1); rtl8225_write(dev, 0x4, 0x8C3); msleep(1); rtl8225_write(dev, 0x5, 0xC72); msleep(1); rtl8225_write(dev, 0x6, 0x0E6); msleep(1); rtl8225_write(dev, 0x7, 0x82A); msleep(1); rtl8225_write(dev, 0x8, 0x03F); msleep(1); rtl8225_write(dev, 0x9, 0x335); msleep(1); rtl8225_write(dev, 0xa, 0x9D4); msleep(1); rtl8225_write(dev, 0xb, 0x7BB); msleep(1); rtl8225_write(dev, 0xc, 0x850); msleep(1); rtl8225_write(dev, 0xd, 0xCDF); msleep(1); rtl8225_write(dev, 0xe, 0x02B); msleep(1); rtl8225_write(dev, 0xf, 0x114); msleep(100); if (!(rtl8225_read(dev, 6) & (1 << 7))) { rtl8225_write(dev, 0x02, 0x0C4D); msleep(200); rtl8225_write(dev, 0x02, 0x044D); msleep(100); /* TODO: readd calibration failure message when the calibration check works */ } rtl8225_write(dev, 0x0, 0x1B7); rtl8225_write(dev, 0x3, 0x002); rtl8225_write(dev, 0x5, 0x004); for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { rtl8225_write(dev, 0x1, i + 1); rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); } rtl8225_write(dev, 0x0, 0x0B7); msleep(100); rtl8225_write(dev, 0x2, 0xC4D); msleep(200); rtl8225_write(dev, 0x2, 0x44D); msleep(100); rtl8225_write(dev, 0x00, 0x2BF); rtl8225_write(dev, 0xFF, 0xFFFF); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); msleep(1); rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); msleep(1); } msleep(1); rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1); rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1); rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1); rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1); rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */ rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); rtl8225_write_phy_cck(dev, 0x13, 0xd0); rtl8225_write_phy_cck(dev, 0x19, 0x00); rtl8225_write_phy_cck(dev, 0x1a, 0xa0); rtl8225_write_phy_cck(dev, 0x1b, 0x08); rtl8225_write_phy_cck(dev, 0x40, 0x86); rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1); rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1); rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1); rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1); rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1); rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1); rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1); rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1); rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1); rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1); rtl8225z2_rf_set_tx_power(dev, 1); /* RX antenna default to A */ rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ msleep(1); rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); } static void rtl8225_rf_stop(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; u8 reg; rtl8225_write(dev, 0x4, 0x1f); msleep(1); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF); rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF); rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, struct ieee80211_conf *conf) { struct rtl8180_priv *priv = dev->priv; int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); if (priv->rf->init == rtl8225_rf_init) rtl8225_rf_set_tx_power(dev, chan); else rtl8225z2_rf_set_tx_power(dev, chan); rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); msleep(10); } static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev, struct ieee80211_bss_conf *info) { struct rtl8180_priv *priv = dev->priv; if (info->use_short_slot) { rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); rtl818x_iowrite8(priv, &priv->map->EIFS, 81); rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); } else { rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44); rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); rtl818x_iowrite8(priv, &priv->map->EIFS, 81); rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); } } static const struct rtl818x_rf_ops rtl8225_ops = { .name = "rtl8225", .init = rtl8225_rf_init, .stop = rtl8225_rf_stop, .set_chan = rtl8225_rf_set_channel, .conf_erp = rtl8225_rf_conf_erp, }; static const struct rtl818x_rf_ops rtl8225z2_ops = { .name = "rtl8225z2", .init = rtl8225z2_rf_init, .stop = rtl8225_rf_stop, .set_chan = rtl8225_rf_set_channel, .conf_erp = rtl8225_rf_conf_erp, }; const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; u16 reg8, reg9; rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); msleep(100); rtl8225_write(dev, 0, 0x1B7); reg8 = rtl8225_read(dev, 8); reg9 = rtl8225_read(dev, 9); rtl8225_write(dev, 0, 0x0B7); if (reg8 != 0x588 || reg9 != 0x700) return &rtl8225_ops; return &rtl8225z2_ops; } compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h0000644000175000017500000000527112026211315024335 0ustar mcgrofmcgrof#ifndef RTL8180_H #define RTL8180_H #include "rtl818x.h" #define MAX_RX_SIZE IEEE80211_MAX_RTS_THRESHOLD #define RF_PARAM_ANALOGPHY (1 << 0) #define RF_PARAM_ANTBDEFAULT (1 << 1) #define RF_PARAM_CARRIERSENSE1 (1 << 2) #define RF_PARAM_CARRIERSENSE2 (1 << 3) #define BB_ANTATTEN_CHAN14 0x0C #define BB_ANTENNA_B 0x40 #define BB_HOST_BANG (1 << 30) #define BB_HOST_BANG_EN (1 << 2) #define BB_HOST_BANG_CLK (1 << 1) #define BB_HOST_BANG_DATA 1 #define ANAPARAM_TXDACOFF_SHIFT 27 #define ANAPARAM_PWR0_SHIFT 28 #define ANAPARAM_PWR0_MASK (0x07 << ANAPARAM_PWR0_SHIFT) #define ANAPARAM_PWR1_SHIFT 20 #define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT) struct rtl8180_tx_desc { __le32 flags; __le16 rts_duration; __le16 plcp_len; __le32 tx_buf; __le32 frame_len; __le32 next_tx_desc; u8 cw; u8 retry_limit; u8 agc; u8 flags2; u32 reserved[2]; } __packed; struct rtl8180_rx_desc { __le32 flags; __le32 flags2; union { __le32 rx_buf; __le64 tsft; }; } __packed; struct rtl8180_tx_ring { struct rtl8180_tx_desc *desc; dma_addr_t dma; unsigned int idx; unsigned int entries; struct sk_buff_head queue; }; struct rtl8180_vif { struct ieee80211_hw *dev; /* beaconing */ struct delayed_work beacon_work; bool enable_beacon; }; struct rtl8180_priv { /* common between rtl818x drivers */ struct rtl818x_csr __iomem *map; const struct rtl818x_rf_ops *rf; struct ieee80211_vif *vif; /* rtl8180 driver specific */ spinlock_t lock; struct rtl8180_rx_desc *rx_ring; dma_addr_t rx_ring_dma; unsigned int rx_idx; struct sk_buff *rx_buf[32]; struct rtl8180_tx_ring tx_ring[4]; struct ieee80211_channel channels[14]; struct ieee80211_rate rates[12]; struct ieee80211_supported_band band; struct pci_dev *pdev; u32 rx_conf; int r8185; u32 anaparam; u16 rfparam; u8 csthreshold; /* sequence # */ u16 seqno; }; void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam); static inline u8 rtl818x_ioread8(struct rtl8180_priv *priv, u8 __iomem *addr) { return ioread8(addr); } static inline u16 rtl818x_ioread16(struct rtl8180_priv *priv, __le16 __iomem *addr) { return ioread16(addr); } static inline u32 rtl818x_ioread32(struct rtl8180_priv *priv, __le32 __iomem *addr) { return ioread32(addr); } static inline void rtl818x_iowrite8(struct rtl8180_priv *priv, u8 __iomem *addr, u8 val) { iowrite8(val, addr); } static inline void rtl818x_iowrite16(struct rtl8180_priv *priv, __le16 __iomem *addr, u16 val) { iowrite16(val, addr); } static inline void rtl818x_iowrite32(struct rtl8180_priv *priv, __le32 __iomem *addr, u32 val) { iowrite32(val, addr); } #endif /* RTL8180_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/max2820.h0000644000175000017500000000150612026211315024311 0ustar mcgrofmcgrof#ifndef RTL8180_MAX2820_H #define RTL8180_MAX2820_H /* * Radio tuning for Maxim max2820 on RTL8180 * * Copyright 2007 Andrea Merello * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things * * I want to thanks the Authors of such projects and the Ndiswrapper * project Authors. * * A special Big Thanks also is for all people who donated me cards, * making possible the creation of the original rtl8180 driver * from which this code is derived! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define MAXIM_ANTENNA 0xb3 extern const struct rtl818x_rf_ops max2820_rf_ops; #endif /* RTL8180_MAX2820_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/max2820.c0000644000175000017500000001000612026211315024277 0ustar mcgrofmcgrof/* * Radio tuning for Maxim max2820 on RTL8180 * * Copyright 2007 Andrea Merello * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things * * I want to thanks the Authors of such projects and the Ndiswrapper * project Authors. * * A special Big Thanks also is for all people who donated me cards, * making possible the creation of the original rtl8180 driver * from which this code is derived! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "rtl8180.h" #include "max2820.h" static const u32 max2820_chan[] = { 12, /* CH 1 */ 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 84, /* CH 14 */ }; static void write_max2820(struct ieee80211_hw *dev, u8 addr, u32 data) { struct rtl8180_priv *priv = dev->priv; u32 phy_config; phy_config = 0x90 + (data & 0xf); phy_config <<= 16; phy_config += addr; phy_config <<= 8; phy_config += (data >> 4) & 0xff; rtl818x_iowrite32(priv, (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); msleep(1); } static void max2820_write_phy_antenna(struct ieee80211_hw *dev, short chan) { struct rtl8180_priv *priv = dev->priv; u8 ant; ant = MAXIM_ANTENNA; if (priv->rfparam & RF_PARAM_ANTBDEFAULT) ant |= BB_ANTENNA_B; if (chan == 14) ant |= BB_ANTATTEN_CHAN14; rtl8180_write_phy(dev, 0x10, ant); } static u8 max2820_rf_calc_rssi(u8 agc, u8 sq) { bool odd; odd = !!(agc & 1); agc >>= 1; if (odd) agc += 76; else agc += 66; /* TODO: change addends above to avoid mult / div below */ return 65 * agc / 100; } static void max2820_rf_set_channel(struct ieee80211_hw *dev, struct ieee80211_conf *conf) { struct rtl8180_priv *priv = dev->priv; int channel = conf ? ieee80211_frequency_to_channel(conf->channel->center_freq) : 1; unsigned int chan_idx = channel - 1; u32 txpw = priv->channels[chan_idx].hw_value & 0xFF; u32 chan = max2820_chan[chan_idx]; /* While philips SA2400 drive the PA bias from * sa2400, for MAXIM we do this directly from BB */ rtl8180_write_phy(dev, 3, txpw); max2820_write_phy_antenna(dev, channel); write_max2820(dev, 3, chan); } static void max2820_rf_stop(struct ieee80211_hw *dev) { rtl8180_write_phy(dev, 3, 0x8); write_max2820(dev, 1, 0); } static void max2820_rf_init(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; /* MAXIM from netbsd driver */ write_max2820(dev, 0, 0x007); /* test mode as indicated in datasheet */ write_max2820(dev, 1, 0x01e); /* enable register */ write_max2820(dev, 2, 0x001); /* synt register */ max2820_rf_set_channel(dev, NULL); write_max2820(dev, 4, 0x313); /* rx register */ /* PA is driven directly by the BB, we keep the MAXIM bias * at the highest value in case that setting it to lower * values may introduce some further attenuation somewhere.. */ write_max2820(dev, 5, 0x00f); /* baseband configuration */ rtl8180_write_phy(dev, 0, 0x88); /* sys1 */ rtl8180_write_phy(dev, 3, 0x08); /* txagc */ rtl8180_write_phy(dev, 4, 0xf8); /* lnadet */ rtl8180_write_phy(dev, 5, 0x90); /* ifagcinit */ rtl8180_write_phy(dev, 6, 0x1a); /* ifagclimit */ rtl8180_write_phy(dev, 7, 0x64); /* ifagcdet */ max2820_write_phy_antenna(dev, 1); rtl8180_write_phy(dev, 0x11, 0x88); /* trl */ if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & RTL818X_CONFIG2_ANTENNA_DIV) rtl8180_write_phy(dev, 0x12, 0xc7); else rtl8180_write_phy(dev, 0x12, 0x47); rtl8180_write_phy(dev, 0x13, 0x9b); rtl8180_write_phy(dev, 0x19, 0x0); /* CHESTLIM */ rtl8180_write_phy(dev, 0x1a, 0x9f); /* CHSQLIM */ max2820_rf_set_channel(dev, NULL); } const struct rtl818x_rf_ops max2820_rf_ops = { .name = "Maxim", .init = max2820_rf_init, .stop = max2820_rf_stop, .set_chan = max2820_rf_set_channel, .calc_rssi = max2820_rf_calc_rssi, }; compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/grf5101.h0000644000175000017500000000150612026211315024275 0ustar mcgrofmcgrof#ifndef RTL8180_GRF5101_H #define RTL8180_GRF5101_H /* * Radio tuning for GCT GRF5101 on RTL8180 * * Copyright 2007 Andrea Merello * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things * * I want to thanks the Authors of such projects and the Ndiswrapper * project Authors. * * A special Big Thanks also is for all people who donated me cards, * making possible the creation of the original rtl8180 driver * from which this code is derived! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define GRF5101_ANTENNA 0xA3 extern const struct rtl818x_rf_ops grf5101_rf_ops; #endif /* RTL8180_GRF5101_H */ compat-drivers-2012-09-18/drivers/net/wireless/rtl818x/rtl8180/grf5101.c0000644000175000017500000001175512026211315024277 0ustar mcgrofmcgrof /* * Radio tuning for GCT GRF5101 on RTL8180 * * Copyright 2007 Andrea Merello * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things * * I want to thanks the Authors of such projects and the Ndiswrapper * project Authors. * * A special Big Thanks also is for all people who donated me cards, * making possible the creation of the original rtl8180 driver * from which this code is derived! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "rtl8180.h" #include "grf5101.h" static const int grf5101_encode[] = { 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF }; static void write_grf5101(struct ieee80211_hw *dev, u8 addr, u32 data) { struct rtl8180_priv *priv = dev->priv; u32 phy_config; phy_config = grf5101_encode[(data >> 8) & 0xF]; phy_config |= grf5101_encode[(data >> 4) & 0xF] << 4; phy_config |= grf5101_encode[data & 0xF] << 8; phy_config |= grf5101_encode[(addr >> 1) & 0xF] << 12; phy_config |= (addr & 1) << 16; phy_config |= grf5101_encode[(data & 0xf000) >> 12] << 24; /* MAC will bang bits to the chip */ phy_config |= 0x90000000; rtl818x_iowrite32(priv, (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); msleep(3); } static void grf5101_write_phy_antenna(struct ieee80211_hw *dev, short chan) { struct rtl8180_priv *priv = dev->priv; u8 ant = GRF5101_ANTENNA; if (priv->rfparam & RF_PARAM_ANTBDEFAULT) ant |= BB_ANTENNA_B; if (chan == 14) ant |= BB_ANTATTEN_CHAN14; rtl8180_write_phy(dev, 0x10, ant); } static u8 grf5101_rf_calc_rssi(u8 agc, u8 sq) { if (agc > 60) return 65; /* TODO(?): just return agc (or agc + 5) to avoid mult / div */ return 65 * agc / 60; } static void grf5101_rf_set_channel(struct ieee80211_hw *dev, struct ieee80211_conf *conf) { struct rtl8180_priv *priv = dev->priv; int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; u32 chan = channel - 1; /* set TX power */ write_grf5101(dev, 0x15, 0x0); write_grf5101(dev, 0x06, txpw); write_grf5101(dev, 0x15, 0x10); write_grf5101(dev, 0x15, 0x0); /* set frequency */ write_grf5101(dev, 0x07, 0x0); write_grf5101(dev, 0x0B, chan); write_grf5101(dev, 0x07, 0x1000); grf5101_write_phy_antenna(dev, channel); } static void grf5101_rf_stop(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; u32 anaparam; anaparam = priv->anaparam; anaparam &= 0x000fffff; anaparam |= 0x3f900000; rtl8180_set_anaparam(priv, anaparam); write_grf5101(dev, 0x07, 0x0); write_grf5101(dev, 0x1f, 0x45); write_grf5101(dev, 0x1f, 0x5); write_grf5101(dev, 0x00, 0x8e4); } static void grf5101_rf_init(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; rtl8180_set_anaparam(priv, priv->anaparam); write_grf5101(dev, 0x1f, 0x0); write_grf5101(dev, 0x1f, 0x0); write_grf5101(dev, 0x1f, 0x40); write_grf5101(dev, 0x1f, 0x60); write_grf5101(dev, 0x1f, 0x61); write_grf5101(dev, 0x1f, 0x61); write_grf5101(dev, 0x00, 0xae4); write_grf5101(dev, 0x1f, 0x1); write_grf5101(dev, 0x1f, 0x41); write_grf5101(dev, 0x1f, 0x61); write_grf5101(dev, 0x01, 0x1a23); write_grf5101(dev, 0x02, 0x4971); write_grf5101(dev, 0x03, 0x41de); write_grf5101(dev, 0x04, 0x2d80); write_grf5101(dev, 0x05, 0x68ff); /* 0x61ff original value */ write_grf5101(dev, 0x06, 0x0); write_grf5101(dev, 0x07, 0x0); write_grf5101(dev, 0x08, 0x7533); write_grf5101(dev, 0x09, 0xc401); write_grf5101(dev, 0x0a, 0x0); write_grf5101(dev, 0x0c, 0x1c7); write_grf5101(dev, 0x0d, 0x29d3); write_grf5101(dev, 0x0e, 0x2e8); write_grf5101(dev, 0x10, 0x192); write_grf5101(dev, 0x11, 0x248); write_grf5101(dev, 0x12, 0x0); write_grf5101(dev, 0x13, 0x20c4); write_grf5101(dev, 0x14, 0xf4fc); write_grf5101(dev, 0x15, 0x0); write_grf5101(dev, 0x16, 0x1500); write_grf5101(dev, 0x07, 0x1000); /* baseband configuration */ rtl8180_write_phy(dev, 0, 0xa8); rtl8180_write_phy(dev, 3, 0x0); rtl8180_write_phy(dev, 4, 0xc0); rtl8180_write_phy(dev, 5, 0x90); rtl8180_write_phy(dev, 6, 0x1e); rtl8180_write_phy(dev, 7, 0x64); grf5101_write_phy_antenna(dev, 1); rtl8180_write_phy(dev, 0x11, 0x88); if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & RTL818X_CONFIG2_ANTENNA_DIV) rtl8180_write_phy(dev, 0x12, 0xc0); /* enable ant diversity */ else rtl8180_write_phy(dev, 0x12, 0x40); /* disable ant diversity */ rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); rtl8180_write_phy(dev, 0x19, 0x0); rtl8180_write_phy(dev, 0x1a, 0xa0); rtl8180_write_phy(dev, 0x1b, 0x44); } const struct rtl818x_rf_ops grf5101_rf_ops = { .name = "GCT", .init = grf5101_rf_init, .stop = grf5101_rf_stop, .set_chan = grf5101_rf_set_channel, .calc_rssi = grf5101_rf_calc_rssi, }; compat-drivers-2012-09-18/drivers/net/wireless/p54/0000755000175000017500000000000012026211315021071 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/p54/p54usb.c0000644000175000017500000007226312026211315022371 0ustar mcgrofmcgrof /* * Linux device driver for USB based Prism54 * * Copyright (c) 2006, Michael Wu * * Based on the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include "p54.h" #include "lmac.h" #include "p54usb.h" MODULE_AUTHOR("Michael Wu "); MODULE_DESCRIPTION("Prism54 USB wireless driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("prism54usb"); MODULE_FIRMWARE("isl3886usb"); MODULE_FIRMWARE("isl3887usb"); /* * Note: * * Always update our wiki's device list (located at: * http://wireless.kernel.org/en/users/Drivers/p54/devices ), * whenever you add a new device. */ static struct usb_device_id p54u_table[] = { /* Version 1 devices (pci chip + net2280) */ {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */ {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */ {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */ {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */ {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */ {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */ {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */ {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */ {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */ {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ /* Version 2 devices (3887) */ {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */ {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above, * just noting it here for clarity */ {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */ {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */ {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */ {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */ {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */ {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */ {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */ {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */ {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */ {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ {} }; MODULE_DEVICE_TABLE(usb, p54u_table); static const struct { u32 intf; enum p54u_hw_type type; const char *fw; char hw[20]; } p54u_fwlist[__NUM_P54U_HWTYPES] = { { .type = P54U_NET2280, .intf = FW_LM86, .fw = "isl3886usb", .hw = "ISL3886 + net2280", }, { .type = P54U_3887, .intf = FW_LM87, .fw = "isl3887usb", .hw = "ISL3887", }, }; static void p54u_rx_cb(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; struct ieee80211_hw *dev = info->dev; struct p54u_priv *priv = dev->priv; skb_unlink(skb, &priv->rx_queue); if (unlikely(urb->status)) { dev_kfree_skb_irq(skb); return; } skb_put(skb, urb->actual_length); if (priv->hw_type == P54U_NET2280) skb_pull(skb, priv->common.tx_hdr_len); if (priv->common.fw_interface == FW_LM87) { skb_pull(skb, 4); skb_put(skb, 4); } if (p54_rx(dev, skb)) { skb = dev_alloc_skb(priv->common.rx_mtu + 32); if (unlikely(!skb)) { /* TODO check rx queue length and refill *somewhere* */ return; } info = (struct p54u_rx_info *) skb->cb; info->urb = urb; info->dev = dev; urb->transfer_buffer = skb_tail_pointer(skb); urb->context = skb; } else { if (priv->hw_type == P54U_NET2280) skb_push(skb, priv->common.tx_hdr_len); if (priv->common.fw_interface == FW_LM87) { skb_push(skb, 4); skb_put(skb, 4); } skb_reset_tail_pointer(skb); skb_trim(skb, 0); urb->transfer_buffer = skb_tail_pointer(skb); } skb_queue_tail(&priv->rx_queue, skb); usb_anchor_urb(urb, &priv->submitted); if (usb_submit_urb(urb, GFP_ATOMIC)) { skb_unlink(skb, &priv->rx_queue); usb_unanchor_urb(urb); dev_kfree_skb_irq(skb); } } static void p54u_tx_cb(struct urb *urb) { struct sk_buff *skb = urb->context; struct ieee80211_hw *dev = usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); p54_free_skb(dev, skb); } static void p54u_tx_dummy_cb(struct urb *urb) { } static void p54u_free_urbs(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; usb_kill_anchored_urbs(&priv->submitted); } static void p54u_stop(struct ieee80211_hw *dev) { /* * TODO: figure out how to reliably stop the 3887 and net2280 so * the hardware is still usable next time we want to start it. * until then, we just stop listening to the hardware.. */ p54u_free_urbs(dev); } static int p54u_init_urbs(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; struct urb *entry = NULL; struct sk_buff *skb; struct p54u_rx_info *info; int ret = 0; while (skb_queue_len(&priv->rx_queue) < 32) { skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); if (!skb) { ret = -ENOMEM; goto err; } entry = usb_alloc_urb(0, GFP_KERNEL); if (!entry) { ret = -ENOMEM; goto err; } usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), skb_tail_pointer(skb), priv->common.rx_mtu + 32, p54u_rx_cb, skb); info = (struct p54u_rx_info *) skb->cb; info->urb = entry; info->dev = dev; skb_queue_tail(&priv->rx_queue, skb); usb_anchor_urb(entry, &priv->submitted); ret = usb_submit_urb(entry, GFP_KERNEL); if (ret) { skb_unlink(skb, &priv->rx_queue); usb_unanchor_urb(entry); goto err; } usb_free_urb(entry); entry = NULL; } return 0; err: usb_free_urb(entry); kfree_skb(skb); p54u_free_urbs(dev); return ret; } static int p54u_open(struct ieee80211_hw *dev) { /* * TODO: Because we don't know how to reliably stop the 3887 and * the isl3886+net2280, other than brutally cut off all * communications. We have to reinitialize the urbs on every start. */ return p54u_init_urbs(dev); } static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) { u32 chk = 0; length >>= 2; while (length--) { chk ^= le32_to_cpu(*data++); chk = (chk >> 5) ^ (chk << 3); } return cpu_to_le32(chk); } static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54u_priv *priv = dev->priv; struct urb *data_urb; struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); data_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!data_urb) { p54_free_skb(dev, skb); return; } hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? p54u_tx_cb : p54u_tx_dummy_cb, skb); data_urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(data_urb, &priv->submitted); if (usb_submit_urb(data_urb, GFP_ATOMIC)) { usb_unanchor_urb(data_urb); p54_free_skb(dev, skb); } usb_free_urb(data_urb); } static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54u_priv *priv = dev->priv; struct urb *int_urb = NULL, *data_urb = NULL; struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); struct net2280_reg_write *reg = NULL; int err = -ENOMEM; reg = kmalloc(sizeof(*reg), GFP_ATOMIC); if (!reg) goto out; int_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!int_urb) goto out; data_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!data_urb) goto out; reg->port = cpu_to_le16(NET2280_DEV_U32); reg->addr = cpu_to_le32(P54U_DEV_BASE); reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); memset(hdr, 0, sizeof(*hdr)); hdr->len = cpu_to_le16(skb->len); hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; usb_fill_bulk_urb(int_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), p54u_tx_dummy_cb, dev); /* * URB_FREE_BUFFER triggers a code path in the USB subsystem that will * free what is inside the transfer_buffer after the last reference to * the int_urb is dropped. */ int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET; reg = NULL; usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? p54u_tx_cb : p54u_tx_dummy_cb, skb); data_urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(int_urb, &priv->submitted); err = usb_submit_urb(int_urb, GFP_ATOMIC); if (err) { usb_unanchor_urb(int_urb); goto out; } usb_anchor_urb(data_urb, &priv->submitted); err = usb_submit_urb(data_urb, GFP_ATOMIC); if (err) { usb_unanchor_urb(data_urb); goto out; } out: usb_free_urb(int_urb); usb_free_urb(data_urb); if (err) { kfree(reg); p54_free_skb(dev, skb); } } static int p54u_write(struct p54u_priv *priv, struct net2280_reg_write *buf, enum net2280_op_type type, __le32 addr, __le32 val) { unsigned int ep; int alen; if (type & 0x0800) ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); else ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); buf->port = cpu_to_le16(type); buf->addr = addr; buf->val = val; return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000); } static int p54u_read(struct p54u_priv *priv, void *buf, enum net2280_op_type type, __le32 addr, __le32 *val) { struct net2280_reg_read *read = buf; __le32 *reg = buf; unsigned int ep; int alen, err; if (type & 0x0800) ep = P54U_PIPE_DEV; else ep = P54U_PIPE_BRG; read->port = cpu_to_le16(type); read->addr = addr; err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), read, sizeof(*read), &alen, 1000); if (err) return err; err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), reg, sizeof(*reg), &alen, 1000); if (err) return err; *val = *reg; return 0; } static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, void *data, size_t len) { int alen; return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), data, len, &alen, 2000); } static int p54u_device_reset(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING); if (lock) { ret = usb_lock_device_for_reset(priv->udev, priv->intf); if (ret < 0) { dev_err(&priv->udev->dev, "(p54usb) unable to lock " "device for reset (%d)!\n", ret); return ret; } } ret = usb_reset_device(priv->udev); if (lock) usb_unlock_device(priv->udev); if (ret) dev_err(&priv->udev->dev, "(p54usb) unable to reset " "device (%d)!\n", ret); return ret; } static const char p54u_romboot_3887[] = "~~~~"; static int p54u_firmware_reset_3887(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; u8 *buf; int ret; buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL); if (!buf) return -ENOMEM; ret = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 4); kfree(buf); if (ret) dev_err(&priv->udev->dev, "(p54usb) unable to jump to " "boot ROM (%d)!\n", ret); return ret; } static const char p54u_firmware_upload_3887[] = "<\r"; static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; int err, alen; u8 carry = 0; u8 *buf, *tmp; const u8 *data; unsigned int left, remains, block_size; struct x2_header *hdr; unsigned long timeout; err = p54u_firmware_reset_3887(dev); if (err) return err; tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); if (!buf) { dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware" "upload buffer!\n"); return -ENOMEM; } left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size); strcpy(buf, p54u_firmware_upload_3887); left -= strlen(p54u_firmware_upload_3887); tmp += strlen(p54u_firmware_upload_3887); data = priv->fw->data; remains = priv->fw->size; hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887)); memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); hdr->fw_length = cpu_to_le32(priv->fw->size); hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, sizeof(u32)*2)); left -= sizeof(*hdr); tmp += sizeof(*hdr); while (remains) { while (left--) { if (carry) { *tmp++ = carry; carry = 0; remains--; continue; } switch (*data) { case '~': *tmp++ = '}'; carry = '^'; break; case '}': *tmp++ = '}'; carry = ']'; break; default: *tmp++ = *data; remains--; break; } data++; } err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); if (err) { dev_err(&priv->udev->dev, "(p54usb) firmware " "upload failed!\n"); goto err_upload_failed; } tmp = buf; left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); } *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data, priv->fw->size)); err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); if (err) { dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); goto err_upload_failed; } timeout = jiffies + msecs_to_jiffies(1000); while (!(err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { if (alen > 2 && !memcmp(buf, "OK", 2)) break; if (alen > 5 && !memcmp(buf, "ERROR", 5)) { err = -EINVAL; break; } if (time_after(jiffies, timeout)) { dev_err(&priv->udev->dev, "(p54usb) firmware boot " "timed out!\n"); err = -ETIMEDOUT; break; } } if (err) { dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); goto err_upload_failed; } buf[0] = 'g'; buf[1] = '\r'; err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); if (err) { dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n"); goto err_upload_failed; } timeout = jiffies + msecs_to_jiffies(1000); while (!(err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { if (alen > 0 && buf[0] == 'g') break; if (time_after(jiffies, timeout)) { err = -ETIMEDOUT; break; } } if (err) goto err_upload_failed; err_upload_failed: kfree(buf); return err; } static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; int err, alen; void *buf; __le32 reg; unsigned int remains, offset; const u8 *data; buf = kmalloc(512, GFP_KERNEL); if (!buf) { dev_err(&priv->udev->dev, "(p54usb) firmware buffer " "alloc failed!\n"); return -ENOMEM; } #define P54U_WRITE(type, addr, data) \ do {\ err = p54u_write(priv, buf, type,\ cpu_to_le32((u32)(unsigned long)addr), data);\ if (err) \ goto fail;\ } while (0) #define P54U_READ(type, addr) \ do {\ err = p54u_read(priv, buf, type,\ cpu_to_le32((u32)(unsigned long)addr), ®);\ if (err)\ goto fail;\ } while (0) /* power down net2280 bridge */ P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL); reg |= cpu_to_le32(P54U_BRG_POWER_DOWN); reg &= cpu_to_le32(~P54U_BRG_POWER_UP); P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); mdelay(100); /* power up bridge */ reg |= cpu_to_le32(P54U_BRG_POWER_UP); reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN); P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); mdelay(100); P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT, cpu_to_le32(NET2280_CLK_30Mhz | NET2280_PCI_ENABLE | NET2280_PCI_SOFT_RESET)); mdelay(20); P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND, cpu_to_le32(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)); P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0, cpu_to_le32(NET2280_BASE)); P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS); reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT); P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg); // TODO: we really need this? P54U_READ(NET2280_BRG_U32, NET2280_RELNUM); P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP, cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP, cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2, cpu_to_le32(NET2280_BASE2)); /* finally done setting up the bridge */ P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND, cpu_to_le32(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)); P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0); P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0, cpu_to_le32(P54U_DEV_BASE)); P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); /* do romboot */ P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0); P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); mdelay(20); reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); mdelay(20); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); mdelay(100); P54U_READ(NET2280_DEV_U32, &devreg->int_ident); P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); /* finally, we can upload firmware now! */ remains = priv->fw->size; data = priv->fw->data; offset = ISL38XX_DEV_FIRMWARE_ADDR; while (remains) { unsigned int block_len = min(remains, (unsigned int)512); memcpy(buf, data, block_len); err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); if (err) { dev_err(&priv->udev->dev, "(p54usb) firmware block " "upload failed\n"); goto fail; } P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base, cpu_to_le32(0xc0000f00)); P54U_WRITE(NET2280_DEV_U32, 0x0020 | (unsigned long)&devreg->direct_mem_win, 0); P54U_WRITE(NET2280_DEV_U32, 0x0020 | (unsigned long)&devreg->direct_mem_win, cpu_to_le32(1)); P54U_WRITE(NET2280_DEV_U32, 0x0024 | (unsigned long)&devreg->direct_mem_win, cpu_to_le32(block_len)); P54U_WRITE(NET2280_DEV_U32, 0x0028 | (unsigned long)&devreg->direct_mem_win, cpu_to_le32(offset)); P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr, cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR)); P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len, cpu_to_le32(block_len >> 2)); P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl, cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER)); mdelay(10); P54U_READ(NET2280_DEV_U32, 0x002C | (unsigned long)&devreg->direct_mem_win); if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { dev_err(&priv->udev->dev, "(p54usb) firmware DMA " "transfer failed\n"); goto fail; } P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT, cpu_to_le32(NET2280_FIFO_FLUSH)); remains -= block_len; data += block_len; offset += block_len; } /* do ramboot */ P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); mdelay(20); reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); mdelay(100); P54U_READ(NET2280_DEV_U32, &devreg->int_ident); P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); /* start up the firmware */ P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT)); P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE | NET2280_USB_INTERRUPT_ENABLE)); P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); err = usb_interrupt_msg(priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT), buf, sizeof(__le32), &alen, 1000); if (err || alen != sizeof(__le32)) goto fail; P54U_READ(NET2280_DEV_U32, &devreg->int_ident); P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))) err = -EINVAL; P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); #undef P54U_WRITE #undef P54U_READ fail: kfree(buf); return err; } static int p54_find_type(struct p54u_priv *priv) { int i; for (i = 0; i < __NUM_P54U_HWTYPES; i++) if (p54u_fwlist[i].type == priv->hw_type) break; if (i == __NUM_P54U_HWTYPES) return -EOPNOTSUPP; return i; } static int p54u_start_ops(struct p54u_priv *priv) { struct ieee80211_hw *dev = priv->common.hw; int ret; ret = p54_parse_firmware(dev, priv->fw); if (ret) goto err_out; ret = p54_find_type(priv); if (ret < 0) goto err_out; if (priv->common.fw_interface != p54u_fwlist[ret].intf) { dev_err(&priv->udev->dev, "wrong firmware, please get " "a firmware for \"%s\" and try again.\n", p54u_fwlist[ret].hw); ret = -ENODEV; goto err_out; } ret = priv->upload_fw(dev); if (ret) goto err_out; ret = p54u_open(dev); if (ret) goto err_out; ret = p54_read_eeprom(dev); if (ret) goto err_stop; p54u_stop(dev); ret = p54_register_common(dev, &priv->udev->dev); if (ret) goto err_stop; return 0; err_stop: p54u_stop(dev); err_out: /* * p54u_disconnect will do the rest of the * cleanup */ return ret; } static void p54u_load_firmware_cb(const struct firmware *firmware, void *context) { struct p54u_priv *priv = context; struct usb_device *udev = priv->udev; int err; complete(&priv->fw_wait_load); if (firmware) { priv->fw = firmware; err = p54u_start_ops(priv); } else { err = -ENOENT; dev_err(&udev->dev, "Firmware not found.\n"); } if (err) { struct device *parent = priv->udev->dev.parent; dev_err(&udev->dev, "failed to initialize device (%d)\n", err); if (parent) device_lock(parent); device_release_driver(&udev->dev); /* * At this point p54u_disconnect has already freed * the "priv" context. Do not use it anymore! */ priv = NULL; if (parent) device_unlock(parent); } usb_put_dev(udev); } static int p54u_load_firmware(struct ieee80211_hw *dev, struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct p54u_priv *priv = dev->priv; struct device *device = &udev->dev; int err, i; BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); init_completion(&priv->fw_wait_load); i = p54_find_type(priv); if (i < 0) return i; dev_info(&priv->udev->dev, "Loading firmware file %s\n", p54u_fwlist[i].fw); usb_get_dev(udev); err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw, device, GFP_KERNEL, priv, p54u_load_firmware_cb); if (err) { dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " "(%d)!\n", p54u_fwlist[i].fw, err); } return err; } static int __devinit p54u_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct ieee80211_hw *dev; struct p54u_priv *priv; int err; unsigned int i, recognized_pipes; dev = p54_init_common(sizeof(*priv)); if (!dev) { dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n"); return -ENOMEM; } priv = dev->priv; priv->hw_type = P54U_INVALID_HW; SET_IEEE80211_DEV(dev, &intf->dev); usb_set_intfdata(intf, dev); priv->udev = udev; priv->intf = intf; skb_queue_head_init(&priv->rx_queue); init_usb_anchor(&priv->submitted); usb_get_dev(udev); /* really lazy and simple way of figuring out if we're a 3887 */ /* TODO: should just stick the identification in the device table */ i = intf->altsetting->desc.bNumEndpoints; recognized_pipes = 0; while (i--) { switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) { case P54U_PIPE_DATA: case P54U_PIPE_MGMT: case P54U_PIPE_BRG: case P54U_PIPE_DEV: case P54U_PIPE_DATA | USB_DIR_IN: case P54U_PIPE_MGMT | USB_DIR_IN: case P54U_PIPE_BRG | USB_DIR_IN: case P54U_PIPE_DEV | USB_DIR_IN: case P54U_PIPE_INT | USB_DIR_IN: recognized_pipes++; } } priv->common.open = p54u_open; priv->common.stop = p54u_stop; if (recognized_pipes < P54U_PIPE_NUMBER) { #ifdef CONFIG_PM /* ISL3887 needs a full reset on resume */ udev->reset_resume = 1; #endif /* CONFIG_PM */ err = p54u_device_reset(dev); priv->hw_type = P54U_3887; dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr); priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr); priv->common.tx = p54u_tx_lm87; priv->upload_fw = p54u_upload_firmware_3887; } else { priv->hw_type = P54U_NET2280; dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); priv->common.tx = p54u_tx_net2280; priv->upload_fw = p54u_upload_firmware_net2280; } err = p54u_load_firmware(dev, intf); return err; } static void __devexit p54u_disconnect(struct usb_interface *intf) { struct ieee80211_hw *dev = usb_get_intfdata(intf); struct p54u_priv *priv; if (!dev) return; priv = dev->priv; wait_for_completion(&priv->fw_wait_load); p54_unregister_common(dev); usb_put_dev(interface_to_usbdev(intf)); release_firmware(priv->fw); p54_free_common(dev); } static int p54u_pre_reset(struct usb_interface *intf) { struct ieee80211_hw *dev = usb_get_intfdata(intf); if (!dev) return -ENODEV; p54u_stop(dev); return 0; } static int p54u_resume(struct usb_interface *intf) { struct ieee80211_hw *dev = usb_get_intfdata(intf); struct p54u_priv *priv; if (!dev) return -ENODEV; priv = dev->priv; if (unlikely(!(priv->upload_fw && priv->fw))) return 0; return priv->upload_fw(dev); } static int p54u_post_reset(struct usb_interface *intf) { struct ieee80211_hw *dev = usb_get_intfdata(intf); struct p54u_priv *priv; int err; err = p54u_resume(intf); if (err) return err; /* reinitialize old device state */ priv = dev->priv; if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED) ieee80211_restart_hw(dev); return 0; } #ifdef CONFIG_PM static int p54u_suspend(struct usb_interface *intf, pm_message_t message) { return p54u_pre_reset(intf); } #endif /* CONFIG_PM */ static struct usb_driver p54u_driver = { .name = "p54usb", .id_table = p54u_table, .probe = p54u_probe, .disconnect = __devexit_p(p54u_disconnect), .pre_reset = p54u_pre_reset, .post_reset = p54u_post_reset, #ifdef CONFIG_PM .suspend = p54u_suspend, .resume = p54u_resume, .reset_resume = p54u_resume, #endif /* CONFIG_PM */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) .soft_unbind = 1, #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(p54u_driver); compat-drivers-2012-09-18/drivers/net/wireless/p54/main.c0000644000175000017500000005231612026211315022170 0ustar mcgrofmcgrof/* * mac80211 glue code for mac80211 Prism54 drivers * * Copyright (c) 2006, Michael Wu * Copyright (c) 2007-2009, Christian Lamparter * Copyright 2008, Johannes Berg * * Based on: * - the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * - stlc45xx driver * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "p54.h" #include "lmac.h" static bool modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); MODULE_AUTHOR("Michael Wu "); MODULE_DESCRIPTION("Softmac Prism54 common code"); MODULE_LICENSE("GPL"); MODULE_ALIAS("prism54common"); static int p54_sta_add_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct p54_common *priv = hw->priv; /* * Notify the firmware that we don't want or we don't * need to buffer frames for this station anymore. */ p54_sta_unlock(priv, sta->addr); return 0; } static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) { struct p54_common *priv = dev->priv; switch (notify_cmd) { case STA_NOTIFY_AWAKE: /* update the firmware's filter table */ p54_sta_unlock(priv, sta->addr); break; default: break; } } static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, bool set) { struct p54_common *priv = dev->priv; return p54_update_beacon_tim(priv, sta->aid, set); } u8 *p54_find_ie(struct sk_buff *skb, u8 ie) { struct ieee80211_mgmt *mgmt = (void *)skb->data; u8 *pos, *end; if (skb->len <= sizeof(mgmt)) return NULL; pos = (u8 *)mgmt->u.beacon.variable; end = skb->data + skb->len; while (pos < end) { if (pos + 2 + pos[1] > end) return NULL; if (pos[0] == ie) return pos; pos += 2 + pos[1]; } return NULL; } static int p54_beacon_format_ie_tim(struct sk_buff *skb) { /* * the good excuse for this mess is ... the firmware. * The dummy TIM MUST be at the end of the beacon frame, * because it'll be overwritten! */ u8 *tim; u8 dtim_len; u8 dtim_period; u8 *next; tim = p54_find_ie(skb, WLAN_EID_TIM); if (!tim) return 0; dtim_len = tim[1]; dtim_period = tim[3]; next = tim + 2 + dtim_len; if (dtim_len < 3) return -EINVAL; memmove(tim, next, skb_tail_pointer(skb) - next); tim = skb_tail_pointer(skb) - (dtim_len + 2); /* add the dummy at the end */ tim[0] = WLAN_EID_TIM; tim[1] = 3; tim[2] = 0; tim[3] = dtim_period; tim[4] = 0; if (dtim_len > 3) skb_trim(skb, skb->len - (dtim_len - 3)); return 0; } static int p54_beacon_update(struct p54_common *priv, struct ieee80211_vif *vif) { struct ieee80211_tx_control control = { }; struct sk_buff *beacon; int ret; beacon = ieee80211_beacon_get(priv->hw, vif); if (!beacon) return -ENOMEM; ret = p54_beacon_format_ie_tim(beacon); if (ret) return ret; /* * During operation, the firmware takes care of beaconing. * The driver only needs to upload a new beacon template, once * the template was changed by the stack or userspace. * * LMAC API 3.2.2 also specifies that the driver does not need * to cancel the old beacon template by hand, instead the firmware * will release the previous one through the feedback mechanism. */ p54_tx_80211(priv->hw, &control, beacon); priv->tsf_high32 = 0; priv->tsf_low32 = 0; return 0; } static int p54_start(struct ieee80211_hw *dev) { struct p54_common *priv = dev->priv; int err; mutex_lock(&priv->conf_mutex); err = priv->open(dev); if (err) goto out; P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47); P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94); P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0); P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0); err = p54_set_edcf(priv); if (err) goto out; memset(priv->bssid, ~0, ETH_ALEN); priv->mode = NL80211_IFTYPE_MONITOR; err = p54_setup_mac(priv); if (err) { priv->mode = NL80211_IFTYPE_UNSPECIFIED; goto out; } ieee80211_queue_delayed_work(dev, &priv->work, 0); priv->softled_state = 0; err = p54_set_leds(priv); out: mutex_unlock(&priv->conf_mutex); return err; } static void p54_stop(struct ieee80211_hw *dev) { struct p54_common *priv = dev->priv; int i; priv->mode = NL80211_IFTYPE_UNSPECIFIED; priv->softled_state = 0; cancel_delayed_work_sync(&priv->work); mutex_lock(&priv->conf_mutex); p54_set_leds(priv); priv->stop(dev); skb_queue_purge(&priv->tx_pending); skb_queue_purge(&priv->tx_queue); for (i = 0; i < P54_QUEUE_NUM; i++) { priv->tx_stats[i].count = 0; priv->tx_stats[i].len = 0; } priv->beacon_req_id = cpu_to_le32(0); priv->tsf_high32 = priv->tsf_low32 = 0; mutex_unlock(&priv->conf_mutex); } static int p54_add_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct p54_common *priv = dev->priv; int err; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; mutex_lock(&priv->conf_mutex); if (priv->mode != NL80211_IFTYPE_MONITOR) { mutex_unlock(&priv->conf_mutex); return -EOPNOTSUPP; } priv->vif = vif; switch (vif->type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: priv->mode = vif->type; break; default: mutex_unlock(&priv->conf_mutex); return -EOPNOTSUPP; } memcpy(priv->mac_addr, vif->addr, ETH_ALEN); err = p54_setup_mac(priv); mutex_unlock(&priv->conf_mutex); return err; } static void p54_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct p54_common *priv = dev->priv; mutex_lock(&priv->conf_mutex); priv->vif = NULL; /* * LMAC API 3.2.2 states that any active beacon template must be * canceled by the driver before attempting a mode transition. */ if (le32_to_cpu(priv->beacon_req_id) != 0) { p54_tx_cancel(priv, priv->beacon_req_id); wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ); } priv->mode = NL80211_IFTYPE_MONITOR; memset(priv->mac_addr, 0, ETH_ALEN); memset(priv->bssid, 0, ETH_ALEN); p54_setup_mac(priv); mutex_unlock(&priv->conf_mutex); } static int p54_wait_for_stats(struct ieee80211_hw *dev) { struct p54_common *priv = dev->priv; int ret; priv->update_stats = true; ret = p54_fetch_statistics(priv); if (ret) return ret; ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ); if (ret == 0) return -ETIMEDOUT; return 0; } static void p54_reset_stats(struct p54_common *priv) { struct ieee80211_channel *chan = priv->curchan; if (chan) { struct survey_info *info = &priv->survey[chan->hw_value]; /* only reset channel statistics, don't touch .filled, etc. */ info->channel_time = 0; info->channel_time_busy = 0; info->channel_time_tx = 0; } priv->update_stats = true; priv->survey_raw.active = 0; priv->survey_raw.cca = 0; priv->survey_raw.tx = 0; } static int p54_config(struct ieee80211_hw *dev, u32 changed) { int ret = 0; struct p54_common *priv = dev->priv; struct ieee80211_conf *conf = &dev->conf; mutex_lock(&priv->conf_mutex); if (changed & IEEE80211_CONF_CHANGE_POWER) priv->output_power = conf->power_level << 2; if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *oldchan; WARN_ON(p54_wait_for_stats(dev)); oldchan = priv->curchan; priv->curchan = NULL; ret = p54_scan(priv, P54_SCAN_EXIT, 0); if (ret) { priv->curchan = oldchan; goto out; } /* * TODO: Use the LM_SCAN_TRAP to determine the current * operating channel. */ priv->curchan = priv->hw->conf.channel; p54_reset_stats(priv); WARN_ON(p54_fetch_statistics(priv)); } if (changed & IEEE80211_CONF_CHANGE_PS) { WARN_ON(p54_wait_for_stats(dev)); ret = p54_set_ps(priv); if (ret) goto out; WARN_ON(p54_wait_for_stats(dev)); } if (changed & IEEE80211_CONF_CHANGE_IDLE) { WARN_ON(p54_wait_for_stats(dev)); ret = p54_setup_mac(priv); if (ret) goto out; WARN_ON(p54_wait_for_stats(dev)); } out: mutex_unlock(&priv->conf_mutex); return ret; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 p54_prepare_multicast(struct ieee80211_hw *dev, struct netdev_hw_addr_list *mc_list) #else static u64 p54_prepare_multicast(struct ieee80211_hw *dev, int mc_count, struct dev_addr_list *ha) #endif { struct p54_common *priv = dev->priv; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; #endif int i; BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) != ARRAY_SIZE(((struct p54_group_address_table *)NULL)->mac_list)); /* * The first entry is reserved for the global broadcast MAC. * Otherwise the firmware will drop it and ARP will no longer work. */ i = 1; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i; netdev_hw_addr_list_for_each(ha, mc_list) { memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN); #else priv->mc_maclist_num = mc_count + i; while (i <= mc_count) { if (!ha) break; memcpy(&priv->mc_maclist[i], ha->dmi_addr, ETH_ALEN); #endif i++; if (i >= ARRAY_SIZE(priv->mc_maclist)) break; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) ha = ha->next; #endif } return 1; /* update */ } static void p54_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct p54_common *priv = dev->priv; *total_flags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_OTHER_BSS; priv->filter_flags = *total_flags; if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) p54_setup_mac(priv); if (changed_flags & FIF_ALLMULTI || multicast) p54_set_groupfilter(priv); } static int p54_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct p54_common *priv = dev->priv; int ret; mutex_lock(&priv->conf_mutex); if (queue < dev->queues) { P54_SET_QUEUE(priv->qos_params[queue], params->aifs, params->cw_min, params->cw_max, params->txop); ret = p54_set_edcf(priv); } else ret = -EINVAL; mutex_unlock(&priv->conf_mutex); return ret; } static void p54_work(struct work_struct *work) { struct p54_common *priv = container_of(work, struct p54_common, work.work); if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) return ; /* * TODO: walk through tx_queue and do the following tasks * 1. initiate bursts. * 2. cancel stuck frames / reset the device if necessary. */ mutex_lock(&priv->conf_mutex); WARN_ON_ONCE(p54_fetch_statistics(priv)); mutex_unlock(&priv->conf_mutex); } static int p54_get_stats(struct ieee80211_hw *dev, struct ieee80211_low_level_stats *stats) { struct p54_common *priv = dev->priv; memcpy(stats, &priv->stats, sizeof(*stats)); return 0; } static void p54_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct p54_common *priv = dev->priv; mutex_lock(&priv->conf_mutex); if (changed & BSS_CHANGED_BSSID) { memcpy(priv->bssid, info->bssid, ETH_ALEN); p54_setup_mac(priv); } if (changed & BSS_CHANGED_BEACON) { p54_scan(priv, P54_SCAN_EXIT, 0); p54_setup_mac(priv); p54_beacon_update(priv, vif); p54_set_edcf(priv); } if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) { priv->use_short_slot = info->use_short_slot; p54_set_edcf(priv); } if (changed & BSS_CHANGED_BASIC_RATES) { if (dev->conf.channel->band == IEEE80211_BAND_5GHZ) priv->basic_rate_mask = (info->basic_rates << 4); else priv->basic_rate_mask = info->basic_rates; p54_setup_mac(priv); if (priv->fw_var >= 0x500) p54_scan(priv, P54_SCAN_EXIT, 0); } if (changed & BSS_CHANGED_ASSOC) { if (info->assoc) { priv->aid = info->aid; priv->wakeup_timer = info->beacon_int * info->dtim_period * 5; p54_setup_mac(priv); } else { priv->wakeup_timer = 500; priv->aid = 0; } } mutex_unlock(&priv->conf_mutex); } static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct p54_common *priv = dev->priv; int slot, ret = 0; u8 algo = 0; u8 *addr = NULL; if (modparam_nohwcrypt) return -EOPNOTSUPP; mutex_lock(&priv->conf_mutex); if (cmd == SET_KEY) { switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | BR_DESC_PRIV_CAP_TKIP))) { ret = -EOPNOTSUPP; goto out_unlock; } key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; algo = P54_CRYPTO_TKIPMICHAEL; break; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) { ret = -EOPNOTSUPP; goto out_unlock; } key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; algo = P54_CRYPTO_WEP; break; case WLAN_CIPHER_SUITE_CCMP: if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) { ret = -EOPNOTSUPP; goto out_unlock; } key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; algo = P54_CRYPTO_AESCCMP; break; default: ret = -EOPNOTSUPP; goto out_unlock; } slot = bitmap_find_free_region(priv->used_rxkeys, priv->rx_keycache_size, 0); if (slot < 0) { /* * The device supports the chosen algorithm, but the * firmware does not provide enough key slots to store * all of them. * But encryption offload for outgoing frames is always * possible, so we just pretend that the upload was * successful and do the decryption in software. */ /* mark the key as invalid. */ key->hw_key_idx = 0xff; goto out_unlock; } } else { slot = key->hw_key_idx; if (slot == 0xff) { /* This key was not uploaded into the rx key cache. */ goto out_unlock; } bitmap_release_region(priv->used_rxkeys, slot, 0); algo = 0; } if (sta) addr = sta->addr; ret = p54_upload_key(priv, algo, slot, key->keyidx, key->keylen, addr, key->key); if (ret) { bitmap_release_region(priv->used_rxkeys, slot, 0); ret = -EOPNOTSUPP; goto out_unlock; } key->hw_key_idx = slot; out_unlock: mutex_unlock(&priv->conf_mutex); return ret; } static int p54_get_survey(struct ieee80211_hw *dev, int idx, struct survey_info *survey) { struct p54_common *priv = dev->priv; struct ieee80211_channel *chan; int err, tries; bool in_use = false; if (idx >= priv->chan_num) return -ENOENT; #define MAX_TRIES 1 for (tries = 0; tries < MAX_TRIES; tries++) { chan = priv->curchan; if (chan && chan->hw_value == idx) { mutex_lock(&priv->conf_mutex); err = p54_wait_for_stats(dev); mutex_unlock(&priv->conf_mutex); if (err) return err; in_use = true; } memcpy(survey, &priv->survey[idx], sizeof(*survey)); if (in_use) { /* test if the reported statistics are valid. */ if (survey->channel_time != 0) { survey->filled |= SURVEY_INFO_IN_USE; } else { /* * hw/fw has not accumulated enough sample sets. * Wait for 100ms, this ought to be enough to * to get at least one non-null set of channel * usage statistics. */ msleep(100); continue; } } return 0; } return -ETIMEDOUT; #undef MAX_TRIES } static unsigned int p54_flush_count(struct p54_common *priv) { unsigned int total = 0, i; BUILD_BUG_ON(P54_QUEUE_NUM > ARRAY_SIZE(priv->tx_stats)); /* * Because the firmware has the sole control over any frames * in the P54_QUEUE_BEACON or P54_QUEUE_SCAN queues, they * don't really count as pending or active. */ for (i = P54_QUEUE_MGMT; i < P54_QUEUE_NUM; i++) total += priv->tx_stats[i].len; return total; } static void p54_flush(struct ieee80211_hw *dev, bool drop) { struct p54_common *priv = dev->priv; unsigned int total, i; /* * Currently, it wouldn't really matter if we wait for one second * or 15 minutes. But once someone gets around and completes the * TODOs [ancel stuck frames / reset device] in p54_work, it will * suddenly make sense to wait that long. */ i = P54_STATISTICS_UPDATE * 2 / 20; /* * In this case no locking is required because as we speak the * queues have already been stopped and no new frames can sneak * up from behind. */ while ((total = p54_flush_count(priv) && i--)) { /* waste time */ msleep(20); } WARN(total, "tx flush timeout, unresponsive firmware"); } static void p54_set_coverage_class(struct ieee80211_hw *dev, u8 coverage_class) { struct p54_common *priv = dev->priv; mutex_lock(&priv->conf_mutex); /* support all coverage class values as in 802.11-2007 Table 7-27 */ priv->coverage_class = clamp_t(u8, coverage_class, 0, 31); p54_set_edcf(priv); mutex_unlock(&priv->conf_mutex); } static const struct ieee80211_ops p54_ops = { .tx = p54_tx_80211, .start = p54_start, .stop = p54_stop, .add_interface = p54_add_interface, .remove_interface = p54_remove_interface, .set_tim = p54_set_tim, .sta_notify = p54_sta_notify, .sta_add = p54_sta_add_remove, .sta_remove = p54_sta_add_remove, .set_key = p54_set_key, .config = p54_config, .flush = p54_flush, .bss_info_changed = p54_bss_info_changed, .prepare_multicast = p54_prepare_multicast, .configure_filter = p54_configure_filter, .conf_tx = p54_conf_tx, .get_stats = p54_get_stats, .get_survey = p54_get_survey, .set_coverage_class = p54_set_coverage_class, }; struct ieee80211_hw *p54_init_common(size_t priv_data_len) { struct ieee80211_hw *dev; struct p54_common *priv; dev = ieee80211_alloc_hw(priv_data_len, &p54_ops); if (!dev) return NULL; priv = dev->priv; priv->hw = dev; priv->mode = NL80211_IFTYPE_UNSPECIFIED; priv->basic_rate_mask = 0x15f; spin_lock_init(&priv->tx_stats_lock); skb_queue_head_init(&priv->tx_queue); skb_queue_head_init(&priv->tx_pending); dev->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_REPORTS_TX_ACK_STATUS; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); dev->channel_change_time = 1000; /* TODO: find actual value */ priv->beacon_req_id = cpu_to_le32(0); priv->tx_stats[P54_QUEUE_BEACON].limit = 1; priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; priv->tx_stats[P54_QUEUE_MGMT].limit = 3; priv->tx_stats[P54_QUEUE_CAB].limit = 3; priv->tx_stats[P54_QUEUE_DATA].limit = 5; dev->queues = 1; priv->noise = -94; /* * We support at most 8 tries no matter which rate they're at, * we cannot support max_rates * max_rate_tries as we set it * here, but setting it correctly to 4/2 or so would limit us * artificially if the RC algorithm wants just two rates, so * let's say 4/7, we'll redistribute it at TX time, see the * comments there. */ dev->max_rates = 4; dev->max_rate_tries = 7; dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 + sizeof(struct p54_tx_data); /* * For now, disable PS by default because it affects * link stability significantly. */ dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; mutex_init(&priv->conf_mutex); mutex_init(&priv->eeprom_mutex); init_completion(&priv->stat_comp); init_completion(&priv->eeprom_comp); init_completion(&priv->beacon_comp); INIT_DELAYED_WORK(&priv->work, p54_work); memset(&priv->mc_maclist[0], ~0, ETH_ALEN); priv->curchan = NULL; p54_reset_stats(priv); return dev; } EXPORT_SYMBOL_GPL(p54_init_common); int p54_register_common(struct ieee80211_hw *dev, struct device *pdev) { struct p54_common __maybe_unused *priv = dev->priv; int err; err = ieee80211_register_hw(dev); if (err) { dev_err(pdev, "Cannot register device (%d).\n", err); return err; } priv->registered = true; #ifdef CONFIG_P54_LEDS err = p54_init_leds(priv); if (err) { p54_unregister_common(dev); return err; } #endif /* CONFIG_P54_LEDS */ dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy)); return 0; } EXPORT_SYMBOL_GPL(p54_register_common); void p54_free_common(struct ieee80211_hw *dev) { struct p54_common *priv = dev->priv; unsigned int i; for (i = 0; i < IEEE80211_NUM_BANDS; i++) kfree(priv->band_table[i]); kfree(priv->iq_autocal); kfree(priv->output_limit); kfree(priv->curve_data); kfree(priv->rssi_db); kfree(priv->used_rxkeys); kfree(priv->survey); priv->iq_autocal = NULL; priv->output_limit = NULL; priv->curve_data = NULL; priv->rssi_db = NULL; priv->used_rxkeys = NULL; priv->survey = NULL; ieee80211_free_hw(dev); } EXPORT_SYMBOL_GPL(p54_free_common); void p54_unregister_common(struct ieee80211_hw *dev) { struct p54_common *priv = dev->priv; #ifdef CONFIG_P54_LEDS p54_unregister_leds(priv); #endif /* CONFIG_P54_LEDS */ if (priv->registered) { priv->registered = false; ieee80211_unregister_hw(dev); } mutex_destroy(&priv->conf_mutex); mutex_destroy(&priv->eeprom_mutex); } EXPORT_SYMBOL_GPL(p54_unregister_common); compat-drivers-2012-09-18/drivers/net/wireless/p54/p54pci.c0000644000175000017500000004151512026211315022347 0ustar mcgrofmcgrof /* * Linux device driver for PCI based Prism54 * * Copyright (c) 2006, Michael Wu * Copyright (c) 2008, Christian Lamparter * * Based on the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "p54.h" #include "lmac.h" #include "p54pci.h" MODULE_AUTHOR("Michael Wu "); MODULE_DESCRIPTION("Prism54 PCI wireless driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("prism54pci"); MODULE_FIRMWARE("isl3886pci"); static DEFINE_PCI_DEVICE_TABLE(p54p_table) = { /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */ { PCI_DEVICE(0x1260, 0x3890) }, /* 3COM 3CRWE154G72 Wireless LAN adapter */ { PCI_DEVICE(0x10b7, 0x6001) }, /* Intersil PRISM Indigo Wireless LAN adapter */ { PCI_DEVICE(0x1260, 0x3877) }, /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */ { PCI_DEVICE(0x1260, 0x3886) }, /* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */ { PCI_DEVICE(0x1260, 0xffff) }, { }, }; MODULE_DEVICE_TABLE(pci, p54p_table); static int p54p_upload_firmware(struct ieee80211_hw *dev) { struct p54p_priv *priv = dev->priv; __le32 reg; int err; __le32 *data; u32 remains, left, device_addr; P54P_WRITE(int_enable, cpu_to_le32(0)); P54P_READ(int_enable); udelay(10); reg = P54P_READ(ctrl_stat); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); P54P_WRITE(ctrl_stat, reg); P54P_READ(ctrl_stat); udelay(10); reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); P54P_WRITE(ctrl_stat, reg); wmb(); udelay(10); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); P54P_WRITE(ctrl_stat, reg); wmb(); /* wait for the firmware to reset properly */ mdelay(10); err = p54_parse_firmware(dev, priv->firmware); if (err) return err; if (priv->common.fw_interface != FW_LM86) { dev_err(&priv->pdev->dev, "wrong firmware, " "please get a LM86(PCI) firmware a try again.\n"); return -EINVAL; } data = (__le32 *) priv->firmware->data; remains = priv->firmware->size; device_addr = ISL38XX_DEV_FIRMWARE_ADDR; while (remains) { u32 i = 0; left = min((u32)0x1000, remains); P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr)); P54P_READ(int_enable); device_addr += 0x1000; while (i < left) { P54P_WRITE(direct_mem_win[i], *data++); i += sizeof(u32); } remains -= left; P54P_READ(int_enable); } reg = P54P_READ(ctrl_stat); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); P54P_WRITE(ctrl_stat, reg); P54P_READ(ctrl_stat); udelay(10); reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); P54P_WRITE(ctrl_stat, reg); wmb(); udelay(10); reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); P54P_WRITE(ctrl_stat, reg); wmb(); udelay(10); /* wait for the firmware to boot properly */ mdelay(100); return 0; } static void p54p_refill_rx_ring(struct ieee80211_hw *dev, int ring_index, struct p54p_desc *ring, u32 ring_limit, struct sk_buff **rx_buf, u32 index) { struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; u32 limit, idx, i; idx = le32_to_cpu(ring_control->host_idx[ring_index]); limit = idx; limit -= index; limit = ring_limit - limit; i = idx % ring_limit; while (limit-- > 1) { struct p54p_desc *desc = &ring[i]; if (!desc->host_addr) { struct sk_buff *skb; dma_addr_t mapping; skb = dev_alloc_skb(priv->common.rx_mtu + 32); if (!skb) break; mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); if (pci_dma_mapping_error(priv->pdev, mapping)) { dev_kfree_skb_any(skb); dev_err(&priv->pdev->dev, "RX DMA Mapping error\n"); break; } desc->host_addr = cpu_to_le32(mapping); desc->device_addr = 0; // FIXME: necessary? desc->len = cpu_to_le16(priv->common.rx_mtu + 32); desc->flags = 0; rx_buf[i] = skb; } i++; idx++; i %= ring_limit; } wmb(); ring_control->host_idx[ring_index] = cpu_to_le32(idx); } static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index, int ring_index, struct p54p_desc *ring, u32 ring_limit, struct sk_buff **rx_buf) { struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; struct p54p_desc *desc; u32 idx, i; i = (*index) % ring_limit; (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); idx %= ring_limit; while (i != idx) { u16 len; struct sk_buff *skb; dma_addr_t dma_addr; desc = &ring[i]; len = le16_to_cpu(desc->len); skb = rx_buf[i]; if (!skb) { i++; i %= ring_limit; continue; } if (unlikely(len > priv->common.rx_mtu)) { if (net_ratelimit()) dev_err(&priv->pdev->dev, "rx'd frame size " "exceeds length threshold.\n"); len = priv->common.rx_mtu; } dma_addr = le32_to_cpu(desc->host_addr); pci_dma_sync_single_for_cpu(priv->pdev, dma_addr, priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); skb_put(skb, len); if (p54_rx(dev, skb)) { pci_unmap_single(priv->pdev, dma_addr, priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); rx_buf[i] = NULL; desc->host_addr = cpu_to_le32(0); } else { skb_trim(skb, 0); pci_dma_sync_single_for_device(priv->pdev, dma_addr, priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); desc->len = cpu_to_le16(priv->common.rx_mtu + 32); } i++; i %= ring_limit; } p54p_refill_rx_ring(dev, ring_index, ring, ring_limit, rx_buf, *index); } static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, int ring_index, struct p54p_desc *ring, u32 ring_limit, struct sk_buff **tx_buf) { struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; struct p54p_desc *desc; struct sk_buff *skb; u32 idx, i; i = (*index) % ring_limit; (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); idx %= ring_limit; while (i != idx) { desc = &ring[i]; skb = tx_buf[i]; tx_buf[i] = NULL; pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), le16_to_cpu(desc->len), PCI_DMA_TODEVICE); desc->host_addr = 0; desc->device_addr = 0; desc->len = 0; desc->flags = 0; if (skb && FREE_AFTER_TX(skb)) p54_free_skb(dev, skb); i++; i %= ring_limit; } } static void p54p_tasklet(unsigned long dev_id) { struct ieee80211_hw *dev = (struct ieee80211_hw *)dev_id; struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt, ARRAY_SIZE(ring_control->tx_mgmt), priv->tx_buf_mgmt); p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data, ARRAY_SIZE(ring_control->tx_data), priv->tx_buf_data); p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt, ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt); p54p_check_rx_ring(dev, &priv->rx_idx_data, 0, ring_control->rx_data, ARRAY_SIZE(ring_control->rx_data), priv->rx_buf_data); wmb(); P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); } static irqreturn_t p54p_interrupt(int irq, void *dev_id) { struct ieee80211_hw *dev = dev_id; struct p54p_priv *priv = dev->priv; __le32 reg; reg = P54P_READ(int_ident); if (unlikely(reg == cpu_to_le32(0xFFFFFFFF))) { goto out; } P54P_WRITE(int_ack, reg); reg &= P54P_READ(int_enable); if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) tasklet_schedule(&priv->tasklet); else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)) complete(&priv->boot_comp); out: return reg ? IRQ_HANDLED : IRQ_NONE; } static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { unsigned long flags; struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; struct p54p_desc *desc; dma_addr_t mapping; u32 idx, i; spin_lock_irqsave(&priv->lock, flags); idx = le32_to_cpu(ring_control->host_idx[1]); i = idx % ARRAY_SIZE(ring_control->tx_data); mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(priv->pdev, mapping)) { spin_unlock_irqrestore(&priv->lock, flags); p54_free_skb(dev, skb); dev_err(&priv->pdev->dev, "TX DMA mapping error\n"); return ; } priv->tx_buf_data[i] = skb; desc = &ring_control->tx_data[i]; desc->host_addr = cpu_to_le32(mapping); desc->device_addr = ((struct p54_hdr *)skb->data)->req_id; desc->len = cpu_to_le16(skb->len); desc->flags = 0; wmb(); ring_control->host_idx[1] = cpu_to_le32(idx + 1); spin_unlock_irqrestore(&priv->lock, flags); P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); P54P_READ(dev_int); } static void p54p_stop(struct ieee80211_hw *dev) { struct p54p_priv *priv = dev->priv; struct p54p_ring_control *ring_control = priv->ring_control; unsigned int i; struct p54p_desc *desc; P54P_WRITE(int_enable, cpu_to_le32(0)); P54P_READ(int_enable); udelay(10); free_irq(priv->pdev->irq, dev); tasklet_kill(&priv->tasklet); P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); for (i = 0; i < ARRAY_SIZE(priv->rx_buf_data); i++) { desc = &ring_control->rx_data[i]; if (desc->host_addr) pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); kfree_skb(priv->rx_buf_data[i]); priv->rx_buf_data[i] = NULL; } for (i = 0; i < ARRAY_SIZE(priv->rx_buf_mgmt); i++) { desc = &ring_control->rx_mgmt[i]; if (desc->host_addr) pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); kfree_skb(priv->rx_buf_mgmt[i]); priv->rx_buf_mgmt[i] = NULL; } for (i = 0; i < ARRAY_SIZE(priv->tx_buf_data); i++) { desc = &ring_control->tx_data[i]; if (desc->host_addr) pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), le16_to_cpu(desc->len), PCI_DMA_TODEVICE); p54_free_skb(dev, priv->tx_buf_data[i]); priv->tx_buf_data[i] = NULL; } for (i = 0; i < ARRAY_SIZE(priv->tx_buf_mgmt); i++) { desc = &ring_control->tx_mgmt[i]; if (desc->host_addr) pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), le16_to_cpu(desc->len), PCI_DMA_TODEVICE); p54_free_skb(dev, priv->tx_buf_mgmt[i]); priv->tx_buf_mgmt[i] = NULL; } memset(ring_control, 0, sizeof(*ring_control)); } static int p54p_open(struct ieee80211_hw *dev) { struct p54p_priv *priv = dev->priv; int err; init_completion(&priv->boot_comp); err = request_irq(priv->pdev->irq, p54p_interrupt, IRQF_SHARED, "p54pci", dev); if (err) { dev_err(&priv->pdev->dev, "failed to register IRQ handler\n"); return err; } memset(priv->ring_control, 0, sizeof(*priv->ring_control)); err = p54p_upload_firmware(dev); if (err) { free_irq(priv->pdev->irq, dev); return err; } priv->rx_idx_data = priv->tx_idx_data = 0; priv->rx_idx_mgmt = priv->tx_idx_mgmt = 0; p54p_refill_rx_ring(dev, 0, priv->ring_control->rx_data, ARRAY_SIZE(priv->ring_control->rx_data), priv->rx_buf_data, 0); p54p_refill_rx_ring(dev, 2, priv->ring_control->rx_mgmt, ARRAY_SIZE(priv->ring_control->rx_mgmt), priv->rx_buf_mgmt, 0); P54P_WRITE(ring_control_base, cpu_to_le32(priv->ring_control_dma)); P54P_READ(ring_control_base); wmb(); udelay(10); P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT)); P54P_READ(int_enable); wmb(); udelay(10); P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); P54P_READ(dev_int); if (!wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ)) { wiphy_err(dev->wiphy, "Cannot boot firmware!\n"); p54p_stop(dev); return -ETIMEDOUT; } P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)); P54P_READ(int_enable); wmb(); udelay(10); P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); P54P_READ(dev_int); wmb(); udelay(10); return 0; } static void p54p_firmware_step2(const struct firmware *fw, void *context) { struct p54p_priv *priv = context; struct ieee80211_hw *dev = priv->common.hw; struct pci_dev *pdev = priv->pdev; int err; if (!fw) { dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n"); err = -ENOENT; goto out; } priv->firmware = fw; err = p54p_open(dev); if (err) goto out; err = p54_read_eeprom(dev); p54p_stop(dev); if (err) goto out; err = p54_register_common(dev, &pdev->dev); if (err) goto out; out: complete(&priv->fw_loaded); if (err) { struct device *parent = pdev->dev.parent; if (parent) device_lock(parent); /* * This will indirectly result in a call to p54p_remove. * Hence, we don't need to bother with freeing any * allocated ressources at all. */ device_release_driver(&pdev->dev); if (parent) device_unlock(parent); } pci_dev_put(pdev); } static int __devinit p54p_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct p54p_priv *priv; struct ieee80211_hw *dev; unsigned long mem_addr, mem_len; int err; pci_dev_get(pdev); err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "Cannot enable new PCI device\n"); return err; } mem_addr = pci_resource_start(pdev, 0); mem_len = pci_resource_len(pdev, 0); if (mem_len < sizeof(struct p54p_csr)) { dev_err(&pdev->dev, "Too short PCI resources\n"); goto err_disable_dev; } err = pci_request_regions(pdev, "p54pci"); if (err) { dev_err(&pdev->dev, "Cannot obtain PCI resources\n"); goto err_disable_dev; } if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { dev_err(&pdev->dev, "No suitable DMA available\n"); goto err_free_reg; } pci_set_master(pdev); pci_try_set_mwi(pdev); pci_write_config_byte(pdev, 0x40, 0); pci_write_config_byte(pdev, 0x41, 0); dev = p54_init_common(sizeof(*priv)); if (!dev) { dev_err(&pdev->dev, "ieee80211 alloc failed\n"); err = -ENOMEM; goto err_free_reg; } priv = dev->priv; priv->pdev = pdev; init_completion(&priv->fw_loaded); SET_IEEE80211_DEV(dev, &pdev->dev); pci_set_drvdata(pdev, dev); priv->map = ioremap(mem_addr, mem_len); if (!priv->map) { dev_err(&pdev->dev, "Cannot map device memory\n"); err = -ENOMEM; goto err_free_dev; } priv->ring_control = pci_alloc_consistent(pdev, sizeof(*priv->ring_control), &priv->ring_control_dma); if (!priv->ring_control) { dev_err(&pdev->dev, "Cannot allocate rings\n"); err = -ENOMEM; goto err_iounmap; } priv->common.open = p54p_open; priv->common.stop = p54p_stop; priv->common.tx = p54p_tx; spin_lock_init(&priv->lock); tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev); err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci", &priv->pdev->dev, GFP_KERNEL, priv, p54p_firmware_step2); if (!err) return 0; pci_free_consistent(pdev, sizeof(*priv->ring_control), priv->ring_control, priv->ring_control_dma); err_iounmap: iounmap(priv->map); err_free_dev: pci_set_drvdata(pdev, NULL); p54_free_common(dev); err_free_reg: pci_release_regions(pdev); err_disable_dev: pci_disable_device(pdev); pci_dev_put(pdev); return err; } static void __devexit p54p_remove(struct pci_dev *pdev) { struct ieee80211_hw *dev = pci_get_drvdata(pdev); struct p54p_priv *priv; if (!dev) return; priv = dev->priv; wait_for_completion(&priv->fw_loaded); p54_unregister_common(dev); release_firmware(priv->firmware); pci_free_consistent(pdev, sizeof(*priv->ring_control), priv->ring_control, priv->ring_control_dma); iounmap(priv->map); pci_release_regions(pdev); pci_disable_device(pdev); p54_free_common(dev); } #ifdef CONFIG_PM static int p54p_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); pci_save_state(pdev); pci_set_power_state(pdev, PCI_D3hot); pci_disable_device(pdev); return 0; } static int p54p_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); int err; err = pci_reenable_device(pdev); if (err) return err; return pci_set_power_state(pdev, PCI_D0); } compat_pci_suspend(p54p_suspend) compat_pci_resume(p54p_resume) static SIMPLE_DEV_PM_OPS(p54pci_pm_ops, p54p_suspend, p54p_resume); #define P54P_PM_OPS (&p54pci_pm_ops) #else #define P54P_PM_OPS (NULL) #endif /* CONFIG_PM */ static struct pci_driver p54p_driver = { .name = "p54pci", .id_table = p54p_table, .probe = p54p_probe, .remove = __devexit_p(p54p_remove), #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = P54P_PM_OPS, #elif defined(CONFIG_PM) .suspend = p54p_suspend_compat, .resume = p54p_resume_compat, #endif }; module_pci_driver(p54p_driver); compat-drivers-2012-09-18/drivers/net/wireless/p54/txrx.c0000644000175000017500000006244412026211315022254 0ustar mcgrofmcgrof/* * Common code for mac80211 Prism54 drivers * * Copyright (c) 2006, Michael Wu * Copyright (c) 2007-2009, Christian Lamparter * Copyright 2008, Johannes Berg * * Based on: * - the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * - stlc45xx driver * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "p54.h" #include "lmac.h" #ifdef P54_MM_DEBUG static void p54_dump_tx_queue(struct p54_common *priv) { unsigned long flags; struct ieee80211_tx_info *info; struct p54_tx_info *range; struct sk_buff *skb; struct p54_hdr *hdr; unsigned int i = 0; u32 prev_addr; u32 largest_hole = 0, free; spin_lock_irqsave(&priv->tx_queue.lock, flags); wiphy_debug(priv->hw->wiphy, "/ --- tx queue dump (%d entries) ---\n", skb_queue_len(&priv->tx_queue)); prev_addr = priv->rx_start; skb_queue_walk(&priv->tx_queue, skb) { info = IEEE80211_SKB_CB(skb); range = (void *) info->rate_driver_data; hdr = (void *) skb->data; free = range->start_addr - prev_addr; wiphy_debug(priv->hw->wiphy, "| [%02d] => [skb:%p skb_len:0x%04x " "hdr:{flags:%02x len:%04x req_id:%04x type:%02x} " "mem:{start:%04x end:%04x, free:%d}]\n", i++, skb, skb->len, le16_to_cpu(hdr->flags), le16_to_cpu(hdr->len), le32_to_cpu(hdr->req_id), le16_to_cpu(hdr->type), range->start_addr, range->end_addr, free); prev_addr = range->end_addr; largest_hole = max(largest_hole, free); } free = priv->rx_end - prev_addr; largest_hole = max(largest_hole, free); wiphy_debug(priv->hw->wiphy, "\\ --- [free: %d], largest free block: %d ---\n", free, largest_hole); spin_unlock_irqrestore(&priv->tx_queue.lock, flags); } #endif /* P54_MM_DEBUG */ /* * So, the firmware is somewhat stupid and doesn't know what places in its * memory incoming data should go to. By poking around in the firmware, we * can find some unused memory to upload our packets to. However, data that we * want the card to TX needs to stay intact until the card has told us that * it is done with it. This function finds empty places we can upload to and * marks allocated areas as reserved if necessary. p54_find_and_unlink_skb or * p54_free_skb frees allocated areas. */ static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb) { struct sk_buff *entry, *target_skb = NULL; struct ieee80211_tx_info *info; struct p54_tx_info *range; struct p54_hdr *data = (void *) skb->data; unsigned long flags; u32 last_addr = priv->rx_start; u32 target_addr = priv->rx_start; u16 len = priv->headroom + skb->len + priv->tailroom + 3; info = IEEE80211_SKB_CB(skb); range = (void *) info->rate_driver_data; len = (range->extra_len + len) & ~0x3; spin_lock_irqsave(&priv->tx_queue.lock, flags); if (unlikely(skb_queue_len(&priv->tx_queue) == 32)) { /* * The tx_queue is now really full. * * TODO: check if the device has crashed and reset it. */ spin_unlock_irqrestore(&priv->tx_queue.lock, flags); return -EBUSY; } skb_queue_walk(&priv->tx_queue, entry) { u32 hole_size; info = IEEE80211_SKB_CB(entry); range = (void *) info->rate_driver_data; hole_size = range->start_addr - last_addr; if (!target_skb && hole_size >= len) { target_skb = entry->prev; hole_size -= len; target_addr = last_addr; break; } last_addr = range->end_addr; } if (unlikely(!target_skb)) { if (priv->rx_end - last_addr >= len) { target_skb = priv->tx_queue.prev; if (!skb_queue_empty(&priv->tx_queue)) { info = IEEE80211_SKB_CB(target_skb); range = (void *)info->rate_driver_data; target_addr = range->end_addr; } } else { spin_unlock_irqrestore(&priv->tx_queue.lock, flags); return -ENOSPC; } } info = IEEE80211_SKB_CB(skb); range = (void *) info->rate_driver_data; range->start_addr = target_addr; range->end_addr = target_addr + len; data->req_id = cpu_to_le32(target_addr + priv->headroom); if (IS_DATA_FRAME(skb) && unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) priv->beacon_req_id = data->req_id; __skb_queue_after(&priv->tx_queue, target_skb, skb); spin_unlock_irqrestore(&priv->tx_queue.lock, flags); return 0; } static void p54_tx_pending(struct p54_common *priv) { struct sk_buff *skb; int ret; skb = skb_dequeue(&priv->tx_pending); if (unlikely(!skb)) return ; ret = p54_assign_address(priv, skb); if (unlikely(ret)) skb_queue_head(&priv->tx_pending, skb); else priv->tx(priv->hw, skb); } static void p54_wake_queues(struct p54_common *priv) { unsigned long flags; unsigned int i; if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) return ; p54_tx_pending(priv); spin_lock_irqsave(&priv->tx_stats_lock, flags); for (i = 0; i < priv->hw->queues; i++) { if (priv->tx_stats[i + P54_QUEUE_DATA].len < priv->tx_stats[i + P54_QUEUE_DATA].limit) ieee80211_wake_queue(priv->hw, i); } spin_unlock_irqrestore(&priv->tx_stats_lock, flags); } static int p54_tx_qos_accounting_alloc(struct p54_common *priv, struct sk_buff *skb, const u16 p54_queue) { struct p54_tx_queue_stats *queue; unsigned long flags; if (WARN_ON(p54_queue >= P54_QUEUE_NUM)) return -EINVAL; queue = &priv->tx_stats[p54_queue]; spin_lock_irqsave(&priv->tx_stats_lock, flags); if (unlikely(queue->len >= queue->limit && IS_QOS_QUEUE(p54_queue))) { spin_unlock_irqrestore(&priv->tx_stats_lock, flags); return -ENOSPC; } queue->len++; queue->count++; if (unlikely(queue->len == queue->limit && IS_QOS_QUEUE(p54_queue))) { u16 ac_queue = p54_queue - P54_QUEUE_DATA; ieee80211_stop_queue(priv->hw, ac_queue); } spin_unlock_irqrestore(&priv->tx_stats_lock, flags); return 0; } static void p54_tx_qos_accounting_free(struct p54_common *priv, struct sk_buff *skb) { if (IS_DATA_FRAME(skb)) { unsigned long flags; spin_lock_irqsave(&priv->tx_stats_lock, flags); priv->tx_stats[GET_HW_QUEUE(skb)].len--; spin_unlock_irqrestore(&priv->tx_stats_lock, flags); if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) { if (priv->beacon_req_id == GET_REQ_ID(skb)) { /* this is the active beacon set anymore */ priv->beacon_req_id = 0; } complete(&priv->beacon_comp); } } p54_wake_queues(priv); } void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54_common *priv = dev->priv; if (unlikely(!skb)) return ; skb_unlink(skb, &priv->tx_queue); p54_tx_qos_accounting_free(priv, skb); ieee80211_free_txskb(dev, skb); } EXPORT_SYMBOL_GPL(p54_free_skb); static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv, const __le32 req_id) { struct sk_buff *entry; unsigned long flags; spin_lock_irqsave(&priv->tx_queue.lock, flags); skb_queue_walk(&priv->tx_queue, entry) { struct p54_hdr *hdr = (struct p54_hdr *) entry->data; if (hdr->req_id == req_id) { __skb_unlink(entry, &priv->tx_queue); spin_unlock_irqrestore(&priv->tx_queue.lock, flags); p54_tx_qos_accounting_free(priv, entry); return entry; } } spin_unlock_irqrestore(&priv->tx_queue.lock, flags); return NULL; } void p54_tx(struct p54_common *priv, struct sk_buff *skb) { skb_queue_tail(&priv->tx_pending, skb); p54_tx_pending(priv); } static int p54_rssi_to_dbm(struct p54_common *priv, int rssi) { if (priv->rxhw != 5) { return ((rssi * priv->cur_rssi->mul) / 64 + priv->cur_rssi->add) / 4; } else { /* * TODO: find the correct formula */ return rssi / 2 - 110; } } /* * Even if the firmware is capable of dealing with incoming traffic, * while dozing, we have to prepared in case mac80211 uses PS-POLL * to retrieve outstanding frames from our AP. * (see comment in net/mac80211/mlme.c @ line 1993) */ static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *) skb->data; struct ieee80211_tim_ie *tim_ie; u8 *tim; u8 tim_len; bool new_psm; /* only beacons have a TIM IE */ if (!ieee80211_is_beacon(hdr->frame_control)) return; if (!priv->aid) return; /* only consider beacons from the associated BSSID */ if (!ether_addr_equal(hdr->addr3, priv->bssid)) return; tim = p54_find_ie(skb, WLAN_EID_TIM); if (!tim) return; tim_len = tim[1]; tim_ie = (struct ieee80211_tim_ie *) &tim[2]; new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid); if (new_psm != priv->powersave_override) { priv->powersave_override = new_psm; p54_set_ps(priv); } } static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) { struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data; struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); u16 freq = le16_to_cpu(hdr->freq); size_t header_len = sizeof(*hdr); u32 tsf32; u8 rate = hdr->rate & 0xf; /* * If the device is in a unspecified state we have to * ignore all data frames. Else we could end up with a * nasty crash. */ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) return 0; if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) return 0; if (hdr->decrypt_status == P54_DECRYPT_OK) rx_status->flag |= RX_FLAG_DECRYPTED; if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) || (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP)) rx_status->flag |= RX_FLAG_MMIC_ERROR; rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi); if (hdr->rate & 0x10) rx_status->flag |= RX_FLAG_SHORTPRE; if (priv->hw->conf.channel->band == IEEE80211_BAND_5GHZ) rx_status->rate_idx = (rate < 4) ? 0 : rate - 4; else rx_status->rate_idx = rate; rx_status->freq = freq; rx_status->band = priv->hw->conf.channel->band; rx_status->antenna = hdr->antenna; tsf32 = le32_to_cpu(hdr->tsf32); if (tsf32 < priv->tsf_low32) priv->tsf_high32++; rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32; priv->tsf_low32 = tsf32; rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) header_len += hdr->align[0]; skb_pull(skb, header_len); skb_trim(skb, le16_to_cpu(hdr->len)); if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS)) p54_pspoll_workaround(priv, skb); ieee80211_rx_irqsafe(priv->hw, skb); ieee80211_queue_delayed_work(priv->hw, &priv->work, msecs_to_jiffies(P54_STATISTICS_UPDATE)); return -1; } static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data; struct ieee80211_tx_info *info; struct p54_hdr *entry_hdr; struct p54_tx_data *entry_data; struct sk_buff *entry; unsigned int pad = 0, frame_len; int count, idx; entry = p54_find_and_unlink_skb(priv, hdr->req_id); if (unlikely(!entry)) return ; frame_len = entry->len; info = IEEE80211_SKB_CB(entry); entry_hdr = (struct p54_hdr *) entry->data; entry_data = (struct p54_tx_data *) entry_hdr->data; priv->stats.dot11ACKFailureCount += payload->tries - 1; /* * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are * generated by the driver. Therefore tx_status is bogus * and we don't want to confuse the mac80211 stack. */ if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { dev_kfree_skb_any(entry); return ; } /* * Clear manually, ieee80211_tx_info_clear_status would * clear the counts too and we need them. */ memset(&info->status.ack_signal, 0, sizeof(struct ieee80211_tx_info) - offsetof(struct ieee80211_tx_info, status.ack_signal)); BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.ack_signal) != 20); if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) pad = entry_data->align[0]; /* walk through the rates array and adjust the counts */ count = payload->tries; for (idx = 0; idx < 4; idx++) { if (count >= info->status.rates[idx].count) { count -= info->status.rates[idx].count; } else if (count > 0) { info->status.rates[idx].count = count; count = 0; } else { info->status.rates[idx].idx = -1; info->status.rates[idx].count = 0; } } if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !(payload->status & P54_TX_FAILED)) info->flags |= IEEE80211_TX_STAT_ACK; if (payload->status & P54_TX_PSM_CANCELLED) info->flags |= IEEE80211_TX_STAT_TX_FILTERED; info->status.ack_signal = p54_rssi_to_dbm(priv, (int)payload->ack_rssi); /* Undo all changes to the frame. */ switch (entry_data->key_type) { case P54_CRYPTO_TKIPMICHAEL: { u8 *iv = (u8 *)(entry_data->align + pad + entry_data->crypt_offset); /* Restore the original TKIP IV. */ iv[2] = iv[0]; iv[0] = iv[1]; iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */ frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */ break; } case P54_CRYPTO_AESCCMP: frame_len -= 8; /* remove CCMP_MIC */ break; case P54_CRYPTO_WEP: frame_len -= 4; /* remove WEP_ICV */ break; } skb_trim(entry, frame_len); skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data)); ieee80211_tx_status_irqsafe(priv->hw, entry); } static void p54_rx_eeprom_readback(struct p54_common *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data; struct sk_buff *tmp; if (!priv->eeprom) return ; if (priv->fw_var >= 0x509) { memcpy(priv->eeprom, eeprom->v2.data, le16_to_cpu(eeprom->v2.len)); } else { memcpy(priv->eeprom, eeprom->v1.data, le16_to_cpu(eeprom->v1.len)); } priv->eeprom = NULL; tmp = p54_find_and_unlink_skb(priv, hdr->req_id); dev_kfree_skb_any(tmp); complete(&priv->eeprom_comp); } static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; struct p54_statistics *stats = (struct p54_statistics *) hdr->data; struct sk_buff *tmp; struct ieee80211_channel *chan; unsigned int i, rssi, tx, cca, dtime, dtotal, dcca, dtx, drssi, unit; u32 tsf32; if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) return ; tsf32 = le32_to_cpu(stats->tsf32); if (tsf32 < priv->tsf_low32) priv->tsf_high32++; priv->tsf_low32 = tsf32; priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail); priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success); priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs); priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise)); /* * STSW450X LMAC API page 26 - 3.8 Statistics * "The exact measurement period can be derived from the * timestamp member". */ dtime = tsf32 - priv->survey_raw.timestamp; /* * STSW450X LMAC API page 26 - 3.8.1 Noise histogram * The LMAC samples RSSI, CCA and transmit state at regular * periods (typically 8 times per 1k [as in 1024] usec). */ cca = le32_to_cpu(stats->sample_cca); tx = le32_to_cpu(stats->sample_tx); rssi = 0; for (i = 0; i < ARRAY_SIZE(stats->sample_noise); i++) rssi += le32_to_cpu(stats->sample_noise[i]); dcca = cca - priv->survey_raw.cached_cca; drssi = rssi - priv->survey_raw.cached_rssi; dtx = tx - priv->survey_raw.cached_tx; dtotal = dcca + drssi + dtx; /* * update statistics when more than a second is over since the * last call, or when a update is badly needed. */ if (dtotal && (priv->update_stats || dtime >= USEC_PER_SEC) && dtime >= dtotal) { priv->survey_raw.timestamp = tsf32; priv->update_stats = false; unit = dtime / dtotal; if (dcca) { priv->survey_raw.cca += dcca * unit; priv->survey_raw.cached_cca = cca; } if (dtx) { priv->survey_raw.tx += dtx * unit; priv->survey_raw.cached_tx = tx; } if (drssi) { priv->survey_raw.rssi += drssi * unit; priv->survey_raw.cached_rssi = rssi; } /* 1024 usec / 8 times = 128 usec / time */ if (!(priv->phy_ps || priv->phy_idle)) priv->survey_raw.active += dtotal * unit; else priv->survey_raw.active += (dcca + dtx) * unit; } chan = priv->curchan; if (chan) { struct survey_info *survey = &priv->survey[chan->hw_value]; survey->noise = clamp_t(s8, priv->noise, -128, 127); survey->channel_time = priv->survey_raw.active; survey->channel_time_tx = priv->survey_raw.tx; survey->channel_time_busy = priv->survey_raw.tx + priv->survey_raw.cca; do_div(survey->channel_time, 1024); do_div(survey->channel_time_tx, 1024); do_div(survey->channel_time_busy, 1024); } tmp = p54_find_and_unlink_skb(priv, hdr->req_id); dev_kfree_skb_any(tmp); complete(&priv->stat_comp); } static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; struct p54_trap *trap = (struct p54_trap *) hdr->data; u16 event = le16_to_cpu(trap->event); u16 freq = le16_to_cpu(trap->frequency); switch (event) { case P54_TRAP_BEACON_TX: break; case P54_TRAP_RADAR: wiphy_info(priv->hw->wiphy, "radar (freq:%d MHz)\n", freq); break; case P54_TRAP_NO_BEACON: if (priv->vif) ieee80211_beacon_loss(priv->vif); break; case P54_TRAP_SCAN: break; case P54_TRAP_TBTT: break; case P54_TRAP_TIMER: break; case P54_TRAP_FAA_RADIO_OFF: wiphy_rfkill_set_hw_state(priv->hw->wiphy, true); break; case P54_TRAP_FAA_RADIO_ON: wiphy_rfkill_set_hw_state(priv->hw->wiphy, false); break; default: wiphy_info(priv->hw->wiphy, "received event:%x freq:%d\n", event, freq); break; } } static int p54_rx_control(struct p54_common *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; switch (le16_to_cpu(hdr->type)) { case P54_CONTROL_TYPE_TXDONE: p54_rx_frame_sent(priv, skb); break; case P54_CONTROL_TYPE_TRAP: p54_rx_trap(priv, skb); break; case P54_CONTROL_TYPE_BBP: break; case P54_CONTROL_TYPE_STAT_READBACK: p54_rx_stats(priv, skb); break; case P54_CONTROL_TYPE_EEPROM_READBACK: p54_rx_eeprom_readback(priv, skb); break; default: wiphy_debug(priv->hw->wiphy, "not handling 0x%02x type control frame\n", le16_to_cpu(hdr->type)); break; } return 0; } /* returns zero if skb can be reused */ int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54_common *priv = dev->priv; u16 type = le16_to_cpu(*((__le16 *)skb->data)); if (type & P54_HDR_FLAG_CONTROL) return p54_rx_control(priv, skb); else return p54_rx_data(priv, skb); } EXPORT_SYMBOL_GPL(p54_rx); static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, u8 *queue, u32 *extra_len, u16 *flags, u16 *aid, bool *burst_possible) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_data_qos(hdr->frame_control)) *burst_possible = true; else *burst_possible = false; if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER) *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA; switch (priv->mode) { case NL80211_IFTYPE_MONITOR: /* * We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for * every frame in promiscuous/monitor mode. * see STSW45x0C LMAC API - page 12. */ *aid = 0; *flags |= P54_HDR_FLAG_DATA_OUT_PROMISC; break; case NL80211_IFTYPE_STATION: *aid = 1; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { *aid = 0; *queue = P54_QUEUE_CAB; return; } if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { if (ieee80211_is_probe_resp(hdr->frame_control)) { *aid = 0; *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP | P54_HDR_FLAG_DATA_OUT_NOCANCEL; return; } else if (ieee80211_is_beacon(hdr->frame_control)) { *aid = 0; if (info->flags & IEEE80211_TX_CTL_INJECTED) { /* * Injecting beacons on top of a AP is * not a good idea... nevertheless, * it should be doable. */ return; } *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP; *queue = P54_QUEUE_BEACON; *extra_len = IEEE80211_MAX_TIM_LEN; return; } } if (sta) *aid = sta->aid; break; } } static u8 p54_convert_algo(u32 cipher) { switch (cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: return P54_CRYPTO_WEP; case WLAN_CIPHER_SUITE_TKIP: return P54_CRYPTO_TKIPMICHAEL; case WLAN_CIPHER_SUITE_CCMP: return P54_CRYPTO_AESCCMP; default: return 0; } } void p54_tx_80211(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct p54_common *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct p54_tx_info *p54info; struct p54_hdr *hdr; struct p54_tx_data *txhdr; unsigned int padding, len, extra_len = 0; int i, j, ridx; u16 hdr_flags = 0, aid = 0; u8 rate, queue = 0, crypt_offset = 0; u8 cts_rate = 0x20; u8 rc_flags; u8 calculated_tries[4]; u8 nrates = 0, nremaining = 8; bool burst_allowed = false; p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len, &hdr_flags, &aid, &burst_allowed); if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { ieee80211_free_txskb(dev, skb); return; } padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; len = skb->len; if (info->control.hw_key) { crypt_offset = ieee80211_get_hdrlen_from_skb(skb); if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { u8 *iv = (u8 *)(skb->data + crypt_offset); /* * The firmware excepts that the IV has to have * this special format */ iv[1] = iv[0]; iv[0] = iv[2]; iv[2] = 0; } } txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding); hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr)); if (padding) hdr_flags |= P54_HDR_FLAG_DATA_ALIGN; hdr->type = cpu_to_le16(aid); hdr->rts_tries = info->control.rates[0].count; /* * we register the rates in perfect order, and * RTS/CTS won't happen on 5 GHz */ cts_rate = info->control.rts_cts_rate_idx; memset(&txhdr->rateset, 0, sizeof(txhdr->rateset)); /* see how many rates got used */ for (i = 0; i < dev->max_rates; i++) { if (info->control.rates[i].idx < 0) break; nrates++; } /* limit tries to 8/nrates per rate */ for (i = 0; i < nrates; i++) { /* * The magic expression here is equivalent to 8/nrates for * all values that matter, but avoids division and jumps. * Note that nrates can only take the values 1 through 4. */ calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1, info->control.rates[i].count); nremaining -= calculated_tries[i]; } /* if there are tries left, distribute from back to front */ for (i = nrates - 1; nremaining > 0 && i >= 0; i--) { int tmp = info->control.rates[i].count - calculated_tries[i]; if (tmp <= 0) continue; /* RC requested more tries at this rate */ tmp = min_t(int, tmp, nremaining); calculated_tries[i] += tmp; nremaining -= tmp; } ridx = 0; for (i = 0; i < nrates && ridx < 8; i++) { /* we register the rates in perfect order */ rate = info->control.rates[i].idx; if (info->band == IEEE80211_BAND_5GHZ) rate += 4; /* store the count we actually calculated for TX status */ info->control.rates[i].count = calculated_tries[i]; rc_flags = info->control.rates[i].flags; if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) { rate |= 0x10; cts_rate |= 0x10; } if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { burst_allowed = false; rate |= 0x40; } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { rate |= 0x20; burst_allowed = false; } for (j = 0; j < calculated_tries[i] && ridx < 8; j++) { txhdr->rateset[ridx] = rate; ridx++; } } if (burst_allowed) hdr_flags |= P54_HDR_FLAG_DATA_OUT_BURST; /* TODO: enable bursting */ hdr->flags = cpu_to_le16(hdr_flags); hdr->tries = ridx; txhdr->rts_rate_idx = 0; if (info->control.hw_key) { txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher); txhdr->key_len = min((u8)16, info->control.hw_key->keylen); memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { /* reserve space for the MIC key */ len += 8; memcpy(skb_put(skb, 8), &(info->control.hw_key->key [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8); } /* reserve some space for ICV */ len += info->control.hw_key->icv_len; memset(skb_put(skb, info->control.hw_key->icv_len), 0, info->control.hw_key->icv_len); } else { txhdr->key_type = 0; txhdr->key_len = 0; } txhdr->crypt_offset = crypt_offset; txhdr->hw_queue = queue; txhdr->backlog = priv->tx_stats[queue].len - 1; memset(txhdr->durations, 0, sizeof(txhdr->durations)); txhdr->tx_antenna = 2 & priv->tx_diversity_mask; if (priv->rxhw == 5) { txhdr->longbow.cts_rate = cts_rate; txhdr->longbow.output_power = cpu_to_le16(priv->output_power); } else { txhdr->normal.output_power = priv->output_power; txhdr->normal.cts_rate = cts_rate; } if (padding) txhdr->align[0] = padding; hdr->len = cpu_to_le16(len); /* modifies skb->cb and with it info, so must be last! */ p54info = (void *) info->rate_driver_data; p54info->extra_len = extra_len; p54_tx(priv, skb); } compat-drivers-2012-09-18/drivers/net/wireless/p54/p54usb.h0000644000175000017500000000734212026211315022372 0ustar mcgrofmcgrof#ifndef P54USB_H #define P54USB_H /* * Defines for USB based mac80211 Prism54 driver * * Copyright (c) 2006, Michael Wu * * Based on the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* for isl3886 register definitions used on ver 1 devices */ #include "p54pci.h" #include "net2280.h" /* pci */ #define NET2280_BASE 0x10000000 #define NET2280_BASE2 0x20000000 /* gpio */ #define P54U_BRG_POWER_UP (1 << GPIO0_DATA) #define P54U_BRG_POWER_DOWN (1 << GPIO1_DATA) /* devinit */ #define NET2280_CLK_4Mhz (15 << LOCAL_CLOCK_FREQUENCY) #define NET2280_CLK_30Mhz (2 << LOCAL_CLOCK_FREQUENCY) #define NET2280_CLK_60Mhz (1 << LOCAL_CLOCK_FREQUENCY) #define NET2280_CLK_STOP (0 << LOCAL_CLOCK_FREQUENCY) #define NET2280_PCI_ENABLE (1 << PCI_ENABLE) #define NET2280_PCI_SOFT_RESET (1 << PCI_SOFT_RESET) /* endpoints */ #define NET2280_CLEAR_NAK_OUT_PACKETS_MODE (1 << CLEAR_NAK_OUT_PACKETS_MODE) #define NET2280_FIFO_FLUSH (1 << FIFO_FLUSH) /* irq */ #define NET2280_USB_INTERRUPT_ENABLE (1 << USB_INTERRUPT_ENABLE) #define NET2280_PCI_INTA_INTERRUPT (1 << PCI_INTA_INTERRUPT) #define NET2280_PCI_INTA_INTERRUPT_ENABLE (1 << PCI_INTA_INTERRUPT_ENABLE) /* registers */ #define NET2280_DEVINIT 0x00 #define NET2280_USBIRQENB1 0x24 #define NET2280_IRQSTAT1 0x2c #define NET2280_FIFOCTL 0x38 #define NET2280_GPIOCTL 0x50 #define NET2280_RELNUM 0x88 #define NET2280_EPA_RSP 0x324 #define NET2280_EPA_STAT 0x32c #define NET2280_EPB_STAT 0x34c #define NET2280_EPC_RSP 0x364 #define NET2280_EPC_STAT 0x36c #define NET2280_EPD_STAT 0x38c #define NET2280_EPA_CFG 0x320 #define NET2280_EPB_CFG 0x340 #define NET2280_EPC_CFG 0x360 #define NET2280_EPD_CFG 0x380 #define NET2280_EPE_CFG 0x3A0 #define NET2280_EPF_CFG 0x3C0 #define P54U_DEV_BASE 0x40000000 struct net2280_tx_hdr { __le32 device_addr; __le16 len; __le16 follower; /* ? */ u8 padding[8]; } __packed; struct lm87_tx_hdr { __le32 device_addr; __le32 chksum; } __packed; /* Some flags for the isl hardware registers controlling DMA inside the * chip */ #define ISL38XX_DMA_STATUS_DONE 0x00000001 #define ISL38XX_DMA_STATUS_READY 0x00000002 #define NET2280_EPA_FIFO_PCI_ADDR 0x20000000 #define ISL38XX_DMA_MASTER_CONTROL_TRIGGER 0x00000004 enum net2280_op_type { NET2280_BRG_U32 = 0x001F, NET2280_BRG_CFG_U32 = 0x000F, NET2280_BRG_CFG_U16 = 0x0003, NET2280_DEV_U32 = 0x080F, NET2280_DEV_CFG_U32 = 0x088F, NET2280_DEV_CFG_U16 = 0x0883 }; #define P54U_FW_BLOCK 2048 #define X2_SIGNATURE "x2 " #define X2_SIGNATURE_SIZE 4 struct x2_header { u8 signature[X2_SIGNATURE_SIZE]; __le32 fw_load_addr; __le32 fw_length; __le32 crc; } __packed; /* pipes 3 and 4 are not used by the driver */ #define P54U_PIPE_NUMBER 9 enum p54u_pipe_addr { P54U_PIPE_DATA = 0x01, P54U_PIPE_MGMT = 0x02, P54U_PIPE_3 = 0x03, P54U_PIPE_4 = 0x04, P54U_PIPE_BRG = 0x0d, P54U_PIPE_DEV = 0x0e, P54U_PIPE_INT = 0x0f }; struct p54u_rx_info { struct urb *urb; struct ieee80211_hw *dev; }; enum p54u_hw_type { P54U_INVALID_HW, P54U_NET2280, P54U_3887, /* keep last */ __NUM_P54U_HWTYPES, }; struct p54u_priv { struct p54_common common; struct usb_device *udev; struct usb_interface *intf; int (*upload_fw)(struct ieee80211_hw *dev); enum p54u_hw_type hw_type; spinlock_t lock; struct sk_buff_head rx_queue; struct usb_anchor submitted; const struct firmware *fw; /* asynchronous firmware callback */ struct completion fw_wait_load; }; #endif /* P54USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/p54spi.h0000644000175000017500000000617212026211315022374 0ustar mcgrofmcgrof/* * Copyright (C) 2008 Christian Lamparter * * This driver is a port from stlc45xx: * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef P54SPI_H #define P54SPI_H #include #include #include #include "p54.h" /* Bit 15 is read/write bit; ON = READ, OFF = WRITE */ #define SPI_ADRS_READ_BIT_15 0x8000 #define SPI_ADRS_ARM_INTERRUPTS 0x00 #define SPI_ADRS_ARM_INT_EN 0x04 #define SPI_ADRS_HOST_INTERRUPTS 0x08 #define SPI_ADRS_HOST_INT_EN 0x0c #define SPI_ADRS_HOST_INT_ACK 0x10 #define SPI_ADRS_GEN_PURP_1 0x14 #define SPI_ADRS_GEN_PURP_2 0x18 #define SPI_ADRS_DEV_CTRL_STAT 0x26 /* high word */ #define SPI_ADRS_DMA_DATA 0x28 #define SPI_ADRS_DMA_WRITE_CTRL 0x2c #define SPI_ADRS_DMA_WRITE_LEN 0x2e #define SPI_ADRS_DMA_WRITE_BASE 0x30 #define SPI_ADRS_DMA_READ_CTRL 0x34 #define SPI_ADRS_DMA_READ_LEN 0x36 #define SPI_ADRS_DMA_READ_BASE 0x38 #define SPI_CTRL_STAT_HOST_OVERRIDE 0x8000 #define SPI_CTRL_STAT_START_HALTED 0x4000 #define SPI_CTRL_STAT_RAM_BOOT 0x2000 #define SPI_CTRL_STAT_HOST_RESET 0x1000 #define SPI_CTRL_STAT_HOST_CPU_EN 0x0800 #define SPI_DMA_WRITE_CTRL_ENABLE 0x0001 #define SPI_DMA_READ_CTRL_ENABLE 0x0001 #define HOST_ALLOWED (1 << 7) #define SPI_TIMEOUT 100 /* msec */ #define SPI_MAX_TX_PACKETS 32 #define SPI_MAX_PACKET_SIZE 32767 #define SPI_TARGET_INT_WAKEUP 0x00000001 #define SPI_TARGET_INT_SLEEP 0x00000002 #define SPI_TARGET_INT_RDDONE 0x00000004 #define SPI_TARGET_INT_CTS 0x00004000 #define SPI_TARGET_INT_DR 0x00008000 #define SPI_HOST_INT_READY 0x00000001 #define SPI_HOST_INT_WR_READY 0x00000002 #define SPI_HOST_INT_SW_UPDATE 0x00000004 #define SPI_HOST_INT_UPDATE 0x10000000 /* clear to send */ #define SPI_HOST_INT_CR 0x00004000 /* data ready */ #define SPI_HOST_INT_DR 0x00008000 #define SPI_HOST_INTS_DEFAULT \ (SPI_HOST_INT_READY | SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE) #define TARGET_BOOT_SLEEP 50 struct p54s_dma_regs { __le16 cmd; __le16 len; __le32 addr; } __packed; struct p54s_tx_info { struct list_head tx_list; }; struct p54s_priv { /* p54_common has to be the first entry */ struct p54_common common; struct ieee80211_hw *hw; struct spi_device *spi; struct work_struct work; struct mutex mutex; struct completion fw_comp; spinlock_t tx_lock; /* protected by tx_lock */ struct list_head tx_pending; enum fw_state fw_state; const struct firmware *firmware; }; #endif /* P54SPI_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/p54spi_eeprom.h0000644000175000017500000006451312026211315023746 0ustar mcgrofmcgrof/* * Copyright (C) 2003 Conexant Americas Inc. All Rights Reserved. * Copyright (C) 2004, 2005, 2006 Nokia Corporation * Copyright 2008 Johannes Berg * Copyright 2008 Christian Lamparter * * based on: * - cx3110x's pda.h from Nokia * - cx3110-transfer.log by Johannes Berg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef P54SPI_EEPROM_H #define P54SPI_EEPROM_H static unsigned char p54spi_eeprom[] = { /* struct eeprom_pda_wrap */ 0x47, 0x4d, 0x55, 0xaa, /* magic */ 0x00, 0x00, /* pad */ 0x00, 0x00, /* eeprom_pda_data_wrap length */ 0x00, 0x00, 0x00, 0x00, /* arm opcode */ /* bogus MAC address */ 0x04, 0x00, 0x01, 0x01, /* PDR_MAC_ADDRESS */ 0x00, 0x02, 0xee, 0xc0, 0xff, 0xee, /* struct bootrec_exp_if */ 0x06, 0x00, 0x01, 0x10, /* PDR_INTERFACE_LIST */ 0x00, 0x00, /* role */ 0x0f, 0x00, /* if_id */ 0x85, 0x00, /* variant = Longbow RF, 2GHz */ 0x01, 0x00, /* btm_compat */ 0x1f, 0x00, /* top_compat */ 0x03, 0x00, 0x02, 0x10, /* PDR_HARDWARE_PLATFORM_COMPONENT_ID */ 0x03, 0x20, 0x00, 0x43, /* struct pda_country[6] */ 0x0d, 0x00, 0x07, 0x10, /* PDR_COUNTRY_LIST */ 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* struct pda_country */ 0x03, 0x00, 0x08, 0x10, /* PDR_DEFAULT_COUNTRY */ 0x30, 0x00, 0x00, 0x00, /* ETSI */ 0x03, 0x00, 0x00, 0x11, /* PDR_ANTENNA_GAIN */ 0x08, 0x08, 0x08, 0x08, 0x0a, 0x00, 0xff, 0xca, /* PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 */ 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x85, 0x09, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, /* struct pda_custom_wrapper */ 0x10, 0x06, 0x5d, 0xb0, /* PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM */ 0x0d, 0x00, 0xee, 0x00, /* 13 entries, 238 bytes per entry */ 0x00, 0x00, 0x16, 0x0c, /* no offset, 3094 total len */ /* 2412 MHz */ 0x6c, 0x09, 0x10, 0x01, 0x9a, 0x84, 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0xf0, 0x00, 0x94, 0x6c, 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0xd0, 0x00, 0xaa, 0x5a, 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0xa0, 0x00, 0xf3, 0x47, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x50, 0x00, 0x59, 0x36, 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0x00, 0x00, 0xe4, 0x2d, 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2417 MHz */ 0x71, 0x09, 0x10, 0x01, 0xb9, 0x83, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0xf0, 0x00, 0x2e, 0x6c, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xd0, 0x00, 0x8d, 0x5a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xa0, 0x00, 0x0a, 0x48, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0x50, 0x00, 0x7c, 0x36, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x00, 0x00, 0xf5, 0x2d, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2422 MHz */ 0x76, 0x09, 0x10, 0x01, 0xb9, 0x83, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0xf0, 0x00, 0x2e, 0x6c, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xd0, 0x00, 0x8d, 0x5a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xa0, 0x00, 0x0a, 0x48, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0x50, 0x00, 0x7c, 0x36, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x00, 0x00, 0xf5, 0x2d, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2427 MHz */ 0x7b, 0x09, 0x10, 0x01, 0x48, 0x83, 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf0, 0x00, 0xfb, 0x6b, 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xd0, 0x00, 0x7e, 0x5a, 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xa0, 0x00, 0x15, 0x48, 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0x50, 0x00, 0x8e, 0x36, 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x00, 0x00, 0xfe, 0x2d, 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2432 MHz */ 0x80, 0x09, 0x10, 0x01, 0xd7, 0x82, 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xf0, 0x00, 0xc8, 0x6b, 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xd0, 0x00, 0x6f, 0x5a, 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xa0, 0x00, 0x20, 0x48, 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x50, 0x00, 0x9f, 0x36, 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x00, 0x00, 0x06, 0x2e, 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2437 MHz */ 0x85, 0x09, 0x10, 0x01, 0x67, 0x82, 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xf0, 0x00, 0x95, 0x6b, 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xd0, 0x00, 0x61, 0x5a, 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0xa0, 0x00, 0x2c, 0x48, 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x50, 0x00, 0xb1, 0x36, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x00, 0x00, 0x0f, 0x2e, 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2442 MHz */ 0x8a, 0x09, 0x10, 0x01, 0xf6, 0x81, 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xf0, 0x00, 0x62, 0x6b, 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0xd0, 0x00, 0x52, 0x5a, 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0xa0, 0x00, 0x37, 0x48, 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x50, 0x00, 0xc2, 0x36, 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x00, 0x17, 0x2e, 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2447 MHz */ 0x8f, 0x09, 0x10, 0x01, 0x75, 0x83, 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf0, 0x00, 0x4b, 0x6c, 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd0, 0x00, 0xda, 0x5a, 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xa0, 0x00, 0x6d, 0x48, 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x50, 0x00, 0xc6, 0x36, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x00, 0x00, 0x15, 0x2e, 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2452 MHz */ 0x94, 0x09, 0x10, 0x01, 0xf4, 0x84, 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0xf0, 0x00, 0x34, 0x6d, 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0xd0, 0x00, 0x62, 0x5b, 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xa0, 0x00, 0xa2, 0x48, 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0x50, 0x00, 0xc9, 0x36, 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x00, 0x00, 0x12, 0x2e, 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2452 MHz */ 0x99, 0x09, 0x10, 0x01, 0x74, 0x86, 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0xf0, 0x00, 0x1e, 0x6e, 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0xd0, 0x00, 0xeb, 0x5b, 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0xa0, 0x00, 0xd8, 0x48, 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0x50, 0x00, 0xcd, 0x36, 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x00, 0x00, 0x10, 0x2e, 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2557 MHz */ 0x9e, 0x09, 0x10, 0x01, 0xf3, 0x87, 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xf0, 0x00, 0x07, 0x6f, 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0xd0, 0x00, 0x73, 0x5c, 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0xa0, 0x00, 0x0d, 0x49, 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x50, 0x00, 0xd1, 0x36, 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x00, 0x00, 0x0e, 0x2e, 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2562 MHz */ 0xa3, 0x09, 0x10, 0x01, 0x72, 0x89, 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xf0, 0x00, 0xf0, 0x6f, 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xd0, 0x00, 0xfb, 0x5c, 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0xa0, 0x00, 0x43, 0x49, 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x50, 0x00, 0xd4, 0x36, 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0x00, 0x00, 0x0b, 0x2e, 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* 2572 MHz */ 0xa8, 0x09, 0x10, 0x01, 0xf1, 0x8a, 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0xf0, 0x00, 0xd9, 0x70, 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xd0, 0x00, 0x83, 0x5d, 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xa0, 0x00, 0x78, 0x49, 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x50, 0x00, 0xd8, 0x36, 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0x00, 0x00, 0x09, 0x2e, 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, /* * Not really sure if this is actually the power_limit database, * it looks a bit "related" to PDR_PRISM_ZIF_TX_IQ_CALIBRATION */ /* struct pda_custom_wrapper */ 0xae, 0x00, 0xef, 0xbe, /* PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM */ 0x0d, 0x00, 0x1a, 0x00, /* 13 entries, 26 bytes per entry */ 0x00, 0x00, 0x52, 0x01, /* no offset, 338 bytes total */ /* 2412 MHz */ 0x6c, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2417 MHz */ 0x71, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2422 MHz */ 0x76, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2427 MHz */ 0x7b, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2432 MHz */ 0x80, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2437 MHz */ 0x85, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2442 MHz */ 0x8a, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2447 MHz */ 0x8f, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2452 MHz */ 0x94, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2457 MHz */ 0x99, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2462 MHz */ 0x9e, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2467 MHz */ 0xa3, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* 2472 MHz */ 0xa8, 0x09, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, /* struct pda_iq_autocal_entry[13] */ 0x42, 0x00, 0x06, 0x19, /* PDR_PRISM_ZIF_TX_IQ_CALIBRATION */ /* 2412 MHz */ 0x6c, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2417 MHz */ 0x71, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2422 MHz */ 0x76, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2427 MHz */ 0x7b, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2432 MHz */ 0x80, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2437 MHz */ 0x85, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2442 MHz */ 0x8a, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2447 MHz */ 0x8f, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2452 MHz */ 0x94, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, /* 2457 MHz */ 0x99, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, /* 2462 MHz */ 0x9e, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, /* 2467 MHz */ 0xa3, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, /* 2472 MHz */ 0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, /* PDR_END */ 0xb6, 0x04, }; #endif /* P54SPI_EEPROM_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/p54spi.c0000644000175000017500000004206012026211315022363 0ustar mcgrofmcgrof/* * Copyright (C) 2008 Christian Lamparter * Copyright 2008 Johannes Berg * * This driver is a port from stlc45xx: * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "p54spi.h" #include "p54.h" #include "lmac.h" #ifdef CONFIG_P54_SPI_DEFAULT_EEPROM #include "p54spi_eeprom.h" #endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */ MODULE_FIRMWARE("3826.arm"); /* * gpios should be handled in board files and provided via platform data, * but because it's currently impossible for p54spi to have a header file * in include/linux, let's use module paramaters for now */ static int p54spi_gpio_power = 97; module_param(p54spi_gpio_power, int, 0444); MODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line"); static int p54spi_gpio_irq = 87; module_param(p54spi_gpio_irq, int, 0444); MODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line"); static void p54spi_spi_read(struct p54s_priv *priv, u8 address, void *buf, size_t len) { struct spi_transfer t[2]; struct spi_message m; __le16 addr; /* We first push the address */ addr = cpu_to_le16(address << 8 | SPI_ADRS_READ_BIT_15); spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].tx_buf = &addr; t[0].len = sizeof(addr); spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; t[1].len = len; spi_message_add_tail(&t[1], &m); spi_sync(priv->spi, &m); } static void p54spi_spi_write(struct p54s_priv *priv, u8 address, const void *buf, size_t len) { struct spi_transfer t[3]; struct spi_message m; __le16 addr; /* We first push the address */ addr = cpu_to_le16(address << 8); spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].tx_buf = &addr; t[0].len = sizeof(addr); spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; t[1].len = len & ~1; spi_message_add_tail(&t[1], &m); if (len % 2) { __le16 last_word; last_word = cpu_to_le16(((u8 *)buf)[len - 1]); t[2].tx_buf = &last_word; t[2].len = sizeof(last_word); spi_message_add_tail(&t[2], &m); } spi_sync(priv->spi, &m); } static u32 p54spi_read32(struct p54s_priv *priv, u8 addr) { __le32 val; p54spi_spi_read(priv, addr, &val, sizeof(val)); return le32_to_cpu(val); } static inline void p54spi_write16(struct p54s_priv *priv, u8 addr, __le16 val) { p54spi_spi_write(priv, addr, &val, sizeof(val)); } static inline void p54spi_write32(struct p54s_priv *priv, u8 addr, __le32 val) { p54spi_spi_write(priv, addr, &val, sizeof(val)); } static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, u32 bits) { int i; for (i = 0; i < 2000; i++) { u32 buffer = p54spi_read32(priv, reg); if ((buffer & bits) == bits) return 1; } return 0; } static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, const void *buf, size_t len) { if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, HOST_ALLOWED)) { dev_err(&priv->spi->dev, "spi_write_dma not allowed " "to DMA write.\n"); return -EAGAIN; } p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len)); p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base); p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len); return 0; } static int p54spi_request_firmware(struct ieee80211_hw *dev) { struct p54s_priv *priv = dev->priv; int ret; /* FIXME: should driver use it's own struct device? */ ret = request_firmware(&priv->firmware, "3826.arm", &priv->spi->dev); if (ret < 0) { dev_err(&priv->spi->dev, "request_firmware() failed: %d", ret); return ret; } ret = p54_parse_firmware(dev, priv->firmware); if (ret) { release_firmware(priv->firmware); return ret; } return 0; } static int p54spi_request_eeprom(struct ieee80211_hw *dev) { struct p54s_priv *priv = dev->priv; const struct firmware *eeprom; int ret; /* * allow users to customize their eeprom. */ ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev); if (ret < 0) { #ifdef CONFIG_P54_SPI_DEFAULT_EEPROM dev_info(&priv->spi->dev, "loading default eeprom...\n"); ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom, sizeof(p54spi_eeprom)); #else dev_err(&priv->spi->dev, "Failed to request user eeprom\n"); #endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */ } else { dev_info(&priv->spi->dev, "loading user eeprom...\n"); ret = p54_parse_eeprom(dev, (void *) eeprom->data, (int)eeprom->size); release_firmware(eeprom); } return ret; } static int p54spi_upload_firmware(struct ieee80211_hw *dev) { struct p54s_priv *priv = dev->priv; unsigned long fw_len, _fw_len; unsigned int offset = 0; int err = 0; u8 *fw; fw_len = priv->firmware->size; fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL); if (!fw) return -ENOMEM; /* stop the device */ p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET | SPI_CTRL_STAT_START_HALTED)); msleep(TARGET_BOOT_SLEEP); p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_START_HALTED)); msleep(TARGET_BOOT_SLEEP); while (fw_len > 0) { _fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE); err = p54spi_spi_write_dma(priv, cpu_to_le32( ISL38XX_DEV_FIRMWARE_ADDR + offset), (fw + offset), _fw_len); if (err < 0) goto out; fw_len -= _fw_len; offset += _fw_len; } BUG_ON(fw_len != 0); /* enable host interrupts */ p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, cpu_to_le32(SPI_HOST_INTS_DEFAULT)); /* boot the device */ p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET | SPI_CTRL_STAT_RAM_BOOT)); msleep(TARGET_BOOT_SLEEP); p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT)); msleep(TARGET_BOOT_SLEEP); out: kfree(fw); return err; } static void p54spi_power_off(struct p54s_priv *priv) { disable_irq(gpio_to_irq(p54spi_gpio_irq)); gpio_set_value(p54spi_gpio_power, 0); } static void p54spi_power_on(struct p54s_priv *priv) { gpio_set_value(p54spi_gpio_power, 1); enable_irq(gpio_to_irq(p54spi_gpio_irq)); /* * need to wait a while before device can be accessed, the length * is just a guess */ msleep(10); } static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val) { p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val)); } static int p54spi_wakeup(struct p54s_priv *priv) { /* wake the chip */ p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, cpu_to_le32(SPI_TARGET_INT_WAKEUP)); /* And wait for the READY interrupt */ if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, SPI_HOST_INT_READY)) { dev_err(&priv->spi->dev, "INT_READY timeout\n"); return -EBUSY; } p54spi_int_ack(priv, SPI_HOST_INT_READY); return 0; } static inline void p54spi_sleep(struct p54s_priv *priv) { p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, cpu_to_le32(SPI_TARGET_INT_SLEEP)); } static void p54spi_int_ready(struct p54s_priv *priv) { p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, cpu_to_le32( SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE)); switch (priv->fw_state) { case FW_STATE_BOOTING: priv->fw_state = FW_STATE_READY; complete(&priv->fw_comp); break; case FW_STATE_RESETTING: priv->fw_state = FW_STATE_READY; /* TODO: reinitialize state */ break; default: break; } } static int p54spi_rx(struct p54s_priv *priv) { struct sk_buff *skb; u16 len; u16 rx_head[2]; #define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16)) if (p54spi_wakeup(priv) < 0) return -EBUSY; /* Read data size and first data word in one SPI transaction * This is workaround for firmware/DMA bug, * when first data word gets lost under high load. */ p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head)); len = rx_head[0]; if (len == 0) { p54spi_sleep(priv); dev_err(&priv->spi->dev, "rx request of zero bytes\n"); return 0; } /* Firmware may insert up to 4 padding bytes after the lmac header, * but it does not amend the size of SPI data transfer. * Such packets has correct data size in header, thus referencing * past the end of allocated skb. Reserve extra 4 bytes for this case */ skb = dev_alloc_skb(len + 4); if (!skb) { p54spi_sleep(priv); dev_err(&priv->spi->dev, "could not alloc skb"); return -ENOMEM; } if (len <= READAHEAD_SZ) { memcpy(skb_put(skb, len), rx_head + 1, len); } else { memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ); p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, skb_put(skb, len - READAHEAD_SZ), len - READAHEAD_SZ); } p54spi_sleep(priv); /* Put additional bytes to compensate for the possible * alignment-caused truncation */ skb_put(skb, 4); if (p54_rx(priv->hw, skb) == 0) dev_kfree_skb(skb); return 0; } static irqreturn_t p54spi_interrupt(int irq, void *config) { struct spi_device *spi = config; struct p54s_priv *priv = dev_get_drvdata(&spi->dev); ieee80211_queue_work(priv->hw, &priv->work); return IRQ_HANDLED; } static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; int ret = 0; if (p54spi_wakeup(priv) < 0) return -EBUSY; ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len); if (ret < 0) goto out; if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, SPI_HOST_INT_WR_READY)) { dev_err(&priv->spi->dev, "WR_READY timeout\n"); ret = -EAGAIN; goto out; } p54spi_int_ack(priv, SPI_HOST_INT_WR_READY); if (FREE_AFTER_TX(skb)) p54_free_skb(priv->hw, skb); out: p54spi_sleep(priv); return ret; } static int p54spi_wq_tx(struct p54s_priv *priv) { struct p54s_tx_info *entry; struct sk_buff *skb; struct ieee80211_tx_info *info; struct p54_tx_info *minfo; struct p54s_tx_info *dinfo; unsigned long flags; int ret = 0; spin_lock_irqsave(&priv->tx_lock, flags); while (!list_empty(&priv->tx_pending)) { entry = list_entry(priv->tx_pending.next, struct p54s_tx_info, tx_list); list_del_init(&entry->tx_list); spin_unlock_irqrestore(&priv->tx_lock, flags); dinfo = container_of((void *) entry, struct p54s_tx_info, tx_list); minfo = container_of((void *) dinfo, struct p54_tx_info, data); info = container_of((void *) minfo, struct ieee80211_tx_info, rate_driver_data); skb = container_of((void *) info, struct sk_buff, cb); ret = p54spi_tx_frame(priv, skb); if (ret < 0) { p54_free_skb(priv->hw, skb); return ret; } spin_lock_irqsave(&priv->tx_lock, flags); } spin_unlock_irqrestore(&priv->tx_lock, flags); return ret; } static void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54s_priv *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct p54_tx_info *mi = (struct p54_tx_info *) info->rate_driver_data; struct p54s_tx_info *di = (struct p54s_tx_info *) mi->data; unsigned long flags; BUILD_BUG_ON(sizeof(*di) > sizeof((mi->data))); spin_lock_irqsave(&priv->tx_lock, flags); list_add_tail(&di->tx_list, &priv->tx_pending); spin_unlock_irqrestore(&priv->tx_lock, flags); ieee80211_queue_work(priv->hw, &priv->work); } static void p54spi_work(struct work_struct *work) { struct p54s_priv *priv = container_of(work, struct p54s_priv, work); u32 ints; int ret; mutex_lock(&priv->mutex); if (priv->fw_state == FW_STATE_OFF) goto out; ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); if (ints & SPI_HOST_INT_READY) { p54spi_int_ready(priv); p54spi_int_ack(priv, SPI_HOST_INT_READY); } if (priv->fw_state != FW_STATE_READY) goto out; if (ints & SPI_HOST_INT_UPDATE) { p54spi_int_ack(priv, SPI_HOST_INT_UPDATE); ret = p54spi_rx(priv); if (ret < 0) goto out; } if (ints & SPI_HOST_INT_SW_UPDATE) { p54spi_int_ack(priv, SPI_HOST_INT_SW_UPDATE); ret = p54spi_rx(priv); if (ret < 0) goto out; } ret = p54spi_wq_tx(priv); out: mutex_unlock(&priv->mutex); } static int p54spi_op_start(struct ieee80211_hw *dev) { struct p54s_priv *priv = dev->priv; unsigned long timeout; int ret = 0; if (mutex_lock_interruptible(&priv->mutex)) { ret = -EINTR; goto out; } priv->fw_state = FW_STATE_BOOTING; p54spi_power_on(priv); ret = p54spi_upload_firmware(dev); if (ret < 0) { p54spi_power_off(priv); goto out_unlock; } mutex_unlock(&priv->mutex); timeout = msecs_to_jiffies(2000); timeout = wait_for_completion_interruptible_timeout(&priv->fw_comp, timeout); if (!timeout) { dev_err(&priv->spi->dev, "firmware boot failed"); p54spi_power_off(priv); ret = -1; goto out; } if (mutex_lock_interruptible(&priv->mutex)) { ret = -EINTR; p54spi_power_off(priv); goto out; } WARN_ON(priv->fw_state != FW_STATE_READY); out_unlock: mutex_unlock(&priv->mutex); out: return ret; } static void p54spi_op_stop(struct ieee80211_hw *dev) { struct p54s_priv *priv = dev->priv; unsigned long flags; mutex_lock(&priv->mutex); WARN_ON(priv->fw_state != FW_STATE_READY); p54spi_power_off(priv); spin_lock_irqsave(&priv->tx_lock, flags); INIT_LIST_HEAD(&priv->tx_pending); spin_unlock_irqrestore(&priv->tx_lock, flags); priv->fw_state = FW_STATE_OFF; mutex_unlock(&priv->mutex); cancel_work_sync(&priv->work); } static int __devinit p54spi_probe(struct spi_device *spi) { struct p54s_priv *priv = NULL; struct ieee80211_hw *hw; int ret = -EINVAL; hw = p54_init_common(sizeof(*priv)); if (!hw) { dev_err(&spi->dev, "could not alloc ieee80211_hw"); return -ENOMEM; } priv = hw->priv; priv->hw = hw; dev_set_drvdata(&spi->dev, priv); priv->spi = spi; spi->bits_per_word = 16; spi->max_speed_hz = 24000000; ret = spi_setup(spi); if (ret < 0) { dev_err(&priv->spi->dev, "spi_setup failed"); goto err_free; } ret = gpio_request(p54spi_gpio_power, "p54spi power"); if (ret < 0) { dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret); goto err_free; } ret = gpio_request(p54spi_gpio_irq, "p54spi irq"); if (ret < 0) { dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret); goto err_free_gpio_power; } gpio_direction_output(p54spi_gpio_power, 0); gpio_direction_input(p54spi_gpio_irq); ret = request_irq(gpio_to_irq(p54spi_gpio_irq), p54spi_interrupt, IRQF_DISABLED, "p54spi", priv->spi); if (ret < 0) { dev_err(&priv->spi->dev, "request_irq() failed"); goto err_free_gpio_irq; } irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING); disable_irq(gpio_to_irq(p54spi_gpio_irq)); INIT_WORK(&priv->work, p54spi_work); init_completion(&priv->fw_comp); INIT_LIST_HEAD(&priv->tx_pending); mutex_init(&priv->mutex); spin_lock_init(&priv->tx_lock); SET_IEEE80211_DEV(hw, &spi->dev); priv->common.open = p54spi_op_start; priv->common.stop = p54spi_op_stop; priv->common.tx = p54spi_op_tx; ret = p54spi_request_firmware(hw); if (ret < 0) goto err_free_common; ret = p54spi_request_eeprom(hw); if (ret) goto err_free_common; ret = p54_register_common(hw, &priv->spi->dev); if (ret) goto err_free_common; return 0; err_free_common: free_irq(gpio_to_irq(p54spi_gpio_irq), spi); err_free_gpio_irq: gpio_free(p54spi_gpio_irq); err_free_gpio_power: gpio_free(p54spi_gpio_power); err_free: p54_free_common(priv->hw); return ret; } static int __devexit p54spi_remove(struct spi_device *spi) { struct p54s_priv *priv = dev_get_drvdata(&spi->dev); p54_unregister_common(priv->hw); free_irq(gpio_to_irq(p54spi_gpio_irq), spi); gpio_free(p54spi_gpio_power); gpio_free(p54spi_gpio_irq); release_firmware(priv->firmware); mutex_destroy(&priv->mutex); p54_free_common(priv->hw); return 0; } static struct spi_driver p54spi_driver = { .driver = { .name = "p54spi", .owner = THIS_MODULE, }, .probe = p54spi_probe, .remove = __devexit_p(p54spi_remove), }; static int __init p54spi_init(void) { int ret; ret = spi_register_driver(&p54spi_driver); if (ret < 0) { printk(KERN_ERR "failed to register SPI driver: %d", ret); goto out; } out: return ret; } static void __exit p54spi_exit(void) { spi_unregister_driver(&p54spi_driver); } module_init(p54spi_init); module_exit(p54spi_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Lamparter "); MODULE_ALIAS("spi:cx3110x"); MODULE_ALIAS("spi:p54spi"); MODULE_ALIAS("spi:stlc45xx"); compat-drivers-2012-09-18/drivers/net/wireless/p54/p54pci.h0000644000175000017500000000623012026211315022347 0ustar mcgrofmcgrof#ifndef P54PCI_H #define P54PCI_H #include /* * Defines for PCI based mac80211 Prism54 driver * * Copyright (c) 2006, Michael Wu * * Based on the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* Device Interrupt register bits */ #define ISL38XX_DEV_INT_RESET 0x0001 #define ISL38XX_DEV_INT_UPDATE 0x0002 #define ISL38XX_DEV_INT_WAKEUP 0x0008 #define ISL38XX_DEV_INT_SLEEP 0x0010 #define ISL38XX_DEV_INT_ABORT 0x0020 /* these two only used in USB */ #define ISL38XX_DEV_INT_DATA 0x0040 #define ISL38XX_DEV_INT_MGMT 0x0080 #define ISL38XX_DEV_INT_PCIUART_CTS 0x4000 #define ISL38XX_DEV_INT_PCIUART_DR 0x8000 /* Interrupt Identification/Acknowledge/Enable register bits */ #define ISL38XX_INT_IDENT_UPDATE 0x0002 #define ISL38XX_INT_IDENT_INIT 0x0004 #define ISL38XX_INT_IDENT_WAKEUP 0x0008 #define ISL38XX_INT_IDENT_SLEEP 0x0010 #define ISL38XX_INT_IDENT_PCIUART_CTS 0x4000 #define ISL38XX_INT_IDENT_PCIUART_DR 0x8000 /* Control/Status register bits */ #define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200 #define ISL38XX_CTRL_STAT_CLKRUN 0x00800000 #define ISL38XX_CTRL_STAT_RESET 0x10000000 #define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000 #define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000 #define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000 struct p54p_csr { __le32 dev_int; u8 unused_1[12]; __le32 int_ident; __le32 int_ack; __le32 int_enable; u8 unused_2[4]; union { __le32 ring_control_base; __le32 gen_purp_com[2]; }; u8 unused_3[8]; __le32 direct_mem_base; u8 unused_4[44]; __le32 dma_addr; __le32 dma_len; __le32 dma_ctrl; u8 unused_5[12]; __le32 ctrl_stat; u8 unused_6[1924]; u8 cardbus_cis[0x800]; u8 direct_mem_win[0x1000]; } __packed; /* usb backend only needs the register defines above */ #ifndef P54USB_H struct p54p_desc { __le32 host_addr; __le32 device_addr; __le16 len; __le16 flags; } __packed; struct p54p_ring_control { __le32 host_idx[4]; __le32 device_idx[4]; struct p54p_desc rx_data[8]; struct p54p_desc tx_data[32]; struct p54p_desc rx_mgmt[4]; struct p54p_desc tx_mgmt[4]; } __packed; #define P54P_READ(r) (__force __le32)__raw_readl(&priv->map->r) #define P54P_WRITE(r, val) __raw_writel((__force u32)(__le32)(val), &priv->map->r) struct p54p_priv { struct p54_common common; struct pci_dev *pdev; struct p54p_csr __iomem *map; struct tasklet_struct tasklet; const struct firmware *firmware; spinlock_t lock; struct p54p_ring_control *ring_control; dma_addr_t ring_control_dma; u32 rx_idx_data, tx_idx_data; u32 rx_idx_mgmt, tx_idx_mgmt; struct sk_buff *rx_buf_data[8]; struct sk_buff *rx_buf_mgmt[4]; struct sk_buff *tx_buf_data[32]; struct sk_buff *tx_buf_mgmt[4]; struct completion boot_comp; struct completion fw_loaded; }; #endif /* P54USB_H */ #endif /* P54PCI_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/p54.h0000644000175000017500000001427112026211315021657 0ustar mcgrofmcgrof/* * Shared defines for all mac80211 Prism54 code * * Copyright (c) 2006, Michael Wu * * Based on the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef P54_H #define P54_H #ifdef CONFIG_P54_LEDS #include #endif /* CONFIG_P54_LEDS */ #define ISL38XX_DEV_FIRMWARE_ADDR 0x20000 #define BR_CODE_MIN 0x80000000 #define BR_CODE_COMPONENT_ID 0x80000001 #define BR_CODE_COMPONENT_VERSION 0x80000002 #define BR_CODE_DEPENDENT_IF 0x80000003 #define BR_CODE_EXPOSED_IF 0x80000004 #define BR_CODE_DESCR 0x80000101 #define BR_CODE_MAX 0x8FFFFFFF #define BR_CODE_END_OF_BRA 0xFF0000FF #define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF struct bootrec { __le32 code; __le32 len; u32 data[10]; } __packed; /* Interface role definitions */ #define BR_INTERFACE_ROLE_SERVER 0x0000 #define BR_INTERFACE_ROLE_CLIENT 0x8000 #define BR_DESC_PRIV_CAP_WEP BIT(0) #define BR_DESC_PRIV_CAP_TKIP BIT(1) #define BR_DESC_PRIV_CAP_MICHAEL BIT(2) #define BR_DESC_PRIV_CAP_CCX_CP BIT(3) #define BR_DESC_PRIV_CAP_CCX_MIC BIT(4) #define BR_DESC_PRIV_CAP_AESCCMP BIT(5) struct bootrec_desc { __le16 modes; __le16 flags; __le32 rx_start; __le32 rx_end; u8 headroom; u8 tailroom; u8 tx_queues; u8 tx_depth; u8 privacy_caps; u8 rx_keycache_size; u8 time_size; u8 padding; u8 rates[16]; u8 padding2[4]; __le16 rx_mtu; } __packed; #define FW_FMAC 0x464d4143 #define FW_LM86 0x4c4d3836 #define FW_LM87 0x4c4d3837 #define FW_LM20 0x4c4d3230 struct bootrec_comp_id { __le32 fw_variant; } __packed; struct bootrec_comp_ver { char fw_version[24]; } __packed; struct bootrec_end { __le16 crc; u8 padding[2]; u8 md5[16]; } __packed; /* provide 16 bytes for the transport back-end */ #define P54_TX_INFO_DATA_SIZE 16 /* stored in ieee80211_tx_info's rate_driver_data */ struct p54_tx_info { u32 start_addr; u32 end_addr; union { void *data[P54_TX_INFO_DATA_SIZE / sizeof(void *)]; struct { u32 extra_len; }; }; }; #define P54_MAX_CTRL_FRAME_LEN 0x1000 #define P54_SET_QUEUE(queue, ai_fs, cw_min, cw_max, _txop) \ do { \ queue.aifs = cpu_to_le16(ai_fs); \ queue.cwmin = cpu_to_le16(cw_min); \ queue.cwmax = cpu_to_le16(cw_max); \ queue.txop = cpu_to_le16(_txop); \ } while (0) struct p54_edcf_queue_param { __le16 aifs; __le16 cwmin; __le16 cwmax; __le16 txop; } __packed; struct p54_rssi_db_entry { u16 freq; s16 mul; s16 add; s16 longbow_unkn; s16 longbow_unk2; }; struct p54_cal_database { size_t entries; size_t entry_size; size_t offset; size_t len; u8 data[0]; }; #define EEPROM_READBACK_LEN 0x3fc enum fw_state { FW_STATE_OFF, FW_STATE_BOOTING, FW_STATE_READY, FW_STATE_RESET, FW_STATE_RESETTING, }; #ifdef CONFIG_P54_LEDS #define P54_LED_MAX_NAME_LEN 31 struct p54_led_dev { struct ieee80211_hw *hw_dev; struct led_classdev led_dev; char name[P54_LED_MAX_NAME_LEN + 1]; unsigned int toggled; unsigned int index; unsigned int registered; }; #endif /* CONFIG_P54_LEDS */ struct p54_tx_queue_stats { unsigned int len; unsigned int limit; unsigned int count; }; struct p54_common { struct ieee80211_hw *hw; struct ieee80211_vif *vif; void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb); int (*open)(struct ieee80211_hw *dev); void (*stop)(struct ieee80211_hw *dev); struct sk_buff_head tx_pending; struct sk_buff_head tx_queue; struct mutex conf_mutex; bool registered; /* memory management (as seen by the firmware) */ u32 rx_start; u32 rx_end; u16 rx_mtu; u8 headroom; u8 tailroom; /* firmware/hardware info */ unsigned int tx_hdr_len; unsigned int fw_var; unsigned int fw_interface; u8 version; /* (e)DCF / QOS state */ bool use_short_slot; spinlock_t tx_stats_lock; struct p54_tx_queue_stats tx_stats[8]; struct p54_edcf_queue_param qos_params[8]; /* Radio data */ u16 rxhw; u8 rx_diversity_mask; u8 tx_diversity_mask; unsigned int output_power; struct p54_rssi_db_entry *cur_rssi; struct ieee80211_channel *curchan; struct survey_info *survey; unsigned int chan_num; struct completion stat_comp; bool update_stats; struct { unsigned int timestamp; unsigned int cached_cca; unsigned int cached_tx; unsigned int cached_rssi; u64 active; u64 cca; u64 tx; u64 rssi; } survey_raw; int noise; /* calibration, output power limit and rssi<->dBm conversation data */ struct pda_iq_autocal_entry *iq_autocal; unsigned int iq_autocal_len; struct p54_cal_database *curve_data; struct p54_cal_database *output_limit; struct p54_cal_database *rssi_db; struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS]; /* BBP/MAC state */ u8 mac_addr[ETH_ALEN]; u8 bssid[ETH_ALEN]; u8 mc_maclist[4][ETH_ALEN]; u16 wakeup_timer; unsigned int filter_flags; int mc_maclist_num; int mode; u32 tsf_low32, tsf_high32; u32 basic_rate_mask; u16 aid; u8 coverage_class; bool phy_idle; bool phy_ps; bool powersave_override; __le32 beacon_req_id; struct completion beacon_comp; /* cryptographic engine information */ u8 privacy_caps; u8 rx_keycache_size; unsigned long *used_rxkeys; /* LED management */ #ifdef CONFIG_P54_LEDS struct p54_led_dev leds[4]; struct delayed_work led_work; #endif /* CONFIG_P54_LEDS */ u16 softled_state; /* bit field of glowing LEDs */ /* statistics */ struct ieee80211_low_level_stats stats; struct delayed_work work; /* eeprom handling */ void *eeprom; struct completion eeprom_comp; struct mutex eeprom_mutex; }; /* interfaces for the drivers */ int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb); void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb); int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw); int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len); int p54_read_eeprom(struct ieee80211_hw *dev); struct ieee80211_hw *p54_init_common(size_t priv_data_len); int p54_register_common(struct ieee80211_hw *dev, struct device *pdev); void p54_free_common(struct ieee80211_hw *dev); void p54_unregister_common(struct ieee80211_hw *dev); #endif /* P54_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/net2280.h0000644000175000017500000003571612026211315022360 0ustar mcgrofmcgrof#ifndef NET2280_H #define NET2280_H /* * NetChip 2280 high/full speed USB device controller. * Unlike many such controllers, this one talks PCI. */ /* * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) * Copyright (C) 2003 David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*-------------------------------------------------------------------------*/ /* NET2280 MEMORY MAPPED REGISTERS * * The register layout came from the chip documentation, and the bit * number definitions were extracted from chip specification. * * Use the shift operator ('<<') to build bit masks, with readl/writel * to access the registers through PCI. */ /* main registers, BAR0 + 0x0000 */ struct net2280_regs { /* offset 0x0000 */ __le32 devinit; #define LOCAL_CLOCK_FREQUENCY 8 #define FORCE_PCI_RESET 7 #define PCI_ID 6 #define PCI_ENABLE 5 #define FIFO_SOFT_RESET 4 #define CFG_SOFT_RESET 3 #define PCI_SOFT_RESET 2 #define USB_SOFT_RESET 1 #define M8051_RESET 0 __le32 eectl; #define EEPROM_ADDRESS_WIDTH 23 #define EEPROM_CHIP_SELECT_ACTIVE 22 #define EEPROM_PRESENT 21 #define EEPROM_VALID 20 #define EEPROM_BUSY 19 #define EEPROM_CHIP_SELECT_ENABLE 18 #define EEPROM_BYTE_READ_START 17 #define EEPROM_BYTE_WRITE_START 16 #define EEPROM_READ_DATA 8 #define EEPROM_WRITE_DATA 0 __le32 eeclkfreq; u32 _unused0; /* offset 0x0010 */ __le32 pciirqenb0; /* interrupt PCI master ... */ #define SETUP_PACKET_INTERRUPT_ENABLE 7 #define ENDPOINT_F_INTERRUPT_ENABLE 6 #define ENDPOINT_E_INTERRUPT_ENABLE 5 #define ENDPOINT_D_INTERRUPT_ENABLE 4 #define ENDPOINT_C_INTERRUPT_ENABLE 3 #define ENDPOINT_B_INTERRUPT_ENABLE 2 #define ENDPOINT_A_INTERRUPT_ENABLE 1 #define ENDPOINT_0_INTERRUPT_ENABLE 0 __le32 pciirqenb1; #define PCI_INTERRUPT_ENABLE 31 #define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 #define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 #define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 #define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 #define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 #define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE 18 #define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 #define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 #define GPIO_INTERRUPT_ENABLE 13 #define DMA_D_INTERRUPT_ENABLE 12 #define DMA_C_INTERRUPT_ENABLE 11 #define DMA_B_INTERRUPT_ENABLE 10 #define DMA_A_INTERRUPT_ENABLE 9 #define EEPROM_DONE_INTERRUPT_ENABLE 8 #define VBUS_INTERRUPT_ENABLE 7 #define CONTROL_STATUS_INTERRUPT_ENABLE 6 #define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 #define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 #define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 #define RESUME_INTERRUPT_ENABLE 1 #define SOF_INTERRUPT_ENABLE 0 __le32 cpu_irqenb0; /* ... or onboard 8051 */ #define SETUP_PACKET_INTERRUPT_ENABLE 7 #define ENDPOINT_F_INTERRUPT_ENABLE 6 #define ENDPOINT_E_INTERRUPT_ENABLE 5 #define ENDPOINT_D_INTERRUPT_ENABLE 4 #define ENDPOINT_C_INTERRUPT_ENABLE 3 #define ENDPOINT_B_INTERRUPT_ENABLE 2 #define ENDPOINT_A_INTERRUPT_ENABLE 1 #define ENDPOINT_0_INTERRUPT_ENABLE 0 __le32 cpu_irqenb1; #define CPU_INTERRUPT_ENABLE 31 #define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 #define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 #define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 #define PCI_INTA_INTERRUPT_ENABLE 24 #define PCI_PME_INTERRUPT_ENABLE 23 #define PCI_SERR_INTERRUPT_ENABLE 22 #define PCI_PERR_INTERRUPT_ENABLE 21 #define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 #define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 #define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 #define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 #define GPIO_INTERRUPT_ENABLE 13 #define DMA_D_INTERRUPT_ENABLE 12 #define DMA_C_INTERRUPT_ENABLE 11 #define DMA_B_INTERRUPT_ENABLE 10 #define DMA_A_INTERRUPT_ENABLE 9 #define EEPROM_DONE_INTERRUPT_ENABLE 8 #define VBUS_INTERRUPT_ENABLE 7 #define CONTROL_STATUS_INTERRUPT_ENABLE 6 #define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 #define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 #define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 #define RESUME_INTERRUPT_ENABLE 1 #define SOF_INTERRUPT_ENABLE 0 /* offset 0x0020 */ u32 _unused1; __le32 usbirqenb1; #define USB_INTERRUPT_ENABLE 31 #define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 #define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 #define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 #define PCI_INTA_INTERRUPT_ENABLE 24 #define PCI_PME_INTERRUPT_ENABLE 23 #define PCI_SERR_INTERRUPT_ENABLE 22 #define PCI_PERR_INTERRUPT_ENABLE 21 #define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 #define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 #define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 #define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 #define GPIO_INTERRUPT_ENABLE 13 #define DMA_D_INTERRUPT_ENABLE 12 #define DMA_C_INTERRUPT_ENABLE 11 #define DMA_B_INTERRUPT_ENABLE 10 #define DMA_A_INTERRUPT_ENABLE 9 #define EEPROM_DONE_INTERRUPT_ENABLE 8 #define VBUS_INTERRUPT_ENABLE 7 #define CONTROL_STATUS_INTERRUPT_ENABLE 6 #define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 #define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 #define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 #define RESUME_INTERRUPT_ENABLE 1 #define SOF_INTERRUPT_ENABLE 0 __le32 irqstat0; #define INTA_ASSERTED 12 #define SETUP_PACKET_INTERRUPT 7 #define ENDPOINT_F_INTERRUPT 6 #define ENDPOINT_E_INTERRUPT 5 #define ENDPOINT_D_INTERRUPT 4 #define ENDPOINT_C_INTERRUPT 3 #define ENDPOINT_B_INTERRUPT 2 #define ENDPOINT_A_INTERRUPT 1 #define ENDPOINT_0_INTERRUPT 0 __le32 irqstat1; #define POWER_STATE_CHANGE_INTERRUPT 27 #define PCI_ARBITER_TIMEOUT_INTERRUPT 26 #define PCI_PARITY_ERROR_INTERRUPT 25 #define PCI_INTA_INTERRUPT 24 #define PCI_PME_INTERRUPT 23 #define PCI_SERR_INTERRUPT 22 #define PCI_PERR_INTERRUPT 21 #define PCI_MASTER_ABORT_RECEIVED_INTERRUPT 20 #define PCI_TARGET_ABORT_RECEIVED_INTERRUPT 19 #define PCI_RETRY_ABORT_INTERRUPT 17 #define PCI_MASTER_CYCLE_DONE_INTERRUPT 16 #define GPIO_INTERRUPT 13 #define DMA_D_INTERRUPT 12 #define DMA_C_INTERRUPT 11 #define DMA_B_INTERRUPT 10 #define DMA_A_INTERRUPT 9 #define EEPROM_DONE_INTERRUPT 8 #define VBUS_INTERRUPT 7 #define CONTROL_STATUS_INTERRUPT 6 #define ROOT_PORT_RESET_INTERRUPT 4 #define SUSPEND_REQUEST_INTERRUPT 3 #define SUSPEND_REQUEST_CHANGE_INTERRUPT 2 #define RESUME_INTERRUPT 1 #define SOF_INTERRUPT 0 /* offset 0x0030 */ __le32 idxaddr; __le32 idxdata; __le32 fifoctl; #define PCI_BASE2_RANGE 16 #define IGNORE_FIFO_AVAILABILITY 3 #define PCI_BASE2_SELECT 2 #define FIFO_CONFIGURATION_SELECT 0 u32 _unused2; /* offset 0x0040 */ __le32 memaddr; #define START 28 #define DIRECTION 27 #define FIFO_DIAGNOSTIC_SELECT 24 #define MEMORY_ADDRESS 0 __le32 memdata0; __le32 memdata1; u32 _unused3; /* offset 0x0050 */ __le32 gpioctl; #define GPIO3_LED_SELECT 12 #define GPIO3_INTERRUPT_ENABLE 11 #define GPIO2_INTERRUPT_ENABLE 10 #define GPIO1_INTERRUPT_ENABLE 9 #define GPIO0_INTERRUPT_ENABLE 8 #define GPIO3_OUTPUT_ENABLE 7 #define GPIO2_OUTPUT_ENABLE 6 #define GPIO1_OUTPUT_ENABLE 5 #define GPIO0_OUTPUT_ENABLE 4 #define GPIO3_DATA 3 #define GPIO2_DATA 2 #define GPIO1_DATA 1 #define GPIO0_DATA 0 __le32 gpiostat; #define GPIO3_INTERRUPT 3 #define GPIO2_INTERRUPT 2 #define GPIO1_INTERRUPT 1 #define GPIO0_INTERRUPT 0 } __packed; /* usb control, BAR0 + 0x0080 */ struct net2280_usb_regs { /* offset 0x0080 */ __le32 stdrsp; #define STALL_UNSUPPORTED_REQUESTS 31 #define SET_TEST_MODE 16 #define GET_OTHER_SPEED_CONFIGURATION 15 #define GET_DEVICE_QUALIFIER 14 #define SET_ADDRESS 13 #define ENDPOINT_SET_CLEAR_HALT 12 #define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP 11 #define GET_STRING_DESCRIPTOR_2 10 #define GET_STRING_DESCRIPTOR_1 9 #define GET_STRING_DESCRIPTOR_0 8 #define GET_SET_INTERFACE 6 #define GET_SET_CONFIGURATION 5 #define GET_CONFIGURATION_DESCRIPTOR 4 #define GET_DEVICE_DESCRIPTOR 3 #define GET_ENDPOINT_STATUS 2 #define GET_INTERFACE_STATUS 1 #define GET_DEVICE_STATUS 0 __le32 prodvendid; #define PRODUCT_ID 16 #define VENDOR_ID 0 __le32 relnum; __le32 usbctl; #define SERIAL_NUMBER_INDEX 16 #define PRODUCT_ID_STRING_ENABLE 13 #define VENDOR_ID_STRING_ENABLE 12 #define USB_ROOT_PORT_WAKEUP_ENABLE 11 #define VBUS_PIN 10 #define TIMED_DISCONNECT 9 #define SUSPEND_IMMEDIATELY 7 #define SELF_POWERED_USB_DEVICE 6 #define REMOTE_WAKEUP_SUPPORT 5 #define PME_POLARITY 4 #define USB_DETECT_ENABLE 3 #define PME_WAKEUP_ENABLE 2 #define DEVICE_REMOTE_WAKEUP_ENABLE 1 #define SELF_POWERED_STATUS 0 /* offset 0x0090 */ __le32 usbstat; #define HIGH_SPEED 7 #define FULL_SPEED 6 #define GENERATE_RESUME 5 #define GENERATE_DEVICE_REMOTE_WAKEUP 4 __le32 xcvrdiag; #define FORCE_HIGH_SPEED_MODE 31 #define FORCE_FULL_SPEED_MODE 30 #define USB_TEST_MODE 24 #define LINE_STATE 16 #define TRANSCEIVER_OPERATION_MODE 2 #define TRANSCEIVER_SELECT 1 #define TERMINATION_SELECT 0 __le32 setup0123; __le32 setup4567; /* offset 0x0090 */ u32 _unused0; __le32 ouraddr; #define FORCE_IMMEDIATE 7 #define OUR_USB_ADDRESS 0 __le32 ourconfig; } __packed; /* pci control, BAR0 + 0x0100 */ struct net2280_pci_regs { /* offset 0x0100 */ __le32 pcimstctl; #define PCI_ARBITER_PARK_SELECT 13 #define PCI_MULTI LEVEL_ARBITER 12 #define PCI_RETRY_ABORT_ENABLE 11 #define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE 10 #define DMA_READ_MULTIPLE_ENABLE 9 #define DMA_READ_LINE_ENABLE 8 #define PCI_MASTER_COMMAND_SELECT 6 #define MEM_READ_OR_WRITE 0 #define IO_READ_OR_WRITE 1 #define CFG_READ_OR_WRITE 2 #define PCI_MASTER_START 5 #define PCI_MASTER_READ_WRITE 4 #define PCI_MASTER_WRITE 0 #define PCI_MASTER_READ 1 #define PCI_MASTER_BYTE_WRITE_ENABLES 0 __le32 pcimstaddr; __le32 pcimstdata; __le32 pcimststat; #define PCI_ARBITER_CLEAR 2 #define PCI_EXTERNAL_ARBITER 1 #define PCI_HOST_MODE 0 } __packed; /* dma control, BAR0 + 0x0180 ... array of four structs like this, * for channels 0..3. see also struct net2280_dma: descriptor * that can be loaded into some of these registers. */ struct net2280_dma_regs { /* [11.7] */ /* offset 0x0180, 0x01a0, 0x01c0, 0x01e0, */ __le32 dmactl; #define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE 25 #define DMA_CLEAR_COUNT_ENABLE 21 #define DESCRIPTOR_POLLING_RATE 19 #define POLL_CONTINUOUS 0 #define POLL_1_USEC 1 #define POLL_100_USEC 2 #define POLL_1_MSEC 3 #define DMA_VALID_BIT_POLLING_ENABLE 18 #define DMA_VALID_BIT_ENABLE 17 #define DMA_SCATTER_GATHER_ENABLE 16 #define DMA_OUT_AUTO_START_ENABLE 4 #define DMA_PREEMPT_ENABLE 3 #define DMA_FIFO_VALIDATE 2 #define DMA_ENABLE 1 #define DMA_ADDRESS_HOLD 0 __le32 dmastat; #define DMA_SCATTER_GATHER_DONE_INTERRUPT 25 #define DMA_TRANSACTION_DONE_INTERRUPT 24 #define DMA_ABORT 1 #define DMA_START 0 u32 _unused0[2]; /* offset 0x0190, 0x01b0, 0x01d0, 0x01f0, */ __le32 dmacount; #define VALID_BIT 31 #define DMA_DIRECTION 30 #define DMA_DONE_INTERRUPT_ENABLE 29 #define END_OF_CHAIN 28 #define DMA_BYTE_COUNT_MASK ((1<<24)-1) #define DMA_BYTE_COUNT 0 __le32 dmaaddr; __le32 dmadesc; u32 _unused1; } __packed; /* dedicated endpoint registers, BAR0 + 0x0200 */ struct net2280_dep_regs { /* [11.8] */ /* offset 0x0200, 0x0210, 0x220, 0x230, 0x240 */ __le32 dep_cfg; /* offset 0x0204, 0x0214, 0x224, 0x234, 0x244 */ __le32 dep_rsp; u32 _unused[2]; } __packed; /* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs * like this, for ep0 then the configurable endpoints A..F * ep0 reserved for control; E and F have only 64 bytes of fifo */ struct net2280_ep_regs { /* [11.9] */ /* offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 */ __le32 ep_cfg; #define ENDPOINT_BYTE_COUNT 16 #define ENDPOINT_ENABLE 10 #define ENDPOINT_TYPE 8 #define ENDPOINT_DIRECTION 7 #define ENDPOINT_NUMBER 0 __le32 ep_rsp; #define SET_NAK_OUT_PACKETS 15 #define SET_EP_HIDE_STATUS_PHASE 14 #define SET_EP_FORCE_CRC_ERROR 13 #define SET_INTERRUPT_MODE 12 #define SET_CONTROL_STATUS_PHASE_HANDSHAKE 11 #define SET_NAK_OUT_PACKETS_MODE 10 #define SET_ENDPOINT_TOGGLE 9 #define SET_ENDPOINT_HALT 8 #define CLEAR_NAK_OUT_PACKETS 7 #define CLEAR_EP_HIDE_STATUS_PHASE 6 #define CLEAR_EP_FORCE_CRC_ERROR 5 #define CLEAR_INTERRUPT_MODE 4 #define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE 3 #define CLEAR_NAK_OUT_PACKETS_MODE 2 #define CLEAR_ENDPOINT_TOGGLE 1 #define CLEAR_ENDPOINT_HALT 0 __le32 ep_irqenb; #define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE 6 #define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 5 #define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 #define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 #define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE 1 #define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 __le32 ep_stat; #define FIFO_VALID_COUNT 24 #define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 22 #define TIMEOUT 21 #define USB_STALL_SENT 20 #define USB_IN_NAK_SENT 19 #define USB_IN_ACK_RCVD 18 #define USB_OUT_PING_NAK_SENT 17 #define USB_OUT_ACK_SENT 16 #define FIFO_OVERFLOW 13 #define FIFO_UNDERFLOW 12 #define FIFO_FULL 11 #define FIFO_EMPTY 10 #define FIFO_FLUSH 9 #define SHORT_PACKET_OUT_DONE_INTERRUPT 6 #define SHORT_PACKET_TRANSFERRED_INTERRUPT 5 #define NAK_OUT_PACKETS 4 #define DATA_PACKET_RECEIVED_INTERRUPT 3 #define DATA_PACKET_TRANSMITTED_INTERRUPT 2 #define DATA_OUT_PING_TOKEN_INTERRUPT 1 #define DATA_IN_TOKEN_INTERRUPT 0 /* offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 */ __le32 ep_avail; __le32 ep_data; u32 _unused0[2]; } __packed; struct net2280_reg_write { __le16 port; __le32 addr; __le32 val; } __packed; struct net2280_reg_read { __le16 port; __le32 addr; } __packed; #endif /* NET2280_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/lmac.h0000644000175000017500000003043212026211315022160 0ustar mcgrofmcgrof/* * LMAC Interface specific definitions for mac80211 Prism54 drivers * * Copyright (c) 2006, Michael Wu * Copyright (c) 2007 - 2009, Christian Lamparter * * Based on: * - the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * * - LMAC API interface header file for STLC4560 (lmac_longbow.h) * Copyright (C) 2007 Conexant Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef LMAC_H #define LMAC_H enum p54_control_frame_types { P54_CONTROL_TYPE_SETUP = 0, P54_CONTROL_TYPE_SCAN, P54_CONTROL_TYPE_TRAP, P54_CONTROL_TYPE_DCFINIT, P54_CONTROL_TYPE_RX_KEYCACHE, P54_CONTROL_TYPE_TIM, P54_CONTROL_TYPE_PSM, P54_CONTROL_TYPE_TXCANCEL, P54_CONTROL_TYPE_TXDONE, P54_CONTROL_TYPE_BURST, P54_CONTROL_TYPE_STAT_READBACK, P54_CONTROL_TYPE_BBP, P54_CONTROL_TYPE_EEPROM_READBACK, P54_CONTROL_TYPE_LED, P54_CONTROL_TYPE_GPIO, P54_CONTROL_TYPE_TIMER, P54_CONTROL_TYPE_MODULATION, P54_CONTROL_TYPE_SYNTH_CONFIG, P54_CONTROL_TYPE_DETECTOR_VALUE, P54_CONTROL_TYPE_XBOW_SYNTH_CFG, P54_CONTROL_TYPE_CCE_QUIET, P54_CONTROL_TYPE_PSM_STA_UNLOCK, P54_CONTROL_TYPE_PCS, P54_CONTROL_TYPE_BT_BALANCER = 28, P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30, P54_CONTROL_TYPE_ARPTABLE = 31, P54_CONTROL_TYPE_BT_OPTIONS = 35, }; #define P54_HDR_FLAG_CONTROL BIT(15) #define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0)) #define P54_HDR_FLAG_DATA_ALIGN BIT(14) #define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0) #define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1) #define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2) #define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3) #define P54_HDR_FLAG_DATA_OUT_BURST BIT(4) #define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5) #define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6) #define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7) #define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8) #define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9) #define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10) #define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11) #define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0) #define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1) #define P54_HDR_FLAG_DATA_IN_MCBC BIT(2) #define P54_HDR_FLAG_DATA_IN_BEACON BIT(3) #define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4) #define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5) #define P54_HDR_FLAG_DATA_IN_DATA BIT(6) #define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7) #define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8) #define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9) struct p54_hdr { __le16 flags; __le16 len; __le32 req_id; __le16 type; /* enum p54_control_frame_types */ u8 rts_tries; u8 tries; u8 data[0]; } __packed; #define GET_REQ_ID(skb) \ (((struct p54_hdr *) ((struct sk_buff *) skb)->data)->req_id) \ #define FREE_AFTER_TX(skb) \ ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET)) #define IS_DATA_FRAME(skb) \ (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL))) #define GET_HW_QUEUE(skb) \ (((struct p54_tx_data *)((struct p54_hdr *) \ skb->data)->data)->hw_queue) /* * shared interface ID definitions * The interface ID is a unique identification of a specific interface. * The following values are reserved: 0x0000, 0x0002, 0x0012, 0x0014, 0x0015 */ #define IF_ID_ISL36356A 0x0001 /* ISL36356A <-> Firmware */ #define IF_ID_MVC 0x0003 /* MAC Virtual Coprocessor */ #define IF_ID_DEBUG 0x0008 /* PolDebug Interface */ #define IF_ID_PRODUCT 0x0009 #define IF_ID_OEM 0x000a #define IF_ID_PCI3877 0x000b /* 3877 <-> Host PCI */ #define IF_ID_ISL37704C 0x000c /* ISL37704C <-> Fw */ #define IF_ID_ISL39000 0x000f /* ISL39000 <-> Fw */ #define IF_ID_ISL39300A 0x0010 /* ISL39300A <-> Fw */ #define IF_ID_ISL37700_UAP 0x0016 /* ISL37700 uAP Fw <-> Fw */ #define IF_ID_ISL39000_UAP 0x0017 /* ISL39000 uAP Fw <-> Fw */ #define IF_ID_LMAC 0x001a /* Interface exposed by LMAC */ struct exp_if { __le16 role; __le16 if_id; __le16 variant; __le16 btm_compat; __le16 top_compat; } __packed; struct dep_if { __le16 role; __le16 if_id; __le16 variant; } __packed; /* driver <-> lmac definitions */ struct p54_eeprom_lm86 { union { struct { __le16 offset; __le16 len; u8 data[0]; } __packed v1; struct { __le32 offset; __le16 len; u8 magic2; u8 pad; u8 magic[4]; u8 data[0]; } __packed v2; } __packed; } __packed; enum p54_rx_decrypt_status { P54_DECRYPT_NONE = 0, P54_DECRYPT_OK, P54_DECRYPT_NOKEY, P54_DECRYPT_NOMICHAEL, P54_DECRYPT_NOCKIPMIC, P54_DECRYPT_FAIL_WEP, P54_DECRYPT_FAIL_TKIP, P54_DECRYPT_FAIL_MICHAEL, P54_DECRYPT_FAIL_CKIPKP, P54_DECRYPT_FAIL_CKIPMIC, P54_DECRYPT_FAIL_AESCCMP }; struct p54_rx_data { __le16 flags; __le16 len; __le16 freq; u8 antenna; u8 rate; u8 rssi; u8 quality; u8 decrypt_status; u8 rssi_raw; __le32 tsf32; __le32 unalloc0; u8 align[0]; } __packed; enum p54_trap_type { P54_TRAP_SCAN = 0, P54_TRAP_TIMER, P54_TRAP_BEACON_TX, P54_TRAP_FAA_RADIO_ON, P54_TRAP_FAA_RADIO_OFF, P54_TRAP_RADAR, P54_TRAP_NO_BEACON, P54_TRAP_TBTT, P54_TRAP_SCO_ENTER, P54_TRAP_SCO_EXIT }; struct p54_trap { __le16 event; __le16 frequency; } __packed; enum p54_frame_sent_status { P54_TX_OK = 0, P54_TX_FAILED, P54_TX_PSM, P54_TX_PSM_CANCELLED = 4 }; struct p54_frame_sent { u8 status; u8 tries; u8 ack_rssi; u8 quality; __le16 seq; u8 antenna; u8 padding; } __packed; enum p54_tx_data_crypt { P54_CRYPTO_NONE = 0, P54_CRYPTO_WEP, P54_CRYPTO_TKIP, P54_CRYPTO_TKIPMICHAEL, P54_CRYPTO_CCX_WEPMIC, P54_CRYPTO_CCX_KPMIC, P54_CRYPTO_CCX_KP, P54_CRYPTO_AESCCMP }; enum p54_tx_data_queue { P54_QUEUE_BEACON = 0, P54_QUEUE_FWSCAN = 1, P54_QUEUE_MGMT = 2, P54_QUEUE_CAB = 3, P54_QUEUE_DATA = 4, P54_QUEUE_AC_NUM = 4, P54_QUEUE_AC_VO = 4, P54_QUEUE_AC_VI = 5, P54_QUEUE_AC_BE = 6, P54_QUEUE_AC_BK = 7, /* keep last */ P54_QUEUE_NUM = 8, }; #define IS_QOS_QUEUE(n) (n >= P54_QUEUE_DATA) struct p54_tx_data { u8 rateset[8]; u8 rts_rate_idx; u8 crypt_offset; u8 key_type; u8 key_len; u8 key[16]; u8 hw_queue; u8 backlog; __le16 durations[4]; u8 tx_antenna; union { struct { u8 cts_rate; __le16 output_power; } __packed longbow; struct { u8 output_power; u8 cts_rate; u8 unalloc; } __packed normal; } __packed; u8 unalloc2[2]; u8 align[0]; } __packed; /* unit is ms */ #define P54_TX_FRAME_LIFETIME 2000 #define P54_TX_TIMEOUT 4000 #define P54_STATISTICS_UPDATE 5000 #define P54_FILTER_TYPE_NONE 0 #define P54_FILTER_TYPE_STATION BIT(0) #define P54_FILTER_TYPE_IBSS BIT(1) #define P54_FILTER_TYPE_AP BIT(2) #define P54_FILTER_TYPE_TRANSPARENT BIT(3) #define P54_FILTER_TYPE_PROMISCUOUS BIT(4) #define P54_FILTER_TYPE_HIBERNATE BIT(5) #define P54_FILTER_TYPE_NOACK BIT(6) #define P54_FILTER_TYPE_RX_DISABLED BIT(7) struct p54_setup_mac { __le16 mac_mode; u8 mac_addr[ETH_ALEN]; u8 bssid[ETH_ALEN]; u8 rx_antenna; u8 rx_align; union { struct { __le32 basic_rate_mask; u8 rts_rates[8]; __le32 rx_addr; __le16 max_rx; __le16 rxhw; __le16 wakeup_timer; __le16 unalloc0; } __packed v1; struct { __le32 rx_addr; __le16 max_rx; __le16 rxhw; __le16 timer; __le16 truncate; __le32 basic_rate_mask; u8 sbss_offset; u8 mcast_window; u8 rx_rssi_threshold; u8 rx_ed_threshold; __le32 ref_clock; __le16 lpf_bandwidth; __le16 osc_start_delay; } __packed v2; } __packed; } __packed; #define P54_SETUP_V1_LEN 40 #define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac)) #define P54_SCAN_EXIT BIT(0) #define P54_SCAN_TRAP BIT(1) #define P54_SCAN_ACTIVE BIT(2) #define P54_SCAN_FILTER BIT(3) struct p54_scan_head { __le16 mode; __le16 dwell; u8 scan_params[20]; __le16 freq; } __packed; struct p54_pa_curve_data_sample { u8 rf_power; u8 pa_detector; u8 data_barker; u8 data_bpsk; u8 data_qpsk; u8 data_16qam; u8 data_64qam; u8 padding; } __packed; struct p54_scan_body { u8 pa_points_per_curve; u8 val_barker; u8 val_bpsk; u8 val_qpsk; u8 val_16qam; u8 val_64qam; struct p54_pa_curve_data_sample curve_data[8]; u8 dup_bpsk; u8 dup_qpsk; u8 dup_16qam; u8 dup_64qam; } __packed; /* * Warning: Longbow's structures are bogus. */ struct p54_channel_output_limit_longbow { __le16 rf_power_points[12]; } __packed; struct p54_pa_curve_data_sample_longbow { __le16 rf_power; __le16 pa_detector; struct { __le16 data[4]; } points[3] __packed; } __packed; struct p54_scan_body_longbow { struct p54_channel_output_limit_longbow power_limits; struct p54_pa_curve_data_sample_longbow curve_data[8]; __le16 unkn[6]; /* maybe more power_limits or rate_mask */ } __packed; union p54_scan_body_union { struct p54_scan_body normal; struct p54_scan_body_longbow longbow; } __packed; struct p54_scan_tail_rate { __le32 basic_rate_mask; u8 rts_rates[8]; } __packed; struct p54_led { __le16 flags; __le16 mask[2]; __le16 delay[2]; } __packed; struct p54_edcf { u8 flags; u8 slottime; u8 sifs; u8 eofpad; struct p54_edcf_queue_param queue[8]; u8 mapping[4]; __le16 frameburst; __le16 round_trip_delay; } __packed; struct p54_statistics { __le32 rx_success; __le32 rx_bad_fcs; __le32 rx_abort; __le32 rx_abort_phy; __le32 rts_success; __le32 rts_fail; __le32 tsf32; __le32 airtime; __le32 noise; __le32 sample_noise[8]; __le32 sample_cca; __le32 sample_tx; } __packed; struct p54_xbow_synth { __le16 magic1; __le16 magic2; __le16 freq; u32 padding[5]; } __packed; struct p54_timer { __le32 interval; } __packed; struct p54_keycache { u8 entry; u8 key_id; u8 mac[ETH_ALEN]; u8 padding[2]; u8 key_type; u8 key_len; u8 key[24]; } __packed; struct p54_burst { u8 flags; u8 queue; u8 backlog; u8 pad; __le16 durations[32]; } __packed; struct p54_psm_interval { __le16 interval; __le16 periods; } __packed; #define P54_PSM_CAM 0 #define P54_PSM BIT(0) #define P54_PSM_DTIM BIT(1) #define P54_PSM_MCBC BIT(2) #define P54_PSM_CHECKSUM BIT(3) #define P54_PSM_SKIP_MORE_DATA BIT(4) #define P54_PSM_BEACON_TIMEOUT BIT(5) #define P54_PSM_HFOSLEEP BIT(6) #define P54_PSM_AUTOSWITCH_SLEEP BIT(7) #define P54_PSM_LPIT BIT(8) #define P54_PSM_BF_UCAST_SKIP BIT(9) #define P54_PSM_BF_MCAST_SKIP BIT(10) struct p54_psm { __le16 mode; __le16 aid; struct p54_psm_interval intervals[4]; u8 beacon_rssi_skip_max; u8 rssi_delta_threshold; u8 nr; u8 exclude[1]; } __packed; #define MC_FILTER_ADDRESS_NUM 4 struct p54_group_address_table { __le16 filter_enable; __le16 num_address; u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN]; } __packed; struct p54_txcancel { __le32 req_id; } __packed; struct p54_sta_unlock { u8 addr[ETH_ALEN]; u16 padding; } __packed; #define P54_TIM_CLEAR BIT(15) struct p54_tim { u8 count; u8 padding[3]; __le16 entry[8]; } __packed; struct p54_cce_quiet { __le32 period; } __packed; struct p54_bt_balancer { __le16 prio_thresh; __le16 acl_thresh; } __packed; struct p54_arp_table { __le16 filter_enable; u8 ipv4_addr[4]; } __packed; /* LED control */ int p54_set_leds(struct p54_common *priv); int p54_init_leds(struct p54_common *priv); void p54_unregister_leds(struct p54_common *priv); /* xmit functions */ void p54_tx_80211(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb); int p54_tx_cancel(struct p54_common *priv, __le32 req_id); void p54_tx(struct p54_common *priv, struct sk_buff *skb); /* synth/phy configuration */ int p54_init_xbow_synth(struct p54_common *priv); int p54_scan(struct p54_common *priv, u16 mode, u16 dwell); /* MAC */ int p54_sta_unlock(struct p54_common *priv, u8 *addr); int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set); int p54_setup_mac(struct p54_common *priv); int p54_set_ps(struct p54_common *priv); int p54_fetch_statistics(struct p54_common *priv); int p54_set_groupfilter(struct p54_common *priv); /* e/v DCF setup */ int p54_set_edcf(struct p54_common *priv); /* cryptographic engine */ int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len, u8 *addr, u8* key); /* eeprom */ int p54_download_eeprom(struct p54_common *priv, void *buf, u16 offset, u16 len); struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *p, const u16 freq); /* utility */ u8 *p54_find_ie(struct sk_buff *skb, u8 ie); #endif /* LMAC_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/led.c0000644000175000017500000000774112026211315022012 0ustar mcgrofmcgrof/* * Common code for mac80211 Prism54 drivers * * Copyright (c) 2006, Michael Wu * Copyright (c) 2007-2009, Christian Lamparter * Copyright 2008, Johannes Berg * * Based on: * - the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * - stlc45xx driver * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #ifdef CONFIG_P54_LEDS #include #endif /* CONFIG_P54_LEDS */ #include "p54.h" #include "lmac.h" static void p54_update_leds(struct work_struct *work) { struct p54_common *priv = container_of(work, struct p54_common, led_work.work); int err, i, tmp, blink_delay = 400; bool rerun = false; /* Don't toggle the LED, when the device is down. */ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) return ; for (i = 0; i < ARRAY_SIZE(priv->leds); i++) if (priv->leds[i].toggled) { priv->softled_state |= BIT(i); tmp = 70 + 200 / (priv->leds[i].toggled); if (tmp < blink_delay) blink_delay = tmp; if (priv->leds[i].led_dev.brightness == LED_OFF) rerun = true; priv->leds[i].toggled = !!priv->leds[i].led_dev.brightness; } else priv->softled_state &= ~BIT(i); err = p54_set_leds(priv); if (err && net_ratelimit()) wiphy_err(priv->hw->wiphy, "failed to update LEDs (%d).\n", err); if (rerun) ieee80211_queue_delayed_work(priv->hw, &priv->led_work, msecs_to_jiffies(blink_delay)); } static void p54_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev, led_dev); struct ieee80211_hw *dev = led->hw_dev; struct p54_common *priv = dev->priv; if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) return ; if ((brightness) && (led->registered)) { led->toggled++; ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10); } } static int p54_register_led(struct p54_common *priv, unsigned int led_index, char *name, char *trigger) { struct p54_led_dev *led = &priv->leds[led_index]; int err; if (led->registered) return -EEXIST; snprintf(led->name, sizeof(led->name), "p54-%s::%s", wiphy_name(priv->hw->wiphy), name); led->hw_dev = priv->hw; led->index = led_index; led->led_dev.name = led->name; led->led_dev.default_trigger = trigger; led->led_dev.brightness_set = p54_led_brightness_set; err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev); if (err) wiphy_err(priv->hw->wiphy, "Failed to register %s LED.\n", name); else led->registered = 1; return err; } int p54_init_leds(struct p54_common *priv) { int err; /* * TODO: * Figure out if the EEPROM contains some hints about the number * of available/programmable LEDs of the device. */ INIT_DELAYED_WORK(&priv->led_work, p54_update_leds); err = p54_register_led(priv, 0, "assoc", ieee80211_get_assoc_led_name(priv->hw)); if (err) return err; err = p54_register_led(priv, 1, "tx", ieee80211_get_tx_led_name(priv->hw)); if (err) return err; err = p54_register_led(priv, 2, "rx", ieee80211_get_rx_led_name(priv->hw)); if (err) return err; err = p54_register_led(priv, 3, "radio", ieee80211_get_radio_led_name(priv->hw)); if (err) return err; err = p54_set_leds(priv); return err; } void p54_unregister_leds(struct p54_common *priv) { int i; for (i = 0; i < ARRAY_SIZE(priv->leds); i++) { if (priv->leds[i].registered) { priv->leds[i].registered = false; priv->leds[i].toggled = 0; led_classdev_unregister(&priv->leds[i].led_dev); } } cancel_delayed_work_sync(&priv->led_work); } compat-drivers-2012-09-18/drivers/net/wireless/p54/fwio.c0000644000175000017500000005161212026211315022206 0ustar mcgrofmcgrof/* * Firmware I/O code for mac80211 Prism54 drivers * * Copyright (c) 2006, Michael Wu * Copyright (c) 2007-2009, Christian Lamparter * Copyright 2008, Johannes Berg * * Based on: * - the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * - stlc45xx driver * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "p54.h" #include "eeprom.h" #include "lmac.h" int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) { struct p54_common *priv = dev->priv; struct exp_if *exp_if; struct bootrec *bootrec; u32 *data = (u32 *)fw->data; u32 *end_data = (u32 *)fw->data + (fw->size >> 2); u8 *fw_version = NULL; size_t len; int i; int maxlen; if (priv->rx_start) return 0; while (data < end_data && *data) data++; while (data < end_data && !*data) data++; bootrec = (struct bootrec *) data; while (bootrec->data <= end_data && (bootrec->data + (len = le32_to_cpu(bootrec->len))) <= end_data) { u32 code = le32_to_cpu(bootrec->code); switch (code) { case BR_CODE_COMPONENT_ID: priv->fw_interface = be32_to_cpup((__be32 *) bootrec->data); switch (priv->fw_interface) { case FW_LM86: case FW_LM20: case FW_LM87: { char *iftype = (char *)bootrec->data; wiphy_info(priv->hw->wiphy, "p54 detected a LM%c%c firmware\n", iftype[2], iftype[3]); break; } case FW_FMAC: default: wiphy_err(priv->hw->wiphy, "unsupported firmware\n"); return -ENODEV; } break; case BR_CODE_COMPONENT_VERSION: /* 24 bytes should be enough for all firmwares */ if (strnlen((unsigned char *) bootrec->data, 24) < 24) fw_version = (unsigned char *) bootrec->data; break; case BR_CODE_DESCR: { struct bootrec_desc *desc = (struct bootrec_desc *)bootrec->data; priv->rx_start = le32_to_cpu(desc->rx_start); /* FIXME add sanity checking */ priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; priv->headroom = desc->headroom; priv->tailroom = desc->tailroom; priv->privacy_caps = desc->privacy_caps; priv->rx_keycache_size = desc->rx_keycache_size; if (le32_to_cpu(bootrec->len) == 11) priv->rx_mtu = le16_to_cpu(desc->rx_mtu); else priv->rx_mtu = (size_t) 0x620 - priv->tx_hdr_len; maxlen = priv->tx_hdr_len + /* USB devices */ sizeof(struct p54_rx_data) + 4 + /* rx alignment */ IEEE80211_MAX_FRAG_THRESHOLD; if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) { printk(KERN_INFO "p54: rx_mtu reduced from %d " "to %d\n", priv->rx_mtu, maxlen); priv->rx_mtu = maxlen; } break; } case BR_CODE_EXPOSED_IF: exp_if = (struct exp_if *) bootrec->data; for (i = 0; i < (len * sizeof(*exp_if) / 4); i++) if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC)) priv->fw_var = le16_to_cpu(exp_if[i].variant); break; case BR_CODE_DEPENDENT_IF: break; case BR_CODE_END_OF_BRA: case LEGACY_BR_CODE_END_OF_BRA: end_data = NULL; break; default: break; } bootrec = (struct bootrec *)&bootrec->data[len]; } if (fw_version) { wiphy_info(priv->hw->wiphy, "FW rev %s - Softmac protocol %x.%x\n", fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version), "%s - %x.%x", fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); } if (priv->fw_var < 0x500) wiphy_info(priv->hw->wiphy, "you are using an obsolete firmware. " "visit http://wireless.kernel.org/en/users/Drivers/p54 " "and grab one for \"kernel >= 2.6.28\"!\n"); if (priv->fw_var >= 0x300) { /* Firmware supports QoS, use it! */ if (priv->fw_var >= 0x500) { priv->tx_stats[P54_QUEUE_AC_VO].limit = 16; priv->tx_stats[P54_QUEUE_AC_VI].limit = 16; priv->tx_stats[P54_QUEUE_AC_BE].limit = 16; priv->tx_stats[P54_QUEUE_AC_BK].limit = 16; } else { priv->tx_stats[P54_QUEUE_AC_VO].limit = 3; priv->tx_stats[P54_QUEUE_AC_VI].limit = 4; priv->tx_stats[P54_QUEUE_AC_BE].limit = 3; priv->tx_stats[P54_QUEUE_AC_BK].limit = 2; } priv->hw->queues = P54_QUEUE_AC_NUM; } wiphy_info(priv->hw->wiphy, "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n", (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no", (priv->privacy_caps & (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL)) ? "YES" : "no", (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) ? "YES" : "no"); if (priv->rx_keycache_size) { /* * NOTE: * * The firmware provides at most 255 (0 - 254) slots * for keys which are then used to offload decryption. * As a result the 255 entry (aka 0xff) can be used * safely by the driver to mark keys that didn't fit * into the full cache. This trick saves us from * keeping a extra list for uploaded keys. */ priv->used_rxkeys = kzalloc(BITS_TO_LONGS( priv->rx_keycache_size), GFP_KERNEL); if (!priv->used_rxkeys) return -ENOMEM; } return 0; } EXPORT_SYMBOL_GPL(p54_parse_firmware); static struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags, u16 payload_len, u16 type, gfp_t memflags) { struct p54_hdr *hdr; struct sk_buff *skb; size_t frame_len = sizeof(*hdr) + payload_len; if (frame_len > P54_MAX_CTRL_FRAME_LEN) return NULL; if (unlikely(skb_queue_len(&priv->tx_pending) > 64)) return NULL; skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags); if (!skb) return NULL; skb_reserve(skb, priv->tx_hdr_len); hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr)); hdr->flags = cpu_to_le16(hdr_flags); hdr->len = cpu_to_le16(payload_len); hdr->type = cpu_to_le16(type); hdr->tries = hdr->rts_tries = 0; return skb; } int p54_download_eeprom(struct p54_common *priv, void *buf, u16 offset, u16 len) { struct p54_eeprom_lm86 *eeprom_hdr; struct sk_buff *skb; size_t eeprom_hdr_size; int ret = 0; if (priv->fw_var >= 0x509) eeprom_hdr_size = sizeof(*eeprom_hdr); else eeprom_hdr_size = 0x4; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size + len, P54_CONTROL_TYPE_EEPROM_READBACK, GFP_KERNEL); if (unlikely(!skb)) return -ENOMEM; mutex_lock(&priv->eeprom_mutex); priv->eeprom = buf; eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb, eeprom_hdr_size + len); if (priv->fw_var < 0x509) { eeprom_hdr->v1.offset = cpu_to_le16(offset); eeprom_hdr->v1.len = cpu_to_le16(len); } else { eeprom_hdr->v2.offset = cpu_to_le32(offset); eeprom_hdr->v2.len = cpu_to_le16(len); eeprom_hdr->v2.magic2 = 0xf; memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4); } p54_tx(priv, skb); if (!wait_for_completion_interruptible_timeout( &priv->eeprom_comp, HZ)) { wiphy_err(priv->hw->wiphy, "device does not respond!\n"); ret = -EBUSY; } priv->eeprom = NULL; mutex_unlock(&priv->eeprom_mutex); return ret; } int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set) { struct sk_buff *skb; struct p54_tim *tim; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim), P54_CONTROL_TYPE_TIM, GFP_ATOMIC); if (unlikely(!skb)) return -ENOMEM; tim = (struct p54_tim *) skb_put(skb, sizeof(*tim)); tim->count = 1; tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid); p54_tx(priv, skb); return 0; } int p54_sta_unlock(struct p54_common *priv, u8 *addr) { struct sk_buff *skb; struct p54_sta_unlock *sta; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta), P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC); if (unlikely(!skb)) return -ENOMEM; sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta)); memcpy(sta->addr, addr, ETH_ALEN); p54_tx(priv, skb); return 0; } int p54_tx_cancel(struct p54_common *priv, __le32 req_id) { struct sk_buff *skb; struct p54_txcancel *cancel; u32 _req_id = le32_to_cpu(req_id); if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end)) return -EINVAL; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel), P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC); if (unlikely(!skb)) return -ENOMEM; cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel)); cancel->req_id = req_id; p54_tx(priv, skb); return 0; } int p54_setup_mac(struct p54_common *priv) { struct sk_buff *skb; struct p54_setup_mac *setup; u16 mode; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup), P54_CONTROL_TYPE_SETUP, GFP_ATOMIC); if (!skb) return -ENOMEM; setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup)); if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { switch (priv->mode) { case NL80211_IFTYPE_STATION: mode = P54_FILTER_TYPE_STATION; break; case NL80211_IFTYPE_AP: mode = P54_FILTER_TYPE_AP; break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: mode = P54_FILTER_TYPE_IBSS; break; case NL80211_IFTYPE_MONITOR: mode = P54_FILTER_TYPE_PROMISCUOUS; break; default: mode = P54_FILTER_TYPE_HIBERNATE; break; } /* * "TRANSPARENT and PROMISCUOUS are mutually exclusive" * STSW45X0C LMAC API - page 12 */ if (((priv->filter_flags & FIF_PROMISC_IN_BSS) || (priv->filter_flags & FIF_OTHER_BSS)) && (mode != P54_FILTER_TYPE_PROMISCUOUS)) mode |= P54_FILTER_TYPE_TRANSPARENT; } else { mode = P54_FILTER_TYPE_HIBERNATE; } setup->mac_mode = cpu_to_le16(mode); memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN); memcpy(setup->bssid, priv->bssid, ETH_ALEN); setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */ setup->rx_align = 0; if (priv->fw_var < 0x500) { setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); memset(setup->v1.rts_rates, 0, 8); setup->v1.rx_addr = cpu_to_le32(priv->rx_end); setup->v1.max_rx = cpu_to_le16(priv->rx_mtu); setup->v1.rxhw = cpu_to_le16(priv->rxhw); setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer); setup->v1.unalloc0 = cpu_to_le16(0); } else { setup->v2.rx_addr = cpu_to_le32(priv->rx_end); setup->v2.max_rx = cpu_to_le16(priv->rx_mtu); setup->v2.rxhw = cpu_to_le16(priv->rxhw); setup->v2.timer = cpu_to_le16(priv->wakeup_timer); setup->v2.truncate = cpu_to_le16(48896); setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); setup->v2.sbss_offset = 0; setup->v2.mcast_window = 0; setup->v2.rx_rssi_threshold = 0; setup->v2.rx_ed_threshold = 0; setup->v2.ref_clock = cpu_to_le32(644245094); setup->v2.lpf_bandwidth = cpu_to_le16(65535); setup->v2.osc_start_delay = cpu_to_le16(65535); } p54_tx(priv, skb); priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE; return 0; } int p54_scan(struct p54_common *priv, u16 mode, u16 dwell) { struct sk_buff *skb; struct p54_hdr *hdr; struct p54_scan_head *head; struct p54_iq_autocal_entry *iq_autocal; union p54_scan_body_union *body; struct p54_scan_tail_rate *rate; struct pda_rssi_cal_entry *rssi; struct p54_rssi_db_entry *rssi_data; unsigned int i; void *entry; __le16 freq = cpu_to_le16(priv->hw->conf.channel->center_freq); skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + 2 + sizeof(*iq_autocal) + sizeof(*body) + sizeof(*rate) + 2 * sizeof(*rssi), P54_CONTROL_TYPE_SCAN, GFP_ATOMIC); if (!skb) return -ENOMEM; head = (struct p54_scan_head *) skb_put(skb, sizeof(*head)); memset(head->scan_params, 0, sizeof(head->scan_params)); head->mode = cpu_to_le16(mode); head->dwell = cpu_to_le16(dwell); head->freq = freq; if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { __le16 *pa_power_points = (__le16 *) skb_put(skb, 2); *pa_power_points = cpu_to_le16(0x0c); } iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal)); for (i = 0; i < priv->iq_autocal_len; i++) { if (priv->iq_autocal[i].freq != freq) continue; memcpy(iq_autocal, &priv->iq_autocal[i].params, sizeof(struct p54_iq_autocal_entry)); break; } if (i == priv->iq_autocal_len) goto err; if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) body = (void *) skb_put(skb, sizeof(body->longbow)); else body = (void *) skb_put(skb, sizeof(body->normal)); for (i = 0; i < priv->output_limit->entries; i++) { __le16 *entry_freq = (void *) (priv->output_limit->data + priv->output_limit->entry_size * i); if (*entry_freq != freq) continue; if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { memcpy(&body->longbow.power_limits, (void *) entry_freq + sizeof(__le16), priv->output_limit->entry_size); } else { struct pda_channel_output_limit *limits = (void *) entry_freq; body->normal.val_barker = 0x38; body->normal.val_bpsk = body->normal.dup_bpsk = limits->val_bpsk; body->normal.val_qpsk = body->normal.dup_qpsk = limits->val_qpsk; body->normal.val_16qam = body->normal.dup_16qam = limits->val_16qam; body->normal.val_64qam = body->normal.dup_64qam = limits->val_64qam; } break; } if (i == priv->output_limit->entries) goto err; entry = (void *)(priv->curve_data->data + priv->curve_data->offset); for (i = 0; i < priv->curve_data->entries; i++) { if (*((__le16 *)entry) != freq) { entry += priv->curve_data->entry_size; continue; } if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { memcpy(&body->longbow.curve_data, entry + sizeof(__le16), priv->curve_data->entry_size); } else { struct p54_scan_body *chan = &body->normal; struct pda_pa_curve_data *curve_data = (void *) priv->curve_data->data; entry += sizeof(__le16); chan->pa_points_per_curve = 8; memset(chan->curve_data, 0, sizeof(*chan->curve_data)); memcpy(chan->curve_data, entry, sizeof(struct p54_pa_curve_data_sample) * min((u8)8, curve_data->points_per_channel)); } break; } if (i == priv->curve_data->entries) goto err; if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) { rate = (void *) skb_put(skb, sizeof(*rate)); rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); for (i = 0; i < sizeof(rate->rts_rates); i++) rate->rts_rates[i] = i; } rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi)); rssi_data = p54_rssi_find(priv, le16_to_cpu(freq)); rssi->mul = cpu_to_le16(rssi_data->mul); rssi->add = cpu_to_le16(rssi_data->add); if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { /* Longbow frontend needs ever more */ rssi = (void *) skb_put(skb, sizeof(*rssi)); rssi->mul = cpu_to_le16(rssi_data->longbow_unkn); rssi->add = cpu_to_le16(rssi_data->longbow_unk2); } if (priv->fw_var >= 0x509) { rate = (void *) skb_put(skb, sizeof(*rate)); rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); for (i = 0; i < sizeof(rate->rts_rates); i++) rate->rts_rates[i] = i; } hdr = (struct p54_hdr *) skb->data; hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); p54_tx(priv, skb); priv->cur_rssi = rssi_data; return 0; err: wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n", ieee80211_frequency_to_channel( priv->hw->conf.channel->center_freq)); dev_kfree_skb_any(skb); return -EINVAL; } int p54_set_leds(struct p54_common *priv) { struct sk_buff *skb; struct p54_led *led; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led), P54_CONTROL_TYPE_LED, GFP_ATOMIC); if (unlikely(!skb)) return -ENOMEM; led = (struct p54_led *) skb_put(skb, sizeof(*led)); led->flags = cpu_to_le16(0x0003); led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state); led->delay[0] = cpu_to_le16(1); led->delay[1] = cpu_to_le16(0); p54_tx(priv, skb); return 0; } int p54_set_edcf(struct p54_common *priv) { struct sk_buff *skb; struct p54_edcf *edcf; u8 rtd; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); if (unlikely(!skb)) return -ENOMEM; edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf)); if (priv->use_short_slot) { edcf->slottime = 9; edcf->sifs = 0x10; edcf->eofpad = 0x00; } else { edcf->slottime = 20; edcf->sifs = 0x0a; edcf->eofpad = 0x06; } /* * calculate the extra round trip delay according to the * formula from 802.11-2007 17.3.8.6. */ rtd = 3 * priv->coverage_class; edcf->slottime += rtd; edcf->round_trip_delay = cpu_to_le16(rtd); /* (see prism54/isl_oid.h for further details) */ edcf->frameburst = cpu_to_le16(0); edcf->flags = 0; memset(edcf->mapping, 0, sizeof(edcf->mapping)); memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); p54_tx(priv, skb); return 0; } int p54_set_ps(struct p54_common *priv) { struct sk_buff *skb; struct p54_psm *psm; unsigned int i; u16 mode; if (priv->hw->conf.flags & IEEE80211_CONF_PS && !priv->powersave_override) mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | P54_PSM_CHECKSUM | P54_PSM_MCBC; else mode = P54_PSM_CAM; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm), P54_CONTROL_TYPE_PSM, GFP_ATOMIC); if (!skb) return -ENOMEM; psm = (struct p54_psm *)skb_put(skb, sizeof(*psm)); psm->mode = cpu_to_le16(mode); psm->aid = cpu_to_le16(priv->aid); for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) { psm->intervals[i].interval = cpu_to_le16(priv->hw->conf.listen_interval); psm->intervals[i].periods = cpu_to_le16(1); } psm->beacon_rssi_skip_max = 200; psm->rssi_delta_threshold = 0; psm->nr = 1; psm->exclude[0] = WLAN_EID_TIM; p54_tx(priv, skb); priv->phy_ps = mode != P54_PSM_CAM; return 0; } int p54_init_xbow_synth(struct p54_common *priv) { struct sk_buff *skb; struct p54_xbow_synth *xbow; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow), P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL); if (unlikely(!skb)) return -ENOMEM; xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow)); xbow->magic1 = cpu_to_le16(0x1); xbow->magic2 = cpu_to_le16(0x2); xbow->freq = cpu_to_le16(5390); memset(xbow->padding, 0, sizeof(xbow->padding)); p54_tx(priv, skb); return 0; } int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len, u8 *addr, u8* key) { struct sk_buff *skb; struct p54_keycache *rxkey; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey), P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL); if (unlikely(!skb)) return -ENOMEM; rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey)); rxkey->entry = slot; rxkey->key_id = idx; rxkey->key_type = algo; if (addr) memcpy(rxkey->mac, addr, ETH_ALEN); else memset(rxkey->mac, ~0, ETH_ALEN); switch (algo) { case P54_CRYPTO_WEP: case P54_CRYPTO_AESCCMP: rxkey->key_len = min_t(u8, 16, len); memcpy(rxkey->key, key, rxkey->key_len); break; case P54_CRYPTO_TKIPMICHAEL: rxkey->key_len = 24; memcpy(rxkey->key, key, 16); memcpy(&(rxkey->key[16]), &(key [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); break; case P54_CRYPTO_NONE: rxkey->key_len = 0; memset(rxkey->key, 0, sizeof(rxkey->key)); break; default: wiphy_err(priv->hw->wiphy, "invalid cryptographic algorithm: %d\n", algo); dev_kfree_skb(skb); return -EINVAL; } p54_tx(priv, skb); return 0; } int p54_fetch_statistics(struct p54_common *priv) { struct ieee80211_tx_info *txinfo; struct p54_tx_info *p54info; struct sk_buff *skb; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, sizeof(struct p54_statistics), P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL); if (!skb) return -ENOMEM; /* * The statistic feedback causes some extra headaches here, if it * is not to crash/corrupt the firmware data structures. * * Unlike all other Control Get OIDs we can not use helpers like * skb_put to reserve the space for the data we're requesting. * Instead the extra frame length -which will hold the results later- * will only be told to the p54_assign_address, so that following * frames won't be placed into the allegedly empty area. */ txinfo = IEEE80211_SKB_CB(skb); p54info = (void *) txinfo->rate_driver_data; p54info->extra_len = sizeof(struct p54_statistics); p54_tx(priv, skb); return 0; } int p54_set_groupfilter(struct p54_common *priv) { struct p54_group_address_table *grp; struct sk_buff *skb; bool on = false; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp), P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL); if (!skb) return -ENOMEM; grp = (struct p54_group_address_table *)skb_put(skb, sizeof(*grp)); on = !(priv->filter_flags & FIF_ALLMULTI) && (priv->mc_maclist_num > 0 && priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM); if (on) { grp->filter_enable = cpu_to_le16(1); grp->num_address = cpu_to_le16(priv->mc_maclist_num); memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list)); } else { grp->filter_enable = cpu_to_le16(0); grp->num_address = cpu_to_le16(0); memset(grp->mac_list, 0, sizeof(grp->mac_list)); } p54_tx(priv, skb); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/p54/eeprom.h0000644000175000017500000001513712026211315022540 0ustar mcgrofmcgrof/* * eeprom specific definitions for mac80211 Prism54 drivers * * Copyright (c) 2006, Michael Wu * Copyright (c) 2007-2009, Christian Lamparter * * Based on: * - the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * * - LMAC API interface header file for STLC4560 (lmac_longbow.h) * Copyright (C) 2007 Conexant Systems, Inc. * * - islmvc driver * Copyright (C) 2001 Intersil Americas Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef EEPROM_H #define EEPROM_H /* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */ struct pda_entry { __le16 len; /* includes both code and data */ __le16 code; u8 data[0]; } __packed; struct eeprom_pda_wrap { __le32 magic; __le16 pad; __le16 len; __le32 arm_opcode; u8 data[0]; } __packed; struct p54_iq_autocal_entry { __le16 iq_param[4]; } __packed; struct pda_iq_autocal_entry { __le16 freq; struct p54_iq_autocal_entry params; } __packed; struct pda_channel_output_limit { __le16 freq; u8 val_bpsk; u8 val_qpsk; u8 val_16qam; u8 val_64qam; u8 rate_set_mask; u8 rate_set_size; } __packed; struct pda_channel_output_limit_point_longbow { __le16 val_bpsk; __le16 val_qpsk; __le16 val_16qam; __le16 val_64qam; } __packed; struct pda_channel_output_limit_longbow { __le16 freq; struct pda_channel_output_limit_point_longbow point[3]; } __packed; struct pda_pa_curve_data_sample_rev0 { u8 rf_power; u8 pa_detector; u8 pcv; } __packed; struct pda_pa_curve_data_sample_rev1 { u8 rf_power; u8 pa_detector; u8 data_barker; u8 data_bpsk; u8 data_qpsk; u8 data_16qam; u8 data_64qam; } __packed; struct pda_pa_curve_data { u8 cal_method_rev; u8 channels; u8 points_per_channel; u8 padding; u8 data[0]; } __packed; struct pda_rssi_cal_ext_entry { __le16 freq; __le16 mul; __le16 add; } __packed; struct pda_rssi_cal_entry { __le16 mul; __le16 add; } __packed; struct pda_country { u8 regdomain; u8 alpha2[2]; u8 flags; } __packed; struct pda_antenna_gain { struct { u8 gain_5GHz; /* 0.25 dBi units */ u8 gain_2GHz; /* 0.25 dBi units */ } __packed antenna[0]; } __packed; struct pda_custom_wrapper { __le16 entries; __le16 entry_size; __le16 offset; __le16 len; u8 data[0]; } __packed; /* * this defines the PDR codes used to build PDAs as defined in document * number 553155. The current implementation mirrors version 1.1 of the * document and lists only PDRs supported by the ARM platform. */ /* common and choice range (0x0000 - 0x0fff) */ #define PDR_END 0x0000 #define PDR_MANUFACTURING_PART_NUMBER 0x0001 #define PDR_PDA_VERSION 0x0002 #define PDR_NIC_SERIAL_NUMBER 0x0003 #define PDR_NIC_RAM_SIZE 0x0005 #define PDR_RFMODEM_SUP_RANGE 0x0006 #define PDR_PRISM_MAC_SUP_RANGE 0x0007 #define PDR_NIC_ID 0x0008 #define PDR_MAC_ADDRESS 0x0101 #define PDR_REGULATORY_DOMAIN_LIST 0x0103 /* obsolete */ #define PDR_ALLOWED_CHAN_SET 0x0104 #define PDR_DEFAULT_CHAN 0x0105 #define PDR_TEMPERATURE_TYPE 0x0107 #define PDR_IFR_SETTING 0x0200 #define PDR_RFR_SETTING 0x0201 #define PDR_3861_BASELINE_REG_SETTINGS 0x0202 #define PDR_3861_SHADOW_REG_SETTINGS 0x0203 #define PDR_3861_IFRF_REG_SETTINGS 0x0204 #define PDR_3861_CHAN_CALIB_SET_POINTS 0x0300 #define PDR_3861_CHAN_CALIB_INTEGRATOR 0x0301 #define PDR_3842_PRISM_II_NIC_CONFIG 0x0400 #define PDR_PRISM_USB_ID 0x0401 #define PDR_PRISM_PCI_ID 0x0402 #define PDR_PRISM_PCI_IF_CONFIG 0x0403 #define PDR_PRISM_PCI_PM_CONFIG 0x0404 #define PDR_3861_MF_TEST_CHAN_SET_POINTS 0x0900 #define PDR_3861_MF_TEST_CHAN_INTEGRATORS 0x0901 /* ARM range (0x1000 - 0x1fff) */ #define PDR_COUNTRY_INFORMATION 0x1000 /* obsolete */ #define PDR_INTERFACE_LIST 0x1001 #define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002 #define PDR_OEM_NAME 0x1003 #define PDR_PRODUCT_NAME 0x1004 #define PDR_UTF8_OEM_NAME 0x1005 #define PDR_UTF8_PRODUCT_NAME 0x1006 #define PDR_COUNTRY_LIST 0x1007 #define PDR_DEFAULT_COUNTRY 0x1008 #define PDR_ANTENNA_GAIN 0x1100 #define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901 #define PDR_RSSI_LINEAR_APPROXIMATION 0x1902 #define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903 #define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904 #define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905 #define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906 #define PDR_REGULATORY_POWER_LIMITS 0x1907 #define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908 #define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909 #define PDR_PRISM_TX_IQ_CALIBRATION 0x190a /* reserved range (0x2000 - 0x7fff) */ /* customer range (0x8000 - 0xffff) */ #define PDR_BASEBAND_REGISTERS 0x8000 #define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001 /* used by our modificated eeprom image */ #define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD #define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 0xCAFF #define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF #define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D /* Interface Definitions */ #define PDR_INTERFACE_ROLE_SERVER 0x0000 #define PDR_INTERFACE_ROLE_CLIENT 0x0001 /* PDR definitions for default country & country list */ #define PDR_COUNTRY_CERT_CODE 0x80 #define PDR_COUNTRY_CERT_CODE_REAL 0x00 #define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80 #define PDR_COUNTRY_CERT_BAND 0x40 #define PDR_COUNTRY_CERT_BAND_2GHZ 0x00 #define PDR_COUNTRY_CERT_BAND_5GHZ 0x40 #define PDR_COUNTRY_CERT_IODOOR 0x30 #define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00 #define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20 #define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30 #define PDR_COUNTRY_CERT_INDEX 0x0f /* Specific LMAC FW/HW variant definitions */ #define PDR_SYNTH_FRONTEND_MASK 0x0007 #define PDR_SYNTH_FRONTEND_DUETTE3 0x0001 #define PDR_SYNTH_FRONTEND_DUETTE2 0x0002 #define PDR_SYNTH_FRONTEND_FRISBEE 0x0003 #define PDR_SYNTH_FRONTEND_XBOW 0x0004 #define PDR_SYNTH_FRONTEND_LONGBOW 0x0005 #define PDR_SYNTH_IQ_CAL_MASK 0x0018 #define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000 #define PDR_SYNTH_IQ_CAL_DISABLED 0x0008 #define PDR_SYNTH_IQ_CAL_ZIF 0x0010 #define PDR_SYNTH_FAA_SWITCH_MASK 0x0020 #define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0020 #define PDR_SYNTH_24_GHZ_MASK 0x0040 #define PDR_SYNTH_24_GHZ_DISABLED 0x0040 #define PDR_SYNTH_5_GHZ_MASK 0x0080 #define PDR_SYNTH_5_GHZ_DISABLED 0x0080 #define PDR_SYNTH_RX_DIV_MASK 0x0100 #define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100 #define PDR_SYNTH_TX_DIV_MASK 0x0200 #define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200 #define PDR_SYNTH_ASM_MASK 0x0400 #define PDR_SYNTH_ASM_XSWON 0x0400 #endif /* EEPROM_H */ compat-drivers-2012-09-18/drivers/net/wireless/p54/eeprom.c0000644000175000017500000006151512026211315022534 0ustar mcgrofmcgrof/* * EEPROM parser code for mac80211 Prism54 drivers * * Copyright (c) 2006, Michael Wu * Copyright (c) 2007-2009, Christian Lamparter * Copyright 2008, Johannes Berg * * Based on: * - the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note , et al. * - stlc45xx driver * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include "p54.h" #include "eeprom.h" #include "lmac.h" static struct ieee80211_rate p54_bgrates[] = { { .bitrate = 10, .hw_value = 0, }, { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = 4, }, { .bitrate = 90, .hw_value = 5, }, { .bitrate = 120, .hw_value = 6, }, { .bitrate = 180, .hw_value = 7, }, { .bitrate = 240, .hw_value = 8, }, { .bitrate = 360, .hw_value = 9, }, { .bitrate = 480, .hw_value = 10, }, { .bitrate = 540, .hw_value = 11, }, }; static struct ieee80211_rate p54_arates[] = { { .bitrate = 60, .hw_value = 4, }, { .bitrate = 90, .hw_value = 5, }, { .bitrate = 120, .hw_value = 6, }, { .bitrate = 180, .hw_value = 7, }, { .bitrate = 240, .hw_value = 8, }, { .bitrate = 360, .hw_value = 9, }, { .bitrate = 480, .hw_value = 10, }, { .bitrate = 540, .hw_value = 11, }, }; static struct p54_rssi_db_entry p54_rssi_default = { /* * The defaults are taken from usb-logs of the * vendor driver. So, they should be safe to * use in case we can't get a match from the * rssi <-> dBm conversion database. */ .mul = 130, .add = -398, }; #define CHAN_HAS_CAL BIT(0) #define CHAN_HAS_LIMIT BIT(1) #define CHAN_HAS_CURVE BIT(2) #define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE) struct p54_channel_entry { u16 freq; u16 data; int index; int max_power; enum ieee80211_band band; }; struct p54_channel_list { struct p54_channel_entry *channels; size_t entries; size_t max_entries; size_t band_channel_num[IEEE80211_NUM_BANDS]; }; static int p54_get_band_from_freq(u16 freq) { /* FIXME: sync these values with the 802.11 spec */ if ((freq >= 2412) && (freq <= 2484)) return IEEE80211_BAND_2GHZ; if ((freq >= 4920) && (freq <= 5825)) return IEEE80211_BAND_5GHZ; return -1; } static int same_band(u16 freq, u16 freq2) { return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2); } static int p54_compare_channels(const void *_a, const void *_b) { const struct p54_channel_entry *a = _a; const struct p54_channel_entry *b = _b; return a->freq - b->freq; } static int p54_compare_rssichan(const void *_a, const void *_b) { const struct p54_rssi_db_entry *a = _a; const struct p54_rssi_db_entry *b = _b; return a->freq - b->freq; } static int p54_fill_band_bitrates(struct ieee80211_hw *dev, struct ieee80211_supported_band *band_entry, enum ieee80211_band band) { /* TODO: generate rate array dynamically */ switch (band) { case IEEE80211_BAND_2GHZ: band_entry->bitrates = p54_bgrates; band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates); break; case IEEE80211_BAND_5GHZ: band_entry->bitrates = p54_arates; band_entry->n_bitrates = ARRAY_SIZE(p54_arates); break; default: return -EINVAL; } return 0; } static int p54_generate_band(struct ieee80211_hw *dev, struct p54_channel_list *list, unsigned int *chan_num, enum ieee80211_band band) { struct p54_common *priv = dev->priv; struct ieee80211_supported_band *tmp, *old; unsigned int i, j; int ret = -ENOMEM; if ((!list->entries) || (!list->band_channel_num[band])) return -EINVAL; tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) goto err_out; tmp->channels = kzalloc(sizeof(struct ieee80211_channel) * list->band_channel_num[band], GFP_KERNEL); if (!tmp->channels) goto err_out; ret = p54_fill_band_bitrates(dev, tmp, band); if (ret) goto err_out; for (i = 0, j = 0; (j < list->band_channel_num[band]) && (i < list->entries); i++) { struct p54_channel_entry *chan = &list->channels[i]; struct ieee80211_channel *dest = &tmp->channels[j]; if (chan->band != band) continue; if (chan->data != CHAN_HAS_ALL) { wiphy_err(dev->wiphy, "%s%s%s is/are missing for " "channel:%d [%d MHz].\n", (chan->data & CHAN_HAS_CAL ? "" : " [iqauto calibration data]"), (chan->data & CHAN_HAS_LIMIT ? "" : " [output power limits]"), (chan->data & CHAN_HAS_CURVE ? "" : " [curve data]"), chan->index, chan->freq); continue; } dest->band = chan->band; dest->center_freq = chan->freq; dest->max_power = chan->max_power; priv->survey[*chan_num].channel = &tmp->channels[j]; priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_TX; dest->hw_value = (*chan_num); j++; (*chan_num)++; } if (j == 0) { wiphy_err(dev->wiphy, "Disabling totally damaged %d GHz band\n", (band == IEEE80211_BAND_2GHZ) ? 2 : 5); ret = -ENODATA; goto err_out; } tmp->n_channels = j; old = priv->band_table[band]; priv->band_table[band] = tmp; if (old) { kfree(old->channels); kfree(old); } return 0; err_out: if (tmp) { kfree(tmp->channels); kfree(tmp); } return ret; } static struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list, u16 freq, u16 data) { int i; struct p54_channel_entry *entry = NULL; /* * usually all lists in the eeprom are mostly sorted. * so it's very likely that the entry we are looking for * is right at the end of the list */ for (i = list->entries; i >= 0; i--) { if (freq == list->channels[i].freq) { entry = &list->channels[i]; break; } } if ((i < 0) && (list->entries < list->max_entries)) { /* entry does not exist yet. Initialize a new one. */ int band = p54_get_band_from_freq(freq); /* * filter out frequencies which don't belong into * any supported band. */ if (band >= 0) { i = list->entries++; list->band_channel_num[band]++; entry = &list->channels[i]; entry->freq = freq; entry->band = band; entry->index = ieee80211_frequency_to_channel(freq); entry->max_power = 0; entry->data = 0; } } if (entry) entry->data |= data; return entry; } static int p54_get_maxpower(struct p54_common *priv, void *data) { switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) { case PDR_SYNTH_FRONTEND_LONGBOW: { struct pda_channel_output_limit_longbow *pda = data; int j; u16 rawpower = 0; pda = data; for (j = 0; j < ARRAY_SIZE(pda->point); j++) { struct pda_channel_output_limit_point_longbow *point = &pda->point[j]; rawpower = max_t(u16, rawpower, le16_to_cpu(point->val_qpsk)); rawpower = max_t(u16, rawpower, le16_to_cpu(point->val_bpsk)); rawpower = max_t(u16, rawpower, le16_to_cpu(point->val_16qam)); rawpower = max_t(u16, rawpower, le16_to_cpu(point->val_64qam)); } /* longbow seems to use 1/16 dBm units */ return rawpower / 16; } case PDR_SYNTH_FRONTEND_DUETTE3: case PDR_SYNTH_FRONTEND_DUETTE2: case PDR_SYNTH_FRONTEND_FRISBEE: case PDR_SYNTH_FRONTEND_XBOW: { struct pda_channel_output_limit *pda = data; u8 rawpower = 0; rawpower = max(rawpower, pda->val_qpsk); rawpower = max(rawpower, pda->val_bpsk); rawpower = max(rawpower, pda->val_16qam); rawpower = max(rawpower, pda->val_64qam); /* raw values are in 1/4 dBm units */ return rawpower / 4; } default: return 20; } } static int p54_generate_channel_lists(struct ieee80211_hw *dev) { struct p54_common *priv = dev->priv; struct p54_channel_list *list; unsigned int i, j, k, max_channel_num; int ret = 0; u16 freq; if ((priv->iq_autocal_len != priv->curve_data->entries) || (priv->iq_autocal_len != priv->output_limit->entries)) wiphy_err(dev->wiphy, "Unsupported or damaged EEPROM detected. " "You may not be able to use all channels.\n"); max_channel_num = max_t(unsigned int, priv->output_limit->entries, priv->iq_autocal_len); max_channel_num = max_t(unsigned int, max_channel_num, priv->curve_data->entries); list = kzalloc(sizeof(*list), GFP_KERNEL); if (!list) { ret = -ENOMEM; goto free; } priv->chan_num = max_channel_num; priv->survey = kzalloc(sizeof(struct survey_info) * max_channel_num, GFP_KERNEL); if (!priv->survey) { ret = -ENOMEM; goto free; } list->max_entries = max_channel_num; list->channels = kzalloc(sizeof(struct p54_channel_entry) * max_channel_num, GFP_KERNEL); if (!list->channels) { ret = -ENOMEM; goto free; } for (i = 0; i < max_channel_num; i++) { if (i < priv->iq_autocal_len) { freq = le16_to_cpu(priv->iq_autocal[i].freq); p54_update_channel_param(list, freq, CHAN_HAS_CAL); } if (i < priv->output_limit->entries) { struct p54_channel_entry *tmp; void *data = (void *) ((unsigned long) i * priv->output_limit->entry_size + priv->output_limit->offset + priv->output_limit->data); freq = le16_to_cpup((__le16 *) data); tmp = p54_update_channel_param(list, freq, CHAN_HAS_LIMIT); if (tmp) { tmp->max_power = p54_get_maxpower(priv, data); } } if (i < priv->curve_data->entries) { freq = le16_to_cpup((__le16 *) (i * priv->curve_data->entry_size + priv->curve_data->offset + priv->curve_data->data)); p54_update_channel_param(list, freq, CHAN_HAS_CURVE); } } /* sort the channel list by frequency */ sort(list->channels, list->entries, sizeof(struct p54_channel_entry), p54_compare_channels, NULL); k = 0; for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) { if (p54_generate_band(dev, list, &k, i) == 0) j++; } if (j == 0) { /* no useable band available. */ ret = -EINVAL; } free: if (list) { kfree(list->channels); kfree(list); } if (ret) { kfree(priv->survey); priv->survey = NULL; } return ret; } static int p54_convert_rev0(struct ieee80211_hw *dev, struct pda_pa_curve_data *curve_data) { struct p54_common *priv = dev->priv; struct p54_pa_curve_data_sample *dst; struct pda_pa_curve_data_sample_rev0 *src; size_t cd_len = sizeof(*curve_data) + (curve_data->points_per_channel*sizeof(*dst) + 2) * curve_data->channels; unsigned int i, j; void *source, *target; priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len, GFP_KERNEL); if (!priv->curve_data) return -ENOMEM; priv->curve_data->entries = curve_data->channels; priv->curve_data->entry_size = sizeof(__le16) + sizeof(*dst) * curve_data->points_per_channel; priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); priv->curve_data->len = cd_len; memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); source = curve_data->data; target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; for (i = 0; i < curve_data->channels; i++) { __le16 *freq = source; source += sizeof(__le16); *((__le16 *)target) = *freq; target += sizeof(__le16); for (j = 0; j < curve_data->points_per_channel; j++) { dst = target; src = source; dst->rf_power = src->rf_power; dst->pa_detector = src->pa_detector; dst->data_64qam = src->pcv; /* "invent" the points for the other modulations */ #define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y)) dst->data_16qam = SUB(src->pcv, 12); dst->data_qpsk = SUB(dst->data_16qam, 12); dst->data_bpsk = SUB(dst->data_qpsk, 12); dst->data_barker = SUB(dst->data_bpsk, 14); #undef SUB target += sizeof(*dst); source += sizeof(*src); } } return 0; } static int p54_convert_rev1(struct ieee80211_hw *dev, struct pda_pa_curve_data *curve_data) { struct p54_common *priv = dev->priv; struct p54_pa_curve_data_sample *dst; struct pda_pa_curve_data_sample_rev1 *src; size_t cd_len = sizeof(*curve_data) + (curve_data->points_per_channel*sizeof(*dst) + 2) * curve_data->channels; unsigned int i, j; void *source, *target; priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data), GFP_KERNEL); if (!priv->curve_data) return -ENOMEM; priv->curve_data->entries = curve_data->channels; priv->curve_data->entry_size = sizeof(__le16) + sizeof(*dst) * curve_data->points_per_channel; priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); priv->curve_data->len = cd_len; memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); source = curve_data->data; target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; for (i = 0; i < curve_data->channels; i++) { __le16 *freq = source; source += sizeof(__le16); *((__le16 *)target) = *freq; target += sizeof(__le16); for (j = 0; j < curve_data->points_per_channel; j++) { memcpy(target, source, sizeof(*src)); target += sizeof(*dst); source += sizeof(*src); } source++; } return 0; } static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; static int p54_parse_rssical(struct ieee80211_hw *dev, u8 *data, int len, u16 type) { struct p54_common *priv = dev->priv; struct p54_rssi_db_entry *entry; size_t db_len, entries; int offset = 0, i; if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; if (len != sizeof(struct pda_rssi_cal_entry) * entries) { wiphy_err(dev->wiphy, "rssical size mismatch.\n"); goto err_data; } } else { /* * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...) * have an empty two byte header. */ if (*((__le16 *)&data[offset]) == cpu_to_le16(0)) offset += 2; entries = (len - offset) / sizeof(struct pda_rssi_cal_ext_entry); if ((len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || entries <= 0) { wiphy_err(dev->wiphy, "invalid rssi database.\n"); goto err_data; } } db_len = sizeof(*entry) * entries; priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL); if (!priv->rssi_db) return -ENOMEM; priv->rssi_db->offset = 0; priv->rssi_db->entries = entries; priv->rssi_db->entry_size = sizeof(*entry); priv->rssi_db->len = db_len; entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset); if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset]; for (i = 0; i < entries; i++) { entry[i].freq = le16_to_cpu(cal[i].freq); entry[i].mul = (s16) le16_to_cpu(cal[i].mul); entry[i].add = (s16) le16_to_cpu(cal[i].add); } } else { struct pda_rssi_cal_entry *cal = (void *) &data[offset]; for (i = 0; i < entries; i++) { u16 freq = 0; switch (i) { case IEEE80211_BAND_2GHZ: freq = 2437; break; case IEEE80211_BAND_5GHZ: freq = 5240; break; } entry[i].freq = freq; entry[i].mul = (s16) le16_to_cpu(cal[i].mul); entry[i].add = (s16) le16_to_cpu(cal[i].add); } } /* sort the list by channel frequency */ sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); return 0; err_data: wiphy_err(dev->wiphy, "rssi calibration data packing type:(%x) len:%d.\n", type, len); print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len); wiphy_err(dev->wiphy, "please report this issue.\n"); return -EINVAL; } struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq) { struct p54_rssi_db_entry *entry; int i, found = -1; if (!priv->rssi_db) return &p54_rssi_default; entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset); for (i = 0; i < priv->rssi_db->entries; i++) { if (!same_band(freq, entry[i].freq)) continue; if (found == -1) { found = i; continue; } /* nearest match */ if (abs(freq - entry[i].freq) < abs(freq - entry[found].freq)) { found = i; continue; } else { break; } } return found < 0 ? &p54_rssi_default : &entry[found]; } static void p54_parse_default_country(struct ieee80211_hw *dev, void *data, int len) { struct pda_country *country; if (len != sizeof(*country)) { wiphy_err(dev->wiphy, "found possible invalid default country eeprom entry. (entry size: %d)\n", len); print_hex_dump_bytes("country:", DUMP_PREFIX_NONE, data, len); wiphy_err(dev->wiphy, "please report this issue.\n"); return; } country = (struct pda_country *) data; if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO) regulatory_hint(dev->wiphy, country->alpha2); else { /* TODO: * write a shared/common function that converts * "Regulatory domain codes" (802.11-2007 14.8.2.2) * into ISO/IEC 3166-1 alpha2 for regulatory_hint. */ } } static int p54_convert_output_limits(struct ieee80211_hw *dev, u8 *data, size_t len) { struct p54_common *priv = dev->priv; if (len < 2) return -EINVAL; if (data[0] != 0) { wiphy_err(dev->wiphy, "unknown output power db revision:%x\n", data[0]); return -EINVAL; } if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len) return -EINVAL; priv->output_limit = kmalloc(data[1] * sizeof(struct pda_channel_output_limit) + sizeof(*priv->output_limit), GFP_KERNEL); if (!priv->output_limit) return -ENOMEM; priv->output_limit->offset = 0; priv->output_limit->entries = data[1]; priv->output_limit->entry_size = sizeof(struct pda_channel_output_limit); priv->output_limit->len = priv->output_limit->entry_size * priv->output_limit->entries + priv->output_limit->offset; memcpy(priv->output_limit->data, &data[2], data[1] * sizeof(struct pda_channel_output_limit)); return 0; } static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src, size_t total_len) { struct p54_cal_database *dst; size_t payload_len, entries, entry_size, offset; payload_len = le16_to_cpu(src->len); entries = le16_to_cpu(src->entries); entry_size = le16_to_cpu(src->entry_size); offset = le16_to_cpu(src->offset); if (((entries * entry_size + offset) != payload_len) || (payload_len + sizeof(*src) != total_len)) return NULL; dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL); if (!dst) return NULL; dst->entries = entries; dst->entry_size = entry_size; dst->offset = offset; dst->len = payload_len; memcpy(dst->data, src->data, payload_len); return dst; } int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) { struct p54_common *priv = dev->priv; struct eeprom_pda_wrap *wrap; struct pda_entry *entry; unsigned int data_len, entry_len; void *tmp; int err; u8 *end = (u8 *)eeprom + len; u16 synth = 0; u16 crc16 = ~0; wrap = (struct eeprom_pda_wrap *) eeprom; entry = (void *)wrap->data + le16_to_cpu(wrap->len); /* verify that at least the entry length/code fits */ while ((u8 *)entry <= end - sizeof(*entry)) { entry_len = le16_to_cpu(entry->len); data_len = ((entry_len - 1) << 1); /* abort if entry exceeds whole structure */ if ((u8 *)entry + sizeof(*entry) + data_len > end) break; switch (le16_to_cpu(entry->code)) { case PDR_MAC_ADDRESS: if (data_len != ETH_ALEN) break; SET_IEEE80211_PERM_ADDR(dev, entry->data); break; case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS: if (priv->output_limit) break; err = p54_convert_output_limits(dev, entry->data, data_len); if (err) goto err; break; case PDR_PRISM_PA_CAL_CURVE_DATA: { struct pda_pa_curve_data *curve_data = (struct pda_pa_curve_data *)entry->data; if (data_len < sizeof(*curve_data)) { err = -EINVAL; goto err; } switch (curve_data->cal_method_rev) { case 0: err = p54_convert_rev0(dev, curve_data); break; case 1: err = p54_convert_rev1(dev, curve_data); break; default: wiphy_err(dev->wiphy, "unknown curve data revision %d\n", curve_data->cal_method_rev); err = -ENODEV; break; } if (err) goto err; } break; case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: priv->iq_autocal = kmemdup(entry->data, data_len, GFP_KERNEL); if (!priv->iq_autocal) { err = -ENOMEM; goto err; } priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry); break; case PDR_DEFAULT_COUNTRY: p54_parse_default_country(dev, entry->data, data_len); break; case PDR_INTERFACE_LIST: tmp = entry->data; while ((u8 *)tmp < entry->data + data_len) { struct exp_if *exp_if = tmp; if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000)) synth = le16_to_cpu(exp_if->variant); tmp += sizeof(*exp_if); } break; case PDR_HARDWARE_PLATFORM_COMPONENT_ID: if (data_len < 2) break; priv->version = *(u8 *)(entry->data + 1); break; case PDR_RSSI_LINEAR_APPROXIMATION: case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: err = p54_parse_rssical(dev, entry->data, data_len, le16_to_cpu(entry->code)); if (err) goto err; break; case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { struct pda_custom_wrapper *pda = (void *) entry->data; __le16 *src; u16 *dst; int i; if (priv->rssi_db || data_len < sizeof(*pda)) break; priv->rssi_db = p54_convert_db(pda, data_len); if (!priv->rssi_db) break; src = (void *) priv->rssi_db->data; dst = (void *) priv->rssi_db->data; for (i = 0; i < priv->rssi_db->entries; i++) *(dst++) = (s16) le16_to_cpu(*(src++)); } break; case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { struct pda_custom_wrapper *pda = (void *) entry->data; if (priv->output_limit || data_len < sizeof(*pda)) break; priv->output_limit = p54_convert_db(pda, data_len); } break; case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: { struct pda_custom_wrapper *pda = (void *) entry->data; if (priv->curve_data || data_len < sizeof(*pda)) break; priv->curve_data = p54_convert_db(pda, data_len); } break; case PDR_END: crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry)); if (crc16 != le16_to_cpup((__le16 *)entry->data)) { wiphy_err(dev->wiphy, "eeprom failed checksum " "test!\n"); err = -ENOMSG; goto err; } else { goto good_eeprom; } break; default: break; } crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2); entry = (void *)entry + (entry_len + 1) * 2; } wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n"); err = -ENODATA; goto err; good_eeprom: if (!synth || !priv->iq_autocal || !priv->output_limit || !priv->curve_data) { wiphy_err(dev->wiphy, "not all required entries found in eeprom!\n"); err = -EINVAL; goto err; } priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; err = p54_generate_channel_lists(dev); if (err) goto err; if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) p54_init_xbow_synth(priv); if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) dev->wiphy->bands[IEEE80211_BAND_2GHZ] = priv->band_table[IEEE80211_BAND_2GHZ]; if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) dev->wiphy->bands[IEEE80211_BAND_5GHZ] = priv->band_table[IEEE80211_BAND_5GHZ]; if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) priv->rx_diversity_mask = 3; if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) priv->tx_diversity_mask = 3; if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { u8 perm_addr[ETH_ALEN]; wiphy_warn(dev->wiphy, "Invalid hwaddr! Using randomly generated MAC addr\n"); eth_random_addr(perm_addr); SET_IEEE80211_PERM_ADDR(dev, perm_addr); } priv->cur_rssi = &p54_rssi_default; wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", dev->wiphy->perm_addr, priv->version, p54_rf_chips[priv->rxhw]); return 0; err: kfree(priv->iq_autocal); kfree(priv->output_limit); kfree(priv->curve_data); kfree(priv->rssi_db); kfree(priv->survey); priv->iq_autocal = NULL; priv->output_limit = NULL; priv->curve_data = NULL; priv->rssi_db = NULL; priv->survey = NULL; wiphy_err(dev->wiphy, "eeprom parse failed!\n"); return err; } EXPORT_SYMBOL_GPL(p54_parse_eeprom); int p54_read_eeprom(struct ieee80211_hw *dev) { struct p54_common *priv = dev->priv; size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize; int ret = -ENOMEM; void *eeprom; maxblocksize = EEPROM_READBACK_LEN; if (priv->fw_var >= 0x509) maxblocksize -= 0xc; else maxblocksize -= 0x4; eeprom = kzalloc(eeprom_size, GFP_KERNEL); if (unlikely(!eeprom)) goto free; while (eeprom_size) { blocksize = min(eeprom_size, maxblocksize); ret = p54_download_eeprom(priv, eeprom + offset, offset, blocksize); if (unlikely(ret)) goto free; offset += blocksize; eeprom_size -= blocksize; } ret = p54_parse_eeprom(dev, eeprom, offset); free: kfree(eeprom); return ret; } EXPORT_SYMBOL_GPL(p54_read_eeprom); compat-drivers-2012-09-18/drivers/net/wireless/p54/Makefile0000644000175000017500000000035212026211315022531 0ustar mcgrofmcgrofp54common-objs := eeprom.o fwio.o txrx.o main.o p54common-$(CONFIG_P54_LEDS) += led.o obj-$(CONFIG_P54_COMMON) += p54common.o obj-$(CONFIG_P54_USB) += p54usb.o obj-$(CONFIG_P54_PCI) += p54pci.o obj-$(CONFIG_P54_SPI) += p54spi.o compat-drivers-2012-09-18/drivers/net/wireless/p54/Kconfig0000644000175000017500000000444112026211315022377 0ustar mcgrofmcgrofconfig P54_COMMON tristate "Softmac Prism54 support" depends on MAC80211 && EXPERIMENTAL select FW_LOADER select CRC_CCITT ---help--- This is common code for isl38xx/stlc45xx based modules. This module does nothing by itself - the USB/PCI/SPI front-ends also need to be enabled in order to support any devices. These devices require softmac firmware which can be found at If you choose to build a module, it'll be called p54common. config P54_USB tristate "Prism54 USB support" depends on P54_COMMON && USB select CRC32 ---help--- This driver is for USB isl38xx based wireless cards. These devices require softmac firmware which can be found at If you choose to build a module, it'll be called p54usb. config P54_PCI tristate "Prism54 PCI support" depends on P54_COMMON && PCI ---help--- This driver is for PCI isl38xx based wireless cards. This driver supports most devices that are supported by the fullmac prism54 driver plus many devices which are not supported by the fullmac driver/firmware. This driver requires softmac firmware which can be found at If you choose to build a module, it'll be called p54pci. config P54_SPI tristate "Prism54 SPI (stlc45xx) support" depends on P54_COMMON && SPI_MASTER && GENERIC_HARDIRQS ---help--- This driver is for stlc4550 or stlc4560 based wireless chips such as Nokia's N800/N810 Portable Internet Tablet. If you choose to build a module, it'll be called p54spi. config P54_SPI_DEFAULT_EEPROM bool "Include fallback EEPROM blob" depends on P54_SPI default n ---help--- Unlike the PCI or USB devices, the SPI variants don't have a dedicated EEPROM chip to store all device specific values for calibration, country and interface settings. The driver will try to load the image "3826.eeprom", if the file is put at the right place. (usually /lib/firmware.) Only if this request fails, this option will provide a backup set of generic values to get the device working. Enabling this option adds about 4k to p54spi. config P54_LEDS bool depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON) default y compat-drivers-2012-09-18/drivers/net/wireless/libertas/0000755000175000017500000000000012026211315022266 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/libertas/main.c0000644000175000017500000007333312026211315023367 0ustar mcgrofmcgrof/* * This file contains the major functions in WLAN * driver. It includes init, exit, open, close and main * thread etc.. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include "host.h" #include "decl.h" #include "dev.h" #include "cfg.h" #include "debugfs.h" #include "cmd.h" #include "mesh.h" #define DRIVER_RELEASE_VERSION "323.p0" const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION #ifdef DEBUG "-dbg" #endif ""; /* Module parameters */ unsigned int lbs_debug; EXPORT_SYMBOL_GPL(lbs_debug); module_param_named(libertas_debug, lbs_debug, int, 0644); unsigned int lbs_disablemesh; EXPORT_SYMBOL_GPL(lbs_disablemesh); module_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644); /* * This global structure is used to send the confirm_sleep command as * fast as possible down to the firmware. */ struct cmd_confirm_sleep confirm_sleep; /* * the table to keep region code */ u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; /* * FW rate table. FW refers to rates by their index in this table, not by the * rate value itself. Values of 0x00 are * reserved positions. */ static u8 fw_data_rates[MAX_RATES] = { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 }; /** * lbs_fw_index_to_data_rate - use index to get the data rate * * @idx: The index of data rate * returns: data rate or 0 */ u32 lbs_fw_index_to_data_rate(u8 idx) { if (idx >= sizeof(fw_data_rates)) idx = 0; return fw_data_rates[idx]; } /** * lbs_data_rate_to_fw_index - use rate to get the index * * @rate: data rate * returns: index or 0 */ u8 lbs_data_rate_to_fw_index(u32 rate) { u8 i; if (!rate) return 0; for (i = 0; i < sizeof(fw_data_rates); i++) { if (rate == fw_data_rates[i]) return i; } return 0; } int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) { int ret = 0; switch (type) { case NL80211_IFTYPE_MONITOR: ret = lbs_set_monitor_mode(priv, 1); break; case NL80211_IFTYPE_STATION: if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) ret = lbs_set_monitor_mode(priv, 0); if (!ret) ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); break; case NL80211_IFTYPE_ADHOC: if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) ret = lbs_set_monitor_mode(priv, 0); if (!ret) ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); break; default: ret = -ENOTSUPP; } return ret; } int lbs_start_iface(struct lbs_private *priv) { struct cmd_ds_802_11_mac_address cmd; int ret; if (priv->power_restore) { ret = priv->power_restore(priv); if (ret) return ret; } cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); if (ret) { lbs_deb_net("set MAC address failed\n"); goto err; } ret = lbs_set_iface_type(priv, priv->wdev->iftype); if (ret) { lbs_deb_net("set iface type failed\n"); goto err; } ret = lbs_set_11d_domain_info(priv); if (ret) { lbs_deb_net("set 11d domain info failed\n"); goto err; } lbs_update_channel(priv); priv->iface_running = true; return 0; err: if (priv->power_save) priv->power_save(priv); return ret; } /** * lbs_dev_open - open the ethX interface * * @dev: A pointer to &net_device structure * returns: 0 or -EBUSY if monitor mode active */ static int lbs_dev_open(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; int ret = 0; lbs_deb_enter(LBS_DEB_NET); if (!priv->iface_running) { ret = lbs_start_iface(priv); if (ret) goto out; } spin_lock_irq(&priv->driver_lock); netif_carrier_off(dev); if (!priv->tx_pending_len) netif_wake_queue(dev); spin_unlock_irq(&priv->driver_lock); out: lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); return ret; } static bool lbs_command_queue_empty(struct lbs_private *priv) { unsigned long flags; bool ret; spin_lock_irqsave(&priv->driver_lock, flags); ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); spin_unlock_irqrestore(&priv->driver_lock, flags); return ret; } int lbs_stop_iface(struct lbs_private *priv) { unsigned long flags; int ret = 0; lbs_deb_enter(LBS_DEB_MAIN); spin_lock_irqsave(&priv->driver_lock, flags); priv->iface_running = false; kfree_skb(priv->currenttxskb); priv->currenttxskb = NULL; priv->tx_pending_len = 0; spin_unlock_irqrestore(&priv->driver_lock, flags); cancel_work_sync(&priv->mcast_work); del_timer_sync(&priv->tx_lockup_timer); /* Disable command processing, and wait for all commands to complete */ lbs_deb_main("waiting for commands to complete\n"); wait_event(priv->waitq, lbs_command_queue_empty(priv)); lbs_deb_main("all commands completed\n"); if (priv->power_save) ret = priv->power_save(priv); lbs_deb_leave(LBS_DEB_MAIN); return ret; } /** * lbs_eth_stop - close the ethX interface * * @dev: A pointer to &net_device structure * returns: 0 */ static int lbs_eth_stop(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; lbs_deb_enter(LBS_DEB_NET); if (priv->connect_status == LBS_CONNECTED) lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); spin_lock_irq(&priv->driver_lock); netif_stop_queue(dev); spin_unlock_irq(&priv->driver_lock); lbs_update_mcast(priv); cancel_delayed_work_sync(&priv->scan_work); if (priv->scan_req) lbs_scan_done(priv); netif_carrier_off(priv->dev); if (!lbs_iface_active(priv)) lbs_stop_iface(priv); lbs_deb_leave(LBS_DEB_NET); return 0; } void lbs_host_to_card_done(struct lbs_private *priv) { unsigned long flags; lbs_deb_enter(LBS_DEB_THREAD); spin_lock_irqsave(&priv->driver_lock, flags); del_timer(&priv->tx_lockup_timer); priv->dnld_sent = DNLD_RES_RECEIVED; /* Wake main thread if commands are pending */ if (!priv->cur_cmd || priv->tx_pending_len > 0) { if (!priv->wakeup_dev_required) wake_up(&priv->waitq); } spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_THREAD); } EXPORT_SYMBOL_GPL(lbs_host_to_card_done); int lbs_set_mac_address(struct net_device *dev, void *addr) { int ret = 0; struct lbs_private *priv = dev->ml_priv; struct sockaddr *phwaddr = addr; lbs_deb_enter(LBS_DEB_NET); /* * Can only set MAC address when all interfaces are down, to be written * to the hardware when one of them is brought up. */ if (lbs_iface_active(priv)) return -EBUSY; /* In case it was called from the mesh device */ dev = priv->dev; memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); if (priv->mesh_dev) memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); return ret; } static inline int mac_in_list(unsigned char *list, int list_len, unsigned char *mac) { while (list_len) { if (!memcmp(list, mac, ETH_ALEN)) return 1; list += ETH_ALEN; list_len--; } return 0; } static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, struct net_device *dev, int nr_addrs) { int i = nr_addrs; struct netdev_hw_addr *ha; int cnt; if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) return nr_addrs; netif_addr_lock_bh(dev); cnt = netdev_mc_count(dev); netdev_for_each_mc_addr(ha, dev) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { #else if (mac_in_list(cmd->maclist, nr_addrs, ha->dmi_addr)) { #endif lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr); #else ha->dmi_addr); #endif cnt--; continue; } if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) break; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); #else memcpy(&cmd->maclist[6*i], ha->dmi_addr, ETH_ALEN); #endif lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr); #else ha->dmi_addr); #endif i++; cnt--; } netif_addr_unlock_bh(dev); if (cnt) return -EOVERFLOW; return i; } void lbs_update_mcast(struct lbs_private *priv) { struct cmd_ds_mac_multicast_adr mcast_cmd; int dev_flags = 0; int nr_addrs; int old_mac_control = priv->mac_control; lbs_deb_enter(LBS_DEB_NET); if (netif_running(priv->dev)) dev_flags |= priv->dev->flags; if (priv->mesh_dev && netif_running(priv->mesh_dev)) dev_flags |= priv->mesh_dev->flags; if (dev_flags & IFF_PROMISC) { priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | CMD_ACT_MAC_MULTICAST_ENABLE); goto out_set_mac_control; } else if (dev_flags & IFF_ALLMULTI) { do_allmulti: priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | CMD_ACT_MAC_MULTICAST_ENABLE); goto out_set_mac_control; } /* Once for priv->dev, again for priv->mesh_dev if it exists */ nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0); if (nr_addrs >= 0 && priv->mesh_dev) nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs); if (nr_addrs < 0) goto do_allmulti; if (nr_addrs) { int size = offsetof(struct cmd_ds_mac_multicast_adr, maclist[6*nr_addrs]); mcast_cmd.action = cpu_to_le16(CMD_ACT_SET); mcast_cmd.hdr.size = cpu_to_le16(size); mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs); lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size); priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; } else priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | CMD_ACT_MAC_ALL_MULTICAST_ENABLE); out_set_mac_control: if (priv->mac_control != old_mac_control) lbs_set_mac_control(priv); lbs_deb_leave(LBS_DEB_NET); } static void lbs_set_mcast_worker(struct work_struct *work) { struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); lbs_update_mcast(priv); } void lbs_set_multicast_list(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; schedule_work(&priv->mcast_work); } /** * lbs_thread - handles the major jobs in the LBS driver. * It handles all events generated by firmware, RX data received * from firmware and TX data sent from kernel. * * @data: A pointer to &lbs_thread structure * returns: 0 */ static int lbs_thread(void *data) { struct net_device *dev = data; struct lbs_private *priv = dev->ml_priv; wait_queue_t wait; lbs_deb_enter(LBS_DEB_THREAD); init_waitqueue_entry(&wait, current); for (;;) { int shouldsleep; u8 resp_idx; lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); add_wait_queue(&priv->waitq, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&priv->driver_lock); if (kthread_should_stop()) shouldsleep = 0; /* Bye */ else if (priv->surpriseremoved) shouldsleep = 1; /* We need to wait until we're _told_ to die */ else if (priv->psstate == PS_STATE_SLEEP) shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ else if (priv->cmd_timed_out) shouldsleep = 0; /* Command timed out. Recover */ else if (!priv->fw_ready) shouldsleep = 1; /* Firmware not ready. We're waiting for it */ else if (priv->dnld_sent) shouldsleep = 1; /* Something is en route to the device already */ else if (priv->tx_pending_len > 0) shouldsleep = 0; /* We've a packet to send */ else if (priv->resp_len[priv->resp_idx]) shouldsleep = 0; /* We have a command response */ else if (priv->cur_cmd) shouldsleep = 1; /* Can't send a command; one already running */ else if (!list_empty(&priv->cmdpendingq) && !(priv->wakeup_dev_required)) shouldsleep = 0; /* We have a command to send */ else if (kfifo_len(&priv->event_fifo)) shouldsleep = 0; /* We have an event to process */ else shouldsleep = 1; /* No command */ if (shouldsleep) { lbs_deb_thread("sleeping, connect_status %d, " "psmode %d, psstate %d\n", priv->connect_status, priv->psmode, priv->psstate); spin_unlock_irq(&priv->driver_lock); schedule(); } else spin_unlock_irq(&priv->driver_lock); lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", priv->currenttxskb, priv->dnld_sent); set_current_state(TASK_RUNNING); remove_wait_queue(&priv->waitq, &wait); lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); if (kthread_should_stop()) { lbs_deb_thread("break from main thread\n"); break; } if (priv->surpriseremoved) { lbs_deb_thread("adapter removed; waiting to die...\n"); continue; } lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); /* Process any pending command response */ spin_lock_irq(&priv->driver_lock); resp_idx = priv->resp_idx; if (priv->resp_len[resp_idx]) { spin_unlock_irq(&priv->driver_lock); lbs_process_command_response(priv, priv->resp_buf[resp_idx], priv->resp_len[resp_idx]); spin_lock_irq(&priv->driver_lock); priv->resp_len[resp_idx] = 0; } spin_unlock_irq(&priv->driver_lock); /* Process hardware events, e.g. card removed, link lost */ spin_lock_irq(&priv->driver_lock); while (kfifo_len(&priv->event_fifo)) { u32 event; if (kfifo_out(&priv->event_fifo, (unsigned char *) &event, sizeof(event)) != sizeof(event)) break; spin_unlock_irq(&priv->driver_lock); lbs_process_event(priv, event); spin_lock_irq(&priv->driver_lock); } spin_unlock_irq(&priv->driver_lock); if (priv->wakeup_dev_required) { lbs_deb_thread("Waking up device...\n"); /* Wake up device */ if (priv->exit_deep_sleep(priv)) lbs_deb_thread("Wakeup device failed\n"); continue; } /* command timeout stuff */ if (priv->cmd_timed_out && priv->cur_cmd) { struct cmd_ctrl_node *cmdnode = priv->cur_cmd; netdev_info(dev, "Timeout submitting command 0x%04x\n", le16_to_cpu(cmdnode->cmdbuf->command)); lbs_complete_command(priv, cmdnode, -ETIMEDOUT); /* Reset card, but only when it isn't in the process * of being shutdown anyway. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)) if (!dev->dismantle && priv->reset_card) #else if (priv->reset_card) #endif priv->reset_card(priv); } priv->cmd_timed_out = 0; if (!priv->fw_ready) continue; /* Check if we need to confirm Sleep Request received previously */ if (priv->psstate == PS_STATE_PRE_SLEEP && !priv->dnld_sent && !priv->cur_cmd) { if (priv->connect_status == LBS_CONNECTED) { lbs_deb_thread("pre-sleep, currenttxskb %p, " "dnld_sent %d, cur_cmd %p\n", priv->currenttxskb, priv->dnld_sent, priv->cur_cmd); lbs_ps_confirm_sleep(priv); } else { /* workaround for firmware sending * deauth/linkloss event immediately * after sleep request; remove this * after firmware fixes it */ priv->psstate = PS_STATE_AWAKE; netdev_alert(dev, "ignore PS_SleepConfirm in non-connected state\n"); } } /* The PS state is changed during processing of Sleep Request * event above */ if ((priv->psstate == PS_STATE_SLEEP) || (priv->psstate == PS_STATE_PRE_SLEEP)) continue; if (priv->is_deep_sleep) continue; /* Execute the next command */ if (!priv->dnld_sent && !priv->cur_cmd) lbs_execute_next_command(priv); spin_lock_irq(&priv->driver_lock); if (!priv->dnld_sent && priv->tx_pending_len > 0) { int ret = priv->hw_host_to_card(priv, MVMS_DAT, priv->tx_pending_buf, priv->tx_pending_len); if (ret) { lbs_deb_tx("host_to_card failed %d\n", ret); priv->dnld_sent = DNLD_RES_RECEIVED; } else { mod_timer(&priv->tx_lockup_timer, jiffies + (HZ * 5)); } priv->tx_pending_len = 0; if (!priv->currenttxskb) { /* We can wake the queues immediately if we aren't waiting for TX feedback */ if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); if (priv->mesh_dev && netif_running(priv->mesh_dev)) netif_wake_queue(priv->mesh_dev); } } spin_unlock_irq(&priv->driver_lock); } del_timer(&priv->command_timer); del_timer(&priv->tx_lockup_timer); del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_THREAD); return 0; } /** * lbs_setup_firmware - gets the HW spec from the firmware and sets * some basic parameters * * @priv: A pointer to &struct lbs_private structure * returns: 0 or -1 */ static int lbs_setup_firmware(struct lbs_private *priv) { int ret = -1; s16 curlevel = 0, minlevel = 0, maxlevel = 0; lbs_deb_enter(LBS_DEB_FW); /* Read MAC address from firmware */ memset(priv->current_addr, 0xff, ETH_ALEN); ret = lbs_update_hw_spec(priv); if (ret) goto done; /* Read power levels if available */ ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); if (ret == 0) { priv->txpower_cur = curlevel; priv->txpower_min = minlevel; priv->txpower_max = maxlevel; } /* Send cmd to FW to enable 11D function */ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); if (ret) goto done; ret = lbs_set_mac_control_sync(priv); done: lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); return ret; } int lbs_suspend(struct lbs_private *priv) { int ret; lbs_deb_enter(LBS_DEB_FW); if (priv->is_deep_sleep) { ret = lbs_set_deep_sleep(priv, 0); if (ret) { netdev_err(priv->dev, "deep sleep cancellation failed: %d\n", ret); return ret; } priv->deep_sleep_required = 1; } ret = lbs_set_host_sleep(priv, 1); netif_device_detach(priv->dev); if (priv->mesh_dev) netif_device_detach(priv->mesh_dev); lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(lbs_suspend); int lbs_resume(struct lbs_private *priv) { int ret; lbs_deb_enter(LBS_DEB_FW); ret = lbs_set_host_sleep(priv, 0); netif_device_attach(priv->dev); if (priv->mesh_dev) netif_device_attach(priv->mesh_dev); if (priv->deep_sleep_required) { priv->deep_sleep_required = 0; ret = lbs_set_deep_sleep(priv, 1); if (ret) netdev_err(priv->dev, "deep sleep activation failed: %d\n", ret); } if (priv->setup_fw_on_resume) ret = lbs_setup_firmware(priv); lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(lbs_resume); /** * lbs_cmd_timeout_handler - handles the timeout of command sending. * It will re-send the same command again. * * @data: &struct lbs_private pointer */ static void lbs_cmd_timeout_handler(unsigned long data) { struct lbs_private *priv = (struct lbs_private *)data; unsigned long flags; lbs_deb_enter(LBS_DEB_CMD); spin_lock_irqsave(&priv->driver_lock, flags); if (!priv->cur_cmd) goto out; netdev_info(priv->dev, "command 0x%04x timed out\n", le16_to_cpu(priv->cur_cmd->cmdbuf->command)); priv->cmd_timed_out = 1; /* * If the device didn't even acknowledge the command, reset the state * so that we don't block all future commands due to this one timeout. */ if (priv->dnld_sent == DNLD_CMD_SENT) priv->dnld_sent = DNLD_RES_RECEIVED; wake_up(&priv->waitq); out: spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_CMD); } /** * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames * to the hardware. This is known to frequently happen with SD8686 when * waking up after a Wake-on-WLAN-triggered resume. * * @data: &struct lbs_private pointer */ static void lbs_tx_lockup_handler(unsigned long data) { struct lbs_private *priv = (struct lbs_private *)data; unsigned long flags; lbs_deb_enter(LBS_DEB_TX); spin_lock_irqsave(&priv->driver_lock, flags); netdev_info(priv->dev, "TX lockup detected\n"); if (priv->reset_card) priv->reset_card(priv); priv->dnld_sent = DNLD_RES_RECEIVED; wake_up_interruptible(&priv->waitq); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_TX); } /** * auto_deepsleep_timer_fn - put the device back to deep sleep mode when * timer expires and no activity (command, event, data etc.) is detected. * @data: &struct lbs_private pointer * returns: N/A */ static void auto_deepsleep_timer_fn(unsigned long data) { struct lbs_private *priv = (struct lbs_private *)data; lbs_deb_enter(LBS_DEB_CMD); if (priv->is_activity_detected) { priv->is_activity_detected = 0; } else { if (priv->is_auto_deep_sleep_enabled && (!priv->wakeup_dev_required) && (priv->connect_status != LBS_CONNECTED)) { struct cmd_header cmd; lbs_deb_main("Entering auto deep sleep mode...\n"); memset(&cmd, 0, sizeof(cmd)); cmd.size = cpu_to_le16(sizeof(cmd)); lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd)); } } mod_timer(&priv->auto_deepsleep_timer , jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); lbs_deb_leave(LBS_DEB_CMD); } int lbs_enter_auto_deep_sleep(struct lbs_private *priv) { lbs_deb_enter(LBS_DEB_SDIO); priv->is_auto_deep_sleep_enabled = 1; if (priv->is_deep_sleep) priv->wakeup_dev_required = 1; mod_timer(&priv->auto_deepsleep_timer , jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); lbs_deb_leave(LBS_DEB_SDIO); return 0; } int lbs_exit_auto_deep_sleep(struct lbs_private *priv) { lbs_deb_enter(LBS_DEB_SDIO); priv->is_auto_deep_sleep_enabled = 0; priv->auto_deep_sleep_timeout = 0; del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_SDIO); return 0; } static int lbs_init_adapter(struct lbs_private *priv) { int ret; lbs_deb_enter(LBS_DEB_MAIN); memset(priv->current_addr, 0xff, ETH_ALEN); priv->connect_status = LBS_DISCONNECTED; priv->channel = DEFAULT_AD_HOC_CHANNEL; priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; priv->radio_on = 1; priv->psmode = LBS802_11POWERMODECAM; priv->psstate = PS_STATE_FULL_POWER; priv->is_deep_sleep = 0; priv->is_auto_deep_sleep_enabled = 0; priv->deep_sleep_required = 0; priv->wakeup_dev_required = 0; init_waitqueue_head(&priv->ds_awake_q); init_waitqueue_head(&priv->scan_q); priv->authtype_auto = 1; priv->is_host_sleep_configured = 0; priv->is_host_sleep_activated = 0; init_waitqueue_head(&priv->host_sleep_q); init_waitqueue_head(&priv->fw_waitq); mutex_init(&priv->lock); setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, (unsigned long)priv); setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler, (unsigned long)priv); setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, (unsigned long)priv); INIT_LIST_HEAD(&priv->cmdfreeq); INIT_LIST_HEAD(&priv->cmdpendingq); spin_lock_init(&priv->driver_lock); /* Allocate the command buffers */ if (lbs_allocate_cmd_buffer(priv)) { pr_err("Out of memory allocating command buffers\n"); ret = -ENOMEM; goto out; } priv->resp_idx = 0; priv->resp_len[0] = priv->resp_len[1] = 0; /* Create the event FIFO */ ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL); if (ret) { pr_err("Out of memory allocating event FIFO buffer\n"); goto out; } out: lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); return ret; } static void lbs_free_adapter(struct lbs_private *priv) { lbs_deb_enter(LBS_DEB_MAIN); lbs_free_cmd_buffer(priv); kfifo_free(&priv->event_fifo); del_timer(&priv->command_timer); del_timer(&priv->tx_lockup_timer); del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_MAIN); } static const struct net_device_ops lbs_netdev_ops = { .ndo_open = lbs_dev_open, .ndo_stop = lbs_eth_stop, .ndo_start_xmit = lbs_hard_start_xmit, .ndo_set_mac_address = lbs_set_mac_address, .ndo_set_rx_mode = lbs_set_multicast_list, .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; /** * lbs_add_card - adds the card. It will probe the * card, allocate the lbs_priv and initialize the device. * * @card: A pointer to card * @dmdev: A pointer to &struct device * returns: A pointer to &struct lbs_private structure */ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) { struct net_device *dev; struct wireless_dev *wdev; struct lbs_private *priv = NULL; lbs_deb_enter(LBS_DEB_MAIN); /* Allocate an Ethernet device and register it */ wdev = lbs_cfg_alloc(dmdev); if (IS_ERR(wdev)) { pr_err("cfg80211 init failed\n"); goto done; } wdev->iftype = NL80211_IFTYPE_STATION; priv = wdev_priv(wdev); priv->wdev = wdev; if (lbs_init_adapter(priv)) { pr_err("failed to initialize adapter structure\n"); goto err_wdev; } dev = alloc_netdev(0, "wlan%d", ether_setup); if (!dev) { dev_err(dmdev, "no memory for network device instance\n"); goto err_adapter; } dev->ieee80211_ptr = wdev; dev->ml_priv = priv; SET_NETDEV_DEV(dev, dmdev); wdev->netdev = dev; priv->dev = dev; netdev_attach_ops(dev, &lbs_netdev_ops); dev->watchdog_timeo = 5 * HZ; dev->ethtool_ops = &lbs_ethtool_ops; dev->flags |= IFF_BROADCAST | IFF_MULTICAST; priv->card = card; strcpy(dev->name, "wlan%d"); lbs_deb_thread("Starting main thread...\n"); init_waitqueue_head(&priv->waitq); priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); if (IS_ERR(priv->main_thread)) { lbs_deb_thread("Error creating main thread.\n"); goto err_ndev; } priv->work_thread = create_singlethread_workqueue("lbs_worker"); INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); priv->wol_criteria = EHS_REMOVE_WAKEUP; priv->wol_gpio = 0xff; priv->wol_gap = 20; priv->ehs_remove_supported = true; goto done; err_ndev: free_netdev(dev); err_adapter: lbs_free_adapter(priv); err_wdev: lbs_cfg_free(priv); priv = NULL; done: lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv); return priv; } EXPORT_SYMBOL_GPL(lbs_add_card); void lbs_remove_card(struct lbs_private *priv) { struct net_device *dev = priv->dev; lbs_deb_enter(LBS_DEB_MAIN); lbs_remove_mesh(priv); if (priv->wiphy_registered) lbs_scan_deinit(priv); lbs_wait_for_firmware_load(priv); /* worker thread destruction blocks on the in-flight command which * should have been cleared already in lbs_stop_card(). */ lbs_deb_main("destroying worker thread\n"); destroy_workqueue(priv->work_thread); lbs_deb_main("done destroying worker thread\n"); if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { priv->psmode = LBS802_11POWERMODECAM; lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); } if (priv->is_deep_sleep) { priv->is_deep_sleep = 0; wake_up_interruptible(&priv->ds_awake_q); } priv->is_host_sleep_configured = 0; priv->is_host_sleep_activated = 0; wake_up_interruptible(&priv->host_sleep_q); /* Stop the thread servicing the interrupts */ priv->surpriseremoved = 1; kthread_stop(priv->main_thread); lbs_free_adapter(priv); lbs_cfg_free(priv); free_netdev(dev); lbs_deb_leave(LBS_DEB_MAIN); } EXPORT_SYMBOL_GPL(lbs_remove_card); int lbs_rtap_supported(struct lbs_private *priv) { if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) return 1; /* newer firmware use a capability mask */ return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); } int lbs_start_card(struct lbs_private *priv) { struct net_device *dev = priv->dev; int ret = -1; lbs_deb_enter(LBS_DEB_MAIN); /* poke the firmware */ ret = lbs_setup_firmware(priv); if (ret) goto done; if (!lbs_disablemesh) lbs_init_mesh(priv); else pr_info("%s: mesh disabled\n", dev->name); if (lbs_cfg_register(priv)) { pr_err("cannot register device\n"); goto done; } if (lbs_mesh_activated(priv)) lbs_start_mesh(priv); lbs_debugfs_init_one(priv, dev); netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); ret = 0; done: lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(lbs_start_card); void lbs_stop_card(struct lbs_private *priv) { struct net_device *dev; lbs_deb_enter(LBS_DEB_MAIN); if (!priv) goto out; dev = priv->dev; /* If the netdev isn't registered, it means that lbs_start_card() was * never called so we have nothing to do here. */ if (dev->reg_state != NETREG_REGISTERED) goto out; netif_stop_queue(dev); netif_carrier_off(dev); lbs_debugfs_remove_one(priv); lbs_deinit_mesh(priv); unregister_netdev(dev); out: lbs_deb_leave(LBS_DEB_MAIN); } EXPORT_SYMBOL_GPL(lbs_stop_card); void lbs_queue_event(struct lbs_private *priv, u32 event) { unsigned long flags; lbs_deb_enter(LBS_DEB_THREAD); spin_lock_irqsave(&priv->driver_lock, flags); if (priv->psstate == PS_STATE_SLEEP) priv->psstate = PS_STATE_AWAKE; kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); wake_up(&priv->waitq); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_THREAD); } EXPORT_SYMBOL_GPL(lbs_queue_event); void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) { lbs_deb_enter(LBS_DEB_THREAD); if (priv->psstate == PS_STATE_SLEEP) priv->psstate = PS_STATE_AWAKE; /* Swap buffers by flipping the response index */ BUG_ON(resp_idx > 1); priv->resp_idx = resp_idx; wake_up(&priv->waitq); lbs_deb_leave(LBS_DEB_THREAD); } EXPORT_SYMBOL_GPL(lbs_notify_command_response); static int __init lbs_init_module(void) { lbs_deb_enter(LBS_DEB_MAIN); memset(&confirm_sleep, 0, sizeof(confirm_sleep)); confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED); lbs_debugfs_init(); lbs_deb_leave(LBS_DEB_MAIN); return 0; } static void __exit lbs_exit_module(void) { lbs_deb_enter(LBS_DEB_MAIN); lbs_debugfs_remove(); lbs_deb_leave(LBS_DEB_MAIN); } module_init(lbs_init_module); module_exit(lbs_exit_module); MODULE_DESCRIPTION("Libertas WLAN Driver Library"); MODULE_AUTHOR("Marvell International Ltd."); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/libertas/if_usb.c0000644000175000017500000006356312026211315023716 0ustar mcgrofmcgrof/* * This file contains functions used in USB interface module. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #ifdef CONFIG_OLPC #include #endif #define DRV_NAME "usb8xxx" #include "host.h" #include "decl.h" #include "defs.h" #include "dev.h" #include "cmd.h" #include "if_usb.h" #define INSANEDEBUG 0 #define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0) #define MESSAGE_HEADER_LEN 4 MODULE_FIRMWARE("libertas/usb8388_v9.bin"); MODULE_FIRMWARE("libertas/usb8388_v5.bin"); MODULE_FIRMWARE("libertas/usb8388.bin"); MODULE_FIRMWARE("libertas/usb8682.bin"); MODULE_FIRMWARE("usb8388.bin"); enum { MODEL_UNKNOWN = 0x0, MODEL_8388 = 0x1, MODEL_8682 = 0x2 }; /* table of firmware file names */ static const struct lbs_fw_table fw_table[] = { { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, { MODEL_8388, "libertas/usb8388.bin", NULL }, { MODEL_8388, "usb8388.bin", NULL }, { MODEL_8682, "libertas/usb8682.bin", NULL } }; static struct usb_device_id if_usb_table[] = { /* Enter the device signature inside */ { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, if_usb_table); static void if_usb_receive(struct urb *urb); static void if_usb_receive_fwload(struct urb *urb); static void if_usb_prog_firmware(struct lbs_private *priv, int ret, const struct firmware *fw, const struct firmware *unused); static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, uint8_t *payload, uint16_t nb); static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb); static void if_usb_free(struct if_usb_card *cardp); static int if_usb_submit_rx_urb(struct if_usb_card *cardp); static int if_usb_reset_device(struct if_usb_card *cardp); /** * if_usb_write_bulk_callback - callback function to handle the status * of the URB * @urb: pointer to &urb structure * returns: N/A */ static void if_usb_write_bulk_callback(struct urb *urb) { struct if_usb_card *cardp = (struct if_usb_card *) urb->context; /* handle the transmission complete validations */ if (urb->status == 0) { struct lbs_private *priv = cardp->priv; lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n"); lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", urb->actual_length); /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not * passed up to the lbs level. */ if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) lbs_host_to_card_done(priv); } else { /* print the failure status number for debug */ pr_info("URB in failure status: %d\n", urb->status); } } /** * if_usb_free - free tx/rx urb, skb and rx buffer * @cardp: pointer to &if_usb_card * returns: N/A */ static void if_usb_free(struct if_usb_card *cardp) { lbs_deb_enter(LBS_DEB_USB); /* Unlink tx & rx urb */ usb_kill_urb(cardp->tx_urb); usb_kill_urb(cardp->rx_urb); usb_free_urb(cardp->tx_urb); cardp->tx_urb = NULL; usb_free_urb(cardp->rx_urb); cardp->rx_urb = NULL; kfree(cardp->ep_out_buf); cardp->ep_out_buf = NULL; lbs_deb_leave(LBS_DEB_USB); } static void if_usb_setup_firmware(struct lbs_private *priv) { struct if_usb_card *cardp = priv->card; struct cmd_ds_set_boot2_ver b2_cmd; struct cmd_ds_802_11_fw_wake_method wake_method; b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); b2_cmd.action = 0; b2_cmd.version = cardp->boot2_version; if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) lbs_deb_usb("Setting boot2 version failed\n"); priv->wol_gpio = 2; /* Wake via GPIO2... */ priv->wol_gap = 20; /* ... after 20ms */ lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA, (struct wol_config *) NULL); wake_method.hdr.size = cpu_to_le16(sizeof(wake_method)); wake_method.action = cpu_to_le16(CMD_ACT_GET); if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { netdev_info(priv->dev, "Firmware does not seem to support PS mode\n"); priv->fwcapinfo &= ~FW_CAPINFO_PS; } else { if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); } else { /* The versions which boot up this way don't seem to work even if we set it to the command interrupt */ priv->fwcapinfo &= ~FW_CAPINFO_PS; netdev_info(priv->dev, "Firmware doesn't wake via command interrupt; disabling PS mode\n"); } } } static void if_usb_fw_timeo(unsigned long priv) { struct if_usb_card *cardp = (void *)priv; if (cardp->fwdnldover) { lbs_deb_usb("Download complete, no event. Assuming success\n"); } else { pr_err("Download timed out\n"); cardp->surprise_removed = 1; } wake_up(&cardp->fw_wq); } #ifdef CONFIG_OLPC static void if_usb_reset_olpc_card(struct lbs_private *priv) { printk(KERN_CRIT "Resetting OLPC wireless via EC...\n"); olpc_ec_cmd(0x25, NULL, 0, NULL, 0); } #endif /** * if_usb_probe - sets the configuration values * @intf: &usb_interface pointer * @id: pointer to usb_device_id * returns: 0 on success, error code on failure */ static int if_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; struct lbs_private *priv; struct if_usb_card *cardp; int r = -ENOMEM; int i; udev = interface_to_usbdev(intf); cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); if (!cardp) goto error; setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); init_waitqueue_head(&cardp->fw_wq); cardp->udev = udev; cardp->model = (uint32_t) id->driver_info; iface_desc = intf->cur_altsetting; lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", le16_to_cpu(udev->descriptor.bcdUSB), udev->descriptor.bDeviceClass, udev->descriptor.bDeviceSubClass, udev->descriptor.bDeviceProtocol); for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(endpoint)) { cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); cardp->ep_in = usb_endpoint_num(endpoint); lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); } else if (usb_endpoint_is_bulk_out(endpoint)) { cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); cardp->ep_out = usb_endpoint_num(endpoint); lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); } } if (!cardp->ep_out_size || !cardp->ep_in_size) { lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); goto dealloc; } if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); goto dealloc; } if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) { lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); goto dealloc; } cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); if (!cardp->ep_out_buf) { lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n"); goto dealloc; } if (!(priv = lbs_add_card(cardp, &intf->dev))) goto err_add_card; cardp->priv = priv; priv->hw_host_to_card = if_usb_host_to_card; priv->enter_deep_sleep = NULL; priv->exit_deep_sleep = NULL; priv->reset_deep_sleep_wakeup = NULL; #ifdef CONFIG_OLPC if (machine_is_olpc()) priv->reset_card = if_usb_reset_olpc_card; #endif cardp->boot2_version = udev->descriptor.bcdDevice; usb_get_dev(udev); usb_set_intfdata(intf, cardp); r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, fw_table, if_usb_prog_firmware); if (r) goto err_get_fw; return 0; err_get_fw: lbs_remove_card(priv); err_add_card: if_usb_reset_device(cardp); dealloc: if_usb_free(cardp); error: return r; } /** * if_usb_disconnect - free resource and cleanup * @intf: USB interface structure * returns: N/A */ static void if_usb_disconnect(struct usb_interface *intf) { struct if_usb_card *cardp = usb_get_intfdata(intf); struct lbs_private *priv = cardp->priv; lbs_deb_enter(LBS_DEB_MAIN); cardp->surprise_removed = 1; if (priv) { lbs_stop_card(priv); lbs_remove_card(priv); } /* Unlink and free urb */ if_usb_free(cardp); usb_set_intfdata(intf, NULL); usb_put_dev(interface_to_usbdev(intf)); lbs_deb_leave(LBS_DEB_MAIN); } /** * if_usb_send_fw_pkt - download FW * @cardp: pointer to &struct if_usb_card * returns: 0 */ static int if_usb_send_fw_pkt(struct if_usb_card *cardp) { struct fwdata *fwdata = cardp->ep_out_buf; const uint8_t *firmware = cardp->fw->data; /* If we got a CRC failure on the last block, back up and retry it */ if (!cardp->CRC_OK) { cardp->totalbytes = cardp->fwlastblksent; cardp->fwseqnum--; } lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", cardp->totalbytes); /* struct fwdata (which we sent to the card) has an extra __le32 field in between the header and the data, which is not in the struct fwheader in the actual firmware binary. Insert the seqnum in the middle... */ memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], sizeof(struct fwheader)); cardp->fwlastblksent = cardp->totalbytes; cardp->totalbytes += sizeof(struct fwheader); memcpy(fwdata->data, &firmware[cardp->totalbytes], le32_to_cpu(fwdata->hdr.datalength)); lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n", le32_to_cpu(fwdata->hdr.datalength)); fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + le32_to_cpu(fwdata->hdr.datalength)); if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, cardp->totalbytes); } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); lbs_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); cardp->fwfinalblk = 1; } lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", cardp->totalbytes); return 0; } static int if_usb_reset_device(struct if_usb_card *cardp) { struct cmd_header *cmd = cardp->ep_out_buf + 4; int ret; lbs_deb_enter(LBS_DEB_USB); *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); cmd->command = cpu_to_le16(CMD_802_11_RESET); cmd->size = cpu_to_le16(sizeof(cmd)); cmd->result = cpu_to_le16(0); cmd->seqnum = cpu_to_le16(0x5a5a); usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header)); msleep(100); ret = usb_reset_device(cardp->udev); msleep(100); #ifdef CONFIG_OLPC if (ret && machine_is_olpc()) if_usb_reset_olpc_card(NULL); #endif lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret); return ret; } /** * usb_tx_block - transfer the data to the device * @cardp: pointer to &struct if_usb_card * @payload: pointer to payload data * @nb: data length * returns: 0 for success or negative error code */ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb) { int ret; /* check if device is removed */ if (cardp->surprise_removed) { lbs_deb_usbd(&cardp->udev->dev, "Device removed\n"); ret = -ENODEV; goto tx_ret; } usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, usb_sndbulkpipe(cardp->udev, cardp->ep_out), payload, nb, if_usb_write_bulk_callback, cardp); cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); } else { lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); ret = 0; } tx_ret: return ret; } static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, void (*callbackfn)(struct urb *urb)) { struct sk_buff *skb; int ret = -1; if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { pr_err("No free skb\n"); goto rx_ret; } cardp->rx_skb = skb; /* Fill the receive configuration URB and initialise the Rx call back */ usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, usb_rcvbulkpipe(cardp->udev, cardp->ep_in), skb->data + IPFIELD_ALIGN_OFFSET, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); kfree_skb(skb); cardp->rx_skb = NULL; ret = -1; } else { lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); ret = 0; } rx_ret: return ret; } static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) { return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); } static int if_usb_submit_rx_urb(struct if_usb_card *cardp) { return __if_usb_submit_rx_urb(cardp, &if_usb_receive); } static void if_usb_receive_fwload(struct urb *urb) { struct if_usb_card *cardp = urb->context; struct sk_buff *skb = cardp->rx_skb; struct fwsyncheader *syncfwheader; struct bootcmdresp bootcmdresp; if (urb->status) { lbs_deb_usbd(&cardp->udev->dev, "URB status is failed during fw load\n"); kfree_skb(skb); return; } if (cardp->fwdnldover) { __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { pr_info("Firmware ready event received\n"); wake_up(&cardp->fw_wq); } else { lbs_deb_usb("Waiting for confirmation; got %x %x\n", le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); if_usb_submit_rx_urb_fwload(cardp); } kfree_skb(skb); return; } if (cardp->bootcmdresp <= 0) { memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, sizeof(bootcmdresp)); if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { kfree_skb(skb); if_usb_submit_rx_urb_fwload(cardp); cardp->bootcmdresp = BOOT_CMD_RESP_OK; lbs_deb_usbd(&cardp->udev->dev, "Received valid boot command response\n"); return; } if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { if (!cardp->bootcmdresp) pr_info("Firmware already seems alive; resetting\n"); cardp->bootcmdresp = -1; } else { pr_info("boot cmd response wrong magic number (0x%x)\n", le32_to_cpu(bootcmdresp.magic)); } } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { pr_info("boot cmd response cmd_tag error (%d)\n", bootcmdresp.cmd); } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { pr_info("boot cmd response result error (%d)\n", bootcmdresp.result); } else { cardp->bootcmdresp = 1; lbs_deb_usbd(&cardp->udev->dev, "Received valid boot command response\n"); } kfree_skb(skb); if_usb_submit_rx_urb_fwload(cardp); return; } syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET, sizeof(struct fwsyncheader), GFP_ATOMIC); if (!syncfwheader) { lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); kfree_skb(skb); return; } if (!syncfwheader->cmd) { lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", le32_to_cpu(syncfwheader->seqnum)); cardp->CRC_OK = 1; } else { lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); cardp->CRC_OK = 0; } kfree_skb(skb); /* Give device 5s to either write firmware to its RAM or eeprom */ mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); if (cardp->fwfinalblk) { cardp->fwdnldover = 1; goto exit; } if_usb_send_fw_pkt(cardp); exit: if_usb_submit_rx_urb_fwload(cardp); kfree(syncfwheader); } #define MRVDRV_MIN_PKT_LEN 30 static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, struct if_usb_card *cardp, struct lbs_private *priv) { if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); kfree_skb(skb); return; } skb_reserve(skb, IPFIELD_ALIGN_OFFSET); skb_put(skb, recvlength); skb_pull(skb, MESSAGE_HEADER_LEN); lbs_process_rxed_packet(priv, skb); } static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, struct sk_buff *skb, struct if_usb_card *cardp, struct lbs_private *priv) { u8 i; if (recvlength > LBS_CMD_BUFFER_SIZE) { lbs_deb_usbd(&cardp->udev->dev, "The receive buffer is too large\n"); kfree_skb(skb); return; } BUG_ON(!in_interrupt()); spin_lock(&priv->driver_lock); i = (priv->resp_idx == 0) ? 1 : 0; BUG_ON(priv->resp_len[i]); priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN); memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN, priv->resp_len[i]); kfree_skb(skb); lbs_notify_command_response(priv, i); spin_unlock(&priv->driver_lock); lbs_deb_usbd(&cardp->udev->dev, "Wake up main thread to handle cmd response\n"); } /** * if_usb_receive - read the packet into the upload buffer, * wake up the main thread and initialise the Rx callack * * @urb: pointer to &struct urb * returns: N/A */ static void if_usb_receive(struct urb *urb) { struct if_usb_card *cardp = urb->context; struct sk_buff *skb = cardp->rx_skb; struct lbs_private *priv = cardp->priv; int recvlength = urb->actual_length; uint8_t *recvbuff = NULL; uint32_t recvtype = 0; __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); uint32_t event; lbs_deb_enter(LBS_DEB_USB); if (recvlength) { if (urb->status) { lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", urb->status); kfree_skb(skb); goto setup_for_next; } recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; recvtype = le32_to_cpu(pkt[0]); lbs_deb_usbd(&cardp->udev->dev, "Recv length = 0x%x, Recv type = 0x%X\n", recvlength, recvtype); } else if (urb->status) { kfree_skb(skb); goto rx_exit; } switch (recvtype) { case CMD_TYPE_DATA: process_cmdtypedata(recvlength, skb, cardp, priv); break; case CMD_TYPE_REQUEST: process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); break; case CMD_TYPE_INDICATION: /* Event handling */ event = le32_to_cpu(pkt[1]); lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event); kfree_skb(skb); /* Icky undocumented magic special case */ if (event & 0xffff0000) { u32 trycount = (event & 0xffff0000) >> 16; lbs_send_tx_feedback(priv, trycount); } else lbs_queue_event(priv, event & 0xFF); break; default: lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n", recvtype); kfree_skb(skb); break; } setup_for_next: if_usb_submit_rx_urb(cardp); rx_exit: lbs_deb_leave(LBS_DEB_USB); } /** * if_usb_host_to_card - downloads data to FW * @priv: pointer to &struct lbs_private structure * @type: type of data * @payload: pointer to data buffer * @nb: number of bytes * returns: 0 for success or negative error code */ static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, uint8_t *payload, uint16_t nb) { struct if_usb_card *cardp = priv->card; lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type); lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb); if (type == MVMS_CMD) { *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); priv->dnld_sent = DNLD_CMD_SENT; } else { *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); priv->dnld_sent = DNLD_DATA_SENT; } memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN); } /** * if_usb_issue_boot_command - issues Boot command to the Boot2 code * @cardp: pointer to &if_usb_card * @ivalue: 1:Boot from FW by USB-Download * 2:Boot from FW in EEPROM * returns: 0 for success or negative error code */ static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) { struct bootcmd *bootcmd = cardp->ep_out_buf; /* Prepare command */ bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); bootcmd->cmd = ivalue; memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); /* Issue command */ usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd)); return 0; } /** * check_fwfile_format - check the validity of Boot2/FW image * * @data: pointer to image * @totlen: image length * returns: 0 (good) or 1 (failure) */ static int check_fwfile_format(const uint8_t *data, uint32_t totlen) { uint32_t bincmd, exit; uint32_t blksize, offset, len; int ret; ret = 1; exit = len = 0; do { struct fwheader *fwh = (void *)data; bincmd = le32_to_cpu(fwh->dnldcmd); blksize = le32_to_cpu(fwh->datalength); switch (bincmd) { case FW_HAS_DATA_TO_RECV: offset = sizeof(struct fwheader) + blksize; data += offset; len += offset; if (len >= totlen) exit = 1; break; case FW_HAS_LAST_BLOCK: exit = 1; ret = 0; break; default: exit = 1; break; } } while (!exit); if (ret) pr_err("firmware file format check FAIL\n"); else lbs_deb_fw("firmware file format check PASS\n"); return ret; } static void if_usb_prog_firmware(struct lbs_private *priv, int ret, const struct firmware *fw, const struct firmware *unused) { struct if_usb_card *cardp = priv->card; int i = 0; static int reset_count = 10; lbs_deb_enter(LBS_DEB_USB); if (ret) { pr_err("failed to find firmware (%d)\n", ret); goto done; } cardp->fw = fw; if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { ret = -EINVAL; goto release_fw; } /* Cancel any pending usb business */ usb_kill_urb(cardp->rx_urb); usb_kill_urb(cardp->tx_urb); cardp->fwlastblksent = 0; cardp->fwdnldover = 0; cardp->totalbytes = 0; cardp->fwfinalblk = 0; cardp->bootcmdresp = 0; restart: if (if_usb_submit_rx_urb_fwload(cardp) < 0) { lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); ret = -EIO; goto release_fw; } cardp->bootcmdresp = 0; do { int j = 0; i++; if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); /* wait for command response */ do { j++; msleep_interruptible(100); } while (cardp->bootcmdresp == 0 && j < 10); } while (cardp->bootcmdresp == 0 && i < 5); if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { /* Return to normal operation */ ret = -EOPNOTSUPP; usb_kill_urb(cardp->rx_urb); usb_kill_urb(cardp->tx_urb); if (if_usb_submit_rx_urb(cardp) < 0) ret = -EIO; goto release_fw; } else if (cardp->bootcmdresp <= 0) { if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } ret = -EIO; goto release_fw; } i = 0; cardp->totalbytes = 0; cardp->fwlastblksent = 0; cardp->CRC_OK = 1; cardp->fwdnldover = 0; cardp->fwseqnum = -1; cardp->totalbytes = 0; cardp->fwfinalblk = 0; /* Send the first firmware packet... */ if_usb_send_fw_pkt(cardp); /* ... and wait for the process to complete */ wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); del_timer_sync(&cardp->fw_timeout); usb_kill_urb(cardp->rx_urb); if (!cardp->fwdnldover) { pr_info("failed to load fw, resetting device!\n"); if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } pr_info("FW download failure, time = %d ms\n", i * 100); ret = -EIO; goto release_fw; } cardp->priv->fw_ready = 1; if_usb_submit_rx_urb(cardp); if (lbs_start_card(priv)) goto release_fw; if_usb_setup_firmware(priv); /* * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. */ priv->wol_criteria = EHS_REMOVE_WAKEUP; if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) priv->ehs_remove_supported = false; release_fw: release_firmware(cardp->fw); cardp->fw = NULL; done: lbs_deb_leave(LBS_DEB_USB); } #ifdef CONFIG_PM static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct if_usb_card *cardp = usb_get_intfdata(intf); struct lbs_private *priv = cardp->priv; int ret; lbs_deb_enter(LBS_DEB_USB); if (priv->psstate != PS_STATE_FULL_POWER) { ret = -1; goto out; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) #ifdef CONFIG_OLPC if (machine_is_olpc()) { if (priv->wol_criteria == EHS_REMOVE_WAKEUP) olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); else olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); } #endif #endif ret = lbs_suspend(priv); if (ret) goto out; /* Unlink tx & rx urb */ usb_kill_urb(cardp->tx_urb); usb_kill_urb(cardp->rx_urb); out: lbs_deb_leave(LBS_DEB_USB); return ret; } static int if_usb_resume(struct usb_interface *intf) { struct if_usb_card *cardp = usb_get_intfdata(intf); struct lbs_private *priv = cardp->priv; lbs_deb_enter(LBS_DEB_USB); if_usb_submit_rx_urb(cardp); lbs_resume(priv); lbs_deb_leave(LBS_DEB_USB); return 0; } #else #define if_usb_suspend NULL #define if_usb_resume NULL #endif static struct usb_driver if_usb_driver = { .name = DRV_NAME, .probe = if_usb_probe, .disconnect = if_usb_disconnect, .id_table = if_usb_table, .suspend = if_usb_suspend, .resume = if_usb_resume, .reset_resume = if_usb_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(if_usb_driver); MODULE_DESCRIPTION("8388 USB WLAN Driver"); MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/libertas/mesh.c0000644000175000017500000007413312026211315023376 0ustar mcgrofmcgrof#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include "mesh.h" #include "decl.h" #include "cmd.h" static int lbs_add_mesh(struct lbs_private *priv); /*************************************************************************** * Mesh command handling */ static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, struct cmd_ds_mesh_access *cmd) { int ret; lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); cmd->hdr.result = 0; cmd->action = cpu_to_le16(cmd_action); ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); lbs_deb_leave(LBS_DEB_CMD); return ret; } static int __lbs_mesh_config_send(struct lbs_private *priv, struct cmd_ds_mesh_config *cmd, uint16_t action, uint16_t type) { int ret; u16 command = CMD_MESH_CONFIG_OLD; lbs_deb_enter(LBS_DEB_CMD); /* * Command id is 0xac for v10 FW along with mesh interface * id in bits 14-13-12. */ if (priv->mesh_tlv == TLV_TYPE_MESH_ID) command = CMD_MESH_CONFIG | (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); cmd->hdr.command = cpu_to_le16(command); cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); cmd->hdr.result = 0; cmd->type = cpu_to_le16(type); cmd->action = cpu_to_le16(action); ret = lbs_cmd_with_response(priv, command, cmd); lbs_deb_leave(LBS_DEB_CMD); return ret; } static int lbs_mesh_config_send(struct lbs_private *priv, struct cmd_ds_mesh_config *cmd, uint16_t action, uint16_t type) { int ret; if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) return -EOPNOTSUPP; ret = __lbs_mesh_config_send(priv, cmd, action, type); return ret; } /* This function is the CMD_MESH_CONFIG legacy function. It only handles the * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG * are all handled by preparing a struct cmd_ds_mesh_config and passing it to * lbs_mesh_config_send. */ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan) { struct cmd_ds_mesh_config cmd; struct mrvl_meshie *ie; DECLARE_SSID_BUF(ssid); memset(&cmd, 0, sizeof(cmd)); cmd.channel = cpu_to_le16(chan); ie = (struct mrvl_meshie *)cmd.data; switch (action) { case CMD_ACT_MESH_CONFIG_START: ie->id = WLAN_EID_GENERIC; ie->val.oui[0] = 0x00; ie->val.oui[1] = 0x50; ie->val.oui[2] = 0x43; ie->val.type = MARVELL_MESH_IE_TYPE; ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; ie->val.version = MARVELL_MESH_IE_VERSION; ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; ie->val.mesh_id_len = priv->mesh_ssid_len; memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); break; case CMD_ACT_MESH_CONFIG_STOP: break; default: return -1; } lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n", action, priv->mesh_tlv, chan, print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len)); return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); } int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) { priv->mesh_channel = channel; return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); } static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) { return priv->mesh_channel ?: 1; } /*************************************************************************** * Mesh sysfs support */ /* * Attributes exported through sysfs */ /** * lbs_anycast_get - Get function for sysfs attribute anycast_mask * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t lbs_anycast_get(struct device *dev, struct device_attribute *attr, char * buf) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_access mesh_access; int ret; memset(&mesh_access, 0, sizeof(mesh_access)); ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); if (ret) return ret; return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); } /** * lbs_anycast_set - Set function for sysfs attribute anycast_mask * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t lbs_anycast_set(struct device *dev, struct device_attribute *attr, const char * buf, size_t count) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_access mesh_access; uint32_t datum; int ret; memset(&mesh_access, 0, sizeof(mesh_access)); sscanf(buf, "%x", &datum); mesh_access.data[0] = cpu_to_le32(datum); ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); if (ret) return ret; return strlen(buf); } /** * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t lbs_prb_rsp_limit_get(struct device *dev, struct device_attribute *attr, char *buf) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_access mesh_access; int ret; u32 retry_limit; memset(&mesh_access, 0, sizeof(mesh_access)); mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET); ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, &mesh_access); if (ret) return ret; retry_limit = le32_to_cpu(mesh_access.data[1]); return snprintf(buf, 10, "%d\n", retry_limit); } /** * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t lbs_prb_rsp_limit_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_access mesh_access; int ret; unsigned long retry_limit; memset(&mesh_access, 0, sizeof(mesh_access)); mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); if (!strict_strtoul(buf, 10, &retry_limit)) return -ENOTSUPP; if (retry_limit > 15) return -ENOTSUPP; mesh_access.data[1] = cpu_to_le32(retry_limit); ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, &mesh_access); if (ret) return ret; return strlen(buf); } /** * lbs_mesh_get - Get function for sysfs attribute mesh * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t lbs_mesh_get(struct device *dev, struct device_attribute *attr, char * buf) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); } /** * lbs_mesh_set - Set function for sysfs attribute mesh * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t lbs_mesh_set(struct device *dev, struct device_attribute *attr, const char * buf, size_t count) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; int enable; sscanf(buf, "%x", &enable); enable = !!enable; if (enable == !!priv->mesh_dev) return count; if (enable) lbs_add_mesh(priv); else lbs_remove_mesh(priv); return count; } /* * lbs_mesh attribute to be exported per ethX interface * through sysfs (/sys/class/net/ethX/lbs_mesh) */ static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); /* * anycast_mask attribute to be exported per mshX interface * through sysfs (/sys/class/net/mshX/anycast_mask) */ static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); /* * prb_rsp_limit attribute to be exported per mshX interface * through sysfs (/sys/class/net/mshX/prb_rsp_limit) */ static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get, lbs_prb_rsp_limit_set); static struct attribute *lbs_mesh_sysfs_entries[] = { &dev_attr_anycast_mask.attr, &dev_attr_prb_rsp_limit.attr, NULL, }; static const struct attribute_group lbs_mesh_attr_group = { .attrs = lbs_mesh_sysfs_entries, }; /*************************************************************************** * Persistent configuration support */ static int mesh_get_default_parameters(struct device *dev, struct mrvl_mesh_defaults *defs) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_config cmd; int ret; memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, CMD_TYPE_MESH_GET_DEFAULTS); if (ret) return -EOPNOTSUPP; memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); return 0; } /** * bootflag_get - Get function for sysfs attribute bootflag * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t bootflag_get(struct device *dev, struct device_attribute *attr, char *buf) { struct mrvl_mesh_defaults defs; int ret; ret = mesh_get_default_parameters(dev, &defs); if (ret) return ret; return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); } /** * bootflag_set - Set function for sysfs attribute bootflag * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_config cmd; uint32_t datum; int ret; memset(&cmd, 0, sizeof(cmd)); ret = sscanf(buf, "%d", &datum); if ((ret != 1) || (datum > 1)) return -EINVAL; *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); cmd.length = cpu_to_le16(sizeof(uint32_t)); ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, CMD_TYPE_MESH_SET_BOOTFLAG); if (ret) return ret; return strlen(buf); } /** * boottime_get - Get function for sysfs attribute boottime * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t boottime_get(struct device *dev, struct device_attribute *attr, char *buf) { struct mrvl_mesh_defaults defs; int ret; ret = mesh_get_default_parameters(dev, &defs); if (ret) return ret; return snprintf(buf, 12, "%d\n", defs.boottime); } /** * boottime_set - Set function for sysfs attribute boottime * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t boottime_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_config cmd; uint32_t datum; int ret; memset(&cmd, 0, sizeof(cmd)); ret = sscanf(buf, "%d", &datum); if ((ret != 1) || (datum > 255)) return -EINVAL; /* A too small boot time will result in the device booting into * standalone (no-host) mode before the host can take control of it, * so the change will be hard to revert. This may be a desired * feature (e.g to configure a very fast boot time for devices that * will not be attached to a host), but dangerous. So I'm enforcing a * lower limit of 20 seconds: remove and recompile the driver if this * does not work for you. */ datum = (datum < 20) ? 20 : datum; cmd.data[0] = datum; cmd.length = cpu_to_le16(sizeof(uint8_t)); ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, CMD_TYPE_MESH_SET_BOOTTIME); if (ret) return ret; return strlen(buf); } /** * channel_get - Get function for sysfs attribute channel * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t channel_get(struct device *dev, struct device_attribute *attr, char *buf) { struct mrvl_mesh_defaults defs; int ret; ret = mesh_get_default_parameters(dev, &defs); if (ret) return ret; return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); } /** * channel_set - Set function for sysfs attribute channel * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t channel_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lbs_private *priv = to_net_dev(dev)->ml_priv; struct cmd_ds_mesh_config cmd; uint32_t datum; int ret; memset(&cmd, 0, sizeof(cmd)); ret = sscanf(buf, "%d", &datum); if (ret != 1 || datum < 1 || datum > 11) return -EINVAL; *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); cmd.length = cpu_to_le16(sizeof(uint16_t)); ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, CMD_TYPE_MESH_SET_DEF_CHANNEL); if (ret) return ret; return strlen(buf); } /** * mesh_id_get - Get function for sysfs attribute mesh_id * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, char *buf) { struct mrvl_mesh_defaults defs; int ret; ret = mesh_get_default_parameters(dev, &defs); if (ret) return ret; if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { dev_err(dev, "inconsistent mesh ID length\n"); defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; } memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len); buf[defs.meshie.val.mesh_id_len] = '\n'; buf[defs.meshie.val.mesh_id_len + 1] = '\0'; return defs.meshie.val.mesh_id_len + 1; } /** * mesh_id_set - Set function for sysfs attribute mesh_id * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cmd_ds_mesh_config cmd; struct mrvl_mesh_defaults defs; struct mrvl_meshie *ie; struct lbs_private *priv = to_net_dev(dev)->ml_priv; int len; int ret; if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) return -EINVAL; memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); ie = (struct mrvl_meshie *) &cmd.data[0]; /* fetch all other Information Element parameters */ ret = mesh_get_default_parameters(dev, &defs); cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); /* transfer IE elements */ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); len = count - 1; memcpy(ie->val.mesh_id, buf, len); /* SSID len */ ie->val.mesh_id_len = len; /* IE len */ ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, CMD_TYPE_MESH_SET_MESH_IE); if (ret) return ret; return strlen(buf); } /** * protocol_id_get - Get function for sysfs attribute protocol_id * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t protocol_id_get(struct device *dev, struct device_attribute *attr, char *buf) { struct mrvl_mesh_defaults defs; int ret; ret = mesh_get_default_parameters(dev, &defs); if (ret) return ret; return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); } /** * protocol_id_set - Set function for sysfs attribute protocol_id * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t protocol_id_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cmd_ds_mesh_config cmd; struct mrvl_mesh_defaults defs; struct mrvl_meshie *ie; struct lbs_private *priv = to_net_dev(dev)->ml_priv; uint32_t datum; int ret; memset(&cmd, 0, sizeof(cmd)); ret = sscanf(buf, "%d", &datum); if ((ret != 1) || (datum > 255)) return -EINVAL; /* fetch all other Information Element parameters */ ret = mesh_get_default_parameters(dev, &defs); cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); /* transfer IE elements */ ie = (struct mrvl_meshie *) &cmd.data[0]; memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); /* update protocol id */ ie->val.active_protocol_id = datum; ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, CMD_TYPE_MESH_SET_MESH_IE); if (ret) return ret; return strlen(buf); } /** * metric_id_get - Get function for sysfs attribute metric_id * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t metric_id_get(struct device *dev, struct device_attribute *attr, char *buf) { struct mrvl_mesh_defaults defs; int ret; ret = mesh_get_default_parameters(dev, &defs); if (ret) return ret; return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); } /** * metric_id_set - Set function for sysfs attribute metric_id * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cmd_ds_mesh_config cmd; struct mrvl_mesh_defaults defs; struct mrvl_meshie *ie; struct lbs_private *priv = to_net_dev(dev)->ml_priv; uint32_t datum; int ret; memset(&cmd, 0, sizeof(cmd)); ret = sscanf(buf, "%d", &datum); if ((ret != 1) || (datum > 255)) return -EINVAL; /* fetch all other Information Element parameters */ ret = mesh_get_default_parameters(dev, &defs); cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); /* transfer IE elements */ ie = (struct mrvl_meshie *) &cmd.data[0]; memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); /* update metric id */ ie->val.active_metric_id = datum; ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, CMD_TYPE_MESH_SET_MESH_IE); if (ret) return ret; return strlen(buf); } /** * capability_get - Get function for sysfs attribute capability * @dev: the &struct device * @attr: device attributes * @buf: buffer where data will be returned */ static ssize_t capability_get(struct device *dev, struct device_attribute *attr, char *buf) { struct mrvl_mesh_defaults defs; int ret; ret = mesh_get_default_parameters(dev, &defs); if (ret) return ret; return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); } /** * capability_set - Set function for sysfs attribute capability * @dev: the &struct device * @attr: device attributes * @buf: buffer that contains new attribute value * @count: size of buffer */ static ssize_t capability_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cmd_ds_mesh_config cmd; struct mrvl_mesh_defaults defs; struct mrvl_meshie *ie; struct lbs_private *priv = to_net_dev(dev)->ml_priv; uint32_t datum; int ret; memset(&cmd, 0, sizeof(cmd)); ret = sscanf(buf, "%d", &datum); if ((ret != 1) || (datum > 255)) return -EINVAL; /* fetch all other Information Element parameters */ ret = mesh_get_default_parameters(dev, &defs); cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); /* transfer IE elements */ ie = (struct mrvl_meshie *) &cmd.data[0]; memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); /* update value */ ie->val.mesh_capability = datum; ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, CMD_TYPE_MESH_SET_MESH_IE); if (ret) return ret; return strlen(buf); } static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); static DEVICE_ATTR(channel, 0644, channel_get, channel_set); static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); static DEVICE_ATTR(capability, 0644, capability_get, capability_set); static struct attribute *boot_opts_attrs[] = { &dev_attr_bootflag.attr, &dev_attr_boottime.attr, &dev_attr_channel.attr, NULL }; static const struct attribute_group boot_opts_group = { .name = "boot_options", .attrs = boot_opts_attrs, }; static struct attribute *mesh_ie_attrs[] = { &dev_attr_mesh_id.attr, &dev_attr_protocol_id.attr, &dev_attr_metric_id.attr, &dev_attr_capability.attr, NULL }; static const struct attribute_group mesh_ie_group = { .name = "mesh_ie", .attrs = mesh_ie_attrs, }; static void lbs_persist_config_init(struct net_device *dev) { int ret; ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); } static void lbs_persist_config_remove(struct net_device *dev) { sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); } /*************************************************************************** * Initializing and starting, stopping mesh */ /* * Check mesh FW version and appropriately send the mesh start * command */ int lbs_init_mesh(struct lbs_private *priv) { int ret = 0; lbs_deb_enter(LBS_DEB_MESH); /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ /* 5.110.22 have mesh command with 0xa3 command id */ /* 10.0.0.p0 FW brings in mesh config command with different id */ /* Check FW version MSB and initialize mesh_fw_ver */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { /* Enable mesh, if supported, and work out which TLV it uses. 0x100 + 291 is an unofficial value used in 5.110.20.pXX 0x100 + 37 is the official value used in 5.110.21.pXX but we check them in that order because 20.pXX doesn't give an error -- it just silently fails. */ /* 5.110.20.pXX firmware will fail the command if the channel doesn't match the existing channel. But only if the TLV is correct. If the channel is wrong, _BOTH_ versions will give an error to 0x100+291, and allow 0x100+37 to succeed. It's just that 5.110.20.pXX will not have done anything useful */ priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) { priv->mesh_tlv = TLV_TYPE_MESH_ID; if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) priv->mesh_tlv = 0; } } else if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { /* 10.0.0.pXX new firmwares should succeed with TLV * 0x100+37; Do not invoke command with old TLV. */ priv->mesh_tlv = TLV_TYPE_MESH_ID; if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) priv->mesh_tlv = 0; } /* Stop meshing until interface is brought up */ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1); if (priv->mesh_tlv) { sprintf(priv->mesh_ssid, "mesh"); priv->mesh_ssid_len = 4; ret = 1; } lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); return ret; } void lbs_start_mesh(struct lbs_private *priv) { lbs_add_mesh(priv); if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh)) netdev_err(priv->dev, "cannot register lbs_mesh attribute\n"); } int lbs_deinit_mesh(struct lbs_private *priv) { struct net_device *dev = priv->dev; int ret = 0; lbs_deb_enter(LBS_DEB_MESH); if (priv->mesh_tlv) { device_remove_file(&dev->dev, &dev_attr_lbs_mesh); ret = 1; } lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); return ret; } /** * lbs_mesh_stop - close the mshX interface * * @dev: A pointer to &net_device structure * returns: 0 */ static int lbs_mesh_stop(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; lbs_deb_enter(LBS_DEB_MESH); lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, lbs_mesh_get_channel(priv)); spin_lock_irq(&priv->driver_lock); netif_stop_queue(dev); netif_carrier_off(dev); spin_unlock_irq(&priv->driver_lock); lbs_update_mcast(priv); if (!lbs_iface_active(priv)) lbs_stop_iface(priv); lbs_deb_leave(LBS_DEB_MESH); return 0; } /** * lbs_mesh_dev_open - open the mshX interface * * @dev: A pointer to &net_device structure * returns: 0 or -EBUSY if monitor mode active */ static int lbs_mesh_dev_open(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; int ret = 0; lbs_deb_enter(LBS_DEB_NET); if (!priv->iface_running) { ret = lbs_start_iface(priv); if (ret) goto out; } spin_lock_irq(&priv->driver_lock); if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { ret = -EBUSY; spin_unlock_irq(&priv->driver_lock); goto out; } netif_carrier_on(dev); if (!priv->tx_pending_len) netif_wake_queue(dev); spin_unlock_irq(&priv->driver_lock); ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, lbs_mesh_get_channel(priv)); out: lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); return ret; } static const struct net_device_ops mesh_netdev_ops = { .ndo_open = lbs_mesh_dev_open, .ndo_stop = lbs_mesh_stop, .ndo_start_xmit = lbs_hard_start_xmit, .ndo_set_mac_address = lbs_set_mac_address, .ndo_set_rx_mode = lbs_set_multicast_list, }; /** * lbs_add_mesh - add mshX interface * * @priv: A pointer to the &struct lbs_private structure * returns: 0 if successful, -X otherwise */ static int lbs_add_mesh(struct lbs_private *priv) { struct net_device *mesh_dev = NULL; struct wireless_dev *mesh_wdev; int ret = 0; lbs_deb_enter(LBS_DEB_MESH); /* Allocate a virtual mesh device */ mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); if (!mesh_wdev) { lbs_deb_mesh("init mshX wireless device failed\n"); ret = -ENOMEM; goto done; } mesh_dev = alloc_netdev(0, "msh%d", ether_setup); if (!mesh_dev) { lbs_deb_mesh("init mshX device failed\n"); ret = -ENOMEM; goto err_free_wdev; } mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT; mesh_wdev->wiphy = priv->wdev->wiphy; mesh_wdev->netdev = mesh_dev; mesh_dev->ml_priv = priv; mesh_dev->ieee80211_ptr = mesh_wdev; priv->mesh_dev = mesh_dev; netdev_attach_ops(mesh_dev, &mesh_netdev_ops); mesh_dev->ethtool_ops = &lbs_ethtool_ops; memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN); SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; /* Register virtual mesh interface */ ret = register_netdev(mesh_dev); if (ret) { pr_err("cannot register mshX virtual interface\n"); goto err_free_netdev; } ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); if (ret) goto err_unregister; lbs_persist_config_init(mesh_dev); /* Everything successful */ ret = 0; goto done; err_unregister: unregister_netdev(mesh_dev); err_free_netdev: free_netdev(mesh_dev); err_free_wdev: kfree(mesh_wdev); done: lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); return ret; } void lbs_remove_mesh(struct lbs_private *priv) { struct net_device *mesh_dev; mesh_dev = priv->mesh_dev; if (!mesh_dev) return; lbs_deb_enter(LBS_DEB_MESH); netif_stop_queue(mesh_dev); netif_carrier_off(mesh_dev); sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); lbs_persist_config_remove(mesh_dev); unregister_netdev(mesh_dev); priv->mesh_dev = NULL; kfree(mesh_dev->ieee80211_ptr); free_netdev(mesh_dev); lbs_deb_leave(LBS_DEB_MESH); } /*************************************************************************** * Sending and receiving */ struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, struct net_device *dev, struct rxpd *rxpd) { if (priv->mesh_dev) { if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { if (rxpd->rx_control & RxPD_MESH_FRAME) dev = priv->mesh_dev; } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { if (rxpd->u.bss.bss_num == MESH_IFACE_ID) dev = priv->mesh_dev; } } return dev; } void lbs_mesh_set_txpd(struct lbs_private *priv, struct net_device *dev, struct txpd *txpd) { if (dev == priv->mesh_dev) { if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) txpd->u.bss.bss_num = MESH_IFACE_ID; } } /*************************************************************************** * Ethtool related */ static const char * const mesh_stat_strings[] = { "drop_duplicate_bcast", "drop_ttl_zero", "drop_no_fwd_route", "drop_no_buffers", "fwded_unicast_cnt", "fwded_bcast_cnt", "drop_blind_table", "tx_failed_cnt" }; void lbs_mesh_ethtool_get_stats(struct net_device *dev, struct ethtool_stats *stats, uint64_t *data) { struct lbs_private *priv = dev->ml_priv; struct cmd_ds_mesh_access mesh_access; int ret; lbs_deb_enter(LBS_DEB_ETHTOOL); /* Get Mesh Statistics */ ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access); if (ret) { memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t))); return; } priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]); priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]); priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]); priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]); priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]); priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]); priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]); priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]); data[0] = priv->mstats.fwd_drop_rbt; data[1] = priv->mstats.fwd_drop_ttl; data[2] = priv->mstats.fwd_drop_noroute; data[3] = priv->mstats.fwd_drop_nobuf; data[4] = priv->mstats.fwd_unicast_cnt; data[5] = priv->mstats.fwd_bcast_cnt; data[6] = priv->mstats.drop_blind; data[7] = priv->mstats.tx_failed_cnt; lbs_deb_enter(LBS_DEB_ETHTOOL); } int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset) { struct lbs_private *priv = dev->ml_priv; if (sset == ETH_SS_STATS && dev == priv->mesh_dev) return MESH_STATS_NUM; return -EOPNOTSUPP; } void lbs_mesh_ethtool_get_strings(struct net_device *dev, uint32_t stringset, uint8_t *s) { int i; lbs_deb_enter(LBS_DEB_ETHTOOL); switch (stringset) { case ETH_SS_STATS: for (i = 0; i < MESH_STATS_NUM; i++) { memcpy(s + i * ETH_GSTRING_LEN, mesh_stat_strings[i], ETH_GSTRING_LEN); } break; } lbs_deb_enter(LBS_DEB_ETHTOOL); } compat-drivers-2012-09-18/drivers/net/wireless/libertas/if_cs.c0000644000175000017500000007052312026211315023524 0ustar mcgrofmcgrof/* Driver for the Marvell 8385 based compact flash WLAN cards. (C) 2007 by Holger Schurig This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #define DRV_NAME "libertas_cs" #include "decl.h" #include "defs.h" #include "dev.h" /********************************************************************/ /* Module stuff */ /********************************************************************/ MODULE_AUTHOR("Holger Schurig "); MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); MODULE_LICENSE("GPL"); /********************************************************************/ /* Data structures */ /********************************************************************/ struct if_cs_card { struct pcmcia_device *p_dev; struct lbs_private *priv; void __iomem *iobase; bool align_regs; u32 model; }; enum { MODEL_UNKNOWN = 0x00, MODEL_8305 = 0x01, MODEL_8381 = 0x02, MODEL_8385 = 0x03 }; static const struct lbs_fw_table fw_table[] = { { MODEL_8305, "libertas/cf8305.bin", NULL }, { MODEL_8305, "libertas_cs_helper.fw", NULL }, { MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" }, { MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" }, { MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" }, { MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" }, { 0, NULL, NULL } }; MODULE_FIRMWARE("libertas/cf8305.bin"); MODULE_FIRMWARE("libertas/cf8381_helper.bin"); MODULE_FIRMWARE("libertas/cf8381.bin"); MODULE_FIRMWARE("libertas/cf8385_helper.bin"); MODULE_FIRMWARE("libertas/cf8385.bin"); MODULE_FIRMWARE("libertas_cs_helper.fw"); MODULE_FIRMWARE("libertas_cs.fw"); /********************************************************************/ /* Hardware access */ /********************************************************************/ /* This define enables wrapper functions which allow you to dump all register accesses. You normally won't this, except for development */ /* #define DEBUG_IO */ #ifdef DEBUG_IO static int debug_output = 0; #else /* This way the compiler optimizes the printk's away */ #define debug_output 0 #endif static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) { unsigned int val = ioread8(card->iobase + reg); if (debug_output) printk(KERN_INFO "inb %08x<%02x\n", reg, val); return val; } static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) { unsigned int val = ioread16(card->iobase + reg); if (debug_output) printk(KERN_INFO "inw %08x<%04x\n", reg, val); return val; } static inline void if_cs_read16_rep( struct if_cs_card *card, uint reg, void *buf, unsigned long count) { if (debug_output) printk(KERN_INFO "insw %08x<(0x%lx words)\n", reg, count); ioread16_rep(card->iobase + reg, buf, count); } static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) { if (debug_output) printk(KERN_INFO "outb %08x>%02x\n", reg, val); iowrite8(val, card->iobase + reg); } static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) { if (debug_output) printk(KERN_INFO "outw %08x>%04x\n", reg, val); iowrite16(val, card->iobase + reg); } static inline void if_cs_write16_rep( struct if_cs_card *card, uint reg, const void *buf, unsigned long count) { if (debug_output) printk(KERN_INFO "outsw %08x>(0x%lx words)\n", reg, count); iowrite16_rep(card->iobase + reg, buf, count); } /* * I know that polling/delaying is frowned upon. However, this procedure * with polling is needed while downloading the firmware. At this stage, * the hardware does unfortunately not create any interrupts. * * Fortunately, this function is never used once the firmware is in * the card. :-) * * As a reference, see the "Firmware Specification v5.1", page 18 * and 19. I did not follow their suggested timing to the word, * but this works nice & fast anyway. */ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg) { int i; for (i = 0; i < 100000; i++) { u8 val = if_cs_read8(card, addr); if (val == reg) return 0; udelay(5); } return -ETIME; } /* * First the bitmasks for the host/card interrupt/status registers: */ #define IF_CS_BIT_TX 0x0001 #define IF_CS_BIT_RX 0x0002 #define IF_CS_BIT_COMMAND 0x0004 #define IF_CS_BIT_RESP 0x0008 #define IF_CS_BIT_EVENT 0x0010 #define IF_CS_BIT_MASK 0x001f /* * It's not really clear to me what the host status register is for. It * needs to be set almost in union with "host int cause". The following * bits from above are used: * * IF_CS_BIT_TX driver downloaded a data packet * IF_CS_BIT_RX driver got a data packet * IF_CS_BIT_COMMAND driver downloaded a command * IF_CS_BIT_RESP not used (has some meaning with powerdown) * IF_CS_BIT_EVENT driver read a host event */ #define IF_CS_HOST_STATUS 0x00000000 /* * With the host int cause register can the host (that is, Linux) cause * an interrupt in the firmware, to tell the firmware about those events: * * IF_CS_BIT_TX a data packet has been downloaded * IF_CS_BIT_RX a received data packet has retrieved * IF_CS_BIT_COMMAND a firmware block or a command has been downloaded * IF_CS_BIT_RESP not used (has some meaning with powerdown) * IF_CS_BIT_EVENT a host event (link lost etc) has been retrieved */ #define IF_CS_HOST_INT_CAUSE 0x00000002 /* * The host int mask register is used to enable/disable interrupt. However, * I have the suspicion that disabled interrupts are lost. */ #define IF_CS_HOST_INT_MASK 0x00000004 /* * Used to send or receive data packets: */ #define IF_CS_WRITE 0x00000016 #define IF_CS_WRITE_LEN 0x00000014 #define IF_CS_READ 0x00000010 #define IF_CS_READ_LEN 0x00000024 /* * Used to send commands (and to send firmware block) and to * receive command responses: */ #define IF_CS_CMD 0x0000001A #define IF_CS_CMD_LEN 0x00000018 #define IF_CS_RESP 0x00000012 #define IF_CS_RESP_LEN 0x00000030 /* * The card status registers shows what the card/firmware actually * accepts: * * IF_CS_BIT_TX you may send a data packet * IF_CS_BIT_RX you may retrieve a data packet * IF_CS_BIT_COMMAND you may send a command * IF_CS_BIT_RESP you may retrieve a command response * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) * * When reading this register several times, you will get back the same * results --- with one exception: the IF_CS_BIT_EVENT clear itself * automatically. * * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because * we handle this via the card int cause register. */ #define IF_CS_CARD_STATUS 0x00000020 #define IF_CS_CARD_STATUS_MASK 0x7f00 /* * The card int cause register is used by the card/firmware to notify us * about the following events: * * IF_CS_BIT_TX a data packet has successfully been sentx * IF_CS_BIT_RX a data packet has been received and can be retrieved * IF_CS_BIT_COMMAND not used * IF_CS_BIT_RESP the firmware has a command response for us * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) */ #define IF_CS_CARD_INT_CAUSE 0x00000022 /* * This is used to for handshaking with the card's bootloader/helper image * to synchronize downloading of firmware blocks. */ #define IF_CS_SQ_READ_LOW 0x00000028 #define IF_CS_SQ_HELPER_OK 0x10 /* * The scratch register tells us ... * * IF_CS_SCRATCH_BOOT_OK the bootloader runs * IF_CS_SCRATCH_HELPER_OK the helper firmware already runs */ #define IF_CS_SCRATCH 0x0000003F #define IF_CS_SCRATCH_BOOT_OK 0x00 #define IF_CS_SCRATCH_HELPER_OK 0x5a /* * Used to detect ancient chips: */ #define IF_CS_PRODUCT_ID 0x0000001C #define IF_CS_CF8385_B1_REV 0x12 #define IF_CS_CF8381_B3_REV 0x04 #define IF_CS_CF8305_B1_REV 0x03 /* * Used to detect other cards than CF8385 since their revisions of silicon * doesn't match those from CF8385, eg. CF8381 B3 works with this driver. */ #define CF8305_MANFID 0x02db #define CF8305_CARDID 0x8103 #define CF8381_MANFID 0x02db #define CF8381_CARDID 0x6064 #define CF8385_MANFID 0x02df #define CF8385_CARDID 0x8103 /* * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when * that gets fixed. Currently there's no way to access it from the probe hook. */ static inline u32 get_model(u16 manf_id, u16 card_id) { /* NOTE: keep in sync with if_cs_ids */ if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID) return MODEL_8305; else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID) return MODEL_8381; else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID) return MODEL_8385; return MODEL_UNKNOWN; } /********************************************************************/ /* I/O and interrupt handling */ /********************************************************************/ static inline void if_cs_enable_ints(struct if_cs_card *card) { lbs_deb_enter(LBS_DEB_CS); if_cs_write16(card, IF_CS_HOST_INT_MASK, 0); } static inline void if_cs_disable_ints(struct if_cs_card *card) { lbs_deb_enter(LBS_DEB_CS); if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK); } /* * Called from if_cs_host_to_card to send a command to the hardware */ static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) { struct if_cs_card *card = (struct if_cs_card *)priv->card; int ret = -1; int loops = 0; lbs_deb_enter(LBS_DEB_CS); if_cs_disable_ints(card); /* Is hardware ready? */ while (1) { u16 status = if_cs_read16(card, IF_CS_CARD_STATUS); if (status & IF_CS_BIT_COMMAND) break; if (++loops > 100) { netdev_err(priv->dev, "card not ready for commands\n"); goto done; } mdelay(1); } if_cs_write16(card, IF_CS_CMD_LEN, nb); if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2); /* Are we supposed to transfer an odd amount of bytes? */ if (nb & 1) if_cs_write8(card, IF_CS_CMD, buf[nb-1]); /* "Assert the download over interrupt command in the Host * status register" */ if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); /* "Assert the download over interrupt command in the Card * interrupt case register" */ if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); ret = 0; done: if_cs_enable_ints(card); lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } /* * Called from if_cs_host_to_card to send a data to the hardware */ static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) { struct if_cs_card *card = (struct if_cs_card *)priv->card; u16 status; lbs_deb_enter(LBS_DEB_CS); if_cs_disable_ints(card); status = if_cs_read16(card, IF_CS_CARD_STATUS); BUG_ON((status & IF_CS_BIT_TX) == 0); if_cs_write16(card, IF_CS_WRITE_LEN, nb); /* write even number of bytes, then odd byte if necessary */ if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2); if (nb & 1) if_cs_write8(card, IF_CS_WRITE, buf[nb-1]); if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX); if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX); if_cs_enable_ints(card); lbs_deb_leave(LBS_DEB_CS); } /* * Get the command result out of the card. */ static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) { unsigned long flags; int ret = -1; u16 status; lbs_deb_enter(LBS_DEB_CS); /* is hardware ready? */ status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); if ((status & IF_CS_BIT_RESP) == 0) { netdev_err(priv->dev, "no cmd response in card\n"); *len = 0; goto out; } *len = if_cs_read16(priv->card, IF_CS_RESP_LEN); if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) { netdev_err(priv->dev, "card cmd buffer has invalid # of bytes (%d)\n", *len); goto out; } /* read even number of bytes, then odd byte if necessary */ if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16)); if (*len & 1) data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP); /* This is a workaround for a firmware that reports too much * bytes */ *len -= 8; ret = 0; /* Clear this flag again */ spin_lock_irqsave(&priv->driver_lock, flags); priv->dnld_sent = DNLD_RES_RECEIVED; spin_unlock_irqrestore(&priv->driver_lock, flags); out: lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); return ret; } static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) { struct sk_buff *skb = NULL; u16 len; u8 *data; lbs_deb_enter(LBS_DEB_CS); len = if_cs_read16(priv->card, IF_CS_READ_LEN); if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { netdev_err(priv->dev, "card data buffer has invalid # of bytes (%d)\n", len); priv->dev->stats.rx_dropped++; goto dat_err; } skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); if (!skb) goto out; skb_put(skb, len); skb_reserve(skb, 2);/* 16 byte align */ data = skb->data; /* read even number of bytes, then odd byte if necessary */ if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16)); if (len & 1) data[len-1] = if_cs_read8(priv->card, IF_CS_READ); dat_err: if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX); if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX); out: lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb); return skb; } static irqreturn_t if_cs_interrupt(int irq, void *data) { struct if_cs_card *card = data; struct lbs_private *priv = card->priv; u16 cause; lbs_deb_enter(LBS_DEB_CS); /* Ask card interrupt cause register if there is something for us */ cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE); lbs_deb_cs("cause 0x%04x\n", cause); if (cause == 0) { /* Not for us */ return IRQ_NONE; } if (cause == 0xffff) { /* Read in junk, the card has probably been removed */ card->priv->surpriseremoved = 1; return IRQ_HANDLED; } if (cause & IF_CS_BIT_RX) { struct sk_buff *skb; lbs_deb_cs("rx packet\n"); skb = if_cs_receive_data(priv); if (skb) lbs_process_rxed_packet(priv, skb); } if (cause & IF_CS_BIT_TX) { lbs_deb_cs("tx done\n"); lbs_host_to_card_done(priv); } if (cause & IF_CS_BIT_RESP) { unsigned long flags; u8 i; lbs_deb_cs("cmd resp\n"); spin_lock_irqsave(&priv->driver_lock, flags); i = (priv->resp_idx == 0) ? 1 : 0; spin_unlock_irqrestore(&priv->driver_lock, flags); BUG_ON(priv->resp_len[i]); if_cs_receive_cmdres(priv, priv->resp_buf[i], &priv->resp_len[i]); spin_lock_irqsave(&priv->driver_lock, flags); lbs_notify_command_response(priv, i); spin_unlock_irqrestore(&priv->driver_lock, flags); } if (cause & IF_CS_BIT_EVENT) { u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_EVENT); lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8); } /* Clear interrupt cause */ if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK); lbs_deb_leave(LBS_DEB_CS); return IRQ_HANDLED; } /********************************************************************/ /* Firmware */ /********************************************************************/ /* * Tries to program the helper firmware. * * Return 0 on success */ static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw) { int ret = 0; int sent = 0; u8 scratch; lbs_deb_enter(LBS_DEB_CS); /* * This is the only place where an unaligned register access happens on * the CF8305 card, therefore for the sake of speed of the driver, we do * the alignment correction here. */ if (card->align_regs) scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8; else scratch = if_cs_read8(card, IF_CS_SCRATCH); /* "If the value is 0x5a, the firmware is already * downloaded successfully" */ if (scratch == IF_CS_SCRATCH_HELPER_OK) goto done; /* "If the value is != 00, it is invalid value of register */ if (scratch != IF_CS_SCRATCH_BOOT_OK) { ret = -ENODEV; goto done; } lbs_deb_cs("helper size %td\n", fw->size); /* "Set the 5 bytes of the helper image to 0" */ /* Not needed, this contains an ARM branch instruction */ for (;;) { /* "the number of bytes to send is 256" */ int count = 256; int remain = fw->size - sent; if (remain < count) count = remain; /* * "write the number of bytes to be sent to the I/O Command * write length register" */ if_cs_write16(card, IF_CS_CMD_LEN, count); /* "write this to I/O Command port register as 16 bit writes */ if (count) if_cs_write16_rep(card, IF_CS_CMD, &fw->data[sent], count >> 1); /* * "Assert the download over interrupt command in the Host * status register" */ if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); /* * "Assert the download over interrupt command in the Card * interrupt case register" */ if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); /* * "The host polls the Card Status register ... for 50 ms before * declaring a failure" */ ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, IF_CS_BIT_COMMAND); if (ret < 0) { pr_err("can't download helper at 0x%x, ret %d\n", sent, ret); goto done; } if (count == 0) break; sent += count; } done: lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw) { int ret = 0; int retry = 0; int len = 0; int sent; lbs_deb_enter(LBS_DEB_CS); lbs_deb_cs("fw size %td\n", fw->size); ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW, IF_CS_SQ_HELPER_OK); if (ret < 0) { pr_err("helper firmware doesn't answer\n"); goto done; } for (sent = 0; sent < fw->size; sent += len) { len = if_cs_read16(card, IF_CS_SQ_READ_LOW); if (len & 1) { retry++; pr_info("odd, need to retry this firmware block\n"); } else { retry = 0; } if (retry > 20) { pr_err("could not download firmware\n"); ret = -ENODEV; goto done; } if (retry) { sent -= len; } if_cs_write16(card, IF_CS_CMD_LEN, len); if_cs_write16_rep(card, IF_CS_CMD, &fw->data[sent], (len+1) >> 1); if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, IF_CS_BIT_COMMAND); if (ret < 0) { pr_err("can't download firmware at 0x%x\n", sent); goto done; } } ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); if (ret < 0) pr_err("firmware download failed\n"); done: lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static void if_cs_prog_firmware(struct lbs_private *priv, int ret, const struct firmware *helper, const struct firmware *mainfw) { struct if_cs_card *card = priv->card; if (ret) { pr_err("failed to find firmware (%d)\n", ret); return; } /* Load the firmware */ ret = if_cs_prog_helper(card, helper); if (ret == 0 && (card->model != MODEL_8305)) ret = if_cs_prog_real(card, mainfw); if (ret) goto out; /* Now actually get the IRQ */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = request_irq(card->p_dev->irq, if_cs_interrupt, #else ret = request_irq(card->p_dev->irq.AssignedIRQ, if_cs_interrupt, #endif IRQF_SHARED, DRV_NAME, card); if (ret) { pr_err("error in request_irq\n"); goto out; } /* * Clear any interrupt cause that happened while sending * firmware/initializing card */ if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); if_cs_enable_ints(card); /* And finally bring the card up */ priv->fw_ready = 1; if (lbs_start_card(priv) != 0) { pr_err("could not activate card\n"); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) free_irq(card->p_dev->irq, card); #else free_irq(card->p_dev->irq.AssignedIRQ, card); #endif } out: release_firmware(helper); release_firmware(mainfw); } /********************************************************************/ /* Callback functions for libertas.ko */ /********************************************************************/ /* Send commands or data packets to the card */ static int if_cs_host_to_card(struct lbs_private *priv, u8 type, u8 *buf, u16 nb) { int ret = -1; lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb); switch (type) { case MVMS_DAT: priv->dnld_sent = DNLD_DATA_SENT; if_cs_send_data(priv, buf, nb); ret = 0; break; case MVMS_CMD: priv->dnld_sent = DNLD_CMD_SENT; ret = if_cs_send_cmd(priv, buf, nb); break; default: netdev_err(priv->dev, "%s: unsupported type %d\n", __func__, type); } lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static void if_cs_release(struct pcmcia_device *p_dev) { struct if_cs_card *card = p_dev->priv; lbs_deb_enter(LBS_DEB_CS); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) free_irq(p_dev->irq, card); #else free_irq(p_dev->irq.AssignedIRQ, card); #endif pcmcia_disable_device(p_dev); if (card->iobase) ioport_unmap(card->iobase); lbs_deb_leave(LBS_DEB_CS); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) { p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; if (p_dev->resource[1]->end) { pr_err("wrong CIS (check number of IO windows)\n"); return -ENODEV; } #else static int if_cs_ioprobe(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cfg, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; p_dev->resource[0]->start = cfg->io.win[0].base; p_dev->resource[0]->end = cfg->io.win[0].len; #else p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; p_dev->io.BasePort1 = cfg->io.win[0].base; p_dev->io.NumPorts1 = cfg->io.win[0].len; #endif /* Do we need to allocate an interrupt? */ p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ if (cfg->io.nwin != 1) { pr_err("wrong CIS (check number of IO windows)\n"); return -ENODEV; } #endif /* This reserves IO space but doesn't actually enable it */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) return pcmcia_request_io(p_dev); #else return pcmcia_request_io(p_dev, &p_dev->io); #endif } static int if_cs_probe(struct pcmcia_device *p_dev) { int ret = -ENOMEM; unsigned int prod_id; struct lbs_private *priv; struct if_cs_card *card; lbs_deb_enter(LBS_DEB_CS); card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); if (!card) goto out; card->p_dev = p_dev; p_dev->priv = card; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; #else #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; p_dev->irq.Handler = NULL; #endif p_dev->conf.Attributes = 0; p_dev->conf.IntType = INT_MEMORY_AND_IO; #endif if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { pr_err("error in pcmcia_loop_config\n"); goto out1; } /* * Allocate an interrupt line. Note that this does not assign * a handler to the interrupt, unless the 'Handler' member of * the irq structure is initialized. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (!p_dev->irq) goto out1; #else if (p_dev->conf.Attributes & CONF_ENABLE_IRQ) { ret = pcmcia_request_irq(p_dev, &p_dev->irq); if (ret) { pr_err("error in pcmcia_request_irq\n"); goto out1; } } #endif /* Initialize io access */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) card->iobase = ioport_map(p_dev->resource[0]->start, resource_size(p_dev->resource[0])); #else card->iobase = ioport_map(p_dev->io.BasePort1, p_dev->io.NumPorts1); #endif if (!card->iobase) { pr_err("error in ioport_map\n"); ret = -EIO; goto out1; } ret = pcmcia_enable_device(p_dev); if (ret) { pr_err("error in pcmcia_enable_device\n"); goto out2; } /* Finally, report what we've done */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]); #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n", p_dev->irq, p_dev->io.BasePort1, p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1); #else lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n", p_dev->irq.AssignedIRQ, p_dev->io.BasePort1, p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1); #endif /* * Most of the libertas cards can do unaligned register access, but some * weird ones cannot. That's especially true for the CF8305 card. */ card->align_regs = false; card->model = get_model(p_dev->manf_id, p_dev->card_id); if (card->model == MODEL_UNKNOWN) { pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", p_dev->manf_id, p_dev->card_id); goto out2; } /* Check if we have a current silicon */ prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); if (card->model == MODEL_8305) { card->align_regs = true; if (prod_id < IF_CS_CF8305_B1_REV) { pr_err("8305 rev B0 and older are not supported\n"); ret = -ENODEV; goto out2; } } if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) { pr_err("8381 rev B2 and older are not supported\n"); ret = -ENODEV; goto out2; } if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) { pr_err("8385 rev B0 and older are not supported\n"); ret = -ENODEV; goto out2; } /* Make this card known to the libertas driver */ priv = lbs_add_card(card, &p_dev->dev); if (!priv) { ret = -ENOMEM; goto out2; } /* Set up fields in lbs_private */ card->priv = priv; priv->card = card; priv->hw_host_to_card = if_cs_host_to_card; priv->enter_deep_sleep = NULL; priv->exit_deep_sleep = NULL; priv->reset_deep_sleep_wakeup = NULL; /* Get firmware */ ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table, if_cs_prog_firmware); if (ret) { pr_err("failed to find firmware (%d)\n", ret); goto out3; } goto out; out3: lbs_remove_card(priv); out2: ioport_unmap(card->iobase); out1: pcmcia_disable_device(p_dev); out: lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static void if_cs_detach(struct pcmcia_device *p_dev) { struct if_cs_card *card = p_dev->priv; lbs_deb_enter(LBS_DEB_CS); lbs_stop_card(card->priv); lbs_remove_card(card->priv); if_cs_disable_ints(card); if_cs_release(p_dev); kfree(card); lbs_deb_leave(LBS_DEB_CS); } /********************************************************************/ /* Module initialization */ /********************************************************************/ static const struct pcmcia_device_id if_cs_ids[] = { PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), /* NOTE: keep in sync with get_model() */ PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); static struct pcmcia_driver lbs_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = DRV_NAME, #else .drv = { .name = DRV_NAME, }, #endif .probe = if_cs_probe, .remove = if_cs_detach, .id_table = if_cs_ids, }; static int __init if_cs_init(void) { int ret; lbs_deb_enter(LBS_DEB_CS); ret = pcmcia_register_driver(&lbs_driver); lbs_deb_leave(LBS_DEB_CS); return ret; } static void __exit if_cs_exit(void) { lbs_deb_enter(LBS_DEB_CS); pcmcia_unregister_driver(&lbs_driver); lbs_deb_leave(LBS_DEB_CS); } module_init(if_cs_init); module_exit(if_cs_exit); compat-drivers-2012-09-18/drivers/net/wireless/libertas/rx.c0000644000175000017500000001664712026211315023101 0ustar mcgrofmcgrof/* * This file contains the handling of RX in wlan driver. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include "defs.h" #include "host.h" #include "radiotap.h" #include "decl.h" #include "dev.h" #include "mesh.h" struct eth803hdr { u8 dest_addr[6]; u8 src_addr[6]; u16 h803_len; } __packed; struct rfc1042hdr { u8 llc_dsap; u8 llc_ssap; u8 llc_ctrl; u8 snap_oui[3]; u16 snap_type; } __packed; struct rxpackethdr { struct eth803hdr eth803_hdr; struct rfc1042hdr rfc1042_hdr; } __packed; struct rx80211packethdr { struct rxpd rx_pd; void *eth80211_hdr; } __packed; static int process_rxed_802_11_packet(struct lbs_private *priv, struct sk_buff *skb); /** * lbs_process_rxed_packet - processes received packet and forwards it * to kernel/upper layer * * @priv: A pointer to &struct lbs_private * @skb: A pointer to skb which includes the received packet * returns: 0 or -1 */ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) { int ret = 0; struct net_device *dev = priv->dev; struct rxpackethdr *p_rx_pkt; struct rxpd *p_rx_pd; int hdrchop; struct ethhdr *p_ethhdr; static const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; lbs_deb_enter(LBS_DEB_RX); BUG_ON(!skb); skb->ip_summed = CHECKSUM_NONE; if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) return process_rxed_802_11_packet(priv, skb); p_rx_pd = (struct rxpd *) skb->data; p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + le32_to_cpu(p_rx_pd->pkt_ptr)); dev = lbs_mesh_set_dev(priv, dev, p_rx_pd); lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min_t(unsigned int, skb->len, 100)); if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { lbs_deb_rx("rx err: frame received with bad length\n"); dev->stats.rx_length_errors++; ret = 0; dev_kfree_skb(skb); goto done; } lbs_deb_rx("rx data: skb->len - pkt_ptr = %d-%zd = %zd\n", skb->len, (size_t)le32_to_cpu(p_rx_pd->pkt_ptr), skb->len - (size_t)le32_to_cpu(p_rx_pd->pkt_ptr)); lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, sizeof(p_rx_pkt->eth803_hdr.dest_addr)); lbs_deb_hex(LBS_DEB_RX, "RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, sizeof(p_rx_pkt->eth803_hdr.src_addr)); if (memcmp(&p_rx_pkt->rfc1042_hdr, rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { /* * Replace the 803 header and rfc1042 header (llc/snap) with an * EthernetII header, keep the src/dst and snap_type (ethertype) * * The firmware only passes up SNAP frames converting * all RX Data from 802.11 to 802.2/LLC/SNAP frames. * * To create the Ethernet II, just move the src, dst address right * before the snap_type. */ p_ethhdr = (struct ethhdr *) ((u8 *) &p_rx_pkt->eth803_hdr + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) - sizeof(p_rx_pkt->eth803_hdr.dest_addr) - sizeof(p_rx_pkt->eth803_hdr.src_addr) - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, sizeof(p_ethhdr->h_source)); memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, sizeof(p_ethhdr->h_dest)); /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header * that was removed */ hdrchop = (u8 *)p_ethhdr - (u8 *)p_rx_pd; } else { lbs_deb_hex(LBS_DEB_RX, "RX Data: LLC/SNAP", (u8 *) &p_rx_pkt->rfc1042_hdr, sizeof(p_rx_pkt->rfc1042_hdr)); /* Chop off the rxpd */ hdrchop = (u8 *)&p_rx_pkt->eth803_hdr - (u8 *)p_rx_pd; } /* Chop off the leading header bytes so the skb points to the start of * either the reconstructed EthII frame or the 802.2/llc/snap frame */ skb_pull(skb, hdrchop); priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate); lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; skb->protocol = eth_type_trans(skb, dev); if (in_interrupt()) netif_rx(skb); else netif_rx_ni(skb); ret = 0; done: lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(lbs_process_rxed_packet); /** * convert_mv_rate_to_radiotap - converts Tx/Rx rates from Marvell WLAN format * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) * * @rate: Input rate * returns: Output Rate (0 if invalid) */ static u8 convert_mv_rate_to_radiotap(u8 rate) { switch (rate) { case 0: /* 1 Mbps */ return 2; case 1: /* 2 Mbps */ return 4; case 2: /* 5.5 Mbps */ return 11; case 3: /* 11 Mbps */ return 22; /* case 4: reserved */ case 5: /* 6 Mbps */ return 12; case 6: /* 9 Mbps */ return 18; case 7: /* 12 Mbps */ return 24; case 8: /* 18 Mbps */ return 36; case 9: /* 24 Mbps */ return 48; case 10: /* 36 Mbps */ return 72; case 11: /* 48 Mbps */ return 96; case 12: /* 54 Mbps */ return 108; } pr_alert("Invalid Marvell WLAN rate %i\n", rate); return 0; } /** * process_rxed_802_11_packet - processes a received 802.11 packet and forwards * it to kernel/upper layer * * @priv: A pointer to &struct lbs_private * @skb: A pointer to skb which includes the received packet * returns: 0 or -1 */ static int process_rxed_802_11_packet(struct lbs_private *priv, struct sk_buff *skb) { int ret = 0; struct net_device *dev = priv->dev; struct rx80211packethdr *p_rx_pkt; struct rxpd *prxpd; struct rx_radiotap_hdr radiotap_hdr; struct rx_radiotap_hdr *pradiotap_hdr; lbs_deb_enter(LBS_DEB_RX); p_rx_pkt = (struct rx80211packethdr *) skb->data; prxpd = &p_rx_pkt->rx_pd; /* lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); */ if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { lbs_deb_rx("rx err: frame received with bad length\n"); dev->stats.rx_length_errors++; ret = -EINVAL; kfree_skb(skb); goto done; } lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); /* create the exported radio header */ /* radiotap header */ memset(&radiotap_hdr, 0, sizeof(radiotap_hdr)); /* XXX must check radiotap_hdr.hdr.it_pad for pad */ radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr)); radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT); radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); /* XXX must check no carryout */ radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; /* chop the rxpd */ skb_pull(skb, sizeof(struct rxpd)); /* add space for the new radio header */ if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, GFP_ATOMIC)) { netdev_alert(dev, "%s: couldn't pskb_expand_head\n", __func__); ret = -ENOMEM; kfree_skb(skb); goto done; } pradiotap_hdr = (void *)skb_push(skb, sizeof(struct rx_radiotap_hdr)); memcpy(pradiotap_hdr, &radiotap_hdr, sizeof(struct rx_radiotap_hdr)); priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate); lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; skb->protocol = eth_type_trans(skb, priv->dev); if (in_interrupt()) netif_rx(skb); else netif_rx_ni(skb); ret = 0; done: lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/libertas/if_spi.c0000644000175000017500000007760612026211315023723 0ustar mcgrofmcgrof/* * linux/drivers/net/wireless/libertas/if_spi.c * * Driver for Marvell SPI WLAN cards. * * Copyright 2008 Analog Devices Inc. * * Authors: * Andrey Yurovsky * Colin McCabe * * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include "host.h" #include "decl.h" #include "defs.h" #include "dev.h" #include "if_spi.h" struct if_spi_packet { struct list_head list; u16 blen; u8 buffer[0] __attribute__((aligned(4))); }; struct if_spi_card { struct spi_device *spi; struct lbs_private *priv; struct libertas_spi_platform_data *pdata; /* The card ID and card revision, as reported by the hardware. */ u16 card_id; u8 card_rev; /* The last time that we initiated an SPU operation */ unsigned long prev_xfer_time; int use_dummy_writes; unsigned long spu_port_delay; unsigned long spu_reg_delay; /* Handles all SPI communication (except for FW load) */ struct workqueue_struct *workqueue; struct work_struct packet_work; struct work_struct resume_work; u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; /* A buffer of incoming packets from libertas core. * Since we can't sleep in hw_host_to_card, we have to buffer * them. */ struct list_head cmd_packet_list; struct list_head data_packet_list; /* Protects cmd_packet_list and data_packet_list */ spinlock_t buffer_lock; /* True is card suspended */ u8 suspended; }; static void free_if_spi_card(struct if_spi_card *card) { struct list_head *cursor, *next; struct if_spi_packet *packet; list_for_each_safe(cursor, next, &card->cmd_packet_list) { packet = container_of(cursor, struct if_spi_packet, list); list_del(&packet->list); kfree(packet); } list_for_each_safe(cursor, next, &card->data_packet_list) { packet = container_of(cursor, struct if_spi_packet, list); list_del(&packet->list); kfree(packet); } spi_set_drvdata(card->spi, NULL); kfree(card); } #define MODEL_8385 0x04 #define MODEL_8686 0x0b #define MODEL_8688 0x10 static const struct lbs_fw_table fw_table[] = { { MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" }, { MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" }, { MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" }, { MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" }, { MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" }, { 0, NULL, NULL } }; MODULE_FIRMWARE("libertas/gspi8385_helper.bin"); MODULE_FIRMWARE("libertas/gspi8385_hlp.bin"); MODULE_FIRMWARE("libertas/gspi8385.bin"); MODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin"); MODULE_FIRMWARE("libertas/gspi8686_v9.bin"); MODULE_FIRMWARE("libertas/gspi8686_hlp.bin"); MODULE_FIRMWARE("libertas/gspi8686.bin"); MODULE_FIRMWARE("libertas/gspi8688_helper.bin"); MODULE_FIRMWARE("libertas/gspi8688.bin"); /* * SPI Interface Unit Routines * * The SPU sits between the host and the WLAN module. * All communication with the firmware is through SPU transactions. * * First we have to put a SPU register name on the bus. Then we can * either read from or write to that register. * */ static void spu_transaction_init(struct if_spi_card *card) { if (!time_after(jiffies, card->prev_xfer_time + 1)) { /* Unfortunately, the SPU requires a delay between successive * transactions. If our last transaction was more than a jiffy * ago, we have obviously already delayed enough. * If not, we have to busy-wait to be on the safe side. */ ndelay(400); } } static void spu_transaction_finish(struct if_spi_card *card) { card->prev_xfer_time = jiffies; } /* * Write out a byte buffer to an SPI register, * using a series of 16-bit transfers. */ static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) { int err = 0; __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); struct spi_message m; struct spi_transfer reg_trans; struct spi_transfer data_trans; spi_message_init(&m); memset(®_trans, 0, sizeof(reg_trans)); memset(&data_trans, 0, sizeof(data_trans)); /* You must give an even number of bytes to the SPU, even if it * doesn't care about the last one. */ BUG_ON(len & 0x1); spu_transaction_init(card); /* write SPU register index */ reg_trans.tx_buf = ®_out; reg_trans.len = sizeof(reg_out); data_trans.tx_buf = buf; data_trans.len = len; spi_message_add_tail(®_trans, &m); spi_message_add_tail(&data_trans, &m); err = spi_sync(card->spi, &m); spu_transaction_finish(card); return err; } static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) { __le16 buff; buff = cpu_to_le16(val); return spu_write(card, reg, (u8 *)&buff, sizeof(u16)); } static inline int spu_reg_is_port_reg(u16 reg) { switch (reg) { case IF_SPI_IO_RDWRPORT_REG: case IF_SPI_CMD_RDWRPORT_REG: case IF_SPI_DATA_RDWRPORT_REG: return 1; default: return 0; } } static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) { unsigned int delay; int err = 0; __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); struct spi_message m; struct spi_transfer reg_trans; struct spi_transfer dummy_trans; struct spi_transfer data_trans; /* * You must take an even number of bytes from the SPU, even if you * don't care about the last one. */ BUG_ON(len & 0x1); spu_transaction_init(card); spi_message_init(&m); memset(®_trans, 0, sizeof(reg_trans)); memset(&dummy_trans, 0, sizeof(dummy_trans)); memset(&data_trans, 0, sizeof(data_trans)); /* write SPU register index */ reg_trans.tx_buf = ®_out; reg_trans.len = sizeof(reg_out); spi_message_add_tail(®_trans, &m); delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : card->spu_reg_delay; if (card->use_dummy_writes) { /* Clock in dummy cycles while the SPU fills the FIFO */ dummy_trans.len = delay / 8; spi_message_add_tail(&dummy_trans, &m); } else { /* Busy-wait while the SPU fills the FIFO */ reg_trans.delay_usecs = DIV_ROUND_UP((100 + (delay * 10)), 1000); } /* read in data */ data_trans.rx_buf = buf; data_trans.len = len; spi_message_add_tail(&data_trans, &m); err = spi_sync(card->spi, &m); spu_transaction_finish(card); return err; } /* Read 16 bits from an SPI register */ static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) { __le16 buf; int ret; ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); if (ret == 0) *val = le16_to_cpup(&buf); return ret; } /* * Read 32 bits from an SPI register. * The low 16 bits are read first. */ static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) { __le32 buf; int err; err = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); if (!err) *val = le32_to_cpup(&buf); return err; } /* * Keep reading 16 bits from an SPI register until you get the correct result. * * If mask = 0, the correct result is any non-zero number. * If mask != 0, the correct result is any number where * number & target_mask == target * * Returns -ETIMEDOUT if a second passes without the correct result. */ static int spu_wait_for_u16(struct if_spi_card *card, u16 reg, u16 target_mask, u16 target) { int err; unsigned long timeout = jiffies + 5*HZ; while (1) { u16 val; err = spu_read_u16(card, reg, &val); if (err) return err; if (target_mask) { if ((val & target_mask) == target) return 0; } else { if (val) return 0; } udelay(100); if (time_after(jiffies, timeout)) { pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n", __func__, val, target_mask, target); return -ETIMEDOUT; } } } /* * Read 16 bits from an SPI register until you receive a specific value. * Returns -ETIMEDOUT if a 4 tries pass without success. */ static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target) { int err, try; for (try = 0; try < 4; ++try) { u32 val = 0; err = spu_read_u32(card, reg, &val); if (err) return err; if (val == target) return 0; mdelay(100); } return -ETIMEDOUT; } static int spu_set_interrupt_mode(struct if_spi_card *card, int suppress_host_int, int auto_int) { int err = 0; /* * We can suppress a host interrupt by clearing the appropriate * bit in the "host interrupt status mask" register */ if (suppress_host_int) { err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); if (err) return err; } else { err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, IF_SPI_HISM_TX_DOWNLOAD_RDY | IF_SPI_HISM_RX_UPLOAD_RDY | IF_SPI_HISM_CMD_DOWNLOAD_RDY | IF_SPI_HISM_CARDEVENT | IF_SPI_HISM_CMD_UPLOAD_RDY); if (err) return err; } /* * If auto-interrupts are on, the completion of certain transactions * will trigger an interrupt automatically. If auto-interrupts * are off, we need to set the "Card Interrupt Cause" register to * trigger a card interrupt. */ if (auto_int) { err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); if (err) return err; } else { err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); if (err) return err; } return err; } static int spu_get_chip_revision(struct if_spi_card *card, u16 *card_id, u8 *card_rev) { int err = 0; u32 dev_ctrl; err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); if (err) return err; *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); return err; } static int spu_set_bus_mode(struct if_spi_card *card, u16 mode) { int err = 0; u16 rval; /* set bus mode */ err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); if (err) return err; /* Check that we were able to read back what we just wrote. */ err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); if (err) return err; if ((rval & 0xF) != mode) { pr_err("Can't read bus mode register\n"); return -EIO; } return 0; } static int spu_init(struct if_spi_card *card, int use_dummy_writes) { int err = 0; u32 delay; /* * We have to start up in timed delay mode so that we can safely * read the Delay Read Register. */ card->use_dummy_writes = 0; err = spu_set_bus_mode(card, IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); if (err) return err; card->spu_port_delay = 1000; card->spu_reg_delay = 1000; err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); if (err) return err; card->spu_port_delay = delay & 0x0000ffff; card->spu_reg_delay = (delay & 0xffff0000) >> 16; /* If dummy clock delay mode has been requested, switch to it now */ if (use_dummy_writes) { card->use_dummy_writes = 1; err = spu_set_bus_mode(card, IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK | IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); if (err) return err; } lbs_deb_spi("Initialized SPU unit. " "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n", card->spu_port_delay, card->spu_reg_delay); return err; } /* * Firmware Loading */ static int if_spi_prog_helper_firmware(struct if_spi_card *card, const struct firmware *firmware) { int err = 0; int bytes_remaining; const u8 *fw; u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; lbs_deb_enter(LBS_DEB_SPI); err = spu_set_interrupt_mode(card, 1, 0); if (err) goto out; bytes_remaining = firmware->size; fw = firmware->data; /* Load helper firmware image */ while (bytes_remaining > 0) { /* * Scratch pad 1 should contain the number of bytes we * want to download to the firmware */ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, HELPER_FW_LOAD_CHUNK_SZ); if (err) goto out; err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, IF_SPI_HIST_CMD_DOWNLOAD_RDY, IF_SPI_HIST_CMD_DOWNLOAD_RDY); if (err) goto out; /* * Feed the data into the command read/write port reg * in chunks of 64 bytes */ memset(temp, 0, sizeof(temp)); memcpy(temp, fw, min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); mdelay(10); err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, temp, HELPER_FW_LOAD_CHUNK_SZ); if (err) goto out; /* Interrupt the boot code */ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); if (err) goto out; err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_CMD_DOWNLOAD_OVER); if (err) goto out; bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; fw += HELPER_FW_LOAD_CHUNK_SZ; } /* * Once the helper / single stage firmware download is complete, * write 0 to scratch pad 1 and interrupt the * bootloader. This completes the helper download. */ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); if (err) goto out; err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); if (err) goto out; err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_CMD_DOWNLOAD_OVER); out: if (err) pr_err("failed to load helper firmware (err=%d)\n", err); lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); return err; } /* * Returns the length of the next packet the firmware expects us to send. * Sets crc_err if the previous transfer had a CRC error. */ static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, int *crc_err) { u16 len; int err = 0; /* * wait until the host interrupt status register indicates * that we are ready to download */ err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, IF_SPI_HIST_CMD_DOWNLOAD_RDY, IF_SPI_HIST_CMD_DOWNLOAD_RDY); if (err) { pr_err("timed out waiting for host_int_status\n"); return err; } /* Ask the device how many bytes of firmware it wants. */ err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); if (err) return err; if (len > IF_SPI_CMD_BUF_SIZE) { pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n", len); return -EIO; } if (len & 0x1) { lbs_deb_spi("%s: crc error\n", __func__); len &= ~0x1; *crc_err = 1; } else *crc_err = 0; return len; } static int if_spi_prog_main_firmware(struct if_spi_card *card, const struct firmware *firmware) { struct lbs_private *priv = card->priv; int len, prev_len; int bytes, crc_err = 0, err = 0; const u8 *fw; u16 num_crc_errs; lbs_deb_enter(LBS_DEB_SPI); err = spu_set_interrupt_mode(card, 1, 0); if (err) goto out; err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); if (err) { netdev_err(priv->dev, "%s: timed out waiting for initial scratch reg = 0\n", __func__); goto out; } num_crc_errs = 0; prev_len = 0; bytes = firmware->size; fw = firmware->data; while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { if (len < 0) { err = len; goto out; } if (bytes < 0) { /* * If there are no more bytes left, we would normally * expect to have terminated with len = 0 */ netdev_err(priv->dev, "Firmware load wants more bytes than we have to offer.\n"); break; } if (crc_err) { /* Previous transfer failed. */ if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { pr_err("Too many CRC errors encountered in firmware load.\n"); err = -EIO; goto out; } } else { /* Previous transfer succeeded. Advance counters. */ bytes -= prev_len; fw += prev_len; } if (bytes < len) { memset(card->cmd_buffer, 0, len); memcpy(card->cmd_buffer, fw, bytes); } else memcpy(card->cmd_buffer, fw, len); err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); if (err) goto out; err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, card->cmd_buffer, len); if (err) goto out; err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , IF_SPI_CIC_CMD_DOWNLOAD_OVER); if (err) goto out; prev_len = len; } if (bytes > prev_len) { pr_err("firmware load wants fewer bytes than we have to offer\n"); } /* Confirm firmware download */ err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, SUCCESSFUL_FW_DOWNLOAD_MAGIC); if (err) { pr_err("failed to confirm the firmware download\n"); goto out; } out: if (err) pr_err("failed to load firmware (err=%d)\n", err); lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); return err; } /* * SPI Transfer Thread * * The SPI worker handles all SPI transfers, so there is no need for a lock. */ /* Move a command from the card to the host */ static int if_spi_c2h_cmd(struct if_spi_card *card) { struct lbs_private *priv = card->priv; unsigned long flags; int err = 0; u16 len; u8 i; /* * We need a buffer big enough to handle whatever people send to * hw_host_to_card */ BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE); BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); /* * It's just annoying if the buffer size isn't a multiple of 4, because * then we might have len < IF_SPI_CMD_BUF_SIZE but * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE */ BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0); lbs_deb_enter(LBS_DEB_SPI); /* How many bytes are there to read? */ err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); if (err) goto out; if (!len) { netdev_err(priv->dev, "%s: error: card has no data for host\n", __func__); err = -EINVAL; goto out; } else if (len > IF_SPI_CMD_BUF_SIZE) { netdev_err(priv->dev, "%s: error: response packet too large: %d bytes, but maximum is %d\n", __func__, len, IF_SPI_CMD_BUF_SIZE); err = -EINVAL; goto out; } /* Read the data from the WLAN module into our command buffer */ err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG, card->cmd_buffer, ALIGN(len, 4)); if (err) goto out; spin_lock_irqsave(&priv->driver_lock, flags); i = (priv->resp_idx == 0) ? 1 : 0; BUG_ON(priv->resp_len[i]); priv->resp_len[i] = len; memcpy(priv->resp_buf[i], card->cmd_buffer, len); lbs_notify_command_response(priv, i); spin_unlock_irqrestore(&priv->driver_lock, flags); out: if (err) netdev_err(priv->dev, "%s: err=%d\n", __func__, err); lbs_deb_leave(LBS_DEB_SPI); return err; } /* Move data from the card to the host */ static int if_spi_c2h_data(struct if_spi_card *card) { struct lbs_private *priv = card->priv; struct sk_buff *skb; char *data; u16 len; int err = 0; lbs_deb_enter(LBS_DEB_SPI); /* How many bytes are there to read? */ err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); if (err) goto out; if (!len) { netdev_err(priv->dev, "%s: error: card has no data for host\n", __func__); err = -EINVAL; goto out; } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { netdev_err(priv->dev, "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n", __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); err = -EINVAL; goto out; } /* TODO: should we allocate a smaller skb if we have less data? */ skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); if (!skb) { err = -ENOBUFS; goto out; } skb_reserve(skb, IPFIELD_ALIGN_OFFSET); data = skb_put(skb, len); /* Read the data from the WLAN module into our skb... */ err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); if (err) goto free_skb; /* pass the SKB to libertas */ err = lbs_process_rxed_packet(card->priv, skb); if (err) goto free_skb; /* success */ goto out; free_skb: dev_kfree_skb(skb); out: if (err) netdev_err(priv->dev, "%s: err=%d\n", __func__, err); lbs_deb_leave(LBS_DEB_SPI); return err; } /* Move data or a command from the host to the card. */ static void if_spi_h2c(struct if_spi_card *card, struct if_spi_packet *packet, int type) { struct lbs_private *priv = card->priv; int err = 0; u16 int_type, port_reg; switch (type) { case MVMS_DAT: int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; port_reg = IF_SPI_DATA_RDWRPORT_REG; break; case MVMS_CMD: int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; port_reg = IF_SPI_CMD_RDWRPORT_REG; break; default: netdev_err(priv->dev, "can't transfer buffer of type %d\n", type); err = -EINVAL; goto out; } /* Write the data to the card */ err = spu_write(card, port_reg, packet->buffer, packet->blen); if (err) goto out; out: kfree(packet); if (err) netdev_err(priv->dev, "%s: error %d\n", __func__, err); } /* Inform the host about a card event */ static void if_spi_e2h(struct if_spi_card *card) { int err = 0; u32 cause; struct lbs_private *priv = card->priv; err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); if (err) goto out; /* re-enable the card event interrupt */ spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, ~IF_SPI_HICU_CARD_EVENT); /* generate a card interrupt */ spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT); lbs_queue_event(priv, cause & 0xff); out: if (err) netdev_err(priv->dev, "%s: error %d\n", __func__, err); } static void if_spi_host_to_card_worker(struct work_struct *work) { int err; struct if_spi_card *card; u16 hiStatus; unsigned long flags; struct if_spi_packet *packet; struct lbs_private *priv; card = container_of(work, struct if_spi_card, packet_work); priv = card->priv; lbs_deb_enter(LBS_DEB_SPI); /* * Read the host interrupt status register to see what we * can do. */ err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, &hiStatus); if (err) { netdev_err(priv->dev, "I/O error\n"); goto err; } if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { err = if_spi_c2h_cmd(card); if (err) goto err; } if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { err = if_spi_c2h_data(card); if (err) goto err; } /* * workaround: in PS mode, the card does not set the Command * Download Ready bit, but it sets TX Download Ready. */ if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || (card->priv->psstate != PS_STATE_FULL_POWER && (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { /* * This means two things. First of all, * if there was a previous command sent, the card has * successfully received it. * Secondly, it is now ready to download another * command. */ lbs_host_to_card_done(card->priv); /* Do we have any command packets from the host to send? */ packet = NULL; spin_lock_irqsave(&card->buffer_lock, flags); if (!list_empty(&card->cmd_packet_list)) { packet = (struct if_spi_packet *)(card-> cmd_packet_list.next); list_del(&packet->list); } spin_unlock_irqrestore(&card->buffer_lock, flags); if (packet) if_spi_h2c(card, packet, MVMS_CMD); } if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { /* Do we have any data packets from the host to send? */ packet = NULL; spin_lock_irqsave(&card->buffer_lock, flags); if (!list_empty(&card->data_packet_list)) { packet = (struct if_spi_packet *)(card-> data_packet_list.next); list_del(&packet->list); } spin_unlock_irqrestore(&card->buffer_lock, flags); if (packet) if_spi_h2c(card, packet, MVMS_DAT); } if (hiStatus & IF_SPI_HIST_CARD_EVENT) if_spi_e2h(card); err: if (err) netdev_err(priv->dev, "%s: got error %d\n", __func__, err); lbs_deb_leave(LBS_DEB_SPI); } /* * Host to Card * * Called from Libertas to transfer some data to the WLAN device * We can't sleep here. */ static int if_spi_host_to_card(struct lbs_private *priv, u8 type, u8 *buf, u16 nb) { int err = 0; unsigned long flags; struct if_spi_card *card = priv->card; struct if_spi_packet *packet; u16 blen; lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); if (nb == 0) { netdev_err(priv->dev, "%s: invalid size requested: %d\n", __func__, nb); err = -EINVAL; goto out; } blen = ALIGN(nb, 4); packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); if (!packet) { err = -ENOMEM; goto out; } packet->blen = blen; memcpy(packet->buffer, buf, nb); memset(packet->buffer + nb, 0, blen - nb); switch (type) { case MVMS_CMD: priv->dnld_sent = DNLD_CMD_SENT; spin_lock_irqsave(&card->buffer_lock, flags); list_add_tail(&packet->list, &card->cmd_packet_list); spin_unlock_irqrestore(&card->buffer_lock, flags); break; case MVMS_DAT: priv->dnld_sent = DNLD_DATA_SENT; spin_lock_irqsave(&card->buffer_lock, flags); list_add_tail(&packet->list, &card->data_packet_list); spin_unlock_irqrestore(&card->buffer_lock, flags); break; default: kfree(packet); netdev_err(priv->dev, "can't transfer buffer of type %d\n", type); err = -EINVAL; break; } /* Queue spi xfer work */ queue_work(card->workqueue, &card->packet_work); out: lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err); return err; } /* * Host Interrupts * * Service incoming interrupts from the WLAN device. We can't sleep here, so * don't try to talk on the SPI bus, just queue the SPI xfer work. */ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) { struct if_spi_card *card = dev_id; queue_work(card->workqueue, &card->packet_work); return IRQ_HANDLED; } /* * SPI callbacks */ static int if_spi_init_card(struct if_spi_card *card) { struct lbs_private *priv = card->priv; int err, i; u32 scratch; const struct firmware *helper = NULL; const struct firmware *mainfw = NULL; lbs_deb_enter(LBS_DEB_SPI); err = spu_init(card, card->pdata->use_dummy_writes); if (err) goto out; err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); if (err) goto out; err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); if (err) goto out; if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) lbs_deb_spi("Firmware is already loaded for " "Marvell WLAN 802.11 adapter\n"); else { /* Check if we support this card */ for (i = 0; i < ARRAY_SIZE(fw_table); i++) { if (card->card_id == fw_table[i].model) break; } if (i == ARRAY_SIZE(fw_table)) { netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n", card->card_id); err = -ENODEV; goto out; } err = lbs_get_firmware(&card->spi->dev, card->card_id, &fw_table[0], &helper, &mainfw); if (err) { netdev_err(priv->dev, "failed to find firmware (%d)\n", err); goto out; } lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " "(chip_id = 0x%04x, chip_rev = 0x%02x) " "attached to SPI bus_num %d, chip_select %d. " "spi->max_speed_hz=%d\n", card->card_id, card->card_rev, card->spi->master->bus_num, card->spi->chip_select, card->spi->max_speed_hz); err = if_spi_prog_helper_firmware(card, helper); if (err) goto out; err = if_spi_prog_main_firmware(card, mainfw); if (err) goto out; lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); } err = spu_set_interrupt_mode(card, 0, 1); if (err) goto out; out: release_firmware(helper); release_firmware(mainfw); lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); return err; } static void if_spi_resume_worker(struct work_struct *work) { struct if_spi_card *card; card = container_of(work, struct if_spi_card, resume_work); if (card->suspended) { if (card->pdata->setup) card->pdata->setup(card->spi); /* Init card ... */ if_spi_init_card(card); enable_irq(card->spi->irq); /* And resume it ... */ lbs_resume(card->priv); card->suspended = 0; } } static int __devinit if_spi_probe(struct spi_device *spi) { struct if_spi_card *card; struct lbs_private *priv = NULL; struct libertas_spi_platform_data *pdata = spi->dev.platform_data; int err = 0; lbs_deb_enter(LBS_DEB_SPI); if (!pdata) { err = -EINVAL; goto out; } if (pdata->setup) { err = pdata->setup(spi); if (err) goto out; } /* Allocate card structure to represent this specific device */ card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); if (!card) { err = -ENOMEM; goto teardown; } spi_set_drvdata(spi, card); card->pdata = pdata; card->spi = spi; card->prev_xfer_time = jiffies; INIT_LIST_HEAD(&card->cmd_packet_list); INIT_LIST_HEAD(&card->data_packet_list); spin_lock_init(&card->buffer_lock); /* Initialize the SPI Interface Unit */ /* Firmware load */ err = if_spi_init_card(card); if (err) goto free_card; /* * Register our card with libertas. * This will call alloc_etherdev. */ priv = lbs_add_card(card, &spi->dev); if (!priv) { err = -ENOMEM; goto free_card; } card->priv = priv; priv->setup_fw_on_resume = 1; priv->card = card; priv->hw_host_to_card = if_spi_host_to_card; priv->enter_deep_sleep = NULL; priv->exit_deep_sleep = NULL; priv->reset_deep_sleep_wakeup = NULL; priv->fw_ready = 1; /* Initialize interrupt handling stuff. */ card->workqueue = create_workqueue("libertas_spi"); INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); INIT_WORK(&card->resume_work, if_spi_resume_worker); err = request_irq(spi->irq, if_spi_host_interrupt, IRQF_TRIGGER_FALLING, "libertas_spi", card); if (err) { pr_err("can't get host irq line-- request_irq failed\n"); goto terminate_workqueue; } /* * Start the card. * This will call register_netdev, and we'll start * getting interrupts... */ err = lbs_start_card(priv); if (err) goto release_irq; lbs_deb_spi("Finished initializing WLAN module.\n"); /* successful exit */ goto out; release_irq: free_irq(spi->irq, card); terminate_workqueue: flush_workqueue(card->workqueue); destroy_workqueue(card->workqueue); lbs_remove_card(priv); /* will call free_netdev */ free_card: free_if_spi_card(card); teardown: if (pdata->teardown) pdata->teardown(spi); out: lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); return err; } static int __devexit libertas_spi_remove(struct spi_device *spi) { struct if_spi_card *card = spi_get_drvdata(spi); struct lbs_private *priv = card->priv; lbs_deb_spi("libertas_spi_remove\n"); lbs_deb_enter(LBS_DEB_SPI); cancel_work_sync(&card->resume_work); lbs_stop_card(priv); lbs_remove_card(priv); /* will call free_netdev */ free_irq(spi->irq, card); flush_workqueue(card->workqueue); destroy_workqueue(card->workqueue); if (card->pdata->teardown) card->pdata->teardown(spi); free_if_spi_card(card); lbs_deb_leave(LBS_DEB_SPI); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) static int if_spi_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); struct if_spi_card *card = spi_get_drvdata(spi); if (!card->suspended) { lbs_suspend(card->priv); flush_workqueue(card->workqueue); disable_irq(spi->irq); if (card->pdata->teardown) card->pdata->teardown(spi); card->suspended = 1; } return 0; } static int if_spi_resume(struct device *dev) { struct spi_device *spi = to_spi_device(dev); struct if_spi_card *card = spi_get_drvdata(spi); /* Schedule delayed work */ schedule_work(&card->resume_work); return 0; } static const struct dev_pm_ops if_spi_pm_ops = { .suspend = if_spi_suspend, .resume = if_spi_resume, }; #endif static struct spi_driver libertas_spi_driver = { .probe = if_spi_probe, .remove = __devexit_p(libertas_spi_remove), .driver = { .name = "libertas_spi", .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .pm = &if_spi_pm_ops, #endif }, }; /* * Module functions */ static int __init if_spi_init_module(void) { int ret = 0; lbs_deb_enter(LBS_DEB_SPI); printk(KERN_INFO "libertas_spi: Libertas SPI driver\n"); ret = spi_register_driver(&libertas_spi_driver); lbs_deb_leave(LBS_DEB_SPI); return ret; } static void __exit if_spi_exit_module(void) { lbs_deb_enter(LBS_DEB_SPI); spi_unregister_driver(&libertas_spi_driver); lbs_deb_leave(LBS_DEB_SPI); } module_init(if_spi_init_module); module_exit(if_spi_exit_module); MODULE_DESCRIPTION("Libertas SPI WLAN Driver"); MODULE_AUTHOR("Andrey Yurovsky , " "Colin McCabe "); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:libertas_spi"); compat-drivers-2012-09-18/drivers/net/wireless/libertas/if_sdio.c0000644000175000017500000010001412026211315024042 0ustar mcgrofmcgrof/* * linux/drivers/net/wireless/libertas/if_sdio.c * * Copyright 2007-2008 Pierre Ossman * * Inspired by if_cs.c, Copyright 2007 Holger Schurig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This hardware has more or less no CMD53 support, so all registers * must be accessed using sdio_readb()/sdio_writeb(). * * Transfers must be in one transaction or the firmware goes bonkers. * This means that the transfer must either be small enough to do a * byte based transfer or it must be padded to a multiple of the * current block size. * * As SDIO is still new to the kernel, it is unfortunately common with * bugs in the host controllers related to that. One such bug is that * controllers cannot do transfers that aren't a multiple of 4 bytes. * If you don't have time to fix the host controller driver, you can * work around the problem by modifying if_sdio_host_to_card() and * if_sdio_card_to_host() to pad the data. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "host.h" #include "decl.h" #include "defs.h" #include "dev.h" #include "cmd.h" #include "if_sdio.h" static void if_sdio_interrupt(struct sdio_func *func); /* The if_sdio_remove() callback function is called when * user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases * differently for SD8688 combo chip. * If the user is removing the module, the FUNC_SHUTDOWN * command for SD8688 is sent to the firmware. * If the card is removed, there is no need to send this command. * * The variable 'user_rmmod' is used to distinguish these two * scenarios. This flag is initialized as FALSE in case the card * is removed, and will be set to TRUE for module removal when * module_exit function is called. */ static u8 user_rmmod; static const struct sdio_device_id if_sdio_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8688WLAN) }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, if_sdio_ids); #define MODEL_8385 0x04 #define MODEL_8686 0x0b #define MODEL_8688 0x10 static const struct lbs_fw_table fw_table[] = { { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" }, { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" }, { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" }, { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" }, { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" }, { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" }, { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" }, { 0, NULL, NULL } }; MODULE_FIRMWARE("libertas/sd8385_helper.bin"); MODULE_FIRMWARE("libertas/sd8385.bin"); MODULE_FIRMWARE("sd8385_helper.bin"); MODULE_FIRMWARE("sd8385.bin"); MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin"); MODULE_FIRMWARE("libertas/sd8686_v9.bin"); MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin"); MODULE_FIRMWARE("libertas/sd8686_v8.bin"); MODULE_FIRMWARE("sd8686_helper.bin"); MODULE_FIRMWARE("sd8686.bin"); MODULE_FIRMWARE("libertas/sd8688_helper.bin"); MODULE_FIRMWARE("libertas/sd8688.bin"); MODULE_FIRMWARE("sd8688_helper.bin"); MODULE_FIRMWARE("sd8688.bin"); struct if_sdio_packet { struct if_sdio_packet *next; u16 nb; u8 buffer[0] __attribute__((aligned(4))); }; struct if_sdio_card { struct sdio_func *func; struct lbs_private *priv; int model; unsigned long ioport; unsigned int scratch_reg; bool started; wait_queue_head_t pwron_waitq; u8 buffer[65536] __attribute__((aligned(4))); spinlock_t lock; struct if_sdio_packet *packets; struct workqueue_struct *workqueue; struct work_struct packet_worker; u8 rx_unit; }; static void if_sdio_finish_power_on(struct if_sdio_card *card); static int if_sdio_power_off(struct if_sdio_card *card); /********************************************************************/ /* I/O */ /********************************************************************/ /* * For SD8385/SD8686, this function reads firmware status after * the image is downloaded, or reads RX packet length when * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received. * For SD8688, this function reads firmware status only. */ static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) { int ret; u16 scratch; scratch = sdio_readb(card->func, card->scratch_reg, &ret); if (!ret) scratch |= sdio_readb(card->func, card->scratch_reg + 1, &ret) << 8; if (err) *err = ret; if (ret) return 0xffff; return scratch; } static u8 if_sdio_read_rx_unit(struct if_sdio_card *card) { int ret; u8 rx_unit; rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret); if (ret) rx_unit = 0; return rx_unit; } static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err) { int ret; u16 rx_len; switch (card->model) { case MODEL_8385: case MODEL_8686: rx_len = if_sdio_read_scratch(card, &ret); break; case MODEL_8688: default: /* for newer chipsets */ rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); if (!ret) rx_len <<= card->rx_unit; else rx_len = 0xffff; /* invalid length */ break; } if (err) *err = ret; return rx_len; } static int if_sdio_handle_cmd(struct if_sdio_card *card, u8 *buffer, unsigned size) { struct lbs_private *priv = card->priv; int ret; unsigned long flags; u8 i; lbs_deb_enter(LBS_DEB_SDIO); if (size > LBS_CMD_BUFFER_SIZE) { lbs_deb_sdio("response packet too large (%d bytes)\n", (int)size); ret = -E2BIG; goto out; } spin_lock_irqsave(&priv->driver_lock, flags); i = (priv->resp_idx == 0) ? 1 : 0; BUG_ON(priv->resp_len[i]); priv->resp_len[i] = size; memcpy(priv->resp_buf[i], buffer, size); lbs_notify_command_response(priv, i); spin_unlock_irqrestore(&card->priv->driver_lock, flags); ret = 0; out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static int if_sdio_handle_data(struct if_sdio_card *card, u8 *buffer, unsigned size) { int ret; struct sk_buff *skb; char *data; lbs_deb_enter(LBS_DEB_SDIO); if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { lbs_deb_sdio("response packet too large (%d bytes)\n", (int)size); ret = -E2BIG; goto out; } skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN); if (!skb) { ret = -ENOMEM; goto out; } skb_reserve(skb, NET_IP_ALIGN); data = skb_put(skb, size); memcpy(data, buffer, size); lbs_process_rxed_packet(card->priv, skb); ret = 0; out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static int if_sdio_handle_event(struct if_sdio_card *card, u8 *buffer, unsigned size) { int ret; u32 event; lbs_deb_enter(LBS_DEB_SDIO); if (card->model == MODEL_8385) { event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); if (ret) goto out; /* right shift 3 bits to get the event id */ event >>= 3; } else { if (size < 4) { lbs_deb_sdio("event packet too small (%d bytes)\n", (int)size); ret = -EINVAL; goto out; } event = buffer[3] << 24; event |= buffer[2] << 16; event |= buffer[1] << 8; event |= buffer[0] << 0; } lbs_queue_event(card->priv, event & 0xFF); ret = 0; out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition) { u8 status; unsigned long timeout; int ret = 0; timeout = jiffies + HZ; while (1) { status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); if (ret) return ret; if ((status & condition) == condition) break; if (time_after(jiffies, timeout)) return -ETIMEDOUT; mdelay(1); } return ret; } static int if_sdio_card_to_host(struct if_sdio_card *card) { int ret; u16 size, type, chunk; lbs_deb_enter(LBS_DEB_SDIO); size = if_sdio_read_rx_len(card, &ret); if (ret) goto out; if (size < 4) { lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n", (int)size); ret = -EINVAL; goto out; } ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); if (ret) goto out; /* * The transfer must be in one transaction or the firmware * goes suicidal. There's no way to guarantee that for all * controllers, but we can at least try. */ chunk = sdio_align_size(card->func, size); ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); if (ret) goto out; chunk = card->buffer[0] | (card->buffer[1] << 8); type = card->buffer[2] | (card->buffer[3] << 8); lbs_deb_sdio("packet of type %d and size %d bytes\n", (int)type, (int)chunk); if (chunk > size) { lbs_deb_sdio("packet fragment (%d > %d)\n", (int)chunk, (int)size); ret = -EINVAL; goto out; } if (chunk < size) { lbs_deb_sdio("packet fragment (%d < %d)\n", (int)chunk, (int)size); } switch (type) { case MVMS_CMD: ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4); if (ret) goto out; break; case MVMS_DAT: ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4); if (ret) goto out; break; case MVMS_EVENT: ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4); if (ret) goto out; break; default: lbs_deb_sdio("invalid type (%d) from firmware\n", (int)type); ret = -EINVAL; goto out; } out: if (ret) pr_err("problem fetching packet from firmware\n"); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static void if_sdio_host_to_card_worker(struct work_struct *work) { struct if_sdio_card *card; struct if_sdio_packet *packet; int ret; unsigned long flags; lbs_deb_enter(LBS_DEB_SDIO); card = container_of(work, struct if_sdio_card, packet_worker); while (1) { spin_lock_irqsave(&card->lock, flags); packet = card->packets; if (packet) card->packets = packet->next; spin_unlock_irqrestore(&card->lock, flags); if (!packet) break; sdio_claim_host(card->func); ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); if (ret == 0) { ret = sdio_writesb(card->func, card->ioport, packet->buffer, packet->nb); } if (ret) pr_err("error %d sending packet to firmware\n", ret); sdio_release_host(card->func); kfree(packet); } lbs_deb_leave(LBS_DEB_SDIO); } /********************************************************************/ /* Firmware */ /********************************************************************/ #define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY) static int if_sdio_prog_helper(struct if_sdio_card *card, const struct firmware *fw) { int ret; unsigned long timeout; u8 *chunk_buffer; u32 chunk_size; const u8 *firmware; size_t size; lbs_deb_enter(LBS_DEB_SDIO); chunk_buffer = kzalloc(64, GFP_KERNEL); if (!chunk_buffer) { ret = -ENOMEM; goto out; } sdio_claim_host(card->func); ret = sdio_set_block_size(card->func, 32); if (ret) goto release; firmware = fw->data; size = fw->size; while (size) { ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); if (ret) goto release; /* On some platforms (like Davinci) the chip needs more time * between helper blocks. */ mdelay(2); chunk_size = min(size, (size_t)60); *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); memcpy(chunk_buffer + 4, firmware, chunk_size); /* lbs_deb_sdio("sending %d bytes chunk\n", chunk_size); */ ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64); if (ret) goto release; firmware += chunk_size; size -= chunk_size; } /* an empty block marks the end of the transfer */ memset(chunk_buffer, 0, 4); ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64); if (ret) goto release; lbs_deb_sdio("waiting for helper to boot...\n"); /* wait for the helper to boot by looking at the size register */ timeout = jiffies + HZ; while (1) { u16 req_size; req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); if (ret) goto release; req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; if (ret) goto release; if (req_size != 0) break; if (time_after(jiffies, timeout)) { ret = -ETIMEDOUT; goto release; } msleep(10); } ret = 0; release: sdio_release_host(card->func); kfree(chunk_buffer); out: if (ret) pr_err("failed to load helper firmware\n"); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static int if_sdio_prog_real(struct if_sdio_card *card, const struct firmware *fw) { int ret; unsigned long timeout; u8 *chunk_buffer; u32 chunk_size; const u8 *firmware; size_t size, req_size; lbs_deb_enter(LBS_DEB_SDIO); chunk_buffer = kzalloc(512, GFP_KERNEL); if (!chunk_buffer) { ret = -ENOMEM; goto out; } sdio_claim_host(card->func); ret = sdio_set_block_size(card->func, 32); if (ret) goto release; firmware = fw->data; size = fw->size; while (size) { ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); if (ret) goto release; req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); if (ret) goto release; req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; if (ret) goto release; /* lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); */ if (req_size == 0) { lbs_deb_sdio("firmware helper gave up early\n"); ret = -EIO; goto release; } if (req_size & 0x01) { lbs_deb_sdio("firmware helper signalled error\n"); ret = -EIO; goto release; } if (req_size > size) req_size = size; while (req_size) { chunk_size = min(req_size, (size_t)512); memcpy(chunk_buffer, firmware, chunk_size); /* lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n", chunk_size, (chunk_size + 31) / 32 * 32); */ ret = sdio_writesb(card->func, card->ioport, chunk_buffer, roundup(chunk_size, 32)); if (ret) goto release; firmware += chunk_size; size -= chunk_size; req_size -= chunk_size; } } ret = 0; lbs_deb_sdio("waiting for firmware to boot...\n"); /* wait for the firmware to boot */ timeout = jiffies + HZ; while (1) { u16 scratch; scratch = if_sdio_read_scratch(card, &ret); if (ret) goto release; if (scratch == IF_SDIO_FIRMWARE_OK) break; if (time_after(jiffies, timeout)) { ret = -ETIMEDOUT; goto release; } msleep(10); } ret = 0; release: sdio_release_host(card->func); kfree(chunk_buffer); out: if (ret) pr_err("failed to load firmware\n"); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, const struct firmware *helper, const struct firmware *mainfw) { struct if_sdio_card *card = priv->card; if (ret) { pr_err("failed to find firmware (%d)\n", ret); return; } ret = if_sdio_prog_helper(card, helper); if (ret) goto out; lbs_deb_sdio("Helper firmware loaded\n"); ret = if_sdio_prog_real(card, mainfw); if (ret) goto out; lbs_deb_sdio("Firmware loaded\n"); if_sdio_finish_power_on(card); out: release_firmware(helper); release_firmware(mainfw); } static int if_sdio_prog_firmware(struct if_sdio_card *card) { int ret; u16 scratch; lbs_deb_enter(LBS_DEB_SDIO); /* * Disable interrupts */ sdio_claim_host(card->func); sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret); sdio_release_host(card->func); sdio_claim_host(card->func); scratch = if_sdio_read_scratch(card, &ret); sdio_release_host(card->func); lbs_deb_sdio("firmware status = %#x\n", scratch); lbs_deb_sdio("scratch ret = %d\n", ret); if (ret) goto out; /* * The manual clearly describes that FEDC is the right code to use * to detect firmware presence, but for SD8686 it is not that simple. * Scratch is also used to store the RX packet length, so we lose * the FEDC value early on. So we use a non-zero check in order * to validate firmware presence. * Additionally, the SD8686 in the Gumstix always has the high scratch * bit set, even when the firmware is not loaded. So we have to * exclude that from the test. */ if (scratch == IF_SDIO_FIRMWARE_OK) { lbs_deb_sdio("firmware already loaded\n"); if_sdio_finish_power_on(card); return 0; } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) { lbs_deb_sdio("firmware may be running\n"); if_sdio_finish_power_on(card); return 0; } ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model, fw_table, if_sdio_do_prog_firmware); out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } /********************************************************************/ /* Power management */ /********************************************************************/ /* Finish power on sequence (after firmware is loaded) */ static void if_sdio_finish_power_on(struct if_sdio_card *card) { struct sdio_func *func = card->func; struct lbs_private *priv = card->priv; int ret; sdio_claim_host(func); sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); /* * Get rx_unit if the chip is SD8688 or newer. * SD8385 & SD8686 do not have rx_unit. */ if ((card->model != MODEL_8385) && (card->model != MODEL_8686)) card->rx_unit = if_sdio_read_rx_unit(card); else card->rx_unit = 0; /* * Set up the interrupt handler late. * * If we set it up earlier, the (buggy) hardware generates a spurious * interrupt, even before the interrupt has been enabled, with * CCCR_INTx = 0. * * We register the interrupt handler late so that we can handle any * spurious interrupts, and also to avoid generation of that known * spurious interrupt in the first place. */ ret = sdio_claim_irq(func, if_sdio_interrupt); if (ret) goto release; /* * Enable interrupts now that everything is set up */ sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); if (ret) goto release_irq; sdio_release_host(func); /* * FUNC_INIT is required for SD8688 WLAN/BT multiple functions */ if (card->model == MODEL_8688) { struct cmd_header cmd; memset(&cmd, 0, sizeof(cmd)); lbs_deb_sdio("send function INIT command\n"); if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), lbs_cmd_copyback, (unsigned long) &cmd)) netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); } priv->fw_ready = 1; wake_up(&card->pwron_waitq); if (!card->started) { ret = lbs_start_card(priv); if_sdio_power_off(card); if (ret == 0) { card->started = true; /* Tell PM core that we don't need the card to be * powered now */ pm_runtime_put_noidle(&func->dev); } } return; release_irq: sdio_release_irq(func); release: sdio_release_host(func); } static int if_sdio_power_on(struct if_sdio_card *card) { struct sdio_func *func = card->func; struct mmc_host *host = func->card->host; int ret; sdio_claim_host(func); ret = sdio_enable_func(func); if (ret) goto release; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) /* For 1-bit transfers to the 8686 model, we need to enable the * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 * bit to allow access to non-vendor registers. */ if ((card->model == MODEL_8686) && (host->caps & MMC_CAP_SDIO_IRQ) && (host->ios.bus_width == MMC_BUS_WIDTH_1)) { u8 reg; func->card->quirks |= MMC_QUIRK_LENIENT_FN0; reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); if (ret) goto disable; reg |= SDIO_BUS_ECSI; sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); if (ret) goto disable; } #endif card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); if (ret) goto disable; card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; if (ret) goto disable; card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; if (ret) goto disable; sdio_release_host(func); ret = if_sdio_prog_firmware(card); if (ret) { sdio_disable_func(func); return ret; } return 0; disable: sdio_disable_func(func); release: sdio_release_host(func); return ret; } static int if_sdio_power_off(struct if_sdio_card *card) { struct sdio_func *func = card->func; struct lbs_private *priv = card->priv; priv->fw_ready = 0; sdio_claim_host(func); sdio_release_irq(func); sdio_disable_func(func); sdio_release_host(func); return 0; } /*******************************************************************/ /* Libertas callbacks */ /*******************************************************************/ static int if_sdio_host_to_card(struct lbs_private *priv, u8 type, u8 *buf, u16 nb) { int ret; struct if_sdio_card *card; struct if_sdio_packet *packet, *cur; u16 size; unsigned long flags; lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb); card = priv->card; if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) { ret = -EINVAL; goto out; } /* * The transfer must be in one transaction or the firmware * goes suicidal. There's no way to guarantee that for all * controllers, but we can at least try. */ size = sdio_align_size(card->func, nb + 4); packet = kzalloc(sizeof(struct if_sdio_packet) + size, GFP_ATOMIC); if (!packet) { ret = -ENOMEM; goto out; } packet->next = NULL; packet->nb = size; /* * SDIO specific header. */ packet->buffer[0] = (nb + 4) & 0xff; packet->buffer[1] = ((nb + 4) >> 8) & 0xff; packet->buffer[2] = type; packet->buffer[3] = 0; memcpy(packet->buffer + 4, buf, nb); spin_lock_irqsave(&card->lock, flags); if (!card->packets) card->packets = packet; else { cur = card->packets; while (cur->next) cur = cur->next; cur->next = packet; } switch (type) { case MVMS_CMD: priv->dnld_sent = DNLD_CMD_SENT; break; case MVMS_DAT: priv->dnld_sent = DNLD_DATA_SENT; break; default: lbs_deb_sdio("unknown packet type %d\n", (int)type); } spin_unlock_irqrestore(&card->lock, flags); queue_work(card->workqueue, &card->packet_worker); ret = 0; out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static int if_sdio_enter_deep_sleep(struct lbs_private *priv) { int ret = -1; struct cmd_header cmd; memset(&cmd, 0, sizeof(cmd)); lbs_deb_sdio("send DEEP_SLEEP command\n"); ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd), lbs_cmd_copyback, (unsigned long) &cmd); if (ret) netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n"); mdelay(200); return ret; } static int if_sdio_exit_deep_sleep(struct lbs_private *priv) { struct if_sdio_card *card = priv->card; int ret = -1; lbs_deb_enter(LBS_DEB_SDIO); sdio_claim_host(card->func); sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); if (ret) netdev_err(priv->dev, "sdio_writeb failed!\n"); sdio_release_host(card->func); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) { struct if_sdio_card *card = priv->card; int ret = -1; lbs_deb_enter(LBS_DEB_SDIO); sdio_claim_host(card->func); sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret); if (ret) netdev_err(priv->dev, "sdio_writeb failed!\n"); sdio_release_host(card->func); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static struct mmc_host *reset_host; static void if_sdio_reset_card_worker(struct work_struct *work) { /* * The actual reset operation must be run outside of lbs_thread. This * is because mmc_remove_host() will cause the device to be instantly * destroyed, and the libertas driver then needs to end lbs_thread, * leading to a deadlock. * * We run it in a workqueue totally independent from the if_sdio_card * instance for that reason. */ pr_info("Resetting card..."); mmc_remove_host(reset_host); mmc_add_host(reset_host); } static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); static void if_sdio_reset_card(struct lbs_private *priv) { struct if_sdio_card *card = priv->card; if (work_pending(&card_reset_work)) return; reset_host = card->func->card->host; schedule_work(&card_reset_work); } static int if_sdio_power_save(struct lbs_private *priv) { struct if_sdio_card *card = priv->card; int ret; flush_workqueue(card->workqueue); ret = if_sdio_power_off(card); /* Let runtime PM know the card is powered off */ pm_runtime_put_sync(&card->func->dev); return ret; } static int if_sdio_power_restore(struct lbs_private *priv) { struct if_sdio_card *card = priv->card; int r; /* Make sure the card will not be powered off by runtime PM */ pm_runtime_get_sync(&card->func->dev); r = if_sdio_power_on(card); if (r) return r; wait_event(card->pwron_waitq, priv->fw_ready); return 0; } /*******************************************************************/ /* SDIO callbacks */ /*******************************************************************/ static void if_sdio_interrupt(struct sdio_func *func) { int ret; struct if_sdio_card *card; u8 cause; lbs_deb_enter(LBS_DEB_SDIO); card = sdio_get_drvdata(func); cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); if (ret || !cause) goto out; lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret); if (ret) goto out; /* * Ignore the define name, this really means the card has * successfully received the command. */ card->priv->is_activity_detected = 1; if (cause & IF_SDIO_H_INT_DNLD) lbs_host_to_card_done(card->priv); if (cause & IF_SDIO_H_INT_UPLD) { ret = if_sdio_card_to_host(card); if (ret) goto out; } ret = 0; out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); } static int if_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct if_sdio_card *card; struct lbs_private *priv; int ret, i; unsigned int model; struct if_sdio_packet *packet; lbs_deb_enter(LBS_DEB_SDIO); for (i = 0;i < func->card->num_info;i++) { if (sscanf(func->card->info[i], "802.11 SDIO ID: %x", &model) == 1) break; if (sscanf(func->card->info[i], "ID: %x", &model) == 1) break; if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { model = MODEL_8385; break; } } if (i == func->card->num_info) { pr_err("unable to identify card model\n"); return -ENODEV; } card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL); if (!card) return -ENOMEM; card->func = func; card->model = model; switch (card->model) { case MODEL_8385: card->scratch_reg = IF_SDIO_SCRATCH_OLD; break; case MODEL_8686: card->scratch_reg = IF_SDIO_SCRATCH; break; case MODEL_8688: default: /* for newer chipsets */ card->scratch_reg = IF_SDIO_FW_STATUS; break; } spin_lock_init(&card->lock); card->workqueue = create_workqueue("libertas_sdio"); INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); init_waitqueue_head(&card->pwron_waitq); /* Check if we support this card */ for (i = 0; i < ARRAY_SIZE(fw_table); i++) { if (card->model == fw_table[i].model) break; } if (i == ARRAY_SIZE(fw_table)) { pr_err("unknown card model 0x%x\n", card->model); ret = -ENODEV; goto free; } sdio_set_drvdata(func, card); lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " "device = 0x%X, model = 0x%X, ioport = 0x%X\n", func->class, func->vendor, func->device, model, (unsigned)card->ioport); priv = lbs_add_card(card, &func->dev); if (!priv) { ret = -ENOMEM; goto free; } card->priv = priv; priv->card = card; priv->hw_host_to_card = if_sdio_host_to_card; priv->enter_deep_sleep = if_sdio_enter_deep_sleep; priv->exit_deep_sleep = if_sdio_exit_deep_sleep; priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; priv->reset_card = if_sdio_reset_card; priv->power_save = if_sdio_power_save; priv->power_restore = if_sdio_power_restore; ret = if_sdio_power_on(card); if (ret) goto err_activate_card; out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; err_activate_card: flush_workqueue(card->workqueue); lbs_remove_card(priv); free: destroy_workqueue(card->workqueue); while (card->packets) { packet = card->packets; card->packets = card->packets->next; kfree(packet); } kfree(card); goto out; } static void if_sdio_remove(struct sdio_func *func) { struct if_sdio_card *card; struct if_sdio_packet *packet; lbs_deb_enter(LBS_DEB_SDIO); card = sdio_get_drvdata(func); /* Undo decrement done above in if_sdio_probe */ pm_runtime_get_noresume(&func->dev); if (user_rmmod && (card->model == MODEL_8688)) { /* * FUNC_SHUTDOWN is required for SD8688 WLAN/BT * multiple functions */ struct cmd_header cmd; memset(&cmd, 0, sizeof(cmd)); lbs_deb_sdio("send function SHUTDOWN command\n"); if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN, &cmd, sizeof(cmd), lbs_cmd_copyback, (unsigned long) &cmd)) pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n"); } lbs_deb_sdio("call remove card\n"); lbs_stop_card(card->priv); lbs_remove_card(card->priv); flush_workqueue(card->workqueue); destroy_workqueue(card->workqueue); while (card->packets) { packet = card->packets; card->packets = card->packets->next; kfree(packet); } kfree(card); lbs_deb_leave(LBS_DEB_SDIO); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int if_sdio_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); int ret; struct if_sdio_card *card = sdio_get_drvdata(func); mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); /* If we're powered off anyway, just let the mmc layer remove the * card. */ if (!lbs_iface_active(card->priv)) return -ENOSYS; dev_info(dev, "%s: suspend: PM flags = 0x%x\n", sdio_func_id(func), flags); /* If we aren't being asked to wake on anything, we should bail out * and let the SD stack power down the card. */ if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) { dev_info(dev, "Suspend without wake params -- powering down card\n"); return -ENOSYS; } if (!(flags & MMC_PM_KEEP_POWER)) { dev_err(dev, "%s: cannot remain alive while host is suspended\n", sdio_func_id(func)); return -ENOSYS; } ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) return ret; ret = lbs_suspend(card->priv); if (ret) return ret; return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); } static int if_sdio_resume(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct if_sdio_card *card = sdio_get_drvdata(func); int ret; dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func)); ret = lbs_resume(card->priv); return ret; } static const struct dev_pm_ops if_sdio_pm_ops = { .suspend = if_sdio_suspend, .resume = if_sdio_resume, }; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ static struct sdio_driver if_sdio_driver = { .name = "libertas_sdio", .id_table = if_sdio_ids, .probe = if_sdio_probe, .remove = if_sdio_remove, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .drv = { .pm = &if_sdio_pm_ops, }, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ }; /*******************************************************************/ /* Module functions */ /*******************************************************************/ static int __init if_sdio_init_module(void) { int ret = 0; lbs_deb_enter(LBS_DEB_SDIO); printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n"); printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n"); ret = sdio_register_driver(&if_sdio_driver); /* Clear the flag in case user removes the card. */ user_rmmod = 0; lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret; } static void __exit if_sdio_exit_module(void) { lbs_deb_enter(LBS_DEB_SDIO); /* Set the flag as user is removing this module. */ user_rmmod = 1; cancel_work_sync(&card_reset_work); sdio_unregister_driver(&if_sdio_driver); lbs_deb_leave(LBS_DEB_SDIO); } module_init(if_sdio_init_module); module_exit(if_sdio_exit_module); MODULE_DESCRIPTION("Libertas SDIO WLAN Driver"); MODULE_AUTHOR("Pierre Ossman"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/libertas/cfg.c0000644000175000017500000015404512026211315023202 0ustar mcgrofmcgrof/* * Implement cfg80211 ("iw") support. * * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany * Holger Schurig * */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include "decl.h" #include "cfg.h" #include "cmd.h" #include "mesh.h" #define CHAN2G(_channel, _freq, _flags) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel lbs_2ghz_channels[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), CHAN2G(3, 2422, 0), CHAN2G(4, 2427, 0), CHAN2G(5, 2432, 0), CHAN2G(6, 2437, 0), CHAN2G(7, 2442, 0), CHAN2G(8, 2447, 0), CHAN2G(9, 2452, 0), CHAN2G(10, 2457, 0), CHAN2G(11, 2462, 0), CHAN2G(12, 2467, 0), CHAN2G(13, 2472, 0), CHAN2G(14, 2484, 0), }; #define RATETAB_ENT(_rate, _hw_value, _flags) { \ .bitrate = (_rate), \ .hw_value = (_hw_value), \ .flags = (_flags), \ } /* Table 6 in section 3.2.1.1 */ static struct ieee80211_rate lbs_rates[] = { RATETAB_ENT(10, 0, 0), RATETAB_ENT(20, 1, 0), RATETAB_ENT(55, 2, 0), RATETAB_ENT(110, 3, 0), RATETAB_ENT(60, 9, 0), RATETAB_ENT(90, 6, 0), RATETAB_ENT(120, 7, 0), RATETAB_ENT(180, 8, 0), RATETAB_ENT(240, 9, 0), RATETAB_ENT(360, 10, 0), RATETAB_ENT(480, 11, 0), RATETAB_ENT(540, 12, 0), }; static struct ieee80211_supported_band lbs_band_2ghz = { .channels = lbs_2ghz_channels, .n_channels = ARRAY_SIZE(lbs_2ghz_channels), .bitrates = lbs_rates, .n_bitrates = ARRAY_SIZE(lbs_rates), }; static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, }; /* Time to stay on the channel */ #define LBS_DWELL_PASSIVE 100 #define LBS_DWELL_ACTIVE 40 /*************************************************************************** * Misc utility functions * * TLVs are Marvell specific. They are very similar to IEs, they have the * same structure: type, length, data*. The only difference: for IEs, the * type and length are u8, but for TLVs they're __le16. */ /* * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 * in the firmware spec */ static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) { int ret = -ENOTSUPP; switch (auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: case NL80211_AUTHTYPE_SHARED_KEY: ret = auth_type; break; case NL80211_AUTHTYPE_AUTOMATIC: ret = NL80211_AUTHTYPE_OPEN_SYSTEM; break; case NL80211_AUTHTYPE_NETWORK_EAP: ret = 0x80; break; default: /* silence compiler */ break; } return ret; } /* * Various firmware commands need the list of supported rates, but with * the hight-bit set for basic rates */ static int lbs_add_rates(u8 *rates) { size_t i; for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { u8 rate = lbs_rates[i].bitrate / 5; if (rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16) rate |= 0x80; rates[i] = rate; } return ARRAY_SIZE(lbs_rates); } /*************************************************************************** * TLV utility functions * * TLVs are Marvell specific. They are very similar to IEs, they have the * same structure: type, length, data*. The only difference: for IEs, the * type and length are u8, but for TLVs they're __le16. */ /* * Add ssid TLV */ #define LBS_MAX_SSID_TLV_SIZE \ (sizeof(struct mrvl_ie_header) \ + IEEE80211_MAX_SSID_LEN) static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len) { struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv; /* * TLV-ID SSID 00 00 * length 06 00 * ssid 4d 4e 54 45 53 54 */ ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); ssid_tlv->header.len = cpu_to_le16(ssid_len); memcpy(ssid_tlv->ssid, ssid, ssid_len); return sizeof(ssid_tlv->header) + ssid_len; } /* * Add channel list TLV (section 8.4.2) * * Actual channel data comes from priv->wdev->wiphy->channels. */ #define LBS_MAX_CHANNEL_LIST_TLV_SIZE \ (sizeof(struct mrvl_ie_header) \ + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset))) static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv, int last_channel, int active_scan) { int chanscanparamsize = sizeof(struct chanscanparamset) * (last_channel - priv->scan_channel); struct mrvl_ie_header *header = (void *) tlv; /* * TLV-ID CHANLIST 01 01 * length 0e 00 * channel 00 01 00 00 00 64 00 * radio type 00 * channel 01 * scan type 00 * min scan time 00 00 * max scan time 64 00 * channel 2 00 02 00 00 00 64 00 * */ header->type = cpu_to_le16(TLV_TYPE_CHANLIST); header->len = cpu_to_le16(chanscanparamsize); tlv += sizeof(struct mrvl_ie_header); /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel, last_channel); */ memset(tlv, 0, chanscanparamsize); while (priv->scan_channel < last_channel) { struct chanscanparamset *param = (void *) tlv; param->radiotype = CMD_SCAN_RADIO_TYPE_BG; param->channumber = priv->scan_req->channels[priv->scan_channel]->hw_value; if (active_scan) { param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE); } else { param->chanscanmode.passivescan = 1; param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE); } tlv += sizeof(struct chanscanparamset); priv->scan_channel++; } return sizeof(struct mrvl_ie_header) + chanscanparamsize; } /* * Add rates TLV * * The rates are in lbs_bg_rates[], but for the 802.11b * rates the high bit is set. We add this TLV only because * there's a firmware which otherwise doesn't report all * APs in range. */ #define LBS_MAX_RATES_TLV_SIZE \ (sizeof(struct mrvl_ie_header) \ + (ARRAY_SIZE(lbs_rates))) /* Adds a TLV with all rates the hardware supports */ static int lbs_add_supported_rates_tlv(u8 *tlv) { size_t i; struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; /* * TLV-ID RATES 01 00 * length 0e 00 * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c */ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); tlv += sizeof(rate_tlv->header); i = lbs_add_rates(tlv); tlv += i; rate_tlv->header.len = cpu_to_le16(i); return sizeof(rate_tlv->header) + i; } /* Add common rates from a TLV and return the new end of the TLV */ static u8 * add_ie_rates(u8 *tlv, const u8 *ie, int *nrates) { int hw, ap, ap_max = ie[1]; u8 hw_rate; /* Advance past IE header */ ie += 2; lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max); for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { hw_rate = lbs_rates[hw].bitrate / 5; for (ap = 0; ap < ap_max; ap++) { if (hw_rate == (ie[ap] & 0x7f)) { *tlv++ = ie[ap]; *nrates = *nrates + 1; } } } return tlv; } /* * Adds a TLV with all rates the hardware *and* BSS supports. */ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) { struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; const u8 *rates_eid, *ext_rates_eid; int n = 0; rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); /* * 01 00 TLV_TYPE_RATES * 04 00 len * 82 84 8b 96 rates */ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); tlv += sizeof(rate_tlv->header); /* Add basic rates */ if (rates_eid) { tlv = add_ie_rates(tlv, rates_eid, &n); /* Add extended rates, if any */ if (ext_rates_eid) tlv = add_ie_rates(tlv, ext_rates_eid, &n); } else { lbs_deb_assoc("assoc: bss had no basic rate IE\n"); /* Fallback: add basic 802.11b rates */ *tlv++ = 0x82; *tlv++ = 0x84; *tlv++ = 0x8b; *tlv++ = 0x96; n = 4; } rate_tlv->header.len = cpu_to_le16(n); return sizeof(rate_tlv->header) + n; } /* * Add auth type TLV. * * This is only needed for newer firmware (V9 and up). */ #define LBS_MAX_AUTH_TYPE_TLV_SIZE \ sizeof(struct mrvl_ie_auth_type) static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type) { struct mrvl_ie_auth_type *auth = (void *) tlv; /* * 1f 01 TLV_TYPE_AUTH_TYPE * 01 00 len * 01 auth type */ auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header)); auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type)); return sizeof(*auth); } /* * Add channel (phy ds) TLV */ #define LBS_MAX_CHANNEL_TLV_SIZE \ sizeof(struct mrvl_ie_header) static int lbs_add_channel_tlv(u8 *tlv, u8 channel) { struct mrvl_ie_ds_param_set *ds = (void *) tlv; /* * 03 00 TLV_TYPE_PHY_DS * 01 00 len * 06 channel */ ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header)); ds->channel = channel; return sizeof(*ds); } /* * Add (empty) CF param TLV of the form: */ #define LBS_MAX_CF_PARAM_TLV_SIZE \ sizeof(struct mrvl_ie_header) static int lbs_add_cf_param_tlv(u8 *tlv) { struct mrvl_ie_cf_param_set *cf = (void *)tlv; /* * 04 00 TLV_TYPE_CF * 06 00 len * 00 cfpcnt * 00 cfpperiod * 00 00 cfpmaxduration * 00 00 cfpdurationremaining */ cf->header.type = cpu_to_le16(TLV_TYPE_CF); cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header)); return sizeof(*cf); } /* * Add WPA TLV */ #define LBS_MAX_WPA_TLV_SIZE \ (sizeof(struct mrvl_ie_header) \ + 128 /* TODO: I guessed the size */) static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) { size_t tlv_len; /* * We need just convert an IE to an TLV. IEs use u8 for the header, * u8 type * u8 len * u8[] data * but TLVs use __le16 instead: * __le16 type * __le16 len * u8[] data */ *tlv++ = *ie++; *tlv++ = 0; tlv_len = *tlv++ = *ie++; *tlv++ = 0; while (tlv_len--) *tlv++ = *ie++; /* the TLV is two bytes larger than the IE */ return ie_len + 2; } /* * Set Channel */ static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = -ENOTSUPP; lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", channel->center_freq, channel_type); if (channel_type != NL80211_CHAN_NO_HT) goto out; ret = lbs_set_channel(priv, channel->hw_value); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, struct net_device *netdev, struct ieee80211_channel *channel) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = -ENOTSUPP; lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", netdev_name(netdev), channel->center_freq); if (netdev != priv->mesh_dev) goto out; ret = lbs_mesh_set_channel(priv, channel->hw_value); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } /* * Scanning */ /* * When scanning, the firmware doesn't send a nul packet with the power-safe * bit to the AP. So we cannot stay away from our current channel too long, * otherwise we loose data. So take a "nap" while scanning every other * while. */ #define LBS_SCAN_BEFORE_NAP 4 /* * When the firmware reports back a scan-result, it gives us an "u8 rssi", * which isn't really an RSSI, as it becomes larger when moving away from * the AP. Anyway, we need to convert that into mBm. */ #define LBS_SCAN_RSSI_TO_MBM(rssi) \ ((-(int)rssi + 3)*100) static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, struct cmd_header *resp) { struct cfg80211_bss *bss; struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; int bsssize; const u8 *pos; const u8 *tsfdesc; int tsfsize; int i; int ret = -EILSEQ; lbs_deb_enter(LBS_DEB_CFG80211); bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n", scanresp->nr_sets, bsssize, le16_to_cpu(resp->size)); if (scanresp->nr_sets == 0) { ret = 0; goto done; } /* * The general layout of the scan response is described in chapter * 5.7.1. Basically we have a common part, then any number of BSS * descriptor sections. Finally we have section with the same number * of TSFs. * * cmd_ds_802_11_scan_rsp * cmd_header * pos_size * nr_sets * bssdesc 1 * bssid * rssi * timestamp * intvl * capa * IEs * bssdesc 2 * bssdesc n * MrvlIEtypes_TsfFimestamp_t * TSF for BSS 1 * TSF for BSS 2 * TSF for BSS n */ pos = scanresp->bssdesc_and_tlvbuffer; lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer, scanresp->bssdescriptsize); tsfdesc = pos + bsssize; tsfsize = 4 + 8 * scanresp->nr_sets; lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize); /* Validity check: we expect a Marvell-Local TLV */ i = get_unaligned_le16(tsfdesc); tsfdesc += 2; if (i != TLV_TYPE_TSFTIMESTAMP) { lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i); goto done; } /* * Validity check: the TLV holds TSF values with 8 bytes each, so * the size in the TLV must match the nr_sets value */ i = get_unaligned_le16(tsfdesc); tsfdesc += 2; if (i / 8 != scanresp->nr_sets) { lbs_deb_scan("scan response: invalid number of TSF timestamp " "sets (expected %d got %d)\n", scanresp->nr_sets, i / 8); goto done; } for (i = 0; i < scanresp->nr_sets; i++) { const u8 *bssid; const u8 *ie; int left; int ielen; int rssi; u16 intvl; u16 capa; int chan_no = -1; const u8 *ssid = NULL; u8 ssid_len = 0; DECLARE_SSID_BUF(ssid_buf); int len = get_unaligned_le16(pos); pos += 2; /* BSSID */ bssid = pos; pos += ETH_ALEN; /* RSSI */ rssi = *pos++; /* Packet time stamp */ pos += 8; /* Beacon interval */ intvl = get_unaligned_le16(pos); pos += 2; /* Capabilities */ capa = get_unaligned_le16(pos); pos += 2; /* To find out the channel, we must parse the IEs */ ie = pos; /* * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon * interval, capabilities */ ielen = left = len - (6 + 1 + 8 + 2 + 2); while (left >= 2) { u8 id, elen; id = *pos++; elen = *pos++; left -= 2; if (elen > left || elen == 0) { lbs_deb_scan("scan response: invalid IE fmt\n"); goto done; } if (id == WLAN_EID_DS_PARAMS) chan_no = *pos; if (id == WLAN_EID_SSID) { ssid = pos; ssid_len = elen; } left -= elen; pos += elen; } /* No channel, no luck */ if (chan_no != -1) { struct wiphy *wiphy = priv->wdev->wiphy; int freq = ieee80211_channel_to_frequency(chan_no, IEEE80211_BAND_2GHZ); struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %s, " "%d dBm\n", bssid, capa, chan_no, print_ssid(ssid_buf, ssid, ssid_len), LBS_SCAN_RSSI_TO_MBM(rssi)/100); if (channel && !(channel->flags & IEEE80211_CHAN_DISABLED)) { bss = cfg80211_inform_bss(wiphy, channel, bssid, get_unaligned_le64(tsfdesc), capa, intvl, ie, ielen, LBS_SCAN_RSSI_TO_MBM(rssi), GFP_KERNEL); cfg80211_put_bss(bss); } } else lbs_deb_scan("scan response: missing BSS channel IE\n"); tsfdesc += 8; } ret = 0; done: lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); return ret; } /* * Our scan command contains a TLV, consting of a SSID TLV, a channel list * TLV and a rates TLV. Determine the maximum size of them: */ #define LBS_SCAN_MAX_CMD_SIZE \ (sizeof(struct cmd_ds_802_11_scan) \ + LBS_MAX_SSID_TLV_SIZE \ + LBS_MAX_CHANNEL_LIST_TLV_SIZE \ + LBS_MAX_RATES_TLV_SIZE) /* * Assumes priv->scan_req is initialized and valid * Assumes priv->scan_channel is initialized */ static void lbs_scan_worker(struct work_struct *work) { struct lbs_private *priv = container_of(work, struct lbs_private, scan_work.work); struct cmd_ds_802_11_scan *scan_cmd; u8 *tlv; /* pointer into our current, growing TLV storage area */ int last_channel; int running, carrier; lbs_deb_enter(LBS_DEB_SCAN); scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL); if (scan_cmd == NULL) goto out_no_scan_cmd; /* prepare fixed part of scan command */ scan_cmd->bsstype = CMD_BSS_TYPE_ANY; /* stop network while we're away from our main channel */ running = !netif_queue_stopped(priv->dev); carrier = netif_carrier_ok(priv->dev); if (running) netif_stop_queue(priv->dev); if (carrier) netif_carrier_off(priv->dev); /* prepare fixed part of scan command */ tlv = scan_cmd->tlvbuffer; /* add SSID TLV */ if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) tlv += lbs_add_ssid_tlv(tlv, priv->scan_req->ssids[0].ssid, priv->scan_req->ssids[0].ssid_len); /* add channel TLVs */ last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP; if (last_channel > priv->scan_req->n_channels) last_channel = priv->scan_req->n_channels; tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel, priv->scan_req->n_ssids); /* add rates TLV */ tlv += lbs_add_supported_rates_tlv(tlv); if (priv->scan_channel < priv->scan_req->n_channels) { cancel_delayed_work(&priv->scan_work); if (netif_running(priv->dev)) queue_delayed_work(priv->work_thread, &priv->scan_work, msecs_to_jiffies(300)); } /* This is the final data we are about to send */ scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd); lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, sizeof(*scan_cmd)); lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, tlv - scan_cmd->tlvbuffer); __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr, le16_to_cpu(scan_cmd->hdr.size), lbs_ret_scan, 0); if (priv->scan_channel >= priv->scan_req->n_channels) { /* Mark scan done */ cancel_delayed_work(&priv->scan_work); lbs_scan_done(priv); } /* Restart network */ if (carrier) netif_carrier_on(priv->dev); if (running && !priv->tx_pending_len) netif_wake_queue(priv->dev); kfree(scan_cmd); /* Wake up anything waiting on scan completion */ if (priv->scan_req == NULL) { lbs_deb_scan("scan: waking up waiters\n"); wake_up_all(&priv->scan_q); } out_no_scan_cmd: lbs_deb_leave(LBS_DEB_SCAN); } static void _internal_start_scan(struct lbs_private *priv, bool internal, struct cfg80211_scan_request *request) { lbs_deb_enter(LBS_DEB_CFG80211); lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", request->n_ssids, request->n_channels, request->ie_len); priv->scan_channel = 0; priv->scan_req = request; priv->internal_scan = internal; queue_delayed_work(priv->work_thread, &priv->scan_work, msecs_to_jiffies(50)); lbs_deb_leave(LBS_DEB_CFG80211); } /* * Clean up priv->scan_req. Should be used to handle the allocation details. */ void lbs_scan_done(struct lbs_private *priv) { WARN_ON(!priv->scan_req); if (priv->internal_scan) kfree(priv->scan_req); else cfg80211_scan_done(priv->scan_req, false); priv->scan_req = NULL; } static int lbs_cfg_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = 0; lbs_deb_enter(LBS_DEB_CFG80211); if (priv->scan_req || delayed_work_pending(&priv->scan_work)) { /* old scan request not yet processed */ ret = -EAGAIN; goto out; } _internal_start_scan(priv, false, request); if (priv->surpriseremoved) ret = -EIO; out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } /* * Events */ void lbs_send_disconnect_notification(struct lbs_private *priv) { lbs_deb_enter(LBS_DEB_CFG80211); cfg80211_disconnected(priv->dev, 0, NULL, 0, GFP_KERNEL); lbs_deb_leave(LBS_DEB_CFG80211); } void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) { lbs_deb_enter(LBS_DEB_CFG80211); cfg80211_michael_mic_failure(priv->dev, priv->assoc_bss, event == MACREG_INT_CODE_MIC_ERR_MULTICAST ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE, -1, NULL, GFP_KERNEL); lbs_deb_leave(LBS_DEB_CFG80211); } /* * Connect/disconnect */ /* * This removes all WEP keys */ static int lbs_remove_wep_keys(struct lbs_private *priv) { struct cmd_ds_802_11_set_wep cmd; int ret; lbs_deb_enter(LBS_DEB_CFG80211); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.keyindex = cpu_to_le16(priv->wep_tx_key); cmd.action = cpu_to_le16(CMD_ACT_REMOVE); ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); lbs_deb_leave(LBS_DEB_CFG80211); return ret; } /* * Set WEP keys */ static int lbs_set_wep_keys(struct lbs_private *priv) { struct cmd_ds_802_11_set_wep cmd; int i; int ret; lbs_deb_enter(LBS_DEB_CFG80211); /* * command 13 00 * size 50 00 * sequence xx xx * result 00 00 * action 02 00 ACT_ADD * transmit key 00 00 * type for key 1 01 WEP40 * type for key 2 00 * type for key 3 00 * type for key 4 00 * key 1 39 39 39 39 39 00 00 00 * 00 00 00 00 00 00 00 00 * key 2 00 00 00 00 00 00 00 00 * 00 00 00 00 00 00 00 00 * key 3 00 00 00 00 00 00 00 00 * 00 00 00 00 00 00 00 00 * key 4 00 00 00 00 00 00 00 00 */ if (priv->wep_key_len[0] || priv->wep_key_len[1] || priv->wep_key_len[2] || priv->wep_key_len[3]) { /* Only set wep keys if we have at least one of them */ memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.keyindex = cpu_to_le16(priv->wep_tx_key); cmd.action = cpu_to_le16(CMD_ACT_ADD); for (i = 0; i < 4; i++) { switch (priv->wep_key_len[i]) { case WLAN_KEY_LEN_WEP40: cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; break; case WLAN_KEY_LEN_WEP104: cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; break; default: cmd.keytype[i] = 0; break; } memcpy(cmd.keymaterial[i], priv->wep_key[i], priv->wep_key_len[i]); } ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); } else { /* Otherwise remove all wep keys */ ret = lbs_remove_wep_keys(priv); } lbs_deb_leave(LBS_DEB_CFG80211); return ret; } /* * Enable/Disable RSN status */ static int lbs_enable_rsn(struct lbs_private *priv, int enable) { struct cmd_ds_802_11_enable_rsn cmd; int ret; lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable); /* * cmd 2f 00 * size 0c 00 * sequence xx xx * result 00 00 * action 01 00 ACT_SET * enable 01 00 */ memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.enable = cpu_to_le16(enable); ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); lbs_deb_leave(LBS_DEB_CFG80211); return ret; } /* * Set WPA/WPA key material */ /* * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we * get rid of WEXT, this should go into host.h */ struct cmd_key_material { struct cmd_header hdr; __le16 action; struct MrvlIEtype_keyParamSet param; } __packed; static int lbs_set_key_material(struct lbs_private *priv, int key_type, int key_info, u8 *key, u16 key_len) { struct cmd_key_material cmd; int ret; lbs_deb_enter(LBS_DEB_CFG80211); /* * Example for WPA (TKIP): * * cmd 5e 00 * size 34 00 * sequence xx xx * result 00 00 * action 01 00 * TLV type 00 01 key param * length 00 26 * key type 01 00 TKIP * key info 06 00 UNICAST | ENABLED * key len 20 00 * key 32 bytes */ memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4); cmd.param.keytypeid = cpu_to_le16(key_type); cmd.param.keyinfo = cpu_to_le16(key_info); cmd.param.keylen = cpu_to_le16(key_len); if (key && key_len) memcpy(cmd.param.key, key, key_len); ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); lbs_deb_leave(LBS_DEB_CFG80211); return ret; } /* * Sets the auth type (open, shared, etc) in the firmware. That * we use CMD_802_11_AUTHENTICATE is misleading, this firmware * command doesn't send an authentication frame at all, it just * stores the auth_type. */ static int lbs_set_authtype(struct lbs_private *priv, struct cfg80211_connect_params *sme) { struct cmd_ds_802_11_authenticate cmd; int ret; lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type); /* * cmd 11 00 * size 19 00 * sequence xx xx * result 00 00 * BSS id 00 13 19 80 da 30 * auth type 00 * reserved 00 00 00 00 00 00 00 00 00 00 */ memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); if (sme->bssid) memcpy(cmd.bssid, sme->bssid, ETH_ALEN); /* convert auth_type */ ret = lbs_auth_to_authtype(sme->auth_type); if (ret < 0) goto done; cmd.authtype = ret; ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); done: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } /* * Create association request */ #define LBS_ASSOC_MAX_CMD_SIZE \ (sizeof(struct cmd_ds_802_11_associate) \ - 512 /* cmd_ds_802_11_associate.iebuf */ \ + LBS_MAX_SSID_TLV_SIZE \ + LBS_MAX_CHANNEL_TLV_SIZE \ + LBS_MAX_CF_PARAM_TLV_SIZE \ + LBS_MAX_AUTH_TYPE_TLV_SIZE \ + LBS_MAX_WPA_TLV_SIZE) static int lbs_associate(struct lbs_private *priv, struct cfg80211_bss *bss, struct cfg80211_connect_params *sme) { struct cmd_ds_802_11_associate_response *resp; struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE, GFP_KERNEL); const u8 *ssid_eid; size_t len, resp_ie_len; int status; int ret; u8 *pos = &(cmd->iebuf[0]); u8 *tmp; lbs_deb_enter(LBS_DEB_CFG80211); if (!cmd) { ret = -ENOMEM; goto done; } /* * cmd 50 00 * length 34 00 * sequence xx xx * result 00 00 * BSS id 00 13 19 80 da 30 * capabilities 11 00 * listen interval 0a 00 * beacon interval 00 00 * DTIM period 00 * TLVs xx (up to 512 bytes) */ cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE); /* Fill in static fields */ memcpy(cmd->bssid, bss->bssid, ETH_ALEN); cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); cmd->capability = cpu_to_le16(bss->capability); /* add SSID TLV */ ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); if (ssid_eid) pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); else lbs_deb_assoc("no SSID\n"); /* add DS param TLV */ if (bss->channel) pos += lbs_add_channel_tlv(pos, bss->channel->hw_value); else lbs_deb_assoc("no channel\n"); /* add (empty) CF param TLV */ pos += lbs_add_cf_param_tlv(pos); /* add rates TLV */ tmp = pos + 4; /* skip Marvell IE header */ pos += lbs_add_common_rates_tlv(pos, bss); lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp); /* add auth type TLV */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9) pos += lbs_add_auth_type_tlv(pos, sme->auth_type); /* add WPA/WPA2 TLV */ if (sme->ie && sme->ie_len) pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len); len = (sizeof(*cmd) - sizeof(cmd->iebuf)) + (u16)(pos - (u8 *) &cmd->iebuf); cmd->hdr.size = cpu_to_le16(len); lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd, le16_to_cpu(cmd->hdr.size)); /* store for later use */ memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN); ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd); if (ret) goto done; /* generate connect message to cfg80211 */ resp = (void *) cmd; /* recast for easier field access */ status = le16_to_cpu(resp->statuscode); /* Older FW versions map the IEEE 802.11 Status Code in the association * response to the following values returned in resp->statuscode: * * IEEE Status Code Marvell Status Code * 0 -> 0x0000 ASSOC_RESULT_SUCCESS * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED * others -> 0x0003 ASSOC_RESULT_REFUSED * * Other response codes: * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for * association response from the AP) */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { switch (status) { case 0: break; case 1: lbs_deb_assoc("invalid association parameters\n"); status = WLAN_STATUS_CAPS_UNSUPPORTED; break; case 2: lbs_deb_assoc("timer expired while waiting for AP\n"); status = WLAN_STATUS_AUTH_TIMEOUT; break; case 3: lbs_deb_assoc("association refused by AP\n"); status = WLAN_STATUS_ASSOC_DENIED_UNSPEC; break; case 4: lbs_deb_assoc("authentication refused by AP\n"); status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; break; default: lbs_deb_assoc("association failure %d\n", status); /* v5 OLPC firmware does return the AP status code if * it's not one of the values above. Let that through. */ break; } } lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, " "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode), le16_to_cpu(resp->capability), le16_to_cpu(resp->aid)); resp_ie_len = le16_to_cpu(resp->hdr.size) - sizeof(resp->hdr) - 6; cfg80211_connect_result(priv->dev, priv->assoc_bss, sme->ie, sme->ie_len, resp->iebuf, resp_ie_len, status, GFP_KERNEL); if (status == 0) { /* TODO: get rid of priv->connect_status */ priv->connect_status = LBS_CONNECTED; netif_carrier_on(priv->dev); if (!priv->tx_pending_len) netif_tx_wake_all_queues(priv->dev); } kfree(cmd); done: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } static struct cfg80211_scan_request * _new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) { struct cfg80211_scan_request *creq = NULL; int i, n_channels = 0; enum ieee80211_band band; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) n_channels += wiphy->bands[band]->n_channels; } creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), GFP_ATOMIC); if (!creq) return NULL; /* SSIDs come after channels */ creq->ssids = (void *)&creq->channels[n_channels]; creq->n_channels = n_channels; creq->n_ssids = 1; /* Scan all available channels */ i = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { int j; if (!wiphy->bands[band]) continue; for (j = 0; j < wiphy->bands[band]->n_channels; j++) { /* ignore disabled channels */ if (wiphy->bands[band]->channels[j].flags & IEEE80211_CHAN_DISABLED) continue; creq->channels[i] = &wiphy->bands[band]->channels[j]; i++; } } if (i) { /* Set real number of channels specified in creq->channels[] */ creq->n_channels = i; /* Scan for the SSID we're going to connect to */ memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len); creq->ssids[0].ssid_len = sme->ssid_len; } else { /* No channels found... */ kfree(creq); creq = NULL; } return creq; } static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct lbs_private *priv = wiphy_priv(wiphy); struct cfg80211_bss *bss = NULL; int ret = 0; u8 preamble = RADIO_PREAMBLE_SHORT; if (dev == priv->mesh_dev) return -EOPNOTSUPP; lbs_deb_enter(LBS_DEB_CFG80211); if (!sme->bssid) { struct cfg80211_scan_request *creq; /* * Scan for the requested network after waiting for existing * scans to finish. */ lbs_deb_assoc("assoc: waiting for existing scans\n"); wait_event_interruptible_timeout(priv->scan_q, (priv->scan_req == NULL), (15 * HZ)); creq = _new_connect_scan_req(wiphy, sme); if (!creq) { ret = -EINVAL; goto done; } lbs_deb_assoc("assoc: scanning for compatible AP\n"); _internal_start_scan(priv, true, creq); lbs_deb_assoc("assoc: waiting for scan to complete\n"); wait_event_interruptible_timeout(priv->scan_q, (priv->scan_req == NULL), (15 * HZ)); lbs_deb_assoc("assoc: scanning competed\n"); } /* Find the BSS we want using available scan results */ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid, sme->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!bss) { wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", sme->bssid); ret = -ENOENT; goto done; } lbs_deb_assoc("trying %pM\n", bss->bssid); lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", sme->crypto.cipher_group, sme->key_idx, sme->key_len); /* As this is a new connection, clear locally stored WEP keys */ priv->wep_tx_key = 0; memset(priv->wep_key, 0, sizeof(priv->wep_key)); memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); /* set/remove WEP keys */ switch (sme->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: /* Store provided WEP keys in priv-> */ priv->wep_tx_key = sme->key_idx; priv->wep_key_len[sme->key_idx] = sme->key_len; memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len); /* Set WEP keys and WEP mode */ lbs_set_wep_keys(priv); priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; lbs_set_mac_control(priv); /* No RSN mode for WEP */ lbs_enable_rsn(priv, 0); break; case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */ /* * If we don't have no WEP, no WPA and no WPA2, * we remove all keys like in the WPA/WPA2 setup, * we just don't set RSN. * * Therefore: fall-through */ case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_CCMP: /* Remove WEP keys and WEP mode */ lbs_remove_wep_keys(priv); priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; lbs_set_mac_control(priv); /* clear the WPA/WPA2 keys */ lbs_set_key_material(priv, KEY_TYPE_ID_WEP, /* doesn't matter */ KEY_INFO_WPA_UNICAST, NULL, 0); lbs_set_key_material(priv, KEY_TYPE_ID_WEP, /* doesn't matter */ KEY_INFO_WPA_MCAST, NULL, 0); /* RSN mode for WPA/WPA2 */ lbs_enable_rsn(priv, sme->crypto.cipher_group != 0); break; default: wiphy_err(wiphy, "unsupported cipher group 0x%x\n", sme->crypto.cipher_group); ret = -ENOTSUPP; goto done; } ret = lbs_set_authtype(priv, sme); if (ret == -ENOTSUPP) { wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); goto done; } lbs_set_radio(priv, preamble, 1); /* Do the actual association */ ret = lbs_associate(priv, bss, sme); done: if (bss) cfg80211_put_bss(bss); lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } int lbs_disconnect(struct lbs_private *priv, u16 reason) { struct cmd_ds_802_11_deauthenticate cmd; int ret; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); /* Mildly ugly to use a locally store my own BSSID ... */ memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); cmd.reasoncode = cpu_to_le16(reason); ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); if (ret) return ret; cfg80211_disconnected(priv->dev, reason, NULL, 0, GFP_KERNEL); priv->connect_status = LBS_DISCONNECTED; return 0; } static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code) { struct lbs_private *priv = wiphy_priv(wiphy); if (dev == priv->mesh_dev) return -EOPNOTSUPP; lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); /* store for lbs_cfg_ret_disconnect() */ priv->disassoc_reason = reason_code; return lbs_disconnect(priv, reason_code); } static int lbs_cfg_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast) { struct lbs_private *priv = wiphy_priv(wiphy); if (netdev == priv->mesh_dev) return -EOPNOTSUPP; lbs_deb_enter(LBS_DEB_CFG80211); if (key_index != priv->wep_tx_key) { lbs_deb_assoc("set_default_key: to %d\n", key_index); priv->wep_tx_key = key_index; lbs_set_wep_keys(priv); } return 0; } static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, u8 idx, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct lbs_private *priv = wiphy_priv(wiphy); u16 key_info; u16 key_type; int ret = 0; if (netdev == priv->mesh_dev) return -EOPNOTSUPP; lbs_deb_enter(LBS_DEB_CFG80211); lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n", params->cipher, mac_addr); lbs_deb_assoc("add_key: key index %d, key len %d\n", idx, params->key_len); if (params->key_len) lbs_deb_hex(LBS_DEB_CFG80211, "KEY", params->key, params->key_len); lbs_deb_assoc("add_key: seq len %d\n", params->seq_len); if (params->seq_len) lbs_deb_hex(LBS_DEB_CFG80211, "SEQ", params->seq, params->seq_len); switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: /* actually compare if something has changed ... */ if ((priv->wep_key_len[idx] != params->key_len) || memcmp(priv->wep_key[idx], params->key, params->key_len) != 0) { priv->wep_key_len[idx] = params->key_len; memcpy(priv->wep_key[idx], params->key, params->key_len); lbs_set_wep_keys(priv); } break; case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_CCMP: key_info = KEY_INFO_WPA_ENABLED | ((idx == 0) ? KEY_INFO_WPA_UNICAST : KEY_INFO_WPA_MCAST); key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP) ? KEY_TYPE_ID_TKIP : KEY_TYPE_ID_AES; lbs_set_key_material(priv, key_type, key_info, params->key, params->key_len); break; default: wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher); ret = -ENOTSUPP; break; } return ret; } static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr) { lbs_deb_enter(LBS_DEB_CFG80211); lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n", key_index, mac_addr); #ifdef TODO struct lbs_private *priv = wiphy_priv(wiphy); /* * I think can keep this a NO-OP, because: * - we clear all keys whenever we do lbs_cfg_connect() anyway * - neither "iw" nor "wpa_supplicant" won't call this during * an ongoing connection * - TODO: but I have to check if this is still true when * I set the AP to periodic re-keying * - we've not kzallec() something when we've added a key at * lbs_cfg_connect() or lbs_cfg_add_key(). * * This causes lbs_cfg_del_key() only called at disconnect time, * where we'd just waste time deleting a key that is not going * to be used anyway. */ if (key_index < 3 && priv->wep_key_len[key_index]) { priv->wep_key_len[key_index] = 0; lbs_set_wep_keys(priv); } #endif return 0; } /* * Get station */ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo) { struct lbs_private *priv = wiphy_priv(wiphy); s8 signal, noise; int ret; size_t i; lbs_deb_enter(LBS_DEB_CFG80211); sinfo->filled |= STATION_INFO_TX_BYTES | STATION_INFO_TX_PACKETS | STATION_INFO_RX_BYTES | STATION_INFO_RX_PACKETS; sinfo->tx_bytes = priv->dev->stats.tx_bytes; sinfo->tx_packets = priv->dev->stats.tx_packets; sinfo->rx_bytes = priv->dev->stats.rx_bytes; sinfo->rx_packets = priv->dev->stats.rx_packets; /* Get current RSSI */ ret = lbs_get_rssi(priv, &signal, &noise); if (ret == 0) { sinfo->signal = signal; sinfo->filled |= STATION_INFO_SIGNAL; } /* Convert priv->cur_rate from hw_value to NL80211 value */ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { if (priv->cur_rate == lbs_rates[i].hw_value) { sinfo->txrate.legacy = lbs_rates[i].bitrate; sinfo->filled |= STATION_INFO_TX_BITRATE; break; } } return 0; } /* * Change interface */ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = 0; if (dev == priv->mesh_dev) return -EOPNOTSUPP; switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: break; default: return -EOPNOTSUPP; } lbs_deb_enter(LBS_DEB_CFG80211); if (priv->iface_running) ret = lbs_set_iface_type(priv, type); if (!ret) priv->wdev->iftype = type; lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } /* * IBSS (Ad-Hoc) */ /* * The firmware needs the following bits masked out of the beacon-derived * capability field when associating/joining to a BSS: * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused) */ #define CAPINFO_MASK (~(0xda00)) static void lbs_join_post(struct lbs_private *priv, struct cfg80211_ibss_params *params, u8 *bssid, u16 capability) { u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */ 2 + 4 + /* basic rates */ 2 + 1 + /* DS parameter */ 2 + 2 + /* atim */ 2 + 8]; /* extended rates */ u8 *fake = fake_ie; struct cfg80211_bss *bss; lbs_deb_enter(LBS_DEB_CFG80211); /* * For cfg80211_inform_bss, we'll need a fake IE, as we can't get * the real IE from the firmware. So we fabricate a fake IE based on * what the firmware actually sends (sniffed with wireshark). */ /* Fake SSID IE */ *fake++ = WLAN_EID_SSID; *fake++ = params->ssid_len; memcpy(fake, params->ssid, params->ssid_len); fake += params->ssid_len; /* Fake supported basic rates IE */ *fake++ = WLAN_EID_SUPP_RATES; *fake++ = 4; *fake++ = 0x82; *fake++ = 0x84; *fake++ = 0x8b; *fake++ = 0x96; /* Fake DS channel IE */ *fake++ = WLAN_EID_DS_PARAMS; *fake++ = 1; *fake++ = params->channel->hw_value; /* Fake IBSS params IE */ *fake++ = WLAN_EID_IBSS_PARAMS; *fake++ = 2; *fake++ = 0; /* ATIM=0 */ *fake++ = 0; /* Fake extended rates IE, TODO: don't add this for 802.11b only, * but I don't know how this could be checked */ *fake++ = WLAN_EID_EXT_SUPP_RATES; *fake++ = 8; *fake++ = 0x0c; *fake++ = 0x12; *fake++ = 0x18; *fake++ = 0x24; *fake++ = 0x30; *fake++ = 0x48; *fake++ = 0x60; *fake++ = 0x6c; lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); bss = cfg80211_inform_bss(priv->wdev->wiphy, params->channel, bssid, 0, capability, params->beacon_interval, fake_ie, fake - fake_ie, 0, GFP_KERNEL); cfg80211_put_bss(bss); memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); priv->wdev->ssid_len = params->ssid_len; cfg80211_ibss_joined(priv->dev, bssid, GFP_KERNEL); /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ priv->connect_status = LBS_CONNECTED; netif_carrier_on(priv->dev); if (!priv->tx_pending_len) netif_wake_queue(priv->dev); lbs_deb_leave(LBS_DEB_CFG80211); } static int lbs_ibss_join_existing(struct lbs_private *priv, struct cfg80211_ibss_params *params, struct cfg80211_bss *bss) { const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); struct cmd_ds_802_11_ad_hoc_join cmd; u8 preamble = RADIO_PREAMBLE_SHORT; int ret = 0; lbs_deb_enter(LBS_DEB_CFG80211); /* TODO: set preamble based on scan result */ ret = lbs_set_radio(priv, preamble, 1); if (ret) goto out; /* * Example CMD_802_11_AD_HOC_JOIN command: * * command 2c 00 CMD_802_11_AD_HOC_JOIN * size 65 00 * sequence xx xx * result 00 00 * bssid 02 27 27 97 2f 96 * ssid 49 42 53 53 00 00 00 00 * 00 00 00 00 00 00 00 00 * 00 00 00 00 00 00 00 00 * 00 00 00 00 00 00 00 00 * type 02 CMD_BSS_TYPE_IBSS * beacon period 64 00 * dtim period 00 * timestamp 00 00 00 00 00 00 00 00 * localtime 00 00 00 00 00 00 00 00 * IE DS 03 * IE DS len 01 * IE DS channel 01 * reserveed 00 00 00 00 * IE IBSS 06 * IE IBSS len 02 * IE IBSS atim 00 00 * reserved 00 00 00 00 * capability 02 00 * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00 * fail timeout ff 00 * probe delay 00 00 */ memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN); memcpy(cmd.bss.ssid, params->ssid, params->ssid_len); cmd.bss.type = CMD_BSS_TYPE_IBSS; cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; cmd.bss.ds.header.len = 1; cmd.bss.ds.channel = params->channel->hw_value; cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; cmd.bss.ibss.header.len = 2; cmd.bss.ibss.atimwindow = 0; cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); /* set rates to the intersection of our rates and the rates in the bss */ if (!rates_eid) { lbs_add_rates(cmd.bss.rates); } else { int hw, i; u8 rates_max = rates_eid[1]; u8 *rates = cmd.bss.rates; for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { u8 hw_rate = lbs_rates[hw].bitrate / 5; for (i = 0; i < rates_max; i++) { if (hw_rate == (rates_eid[i+2] & 0x7f)) { u8 rate = rates_eid[i+2]; if (rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16) rate |= 0x80; *rates++ = rate; } } } } /* Only v8 and below support setting this */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); } ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); if (ret) goto out; /* * This is a sample response to CMD_802_11_AD_HOC_JOIN: * * response 2c 80 * size 09 00 * sequence xx xx * result 00 00 * reserved 00 */ lbs_join_post(priv, params, bss->bssid, bss->capability); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } static int lbs_ibss_start_new(struct lbs_private *priv, struct cfg80211_ibss_params *params) { struct cmd_ds_802_11_ad_hoc_start cmd; struct cmd_ds_802_11_ad_hoc_result *resp = (struct cmd_ds_802_11_ad_hoc_result *) &cmd; u8 preamble = RADIO_PREAMBLE_SHORT; int ret = 0; u16 capability; lbs_deb_enter(LBS_DEB_CFG80211); ret = lbs_set_radio(priv, preamble, 1); if (ret) goto out; /* * Example CMD_802_11_AD_HOC_START command: * * command 2b 00 CMD_802_11_AD_HOC_START * size b1 00 * sequence xx xx * result 00 00 * ssid 54 45 53 54 00 00 00 00 * 00 00 00 00 00 00 00 00 * 00 00 00 00 00 00 00 00 * 00 00 00 00 00 00 00 00 * bss type 02 * beacon period 64 00 * dtim period 00 * IE IBSS 06 * IE IBSS len 02 * IE IBSS atim 00 00 * reserved 00 00 00 00 * IE DS 03 * IE DS len 01 * IE DS channel 01 * reserved 00 00 00 00 * probe delay 00 00 * capability 02 00 * rates 82 84 8b 96 (basic rates with have bit 7 set) * 0c 12 18 24 30 48 60 6c * padding 100 bytes */ memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); memcpy(cmd.ssid, params->ssid, params->ssid_len); cmd.bsstype = CMD_BSS_TYPE_IBSS; cmd.beaconperiod = cpu_to_le16(params->beacon_interval); cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS; cmd.ibss.header.len = 2; cmd.ibss.atimwindow = 0; cmd.ds.header.id = WLAN_EID_DS_PARAMS; cmd.ds.header.len = 1; cmd.ds.channel = params->channel->hw_value; /* Only v8 and below support setting probe delay */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); /* TODO: mix in WLAN_CAPABILITY_PRIVACY */ capability = WLAN_CAPABILITY_IBSS; cmd.capability = cpu_to_le16(capability); lbs_add_rates(cmd.rates); ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd); if (ret) goto out; /* * This is a sample response to CMD_802_11_AD_HOC_JOIN: * * response 2b 80 * size 14 00 * sequence xx xx * result 00 00 * reserved 00 * bssid 02 2b 7b 0f 86 0e */ lbs_join_post(priv, params, resp->bssid, capability); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = 0; struct cfg80211_bss *bss; DECLARE_SSID_BUF(ssid_buf); if (dev == priv->mesh_dev) return -EOPNOTSUPP; lbs_deb_enter(LBS_DEB_CFG80211); if (!params->channel) { ret = -ENOTSUPP; goto out; } ret = lbs_set_channel(priv, params->channel->hw_value); if (ret) goto out; /* Search if someone is beaconing. This assumes that the * bss list is populated already */ bss = cfg80211_get_bss(wiphy, params->channel, params->bssid, params->ssid, params->ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); if (bss) { ret = lbs_ibss_join_existing(priv, params, bss); cfg80211_put_bss(bss); } else ret = lbs_ibss_start_new(priv, params); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct lbs_private *priv = wiphy_priv(wiphy); struct cmd_ds_802_11_ad_hoc_stop cmd; int ret = 0; if (dev == priv->mesh_dev) return -EOPNOTSUPP; lbs_deb_enter(LBS_DEB_CFG80211); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd); /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */ lbs_mac_event_disconnected(priv); lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } /* * Initialization */ static struct cfg80211_ops lbs_cfg80211_ops = { .set_monitor_channel = lbs_cfg_set_monitor_channel, .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, .scan = lbs_cfg_scan, .connect = lbs_cfg_connect, .disconnect = lbs_cfg_disconnect, .add_key = lbs_cfg_add_key, .del_key = lbs_cfg_del_key, .set_default_key = lbs_cfg_set_default_key, .get_station = lbs_cfg_get_station, .change_virtual_intf = lbs_change_intf, .join_ibss = lbs_join_ibss, .leave_ibss = lbs_leave_ibss, }; /* * At this time lbs_private *priv doesn't even exist, so we just allocate * memory and don't initialize the wiphy further. This is postponed until we * can talk to the firmware and happens at registration time in * lbs_cfg_wiphy_register(). */ struct wireless_dev *lbs_cfg_alloc(struct device *dev) { int ret = 0; struct wireless_dev *wdev; lbs_deb_enter(LBS_DEB_CFG80211); wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); if (!wdev) { dev_err(dev, "cannot allocate wireless device\n"); return ERR_PTR(-ENOMEM); } wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); if (!wdev->wiphy) { dev_err(dev, "cannot allocate wiphy\n"); ret = -ENOMEM; goto err_wiphy_new; } lbs_deb_leave(LBS_DEB_CFG80211); return wdev; err_wiphy_new: kfree(wdev); lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ERR_PTR(ret); } static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) { struct region_code_mapping { const char *cn; int code; }; /* Section 5.17.2 */ static const struct region_code_mapping regmap[] = { {"US ", 0x10}, /* US FCC */ {"CA ", 0x20}, /* Canada */ {"EU ", 0x30}, /* ETSI */ {"ES ", 0x31}, /* Spain */ {"FR ", 0x32}, /* France */ {"JP ", 0x40}, /* Japan */ }; size_t i; lbs_deb_enter(LBS_DEB_CFG80211); for (i = 0; i < ARRAY_SIZE(regmap); i++) if (regmap[i].code == priv->regioncode) { regulatory_hint(priv->wdev->wiphy, regmap[i].cn); break; } lbs_deb_leave(LBS_DEB_CFG80211); } /* * This function get's called after lbs_setup_firmware() determined the * firmware capabities. So we can setup the wiphy according to our * hardware/firmware. */ int lbs_cfg_register(struct lbs_private *priv) { struct wireless_dev *wdev = priv->wdev; int ret; lbs_deb_enter(LBS_DEB_CFG80211); wdev->wiphy->max_scan_ssids = 1; wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); if (lbs_rtap_supported(priv)) wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); if (lbs_mesh_activated(priv)) wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz; /* * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have * never seen a firmware without WPA */ wdev->wiphy->cipher_suites = cipher_suites; wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); wdev->wiphy->reg_notifier = lbs_reg_notifier; ret = wiphy_register(wdev->wiphy); if (ret < 0) pr_err("cannot register wiphy device\n"); priv->wiphy_registered = true; ret = register_netdev(priv->dev); if (ret) pr_err("cannot register network device\n"); INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); lbs_cfg_set_regulatory_hint(priv); lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } int lbs_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = 0; lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " "callback for domain %c%c\n", request->alpha2[0], request->alpha2[1]); memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); if (lbs_iface_active(priv)) ret = lbs_set_11d_domain_info(priv); lbs_deb_leave(LBS_DEB_CFG80211); return ret; } void lbs_scan_deinit(struct lbs_private *priv) { lbs_deb_enter(LBS_DEB_CFG80211); cancel_delayed_work_sync(&priv->scan_work); } void lbs_cfg_free(struct lbs_private *priv) { struct wireless_dev *wdev = priv->wdev; lbs_deb_enter(LBS_DEB_CFG80211); if (!wdev) return; if (priv->wiphy_registered) wiphy_unregister(wdev->wiphy); if (wdev->wiphy) wiphy_free(wdev->wiphy); kfree(wdev); } compat-drivers-2012-09-18/drivers/net/wireless/libertas/Makefile0000644000175000017500000000110412026211315023722 0ustar mcgrofmcgroflibertas-y += cfg.o libertas-y += cmd.o libertas-y += cmdresp.o libertas-y += debugfs.o libertas-y += ethtool.o libertas-y += main.o libertas-y += rx.o libertas-y += tx.o libertas-y += firmware.o libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o usb8xxx-objs += if_usb.o libertas_cs-objs += if_cs.o libertas_sdio-objs += if_sdio.o libertas_spi-objs += if_spi.o obj-$(CONFIG_LIBERTAS) += libertas.o obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o obj-$(CONFIG_COMPAT_LIBERTAS_SDIO) += libertas_sdio.o obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o compat-drivers-2012-09-18/drivers/net/wireless/libertas/defs.h0000644000175000017500000002706712026211315023374 0ustar mcgrofmcgrof/* * This header file contains global constant/enum definitions, * global variable declaration. */ #ifndef _LBS_DEFS_H_ #define _LBS_DEFS_H_ #include #ifdef CONFIG_LIBERTAS_DEBUG #define DEBUG #define PROC_DEBUG #endif #ifndef DRV_NAME #define DRV_NAME "libertas" #endif /* * Really nasty hack to avoid stuffing compat.diff with tons of ifdefs, * we could add this to a compat header file but too lazy to check ml_priv * is not used anywhere else */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) #define ml_priv priv #endif #define LBS_DEB_ENTER 0x00000001 #define LBS_DEB_LEAVE 0x00000002 #define LBS_DEB_MAIN 0x00000004 #define LBS_DEB_NET 0x00000008 #define LBS_DEB_MESH 0x00000010 #define LBS_DEB_WEXT 0x00000020 #define LBS_DEB_IOCTL 0x00000040 #define LBS_DEB_SCAN 0x00000080 #define LBS_DEB_ASSOC 0x00000100 #define LBS_DEB_JOIN 0x00000200 #define LBS_DEB_11D 0x00000400 #define LBS_DEB_DEBUGFS 0x00000800 #define LBS_DEB_ETHTOOL 0x00001000 #define LBS_DEB_HOST 0x00002000 #define LBS_DEB_CMD 0x00004000 #define LBS_DEB_RX 0x00008000 #define LBS_DEB_TX 0x00010000 #define LBS_DEB_USB 0x00020000 #define LBS_DEB_CS 0x00040000 #define LBS_DEB_FW 0x00080000 #define LBS_DEB_THREAD 0x00100000 #define LBS_DEB_HEX 0x00200000 #define LBS_DEB_SDIO 0x00400000 #define LBS_DEB_SYSFS 0x00800000 #define LBS_DEB_SPI 0x01000000 #define LBS_DEB_CFG80211 0x02000000 extern unsigned int lbs_debug; #ifdef DEBUG #define LBS_DEB_LL(grp, grpnam, fmt, args...) \ do { if ((lbs_debug & (grp)) == (grp)) \ printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ in_interrupt() ? " (INT)" : "", ## args); } while (0) #else #define LBS_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) #endif #define lbs_deb_enter(grp) \ LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s()\n", __func__); #define lbs_deb_enter_args(grp, fmt, args...) \ LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); #define lbs_deb_leave(grp) \ LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s()\n", __func__); #define lbs_deb_leave_args(grp, fmt, args...) \ LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ __func__, ##args); #define lbs_deb_main(fmt, args...) LBS_DEB_LL(LBS_DEB_MAIN, " main", fmt, ##args) #define lbs_deb_net(fmt, args...) LBS_DEB_LL(LBS_DEB_NET, " net", fmt, ##args) #define lbs_deb_mesh(fmt, args...) LBS_DEB_LL(LBS_DEB_MESH, " mesh", fmt, ##args) #define lbs_deb_wext(fmt, args...) LBS_DEB_LL(LBS_DEB_WEXT, " wext", fmt, ##args) #define lbs_deb_ioctl(fmt, args...) LBS_DEB_LL(LBS_DEB_IOCTL, " ioctl", fmt, ##args) #define lbs_deb_scan(fmt, args...) LBS_DEB_LL(LBS_DEB_SCAN, " scan", fmt, ##args) #define lbs_deb_assoc(fmt, args...) LBS_DEB_LL(LBS_DEB_ASSOC, " assoc", fmt, ##args) #define lbs_deb_join(fmt, args...) LBS_DEB_LL(LBS_DEB_JOIN, " join", fmt, ##args) #define lbs_deb_11d(fmt, args...) LBS_DEB_LL(LBS_DEB_11D, " 11d", fmt, ##args) #define lbs_deb_debugfs(fmt, args...) LBS_DEB_LL(LBS_DEB_DEBUGFS, " debugfs", fmt, ##args) #define lbs_deb_ethtool(fmt, args...) LBS_DEB_LL(LBS_DEB_ETHTOOL, " ethtool", fmt, ##args) #define lbs_deb_host(fmt, args...) LBS_DEB_LL(LBS_DEB_HOST, " host", fmt, ##args) #define lbs_deb_cmd(fmt, args...) LBS_DEB_LL(LBS_DEB_CMD, " cmd", fmt, ##args) #define lbs_deb_rx(fmt, args...) LBS_DEB_LL(LBS_DEB_RX, " rx", fmt, ##args) #define lbs_deb_tx(fmt, args...) LBS_DEB_LL(LBS_DEB_TX, " tx", fmt, ##args) #define lbs_deb_fw(fmt, args...) LBS_DEB_LL(LBS_DEB_FW, " fw", fmt, ##args) #define lbs_deb_usb(fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usb", fmt, ##args) #define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) #define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args) #define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) #define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args) #define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args) #define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args) #define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) #ifdef DEBUG static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) { int i = 0; if (len && (lbs_debug & LBS_DEB_HEX) && (lbs_debug & grp)) { for (i = 1; i <= len; i++) { if ((i & 0xf) == 1) { if (i != 1) printk("\n"); printk(DRV_NAME " %s: ", prompt); } printk("%02x ", (u8) * buf); buf++; } printk("\n"); } } #else #define lbs_deb_hex(grp,prompt,buf,len) do {} while (0) #endif /* Buffer Constants */ /* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas * driver has more local TxPDs. Each TxPD on the host memory is associated * with a Tx control node. The driver maintains 8 RxPD descriptors for * station firmware to store Rx packet information. * * Current version of MAC has a 32x6 multicast address buffer. * * 802.11b can have up to 14 channels, the driver keeps the * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. */ #define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 #define LBS_NUM_CMD_BUFFERS 10 #define LBS_CMD_BUFFER_SIZE (2 * 1024) #define MRVDRV_MAX_CHANNEL_SIZE 14 #define MRVDRV_ASSOCIATION_TIME_OUT 255 #define MRVDRV_SNAP_HEADER_LEN 8 #define LBS_UPLD_SIZE 2312 #define DEV_NAME_LEN 32 /* Wake criteria for HOST_SLEEP_CFG command */ #define EHS_WAKE_ON_BROADCAST_DATA 0x0001 #define EHS_WAKE_ON_UNICAST_DATA 0x0002 #define EHS_WAKE_ON_MAC_EVENT 0x0004 #define EHS_WAKE_ON_MULTICAST_DATA 0x0008 #define EHS_REMOVE_WAKEUP 0xFFFFFFFF /* Wake rules for Host_Sleep_CFG command */ #define WOL_RULE_NET_TYPE_INFRA_OR_IBSS 0x00 #define WOL_RULE_NET_TYPE_MESH 0x10 #define WOL_RULE_ADDR_TYPE_BCAST 0x01 #define WOL_RULE_ADDR_TYPE_MCAST 0x08 #define WOL_RULE_ADDR_TYPE_UCAST 0x02 #define WOL_RULE_OP_AND 0x01 #define WOL_RULE_OP_OR 0x02 #define WOL_RULE_OP_INVALID 0xFF #define WOL_RESULT_VALID_CMD 0 #define WOL_RESULT_NOSPC_ERR 1 #define WOL_RESULT_EEXIST_ERR 2 /* Misc constants */ /* This section defines 802.11 specific contants */ #define MRVDRV_MAX_BSS_DESCRIPTS 16 #define MRVDRV_MAX_REGION_CODE 6 #define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 #define MRVDRV_CHANNELS_PER_SCAN 4 #define MRVDRV_MAX_CHANNELS_PER_SCAN 14 #define MRVDRV_MIN_BEACON_INTERVAL 20 #define MRVDRV_MAX_BEACON_INTERVAL 1000 #define MRVDRV_BEACON_INTERVAL 100 #define MARVELL_MESH_IE_LENGTH 9 /* * Values used to populate the struct mrvl_mesh_ie. The only time you need this * is when enabling the mesh using CMD_MESH_CONFIG. */ #define MARVELL_MESH_IE_TYPE 4 #define MARVELL_MESH_IE_SUBTYPE 0 #define MARVELL_MESH_IE_VERSION 0 #define MARVELL_MESH_PROTO_ID_HWMP 0 #define MARVELL_MESH_METRIC_ID 0 #define MARVELL_MESH_CAPABILITY 0 /* INT status Bit Definition */ #define MRVDRV_TX_DNLD_RDY 0x0001 #define MRVDRV_RX_UPLD_RDY 0x0002 #define MRVDRV_CMD_DNLD_RDY 0x0004 #define MRVDRV_CMD_UPLD_RDY 0x0008 #define MRVDRV_CARDEVENT 0x0010 /* Automatic TX control default levels */ #define POW_ADAPT_DEFAULT_P0 13 #define POW_ADAPT_DEFAULT_P1 15 #define POW_ADAPT_DEFAULT_P2 18 #define TPC_DEFAULT_P0 5 #define TPC_DEFAULT_P1 10 #define TPC_DEFAULT_P2 13 /* TxPD status */ /* * Station firmware use TxPD status field to report final Tx transmit * result, Bit masks are used to present combined situations. */ #define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 #define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 /* Tx mesh flag */ /* * Currently we are using normal WDS flag as mesh flag. * TODO: change to proper mesh flag when MAC understands it. */ #define TxPD_CONTROL_WDS_FRAME (1<<17) #define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME /* Mesh interface ID */ #define MESH_IFACE_ID 0x0001 /* Mesh id should be in bits 14-13-12 */ #define MESH_IFACE_BIT_OFFSET 0x000c /* Mesh enable bit in FW capability */ #define MESH_CAPINFO_ENABLE_MASK (1<<16) /* FW definition from Marvell v4 */ #define MRVL_FW_V4 (0x04) /* FW definition from Marvell v5 */ #define MRVL_FW_V5 (0x05) /* FW definition from Marvell v10 */ #define MRVL_FW_V10 (0x0a) /* FW major revision definition */ #define MRVL_FW_MAJOR_REV(x) ((x)>>24) /* RxPD status */ #define MRVDRV_RXPD_STATUS_OK 0x0001 /* RxPD status - Received packet types */ /* Rx mesh flag */ /* * Currently we are using normal WDS flag as mesh flag. * TODO: change to proper mesh flag when MAC understands it. */ #define RxPD_CONTROL_WDS_FRAME (0x40) #define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME /* RSSI-related defines */ /* * RSSI constants are used to implement 802.11 RSSI threshold * indication. if the Rx packet signal got too weak for 5 consecutive * times, miniport driver (driver) will report this event to wrapper */ #define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) /* RTS/FRAG related defines */ #define MRVDRV_RTS_MIN_VALUE 0 #define MRVDRV_RTS_MAX_VALUE 2347 #define MRVDRV_FRAG_MIN_VALUE 256 #define MRVDRV_FRAG_MAX_VALUE 2346 /* This is for firmware specific length */ #define EXTRA_LEN 36 #define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) #define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ (ETH_FRAME_LEN + sizeof(struct rxpd) \ + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) #define CMD_F_HOSTCMD (1 << 0) #define FW_CAPINFO_WPA (1 << 0) #define FW_CAPINFO_PS (1 << 1) #define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13) #define FW_CAPINFO_BOOT2_UPGRADE (1<<14) #define FW_CAPINFO_PERSISTENT_CONFIG (1<<15) #define KEY_LEN_WPA_AES 16 #define KEY_LEN_WPA_TKIP 32 #define KEY_LEN_WEP_104 13 #define KEY_LEN_WEP_40 5 #define RF_ANTENNA_1 0x1 #define RF_ANTENNA_2 0x2 #define RF_ANTENNA_AUTO 0xFFFF #define BAND_B (0x01) #define BAND_G (0x02) #define ALL_802_11_BANDS (BAND_B | BAND_G) #define MAX_RATES 14 #define MAX_LEDS 8 /* Global Variable Declaration */ extern const char lbs_driver_version[]; extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE]; /* ENUM definition */ /* SNRNF_TYPE */ enum SNRNF_TYPE { TYPE_BEACON = 0, TYPE_RXPD, MAX_TYPE_B }; /* SNRNF_DATA */ enum SNRNF_DATA { TYPE_NOAVG = 0, TYPE_AVG, MAX_TYPE_AVG }; /* LBS_802_11_POWER_MODE */ enum LBS_802_11_POWER_MODE { LBS802_11POWERMODECAM, LBS802_11POWERMODEMAX_PSP, LBS802_11POWERMODEFAST_PSP, /* not a real mode, defined as an upper bound */ LBS802_11POWEMODEMAX }; /* PS_STATE */ enum PS_STATE { PS_STATE_FULL_POWER, PS_STATE_AWAKE, PS_STATE_PRE_SLEEP, PS_STATE_SLEEP }; /* DNLD_STATE */ enum DNLD_STATE { DNLD_RES_RECEIVED, DNLD_DATA_SENT, DNLD_CMD_SENT, DNLD_BOOTCMD_SENT, }; /* LBS_MEDIA_STATE */ enum LBS_MEDIA_STATE { LBS_CONNECTED, LBS_DISCONNECTED }; /* LBS_802_11_PRIVACY_FILTER */ enum LBS_802_11_PRIVACY_FILTER { LBS802_11PRIVFILTERACCEPTALL, LBS802_11PRIVFILTER8021XWEP }; /* mv_ms_type */ enum mv_ms_type { MVMS_DAT = 0, MVMS_CMD = 1, MVMS_TXDONE = 2, MVMS_EVENT }; /* KEY_TYPE_ID */ enum KEY_TYPE_ID { KEY_TYPE_ID_WEP = 0, KEY_TYPE_ID_TKIP, KEY_TYPE_ID_AES }; /* KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ enum KEY_INFO_WPA { KEY_INFO_WPA_MCAST = 0x01, KEY_INFO_WPA_UNICAST = 0x02, KEY_INFO_WPA_ENABLED = 0x04 }; /* Default values for fwt commands. */ #define FWT_DEFAULT_METRIC 0 #define FWT_DEFAULT_DIR 1 /* Default Rate, 11Mbps */ #define FWT_DEFAULT_RATE 3 #define FWT_DEFAULT_SSN 0xffffffff #define FWT_DEFAULT_DSN 0 #define FWT_DEFAULT_HOPCOUNT 0 #define FWT_DEFAULT_TTL 0 #define FWT_DEFAULT_EXPIRATION 0 #define FWT_DEFAULT_SLEEPMODE 0 #define FWT_DEFAULT_SNR 0 #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/types.h0000644000175000017500000001375212026211315023613 0ustar mcgrofmcgrof/* * This header file contains definition for global types */ #ifndef _LBS_TYPES_H_ #define _LBS_TYPES_H_ #include #include #include struct ieee_ie_header { u8 id; u8 len; } __packed; struct ieee_ie_cf_param_set { struct ieee_ie_header header; u8 cfpcnt; u8 cfpperiod; __le16 cfpmaxduration; __le16 cfpdurationremaining; } __packed; struct ieee_ie_ibss_param_set { struct ieee_ie_header header; __le16 atimwindow; } __packed; union ieee_ss_param_set { struct ieee_ie_cf_param_set cf; struct ieee_ie_ibss_param_set ibss; } __packed; struct ieee_ie_fh_param_set { struct ieee_ie_header header; __le16 dwelltime; u8 hopset; u8 hoppattern; u8 hopindex; } __packed; struct ieee_ie_ds_param_set { struct ieee_ie_header header; u8 channel; } __packed; union ieee_phy_param_set { struct ieee_ie_fh_param_set fh; struct ieee_ie_ds_param_set ds; } __packed; /* TLV type ID definition */ #define PROPRIETARY_TLV_BASE_ID 0x0100 /* Terminating TLV type */ #define MRVL_TERMINATE_TLV_ID 0xffff #define TLV_TYPE_SSID 0x0000 #define TLV_TYPE_RATES 0x0001 #define TLV_TYPE_PHY_FH 0x0002 #define TLV_TYPE_PHY_DS 0x0003 #define TLV_TYPE_CF 0x0004 #define TLV_TYPE_IBSS 0x0006 #define TLV_TYPE_DOMAIN 0x0007 #define TLV_TYPE_POWER_CAPABILITY 0x0021 #define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) #define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) #define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) #define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) #define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) #define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) #define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) #define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) #define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) #define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) #define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) #define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) #define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) #define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) #define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) #define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) #define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) #define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) #define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) #define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) #define TLV_TYPE_MESH_ID (PROPRIETARY_TLV_BASE_ID + 37) #define TLV_TYPE_OLD_MESH_ID (PROPRIETARY_TLV_BASE_ID + 291) /* TLV related data structures */ struct mrvl_ie_header { __le16 type; __le16 len; } __packed; struct mrvl_ie_data { struct mrvl_ie_header header; u8 Data[1]; } __packed; struct mrvl_ie_rates_param_set { struct mrvl_ie_header header; u8 rates[1]; } __packed; struct mrvl_ie_ssid_param_set { struct mrvl_ie_header header; u8 ssid[1]; } __packed; struct mrvl_ie_wildcard_ssid_param_set { struct mrvl_ie_header header; u8 MaxSsidlength; u8 ssid[1]; } __packed; struct chanscanmode { #ifdef __BIG_ENDIAN_BITFIELD u8 reserved_2_7:6; u8 disablechanfilt:1; u8 passivescan:1; #else u8 passivescan:1; u8 disablechanfilt:1; u8 reserved_2_7:6; #endif } __packed; struct chanscanparamset { u8 radiotype; u8 channumber; struct chanscanmode chanscanmode; __le16 minscantime; __le16 maxscantime; } __packed; struct mrvl_ie_chanlist_param_set { struct mrvl_ie_header header; struct chanscanparamset chanscanparam[1]; } __packed; struct mrvl_ie_cf_param_set { struct mrvl_ie_header header; u8 cfpcnt; u8 cfpperiod; __le16 cfpmaxduration; __le16 cfpdurationremaining; } __packed; struct mrvl_ie_ds_param_set { struct mrvl_ie_header header; u8 channel; } __packed; struct mrvl_ie_rsn_param_set { struct mrvl_ie_header header; u8 rsnie[1]; } __packed; struct mrvl_ie_tsf_timestamp { struct mrvl_ie_header header; __le64 tsftable[1]; } __packed; /* v9 and later firmware only */ struct mrvl_ie_auth_type { struct mrvl_ie_header header; __le16 auth; } __packed; /* Local Power capability */ struct mrvl_ie_power_capability { struct mrvl_ie_header header; s8 minpower; s8 maxpower; } __packed; /* used in CMD_802_11_SUBSCRIBE_EVENT for SNR, RSSI and Failure */ struct mrvl_ie_thresholds { struct mrvl_ie_header header; u8 value; u8 freq; } __packed; struct mrvl_ie_beacons_missed { struct mrvl_ie_header header; u8 beaconmissed; u8 reserved; } __packed; struct mrvl_ie_num_probes { struct mrvl_ie_header header; __le16 numprobes; } __packed; struct mrvl_ie_bcast_probe { struct mrvl_ie_header header; __le16 bcastprobe; } __packed; struct mrvl_ie_num_ssid_probe { struct mrvl_ie_header header; __le16 numssidprobe; } __packed; struct led_pin { u8 led; u8 pin; } __packed; struct mrvl_ie_ledgpio { struct mrvl_ie_header header; struct led_pin ledpin[1]; } __packed; struct led_bhv { uint8_t firmwarestate; uint8_t led; uint8_t ledstate; uint8_t ledarg; } __packed; struct mrvl_ie_ledbhv { struct mrvl_ie_header header; struct led_bhv ledbhv[1]; } __packed; /* * Meant to be packed as the value member of a struct ieee80211_info_element. * Note that the len member of the ieee80211_info_element varies depending on * the mesh_id_len */ struct mrvl_meshie_val { uint8_t oui[3]; uint8_t type; uint8_t subtype; uint8_t version; uint8_t active_protocol_id; uint8_t active_metric_id; uint8_t mesh_capability; uint8_t mesh_id_len; uint8_t mesh_id[IEEE80211_MAX_SSID_LEN]; } __packed; struct mrvl_meshie { u8 id, len; struct mrvl_meshie_val val; } __packed; struct mrvl_mesh_defaults { __le32 bootflag; uint8_t boottime; uint8_t reserved; __le16 channel; struct mrvl_meshie meshie; } __packed; #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/tx.c0000644000175000017500000001261512026211315023072 0ustar mcgrofmcgrof/* * This file contains the handling of TX in wlan driver. */ #include #include #include #include #include #include #include "host.h" #include "radiotap.h" #include "decl.h" #include "defs.h" #include "dev.h" #include "mesh.h" /** * convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) * * @rate: Input rate * returns: Output Rate (0 if invalid) */ static u32 convert_radiotap_rate_to_mv(u8 rate) { switch (rate) { case 2: /* 1 Mbps */ return 0 | (1 << 4); case 4: /* 2 Mbps */ return 1 | (1 << 4); case 11: /* 5.5 Mbps */ return 2 | (1 << 4); case 22: /* 11 Mbps */ return 3 | (1 << 4); case 12: /* 6 Mbps */ return 4 | (1 << 4); case 18: /* 9 Mbps */ return 5 | (1 << 4); case 24: /* 12 Mbps */ return 6 | (1 << 4); case 36: /* 18 Mbps */ return 7 | (1 << 4); case 48: /* 24 Mbps */ return 8 | (1 << 4); case 72: /* 36 Mbps */ return 9 | (1 << 4); case 96: /* 48 Mbps */ return 10 | (1 << 4); case 108: /* 54 Mbps */ return 11 | (1 << 4); } return 0; } /** * lbs_hard_start_xmit - checks the conditions and sends packet to IF * layer if everything is ok * * @skb: A pointer to skb which includes TX packet * @dev: A pointer to the &struct net_device * returns: 0 or -1 */ netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long flags; struct lbs_private *priv = dev->ml_priv; struct txpd *txpd; char *p802x_hdr; uint16_t pkt_len; netdev_tx_t ret = NETDEV_TX_OK; lbs_deb_enter(LBS_DEB_TX); /* We need to protect against the queues being restarted before we get round to stopping them */ spin_lock_irqsave(&priv->driver_lock, flags); if (priv->surpriseremoved) goto free; if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { lbs_deb_tx("tx err: skb length %d 0 or > %zd\n", skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); /* We'll never manage to send this one; drop it and return 'OK' */ dev->stats.tx_dropped++; dev->stats.tx_errors++; goto free; } netif_stop_queue(priv->dev); if (priv->mesh_dev) netif_stop_queue(priv->mesh_dev); if (priv->tx_pending_len) { /* This can happen if packets come in on the mesh and eth device simultaneously -- there's no mutual exclusion on hard_start_xmit() calls between devices. */ lbs_deb_tx("Packet on %s while busy\n", dev->name); ret = NETDEV_TX_BUSY; goto unlock; } priv->tx_pending_len = -1; spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); txpd = (void *)priv->tx_pending_buf; memset(txpd, 0, sizeof(struct txpd)); p802x_hdr = skb->data; pkt_len = skb->len; if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data; /* set txpd fields from the radiotap header */ txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate)); /* skip the radiotap header */ p802x_hdr += sizeof(*rtap_hdr); pkt_len -= sizeof(*rtap_hdr); /* copy destination address from 802.11 header */ memcpy(txpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); } else { /* copy destination address from 802.3 header */ memcpy(txpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); } txpd->tx_packet_length = cpu_to_le16(pkt_len); txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); lbs_mesh_set_txpd(priv, dev, txpd); lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd)); lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); spin_lock_irqsave(&priv->driver_lock, flags); priv->tx_pending_len = pkt_len + sizeof(struct txpd); lbs_deb_tx("%s lined up packet\n", __func__); dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { /* Keep the skb to echo it back once Tx feedback is received from FW */ skb_orphan(skb); /* Keep the skb around for when we get feedback */ priv->currenttxskb = skb; } else { free: dev_kfree_skb_any(skb); } unlock: spin_unlock_irqrestore(&priv->driver_lock, flags); wake_up(&priv->waitq); lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret); return ret; } /** * lbs_send_tx_feedback - sends to the host the last transmitted packet, * filling the radiotap headers with transmission information. * * @priv: A pointer to &struct lbs_private structure * @try_count: A 32-bit value containing transmission retry status. * * returns: void */ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count) { struct tx_radiotap_hdr *radiotap_hdr; if (priv->wdev->iftype != NL80211_IFTYPE_MONITOR || priv->currenttxskb == NULL) return; radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data; radiotap_hdr->data_retries = try_count ? (1 + priv->txretrycount - try_count) : 0; priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb, priv->dev); netif_rx(priv->currenttxskb); priv->currenttxskb = NULL; if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); if (priv->mesh_dev && netif_running(priv->mesh_dev)) netif_wake_queue(priv->mesh_dev); } EXPORT_SYMBOL_GPL(lbs_send_tx_feedback); compat-drivers-2012-09-18/drivers/net/wireless/libertas/radiotap.h0000644000175000017500000000230712026211315024244 0ustar mcgrofmcgrof#include struct tx_radiotap_hdr { struct ieee80211_radiotap_header hdr; u8 rate; u8 txpower; u8 rts_retries; u8 data_retries; } __packed; #define TX_RADIOTAP_PRESENT ( \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ 0) #define IEEE80211_FC_VERSION_MASK 0x0003 #define IEEE80211_FC_TYPE_MASK 0x000c #define IEEE80211_FC_TYPE_MGT 0x0000 #define IEEE80211_FC_TYPE_CTL 0x0004 #define IEEE80211_FC_TYPE_DATA 0x0008 #define IEEE80211_FC_SUBTYPE_MASK 0x00f0 #define IEEE80211_FC_TOFROMDS_MASK 0x0300 #define IEEE80211_FC_TODS_MASK 0x0100 #define IEEE80211_FC_FROMDS_MASK 0x0200 #define IEEE80211_FC_NODS 0x0000 #define IEEE80211_FC_TODS 0x0100 #define IEEE80211_FC_FROMDS 0x0200 #define IEEE80211_FC_DSTODS 0x0300 struct rx_radiotap_hdr { struct ieee80211_radiotap_header hdr; u8 flags; u8 rate; u8 antsignal; } __packed; #define RX_RADIOTAP_PRESENT ( \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ 0) compat-drivers-2012-09-18/drivers/net/wireless/libertas/mesh.h0000644000175000017500000000325212026211315023375 0ustar mcgrofmcgrof/* * Contains all definitions needed for the Libertas' MESH implementation. */ #ifndef _LBS_MESH_H_ #define _LBS_MESH_H_ #include #include #include "host.h" #include "dev.h" #ifdef CONFIG_LIBERTAS_MESH struct net_device; int lbs_init_mesh(struct lbs_private *priv); void lbs_start_mesh(struct lbs_private *priv); int lbs_deinit_mesh(struct lbs_private *priv); void lbs_remove_mesh(struct lbs_private *priv); static inline bool lbs_mesh_activated(struct lbs_private *priv) { /* Mesh SSID is only programmed after successful init */ return priv->mesh_ssid_len != 0; } int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel); /* Sending / Receiving */ struct rxpd; struct txpd; struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, struct net_device *dev, struct rxpd *rxpd); void lbs_mesh_set_txpd(struct lbs_private *priv, struct net_device *dev, struct txpd *txpd); /* Command handling */ struct cmd_ds_command; struct cmd_ds_mesh_access; struct cmd_ds_mesh_config; /* Ethtool statistics */ struct ethtool_stats; void lbs_mesh_ethtool_get_stats(struct net_device *dev, struct ethtool_stats *stats, uint64_t *data); int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset); void lbs_mesh_ethtool_get_strings(struct net_device *dev, uint32_t stringset, uint8_t *s); #else #define lbs_init_mesh(priv) #define lbs_deinit_mesh(priv) #define lbs_start_mesh(priv) #define lbs_add_mesh(priv) #define lbs_remove_mesh(priv) #define lbs_mesh_set_dev(priv, dev, rxpd) (dev) #define lbs_mesh_set_txpd(priv, dev, txpd) #define lbs_mesh_set_channel(priv, channel) (0) #define lbs_mesh_activated(priv) (false) #endif #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/if_usb.h0000644000175000017500000000353612026211315023715 0ustar mcgrofmcgrof#ifndef _LBS_IF_USB_H #define _LBS_IF_USB_H #include #include struct lbs_private; /* * This file contains definition for USB interface. */ #define CMD_TYPE_REQUEST 0xF00DFACE #define CMD_TYPE_DATA 0xBEADC0DE #define CMD_TYPE_INDICATION 0xBEEFFACE #define IPFIELD_ALIGN_OFFSET 2 #define BOOT_CMD_FW_BY_USB 0x01 #define BOOT_CMD_FW_IN_EEPROM 0x02 #define BOOT_CMD_UPDATE_BOOT2 0x03 #define BOOT_CMD_UPDATE_FW 0x04 #define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ struct bootcmd { __le32 magic; uint8_t cmd; uint8_t pad[11]; }; #define BOOT_CMD_RESP_OK 0x0001 #define BOOT_CMD_RESP_FAIL 0x0000 #define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002 struct bootcmdresp { __le32 magic; uint8_t cmd; uint8_t result; uint8_t pad[2]; }; /* USB card description structure*/ struct if_usb_card { struct usb_device *udev; uint32_t model; /* MODEL_* */ struct urb *rx_urb, *tx_urb; struct lbs_private *priv; struct sk_buff *rx_skb; uint8_t ep_in; uint8_t ep_out; /* bootcmdresp == 0 means command is pending * bootcmdresp < 0 means error * bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware */ int8_t bootcmdresp; int ep_in_size; void *ep_out_buf; int ep_out_size; const struct firmware *fw; struct timer_list fw_timeout; wait_queue_head_t fw_wq; uint32_t fwseqnum; uint32_t totalbytes; uint32_t fwlastblksent; uint8_t CRC_OK; uint8_t fwdnldover; uint8_t fwfinalblk; uint8_t surprise_removed; __le16 boot2_version; }; /* fwheader */ struct fwheader { __le32 dnldcmd; __le32 baseaddr; __le32 datalength; __le32 CRC; }; #define FW_MAX_DATA_BLK_SIZE 600 /* FWData */ struct fwdata { struct fwheader hdr; __le32 seqnum; uint8_t data[0]; }; /* fwsyncheader */ struct fwsyncheader { __le32 cmd; __le32 seqnum; }; #define FW_HAS_DATA_TO_RECV 0x00000001 #define FW_HAS_LAST_BLOCK 0x00000004 #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/if_spi.h0000644000175000017500000002155612026211315023721 0ustar mcgrofmcgrof/* * linux/drivers/net/wireless/libertas/if_spi.c * * Driver for Marvell SPI WLAN cards. * * Copyright 2008 Analog Devices Inc. * * Authors: * Andrey Yurovsky * Colin McCabe * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #ifndef _LBS_IF_SPI_H_ #define _LBS_IF_SPI_H_ #define IPFIELD_ALIGN_OFFSET 2 #define IF_SPI_CMD_BUF_SIZE 2400 /***************** Firmware *****************/ #define IF_SPI_FW_NAME_MAX 30 #define MAX_MAIN_FW_LOAD_CRC_ERR 10 /* Chunk size when loading the helper firmware */ #define HELPER_FW_LOAD_CHUNK_SZ 64 /* Value to write to indicate end of helper firmware dnld */ #define FIRMWARE_DNLD_OK 0x0000 /* Value to check once the main firmware is downloaded */ #define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888 /***************** SPI Interface Unit *****************/ /* Masks used in SPI register read/write operations */ #define IF_SPI_READ_OPERATION_MASK 0x0 #define IF_SPI_WRITE_OPERATION_MASK 0x8000 /* SPI register offsets. 4-byte aligned. */ #define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */ #define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */ #define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */ #define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */ #define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */ #define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */ #define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */ #define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */ #define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */ #define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */ #define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */ #define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */ #define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */ #define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */ #define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */ #define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */ #define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */ #define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */ #define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */ #define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */ #define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */ #define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */ #define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */ #define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */ #define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */ #define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */ #define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */ #define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */ #define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */ /***************** IF_SPI_DEVICEID_CTRL_REG *****************/ #define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16) #define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff) /***************** IF_SPI_HOST_INT_CTRL_REG *****************/ /* Host Interrupt Control bit : Wake up */ #define IF_SPI_HICT_WAKE_UP (1<<0) /* Host Interrupt Control bit : WLAN ready */ #define IF_SPI_HICT_WLAN_READY (1<<1) /*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */ /*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */ /*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */ /* Host Interrupt Control bit : Tx auto download */ #define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5) /* Host Interrupt Control bit : Rx auto upload */ #define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6) /* Host Interrupt Control bit : Command auto download */ #define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7) /* Host Interrupt Control bit : Command auto upload */ #define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8) /***************** IF_SPI_CARD_INT_CAUSE_REG *****************/ /* Card Interrupt Case bit : Tx download over */ #define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0) /* Card Interrupt Case bit : Rx upload over */ #define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1) /* Card Interrupt Case bit : Command download over */ #define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2) /* Card Interrupt Case bit : Host event */ #define IF_SPI_CIC_HOST_EVENT (1<<3) /* Card Interrupt Case bit : Command upload over */ #define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4) /* Card Interrupt Case bit : Power down */ #define IF_SPI_CIC_POWER_DOWN (1<<5) /***************** IF_SPI_CARD_INT_STATUS_REG *****************/ #define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0) #define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1) #define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2) #define IF_SPI_CIS_HOST_EVENT (1<<3) #define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4) #define IF_SPI_CIS_POWER_DOWN (1<<5) /***************** IF_SPI_HOST_INT_CAUSE_REG *****************/ #define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0) #define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1) #define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2) #define IF_SPI_HICU_CARD_EVENT (1<<3) #define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4) #define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5) #define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6) #define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7) #define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8) #define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9) #define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10) /***************** IF_SPI_HOST_INT_STATUS_REG *****************/ /* Host Interrupt Status bit : Tx download ready */ #define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0) /* Host Interrupt Status bit : Rx upload ready */ #define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1) /* Host Interrupt Status bit : Command download ready */ #define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2) /* Host Interrupt Status bit : Card event */ #define IF_SPI_HIST_CARD_EVENT (1<<3) /* Host Interrupt Status bit : Command upload ready */ #define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4) /* Host Interrupt Status bit : I/O write FIFO overflow */ #define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5) /* Host Interrupt Status bit : I/O read FIFO underflow */ #define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6) /* Host Interrupt Status bit : Data write FIFO overflow */ #define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7) /* Host Interrupt Status bit : Data read FIFO underflow */ #define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8) /* Host Interrupt Status bit : Command write FIFO overflow */ #define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9) /* Host Interrupt Status bit : Command read FIFO underflow */ #define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10) /***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/ /* Host Interrupt Status Mask bit : Tx download ready */ #define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0) /* Host Interrupt Status Mask bit : Rx upload ready */ #define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1) /* Host Interrupt Status Mask bit : Command download ready */ #define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2) /* Host Interrupt Status Mask bit : Card event */ #define IF_SPI_HISM_CARDEVENT (1<<3) /* Host Interrupt Status Mask bit : Command upload ready */ #define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4) /* Host Interrupt Status Mask bit : I/O write FIFO overflow */ #define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5) /* Host Interrupt Status Mask bit : I/O read FIFO underflow */ #define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6) /* Host Interrupt Status Mask bit : Data write FIFO overflow */ #define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7) /* Host Interrupt Status Mask bit : Data write FIFO underflow */ #define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8) /* Host Interrupt Status Mask bit : Command write FIFO overflow */ #define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9) /* Host Interrupt Status Mask bit : Command write FIFO underflow */ #define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10) /***************** IF_SPI_SPU_BUS_MODE_REG *****************/ /* SCK edge on which the WLAN module outputs data on MISO */ #define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8 #define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0 /* In a SPU read operation, there is a delay between writing the SPU * register name and getting back data from the WLAN module. * This can be specified in terms of nanoseconds or in terms of dummy * clock cycles which the master must output before receiving a response. */ #define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4 #define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0 /* Some different modes of SPI operation */ #define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00 #define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01 #define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02 #define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03 #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/if_sdio.h0000644000175000017500000000250312026211315024053 0ustar mcgrofmcgrof/* * linux/drivers/net/wireless/libertas/if_sdio.h * * Copyright 2007 Pierre Ossman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #ifndef _LBS_IF_SDIO_H #define _LBS_IF_SDIO_H #define IF_SDIO_IOPORT 0x00 #define IF_SDIO_H_INT_MASK 0x04 #define IF_SDIO_H_INT_OFLOW 0x08 #define IF_SDIO_H_INT_UFLOW 0x04 #define IF_SDIO_H_INT_DNLD 0x02 #define IF_SDIO_H_INT_UPLD 0x01 #define IF_SDIO_H_INT_STATUS 0x05 #define IF_SDIO_H_INT_RSR 0x06 #define IF_SDIO_H_INT_STATUS2 0x07 #define IF_SDIO_RD_BASE 0x10 #define IF_SDIO_STATUS 0x20 #define IF_SDIO_IO_RDY 0x08 #define IF_SDIO_CIS_RDY 0x04 #define IF_SDIO_UL_RDY 0x02 #define IF_SDIO_DL_RDY 0x01 #define IF_SDIO_C_INT_MASK 0x24 #define IF_SDIO_C_INT_STATUS 0x28 #define IF_SDIO_C_INT_RSR 0x2C #define IF_SDIO_SCRATCH 0x34 #define IF_SDIO_SCRATCH_OLD 0x80fe #define IF_SDIO_FW_STATUS 0x40 #define IF_SDIO_FIRMWARE_OK 0xfedc #define IF_SDIO_RX_LEN 0x42 #define IF_SDIO_RX_UNIT 0x43 #define IF_SDIO_EVENT 0x80fc #define IF_SDIO_BLOCK_SIZE 256 #define CONFIGURATION_REG 0x03 #define HOST_POWER_UP (0x1U << 1) #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/host.h0000644000175000017500000005710012026211315023417 0ustar mcgrofmcgrof/* * This file function prototypes, data structure * and definitions for all the host/station commands */ #ifndef _LBS_HOST_H_ #define _LBS_HOST_H_ #include "types.h" #include "defs.h" #define DEFAULT_AD_HOC_CHANNEL 6 #define CMD_OPTION_WAITFORRSP 0x0002 /* Host command IDs */ /* * Return command are almost always the same as the host command, but with * bit 15 set high. There are a few exceptions, though... */ #define CMD_RET(cmd) (0x8000 | cmd) /* Return command convention exceptions: */ #define CMD_RET_802_11_ASSOCIATE 0x8012 /* Command codes */ #define CMD_GET_HW_SPEC 0x0003 #define CMD_EEPROM_UPDATE 0x0004 #define CMD_802_11_RESET 0x0005 #define CMD_802_11_SCAN 0x0006 #define CMD_802_11_GET_LOG 0x000b #define CMD_MAC_MULTICAST_ADR 0x0010 #define CMD_802_11_AUTHENTICATE 0x0011 #define CMD_802_11_EEPROM_ACCESS 0x0059 #define CMD_802_11_ASSOCIATE 0x0050 #define CMD_802_11_SET_WEP 0x0013 #define CMD_802_11_GET_STAT 0x0014 #define CMD_802_3_GET_STAT 0x0015 #define CMD_802_11_SNMP_MIB 0x0016 #define CMD_MAC_REG_MAP 0x0017 #define CMD_BBP_REG_MAP 0x0018 #define CMD_MAC_REG_ACCESS 0x0019 #define CMD_BBP_REG_ACCESS 0x001a #define CMD_RF_REG_ACCESS 0x001b #define CMD_802_11_RADIO_CONTROL 0x001c #define CMD_802_11_RF_CHANNEL 0x001d #define CMD_802_11_RF_TX_POWER 0x001e #define CMD_802_11_RSSI 0x001f #define CMD_802_11_RF_ANTENNA 0x0020 #define CMD_802_11_PS_MODE 0x0021 #define CMD_802_11_DATA_RATE 0x0022 #define CMD_RF_REG_MAP 0x0023 #define CMD_802_11_DEAUTHENTICATE 0x0024 #define CMD_802_11_REASSOCIATE 0x0025 #define CMD_MAC_CONTROL 0x0028 #define CMD_802_11_AD_HOC_START 0x002b #define CMD_802_11_AD_HOC_JOIN 0x002c #define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e #define CMD_802_11_ENABLE_RSN 0x002f #define CMD_802_11_SET_AFC 0x003c #define CMD_802_11_GET_AFC 0x003d #define CMD_802_11_DEEP_SLEEP 0x003e #define CMD_802_11_AD_HOC_STOP 0x0040 #define CMD_802_11_HOST_SLEEP_CFG 0x0043 #define CMD_802_11_WAKEUP_CONFIRM 0x0044 #define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045 #define CMD_802_11_BEACON_STOP 0x0049 #define CMD_802_11_MAC_ADDRESS 0x004d #define CMD_802_11_LED_GPIO_CTRL 0x004e #define CMD_802_11_BAND_CONFIG 0x0058 #define CMD_GSPI_BUS_CONFIG 0x005a #define CMD_802_11D_DOMAIN_INFO 0x005b #define CMD_802_11_KEY_MATERIAL 0x005e #define CMD_802_11_SLEEP_PARAMS 0x0066 #define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 #define CMD_802_11_SLEEP_PERIOD 0x0068 #define CMD_802_11_TPC_CFG 0x0072 #define CMD_802_11_PA_CFG 0x0073 #define CMD_802_11_FW_WAKE_METHOD 0x0074 #define CMD_802_11_SUBSCRIBE_EVENT 0x0075 #define CMD_802_11_RATE_ADAPT_RATESET 0x0076 #define CMD_802_11_TX_RATE_QUERY 0x007f #define CMD_GET_TSF 0x0080 #define CMD_BT_ACCESS 0x0087 #define CMD_FWT_ACCESS 0x0095 #define CMD_802_11_MONITOR_MODE 0x0098 #define CMD_MESH_ACCESS 0x009b #define CMD_MESH_CONFIG_OLD 0x00a3 #define CMD_MESH_CONFIG 0x00ac #define CMD_SET_BOOT2_VER 0x00a5 #define CMD_FUNC_INIT 0x00a9 #define CMD_FUNC_SHUTDOWN 0x00aa #define CMD_802_11_BEACON_CTRL 0x00b0 /* For the IEEE Power Save */ #define PS_MODE_ACTION_ENTER_PS 0x0030 #define PS_MODE_ACTION_EXIT_PS 0x0031 #define PS_MODE_ACTION_SLEEP_CONFIRMED 0x0034 #define CMD_ENABLE_RSN 0x0001 #define CMD_DISABLE_RSN 0x0000 #define CMD_ACT_GET 0x0000 #define CMD_ACT_SET 0x0001 /* Define action or option for CMD_802_11_SET_WEP */ #define CMD_ACT_ADD 0x0002 #define CMD_ACT_REMOVE 0x0004 #define CMD_TYPE_WEP_40_BIT 0x01 #define CMD_TYPE_WEP_104_BIT 0x02 #define CMD_NUM_OF_WEP_KEYS 4 #define CMD_WEP_KEY_INDEX_MASK 0x3fff /* Define action or option for CMD_802_11_SCAN */ #define CMD_BSS_TYPE_BSS 0x0001 #define CMD_BSS_TYPE_IBSS 0x0002 #define CMD_BSS_TYPE_ANY 0x0003 /* Define action or option for CMD_802_11_SCAN */ #define CMD_SCAN_TYPE_ACTIVE 0x0000 #define CMD_SCAN_TYPE_PASSIVE 0x0001 #define CMD_SCAN_RADIO_TYPE_BG 0 #define CMD_SCAN_PROBE_DELAY_TIME 0 /* Define action or option for CMD_MAC_CONTROL */ #define CMD_ACT_MAC_RX_ON 0x0001 #define CMD_ACT_MAC_TX_ON 0x0002 #define CMD_ACT_MAC_LOOPBACK_ON 0x0004 #define CMD_ACT_MAC_WEP_ENABLE 0x0008 #define CMD_ACT_MAC_INT_ENABLE 0x0010 #define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 #define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 #define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 #define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 #define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 /* Event flags for CMD_802_11_SUBSCRIBE_EVENT */ #define CMD_SUBSCRIBE_RSSI_LOW 0x0001 #define CMD_SUBSCRIBE_SNR_LOW 0x0002 #define CMD_SUBSCRIBE_FAILCOUNT 0x0004 #define CMD_SUBSCRIBE_BCNMISS 0x0008 #define CMD_SUBSCRIBE_RSSI_HIGH 0x0010 #define CMD_SUBSCRIBE_SNR_HIGH 0x0020 #define RADIO_PREAMBLE_LONG 0x00 #define RADIO_PREAMBLE_SHORT 0x02 #define RADIO_PREAMBLE_AUTO 0x04 /* Define action or option for CMD_802_11_RF_CHANNEL */ #define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 #define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 /* Define action or option for CMD_802_11_DATA_RATE */ #define CMD_ACT_SET_TX_AUTO 0x0000 #define CMD_ACT_SET_TX_FIX_RATE 0x0001 #define CMD_ACT_GET_TX_RATE 0x0002 /* Options for CMD_802_11_FW_WAKE_METHOD */ #define CMD_WAKE_METHOD_UNCHANGED 0x0000 #define CMD_WAKE_METHOD_COMMAND_INT 0x0001 #define CMD_WAKE_METHOD_GPIO 0x0002 /* Object IDs for CMD_802_11_SNMP_MIB */ #define SNMP_MIB_OID_BSS_TYPE 0x0000 #define SNMP_MIB_OID_OP_RATE_SET 0x0001 #define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */ #define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */ #define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */ #define SNMP_MIB_OID_RTS_THRESHOLD 0x0005 #define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006 #define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007 #define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008 #define SNMP_MIB_OID_11D_ENABLE 0x0009 #define SNMP_MIB_OID_11H_ENABLE 0x000A /* Define action or option for CMD_BT_ACCESS */ enum cmd_bt_access_opts { /* The bt commands start at 5 instead of 1 because the old dft commands * are mapped to 1-4. These old commands are no longer maintained and * should not be called. */ CMD_ACT_BT_ACCESS_ADD = 5, CMD_ACT_BT_ACCESS_DEL, CMD_ACT_BT_ACCESS_LIST, CMD_ACT_BT_ACCESS_RESET, CMD_ACT_BT_ACCESS_SET_INVERT, CMD_ACT_BT_ACCESS_GET_INVERT }; /* Define action or option for CMD_FWT_ACCESS */ enum cmd_fwt_access_opts { CMD_ACT_FWT_ACCESS_ADD = 1, CMD_ACT_FWT_ACCESS_DEL, CMD_ACT_FWT_ACCESS_LOOKUP, CMD_ACT_FWT_ACCESS_LIST, CMD_ACT_FWT_ACCESS_LIST_ROUTE, CMD_ACT_FWT_ACCESS_LIST_NEIGHBOR, CMD_ACT_FWT_ACCESS_RESET, CMD_ACT_FWT_ACCESS_CLEANUP, CMD_ACT_FWT_ACCESS_TIME, }; /* Define action or option for CMD_802_11_HOST_SLEEP_CFG */ enum cmd_wol_cfg_opts { CMD_ACT_ACTION_NONE = 0, CMD_ACT_SET_WOL_RULE, CMD_ACT_GET_WOL_RULE, CMD_ACT_RESET_WOL_RULE, }; /* Define action or option for CMD_MESH_ACCESS */ enum cmd_mesh_access_opts { CMD_ACT_MESH_GET_TTL = 1, CMD_ACT_MESH_SET_TTL, CMD_ACT_MESH_GET_STATS, CMD_ACT_MESH_GET_ANYCAST, CMD_ACT_MESH_SET_ANYCAST, CMD_ACT_MESH_SET_LINK_COSTS, CMD_ACT_MESH_GET_LINK_COSTS, CMD_ACT_MESH_SET_BCAST_RATE, CMD_ACT_MESH_GET_BCAST_RATE, CMD_ACT_MESH_SET_RREQ_DELAY, CMD_ACT_MESH_GET_RREQ_DELAY, CMD_ACT_MESH_SET_ROUTE_EXP, CMD_ACT_MESH_GET_ROUTE_EXP, CMD_ACT_MESH_SET_AUTOSTART_ENABLED, CMD_ACT_MESH_GET_AUTOSTART_ENABLED, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT = 17, }; /* Define actions and types for CMD_MESH_CONFIG */ enum cmd_mesh_config_actions { CMD_ACT_MESH_CONFIG_STOP = 0, CMD_ACT_MESH_CONFIG_START, CMD_ACT_MESH_CONFIG_SET, CMD_ACT_MESH_CONFIG_GET, }; enum cmd_mesh_config_types { CMD_TYPE_MESH_SET_BOOTFLAG = 1, CMD_TYPE_MESH_SET_BOOTTIME, CMD_TYPE_MESH_SET_DEF_CHANNEL, CMD_TYPE_MESH_SET_MESH_IE, CMD_TYPE_MESH_GET_DEFAULTS, CMD_TYPE_MESH_GET_MESH_IE, /* GET_DEFAULTS is superset of GET_MESHIE */ }; /* Card Event definition */ #define MACREG_INT_CODE_TX_PPA_FREE 0 #define MACREG_INT_CODE_TX_DMA_DONE 1 #define MACREG_INT_CODE_LINK_LOST_W_SCAN 2 #define MACREG_INT_CODE_LINK_LOST_NO_SCAN 3 #define MACREG_INT_CODE_LINK_SENSED 4 #define MACREG_INT_CODE_CMD_FINISHED 5 #define MACREG_INT_CODE_MIB_CHANGED 6 #define MACREG_INT_CODE_INIT_DONE 7 #define MACREG_INT_CODE_DEAUTHENTICATED 8 #define MACREG_INT_CODE_DISASSOCIATED 9 #define MACREG_INT_CODE_PS_AWAKE 10 #define MACREG_INT_CODE_PS_SLEEP 11 #define MACREG_INT_CODE_MIC_ERR_MULTICAST 13 #define MACREG_INT_CODE_MIC_ERR_UNICAST 14 #define MACREG_INT_CODE_WM_AWAKE 15 #define MACREG_INT_CODE_DEEP_SLEEP_AWAKE 16 #define MACREG_INT_CODE_ADHOC_BCN_LOST 17 #define MACREG_INT_CODE_HOST_AWAKE 18 #define MACREG_INT_CODE_STOP_TX 19 #define MACREG_INT_CODE_START_TX 20 #define MACREG_INT_CODE_CHANNEL_SWITCH 21 #define MACREG_INT_CODE_MEASUREMENT_RDY 22 #define MACREG_INT_CODE_WMM_CHANGE 23 #define MACREG_INT_CODE_BG_SCAN_REPORT 24 #define MACREG_INT_CODE_RSSI_LOW 25 #define MACREG_INT_CODE_SNR_LOW 26 #define MACREG_INT_CODE_MAX_FAIL 27 #define MACREG_INT_CODE_RSSI_HIGH 28 #define MACREG_INT_CODE_SNR_HIGH 29 #define MACREG_INT_CODE_MESH_AUTO_STARTED 35 #define MACREG_INT_CODE_FIRMWARE_READY 48 /* 802.11-related definitions */ /* TxPD descriptor */ struct txpd { /* union to cope up with later FW revisions */ union { /* Current Tx packet status */ __le32 tx_status; struct { /* BSS type: client, AP, etc. */ u8 bss_type; /* BSS number */ u8 bss_num; /* Reserved */ __le16 reserved; } bss; } u; /* Tx control */ __le32 tx_control; __le32 tx_packet_location; /* Tx packet length */ __le16 tx_packet_length; /* First 2 byte of destination MAC address */ u8 tx_dest_addr_high[2]; /* Last 4 byte of destination MAC address */ u8 tx_dest_addr_low[4]; /* Pkt Priority */ u8 priority; /* Pkt Trasnit Power control */ u8 powermgmt; /* Amount of time the packet has been queued (units = 2ms) */ u8 pktdelay_2ms; /* reserved */ u8 reserved1; } __packed; /* RxPD Descriptor */ struct rxpd { /* union to cope up with later FW revisions */ union { /* Current Rx packet status */ __le16 status; struct { /* BSS type: client, AP, etc. */ u8 bss_type; /* BSS number */ u8 bss_num; } __packed bss; } __packed u; /* SNR */ u8 snr; /* Tx control */ u8 rx_control; /* Pkt length */ __le16 pkt_len; /* Noise Floor */ u8 nf; /* Rx Packet Rate */ u8 rx_rate; /* Pkt addr */ __le32 pkt_ptr; /* Next Rx RxPD addr */ __le32 next_rxpd_ptr; /* Pkt Priority */ u8 priority; u8 reserved[3]; } __packed; struct cmd_header { __le16 command; __le16 size; __le16 seqnum; __le16 result; } __packed; /* Generic structure to hold all key types. */ struct enc_key { u16 len; u16 flags; /* KEY_INFO_* from defs.h */ u16 type; /* KEY_TYPE_* from defs.h */ u8 key[32]; }; /* lbs_offset_value */ struct lbs_offset_value { u32 offset; u32 value; } __packed; #define MAX_11D_TRIPLETS 83 struct mrvl_ie_domain_param_set { struct mrvl_ie_header header; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; struct ieee80211_country_ie_triplet triplet[MAX_11D_TRIPLETS]; } __packed; struct cmd_ds_802_11d_domain_info { struct cmd_header hdr; __le16 action; struct mrvl_ie_domain_param_set domain; } __packed; /* * Define data structure for CMD_GET_HW_SPEC * This structure defines the response for the GET_HW_SPEC command */ struct cmd_ds_get_hw_spec { struct cmd_header hdr; /* HW Interface version number */ __le16 hwifversion; /* HW version number */ __le16 version; /* Max number of TxPD FW can handle */ __le16 nr_txpd; /* Max no of Multicast address */ __le16 nr_mcast_adr; /* MAC address */ u8 permanentaddr[6]; /* region Code */ __le16 regioncode; /* Number of antenna used */ __le16 nr_antenna; /* FW release number, example 0x01030304 = 2.3.4p1 */ __le32 fwrelease; /* Base Address of TxPD queue */ __le32 wcb_base; /* Read Pointer of RxPd queue */ __le32 rxpd_rdptr; /* Write Pointer of RxPd queue */ __le32 rxpd_wrptr; /*FW/HW capability */ __le32 fwcapinfo; } __packed; struct cmd_ds_802_11_subscribe_event { struct cmd_header hdr; __le16 action; __le16 events; /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a * number of TLVs. From the v5.1 manual, those TLVs would add up to * 40 bytes. However, future firmware might add additional TLVs, so I * bump this up a bit. */ uint8_t tlv[128]; } __packed; /* * This scan handle Country Information IE(802.11d compliant) * Define data structure for CMD_802_11_SCAN */ struct cmd_ds_802_11_scan { struct cmd_header hdr; uint8_t bsstype; uint8_t bssid[ETH_ALEN]; uint8_t tlvbuffer[0]; } __packed; struct cmd_ds_802_11_scan_rsp { struct cmd_header hdr; __le16 bssdescriptsize; uint8_t nr_sets; uint8_t bssdesc_and_tlvbuffer[0]; } __packed; struct cmd_ds_802_11_get_log { struct cmd_header hdr; __le32 mcasttxframe; __le32 failed; __le32 retry; __le32 multiretry; __le32 framedup; __le32 rtssuccess; __le32 rtsfailure; __le32 ackfailure; __le32 rxfrag; __le32 mcastrxframe; __le32 fcserror; __le32 txframe; __le32 wepundecryptable; } __packed; struct cmd_ds_mac_control { struct cmd_header hdr; __le16 action; u16 reserved; } __packed; struct cmd_ds_mac_multicast_adr { struct cmd_header hdr; __le16 action; __le16 nr_of_adrs; u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; } __packed; struct cmd_ds_802_11_authenticate { struct cmd_header hdr; u8 bssid[ETH_ALEN]; u8 authtype; u8 reserved[10]; } __packed; struct cmd_ds_802_11_deauthenticate { struct cmd_header hdr; u8 macaddr[ETH_ALEN]; __le16 reasoncode; } __packed; struct cmd_ds_802_11_associate { struct cmd_header hdr; u8 bssid[6]; __le16 capability; __le16 listeninterval; __le16 bcnperiod; u8 dtimperiod; u8 iebuf[512]; /* Enough for required and most optional IEs */ } __packed; struct cmd_ds_802_11_associate_response { struct cmd_header hdr; __le16 capability; __le16 statuscode; __le16 aid; u8 iebuf[512]; } __packed; struct cmd_ds_802_11_set_wep { struct cmd_header hdr; /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ __le16 action; /* key Index selected for Tx */ __le16 keyindex; /* 40, 128bit or TXWEP */ uint8_t keytype[4]; uint8_t keymaterial[4][16]; } __packed; struct cmd_ds_802_11_snmp_mib { struct cmd_header hdr; __le16 action; __le16 oid; __le16 bufsize; u8 value[128]; } __packed; struct cmd_ds_reg_access { struct cmd_header hdr; __le16 action; __le16 offset; union { u8 bbp_rf; /* for BBP and RF registers */ __le32 mac; /* for MAC registers */ } value; } __packed; struct cmd_ds_802_11_radio_control { struct cmd_header hdr; __le16 action; __le16 control; } __packed; struct cmd_ds_802_11_beacon_control { struct cmd_header hdr; __le16 action; __le16 beacon_enable; __le16 beacon_period; } __packed; struct cmd_ds_802_11_sleep_params { struct cmd_header hdr; /* ACT_GET/ACT_SET */ __le16 action; /* Sleep clock error in ppm */ __le16 error; /* Wakeup offset in usec */ __le16 offset; /* Clock stabilization time in usec */ __le16 stabletime; /* control periodic calibration */ uint8_t calcontrol; /* control the use of external sleep clock */ uint8_t externalsleepclk; /* reserved field, should be set to zero */ __le16 reserved; } __packed; struct cmd_ds_802_11_rf_channel { struct cmd_header hdr; __le16 action; __le16 channel; __le16 rftype; /* unused */ __le16 reserved; /* unused */ u8 channellist[32]; /* unused */ } __packed; struct cmd_ds_802_11_rssi { struct cmd_header hdr; /* * request: number of beacons (N) to average the SNR and NF over * response: SNR of most recent beacon */ __le16 n_or_snr; /* * The following fields are only set in the response. * In the request these are reserved and should be set to 0. */ __le16 nf; /* most recent beacon noise floor */ __le16 avg_snr; /* average SNR weighted by N from request */ __le16 avg_nf; /* average noise floor weighted by N from request */ } __packed; struct cmd_ds_802_11_mac_address { struct cmd_header hdr; __le16 action; u8 macadd[ETH_ALEN]; } __packed; struct cmd_ds_802_11_rf_tx_power { struct cmd_header hdr; __le16 action; __le16 curlevel; s8 maxlevel; s8 minlevel; } __packed; /* MONITOR_MODE only exists in OLPC v5 firmware */ struct cmd_ds_802_11_monitor_mode { struct cmd_header hdr; __le16 action; __le16 mode; } __packed; struct cmd_ds_set_boot2_ver { struct cmd_header hdr; __le16 action; __le16 version; } __packed; struct cmd_ds_802_11_fw_wake_method { struct cmd_header hdr; __le16 action; __le16 method; } __packed; struct cmd_ds_802_11_ps_mode { struct cmd_header hdr; __le16 action; /* * Interval for keepalive in PS mode: * 0x0000 = don't change * 0x001E = firmware default * 0xFFFF = disable */ __le16 nullpktinterval; /* * Number of DTIM intervals to wake up for: * 0 = don't change * 1 = firmware default * 5 = max */ __le16 multipledtim; __le16 reserved; __le16 locallisteninterval; /* * AdHoc awake period (FW v9+ only): * 0 = don't change * 1 = always awake (IEEE standard behavior) * 2 - 31 = sleep for (n - 1) periods and awake for 1 period * 32 - 254 = invalid * 255 = sleep at each ATIM */ __le16 adhoc_awake_period; } __packed; struct cmd_confirm_sleep { struct cmd_header hdr; __le16 action; __le16 nullpktinterval; __le16 multipledtim; __le16 reserved; __le16 locallisteninterval; } __packed; struct cmd_ds_802_11_data_rate { struct cmd_header hdr; __le16 action; __le16 reserved; u8 rates[MAX_RATES]; } __packed; struct cmd_ds_802_11_rate_adapt_rateset { struct cmd_header hdr; __le16 action; __le16 enablehwauto; __le16 bitmap; } __packed; struct cmd_ds_802_11_ad_hoc_start { struct cmd_header hdr; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 bsstype; __le16 beaconperiod; u8 dtimperiod; /* Reserved on v9 and later */ struct ieee_ie_ibss_param_set ibss; u8 reserved1[4]; struct ieee_ie_ds_param_set ds; u8 reserved2[4]; __le16 probedelay; /* Reserved on v9 and later */ __le16 capability; u8 rates[MAX_RATES]; u8 tlv_memory_size_pad[100]; } __packed; struct cmd_ds_802_11_ad_hoc_result { struct cmd_header hdr; u8 pad[3]; u8 bssid[ETH_ALEN]; } __packed; struct adhoc_bssdesc { u8 bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 type; __le16 beaconperiod; u8 dtimperiod; __le64 timestamp; __le64 localtime; struct ieee_ie_ds_param_set ds; u8 reserved1[4]; struct ieee_ie_ibss_param_set ibss; u8 reserved2[4]; __le16 capability; u8 rates[MAX_RATES]; /* * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the * Adhoc join command and will cause a binary layout mismatch with * the firmware */ } __packed; struct cmd_ds_802_11_ad_hoc_join { struct cmd_header hdr; struct adhoc_bssdesc bss; __le16 failtimeout; /* Reserved on v9 and later */ __le16 probedelay; /* Reserved on v9 and later */ } __packed; struct cmd_ds_802_11_ad_hoc_stop { struct cmd_header hdr; } __packed; struct cmd_ds_802_11_enable_rsn { struct cmd_header hdr; __le16 action; __le16 enable; } __packed; struct MrvlIEtype_keyParamSet { /* type ID */ __le16 type; /* length of Payload */ __le16 length; /* type of key: WEP=0, TKIP=1, AES=2 */ __le16 keytypeid; /* key control Info specific to a keytypeid */ __le16 keyinfo; /* length of key */ __le16 keylen; /* key material of size keylen */ u8 key[32]; } __packed; #define MAX_WOL_RULES 16 struct host_wol_rule { uint8_t rule_no; uint8_t rule_ops; __le16 sig_offset; __le16 sig_length; __le16 reserve; __be32 sig_mask; __be32 signature; } __packed; struct wol_config { uint8_t action; uint8_t pattern; uint8_t no_rules_in_cmd; uint8_t result; struct host_wol_rule rule[MAX_WOL_RULES]; } __packed; struct cmd_ds_host_sleep { struct cmd_header hdr; __le32 criteria; uint8_t gpio; uint16_t gap; struct wol_config wol_conf; } __packed; struct cmd_ds_802_11_key_material { struct cmd_header hdr; __le16 action; struct MrvlIEtype_keyParamSet keyParamSet[2]; } __packed; struct cmd_ds_802_11_eeprom_access { struct cmd_header hdr; __le16 action; __le16 offset; __le16 len; /* firmware says it returns a maximum of 20 bytes */ #define LBS_EEPROM_READ_LEN 20 u8 value[LBS_EEPROM_READ_LEN]; } __packed; struct cmd_ds_802_11_tpc_cfg { struct cmd_header hdr; __le16 action; uint8_t enable; int8_t P0; int8_t P1; int8_t P2; uint8_t usesnr; } __packed; struct cmd_ds_802_11_pa_cfg { struct cmd_header hdr; __le16 action; uint8_t enable; int8_t P0; int8_t P1; int8_t P2; } __packed; struct cmd_ds_802_11_led_ctrl { struct cmd_header hdr; __le16 action; __le16 numled; u8 data[256]; } __packed; /* Automatic Frequency Control */ struct cmd_ds_802_11_afc { struct cmd_header hdr; __le16 afc_auto; union { struct { __le16 threshold; __le16 period; }; struct { __le16 timing_offset; /* signed */ __le16 carrier_offset; /* signed */ }; }; } __packed; struct cmd_tx_rate_query { __le16 txrate; } __packed; struct cmd_ds_get_tsf { __le64 tsfvalue; } __packed; struct cmd_ds_bt_access { struct cmd_header hdr; __le16 action; __le32 id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; } __packed; struct cmd_ds_fwt_access { struct cmd_header hdr; __le16 action; __le32 id; u8 valid; u8 da[ETH_ALEN]; u8 dir; u8 ra[ETH_ALEN]; __le32 ssn; __le32 dsn; __le32 metric; u8 rate; u8 hopcount; u8 ttl; __le32 expiration; u8 sleepmode; __le32 snr; __le32 references; u8 prec[ETH_ALEN]; } __packed; struct cmd_ds_mesh_config { struct cmd_header hdr; __le16 action; __le16 channel; __le16 type; __le16 length; u8 data[128]; /* last position reserved */ } __packed; struct cmd_ds_mesh_access { struct cmd_header hdr; __le16 action; __le32 data[32]; /* last position reserved */ } __packed; /* Number of stats counters returned by the firmware */ #define MESH_STATS_NUM 8 #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/firmware.c0000644000175000017500000001266512026211315024260 0ustar mcgrofmcgrof/* * Firmware loading and handling functions. */ #include #include #include #include "dev.h" #include "decl.h" static void load_next_firmware_from_table(struct lbs_private *private); static void lbs_fw_loaded(struct lbs_private *priv, int ret, const struct firmware *helper, const struct firmware *mainfw) { unsigned long flags; lbs_deb_fw("firmware load complete, code %d\n", ret); /* User must free helper/mainfw */ priv->fw_callback(priv, ret, helper, mainfw); spin_lock_irqsave(&priv->driver_lock, flags); priv->fw_callback = NULL; wake_up(&priv->fw_waitq); spin_unlock_irqrestore(&priv->driver_lock, flags); } static void do_load_firmware(struct lbs_private *priv, const char *name, void (*cb)(const struct firmware *fw, void *context)) { int ret; lbs_deb_fw("Requesting %s\n", name); ret = request_firmware_nowait(THIS_MODULE, true, name, priv->fw_device, GFP_KERNEL, priv, cb); if (ret) { lbs_deb_fw("request_firmware_nowait error %d\n", ret); lbs_fw_loaded(priv, ret, NULL, NULL); } } static void main_firmware_cb(const struct firmware *firmware, void *context) { struct lbs_private *priv = context; if (!firmware) { /* Failed to find firmware: try next table entry */ load_next_firmware_from_table(priv); return; } /* Firmware found! */ lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); } static void helper_firmware_cb(const struct firmware *firmware, void *context) { struct lbs_private *priv = context; if (!firmware) { /* Failed to find firmware: try next table entry */ load_next_firmware_from_table(priv); return; } /* Firmware found! */ if (priv->fw_iter->fwname) { priv->helper_fw = firmware; do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); } else { /* No main firmware needed for this helper --> success! */ lbs_fw_loaded(priv, 0, firmware, NULL); } } static void load_next_firmware_from_table(struct lbs_private *priv) { const struct lbs_fw_table *iter; if (!priv->fw_iter) iter = priv->fw_table; else iter = ++priv->fw_iter; if (priv->helper_fw) { release_firmware(priv->helper_fw); priv->helper_fw = NULL; } next: if (!iter->helper) { /* End of table hit. */ lbs_fw_loaded(priv, -ENOENT, NULL, NULL); return; } if (iter->model != priv->fw_model) { iter++; goto next; } priv->fw_iter = iter; do_load_firmware(priv, iter->helper, helper_firmware_cb); } void lbs_wait_for_firmware_load(struct lbs_private *priv) { wait_event(priv->fw_waitq, priv->fw_callback == NULL); } /** * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load * either a helper firmware and a main firmware (2-stage), or just the helper. * * @priv: Pointer to lbs_private instance * @dev: A pointer to &device structure * @card_model: Bus-specific card model ID used to filter firmware table * elements * @fw_table: Table of firmware file names and device model numbers * terminated by an entry with a NULL helper name * @callback: User callback to invoke when firmware load succeeds or fails. */ int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, u32 card_model, const struct lbs_fw_table *fw_table, lbs_fw_cb callback) { unsigned long flags; spin_lock_irqsave(&priv->driver_lock, flags); if (priv->fw_callback) { lbs_deb_fw("firmware load already in progress\n"); spin_unlock_irqrestore(&priv->driver_lock, flags); return -EBUSY; } priv->fw_device = device; priv->fw_callback = callback; priv->fw_table = fw_table; priv->fw_iter = NULL; priv->fw_model = card_model; spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_fw("Starting async firmware load\n"); load_next_firmware_from_table(priv); return 0; } EXPORT_SYMBOL_GPL(lbs_get_firmware_async); /** * lbs_get_firmware - Retrieves two-stage firmware * * @dev: A pointer to &device structure * @card_model: Bus-specific card model ID used to filter firmware table * elements * @fw_table: Table of firmware file names and device model numbers * terminated by an entry with a NULL helper name * @helper: On success, the helper firmware; caller must free * @mainfw: On success, the main firmware; caller must free * * Deprecated: use lbs_get_firmware_async() instead. * * returns: 0 on success, non-zero on failure */ int lbs_get_firmware(struct device *dev, u32 card_model, const struct lbs_fw_table *fw_table, const struct firmware **helper, const struct firmware **mainfw) { const struct lbs_fw_table *iter; int ret; BUG_ON(helper == NULL); BUG_ON(mainfw == NULL); /* Search for firmware to use from the table. */ iter = fw_table; while (iter && iter->helper) { if (iter->model != card_model) goto next; if (*helper == NULL) { ret = request_firmware(helper, iter->helper, dev); if (ret) goto next; /* If the device has one-stage firmware (ie cf8305) and * we've got it then we don't need to bother with the * main firmware. */ if (iter->fwname == NULL) return 0; } if (*mainfw == NULL) { ret = request_firmware(mainfw, iter->fwname, dev); if (ret) { /* Clear the helper to ensure we don't have * mismatched firmware pairs. */ release_firmware(*helper); *helper = NULL; } } if (*helper && *mainfw) return 0; next: iter++; } /* Failed */ release_firmware(*helper); *helper = NULL; release_firmware(*mainfw); *mainfw = NULL; return -ENOENT; } EXPORT_SYMBOL_GPL(lbs_get_firmware); compat-drivers-2012-09-18/drivers/net/wireless/libertas/ethtool.c0000644000175000017500000000645312026211315024120 0ustar mcgrofmcgrof#include #include #include #include #include "decl.h" #include "cmd.h" #include "mesh.h" static void lbs_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct lbs_private *priv = dev->ml_priv; snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u.%u.p%u", priv->fwrelease >> 24 & 0xff, priv->fwrelease >> 16 & 0xff, priv->fwrelease >> 8 & 0xff, priv->fwrelease & 0xff); strlcpy(info->driver, "libertas", sizeof(info->driver)); strlcpy(info->version, lbs_driver_version, sizeof(info->version)); } /* * All 8388 parts have 16KiB EEPROM size at the time of writing. * In case that changes this needs fixing. */ #define LBS_EEPROM_LEN 16384 static int lbs_ethtool_get_eeprom_len(struct net_device *dev) { return LBS_EEPROM_LEN; } static int lbs_ethtool_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { struct lbs_private *priv = dev->ml_priv; struct cmd_ds_802_11_eeprom_access cmd; int ret; lbs_deb_enter(LBS_DEB_ETHTOOL); if (eeprom->offset + eeprom->len > LBS_EEPROM_LEN || eeprom->len > LBS_EEPROM_READ_LEN) { ret = -EINVAL; goto out; } cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) - LBS_EEPROM_READ_LEN + eeprom->len); cmd.action = cpu_to_le16(CMD_ACT_GET); cmd.offset = cpu_to_le16(eeprom->offset); cmd.len = cpu_to_le16(eeprom->len); ret = lbs_cmd_with_response(priv, CMD_802_11_EEPROM_ACCESS, &cmd); if (!ret) memcpy(bytes, cmd.value, eeprom->len); out: lbs_deb_leave_args(LBS_DEB_ETHTOOL, "ret %d", ret); return ret; } static void lbs_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct lbs_private *priv = dev->ml_priv; wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; if (priv->wol_criteria == EHS_REMOVE_WAKEUP) return; if (priv->wol_criteria & EHS_WAKE_ON_UNICAST_DATA) wol->wolopts |= WAKE_UCAST; if (priv->wol_criteria & EHS_WAKE_ON_MULTICAST_DATA) wol->wolopts |= WAKE_MCAST; if (priv->wol_criteria & EHS_WAKE_ON_BROADCAST_DATA) wol->wolopts |= WAKE_BCAST; if (priv->wol_criteria & EHS_WAKE_ON_MAC_EVENT) wol->wolopts |= WAKE_PHY; } static int lbs_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct lbs_private *priv = dev->ml_priv; if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) return -EOPNOTSUPP; priv->wol_criteria = 0; if (wol->wolopts & WAKE_UCAST) priv->wol_criteria |= EHS_WAKE_ON_UNICAST_DATA; if (wol->wolopts & WAKE_MCAST) priv->wol_criteria |= EHS_WAKE_ON_MULTICAST_DATA; if (wol->wolopts & WAKE_BCAST) priv->wol_criteria |= EHS_WAKE_ON_BROADCAST_DATA; if (wol->wolopts & WAKE_PHY) priv->wol_criteria |= EHS_WAKE_ON_MAC_EVENT; if (wol->wolopts == 0) priv->wol_criteria |= EHS_REMOVE_WAKEUP; return 0; } const struct ethtool_ops lbs_ethtool_ops = { .get_drvinfo = lbs_ethtool_get_drvinfo, .get_eeprom = lbs_ethtool_get_eeprom, .get_eeprom_len = lbs_ethtool_get_eeprom_len, #ifdef CONFIG_LIBERTAS_MESH .get_sset_count = lbs_mesh_ethtool_get_sset_count, .get_ethtool_stats = lbs_mesh_ethtool_get_stats, .get_strings = lbs_mesh_ethtool_get_strings, #endif .get_wol = lbs_ethtool_get_wol, .set_wol = lbs_ethtool_set_wol, }; compat-drivers-2012-09-18/drivers/net/wireless/libertas/dev.h0000644000175000017500000001211612026211315023216 0ustar mcgrofmcgrof/* * This file contains definitions and data structures specific * to Marvell 802.11 NIC. It contains the Device Information * structure struct lbs_private.. */ #ifndef _LBS_DEV_H_ #define _LBS_DEV_H_ #include "defs.h" #include "decl.h" #include "host.h" #include /* sleep_params */ struct sleep_params { uint16_t sp_error; uint16_t sp_offset; uint16_t sp_stabletime; uint8_t sp_calcontrol; uint8_t sp_extsleepclk; uint16_t sp_reserved; }; /* Mesh statistics */ struct lbs_mesh_stats { u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ u32 fwd_drop_ttl; /* Fwd: TTL zero */ u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ u32 fwd_drop_noroute; /* Fwd: No route to Destination */ u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ u32 drop_blind; /* Rx: Dropped by blinding table */ u32 tx_failed_cnt; /* Tx: Failed transmissions */ }; /* Private structure for the MV device */ struct lbs_private { /* Basic networking */ struct net_device *dev; u32 connect_status; struct work_struct mcast_work; u32 nr_of_multicastmacaddr; u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; /* CFG80211 */ struct wireless_dev *wdev; bool wiphy_registered; struct cfg80211_scan_request *scan_req; u8 assoc_bss[ETH_ALEN]; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; u8 disassoc_reason; /* Mesh */ struct net_device *mesh_dev; /* Virtual device */ #ifdef CONFIG_LIBERTAS_MESH struct lbs_mesh_stats mstats; uint16_t mesh_tlv; u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 mesh_ssid_len; u8 mesh_channel; #endif /* Debugfs */ struct dentry *debugfs_dir; struct dentry *debugfs_debug; struct dentry *debugfs_files[6]; struct dentry *events_dir; struct dentry *debugfs_events_files[6]; struct dentry *regs_dir; struct dentry *debugfs_regs_files[6]; /* Hardware debugging */ u32 mac_offset; u32 bbp_offset; u32 rf_offset; /* Power management */ u16 psmode; u32 psstate; u8 needtowakeup; /* Deep sleep */ int is_deep_sleep; int deep_sleep_required; int is_auto_deep_sleep_enabled; int wakeup_dev_required; int is_activity_detected; int auto_deep_sleep_timeout; /* in ms */ wait_queue_head_t ds_awake_q; struct timer_list auto_deepsleep_timer; /* Host sleep*/ int is_host_sleep_configured; int is_host_sleep_activated; wait_queue_head_t host_sleep_q; /* Hardware access */ void *card; bool iface_running; u8 fw_ready; u8 surpriseremoved; u8 setup_fw_on_resume; int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); void (*reset_card) (struct lbs_private *priv); int (*power_save) (struct lbs_private *priv); int (*power_restore) (struct lbs_private *priv); int (*enter_deep_sleep) (struct lbs_private *priv); int (*exit_deep_sleep) (struct lbs_private *priv); int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); /* Adapter info (from EEPROM) */ u32 fwrelease; u32 fwcapinfo; u16 regioncode; u8 current_addr[ETH_ALEN]; u8 copied_hwaddr; /* Command download */ u8 dnld_sent; /* bit0 1/0=data_sent/data_tx_done, bit1 1/0=cmd_sent/cmd_tx_done, all other bits reserved 0 */ u16 seqnum; struct cmd_ctrl_node *cmd_array; struct cmd_ctrl_node *cur_cmd; struct list_head cmdfreeq; /* free command buffers */ struct list_head cmdpendingq; /* pending command buffers */ struct timer_list command_timer; int cmd_timed_out; /* Command responses sent from the hardware to the driver */ u8 resp_idx; u8 resp_buf[2][LBS_UPLD_SIZE]; u32 resp_len[2]; /* Events sent from hardware to driver */ struct kfifo event_fifo; /* thread to service interrupts */ struct task_struct *main_thread; wait_queue_head_t waitq; struct workqueue_struct *work_thread; /* Encryption stuff */ u8 authtype_auto; u8 wep_tx_key; u8 wep_key[4][WLAN_KEY_LEN_WEP104]; u8 wep_key_len[4]; /* Wake On LAN */ uint32_t wol_criteria; uint8_t wol_gpio; uint8_t wol_gap; bool ehs_remove_supported; /* Transmitting */ int tx_pending_len; /* -1 while building packet */ u8 tx_pending_buf[LBS_UPLD_SIZE]; /* protected by hard_start_xmit serialization */ u8 txretrycount; struct sk_buff *currenttxskb; struct timer_list tx_lockup_timer; /* Locks */ struct mutex lock; spinlock_t driver_lock; /* NIC/link operation characteristics */ u16 mac_control; u8 radio_on; u8 cur_rate; u8 channel; s16 txpower_cur; s16 txpower_min; s16 txpower_max; /* Scanning */ struct delayed_work scan_work; int scan_channel; /* Queue of things waiting for scan completion */ wait_queue_head_t scan_q; /* Whether the scan was initiated internally and not by cfg80211 */ bool internal_scan; /* Firmware load */ u32 fw_model; wait_queue_head_t fw_waitq; struct device *fw_device; const struct firmware *helper_fw; const struct lbs_fw_table *fw_table; const struct lbs_fw_table *fw_iter; lbs_fw_cb fw_callback; }; extern struct cmd_confirm_sleep confirm_sleep; /* Check if there is an interface active. */ static inline int lbs_iface_active(struct lbs_private *priv) { int r; r = netif_running(priv->dev); if (priv->mesh_dev) r |= netif_running(priv->mesh_dev); return r; } #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/decl.h0000644000175000017500000000447712026211315023362 0ustar mcgrofmcgrof /* * This file contains declaration referring to * functions defined in other source files */ #ifndef _LBS_DECL_H_ #define _LBS_DECL_H_ #include #include #include /* Should be terminated by a NULL entry */ struct lbs_fw_table { int model; const char *helper; const char *fwname; }; struct lbs_private; typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret, const struct firmware *helper, const struct firmware *mainfw); struct lbs_private; struct sk_buff; struct net_device; struct cmd_ds_command; /* ethtool.c */ extern const struct ethtool_ops lbs_ethtool_ops; /* tx.c */ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count); netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); /* rx.c */ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *); /* main.c */ struct lbs_private *lbs_add_card(void *card, struct device *dmdev); void lbs_remove_card(struct lbs_private *priv); int lbs_start_card(struct lbs_private *priv); void lbs_stop_card(struct lbs_private *priv); void lbs_host_to_card_done(struct lbs_private *priv); int lbs_start_iface(struct lbs_private *priv); int lbs_stop_iface(struct lbs_private *priv); int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type); int lbs_rtap_supported(struct lbs_private *priv); int lbs_set_mac_address(struct net_device *dev, void *addr); void lbs_set_multicast_list(struct net_device *dev); void lbs_update_mcast(struct lbs_private *priv); int lbs_suspend(struct lbs_private *priv); int lbs_resume(struct lbs_private *priv); void lbs_queue_event(struct lbs_private *priv, u32 event); void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); int lbs_enter_auto_deep_sleep(struct lbs_private *priv); int lbs_exit_auto_deep_sleep(struct lbs_private *priv); u32 lbs_fw_index_to_data_rate(u8 index); u8 lbs_data_rate_to_fw_index(u32 rate); int lbs_get_firmware(struct device *dev, u32 card_model, const struct lbs_fw_table *fw_table, const struct firmware **helper, const struct firmware **mainfw); int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, u32 card_model, const struct lbs_fw_table *fw_table, lbs_fw_cb callback); void lbs_wait_for_firmware_load(struct lbs_private *priv); #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/debugfs.h0000644000175000017500000000037212026211315024060 0ustar mcgrofmcgrof#ifndef _LBS_DEBUGFS_H_ #define _LBS_DEBUGFS_H_ void lbs_debugfs_init(void); void lbs_debugfs_remove(void); void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev); void lbs_debugfs_remove_one(struct lbs_private *priv); #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/debugfs.c0000644000175000017500000005517512026211315024066 0ustar mcgrofmcgrof#include #include #include #include #include #include #include #include #include "decl.h" #include "cmd.h" #include "debugfs.h" static struct dentry *lbs_dir; static char *szStates[] = { "Connected", "Disconnected" }; #ifdef PROC_DEBUG static void lbs_debug_init(struct lbs_private *priv); #endif static ssize_t write_file_dummy(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { return -EINVAL; } static const size_t len = PAGE_SIZE; static ssize_t lbs_dev_info(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; size_t pos = 0; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; ssize_t res; if (!buf) return -ENOMEM; pos += snprintf(buf+pos, len-pos, "state = %s\n", szStates[priv->connect_status]); pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", (u32) priv->regioncode); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); free_page(addr); return res; } static ssize_t lbs_sleepparams_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t buf_size, ret; struct sleep_params sp; int p1, p2, p3, p4, p5, p6; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, user_buf, buf_size)) { ret = -EFAULT; goto out_unlock; } ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); if (ret != 6) { ret = -EINVAL; goto out_unlock; } sp.sp_error = p1; sp.sp_offset = p2; sp.sp_stabletime = p3; sp.sp_calcontrol = p4; sp.sp_extsleepclk = p5; sp.sp_reserved = p6; ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp); if (!ret) ret = count; else if (ret > 0) ret = -EINVAL; out_unlock: free_page(addr); return ret; } static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t ret; size_t pos = 0; struct sleep_params sp; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp); if (ret) goto out_unlock; pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error, sp.sp_offset, sp.sp_stabletime, sp.sp_calcontrol, sp.sp_extsleepclk, sp.sp_reserved); ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); out_unlock: free_page(addr); return ret; } static ssize_t lbs_host_sleep_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t buf_size, ret; int host_sleep; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, user_buf, buf_size)) { ret = -EFAULT; goto out_unlock; } ret = sscanf(buf, "%d", &host_sleep); if (ret != 1) { ret = -EINVAL; goto out_unlock; } if (host_sleep == 0) ret = lbs_set_host_sleep(priv, 0); else if (host_sleep == 1) { if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { netdev_info(priv->dev, "wake parameters not configured\n"); ret = -EINVAL; goto out_unlock; } ret = lbs_set_host_sleep(priv, 1); } else { netdev_err(priv->dev, "invalid option\n"); ret = -EINVAL; } if (!ret) ret = count; out_unlock: free_page(addr); return ret; } static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t ret; size_t pos = 0; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated); ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); free_page(addr); return ret; } /* * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the * firmware. Here's an example: * 04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00 * 00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length, * 00 00 are the data bytes of this TLV. For this TLV, their meaning is * defined in mrvlietypes_thresholds * * This function searches in this TLV data chunk for a given TLV type * and returns a pointer to the first data byte of the TLV, or to NULL * if the TLV hasn't been found. */ static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size) { struct mrvl_ie_header *tlv_h; uint16_t length; ssize_t pos = 0; while (pos < size) { tlv_h = (struct mrvl_ie_header *) tlv; if (!tlv_h->len) return NULL; if (tlv_h->type == cpu_to_le16(tlv_type)) return tlv_h; length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h); pos += length; tlv += length; } return NULL; } static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask, struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct cmd_ds_802_11_subscribe_event *subscribed; struct mrvl_ie_thresholds *got; struct lbs_private *priv = file->private_data; ssize_t ret = 0; size_t pos = 0; char *buf; u8 value; u8 freq; int events = 0; buf = (char *)get_zeroed_page(GFP_KERNEL); if (!buf) return -ENOMEM; subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL); if (!subscribed) { ret = -ENOMEM; goto out_page; } subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed)); subscribed->action = cpu_to_le16(CMD_ACT_GET); ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed); if (ret) goto out_cmd; got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv)); if (got) { value = got->value; freq = got->freq; events = le16_to_cpu(subscribed->events); pos += snprintf(buf, len, "%d %d %d\n", value, freq, !!(events & event_mask)); } ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); out_cmd: kfree(subscribed); out_page: free_page((unsigned long)buf); return ret; } static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask, struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct cmd_ds_802_11_subscribe_event *events; struct mrvl_ie_thresholds *tlv; struct lbs_private *priv = file->private_data; ssize_t buf_size; int value, freq, new_mask; uint16_t curr_mask; char *buf; int ret; buf = (char *)get_zeroed_page(GFP_KERNEL); if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { ret = -EFAULT; goto out_page; } ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask); if (ret != 3) { ret = -EINVAL; goto out_page; } events = kzalloc(sizeof(*events), GFP_KERNEL); if (!events) { ret = -ENOMEM; goto out_page; } events->hdr.size = cpu_to_le16(sizeof(*events)); events->action = cpu_to_le16(CMD_ACT_GET); ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); if (ret) goto out_events; curr_mask = le16_to_cpu(events->events); if (new_mask) new_mask = curr_mask | event_mask; else new_mask = curr_mask & ~event_mask; /* Now everything is set and we can send stuff down to the firmware */ tlv = (void *)events->tlv; events->action = cpu_to_le16(CMD_ACT_SET); events->events = cpu_to_le16(new_mask); tlv->header.type = cpu_to_le16(tlv_type); tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header)); tlv->value = value; if (tlv_type != TLV_TYPE_BCNMISS) tlv->freq = freq; /* The command header, the action, the event mask, and one TLV */ events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv)); ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); if (!ret) ret = count; out_events: kfree(events); out_page: free_page((unsigned long)buf); return ret; } static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, file, userbuf, count, ppos); } static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, file, userbuf, count, ppos); } static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, file, userbuf, count, ppos); } static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, file, userbuf, count, ppos); } static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, file, userbuf, count, ppos); } static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, file, userbuf, count, ppos); } static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, file, userbuf, count, ppos); } static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, file, userbuf, count, ppos); } static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, file, userbuf, count, ppos); } static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, file, userbuf, count, ppos); } static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, file, userbuf, count, ppos); } static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, file, userbuf, count, ppos); } static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t pos = 0; int ret; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; u32 val = 0; if (!buf) return -ENOMEM; ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val); mdelay(10); if (!ret) { pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n", priv->mac_offset, val); ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); } free_page(addr); return ret; } static ssize_t lbs_rdmac_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t res, buf_size; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { res = -EFAULT; goto out_unlock; } priv->mac_offset = simple_strtoul(buf, NULL, 16); res = count; out_unlock: free_page(addr); return res; } static ssize_t lbs_wrmac_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t res, buf_size; u32 offset, value; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { res = -EFAULT; goto out_unlock; } res = sscanf(buf, "%x %x", &offset, &value); if (res != 2) { res = -EFAULT; goto out_unlock; } res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value); mdelay(10); if (!res) res = count; out_unlock: free_page(addr); return res; } static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t pos = 0; int ret; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; u32 val; if (!buf) return -ENOMEM; ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val); mdelay(10); if (!ret) { pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n", priv->bbp_offset, val); ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); } free_page(addr); return ret; } static ssize_t lbs_rdbbp_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t res, buf_size; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { res = -EFAULT; goto out_unlock; } priv->bbp_offset = simple_strtoul(buf, NULL, 16); res = count; out_unlock: free_page(addr); return res; } static ssize_t lbs_wrbbp_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t res, buf_size; u32 offset, value; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { res = -EFAULT; goto out_unlock; } res = sscanf(buf, "%x %x", &offset, &value); if (res != 2) { res = -EFAULT; goto out_unlock; } res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value); mdelay(10); if (!res) res = count; out_unlock: free_page(addr); return res; } static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t pos = 0; int ret; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; u32 val; if (!buf) return -ENOMEM; ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val); mdelay(10); if (!ret) { pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n", priv->rf_offset, val); ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); } free_page(addr); return ret; } static ssize_t lbs_rdrf_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t res, buf_size; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { res = -EFAULT; goto out_unlock; } priv->rf_offset = simple_strtoul(buf, NULL, 16); res = count; out_unlock: free_page(addr); return res; } static ssize_t lbs_wrrf_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct lbs_private *priv = file->private_data; ssize_t res, buf_size; u32 offset, value; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { res = -EFAULT; goto out_unlock; } res = sscanf(buf, "%x %x", &offset, &value); if (res != 2) { res = -EFAULT; goto out_unlock; } res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value); mdelay(10); if (!res) res = count; out_unlock: free_page(addr); return res; } #define FOPS(fread, fwrite) { \ .owner = THIS_MODULE, \ .open = simple_open, \ .read = (fread), \ .write = (fwrite), \ .llseek = generic_file_llseek, \ } struct lbs_debugfs_files { const char *name; umode_t perm; struct file_operations fops; }; static const struct lbs_debugfs_files debugfs_files[] = { { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, { "sleepparams", 0644, FOPS(lbs_sleepparams_read, lbs_sleepparams_write), }, { "hostsleep", 0644, FOPS(lbs_host_sleep_read, lbs_host_sleep_write), }, }; static const struct lbs_debugfs_files debugfs_events_files[] = { {"low_rssi", 0644, FOPS(lbs_lowrssi_read, lbs_lowrssi_write), }, {"low_snr", 0644, FOPS(lbs_lowsnr_read, lbs_lowsnr_write), }, {"failure_count", 0644, FOPS(lbs_failcount_read, lbs_failcount_write), }, {"beacon_missed", 0644, FOPS(lbs_bcnmiss_read, lbs_bcnmiss_write), }, {"high_rssi", 0644, FOPS(lbs_highrssi_read, lbs_highrssi_write), }, {"high_snr", 0644, FOPS(lbs_highsnr_read, lbs_highsnr_write), }, }; static const struct lbs_debugfs_files debugfs_regs_files[] = { {"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), }, {"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), }, {"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), }, {"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), }, {"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), }, {"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), }, }; void lbs_debugfs_init(void) { if (!lbs_dir) lbs_dir = debugfs_create_dir("lbs_wireless", NULL); } void lbs_debugfs_remove(void) { if (lbs_dir) debugfs_remove(lbs_dir); } void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev) { int i; const struct lbs_debugfs_files *files; if (!lbs_dir) goto exit; priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir); if (!priv->debugfs_dir) goto exit; for (i=0; idebugfs_files[i] = debugfs_create_file(files->name, files->perm, priv->debugfs_dir, priv, &files->fops); } priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); if (!priv->events_dir) goto exit; for (i=0; idebugfs_events_files[i] = debugfs_create_file(files->name, files->perm, priv->events_dir, priv, &files->fops); } priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); if (!priv->regs_dir) goto exit; for (i=0; idebugfs_regs_files[i] = debugfs_create_file(files->name, files->perm, priv->regs_dir, priv, &files->fops); } #ifdef PROC_DEBUG lbs_debug_init(priv); #endif exit: return; } void lbs_debugfs_remove_one(struct lbs_private *priv) { int i; for(i=0; idebugfs_regs_files[i]); debugfs_remove(priv->regs_dir); for(i=0; idebugfs_events_files[i]); debugfs_remove(priv->events_dir); #ifdef PROC_DEBUG debugfs_remove(priv->debugfs_debug); #endif for(i=0; idebugfs_files[i]); debugfs_remove(priv->debugfs_dir); } /* debug entry */ #ifdef PROC_DEBUG #define item_size(n) (FIELD_SIZEOF(struct lbs_private, n)) #define item_addr(n) (offsetof(struct lbs_private, n)) struct debug_data { char name[32]; u32 size; size_t addr; }; /* To debug any member of struct lbs_private, simply add one line here. */ static struct debug_data items[] = { {"psmode", item_size(psmode), item_addr(psmode)}, {"psstate", item_size(psstate), item_addr(psstate)}, }; static int num_of_items = ARRAY_SIZE(items); /** * lbs_debugfs_read - proc read function * * @file: file to read * @userbuf: pointer to buffer * @count: number of bytes to read * @ppos: read data starting position * * returns: amount of data read or negative error code */ static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { int val = 0; size_t pos = 0; ssize_t res; char *p; int i; struct debug_data *d; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; if (!buf) return -ENOMEM; p = buf; d = file->private_data; for (i = 0; i < num_of_items; i++) { if (d[i].size == 1) val = *((u8 *) d[i].addr); else if (d[i].size == 2) val = *((u16 *) d[i].addr); else if (d[i].size == 4) val = *((u32 *) d[i].addr); else if (d[i].size == 8) val = *((u64 *) d[i].addr); pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); } res = simple_read_from_buffer(userbuf, count, ppos, p, pos); free_page(addr); return res; } /** * lbs_debugfs_write - proc write function * * @f: file pointer * @buf: pointer to data buffer * @cnt: data number to write * @ppos: file position * * returns: amount of data written */ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, size_t cnt, loff_t *ppos) { int r, i; char *pdata; char *p; char *p0; char *p1; char *p2; struct debug_data *d = f->private_data; pdata = kmalloc(cnt, GFP_KERNEL); if (pdata == NULL) return 0; if (copy_from_user(pdata, buf, cnt)) { lbs_deb_debugfs("Copy from user failed\n"); kfree(pdata); return 0; } p0 = pdata; for (i = 0; i < num_of_items; i++) { do { p = strstr(p0, d[i].name); if (p == NULL) break; p1 = strchr(p, '\n'); if (p1 == NULL) break; p0 = p1++; p2 = strchr(p, '='); if (!p2) break; p2++; r = simple_strtoul(p2, NULL, 0); if (d[i].size == 1) *((u8 *) d[i].addr) = (u8) r; else if (d[i].size == 2) *((u16 *) d[i].addr) = (u16) r; else if (d[i].size == 4) *((u32 *) d[i].addr) = (u32) r; else if (d[i].size == 8) *((u64 *) d[i].addr) = (u64) r; break; } while (1); } kfree(pdata); return (ssize_t)cnt; } static const struct file_operations lbs_debug_fops = { .owner = THIS_MODULE, .open = simple_open, .write = lbs_debugfs_write, .read = lbs_debugfs_read, .llseek = default_llseek, }; /** * lbs_debug_init - create debug proc file * * @priv: pointer to &struct lbs_private * * returns: N/A */ static void lbs_debug_init(struct lbs_private *priv) { int i; if (!priv->debugfs_dir) return; for (i = 0; i < num_of_items; i++) items[i].addr += (size_t) priv; priv->debugfs_debug = debugfs_create_file("debug", 0644, priv->debugfs_dir, &items[0], &lbs_debug_fops); } #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/cmdresp.c0000644000175000017500000002221412026211315024070 0ustar mcgrofmcgrof/* * This file contains the handling of command * responses as well as events generated by firmware. */ #include #include #include #include #include #include #include "cfg.h" #include "cmd.h" /** * lbs_mac_event_disconnected - handles disconnect event. It * reports disconnect to upper layer, clean tx/rx packets, * reset link state etc. * * @priv: A pointer to struct lbs_private structure * * returns: n/a */ void lbs_mac_event_disconnected(struct lbs_private *priv) { if (priv->connect_status != LBS_CONNECTED) return; lbs_deb_enter(LBS_DEB_ASSOC); /* * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. * It causes problem in the Supplicant */ msleep_interruptible(1000); if (priv->wdev->iftype == NL80211_IFTYPE_STATION) lbs_send_disconnect_notification(priv); /* report disconnect to upper layer */ netif_stop_queue(priv->dev); netif_carrier_off(priv->dev); /* Free Tx and Rx packets */ kfree_skb(priv->currenttxskb); priv->currenttxskb = NULL; priv->tx_pending_len = 0; priv->connect_status = LBS_DISCONNECTED; if (priv->psstate != PS_STATE_FULL_POWER) { /* make firmware to exit PS mode */ lbs_deb_cmd("disconnected, so exit PS mode\n"); lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); } lbs_deb_leave(LBS_DEB_ASSOC); } int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) { uint16_t respcmd, curcmd; struct cmd_header *resp; int ret = 0; unsigned long flags; uint16_t result; lbs_deb_enter(LBS_DEB_HOST); mutex_lock(&priv->lock); spin_lock_irqsave(&priv->driver_lock, flags); if (!priv->cur_cmd) { lbs_deb_host("CMD_RESP: cur_cmd is NULL\n"); ret = -1; spin_unlock_irqrestore(&priv->driver_lock, flags); goto done; } resp = (void *)data; curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); respcmd = le16_to_cpu(resp->command); result = le16_to_cpu(resp->result); lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n", respcmd, le16_to_cpu(resp->seqnum), len); lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len); if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { netdev_info(priv->dev, "Received CMD_RESP with invalid sequence %d (expected %d)\n", le16_to_cpu(resp->seqnum), le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum)); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } if (respcmd != CMD_RET(curcmd) && respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) { netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n", respcmd, curcmd); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } if (resp->result == cpu_to_le16(0x0004)) { /* 0x0004 means -EAGAIN. Drop the response, let it time out and be resubmitted */ netdev_info(priv->dev, "Firmware returns DEFER to command %x. Will let it time out...\n", le16_to_cpu(resp->command)); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } /* Now we got response from FW, cancel the command timer */ del_timer(&priv->command_timer); priv->cmd_timed_out = 0; if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1]; u16 action = le16_to_cpu(psmode->action); lbs_deb_host( "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", result, action); if (result) { lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", result); /* * We should not re-try enter-ps command in * ad-hoc mode. It takes place in * lbs_execute_next_command(). */ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR && action == PS_MODE_ACTION_ENTER_PS) priv->psmode = LBS802_11POWERMODECAM; } else if (action == PS_MODE_ACTION_ENTER_PS) { priv->needtowakeup = 0; priv->psstate = PS_STATE_AWAKE; lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); if (priv->connect_status != LBS_CONNECTED) { /* * When Deauth Event received before Enter_PS command * response, We need to wake up the firmware. */ lbs_deb_host( "disconnected, invoking lbs_ps_wakeup\n"); spin_unlock_irqrestore(&priv->driver_lock, flags); mutex_unlock(&priv->lock); lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); mutex_lock(&priv->lock); spin_lock_irqsave(&priv->driver_lock, flags); } } else if (action == PS_MODE_ACTION_EXIT_PS) { priv->needtowakeup = 0; priv->psstate = PS_STATE_FULL_POWER; lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); } else { lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); } __lbs_complete_command(priv, priv->cur_cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = 0; goto done; } /* If the command is not successful, cleanup and return failure */ if ((result != 0 || !(respcmd & 0x8000))) { lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", result, respcmd); /* * Handling errors here */ switch (respcmd) { case CMD_RET(CMD_GET_HW_SPEC): case CMD_RET(CMD_802_11_RESET): lbs_deb_host("CMD_RESP: reset failed\n"); break; } __lbs_complete_command(priv, priv->cur_cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } spin_unlock_irqrestore(&priv->driver_lock, flags); if (priv->cur_cmd && priv->cur_cmd->callback) { ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, resp); } spin_lock_irqsave(&priv->driver_lock, flags); if (priv->cur_cmd) { /* Clean up and Put current command back to cmdfreeq */ __lbs_complete_command(priv, priv->cur_cmd, result); } spin_unlock_irqrestore(&priv->driver_lock, flags); done: mutex_unlock(&priv->lock); lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); return ret; } int lbs_process_event(struct lbs_private *priv, u32 event) { int ret = 0; struct cmd_header cmd; lbs_deb_enter(LBS_DEB_CMD); switch (event) { case MACREG_INT_CODE_LINK_SENSED: lbs_deb_cmd("EVENT: link sensed\n"); break; case MACREG_INT_CODE_DEAUTHENTICATED: lbs_deb_cmd("EVENT: deauthenticated\n"); lbs_mac_event_disconnected(priv); break; case MACREG_INT_CODE_DISASSOCIATED: lbs_deb_cmd("EVENT: disassociated\n"); lbs_mac_event_disconnected(priv); break; case MACREG_INT_CODE_LINK_LOST_NO_SCAN: lbs_deb_cmd("EVENT: link lost\n"); lbs_mac_event_disconnected(priv); break; case MACREG_INT_CODE_PS_SLEEP: lbs_deb_cmd("EVENT: ps sleep\n"); /* handle unexpected PS SLEEP event */ if (priv->psstate == PS_STATE_FULL_POWER) { lbs_deb_cmd( "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n"); break; } priv->psstate = PS_STATE_PRE_SLEEP; lbs_ps_confirm_sleep(priv); break; case MACREG_INT_CODE_HOST_AWAKE: lbs_deb_cmd("EVENT: host awake\n"); if (priv->reset_deep_sleep_wakeup) priv->reset_deep_sleep_wakeup(priv); priv->is_deep_sleep = 0; lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd, sizeof(cmd)); priv->is_host_sleep_activated = 0; wake_up_interruptible(&priv->host_sleep_q); break; case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: if (priv->reset_deep_sleep_wakeup) priv->reset_deep_sleep_wakeup(priv); lbs_deb_cmd("EVENT: ds awake\n"); priv->is_deep_sleep = 0; priv->wakeup_dev_required = 0; wake_up_interruptible(&priv->ds_awake_q); break; case MACREG_INT_CODE_PS_AWAKE: lbs_deb_cmd("EVENT: ps awake\n"); /* handle unexpected PS AWAKE event */ if (priv->psstate == PS_STATE_FULL_POWER) { lbs_deb_cmd( "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); break; } priv->psstate = PS_STATE_AWAKE; if (priv->needtowakeup) { /* * wait for the command processing to finish * before resuming sending * priv->needtowakeup will be set to FALSE * in lbs_ps_wakeup() */ lbs_deb_cmd("waking up ...\n"); lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); } break; case MACREG_INT_CODE_MIC_ERR_UNICAST: lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); lbs_send_mic_failureevent(priv, event); break; case MACREG_INT_CODE_MIC_ERR_MULTICAST: lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); lbs_send_mic_failureevent(priv, event); break; case MACREG_INT_CODE_MIB_CHANGED: lbs_deb_cmd("EVENT: MIB CHANGED\n"); break; case MACREG_INT_CODE_INIT_DONE: lbs_deb_cmd("EVENT: INIT DONE\n"); break; case MACREG_INT_CODE_ADHOC_BCN_LOST: lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); break; case MACREG_INT_CODE_RSSI_LOW: netdev_alert(priv->dev, "EVENT: rssi low\n"); break; case MACREG_INT_CODE_SNR_LOW: netdev_alert(priv->dev, "EVENT: snr low\n"); break; case MACREG_INT_CODE_MAX_FAIL: netdev_alert(priv->dev, "EVENT: max fail\n"); break; case MACREG_INT_CODE_RSSI_HIGH: netdev_alert(priv->dev, "EVENT: rssi high\n"); break; case MACREG_INT_CODE_SNR_HIGH: netdev_alert(priv->dev, "EVENT: snr high\n"); break; case MACREG_INT_CODE_MESH_AUTO_STARTED: /* Ignore spurious autostart events */ netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n"); break; default: netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event); break; } lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/libertas/cmd.h0000644000175000017500000001025512026211315023205 0ustar mcgrofmcgrof/* Copyright (C) 2007, Red Hat, Inc. */ #ifndef _LBS_CMD_H_ #define _LBS_CMD_H_ #include #include "host.h" #include "dev.h" /* Command & response transfer between host and card */ struct cmd_ctrl_node { struct list_head list; int result; /* command response */ int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *); unsigned long callback_arg; /* command data */ struct cmd_header *cmdbuf; /* wait queue */ u16 cmdwaitqwoken; wait_queue_head_t cmdwait_q; }; /* lbs_cmd() infers the size of the buffer to copy data back into, from the size of the target of the pointer. Since the command to be sent may often be smaller, that size is set in cmd->size by the caller.*/ #define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ __lbs_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ }) #define lbs_cmd_with_response(priv, cmdnr, cmd) \ lbs_cmd(priv, cmdnr, cmd, lbs_cmd_copyback, (unsigned long) (cmd)) void lbs_cmd_async(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size); int __lbs_cmd(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), unsigned long callback_arg); struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), unsigned long callback_arg); int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, struct cmd_header *resp); int lbs_allocate_cmd_buffer(struct lbs_private *priv); int lbs_free_cmd_buffer(struct lbs_private *priv); int lbs_execute_next_command(struct lbs_private *priv); void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, int result); void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, int result); int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); /* From cmdresp.c */ void lbs_mac_event_disconnected(struct lbs_private *priv); /* Events */ int lbs_process_event(struct lbs_private *priv, u32 event); /* Actual commands */ int lbs_update_hw_spec(struct lbs_private *priv); int lbs_set_channel(struct lbs_private *priv, u8 channel); int lbs_update_channel(struct lbs_private *priv); int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, struct wol_config *p_wol_config); int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, struct sleep_params *sp); void lbs_ps_confirm_sleep(struct lbs_private *priv); int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on); void lbs_set_mac_control(struct lbs_private *priv); int lbs_set_mac_control_sync(struct lbs_private *priv); int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, s16 *maxlevel); int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); /* Commands only used in wext.c, assoc. and scan.c */ int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, int8_t p2); int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, int8_t p2, int usesnr); int lbs_set_data_rate(struct lbs_private *priv, u8 rate); int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, uint16_t cmd_action); int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep); int lbs_set_monitor_mode(struct lbs_private *priv, int enable); int lbs_get_rssi(struct lbs_private *priv, s8 *snr, s8 *nf); int lbs_set_11d_domain_info(struct lbs_private *priv); int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value); int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value); int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block); #endif /* _LBS_CMD_H */ compat-drivers-2012-09-18/drivers/net/wireless/libertas/cmd.c0000644000175000017500000012621512026211315023204 0ustar mcgrofmcgrof/* * This file contains the handling of command. * It prepares command and sends it to firmware when it is ready. */ #include #include #include #include #include #include #include "decl.h" #include "cfg.h" #include "cmd.h" #define CAL_NF(nf) ((s32)(-(s32)(nf))) #define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf))) /** * lbs_cmd_copyback - Simple callback that copies response back into command * * @priv: A pointer to &struct lbs_private structure * @extra: A pointer to the original command structure for which * 'resp' is a response * @resp: A pointer to the command response * * returns: 0 on success, error on failure */ int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, struct cmd_header *resp) { struct cmd_header *buf = (void *)extra; uint16_t copy_len; copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); memcpy(buf, resp, copy_len); return 0; } EXPORT_SYMBOL_GPL(lbs_cmd_copyback); /** * lbs_cmd_async_callback - Simple callback that ignores the result. * Use this if you just want to send a command to the hardware, but don't * care for the result. * * @priv: ignored * @extra: ignored * @resp: ignored * * returns: 0 for success */ static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra, struct cmd_header *resp) { return 0; } /** * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode * * @cmd: the command ID * * returns: 1 if allowed, 0 if not allowed */ static u8 is_command_allowed_in_ps(u16 cmd) { switch (cmd) { case CMD_802_11_RSSI: return 1; case CMD_802_11_HOST_SLEEP_CFG: return 1; default: break; } return 0; } /** * lbs_update_hw_spec - Updates the hardware details like MAC address * and regulatory region * * @priv: A pointer to &struct lbs_private structure * * returns: 0 on success, error on failure */ int lbs_update_hw_spec(struct lbs_private *priv) { struct cmd_ds_get_hw_spec cmd; int ret = -1; u32 i; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); if (ret) goto out; priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); /* The firmware release is in an interesting format: the patch * level is in the most significant nibble ... so fix that: */ priv->fwrelease = le32_to_cpu(cmd.fwrelease); priv->fwrelease = (priv->fwrelease << 8) | (priv->fwrelease >> 24 & 0xff); /* Some firmware capabilities: * CF card firmware 5.0.16p0: cap 0x00000303 * USB dongle firmware 5.110.17p2: cap 0x00000303 */ netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n", cmd.permanentaddr, priv->fwrelease >> 24 & 0xff, priv->fwrelease >> 16 & 0xff, priv->fwrelease >> 8 & 0xff, priv->fwrelease & 0xff, priv->fwcapinfo); lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", cmd.hwifversion, cmd.version); /* Clamp region code to 8-bit since FW spec indicates that it should * only ever be 8-bit, even though the field size is 16-bit. Some firmware * returns non-zero high 8 bits here. * * Firmware version 4.0.102 used in CF8381 has region code shifted. We * need to check for this problem and handle it properly. */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; else priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { /* use the region code to search for the index */ if (priv->regioncode == lbs_region_code_to_index[i]) break; } /* if it's unidentified region code, use the default (USA) */ if (i >= MRVDRV_MAX_REGION_CODE) { priv->regioncode = 0x10; netdev_info(priv->dev, "unidentified region code; using the default (USA)\n"); } if (priv->current_addr[0] == 0xff) memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); if (!priv->copied_hwaddr) { memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); if (priv->mesh_dev) memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN); priv->copied_hwaddr = 1; } out: lbs_deb_leave(LBS_DEB_CMD); return ret; } static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, struct cmd_header *resp) { lbs_deb_enter(LBS_DEB_CMD); if (priv->is_host_sleep_activated) { priv->is_host_sleep_configured = 0; if (priv->psstate == PS_STATE_FULL_POWER) { priv->is_host_sleep_activated = 0; wake_up_interruptible(&priv->host_sleep_q); } } else { priv->is_host_sleep_configured = 1; } lbs_deb_leave(LBS_DEB_CMD); return 0; } int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, struct wol_config *p_wol_config) { struct cmd_ds_host_sleep cmd_config; int ret; /* * Certain firmware versions do not support EHS_REMOVE_WAKEUP command * and the card will return a failure. Since we need to be * able to reset the mask, in those cases we set a 0 mask instead. */ if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported) criteria = 0; cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config)); cmd_config.criteria = cpu_to_le32(criteria); cmd_config.gpio = priv->wol_gpio; cmd_config.gap = priv->wol_gap; if (p_wol_config != NULL) memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config, sizeof(struct wol_config)); else cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, le16_to_cpu(cmd_config.hdr.size), lbs_ret_host_sleep_cfg, 0); if (!ret) { if (p_wol_config) memcpy((uint8_t *) p_wol_config, (uint8_t *)&cmd_config.wol_conf, sizeof(struct wol_config)); } else { netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret); } return ret; } EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); /** * lbs_set_ps_mode - Sets the Power Save mode * * @priv: A pointer to &struct lbs_private structure * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or * PS_MODE_ACTION_EXIT_PS) * @block: Whether to block on a response or not * * returns: 0 on success, error on failure */ int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block) { struct cmd_ds_802_11_ps_mode cmd; int ret = 0; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(cmd_action); if (cmd_action == PS_MODE_ACTION_ENTER_PS) { lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */ } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); } else { /* We don't handle CONFIRM_SLEEP here because it needs to * be fastpathed to the firmware. */ lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); ret = -EOPNOTSUPP; goto out; } if (block) ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); else lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, struct sleep_params *sp) { struct cmd_ds_802_11_sleep_params cmd; int ret; lbs_deb_enter(LBS_DEB_CMD); if (cmd_action == CMD_ACT_GET) { memset(&cmd, 0, sizeof(cmd)); } else { cmd.error = cpu_to_le16(sp->sp_error); cmd.offset = cpu_to_le16(sp->sp_offset); cmd.stabletime = cpu_to_le16(sp->sp_stabletime); cmd.calcontrol = sp->sp_calcontrol; cmd.externalsleepclk = sp->sp_extsleepclk; cmd.reserved = cpu_to_le16(sp->sp_reserved); } cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(cmd_action); ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd); if (!ret) { lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, " "calcontrol 0x%x extsleepclk 0x%x\n", le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset), le16_to_cpu(cmd.stabletime), cmd.calcontrol, cmd.externalsleepclk); sp->sp_error = le16_to_cpu(cmd.error); sp->sp_offset = le16_to_cpu(cmd.offset); sp->sp_stabletime = le16_to_cpu(cmd.stabletime); sp->sp_calcontrol = cmd.calcontrol; sp->sp_extsleepclk = cmd.externalsleepclk; sp->sp_reserved = le16_to_cpu(cmd.reserved); } lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return 0; } static int lbs_wait_for_ds_awake(struct lbs_private *priv) { int ret = 0; lbs_deb_enter(LBS_DEB_CMD); if (priv->is_deep_sleep) { if (!wait_event_interruptible_timeout(priv->ds_awake_q, !priv->is_deep_sleep, (10 * HZ))) { netdev_err(priv->dev, "ds_awake_q: timer expired\n"); ret = -1; } } lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) { int ret = 0; lbs_deb_enter(LBS_DEB_CMD); if (deep_sleep) { if (priv->is_deep_sleep != 1) { lbs_deb_cmd("deep sleep: sleep\n"); BUG_ON(!priv->enter_deep_sleep); ret = priv->enter_deep_sleep(priv); if (!ret) { netif_stop_queue(priv->dev); netif_carrier_off(priv->dev); } } else { netdev_err(priv->dev, "deep sleep: already enabled\n"); } } else { if (priv->is_deep_sleep) { lbs_deb_cmd("deep sleep: wakeup\n"); BUG_ON(!priv->exit_deep_sleep); ret = priv->exit_deep_sleep(priv); if (!ret) { ret = lbs_wait_for_ds_awake(priv); if (ret) netdev_err(priv->dev, "deep sleep: wakeup failed\n"); } } } lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } static int lbs_ret_host_sleep_activate(struct lbs_private *priv, unsigned long dummy, struct cmd_header *cmd) { lbs_deb_enter(LBS_DEB_FW); priv->is_host_sleep_activated = 1; wake_up_interruptible(&priv->host_sleep_q); lbs_deb_leave(LBS_DEB_FW); return 0; } int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) { struct cmd_header cmd; int ret = 0; uint32_t criteria = EHS_REMOVE_WAKEUP; lbs_deb_enter(LBS_DEB_CMD); if (host_sleep) { if (priv->is_host_sleep_activated != 1) { memset(&cmd, 0, sizeof(cmd)); ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, (struct wol_config *)NULL); if (ret) { netdev_info(priv->dev, "Host sleep configuration failed: %d\n", ret); return ret; } if (priv->psstate == PS_STATE_FULL_POWER) { ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd, sizeof(cmd), lbs_ret_host_sleep_activate, 0); if (ret) netdev_info(priv->dev, "HOST_SLEEP_ACTIVATE failed: %d\n", ret); } if (!wait_event_interruptible_timeout( priv->host_sleep_q, priv->is_host_sleep_activated, (10 * HZ))) { netdev_err(priv->dev, "host_sleep_q: timer expired\n"); ret = -1; } } else { netdev_err(priv->dev, "host sleep: already enabled\n"); } } else { if (priv->is_host_sleep_activated) ret = lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL); } return ret; } /** * lbs_set_snmp_mib - Set an SNMP MIB value * * @priv: A pointer to &struct lbs_private structure * @oid: The OID to set in the firmware * @val: Value to set the OID to * * returns: 0 on success, error on failure */ int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) { struct cmd_ds_802_11_snmp_mib cmd; int ret; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof (cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.oid = cpu_to_le16((u16) oid); switch (oid) { case SNMP_MIB_OID_BSS_TYPE: cmd.bufsize = cpu_to_le16(sizeof(u8)); cmd.value[0] = val; break; case SNMP_MIB_OID_11D_ENABLE: case SNMP_MIB_OID_FRAG_THRESHOLD: case SNMP_MIB_OID_RTS_THRESHOLD: case SNMP_MIB_OID_SHORT_RETRY_LIMIT: case SNMP_MIB_OID_LONG_RETRY_LIMIT: cmd.bufsize = cpu_to_le16(sizeof(u16)); *((__le16 *)(&cmd.value)) = cpu_to_le16(val); break; default: lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid); ret = -EINVAL; goto out; } lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n", le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val); ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } /** * lbs_get_snmp_mib - Get an SNMP MIB value * * @priv: A pointer to &struct lbs_private structure * @oid: The OID to retrieve from the firmware * @out_val: Location for the returned value * * returns: 0 on success, error on failure */ int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) { struct cmd_ds_802_11_snmp_mib cmd; int ret; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof (cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_GET); cmd.oid = cpu_to_le16(oid); ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); if (ret) goto out; switch (le16_to_cpu(cmd.bufsize)) { case sizeof(u8): *out_val = cmd.value[0]; break; case sizeof(u16): *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); break; default: lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", oid, le16_to_cpu(cmd.bufsize)); break; } out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } /** * lbs_get_tx_power - Get the min, max, and current TX power * * @priv: A pointer to &struct lbs_private structure * @curlevel: Current power level in dBm * @minlevel: Minimum supported power level in dBm (optional) * @maxlevel: Maximum supported power level in dBm (optional) * * returns: 0 on success, error on failure */ int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, s16 *maxlevel) { struct cmd_ds_802_11_rf_tx_power cmd; int ret; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_GET); ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); if (ret == 0) { *curlevel = le16_to_cpu(cmd.curlevel); if (minlevel) *minlevel = cmd.minlevel; if (maxlevel) *maxlevel = cmd.maxlevel; } lbs_deb_leave(LBS_DEB_CMD); return ret; } /** * lbs_set_tx_power - Set the TX power * * @priv: A pointer to &struct lbs_private structure * @dbm: The desired power level in dBm * * returns: 0 on success, error on failure */ int lbs_set_tx_power(struct lbs_private *priv, s16 dbm) { struct cmd_ds_802_11_rf_tx_power cmd; int ret; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.curlevel = cpu_to_le16(dbm); lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); lbs_deb_leave(LBS_DEB_CMD); return ret; } /** * lbs_set_monitor_mode - Enable or disable monitor mode * (only implemented on OLPC usb8388 FW) * * @priv: A pointer to &struct lbs_private structure * @enable: 1 to enable monitor mode, 0 to disable * * returns: 0 on success, error on failure */ int lbs_set_monitor_mode(struct lbs_private *priv, int enable) { struct cmd_ds_802_11_monitor_mode cmd; int ret; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); if (enable) cmd.mode = cpu_to_le16(0x1); lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); if (ret == 0) { priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : ARPHRD_ETHER; } lbs_deb_leave(LBS_DEB_CMD); return ret; } /** * lbs_get_channel - Get the radio channel * * @priv: A pointer to &struct lbs_private structure * * returns: The channel on success, error on failure */ static int lbs_get_channel(struct lbs_private *priv) { struct cmd_ds_802_11_rf_channel cmd; int ret = 0; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET); ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); if (ret) goto out; ret = le16_to_cpu(cmd.channel); lbs_deb_cmd("current radio channel is %d\n", ret); out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } int lbs_update_channel(struct lbs_private *priv) { int ret; /* the channel in f/w could be out of sync; get the current channel */ lbs_deb_enter(LBS_DEB_ASSOC); ret = lbs_get_channel(priv); if (ret > 0) { priv->channel = ret; ret = 0; } lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); return ret; } /** * lbs_set_channel - Set the radio channel * * @priv: A pointer to &struct lbs_private structure * @channel: The desired channel, or 0 to clear a locked channel * * returns: 0 on success, error on failure */ int lbs_set_channel(struct lbs_private *priv, u8 channel) { struct cmd_ds_802_11_rf_channel cmd; #ifdef DEBUG u8 old_channel = priv->channel; #endif int ret = 0; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); cmd.channel = cpu_to_le16(channel); ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); if (ret) goto out; priv->channel = (uint8_t) le16_to_cpu(cmd.channel); lbs_deb_cmd("channel switch from %d to %d\n", old_channel, priv->channel); out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } /** * lbs_get_rssi - Get current RSSI and noise floor * * @priv: A pointer to &struct lbs_private structure * @rssi: On successful return, signal level in mBm * @nf: On successful return, Noise floor * * returns: The channel on success, error on failure */ int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) { struct cmd_ds_802_11_rssi cmd; int ret = 0; lbs_deb_enter(LBS_DEB_CMD); BUG_ON(rssi == NULL); BUG_ON(nf == NULL); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); /* Average SNR over last 8 beacons */ cmd.n_or_snr = cpu_to_le16(8); ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); if (ret == 0) { *nf = CAL_NF(le16_to_cpu(cmd.nf)); *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); } lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } /** * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information * to the firmware * * @priv: pointer to &struct lbs_private * * returns: 0 on success, error code on failure */ int lbs_set_11d_domain_info(struct lbs_private *priv) { struct wiphy *wiphy = priv->wdev->wiphy; struct ieee80211_supported_band **bands = wiphy->bands; struct cmd_ds_802_11d_domain_info cmd; struct mrvl_ie_domain_param_set *domain = &cmd.domain; struct ieee80211_country_ie_triplet *t; enum ieee80211_band band; struct ieee80211_channel *ch; u8 num_triplet = 0; u8 num_parsed_chan = 0; u8 first_channel = 0, next_chan = 0, max_pwr = 0; u8 i, flag = 0; size_t triplet_size; int ret = 0; lbs_deb_enter(LBS_DEB_11D); if (!priv->country_code[0]) goto out; memset(&cmd, 0, sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); lbs_deb_11d("Setting country code '%c%c'\n", priv->country_code[0], priv->country_code[1]); domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); /* Set country code */ domain->country_code[0] = priv->country_code[0]; domain->country_code[1] = priv->country_code[1]; domain->country_code[2] = ' '; /* Now set up the channel triplets; firmware is somewhat picky here * and doesn't validate channel numbers and spans; hence it would * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since * the last 3 aren't valid channels, the driver is responsible for * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) * etc. */ for (band = 0; (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS); band++) { if (!bands[band]) continue; for (i = 0; (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); i++) { ch = &bands[band]->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; if (!flag) { flag = 1; next_chan = first_channel = (u32) ch->hw_value; max_pwr = ch->max_power; num_parsed_chan = 1; continue; } if ((ch->hw_value == next_chan + 1) && (ch->max_power == max_pwr)) { /* Consolidate adjacent channels */ next_chan++; num_parsed_chan++; } else { /* Add this triplet */ lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, num_parsed_chan, max_pwr); t = &domain->triplet[num_triplet]; t->chans.first_channel = first_channel; t->chans.num_channels = num_parsed_chan; t->chans.max_power = max_pwr; num_triplet++; flag = 0; } } if (flag) { /* Add last triplet */ lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, num_parsed_chan, max_pwr); t = &domain->triplet[num_triplet]; t->chans.first_channel = first_channel; t->chans.num_channels = num_parsed_chan; t->chans.max_power = max_pwr; num_triplet++; } } lbs_deb_11d("# triplets %d\n", num_triplet); /* Set command header sizes */ triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); domain->header.len = cpu_to_le16(sizeof(domain->country_code) + triplet_size); lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", (u8 *) &cmd.domain.country_code, le16_to_cpu(domain->header.len)); cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + sizeof(cmd.action) + sizeof(cmd.domain.header) + sizeof(cmd.domain.country_code) + triplet_size); ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); out: lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); return ret; } /** * lbs_get_reg - Read a MAC, Baseband, or RF register * * @priv: pointer to &struct lbs_private * @reg: register command, one of CMD_MAC_REG_ACCESS, * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS * @offset: byte offset of the register to get * @value: on success, the value of the register at 'offset' * * returns: 0 on success, error code on failure */ int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) { struct cmd_ds_reg_access cmd; int ret = 0; lbs_deb_enter(LBS_DEB_CMD); BUG_ON(value == NULL); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_GET); cmd.offset = cpu_to_le16(offset); if (reg != CMD_MAC_REG_ACCESS && reg != CMD_BBP_REG_ACCESS && reg != CMD_RF_REG_ACCESS) { ret = -EINVAL; goto out; } ret = lbs_cmd_with_response(priv, reg, &cmd); if (!ret) { if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) *value = cmd.value.bbp_rf; else if (reg == CMD_MAC_REG_ACCESS) *value = le32_to_cpu(cmd.value.mac); } out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } /** * lbs_set_reg - Write a MAC, Baseband, or RF register * * @priv: pointer to &struct lbs_private * @reg: register command, one of CMD_MAC_REG_ACCESS, * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS * @offset: byte offset of the register to set * @value: the value to write to the register at 'offset' * * returns: 0 on success, error code on failure */ int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) { struct cmd_ds_reg_access cmd; int ret = 0; lbs_deb_enter(LBS_DEB_CMD); memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.offset = cpu_to_le16(offset); if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) cmd.value.bbp_rf = (u8) (value & 0xFF); else if (reg == CMD_MAC_REG_ACCESS) cmd.value.mac = cpu_to_le32(value); else { ret = -EINVAL; goto out; } ret = lbs_cmd_with_response(priv, reg, &cmd); out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } static void lbs_queue_cmd(struct lbs_private *priv, struct cmd_ctrl_node *cmdnode) { unsigned long flags; int addtail = 1; lbs_deb_enter(LBS_DEB_HOST); if (!cmdnode) { lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n"); goto done; } if (!cmdnode->cmdbuf->size) { lbs_deb_host("DNLD_CMD: cmd size is zero\n"); goto done; } cmdnode->result = 0; /* Exit_PS command needs to be queued in the header always. */ if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf; if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { if (priv->psstate != PS_STATE_FULL_POWER) addtail = 0; } } if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) addtail = 0; spin_lock_irqsave(&priv->driver_lock, flags); if (addtail) list_add_tail(&cmdnode->list, &priv->cmdpendingq); else list_add(&cmdnode->list, &priv->cmdpendingq); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", le16_to_cpu(cmdnode->cmdbuf->command)); done: lbs_deb_leave(LBS_DEB_HOST); } static void lbs_submit_command(struct lbs_private *priv, struct cmd_ctrl_node *cmdnode) { unsigned long flags; struct cmd_header *cmd; uint16_t cmdsize; uint16_t command; int timeo = 3 * HZ; int ret; lbs_deb_enter(LBS_DEB_HOST); cmd = cmdnode->cmdbuf; spin_lock_irqsave(&priv->driver_lock, flags); priv->seqnum++; cmd->seqnum = cpu_to_le16(priv->seqnum); priv->cur_cmd = cmdnode; spin_unlock_irqrestore(&priv->driver_lock, flags); cmdsize = le16_to_cpu(cmd->size); command = le16_to_cpu(cmd->command); /* These commands take longer */ if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE) timeo = 5 * HZ; lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", command, le16_to_cpu(cmd->seqnum), cmdsize); lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); if (ret) { netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", ret); /* Reset dnld state machine, report failure */ priv->dnld_sent = DNLD_RES_RECEIVED; lbs_complete_command(priv, cmdnode, ret); } if (command == CMD_802_11_DEEP_SLEEP) { if (priv->is_auto_deep_sleep_enabled) { priv->wakeup_dev_required = 1; priv->dnld_sent = 0; } priv->is_deep_sleep = 1; lbs_complete_command(priv, cmdnode, 0); } else { /* Setup the timer after transmit command */ mod_timer(&priv->command_timer, jiffies + timeo); } lbs_deb_leave(LBS_DEB_HOST); } /* * This function inserts command node to cmdfreeq * after cleans it. Requires priv->driver_lock held. */ static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv, struct cmd_ctrl_node *cmdnode) { lbs_deb_enter(LBS_DEB_HOST); if (!cmdnode) goto out; cmdnode->callback = NULL; cmdnode->callback_arg = 0; memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); list_add_tail(&cmdnode->list, &priv->cmdfreeq); out: lbs_deb_leave(LBS_DEB_HOST); } static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, struct cmd_ctrl_node *ptempcmd) { unsigned long flags; spin_lock_irqsave(&priv->driver_lock, flags); __lbs_cleanup_and_insert_cmd(priv, ptempcmd); spin_unlock_irqrestore(&priv->driver_lock, flags); } void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, int result) { /* * Normally, commands are removed from cmdpendingq before being * submitted. However, we can arrive here on alternative codepaths * where the command is still pending. Make sure the command really * isn't part of a list at this point. */ list_del_init(&cmd->list); cmd->result = result; cmd->cmdwaitqwoken = 1; wake_up(&cmd->cmdwait_q); if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) __lbs_cleanup_and_insert_cmd(priv, cmd); priv->cur_cmd = NULL; wake_up(&priv->waitq); } void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, int result) { unsigned long flags; spin_lock_irqsave(&priv->driver_lock, flags); __lbs_complete_command(priv, cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); } int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) { struct cmd_ds_802_11_radio_control cmd; int ret = -EINVAL; lbs_deb_enter(LBS_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); /* Only v8 and below support setting the preamble */ if (priv->fwrelease < 0x09000000) { switch (preamble) { case RADIO_PREAMBLE_SHORT: case RADIO_PREAMBLE_AUTO: case RADIO_PREAMBLE_LONG: cmd.control = cpu_to_le16(preamble); break; default: goto out; } } if (radio_on) cmd.control |= cpu_to_le16(0x1); else { cmd.control &= cpu_to_le16(~0x1); priv->txpower_cur = 0; } lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n", radio_on ? "ON" : "OFF", preamble); priv->radio_on = radio_on; ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); out: lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); return ret; } void lbs_set_mac_control(struct lbs_private *priv) { struct cmd_ds_mac_control cmd; lbs_deb_enter(LBS_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(priv->mac_control); cmd.reserved = 0; lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); lbs_deb_leave(LBS_DEB_CMD); } int lbs_set_mac_control_sync(struct lbs_private *priv) { struct cmd_ds_mac_control cmd; int ret = 0; lbs_deb_enter(LBS_DEB_CMD); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(priv->mac_control); cmd.reserved = 0; ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); lbs_deb_leave(LBS_DEB_CMD); return ret; } /** * lbs_allocate_cmd_buffer - allocates the command buffer and links * it to command free queue * * @priv: A pointer to &struct lbs_private structure * * returns: 0 for success or -1 on error */ int lbs_allocate_cmd_buffer(struct lbs_private *priv) { int ret = 0; u32 bufsize; u32 i; struct cmd_ctrl_node *cmdarray; lbs_deb_enter(LBS_DEB_HOST); /* Allocate and initialize the command array */ bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) { lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); ret = -1; goto done; } priv->cmd_array = cmdarray; /* Allocate and initialize each command buffer in the command array */ for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); if (!cmdarray[i].cmdbuf) { lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); ret = -1; goto done; } } for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { init_waitqueue_head(&cmdarray[i].cmdwait_q); lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); } ret = 0; done: lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); return ret; } /** * lbs_free_cmd_buffer - free the command buffer * * @priv: A pointer to &struct lbs_private structure * * returns: 0 for success */ int lbs_free_cmd_buffer(struct lbs_private *priv) { struct cmd_ctrl_node *cmdarray; unsigned int i; lbs_deb_enter(LBS_DEB_HOST); /* need to check if cmd array is allocated or not */ if (priv->cmd_array == NULL) { lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); goto done; } cmdarray = priv->cmd_array; /* Release shared memory buffers */ for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { if (cmdarray[i].cmdbuf) { kfree(cmdarray[i].cmdbuf); cmdarray[i].cmdbuf = NULL; } } /* Release cmd_ctrl_node */ if (priv->cmd_array) { kfree(priv->cmd_array); priv->cmd_array = NULL; } done: lbs_deb_leave(LBS_DEB_HOST); return 0; } /** * lbs_get_free_cmd_node - gets a free command node if available in * command free queue * * @priv: A pointer to &struct lbs_private structure * * returns: A pointer to &cmd_ctrl_node structure on success * or %NULL on error */ static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) { struct cmd_ctrl_node *tempnode; unsigned long flags; lbs_deb_enter(LBS_DEB_HOST); if (!priv) return NULL; spin_lock_irqsave(&priv->driver_lock, flags); if (!list_empty(&priv->cmdfreeq)) { tempnode = list_first_entry(&priv->cmdfreeq, struct cmd_ctrl_node, list); list_del_init(&tempnode->list); } else { lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); tempnode = NULL; } spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_HOST); return tempnode; } /** * lbs_execute_next_command - execute next command in command * pending queue. Will put firmware back to PS mode if applicable. * * @priv: A pointer to &struct lbs_private structure * * returns: 0 on success or -1 on error */ int lbs_execute_next_command(struct lbs_private *priv) { struct cmd_ctrl_node *cmdnode = NULL; struct cmd_header *cmd; unsigned long flags; int ret = 0; /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the * only caller to us is lbs_thread() and we get even when a * data packet is received */ lbs_deb_enter(LBS_DEB_THREAD); spin_lock_irqsave(&priv->driver_lock, flags); if (priv->cur_cmd) { netdev_alert(priv->dev, "EXEC_NEXT_CMD: already processing command!\n"); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; goto done; } if (!list_empty(&priv->cmdpendingq)) { cmdnode = list_first_entry(&priv->cmdpendingq, struct cmd_ctrl_node, list); } spin_unlock_irqrestore(&priv->driver_lock, flags); if (cmdnode) { cmd = cmdnode->cmdbuf; if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) { if ((priv->psstate == PS_STATE_SLEEP) || (priv->psstate == PS_STATE_PRE_SLEEP)) { lbs_deb_host( "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n", le16_to_cpu(cmd->command), priv->psstate); ret = -1; goto done; } lbs_deb_host("EXEC_NEXT_CMD: OK to send command " "0x%04x in psstate %d\n", le16_to_cpu(cmd->command), priv->psstate); } else if (priv->psstate != PS_STATE_FULL_POWER) { /* * 1. Non-PS command: * Queue it. set needtowakeup to TRUE if current state * is SLEEP, otherwise call send EXIT_PS. * 2. PS command but not EXIT_PS: * Ignore it. * 3. PS command EXIT_PS: * Set needtowakeup to TRUE if current state is SLEEP, * otherwise send this command down to firmware * immediately. */ if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) { /* Prepare to send Exit PS, * this non PS command will be sent later */ if ((priv->psstate == PS_STATE_SLEEP) || (priv->psstate == PS_STATE_PRE_SLEEP) ) { /* w/ new scheme, it will not reach here. since it is blocked in main_thread. */ priv->needtowakeup = 1; } else { lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); } ret = 0; goto done; } else { /* * PS command. Ignore it if it is not Exit_PS. * otherwise send it down immediately. */ struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1]; lbs_deb_host( "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", psm->action); if (psm->action != cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); lbs_complete_command(priv, cmdnode, 0); ret = 0; goto done; } if ((priv->psstate == PS_STATE_SLEEP) || (priv->psstate == PS_STATE_PRE_SLEEP)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); lbs_complete_command(priv, cmdnode, 0); priv->needtowakeup = 1; ret = 0; goto done; } lbs_deb_host( "EXEC_NEXT_CMD: sending EXIT_PS\n"); } } spin_lock_irqsave(&priv->driver_lock, flags); list_del_init(&cmdnode->list); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", le16_to_cpu(cmd->command)); lbs_submit_command(priv, cmdnode); } else { /* * check if in power save mode, if yes, put the device back * to PS mode */ #ifdef TODO /* * This was the old code for libertas+wext. Someone that * understands this beast should re-code it in a sane way. * * I actually don't understand why this is related to WPA * and to connection status, shouldn't powering should be * independ of such things? */ if ((priv->psmode != LBS802_11POWERMODECAM) && (priv->psstate == PS_STATE_FULL_POWER) && ((priv->connect_status == LBS_CONNECTED) || lbs_mesh_connected(priv))) { if (priv->secinfo.WPAenabled || priv->secinfo.WPA2enabled) { /* check for valid WPA group keys */ if (priv->wpa_mcast_key.len || priv->wpa_unicast_key.len) { lbs_deb_host( "EXEC_NEXT_CMD: WPA enabled and GTK_SET" " go back to PS_SLEEP"); lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, false); } } else { lbs_deb_host( "EXEC_NEXT_CMD: cmdpendingq empty, " "go back to PS_SLEEP"); lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, false); } } #endif } ret = 0; done: lbs_deb_leave(LBS_DEB_THREAD); return ret; } static void lbs_send_confirmsleep(struct lbs_private *priv) { unsigned long flags; int ret; lbs_deb_enter(LBS_DEB_HOST); lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep, sizeof(confirm_sleep)); ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep, sizeof(confirm_sleep)); if (ret) { netdev_alert(priv->dev, "confirm_sleep failed\n"); goto out; } spin_lock_irqsave(&priv->driver_lock, flags); /* We don't get a response on the sleep-confirmation */ priv->dnld_sent = DNLD_RES_RECEIVED; if (priv->is_host_sleep_configured) { priv->is_host_sleep_activated = 1; wake_up_interruptible(&priv->host_sleep_q); } /* If nothing to do, go back to sleep (?) */ if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx]) priv->psstate = PS_STATE_SLEEP; spin_unlock_irqrestore(&priv->driver_lock, flags); out: lbs_deb_leave(LBS_DEB_HOST); } /** * lbs_ps_confirm_sleep - checks condition and prepares to * send sleep confirm command to firmware if ok * * @priv: A pointer to &struct lbs_private structure * * returns: n/a */ void lbs_ps_confirm_sleep(struct lbs_private *priv) { unsigned long flags =0; int allowed = 1; lbs_deb_enter(LBS_DEB_HOST); spin_lock_irqsave(&priv->driver_lock, flags); if (priv->dnld_sent) { allowed = 0; lbs_deb_host("dnld_sent was set\n"); } /* In-progress command? */ if (priv->cur_cmd) { allowed = 0; lbs_deb_host("cur_cmd was set\n"); } /* Pending events or command responses? */ if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) { allowed = 0; lbs_deb_host("pending events or command responses\n"); } spin_unlock_irqrestore(&priv->driver_lock, flags); if (allowed) { lbs_deb_host("sending lbs_ps_confirm_sleep\n"); lbs_send_confirmsleep(priv); } else { lbs_deb_host("sleep confirm has been delayed\n"); } lbs_deb_leave(LBS_DEB_HOST); } /** * lbs_set_tpc_cfg - Configures the transmission power control functionality * * @priv: A pointer to &struct lbs_private structure * @enable: Transmission power control enable * @p0: Power level when link quality is good (dBm). * @p1: Power level when link quality is fair (dBm). * @p2: Power level when link quality is poor (dBm). * @usesnr: Use Signal to Noise Ratio in TPC * * returns: 0 on success */ int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, int8_t p2, int usesnr) { struct cmd_ds_802_11_tpc_cfg cmd; int ret; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.enable = !!enable; cmd.usesnr = !!usesnr; cmd.P0 = p0; cmd.P1 = p1; cmd.P2 = p2; ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); return ret; } /** * lbs_set_power_adapt_cfg - Configures the power adaptation settings * * @priv: A pointer to &struct lbs_private structure * @enable: Power adaptation enable * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). * @p2: Power level for 48 and 54 Mbps (dBm). * * returns: 0 on Success */ int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, int8_t p2) { struct cmd_ds_802_11_pa_cfg cmd; int ret; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); cmd.enable = !!enable; cmd.P0 = p0; cmd.P1 = p1; cmd.P2 = p2; ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); return ret; } struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), unsigned long callback_arg) { struct cmd_ctrl_node *cmdnode; lbs_deb_enter(LBS_DEB_HOST); if (priv->surpriseremoved) { lbs_deb_host("PREP_CMD: card removed\n"); cmdnode = ERR_PTR(-ENOENT); goto done; } /* No commands are allowed in Deep Sleep until we toggle the GPIO * to wake up the card and it has signaled that it's ready. */ if (!priv->is_auto_deep_sleep_enabled) { if (priv->is_deep_sleep) { lbs_deb_cmd("command not allowed in deep sleep\n"); cmdnode = ERR_PTR(-EBUSY); goto done; } } cmdnode = lbs_get_free_cmd_node(priv); if (cmdnode == NULL) { lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); /* Wake up main thread to execute next command */ wake_up(&priv->waitq); cmdnode = ERR_PTR(-ENOBUFS); goto done; } cmdnode->callback = callback; cmdnode->callback_arg = callback_arg; /* Copy the incoming command to the buffer */ memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); /* Set command, clean result, move to buffer */ cmdnode->cmdbuf->command = cpu_to_le16(command); cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); cmdnode->cmdbuf->result = 0; lbs_deb_host("PREP_CMD: command 0x%04x\n", command); cmdnode->cmdwaitqwoken = 0; lbs_queue_cmd(priv, cmdnode); wake_up(&priv->waitq); done: lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode); return cmdnode; } void lbs_cmd_async(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size) { lbs_deb_enter(LBS_DEB_CMD); __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, lbs_cmd_async_callback, 0); lbs_deb_leave(LBS_DEB_CMD); } int __lbs_cmd(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), unsigned long callback_arg) { struct cmd_ctrl_node *cmdnode; unsigned long flags; int ret = 0; lbs_deb_enter(LBS_DEB_HOST); cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, callback, callback_arg); if (IS_ERR(cmdnode)) { ret = PTR_ERR(cmdnode); goto done; } might_sleep(); /* * Be careful with signals here. A signal may be received as the system * goes into suspend or resume. We do not want this to interrupt the * command, so we perform an uninterruptible sleep. */ wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); spin_lock_irqsave(&priv->driver_lock, flags); ret = cmdnode->result; if (ret) netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n", command, ret); __lbs_cleanup_and_insert_cmd(priv, cmdnode); spin_unlock_irqrestore(&priv->driver_lock, flags); done: lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); return ret; } EXPORT_SYMBOL_GPL(__lbs_cmd); compat-drivers-2012-09-18/drivers/net/wireless/libertas/cfg.h0000644000175000017500000000122312026211315023174 0ustar mcgrofmcgrof#ifndef __LBS_CFG80211_H__ #define __LBS_CFG80211_H__ struct device; struct lbs_private; struct regulatory_request; struct wiphy; struct wireless_dev *lbs_cfg_alloc(struct device *dev); int lbs_cfg_register(struct lbs_private *priv); void lbs_cfg_free(struct lbs_private *priv); int lbs_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); void lbs_send_disconnect_notification(struct lbs_private *priv); void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); void lbs_scan_done(struct lbs_private *priv); void lbs_scan_deinit(struct lbs_private *priv); int lbs_disconnect(struct lbs_private *priv, u16 reason); #endif compat-drivers-2012-09-18/drivers/net/wireless/libertas/Kconfig0000644000175000017500000000230112026211315023565 0ustar mcgrofmcgrofconfig LIBERTAS tristate "Marvell 8xxx Libertas WLAN driver support" depends on CFG80211 select WIRELESS_EXT select WEXT_SPY select LIB80211 select FW_LOADER ---help--- A library for Marvell Libertas 8xxx devices. config LIBERTAS_USB tristate "Marvell Libertas 8388 USB 802.11b/g cards" depends on LIBERTAS && USB ---help--- A driver for Marvell Libertas 8388 USB devices. config LIBERTAS_CS tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" depends on LIBERTAS && PCMCIA ---help--- A driver for Marvell Libertas 8385 CompactFlash devices. config LIBERTAS_SDIO tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards" depends on LIBERTAS && MMC ---help--- A driver for Marvell Libertas 8385/8686/8688 SDIO devices. config LIBERTAS_SPI tristate "Marvell Libertas 8686 SPI 802.11b/g cards" depends on LIBERTAS && SPI ---help--- A driver for Marvell Libertas 8686 SPI devices. config LIBERTAS_DEBUG bool "Enable full debugging output in the Libertas module." depends on LIBERTAS ---help--- Debugging support. config LIBERTAS_MESH bool "Enable mesh support" depends on LIBERTAS help This enables Libertas' MESH support, used by e.g. the OLPC people. compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/0000755000175000017500000000000012026211315021754 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_usb.c0000644000175000017500000014514512026211315023420 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * Copyright (C) 2006-2007 Michael Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "zd_def.h" #include "zd_mac.h" #include "zd_usb.h" static struct usb_device_id usb_ids[] = { /* ZD1211 */ { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0ace, 0xa211), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x14ea, 0xab10), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x3207), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0409, 0x0248), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x054c, 0x0257), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0xe501), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x2019, 0xed01), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, {} }; MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip."); MODULE_AUTHOR("Ulrich Kunitz"); MODULE_AUTHOR("Daniel Drake"); MODULE_VERSION("1.0"); MODULE_DEVICE_TABLE(usb, usb_ids); #define FW_ZD1211_PREFIX "zd1211/zd1211_" #define FW_ZD1211B_PREFIX "zd1211/zd1211b_" static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, unsigned int count); /* USB device initialization */ static void int_urb_complete(struct urb *urb); static int request_fw_file( const struct firmware **fw, const char *name, struct device *device) { int r; dev_dbg_f(device, "fw name %s\n", name); r = request_firmware(fw, name, device); if (r) dev_err(device, "Could not load firmware file %s. Error number %d\n", name, r); return r; } static inline u16 get_bcdDevice(const struct usb_device *udev) { return le16_to_cpu(udev->descriptor.bcdDevice); } enum upload_code_flags { REBOOT = 1, }; /* Ensures that MAX_TRANSFER_SIZE is even. */ #define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1) static int upload_code(struct usb_device *udev, const u8 *data, size_t size, u16 code_offset, int flags) { u8 *p; int r; /* USB request blocks need "kmalloced" buffers. */ p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL); if (!p) { dev_err(&udev->dev, "out of memory\n"); r = -ENOMEM; goto error; } size &= ~1; while (size > 0) { size_t transfer_size = size <= MAX_TRANSFER_SIZE ? size : MAX_TRANSFER_SIZE; dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size); memcpy(p, data, transfer_size); r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_FIRMWARE_DOWNLOAD, USB_DIR_OUT | USB_TYPE_VENDOR, code_offset, 0, p, transfer_size, 1000 /* ms */); if (r < 0) { dev_err(&udev->dev, "USB control request for firmware upload" " failed. Error number %d\n", r); goto error; } transfer_size = r & ~1; size -= transfer_size; data += transfer_size; code_offset += transfer_size/sizeof(u16); } if (flags & REBOOT) { u8 ret; /* Use "DMA-aware" buffer. */ r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), USB_REQ_FIRMWARE_CONFIRM, USB_DIR_IN | USB_TYPE_VENDOR, 0, 0, p, sizeof(ret), 5000 /* ms */); if (r != sizeof(ret)) { dev_err(&udev->dev, "control request firmeware confirmation failed." " Return value %d\n", r); if (r >= 0) r = -ENODEV; goto error; } ret = p[0]; if (ret & 0x80) { dev_err(&udev->dev, "Internal error while downloading." " Firmware confirm return value %#04x\n", (unsigned int)ret); r = -ENODEV; goto error; } dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n", (unsigned int)ret); } r = 0; error: kfree(p); return r; } static u16 get_word(const void *data, u16 offset) { const __le16 *p = data; return le16_to_cpu(p[offset]); } static char *get_fw_name(struct zd_usb *usb, char *buffer, size_t size, const char* postfix) { scnprintf(buffer, size, "%s%s", usb->is_zd1211b ? FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX, postfix); return buffer; } static int handle_version_mismatch(struct zd_usb *usb, const struct firmware *ub_fw) { struct usb_device *udev = zd_usb_to_usbdev(usb); const struct firmware *ur_fw = NULL; int offset; int r = 0; char fw_name[128]; r = request_fw_file(&ur_fw, get_fw_name(usb, fw_name, sizeof(fw_name), "ur"), &udev->dev); if (r) goto error; r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT); if (r) goto error; offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16)); r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset, E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT); /* At this point, the vendor driver downloads the whole firmware * image, hacks around with version IDs, and uploads it again, * completely overwriting the boot code. We do not do this here as * it is not required on any tested devices, and it is suspected to * cause problems. */ error: release_firmware(ur_fw); return r; } static int upload_firmware(struct zd_usb *usb) { int r; u16 fw_bcdDevice; u16 bcdDevice; struct usb_device *udev = zd_usb_to_usbdev(usb); const struct firmware *ub_fw = NULL; const struct firmware *uph_fw = NULL; char fw_name[128]; bcdDevice = get_bcdDevice(udev); r = request_fw_file(&ub_fw, get_fw_name(usb, fw_name, sizeof(fw_name), "ub"), &udev->dev); if (r) goto error; fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET); if (fw_bcdDevice != bcdDevice) { dev_info(&udev->dev, "firmware version %#06x and device bootcode version " "%#06x differ\n", fw_bcdDevice, bcdDevice); if (bcdDevice <= 0x4313) dev_warn(&udev->dev, "device has old bootcode, please " "report success or failure\n"); r = handle_version_mismatch(usb, ub_fw); if (r) goto error; } else { dev_dbg_f(&udev->dev, "firmware device id %#06x is equal to the " "actual device id\n", fw_bcdDevice); } r = request_fw_file(&uph_fw, get_fw_name(usb, fw_name, sizeof(fw_name), "uphr"), &udev->dev); if (r) goto error; r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT); if (r) { dev_err(&udev->dev, "Could not upload firmware code uph. Error number %d\n", r); } /* FALL-THROUGH */ error: release_firmware(ub_fw); release_firmware(uph_fw); return r; } MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ur"); MODULE_FIRMWARE(FW_ZD1211_PREFIX "ur"); MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ub"); MODULE_FIRMWARE(FW_ZD1211_PREFIX "ub"); MODULE_FIRMWARE(FW_ZD1211B_PREFIX "uphr"); MODULE_FIRMWARE(FW_ZD1211_PREFIX "uphr"); /* Read data from device address space using "firmware interface" which does * not require firmware to be loaded. */ int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len) { int r; struct usb_device *udev = zd_usb_to_usbdev(usb); u8 *buf; /* Use "DMA-aware" buffer. */ buf = kmalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), USB_REQ_FIRMWARE_READ_DATA, USB_DIR_IN | 0x40, addr, 0, buf, len, 5000); if (r < 0) { dev_err(&udev->dev, "read over firmware interface failed: %d\n", r); goto exit; } else if (r != len) { dev_err(&udev->dev, "incomplete read over firmware interface: %d/%d\n", r, len); r = -EIO; goto exit; } r = 0; memcpy(data, buf, len); exit: kfree(buf); return r; } #define urb_dev(urb) (&(urb)->dev->dev) static inline void handle_regs_int_override(struct urb *urb) { struct zd_usb *usb = urb->context; struct zd_usb_interrupt *intr = &usb->intr; spin_lock(&intr->lock); if (atomic_read(&intr->read_regs_enabled)) { atomic_set(&intr->read_regs_enabled, 0); intr->read_regs_int_overridden = 1; complete(&intr->read_regs.completion); } spin_unlock(&intr->lock); } static inline void handle_regs_int(struct urb *urb) { struct zd_usb *usb = urb->context; struct zd_usb_interrupt *intr = &usb->intr; int len; u16 int_num; ZD_ASSERT(in_interrupt()); spin_lock(&intr->lock); int_num = le16_to_cpu(*(__le16 *)(urb->transfer_buffer+2)); if (int_num == CR_INTERRUPT) { struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context)); spin_lock(&mac->lock); memcpy(&mac->intr_buffer, urb->transfer_buffer, USB_MAX_EP_INT_BUFFER); spin_unlock(&mac->lock); schedule_work(&mac->process_intr); } else if (atomic_read(&intr->read_regs_enabled)) { len = urb->actual_length; intr->read_regs.length = urb->actual_length; if (len > sizeof(intr->read_regs.buffer)) len = sizeof(intr->read_regs.buffer); memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); /* Sometimes USB_INT_ID_REGS is not overridden, but comes after * USB_INT_ID_RETRY_FAILED. Read-reg retry then gets this * delayed USB_INT_ID_REGS, but leaves USB_INT_ID_REGS of * retry unhandled. Next read-reg command then might catch * this wrong USB_INT_ID_REGS. Fix by ignoring wrong reads. */ if (!check_read_regs(usb, intr->read_regs.req, intr->read_regs.req_count)) goto out; atomic_set(&intr->read_regs_enabled, 0); intr->read_regs_int_overridden = 0; complete(&intr->read_regs.completion); goto out; } out: spin_unlock(&intr->lock); /* CR_INTERRUPT might override read_reg too. */ if (int_num == CR_INTERRUPT && atomic_read(&intr->read_regs_enabled)) handle_regs_int_override(urb); } static void int_urb_complete(struct urb *urb) { int r; struct usb_int_header *hdr; struct zd_usb *usb; struct zd_usb_interrupt *intr; switch (urb->status) { case 0: break; case -ESHUTDOWN: case -EINVAL: case -ENODEV: case -ENOENT: case -ECONNRESET: case -EPIPE: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); return; default: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); goto resubmit; } if (urb->actual_length < sizeof(hdr)) { dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb); goto resubmit; } hdr = urb->transfer_buffer; if (hdr->type != USB_INT_TYPE) { dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb); goto resubmit; } /* USB_INT_ID_RETRY_FAILED triggered by tx-urb submit can override * pending USB_INT_ID_REGS causing read command timeout. */ usb = urb->context; intr = &usb->intr; if (hdr->id != USB_INT_ID_REGS && atomic_read(&intr->read_regs_enabled)) handle_regs_int_override(urb); switch (hdr->id) { case USB_INT_ID_REGS: handle_regs_int(urb); break; case USB_INT_ID_RETRY_FAILED: zd_mac_tx_failed(urb); break; default: dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb, (unsigned int)hdr->id); goto resubmit; } resubmit: r = usb_submit_urb(urb, GFP_ATOMIC); if (r) { dev_dbg_f(urb_dev(urb), "error: resubmit urb %p err code %d\n", urb, r); /* TODO: add worker to reset intr->urb */ } return; } static inline int int_urb_interval(struct usb_device *udev) { switch (udev->speed) { case USB_SPEED_HIGH: return 4; case USB_SPEED_LOW: return 10; case USB_SPEED_FULL: default: return 1; } } static inline int usb_int_enabled(struct zd_usb *usb) { unsigned long flags; struct zd_usb_interrupt *intr = &usb->intr; struct urb *urb; spin_lock_irqsave(&intr->lock, flags); urb = intr->urb; spin_unlock_irqrestore(&intr->lock, flags); return urb != NULL; } int zd_usb_enable_int(struct zd_usb *usb) { int r; struct usb_device *udev = zd_usb_to_usbdev(usb); struct zd_usb_interrupt *intr = &usb->intr; struct urb *urb; dev_dbg_f(zd_usb_dev(usb), "\n"); urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { r = -ENOMEM; goto out; } ZD_ASSERT(!irqs_disabled()); spin_lock_irq(&intr->lock); if (intr->urb) { spin_unlock_irq(&intr->lock); r = 0; goto error_free_urb; } intr->urb = urb; spin_unlock_irq(&intr->lock); r = -ENOMEM; intr->buffer = usb_alloc_coherent(udev, USB_MAX_EP_INT_BUFFER, GFP_KERNEL, &intr->buffer_dma); if (!intr->buffer) { dev_dbg_f(zd_usb_dev(usb), "couldn't allocate transfer_buffer\n"); goto error_set_urb_null; } usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN), intr->buffer, USB_MAX_EP_INT_BUFFER, int_urb_complete, usb, intr->interval); urb->transfer_dma = intr->buffer_dma; urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); r = usb_submit_urb(urb, GFP_KERNEL); if (r) { dev_dbg_f(zd_usb_dev(usb), "Couldn't submit urb. Error number %d\n", r); goto error; } return 0; error: usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, intr->buffer, intr->buffer_dma); error_set_urb_null: spin_lock_irq(&intr->lock); intr->urb = NULL; spin_unlock_irq(&intr->lock); error_free_urb: usb_free_urb(urb); out: return r; } void zd_usb_disable_int(struct zd_usb *usb) { unsigned long flags; struct usb_device *udev = zd_usb_to_usbdev(usb); struct zd_usb_interrupt *intr = &usb->intr; struct urb *urb; void *buffer; dma_addr_t buffer_dma; spin_lock_irqsave(&intr->lock, flags); urb = intr->urb; if (!urb) { spin_unlock_irqrestore(&intr->lock, flags); return; } intr->urb = NULL; buffer = intr->buffer; buffer_dma = intr->buffer_dma; intr->buffer = NULL; spin_unlock_irqrestore(&intr->lock, flags); usb_kill_urb(urb); dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb); usb_free_urb(urb); if (buffer) usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, buffer, buffer_dma); } static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, unsigned int length) { int i; const struct rx_length_info *length_info; if (length < sizeof(struct rx_length_info)) { /* It's not a complete packet anyhow. */ dev_dbg_f(zd_usb_dev(usb), "invalid, small RX packet : %d\n", length); return; } length_info = (struct rx_length_info *) (buffer + length - sizeof(struct rx_length_info)); /* It might be that three frames are merged into a single URB * transaction. We have to check for the length info tag. * * While testing we discovered that length_info might be unaligned, * because if USB transactions are merged, the last packet will not * be padded. Unaligned access might also happen if the length_info * structure is not present. */ if (get_unaligned_le16(&length_info->tag) == RX_LENGTH_INFO_TAG) { unsigned int l, k, n; for (i = 0, l = 0;; i++) { k = get_unaligned_le16(&length_info->length[i]); if (k == 0) return; n = l+k; if (n > length) return; zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k); if (i >= 2) return; l = (n+3) & ~3; } } else { zd_mac_rx(zd_usb_to_hw(usb), buffer, length); } } static void rx_urb_complete(struct urb *urb) { int r; struct zd_usb *usb; struct zd_usb_rx *rx; const u8 *buffer; unsigned int length; switch (urb->status) { case 0: break; case -ESHUTDOWN: case -EINVAL: case -ENODEV: case -ENOENT: case -ECONNRESET: case -EPIPE: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); return; default: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); goto resubmit; } buffer = urb->transfer_buffer; length = urb->actual_length; usb = urb->context; rx = &usb->rx; tasklet_schedule(&rx->reset_timer_tasklet); if (length%rx->usb_packet_size > rx->usb_packet_size-4) { /* If there is an old first fragment, we don't care. */ dev_dbg_f(urb_dev(urb), "*** first fragment ***\n"); ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment)); spin_lock(&rx->lock); memcpy(rx->fragment, buffer, length); rx->fragment_length = length; spin_unlock(&rx->lock); goto resubmit; } spin_lock(&rx->lock); if (rx->fragment_length > 0) { /* We are on a second fragment, we believe */ ZD_ASSERT(length + rx->fragment_length <= ARRAY_SIZE(rx->fragment)); dev_dbg_f(urb_dev(urb), "*** second fragment ***\n"); memcpy(rx->fragment+rx->fragment_length, buffer, length); handle_rx_packet(usb, rx->fragment, rx->fragment_length + length); rx->fragment_length = 0; spin_unlock(&rx->lock); } else { spin_unlock(&rx->lock); handle_rx_packet(usb, buffer, length); } resubmit: r = usb_submit_urb(urb, GFP_ATOMIC); if (r) dev_dbg_f(urb_dev(urb), "urb %p resubmit error %d\n", urb, r); } static struct urb *alloc_rx_urb(struct zd_usb *usb) { struct usb_device *udev = zd_usb_to_usbdev(usb); struct urb *urb; void *buffer; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return NULL; buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL, &urb->transfer_dma); if (!buffer) { usb_free_urb(urb); return NULL; } usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN), buffer, USB_MAX_RX_SIZE, rx_urb_complete, usb); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; return urb; } static void free_rx_urb(struct urb *urb) { if (!urb) return; usb_free_coherent(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); usb_free_urb(urb); } static int __zd_usb_enable_rx(struct zd_usb *usb) { int i, r; struct zd_usb_rx *rx = &usb->rx; struct urb **urbs; dev_dbg_f(zd_usb_dev(usb), "\n"); r = -ENOMEM; urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); if (!urbs) goto error; for (i = 0; i < RX_URBS_COUNT; i++) { urbs[i] = alloc_rx_urb(usb); if (!urbs[i]) goto error; } ZD_ASSERT(!irqs_disabled()); spin_lock_irq(&rx->lock); if (rx->urbs) { spin_unlock_irq(&rx->lock); r = 0; goto error; } rx->urbs = urbs; rx->urbs_count = RX_URBS_COUNT; spin_unlock_irq(&rx->lock); for (i = 0; i < RX_URBS_COUNT; i++) { r = usb_submit_urb(urbs[i], GFP_KERNEL); if (r) goto error_submit; } return 0; error_submit: for (i = 0; i < RX_URBS_COUNT; i++) { usb_kill_urb(urbs[i]); } spin_lock_irq(&rx->lock); rx->urbs = NULL; rx->urbs_count = 0; spin_unlock_irq(&rx->lock); error: if (urbs) { for (i = 0; i < RX_URBS_COUNT; i++) free_rx_urb(urbs[i]); } return r; } int zd_usb_enable_rx(struct zd_usb *usb) { int r; struct zd_usb_rx *rx = &usb->rx; mutex_lock(&rx->setup_mutex); r = __zd_usb_enable_rx(usb); mutex_unlock(&rx->setup_mutex); zd_usb_reset_rx_idle_timer(usb); return r; } static void __zd_usb_disable_rx(struct zd_usb *usb) { int i; unsigned long flags; struct urb **urbs; unsigned int count; struct zd_usb_rx *rx = &usb->rx; spin_lock_irqsave(&rx->lock, flags); urbs = rx->urbs; count = rx->urbs_count; spin_unlock_irqrestore(&rx->lock, flags); if (!urbs) return; for (i = 0; i < count; i++) { usb_kill_urb(urbs[i]); free_rx_urb(urbs[i]); } kfree(urbs); spin_lock_irqsave(&rx->lock, flags); rx->urbs = NULL; rx->urbs_count = 0; spin_unlock_irqrestore(&rx->lock, flags); } void zd_usb_disable_rx(struct zd_usb *usb) { struct zd_usb_rx *rx = &usb->rx; mutex_lock(&rx->setup_mutex); __zd_usb_disable_rx(usb); mutex_unlock(&rx->setup_mutex); tasklet_kill(&rx->reset_timer_tasklet); cancel_delayed_work_sync(&rx->idle_work); } static void zd_usb_reset_rx(struct zd_usb *usb) { bool do_reset; struct zd_usb_rx *rx = &usb->rx; unsigned long flags; mutex_lock(&rx->setup_mutex); spin_lock_irqsave(&rx->lock, flags); do_reset = rx->urbs != NULL; spin_unlock_irqrestore(&rx->lock, flags); if (do_reset) { __zd_usb_disable_rx(usb); __zd_usb_enable_rx(usb); } mutex_unlock(&rx->setup_mutex); if (do_reset) zd_usb_reset_rx_idle_timer(usb); } /** * zd_usb_disable_tx - disable transmission * @usb: the zd1211rw-private USB structure * * Frees all URBs in the free list and marks the transmission as disabled. */ void zd_usb_disable_tx(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; unsigned long flags; atomic_set(&tx->enabled, 0); /* kill all submitted tx-urbs */ usb_kill_anchored_urbs(&tx->submitted); spin_lock_irqsave(&tx->lock, flags); WARN_ON(!skb_queue_empty(&tx->submitted_skbs)); WARN_ON(tx->submitted_urbs != 0); tx->submitted_urbs = 0; spin_unlock_irqrestore(&tx->lock, flags); /* The stopped state is ignored, relying on ieee80211_wake_queues() * in a potentionally following zd_usb_enable_tx(). */ } /** * zd_usb_enable_tx - enables transmission * @usb: a &struct zd_usb pointer * * This function enables transmission and prepares the &zd_usb_tx data * structure. */ void zd_usb_enable_tx(struct zd_usb *usb) { unsigned long flags; struct zd_usb_tx *tx = &usb->tx; spin_lock_irqsave(&tx->lock, flags); atomic_set(&tx->enabled, 1); tx->submitted_urbs = 0; ieee80211_wake_queues(zd_usb_to_hw(usb)); tx->stopped = 0; spin_unlock_irqrestore(&tx->lock, flags); } static void tx_dec_submitted_urbs(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; unsigned long flags; spin_lock_irqsave(&tx->lock, flags); --tx->submitted_urbs; if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) { ieee80211_wake_queues(zd_usb_to_hw(usb)); tx->stopped = 0; } spin_unlock_irqrestore(&tx->lock, flags); } static void tx_inc_submitted_urbs(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; unsigned long flags; spin_lock_irqsave(&tx->lock, flags); ++tx->submitted_urbs; if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) { ieee80211_stop_queues(zd_usb_to_hw(usb)); tx->stopped = 1; } spin_unlock_irqrestore(&tx->lock, flags); } /** * tx_urb_complete - completes the execution of an URB * @urb: a URB * * This function is called if the URB has been transferred to a device or an * error has happened. */ static void tx_urb_complete(struct urb *urb) { int r; struct sk_buff *skb; struct ieee80211_tx_info *info; struct zd_usb *usb; struct zd_usb_tx *tx; skb = (struct sk_buff *)urb->context; info = IEEE80211_SKB_CB(skb); /* * grab 'usb' pointer before handing off the skb (since * it might be freed by zd_mac_tx_to_dev or mac80211) */ usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb; tx = &usb->tx; switch (urb->status) { case 0: break; case -ESHUTDOWN: case -EINVAL: case -ENODEV: case -ENOENT: case -ECONNRESET: case -EPIPE: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); break; default: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); goto resubmit; } free_urb: skb_unlink(skb, &usb->tx.submitted_skbs); zd_mac_tx_to_dev(skb, urb->status); usb_free_urb(urb); tx_dec_submitted_urbs(usb); return; resubmit: usb_anchor_urb(urb, &tx->submitted); r = usb_submit_urb(urb, GFP_ATOMIC); if (r) { usb_unanchor_urb(urb); dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); goto free_urb; } } /** * zd_usb_tx: initiates transfer of a frame of the device * * @usb: the zd1211rw-private USB structure * @skb: a &struct sk_buff pointer * * This function tranmits a frame to the device. It doesn't wait for * completion. The frame must contain the control set and have all the * control set information available. * * The function returns 0 if the transfer has been successfully initiated. */ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb) { int r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct usb_device *udev = zd_usb_to_usbdev(usb); struct urb *urb; struct zd_usb_tx *tx = &usb->tx; if (!atomic_read(&tx->enabled)) { r = -ENOENT; goto out; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { r = -ENOMEM; goto out; } usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), skb->data, skb->len, tx_urb_complete, skb); info->rate_driver_data[1] = (void *)jiffies; skb_queue_tail(&tx->submitted_skbs, skb); usb_anchor_urb(urb, &tx->submitted); r = usb_submit_urb(urb, GFP_ATOMIC); if (r) { dev_dbg_f(zd_usb_dev(usb), "error submit urb %p %d\n", urb, r); usb_unanchor_urb(urb); skb_unlink(skb, &tx->submitted_skbs); goto error; } tx_inc_submitted_urbs(usb); return 0; error: usb_free_urb(urb); out: return r; } static bool zd_tx_timeout(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; struct sk_buff_head *q = &tx->submitted_skbs; struct sk_buff *skb, *skbnext; struct ieee80211_tx_info *info; unsigned long flags, trans_start; bool have_timedout = false; spin_lock_irqsave(&q->lock, flags); skb_queue_walk_safe(q, skb, skbnext) { info = IEEE80211_SKB_CB(skb); trans_start = (unsigned long)info->rate_driver_data[1]; if (time_is_before_jiffies(trans_start + ZD_TX_TIMEOUT)) { have_timedout = true; break; } } spin_unlock_irqrestore(&q->lock, flags); return have_timedout; } static void zd_tx_watchdog_handler(struct work_struct *work) { struct zd_usb *usb = container_of(work, struct zd_usb, tx.watchdog_work.work); struct zd_usb_tx *tx = &usb->tx; if (!atomic_read(&tx->enabled) || !tx->watchdog_enabled) goto out; if (!zd_tx_timeout(usb)) goto out; /* TX halted, try reset */ dev_warn(zd_usb_dev(usb), "TX-stall detected, resetting device..."); usb_queue_reset_device(usb->intf); /* reset will stop this worker, don't rearm */ return; out: queue_delayed_work(zd_workqueue, &tx->watchdog_work, ZD_TX_WATCHDOG_INTERVAL); } void zd_tx_watchdog_enable(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; if (!tx->watchdog_enabled) { dev_dbg_f(zd_usb_dev(usb), "\n"); queue_delayed_work(zd_workqueue, &tx->watchdog_work, ZD_TX_WATCHDOG_INTERVAL); tx->watchdog_enabled = 1; } } void zd_tx_watchdog_disable(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; if (tx->watchdog_enabled) { dev_dbg_f(zd_usb_dev(usb), "\n"); tx->watchdog_enabled = 0; cancel_delayed_work_sync(&tx->watchdog_work); } } static void zd_rx_idle_timer_handler(struct work_struct *work) { struct zd_usb *usb = container_of(work, struct zd_usb, rx.idle_work.work); struct zd_mac *mac = zd_usb_to_mac(usb); if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) return; dev_dbg_f(zd_usb_dev(usb), "\n"); /* 30 seconds since last rx, reset rx */ zd_usb_reset_rx(usb); } static void zd_usb_reset_rx_idle_timer_tasklet(unsigned long param) { struct zd_usb *usb = (struct zd_usb *)param; zd_usb_reset_rx_idle_timer(usb); } void zd_usb_reset_rx_idle_timer(struct zd_usb *usb) { struct zd_usb_rx *rx = &usb->rx; mod_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL); } static inline void init_usb_interrupt(struct zd_usb *usb) { struct zd_usb_interrupt *intr = &usb->intr; spin_lock_init(&intr->lock); intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); init_completion(&intr->read_regs.completion); atomic_set(&intr->read_regs_enabled, 0); intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); } static inline void init_usb_rx(struct zd_usb *usb) { struct zd_usb_rx *rx = &usb->rx; spin_lock_init(&rx->lock); mutex_init(&rx->setup_mutex); if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) { rx->usb_packet_size = 512; } else { rx->usb_packet_size = 64; } ZD_ASSERT(rx->fragment_length == 0); INIT_DELAYED_WORK(&rx->idle_work, zd_rx_idle_timer_handler); rx->reset_timer_tasklet.func = zd_usb_reset_rx_idle_timer_tasklet; rx->reset_timer_tasklet.data = (unsigned long)usb; } static inline void init_usb_tx(struct zd_usb *usb) { struct zd_usb_tx *tx = &usb->tx; spin_lock_init(&tx->lock); atomic_set(&tx->enabled, 0); tx->stopped = 0; skb_queue_head_init(&tx->submitted_skbs); init_usb_anchor(&tx->submitted); tx->submitted_urbs = 0; tx->watchdog_enabled = 0; INIT_DELAYED_WORK(&tx->watchdog_work, zd_tx_watchdog_handler); } void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, struct usb_interface *intf) { memset(usb, 0, sizeof(*usb)); usb->intf = usb_get_intf(intf); usb_set_intfdata(usb->intf, hw); init_usb_anchor(&usb->submitted_cmds); init_usb_interrupt(usb); init_usb_tx(usb); init_usb_rx(usb); } void zd_usb_clear(struct zd_usb *usb) { usb_set_intfdata(usb->intf, NULL); usb_put_intf(usb->intf); ZD_MEMCLEAR(usb, sizeof(*usb)); /* FIXME: usb_interrupt, usb_tx, usb_rx? */ } static const char *speed(enum usb_device_speed speed) { switch (speed) { case USB_SPEED_LOW: return "low"; case USB_SPEED_FULL: return "full"; case USB_SPEED_HIGH: return "high"; default: return "unknown speed"; } } static int scnprint_id(struct usb_device *udev, char *buffer, size_t size) { return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), get_bcdDevice(udev), speed(udev->speed)); } int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size) { struct usb_device *udev = interface_to_usbdev(usb->intf); return scnprint_id(udev, buffer, size); } #ifdef DEBUG static void print_id(struct usb_device *udev) { char buffer[40]; scnprint_id(udev, buffer, sizeof(buffer)); buffer[sizeof(buffer)-1] = 0; dev_dbg_f(&udev->dev, "%s\n", buffer); } #else #define print_id(udev) do { } while (0) #endif static int eject_installer(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *iface_desc = &intf->altsetting[0]; struct usb_endpoint_descriptor *endpoint; unsigned char *cmd; u8 bulk_out_ep; int r; /* Find bulk out endpoint */ for (r = 1; r >= 0; r--) { endpoint = &iface_desc->endpoint[r].desc; if (usb_endpoint_dir_out(endpoint) && usb_endpoint_xfer_bulk(endpoint)) { bulk_out_ep = endpoint->bEndpointAddress; break; } } if (r == -1) { dev_err(&udev->dev, "zd1211rw: Could not find bulk out endpoint\n"); return -ENODEV; } cmd = kzalloc(31, GFP_KERNEL); if (cmd == NULL) return -ENODEV; /* USB bulk command block */ cmd[0] = 0x55; /* bulk command signature */ cmd[1] = 0x53; /* bulk command signature */ cmd[2] = 0x42; /* bulk command signature */ cmd[3] = 0x43; /* bulk command signature */ cmd[14] = 6; /* command length */ cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */ cmd[19] = 0x2; /* eject disc */ dev_info(&udev->dev, "Ejecting virtual installer media...\n"); r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), cmd, 31, NULL, 2000); kfree(cmd); if (r) return r; /* At this point, the device disconnects and reconnects with the real * ID numbers. */ usb_set_intfdata(intf, NULL); return 0; } int zd_usb_init_hw(struct zd_usb *usb) { int r; struct zd_mac *mac = zd_usb_to_mac(usb); dev_dbg_f(zd_usb_dev(usb), "\n"); r = upload_firmware(usb); if (r) { dev_err(zd_usb_dev(usb), "couldn't load firmware. Error number %d\n", r); return r; } r = usb_reset_configuration(zd_usb_to_usbdev(usb)); if (r) { dev_dbg_f(zd_usb_dev(usb), "couldn't reset configuration. Error number %d\n", r); return r; } r = zd_mac_init_hw(mac->hw); if (r) { dev_dbg_f(zd_usb_dev(usb), "couldn't initialize mac. Error number %d\n", r); return r; } usb->initialized = 1; return 0; } static int probe(struct usb_interface *intf, const struct usb_device_id *id) { int r; struct usb_device *udev = interface_to_usbdev(intf); struct zd_usb *usb; struct ieee80211_hw *hw = NULL; print_id(udev); if (id->driver_info & DEVICE_INSTALLER) return eject_installer(intf); switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: break; default: dev_dbg_f(&intf->dev, "Unknown USB speed\n"); r = -ENODEV; goto error; } r = usb_reset_device(udev); if (r) { dev_err(&intf->dev, "couldn't reset usb device. Error number %d\n", r); goto error; } hw = zd_mac_alloc_hw(intf); if (hw == NULL) { r = -ENOMEM; goto error; } usb = &zd_hw_mac(hw)->chip.usb; usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0; r = zd_mac_preinit_hw(hw); if (r) { dev_dbg_f(&intf->dev, "couldn't initialize mac. Error number %d\n", r); goto error; } r = ieee80211_register_hw(hw); if (r) { dev_dbg_f(&intf->dev, "couldn't register device. Error number %d\n", r); goto error; } dev_dbg_f(&intf->dev, "successful\n"); dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy)); return 0; error: usb_reset_device(interface_to_usbdev(intf)); if (hw) { zd_mac_clear(zd_hw_mac(hw)); ieee80211_free_hw(hw); } return r; } static void disconnect(struct usb_interface *intf) { struct ieee80211_hw *hw = zd_intf_to_hw(intf); struct zd_mac *mac; struct zd_usb *usb; /* Either something really bad happened, or we're just dealing with * a DEVICE_INSTALLER. */ if (hw == NULL) return; mac = zd_hw_mac(hw); usb = &mac->chip.usb; dev_dbg_f(zd_usb_dev(usb), "\n"); ieee80211_unregister_hw(hw); /* Just in case something has gone wrong! */ zd_usb_disable_tx(usb); zd_usb_disable_rx(usb); zd_usb_disable_int(usb); /* If the disconnect has been caused by a removal of the * driver module, the reset allows reloading of the driver. If the * reset will not be executed here, the upload of the firmware in the * probe function caused by the reloading of the driver will fail. */ usb_reset_device(interface_to_usbdev(intf)); zd_mac_clear(mac); ieee80211_free_hw(hw); dev_dbg(&intf->dev, "disconnected\n"); } static void zd_usb_resume(struct zd_usb *usb) { struct zd_mac *mac = zd_usb_to_mac(usb); int r; dev_dbg_f(zd_usb_dev(usb), "\n"); r = zd_op_start(zd_usb_to_hw(usb)); if (r < 0) { dev_warn(zd_usb_dev(usb), "Device resume failed " "with error code %d. Retrying...\n", r); if (usb->was_running) set_bit(ZD_DEVICE_RUNNING, &mac->flags); usb_queue_reset_device(usb->intf); return; } if (mac->type != NL80211_IFTYPE_UNSPECIFIED) { r = zd_restore_settings(mac); if (r < 0) { dev_dbg(zd_usb_dev(usb), "failed to restore settings, %d\n", r); return; } } } static void zd_usb_stop(struct zd_usb *usb) { dev_dbg_f(zd_usb_dev(usb), "\n"); zd_op_stop(zd_usb_to_hw(usb)); zd_usb_disable_tx(usb); zd_usb_disable_rx(usb); zd_usb_disable_int(usb); usb->initialized = 0; } static int pre_reset(struct usb_interface *intf) { struct ieee80211_hw *hw = usb_get_intfdata(intf); struct zd_mac *mac; struct zd_usb *usb; if (!hw || intf->condition != USB_INTERFACE_BOUND) return 0; mac = zd_hw_mac(hw); usb = &mac->chip.usb; usb->was_running = test_bit(ZD_DEVICE_RUNNING, &mac->flags); zd_usb_stop(usb); mutex_lock(&mac->chip.mutex); return 0; } static int post_reset(struct usb_interface *intf) { struct ieee80211_hw *hw = usb_get_intfdata(intf); struct zd_mac *mac; struct zd_usb *usb; if (!hw || intf->condition != USB_INTERFACE_BOUND) return 0; mac = zd_hw_mac(hw); usb = &mac->chip.usb; mutex_unlock(&mac->chip.mutex); if (usb->was_running) zd_usb_resume(usb); return 0; } static struct usb_driver driver = { .name = KBUILD_MODNAME, .id_table = usb_ids, .probe = probe, .disconnect = disconnect, .pre_reset = pre_reset, .post_reset = post_reset, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; struct workqueue_struct *zd_workqueue; static int __init usb_init(void) { int r; pr_debug("%s usb_init()\n", driver.name); zd_workqueue = create_singlethread_workqueue(driver.name); if (zd_workqueue == NULL) { printk(KERN_ERR "%s couldn't create workqueue\n", driver.name); return -ENOMEM; } r = usb_register(&driver); if (r) { destroy_workqueue(zd_workqueue); printk(KERN_ERR "%s usb_register() failed. Error number %d\n", driver.name, r); return r; } pr_debug("%s initialized\n", driver.name); return 0; } static void __exit usb_exit(void) { pr_debug("%s usb_exit()\n", driver.name); usb_deregister(&driver); destroy_workqueue(zd_workqueue); } module_init(usb_init); module_exit(usb_exit); static int zd_ep_regs_out_msg(struct usb_device *udev, void *data, int len, int *actual_length, int timeout) { /* In USB 2.0 mode EP_REGS_OUT endpoint is interrupt type. However in * USB 1.1 mode endpoint is bulk. Select correct type URB by endpoint * descriptor. */ struct usb_host_endpoint *ep; unsigned int pipe; pipe = usb_sndintpipe(udev, EP_REGS_OUT); ep = usb_pipe_endpoint(udev, pipe); if (!ep) return -EINVAL; if (usb_endpoint_xfer_int(&ep->desc)) { return usb_interrupt_msg(udev, pipe, data, len, actual_length, timeout); } else { pipe = usb_sndbulkpipe(udev, EP_REGS_OUT); return usb_bulk_msg(udev, pipe, data, len, actual_length, timeout); } } static int usb_int_regs_length(unsigned int count) { return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); } static void prepare_read_regs_int(struct zd_usb *usb, struct usb_req_read_regs *req, unsigned int count) { struct zd_usb_interrupt *intr = &usb->intr; spin_lock_irq(&intr->lock); atomic_set(&intr->read_regs_enabled, 1); intr->read_regs.req = req; intr->read_regs.req_count = count; INIT_COMPLETION(intr->read_regs.completion); spin_unlock_irq(&intr->lock); } static void disable_read_regs_int(struct zd_usb *usb) { struct zd_usb_interrupt *intr = &usb->intr; spin_lock_irq(&intr->lock); atomic_set(&intr->read_regs_enabled, 0); spin_unlock_irq(&intr->lock); } static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, unsigned int count) { int i; struct zd_usb_interrupt *intr = &usb->intr; struct read_regs_int *rr = &intr->read_regs; struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; /* The created block size seems to be larger than expected. * However results appear to be correct. */ if (rr->length < usb_int_regs_length(count)) { dev_dbg_f(zd_usb_dev(usb), "error: actual length %d less than expected %d\n", rr->length, usb_int_regs_length(count)); return false; } if (rr->length > sizeof(rr->buffer)) { dev_dbg_f(zd_usb_dev(usb), "error: actual length %d exceeds buffer size %zu\n", rr->length, sizeof(rr->buffer)); return false; } for (i = 0; i < count; i++) { struct reg_data *rd = ®s->regs[i]; if (rd->addr != req->addr[i]) { dev_dbg_f(zd_usb_dev(usb), "rd[%d] addr %#06hx expected %#06hx\n", i, le16_to_cpu(rd->addr), le16_to_cpu(req->addr[i])); return false; } } return true; } static int get_results(struct zd_usb *usb, u16 *values, struct usb_req_read_regs *req, unsigned int count, bool *retry) { int r; int i; struct zd_usb_interrupt *intr = &usb->intr; struct read_regs_int *rr = &intr->read_regs; struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; spin_lock_irq(&intr->lock); r = -EIO; /* Read failed because firmware bug? */ *retry = !!intr->read_regs_int_overridden; if (*retry) goto error_unlock; if (!check_read_regs(usb, req, count)) { dev_dbg_f(zd_usb_dev(usb), "error: invalid read regs\n"); goto error_unlock; } for (i = 0; i < count; i++) { struct reg_data *rd = ®s->regs[i]; values[i] = le16_to_cpu(rd->value); } r = 0; error_unlock: spin_unlock_irq(&intr->lock); return r; } int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, const zd_addr_t *addresses, unsigned int count) { int r, i, req_len, actual_req_len, try_count = 0; struct usb_device *udev; struct usb_req_read_regs *req = NULL; unsigned long timeout; bool retry = false; if (count < 1) { dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); return -EINVAL; } if (count > USB_MAX_IOREAD16_COUNT) { dev_dbg_f(zd_usb_dev(usb), "error: count %u exceeds possible max %u\n", count, USB_MAX_IOREAD16_COUNT); return -EINVAL; } if (in_atomic()) { dev_dbg_f(zd_usb_dev(usb), "error: io in atomic context not supported\n"); return -EWOULDBLOCK; } if (!usb_int_enabled(usb)) { dev_dbg_f(zd_usb_dev(usb), "error: usb interrupt not enabled\n"); return -EWOULDBLOCK; } ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); BUILD_BUG_ON(sizeof(struct usb_req_read_regs) + USB_MAX_IOREAD16_COUNT * sizeof(__le16) > sizeof(usb->req_buf)); BUG_ON(sizeof(struct usb_req_read_regs) + count * sizeof(__le16) > sizeof(usb->req_buf)); req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); req = (void *)usb->req_buf; req->id = cpu_to_le16(USB_REQ_READ_REGS); for (i = 0; i < count; i++) req->addr[i] = cpu_to_le16((u16)addresses[i]); retry_read: try_count++; udev = zd_usb_to_usbdev(usb); prepare_read_regs_int(usb, req, count); r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); if (r) { dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg(). Error number %d\n", r); goto error; } if (req_len != actual_req_len) { dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()\n" " req_len %d != actual_req_len %d\n", req_len, actual_req_len); r = -EIO; goto error; } timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion, msecs_to_jiffies(50)); if (!timeout) { disable_read_regs_int(usb); dev_dbg_f(zd_usb_dev(usb), "read timed out\n"); r = -ETIMEDOUT; goto error; } r = get_results(usb, values, req, count, &retry); if (retry && try_count < 20) { dev_dbg_f(zd_usb_dev(usb), "read retry, tries so far: %d\n", try_count); goto retry_read; } error: return r; } static void iowrite16v_urb_complete(struct urb *urb) { struct zd_usb *usb = urb->context; if (urb->status && !usb->cmd_error) usb->cmd_error = urb->status; if (!usb->cmd_error && urb->actual_length != urb->transfer_buffer_length) usb->cmd_error = -EIO; } static int zd_submit_waiting_urb(struct zd_usb *usb, bool last) { int r = 0; struct urb *urb = usb->urb_async_waiting; if (!urb) return 0; usb->urb_async_waiting = NULL; if (!last) urb->transfer_flags |= URB_NO_INTERRUPT; usb_anchor_urb(urb, &usb->submitted_cmds); r = usb_submit_urb(urb, GFP_KERNEL); if (r) { usb_unanchor_urb(urb); dev_dbg_f(zd_usb_dev(usb), "error in usb_submit_urb(). Error number %d\n", r); goto error; } /* fall-through with r == 0 */ error: usb_free_urb(urb); return r; } void zd_usb_iowrite16v_async_start(struct zd_usb *usb) { ZD_ASSERT(usb_anchor_empty(&usb->submitted_cmds)); ZD_ASSERT(usb->urb_async_waiting == NULL); ZD_ASSERT(!usb->in_async); ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); usb->in_async = 1; usb->cmd_error = 0; usb->urb_async_waiting = NULL; } int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout) { int r; ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); ZD_ASSERT(usb->in_async); /* Submit last iowrite16v URB */ r = zd_submit_waiting_urb(usb, true); if (r) { dev_dbg_f(zd_usb_dev(usb), "error in zd_submit_waiting_usb(). " "Error number %d\n", r); usb_kill_anchored_urbs(&usb->submitted_cmds); goto error; } if (timeout) timeout = usb_wait_anchor_empty_timeout(&usb->submitted_cmds, timeout); if (!timeout) { usb_kill_anchored_urbs(&usb->submitted_cmds); if (usb->cmd_error == -ENOENT) { dev_dbg_f(zd_usb_dev(usb), "timed out"); r = -ETIMEDOUT; goto error; } } r = usb->cmd_error; error: usb->in_async = 0; return r; } int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, unsigned int count) { int r; struct usb_device *udev; struct usb_req_write_regs *req = NULL; int i, req_len; struct urb *urb; struct usb_host_endpoint *ep; ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); ZD_ASSERT(usb->in_async); if (count == 0) return 0; if (count > USB_MAX_IOWRITE16_COUNT) { dev_dbg_f(zd_usb_dev(usb), "error: count %u exceeds possible max %u\n", count, USB_MAX_IOWRITE16_COUNT); return -EINVAL; } if (in_atomic()) { dev_dbg_f(zd_usb_dev(usb), "error: io in atomic context not supported\n"); return -EWOULDBLOCK; } udev = zd_usb_to_usbdev(usb); ep = usb_pipe_endpoint(udev, usb_sndintpipe(udev, EP_REGS_OUT)); if (!ep) return -ENOENT; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; req_len = sizeof(struct usb_req_write_regs) + count * sizeof(struct reg_data); req = kmalloc(req_len, GFP_KERNEL); if (!req) { r = -ENOMEM; goto error; } req->id = cpu_to_le16(USB_REQ_WRITE_REGS); for (i = 0; i < count; i++) { struct reg_data *rw = &req->reg_writes[i]; rw->addr = cpu_to_le16((u16)ioreqs[i].addr); rw->value = cpu_to_le16(ioreqs[i].value); } /* In USB 2.0 mode endpoint is interrupt type. However in USB 1.1 mode * endpoint is bulk. Select correct type URB by endpoint descriptor. */ if (usb_endpoint_xfer_int(&ep->desc)) usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT), req, req_len, iowrite16v_urb_complete, usb, ep->desc.bInterval); else usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_REGS_OUT), req, req_len, iowrite16v_urb_complete, usb); urb->transfer_flags |= URB_FREE_BUFFER; /* Submit previous URB */ r = zd_submit_waiting_urb(usb, false); if (r) { dev_dbg_f(zd_usb_dev(usb), "error in zd_submit_waiting_usb(). " "Error number %d\n", r); goto error; } /* Delay submit so that URB_NO_INTERRUPT flag can be set for all URBs * of currect batch except for very last. */ usb->urb_async_waiting = urb; return 0; error: usb_free_urb(urb); return r; } int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, unsigned int count) { int r; zd_usb_iowrite16v_async_start(usb); r = zd_usb_iowrite16v_async(usb, ioreqs, count); if (r) { zd_usb_iowrite16v_async_end(usb, 0); return r; } return zd_usb_iowrite16v_async_end(usb, 50 /* ms */); } int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) { int r; struct usb_device *udev; struct usb_req_rfwrite *req = NULL; int i, req_len, actual_req_len; u16 bit_value_template; if (in_atomic()) { dev_dbg_f(zd_usb_dev(usb), "error: io in atomic context not supported\n"); return -EWOULDBLOCK; } if (bits < USB_MIN_RFWRITE_BIT_COUNT) { dev_dbg_f(zd_usb_dev(usb), "error: bits %d are smaller than" " USB_MIN_RFWRITE_BIT_COUNT %d\n", bits, USB_MIN_RFWRITE_BIT_COUNT); return -EINVAL; } if (bits > USB_MAX_RFWRITE_BIT_COUNT) { dev_dbg_f(zd_usb_dev(usb), "error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n", bits, USB_MAX_RFWRITE_BIT_COUNT); return -EINVAL; } #ifdef DEBUG if (value & (~0UL << bits)) { dev_dbg_f(zd_usb_dev(usb), "error: value %#09x has bits >= %d set\n", value, bits); return -EINVAL; } #endif /* DEBUG */ dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits); r = zd_usb_ioread16(usb, &bit_value_template, ZD_CR203); if (r) { dev_dbg_f(zd_usb_dev(usb), "error %d: Couldn't read ZD_CR203\n", r); return r; } bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); BUILD_BUG_ON(sizeof(struct usb_req_rfwrite) + USB_MAX_RFWRITE_BIT_COUNT * sizeof(__le16) > sizeof(usb->req_buf)); BUG_ON(sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16) > sizeof(usb->req_buf)); req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); req = (void *)usb->req_buf; req->id = cpu_to_le16(USB_REQ_WRITE_RF); /* 1: 3683a, but not used in ZYDAS driver */ req->value = cpu_to_le16(2); req->bits = cpu_to_le16(bits); for (i = 0; i < bits; i++) { u16 bv = bit_value_template; if (value & (1 << (bits-1-i))) bv |= RF_DATA; req->bit_values[i] = cpu_to_le16(bv); } udev = zd_usb_to_usbdev(usb); r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); if (r) { dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg(). Error number %d\n", r); goto out; } if (req_len != actual_req_len) { dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()" " req_len %d != actual_req_len %d\n", req_len, actual_req_len); r = -EIO; goto out; } /* FALL-THROUGH with r == 0 */ out: return r; } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_mac.c0000644000175000017500000012153712026211315023366 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * Copyright (C) 2006-2007 Michael Wu * Copyright (C) 2007-2008 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "zd_def.h" #include "zd_chip.h" #include "zd_mac.h" #include "zd_rf.h" struct zd_reg_alpha2_map { u32 reg; char alpha2[2]; }; static struct zd_reg_alpha2_map reg_alpha2_map[] = { { ZD_REGDOMAIN_FCC, "US" }, { ZD_REGDOMAIN_IC, "CA" }, { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */ { ZD_REGDOMAIN_JAPAN, "JP" }, { ZD_REGDOMAIN_JAPAN_2, "JP" }, { ZD_REGDOMAIN_JAPAN_3, "JP" }, { ZD_REGDOMAIN_SPAIN, "ES" }, { ZD_REGDOMAIN_FRANCE, "FR" }, }; /* This table contains the hardware specific values for the modulation rates. */ static const struct ieee80211_rate zd_rates[] = { { .bitrate = 10, .hw_value = ZD_CCK_RATE_1M, }, { .bitrate = 20, .hw_value = ZD_CCK_RATE_2M, .hw_value_short = ZD_CCK_RATE_2M | ZD_CCK_PREA_SHORT, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = ZD_CCK_RATE_5_5M, .hw_value_short = ZD_CCK_RATE_5_5M | ZD_CCK_PREA_SHORT, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .hw_value = ZD_CCK_RATE_11M, .hw_value_short = ZD_CCK_RATE_11M | ZD_CCK_PREA_SHORT, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = ZD_OFDM_RATE_6M, .flags = 0 }, { .bitrate = 90, .hw_value = ZD_OFDM_RATE_9M, .flags = 0 }, { .bitrate = 120, .hw_value = ZD_OFDM_RATE_12M, .flags = 0 }, { .bitrate = 180, .hw_value = ZD_OFDM_RATE_18M, .flags = 0 }, { .bitrate = 240, .hw_value = ZD_OFDM_RATE_24M, .flags = 0 }, { .bitrate = 360, .hw_value = ZD_OFDM_RATE_36M, .flags = 0 }, { .bitrate = 480, .hw_value = ZD_OFDM_RATE_48M, .flags = 0 }, { .bitrate = 540, .hw_value = ZD_OFDM_RATE_54M, .flags = 0 }, }; /* * Zydas retry rates table. Each line is listed in the same order as * in zd_rates[] and contains all the rate used when a packet is sent * starting with a given rates. Let's consider an example : * * "11 Mbits : 4, 3, 2, 1, 0" means : * - packet is sent using 4 different rates * - 1st rate is index 3 (ie 11 Mbits) * - 2nd rate is index 2 (ie 5.5 Mbits) * - 3rd rate is index 1 (ie 2 Mbits) * - 4th rate is index 0 (ie 1 Mbits) */ static const struct tx_retry_rate zd_retry_rates[] = { { /* 1 Mbits */ 1, { 0 }}, { /* 2 Mbits */ 2, { 1, 0 }}, { /* 5.5 Mbits */ 3, { 2, 1, 0 }}, { /* 11 Mbits */ 4, { 3, 2, 1, 0 }}, { /* 6 Mbits */ 5, { 4, 3, 2, 1, 0 }}, { /* 9 Mbits */ 6, { 5, 4, 3, 2, 1, 0}}, { /* 12 Mbits */ 5, { 6, 3, 2, 1, 0 }}, { /* 18 Mbits */ 6, { 7, 6, 3, 2, 1, 0 }}, { /* 24 Mbits */ 6, { 8, 6, 3, 2, 1, 0 }}, { /* 36 Mbits */ 7, { 9, 8, 6, 3, 2, 1, 0 }}, { /* 48 Mbits */ 8, {10, 9, 8, 6, 3, 2, 1, 0 }}, { /* 54 Mbits */ 9, {11, 10, 9, 8, 6, 3, 2, 1, 0 }} }; static const struct ieee80211_channel zd_channels[] = { { .center_freq = 2412, .hw_value = 1 }, { .center_freq = 2417, .hw_value = 2 }, { .center_freq = 2422, .hw_value = 3 }, { .center_freq = 2427, .hw_value = 4 }, { .center_freq = 2432, .hw_value = 5 }, { .center_freq = 2437, .hw_value = 6 }, { .center_freq = 2442, .hw_value = 7 }, { .center_freq = 2447, .hw_value = 8 }, { .center_freq = 2452, .hw_value = 9 }, { .center_freq = 2457, .hw_value = 10 }, { .center_freq = 2462, .hw_value = 11 }, { .center_freq = 2467, .hw_value = 12 }, { .center_freq = 2472, .hw_value = 13 }, { .center_freq = 2484, .hw_value = 14 }, }; static void housekeeping_init(struct zd_mac *mac); static void housekeeping_enable(struct zd_mac *mac); static void housekeeping_disable(struct zd_mac *mac); static void beacon_init(struct zd_mac *mac); static void beacon_enable(struct zd_mac *mac); static void beacon_disable(struct zd_mac *mac); static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble); static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon, bool in_intr); static int zd_reg2alpha2(u8 regdomain, char *alpha2) { unsigned int i; struct zd_reg_alpha2_map *reg_map; for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) { reg_map = ®_alpha2_map[i]; if (regdomain == reg_map->reg) { alpha2[0] = reg_map->alpha2[0]; alpha2[1] = reg_map->alpha2[1]; return 0; } } return 1; } static int zd_check_signal(struct ieee80211_hw *hw, int signal) { struct zd_mac *mac = zd_hw_mac(hw); dev_dbg_f_cond(zd_mac_dev(mac), signal < 0 || signal > 100, "%s: signal value from device not in range 0..100, " "but %d.\n", __func__, signal); if (signal < 0) signal = 0; else if (signal > 100) signal = 100; return signal; } int zd_mac_preinit_hw(struct ieee80211_hw *hw) { int r; u8 addr[ETH_ALEN]; struct zd_mac *mac = zd_hw_mac(hw); r = zd_chip_read_mac_addr_fw(&mac->chip, addr); if (r) return r; SET_IEEE80211_PERM_ADDR(hw, addr); return 0; } int zd_mac_init_hw(struct ieee80211_hw *hw) { int r; struct zd_mac *mac = zd_hw_mac(hw); struct zd_chip *chip = &mac->chip; char alpha2[2]; u8 default_regdomain; r = zd_chip_enable_int(chip); if (r) goto out; r = zd_chip_init_hw(chip); if (r) goto disable_int; ZD_ASSERT(!irqs_disabled()); r = zd_read_regdomain(chip, &default_regdomain); if (r) goto disable_int; spin_lock_irq(&mac->lock); mac->regdomain = mac->default_regdomain = default_regdomain; spin_unlock_irq(&mac->lock); /* We must inform the device that we are doing encryption/decryption in * software at the moment. */ r = zd_set_encryption_type(chip, ENC_SNIFFER); if (r) goto disable_int; r = zd_reg2alpha2(mac->regdomain, alpha2); if (r) goto disable_int; r = regulatory_hint(hw->wiphy, alpha2); disable_int: zd_chip_disable_int(chip); out: return r; } void zd_mac_clear(struct zd_mac *mac) { flush_workqueue(zd_workqueue); zd_chip_clear(&mac->chip); ZD_ASSERT(!spin_is_locked(&mac->lock)); ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); } static int set_rx_filter(struct zd_mac *mac) { unsigned long flags; u32 filter = STA_RX_FILTER; spin_lock_irqsave(&mac->lock, flags); if (mac->pass_ctrl) filter |= RX_FILTER_CTRL; spin_unlock_irqrestore(&mac->lock, flags); return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); } static int set_mac_and_bssid(struct zd_mac *mac) { int r; if (!mac->vif) return -1; r = zd_write_mac_addr(&mac->chip, mac->vif->addr); if (r) return r; /* Vendor driver after setting MAC either sets BSSID for AP or * filter for other modes. */ if (mac->type != NL80211_IFTYPE_AP) return set_rx_filter(mac); else return zd_write_bssid(&mac->chip, mac->vif->addr); } static int set_mc_hash(struct zd_mac *mac) { struct zd_mc_hash hash; zd_mc_clear(&hash); return zd_chip_set_multicast_hash(&mac->chip, &hash); } int zd_op_start(struct ieee80211_hw *hw) { struct zd_mac *mac = zd_hw_mac(hw); struct zd_chip *chip = &mac->chip; struct zd_usb *usb = &chip->usb; int r; if (!usb->initialized) { r = zd_usb_init_hw(usb); if (r) goto out; } r = zd_chip_enable_int(chip); if (r < 0) goto out; r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G); if (r < 0) goto disable_int; r = set_rx_filter(mac); if (r) goto disable_int; r = set_mc_hash(mac); if (r) goto disable_int; /* Wait after setting the multicast hash table and powering on * the radio otherwise interface bring up will fail. This matches * what the vendor driver did. */ msleep(10); r = zd_chip_switch_radio_on(chip); if (r < 0) { dev_err(zd_chip_dev(chip), "%s: failed to set radio on\n", __func__); goto disable_int; } r = zd_chip_enable_rxtx(chip); if (r < 0) goto disable_radio; r = zd_chip_enable_hwint(chip); if (r < 0) goto disable_rxtx; housekeeping_enable(mac); beacon_enable(mac); set_bit(ZD_DEVICE_RUNNING, &mac->flags); return 0; disable_rxtx: zd_chip_disable_rxtx(chip); disable_radio: zd_chip_switch_radio_off(chip); disable_int: zd_chip_disable_int(chip); out: return r; } void zd_op_stop(struct ieee80211_hw *hw) { struct zd_mac *mac = zd_hw_mac(hw); struct zd_chip *chip = &mac->chip; struct sk_buff *skb; struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue; clear_bit(ZD_DEVICE_RUNNING, &mac->flags); /* The order here deliberately is a little different from the open() * method, since we need to make sure there is no opportunity for RX * frames to be processed by mac80211 after we have stopped it. */ zd_chip_disable_rxtx(chip); beacon_disable(mac); housekeeping_disable(mac); flush_workqueue(zd_workqueue); zd_chip_disable_hwint(chip); zd_chip_switch_radio_off(chip); zd_chip_disable_int(chip); while ((skb = skb_dequeue(ack_wait_queue))) dev_kfree_skb_any(skb); } int zd_restore_settings(struct zd_mac *mac) { struct sk_buff *beacon; struct zd_mc_hash multicast_hash; unsigned int short_preamble; int r, beacon_interval, beacon_period; u8 channel; dev_dbg_f(zd_mac_dev(mac), "\n"); spin_lock_irq(&mac->lock); multicast_hash = mac->multicast_hash; short_preamble = mac->short_preamble; beacon_interval = mac->beacon.interval; beacon_period = mac->beacon.period; channel = mac->channel; spin_unlock_irq(&mac->lock); r = set_mac_and_bssid(mac); if (r < 0) { dev_dbg_f(zd_mac_dev(mac), "set_mac_and_bssid failed, %d\n", r); return r; } r = zd_chip_set_channel(&mac->chip, channel); if (r < 0) { dev_dbg_f(zd_mac_dev(mac), "zd_chip_set_channel failed, %d\n", r); return r; } set_rts_cts(mac, short_preamble); r = zd_chip_set_multicast_hash(&mac->chip, &multicast_hash); if (r < 0) { dev_dbg_f(zd_mac_dev(mac), "zd_chip_set_multicast_hash failed, %d\n", r); return r; } if (mac->type == NL80211_IFTYPE_MESH_POINT || mac->type == NL80211_IFTYPE_ADHOC || mac->type == NL80211_IFTYPE_AP) { if (mac->vif != NULL) { beacon = ieee80211_beacon_get(mac->hw, mac->vif); if (beacon) zd_mac_config_beacon(mac->hw, beacon, false); } zd_set_beacon_interval(&mac->chip, beacon_interval, beacon_period, mac->type); spin_lock_irq(&mac->lock); mac->beacon.last_update = jiffies; spin_unlock_irq(&mac->lock); } return 0; } /** * zd_mac_tx_status - reports tx status of a packet if required * @hw - a &struct ieee80211_hw pointer * @skb - a sk-buffer * @flags: extra flags to set in the TX status info * @ackssi: ACK signal strength * @success - True for successful transmission of the frame * * This information calls ieee80211_tx_status_irqsafe() if required by the * control information. It copies the control information into the status * information. * * If no status information has been requested, the skb is freed. */ static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, int ackssi, struct tx_status *tx_status) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int i; int success = 1, retry = 1; int first_idx; const struct tx_retry_rate *retries; ieee80211_tx_info_clear_status(info); if (tx_status) { success = !tx_status->failure; retry = tx_status->retry + success; } if (success) { /* success */ info->flags |= IEEE80211_TX_STAT_ACK; } else { /* failure */ info->flags &= ~IEEE80211_TX_STAT_ACK; } first_idx = info->status.rates[0].idx; ZD_ASSERT(0<=first_idx && first_idxcount); info->status.rates[0].idx = retries->rate[0]; info->status.rates[0].count = 1; // (retry > 1 ? 2 : 1); for (i=1; istatus.rates[i].idx = retries->rate[i]; info->status.rates[i].count = 1; // ((i==retry-1) && success ? 1:2); } for (; istatus.rates[i].idx = retries->rate[retry - 1]; info->status.rates[i].count = 1; // (success ? 1:2); } if (istatus.rates[i].idx = -1; /* terminate */ info->status.ack_signal = zd_check_signal(hw, ackssi); ieee80211_tx_status_irqsafe(hw, skb); } /** * zd_mac_tx_failed - callback for failed frames * @dev: the mac80211 wireless device * * This function is called if a frame couldn't be successfully * transferred. The first frame from the tx queue, will be selected and * reported as error to the upper layers. */ void zd_mac_tx_failed(struct urb *urb) { struct ieee80211_hw * hw = zd_usb_to_hw(urb->context); struct zd_mac *mac = zd_hw_mac(hw); struct sk_buff_head *q = &mac->ack_wait_queue; struct sk_buff *skb; struct tx_status *tx_status = (struct tx_status *)urb->transfer_buffer; unsigned long flags; int success = !tx_status->failure; int retry = tx_status->retry + success; int found = 0; int i, position = 0; q = &mac->ack_wait_queue; spin_lock_irqsave(&q->lock, flags); skb_queue_walk(q, skb) { struct ieee80211_hdr *tx_hdr; struct ieee80211_tx_info *info; int first_idx, final_idx; const struct tx_retry_rate *retries; u8 final_rate; position ++; /* if the hardware reports a failure and we had a 802.11 ACK * pending, then we skip the first skb when searching for a * matching frame */ if (tx_status->failure && mac->ack_pending && skb_queue_is_first(q, skb)) { continue; } tx_hdr = (struct ieee80211_hdr *)skb->data; /* we skip all frames not matching the reported destination */ if (unlikely(memcmp(tx_hdr->addr1, tx_status->mac, ETH_ALEN))) { continue; } /* we skip all frames not matching the reported final rate */ info = IEEE80211_SKB_CB(skb); first_idx = info->status.rates[0].idx; ZD_ASSERT(0<=first_idx && first_idx retries->count) continue; final_idx = retries->rate[retry - 1]; final_rate = zd_rates[final_idx].hw_value; if (final_rate != tx_status->rate) { continue; } found = 1; break; } if (found) { for (i=1; i<=position; i++) { skb = __skb_dequeue(q); zd_mac_tx_status(hw, skb, mac->ack_pending ? mac->ack_signal : 0, i == position ? tx_status : NULL); mac->ack_pending = 0; } } spin_unlock_irqrestore(&q->lock, flags); } /** * zd_mac_tx_to_dev - callback for USB layer * @skb: a &sk_buff pointer * @error: error value, 0 if transmission successful * * Informs the MAC layer that the frame has successfully transferred to the * device. If an ACK is required and the transfer to the device has been * successful, the packets are put on the @ack_wait_queue with * the control set removed. */ void zd_mac_tx_to_dev(struct sk_buff *skb, int error) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hw *hw = info->rate_driver_data[0]; struct zd_mac *mac = zd_hw_mac(hw); ieee80211_tx_info_clear_status(info); skb_pull(skb, sizeof(struct zd_ctrlset)); if (unlikely(error || (info->flags & IEEE80211_TX_CTL_NO_ACK))) { /* * FIXME : do we need to fill in anything ? */ ieee80211_tx_status_irqsafe(hw, skb); } else { struct sk_buff_head *q = &mac->ack_wait_queue; skb_queue_tail(q, skb); while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) { zd_mac_tx_status(hw, skb_dequeue(q), mac->ack_pending ? mac->ack_signal : 0, NULL); mac->ack_pending = 0; } } } static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length) { /* ZD_PURE_RATE() must be used to remove the modulation type flag of * the zd-rate values. */ static const u8 rate_divisor[] = { [ZD_PURE_RATE(ZD_CCK_RATE_1M)] = 1, [ZD_PURE_RATE(ZD_CCK_RATE_2M)] = 2, /* Bits must be doubled. */ [ZD_PURE_RATE(ZD_CCK_RATE_5_5M)] = 11, [ZD_PURE_RATE(ZD_CCK_RATE_11M)] = 11, [ZD_PURE_RATE(ZD_OFDM_RATE_6M)] = 6, [ZD_PURE_RATE(ZD_OFDM_RATE_9M)] = 9, [ZD_PURE_RATE(ZD_OFDM_RATE_12M)] = 12, [ZD_PURE_RATE(ZD_OFDM_RATE_18M)] = 18, [ZD_PURE_RATE(ZD_OFDM_RATE_24M)] = 24, [ZD_PURE_RATE(ZD_OFDM_RATE_36M)] = 36, [ZD_PURE_RATE(ZD_OFDM_RATE_48M)] = 48, [ZD_PURE_RATE(ZD_OFDM_RATE_54M)] = 54, }; u32 bits = (u32)tx_length * 8; u32 divisor; divisor = rate_divisor[ZD_PURE_RATE(zd_rate)]; if (divisor == 0) return -EINVAL; switch (zd_rate) { case ZD_CCK_RATE_5_5M: bits = (2*bits) + 10; /* round up to the next integer */ break; case ZD_CCK_RATE_11M: if (service) { u32 t = bits % 11; *service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION; if (0 < t && t <= 3) { *service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION; } } bits += 10; /* round up to the next integer */ break; } return bits/divisor; } static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs, struct ieee80211_hdr *header, struct ieee80211_tx_info *info) { /* * CONTROL TODO: * - if backoff needed, enable bit 0 * - if burst (backoff not needed) disable bit 0 */ cs->control = 0; /* First fragment */ if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) cs->control |= ZD_CS_NEED_RANDOM_BACKOFF; /* No ACK expected (multicast, etc.) */ if (info->flags & IEEE80211_TX_CTL_NO_ACK) cs->control |= ZD_CS_NO_ACK; /* PS-POLL */ if (ieee80211_is_pspoll(header->frame_control)) cs->control |= ZD_CS_PS_POLL_FRAME; if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) cs->control |= ZD_CS_RTS; if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) cs->control |= ZD_CS_SELF_CTS; /* FIXME: Management frame? */ } static bool zd_mac_match_cur_beacon(struct zd_mac *mac, struct sk_buff *beacon) { if (!mac->beacon.cur_beacon) return false; if (mac->beacon.cur_beacon->len != beacon->len) return false; return !memcmp(beacon->data, mac->beacon.cur_beacon->data, beacon->len); } static void zd_mac_free_cur_beacon_locked(struct zd_mac *mac) { ZD_ASSERT(mutex_is_locked(&mac->chip.mutex)); kfree_skb(mac->beacon.cur_beacon); mac->beacon.cur_beacon = NULL; } static void zd_mac_free_cur_beacon(struct zd_mac *mac) { mutex_lock(&mac->chip.mutex); zd_mac_free_cur_beacon_locked(mac); mutex_unlock(&mac->chip.mutex); } static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon, bool in_intr) { struct zd_mac *mac = zd_hw_mac(hw); int r, ret, num_cmds, req_pos = 0; u32 tmp, j = 0; /* 4 more bytes for tail CRC */ u32 full_len = beacon->len + 4; unsigned long end_jiffies, message_jiffies; struct zd_ioreq32 *ioreqs; mutex_lock(&mac->chip.mutex); /* Check if hw already has this beacon. */ if (zd_mac_match_cur_beacon(mac, beacon)) { r = 0; goto out_nofree; } /* Alloc memory for full beacon write at once. */ num_cmds = 1 + zd_chip_is_zd1211b(&mac->chip) + full_len; ioreqs = kmalloc(num_cmds * sizeof(struct zd_ioreq32), GFP_KERNEL); if (!ioreqs) { r = -ENOMEM; goto out_nofree; } r = zd_iowrite32_locked(&mac->chip, 0, CR_BCN_FIFO_SEMAPHORE); if (r < 0) goto out; r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE); if (r < 0) goto release_sema; if (in_intr && tmp & 0x2) { r = -EBUSY; goto release_sema; } end_jiffies = jiffies + HZ / 2; /*~500ms*/ message_jiffies = jiffies + HZ / 10; /*~100ms*/ while (tmp & 0x2) { r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE); if (r < 0) goto release_sema; if (time_is_before_eq_jiffies(message_jiffies)) { message_jiffies = jiffies + HZ / 10; dev_err(zd_mac_dev(mac), "CR_BCN_FIFO_SEMAPHORE not ready\n"); if (time_is_before_eq_jiffies(end_jiffies)) { dev_err(zd_mac_dev(mac), "Giving up beacon config.\n"); r = -ETIMEDOUT; goto reset_device; } } msleep(20); } ioreqs[req_pos].addr = CR_BCN_FIFO; ioreqs[req_pos].value = full_len - 1; req_pos++; if (zd_chip_is_zd1211b(&mac->chip)) { ioreqs[req_pos].addr = CR_BCN_LENGTH; ioreqs[req_pos].value = full_len - 1; req_pos++; } for (j = 0 ; j < beacon->len; j++) { ioreqs[req_pos].addr = CR_BCN_FIFO; ioreqs[req_pos].value = *((u8 *)(beacon->data + j)); req_pos++; } for (j = 0; j < 4; j++) { ioreqs[req_pos].addr = CR_BCN_FIFO; ioreqs[req_pos].value = 0x0; req_pos++; } BUG_ON(req_pos != num_cmds); r = zd_iowrite32a_locked(&mac->chip, ioreqs, num_cmds); release_sema: /* * Try very hard to release device beacon semaphore, as otherwise * device/driver can be left in unusable state. */ end_jiffies = jiffies + HZ / 2; /*~500ms*/ ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); while (ret < 0) { if (in_intr || time_is_before_eq_jiffies(end_jiffies)) { ret = -ETIMEDOUT; break; } msleep(20); ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); } if (ret < 0) dev_err(zd_mac_dev(mac), "Could not release " "CR_BCN_FIFO_SEMAPHORE!\n"); if (r < 0 || ret < 0) { if (r >= 0) r = ret; /* We don't know if beacon was written successfully or not, * so clear current. */ zd_mac_free_cur_beacon_locked(mac); goto out; } /* Beacon has now been written successfully, update current. */ zd_mac_free_cur_beacon_locked(mac); mac->beacon.cur_beacon = beacon; beacon = NULL; /* 802.11b/g 2.4G CCK 1Mb * 802.11a, not yet implemented, uses different values (see GPL vendor * driver) */ r = zd_iowrite32_locked(&mac->chip, 0x00000400 | (full_len << 19), CR_BCN_PLCP_CFG); out: kfree(ioreqs); out_nofree: kfree_skb(beacon); mutex_unlock(&mac->chip.mutex); return r; reset_device: zd_mac_free_cur_beacon_locked(mac); kfree_skb(beacon); mutex_unlock(&mac->chip.mutex); kfree(ioreqs); /* semaphore stuck, reset device to avoid fw freeze later */ dev_warn(zd_mac_dev(mac), "CR_BCN_FIFO_SEMAPHORE stuck, " "resetting device..."); usb_queue_reset_device(mac->chip.usb.intf); return r; } static int fill_ctrlset(struct zd_mac *mac, struct sk_buff *skb) { int r; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; unsigned int frag_len = skb->len + FCS_LEN; unsigned int packet_length; struct ieee80211_rate *txrate; struct zd_ctrlset *cs = (struct zd_ctrlset *) skb_push(skb, sizeof(struct zd_ctrlset)); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ZD_ASSERT(frag_len <= 0xffff); /* * Firmware computes the duration itself (for all frames except PSPoll) * and needs the field set to 0 at input, otherwise firmware messes up * duration_id and sets bits 14 and 15 on. */ if (!ieee80211_is_pspoll(hdr->frame_control)) hdr->duration_id = 0; txrate = ieee80211_get_tx_rate(mac->hw, info); cs->modulation = txrate->hw_value; if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) cs->modulation = txrate->hw_value_short; cs->tx_length = cpu_to_le16(frag_len); cs_set_control(mac, cs, hdr, info); packet_length = frag_len + sizeof(struct zd_ctrlset) + 10; ZD_ASSERT(packet_length <= 0xffff); /* ZD1211B: Computing the length difference this way, gives us * flexibility to compute the packet length. */ cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ? packet_length - frag_len : packet_length); /* * CURRENT LENGTH: * - transmit frame length in microseconds * - seems to be derived from frame length * - see Cal_Us_Service() in zdinlinef.h * - if macp->bTxBurstEnable is enabled, then multiply by 4 * - bTxBurstEnable is never set in the vendor driver * * SERVICE: * - "for PLCP configuration" * - always 0 except in some situations at 802.11b 11M * - see line 53 of zdinlinef.h */ cs->service = 0; r = zd_calc_tx_length_us(&cs->service, ZD_RATE(cs->modulation), le16_to_cpu(cs->tx_length)); if (r < 0) return r; cs->current_length = cpu_to_le16(r); cs->next_frame_length = 0; return 0; } /** * zd_op_tx - transmits a network frame to the device * * @dev: mac80211 hardware device * @skb: socket buffer * @control: the control structure * * This function transmit an IEEE 802.11 network frame to the device. The * control block of the skbuff will be initialized. If necessary the incoming * mac80211 queues will be stopped. */ static void zd_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct zd_mac *mac = zd_hw_mac(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int r; r = fill_ctrlset(mac, skb); if (r) goto fail; info->rate_driver_data[0] = hw; r = zd_usb_tx(&mac->chip.usb, skb); if (r) goto fail; return; fail: dev_kfree_skb(skb); } /** * filter_ack - filters incoming packets for acknowledgements * @dev: the mac80211 device * @rx_hdr: received header * @stats: the status for the received packet * * This functions looks for ACK packets and tries to match them with the * frames in the tx queue. If a match is found the frame will be dequeued and * the upper layers is informed about the successful transmission. If * mac80211 queues have been stopped and the number of frames still to be * transmitted is low the queues will be opened again. * * Returns 1 if the frame was an ACK, 0 if it was ignored. */ static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr, struct ieee80211_rx_status *stats) { struct zd_mac *mac = zd_hw_mac(hw); struct sk_buff *skb; struct sk_buff_head *q; unsigned long flags; int found = 0; int i, position = 0; if (!ieee80211_is_ack(rx_hdr->frame_control)) return 0; q = &mac->ack_wait_queue; spin_lock_irqsave(&q->lock, flags); skb_queue_walk(q, skb) { struct ieee80211_hdr *tx_hdr; position ++; if (mac->ack_pending && skb_queue_is_first(q, skb)) continue; tx_hdr = (struct ieee80211_hdr *)skb->data; if (likely(!memcmp(tx_hdr->addr2, rx_hdr->addr1, ETH_ALEN))) { found = 1; break; } } if (found) { for (i=1; iack_pending ? mac->ack_signal : 0, NULL); mac->ack_pending = 0; } mac->ack_pending = 1; mac->ack_signal = stats->signal; /* Prevent pending tx-packet on AP-mode */ if (mac->type == NL80211_IFTYPE_AP) { skb = __skb_dequeue(q); zd_mac_tx_status(hw, skb, mac->ack_signal, NULL); mac->ack_pending = 0; } } spin_unlock_irqrestore(&q->lock, flags); return 1; } int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) { struct zd_mac *mac = zd_hw_mac(hw); struct ieee80211_rx_status stats; const struct rx_status *status; struct sk_buff *skb; int bad_frame = 0; __le16 fc; int need_padding; int i; u8 rate; if (length < ZD_PLCP_HEADER_SIZE + 10 /* IEEE80211_1ADDR_LEN */ + FCS_LEN + sizeof(struct rx_status)) return -EINVAL; memset(&stats, 0, sizeof(stats)); /* Note about pass_failed_fcs and pass_ctrl access below: * mac locking intentionally omitted here, as this is the only unlocked * reader and the only writer is configure_filter. Plus, if there were * any races accessing these variables, it wouldn't really matter. * If mac80211 ever provides a way for us to access filter flags * from outside configure_filter, we could improve on this. Also, this * situation may change once we implement some kind of DMA-into-skb * RX path. */ /* Caller has to ensure that length >= sizeof(struct rx_status). */ status = (struct rx_status *) (buffer + (length - sizeof(struct rx_status))); if (status->frame_status & ZD_RX_ERROR) { if (mac->pass_failed_fcs && (status->frame_status & ZD_RX_CRC32_ERROR)) { stats.flag |= RX_FLAG_FAILED_FCS_CRC; bad_frame = 1; } else { return -EINVAL; } } stats.freq = zd_channels[_zd_chip_get_channel(&mac->chip) - 1].center_freq; stats.band = IEEE80211_BAND_2GHZ; stats.signal = zd_check_signal(hw, status->signal_strength); rate = zd_rx_rate(buffer, status); /* todo: return index in the big switches in zd_rx_rate instead */ for (i = 0; i < mac->band.n_bitrates; i++) if (rate == mac->band.bitrates[i].hw_value) stats.rate_idx = i; length -= ZD_PLCP_HEADER_SIZE + sizeof(struct rx_status); buffer += ZD_PLCP_HEADER_SIZE; /* Except for bad frames, filter each frame to see if it is an ACK, in * which case our internal TX tracking is updated. Normally we then * bail here as there's no need to pass ACKs on up to the stack, but * there is also the case where the stack has requested us to pass * control frames on up (pass_ctrl) which we must consider. */ if (!bad_frame && filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) && !mac->pass_ctrl) return 0; fc = get_unaligned((__le16*)buffer); need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc); skb = dev_alloc_skb(length + (need_padding ? 2 : 0)); if (skb == NULL) return -ENOMEM; if (need_padding) { /* Make sure the payload data is 4 byte aligned. */ skb_reserve(skb, 2); } /* FIXME : could we avoid this big memcpy ? */ memcpy(skb_put(skb, length), buffer, length); memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); ieee80211_rx_irqsafe(hw, skb); return 0; } static int zd_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct zd_mac *mac = zd_hw_mac(hw); /* using NL80211_IFTYPE_UNSPECIFIED to indicate no mode selected */ if (mac->type != NL80211_IFTYPE_UNSPECIFIED) return -EOPNOTSUPP; switch (vif->type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: mac->type = vif->type; break; default: return -EOPNOTSUPP; } mac->vif = vif; return set_mac_and_bssid(mac); } static void zd_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct zd_mac *mac = zd_hw_mac(hw); mac->type = NL80211_IFTYPE_UNSPECIFIED; mac->vif = NULL; zd_set_beacon_interval(&mac->chip, 0, 0, NL80211_IFTYPE_UNSPECIFIED); zd_write_mac_addr(&mac->chip, NULL); zd_mac_free_cur_beacon(mac); } static int zd_op_config(struct ieee80211_hw *hw, u32 changed) { struct zd_mac *mac = zd_hw_mac(hw); struct ieee80211_conf *conf = &hw->conf; spin_lock_irq(&mac->lock); mac->channel = conf->channel->hw_value; spin_unlock_irq(&mac->lock); return zd_chip_set_channel(&mac->chip, conf->channel->hw_value); } static void zd_beacon_done(struct zd_mac *mac) { struct sk_buff *skb, *beacon; if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) return; if (!mac->vif || mac->vif->type != NL80211_IFTYPE_AP) return; /* * Send out buffered broad- and multicast frames. */ while (!ieee80211_queue_stopped(mac->hw, 0)) { skb = ieee80211_get_buffered_bc(mac->hw, mac->vif); if (!skb) break; zd_op_tx(mac->hw, NULL, skb); } /* * Fetch next beacon so that tim_count is updated. */ beacon = ieee80211_beacon_get(mac->hw, mac->vif); if (beacon) zd_mac_config_beacon(mac->hw, beacon, true); spin_lock_irq(&mac->lock); mac->beacon.last_update = jiffies; spin_unlock_irq(&mac->lock); } static void zd_process_intr(struct work_struct *work) { u16 int_status; unsigned long flags; struct zd_mac *mac = container_of(work, struct zd_mac, process_intr); spin_lock_irqsave(&mac->lock, flags); int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4)); spin_unlock_irqrestore(&mac->lock, flags); if (int_status & INT_CFG_NEXT_BCN) { /*dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");*/ zd_beacon_done(mac); } else { dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n"); } zd_chip_enable_hwint(&mac->chip); } static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *ha) #endif { struct zd_mac *mac = zd_hw_mac(hw); struct zd_mc_hash hash; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; #else int i; #endif zd_mc_clear(&hash); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->addr); zd_mc_add_addr(&hash, ha->addr); #else for (i = 0; i < mc_count; i++) { if (!ha) break; dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->dmi_addr); zd_mc_add_addr(&hash, ha->dmi_addr); ha = ha->next; #endif } return hash.low | ((u64)hash.high << 32); } #define SUPPORTED_FIF_FLAGS \ (FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \ FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC) static void zd_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 multicast) { struct zd_mc_hash hash = { .low = multicast, .high = multicast >> 32, }; struct zd_mac *mac = zd_hw_mac(hw); unsigned long flags; int r; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; /* * If multicast parameter (as returned by zd_op_prepare_multicast) * has changed, no bit in changed_flags is set. To handle this * situation, we do not return if changed_flags is 0. If we do so, * we will have some issue with IPv6 which uses multicast for link * layer address resolution. */ if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) zd_mc_add_all(&hash); spin_lock_irqsave(&mac->lock, flags); mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); mac->multicast_hash = hash; spin_unlock_irqrestore(&mac->lock, flags); zd_chip_set_multicast_hash(&mac->chip, &hash); if (changed_flags & FIF_CONTROL) { r = set_rx_filter(mac); if (r) dev_err(zd_mac_dev(mac), "set_rx_filter error %d\n", r); } /* no handling required for FIF_OTHER_BSS as we don't currently * do BSSID filtering */ /* FIXME: in future it would be nice to enable the probe response * filter (so that the driver doesn't see them) until * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd * have to schedule work to enable prbresp reception, which might * happen too late. For now we'll just listen and forward them all the * time. */ } static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble) { mutex_lock(&mac->chip.mutex); zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble); mutex_unlock(&mac->chip.mutex); } static void zd_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct zd_mac *mac = zd_hw_mac(hw); int associated; dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); if (mac->type == NL80211_IFTYPE_MESH_POINT || mac->type == NL80211_IFTYPE_ADHOC || mac->type == NL80211_IFTYPE_AP) { associated = true; if (changes & BSS_CHANGED_BEACON) { struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); if (beacon) { zd_chip_disable_hwint(&mac->chip); zd_mac_config_beacon(hw, beacon, false); zd_chip_enable_hwint(&mac->chip); } } if (changes & BSS_CHANGED_BEACON_ENABLED) { u16 interval = 0; u8 period = 0; if (bss_conf->enable_beacon) { period = bss_conf->dtim_period; interval = bss_conf->beacon_int; } spin_lock_irq(&mac->lock); mac->beacon.period = period; mac->beacon.interval = interval; mac->beacon.last_update = jiffies; spin_unlock_irq(&mac->lock); zd_set_beacon_interval(&mac->chip, interval, period, mac->type); } } else associated = is_valid_ether_addr(bss_conf->bssid); spin_lock_irq(&mac->lock); mac->associated = associated; spin_unlock_irq(&mac->lock); /* TODO: do hardware bssid filtering */ if (changes & BSS_CHANGED_ERP_PREAMBLE) { spin_lock_irq(&mac->lock); mac->short_preamble = bss_conf->use_short_preamble; spin_unlock_irq(&mac->lock); set_rts_cts(mac, bss_conf->use_short_preamble); } } static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct zd_mac *mac = zd_hw_mac(hw); return zd_chip_get_tsf(&mac->chip); } static const struct ieee80211_ops zd_ops = { .tx = zd_op_tx, .start = zd_op_start, .stop = zd_op_stop, .add_interface = zd_op_add_interface, .remove_interface = zd_op_remove_interface, .config = zd_op_config, .prepare_multicast = zd_op_prepare_multicast, .configure_filter = zd_op_configure_filter, .bss_info_changed = zd_op_bss_info_changed, .get_tsf = zd_op_get_tsf, }; struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) { struct zd_mac *mac; struct ieee80211_hw *hw; hw = ieee80211_alloc_hw(sizeof(struct zd_mac), &zd_ops); if (!hw) { dev_dbg_f(&intf->dev, "out of memory\n"); return NULL; } mac = zd_hw_mac(hw); memset(mac, 0, sizeof(*mac)); spin_lock_init(&mac->lock); mac->hw = hw; mac->type = NL80211_IFTYPE_UNSPECIFIED; memcpy(mac->channels, zd_channels, sizeof(zd_channels)); memcpy(mac->rates, zd_rates, sizeof(zd_rates)); mac->band.n_bitrates = ARRAY_SIZE(zd_rates); mac->band.bitrates = mac->rates; mac->band.n_channels = ARRAY_SIZE(zd_channels); mac->band.channels = mac->channels; hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band; hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_UNSPEC | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_MFP_CAPABLE; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); hw->max_signal = 100; hw->queues = 1; hw->extra_tx_headroom = sizeof(struct zd_ctrlset); /* * Tell mac80211 that we support multi rate retries */ hw->max_rates = IEEE80211_TX_MAX_RATES; hw->max_rate_tries = 18; /* 9 rates * 2 retries/rate */ skb_queue_head_init(&mac->ack_wait_queue); mac->ack_pending = 0; zd_chip_init(&mac->chip, hw, intf); housekeeping_init(mac); beacon_init(mac); INIT_WORK(&mac->process_intr, zd_process_intr); SET_IEEE80211_DEV(hw, &intf->dev); return hw; } #define BEACON_WATCHDOG_DELAY round_jiffies_relative(HZ) static void beacon_watchdog_handler(struct work_struct *work) { struct zd_mac *mac = container_of(work, struct zd_mac, beacon.watchdog_work.work); struct sk_buff *beacon; unsigned long timeout; int interval, period; if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) goto rearm; if (mac->type != NL80211_IFTYPE_AP || !mac->vif) goto rearm; spin_lock_irq(&mac->lock); interval = mac->beacon.interval; period = mac->beacon.period; timeout = mac->beacon.last_update + msecs_to_jiffies(interval * 1024 / 1000) * 3; spin_unlock_irq(&mac->lock); if (interval > 0 && time_is_before_jiffies(timeout)) { dev_dbg_f(zd_mac_dev(mac), "beacon interrupt stalled, " "restarting. " "(interval: %d, dtim: %d)\n", interval, period); zd_chip_disable_hwint(&mac->chip); beacon = ieee80211_beacon_get(mac->hw, mac->vif); if (beacon) { zd_mac_free_cur_beacon(mac); zd_mac_config_beacon(mac->hw, beacon, false); } zd_set_beacon_interval(&mac->chip, interval, period, mac->type); zd_chip_enable_hwint(&mac->chip); spin_lock_irq(&mac->lock); mac->beacon.last_update = jiffies; spin_unlock_irq(&mac->lock); } rearm: queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, BEACON_WATCHDOG_DELAY); } static void beacon_init(struct zd_mac *mac) { INIT_DELAYED_WORK(&mac->beacon.watchdog_work, beacon_watchdog_handler); } static void beacon_enable(struct zd_mac *mac) { dev_dbg_f(zd_mac_dev(mac), "\n"); mac->beacon.last_update = jiffies; queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, BEACON_WATCHDOG_DELAY); } static void beacon_disable(struct zd_mac *mac) { dev_dbg_f(zd_mac_dev(mac), "\n"); cancel_delayed_work_sync(&mac->beacon.watchdog_work); zd_mac_free_cur_beacon(mac); } #define LINK_LED_WORK_DELAY HZ static void link_led_handler(struct work_struct *work) { struct zd_mac *mac = container_of(work, struct zd_mac, housekeeping.link_led_work.work); struct zd_chip *chip = &mac->chip; int is_associated; int r; if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) goto requeue; spin_lock_irq(&mac->lock); is_associated = mac->associated; spin_unlock_irq(&mac->lock); r = zd_chip_control_leds(chip, is_associated ? ZD_LED_ASSOCIATED : ZD_LED_SCANNING); if (r) dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r); requeue: queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, LINK_LED_WORK_DELAY); } static void housekeeping_init(struct zd_mac *mac) { INIT_DELAYED_WORK(&mac->housekeeping.link_led_work, link_led_handler); } static void housekeeping_enable(struct zd_mac *mac) { dev_dbg_f(zd_mac_dev(mac), "\n"); queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, 0); } static void housekeeping_disable(struct zd_mac *mac) { dev_dbg_f(zd_mac_dev(mac), "\n"); cancel_delayed_work_sync(&mac->housekeeping.link_led_work); zd_chip_control_leds(&mac->chip, ZD_LED_OFF); } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/Makefile0000644000175000017500000000033112026211315023411 0ustar mcgrofmcgrofobj-$(CONFIG_COMPAT_ZD1211RW) += zd1211rw.o zd1211rw-objs := zd_chip.o zd_mac.o \ zd_rf_al2230.o zd_rf_rf2959.o \ zd_rf_al7230b.o zd_rf_uw2453.o \ zd_rf.o zd_usb.o ccflags-$(CONFIG_ZD1211RW_DEBUG) := -DDEBUG compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_usb.h0000644000175000017500000001657012026211315023424 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _ZD_USB_H #define _ZD_USB_H #include #include #include #include #include #include "zd_def.h" #define ZD_USB_TX_HIGH 5 #define ZD_USB_TX_LOW 2 #define ZD_TX_TIMEOUT (HZ * 5) #define ZD_TX_WATCHDOG_INTERVAL round_jiffies_relative(HZ) #define ZD_RX_IDLE_INTERVAL round_jiffies_relative(30 * HZ) enum devicetype { DEVICE_ZD1211 = 0, DEVICE_ZD1211B = 1, DEVICE_INSTALLER = 2, }; enum endpoints { EP_CTRL = 0, EP_DATA_OUT = 1, EP_DATA_IN = 2, EP_INT_IN = 3, EP_REGS_OUT = 4, }; enum { USB_MAX_TRANSFER_SIZE = 4096, /* bytes */ /* FIXME: The original driver uses this value. We have to check, * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be * used if one combined frame is split over two USB transactions. */ USB_MAX_RX_SIZE = 4800, /* bytes */ USB_MAX_IOWRITE16_COUNT = 15, USB_MAX_IOWRITE32_COUNT = USB_MAX_IOWRITE16_COUNT/2, USB_MAX_IOREAD16_COUNT = 15, USB_MAX_IOREAD32_COUNT = USB_MAX_IOREAD16_COUNT/2, USB_MIN_RFWRITE_BIT_COUNT = 16, USB_MAX_RFWRITE_BIT_COUNT = 28, USB_MAX_EP_INT_BUFFER = 64, USB_ZD1211B_BCD_DEVICE = 0x4810, }; enum control_requests { USB_REQ_WRITE_REGS = 0x21, USB_REQ_READ_REGS = 0x22, USB_REQ_WRITE_RF = 0x23, USB_REQ_PROG_FLASH = 0x24, USB_REQ_EEPROM_START = 0x0128, /* ? request is a byte */ USB_REQ_EEPROM_MID = 0x28, USB_REQ_EEPROM_END = 0x0228, /* ? request is a byte */ USB_REQ_FIRMWARE_DOWNLOAD = 0x30, USB_REQ_FIRMWARE_CONFIRM = 0x31, USB_REQ_FIRMWARE_READ_DATA = 0x32, }; struct usb_req_read_regs { __le16 id; __le16 addr[0]; } __packed; struct reg_data { __le16 addr; __le16 value; } __packed; struct usb_req_write_regs { __le16 id; struct reg_data reg_writes[0]; } __packed; enum { RF_IF_LE = 0x02, RF_CLK = 0x04, RF_DATA = 0x08, }; struct usb_req_rfwrite { __le16 id; __le16 value; /* 1: 3683a */ /* 2: other (default) */ __le16 bits; /* RF2595: 24 */ __le16 bit_values[0]; /* (ZD_CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */ } __packed; /* USB interrupt */ enum usb_int_id { USB_INT_TYPE = 0x01, USB_INT_ID_REGS = 0x90, USB_INT_ID_RETRY_FAILED = 0xa0, }; enum usb_int_flags { USB_INT_READ_REGS_EN = 0x01, }; struct usb_int_header { u8 type; /* must always be 1 */ u8 id; } __packed; struct usb_int_regs { struct usb_int_header hdr; struct reg_data regs[0]; } __packed; struct usb_int_retry_fail { struct usb_int_header hdr; u8 new_rate; u8 _dummy; u8 addr[ETH_ALEN]; u8 ibss_wakeup_dest; } __packed; struct read_regs_int { struct completion completion; struct usb_req_read_regs *req; unsigned int req_count; /* Stores the USB int structure and contains the USB address of the * first requested register before request. */ u8 buffer[USB_MAX_EP_INT_BUFFER]; int length; __le16 cr_int_addr; }; struct zd_ioreq16 { zd_addr_t addr; u16 value; }; struct zd_ioreq32 { zd_addr_t addr; u32 value; }; struct zd_usb_interrupt { struct read_regs_int read_regs; spinlock_t lock; struct urb *urb; void *buffer; dma_addr_t buffer_dma; int interval; atomic_t read_regs_enabled; u8 read_regs_int_overridden:1; }; static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) { return (struct usb_int_regs *)intr->read_regs.buffer; } #define RX_URBS_COUNT 5 struct zd_usb_rx { spinlock_t lock; struct mutex setup_mutex; struct delayed_work idle_work; struct tasklet_struct reset_timer_tasklet; u8 fragment[2 * USB_MAX_RX_SIZE]; unsigned int fragment_length; unsigned int usb_packet_size; struct urb **urbs; int urbs_count; }; /** * struct zd_usb_tx - structure used for transmitting frames * @enabled: atomic enabled flag, indicates whether tx is enabled * @lock: lock for transmission * @submitted: anchor for URBs sent to device * @submitted_urbs: atomic integer that counts the URBs having sent to the * device, which haven't been completed * @stopped: indicates whether higher level tx queues are stopped */ struct zd_usb_tx { atomic_t enabled; spinlock_t lock; struct delayed_work watchdog_work; struct sk_buff_head submitted_skbs; struct usb_anchor submitted; int submitted_urbs; u8 stopped:1, watchdog_enabled:1; }; /* Contains the usb parts. The structure doesn't require a lock because intf * will not be changed after initialization. */ struct zd_usb { struct zd_usb_interrupt intr; struct zd_usb_rx rx; struct zd_usb_tx tx; struct usb_interface *intf; struct usb_anchor submitted_cmds; struct urb *urb_async_waiting; int cmd_error; u8 req_buf[64]; /* zd_usb_iowrite16v needs 62 bytes */ u8 is_zd1211b:1, initialized:1, was_running:1, in_async:1; }; #define zd_usb_dev(usb) (&usb->intf->dev) static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb) { return interface_to_usbdev(usb->intf); } static inline struct ieee80211_hw *zd_intf_to_hw(struct usb_interface *intf) { return usb_get_intfdata(intf); } static inline struct ieee80211_hw *zd_usb_to_hw(struct zd_usb *usb) { return zd_intf_to_hw(usb->intf); } void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, struct usb_interface *intf); int zd_usb_init_hw(struct zd_usb *usb); void zd_usb_clear(struct zd_usb *usb); int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size); void zd_tx_watchdog_enable(struct zd_usb *usb); void zd_tx_watchdog_disable(struct zd_usb *usb); int zd_usb_enable_int(struct zd_usb *usb); void zd_usb_disable_int(struct zd_usb *usb); int zd_usb_enable_rx(struct zd_usb *usb); void zd_usb_disable_rx(struct zd_usb *usb); void zd_usb_reset_rx_idle_timer(struct zd_usb *usb); void zd_usb_enable_tx(struct zd_usb *usb); void zd_usb_disable_tx(struct zd_usb *usb); int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb); int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, const zd_addr_t *addresses, unsigned int count); static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value, const zd_addr_t addr) { return zd_usb_ioread16v(usb, value, &addr, 1); } void zd_usb_iowrite16v_async_start(struct zd_usb *usb); int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout); int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, unsigned int count); int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, unsigned int count); int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits); int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len); extern struct workqueue_struct *zd_workqueue; #endif /* _ZD_USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c0000644000175000017500000003663512026211315024272 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "zd_rf.h" #include "zd_usb.h" #include "zd_chip.h" /* This RF programming code is based upon the code found in v2.16.0.0 of the * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs * for this RF on their website, so we're able to understand more than * usual as to what is going on. Thumbs up for Ubec for doing that. */ /* The 3-wire serial interface provides access to 8 write-only registers. * The data format is a 4 bit register address followed by a 20 bit value. */ #define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff)) /* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth * fractional divide ratio) and 3 (VCO config). * * We configure the RF to produce an interrupt when the PLL is locked onto * the configured frequency. During initialization, we run through a variety * of different VCO configurations on channel 1 until we detect a PLL lock. * When this happens, we remember which VCO configuration produced the lock * and use it later. Actually, we use the configuration *after* the one that * produced the lock, which seems odd, but it works. * * If we do not see a PLL lock on any standard VCO config, we fall back on an * autocal configuration, which has a fixed (as opposed to per-channel) VCO * config and different synth values from the standard set (divide ratio * is still shared with the standard set). */ /* The per-channel synth values for all standard VCO configurations. These get * written to register 1. */ static const u8 uw2453_std_synth[] = { RF_CHANNEL( 1) = 0x47, RF_CHANNEL( 2) = 0x47, RF_CHANNEL( 3) = 0x67, RF_CHANNEL( 4) = 0x67, RF_CHANNEL( 5) = 0x67, RF_CHANNEL( 6) = 0x67, RF_CHANNEL( 7) = 0x57, RF_CHANNEL( 8) = 0x57, RF_CHANNEL( 9) = 0x57, RF_CHANNEL(10) = 0x57, RF_CHANNEL(11) = 0x77, RF_CHANNEL(12) = 0x77, RF_CHANNEL(13) = 0x77, RF_CHANNEL(14) = 0x4f, }; /* This table stores the synthesizer fractional divide ratio for *all* VCO * configurations (both standard and autocal). These get written to register 2. */ static const u16 uw2453_synth_divide[] = { RF_CHANNEL( 1) = 0x999, RF_CHANNEL( 2) = 0x99b, RF_CHANNEL( 3) = 0x998, RF_CHANNEL( 4) = 0x99a, RF_CHANNEL( 5) = 0x999, RF_CHANNEL( 6) = 0x99b, RF_CHANNEL( 7) = 0x998, RF_CHANNEL( 8) = 0x99a, RF_CHANNEL( 9) = 0x999, RF_CHANNEL(10) = 0x99b, RF_CHANNEL(11) = 0x998, RF_CHANNEL(12) = 0x99a, RF_CHANNEL(13) = 0x999, RF_CHANNEL(14) = 0xccc, }; /* Here is the data for all the standard VCO configurations. We shrink our * table a little by observing that both channels in a consecutive pair share * the same value. We also observe that the high 4 bits ([0:3] in the specs) * are all 'Reserved' and are always set to 0x4 - we chop them off in the data * below. */ #define CHAN_TO_PAIRIDX(a) ((a - 1) / 2) #define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)] static const u16 uw2453_std_vco_cfg[][7] = { { /* table 1 */ RF_CHANPAIR( 1, 2) = 0x664d, RF_CHANPAIR( 3, 4) = 0x604d, RF_CHANPAIR( 5, 6) = 0x6675, RF_CHANPAIR( 7, 8) = 0x6475, RF_CHANPAIR( 9, 10) = 0x6655, RF_CHANPAIR(11, 12) = 0x6455, RF_CHANPAIR(13, 14) = 0x6665, }, { /* table 2 */ RF_CHANPAIR( 1, 2) = 0x666d, RF_CHANPAIR( 3, 4) = 0x606d, RF_CHANPAIR( 5, 6) = 0x664d, RF_CHANPAIR( 7, 8) = 0x644d, RF_CHANPAIR( 9, 10) = 0x6675, RF_CHANPAIR(11, 12) = 0x6475, RF_CHANPAIR(13, 14) = 0x6655, }, { /* table 3 */ RF_CHANPAIR( 1, 2) = 0x665d, RF_CHANPAIR( 3, 4) = 0x605d, RF_CHANPAIR( 5, 6) = 0x666d, RF_CHANPAIR( 7, 8) = 0x646d, RF_CHANPAIR( 9, 10) = 0x664d, RF_CHANPAIR(11, 12) = 0x644d, RF_CHANPAIR(13, 14) = 0x6675, }, { /* table 4 */ RF_CHANPAIR( 1, 2) = 0x667d, RF_CHANPAIR( 3, 4) = 0x607d, RF_CHANPAIR( 5, 6) = 0x665d, RF_CHANPAIR( 7, 8) = 0x645d, RF_CHANPAIR( 9, 10) = 0x666d, RF_CHANPAIR(11, 12) = 0x646d, RF_CHANPAIR(13, 14) = 0x664d, }, { /* table 5 */ RF_CHANPAIR( 1, 2) = 0x6643, RF_CHANPAIR( 3, 4) = 0x6043, RF_CHANPAIR( 5, 6) = 0x667d, RF_CHANPAIR( 7, 8) = 0x647d, RF_CHANPAIR( 9, 10) = 0x665d, RF_CHANPAIR(11, 12) = 0x645d, RF_CHANPAIR(13, 14) = 0x666d, }, { /* table 6 */ RF_CHANPAIR( 1, 2) = 0x6663, RF_CHANPAIR( 3, 4) = 0x6063, RF_CHANPAIR( 5, 6) = 0x6643, RF_CHANPAIR( 7, 8) = 0x6443, RF_CHANPAIR( 9, 10) = 0x667d, RF_CHANPAIR(11, 12) = 0x647d, RF_CHANPAIR(13, 14) = 0x665d, }, { /* table 7 */ RF_CHANPAIR( 1, 2) = 0x6653, RF_CHANPAIR( 3, 4) = 0x6053, RF_CHANPAIR( 5, 6) = 0x6663, RF_CHANPAIR( 7, 8) = 0x6463, RF_CHANPAIR( 9, 10) = 0x6643, RF_CHANPAIR(11, 12) = 0x6443, RF_CHANPAIR(13, 14) = 0x667d, }, { /* table 8 */ RF_CHANPAIR( 1, 2) = 0x6673, RF_CHANPAIR( 3, 4) = 0x6073, RF_CHANPAIR( 5, 6) = 0x6653, RF_CHANPAIR( 7, 8) = 0x6453, RF_CHANPAIR( 9, 10) = 0x6663, RF_CHANPAIR(11, 12) = 0x6463, RF_CHANPAIR(13, 14) = 0x6643, }, { /* table 9 */ RF_CHANPAIR( 1, 2) = 0x664b, RF_CHANPAIR( 3, 4) = 0x604b, RF_CHANPAIR( 5, 6) = 0x6673, RF_CHANPAIR( 7, 8) = 0x6473, RF_CHANPAIR( 9, 10) = 0x6653, RF_CHANPAIR(11, 12) = 0x6453, RF_CHANPAIR(13, 14) = 0x6663, }, { /* table 10 */ RF_CHANPAIR( 1, 2) = 0x666b, RF_CHANPAIR( 3, 4) = 0x606b, RF_CHANPAIR( 5, 6) = 0x664b, RF_CHANPAIR( 7, 8) = 0x644b, RF_CHANPAIR( 9, 10) = 0x6673, RF_CHANPAIR(11, 12) = 0x6473, RF_CHANPAIR(13, 14) = 0x6653, }, { /* table 11 */ RF_CHANPAIR( 1, 2) = 0x665b, RF_CHANPAIR( 3, 4) = 0x605b, RF_CHANPAIR( 5, 6) = 0x666b, RF_CHANPAIR( 7, 8) = 0x646b, RF_CHANPAIR( 9, 10) = 0x664b, RF_CHANPAIR(11, 12) = 0x644b, RF_CHANPAIR(13, 14) = 0x6673, }, }; /* The per-channel synth values for autocal. These get written to register 1. */ static const u16 uw2453_autocal_synth[] = { RF_CHANNEL( 1) = 0x6847, RF_CHANNEL( 2) = 0x6847, RF_CHANNEL( 3) = 0x6867, RF_CHANNEL( 4) = 0x6867, RF_CHANNEL( 5) = 0x6867, RF_CHANNEL( 6) = 0x6867, RF_CHANNEL( 7) = 0x6857, RF_CHANNEL( 8) = 0x6857, RF_CHANNEL( 9) = 0x6857, RF_CHANNEL(10) = 0x6857, RF_CHANNEL(11) = 0x6877, RF_CHANNEL(12) = 0x6877, RF_CHANNEL(13) = 0x6877, RF_CHANNEL(14) = 0x684f, }; /* The VCO configuration for autocal (all channels) */ static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662; /* TX gain settings. The array index corresponds to the TX power integration * values found in the EEPROM. The values get written to register 7. */ static u32 uw2453_txgain[] = { [0x00] = 0x0e313, [0x01] = 0x0fb13, [0x02] = 0x0e093, [0x03] = 0x0f893, [0x04] = 0x0ea93, [0x05] = 0x1f093, [0x06] = 0x1f493, [0x07] = 0x1f693, [0x08] = 0x1f393, [0x09] = 0x1f35b, [0x0a] = 0x1e6db, [0x0b] = 0x1ff3f, [0x0c] = 0x1ffff, [0x0d] = 0x361d7, [0x0e] = 0x37fbf, [0x0f] = 0x3ff8b, [0x10] = 0x3ff33, [0x11] = 0x3fb3f, [0x12] = 0x3ffff, }; /* RF-specific structure */ struct uw2453_priv { /* index into synth/VCO config tables where PLL lock was found * -1 means autocal */ int config; }; #define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv) static int uw2453_synth_set_channel(struct zd_chip *chip, int channel, bool autocal) { int r; int idx = channel - 1; u32 val; if (autocal) val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]); else val = UW2453_REGWRITE(1, uw2453_std_synth[idx]); r = zd_rfwrite_locked(chip, val, RF_RV_BITS); if (r) return r; return zd_rfwrite_locked(chip, UW2453_REGWRITE(2, uw2453_synth_divide[idx]), RF_RV_BITS); } static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value) { /* vendor driver always sets these upper bits even though the specs say * they are reserved */ u32 val = 0x40000 | value; return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), RF_RV_BITS); } static int uw2453_init_mode(struct zd_chip *chip) { static const u32 rv[] = { UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */ UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */ UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */ UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */ }; return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); } static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel) { u8 int_value = chip->pwr_int_values[channel - 1]; if (int_value >= ARRAY_SIZE(uw2453_txgain)) { dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for " "int value %x on channel %d\n", int_value, channel); return 0; } return zd_rfwrite_locked(chip, UW2453_REGWRITE(7, uw2453_txgain[int_value]), RF_RV_BITS); } static int uw2453_init_hw(struct zd_rf *rf) { int i, r; int found_config = -1; u16 intr_status; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 }, { ZD_CR17, 0x28 }, /* 6112 no change */ { ZD_CR23, 0x38 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, { ZD_CR27, 0x15 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, { ZD_CR33, 0x28 }, { ZD_CR34, 0x30 }, { ZD_CR35, 0x43 }, /* 6112 3e->43 */ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, { ZD_CR46, 0x92 }, /* 6112 96->92 */ { ZD_CR47, 0x1e }, { ZD_CR48, 0x04 }, /* 5602 Roger */ { ZD_CR49, 0xfa }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, { ZD_CR91, 0x00 }, { ZD_CR92, 0x0a }, { ZD_CR98, 0x8d }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x02 }, { ZD_CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */ { ZD_CR102, 0x27 }, { ZD_CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f * 6221 1f->1c */ { ZD_CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */ { ZD_CR109, 0x13 }, { ZD_CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */ { ZD_CR111, 0x13 }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, { ZD_CR114, 0x23 }, /* 6221 27->23 */ { ZD_CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */ { ZD_CR116, 0x24 }, /* 6220 1c->24 */ { ZD_CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */ { ZD_CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */ { ZD_CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */ { ZD_CR120, 0x4f }, { ZD_CR121, 0x1f }, /* 6220 4f->1f */ { ZD_CR122, 0xf0 }, { ZD_CR123, 0x57 }, { ZD_CR125, 0xad }, { ZD_CR126, 0x6c }, { ZD_CR127, 0x03 }, { ZD_CR128, 0x14 }, /* 6302 12->11 */ { ZD_CR129, 0x12 }, /* 6301 10->0f */ { ZD_CR130, 0x10 }, { ZD_CR137, 0x50 }, { ZD_CR138, 0xa8 }, { ZD_CR144, 0xac }, { ZD_CR146, 0x20 }, { ZD_CR252, 0xff }, { ZD_CR253, 0xff }, }; static const u32 rv[] = { UW2453_REGWRITE(4, 0x2b), /* configure receiver gain */ UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */ UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */ UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */ /* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins, * RSSI circuit powered down, reduced RSSI range */ UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */ /* synthesizer configuration for channel 1 */ UW2453_REGWRITE(1, 0x47), UW2453_REGWRITE(2, 0x999), /* disable manual VCO band selection */ UW2453_REGWRITE(3, 0x7602), /* enable manual VCO band selection, configure current level */ UW2453_REGWRITE(3, 0x46063), }; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) return r; r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); if (r) return r; r = uw2453_init_mode(chip); if (r) return r; /* Try all standard VCO configuration settings on channel 1 */ for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) { /* Configure synthesizer for channel 1 */ r = uw2453_synth_set_channel(chip, 1, false); if (r) return r; /* Write VCO config */ r = uw2453_write_vco_cfg(chip, uw2453_std_vco_cfg[i][0]); if (r) return r; /* ack interrupt event */ r = zd_iowrite16_locked(chip, 0x0f, UW2453_INTR_REG); if (r) return r; /* check interrupt status */ r = zd_ioread16_locked(chip, &intr_status, UW2453_INTR_REG); if (r) return r; if (!(intr_status & 0xf)) { dev_dbg_f(zd_chip_dev(chip), "PLL locked on configuration %d\n", i); found_config = i; break; } } if (found_config == -1) { /* autocal */ dev_dbg_f(zd_chip_dev(chip), "PLL did not lock, using autocal\n"); r = uw2453_synth_set_channel(chip, 1, true); if (r) return r; r = uw2453_write_vco_cfg(chip, UW2453_AUTOCAL_VCO_CFG); if (r) return r; } /* To match the vendor driver behaviour, we use the configuration after * the one that produced a lock. */ UW2453_PRIV(rf)->config = found_config + 1; return zd_iowrite16_locked(chip, 0x06, ZD_CR203); } static int uw2453_set_channel(struct zd_rf *rf, u8 channel) { int r; u16 vco_cfg; int config = UW2453_PRIV(rf)->config; bool autocal = (config == -1); struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, }; r = uw2453_synth_set_channel(chip, channel, autocal); if (r) return r; if (autocal) vco_cfg = UW2453_AUTOCAL_VCO_CFG; else vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)]; r = uw2453_write_vco_cfg(chip, vco_cfg); if (r) return r; r = uw2453_init_mode(chip); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) return r; r = uw2453_set_tx_gain_level(chip, channel); if (r) return r; return zd_iowrite16_locked(chip, 0x06, ZD_CR203); } static int uw2453_switch_radio_on(struct zd_rf *rf) { int r; struct zd_chip *chip = zd_rf_to_chip(rf); struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x00 }, { ZD_CR251, 0x3f }, }; /* enter RXTX mode */ r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), RF_RV_BITS); if (r) return r; if (zd_chip_is_zd1211b(chip)) ioreqs[1].value = 0x7f; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int uw2453_switch_radio_off(struct zd_rf *rf) { int r; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x04 }, { ZD_CR251, 0x2f }, }; /* enter IDLE mode */ /* FIXME: shouldn't we go to SLEEP? sent email to zydas */ r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), RF_RV_BITS); if (r) return r; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static void uw2453_clear(struct zd_rf *rf) { kfree(rf->priv); } int zd_rf_init_uw2453(struct zd_rf *rf) { rf->init_hw = uw2453_init_hw; rf->set_channel = uw2453_set_channel; rf->switch_radio_on = uw2453_switch_radio_on; rf->switch_radio_off = uw2453_switch_radio_off; rf->patch_6m_band_edge = zd_rf_generic_patch_6m; rf->clear = uw2453_clear; /* we have our own TX integration code */ rf->update_channel_int = 0; rf->priv = kmalloc(sizeof(struct uw2453_priv), GFP_KERNEL); if (rf->priv == NULL) return -ENOMEM; return 0; } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c0000644000175000017500000002127212026211315024250 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "zd_rf.h" #include "zd_usb.h" #include "zd_chip.h" static const u32 rf2959_table[][2] = { RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 }, RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 }, RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 }, RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 }, RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 }, RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 }, RF_CHANNEL(10) = { 0x181a09, 0x1e6666 }, RF_CHANNEL(11) = { 0x181a19, 0x1e6666 }, RF_CHANNEL(12) = { 0x181a29, 0x1e6666 }, RF_CHANNEL(13) = { 0x181a39, 0x1e6666 }, RF_CHANNEL(14) = { 0x181a60, 0x1c0000 }, }; #if 0 static int bits(u32 rw, int from, int to) { rw &= ~(0xffffffffU << (to+1)); rw >>= from; return rw; } static int bit(u32 rw, int bit) { return bits(rw, bit, bit); } static void dump_regwrite(u32 rw) { int reg = bits(rw, 18, 22); int rw_flag = bits(rw, 23, 23); PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag); switch (reg) { case 0: PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d" " if_vco_reg_en %d if_vga_en %d", bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1), bit(rw, 0)); break; case 1: PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d" " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d" " ifloopc %d dac1 %d", bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3)); break; case 2: PDEBUG("reg2 IFPLL2 n1 %d num1 %d", bits(rw, 6, 17), bits(rw, 0, 5)); break; case 3: PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17)); break; case 4: PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d", bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); break; case 5: PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d" " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d" " dac %d", bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3)); break; case 6: PDEBUG("reg6 RFPLL2 n %d num %d", bits(rw, 6, 17), bits(rw, 0, 5)); break; case 7: PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17)); break; case 8: PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d", bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); break; case 9: PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d", bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7), bits(rw, 0, 2)); break; case 10: PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d" " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d" " intbiasen %d tybypass %d", bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14), bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2), bit(rw, 1), bit(rw, 0)); break; case 11: PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d" " tx_delay %d", bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8), bits(rw, 0, 2)); break; case 12: PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d", bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5)); break; case 13: PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d" " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d" " rf_biasvco %d", bit(rw, 17), bit(rw, 16), bit(rw, 15), bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4), bits(rw, 0, 2)); break; case 14: PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d" " tx_acal %d tx_pcal %d", bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8), bits(rw, 0, 3)); break; } } #endif /* 0 */ static int rf2959_init_hw(struct zd_rf *rf) { int r; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR2, 0x1E }, { ZD_CR9, 0x20 }, { ZD_CR10, 0x89 }, { ZD_CR11, 0x00 }, { ZD_CR15, 0xD0 }, { ZD_CR17, 0x68 }, { ZD_CR19, 0x4a }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0E }, { ZD_CR23, 0x48 }, /* normal size for cca threshold */ { ZD_CR24, 0x14 }, /* { ZD_CR24, 0x20 }, */ { ZD_CR26, 0x90 }, { ZD_CR27, 0x30 }, { ZD_CR29, 0x20 }, { ZD_CR31, 0xb2 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x28 }, { ZD_CR38, 0x30 }, { ZD_CR34, 0x0f }, { ZD_CR35, 0xF0 }, { ZD_CR41, 0x2a }, { ZD_CR46, 0x7F }, { ZD_CR47, 0x1E }, { ZD_CR51, 0xc5 }, { ZD_CR52, 0xc5 }, { ZD_CR53, 0xc5 }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 }, { ZD_CR85, 0x00 }, { ZD_CR86, 0x10 }, { ZD_CR87, 0x2A }, { ZD_CR88, 0x10 }, { ZD_CR89, 0x24 }, { ZD_CR90, 0x18 }, /* { ZD_CR91, 0x18 }, */ /* should solve continuous CTS frame problems */ { ZD_CR91, 0x00 }, { ZD_CR92, 0x0a }, { ZD_CR93, 0x00 }, { ZD_CR94, 0x01 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0x40 }, { ZD_CR97, 0x37 }, { ZD_CR98, 0x05 }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 }, { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 }, { ZD_CR105, 0x12 }, /* normal size */ { ZD_CR106, 0x1a }, /* { ZD_CR106, 0x22 }, */ { ZD_CR107, 0x24 }, { ZD_CR108, 0x0a }, { ZD_CR109, 0x13 }, { ZD_CR110, 0x2F }, { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x40 }, { ZD_CR116, 0x40 }, { ZD_CR117, 0xF0 }, { ZD_CR118, 0xF0 }, { ZD_CR119, 0x16 }, /* no TX continuation */ { ZD_CR122, 0x00 }, /* { ZD_CR122, 0xff }, */ { ZD_CR127, 0x03 }, { ZD_CR131, 0x08 }, { ZD_CR138, 0x28 }, { ZD_CR148, 0x44 }, { ZD_CR150, 0x10 }, { ZD_CR169, 0xBB }, { ZD_CR170, 0xBB }, }; static const u32 rv[] = { 0x000007, /* REG0(CFG1) */ 0x07dd43, /* REG1(IFPLL1) */ 0x080959, /* REG2(IFPLL2) */ 0x0e6666, 0x116a57, /* REG4 */ 0x17dd43, /* REG5 */ 0x1819f9, /* REG6 */ 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, /* The Zydas driver somehow forgets to set this value. It's * only set for Japan. We are using internal power control * for now. */ 0x294128, /* internal power */ /* 0x28252c, */ /* External control TX power */ /* ZD_CR31_CCK, ZD_CR51_6-36M, ZD_CR52_48M, ZD_CR53_54M */ 0x2c0000, 0x300000, 0x340000, /* REG13(0xD) */ 0x381e0f, /* REG14(0xE) */ /* Bogus, RF2959's data sheet doesn't know register 27, which is * actually referenced here. The commented 0x11 is 17. */ 0x6c180f, /* REG27(0x11) */ }; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) return r; return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); } static int rf2959_set_channel(struct zd_rf *rf, u8 channel) { int i, r; const u32 *rv = rf2959_table[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); for (i = 0; i < 2; i++) { r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS); if (r) return r; } return 0; } static int rf2959_switch_radio_on(struct zd_rf *rf) { static const struct zd_ioreq16 ioreqs[] = { { ZD_CR10, 0x89 }, { ZD_CR11, 0x00 }, }; struct zd_chip *chip = zd_rf_to_chip(rf); return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int rf2959_switch_radio_off(struct zd_rf *rf) { static const struct zd_ioreq16 ioreqs[] = { { ZD_CR10, 0x15 }, { ZD_CR11, 0x81 }, }; struct zd_chip *chip = zd_rf_to_chip(rf); return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } int zd_rf_init_rf2959(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); if (zd_chip_is_zd1211b(chip)) { dev_err(zd_chip_dev(chip), "RF2959 is currently not supported for ZD1211B" " devices\n"); return -ENODEV; } rf->init_hw = rf2959_init_hw; rf->set_channel = rf2959_set_channel; rf->switch_radio_on = rf2959_switch_radio_on; rf->switch_radio_off = rf2959_switch_radio_off; return 0; } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_rf.h0000644000175000017500000000632012026211315023232 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _ZD_RF_H #define _ZD_RF_H #define UW2451_RF 0x2 #define UCHIP_RF 0x3 #define AL2230_RF 0x4 #define AL7230B_RF 0x5 /* a,b,g */ #define THETA_RF 0x6 #define AL2210_RF 0x7 #define MAXIM_NEW_RF 0x8 #define UW2453_RF 0x9 #define AL2230S_RF 0xa #define RALINK_RF 0xb #define INTERSIL_RF 0xc #define RF2959_RF 0xd #define MAXIM_NEW2_RF 0xe #define PHILIPS_RF 0xf #define RF_CHANNEL(ch) [(ch)-1] /* Provides functions of the RF transceiver. */ enum { RF_REG_BITS = 6, RF_VALUE_BITS = 18, RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS, }; struct zd_rf { u8 type; u8 channel; /* whether channel integration and calibration should be updated * defaults to 1 (yes) */ u8 update_channel_int:1; /* whether ZD_CR47 should be patched from the EEPROM, if the appropriate * flag is set in the POD. The vendor driver suggests that this should * be done for all RF's, but a bug in their code prevents but their * HW_OverWritePhyRegFromE2P() routine from ever taking effect. */ u8 patch_cck_gain:1; /* private RF driver data */ void *priv; /* RF-specific functions */ int (*init_hw)(struct zd_rf *rf); int (*set_channel)(struct zd_rf *rf, u8 channel); int (*switch_radio_on)(struct zd_rf *rf); int (*switch_radio_off)(struct zd_rf *rf); int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel); void (*clear)(struct zd_rf *rf); }; const char *zd_rf_name(u8 type); void zd_rf_init(struct zd_rf *rf); void zd_rf_clear(struct zd_rf *rf); int zd_rf_init_hw(struct zd_rf *rf, u8 type); int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size); int zd_rf_set_channel(struct zd_rf *rf, u8 channel); int zd_switch_radio_on(struct zd_rf *rf); int zd_switch_radio_off(struct zd_rf *rf); int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); static inline int zd_rf_should_update_pwr_int(struct zd_rf *rf) { return rf->update_channel_int; } static inline int zd_rf_should_patch_cck_gain(struct zd_rf *rf) { return rf->patch_cck_gain; } int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); /* Functions for individual RF chips */ int zd_rf_init_rf2959(struct zd_rf *rf); int zd_rf_init_al2230(struct zd_rf *rf); int zd_rf_init_al7230b(struct zd_rf *rf); int zd_rf_init_uw2453(struct zd_rf *rf); #endif /* _ZD_RF_H */ compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_rf.c0000644000175000017500000000772212026211315023234 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "zd_def.h" #include "zd_rf.h" #include "zd_mac.h" #include "zd_chip.h" static const char * const rfs[] = { [0] = "unknown RF0", [1] = "unknown RF1", [UW2451_RF] = "UW2451_RF", [UCHIP_RF] = "UCHIP_RF", [AL2230_RF] = "AL2230_RF", [AL7230B_RF] = "AL7230B_RF", [THETA_RF] = "THETA_RF", [AL2210_RF] = "AL2210_RF", [MAXIM_NEW_RF] = "MAXIM_NEW_RF", [UW2453_RF] = "UW2453_RF", [AL2230S_RF] = "AL2230S_RF", [RALINK_RF] = "RALINK_RF", [INTERSIL_RF] = "INTERSIL_RF", [RF2959_RF] = "RF2959_RF", [MAXIM_NEW2_RF] = "MAXIM_NEW2_RF", [PHILIPS_RF] = "PHILIPS_RF", }; const char *zd_rf_name(u8 type) { if (type & 0xf0) type = 0; return rfs[type]; } void zd_rf_init(struct zd_rf *rf) { memset(rf, 0, sizeof(*rf)); /* default to update channel integration, as almost all RF's do want * this */ rf->update_channel_int = 1; } void zd_rf_clear(struct zd_rf *rf) { if (rf->clear) rf->clear(rf); ZD_MEMCLEAR(rf, sizeof(*rf)); } int zd_rf_init_hw(struct zd_rf *rf, u8 type) { int r = 0; int t; struct zd_chip *chip = zd_rf_to_chip(rf); ZD_ASSERT(mutex_is_locked(&chip->mutex)); switch (type) { case RF2959_RF: r = zd_rf_init_rf2959(rf); break; case AL2230_RF: case AL2230S_RF: r = zd_rf_init_al2230(rf); break; case AL7230B_RF: r = zd_rf_init_al7230b(rf); break; case MAXIM_NEW_RF: case UW2453_RF: r = zd_rf_init_uw2453(rf); break; default: dev_err(zd_chip_dev(chip), "RF %s %#x is not supported\n", zd_rf_name(type), type); rf->type = 0; return -ENODEV; } if (r) return r; rf->type = type; r = zd_chip_lock_phy_regs(chip); if (r) return r; t = rf->init_hw(rf); r = zd_chip_unlock_phy_regs(chip); if (t) r = t; return r; } int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size) { return scnprintf(buffer, size, "%s", zd_rf_name(rf->type)); } int zd_rf_set_channel(struct zd_rf *rf, u8 channel) { int r; ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex)); if (channel < MIN_CHANNEL24) return -EINVAL; if (channel > MAX_CHANNEL24) return -EINVAL; dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel); r = rf->set_channel(rf, channel); if (r >= 0) rf->channel = channel; return r; } int zd_switch_radio_on(struct zd_rf *rf) { int r, t; struct zd_chip *chip = zd_rf_to_chip(rf); ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_chip_lock_phy_regs(chip); if (r) return r; t = rf->switch_radio_on(rf); r = zd_chip_unlock_phy_regs(chip); if (t) r = t; return r; } int zd_switch_radio_off(struct zd_rf *rf) { int r, t; struct zd_chip *chip = zd_rf_to_chip(rf); /* TODO: move phy regs handling to zd_chip */ ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_chip_lock_phy_regs(chip); if (r) return r; t = rf->switch_radio_off(rf); r = zd_chip_unlock_phy_regs(chip); if (t) r = t; return r; } int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel) { if (!rf->patch_6m_band_edge) return 0; return rf->patch_6m_band_edge(rf, channel); } int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel) { return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel); } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c0000644000175000017500000003210712026211315024361 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "zd_rf.h" #include "zd_usb.h" #include "zd_chip.h" static const u32 chan_rv[][2] = { RF_CHANNEL( 1) = { 0x09ec00, 0x8cccc8 }, RF_CHANNEL( 2) = { 0x09ec00, 0x8cccd8 }, RF_CHANNEL( 3) = { 0x09ec00, 0x8cccc0 }, RF_CHANNEL( 4) = { 0x09ec00, 0x8cccd0 }, RF_CHANNEL( 5) = { 0x05ec00, 0x8cccc8 }, RF_CHANNEL( 6) = { 0x05ec00, 0x8cccd8 }, RF_CHANNEL( 7) = { 0x05ec00, 0x8cccc0 }, RF_CHANNEL( 8) = { 0x05ec00, 0x8cccd0 }, RF_CHANNEL( 9) = { 0x0dec00, 0x8cccc8 }, RF_CHANNEL(10) = { 0x0dec00, 0x8cccd8 }, RF_CHANNEL(11) = { 0x0dec00, 0x8cccc0 }, RF_CHANNEL(12) = { 0x0dec00, 0x8cccd0 }, RF_CHANNEL(13) = { 0x03ec00, 0x8cccc8 }, RF_CHANNEL(14) = { 0x03ec00, 0x866660 }, }; static const u32 std_rv[] = { 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, /* freq shift 0xaad401 */ 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, }; static const u32 rv_init1[] = { 0x3c9000, 0xbfffff, 0x700000, 0xf15d58, }; static const u32 rv_init2[] = { 0xf15d59, 0xf15d5c, 0xf15d58, }; static const struct zd_ioreq16 ioreqs_sw[] = { { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, }; static int zd1211b_al7230b_finalize(struct zd_chip *chip) { int r; static const struct zd_ioreq16 ioreqs[] = { { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, { ZD_CR203, 0x04 }, { }, { ZD_CR240, 0x80 }, }; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) return r; if (chip->new_phy_layout) { /* antenna selection? */ r = zd_iowrite16_locked(chip, 0xe5, ZD_CR9); if (r) return r; } return zd_iowrite16_locked(chip, 0x04, ZD_CR203); } static int zd1211_al7230b_init_hw(struct zd_rf *rf) { int r; struct zd_chip *chip = zd_rf_to_chip(rf); /* All of these writes are identical to AL2230 unless otherwise * specified */ static const struct zd_ioreq16 ioreqs_1[] = { /* This one is 7230-specific, and happens before the rest */ { ZD_CR240, 0x57 }, { }, { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, { ZD_CR44, 0x33 }, /* This value is different for 7230 (was: 0x2a) */ { ZD_CR106, 0x22 }, { ZD_CR107, 0x1a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 }, { ZD_CR111, 0x2b }, { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a }, /* This happened further down in AL2230, * and the value changed (was: 0xe0) */ { ZD_CR122, 0xfc }, { ZD_CR10, 0x89 }, /* for newest (3rd cut) AL2300 */ { ZD_CR17, 0x28 }, { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 }, /* for newest (3rd cut) AL2300 */ { ZD_CR35, 0x3e }, { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, /* for newest (3rd cut) AL2300 */ { ZD_CR46, 0x96 }, { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 }, /* This value is different for 7230 (was: 0x00) */ { ZD_CR100, 0x02 }, { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, /* This value is different for 7230 (was: 0x24) */ { ZD_CR106, 0x22 }, /* This value is different for 7230 (was: 0x2a) */ { ZD_CR107, 0x3f }, { ZD_CR109, 0x09 }, /* This value is different for 7230 (was: 0x13) */ { ZD_CR110, 0x1f }, { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, /* for newest (3rd cut) AL2300 */ { ZD_CR115, 0x24 }, /* This value is different for 7230 (was: 0x24) */ { ZD_CR116, 0x3f }, /* This value is different for 7230 (was: 0xf4) */ { ZD_CR117, 0xfa }, { ZD_CR118, 0xfc }, { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, { ZD_CR121, 0x77 }, { ZD_CR137, 0x88 }, /* This one is 7230-specific */ { ZD_CR138, 0xa8 }, /* This value is different for 7230 (was: 0xff) */ { ZD_CR252, 0x34 }, /* This value is different for 7230 (was: 0xff) */ { ZD_CR253, 0x34 }, /* PLL_OFF */ { ZD_CR251, 0x2f }, }; static const struct zd_ioreq16 ioreqs_2[] = { { ZD_CR251, 0x3f }, /* PLL_ON */ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, }; r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); if (r) return r; r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; r = zd_iowrite16_locked(chip, 0x06, ZD_CR203); if (r) return r; r = zd_iowrite16_locked(chip, 0x80, ZD_CR240); if (r) return r; return 0; } static int zd1211b_al7230b_init_hw(struct zd_rf *rf) { int r; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs_1[] = { { ZD_CR240, 0x57 }, { ZD_CR9, 0x9 }, { }, { ZD_CR10, 0x8b }, { ZD_CR15, 0x20 }, { ZD_CR17, 0x2B }, /* for newest (3rd cut) AL2230 */ { ZD_CR20, 0x10 }, /* 4N25->Stone Request */ { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, { ZD_CR33, 0x28 }, /* 5613 */ { ZD_CR34, 0x30 }, { ZD_CR35, 0x3e }, /* for newest (3rd cut) AL2230 */ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, { ZD_CR46, 0x99 }, /* for newest (3rd cut) AL2230 */ { ZD_CR47, 0x1e }, /* ZD1215 5610 */ { ZD_CR48, 0x00 }, { ZD_CR49, 0x00 }, { ZD_CR51, 0x01 }, { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 }, { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 }, { ZD_CR69, 0x28 }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR87, 0x0A }, { ZD_CR89, 0x04 }, { ZD_CR90, 0x58 }, /* 5112 */ { ZD_CR91, 0x00 }, /* 5613 */ { ZD_CR92, 0x0a }, { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */ { ZD_CR99, 0x00 }, { ZD_CR100, 0x02 }, { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR106, 0x20 }, /* change to 0x24 for AL7230B */ { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */ { ZD_CR112, 0x1f }, }; static const struct zd_ioreq16 ioreqs_new_phy[] = { { ZD_CR107, 0x28 }, { ZD_CR110, 0x1f }, /* 5127, 0x13->0x1f */ { ZD_CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */ { ZD_CR116, 0x2a }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x12 }, { ZD_CR121, 0x6c }, /* 5613 */ }; static const struct zd_ioreq16 ioreqs_old_phy[] = { { ZD_CR107, 0x24 }, { ZD_CR110, 0x13 }, /* 5127, 0x13->0x1f */ { ZD_CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */ { ZD_CR116, 0x24 }, { ZD_CR118, 0xfc }, { ZD_CR119, 0x11 }, { ZD_CR121, 0x6a }, /* 5613 */ }; static const struct zd_ioreq16 ioreqs_2[] = { { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x24 }, { ZD_CR117, 0xfa }, { ZD_CR120, 0x4f }, { ZD_CR122, 0xfc }, /* E0->FCh at 4901 */ { ZD_CR123, 0x57 }, /* 5613 */ { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */ { ZD_CR126, 0x6c }, /* 5613 */ { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */ { ZD_CR130, 0x10 }, { ZD_CR131, 0x00 }, /* 5112 */ { ZD_CR137, 0x50 }, /* 5613 */ { ZD_CR138, 0xa8 }, /* 5112 */ { ZD_CR144, 0xac }, /* 5613 */ { ZD_CR148, 0x40 }, /* 5112 */ { ZD_CR149, 0x40 }, /* 4O07, 50->40 */ { ZD_CR150, 0x1a }, /* 5112, 0C->1A */ { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 }, { ZD_CR251, 0x2f }, /* PLL_OFF */ }; static const struct zd_ioreq16 ioreqs_3[] = { { ZD_CR251, 0x7f }, /* PLL_ON */ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, }; r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); if (r) return r; if (chip->new_phy_layout) r = zd_iowrite16a_locked(chip, ioreqs_new_phy, ARRAY_SIZE(ioreqs_new_phy)); else r = zd_iowrite16a_locked(chip, ioreqs_old_phy, ARRAY_SIZE(ioreqs_old_phy)); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); if (r) return r; r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; return zd1211b_al7230b_finalize(chip); } static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel) { int r; const u32 *rv = chan_rv[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { /* PLL_ON */ { ZD_CR251, 0x3f }, { ZD_CR203, 0x06 }, { ZD_CR240, 0x08 }, }; r = zd_iowrite16_locked(chip, 0x57, ZD_CR240); if (r) return r; /* PLL_OFF */ r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251); if (r) return r; r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) return r; r = zd_rfwrite_cr_locked(chip, 0xf15d58); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv, 2); if (r) return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) return r; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel) { int r; const u32 *rv = chan_rv[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); r = zd_iowrite16_locked(chip, 0x57, ZD_CR240); if (r) return r; r = zd_iowrite16_locked(chip, 0xe4, ZD_CR9); if (r) return r; /* PLL_OFF */ r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251); if (r) return r; r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) return r; r = zd_rfwrite_cr_locked(chip, 0xf15d58); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv, 2); if (r) return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) return r; r = zd_iowrite16_locked(chip, 0x7f, ZD_CR251); if (r) return r; return zd1211b_al7230b_finalize(chip); } static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x00 }, { ZD_CR251, 0x3f }, }; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x00 }, { ZD_CR251, 0x7f }, }; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int al7230b_switch_radio_off(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x04 }, { ZD_CR251, 0x2f }, }; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } /* ZD1211B+AL7230B 6m band edge patching differs slightly from other * configurations */ static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel) { struct zd_chip *chip = zd_rf_to_chip(rf); struct zd_ioreq16 ioreqs[] = { { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, }; /* FIXME: Channel 11 is not the edge for all regulatory domains. */ if (channel == 1) { ioreqs[0].value = 0x0e; ioreqs[1].value = 0x10; } else if (channel == 11) { ioreqs[0].value = 0x10; ioreqs[1].value = 0x10; } dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } int zd_rf_init_al7230b(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); if (zd_chip_is_zd1211b(chip)) { rf->init_hw = zd1211b_al7230b_init_hw; rf->switch_radio_on = zd1211b_al7230b_switch_radio_on; rf->set_channel = zd1211b_al7230b_set_channel; rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m; } else { rf->init_hw = zd1211_al7230b_init_hw; rf->switch_radio_on = zd1211_al7230b_switch_radio_on; rf->set_channel = zd1211_al7230b_set_channel; rf->patch_6m_band_edge = zd_rf_generic_patch_6m; rf->patch_cck_gain = 1; } rf->switch_radio_off = al7230b_switch_radio_off; return 0; } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_rf_al2230.c0000644000175000017500000003105612026211315024214 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "zd_rf.h" #include "zd_usb.h" #include "zd_chip.h" #define IS_AL2230S(chip) ((chip)->al2230s_bit || (chip)->rf.type == AL2230S_RF) static const u32 zd1211_al2230_table[][3] = { RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, }, RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, }, RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, }, RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, }, RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, }, RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, }, RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, }, RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, }, RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, }, RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, }, RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, }, RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, }, RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, }, RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, }, }; static const u32 zd1211b_al2230_table[][3] = { RF_CHANNEL( 1) = { 0x09efc0, 0x8cccc0, 0xb00000, }, RF_CHANNEL( 2) = { 0x09efc0, 0x8cccd0, 0xb00000, }, RF_CHANNEL( 3) = { 0x09e7c0, 0x8cccc0, 0xb00000, }, RF_CHANNEL( 4) = { 0x09e7c0, 0x8cccd0, 0xb00000, }, RF_CHANNEL( 5) = { 0x05efc0, 0x8cccc0, 0xb00000, }, RF_CHANNEL( 6) = { 0x05efc0, 0x8cccd0, 0xb00000, }, RF_CHANNEL( 7) = { 0x05e7c0, 0x8cccc0, 0xb00000, }, RF_CHANNEL( 8) = { 0x05e7c0, 0x8cccd0, 0xb00000, }, RF_CHANNEL( 9) = { 0x0defc0, 0x8cccc0, 0xb00000, }, RF_CHANNEL(10) = { 0x0defc0, 0x8cccd0, 0xb00000, }, RF_CHANNEL(11) = { 0x0de7c0, 0x8cccc0, 0xb00000, }, RF_CHANNEL(12) = { 0x0de7c0, 0x8cccd0, 0xb00000, }, RF_CHANNEL(13) = { 0x03efc0, 0x8cccc0, 0xb00000, }, RF_CHANNEL(14) = { 0x03e7c0, 0x866660, 0xb00000, }, }; static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = { { ZD_CR240, 0x57 }, { ZD_CR9, 0xe0 }, }; static const struct zd_ioreq16 ioreqs_init_al2230s[] = { { ZD_CR47, 0x1e }, /* MARK_002 */ { ZD_CR106, 0x22 }, { ZD_CR107, 0x2a }, /* MARK_002 */ { ZD_CR109, 0x13 }, /* MARK_002 */ { ZD_CR118, 0xf8 }, /* MARK_002 */ { ZD_CR119, 0x12 }, { ZD_CR122, 0xe0 }, { ZD_CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */ { ZD_CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */ { ZD_CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */ }; static int zd1211b_al2230_finalize_rf(struct zd_chip *chip) { int r; static const struct zd_ioreq16 ioreqs[] = { { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, { ZD_CR203, 0x06 }, { }, { ZD_CR240, 0x80 }, }; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) return r; /* related to antenna selection? */ if (chip->new_phy_layout) { r = zd_iowrite16_locked(chip, 0xe1, ZD_CR9); if (r) return r; } return zd_iowrite16_locked(chip, 0x06, ZD_CR203); } static int zd1211_al2230_init_hw(struct zd_rf *rf) { int r; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs_init[] = { { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, { ZD_CR44, 0x33 }, { ZD_CR106, 0x2a }, { ZD_CR107, 0x1a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 }, { ZD_CR111, 0x2b }, { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a }, { ZD_CR10, 0x89 }, /* for newest (3rd cut) AL2300 */ { ZD_CR17, 0x28 }, { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 }, /* for newest (3rd cut) AL2300 */ { ZD_CR35, 0x3e }, { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, /* for newest (3rd cut) AL2300 */ { ZD_CR46, 0x96 }, { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 }, { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR106, 0x24 }, { ZD_CR107, 0x2a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x13 }, { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, /* for newest (3rd cut) AL2300 */ { ZD_CR115, 0x24 }, { ZD_CR116, 0x24 }, { ZD_CR117, 0xf4 }, { ZD_CR118, 0xfc }, { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, { ZD_CR121, 0x77 }, { ZD_CR122, 0xe0 }, { ZD_CR137, 0x88 }, { ZD_CR252, 0xff }, { ZD_CR253, 0xff }, }; static const struct zd_ioreq16 ioreqs_pll[] = { /* shdnb(PLL_ON)=0 */ { ZD_CR251, 0x2f }, /* shdnb(PLL_ON)=1 */ { ZD_CR251, 0x3f }, { ZD_CR138, 0x28 }, { ZD_CR203, 0x06 }, }; static const u32 rv1[] = { /* Channel 1 */ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, }; static const u32 rv2[] = { 0x000da4, 0x0f4dc5, /* fix freq shift, 0x04edc5 */ 0x0805b6, 0x011687, 0x000688, 0x0403b9, /* external control TX power (ZD_CR31) */ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f, }; static const u32 rv3[] = { 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f, }; r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init)); if (r) return r; if (IS_AL2230S(chip)) { r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, ARRAY_SIZE(ioreqs_init_al2230s)); if (r) return r; } r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); if (r) return r; /* improve band edge for AL2230S */ if (IS_AL2230S(chip)) r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS); else r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS); if (r) return r; r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll)); if (r) return r; r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); if (r) return r; return 0; } static int zd1211b_al2230_init_hw(struct zd_rf *rf) { int r; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs1[] = { { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 }, { ZD_CR17, 0x2B }, /* for newest(3rd cut) AL2230 */ { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, { ZD_CR33, 0x28 }, /* 5621 */ { ZD_CR34, 0x30 }, { ZD_CR35, 0x3e }, /* for newest(3rd cut) AL2230 */ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, { ZD_CR46, 0x99 }, /* for newest(3rd cut) AL2230 */ { ZD_CR47, 0x1e }, /* ZD1211B 05.06.10 */ { ZD_CR48, 0x06 }, { ZD_CR49, 0xf9 }, { ZD_CR51, 0x01 }, { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 }, { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 }, { ZD_CR69, 0x28 }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, { ZD_CR91, 0x00 }, /* 5621 */ { ZD_CR92, 0x0a }, { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */ { ZD_CR99, 0x00 }, /* 5621 */ { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR106, 0x24 }, /* for newest(3rd cut) AL2230 */ { ZD_CR107, 0x2a }, { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */ { ZD_CR110, 0x1f }, /* 4804, for 1212 new algorithm */ { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) * AL2230 */ { ZD_CR116, 0x24 }, { ZD_CR117, 0xfa }, /* for 1211b */ { ZD_CR118, 0xfa }, /* for 1211b */ { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, { ZD_CR121, 0x6c }, /* for 1211b */ { ZD_CR122, 0xfc }, /* E0->FC at 4902 */ { ZD_CR123, 0x57 }, /* 5623 */ { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */ { ZD_CR126, 0x6c }, /* 5614 */ { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */ { ZD_CR137, 0x50 }, /* 5614 */ { ZD_CR138, 0xa8 }, { ZD_CR144, 0xac }, /* 5621 */ { ZD_CR150, 0x0d }, { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 }, }; static const u32 rv1[] = { 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000, }; static const u32 rv2[] = { /* To improve AL2230 yield, improve phase noise, 4713 */ 0x25a000, 0xa3b2f0, 0x6da010, /* Reg6 update for MP versio */ 0xe36280, /* Modified by jxiao for Bor-Chin on 2004/08/02 */ 0x116000, 0x9dc020, /* External control TX power (ZD_CR31) */ 0x5ddb00, /* RegA update for MP version */ 0xd99000, /* RegB update for MP version */ 0x3ffbd0, /* RegC update for MP version */ 0xb00000, /* RegD update for MP version */ /* improve phase noise and remove phase calibration,4713 */ 0xf01a00, }; static const struct zd_ioreq16 ioreqs2[] = { { ZD_CR251, 0x2f }, /* shdnb(PLL_ON)=0 */ { ZD_CR251, 0x7f }, /* shdnb(PLL_ON)=1 */ }; static const u32 rv3[] = { /* To improve AL2230 yield, 4713 */ 0xf01b00, 0xf01e00, 0xf01a00, }; static const struct zd_ioreq16 ioreqs3[] = { /* related to 6M band edge patching, happens unconditionally */ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, }; r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, ARRAY_SIZE(zd1211b_ioreqs_shared_1)); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); if (r) return r; if (IS_AL2230S(chip)) { r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, ARRAY_SIZE(ioreqs_init_al2230s)); if (r) return r; } r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1)); if (r) return r; if (IS_AL2230S(chip)) r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS); else r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2)); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3)); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); if (r) return r; return zd1211b_al2230_finalize_rf(chip); } static int zd1211_al2230_set_channel(struct zd_rf *rf, u8 channel) { int r; const u32 *rv = zd1211_al2230_table[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR138, 0x28 }, { ZD_CR203, 0x06 }, }; r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS); if (r) return r; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int zd1211b_al2230_set_channel(struct zd_rf *rf, u8 channel) { int r; const u32 *rv = zd1211b_al2230_table[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, ARRAY_SIZE(zd1211b_ioreqs_shared_1)); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv, 3); if (r) return r; return zd1211b_al2230_finalize_rf(chip); } static int zd1211_al2230_switch_radio_on(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x00 }, { ZD_CR251, 0x3f }, }; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x00 }, { ZD_CR251, 0x7f }, }; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int al2230_switch_radio_off(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { { ZD_CR11, 0x04 }, { ZD_CR251, 0x2f }, }; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } int zd_rf_init_al2230(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); rf->switch_radio_off = al2230_switch_radio_off; if (zd_chip_is_zd1211b(chip)) { rf->init_hw = zd1211b_al2230_init_hw; rf->set_channel = zd1211b_al2230_set_channel; rf->switch_radio_on = zd1211b_al2230_switch_radio_on; } else { rf->init_hw = zd1211_al2230_init_hw; rf->set_channel = zd1211_al2230_set_channel; rf->switch_radio_on = zd1211_al2230_switch_radio_on; } rf->patch_6m_band_edge = zd_rf_generic_patch_6m; rf->patch_cck_gain = 1; return 0; } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_mac.h0000644000175000017500000002214112026211315023362 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _ZD_MAC_H #define _ZD_MAC_H #include #include #include "zd_chip.h" struct zd_ctrlset { u8 modulation; __le16 tx_length; u8 control; /* stores only the difference to tx_length on ZD1211B */ __le16 packet_length; __le16 current_length; u8 service; __le16 next_frame_length; } __packed; #define ZD_CS_RESERVED_SIZE 25 /* The field modulation of struct zd_ctrlset controls the bit rate, the use * of short or long preambles in 802.11b (CCK mode) or the use of 802.11a or * 802.11g in OFDM mode. * * The term zd-rate is used for the combination of the modulation type flag * and the "pure" rate value. */ #define ZD_PURE_RATE_MASK 0x0f #define ZD_MODULATION_TYPE_MASK 0x10 #define ZD_RATE_MASK (ZD_PURE_RATE_MASK|ZD_MODULATION_TYPE_MASK) #define ZD_PURE_RATE(modulation) ((modulation) & ZD_PURE_RATE_MASK) #define ZD_MODULATION_TYPE(modulation) ((modulation) & ZD_MODULATION_TYPE_MASK) #define ZD_RATE(modulation) ((modulation) & ZD_RATE_MASK) /* The two possible modulation types. Notify that 802.11b doesn't use the CCK * codeing for the 1 and 2 MBit/s rate. We stay with the term here to remain * consistent with uses the term at other places. */ #define ZD_CCK 0x00 #define ZD_OFDM 0x10 /* The ZD1211 firmware uses proprietary encodings of the 802.11b (CCK) rates. * For OFDM the PLCP rate encodings are used. We combine these "pure" rates * with the modulation type flag and call the resulting values zd-rates. */ #define ZD_CCK_RATE_1M (ZD_CCK|0x00) #define ZD_CCK_RATE_2M (ZD_CCK|0x01) #define ZD_CCK_RATE_5_5M (ZD_CCK|0x02) #define ZD_CCK_RATE_11M (ZD_CCK|0x03) #define ZD_OFDM_RATE_6M (ZD_OFDM|ZD_OFDM_PLCP_RATE_6M) #define ZD_OFDM_RATE_9M (ZD_OFDM|ZD_OFDM_PLCP_RATE_9M) #define ZD_OFDM_RATE_12M (ZD_OFDM|ZD_OFDM_PLCP_RATE_12M) #define ZD_OFDM_RATE_18M (ZD_OFDM|ZD_OFDM_PLCP_RATE_18M) #define ZD_OFDM_RATE_24M (ZD_OFDM|ZD_OFDM_PLCP_RATE_24M) #define ZD_OFDM_RATE_36M (ZD_OFDM|ZD_OFDM_PLCP_RATE_36M) #define ZD_OFDM_RATE_48M (ZD_OFDM|ZD_OFDM_PLCP_RATE_48M) #define ZD_OFDM_RATE_54M (ZD_OFDM|ZD_OFDM_PLCP_RATE_54M) /* The bit 5 of the zd_ctrlset modulation field controls the preamble in CCK * mode or the 802.11a/802.11g selection in OFDM mode. */ #define ZD_CCK_PREA_LONG 0x00 #define ZD_CCK_PREA_SHORT 0x20 #define ZD_OFDM_MODE_11G 0x00 #define ZD_OFDM_MODE_11A 0x20 /* zd_ctrlset control field */ #define ZD_CS_NEED_RANDOM_BACKOFF 0x01 #define ZD_CS_NO_ACK 0x02 #define ZD_CS_FRAME_TYPE_MASK 0x0c #define ZD_CS_DATA_FRAME 0x00 #define ZD_CS_PS_POLL_FRAME 0x04 #define ZD_CS_MANAGEMENT_FRAME 0x08 #define ZD_CS_NO_SEQUENCE_CTL_FRAME 0x0c #define ZD_CS_WAKE_DESTINATION 0x10 #define ZD_CS_RTS 0x20 #define ZD_CS_ENCRYPT 0x40 #define ZD_CS_SELF_CTS 0x80 /* Incoming frames are prepended by a PLCP header */ #define ZD_PLCP_HEADER_SIZE 5 struct rx_length_info { __le16 length[3]; __le16 tag; } __packed; #define RX_LENGTH_INFO_TAG 0x697e struct rx_status { u8 signal_quality_cck; /* rssi */ u8 signal_strength; u8 signal_quality_ofdm; u8 decryption_type; u8 frame_status; } __packed; /* rx_status field decryption_type */ #define ZD_RX_NO_WEP 0 #define ZD_RX_WEP64 1 #define ZD_RX_TKIP 2 #define ZD_RX_AES 4 #define ZD_RX_WEP128 5 #define ZD_RX_WEP256 6 /* rx_status field frame_status */ #define ZD_RX_FRAME_MODULATION_MASK 0x01 #define ZD_RX_CCK 0x00 #define ZD_RX_OFDM 0x01 #define ZD_RX_TIMEOUT_ERROR 0x02 #define ZD_RX_FIFO_OVERRUN_ERROR 0x04 #define ZD_RX_DECRYPTION_ERROR 0x08 #define ZD_RX_CRC32_ERROR 0x10 #define ZD_RX_NO_ADDR1_MATCH_ERROR 0x20 #define ZD_RX_CRC16_ERROR 0x40 #define ZD_RX_ERROR 0x80 struct tx_retry_rate { int count; /* number of valid element in rate[] array */ int rate[10]; /* retry rates, described by an index in zd_rates[] */ }; struct tx_status { u8 type; /* must always be 0x01 : USB_INT_TYPE */ u8 id; /* must always be 0xa0 : USB_INT_ID_RETRY_FAILED */ u8 rate; u8 pad; u8 mac[ETH_ALEN]; u8 retry; u8 failure; } __packed; enum mac_flags { MAC_FIXED_CHANNEL = 0x01, }; struct housekeeping { struct delayed_work link_led_work; }; struct beacon { struct delayed_work watchdog_work; struct sk_buff *cur_beacon; unsigned long last_update; u16 interval; u8 period; }; enum zd_device_flags { ZD_DEVICE_RUNNING, }; #define ZD_MAC_STATS_BUFFER_SIZE 16 #define ZD_MAC_MAX_ACK_WAITERS 50 struct zd_mac { struct zd_chip chip; spinlock_t lock; spinlock_t intr_lock; struct ieee80211_hw *hw; struct ieee80211_vif *vif; struct housekeeping housekeeping; struct beacon beacon; struct work_struct set_rts_cts_work; struct work_struct process_intr; struct zd_mc_hash multicast_hash; u8 intr_buffer[USB_MAX_EP_INT_BUFFER]; u8 regdomain; u8 default_regdomain; u8 channel; int type; int associated; unsigned long flags; struct sk_buff_head ack_wait_queue; struct ieee80211_channel channels[14]; struct ieee80211_rate rates[12]; struct ieee80211_supported_band band; /* Short preamble (used for RTS/CTS) */ unsigned int short_preamble:1; /* whether to pass frames with CRC errors to stack */ unsigned int pass_failed_fcs:1; /* whether to pass control frames to stack */ unsigned int pass_ctrl:1; /* whether we have received a 802.11 ACK that is pending */ unsigned int ack_pending:1; /* signal strength of the last 802.11 ACK received */ int ack_signal; }; #define ZD_REGDOMAIN_FCC 0x10 #define ZD_REGDOMAIN_IC 0x20 #define ZD_REGDOMAIN_ETSI 0x30 #define ZD_REGDOMAIN_SPAIN 0x31 #define ZD_REGDOMAIN_FRANCE 0x32 #define ZD_REGDOMAIN_JAPAN_2 0x40 #define ZD_REGDOMAIN_JAPAN 0x41 #define ZD_REGDOMAIN_JAPAN_3 0x49 enum { MIN_CHANNEL24 = 1, MAX_CHANNEL24 = 14, }; #define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80 struct ofdm_plcp_header { u8 prefix[3]; __le16 service; } __packed; static inline u8 zd_ofdm_plcp_header_rate(const struct ofdm_plcp_header *header) { return header->prefix[0] & 0xf; } /* The following defines give the encoding of the 4-bit rate field in the * OFDM (802.11a/802.11g) PLCP header. Notify that these values are used to * define the zd-rate values for OFDM. * * See the struct zd_ctrlset definition in zd_mac.h. */ #define ZD_OFDM_PLCP_RATE_6M 0xb #define ZD_OFDM_PLCP_RATE_9M 0xf #define ZD_OFDM_PLCP_RATE_12M 0xa #define ZD_OFDM_PLCP_RATE_18M 0xe #define ZD_OFDM_PLCP_RATE_24M 0x9 #define ZD_OFDM_PLCP_RATE_36M 0xd #define ZD_OFDM_PLCP_RATE_48M 0x8 #define ZD_OFDM_PLCP_RATE_54M 0xc struct cck_plcp_header { u8 signal; u8 service; __le16 length; __le16 crc16; } __packed; static inline u8 zd_cck_plcp_header_signal(const struct cck_plcp_header *header) { return header->signal; } /* These defines give the encodings of the signal field in the 802.11b PLCP * header. The signal field gives the bit rate of the following packet. Even * if technically wrong we use CCK here also for the 1 MBit/s and 2 MBit/s * rate to stay consistent with Zydas and our use of the term. * * Notify that these values are *not* used in the zd-rates. */ #define ZD_CCK_PLCP_SIGNAL_1M 0x0a #define ZD_CCK_PLCP_SIGNAL_2M 0x14 #define ZD_CCK_PLCP_SIGNAL_5M5 0x37 #define ZD_CCK_PLCP_SIGNAL_11M 0x6e static inline struct zd_mac *zd_hw_mac(struct ieee80211_hw *hw) { return hw->priv; } static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip) { return container_of(chip, struct zd_mac, chip); } static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb) { return zd_chip_to_mac(zd_usb_to_chip(usb)); } static inline u8 *zd_mac_get_perm_addr(struct zd_mac *mac) { return mac->hw->wiphy->perm_addr; } #define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip)) struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf); void zd_mac_clear(struct zd_mac *mac); int zd_mac_preinit_hw(struct ieee80211_hw *hw); int zd_mac_init_hw(struct ieee80211_hw *hw); int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length); void zd_mac_tx_failed(struct urb *urb); void zd_mac_tx_to_dev(struct sk_buff *skb, int error); int zd_op_start(struct ieee80211_hw *hw); void zd_op_stop(struct ieee80211_hw *hw); int zd_restore_settings(struct zd_mac *mac); #ifdef DEBUG void zd_dump_rx_status(const struct rx_status *status); #else #define zd_dump_rx_status(status) #endif /* DEBUG */ #endif /* _ZD_MAC_H */ compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_def.h0000644000175000017500000000423012026211315023357 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _ZD_DEF_H #define _ZD_DEF_H #include #include #include typedef u16 __nocast zd_addr_t; #define dev_printk_f(level, dev, fmt, args...) \ dev_printk(level, dev, "%s() " fmt, __func__, ##args) #ifdef DEBUG # define dev_dbg_f(dev, fmt, args...) \ dev_printk_f(KERN_DEBUG, dev, fmt, ## args) # define dev_dbg_f_limit(dev, fmt, args...) do { \ if (net_ratelimit()) \ dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \ } while (0) # define dev_dbg_f_cond(dev, cond, fmt, args...) ({ \ bool __cond = !!(cond); \ if (unlikely(__cond)) \ dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \ }) #else # define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0) # define dev_dbg_f_limit(dev, fmt, args...) do { (void)(dev); } while (0) # define dev_dbg_f_cond(dev, cond, fmt, args...) do { (void)(dev); } while (0) #endif /* DEBUG */ #ifdef DEBUG # define ZD_ASSERT(x) \ do { \ if (unlikely(!(x))) { \ pr_debug("%s:%d ASSERT %s VIOLATED!\n", \ __FILE__, __LINE__, __stringify(x)); \ dump_stack(); \ } \ } while (0) #else # define ZD_ASSERT(x) do { } while (0) #endif #ifdef DEBUG # define ZD_MEMCLEAR(pointer, size) memset((pointer), 0xff, (size)) #else # define ZD_MEMCLEAR(pointer, size) do { } while (0) #endif #endif /* _ZD_DEF_H */ compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_chip.h0000644000175000017500000007653312026211315023563 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _ZD_CHIP_H #define _ZD_CHIP_H #include #include "zd_rf.h" #include "zd_usb.h" /* Header for the Media Access Controller (MAC) and the Baseband Processor * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and * adds a processor for handling the USB protocol. */ /* Address space */ enum { /* CONTROL REGISTERS */ CR_START = 0x9000, /* FIRMWARE */ FW_START = 0xee00, /* EEPROM */ E2P_START = 0xf800, E2P_LEN = 0x800, /* EEPROM layout */ E2P_LOAD_CODE_LEN = 0xe, /* base 0xf800 */ E2P_LOAD_VECT_LEN = 0x9, /* base 0xf80e */ /* E2P_DATA indexes into this */ E2P_DATA_LEN = 0x7e, /* base 0xf817 */ E2P_BOOT_CODE_LEN = 0x760, /* base 0xf895 */ E2P_INTR_VECT_LEN = 0xb, /* base 0xfff5 */ /* Some precomputed offsets into the EEPROM */ E2P_DATA_OFFSET = E2P_LOAD_CODE_LEN + E2P_LOAD_VECT_LEN, E2P_BOOT_CODE_OFFSET = E2P_DATA_OFFSET + E2P_DATA_LEN, }; #define CTL_REG(offset) ((zd_addr_t)(CR_START + (offset))) #define E2P_DATA(offset) ((zd_addr_t)(E2P_START + E2P_DATA_OFFSET + (offset))) #define FWRAW_DATA(offset) ((zd_addr_t)(FW_START + (offset))) /* 8-bit hardware registers */ #define ZD_CR0 CTL_REG(0x0000) #define ZD_CR1 CTL_REG(0x0004) #define ZD_CR2 CTL_REG(0x0008) #define ZD_CR3 CTL_REG(0x000C) #define ZD_CR5 CTL_REG(0x0010) /* bit 5: if set short preamble used * bit 6: filter band - Japan channel 14 on, else off */ #define ZD_CR6 CTL_REG(0x0014) #define ZD_CR7 CTL_REG(0x0018) #define ZD_CR8 CTL_REG(0x001C) #define ZD_CR4 CTL_REG(0x0020) #define ZD_CR9 CTL_REG(0x0024) /* bit 2: antenna switch (together with ZD_CR10) */ #define ZD_CR10 CTL_REG(0x0028) /* bit 1: antenna switch (together with ZD_CR9) * RF2959 controls with ZD_CR11 radion on and off */ #define ZD_CR11 CTL_REG(0x002C) /* bit 6: TX power control for OFDM * RF2959 controls with ZD_CR10 radio on and off */ #define ZD_CR12 CTL_REG(0x0030) #define ZD_CR13 CTL_REG(0x0034) #define ZD_CR14 CTL_REG(0x0038) #define ZD_CR15 CTL_REG(0x003C) #define ZD_CR16 CTL_REG(0x0040) #define ZD_CR17 CTL_REG(0x0044) #define ZD_CR18 CTL_REG(0x0048) #define ZD_CR19 CTL_REG(0x004C) #define ZD_CR20 CTL_REG(0x0050) #define ZD_CR21 CTL_REG(0x0054) #define ZD_CR22 CTL_REG(0x0058) #define ZD_CR23 CTL_REG(0x005C) #define ZD_CR24 CTL_REG(0x0060) /* CCA threshold */ #define ZD_CR25 CTL_REG(0x0064) #define ZD_CR26 CTL_REG(0x0068) #define ZD_CR27 CTL_REG(0x006C) #define ZD_CR28 CTL_REG(0x0070) #define ZD_CR29 CTL_REG(0x0074) #define ZD_CR30 CTL_REG(0x0078) #define ZD_CR31 CTL_REG(0x007C) /* TX power control for RF in * CCK mode */ #define ZD_CR32 CTL_REG(0x0080) #define ZD_CR33 CTL_REG(0x0084) #define ZD_CR34 CTL_REG(0x0088) #define ZD_CR35 CTL_REG(0x008C) #define ZD_CR36 CTL_REG(0x0090) #define ZD_CR37 CTL_REG(0x0094) #define ZD_CR38 CTL_REG(0x0098) #define ZD_CR39 CTL_REG(0x009C) #define ZD_CR40 CTL_REG(0x00A0) #define ZD_CR41 CTL_REG(0x00A4) #define ZD_CR42 CTL_REG(0x00A8) #define ZD_CR43 CTL_REG(0x00AC) #define ZD_CR44 CTL_REG(0x00B0) #define ZD_CR45 CTL_REG(0x00B4) #define ZD_CR46 CTL_REG(0x00B8) #define ZD_CR47 CTL_REG(0x00BC) /* CCK baseband gain * (patch value might be in EEPROM) */ #define ZD_CR48 CTL_REG(0x00C0) #define ZD_CR49 CTL_REG(0x00C4) #define ZD_CR50 CTL_REG(0x00C8) #define ZD_CR51 CTL_REG(0x00CC) /* TX power control for RF in * 6-36M modes */ #define ZD_CR52 CTL_REG(0x00D0) /* TX power control for RF in * 48M mode */ #define ZD_CR53 CTL_REG(0x00D4) /* TX power control for RF in * 54M mode */ #define ZD_CR54 CTL_REG(0x00D8) #define ZD_CR55 CTL_REG(0x00DC) #define ZD_CR56 CTL_REG(0x00E0) #define ZD_CR57 CTL_REG(0x00E4) #define ZD_CR58 CTL_REG(0x00E8) #define ZD_CR59 CTL_REG(0x00EC) #define ZD_CR60 CTL_REG(0x00F0) #define ZD_CR61 CTL_REG(0x00F4) #define ZD_CR62 CTL_REG(0x00F8) #define ZD_CR63 CTL_REG(0x00FC) #define ZD_CR64 CTL_REG(0x0100) #define ZD_CR65 CTL_REG(0x0104) /* OFDM 54M calibration */ #define ZD_CR66 CTL_REG(0x0108) /* OFDM 48M calibration */ #define ZD_CR67 CTL_REG(0x010C) /* OFDM 36M calibration */ #define ZD_CR68 CTL_REG(0x0110) /* CCK calibration */ #define ZD_CR69 CTL_REG(0x0114) #define ZD_CR70 CTL_REG(0x0118) #define ZD_CR71 CTL_REG(0x011C) #define ZD_CR72 CTL_REG(0x0120) #define ZD_CR73 CTL_REG(0x0124) #define ZD_CR74 CTL_REG(0x0128) #define ZD_CR75 CTL_REG(0x012C) #define ZD_CR76 CTL_REG(0x0130) #define ZD_CR77 CTL_REG(0x0134) #define ZD_CR78 CTL_REG(0x0138) #define ZD_CR79 CTL_REG(0x013C) #define ZD_CR80 CTL_REG(0x0140) #define ZD_CR81 CTL_REG(0x0144) #define ZD_CR82 CTL_REG(0x0148) #define ZD_CR83 CTL_REG(0x014C) #define ZD_CR84 CTL_REG(0x0150) #define ZD_CR85 CTL_REG(0x0154) #define ZD_CR86 CTL_REG(0x0158) #define ZD_CR87 CTL_REG(0x015C) #define ZD_CR88 CTL_REG(0x0160) #define ZD_CR89 CTL_REG(0x0164) #define ZD_CR90 CTL_REG(0x0168) #define ZD_CR91 CTL_REG(0x016C) #define ZD_CR92 CTL_REG(0x0170) #define ZD_CR93 CTL_REG(0x0174) #define ZD_CR94 CTL_REG(0x0178) #define ZD_CR95 CTL_REG(0x017C) #define ZD_CR96 CTL_REG(0x0180) #define ZD_CR97 CTL_REG(0x0184) #define ZD_CR98 CTL_REG(0x0188) #define ZD_CR99 CTL_REG(0x018C) #define ZD_CR100 CTL_REG(0x0190) #define ZD_CR101 CTL_REG(0x0194) #define ZD_CR102 CTL_REG(0x0198) #define ZD_CR103 CTL_REG(0x019C) #define ZD_CR104 CTL_REG(0x01A0) #define ZD_CR105 CTL_REG(0x01A4) #define ZD_CR106 CTL_REG(0x01A8) #define ZD_CR107 CTL_REG(0x01AC) #define ZD_CR108 CTL_REG(0x01B0) #define ZD_CR109 CTL_REG(0x01B4) #define ZD_CR110 CTL_REG(0x01B8) #define ZD_CR111 CTL_REG(0x01BC) #define ZD_CR112 CTL_REG(0x01C0) #define ZD_CR113 CTL_REG(0x01C4) #define ZD_CR114 CTL_REG(0x01C8) #define ZD_CR115 CTL_REG(0x01CC) #define ZD_CR116 CTL_REG(0x01D0) #define ZD_CR117 CTL_REG(0x01D4) #define ZD_CR118 CTL_REG(0x01D8) #define ZD_CR119 CTL_REG(0x01DC) #define ZD_CR120 CTL_REG(0x01E0) #define ZD_CR121 CTL_REG(0x01E4) #define ZD_CR122 CTL_REG(0x01E8) #define ZD_CR123 CTL_REG(0x01EC) #define ZD_CR124 CTL_REG(0x01F0) #define ZD_CR125 CTL_REG(0x01F4) #define ZD_CR126 CTL_REG(0x01F8) #define ZD_CR127 CTL_REG(0x01FC) #define ZD_CR128 CTL_REG(0x0200) #define ZD_CR129 CTL_REG(0x0204) #define ZD_CR130 CTL_REG(0x0208) #define ZD_CR131 CTL_REG(0x020C) #define ZD_CR132 CTL_REG(0x0210) #define ZD_CR133 CTL_REG(0x0214) #define ZD_CR134 CTL_REG(0x0218) #define ZD_CR135 CTL_REG(0x021C) #define ZD_CR136 CTL_REG(0x0220) #define ZD_CR137 CTL_REG(0x0224) #define ZD_CR138 CTL_REG(0x0228) #define ZD_CR139 CTL_REG(0x022C) #define ZD_CR140 CTL_REG(0x0230) #define ZD_CR141 CTL_REG(0x0234) #define ZD_CR142 CTL_REG(0x0238) #define ZD_CR143 CTL_REG(0x023C) #define ZD_CR144 CTL_REG(0x0240) #define ZD_CR145 CTL_REG(0x0244) #define ZD_CR146 CTL_REG(0x0248) #define ZD_CR147 CTL_REG(0x024C) #define ZD_CR148 CTL_REG(0x0250) #define ZD_CR149 CTL_REG(0x0254) #define ZD_CR150 CTL_REG(0x0258) #define ZD_CR151 CTL_REG(0x025C) #define ZD_CR152 CTL_REG(0x0260) #define ZD_CR153 CTL_REG(0x0264) #define ZD_CR154 CTL_REG(0x0268) #define ZD_CR155 CTL_REG(0x026C) #define ZD_CR156 CTL_REG(0x0270) #define ZD_CR157 CTL_REG(0x0274) #define ZD_CR158 CTL_REG(0x0278) #define ZD_CR159 CTL_REG(0x027C) #define ZD_CR160 CTL_REG(0x0280) #define ZD_CR161 CTL_REG(0x0284) #define ZD_CR162 CTL_REG(0x0288) #define ZD_CR163 CTL_REG(0x028C) #define ZD_CR164 CTL_REG(0x0290) #define ZD_CR165 CTL_REG(0x0294) #define ZD_CR166 CTL_REG(0x0298) #define ZD_CR167 CTL_REG(0x029C) #define ZD_CR168 CTL_REG(0x02A0) #define ZD_CR169 CTL_REG(0x02A4) #define ZD_CR170 CTL_REG(0x02A8) #define ZD_CR171 CTL_REG(0x02AC) #define ZD_CR172 CTL_REG(0x02B0) #define ZD_CR173 CTL_REG(0x02B4) #define ZD_CR174 CTL_REG(0x02B8) #define ZD_CR175 CTL_REG(0x02BC) #define ZD_CR176 CTL_REG(0x02C0) #define ZD_CR177 CTL_REG(0x02C4) #define ZD_CR178 CTL_REG(0x02C8) #define ZD_CR179 CTL_REG(0x02CC) #define ZD_CR180 CTL_REG(0x02D0) #define ZD_CR181 CTL_REG(0x02D4) #define ZD_CR182 CTL_REG(0x02D8) #define ZD_CR183 CTL_REG(0x02DC) #define ZD_CR184 CTL_REG(0x02E0) #define ZD_CR185 CTL_REG(0x02E4) #define ZD_CR186 CTL_REG(0x02E8) #define ZD_CR187 CTL_REG(0x02EC) #define ZD_CR188 CTL_REG(0x02F0) #define ZD_CR189 CTL_REG(0x02F4) #define ZD_CR190 CTL_REG(0x02F8) #define ZD_CR191 CTL_REG(0x02FC) #define ZD_CR192 CTL_REG(0x0300) #define ZD_CR193 CTL_REG(0x0304) #define ZD_CR194 CTL_REG(0x0308) #define ZD_CR195 CTL_REG(0x030C) #define ZD_CR196 CTL_REG(0x0310) #define ZD_CR197 CTL_REG(0x0314) #define ZD_CR198 CTL_REG(0x0318) #define ZD_CR199 CTL_REG(0x031C) #define ZD_CR200 CTL_REG(0x0320) #define ZD_CR201 CTL_REG(0x0324) #define ZD_CR202 CTL_REG(0x0328) #define ZD_CR203 CTL_REG(0x032C) /* I2C bus template value & flash * control */ #define ZD_CR204 CTL_REG(0x0330) #define ZD_CR205 CTL_REG(0x0334) #define ZD_CR206 CTL_REG(0x0338) #define ZD_CR207 CTL_REG(0x033C) #define ZD_CR208 CTL_REG(0x0340) #define ZD_CR209 CTL_REG(0x0344) #define ZD_CR210 CTL_REG(0x0348) #define ZD_CR211 CTL_REG(0x034C) #define ZD_CR212 CTL_REG(0x0350) #define ZD_CR213 CTL_REG(0x0354) #define ZD_CR214 CTL_REG(0x0358) #define ZD_CR215 CTL_REG(0x035C) #define ZD_CR216 CTL_REG(0x0360) #define ZD_CR217 CTL_REG(0x0364) #define ZD_CR218 CTL_REG(0x0368) #define ZD_CR219 CTL_REG(0x036C) #define ZD_CR220 CTL_REG(0x0370) #define ZD_CR221 CTL_REG(0x0374) #define ZD_CR222 CTL_REG(0x0378) #define ZD_CR223 CTL_REG(0x037C) #define ZD_CR224 CTL_REG(0x0380) #define ZD_CR225 CTL_REG(0x0384) #define ZD_CR226 CTL_REG(0x0388) #define ZD_CR227 CTL_REG(0x038C) #define ZD_CR228 CTL_REG(0x0390) #define ZD_CR229 CTL_REG(0x0394) #define ZD_CR230 CTL_REG(0x0398) #define ZD_CR231 CTL_REG(0x039C) #define ZD_CR232 CTL_REG(0x03A0) #define ZD_CR233 CTL_REG(0x03A4) #define ZD_CR234 CTL_REG(0x03A8) #define ZD_CR235 CTL_REG(0x03AC) #define ZD_CR236 CTL_REG(0x03B0) #define ZD_CR240 CTL_REG(0x03C0) /* bit 7: host-controlled RF register writes * ZD_CR241-ZD_CR245: for hardware controlled writing of RF bits, not needed for * USB */ #define ZD_CR241 CTL_REG(0x03C4) #define ZD_CR242 CTL_REG(0x03C8) #define ZD_CR243 CTL_REG(0x03CC) #define ZD_CR244 CTL_REG(0x03D0) #define ZD_CR245 CTL_REG(0x03D4) #define ZD_CR251 CTL_REG(0x03EC) /* only used for activation and * deactivation of Airoha RFs AL2230 * and AL7230B */ #define ZD_CR252 CTL_REG(0x03F0) #define ZD_CR253 CTL_REG(0x03F4) #define ZD_CR254 CTL_REG(0x03F8) #define ZD_CR255 CTL_REG(0x03FC) #define CR_MAX_PHY_REG 255 /* Taken from the ZYDAS driver, not all of them are relevant for the ZD1211 * driver. */ #define CR_RF_IF_CLK CTL_REG(0x0400) #define CR_RF_IF_DATA CTL_REG(0x0404) #define CR_PE1_PE2 CTL_REG(0x0408) #define CR_PE2_DLY CTL_REG(0x040C) #define CR_LE1 CTL_REG(0x0410) #define CR_LE2 CTL_REG(0x0414) /* Seems to enable/disable GPI (General Purpose IO?) */ #define CR_GPI_EN CTL_REG(0x0418) #define CR_RADIO_PD CTL_REG(0x042C) #define CR_RF2948_PD CTL_REG(0x042C) #define CR_ENABLE_PS_MANUAL_AGC CTL_REG(0x043C) #define CR_CONFIG_PHILIPS CTL_REG(0x0440) #define CR_SA2400_SER_AP CTL_REG(0x0444) #define CR_I2C_WRITE CTL_REG(0x0444) #define CR_SA2400_SER_RP CTL_REG(0x0448) #define CR_RADIO_PE CTL_REG(0x0458) #define CR_RST_BUS_MASTER CTL_REG(0x045C) #define CR_RFCFG CTL_REG(0x0464) #define CR_HSTSCHG CTL_REG(0x046C) #define CR_PHY_ON CTL_REG(0x0474) #define CR_RX_DELAY CTL_REG(0x0478) #define CR_RX_PE_DELAY CTL_REG(0x047C) #define CR_GPIO_1 CTL_REG(0x0490) #define CR_GPIO_2 CTL_REG(0x0494) #define CR_EncryBufMux CTL_REG(0x04A8) #define CR_PS_CTRL CTL_REG(0x0500) #define CR_ADDA_PWR_DWN CTL_REG(0x0504) #define CR_ADDA_MBIAS_WARMTIME CTL_REG(0x0508) #define CR_MAC_PS_STATE CTL_REG(0x050C) #define CR_INTERRUPT CTL_REG(0x0510) #define INT_TX_COMPLETE (1 << 0) #define INT_RX_COMPLETE (1 << 1) #define INT_RETRY_FAIL (1 << 2) #define INT_WAKEUP (1 << 3) #define INT_DTIM_NOTIFY (1 << 5) #define INT_CFG_NEXT_BCN (1 << 6) #define INT_BUS_ABORT (1 << 7) #define INT_TX_FIFO_READY (1 << 8) #define INT_UART (1 << 9) #define INT_TX_COMPLETE_EN (1 << 16) #define INT_RX_COMPLETE_EN (1 << 17) #define INT_RETRY_FAIL_EN (1 << 18) #define INT_WAKEUP_EN (1 << 19) #define INT_DTIM_NOTIFY_EN (1 << 21) #define INT_CFG_NEXT_BCN_EN (1 << 22) #define INT_BUS_ABORT_EN (1 << 23) #define INT_TX_FIFO_READY_EN (1 << 24) #define INT_UART_EN (1 << 25) #define CR_TSF_LOW_PART CTL_REG(0x0514) #define CR_TSF_HIGH_PART CTL_REG(0x0518) /* Following three values are in time units (1024us) * Following condition must be met: * atim < tbtt < bcn */ #define CR_ATIM_WND_PERIOD CTL_REG(0x051C) #define CR_BCN_INTERVAL CTL_REG(0x0520) #define CR_PRE_TBTT CTL_REG(0x0524) /* in units of TU(1024us) */ /* for UART support */ #define CR_UART_RBR_THR_DLL CTL_REG(0x0540) #define CR_UART_DLM_IER CTL_REG(0x0544) #define CR_UART_IIR_FCR CTL_REG(0x0548) #define CR_UART_LCR CTL_REG(0x054c) #define CR_UART_MCR CTL_REG(0x0550) #define CR_UART_LSR CTL_REG(0x0554) #define CR_UART_MSR CTL_REG(0x0558) #define CR_UART_ECR CTL_REG(0x055c) #define CR_UART_STATUS CTL_REG(0x0560) #define CR_PCI_TX_ADDR_P1 CTL_REG(0x0600) #define CR_PCI_TX_AddR_P2 CTL_REG(0x0604) #define CR_PCI_RX_AddR_P1 CTL_REG(0x0608) #define CR_PCI_RX_AddR_P2 CTL_REG(0x060C) /* must be overwritten if custom MAC address will be used */ #define CR_MAC_ADDR_P1 CTL_REG(0x0610) #define CR_MAC_ADDR_P2 CTL_REG(0x0614) #define CR_BSSID_P1 CTL_REG(0x0618) #define CR_BSSID_P2 CTL_REG(0x061C) #define CR_BCN_PLCP_CFG CTL_REG(0x0620) /* Group hash table for filtering incoming packets. * * The group hash table is 64 bit large and split over two parts. The first * part is the lower part. The upper 6 bits of the last byte of the target * address are used as index. Packets are received if the hash table bit is * set. This is used for multicast handling, but for broadcasts (address * ff:ff:ff:ff:ff:ff) the highest bit in the second table must also be set. */ #define CR_GROUP_HASH_P1 CTL_REG(0x0624) #define CR_GROUP_HASH_P2 CTL_REG(0x0628) #define CR_RX_TIMEOUT CTL_REG(0x062C) /* Basic rates supported by the BSS. When producing ACK or CTS messages, the * device will use a rate in this table that is less than or equal to the rate * of the incoming frame which prompted the response. */ #define CR_BASIC_RATE_TBL CTL_REG(0x0630) #define CR_RATE_1M (1 << 0) /* 802.11b */ #define CR_RATE_2M (1 << 1) /* 802.11b */ #define CR_RATE_5_5M (1 << 2) /* 802.11b */ #define CR_RATE_11M (1 << 3) /* 802.11b */ #define CR_RATE_6M (1 << 8) /* 802.11g */ #define CR_RATE_9M (1 << 9) /* 802.11g */ #define CR_RATE_12M (1 << 10) /* 802.11g */ #define CR_RATE_18M (1 << 11) /* 802.11g */ #define CR_RATE_24M (1 << 12) /* 802.11g */ #define CR_RATE_36M (1 << 13) /* 802.11g */ #define CR_RATE_48M (1 << 14) /* 802.11g */ #define CR_RATE_54M (1 << 15) /* 802.11g */ #define CR_RATES_80211G 0xff00 #define CR_RATES_80211B 0x000f /* Mandatory rates required in the BSS. When producing ACK or CTS messages, if * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will * look for a rate in this table that is less than or equal to the rate of * the incoming frame. */ #define CR_MANDATORY_RATE_TBL CTL_REG(0x0634) #define CR_RTS_CTS_RATE CTL_REG(0x0638) /* These are all bit indexes in CR_RTS_CTS_RATE, so remember to shift. */ #define RTSCTS_SH_RTS_RATE 0 #define RTSCTS_SH_EXP_CTS_RATE 4 #define RTSCTS_SH_RTS_MOD_TYPE 8 #define RTSCTS_SH_RTS_PMB_TYPE 9 #define RTSCTS_SH_CTS_RATE 16 #define RTSCTS_SH_CTS_MOD_TYPE 24 #define RTSCTS_SH_CTS_PMB_TYPE 25 #define CR_WEP_PROTECT CTL_REG(0x063C) #define CR_RX_THRESHOLD CTL_REG(0x0640) /* register for controlling the LEDS */ #define CR_LED CTL_REG(0x0644) /* masks for controlling LEDs */ #define LED1 (1 << 8) #define LED2 (1 << 9) #define LED_SW (1 << 10) /* Seems to indicate that the configuration is over. */ #define CR_AFTER_PNP CTL_REG(0x0648) #define CR_ACK_TIME_80211 CTL_REG(0x0658) #define CR_RX_OFFSET CTL_REG(0x065c) #define CR_BCN_LENGTH CTL_REG(0x0664) #define CR_PHY_DELAY CTL_REG(0x066C) #define CR_BCN_FIFO CTL_REG(0x0670) #define CR_SNIFFER_ON CTL_REG(0x0674) #define CR_ENCRYPTION_TYPE CTL_REG(0x0678) #define NO_WEP 0 #define WEP64 1 #define WEP128 5 #define WEP256 6 #define ENC_SNIFFER 8 #define CR_ZD1211_RETRY_MAX CTL_REG(0x067C) #define CR_REG1 CTL_REG(0x0680) /* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical * registers, so one could argue it is a LOCK bit. But calling it * LOCK_PHY_REGS makes it confusing. */ #define UNLOCK_PHY_REGS (1 << 7) #define CR_DEVICE_STATE CTL_REG(0x0684) #define CR_UNDERRUN_CNT CTL_REG(0x0688) #define CR_RX_FILTER CTL_REG(0x068c) #define RX_FILTER_ASSOC_REQUEST (1 << 0) #define RX_FILTER_ASSOC_RESPONSE (1 << 1) #define RX_FILTER_REASSOC_REQUEST (1 << 2) #define RX_FILTER_REASSOC_RESPONSE (1 << 3) #define RX_FILTER_PROBE_REQUEST (1 << 4) #define RX_FILTER_PROBE_RESPONSE (1 << 5) /* bits 6 and 7 reserved */ #define RX_FILTER_BEACON (1 << 8) #define RX_FILTER_ATIM (1 << 9) #define RX_FILTER_DISASSOC (1 << 10) #define RX_FILTER_AUTH (1 << 11) #define RX_FILTER_DEAUTH (1 << 12) #define RX_FILTER_PSPOLL (1 << 26) #define RX_FILTER_RTS (1 << 27) #define RX_FILTER_CTS (1 << 28) #define RX_FILTER_ACK (1 << 29) #define RX_FILTER_CFEND (1 << 30) #define RX_FILTER_CFACK (1 << 31) /* Enable bits for all frames you are interested in. */ #define STA_RX_FILTER (RX_FILTER_ASSOC_REQUEST | RX_FILTER_ASSOC_RESPONSE | \ RX_FILTER_REASSOC_REQUEST | RX_FILTER_REASSOC_RESPONSE | \ RX_FILTER_PROBE_REQUEST | RX_FILTER_PROBE_RESPONSE | \ (0x3 << 6) /* vendor driver sets these reserved bits */ | \ RX_FILTER_BEACON | RX_FILTER_ATIM | RX_FILTER_DISASSOC | \ RX_FILTER_AUTH | RX_FILTER_DEAUTH | \ (0x7 << 13) /* vendor driver sets these reserved bits */ | \ RX_FILTER_PSPOLL | RX_FILTER_ACK) /* 0x2400ffff */ #define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \ RX_FILTER_CFEND | RX_FILTER_CFACK) #define BCN_MODE_AP 0x1000000 #define BCN_MODE_IBSS 0x2000000 /* Monitor mode sets filter to 0xfffff */ #define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690) #define CR_BCN_FIFO_SEMAPHORE CTL_REG(0x0694) #define CR_IFS_VALUE CTL_REG(0x0698) #define IFS_VALUE_DIFS_SH 0 #define IFS_VALUE_EIFS_SH 12 #define IFS_VALUE_SIFS_SH 24 #define IFS_VALUE_DEFAULT (( 50 << IFS_VALUE_DIFS_SH) | \ (1148 << IFS_VALUE_EIFS_SH) | \ ( 10 << IFS_VALUE_SIFS_SH)) #define CR_RX_TIME_OUT CTL_REG(0x069C) #define CR_TOTAL_RX_FRM CTL_REG(0x06A0) #define CR_CRC32_CNT CTL_REG(0x06A4) #define CR_CRC16_CNT CTL_REG(0x06A8) #define CR_DECRYPTION_ERR_UNI CTL_REG(0x06AC) #define CR_RX_FIFO_OVERRUN CTL_REG(0x06B0) #define CR_DECRYPTION_ERR_MUL CTL_REG(0x06BC) #define CR_NAV_CNT CTL_REG(0x06C4) #define CR_NAV_CCA CTL_REG(0x06C8) #define CR_RETRY_CNT CTL_REG(0x06CC) #define CR_READ_TCB_ADDR CTL_REG(0x06E8) #define CR_READ_RFD_ADDR CTL_REG(0x06EC) #define CR_CWMIN_CWMAX CTL_REG(0x06F0) #define CR_TOTAL_TX_FRM CTL_REG(0x06F4) /* CAM: Continuous Access Mode (power management) */ #define CR_CAM_MODE CTL_REG(0x0700) #define MODE_IBSS 0x0 #define MODE_AP 0x1 #define MODE_STA 0x2 #define MODE_AP_WDS 0x3 #define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704) #define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708) #define CR_CAM_ADDRESS CTL_REG(0x070C) #define CR_CAM_DATA CTL_REG(0x0710) #define CR_ROMDIR CTL_REG(0x0714) #define CR_DECRY_ERR_FLG_LOW CTL_REG(0x0714) #define CR_DECRY_ERR_FLG_HIGH CTL_REG(0x0718) #define CR_WEPKEY0 CTL_REG(0x0720) #define CR_WEPKEY1 CTL_REG(0x0724) #define CR_WEPKEY2 CTL_REG(0x0728) #define CR_WEPKEY3 CTL_REG(0x072C) #define CR_WEPKEY4 CTL_REG(0x0730) #define CR_WEPKEY5 CTL_REG(0x0734) #define CR_WEPKEY6 CTL_REG(0x0738) #define CR_WEPKEY7 CTL_REG(0x073C) #define CR_WEPKEY8 CTL_REG(0x0740) #define CR_WEPKEY9 CTL_REG(0x0744) #define CR_WEPKEY10 CTL_REG(0x0748) #define CR_WEPKEY11 CTL_REG(0x074C) #define CR_WEPKEY12 CTL_REG(0x0750) #define CR_WEPKEY13 CTL_REG(0x0754) #define CR_WEPKEY14 CTL_REG(0x0758) #define CR_WEPKEY15 CTL_REG(0x075c) #define CR_TKIP_MODE CTL_REG(0x0760) #define CR_EEPROM_PROTECT0 CTL_REG(0x0758) #define CR_EEPROM_PROTECT1 CTL_REG(0x075C) #define CR_DBG_FIFO_RD CTL_REG(0x0800) #define CR_DBG_SELECT CTL_REG(0x0804) #define CR_FIFO_Length CTL_REG(0x0808) #define CR_RSSI_MGC CTL_REG(0x0810) #define CR_PON CTL_REG(0x0818) #define CR_RX_ON CTL_REG(0x081C) #define CR_TX_ON CTL_REG(0x0820) #define CR_CHIP_EN CTL_REG(0x0824) #define CR_LO_SW CTL_REG(0x0828) #define CR_TXRX_SW CTL_REG(0x082C) #define CR_S_MD CTL_REG(0x0830) #define CR_USB_DEBUG_PORT CTL_REG(0x0888) #define CR_ZD1211B_CWIN_MAX_MIN_AC0 CTL_REG(0x0b00) #define CR_ZD1211B_CWIN_MAX_MIN_AC1 CTL_REG(0x0b04) #define CR_ZD1211B_CWIN_MAX_MIN_AC2 CTL_REG(0x0b08) #define CR_ZD1211B_CWIN_MAX_MIN_AC3 CTL_REG(0x0b0c) #define CR_ZD1211B_AIFS_CTL1 CTL_REG(0x0b10) #define CR_ZD1211B_AIFS_CTL2 CTL_REG(0x0b14) #define CR_ZD1211B_TXOP CTL_REG(0x0b20) #define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28) /* Value for CR_ZD1211_RETRY_MAX & CR_ZD1211B_RETRY_MAX. Vendor driver uses 2, * we use 0. The first rate is tried (count+2), then all next rates are tried * twice, until 1 Mbits is tried. */ #define ZD1211_RETRY_COUNT 0 #define ZD1211B_RETRY_COUNT \ (ZD1211_RETRY_COUNT << 0)| \ (ZD1211_RETRY_COUNT << 8)| \ (ZD1211_RETRY_COUNT << 16)| \ (ZD1211_RETRY_COUNT << 24) /* Used to detect PLL lock */ #define UW2453_INTR_REG ((zd_addr_t)0x85c1) #define CWIN_SIZE 0x007f043f #define HWINT_ENABLED \ (INT_TX_COMPLETE_EN| \ INT_RX_COMPLETE_EN| \ INT_RETRY_FAIL_EN| \ INT_WAKEUP_EN| \ INT_CFG_NEXT_BCN_EN) #define HWINT_DISABLED 0 #define E2P_PWR_INT_GUARD 8 #define E2P_CHANNEL_COUNT 14 /* If you compare this addresses with the ZYDAS orignal driver, please notify * that we use word mapping for the EEPROM. */ /* * Upper 16 bit contains the regulatory domain. */ #define E2P_SUBID E2P_DATA(0x00) #define E2P_POD E2P_DATA(0x02) #define E2P_MAC_ADDR_P1 E2P_DATA(0x04) #define E2P_MAC_ADDR_P2 E2P_DATA(0x06) #define E2P_PWR_CAL_VALUE1 E2P_DATA(0x08) #define E2P_PWR_CAL_VALUE2 E2P_DATA(0x0a) #define E2P_PWR_CAL_VALUE3 E2P_DATA(0x0c) #define E2P_PWR_CAL_VALUE4 E2P_DATA(0x0e) #define E2P_PWR_INT_VALUE1 E2P_DATA(0x10) #define E2P_PWR_INT_VALUE2 E2P_DATA(0x12) #define E2P_PWR_INT_VALUE3 E2P_DATA(0x14) #define E2P_PWR_INT_VALUE4 E2P_DATA(0x16) /* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30) * also only 11 channels. */ #define E2P_ALLOWED_CHANNEL E2P_DATA(0x18) #define E2P_DEVICE_VER E2P_DATA(0x20) #define E2P_PHY_REG E2P_DATA(0x25) #define E2P_36M_CAL_VALUE1 E2P_DATA(0x28) #define E2P_36M_CAL_VALUE2 E2P_DATA(0x2a) #define E2P_36M_CAL_VALUE3 E2P_DATA(0x2c) #define E2P_36M_CAL_VALUE4 E2P_DATA(0x2e) #define E2P_11A_INT_VALUE1 E2P_DATA(0x30) #define E2P_11A_INT_VALUE2 E2P_DATA(0x32) #define E2P_11A_INT_VALUE3 E2P_DATA(0x34) #define E2P_11A_INT_VALUE4 E2P_DATA(0x36) #define E2P_48M_CAL_VALUE1 E2P_DATA(0x38) #define E2P_48M_CAL_VALUE2 E2P_DATA(0x3a) #define E2P_48M_CAL_VALUE3 E2P_DATA(0x3c) #define E2P_48M_CAL_VALUE4 E2P_DATA(0x3e) #define E2P_48M_INT_VALUE1 E2P_DATA(0x40) #define E2P_48M_INT_VALUE2 E2P_DATA(0x42) #define E2P_48M_INT_VALUE3 E2P_DATA(0x44) #define E2P_48M_INT_VALUE4 E2P_DATA(0x46) #define E2P_54M_CAL_VALUE1 E2P_DATA(0x48) /* ??? */ #define E2P_54M_CAL_VALUE2 E2P_DATA(0x4a) #define E2P_54M_CAL_VALUE3 E2P_DATA(0x4c) #define E2P_54M_CAL_VALUE4 E2P_DATA(0x4e) #define E2P_54M_INT_VALUE1 E2P_DATA(0x50) #define E2P_54M_INT_VALUE2 E2P_DATA(0x52) #define E2P_54M_INT_VALUE3 E2P_DATA(0x54) #define E2P_54M_INT_VALUE4 E2P_DATA(0x56) /* This word contains the base address of the FW_REG_ registers below */ #define FWRAW_REGS_ADDR FWRAW_DATA(0x1d) /* All 16 bit values, offset from the address in FWRAW_REGS_ADDR */ enum { FW_REG_FIRMWARE_VER = 0, /* non-zero if USB high speed connection */ FW_REG_USB_SPEED = 1, FW_REG_FIX_TX_RATE = 2, /* Seems to be able to control LEDs over the firmware */ FW_REG_LED_LINK_STATUS = 3, FW_REG_SOFT_RESET = 4, FW_REG_FLASH_CHK = 5, }; /* Values for FW_LINK_STATUS */ #define FW_LINK_OFF 0x0 #define FW_LINK_TX 0x1 /* 0x2 - link led on? */ enum { /* indices for ofdm_cal_values */ OFDM_36M_INDEX = 0, OFDM_48M_INDEX = 1, OFDM_54M_INDEX = 2, }; struct zd_chip { struct zd_usb usb; struct zd_rf rf; struct mutex mutex; /* Base address of FW_REG_ registers */ zd_addr_t fw_regs_base; /* EepSetPoint in the vendor driver */ u8 pwr_cal_values[E2P_CHANNEL_COUNT]; /* integration values in the vendor driver */ u8 pwr_int_values[E2P_CHANNEL_COUNT]; /* SetPointOFDM in the vendor driver */ u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT]; u16 link_led; unsigned int pa_type:4, patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1, new_phy_layout:1, al2230s_bit:1, supports_tx_led:1; }; static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb) { return container_of(usb, struct zd_chip, usb); } static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf) { return container_of(rf, struct zd_chip, rf); } #define zd_chip_dev(chip) (&(chip)->usb.intf->dev) void zd_chip_init(struct zd_chip *chip, struct ieee80211_hw *hw, struct usb_interface *intf); void zd_chip_clear(struct zd_chip *chip); int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr); int zd_chip_init_hw(struct zd_chip *chip); int zd_chip_reset(struct zd_chip *chip); static inline int zd_chip_is_zd1211b(struct zd_chip *chip) { return chip->usb.is_zd1211b; } static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values, const zd_addr_t *addresses, unsigned int count) { ZD_ASSERT(mutex_is_locked(&chip->mutex)); return zd_usb_ioread16v(&chip->usb, values, addresses, count); } static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value, const zd_addr_t addr) { ZD_ASSERT(mutex_is_locked(&chip->mutex)); return zd_usb_ioread16(&chip->usb, value, addr); } int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addresses, unsigned int count); static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value, const zd_addr_t addr) { return zd_ioread32v_locked(chip, value, &addr, 1); } static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value, zd_addr_t addr) { struct zd_ioreq16 ioreq; ZD_ASSERT(mutex_is_locked(&chip->mutex)); ioreq.addr = addr; ioreq.value = value; return zd_usb_iowrite16v(&chip->usb, &ioreq, 1); } int zd_iowrite16a_locked(struct zd_chip *chip, const struct zd_ioreq16 *ioreqs, unsigned int count); int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, unsigned int count); static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value, zd_addr_t addr) { struct zd_ioreq32 ioreq; ioreq.addr = addr; ioreq.value = value; return _zd_iowrite32v_locked(chip, &ioreq, 1); } int zd_iowrite32a_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, unsigned int count); static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits) { ZD_ASSERT(mutex_is_locked(&chip->mutex)); return zd_usb_rfwrite(&chip->usb, value, bits); } int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value); int zd_rfwritev_locked(struct zd_chip *chip, const u32* values, unsigned int count, u8 bits); int zd_rfwritev_cr_locked(struct zd_chip *chip, const u32* values, unsigned int count); /* Locking functions for reading and writing registers. * The different parameters are intentional. */ int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value); int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value); int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value); int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value); int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, u32 *values, unsigned int count); int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, unsigned int count); int zd_chip_set_channel(struct zd_chip *chip, u8 channel); static inline u8 _zd_chip_get_channel(struct zd_chip *chip) { return chip->rf.channel; } u8 zd_chip_get_channel(struct zd_chip *chip); int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain); int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr); int zd_write_bssid(struct zd_chip *chip, const u8 *bssid); int zd_chip_switch_radio_on(struct zd_chip *chip); int zd_chip_switch_radio_off(struct zd_chip *chip); int zd_chip_enable_int(struct zd_chip *chip); void zd_chip_disable_int(struct zd_chip *chip); int zd_chip_enable_rxtx(struct zd_chip *chip); void zd_chip_disable_rxtx(struct zd_chip *chip); int zd_chip_enable_hwint(struct zd_chip *chip); int zd_chip_disable_hwint(struct zd_chip *chip); int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel); int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, int preamble); static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type) { return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type); } static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type) { return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type); } static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates) { return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates); } int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates); int zd_chip_lock_phy_regs(struct zd_chip *chip); int zd_chip_unlock_phy_regs(struct zd_chip *chip); enum led_status { ZD_LED_OFF = 0, ZD_LED_SCANNING = 1, ZD_LED_ASSOCIATED = 2, }; int zd_chip_control_leds(struct zd_chip *chip, enum led_status status); int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period, int type); static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval) { return zd_ioread32(chip, CR_BCN_INTERVAL, interval); } struct rx_status; u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status); struct zd_mc_hash { u32 low; u32 high; }; static inline void zd_mc_clear(struct zd_mc_hash *hash) { hash->low = 0; /* The interfaces must always received broadcasts. * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63. */ hash->high = 0x80000000; } static inline void zd_mc_add_all(struct zd_mc_hash *hash) { hash->low = hash->high = 0xffffffff; } static inline void zd_mc_add_addr(struct zd_mc_hash *hash, u8 *addr) { unsigned int i = addr[5] >> 2; if (i < 32) { hash->low |= 1 << i; } else { hash->high |= 1 << (i-32); } } int zd_chip_set_multicast_hash(struct zd_chip *chip, struct zd_mc_hash *hash); u64 zd_chip_get_tsf(struct zd_chip *chip); #endif /* _ZD_CHIP_H */ compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/zd_chip.c0000644000175000017500000011646212026211315023552 0ustar mcgrofmcgrof/* ZD1211 USB-WLAN driver for Linux * * Copyright (C) 2005-2007 Ulrich Kunitz * Copyright (C) 2006-2007 Daniel Drake * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file implements all the hardware specific functions for the ZD1211 * and ZD1211B chips. Support for the ZD1211B was possible after Timothy * Legge sent me a ZD1211B device. Thank you Tim. -- Uli */ #include #include #include #include "zd_def.h" #include "zd_chip.h" #include "zd_mac.h" #include "zd_rf.h" void zd_chip_init(struct zd_chip *chip, struct ieee80211_hw *hw, struct usb_interface *intf) { memset(chip, 0, sizeof(*chip)); mutex_init(&chip->mutex); zd_usb_init(&chip->usb, hw, intf); zd_rf_init(&chip->rf); } void zd_chip_clear(struct zd_chip *chip) { ZD_ASSERT(!mutex_is_locked(&chip->mutex)); zd_usb_clear(&chip->usb); zd_rf_clear(&chip->rf); mutex_destroy(&chip->mutex); ZD_MEMCLEAR(chip, sizeof(*chip)); } static int scnprint_mac_oui(struct zd_chip *chip, char *buffer, size_t size) { u8 *addr = zd_mac_get_perm_addr(zd_chip_to_mac(chip)); return scnprintf(buffer, size, "%02x-%02x-%02x", addr[0], addr[1], addr[2]); } /* Prints an identifier line, which will support debugging. */ static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) { int i = 0; i = scnprintf(buffer, size, "zd1211%s chip ", zd_chip_is_zd1211b(chip) ? "b" : ""); i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i); i += scnprintf(buffer+i, size-i, " "); i += scnprint_mac_oui(chip, buffer+i, size-i); i += scnprintf(buffer+i, size-i, " "); i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type, chip->patch_cck_gain ? 'g' : '-', chip->patch_cr157 ? '7' : '-', chip->patch_6m_band_edge ? '6' : '-', chip->new_phy_layout ? 'N' : '-', chip->al2230s_bit ? 'S' : '-'); return i; } static void print_id(struct zd_chip *chip) { char buffer[80]; scnprint_id(chip, buffer, sizeof(buffer)); buffer[sizeof(buffer)-1] = 0; dev_info(zd_chip_dev(chip), "%s\n", buffer); } static zd_addr_t inc_addr(zd_addr_t addr) { u16 a = (u16)addr; /* Control registers use byte addressing, but everything else uses word * addressing. */ if ((a & 0xf000) == CR_START) a += 2; else a += 1; return (zd_addr_t)a; } /* Read a variable number of 32-bit values. Parameter count is not allowed to * exceed USB_MAX_IOREAD32_COUNT. */ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr, unsigned int count) { int r; int i; zd_addr_t a16[USB_MAX_IOREAD32_COUNT * 2]; u16 v16[USB_MAX_IOREAD32_COUNT * 2]; unsigned int count16; if (count > USB_MAX_IOREAD32_COUNT) return -EINVAL; /* Use stack for values and addresses. */ count16 = 2 * count; BUG_ON(count16 * sizeof(zd_addr_t) > sizeof(a16)); BUG_ON(count16 * sizeof(u16) > sizeof(v16)); for (i = 0; i < count; i++) { int j = 2*i; /* We read the high word always first. */ a16[j] = inc_addr(addr[i]); a16[j+1] = addr[i]; } r = zd_ioread16v_locked(chip, v16, a16, count16); if (r) { dev_dbg_f(zd_chip_dev(chip), "error: zd_ioread16v_locked. Error number %d\n", r); return r; } for (i = 0; i < count; i++) { int j = 2*i; values[i] = (v16[j] << 16) | v16[j+1]; } return 0; } static int _zd_iowrite32v_async_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, unsigned int count) { int i, j, r; struct zd_ioreq16 ioreqs16[USB_MAX_IOWRITE32_COUNT * 2]; unsigned int count16; /* Use stack for values and addresses. */ ZD_ASSERT(mutex_is_locked(&chip->mutex)); if (count == 0) return 0; if (count > USB_MAX_IOWRITE32_COUNT) return -EINVAL; count16 = 2 * count; BUG_ON(count16 * sizeof(struct zd_ioreq16) > sizeof(ioreqs16)); for (i = 0; i < count; i++) { j = 2*i; /* We write the high word always first. */ ioreqs16[j].value = ioreqs[i].value >> 16; ioreqs16[j].addr = inc_addr(ioreqs[i].addr); ioreqs16[j+1].value = ioreqs[i].value; ioreqs16[j+1].addr = ioreqs[i].addr; } r = zd_usb_iowrite16v_async(&chip->usb, ioreqs16, count16); #ifdef DEBUG if (r) { dev_dbg_f(zd_chip_dev(chip), "error %d in zd_usb_write16v\n", r); } #endif /* DEBUG */ return r; } int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, unsigned int count) { int r; zd_usb_iowrite16v_async_start(&chip->usb); r = _zd_iowrite32v_async_locked(chip, ioreqs, count); if (r) { zd_usb_iowrite16v_async_end(&chip->usb, 0); return r; } return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); } int zd_iowrite16a_locked(struct zd_chip *chip, const struct zd_ioreq16 *ioreqs, unsigned int count) { int r; unsigned int i, j, t, max; ZD_ASSERT(mutex_is_locked(&chip->mutex)); zd_usb_iowrite16v_async_start(&chip->usb); for (i = 0; i < count; i += j + t) { t = 0; max = count-i; if (max > USB_MAX_IOWRITE16_COUNT) max = USB_MAX_IOWRITE16_COUNT; for (j = 0; j < max; j++) { if (!ioreqs[i+j].addr) { t = 1; break; } } r = zd_usb_iowrite16v_async(&chip->usb, &ioreqs[i], j); if (r) { zd_usb_iowrite16v_async_end(&chip->usb, 0); dev_dbg_f(zd_chip_dev(chip), "error zd_usb_iowrite16v. Error number %d\n", r); return r; } } return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); } /* Writes a variable number of 32 bit registers. The functions will split * that in several USB requests. A split can be forced by inserting an IO * request with an zero address field. */ int zd_iowrite32a_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, unsigned int count) { int r; unsigned int i, j, t, max; zd_usb_iowrite16v_async_start(&chip->usb); for (i = 0; i < count; i += j + t) { t = 0; max = count-i; if (max > USB_MAX_IOWRITE32_COUNT) max = USB_MAX_IOWRITE32_COUNT; for (j = 0; j < max; j++) { if (!ioreqs[i+j].addr) { t = 1; break; } } r = _zd_iowrite32v_async_locked(chip, &ioreqs[i], j); if (r) { zd_usb_iowrite16v_async_end(&chip->usb, 0); dev_dbg_f(zd_chip_dev(chip), "error _zd_iowrite32v_locked." " Error number %d\n", r); return r; } } return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); } int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value) { int r; mutex_lock(&chip->mutex); r = zd_ioread16_locked(chip, value, addr); mutex_unlock(&chip->mutex); return r; } int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value) { int r; mutex_lock(&chip->mutex); r = zd_ioread32_locked(chip, value, addr); mutex_unlock(&chip->mutex); return r; } int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value) { int r; mutex_lock(&chip->mutex); r = zd_iowrite16_locked(chip, value, addr); mutex_unlock(&chip->mutex); return r; } int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value) { int r; mutex_lock(&chip->mutex); r = zd_iowrite32_locked(chip, value, addr); mutex_unlock(&chip->mutex); return r; } int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, u32 *values, unsigned int count) { int r; mutex_lock(&chip->mutex); r = zd_ioread32v_locked(chip, values, addresses, count); mutex_unlock(&chip->mutex); return r; } int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, unsigned int count) { int r; mutex_lock(&chip->mutex); r = zd_iowrite32a_locked(chip, ioreqs, count); mutex_unlock(&chip->mutex); return r; } static int read_pod(struct zd_chip *chip, u8 *rf_type) { int r; u32 value; ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_ioread32_locked(chip, &value, E2P_POD); if (r) goto error; dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value); /* FIXME: AL2230 handling (Bit 7 in POD) */ *rf_type = value & 0x0f; chip->pa_type = (value >> 16) & 0x0f; chip->patch_cck_gain = (value >> 8) & 0x1; chip->patch_cr157 = (value >> 13) & 0x1; chip->patch_6m_band_edge = (value >> 21) & 0x1; chip->new_phy_layout = (value >> 31) & 0x1; chip->al2230s_bit = (value >> 7) & 0x1; chip->link_led = ((value >> 4) & 1) ? LED1 : LED2; chip->supports_tx_led = 1; if (value & (1 << 24)) { /* LED scenario */ if (value & (1 << 29)) chip->supports_tx_led = 0; } dev_dbg_f(zd_chip_dev(chip), "RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d " "patch 6M %d new PHY %d link LED%d tx led %d\n", zd_rf_name(*rf_type), *rf_type, chip->pa_type, chip->patch_cck_gain, chip->patch_cr157, chip->patch_6m_band_edge, chip->new_phy_layout, chip->link_led == LED1 ? 1 : 2, chip->supports_tx_led); return 0; error: *rf_type = 0; chip->pa_type = 0; chip->patch_cck_gain = 0; chip->patch_cr157 = 0; chip->patch_6m_band_edge = 0; chip->new_phy_layout = 0; return r; } static int zd_write_mac_addr_common(struct zd_chip *chip, const u8 *mac_addr, const struct zd_ioreq32 *in_reqs, const char *type) { int r; struct zd_ioreq32 reqs[2] = {in_reqs[0], in_reqs[1]}; if (mac_addr) { reqs[0].value = (mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | mac_addr[0]; reqs[1].value = (mac_addr[5] << 8) | mac_addr[4]; dev_dbg_f(zd_chip_dev(chip), "%s addr %pM\n", type, mac_addr); } else { dev_dbg_f(zd_chip_dev(chip), "set NULL %s\n", type); } mutex_lock(&chip->mutex); r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); mutex_unlock(&chip->mutex); return r; } /* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and * CR_MAC_ADDR_P2 must be overwritten */ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr) { static const struct zd_ioreq32 reqs[2] = { [0] = { .addr = CR_MAC_ADDR_P1 }, [1] = { .addr = CR_MAC_ADDR_P2 }, }; return zd_write_mac_addr_common(chip, mac_addr, reqs, "mac"); } int zd_write_bssid(struct zd_chip *chip, const u8 *bssid) { static const struct zd_ioreq32 reqs[2] = { [0] = { .addr = CR_BSSID_P1 }, [1] = { .addr = CR_BSSID_P2 }, }; return zd_write_mac_addr_common(chip, bssid, reqs, "bssid"); } int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain) { int r; u32 value; mutex_lock(&chip->mutex); r = zd_ioread32_locked(chip, &value, E2P_SUBID); mutex_unlock(&chip->mutex); if (r) return r; *regdomain = value >> 16; dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain); return 0; } static int read_values(struct zd_chip *chip, u8 *values, size_t count, zd_addr_t e2p_addr, u32 guard) { int r; int i; u32 v; ZD_ASSERT(mutex_is_locked(&chip->mutex)); for (i = 0;;) { r = zd_ioread32_locked(chip, &v, (zd_addr_t)((u16)e2p_addr+i/2)); if (r) return r; v -= guard; if (i+4 < count) { values[i++] = v; values[i++] = v >> 8; values[i++] = v >> 16; values[i++] = v >> 24; continue; } for (;i < count; i++) values[i] = v >> (8*(i%3)); return 0; } } static int read_pwr_cal_values(struct zd_chip *chip) { return read_values(chip, chip->pwr_cal_values, E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1, 0); } static int read_pwr_int_values(struct zd_chip *chip) { return read_values(chip, chip->pwr_int_values, E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1, E2P_PWR_INT_GUARD); } static int read_ofdm_cal_values(struct zd_chip *chip) { int r; int i; static const zd_addr_t addresses[] = { E2P_36M_CAL_VALUE1, E2P_48M_CAL_VALUE1, E2P_54M_CAL_VALUE1, }; for (i = 0; i < 3; i++) { r = read_values(chip, chip->ofdm_cal_values[i], E2P_CHANNEL_COUNT, addresses[i], 0); if (r) return r; } return 0; } static int read_cal_int_tables(struct zd_chip *chip) { int r; r = read_pwr_cal_values(chip); if (r) return r; r = read_pwr_int_values(chip); if (r) return r; r = read_ofdm_cal_values(chip); if (r) return r; return 0; } /* phy means physical registers */ int zd_chip_lock_phy_regs(struct zd_chip *chip) { int r; u32 tmp; ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_ioread32_locked(chip, &tmp, CR_REG1); if (r) { dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r); return r; } tmp &= ~UNLOCK_PHY_REGS; r = zd_iowrite32_locked(chip, tmp, CR_REG1); if (r) dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); return r; } int zd_chip_unlock_phy_regs(struct zd_chip *chip) { int r; u32 tmp; ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_ioread32_locked(chip, &tmp, CR_REG1); if (r) { dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r); return r; } tmp |= UNLOCK_PHY_REGS; r = zd_iowrite32_locked(chip, tmp, CR_REG1); if (r) dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); return r; } /* ZD_CR157 can be optionally patched by the EEPROM for original ZD1211 */ static int patch_cr157(struct zd_chip *chip) { int r; u16 value; if (!chip->patch_cr157) return 0; r = zd_ioread16_locked(chip, &value, E2P_PHY_REG); if (r) return r; dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8); return zd_iowrite32_locked(chip, value >> 8, ZD_CR157); } /* * 6M band edge can be optionally overwritten for certain RF's * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge * bit (for AL2230, AL2230S) */ static int patch_6m_band_edge(struct zd_chip *chip, u8 channel) { ZD_ASSERT(mutex_is_locked(&chip->mutex)); if (!chip->patch_6m_band_edge) return 0; return zd_rf_patch_6m_band_edge(&chip->rf, channel); } /* Generic implementation of 6M band edge patching, used by most RFs via * zd_rf_generic_patch_6m() */ int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel) { struct zd_ioreq16 ioreqs[] = { { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR47, 0x1e }, }; /* FIXME: Channel 11 is not the edge for all regulatory domains. */ if (channel == 1 || channel == 11) ioreqs[0].value = 0x12; dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int zd1211_hw_reset_phy(struct zd_chip *chip) { static const struct zd_ioreq16 ioreqs[] = { { ZD_CR0, 0x0a }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 }, { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xa0 }, { ZD_CR10, 0x81 }, { ZD_CR11, 0x00 }, { ZD_CR12, 0x7f }, { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 }, { ZD_CR15, 0x3d }, { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e }, { ZD_CR18, 0x0a }, { ZD_CR19, 0x48 }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0c }, { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 }, { ZD_CR24, 0x14 }, { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 }, { ZD_CR27, 0x19 }, { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 }, { ZD_CR30, 0x4b }, { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 }, { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 }, { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c }, { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 }, { ZD_CR43, 0x10 }, { ZD_CR44, 0x12 }, { ZD_CR46, 0xff }, { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b }, { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 }, { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 }, { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff }, { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 }, { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 }, { ZD_CR79, 0x68 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 }, { ZD_CR82, 0x00 }, { ZD_CR83, 0x00 }, { ZD_CR84, 0x00 }, { ZD_CR85, 0x02 }, { ZD_CR86, 0x00 }, { ZD_CR87, 0x00 }, { ZD_CR88, 0xff }, { ZD_CR89, 0xfc }, { ZD_CR90, 0x00 }, { ZD_CR91, 0x00 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x08 }, { ZD_CR94, 0x00 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0xff }, { ZD_CR97, 0xe7 }, { ZD_CR98, 0x00 }, { ZD_CR99, 0x00 }, { ZD_CR100, 0x00 }, { ZD_CR101, 0xae }, { ZD_CR102, 0x02 }, { ZD_CR103, 0x00 }, { ZD_CR104, 0x03 }, { ZD_CR105, 0x65 }, { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 }, { ZD_CR108, 0x0a }, { ZD_CR109, 0xaa }, { ZD_CR110, 0xaa }, { ZD_CR111, 0x25 }, { ZD_CR112, 0x25 }, { ZD_CR113, 0x00 }, { ZD_CR119, 0x1e }, { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 }, { }, { ZD_CR5, 0x00 }, { ZD_CR6, 0x00 }, { ZD_CR7, 0x00 }, { ZD_CR8, 0x00 }, { ZD_CR9, 0x20 }, { ZD_CR12, 0xf0 }, { ZD_CR20, 0x0e }, { ZD_CR21, 0x0e }, { ZD_CR27, 0x10 }, { ZD_CR44, 0x33 }, { ZD_CR47, 0x1E }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 }, { ZD_CR85, 0x00 }, { ZD_CR86, 0x0C }, { ZD_CR87, 0x12 }, { ZD_CR88, 0x0C }, { ZD_CR89, 0x00 }, { ZD_CR90, 0x10 }, { ZD_CR91, 0x08 }, { ZD_CR93, 0x00 }, { ZD_CR94, 0x01 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0x50 }, { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 }, { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 }, { ZD_CR105, 0x12 }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 }, { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 }, { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR120, 0x4f }, { ZD_CR125, 0xaa }, { ZD_CR127, 0x03 }, { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR131, 0x0C }, { ZD_CR136, 0xdf }, { ZD_CR137, 0x40 }, { ZD_CR138, 0xa0 }, { ZD_CR139, 0xb0 }, { ZD_CR140, 0x99 }, { ZD_CR141, 0x82 }, { ZD_CR142, 0x54 }, { ZD_CR143, 0x1c }, { ZD_CR144, 0x6c }, { ZD_CR147, 0x07 }, { ZD_CR148, 0x4c }, { ZD_CR149, 0x50 }, { ZD_CR150, 0x0e }, { ZD_CR151, 0x18 }, { ZD_CR160, 0xfe }, { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa }, { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe }, { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba }, { ZD_CR170, 0xba }, { ZD_CR171, 0xba }, /* Note: ZD_CR204 must lead the ZD_CR203 */ { ZD_CR204, 0x7d }, { }, { ZD_CR203, 0x30 }, }; int r, t; dev_dbg_f(zd_chip_dev(chip), "\n"); r = zd_chip_lock_phy_regs(chip); if (r) goto out; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) goto unlock; r = patch_cr157(chip); unlock: t = zd_chip_unlock_phy_regs(chip); if (t && !r) r = t; out: return r; } static int zd1211b_hw_reset_phy(struct zd_chip *chip) { static const struct zd_ioreq16 ioreqs[] = { { ZD_CR0, 0x14 }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 }, { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xe0 }, { ZD_CR10, 0x81 }, /* power control { { ZD_CR11, 1 << 6 }, */ { ZD_CR11, 0x00 }, { ZD_CR12, 0xf0 }, { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 }, { ZD_CR15, 0x3d }, { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e }, { ZD_CR18, 0x0a }, { ZD_CR19, 0x48 }, { ZD_CR20, 0x10 }, /* Org:0x0E, ComTrend:RalLink AP */ { ZD_CR21, 0x0e }, { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 }, { ZD_CR24, 0x14 }, { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 }, { ZD_CR27, 0x10 }, { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 }, { ZD_CR30, 0x4b }, /* ASIC/FWT, no jointly decoder */ { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 }, { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 }, { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c }, { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 }, { ZD_CR43, 0x10 }, { ZD_CR44, 0x33 }, { ZD_CR46, 0xff }, { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b }, { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 }, { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 }, { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff }, { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 }, { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 }, { ZD_CR79, 0xf0 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 }, { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 }, { ZD_CR85, 0x00 }, { ZD_CR86, 0x0c }, { ZD_CR87, 0x12 }, { ZD_CR88, 0x0c }, { ZD_CR89, 0x00 }, { ZD_CR90, 0x58 }, { ZD_CR91, 0x04 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x00 }, { ZD_CR94, 0x01 }, { ZD_CR95, 0x20 }, /* ZD1211B */ { ZD_CR96, 0x50 }, { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 }, { ZD_CR99, 0x00 }, { ZD_CR100, 0x01 }, { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 }, { ZD_CR105, 0x12 }, { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 }, { ZD_CR108, 0x0a }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 }, { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 }, { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x1e }, { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 }, { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR131, 0x0c }, { ZD_CR136, 0xdf }, { ZD_CR137, 0xa0 }, { ZD_CR138, 0xa8 }, { ZD_CR139, 0xb4 }, { ZD_CR140, 0x98 }, { ZD_CR141, 0x82 }, { ZD_CR142, 0x53 }, { ZD_CR143, 0x1c }, { ZD_CR144, 0x6c }, { ZD_CR147, 0x07 }, { ZD_CR148, 0x40 }, { ZD_CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */ { ZD_CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */ { ZD_CR151, 0x18 }, { ZD_CR159, 0x70 }, { ZD_CR160, 0xfe }, { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa }, { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe }, { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba }, { ZD_CR170, 0xba }, { ZD_CR171, 0xba }, /* Note: ZD_CR204 must lead the ZD_CR203 */ { ZD_CR204, 0x7d }, {}, { ZD_CR203, 0x30 }, }; int r, t; dev_dbg_f(zd_chip_dev(chip), "\n"); r = zd_chip_lock_phy_regs(chip); if (r) goto out; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); t = zd_chip_unlock_phy_regs(chip); if (t && !r) r = t; out: return r; } static int hw_reset_phy(struct zd_chip *chip) { return zd_chip_is_zd1211b(chip) ? zd1211b_hw_reset_phy(chip) : zd1211_hw_reset_phy(chip); } static int zd1211_hw_init_hmac(struct zd_chip *chip) { static const struct zd_ioreq32 ioreqs[] = { { CR_ZD1211_RETRY_MAX, ZD1211_RETRY_COUNT }, { CR_RX_THRESHOLD, 0x000c0640 }, }; dev_dbg_f(zd_chip_dev(chip), "\n"); ZD_ASSERT(mutex_is_locked(&chip->mutex)); return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int zd1211b_hw_init_hmac(struct zd_chip *chip) { static const struct zd_ioreq32 ioreqs[] = { { CR_ZD1211B_RETRY_MAX, ZD1211B_RETRY_COUNT }, { CR_ZD1211B_CWIN_MAX_MIN_AC0, 0x007f003f }, { CR_ZD1211B_CWIN_MAX_MIN_AC1, 0x007f003f }, { CR_ZD1211B_CWIN_MAX_MIN_AC2, 0x003f001f }, { CR_ZD1211B_CWIN_MAX_MIN_AC3, 0x001f000f }, { CR_ZD1211B_AIFS_CTL1, 0x00280028 }, { CR_ZD1211B_AIFS_CTL2, 0x008C003C }, { CR_ZD1211B_TXOP, 0x01800824 }, { CR_RX_THRESHOLD, 0x000c0eff, }, }; dev_dbg_f(zd_chip_dev(chip), "\n"); ZD_ASSERT(mutex_is_locked(&chip->mutex)); return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int hw_init_hmac(struct zd_chip *chip) { int r; static const struct zd_ioreq32 ioreqs[] = { { CR_ACK_TIMEOUT_EXT, 0x20 }, { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, { CR_SNIFFER_ON, 0 }, { CR_RX_FILTER, STA_RX_FILTER }, { CR_GROUP_HASH_P1, 0x00 }, { CR_GROUP_HASH_P2, 0x80000000 }, { CR_REG1, 0xa4 }, { CR_ADDA_PWR_DWN, 0x7f }, { CR_BCN_PLCP_CFG, 0x00f00401 }, { CR_PHY_DELAY, 0x00 }, { CR_ACK_TIMEOUT_EXT, 0x80 }, { CR_ADDA_PWR_DWN, 0x00 }, { CR_ACK_TIME_80211, 0x100 }, { CR_RX_PE_DELAY, 0x70 }, { CR_PS_CTRL, 0x10000000 }, { CR_RTS_CTS_RATE, 0x02030203 }, { CR_AFTER_PNP, 0x1 }, { CR_WEP_PROTECT, 0x114 }, { CR_IFS_VALUE, IFS_VALUE_DEFAULT }, { CR_CAM_MODE, MODE_AP_WDS}, }; ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) return r; return zd_chip_is_zd1211b(chip) ? zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip); } struct aw_pt_bi { u32 atim_wnd_period; u32 pre_tbtt; u32 beacon_interval; }; static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) { int r; static const zd_addr_t aw_pt_bi_addr[] = { CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL }; u32 values[3]; r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, ARRAY_SIZE(aw_pt_bi_addr)); if (r) { memset(s, 0, sizeof(*s)); return r; } s->atim_wnd_period = values[0]; s->pre_tbtt = values[1]; s->beacon_interval = values[2]; return 0; } static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) { struct zd_ioreq32 reqs[3]; u16 b_interval = s->beacon_interval & 0xffff; if (b_interval <= 5) b_interval = 5; if (s->pre_tbtt < 4 || s->pre_tbtt >= b_interval) s->pre_tbtt = b_interval - 1; if (s->atim_wnd_period >= s->pre_tbtt) s->atim_wnd_period = s->pre_tbtt - 1; reqs[0].addr = CR_ATIM_WND_PERIOD; reqs[0].value = s->atim_wnd_period; reqs[1].addr = CR_PRE_TBTT; reqs[1].value = s->pre_tbtt; reqs[2].addr = CR_BCN_INTERVAL; reqs[2].value = (s->beacon_interval & ~0xffff) | b_interval; return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); } static int set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period, int type) { int r; struct aw_pt_bi s; u32 b_interval, mode_flag; ZD_ASSERT(mutex_is_locked(&chip->mutex)); if (interval > 0) { switch (type) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: mode_flag = BCN_MODE_IBSS; break; case NL80211_IFTYPE_AP: mode_flag = BCN_MODE_AP; break; default: mode_flag = 0; break; } } else { dtim_period = 0; mode_flag = 0; } b_interval = mode_flag | (dtim_period << 16) | interval; r = zd_iowrite32_locked(chip, b_interval, CR_BCN_INTERVAL); if (r) return r; r = get_aw_pt_bi(chip, &s); if (r) return r; return set_aw_pt_bi(chip, &s); } int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period, int type) { int r; mutex_lock(&chip->mutex); r = set_beacon_interval(chip, interval, dtim_period, type); mutex_unlock(&chip->mutex); return r; } static int hw_init(struct zd_chip *chip) { int r; dev_dbg_f(zd_chip_dev(chip), "\n"); ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = hw_reset_phy(chip); if (r) return r; r = hw_init_hmac(chip); if (r) return r; return set_beacon_interval(chip, 100, 0, NL80211_IFTYPE_UNSPECIFIED); } static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset) { return (zd_addr_t)((u16)chip->fw_regs_base + offset); } #ifdef DEBUG static int dump_cr(struct zd_chip *chip, const zd_addr_t addr, const char *addr_string) { int r; u32 value; r = zd_ioread32_locked(chip, &value, addr); if (r) { dev_dbg_f(zd_chip_dev(chip), "error reading %s. Error number %d\n", addr_string, r); return r; } dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n", addr_string, (unsigned int)value); return 0; } static int test_init(struct zd_chip *chip) { int r; r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP"); if (r) return r; r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN"); if (r) return r; return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT"); } static void dump_fw_registers(struct zd_chip *chip) { const zd_addr_t addr[4] = { fw_reg_addr(chip, FW_REG_FIRMWARE_VER), fw_reg_addr(chip, FW_REG_USB_SPEED), fw_reg_addr(chip, FW_REG_FIX_TX_RATE), fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), }; int r; u16 values[4]; r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr, ARRAY_SIZE(addr)); if (r) { dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n", r); return; } dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]); dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]); dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]); dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]); } #endif /* DEBUG */ static int print_fw_version(struct zd_chip *chip) { struct wiphy *wiphy = zd_chip_to_mac(chip)->hw->wiphy; int r; u16 version; r = zd_ioread16_locked(chip, &version, fw_reg_addr(chip, FW_REG_FIRMWARE_VER)); if (r) return r; dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version); snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%04hx", version); return 0; } static int set_mandatory_rates(struct zd_chip *chip, int gmode) { u32 rates; ZD_ASSERT(mutex_is_locked(&chip->mutex)); /* This sets the mandatory rates, which only depend from the standard * that the device is supporting. Until further notice we should try * to support 802.11g also for full speed USB. */ if (!gmode) rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M; else rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M| CR_RATE_6M|CR_RATE_12M|CR_RATE_24M; return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL); } int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, int preamble) { u32 value = 0; dev_dbg_f(zd_chip_dev(chip), "preamble=%x\n", preamble); value |= preamble << RTSCTS_SH_RTS_PMB_TYPE; value |= preamble << RTSCTS_SH_CTS_PMB_TYPE; /* We always send 11M RTS/self-CTS messages, like the vendor driver. */ value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_RTS_RATE; value |= ZD_RX_CCK << RTSCTS_SH_RTS_MOD_TYPE; value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_CTS_RATE; value |= ZD_RX_CCK << RTSCTS_SH_CTS_MOD_TYPE; return zd_iowrite32_locked(chip, value, CR_RTS_CTS_RATE); } int zd_chip_enable_hwint(struct zd_chip *chip) { int r; mutex_lock(&chip->mutex); r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT); mutex_unlock(&chip->mutex); return r; } static int disable_hwint(struct zd_chip *chip) { return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT); } int zd_chip_disable_hwint(struct zd_chip *chip) { int r; mutex_lock(&chip->mutex); r = disable_hwint(chip); mutex_unlock(&chip->mutex); return r; } static int read_fw_regs_offset(struct zd_chip *chip) { int r; ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_ioread16_locked(chip, (u16*)&chip->fw_regs_base, FWRAW_REGS_ADDR); if (r) return r; dev_dbg_f(zd_chip_dev(chip), "fw_regs_base: %#06hx\n", (u16)chip->fw_regs_base); return 0; } /* Read mac address using pre-firmware interface */ int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr) { dev_dbg_f(zd_chip_dev(chip), "\n"); return zd_usb_read_fw(&chip->usb, E2P_MAC_ADDR_P1, addr, ETH_ALEN); } int zd_chip_init_hw(struct zd_chip *chip) { int r; u8 rf_type; dev_dbg_f(zd_chip_dev(chip), "\n"); mutex_lock(&chip->mutex); #ifdef DEBUG r = test_init(chip); if (r) goto out; #endif r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP); if (r) goto out; r = read_fw_regs_offset(chip); if (r) goto out; /* GPI is always disabled, also in the other driver. */ r = zd_iowrite32_locked(chip, 0, CR_GPI_EN); if (r) goto out; r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX); if (r) goto out; /* Currently we support IEEE 802.11g for full and high speed USB. * It might be discussed, whether we should suppport pure b mode for * full speed USB. */ r = set_mandatory_rates(chip, 1); if (r) goto out; /* Disabling interrupts is certainly a smart thing here. */ r = disable_hwint(chip); if (r) goto out; r = read_pod(chip, &rf_type); if (r) goto out; r = hw_init(chip); if (r) goto out; r = zd_rf_init_hw(&chip->rf, rf_type); if (r) goto out; r = print_fw_version(chip); if (r) goto out; #ifdef DEBUG dump_fw_registers(chip); r = test_init(chip); if (r) goto out; #endif /* DEBUG */ r = read_cal_int_tables(chip); if (r) goto out; print_id(chip); out: mutex_unlock(&chip->mutex); return r; } static int update_pwr_int(struct zd_chip *chip, u8 channel) { u8 value = chip->pwr_int_values[channel - 1]; return zd_iowrite16_locked(chip, value, ZD_CR31); } static int update_pwr_cal(struct zd_chip *chip, u8 channel) { u8 value = chip->pwr_cal_values[channel-1]; return zd_iowrite16_locked(chip, value, ZD_CR68); } static int update_ofdm_cal(struct zd_chip *chip, u8 channel) { struct zd_ioreq16 ioreqs[3]; ioreqs[0].addr = ZD_CR67; ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1]; ioreqs[1].addr = ZD_CR66; ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1]; ioreqs[2].addr = ZD_CR65; ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1]; return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int update_channel_integration_and_calibration(struct zd_chip *chip, u8 channel) { int r; if (!zd_rf_should_update_pwr_int(&chip->rf)) return 0; r = update_pwr_int(chip, channel); if (r) return r; if (zd_chip_is_zd1211b(chip)) { static const struct zd_ioreq16 ioreqs[] = { { ZD_CR69, 0x28 }, {}, { ZD_CR69, 0x2a }, }; r = update_ofdm_cal(chip, channel); if (r) return r; r = update_pwr_cal(chip, channel); if (r) return r; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) return r; } return 0; } /* The CCK baseband gain can be optionally patched by the EEPROM */ static int patch_cck_gain(struct zd_chip *chip) { int r; u32 value; if (!chip->patch_cck_gain || !zd_rf_should_patch_cck_gain(&chip->rf)) return 0; ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); if (r) return r; dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff); return zd_iowrite16_locked(chip, value & 0xff, ZD_CR47); } int zd_chip_set_channel(struct zd_chip *chip, u8 channel) { int r, t; mutex_lock(&chip->mutex); r = zd_chip_lock_phy_regs(chip); if (r) goto out; r = zd_rf_set_channel(&chip->rf, channel); if (r) goto unlock; r = update_channel_integration_and_calibration(chip, channel); if (r) goto unlock; r = patch_cck_gain(chip); if (r) goto unlock; r = patch_6m_band_edge(chip, channel); if (r) goto unlock; r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS); unlock: t = zd_chip_unlock_phy_regs(chip); if (t && !r) r = t; out: mutex_unlock(&chip->mutex); return r; } u8 zd_chip_get_channel(struct zd_chip *chip) { u8 channel; mutex_lock(&chip->mutex); channel = chip->rf.channel; mutex_unlock(&chip->mutex); return channel; } int zd_chip_control_leds(struct zd_chip *chip, enum led_status status) { const zd_addr_t a[] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), CR_LED, }; int r; u16 v[ARRAY_SIZE(a)]; struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = { [0] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS) }, [1] = { CR_LED }, }; u16 other_led; mutex_lock(&chip->mutex); r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a)); if (r) goto out; other_led = chip->link_led == LED1 ? LED2 : LED1; switch (status) { case ZD_LED_OFF: ioreqs[0].value = FW_LINK_OFF; ioreqs[1].value = v[1] & ~(LED1|LED2); break; case ZD_LED_SCANNING: ioreqs[0].value = FW_LINK_OFF; ioreqs[1].value = v[1] & ~other_led; if (get_seconds() % 3 == 0) { ioreqs[1].value &= ~chip->link_led; } else { ioreqs[1].value |= chip->link_led; } break; case ZD_LED_ASSOCIATED: ioreqs[0].value = FW_LINK_TX; ioreqs[1].value = v[1] & ~other_led; ioreqs[1].value |= chip->link_led; break; default: r = -EINVAL; goto out; } if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) { r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); if (r) goto out; } r = 0; out: mutex_unlock(&chip->mutex); return r; } int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates) { int r; if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G)) return -EINVAL; mutex_lock(&chip->mutex); r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL); mutex_unlock(&chip->mutex); return r; } static inline u8 zd_rate_from_ofdm_plcp_header(const void *rx_frame) { return ZD_OFDM | zd_ofdm_plcp_header_rate(rx_frame); } /** * zd_rx_rate - report zd-rate * @rx_frame - received frame * @rx_status - rx_status as given by the device * * This function converts the rate as encoded in the received packet to the * zd-rate, we are using on other places in the driver. */ u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status) { u8 zd_rate; if (status->frame_status & ZD_RX_OFDM) { zd_rate = zd_rate_from_ofdm_plcp_header(rx_frame); } else { switch (zd_cck_plcp_header_signal(rx_frame)) { case ZD_CCK_PLCP_SIGNAL_1M: zd_rate = ZD_CCK_RATE_1M; break; case ZD_CCK_PLCP_SIGNAL_2M: zd_rate = ZD_CCK_RATE_2M; break; case ZD_CCK_PLCP_SIGNAL_5M5: zd_rate = ZD_CCK_RATE_5_5M; break; case ZD_CCK_PLCP_SIGNAL_11M: zd_rate = ZD_CCK_RATE_11M; break; default: zd_rate = 0; } } return zd_rate; } int zd_chip_switch_radio_on(struct zd_chip *chip) { int r; mutex_lock(&chip->mutex); r = zd_switch_radio_on(&chip->rf); mutex_unlock(&chip->mutex); return r; } int zd_chip_switch_radio_off(struct zd_chip *chip) { int r; mutex_lock(&chip->mutex); r = zd_switch_radio_off(&chip->rf); mutex_unlock(&chip->mutex); return r; } int zd_chip_enable_int(struct zd_chip *chip) { int r; mutex_lock(&chip->mutex); r = zd_usb_enable_int(&chip->usb); mutex_unlock(&chip->mutex); return r; } void zd_chip_disable_int(struct zd_chip *chip) { mutex_lock(&chip->mutex); zd_usb_disable_int(&chip->usb); mutex_unlock(&chip->mutex); /* cancel pending interrupt work */ cancel_work_sync(&zd_chip_to_mac(chip)->process_intr); } int zd_chip_enable_rxtx(struct zd_chip *chip) { int r; mutex_lock(&chip->mutex); zd_usb_enable_tx(&chip->usb); r = zd_usb_enable_rx(&chip->usb); zd_tx_watchdog_enable(&chip->usb); mutex_unlock(&chip->mutex); return r; } void zd_chip_disable_rxtx(struct zd_chip *chip) { mutex_lock(&chip->mutex); zd_tx_watchdog_disable(&chip->usb); zd_usb_disable_rx(&chip->usb); zd_usb_disable_tx(&chip->usb); mutex_unlock(&chip->mutex); } int zd_rfwritev_locked(struct zd_chip *chip, const u32* values, unsigned int count, u8 bits) { int r; unsigned int i; for (i = 0; i < count; i++) { r = zd_rfwrite_locked(chip, values[i], bits); if (r) return r; } return 0; } /* * We can optionally program the RF directly through CR regs, if supported by * the hardware. This is much faster than the older method. */ int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value) { const struct zd_ioreq16 ioreqs[] = { { ZD_CR244, (value >> 16) & 0xff }, { ZD_CR243, (value >> 8) & 0xff }, { ZD_CR242, value & 0xff }, }; ZD_ASSERT(mutex_is_locked(&chip->mutex)); return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } int zd_rfwritev_cr_locked(struct zd_chip *chip, const u32 *values, unsigned int count) { int r; unsigned int i; for (i = 0; i < count; i++) { r = zd_rfwrite_cr_locked(chip, values[i]); if (r) return r; } return 0; } int zd_chip_set_multicast_hash(struct zd_chip *chip, struct zd_mc_hash *hash) { const struct zd_ioreq32 ioreqs[] = { { CR_GROUP_HASH_P1, hash->low }, { CR_GROUP_HASH_P2, hash->high }, }; return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs)); } u64 zd_chip_get_tsf(struct zd_chip *chip) { int r; static const zd_addr_t aw_pt_bi_addr[] = { CR_TSF_LOW_PART, CR_TSF_HIGH_PART }; u32 values[2]; u64 tsf; mutex_lock(&chip->mutex); r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, ARRAY_SIZE(aw_pt_bi_addr)); mutex_unlock(&chip->mutex); if (r) return 0; tsf = values[1]; tsf = (tsf << 32) | values[0]; return tsf; } compat-drivers-2012-09-18/drivers/net/wireless/zd1211rw/Kconfig0000644000175000017500000000121012026211315023251 0ustar mcgrofmcgrofconfig ZD1211RW tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" depends on USB && MAC80211 && EXPERIMENTAL select FW_LOADER ---help--- This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless chip, present in many USB-wireless adapters. Device firmware is required alongside this driver. You can download the firmware distribution from http://zd1211.ath.cx/get-firmware config ZD1211RW_DEBUG bool "ZyDAS ZD1211 debugging" depends on ZD1211RW ---help--- ZD1211 debugging messages. Choosing Y will result in additional debug messages being saved to your kernel logs, which may help debug any problems. compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/0000755000175000017500000000000012026211315021520 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt73usb.c0000644000175000017500000023003312026211315023176 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt73usb Abstract: rt73usb device specific routines. Supported chipsets: rt2571W & rt2671. */ #include #include #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00usb.h" #include "rt73usb.h" /* * Allow hardware encryption to be disabled. */ static bool modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); /* * Register access. * All access to the CSR registers will go through the methods * rt2x00usb_register_read and rt2x00usb_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay * between each attampt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. * The _lock versions must be used if you already hold the csr_mutex */ #define WAIT_FOR_BBP(__dev, __reg) \ rt2x00usb_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) #define WAIT_FOR_RF(__dev, __reg) \ rt2x00usb_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, PHY_CSR3_VALUE, value); rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the read request into the register. * After the data has been written, we wait until hardware * returns the correct value, if at any time the register * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); mutex_unlock(&rt2x00dev->csr_mutex); } static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RF becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_RF(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, PHY_CSR4_VALUE, value); /* * RF5225 and RF2527 contain 21 bits per RF register value, * all others contain 20 bits. */ rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 20 + (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527))); rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR4, reg); rt2x00_rf_write(rt2x00dev, word, value); } mutex_unlock(&rt2x00dev->csr_mutex); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS static const struct rt2x00debug rt73usb_rt2x00debug = { .owner = THIS_MODULE, .csr = { .read = rt2x00usb_register_read, .write = rt2x00usb_register_write, .flags = RT2X00DEBUGFS_OFFSET, .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt73usb_bbp_read, .write = rt73usb_bbp_write, .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt73usb_rf_write, .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ static int rt73usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); return rt2x00_get_field32(reg, MAC_CSR13_VAL7); } #ifdef CONFIG_RT2X00_LIB_LEDS static void rt73usb_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); unsigned int enabled = brightness != LED_OFF; unsigned int a_mode = (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); unsigned int bg_mode = (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); if (led->type == LED_TYPE_RADIO) { rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, MCU_LEDCS_RADIO_STATUS, enabled); rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, 0, led->rt2x00dev->led_mcu_reg, REGISTER_TIMEOUT); } else if (led->type == LED_TYPE_ASSOC) { rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, MCU_LEDCS_LINK_BG_STATUS, bg_mode); rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, MCU_LEDCS_LINK_A_STATUS, a_mode); rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, 0, led->rt2x00dev->led_mcu_reg, REGISTER_TIMEOUT); } else if (led->type == LED_TYPE_QUALITY) { /* * The brightness is divided into 6 levels (0 - 5), * this means we need to convert the brightness * argument into the matching level within that range. */ rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, brightness / (LED_FULL / 6), led->rt2x00dev->led_mcu_reg, REGISTER_TIMEOUT); } } static int rt73usb_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); u32 reg; rt2x00usb_register_read(led->rt2x00dev, MAC_CSR14, ®); rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); rt2x00usb_register_write(led->rt2x00dev, MAC_CSR14, reg); return 0; } static void rt73usb_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; led->type = type; led->led_dev.brightness_set = rt73usb_brightness_set; led->led_dev.blink_set = rt73usb_blink_set; led->flags = LED_INITIALIZED; } #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Configuration handlers. */ static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { struct hw_key_entry key_entry; struct rt2x00_field32 field; u32 mask; u32 reg; if (crypto->cmd == SET_KEY) { /* * rt2x00lib can't determine the correct free * key_idx for shared keys. We have 1 register * with key valid bits. The goal is simple, read * the register, if that is full we have no slots * left. * Note that each BSS is allowed to have up to 4 * shared keys, so put a mask over the allowed * entries. */ mask = (0xf << crypto->bssidx); rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); reg &= mask; if (reg && reg == mask) return -ENOSPC; key->hw_key_idx += reg ? ffz(reg) : 0; /* * Upload key to hardware */ memcpy(key_entry.key, crypto->key, sizeof(key_entry.key)); memcpy(key_entry.tx_mic, crypto->tx_mic, sizeof(key_entry.tx_mic)); memcpy(key_entry.rx_mic, crypto->rx_mic, sizeof(key_entry.rx_mic)); reg = SHARED_KEY_ENTRY(key->hw_key_idx); rt2x00usb_register_multiwrite(rt2x00dev, reg, &key_entry, sizeof(key_entry)); /* * The cipher types are stored over 2 registers. * bssidx 0 and 1 keys are stored in SEC_CSR1 and * bssidx 1 and 2 keys are stored in SEC_CSR5. * Using the correct defines correctly will cause overhead, * so just calculate the correct offset. */ if (key->hw_key_idx < 8) { field.bit_offset = (3 * key->hw_key_idx); field.bit_mask = 0x7 << field.bit_offset; rt2x00usb_register_read(rt2x00dev, SEC_CSR1, ®); rt2x00_set_field32(®, field, crypto->cipher); rt2x00usb_register_write(rt2x00dev, SEC_CSR1, reg); } else { field.bit_offset = (3 * (key->hw_key_idx - 8)); field.bit_mask = 0x7 << field.bit_offset; rt2x00usb_register_read(rt2x00dev, SEC_CSR5, ®); rt2x00_set_field32(®, field, crypto->cipher); rt2x00usb_register_write(rt2x00dev, SEC_CSR5, reg); } /* * The driver does not support the IV/EIV generation * in hardware. However it doesn't support the IV/EIV * inside the ieee80211 frame either, but requires it * to be provided separately for the descriptor. * rt2x00lib will cut the IV/EIV data out of all frames * given to us by mac80211, but we must tell mac80211 * to generate the IV/EIV data. */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; } /* * SEC_CSR0 contains only single-bit fields to indicate * a particular key is valid. Because using the FIELD32() * defines directly will cause a lot of overhead we use * a calculation to determine the correct bit directly. */ mask = 1 << key->hw_key_idx; rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); if (crypto->cmd == SET_KEY) reg |= mask; else if (crypto->cmd == DISABLE_KEY) reg &= ~mask; rt2x00usb_register_write(rt2x00dev, SEC_CSR0, reg); return 0; } static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { struct hw_pairwise_ta_entry addr_entry; struct hw_key_entry key_entry; u32 mask; u32 reg; if (crypto->cmd == SET_KEY) { /* * rt2x00lib can't determine the correct free * key_idx for pairwise keys. We have 2 registers * with key valid bits. The goal is simple, read * the first register, if that is full move to * the next register. * When both registers are full, we drop the key, * otherwise we use the first invalid entry. */ rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); if (reg && reg == ~0) { key->hw_key_idx = 32; rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); if (reg && reg == ~0) return -ENOSPC; } key->hw_key_idx += reg ? ffz(reg) : 0; /* * Upload key to hardware */ memcpy(key_entry.key, crypto->key, sizeof(key_entry.key)); memcpy(key_entry.tx_mic, crypto->tx_mic, sizeof(key_entry.tx_mic)); memcpy(key_entry.rx_mic, crypto->rx_mic, sizeof(key_entry.rx_mic)); reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); rt2x00usb_register_multiwrite(rt2x00dev, reg, &key_entry, sizeof(key_entry)); /* * Send the address and cipher type to the hardware register. */ memset(&addr_entry, 0, sizeof(addr_entry)); memcpy(&addr_entry, crypto->address, ETH_ALEN); addr_entry.cipher = crypto->cipher; reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); rt2x00usb_register_multiwrite(rt2x00dev, reg, &addr_entry, sizeof(addr_entry)); /* * Enable pairwise lookup table for given BSS idx, * without this received frames will not be decrypted * by the hardware. */ rt2x00usb_register_read(rt2x00dev, SEC_CSR4, ®); reg |= (1 << crypto->bssidx); rt2x00usb_register_write(rt2x00dev, SEC_CSR4, reg); /* * The driver does not support the IV/EIV generation * in hardware. However it doesn't support the IV/EIV * inside the ieee80211 frame either, but requires it * to be provided separately for the descriptor. * rt2x00lib will cut the IV/EIV data out of all frames * given to us by mac80211, but we must tell mac80211 * to generate the IV/EIV data. */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; } /* * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate * a particular key is valid. Because using the FIELD32() * defines directly will cause a lot of overhead we use * a calculation to determine the correct bit directly. */ if (key->hw_key_idx < 32) { mask = 1 << key->hw_key_idx; rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); if (crypto->cmd == SET_KEY) reg |= mask; else if (crypto->cmd == DISABLE_KEY) reg &= ~mask; rt2x00usb_register_write(rt2x00dev, SEC_CSR2, reg); } else { mask = 1 << (key->hw_key_idx - 32); rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); if (crypto->cmd == SET_KEY) reg |= mask; else if (crypto->cmd == DISABLE_KEY) reg &= ~mask; rt2x00usb_register_write(rt2x00dev, SEC_CSR3, reg); } return 0; } static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { u32 reg; /* * Start configuration steps. * Note that the version error will always be dropped * and broadcast frames will always be accepted since * there is no filter for it at this time. */ rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, !(filter_flags & FIF_FCSFAIL)); rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, !(filter_flags & FIF_PROMISC_IN_BSS) && !rt2x00dev->intf_ap_count); rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, !(filter_flags & FIF_ALLMULTI)); rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, !(filter_flags & FIF_CONTROL)); rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); } static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags) { u32 reg; if (flags & CONFIG_UPDATE_TYPE) { /* * Enable synchronisation. */ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); } if (flags & CONFIG_UPDATE_MAC) { reg = le32_to_cpu(conf->mac[1]); rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); conf->mac[1] = cpu_to_le32(reg); rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac, sizeof(conf->mac)); } if (flags & CONFIG_UPDATE_BSSID) { reg = le32_to_cpu(conf->bssid[1]); rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); conf->bssid[1] = cpu_to_le32(reg); rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR4, conf->bssid, sizeof(conf->bssid)); } } static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed) { u32 reg; rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); if (changed & BSS_CHANGED_ERP_PREAMBLE) { rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, !!erp->short_preamble); rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); } if (changed & BSS_CHANGED_BASIC_RATES) rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates); if (changed & BSS_CHANGED_BEACON_INT) { rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, erp->beacon_int * 16); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); } if (changed & BSS_CHANGED_ERP_SLOT) { rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); rt2x00usb_register_read(rt2x00dev, MAC_CSR8, ®); rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg); } } static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r3; u8 r4; u8 r77; u8 temp; rt73usb_bbp_read(rt2x00dev, 3, &r3); rt73usb_bbp_read(rt2x00dev, 4, &r4); rt73usb_bbp_read(rt2x00dev, 77, &r77); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); temp = !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags) && (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp); break; case ANTENNA_A: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); break; case ANTENNA_B: default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); break; } rt73usb_bbp_write(rt2x00dev, 77, r77); rt73usb_bbp_write(rt2x00dev, 3, r3); rt73usb_bbp_write(rt2x00dev, 4, r4); } static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r3; u8 r4; u8 r77; rt73usb_bbp_read(rt2x00dev, 3, &r3); rt73usb_bbp_read(rt2x00dev, 4, &r4); rt73usb_bbp_read(rt2x00dev, 77, &r77); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)); /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); break; case ANTENNA_A: rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); break; case ANTENNA_B: default: rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); break; } rt73usb_bbp_write(rt2x00dev, 77, r77); rt73usb_bbp_write(rt2x00dev, 3, r3); rt73usb_bbp_write(rt2x00dev, 4, r4); } struct antenna_sel { u8 word; /* * value[0] -> non-LNA * value[1] -> LNA */ u8 value[2]; }; static const struct antenna_sel antenna_sel_a[] = { { 96, { 0x58, 0x78 } }, { 104, { 0x38, 0x48 } }, { 75, { 0xfe, 0x80 } }, { 86, { 0xfe, 0x80 } }, { 88, { 0xfe, 0x80 } }, { 35, { 0x60, 0x60 } }, { 97, { 0x58, 0x58 } }, { 98, { 0x58, 0x58 } }, }; static const struct antenna_sel antenna_sel_bg[] = { { 96, { 0x48, 0x68 } }, { 104, { 0x2c, 0x3c } }, { 75, { 0xfe, 0x80 } }, { 86, { 0xfe, 0x80 } }, { 88, { 0xfe, 0x80 } }, { 35, { 0x50, 0x50 } }, { 97, { 0x48, 0x48 } }, { 98, { 0x48, 0x48 } }, }; static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { const struct antenna_sel *sel; unsigned int lna; unsigned int i; u32 reg; /* * We should never come here because rt2x00lib is supposed * to catch this and send us the correct antenna explicitely. */ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || ant->tx == ANTENNA_SW_DIVERSITY); if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); } else { sel = antenna_sel_bg; lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) rt73usb_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); rt2x00usb_register_read(rt2x00dev, PHY_CSR0, ®); rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)); rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ)); rt2x00usb_register_write(rt2x00dev, PHY_CSR0, reg); if (rt2x00_rf(rt2x00dev, RF5226) || rt2x00_rf(rt2x00dev, RF5225)) rt73usb_config_antenna_5x(rt2x00dev, ant); else if (rt2x00_rf(rt2x00dev, RF2528) || rt2x00_rf(rt2x00dev, RF2527)) rt73usb_config_antenna_2x(rt2x00dev, ant); } static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u16 eeprom; short lna_gain = 0; if (libconf->conf->channel->band == IEEE80211_BAND_2GHZ) { if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } else { rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); } rt2x00dev->lna_gain = lna_gain; } static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev, struct rf_channel *rf, const int txpower) { u8 r3; u8 r94; u8 smart; rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); rt73usb_bbp_read(rt2x00dev, 3, &r3); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); rt73usb_bbp_write(rt2x00dev, 3, r3); r94 = 6; if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) r94 += txpower - MAX_TXPOWER; else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) r94 += txpower; rt73usb_bbp_write(rt2x00dev, 94, r94); rt73usb_rf_write(rt2x00dev, 1, rf->rf1); rt73usb_rf_write(rt2x00dev, 2, rf->rf2); rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); rt73usb_rf_write(rt2x00dev, 4, rf->rf4); rt73usb_rf_write(rt2x00dev, 1, rf->rf1); rt73usb_rf_write(rt2x00dev, 2, rf->rf2); rt73usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); rt73usb_rf_write(rt2x00dev, 4, rf->rf4); rt73usb_rf_write(rt2x00dev, 1, rf->rf1); rt73usb_rf_write(rt2x00dev, 2, rf->rf2); rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); rt73usb_rf_write(rt2x00dev, 4, rf->rf4); udelay(10); } static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev, const int txpower) { struct rf_channel rf; rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); rt73usb_config_channel(rt2x00dev, &rf, txpower); } static void rt73usb_config_retry_limit(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u32 reg; rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, libconf->conf->long_frame_max_tx_count); rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, libconf->conf->short_frame_max_tx_count); rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); } static void rt73usb_config_ps(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { enum dev_state state = (libconf->conf->flags & IEEE80211_CONF_PS) ? STATE_SLEEP : STATE_AWAKE; u32 reg; if (state == STATE_SLEEP) { rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, rt2x00dev->beacon_int - 10); rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, libconf->conf->listen_interval - 1); rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); /* We must first disable autowake before it can be enabled */ rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, USB_MODE_SLEEP, REGISTER_TIMEOUT); } else { rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, USB_MODE_WAKEUP, REGISTER_TIMEOUT); } } static void rt73usb_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { /* Always recalculate LNA gain before changing configuration */ rt73usb_config_lna_gain(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_CHANNEL) rt73usb_config_channel(rt2x00dev, &libconf->rf, libconf->conf->power_level); if ((flags & IEEE80211_CONF_CHANGE_POWER) && !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level); if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) rt73usb_config_retry_limit(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_PS) rt73usb_config_ps(rt2x00dev, libconf); } /* * Link tuning */ static void rt73usb_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { u32 reg; /* * Update FCS error count from register. */ rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); /* * Update False CCA count from register. */ rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); } static inline void rt73usb_set_vgc(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, u8 vgc_level) { if (qual->vgc_level != vgc_level) { rt73usb_bbp_write(rt2x00dev, 17, vgc_level); qual->vgc_level = vgc_level; qual->vgc_level_reg = vgc_level; } } static void rt73usb_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { rt73usb_set_vgc(rt2x00dev, qual, 0x20); } static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count) { u8 up_bound; u8 low_bound; /* * Determine r17 bounds. */ if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { low_bound = 0x28; up_bound = 0x48; if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { low_bound += 0x10; up_bound += 0x10; } } else { if (qual->rssi > -82) { low_bound = 0x1c; up_bound = 0x40; } else if (qual->rssi > -84) { low_bound = 0x1c; up_bound = 0x20; } else { low_bound = 0x1c; up_bound = 0x1c; } if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { low_bound += 0x14; up_bound += 0x10; } } /* * If we are not associated, we should go straight to the * dynamic CCA tuning. */ if (!rt2x00dev->intf_associated) goto dynamic_cca_tune; /* * Special big-R17 for very short distance */ if (qual->rssi > -35) { rt73usb_set_vgc(rt2x00dev, qual, 0x60); return; } /* * Special big-R17 for short distance */ if (qual->rssi >= -58) { rt73usb_set_vgc(rt2x00dev, qual, up_bound); return; } /* * Special big-R17 for middle-short distance */ if (qual->rssi >= -66) { rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x10); return; } /* * Special mid-R17 for middle distance */ if (qual->rssi >= -74) { rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x08); return; } /* * Special case: Change up_bound based on the rssi. * Lower up_bound when rssi is weaker then -74 dBm. */ up_bound -= 2 * (-74 - qual->rssi); if (low_bound > up_bound) up_bound = low_bound; if (qual->vgc_level > up_bound) { rt73usb_set_vgc(rt2x00dev, qual, up_bound); return; } dynamic_cca_tune: /* * r17 does not yet exceed upper limit, continue and base * the r17 tuning on the false CCA count. */ if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) rt73usb_set_vgc(rt2x00dev, qual, min_t(u8, qual->vgc_level + 4, up_bound)); else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) rt73usb_set_vgc(rt2x00dev, qual, max_t(u8, qual->vgc_level - 4, low_bound)); } /* * Queue handlers. */ static void rt73usb_start_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); break; case QID_BEACON: rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); break; default: break; } } static void rt73usb_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); break; case QID_BEACON: rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); break; default: break; } } /* * Firmware functions */ static char *rt73usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) { return FIRMWARE_RT2571; } static int rt73usb_check_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { u16 fw_crc; u16 crc; /* * Only support 2kb firmware files. */ if (len != 2048) return FW_BAD_LENGTH; /* * The last 2 bytes in the firmware array are the crc checksum itself, * this means that we should never pass those 2 bytes to the crc * algorithm. */ fw_crc = (data[len - 2] << 8 | data[len - 1]); /* * Use the crc itu-t algorithm. */ crc = crc_itu_t(0, data, len - 2); crc = crc_itu_t_byte(crc, 0); crc = crc_itu_t_byte(crc, 0); return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; } static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { unsigned int i; int status; u32 reg; /* * Wait for stable hardware. */ for (i = 0; i < 100; i++) { rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); if (reg) break; msleep(1); } if (!reg) { ERROR(rt2x00dev, "Unstable hardware.\n"); return -EBUSY; } /* * Write firmware to device. */ rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data, len); /* * Send firmware request to device to load firmware, * we need to specify a long timeout time. */ status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, USB_MODE_FIRMWARE, REGISTER_TIMEOUT_FIRMWARE); if (status < 0) { ERROR(rt2x00dev, "Failed to write Firmware to device.\n"); return status; } return 0; } /* * Initialization functions. */ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); rt2x00usb_register_read(rt2x00dev, TXRX_CSR1, ®); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2, 42); /* OFDM Rate */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); rt2x00usb_register_write(rt2x00dev, TXRX_CSR1, reg); /* * CCK TXD BBP registers */ rt2x00usb_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2, 11); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); rt2x00usb_register_write(rt2x00dev, TXRX_CSR2, reg); /* * OFDM TXD BBP registers */ rt2x00usb_register_read(rt2x00dev, TXRX_CSR3, ®); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); rt2x00usb_register_write(rt2x00dev, TXRX_CSR3, reg); rt2x00usb_register_read(rt2x00dev, TXRX_CSR7, ®); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); rt2x00usb_register_write(rt2x00dev, TXRX_CSR7, reg); rt2x00usb_register_read(rt2x00dev, TXRX_CSR8, ®); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); rt2x00usb_register_write(rt2x00dev, TXRX_CSR8, reg); rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); rt2x00usb_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); rt2x00usb_register_read(rt2x00dev, MAC_CSR6, ®); rt2x00_set_field32(®, MAC_CSR6_MAX_FRAME_UNIT, 0xfff); rt2x00usb_register_write(rt2x00dev, MAC_CSR6, reg); rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00000718); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; rt2x00usb_register_write(rt2x00dev, MAC_CSR13, 0x00007f00); /* * Invalidate all Shared Keys (SEC_CSR0), * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) */ rt2x00usb_register_write(rt2x00dev, SEC_CSR0, 0x00000000); rt2x00usb_register_write(rt2x00dev, SEC_CSR1, 0x00000000); rt2x00usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000); reg = 0x000023b0; if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)) rt2x00_set_field32(®, PHY_CSR1_RF_RPI, 1); rt2x00usb_register_write(rt2x00dev, PHY_CSR1, reg); rt2x00usb_register_write(rt2x00dev, PHY_CSR5, 0x00040a06); rt2x00usb_register_write(rt2x00dev, PHY_CSR6, 0x00080606); rt2x00usb_register_write(rt2x00dev, PHY_CSR7, 0x00000408); rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); /* * Clear all beacons * For the Beacon base registers we only need to clear * the first byte since that byte contains the VALID and OWNER * bits which (when set to 0) will invalidate the entire beacon. */ rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0); rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0); rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0); rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0); /* * We must clear the error counters. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); rt2x00usb_register_read(rt2x00dev, STA_CSR2, ®); /* * Reset MAC and BBP registers. */ rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); return 0; } static int rt73usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt73usb_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) return 0; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); return -EACCES; } static int rt73usb_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; u16 eeprom; u8 reg_id; u8 value; if (unlikely(rt73usb_wait_bbp_ready(rt2x00dev))) return -EACCES; rt73usb_bbp_write(rt2x00dev, 3, 0x80); rt73usb_bbp_write(rt2x00dev, 15, 0x30); rt73usb_bbp_write(rt2x00dev, 21, 0xc8); rt73usb_bbp_write(rt2x00dev, 22, 0x38); rt73usb_bbp_write(rt2x00dev, 23, 0x06); rt73usb_bbp_write(rt2x00dev, 24, 0xfe); rt73usb_bbp_write(rt2x00dev, 25, 0x0a); rt73usb_bbp_write(rt2x00dev, 26, 0x0d); rt73usb_bbp_write(rt2x00dev, 32, 0x0b); rt73usb_bbp_write(rt2x00dev, 34, 0x12); rt73usb_bbp_write(rt2x00dev, 37, 0x07); rt73usb_bbp_write(rt2x00dev, 39, 0xf8); rt73usb_bbp_write(rt2x00dev, 41, 0x60); rt73usb_bbp_write(rt2x00dev, 53, 0x10); rt73usb_bbp_write(rt2x00dev, 54, 0x18); rt73usb_bbp_write(rt2x00dev, 60, 0x10); rt73usb_bbp_write(rt2x00dev, 61, 0x04); rt73usb_bbp_write(rt2x00dev, 62, 0x04); rt73usb_bbp_write(rt2x00dev, 75, 0xfe); rt73usb_bbp_write(rt2x00dev, 86, 0xfe); rt73usb_bbp_write(rt2x00dev, 88, 0xfe); rt73usb_bbp_write(rt2x00dev, 90, 0x0f); rt73usb_bbp_write(rt2x00dev, 99, 0x00); rt73usb_bbp_write(rt2x00dev, 102, 0x16); rt73usb_bbp_write(rt2x00dev, 107, 0x04); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); rt73usb_bbp_write(rt2x00dev, reg_id, value); } } return 0; } /* * Device state switch handlers. */ static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev) { /* * Initialize all registers. */ if (unlikely(rt73usb_init_registers(rt2x00dev) || rt73usb_init_bbp(rt2x00dev))) return -EIO; return 0; } static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev) { rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00001818); /* * Disable synchronisation. */ rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, 0); rt2x00usb_disable_radio(rt2x00dev); } static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u32 reg, reg2; unsigned int i; char put_to_sleep; put_to_sleep = (state != STATE_AWAKE); rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®); rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); /* * Device is not guaranteed to be in the requested state yet. * We must wait until the register indicates that the * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®2); state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); if (state == !put_to_sleep) return 0; rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); msleep(10); } return -EBUSY; } static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int retval = 0; switch (state) { case STATE_RADIO_ON: retval = rt73usb_enable_radio(rt2x00dev); break; case STATE_RADIO_OFF: rt73usb_disable_radio(rt2x00dev); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: /* No support, but no error either */ break; case STATE_DEEP_SLEEP: case STATE_SLEEP: case STATE_STANDBY: case STATE_AWAKE: retval = rt73usb_set_state(rt2x00dev, state); break; default: retval = -ENOTSUPP; break; } if (unlikely(retval)) ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", state, retval); return retval; } /* * TX descriptor initialization */ static void rt73usb_write_tx_desc(struct queue_entry *entry, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *txd = (__le32 *) entry->skb->data; u32 word; /* * Start writing the descriptor words. */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_BURST, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, (txdesc->rate_mode == RATE_MODE_OFDM)); rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXD_W0_BURST2, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); rt2x00_desc_write(txd, 0, word); rt2x00_desc_read(txd, 1, &word); rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); rt2x00_desc_write(txd, 1, word); rt2x00_desc_read(txd, 2, &word); rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, txdesc->u.plcp.length_low); rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, txdesc->u.plcp.length_high); rt2x00_desc_write(txd, 2, word); if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); } rt2x00_desc_read(txd, 5, &word); rt2x00_set_field32(&word, TXD_W5_TX_POWER, TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); rt2x00_desc_write(txd, 5, word); /* * Register descriptor details in skb frame descriptor. */ skbdesc->flags |= SKBDESC_DESC_IN_SKB; skbdesc->desc = txd; skbdesc->desc_len = TXD_DESC_SIZE; } /* * TX data initialization */ static void rt73usb_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; unsigned int beacon_base; unsigned int padding_len; u32 orig_reg, reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); orig_reg = reg; rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); /* * Add space for the descriptor in front of the skb. */ skb_push(entry->skb, TXD_DESC_SIZE); memset(entry->skb->data, 0, TXD_DESC_SIZE); /* * Write the TX descriptor for the beacon. */ rt73usb_write_tx_desc(entry, txdesc); /* * Dump beacon to userspace through debugfs. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); /* * Write entire beacon with descriptor and padding to register. */ padding_len = roundup(entry->skb->len, 4) - entry->skb->len; if (padding_len && skb_pad(entry->skb, padding_len)) { ERROR(rt2x00dev, "Failure padding beacon, aborting\n"); /* skb freed by skb_pad() on failure */ entry->skb = NULL; rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); return; } beacon_base = HW_BEACON_OFFSET(entry->entry_idx); rt2x00usb_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, entry->skb->len + padding_len); /* * Enable beaconing again. * * For Wi-Fi faily generated beacons between participating stations. * Set TBTT phase adaptive adjustment step to 8us (default 16us) */ rt2x00usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); /* * Clean up the beacon skb. */ dev_kfree_skb(entry->skb); entry->skb = NULL; } static void rt73usb_clear_beacon(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; unsigned int beacon_base; u32 reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); /* * Clear beacon. */ beacon_base = HW_BEACON_OFFSET(entry->entry_idx); rt2x00usb_register_write(rt2x00dev, beacon_base, 0); /* * Enable beaconing again. */ rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); } static int rt73usb_get_tx_data_len(struct queue_entry *entry) { int length; /* * The length _must_ be a multiple of 4, * but it must _not_ be a multiple of the USB packet size. */ length = roundup(entry->skb->len, 4); length += (4 * !(length % entry->queue->usb_maxpacket)); return length; } /* * RX control handlers */ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) { u8 offset = rt2x00dev->lna_gain; u8 lna; lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); switch (lna) { case 3: offset += 90; break; case 2: offset += 74; break; case 1: offset += 64; break; default: return 0; } if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { if (lna == 3 || lna == 2) offset += 10; } else { if (lna == 3) offset += 6; else if (lna == 2) offset += 8; } } return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; } static void rt73usb_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *rxd = (__le32 *)entry->skb->data; u32 word0; u32 word1; /* * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of * frame data in rt2x00usb. */ memcpy(skbdesc->desc, rxd, skbdesc->desc_len); rxd = (__le32 *)skbdesc->desc; /* * It is now safe to read the descriptor on all architectures. */ rt2x00_desc_read(rxd, 0, &word0); rt2x00_desc_read(rxd, 1, &word1); if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); if (rxdesc->cipher != CIPHER_NONE) { _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); rxdesc->dev_flags |= RXDONE_CRYPTO_IV; _rt2x00_desc_read(rxd, 4, &rxdesc->icv); rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; /* * Hardware has stripped IV/EIV data from 802.11 frame during * decryption. It has provided the data separately but rt2x00lib * should decide if it should be reinserted. */ rxdesc->flags |= RX_FLAG_IV_STRIPPED; /* * The hardware has already checked the Michael Mic and has * stripped it from the frame. Signal this to mac80211. */ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) rxdesc->flags |= RX_FLAG_DECRYPTED; else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) rxdesc->flags |= RX_FLAG_MMIC_ERROR; } /* * Obtain the status about this packet. * When frame was received with an OFDM bitrate, * the signal is the PLCP value. If it was received with * a CCK bitrate the signal is the rate in 100kbit/s. */ rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); rxdesc->rssi = rt73usb_agc_to_rssi(rt2x00dev, word1); rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); if (rt2x00_get_field32(word0, RXD_W0_OFDM)) rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; else rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; /* * Set skb pointers, and update frame information. */ skb_pull(entry->skb, entry->queue->desc_size); skb_trim(entry->skb, rxdesc->size); } /* * Device probe functions. */ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) { u16 word; u8 *mac; s8 value; rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); /* * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { eth_random_addr(mac); EEPROM(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, ANTENNA_B); rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, ANTENNA_B); rt2x00_set_field16(&word, EEPROM_ANTENNA_FRAME_TYPE, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5226); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_LED_POLARITY_RDY_G, 0); rt2x00_set_field16(&word, EEPROM_LED_POLARITY_RDY_A, 0); rt2x00_set_field16(&word, EEPROM_LED_POLARITY_ACT, 0); rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_0, 0); rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_1, 0); rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_2, 0); rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_3, 0); rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_4, 0); rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, LED_MODE_DEFAULT); rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); EEPROM(rt2x00dev, "Led: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); EEPROM(rt2x00dev, "Freq: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); EEPROM(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_2); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); EEPROM(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_2); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); } return 0; } static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 value; u16 eeprom; /* * Read EEPROM word for configuration. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); /* * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); if (!rt2x00_rt(rt2x00dev, RT2573) || (rt2x00_rev(rt2x00dev) == 0)) { ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); return -ENODEV; } if (!rt2x00_rf(rt2x00dev, RF5226) && !rt2x00_rf(rt2x00dev, RF2528) && !rt2x00_rf(rt2x00dev, RF5225) && !rt2x00_rf(rt2x00dev, RF2527)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } /* * Identify default antenna configuration. */ rt2x00dev->default_ant.tx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); rt2x00dev->default_ant.rx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); /* * Read the Frame type. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); /* * Detect if this device has an hardware controlled radio. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Read frequency offset. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); /* * Read external LNA informations. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA)) { __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); } /* * Store led settings, for correct led behaviour. */ #ifdef CONFIG_RT2X00_LIB_LEDS rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); rt73usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); rt73usb_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); if (value == LED_MODE_SIGNAL_STRENGTH) rt73usb_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_0)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_1)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_2)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_3)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_4)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_G)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_A)); #endif /* CONFIG_RT2X00_LIB_LEDS */ return 0; } /* * RF value list for RF2528 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2528[] = { { 1, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea0b }, { 2, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea1f }, { 3, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea0b }, { 4, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea1f }, { 5, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea0b }, { 6, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea1f }, { 7, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea0b }, { 8, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea1f }, { 9, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea0b }, { 10, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea1f }, { 11, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea0b }, { 12, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea1f }, { 13, 0x00002c0c, 0x0000079e, 0x00068255, 0x000fea0b }, { 14, 0x00002c0c, 0x000007a2, 0x00068255, 0x000fea13 }, }; /* * RF value list for RF5226 * Supports: 2.4 GHz & 5.2 GHz */ static const struct rf_channel rf_vals_5226[] = { { 1, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea0b }, { 2, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea1f }, { 3, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea0b }, { 4, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea1f }, { 5, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea0b }, { 6, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea1f }, { 7, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea0b }, { 8, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea1f }, { 9, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea0b }, { 10, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea1f }, { 11, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea0b }, { 12, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea1f }, { 13, 0x00002c0c, 0x0000079e, 0x00068255, 0x000fea0b }, { 14, 0x00002c0c, 0x000007a2, 0x00068255, 0x000fea13 }, /* 802.11 UNI / HyperLan 2 */ { 36, 0x00002c0c, 0x0000099a, 0x00098255, 0x000fea23 }, { 40, 0x00002c0c, 0x000009a2, 0x00098255, 0x000fea03 }, { 44, 0x00002c0c, 0x000009a6, 0x00098255, 0x000fea0b }, { 48, 0x00002c0c, 0x000009aa, 0x00098255, 0x000fea13 }, { 52, 0x00002c0c, 0x000009ae, 0x00098255, 0x000fea1b }, { 56, 0x00002c0c, 0x000009b2, 0x00098255, 0x000fea23 }, { 60, 0x00002c0c, 0x000009ba, 0x00098255, 0x000fea03 }, { 64, 0x00002c0c, 0x000009be, 0x00098255, 0x000fea0b }, /* 802.11 HyperLan 2 */ { 100, 0x00002c0c, 0x00000a2a, 0x000b8255, 0x000fea03 }, { 104, 0x00002c0c, 0x00000a2e, 0x000b8255, 0x000fea0b }, { 108, 0x00002c0c, 0x00000a32, 0x000b8255, 0x000fea13 }, { 112, 0x00002c0c, 0x00000a36, 0x000b8255, 0x000fea1b }, { 116, 0x00002c0c, 0x00000a3a, 0x000b8255, 0x000fea23 }, { 120, 0x00002c0c, 0x00000a82, 0x000b8255, 0x000fea03 }, { 124, 0x00002c0c, 0x00000a86, 0x000b8255, 0x000fea0b }, { 128, 0x00002c0c, 0x00000a8a, 0x000b8255, 0x000fea13 }, { 132, 0x00002c0c, 0x00000a8e, 0x000b8255, 0x000fea1b }, { 136, 0x00002c0c, 0x00000a92, 0x000b8255, 0x000fea23 }, /* 802.11 UNII */ { 140, 0x00002c0c, 0x00000a9a, 0x000b8255, 0x000fea03 }, { 149, 0x00002c0c, 0x00000aa2, 0x000b8255, 0x000fea1f }, { 153, 0x00002c0c, 0x00000aa6, 0x000b8255, 0x000fea27 }, { 157, 0x00002c0c, 0x00000aae, 0x000b8255, 0x000fea07 }, { 161, 0x00002c0c, 0x00000ab2, 0x000b8255, 0x000fea0f }, { 165, 0x00002c0c, 0x00000ab6, 0x000b8255, 0x000fea17 }, /* MMAC(Japan)J52 ch 34,38,42,46 */ { 34, 0x00002c0c, 0x0008099a, 0x000da255, 0x000d3a0b }, { 38, 0x00002c0c, 0x0008099e, 0x000da255, 0x000d3a13 }, { 42, 0x00002c0c, 0x000809a2, 0x000da255, 0x000d3a1b }, { 46, 0x00002c0c, 0x000809a6, 0x000da255, 0x000d3a23 }, }; /* * RF value list for RF5225 & RF2527 * Supports: 2.4 GHz & 5.2 GHz */ static const struct rf_channel rf_vals_5225_2527[] = { { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, /* 802.11 UNI / HyperLan 2 */ { 36, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa23 }, { 40, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa03 }, { 44, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa0b }, { 48, 0x00002ccc, 0x000049aa, 0x0009be55, 0x000ffa13 }, { 52, 0x00002ccc, 0x000049ae, 0x0009ae55, 0x000ffa1b }, { 56, 0x00002ccc, 0x000049b2, 0x0009ae55, 0x000ffa23 }, { 60, 0x00002ccc, 0x000049ba, 0x0009ae55, 0x000ffa03 }, { 64, 0x00002ccc, 0x000049be, 0x0009ae55, 0x000ffa0b }, /* 802.11 HyperLan 2 */ { 100, 0x00002ccc, 0x00004a2a, 0x000bae55, 0x000ffa03 }, { 104, 0x00002ccc, 0x00004a2e, 0x000bae55, 0x000ffa0b }, { 108, 0x00002ccc, 0x00004a32, 0x000bae55, 0x000ffa13 }, { 112, 0x00002ccc, 0x00004a36, 0x000bae55, 0x000ffa1b }, { 116, 0x00002ccc, 0x00004a3a, 0x000bbe55, 0x000ffa23 }, { 120, 0x00002ccc, 0x00004a82, 0x000bbe55, 0x000ffa03 }, { 124, 0x00002ccc, 0x00004a86, 0x000bbe55, 0x000ffa0b }, { 128, 0x00002ccc, 0x00004a8a, 0x000bbe55, 0x000ffa13 }, { 132, 0x00002ccc, 0x00004a8e, 0x000bbe55, 0x000ffa1b }, { 136, 0x00002ccc, 0x00004a92, 0x000bbe55, 0x000ffa23 }, /* 802.11 UNII */ { 140, 0x00002ccc, 0x00004a9a, 0x000bbe55, 0x000ffa03 }, { 149, 0x00002ccc, 0x00004aa2, 0x000bbe55, 0x000ffa1f }, { 153, 0x00002ccc, 0x00004aa6, 0x000bbe55, 0x000ffa27 }, { 157, 0x00002ccc, 0x00004aae, 0x000bbe55, 0x000ffa07 }, { 161, 0x00002ccc, 0x00004ab2, 0x000bbe55, 0x000ffa0f }, { 165, 0x00002ccc, 0x00004ab6, 0x000bbe55, 0x000ffa17 }, /* MMAC(Japan)J52 ch 34,38,42,46 */ { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa0b }, { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000ffa13 }, { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa1b }, { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa23 }, }; static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *tx_power; unsigned int i; /* * Initialize all hw fields. * * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are * capable of sending the buffered frames out after the DTIM * transmission using rt2x00lib_beacondone. This will send out * multicast and broadcast traffic immediately instead of buffering it * infinitly and thus dropping it after some time. */ rt2x00dev->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; if (rt2x00_rf(rt2x00dev, RF2528)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2528); spec->channels = rf_vals_bg_2528; } else if (rt2x00_rf(rt2x00dev, RF5226)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5226); spec->channels = rf_vals_5226; } else if (rt2x00_rf(rt2x00dev, RF2527)) { spec->num_channels = 14; spec->channels = rf_vals_5225_2527; } else if (rt2x00_rf(rt2x00dev, RF5225)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5225_2527); spec->channels = rf_vals_5225_2527; } /* * Create channel information array */ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; spec->channels_info = info; tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); for (i = 0; i < 14; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } if (spec->num_channels > 14) { tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); for (i = 14; i < spec->num_channels; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } } return 0; } static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; u32 reg; /* * Allocate eeprom data. */ retval = rt73usb_validate_eeprom(rt2x00dev); if (retval) return retval; retval = rt73usb_init_eeprom(rt2x00dev); if (retval) return retval; /* * Enable rfkill polling by setting GPIO direction of the * rfkill switch GPIO pin correctly. */ rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); rt2x00_set_field32(®, MAC_CSR13_DIR7, 0); rt2x00usb_register_write(rt2x00dev, MAC_CSR13, reg); /* * Initialize hw specifications. */ retval = rt73usb_probe_hw_mode(rt2x00dev); if (retval) return retval; /* * This device has multiple filters for control frames, * but has no a separate filter for PS Poll frames. */ __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); /* * This device requires firmware. */ __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); if (!modparam_nohwcrypt) __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); /* * Set the rssi offset. */ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; return 0; } /* * IEEE80211 stack callback functions. */ static int rt73usb_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; struct rt2x00_field32 field; int retval; u32 reg; u32 offset; /* * First pass the configuration through rt2x00lib, that will * update the queue settings and validate the input. After that * we are free to update the registers based on the value * in the queue parameter. */ retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; /* * We only need to perform additional register initialization * for WMM queues/ */ if (queue_idx >= 4) return 0; queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); /* Update WMM TXOP register */ offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); field.bit_offset = (queue_idx & 1) * 16; field.bit_mask = 0xffff << field.bit_offset; rt2x00usb_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, field, queue->txop); rt2x00usb_register_write(rt2x00dev, offset, reg); /* Update WMM registers */ field.bit_offset = queue_idx * 4; field.bit_mask = 0xf << field.bit_offset; rt2x00usb_register_read(rt2x00dev, AIFSN_CSR, ®); rt2x00_set_field32(®, field, queue->aifs); rt2x00usb_register_write(rt2x00dev, AIFSN_CSR, reg); rt2x00usb_register_read(rt2x00dev, CWMIN_CSR, ®); rt2x00_set_field32(®, field, queue->cw_min); rt2x00usb_register_write(rt2x00dev, CWMIN_CSR, reg); rt2x00usb_register_read(rt2x00dev, CWMAX_CSR, ®); rt2x00_set_field32(®, field, queue->cw_max); rt2x00usb_register_write(rt2x00dev, CWMAX_CSR, reg); return 0; } static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; rt2x00usb_register_read(rt2x00dev, TXRX_CSR13, ®); tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; rt2x00usb_register_read(rt2x00dev, TXRX_CSR12, ®); tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); return tsf; } static const struct ieee80211_ops rt73usb_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, .set_tim = rt2x00mac_set_tim, .set_key = rt2x00mac_set_key, .sw_scan_start = rt2x00mac_sw_scan_start, .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt73usb_conf_tx, .get_tsf = rt73usb_get_tsf, .rfkill_poll = rt2x00mac_rfkill_poll, .flush = rt2x00mac_flush, .set_antenna = rt2x00mac_set_antenna, .get_antenna = rt2x00mac_get_antenna, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { .probe_hw = rt73usb_probe_hw, .get_firmware_name = rt73usb_get_firmware_name, .check_firmware = rt73usb_check_firmware, .load_firmware = rt73usb_load_firmware, .initialize = rt2x00usb_initialize, .uninitialize = rt2x00usb_uninitialize, .clear_entry = rt2x00usb_clear_entry, .set_device_state = rt73usb_set_device_state, .rfkill_poll = rt73usb_rfkill_poll, .link_stats = rt73usb_link_stats, .reset_tuner = rt73usb_reset_tuner, .link_tuner = rt73usb_link_tuner, .watchdog = rt2x00usb_watchdog, .start_queue = rt73usb_start_queue, .kick_queue = rt2x00usb_kick_queue, .stop_queue = rt73usb_stop_queue, .flush_queue = rt2x00usb_flush_queue, .write_tx_desc = rt73usb_write_tx_desc, .write_beacon = rt73usb_write_beacon, .clear_beacon = rt73usb_clear_beacon, .get_tx_data_len = rt73usb_get_tx_data_len, .fill_rxdone = rt73usb_fill_rxdone, .config_shared_key = rt73usb_config_shared_key, .config_pairwise_key = rt73usb_config_pairwise_key, .config_filter = rt73usb_config_filter, .config_intf = rt73usb_config_intf, .config_erp = rt73usb_config_erp, .config_ant = rt73usb_config_ant, .config = rt73usb_config, }; static const struct data_queue_desc rt73usb_queue_rx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = RXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct data_queue_desc rt73usb_queue_tx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct data_queue_desc rt73usb_queue_bcn = { .entry_num = 4, .data_size = MGMT_FRAME_SIZE, .desc_size = TXINFO_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct rt2x00_ops rt73usb_ops = { .name = KBUILD_MODNAME, .max_sta_intf = 1, .max_ap_intf = 4, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXD_DESC_SIZE, .rx = &rt73usb_queue_rx, .tx = &rt73usb_queue_tx, .bcn = &rt73usb_queue_bcn, .lib = &rt73usb_rt2x00_ops, .hw = &rt73usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS .debugfs = &rt73usb_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * rt73usb module information. */ static struct usb_device_id rt73usb_device_table[] = { /* AboCom */ { USB_DEVICE(0x07b8, 0xb21b) }, { USB_DEVICE(0x07b8, 0xb21c) }, { USB_DEVICE(0x07b8, 0xb21d) }, { USB_DEVICE(0x07b8, 0xb21e) }, { USB_DEVICE(0x07b8, 0xb21f) }, /* AL */ { USB_DEVICE(0x14b2, 0x3c10) }, /* Amigo */ { USB_DEVICE(0x148f, 0x9021) }, { USB_DEVICE(0x0eb0, 0x9021) }, /* AMIT */ { USB_DEVICE(0x18c5, 0x0002) }, /* Askey */ { USB_DEVICE(0x1690, 0x0722) }, /* ASUS */ { USB_DEVICE(0x0b05, 0x1723) }, { USB_DEVICE(0x0b05, 0x1724) }, /* Belkin */ { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050B ver. 3.x */ { USB_DEVICE(0x050d, 0x705a) }, { USB_DEVICE(0x050d, 0x905b) }, { USB_DEVICE(0x050d, 0x905c) }, /* Billionton */ { USB_DEVICE(0x1631, 0xc019) }, { USB_DEVICE(0x08dd, 0x0120) }, /* Buffalo */ { USB_DEVICE(0x0411, 0x00d8) }, { USB_DEVICE(0x0411, 0x00d9) }, { USB_DEVICE(0x0411, 0x00e6) }, { USB_DEVICE(0x0411, 0x00f4) }, { USB_DEVICE(0x0411, 0x0116) }, { USB_DEVICE(0x0411, 0x0119) }, { USB_DEVICE(0x0411, 0x0137) }, /* CEIVA */ { USB_DEVICE(0x178d, 0x02be) }, /* CNet */ { USB_DEVICE(0x1371, 0x9022) }, { USB_DEVICE(0x1371, 0x9032) }, /* Conceptronic */ { USB_DEVICE(0x14b2, 0x3c22) }, /* Corega */ { USB_DEVICE(0x07aa, 0x002e) }, /* D-Link */ { USB_DEVICE(0x07d1, 0x3c03) }, { USB_DEVICE(0x07d1, 0x3c04) }, { USB_DEVICE(0x07d1, 0x3c06) }, { USB_DEVICE(0x07d1, 0x3c07) }, /* Edimax */ { USB_DEVICE(0x7392, 0x7318) }, { USB_DEVICE(0x7392, 0x7618) }, /* EnGenius */ { USB_DEVICE(0x1740, 0x3701) }, /* Gemtek */ { USB_DEVICE(0x15a9, 0x0004) }, /* Gigabyte */ { USB_DEVICE(0x1044, 0x8008) }, { USB_DEVICE(0x1044, 0x800a) }, /* Huawei-3Com */ { USB_DEVICE(0x1472, 0x0009) }, /* Hercules */ { USB_DEVICE(0x06f8, 0xe002) }, { USB_DEVICE(0x06f8, 0xe010) }, { USB_DEVICE(0x06f8, 0xe020) }, /* Linksys */ { USB_DEVICE(0x13b1, 0x0020) }, { USB_DEVICE(0x13b1, 0x0023) }, { USB_DEVICE(0x13b1, 0x0028) }, /* MSI */ { USB_DEVICE(0x0db0, 0x4600) }, { USB_DEVICE(0x0db0, 0x6877) }, { USB_DEVICE(0x0db0, 0x6874) }, { USB_DEVICE(0x0db0, 0xa861) }, { USB_DEVICE(0x0db0, 0xa874) }, /* Ovislink */ { USB_DEVICE(0x1b75, 0x7318) }, /* Ralink */ { USB_DEVICE(0x04bb, 0x093d) }, { USB_DEVICE(0x148f, 0x2573) }, { USB_DEVICE(0x148f, 0x2671) }, { USB_DEVICE(0x0812, 0x3101) }, /* Qcom */ { USB_DEVICE(0x18e8, 0x6196) }, { USB_DEVICE(0x18e8, 0x6229) }, { USB_DEVICE(0x18e8, 0x6238) }, /* Samsung */ { USB_DEVICE(0x04e8, 0x4471) }, /* Senao */ { USB_DEVICE(0x1740, 0x7100) }, /* Sitecom */ { USB_DEVICE(0x0df6, 0x0024) }, { USB_DEVICE(0x0df6, 0x0027) }, { USB_DEVICE(0x0df6, 0x002f) }, { USB_DEVICE(0x0df6, 0x90ac) }, { USB_DEVICE(0x0df6, 0x9712) }, /* Surecom */ { USB_DEVICE(0x0769, 0x31f3) }, /* Tilgin */ { USB_DEVICE(0x6933, 0x5001) }, /* Philips */ { USB_DEVICE(0x0471, 0x200a) }, /* Planex */ { USB_DEVICE(0x2019, 0xab01) }, { USB_DEVICE(0x2019, 0xab50) }, /* WideTell */ { USB_DEVICE(0x7167, 0x3840) }, /* Zcom */ { USB_DEVICE(0x0cde, 0x001c) }, /* ZyXEL */ { USB_DEVICE(0x0586, 0x3415) }, { 0, } }; MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT73 USB Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2571W & RT2671 USB chipset based cards"); MODULE_DEVICE_TABLE(usb, rt73usb_device_table); MODULE_FIRMWARE(FIRMWARE_RT2571); MODULE_LICENSE("GPL"); static int rt73usb_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { return rt2x00usb_probe(usb_intf, &rt73usb_ops); } static struct usb_driver rt73usb_driver = { .name = KBUILD_MODNAME, .id_table = rt73usb_device_table, .probe = rt73usb_probe, .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(rt73usb_driver); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2800usb.c0000644000175000017500000010650612026211315023345 0ustar mcgrofmcgrof/* Copyright (C) 2010 Willow Garage Copyright (C) 2009 - 2010 Ivo van Doorn Copyright (C) 2009 Mattias Nissler Copyright (C) 2009 Felix Fietkau Copyright (C) 2009 Xose Vazquez Perez Copyright (C) 2009 Axel Kollhofer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2800usb Abstract: rt2800usb device specific routines. Supported chipsets: RT2800U. */ #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00usb.h" #include "rt2800lib.h" #include "rt2800.h" #include "rt2800usb.h" /* * Allow hardware encryption to be disabled. */ static bool modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); static bool rt2800usb_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) { return modparam_nohwcrypt; } /* * Queue handlers. */ static void rt2800usb_start_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); break; case QID_BEACON: rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); break; default: break; } } static void rt2800usb_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); break; case QID_BEACON: rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); break; default: break; } } /* * test if there is an entry in any TX queue for which DMA is done * but the TX status has not been returned yet */ static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; tx_queue_for_each(rt2x00dev, queue) { if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) != rt2x00queue_get_entry(queue, Q_INDEX_DONE)) return true; } return false; } static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry) { bool tout; if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) return false; tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); if (unlikely(tout)) WARNING(entry->queue->rt2x00dev, "TX status timeout for entry %d in queue %d\n", entry->entry_idx, entry->queue->qid); return tout; } static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; struct queue_entry *entry; tx_queue_for_each(rt2x00dev, queue) { entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); if (rt2800usb_entry_txstatus_timeout(entry)) return true; } return false; } static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, int urb_status, u32 tx_status) { bool valid; if (urb_status) { WARNING(rt2x00dev, "TX status read failed %d\n", urb_status); goto stop_reading; } valid = rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID); if (valid) { if (!kfifo_put(&rt2x00dev->txstatus_fifo, &tx_status)) WARNING(rt2x00dev, "TX status FIFO overrun\n"); queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); /* Reschedule urb to read TX status again instantly */ return true; } /* Check if there is any entry that timedout waiting on TX status */ if (rt2800usb_txstatus_timeout(rt2x00dev)) queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); if (rt2800usb_txstatus_pending(rt2x00dev)) { /* Read register after 250 us */ hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 250000), HRTIMER_MODE_REL); return false; } stop_reading: clear_bit(TX_STATUS_READING, &rt2x00dev->flags); /* * There is small race window above, between txstatus pending check and * clear_bit someone could do rt2x00usb_interrupt_txdone, so recheck * here again if status reading is needed. */ if (rt2800usb_txstatus_pending(rt2x00dev) && !test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) return true; else return false; } static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev) { if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) return; /* Read TX_STA_FIFO register after 500 us */ hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 500000), HRTIMER_MODE_REL); } static void rt2800usb_tx_dma_done(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; rt2800usb_async_read_tx_status(rt2x00dev); } static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer) { struct rt2x00_dev *rt2x00dev = container_of(timer, struct rt2x00_dev, txstatus_timer); rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, rt2800usb_tx_sta_fifo_read_completed); return HRTIMER_NORESTART; } /* * Firmware functions */ static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) { return FIRMWARE_RT2870; } static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { int status; u32 offset; u32 length; /* * Check which section of the firmware we need. */ if (rt2x00_rt(rt2x00dev, RT2860) || rt2x00_rt(rt2x00dev, RT2872) || rt2x00_rt(rt2x00dev, RT3070)) { offset = 0; length = 4096; } else { offset = 4096; length = 4096; } /* * Write firmware to device. */ rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data + offset, length); rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); /* * Send firmware request to device to load firmware, * we need to specify a long timeout time. */ status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, USB_MODE_FIRMWARE, REGISTER_TIMEOUT_FIRMWARE); if (status < 0) { ERROR(rt2x00dev, "Failed to write Firmware to device.\n"); return status; } msleep(10); rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); return 0; } /* * Device state switch handlers. */ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; /* * Wait until BBP and RF are ready. */ if (rt2800_wait_csr_ready(rt2x00dev)) return -EBUSY; rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, ®); rt2x00usb_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000); reg = 0; rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, USB_MODE_RESET, REGISTER_TIMEOUT); rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); return 0; } static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev))) return -EIO; rt2x00usb_register_read(rt2x00dev, USB_DMA_CFG, ®); rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128); /* * Total room for RX frames in kilobytes, PBF might still exceed * this limit so reduce the number to prevent errors. */ rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_LIMIT, ((rt2x00dev->ops->rx->entry_num * DATA_FRAME_SIZE) / 1024) - 3); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1); rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1); rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg); return rt2800_enable_radio(rt2x00dev); } static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) { rt2800_disable_radio(rt2x00dev); rt2x00usb_disable_radio(rt2x00dev); } static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { if (state == STATE_AWAKE) rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 2); else rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2); return 0; } static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int retval = 0; switch (state) { case STATE_RADIO_ON: /* * Before the radio can be enabled, the device first has * to be woken up. After that it needs a bit of time * to be fully awake and then the radio can be enabled. */ rt2800usb_set_state(rt2x00dev, STATE_AWAKE); msleep(1); retval = rt2800usb_enable_radio(rt2x00dev); break; case STATE_RADIO_OFF: /* * After the radio has been disabled, the device should * be put to sleep for powersaving. */ rt2800usb_disable_radio(rt2x00dev); rt2800usb_set_state(rt2x00dev, STATE_SLEEP); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: /* No support, but no error either */ break; case STATE_DEEP_SLEEP: case STATE_SLEEP: case STATE_STANDBY: case STATE_AWAKE: retval = rt2800usb_set_state(rt2x00dev, state); break; default: retval = -ENOTSUPP; break; } if (unlikely(retval)) ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", state, retval); return retval; } /* * Watchdog handlers */ static void rt2800usb_watchdog(struct rt2x00_dev *rt2x00dev) { unsigned int i; u32 reg; rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) { WARNING(rt2x00dev, "TX HW queue 0 timed out," " invoke forced kick\n"); rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40012); for (i = 0; i < 10; i++) { udelay(10); if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) break; } rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); } rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) { WARNING(rt2x00dev, "TX HW queue 1 timed out," " invoke forced kick\n"); rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf4000a); for (i = 0; i < 10; i++) { udelay(10); if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) break; } rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); } rt2x00usb_watchdog(rt2x00dev); } /* * TX descriptor initialization */ static __le32 *rt2800usb_get_txwi(struct queue_entry *entry) { if (entry->queue->qid == QID_BEACON) return (__le32 *) (entry->skb->data); else return (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE); } static void rt2800usb_write_tx_desc(struct queue_entry *entry, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *txi = (__le32 *) entry->skb->data; u32 word; /* * Initialize TXINFO descriptor */ rt2x00_desc_read(txi, 0, &word); /* * The size of TXINFO_W0_USB_DMA_TX_PKT_LEN is * TXWI + 802.11 header + L2 pad + payload + pad, * so need to decrease size of TXINFO. */ rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_PKT_LEN, roundup(entry->skb->len, 4) - TXINFO_DESC_SIZE); rt2x00_set_field32(&word, TXINFO_W0_WIV, !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); rt2x00_set_field32(&word, TXINFO_W0_QSEL, 2); rt2x00_set_field32(&word, TXINFO_W0_SW_USE_LAST_ROUND, 0); rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_NEXT_VALID, 0); rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_BURST, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); rt2x00_desc_write(txi, 0, word); /* * Register descriptor details in skb frame descriptor. */ skbdesc->flags |= SKBDESC_DESC_IN_SKB; skbdesc->desc = txi; skbdesc->desc_len = TXINFO_DESC_SIZE + TXWI_DESC_SIZE; } /* * TX data initialization */ static int rt2800usb_get_tx_data_len(struct queue_entry *entry) { /* * pad(1~3 bytes) is needed after each 802.11 payload. * USB end pad(4 bytes) is needed at each USB bulk out packet end. * TX frame format is : * | TXINFO | TXWI | 802.11 header | L2 pad | payload | pad | USB end pad | * |<------------- tx_pkt_len ------------->| */ return roundup(entry->skb->len, 4) + 4; } /* * TX control handlers */ static enum txdone_entry_desc_flags rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) { __le32 *txwi; u32 word; int wcid, ack, pid; int tx_wcid, tx_ack, tx_pid, is_agg; /* * This frames has returned with an IO error, * so the status report is not intended for this * frame. */ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) return TXDONE_FAILURE; wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID); ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED); pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE); is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE); /* * Validate if this TX status report is intended for * this entry by comparing the WCID/ACK/PID fields. */ txwi = rt2800usb_get_txwi(entry); rt2x00_desc_read(txwi, 1, &word); tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK); tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID); if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) { WARNING(entry->queue->rt2x00dev, "TX status report missed for queue %d entry %d\n", entry->queue->qid, entry->entry_idx); return TXDONE_UNKNOWN; } return TXDONE_SUCCESS; } static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; struct queue_entry *entry; u32 reg; u8 qid; enum txdone_entry_desc_flags done_status; while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { /* * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is * guaranteed to be one of the TX QIDs . */ qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); if (unlikely(rt2x00queue_empty(queue))) { WARNING(rt2x00dev, "Got TX status for an empty " "queue %u, dropping\n", qid); break; } entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) { WARNING(rt2x00dev, "Data pending for entry %u " "in queue %u\n", entry->entry_idx, qid); break; } done_status = rt2800usb_txdone_entry_check(entry, reg); if (likely(done_status == TXDONE_SUCCESS)) rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry)); else rt2x00lib_txdone_noinfo(entry, done_status); } } static void rt2800usb_txdone_nostatus(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; struct queue_entry *entry; /* * Process any trailing TX status reports for IO failures, * we loop until we find the first non-IO error entry. This * can either be a frame which is free, is being uploaded, * or has completed the upload but didn't have an entry * in the TX_STAT_FIFO register yet. */ tx_queue_for_each(rt2x00dev, queue) { while (!rt2x00queue_empty(queue)) { entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) break; if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); else if (rt2800usb_entry_txstatus_timeout(entry)) rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); else break; } } } static void rt2800usb_work_txdone(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, txdone_work); while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || rt2800usb_txstatus_timeout(rt2x00dev)) { rt2800usb_txdone(rt2x00dev); rt2800usb_txdone_nostatus(rt2x00dev); /* * The hw may delay sending the packet after DMA complete * if the medium is busy, thus the TX_STA_FIFO entry is * also delayed -> use a timer to retrieve it. */ if (rt2800usb_txstatus_pending(rt2x00dev)) rt2800usb_async_read_tx_status(rt2x00dev); } } /* * RX control handlers */ static void rt2800usb_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *rxi = (__le32 *)entry->skb->data; __le32 *rxd; u32 word; int rx_pkt_len; /* * Copy descriptor to the skbdesc->desc buffer, making it safe from * moving of frame data in rt2x00usb. */ memcpy(skbdesc->desc, rxi, skbdesc->desc_len); /* * RX frame format is : * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad | * |<------------ rx_pkt_len -------------->| */ rt2x00_desc_read(rxi, 0, &word); rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN); /* * Remove the RXINFO structure from the sbk. */ skb_pull(entry->skb, RXINFO_DESC_SIZE); /* * Check for rx_pkt_len validity. Return if invalid, leaving * rxdesc->size zeroed out by the upper level. */ if (unlikely(rx_pkt_len == 0 || rx_pkt_len > entry->queue->data_size)) { ERROR(entry->queue->rt2x00dev, "Bad frame size %d, forcing to 0\n", rx_pkt_len); return; } rxd = (__le32 *)(entry->skb->data + rx_pkt_len); /* * It is now safe to read the descriptor on all architectures. */ rt2x00_desc_read(rxd, 0, &word); if (rt2x00_get_field32(word, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W0_CIPHER_ERROR); if (rt2x00_get_field32(word, RXD_W0_DECRYPTED)) { /* * Hardware has stripped IV/EIV data from 802.11 frame during * decryption. Unfortunately the descriptor doesn't contain * any fields with the EIV/IV data either, so they can't * be restored by rt2x00lib. */ rxdesc->flags |= RX_FLAG_IV_STRIPPED; /* * The hardware has already checked the Michael Mic and has * stripped it from the frame. Signal this to mac80211. */ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) rxdesc->flags |= RX_FLAG_DECRYPTED; else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) rxdesc->flags |= RX_FLAG_MMIC_ERROR; } if (rt2x00_get_field32(word, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; if (rt2x00_get_field32(word, RXD_W0_L2PAD)) rxdesc->dev_flags |= RXDONE_L2PAD; /* * Remove RXD descriptor from end of buffer. */ skb_trim(entry->skb, rx_pkt_len); /* * Process the RXWI structure. */ rt2800_process_rxwi(entry, rxdesc); } /* * Device probe functions. */ static void rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) { if (rt2800_efuse_detect(rt2x00dev)) rt2800_read_eeprom_efuse(rt2x00dev); else rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); } static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; retval = rt2800_probe_hw(rt2x00dev); if (retval) return retval; /* * Set txstatus timer function. */ rt2x00dev->txstatus_timer.function = rt2800usb_tx_sta_fifo_timeout; /* * Overwrite TX done handler */ PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); return 0; } static const struct ieee80211_ops rt2800usb_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, .set_tim = rt2x00mac_set_tim, .set_key = rt2x00mac_set_key, .sw_scan_start = rt2x00mac_sw_scan_start, .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .get_tkip_seq = rt2800_get_tkip_seq, .set_rts_threshold = rt2800_set_rts_threshold, .sta_add = rt2x00mac_sta_add, .sta_remove = rt2x00mac_sta_remove, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2800_conf_tx, .get_tsf = rt2800_get_tsf, .rfkill_poll = rt2x00mac_rfkill_poll, .ampdu_action = rt2800_ampdu_action, .flush = rt2x00mac_flush, .get_survey = rt2800_get_survey, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2800_ops rt2800usb_rt2800_ops = { .register_read = rt2x00usb_register_read, .register_read_lock = rt2x00usb_register_read_lock, .register_write = rt2x00usb_register_write, .register_write_lock = rt2x00usb_register_write_lock, .register_multiread = rt2x00usb_register_multiread, .register_multiwrite = rt2x00usb_register_multiwrite, .regbusy_read = rt2x00usb_regbusy_read, .read_eeprom = rt2800usb_read_eeprom, .hwcrypt_disabled = rt2800usb_hwcrypt_disabled, .drv_write_firmware = rt2800usb_write_firmware, .drv_init_registers = rt2800usb_init_registers, .drv_get_txwi = rt2800usb_get_txwi, }; static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { .probe_hw = rt2800usb_probe_hw, .get_firmware_name = rt2800usb_get_firmware_name, .check_firmware = rt2800_check_firmware, .load_firmware = rt2800_load_firmware, .initialize = rt2x00usb_initialize, .uninitialize = rt2x00usb_uninitialize, .clear_entry = rt2x00usb_clear_entry, .set_device_state = rt2800usb_set_device_state, .rfkill_poll = rt2800_rfkill_poll, .link_stats = rt2800_link_stats, .reset_tuner = rt2800_reset_tuner, .link_tuner = rt2800_link_tuner, .gain_calibration = rt2800_gain_calibration, .vco_calibration = rt2800_vco_calibration, .watchdog = rt2800usb_watchdog, .start_queue = rt2800usb_start_queue, .kick_queue = rt2x00usb_kick_queue, .stop_queue = rt2800usb_stop_queue, .flush_queue = rt2x00usb_flush_queue, .tx_dma_done = rt2800usb_tx_dma_done, .write_tx_desc = rt2800usb_write_tx_desc, .write_tx_data = rt2800_write_tx_data, .write_beacon = rt2800_write_beacon, .clear_beacon = rt2800_clear_beacon, .get_tx_data_len = rt2800usb_get_tx_data_len, .fill_rxdone = rt2800usb_fill_rxdone, .config_shared_key = rt2800_config_shared_key, .config_pairwise_key = rt2800_config_pairwise_key, .config_filter = rt2800_config_filter, .config_intf = rt2800_config_intf, .config_erp = rt2800_config_erp, .config_ant = rt2800_config_ant, .config = rt2800_config, .sta_add = rt2800_sta_add, .sta_remove = rt2800_sta_remove, }; static const struct data_queue_desc rt2800usb_queue_rx = { .entry_num = 128, .data_size = AGGREGATION_SIZE, .desc_size = RXINFO_DESC_SIZE + RXWI_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct data_queue_desc rt2800usb_queue_tx = { .entry_num = 16, .data_size = AGGREGATION_SIZE, .desc_size = TXINFO_DESC_SIZE + TXWI_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct data_queue_desc rt2800usb_queue_bcn = { .entry_num = 8, .data_size = MGMT_FRAME_SIZE, .desc_size = TXINFO_DESC_SIZE + TXWI_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct rt2x00_ops rt2800usb_ops = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), .max_sta_intf = 1, .max_ap_intf = 8, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE, .rx = &rt2800usb_queue_rx, .tx = &rt2800usb_queue_tx, .bcn = &rt2800usb_queue_bcn, .lib = &rt2800usb_rt2x00_ops, .drv = &rt2800usb_rt2800_ops, .hw = &rt2800usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS .debugfs = &rt2800_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * rt2800usb module information. */ static struct usb_device_id rt2800usb_device_table[] = { /* Abocom */ { USB_DEVICE(0x07b8, 0x2870) }, { USB_DEVICE(0x07b8, 0x2770) }, { USB_DEVICE(0x07b8, 0x3070) }, { USB_DEVICE(0x07b8, 0x3071) }, { USB_DEVICE(0x07b8, 0x3072) }, { USB_DEVICE(0x1482, 0x3c09) }, /* AirTies */ { USB_DEVICE(0x1eda, 0x2012) }, { USB_DEVICE(0x1eda, 0x2210) }, { USB_DEVICE(0x1eda, 0x2310) }, /* Allwin */ { USB_DEVICE(0x8516, 0x2070) }, { USB_DEVICE(0x8516, 0x2770) }, { USB_DEVICE(0x8516, 0x2870) }, { USB_DEVICE(0x8516, 0x3070) }, { USB_DEVICE(0x8516, 0x3071) }, { USB_DEVICE(0x8516, 0x3072) }, /* Alpha Networks */ { USB_DEVICE(0x14b2, 0x3c06) }, { USB_DEVICE(0x14b2, 0x3c07) }, { USB_DEVICE(0x14b2, 0x3c09) }, { USB_DEVICE(0x14b2, 0x3c12) }, { USB_DEVICE(0x14b2, 0x3c23) }, { USB_DEVICE(0x14b2, 0x3c25) }, { USB_DEVICE(0x14b2, 0x3c27) }, { USB_DEVICE(0x14b2, 0x3c28) }, { USB_DEVICE(0x14b2, 0x3c2c) }, /* Amit */ { USB_DEVICE(0x15c5, 0x0008) }, /* Askey */ { USB_DEVICE(0x1690, 0x0740) }, /* ASUS */ { USB_DEVICE(0x0b05, 0x1731) }, { USB_DEVICE(0x0b05, 0x1732) }, { USB_DEVICE(0x0b05, 0x1742) }, { USB_DEVICE(0x0b05, 0x1784) }, { USB_DEVICE(0x1761, 0x0b05) }, /* AzureWave */ { USB_DEVICE(0x13d3, 0x3247) }, { USB_DEVICE(0x13d3, 0x3273) }, { USB_DEVICE(0x13d3, 0x3305) }, { USB_DEVICE(0x13d3, 0x3307) }, { USB_DEVICE(0x13d3, 0x3321) }, /* Belkin */ { USB_DEVICE(0x050d, 0x8053) }, { USB_DEVICE(0x050d, 0x805c) }, { USB_DEVICE(0x050d, 0x815c) }, { USB_DEVICE(0x050d, 0x825a) }, { USB_DEVICE(0x050d, 0x825b) }, { USB_DEVICE(0x050d, 0x935a) }, { USB_DEVICE(0x050d, 0x935b) }, /* Buffalo */ { USB_DEVICE(0x0411, 0x00e8) }, { USB_DEVICE(0x0411, 0x0158) }, { USB_DEVICE(0x0411, 0x015d) }, { USB_DEVICE(0x0411, 0x016f) }, { USB_DEVICE(0x0411, 0x01a2) }, { USB_DEVICE(0x0411, 0x01ee) }, /* Corega */ { USB_DEVICE(0x07aa, 0x002f) }, { USB_DEVICE(0x07aa, 0x003c) }, { USB_DEVICE(0x07aa, 0x003f) }, { USB_DEVICE(0x18c5, 0x0012) }, /* D-Link */ { USB_DEVICE(0x07d1, 0x3c09) }, { USB_DEVICE(0x07d1, 0x3c0a) }, { USB_DEVICE(0x07d1, 0x3c0d) }, { USB_DEVICE(0x07d1, 0x3c0e) }, { USB_DEVICE(0x07d1, 0x3c0f) }, { USB_DEVICE(0x07d1, 0x3c11) }, { USB_DEVICE(0x07d1, 0x3c13) }, { USB_DEVICE(0x07d1, 0x3c15) }, { USB_DEVICE(0x07d1, 0x3c16) }, { USB_DEVICE(0x2001, 0x3c1b) }, /* Draytek */ { USB_DEVICE(0x07fa, 0x7712) }, /* DVICO */ { USB_DEVICE(0x0fe9, 0xb307) }, /* Edimax */ { USB_DEVICE(0x7392, 0x4085) }, { USB_DEVICE(0x7392, 0x7711) }, { USB_DEVICE(0x7392, 0x7717) }, { USB_DEVICE(0x7392, 0x7718) }, { USB_DEVICE(0x7392, 0x7722) }, /* Encore */ { USB_DEVICE(0x203d, 0x1480) }, { USB_DEVICE(0x203d, 0x14a9) }, /* EnGenius */ { USB_DEVICE(0x1740, 0x9701) }, { USB_DEVICE(0x1740, 0x9702) }, { USB_DEVICE(0x1740, 0x9703) }, { USB_DEVICE(0x1740, 0x9705) }, { USB_DEVICE(0x1740, 0x9706) }, { USB_DEVICE(0x1740, 0x9707) }, { USB_DEVICE(0x1740, 0x9708) }, { USB_DEVICE(0x1740, 0x9709) }, /* Gemtek */ { USB_DEVICE(0x15a9, 0x0012) }, /* Gigabyte */ { USB_DEVICE(0x1044, 0x800b) }, { USB_DEVICE(0x1044, 0x800d) }, /* Hawking */ { USB_DEVICE(0x0e66, 0x0001) }, { USB_DEVICE(0x0e66, 0x0003) }, { USB_DEVICE(0x0e66, 0x0009) }, { USB_DEVICE(0x0e66, 0x000b) }, { USB_DEVICE(0x0e66, 0x0013) }, { USB_DEVICE(0x0e66, 0x0017) }, { USB_DEVICE(0x0e66, 0x0018) }, /* I-O DATA */ { USB_DEVICE(0x04bb, 0x0945) }, { USB_DEVICE(0x04bb, 0x0947) }, { USB_DEVICE(0x04bb, 0x0948) }, /* Linksys */ { USB_DEVICE(0x13b1, 0x0031) }, { USB_DEVICE(0x1737, 0x0070) }, { USB_DEVICE(0x1737, 0x0071) }, { USB_DEVICE(0x1737, 0x0077) }, { USB_DEVICE(0x1737, 0x0078) }, /* Logitec */ { USB_DEVICE(0x0789, 0x0162) }, { USB_DEVICE(0x0789, 0x0163) }, { USB_DEVICE(0x0789, 0x0164) }, { USB_DEVICE(0x0789, 0x0166) }, /* Motorola */ { USB_DEVICE(0x100d, 0x9031) }, /* MSI */ { USB_DEVICE(0x0db0, 0x3820) }, { USB_DEVICE(0x0db0, 0x3821) }, { USB_DEVICE(0x0db0, 0x3822) }, { USB_DEVICE(0x0db0, 0x3870) }, { USB_DEVICE(0x0db0, 0x3871) }, { USB_DEVICE(0x0db0, 0x6899) }, { USB_DEVICE(0x0db0, 0x821a) }, { USB_DEVICE(0x0db0, 0x822a) }, { USB_DEVICE(0x0db0, 0x822b) }, { USB_DEVICE(0x0db0, 0x822c) }, { USB_DEVICE(0x0db0, 0x870a) }, { USB_DEVICE(0x0db0, 0x871a) }, { USB_DEVICE(0x0db0, 0x871b) }, { USB_DEVICE(0x0db0, 0x871c) }, { USB_DEVICE(0x0db0, 0x899a) }, /* Ovislink */ { USB_DEVICE(0x1b75, 0x3071) }, { USB_DEVICE(0x1b75, 0x3072) }, /* Para */ { USB_DEVICE(0x20b8, 0x8888) }, /* Pegatron */ { USB_DEVICE(0x1d4d, 0x0002) }, { USB_DEVICE(0x1d4d, 0x000c) }, { USB_DEVICE(0x1d4d, 0x000e) }, { USB_DEVICE(0x1d4d, 0x0011) }, /* Philips */ { USB_DEVICE(0x0471, 0x200f) }, /* Planex */ { USB_DEVICE(0x2019, 0x5201) }, { USB_DEVICE(0x2019, 0xab25) }, { USB_DEVICE(0x2019, 0xed06) }, /* Quanta */ { USB_DEVICE(0x1a32, 0x0304) }, /* Ralink */ { USB_DEVICE(0x148f, 0x2070) }, { USB_DEVICE(0x148f, 0x2770) }, { USB_DEVICE(0x148f, 0x2870) }, { USB_DEVICE(0x148f, 0x3070) }, { USB_DEVICE(0x148f, 0x3071) }, { USB_DEVICE(0x148f, 0x3072) }, /* Samsung */ { USB_DEVICE(0x04e8, 0x2018) }, /* Siemens */ { USB_DEVICE(0x129b, 0x1828) }, /* Sitecom */ { USB_DEVICE(0x0df6, 0x0017) }, { USB_DEVICE(0x0df6, 0x002b) }, { USB_DEVICE(0x0df6, 0x002c) }, { USB_DEVICE(0x0df6, 0x002d) }, { USB_DEVICE(0x0df6, 0x0039) }, { USB_DEVICE(0x0df6, 0x003b) }, { USB_DEVICE(0x0df6, 0x003d) }, { USB_DEVICE(0x0df6, 0x003e) }, { USB_DEVICE(0x0df6, 0x003f) }, { USB_DEVICE(0x0df6, 0x0040) }, { USB_DEVICE(0x0df6, 0x0042) }, { USB_DEVICE(0x0df6, 0x0047) }, { USB_DEVICE(0x0df6, 0x0048) }, { USB_DEVICE(0x0df6, 0x0051) }, { USB_DEVICE(0x0df6, 0x005f) }, { USB_DEVICE(0x0df6, 0x0060) }, /* SMC */ { USB_DEVICE(0x083a, 0x6618) }, { USB_DEVICE(0x083a, 0x7511) }, { USB_DEVICE(0x083a, 0x7512) }, { USB_DEVICE(0x083a, 0x7522) }, { USB_DEVICE(0x083a, 0x8522) }, { USB_DEVICE(0x083a, 0xa618) }, { USB_DEVICE(0x083a, 0xa701) }, { USB_DEVICE(0x083a, 0xa702) }, { USB_DEVICE(0x083a, 0xa703) }, { USB_DEVICE(0x083a, 0xb522) }, /* Sparklan */ { USB_DEVICE(0x15a9, 0x0006) }, /* Sweex */ { USB_DEVICE(0x177f, 0x0153) }, { USB_DEVICE(0x177f, 0x0302) }, { USB_DEVICE(0x177f, 0x0313) }, /* U-Media */ { USB_DEVICE(0x157e, 0x300e) }, { USB_DEVICE(0x157e, 0x3013) }, /* ZCOM */ { USB_DEVICE(0x0cde, 0x0022) }, { USB_DEVICE(0x0cde, 0x0025) }, /* Zinwell */ { USB_DEVICE(0x5a57, 0x0280) }, { USB_DEVICE(0x5a57, 0x0282) }, { USB_DEVICE(0x5a57, 0x0283) }, { USB_DEVICE(0x5a57, 0x5257) }, /* Zyxel */ { USB_DEVICE(0x0586, 0x3416) }, { USB_DEVICE(0x0586, 0x3418) }, { USB_DEVICE(0x0586, 0x341e) }, { USB_DEVICE(0x0586, 0x343e) }, #ifdef CONFIG_RT2800USB_RT33XX /* Belkin */ { USB_DEVICE(0x050d, 0x945b) }, /* D-Link */ { USB_DEVICE(0x2001, 0x3c17) }, /* Panasonic */ { USB_DEVICE(0x083a, 0xb511) }, /* Philips */ { USB_DEVICE(0x0471, 0x20dd) }, /* Ralink */ { USB_DEVICE(0x148f, 0x3370) }, { USB_DEVICE(0x148f, 0x8070) }, /* Sitecom */ { USB_DEVICE(0x0df6, 0x0050) }, #endif #ifdef CONFIG_RT2800USB_RT35XX /* Allwin */ { USB_DEVICE(0x8516, 0x3572) }, /* Askey */ { USB_DEVICE(0x1690, 0x0744) }, { USB_DEVICE(0x1690, 0x0761) }, { USB_DEVICE(0x1690, 0x0764) }, /* ASUS */ { USB_DEVICE(0x0b05, 0x179d) }, /* Cisco */ { USB_DEVICE(0x167b, 0x4001) }, /* EnGenius */ { USB_DEVICE(0x1740, 0x9801) }, /* I-O DATA */ { USB_DEVICE(0x04bb, 0x0944) }, /* Linksys */ { USB_DEVICE(0x13b1, 0x002f) }, { USB_DEVICE(0x1737, 0x0079) }, /* Ralink */ { USB_DEVICE(0x148f, 0x3572) }, /* Sitecom */ { USB_DEVICE(0x0df6, 0x0041) }, { USB_DEVICE(0x0df6, 0x0062) }, { USB_DEVICE(0x0df6, 0x0065) }, { USB_DEVICE(0x0df6, 0x0066) }, { USB_DEVICE(0x0df6, 0x0068) }, /* Toshiba */ { USB_DEVICE(0x0930, 0x0a07) }, /* Zinwell */ { USB_DEVICE(0x5a57, 0x0284) }, #endif #ifdef CONFIG_RT2800USB_RT53XX /* Arcadyan */ { USB_DEVICE(0x043e, 0x7a12) }, /* Azurewave */ { USB_DEVICE(0x13d3, 0x3329) }, { USB_DEVICE(0x13d3, 0x3365) }, /* D-Link */ { USB_DEVICE(0x2001, 0x3c15) }, { USB_DEVICE(0x2001, 0x3c19) }, { USB_DEVICE(0x2001, 0x3c1c) }, { USB_DEVICE(0x2001, 0x3c1d) }, /* LG innotek */ { USB_DEVICE(0x043e, 0x7a22) }, /* Panasonic */ { USB_DEVICE(0x04da, 0x1801) }, { USB_DEVICE(0x04da, 0x1800) }, /* Philips */ { USB_DEVICE(0x0471, 0x2104) }, /* Ralink */ { USB_DEVICE(0x148f, 0x5370) }, { USB_DEVICE(0x148f, 0x5372) }, /* Unknown */ { USB_DEVICE(0x04da, 0x23f6) }, #endif #ifdef CONFIG_RT2800USB_UNKNOWN /* * Unclear what kind of devices these are (they aren't supported by the * vendor linux driver). */ /* Abocom */ { USB_DEVICE(0x07b8, 0x3073) }, { USB_DEVICE(0x07b8, 0x3074) }, /* Alpha Networks */ { USB_DEVICE(0x14b2, 0x3c08) }, { USB_DEVICE(0x14b2, 0x3c11) }, /* Amigo */ { USB_DEVICE(0x0e0b, 0x9031) }, { USB_DEVICE(0x0e0b, 0x9041) }, /* ASUS */ { USB_DEVICE(0x0b05, 0x166a) }, { USB_DEVICE(0x0b05, 0x1760) }, { USB_DEVICE(0x0b05, 0x1761) }, { USB_DEVICE(0x0b05, 0x1790) }, /* AzureWave */ { USB_DEVICE(0x13d3, 0x3262) }, { USB_DEVICE(0x13d3, 0x3284) }, { USB_DEVICE(0x13d3, 0x3322) }, /* Belkin */ { USB_DEVICE(0x050d, 0x1003) }, /* Buffalo */ { USB_DEVICE(0x0411, 0x012e) }, { USB_DEVICE(0x0411, 0x0148) }, { USB_DEVICE(0x0411, 0x0150) }, /* Corega */ { USB_DEVICE(0x07aa, 0x0041) }, { USB_DEVICE(0x07aa, 0x0042) }, { USB_DEVICE(0x18c5, 0x0008) }, /* D-Link */ { USB_DEVICE(0x07d1, 0x3c0b) }, { USB_DEVICE(0x07d1, 0x3c17) }, /* Encore */ { USB_DEVICE(0x203d, 0x14a1) }, /* Gemtek */ { USB_DEVICE(0x15a9, 0x0010) }, /* Gigabyte */ { USB_DEVICE(0x1044, 0x800c) }, /* Huawei */ { USB_DEVICE(0x148f, 0xf101) }, /* I-O DATA */ { USB_DEVICE(0x04bb, 0x094b) }, /* LevelOne */ { USB_DEVICE(0x1740, 0x0605) }, { USB_DEVICE(0x1740, 0x0615) }, /* Logitec */ { USB_DEVICE(0x0789, 0x0168) }, { USB_DEVICE(0x0789, 0x0169) }, /* Motorola */ { USB_DEVICE(0x100d, 0x9032) }, /* Pegatron */ { USB_DEVICE(0x05a6, 0x0101) }, { USB_DEVICE(0x1d4d, 0x0010) }, /* Planex */ { USB_DEVICE(0x2019, 0xab24) }, /* Qcom */ { USB_DEVICE(0x18e8, 0x6259) }, /* RadioShack */ { USB_DEVICE(0x08b9, 0x1197) }, /* Sitecom */ { USB_DEVICE(0x0df6, 0x003c) }, { USB_DEVICE(0x0df6, 0x004a) }, { USB_DEVICE(0x0df6, 0x004d) }, { USB_DEVICE(0x0df6, 0x0053) }, /* SMC */ { USB_DEVICE(0x083a, 0xa512) }, { USB_DEVICE(0x083a, 0xc522) }, { USB_DEVICE(0x083a, 0xd522) }, { USB_DEVICE(0x083a, 0xf511) }, /* Zyxel */ { USB_DEVICE(0x0586, 0x341a) }, #endif { 0, } }; MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2800 USB Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2870 USB chipset based cards"); MODULE_DEVICE_TABLE(usb, rt2800usb_device_table); MODULE_FIRMWARE(FIRMWARE_RT2870); MODULE_LICENSE("GPL"); static int rt2800usb_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { return rt2x00usb_probe(usb_intf, &rt2800usb_ops); } static struct usb_driver rt2800usb_driver = { .name = KBUILD_MODNAME, .id_table = rt2800usb_device_table, .probe = rt2800usb_probe, .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(rt2800usb_driver); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2500usb.c0000644000175000017500000017247612026211315023353 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2500usb Abstract: rt2500usb device specific routines. Supported chipsets: RT2570. */ #include #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00usb.h" #include "rt2500usb.h" /* * Allow hardware encryption to be disabled. */ static bool modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); /* * Register access. * All access to the CSR registers will go through the methods * rt2500usb_register_read and rt2500usb_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay * between each attampt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. * If the csr_mutex is already held then the _lock variants must * be used instead. */ static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u16 *value) { __le16 reg; rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, ®, sizeof(reg), REGISTER_TIMEOUT); *value = le16_to_cpu(reg); } static inline void rt2500usb_register_read_lock(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u16 *value) { __le16 reg; rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, ®, sizeof(reg), REGISTER_TIMEOUT); *value = le16_to_cpu(reg); } static inline void rt2500usb_register_multiread(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u16 length) { rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, value, length, REGISTER_TIMEOUT16(length)); } static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u16 value) { __le16 reg = cpu_to_le16(value); rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, ®, sizeof(reg), REGISTER_TIMEOUT); } static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u16 value) { __le16 reg = cpu_to_le16(value); rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, ®, sizeof(reg), REGISTER_TIMEOUT); } static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u16 length) { rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, value, length, REGISTER_TIMEOUT16(length)); } static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, struct rt2x00_field16 field, u16 *reg) { unsigned int i; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2500usb_register_read_lock(rt2x00dev, offset, reg); if (!rt2x00_get_field16(*reg, field)) return 1; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "Indirect register access failed: " "offset=0x%.08x, value=0x%.08x\n", offset, *reg); *reg = ~0; return 0; } #define WAIT_FOR_BBP(__dev, __reg) \ rt2500usb_regbusy_read((__dev), PHY_CSR8, PHY_CSR8_BUSY, (__reg)) #define WAIT_FOR_RF(__dev, __reg) \ rt2500usb_regbusy_read((__dev), PHY_CSR10, PHY_CSR10_RF_BUSY, (__reg)) static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u16 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field16(®, PHY_CSR7_DATA, value); rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 0); rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { u16 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the read request into the register. * After the data has been written, we wait until hardware * returns the correct value, if at any time the register * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 1); rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); if (WAIT_FOR_BBP(rt2x00dev, ®)) rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, ®); } *value = rt2x00_get_field16(reg, PHY_CSR7_DATA); mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u16 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RF becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_RF(rt2x00dev, ®)) { reg = 0; rt2x00_set_field16(®, PHY_CSR9_RF_VALUE, value); rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg); reg = 0; rt2x00_set_field16(®, PHY_CSR10_RF_VALUE, value >> 16); rt2x00_set_field16(®, PHY_CSR10_RF_NUMBER_OF_BITS, 20); rt2x00_set_field16(®, PHY_CSR10_RF_IF_SELECT, 0); rt2x00_set_field16(®, PHY_CSR10_RF_BUSY, 1); rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg); rt2x00_rf_write(rt2x00dev, word, value); } mutex_unlock(&rt2x00dev->csr_mutex); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { rt2500usb_register_read(rt2x00dev, offset, (u16 *)value); } static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value) { rt2500usb_register_write(rt2x00dev, offset, value); } static const struct rt2x00debug rt2500usb_rt2x00debug = { .owner = THIS_MODULE, .csr = { .read = _rt2500usb_register_read, .write = _rt2500usb_register_write, .flags = RT2X00DEBUGFS_OFFSET, .word_base = CSR_REG_BASE, .word_size = sizeof(u16), .word_count = CSR_REG_SIZE / sizeof(u16), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt2500usb_bbp_read, .write = rt2500usb_bbp_write, .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt2500usb_rf_write, .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u16 reg; rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); return rt2x00_get_field16(reg, MAC_CSR19_VAL7); } #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2500usb_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); unsigned int enabled = brightness != LED_OFF; u16 reg; rt2500usb_register_read(led->rt2x00dev, MAC_CSR20, ®); if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) rt2x00_set_field16(®, MAC_CSR20_LINK, enabled); else if (led->type == LED_TYPE_ACTIVITY) rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, enabled); rt2500usb_register_write(led->rt2x00dev, MAC_CSR20, reg); } static int rt2500usb_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); u16 reg; rt2500usb_register_read(led->rt2x00dev, MAC_CSR21, ®); rt2x00_set_field16(®, MAC_CSR21_ON_PERIOD, *delay_on); rt2x00_set_field16(®, MAC_CSR21_OFF_PERIOD, *delay_off); rt2500usb_register_write(led->rt2x00dev, MAC_CSR21, reg); return 0; } static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; led->type = type; led->led_dev.brightness_set = rt2500usb_brightness_set; led->led_dev.blink_set = rt2500usb_blink_set; led->flags = LED_INITIALIZED; } #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Configuration handlers. */ /* * rt2500usb does not differentiate between shared and pairwise * keys, so we should use the same function for both key types. */ static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { u32 mask; u16 reg; enum cipher curr_cipher; if (crypto->cmd == SET_KEY) { /* * Disallow to set WEP key other than with index 0, * it is known that not work at least on some hardware. * SW crypto will be used in that case. */ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) && key->keyidx != 0) return -EOPNOTSUPP; /* * Pairwise key will always be entry 0, but this * could collide with a shared key on the same * position... */ mask = TXRX_CSR0_KEY_ID.bit_mask; rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); curr_cipher = rt2x00_get_field16(reg, TXRX_CSR0_ALGORITHM); reg &= mask; if (reg && reg == mask) return -ENOSPC; reg = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); key->hw_key_idx += reg ? ffz(reg) : 0; /* * Hardware requires that all keys use the same cipher * (e.g. TKIP-only, AES-only, but not TKIP+AES). * If this is not the first key, compare the cipher with the * first one and fall back to SW crypto if not the same. */ if (key->hw_key_idx > 0 && crypto->cipher != curr_cipher) return -EOPNOTSUPP; rt2500usb_register_multiwrite(rt2x00dev, KEY_ENTRY(key->hw_key_idx), crypto->key, sizeof(crypto->key)); /* * The driver does not support the IV/EIV generation * in hardware. However it demands the data to be provided * both separately as well as inside the frame. * We already provided the CONFIG_CRYPTO_COPY_IV to rt2x00lib * to ensure rt2x00lib will not strip the data from the * frame after the copy, now we must tell mac80211 * to generate the IV/EIV data. */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; } /* * TXRX_CSR0_KEY_ID contains only single-bit fields to indicate * a particular key is valid. */ rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, crypto->cipher); rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); mask = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); if (crypto->cmd == SET_KEY) mask |= 1 << key->hw_key_idx; else if (crypto->cmd == DISABLE_KEY) mask &= ~(1 << key->hw_key_idx); rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, mask); rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); return 0; } static void rt2500usb_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { u16 reg; /* * Start configuration steps. * Note that the version error will always be dropped * and broadcast frames will always be accepted since * there is no filter for it at this time. */ rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field16(®, TXRX_CSR2_DROP_CRC, !(filter_flags & FIF_FCSFAIL)); rt2x00_set_field16(®, TXRX_CSR2_DROP_PHYSICAL, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field16(®, TXRX_CSR2_DROP_CONTROL, !(filter_flags & FIF_CONTROL)); rt2x00_set_field16(®, TXRX_CSR2_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field16(®, TXRX_CSR2_DROP_TODS, !(filter_flags & FIF_PROMISC_IN_BSS) && !rt2x00dev->intf_ap_count); rt2x00_set_field16(®, TXRX_CSR2_DROP_VERSION_ERROR, 1); rt2x00_set_field16(®, TXRX_CSR2_DROP_MULTICAST, !(filter_flags & FIF_ALLMULTI)); rt2x00_set_field16(®, TXRX_CSR2_DROP_BROADCAST, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); } static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags) { unsigned int bcn_preload; u16 reg; if (flags & CONFIG_UPDATE_TYPE) { /* * Enable beacon config */ bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); rt2500usb_register_read(rt2x00dev, TXRX_CSR20, ®); rt2x00_set_field16(®, TXRX_CSR20_OFFSET, bcn_preload >> 6); rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, 2 * (conf->type != NL80211_IFTYPE_STATION)); rt2500usb_register_write(rt2x00dev, TXRX_CSR20, reg); /* * Enable synchronisation. */ rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); rt2x00_set_field16(®, TXRX_CSR18_OFFSET, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, conf->sync); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); } if (flags & CONFIG_UPDATE_MAC) rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac, (3 * sizeof(__le16))); if (flags & CONFIG_UPDATE_BSSID) rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR5, conf->bssid, (3 * sizeof(__le16))); } static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed) { u16 reg; if (changed & BSS_CHANGED_ERP_PREAMBLE) { rt2500usb_register_read(rt2x00dev, TXRX_CSR10, ®); rt2x00_set_field16(®, TXRX_CSR10_AUTORESPOND_PREAMBLE, !!erp->short_preamble); rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg); } if (changed & BSS_CHANGED_BASIC_RATES) rt2500usb_register_write(rt2x00dev, TXRX_CSR11, erp->basic_rates); if (changed & BSS_CHANGED_BEACON_INT) { rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); rt2x00_set_field16(®, TXRX_CSR18_INTERVAL, erp->beacon_int * 4); rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); } if (changed & BSS_CHANGED_ERP_SLOT) { rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time); rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs); rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs); } } static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r2; u8 r14; u16 csr5; u16 csr6; /* * We should never come here because rt2x00lib is supposed * to catch this and send us the correct antenna explicitely. */ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || ant->tx == ANTENNA_SW_DIVERSITY); rt2500usb_bbp_read(rt2x00dev, 2, &r2); rt2500usb_bbp_read(rt2x00dev, 14, &r14); rt2500usb_register_read(rt2x00dev, PHY_CSR5, &csr5); rt2500usb_register_read(rt2x00dev, PHY_CSR6, &csr6); /* * Configure the TX antenna. */ switch (ant->tx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 1); rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 1); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 1); break; case ANTENNA_A: rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 0); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 0); break; case ANTENNA_B: default: rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 2); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 2); break; } /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 1); break; case ANTENNA_A: rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); break; case ANTENNA_B: default: rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); break; } /* * RT2525E and RT5222 need to flip TX I/Q */ if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1); /* * RT2525E does not need RX I/Q Flip. */ if (rt2x00_rf(rt2x00dev, RF2525E)) rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); } else { rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 0); } rt2500usb_bbp_write(rt2x00dev, 2, r2); rt2500usb_bbp_write(rt2x00dev, 14, r14); rt2500usb_register_write(rt2x00dev, PHY_CSR5, csr5); rt2500usb_register_write(rt2x00dev, PHY_CSR6, csr6); } static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev, struct rf_channel *rf, const int txpower) { /* * Set TXpower. */ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); /* * For RT2525E we should first set the channel to half band higher. */ if (rt2x00_rf(rt2x00dev, RF2525E)) { static const u32 vals[] = { 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba, 0x000008ba, 0x000008be, 0x000008b7, 0x00000902, 0x00000902, 0x00000906 }; rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); if (rf->rf4) rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); } rt2500usb_rf_write(rt2x00dev, 1, rf->rf1); rt2500usb_rf_write(rt2x00dev, 2, rf->rf2); rt2500usb_rf_write(rt2x00dev, 3, rf->rf3); if (rf->rf4) rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); } static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev, const int txpower) { u32 rf3; rt2x00_rf_read(rt2x00dev, 3, &rf3); rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); rt2500usb_rf_write(rt2x00dev, 3, rf3); } static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { enum dev_state state = (libconf->conf->flags & IEEE80211_CONF_PS) ? STATE_SLEEP : STATE_AWAKE; u16 reg; if (state == STATE_SLEEP) { rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, rt2x00dev->beacon_int - 20); rt2x00_set_field16(®, MAC_CSR18_BEACONS_BEFORE_WAKEUP, libconf->conf->listen_interval - 1); /* We must first disable autowake before it can be enabled */ rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 1); rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); } else { rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); } rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } static void rt2500usb_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { if (flags & IEEE80211_CONF_CHANGE_CHANNEL) rt2500usb_config_channel(rt2x00dev, &libconf->rf, libconf->conf->power_level); if ((flags & IEEE80211_CONF_CHANGE_POWER) && !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) rt2500usb_config_txpower(rt2x00dev, libconf->conf->power_level); if (flags & IEEE80211_CONF_CHANGE_PS) rt2500usb_config_ps(rt2x00dev, libconf); } /* * Link tuning */ static void rt2500usb_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { u16 reg; /* * Update FCS error count from register. */ rt2500usb_register_read(rt2x00dev, STA_CSR0, ®); qual->rx_failed = rt2x00_get_field16(reg, STA_CSR0_FCS_ERROR); /* * Update False CCA count from register. */ rt2500usb_register_read(rt2x00dev, STA_CSR3, ®); qual->false_cca = rt2x00_get_field16(reg, STA_CSR3_FALSE_CCA_ERROR); } static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { u16 eeprom; u16 value; rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &eeprom); value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R24_LOW); rt2500usb_bbp_write(rt2x00dev, 24, value); rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &eeprom); value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R25_LOW); rt2500usb_bbp_write(rt2x00dev, 25, value); rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &eeprom); value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R61_LOW); rt2500usb_bbp_write(rt2x00dev, 61, value); rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &eeprom); value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_VGCUPPER); rt2500usb_bbp_write(rt2x00dev, 17, value); qual->vgc_level = value; } /* * Queue handlers. */ static void rt2500usb_start_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u16 reg; switch (queue->qid) { case QID_RX: rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); break; case QID_BEACON: rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); break; default: break; } } static void rt2500usb_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u16 reg; switch (queue->qid) { case QID_RX: rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 1); rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); break; case QID_BEACON: rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); break; default: break; } } /* * Initialization functions. */ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) { u16 reg; rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0x0001, USB_MODE_TEST, REGISTER_TIMEOUT); rt2x00usb_vendor_request_sw(rt2x00dev, USB_SINGLE_WRITE, 0x0308, 0x00f0, REGISTER_TIMEOUT); rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 1); rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x1111); rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x1e11); rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 1); rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 1); rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 0); rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 0); rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 0); rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR5, ®); rt2x00_set_field16(®, TXRX_CSR5_BBP_ID0, 13); rt2x00_set_field16(®, TXRX_CSR5_BBP_ID0_VALID, 1); rt2x00_set_field16(®, TXRX_CSR5_BBP_ID1, 12); rt2x00_set_field16(®, TXRX_CSR5_BBP_ID1_VALID, 1); rt2500usb_register_write(rt2x00dev, TXRX_CSR5, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR6, ®); rt2x00_set_field16(®, TXRX_CSR6_BBP_ID0, 10); rt2x00_set_field16(®, TXRX_CSR6_BBP_ID0_VALID, 1); rt2x00_set_field16(®, TXRX_CSR6_BBP_ID1, 11); rt2x00_set_field16(®, TXRX_CSR6_BBP_ID1_VALID, 1); rt2500usb_register_write(rt2x00dev, TXRX_CSR6, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR7, ®); rt2x00_set_field16(®, TXRX_CSR7_BBP_ID0, 7); rt2x00_set_field16(®, TXRX_CSR7_BBP_ID0_VALID, 1); rt2x00_set_field16(®, TXRX_CSR7_BBP_ID1, 6); rt2x00_set_field16(®, TXRX_CSR7_BBP_ID1_VALID, 1); rt2500usb_register_write(rt2x00dev, TXRX_CSR7, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR8, ®); rt2x00_set_field16(®, TXRX_CSR8_BBP_ID0, 5); rt2x00_set_field16(®, TXRX_CSR8_BBP_ID0_VALID, 1); rt2x00_set_field16(®, TXRX_CSR8_BBP_ID1, 0); rt2x00_set_field16(®, TXRX_CSR8_BBP_ID1_VALID, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR8, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, 0); rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); rt2500usb_register_write(rt2x00dev, TXRX_CSR21, 0xe78f); rt2500usb_register_write(rt2x00dev, MAC_CSR9, 0xff1d); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 0); rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 1); rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) { rt2500usb_register_read(rt2x00dev, PHY_CSR2, ®); rt2x00_set_field16(®, PHY_CSR2_LNA, 0); } else { reg = 0; rt2x00_set_field16(®, PHY_CSR2_LNA, 1); rt2x00_set_field16(®, PHY_CSR2_LNA_MODE, 3); } rt2500usb_register_write(rt2x00dev, PHY_CSR2, reg); rt2500usb_register_write(rt2x00dev, MAC_CSR11, 0x0002); rt2500usb_register_write(rt2x00dev, MAC_CSR22, 0x0053); rt2500usb_register_write(rt2x00dev, MAC_CSR15, 0x01ee); rt2500usb_register_write(rt2x00dev, MAC_CSR16, 0x0000); rt2500usb_register_read(rt2x00dev, MAC_CSR8, ®); rt2x00_set_field16(®, MAC_CSR8_MAX_FRAME_UNIT, rt2x00dev->rx->data_size); rt2500usb_register_write(rt2x00dev, MAC_CSR8, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, CIPHER_NONE); rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, 90); rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); rt2500usb_register_read(rt2x00dev, PHY_CSR4, ®); rt2x00_set_field16(®, PHY_CSR4_LOW_RF_LE, 1); rt2500usb_register_write(rt2x00dev, PHY_CSR4, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR1, ®); rt2x00_set_field16(®, TXRX_CSR1_AUTO_SEQUENCE, 1); rt2500usb_register_write(rt2x00dev, TXRX_CSR1, reg); return 0; } static int rt2500usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2500usb_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) return 0; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); return -EACCES; } static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; u16 eeprom; u8 value; u8 reg_id; if (unlikely(rt2500usb_wait_bbp_ready(rt2x00dev))) return -EACCES; rt2500usb_bbp_write(rt2x00dev, 3, 0x02); rt2500usb_bbp_write(rt2x00dev, 4, 0x19); rt2500usb_bbp_write(rt2x00dev, 14, 0x1c); rt2500usb_bbp_write(rt2x00dev, 15, 0x30); rt2500usb_bbp_write(rt2x00dev, 16, 0xac); rt2500usb_bbp_write(rt2x00dev, 18, 0x18); rt2500usb_bbp_write(rt2x00dev, 19, 0xff); rt2500usb_bbp_write(rt2x00dev, 20, 0x1e); rt2500usb_bbp_write(rt2x00dev, 21, 0x08); rt2500usb_bbp_write(rt2x00dev, 22, 0x08); rt2500usb_bbp_write(rt2x00dev, 23, 0x08); rt2500usb_bbp_write(rt2x00dev, 24, 0x80); rt2500usb_bbp_write(rt2x00dev, 25, 0x50); rt2500usb_bbp_write(rt2x00dev, 26, 0x08); rt2500usb_bbp_write(rt2x00dev, 27, 0x23); rt2500usb_bbp_write(rt2x00dev, 30, 0x10); rt2500usb_bbp_write(rt2x00dev, 31, 0x2b); rt2500usb_bbp_write(rt2x00dev, 32, 0xb9); rt2500usb_bbp_write(rt2x00dev, 34, 0x12); rt2500usb_bbp_write(rt2x00dev, 35, 0x50); rt2500usb_bbp_write(rt2x00dev, 39, 0xc4); rt2500usb_bbp_write(rt2x00dev, 40, 0x02); rt2500usb_bbp_write(rt2x00dev, 41, 0x60); rt2500usb_bbp_write(rt2x00dev, 53, 0x10); rt2500usb_bbp_write(rt2x00dev, 54, 0x18); rt2500usb_bbp_write(rt2x00dev, 56, 0x08); rt2500usb_bbp_write(rt2x00dev, 57, 0x10); rt2500usb_bbp_write(rt2x00dev, 58, 0x08); rt2500usb_bbp_write(rt2x00dev, 61, 0x60); rt2500usb_bbp_write(rt2x00dev, 62, 0x10); rt2500usb_bbp_write(rt2x00dev, 75, 0xff); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); rt2500usb_bbp_write(rt2x00dev, reg_id, value); } } return 0; } /* * Device state switch handlers. */ static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev) { /* * Initialize all registers. */ if (unlikely(rt2500usb_init_registers(rt2x00dev) || rt2500usb_init_bbp(rt2x00dev))) return -EIO; return 0; } static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev) { rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x2121); rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x2121); /* * Disable synchronisation. */ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); rt2x00usb_disable_radio(rt2x00dev); } static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u16 reg; u16 reg2; unsigned int i; char put_to_sleep; char bbp_state; char rf_state; put_to_sleep = (state != STATE_AWAKE); reg = 0; rt2x00_set_field16(®, MAC_CSR17_BBP_DESIRE_STATE, state); rt2x00_set_field16(®, MAC_CSR17_RF_DESIRE_STATE, state); rt2x00_set_field16(®, MAC_CSR17_PUT_TO_SLEEP, put_to_sleep); rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); rt2x00_set_field16(®, MAC_CSR17_SET_STATE, 1); rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); /* * Device is not guaranteed to be in the requested state yet. * We must wait until the register indicates that the * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2500usb_register_read(rt2x00dev, MAC_CSR17, ®2); bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE); rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE); if (bbp_state == state && rf_state == state) return 0; rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); msleep(30); } return -EBUSY; } static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int retval = 0; switch (state) { case STATE_RADIO_ON: retval = rt2500usb_enable_radio(rt2x00dev); break; case STATE_RADIO_OFF: rt2500usb_disable_radio(rt2x00dev); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: /* No support, but no error either */ break; case STATE_DEEP_SLEEP: case STATE_SLEEP: case STATE_STANDBY: case STATE_AWAKE: retval = rt2500usb_set_state(rt2x00dev, state); break; default: retval = -ENOTSUPP; break; } if (unlikely(retval)) ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", state, retval); return retval; } /* * TX descriptor initialization */ static void rt2500usb_write_tx_desc(struct queue_entry *entry, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *txd = (__le32 *) entry->skb->data; u32 word; /* * Start writing the descriptor words. */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, txdesc->retry_limit); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, (txdesc->rate_mode == RATE_MODE_OFDM)); rt2x00_set_field32(&word, TXD_W0_NEW_SEQ, test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXD_W0_CIPHER, !!txdesc->cipher); rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx); rt2x00_desc_write(txd, 0, word); rt2x00_desc_read(txd, 1, &word); rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); rt2x00_set_field32(&word, TXD_W1_AIFS, entry->queue->aifs); rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); rt2x00_desc_write(txd, 1, word); rt2x00_desc_read(txd, 2, &word); rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, txdesc->u.plcp.length_low); rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, txdesc->u.plcp.length_high); rt2x00_desc_write(txd, 2, word); if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); } /* * Register descriptor details in skb frame descriptor. */ skbdesc->flags |= SKBDESC_DESC_IN_SKB; skbdesc->desc = txd; skbdesc->desc_len = TXD_DESC_SIZE; } /* * TX data initialization */ static void rt2500usb_beacondone(struct urb *urb); static void rt2500usb_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint); int length; u16 reg, reg0; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); /* * Add space for the descriptor in front of the skb. */ skb_push(entry->skb, TXD_DESC_SIZE); memset(entry->skb->data, 0, TXD_DESC_SIZE); /* * Write the TX descriptor for the beacon. */ rt2500usb_write_tx_desc(entry, txdesc); /* * Dump beacon to userspace through debugfs. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); /* * USB devices cannot blindly pass the skb->len as the * length of the data to usb_fill_bulk_urb. Pass the skb * to the driver to determine what the length should be. */ length = rt2x00dev->ops->lib->get_tx_data_len(entry); usb_fill_bulk_urb(bcn_priv->urb, usb_dev, pipe, entry->skb->data, length, rt2500usb_beacondone, entry); /* * Second we need to create the guardian byte. * We only need a single byte, so lets recycle * the 'flags' field we are not using for beacons. */ bcn_priv->guardian_data = 0; usb_fill_bulk_urb(bcn_priv->guardian_urb, usb_dev, pipe, &bcn_priv->guardian_data, 1, rt2500usb_beacondone, entry); /* * Send out the guardian byte. */ usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC); /* * Enable beaconing again. */ rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); reg0 = reg; rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); /* * Beacon generation will fail initially. * To prevent this we need to change the TXRX_CSR19 * register several times (reg0 is the same as reg * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0 * and 1 in reg). */ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); } static int rt2500usb_get_tx_data_len(struct queue_entry *entry) { int length; /* * The length _must_ be a multiple of 2, * but it must _not_ be a multiple of the USB packet size. */ length = roundup(entry->skb->len, 2); length += (2 * !(length % entry->queue->usb_maxpacket)); return length; } /* * RX control handlers */ static void rt2500usb_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv = entry->priv_data; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *rxd = (__le32 *)(entry->skb->data + (entry_priv->urb->actual_length - entry->queue->desc_size)); u32 word0; u32 word1; /* * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of * frame data in rt2x00usb. */ memcpy(skbdesc->desc, rxd, skbdesc->desc_len); rxd = (__le32 *)skbdesc->desc; /* * It is now safe to read the descriptor on all architectures. */ rt2x00_desc_read(rxd, 0, &word0); rt2x00_desc_read(rxd, 1, &word1); if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER); if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR)) rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY; if (rxdesc->cipher != CIPHER_NONE) { _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); rxdesc->dev_flags |= RXDONE_CRYPTO_IV; /* ICV is located at the end of frame */ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) rxdesc->flags |= RX_FLAG_DECRYPTED; else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) rxdesc->flags |= RX_FLAG_MMIC_ERROR; } /* * Obtain the status about this packet. * When frame was received with an OFDM bitrate, * the signal is the PLCP value. If it was received with * a CCK bitrate the signal is the rate in 100kbit/s. */ rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); rxdesc->rssi = rt2x00_get_field32(word1, RXD_W1_RSSI) - rt2x00dev->rssi_offset; rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); if (rt2x00_get_field32(word0, RXD_W0_OFDM)) rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; else rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; /* * Adjust the skb memory window to the frame boundaries. */ skb_trim(entry->skb, rxdesc->size); } /* * Interrupt functions. */ static void rt2500usb_beacondone(struct urb *urb) { struct queue_entry *entry = (struct queue_entry *)urb->context; struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &entry->queue->rt2x00dev->flags)) return; /* * Check if this was the guardian beacon, * if that was the case we need to send the real beacon now. * Otherwise we should free the sk_buffer, the device * should be doing the rest of the work now. */ if (bcn_priv->guardian_urb == urb) { usb_submit_urb(bcn_priv->urb, GFP_ATOMIC); } else if (bcn_priv->urb == urb) { dev_kfree_skb(entry->skb); entry->skb = NULL; } } /* * Device probe functions. */ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) { u16 word; u8 *mac; u8 bbp; rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); /* * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { eth_random_addr(mac); EEPROM(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, ANTENNA_SW_DIVERSITY); rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, ANTENNA_SW_DIVERSITY); rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE, LED_MODE_DEFAULT); rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, DEFAULT_RSSI_OFFSET); rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); EEPROM(rt2x00dev, "Calibrate offset: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_THRESHOLD, 45); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE, word); EEPROM(rt2x00dev, "BBPtune: 0x%04x\n", word); } /* * Switch lower vgc bound to current BBP R17 value, * lower the value a bit for better quality. */ rt2500usb_bbp_read(rt2x00dev, 17, &bbp); bbp -= 6; rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCUPPER, 0x40); rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); EEPROM(rt2x00dev, "BBPtune vgc: 0x%04x\n", word); } else { rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_LOW, 0x48); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_HIGH, 0x41); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R17, word); EEPROM(rt2x00dev, "BBPtune r17: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_LOW, 0x40); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_HIGH, 0x80); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R24, word); EEPROM(rt2x00dev, "BBPtune r24: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_LOW, 0x40); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_HIGH, 0x50); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R25, word); EEPROM(rt2x00dev, "BBPtune r25: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_LOW, 0x60); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_HIGH, 0x6d); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R61, word); EEPROM(rt2x00dev, "BBPtune r61: 0x%04x\n", word); } return 0; } static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) { u16 reg; u16 value; u16 eeprom; /* * Read EEPROM word for configuration. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); /* * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2570, value, reg); if (((reg & 0xfff0) != 0) || ((reg & 0x0000000f) == 0)) { ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); return -ENODEV; } if (!rt2x00_rf(rt2x00dev, RF2522) && !rt2x00_rf(rt2x00dev, RF2523) && !rt2x00_rf(rt2x00dev, RF2524) && !rt2x00_rf(rt2x00dev, RF2525) && !rt2x00_rf(rt2x00dev, RF2525E) && !rt2x00_rf(rt2x00dev, RF5222)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } /* * Identify default antenna configuration. */ rt2x00dev->default_ant.tx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); rt2x00dev->default_ant.rx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); /* * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead. * I am not 100% sure about this, but the legacy drivers do not * indicate antenna swapping in software is required when * diversity is enabled. */ if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; /* * Store led mode, for correct led behaviour. */ #ifdef CONFIG_RT2X00_LIB_LEDS value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); if (value == LED_MODE_TXRX_ACTIVITY || value == LED_MODE_DEFAULT || value == LED_MODE_ASUS) rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_ACTIVITY); #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Detect if this device has an hardware controlled radio. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Read the RSSI <-> dBm offset information. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); rt2x00dev->rssi_offset = rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); return 0; } /* * RF value list for RF2522 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2522[] = { { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 }, { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 }, { 3, 0x00002050, 0x000c2002, 0x00000101, 0 }, { 4, 0x00002050, 0x000c2016, 0x00000101, 0 }, { 5, 0x00002050, 0x000c202a, 0x00000101, 0 }, { 6, 0x00002050, 0x000c203e, 0x00000101, 0 }, { 7, 0x00002050, 0x000c2052, 0x00000101, 0 }, { 8, 0x00002050, 0x000c2066, 0x00000101, 0 }, { 9, 0x00002050, 0x000c207a, 0x00000101, 0 }, { 10, 0x00002050, 0x000c208e, 0x00000101, 0 }, { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 }, { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 }, { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 }, { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 }, }; /* * RF value list for RF2523 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2523[] = { { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b }, { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b }, { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b }, { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b }, { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b }, { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b }, { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b }, { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b }, { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b }, { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b }, { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b }, { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b }, { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b }, { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 }, }; /* * RF value list for RF2524 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2524[] = { { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b }, { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b }, { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b }, { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b }, { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b }, { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b }, { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b }, { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b }, { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b }, { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b }, { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b }, { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b }, { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b }, { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 }, }; /* * RF value list for RF2525 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2525[] = { { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b }, { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b }, { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b }, { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b }, { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b }, { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b }, { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b }, { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b }, { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b }, { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b }, { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b }, { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b }, { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b }, { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 }, }; /* * RF value list for RF2525e * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2525e[] = { { 1, 0x00022010, 0x0000089a, 0x00060111, 0x00000e1b }, { 2, 0x00022010, 0x0000089e, 0x00060111, 0x00000e07 }, { 3, 0x00022010, 0x0000089e, 0x00060111, 0x00000e1b }, { 4, 0x00022010, 0x000008a2, 0x00060111, 0x00000e07 }, { 5, 0x00022010, 0x000008a2, 0x00060111, 0x00000e1b }, { 6, 0x00022010, 0x000008a6, 0x00060111, 0x00000e07 }, { 7, 0x00022010, 0x000008a6, 0x00060111, 0x00000e1b }, { 8, 0x00022010, 0x000008aa, 0x00060111, 0x00000e07 }, { 9, 0x00022010, 0x000008aa, 0x00060111, 0x00000e1b }, { 10, 0x00022010, 0x000008ae, 0x00060111, 0x00000e07 }, { 11, 0x00022010, 0x000008ae, 0x00060111, 0x00000e1b }, { 12, 0x00022010, 0x000008b2, 0x00060111, 0x00000e07 }, { 13, 0x00022010, 0x000008b2, 0x00060111, 0x00000e1b }, { 14, 0x00022010, 0x000008b6, 0x00060111, 0x00000e23 }, }; /* * RF value list for RF5222 * Supports: 2.4 GHz & 5.2 GHz */ static const struct rf_channel rf_vals_5222[] = { { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b }, { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b }, { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b }, { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b }, { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b }, { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b }, { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b }, { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b }, { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b }, { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b }, { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b }, { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b }, { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b }, { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b }, /* 802.11 UNI / HyperLan 2 */ { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f }, { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f }, { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f }, { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f }, { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f }, { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f }, { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f }, { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f }, /* 802.11 HyperLan 2 */ { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f }, { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f }, { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f }, { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f }, { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f }, { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f }, { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f }, { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f }, { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f }, { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f }, /* 802.11 UNII */ { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f }, { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 }, { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 }, { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 }, { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, }; static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *tx_power; unsigned int i; /* * Initialize all hw fields. * * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are * capable of sending the buffered frames out after the DTIM * transmission using rt2x00lib_beacondone. This will send out * multicast and broadcast traffic immediately instead of buffering it * infinitly and thus dropping it after some time. */ rt2x00dev->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; if (rt2x00_rf(rt2x00dev, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); spec->channels = rf_vals_bg_2522; } else if (rt2x00_rf(rt2x00dev, RF2523)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); spec->channels = rf_vals_bg_2523; } else if (rt2x00_rf(rt2x00dev, RF2524)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); spec->channels = rf_vals_bg_2524; } else if (rt2x00_rf(rt2x00dev, RF2525)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); spec->channels = rf_vals_bg_2525; } else if (rt2x00_rf(rt2x00dev, RF2525E)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); spec->channels = rf_vals_bg_2525e; } else if (rt2x00_rf(rt2x00dev, RF5222)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; } /* * Create channel information array */ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; spec->channels_info = info; tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); for (i = 0; i < 14; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } if (spec->num_channels > 14) { for (i = 14; i < spec->num_channels; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = DEFAULT_TXPOWER; } } return 0; } static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; u16 reg; /* * Allocate eeprom data. */ retval = rt2500usb_validate_eeprom(rt2x00dev); if (retval) return retval; retval = rt2500usb_init_eeprom(rt2x00dev); if (retval) return retval; /* * Enable rfkill polling by setting GPIO direction of the * rfkill switch GPIO pin correctly. */ rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); rt2x00_set_field16(®, MAC_CSR19_DIR0, 0); rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg); /* * Initialize hw specifications. */ retval = rt2500usb_probe_hw_mode(rt2x00dev); if (retval) return retval; /* * This device requires the atim queue */ __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); __set_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags); if (!modparam_nohwcrypt) { __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags); } __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); /* * Set the rssi offset. */ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; return 0; } static const struct ieee80211_ops rt2500usb_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, .set_tim = rt2x00mac_set_tim, .set_key = rt2x00mac_set_key, .sw_scan_start = rt2x00mac_sw_scan_start, .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, .rfkill_poll = rt2x00mac_rfkill_poll, .flush = rt2x00mac_flush, .set_antenna = rt2x00mac_set_antenna, .get_antenna = rt2x00mac_get_antenna, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { .probe_hw = rt2500usb_probe_hw, .initialize = rt2x00usb_initialize, .uninitialize = rt2x00usb_uninitialize, .clear_entry = rt2x00usb_clear_entry, .set_device_state = rt2500usb_set_device_state, .rfkill_poll = rt2500usb_rfkill_poll, .link_stats = rt2500usb_link_stats, .reset_tuner = rt2500usb_reset_tuner, .watchdog = rt2x00usb_watchdog, .start_queue = rt2500usb_start_queue, .kick_queue = rt2x00usb_kick_queue, .stop_queue = rt2500usb_stop_queue, .flush_queue = rt2x00usb_flush_queue, .write_tx_desc = rt2500usb_write_tx_desc, .write_beacon = rt2500usb_write_beacon, .get_tx_data_len = rt2500usb_get_tx_data_len, .fill_rxdone = rt2500usb_fill_rxdone, .config_shared_key = rt2500usb_config_key, .config_pairwise_key = rt2500usb_config_key, .config_filter = rt2500usb_config_filter, .config_intf = rt2500usb_config_intf, .config_erp = rt2500usb_config_erp, .config_ant = rt2500usb_config_ant, .config = rt2500usb_config, }; static const struct data_queue_desc rt2500usb_queue_rx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = RXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct data_queue_desc rt2500usb_queue_tx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct data_queue_desc rt2500usb_queue_bcn = { .entry_num = 1, .data_size = MGMT_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb_bcn), }; static const struct data_queue_desc rt2500usb_queue_atim = { .entry_num = 8, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_usb), }; static const struct rt2x00_ops rt2500usb_ops = { .name = KBUILD_MODNAME, .max_sta_intf = 1, .max_ap_intf = 1, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXD_DESC_SIZE, .rx = &rt2500usb_queue_rx, .tx = &rt2500usb_queue_tx, .bcn = &rt2500usb_queue_bcn, .atim = &rt2500usb_queue_atim, .lib = &rt2500usb_rt2x00_ops, .hw = &rt2500usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS .debugfs = &rt2500usb_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * rt2500usb module information. */ static struct usb_device_id rt2500usb_device_table[] = { /* ASUS */ { USB_DEVICE(0x0b05, 0x1706) }, { USB_DEVICE(0x0b05, 0x1707) }, /* Belkin */ { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050A ver. 2.x */ { USB_DEVICE(0x050d, 0x7051) }, /* Cisco Systems */ { USB_DEVICE(0x13b1, 0x000d) }, { USB_DEVICE(0x13b1, 0x0011) }, { USB_DEVICE(0x13b1, 0x001a) }, /* Conceptronic */ { USB_DEVICE(0x14b2, 0x3c02) }, /* D-LINK */ { USB_DEVICE(0x2001, 0x3c00) }, /* Gigabyte */ { USB_DEVICE(0x1044, 0x8001) }, { USB_DEVICE(0x1044, 0x8007) }, /* Hercules */ { USB_DEVICE(0x06f8, 0xe000) }, /* Melco */ { USB_DEVICE(0x0411, 0x005e) }, { USB_DEVICE(0x0411, 0x0066) }, { USB_DEVICE(0x0411, 0x0067) }, { USB_DEVICE(0x0411, 0x008b) }, { USB_DEVICE(0x0411, 0x0097) }, /* MSI */ { USB_DEVICE(0x0db0, 0x6861) }, { USB_DEVICE(0x0db0, 0x6865) }, { USB_DEVICE(0x0db0, 0x6869) }, /* Ralink */ { USB_DEVICE(0x148f, 0x1706) }, { USB_DEVICE(0x148f, 0x2570) }, { USB_DEVICE(0x148f, 0x9020) }, /* Sagem */ { USB_DEVICE(0x079b, 0x004b) }, /* Siemens */ { USB_DEVICE(0x0681, 0x3c06) }, /* SMC */ { USB_DEVICE(0x0707, 0xee13) }, /* Spairon */ { USB_DEVICE(0x114b, 0x0110) }, /* SURECOM */ { USB_DEVICE(0x0769, 0x11f3) }, /* Trust */ { USB_DEVICE(0x0eb0, 0x9020) }, /* VTech */ { USB_DEVICE(0x0f88, 0x3012) }, /* Zinwell */ { USB_DEVICE(0x5a57, 0x0260) }, { 0, } }; MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2500 USB Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2570 USB chipset based cards"); MODULE_DEVICE_TABLE(usb, rt2500usb_device_table); MODULE_LICENSE("GPL"); static int rt2500usb_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { return rt2x00usb_probe(usb_intf, &rt2500usb_ops); } static struct usb_driver rt2500usb_driver = { .name = KBUILD_MODNAME, .id_table = rt2500usb_device_table, .probe = rt2500usb_probe, .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(rt2500usb_driver); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt73usb.h0000644000175000017500000007452312026211315023215 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt73usb Abstract: Data structures and registers for the rt73usb module. Supported chipsets: rt2571W & rt2671. */ #ifndef RT73USB_H #define RT73USB_H /* * RF chip defines. */ #define RF5226 0x0001 #define RF2528 0x0002 #define RF5225 0x0003 #define RF2527 0x0004 /* * Signal information. * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 120 /* * Register layout information. */ #define CSR_REG_BASE 0x3000 #define CSR_REG_SIZE 0x04b0 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0100 #define BBP_BASE 0x0000 #define BBP_SIZE 0x0080 #define RF_BASE 0x0004 #define RF_SIZE 0x0010 /* * Number of TX queues. */ #define NUM_TX_QUEUES 4 /* * USB registers. */ /* * MCU_LEDCS: LED control for MCU Mailbox. */ #define MCU_LEDCS_LED_MODE FIELD16(0x001f) #define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) #define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) #define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) #define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) #define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) #define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) #define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) #define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) #define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) #define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) #define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) /* * 8051 firmware image. */ #define FIRMWARE_RT2571 "rt73.bin" #define FIRMWARE_IMAGE_BASE 0x0800 /* * Security key table memory. * 16 entries 32-byte for shared key table * 64 entries 32-byte for pairwise key table * 64 entries 8-byte for pairwise ta key table */ #define SHARED_KEY_TABLE_BASE 0x1000 #define PAIRWISE_KEY_TABLE_BASE 0x1200 #define PAIRWISE_TA_TABLE_BASE 0x1a00 #define SHARED_KEY_ENTRY(__idx) \ ( SHARED_KEY_TABLE_BASE + \ ((__idx) * sizeof(struct hw_key_entry)) ) #define PAIRWISE_KEY_ENTRY(__idx) \ ( PAIRWISE_KEY_TABLE_BASE + \ ((__idx) * sizeof(struct hw_key_entry)) ) #define PAIRWISE_TA_ENTRY(__idx) \ ( PAIRWISE_TA_TABLE_BASE + \ ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) struct hw_key_entry { u8 key[16]; u8 tx_mic[8]; u8 rx_mic[8]; } __packed; struct hw_pairwise_ta_entry { u8 address[6]; u8 cipher; u8 reserved; } __packed; /* * Since NULL frame won't be that long (256 byte), * We steal 16 tail bytes to save debugging settings. */ #define HW_DEBUG_SETTING_BASE 0x2bf0 /* * On-chip BEACON frame space. */ #define HW_BEACON_BASE0 0x2400 #define HW_BEACON_BASE1 0x2500 #define HW_BEACON_BASE2 0x2600 #define HW_BEACON_BASE3 0x2700 #define HW_BEACON_OFFSET(__index) \ ( HW_BEACON_BASE0 + (__index * 0x0100) ) /* * MAC Control/Status Registers(CSR). * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * MAC_CSR0: ASIC revision number. */ #define MAC_CSR0 0x3000 #define MAC_CSR0_REVISION FIELD32(0x0000000f) #define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) /* * MAC_CSR1: System control register. * SOFT_RESET: Software reset bit, 1: reset, 0: normal. * BBP_RESET: Hardware reset BBP. * HOST_READY: Host is ready after initialization, 1: ready. */ #define MAC_CSR1 0x3004 #define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) #define MAC_CSR1_BBP_RESET FIELD32(0x00000002) #define MAC_CSR1_HOST_READY FIELD32(0x00000004) /* * MAC_CSR2: STA MAC register 0. */ #define MAC_CSR2 0x3008 #define MAC_CSR2_BYTE0 FIELD32(0x000000ff) #define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) #define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) #define MAC_CSR2_BYTE3 FIELD32(0xff000000) /* * MAC_CSR3: STA MAC register 1. * UNICAST_TO_ME_MASK: * Used to mask off bits from byte 5 of the MAC address * to determine the UNICAST_TO_ME bit for RX frames. * The full mask is complemented by BSS_ID_MASK: * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK */ #define MAC_CSR3 0x300c #define MAC_CSR3_BYTE4 FIELD32(0x000000ff) #define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) #define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) /* * MAC_CSR4: BSSID register 0. */ #define MAC_CSR4 0x3010 #define MAC_CSR4_BYTE0 FIELD32(0x000000ff) #define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) #define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) #define MAC_CSR4_BYTE3 FIELD32(0xff000000) /* * MAC_CSR5: BSSID register 1. * BSS_ID_MASK: * This mask is used to mask off bits 0 and 1 of byte 5 of the * BSSID. This will make sure that those bits will be ignored * when determining the MY_BSS of RX frames. * 0: 1-BSSID mode (BSS index = 0) * 1: 2-BSSID mode (BSS index: Byte5, bit 0) * 2: 2-BSSID mode (BSS index: byte5, bit 1) * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) */ #define MAC_CSR5 0x3014 #define MAC_CSR5_BYTE4 FIELD32(0x000000ff) #define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) #define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) /* * MAC_CSR6: Maximum frame length register. */ #define MAC_CSR6 0x3018 #define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x00000fff) /* * MAC_CSR7: Reserved */ #define MAC_CSR7 0x301c /* * MAC_CSR8: SIFS/EIFS register. * All units are in US. */ #define MAC_CSR8 0x3020 #define MAC_CSR8_SIFS FIELD32(0x000000ff) #define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) #define MAC_CSR8_EIFS FIELD32(0xffff0000) /* * MAC_CSR9: Back-Off control register. * SLOT_TIME: Slot time, default is 20us for 802.11BG. * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. */ #define MAC_CSR9 0x3024 #define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) #define MAC_CSR9_CWMIN FIELD32(0x00000f00) #define MAC_CSR9_CWMAX FIELD32(0x0000f000) #define MAC_CSR9_CW_SELECT FIELD32(0x00010000) /* * MAC_CSR10: Power state configuration. */ #define MAC_CSR10 0x3028 /* * MAC_CSR11: Power saving transition time register. * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. * WAKEUP_LATENCY: In unit of TU. */ #define MAC_CSR11 0x302c #define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) #define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) #define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) #define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) /* * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). * CURRENT_STATE: 0:sleep, 1:awake. * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. */ #define MAC_CSR12 0x3030 #define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) #define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) #define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) #define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) /* * MAC_CSR13: GPIO. * MAC_CSR13_VALx: GPIO value * MAC_CSR13_DIRx: GPIO direction: 0 = input; 1 = output */ #define MAC_CSR13 0x3034 #define MAC_CSR13_VAL0 FIELD32(0x00000001) #define MAC_CSR13_VAL1 FIELD32(0x00000002) #define MAC_CSR13_VAL2 FIELD32(0x00000004) #define MAC_CSR13_VAL3 FIELD32(0x00000008) #define MAC_CSR13_VAL4 FIELD32(0x00000010) #define MAC_CSR13_VAL5 FIELD32(0x00000020) #define MAC_CSR13_VAL6 FIELD32(0x00000040) #define MAC_CSR13_VAL7 FIELD32(0x00000080) #define MAC_CSR13_DIR0 FIELD32(0x00000100) #define MAC_CSR13_DIR1 FIELD32(0x00000200) #define MAC_CSR13_DIR2 FIELD32(0x00000400) #define MAC_CSR13_DIR3 FIELD32(0x00000800) #define MAC_CSR13_DIR4 FIELD32(0x00001000) #define MAC_CSR13_DIR5 FIELD32(0x00002000) #define MAC_CSR13_DIR6 FIELD32(0x00004000) #define MAC_CSR13_DIR7 FIELD32(0x00008000) /* * MAC_CSR14: LED control register. * ON_PERIOD: On period, default 70ms. * OFF_PERIOD: Off period, default 30ms. * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. * SW_LED: s/w LED, 1: ON, 0: OFF. * HW_LED_POLARITY: 0: active low, 1: active high. */ #define MAC_CSR14 0x3038 #define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) #define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) #define MAC_CSR14_HW_LED FIELD32(0x00010000) #define MAC_CSR14_SW_LED FIELD32(0x00020000) #define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) #define MAC_CSR14_SW_LED2 FIELD32(0x00080000) /* * MAC_CSR15: NAV control. */ #define MAC_CSR15 0x303c /* * TXRX control registers. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * TXRX_CSR0: TX/RX configuration register. * TSF_OFFSET: Default is 24. * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. * DISABLE_RX: Disable Rx engine. * DROP_CRC: Drop CRC error. * DROP_PHYSICAL: Drop physical error. * DROP_CONTROL: Drop control frame. * DROP_NOT_TO_ME: Drop not to me unicast frame. * DROP_TO_DS: Drop fram ToDs bit is true. * DROP_VERSION_ERROR: Drop version error frame. * DROP_MULTICAST: Drop multicast frames. * DROP_BORADCAST: Drop broadcast frames. * DROP_ACK_CTS: Drop received ACK and CTS. */ #define TXRX_CSR0 0x3040 #define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) #define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) #define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) #define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) #define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) #define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) #define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) #define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) #define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) #define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) #define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) #define TXRX_CSR0_DROP_BROADCAST FIELD32(0x01000000) #define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) #define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) /* * TXRX_CSR1 */ #define TXRX_CSR1 0x3044 #define TXRX_CSR1_BBP_ID0 FIELD32(0x0000007f) #define TXRX_CSR1_BBP_ID0_VALID FIELD32(0x00000080) #define TXRX_CSR1_BBP_ID1 FIELD32(0x00007f00) #define TXRX_CSR1_BBP_ID1_VALID FIELD32(0x00008000) #define TXRX_CSR1_BBP_ID2 FIELD32(0x007f0000) #define TXRX_CSR1_BBP_ID2_VALID FIELD32(0x00800000) #define TXRX_CSR1_BBP_ID3 FIELD32(0x7f000000) #define TXRX_CSR1_BBP_ID3_VALID FIELD32(0x80000000) /* * TXRX_CSR2 */ #define TXRX_CSR2 0x3048 #define TXRX_CSR2_BBP_ID0 FIELD32(0x0000007f) #define TXRX_CSR2_BBP_ID0_VALID FIELD32(0x00000080) #define TXRX_CSR2_BBP_ID1 FIELD32(0x00007f00) #define TXRX_CSR2_BBP_ID1_VALID FIELD32(0x00008000) #define TXRX_CSR2_BBP_ID2 FIELD32(0x007f0000) #define TXRX_CSR2_BBP_ID2_VALID FIELD32(0x00800000) #define TXRX_CSR2_BBP_ID3 FIELD32(0x7f000000) #define TXRX_CSR2_BBP_ID3_VALID FIELD32(0x80000000) /* * TXRX_CSR3 */ #define TXRX_CSR3 0x304c #define TXRX_CSR3_BBP_ID0 FIELD32(0x0000007f) #define TXRX_CSR3_BBP_ID0_VALID FIELD32(0x00000080) #define TXRX_CSR3_BBP_ID1 FIELD32(0x00007f00) #define TXRX_CSR3_BBP_ID1_VALID FIELD32(0x00008000) #define TXRX_CSR3_BBP_ID2 FIELD32(0x007f0000) #define TXRX_CSR3_BBP_ID2_VALID FIELD32(0x00800000) #define TXRX_CSR3_BBP_ID3 FIELD32(0x7f000000) #define TXRX_CSR3_BBP_ID3_VALID FIELD32(0x80000000) /* * TXRX_CSR4: Auto-Responder/Tx-retry register. * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. * OFDM_TX_RATE_DOWN: 1:enable. * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. */ #define TXRX_CSR4 0x3050 #define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) #define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) #define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) #define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) #define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) #define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) #define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) #define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) #define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) #define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) /* * TXRX_CSR5 */ #define TXRX_CSR5 0x3054 /* * TXRX_CSR6: ACK/CTS payload consumed time */ #define TXRX_CSR6 0x3058 /* * TXRX_CSR7: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. */ #define TXRX_CSR7 0x305c #define TXRX_CSR7_ACK_CTS_6MBS FIELD32(0x000000ff) #define TXRX_CSR7_ACK_CTS_9MBS FIELD32(0x0000ff00) #define TXRX_CSR7_ACK_CTS_12MBS FIELD32(0x00ff0000) #define TXRX_CSR7_ACK_CTS_18MBS FIELD32(0xff000000) /* * TXRX_CSR8: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. */ #define TXRX_CSR8 0x3060 #define TXRX_CSR8_ACK_CTS_24MBS FIELD32(0x000000ff) #define TXRX_CSR8_ACK_CTS_36MBS FIELD32(0x0000ff00) #define TXRX_CSR8_ACK_CTS_48MBS FIELD32(0x00ff0000) #define TXRX_CSR8_ACK_CTS_54MBS FIELD32(0xff000000) /* * TXRX_CSR9: Synchronization control register. * BEACON_INTERVAL: In unit of 1/16 TU. * TSF_TICKING: Enable TSF auto counting. * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. * BEACON_GEN: Enable beacon generator. */ #define TXRX_CSR9 0x3064 #define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) #define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) #define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) #define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) #define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) #define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) /* * TXRX_CSR10: BEACON alignment. */ #define TXRX_CSR10 0x3068 /* * TXRX_CSR11: AES mask. */ #define TXRX_CSR11 0x306c /* * TXRX_CSR12: TSF low 32. */ #define TXRX_CSR12 0x3070 #define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) /* * TXRX_CSR13: TSF high 32. */ #define TXRX_CSR13 0x3074 #define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) /* * TXRX_CSR14: TBTT timer. */ #define TXRX_CSR14 0x3078 /* * TXRX_CSR15: TKIP MIC priority byte "AND" mask. */ #define TXRX_CSR15 0x307c /* * PHY control registers. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * PHY_CSR0: RF/PS control. */ #define PHY_CSR0 0x3080 #define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) #define PHY_CSR0_PA_PE_A FIELD32(0x00020000) /* * PHY_CSR1 */ #define PHY_CSR1 0x3084 #define PHY_CSR1_RF_RPI FIELD32(0x00010000) /* * PHY_CSR2: Pre-TX BBP control. */ #define PHY_CSR2 0x3088 /* * PHY_CSR3: BBP serial control register. * VALUE: Register value to program into BBP. * REG_NUM: Selected BBP register. * READ_CONTROL: 0: Write BBP, 1: Read BBP. * BUSY: 1: ASIC is busy execute BBP programming. */ #define PHY_CSR3 0x308c #define PHY_CSR3_VALUE FIELD32(0x000000ff) #define PHY_CSR3_REGNUM FIELD32(0x00007f00) #define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) #define PHY_CSR3_BUSY FIELD32(0x00010000) /* * PHY_CSR4: RF serial control register * VALUE: Register value (include register id) serial out to RF/IF chip. * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). * IF_SELECT: 1: select IF to program, 0: select RF to program. * PLL_LD: RF PLL_LD status. * BUSY: 1: ASIC is busy execute RF programming. */ #define PHY_CSR4 0x3090 #define PHY_CSR4_VALUE FIELD32(0x00ffffff) #define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) #define PHY_CSR4_IF_SELECT FIELD32(0x20000000) #define PHY_CSR4_PLL_LD FIELD32(0x40000000) #define PHY_CSR4_BUSY FIELD32(0x80000000) /* * PHY_CSR5: RX to TX signal switch timing control. */ #define PHY_CSR5 0x3094 #define PHY_CSR5_IQ_FLIP FIELD32(0x00000004) /* * PHY_CSR6: TX to RX signal timing control. */ #define PHY_CSR6 0x3098 #define PHY_CSR6_IQ_FLIP FIELD32(0x00000004) /* * PHY_CSR7: TX DAC switching timing control. */ #define PHY_CSR7 0x309c /* * Security control register. */ /* * SEC_CSR0: Shared key table control. */ #define SEC_CSR0 0x30a0 #define SEC_CSR0_BSS0_KEY0_VALID FIELD32(0x00000001) #define SEC_CSR0_BSS0_KEY1_VALID FIELD32(0x00000002) #define SEC_CSR0_BSS0_KEY2_VALID FIELD32(0x00000004) #define SEC_CSR0_BSS0_KEY3_VALID FIELD32(0x00000008) #define SEC_CSR0_BSS1_KEY0_VALID FIELD32(0x00000010) #define SEC_CSR0_BSS1_KEY1_VALID FIELD32(0x00000020) #define SEC_CSR0_BSS1_KEY2_VALID FIELD32(0x00000040) #define SEC_CSR0_BSS1_KEY3_VALID FIELD32(0x00000080) #define SEC_CSR0_BSS2_KEY0_VALID FIELD32(0x00000100) #define SEC_CSR0_BSS2_KEY1_VALID FIELD32(0x00000200) #define SEC_CSR0_BSS2_KEY2_VALID FIELD32(0x00000400) #define SEC_CSR0_BSS2_KEY3_VALID FIELD32(0x00000800) #define SEC_CSR0_BSS3_KEY0_VALID FIELD32(0x00001000) #define SEC_CSR0_BSS3_KEY1_VALID FIELD32(0x00002000) #define SEC_CSR0_BSS3_KEY2_VALID FIELD32(0x00004000) #define SEC_CSR0_BSS3_KEY3_VALID FIELD32(0x00008000) /* * SEC_CSR1: Shared key table security mode register. */ #define SEC_CSR1 0x30a4 #define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) #define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) #define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) #define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) #define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) #define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) #define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) #define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) /* * Pairwise key table valid bitmap registers. * SEC_CSR2: pairwise key table valid bitmap 0. * SEC_CSR3: pairwise key table valid bitmap 1. */ #define SEC_CSR2 0x30a8 #define SEC_CSR3 0x30ac /* * SEC_CSR4: Pairwise key table lookup control. */ #define SEC_CSR4 0x30b0 #define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) #define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) #define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) #define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) /* * SEC_CSR5: shared key table security mode register. */ #define SEC_CSR5 0x30b4 #define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) #define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) #define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) #define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) #define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) #define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) #define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) #define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) /* * STA control registers. */ /* * STA_CSR0: RX PLCP error count & RX FCS error count. */ #define STA_CSR0 0x30c0 #define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) #define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) /* * STA_CSR1: RX False CCA count & RX LONG frame count. */ #define STA_CSR1 0x30c4 #define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) #define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) /* * STA_CSR2: TX Beacon count and RX FIFO overflow count. */ #define STA_CSR2 0x30c8 #define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) #define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) /* * STA_CSR3: TX Beacon count. */ #define STA_CSR3 0x30cc #define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) /* * STA_CSR4: TX Retry count. */ #define STA_CSR4 0x30d0 #define STA_CSR4_TX_NO_RETRY_COUNT FIELD32(0x0000ffff) #define STA_CSR4_TX_ONE_RETRY_COUNT FIELD32(0xffff0000) /* * STA_CSR5: TX Retry count. */ #define STA_CSR5 0x30d4 #define STA_CSR4_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) #define STA_CSR4_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) /* * QOS control registers. */ /* * QOS_CSR1: TXOP holder MAC address register. */ #define QOS_CSR1 0x30e4 #define QOS_CSR1_BYTE4 FIELD32(0x000000ff) #define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) /* * QOS_CSR2: TXOP holder timeout register. */ #define QOS_CSR2 0x30e8 /* * RX QOS-CFPOLL MAC address register. * QOS_CSR3: RX QOS-CFPOLL MAC address 0. * QOS_CSR4: RX QOS-CFPOLL MAC address 1. */ #define QOS_CSR3 0x30ec #define QOS_CSR4 0x30f0 /* * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. */ #define QOS_CSR5 0x30f4 /* * WMM Scheduler Register */ /* * AIFSN_CSR: AIFSN for each EDCA AC. * AIFSN0: For AC_VO. * AIFSN1: For AC_VI. * AIFSN2: For AC_BE. * AIFSN3: For AC_BK. */ #define AIFSN_CSR 0x0400 #define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) #define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) #define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) #define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) /* * CWMIN_CSR: CWmin for each EDCA AC. * CWMIN0: For AC_VO. * CWMIN1: For AC_VI. * CWMIN2: For AC_BE. * CWMIN3: For AC_BK. */ #define CWMIN_CSR 0x0404 #define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) #define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) #define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) #define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) /* * CWMAX_CSR: CWmax for each EDCA AC. * CWMAX0: For AC_VO. * CWMAX1: For AC_VI. * CWMAX2: For AC_BE. * CWMAX3: For AC_BK. */ #define CWMAX_CSR 0x0408 #define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) #define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) #define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) #define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) /* * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. * AC0_TX_OP: For AC_VO, in unit of 32us. * AC1_TX_OP: For AC_VI, in unit of 32us. */ #define AC_TXOP_CSR0 0x040c #define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) #define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) /* * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. * AC2_TX_OP: For AC_BE, in unit of 32us. * AC3_TX_OP: For AC_BK, in unit of 32us. */ #define AC_TXOP_CSR1 0x0410 #define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) #define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) /* * BBP registers. * The wordsize of the BBP is 8 bits. */ /* * R2 */ #define BBP_R2_BG_MODE FIELD8(0x20) /* * R3 */ #define BBP_R3_SMART_MODE FIELD8(0x01) /* * R4: RX antenna control * FRAME_END: 1 - DPDT, 0 - SPDT (Only valid for 802.11G, RF2527 & RF2529) */ /* * ANTENNA_CONTROL semantics (guessed): * 0x1: Software controlled antenna switching (fixed or SW diversity) * 0x2: Hardware diversity. */ #define BBP_R4_RX_ANTENNA_CONTROL FIELD8(0x03) #define BBP_R4_RX_FRAME_END FIELD8(0x20) /* * R77 */ #define BBP_R77_RX_ANTENNA FIELD8(0x03) /* * RF registers */ /* * RF 3 */ #define RF3_TXPOWER FIELD32(0x00003e00) /* * RF 4 */ #define RF4_FREQ_OFFSET FIELD32(0x0003f000) /* * EEPROM content. * The wordsize of the EEPROM is 16 bits. */ /* * HW MAC address. */ #define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) #define EEPROM_MAC_ADDR1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) #define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) /* * EEPROM antenna. * ANTENNA_NUM: Number of antennas. * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. * DYN_TXAGC: Dynamic TX AGC control. * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. * RF_TYPE: Rf_type of this adapter. */ #define EEPROM_ANTENNA 0x0010 #define EEPROM_ANTENNA_NUM FIELD16(0x0003) #define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) #define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) #define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) #define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) #define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) #define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) /* * EEPROM NIC config. * EXTERNAL_LNA: External LNA. */ #define EEPROM_NIC 0x0011 #define EEPROM_NIC_EXTERNAL_LNA FIELD16(0x0010) /* * EEPROM geography. * GEO_A: Default geographical setting for 5GHz band * GEO: Default geographical setting. */ #define EEPROM_GEOGRAPHY 0x0012 #define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) #define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) /* * EEPROM BBP. */ #define EEPROM_BBP_START 0x0013 #define EEPROM_BBP_SIZE 16 #define EEPROM_BBP_VALUE FIELD16(0x00ff) #define EEPROM_BBP_REG_ID FIELD16(0xff00) /* * EEPROM TXPOWER 802.11G */ #define EEPROM_TXPOWER_G_START 0x0023 #define EEPROM_TXPOWER_G_SIZE 7 #define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_G_2 FIELD16(0xff00) /* * EEPROM Frequency */ #define EEPROM_FREQ 0x002f #define EEPROM_FREQ_OFFSET FIELD16(0x00ff) #define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) #define EEPROM_FREQ_SEQ FIELD16(0x0300) /* * EEPROM LED. * POLARITY_RDY_G: Polarity RDY_G setting. * POLARITY_RDY_A: Polarity RDY_A setting. * POLARITY_ACT: Polarity ACT setting. * POLARITY_GPIO_0: Polarity GPIO0 setting. * POLARITY_GPIO_1: Polarity GPIO1 setting. * POLARITY_GPIO_2: Polarity GPIO2 setting. * POLARITY_GPIO_3: Polarity GPIO3 setting. * POLARITY_GPIO_4: Polarity GPIO4 setting. * LED_MODE: Led mode. */ #define EEPROM_LED 0x0030 #define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) #define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) #define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) #define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) #define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) #define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) #define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) #define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) #define EEPROM_LED_LED_MODE FIELD16(0x1f00) /* * EEPROM TXPOWER 802.11A */ #define EEPROM_TXPOWER_A_START 0x0031 #define EEPROM_TXPOWER_A_SIZE 12 #define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_A_2 FIELD16(0xff00) /* * EEPROM RSSI offset 802.11BG */ #define EEPROM_RSSI_OFFSET_BG 0x004d #define EEPROM_RSSI_OFFSET_BG_1 FIELD16(0x00ff) #define EEPROM_RSSI_OFFSET_BG_2 FIELD16(0xff00) /* * EEPROM RSSI offset 802.11A */ #define EEPROM_RSSI_OFFSET_A 0x004e #define EEPROM_RSSI_OFFSET_A_1 FIELD16(0x00ff) #define EEPROM_RSSI_OFFSET_A_2 FIELD16(0xff00) /* * DMA descriptor defines. */ #define TXD_DESC_SIZE ( 6 * sizeof(__le32) ) #define TXINFO_SIZE ( 6 * sizeof(__le32) ) #define RXD_DESC_SIZE ( 6 * sizeof(__le32) ) /* * TX descriptor format for TX, PRIO and Beacon Ring. */ /* * Word0 * BURST: Next frame belongs to same "burst" event. * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. * KEY_TABLE: Use per-client pairwise KEY table. * KEY_INDEX: * Key index (0~31) to the pairwise KEY table. * 0~3 to shared KEY table 0 (BSS0). * 4~7 to shared KEY table 1 (BSS1). * 8~11 to shared KEY table 2 (BSS2). * 12~15 to shared KEY table 3 (BSS3). * BURST2: For backward compatibility, set to same value as BURST. */ #define TXD_W0_BURST FIELD32(0x00000001) #define TXD_W0_VALID FIELD32(0x00000002) #define TXD_W0_MORE_FRAG FIELD32(0x00000004) #define TXD_W0_ACK FIELD32(0x00000008) #define TXD_W0_TIMESTAMP FIELD32(0x00000010) #define TXD_W0_OFDM FIELD32(0x00000020) #define TXD_W0_IFS FIELD32(0x00000040) #define TXD_W0_RETRY_MODE FIELD32(0x00000080) #define TXD_W0_TKIP_MIC FIELD32(0x00000100) #define TXD_W0_KEY_TABLE FIELD32(0x00000200) #define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) #define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) #define TXD_W0_BURST2 FIELD32(0x10000000) #define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) /* * Word1 * HOST_Q_ID: EDCA/HCCA queue ID. * HW_SEQUENCE: MAC overwrites the frame sequence number. * BUFFER_COUNT: Number of buffers in this TXD. */ #define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) #define TXD_W1_AIFSN FIELD32(0x000000f0) #define TXD_W1_CWMIN FIELD32(0x00000f00) #define TXD_W1_CWMAX FIELD32(0x0000f000) #define TXD_W1_IV_OFFSET FIELD32(0x003f0000) #define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) #define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) /* * Word2: PLCP information */ #define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) #define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) #define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) #define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) /* * Word3 */ #define TXD_W3_IV FIELD32(0xffffffff) /* * Word4 */ #define TXD_W4_EIV FIELD32(0xffffffff) /* * Word5 * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). * PACKET_ID: Driver assigned packet ID to categorize TXResult in interrupt. * WAITING_DMA_DONE_INT: TXD been filled with data * and waiting for TxDoneISR housekeeping. */ #define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) #define TXD_W5_PACKET_ID FIELD32(0x0000ff00) #define TXD_W5_TX_POWER FIELD32(0x00ff0000) #define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) /* * RX descriptor format for RX Ring. */ /* * Word0 * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. * KEY_INDEX: Decryption key actually used. */ #define RXD_W0_OWNER_NIC FIELD32(0x00000001) #define RXD_W0_DROP FIELD32(0x00000002) #define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) #define RXD_W0_MULTICAST FIELD32(0x00000008) #define RXD_W0_BROADCAST FIELD32(0x00000010) #define RXD_W0_MY_BSS FIELD32(0x00000020) #define RXD_W0_CRC_ERROR FIELD32(0x00000040) #define RXD_W0_OFDM FIELD32(0x00000080) #define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) #define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) #define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) #define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) /* * WORD1 * SIGNAL: RX raw data rate reported by BBP. * RSSI: RSSI reported by BBP. */ #define RXD_W1_SIGNAL FIELD32(0x000000ff) #define RXD_W1_RSSI_AGC FIELD32(0x00001f00) #define RXD_W1_RSSI_LNA FIELD32(0x00006000) #define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) /* * Word2 * IV: Received IV of originally encrypted. */ #define RXD_W2_IV FIELD32(0xffffffff) /* * Word3 * EIV: Received EIV of originally encrypted. */ #define RXD_W3_EIV FIELD32(0xffffffff) /* * Word4 * ICV: Received ICV of originally encrypted. * NOTE: This is a guess, the official definition is "reserved" */ #define RXD_W4_ICV FIELD32(0xffffffff) /* * the above 20-byte is called RXINFO and will be DMAed to MAC RX block * and passed to the HOST driver. * The following fields are for DMA block and HOST usage only. * Can't be touched by ASIC MAC block. */ /* * Word5 */ #define RXD_W5_RESERVED FIELD32(0xffffffff) /* * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 #define TXPOWER_FROM_DEV(__txpower) \ (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT73USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt61pci.h0000644000175000017500000012373612026211315023175 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt61pci Abstract: Data structures and registers for the rt61pci module. Supported chipsets: RT2561, RT2561s, RT2661. */ #ifndef RT61PCI_H #define RT61PCI_H /* * RT chip PCI IDs. */ #define RT2561s_PCI_ID 0x0301 #define RT2561_PCI_ID 0x0302 #define RT2661_PCI_ID 0x0401 /* * RF chip defines. */ #define RF5225 0x0001 #define RF5325 0x0002 #define RF2527 0x0003 #define RF2529 0x0004 /* * Signal information. * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 120 /* * Register layout information. */ #define CSR_REG_BASE 0x3000 #define CSR_REG_SIZE 0x04b0 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0100 #define BBP_BASE 0x0000 #define BBP_SIZE 0x0080 #define RF_BASE 0x0004 #define RF_SIZE 0x0010 /* * Number of TX queues. */ #define NUM_TX_QUEUES 4 /* * PCI registers. */ /* * HOST_CMD_CSR: For HOST to interrupt embedded processor */ #define HOST_CMD_CSR 0x0008 #define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x0000007f) #define HOST_CMD_CSR_INTERRUPT_MCU FIELD32(0x00000080) /* * MCU_CNTL_CSR * SELECT_BANK: Select 8051 program bank. * RESET: Enable 8051 reset state. * READY: Ready state for 8051. */ #define MCU_CNTL_CSR 0x000c #define MCU_CNTL_CSR_SELECT_BANK FIELD32(0x00000001) #define MCU_CNTL_CSR_RESET FIELD32(0x00000002) #define MCU_CNTL_CSR_READY FIELD32(0x00000004) /* * SOFT_RESET_CSR * FORCE_CLOCK_ON: Host force MAC clock ON */ #define SOFT_RESET_CSR 0x0010 #define SOFT_RESET_CSR_FORCE_CLOCK_ON FIELD32(0x00000002) /* * MCU_INT_SOURCE_CSR: MCU interrupt source/mask register. */ #define MCU_INT_SOURCE_CSR 0x0014 #define MCU_INT_SOURCE_CSR_0 FIELD32(0x00000001) #define MCU_INT_SOURCE_CSR_1 FIELD32(0x00000002) #define MCU_INT_SOURCE_CSR_2 FIELD32(0x00000004) #define MCU_INT_SOURCE_CSR_3 FIELD32(0x00000008) #define MCU_INT_SOURCE_CSR_4 FIELD32(0x00000010) #define MCU_INT_SOURCE_CSR_5 FIELD32(0x00000020) #define MCU_INT_SOURCE_CSR_6 FIELD32(0x00000040) #define MCU_INT_SOURCE_CSR_7 FIELD32(0x00000080) #define MCU_INT_SOURCE_CSR_TWAKEUP FIELD32(0x00000100) #define MCU_INT_SOURCE_CSR_TBTT_EXPIRE FIELD32(0x00000200) /* * MCU_INT_MASK_CSR: MCU interrupt source/mask register. */ #define MCU_INT_MASK_CSR 0x0018 #define MCU_INT_MASK_CSR_0 FIELD32(0x00000001) #define MCU_INT_MASK_CSR_1 FIELD32(0x00000002) #define MCU_INT_MASK_CSR_2 FIELD32(0x00000004) #define MCU_INT_MASK_CSR_3 FIELD32(0x00000008) #define MCU_INT_MASK_CSR_4 FIELD32(0x00000010) #define MCU_INT_MASK_CSR_5 FIELD32(0x00000020) #define MCU_INT_MASK_CSR_6 FIELD32(0x00000040) #define MCU_INT_MASK_CSR_7 FIELD32(0x00000080) #define MCU_INT_MASK_CSR_TWAKEUP FIELD32(0x00000100) #define MCU_INT_MASK_CSR_TBTT_EXPIRE FIELD32(0x00000200) /* * PCI_USEC_CSR */ #define PCI_USEC_CSR 0x001c /* * Security key table memory. * 16 entries 32-byte for shared key table * 64 entries 32-byte for pairwise key table * 64 entries 8-byte for pairwise ta key table */ #define SHARED_KEY_TABLE_BASE 0x1000 #define PAIRWISE_KEY_TABLE_BASE 0x1200 #define PAIRWISE_TA_TABLE_BASE 0x1a00 #define SHARED_KEY_ENTRY(__idx) \ ( SHARED_KEY_TABLE_BASE + \ ((__idx) * sizeof(struct hw_key_entry)) ) #define PAIRWISE_KEY_ENTRY(__idx) \ ( PAIRWISE_KEY_TABLE_BASE + \ ((__idx) * sizeof(struct hw_key_entry)) ) #define PAIRWISE_TA_ENTRY(__idx) \ ( PAIRWISE_TA_TABLE_BASE + \ ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) struct hw_key_entry { u8 key[16]; u8 tx_mic[8]; u8 rx_mic[8]; } __packed; struct hw_pairwise_ta_entry { u8 address[6]; u8 cipher; u8 reserved; } __packed; /* * Other on-chip shared memory space. */ #define HW_CIS_BASE 0x2000 #define HW_NULL_BASE 0x2b00 /* * Since NULL frame won't be that long (256 byte), * We steal 16 tail bytes to save debugging settings. */ #define HW_DEBUG_SETTING_BASE 0x2bf0 /* * On-chip BEACON frame space. */ #define HW_BEACON_BASE0 0x2c00 #define HW_BEACON_BASE1 0x2d00 #define HW_BEACON_BASE2 0x2e00 #define HW_BEACON_BASE3 0x2f00 #define HW_BEACON_OFFSET(__index) \ ( HW_BEACON_BASE0 + (__index * 0x0100) ) /* * HOST-MCU shared memory. */ /* * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. */ #define H2M_MAILBOX_CSR 0x2100 #define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) #define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) #define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) #define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) /* * MCU_LEDCS: LED control for MCU Mailbox. */ #define MCU_LEDCS_LED_MODE FIELD16(0x001f) #define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) #define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) #define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) #define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) #define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) #define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) #define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) #define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) #define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) #define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) #define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) /* * M2H_CMD_DONE_CSR. */ #define M2H_CMD_DONE_CSR 0x2104 /* * MCU_TXOP_ARRAY_BASE. */ #define MCU_TXOP_ARRAY_BASE 0x2110 /* * MAC Control/Status Registers(CSR). * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * MAC_CSR0: ASIC revision number. */ #define MAC_CSR0 0x3000 #define MAC_CSR0_REVISION FIELD32(0x0000000f) #define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) /* * MAC_CSR1: System control register. * SOFT_RESET: Software reset bit, 1: reset, 0: normal. * BBP_RESET: Hardware reset BBP. * HOST_READY: Host is ready after initialization, 1: ready. */ #define MAC_CSR1 0x3004 #define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) #define MAC_CSR1_BBP_RESET FIELD32(0x00000002) #define MAC_CSR1_HOST_READY FIELD32(0x00000004) /* * MAC_CSR2: STA MAC register 0. */ #define MAC_CSR2 0x3008 #define MAC_CSR2_BYTE0 FIELD32(0x000000ff) #define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) #define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) #define MAC_CSR2_BYTE3 FIELD32(0xff000000) /* * MAC_CSR3: STA MAC register 1. * UNICAST_TO_ME_MASK: * Used to mask off bits from byte 5 of the MAC address * to determine the UNICAST_TO_ME bit for RX frames. * The full mask is complemented by BSS_ID_MASK: * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK */ #define MAC_CSR3 0x300c #define MAC_CSR3_BYTE4 FIELD32(0x000000ff) #define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) #define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) /* * MAC_CSR4: BSSID register 0. */ #define MAC_CSR4 0x3010 #define MAC_CSR4_BYTE0 FIELD32(0x000000ff) #define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) #define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) #define MAC_CSR4_BYTE3 FIELD32(0xff000000) /* * MAC_CSR5: BSSID register 1. * BSS_ID_MASK: * This mask is used to mask off bits 0 and 1 of byte 5 of the * BSSID. This will make sure that those bits will be ignored * when determining the MY_BSS of RX frames. * 0: 1-BSSID mode (BSS index = 0) * 1: 2-BSSID mode (BSS index: Byte5, bit 0) * 2: 2-BSSID mode (BSS index: byte5, bit 1) * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) */ #define MAC_CSR5 0x3014 #define MAC_CSR5_BYTE4 FIELD32(0x000000ff) #define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) #define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) /* * MAC_CSR6: Maximum frame length register. */ #define MAC_CSR6 0x3018 #define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x00000fff) /* * MAC_CSR7: Reserved */ #define MAC_CSR7 0x301c /* * MAC_CSR8: SIFS/EIFS register. * All units are in US. */ #define MAC_CSR8 0x3020 #define MAC_CSR8_SIFS FIELD32(0x000000ff) #define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) #define MAC_CSR8_EIFS FIELD32(0xffff0000) /* * MAC_CSR9: Back-Off control register. * SLOT_TIME: Slot time, default is 20us for 802.11BG. * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. */ #define MAC_CSR9 0x3024 #define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) #define MAC_CSR9_CWMIN FIELD32(0x00000f00) #define MAC_CSR9_CWMAX FIELD32(0x0000f000) #define MAC_CSR9_CW_SELECT FIELD32(0x00010000) /* * MAC_CSR10: Power state configuration. */ #define MAC_CSR10 0x3028 /* * MAC_CSR11: Power saving transition time register. * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. * WAKEUP_LATENCY: In unit of TU. */ #define MAC_CSR11 0x302c #define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) #define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) #define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) #define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) /* * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). * CURRENT_STATE: 0:sleep, 1:awake. * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. */ #define MAC_CSR12 0x3030 #define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) #define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) #define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) #define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) /* * MAC_CSR13: GPIO. * MAC_CSR13_VALx: GPIO value * MAC_CSR13_DIRx: GPIO direction: 0 = output; 1 = input */ #define MAC_CSR13 0x3034 #define MAC_CSR13_VAL0 FIELD32(0x00000001) #define MAC_CSR13_VAL1 FIELD32(0x00000002) #define MAC_CSR13_VAL2 FIELD32(0x00000004) #define MAC_CSR13_VAL3 FIELD32(0x00000008) #define MAC_CSR13_VAL4 FIELD32(0x00000010) #define MAC_CSR13_VAL5 FIELD32(0x00000020) #define MAC_CSR13_DIR0 FIELD32(0x00000100) #define MAC_CSR13_DIR1 FIELD32(0x00000200) #define MAC_CSR13_DIR2 FIELD32(0x00000400) #define MAC_CSR13_DIR3 FIELD32(0x00000800) #define MAC_CSR13_DIR4 FIELD32(0x00001000) #define MAC_CSR13_DIR5 FIELD32(0x00002000) /* * MAC_CSR14: LED control register. * ON_PERIOD: On period, default 70ms. * OFF_PERIOD: Off period, default 30ms. * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. * SW_LED: s/w LED, 1: ON, 0: OFF. * HW_LED_POLARITY: 0: active low, 1: active high. */ #define MAC_CSR14 0x3038 #define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) #define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) #define MAC_CSR14_HW_LED FIELD32(0x00010000) #define MAC_CSR14_SW_LED FIELD32(0x00020000) #define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) #define MAC_CSR14_SW_LED2 FIELD32(0x00080000) /* * MAC_CSR15: NAV control. */ #define MAC_CSR15 0x303c /* * TXRX control registers. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * TXRX_CSR0: TX/RX configuration register. * TSF_OFFSET: Default is 24. * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. * DISABLE_RX: Disable Rx engine. * DROP_CRC: Drop CRC error. * DROP_PHYSICAL: Drop physical error. * DROP_CONTROL: Drop control frame. * DROP_NOT_TO_ME: Drop not to me unicast frame. * DROP_TO_DS: Drop fram ToDs bit is true. * DROP_VERSION_ERROR: Drop version error frame. * DROP_MULTICAST: Drop multicast frames. * DROP_BORADCAST: Drop broadcast frames. * DROP_ACK_CTS: Drop received ACK and CTS. */ #define TXRX_CSR0 0x3040 #define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) #define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) #define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) #define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) #define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) #define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) #define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) #define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) #define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) #define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) #define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) #define TXRX_CSR0_DROP_BROADCAST FIELD32(0x01000000) #define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) #define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) /* * TXRX_CSR1 */ #define TXRX_CSR1 0x3044 #define TXRX_CSR1_BBP_ID0 FIELD32(0x0000007f) #define TXRX_CSR1_BBP_ID0_VALID FIELD32(0x00000080) #define TXRX_CSR1_BBP_ID1 FIELD32(0x00007f00) #define TXRX_CSR1_BBP_ID1_VALID FIELD32(0x00008000) #define TXRX_CSR1_BBP_ID2 FIELD32(0x007f0000) #define TXRX_CSR1_BBP_ID2_VALID FIELD32(0x00800000) #define TXRX_CSR1_BBP_ID3 FIELD32(0x7f000000) #define TXRX_CSR1_BBP_ID3_VALID FIELD32(0x80000000) /* * TXRX_CSR2 */ #define TXRX_CSR2 0x3048 #define TXRX_CSR2_BBP_ID0 FIELD32(0x0000007f) #define TXRX_CSR2_BBP_ID0_VALID FIELD32(0x00000080) #define TXRX_CSR2_BBP_ID1 FIELD32(0x00007f00) #define TXRX_CSR2_BBP_ID1_VALID FIELD32(0x00008000) #define TXRX_CSR2_BBP_ID2 FIELD32(0x007f0000) #define TXRX_CSR2_BBP_ID2_VALID FIELD32(0x00800000) #define TXRX_CSR2_BBP_ID3 FIELD32(0x7f000000) #define TXRX_CSR2_BBP_ID3_VALID FIELD32(0x80000000) /* * TXRX_CSR3 */ #define TXRX_CSR3 0x304c #define TXRX_CSR3_BBP_ID0 FIELD32(0x0000007f) #define TXRX_CSR3_BBP_ID0_VALID FIELD32(0x00000080) #define TXRX_CSR3_BBP_ID1 FIELD32(0x00007f00) #define TXRX_CSR3_BBP_ID1_VALID FIELD32(0x00008000) #define TXRX_CSR3_BBP_ID2 FIELD32(0x007f0000) #define TXRX_CSR3_BBP_ID2_VALID FIELD32(0x00800000) #define TXRX_CSR3_BBP_ID3 FIELD32(0x7f000000) #define TXRX_CSR3_BBP_ID3_VALID FIELD32(0x80000000) /* * TXRX_CSR4: Auto-Responder/Tx-retry register. * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. * OFDM_TX_RATE_DOWN: 1:enable. * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. */ #define TXRX_CSR4 0x3050 #define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) #define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) #define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) #define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) #define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) #define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) #define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) #define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) #define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) #define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) /* * TXRX_CSR5 */ #define TXRX_CSR5 0x3054 /* * TXRX_CSR6: ACK/CTS payload consumed time */ #define TXRX_CSR6 0x3058 /* * TXRX_CSR7: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. */ #define TXRX_CSR7 0x305c #define TXRX_CSR7_ACK_CTS_6MBS FIELD32(0x000000ff) #define TXRX_CSR7_ACK_CTS_9MBS FIELD32(0x0000ff00) #define TXRX_CSR7_ACK_CTS_12MBS FIELD32(0x00ff0000) #define TXRX_CSR7_ACK_CTS_18MBS FIELD32(0xff000000) /* * TXRX_CSR8: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. */ #define TXRX_CSR8 0x3060 #define TXRX_CSR8_ACK_CTS_24MBS FIELD32(0x000000ff) #define TXRX_CSR8_ACK_CTS_36MBS FIELD32(0x0000ff00) #define TXRX_CSR8_ACK_CTS_48MBS FIELD32(0x00ff0000) #define TXRX_CSR8_ACK_CTS_54MBS FIELD32(0xff000000) /* * TXRX_CSR9: Synchronization control register. * BEACON_INTERVAL: In unit of 1/16 TU. * TSF_TICKING: Enable TSF auto counting. * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. * BEACON_GEN: Enable beacon generator. */ #define TXRX_CSR9 0x3064 #define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) #define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) #define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) #define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) #define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) #define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) /* * TXRX_CSR10: BEACON alignment. */ #define TXRX_CSR10 0x3068 /* * TXRX_CSR11: AES mask. */ #define TXRX_CSR11 0x306c /* * TXRX_CSR12: TSF low 32. */ #define TXRX_CSR12 0x3070 #define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) /* * TXRX_CSR13: TSF high 32. */ #define TXRX_CSR13 0x3074 #define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) /* * TXRX_CSR14: TBTT timer. */ #define TXRX_CSR14 0x3078 /* * TXRX_CSR15: TKIP MIC priority byte "AND" mask. */ #define TXRX_CSR15 0x307c /* * PHY control registers. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * PHY_CSR0: RF/PS control. */ #define PHY_CSR0 0x3080 #define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) #define PHY_CSR0_PA_PE_A FIELD32(0x00020000) /* * PHY_CSR1 */ #define PHY_CSR1 0x3084 /* * PHY_CSR2: Pre-TX BBP control. */ #define PHY_CSR2 0x3088 /* * PHY_CSR3: BBP serial control register. * VALUE: Register value to program into BBP. * REG_NUM: Selected BBP register. * READ_CONTROL: 0: Write BBP, 1: Read BBP. * BUSY: 1: ASIC is busy execute BBP programming. */ #define PHY_CSR3 0x308c #define PHY_CSR3_VALUE FIELD32(0x000000ff) #define PHY_CSR3_REGNUM FIELD32(0x00007f00) #define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) #define PHY_CSR3_BUSY FIELD32(0x00010000) /* * PHY_CSR4: RF serial control register * VALUE: Register value (include register id) serial out to RF/IF chip. * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). * IF_SELECT: 1: select IF to program, 0: select RF to program. * PLL_LD: RF PLL_LD status. * BUSY: 1: ASIC is busy execute RF programming. */ #define PHY_CSR4 0x3090 #define PHY_CSR4_VALUE FIELD32(0x00ffffff) #define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) #define PHY_CSR4_IF_SELECT FIELD32(0x20000000) #define PHY_CSR4_PLL_LD FIELD32(0x40000000) #define PHY_CSR4_BUSY FIELD32(0x80000000) /* * PHY_CSR5: RX to TX signal switch timing control. */ #define PHY_CSR5 0x3094 #define PHY_CSR5_IQ_FLIP FIELD32(0x00000004) /* * PHY_CSR6: TX to RX signal timing control. */ #define PHY_CSR6 0x3098 #define PHY_CSR6_IQ_FLIP FIELD32(0x00000004) /* * PHY_CSR7: TX DAC switching timing control. */ #define PHY_CSR7 0x309c /* * Security control register. */ /* * SEC_CSR0: Shared key table control. */ #define SEC_CSR0 0x30a0 #define SEC_CSR0_BSS0_KEY0_VALID FIELD32(0x00000001) #define SEC_CSR0_BSS0_KEY1_VALID FIELD32(0x00000002) #define SEC_CSR0_BSS0_KEY2_VALID FIELD32(0x00000004) #define SEC_CSR0_BSS0_KEY3_VALID FIELD32(0x00000008) #define SEC_CSR0_BSS1_KEY0_VALID FIELD32(0x00000010) #define SEC_CSR0_BSS1_KEY1_VALID FIELD32(0x00000020) #define SEC_CSR0_BSS1_KEY2_VALID FIELD32(0x00000040) #define SEC_CSR0_BSS1_KEY3_VALID FIELD32(0x00000080) #define SEC_CSR0_BSS2_KEY0_VALID FIELD32(0x00000100) #define SEC_CSR0_BSS2_KEY1_VALID FIELD32(0x00000200) #define SEC_CSR0_BSS2_KEY2_VALID FIELD32(0x00000400) #define SEC_CSR0_BSS2_KEY3_VALID FIELD32(0x00000800) #define SEC_CSR0_BSS3_KEY0_VALID FIELD32(0x00001000) #define SEC_CSR0_BSS3_KEY1_VALID FIELD32(0x00002000) #define SEC_CSR0_BSS3_KEY2_VALID FIELD32(0x00004000) #define SEC_CSR0_BSS3_KEY3_VALID FIELD32(0x00008000) /* * SEC_CSR1: Shared key table security mode register. */ #define SEC_CSR1 0x30a4 #define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) #define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) #define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) #define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) #define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) #define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) #define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) #define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) /* * Pairwise key table valid bitmap registers. * SEC_CSR2: pairwise key table valid bitmap 0. * SEC_CSR3: pairwise key table valid bitmap 1. */ #define SEC_CSR2 0x30a8 #define SEC_CSR3 0x30ac /* * SEC_CSR4: Pairwise key table lookup control. */ #define SEC_CSR4 0x30b0 #define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) #define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) #define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) #define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) /* * SEC_CSR5: shared key table security mode register. */ #define SEC_CSR5 0x30b4 #define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) #define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) #define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) #define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) #define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) #define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) #define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) #define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) /* * STA control registers. */ /* * STA_CSR0: RX PLCP error count & RX FCS error count. */ #define STA_CSR0 0x30c0 #define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) #define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) /* * STA_CSR1: RX False CCA count & RX LONG frame count. */ #define STA_CSR1 0x30c4 #define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) #define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) /* * STA_CSR2: TX Beacon count and RX FIFO overflow count. */ #define STA_CSR2 0x30c8 #define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) #define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) /* * STA_CSR3: TX Beacon count. */ #define STA_CSR3 0x30cc #define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) /* * STA_CSR4: TX Result status register. * VALID: 1:This register contains a valid TX result. */ #define STA_CSR4 0x30d0 #define STA_CSR4_VALID FIELD32(0x00000001) #define STA_CSR4_TX_RESULT FIELD32(0x0000000e) #define STA_CSR4_RETRY_COUNT FIELD32(0x000000f0) #define STA_CSR4_PID_SUBTYPE FIELD32(0x00001f00) #define STA_CSR4_PID_TYPE FIELD32(0x0000e000) #define STA_CSR4_TXRATE FIELD32(0x000f0000) /* * QOS control registers. */ /* * QOS_CSR0: TXOP holder MAC address register. */ #define QOS_CSR0 0x30e0 #define QOS_CSR0_BYTE0 FIELD32(0x000000ff) #define QOS_CSR0_BYTE1 FIELD32(0x0000ff00) #define QOS_CSR0_BYTE2 FIELD32(0x00ff0000) #define QOS_CSR0_BYTE3 FIELD32(0xff000000) /* * QOS_CSR1: TXOP holder MAC address register. */ #define QOS_CSR1 0x30e4 #define QOS_CSR1_BYTE4 FIELD32(0x000000ff) #define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) /* * QOS_CSR2: TXOP holder timeout register. */ #define QOS_CSR2 0x30e8 /* * RX QOS-CFPOLL MAC address register. * QOS_CSR3: RX QOS-CFPOLL MAC address 0. * QOS_CSR4: RX QOS-CFPOLL MAC address 1. */ #define QOS_CSR3 0x30ec #define QOS_CSR4 0x30f0 /* * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. */ #define QOS_CSR5 0x30f4 /* * Host DMA registers. */ /* * AC0_BASE_CSR: AC_VO base address. */ #define AC0_BASE_CSR 0x3400 #define AC0_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* * AC1_BASE_CSR: AC_VI base address. */ #define AC1_BASE_CSR 0x3404 #define AC1_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* * AC2_BASE_CSR: AC_BE base address. */ #define AC2_BASE_CSR 0x3408 #define AC2_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* * AC3_BASE_CSR: AC_BK base address. */ #define AC3_BASE_CSR 0x340c #define AC3_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* * MGMT_BASE_CSR: MGMT ring base address. */ #define MGMT_BASE_CSR 0x3410 #define MGMT_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* * TX_RING_CSR0: TX Ring size for AC_VO, AC_VI, AC_BE, AC_BK. */ #define TX_RING_CSR0 0x3418 #define TX_RING_CSR0_AC0_RING_SIZE FIELD32(0x000000ff) #define TX_RING_CSR0_AC1_RING_SIZE FIELD32(0x0000ff00) #define TX_RING_CSR0_AC2_RING_SIZE FIELD32(0x00ff0000) #define TX_RING_CSR0_AC3_RING_SIZE FIELD32(0xff000000) /* * TX_RING_CSR1: TX Ring size for MGMT Ring, HCCA Ring * TXD_SIZE: In unit of 32-bit. */ #define TX_RING_CSR1 0x341c #define TX_RING_CSR1_MGMT_RING_SIZE FIELD32(0x000000ff) #define TX_RING_CSR1_HCCA_RING_SIZE FIELD32(0x0000ff00) #define TX_RING_CSR1_TXD_SIZE FIELD32(0x003f0000) /* * AIFSN_CSR: AIFSN for each EDCA AC. * AIFSN0: For AC_VO. * AIFSN1: For AC_VI. * AIFSN2: For AC_BE. * AIFSN3: For AC_BK. */ #define AIFSN_CSR 0x3420 #define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) #define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) #define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) #define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) /* * CWMIN_CSR: CWmin for each EDCA AC. * CWMIN0: For AC_VO. * CWMIN1: For AC_VI. * CWMIN2: For AC_BE. * CWMIN3: For AC_BK. */ #define CWMIN_CSR 0x3424 #define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) #define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) #define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) #define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) /* * CWMAX_CSR: CWmax for each EDCA AC. * CWMAX0: For AC_VO. * CWMAX1: For AC_VI. * CWMAX2: For AC_BE. * CWMAX3: For AC_BK. */ #define CWMAX_CSR 0x3428 #define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) #define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) #define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) #define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) /* * TX_DMA_DST_CSR: TX DMA destination * 0: TX ring0, 1: TX ring1, 2: TX ring2 3: invalid */ #define TX_DMA_DST_CSR 0x342c #define TX_DMA_DST_CSR_DEST_AC0 FIELD32(0x00000003) #define TX_DMA_DST_CSR_DEST_AC1 FIELD32(0x0000000c) #define TX_DMA_DST_CSR_DEST_AC2 FIELD32(0x00000030) #define TX_DMA_DST_CSR_DEST_AC3 FIELD32(0x000000c0) #define TX_DMA_DST_CSR_DEST_MGMT FIELD32(0x00000300) /* * TX_CNTL_CSR: KICK/Abort TX. * KICK_TX_AC0: For AC_VO. * KICK_TX_AC1: For AC_VI. * KICK_TX_AC2: For AC_BE. * KICK_TX_AC3: For AC_BK. * ABORT_TX_AC0: For AC_VO. * ABORT_TX_AC1: For AC_VI. * ABORT_TX_AC2: For AC_BE. * ABORT_TX_AC3: For AC_BK. */ #define TX_CNTL_CSR 0x3430 #define TX_CNTL_CSR_KICK_TX_AC0 FIELD32(0x00000001) #define TX_CNTL_CSR_KICK_TX_AC1 FIELD32(0x00000002) #define TX_CNTL_CSR_KICK_TX_AC2 FIELD32(0x00000004) #define TX_CNTL_CSR_KICK_TX_AC3 FIELD32(0x00000008) #define TX_CNTL_CSR_KICK_TX_MGMT FIELD32(0x00000010) #define TX_CNTL_CSR_ABORT_TX_AC0 FIELD32(0x00010000) #define TX_CNTL_CSR_ABORT_TX_AC1 FIELD32(0x00020000) #define TX_CNTL_CSR_ABORT_TX_AC2 FIELD32(0x00040000) #define TX_CNTL_CSR_ABORT_TX_AC3 FIELD32(0x00080000) #define TX_CNTL_CSR_ABORT_TX_MGMT FIELD32(0x00100000) /* * LOAD_TX_RING_CSR: Load RX desriptor */ #define LOAD_TX_RING_CSR 0x3434 #define LOAD_TX_RING_CSR_LOAD_TXD_AC0 FIELD32(0x00000001) #define LOAD_TX_RING_CSR_LOAD_TXD_AC1 FIELD32(0x00000002) #define LOAD_TX_RING_CSR_LOAD_TXD_AC2 FIELD32(0x00000004) #define LOAD_TX_RING_CSR_LOAD_TXD_AC3 FIELD32(0x00000008) #define LOAD_TX_RING_CSR_LOAD_TXD_MGMT FIELD32(0x00000010) /* * Several read-only registers, for debugging. */ #define AC0_TXPTR_CSR 0x3438 #define AC1_TXPTR_CSR 0x343c #define AC2_TXPTR_CSR 0x3440 #define AC3_TXPTR_CSR 0x3444 #define MGMT_TXPTR_CSR 0x3448 /* * RX_BASE_CSR */ #define RX_BASE_CSR 0x3450 #define RX_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* * RX_RING_CSR. * RXD_SIZE: In unit of 32-bit. */ #define RX_RING_CSR 0x3454 #define RX_RING_CSR_RING_SIZE FIELD32(0x000000ff) #define RX_RING_CSR_RXD_SIZE FIELD32(0x00003f00) #define RX_RING_CSR_RXD_WRITEBACK_SIZE FIELD32(0x00070000) /* * RX_CNTL_CSR */ #define RX_CNTL_CSR 0x3458 #define RX_CNTL_CSR_ENABLE_RX_DMA FIELD32(0x00000001) #define RX_CNTL_CSR_LOAD_RXD FIELD32(0x00000002) /* * RXPTR_CSR: Read-only, for debugging. */ #define RXPTR_CSR 0x345c /* * PCI_CFG_CSR */ #define PCI_CFG_CSR 0x3460 /* * BUF_FORMAT_CSR */ #define BUF_FORMAT_CSR 0x3464 /* * INT_SOURCE_CSR: Interrupt source register. * Write one to clear corresponding bit. */ #define INT_SOURCE_CSR 0x3468 #define INT_SOURCE_CSR_TXDONE FIELD32(0x00000001) #define INT_SOURCE_CSR_RXDONE FIELD32(0x00000002) #define INT_SOURCE_CSR_BEACON_DONE FIELD32(0x00000004) #define INT_SOURCE_CSR_TX_ABORT_DONE FIELD32(0x00000010) #define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00010000) #define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00020000) #define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00040000) #define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00080000) #define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00100000) #define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00200000) /* * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. * MITIGATION_PERIOD: Interrupt mitigation in unit of 32 PCI clock. */ #define INT_MASK_CSR 0x346c #define INT_MASK_CSR_TXDONE FIELD32(0x00000001) #define INT_MASK_CSR_RXDONE FIELD32(0x00000002) #define INT_MASK_CSR_BEACON_DONE FIELD32(0x00000004) #define INT_MASK_CSR_TX_ABORT_DONE FIELD32(0x00000010) #define INT_MASK_CSR_ENABLE_MITIGATION FIELD32(0x00000080) #define INT_MASK_CSR_MITIGATION_PERIOD FIELD32(0x0000ff00) #define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00010000) #define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00020000) #define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00040000) #define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00080000) #define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00100000) #define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00200000) /* * E2PROM_CSR: EEPROM control register. * RELOAD: Write 1 to reload eeprom content. * TYPE_93C46: 1: 93c46, 0:93c66. * LOAD_STATUS: 1:loading, 0:done. */ #define E2PROM_CSR 0x3470 #define E2PROM_CSR_RELOAD FIELD32(0x00000001) #define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000002) #define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000004) #define E2PROM_CSR_DATA_IN FIELD32(0x00000008) #define E2PROM_CSR_DATA_OUT FIELD32(0x00000010) #define E2PROM_CSR_TYPE_93C46 FIELD32(0x00000020) #define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) /* * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. * AC0_TX_OP: For AC_VO, in unit of 32us. * AC1_TX_OP: For AC_VI, in unit of 32us. */ #define AC_TXOP_CSR0 0x3474 #define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) #define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) /* * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. * AC2_TX_OP: For AC_BE, in unit of 32us. * AC3_TX_OP: For AC_BK, in unit of 32us. */ #define AC_TXOP_CSR1 0x3478 #define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) #define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) /* * DMA_STATUS_CSR */ #define DMA_STATUS_CSR 0x3480 /* * TEST_MODE_CSR */ #define TEST_MODE_CSR 0x3484 /* * UART0_TX_CSR */ #define UART0_TX_CSR 0x3488 /* * UART0_RX_CSR */ #define UART0_RX_CSR 0x348c /* * UART0_FRAME_CSR */ #define UART0_FRAME_CSR 0x3490 /* * UART0_BUFFER_CSR */ #define UART0_BUFFER_CSR 0x3494 /* * IO_CNTL_CSR * RF_PS: Set RF interface value to power save */ #define IO_CNTL_CSR 0x3498 #define IO_CNTL_CSR_RF_PS FIELD32(0x00000004) /* * UART_INT_SOURCE_CSR */ #define UART_INT_SOURCE_CSR 0x34a8 /* * UART_INT_MASK_CSR */ #define UART_INT_MASK_CSR 0x34ac /* * PBF_QUEUE_CSR */ #define PBF_QUEUE_CSR 0x34b0 /* * Firmware DMA registers. * Firmware DMA registers are dedicated for MCU usage * and should not be touched by host driver. * Therefore we skip the definition of these registers. */ #define FW_TX_BASE_CSR 0x34c0 #define FW_TX_START_CSR 0x34c4 #define FW_TX_LAST_CSR 0x34c8 #define FW_MODE_CNTL_CSR 0x34cc #define FW_TXPTR_CSR 0x34d0 /* * 8051 firmware image. */ #define FIRMWARE_RT2561 "rt2561.bin" #define FIRMWARE_RT2561s "rt2561s.bin" #define FIRMWARE_RT2661 "rt2661.bin" #define FIRMWARE_IMAGE_BASE 0x4000 /* * BBP registers. * The wordsize of the BBP is 8 bits. */ /* * R2 */ #define BBP_R2_BG_MODE FIELD8(0x20) /* * R3 */ #define BBP_R3_SMART_MODE FIELD8(0x01) /* * R4: RX antenna control * FRAME_END: 1 - DPDT, 0 - SPDT (Only valid for 802.11G, RF2527 & RF2529) */ /* * ANTENNA_CONTROL semantics (guessed): * 0x1: Software controlled antenna switching (fixed or SW diversity) * 0x2: Hardware diversity. */ #define BBP_R4_RX_ANTENNA_CONTROL FIELD8(0x03) #define BBP_R4_RX_FRAME_END FIELD8(0x20) /* * R77 */ #define BBP_R77_RX_ANTENNA FIELD8(0x03) /* * RF registers */ /* * RF 3 */ #define RF3_TXPOWER FIELD32(0x00003e00) /* * RF 4 */ #define RF4_FREQ_OFFSET FIELD32(0x0003f000) /* * EEPROM content. * The wordsize of the EEPROM is 16 bits. */ /* * HW MAC address. */ #define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) #define EEPROM_MAC_ADDR1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) #define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) /* * EEPROM antenna. * ANTENNA_NUM: Number of antenna's. * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. * DYN_TXAGC: Dynamic TX AGC control. * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. * RF_TYPE: Rf_type of this adapter. */ #define EEPROM_ANTENNA 0x0010 #define EEPROM_ANTENNA_NUM FIELD16(0x0003) #define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) #define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) #define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) #define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) #define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) #define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) /* * EEPROM NIC config. * ENABLE_DIVERSITY: 1:enable, 0:disable. * EXTERNAL_LNA_BG: External LNA enable for 2.4G. * CARDBUS_ACCEL: 0:enable, 1:disable. * EXTERNAL_LNA_A: External LNA enable for 5G. */ #define EEPROM_NIC 0x0011 #define EEPROM_NIC_ENABLE_DIVERSITY FIELD16(0x0001) #define EEPROM_NIC_TX_DIVERSITY FIELD16(0x0002) #define EEPROM_NIC_RX_FIXED FIELD16(0x0004) #define EEPROM_NIC_TX_FIXED FIELD16(0x0008) #define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0010) #define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0020) #define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0040) /* * EEPROM geography. * GEO_A: Default geographical setting for 5GHz band * GEO: Default geographical setting. */ #define EEPROM_GEOGRAPHY 0x0012 #define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) #define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) /* * EEPROM BBP. */ #define EEPROM_BBP_START 0x0013 #define EEPROM_BBP_SIZE 16 #define EEPROM_BBP_VALUE FIELD16(0x00ff) #define EEPROM_BBP_REG_ID FIELD16(0xff00) /* * EEPROM TXPOWER 802.11G */ #define EEPROM_TXPOWER_G_START 0x0023 #define EEPROM_TXPOWER_G_SIZE 7 #define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_G_2 FIELD16(0xff00) /* * EEPROM Frequency */ #define EEPROM_FREQ 0x002f #define EEPROM_FREQ_OFFSET FIELD16(0x00ff) #define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) #define EEPROM_FREQ_SEQ FIELD16(0x0300) /* * EEPROM LED. * POLARITY_RDY_G: Polarity RDY_G setting. * POLARITY_RDY_A: Polarity RDY_A setting. * POLARITY_ACT: Polarity ACT setting. * POLARITY_GPIO_0: Polarity GPIO0 setting. * POLARITY_GPIO_1: Polarity GPIO1 setting. * POLARITY_GPIO_2: Polarity GPIO2 setting. * POLARITY_GPIO_3: Polarity GPIO3 setting. * POLARITY_GPIO_4: Polarity GPIO4 setting. * LED_MODE: Led mode. */ #define EEPROM_LED 0x0030 #define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) #define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) #define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) #define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) #define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) #define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) #define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) #define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) #define EEPROM_LED_LED_MODE FIELD16(0x1f00) /* * EEPROM TXPOWER 802.11A */ #define EEPROM_TXPOWER_A_START 0x0031 #define EEPROM_TXPOWER_A_SIZE 12 #define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_A_2 FIELD16(0xff00) /* * EEPROM RSSI offset 802.11BG */ #define EEPROM_RSSI_OFFSET_BG 0x004d #define EEPROM_RSSI_OFFSET_BG_1 FIELD16(0x00ff) #define EEPROM_RSSI_OFFSET_BG_2 FIELD16(0xff00) /* * EEPROM RSSI offset 802.11A */ #define EEPROM_RSSI_OFFSET_A 0x004e #define EEPROM_RSSI_OFFSET_A_1 FIELD16(0x00ff) #define EEPROM_RSSI_OFFSET_A_2 FIELD16(0xff00) /* * MCU mailbox commands. */ #define MCU_SLEEP 0x30 #define MCU_WAKEUP 0x31 #define MCU_LED 0x50 #define MCU_LED_STRENGTH 0x52 /* * DMA descriptor defines. */ #define TXD_DESC_SIZE ( 16 * sizeof(__le32) ) #define TXINFO_SIZE ( 6 * sizeof(__le32) ) #define RXD_DESC_SIZE ( 16 * sizeof(__le32) ) /* * TX descriptor format for TX, PRIO and Beacon Ring. */ /* * Word0 * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. * KEY_TABLE: Use per-client pairwise KEY table. * KEY_INDEX: * Key index (0~31) to the pairwise KEY table. * 0~3 to shared KEY table 0 (BSS0). * 4~7 to shared KEY table 1 (BSS1). * 8~11 to shared KEY table 2 (BSS2). * 12~15 to shared KEY table 3 (BSS3). * BURST: Next frame belongs to same "burst" event. */ #define TXD_W0_OWNER_NIC FIELD32(0x00000001) #define TXD_W0_VALID FIELD32(0x00000002) #define TXD_W0_MORE_FRAG FIELD32(0x00000004) #define TXD_W0_ACK FIELD32(0x00000008) #define TXD_W0_TIMESTAMP FIELD32(0x00000010) #define TXD_W0_OFDM FIELD32(0x00000020) #define TXD_W0_IFS FIELD32(0x00000040) #define TXD_W0_RETRY_MODE FIELD32(0x00000080) #define TXD_W0_TKIP_MIC FIELD32(0x00000100) #define TXD_W0_KEY_TABLE FIELD32(0x00000200) #define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) #define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) #define TXD_W0_BURST FIELD32(0x10000000) #define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) /* * Word1 * HOST_Q_ID: EDCA/HCCA queue ID. * HW_SEQUENCE: MAC overwrites the frame sequence number. * BUFFER_COUNT: Number of buffers in this TXD. */ #define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) #define TXD_W1_AIFSN FIELD32(0x000000f0) #define TXD_W1_CWMIN FIELD32(0x00000f00) #define TXD_W1_CWMAX FIELD32(0x0000f000) #define TXD_W1_IV_OFFSET FIELD32(0x003f0000) #define TXD_W1_PIGGY_BACK FIELD32(0x01000000) #define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) #define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) /* * Word2: PLCP information */ #define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) #define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) #define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) #define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) /* * Word3 */ #define TXD_W3_IV FIELD32(0xffffffff) /* * Word4 */ #define TXD_W4_EIV FIELD32(0xffffffff) /* * Word5 * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). * TXD_W5_PID_SUBTYPE: Driver assigned packet ID index for txdone handler. * TXD_W5_PID_TYPE: Driver assigned packet ID type for txdone handler. * WAITING_DMA_DONE_INT: TXD been filled with data * and waiting for TxDoneISR housekeeping. */ #define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) #define TXD_W5_PID_SUBTYPE FIELD32(0x00001f00) #define TXD_W5_PID_TYPE FIELD32(0x0000e000) #define TXD_W5_TX_POWER FIELD32(0x00ff0000) #define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) /* * the above 24-byte is called TXINFO and will be DMAed to MAC block * through TXFIFO. MAC block use this TXINFO to control the transmission * behavior of this frame. * The following fields are not used by MAC block. * They are used by DMA block and HOST driver only. * Once a frame has been DMA to ASIC, all the following fields are useless * to ASIC. */ /* * Word6-10: Buffer physical address */ #define TXD_W6_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) #define TXD_W7_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) #define TXD_W8_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) #define TXD_W9_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) #define TXD_W10_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) /* * Word11-13: Buffer length */ #define TXD_W11_BUFFER_LENGTH0 FIELD32(0x00000fff) #define TXD_W11_BUFFER_LENGTH1 FIELD32(0x0fff0000) #define TXD_W12_BUFFER_LENGTH2 FIELD32(0x00000fff) #define TXD_W12_BUFFER_LENGTH3 FIELD32(0x0fff0000) #define TXD_W13_BUFFER_LENGTH4 FIELD32(0x00000fff) /* * Word14 */ #define TXD_W14_SK_BUFFER FIELD32(0xffffffff) /* * Word15 */ #define TXD_W15_NEXT_SK_BUFFER FIELD32(0xffffffff) /* * RX descriptor format for RX Ring. */ /* * Word0 * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. * KEY_INDEX: Decryption key actually used. */ #define RXD_W0_OWNER_NIC FIELD32(0x00000001) #define RXD_W0_DROP FIELD32(0x00000002) #define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) #define RXD_W0_MULTICAST FIELD32(0x00000008) #define RXD_W0_BROADCAST FIELD32(0x00000010) #define RXD_W0_MY_BSS FIELD32(0x00000020) #define RXD_W0_CRC_ERROR FIELD32(0x00000040) #define RXD_W0_OFDM FIELD32(0x00000080) #define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) #define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) #define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) #define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) /* * Word1 * SIGNAL: RX raw data rate reported by BBP. */ #define RXD_W1_SIGNAL FIELD32(0x000000ff) #define RXD_W1_RSSI_AGC FIELD32(0x00001f00) #define RXD_W1_RSSI_LNA FIELD32(0x00006000) #define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) /* * Word2 * IV: Received IV of originally encrypted. */ #define RXD_W2_IV FIELD32(0xffffffff) /* * Word3 * EIV: Received EIV of originally encrypted. */ #define RXD_W3_EIV FIELD32(0xffffffff) /* * Word4 * ICV: Received ICV of originally encrypted. * NOTE: This is a guess, the official definition is "reserved" */ #define RXD_W4_ICV FIELD32(0xffffffff) /* * the above 20-byte is called RXINFO and will be DMAed to MAC RX block * and passed to the HOST driver. * The following fields are for DMA block and HOST usage only. * Can't be touched by ASIC MAC block. */ /* * Word5 */ #define RXD_W5_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) /* * Word6-15: Reserved */ #define RXD_W6_RESERVED FIELD32(0xffffffff) #define RXD_W7_RESERVED FIELD32(0xffffffff) #define RXD_W8_RESERVED FIELD32(0xffffffff) #define RXD_W9_RESERVED FIELD32(0xffffffff) #define RXD_W10_RESERVED FIELD32(0xffffffff) #define RXD_W11_RESERVED FIELD32(0xffffffff) #define RXD_W12_RESERVED FIELD32(0xffffffff) #define RXD_W13_RESERVED FIELD32(0xffffffff) #define RXD_W14_RESERVED FIELD32(0xffffffff) #define RXD_W15_RESERVED FIELD32(0xffffffff) /* * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 #define TXPOWER_FROM_DEV(__txpower) \ (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT61PCI_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt61pci.c0000644000175000017500000027352712026211315023174 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt61pci Abstract: rt61pci device specific routines. Supported chipsets: RT2561, RT2561s, RT2661. */ #include #include #include #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00pci.h" #include "rt61pci.h" /* * Allow hardware encryption to be disabled. */ static bool modparam_nohwcrypt = false; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); /* * Register access. * BBP and RF register require indirect register access, * and use the CSR registers PHY_CSR3 and PHY_CSR4 to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay * between each attempt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. */ #define WAIT_FOR_BBP(__dev, __reg) \ rt2x00pci_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) #define WAIT_FOR_RF(__dev, __reg) \ rt2x00pci_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) #define WAIT_FOR_MCU(__dev, __reg) \ rt2x00pci_regbusy_read((__dev), H2M_MAILBOX_CSR, \ H2M_MAILBOX_CSR_OWNER, (__reg)) static void rt61pci_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, PHY_CSR3_VALUE, value); rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt61pci_bbp_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the read request into the register. * After the data has been written, we wait until hardware * returns the correct value, if at any time the register * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg); WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); mutex_unlock(&rt2x00dev->csr_mutex); } static void rt61pci_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RF becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_RF(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, PHY_CSR4_VALUE, value); rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 21); rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); rt2x00pci_register_write(rt2x00dev, PHY_CSR4, reg); rt2x00_rf_write(rt2x00dev, word, value); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev, const u8 command, const u8 token, const u8 arg0, const u8 arg1) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the MCU becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_MCU(rt2x00dev, ®)) { rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg); rt2x00pci_register_read(rt2x00dev, HOST_CMD_CSR, ®); rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); rt2x00_set_field32(®, HOST_CMD_CSR_INTERRUPT_MCU, 1); rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg; rt2x00pci_register_read(rt2x00dev, E2PROM_CSR, ®); eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); eeprom->reg_data_clock = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); eeprom->reg_chip_select = !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); } static void rt61pci_eepromregister_write(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg = 0; rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, !!eeprom->reg_data_clock); rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, !!eeprom->reg_chip_select); rt2x00pci_register_write(rt2x00dev, E2PROM_CSR, reg); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS static const struct rt2x00debug rt61pci_rt2x00debug = { .owner = THIS_MODULE, .csr = { .read = rt2x00pci_register_read, .write = rt2x00pci_register_write, .flags = RT2X00DEBUGFS_OFFSET, .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt61pci_bbp_read, .write = rt61pci_bbp_write, .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt61pci_rf_write, .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00pci_register_read(rt2x00dev, MAC_CSR13, ®); return rt2x00_get_field32(reg, MAC_CSR13_VAL5); } #ifdef CONFIG_RT2X00_LIB_LEDS static void rt61pci_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); unsigned int enabled = brightness != LED_OFF; unsigned int a_mode = (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); unsigned int bg_mode = (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); if (led->type == LED_TYPE_RADIO) { rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, MCU_LEDCS_RADIO_STATUS, enabled); rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, (led->rt2x00dev->led_mcu_reg & 0xff), ((led->rt2x00dev->led_mcu_reg >> 8))); } else if (led->type == LED_TYPE_ASSOC) { rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, MCU_LEDCS_LINK_BG_STATUS, bg_mode); rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, MCU_LEDCS_LINK_A_STATUS, a_mode); rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, (led->rt2x00dev->led_mcu_reg & 0xff), ((led->rt2x00dev->led_mcu_reg >> 8))); } else if (led->type == LED_TYPE_QUALITY) { /* * The brightness is divided into 6 levels (0 - 5), * this means we need to convert the brightness * argument into the matching level within that range. */ rt61pci_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, brightness / (LED_FULL / 6), 0); } } static int rt61pci_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); u32 reg; rt2x00pci_register_read(led->rt2x00dev, MAC_CSR14, ®); rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); rt2x00pci_register_write(led->rt2x00dev, MAC_CSR14, reg); return 0; } static void rt61pci_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; led->type = type; led->led_dev.brightness_set = rt61pci_brightness_set; led->led_dev.blink_set = rt61pci_blink_set; led->flags = LED_INITIALIZED; } #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Configuration handlers. */ static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { struct hw_key_entry key_entry; struct rt2x00_field32 field; u32 mask; u32 reg; if (crypto->cmd == SET_KEY) { /* * rt2x00lib can't determine the correct free * key_idx for shared keys. We have 1 register * with key valid bits. The goal is simple, read * the register, if that is full we have no slots * left. * Note that each BSS is allowed to have up to 4 * shared keys, so put a mask over the allowed * entries. */ mask = (0xf << crypto->bssidx); rt2x00pci_register_read(rt2x00dev, SEC_CSR0, ®); reg &= mask; if (reg && reg == mask) return -ENOSPC; key->hw_key_idx += reg ? ffz(reg) : 0; /* * Upload key to hardware */ memcpy(key_entry.key, crypto->key, sizeof(key_entry.key)); memcpy(key_entry.tx_mic, crypto->tx_mic, sizeof(key_entry.tx_mic)); memcpy(key_entry.rx_mic, crypto->rx_mic, sizeof(key_entry.rx_mic)); reg = SHARED_KEY_ENTRY(key->hw_key_idx); rt2x00pci_register_multiwrite(rt2x00dev, reg, &key_entry, sizeof(key_entry)); /* * The cipher types are stored over 2 registers. * bssidx 0 and 1 keys are stored in SEC_CSR1 and * bssidx 1 and 2 keys are stored in SEC_CSR5. * Using the correct defines correctly will cause overhead, * so just calculate the correct offset. */ if (key->hw_key_idx < 8) { field.bit_offset = (3 * key->hw_key_idx); field.bit_mask = 0x7 << field.bit_offset; rt2x00pci_register_read(rt2x00dev, SEC_CSR1, ®); rt2x00_set_field32(®, field, crypto->cipher); rt2x00pci_register_write(rt2x00dev, SEC_CSR1, reg); } else { field.bit_offset = (3 * (key->hw_key_idx - 8)); field.bit_mask = 0x7 << field.bit_offset; rt2x00pci_register_read(rt2x00dev, SEC_CSR5, ®); rt2x00_set_field32(®, field, crypto->cipher); rt2x00pci_register_write(rt2x00dev, SEC_CSR5, reg); } /* * The driver does not support the IV/EIV generation * in hardware. However it doesn't support the IV/EIV * inside the ieee80211 frame either, but requires it * to be provided separately for the descriptor. * rt2x00lib will cut the IV/EIV data out of all frames * given to us by mac80211, but we must tell mac80211 * to generate the IV/EIV data. */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; } /* * SEC_CSR0 contains only single-bit fields to indicate * a particular key is valid. Because using the FIELD32() * defines directly will cause a lot of overhead, we use * a calculation to determine the correct bit directly. */ mask = 1 << key->hw_key_idx; rt2x00pci_register_read(rt2x00dev, SEC_CSR0, ®); if (crypto->cmd == SET_KEY) reg |= mask; else if (crypto->cmd == DISABLE_KEY) reg &= ~mask; rt2x00pci_register_write(rt2x00dev, SEC_CSR0, reg); return 0; } static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { struct hw_pairwise_ta_entry addr_entry; struct hw_key_entry key_entry; u32 mask; u32 reg; if (crypto->cmd == SET_KEY) { /* * rt2x00lib can't determine the correct free * key_idx for pairwise keys. We have 2 registers * with key valid bits. The goal is simple: read * the first register. If that is full, move to * the next register. * When both registers are full, we drop the key. * Otherwise, we use the first invalid entry. */ rt2x00pci_register_read(rt2x00dev, SEC_CSR2, ®); if (reg && reg == ~0) { key->hw_key_idx = 32; rt2x00pci_register_read(rt2x00dev, SEC_CSR3, ®); if (reg && reg == ~0) return -ENOSPC; } key->hw_key_idx += reg ? ffz(reg) : 0; /* * Upload key to hardware */ memcpy(key_entry.key, crypto->key, sizeof(key_entry.key)); memcpy(key_entry.tx_mic, crypto->tx_mic, sizeof(key_entry.tx_mic)); memcpy(key_entry.rx_mic, crypto->rx_mic, sizeof(key_entry.rx_mic)); memset(&addr_entry, 0, sizeof(addr_entry)); memcpy(&addr_entry, crypto->address, ETH_ALEN); addr_entry.cipher = crypto->cipher; reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); rt2x00pci_register_multiwrite(rt2x00dev, reg, &key_entry, sizeof(key_entry)); reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); rt2x00pci_register_multiwrite(rt2x00dev, reg, &addr_entry, sizeof(addr_entry)); /* * Enable pairwise lookup table for given BSS idx. * Without this, received frames will not be decrypted * by the hardware. */ rt2x00pci_register_read(rt2x00dev, SEC_CSR4, ®); reg |= (1 << crypto->bssidx); rt2x00pci_register_write(rt2x00dev, SEC_CSR4, reg); /* * The driver does not support the IV/EIV generation * in hardware. However it doesn't support the IV/EIV * inside the ieee80211 frame either, but requires it * to be provided separately for the descriptor. * rt2x00lib will cut the IV/EIV data out of all frames * given to us by mac80211, but we must tell mac80211 * to generate the IV/EIV data. */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; } /* * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate * a particular key is valid. Because using the FIELD32() * defines directly will cause a lot of overhead, we use * a calculation to determine the correct bit directly. */ if (key->hw_key_idx < 32) { mask = 1 << key->hw_key_idx; rt2x00pci_register_read(rt2x00dev, SEC_CSR2, ®); if (crypto->cmd == SET_KEY) reg |= mask; else if (crypto->cmd == DISABLE_KEY) reg &= ~mask; rt2x00pci_register_write(rt2x00dev, SEC_CSR2, reg); } else { mask = 1 << (key->hw_key_idx - 32); rt2x00pci_register_read(rt2x00dev, SEC_CSR3, ®); if (crypto->cmd == SET_KEY) reg |= mask; else if (crypto->cmd == DISABLE_KEY) reg &= ~mask; rt2x00pci_register_write(rt2x00dev, SEC_CSR3, reg); } return 0; } static void rt61pci_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { u32 reg; /* * Start configuration steps. * Note that the version error will always be dropped * and broadcast frames will always be accepted since * there is no filter for it at this time. */ rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, !(filter_flags & FIF_FCSFAIL)); rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, !(filter_flags & FIF_PROMISC_IN_BSS) && !rt2x00dev->intf_ap_count); rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, !(filter_flags & FIF_ALLMULTI)); rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, !(filter_flags & FIF_CONTROL)); rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); } static void rt61pci_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags) { u32 reg; if (flags & CONFIG_UPDATE_TYPE) { /* * Enable synchronisation. */ rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); } if (flags & CONFIG_UPDATE_MAC) { reg = le32_to_cpu(conf->mac[1]); rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); conf->mac[1] = cpu_to_le32(reg); rt2x00pci_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac, sizeof(conf->mac)); } if (flags & CONFIG_UPDATE_BSSID) { reg = le32_to_cpu(conf->bssid[1]); rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); conf->bssid[1] = cpu_to_le32(reg); rt2x00pci_register_multiwrite(rt2x00dev, MAC_CSR4, conf->bssid, sizeof(conf->bssid)); } } static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed) { u32 reg; rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); if (changed & BSS_CHANGED_ERP_PREAMBLE) { rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, ®); rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, !!erp->short_preamble); rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg); } if (changed & BSS_CHANGED_BASIC_RATES) rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates); if (changed & BSS_CHANGED_BEACON_INT) { rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, erp->beacon_int * 16); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); } if (changed & BSS_CHANGED_ERP_SLOT) { rt2x00pci_register_read(rt2x00dev, MAC_CSR9, ®); rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg); rt2x00pci_register_read(rt2x00dev, MAC_CSR8, ®); rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg); } } static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r3; u8 r4; u8 r77; rt61pci_bbp_read(rt2x00dev, 3, &r3); rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF5325)); /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ)); break; case ANTENNA_A: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); break; case ANTENNA_B: default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); break; } rt61pci_bbp_write(rt2x00dev, 77, r77); rt61pci_bbp_write(rt2x00dev, 3, r3); rt61pci_bbp_write(rt2x00dev, 4, r4); } static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r3; u8 r4; u8 r77; rt61pci_bbp_read(rt2x00dev, 3, &r3); rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529)); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)); /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); break; case ANTENNA_A: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); break; case ANTENNA_B: default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); break; } rt61pci_bbp_write(rt2x00dev, 77, r77); rt61pci_bbp_write(rt2x00dev, 3, r3); rt61pci_bbp_write(rt2x00dev, 4, r4); } static void rt61pci_config_antenna_2529_rx(struct rt2x00_dev *rt2x00dev, const int p1, const int p2) { u32 reg; rt2x00pci_register_read(rt2x00dev, MAC_CSR13, ®); rt2x00_set_field32(®, MAC_CSR13_DIR4, 0); rt2x00_set_field32(®, MAC_CSR13_VAL4, p1); rt2x00_set_field32(®, MAC_CSR13_DIR3, 0); rt2x00_set_field32(®, MAC_CSR13_VAL3, !p2); rt2x00pci_register_write(rt2x00dev, MAC_CSR13, reg); } static void rt61pci_config_antenna_2529(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r3; u8 r4; u8 r77; rt61pci_bbp_read(rt2x00dev, 3, &r3); rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_A: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); rt61pci_config_antenna_2529_rx(rt2x00dev, 0, 0); break; case ANTENNA_HW_DIVERSITY: /* * FIXME: Antenna selection for the rf 2529 is very confusing * in the legacy driver. Just default to antenna B until the * legacy code can be properly translated into rt2x00 code. */ case ANTENNA_B: default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); rt61pci_config_antenna_2529_rx(rt2x00dev, 1, 1); break; } rt61pci_bbp_write(rt2x00dev, 77, r77); rt61pci_bbp_write(rt2x00dev, 3, r3); rt61pci_bbp_write(rt2x00dev, 4, r4); } struct antenna_sel { u8 word; /* * value[0] -> non-LNA * value[1] -> LNA */ u8 value[2]; }; static const struct antenna_sel antenna_sel_a[] = { { 96, { 0x58, 0x78 } }, { 104, { 0x38, 0x48 } }, { 75, { 0xfe, 0x80 } }, { 86, { 0xfe, 0x80 } }, { 88, { 0xfe, 0x80 } }, { 35, { 0x60, 0x60 } }, { 97, { 0x58, 0x58 } }, { 98, { 0x58, 0x58 } }, }; static const struct antenna_sel antenna_sel_bg[] = { { 96, { 0x48, 0x68 } }, { 104, { 0x2c, 0x3c } }, { 75, { 0xfe, 0x80 } }, { 86, { 0xfe, 0x80 } }, { 88, { 0xfe, 0x80 } }, { 35, { 0x50, 0x50 } }, { 97, { 0x48, 0x48 } }, { 98, { 0x48, 0x48 } }, }; static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { const struct antenna_sel *sel; unsigned int lna; unsigned int i; u32 reg; /* * We should never come here because rt2x00lib is supposed * to catch this and send us the correct antenna explicitely. */ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || ant->tx == ANTENNA_SW_DIVERSITY); if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); } else { sel = antenna_sel_bg; lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) rt61pci_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); rt2x00pci_register_read(rt2x00dev, PHY_CSR0, ®); rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); rt2x00pci_register_write(rt2x00dev, PHY_CSR0, reg); if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) rt61pci_config_antenna_5x(rt2x00dev, ant); else if (rt2x00_rf(rt2x00dev, RF2527)) rt61pci_config_antenna_2x(rt2x00dev, ant); else if (rt2x00_rf(rt2x00dev, RF2529)) { if (test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) rt61pci_config_antenna_2x(rt2x00dev, ant); else rt61pci_config_antenna_2529(rt2x00dev, ant); } } static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u16 eeprom; short lna_gain = 0; if (libconf->conf->channel->band == IEEE80211_BAND_2GHZ) { if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } else { if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); } rt2x00dev->lna_gain = lna_gain; } static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev, struct rf_channel *rf, const int txpower) { u8 r3; u8 r94; u8 smart; rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); rt61pci_bbp_read(rt2x00dev, 3, &r3); rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); rt61pci_bbp_write(rt2x00dev, 3, r3); r94 = 6; if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) r94 += txpower - MAX_TXPOWER; else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) r94 += txpower; rt61pci_bbp_write(rt2x00dev, 94, r94); rt61pci_rf_write(rt2x00dev, 1, rf->rf1); rt61pci_rf_write(rt2x00dev, 2, rf->rf2); rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); rt61pci_rf_write(rt2x00dev, 4, rf->rf4); udelay(200); rt61pci_rf_write(rt2x00dev, 1, rf->rf1); rt61pci_rf_write(rt2x00dev, 2, rf->rf2); rt61pci_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); rt61pci_rf_write(rt2x00dev, 4, rf->rf4); udelay(200); rt61pci_rf_write(rt2x00dev, 1, rf->rf1); rt61pci_rf_write(rt2x00dev, 2, rf->rf2); rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); rt61pci_rf_write(rt2x00dev, 4, rf->rf4); msleep(1); } static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev, const int txpower) { struct rf_channel rf; rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); rt61pci_config_channel(rt2x00dev, &rf, txpower); } static void rt61pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u32 reg; rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, ®); rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, libconf->conf->long_frame_max_tx_count); rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, libconf->conf->short_frame_max_tx_count); rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg); } static void rt61pci_config_ps(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { enum dev_state state = (libconf->conf->flags & IEEE80211_CONF_PS) ? STATE_SLEEP : STATE_AWAKE; u32 reg; if (state == STATE_SLEEP) { rt2x00pci_register_read(rt2x00dev, MAC_CSR11, ®); rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, rt2x00dev->beacon_int - 10); rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, libconf->conf->listen_interval - 1); rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); /* We must first disable autowake before it can be enabled */ rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); rt2x00pci_register_write(rt2x00dev, MAC_CSR11, reg); rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); rt2x00pci_register_write(rt2x00dev, MAC_CSR11, reg); rt2x00pci_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000005); rt2x00pci_register_write(rt2x00dev, IO_CNTL_CSR, 0x0000001c); rt2x00pci_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000060); rt61pci_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 0); } else { rt2x00pci_register_read(rt2x00dev, MAC_CSR11, ®); rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); rt2x00pci_register_write(rt2x00dev, MAC_CSR11, reg); rt2x00pci_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000007); rt2x00pci_register_write(rt2x00dev, IO_CNTL_CSR, 0x00000018); rt2x00pci_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000020); rt61pci_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0); } } static void rt61pci_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { /* Always recalculate LNA gain before changing configuration */ rt61pci_config_lna_gain(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_CHANNEL) rt61pci_config_channel(rt2x00dev, &libconf->rf, libconf->conf->power_level); if ((flags & IEEE80211_CONF_CHANGE_POWER) && !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level); if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) rt61pci_config_retry_limit(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_PS) rt61pci_config_ps(rt2x00dev, libconf); } /* * Link tuning */ static void rt61pci_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { u32 reg; /* * Update FCS error count from register. */ rt2x00pci_register_read(rt2x00dev, STA_CSR0, ®); qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); /* * Update False CCA count from register. */ rt2x00pci_register_read(rt2x00dev, STA_CSR1, ®); qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); } static inline void rt61pci_set_vgc(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, u8 vgc_level) { if (qual->vgc_level != vgc_level) { rt61pci_bbp_write(rt2x00dev, 17, vgc_level); qual->vgc_level = vgc_level; qual->vgc_level_reg = vgc_level; } } static void rt61pci_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { rt61pci_set_vgc(rt2x00dev, qual, 0x20); } static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count) { u8 up_bound; u8 low_bound; /* * Determine r17 bounds. */ if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { low_bound = 0x28; up_bound = 0x48; if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { low_bound += 0x10; up_bound += 0x10; } } else { low_bound = 0x20; up_bound = 0x40; if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { low_bound += 0x10; up_bound += 0x10; } } /* * If we are not associated, we should go straight to the * dynamic CCA tuning. */ if (!rt2x00dev->intf_associated) goto dynamic_cca_tune; /* * Special big-R17 for very short distance */ if (qual->rssi >= -35) { rt61pci_set_vgc(rt2x00dev, qual, 0x60); return; } /* * Special big-R17 for short distance */ if (qual->rssi >= -58) { rt61pci_set_vgc(rt2x00dev, qual, up_bound); return; } /* * Special big-R17 for middle-short distance */ if (qual->rssi >= -66) { rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x10); return; } /* * Special mid-R17 for middle distance */ if (qual->rssi >= -74) { rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x08); return; } /* * Special case: Change up_bound based on the rssi. * Lower up_bound when rssi is weaker then -74 dBm. */ up_bound -= 2 * (-74 - qual->rssi); if (low_bound > up_bound) up_bound = low_bound; if (qual->vgc_level > up_bound) { rt61pci_set_vgc(rt2x00dev, qual, up_bound); return; } dynamic_cca_tune: /* * r17 does not yet exceed upper limit, continue and base * the r17 tuning on the false CCA count. */ if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) rt61pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) rt61pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); } /* * Queue handlers. */ static void rt61pci_start_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); break; default: break; } } static void rt61pci_kick_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_AC_VO: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC0, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; case QID_AC_VI: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC1, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; case QID_AC_BE: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC2, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; case QID_AC_BK: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC3, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; default: break; } } static void rt61pci_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_AC_VO: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC0, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; case QID_AC_VI: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC1, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; case QID_AC_BE: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC2, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; case QID_AC_BK: rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC3, 1); rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); break; case QID_RX: rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); /* * Wait for possibly running tbtt tasklets. */ tasklet_kill(&rt2x00dev->tbtt_tasklet); break; default: break; } } /* * Firmware functions */ static char *rt61pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) { u16 chip; char *fw_name; pci_read_config_word(to_pci_dev(rt2x00dev->dev), PCI_DEVICE_ID, &chip); switch (chip) { case RT2561_PCI_ID: fw_name = FIRMWARE_RT2561; break; case RT2561s_PCI_ID: fw_name = FIRMWARE_RT2561s; break; case RT2661_PCI_ID: fw_name = FIRMWARE_RT2661; break; default: fw_name = NULL; break; } return fw_name; } static int rt61pci_check_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { u16 fw_crc; u16 crc; /* * Only support 8kb firmware files. */ if (len != 8192) return FW_BAD_LENGTH; /* * The last 2 bytes in the firmware array are the crc checksum itself. * This means that we should never pass those 2 bytes to the crc * algorithm. */ fw_crc = (data[len - 2] << 8 | data[len - 1]); /* * Use the crc itu-t algorithm. */ crc = crc_itu_t(0, data, len - 2); crc = crc_itu_t_byte(crc, 0); crc = crc_itu_t_byte(crc, 0); return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; } static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { int i; u32 reg; /* * Wait for stable hardware. */ for (i = 0; i < 100; i++) { rt2x00pci_register_read(rt2x00dev, MAC_CSR0, ®); if (reg) break; msleep(1); } if (!reg) { ERROR(rt2x00dev, "Unstable hardware.\n"); return -EBUSY; } /* * Prepare MCU and mailbox for firmware loading. */ reg = 0; rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); rt2x00pci_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, 0); /* * Write firmware to device. */ reg = 0; rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 1); rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); rt2x00pci_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data, len); rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 0); rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 0); rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); for (i = 0; i < 100; i++) { rt2x00pci_register_read(rt2x00dev, MCU_CNTL_CSR, ®); if (rt2x00_get_field32(reg, MCU_CNTL_CSR_READY)) break; msleep(1); } if (i == 100) { ERROR(rt2x00dev, "MCU Control register not ready.\n"); return -EBUSY; } /* * Hardware needs another millisecond before it is ready. */ msleep(1); /* * Reset MAC and BBP registers. */ reg = 0; rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); return 0; } /* * Initialization functions. */ static bool rt61pci_get_entry_state(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 0, &word); return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); } else { rt2x00_desc_read(entry_priv->desc, 0, &word); return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || rt2x00_get_field32(word, TXD_W0_VALID)); } } static void rt61pci_clear_entry(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 5, &word); rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS, skbdesc->skb_dma); rt2x00_desc_write(entry_priv->desc, 5, word); rt2x00_desc_read(entry_priv->desc, 0, &word); rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); rt2x00_desc_write(entry_priv->desc, 0, word); } else { rt2x00_desc_read(entry_priv->desc, 0, &word); rt2x00_set_field32(&word, TXD_W0_VALID, 0); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); rt2x00_desc_write(entry_priv->desc, 0, word); } } static int rt61pci_init_queues(struct rt2x00_dev *rt2x00dev) { struct queue_entry_priv_pci *entry_priv; u32 reg; /* * Initialize registers. */ rt2x00pci_register_read(rt2x00dev, TX_RING_CSR0, ®); rt2x00_set_field32(®, TX_RING_CSR0_AC0_RING_SIZE, rt2x00dev->tx[0].limit); rt2x00_set_field32(®, TX_RING_CSR0_AC1_RING_SIZE, rt2x00dev->tx[1].limit); rt2x00_set_field32(®, TX_RING_CSR0_AC2_RING_SIZE, rt2x00dev->tx[2].limit); rt2x00_set_field32(®, TX_RING_CSR0_AC3_RING_SIZE, rt2x00dev->tx[3].limit); rt2x00pci_register_write(rt2x00dev, TX_RING_CSR0, reg); rt2x00pci_register_read(rt2x00dev, TX_RING_CSR1, ®); rt2x00_set_field32(®, TX_RING_CSR1_TXD_SIZE, rt2x00dev->tx[0].desc_size / 4); rt2x00pci_register_write(rt2x00dev, TX_RING_CSR1, reg); entry_priv = rt2x00dev->tx[0].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, AC0_BASE_CSR, ®); rt2x00_set_field32(®, AC0_BASE_CSR_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, AC0_BASE_CSR, reg); entry_priv = rt2x00dev->tx[1].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, AC1_BASE_CSR, ®); rt2x00_set_field32(®, AC1_BASE_CSR_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, AC1_BASE_CSR, reg); entry_priv = rt2x00dev->tx[2].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, AC2_BASE_CSR, ®); rt2x00_set_field32(®, AC2_BASE_CSR_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, AC2_BASE_CSR, reg); entry_priv = rt2x00dev->tx[3].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, AC3_BASE_CSR, ®); rt2x00_set_field32(®, AC3_BASE_CSR_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, AC3_BASE_CSR, reg); rt2x00pci_register_read(rt2x00dev, RX_RING_CSR, ®); rt2x00_set_field32(®, RX_RING_CSR_RING_SIZE, rt2x00dev->rx->limit); rt2x00_set_field32(®, RX_RING_CSR_RXD_SIZE, rt2x00dev->rx->desc_size / 4); rt2x00_set_field32(®, RX_RING_CSR_RXD_WRITEBACK_SIZE, 4); rt2x00pci_register_write(rt2x00dev, RX_RING_CSR, reg); entry_priv = rt2x00dev->rx->entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, RX_BASE_CSR, ®); rt2x00_set_field32(®, RX_BASE_CSR_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, RX_BASE_CSR, reg); rt2x00pci_register_read(rt2x00dev, TX_DMA_DST_CSR, ®); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC0, 2); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC1, 2); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC2, 2); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC3, 2); rt2x00pci_register_write(rt2x00dev, TX_DMA_DST_CSR, reg); rt2x00pci_register_read(rt2x00dev, LOAD_TX_RING_CSR, ®); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC0, 1); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC1, 1); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC2, 1); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC3, 1); rt2x00pci_register_write(rt2x00dev, LOAD_TX_RING_CSR, reg); rt2x00pci_register_read(rt2x00dev, RX_CNTL_CSR, ®); rt2x00_set_field32(®, RX_CNTL_CSR_LOAD_RXD, 1); rt2x00pci_register_write(rt2x00dev, RX_CNTL_CSR, reg); return 0; } static int rt61pci_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); rt2x00pci_register_read(rt2x00dev, TXRX_CSR1, ®); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2, 42); /* OFDM Rate */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); rt2x00pci_register_write(rt2x00dev, TXRX_CSR1, reg); /* * CCK TXD BBP registers */ rt2x00pci_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2, 11); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); rt2x00pci_register_write(rt2x00dev, TXRX_CSR2, reg); /* * OFDM TXD BBP registers */ rt2x00pci_register_read(rt2x00dev, TXRX_CSR3, ®); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); rt2x00pci_register_write(rt2x00dev, TXRX_CSR3, reg); rt2x00pci_register_read(rt2x00dev, TXRX_CSR7, ®); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); rt2x00pci_register_write(rt2x00dev, TXRX_CSR7, reg); rt2x00pci_register_read(rt2x00dev, TXRX_CSR8, ®); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); rt2x00pci_register_write(rt2x00dev, TXRX_CSR8, reg); rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); rt2x00pci_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); rt2x00pci_register_write(rt2x00dev, MAC_CSR6, 0x00000fff); rt2x00pci_register_read(rt2x00dev, MAC_CSR9, ®); rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg); rt2x00pci_register_write(rt2x00dev, MAC_CSR10, 0x0000071c); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; rt2x00pci_register_write(rt2x00dev, MAC_CSR13, 0x0000e000); /* * Invalidate all Shared Keys (SEC_CSR0), * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) */ rt2x00pci_register_write(rt2x00dev, SEC_CSR0, 0x00000000); rt2x00pci_register_write(rt2x00dev, SEC_CSR1, 0x00000000); rt2x00pci_register_write(rt2x00dev, SEC_CSR5, 0x00000000); rt2x00pci_register_write(rt2x00dev, PHY_CSR1, 0x000023b0); rt2x00pci_register_write(rt2x00dev, PHY_CSR5, 0x060a100c); rt2x00pci_register_write(rt2x00dev, PHY_CSR6, 0x00080606); rt2x00pci_register_write(rt2x00dev, PHY_CSR7, 0x00000a08); rt2x00pci_register_write(rt2x00dev, PCI_CFG_CSR, 0x28ca4404); rt2x00pci_register_write(rt2x00dev, TEST_MODE_CSR, 0x00000200); rt2x00pci_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); /* * Clear all beacons * For the Beacon base registers we only need to clear * the first byte since that byte contains the VALID and OWNER * bits which (when set to 0) will invalidate the entire beacon. */ rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE0, 0); rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE1, 0); rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE2, 0); rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE3, 0); /* * We must clear the error counters. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ rt2x00pci_register_read(rt2x00dev, STA_CSR0, ®); rt2x00pci_register_read(rt2x00dev, STA_CSR1, ®); rt2x00pci_register_read(rt2x00dev, STA_CSR2, ®); /* * Reset MAC and BBP registers. */ rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); return 0; } static int rt61pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt61pci_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) return 0; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); return -EACCES; } static int rt61pci_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; u16 eeprom; u8 reg_id; u8 value; if (unlikely(rt61pci_wait_bbp_ready(rt2x00dev))) return -EACCES; rt61pci_bbp_write(rt2x00dev, 3, 0x00); rt61pci_bbp_write(rt2x00dev, 15, 0x30); rt61pci_bbp_write(rt2x00dev, 21, 0xc8); rt61pci_bbp_write(rt2x00dev, 22, 0x38); rt61pci_bbp_write(rt2x00dev, 23, 0x06); rt61pci_bbp_write(rt2x00dev, 24, 0xfe); rt61pci_bbp_write(rt2x00dev, 25, 0x0a); rt61pci_bbp_write(rt2x00dev, 26, 0x0d); rt61pci_bbp_write(rt2x00dev, 34, 0x12); rt61pci_bbp_write(rt2x00dev, 37, 0x07); rt61pci_bbp_write(rt2x00dev, 39, 0xf8); rt61pci_bbp_write(rt2x00dev, 41, 0x60); rt61pci_bbp_write(rt2x00dev, 53, 0x10); rt61pci_bbp_write(rt2x00dev, 54, 0x18); rt61pci_bbp_write(rt2x00dev, 60, 0x10); rt61pci_bbp_write(rt2x00dev, 61, 0x04); rt61pci_bbp_write(rt2x00dev, 62, 0x04); rt61pci_bbp_write(rt2x00dev, 75, 0xfe); rt61pci_bbp_write(rt2x00dev, 86, 0xfe); rt61pci_bbp_write(rt2x00dev, 88, 0xfe); rt61pci_bbp_write(rt2x00dev, 90, 0x0f); rt61pci_bbp_write(rt2x00dev, 99, 0x00); rt61pci_bbp_write(rt2x00dev, 102, 0x16); rt61pci_bbp_write(rt2x00dev, 107, 0x04); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); rt61pci_bbp_write(rt2x00dev, reg_id, value); } } return 0; } /* * Device state switch handlers. */ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int mask = (state == STATE_RADIO_IRQ_OFF); u32 reg; unsigned long flags; /* * When interrupts are being enabled, the interrupt registers * should clear the register to assure a clean state. */ if (state == STATE_RADIO_IRQ_ON) { rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, ®); rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg); rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); } /* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); rt2x00_set_field32(®, INT_MASK_CSR_TXDONE, mask); rt2x00_set_field32(®, INT_MASK_CSR_RXDONE, mask); rt2x00_set_field32(®, INT_MASK_CSR_BEACON_DONE, mask); rt2x00_set_field32(®, INT_MASK_CSR_ENABLE_MITIGATION, mask); rt2x00_set_field32(®, INT_MASK_CSR_MITIGATION_PERIOD, 0xff); rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); rt2x00_set_field32(®, MCU_INT_MASK_CSR_0, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_1, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_2, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_3, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_4, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_5, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_6, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_7, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_TWAKEUP, mask); rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); if (state == STATE_RADIO_IRQ_OFF) { /* * Ensure that all tasklets are finished. */ tasklet_kill(&rt2x00dev->txstatus_tasklet); tasklet_kill(&rt2x00dev->rxdone_tasklet); tasklet_kill(&rt2x00dev->autowake_tasklet); tasklet_kill(&rt2x00dev->tbtt_tasklet); } } static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; /* * Initialize all registers. */ if (unlikely(rt61pci_init_queues(rt2x00dev) || rt61pci_init_registers(rt2x00dev) || rt61pci_init_bbp(rt2x00dev))) return -EIO; /* * Enable RX. */ rt2x00pci_register_read(rt2x00dev, RX_CNTL_CSR, ®); rt2x00_set_field32(®, RX_CNTL_CSR_ENABLE_RX_DMA, 1); rt2x00pci_register_write(rt2x00dev, RX_CNTL_CSR, reg); return 0; } static void rt61pci_disable_radio(struct rt2x00_dev *rt2x00dev) { /* * Disable power */ rt2x00pci_register_write(rt2x00dev, MAC_CSR10, 0x00001818); } static int rt61pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u32 reg, reg2; unsigned int i; char put_to_sleep; put_to_sleep = (state != STATE_AWAKE); rt2x00pci_register_read(rt2x00dev, MAC_CSR12, ®); rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); rt2x00pci_register_write(rt2x00dev, MAC_CSR12, reg); /* * Device is not guaranteed to be in the requested state yet. * We must wait until the register indicates that the * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00pci_register_read(rt2x00dev, MAC_CSR12, ®2); state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); if (state == !put_to_sleep) return 0; rt2x00pci_register_write(rt2x00dev, MAC_CSR12, reg); msleep(10); } return -EBUSY; } static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int retval = 0; switch (state) { case STATE_RADIO_ON: retval = rt61pci_enable_radio(rt2x00dev); break; case STATE_RADIO_OFF: rt61pci_disable_radio(rt2x00dev); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: rt61pci_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: case STATE_STANDBY: case STATE_AWAKE: retval = rt61pci_set_state(rt2x00dev, state); break; default: retval = -ENOTSUPP; break; } if (unlikely(retval)) ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", state, retval); return retval; } /* * TX descriptor initialization */ static void rt61pci_write_tx_desc(struct queue_entry *entry, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct queue_entry_priv_pci *entry_priv = entry->priv_data; __le32 *txd = entry_priv->desc; u32 word; /* * Start writing the descriptor words. */ rt2x00_desc_read(txd, 1, &word); rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); rt2x00_desc_write(txd, 1, word); rt2x00_desc_read(txd, 2, &word); rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, txdesc->u.plcp.length_low); rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, txdesc->u.plcp.length_high); rt2x00_desc_write(txd, 2, word); if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); } rt2x00_desc_read(txd, 5, &word); rt2x00_set_field32(&word, TXD_W5_PID_TYPE, entry->queue->qid); rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE, skbdesc->entry->entry_idx); rt2x00_set_field32(&word, TXD_W5_TX_POWER, TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); rt2x00_desc_write(txd, 5, word); if (entry->queue->qid != QID_BEACON) { rt2x00_desc_read(txd, 6, &word); rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS, skbdesc->skb_dma); rt2x00_desc_write(txd, 6, word); rt2x00_desc_read(txd, 11, &word); rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0, txdesc->length); rt2x00_desc_write(txd, 11, word); } /* * Writing TXD word 0 must the last to prevent a race condition with * the device, whereby the device may take hold of the TXD before we * finished updating it. */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, (txdesc->rate_mode == RATE_MODE_OFDM)); rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXD_W0_BURST, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); rt2x00_desc_write(txd, 0, word); /* * Register descriptor details in skb frame descriptor. */ skbdesc->desc = txd; skbdesc->desc_len = (entry->queue->qid == QID_BEACON) ? TXINFO_SIZE : TXD_DESC_SIZE; } /* * TX data initialization */ static void rt61pci_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_pci *entry_priv = entry->priv_data; unsigned int beacon_base; unsigned int padding_len; u32 orig_reg, reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); orig_reg = reg; rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); /* * Write the TX descriptor for the beacon. */ rt61pci_write_tx_desc(entry, txdesc); /* * Dump beacon to userspace through debugfs. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); /* * Write entire beacon with descriptor and padding to register. */ padding_len = roundup(entry->skb->len, 4) - entry->skb->len; if (padding_len && skb_pad(entry->skb, padding_len)) { ERROR(rt2x00dev, "Failure padding beacon, aborting\n"); /* skb freed by skb_pad() on failure */ entry->skb = NULL; rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, orig_reg); return; } beacon_base = HW_BEACON_OFFSET(entry->entry_idx); rt2x00pci_register_multiwrite(rt2x00dev, beacon_base, entry_priv->desc, TXINFO_SIZE); rt2x00pci_register_multiwrite(rt2x00dev, beacon_base + TXINFO_SIZE, entry->skb->data, entry->skb->len + padding_len); /* * Enable beaconing again. * * For Wi-Fi faily generated beacons between participating * stations. Set TBTT phase adaptive adjustment step to 8us. */ rt2x00pci_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); /* * Clean up beacon skb. */ dev_kfree_skb_any(entry->skb); entry->skb = NULL; } static void rt61pci_clear_beacon(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; u32 reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); /* * Clear beacon. */ rt2x00pci_register_write(rt2x00dev, HW_BEACON_OFFSET(entry->entry_idx), 0); /* * Enable beaconing again. */ rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); } /* * RX control handlers */ static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) { u8 offset = rt2x00dev->lna_gain; u8 lna; lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); switch (lna) { case 3: offset += 90; break; case 2: offset += 74; break; case 1: offset += 64; break; default: return 0; } if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { if (lna == 3 || lna == 2) offset += 10; } return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; } static void rt61pci_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word0; u32 word1; rt2x00_desc_read(entry_priv->desc, 0, &word0); rt2x00_desc_read(entry_priv->desc, 1, &word1); if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); if (rxdesc->cipher != CIPHER_NONE) { _rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv[0]); _rt2x00_desc_read(entry_priv->desc, 3, &rxdesc->iv[1]); rxdesc->dev_flags |= RXDONE_CRYPTO_IV; _rt2x00_desc_read(entry_priv->desc, 4, &rxdesc->icv); rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; /* * Hardware has stripped IV/EIV data from 802.11 frame during * decryption. It has provided the data separately but rt2x00lib * should decide if it should be reinserted. */ rxdesc->flags |= RX_FLAG_IV_STRIPPED; /* * The hardware has already checked the Michael Mic and has * stripped it from the frame. Signal this to mac80211. */ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) rxdesc->flags |= RX_FLAG_DECRYPTED; else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) rxdesc->flags |= RX_FLAG_MMIC_ERROR; } /* * Obtain the status about this packet. * When frame was received with an OFDM bitrate, * the signal is the PLCP value. If it was received with * a CCK bitrate the signal is the rate in 100kbit/s. */ rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); rxdesc->rssi = rt61pci_agc_to_rssi(rt2x00dev, word1); rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); if (rt2x00_get_field32(word0, RXD_W0_OFDM)) rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; else rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; } /* * Interrupt functions. */ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; struct queue_entry *entry; struct queue_entry *entry_done; struct queue_entry_priv_pci *entry_priv; struct txdone_entry_desc txdesc; u32 word; u32 reg; int type; int index; int i; /* * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO * at most X times and also stop processing once the TX_STA_FIFO_VALID * flag is not set anymore. * * The legacy drivers use X=TX_RING_SIZE but state in a comment * that the TX_STA_FIFO stack has a size of 16. We stick to our * tx ring size for now. */ for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) { rt2x00pci_register_read(rt2x00dev, STA_CSR4, ®); if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) break; /* * Skip this entry when it contains an invalid * queue identication number. */ type = rt2x00_get_field32(reg, STA_CSR4_PID_TYPE); queue = rt2x00queue_get_tx_queue(rt2x00dev, type); if (unlikely(!queue)) continue; /* * Skip this entry when it contains an invalid * index number. */ index = rt2x00_get_field32(reg, STA_CSR4_PID_SUBTYPE); if (unlikely(index >= queue->limit)) continue; entry = &queue->entries[index]; entry_priv = entry->priv_data; rt2x00_desc_read(entry_priv->desc, 0, &word); if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || !rt2x00_get_field32(word, TXD_W0_VALID)) return; entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); while (entry != entry_done) { /* Catch up. * Just report any entries we missed as failed. */ WARNING(rt2x00dev, "TX status report missed for entry %d\n", entry_done->entry_idx); rt2x00lib_txdone_noinfo(entry_done, TXDONE_UNKNOWN); entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); } /* * Obtain the status about this packet. */ txdesc.flags = 0; switch (rt2x00_get_field32(reg, STA_CSR4_TX_RESULT)) { case 0: /* Success, maybe with retry */ __set_bit(TXDONE_SUCCESS, &txdesc.flags); break; case 6: /* Failure, excessive retries */ __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); /* Don't break, this is a failed frame! */ default: /* Failure */ __set_bit(TXDONE_FAILURE, &txdesc.flags); } txdesc.retry = rt2x00_get_field32(reg, STA_CSR4_RETRY_COUNT); /* * the frame was retried at least once * -> hw used fallback rates */ if (txdesc.retry) __set_bit(TXDONE_FALLBACK, &txdesc.flags); rt2x00lib_txdone(entry, &txdesc); } } static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev) { struct rt2x00lib_conf libconf = { .conf = &rt2x00dev->hw->conf }; rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); } static inline void rt61pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, struct rt2x00_field32 irq_field) { u32 reg; /* * Enable a single interrupt. The interrupt mask register * access needs locking. */ spin_lock_irq(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); rt2x00_set_field32(®, irq_field, 0); rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); spin_unlock_irq(&rt2x00dev->irqmask_lock); } static void rt61pci_enable_mcu_interrupt(struct rt2x00_dev *rt2x00dev, struct rt2x00_field32 irq_field) { u32 reg; /* * Enable a single MCU interrupt. The interrupt mask register * access needs locking. */ spin_lock_irq(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); rt2x00_set_field32(®, irq_field, 0); rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); spin_unlock_irq(&rt2x00dev->irqmask_lock); } static void rt61pci_txstatus_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; rt61pci_txdone(rt2x00dev); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE); } static void rt61pci_tbtt_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; rt2x00lib_beacondone(rt2x00dev); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE); } static void rt61pci_rxdone_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; if (rt2x00pci_rxdone(rt2x00dev)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RXDONE); } static void rt61pci_autowake_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; rt61pci_wakeup(rt2x00dev); rt2x00pci_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP); } static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; u32 reg_mcu, mask_mcu; u32 reg, mask; /* * Get the interrupt sources & saved to local variable. * Write register value back to clear pending interrupts. */ rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®_mcu); rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg_mcu); rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, ®); rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg); if (!reg && !reg_mcu) return IRQ_NONE; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return IRQ_HANDLED; /* * Schedule tasklets for interrupt handling. */ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) tasklet_schedule(&rt2x00dev->txstatus_tasklet); if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP)) tasklet_schedule(&rt2x00dev->autowake_tasklet); /* * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits * for interrupts and interrupt masks we can just use the value of * INT_SOURCE_CSR to create the interrupt mask. */ mask = reg; mask_mcu = reg_mcu; /* * Disable all interrupts for which a tasklet was scheduled right now, * the tasklet will reenable the appropriate interrupts. */ spin_lock(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); reg |= mask; rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); reg |= mask_mcu; rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); spin_unlock(&rt2x00dev->irqmask_lock); return IRQ_HANDLED; } /* * Device probe functions. */ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) { struct eeprom_93cx6 eeprom; u32 reg; u16 word; u8 *mac; s8 value; rt2x00pci_register_read(rt2x00dev, E2PROM_CSR, ®); eeprom.data = rt2x00dev; eeprom.register_read = rt61pci_eepromregister_read; eeprom.register_write = rt61pci_eepromregister_write; eeprom.width = rt2x00_get_field32(reg, E2PROM_CSR_TYPE_93C46) ? PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; eeprom.reg_data_in = 0; eeprom.reg_data_out = 0; eeprom.reg_data_clock = 0; eeprom.reg_chip_select = 0; eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, EEPROM_SIZE / sizeof(u16)); /* * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { eth_random_addr(mac); EEPROM(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, ANTENNA_B); rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, ANTENNA_B); rt2x00_set_field16(&word, EEPROM_ANTENNA_FRAME_TYPE, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5225); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_ENABLE_DIVERSITY, 0); rt2x00_set_field16(&word, EEPROM_NIC_TX_DIVERSITY, 0); rt2x00_set_field16(&word, EEPROM_NIC_RX_FIXED, 0); rt2x00_set_field16(&word, EEPROM_NIC_TX_FIXED, 0); rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0); rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, LED_MODE_DEFAULT); rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); EEPROM(rt2x00dev, "Led: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); EEPROM(rt2x00dev, "Freq: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); EEPROM(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_2); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); EEPROM(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_2); if (value < -10 || value > 10) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); } return 0; } static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 value; u16 eeprom; /* * Read EEPROM word for configuration. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); /* * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); if (!rt2x00_rf(rt2x00dev, RF5225) && !rt2x00_rf(rt2x00dev, RF5325) && !rt2x00_rf(rt2x00dev, RF2527) && !rt2x00_rf(rt2x00dev, RF2529)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } /* * Determine number of antennas. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_NUM) == 2) __set_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags); /* * Identify default antenna configuration. */ rt2x00dev->default_ant.tx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); rt2x00dev->default_ant.rx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); /* * Read the Frame type. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); /* * Detect if this device has a hardware controlled radio. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Read frequency offset and RF programming sequence. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_FREQ_SEQ)) __set_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags); rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); /* * Read external LNA informations. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A)) __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG)) __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); /* * When working with a RF2529 chip without double antenna, * the antenna settings should be gathered from the NIC * eeprom word. */ if (rt2x00_rf(rt2x00dev, RF2529) && !test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) { rt2x00dev->default_ant.rx = ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); rt2x00dev->default_ant.tx = ANTENNA_B - rt2x00_get_field16(eeprom, EEPROM_NIC_TX_FIXED); if (rt2x00_get_field16(eeprom, EEPROM_NIC_TX_DIVERSITY)) rt2x00dev->default_ant.tx = ANTENNA_SW_DIVERSITY; if (rt2x00_get_field16(eeprom, EEPROM_NIC_ENABLE_DIVERSITY)) rt2x00dev->default_ant.rx = ANTENNA_SW_DIVERSITY; } /* * Store led settings, for correct led behaviour. * If the eeprom value is invalid, * switch to default led mode. */ #ifdef CONFIG_RT2X00_LIB_LEDS rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); value = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE); rt61pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); rt61pci_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); if (value == LED_MODE_SIGNAL_STRENGTH) rt61pci_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_0)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_1)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_2)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_3)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_4)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_G)); rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_A)); #endif /* CONFIG_RT2X00_LIB_LEDS */ return 0; } /* * RF value list for RF5225 & RF5325 * Supports: 2.4 GHz & 5.2 GHz, rf_sequence disabled */ static const struct rf_channel rf_vals_noseq[] = { { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, /* 802.11 UNI / HyperLan 2 */ { 36, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa23 }, { 40, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa03 }, { 44, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa0b }, { 48, 0x00002ccc, 0x000049aa, 0x0009be55, 0x000ffa13 }, { 52, 0x00002ccc, 0x000049ae, 0x0009ae55, 0x000ffa1b }, { 56, 0x00002ccc, 0x000049b2, 0x0009ae55, 0x000ffa23 }, { 60, 0x00002ccc, 0x000049ba, 0x0009ae55, 0x000ffa03 }, { 64, 0x00002ccc, 0x000049be, 0x0009ae55, 0x000ffa0b }, /* 802.11 HyperLan 2 */ { 100, 0x00002ccc, 0x00004a2a, 0x000bae55, 0x000ffa03 }, { 104, 0x00002ccc, 0x00004a2e, 0x000bae55, 0x000ffa0b }, { 108, 0x00002ccc, 0x00004a32, 0x000bae55, 0x000ffa13 }, { 112, 0x00002ccc, 0x00004a36, 0x000bae55, 0x000ffa1b }, { 116, 0x00002ccc, 0x00004a3a, 0x000bbe55, 0x000ffa23 }, { 120, 0x00002ccc, 0x00004a82, 0x000bbe55, 0x000ffa03 }, { 124, 0x00002ccc, 0x00004a86, 0x000bbe55, 0x000ffa0b }, { 128, 0x00002ccc, 0x00004a8a, 0x000bbe55, 0x000ffa13 }, { 132, 0x00002ccc, 0x00004a8e, 0x000bbe55, 0x000ffa1b }, { 136, 0x00002ccc, 0x00004a92, 0x000bbe55, 0x000ffa23 }, /* 802.11 UNII */ { 140, 0x00002ccc, 0x00004a9a, 0x000bbe55, 0x000ffa03 }, { 149, 0x00002ccc, 0x00004aa2, 0x000bbe55, 0x000ffa1f }, { 153, 0x00002ccc, 0x00004aa6, 0x000bbe55, 0x000ffa27 }, { 157, 0x00002ccc, 0x00004aae, 0x000bbe55, 0x000ffa07 }, { 161, 0x00002ccc, 0x00004ab2, 0x000bbe55, 0x000ffa0f }, { 165, 0x00002ccc, 0x00004ab6, 0x000bbe55, 0x000ffa17 }, /* MMAC(Japan)J52 ch 34,38,42,46 */ { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa0b }, { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000ffa13 }, { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa1b }, { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa23 }, }; /* * RF value list for RF5225 & RF5325 * Supports: 2.4 GHz & 5.2 GHz, rf_sequence enabled */ static const struct rf_channel rf_vals_seq[] = { { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, /* 802.11 UNI / HyperLan 2 */ { 36, 0x00002cd4, 0x0004481a, 0x00098455, 0x000c0a03 }, { 40, 0x00002cd0, 0x00044682, 0x00098455, 0x000c0a03 }, { 44, 0x00002cd0, 0x00044686, 0x00098455, 0x000c0a1b }, { 48, 0x00002cd0, 0x0004468e, 0x00098655, 0x000c0a0b }, { 52, 0x00002cd0, 0x00044692, 0x00098855, 0x000c0a23 }, { 56, 0x00002cd0, 0x0004469a, 0x00098c55, 0x000c0a13 }, { 60, 0x00002cd0, 0x000446a2, 0x00098e55, 0x000c0a03 }, { 64, 0x00002cd0, 0x000446a6, 0x00099255, 0x000c0a1b }, /* 802.11 HyperLan 2 */ { 100, 0x00002cd4, 0x0004489a, 0x000b9855, 0x000c0a03 }, { 104, 0x00002cd4, 0x000448a2, 0x000b9855, 0x000c0a03 }, { 108, 0x00002cd4, 0x000448aa, 0x000b9855, 0x000c0a03 }, { 112, 0x00002cd4, 0x000448b2, 0x000b9a55, 0x000c0a03 }, { 116, 0x00002cd4, 0x000448ba, 0x000b9a55, 0x000c0a03 }, { 120, 0x00002cd0, 0x00044702, 0x000b9a55, 0x000c0a03 }, { 124, 0x00002cd0, 0x00044706, 0x000b9a55, 0x000c0a1b }, { 128, 0x00002cd0, 0x0004470e, 0x000b9c55, 0x000c0a0b }, { 132, 0x00002cd0, 0x00044712, 0x000b9c55, 0x000c0a23 }, { 136, 0x00002cd0, 0x0004471a, 0x000b9e55, 0x000c0a13 }, /* 802.11 UNII */ { 140, 0x00002cd0, 0x00044722, 0x000b9e55, 0x000c0a03 }, { 149, 0x00002cd0, 0x0004472e, 0x000ba255, 0x000c0a1b }, { 153, 0x00002cd0, 0x00044736, 0x000ba255, 0x000c0a0b }, { 157, 0x00002cd4, 0x0004490a, 0x000ba255, 0x000c0a17 }, { 161, 0x00002cd4, 0x00044912, 0x000ba255, 0x000c0a17 }, { 165, 0x00002cd4, 0x0004491a, 0x000ba255, 0x000c0a17 }, /* MMAC(Japan)J52 ch 34,38,42,46 */ { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000c0a0b }, { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000c0a13 }, { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000c0a1b }, { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000c0a23 }, }; static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *tx_power; unsigned int i; /* * Disable powersaving as default. */ rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; /* * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* * As rt61 has a global fallback table we cannot specify * more then one tx rate per frame but since the hw will * try several rates (based on the fallback table) we should * initialize max_report_rates to the maximum number of rates * we are going to try. Otherwise mac80211 will truncate our * reported tx rates and the rc algortihm will end up with * incorrect data. */ rt2x00dev->hw->max_rates = 1; rt2x00dev->hw->max_report_rates = 7; rt2x00dev->hw->max_rate_tries = 1; /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; if (!test_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags)) { spec->num_channels = 14; spec->channels = rf_vals_noseq; } else { spec->num_channels = 14; spec->channels = rf_vals_seq; } if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_seq); } /* * Create channel information array */ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; spec->channels_info = info; tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); for (i = 0; i < 14; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } if (spec->num_channels > 14) { tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); for (i = 14; i < spec->num_channels; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } } return 0; } static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; u32 reg; /* * Disable power saving. */ rt2x00pci_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000007); /* * Allocate eeprom data. */ retval = rt61pci_validate_eeprom(rt2x00dev); if (retval) return retval; retval = rt61pci_init_eeprom(rt2x00dev); if (retval) return retval; /* * Enable rfkill polling by setting GPIO direction of the * rfkill switch GPIO pin correctly. */ rt2x00pci_register_read(rt2x00dev, MAC_CSR13, ®); rt2x00_set_field32(®, MAC_CSR13_DIR5, 1); rt2x00pci_register_write(rt2x00dev, MAC_CSR13, reg); /* * Initialize hw specifications. */ retval = rt61pci_probe_hw_mode(rt2x00dev); if (retval) return retval; /* * This device has multiple filters for control frames, * but has no a separate filter for PS Poll frames. */ __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); /* * This device requires firmware and DMA mapped skbs. */ __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); if (!modparam_nohwcrypt) __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); /* * Set the rssi offset. */ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; return 0; } /* * IEEE80211 stack callback functions. */ static int rt61pci_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; struct rt2x00_field32 field; int retval; u32 reg; u32 offset; /* * First pass the configuration through rt2x00lib, that will * update the queue settings and validate the input. After that * we are free to update the registers based on the value * in the queue parameter. */ retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; /* * We only need to perform additional register initialization * for WMM queues. */ if (queue_idx >= 4) return 0; queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); /* Update WMM TXOP register */ offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); field.bit_offset = (queue_idx & 1) * 16; field.bit_mask = 0xffff << field.bit_offset; rt2x00pci_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, field, queue->txop); rt2x00pci_register_write(rt2x00dev, offset, reg); /* Update WMM registers */ field.bit_offset = queue_idx * 4; field.bit_mask = 0xf << field.bit_offset; rt2x00pci_register_read(rt2x00dev, AIFSN_CSR, ®); rt2x00_set_field32(®, field, queue->aifs); rt2x00pci_register_write(rt2x00dev, AIFSN_CSR, reg); rt2x00pci_register_read(rt2x00dev, CWMIN_CSR, ®); rt2x00_set_field32(®, field, queue->cw_min); rt2x00pci_register_write(rt2x00dev, CWMIN_CSR, reg); rt2x00pci_register_read(rt2x00dev, CWMAX_CSR, ®); rt2x00_set_field32(®, field, queue->cw_max); rt2x00pci_register_write(rt2x00dev, CWMAX_CSR, reg); return 0; } static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; rt2x00pci_register_read(rt2x00dev, TXRX_CSR13, ®); tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; rt2x00pci_register_read(rt2x00dev, TXRX_CSR12, ®); tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); return tsf; } static const struct ieee80211_ops rt61pci_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, .set_key = rt2x00mac_set_key, .sw_scan_start = rt2x00mac_sw_scan_start, .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt61pci_conf_tx, .get_tsf = rt61pci_get_tsf, .rfkill_poll = rt2x00mac_rfkill_poll, .flush = rt2x00mac_flush, .set_antenna = rt2x00mac_set_antenna, .get_antenna = rt2x00mac_get_antenna, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { .irq_handler = rt61pci_interrupt, .txstatus_tasklet = rt61pci_txstatus_tasklet, .tbtt_tasklet = rt61pci_tbtt_tasklet, .rxdone_tasklet = rt61pci_rxdone_tasklet, .autowake_tasklet = rt61pci_autowake_tasklet, .probe_hw = rt61pci_probe_hw, .get_firmware_name = rt61pci_get_firmware_name, .check_firmware = rt61pci_check_firmware, .load_firmware = rt61pci_load_firmware, .initialize = rt2x00pci_initialize, .uninitialize = rt2x00pci_uninitialize, .get_entry_state = rt61pci_get_entry_state, .clear_entry = rt61pci_clear_entry, .set_device_state = rt61pci_set_device_state, .rfkill_poll = rt61pci_rfkill_poll, .link_stats = rt61pci_link_stats, .reset_tuner = rt61pci_reset_tuner, .link_tuner = rt61pci_link_tuner, .start_queue = rt61pci_start_queue, .kick_queue = rt61pci_kick_queue, .stop_queue = rt61pci_stop_queue, .flush_queue = rt2x00pci_flush_queue, .write_tx_desc = rt61pci_write_tx_desc, .write_beacon = rt61pci_write_beacon, .clear_beacon = rt61pci_clear_beacon, .fill_rxdone = rt61pci_fill_rxdone, .config_shared_key = rt61pci_config_shared_key, .config_pairwise_key = rt61pci_config_pairwise_key, .config_filter = rt61pci_config_filter, .config_intf = rt61pci_config_intf, .config_erp = rt61pci_config_erp, .config_ant = rt61pci_config_ant, .config = rt61pci_config, }; static const struct data_queue_desc rt61pci_queue_rx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = RXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt61pci_queue_tx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt61pci_queue_bcn = { .entry_num = 4, .data_size = 0, /* No DMA required for beacons */ .desc_size = TXINFO_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct rt2x00_ops rt61pci_ops = { .name = KBUILD_MODNAME, .max_sta_intf = 1, .max_ap_intf = 4, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = 0, .rx = &rt61pci_queue_rx, .tx = &rt61pci_queue_tx, .bcn = &rt61pci_queue_bcn, .lib = &rt61pci_rt2x00_ops, .hw = &rt61pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS .debugfs = &rt61pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * RT61pci module information. */ static DEFINE_PCI_DEVICE_TABLE(rt61pci_device_table) = { /* RT2561s */ { PCI_DEVICE(0x1814, 0x0301) }, /* RT2561 v2 */ { PCI_DEVICE(0x1814, 0x0302) }, /* RT2661 */ { PCI_DEVICE(0x1814, 0x0401) }, { 0, } }; MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT61 PCI & PCMCIA Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2561, RT2561s & RT2661 " "PCI & PCMCIA chipset based cards"); MODULE_DEVICE_TABLE(pci, rt61pci_device_table); MODULE_FIRMWARE(FIRMWARE_RT2561); MODULE_FIRMWARE(FIRMWARE_RT2561s); MODULE_FIRMWARE(FIRMWARE_RT2661); MODULE_LICENSE("GPL"); static int rt61pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { return rt2x00pci_probe(pci_dev, &rt61pci_ops); } static struct pci_driver rt61pci_driver = { .name = KBUILD_MODNAME, .id_table = rt61pci_device_table, .probe = rt61pci_probe, .remove = __devexit_p(rt2x00pci_remove), .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; module_pci_driver(rt61pci_driver); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00usb.h0000644000175000017500000003442012026211315023445 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00usb Abstract: Data structures for the rt2x00usb module. */ #ifndef RT2X00USB_H #define RT2X00USB_H #include #define to_usb_device_intf(d) \ ({ \ struct usb_interface *intf = to_usb_interface(d); \ interface_to_usbdev(intf); \ }) /* * For USB vendor requests we need to pass a timeout * time in ms, for this we use the REGISTER_TIMEOUT, * however when loading firmware a higher value is * required. In that case we use the REGISTER_TIMEOUT_FIRMWARE. */ #define REGISTER_TIMEOUT 500 #define REGISTER_TIMEOUT_FIRMWARE 1000 /** * REGISTER_TIMEOUT16 - Determine the timeout for 16bit register access * @__datalen: Data length */ #define REGISTER_TIMEOUT16(__datalen) \ ( REGISTER_TIMEOUT * ((__datalen) / sizeof(u16)) ) /** * REGISTER_TIMEOUT32 - Determine the timeout for 32bit register access * @__datalen: Data length */ #define REGISTER_TIMEOUT32(__datalen) \ ( REGISTER_TIMEOUT * ((__datalen) / sizeof(u32)) ) /* * Cache size */ #define CSR_CACHE_SIZE 64 /* * USB request types. */ #define USB_VENDOR_REQUEST ( USB_TYPE_VENDOR | USB_RECIP_DEVICE ) #define USB_VENDOR_REQUEST_IN ( USB_DIR_IN | USB_VENDOR_REQUEST ) #define USB_VENDOR_REQUEST_OUT ( USB_DIR_OUT | USB_VENDOR_REQUEST ) /** * enum rt2x00usb_vendor_request: USB vendor commands. */ enum rt2x00usb_vendor_request { USB_DEVICE_MODE = 1, USB_SINGLE_WRITE = 2, USB_SINGLE_READ = 3, USB_MULTI_WRITE = 6, USB_MULTI_READ = 7, USB_EEPROM_WRITE = 8, USB_EEPROM_READ = 9, USB_LED_CONTROL = 10, /* RT73USB */ USB_RX_CONTROL = 12, }; /** * enum rt2x00usb_mode_offset: Device modes offset. */ enum rt2x00usb_mode_offset { USB_MODE_RESET = 1, USB_MODE_UNPLUG = 2, USB_MODE_FUNCTION = 3, USB_MODE_TEST = 4, USB_MODE_SLEEP = 7, /* RT73USB */ USB_MODE_FIRMWARE = 8, /* RT73USB */ USB_MODE_WAKEUP = 9, /* RT73USB */ }; /** * rt2x00usb_vendor_request - Send register command to device * @rt2x00dev: Pointer to &struct rt2x00_dev * @request: USB vendor command (See &enum rt2x00usb_vendor_request) * @requesttype: Request type &USB_VENDOR_REQUEST_* * @offset: Register offset to perform action on * @value: Value to write to device * @buffer: Buffer where information will be read/written to by device * @buffer_length: Size of &buffer * @timeout: Operation timeout * * This is the main function to communicate with the device, * the &buffer argument _must_ either be NULL or point to * a buffer allocated by kmalloc. Failure to do so can lead * to unexpected behavior depending on the architecture. */ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, const u16 offset, const u16 value, void *buffer, const u16 buffer_length, const int timeout); /** * rt2x00usb_vendor_request_buff - Send register command to device (buffered) * @rt2x00dev: Pointer to &struct rt2x00_dev * @request: USB vendor command (See &enum rt2x00usb_vendor_request) * @requesttype: Request type &USB_VENDOR_REQUEST_* * @offset: Register offset to perform action on * @buffer: Buffer where information will be read/written to by device * @buffer_length: Size of &buffer * @timeout: Operation timeout * * This function will use a previously with kmalloc allocated cache * to communicate with the device. The contents of the buffer pointer * will be copied to this cache when writing, or read from the cache * when reading. * Buffers send to &rt2x00usb_vendor_request _must_ be allocated with * kmalloc. Hence the reason for using a previously allocated cache * which has been allocated properly. */ int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, const u16 offset, void *buffer, const u16 buffer_length, const int timeout); /** * rt2x00usb_vendor_request_buff - Send register command to device (buffered) * @rt2x00dev: Pointer to &struct rt2x00_dev * @request: USB vendor command (See &enum rt2x00usb_vendor_request) * @requesttype: Request type &USB_VENDOR_REQUEST_* * @offset: Register offset to perform action on * @buffer: Buffer where information will be read/written to by device * @buffer_length: Size of &buffer * @timeout: Operation timeout * * A version of &rt2x00usb_vendor_request_buff which must be called * if the usb_cache_mutex is already held. */ int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, const u16 offset, void *buffer, const u16 buffer_length, const int timeout); /** * rt2x00usb_vendor_request_sw - Send single register command to device * @rt2x00dev: Pointer to &struct rt2x00_dev * @request: USB vendor command (See &enum rt2x00usb_vendor_request) * @offset: Register offset to perform action on * @value: Value to write to device * @timeout: Operation timeout * * Simple wrapper around rt2x00usb_vendor_request to write a single * command to the device. Since we don't use the buffer argument we * don't have to worry about kmalloc here. */ static inline int rt2x00usb_vendor_request_sw(struct rt2x00_dev *rt2x00dev, const u8 request, const u16 offset, const u16 value, const int timeout) { return rt2x00usb_vendor_request(rt2x00dev, request, USB_VENDOR_REQUEST_OUT, offset, value, NULL, 0, timeout); } /** * rt2x00usb_eeprom_read - Read eeprom from device * @rt2x00dev: Pointer to &struct rt2x00_dev * @eeprom: Pointer to eeprom array to store the information in * @length: Number of bytes to read from the eeprom * * Simple wrapper around rt2x00usb_vendor_request to read the eeprom * from the device. Note that the eeprom argument _must_ be allocated using * kmalloc for correct handling inside the kernel USB layer. */ static inline int rt2x00usb_eeprom_read(struct rt2x00_dev *rt2x00dev, __le16 *eeprom, const u16 length) { return rt2x00usb_vendor_request(rt2x00dev, USB_EEPROM_READ, USB_VENDOR_REQUEST_IN, 0, 0, eeprom, length, REGISTER_TIMEOUT16(length)); } /** * rt2x00usb_register_read - Read 32bit register word * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @value: Pointer to where register contents should be stored * * This function is a simple wrapper for 32bit register access * through rt2x00usb_vendor_request_buff(). */ static inline void rt2x00usb_register_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { __le32 reg; rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, ®, sizeof(reg), REGISTER_TIMEOUT); *value = le32_to_cpu(reg); } /** * rt2x00usb_register_read_lock - Read 32bit register word * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @value: Pointer to where register contents should be stored * * This function is a simple wrapper for 32bit register access * through rt2x00usb_vendor_req_buff_lock(). */ static inline void rt2x00usb_register_read_lock(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { __le32 reg; rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, ®, sizeof(reg), REGISTER_TIMEOUT); *value = le32_to_cpu(reg); } /** * rt2x00usb_register_multiread - Read 32bit register words * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @value: Pointer to where register contents should be stored * @length: Length of the data * * This function is a simple wrapper for 32bit register access * through rt2x00usb_vendor_request_buff(). */ static inline void rt2x00usb_register_multiread(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u32 length) { rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, value, length, REGISTER_TIMEOUT32(length)); } /** * rt2x00usb_register_write - Write 32bit register word * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @value: Data which should be written * * This function is a simple wrapper for 32bit register access * through rt2x00usb_vendor_request_buff(). */ static inline void rt2x00usb_register_write(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value) { __le32 reg = cpu_to_le32(value); rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, ®, sizeof(reg), REGISTER_TIMEOUT); } /** * rt2x00usb_register_write_lock - Write 32bit register word * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @value: Data which should be written * * This function is a simple wrapper for 32bit register access * through rt2x00usb_vendor_req_buff_lock(). */ static inline void rt2x00usb_register_write_lock(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value) { __le32 reg = cpu_to_le32(value); rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, ®, sizeof(reg), REGISTER_TIMEOUT); } /** * rt2x00usb_register_multiwrite - Write 32bit register words * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @value: Data which should be written * @length: Length of the data * * This function is a simple wrapper for 32bit register access * through rt2x00usb_vendor_request_buff(). */ static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const void *value, const u32 length) { rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, (void *)value, length, REGISTER_TIMEOUT32(length)); } /** * rt2x00usb_regbusy_read - Read from register with busy check * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @field: Field to check if register is busy * @reg: Pointer to where register contents should be stored * * This function will read the given register, and checks if the * register is busy. If it is, it will sleep for a couple of * microseconds before reading the register again. If the register * is not read after a certain timeout, this function will return * FALSE. */ int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const struct rt2x00_field32 field, u32 *reg); /** * rt2x00usb_register_read_async - Asynchronously read 32bit register word * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @callback: Functon to call when read completes. * * Submit a control URB to read a 32bit register. This safe to * be called from atomic context. The callback will be called * when the URB completes. Otherwise the function is similar * to rt2x00usb_register_read(). * When the callback function returns false, the memory will be cleaned up, * when it returns true, the urb will be fired again. */ void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, const unsigned int offset, bool (*callback)(struct rt2x00_dev*, int, u32)); /* * Radio handlers */ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev); /** * struct queue_entry_priv_usb: Per entry USB specific information * * @urb: Urb structure used for device communication. */ struct queue_entry_priv_usb { struct urb *urb; }; /** * struct queue_entry_priv_usb_bcn: Per TX entry USB specific information * * The first section should match &struct queue_entry_priv_usb exactly. * rt2500usb can use this structure to send a guardian byte when working * with beacons. * * @urb: Urb structure used for device communication. * @guardian_data: Set to 0, used for sending the guardian data. * @guardian_urb: Urb structure used to send the guardian data. */ struct queue_entry_priv_usb_bcn { struct urb *urb; unsigned int guardian_data; struct urb *guardian_urb; }; /** * rt2x00usb_kick_queue - Kick data queue * @queue: Data queue to kick * * This will walk through all entries of the queue and push all pending * frames to the hardware as a single burst. */ void rt2x00usb_kick_queue(struct data_queue *queue); /** * rt2x00usb_flush_queue - Flush data queue * @queue: Data queue to stop * @drop: True to drop all pending frames. * * This will walk through all entries of the queue and will optionally * kill all URB's which were send to the device, or at least wait until * they have been returned from the device.. */ void rt2x00usb_flush_queue(struct data_queue *queue, bool drop); /** * rt2x00usb_watchdog - Watchdog for USB communication * @rt2x00dev: Pointer to &struct rt2x00_dev * * Check the health of the USB communication and determine * if timeouts have occurred. If this is the case, this function * will reset all communication to restore functionality again. */ void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev); /* * Device initialization handlers. */ void rt2x00usb_clear_entry(struct queue_entry *entry); int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev); void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev); /* * USB driver handlers. */ int rt2x00usb_probe(struct usb_interface *usb_intf, const struct rt2x00_ops *ops); void rt2x00usb_disconnect(struct usb_interface *usb_intf); #ifdef CONFIG_PM int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state); int rt2x00usb_resume(struct usb_interface *usb_intf); #else #define rt2x00usb_suspend NULL #define rt2x00usb_resume NULL #endif /* CONFIG_PM */ #endif /* RT2X00USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00usb.c0000644000175000017500000005352312026211315023445 0ustar mcgrofmcgrof/* Copyright (C) 2010 Willow Garage Copyright (C) 2004 - 2010 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00usb Abstract: rt2x00 generic usb device routines. */ #include #include #include #include #include #include "rt2x00.h" #include "rt2x00usb.h" /* * Interfacing with the HW. */ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, const u16 offset, const u16 value, void *buffer, const u16 buffer_length, const int timeout) { struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); int status; unsigned int i; unsigned int pipe = (requesttype == USB_VENDOR_REQUEST_IN) ? usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return -ENODEV; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { status = usb_control_msg(usb_dev, pipe, request, requesttype, value, offset, buffer, buffer_length, timeout); if (status >= 0) return 0; /* * Check for errors * -ENODEV: Device has disappeared, no point continuing. * All other errors: Try again. */ else if (status == -ENODEV) { clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); break; } } ERROR(rt2x00dev, "Vendor Request 0x%02x failed for offset 0x%04x with error %d.\n", request, offset, status); return status; } EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request); int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, const u16 offset, void *buffer, const u16 buffer_length, const int timeout) { int status; BUG_ON(!mutex_is_locked(&rt2x00dev->csr_mutex)); /* * Check for Cache availability. */ if (unlikely(!rt2x00dev->csr.cache || buffer_length > CSR_CACHE_SIZE)) { ERROR(rt2x00dev, "CSR cache not available.\n"); return -ENOMEM; } if (requesttype == USB_VENDOR_REQUEST_OUT) memcpy(rt2x00dev->csr.cache, buffer, buffer_length); status = rt2x00usb_vendor_request(rt2x00dev, request, requesttype, offset, 0, rt2x00dev->csr.cache, buffer_length, timeout); if (!status && requesttype == USB_VENDOR_REQUEST_IN) memcpy(buffer, rt2x00dev->csr.cache, buffer_length); return status; } EXPORT_SYMBOL_GPL(rt2x00usb_vendor_req_buff_lock); int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, const u16 offset, void *buffer, const u16 buffer_length, const int timeout) { int status = 0; unsigned char *tb; u16 off, len, bsize; mutex_lock(&rt2x00dev->csr_mutex); tb = (char *)buffer; off = offset; len = buffer_length; while (len && !status) { bsize = min_t(u16, CSR_CACHE_SIZE, len); status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request, requesttype, off, tb, bsize, timeout); tb += bsize; len -= bsize; off += bsize; } mutex_unlock(&rt2x00dev->csr_mutex); return status; } EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const struct rt2x00_field32 field, u32 *reg) { unsigned int i; if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return -ENODEV; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00usb_register_read_lock(rt2x00dev, offset, reg); if (!rt2x00_get_field32(*reg, field)) return 1; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "Indirect register access failed: " "offset=0x%.08x, value=0x%.08x\n", offset, *reg); *reg = ~0; return 0; } EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read); struct rt2x00_async_read_data { __le32 reg; struct usb_ctrlrequest cr; struct rt2x00_dev *rt2x00dev; bool (*callback)(struct rt2x00_dev *, int, u32); }; static void rt2x00usb_register_read_async_cb(struct urb *urb) { struct rt2x00_async_read_data *rd = urb->context; if (rd->callback(rd->rt2x00dev, urb->status, le32_to_cpu(rd->reg))) { if (usb_submit_urb(urb, GFP_ATOMIC) < 0) kfree(rd); } else kfree(rd); } void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, const unsigned int offset, bool (*callback)(struct rt2x00_dev*, int, u32)) { struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); struct urb *urb; struct rt2x00_async_read_data *rd; rd = kmalloc(sizeof(*rd), GFP_ATOMIC); if (!rd) return; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { kfree(rd); return; } rd->rt2x00dev = rt2x00dev; rd->callback = callback; rd->cr.bRequestType = USB_VENDOR_REQUEST_IN; rd->cr.bRequest = USB_MULTI_READ; rd->cr.wValue = 0; rd->cr.wIndex = cpu_to_le16(offset); rd->cr.wLength = cpu_to_le16(sizeof(u32)); usb_fill_control_urb(urb, usb_dev, usb_rcvctrlpipe(usb_dev, 0), (unsigned char *)(&rd->cr), &rd->reg, sizeof(rd->reg), rt2x00usb_register_read_async_cb, rd); if (usb_submit_urb(urb, GFP_ATOMIC) < 0) kfree(rd); usb_free_urb(urb); } EXPORT_SYMBOL_GPL(rt2x00usb_register_read_async); /* * TX data handlers. */ static void rt2x00usb_work_txdone_entry(struct queue_entry *entry) { /* * If the transfer to hardware succeeded, it does not mean the * frame was send out correctly. It only means the frame * was successfully pushed to the hardware, we have no * way to determine the transmission status right now. * (Only indirectly by looking at the failed TX counters * in the register). */ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); else rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); } static void rt2x00usb_work_txdone(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, txdone_work); struct data_queue *queue; struct queue_entry *entry; tx_queue_for_each(rt2x00dev, queue) { while (!rt2x00queue_empty(queue)) { entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) break; rt2x00usb_work_txdone_entry(entry); } } } static void rt2x00usb_interrupt_txdone(struct urb *urb) { struct queue_entry *entry = (struct queue_entry *)urb->context; struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return; /* * Check if the frame was correctly uploaded */ if (urb->status) set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); /* * Report the frame as DMA done */ rt2x00lib_dmadone(entry); if (rt2x00dev->ops->lib->tx_dma_done) rt2x00dev->ops->lib->tx_dma_done(entry); /* * Schedule the delayed work for reading the TX status * from the device. */ if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) || !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); } static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); struct queue_entry_priv_usb *entry_priv = entry->priv_data; u32 length; int status; if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags) || test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) return false; /* * USB devices require certain padding at the end of each frame * and urb. Those paddings are not included in skbs. Pass entry * to the driver to determine what the overall length should be. */ length = rt2x00dev->ops->lib->get_tx_data_len(entry); status = skb_padto(entry->skb, length); if (unlikely(status)) { /* TODO: report something more appropriate than IO_FAILED. */ WARNING(rt2x00dev, "TX SKB padding error, out of memory\n"); set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); rt2x00lib_dmadone(entry); return false; } usb_fill_bulk_urb(entry_priv->urb, usb_dev, usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint), entry->skb->data, length, rt2x00usb_interrupt_txdone, entry); status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); if (status) { if (status == -ENODEV) clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); rt2x00lib_dmadone(entry); } return false; } /* * RX data handlers. */ static void rt2x00usb_work_rxdone(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, rxdone_work); struct queue_entry *entry; struct skb_frame_desc *skbdesc; u8 rxd[32]; while (!rt2x00queue_empty(rt2x00dev->rx)) { entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) break; /* * Fill in desc fields of the skb descriptor */ skbdesc = get_skb_frame_desc(entry->skb); skbdesc->desc = rxd; skbdesc->desc_len = entry->queue->desc_size; /* * Send the frame to rt2x00lib for further processing. */ rt2x00lib_rxdone(entry, GFP_KERNEL); } } static void rt2x00usb_interrupt_rxdone(struct urb *urb) { struct queue_entry *entry = (struct queue_entry *)urb->context; struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return; /* * Report the frame as DMA done */ rt2x00lib_dmadone(entry); /* * Check if the received data is simply too small * to be actually valid, or if the urb is signaling * a problem. */ if (urb->actual_length < entry->queue->desc_size || urb->status) set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); /* * Schedule the delayed work for reading the RX status * from the device. */ queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work); } static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); struct queue_entry_priv_usb *entry_priv = entry->priv_data; int status; if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) return false; rt2x00lib_dmastart(entry); usb_fill_bulk_urb(entry_priv->urb, usb_dev, usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint), entry->skb->data, entry->skb->len, rt2x00usb_interrupt_rxdone, entry); status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); if (status) { if (status == -ENODEV) clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); rt2x00lib_dmadone(entry); } return false; } void rt2x00usb_kick_queue(struct data_queue *queue) { switch (queue->qid) { case QID_AC_VO: case QID_AC_VI: case QID_AC_BE: case QID_AC_BK: if (!rt2x00queue_empty(queue)) rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, rt2x00usb_kick_tx_entry); break; case QID_RX: if (!rt2x00queue_full(queue)) rt2x00queue_for_each_entry(queue, Q_INDEX, Q_INDEX_DONE, rt2x00usb_kick_rx_entry); break; default: break; } } EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue); static bool rt2x00usb_flush_entry(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv = entry->priv_data; struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return false; usb_kill_urb(entry_priv->urb); /* * Kill guardian urb (if required by driver). */ if ((entry->queue->qid == QID_BEACON) && (test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags))) usb_kill_urb(bcn_priv->guardian_urb); return false; } void rt2x00usb_flush_queue(struct data_queue *queue, bool drop) { struct work_struct *completion; unsigned int i; if (drop) rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, rt2x00usb_flush_entry); /* * Obtain the queue completion handler */ switch (queue->qid) { case QID_AC_VO: case QID_AC_VI: case QID_AC_BE: case QID_AC_BK: completion = &queue->rt2x00dev->txdone_work; break; case QID_RX: completion = &queue->rt2x00dev->rxdone_work; break; default: return; } for (i = 0; i < 10; i++) { /* * Check if the driver is already done, otherwise we * have to sleep a little while to give the driver/hw * the oppurtunity to complete interrupt process itself. */ if (rt2x00queue_empty(queue)) break; /* * Schedule the completion handler manually, when this * worker function runs, it should cleanup the queue. */ queue_work(queue->rt2x00dev->workqueue, completion); /* * Wait for a little while to give the driver * the oppurtunity to recover itself. */ msleep(10); } } EXPORT_SYMBOL_GPL(rt2x00usb_flush_queue); static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) { WARNING(queue->rt2x00dev, "TX queue %d DMA timed out," " invoke forced forced reset\n", queue->qid); rt2x00queue_flush_queue(queue, true); } static int rt2x00usb_dma_timeout(struct data_queue *queue) { struct queue_entry *entry; entry = rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE); return rt2x00queue_dma_timeout(entry); } void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; tx_queue_for_each(rt2x00dev, queue) { if (!rt2x00queue_empty(queue)) { if (rt2x00usb_dma_timeout(queue)) rt2x00usb_watchdog_tx_dma(queue); } } } EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); /* * Radio handlers */ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) { rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0, REGISTER_TIMEOUT); } EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); /* * Device initialization handlers. */ void rt2x00usb_clear_entry(struct queue_entry *entry) { entry->flags = 0; if (entry->queue->qid == QID_RX) rt2x00usb_kick_rx_entry(entry); } EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry); static void rt2x00usb_assign_endpoint(struct data_queue *queue, struct usb_endpoint_descriptor *ep_desc) { struct usb_device *usb_dev = to_usb_device_intf(queue->rt2x00dev->dev); int pipe; queue->usb_endpoint = usb_endpoint_num(ep_desc); if (queue->qid == QID_RX) { pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint); queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0); } else { pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint); queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1); } if (!queue->usb_maxpacket) queue->usb_maxpacket = 1; } static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev) { struct usb_interface *intf = to_usb_interface(rt2x00dev->dev); struct usb_host_interface *intf_desc = intf->cur_altsetting; struct usb_endpoint_descriptor *ep_desc; struct data_queue *queue = rt2x00dev->tx; struct usb_endpoint_descriptor *tx_ep_desc = NULL; unsigned int i; /* * Walk through all available endpoints to search for "bulk in" * and "bulk out" endpoints. When we find such endpoints collect * the information we need from the descriptor and assign it * to the queue. */ for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { ep_desc = &intf_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(ep_desc)) { rt2x00usb_assign_endpoint(rt2x00dev->rx, ep_desc); } else if (usb_endpoint_is_bulk_out(ep_desc) && (queue != queue_end(rt2x00dev))) { rt2x00usb_assign_endpoint(queue, ep_desc); queue = queue_next(queue); tx_ep_desc = ep_desc; } } /* * At least 1 endpoint for RX and 1 endpoint for TX must be available. */ if (!rt2x00dev->rx->usb_endpoint || !rt2x00dev->tx->usb_endpoint) { ERROR(rt2x00dev, "Bulk-in/Bulk-out endpoints not found\n"); return -EPIPE; } /* * It might be possible not all queues have a dedicated endpoint. * Loop through all TX queues and copy the endpoint information * which we have gathered from already assigned endpoints. */ txall_queue_for_each(rt2x00dev, queue) { if (!queue->usb_endpoint) rt2x00usb_assign_endpoint(queue, tx_ep_desc); } return 0; } static int rt2x00usb_alloc_entries(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv; struct queue_entry_priv_usb_bcn *bcn_priv; unsigned int i; for (i = 0; i < queue->limit; i++) { entry_priv = queue->entries[i].priv_data; entry_priv->urb = usb_alloc_urb(0, GFP_KERNEL); if (!entry_priv->urb) return -ENOMEM; } /* * If this is not the beacon queue or * no guardian byte was required for the beacon, * then we are done. */ if (queue->qid != QID_BEACON || !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) return 0; for (i = 0; i < queue->limit; i++) { bcn_priv = queue->entries[i].priv_data; bcn_priv->guardian_urb = usb_alloc_urb(0, GFP_KERNEL); if (!bcn_priv->guardian_urb) return -ENOMEM; } return 0; } static void rt2x00usb_free_entries(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv; struct queue_entry_priv_usb_bcn *bcn_priv; unsigned int i; if (!queue->entries) return; for (i = 0; i < queue->limit; i++) { entry_priv = queue->entries[i].priv_data; usb_kill_urb(entry_priv->urb); usb_free_urb(entry_priv->urb); } /* * If this is not the beacon queue or * no guardian byte was required for the beacon, * then we are done. */ if (queue->qid != QID_BEACON || !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) return; for (i = 0; i < queue->limit; i++) { bcn_priv = queue->entries[i].priv_data; usb_kill_urb(bcn_priv->guardian_urb); usb_free_urb(bcn_priv->guardian_urb); } } int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; int status; /* * Find endpoints for each queue */ status = rt2x00usb_find_endpoints(rt2x00dev); if (status) goto exit; /* * Allocate DMA */ queue_for_each(rt2x00dev, queue) { status = rt2x00usb_alloc_entries(queue); if (status) goto exit; } return 0; exit: rt2x00usb_uninitialize(rt2x00dev); return status; } EXPORT_SYMBOL_GPL(rt2x00usb_initialize); void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; queue_for_each(rt2x00dev, queue) rt2x00usb_free_entries(queue); } EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); /* * USB driver handlers. */ static void rt2x00usb_free_reg(struct rt2x00_dev *rt2x00dev) { kfree(rt2x00dev->rf); rt2x00dev->rf = NULL; kfree(rt2x00dev->eeprom); rt2x00dev->eeprom = NULL; kfree(rt2x00dev->csr.cache); rt2x00dev->csr.cache = NULL; } static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) { rt2x00dev->csr.cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); if (!rt2x00dev->csr.cache) goto exit; rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); if (!rt2x00dev->eeprom) goto exit; rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); if (!rt2x00dev->rf) goto exit; return 0; exit: ERROR_PROBE("Failed to allocate registers.\n"); rt2x00usb_free_reg(rt2x00dev); return -ENOMEM; } int rt2x00usb_probe(struct usb_interface *usb_intf, const struct rt2x00_ops *ops) { struct usb_device *usb_dev = interface_to_usbdev(usb_intf); struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; usb_dev = usb_get_dev(usb_dev); usb_reset_device(usb_dev); hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { ERROR_PROBE("Failed to allocate hardware.\n"); retval = -ENOMEM; goto exit_put_device; } usb_set_intfdata(usb_intf, hw); rt2x00dev = hw->priv; rt2x00dev->dev = &usb_intf->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); retval = rt2x00usb_alloc_reg(rt2x00dev); if (retval) goto exit_free_device; retval = rt2x00lib_probe_dev(rt2x00dev); if (retval) goto exit_free_reg; return 0; exit_free_reg: rt2x00usb_free_reg(rt2x00dev); exit_free_device: ieee80211_free_hw(hw); exit_put_device: usb_put_dev(usb_dev); usb_set_intfdata(usb_intf, NULL); return retval; } EXPORT_SYMBOL_GPL(rt2x00usb_probe); void rt2x00usb_disconnect(struct usb_interface *usb_intf) { struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); struct rt2x00_dev *rt2x00dev = hw->priv; /* * Free all allocated data. */ rt2x00lib_remove_dev(rt2x00dev); rt2x00usb_free_reg(rt2x00dev); ieee80211_free_hw(hw); /* * Free the USB device data. */ usb_set_intfdata(usb_intf, NULL); usb_put_dev(interface_to_usbdev(usb_intf)); } EXPORT_SYMBOL_GPL(rt2x00usb_disconnect); #ifdef CONFIG_PM int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state) { struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); struct rt2x00_dev *rt2x00dev = hw->priv; return rt2x00lib_suspend(rt2x00dev, state); } EXPORT_SYMBOL_GPL(rt2x00usb_suspend); int rt2x00usb_resume(struct usb_interface *usb_intf) { struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); struct rt2x00_dev *rt2x00dev = hw->priv; return rt2x00lib_resume(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00usb_resume); #endif /* CONFIG_PM */ /* * rt2x00usb module information. */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("rt2x00 usb library"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00soc.h0000644000175000017500000000246712026211315023446 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00soc Abstract: Data structures for the rt2x00soc module. */ #ifndef RT2X00SOC_H #define RT2X00SOC_H /* * SoC driver handlers. */ int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops); int rt2x00soc_remove(struct platform_device *pdev); #ifdef CONFIG_PM int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state); int rt2x00soc_resume(struct platform_device *pdev); #else #define rt2x00soc_suspend NULL #define rt2x00soc_resume NULL #endif /* CONFIG_PM */ #endif /* RT2X00SOC_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00soc.c0000644000175000017500000000746412026211315023443 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn Copyright (C) 2004 - 2009 Felix Fietkau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00soc Abstract: rt2x00 generic soc device routines. */ #include #include #include #include #include #include "rt2x00.h" #include "rt2x00soc.h" static void rt2x00soc_free_reg(struct rt2x00_dev *rt2x00dev) { kfree(rt2x00dev->rf); rt2x00dev->rf = NULL; kfree(rt2x00dev->eeprom); rt2x00dev->eeprom = NULL; iounmap(rt2x00dev->csr.base); } static int rt2x00soc_alloc_reg(struct rt2x00_dev *rt2x00dev) { struct platform_device *pdev = to_platform_device(rt2x00dev->dev); struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; rt2x00dev->csr.base = ioremap(res->start, resource_size(res)); if (!rt2x00dev->csr.base) return -ENOMEM; rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); if (!rt2x00dev->eeprom) goto exit; rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); if (!rt2x00dev->rf) goto exit; return 0; exit: ERROR_PROBE("Failed to allocate registers.\n"); rt2x00soc_free_reg(rt2x00dev); return -ENOMEM; } int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops) { struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { ERROR_PROBE("Failed to allocate hardware.\n"); return -ENOMEM; } platform_set_drvdata(pdev, hw); rt2x00dev = hw->priv; rt2x00dev->dev = &pdev->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; rt2x00dev->irq = platform_get_irq(pdev, 0); rt2x00dev->name = pdev->dev.driver->name; rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); retval = rt2x00soc_alloc_reg(rt2x00dev); if (retval) goto exit_free_device; retval = rt2x00lib_probe_dev(rt2x00dev); if (retval) goto exit_free_reg; return 0; exit_free_reg: rt2x00soc_free_reg(rt2x00dev); exit_free_device: ieee80211_free_hw(hw); return retval; } EXPORT_SYMBOL_GPL(rt2x00soc_probe); int rt2x00soc_remove(struct platform_device *pdev) { struct ieee80211_hw *hw = platform_get_drvdata(pdev); struct rt2x00_dev *rt2x00dev = hw->priv; /* * Free all allocated data. */ rt2x00lib_remove_dev(rt2x00dev); rt2x00soc_free_reg(rt2x00dev); ieee80211_free_hw(hw); return 0; } EXPORT_SYMBOL_GPL(rt2x00soc_remove); #ifdef CONFIG_PM int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state) { struct ieee80211_hw *hw = platform_get_drvdata(pdev); struct rt2x00_dev *rt2x00dev = hw->priv; return rt2x00lib_suspend(rt2x00dev, state); } EXPORT_SYMBOL_GPL(rt2x00soc_suspend); int rt2x00soc_resume(struct platform_device *pdev) { struct ieee80211_hw *hw = platform_get_drvdata(pdev); struct rt2x00_dev *rt2x00dev = hw->priv; return rt2x00lib_resume(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00soc_resume); #endif /* CONFIG_PM */ /* * rt2x00soc module information. */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("rt2x00 soc library"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00reg.h0000644000175000017500000001423312026211315023431 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00 Abstract: rt2x00 generic register information. */ #ifndef RT2X00REG_H #define RT2X00REG_H /* * RX crypto status */ enum rx_crypto { RX_CRYPTO_SUCCESS = 0, RX_CRYPTO_FAIL_ICV = 1, RX_CRYPTO_FAIL_MIC = 2, RX_CRYPTO_FAIL_KEY = 3, }; /* * Antenna values */ enum antenna { ANTENNA_SW_DIVERSITY = 0, ANTENNA_A = 1, ANTENNA_B = 2, ANTENNA_HW_DIVERSITY = 3, }; /* * Led mode values. */ enum led_mode { LED_MODE_DEFAULT = 0, LED_MODE_TXRX_ACTIVITY = 1, LED_MODE_SIGNAL_STRENGTH = 2, LED_MODE_ASUS = 3, LED_MODE_ALPHA = 4, }; /* * TSF sync values */ enum tsf_sync { TSF_SYNC_NONE = 0, TSF_SYNC_INFRA = 1, TSF_SYNC_ADHOC = 2, TSF_SYNC_AP_NONE = 3, }; /* * Device states */ enum dev_state { STATE_DEEP_SLEEP = 0, STATE_SLEEP = 1, STATE_STANDBY = 2, STATE_AWAKE = 3, /* * Additional device states, these values are * not strict since they are not directly passed * into the device. */ STATE_RADIO_ON, STATE_RADIO_OFF, STATE_RADIO_IRQ_ON, STATE_RADIO_IRQ_OFF, }; /* * IFS backoff values */ enum ifs { IFS_BACKOFF = 0, IFS_SIFS = 1, IFS_NEW_BACKOFF = 2, IFS_NONE = 3, }; /* * IFS backoff values for HT devices */ enum txop { TXOP_HTTXOP = 0, TXOP_PIFS = 1, TXOP_SIFS = 2, TXOP_BACKOFF = 3, }; /* * Cipher types for hardware encryption */ enum cipher { CIPHER_NONE = 0, CIPHER_WEP64 = 1, CIPHER_WEP128 = 2, CIPHER_TKIP = 3, CIPHER_AES = 4, /* * The following fields were added by rt61pci and rt73usb. */ CIPHER_CKIP64 = 5, CIPHER_CKIP128 = 6, CIPHER_TKIP_NO_MIC = 7, /* Don't send to device */ /* * Max cipher type. * Note that CIPHER_NONE isn't counted, and CKIP64 and CKIP128 * are excluded due to limitations in mac80211. */ CIPHER_MAX = 4, }; /* * Rate modulations */ enum rate_modulation { RATE_MODE_CCK = 0, RATE_MODE_OFDM = 1, RATE_MODE_HT_MIX = 2, RATE_MODE_HT_GREENFIELD = 3, }; /* * Firmware validation error codes */ enum firmware_errors { FW_OK, FW_BAD_CRC, FW_BAD_LENGTH, FW_BAD_VERSION, }; /* * Register handlers. * We store the position of a register field inside a field structure, * This will simplify the process of setting and reading a certain field * inside the register while making sure the process remains byte order safe. */ struct rt2x00_field8 { u8 bit_offset; u8 bit_mask; }; struct rt2x00_field16 { u16 bit_offset; u16 bit_mask; }; struct rt2x00_field32 { u32 bit_offset; u32 bit_mask; }; /* * Power of two check, this will check * if the mask that has been given contains and contiguous set of bits. * Note that we cannot use the is_power_of_2() function since this * check must be done at compile-time. */ #define is_power_of_two(x) ( !((x) & ((x)-1)) ) #define low_bit_mask(x) ( ((x)-1) & ~(x) ) #define is_valid_mask(x) is_power_of_two(1LU + (x) + low_bit_mask(x)) /* * Macros to find first set bit in a variable. * These macros behave the same as the __ffs() functions but * the most important difference that this is done during * compile-time rather then run-time. */ #define compile_ffs2(__x) \ __builtin_choose_expr(((__x) & 0x1), 0, 1) #define compile_ffs4(__x) \ __builtin_choose_expr(((__x) & 0x3), \ (compile_ffs2((__x))), \ (compile_ffs2((__x) >> 2) + 2)) #define compile_ffs8(__x) \ __builtin_choose_expr(((__x) & 0xf), \ (compile_ffs4((__x))), \ (compile_ffs4((__x) >> 4) + 4)) #define compile_ffs16(__x) \ __builtin_choose_expr(((__x) & 0xff), \ (compile_ffs8((__x))), \ (compile_ffs8((__x) >> 8) + 8)) #define compile_ffs32(__x) \ __builtin_choose_expr(((__x) & 0xffff), \ (compile_ffs16((__x))), \ (compile_ffs16((__x) >> 16) + 16)) /* * This macro will check the requirements for the FIELD{8,16,32} macros * The mask should be a constant non-zero contiguous set of bits which * does not exceed the given typelimit. */ #define FIELD_CHECK(__mask, __type) \ BUILD_BUG_ON(!(__mask) || \ !is_valid_mask(__mask) || \ (__mask) != (__type)(__mask)) \ #define FIELD8(__mask) \ ({ \ FIELD_CHECK(__mask, u8); \ (struct rt2x00_field8) { \ compile_ffs8(__mask), (__mask) \ }; \ }) #define FIELD16(__mask) \ ({ \ FIELD_CHECK(__mask, u16); \ (struct rt2x00_field16) { \ compile_ffs16(__mask), (__mask) \ }; \ }) #define FIELD32(__mask) \ ({ \ FIELD_CHECK(__mask, u32); \ (struct rt2x00_field32) { \ compile_ffs32(__mask), (__mask) \ }; \ }) #define SET_FIELD(__reg, __type, __field, __value)\ ({ \ typecheck(__type, __field); \ *(__reg) &= ~((__field).bit_mask); \ *(__reg) |= ((__value) << \ ((__field).bit_offset)) & \ ((__field).bit_mask); \ }) #define GET_FIELD(__reg, __type, __field) \ ({ \ typecheck(__type, __field); \ ((__reg) & ((__field).bit_mask)) >> \ ((__field).bit_offset); \ }) #define rt2x00_set_field32(__reg, __field, __value) \ SET_FIELD(__reg, struct rt2x00_field32, __field, __value) #define rt2x00_get_field32(__reg, __field) \ GET_FIELD(__reg, struct rt2x00_field32, __field) #define rt2x00_set_field16(__reg, __field, __value) \ SET_FIELD(__reg, struct rt2x00_field16, __field, __value) #define rt2x00_get_field16(__reg, __field) \ GET_FIELD(__reg, struct rt2x00_field16, __field) #define rt2x00_set_field8(__reg, __field, __value) \ SET_FIELD(__reg, struct rt2x00_field8, __field, __value) #define rt2x00_get_field8(__reg, __field) \ GET_FIELD(__reg, struct rt2x00_field8, __field) #endif /* RT2X00REG_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00queue.h0000644000175000017500000005321112026211315023777 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2010 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00 Abstract: rt2x00 queue datastructures and routines */ #ifndef RT2X00QUEUE_H #define RT2X00QUEUE_H #include /** * DOC: Entry frame size * * Ralink PCI devices demand the Frame size to be a multiple of 128 bytes, * for USB devices this restriction does not apply, but the value of * 2432 makes sense since it is big enough to contain the maximum fragment * size according to the ieee802.11 specs. * The aggregation size depends on support from the driver, but should * be something around 3840 bytes. */ #define DATA_FRAME_SIZE 2432 #define MGMT_FRAME_SIZE 256 #define AGGREGATION_SIZE 3840 /** * enum data_queue_qid: Queue identification * * @QID_AC_VO: AC VO queue * @QID_AC_VI: AC VI queue * @QID_AC_BE: AC BE queue * @QID_AC_BK: AC BK queue * @QID_HCCA: HCCA queue * @QID_MGMT: MGMT queue (prio queue) * @QID_RX: RX queue * @QID_OTHER: None of the above (don't use, only present for completeness) * @QID_BEACON: Beacon queue (value unspecified, don't send it to device) * @QID_ATIM: Atim queue (value unspecified, don't send it to device) */ enum data_queue_qid { QID_AC_VO = 0, QID_AC_VI = 1, QID_AC_BE = 2, QID_AC_BK = 3, QID_HCCA = 4, QID_MGMT = 13, QID_RX = 14, QID_OTHER = 15, QID_BEACON, QID_ATIM, }; /** * enum skb_frame_desc_flags: Flags for &struct skb_frame_desc * * @SKBDESC_DMA_MAPPED_RX: &skb_dma field has been mapped for RX * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX * @SKBDESC_IV_STRIPPED: Frame contained a IV/EIV provided by * mac80211 but was stripped for processing by the driver. * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211, * don't try to pass it back. * @SKBDESC_DESC_IN_SKB: The descriptor is at the start of the * skb, instead of in the desc field. */ enum skb_frame_desc_flags { SKBDESC_DMA_MAPPED_RX = 1 << 0, SKBDESC_DMA_MAPPED_TX = 1 << 1, SKBDESC_IV_STRIPPED = 1 << 2, SKBDESC_NOT_MAC80211 = 1 << 3, SKBDESC_DESC_IN_SKB = 1 << 4, }; /** * struct skb_frame_desc: Descriptor information for the skb buffer * * This structure is placed over the driver_data array, this means that * this structure should not exceed the size of that array (40 bytes). * * @flags: Frame flags, see &enum skb_frame_desc_flags. * @desc_len: Length of the frame descriptor. * @tx_rate_idx: the index of the TX rate, used for TX status reporting * @tx_rate_flags: the TX rate flags, used for TX status reporting * @desc: Pointer to descriptor part of the frame. * Note that this pointer could point to something outside * of the scope of the skb->data pointer. * @iv: IV/EIV data used during encryption/decryption. * @skb_dma: (PCI-only) the DMA address associated with the sk buffer. * @entry: The entry to which this sk buffer belongs. */ struct skb_frame_desc { u8 flags; u8 desc_len; u8 tx_rate_idx; u8 tx_rate_flags; void *desc; __le32 iv[2]; dma_addr_t skb_dma; struct queue_entry *entry; }; /** * get_skb_frame_desc - Obtain the rt2x00 frame descriptor from a sk_buff. * @skb: &struct sk_buff from where we obtain the &struct skb_frame_desc */ static inline struct skb_frame_desc* get_skb_frame_desc(struct sk_buff *skb) { BUILD_BUG_ON(sizeof(struct skb_frame_desc) > IEEE80211_TX_INFO_DRIVER_DATA_SIZE); return (struct skb_frame_desc *)&IEEE80211_SKB_CB(skb)->driver_data; } /** * enum rxdone_entry_desc_flags: Flags for &struct rxdone_entry_desc * * @RXDONE_SIGNAL_PLCP: Signal field contains the plcp value. * @RXDONE_SIGNAL_BITRATE: Signal field contains the bitrate value. * @RXDONE_SIGNAL_MCS: Signal field contains the mcs value. * @RXDONE_MY_BSS: Does this frame originate from device's BSS. * @RXDONE_CRYPTO_IV: Driver provided IV/EIV data. * @RXDONE_CRYPTO_ICV: Driver provided ICV data. * @RXDONE_L2PAD: 802.11 payload has been padded to 4-byte boundary. */ enum rxdone_entry_desc_flags { RXDONE_SIGNAL_PLCP = BIT(0), RXDONE_SIGNAL_BITRATE = BIT(1), RXDONE_SIGNAL_MCS = BIT(2), RXDONE_MY_BSS = BIT(3), RXDONE_CRYPTO_IV = BIT(4), RXDONE_CRYPTO_ICV = BIT(5), RXDONE_L2PAD = BIT(6), }; /** * RXDONE_SIGNAL_MASK - Define to mask off all &rxdone_entry_desc_flags flags * except for the RXDONE_SIGNAL_* flags. This is useful to convert the dev_flags * from &rxdone_entry_desc to a signal value type. */ #define RXDONE_SIGNAL_MASK \ ( RXDONE_SIGNAL_PLCP | RXDONE_SIGNAL_BITRATE | RXDONE_SIGNAL_MCS ) /** * struct rxdone_entry_desc: RX Entry descriptor * * Summary of information that has been read from the RX frame descriptor. * * @timestamp: RX Timestamp * @signal: Signal of the received frame. * @rssi: RSSI of the received frame. * @size: Data size of the received frame. * @flags: MAC80211 receive flags (See &enum mac80211_rx_flags). * @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags). * @rate_mode: Rate mode (See @enum rate_modulation). * @cipher: Cipher type used during decryption. * @cipher_status: Decryption status. * @iv: IV/EIV data used during decryption. * @icv: ICV data used during decryption. */ struct rxdone_entry_desc { u64 timestamp; int signal; int rssi; int size; int flags; int dev_flags; u16 rate_mode; u8 cipher; u8 cipher_status; __le32 iv[2]; __le32 icv; }; /** * enum txdone_entry_desc_flags: Flags for &struct txdone_entry_desc * * Every txdone report has to contain the basic result of the * transmission, either &TXDONE_UNKNOWN, &TXDONE_SUCCESS or * &TXDONE_FAILURE. The flag &TXDONE_FALLBACK can be used in * conjunction with all of these flags but should only be set * if retires > 0. The flag &TXDONE_EXCESSIVE_RETRY can only be used * in conjunction with &TXDONE_FAILURE. * * @TXDONE_UNKNOWN: Hardware could not determine success of transmission. * @TXDONE_SUCCESS: Frame was successfully send * @TXDONE_FALLBACK: Hardware used fallback rates for retries * @TXDONE_FAILURE: Frame was not successfully send * @TXDONE_EXCESSIVE_RETRY: In addition to &TXDONE_FAILURE, the * frame transmission failed due to excessive retries. */ enum txdone_entry_desc_flags { TXDONE_UNKNOWN, TXDONE_SUCCESS, TXDONE_FALLBACK, TXDONE_FAILURE, TXDONE_EXCESSIVE_RETRY, TXDONE_AMPDU, }; /** * struct txdone_entry_desc: TX done entry descriptor * * Summary of information that has been read from the TX frame descriptor * after the device is done with transmission. * * @flags: TX done flags (See &enum txdone_entry_desc_flags). * @retry: Retry count. */ struct txdone_entry_desc { unsigned long flags; int retry; }; /** * enum txentry_desc_flags: Status flags for TX entry descriptor * * @ENTRY_TXD_RTS_FRAME: This frame is a RTS frame. * @ENTRY_TXD_CTS_FRAME: This frame is a CTS-to-self frame. * @ENTRY_TXD_GENERATE_SEQ: This frame requires sequence counter. * @ENTRY_TXD_FIRST_FRAGMENT: This is the first frame. * @ENTRY_TXD_MORE_FRAG: This frame is followed by another fragment. * @ENTRY_TXD_REQ_TIMESTAMP: Require timestamp to be inserted. * @ENTRY_TXD_BURST: This frame belongs to the same burst event. * @ENTRY_TXD_ACK: An ACK is required for this frame. * @ENTRY_TXD_RETRY_MODE: When set, the long retry count is used. * @ENTRY_TXD_ENCRYPT: This frame should be encrypted. * @ENTRY_TXD_ENCRYPT_PAIRWISE: Use pairwise key table (instead of shared). * @ENTRY_TXD_ENCRYPT_IV: Generate IV/EIV in hardware. * @ENTRY_TXD_ENCRYPT_MMIC: Generate MIC in hardware. * @ENTRY_TXD_HT_AMPDU: This frame is part of an AMPDU. * @ENTRY_TXD_HT_BW_40: Use 40MHz Bandwidth. * @ENTRY_TXD_HT_SHORT_GI: Use short GI. * @ENTRY_TXD_HT_MIMO_PS: The receiving STA is in dynamic SM PS mode. */ enum txentry_desc_flags { ENTRY_TXD_RTS_FRAME, ENTRY_TXD_CTS_FRAME, ENTRY_TXD_GENERATE_SEQ, ENTRY_TXD_FIRST_FRAGMENT, ENTRY_TXD_MORE_FRAG, ENTRY_TXD_REQ_TIMESTAMP, ENTRY_TXD_BURST, ENTRY_TXD_ACK, ENTRY_TXD_RETRY_MODE, ENTRY_TXD_ENCRYPT, ENTRY_TXD_ENCRYPT_PAIRWISE, ENTRY_TXD_ENCRYPT_IV, ENTRY_TXD_ENCRYPT_MMIC, ENTRY_TXD_HT_AMPDU, ENTRY_TXD_HT_BW_40, ENTRY_TXD_HT_SHORT_GI, ENTRY_TXD_HT_MIMO_PS, }; /** * struct txentry_desc: TX Entry descriptor * * Summary of information for the frame descriptor before sending a TX frame. * * @flags: Descriptor flags (See &enum queue_entry_flags). * @length: Length of the entire frame. * @header_length: Length of 802.11 header. * @length_high: PLCP length high word. * @length_low: PLCP length low word. * @signal: PLCP signal. * @service: PLCP service. * @msc: MCS. * @stbc: Use Space Time Block Coding (only available for MCS rates < 8). * @ba_size: Size of the recepients RX reorder buffer - 1. * @rate_mode: Rate mode (See @enum rate_modulation). * @mpdu_density: MDPU density. * @retry_limit: Max number of retries. * @ifs: IFS value. * @txop: IFS value for 11n capable chips. * @cipher: Cipher type used for encryption. * @key_idx: Key index used for encryption. * @iv_offset: Position where IV should be inserted by hardware. * @iv_len: Length of IV data. */ struct txentry_desc { unsigned long flags; u16 length; u16 header_length; union { struct { u16 length_high; u16 length_low; u16 signal; u16 service; enum ifs ifs; } plcp; struct { u16 mcs; u8 stbc; u8 ba_size; u8 mpdu_density; enum txop txop; int wcid; } ht; } u; enum rate_modulation rate_mode; short retry_limit; enum cipher cipher; u16 key_idx; u16 iv_offset; u16 iv_len; }; /** * enum queue_entry_flags: Status flags for queue entry * * @ENTRY_BCN_ASSIGNED: This entry has been assigned to an interface. * As long as this bit is set, this entry may only be touched * through the interface structure. * @ENTRY_OWNER_DEVICE_DATA: This entry is owned by the device for data * transfer (either TX or RX depending on the queue). The entry should * only be touched after the device has signaled it is done with it. * @ENTRY_DATA_PENDING: This entry contains a valid frame and is waiting * for the signal to start sending. * @ENTRY_DATA_IO_FAILED: Hardware indicated that an IO error occurred * while transferring the data to the hardware. No TX status report will * be expected from the hardware. * @ENTRY_DATA_STATUS_PENDING: The entry has been send to the device and * returned. It is now waiting for the status reporting before the * entry can be reused again. */ enum queue_entry_flags { ENTRY_BCN_ASSIGNED, ENTRY_OWNER_DEVICE_DATA, ENTRY_DATA_PENDING, ENTRY_DATA_IO_FAILED, ENTRY_DATA_STATUS_PENDING, }; /** * struct queue_entry: Entry inside the &struct data_queue * * @flags: Entry flags, see &enum queue_entry_flags. * @last_action: Timestamp of last change. * @queue: The data queue (&struct data_queue) to which this entry belongs. * @skb: The buffer which is currently being transmitted (for TX queue), * or used to directly receive data in (for RX queue). * @entry_idx: The entry index number. * @priv_data: Private data belonging to this queue entry. The pointer * points to data specific to a particular driver and queue type. */ struct queue_entry { unsigned long flags; unsigned long last_action; struct data_queue *queue; struct sk_buff *skb; unsigned int entry_idx; void *priv_data; }; /** * enum queue_index: Queue index type * * @Q_INDEX: Index pointer to the current entry in the queue, if this entry is * owned by the hardware then the queue is considered to be full. * @Q_INDEX_DMA_DONE: Index pointer for the next entry which will have been * transferred to the hardware. * @Q_INDEX_DONE: Index pointer to the next entry which will be completed by * the hardware and for which we need to run the txdone handler. If this * entry is not owned by the hardware the queue is considered to be empty. * @Q_INDEX_MAX: Keep last, used in &struct data_queue to determine the size * of the index array. */ enum queue_index { Q_INDEX, Q_INDEX_DMA_DONE, Q_INDEX_DONE, Q_INDEX_MAX, }; /** * enum data_queue_flags: Status flags for data queues * * @QUEUE_STARTED: The queue has been started. Fox RX queues this means the * device might be DMA'ing skbuffers. TX queues will accept skbuffers to * be transmitted and beacon queues will start beaconing the configured * beacons. * @QUEUE_PAUSED: The queue has been started but is currently paused. * When this bit is set, the queue has been stopped in mac80211, * preventing new frames to be enqueued. However, a few frames * might still appear shortly after the pausing... */ enum data_queue_flags { QUEUE_STARTED, QUEUE_PAUSED, }; /** * struct data_queue: Data queue * * @rt2x00dev: Pointer to main &struct rt2x00dev where this queue belongs to. * @entries: Base address of the &struct queue_entry which are * part of this queue. * @qid: The queue identification, see &enum data_queue_qid. * @flags: Entry flags, see &enum queue_entry_flags. * @status_lock: The mutex for protecting the start/stop/flush * handling on this queue. * @tx_lock: Spinlock to serialize tx operations on this queue. * @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or * @index_crypt needs to be changed this lock should be grabbed to prevent * index corruption due to concurrency. * @count: Number of frames handled in the queue. * @limit: Maximum number of entries in the queue. * @threshold: Minimum number of free entries before queue is kicked by force. * @length: Number of frames in queue. * @index: Index pointers to entry positions in the queue, * use &enum queue_index to get a specific index field. * @txop: maximum burst time. * @aifs: The aifs value for outgoing frames (field ignored in RX queue). * @cw_min: The cw min value for outgoing frames (field ignored in RX queue). * @cw_max: The cw max value for outgoing frames (field ignored in RX queue). * @data_size: Maximum data size for the frames in this queue. * @desc_size: Hardware descriptor size for the data in this queue. * @usb_endpoint: Device endpoint used for communication (USB only) * @usb_maxpacket: Max packet size for given endpoint (USB only) */ struct data_queue { struct rt2x00_dev *rt2x00dev; struct queue_entry *entries; enum data_queue_qid qid; unsigned long flags; struct mutex status_lock; spinlock_t tx_lock; spinlock_t index_lock; unsigned int count; unsigned short limit; unsigned short threshold; unsigned short length; unsigned short index[Q_INDEX_MAX]; unsigned short txop; unsigned short aifs; unsigned short cw_min; unsigned short cw_max; unsigned short data_size; unsigned short desc_size; unsigned short usb_endpoint; unsigned short usb_maxpacket; }; /** * struct data_queue_desc: Data queue description * * The information in this structure is used by drivers * to inform rt2x00lib about the creation of the data queue. * * @entry_num: Maximum number of entries for a queue. * @data_size: Maximum data size for the frames in this queue. * @desc_size: Hardware descriptor size for the data in this queue. * @priv_size: Size of per-queue_entry private data. */ struct data_queue_desc { unsigned short entry_num; unsigned short data_size; unsigned short desc_size; unsigned short priv_size; }; /** * queue_end - Return pointer to the last queue (HELPER MACRO). * @__dev: Pointer to &struct rt2x00_dev * * Using the base rx pointer and the maximum number of available queues, * this macro will return the address of 1 position beyond the end of the * queues array. */ #define queue_end(__dev) \ &(__dev)->rx[(__dev)->data_queues] /** * tx_queue_end - Return pointer to the last TX queue (HELPER MACRO). * @__dev: Pointer to &struct rt2x00_dev * * Using the base tx pointer and the maximum number of available TX * queues, this macro will return the address of 1 position beyond * the end of the TX queue array. */ #define tx_queue_end(__dev) \ &(__dev)->tx[(__dev)->ops->tx_queues] /** * queue_next - Return pointer to next queue in list (HELPER MACRO). * @__queue: Current queue for which we need the next queue * * Using the current queue address we take the address directly * after the queue to take the next queue. Note that this macro * should be used carefully since it does not protect against * moving past the end of the list. (See macros &queue_end and * &tx_queue_end for determining the end of the queue). */ #define queue_next(__queue) \ &(__queue)[1] /** * queue_loop - Loop through the queues within a specific range (HELPER MACRO). * @__entry: Pointer where the current queue entry will be stored in. * @__start: Start queue pointer. * @__end: End queue pointer. * * This macro will loop through all queues between &__start and &__end. */ #define queue_loop(__entry, __start, __end) \ for ((__entry) = (__start); \ prefetch(queue_next(__entry)), (__entry) != (__end);\ (__entry) = queue_next(__entry)) /** * queue_for_each - Loop through all queues * @__dev: Pointer to &struct rt2x00_dev * @__entry: Pointer where the current queue entry will be stored in. * * This macro will loop through all available queues. */ #define queue_for_each(__dev, __entry) \ queue_loop(__entry, (__dev)->rx, queue_end(__dev)) /** * tx_queue_for_each - Loop through the TX queues * @__dev: Pointer to &struct rt2x00_dev * @__entry: Pointer where the current queue entry will be stored in. * * This macro will loop through all TX related queues excluding * the Beacon and Atim queues. */ #define tx_queue_for_each(__dev, __entry) \ queue_loop(__entry, (__dev)->tx, tx_queue_end(__dev)) /** * txall_queue_for_each - Loop through all TX related queues * @__dev: Pointer to &struct rt2x00_dev * @__entry: Pointer where the current queue entry will be stored in. * * This macro will loop through all TX related queues including * the Beacon and Atim queues. */ #define txall_queue_for_each(__dev, __entry) \ queue_loop(__entry, (__dev)->tx, queue_end(__dev)) /** * rt2x00queue_for_each_entry - Loop through all entries in the queue * @queue: Pointer to @data_queue * @start: &enum queue_index Pointer to start index * @end: &enum queue_index Pointer to end index * @fn: The function to call for each &struct queue_entry * * This will walk through all entries in the queue, in chronological * order. This means it will start at the current @start pointer * and will walk through the queue until it reaches the @end pointer. * * If fn returns true for an entry rt2x00queue_for_each_entry will stop * processing and return true as well. */ bool rt2x00queue_for_each_entry(struct data_queue *queue, enum queue_index start, enum queue_index end, bool (*fn)(struct queue_entry *entry)); /** * rt2x00queue_empty - Check if the queue is empty. * @queue: Queue to check if empty. */ static inline int rt2x00queue_empty(struct data_queue *queue) { return queue->length == 0; } /** * rt2x00queue_full - Check if the queue is full. * @queue: Queue to check if full. */ static inline int rt2x00queue_full(struct data_queue *queue) { return queue->length == queue->limit; } /** * rt2x00queue_free - Check the number of available entries in queue. * @queue: Queue to check. */ static inline int rt2x00queue_available(struct data_queue *queue) { return queue->limit - queue->length; } /** * rt2x00queue_threshold - Check if the queue is below threshold * @queue: Queue to check. */ static inline int rt2x00queue_threshold(struct data_queue *queue) { return rt2x00queue_available(queue) < queue->threshold; } /** * rt2x00queue_dma_timeout - Check if a timeout occurred for DMA transfers * @entry: Queue entry to check. */ static inline int rt2x00queue_dma_timeout(struct queue_entry *entry) { if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return false; return time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); } /** * _rt2x00_desc_read - Read a word from the hardware descriptor. * @desc: Base descriptor address * @word: Word index from where the descriptor should be read. * @value: Address where the descriptor value should be written into. */ static inline void _rt2x00_desc_read(__le32 *desc, const u8 word, __le32 *value) { *value = desc[word]; } /** * rt2x00_desc_read - Read a word from the hardware descriptor, this * function will take care of the byte ordering. * @desc: Base descriptor address * @word: Word index from where the descriptor should be read. * @value: Address where the descriptor value should be written into. */ static inline void rt2x00_desc_read(__le32 *desc, const u8 word, u32 *value) { __le32 tmp; _rt2x00_desc_read(desc, word, &tmp); *value = le32_to_cpu(tmp); } /** * rt2x00_desc_write - write a word to the hardware descriptor, this * function will take care of the byte ordering. * @desc: Base descriptor address * @word: Word index from where the descriptor should be written. * @value: Value that should be written into the descriptor. */ static inline void _rt2x00_desc_write(__le32 *desc, const u8 word, __le32 value) { desc[word] = value; } /** * rt2x00_desc_write - write a word to the hardware descriptor. * @desc: Base descriptor address * @word: Word index from where the descriptor should be written. * @value: Value that should be written into the descriptor. */ static inline void rt2x00_desc_write(__le32 *desc, const u8 word, u32 value) { _rt2x00_desc_write(desc, word, cpu_to_le32(value)); } #endif /* RT2X00QUEUE_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00queue.c0000644000175000017500000010320012026211315023764 0ustar mcgrofmcgrof/* Copyright (C) 2010 Willow Garage Copyright (C) 2004 - 2010 Ivo van Doorn Copyright (C) 2004 - 2009 Gertjan van Wingerde This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 queue specific routines. */ #include #include #include #include #include "rt2x00.h" #include "rt2x00lib.h" struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct sk_buff *skb; struct skb_frame_desc *skbdesc; unsigned int frame_size; unsigned int head_size = 0; unsigned int tail_size = 0; /* * The frame size includes descriptor size, because the * hardware directly receive the frame into the skbuffer. */ frame_size = entry->queue->data_size + entry->queue->desc_size; /* * The payload should be aligned to a 4-byte boundary, * this means we need at least 3 bytes for moving the frame * into the correct offset. */ head_size = 4; /* * For IV/EIV/ICV assembly we must make sure there is * at least 8 bytes bytes available in headroom for IV/EIV * and 8 bytes for ICV data as tailroon. */ if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) { head_size += 8; tail_size += 8; } /* * Allocate skbuffer. */ skb = __dev_alloc_skb(frame_size + head_size + tail_size, gfp); if (!skb) return NULL; /* * Make sure we not have a frame with the requested bytes * available in the head and tail. */ skb_reserve(skb, head_size); skb_put(skb, frame_size); /* * Populate skbdesc. */ skbdesc = get_skb_frame_desc(skb); memset(skbdesc, 0, sizeof(*skbdesc)); skbdesc->entry = entry; if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) { skbdesc->skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, DMA_FROM_DEVICE); skbdesc->flags |= SKBDESC_DMA_MAPPED_RX; } return skb; } void rt2x00queue_map_txskb(struct queue_entry *entry) { struct device *dev = entry->queue->rt2x00dev->dev; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); skbdesc->skb_dma = dma_map_single(dev, entry->skb->data, entry->skb->len, DMA_TO_DEVICE); skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; } EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb); void rt2x00queue_unmap_skb(struct queue_entry *entry) { struct device *dev = entry->queue->rt2x00dev->dev; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); if (skbdesc->flags & SKBDESC_DMA_MAPPED_RX) { dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, DMA_FROM_DEVICE); skbdesc->flags &= ~SKBDESC_DMA_MAPPED_RX; } else if (skbdesc->flags & SKBDESC_DMA_MAPPED_TX) { dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, DMA_TO_DEVICE); skbdesc->flags &= ~SKBDESC_DMA_MAPPED_TX; } } EXPORT_SYMBOL_GPL(rt2x00queue_unmap_skb); void rt2x00queue_free_skb(struct queue_entry *entry) { if (!entry->skb) return; rt2x00queue_unmap_skb(entry); dev_kfree_skb_any(entry->skb); entry->skb = NULL; } void rt2x00queue_align_frame(struct sk_buff *skb) { unsigned int frame_length = skb->len; unsigned int align = ALIGN_SIZE(skb, 0); if (!align) return; skb_push(skb, align); memmove(skb->data, skb->data + align, frame_length); skb_trim(skb, frame_length); } void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length) { unsigned int payload_length = skb->len - header_length; unsigned int header_align = ALIGN_SIZE(skb, 0); unsigned int payload_align = ALIGN_SIZE(skb, header_length); unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0; /* * Adjust the header alignment if the payload needs to be moved more * than the header. */ if (payload_align > header_align) header_align += 4; /* There is nothing to do if no alignment is needed */ if (!header_align) return; /* Reserve the amount of space needed in front of the frame */ skb_push(skb, header_align); /* * Move the header. */ memmove(skb->data, skb->data + header_align, header_length); /* Move the payload, if present and if required */ if (payload_length && payload_align) memmove(skb->data + header_length + l2pad, skb->data + header_length + l2pad + payload_align, payload_length); /* Trim the skb to the correct size */ skb_trim(skb, header_length + l2pad + payload_length); } void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length) { /* * L2 padding is only present if the skb contains more than just the * IEEE 802.11 header. */ unsigned int l2pad = (skb->len > header_length) ? L2PAD_SIZE(header_length) : 0; if (!l2pad) return; memmove(skb->data + l2pad, skb->data, header_length); skb_pull(skb, l2pad); } static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif); u16 seqno; if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) return; __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags)) { /* * rt2800 has a H/W (or F/W) bug, device incorrectly increase * seqno on retransmited data (non-QOS) frames. To workaround * the problem let's generate seqno in software if QOS is * disabled. */ if (test_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags)) __clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); else /* H/W will generate sequence number */ return; } /* * The hardware is not able to insert a sequence number. Assign a * software generated one here. * * This is wrong because beacons are not getting sequence * numbers assigned properly. * * A secondary problem exists for drivers that cannot toggle * sequence counting per-frame, since those will override the * sequence counter given by mac80211. */ if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) seqno = atomic_add_return(0x10, &intf->seqno); else seqno = atomic_read(&intf->seqno); hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seqno); } static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc, const struct rt2x00_rate *hwrate) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; unsigned int data_length; unsigned int duration; unsigned int residual; /* * Determine with what IFS priority this frame should be send. * Set ifs to IFS_SIFS when the this is not the first fragment, * or this fragment came after RTS/CTS. */ if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) txdesc->u.plcp.ifs = IFS_BACKOFF; else txdesc->u.plcp.ifs = IFS_SIFS; /* Data length + CRC + Crypto overhead (IV/EIV/ICV/MIC) */ data_length = skb->len + 4; data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); /* * PLCP setup * Length calculation depends on OFDM/CCK rate. */ txdesc->u.plcp.signal = hwrate->plcp; txdesc->u.plcp.service = 0x04; if (hwrate->flags & DEV_RATE_OFDM) { txdesc->u.plcp.length_high = (data_length >> 6) & 0x3f; txdesc->u.plcp.length_low = data_length & 0x3f; } else { /* * Convert length to microseconds. */ residual = GET_DURATION_RES(data_length, hwrate->bitrate); duration = GET_DURATION(data_length, hwrate->bitrate); if (residual != 0) { duration++; /* * Check if we need to set the Length Extension */ if (hwrate->bitrate == 110 && residual <= 30) txdesc->u.plcp.service |= 0x80; } txdesc->u.plcp.length_high = (duration >> 8) & 0xff; txdesc->u.plcp.length_low = duration & 0xff; /* * When preamble is enabled we should set the * preamble bit for the signal. */ if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) txdesc->u.plcp.signal |= 0x08; } } static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc, struct ieee80211_sta *sta, const struct rt2x00_rate *hwrate) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct rt2x00_sta *sta_priv = NULL; if (sta) { txdesc->u.ht.mpdu_density = sta->ht_cap.ampdu_density; sta_priv = sta_to_rt2x00_sta(sta); txdesc->u.ht.wcid = sta_priv->wcid; } /* * If IEEE80211_TX_RC_MCS is set txrate->idx just contains the * mcs rate to be used */ if (txrate->flags & IEEE80211_TX_RC_MCS) { txdesc->u.ht.mcs = txrate->idx; /* * MIMO PS should be set to 1 for STA's using dynamic SM PS * when using more then one tx stream (>MCS7). */ if (sta && txdesc->u.ht.mcs > 7 && ((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> IEEE80211_HT_CAP_SM_PS_SHIFT) == WLAN_HT_CAP_SM_PS_DYNAMIC) __set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags); } else { txdesc->u.ht.mcs = rt2x00_get_rate_mcs(hwrate->mcs); if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) txdesc->u.ht.mcs |= 0x08; } if (test_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags)) { if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) txdesc->u.ht.txop = TXOP_SIFS; else txdesc->u.ht.txop = TXOP_BACKOFF; /* Left zero on all other settings. */ return; } txdesc->u.ht.ba_size = 7; /* FIXME: What value is needed? */ /* * Only one STBC stream is supported for now. */ if (tx_info->flags & IEEE80211_TX_CTL_STBC) txdesc->u.ht.stbc = 1; /* * This frame is eligible for an AMPDU, however, don't aggregate * frames that are intended to probe a specific tx rate. */ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU && !(tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) __set_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags); /* * Set 40Mhz mode if necessary (for legacy rates this will * duplicate the frame to both channels). */ if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH || txrate->flags & IEEE80211_TX_RC_DUP_DATA) __set_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags); if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) __set_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags); /* * Determine IFS values * - Use TXOP_BACKOFF for management frames except beacons * - Use TXOP_SIFS for fragment bursts * - Use TXOP_HTTXOP for everything else * * Note: rt2800 devices won't use CTS protection (if used) * for frames not transmitted with TXOP_HTTXOP */ if (ieee80211_is_mgmt(hdr->frame_control) && !ieee80211_is_beacon(hdr->frame_control)) txdesc->u.ht.txop = TXOP_BACKOFF; else if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) txdesc->u.ht.txop = TXOP_SIFS; else txdesc->u.ht.txop = TXOP_HTTXOP; } static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc, struct ieee80211_sta *sta) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; struct ieee80211_rate *rate; const struct rt2x00_rate *hwrate = NULL; memset(txdesc, 0, sizeof(*txdesc)); /* * Header and frame information. */ txdesc->length = skb->len; txdesc->header_length = ieee80211_get_hdrlen_from_skb(skb); /* * Check whether this frame is to be acked. */ if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) __set_bit(ENTRY_TXD_ACK, &txdesc->flags); /* * Check if this is a RTS/CTS frame */ if (ieee80211_is_rts(hdr->frame_control) || ieee80211_is_cts(hdr->frame_control)) { __set_bit(ENTRY_TXD_BURST, &txdesc->flags); if (ieee80211_is_rts(hdr->frame_control)) __set_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags); else __set_bit(ENTRY_TXD_CTS_FRAME, &txdesc->flags); if (tx_info->control.rts_cts_rate_idx >= 0) rate = ieee80211_get_rts_cts_rate(rt2x00dev->hw, tx_info); } /* * Determine retry information. */ txdesc->retry_limit = tx_info->control.rates[0].count - 1; if (txdesc->retry_limit >= rt2x00dev->long_retry) __set_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags); /* * Check if more fragments are pending */ if (ieee80211_has_morefrags(hdr->frame_control)) { __set_bit(ENTRY_TXD_BURST, &txdesc->flags); __set_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags); } /* * Check if more frames (!= fragments) are pending */ if (tx_info->flags & IEEE80211_TX_CTL_MORE_FRAMES) __set_bit(ENTRY_TXD_BURST, &txdesc->flags); /* * Beacons and probe responses require the tsf timestamp * to be inserted into the frame. */ if (ieee80211_is_beacon(hdr->frame_control) || ieee80211_is_probe_resp(hdr->frame_control)) __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags); if ((tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) && !test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)) __set_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags); /* * Determine rate modulation. */ if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) txdesc->rate_mode = RATE_MODE_HT_GREENFIELD; else if (txrate->flags & IEEE80211_TX_RC_MCS) txdesc->rate_mode = RATE_MODE_HT_MIX; else { rate = ieee80211_get_tx_rate(rt2x00dev->hw, tx_info); hwrate = rt2x00_get_rate(rate->hw_value); if (hwrate->flags & DEV_RATE_OFDM) txdesc->rate_mode = RATE_MODE_OFDM; else txdesc->rate_mode = RATE_MODE_CCK; } /* * Apply TX descriptor handling by components */ rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc); rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc); if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags)) rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc, sta, hwrate); else rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb, txdesc, hwrate); } static int rt2x00queue_write_tx_data(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; /* * This should not happen, we already checked the entry * was ours. When the hardware disagrees there has been * a queue corruption! */ if (unlikely(rt2x00dev->ops->lib->get_entry_state && rt2x00dev->ops->lib->get_entry_state(entry))) { ERROR(rt2x00dev, "Corrupt queue %d, accessing entry which is not ours.\n" "Please file bug report to %s.\n", entry->queue->qid, DRV_PROJECT); return -EINVAL; } /* * Add the requested extra tx headroom in front of the skb. */ skb_push(entry->skb, rt2x00dev->ops->extra_tx_headroom); memset(entry->skb->data, 0, rt2x00dev->ops->extra_tx_headroom); /* * Call the driver's write_tx_data function, if it exists. */ if (rt2x00dev->ops->lib->write_tx_data) rt2x00dev->ops->lib->write_tx_data(entry, txdesc); /* * Map the skb to DMA. */ if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) rt2x00queue_map_txskb(entry); return 0; } static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry, struct txentry_desc *txdesc) { struct data_queue *queue = entry->queue; queue->rt2x00dev->ops->lib->write_tx_desc(entry, txdesc); /* * All processing on the frame has been completed, this means * it is now ready to be dumped to userspace through debugfs. */ rt2x00debug_dump_frame(queue->rt2x00dev, DUMP_FRAME_TX, entry->skb); } static void rt2x00queue_kick_tx_queue(struct data_queue *queue, struct txentry_desc *txdesc) { /* * Check if we need to kick the queue, there are however a few rules * 1) Don't kick unless this is the last in frame in a burst. * When the burst flag is set, this frame is always followed * by another frame which in some way are related to eachother. * This is true for fragments, RTS or CTS-to-self frames. * 2) Rule 1 can be broken when the available entries * in the queue are less then a certain threshold. */ if (rt2x00queue_threshold(queue) || !test_bit(ENTRY_TXD_BURST, &txdesc->flags)) queue->rt2x00dev->ops->lib->kick_queue(queue); } int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, bool local) { struct ieee80211_tx_info *tx_info; struct queue_entry *entry; struct txentry_desc txdesc; struct skb_frame_desc *skbdesc; u8 rate_idx, rate_flags; int ret = 0; /* * Copy all TX descriptor information into txdesc, * after that we are free to use the skb->cb array * for our information. */ rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, NULL); /* * All information is retrieved from the skb->cb array, * now we should claim ownership of the driver part of that * array, preserving the bitrate index and flags. */ tx_info = IEEE80211_SKB_CB(skb); rate_idx = tx_info->control.rates[0].idx; rate_flags = tx_info->control.rates[0].flags; skbdesc = get_skb_frame_desc(skb); memset(skbdesc, 0, sizeof(*skbdesc)); skbdesc->tx_rate_idx = rate_idx; skbdesc->tx_rate_flags = rate_flags; if (local) skbdesc->flags |= SKBDESC_NOT_MAC80211; /* * When hardware encryption is supported, and this frame * is to be encrypted, we should strip the IV/EIV data from * the frame so we can provide it to the driver separately. */ if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) { if (test_bit(REQUIRE_COPY_IV, &queue->rt2x00dev->cap_flags)) rt2x00crypto_tx_copy_iv(skb, &txdesc); else rt2x00crypto_tx_remove_iv(skb, &txdesc); } /* * When DMA allocation is required we should guarantee to the * driver that the DMA is aligned to a 4-byte boundary. * However some drivers require L2 padding to pad the payload * rather then the header. This could be a requirement for * PCI and USB devices, while header alignment only is valid * for PCI devices. */ if (test_bit(REQUIRE_L2PAD, &queue->rt2x00dev->cap_flags)) rt2x00queue_insert_l2pad(skb, txdesc.header_length); else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags)) rt2x00queue_align_frame(skb); /* * That function must be called with bh disabled. */ spin_lock(&queue->tx_lock); if (unlikely(rt2x00queue_full(queue))) { ERROR(queue->rt2x00dev, "Dropping frame due to full tx queue %d.\n", queue->qid); ret = -ENOBUFS; goto out; } entry = rt2x00queue_get_entry(queue, Q_INDEX); if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))) { ERROR(queue->rt2x00dev, "Arrived at non-free entry in the non-full queue %d.\n" "Please file bug report to %s.\n", queue->qid, DRV_PROJECT); ret = -EINVAL; goto out; } skbdesc->entry = entry; entry->skb = skb; /* * It could be possible that the queue was corrupted and this * call failed. Since we always return NETDEV_TX_OK to mac80211, * this frame will simply be dropped. */ if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) { clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); entry->skb = NULL; ret = -EIO; goto out; } set_bit(ENTRY_DATA_PENDING, &entry->flags); rt2x00queue_index_inc(entry, Q_INDEX); rt2x00queue_write_tx_descriptor(entry, &txdesc); rt2x00queue_kick_tx_queue(queue, &txdesc); out: spin_unlock(&queue->tx_lock); return ret; } int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif) { struct rt2x00_intf *intf = vif_to_intf(vif); if (unlikely(!intf->beacon)) return -ENOBUFS; mutex_lock(&intf->beacon_skb_mutex); /* * Clean up the beacon skb. */ rt2x00queue_free_skb(intf->beacon); /* * Clear beacon (single bssid devices don't need to clear the beacon * since the beacon queue will get stopped anyway). */ if (rt2x00dev->ops->lib->clear_beacon) rt2x00dev->ops->lib->clear_beacon(intf->beacon); mutex_unlock(&intf->beacon_skb_mutex); return 0; } int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif) { struct rt2x00_intf *intf = vif_to_intf(vif); struct skb_frame_desc *skbdesc; struct txentry_desc txdesc; if (unlikely(!intf->beacon)) return -ENOBUFS; /* * Clean up the beacon skb. */ rt2x00queue_free_skb(intf->beacon); intf->beacon->skb = ieee80211_beacon_get(rt2x00dev->hw, vif); if (!intf->beacon->skb) return -ENOMEM; /* * Copy all TX descriptor information into txdesc, * after that we are free to use the skb->cb array * for our information. */ rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc, NULL); /* * Fill in skb descriptor */ skbdesc = get_skb_frame_desc(intf->beacon->skb); memset(skbdesc, 0, sizeof(*skbdesc)); skbdesc->entry = intf->beacon; /* * Send beacon to hardware. */ rt2x00dev->ops->lib->write_beacon(intf->beacon, &txdesc); return 0; } int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif) { struct rt2x00_intf *intf = vif_to_intf(vif); int ret; mutex_lock(&intf->beacon_skb_mutex); ret = rt2x00queue_update_beacon_locked(rt2x00dev, vif); mutex_unlock(&intf->beacon_skb_mutex); return ret; } bool rt2x00queue_for_each_entry(struct data_queue *queue, enum queue_index start, enum queue_index end, bool (*fn)(struct queue_entry *entry)) { unsigned long irqflags; unsigned int index_start; unsigned int index_end; unsigned int i; if (unlikely(start >= Q_INDEX_MAX || end >= Q_INDEX_MAX)) { ERROR(queue->rt2x00dev, "Entry requested from invalid index range (%d - %d)\n", start, end); return true; } /* * Only protect the range we are going to loop over, * if during our loop a extra entry is set to pending * it should not be kicked during this run, since it * is part of another TX operation. */ spin_lock_irqsave(&queue->index_lock, irqflags); index_start = queue->index[start]; index_end = queue->index[end]; spin_unlock_irqrestore(&queue->index_lock, irqflags); /* * Start from the TX done pointer, this guarantees that we will * send out all frames in the correct order. */ if (index_start < index_end) { for (i = index_start; i < index_end; i++) { if (fn(&queue->entries[i])) return true; } } else { for (i = index_start; i < queue->limit; i++) { if (fn(&queue->entries[i])) return true; } for (i = 0; i < index_end; i++) { if (fn(&queue->entries[i])) return true; } } return false; } EXPORT_SYMBOL_GPL(rt2x00queue_for_each_entry); struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, enum queue_index index) { struct queue_entry *entry; unsigned long irqflags; if (unlikely(index >= Q_INDEX_MAX)) { ERROR(queue->rt2x00dev, "Entry requested from invalid index type (%d)\n", index); return NULL; } spin_lock_irqsave(&queue->index_lock, irqflags); entry = &queue->entries[queue->index[index]]; spin_unlock_irqrestore(&queue->index_lock, irqflags); return entry; } EXPORT_SYMBOL_GPL(rt2x00queue_get_entry); void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index) { struct data_queue *queue = entry->queue; unsigned long irqflags; if (unlikely(index >= Q_INDEX_MAX)) { ERROR(queue->rt2x00dev, "Index change on invalid index type (%d)\n", index); return; } spin_lock_irqsave(&queue->index_lock, irqflags); queue->index[index]++; if (queue->index[index] >= queue->limit) queue->index[index] = 0; entry->last_action = jiffies; if (index == Q_INDEX) { queue->length++; } else if (index == Q_INDEX_DONE) { queue->length--; queue->count++; } spin_unlock_irqrestore(&queue->index_lock, irqflags); } void rt2x00queue_pause_queue(struct data_queue *queue) { if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || !test_bit(QUEUE_STARTED, &queue->flags) || test_and_set_bit(QUEUE_PAUSED, &queue->flags)) return; switch (queue->qid) { case QID_AC_VO: case QID_AC_VI: case QID_AC_BE: case QID_AC_BK: /* * For TX queues, we have to disable the queue * inside mac80211. */ ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid); break; default: break; } } EXPORT_SYMBOL_GPL(rt2x00queue_pause_queue); void rt2x00queue_unpause_queue(struct data_queue *queue) { if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || !test_bit(QUEUE_STARTED, &queue->flags) || !test_and_clear_bit(QUEUE_PAUSED, &queue->flags)) return; switch (queue->qid) { case QID_AC_VO: case QID_AC_VI: case QID_AC_BE: case QID_AC_BK: /* * For TX queues, we have to enable the queue * inside mac80211. */ ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid); break; case QID_RX: /* * For RX we need to kick the queue now in order to * receive frames. */ queue->rt2x00dev->ops->lib->kick_queue(queue); default: break; } } EXPORT_SYMBOL_GPL(rt2x00queue_unpause_queue); void rt2x00queue_start_queue(struct data_queue *queue) { mutex_lock(&queue->status_lock); if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || test_and_set_bit(QUEUE_STARTED, &queue->flags)) { mutex_unlock(&queue->status_lock); return; } set_bit(QUEUE_PAUSED, &queue->flags); queue->rt2x00dev->ops->lib->start_queue(queue); rt2x00queue_unpause_queue(queue); mutex_unlock(&queue->status_lock); } EXPORT_SYMBOL_GPL(rt2x00queue_start_queue); void rt2x00queue_stop_queue(struct data_queue *queue) { mutex_lock(&queue->status_lock); if (!test_and_clear_bit(QUEUE_STARTED, &queue->flags)) { mutex_unlock(&queue->status_lock); return; } rt2x00queue_pause_queue(queue); queue->rt2x00dev->ops->lib->stop_queue(queue); mutex_unlock(&queue->status_lock); } EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue); void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) { bool started; bool tx_queue = (queue->qid == QID_AC_VO) || (queue->qid == QID_AC_VI) || (queue->qid == QID_AC_BE) || (queue->qid == QID_AC_BK); mutex_lock(&queue->status_lock); /* * If the queue has been started, we must stop it temporarily * to prevent any new frames to be queued on the device. If * we are not dropping the pending frames, the queue must * only be stopped in the software and not the hardware, * otherwise the queue will never become empty on its own. */ started = test_bit(QUEUE_STARTED, &queue->flags); if (started) { /* * Pause the queue */ rt2x00queue_pause_queue(queue); /* * If we are not supposed to drop any pending * frames, this means we must force a start (=kick) * to the queue to make sure the hardware will * start transmitting. */ if (!drop && tx_queue) queue->rt2x00dev->ops->lib->kick_queue(queue); } /* * Check if driver supports flushing, if that is the case we can * defer the flushing to the driver. Otherwise we must use the * alternative which just waits for the queue to become empty. */ if (likely(queue->rt2x00dev->ops->lib->flush_queue)) queue->rt2x00dev->ops->lib->flush_queue(queue, drop); /* * The queue flush has failed... */ if (unlikely(!rt2x00queue_empty(queue))) WARNING(queue->rt2x00dev, "Queue %d failed to flush\n", queue->qid); /* * Restore the queue to the previous status */ if (started) rt2x00queue_unpause_queue(queue); mutex_unlock(&queue->status_lock); } EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue); void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; /* * rt2x00queue_start_queue will call ieee80211_wake_queue * for each queue after is has been properly initialized. */ tx_queue_for_each(rt2x00dev, queue) rt2x00queue_start_queue(queue); rt2x00queue_start_queue(rt2x00dev->rx); } EXPORT_SYMBOL_GPL(rt2x00queue_start_queues); void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; /* * rt2x00queue_stop_queue will call ieee80211_stop_queue * as well, but we are completely shutting doing everything * now, so it is much safer to stop all TX queues at once, * and use rt2x00queue_stop_queue for cleaning up. */ ieee80211_stop_queues(rt2x00dev->hw); tx_queue_for_each(rt2x00dev, queue) rt2x00queue_stop_queue(queue); rt2x00queue_stop_queue(rt2x00dev->rx); } EXPORT_SYMBOL_GPL(rt2x00queue_stop_queues); void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop) { struct data_queue *queue; tx_queue_for_each(rt2x00dev, queue) rt2x00queue_flush_queue(queue, drop); rt2x00queue_flush_queue(rt2x00dev->rx, drop); } EXPORT_SYMBOL_GPL(rt2x00queue_flush_queues); static void rt2x00queue_reset(struct data_queue *queue) { unsigned long irqflags; unsigned int i; spin_lock_irqsave(&queue->index_lock, irqflags); queue->count = 0; queue->length = 0; for (i = 0; i < Q_INDEX_MAX; i++) queue->index[i] = 0; spin_unlock_irqrestore(&queue->index_lock, irqflags); } void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; unsigned int i; queue_for_each(rt2x00dev, queue) { rt2x00queue_reset(queue); for (i = 0; i < queue->limit; i++) rt2x00dev->ops->lib->clear_entry(&queue->entries[i]); } } static int rt2x00queue_alloc_entries(struct data_queue *queue, const struct data_queue_desc *qdesc) { struct queue_entry *entries; unsigned int entry_size; unsigned int i; rt2x00queue_reset(queue); queue->limit = qdesc->entry_num; queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10); queue->data_size = qdesc->data_size; queue->desc_size = qdesc->desc_size; /* * Allocate all queue entries. */ entry_size = sizeof(*entries) + qdesc->priv_size; entries = kcalloc(queue->limit, entry_size, GFP_KERNEL); if (!entries) return -ENOMEM; #define QUEUE_ENTRY_PRIV_OFFSET(__base, __index, __limit, __esize, __psize) \ (((char *)(__base)) + ((__limit) * (__esize)) + \ ((__index) * (__psize))) for (i = 0; i < queue->limit; i++) { entries[i].flags = 0; entries[i].queue = queue; entries[i].skb = NULL; entries[i].entry_idx = i; entries[i].priv_data = QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit, sizeof(*entries), qdesc->priv_size); } #undef QUEUE_ENTRY_PRIV_OFFSET queue->entries = entries; return 0; } static void rt2x00queue_free_skbs(struct data_queue *queue) { unsigned int i; if (!queue->entries) return; for (i = 0; i < queue->limit; i++) { rt2x00queue_free_skb(&queue->entries[i]); } } static int rt2x00queue_alloc_rxskbs(struct data_queue *queue) { unsigned int i; struct sk_buff *skb; for (i = 0; i < queue->limit; i++) { skb = rt2x00queue_alloc_rxskb(&queue->entries[i], GFP_KERNEL); if (!skb) return -ENOMEM; queue->entries[i].skb = skb; } return 0; } int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; int status; status = rt2x00queue_alloc_entries(rt2x00dev->rx, rt2x00dev->ops->rx); if (status) goto exit; tx_queue_for_each(rt2x00dev, queue) { status = rt2x00queue_alloc_entries(queue, rt2x00dev->ops->tx); if (status) goto exit; } status = rt2x00queue_alloc_entries(rt2x00dev->bcn, rt2x00dev->ops->bcn); if (status) goto exit; if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) { status = rt2x00queue_alloc_entries(rt2x00dev->atim, rt2x00dev->ops->atim); if (status) goto exit; } status = rt2x00queue_alloc_rxskbs(rt2x00dev->rx); if (status) goto exit; return 0; exit: ERROR(rt2x00dev, "Queue entries allocation failed.\n"); rt2x00queue_uninitialize(rt2x00dev); return status; } void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; rt2x00queue_free_skbs(rt2x00dev->rx); queue_for_each(rt2x00dev, queue) { kfree(queue->entries); queue->entries = NULL; } } static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, struct data_queue *queue, enum data_queue_qid qid) { mutex_init(&queue->status_lock); spin_lock_init(&queue->tx_lock); spin_lock_init(&queue->index_lock); queue->rt2x00dev = rt2x00dev; queue->qid = qid; queue->txop = 0; queue->aifs = 2; queue->cw_min = 5; queue->cw_max = 10; } int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; enum data_queue_qid qid; unsigned int req_atim = !!test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); /* * We need the following queues: * RX: 1 * TX: ops->tx_queues * Beacon: 1 * Atim: 1 (if required) */ rt2x00dev->data_queues = 2 + rt2x00dev->ops->tx_queues + req_atim; queue = kcalloc(rt2x00dev->data_queues, sizeof(*queue), GFP_KERNEL); if (!queue) { ERROR(rt2x00dev, "Queue allocation failed.\n"); return -ENOMEM; } /* * Initialize pointers */ rt2x00dev->rx = queue; rt2x00dev->tx = &queue[1]; rt2x00dev->bcn = &queue[1 + rt2x00dev->ops->tx_queues]; rt2x00dev->atim = req_atim ? &queue[2 + rt2x00dev->ops->tx_queues] : NULL; /* * Initialize queue parameters. * RX: qid = QID_RX * TX: qid = QID_AC_VO + index * TX: cw_min: 2^5 = 32. * TX: cw_max: 2^10 = 1024. * BCN: qid = QID_BEACON * ATIM: qid = QID_ATIM */ rt2x00queue_init(rt2x00dev, rt2x00dev->rx, QID_RX); qid = QID_AC_VO; tx_queue_for_each(rt2x00dev, queue) rt2x00queue_init(rt2x00dev, queue, qid++); rt2x00queue_init(rt2x00dev, rt2x00dev->bcn, QID_BEACON); if (req_atim) rt2x00queue_init(rt2x00dev, rt2x00dev->atim, QID_ATIM); return 0; } void rt2x00queue_free(struct rt2x00_dev *rt2x00dev) { kfree(rt2x00dev->rx); rt2x00dev->rx = NULL; rt2x00dev->tx = NULL; rt2x00dev->bcn = NULL; } compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00pci.h0000644000175000017500000000772712026211315023441 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00pci Abstract: Data structures for the rt2x00pci module. */ #ifndef RT2X00PCI_H #define RT2X00PCI_H #include #include /* * This variable should be used with the * pci_driver structure initialization. */ #define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops) /* * Register access. */ static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { *value = readl(rt2x00dev->csr.base + offset); } static inline void rt2x00pci_register_multiread(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u32 length) { memcpy_fromio(value, rt2x00dev->csr.base + offset, length); } static inline void rt2x00pci_register_write(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value) { writel(value, rt2x00dev->csr.base + offset); } static inline void rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const void *value, const u32 length) { __iowrite32_copy(rt2x00dev->csr.base + offset, value, length >> 2); } /** * rt2x00pci_regbusy_read - Read from register with busy check * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * @offset: Register offset * @field: Field to check if register is busy * @reg: Pointer to where register contents should be stored * * This function will read the given register, and checks if the * register is busy. If it is, it will sleep for a couple of * microseconds before reading the register again. If the register * is not read after a certain timeout, this function will return * FALSE. */ int rt2x00pci_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const struct rt2x00_field32 field, u32 *reg); /** * struct queue_entry_priv_pci: Per entry PCI specific information * * @desc: Pointer to device descriptor * @desc_dma: DMA pointer to &desc. * @data: Pointer to device's entry memory. * @data_dma: DMA pointer to &data. */ struct queue_entry_priv_pci { __le32 *desc; dma_addr_t desc_dma; }; /** * rt2x00pci_rxdone - Handle RX done events * @rt2x00dev: Device pointer, see &struct rt2x00_dev. * * Returns true if there are still rx frames pending and false if all * pending rx frames were processed. */ bool rt2x00pci_rxdone(struct rt2x00_dev *rt2x00dev); /** * rt2x00pci_flush_queue - Flush data queue * @queue: Data queue to stop * @drop: True to drop all pending frames. * * This will wait for a maximum of 100ms, waiting for the queues * to become empty. */ void rt2x00pci_flush_queue(struct data_queue *queue, bool drop); /* * Device initialization handlers. */ int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev); void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev); /* * PCI driver handlers. */ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops); void rt2x00pci_remove(struct pci_dev *pci_dev); #ifdef CONFIG_PM int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state); int rt2x00pci_resume(struct pci_dev *pci_dev); #else #define rt2x00pci_suspend NULL #define rt2x00pci_resume NULL #endif /* CONFIG_PM */ #endif /* RT2X00PCI_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00pci.c0000644000175000017500000002156212026211315023425 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00pci Abstract: rt2x00 generic pci device routines. */ #include #include #include #include #include #include "rt2x00.h" #include "rt2x00pci.h" /* * Register access. */ int rt2x00pci_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const struct rt2x00_field32 field, u32 *reg) { unsigned int i; if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00pci_register_read(rt2x00dev, offset, reg); if (!rt2x00_get_field32(*reg, field)) return 1; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "Indirect register access failed: " "offset=0x%.08x, value=0x%.08x\n", offset, *reg); *reg = ~0; return 0; } EXPORT_SYMBOL_GPL(rt2x00pci_regbusy_read); bool rt2x00pci_rxdone(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue = rt2x00dev->rx; struct queue_entry *entry; struct queue_entry_priv_pci *entry_priv; struct skb_frame_desc *skbdesc; int max_rx = 16; while (--max_rx) { entry = rt2x00queue_get_entry(queue, Q_INDEX); entry_priv = entry->priv_data; if (rt2x00dev->ops->lib->get_entry_state(entry)) break; /* * Fill in desc fields of the skb descriptor */ skbdesc = get_skb_frame_desc(entry->skb); skbdesc->desc = entry_priv->desc; skbdesc->desc_len = entry->queue->desc_size; /* * DMA is already done, notify rt2x00lib that * it finished successfully. */ rt2x00lib_dmastart(entry); rt2x00lib_dmadone(entry); /* * Send the frame to rt2x00lib for further processing. */ rt2x00lib_rxdone(entry, GFP_ATOMIC); } return !max_rx; } EXPORT_SYMBOL_GPL(rt2x00pci_rxdone); void rt2x00pci_flush_queue(struct data_queue *queue, bool drop) { unsigned int i; for (i = 0; !rt2x00queue_empty(queue) && i < 10; i++) msleep(10); } EXPORT_SYMBOL_GPL(rt2x00pci_flush_queue); /* * Device initialization handlers. */ static int rt2x00pci_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, struct data_queue *queue) { struct queue_entry_priv_pci *entry_priv; void *addr; dma_addr_t dma; unsigned int i; /* * Allocate DMA memory for descriptor and buffer. */ addr = dma_alloc_coherent(rt2x00dev->dev, queue->limit * queue->desc_size, &dma, GFP_KERNEL); if (!addr) return -ENOMEM; memset(addr, 0, queue->limit * queue->desc_size); /* * Initialize all queue entries to contain valid addresses. */ for (i = 0; i < queue->limit; i++) { entry_priv = queue->entries[i].priv_data; entry_priv->desc = addr + i * queue->desc_size; entry_priv->desc_dma = dma + i * queue->desc_size; } return 0; } static void rt2x00pci_free_queue_dma(struct rt2x00_dev *rt2x00dev, struct data_queue *queue) { struct queue_entry_priv_pci *entry_priv = queue->entries[0].priv_data; if (entry_priv->desc) dma_free_coherent(rt2x00dev->dev, queue->limit * queue->desc_size, entry_priv->desc, entry_priv->desc_dma); entry_priv->desc = NULL; } int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; int status; /* * Allocate DMA */ queue_for_each(rt2x00dev, queue) { status = rt2x00pci_alloc_queue_dma(rt2x00dev, queue); if (status) goto exit; } /* * Register interrupt handler. */ status = request_irq(rt2x00dev->irq, rt2x00dev->ops->lib->irq_handler, IRQF_SHARED, rt2x00dev->name, rt2x00dev); if (status) { ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n", rt2x00dev->irq, status); goto exit; } return 0; exit: queue_for_each(rt2x00dev, queue) rt2x00pci_free_queue_dma(rt2x00dev, queue); return status; } EXPORT_SYMBOL_GPL(rt2x00pci_initialize); void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; /* * Free irq line. */ free_irq(rt2x00dev->irq, rt2x00dev); /* * Free DMA */ queue_for_each(rt2x00dev, queue) rt2x00pci_free_queue_dma(rt2x00dev, queue); } EXPORT_SYMBOL_GPL(rt2x00pci_uninitialize); /* * PCI driver handlers. */ static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) { kfree(rt2x00dev->rf); rt2x00dev->rf = NULL; kfree(rt2x00dev->eeprom); rt2x00dev->eeprom = NULL; if (rt2x00dev->csr.base) { iounmap(rt2x00dev->csr.base); rt2x00dev->csr.base = NULL; } } static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) { struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0); if (!rt2x00dev->csr.base) goto exit; rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); if (!rt2x00dev->eeprom) goto exit; rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); if (!rt2x00dev->rf) goto exit; return 0; exit: ERROR_PROBE("Failed to allocate registers.\n"); rt2x00pci_free_reg(rt2x00dev); return -ENOMEM; } int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) { struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; u16 chip; retval = pci_enable_device(pci_dev); if (retval) { ERROR_PROBE("Enable device failed.\n"); return retval; } retval = pci_request_regions(pci_dev, pci_name(pci_dev)); if (retval) { ERROR_PROBE("PCI request regions failed.\n"); goto exit_disable_device; } pci_set_master(pci_dev); if (pci_set_mwi(pci_dev)) ERROR_PROBE("MWI not available.\n"); if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { ERROR_PROBE("PCI DMA not supported.\n"); retval = -EIO; goto exit_release_regions; } hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { ERROR_PROBE("Failed to allocate hardware.\n"); retval = -ENOMEM; goto exit_release_regions; } pci_set_drvdata(pci_dev, hw); rt2x00dev = hw->priv; rt2x00dev->dev = &pci_dev->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; rt2x00dev->irq = pci_dev->irq; rt2x00dev->name = pci_name(pci_dev); if (pci_is_pcie(pci_dev)) rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); else rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); retval = rt2x00pci_alloc_reg(rt2x00dev); if (retval) goto exit_free_device; /* * Because rt3290 chip use different efuse offset to read efuse data. * So before read efuse it need to indicate it is the * rt3290 or not. */ pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); rt2x00dev->chip.rt = chip; retval = rt2x00lib_probe_dev(rt2x00dev); if (retval) goto exit_free_reg; return 0; exit_free_reg: rt2x00pci_free_reg(rt2x00dev); exit_free_device: ieee80211_free_hw(hw); exit_release_regions: pci_release_regions(pci_dev); exit_disable_device: pci_disable_device(pci_dev); pci_set_drvdata(pci_dev, NULL); return retval; } EXPORT_SYMBOL_GPL(rt2x00pci_probe); void rt2x00pci_remove(struct pci_dev *pci_dev) { struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); struct rt2x00_dev *rt2x00dev = hw->priv; /* * Free all allocated data. */ rt2x00lib_remove_dev(rt2x00dev); rt2x00pci_free_reg(rt2x00dev); ieee80211_free_hw(hw); /* * Free the PCI device data. */ pci_set_drvdata(pci_dev, NULL); pci_disable_device(pci_dev); pci_release_regions(pci_dev); } EXPORT_SYMBOL_GPL(rt2x00pci_remove); #ifdef CONFIG_PM int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) { struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); struct rt2x00_dev *rt2x00dev = hw->priv; int retval; retval = rt2x00lib_suspend(rt2x00dev, state); if (retval) return retval; pci_save_state(pci_dev); pci_disable_device(pci_dev); return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); } EXPORT_SYMBOL_GPL(rt2x00pci_suspend); int rt2x00pci_resume(struct pci_dev *pci_dev) { struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); struct rt2x00_dev *rt2x00dev = hw->priv; if (pci_set_power_state(pci_dev, PCI_D0) || pci_enable_device(pci_dev)) { ERROR(rt2x00dev, "Failed to resume device.\n"); return -EIO; } pci_restore_state(pci_dev); return rt2x00lib_resume(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00pci_resume); #endif /* CONFIG_PM */ /* * rt2x00pci module information. */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("rt2x00 pci library"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00mac.c0000644000175000017500000006150112026211315023407 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00mac Abstract: rt2x00 generic mac80211 routines. */ #include #include #include "rt2x00.h" #include "rt2x00lib.h" static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, struct data_queue *queue, struct sk_buff *frag_skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(frag_skb); struct ieee80211_tx_info *rts_info; struct sk_buff *skb; unsigned int data_length; int retval = 0; if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) data_length = sizeof(struct ieee80211_cts); else data_length = sizeof(struct ieee80211_rts); skb = dev_alloc_skb(data_length + rt2x00dev->hw->extra_tx_headroom); if (unlikely(!skb)) { WARNING(rt2x00dev, "Failed to create RTS/CTS frame.\n"); return -ENOMEM; } skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); skb_put(skb, data_length); /* * Copy TX information over from original frame to * RTS/CTS frame. Note that we set the no encryption flag * since we don't want this frame to be encrypted. * RTS frames should be acked, while CTS-to-self frames * should not. The ready for TX flag is cleared to prevent * it being automatically send when the descriptor is * written to the hardware. */ memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb)); rts_info = IEEE80211_SKB_CB(skb); rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT; if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) rts_info->flags |= IEEE80211_TX_CTL_NO_ACK; else rts_info->flags &= ~IEEE80211_TX_CTL_NO_ACK; /* Disable hardware encryption */ rts_info->control.hw_key = NULL; /* * RTS/CTS frame should use the length of the frame plus any * encryption overhead that will be added by the hardware. */ data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) ieee80211_ctstoself_get(rt2x00dev->hw, tx_info->control.vif, frag_skb->data, data_length, tx_info, (struct ieee80211_cts *)(skb->data)); else ieee80211_rts_get(rt2x00dev->hw, tx_info->control.vif, frag_skb->data, data_length, tx_info, (struct ieee80211_rts *)(skb->data)); retval = rt2x00queue_write_tx_frame(queue, skb, true); if (retval) { dev_kfree_skb_any(skb); WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); } return retval; } void rt2x00mac_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); enum data_queue_qid qid = skb_get_queue_mapping(skb); struct data_queue *queue = NULL; /* * Mac80211 might be calling this function while we are trying * to remove the device or perhaps suspending it. * Note that we can only stop the TX queues inside the TX path * due to possible race conditions in mac80211. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) goto exit_free_skb; /* * Use the ATIM queue if appropriate and present. */ if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) qid = QID_ATIM; queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); if (unlikely(!queue)) { ERROR(rt2x00dev, "Attempt to send packet over invalid queue %d.\n" "Please file bug report to %s.\n", qid, DRV_PROJECT); goto exit_free_skb; } /* * If CTS/RTS is required. create and queue that frame first. * Make sure we have at least enough entries available to send * this CTS/RTS frame as well as the data frame. * Note that when the driver has set the set_rts_threshold() * callback function it doesn't need software generation of * either RTS or CTS-to-self frame and handles everything * inside the hardware. */ if (!rt2x00dev->ops->hw->set_rts_threshold && (tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS | IEEE80211_TX_RC_USE_CTS_PROTECT))) { if (rt2x00queue_available(queue) <= 1) goto exit_fail; if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb)) goto exit_fail; } if (unlikely(rt2x00queue_write_tx_frame(queue, skb, false))) goto exit_fail; /* * Pausing queue has to be serialized with rt2x00lib_txdone(). Note * we should not use spin_lock_bh variant as bottom halve was already * disabled before ieee80211_xmit() call. */ spin_lock(&queue->tx_lock); if (rt2x00queue_threshold(queue)) rt2x00queue_pause_queue(queue); spin_unlock(&queue->tx_lock); return; exit_fail: spin_lock(&queue->tx_lock); rt2x00queue_pause_queue(queue); spin_unlock(&queue->tx_lock); exit_free_skb: ieee80211_free_txskb(hw, skb); } EXPORT_SYMBOL_GPL(rt2x00mac_tx); int rt2x00mac_start(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; return rt2x00lib_start(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00mac_start); void rt2x00mac_stop(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return; rt2x00lib_stop(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00mac_stop); int rt2x00mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_intf *intf = vif_to_intf(vif); struct data_queue *queue = rt2x00dev->bcn; struct queue_entry *entry = NULL; unsigned int i; /* * Don't allow interfaces to be added * the device has disappeared. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) return -ENODEV; switch (vif->type) { case NL80211_IFTYPE_AP: /* * We don't support mixed combinations of * sta and ap interfaces. */ if (rt2x00dev->intf_sta_count) return -ENOBUFS; /* * Check if we exceeded the maximum amount * of supported interfaces. */ if (rt2x00dev->intf_ap_count >= rt2x00dev->ops->max_ap_intf) return -ENOBUFS; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_WDS: /* * We don't support mixed combinations of * sta and ap interfaces. */ if (rt2x00dev->intf_ap_count) return -ENOBUFS; /* * Check if we exceeded the maximum amount * of supported interfaces. */ if (rt2x00dev->intf_sta_count >= rt2x00dev->ops->max_sta_intf) return -ENOBUFS; break; default: return -EINVAL; } /* * Loop through all beacon queues to find a free * entry. Since there are as much beacon entries * as the maximum interfaces, this search shouldn't * fail. */ for (i = 0; i < queue->limit; i++) { entry = &queue->entries[i]; if (!test_and_set_bit(ENTRY_BCN_ASSIGNED, &entry->flags)) break; } if (unlikely(i == queue->limit)) return -ENOBUFS; /* * We are now absolutely sure the interface can be created, * increase interface count and start initialization. */ if (vif->type == NL80211_IFTYPE_AP) rt2x00dev->intf_ap_count++; else rt2x00dev->intf_sta_count++; mutex_init(&intf->beacon_skb_mutex); intf->beacon = entry; /* * The MAC address must be configured after the device * has been initialized. Otherwise the device can reset * the MAC registers. * The BSSID address must only be configured in AP mode, * however we should not send an empty BSSID address for * STA interfaces at this time, since this can cause * invalid behavior in the device. */ rt2x00lib_config_intf(rt2x00dev, intf, vif->type, vif->addr, NULL); /* * Some filters depend on the current working mode. We can force * an update during the next configure_filter() run by mac80211 by * resetting the current packet_filter state. */ rt2x00dev->packet_filter = 0; return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); void rt2x00mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_intf *intf = vif_to_intf(vif); /* * Don't allow interfaces to be remove while * either the device has disappeared or when * no interface is present. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || (vif->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) || (vif->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count)) return; if (vif->type == NL80211_IFTYPE_AP) rt2x00dev->intf_ap_count--; else rt2x00dev->intf_sta_count--; /* * Release beacon entry so it is available for * new interfaces again. */ clear_bit(ENTRY_BCN_ASSIGNED, &intf->beacon->flags); /* * Make sure the bssid and mac address registers * are cleared to prevent false ACKing of frames. */ rt2x00lib_config_intf(rt2x00dev, intf, NL80211_IFTYPE_UNSPECIFIED, NULL, NULL); } EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_conf *conf = &hw->conf; /* * mac80211 might be calling this function while we are trying * to remove the device or perhaps suspending it. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; /* * Some configuration parameters (e.g. channel and antenna values) can * only be set when the radio is enabled, but do require the RX to * be off. During this period we should keep link tuning enabled, * if for any reason the link tuner must be reset, this will be * handled by rt2x00lib_config(). */ rt2x00queue_stop_queue(rt2x00dev->rx); /* * When we've just turned on the radio, we want to reprogram * everything to ensure a consistent state */ rt2x00lib_config(rt2x00dev, conf, changed); /* * After the radio has been enabled we need to configure * the antenna to the default settings. rt2x00lib_config_antenna() * should determine if any action should be taken based on * checking if diversity has been enabled or no antenna changes * have been made since the last configuration change. */ rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant); /* Turn RX back on */ rt2x00queue_start_queue(rt2x00dev->rx); return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_config); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct rt2x00_dev *rt2x00dev = hw->priv; /* * Mask off any flags we are going to ignore * from the total_flags field. */ *total_flags &= FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL | FIF_CONTROL | FIF_PSPOLL | FIF_OTHER_BSS | FIF_PROMISC_IN_BSS; /* * Apply some rules to the filters: * - Some filters imply different filters to be set. * - Some things we can't filter out at all. * - Multicast filter seems to kill broadcast traffic so never use it. */ *total_flags |= FIF_ALLMULTI; if (*total_flags & FIF_OTHER_BSS || *total_flags & FIF_PROMISC_IN_BSS) *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; /* * If the device has a single filter for all control frames, * FIF_CONTROL and FIF_PSPOLL flags imply each other. * And if the device has more than one filter for control frames * of different types, but has no a separate filter for PS Poll frames, * FIF_CONTROL flag implies FIF_PSPOLL. */ if (!test_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags)) { if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL) *total_flags |= FIF_CONTROL | FIF_PSPOLL; } if (!test_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags)) { if (*total_flags & FIF_CONTROL) *total_flags |= FIF_PSPOLL; } /* * Check if there is any work left for us. */ if (rt2x00dev->packet_filter == *total_flags) return; rt2x00dev->packet_filter = *total_flags; rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); } EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); static void rt2x00mac_set_tim_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rt2x00_intf *intf = vif_to_intf(vif); if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_ADHOC && vif->type != NL80211_IFTYPE_MESH_POINT && vif->type != NL80211_IFTYPE_WDS) return; set_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags); } int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { struct rt2x00_dev *rt2x00dev = hw->priv; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, rt2x00mac_set_tim_iter, rt2x00dev); /* queue work to upodate the beacon template */ ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_set_tim); #ifdef CONFIG_RT2X00_LIB_CRYPTO static void memcpy_tkip(struct rt2x00lib_crypto *crypto, u8 *key, u8 key_len) { if (key_len > NL80211_TKIP_DATA_OFFSET_ENCR_KEY) memcpy(crypto->key, &key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY], sizeof(crypto->key)); if (key_len > NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) memcpy(crypto->tx_mic, &key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], sizeof(crypto->tx_mic)); if (key_len > NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY) memcpy(crypto->rx_mic, &key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], sizeof(crypto->rx_mic)); } int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct rt2x00_dev *rt2x00dev = hw->priv; int (*set_key) (struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); struct rt2x00lib_crypto crypto; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; struct rt2x00_sta *sta_priv = NULL; if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) return -EOPNOTSUPP; /* * To support IBSS RSN, don't program group keys in IBSS, the * hardware will then not attempt to decrypt the frames. */ if (vif->type == NL80211_IFTYPE_ADHOC && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) return -EOPNOTSUPP; if (key->keylen > 32) return -ENOSPC; memset(&crypto, 0, sizeof(crypto)); crypto.bssidx = rt2x00lib_get_bssidx(rt2x00dev, vif); crypto.cipher = rt2x00crypto_key_to_cipher(key); if (crypto.cipher == CIPHER_NONE) return -EOPNOTSUPP; crypto.cmd = cmd; if (sta) { crypto.address = sta->addr; sta_priv = sta_to_rt2x00_sta(sta); crypto.wcid = sta_priv->wcid; } else crypto.address = bcast_addr; if (crypto.cipher == CIPHER_TKIP) memcpy_tkip(&crypto, &key->key[0], key->keylen); else memcpy(crypto.key, &key->key[0], key->keylen); /* * Each BSS has a maximum of 4 shared keys. * Shared key index values: * 0) BSS0 key0 * 1) BSS0 key1 * ... * 4) BSS1 key0 * ... * 8) BSS2 key0 * ... * Both pairwise as shared key indeces are determined by * driver. This is required because the hardware requires * keys to be assigned in correct order (When key 1 is * provided but key 0 is not, then the key is not found * by the hardware during RX). */ if (cmd == SET_KEY) key->hw_key_idx = 0; if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) set_key = rt2x00dev->ops->lib->config_pairwise_key; else set_key = rt2x00dev->ops->lib->config_shared_key; if (!set_key) return -EOPNOTSUPP; return set_key(rt2x00dev, &crypto, key); } EXPORT_SYMBOL_GPL(rt2x00mac_set_key); #endif /* CONFIG_RT2X00_LIB_CRYPTO */ int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); /* * If there's no space left in the device table store * -1 as wcid but tell mac80211 everything went ok. */ if (rt2x00dev->ops->lib->sta_add(rt2x00dev, vif, sta)) sta_priv->wcid = -1; return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_sta_add); int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); /* * If we never sent the STA to the device no need to clean it up. */ if (sta_priv->wcid < 0) return 0; return rt2x00dev->ops->lib->sta_remove(rt2x00dev, sta_priv->wcid); } EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove); void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); rt2x00link_stop_tuner(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start); void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); rt2x00link_start_tuner(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_complete); int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct rt2x00_dev *rt2x00dev = hw->priv; /* * The dot11ACKFailureCount, dot11RTSFailureCount and * dot11RTSSuccessCount are updated in interrupt time. * dot11FCSErrorCount is updated in the link tuner. */ memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_intf *intf = vif_to_intf(vif); /* * mac80211 might be calling this function while we are trying * to remove the device or perhaps suspending it. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return; /* * Update the BSSID. */ if (changes & BSS_CHANGED_BSSID) rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, bss_conf->bssid); /* * Update the beacon. This is only required on USB devices. PCI * devices fetch beacons periodically. */ if (changes & BSS_CHANGED_BEACON && rt2x00_is_usb(rt2x00dev)) rt2x00queue_update_beacon(rt2x00dev, vif); /* * Start/stop beaconing. */ if (changes & BSS_CHANGED_BEACON_ENABLED) { if (!bss_conf->enable_beacon && intf->enable_beacon) { rt2x00queue_clear_beacon(rt2x00dev, vif); rt2x00dev->intf_beaconing--; intf->enable_beacon = false; if (rt2x00dev->intf_beaconing == 0) { /* * Last beaconing interface disabled * -> stop beacon queue. */ mutex_lock(&intf->beacon_skb_mutex); rt2x00queue_stop_queue(rt2x00dev->bcn); mutex_unlock(&intf->beacon_skb_mutex); } } else if (bss_conf->enable_beacon && !intf->enable_beacon) { rt2x00dev->intf_beaconing++; intf->enable_beacon = true; if (rt2x00dev->intf_beaconing == 1) { /* * First beaconing interface enabled * -> start beacon queue. */ mutex_lock(&intf->beacon_skb_mutex); rt2x00queue_start_queue(rt2x00dev->bcn); mutex_unlock(&intf->beacon_skb_mutex); } } } /* * When the association status has changed we must reset the link * tuner counter. This is because some drivers determine if they * should perform link tuning based on the number of seconds * while associated or not associated. */ if (changes & BSS_CHANGED_ASSOC) { rt2x00dev->link.count = 0; if (bss_conf->assoc) rt2x00dev->intf_associated++; else rt2x00dev->intf_associated--; rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); clear_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); } /* * Check for access point which do not support 802.11e . We have to * generate data frames sequence number in S/W for such AP, because * of H/W bug. */ if (changes & BSS_CHANGED_QOS && !bss_conf->qos) set_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); /* * When the erp information has changed, we should perform * additional configuration steps. For all other changes we are done. */ if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT | BSS_CHANGED_HT)) rt2x00lib_config_erp(rt2x00dev, intf, bss_conf, changes); } EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); int rt2x00mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); if (unlikely(!queue)) return -EINVAL; /* * The passed variables are stored as real value ((2^n)-1). * Ralink registers require to know the bit number 'n'. */ if (params->cw_min > 0) queue->cw_min = fls(params->cw_min); else queue->cw_min = 5; /* cw_min: 2^5 = 32. */ if (params->cw_max > 0) queue->cw_max = fls(params->cw_max); else queue->cw_max = 10; /* cw_min: 2^10 = 1024. */ queue->aifs = params->aifs; queue->txop = params->txop; INFO(rt2x00dev, "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d.\n", queue_idx, queue->cw_min, queue->cw_max, queue->aifs, queue->txop); return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; bool active = !!rt2x00dev->ops->lib->rfkill_poll(rt2x00dev); wiphy_rfkill_set_hw_state(hw->wiphy, !active); } EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; tx_queue_for_each(rt2x00dev, queue) rt2x00queue_flush_queue(queue, drop); } EXPORT_SYMBOL_GPL(rt2x00mac_flush); int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) { struct rt2x00_dev *rt2x00dev = hw->priv; struct link_ant *ant = &rt2x00dev->link.ant; struct antenna_setup *def = &rt2x00dev->default_ant; struct antenna_setup setup; // The antenna value is not supposed to be 0, // or exceed the maximum number of antenna's. if (!tx_ant || (tx_ant & ~3) || !rx_ant || (rx_ant & ~3)) return -EINVAL; // When the client tried to configure the antenna to or from // diversity mode, we must reset the default antenna as well // as that controls the diversity switch. if (ant->flags & ANTENNA_TX_DIVERSITY && tx_ant != 3) ant->flags &= ~ANTENNA_TX_DIVERSITY; if (ant->flags & ANTENNA_RX_DIVERSITY && rx_ant != 3) ant->flags &= ~ANTENNA_RX_DIVERSITY; // If diversity is being enabled, check if we need hardware // or software diversity. In the latter case, reset the value, // and make sure we update the antenna flags to have the // link tuner pick up the diversity tuning. if (tx_ant == 3 && def->tx == ANTENNA_SW_DIVERSITY) { tx_ant = ANTENNA_SW_DIVERSITY; ant->flags |= ANTENNA_TX_DIVERSITY; } if (rx_ant == 3 && def->rx == ANTENNA_SW_DIVERSITY) { rx_ant = ANTENNA_SW_DIVERSITY; ant->flags |= ANTENNA_RX_DIVERSITY; } setup.tx = tx_ant; setup.rx = rx_ant; rt2x00lib_config_antenna(rt2x00dev, setup); return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_set_antenna); int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) { struct rt2x00_dev *rt2x00dev = hw->priv; struct link_ant *ant = &rt2x00dev->link.ant; struct antenna_setup *active = &rt2x00dev->link.ant.active; // When software diversity is active, we must report this to the // client and not the current active antenna state. if (ant->flags & ANTENNA_TX_DIVERSITY) *tx_ant = ANTENNA_HW_DIVERSITY; else *tx_ant = active->tx; if (ant->flags & ANTENNA_RX_DIVERSITY) *rx_ant = ANTENNA_HW_DIVERSITY; else *rx_ant = active->rx; return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_get_antenna); void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; tx_queue_for_each(rt2x00dev, queue) { *tx += queue->length; *tx_max += queue->limit; } *rx = rt2x00dev->rx->length; *rx_max = rt2x00dev->rx->limit; } EXPORT_SYMBOL_GPL(rt2x00mac_get_ringparam); bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; tx_queue_for_each(rt2x00dev, queue) { if (!rt2x00queue_empty(queue)) return true; } return false; } EXPORT_SYMBOL_GPL(rt2x00mac_tx_frames_pending); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00link.c0000644000175000017500000003510712026211315023607 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 generic link tuning routines. */ #include #include #include "rt2x00.h" #include "rt2x00lib.h" /* * When we lack RSSI information return something less then -80 to * tell the driver to tune the device to maximum sensitivity. */ #define DEFAULT_RSSI -128 /* * Helper struct and macro to work with moving/walking averages. * When adding a value to the average value the following calculation * is needed: * * avg_rssi = ((avg_rssi * 7) + rssi) / 8; * * The advantage of this approach is that we only need 1 variable * to store the average in (No need for a count and a total). * But more importantly, normal average values will over time * move less and less towards newly added values this results * that with link tuning, the device can have a very good RSSI * for a few minutes but when the device is moved away from the AP * the average will not decrease fast enough to compensate. * The walking average compensates this and will move towards * the new values correctly allowing a effective link tuning, * the speed of the average moving towards other values depends * on the value for the number of samples. The higher the number * of samples, the slower the average will move. * We use two variables to keep track of the average value to * compensate for the rounding errors. This can be a significant * error (>5dBm) if the factor is too low. */ #define AVG_SAMPLES 8 #define AVG_FACTOR 1000 #define MOVING_AVERAGE(__avg, __val) \ ({ \ struct avg_val __new; \ __new.avg_weight = \ (__avg).avg_weight ? \ ((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \ ((__val) * (AVG_FACTOR))) / \ (AVG_SAMPLES)) : \ ((__val) * (AVG_FACTOR)); \ __new.avg = __new.avg_weight / (AVG_FACTOR); \ __new; \ }) static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success) return ant->rssi_ant.avg; return DEFAULT_RSSI; } static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; if (ant->rssi_history) return ant->rssi_history; return DEFAULT_RSSI; } static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, int rssi) { struct link_ant *ant = &rt2x00dev->link.ant; ant->rssi_history = rssi; } static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) { rt2x00dev->link.ant.rssi_ant.avg = 0; rt2x00dev->link.ant.rssi_ant.avg_weight = 0; } static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; struct antenna_setup new_ant; int other_antenna; int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev); int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev); memcpy(&new_ant, &ant->active, sizeof(new_ant)); /* * We are done sampling. Now we should evaluate the results. */ ant->flags &= ~ANTENNA_MODE_SAMPLE; /* * During the last period we have sampled the RSSI * from both antennas. It now is time to determine * which antenna demonstrated the best performance. * When we are already on the antenna with the best * performance, just create a good starting point * for the history and we are done. */ if (sample_current >= sample_other) { rt2x00link_antenna_update_rssi_history(rt2x00dev, sample_current); return; } other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; if (ant->flags & ANTENNA_RX_DIVERSITY) new_ant.rx = other_antenna; if (ant->flags & ANTENNA_TX_DIVERSITY) new_ant.tx = other_antenna; rt2x00lib_config_antenna(rt2x00dev, new_ant); } static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; struct antenna_setup new_ant; int rssi_curr; int rssi_old; memcpy(&new_ant, &ant->active, sizeof(new_ant)); /* * Get current RSSI value along with the historical value, * after that update the history with the current value. */ rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev); rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev); rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr); /* * Legacy driver indicates that we should swap antenna's * when the difference in RSSI is greater that 5. This * also should be done when the RSSI was actually better * then the previous sample. * When the difference exceeds the threshold we should * sample the rssi from the other antenna to make a valid * comparison between the 2 antennas. */ if (abs(rssi_curr - rssi_old) < 5) return; ant->flags |= ANTENNA_MODE_SAMPLE; if (ant->flags & ANTENNA_RX_DIVERSITY) new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; if (ant->flags & ANTENNA_TX_DIVERSITY) new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; rt2x00lib_config_antenna(rt2x00dev, new_ant); } static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; /* * Determine if software diversity is enabled for * either the TX or RX antenna (or both). */ if (!(ant->flags & ANTENNA_RX_DIVERSITY) && !(ant->flags & ANTENNA_TX_DIVERSITY)) { ant->flags = 0; return true; } /* * If we have only sampled the data over the last period * we should now harvest the data. Otherwise just evaluate * the data. The latter should only be performed once * every 2 seconds. */ if (ant->flags & ANTENNA_MODE_SAMPLE) { rt2x00lib_antenna_diversity_sample(rt2x00dev); return true; } else if (rt2x00dev->link.count & 1) { rt2x00lib_antenna_diversity_eval(rt2x00dev); return true; } return false; } void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct rxdone_entry_desc *rxdesc) { struct link *link = &rt2x00dev->link; struct link_qual *qual = &rt2x00dev->link.qual; struct link_ant *ant = &rt2x00dev->link.ant; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; /* * No need to update the stats for !=STA interfaces */ if (!rt2x00dev->intf_sta_count) return; /* * Frame was received successfully since non-succesfull * frames would have been dropped by the hardware. */ qual->rx_success++; /* * We are only interested in quality statistics from * beacons which came from the BSS which we are * associated with. */ if (!ieee80211_is_beacon(hdr->frame_control) || !(rxdesc->dev_flags & RXDONE_MY_BSS)) return; /* * Update global RSSI */ link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi); /* * Update antenna RSSI */ ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi); } void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) { struct link *link = &rt2x00dev->link; /* * Link tuning should only be performed when * an active sta interface exists. AP interfaces * don't need link tuning and monitor mode interfaces * should never have to work with link tuners. */ if (!rt2x00dev->intf_sta_count) return; /** * While scanning, link tuning is disabled. By default * the most sensitive settings will be used to make sure * that all beacons and probe responses will be received * during the scan. */ if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) return; rt2x00link_reset_tuner(rt2x00dev, false); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) { cancel_delayed_work_sync(&rt2x00dev->link.work); } void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) { struct link_qual *qual = &rt2x00dev->link.qual; u8 vgc_level = qual->vgc_level_reg; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* * Reset link information. * Both the currently active vgc level as well as * the link tuner counter should be reset. Resetting * the counter is important for devices where the * device should only perform link tuning during the * first minute after being enabled. */ rt2x00dev->link.count = 0; memset(qual, 0, sizeof(*qual)); /* * Restore the VGC level as stored in the registers, * the driver can use this to determine if the register * must be updated during reset or not. */ qual->vgc_level_reg = vgc_level; /* * Reset the link tuner. */ rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual); if (antenna) rt2x00link_antenna_reset(rt2x00dev); } static void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev) { struct link_qual *qual = &rt2x00dev->link.qual; qual->rx_success = 0; qual->rx_failed = 0; qual->tx_success = 0; qual->tx_failed = 0; } static void rt2x00link_tuner(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, link.work.work); struct link *link = &rt2x00dev->link; struct link_qual *qual = &rt2x00dev->link.qual; /* * When the radio is shutting down we should * immediately cease all link tuning. */ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) return; /* * Update statistics. */ rt2x00dev->ops->lib->link_stats(rt2x00dev, qual); rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed; /* * Update quality RSSI for link tuning, * when we have received some frames and we managed to * collect the RSSI data we could use this. Otherwise we * must fallback to the default RSSI value. */ if (!link->avg_rssi.avg || !qual->rx_success) qual->rssi = DEFAULT_RSSI; else qual->rssi = link->avg_rssi.avg; /* * Check if link tuning is supported by the hardware, some hardware * do not support link tuning at all, while other devices can disable * the feature from the EEPROM. */ if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags)) rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); /* * Send a signal to the led to update the led signal strength. */ rt2x00leds_led_quality(rt2x00dev, qual->rssi); /* * Evaluate antenna setup, make this the last step when * rt2x00lib_antenna_diversity made changes the quality * statistics will be reset. */ if (rt2x00lib_antenna_diversity(rt2x00dev)) rt2x00link_reset_qual(rt2x00dev); /* * Increase tuner counter, and reschedule the next link tuner run. */ link->count++; if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev) { struct link *link = &rt2x00dev->link; if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && rt2x00dev->ops->lib->watchdog) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->watchdog_work, WATCHDOG_INTERVAL); } void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev) { cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work); } static void rt2x00link_watchdog(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, link.watchdog_work.work); struct link *link = &rt2x00dev->link; /* * When the radio is shutting down we should * immediately cease the watchdog monitoring. */ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; rt2x00dev->ops->lib->watchdog(rt2x00dev); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->watchdog_work, WATCHDOG_INTERVAL); } void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev) { struct link *link = &rt2x00dev->link; if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && rt2x00dev->ops->lib->gain_calibration) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->agc_work, AGC_INTERVAL); } void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev) { struct link *link = &rt2x00dev->link; if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && rt2x00dev->ops->lib->vco_calibration) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->vco_work, VCO_INTERVAL); } void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev) { cancel_delayed_work_sync(&rt2x00dev->link.agc_work); } void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev) { cancel_delayed_work_sync(&rt2x00dev->link.vco_work); } static void rt2x00link_agc(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, link.agc_work.work); struct link *link = &rt2x00dev->link; /* * When the radio is shutting down we should * immediately cease the watchdog monitoring. */ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; rt2x00dev->ops->lib->gain_calibration(rt2x00dev); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->agc_work, AGC_INTERVAL); } static void rt2x00link_vcocal(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, link.vco_work.work); struct link *link = &rt2x00dev->link; /* * When the radio is shutting down we should * immediately cease the VCO calibration. */ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; rt2x00dev->ops->lib->vco_calibration(rt2x00dev); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) ieee80211_queue_delayed_work(rt2x00dev->hw, &link->vco_work, VCO_INTERVAL); } void rt2x00link_register(struct rt2x00_dev *rt2x00dev) { INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal); INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); } compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00lib.h0000644000175000017500000003402612026211315023424 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn Copyright (C) 2004 - 2009 Gertjan van Wingerde This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: Data structures and definitions for the rt2x00lib module. */ #ifndef RT2X00LIB_H #define RT2X00LIB_H /* * Interval defines */ #define WATCHDOG_INTERVAL round_jiffies_relative(HZ) #define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) #define AGC_INTERVAL round_jiffies_relative(4 * HZ) #define VCO_INTERVAL round_jiffies_relative(10 * HZ) /* 10 sec */ /* * rt2x00_rate: Per rate device information */ struct rt2x00_rate { unsigned short flags; #define DEV_RATE_CCK 0x0001 #define DEV_RATE_OFDM 0x0002 #define DEV_RATE_SHORT_PREAMBLE 0x0004 unsigned short bitrate; /* In 100kbit/s */ unsigned short ratemask; unsigned short plcp; unsigned short mcs; }; extern const struct rt2x00_rate rt2x00_supported_rates[12]; static inline const struct rt2x00_rate *rt2x00_get_rate(const u16 hw_value) { return &rt2x00_supported_rates[hw_value & 0xff]; } #define RATE_MCS(__mode, __mcs) \ ((((__mode) & 0x00ff) << 8) | ((__mcs) & 0x00ff)) static inline int rt2x00_get_rate_mcs(const u16 mcs_value) { return (mcs_value & 0x00ff); } /* * Radio control handlers. */ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev); void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev); /* * Initialization handlers. */ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev); void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev); /* * Configuration handlers. */ void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, enum nl80211_iftype type, const u8 *mac, const u8 *bssid); void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct ieee80211_bss_conf *conf, u32 changed); void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, struct antenna_setup ant); void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, const unsigned int changed_flags); /** * DOC: Queue handlers */ /** * rt2x00queue_alloc_rxskb - allocate a skb for RX purposes. * @entry: The entry for which the skb will be applicable. */ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp); /** * rt2x00queue_free_skb - free a skb * @entry: The entry for which the skb will be applicable. */ void rt2x00queue_free_skb(struct queue_entry *entry); /** * rt2x00queue_align_frame - Align 802.11 frame to 4-byte boundary * @skb: The skb to align * * Align the start of the 802.11 frame to a 4-byte boundary, this could * mean the payload is not aligned properly though. */ void rt2x00queue_align_frame(struct sk_buff *skb); /** * rt2x00queue_insert_l2pad - Align 802.11 header & payload to 4-byte boundary * @skb: The skb to align * @header_length: Length of 802.11 header * * Apply L2 padding to align both header and payload to 4-byte boundary */ void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length); /** * rt2x00queue_insert_l2pad - Remove L2 padding from 802.11 frame * @skb: The skb to align * @header_length: Length of 802.11 header * * Remove L2 padding used to align both header and payload to 4-byte boundary, * by removing the L2 padding the header will no longer be 4-byte aligned. */ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length); /** * rt2x00queue_write_tx_frame - Write TX frame to hardware * @queue: Queue over which the frame should be send * @skb: The skb to send * @local: frame is not from mac80211 */ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, bool local); /** * rt2x00queue_update_beacon - Send new beacon from mac80211 * to hardware. Handles locking by itself (mutex). * @rt2x00dev: Pointer to &struct rt2x00_dev. * @vif: Interface for which the beacon should be updated. */ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif); /** * rt2x00queue_update_beacon_locked - Send new beacon from mac80211 * to hardware. Caller needs to ensure locking. * @rt2x00dev: Pointer to &struct rt2x00_dev. * @vif: Interface for which the beacon should be updated. */ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif); /** * rt2x00queue_clear_beacon - Clear beacon in hardware * @rt2x00dev: Pointer to &struct rt2x00_dev. * @vif: Interface for which the beacon should be updated. */ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif); /** * rt2x00queue_index_inc - Index incrementation function * @entry: Queue entry (&struct queue_entry) to perform the action on. * @index: Index type (&enum queue_index) to perform the action on. * * This function will increase the requested index on the entry's queue, * it will grab the appropriate locks and handle queue overflow events by * resetting the index to the start of the queue. */ void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index); /** * rt2x00queue_init_queues - Initialize all data queues * @rt2x00dev: Pointer to &struct rt2x00_dev. * * This function will loop through all available queues to clear all * index numbers and set the queue entry to the correct initialization * state. */ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev); int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev); void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev); int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev); void rt2x00queue_free(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_update_stats - Update link statistics from RX frame * @rt2x00dev: Pointer to &struct rt2x00_dev. * @skb: Received frame * @rxdesc: Received frame descriptor * * Update link statistics based on the information from the * received frame descriptor. */ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct rxdone_entry_desc *rxdesc); /** * rt2x00link_start_tuner - Start periodic link tuner work * @rt2x00dev: Pointer to &struct rt2x00_dev. * * This start the link tuner periodic work, this work will * be executed periodically until &rt2x00link_stop_tuner has * been called. */ void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_stop_tuner - Stop periodic link tuner work * @rt2x00dev: Pointer to &struct rt2x00_dev. * * After this function completed the link tuner will not * be running until &rt2x00link_start_tuner is called. */ void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_reset_tuner - Reset periodic link tuner work * @rt2x00dev: Pointer to &struct rt2x00_dev. * @antenna: Should the antenna tuning also be reset * * The VGC limit configured in the hardware will be reset to 0 * which forces the driver to rediscover the correct value for * the current association. This is needed when configuration * options have changed which could drastically change the * SNR level or link quality (i.e. changing the antenna setting). * * Resetting the link tuner will also cause the periodic work counter * to be reset. Any driver which has a fixed limit on the number * of rounds the link tuner is supposed to work will accept the * tuner actions again if this limit was previously reached. * * If @antenna is set to true a the software antenna diversity * tuning will also be reset. */ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna); /** * rt2x00link_start_watchdog - Start periodic watchdog monitoring * @rt2x00dev: Pointer to &struct rt2x00_dev. * * This start the watchdog periodic work, this work will *be executed periodically until &rt2x00link_stop_watchdog has * been called. */ void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_stop_watchdog - Stop periodic watchdog monitoring * @rt2x00dev: Pointer to &struct rt2x00_dev. * * After this function completed the watchdog monitoring will not * be running until &rt2x00link_start_watchdog is called. */ void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_start_agc - Start periodic gain calibration * @rt2x00dev: Pointer to &struct rt2x00_dev. */ void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_start_vcocal - Start periodic VCO calibration * @rt2x00dev: Pointer to &struct rt2x00_dev. */ void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_stop_agc - Stop periodic gain calibration * @rt2x00dev: Pointer to &struct rt2x00_dev. */ void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_stop_vcocal - Stop periodic VCO calibration * @rt2x00dev: Pointer to &struct rt2x00_dev. */ void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev); /** * rt2x00link_register - Initialize link tuning & watchdog functionality * @rt2x00dev: Pointer to &struct rt2x00_dev. * * Initialize work structure and all link tuning and watchdog related * parameters. This will not start the periodic work itself. */ void rt2x00link_register(struct rt2x00_dev *rt2x00dev); /* * Firmware handlers. */ #ifdef CONFIG_RT2X00_LIB_FIRMWARE int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev); void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev); #else static inline int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) { return 0; } static inline void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) { } #endif /* CONFIG_RT2X00_LIB_FIRMWARE */ /* * Debugfs handlers. */ #ifdef CONFIG_RT2X00_LIB_DEBUGFS void rt2x00debug_register(struct rt2x00_dev *rt2x00dev); void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev); void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, struct rxdone_entry_desc *rxdesc); #else static inline void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) { } static inline void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) { } static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, struct rxdone_entry_desc *rxdesc) { } #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ /* * Crypto handlers. */ #ifdef CONFIG_RT2X00_LIB_CRYPTO enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key); void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc); unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb); void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc); void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc); void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length); void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int header_length, struct rxdone_entry_desc *rxdesc); #else static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) { return CIPHER_NONE; } static inline void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc) { } static inline unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) { return 0; } static inline void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) { } static inline void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) { } static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) { } static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int header_length, struct rxdone_entry_desc *rxdesc) { } #endif /* CONFIG_RT2X00_LIB_CRYPTO */ /* * RFkill handlers. */ static inline void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) { if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) wiphy_rfkill_start_polling(rt2x00dev->hw->wiphy); } static inline void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) { if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) wiphy_rfkill_stop_polling(rt2x00dev->hw->wiphy); } /* * LED handlers */ #ifdef CONFIG_RT2X00_LIB_LEDS void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi); void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled); void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled); void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled); void rt2x00leds_register(struct rt2x00_dev *rt2x00dev); void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev); void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev); void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev); #else static inline void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi) { } static inline void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled) { } static inline void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled) { } static inline void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled) { } static inline void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) { } static inline void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) { } static inline void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) { } static inline void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) { } #endif /* CONFIG_RT2X00_LIB_LEDS */ #endif /* RT2X00LIB_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00leds.h0000644000175000017500000000231612026211315023602 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 led datastructures and routines */ #ifndef RT2X00LEDS_H #define RT2X00LEDS_H enum led_type { LED_TYPE_RADIO, LED_TYPE_ASSOC, LED_TYPE_ACTIVITY, LED_TYPE_QUALITY, }; struct rt2x00_led { struct rt2x00_dev *rt2x00dev; struct led_classdev led_dev; enum led_type type; unsigned int flags; #define LED_INITIALIZED ( 1 << 0 ) #define LED_REGISTERED ( 1 << 1 ) }; #endif /* RT2X00LEDS_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00leds.c0000644000175000017500000001473312026211315023603 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 led specific routines. */ #include #include #include "rt2x00.h" #include "rt2x00lib.h" void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi) { struct rt2x00_led *led = &rt2x00dev->led_qual; unsigned int brightness; if ((led->type != LED_TYPE_QUALITY) || !(led->flags & LED_REGISTERED)) return; /* * Led handling requires a positive value for the rssi, * to do that correctly we need to add the correction. */ rssi += rt2x00dev->rssi_offset; /* * Get the rssi level, this is used to convert the rssi * to a LED value inside the range LED_OFF - LED_FULL. */ if (rssi <= 30) rssi = 0; else if (rssi <= 39) rssi = 1; else if (rssi <= 49) rssi = 2; else if (rssi <= 53) rssi = 3; else if (rssi <= 63) rssi = 4; else rssi = 5; /* * Note that we must _not_ send LED_OFF since the driver * is going to calculate the value and might use it in a * division. */ brightness = ((LED_FULL / 6) * rssi) + 1; if (brightness != led->led_dev.brightness) { led->led_dev.brightness_set(&led->led_dev, brightness); led->led_dev.brightness = brightness; } } static void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled) { unsigned int brightness = enabled ? LED_FULL : LED_OFF; if (!(led->flags & LED_REGISTERED)) return; led->led_dev.brightness_set(&led->led_dev, brightness); led->led_dev.brightness = brightness; } void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled) { if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY) rt2x00led_led_simple(&rt2x00dev->led_qual, enabled); } void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled) { if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC) rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled); } void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled) { if (rt2x00dev->led_radio.type == LED_TYPE_RADIO) rt2x00led_led_simple(&rt2x00dev->led_radio, enabled); } static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, const char *name) { struct device *device = wiphy_dev(rt2x00dev->hw->wiphy); int retval; led->led_dev.name = name; led->led_dev.brightness = LED_OFF; retval = led_classdev_register(device, &led->led_dev); if (retval) { ERROR(rt2x00dev, "Failed to register led handler.\n"); return retval; } led->flags |= LED_REGISTERED; return 0; } void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) { char name[36]; int retval; unsigned long on_period; unsigned long off_period; const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy); if (rt2x00dev->led_radio.flags & LED_INITIALIZED) { snprintf(name, sizeof(name), "%s-%s::radio", rt2x00dev->ops->name, phy_name); retval = rt2x00leds_register_led(rt2x00dev, &rt2x00dev->led_radio, name); if (retval) goto exit_fail; } if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) { snprintf(name, sizeof(name), "%s-%s::assoc", rt2x00dev->ops->name, phy_name); retval = rt2x00leds_register_led(rt2x00dev, &rt2x00dev->led_assoc, name); if (retval) goto exit_fail; } if (rt2x00dev->led_qual.flags & LED_INITIALIZED) { snprintf(name, sizeof(name), "%s-%s::quality", rt2x00dev->ops->name, phy_name); retval = rt2x00leds_register_led(rt2x00dev, &rt2x00dev->led_qual, name); if (retval) goto exit_fail; } /* * Initialize blink time to default value: * On period: 70ms * Off period: 30ms */ if (rt2x00dev->led_radio.led_dev.blink_set) { on_period = 70; off_period = 30; rt2x00dev->led_radio.led_dev.blink_set( &rt2x00dev->led_radio.led_dev, &on_period, &off_period); } return; exit_fail: rt2x00leds_unregister(rt2x00dev); } static void rt2x00leds_unregister_led(struct rt2x00_led *led) { led_classdev_unregister(&led->led_dev); /* * This might look weird, but when we are unregistering while * suspended the led is already off, and since we haven't * fully resumed yet, access to the device might not be * possible yet. */ if (!(led->led_dev.flags & LED_SUSPENDED)) led->led_dev.brightness_set(&led->led_dev, LED_OFF); led->flags &= ~LED_REGISTERED; } void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) { if (rt2x00dev->led_qual.flags & LED_REGISTERED) rt2x00leds_unregister_led(&rt2x00dev->led_qual); if (rt2x00dev->led_assoc.flags & LED_REGISTERED) rt2x00leds_unregister_led(&rt2x00dev->led_assoc); if (rt2x00dev->led_radio.flags & LED_REGISTERED) rt2x00leds_unregister_led(&rt2x00dev->led_radio); } static inline void rt2x00leds_suspend_led(struct rt2x00_led *led) { led_classdev_suspend(&led->led_dev); /* This shouldn't be needed, but just to be safe */ led->led_dev.brightness_set(&led->led_dev, LED_OFF); led->led_dev.brightness = LED_OFF; } void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) { if (rt2x00dev->led_qual.flags & LED_REGISTERED) rt2x00leds_suspend_led(&rt2x00dev->led_qual); if (rt2x00dev->led_assoc.flags & LED_REGISTERED) rt2x00leds_suspend_led(&rt2x00dev->led_assoc); if (rt2x00dev->led_radio.flags & LED_REGISTERED) rt2x00leds_suspend_led(&rt2x00dev->led_radio); } static inline void rt2x00leds_resume_led(struct rt2x00_led *led) { led_classdev_resume(&led->led_dev); /* Device might have enabled the LEDS during resume */ led->led_dev.brightness_set(&led->led_dev, LED_OFF); led->led_dev.brightness = LED_OFF; } void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) { if (rt2x00dev->led_radio.flags & LED_REGISTERED) rt2x00leds_resume_led(&rt2x00dev->led_radio); if (rt2x00dev->led_assoc.flags & LED_REGISTERED) rt2x00leds_resume_led(&rt2x00dev->led_assoc); if (rt2x00dev->led_qual.flags & LED_REGISTERED) rt2x00leds_resume_led(&rt2x00dev->led_qual); } compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00.h0000644000175000017500000010415312026211315022734 0ustar mcgrofmcgrof/* Copyright (C) 2010 Willow Garage Copyright (C) 2004 - 2010 Ivo van Doorn Copyright (C) 2004 - 2009 Gertjan van Wingerde This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00 Abstract: rt2x00 global information. */ #ifndef RT2X00_H #define RT2X00_H #include #include #include #include #include #include #include #include #include #include #include #include #include "rt2x00debug.h" #include "rt2x00dump.h" #include "rt2x00leds.h" #include "rt2x00reg.h" #include "rt2x00queue.h" /* * Module information. */ #define DRV_VERSION "2.3.0" #define DRV_PROJECT "http://rt2x00.serialmonkey.com" /* * Debug definitions. * Debug output has to be enabled during compile time. */ #define DEBUG_PRINTK_MSG(__dev, __kernlvl, __lvl, __msg, __args...) \ printk(__kernlvl "%s -> %s: %s - " __msg, \ wiphy_name((__dev)->hw->wiphy), __func__, __lvl, ##__args) #define DEBUG_PRINTK_PROBE(__kernlvl, __lvl, __msg, __args...) \ printk(__kernlvl "%s -> %s: %s - " __msg, \ KBUILD_MODNAME, __func__, __lvl, ##__args) #ifdef CONFIG_RT2X00_DEBUG #define DEBUG_PRINTK(__dev, __kernlvl, __lvl, __msg, __args...) \ DEBUG_PRINTK_MSG(__dev, __kernlvl, __lvl, __msg, ##__args) #else #define DEBUG_PRINTK(__dev, __kernlvl, __lvl, __msg, __args...) \ do { } while (0) #endif /* CONFIG_RT2X00_DEBUG */ /* * Various debug levels. * The debug levels PANIC and ERROR both indicate serious problems, * for this reason they should never be ignored. * The special ERROR_PROBE message is for messages that are generated * when the rt2x00_dev is not yet initialized. */ #define PANIC(__dev, __msg, __args...) \ DEBUG_PRINTK_MSG(__dev, KERN_CRIT, "Panic", __msg, ##__args) #define ERROR(__dev, __msg, __args...) \ DEBUG_PRINTK_MSG(__dev, KERN_ERR, "Error", __msg, ##__args) #define ERROR_PROBE(__msg, __args...) \ DEBUG_PRINTK_PROBE(KERN_ERR, "Error", __msg, ##__args) #define WARNING(__dev, __msg, __args...) \ DEBUG_PRINTK(__dev, KERN_WARNING, "Warning", __msg, ##__args) #define NOTICE(__dev, __msg, __args...) \ DEBUG_PRINTK(__dev, KERN_NOTICE, "Notice", __msg, ##__args) #define INFO(__dev, __msg, __args...) \ DEBUG_PRINTK(__dev, KERN_INFO, "Info", __msg, ##__args) #define DEBUG(__dev, __msg, __args...) \ DEBUG_PRINTK(__dev, KERN_DEBUG, "Debug", __msg, ##__args) #define EEPROM(__dev, __msg, __args...) \ DEBUG_PRINTK(__dev, KERN_DEBUG, "EEPROM recovery", __msg, ##__args) /* * Duration calculations * The rate variable passed is: 100kbs. * To convert from bytes to bits we multiply size with 8, * then the size is multiplied with 10 to make the * real rate -> rate argument correction. */ #define GET_DURATION(__size, __rate) (((__size) * 8 * 10) / (__rate)) #define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate)) /* * Determine the number of L2 padding bytes required between the header and * the payload. */ #define L2PAD_SIZE(__hdrlen) (-(__hdrlen) & 3) /* * Determine the alignment requirement, * to make sure the 802.11 payload is padded to a 4-byte boundrary * we must determine the address of the payload and calculate the * amount of bytes needed to move the data. */ #define ALIGN_SIZE(__skb, __header) \ ( ((unsigned long)((__skb)->data + (__header))) & 3 ) /* * Constants for extra TX headroom for alignment purposes. */ #define RT2X00_ALIGN_SIZE 4 /* Only whole frame needs alignment */ #define RT2X00_L2PAD_SIZE 8 /* Both header & payload need alignment */ /* * Standard timing and size defines. * These values should follow the ieee80211 specifications. */ #define ACK_SIZE 14 #define IEEE80211_HEADER 24 #define PLCP 48 #define BEACON 100 #define PREAMBLE 144 #define SHORT_PREAMBLE 72 #define SLOT_TIME 20 #define SHORT_SLOT_TIME 9 #define SIFS 10 #define PIFS ( SIFS + SLOT_TIME ) #define SHORT_PIFS ( SIFS + SHORT_SLOT_TIME ) #define DIFS ( PIFS + SLOT_TIME ) #define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME ) #define EIFS ( SIFS + DIFS + \ GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) #define SHORT_EIFS ( SIFS + SHORT_DIFS + \ GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) /* * Structure for average calculation * The avg field contains the actual average value, * but avg_weight is internally used during calculations * to prevent rounding errors. */ struct avg_val { int avg; int avg_weight; }; enum rt2x00_chip_intf { RT2X00_CHIP_INTF_PCI, RT2X00_CHIP_INTF_PCIE, RT2X00_CHIP_INTF_USB, RT2X00_CHIP_INTF_SOC, }; /* * Chipset identification * The chipset on the device is composed of a RT and RF chip. * The chipset combination is important for determining device capabilities. */ struct rt2x00_chip { u16 rt; #define RT2460 0x2460 #define RT2560 0x2560 #define RT2570 0x2570 #define RT2661 0x2661 #define RT2573 0x2573 #define RT2860 0x2860 /* 2.4GHz */ #define RT2872 0x2872 /* WSOC */ #define RT2883 0x2883 /* WSOC */ #define RT3070 0x3070 #define RT3071 0x3071 #define RT3090 0x3090 /* 2.4GHz PCIe */ #define RT3290 0x3290 #define RT3352 0x3352 /* WSOC */ #define RT3390 0x3390 #define RT3572 0x3572 #define RT3593 0x3593 #define RT3883 0x3883 /* WSOC */ #define RT5390 0x5390 /* 2.4GHz */ #define RT5392 0x5392 /* 2.4GHz */ u16 rf; u16 rev; enum rt2x00_chip_intf intf; }; /* * RF register values that belong to a particular channel. */ struct rf_channel { int channel; u32 rf1; u32 rf2; u32 rf3; u32 rf4; }; /* * Channel information structure */ struct channel_info { unsigned int flags; #define GEOGRAPHY_ALLOWED 0x00000001 short max_power; short default_power1; short default_power2; }; /* * Antenna setup values. */ struct antenna_setup { enum antenna rx; enum antenna tx; u8 rx_chain_num; u8 tx_chain_num; }; /* * Quality statistics about the currently active link. */ struct link_qual { /* * Statistics required for Link tuning by driver * The rssi value is provided by rt2x00lib during the * link_tuner() callback function. * The false_cca field is filled during the link_stats() * callback function and could be used during the * link_tuner() callback function. */ int rssi; int false_cca; /* * VGC levels * Hardware driver will tune the VGC level during each call * to the link_tuner() callback function. This vgc_level is * is determined based on the link quality statistics like * average RSSI and the false CCA count. * * In some cases the drivers need to differentiate between * the currently "desired" VGC level and the level configured * in the hardware. The latter is important to reduce the * number of BBP register reads to reduce register access * overhead. For this reason we store both values here. */ u8 vgc_level; u8 vgc_level_reg; /* * Statistics required for Signal quality calculation. * These fields might be changed during the link_stats() * callback function. */ int rx_success; int rx_failed; int tx_success; int tx_failed; }; /* * Antenna settings about the currently active link. */ struct link_ant { /* * Antenna flags */ unsigned int flags; #define ANTENNA_RX_DIVERSITY 0x00000001 #define ANTENNA_TX_DIVERSITY 0x00000002 #define ANTENNA_MODE_SAMPLE 0x00000004 /* * Currently active TX/RX antenna setup. * When software diversity is used, this will indicate * which antenna is actually used at this time. */ struct antenna_setup active; /* * RSSI history information for the antenna. * Used to determine when to switch antenna * when using software diversity. */ int rssi_history; /* * Current RSSI average of the currently active antenna. * Similar to the avg_rssi in the link_qual structure * this value is updated by using the walking average. */ struct avg_val rssi_ant; }; /* * To optimize the quality of the link we need to store * the quality of received frames and periodically * optimize the link. */ struct link { /* * Link tuner counter * The number of times the link has been tuned * since the radio has been switched on. */ u32 count; /* * Quality measurement values. */ struct link_qual qual; /* * TX/RX antenna setup. */ struct link_ant ant; /* * Currently active average RSSI value */ struct avg_val avg_rssi; /* * Work structure for scheduling periodic link tuning. */ struct delayed_work work; /* * Work structure for scheduling periodic watchdog monitoring. * This work must be scheduled on the kernel workqueue, while * all other work structures must be queued on the mac80211 * workqueue. This guarantees that the watchdog can schedule * other work structures and wait for their completion in order * to bring the device/driver back into the desired state. */ struct delayed_work watchdog_work; /* * Work structure for scheduling periodic AGC adjustments. */ struct delayed_work agc_work; /* * Work structure for scheduling periodic VCO calibration. */ struct delayed_work vco_work; }; enum rt2x00_delayed_flags { DELAYED_UPDATE_BEACON, }; /* * Interface structure * Per interface configuration details, this structure * is allocated as the private data for ieee80211_vif. */ struct rt2x00_intf { /* * beacon->skb must be protected with the mutex. */ struct mutex beacon_skb_mutex; /* * Entry in the beacon queue which belongs to * this interface. Each interface has its own * dedicated beacon entry. */ struct queue_entry *beacon; bool enable_beacon; /* * Actions that needed rescheduling. */ unsigned long delayed_flags; /* * Software sequence counter, this is only required * for hardware which doesn't support hardware * sequence counting. */ atomic_t seqno; }; static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif) { return (struct rt2x00_intf *)vif->drv_priv; } /** * struct hw_mode_spec: Hardware specifications structure * * Details about the supported modes, rates and channels * of a particular chipset. This is used by rt2x00lib * to build the ieee80211_hw_mode array for mac80211. * * @supported_bands: Bitmask contained the supported bands (2.4GHz, 5.2GHz). * @supported_rates: Rate types which are supported (CCK, OFDM). * @num_channels: Number of supported channels. This is used as array size * for @tx_power_a, @tx_power_bg and @channels. * @channels: Device/chipset specific channel values (See &struct rf_channel). * @channels_info: Additional information for channels (See &struct channel_info). * @ht: Driver HT Capabilities (See &ieee80211_sta_ht_cap). */ struct hw_mode_spec { unsigned int supported_bands; #define SUPPORT_BAND_2GHZ 0x00000001 #define SUPPORT_BAND_5GHZ 0x00000002 unsigned int supported_rates; #define SUPPORT_RATE_CCK 0x00000001 #define SUPPORT_RATE_OFDM 0x00000002 unsigned int num_channels; const struct rf_channel *channels; const struct channel_info *channels_info; struct ieee80211_sta_ht_cap ht; }; /* * Configuration structure wrapper around the * mac80211 configuration structure. * When mac80211 configures the driver, rt2x00lib * can precalculate values which are equal for all * rt2x00 drivers. Those values can be stored in here. */ struct rt2x00lib_conf { struct ieee80211_conf *conf; struct rf_channel rf; struct channel_info channel; }; /* * Configuration structure for erp settings. */ struct rt2x00lib_erp { int short_preamble; int cts_protection; u32 basic_rates; int slot_time; short sifs; short pifs; short difs; short eifs; u16 beacon_int; u16 ht_opmode; }; /* * Configuration structure for hardware encryption. */ struct rt2x00lib_crypto { enum cipher cipher; enum set_key_cmd cmd; const u8 *address; u32 bssidx; u8 key[16]; u8 tx_mic[8]; u8 rx_mic[8]; int wcid; }; /* * Configuration structure wrapper around the * rt2x00 interface configuration handler. */ struct rt2x00intf_conf { /* * Interface type */ enum nl80211_iftype type; /* * TSF sync value, this is dependent on the operation type. */ enum tsf_sync sync; /* * The MAC and BSSID addresses are simple array of bytes, * these arrays are little endian, so when sending the addresses * to the drivers, copy the it into a endian-signed variable. * * Note that all devices (except rt2500usb) have 32 bits * register word sizes. This means that whatever variable we * pass _must_ be a multiple of 32 bits. Otherwise the device * might not accept what we are sending to it. * This will also make it easier for the driver to write * the data to the device. */ __le32 mac[2]; __le32 bssid[2]; }; /* * Private structure for storing STA details * wcid: Wireless Client ID */ struct rt2x00_sta { int wcid; }; static inline struct rt2x00_sta* sta_to_rt2x00_sta(struct ieee80211_sta *sta) { return (struct rt2x00_sta *)sta->drv_priv; } /* * rt2x00lib callback functions. */ struct rt2x00lib_ops { /* * Interrupt handlers. */ irq_handler_t irq_handler; /* * TX status tasklet handler. */ void (*txstatus_tasklet) (unsigned long data); void (*pretbtt_tasklet) (unsigned long data); void (*tbtt_tasklet) (unsigned long data); void (*rxdone_tasklet) (unsigned long data); void (*autowake_tasklet) (unsigned long data); /* * Device init handlers. */ int (*probe_hw) (struct rt2x00_dev *rt2x00dev); char *(*get_firmware_name) (struct rt2x00_dev *rt2x00dev); int (*check_firmware) (struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len); int (*load_firmware) (struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len); /* * Device initialization/deinitialization handlers. */ int (*initialize) (struct rt2x00_dev *rt2x00dev); void (*uninitialize) (struct rt2x00_dev *rt2x00dev); /* * queue initialization handlers */ bool (*get_entry_state) (struct queue_entry *entry); void (*clear_entry) (struct queue_entry *entry); /* * Radio control handlers. */ int (*set_device_state) (struct rt2x00_dev *rt2x00dev, enum dev_state state); int (*rfkill_poll) (struct rt2x00_dev *rt2x00dev); void (*link_stats) (struct rt2x00_dev *rt2x00dev, struct link_qual *qual); void (*reset_tuner) (struct rt2x00_dev *rt2x00dev, struct link_qual *qual); void (*link_tuner) (struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count); void (*gain_calibration) (struct rt2x00_dev *rt2x00dev); void (*vco_calibration) (struct rt2x00_dev *rt2x00dev); /* * Data queue handlers. */ void (*watchdog) (struct rt2x00_dev *rt2x00dev); void (*start_queue) (struct data_queue *queue); void (*kick_queue) (struct data_queue *queue); void (*stop_queue) (struct data_queue *queue); void (*flush_queue) (struct data_queue *queue, bool drop); void (*tx_dma_done) (struct queue_entry *entry); /* * TX control handlers */ void (*write_tx_desc) (struct queue_entry *entry, struct txentry_desc *txdesc); void (*write_tx_data) (struct queue_entry *entry, struct txentry_desc *txdesc); void (*write_beacon) (struct queue_entry *entry, struct txentry_desc *txdesc); void (*clear_beacon) (struct queue_entry *entry); int (*get_tx_data_len) (struct queue_entry *entry); /* * RX control handlers */ void (*fill_rxdone) (struct queue_entry *entry, struct rxdone_entry_desc *rxdesc); /* * Configuration handlers. */ int (*config_shared_key) (struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); int (*config_pairwise_key) (struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); void (*config_filter) (struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags); void (*config_intf) (struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags); #define CONFIG_UPDATE_TYPE ( 1 << 1 ) #define CONFIG_UPDATE_MAC ( 1 << 2 ) #define CONFIG_UPDATE_BSSID ( 1 << 3 ) void (*config_erp) (struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed); void (*config_ant) (struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant); void (*config) (struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int changed_flags); int (*sta_add) (struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int (*sta_remove) (struct rt2x00_dev *rt2x00dev, int wcid); }; /* * rt2x00 driver callback operation structure. */ struct rt2x00_ops { const char *name; const unsigned int drv_data_size; const unsigned int max_sta_intf; const unsigned int max_ap_intf; const unsigned int eeprom_size; const unsigned int rf_size; const unsigned int tx_queues; const unsigned int extra_tx_headroom; const struct data_queue_desc *rx; const struct data_queue_desc *tx; const struct data_queue_desc *bcn; const struct data_queue_desc *atim; const struct rt2x00lib_ops *lib; const void *drv; const struct ieee80211_ops *hw; #ifdef CONFIG_RT2X00_LIB_DEBUGFS const struct rt2x00debug *debugfs; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * rt2x00 state flags */ enum rt2x00_state_flags { /* * Device flags */ DEVICE_STATE_PRESENT, DEVICE_STATE_REGISTERED_HW, DEVICE_STATE_INITIALIZED, DEVICE_STATE_STARTED, DEVICE_STATE_ENABLED_RADIO, DEVICE_STATE_SCANNING, /* * Driver configuration */ CONFIG_CHANNEL_HT40, CONFIG_POWERSAVING, CONFIG_HT_DISABLED, CONFIG_QOS_DISABLED, /* * Mark we currently are sequentially reading TX_STA_FIFO register * FIXME: this is for only rt2800usb, should go to private data */ TX_STATUS_READING, }; /* * rt2x00 capability flags */ enum rt2x00_capability_flags { /* * Requirements */ REQUIRE_FIRMWARE, REQUIRE_BEACON_GUARD, REQUIRE_ATIM_QUEUE, REQUIRE_DMA, REQUIRE_COPY_IV, REQUIRE_L2PAD, REQUIRE_TXSTATUS_FIFO, REQUIRE_TASKLET_CONTEXT, REQUIRE_SW_SEQNO, REQUIRE_HT_TX_DESC, REQUIRE_PS_AUTOWAKE, /* * Capabilities */ CAPABILITY_HW_BUTTON, CAPABILITY_HW_CRYPTO, CAPABILITY_POWER_LIMIT, CAPABILITY_CONTROL_FILTERS, CAPABILITY_CONTROL_FILTER_PSPOLL, CAPABILITY_PRE_TBTT_INTERRUPT, CAPABILITY_LINK_TUNING, CAPABILITY_FRAME_TYPE, CAPABILITY_RF_SEQUENCE, CAPABILITY_EXTERNAL_LNA_A, CAPABILITY_EXTERNAL_LNA_BG, CAPABILITY_DOUBLE_ANTENNA, CAPABILITY_BT_COEXIST, CAPABILITY_VCO_RECALIBRATION, }; /* * rt2x00 device structure. */ struct rt2x00_dev { /* * Device structure. * The structure stored in here depends on the * system bus (PCI or USB). * When accessing this variable, the rt2x00dev_{pci,usb} * macros should be used for correct typecasting. */ struct device *dev; /* * Callback functions. */ const struct rt2x00_ops *ops; /* * Driver data. */ void *drv_data; /* * IEEE80211 control structure. */ struct ieee80211_hw *hw; struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; enum ieee80211_band curr_band; int curr_freq; /* * If enabled, the debugfs interface structures * required for deregistration of debugfs. */ #ifdef CONFIG_RT2X00_LIB_DEBUGFS struct rt2x00debug_intf *debugfs_intf; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ /* * LED structure for changing the LED status * by mac8011 or the kernel. */ #ifdef CONFIG_RT2X00_LIB_LEDS struct rt2x00_led led_radio; struct rt2x00_led led_assoc; struct rt2x00_led led_qual; u16 led_mcu_reg; #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Device state flags. * In these flags the current status is stored. * Access to these flags should occur atomically. */ unsigned long flags; /* * Device capabiltiy flags. * In these flags the device/driver capabilities are stored. * Access to these flags should occur non-atomically. */ unsigned long cap_flags; /* * Device information, Bus IRQ and name (PCI, SoC) */ int irq; const char *name; /* * Chipset identification. */ struct rt2x00_chip chip; /* * hw capability specifications. */ struct hw_mode_spec spec; /* * This is the default TX/RX antenna setup as indicated * by the device's EEPROM. */ struct antenna_setup default_ant; /* * Register pointers * csr.base: CSR base register address. (PCI) * csr.cache: CSR cache for usb_control_msg. (USB) */ union csr { void __iomem *base; void *cache; } csr; /* * Mutex to protect register accesses. * For PCI and USB devices it protects against concurrent indirect * register access (BBP, RF, MCU) since accessing those * registers require multiple calls to the CSR registers. * For USB devices it also protects the csr_cache since that * field is used for normal CSR access and it cannot support * multiple callers simultaneously. */ struct mutex csr_mutex; /* * Current packet filter configuration for the device. * This contains all currently active FIF_* flags send * to us by mac80211 during configure_filter(). */ unsigned int packet_filter; /* * Interface details: * - Open ap interface count. * - Open sta interface count. * - Association count. * - Beaconing enabled count. */ unsigned int intf_ap_count; unsigned int intf_sta_count; unsigned int intf_associated; unsigned int intf_beaconing; /* * Link quality */ struct link link; /* * EEPROM data. */ __le16 *eeprom; /* * Active RF register values. * These are stored here so we don't need * to read the rf registers and can directly * use this value instead. * This field should be accessed by using * rt2x00_rf_read() and rt2x00_rf_write(). */ u32 *rf; /* * LNA gain */ short lna_gain; /* * Current TX power value. */ u16 tx_power; /* * Current retry values. */ u8 short_retry; u8 long_retry; /* * Rssi <-> Dbm offset */ u8 rssi_offset; /* * Frequency offset. */ u8 freq_offset; /* * Association id. */ u16 aid; /* * Beacon interval. */ u16 beacon_int; /** * Timestamp of last received beacon */ unsigned long last_beacon; /* * Low level statistics which will have * to be kept up to date while device is running. */ struct ieee80211_low_level_stats low_level_stats; /** * Work queue for all work which should not be placed * on the mac80211 workqueue (because of dependencies * between various work structures). */ struct workqueue_struct *workqueue; /* * Scheduled work. * NOTE: intf_work will use ieee80211_iterate_active_interfaces() * which means it cannot be placed on the hw->workqueue * due to RTNL locking requirements. */ struct work_struct intf_work; /** * Scheduled work for TX/RX done handling (USB devices) */ struct work_struct rxdone_work; struct work_struct txdone_work; /* * Powersaving work */ struct delayed_work autowakeup_work; struct work_struct sleep_work; /* * Data queue arrays for RX, TX, Beacon and ATIM. */ unsigned int data_queues; struct data_queue *rx; struct data_queue *tx; struct data_queue *bcn; struct data_queue *atim; /* * Firmware image. */ const struct firmware *fw; /* * FIFO for storing tx status reports between isr and tasklet. */ DECLARE_KFIFO_PTR(txstatus_fifo, u32); /* * Timer to ensure tx status reports are read (rt2800usb). */ struct hrtimer txstatus_timer; /* * Tasklet for processing tx status reports (rt2800pci). */ struct tasklet_struct txstatus_tasklet; struct tasklet_struct pretbtt_tasklet; struct tasklet_struct tbtt_tasklet; struct tasklet_struct rxdone_tasklet; struct tasklet_struct autowake_tasklet; /* * Used for VCO periodic calibration. */ int rf_channel; /* * Protect the interrupt mask register. */ spinlock_t irqmask_lock; }; /* * Register defines. * Some registers require multiple attempts before success, * in those cases REGISTER_BUSY_COUNT attempts should be * taken with a REGISTER_BUSY_DELAY interval. */ #define REGISTER_BUSY_COUNT 100 #define REGISTER_BUSY_DELAY 100 /* * Generic RF access. * The RF is being accessed by word index. */ static inline void rt2x00_rf_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u32 *data) { BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); *data = rt2x00dev->rf[word - 1]; } static inline void rt2x00_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, u32 data) { BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); rt2x00dev->rf[word - 1] = data; } /* * Generic EEPROM access. * The EEPROM is being accessed by word index. */ static inline void *rt2x00_eeprom_addr(struct rt2x00_dev *rt2x00dev, const unsigned int word) { return (void *)&rt2x00dev->eeprom[word]; } static inline void rt2x00_eeprom_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u16 *data) { *data = le16_to_cpu(rt2x00dev->eeprom[word]); } static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, u16 data) { rt2x00dev->eeprom[word] = cpu_to_le16(data); } /* * Chipset handlers */ static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev, const u16 rt, const u16 rf, const u16 rev) { rt2x00dev->chip.rt = rt; rt2x00dev->chip.rf = rf; rt2x00dev->chip.rev = rev; INFO(rt2x00dev, "Chipset detected - rt: %04x, rf: %04x, rev: %04x.\n", rt2x00dev->chip.rt, rt2x00dev->chip.rf, rt2x00dev->chip.rev); } static inline bool rt2x00_rt(struct rt2x00_dev *rt2x00dev, const u16 rt) { return (rt2x00dev->chip.rt == rt); } static inline bool rt2x00_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) { return (rt2x00dev->chip.rf == rf); } static inline u16 rt2x00_rev(struct rt2x00_dev *rt2x00dev) { return rt2x00dev->chip.rev; } static inline bool rt2x00_rt_rev(struct rt2x00_dev *rt2x00dev, const u16 rt, const u16 rev) { return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) == rev); } static inline bool rt2x00_rt_rev_lt(struct rt2x00_dev *rt2x00dev, const u16 rt, const u16 rev) { return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) < rev); } static inline bool rt2x00_rt_rev_gte(struct rt2x00_dev *rt2x00dev, const u16 rt, const u16 rev) { return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) >= rev); } static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev, enum rt2x00_chip_intf intf) { rt2x00dev->chip.intf = intf; } static inline bool rt2x00_intf(struct rt2x00_dev *rt2x00dev, enum rt2x00_chip_intf intf) { return (rt2x00dev->chip.intf == intf); } static inline bool rt2x00_is_pci(struct rt2x00_dev *rt2x00dev) { return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI) || rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); } static inline bool rt2x00_is_pcie(struct rt2x00_dev *rt2x00dev) { return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); } static inline bool rt2x00_is_usb(struct rt2x00_dev *rt2x00dev) { return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); } static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) { return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); } /** * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes. * @entry: Pointer to &struct queue_entry */ void rt2x00queue_map_txskb(struct queue_entry *entry); /** * rt2x00queue_unmap_skb - Unmap a skb from DMA. * @entry: Pointer to &struct queue_entry */ void rt2x00queue_unmap_skb(struct queue_entry *entry); /** * rt2x00queue_get_tx_queue - Convert tx queue index to queue pointer * @rt2x00dev: Pointer to &struct rt2x00_dev. * @queue: rt2x00 queue index (see &enum data_queue_qid). * * Returns NULL for non tx queues. */ static inline struct data_queue * rt2x00queue_get_tx_queue(struct rt2x00_dev *rt2x00dev, const enum data_queue_qid queue) { if (queue < rt2x00dev->ops->tx_queues && rt2x00dev->tx) return &rt2x00dev->tx[queue]; if (queue == QID_ATIM) return rt2x00dev->atim; return NULL; } /** * rt2x00queue_get_entry - Get queue entry where the given index points to. * @queue: Pointer to &struct data_queue from where we obtain the entry. * @index: Index identifier for obtaining the correct index. */ struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, enum queue_index index); /** * rt2x00queue_pause_queue - Pause a data queue * @queue: Pointer to &struct data_queue. * * This function will pause the data queue locally, preventing * new frames to be added to the queue (while the hardware is * still allowed to run). */ void rt2x00queue_pause_queue(struct data_queue *queue); /** * rt2x00queue_unpause_queue - unpause a data queue * @queue: Pointer to &struct data_queue. * * This function will unpause the data queue locally, allowing * new frames to be added to the queue again. */ void rt2x00queue_unpause_queue(struct data_queue *queue); /** * rt2x00queue_start_queue - Start a data queue * @queue: Pointer to &struct data_queue. * * This function will start handling all pending frames in the queue. */ void rt2x00queue_start_queue(struct data_queue *queue); /** * rt2x00queue_stop_queue - Halt a data queue * @queue: Pointer to &struct data_queue. * * This function will stop all pending frames in the queue. */ void rt2x00queue_stop_queue(struct data_queue *queue); /** * rt2x00queue_flush_queue - Flush a data queue * @queue: Pointer to &struct data_queue. * @drop: True to drop all pending frames. * * This function will flush the queue. After this call * the queue is guaranteed to be empty. */ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop); /** * rt2x00queue_start_queues - Start all data queues * @rt2x00dev: Pointer to &struct rt2x00_dev. * * This function will loop through all available queues to start them */ void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev); /** * rt2x00queue_stop_queues - Halt all data queues * @rt2x00dev: Pointer to &struct rt2x00_dev. * * This function will loop through all available queues to stop * any pending frames. */ void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev); /** * rt2x00queue_flush_queues - Flush all data queues * @rt2x00dev: Pointer to &struct rt2x00_dev. * @drop: True to drop all pending frames. * * This function will loop through all available queues to flush * any pending frames. */ void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop); /* * Debugfs handlers. */ /** * rt2x00debug_dump_frame - Dump a frame to userspace through debugfs. * @rt2x00dev: Pointer to &struct rt2x00_dev. * @type: The type of frame that is being dumped. * @skb: The skb containing the frame to be dumped. */ #ifdef CONFIG_RT2X00_LIB_DEBUGFS void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, enum rt2x00_dump_type type, struct sk_buff *skb); #else static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, enum rt2x00_dump_type type, struct sk_buff *skb) { } #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ /* * Utility functions. */ u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif); /* * Interrupt context handlers. */ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev); void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev); void rt2x00lib_dmastart(struct queue_entry *entry); void rt2x00lib_dmadone(struct queue_entry *entry); void rt2x00lib_txdone(struct queue_entry *entry, struct txdone_entry_desc *txdesc); void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status); void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp); /* * mac80211 handlers. */ void rt2x00mac_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); int rt2x00mac_start(struct ieee80211_hw *hw); void rt2x00mac_stop(struct ieee80211_hw *hw); int rt2x00mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void rt2x00mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast); int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); #ifdef CONFIG_RT2X00_LIB_CRYPTO int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); #else #define rt2x00mac_set_key NULL #endif /* CONFIG_RT2X00_LIB_CRYPTO */ int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw); void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw); int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); int rt2x00mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop); int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw); /* * Driver allocation handlers. */ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev); void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev); #ifdef CONFIG_PM int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state); int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev); #endif /* CONFIG_PM */ #endif /* RT2X00_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00firmware.c0000644000175000017500000000645312026211315024470 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn Copyright (C) 2004 - 2009 Gertjan van Wingerde This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 firmware loading routines. */ #include #include #include "rt2x00.h" #include "rt2x00lib.h" static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev) { struct device *device = wiphy_dev(rt2x00dev->hw->wiphy); const struct firmware *fw; char *fw_name; int retval; /* * Read correct firmware from harddisk. */ fw_name = rt2x00dev->ops->lib->get_firmware_name(rt2x00dev); if (!fw_name) { ERROR(rt2x00dev, "Invalid firmware filename.\n" "Please file bug report to %s.\n", DRV_PROJECT); return -EINVAL; } INFO(rt2x00dev, "Loading firmware file '%s'.\n", fw_name); retval = request_firmware(&fw, fw_name, device); if (retval) { ERROR(rt2x00dev, "Failed to request Firmware.\n"); return retval; } if (!fw || !fw->size || !fw->data) { ERROR(rt2x00dev, "Failed to read Firmware.\n"); release_firmware(fw); return -ENOENT; } INFO(rt2x00dev, "Firmware detected - version: %d.%d.\n", fw->data[fw->size - 4], fw->data[fw->size - 3]); snprintf(rt2x00dev->hw->wiphy->fw_version, sizeof(rt2x00dev->hw->wiphy->fw_version), "%d.%d", fw->data[fw->size - 4], fw->data[fw->size - 3]); retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size); switch (retval) { case FW_OK: break; case FW_BAD_CRC: ERROR(rt2x00dev, "Firmware checksum error.\n"); goto exit; case FW_BAD_LENGTH: ERROR(rt2x00dev, "Invalid firmware file length (len=%zu)\n", fw->size); goto exit; case FW_BAD_VERSION: ERROR(rt2x00dev, "Current firmware does not support detected chipset.\n"); goto exit; } rt2x00dev->fw = fw; return 0; exit: release_firmware(fw); return -ENOENT; } int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) { int retval; if (!test_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags)) return 0; if (!rt2x00dev->fw) { retval = rt2x00lib_request_firmware(rt2x00dev); if (retval) return retval; } /* * Send firmware to the device. */ retval = rt2x00dev->ops->lib->load_firmware(rt2x00dev, rt2x00dev->fw->data, rt2x00dev->fw->size); /* * When the firmware is uploaded to the hardware the LED * association status might have been triggered, for correct * LED handling it should now be reset. */ rt2x00leds_led_assoc(rt2x00dev, false); return retval; } void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) { release_firmware(rt2x00dev->fw); rt2x00dev->fw = NULL; } compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00dump.h0000644000175000017500000001045412026211315023622 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00dump Abstract: Data structures for the rt2x00debug & userspace. The declarations in this file can be used by both rt2x00 and userspace and therefore should be kept together in this file. */ #ifndef RT2X00DUMP_H #define RT2X00DUMP_H /** * DOC: Introduction * * This header is intended to be exported to userspace, * to make the structures and enumerations available to userspace * applications. This means that all data types should be exportable. * * When rt2x00 is compiled with debugfs support enabled, * it is possible to capture all data coming in and out of the device * by reading the frame dump file. This file can have only a single reader. * The following frames will be reported: * - All incoming frames (rx) * - All outgoing frames (tx, including beacon and atim) * - All completed frames (txdone including atim) * * The data is send to the file using the following format: * * [rt2x00dump header][hardware descriptor][ieee802.11 frame] * * rt2x00dump header: The description of the dumped frame, as well as * additional information useful for debugging. See &rt2x00dump_hdr. * hardware descriptor: Descriptor that was used to receive or transmit * the frame. * ieee802.11 frame: The actual frame that was received or transmitted. */ /** * enum rt2x00_dump_type - Frame type * * These values are used for the @type member of &rt2x00dump_hdr. * @DUMP_FRAME_RXDONE: This frame has been received by the hardware. * @DUMP_FRAME_TX: This frame is queued for transmission to the hardware. * @DUMP_FRAME_TXDONE: This frame indicates the device has handled * the tx event which has either succeeded or failed. A frame * with this type should also have been reported with as a * %DUMP_FRAME_TX frame. * @DUMP_FRAME_BEACON: This beacon frame is queued for transmission to the * hardware. */ enum rt2x00_dump_type { DUMP_FRAME_RXDONE = 1, DUMP_FRAME_TX = 2, DUMP_FRAME_TXDONE = 3, DUMP_FRAME_BEACON = 4, }; /** * struct rt2x00dump_hdr - Dump frame header * * Each frame dumped to the debugfs file starts with this header * attached. This header contains the description of the actual * frame which was dumped. * * New fields inside the structure must be appended to the end of * the structure. This way userspace tools compiled for earlier * header versions can still correctly handle the frame dump * (although they will not handle all data passed to them in the dump). * * @version: Header version should always be set to %DUMP_HEADER_VERSION. * This field must be checked by userspace to determine if it can * handle this frame. * @header_length: The length of the &rt2x00dump_hdr structure. This is * used for compatibility reasons so userspace can easily determine * the location of the next field in the dump. * @desc_length: The length of the device descriptor. * @data_length: The length of the frame data (including the ieee802.11 header. * @chip_rt: RT chipset * @chip_rf: RF chipset * @chip_rev: Chipset revision * @type: The frame type (&rt2x00_dump_type) * @queue_index: The index number of the data queue. * @entry_index: The index number of the entry inside the data queue. * @timestamp_sec: Timestamp - seconds * @timestamp_usec: Timestamp - microseconds */ struct rt2x00dump_hdr { __le32 version; #define DUMP_HEADER_VERSION 2 __le32 header_length; __le32 desc_length; __le32 data_length; __le16 chip_rt; __le16 chip_rf; __le16 chip_rev; __le16 type; __u8 queue_index; __u8 entry_index; __le32 timestamp_sec; __le32 timestamp_usec; }; #endif /* RT2X00DUMP_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00dev.c0000644000175000017500000010336612026211315023433 0ustar mcgrofmcgrof/* Copyright (C) 2010 Willow Garage Copyright (C) 2004 - 2010 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 generic device routines. */ #include #include #include #include #include "rt2x00.h" #include "rt2x00lib.h" /* * Utility functions. */ u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif) { /* * When in STA mode, bssidx is always 0 otherwise local_address[5] * contains the bss number, see BSS_ID_MASK comments for details. */ if (rt2x00dev->intf_sta_count) return 0; return vif->addr[5] & (rt2x00dev->ops->max_ap_intf - 1); } EXPORT_SYMBOL_GPL(rt2x00lib_get_bssidx); /* * Radio control handlers. */ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) { int status; /* * Don't enable the radio twice. * And check if the hardware button has been disabled. */ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; /* * Initialize all data queues. */ rt2x00queue_init_queues(rt2x00dev); /* * Enable radio. */ status = rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_ON); if (status) return status; rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_ON); rt2x00leds_led_radio(rt2x00dev, true); rt2x00led_led_activity(rt2x00dev, true); set_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags); /* * Enable queues. */ rt2x00queue_start_queues(rt2x00dev); rt2x00link_start_tuner(rt2x00dev); rt2x00link_start_agc(rt2x00dev); if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) rt2x00link_start_vcocal(rt2x00dev); /* * Start watchdog monitoring. */ rt2x00link_start_watchdog(rt2x00dev); return 0; } void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) { if (!test_and_clear_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* * Stop watchdog monitoring. */ rt2x00link_stop_watchdog(rt2x00dev); /* * Stop all queues */ rt2x00link_stop_agc(rt2x00dev); if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) rt2x00link_stop_vcocal(rt2x00dev); rt2x00link_stop_tuner(rt2x00dev); rt2x00queue_stop_queues(rt2x00dev); rt2x00queue_flush_queues(rt2x00dev, true); /* * Disable radio. */ rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_OFF); rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF); rt2x00led_led_activity(rt2x00dev, false); rt2x00leds_led_radio(rt2x00dev, false); } static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = data; struct rt2x00_intf *intf = vif_to_intf(vif); /* * It is possible the radio was disabled while the work had been * scheduled. If that happens we should return here immediately, * note that in the spinlock protected area above the delayed_flags * have been cleared correctly. */ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) rt2x00queue_update_beacon(rt2x00dev, vif); } static void rt2x00lib_intf_scheduled(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, intf_work); /* * Iterate over each interface and perform the * requested configurations. */ ieee80211_iterate_active_interfaces(rt2x00dev->hw, rt2x00lib_intf_scheduled_iter, rt2x00dev); } static void rt2x00lib_autowakeup(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, autowakeup_work.work); if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return; if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) ERROR(rt2x00dev, "Device failed to wakeup.\n"); clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); } /* * Interrupt context handlers. */ static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = data; struct sk_buff *skb; /* * Only AP mode interfaces do broad- and multicast buffering */ if (vif->type != NL80211_IFTYPE_AP) return; /* * Send out buffered broad- and multicast frames */ skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); while (skb) { rt2x00mac_tx(rt2x00dev->hw, NULL, skb); skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); } } static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = data; if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_ADHOC && vif->type != NL80211_IFTYPE_MESH_POINT && vif->type != NL80211_IFTYPE_WDS) return; /* * Update the beacon without locking. This is safe on PCI devices * as they only update the beacon periodically here. This should * never be called for USB devices. */ WARN_ON(rt2x00_is_usb(rt2x00dev)); rt2x00queue_update_beacon_locked(rt2x00dev, vif); } void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) { if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* send buffered bc/mc frames out for every bssid */ ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, rt2x00lib_bc_buffer_iter, rt2x00dev); /* * Devices with pre tbtt interrupt don't need to update the beacon * here as they will fetch the next beacon directly prior to * transmission. */ if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags)) return; /* fetch next beacon */ ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, rt2x00lib_beaconupdate_iter, rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) { if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* fetch next beacon */ ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, rt2x00lib_beaconupdate_iter, rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); void rt2x00lib_dmastart(struct queue_entry *entry) { set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); rt2x00queue_index_inc(entry, Q_INDEX); } EXPORT_SYMBOL_GPL(rt2x00lib_dmastart); void rt2x00lib_dmadone(struct queue_entry *entry) { set_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags); clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); rt2x00queue_index_inc(entry, Q_INDEX_DMA_DONE); } EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); void rt2x00lib_txdone(struct queue_entry *entry, struct txdone_entry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); unsigned int header_length, i; u8 rate_idx, rate_flags, retry_rates; u8 skbdesc_flags = skbdesc->flags; bool success; /* * Unmap the skb. */ rt2x00queue_unmap_skb(entry); /* * Remove the extra tx headroom from the skb. */ skb_pull(entry->skb, rt2x00dev->ops->extra_tx_headroom); /* * Signal that the TX descriptor is no longer in the skb. */ skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; /* * Determine the length of 802.11 header. */ header_length = ieee80211_get_hdrlen_from_skb(entry->skb); /* * Remove L2 padding which was added during */ if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) rt2x00queue_remove_l2pad(entry->skb, header_length); /* * If the IV/EIV data was stripped from the frame before it was * passed to the hardware, we should now reinsert it again because * mac80211 will expect the same data to be present it the * frame as it was passed to us. */ if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) rt2x00crypto_tx_insert_iv(entry->skb, header_length); /* * Send frame to debugfs immediately, after this call is completed * we are going to overwrite the skb->cb array. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb); /* * Determine if the frame has been successfully transmitted. */ success = test_bit(TXDONE_SUCCESS, &txdesc->flags) || test_bit(TXDONE_UNKNOWN, &txdesc->flags); /* * Update TX statistics. */ rt2x00dev->link.qual.tx_success += success; rt2x00dev->link.qual.tx_failed += !success; rate_idx = skbdesc->tx_rate_idx; rate_flags = skbdesc->tx_rate_flags; retry_rates = test_bit(TXDONE_FALLBACK, &txdesc->flags) ? (txdesc->retry + 1) : 1; /* * Initialize TX status */ memset(&tx_info->status, 0, sizeof(tx_info->status)); tx_info->status.ack_signal = 0; /* * Frame was send with retries, hardware tried * different rates to send out the frame, at each * retry it lowered the rate 1 step except when the * lowest rate was used. */ for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) { tx_info->status.rates[i].idx = rate_idx - i; tx_info->status.rates[i].flags = rate_flags; if (rate_idx - i == 0) { /* * The lowest rate (index 0) was used until the * number of max retries was reached. */ tx_info->status.rates[i].count = retry_rates - i; i++; break; } tx_info->status.rates[i].count = 1; } if (i < (IEEE80211_TX_MAX_RATES - 1)) tx_info->status.rates[i].idx = -1; /* terminate */ if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { if (success) tx_info->flags |= IEEE80211_TX_STAT_ACK; else rt2x00dev->low_level_stats.dot11ACKFailureCount++; } /* * Every single frame has it's own tx status, hence report * every frame as ampdu of size 1. * * TODO: if we can find out how many frames were aggregated * by the hw we could provide the real ampdu_len to mac80211 * which would allow the rc algorithm to better decide on * which rates are suitable. */ if (test_bit(TXDONE_AMPDU, &txdesc->flags) || tx_info->flags & IEEE80211_TX_CTL_AMPDU) { tx_info->flags |= IEEE80211_TX_STAT_AMPDU; tx_info->status.ampdu_len = 1; tx_info->status.ampdu_ack_len = success ? 1 : 0; /* * TODO: Need to tear down BA session here * if not successful. */ } if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { if (success) rt2x00dev->low_level_stats.dot11RTSSuccessCount++; else rt2x00dev->low_level_stats.dot11RTSFailureCount++; } /* * Only send the status report to mac80211 when it's a frame * that originated in mac80211. If this was a extra frame coming * through a mac80211 library call (RTS/CTS) then we should not * send the status report back. */ if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { if (test_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags)) ieee80211_tx_status(rt2x00dev->hw, entry->skb); else ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); } else dev_kfree_skb_any(entry->skb); /* * Make this entry available for reuse. */ entry->skb = NULL; entry->flags = 0; rt2x00dev->ops->lib->clear_entry(entry); rt2x00queue_index_inc(entry, Q_INDEX_DONE); /* * If the data queue was below the threshold before the txdone * handler we must make sure the packet queue in the mac80211 stack * is reenabled when the txdone handler has finished. This has to be * serialized with rt2x00mac_tx(), otherwise we can wake up queue * before it was stopped. */ spin_lock_bh(&entry->queue->tx_lock); if (!rt2x00queue_threshold(entry->queue)) rt2x00queue_unpause_queue(entry->queue); spin_unlock_bh(&entry->queue->tx_lock); } EXPORT_SYMBOL_GPL(rt2x00lib_txdone); void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status) { struct txdone_entry_desc txdesc; txdesc.flags = 0; __set_bit(status, &txdesc.flags); txdesc.retry = 0; rt2x00lib_txdone(entry, &txdesc); } EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie) { struct ieee80211_mgmt *mgmt = (void *)data; u8 *pos, *end; pos = (u8 *)mgmt->u.beacon.variable; end = data + len; while (pos < end) { if (pos + 2 + pos[1] > end) return NULL; if (pos[0] == ie) return pos; pos += 2 + pos[1]; } return NULL; } static void rt2x00lib_sleep(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, sleep_work); if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return; /* * Check again is powersaving is enabled, to prevent races from delayed * work execution. */ if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, IEEE80211_CONF_CHANGE_PS); } static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct rxdone_entry_desc *rxdesc) { struct ieee80211_hdr *hdr = (void *) skb->data; struct ieee80211_tim_ie *tim_ie; u8 *tim; u8 tim_len; bool cam; /* If this is not a beacon, or if mac80211 has no powersaving * configured, or if the device is already in powersaving mode * we can exit now. */ if (likely(!ieee80211_is_beacon(hdr->frame_control) || !(rt2x00dev->hw->conf.flags & IEEE80211_CONF_PS))) return; /* min. beacon length + FCS_LEN */ if (skb->len <= 40 + FCS_LEN) return; /* and only beacons from the associated BSSID, please */ if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || !rt2x00dev->aid) return; rt2x00dev->last_beacon = jiffies; tim = rt2x00lib_find_ie(skb->data, skb->len - FCS_LEN, WLAN_EID_TIM); if (!tim) return; if (tim[1] < sizeof(*tim_ie)) return; tim_len = tim[1]; tim_ie = (struct ieee80211_tim_ie *) &tim[2]; /* Check whenever the PHY can be turned off again. */ /* 1. What about buffered unicast traffic for our AID? */ cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid); /* 2. Maybe the AP wants to send multicast/broadcast data? */ cam |= (tim_ie->bitmap_ctrl & 0x01); if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work); } static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, struct rxdone_entry_desc *rxdesc) { struct ieee80211_supported_band *sband; const struct rt2x00_rate *rate; unsigned int i; int signal = rxdesc->signal; int type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); switch (rxdesc->rate_mode) { case RATE_MODE_CCK: case RATE_MODE_OFDM: /* * For non-HT rates the MCS value needs to contain the * actually used rate modulation (CCK or OFDM). */ if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) signal = RATE_MCS(rxdesc->rate_mode, signal); sband = &rt2x00dev->bands[rt2x00dev->curr_band]; for (i = 0; i < sband->n_bitrates; i++) { rate = rt2x00_get_rate(sband->bitrates[i].hw_value); if (((type == RXDONE_SIGNAL_PLCP) && (rate->plcp == signal)) || ((type == RXDONE_SIGNAL_BITRATE) && (rate->bitrate == signal)) || ((type == RXDONE_SIGNAL_MCS) && (rate->mcs == signal))) { return i; } } break; case RATE_MODE_HT_MIX: case RATE_MODE_HT_GREENFIELD: if (signal >= 0 && signal <= 76) return signal; break; default: break; } WARNING(rt2x00dev, "Frame received with unrecognized signal, " "mode=0x%.4x, signal=0x%.4x, type=%d.\n", rxdesc->rate_mode, signal, type); return 0; } void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct rxdone_entry_desc rxdesc; struct sk_buff *skb; struct ieee80211_rx_status *rx_status; unsigned int header_length; int rate_idx; if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) goto submit_entry; if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) goto submit_entry; /* * Allocate a new sk_buffer. If no new buffer available, drop the * received frame and reuse the existing buffer. */ skb = rt2x00queue_alloc_rxskb(entry, gfp); if (!skb) goto submit_entry; /* * Unmap the skb. */ rt2x00queue_unmap_skb(entry); /* * Extract the RXD details. */ memset(&rxdesc, 0, sizeof(rxdesc)); rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); /* * Check for valid size in case we get corrupted descriptor from * hardware. */ if (unlikely(rxdesc.size == 0 || rxdesc.size > entry->queue->data_size)) { ERROR(rt2x00dev, "Wrong frame size %d max %d.\n", rxdesc.size, entry->queue->data_size); dev_kfree_skb(entry->skb); goto renew_skb; } /* * The data behind the ieee80211 header must be * aligned on a 4 byte boundary. */ header_length = ieee80211_get_hdrlen_from_skb(entry->skb); /* * Hardware might have stripped the IV/EIV/ICV data, * in that case it is possible that the data was * provided separately (through hardware descriptor) * in which case we should reinsert the data into the frame. */ if ((rxdesc.dev_flags & RXDONE_CRYPTO_IV) && (rxdesc.flags & RX_FLAG_IV_STRIPPED)) rt2x00crypto_rx_insert_iv(entry->skb, header_length, &rxdesc); else if (header_length && (rxdesc.size > header_length) && (rxdesc.dev_flags & RXDONE_L2PAD)) rt2x00queue_remove_l2pad(entry->skb, header_length); /* Trim buffer to correct size */ skb_trim(entry->skb, rxdesc.size); /* * Translate the signal to the correct bitrate index. */ rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); if (rxdesc.rate_mode == RATE_MODE_HT_MIX || rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD) rxdesc.flags |= RX_FLAG_HT; /* * Check if this is a beacon, and more frames have been * buffered while we were in powersaving mode. */ rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); /* * Update extra components */ rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); rt2x00debug_update_crypto(rt2x00dev, &rxdesc); rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); /* * Initialize RX status information, and send frame * to mac80211. */ rx_status = IEEE80211_SKB_RXCB(entry->skb); rx_status->mactime = rxdesc.timestamp; rx_status->band = rt2x00dev->curr_band; rx_status->freq = rt2x00dev->curr_freq; rx_status->rate_idx = rate_idx; rx_status->signal = rxdesc.rssi; rx_status->flag = rxdesc.flags; rx_status->antenna = rt2x00dev->link.ant.active.rx; ieee80211_rx_ni(rt2x00dev->hw, entry->skb); renew_skb: /* * Replace the skb with the freshly allocated one. */ entry->skb = skb; submit_entry: entry->flags = 0; rt2x00queue_index_inc(entry, Q_INDEX_DONE); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2x00dev->ops->lib->clear_entry(entry); } EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); /* * Driver initialization handlers. */ const struct rt2x00_rate rt2x00_supported_rates[12] = { { .flags = DEV_RATE_CCK, .bitrate = 10, .ratemask = BIT(0), .plcp = 0x00, .mcs = RATE_MCS(RATE_MODE_CCK, 0), }, { .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, .bitrate = 20, .ratemask = BIT(1), .plcp = 0x01, .mcs = RATE_MCS(RATE_MODE_CCK, 1), }, { .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, .bitrate = 55, .ratemask = BIT(2), .plcp = 0x02, .mcs = RATE_MCS(RATE_MODE_CCK, 2), }, { .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, .bitrate = 110, .ratemask = BIT(3), .plcp = 0x03, .mcs = RATE_MCS(RATE_MODE_CCK, 3), }, { .flags = DEV_RATE_OFDM, .bitrate = 60, .ratemask = BIT(4), .plcp = 0x0b, .mcs = RATE_MCS(RATE_MODE_OFDM, 0), }, { .flags = DEV_RATE_OFDM, .bitrate = 90, .ratemask = BIT(5), .plcp = 0x0f, .mcs = RATE_MCS(RATE_MODE_OFDM, 1), }, { .flags = DEV_RATE_OFDM, .bitrate = 120, .ratemask = BIT(6), .plcp = 0x0a, .mcs = RATE_MCS(RATE_MODE_OFDM, 2), }, { .flags = DEV_RATE_OFDM, .bitrate = 180, .ratemask = BIT(7), .plcp = 0x0e, .mcs = RATE_MCS(RATE_MODE_OFDM, 3), }, { .flags = DEV_RATE_OFDM, .bitrate = 240, .ratemask = BIT(8), .plcp = 0x09, .mcs = RATE_MCS(RATE_MODE_OFDM, 4), }, { .flags = DEV_RATE_OFDM, .bitrate = 360, .ratemask = BIT(9), .plcp = 0x0d, .mcs = RATE_MCS(RATE_MODE_OFDM, 5), }, { .flags = DEV_RATE_OFDM, .bitrate = 480, .ratemask = BIT(10), .plcp = 0x08, .mcs = RATE_MCS(RATE_MODE_OFDM, 6), }, { .flags = DEV_RATE_OFDM, .bitrate = 540, .ratemask = BIT(11), .plcp = 0x0c, .mcs = RATE_MCS(RATE_MODE_OFDM, 7), }, }; static void rt2x00lib_channel(struct ieee80211_channel *entry, const int channel, const int tx_power, const int value) { /* XXX: this assumption about the band is wrong for 802.11j */ entry->band = channel <= 14 ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; entry->center_freq = ieee80211_channel_to_frequency(channel, entry->band); entry->hw_value = value; entry->max_power = tx_power; entry->max_antenna_gain = 0xff; } static void rt2x00lib_rate(struct ieee80211_rate *entry, const u16 index, const struct rt2x00_rate *rate) { entry->flags = 0; entry->bitrate = rate->bitrate; entry->hw_value = index; entry->hw_value_short = index; if (rate->flags & DEV_RATE_SHORT_PREAMBLE) entry->flags |= IEEE80211_RATE_SHORT_PREAMBLE; } static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, struct hw_mode_spec *spec) { struct ieee80211_hw *hw = rt2x00dev->hw; struct ieee80211_channel *channels; struct ieee80211_rate *rates; unsigned int num_rates; unsigned int i; num_rates = 0; if (spec->supported_rates & SUPPORT_RATE_CCK) num_rates += 4; if (spec->supported_rates & SUPPORT_RATE_OFDM) num_rates += 8; channels = kcalloc(spec->num_channels, sizeof(*channels), GFP_KERNEL); if (!channels) return -ENOMEM; rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); if (!rates) goto exit_free_channels; /* * Initialize Rate list. */ for (i = 0; i < num_rates; i++) rt2x00lib_rate(&rates[i], i, rt2x00_get_rate(i)); /* * Initialize Channel list. */ for (i = 0; i < spec->num_channels; i++) { rt2x00lib_channel(&channels[i], spec->channels[i].channel, spec->channels_info[i].max_power, i); } /* * Intitialize 802.11b, 802.11g * Rates: CCK, OFDM. * Channels: 2.4 GHz */ if (spec->supported_bands & SUPPORT_BAND_2GHZ) { rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_channels = 14; rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_bitrates = num_rates; rt2x00dev->bands[IEEE80211_BAND_2GHZ].channels = channels; rt2x00dev->bands[IEEE80211_BAND_2GHZ].bitrates = rates; hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &rt2x00dev->bands[IEEE80211_BAND_2GHZ]; memcpy(&rt2x00dev->bands[IEEE80211_BAND_2GHZ].ht_cap, &spec->ht, sizeof(spec->ht)); } /* * Intitialize 802.11a * Rates: OFDM. * Channels: OFDM, UNII, HiperLAN2. */ if (spec->supported_bands & SUPPORT_BAND_5GHZ) { rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_channels = spec->num_channels - 14; rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_bitrates = num_rates - 4; rt2x00dev->bands[IEEE80211_BAND_5GHZ].channels = &channels[14]; rt2x00dev->bands[IEEE80211_BAND_5GHZ].bitrates = &rates[4]; hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &rt2x00dev->bands[IEEE80211_BAND_5GHZ]; memcpy(&rt2x00dev->bands[IEEE80211_BAND_5GHZ].ht_cap, &spec->ht, sizeof(spec->ht)); } return 0; exit_free_channels: kfree(channels); ERROR(rt2x00dev, "Allocation ieee80211 modes failed.\n"); return -ENOMEM; } static void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) { if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) ieee80211_unregister_hw(rt2x00dev->hw); if (likely(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ])) { kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels); kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->bitrates); rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; } kfree(rt2x00dev->spec.channels_info); } static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; int status; if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) return 0; /* * Initialize HW modes. */ status = rt2x00lib_probe_hw_modes(rt2x00dev, spec); if (status) return status; /* * Initialize HW fields. */ rt2x00dev->hw->queues = rt2x00dev->ops->tx_queues; /* * Initialize extra TX headroom required. */ rt2x00dev->hw->extra_tx_headroom = max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, rt2x00dev->ops->extra_tx_headroom); /* * Take TX headroom required for alignment into account. */ if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; else if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; /* * Tell mac80211 about the size of our private STA structure. */ rt2x00dev->hw->sta_data_size = sizeof(struct rt2x00_sta); /* * Allocate tx status FIFO for driver use. */ if (test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags)) { /* * Allocate the txstatus fifo. In the worst case the tx * status fifo has to hold the tx status of all entries * in all tx queues. Hence, calculate the kfifo size as * tx_queues * entry_num and round up to the nearest * power of 2. */ int kfifo_size = roundup_pow_of_two(rt2x00dev->ops->tx_queues * rt2x00dev->ops->tx->entry_num * sizeof(u32)); status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size, GFP_KERNEL); if (status) return status; } /* * Initialize tasklets if used by the driver. Tasklets are * disabled until the interrupts are turned on. The driver * has to handle that. */ #define RT2X00_TASKLET_INIT(taskletname) \ if (rt2x00dev->ops->lib->taskletname) { \ tasklet_init(&rt2x00dev->taskletname, \ rt2x00dev->ops->lib->taskletname, \ (unsigned long)rt2x00dev); \ } RT2X00_TASKLET_INIT(txstatus_tasklet); RT2X00_TASKLET_INIT(pretbtt_tasklet); RT2X00_TASKLET_INIT(tbtt_tasklet); RT2X00_TASKLET_INIT(rxdone_tasklet); RT2X00_TASKLET_INIT(autowake_tasklet); #undef RT2X00_TASKLET_INIT /* * Register HW. */ status = ieee80211_register_hw(rt2x00dev->hw); if (status) return status; set_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags); return 0; } /* * Initialization/uninitialization handlers. */ static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) { if (!test_and_clear_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) return; /* * Unregister extra components. */ rt2x00rfkill_unregister(rt2x00dev); /* * Allow the HW to uninitialize. */ rt2x00dev->ops->lib->uninitialize(rt2x00dev); /* * Free allocated queue entries. */ rt2x00queue_uninitialize(rt2x00dev); } static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) { int status; if (test_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) return 0; /* * Allocate all queue entries. */ status = rt2x00queue_initialize(rt2x00dev); if (status) return status; /* * Initialize the device. */ status = rt2x00dev->ops->lib->initialize(rt2x00dev); if (status) { rt2x00queue_uninitialize(rt2x00dev); return status; } set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); return 0; } int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) { int retval; if (test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) return 0; /* * If this is the first interface which is added, * we should load the firmware now. */ retval = rt2x00lib_load_firmware(rt2x00dev); if (retval) return retval; /* * Initialize the device. */ retval = rt2x00lib_initialize(rt2x00dev); if (retval) return retval; rt2x00dev->intf_ap_count = 0; rt2x00dev->intf_sta_count = 0; rt2x00dev->intf_associated = 0; /* Enable the radio */ retval = rt2x00lib_enable_radio(rt2x00dev); if (retval) return retval; set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); return 0; } void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) { if (!test_and_clear_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) return; /* * Perhaps we can add something smarter here, * but for now just disabling the radio should do. */ rt2x00lib_disable_radio(rt2x00dev); rt2x00dev->intf_ap_count = 0; rt2x00dev->intf_sta_count = 0; rt2x00dev->intf_associated = 0; } /* * driver allocation handlers. */ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) { int retval = -ENOMEM; /* * Allocate the driver data memory, if necessary. */ if (rt2x00dev->ops->drv_data_size > 0) { rt2x00dev->drv_data = kzalloc(rt2x00dev->ops->drv_data_size, GFP_KERNEL); if (!rt2x00dev->drv_data) { retval = -ENOMEM; goto exit; } } spin_lock_init(&rt2x00dev->irqmask_lock); mutex_init(&rt2x00dev->csr_mutex); set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); /* * Make room for rt2x00_intf inside the per-interface * structure ieee80211_vif. */ rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf); /* * Determine which operating modes are supported, all modes * which require beaconing, depend on the availability of * beacon entries. */ rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); if (rt2x00dev->ops->bcn->entry_num > 0) rt2x00dev->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_WDS); rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; /* * Initialize work. */ rt2x00dev->workqueue = alloc_ordered_workqueue(wiphy_name(rt2x00dev->hw->wiphy), 0); if (!rt2x00dev->workqueue) { retval = -ENOMEM; goto exit; } INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep); /* * Let the driver probe the device to detect the capabilities. */ retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev); if (retval) { ERROR(rt2x00dev, "Failed to allocate device.\n"); goto exit; } /* * Allocate queue array. */ retval = rt2x00queue_allocate(rt2x00dev); if (retval) goto exit; /* * Initialize ieee80211 structure. */ retval = rt2x00lib_probe_hw(rt2x00dev); if (retval) { ERROR(rt2x00dev, "Failed to initialize hw.\n"); goto exit; } /* * Register extra components. */ rt2x00link_register(rt2x00dev); rt2x00leds_register(rt2x00dev); rt2x00debug_register(rt2x00dev); rt2x00rfkill_register(rt2x00dev); return 0; exit: rt2x00lib_remove_dev(rt2x00dev); return retval; } EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) { clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); /* * Disable radio. */ rt2x00lib_disable_radio(rt2x00dev); /* * Stop all work. */ cancel_work_sync(&rt2x00dev->intf_work); cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); cancel_work_sync(&rt2x00dev->sleep_work); if (rt2x00_is_usb(rt2x00dev)) { hrtimer_cancel(&rt2x00dev->txstatus_timer); cancel_work_sync(&rt2x00dev->rxdone_work); cancel_work_sync(&rt2x00dev->txdone_work); } if (rt2x00dev->workqueue) destroy_workqueue(rt2x00dev->workqueue); /* * Free the tx status fifo. */ kfifo_free(&rt2x00dev->txstatus_fifo); /* * Kill the tx status tasklet. */ tasklet_kill(&rt2x00dev->txstatus_tasklet); tasklet_kill(&rt2x00dev->pretbtt_tasklet); tasklet_kill(&rt2x00dev->tbtt_tasklet); tasklet_kill(&rt2x00dev->rxdone_tasklet); tasklet_kill(&rt2x00dev->autowake_tasklet); /* * Uninitialize device. */ rt2x00lib_uninitialize(rt2x00dev); /* * Free extra components */ rt2x00debug_deregister(rt2x00dev); rt2x00leds_unregister(rt2x00dev); /* * Free ieee80211_hw memory. */ rt2x00lib_remove_hw(rt2x00dev); /* * Free firmware image. */ rt2x00lib_free_firmware(rt2x00dev); /* * Free queue structures. */ rt2x00queue_free(rt2x00dev); /* * Free the driver data. */ if (rt2x00dev->drv_data) kfree(rt2x00dev->drv_data); } EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); /* * Device state handlers */ #ifdef CONFIG_PM int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state) { NOTICE(rt2x00dev, "Going to sleep.\n"); /* * Prevent mac80211 from accessing driver while suspended. */ if (!test_and_clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; /* * Cleanup as much as possible. */ rt2x00lib_uninitialize(rt2x00dev); /* * Suspend/disable extra components. */ rt2x00leds_suspend(rt2x00dev); rt2x00debug_deregister(rt2x00dev); /* * Set device mode to sleep for power management, * on some hardware this call seems to consistently fail. * From the specifications it is hard to tell why it fails, * and if this is a "bad thing". * Overall it is safe to just ignore the failure and * continue suspending. The only downside is that the * device will not be in optimal power save mode, but with * the radio and the other components already disabled the * device is as good as disabled. */ if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_SLEEP)) WARNING(rt2x00dev, "Device failed to enter sleep state, " "continue suspending.\n"); return 0; } EXPORT_SYMBOL_GPL(rt2x00lib_suspend); int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) { NOTICE(rt2x00dev, "Waking up.\n"); /* * Restore/enable extra components. */ rt2x00debug_register(rt2x00dev); rt2x00leds_resume(rt2x00dev); /* * We are ready again to receive requests from mac80211. */ set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); return 0; } EXPORT_SYMBOL_GPL(rt2x00lib_resume); #endif /* CONFIG_PM */ /* * rt2x00lib module information. */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("rt2x00 library"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00debug.h0000644000175000017500000000373412026211315023746 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00debug Abstract: Data structures for the rt2x00debug. */ #ifndef RT2X00DEBUG_H #define RT2X00DEBUG_H struct rt2x00_dev; /** * enum rt2x00debugfs_entry_flags: Flags for debugfs registry entry * * @RT2X00DEBUGFS_OFFSET: rt2x00lib should pass the register offset * as argument when using the callback function read()/write() */ enum rt2x00debugfs_entry_flags { RT2X00DEBUGFS_OFFSET = (1 << 0), }; #define RT2X00DEBUGFS_REGISTER_ENTRY(__name, __type) \ struct reg##__name { \ void (*read)(struct rt2x00_dev *rt2x00dev, \ const unsigned int word, __type *data); \ void (*write)(struct rt2x00_dev *rt2x00dev, \ const unsigned int word, __type data); \ \ unsigned int flags; \ \ unsigned int word_base; \ unsigned int word_size; \ unsigned int word_count; \ } __name struct rt2x00debug { /* * Reference to the modules structure. */ struct module *owner; /* * Register access entries. */ RT2X00DEBUGFS_REGISTER_ENTRY(csr, u32); RT2X00DEBUGFS_REGISTER_ENTRY(eeprom, u16); RT2X00DEBUGFS_REGISTER_ENTRY(bbp, u8); RT2X00DEBUGFS_REGISTER_ENTRY(rf, u32); RT2X00DEBUGFS_REGISTER_ENTRY(rfcsr, u8); }; #endif /* RT2X00DEBUG_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00debug.c0000644000175000017500000005265612026211315023750 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 debugfs specific routines. */ #include #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00lib.h" #include "rt2x00dump.h" #define MAX_LINE_LENGTH 64 struct rt2x00debug_crypto { unsigned long success; unsigned long icv_error; unsigned long mic_error; unsigned long key_error; }; struct rt2x00debug_intf { /* * Pointer to driver structure where * this debugfs entry belongs to. */ struct rt2x00_dev *rt2x00dev; /* * Reference to the rt2x00debug structure * which can be used to communicate with * the registers. */ const struct rt2x00debug *debug; /* * Debugfs entries for: * - driver folder * - driver file * - chipset file * - device state flags file * - device capability flags file * - register folder * - csr offset/value files * - eeprom offset/value files * - bbp offset/value files * - rf offset/value files * - rfcsr offset/value files * - queue folder * - frame dump file * - queue stats file * - crypto stats file */ struct dentry *driver_folder; struct dentry *driver_entry; struct dentry *chipset_entry; struct dentry *dev_flags; struct dentry *cap_flags; struct dentry *register_folder; struct dentry *csr_off_entry; struct dentry *csr_val_entry; struct dentry *eeprom_off_entry; struct dentry *eeprom_val_entry; struct dentry *bbp_off_entry; struct dentry *bbp_val_entry; struct dentry *rf_off_entry; struct dentry *rf_val_entry; struct dentry *rfcsr_off_entry; struct dentry *rfcsr_val_entry; struct dentry *queue_folder; struct dentry *queue_frame_dump_entry; struct dentry *queue_stats_entry; struct dentry *crypto_stats_entry; /* * The frame dump file only allows a single reader, * so we need to store the current state here. */ unsigned long frame_dump_flags; #define FRAME_DUMP_FILE_OPEN 1 /* * We queue each frame before dumping it to the user, * per read command we will pass a single skb structure * so we should be prepared to queue multiple sk buffers * before sending it to userspace. */ struct sk_buff_head frame_dump_skbqueue; wait_queue_head_t frame_dump_waitqueue; /* * HW crypto statistics. * All statistics are stored separately per cipher type. */ struct rt2x00debug_crypto crypto_stats[CIPHER_MAX]; /* * Driver and chipset files will use a data buffer * that has been created in advance. This will simplify * the code since we can use the debugfs functions. */ struct debugfs_blob_wrapper driver_blob; struct debugfs_blob_wrapper chipset_blob; /* * Requested offset for each register type. */ unsigned int offset_csr; unsigned int offset_eeprom; unsigned int offset_bbp; unsigned int offset_rf; unsigned int offset_rfcsr; }; void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, struct rxdone_entry_desc *rxdesc) { struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; enum cipher cipher = rxdesc->cipher; enum rx_crypto status = rxdesc->cipher_status; if (cipher == CIPHER_TKIP_NO_MIC) cipher = CIPHER_TKIP; if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX) return; /* Remove CIPHER_NONE index */ cipher--; intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS); intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV); intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC); intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY); } void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, enum rt2x00_dump_type type, struct sk_buff *skb) { struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); struct sk_buff *skbcopy; struct rt2x00dump_hdr *dump_hdr; struct timeval timestamp; u32 data_len; if (likely(!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))) return; do_gettimeofday(×tamp); if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) { DEBUG(rt2x00dev, "txrx dump queue length exceeded.\n"); return; } data_len = skb->len; if (skbdesc->flags & SKBDESC_DESC_IN_SKB) data_len -= skbdesc->desc_len; skbcopy = alloc_skb(sizeof(*dump_hdr) + skbdesc->desc_len + data_len, GFP_ATOMIC); if (!skbcopy) { DEBUG(rt2x00dev, "Failed to copy skb for dump.\n"); return; } dump_hdr = (struct rt2x00dump_hdr *)skb_put(skbcopy, sizeof(*dump_hdr)); dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION); dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr)); dump_hdr->desc_length = cpu_to_le32(skbdesc->desc_len); dump_hdr->data_length = cpu_to_le32(data_len); dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt); dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf); dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev); dump_hdr->type = cpu_to_le16(type); dump_hdr->queue_index = skbdesc->entry->queue->qid; dump_hdr->entry_index = skbdesc->entry->entry_idx; dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec); dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_usec); if (!(skbdesc->flags & SKBDESC_DESC_IN_SKB)) memcpy(skb_put(skbcopy, skbdesc->desc_len), skbdesc->desc, skbdesc->desc_len); memcpy(skb_put(skbcopy, skb->len), skb->data, skb->len); skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy); wake_up_interruptible(&intf->frame_dump_waitqueue); /* * Verify that the file has not been closed while we were working. */ if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) skb_queue_purge(&intf->frame_dump_skbqueue); } EXPORT_SYMBOL_GPL(rt2x00debug_dump_frame); static int rt2x00debug_file_open(struct inode *inode, struct file *file) { struct rt2x00debug_intf *intf = inode->i_private; file->private_data = inode->i_private; if (!try_module_get(intf->debug->owner)) return -EBUSY; return 0; } static int rt2x00debug_file_release(struct inode *inode, struct file *file) { struct rt2x00debug_intf *intf = file->private_data; module_put(intf->debug->owner); return 0; } static int rt2x00debug_open_queue_dump(struct inode *inode, struct file *file) { struct rt2x00debug_intf *intf = inode->i_private; int retval; retval = rt2x00debug_file_open(inode, file); if (retval) return retval; if (test_and_set_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) { rt2x00debug_file_release(inode, file); return -EBUSY; } return 0; } static int rt2x00debug_release_queue_dump(struct inode *inode, struct file *file) { struct rt2x00debug_intf *intf = inode->i_private; skb_queue_purge(&intf->frame_dump_skbqueue); clear_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags); return rt2x00debug_file_release(inode, file); } static ssize_t rt2x00debug_read_queue_dump(struct file *file, char __user *buf, size_t length, loff_t *offset) { struct rt2x00debug_intf *intf = file->private_data; struct sk_buff *skb; size_t status; int retval; if (file->f_flags & O_NONBLOCK) return -EAGAIN; retval = wait_event_interruptible(intf->frame_dump_waitqueue, (skb = skb_dequeue(&intf->frame_dump_skbqueue))); if (retval) return retval; status = min((size_t)skb->len, length); if (copy_to_user(buf, skb->data, status)) { status = -EFAULT; goto exit; } *offset += status; exit: kfree_skb(skb); return status; } static unsigned int rt2x00debug_poll_queue_dump(struct file *file, poll_table *wait) { struct rt2x00debug_intf *intf = file->private_data; poll_wait(file, &intf->frame_dump_waitqueue, wait); if (!skb_queue_empty(&intf->frame_dump_skbqueue)) return POLLOUT | POLLWRNORM; return 0; } static const struct file_operations rt2x00debug_fop_queue_dump = { .owner = THIS_MODULE, .read = rt2x00debug_read_queue_dump, .poll = rt2x00debug_poll_queue_dump, .open = rt2x00debug_open_queue_dump, .release = rt2x00debug_release_queue_dump, .llseek = default_llseek, }; static ssize_t rt2x00debug_read_queue_stats(struct file *file, char __user *buf, size_t length, loff_t *offset) { struct rt2x00debug_intf *intf = file->private_data; struct data_queue *queue; unsigned long irqflags; unsigned int lines = 1 + intf->rt2x00dev->data_queues; size_t size; char *data; char *temp; if (*offset) return 0; data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return -ENOMEM; temp = data + sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n"); queue_for_each(intf->rt2x00dev, queue) { spin_lock_irqsave(&queue->index_lock, irqflags); temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n", queue->qid, (unsigned int)queue->flags, queue->count, queue->limit, queue->length, queue->index[Q_INDEX], queue->index[Q_INDEX_DMA_DONE], queue->index[Q_INDEX_DONE]); spin_unlock_irqrestore(&queue->index_lock, irqflags); } size = strlen(data); size = min(size, length); if (copy_to_user(buf, data, size)) { kfree(data); return -EFAULT; } kfree(data); *offset += size; return size; } static const struct file_operations rt2x00debug_fop_queue_stats = { .owner = THIS_MODULE, .read = rt2x00debug_read_queue_stats, .open = rt2x00debug_file_open, .release = rt2x00debug_file_release, .llseek = default_llseek, }; #ifdef CONFIG_RT2X00_LIB_CRYPTO static ssize_t rt2x00debug_read_crypto_stats(struct file *file, char __user *buf, size_t length, loff_t *offset) { struct rt2x00debug_intf *intf = file->private_data; static const char * const name[] = { "WEP64", "WEP128", "TKIP", "AES" }; char *data; char *temp; size_t size; unsigned int i; if (*offset) return 0; data = kzalloc((1 + CIPHER_MAX) * MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return -ENOMEM; temp = data; temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n"); for (i = 0; i < CIPHER_MAX; i++) { temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i], intf->crypto_stats[i].success, intf->crypto_stats[i].icv_error, intf->crypto_stats[i].mic_error, intf->crypto_stats[i].key_error); } size = strlen(data); size = min(size, length); if (copy_to_user(buf, data, size)) { kfree(data); return -EFAULT; } kfree(data); *offset += size; return size; } static const struct file_operations rt2x00debug_fop_crypto_stats = { .owner = THIS_MODULE, .read = rt2x00debug_read_crypto_stats, .open = rt2x00debug_file_open, .release = rt2x00debug_file_release, .llseek = default_llseek, }; #endif #define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \ static ssize_t rt2x00debug_read_##__name(struct file *file, \ char __user *buf, \ size_t length, \ loff_t *offset) \ { \ struct rt2x00debug_intf *intf = file->private_data; \ const struct rt2x00debug *debug = intf->debug; \ char line[16]; \ size_t size; \ unsigned int index = intf->offset_##__name; \ __type value; \ \ if (*offset) \ return 0; \ \ if (index >= debug->__name.word_count) \ return -EINVAL; \ \ index += (debug->__name.word_base / \ debug->__name.word_size); \ \ if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ index *= debug->__name.word_size; \ \ debug->__name.read(intf->rt2x00dev, index, &value); \ \ size = sprintf(line, __format, value); \ \ if (copy_to_user(buf, line, size)) \ return -EFAULT; \ \ *offset += size; \ return size; \ } #define RT2X00DEBUGFS_OPS_WRITE(__name, __type) \ static ssize_t rt2x00debug_write_##__name(struct file *file, \ const char __user *buf,\ size_t length, \ loff_t *offset) \ { \ struct rt2x00debug_intf *intf = file->private_data; \ const struct rt2x00debug *debug = intf->debug; \ char line[16]; \ size_t size; \ unsigned int index = intf->offset_##__name; \ __type value; \ \ if (*offset) \ return 0; \ \ if (index >= debug->__name.word_count) \ return -EINVAL; \ \ if (length > sizeof(line)) \ return -EINVAL; \ \ if (copy_from_user(line, buf, length)) \ return -EFAULT; \ \ size = strlen(line); \ value = simple_strtoul(line, NULL, 0); \ \ index += (debug->__name.word_base / \ debug->__name.word_size); \ \ if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ index *= debug->__name.word_size; \ \ debug->__name.write(intf->rt2x00dev, index, value); \ \ *offset += size; \ return size; \ } #define RT2X00DEBUGFS_OPS(__name, __format, __type) \ RT2X00DEBUGFS_OPS_READ(__name, __format, __type); \ RT2X00DEBUGFS_OPS_WRITE(__name, __type); \ \ static const struct file_operations rt2x00debug_fop_##__name = {\ .owner = THIS_MODULE, \ .read = rt2x00debug_read_##__name, \ .write = rt2x00debug_write_##__name, \ .open = rt2x00debug_file_open, \ .release = rt2x00debug_file_release, \ .llseek = generic_file_llseek, \ }; RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32); RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16); RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8); RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32); RT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8); static ssize_t rt2x00debug_read_dev_flags(struct file *file, char __user *buf, size_t length, loff_t *offset) { struct rt2x00debug_intf *intf = file->private_data; char line[16]; size_t size; if (*offset) return 0; size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags); if (copy_to_user(buf, line, size)) return -EFAULT; *offset += size; return size; } static const struct file_operations rt2x00debug_fop_dev_flags = { .owner = THIS_MODULE, .read = rt2x00debug_read_dev_flags, .open = rt2x00debug_file_open, .release = rt2x00debug_file_release, .llseek = default_llseek, }; static ssize_t rt2x00debug_read_cap_flags(struct file *file, char __user *buf, size_t length, loff_t *offset) { struct rt2x00debug_intf *intf = file->private_data; char line[16]; size_t size; if (*offset) return 0; size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags); if (copy_to_user(buf, line, size)) return -EFAULT; *offset += size; return size; } static const struct file_operations rt2x00debug_fop_cap_flags = { .owner = THIS_MODULE, .read = rt2x00debug_read_cap_flags, .open = rt2x00debug_file_open, .release = rt2x00debug_file_release, .llseek = default_llseek, }; static struct dentry *rt2x00debug_create_file_driver(const char *name, struct rt2x00debug_intf *intf, struct debugfs_blob_wrapper *blob) { char *data; data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return NULL; blob->data = data; data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name); data += sprintf(data, "version:\t%s\n", DRV_VERSION); blob->size = strlen(blob->data); return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); } static struct dentry *rt2x00debug_create_file_chipset(const char *name, struct rt2x00debug_intf *intf, struct debugfs_blob_wrapper *blob) { const struct rt2x00debug *debug = intf->debug; char *data; data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return NULL; blob->data = data; data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt); data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf); data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev); data += sprintf(data, "\n"); data += sprintf(data, "register\tbase\twords\twordsize\n"); #define RT2X00DEBUGFS_SPRINTF_REGISTER(__name) \ { \ if(debug->__name.read) \ data += sprintf(data, __stringify(__name) \ "\t%d\t%d\t%d\n", \ debug->__name.word_base, \ debug->__name.word_count, \ debug->__name.word_size); \ } RT2X00DEBUGFS_SPRINTF_REGISTER(csr); RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom); RT2X00DEBUGFS_SPRINTF_REGISTER(bbp); RT2X00DEBUGFS_SPRINTF_REGISTER(rf); RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr); #undef RT2X00DEBUGFS_SPRINTF_REGISTER blob->size = strlen(blob->data); return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); } void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) { const struct rt2x00debug *debug = rt2x00dev->ops->debugfs; struct rt2x00debug_intf *intf; intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL); if (!intf) { ERROR(rt2x00dev, "Failed to allocate debug handler.\n"); return; } intf->debug = debug; intf->rt2x00dev = rt2x00dev; rt2x00dev->debugfs_intf = intf; intf->driver_folder = debugfs_create_dir(intf->rt2x00dev->ops->name, rt2x00dev->hw->wiphy->debugfsdir); if (IS_ERR(intf->driver_folder) || !intf->driver_folder) goto exit; intf->driver_entry = rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob); if (IS_ERR(intf->driver_entry) || !intf->driver_entry) goto exit; intf->chipset_entry = rt2x00debug_create_file_chipset("chipset", intf, &intf->chipset_blob); if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry) goto exit; intf->dev_flags = debugfs_create_file("dev_flags", S_IRUSR, intf->driver_folder, intf, &rt2x00debug_fop_dev_flags); if (IS_ERR(intf->dev_flags) || !intf->dev_flags) goto exit; intf->cap_flags = debugfs_create_file("cap_flags", S_IRUSR, intf->driver_folder, intf, &rt2x00debug_fop_cap_flags); if (IS_ERR(intf->cap_flags) || !intf->cap_flags) goto exit; intf->register_folder = debugfs_create_dir("register", intf->driver_folder); if (IS_ERR(intf->register_folder) || !intf->register_folder) goto exit; #define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ ({ \ if(debug->__name.read) { \ (__intf)->__name##_off_entry = \ debugfs_create_u32(__stringify(__name) "_offset", \ S_IRUSR | S_IWUSR, \ (__intf)->register_folder, \ &(__intf)->offset_##__name); \ if (IS_ERR((__intf)->__name##_off_entry) \ || !(__intf)->__name##_off_entry) \ goto exit; \ \ (__intf)->__name##_val_entry = \ debugfs_create_file(__stringify(__name) "_value", \ S_IRUSR | S_IWUSR, \ (__intf)->register_folder, \ (__intf), &rt2x00debug_fop_##__name); \ if (IS_ERR((__intf)->__name##_val_entry) \ || !(__intf)->__name##_val_entry) \ goto exit; \ } \ }) RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr); #undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY intf->queue_folder = debugfs_create_dir("queue", intf->driver_folder); if (IS_ERR(intf->queue_folder) || !intf->queue_folder) goto exit; intf->queue_frame_dump_entry = debugfs_create_file("dump", S_IRUSR, intf->queue_folder, intf, &rt2x00debug_fop_queue_dump); if (IS_ERR(intf->queue_frame_dump_entry) || !intf->queue_frame_dump_entry) goto exit; skb_queue_head_init(&intf->frame_dump_skbqueue); init_waitqueue_head(&intf->frame_dump_waitqueue); intf->queue_stats_entry = debugfs_create_file("queue", S_IRUSR, intf->queue_folder, intf, &rt2x00debug_fop_queue_stats); #ifdef CONFIG_RT2X00_LIB_CRYPTO if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) intf->crypto_stats_entry = debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, intf, &rt2x00debug_fop_crypto_stats); #endif return; exit: rt2x00debug_deregister(rt2x00dev); ERROR(rt2x00dev, "Failed to register debug handler.\n"); } void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) { struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; if (unlikely(!intf)) return; skb_queue_purge(&intf->frame_dump_skbqueue); #ifdef CONFIG_RT2X00_LIB_CRYPTO debugfs_remove(intf->crypto_stats_entry); #endif debugfs_remove(intf->queue_stats_entry); debugfs_remove(intf->queue_frame_dump_entry); debugfs_remove(intf->queue_folder); debugfs_remove(intf->rfcsr_val_entry); debugfs_remove(intf->rfcsr_off_entry); debugfs_remove(intf->rf_val_entry); debugfs_remove(intf->rf_off_entry); debugfs_remove(intf->bbp_val_entry); debugfs_remove(intf->bbp_off_entry); debugfs_remove(intf->eeprom_val_entry); debugfs_remove(intf->eeprom_off_entry); debugfs_remove(intf->csr_val_entry); debugfs_remove(intf->csr_off_entry); debugfs_remove(intf->register_folder); debugfs_remove(intf->dev_flags); debugfs_remove(intf->cap_flags); debugfs_remove(intf->chipset_entry); debugfs_remove(intf->driver_entry); debugfs_remove(intf->driver_folder); kfree(intf->chipset_blob.data); kfree(intf->driver_blob.data); kfree(intf); rt2x00dev->debugfs_intf = NULL; } compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00crypto.c0000644000175000017500000001554612026211315024177 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 crypto specific routines. */ #include #include #include "rt2x00.h" #include "rt2x00lib.h" enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) { switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: return CIPHER_WEP64; case WLAN_CIPHER_SUITE_WEP104: return CIPHER_WEP128; case WLAN_CIPHER_SUITE_TKIP: return CIPHER_TKIP; case WLAN_CIPHER_SUITE_CCMP: return CIPHER_AES; default: return CIPHER_NONE; } } void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct txentry_desc *txdesc) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !hw_key) return; __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); txdesc->key_idx = hw_key->hw_key_idx; txdesc->iv_offset = txdesc->header_length; txdesc->iv_len = hw_key->iv_len; if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); } unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *key = tx_info->control.hw_key; unsigned int overhead = 0; if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !key) return overhead; /* * Extend frame length to include IV/EIV/ICV/MMIC, * note that these lengths should only be added when * mac80211 does not generate it. */ overhead += key->icv_len; if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) overhead += key->iv_len; if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { if (key->cipher == WLAN_CIPHER_SUITE_TKIP) overhead += 8; } return overhead; } void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); if (unlikely(!txdesc->iv_len)) return; /* Copy IV/EIV data */ memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); } void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); if (unlikely(!txdesc->iv_len)) return; /* Copy IV/EIV data */ memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); /* Move ieee80211 header */ memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); /* Pull buffer to correct size */ skb_pull(skb, txdesc->iv_len); txdesc->length -= txdesc->iv_len; /* IV/EIV data has officially been stripped */ skbdesc->flags |= SKBDESC_IV_STRIPPED; } void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); const unsigned int iv_len = ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) return; skb_push(skb, iv_len); /* Move ieee80211 header */ memmove(skb->data, skb->data + iv_len, header_length); /* Copy IV/EIV data */ memcpy(skb->data + header_length, skbdesc->iv, iv_len); /* IV/EIV data has returned into the frame */ skbdesc->flags &= ~SKBDESC_IV_STRIPPED; } void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int header_length, struct rxdone_entry_desc *rxdesc) { unsigned int payload_len = rxdesc->size - header_length; unsigned int align = ALIGN_SIZE(skb, header_length); unsigned int iv_len; unsigned int icv_len; unsigned int transfer = 0; /* * WEP64/WEP128: Provides IV & ICV * TKIP: Provides IV/EIV & ICV * AES: Provies IV/EIV & ICV */ switch (rxdesc->cipher) { case CIPHER_WEP64: case CIPHER_WEP128: iv_len = 4; icv_len = 4; break; case CIPHER_TKIP: iv_len = 8; icv_len = 4; break; case CIPHER_AES: iv_len = 8; icv_len = 8; break; default: /* Unsupport type */ return; } /* * Make room for new data. There are 2 possibilities * either the alignment is already present between * the 802.11 header and payload. In that case we * we have to move the header less then the iv_len * since we can use the already available l2pad bytes * for the iv data. * When the alignment must be added manually we must * move the header more then iv_len since we must * make room for the payload move as well. */ if (rxdesc->dev_flags & RXDONE_L2PAD) { skb_push(skb, iv_len - align); skb_put(skb, icv_len); /* Move ieee80211 header */ memmove(skb->data + transfer, skb->data + transfer + (iv_len - align), header_length); transfer += header_length; } else { skb_push(skb, iv_len + align); if (align < icv_len) skb_put(skb, icv_len - align); else if (align > icv_len) skb_trim(skb, rxdesc->size + iv_len + icv_len); /* Move ieee80211 header */ memmove(skb->data + transfer, skb->data + transfer + iv_len + align, header_length); transfer += header_length; } /* Copy IV/EIV data */ memcpy(skb->data + transfer, rxdesc->iv, iv_len); transfer += iv_len; /* * Move payload for alignment purposes. Note that * this is only needed when no l2 padding is present. */ if (!(rxdesc->dev_flags & RXDONE_L2PAD)) { memmove(skb->data + transfer, skb->data + transfer + align, payload_len); } /* * NOTE: Always count the payload as transferred, * even when alignment was set to zero. This is required * for determining the correct offset for the ICV data. */ transfer += payload_len; /* * Copy ICV data * AES appends 8 bytes, we can't fill the upper * 4 bytes, but mac80211 doesn't care about what * we provide here anyway and strips it immediately. */ memcpy(skb->data + transfer, &rxdesc->icv, 4); transfer += icv_len; /* IV/EIV/ICV has been inserted into frame */ rxdesc->size = transfer; rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; } compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2x00config.c0000644000175000017500000002016312026211315024113 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2x00lib Abstract: rt2x00 generic configuration routines. */ #include #include #include "rt2x00.h" #include "rt2x00lib.h" void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, enum nl80211_iftype type, const u8 *mac, const u8 *bssid) { struct rt2x00intf_conf conf; unsigned int flags = 0; conf.type = type; switch (type) { case NL80211_IFTYPE_ADHOC: conf.sync = TSF_SYNC_ADHOC; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_WDS: conf.sync = TSF_SYNC_AP_NONE; break; case NL80211_IFTYPE_STATION: conf.sync = TSF_SYNC_INFRA; break; default: conf.sync = TSF_SYNC_NONE; break; } /* * Note that when NULL is passed as address we will send * 00:00:00:00:00 to the device to clear the address. * This will prevent the device being confused when it wants * to ACK frames or considers itself associated. */ memset(conf.mac, 0, sizeof(conf.mac)); if (mac) memcpy(conf.mac, mac, ETH_ALEN); memset(conf.bssid, 0, sizeof(conf.bssid)); if (bssid) memcpy(conf.bssid, bssid, ETH_ALEN); flags |= CONFIG_UPDATE_TYPE; if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) flags |= CONFIG_UPDATE_MAC; if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) flags |= CONFIG_UPDATE_BSSID; rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags); } void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct rt2x00lib_erp erp; memset(&erp, 0, sizeof(erp)); erp.short_preamble = bss_conf->use_short_preamble; erp.cts_protection = bss_conf->use_cts_prot; erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME; erp.sifs = SIFS; erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS; erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS; erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS; erp.basic_rates = bss_conf->basic_rates; erp.beacon_int = bss_conf->beacon_int; /* Update the AID, this is needed for dynamic PS support */ rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0; rt2x00dev->last_beacon = bss_conf->sync_tsf; /* Update global beacon interval time, this is needed for PS support */ rt2x00dev->beacon_int = bss_conf->beacon_int; if (changed & BSS_CHANGED_HT) erp.ht_opmode = bss_conf->ht_operation_mode; rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed); } void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, struct antenna_setup config) { struct link_ant *ant = &rt2x00dev->link.ant; struct antenna_setup *def = &rt2x00dev->default_ant; struct antenna_setup *active = &rt2x00dev->link.ant.active; /* * When the caller tries to send the SW diversity, * we must update the ANTENNA_RX_DIVERSITY flag to * enable the antenna diversity in the link tuner. * * Secondly, we must guarentee we never send the * software antenna diversity command to the driver. */ if (!(ant->flags & ANTENNA_RX_DIVERSITY)) { if (config.rx == ANTENNA_SW_DIVERSITY) { ant->flags |= ANTENNA_RX_DIVERSITY; if (def->rx == ANTENNA_SW_DIVERSITY) config.rx = ANTENNA_B; else config.rx = def->rx; } } else if (config.rx == ANTENNA_SW_DIVERSITY) config.rx = active->rx; if (!(ant->flags & ANTENNA_TX_DIVERSITY)) { if (config.tx == ANTENNA_SW_DIVERSITY) { ant->flags |= ANTENNA_TX_DIVERSITY; if (def->tx == ANTENNA_SW_DIVERSITY) config.tx = ANTENNA_B; else config.tx = def->tx; } } else if (config.tx == ANTENNA_SW_DIVERSITY) config.tx = active->tx; /* * Antenna setup changes require the RX to be disabled, * else the changes will be ignored by the device. */ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2x00queue_stop_queue(rt2x00dev->rx); /* * Write new antenna setup to device and reset the link tuner. * The latter is required since we need to recalibrate the * noise-sensitivity ratio for the new setup. */ rt2x00dev->ops->lib->config_ant(rt2x00dev, &config); rt2x00link_reset_tuner(rt2x00dev, true); memcpy(active, &config, sizeof(config)); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2x00queue_start_queue(rt2x00dev->rx); } static u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf) { struct hw_mode_spec *spec = &rt2x00dev->spec; int center_channel; u16 i; /* * Initialize center channel to current channel. */ center_channel = spec->channels[conf->channel->hw_value].channel; /* * Adjust center channel to HT40+ and HT40- operation. */ if (conf_is_ht40_plus(conf)) center_channel += 2; else if (conf_is_ht40_minus(conf)) center_channel -= (center_channel == 14) ? 1 : 2; for (i = 0; i < spec->num_channels; i++) if (spec->channels[i].channel == center_channel) return i; WARN_ON(1); return conf->channel->hw_value; } void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, unsigned int ieee80211_flags) { struct rt2x00lib_conf libconf; u16 hw_value; u16 autowake_timeout; u16 beacon_int; u16 beacon_diff; memset(&libconf, 0, sizeof(libconf)); libconf.conf = conf; if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) { if (!conf_is_ht(conf)) set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); else clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); if (conf_is_ht40(conf)) { set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); hw_value = rt2x00ht_center_channel(rt2x00dev, conf); } else { clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); hw_value = conf->channel->hw_value; } memcpy(&libconf.rf, &rt2x00dev->spec.channels[hw_value], sizeof(libconf.rf)); memcpy(&libconf.channel, &rt2x00dev->spec.channels_info[hw_value], sizeof(libconf.channel)); /* Used for VCO periodic calibration */ rt2x00dev->rf_channel = libconf.rf.channel; } if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); /* * Start configuration. */ rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags); /* * Some configuration changes affect the link quality * which means we need to reset the link tuner. */ if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) rt2x00link_reset_tuner(rt2x00dev, false); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && (conf->flags & IEEE80211_CONF_PS)) { beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int); if (beacon_diff > beacon_int) beacon_diff = 0; autowake_timeout = (conf->max_sleep_period * beacon_int) - beacon_diff; queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->autowakeup_work, autowake_timeout - 15); } if (conf->flags & IEEE80211_CONF_PS) set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); else clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); rt2x00dev->curr_band = conf->channel->band; rt2x00dev->curr_freq = conf->channel->center_freq; rt2x00dev->tx_power = conf->power_level; rt2x00dev->short_retry = conf->short_frame_max_tx_count; rt2x00dev->long_retry = conf->long_frame_max_tx_count; } compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2800usb.h0000644000175000017500000000677412026211315023360 0ustar mcgrofmcgrof/* Copyright (C) 2009 Ivo van Doorn Copyright (C) 2009 Mattias Nissler Copyright (C) 2009 Felix Fietkau Copyright (C) 2009 Xose Vazquez Perez Copyright (C) 2009 Axel Kollhofer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2800usb Abstract: Data structures and registers for the rt2800usb module. Supported chipsets: RT2800U. */ #ifndef RT2800USB_H #define RT2800USB_H /* * 8051 firmware image. */ #define FIRMWARE_RT2870 "rt2870.bin" #define FIRMWARE_IMAGE_BASE 0x3000 /* * DMA descriptor defines. */ #define TXINFO_DESC_SIZE (1 * sizeof(__le32)) #define RXINFO_DESC_SIZE (1 * sizeof(__le32)) /* * TX Info structure */ /* * Word0 * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. * 0:MGMT, 1:HCCA 2:EDCA * USB_DMA_NEXT_VALID: Used ONLY in USB bulk Aggregation, NextValid * DMA_TX_BURST: used ONLY in USB bulk Aggregation. * Force USB DMA transmit frame from current selected endpoint */ #define TXINFO_W0_USB_DMA_TX_PKT_LEN FIELD32(0x0000ffff) #define TXINFO_W0_WIV FIELD32(0x01000000) #define TXINFO_W0_QSEL FIELD32(0x06000000) #define TXINFO_W0_SW_USE_LAST_ROUND FIELD32(0x08000000) #define TXINFO_W0_USB_DMA_NEXT_VALID FIELD32(0x40000000) #define TXINFO_W0_USB_DMA_TX_BURST FIELD32(0x80000000) /* * RX Info structure */ /* * Word 0 */ #define RXINFO_W0_USB_DMA_RX_PKT_LEN FIELD32(0x0000ffff) /* * RX descriptor format for RX Ring. */ /* * Word0 * UNICAST_TO_ME: This RX frame is unicast to me. * MULTICAST: This is a multicast frame. * BROADCAST: This is a broadcast frame. * MY_BSS: this frame belongs to the same BSSID. * CRC_ERROR: CRC error. * CIPHER_ERROR: 0: decryption okay, 1:ICV error, 2:MIC error, 3:KEY not valid. * AMSDU: rx with 802.3 header, not 802.11 header. */ #define RXD_W0_BA FIELD32(0x00000001) #define RXD_W0_DATA FIELD32(0x00000002) #define RXD_W0_NULLDATA FIELD32(0x00000004) #define RXD_W0_FRAG FIELD32(0x00000008) #define RXD_W0_UNICAST_TO_ME FIELD32(0x00000010) #define RXD_W0_MULTICAST FIELD32(0x00000020) #define RXD_W0_BROADCAST FIELD32(0x00000040) #define RXD_W0_MY_BSS FIELD32(0x00000080) #define RXD_W0_CRC_ERROR FIELD32(0x00000100) #define RXD_W0_CIPHER_ERROR FIELD32(0x00000600) #define RXD_W0_AMSDU FIELD32(0x00000800) #define RXD_W0_HTC FIELD32(0x00001000) #define RXD_W0_RSSI FIELD32(0x00002000) #define RXD_W0_L2PAD FIELD32(0x00004000) #define RXD_W0_AMPDU FIELD32(0x00008000) #define RXD_W0_DECRYPTED FIELD32(0x00010000) #define RXD_W0_PLCP_RSSI FIELD32(0x00020000) #define RXD_W0_CIPHER_ALG FIELD32(0x00040000) #define RXD_W0_LAST_AMSDU FIELD32(0x00080000) #define RXD_W0_PLCP_SIGNAL FIELD32(0xfff00000) #endif /* RT2800USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2800pci.h0000644000175000017500000001010112026211315023315 0ustar mcgrofmcgrof/* Copyright (C) 2009 Ivo van Doorn Copyright (C) 2009 Alban Browaeys Copyright (C) 2009 Felix Fietkau Copyright (C) 2009 Luis Correia Copyright (C) 2009 Mattias Nissler Copyright (C) 2009 Mark Asselstine Copyright (C) 2009 Xose Vazquez Perez Copyright (C) 2009 Bart Zolnierkiewicz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2800pci Abstract: Data structures and registers for the rt2800pci module. Supported chipsets: RT2800E & RT2800ED. */ #ifndef RT2800PCI_H #define RT2800PCI_H /* * Queue register offset macros */ #define TX_QUEUE_REG_OFFSET 0x10 #define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) #define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) #define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) #define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) /* * 8051 firmware image. */ #define FIRMWARE_RT2860 "rt2860.bin" #define FIRMWARE_RT3290 "rt3290.bin" #define FIRMWARE_IMAGE_BASE 0x2000 /* * DMA descriptor defines. */ #define TXD_DESC_SIZE (4 * sizeof(__le32)) #define RXD_DESC_SIZE (4 * sizeof(__le32)) /* * TX descriptor format for TX, PRIO and Beacon Ring. */ /* * Word0 */ #define TXD_W0_SD_PTR0 FIELD32(0xffffffff) /* * Word1 */ #define TXD_W1_SD_LEN1 FIELD32(0x00003fff) #define TXD_W1_LAST_SEC1 FIELD32(0x00004000) #define TXD_W1_BURST FIELD32(0x00008000) #define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) #define TXD_W1_LAST_SEC0 FIELD32(0x40000000) #define TXD_W1_DMA_DONE FIELD32(0x80000000) /* * Word2 */ #define TXD_W2_SD_PTR1 FIELD32(0xffffffff) /* * Word3 * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. * 0:MGMT, 1:HCCA 2:EDCA */ #define TXD_W3_WIV FIELD32(0x01000000) #define TXD_W3_QSEL FIELD32(0x06000000) #define TXD_W3_TCO FIELD32(0x20000000) #define TXD_W3_UCO FIELD32(0x40000000) #define TXD_W3_ICO FIELD32(0x80000000) /* * RX descriptor format for RX Ring. */ /* * Word0 */ #define RXD_W0_SDP0 FIELD32(0xffffffff) /* * Word1 */ #define RXD_W1_SDL1 FIELD32(0x00003fff) #define RXD_W1_SDL0 FIELD32(0x3fff0000) #define RXD_W1_LS0 FIELD32(0x40000000) #define RXD_W1_DMA_DONE FIELD32(0x80000000) /* * Word2 */ #define RXD_W2_SDP1 FIELD32(0xffffffff) /* * Word3 * AMSDU: RX with 802.3 header, not 802.11 header. * DECRYPTED: This frame is being decrypted. */ #define RXD_W3_BA FIELD32(0x00000001) #define RXD_W3_DATA FIELD32(0x00000002) #define RXD_W3_NULLDATA FIELD32(0x00000004) #define RXD_W3_FRAG FIELD32(0x00000008) #define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) #define RXD_W3_MULTICAST FIELD32(0x00000020) #define RXD_W3_BROADCAST FIELD32(0x00000040) #define RXD_W3_MY_BSS FIELD32(0x00000080) #define RXD_W3_CRC_ERROR FIELD32(0x00000100) #define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) #define RXD_W3_AMSDU FIELD32(0x00000800) #define RXD_W3_HTC FIELD32(0x00001000) #define RXD_W3_RSSI FIELD32(0x00002000) #define RXD_W3_L2PAD FIELD32(0x00004000) #define RXD_W3_AMPDU FIELD32(0x00008000) #define RXD_W3_DECRYPTED FIELD32(0x00010000) #define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) #define RXD_W3_PLCP_RSSI FIELD32(0x00040000) #endif /* RT2800PCI_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2800pci.c0000644000175000017500000010773412026211315023333 0ustar mcgrofmcgrof/* Copyright (C) 2009 - 2010 Ivo van Doorn Copyright (C) 2009 Alban Browaeys Copyright (C) 2009 Felix Fietkau Copyright (C) 2009 Luis Correia Copyright (C) 2009 Mattias Nissler Copyright (C) 2009 Mark Asselstine Copyright (C) 2009 Xose Vazquez Perez Copyright (C) 2009 Bart Zolnierkiewicz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2800pci Abstract: rt2800pci device specific routines. Supported chipsets: RT2800E & RT2800ED. */ #include #include #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00pci.h" #include "rt2x00soc.h" #include "rt2800lib.h" #include "rt2800.h" #include "rt2800pci.h" /* * Allow hardware encryption to be disabled. */ static bool modparam_nohwcrypt = false; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); static bool rt2800pci_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) { return modparam_nohwcrypt; } static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) { unsigned int i; u32 reg; /* * SOC devices don't support MCU requests. */ if (rt2x00_is_soc(rt2x00dev)) return; for (i = 0; i < 200; i++) { rt2x00pci_register_read(rt2x00dev, H2M_MAILBOX_CID, ®); if ((rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD0) == token) || (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD1) == token) || (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD2) == token) || (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD3) == token)) break; udelay(REGISTER_BUSY_DELAY); } if (i == 200) ERROR(rt2x00dev, "MCU request failed, no response from hardware\n"); rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); } #if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) { void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); iounmap(base_addr); } #else static inline void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) { } #endif /* CONFIG_RALINK_RT288X || CONFIG_RALINK_RT305X */ #ifdef CONFIG_PCI static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg; rt2x00pci_register_read(rt2x00dev, E2PROM_CSR, ®); eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); eeprom->reg_data_clock = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); eeprom->reg_chip_select = !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); } static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg = 0; rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, !!eeprom->reg_data_clock); rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, !!eeprom->reg_chip_select); rt2x00pci_register_write(rt2x00dev, E2PROM_CSR, reg); } static void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) { struct eeprom_93cx6 eeprom; u32 reg; rt2x00pci_register_read(rt2x00dev, E2PROM_CSR, ®); eeprom.data = rt2x00dev; eeprom.register_read = rt2800pci_eepromregister_read; eeprom.register_write = rt2800pci_eepromregister_write; switch (rt2x00_get_field32(reg, E2PROM_CSR_TYPE)) { case 0: eeprom.width = PCI_EEPROM_WIDTH_93C46; break; case 1: eeprom.width = PCI_EEPROM_WIDTH_93C66; break; default: eeprom.width = PCI_EEPROM_WIDTH_93C86; break; } eeprom.reg_data_in = 0; eeprom.reg_data_out = 0; eeprom.reg_data_clock = 0; eeprom.reg_chip_select = 0; eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, EEPROM_SIZE / sizeof(u16)); } static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) { return rt2800_efuse_detect(rt2x00dev); } static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { rt2800_read_eeprom_efuse(rt2x00dev); } #else static inline void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) { } static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) { return 0; } static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { } #endif /* CONFIG_PCI */ /* * Queue handlers. */ static void rt2800pci_start_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00pci_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); rt2x00pci_register_write(rt2x00dev, MAC_SYS_CTRL, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, BCN_TIME_CFG, reg); rt2x00pci_register_read(rt2x00dev, INT_TIMER_EN, ®); rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); rt2x00pci_register_write(rt2x00dev, INT_TIMER_EN, reg); break; default: break; } } static void rt2800pci_kick_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; struct queue_entry *entry; switch (queue->qid) { case QID_AC_VO: case QID_AC_VI: case QID_AC_BE: case QID_AC_BK: entry = rt2x00queue_get_entry(queue, Q_INDEX); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), entry->entry_idx); break; case QID_MGMT: entry = rt2x00queue_get_entry(queue, Q_INDEX); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX(5), entry->entry_idx); break; default: break; } } static void rt2800pci_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00pci_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); rt2x00pci_register_write(rt2x00dev, MAC_SYS_CTRL, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, BCN_TIME_CFG, reg); rt2x00pci_register_read(rt2x00dev, INT_TIMER_EN, ®); rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); rt2x00pci_register_write(rt2x00dev, INT_TIMER_EN, reg); /* * Wait for current invocation to finish. The tasklet * won't be scheduled anymore afterwards since we disabled * the TBTT and PRE TBTT timer. */ tasklet_kill(&rt2x00dev->tbtt_tasklet); tasklet_kill(&rt2x00dev->pretbtt_tasklet); break; default: break; } } /* * Firmware functions */ static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) { /* * Chip rt3290 use specific 4KB firmware named rt3290.bin. */ if (rt2x00_rt(rt2x00dev, RT3290)) return FIRMWARE_RT3290; else return FIRMWARE_RT2860; } static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { u32 reg; /* * enable Host program ram write selection */ reg = 0; rt2x00_set_field32(®, PBF_SYS_CTRL_HOST_RAM_WRITE, 1); rt2x00pci_register_write(rt2x00dev, PBF_SYS_CTRL, reg); /* * Write firmware to device. */ rt2x00pci_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data, len); rt2x00pci_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000); rt2x00pci_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001); rt2x00pci_register_write(rt2x00dev, H2M_BBP_AGENT, 0); rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); return 0; } /* * Initialization functions. */ static bool rt2800pci_get_entry_state(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 1, &word); return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); } else { rt2x00_desc_read(entry_priv->desc, 1, &word); return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); } } static void rt2800pci_clear_entry(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 0, &word); rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); rt2x00_desc_write(entry_priv->desc, 0, word); rt2x00_desc_read(entry_priv->desc, 1, &word); rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); rt2x00_desc_write(entry_priv->desc, 1, word); /* * Set RX IDX in register to inform hardware that we have * handled this entry and it is available for reuse again. */ rt2x00pci_register_write(rt2x00dev, RX_CRX_IDX, entry->entry_idx); } else { rt2x00_desc_read(entry_priv->desc, 1, &word); rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); rt2x00_desc_write(entry_priv->desc, 1, word); } } static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) { struct queue_entry_priv_pci *entry_priv; /* * Initialize registers. */ entry_priv = rt2x00dev->tx[0].entries[0].priv_data; rt2x00pci_register_write(rt2x00dev, TX_BASE_PTR0, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TX_MAX_CNT0, rt2x00dev->tx[0].limit); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX0, 0); rt2x00pci_register_write(rt2x00dev, TX_DTX_IDX0, 0); entry_priv = rt2x00dev->tx[1].entries[0].priv_data; rt2x00pci_register_write(rt2x00dev, TX_BASE_PTR1, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TX_MAX_CNT1, rt2x00dev->tx[1].limit); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX1, 0); rt2x00pci_register_write(rt2x00dev, TX_DTX_IDX1, 0); entry_priv = rt2x00dev->tx[2].entries[0].priv_data; rt2x00pci_register_write(rt2x00dev, TX_BASE_PTR2, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TX_MAX_CNT2, rt2x00dev->tx[2].limit); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX2, 0); rt2x00pci_register_write(rt2x00dev, TX_DTX_IDX2, 0); entry_priv = rt2x00dev->tx[3].entries[0].priv_data; rt2x00pci_register_write(rt2x00dev, TX_BASE_PTR3, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TX_MAX_CNT3, rt2x00dev->tx[3].limit); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX3, 0); rt2x00pci_register_write(rt2x00dev, TX_DTX_IDX3, 0); rt2x00pci_register_write(rt2x00dev, TX_BASE_PTR4, 0); rt2x00pci_register_write(rt2x00dev, TX_MAX_CNT4, 0); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX4, 0); rt2x00pci_register_write(rt2x00dev, TX_DTX_IDX4, 0); rt2x00pci_register_write(rt2x00dev, TX_BASE_PTR5, 0); rt2x00pci_register_write(rt2x00dev, TX_MAX_CNT5, 0); rt2x00pci_register_write(rt2x00dev, TX_CTX_IDX5, 0); rt2x00pci_register_write(rt2x00dev, TX_DTX_IDX5, 0); entry_priv = rt2x00dev->rx->entries[0].priv_data; rt2x00pci_register_write(rt2x00dev, RX_BASE_PTR, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, RX_MAX_CNT, rt2x00dev->rx[0].limit); rt2x00pci_register_write(rt2x00dev, RX_CRX_IDX, rt2x00dev->rx[0].limit - 1); rt2x00pci_register_write(rt2x00dev, RX_DRX_IDX, 0); rt2800_disable_wpdma(rt2x00dev); rt2x00pci_register_write(rt2x00dev, DELAY_INT_CFG, 0); return 0; } /* * Device state switch handlers. */ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u32 reg; unsigned long flags; /* * When interrupts are being enabled, the interrupt registers * should clear the register to assure a clean state. */ if (state == STATE_RADIO_IRQ_ON) { rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, ®); rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg); } spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); reg = 0; if (state == STATE_RADIO_IRQ_ON) { rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); } rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); if (state == STATE_RADIO_IRQ_OFF) { /* * Wait for possibly running tasklets to finish. */ tasklet_kill(&rt2x00dev->txstatus_tasklet); tasklet_kill(&rt2x00dev->rxdone_tasklet); tasklet_kill(&rt2x00dev->autowake_tasklet); tasklet_kill(&rt2x00dev->tbtt_tasklet); tasklet_kill(&rt2x00dev->pretbtt_tasklet); } } static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; /* * Reset DMA indexes */ rt2x00pci_register_read(rt2x00dev, WPDMA_RST_IDX, ®); rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); rt2x00pci_register_write(rt2x00dev, WPDMA_RST_IDX, reg); rt2x00pci_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); rt2x00pci_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); if (rt2x00_is_pcie(rt2x00dev) && (rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392))) { rt2x00pci_register_read(rt2x00dev, AUX_CTRL, ®); rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); rt2x00pci_register_write(rt2x00dev, AUX_CTRL, reg); } rt2x00pci_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); reg = 0; rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); rt2x00pci_register_write(rt2x00dev, MAC_SYS_CTRL, reg); rt2x00pci_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); return 0; } static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) { int retval; /* Wait for DMA, ignore error until we initialize queues. */ rt2800_wait_wpdma_ready(rt2x00dev); if (unlikely(rt2800pci_init_queues(rt2x00dev))) return -EIO; retval = rt2800_enable_radio(rt2x00dev); if (retval) return retval; /* After resume MCU_BOOT_SIGNAL will trash these. */ rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_RADIO_OFF, 0xff, 0x02); rt2800pci_mcu_status(rt2x00dev, TOKEN_RADIO_OFF); rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, 0, 0); rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); return retval; } static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) { if (rt2x00_is_soc(rt2x00dev)) { rt2800_disable_radio(rt2x00dev); rt2x00pci_register_write(rt2x00dev, PWR_PIN_CFG, 0); rt2x00pci_register_write(rt2x00dev, TX_PIN_CFG, 0); } } static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { if (state == STATE_AWAKE) { rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, 0, 0x02); rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); } else if (state == STATE_SLEEP) { rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_STATUS, 0xffffffff); rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CID, 0xffffffff); rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_SLEEP, 0xff, 0x01); } return 0; } static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int retval = 0; switch (state) { case STATE_RADIO_ON: retval = rt2800pci_enable_radio(rt2x00dev); break; case STATE_RADIO_OFF: /* * After the radio has been disabled, the device should * be put to sleep for powersaving. */ rt2800pci_disable_radio(rt2x00dev); rt2800pci_set_state(rt2x00dev, STATE_SLEEP); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: rt2800pci_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: case STATE_STANDBY: case STATE_AWAKE: retval = rt2800pci_set_state(rt2x00dev, state); break; default: retval = -ENOTSUPP; break; } if (unlikely(retval)) ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", state, retval); return retval; } /* * TX descriptor initialization */ static __le32 *rt2800pci_get_txwi(struct queue_entry *entry) { return (__le32 *) entry->skb->data; } static void rt2800pci_write_tx_desc(struct queue_entry *entry, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct queue_entry_priv_pci *entry_priv = entry->priv_data; __le32 *txd = entry_priv->desc; u32 word; /* * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 * must contains a TXWI structure + 802.11 header + padding + 802.11 * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 * data. It means that LAST_SEC0 is always 0. */ /* * Initialize TX descriptor */ word = 0; rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); rt2x00_desc_write(txd, 0, word); word = 0; rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W1_BURST, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W1_SD_LEN0, TXWI_DESC_SIZE); rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); rt2x00_desc_write(txd, 1, word); word = 0; rt2x00_set_field32(&word, TXD_W2_SD_PTR1, skbdesc->skb_dma + TXWI_DESC_SIZE); rt2x00_desc_write(txd, 2, word); word = 0; rt2x00_set_field32(&word, TXD_W3_WIV, !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W3_QSEL, 2); rt2x00_desc_write(txd, 3, word); /* * Register descriptor details in skb frame descriptor. */ skbdesc->desc = txd; skbdesc->desc_len = TXD_DESC_SIZE; } /* * RX control handlers */ static void rt2800pci_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; __le32 *rxd = entry_priv->desc; u32 word; rt2x00_desc_read(rxd, 3, &word); if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; /* * Unfortunately we don't know the cipher type used during * decryption. This prevents us from correct providing * correct statistics through debugfs. */ rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { /* * Hardware has stripped IV/EIV data from 802.11 frame during * decryption. Unfortunately the descriptor doesn't contain * any fields with the EIV/IV data either, so they can't * be restored by rt2x00lib. */ rxdesc->flags |= RX_FLAG_IV_STRIPPED; /* * The hardware has already checked the Michael Mic and has * stripped it from the frame. Signal this to mac80211. */ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) rxdesc->flags |= RX_FLAG_DECRYPTED; else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) rxdesc->flags |= RX_FLAG_MMIC_ERROR; } if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; if (rt2x00_get_field32(word, RXD_W3_L2PAD)) rxdesc->dev_flags |= RXDONE_L2PAD; /* * Process the RXWI structure that is at the start of the buffer. */ rt2800_process_rxwi(entry, rxdesc); } /* * Interrupt functions. */ static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev) { struct ieee80211_conf conf = { .flags = 0 }; struct rt2x00lib_conf libconf = { .conf = &conf }; rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); } static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) { struct data_queue *queue; struct queue_entry *entry; u32 status; u8 qid; int max_tx_done = 16; while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); if (unlikely(qid >= QID_RX)) { /* * Unknown queue, this shouldn't happen. Just drop * this tx status. */ WARNING(rt2x00dev, "Got TX status report with " "unexpected pid %u, dropping\n", qid); break; } queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); if (unlikely(queue == NULL)) { /* * The queue is NULL, this shouldn't happen. Stop * processing here and drop the tx status */ WARNING(rt2x00dev, "Got TX status for an unavailable " "queue %u, dropping\n", qid); break; } if (unlikely(rt2x00queue_empty(queue))) { /* * The queue is empty. Stop processing here * and drop the tx status. */ WARNING(rt2x00dev, "Got TX status for an empty " "queue %u, dropping\n", qid); break; } entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); rt2800_txdone_entry(entry, status, rt2800pci_get_txwi(entry)); if (--max_tx_done == 0) break; } return !max_tx_done; } static inline void rt2800pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, struct rt2x00_field32 irq_field) { u32 reg; /* * Enable a single interrupt. The interrupt mask register * access needs locking. */ spin_lock_irq(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); rt2x00_set_field32(®, irq_field, 1); rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); spin_unlock_irq(&rt2x00dev->irqmask_lock); } static void rt2800pci_txstatus_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; if (rt2800pci_txdone(rt2x00dev)) tasklet_schedule(&rt2x00dev->txstatus_tasklet); /* * No need to enable the tx status interrupt here as we always * leave it enabled to minimize the possibility of a tx status * register overflow. See comment in interrupt handler. */ } static void rt2800pci_pretbtt_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; rt2x00lib_pretbtt(rt2x00dev); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); } static void rt2800pci_tbtt_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; u32 reg; rt2x00lib_beacondone(rt2x00dev); if (rt2x00dev->intf_ap_count) { /* * The rt2800pci hardware tbtt timer is off by 1us per tbtt * causing beacon skew and as a result causing problems with * some powersaving clients over time. Shorten the beacon * interval every 64 beacons by 64us to mitigate this effect. */ if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { rt2x00pci_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, (rt2x00dev->beacon_int * 16) - 1); rt2x00pci_register_write(rt2x00dev, BCN_TIME_CFG, reg); } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { rt2x00pci_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, (rt2x00dev->beacon_int * 16)); rt2x00pci_register_write(rt2x00dev, BCN_TIME_CFG, reg); } drv_data->tbtt_tick++; drv_data->tbtt_tick %= BCN_TBTT_OFFSET; } if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); } static void rt2800pci_rxdone_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; if (rt2x00pci_rxdone(rt2x00dev)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); } static void rt2800pci_autowake_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; rt2800pci_wakeup(rt2x00dev); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP); } static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) { u32 status; int i; /* * The TX_FIFO_STATUS interrupt needs special care. We should * read TX_STA_FIFO but we should do it immediately as otherwise * the register can overflow and we would lose status reports. * * Hence, read the TX_STA_FIFO register and copy all tx status * reports into a kernel FIFO which is handled in the txstatus * tasklet. We use a tasklet to process the tx status reports * because we can schedule the tasklet multiple times (when the * interrupt fires again during tx status processing). * * Furthermore we don't disable the TX_FIFO_STATUS * interrupt here but leave it enabled so that the TX_STA_FIFO * can also be read while the tx status tasklet gets executed. * * Since we have only one producer and one consumer we don't * need to lock the kfifo. */ for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) { rt2x00pci_register_read(rt2x00dev, TX_STA_FIFO, &status); if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) break; if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) { WARNING(rt2x00dev, "TX status FIFO overrun," "drop tx status report.\n"); break; } } /* Schedule the tasklet for processing the tx status. */ tasklet_schedule(&rt2x00dev->txstatus_tasklet); } static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; u32 reg, mask; /* Read status and ACK all interrupts */ rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, ®); rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg); if (!reg) return IRQ_NONE; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return IRQ_HANDLED; /* * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits * for interrupts and interrupt masks we can just use the value of * INT_SOURCE_CSR to create the interrupt mask. */ mask = ~reg; if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { rt2800pci_txstatus_interrupt(rt2x00dev); /* * Never disable the TX_FIFO_STATUS interrupt. */ rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); } if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) tasklet_schedule(&rt2x00dev->autowake_tasklet); /* * Disable all interrupts for which a tasklet was scheduled right now, * the tasklet will reenable the appropriate interrupts. */ spin_lock(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); reg &= mask; rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); spin_unlock(&rt2x00dev->irqmask_lock); return IRQ_HANDLED; } /* * Device probe functions. */ static void rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) { if (rt2x00_is_soc(rt2x00dev)) rt2800pci_read_eeprom_soc(rt2x00dev); else if (rt2800pci_efuse_detect(rt2x00dev)) rt2800pci_read_eeprom_efuse(rt2x00dev); else rt2800pci_read_eeprom_pci(rt2x00dev); } static const struct ieee80211_ops rt2800pci_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, .set_key = rt2x00mac_set_key, .sw_scan_start = rt2x00mac_sw_scan_start, .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .get_tkip_seq = rt2800_get_tkip_seq, .set_rts_threshold = rt2800_set_rts_threshold, .sta_add = rt2x00mac_sta_add, .sta_remove = rt2x00mac_sta_remove, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2800_conf_tx, .get_tsf = rt2800_get_tsf, .rfkill_poll = rt2x00mac_rfkill_poll, .ampdu_action = rt2800_ampdu_action, .flush = rt2x00mac_flush, .get_survey = rt2800_get_survey, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2800_ops rt2800pci_rt2800_ops = { .register_read = rt2x00pci_register_read, .register_read_lock = rt2x00pci_register_read, /* same for PCI */ .register_write = rt2x00pci_register_write, .register_write_lock = rt2x00pci_register_write, /* same for PCI */ .register_multiread = rt2x00pci_register_multiread, .register_multiwrite = rt2x00pci_register_multiwrite, .regbusy_read = rt2x00pci_regbusy_read, .read_eeprom = rt2800pci_read_eeprom, .hwcrypt_disabled = rt2800pci_hwcrypt_disabled, .drv_write_firmware = rt2800pci_write_firmware, .drv_init_registers = rt2800pci_init_registers, .drv_get_txwi = rt2800pci_get_txwi, }; static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .irq_handler = rt2800pci_interrupt, .txstatus_tasklet = rt2800pci_txstatus_tasklet, .pretbtt_tasklet = rt2800pci_pretbtt_tasklet, .tbtt_tasklet = rt2800pci_tbtt_tasklet, .rxdone_tasklet = rt2800pci_rxdone_tasklet, .autowake_tasklet = rt2800pci_autowake_tasklet, .probe_hw = rt2800_probe_hw, .get_firmware_name = rt2800pci_get_firmware_name, .check_firmware = rt2800_check_firmware, .load_firmware = rt2800_load_firmware, .initialize = rt2x00pci_initialize, .uninitialize = rt2x00pci_uninitialize, .get_entry_state = rt2800pci_get_entry_state, .clear_entry = rt2800pci_clear_entry, .set_device_state = rt2800pci_set_device_state, .rfkill_poll = rt2800_rfkill_poll, .link_stats = rt2800_link_stats, .reset_tuner = rt2800_reset_tuner, .link_tuner = rt2800_link_tuner, .gain_calibration = rt2800_gain_calibration, .vco_calibration = rt2800_vco_calibration, .start_queue = rt2800pci_start_queue, .kick_queue = rt2800pci_kick_queue, .stop_queue = rt2800pci_stop_queue, .flush_queue = rt2x00pci_flush_queue, .write_tx_desc = rt2800pci_write_tx_desc, .write_tx_data = rt2800_write_tx_data, .write_beacon = rt2800_write_beacon, .clear_beacon = rt2800_clear_beacon, .fill_rxdone = rt2800pci_fill_rxdone, .config_shared_key = rt2800_config_shared_key, .config_pairwise_key = rt2800_config_pairwise_key, .config_filter = rt2800_config_filter, .config_intf = rt2800_config_intf, .config_erp = rt2800_config_erp, .config_ant = rt2800_config_ant, .config = rt2800_config, .sta_add = rt2800_sta_add, .sta_remove = rt2800_sta_remove, }; static const struct data_queue_desc rt2800pci_queue_rx = { .entry_num = 128, .data_size = AGGREGATION_SIZE, .desc_size = RXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2800pci_queue_tx = { .entry_num = 64, .data_size = AGGREGATION_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2800pci_queue_bcn = { .entry_num = 8, .data_size = 0, /* No DMA required for beacons */ .desc_size = TXWI_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct rt2x00_ops rt2800pci_ops = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), .max_sta_intf = 1, .max_ap_intf = 8, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXWI_DESC_SIZE, .rx = &rt2800pci_queue_rx, .tx = &rt2800pci_queue_tx, .bcn = &rt2800pci_queue_bcn, .lib = &rt2800pci_rt2x00_ops, .drv = &rt2800pci_rt2800_ops, .hw = &rt2800pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS .debugfs = &rt2800_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * RT2800pci module information. */ #ifdef CONFIG_PCI static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { { PCI_DEVICE(0x1814, 0x0601) }, { PCI_DEVICE(0x1814, 0x0681) }, { PCI_DEVICE(0x1814, 0x0701) }, { PCI_DEVICE(0x1814, 0x0781) }, { PCI_DEVICE(0x1814, 0x3090) }, { PCI_DEVICE(0x1814, 0x3091) }, { PCI_DEVICE(0x1814, 0x3092) }, { PCI_DEVICE(0x1432, 0x7708) }, { PCI_DEVICE(0x1432, 0x7727) }, { PCI_DEVICE(0x1432, 0x7728) }, { PCI_DEVICE(0x1432, 0x7738) }, { PCI_DEVICE(0x1432, 0x7748) }, { PCI_DEVICE(0x1432, 0x7758) }, { PCI_DEVICE(0x1432, 0x7768) }, { PCI_DEVICE(0x1462, 0x891a) }, { PCI_DEVICE(0x1a3b, 0x1059) }, #ifdef CONFIG_RT2800PCI_RT3290 { PCI_DEVICE(0x1814, 0x3290) }, #endif #ifdef CONFIG_RT2800PCI_RT33XX { PCI_DEVICE(0x1814, 0x3390) }, #endif #ifdef CONFIG_RT2800PCI_RT35XX { PCI_DEVICE(0x1432, 0x7711) }, { PCI_DEVICE(0x1432, 0x7722) }, { PCI_DEVICE(0x1814, 0x3060) }, { PCI_DEVICE(0x1814, 0x3062) }, { PCI_DEVICE(0x1814, 0x3562) }, { PCI_DEVICE(0x1814, 0x3592) }, { PCI_DEVICE(0x1814, 0x3593) }, #endif #ifdef CONFIG_RT2800PCI_RT53XX { PCI_DEVICE(0x1814, 0x5360) }, { PCI_DEVICE(0x1814, 0x5362) }, { PCI_DEVICE(0x1814, 0x5390) }, { PCI_DEVICE(0x1814, 0x5392) }, { PCI_DEVICE(0x1814, 0x539a) }, { PCI_DEVICE(0x1814, 0x539b) }, { PCI_DEVICE(0x1814, 0x539f) }, #endif { 0, } }; #endif /* CONFIG_PCI */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards"); #ifdef CONFIG_PCI MODULE_FIRMWARE(FIRMWARE_RT2860); MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); #endif /* CONFIG_PCI */ MODULE_LICENSE("GPL"); #if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) static int rt2800soc_probe(struct platform_device *pdev) { return rt2x00soc_probe(pdev, &rt2800pci_ops); } static struct platform_driver rt2800soc_driver = { .driver = { .name = "rt2800_wmac", .owner = THIS_MODULE, .mod_name = KBUILD_MODNAME, }, .probe = rt2800soc_probe, .remove = __devexit_p(rt2x00soc_remove), .suspend = rt2x00soc_suspend, .resume = rt2x00soc_resume, }; #endif /* CONFIG_RALINK_RT288X || CONFIG_RALINK_RT305X */ #ifdef CONFIG_PCI static int rt2800pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { return rt2x00pci_probe(pci_dev, &rt2800pci_ops); } static struct pci_driver rt2800pci_driver = { .name = KBUILD_MODNAME, .id_table = rt2800pci_device_table, .probe = rt2800pci_probe, .remove = __devexit_p(rt2x00pci_remove), .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; #endif /* CONFIG_PCI */ static int __init rt2800pci_init(void) { int ret = 0; #if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) ret = platform_driver_register(&rt2800soc_driver); if (ret) return ret; #endif #ifdef CONFIG_PCI ret = pci_register_driver(&rt2800pci_driver); if (ret) { #if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) platform_driver_unregister(&rt2800soc_driver); #endif return ret; } #endif return ret; } static void __exit rt2800pci_exit(void) { #ifdef CONFIG_PCI pci_unregister_driver(&rt2800pci_driver); #endif #if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) platform_driver_unregister(&rt2800soc_driver); #endif } module_init(rt2800pci_init); module_exit(rt2800pci_exit); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2800lib.h0000644000175000017500000002010412026211315023314 0ustar mcgrofmcgrof/* Copyright (C) 2010 Willow Garage Copyright (C) 2010 Ivo van Doorn Copyright (C) 2009 Bartlomiej Zolnierkiewicz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef RT2800LIB_H #define RT2800LIB_H struct rt2800_ops { void (*register_read)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value); void (*register_read_lock)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value); void (*register_write)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value); void (*register_write_lock)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value); void (*register_multiread)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u32 length); void (*register_multiwrite)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const void *value, const u32 length); int (*regbusy_read)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const struct rt2x00_field32 field, u32 *reg); void (*read_eeprom)(struct rt2x00_dev *rt2x00dev); bool (*hwcrypt_disabled)(struct rt2x00_dev *rt2x00dev); int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len); int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev); __le32 *(*drv_get_txwi)(struct queue_entry *entry); }; static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; rt2800ops->register_read(rt2x00dev, offset, value); } static inline void rt2800_register_read_lock(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; rt2800ops->register_read_lock(rt2x00dev, offset, value); } static inline void rt2800_register_write(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; rt2800ops->register_write(rt2x00dev, offset, value); } static inline void rt2800_register_write_lock(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 value) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; rt2800ops->register_write_lock(rt2x00dev, offset, value); } static inline void rt2800_register_multiread(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u32 length) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; rt2800ops->register_multiread(rt2x00dev, offset, value, length); } static inline void rt2800_register_multiwrite(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const void *value, const u32 length) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; rt2800ops->register_multiwrite(rt2x00dev, offset, value, length); } static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, const struct rt2x00_field32 field, u32 *reg) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg); } static inline void rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; rt2800ops->read_eeprom(rt2x00dev); } static inline bool rt2800_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; return rt2800ops->hwcrypt_disabled(rt2x00dev); } static inline int rt2800_drv_write_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; return rt2800ops->drv_write_firmware(rt2x00dev, data, len); } static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; return rt2800ops->drv_init_registers(rt2x00dev); } static inline __le32 *rt2800_drv_get_txwi(struct queue_entry *entry) { const struct rt2800_ops *rt2800ops = entry->queue->rt2x00dev->ops->drv; return rt2800ops->drv_get_txwi(entry); } void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, const u8 command, const u8 token, const u8 arg0, const u8 arg1); int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev); int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev); int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len); int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len); void rt2800_write_tx_data(struct queue_entry *entry, struct txentry_desc *txdesc); void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc); void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32* txwi); void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); void rt2800_clear_beacon(struct queue_entry *entry); extern const struct rt2x00debug rt2800_rt2x00debug; int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev); int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid); void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags); void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags); void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed); void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant); void rt2800_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags); void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count); void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev); void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev); int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev); void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev); int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev); void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, u16 *iv16); int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value); int rt2800_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params); u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size); int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); #endif /* RT2800LIB_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2800lib.c0000644000175000017500000053470612026211315023331 0ustar mcgrofmcgrof/* Copyright (C) 2010 Willow Garage Copyright (C) 2010 Ivo van Doorn Copyright (C) 2009 Bartlomiej Zolnierkiewicz Copyright (C) 2009 Gertjan van Wingerde Based on the original rt2800pci.c and rt2800usb.c. Copyright (C) 2009 Alban Browaeys Copyright (C) 2009 Felix Fietkau Copyright (C) 2009 Luis Correia Copyright (C) 2009 Mattias Nissler Copyright (C) 2009 Mark Asselstine Copyright (C) 2009 Xose Vazquez Perez This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2800lib Abstract: rt2800 generic device routines. */ #include #include #include #include #include "rt2x00.h" #include "rt2800lib.h" #include "rt2800.h" /* * Register access. * All access to the CSR registers will go through the methods * rt2800_register_read and rt2800_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay * between each attampt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. * The _lock versions must be used if you already hold the csr_mutex */ #define WAIT_FOR_BBP(__dev, __reg) \ rt2800_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg)) #define WAIT_FOR_RFCSR(__dev, __reg) \ rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg)) #define WAIT_FOR_RF(__dev, __reg) \ rt2800_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg)) #define WAIT_FOR_MCU(__dev, __reg) \ rt2800_regbusy_read((__dev), H2M_MAILBOX_CSR, \ H2M_MAILBOX_CSR_OWNER, (__reg)) static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev) { /* check for rt2872 on SoC */ if (!rt2x00_is_soc(rt2x00dev) || !rt2x00_rt(rt2x00dev, RT2872)) return false; /* we know for sure that these rf chipsets are used on rt305x boards */ if (rt2x00_rf(rt2x00dev, RF3020) || rt2x00_rf(rt2x00dev, RF3021) || rt2x00_rf(rt2x00dev, RF3022)) return true; NOTICE(rt2x00dev, "Unknown RF chipset on rt305x\n"); return false; } static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, BBP_CSR_CFG_VALUE, value); rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 0); rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2800_bbp_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the read request into the register. * After the data has been written, we wait until hardware * returns the correct value, if at any time the register * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 1); rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE); mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RFCSR becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RFCSR becomes available, afterwards we * can safely write the read request into the register. * After the data has been written, we wait until hardware * returns the correct value, if at any time the register * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); WAIT_FOR_RFCSR(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RF becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_RF(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, RF_CSR_CFG0_REG_VALUE_BW, value); rt2x00_set_field32(®, RF_CSR_CFG0_STANDBYMODE, 0); rt2x00_set_field32(®, RF_CSR_CFG0_SEL, 0); rt2x00_set_field32(®, RF_CSR_CFG0_BUSY, 1); rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg); rt2x00_rf_write(rt2x00dev, word, value); } mutex_unlock(&rt2x00dev->csr_mutex); } static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev) { u32 reg; int i, count; rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); if (rt2x00_get_field32(reg, WLAN_EN)) return 0; rt2x00_set_field32(®, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff); rt2x00_set_field32(®, FRC_WL_ANT_SET, 1); rt2x00_set_field32(®, WLAN_CLK_EN, 0); rt2x00_set_field32(®, WLAN_EN, 1); rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); udelay(REGISTER_BUSY_DELAY); count = 0; do { /* * Check PLL_LD & XTAL_RDY. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2800_register_read(rt2x00dev, CMB_CTRL, ®); if (rt2x00_get_field32(reg, PLL_LD) && rt2x00_get_field32(reg, XTAL_RDY)) break; udelay(REGISTER_BUSY_DELAY); } if (i >= REGISTER_BUSY_COUNT) { if (count >= 10) return -EIO; rt2800_register_write(rt2x00dev, 0x58, 0x018); udelay(REGISTER_BUSY_DELAY); rt2800_register_write(rt2x00dev, 0x58, 0x418); udelay(REGISTER_BUSY_DELAY); rt2800_register_write(rt2x00dev, 0x58, 0x618); udelay(REGISTER_BUSY_DELAY); count++; } else { count = 0; } rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 0); rt2x00_set_field32(®, WLAN_CLK_EN, 1); rt2x00_set_field32(®, WLAN_RESET, 1); rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); udelay(10); rt2x00_set_field32(®, WLAN_RESET, 0); rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); udelay(10); rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff); } while (count != 0); return 0; } void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, const u8 command, const u8 token, const u8 arg0, const u8 arg1) { u32 reg; /* * SOC devices don't support MCU requests. */ if (rt2x00_is_soc(rt2x00dev)) return; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the MCU becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_MCU(rt2x00dev, ®)) { rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); rt2800_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg); reg = 0; rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); rt2800_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } EXPORT_SYMBOL_GPL(rt2800_mcu_request); int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i = 0; u32 reg; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2800_register_read(rt2x00dev, MAC_CSR0, ®); if (reg && reg != ~0) return 0; msleep(1); } ERROR(rt2x00dev, "Unstable hardware.\n"); return -EBUSY; } EXPORT_SYMBOL_GPL(rt2800_wait_csr_ready); int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u32 reg; /* * Some devices are really slow to respond here. Wait a whole second * before timing out. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) return 0; msleep(10); } ERROR(rt2x00dev, "WPDMA TX/RX busy [0x%08x].\n", reg); return -EACCES; } EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready); void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); } EXPORT_SYMBOL_GPL(rt2800_disable_wpdma); static bool rt2800_check_firmware_crc(const u8 *data, const size_t len) { u16 fw_crc; u16 crc; /* * The last 2 bytes in the firmware array are the crc checksum itself, * this means that we should never pass those 2 bytes to the crc * algorithm. */ fw_crc = (data[len - 2] << 8 | data[len - 1]); /* * Use the crc ccitt algorithm. * This will return the same value as the legacy driver which * used bit ordering reversion on the both the firmware bytes * before input input as well as on the final output. * Obviously using crc ccitt directly is much more efficient. */ crc = crc_ccitt(~0, data, len - 2); /* * There is a small difference between the crc-itu-t + bitrev and * the crc-ccitt crc calculation. In the latter method the 2 bytes * will be swapped, use swab16 to convert the crc to the correct * value. */ crc = swab16(crc); return fw_crc == crc; } int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { size_t offset = 0; size_t fw_len; bool multiple; /* * PCI(e) & SOC devices require firmware with a length * of 8kb. USB devices require firmware files with a length * of 4kb. Certain USB chipsets however require different firmware, * which Ralink only provides attached to the original firmware * file. Thus for USB devices, firmware files have a length * which is a multiple of 4kb. The firmware for rt3290 chip also * have a length which is a multiple of 4kb. */ if (rt2x00_is_usb(rt2x00dev) || rt2x00_rt(rt2x00dev, RT3290)) fw_len = 4096; else fw_len = 8192; multiple = true; /* * Validate the firmware length */ if (len != fw_len && (!multiple || (len % fw_len) != 0)) return FW_BAD_LENGTH; /* * Check if the chipset requires one of the upper parts * of the firmware. */ if (rt2x00_is_usb(rt2x00dev) && !rt2x00_rt(rt2x00dev, RT2860) && !rt2x00_rt(rt2x00dev, RT2872) && !rt2x00_rt(rt2x00dev, RT3070) && ((len / fw_len) == 1)) return FW_BAD_VERSION; /* * 8kb firmware files must be checked as if it were * 2 separate firmware files. */ while (offset < len) { if (!rt2800_check_firmware_crc(data + offset, fw_len)) return FW_BAD_CRC; offset += fw_len; } return FW_OK; } EXPORT_SYMBOL_GPL(rt2800_check_firmware); int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, const u8 *data, const size_t len) { unsigned int i; u32 reg; int retval; if (rt2x00_rt(rt2x00dev, RT3290)) { retval = rt2800_enable_wlan_rt3290(rt2x00dev); if (retval) return -EBUSY; } /* * If driver doesn't wake up firmware here, * rt2800_load_firmware will hang forever when interface is up again. */ rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000); /* * Wait for stable hardware. */ if (rt2800_wait_csr_ready(rt2x00dev)) return -EBUSY; if (rt2x00_is_pci(rt2x00dev)) { if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_register_read(rt2x00dev, AUX_CTRL, ®); rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); rt2800_register_write(rt2x00dev, AUX_CTRL, reg); } rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002); } rt2800_disable_wpdma(rt2x00dev); /* * Write firmware to the device. */ rt2800_drv_write_firmware(rt2x00dev, data, len); /* * Wait for device to stabilize. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY)) break; msleep(1); } if (i == REGISTER_BUSY_COUNT) { ERROR(rt2x00dev, "PBF system register not ready.\n"); return -EBUSY; } /* * Disable DMA, will be reenabled later when enabling * the radio. */ rt2800_disable_wpdma(rt2x00dev); /* * Initialize firmware. */ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); if (rt2x00_is_usb(rt2x00dev)) rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); msleep(1); return 0; } EXPORT_SYMBOL_GPL(rt2800_load_firmware); void rt2800_write_tx_data(struct queue_entry *entry, struct txentry_desc *txdesc) { __le32 *txwi = rt2800_drv_get_txwi(entry); u32 word; /* * Initialize TX Info descriptor */ rt2x00_desc_read(txwi, 0, &word); rt2x00_set_field32(&word, TXWI_W0_FRAG, test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, test_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0); rt2x00_set_field32(&word, TXWI_W0_TS, test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W0_AMPDU, test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, txdesc->u.ht.mpdu_density); rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->u.ht.txop); rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->u.ht.mcs); rt2x00_set_field32(&word, TXWI_W0_BW, test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W0_SHORT_GI, test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->u.ht.stbc); rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode); rt2x00_desc_write(txwi, 0, word); rt2x00_desc_read(txwi, 1, &word); rt2x00_set_field32(&word, TXWI_W1_ACK, test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W1_NSEQ, test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->u.ht.ba_size); rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID, test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ? txdesc->key_idx : txdesc->u.ht.wcid); rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXWI_W1_PACKETID_QUEUE, entry->queue->qid); rt2x00_set_field32(&word, TXWI_W1_PACKETID_ENTRY, (entry->entry_idx % 3) + 1); rt2x00_desc_write(txwi, 1, word); /* * Always write 0 to IV/EIV fields, hardware will insert the IV * from the IVEIV register when TXD_W3_WIV is set to 0. * When TXD_W3_WIV is set to 1 it will use the IV data * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which * crypto entry in the registers should be used to encrypt the frame. */ _rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */); _rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */); } EXPORT_SYMBOL_GPL(rt2800_write_tx_data); static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) { s8 rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); s8 rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); s8 rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); u16 eeprom; u8 offset0; u8 offset1; u8 offset2; if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom); offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0); offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1); rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2); } else { rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom); offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0); offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1); rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2); } /* * Convert the value from the descriptor into the RSSI value * If the value in the descriptor is 0, it is considered invalid * and the default (extremely low) rssi value is assumed */ rssi0 = (rssi0) ? (-12 - offset0 - rt2x00dev->lna_gain - rssi0) : -128; rssi1 = (rssi1) ? (-12 - offset1 - rt2x00dev->lna_gain - rssi1) : -128; rssi2 = (rssi2) ? (-12 - offset2 - rt2x00dev->lna_gain - rssi2) : -128; /* * mac80211 only accepts a single RSSI value. Calculating the * average doesn't deliver a fair answer either since -60:-60 would * be considered equally good as -50:-70 while the second is the one * which gives less energy... */ rssi0 = max(rssi0, rssi1); return (int)max(rssi0, rssi2); } void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { __le32 *rxwi = (__le32 *) entry->skb->data; u32 word; rt2x00_desc_read(rxwi, 0, &word); rxdesc->cipher = rt2x00_get_field32(word, RXWI_W0_UDF); rxdesc->size = rt2x00_get_field32(word, RXWI_W0_MPDU_TOTAL_BYTE_COUNT); rt2x00_desc_read(rxwi, 1, &word); if (rt2x00_get_field32(word, RXWI_W1_SHORT_GI)) rxdesc->flags |= RX_FLAG_SHORT_GI; if (rt2x00_get_field32(word, RXWI_W1_BW)) rxdesc->flags |= RX_FLAG_40MHZ; /* * Detect RX rate, always use MCS as signal type. */ rxdesc->dev_flags |= RXDONE_SIGNAL_MCS; rxdesc->signal = rt2x00_get_field32(word, RXWI_W1_MCS); rxdesc->rate_mode = rt2x00_get_field32(word, RXWI_W1_PHYMODE); /* * Mask of 0x8 bit to remove the short preamble flag. */ if (rxdesc->rate_mode == RATE_MODE_CCK) rxdesc->signal &= ~0x8; rt2x00_desc_read(rxwi, 2, &word); /* * Convert descriptor AGC value to RSSI value. */ rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word); /* * Remove RXWI descriptor from start of buffer. */ skb_pull(entry->skb, RXWI_DESC_SIZE); } EXPORT_SYMBOL_GPL(rt2800_process_rxwi); void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct txdone_entry_desc txdesc; u32 word; u16 mcs, real_mcs; int aggr, ampdu; /* * Obtain the status about this packet. */ txdesc.flags = 0; rt2x00_desc_read(txwi, 0, &word); mcs = rt2x00_get_field32(word, TXWI_W0_MCS); ampdu = rt2x00_get_field32(word, TXWI_W0_AMPDU); real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS); aggr = rt2x00_get_field32(status, TX_STA_FIFO_TX_AGGRE); /* * If a frame was meant to be sent as a single non-aggregated MPDU * but ended up in an aggregate the used tx rate doesn't correlate * with the one specified in the TXWI as the whole aggregate is sent * with the same rate. * * For example: two frames are sent to rt2x00, the first one sets * AMPDU=1 and requests MCS7 whereas the second frame sets AMDPU=0 * and requests MCS15. If the hw aggregates both frames into one * AMDPU the tx status for both frames will contain MCS7 although * the frame was sent successfully. * * Hence, replace the requested rate with the real tx rate to not * confuse the rate control algortihm by providing clearly wrong * data. */ if (unlikely(aggr == 1 && ampdu == 0 && real_mcs != mcs)) { skbdesc->tx_rate_idx = real_mcs; mcs = real_mcs; } if (aggr == 1 || ampdu == 1) __set_bit(TXDONE_AMPDU, &txdesc.flags); /* * Ralink has a retry mechanism using a global fallback * table. We setup this fallback table to try the immediate * lower rate for all rates. In the TX_STA_FIFO, the MCS field * always contains the MCS used for the last transmission, be * it successful or not. */ if (rt2x00_get_field32(status, TX_STA_FIFO_TX_SUCCESS)) { /* * Transmission succeeded. The number of retries is * mcs - real_mcs */ __set_bit(TXDONE_SUCCESS, &txdesc.flags); txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0); } else { /* * Transmission failed. The number of retries is * always 7 in this case (for a total number of 8 * frames sent). */ __set_bit(TXDONE_FAILURE, &txdesc.flags); txdesc.retry = rt2x00dev->long_retry; } /* * the frame was retried at least once * -> hw used fallback rates */ if (txdesc.retry) __set_bit(TXDONE_FALLBACK, &txdesc.flags); rt2x00lib_txdone(entry, &txdesc); } EXPORT_SYMBOL_GPL(rt2800_txdone_entry); void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); unsigned int beacon_base; unsigned int padding_len; u32 orig_reg, reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); orig_reg = reg; rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); /* * Add space for the TXWI in front of the skb. */ memset(skb_push(entry->skb, TXWI_DESC_SIZE), 0, TXWI_DESC_SIZE); /* * Register descriptor details in skb frame descriptor. */ skbdesc->flags |= SKBDESC_DESC_IN_SKB; skbdesc->desc = entry->skb->data; skbdesc->desc_len = TXWI_DESC_SIZE; /* * Add the TXWI for the beacon to the skb. */ rt2800_write_tx_data(entry, txdesc); /* * Dump beacon to userspace through debugfs. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); /* * Write entire beacon with TXWI and padding to register. */ padding_len = roundup(entry->skb->len, 4) - entry->skb->len; if (padding_len && skb_pad(entry->skb, padding_len)) { ERROR(rt2x00dev, "Failure padding beacon, aborting\n"); /* skb freed by skb_pad() on failure */ entry->skb = NULL; rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); return; } beacon_base = HW_BEACON_OFFSET(entry->entry_idx); rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, entry->skb->len + padding_len); /* * Enable beaconing again. */ rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); /* * Clean up beacon skb. */ dev_kfree_skb_any(entry->skb); entry->skb = NULL; } EXPORT_SYMBOL_GPL(rt2800_write_beacon); static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, unsigned int beacon_base) { int i; /* * For the Beacon base registers we only need to clear * the whole TXWI which (when set to 0) will invalidate * the entire beacon. */ for (i = 0; i < TXWI_DESC_SIZE; i += sizeof(__le32)) rt2800_register_write(rt2x00dev, beacon_base + i, 0); } void rt2800_clear_beacon(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; u32 reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); /* * Clear beacon. */ rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_OFFSET(entry->entry_idx)); /* * Enabled beaconing again. */ rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); } EXPORT_SYMBOL_GPL(rt2800_clear_beacon); #ifdef CONFIG_RT2X00_LIB_DEBUGFS const struct rt2x00debug rt2800_rt2x00debug = { .owner = THIS_MODULE, .csr = { .read = rt2800_register_read, .write = rt2800_register_write, .flags = RT2X00DEBUGFS_OFFSET, .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt2800_bbp_read, .write = rt2800_bbp_write, .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt2800_rf_write, .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, .rfcsr = { .read = rt2800_rfcsr_read, .write = rt2800_rfcsr_write, .word_base = RFCSR_BASE, .word_size = sizeof(u8), .word_count = RFCSR_SIZE / sizeof(u8), }, }; EXPORT_SYMBOL_GPL(rt2800_rt2x00debug); #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; if (rt2x00_rt(rt2x00dev, RT3290)) { rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); return rt2x00_get_field32(reg, WLAN_GPIO_IN_BIT0); } else { rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); return rt2x00_get_field32(reg, GPIO_CTRL_VAL2); } } EXPORT_SYMBOL_GPL(rt2800_rfkill_poll); #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2800_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); unsigned int enabled = brightness != LED_OFF; unsigned int bg_mode = (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); unsigned int polarity = rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, EEPROM_FREQ_LED_POLARITY); unsigned int ledmode = rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, EEPROM_FREQ_LED_MODE); u32 reg; /* Check for SoC (SOC devices don't support MCU requests) */ if (rt2x00_is_soc(led->rt2x00dev)) { rt2800_register_read(led->rt2x00dev, LED_CFG, ®); /* Set LED Polarity */ rt2x00_set_field32(®, LED_CFG_LED_POLAR, polarity); /* Set LED Mode */ if (led->type == LED_TYPE_RADIO) { rt2x00_set_field32(®, LED_CFG_G_LED_MODE, enabled ? 3 : 0); } else if (led->type == LED_TYPE_ASSOC) { rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, enabled ? 3 : 0); } else if (led->type == LED_TYPE_QUALITY) { rt2x00_set_field32(®, LED_CFG_R_LED_MODE, enabled ? 3 : 0); } rt2800_register_write(led->rt2x00dev, LED_CFG, reg); } else { if (led->type == LED_TYPE_RADIO) { rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, enabled ? 0x20 : 0); } else if (led->type == LED_TYPE_ASSOC) { rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20); } else if (led->type == LED_TYPE_QUALITY) { /* * The brightness is divided into 6 levels (0 - 5), * The specs tell us the following levels: * 0, 1 ,3, 7, 15, 31 * to determine the level in a simple way we can simply * work with bitshifting: * (1 << level) - 1 */ rt2800_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, (1 << brightness / (LED_FULL / 6)) - 1, polarity); } } } static void rt2800_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; led->type = type; led->led_dev.brightness_set = rt2800_brightness_set; led->flags = LED_INITIALIZED; } #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Configuration handlers. */ static void rt2800_config_wcid(struct rt2x00_dev *rt2x00dev, const u8 *address, int wcid) { struct mac_wcid_entry wcid_entry; u32 offset; offset = MAC_WCID_ENTRY(wcid); memset(&wcid_entry, 0xff, sizeof(wcid_entry)); if (address) memcpy(wcid_entry.mac, address, ETH_ALEN); rt2800_register_multiwrite(rt2x00dev, offset, &wcid_entry, sizeof(wcid_entry)); } static void rt2800_delete_wcid_attr(struct rt2x00_dev *rt2x00dev, int wcid) { u32 offset; offset = MAC_WCID_ATTR_ENTRY(wcid); rt2800_register_write(rt2x00dev, offset, 0); } static void rt2800_config_wcid_attr_bssidx(struct rt2x00_dev *rt2x00dev, int wcid, u32 bssidx) { u32 offset = MAC_WCID_ATTR_ENTRY(wcid); u32 reg; /* * The BSS Idx numbers is split in a main value of 3 bits, * and a extended field for adding one additional bit to the value. */ rt2800_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX, (bssidx & 0x7)); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT, (bssidx & 0x8) >> 3); rt2800_register_write(rt2x00dev, offset, reg); } static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { struct mac_iveiv_entry iveiv_entry; u32 offset; u32 reg; offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx); if (crypto->cmd == SET_KEY) { rt2800_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); /* * Both the cipher as the BSS Idx numbers are split in a main * value of 3 bits, and a extended field for adding one additional * bit to the value. */ rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, (crypto->cipher & 0x7)); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, (crypto->cipher & 0x8) >> 3); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher); rt2800_register_write(rt2x00dev, offset, reg); } else { /* Delete the cipher without touching the bssidx */ rt2800_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, 0); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, 0); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, 0); rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, 0); rt2800_register_write(rt2x00dev, offset, reg); } offset = MAC_IVEIV_ENTRY(key->hw_key_idx); memset(&iveiv_entry, 0, sizeof(iveiv_entry)); if ((crypto->cipher == CIPHER_TKIP) || (crypto->cipher == CIPHER_TKIP_NO_MIC) || (crypto->cipher == CIPHER_AES)) iveiv_entry.iv[3] |= 0x20; iveiv_entry.iv[3] |= key->keyidx << 6; rt2800_register_multiwrite(rt2x00dev, offset, &iveiv_entry, sizeof(iveiv_entry)); } int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { struct hw_key_entry key_entry; struct rt2x00_field32 field; u32 offset; u32 reg; if (crypto->cmd == SET_KEY) { key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx; memcpy(key_entry.key, crypto->key, sizeof(key_entry.key)); memcpy(key_entry.tx_mic, crypto->tx_mic, sizeof(key_entry.tx_mic)); memcpy(key_entry.rx_mic, crypto->rx_mic, sizeof(key_entry.rx_mic)); offset = SHARED_KEY_ENTRY(key->hw_key_idx); rt2800_register_multiwrite(rt2x00dev, offset, &key_entry, sizeof(key_entry)); } /* * The cipher types are stored over multiple registers * starting with SHARED_KEY_MODE_BASE each word will have * 32 bits and contains the cipher types for 2 bssidx each. * Using the correct defines correctly will cause overhead, * so just calculate the correct offset. */ field.bit_offset = 4 * (key->hw_key_idx % 8); field.bit_mask = 0x7 << field.bit_offset; offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8); rt2800_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, field, (crypto->cmd == SET_KEY) * crypto->cipher); rt2800_register_write(rt2x00dev, offset, reg); /* * Update WCID information */ rt2800_config_wcid(rt2x00dev, crypto->address, key->hw_key_idx); rt2800_config_wcid_attr_bssidx(rt2x00dev, key->hw_key_idx, crypto->bssidx); rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); return 0; } EXPORT_SYMBOL_GPL(rt2800_config_shared_key); static inline int rt2800_find_wcid(struct rt2x00_dev *rt2x00dev) { struct mac_wcid_entry wcid_entry; int idx; u32 offset; /* * Search for the first free WCID entry and return the corresponding * index. * * Make sure the WCID starts _after_ the last possible shared key * entry (>32). * * Since parts of the pairwise key table might be shared with * the beacon frame buffers 6 & 7 we should only write into the * first 222 entries. */ for (idx = 33; idx <= 222; idx++) { offset = MAC_WCID_ENTRY(idx); rt2800_register_multiread(rt2x00dev, offset, &wcid_entry, sizeof(wcid_entry)); if (is_broadcast_ether_addr(wcid_entry.mac)) return idx; } /* * Use -1 to indicate that we don't have any more space in the WCID * table. */ return -1; } int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key) { struct hw_key_entry key_entry; u32 offset; if (crypto->cmd == SET_KEY) { /* * Allow key configuration only for STAs that are * known by the hw. */ if (crypto->wcid < 0) return -ENOSPC; key->hw_key_idx = crypto->wcid; memcpy(key_entry.key, crypto->key, sizeof(key_entry.key)); memcpy(key_entry.tx_mic, crypto->tx_mic, sizeof(key_entry.tx_mic)); memcpy(key_entry.rx_mic, crypto->rx_mic, sizeof(key_entry.rx_mic)); offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx); rt2800_register_multiwrite(rt2x00dev, offset, &key_entry, sizeof(key_entry)); } /* * Update WCID information */ rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); return 0; } EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key); int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { int wcid; struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); /* * Find next free WCID. */ wcid = rt2800_find_wcid(rt2x00dev); /* * Store selected wcid even if it is invalid so that we can * later decide if the STA is uploaded into the hw. */ sta_priv->wcid = wcid; /* * No space left in the device, however, we can still communicate * with the STA -> No error. */ if (wcid < 0) return 0; /* * Clean up WCID attributes and write STA address to the device. */ rt2800_delete_wcid_attr(rt2x00dev, wcid); rt2800_config_wcid(rt2x00dev, sta->addr, wcid); rt2800_config_wcid_attr_bssidx(rt2x00dev, wcid, rt2x00lib_get_bssidx(rt2x00dev, vif)); return 0; } EXPORT_SYMBOL_GPL(rt2800_sta_add); int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid) { /* * Remove WCID entry, no need to clean the attributes as they will * get renewed when the WCID is reused. */ rt2800_config_wcid(rt2x00dev, NULL, wcid); return 0; } EXPORT_SYMBOL_GPL(rt2800_sta_remove); void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { u32 reg; /* * Start configuration steps. * Note that the version error will always be dropped * and broadcast frames will always be accepted since * there is no filter for it at this time. */ rt2800_register_read(rt2x00dev, RX_FILTER_CFG, ®); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CRC_ERROR, !(filter_flags & FIF_FCSFAIL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PHY_ERROR, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_VER_ERROR, 1); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_MULTICAST, !(filter_flags & FIF_ALLMULTI)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BROADCAST, 0); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_DUPLICATE, 1); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END_ACK, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_ACK, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CTS, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_RTS, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, !(filter_flags & FIF_PSPOLL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, !(filter_flags & FIF_CONTROL)); rt2800_register_write(rt2x00dev, RX_FILTER_CFG, reg); } EXPORT_SYMBOL_GPL(rt2800_config_filter); void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags) { u32 reg; bool update_bssid = false; if (flags & CONFIG_UPDATE_TYPE) { /* * Enable synchronisation. */ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, conf->sync); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); if (conf->sync == TSF_SYNC_AP_NONE) { /* * Tune beacon queue transmit parameters for AP mode */ rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 0); rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 1); rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 0); rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); } else { rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 4); rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 2); rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 16); rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); } } if (flags & CONFIG_UPDATE_MAC) { if (flags & CONFIG_UPDATE_TYPE && conf->sync == TSF_SYNC_AP_NONE) { /* * The BSSID register has to be set to our own mac * address in AP mode. */ memcpy(conf->bssid, conf->mac, sizeof(conf->mac)); update_bssid = true; } if (!is_zero_ether_addr((const u8 *)conf->mac)) { reg = le32_to_cpu(conf->mac[1]); rt2x00_set_field32(®, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff); conf->mac[1] = cpu_to_le32(reg); } rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0, conf->mac, sizeof(conf->mac)); } if ((flags & CONFIG_UPDATE_BSSID) || update_bssid) { if (!is_zero_ether_addr((const u8 *)conf->bssid)) { reg = le32_to_cpu(conf->bssid[1]); rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 3); rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 7); conf->bssid[1] = cpu_to_le32(reg); } rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0, conf->bssid, sizeof(conf->bssid)); } } EXPORT_SYMBOL_GPL(rt2800_config_intf); static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp) { bool any_sta_nongf = !!(erp->ht_opmode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); u8 protection = erp->ht_opmode & IEEE80211_HT_OP_MODE_PROTECTION; u8 mm20_mode, mm40_mode, gf20_mode, gf40_mode; u16 mm20_rate, mm40_rate, gf20_rate, gf40_rate; u32 reg; /* default protection rate for HT20: OFDM 24M */ mm20_rate = gf20_rate = 0x4004; /* default protection rate for HT40: duplicate OFDM 24M */ mm40_rate = gf40_rate = 0x4084; switch (protection) { case IEEE80211_HT_OP_MODE_PROTECTION_NONE: /* * All STAs in this BSS are HT20/40 but there might be * STAs not supporting greenfield mode. * => Disable protection for HT transmissions. */ mm20_mode = mm40_mode = gf20_mode = gf40_mode = 0; break; case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: /* * All STAs in this BSS are HT20 or HT20/40 but there * might be STAs not supporting greenfield mode. * => Protect all HT40 transmissions. */ mm20_mode = gf20_mode = 0; mm40_mode = gf40_mode = 2; break; case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: /* * Nonmember protection: * According to 802.11n we _should_ protect all * HT transmissions (but we don't have to). * * But if cts_protection is enabled we _shall_ protect * all HT transmissions using a CCK rate. * * And if any station is non GF we _shall_ protect * GF transmissions. * * We decide to protect everything * -> fall through to mixed mode. */ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: /* * Legacy STAs are present * => Protect all HT transmissions. */ mm20_mode = mm40_mode = gf20_mode = gf40_mode = 2; /* * If erp protection is needed we have to protect HT * transmissions with CCK 11M long preamble. */ if (erp->cts_protection) { /* don't duplicate RTS/CTS in CCK mode */ mm20_rate = mm40_rate = 0x0003; gf20_rate = gf40_rate = 0x0003; } break; } /* check for STAs not supporting greenfield mode */ if (any_sta_nongf) gf20_mode = gf40_mode = 2; /* Update HT protection config */ rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, mm20_rate); rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, mm20_mode); rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, mm40_rate); rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, mm40_mode); rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, gf20_rate); rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, gf20_mode); rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, gf40_rate); rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, gf40_mode); rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); } void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed) { u32 reg; if (changed & BSS_CHANGED_ERP_PREAMBLE) { rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, !!erp->short_preamble); rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, !!erp->short_preamble); rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, erp->cts_protection ? 2 : 0); rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); } if (changed & BSS_CHANGED_BASIC_RATES) { rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, erp->basic_rates); rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); } if (changed & BSS_CHANGED_ERP_SLOT) { rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time); rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, erp->eifs); rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); } if (changed & BSS_CHANGED_BEACON_INT) { rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, erp->beacon_int * 16); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); } if (changed & BSS_CHANGED_HT) rt2800_config_ht_opmode(rt2x00dev, erp); } EXPORT_SYMBOL_GPL(rt2800_config_erp); static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 eeprom; u8 led_ctrl, led_g_mode, led_r_mode; rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { rt2x00_set_field32(®, GPIO_SWITCH_0, 1); rt2x00_set_field32(®, GPIO_SWITCH_1, 1); } else { rt2x00_set_field32(®, GPIO_SWITCH_0, 0); rt2x00_set_field32(®, GPIO_SWITCH_1, 0); } rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); rt2800_register_read(rt2x00dev, LED_CFG, ®); led_g_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 3 : 0; led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3; if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) || led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) { rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE); if (led_ctrl == 0 || led_ctrl > 0x40) { rt2x00_set_field32(®, LED_CFG_G_LED_MODE, led_g_mode); rt2x00_set_field32(®, LED_CFG_R_LED_MODE, led_r_mode); rt2800_register_write(rt2x00dev, LED_CFG, reg); } else { rt2800_mcu_request(rt2x00dev, MCU_BAND_SELECT, 0xff, (led_g_mode << 2) | led_r_mode, 1); } } } static void rt2800_set_ant_diversity(struct rt2x00_dev *rt2x00dev, enum antenna ant) { u32 reg; u8 eesk_pin = (ant == ANTENNA_A) ? 1 : 0; u8 gpio_bit3 = (ant == ANTENNA_A) ? 0 : 1; if (rt2x00_is_pci(rt2x00dev)) { rt2800_register_read(rt2x00dev, E2PROM_CSR, ®); rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, eesk_pin); rt2800_register_write(rt2x00dev, E2PROM_CSR, reg); } else if (rt2x00_is_usb(rt2x00dev)) rt2800_mcu_request(rt2x00dev, MCU_ANT_SELECT, 0xff, eesk_pin, 0); rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); rt2x00_set_field32(®, GPIO_CTRL_VAL3, gpio_bit3); rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); } void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r1; u8 r3; u16 eeprom; rt2800_bbp_read(rt2x00dev, 1, &r1); rt2800_bbp_read(rt2x00dev, 3, &r3); if (rt2x00_rt(rt2x00dev, RT3572) && test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) rt2800_config_3572bt_ant(rt2x00dev); /* * Configure the TX antenna. */ switch (ant->tx_chain_num) { case 1: rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); break; case 2: if (rt2x00_rt(rt2x00dev, RT3572) && test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1); else rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); break; case 3: rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); break; } /* * Configure the RX antenna. */ switch (ant->rx_chain_num) { case 1: if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT3390)) { rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY)) rt2800_set_ant_diversity(rt2x00dev, rt2x00dev->default_ant.rx); } rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); break; case 2: if (rt2x00_rt(rt2x00dev, RT3572) && test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { rt2x00_set_field8(&r3, BBP3_RX_ADC, 1); rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); rt2800_set_ant_diversity(rt2x00dev, ANTENNA_B); } else { rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1); } break; case 3: rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2); break; } rt2800_bbp_write(rt2x00dev, 3, r3); rt2800_bbp_write(rt2x00dev, 1, r1); } EXPORT_SYMBOL_GPL(rt2800_config_ant); static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u16 eeprom; short lna_gain; if (libconf->rf.channel <= 14) { rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG); } else if (libconf->rf.channel <= 64) { rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0); } else if (libconf->rf.channel <= 128) { rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1); } else { rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2); } rt2x00dev->lna_gain = lna_gain; } static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, struct channel_info *info) { rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); if (rt2x00dev->default_ant.tx_chain_num == 1) rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1); if (rt2x00dev->default_ant.rx_chain_num == 1) { rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1); rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); } else if (rt2x00dev->default_ant.rx_chain_num == 2) rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); if (rf->channel > 14) { /* * When TX power is below 0, we should increase it by 7 to * make it a positive value (Minimum value is -7). * However this means that values between 0 and 7 have * double meaning, and we should set a 7DBm boost flag. */ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST, (info->default_power1 >= 0)); if (info->default_power1 < 0) info->default_power1 += 7; rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->default_power1); rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST, (info->default_power2 >= 0)); if (info->default_power2 < 0) info->default_power2 += 7; rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->default_power2); } else { rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->default_power1); rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->default_power2); } rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf)); rt2800_rf_write(rt2x00dev, 1, rf->rf1); rt2800_rf_write(rt2x00dev, 2, rf->rf2); rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); rt2800_rf_write(rt2x00dev, 4, rf->rf4); udelay(200); rt2800_rf_write(rt2x00dev, 1, rf->rf1); rt2800_rf_write(rt2x00dev, 2, rf->rf2); rt2800_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); rt2800_rf_write(rt2x00dev, 4, rf->rf4); udelay(200); rt2800_rf_write(rt2x00dev, 1, rf->rf1); rt2800_rf_write(rt2x00dev, 2, rf->rf2); rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); rt2800_rf_write(rt2x00dev, 4, rf->rf4); } static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, struct channel_info *info) { struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; u8 rfcsr, calib_tx, calib_rx; rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR3_K, rf->rf3); rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1); rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2); rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); if (rt2x00_rt(rt2x00dev, RT3390)) { rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, rt2x00dev->default_ant.rx_chain_num == 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, rt2x00dev->default_ant.tx_chain_num == 1); } else { rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); switch (rt2x00dev->default_ant.tx_chain_num) { case 1: rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); /* fall through */ case 2: rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); break; } switch (rt2x00dev->default_ant.rx_chain_num) { case 1: rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); /* fall through */ case 2: rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); break; } } rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); msleep(1); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); if (rt2x00_rt(rt2x00dev, RT3390)) { calib_tx = conf_is_ht40(conf) ? 0x68 : 0x4f; calib_rx = conf_is_ht40(conf) ? 0x6f : 0x4f; } else { if (conf_is_ht40(conf)) { calib_tx = drv_data->calibration_bw40; calib_rx = drv_data->calibration_bw40; } else { calib_tx = drv_data->calibration_bw20; calib_rx = drv_data->calibration_bw20; } } rt2800_rfcsr_read(rt2x00dev, 24, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR24_TX_CALIB, calib_tx); rt2800_rfcsr_write(rt2x00dev, 24, rfcsr); rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR31_RX_CALIB, calib_rx); rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); msleep(1); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); } static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, struct channel_info *info) { struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; u8 rfcsr; u32 reg; if (rf->channel <= 14) { rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); } else { rt2800_bbp_write(rt2x00dev, 25, 0x09); rt2800_bbp_write(rt2x00dev, 26, 0xff); } rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); rt2800_rfcsr_write(rt2x00dev, 3, rf->rf3); rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); if (rf->channel <= 14) rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 2); else rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 1); rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); rt2800_rfcsr_read(rt2x00dev, 5, &rfcsr); if (rf->channel <= 14) rt2x00_set_field8(&rfcsr, RFCSR5_R1, 1); else rt2x00_set_field8(&rfcsr, RFCSR5_R1, 2); rt2800_rfcsr_write(rt2x00dev, 5, rfcsr); rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); if (rf->channel <= 14) { rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 3); rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1); } else { rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 7); rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, (info->default_power1 & 0x3) | ((info->default_power1 & 0xC) << 1)); } rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); if (rf->channel <= 14) { rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 3); rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2); } else { rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 7); rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, (info->default_power2 & 0x3) | ((info->default_power2 & 0xC) << 1)); } rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { if (rf->channel <= 14) { rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); } rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); } else { switch (rt2x00dev->default_ant.tx_chain_num) { case 1: rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); case 2: rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); break; } switch (rt2x00dev->default_ant.rx_chain_num) { case 1: rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); case 2: rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); break; } } rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); if (conf_is_ht40(conf)) { rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw40); rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw40); } else { rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw20); rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw20); } if (rf->channel <= 14) { rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); rt2800_rfcsr_write(rt2x00dev, 15, 0x53); rfcsr = 0x4c; rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, drv_data->txmixer_gain_24g); rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); rt2800_rfcsr_write(rt2x00dev, 17, 0x23); rt2800_rfcsr_write(rt2x00dev, 19, 0x93); rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); rt2800_rfcsr_write(rt2x00dev, 25, 0x15); rt2800_rfcsr_write(rt2x00dev, 26, 0x85); rt2800_rfcsr_write(rt2x00dev, 27, 0x00); rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); } else { rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR7_BIT2, 1); rt2x00_set_field8(&rfcsr, RFCSR7_BIT3, 0); rt2x00_set_field8(&rfcsr, RFCSR7_BIT4, 1); rt2x00_set_field8(&rfcsr, RFCSR7_BITS67, 0); rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); rt2800_rfcsr_write(rt2x00dev, 11, 0x00); rt2800_rfcsr_write(rt2x00dev, 15, 0x43); rfcsr = 0x7a; rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, drv_data->txmixer_gain_5g); rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); rt2800_rfcsr_write(rt2x00dev, 17, 0x23); if (rf->channel <= 64) { rt2800_rfcsr_write(rt2x00dev, 19, 0xb7); rt2800_rfcsr_write(rt2x00dev, 20, 0xf6); rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); } else if (rf->channel <= 128) { rt2800_rfcsr_write(rt2x00dev, 19, 0x74); rt2800_rfcsr_write(rt2x00dev, 20, 0xf4); rt2800_rfcsr_write(rt2x00dev, 25, 0x01); } else { rt2800_rfcsr_write(rt2x00dev, 19, 0x72); rt2800_rfcsr_write(rt2x00dev, 20, 0xf3); rt2800_rfcsr_write(rt2x00dev, 25, 0x01); } rt2800_rfcsr_write(rt2x00dev, 26, 0x87); rt2800_rfcsr_write(rt2x00dev, 27, 0x01); rt2800_rfcsr_write(rt2x00dev, 29, 0x9f); } rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); if (rf->channel <= 14) rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); else rt2x00_set_field32(®, GPIO_CTRL_VAL7, 0); rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); } #define POWER_BOUND 0x27 #define FREQ_OFFSET_BOUND 0x5f static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, struct channel_info *info) { u8 rfcsr; rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); if (info->default_power1 > POWER_BOUND) rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); else rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND) rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND); else rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset); rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); if (rf->channel <= 14) { if (rf->channel == 6) rt2800_bbp_write(rt2x00dev, 68, 0x0c); else rt2800_bbp_write(rt2x00dev, 68, 0x0b); if (rf->channel >= 1 && rf->channel <= 6) rt2800_bbp_write(rt2x00dev, 59, 0x0f); else if (rf->channel >= 7 && rf->channel <= 11) rt2800_bbp_write(rt2x00dev, 59, 0x0e); else if (rf->channel >= 12 && rf->channel <= 14) rt2800_bbp_write(rt2x00dev, 59, 0x0d); } } static void rt2800_config_channel_rf3322(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, struct channel_info *info) { u8 rfcsr; rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); rt2800_rfcsr_write(rt2x00dev, 11, 0x42); rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); rt2800_rfcsr_write(rt2x00dev, 13, 0x00); if (info->default_power1 > POWER_BOUND) rt2800_rfcsr_write(rt2x00dev, 47, POWER_BOUND); else rt2800_rfcsr_write(rt2x00dev, 47, info->default_power1); if (info->default_power2 > POWER_BOUND) rt2800_rfcsr_write(rt2x00dev, 48, POWER_BOUND); else rt2800_rfcsr_write(rt2x00dev, 48, info->default_power2); rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND) rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND); else rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset); rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); if ( rt2x00dev->default_ant.tx_chain_num == 2 ) rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); else rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); if ( rt2x00dev->default_ant.rx_chain_num == 2 ) rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); else rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); rt2800_rfcsr_write(rt2x00dev, 31, 80); } static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, struct channel_info *info) { u8 rfcsr; rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); if (info->default_power1 > POWER_BOUND) rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); else rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); if (info->default_power1 > POWER_BOUND) rt2x00_set_field8(&rfcsr, RFCSR50_TX, POWER_BOUND); else rt2x00_set_field8(&rfcsr, RFCSR50_TX, info->default_power2); rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); } rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); if (rt2x00_rt(rt2x00dev, RT5392)) { rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); } rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND) rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND); else rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset); rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); if (rf->channel <= 14) { int idx = rf->channel-1; if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { /* r55/r59 value array of channel 1~14 */ static const char r55_bt_rev[] = {0x83, 0x83, 0x83, 0x73, 0x73, 0x63, 0x53, 0x53, 0x53, 0x43, 0x43, 0x43, 0x43, 0x43}; static const char r59_bt_rev[] = {0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0b, 0x0a, 0x09, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}; rt2800_rfcsr_write(rt2x00dev, 55, r55_bt_rev[idx]); rt2800_rfcsr_write(rt2x00dev, 59, r59_bt_rev[idx]); } else { static const char r59_bt[] = {0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x89, 0x88, 0x88, 0x86, 0x85, 0x84}; rt2800_rfcsr_write(rt2x00dev, 59, r59_bt[idx]); } } else { if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { static const char r55_nonbt_rev[] = {0x23, 0x23, 0x23, 0x23, 0x13, 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; static const char r59_nonbt_rev[] = {0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x05, 0x04, 0x04}; rt2800_rfcsr_write(rt2x00dev, 55, r55_nonbt_rev[idx]); rt2800_rfcsr_write(rt2x00dev, 59, r59_nonbt_rev[idx]); } else if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { static const char r59_non_bt[] = {0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8d, 0x8a, 0x88, 0x88, 0x87, 0x87, 0x86}; rt2800_rfcsr_write(rt2x00dev, 59, r59_non_bt[idx]); } } } } static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, struct channel_info *info) { u32 reg; unsigned int tx_pin; u8 bbp, rfcsr; if (rf->channel <= 14) { info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1); info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2); } else { info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1); info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2); } switch (rt2x00dev->chip.rf) { case RF2020: case RF3020: case RF3021: case RF3022: case RF3320: rt2800_config_channel_rf3xxx(rt2x00dev, conf, rf, info); break; case RF3052: rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info); break; case RF3290: rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info); break; case RF3322: rt2800_config_channel_rf3322(rt2x00dev, conf, rf, info); break; case RF5360: case RF5370: case RF5372: case RF5390: case RF5392: rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info); break; default: rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); } if (rt2x00_rf(rt2x00dev, RF3290) || rt2x00_rf(rt2x00dev, RF3322) || rt2x00_rf(rt2x00dev, RF5360) || rt2x00_rf(rt2x00dev, RF5370) || rt2x00_rf(rt2x00dev, RF5372) || rt2x00_rf(rt2x00dev, RF5390) || rt2x00_rf(rt2x00dev, RF5392)) { rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, 0); rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, 0); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); } /* * Change BBP settings */ if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_bbp_write(rt2x00dev, 27, 0x0); rt2800_bbp_write(rt2x00dev, 62, 0x26 + rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 27, 0x20); rt2800_bbp_write(rt2x00dev, 62, 0x26 + rt2x00dev->lna_gain); } else { rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 86, 0); } if (rf->channel <= 14) { if (!rt2x00_rt(rt2x00dev, RT5390) && !rt2x00_rt(rt2x00dev, RT5392)) { if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 75, 0x46); } else { rt2800_bbp_write(rt2x00dev, 82, 0x84); rt2800_bbp_write(rt2x00dev, 75, 0x50); } } } else { if (rt2x00_rt(rt2x00dev, RT3572)) rt2800_bbp_write(rt2x00dev, 82, 0x94); else rt2800_bbp_write(rt2x00dev, 82, 0xf2); if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) rt2800_bbp_write(rt2x00dev, 75, 0x46); else rt2800_bbp_write(rt2x00dev, 75, 0x50); } rt2800_register_read(rt2x00dev, TX_BAND_CFG, ®); rt2x00_set_field32(®, TX_BAND_CFG_HT40_MINUS, conf_is_ht40_minus(conf)); rt2x00_set_field32(®, TX_BAND_CFG_A, rf->channel > 14); rt2x00_set_field32(®, TX_BAND_CFG_BG, rf->channel <= 14); rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg); if (rt2x00_rt(rt2x00dev, RT3572)) rt2800_rfcsr_write(rt2x00dev, 8, 0); tx_pin = 0; /* Turn on unused PA or LNA when not using 1T or 1R */ if (rt2x00dev->default_ant.tx_chain_num == 2) { rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, rf->channel > 14); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, rf->channel <= 14); } /* Turn on unused PA or LNA when not using 1T or 1R */ if (rt2x00dev->default_ant.rx_chain_num == 2) { rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); } rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); else rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, rf->channel <= 14); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14); rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); if (rt2x00_rt(rt2x00dev, RT3572)) rt2800_rfcsr_write(rt2x00dev, 8, 0x80); rt2800_bbp_read(rt2x00dev, 4, &bbp); rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); rt2800_bbp_write(rt2x00dev, 4, bbp); rt2800_bbp_read(rt2x00dev, 3, &bbp); rt2x00_set_field8(&bbp, BBP3_HT40_MINUS, conf_is_ht40_minus(conf)); rt2800_bbp_write(rt2x00dev, 3, bbp); if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { if (conf_is_ht40(conf)) { rt2800_bbp_write(rt2x00dev, 69, 0x1a); rt2800_bbp_write(rt2x00dev, 70, 0x0a); rt2800_bbp_write(rt2x00dev, 73, 0x16); } else { rt2800_bbp_write(rt2x00dev, 69, 0x16); rt2800_bbp_write(rt2x00dev, 70, 0x08); rt2800_bbp_write(rt2x00dev, 73, 0x11); } } msleep(1); /* * Clear channel statistic counters */ rt2800_register_read(rt2x00dev, CH_IDLE_STA, ®); rt2800_register_read(rt2x00dev, CH_BUSY_STA, ®); rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, ®); /* * Clear update flag */ if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_bbp_read(rt2x00dev, 49, &bbp); rt2x00_set_field8(&bbp, BBP49_UPDATE_FLAG, 0); rt2800_bbp_write(rt2x00dev, 49, bbp); } } static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) { u8 tssi_bounds[9]; u8 current_tssi; u16 eeprom; u8 step; int i; /* * Read TSSI boundaries for temperature compensation from * the EEPROM. * * Array idx 0 1 2 3 4 5 6 7 8 * Matching Delta value -4 -3 -2 -1 0 +1 +2 +3 +4 * Example TSSI bounds 0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00 */ if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom); tssi_bounds[0] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG1_MINUS4); tssi_bounds[1] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG1_MINUS3); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom); tssi_bounds[2] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG2_MINUS2); tssi_bounds[3] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG2_MINUS1); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom); tssi_bounds[4] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG3_REF); tssi_bounds[5] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG3_PLUS1); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom); tssi_bounds[6] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG4_PLUS2); tssi_bounds[7] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG4_PLUS3); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom); tssi_bounds[8] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG5_PLUS4); step = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG5_AGC_STEP); } else { rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom); tssi_bounds[0] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A1_MINUS4); tssi_bounds[1] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A1_MINUS3); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom); tssi_bounds[2] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A2_MINUS2); tssi_bounds[3] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A2_MINUS1); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom); tssi_bounds[4] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A3_REF); tssi_bounds[5] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A3_PLUS1); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom); tssi_bounds[6] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A4_PLUS2); tssi_bounds[7] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A4_PLUS3); rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom); tssi_bounds[8] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A5_PLUS4); step = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A5_AGC_STEP); } /* * Check if temperature compensation is supported. */ if (tssi_bounds[4] == 0xff) return 0; /* * Read current TSSI (BBP 49). */ rt2800_bbp_read(rt2x00dev, 49, ¤t_tssi); /* * Compare TSSI value (BBP49) with the compensation boundaries * from the EEPROM and increase or decrease tx power. */ for (i = 0; i <= 3; i++) { if (current_tssi > tssi_bounds[i]) break; } if (i == 4) { for (i = 8; i >= 5; i--) { if (current_tssi < tssi_bounds[i]) break; } } return (i - 4) * step; } static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev, enum ieee80211_band band) { u16 eeprom; u8 comp_en; u8 comp_type; int comp_value = 0; rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom); /* * HT40 compensation not required. */ if (eeprom == 0xffff || !test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) return 0; if (band == IEEE80211_BAND_2GHZ) { comp_en = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_DELTA_ENABLE_2G); if (comp_en) { comp_type = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_DELTA_TYPE_2G); comp_value = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_DELTA_VALUE_2G); if (!comp_type) comp_value = -comp_value; } } else { comp_en = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_DELTA_ENABLE_5G); if (comp_en) { comp_type = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_DELTA_TYPE_5G); comp_value = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_DELTA_VALUE_5G); if (!comp_type) comp_value = -comp_value; } } return comp_value; } static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, enum ieee80211_band band, int power_level, u8 txpower, int delta) { u32 reg; u16 eeprom; u8 criterion; u8 eirp_txpower; u8 eirp_txpower_criterion; u8 reg_limit; if (!((band == IEEE80211_BAND_5GHZ) && is_rate_b)) return txpower; if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) { /* * Check if eirp txpower exceed txpower_limit. * We use OFDM 6M as criterion and its eirp txpower * is stored at EEPROM_EIRP_MAX_TX_POWER. * .11b data rate need add additional 4dbm * when calculating eirp txpower. */ rt2800_register_read(rt2x00dev, TX_PWR_CFG_0, ®); criterion = rt2x00_get_field32(reg, TX_PWR_CFG_0_6MBS); rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); if (band == IEEE80211_BAND_2GHZ) eirp_txpower_criterion = rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ); else eirp_txpower_criterion = rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_5GHZ); eirp_txpower = eirp_txpower_criterion + (txpower - criterion) + (is_rate_b ? 4 : 0) + delta; reg_limit = (eirp_txpower > power_level) ? (eirp_txpower - power_level) : 0; } else reg_limit = 0; return txpower + delta - reg_limit; } static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, enum ieee80211_band band, int power_level) { u8 txpower; u16 eeprom; int i, is_rate_b; u32 reg; u8 r1; u32 offset; int delta; /* * Calculate HT40 compensation delta */ delta = rt2800_get_txpower_bw_comp(rt2x00dev, band); /* * calculate temperature compensation delta */ delta += rt2800_get_gain_calibration_delta(rt2x00dev); /* * set to normal bbp tx power control mode: +/- 0dBm */ rt2800_bbp_read(rt2x00dev, 1, &r1); rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, 0); rt2800_bbp_write(rt2x00dev, 1, r1); offset = TX_PWR_CFG_0; for (i = 0; i < EEPROM_TXPOWER_BYRATE_SIZE; i += 2) { /* just to be safe */ if (offset > TX_PWR_CFG_4) break; rt2800_register_read(rt2x00dev, offset, ®); /* read the next four txpower values */ rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i, &eeprom); is_rate_b = i ? 0 : 1; /* * TX_PWR_CFG_0: 1MBS, TX_PWR_CFG_1: 24MBS, * TX_PWR_CFG_2: MCS4, TX_PWR_CFG_3: MCS12, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE0, txpower); /* * TX_PWR_CFG_0: 2MBS, TX_PWR_CFG_1: 36MBS, * TX_PWR_CFG_2: MCS5, TX_PWR_CFG_3: MCS13, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE1, txpower); /* * TX_PWR_CFG_0: 5.5MBS, TX_PWR_CFG_1: 48MBS, * TX_PWR_CFG_2: MCS6, TX_PWR_CFG_3: MCS14, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE2, txpower); /* * TX_PWR_CFG_0: 11MBS, TX_PWR_CFG_1: 54MBS, * TX_PWR_CFG_2: MCS7, TX_PWR_CFG_3: MCS15, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE3, txpower); /* read the next four txpower values */ rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i + 1, &eeprom); is_rate_b = 0; /* * TX_PWR_CFG_0: 6MBS, TX_PWR_CFG_1: MCS0, * TX_PWR_CFG_2: MCS8, TX_PWR_CFG_3: unknown, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE4, txpower); /* * TX_PWR_CFG_0: 9MBS, TX_PWR_CFG_1: MCS1, * TX_PWR_CFG_2: MCS9, TX_PWR_CFG_3: unknown, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE5, txpower); /* * TX_PWR_CFG_0: 12MBS, TX_PWR_CFG_1: MCS2, * TX_PWR_CFG_2: MCS10, TX_PWR_CFG_3: unknown, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE6, txpower); /* * TX_PWR_CFG_0: 18MBS, TX_PWR_CFG_1: MCS3, * TX_PWR_CFG_2: MCS11, TX_PWR_CFG_3: unknown, * TX_PWR_CFG_4: unknown */ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, power_level, txpower, delta); rt2x00_set_field32(®, TX_PWR_CFG_RATE7, txpower); rt2800_register_write(rt2x00dev, offset, reg); /* next TX_PWR_CFG register */ offset += 4; } } void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev) { rt2800_config_txpower(rt2x00dev, rt2x00dev->curr_band, rt2x00dev->tx_power); } EXPORT_SYMBOL_GPL(rt2800_gain_calibration); void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) { u32 tx_pin; u8 rfcsr; /* * A voltage-controlled oscillator(VCO) is an electronic oscillator * designed to be controlled in oscillation frequency by a voltage * input. Maybe the temperature will affect the frequency of * oscillation to be shifted. The VCO calibration will be called * periodically to adjust the frequency to be precision. */ rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); tx_pin &= TX_PIN_CFG_PA_PE_DISABLE; rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); switch (rt2x00dev->chip.rf) { case RF2020: case RF3020: case RF3021: case RF3022: case RF3320: case RF3052: rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); break; case RF3290: case RF5360: case RF5370: case RF5372: case RF5390: case RF5392: rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); break; default: return; } mdelay(1); rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); if (rt2x00dev->rf_channel <= 14) { switch (rt2x00dev->default_ant.tx_chain_num) { case 3: rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, 1); /* fall through */ case 2: rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1); /* fall through */ case 1: default: rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); break; } } else { switch (rt2x00dev->default_ant.tx_chain_num) { case 3: rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, 1); /* fall through */ case 2: rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1); /* fall through */ case 1: default: rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, 1); break; } } rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); } EXPORT_SYMBOL_GPL(rt2800_vco_calibration); static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u32 reg; rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, libconf->conf->short_frame_max_tx_count); rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, libconf->conf->long_frame_max_tx_count); rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); } static void rt2800_config_ps(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { enum dev_state state = (libconf->conf->flags & IEEE80211_CONF_PS) ? STATE_SLEEP : STATE_AWAKE; u32 reg; if (state == STATE_SLEEP) { rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0); rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5); rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, libconf->conf->listen_interval - 1); rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 1); rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } else { rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0); rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0); rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 0); rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } } void rt2800_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { /* Always recalculate LNA gain before changing configuration */ rt2800_config_lna_gain(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_CHANNEL) { rt2800_config_channel(rt2x00dev, libconf->conf, &libconf->rf, &libconf->channel); rt2800_config_txpower(rt2x00dev, libconf->conf->channel->band, libconf->conf->power_level); } if (flags & IEEE80211_CONF_CHANGE_POWER) rt2800_config_txpower(rt2x00dev, libconf->conf->channel->band, libconf->conf->power_level); if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) rt2800_config_retry_limit(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_PS) rt2800_config_ps(rt2x00dev, libconf); } EXPORT_SYMBOL_GPL(rt2800_config); /* * Link tuning */ void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { u32 reg; /* * Update FCS error count from register. */ rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR); } EXPORT_SYMBOL_GPL(rt2800_link_stats); static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) { if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) return 0x1c + (2 * rt2x00dev->lna_gain); else return 0x2e + rt2x00dev->lna_gain; } if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) return 0x32 + (rt2x00dev->lna_gain * 5) / 3; else return 0x3a + (rt2x00dev->lna_gain * 5) / 3; } static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, u8 vgc_level) { if (qual->vgc_level != vgc_level) { rt2800_bbp_write(rt2x00dev, 66, vgc_level); qual->vgc_level = vgc_level; qual->vgc_level_reg = vgc_level; } } void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { rt2800_set_vgc(rt2x00dev, qual, rt2800_get_default_vgc(rt2x00dev)); } EXPORT_SYMBOL_GPL(rt2800_reset_tuner); void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count) { if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) return; /* * When RSSI is better then -80 increase VGC level with 0x10 */ rt2800_set_vgc(rt2x00dev, qual, rt2800_get_default_vgc(rt2x00dev) + ((qual->rssi > -80) * 0x10)); } EXPORT_SYMBOL_GPL(rt2800_link_tuner); /* * Initialization functions. */ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 eeprom; unsigned int i; int ret; rt2800_disable_wpdma(rt2x00dev); ret = rt2800_drv_init_registers(rt2x00dev); if (ret) return ret; rt2800_register_read(rt2x00dev, BCN_OFFSET0, ®); rt2x00_set_field32(®, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */ rt2x00_set_field32(®, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */ rt2x00_set_field32(®, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */ rt2x00_set_field32(®, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */ rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg); rt2800_register_read(rt2x00dev, BCN_OFFSET1, ®); rt2x00_set_field32(®, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */ rt2x00_set_field32(®, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */ rt2x00_set_field32(®, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */ rt2x00_set_field32(®, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */ rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg); rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 1600); rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, 0); rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); rt2x00_set_field32(®, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); rt2800_config_filter(rt2x00dev, FIF_ALLMULTI); rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, 9); rt2x00_set_field32(®, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2); rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); if (rt2x00_rt(rt2x00dev, RT3290)) { rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); if (rt2x00_get_field32(reg, WLAN_EN) == 1) { rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 1); rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); } rt2800_register_read(rt2x00dev, CMB_CTRL, ®); if (!(rt2x00_get_field32(reg, LDO0_EN) == 1)) { rt2x00_set_field32(®, LDO0_EN, 1); rt2x00_set_field32(®, LDO_BGSEL, 3); rt2800_register_write(rt2x00dev, CMB_CTRL, reg); } rt2800_register_read(rt2x00dev, OSC_CTRL, ®); rt2x00_set_field32(®, OSC_ROSC_EN, 1); rt2x00_set_field32(®, OSC_CAL_REQ, 1); rt2x00_set_field32(®, OSC_REF_CYCLE, 0x27); rt2800_register_write(rt2x00dev, OSC_CTRL, reg); rt2800_register_read(rt2x00dev, COEX_CFG0, ®); rt2x00_set_field32(®, COEX_CFG_ANT, 0x5e); rt2800_register_write(rt2x00dev, COEX_CFG0, reg); rt2800_register_read(rt2x00dev, COEX_CFG2, ®); rt2x00_set_field32(®, BT_COEX_CFG1, 0x00); rt2x00_set_field32(®, BT_COEX_CFG0, 0x17); rt2x00_set_field32(®, WL_COEX_CFG1, 0x93); rt2x00_set_field32(®, WL_COEX_CFG0, 0x7f); rt2800_register_write(rt2x00dev, COEX_CFG2, reg); rt2800_register_read(rt2x00dev, PLL_CTRL, ®); rt2x00_set_field32(®, PLL_CONTROL, 1); rt2800_register_write(rt2x00dev, PLL_CTRL, reg); } if (rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3390)) { if (rt2x00_rt(rt2x00dev, RT3290)) rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); else rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x0000002c); else rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x0000000f); } else { rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); } } else if (rt2x00_rt(rt2x00dev, RT3070)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x0000002c); } else { rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); } } else if (rt2800_is_305x_soc(rt2x00dev)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000030); } else if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); } else if (rt2x00_rt(rt2x00dev, RT3572)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); } else if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); } else { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); } rt2800_register_read(rt2x00dev, TX_LINK_CFG, ®); rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32); rt2x00_set_field32(®, TX_LINK_CFG_MFB_ENABLE, 0); rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0); rt2x00_set_field32(®, TX_LINK_CFG_TX_MRQ_EN, 0); rt2x00_set_field32(®, TX_LINK_CFG_TX_RDG_EN, 0); rt2x00_set_field32(®, TX_LINK_CFG_TX_CF_ACK_EN, 1); rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB, 0); rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFS, 0); rt2800_register_write(rt2x00dev, TX_LINK_CFG, reg); rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); rt2x00_set_field32(®, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9); rt2x00_set_field32(®, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 32); rt2x00_set_field32(®, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10); rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); rt2800_register_read(rt2x00dev, MAX_LEN_CFG, ®); rt2x00_set_field32(®, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE); if (rt2x00_rt_rev_gte(rt2x00dev, RT2872, REV_RT2872E) || rt2x00_rt(rt2x00dev, RT2883) || rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070E)) rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 2); else rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 1); rt2x00_set_field32(®, MAX_LEN_CFG_MIN_PSDU, 0); rt2x00_set_field32(®, MAX_LEN_CFG_MIN_MPDU, 0); rt2800_register_write(rt2x00dev, MAX_LEN_CFG, reg); rt2800_register_read(rt2x00dev, LED_CFG, ®); rt2x00_set_field32(®, LED_CFG_ON_PERIOD, 70); rt2x00_set_field32(®, LED_CFG_OFF_PERIOD, 30); rt2x00_set_field32(®, LED_CFG_SLOW_BLINK_PERIOD, 3); rt2x00_set_field32(®, LED_CFG_R_LED_MODE, 3); rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 3); rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, 3); rt2x00_set_field32(®, LED_CFG_LED_POLAR, 1); rt2800_register_write(rt2x00dev, LED_CFG, reg); rt2800_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f); rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, 15); rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, 31); rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_THRE, 2000); rt2x00_set_field32(®, TX_RTY_CFG_NON_AGG_RTY_MODE, 0); rt2x00_set_field32(®, TX_RTY_CFG_AGG_RTY_MODE, 0); rt2x00_set_field32(®, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1); rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); rt2x00_set_field32(®, AUTO_RSP_CFG_AUTORESPONDER, 1); rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, 1); rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 0); rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MREF, 0); rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, 1); rt2x00_set_field32(®, AUTO_RSP_CFG_DUAL_CTS_EN, 0); rt2x00_set_field32(®, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0); rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_RATE, 3); rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_CTRL, 0); rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_NAV_SHORT, 1); rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1); rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 0); rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1); rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 0); rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, 1); rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_RATE, 3); rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, 0); rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_NAV_SHORT, 1); rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1); rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 0); rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1); rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 0); rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, 1); rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, 0x4004); rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 0); rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_NAV_SHORT, 1); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0); rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, 0); rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, 0x4084); rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 0); rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_NAV_SHORT, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1); rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, 0); rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, 0x4004); rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 0); rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_NAV_SHORT, 1); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0); rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, 0); rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, 0x4084); rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 0); rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_NAV_SHORT, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1); rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, 0); rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); if (rt2x00_is_usb(rt2x00dev)) { rt2800_register_write(rt2x00dev, PBF_CFG, 0xf40006); rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3); rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_BIG_ENDIAN, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_HDR_SEG_LEN, 0); rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); } /* * The legacy driver also sets TXOP_CTRL_CFG_RESERVED_TRUN_EN to 1 * although it is reserved. */ rt2800_register_read(rt2x00dev, TXOP_CTRL_CFG, ®); rt2x00_set_field32(®, TXOP_CTRL_CFG_TIMEOUT_TRUN_EN, 1); rt2x00_set_field32(®, TXOP_CTRL_CFG_AC_TRUN_EN, 1); rt2x00_set_field32(®, TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN, 1); rt2x00_set_field32(®, TXOP_CTRL_CFG_USER_MODE_TRUN_EN, 1); rt2x00_set_field32(®, TXOP_CTRL_CFG_MIMO_PS_TRUN_EN, 1); rt2x00_set_field32(®, TXOP_CTRL_CFG_RESERVED_TRUN_EN, 1); rt2x00_set_field32(®, TXOP_CTRL_CFG_LSIG_TXOP_EN, 0); rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_EN, 0); rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_DLY, 88); rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CWMIN, 0); rt2800_register_write(rt2x00dev, TXOP_CTRL_CFG, reg); rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, 0x00000002); rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); rt2x00_set_field32(®, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32); rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, IEEE80211_MAX_RTS_THRESHOLD); rt2x00_set_field32(®, TX_RTS_CFG_RTS_FBK_EN, 0); rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); rt2800_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca); /* * Usually the CCK SIFS time should be set to 10 and the OFDM SIFS * time should be set to 16. However, the original Ralink driver uses * 16 for both and indeed using a value of 10 for CCK SIFS results in * connection problems with 11g + CTS protection. Hence, use the same * defaults as the Ralink driver: 16 for both, CCK and OFDM SIFS. */ rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); rt2x00_set_field32(®, XIFS_TIME_CFG_CCKM_SIFS_TIME, 16); rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_SIFS_TIME, 16); rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4); rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, 314); rt2x00_set_field32(®, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1); rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); /* * ASIC will keep garbage value after boot, clear encryption keys. */ for (i = 0; i < 4; i++) rt2800_register_write(rt2x00dev, SHARED_KEY_MODE_ENTRY(i), 0); for (i = 0; i < 256; i++) { rt2800_config_wcid(rt2x00dev, NULL, i); rt2800_delete_wcid_attr(rt2x00dev, i); rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); } /* * Clear all beacons */ rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE0); rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE1); rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE2); rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE3); rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE4); rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE5); rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE6); rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE7); if (rt2x00_is_usb(rt2x00dev)) { rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 30); rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); } else if (rt2x00_is_pcie(rt2x00dev)) { rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 125); rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); } rt2800_register_read(rt2x00dev, HT_FBK_CFG0, ®); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS0FBK, 0); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS1FBK, 0); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS2FBK, 1); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS3FBK, 2); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS4FBK, 3); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS5FBK, 4); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS6FBK, 5); rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS7FBK, 6); rt2800_register_write(rt2x00dev, HT_FBK_CFG0, reg); rt2800_register_read(rt2x00dev, HT_FBK_CFG1, ®); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS8FBK, 8); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS9FBK, 8); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS10FBK, 9); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS11FBK, 10); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS12FBK, 11); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS13FBK, 12); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS14FBK, 13); rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS15FBK, 14); rt2800_register_write(rt2x00dev, HT_FBK_CFG1, reg); rt2800_register_read(rt2x00dev, LG_FBK_CFG0, ®); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS0FBK, 8); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS1FBK, 8); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 9); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS3FBK, 10); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS4FBK, 11); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS5FBK, 12); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS6FBK, 13); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS7FBK, 14); rt2800_register_write(rt2x00dev, LG_FBK_CFG0, reg); rt2800_register_read(rt2x00dev, LG_FBK_CFG1, ®); rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS0FBK, 0); rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS1FBK, 0); rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS2FBK, 1); rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS3FBK, 2); rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg); /* * Do not force the BA window size, we use the TXWI to set it */ rt2800_register_read(rt2x00dev, AMPDU_BA_WINSIZE, ®); rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE, 0); rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE, 0); rt2800_register_write(rt2x00dev, AMPDU_BA_WINSIZE, reg); /* * We must clear the error counters. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); rt2800_register_read(rt2x00dev, RX_STA_CNT1, ®); rt2800_register_read(rt2x00dev, RX_STA_CNT2, ®); rt2800_register_read(rt2x00dev, TX_STA_CNT0, ®); rt2800_register_read(rt2x00dev, TX_STA_CNT1, ®); rt2800_register_read(rt2x00dev, TX_STA_CNT2, ®); /* * Setup leadtime for pre tbtt interrupt to 6ms */ rt2800_register_read(rt2x00dev, INT_TIMER_CFG, ®); rt2x00_set_field32(®, INT_TIMER_CFG_PRE_TBTT_TIMER, 6 << 4); rt2800_register_write(rt2x00dev, INT_TIMER_CFG, reg); /* * Set up channel statistics timer */ rt2800_register_read(rt2x00dev, CH_TIME_CFG, ®); rt2x00_set_field32(®, CH_TIME_CFG_EIFS_BUSY, 1); rt2x00_set_field32(®, CH_TIME_CFG_NAV_BUSY, 1); rt2x00_set_field32(®, CH_TIME_CFG_RX_BUSY, 1); rt2x00_set_field32(®, CH_TIME_CFG_TX_BUSY, 1); rt2x00_set_field32(®, CH_TIME_CFG_TMR_EN, 1); rt2800_register_write(rt2x00dev, CH_TIME_CFG, reg); return 0; } static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u32 reg; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2800_register_read(rt2x00dev, MAC_STATUS_CFG, ®); if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY)) return 0; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "BBP/RF register access failed, aborting.\n"); return -EACCES; } static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u8 value; /* * BBP was enabled after firmware was loaded, * but we need to reactivate it now. */ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); msleep(1); for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2800_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) return 0; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); return -EACCES; } static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; u16 eeprom; u8 reg_id; u8 value; if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) || rt2800_wait_bbp_ready(rt2x00dev))) return -EACCES; if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_bbp_write(rt2x00dev, 3, 0x00); rt2800_bbp_write(rt2x00dev, 4, 0x50); } if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_read(rt2x00dev, 4, &value); rt2x00_set_field8(&value, BBP4_MAC_IF_CTRL, 1); rt2800_bbp_write(rt2x00dev, 4, value); } if (rt2800_is_305x_soc(rt2x00dev) || rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 31, 0x08); if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 47, 0x48); rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 68, 0x0b); if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { rt2800_bbp_write(rt2x00dev, 69, 0x16); rt2800_bbp_write(rt2x00dev, 73, 0x12); } else if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x13); rt2800_bbp_write(rt2x00dev, 75, 0x46); rt2800_bbp_write(rt2x00dev, 76, 0x28); if (rt2x00_rt(rt2x00dev, RT3290)) rt2800_bbp_write(rt2x00dev, 77, 0x58); else rt2800_bbp_write(rt2x00dev, 77, 0x59); } else { rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x10); } rt2800_bbp_write(rt2x00dev, 70, 0x0a); if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 79, 0x13); rt2800_bbp_write(rt2x00dev, 80, 0x05); rt2800_bbp_write(rt2x00dev, 81, 0x33); } else if (rt2800_is_305x_soc(rt2x00dev)) { rt2800_bbp_write(rt2x00dev, 78, 0x0e); rt2800_bbp_write(rt2x00dev, 80, 0x08); } else if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_bbp_write(rt2x00dev, 78, 0x0e); rt2800_bbp_write(rt2x00dev, 80, 0x08); rt2800_bbp_write(rt2x00dev, 81, 0x37); } else { rt2800_bbp_write(rt2x00dev, 81, 0x37); } if (rt2x00_rt(rt2x00dev, RT3290)) { rt2800_bbp_write(rt2x00dev, 74, 0x0b); rt2800_bbp_write(rt2x00dev, 79, 0x18); rt2800_bbp_write(rt2x00dev, 80, 0x09); rt2800_bbp_write(rt2x00dev, 81, 0x33); } rt2800_bbp_write(rt2x00dev, 82, 0x62); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 83, 0x7a); else rt2800_bbp_write(rt2x00dev, 83, 0x6a); if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) rt2800_bbp_write(rt2x00dev, 84, 0x19); else if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 84, 0x9a); else rt2800_bbp_write(rt2x00dev, 84, 0x99); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 86, 0x38); else rt2800_bbp_write(rt2x00dev, 86, 0x00); if (rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 88, 0x90); rt2800_bbp_write(rt2x00dev, 91, 0x04); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 92, 0x02); else rt2800_bbp_write(rt2x00dev, 92, 0x00); if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 95, 0x9a); rt2800_bbp_write(rt2x00dev, 98, 0x12); } if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) || rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392) || rt2800_is_305x_soc(rt2x00dev)) rt2800_bbp_write(rt2x00dev, 103, 0xc0); else rt2800_bbp_write(rt2x00dev, 103, 0x00); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 104, 0x92); if (rt2800_is_305x_soc(rt2x00dev)) rt2800_bbp_write(rt2x00dev, 105, 0x01); else if (rt2x00_rt(rt2x00dev, RT3290)) rt2800_bbp_write(rt2x00dev, 105, 0x1c); else if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 105, 0x34); else if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 105, 0x3c); else rt2800_bbp_write(rt2x00dev, 105, 0x05); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390)) rt2800_bbp_write(rt2x00dev, 106, 0x03); else if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 106, 0x05); else if (rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 106, 0x12); else rt2800_bbp_write(rt2x00dev, 106, 0x35); if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 120, 0x50); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 128, 0x12); if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 134, 0xd0); rt2800_bbp_write(rt2x00dev, 135, 0xf6); } if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 137, 0x0f); if (rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_read(rt2x00dev, 138, &value); rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) value |= 0x20; if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) value &= ~0x02; rt2800_bbp_write(rt2x00dev, 138, value); } if (rt2x00_rt(rt2x00dev, RT3290)) { rt2800_bbp_write(rt2x00dev, 67, 0x24); rt2800_bbp_write(rt2x00dev, 143, 0x04); rt2800_bbp_write(rt2x00dev, 142, 0x99); rt2800_bbp_write(rt2x00dev, 150, 0x30); rt2800_bbp_write(rt2x00dev, 151, 0x2e); rt2800_bbp_write(rt2x00dev, 152, 0x20); rt2800_bbp_write(rt2x00dev, 153, 0x34); rt2800_bbp_write(rt2x00dev, 154, 0x40); rt2800_bbp_write(rt2x00dev, 155, 0x3b); rt2800_bbp_write(rt2x00dev, 253, 0x04); rt2800_bbp_read(rt2x00dev, 47, &value); rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); rt2800_bbp_write(rt2x00dev, 47, value); /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ rt2800_bbp_read(rt2x00dev, 3, &value); rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); rt2800_bbp_write(rt2x00dev, 3, value); } if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_bbp_write(rt2x00dev, 163, 0xbd); /* Set ITxBF timeout to 0x9c40=1000msec */ rt2800_bbp_write(rt2x00dev, 179, 0x02); rt2800_bbp_write(rt2x00dev, 180, 0x00); rt2800_bbp_write(rt2x00dev, 182, 0x40); rt2800_bbp_write(rt2x00dev, 180, 0x01); rt2800_bbp_write(rt2x00dev, 182, 0x9c); rt2800_bbp_write(rt2x00dev, 179, 0x00); /* Reprogram the inband interface to put right values in RXWI */ rt2800_bbp_write(rt2x00dev, 142, 0x04); rt2800_bbp_write(rt2x00dev, 143, 0x3b); rt2800_bbp_write(rt2x00dev, 142, 0x06); rt2800_bbp_write(rt2x00dev, 143, 0xa0); rt2800_bbp_write(rt2x00dev, 142, 0x07); rt2800_bbp_write(rt2x00dev, 143, 0xa1); rt2800_bbp_write(rt2x00dev, 142, 0x08); rt2800_bbp_write(rt2x00dev, 143, 0xa2); rt2800_bbp_write(rt2x00dev, 148, 0xc8); } if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { int ant, div_mode; rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); ant = (div_mode == 3) ? 1 : 0; /* check if this is a Bluetooth combo card */ if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { u32 reg; rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); if (ant == 0) rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); else if (ant == 1) rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); } /* This chip has hardware antenna diversity*/ if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ } rt2800_bbp_read(rt2x00dev, 152, &value); if (ant == 0) rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); else rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); rt2800_bbp_write(rt2x00dev, 152, value); /* Init frequency calibration */ rt2800_bbp_write(rt2x00dev, 142, 1); rt2800_bbp_write(rt2x00dev, 143, 57); } for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); rt2800_bbp_write(rt2x00dev, reg_id, value); } } return 0; } static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev, bool bw40, u8 rfcsr24, u8 filter_target) { unsigned int i; u8 bbp; u8 rfcsr; u8 passband; u8 stopband; u8 overtuned = 0; rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); rt2800_bbp_read(rt2x00dev, 4, &bbp); rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40); rt2800_bbp_write(rt2x00dev, 4, bbp); rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR31_RX_H20M, bw40); rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1); rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); /* * Set power & frequency of passband test tone */ rt2800_bbp_write(rt2x00dev, 24, 0); for (i = 0; i < 100; i++) { rt2800_bbp_write(rt2x00dev, 25, 0x90); msleep(1); rt2800_bbp_read(rt2x00dev, 55, &passband); if (passband) break; } /* * Set power & frequency of stopband test tone */ rt2800_bbp_write(rt2x00dev, 24, 0x06); for (i = 0; i < 100; i++) { rt2800_bbp_write(rt2x00dev, 25, 0x90); msleep(1); rt2800_bbp_read(rt2x00dev, 55, &stopband); if ((passband - stopband) <= filter_target) { rfcsr24++; overtuned += ((passband - stopband) == filter_target); } else break; rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); } rfcsr24 -= !!overtuned; rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); return rfcsr24; } static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) { struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; u8 rfcsr; u8 bbp; u32 reg; u16 eeprom; if (!rt2x00_rt(rt2x00dev, RT3070) && !rt2x00_rt(rt2x00dev, RT3071) && !rt2x00_rt(rt2x00dev, RT3090) && !rt2x00_rt(rt2x00dev, RT3290) && !rt2x00_rt(rt2x00dev, RT3352) && !rt2x00_rt(rt2x00dev, RT3390) && !rt2x00_rt(rt2x00dev, RT3572) && !rt2x00_rt(rt2x00dev, RT5390) && !rt2x00_rt(rt2x00dev, RT5392) && !rt2800_is_305x_soc(rt2x00dev)) return 0; /* * Init RF calibration. */ if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1); rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); msleep(1); rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 0); rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); } else { rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); msleep(1); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); } if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090)) { rt2800_rfcsr_write(rt2x00dev, 4, 0x40); rt2800_rfcsr_write(rt2x00dev, 5, 0x03); rt2800_rfcsr_write(rt2x00dev, 6, 0x02); rt2800_rfcsr_write(rt2x00dev, 7, 0x60); rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); rt2800_rfcsr_write(rt2x00dev, 10, 0x41); rt2800_rfcsr_write(rt2x00dev, 11, 0x21); rt2800_rfcsr_write(rt2x00dev, 12, 0x7b); rt2800_rfcsr_write(rt2x00dev, 14, 0x90); rt2800_rfcsr_write(rt2x00dev, 15, 0x58); rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); rt2800_rfcsr_write(rt2x00dev, 17, 0x92); rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); rt2800_rfcsr_write(rt2x00dev, 19, 0x02); rt2800_rfcsr_write(rt2x00dev, 20, 0xba); rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); rt2800_rfcsr_write(rt2x00dev, 24, 0x16); rt2800_rfcsr_write(rt2x00dev, 25, 0x01); rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); } else if (rt2x00_rt(rt2x00dev, RT3290)) { rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); rt2800_rfcsr_write(rt2x00dev, 2, 0x80); rt2800_rfcsr_write(rt2x00dev, 3, 0x08); rt2800_rfcsr_write(rt2x00dev, 4, 0x00); rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); rt2800_rfcsr_write(rt2x00dev, 8, 0xf3); rt2800_rfcsr_write(rt2x00dev, 9, 0x02); rt2800_rfcsr_write(rt2x00dev, 10, 0x53); rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); rt2800_rfcsr_write(rt2x00dev, 12, 0x46); rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); rt2800_rfcsr_write(rt2x00dev, 18, 0x02); rt2800_rfcsr_write(rt2x00dev, 22, 0x20); rt2800_rfcsr_write(rt2x00dev, 25, 0x83); rt2800_rfcsr_write(rt2x00dev, 26, 0x82); rt2800_rfcsr_write(rt2x00dev, 27, 0x09); rt2800_rfcsr_write(rt2x00dev, 29, 0x10); rt2800_rfcsr_write(rt2x00dev, 30, 0x10); rt2800_rfcsr_write(rt2x00dev, 31, 0x80); rt2800_rfcsr_write(rt2x00dev, 32, 0x80); rt2800_rfcsr_write(rt2x00dev, 33, 0x00); rt2800_rfcsr_write(rt2x00dev, 34, 0x05); rt2800_rfcsr_write(rt2x00dev, 35, 0x12); rt2800_rfcsr_write(rt2x00dev, 36, 0x00); rt2800_rfcsr_write(rt2x00dev, 38, 0x85); rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); rt2800_rfcsr_write(rt2x00dev, 43, 0x7b); rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); rt2800_rfcsr_write(rt2x00dev, 46, 0x73); rt2800_rfcsr_write(rt2x00dev, 47, 0x00); rt2800_rfcsr_write(rt2x00dev, 48, 0x10); rt2800_rfcsr_write(rt2x00dev, 49, 0x98); rt2800_rfcsr_write(rt2x00dev, 52, 0x38); rt2800_rfcsr_write(rt2x00dev, 53, 0x00); rt2800_rfcsr_write(rt2x00dev, 54, 0x78); rt2800_rfcsr_write(rt2x00dev, 55, 0x43); rt2800_rfcsr_write(rt2x00dev, 56, 0x02); rt2800_rfcsr_write(rt2x00dev, 57, 0x80); rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); rt2800_rfcsr_write(rt2x00dev, 59, 0x09); rt2800_rfcsr_write(rt2x00dev, 60, 0x45); rt2800_rfcsr_write(rt2x00dev, 61, 0xc1); } else if (rt2x00_rt(rt2x00dev, RT3390)) { rt2800_rfcsr_write(rt2x00dev, 0, 0xa0); rt2800_rfcsr_write(rt2x00dev, 1, 0xe1); rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); rt2800_rfcsr_write(rt2x00dev, 3, 0x62); rt2800_rfcsr_write(rt2x00dev, 4, 0x40); rt2800_rfcsr_write(rt2x00dev, 5, 0x8b); rt2800_rfcsr_write(rt2x00dev, 6, 0x42); rt2800_rfcsr_write(rt2x00dev, 7, 0x34); rt2800_rfcsr_write(rt2x00dev, 8, 0x00); rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); rt2800_rfcsr_write(rt2x00dev, 10, 0x61); rt2800_rfcsr_write(rt2x00dev, 11, 0x21); rt2800_rfcsr_write(rt2x00dev, 12, 0x3b); rt2800_rfcsr_write(rt2x00dev, 13, 0xe0); rt2800_rfcsr_write(rt2x00dev, 14, 0x90); rt2800_rfcsr_write(rt2x00dev, 15, 0x53); rt2800_rfcsr_write(rt2x00dev, 16, 0xe0); rt2800_rfcsr_write(rt2x00dev, 17, 0x94); rt2800_rfcsr_write(rt2x00dev, 18, 0x5c); rt2800_rfcsr_write(rt2x00dev, 19, 0x4a); rt2800_rfcsr_write(rt2x00dev, 20, 0xb2); rt2800_rfcsr_write(rt2x00dev, 21, 0xf6); rt2800_rfcsr_write(rt2x00dev, 22, 0x00); rt2800_rfcsr_write(rt2x00dev, 23, 0x14); rt2800_rfcsr_write(rt2x00dev, 24, 0x08); rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); rt2800_rfcsr_write(rt2x00dev, 26, 0x85); rt2800_rfcsr_write(rt2x00dev, 27, 0x00); rt2800_rfcsr_write(rt2x00dev, 28, 0x41); rt2800_rfcsr_write(rt2x00dev, 29, 0x8f); rt2800_rfcsr_write(rt2x00dev, 30, 0x20); rt2800_rfcsr_write(rt2x00dev, 31, 0x0f); } else if (rt2x00_rt(rt2x00dev, RT3572)) { rt2800_rfcsr_write(rt2x00dev, 0, 0x70); rt2800_rfcsr_write(rt2x00dev, 1, 0x81); rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); rt2800_rfcsr_write(rt2x00dev, 3, 0x02); rt2800_rfcsr_write(rt2x00dev, 4, 0x4c); rt2800_rfcsr_write(rt2x00dev, 5, 0x05); rt2800_rfcsr_write(rt2x00dev, 6, 0x4a); rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); rt2800_rfcsr_write(rt2x00dev, 12, 0x70); rt2800_rfcsr_write(rt2x00dev, 13, 0x65); rt2800_rfcsr_write(rt2x00dev, 14, 0xa0); rt2800_rfcsr_write(rt2x00dev, 15, 0x53); rt2800_rfcsr_write(rt2x00dev, 16, 0x4c); rt2800_rfcsr_write(rt2x00dev, 17, 0x23); rt2800_rfcsr_write(rt2x00dev, 18, 0xac); rt2800_rfcsr_write(rt2x00dev, 19, 0x93); rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); rt2800_rfcsr_write(rt2x00dev, 21, 0xd0); rt2800_rfcsr_write(rt2x00dev, 22, 0x00); rt2800_rfcsr_write(rt2x00dev, 23, 0x3c); rt2800_rfcsr_write(rt2x00dev, 24, 0x16); rt2800_rfcsr_write(rt2x00dev, 25, 0x15); rt2800_rfcsr_write(rt2x00dev, 26, 0x85); rt2800_rfcsr_write(rt2x00dev, 27, 0x00); rt2800_rfcsr_write(rt2x00dev, 28, 0x00); rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); rt2800_rfcsr_write(rt2x00dev, 30, 0x09); rt2800_rfcsr_write(rt2x00dev, 31, 0x10); } else if (rt2800_is_305x_soc(rt2x00dev)) { rt2800_rfcsr_write(rt2x00dev, 0, 0x50); rt2800_rfcsr_write(rt2x00dev, 1, 0x01); rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); rt2800_rfcsr_write(rt2x00dev, 3, 0x75); rt2800_rfcsr_write(rt2x00dev, 4, 0x40); rt2800_rfcsr_write(rt2x00dev, 5, 0x03); rt2800_rfcsr_write(rt2x00dev, 6, 0x02); rt2800_rfcsr_write(rt2x00dev, 7, 0x50); rt2800_rfcsr_write(rt2x00dev, 8, 0x39); rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); rt2800_rfcsr_write(rt2x00dev, 10, 0x60); rt2800_rfcsr_write(rt2x00dev, 11, 0x21); rt2800_rfcsr_write(rt2x00dev, 12, 0x75); rt2800_rfcsr_write(rt2x00dev, 13, 0x75); rt2800_rfcsr_write(rt2x00dev, 14, 0x90); rt2800_rfcsr_write(rt2x00dev, 15, 0x58); rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); rt2800_rfcsr_write(rt2x00dev, 17, 0x92); rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); rt2800_rfcsr_write(rt2x00dev, 19, 0x02); rt2800_rfcsr_write(rt2x00dev, 20, 0xba); rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); rt2800_rfcsr_write(rt2x00dev, 22, 0x00); rt2800_rfcsr_write(rt2x00dev, 23, 0x31); rt2800_rfcsr_write(rt2x00dev, 24, 0x08); rt2800_rfcsr_write(rt2x00dev, 25, 0x01); rt2800_rfcsr_write(rt2x00dev, 26, 0x25); rt2800_rfcsr_write(rt2x00dev, 27, 0x23); rt2800_rfcsr_write(rt2x00dev, 28, 0x13); rt2800_rfcsr_write(rt2x00dev, 29, 0x83); rt2800_rfcsr_write(rt2x00dev, 30, 0x00); rt2800_rfcsr_write(rt2x00dev, 31, 0x00); return 0; } else if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_rfcsr_write(rt2x00dev, 0, 0xf0); rt2800_rfcsr_write(rt2x00dev, 1, 0x23); rt2800_rfcsr_write(rt2x00dev, 2, 0x50); rt2800_rfcsr_write(rt2x00dev, 3, 0x18); rt2800_rfcsr_write(rt2x00dev, 4, 0x00); rt2800_rfcsr_write(rt2x00dev, 5, 0x00); rt2800_rfcsr_write(rt2x00dev, 6, 0x33); rt2800_rfcsr_write(rt2x00dev, 7, 0x00); rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); rt2800_rfcsr_write(rt2x00dev, 9, 0x02); rt2800_rfcsr_write(rt2x00dev, 10, 0xd2); rt2800_rfcsr_write(rt2x00dev, 11, 0x42); rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); rt2800_rfcsr_write(rt2x00dev, 13, 0x00); rt2800_rfcsr_write(rt2x00dev, 14, 0x5a); rt2800_rfcsr_write(rt2x00dev, 15, 0x00); rt2800_rfcsr_write(rt2x00dev, 16, 0x01); rt2800_rfcsr_write(rt2x00dev, 18, 0x45); rt2800_rfcsr_write(rt2x00dev, 19, 0x02); rt2800_rfcsr_write(rt2x00dev, 20, 0x00); rt2800_rfcsr_write(rt2x00dev, 21, 0x00); rt2800_rfcsr_write(rt2x00dev, 22, 0x00); rt2800_rfcsr_write(rt2x00dev, 23, 0x00); rt2800_rfcsr_write(rt2x00dev, 24, 0x00); rt2800_rfcsr_write(rt2x00dev, 25, 0x80); rt2800_rfcsr_write(rt2x00dev, 26, 0x00); rt2800_rfcsr_write(rt2x00dev, 27, 0x03); rt2800_rfcsr_write(rt2x00dev, 28, 0x03); rt2800_rfcsr_write(rt2x00dev, 29, 0x00); rt2800_rfcsr_write(rt2x00dev, 30, 0x10); rt2800_rfcsr_write(rt2x00dev, 31, 0x80); rt2800_rfcsr_write(rt2x00dev, 32, 0x80); rt2800_rfcsr_write(rt2x00dev, 33, 0x00); rt2800_rfcsr_write(rt2x00dev, 34, 0x01); rt2800_rfcsr_write(rt2x00dev, 35, 0x03); rt2800_rfcsr_write(rt2x00dev, 36, 0xbd); rt2800_rfcsr_write(rt2x00dev, 37, 0x3c); rt2800_rfcsr_write(rt2x00dev, 38, 0x5f); rt2800_rfcsr_write(rt2x00dev, 39, 0xc5); rt2800_rfcsr_write(rt2x00dev, 40, 0x33); rt2800_rfcsr_write(rt2x00dev, 41, 0x5b); rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); rt2800_rfcsr_write(rt2x00dev, 43, 0xdb); rt2800_rfcsr_write(rt2x00dev, 44, 0xdb); rt2800_rfcsr_write(rt2x00dev, 45, 0xdb); rt2800_rfcsr_write(rt2x00dev, 46, 0xdd); rt2800_rfcsr_write(rt2x00dev, 47, 0x0d); rt2800_rfcsr_write(rt2x00dev, 48, 0x14); rt2800_rfcsr_write(rt2x00dev, 49, 0x00); rt2800_rfcsr_write(rt2x00dev, 50, 0x2d); rt2800_rfcsr_write(rt2x00dev, 51, 0x7f); rt2800_rfcsr_write(rt2x00dev, 52, 0x00); rt2800_rfcsr_write(rt2x00dev, 53, 0x52); rt2800_rfcsr_write(rt2x00dev, 54, 0x1b); rt2800_rfcsr_write(rt2x00dev, 55, 0x7f); rt2800_rfcsr_write(rt2x00dev, 56, 0x00); rt2800_rfcsr_write(rt2x00dev, 57, 0x52); rt2800_rfcsr_write(rt2x00dev, 58, 0x1b); rt2800_rfcsr_write(rt2x00dev, 59, 0x00); rt2800_rfcsr_write(rt2x00dev, 60, 0x00); rt2800_rfcsr_write(rt2x00dev, 61, 0x00); rt2800_rfcsr_write(rt2x00dev, 62, 0x00); rt2800_rfcsr_write(rt2x00dev, 63, 0x00); } else if (rt2x00_rt(rt2x00dev, RT5390)) { rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); rt2800_rfcsr_write(rt2x00dev, 2, 0x80); rt2800_rfcsr_write(rt2x00dev, 3, 0x88); rt2800_rfcsr_write(rt2x00dev, 5, 0x10); if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); else rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); rt2800_rfcsr_write(rt2x00dev, 7, 0x00); rt2800_rfcsr_write(rt2x00dev, 10, 0x53); rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); rt2800_rfcsr_write(rt2x00dev, 12, 0xc6); rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); rt2800_rfcsr_write(rt2x00dev, 14, 0x00); rt2800_rfcsr_write(rt2x00dev, 15, 0x00); rt2800_rfcsr_write(rt2x00dev, 16, 0x00); rt2800_rfcsr_write(rt2x00dev, 18, 0x03); rt2800_rfcsr_write(rt2x00dev, 19, 0x00); rt2800_rfcsr_write(rt2x00dev, 20, 0x00); rt2800_rfcsr_write(rt2x00dev, 21, 0x00); rt2800_rfcsr_write(rt2x00dev, 22, 0x20); rt2800_rfcsr_write(rt2x00dev, 23, 0x00); rt2800_rfcsr_write(rt2x00dev, 24, 0x00); if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) rt2800_rfcsr_write(rt2x00dev, 25, 0x80); else rt2800_rfcsr_write(rt2x00dev, 25, 0xc0); rt2800_rfcsr_write(rt2x00dev, 26, 0x00); rt2800_rfcsr_write(rt2x00dev, 27, 0x09); rt2800_rfcsr_write(rt2x00dev, 28, 0x00); rt2800_rfcsr_write(rt2x00dev, 29, 0x10); rt2800_rfcsr_write(rt2x00dev, 30, 0x00); rt2800_rfcsr_write(rt2x00dev, 31, 0x80); rt2800_rfcsr_write(rt2x00dev, 32, 0x80); rt2800_rfcsr_write(rt2x00dev, 33, 0x00); rt2800_rfcsr_write(rt2x00dev, 34, 0x07); rt2800_rfcsr_write(rt2x00dev, 35, 0x12); rt2800_rfcsr_write(rt2x00dev, 36, 0x00); rt2800_rfcsr_write(rt2x00dev, 37, 0x08); rt2800_rfcsr_write(rt2x00dev, 38, 0x85); rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); else rt2800_rfcsr_write(rt2x00dev, 40, 0x4b); rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); rt2800_rfcsr_write(rt2x00dev, 42, 0xd2); rt2800_rfcsr_write(rt2x00dev, 43, 0x9a); rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) rt2800_rfcsr_write(rt2x00dev, 46, 0x73); else rt2800_rfcsr_write(rt2x00dev, 46, 0x7b); rt2800_rfcsr_write(rt2x00dev, 47, 0x00); rt2800_rfcsr_write(rt2x00dev, 48, 0x10); rt2800_rfcsr_write(rt2x00dev, 49, 0x94); rt2800_rfcsr_write(rt2x00dev, 52, 0x38); if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) rt2800_rfcsr_write(rt2x00dev, 53, 0x00); else rt2800_rfcsr_write(rt2x00dev, 53, 0x84); rt2800_rfcsr_write(rt2x00dev, 54, 0x78); rt2800_rfcsr_write(rt2x00dev, 55, 0x44); rt2800_rfcsr_write(rt2x00dev, 56, 0x22); rt2800_rfcsr_write(rt2x00dev, 57, 0x80); rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); rt2800_rfcsr_write(rt2x00dev, 59, 0x63); rt2800_rfcsr_write(rt2x00dev, 60, 0x45); if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); else rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); rt2800_rfcsr_write(rt2x00dev, 62, 0x00); rt2800_rfcsr_write(rt2x00dev, 63, 0x00); } else if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_write(rt2x00dev, 1, 0x17); rt2800_rfcsr_write(rt2x00dev, 2, 0x80); rt2800_rfcsr_write(rt2x00dev, 3, 0x88); rt2800_rfcsr_write(rt2x00dev, 5, 0x10); rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); rt2800_rfcsr_write(rt2x00dev, 7, 0x00); rt2800_rfcsr_write(rt2x00dev, 10, 0x53); rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); rt2800_rfcsr_write(rt2x00dev, 12, 0x46); rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); rt2800_rfcsr_write(rt2x00dev, 14, 0x00); rt2800_rfcsr_write(rt2x00dev, 15, 0x00); rt2800_rfcsr_write(rt2x00dev, 16, 0x00); rt2800_rfcsr_write(rt2x00dev, 18, 0x03); rt2800_rfcsr_write(rt2x00dev, 19, 0x4d); rt2800_rfcsr_write(rt2x00dev, 20, 0x00); rt2800_rfcsr_write(rt2x00dev, 21, 0x8d); rt2800_rfcsr_write(rt2x00dev, 22, 0x20); rt2800_rfcsr_write(rt2x00dev, 23, 0x0b); rt2800_rfcsr_write(rt2x00dev, 24, 0x44); rt2800_rfcsr_write(rt2x00dev, 25, 0x80); rt2800_rfcsr_write(rt2x00dev, 26, 0x82); rt2800_rfcsr_write(rt2x00dev, 27, 0x09); rt2800_rfcsr_write(rt2x00dev, 28, 0x00); rt2800_rfcsr_write(rt2x00dev, 29, 0x10); rt2800_rfcsr_write(rt2x00dev, 30, 0x10); rt2800_rfcsr_write(rt2x00dev, 31, 0x80); rt2800_rfcsr_write(rt2x00dev, 32, 0x20); rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); rt2800_rfcsr_write(rt2x00dev, 34, 0x07); rt2800_rfcsr_write(rt2x00dev, 35, 0x12); rt2800_rfcsr_write(rt2x00dev, 36, 0x00); rt2800_rfcsr_write(rt2x00dev, 37, 0x08); rt2800_rfcsr_write(rt2x00dev, 38, 0x89); rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); rt2800_rfcsr_write(rt2x00dev, 40, 0x0f); rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); rt2800_rfcsr_write(rt2x00dev, 43, 0x9b); rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); rt2800_rfcsr_write(rt2x00dev, 46, 0x73); rt2800_rfcsr_write(rt2x00dev, 47, 0x0c); rt2800_rfcsr_write(rt2x00dev, 48, 0x10); rt2800_rfcsr_write(rt2x00dev, 49, 0x94); rt2800_rfcsr_write(rt2x00dev, 50, 0x94); rt2800_rfcsr_write(rt2x00dev, 51, 0x3a); rt2800_rfcsr_write(rt2x00dev, 52, 0x48); rt2800_rfcsr_write(rt2x00dev, 53, 0x44); rt2800_rfcsr_write(rt2x00dev, 54, 0x38); rt2800_rfcsr_write(rt2x00dev, 55, 0x43); rt2800_rfcsr_write(rt2x00dev, 56, 0xa1); rt2800_rfcsr_write(rt2x00dev, 57, 0x00); rt2800_rfcsr_write(rt2x00dev, 58, 0x39); rt2800_rfcsr_write(rt2x00dev, 59, 0x07); rt2800_rfcsr_write(rt2x00dev, 60, 0x45); rt2800_rfcsr_write(rt2x00dev, 61, 0x91); rt2800_rfcsr_write(rt2x00dev, 62, 0x39); rt2800_rfcsr_write(rt2x00dev, 63, 0x07); } if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { rt2800_register_read(rt2x00dev, LDO_CFG0, ®); rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); rt2800_register_write(rt2x00dev, LDO_CFG0, reg); } else if (rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090)) { rt2800_rfcsr_write(rt2x00dev, 31, 0x14); rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); rt2800_register_read(rt2x00dev, LDO_CFG0, ®); rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) { rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); else rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); } rt2800_register_write(rt2x00dev, LDO_CFG0, reg); rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); rt2x00_set_field32(®, GPIO_SWITCH_5, 0); rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); } else if (rt2x00_rt(rt2x00dev, RT3390)) { rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); rt2x00_set_field32(®, GPIO_SWITCH_5, 0); rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); } else if (rt2x00_rt(rt2x00dev, RT3572)) { rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); rt2800_register_read(rt2x00dev, LDO_CFG0, ®); rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); rt2800_register_write(rt2x00dev, LDO_CFG0, reg); msleep(1); rt2800_register_read(rt2x00dev, LDO_CFG0, ®); rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); rt2800_register_write(rt2x00dev, LDO_CFG0, reg); } /* * Set RX Filter calibration for 20MHz and 40MHz */ if (rt2x00_rt(rt2x00dev, RT3070)) { drv_data->calibration_bw20 = rt2800_init_rx_filter(rt2x00dev, false, 0x07, 0x16); drv_data->calibration_bw40 = rt2800_init_rx_filter(rt2x00dev, true, 0x27, 0x19); } else if (rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT3572)) { drv_data->calibration_bw20 = rt2800_init_rx_filter(rt2x00dev, false, 0x07, 0x13); drv_data->calibration_bw40 = rt2800_init_rx_filter(rt2x00dev, true, 0x27, 0x15); } /* * Save BBP 25 & 26 values for later use in channel switching */ rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); if (!rt2x00_rt(rt2x00dev, RT5390) && !rt2x00_rt(rt2x00dev, RT5392)) { /* * Set back to initial state */ rt2800_bbp_write(rt2x00dev, 24, 0); rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0); rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); /* * Set BBP back to BW20 */ rt2800_bbp_read(rt2x00dev, 4, &bbp); rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0); rt2800_bbp_write(rt2x00dev, 4, bbp); } if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F) || rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) rt2800_rfcsr_write(rt2x00dev, 27, 0x03); rt2800_register_read(rt2x00dev, OPT_14_CSR, ®); rt2x00_set_field32(®, OPT_14_CSR_BIT0, 1); rt2800_register_write(rt2x00dev, OPT_14_CSR, reg); if (!rt2x00_rt(rt2x00dev, RT5390) && !rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR17_TX_LO1_EN, 0); if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { if (!test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) rt2x00_set_field8(&rfcsr, RFCSR17_R, 1); } rt2x00_set_field8(&rfcsr, RFCSR17_TXMIXER_GAIN, drv_data->txmixer_gain_24g); rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); } if (rt2x00_rt(rt2x00dev, RT3090)) { rt2800_bbp_read(rt2x00dev, 138, &bbp); /* Turn off unused DAC1 and ADC1 to reduce power consumption */ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) rt2x00_set_field8(&bbp, BBP138_TX_DAC1, 1); rt2800_bbp_write(rt2x00dev, 138, bbp); } if (rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3390)) { rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); rt2800_rfcsr_read(rt2x00dev, 15, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR15_TX_LO2_EN, 0); rt2800_rfcsr_write(rt2x00dev, 15, rfcsr); rt2800_rfcsr_read(rt2x00dev, 20, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR20_RX_LO1_EN, 0); rt2800_rfcsr_write(rt2x00dev, 20, rfcsr); rt2800_rfcsr_read(rt2x00dev, 21, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR21_RX_LO2_EN, 0); rt2800_rfcsr_write(rt2x00dev, 21, rfcsr); } if (rt2x00_rt(rt2x00dev, RT3070)) { rt2800_rfcsr_read(rt2x00dev, 27, &rfcsr); if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) rt2x00_set_field8(&rfcsr, RFCSR27_R1, 3); else rt2x00_set_field8(&rfcsr, RFCSR27_R1, 0); rt2x00_set_field8(&rfcsr, RFCSR27_R2, 0); rt2x00_set_field8(&rfcsr, RFCSR27_R3, 0); rt2x00_set_field8(&rfcsr, RFCSR27_R4, 0); rt2800_rfcsr_write(rt2x00dev, 27, rfcsr); } if (rt2x00_rt(rt2x00dev, RT3290)) { rt2800_rfcsr_read(rt2x00dev, 29, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR29_RSSI_GAIN, 3); rt2800_rfcsr_write(rt2x00dev, 29, rfcsr); } if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0); rt2800_rfcsr_write(rt2x00dev, 38, rfcsr); rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0); rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); } return 0; } int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 word; /* * Initialize all registers. */ if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || rt2800_init_registers(rt2x00dev) || rt2800_init_bbp(rt2x00dev) || rt2800_init_rfcsr(rt2x00dev))) return -EIO; /* * Send signal to firmware during boot time. */ rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); if (rt2x00_is_usb(rt2x00dev) && (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3572))) { udelay(200); rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0); udelay(10); } /* * Enable RX. */ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); udelay(50); rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1); rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2); rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); /* * Initialize LED control */ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word); rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff, word & 0xff, (word >> 8) & 0xff); rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word); rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff, word & 0xff, (word >> 8) & 0xff); rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word); rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff, word & 0xff, (word >> 8) & 0xff); return 0; } EXPORT_SYMBOL_GPL(rt2800_enable_radio); void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2800_disable_wpdma(rt2x00dev); /* Wait for DMA, ignore error */ rt2800_wait_wpdma_ready(rt2x00dev); rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 0); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); } EXPORT_SYMBOL_GPL(rt2800_disable_radio); int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 efuse_ctrl_reg; if (rt2x00_rt(rt2x00dev, RT3290)) efuse_ctrl_reg = EFUSE_CTRL_3290; else efuse_ctrl_reg = EFUSE_CTRL; rt2800_register_read(rt2x00dev, efuse_ctrl_reg, ®); return rt2x00_get_field32(reg, EFUSE_CTRL_PRESENT); } EXPORT_SYMBOL_GPL(rt2800_efuse_detect); static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i) { u32 reg; u16 efuse_ctrl_reg; u16 efuse_data0_reg; u16 efuse_data1_reg; u16 efuse_data2_reg; u16 efuse_data3_reg; if (rt2x00_rt(rt2x00dev, RT3290)) { efuse_ctrl_reg = EFUSE_CTRL_3290; efuse_data0_reg = EFUSE_DATA0_3290; efuse_data1_reg = EFUSE_DATA1_3290; efuse_data2_reg = EFUSE_DATA2_3290; efuse_data3_reg = EFUSE_DATA3_3290; } else { efuse_ctrl_reg = EFUSE_CTRL; efuse_data0_reg = EFUSE_DATA0; efuse_data1_reg = EFUSE_DATA1; efuse_data2_reg = EFUSE_DATA2; efuse_data3_reg = EFUSE_DATA3; } mutex_lock(&rt2x00dev->csr_mutex); rt2800_register_read_lock(rt2x00dev, efuse_ctrl_reg, ®); rt2x00_set_field32(®, EFUSE_CTRL_ADDRESS_IN, i); rt2x00_set_field32(®, EFUSE_CTRL_MODE, 0); rt2x00_set_field32(®, EFUSE_CTRL_KICK, 1); rt2800_register_write_lock(rt2x00dev, efuse_ctrl_reg, reg); /* Wait until the EEPROM has been loaded */ rt2800_regbusy_read(rt2x00dev, efuse_ctrl_reg, EFUSE_CTRL_KICK, ®); /* Apparently the data is read from end to start */ rt2800_register_read_lock(rt2x00dev, efuse_data3_reg, ®); /* The returned value is in CPU order, but eeprom is le */ *(u32 *)&rt2x00dev->eeprom[i] = cpu_to_le32(reg); rt2800_register_read_lock(rt2x00dev, efuse_data2_reg, ®); *(u32 *)&rt2x00dev->eeprom[i + 2] = cpu_to_le32(reg); rt2800_register_read_lock(rt2x00dev, efuse_data1_reg, ®); *(u32 *)&rt2x00dev->eeprom[i + 4] = cpu_to_le32(reg); rt2800_register_read_lock(rt2x00dev, efuse_data0_reg, ®); *(u32 *)&rt2x00dev->eeprom[i + 6] = cpu_to_le32(reg); mutex_unlock(&rt2x00dev->csr_mutex); } void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { unsigned int i; for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8) rt2800_efuse_read(rt2x00dev, i); } EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse); static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) { struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; u16 word; u8 *mac; u8 default_lna_gain; /* * Read the EEPROM. */ rt2800_read_eeprom(rt2x00dev); /* * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { eth_random_addr(mac); EEPROM(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1); rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); } else if (rt2x00_rt(rt2x00dev, RT2860) || rt2x00_rt(rt2x00dev, RT2872)) { /* * There is a max of 2 RX streams for RT28x0 series */ if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2) rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_CARDBUS_ACCEL, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_2G, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_5G, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_WPS_PBC, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_2G, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_5G, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BROADBAND_EXT_LNA, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_ANT_DIVERSITY, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word); EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); if ((word & 0x00ff) == 0x00ff) { rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); EEPROM(rt2x00dev, "Freq: 0x%04x\n", word); } if ((word & 0xff00) == 0xff00) { rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE, LED_MODE_TXRX_ACTIVITY); rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555); rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221); rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8); EEPROM(rt2x00dev, "Led Mode: 0x%04x\n", word); } /* * During the LNA validation we are going to use * lna0 as correct value. Note that EEPROM_LNA * is never validated. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word); default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0); rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word); if ((word & 0x00ff) != 0x00ff) { drv_data->txmixer_gain_24g = rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL); } else { drv_data->txmixer_gain_24g = 0; } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0); if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, default_lna_gain); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word); if ((word & 0x00ff) != 0x00ff) { drv_data->txmixer_gain_5g = rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL); } else { drv_data->txmixer_gain_5g = 0; } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0); if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, default_lna_gain); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); return 0; } static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 value; u16 eeprom; /* * Read EEPROM word for configuration. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); /* * Identify RF chipset by EEPROM value * RT28xx/RT30xx: defined in "EEPROM_NIC_CONF0_RF_TYPE" field * RT53xx: defined in "EEPROM_CHIP_ID" field */ if (rt2x00_rt(rt2x00dev, RT3290)) rt2800_register_read(rt2x00dev, MAC_CSR0_3290, ®); else rt2800_register_read(rt2x00dev, MAC_CSR0, ®); if (rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT3290 || rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5390 || rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5392) rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &value); else value = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); switch (rt2x00dev->chip.rt) { case RT2860: case RT2872: case RT2883: case RT3070: case RT3071: case RT3090: case RT3290: case RT3352: case RT3390: case RT3572: case RT5390: case RT5392: break; default: ERROR(rt2x00dev, "Invalid RT chipset 0x%04x detected.\n", rt2x00dev->chip.rt); return -ENODEV; } switch (rt2x00dev->chip.rf) { case RF2820: case RF2850: case RF2720: case RF2750: case RF3020: case RF2020: case RF3021: case RF3022: case RF3052: case RF3290: case RF3320: case RF3322: case RF5360: case RF5370: case RF5372: case RF5390: case RF5392: break; default: ERROR(rt2x00dev, "Invalid RF chipset 0x%04x detected.\n", rt2x00dev->chip.rf); return -ENODEV; } /* * Identify default antenna configuration. */ rt2x00dev->default_ant.tx_chain_num = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH); rt2x00dev->default_ant.rx_chain_num = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH); rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT3390)) { value = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); switch (value) { case 0: case 1: case 2: rt2x00dev->default_ant.tx = ANTENNA_A; rt2x00dev->default_ant.rx = ANTENNA_A; break; case 3: rt2x00dev->default_ant.tx = ANTENNA_A; rt2x00dev->default_ant.rx = ANTENNA_B; break; } } else { rt2x00dev->default_ant.tx = ANTENNA_A; rt2x00dev->default_ant.rx = ANTENNA_A; } if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; /* Unused */ rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; /* Unused */ } /* * Determine external LNA informations. */ if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G)) __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G)) __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); /* * Detect if this device has an hardware controlled radio. */ if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_HW_RADIO)) __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Detect if this device has Bluetooth co-existence. */ if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_BT_COEXIST)) __set_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags); /* * Read frequency offset and RF programming sequence. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); /* * Store led settings, for correct led behaviour. */ #ifdef CONFIG_RT2X00_LIB_LEDS rt2800_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); rt2x00dev->led_mcu_reg = eeprom; #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Check if support EIRP tx power limit feature. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) < EIRP_MAX_TX_POWER_LIMIT) __set_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags); return 0; } /* * RF value list for rt28xx * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750) */ static const struct rf_channel rf_vals[] = { { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b }, { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f }, { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b }, { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f }, { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b }, { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f }, { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b }, { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f }, { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b }, { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f }, { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b }, { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f }, { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b }, { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 }, /* 802.11 UNI / HyperLan 2 */ { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 }, { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 }, { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 }, { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 }, { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b }, { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b }, { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 }, { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 }, { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b }, { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 }, { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 }, { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 }, /* 802.11 HyperLan 2 */ { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 }, { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 }, { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 }, { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 }, { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 }, { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b }, { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 }, { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 }, { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 }, { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 }, { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b }, { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 }, { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b }, { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 }, { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b }, { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 }, /* 802.11 UNII */ { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 }, { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 }, { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f }, { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f }, { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 }, { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 }, { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 }, { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f }, { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 }, { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 }, { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f }, /* 802.11 Japan */ { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b }, { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 }, { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b }, { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 }, { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 }, { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b }, { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 }, }; /* * RF value list for rt3xxx * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052) */ static const struct rf_channel rf_vals_3x[] = { {1, 241, 2, 2 }, {2, 241, 2, 7 }, {3, 242, 2, 2 }, {4, 242, 2, 7 }, {5, 243, 2, 2 }, {6, 243, 2, 7 }, {7, 244, 2, 2 }, {8, 244, 2, 7 }, {9, 245, 2, 2 }, {10, 245, 2, 7 }, {11, 246, 2, 2 }, {12, 246, 2, 7 }, {13, 247, 2, 2 }, {14, 248, 2, 4 }, /* 802.11 UNI / HyperLan 2 */ {36, 0x56, 0, 4}, {38, 0x56, 0, 6}, {40, 0x56, 0, 8}, {44, 0x57, 0, 0}, {46, 0x57, 0, 2}, {48, 0x57, 0, 4}, {52, 0x57, 0, 8}, {54, 0x57, 0, 10}, {56, 0x58, 0, 0}, {60, 0x58, 0, 4}, {62, 0x58, 0, 6}, {64, 0x58, 0, 8}, /* 802.11 HyperLan 2 */ {100, 0x5b, 0, 8}, {102, 0x5b, 0, 10}, {104, 0x5c, 0, 0}, {108, 0x5c, 0, 4}, {110, 0x5c, 0, 6}, {112, 0x5c, 0, 8}, {116, 0x5d, 0, 0}, {118, 0x5d, 0, 2}, {120, 0x5d, 0, 4}, {124, 0x5d, 0, 8}, {126, 0x5d, 0, 10}, {128, 0x5e, 0, 0}, {132, 0x5e, 0, 4}, {134, 0x5e, 0, 6}, {136, 0x5e, 0, 8}, {140, 0x5f, 0, 0}, /* 802.11 UNII */ {149, 0x5f, 0, 9}, {151, 0x5f, 0, 11}, {153, 0x60, 0, 1}, {157, 0x60, 0, 5}, {159, 0x60, 0, 7}, {161, 0x60, 0, 9}, {165, 0x61, 0, 1}, {167, 0x61, 0, 3}, {169, 0x61, 0, 5}, {171, 0x61, 0, 7}, {173, 0x61, 0, 9}, }; static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *default_power1; char *default_power2; unsigned int i; u16 eeprom; /* * Disable powersaving as default on PCI devices. */ if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; /* * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_REPORTS_TX_ACK_STATUS; /* * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices * unless we are capable of sending the buffered frames out after the * DTIM transmission using rt2x00lib_beacondone. This will send out * multicast and broadcast traffic immediately instead of buffering it * infinitly and thus dropping it after some time. */ if (!rt2x00_is_usb(rt2x00dev)) rt2x00dev->hw->flags |= IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* * As rt2800 has a global fallback table we cannot specify * more then one tx rate per frame but since the hw will * try several rates (based on the fallback table) we should * initialize max_report_rates to the maximum number of rates * we are going to try. Otherwise mac80211 will truncate our * reported tx rates and the rc algortihm will end up with * incorrect data. */ rt2x00dev->hw->max_rates = 1; rt2x00dev->hw->max_report_rates = 7; rt2x00dev->hw->max_rate_tries = 1; rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; if (rt2x00_rf(rt2x00dev, RF2820) || rt2x00_rf(rt2x00dev, RF2720)) { spec->num_channels = 14; spec->channels = rf_vals; } else if (rt2x00_rf(rt2x00dev, RF2850) || rt2x00_rf(rt2x00dev, RF2750)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals); spec->channels = rf_vals; } else if (rt2x00_rf(rt2x00dev, RF3020) || rt2x00_rf(rt2x00dev, RF2020) || rt2x00_rf(rt2x00dev, RF3021) || rt2x00_rf(rt2x00dev, RF3022) || rt2x00_rf(rt2x00dev, RF3290) || rt2x00_rf(rt2x00dev, RF3320) || rt2x00_rf(rt2x00dev, RF3322) || rt2x00_rf(rt2x00dev, RF5360) || rt2x00_rf(rt2x00dev, RF5370) || rt2x00_rf(rt2x00dev, RF5372) || rt2x00_rf(rt2x00dev, RF5390) || rt2x00_rf(rt2x00dev, RF5392)) { spec->num_channels = 14; spec->channels = rf_vals_3x; } else if (rt2x00_rf(rt2x00dev, RF3052)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_3x); spec->channels = rf_vals_3x; } /* * Initialize HT information. */ if (!rt2x00_rf(rt2x00dev, RF2020)) spec->ht.ht_supported = true; else spec->ht.ht_supported = false; spec->ht.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40; if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) >= 2) spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC; spec->ht.cap |= rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) << IEEE80211_HT_CAP_RX_STBC_SHIFT; spec->ht.ampdu_factor = 3; spec->ht.ampdu_density = 4; spec->ht.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED | IEEE80211_HT_MCS_TX_RX_DIFF | ((rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); switch (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH)) { case 3: spec->ht.mcs.rx_mask[2] = 0xff; case 2: spec->ht.mcs.rx_mask[1] = 0xff; case 1: spec->ht.mcs.rx_mask[0] = 0xff; spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */ break; } /* * Create channel information array */ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; spec->channels_info = info; default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); for (i = 0; i < 14; i++) { info[i].default_power1 = default_power1[i]; info[i].default_power2 = default_power2[i]; } if (spec->num_channels > 14) { default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1); default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2); for (i = 14; i < spec->num_channels; i++) { info[i].default_power1 = default_power1[i]; info[i].default_power2 = default_power2[i]; } } switch (rt2x00dev->chip.rf) { case RF2020: case RF3020: case RF3021: case RF3022: case RF3320: case RF3052: case RF3290: case RF5360: case RF5370: case RF5372: case RF5390: case RF5392: __set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags); break; } return 0; } int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; u32 reg; /* * Allocate eeprom data. */ retval = rt2800_validate_eeprom(rt2x00dev); if (retval) return retval; retval = rt2800_init_eeprom(rt2x00dev); if (retval) return retval; /* * Enable rfkill polling by setting GPIO direction of the * rfkill switch GPIO pin correctly. */ rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); rt2x00_set_field32(®, GPIO_CTRL_DIR2, 1); rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); /* * Initialize hw specifications. */ retval = rt2800_probe_hw_mode(rt2x00dev); if (retval) return retval; /* * Set device capabilities. */ __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); __set_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags); if (!rt2x00_is_usb(rt2x00dev)) __set_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags); /* * Set device requirements. */ if (!rt2x00_is_soc(rt2x00dev)) __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); __set_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags); __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); if (!rt2800_hwcrypt_disabled(rt2x00dev)) __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); if (rt2x00_is_usb(rt2x00dev)) __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); else { __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); __set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags); } /* * Set the rssi offset. */ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; return 0; } EXPORT_SYMBOL_GPL(rt2800_probe_hw); /* * IEEE80211 stack callback functions. */ void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, u16 *iv16) { struct rt2x00_dev *rt2x00dev = hw->priv; struct mac_iveiv_entry iveiv_entry; u32 offset; offset = MAC_IVEIV_ENTRY(hw_key_idx); rt2800_register_multiread(rt2x00dev, offset, &iveiv_entry, sizeof(iveiv_entry)); memcpy(iv16, &iveiv_entry.iv[0], sizeof(*iv16)); memcpy(iv32, &iveiv_entry.iv[4], sizeof(*iv32)); } EXPORT_SYMBOL_GPL(rt2800_get_tkip_seq); int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct rt2x00_dev *rt2x00dev = hw->priv; u32 reg; bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD); rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, value); rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, enabled); rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, enabled); rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, enabled); rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, enabled); rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, enabled); rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, enabled); rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); return 0; } EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold); int rt2800_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; struct rt2x00_field32 field; int retval; u32 reg; u32 offset; /* * First pass the configuration through rt2x00lib, that will * update the queue settings and validate the input. After that * we are free to update the registers based on the value * in the queue parameter. */ retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; /* * We only need to perform additional register initialization * for WMM queues/ */ if (queue_idx >= 4) return 0; queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); /* Update WMM TXOP register */ offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2))); field.bit_offset = (queue_idx & 1) * 16; field.bit_mask = 0xffff << field.bit_offset; rt2800_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, field, queue->txop); rt2800_register_write(rt2x00dev, offset, reg); /* Update WMM registers */ field.bit_offset = queue_idx * 4; field.bit_mask = 0xf << field.bit_offset; rt2800_register_read(rt2x00dev, WMM_AIFSN_CFG, ®); rt2x00_set_field32(®, field, queue->aifs); rt2800_register_write(rt2x00dev, WMM_AIFSN_CFG, reg); rt2800_register_read(rt2x00dev, WMM_CWMIN_CFG, ®); rt2x00_set_field32(®, field, queue->cw_min); rt2800_register_write(rt2x00dev, WMM_CWMIN_CFG, reg); rt2800_register_read(rt2x00dev, WMM_CWMAX_CFG, ®); rt2x00_set_field32(®, field, queue->cw_max); rt2800_register_write(rt2x00dev, WMM_CWMAX_CFG, reg); /* Update EDCA registers */ offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx); rt2800_register_read(rt2x00dev, offset, ®); rt2x00_set_field32(®, EDCA_AC0_CFG_TX_OP, queue->txop); rt2x00_set_field32(®, EDCA_AC0_CFG_AIFSN, queue->aifs); rt2x00_set_field32(®, EDCA_AC0_CFG_CWMIN, queue->cw_min); rt2x00_set_field32(®, EDCA_AC0_CFG_CWMAX, queue->cw_max); rt2800_register_write(rt2x00dev, offset, reg); return 0; } EXPORT_SYMBOL_GPL(rt2800_conf_tx); u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; rt2800_register_read(rt2x00dev, TSF_TIMER_DW1, ®); tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32; rt2800_register_read(rt2x00dev, TSF_TIMER_DW0, ®); tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD); return tsf; } EXPORT_SYMBOL_GPL(rt2800_get_tsf); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; int ret = 0; /* * Don't allow aggregation for stations the hardware isn't aware * of because tx status reports for frames to an unknown station * always contain wcid=255 and thus we can't distinguish between * multiple stations which leads to unwanted situations when the * hw reorders frames due to aggregation. */ if (sta_priv->wcid < 0) return 1; switch (action) { case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: /* * The hw itself takes care of setting up BlockAck mechanisms. * So, we only have to allow mac80211 to nagotiate a BlockAck * agreement. Once that is done, the hw will BlockAck incoming * AMPDUs without further setup. */ break; case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: break; default: WARNING((struct rt2x00_dev *)hw->priv, "Unknown AMPDU action\n"); } return ret; } EXPORT_SYMBOL_GPL(rt2800_ampdu_action); int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_conf *conf = &hw->conf; u32 idle, busy, busy_ext; if (idx != 0) return -ENOENT; survey->channel = conf->channel; rt2800_register_read(rt2x00dev, CH_IDLE_STA, &idle); rt2800_register_read(rt2x00dev, CH_BUSY_STA, &busy); rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &busy_ext); if (idle || busy) { survey->filled = SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_EXT_BUSY; survey->channel_time = (idle + busy) / 1000; survey->channel_time_busy = busy / 1000; survey->channel_time_ext_busy = busy_ext / 1000; } if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) survey->filled |= SURVEY_INFO_IN_USE; return 0; } EXPORT_SYMBOL_GPL(rt2800_get_survey); MODULE_AUTHOR(DRV_PROJECT ", Bartlomiej Zolnierkiewicz"); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2800 library"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2800.h0000644000175000017500000023734712026211315022650 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2010 Ivo van Doorn Copyright (C) 2010 Willow Garage Copyright (C) 2009 Alban Browaeys Copyright (C) 2009 Felix Fietkau Copyright (C) 2009 Luis Correia Copyright (C) 2009 Mattias Nissler Copyright (C) 2009 Mark Asselstine Copyright (C) 2009 Xose Vazquez Perez Copyright (C) 2009 Bart Zolnierkiewicz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2800 Abstract: Data structures and registers for the rt2800 modules. Supported chipsets: RT2800E, RT2800ED & RT2800U. */ #ifndef RT2800_H #define RT2800_H /* * RF chip defines. * * RF2820 2.4G 2T3R * RF2850 2.4G/5G 2T3R * RF2720 2.4G 1T2R * RF2750 2.4G/5G 1T2R * RF3020 2.4G 1T1R * RF2020 2.4G B/G * RF3021 2.4G 1T2R * RF3022 2.4G 2T2R * RF3052 2.4G/5G 2T2R * RF2853 2.4G/5G 3T3R * RF3320 2.4G 1T1R(RT3350/RT3370/RT3390) * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392) * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662) * RF5360 2.4G 1T1R * RF5370 2.4G 1T1R * RF5390 2.4G 1T1R */ #define RF2820 0x0001 #define RF2850 0x0002 #define RF2720 0x0003 #define RF2750 0x0004 #define RF3020 0x0005 #define RF2020 0x0006 #define RF3021 0x0007 #define RF3022 0x0008 #define RF3052 0x0009 #define RF2853 0x000a #define RF3320 0x000b #define RF3322 0x000c #define RF3053 0x000d #define RF3290 0x3290 #define RF5360 0x5360 #define RF5370 0x5370 #define RF5372 0x5372 #define RF5390 0x5390 #define RF5392 0x5392 /* * Chipset revisions. */ #define REV_RT2860C 0x0100 #define REV_RT2860D 0x0101 #define REV_RT2872E 0x0200 #define REV_RT3070E 0x0200 #define REV_RT3070F 0x0201 #define REV_RT3071E 0x0211 #define REV_RT3090E 0x0211 #define REV_RT3390E 0x0211 #define REV_RT5390F 0x0502 #define REV_RT5390R 0x1502 /* * Signal information. * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 120 /* * Register layout information. */ #define CSR_REG_BASE 0x1000 #define CSR_REG_SIZE 0x0800 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0110 #define BBP_BASE 0x0000 #define BBP_SIZE 0x00ff #define RF_BASE 0x0004 #define RF_SIZE 0x0010 #define RFCSR_BASE 0x0000 #define RFCSR_SIZE 0x0040 /* * Number of TX queues. */ #define NUM_TX_QUEUES 4 /* * Registers. */ /* * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number. */ #define MAC_CSR0_3290 0x0000 /* * E2PROM_CSR: PCI EEPROM control register. * RELOAD: Write 1 to reload eeprom content. * TYPE: 0: 93c46, 1:93c66. * LOAD_STATUS: 1:loading, 0:done. */ #define E2PROM_CSR 0x0004 #define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000001) #define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000002) #define E2PROM_CSR_DATA_IN FIELD32(0x00000004) #define E2PROM_CSR_DATA_OUT FIELD32(0x00000008) #define E2PROM_CSR_TYPE FIELD32(0x00000030) #define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) #define E2PROM_CSR_RELOAD FIELD32(0x00000080) /* * CMB_CTRL_CFG */ #define CMB_CTRL 0x0020 #define AUX_OPT_BIT0 FIELD32(0x00000001) #define AUX_OPT_BIT1 FIELD32(0x00000002) #define AUX_OPT_BIT2 FIELD32(0x00000004) #define AUX_OPT_BIT3 FIELD32(0x00000008) #define AUX_OPT_BIT4 FIELD32(0x00000010) #define AUX_OPT_BIT5 FIELD32(0x00000020) #define AUX_OPT_BIT6 FIELD32(0x00000040) #define AUX_OPT_BIT7 FIELD32(0x00000080) #define AUX_OPT_BIT8 FIELD32(0x00000100) #define AUX_OPT_BIT9 FIELD32(0x00000200) #define AUX_OPT_BIT10 FIELD32(0x00000400) #define AUX_OPT_BIT11 FIELD32(0x00000800) #define AUX_OPT_BIT12 FIELD32(0x00001000) #define AUX_OPT_BIT13 FIELD32(0x00002000) #define AUX_OPT_BIT14 FIELD32(0x00004000) #define AUX_OPT_BIT15 FIELD32(0x00008000) #define LDO25_LEVEL FIELD32(0x00030000) #define LDO25_LARGEA FIELD32(0x00040000) #define LDO25_FRC_ON FIELD32(0x00080000) #define CMB_RSV FIELD32(0x00300000) #define XTAL_RDY FIELD32(0x00400000) #define PLL_LD FIELD32(0x00800000) #define LDO_CORE_LEVEL FIELD32(0x0F000000) #define LDO_BGSEL FIELD32(0x30000000) #define LDO3_EN FIELD32(0x40000000) #define LDO0_EN FIELD32(0x80000000) /* * EFUSE_CSR_3290: RT3290 EEPROM */ #define EFUSE_CTRL_3290 0x0024 /* * EFUSE_DATA3 of 3290 */ #define EFUSE_DATA3_3290 0x0028 /* * EFUSE_DATA2 of 3290 */ #define EFUSE_DATA2_3290 0x002c /* * EFUSE_DATA1 of 3290 */ #define EFUSE_DATA1_3290 0x0030 /* * EFUSE_DATA0 of 3290 */ #define EFUSE_DATA0_3290 0x0034 /* * OSC_CTRL_CFG * Ring oscillator configuration */ #define OSC_CTRL 0x0038 #define OSC_REF_CYCLE FIELD32(0x00001fff) #define OSC_RSV FIELD32(0x0000e000) #define OSC_CAL_CNT FIELD32(0x0fff0000) #define OSC_CAL_ACK FIELD32(0x10000000) #define OSC_CLK_32K_VLD FIELD32(0x20000000) #define OSC_CAL_REQ FIELD32(0x40000000) #define OSC_ROSC_EN FIELD32(0x80000000) /* * COEX_CFG_0 */ #define COEX_CFG0 0x0040 #define COEX_CFG_ANT FIELD32(0xff000000) /* * COEX_CFG_1 */ #define COEX_CFG1 0x0044 /* * COEX_CFG_2 */ #define COEX_CFG2 0x0048 #define BT_COEX_CFG1 FIELD32(0xff000000) #define BT_COEX_CFG0 FIELD32(0x00ff0000) #define WL_COEX_CFG1 FIELD32(0x0000ff00) #define WL_COEX_CFG0 FIELD32(0x000000ff) /* * PLL_CTRL_CFG * PLL configuration register */ #define PLL_CTRL 0x0050 #define PLL_RESERVED_INPUT1 FIELD32(0x000000ff) #define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00) #define PLL_CONTROL FIELD32(0x00070000) #define PLL_LPF_R1 FIELD32(0x00080000) #define PLL_LPF_C1_CTRL FIELD32(0x00300000) #define PLL_LPF_C2_CTRL FIELD32(0x00c00000) #define PLL_CP_CURRENT_CTRL FIELD32(0x03000000) #define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000) #define PLL_LOCK_CTRL FIELD32(0x70000000) #define PLL_VBGBK_EN FIELD32(0x80000000) /* * WLAN_CTRL_CFG * RT3290 wlan configuration */ #define WLAN_FUN_CTRL 0x0080 #define WLAN_EN FIELD32(0x00000001) #define WLAN_CLK_EN FIELD32(0x00000002) #define WLAN_RSV1 FIELD32(0x00000004) #define WLAN_RESET FIELD32(0x00000008) #define PCIE_APP0_CLK_REQ FIELD32(0x00000010) #define FRC_WL_ANT_SET FIELD32(0x00000020) #define INV_TR_SW0 FIELD32(0x00000040) #define WLAN_GPIO_IN_BIT0 FIELD32(0x00000100) #define WLAN_GPIO_IN_BIT1 FIELD32(0x00000200) #define WLAN_GPIO_IN_BIT2 FIELD32(0x00000400) #define WLAN_GPIO_IN_BIT3 FIELD32(0x00000800) #define WLAN_GPIO_IN_BIT4 FIELD32(0x00001000) #define WLAN_GPIO_IN_BIT5 FIELD32(0x00002000) #define WLAN_GPIO_IN_BIT6 FIELD32(0x00004000) #define WLAN_GPIO_IN_BIT7 FIELD32(0x00008000) #define WLAN_GPIO_IN_BIT_ALL FIELD32(0x0000ff00) #define WLAN_GPIO_OUT_BIT0 FIELD32(0x00010000) #define WLAN_GPIO_OUT_BIT1 FIELD32(0x00020000) #define WLAN_GPIO_OUT_BIT2 FIELD32(0x00040000) #define WLAN_GPIO_OUT_BIT3 FIELD32(0x00050000) #define WLAN_GPIO_OUT_BIT4 FIELD32(0x00100000) #define WLAN_GPIO_OUT_BIT5 FIELD32(0x00200000) #define WLAN_GPIO_OUT_BIT6 FIELD32(0x00400000) #define WLAN_GPIO_OUT_BIT7 FIELD32(0x00800000) #define WLAN_GPIO_OUT_BIT_ALL FIELD32(0x00ff0000) #define WLAN_GPIO_OUT_OE_BIT0 FIELD32(0x01000000) #define WLAN_GPIO_OUT_OE_BIT1 FIELD32(0x02000000) #define WLAN_GPIO_OUT_OE_BIT2 FIELD32(0x04000000) #define WLAN_GPIO_OUT_OE_BIT3 FIELD32(0x08000000) #define WLAN_GPIO_OUT_OE_BIT4 FIELD32(0x10000000) #define WLAN_GPIO_OUT_OE_BIT5 FIELD32(0x20000000) #define WLAN_GPIO_OUT_OE_BIT6 FIELD32(0x40000000) #define WLAN_GPIO_OUT_OE_BIT7 FIELD32(0x80000000) #define WLAN_GPIO_OUT_OE_BIT_ALL FIELD32(0xff000000) /* * AUX_CTRL: Aux/PCI-E related configuration */ #define AUX_CTRL 0x10c #define AUX_CTRL_WAKE_PCIE_EN FIELD32(0x00000002) #define AUX_CTRL_FORCE_PCIE_CLK FIELD32(0x00000400) /* * OPT_14: Unknown register used by rt3xxx devices. */ #define OPT_14_CSR 0x0114 #define OPT_14_CSR_BIT0 FIELD32(0x00000001) /* * INT_SOURCE_CSR: Interrupt source register. * Write one to clear corresponding bit. * TX_FIFO_STATUS: FIFO Statistics is full, sw should read TX_STA_FIFO */ #define INT_SOURCE_CSR 0x0200 #define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001) #define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002) #define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004) #define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008) #define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010) #define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020) #define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040) #define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080) #define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100) #define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200) #define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400) #define INT_SOURCE_CSR_TBTT FIELD32(0x00000800) #define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000) #define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000) #define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000) #define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000) #define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000) #define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000) /* * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. */ #define INT_MASK_CSR 0x0204 #define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001) #define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002) #define INT_MASK_CSR_RX_DONE FIELD32(0x00000004) #define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008) #define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010) #define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020) #define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040) #define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080) #define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100) #define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200) #define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400) #define INT_MASK_CSR_TBTT FIELD32(0x00000800) #define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000) #define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000) #define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000) #define INT_MASK_CSR_GPTIMER FIELD32(0x00008000) #define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000) #define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000) /* * WPDMA_GLO_CFG */ #define WPDMA_GLO_CFG 0x0208 #define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001) #define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002) #define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004) #define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008) #define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030) #define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040) #define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080) #define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00) #define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000) /* * WPDMA_RST_IDX */ #define WPDMA_RST_IDX 0x020c #define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001) #define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002) #define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004) #define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008) #define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010) #define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020) #define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000) /* * DELAY_INT_CFG */ #define DELAY_INT_CFG 0x0210 #define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff) #define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00) #define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000) #define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000) #define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000) #define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000) /* * WMM_AIFSN_CFG: Aifsn for each EDCA AC * AIFSN0: AC_VO * AIFSN1: AC_VI * AIFSN2: AC_BE * AIFSN3: AC_BK */ #define WMM_AIFSN_CFG 0x0214 #define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f) #define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0) #define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00) #define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000) /* * WMM_CWMIN_CSR: CWmin for each EDCA AC * CWMIN0: AC_VO * CWMIN1: AC_VI * CWMIN2: AC_BE * CWMIN3: AC_BK */ #define WMM_CWMIN_CFG 0x0218 #define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f) #define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0) #define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00) #define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000) /* * WMM_CWMAX_CSR: CWmax for each EDCA AC * CWMAX0: AC_VO * CWMAX1: AC_VI * CWMAX2: AC_BE * CWMAX3: AC_BK */ #define WMM_CWMAX_CFG 0x021c #define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f) #define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0) #define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00) #define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000) /* * AC_TXOP0: AC_VO/AC_VI TXOP register * AC0TXOP: AC_VO in unit of 32us * AC1TXOP: AC_VI in unit of 32us */ #define WMM_TXOP0_CFG 0x0220 #define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff) #define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000) /* * AC_TXOP1: AC_BE/AC_BK TXOP register * AC2TXOP: AC_BE in unit of 32us * AC3TXOP: AC_BK in unit of 32us */ #define WMM_TXOP1_CFG 0x0224 #define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff) #define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000) /* * GPIO_CTRL: * GPIO_CTRL_VALx: GPIO value * GPIO_CTRL_DIRx: GPIO direction: 0 = output; 1 = input */ #define GPIO_CTRL 0x0228 #define GPIO_CTRL_VAL0 FIELD32(0x00000001) #define GPIO_CTRL_VAL1 FIELD32(0x00000002) #define GPIO_CTRL_VAL2 FIELD32(0x00000004) #define GPIO_CTRL_VAL3 FIELD32(0x00000008) #define GPIO_CTRL_VAL4 FIELD32(0x00000010) #define GPIO_CTRL_VAL5 FIELD32(0x00000020) #define GPIO_CTRL_VAL6 FIELD32(0x00000040) #define GPIO_CTRL_VAL7 FIELD32(0x00000080) #define GPIO_CTRL_DIR0 FIELD32(0x00000100) #define GPIO_CTRL_DIR1 FIELD32(0x00000200) #define GPIO_CTRL_DIR2 FIELD32(0x00000400) #define GPIO_CTRL_DIR3 FIELD32(0x00000800) #define GPIO_CTRL_DIR4 FIELD32(0x00001000) #define GPIO_CTRL_DIR5 FIELD32(0x00002000) #define GPIO_CTRL_DIR6 FIELD32(0x00004000) #define GPIO_CTRL_DIR7 FIELD32(0x00008000) #define GPIO_CTRL_VAL8 FIELD32(0x00010000) #define GPIO_CTRL_VAL9 FIELD32(0x00020000) #define GPIO_CTRL_VAL10 FIELD32(0x00040000) #define GPIO_CTRL_DIR8 FIELD32(0x01000000) #define GPIO_CTRL_DIR9 FIELD32(0x02000000) #define GPIO_CTRL_DIR10 FIELD32(0x04000000) /* * MCU_CMD_CFG */ #define MCU_CMD_CFG 0x022c /* * AC_VO register offsets */ #define TX_BASE_PTR0 0x0230 #define TX_MAX_CNT0 0x0234 #define TX_CTX_IDX0 0x0238 #define TX_DTX_IDX0 0x023c /* * AC_VI register offsets */ #define TX_BASE_PTR1 0x0240 #define TX_MAX_CNT1 0x0244 #define TX_CTX_IDX1 0x0248 #define TX_DTX_IDX1 0x024c /* * AC_BE register offsets */ #define TX_BASE_PTR2 0x0250 #define TX_MAX_CNT2 0x0254 #define TX_CTX_IDX2 0x0258 #define TX_DTX_IDX2 0x025c /* * AC_BK register offsets */ #define TX_BASE_PTR3 0x0260 #define TX_MAX_CNT3 0x0264 #define TX_CTX_IDX3 0x0268 #define TX_DTX_IDX3 0x026c /* * HCCA register offsets */ #define TX_BASE_PTR4 0x0270 #define TX_MAX_CNT4 0x0274 #define TX_CTX_IDX4 0x0278 #define TX_DTX_IDX4 0x027c /* * MGMT register offsets */ #define TX_BASE_PTR5 0x0280 #define TX_MAX_CNT5 0x0284 #define TX_CTX_IDX5 0x0288 #define TX_DTX_IDX5 0x028c /* * RX register offsets */ #define RX_BASE_PTR 0x0290 #define RX_MAX_CNT 0x0294 #define RX_CRX_IDX 0x0298 #define RX_DRX_IDX 0x029c /* * USB_DMA_CFG * RX_BULK_AGG_TIMEOUT: Rx Bulk Aggregation TimeOut in unit of 33ns. * RX_BULK_AGG_LIMIT: Rx Bulk Aggregation Limit in unit of 256 bytes. * PHY_CLEAR: phy watch dog enable. * TX_CLEAR: Clear USB DMA TX path. * TXOP_HALT: Halt TXOP count down when TX buffer is full. * RX_BULK_AGG_EN: Enable Rx Bulk Aggregation. * RX_BULK_EN: Enable USB DMA Rx. * TX_BULK_EN: Enable USB DMA Tx. * EP_OUT_VALID: OUT endpoint data valid. * RX_BUSY: USB DMA RX FSM busy. * TX_BUSY: USB DMA TX FSM busy. */ #define USB_DMA_CFG 0x02a0 #define USB_DMA_CFG_RX_BULK_AGG_TIMEOUT FIELD32(0x000000ff) #define USB_DMA_CFG_RX_BULK_AGG_LIMIT FIELD32(0x0000ff00) #define USB_DMA_CFG_PHY_CLEAR FIELD32(0x00010000) #define USB_DMA_CFG_TX_CLEAR FIELD32(0x00080000) #define USB_DMA_CFG_TXOP_HALT FIELD32(0x00100000) #define USB_DMA_CFG_RX_BULK_AGG_EN FIELD32(0x00200000) #define USB_DMA_CFG_RX_BULK_EN FIELD32(0x00400000) #define USB_DMA_CFG_TX_BULK_EN FIELD32(0x00800000) #define USB_DMA_CFG_EP_OUT_VALID FIELD32(0x3f000000) #define USB_DMA_CFG_RX_BUSY FIELD32(0x40000000) #define USB_DMA_CFG_TX_BUSY FIELD32(0x80000000) /* * US_CYC_CNT * BT_MODE_EN: Bluetooth mode enable * CLOCK CYCLE: Clock cycle count in 1us. * PCI:0x21, PCIE:0x7d, USB:0x1e */ #define US_CYC_CNT 0x02a4 #define US_CYC_CNT_BT_MODE_EN FIELD32(0x00000100) #define US_CYC_CNT_CLOCK_CYCLE FIELD32(0x000000ff) /* * PBF_SYS_CTRL * HOST_RAM_WRITE: enable Host program ram write selection */ #define PBF_SYS_CTRL 0x0400 #define PBF_SYS_CTRL_READY FIELD32(0x00000080) #define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000) /* * HOST-MCU shared memory */ #define HOST_CMD_CSR 0x0404 #define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff) /* * PBF registers * Most are for debug. Driver doesn't touch PBF register. */ #define PBF_CFG 0x0408 #define PBF_MAX_PCNT 0x040c #define PBF_CTRL 0x0410 #define PBF_INT_STA 0x0414 #define PBF_INT_ENA 0x0418 /* * BCN_OFFSET0: */ #define BCN_OFFSET0 0x042c #define BCN_OFFSET0_BCN0 FIELD32(0x000000ff) #define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00) #define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000) #define BCN_OFFSET0_BCN3 FIELD32(0xff000000) /* * BCN_OFFSET1: */ #define BCN_OFFSET1 0x0430 #define BCN_OFFSET1_BCN4 FIELD32(0x000000ff) #define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00) #define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000) #define BCN_OFFSET1_BCN7 FIELD32(0xff000000) /* * TXRXQ_PCNT: PBF register * PCNT_TX0Q: Page count for TX hardware queue 0 * PCNT_TX1Q: Page count for TX hardware queue 1 * PCNT_TX2Q: Page count for TX hardware queue 2 * PCNT_RX0Q: Page count for RX hardware queue */ #define TXRXQ_PCNT 0x0438 #define TXRXQ_PCNT_TX0Q FIELD32(0x000000ff) #define TXRXQ_PCNT_TX1Q FIELD32(0x0000ff00) #define TXRXQ_PCNT_TX2Q FIELD32(0x00ff0000) #define TXRXQ_PCNT_RX0Q FIELD32(0xff000000) /* * PBF register * Debug. Driver doesn't touch PBF register. */ #define PBF_DBG 0x043c /* * RF registers */ #define RF_CSR_CFG 0x0500 #define RF_CSR_CFG_DATA FIELD32(0x000000ff) #define RF_CSR_CFG_REGNUM FIELD32(0x00003f00) #define RF_CSR_CFG_WRITE FIELD32(0x00010000) #define RF_CSR_CFG_BUSY FIELD32(0x00020000) /* * EFUSE_CSR: RT30x0 EEPROM */ #define EFUSE_CTRL 0x0580 #define EFUSE_CTRL_ADDRESS_IN FIELD32(0x03fe0000) #define EFUSE_CTRL_MODE FIELD32(0x000000c0) #define EFUSE_CTRL_KICK FIELD32(0x40000000) #define EFUSE_CTRL_PRESENT FIELD32(0x80000000) /* * EFUSE_DATA0 */ #define EFUSE_DATA0 0x0590 /* * EFUSE_DATA1 */ #define EFUSE_DATA1 0x0594 /* * EFUSE_DATA2 */ #define EFUSE_DATA2 0x0598 /* * EFUSE_DATA3 */ #define EFUSE_DATA3 0x059c /* * LDO_CFG0 */ #define LDO_CFG0 0x05d4 #define LDO_CFG0_DELAY3 FIELD32(0x000000ff) #define LDO_CFG0_DELAY2 FIELD32(0x0000ff00) #define LDO_CFG0_DELAY1 FIELD32(0x00ff0000) #define LDO_CFG0_BGSEL FIELD32(0x03000000) #define LDO_CFG0_LDO_CORE_VLEVEL FIELD32(0x1c000000) #define LD0_CFG0_LDO25_LEVEL FIELD32(0x60000000) #define LDO_CFG0_LDO25_LARGEA FIELD32(0x80000000) /* * GPIO_SWITCH */ #define GPIO_SWITCH 0x05dc #define GPIO_SWITCH_0 FIELD32(0x00000001) #define GPIO_SWITCH_1 FIELD32(0x00000002) #define GPIO_SWITCH_2 FIELD32(0x00000004) #define GPIO_SWITCH_3 FIELD32(0x00000008) #define GPIO_SWITCH_4 FIELD32(0x00000010) #define GPIO_SWITCH_5 FIELD32(0x00000020) #define GPIO_SWITCH_6 FIELD32(0x00000040) #define GPIO_SWITCH_7 FIELD32(0x00000080) /* * MAC Control/Status Registers(CSR). * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * MAC_CSR0: ASIC revision number. * ASIC_REV: 0 * ASIC_VER: 2860 or 2870 */ #define MAC_CSR0 0x1000 #define MAC_CSR0_REVISION FIELD32(0x0000ffff) #define MAC_CSR0_CHIPSET FIELD32(0xffff0000) /* * MAC_SYS_CTRL: */ #define MAC_SYS_CTRL 0x1004 #define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001) #define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002) #define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004) #define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008) #define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010) #define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020) #define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040) #define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080) /* * MAC_ADDR_DW0: STA MAC register 0 */ #define MAC_ADDR_DW0 0x1008 #define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff) #define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00) #define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000) #define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000) /* * MAC_ADDR_DW1: STA MAC register 1 * UNICAST_TO_ME_MASK: * Used to mask off bits from byte 5 of the MAC address * to determine the UNICAST_TO_ME bit for RX frames. * The full mask is complemented by BSS_ID_MASK: * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK */ #define MAC_ADDR_DW1 0x100c #define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff) #define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00) #define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) /* * MAC_BSSID_DW0: BSSID register 0 */ #define MAC_BSSID_DW0 0x1010 #define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff) #define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00) #define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000) #define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000) /* * MAC_BSSID_DW1: BSSID register 1 * BSS_ID_MASK: * 0: 1-BSSID mode (BSS index = 0) * 1: 2-BSSID mode (BSS index: Byte5, bit 0) * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1) * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2) * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the * BSSID. This will make sure that those bits will be ignored * when determining the MY_BSS of RX frames. */ #define MAC_BSSID_DW1 0x1014 #define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff) #define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00) #define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000) #define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000) /* * MAX_LEN_CFG: Maximum frame length register. * MAX_MPDU: rt2860b max 16k bytes * MAX_PSDU: Maximum PSDU length * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16 */ #define MAX_LEN_CFG 0x1018 #define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff) #define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000) #define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000) #define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000) /* * BBP_CSR_CFG: BBP serial control register * VALUE: Register value to program into BBP * REG_NUM: Selected BBP register * READ_CONTROL: 0 write BBP, 1 read BBP * BUSY: ASIC is busy executing BBP commands * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks * BBP_RW_MODE: 0 serial, 1 parallel */ #define BBP_CSR_CFG 0x101c #define BBP_CSR_CFG_VALUE FIELD32(0x000000ff) #define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00) #define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000) #define BBP_CSR_CFG_BUSY FIELD32(0x00020000) #define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000) #define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000) /* * RF_CSR_CFG0: RF control register * REGID_AND_VALUE: Register value to program into RF * BITWIDTH: Selected RF register * STANDBYMODE: 0 high when standby, 1 low when standby * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate * BUSY: ASIC is busy executing RF commands */ #define RF_CSR_CFG0 0x1020 #define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff) #define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000) #define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff) #define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000) #define RF_CSR_CFG0_SEL FIELD32(0x40000000) #define RF_CSR_CFG0_BUSY FIELD32(0x80000000) /* * RF_CSR_CFG1: RF control register * REGID_AND_VALUE: Register value to program into RF * RFGAP: Gap between BB_CONTROL_RF and RF_LE * 0: 3 system clock cycle (37.5usec) * 1: 5 system clock cycle (62.5usec) */ #define RF_CSR_CFG1 0x1024 #define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff) #define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000) /* * RF_CSR_CFG2: RF control register * VALUE: Register value to program into RF */ #define RF_CSR_CFG2 0x1028 #define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff) /* * LED_CFG: LED control * ON_PERIOD: LED active time (ms) during TX (only used for LED mode 1) * OFF_PERIOD: LED inactive time (ms) during TX (only used for LED mode 1) * SLOW_BLINK_PERIOD: LED blink interval in seconds (only used for LED mode 2) * color LED's: * 0: off * 1: blinking upon TX2 * 2: periodic slow blinking * 3: always on * LED polarity: * 0: active low * 1: active high */ #define LED_CFG 0x102c #define LED_CFG_ON_PERIOD FIELD32(0x000000ff) #define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00) #define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000) #define LED_CFG_R_LED_MODE FIELD32(0x03000000) #define LED_CFG_G_LED_MODE FIELD32(0x0c000000) #define LED_CFG_Y_LED_MODE FIELD32(0x30000000) #define LED_CFG_LED_POLAR FIELD32(0x40000000) /* * AMPDU_BA_WINSIZE: Force BlockAck window size * FORCE_WINSIZE_ENABLE: * 0: Disable forcing of BlockAck window size * 1: Enable forcing of BlockAck window size, overwrites values BlockAck * window size values in the TXWI * FORCE_WINSIZE: BlockAck window size */ #define AMPDU_BA_WINSIZE 0x1040 #define AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE FIELD32(0x00000020) #define AMPDU_BA_WINSIZE_FORCE_WINSIZE FIELD32(0x0000001f) /* * XIFS_TIME_CFG: MAC timing * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX * when MAC doesn't reference BBP signal BBRXEND * EIFS: unit 1us * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer * */ #define XIFS_TIME_CFG 0x1100 #define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff) #define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00) #define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000) #define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000) #define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000) /* * BKOFF_SLOT_CFG: */ #define BKOFF_SLOT_CFG 0x1104 #define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff) #define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00) /* * NAV_TIME_CFG: */ #define NAV_TIME_CFG 0x1108 #define NAV_TIME_CFG_SIFS FIELD32(0x000000ff) #define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00) #define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000) #define NAV_TIME_ZERO_SIFS FIELD32(0x02000000) /* * CH_TIME_CFG: count as channel busy * EIFS_BUSY: Count EIFS as channel busy * NAV_BUSY: Count NAS as channel busy * RX_BUSY: Count RX as channel busy * TX_BUSY: Count TX as channel busy * TMR_EN: Enable channel statistics timer */ #define CH_TIME_CFG 0x110c #define CH_TIME_CFG_EIFS_BUSY FIELD32(0x00000010) #define CH_TIME_CFG_NAV_BUSY FIELD32(0x00000008) #define CH_TIME_CFG_RX_BUSY FIELD32(0x00000004) #define CH_TIME_CFG_TX_BUSY FIELD32(0x00000002) #define CH_TIME_CFG_TMR_EN FIELD32(0x00000001) /* * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us */ #define PBF_LIFE_TIMER 0x1110 /* * BCN_TIME_CFG: * BEACON_INTERVAL: in unit of 1/16 TU * TSF_TICKING: Enable TSF auto counting * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode * BEACON_GEN: Enable beacon generator */ #define BCN_TIME_CFG 0x1114 #define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff) #define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000) #define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000) #define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000) #define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000) #define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000) /* * TBTT_SYNC_CFG: * BCN_AIFSN: Beacon AIFSN after TBTT interrupt in slots * BCN_CWMIN: Beacon CWMin after TBTT interrupt in slots */ #define TBTT_SYNC_CFG 0x1118 #define TBTT_SYNC_CFG_TBTT_ADJUST FIELD32(0x000000ff) #define TBTT_SYNC_CFG_BCN_EXP_WIN FIELD32(0x0000ff00) #define TBTT_SYNC_CFG_BCN_AIFSN FIELD32(0x000f0000) #define TBTT_SYNC_CFG_BCN_CWMIN FIELD32(0x00f00000) /* * TSF_TIMER_DW0: Local lsb TSF timer, read-only */ #define TSF_TIMER_DW0 0x111c #define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff) /* * TSF_TIMER_DW1: Local msb TSF timer, read-only */ #define TSF_TIMER_DW1 0x1120 #define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff) /* * TBTT_TIMER: TImer remains till next TBTT, read-only */ #define TBTT_TIMER 0x1124 /* * INT_TIMER_CFG: timer configuration * PRE_TBTT_TIMER: leadtime to tbtt for pretbtt interrupt in units of 1/16 TU * GP_TIMER: period of general purpose timer in units of 1/16 TU */ #define INT_TIMER_CFG 0x1128 #define INT_TIMER_CFG_PRE_TBTT_TIMER FIELD32(0x0000ffff) #define INT_TIMER_CFG_GP_TIMER FIELD32(0xffff0000) /* * INT_TIMER_EN: GP-timer and pre-tbtt Int enable */ #define INT_TIMER_EN 0x112c #define INT_TIMER_EN_PRE_TBTT_TIMER FIELD32(0x00000001) #define INT_TIMER_EN_GP_TIMER FIELD32(0x00000002) /* * CH_IDLE_STA: channel idle time (in us) */ #define CH_IDLE_STA 0x1130 /* * CH_BUSY_STA: channel busy time on primary channel (in us) */ #define CH_BUSY_STA 0x1134 /* * CH_BUSY_STA_SEC: channel busy time on secondary channel in HT40 mode (in us) */ #define CH_BUSY_STA_SEC 0x1138 /* * MAC_STATUS_CFG: * BBP_RF_BUSY: When set to 0, BBP and RF are stable. * if 1 or higher one of the 2 registers is busy. */ #define MAC_STATUS_CFG 0x1200 #define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003) /* * PWR_PIN_CFG: */ #define PWR_PIN_CFG 0x1204 /* * AUTOWAKEUP_CFG: Manual power control / status register * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set * AUTOWAKE: 0:sleep, 1:awake */ #define AUTOWAKEUP_CFG 0x1208 #define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff) #define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00) #define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000) /* * EDCA_AC0_CFG: */ #define EDCA_AC0_CFG 0x1300 #define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff) #define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00) #define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000) #define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000) /* * EDCA_AC1_CFG: */ #define EDCA_AC1_CFG 0x1304 #define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff) #define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00) #define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000) #define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000) /* * EDCA_AC2_CFG: */ #define EDCA_AC2_CFG 0x1308 #define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff) #define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00) #define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000) #define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000) /* * EDCA_AC3_CFG: */ #define EDCA_AC3_CFG 0x130c #define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff) #define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00) #define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000) #define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000) /* * EDCA_TID_AC_MAP: */ #define EDCA_TID_AC_MAP 0x1310 /* * TX_PWR_CFG: */ #define TX_PWR_CFG_RATE0 FIELD32(0x0000000f) #define TX_PWR_CFG_RATE1 FIELD32(0x000000f0) #define TX_PWR_CFG_RATE2 FIELD32(0x00000f00) #define TX_PWR_CFG_RATE3 FIELD32(0x0000f000) #define TX_PWR_CFG_RATE4 FIELD32(0x000f0000) #define TX_PWR_CFG_RATE5 FIELD32(0x00f00000) #define TX_PWR_CFG_RATE6 FIELD32(0x0f000000) #define TX_PWR_CFG_RATE7 FIELD32(0xf0000000) /* * TX_PWR_CFG_0: */ #define TX_PWR_CFG_0 0x1314 #define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f) #define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0) #define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00) #define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000) #define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000) #define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000) #define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000) #define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000) /* * TX_PWR_CFG_1: */ #define TX_PWR_CFG_1 0x1318 #define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f) #define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0) #define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00) #define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000) #define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000) #define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000) #define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000) #define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000) /* * TX_PWR_CFG_2: */ #define TX_PWR_CFG_2 0x131c #define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f) #define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0) #define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00) #define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000) #define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000) #define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000) #define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000) #define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000) /* * TX_PWR_CFG_3: */ #define TX_PWR_CFG_3 0x1320 #define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f) #define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0) #define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00) #define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000) #define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000) #define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000) #define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000) #define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000) /* * TX_PWR_CFG_4: */ #define TX_PWR_CFG_4 0x1324 #define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f) #define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0) #define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) #define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) /* * TX_PIN_CFG: */ #define TX_PIN_CFG 0x1328 #define TX_PIN_CFG_PA_PE_DISABLE 0xfcfffff0 #define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001) #define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002) #define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004) #define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008) #define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010) #define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020) #define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040) #define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080) #define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100) #define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200) #define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400) #define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800) #define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000) #define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000) #define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000) #define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000) #define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000) #define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000) #define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000) #define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000) #define TX_PIN_CFG_PA_PE_A2_EN FIELD32(0x01000000) #define TX_PIN_CFG_PA_PE_G2_EN FIELD32(0x02000000) #define TX_PIN_CFG_PA_PE_A2_POL FIELD32(0x04000000) #define TX_PIN_CFG_PA_PE_G2_POL FIELD32(0x08000000) #define TX_PIN_CFG_LNA_PE_A2_EN FIELD32(0x10000000) #define TX_PIN_CFG_LNA_PE_G2_EN FIELD32(0x20000000) #define TX_PIN_CFG_LNA_PE_A2_POL FIELD32(0x40000000) #define TX_PIN_CFG_LNA_PE_G2_POL FIELD32(0x80000000) /* * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz */ #define TX_BAND_CFG 0x132c #define TX_BAND_CFG_HT40_MINUS FIELD32(0x00000001) #define TX_BAND_CFG_A FIELD32(0x00000002) #define TX_BAND_CFG_BG FIELD32(0x00000004) /* * TX_SW_CFG0: */ #define TX_SW_CFG0 0x1330 /* * TX_SW_CFG1: */ #define TX_SW_CFG1 0x1334 /* * TX_SW_CFG2: */ #define TX_SW_CFG2 0x1338 /* * TXOP_THRES_CFG: */ #define TXOP_THRES_CFG 0x133c /* * TXOP_CTRL_CFG: * TIMEOUT_TRUN_EN: Enable/Disable TXOP timeout truncation * AC_TRUN_EN: Enable/Disable truncation for AC change * TXRATEGRP_TRUN_EN: Enable/Disable truncation for TX rate group change * USER_MODE_TRUN_EN: Enable/Disable truncation for user TXOP mode * MIMO_PS_TRUN_EN: Enable/Disable truncation for MIMO PS RTS/CTS * RESERVED_TRUN_EN: Reserved * LSIG_TXOP_EN: Enable/Disable L-SIG TXOP protection * EXT_CCA_EN: Enable/Disable extension channel CCA reference (Defer 40Mhz * transmissions if extension CCA is clear). * EXT_CCA_DLY: Extension CCA signal delay time (unit: us) * EXT_CWMIN: CwMin for extension channel backoff * 0: Disabled * */ #define TXOP_CTRL_CFG 0x1340 #define TXOP_CTRL_CFG_TIMEOUT_TRUN_EN FIELD32(0x00000001) #define TXOP_CTRL_CFG_AC_TRUN_EN FIELD32(0x00000002) #define TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN FIELD32(0x00000004) #define TXOP_CTRL_CFG_USER_MODE_TRUN_EN FIELD32(0x00000008) #define TXOP_CTRL_CFG_MIMO_PS_TRUN_EN FIELD32(0x00000010) #define TXOP_CTRL_CFG_RESERVED_TRUN_EN FIELD32(0x00000020) #define TXOP_CTRL_CFG_LSIG_TXOP_EN FIELD32(0x00000040) #define TXOP_CTRL_CFG_EXT_CCA_EN FIELD32(0x00000080) #define TXOP_CTRL_CFG_EXT_CCA_DLY FIELD32(0x0000ff00) #define TXOP_CTRL_CFG_EXT_CWMIN FIELD32(0x000f0000) /* * TX_RTS_CFG: * RTS_THRES: unit:byte * RTS_FBK_EN: enable rts rate fallback */ #define TX_RTS_CFG 0x1344 #define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff) #define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00) #define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000) /* * TX_TIMEOUT_CFG: * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation. * it is recommended that: * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT) */ #define TX_TIMEOUT_CFG 0x1348 #define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0) #define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00) #define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000) /* * TX_RTY_CFG: * SHORT_RTY_LIMIT: short retry limit * LONG_RTY_LIMIT: long retry limit * LONG_RTY_THRE: Long retry threshoold * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode * 0:expired by retry limit, 1: expired by mpdu life timer * AGG_RTY_MODE: Aggregate MPDU retry mode * 0:expired by retry limit, 1: expired by mpdu life timer * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable */ #define TX_RTY_CFG 0x134c #define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff) #define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00) #define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000) #define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000) #define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000) #define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000) /* * TX_LINK_CFG: * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us * MFB_ENABLE: TX apply remote MFB 1:enable * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable * 0: not apply remote remote unsolicit (MFS=7) * TX_MRQ_EN: MCS request TX enable * TX_RDG_EN: RDG TX enable * TX_CF_ACK_EN: Piggyback CF-ACK enable * REMOTE_MFB: remote MCS feedback * REMOTE_MFS: remote MCS feedback sequence number */ #define TX_LINK_CFG 0x1350 #define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff) #define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100) #define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200) #define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400) #define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800) #define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000) #define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000) #define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000) /* * HT_FBK_CFG0: */ #define HT_FBK_CFG0 0x1354 #define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f) #define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0) #define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00) #define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000) #define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000) #define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000) #define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000) #define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000) /* * HT_FBK_CFG1: */ #define HT_FBK_CFG1 0x1358 #define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f) #define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0) #define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00) #define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000) #define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000) #define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000) #define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000) #define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000) /* * LG_FBK_CFG0: */ #define LG_FBK_CFG0 0x135c #define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f) #define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0) #define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00) #define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000) #define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000) #define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000) #define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000) #define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000) /* * LG_FBK_CFG1: */ #define LG_FBK_CFG1 0x1360 #define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f) #define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0) #define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00) #define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000) /* * CCK_PROT_CFG: CCK Protection * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd) * PROTECT_CTRL: Protection control frame type for CCK TX * 0:none, 1:RTS/CTS, 2:CTS-to-self * PROTECT_NAV_SHORT: TXOP protection type for CCK TX with short NAV * PROTECT_NAV_LONG: TXOP protection type for CCK TX with long NAV * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow * RTS_TH_EN: RTS threshold enable on CCK TX */ #define CCK_PROT_CFG 0x1364 #define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) #define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) #define CCK_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) #define CCK_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) #define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) #define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) #define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) #define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) #define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) #define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) #define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) /* * OFDM_PROT_CFG: OFDM Protection */ #define OFDM_PROT_CFG 0x1368 #define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) #define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) #define OFDM_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) #define OFDM_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) #define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) #define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) #define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) #define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) #define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) #define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) #define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) /* * MM20_PROT_CFG: MM20 Protection */ #define MM20_PROT_CFG 0x136c #define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) #define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) #define MM20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) #define MM20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) #define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) #define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) #define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) #define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) #define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) #define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) #define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) /* * MM40_PROT_CFG: MM40 Protection */ #define MM40_PROT_CFG 0x1370 #define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) #define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) #define MM40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) #define MM40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) #define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) #define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) #define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) #define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) #define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) #define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) #define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) /* * GF20_PROT_CFG: GF20 Protection */ #define GF20_PROT_CFG 0x1374 #define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) #define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) #define GF20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) #define GF20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) #define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) #define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) #define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) #define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) #define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) #define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) #define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) /* * GF40_PROT_CFG: GF40 Protection */ #define GF40_PROT_CFG 0x1378 #define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) #define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) #define GF40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) #define GF40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) #define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) #define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) #define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) #define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) #define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) #define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) #define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) /* * EXP_CTS_TIME: */ #define EXP_CTS_TIME 0x137c /* * EXP_ACK_TIME: */ #define EXP_ACK_TIME 0x1380 /* * RX_FILTER_CFG: RX configuration register. */ #define RX_FILTER_CFG 0x1400 #define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001) #define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002) #define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004) #define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008) #define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010) #define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020) #define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040) #define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080) #define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100) #define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200) #define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400) #define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800) #define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000) #define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000) #define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000) #define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000) #define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000) /* * AUTO_RSP_CFG: * AUTORESPONDER: 0: disable, 1: enable * BAC_ACK_POLICY: 0:long, 1:short preamble * CTS_40_MMODE: Response CTS 40MHz duplicate mode * CTS_40_MREF: Response CTS 40MHz duplicate mode * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble * DUAL_CTS_EN: Power bit value in control frame * ACK_CTS_PSM_BIT:Power bit value in control frame */ #define AUTO_RSP_CFG 0x1404 #define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001) #define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002) #define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004) #define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008) #define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010) #define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040) #define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080) /* * LEGACY_BASIC_RATE: */ #define LEGACY_BASIC_RATE 0x1408 /* * HT_BASIC_RATE: */ #define HT_BASIC_RATE 0x140c /* * HT_CTRL_CFG: */ #define HT_CTRL_CFG 0x1410 /* * SIFS_COST_CFG: */ #define SIFS_COST_CFG 0x1414 /* * RX_PARSER_CFG: * Set NAV for all received frames */ #define RX_PARSER_CFG 0x1418 /* * TX_SEC_CNT0: */ #define TX_SEC_CNT0 0x1500 /* * RX_SEC_CNT0: */ #define RX_SEC_CNT0 0x1504 /* * CCMP_FC_MUTE: */ #define CCMP_FC_MUTE 0x1508 /* * TXOP_HLDR_ADDR0: */ #define TXOP_HLDR_ADDR0 0x1600 /* * TXOP_HLDR_ADDR1: */ #define TXOP_HLDR_ADDR1 0x1604 /* * TXOP_HLDR_ET: */ #define TXOP_HLDR_ET 0x1608 /* * QOS_CFPOLL_RA_DW0: */ #define QOS_CFPOLL_RA_DW0 0x160c /* * QOS_CFPOLL_RA_DW1: */ #define QOS_CFPOLL_RA_DW1 0x1610 /* * QOS_CFPOLL_QC: */ #define QOS_CFPOLL_QC 0x1614 /* * RX_STA_CNT0: RX PLCP error count & RX CRC error count */ #define RX_STA_CNT0 0x1700 #define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff) #define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000) /* * RX_STA_CNT1: RX False CCA count & RX LONG frame count */ #define RX_STA_CNT1 0x1704 #define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff) #define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000) /* * RX_STA_CNT2: */ #define RX_STA_CNT2 0x1708 #define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff) #define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000) /* * TX_STA_CNT0: TX Beacon count */ #define TX_STA_CNT0 0x170c #define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff) #define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000) /* * TX_STA_CNT1: TX tx count */ #define TX_STA_CNT1 0x1710 #define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff) #define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000) /* * TX_STA_CNT2: TX tx count */ #define TX_STA_CNT2 0x1714 #define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff) #define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000) /* * TX_STA_FIFO: TX Result for specific PID status fifo register. * * This register is implemented as FIFO with 16 entries in the HW. Each * register read fetches the next tx result. If the FIFO is full because * it wasn't read fast enough after the according interrupt (TX_FIFO_STATUS) * triggered, the hw seems to simply drop further tx results. * * VALID: 1: this tx result is valid * 0: no valid tx result -> driver should stop reading * PID_TYPE: The PID latched from the PID field in the TXWI, can be used * to match a frame with its tx result (even though the PID is * only 4 bits wide). * PID_QUEUE: Part of PID_TYPE, this is the queue index number (0-3) * PID_ENTRY: Part of PID_TYPE, this is the queue entry index number (1-3) * This identification number is calculated by ((idx % 3) + 1). * TX_SUCCESS: Indicates tx success (1) or failure (0) * TX_AGGRE: Indicates if the frame was part of an aggregate (1) or not (0) * TX_ACK_REQUIRED: Indicates if the frame needed to get ack'ed (1) or not (0) * WCID: The wireless client ID. * MCS: The tx rate used during the last transmission of this frame, be it * successful or not. * PHYMODE: The phymode used for the transmission. */ #define TX_STA_FIFO 0x1718 #define TX_STA_FIFO_VALID FIELD32(0x00000001) #define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e) #define TX_STA_FIFO_PID_QUEUE FIELD32(0x00000006) #define TX_STA_FIFO_PID_ENTRY FIELD32(0x00000018) #define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020) #define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040) #define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080) #define TX_STA_FIFO_WCID FIELD32(0x0000ff00) #define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000) #define TX_STA_FIFO_MCS FIELD32(0x007f0000) #define TX_STA_FIFO_PHYMODE FIELD32(0xc0000000) /* * TX_AGG_CNT: Debug counter */ #define TX_AGG_CNT 0x171c #define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT0: */ #define TX_AGG_CNT0 0x1720 #define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT1: */ #define TX_AGG_CNT1 0x1724 #define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT2: */ #define TX_AGG_CNT2 0x1728 #define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT3: */ #define TX_AGG_CNT3 0x172c #define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT4: */ #define TX_AGG_CNT4 0x1730 #define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT5: */ #define TX_AGG_CNT5 0x1734 #define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT6: */ #define TX_AGG_CNT6 0x1738 #define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000) /* * TX_AGG_CNT7: */ #define TX_AGG_CNT7 0x173c #define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff) #define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000) /* * MPDU_DENSITY_CNT: * TX_ZERO_DEL: TX zero length delimiter count * RX_ZERO_DEL: RX zero length delimiter count */ #define MPDU_DENSITY_CNT 0x1740 #define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff) #define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000) /* * Security key table memory. * * The pairwise key table shares some memory with the beacon frame * buffers 6 and 7. That basically means that when beacon 6 & 7 * are used we should only use the reduced pairwise key table which * has a maximum of 222 entries. * * --------------------------------------------- * |0x4000 | Pairwise Key | Reduced Pairwise | * | | Table | Key Table | * | | Size: 256 * 32 | Size: 222 * 32 | * |0x5BC0 | |------------------- * | | | Beacon 6 | * |0x5DC0 | |------------------- * | | | Beacon 7 | * |0x5FC0 | |------------------- * |0x5FFF | | * -------------------------- * * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry * SHARED_KEY_TABLE_BASE: 32-byte * 16-entry * SHARED_KEY_MODE_BASE: 4-byte * 16-entry */ #define MAC_WCID_BASE 0x1800 #define PAIRWISE_KEY_TABLE_BASE 0x4000 #define MAC_IVEIV_TABLE_BASE 0x6000 #define MAC_WCID_ATTRIBUTE_BASE 0x6800 #define SHARED_KEY_TABLE_BASE 0x6c00 #define SHARED_KEY_MODE_BASE 0x7000 #define MAC_WCID_ENTRY(__idx) \ (MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry))) #define PAIRWISE_KEY_ENTRY(__idx) \ (PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) #define MAC_IVEIV_ENTRY(__idx) \ (MAC_IVEIV_TABLE_BASE + ((__idx) * sizeof(struct mac_iveiv_entry))) #define MAC_WCID_ATTR_ENTRY(__idx) \ (MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32))) #define SHARED_KEY_ENTRY(__idx) \ (SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) #define SHARED_KEY_MODE_ENTRY(__idx) \ (SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32))) struct mac_wcid_entry { u8 mac[6]; u8 reserved[2]; } __packed; struct hw_key_entry { u8 key[16]; u8 tx_mic[8]; u8 rx_mic[8]; } __packed; struct mac_iveiv_entry { u8 iv[8]; } __packed; /* * MAC_WCID_ATTRIBUTE: */ #define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001) #define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e) #define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070) #define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380) #define MAC_WCID_ATTRIBUTE_CIPHER_EXT FIELD32(0x00000400) #define MAC_WCID_ATTRIBUTE_BSS_IDX_EXT FIELD32(0x00000800) #define MAC_WCID_ATTRIBUTE_WAPI_MCBC FIELD32(0x00008000) #define MAC_WCID_ATTRIBUTE_WAPI_KEY_IDX FIELD32(0xff000000) /* * SHARED_KEY_MODE: */ #define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007) #define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070) #define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700) #define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000) #define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000) #define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000) #define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000) #define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000) /* * HOST-MCU communication */ /* * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. * CMD_TOKEN: Command id, 0xff disable status reporting. */ #define H2M_MAILBOX_CSR 0x7010 #define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) #define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) #define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) #define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) /* * H2M_MAILBOX_CID: * Free slots contain 0xff. MCU will store command's token to lowest free slot. * If all slots are occupied status will be dropped. */ #define H2M_MAILBOX_CID 0x7014 #define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff) #define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00) #define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000) #define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000) /* * H2M_MAILBOX_STATUS: * Command status will be saved to same slot as command id. */ #define H2M_MAILBOX_STATUS 0x701c /* * H2M_INT_SRC: */ #define H2M_INT_SRC 0x7024 /* * H2M_BBP_AGENT: */ #define H2M_BBP_AGENT 0x7028 /* * MCU_LEDCS: LED control for MCU Mailbox. */ #define MCU_LEDCS_LED_MODE FIELD8(0x1f) #define MCU_LEDCS_POLARITY FIELD8(0x01) /* * HW_CS_CTS_BASE: * Carrier-sense CTS frame base address. * It's where mac stores carrier-sense frame for carrier-sense function. */ #define HW_CS_CTS_BASE 0x7700 /* * HW_DFS_CTS_BASE: * DFS CTS frame base address. It's where mac stores CTS frame for DFS. */ #define HW_DFS_CTS_BASE 0x7780 /* * TXRX control registers - base address 0x3000 */ /* * TXRX_CSR1: * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first.. */ #define TXRX_CSR1 0x77d0 /* * HW_DEBUG_SETTING_BASE: * since NULL frame won't be that long (256 byte) * We steal 16 tail bytes to save debugging settings */ #define HW_DEBUG_SETTING_BASE 0x77f0 #define HW_DEBUG_SETTING_BASE2 0x7770 /* * HW_BEACON_BASE * In order to support maximum 8 MBSS and its maximum length * is 512 bytes for each beacon * Three section discontinue memory segments will be used. * 1. The original region for BCN 0~3 * 2. Extract memory from FCE table for BCN 4~5 * 3. Extract memory from Pair-wise key table for BCN 6~7 * It occupied those memory of wcid 238~253 for BCN 6 * and wcid 222~237 for BCN 7 (see Security key table memory * for more info). * * IMPORTANT NOTE: Not sure why legacy driver does this, * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6. */ #define HW_BEACON_BASE0 0x7800 #define HW_BEACON_BASE1 0x7a00 #define HW_BEACON_BASE2 0x7c00 #define HW_BEACON_BASE3 0x7e00 #define HW_BEACON_BASE4 0x7200 #define HW_BEACON_BASE5 0x7400 #define HW_BEACON_BASE6 0x5dc0 #define HW_BEACON_BASE7 0x5bc0 #define HW_BEACON_OFFSET(__index) \ (((__index) < 4) ? (HW_BEACON_BASE0 + (__index * 0x0200)) : \ (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \ (HW_BEACON_BASE6 - ((__index - 6) * 0x0200)))) /* * BBP registers. * The wordsize of the BBP is 8 bits. */ /* * BBP 1: TX Antenna & Power Control * POWER_CTRL: * 0 - normal, * 1 - drop tx power by 6dBm, * 2 - drop tx power by 12dBm, * 3 - increase tx power by 6dBm */ #define BBP1_TX_POWER_CTRL FIELD8(0x07) #define BBP1_TX_ANTENNA FIELD8(0x18) /* * BBP 3: RX Antenna */ #define BBP3_RX_ADC FIELD8(0x03) #define BBP3_RX_ANTENNA FIELD8(0x18) #define BBP3_HT40_MINUS FIELD8(0x20) #define BBP3_ADC_MODE_SWITCH FIELD8(0x40) #define BBP3_ADC_INIT_MODE FIELD8(0x80) /* * BBP 4: Bandwidth */ #define BBP4_TX_BF FIELD8(0x01) #define BBP4_BANDWIDTH FIELD8(0x18) #define BBP4_MAC_IF_CTRL FIELD8(0x40) /* * BBP 47: Bandwidth */ #define BBP47_TSSI_REPORT_SEL FIELD8(0x03) #define BBP47_TSSI_UPDATE_REQ FIELD8(0x04) #define BBP47_TSSI_TSSI_MODE FIELD8(0x18) #define BBP47_TSSI_ADC6 FIELD8(0x80) /* * BBP 49 */ #define BBP49_UPDATE_FLAG FIELD8(0x01) /* * BBP 109 */ #define BBP109_TX0_POWER FIELD8(0x0f) #define BBP109_TX1_POWER FIELD8(0xf0) /* * BBP 138: Unknown */ #define BBP138_RX_ADC1 FIELD8(0x02) #define BBP138_RX_ADC2 FIELD8(0x04) #define BBP138_TX_DAC1 FIELD8(0x20) #define BBP138_TX_DAC2 FIELD8(0x40) /* * BBP 152: Rx Ant */ #define BBP152_RX_DEFAULT_ANT FIELD8(0x80) /* * RFCSR registers * The wordsize of the RFCSR is 8 bits. */ /* * RFCSR 1: */ #define RFCSR1_RF_BLOCK_EN FIELD8(0x01) #define RFCSR1_PLL_PD FIELD8(0x02) #define RFCSR1_RX0_PD FIELD8(0x04) #define RFCSR1_TX0_PD FIELD8(0x08) #define RFCSR1_RX1_PD FIELD8(0x10) #define RFCSR1_TX1_PD FIELD8(0x20) #define RFCSR1_RX2_PD FIELD8(0x40) #define RFCSR1_TX2_PD FIELD8(0x80) /* * RFCSR 2: */ #define RFCSR2_RESCAL_EN FIELD8(0x80) /* * RFCSR 3: */ #define RFCSR3_K FIELD8(0x0f) /* Bits [7-4] for RF3320 (RT3370/RT3390), on other chipsets reserved */ #define RFCSR3_PA1_BIAS_CCK FIELD8(0x70); #define RFCSR3_PA2_CASCODE_BIAS_CCKK FIELD8(0x80); /* * FRCSR 5: */ #define RFCSR5_R1 FIELD8(0x0c) /* * RFCSR 6: */ #define RFCSR6_R1 FIELD8(0x03) #define RFCSR6_R2 FIELD8(0x40) #define RFCSR6_TXDIV FIELD8(0x0c) /* * RFCSR 7: */ #define RFCSR7_RF_TUNING FIELD8(0x01) #define RFCSR7_BIT1 FIELD8(0x02) #define RFCSR7_BIT2 FIELD8(0x04) #define RFCSR7_BIT3 FIELD8(0x08) #define RFCSR7_BIT4 FIELD8(0x10) #define RFCSR7_BIT5 FIELD8(0x20) #define RFCSR7_BITS67 FIELD8(0xc0) /* * RFCSR 11: */ #define RFCSR11_R FIELD8(0x03) /* * RFCSR 12: */ #define RFCSR12_TX_POWER FIELD8(0x1f) #define RFCSR12_DR0 FIELD8(0xe0) /* * RFCSR 13: */ #define RFCSR13_TX_POWER FIELD8(0x1f) #define RFCSR13_DR0 FIELD8(0xe0) /* * RFCSR 15: */ #define RFCSR15_TX_LO2_EN FIELD8(0x08) /* * RFCSR 16: */ #define RFCSR16_TXMIXER_GAIN FIELD8(0x07) /* * RFCSR 17: */ #define RFCSR17_TXMIXER_GAIN FIELD8(0x07) #define RFCSR17_TX_LO1_EN FIELD8(0x08) #define RFCSR17_R FIELD8(0x20) #define RFCSR17_CODE FIELD8(0x7f) /* * RFCSR 20: */ #define RFCSR20_RX_LO1_EN FIELD8(0x08) /* * RFCSR 21: */ #define RFCSR21_RX_LO2_EN FIELD8(0x08) /* * RFCSR 22: */ #define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01) /* * RFCSR 23: */ #define RFCSR23_FREQ_OFFSET FIELD8(0x7f) /* * RFCSR 24: */ #define RFCSR24_TX_AGC_FC FIELD8(0x1f) #define RFCSR24_TX_H20M FIELD8(0x20) #define RFCSR24_TX_CALIB FIELD8(0x7f) /* * RFCSR 27: */ #define RFCSR27_R1 FIELD8(0x03) #define RFCSR27_R2 FIELD8(0x04) #define RFCSR27_R3 FIELD8(0x30) #define RFCSR27_R4 FIELD8(0x40) /* * RFCSR 29: */ #define RFCSR29_ADC6_TEST FIELD8(0x01) #define RFCSR29_ADC6_INT_TEST FIELD8(0x02) #define RFCSR29_RSSI_RESET FIELD8(0x04) #define RFCSR29_RSSI_ON FIELD8(0x08) #define RFCSR29_RSSI_RIP_CTRL FIELD8(0x30) #define RFCSR29_RSSI_GAIN FIELD8(0xc0) /* * RFCSR 30: */ #define RFCSR30_TX_H20M FIELD8(0x02) #define RFCSR30_RX_H20M FIELD8(0x04) #define RFCSR30_RX_VCM FIELD8(0x18) #define RFCSR30_RF_CALIBRATION FIELD8(0x80) /* * RFCSR 31: */ #define RFCSR31_RX_AGC_FC FIELD8(0x1f) #define RFCSR31_RX_H20M FIELD8(0x20) #define RFCSR31_RX_CALIB FIELD8(0x7f) /* * RFCSR 38: */ #define RFCSR38_RX_LO1_EN FIELD8(0x20) /* * RFCSR 39: */ #define RFCSR39_RX_LO2_EN FIELD8(0x80) /* * RFCSR 49: */ #define RFCSR49_TX FIELD8(0x3f) /* * RFCSR 50: */ #define RFCSR50_TX FIELD8(0x3f) /* * RF registers */ /* * RF 2 */ #define RF2_ANTENNA_RX2 FIELD32(0x00000040) #define RF2_ANTENNA_TX1 FIELD32(0x00004000) #define RF2_ANTENNA_RX1 FIELD32(0x00020000) /* * RF 3 */ #define RF3_TXPOWER_G FIELD32(0x00003e00) #define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200) #define RF3_TXPOWER_A FIELD32(0x00003c00) /* * RF 4 */ #define RF4_TXPOWER_G FIELD32(0x000007c0) #define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040) #define RF4_TXPOWER_A FIELD32(0x00000780) #define RF4_FREQ_OFFSET FIELD32(0x001f8000) #define RF4_HT40 FIELD32(0x00200000) /* * EEPROM content. * The wordsize of the EEPROM is 16 bits. */ /* * Chip ID */ #define EEPROM_CHIP_ID 0x0000 /* * EEPROM Version */ #define EEPROM_VERSION 0x0001 #define EEPROM_VERSION_FAE FIELD16(0x00ff) #define EEPROM_VERSION_VERSION FIELD16(0xff00) /* * HW MAC address. */ #define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) #define EEPROM_MAC_ADDR_1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) #define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) /* * EEPROM NIC Configuration 0 * RXPATH: 1: 1R, 2: 2R, 3: 3R * TXPATH: 1: 1T, 2: 2T, 3: 3T * RF_TYPE: RFIC type */ #define EEPROM_NIC_CONF0 0x001a #define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f) #define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0) #define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) /* * EEPROM NIC Configuration 1 * HW_RADIO: 0: disable, 1: enable * EXTERNAL_TX_ALC: 0: disable, 1: enable * EXTERNAL_LNA_2G: 0: disable, 1: enable * EXTERNAL_LNA_5G: 0: disable, 1: enable * CARDBUS_ACCEL: 0: enable, 1: disable * BW40M_SB_2G: 0: disable, 1: enable * BW40M_SB_5G: 0: disable, 1: enable * WPS_PBC: 0: disable, 1: enable * BW40M_2G: 0: enable, 1: disable * BW40M_5G: 0: enable, 1: disable * BROADBAND_EXT_LNA: 0: disable, 1: enable * ANT_DIVERSITY: 00: Disable, 01: Diversity, * 10: Main antenna, 11: Aux antenna * INTERNAL_TX_ALC: 0: disable, 1: enable * BT_COEXIST: 0: disable, 1: enable * DAC_TEST: 0: disable, 1: enable */ #define EEPROM_NIC_CONF1 0x001b #define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001) #define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) #define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) #define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) #define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010) #define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020) #define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040) #define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080) #define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100) #define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200) #define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) #define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800) #define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) #define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000) #define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000) /* * EEPROM frequency */ #define EEPROM_FREQ 0x001d #define EEPROM_FREQ_OFFSET FIELD16(0x00ff) #define EEPROM_FREQ_LED_MODE FIELD16(0x7f00) #define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000) /* * EEPROM LED * POLARITY_RDY_G: Polarity RDY_G setting. * POLARITY_RDY_A: Polarity RDY_A setting. * POLARITY_ACT: Polarity ACT setting. * POLARITY_GPIO_0: Polarity GPIO0 setting. * POLARITY_GPIO_1: Polarity GPIO1 setting. * POLARITY_GPIO_2: Polarity GPIO2 setting. * POLARITY_GPIO_3: Polarity GPIO3 setting. * POLARITY_GPIO_4: Polarity GPIO4 setting. * LED_MODE: Led mode. */ #define EEPROM_LED_AG_CONF 0x001e #define EEPROM_LED_ACT_CONF 0x001f #define EEPROM_LED_POLARITY 0x0020 #define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001) #define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) #define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) #define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) #define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) #define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) #define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) #define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) #define EEPROM_LED_LED_MODE FIELD16(0x1f00) /* * EEPROM NIC Configuration 2 * RX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved */ #define EEPROM_NIC_CONF2 0x0021 #define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) #define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) #define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) /* * EEPROM LNA */ #define EEPROM_LNA 0x0022 #define EEPROM_LNA_BG FIELD16(0x00ff) #define EEPROM_LNA_A0 FIELD16(0xff00) /* * EEPROM RSSI BG offset */ #define EEPROM_RSSI_BG 0x0023 #define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff) #define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00) /* * EEPROM RSSI BG2 offset */ #define EEPROM_RSSI_BG2 0x0024 #define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff) #define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00) /* * EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2). */ #define EEPROM_TXMIXER_GAIN_BG 0x0024 #define EEPROM_TXMIXER_GAIN_BG_VAL FIELD16(0x0007) /* * EEPROM RSSI A offset */ #define EEPROM_RSSI_A 0x0025 #define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff) #define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00) /* * EEPROM RSSI A2 offset */ #define EEPROM_RSSI_A2 0x0026 #define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) #define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) /* * EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2). */ #define EEPROM_TXMIXER_GAIN_A 0x0026 #define EEPROM_TXMIXER_GAIN_A_VAL FIELD16(0x0007) /* * EEPROM EIRP Maximum TX power values(unit: dbm) */ #define EEPROM_EIRP_MAX_TX_POWER 0x0027 #define EEPROM_EIRP_MAX_TX_POWER_2GHZ FIELD16(0x00ff) #define EEPROM_EIRP_MAX_TX_POWER_5GHZ FIELD16(0xff00) /* * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power. * This is delta in 40MHZ. * VALUE: Tx Power dalta value, MAX=4(unit: dbm) * TYPE: 1: Plus the delta value, 0: minus the delta value * ENABLE: enable tx power compensation for 40BW */ #define EEPROM_TXPOWER_DELTA 0x0028 #define EEPROM_TXPOWER_DELTA_VALUE_2G FIELD16(0x003f) #define EEPROM_TXPOWER_DELTA_TYPE_2G FIELD16(0x0040) #define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080) #define EEPROM_TXPOWER_DELTA_VALUE_5G FIELD16(0x3f00) #define EEPROM_TXPOWER_DELTA_TYPE_5G FIELD16(0x4000) #define EEPROM_TXPOWER_DELTA_ENABLE_5G FIELD16(0x8000) /* * EEPROM TXPOWER 802.11BG */ #define EEPROM_TXPOWER_BG1 0x0029 #define EEPROM_TXPOWER_BG2 0x0030 #define EEPROM_TXPOWER_BG_SIZE 7 #define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11BG * MINUS4: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -4) * MINUS3: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -3) */ #define EEPROM_TSSI_BOUND_BG1 0x0037 #define EEPROM_TSSI_BOUND_BG1_MINUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG1_MINUS3 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11BG * MINUS2: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -2) * MINUS1: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -1) */ #define EEPROM_TSSI_BOUND_BG2 0x0038 #define EEPROM_TSSI_BOUND_BG2_MINUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG2_MINUS1 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11BG * REF: Reference TSSI value, no tx power changes needed * PLUS1: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 1) */ #define EEPROM_TSSI_BOUND_BG3 0x0039 #define EEPROM_TSSI_BOUND_BG3_REF FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG3_PLUS1 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11BG * PLUS2: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 2) * PLUS3: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 3) */ #define EEPROM_TSSI_BOUND_BG4 0x003a #define EEPROM_TSSI_BOUND_BG4_PLUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG4_PLUS3 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11BG * PLUS4: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 4) * AGC_STEP: Temperature compensation step. */ #define EEPROM_TSSI_BOUND_BG5 0x003b #define EEPROM_TSSI_BOUND_BG5_PLUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00) /* * EEPROM TXPOWER 802.11A */ #define EEPROM_TXPOWER_A1 0x003c #define EEPROM_TXPOWER_A2 0x0053 #define EEPROM_TXPOWER_A_SIZE 6 #define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_A_2 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11A * MINUS4: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -4) * MINUS3: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -3) */ #define EEPROM_TSSI_BOUND_A1 0x006a #define EEPROM_TSSI_BOUND_A1_MINUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A1_MINUS3 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11A * MINUS2: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -2) * MINUS1: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -1) */ #define EEPROM_TSSI_BOUND_A2 0x006b #define EEPROM_TSSI_BOUND_A2_MINUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A2_MINUS1 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11A * REF: Reference TSSI value, no tx power changes needed * PLUS1: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 1) */ #define EEPROM_TSSI_BOUND_A3 0x006c #define EEPROM_TSSI_BOUND_A3_REF FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A3_PLUS1 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11A * PLUS2: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 2) * PLUS3: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 3) */ #define EEPROM_TSSI_BOUND_A4 0x006d #define EEPROM_TSSI_BOUND_A4_PLUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A4_PLUS3 FIELD16(0xff00) /* * EEPROM temperature compensation boundaries 802.11A * PLUS4: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 4) * AGC_STEP: Temperature compensation step. */ #define EEPROM_TSSI_BOUND_A5 0x006e #define EEPROM_TSSI_BOUND_A5_PLUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A5_AGC_STEP FIELD16(0xff00) /* * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode */ #define EEPROM_TXPOWER_BYRATE 0x006f #define EEPROM_TXPOWER_BYRATE_SIZE 9 #define EEPROM_TXPOWER_BYRATE_RATE0 FIELD16(0x000f) #define EEPROM_TXPOWER_BYRATE_RATE1 FIELD16(0x00f0) #define EEPROM_TXPOWER_BYRATE_RATE2 FIELD16(0x0f00) #define EEPROM_TXPOWER_BYRATE_RATE3 FIELD16(0xf000) /* * EEPROM BBP. */ #define EEPROM_BBP_START 0x0078 #define EEPROM_BBP_SIZE 16 #define EEPROM_BBP_VALUE FIELD16(0x00ff) #define EEPROM_BBP_REG_ID FIELD16(0xff00) /* * MCU mailbox commands. * MCU_SLEEP - go to power-save mode. * arg1: 1: save as much power as possible, 0: save less power. * status: 1: success, 2: already asleep, * 3: maybe MAC is busy so can't finish this task. * MCU_RADIO_OFF * arg0: 0: do power-saving, NOT turn off radio. */ #define MCU_SLEEP 0x30 #define MCU_WAKEUP 0x31 #define MCU_RADIO_OFF 0x35 #define MCU_CURRENT 0x36 #define MCU_LED 0x50 #define MCU_LED_STRENGTH 0x51 #define MCU_LED_AG_CONF 0x52 #define MCU_LED_ACT_CONF 0x53 #define MCU_LED_LED_POLARITY 0x54 #define MCU_RADAR 0x60 #define MCU_BOOT_SIGNAL 0x72 #define MCU_ANT_SELECT 0X73 #define MCU_BBP_SIGNAL 0x80 #define MCU_POWER_SAVE 0x83 #define MCU_BAND_SELECT 0x91 /* * MCU mailbox tokens */ #define TOKEN_SLEEP 1 #define TOKEN_RADIO_OFF 2 #define TOKEN_WAKEUP 3 /* * DMA descriptor defines. */ #define TXWI_DESC_SIZE (4 * sizeof(__le32)) #define RXWI_DESC_SIZE (4 * sizeof(__le32)) /* * TX WI structure */ /* * Word0 * FRAG: 1 To inform TKIP engine this is a fragment. * MIMO_PS: The remote peer is in dynamic MIMO-PS mode * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs * BW: Channel bandwidth 0:20MHz, 1:40 MHz (for legacy rates this will * duplicate the frame to both channels). * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED * AMPDU: 1: this frame is eligible for AMPDU aggregation, the hw will * aggregate consecutive frames with the same RA and QoS TID. If * a frame A with the same RA and QoS TID but AMPDU=0 is queued * directly after a frame B with AMPDU=1, frame A might still * get aggregated into the AMPDU started by frame B. So, setting * AMPDU to 0 does _not_ necessarily mean the frame is sent as * MPDU, it can still end up in an AMPDU if the previous frame * was tagged as AMPDU. */ #define TXWI_W0_FRAG FIELD32(0x00000001) #define TXWI_W0_MIMO_PS FIELD32(0x00000002) #define TXWI_W0_CF_ACK FIELD32(0x00000004) #define TXWI_W0_TS FIELD32(0x00000008) #define TXWI_W0_AMPDU FIELD32(0x00000010) #define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0) #define TXWI_W0_TX_OP FIELD32(0x00000300) #define TXWI_W0_MCS FIELD32(0x007f0000) #define TXWI_W0_BW FIELD32(0x00800000) #define TXWI_W0_SHORT_GI FIELD32(0x01000000) #define TXWI_W0_STBC FIELD32(0x06000000) #define TXWI_W0_IFS FIELD32(0x08000000) #define TXWI_W0_PHYMODE FIELD32(0xc0000000) /* * Word1 * ACK: 0: No Ack needed, 1: Ack needed * NSEQ: 0: Don't assign hw sequence number, 1: Assign hw sequence number * BW_WIN_SIZE: BA windows size of the recipient * WIRELESS_CLI_ID: Client ID for WCID table access * MPDU_TOTAL_BYTE_COUNT: Length of 802.11 frame * PACKETID: Will be latched into the TX_STA_FIFO register once the according * frame was processed. If multiple frames are aggregated together * (AMPDU==1) the reported tx status will always contain the packet * id of the first frame. 0: Don't report tx status for this frame. * PACKETID_QUEUE: Part of PACKETID, This is the queue index (0-3) * PACKETID_ENTRY: Part of PACKETID, THis is the queue entry index (1-3) * This identification number is calculated by ((idx % 3) + 1). * The (+1) is required to prevent PACKETID to become 0. */ #define TXWI_W1_ACK FIELD32(0x00000001) #define TXWI_W1_NSEQ FIELD32(0x00000002) #define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc) #define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00) #define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) #define TXWI_W1_PACKETID FIELD32(0xf0000000) #define TXWI_W1_PACKETID_QUEUE FIELD32(0x30000000) #define TXWI_W1_PACKETID_ENTRY FIELD32(0xc0000000) /* * Word2 */ #define TXWI_W2_IV FIELD32(0xffffffff) /* * Word3 */ #define TXWI_W3_EIV FIELD32(0xffffffff) /* * RX WI structure */ /* * Word0 */ #define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff) #define RXWI_W0_KEY_INDEX FIELD32(0x00000300) #define RXWI_W0_BSSID FIELD32(0x00001c00) #define RXWI_W0_UDF FIELD32(0x0000e000) #define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) #define RXWI_W0_TID FIELD32(0xf0000000) /* * Word1 */ #define RXWI_W1_FRAG FIELD32(0x0000000f) #define RXWI_W1_SEQUENCE FIELD32(0x0000fff0) #define RXWI_W1_MCS FIELD32(0x007f0000) #define RXWI_W1_BW FIELD32(0x00800000) #define RXWI_W1_SHORT_GI FIELD32(0x01000000) #define RXWI_W1_STBC FIELD32(0x06000000) #define RXWI_W1_PHYMODE FIELD32(0xc0000000) /* * Word2 */ #define RXWI_W2_RSSI0 FIELD32(0x000000ff) #define RXWI_W2_RSSI1 FIELD32(0x0000ff00) #define RXWI_W2_RSSI2 FIELD32(0x00ff0000) /* * Word3 */ #define RXWI_W3_SNR0 FIELD32(0x000000ff) #define RXWI_W3_SNR1 FIELD32(0x0000ff00) /* * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_G_TXPOWER 0 #define MIN_A_TXPOWER -7 #define MAX_G_TXPOWER 31 #define MAX_A_TXPOWER 15 #define DEFAULT_TXPOWER 5 #define TXPOWER_G_FROM_DEV(__txpower) \ ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_G_TO_DEV(__txpower) \ clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER) #define TXPOWER_A_FROM_DEV(__txpower) \ ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_A_TO_DEV(__txpower) \ clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER) /* * Board's maximun TX power limitation */ #define EIRP_MAX_TX_POWER_LIMIT 0x50 /* * Number of TBTT intervals after which we have to adjust * the hw beacon timer. */ #define BCN_TBTT_OFFSET 64 /* * RT2800 driver data structure */ struct rt2800_drv_data { u8 calibration_bw20; u8 calibration_bw40; u8 bbp25; u8 bbp26; u8 txmixer_gain_24g; u8 txmixer_gain_5g; unsigned int tbtt_tick; }; #endif /* RT2800_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2500usb.h0000644000175000017500000005166212026211315023351 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2500usb Abstract: Data structures and registers for the rt2500usb module. Supported chipsets: RT2570. */ #ifndef RT2500USB_H #define RT2500USB_H /* * RF chip defines. */ #define RF2522 0x0000 #define RF2523 0x0001 #define RF2524 0x0002 #define RF2525 0x0003 #define RF2525E 0x0005 #define RF5222 0x0010 /* * RT2570 version */ #define RT2570_VERSION_B 2 #define RT2570_VERSION_C 3 #define RT2570_VERSION_D 4 /* * Signal information. * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 120 /* * Register layout information. */ #define CSR_REG_BASE 0x0400 #define CSR_REG_SIZE 0x0100 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x006a #define BBP_BASE 0x0000 #define BBP_SIZE 0x0060 #define RF_BASE 0x0004 #define RF_SIZE 0x0010 /* * Number of TX queues. */ #define NUM_TX_QUEUES 2 /* * Control/Status Registers(CSR). * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * MAC_CSR0: ASIC revision number. */ #define MAC_CSR0 0x0400 /* * MAC_CSR1: System control. * SOFT_RESET: Software reset, 1: reset, 0: normal. * BBP_RESET: Hardware reset, 1: reset, 0, release. * HOST_READY: Host ready after initialization. */ #define MAC_CSR1 0x0402 #define MAC_CSR1_SOFT_RESET FIELD16(0x00000001) #define MAC_CSR1_BBP_RESET FIELD16(0x00000002) #define MAC_CSR1_HOST_READY FIELD16(0x00000004) /* * MAC_CSR2: STA MAC register 0. */ #define MAC_CSR2 0x0404 #define MAC_CSR2_BYTE0 FIELD16(0x00ff) #define MAC_CSR2_BYTE1 FIELD16(0xff00) /* * MAC_CSR3: STA MAC register 1. */ #define MAC_CSR3 0x0406 #define MAC_CSR3_BYTE2 FIELD16(0x00ff) #define MAC_CSR3_BYTE3 FIELD16(0xff00) /* * MAC_CSR4: STA MAC register 2. */ #define MAC_CSR4 0X0408 #define MAC_CSR4_BYTE4 FIELD16(0x00ff) #define MAC_CSR4_BYTE5 FIELD16(0xff00) /* * MAC_CSR5: BSSID register 0. */ #define MAC_CSR5 0x040a #define MAC_CSR5_BYTE0 FIELD16(0x00ff) #define MAC_CSR5_BYTE1 FIELD16(0xff00) /* * MAC_CSR6: BSSID register 1. */ #define MAC_CSR6 0x040c #define MAC_CSR6_BYTE2 FIELD16(0x00ff) #define MAC_CSR6_BYTE3 FIELD16(0xff00) /* * MAC_CSR7: BSSID register 2. */ #define MAC_CSR7 0x040e #define MAC_CSR7_BYTE4 FIELD16(0x00ff) #define MAC_CSR7_BYTE5 FIELD16(0xff00) /* * MAC_CSR8: Max frame length. */ #define MAC_CSR8 0x0410 #define MAC_CSR8_MAX_FRAME_UNIT FIELD16(0x0fff) /* * Misc MAC_CSR registers. * MAC_CSR9: Timer control. * MAC_CSR10: Slot time. * MAC_CSR11: SIFS. * MAC_CSR12: EIFS. * MAC_CSR13: Power mode0. * MAC_CSR14: Power mode1. * MAC_CSR15: Power saving transition0 * MAC_CSR16: Power saving transition1 */ #define MAC_CSR9 0x0412 #define MAC_CSR10 0x0414 #define MAC_CSR11 0x0416 #define MAC_CSR12 0x0418 #define MAC_CSR13 0x041a #define MAC_CSR14 0x041c #define MAC_CSR15 0x041e #define MAC_CSR16 0x0420 /* * MAC_CSR17: Manual power control / status register. * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. * SET_STATE: Set state. Write 1 to trigger, self cleared. * BBP_DESIRE_STATE: BBP desired state. * RF_DESIRE_STATE: RF desired state. * BBP_CURRENT_STATE: BBP current state. * RF_CURRENT_STATE: RF current state. * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. */ #define MAC_CSR17 0x0422 #define MAC_CSR17_SET_STATE FIELD16(0x0001) #define MAC_CSR17_BBP_DESIRE_STATE FIELD16(0x0006) #define MAC_CSR17_RF_DESIRE_STATE FIELD16(0x0018) #define MAC_CSR17_BBP_CURR_STATE FIELD16(0x0060) #define MAC_CSR17_RF_CURR_STATE FIELD16(0x0180) #define MAC_CSR17_PUT_TO_SLEEP FIELD16(0x0200) /* * MAC_CSR18: Wakeup timer register. * DELAY_AFTER_BEACON: Delay after Tbcn expired in units of 1/16 TU. * BEACONS_BEFORE_WAKEUP: Number of beacon before wakeup. * AUTO_WAKE: Enable auto wakeup / sleep mechanism. */ #define MAC_CSR18 0x0424 #define MAC_CSR18_DELAY_AFTER_BEACON FIELD16(0x00ff) #define MAC_CSR18_BEACONS_BEFORE_WAKEUP FIELD16(0x7f00) #define MAC_CSR18_AUTO_WAKE FIELD16(0x8000) /* * MAC_CSR19: GPIO control register. * MAC_CSR19_VALx: GPIO value * MAC_CSR19_DIRx: GPIO direction: 0 = input; 1 = output */ #define MAC_CSR19 0x0426 #define MAC_CSR19_VAL0 FIELD16(0x0001) #define MAC_CSR19_VAL1 FIELD16(0x0002) #define MAC_CSR19_VAL2 FIELD16(0x0004) #define MAC_CSR19_VAL3 FIELD16(0x0008) #define MAC_CSR19_VAL4 FIELD16(0x0010) #define MAC_CSR19_VAL5 FIELD16(0x0020) #define MAC_CSR19_VAL6 FIELD16(0x0040) #define MAC_CSR19_VAL7 FIELD16(0x0080) #define MAC_CSR19_DIR0 FIELD16(0x0100) #define MAC_CSR19_DIR1 FIELD16(0x0200) #define MAC_CSR19_DIR2 FIELD16(0x0400) #define MAC_CSR19_DIR3 FIELD16(0x0800) #define MAC_CSR19_DIR4 FIELD16(0x1000) #define MAC_CSR19_DIR5 FIELD16(0x2000) #define MAC_CSR19_DIR6 FIELD16(0x4000) #define MAC_CSR19_DIR7 FIELD16(0x8000) /* * MAC_CSR20: LED control register. * ACTIVITY: 0: idle, 1: active. * LINK: 0: linkoff, 1: linkup. * ACTIVITY_POLARITY: 0: active low, 1: active high. */ #define MAC_CSR20 0x0428 #define MAC_CSR20_ACTIVITY FIELD16(0x0001) #define MAC_CSR20_LINK FIELD16(0x0002) #define MAC_CSR20_ACTIVITY_POLARITY FIELD16(0x0004) /* * MAC_CSR21: LED control register. * ON_PERIOD: On period, default 70ms. * OFF_PERIOD: Off period, default 30ms. */ #define MAC_CSR21 0x042a #define MAC_CSR21_ON_PERIOD FIELD16(0x00ff) #define MAC_CSR21_OFF_PERIOD FIELD16(0xff00) /* * MAC_CSR22: Collision window control register. */ #define MAC_CSR22 0x042c /* * Transmit related CSRs. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * TXRX_CSR0: Security control register. */ #define TXRX_CSR0 0x0440 #define TXRX_CSR0_ALGORITHM FIELD16(0x0007) #define TXRX_CSR0_IV_OFFSET FIELD16(0x01f8) #define TXRX_CSR0_KEY_ID FIELD16(0x1e00) /* * TXRX_CSR1: TX configuration. * ACK_TIMEOUT: ACK Timeout in unit of 1-us. * TSF_OFFSET: TSF offset in MAC header. * AUTO_SEQUENCE: Let ASIC control frame sequence number. */ #define TXRX_CSR1 0x0442 #define TXRX_CSR1_ACK_TIMEOUT FIELD16(0x00ff) #define TXRX_CSR1_TSF_OFFSET FIELD16(0x7f00) #define TXRX_CSR1_AUTO_SEQUENCE FIELD16(0x8000) /* * TXRX_CSR2: RX control. * DISABLE_RX: Disable rx engine. * DROP_CRC: Drop crc error. * DROP_PHYSICAL: Drop physical error. * DROP_CONTROL: Drop control frame. * DROP_NOT_TO_ME: Drop not to me unicast frame. * DROP_TODS: Drop frame tods bit is true. * DROP_VERSION_ERROR: Drop version error frame. * DROP_MCAST: Drop multicast frames. * DROP_BCAST: Drop broadcast frames. */ #define TXRX_CSR2 0x0444 #define TXRX_CSR2_DISABLE_RX FIELD16(0x0001) #define TXRX_CSR2_DROP_CRC FIELD16(0x0002) #define TXRX_CSR2_DROP_PHYSICAL FIELD16(0x0004) #define TXRX_CSR2_DROP_CONTROL FIELD16(0x0008) #define TXRX_CSR2_DROP_NOT_TO_ME FIELD16(0x0010) #define TXRX_CSR2_DROP_TODS FIELD16(0x0020) #define TXRX_CSR2_DROP_VERSION_ERROR FIELD16(0x0040) #define TXRX_CSR2_DROP_MULTICAST FIELD16(0x0200) #define TXRX_CSR2_DROP_BROADCAST FIELD16(0x0400) /* * RX BBP ID registers * TXRX_CSR3: CCK RX BBP ID. * TXRX_CSR4: OFDM RX BBP ID. */ #define TXRX_CSR3 0x0446 #define TXRX_CSR4 0x0448 /* * TXRX_CSR5: CCK TX BBP ID0. */ #define TXRX_CSR5 0x044a #define TXRX_CSR5_BBP_ID0 FIELD16(0x007f) #define TXRX_CSR5_BBP_ID0_VALID FIELD16(0x0080) #define TXRX_CSR5_BBP_ID1 FIELD16(0x7f00) #define TXRX_CSR5_BBP_ID1_VALID FIELD16(0x8000) /* * TXRX_CSR6: CCK TX BBP ID1. */ #define TXRX_CSR6 0x044c #define TXRX_CSR6_BBP_ID0 FIELD16(0x007f) #define TXRX_CSR6_BBP_ID0_VALID FIELD16(0x0080) #define TXRX_CSR6_BBP_ID1 FIELD16(0x7f00) #define TXRX_CSR6_BBP_ID1_VALID FIELD16(0x8000) /* * TXRX_CSR7: OFDM TX BBP ID0. */ #define TXRX_CSR7 0x044e #define TXRX_CSR7_BBP_ID0 FIELD16(0x007f) #define TXRX_CSR7_BBP_ID0_VALID FIELD16(0x0080) #define TXRX_CSR7_BBP_ID1 FIELD16(0x7f00) #define TXRX_CSR7_BBP_ID1_VALID FIELD16(0x8000) /* * TXRX_CSR8: OFDM TX BBP ID1. */ #define TXRX_CSR8 0x0450 #define TXRX_CSR8_BBP_ID0 FIELD16(0x007f) #define TXRX_CSR8_BBP_ID0_VALID FIELD16(0x0080) #define TXRX_CSR8_BBP_ID1 FIELD16(0x7f00) #define TXRX_CSR8_BBP_ID1_VALID FIELD16(0x8000) /* * TXRX_CSR9: TX ACK time-out. */ #define TXRX_CSR9 0x0452 /* * TXRX_CSR10: Auto responder control. */ #define TXRX_CSR10 0x0454 #define TXRX_CSR10_AUTORESPOND_PREAMBLE FIELD16(0x0004) /* * TXRX_CSR11: Auto responder basic rate. */ #define TXRX_CSR11 0x0456 /* * ACK/CTS time registers. */ #define TXRX_CSR12 0x0458 #define TXRX_CSR13 0x045a #define TXRX_CSR14 0x045c #define TXRX_CSR15 0x045e #define TXRX_CSR16 0x0460 #define TXRX_CSR17 0x0462 /* * TXRX_CSR18: Synchronization control register. */ #define TXRX_CSR18 0x0464 #define TXRX_CSR18_OFFSET FIELD16(0x000f) #define TXRX_CSR18_INTERVAL FIELD16(0xfff0) /* * TXRX_CSR19: Synchronization control register. * TSF_COUNT: Enable TSF auto counting. * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. * TBCN: Enable Tbcn with reload value. * BEACON_GEN: Enable beacon generator. */ #define TXRX_CSR19 0x0466 #define TXRX_CSR19_TSF_COUNT FIELD16(0x0001) #define TXRX_CSR19_TSF_SYNC FIELD16(0x0006) #define TXRX_CSR19_TBCN FIELD16(0x0008) #define TXRX_CSR19_BEACON_GEN FIELD16(0x0010) /* * TXRX_CSR20: Tx BEACON offset time control register. * OFFSET: In units of usec. * BCN_EXPECT_WINDOW: Default: 2^CWmin */ #define TXRX_CSR20 0x0468 #define TXRX_CSR20_OFFSET FIELD16(0x1fff) #define TXRX_CSR20_BCN_EXPECT_WINDOW FIELD16(0xe000) /* * TXRX_CSR21 */ #define TXRX_CSR21 0x046a /* * Encryption related CSRs. * */ /* * SEC_CSR0: Shared key 0, word 0 * SEC_CSR1: Shared key 0, word 1 * SEC_CSR2: Shared key 0, word 2 * SEC_CSR3: Shared key 0, word 3 * SEC_CSR4: Shared key 0, word 4 * SEC_CSR5: Shared key 0, word 5 * SEC_CSR6: Shared key 0, word 6 * SEC_CSR7: Shared key 0, word 7 */ #define SEC_CSR0 0x0480 #define SEC_CSR1 0x0482 #define SEC_CSR2 0x0484 #define SEC_CSR3 0x0486 #define SEC_CSR4 0x0488 #define SEC_CSR5 0x048a #define SEC_CSR6 0x048c #define SEC_CSR7 0x048e /* * SEC_CSR8: Shared key 1, word 0 * SEC_CSR9: Shared key 1, word 1 * SEC_CSR10: Shared key 1, word 2 * SEC_CSR11: Shared key 1, word 3 * SEC_CSR12: Shared key 1, word 4 * SEC_CSR13: Shared key 1, word 5 * SEC_CSR14: Shared key 1, word 6 * SEC_CSR15: Shared key 1, word 7 */ #define SEC_CSR8 0x0490 #define SEC_CSR9 0x0492 #define SEC_CSR10 0x0494 #define SEC_CSR11 0x0496 #define SEC_CSR12 0x0498 #define SEC_CSR13 0x049a #define SEC_CSR14 0x049c #define SEC_CSR15 0x049e /* * SEC_CSR16: Shared key 2, word 0 * SEC_CSR17: Shared key 2, word 1 * SEC_CSR18: Shared key 2, word 2 * SEC_CSR19: Shared key 2, word 3 * SEC_CSR20: Shared key 2, word 4 * SEC_CSR21: Shared key 2, word 5 * SEC_CSR22: Shared key 2, word 6 * SEC_CSR23: Shared key 2, word 7 */ #define SEC_CSR16 0x04a0 #define SEC_CSR17 0x04a2 #define SEC_CSR18 0X04A4 #define SEC_CSR19 0x04a6 #define SEC_CSR20 0x04a8 #define SEC_CSR21 0x04aa #define SEC_CSR22 0x04ac #define SEC_CSR23 0x04ae /* * SEC_CSR24: Shared key 3, word 0 * SEC_CSR25: Shared key 3, word 1 * SEC_CSR26: Shared key 3, word 2 * SEC_CSR27: Shared key 3, word 3 * SEC_CSR28: Shared key 3, word 4 * SEC_CSR29: Shared key 3, word 5 * SEC_CSR30: Shared key 3, word 6 * SEC_CSR31: Shared key 3, word 7 */ #define SEC_CSR24 0x04b0 #define SEC_CSR25 0x04b2 #define SEC_CSR26 0x04b4 #define SEC_CSR27 0x04b6 #define SEC_CSR28 0x04b8 #define SEC_CSR29 0x04ba #define SEC_CSR30 0x04bc #define SEC_CSR31 0x04be #define KEY_ENTRY(__idx) \ ( SEC_CSR0 + ((__idx) * 16) ) /* * PHY control registers. */ /* * PHY_CSR0: RF switching timing control. */ #define PHY_CSR0 0x04c0 /* * PHY_CSR1: TX PA configuration. */ #define PHY_CSR1 0x04c2 /* * MAC configuration registers. */ /* * PHY_CSR2: TX MAC configuration. * NOTE: Both register fields are complete dummy, * documentation and legacy drivers are unclear un * what this register means or what fields exists. */ #define PHY_CSR2 0x04c4 #define PHY_CSR2_LNA FIELD16(0x0002) #define PHY_CSR2_LNA_MODE FIELD16(0x3000) /* * PHY_CSR3: RX MAC configuration. */ #define PHY_CSR3 0x04c6 /* * PHY_CSR4: Interface configuration. */ #define PHY_CSR4 0x04c8 #define PHY_CSR4_LOW_RF_LE FIELD16(0x0001) /* * BBP pre-TX registers. * PHY_CSR5: BBP pre-TX CCK. */ #define PHY_CSR5 0x04ca #define PHY_CSR5_CCK FIELD16(0x0003) #define PHY_CSR5_CCK_FLIP FIELD16(0x0004) /* * BBP pre-TX registers. * PHY_CSR6: BBP pre-TX OFDM. */ #define PHY_CSR6 0x04cc #define PHY_CSR6_OFDM FIELD16(0x0003) #define PHY_CSR6_OFDM_FLIP FIELD16(0x0004) /* * PHY_CSR7: BBP access register 0. * BBP_DATA: BBP data. * BBP_REG_ID: BBP register ID. * BBP_READ_CONTROL: 0: write, 1: read. */ #define PHY_CSR7 0x04ce #define PHY_CSR7_DATA FIELD16(0x00ff) #define PHY_CSR7_REG_ID FIELD16(0x7f00) #define PHY_CSR7_READ_CONTROL FIELD16(0x8000) /* * PHY_CSR8: BBP access register 1. * BBP_BUSY: ASIC is busy execute BBP programming. */ #define PHY_CSR8 0x04d0 #define PHY_CSR8_BUSY FIELD16(0x0001) /* * PHY_CSR9: RF access register. * RF_VALUE: Register value + id to program into rf/if. */ #define PHY_CSR9 0x04d2 #define PHY_CSR9_RF_VALUE FIELD16(0xffff) /* * PHY_CSR10: RF access register. * RF_VALUE: Register value + id to program into rf/if. * RF_NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). * RF_IF_SELECT: Chip to program: 0: rf, 1: if. * RF_PLL_LD: Rf pll_ld status. * RF_BUSY: 1: asic is busy execute rf programming. */ #define PHY_CSR10 0x04d4 #define PHY_CSR10_RF_VALUE FIELD16(0x00ff) #define PHY_CSR10_RF_NUMBER_OF_BITS FIELD16(0x1f00) #define PHY_CSR10_RF_IF_SELECT FIELD16(0x2000) #define PHY_CSR10_RF_PLL_LD FIELD16(0x4000) #define PHY_CSR10_RF_BUSY FIELD16(0x8000) /* * STA_CSR0: FCS error count. * FCS_ERROR: FCS error count, cleared when read. */ #define STA_CSR0 0x04e0 #define STA_CSR0_FCS_ERROR FIELD16(0xffff) /* * STA_CSR1: PLCP error count. */ #define STA_CSR1 0x04e2 /* * STA_CSR2: LONG error count. */ #define STA_CSR2 0x04e4 /* * STA_CSR3: CCA false alarm. * FALSE_CCA_ERROR: False CCA error count, cleared when read. */ #define STA_CSR3 0x04e6 #define STA_CSR3_FALSE_CCA_ERROR FIELD16(0xffff) /* * STA_CSR4: RX FIFO overflow. */ #define STA_CSR4 0x04e8 /* * STA_CSR5: Beacon sent counter. */ #define STA_CSR5 0x04ea /* * Statistics registers */ #define STA_CSR6 0x04ec #define STA_CSR7 0x04ee #define STA_CSR8 0x04f0 #define STA_CSR9 0x04f2 #define STA_CSR10 0x04f4 /* * BBP registers. * The wordsize of the BBP is 8 bits. */ /* * R2: TX antenna control */ #define BBP_R2_TX_ANTENNA FIELD8(0x03) #define BBP_R2_TX_IQ_FLIP FIELD8(0x04) /* * R14: RX antenna control */ #define BBP_R14_RX_ANTENNA FIELD8(0x03) #define BBP_R14_RX_IQ_FLIP FIELD8(0x04) /* * RF registers. */ /* * RF 1 */ #define RF1_TUNER FIELD32(0x00020000) /* * RF 3 */ #define RF3_TUNER FIELD32(0x00000100) #define RF3_TXPOWER FIELD32(0x00003e00) /* * EEPROM contents. */ /* * HW MAC address. */ #define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) #define EEPROM_MAC_ADDR1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) #define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) /* * EEPROM antenna. * ANTENNA_NUM: Number of antenna's. * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * LED_MODE: 0: default, 1: TX/RX activity, 2: Single (ignore link), 3: rsvd. * DYN_TXAGC: Dynamic TX AGC control. * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. * RF_TYPE: Rf_type of this adapter. */ #define EEPROM_ANTENNA 0x000b #define EEPROM_ANTENNA_NUM FIELD16(0x0003) #define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) #define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) #define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) #define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) #define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) #define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) /* * EEPROM NIC config. * CARDBUS_ACCEL: 0: enable, 1: disable. * DYN_BBP_TUNE: 0: enable, 1: disable. * CCK_TX_POWER: CCK TX power compensation. */ #define EEPROM_NIC 0x000c #define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) #define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) #define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) /* * EEPROM geography. * GEO: Default geography setting for device. */ #define EEPROM_GEOGRAPHY 0x000d #define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) /* * EEPROM BBP. */ #define EEPROM_BBP_START 0x000e #define EEPROM_BBP_SIZE 16 #define EEPROM_BBP_VALUE FIELD16(0x00ff) #define EEPROM_BBP_REG_ID FIELD16(0xff00) /* * EEPROM TXPOWER */ #define EEPROM_TXPOWER_START 0x001e #define EEPROM_TXPOWER_SIZE 7 #define EEPROM_TXPOWER_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_2 FIELD16(0xff00) /* * EEPROM Tuning threshold */ #define EEPROM_BBPTUNE 0x0030 #define EEPROM_BBPTUNE_THRESHOLD FIELD16(0x00ff) /* * EEPROM BBP R24 Tuning. */ #define EEPROM_BBPTUNE_R24 0x0031 #define EEPROM_BBPTUNE_R24_LOW FIELD16(0x00ff) #define EEPROM_BBPTUNE_R24_HIGH FIELD16(0xff00) /* * EEPROM BBP R25 Tuning. */ #define EEPROM_BBPTUNE_R25 0x0032 #define EEPROM_BBPTUNE_R25_LOW FIELD16(0x00ff) #define EEPROM_BBPTUNE_R25_HIGH FIELD16(0xff00) /* * EEPROM BBP R24 Tuning. */ #define EEPROM_BBPTUNE_R61 0x0033 #define EEPROM_BBPTUNE_R61_LOW FIELD16(0x00ff) #define EEPROM_BBPTUNE_R61_HIGH FIELD16(0xff00) /* * EEPROM BBP VGC Tuning. */ #define EEPROM_BBPTUNE_VGC 0x0034 #define EEPROM_BBPTUNE_VGCUPPER FIELD16(0x00ff) #define EEPROM_BBPTUNE_VGCLOWER FIELD16(0xff00) /* * EEPROM BBP R17 Tuning. */ #define EEPROM_BBPTUNE_R17 0x0035 #define EEPROM_BBPTUNE_R17_LOW FIELD16(0x00ff) #define EEPROM_BBPTUNE_R17_HIGH FIELD16(0xff00) /* * RSSI <-> dBm offset calibration */ #define EEPROM_CALIBRATE_OFFSET 0x0036 #define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) /* * DMA descriptor defines. */ #define TXD_DESC_SIZE ( 5 * sizeof(__le32) ) #define RXD_DESC_SIZE ( 4 * sizeof(__le32) ) /* * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. */ /* * Word0 */ #define TXD_W0_PACKET_ID FIELD32(0x0000000f) #define TXD_W0_RETRY_LIMIT FIELD32(0x000000f0) #define TXD_W0_MORE_FRAG FIELD32(0x00000100) #define TXD_W0_ACK FIELD32(0x00000200) #define TXD_W0_TIMESTAMP FIELD32(0x00000400) #define TXD_W0_OFDM FIELD32(0x00000800) #define TXD_W0_NEW_SEQ FIELD32(0x00001000) #define TXD_W0_IFS FIELD32(0x00006000) #define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) #define TXD_W0_CIPHER FIELD32(0x20000000) #define TXD_W0_KEY_ID FIELD32(0xc0000000) /* * Word1 */ #define TXD_W1_IV_OFFSET FIELD32(0x0000003f) #define TXD_W1_AIFS FIELD32(0x000000c0) #define TXD_W1_CWMIN FIELD32(0x00000f00) #define TXD_W1_CWMAX FIELD32(0x0000f000) /* * Word2: PLCP information */ #define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) #define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) #define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) #define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) /* * Word3 */ #define TXD_W3_IV FIELD32(0xffffffff) /* * Word4 */ #define TXD_W4_EIV FIELD32(0xffffffff) /* * RX descriptor format for RX Ring. */ /* * Word0 */ #define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) #define RXD_W0_MULTICAST FIELD32(0x00000004) #define RXD_W0_BROADCAST FIELD32(0x00000008) #define RXD_W0_MY_BSS FIELD32(0x00000010) #define RXD_W0_CRC_ERROR FIELD32(0x00000020) #define RXD_W0_OFDM FIELD32(0x00000040) #define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) #define RXD_W0_CIPHER FIELD32(0x00000100) #define RXD_W0_CIPHER_ERROR FIELD32(0x00000200) #define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) /* * Word1 */ #define RXD_W1_RSSI FIELD32(0x000000ff) #define RXD_W1_SIGNAL FIELD32(0x0000ff00) /* * Word2 */ #define RXD_W2_IV FIELD32(0xffffffff) /* * Word3 */ #define RXD_W3_EIV FIELD32(0xffffffff) /* * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 #define TXPOWER_FROM_DEV(__txpower) \ (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2500pci.h0000644000175000017500000010641312026211315023326 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2500pci Abstract: Data structures and registers for the rt2500pci module. Supported chipsets: RT2560. */ #ifndef RT2500PCI_H #define RT2500PCI_H /* * RF chip defines. */ #define RF2522 0x0000 #define RF2523 0x0001 #define RF2524 0x0002 #define RF2525 0x0003 #define RF2525E 0x0004 #define RF5222 0x0010 /* * RT2560 version */ #define RT2560_VERSION_B 2 #define RT2560_VERSION_C 3 #define RT2560_VERSION_D 4 /* * Signal information. * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 121 /* * Register layout information. */ #define CSR_REG_BASE 0x0000 #define CSR_REG_SIZE 0x0174 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0200 #define BBP_BASE 0x0000 #define BBP_SIZE 0x0040 #define RF_BASE 0x0004 #define RF_SIZE 0x0010 /* * Number of TX queues. */ #define NUM_TX_QUEUES 2 /* * Control/Status Registers(CSR). * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * CSR0: ASIC revision number. */ #define CSR0 0x0000 #define CSR0_REVISION FIELD32(0x0000ffff) /* * CSR1: System control register. * SOFT_RESET: Software reset, 1: reset, 0: normal. * BBP_RESET: Hardware reset, 1: reset, 0, release. * HOST_READY: Host ready after initialization. */ #define CSR1 0x0004 #define CSR1_SOFT_RESET FIELD32(0x00000001) #define CSR1_BBP_RESET FIELD32(0x00000002) #define CSR1_HOST_READY FIELD32(0x00000004) /* * CSR2: System admin status register (invalid). */ #define CSR2 0x0008 /* * CSR3: STA MAC address register 0. */ #define CSR3 0x000c #define CSR3_BYTE0 FIELD32(0x000000ff) #define CSR3_BYTE1 FIELD32(0x0000ff00) #define CSR3_BYTE2 FIELD32(0x00ff0000) #define CSR3_BYTE3 FIELD32(0xff000000) /* * CSR4: STA MAC address register 1. */ #define CSR4 0x0010 #define CSR4_BYTE4 FIELD32(0x000000ff) #define CSR4_BYTE5 FIELD32(0x0000ff00) /* * CSR5: BSSID register 0. */ #define CSR5 0x0014 #define CSR5_BYTE0 FIELD32(0x000000ff) #define CSR5_BYTE1 FIELD32(0x0000ff00) #define CSR5_BYTE2 FIELD32(0x00ff0000) #define CSR5_BYTE3 FIELD32(0xff000000) /* * CSR6: BSSID register 1. */ #define CSR6 0x0018 #define CSR6_BYTE4 FIELD32(0x000000ff) #define CSR6_BYTE5 FIELD32(0x0000ff00) /* * CSR7: Interrupt source register. * Write 1 to clear. * TBCN_EXPIRE: Beacon timer expired interrupt. * TWAKE_EXPIRE: Wakeup timer expired interrupt. * TATIMW_EXPIRE: Timer of atim window expired interrupt. * TXDONE_TXRING: Tx ring transmit done interrupt. * TXDONE_ATIMRING: Atim ring transmit done interrupt. * TXDONE_PRIORING: Priority ring transmit done interrupt. * RXDONE: Receive done interrupt. * DECRYPTION_DONE: Decryption done interrupt. * ENCRYPTION_DONE: Encryption done interrupt. * UART1_TX_TRESHOLD: UART1 TX reaches threshold. * UART1_RX_TRESHOLD: UART1 RX reaches threshold. * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. * UART1_TX_BUFF_ERROR: UART1 TX buffer error. * UART1_RX_BUFF_ERROR: UART1 RX buffer error. * UART2_TX_TRESHOLD: UART2 TX reaches threshold. * UART2_RX_TRESHOLD: UART2 RX reaches threshold. * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. * UART2_TX_BUFF_ERROR: UART2 TX buffer error. * UART2_RX_BUFF_ERROR: UART2 RX buffer error. * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). */ #define CSR7 0x001c #define CSR7_TBCN_EXPIRE FIELD32(0x00000001) #define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) #define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) #define CSR7_TXDONE_TXRING FIELD32(0x00000008) #define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) #define CSR7_TXDONE_PRIORING FIELD32(0x00000020) #define CSR7_RXDONE FIELD32(0x00000040) #define CSR7_DECRYPTION_DONE FIELD32(0x00000080) #define CSR7_ENCRYPTION_DONE FIELD32(0x00000100) #define CSR7_UART1_TX_TRESHOLD FIELD32(0x00000200) #define CSR7_UART1_RX_TRESHOLD FIELD32(0x00000400) #define CSR7_UART1_IDLE_TRESHOLD FIELD32(0x00000800) #define CSR7_UART1_TX_BUFF_ERROR FIELD32(0x00001000) #define CSR7_UART1_RX_BUFF_ERROR FIELD32(0x00002000) #define CSR7_UART2_TX_TRESHOLD FIELD32(0x00004000) #define CSR7_UART2_RX_TRESHOLD FIELD32(0x00008000) #define CSR7_UART2_IDLE_TRESHOLD FIELD32(0x00010000) #define CSR7_UART2_TX_BUFF_ERROR FIELD32(0x00020000) #define CSR7_UART2_RX_BUFF_ERROR FIELD32(0x00040000) #define CSR7_TIMER_CSR3_EXPIRE FIELD32(0x00080000) /* * CSR8: Interrupt mask register. * Write 1 to mask interrupt. * TBCN_EXPIRE: Beacon timer expired interrupt. * TWAKE_EXPIRE: Wakeup timer expired interrupt. * TATIMW_EXPIRE: Timer of atim window expired interrupt. * TXDONE_TXRING: Tx ring transmit done interrupt. * TXDONE_ATIMRING: Atim ring transmit done interrupt. * TXDONE_PRIORING: Priority ring transmit done interrupt. * RXDONE: Receive done interrupt. * DECRYPTION_DONE: Decryption done interrupt. * ENCRYPTION_DONE: Encryption done interrupt. * UART1_TX_TRESHOLD: UART1 TX reaches threshold. * UART1_RX_TRESHOLD: UART1 RX reaches threshold. * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. * UART1_TX_BUFF_ERROR: UART1 TX buffer error. * UART1_RX_BUFF_ERROR: UART1 RX buffer error. * UART2_TX_TRESHOLD: UART2 TX reaches threshold. * UART2_RX_TRESHOLD: UART2 RX reaches threshold. * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. * UART2_TX_BUFF_ERROR: UART2 TX buffer error. * UART2_RX_BUFF_ERROR: UART2 RX buffer error. * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). */ #define CSR8 0x0020 #define CSR8_TBCN_EXPIRE FIELD32(0x00000001) #define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) #define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) #define CSR8_TXDONE_TXRING FIELD32(0x00000008) #define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) #define CSR8_TXDONE_PRIORING FIELD32(0x00000020) #define CSR8_RXDONE FIELD32(0x00000040) #define CSR8_DECRYPTION_DONE FIELD32(0x00000080) #define CSR8_ENCRYPTION_DONE FIELD32(0x00000100) #define CSR8_UART1_TX_TRESHOLD FIELD32(0x00000200) #define CSR8_UART1_RX_TRESHOLD FIELD32(0x00000400) #define CSR8_UART1_IDLE_TRESHOLD FIELD32(0x00000800) #define CSR8_UART1_TX_BUFF_ERROR FIELD32(0x00001000) #define CSR8_UART1_RX_BUFF_ERROR FIELD32(0x00002000) #define CSR8_UART2_TX_TRESHOLD FIELD32(0x00004000) #define CSR8_UART2_RX_TRESHOLD FIELD32(0x00008000) #define CSR8_UART2_IDLE_TRESHOLD FIELD32(0x00010000) #define CSR8_UART2_TX_BUFF_ERROR FIELD32(0x00020000) #define CSR8_UART2_RX_BUFF_ERROR FIELD32(0x00040000) #define CSR8_TIMER_CSR3_EXPIRE FIELD32(0x00080000) /* * CSR9: Maximum frame length register. * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. */ #define CSR9 0x0024 #define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) /* * SECCSR0: WEP control register. * KICK_DECRYPT: Kick decryption engine, self-clear. * ONE_SHOT: 0: ring mode, 1: One shot only mode. * DESC_ADDRESS: Descriptor physical address of frame. */ #define SECCSR0 0x0028 #define SECCSR0_KICK_DECRYPT FIELD32(0x00000001) #define SECCSR0_ONE_SHOT FIELD32(0x00000002) #define SECCSR0_DESC_ADDRESS FIELD32(0xfffffffc) /* * CSR11: Back-off control register. * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). * SLOT_TIME: Slot time, default is 20us for 802.11b * CW_SELECT: CWmin/CWmax selection, 1: Register, 0: TXD. * LONG_RETRY: Long retry count. * SHORT_RETRY: Short retry count. */ #define CSR11 0x002c #define CSR11_CWMIN FIELD32(0x0000000f) #define CSR11_CWMAX FIELD32(0x000000f0) #define CSR11_SLOT_TIME FIELD32(0x00001f00) #define CSR11_CW_SELECT FIELD32(0x00002000) #define CSR11_LONG_RETRY FIELD32(0x00ff0000) #define CSR11_SHORT_RETRY FIELD32(0xff000000) /* * CSR12: Synchronization configuration register 0. * All units in 1/16 TU. * BEACON_INTERVAL: Beacon interval, default is 100 TU. * CFP_MAX_DURATION: Cfp maximum duration, default is 100 TU. */ #define CSR12 0x0030 #define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) #define CSR12_CFP_MAX_DURATION FIELD32(0xffff0000) /* * CSR13: Synchronization configuration register 1. * All units in 1/16 TU. * ATIMW_DURATION: Atim window duration. * CFP_PERIOD: Cfp period, default is 0 TU. */ #define CSR13 0x0034 #define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) #define CSR13_CFP_PERIOD FIELD32(0x00ff0000) /* * CSR14: Synchronization control register. * TSF_COUNT: Enable tsf auto counting. * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. * TBCN: Enable tbcn with reload value. * TCFP: Enable tcfp & cfp / cp switching. * TATIMW: Enable tatimw & atim window switching. * BEACON_GEN: Enable beacon generator. * CFP_COUNT_PRELOAD: Cfp count preload value. * TBCM_PRELOAD: Tbcn preload value in units of 64us. */ #define CSR14 0x0038 #define CSR14_TSF_COUNT FIELD32(0x00000001) #define CSR14_TSF_SYNC FIELD32(0x00000006) #define CSR14_TBCN FIELD32(0x00000008) #define CSR14_TCFP FIELD32(0x00000010) #define CSR14_TATIMW FIELD32(0x00000020) #define CSR14_BEACON_GEN FIELD32(0x00000040) #define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) #define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) /* * CSR15: Synchronization status register. * CFP: ASIC is in contention-free period. * ATIMW: ASIC is in ATIM window. * BEACON_SENT: Beacon is send. */ #define CSR15 0x003c #define CSR15_CFP FIELD32(0x00000001) #define CSR15_ATIMW FIELD32(0x00000002) #define CSR15_BEACON_SENT FIELD32(0x00000004) /* * CSR16: TSF timer register 0. */ #define CSR16 0x0040 #define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) /* * CSR17: TSF timer register 1. */ #define CSR17 0x0044 #define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) /* * CSR18: IFS timer register 0. * SIFS: Sifs, default is 10 us. * PIFS: Pifs, default is 30 us. */ #define CSR18 0x0048 #define CSR18_SIFS FIELD32(0x000001ff) #define CSR18_PIFS FIELD32(0x001f0000) /* * CSR19: IFS timer register 1. * DIFS: Difs, default is 50 us. * EIFS: Eifs, default is 364 us. */ #define CSR19 0x004c #define CSR19_DIFS FIELD32(0x0000ffff) #define CSR19_EIFS FIELD32(0xffff0000) /* * CSR20: Wakeup timer register. * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. * AUTOWAKE: Enable auto wakeup / sleep mechanism. */ #define CSR20 0x0050 #define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) #define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) #define CSR20_AUTOWAKE FIELD32(0x01000000) /* * CSR21: EEPROM control register. * RELOAD: Write 1 to reload eeprom content. * TYPE_93C46: 1: 93c46, 0:93c66. */ #define CSR21 0x0054 #define CSR21_RELOAD FIELD32(0x00000001) #define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) #define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) #define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) #define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) #define CSR21_TYPE_93C46 FIELD32(0x00000020) /* * CSR22: CFP control register. * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. */ #define CSR22 0x0058 #define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) #define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) /* * Transmit related CSRs. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * TXCSR0: TX Control Register. * KICK_TX: Kick tx ring. * KICK_ATIM: Kick atim ring. * KICK_PRIO: Kick priority ring. * ABORT: Abort all transmit related ring operation. */ #define TXCSR0 0x0060 #define TXCSR0_KICK_TX FIELD32(0x00000001) #define TXCSR0_KICK_ATIM FIELD32(0x00000002) #define TXCSR0_KICK_PRIO FIELD32(0x00000004) #define TXCSR0_ABORT FIELD32(0x00000008) /* * TXCSR1: TX Configuration Register. * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. * TSF_OFFSET: Insert tsf offset. * AUTORESPONDER: Enable auto responder which include ack & cts. */ #define TXCSR1 0x0064 #define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) #define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) #define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) #define TXCSR1_AUTORESPONDER FIELD32(0x01000000) /* * TXCSR2: Tx descriptor configuration register. * TXD_SIZE: Tx descriptor size, default is 48. * NUM_TXD: Number of tx entries in ring. * NUM_ATIM: Number of atim entries in ring. * NUM_PRIO: Number of priority entries in ring. */ #define TXCSR2 0x0068 #define TXCSR2_TXD_SIZE FIELD32(0x000000ff) #define TXCSR2_NUM_TXD FIELD32(0x0000ff00) #define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) #define TXCSR2_NUM_PRIO FIELD32(0xff000000) /* * TXCSR3: TX Ring Base address register. */ #define TXCSR3 0x006c #define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR4: TX Atim Ring Base address register. */ #define TXCSR4 0x0070 #define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR5: TX Prio Ring Base address register. */ #define TXCSR5 0x0074 #define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR6: Beacon Base address register. */ #define TXCSR6 0x0078 #define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR7: Auto responder control register. * AR_POWERMANAGEMENT: Auto responder power management bit. */ #define TXCSR7 0x007c #define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) /* * TXCSR8: CCK Tx BBP register. */ #define TXCSR8 0x0098 #define TXCSR8_BBP_ID0 FIELD32(0x0000007f) #define TXCSR8_BBP_ID0_VALID FIELD32(0x00000080) #define TXCSR8_BBP_ID1 FIELD32(0x00007f00) #define TXCSR8_BBP_ID1_VALID FIELD32(0x00008000) #define TXCSR8_BBP_ID2 FIELD32(0x007f0000) #define TXCSR8_BBP_ID2_VALID FIELD32(0x00800000) #define TXCSR8_BBP_ID3 FIELD32(0x7f000000) #define TXCSR8_BBP_ID3_VALID FIELD32(0x80000000) /* * TXCSR9: OFDM TX BBP registers * OFDM_SIGNAL: BBP rate field address for OFDM. * OFDM_SERVICE: BBP service field address for OFDM. * OFDM_LENGTH_LOW: BBP length low byte address for OFDM. * OFDM_LENGTH_HIGH: BBP length high byte address for OFDM. */ #define TXCSR9 0x0094 #define TXCSR9_OFDM_RATE FIELD32(0x000000ff) #define TXCSR9_OFDM_SERVICE FIELD32(0x0000ff00) #define TXCSR9_OFDM_LENGTH_LOW FIELD32(0x00ff0000) #define TXCSR9_OFDM_LENGTH_HIGH FIELD32(0xff000000) /* * Receive related CSRs. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * RXCSR0: RX Control Register. * DISABLE_RX: Disable rx engine. * DROP_CRC: Drop crc error. * DROP_PHYSICAL: Drop physical error. * DROP_CONTROL: Drop control frame. * DROP_NOT_TO_ME: Drop not to me unicast frame. * DROP_TODS: Drop frame tods bit is true. * DROP_VERSION_ERROR: Drop version error frame. * PASS_CRC: Pass all packets with crc attached. * PASS_CRC: Pass all packets with crc attached. * PASS_PLCP: Pass all packets with 4 bytes PLCP attached. * DROP_MCAST: Drop multicast frames. * DROP_BCAST: Drop broadcast frames. * ENABLE_QOS: Accept QOS data frame and parse QOS field. */ #define RXCSR0 0x0080 #define RXCSR0_DISABLE_RX FIELD32(0x00000001) #define RXCSR0_DROP_CRC FIELD32(0x00000002) #define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) #define RXCSR0_DROP_CONTROL FIELD32(0x00000008) #define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) #define RXCSR0_DROP_TODS FIELD32(0x00000020) #define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) #define RXCSR0_PASS_CRC FIELD32(0x00000080) #define RXCSR0_PASS_PLCP FIELD32(0x00000100) #define RXCSR0_DROP_MCAST FIELD32(0x00000200) #define RXCSR0_DROP_BCAST FIELD32(0x00000400) #define RXCSR0_ENABLE_QOS FIELD32(0x00000800) /* * RXCSR1: RX descriptor configuration register. * RXD_SIZE: Rx descriptor size, default is 32b. * NUM_RXD: Number of rx entries in ring. */ #define RXCSR1 0x0084 #define RXCSR1_RXD_SIZE FIELD32(0x000000ff) #define RXCSR1_NUM_RXD FIELD32(0x0000ff00) /* * RXCSR2: RX Ring base address register. */ #define RXCSR2 0x0088 #define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) /* * RXCSR3: BBP ID register for Rx operation. * BBP_ID#: BBP register # id. * BBP_ID#_VALID: BBP register # id is valid or not. */ #define RXCSR3 0x0090 #define RXCSR3_BBP_ID0 FIELD32(0x0000007f) #define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) #define RXCSR3_BBP_ID1 FIELD32(0x00007f00) #define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) #define RXCSR3_BBP_ID2 FIELD32(0x007f0000) #define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) #define RXCSR3_BBP_ID3 FIELD32(0x7f000000) #define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) /* * ARCSR1: Auto Responder PLCP config register 1. * AR_BBP_DATA#: Auto responder BBP register # data. * AR_BBP_ID#: Auto responder BBP register # Id. */ #define ARCSR1 0x009c #define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) #define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) #define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) #define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) /* * Miscellaneous Registers. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * PCICSR: PCI control register. * BIG_ENDIAN: 1: big endian, 0: little endian. * RX_TRESHOLD: Rx threshold in dw to start pci access * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. * TX_TRESHOLD: Tx threshold in dw to start pci access * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. * READ_MULTIPLE: Enable memory read multiple. * WRITE_INVALID: Enable memory write & invalid. */ #define PCICSR 0x008c #define PCICSR_BIG_ENDIAN FIELD32(0x00000001) #define PCICSR_RX_TRESHOLD FIELD32(0x00000006) #define PCICSR_TX_TRESHOLD FIELD32(0x00000018) #define PCICSR_BURST_LENTH FIELD32(0x00000060) #define PCICSR_ENABLE_CLK FIELD32(0x00000080) #define PCICSR_READ_MULTIPLE FIELD32(0x00000100) #define PCICSR_WRITE_INVALID FIELD32(0x00000200) /* * CNT0: FCS error count. * FCS_ERROR: FCS error count, cleared when read. */ #define CNT0 0x00a0 #define CNT0_FCS_ERROR FIELD32(0x0000ffff) /* * Statistic Register. * CNT1: PLCP error count. * CNT2: Long error count. */ #define TIMECSR2 0x00a8 #define CNT1 0x00ac #define CNT2 0x00b0 #define TIMECSR3 0x00b4 /* * CNT3: CCA false alarm count. */ #define CNT3 0x00b8 #define CNT3_FALSE_CCA FIELD32(0x0000ffff) /* * Statistic Register. * CNT4: Rx FIFO overflow count. * CNT5: Tx FIFO underrun count. */ #define CNT4 0x00bc #define CNT5 0x00c0 /* * Baseband Control Register. */ /* * PWRCSR0: Power mode configuration register. */ #define PWRCSR0 0x00c4 /* * Power state transition time registers. */ #define PSCSR0 0x00c8 #define PSCSR1 0x00cc #define PSCSR2 0x00d0 #define PSCSR3 0x00d4 /* * PWRCSR1: Manual power control / status register. * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. * SET_STATE: Set state. Write 1 to trigger, self cleared. * BBP_DESIRE_STATE: BBP desired state. * RF_DESIRE_STATE: RF desired state. * BBP_CURR_STATE: BBP current state. * RF_CURR_STATE: RF current state. * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. */ #define PWRCSR1 0x00d8 #define PWRCSR1_SET_STATE FIELD32(0x00000001) #define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) #define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) #define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) #define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) #define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) /* * TIMECSR: Timer control register. * US_COUNT: 1 us timer count in units of clock cycles. * US_64_COUNT: 64 us timer count in units of 1 us timer. * BEACON_EXPECT: Beacon expect window. */ #define TIMECSR 0x00dc #define TIMECSR_US_COUNT FIELD32(0x000000ff) #define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) #define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) /* * MACCSR0: MAC configuration register 0. */ #define MACCSR0 0x00e0 /* * MACCSR1: MAC configuration register 1. * KICK_RX: Kick one-shot rx in one-shot rx mode. * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. * BBPRX_RESET_MODE: Ralink bbp rx reset mode. * AUTO_TXBBP: Auto tx logic access bbp control register. * AUTO_RXBBP: Auto rx logic access bbp control register. * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. * INTERSIL_IF: Intersil if calibration pin. */ #define MACCSR1 0x00e4 #define MACCSR1_KICK_RX FIELD32(0x00000001) #define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) #define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) #define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) #define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) #define MACCSR1_LOOPBACK FIELD32(0x00000060) #define MACCSR1_INTERSIL_IF FIELD32(0x00000080) /* * RALINKCSR: Ralink Rx auto-reset BBCR. * AR_BBP_DATA#: Auto reset BBP register # data. * AR_BBP_ID#: Auto reset BBP register # id. */ #define RALINKCSR 0x00e8 #define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) #define RALINKCSR_AR_BBP_ID0 FIELD32(0x00007f00) #define RALINKCSR_AR_BBP_VALID0 FIELD32(0x00008000) #define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) #define RALINKCSR_AR_BBP_ID1 FIELD32(0x7f000000) #define RALINKCSR_AR_BBP_VALID1 FIELD32(0x80000000) /* * BCNCSR: Beacon interval control register. * CHANGE: Write one to change beacon interval. * DELTATIME: The delta time value. * NUM_BEACON: Number of beacon according to mode. * MODE: Please refer to asic specs. * PLUS: Plus or minus delta time value. */ #define BCNCSR 0x00ec #define BCNCSR_CHANGE FIELD32(0x00000001) #define BCNCSR_DELTATIME FIELD32(0x0000001e) #define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) #define BCNCSR_MODE FIELD32(0x00006000) #define BCNCSR_PLUS FIELD32(0x00008000) /* * BBP / RF / IF Control Register. */ /* * BBPCSR: BBP serial control register. * VALUE: Register value to program into BBP. * REGNUM: Selected BBP register. * BUSY: 1: asic is busy execute BBP programming. * WRITE_CONTROL: 1: write BBP, 0: read BBP. */ #define BBPCSR 0x00f0 #define BBPCSR_VALUE FIELD32(0x000000ff) #define BBPCSR_REGNUM FIELD32(0x00007f00) #define BBPCSR_BUSY FIELD32(0x00008000) #define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) /* * RFCSR: RF serial control register. * VALUE: Register value + id to program into rf/if. * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). * IF_SELECT: Chip to program: 0: rf, 1: if. * PLL_LD: Rf pll_ld status. * BUSY: 1: asic is busy execute rf programming. */ #define RFCSR 0x00f4 #define RFCSR_VALUE FIELD32(0x00ffffff) #define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) #define RFCSR_IF_SELECT FIELD32(0x20000000) #define RFCSR_PLL_LD FIELD32(0x40000000) #define RFCSR_BUSY FIELD32(0x80000000) /* * LEDCSR: LED control register. * ON_PERIOD: On period, default 70ms. * OFF_PERIOD: Off period, default 30ms. * LINK: 0: linkoff, 1: linkup. * ACTIVITY: 0: idle, 1: active. * LINK_POLARITY: 0: active low, 1: active high. * ACTIVITY_POLARITY: 0: active low, 1: active high. * LED_DEFAULT: LED state for "enable" 0: ON, 1: OFF. */ #define LEDCSR 0x00f8 #define LEDCSR_ON_PERIOD FIELD32(0x000000ff) #define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) #define LEDCSR_LINK FIELD32(0x00010000) #define LEDCSR_ACTIVITY FIELD32(0x00020000) #define LEDCSR_LINK_POLARITY FIELD32(0x00040000) #define LEDCSR_ACTIVITY_POLARITY FIELD32(0x00080000) #define LEDCSR_LED_DEFAULT FIELD32(0x00100000) /* * SECCSR3: AES control register. */ #define SECCSR3 0x00fc /* * ASIC pointer information. * RXPTR: Current RX ring address. * TXPTR: Current Tx ring address. * PRIPTR: Current Priority ring address. * ATIMPTR: Current ATIM ring address. */ #define RXPTR 0x0100 #define TXPTR 0x0104 #define PRIPTR 0x0108 #define ATIMPTR 0x010c /* * TXACKCSR0: TX ACK timeout. */ #define TXACKCSR0 0x0110 /* * ACK timeout count registers. * ACKCNT0: TX ACK timeout count. * ACKCNT1: RX ACK timeout count. */ #define ACKCNT0 0x0114 #define ACKCNT1 0x0118 /* * GPIO and others. */ /* * GPIOCSR: GPIO control register. * GPIOCSR_VALx: GPIO value * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input */ #define GPIOCSR 0x0120 #define GPIOCSR_VAL0 FIELD32(0x00000001) #define GPIOCSR_VAL1 FIELD32(0x00000002) #define GPIOCSR_VAL2 FIELD32(0x00000004) #define GPIOCSR_VAL3 FIELD32(0x00000008) #define GPIOCSR_VAL4 FIELD32(0x00000010) #define GPIOCSR_VAL5 FIELD32(0x00000020) #define GPIOCSR_VAL6 FIELD32(0x00000040) #define GPIOCSR_VAL7 FIELD32(0x00000080) #define GPIOCSR_DIR0 FIELD32(0x00000100) #define GPIOCSR_DIR1 FIELD32(0x00000200) #define GPIOCSR_DIR2 FIELD32(0x00000400) #define GPIOCSR_DIR3 FIELD32(0x00000800) #define GPIOCSR_DIR4 FIELD32(0x00001000) #define GPIOCSR_DIR5 FIELD32(0x00002000) #define GPIOCSR_DIR6 FIELD32(0x00004000) #define GPIOCSR_DIR7 FIELD32(0x00008000) /* * FIFO pointer registers. * FIFOCSR0: TX FIFO pointer. * FIFOCSR1: RX FIFO pointer. */ #define FIFOCSR0 0x0128 #define FIFOCSR1 0x012c /* * BCNCSR1: Tx BEACON offset time control register. * PRELOAD: Beacon timer offset in units of usec. * BEACON_CWMIN: 2^CwMin. */ #define BCNCSR1 0x0130 #define BCNCSR1_PRELOAD FIELD32(0x0000ffff) #define BCNCSR1_BEACON_CWMIN FIELD32(0x000f0000) /* * MACCSR2: TX_PE to RX_PE turn-around time control register * DELAY: RX_PE low width, in units of pci clock cycle. */ #define MACCSR2 0x0134 #define MACCSR2_DELAY FIELD32(0x000000ff) /* * TESTCSR: TEST mode selection register. */ #define TESTCSR 0x0138 /* * ARCSR2: 1 Mbps ACK/CTS PLCP. */ #define ARCSR2 0x013c #define ARCSR2_SIGNAL FIELD32(0x000000ff) #define ARCSR2_SERVICE FIELD32(0x0000ff00) #define ARCSR2_LENGTH FIELD32(0xffff0000) /* * ARCSR3: 2 Mbps ACK/CTS PLCP. */ #define ARCSR3 0x0140 #define ARCSR3_SIGNAL FIELD32(0x000000ff) #define ARCSR3_SERVICE FIELD32(0x0000ff00) #define ARCSR3_LENGTH FIELD32(0xffff0000) /* * ARCSR4: 5.5 Mbps ACK/CTS PLCP. */ #define ARCSR4 0x0144 #define ARCSR4_SIGNAL FIELD32(0x000000ff) #define ARCSR4_SERVICE FIELD32(0x0000ff00) #define ARCSR4_LENGTH FIELD32(0xffff0000) /* * ARCSR5: 11 Mbps ACK/CTS PLCP. */ #define ARCSR5 0x0148 #define ARCSR5_SIGNAL FIELD32(0x000000ff) #define ARCSR5_SERVICE FIELD32(0x0000ff00) #define ARCSR5_LENGTH FIELD32(0xffff0000) /* * ARTCSR0: CCK ACK/CTS payload consumed time for 1/2/5.5/11 mbps. */ #define ARTCSR0 0x014c #define ARTCSR0_ACK_CTS_11MBS FIELD32(0x000000ff) #define ARTCSR0_ACK_CTS_5_5MBS FIELD32(0x0000ff00) #define ARTCSR0_ACK_CTS_2MBS FIELD32(0x00ff0000) #define ARTCSR0_ACK_CTS_1MBS FIELD32(0xff000000) /* * ARTCSR1: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. */ #define ARTCSR1 0x0150 #define ARTCSR1_ACK_CTS_6MBS FIELD32(0x000000ff) #define ARTCSR1_ACK_CTS_9MBS FIELD32(0x0000ff00) #define ARTCSR1_ACK_CTS_12MBS FIELD32(0x00ff0000) #define ARTCSR1_ACK_CTS_18MBS FIELD32(0xff000000) /* * ARTCSR2: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. */ #define ARTCSR2 0x0154 #define ARTCSR2_ACK_CTS_24MBS FIELD32(0x000000ff) #define ARTCSR2_ACK_CTS_36MBS FIELD32(0x0000ff00) #define ARTCSR2_ACK_CTS_48MBS FIELD32(0x00ff0000) #define ARTCSR2_ACK_CTS_54MBS FIELD32(0xff000000) /* * SECCSR1: WEP control register. * KICK_ENCRYPT: Kick encryption engine, self-clear. * ONE_SHOT: 0: ring mode, 1: One shot only mode. * DESC_ADDRESS: Descriptor physical address of frame. */ #define SECCSR1 0x0158 #define SECCSR1_KICK_ENCRYPT FIELD32(0x00000001) #define SECCSR1_ONE_SHOT FIELD32(0x00000002) #define SECCSR1_DESC_ADDRESS FIELD32(0xfffffffc) /* * BBPCSR1: BBP TX configuration. */ #define BBPCSR1 0x015c #define BBPCSR1_CCK FIELD32(0x00000003) #define BBPCSR1_CCK_FLIP FIELD32(0x00000004) #define BBPCSR1_OFDM FIELD32(0x00030000) #define BBPCSR1_OFDM_FLIP FIELD32(0x00040000) /* * Dual band configuration registers. * DBANDCSR0: Dual band configuration register 0. * DBANDCSR1: Dual band configuration register 1. */ #define DBANDCSR0 0x0160 #define DBANDCSR1 0x0164 /* * BBPPCSR: BBP Pin control register. */ #define BBPPCSR 0x0168 /* * MAC special debug mode selection registers. * DBGSEL0: MAC special debug mode selection register 0. * DBGSEL1: MAC special debug mode selection register 1. */ #define DBGSEL0 0x016c #define DBGSEL1 0x0170 /* * BISTCSR: BBP BIST register. */ #define BISTCSR 0x0174 /* * Multicast filter registers. * MCAST0: Multicast filter register 0. * MCAST1: Multicast filter register 1. */ #define MCAST0 0x0178 #define MCAST1 0x017c /* * UART registers. * UARTCSR0: UART1 TX register. * UARTCSR1: UART1 RX register. * UARTCSR3: UART1 frame control register. * UARTCSR4: UART1 buffer control register. * UART2CSR0: UART2 TX register. * UART2CSR1: UART2 RX register. * UART2CSR3: UART2 frame control register. * UART2CSR4: UART2 buffer control register. */ #define UARTCSR0 0x0180 #define UARTCSR1 0x0184 #define UARTCSR3 0x0188 #define UARTCSR4 0x018c #define UART2CSR0 0x0190 #define UART2CSR1 0x0194 #define UART2CSR3 0x0198 #define UART2CSR4 0x019c /* * BBP registers. * The wordsize of the BBP is 8 bits. */ /* * R2: TX antenna control */ #define BBP_R2_TX_ANTENNA FIELD8(0x03) #define BBP_R2_TX_IQ_FLIP FIELD8(0x04) /* * R14: RX antenna control */ #define BBP_R14_RX_ANTENNA FIELD8(0x03) #define BBP_R14_RX_IQ_FLIP FIELD8(0x04) /* * BBP_R70 */ #define BBP_R70_JAPAN_FILTER FIELD8(0x08) /* * RF registers */ /* * RF 1 */ #define RF1_TUNER FIELD32(0x00020000) /* * RF 3 */ #define RF3_TUNER FIELD32(0x00000100) #define RF3_TXPOWER FIELD32(0x00003e00) /* * EEPROM content. * The wordsize of the EEPROM is 16 bits. */ /* * HW MAC address. */ #define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) #define EEPROM_MAC_ADDR1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) #define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) /* * EEPROM antenna. * ANTENNA_NUM: Number of antenna's. * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. * DYN_TXAGC: Dynamic TX AGC control. * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. * RF_TYPE: Rf_type of this adapter. */ #define EEPROM_ANTENNA 0x10 #define EEPROM_ANTENNA_NUM FIELD16(0x0003) #define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) #define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) #define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) #define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) #define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) #define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) /* * EEPROM NIC config. * CARDBUS_ACCEL: 0: enable, 1: disable. * DYN_BBP_TUNE: 0: enable, 1: disable. * CCK_TX_POWER: CCK TX power compensation. */ #define EEPROM_NIC 0x11 #define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) #define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) #define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) /* * EEPROM geography. * GEO: Default geography setting for device. */ #define EEPROM_GEOGRAPHY 0x12 #define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) /* * EEPROM BBP. */ #define EEPROM_BBP_START 0x13 #define EEPROM_BBP_SIZE 16 #define EEPROM_BBP_VALUE FIELD16(0x00ff) #define EEPROM_BBP_REG_ID FIELD16(0xff00) /* * EEPROM TXPOWER */ #define EEPROM_TXPOWER_START 0x23 #define EEPROM_TXPOWER_SIZE 7 #define EEPROM_TXPOWER_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_2 FIELD16(0xff00) /* * RSSI <-> dBm offset calibration */ #define EEPROM_CALIBRATE_OFFSET 0x3e #define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) /* * DMA descriptor defines. */ #define TXD_DESC_SIZE (11 * sizeof(__le32)) #define RXD_DESC_SIZE (11 * sizeof(__le32)) /* * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. */ /* * Word0 */ #define TXD_W0_OWNER_NIC FIELD32(0x00000001) #define TXD_W0_VALID FIELD32(0x00000002) #define TXD_W0_RESULT FIELD32(0x0000001c) #define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) #define TXD_W0_MORE_FRAG FIELD32(0x00000100) #define TXD_W0_ACK FIELD32(0x00000200) #define TXD_W0_TIMESTAMP FIELD32(0x00000400) #define TXD_W0_OFDM FIELD32(0x00000800) #define TXD_W0_CIPHER_OWNER FIELD32(0x00001000) #define TXD_W0_IFS FIELD32(0x00006000) #define TXD_W0_RETRY_MODE FIELD32(0x00008000) #define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) #define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) /* * Word1 */ #define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) /* * Word2 */ #define TXD_W2_IV_OFFSET FIELD32(0x0000003f) #define TXD_W2_AIFS FIELD32(0x000000c0) #define TXD_W2_CWMIN FIELD32(0x00000f00) #define TXD_W2_CWMAX FIELD32(0x0000f000) /* * Word3: PLCP information */ #define TXD_W3_PLCP_SIGNAL FIELD32(0x000000ff) #define TXD_W3_PLCP_SERVICE FIELD32(0x0000ff00) #define TXD_W3_PLCP_LENGTH_LOW FIELD32(0x00ff0000) #define TXD_W3_PLCP_LENGTH_HIGH FIELD32(0xff000000) /* * Word4 */ #define TXD_W4_IV FIELD32(0xffffffff) /* * Word5 */ #define TXD_W5_EIV FIELD32(0xffffffff) /* * Word6-9: Key */ #define TXD_W6_KEY FIELD32(0xffffffff) #define TXD_W7_KEY FIELD32(0xffffffff) #define TXD_W8_KEY FIELD32(0xffffffff) #define TXD_W9_KEY FIELD32(0xffffffff) /* * Word10 */ #define TXD_W10_RTS FIELD32(0x00000001) #define TXD_W10_TX_RATE FIELD32(0x000000fe) /* * RX descriptor format for RX Ring. */ /* * Word0 */ #define RXD_W0_OWNER_NIC FIELD32(0x00000001) #define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) #define RXD_W0_MULTICAST FIELD32(0x00000004) #define RXD_W0_BROADCAST FIELD32(0x00000008) #define RXD_W0_MY_BSS FIELD32(0x00000010) #define RXD_W0_CRC_ERROR FIELD32(0x00000020) #define RXD_W0_OFDM FIELD32(0x00000040) #define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) #define RXD_W0_CIPHER_OWNER FIELD32(0x00000100) #define RXD_W0_ICV_ERROR FIELD32(0x00000200) #define RXD_W0_IV_OFFSET FIELD32(0x0000fc00) #define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) #define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) /* * Word1 */ #define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) /* * Word2 */ #define RXD_W2_SIGNAL FIELD32(0x000000ff) #define RXD_W2_RSSI FIELD32(0x0000ff00) #define RXD_W2_TA FIELD32(0xffff0000) /* * Word3 */ #define RXD_W3_TA FIELD32(0xffffffff) /* * Word4 */ #define RXD_W4_IV FIELD32(0xffffffff) /* * Word5 */ #define RXD_W5_EIV FIELD32(0xffffffff) /* * Word6-9: Key */ #define RXD_W6_KEY FIELD32(0xffffffff) #define RXD_W7_KEY FIELD32(0xffffffff) #define RXD_W8_KEY FIELD32(0xffffffff) #define RXD_W9_KEY FIELD32(0xffffffff) /* * Word10 */ #define RXD_W10_DROP FIELD32(0x00000001) /* * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 #define TXPOWER_FROM_DEV(__txpower) \ (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500PCI_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2500pci.c0000644000175000017500000020115512026211315023320 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2500pci Abstract: rt2500pci device specific routines. Supported chipsets: RT2560. */ #include #include #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00pci.h" #include "rt2500pci.h" /* * Register access. * All access to the CSR registers will go through the methods * rt2x00pci_register_read and rt2x00pci_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay * between each attampt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. */ #define WAIT_FOR_BBP(__dev, __reg) \ rt2x00pci_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) #define WAIT_FOR_RF(__dev, __reg) \ rt2x00pci_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) static void rt2500pci_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, BBPCSR_VALUE, value); rt2x00_set_field32(®, BBPCSR_REGNUM, word); rt2x00_set_field32(®, BBPCSR_BUSY, 1); rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500pci_bbp_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the read request into the register. * After the data has been written, we wait until hardware * returns the correct value, if at any time the register * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, BBPCSR_REGNUM, word); rt2x00_set_field32(®, BBPCSR_BUSY, 1); rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, BBPCSR_VALUE); mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500pci_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RF becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_RF(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, RFCSR_VALUE, value); rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); rt2x00_set_field32(®, RFCSR_BUSY, 1); rt2x00pci_register_write(rt2x00dev, RFCSR, reg); rt2x00_rf_write(rt2x00dev, word, value); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500pci_eepromregister_read(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg; rt2x00pci_register_read(rt2x00dev, CSR21, ®); eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); eeprom->reg_data_clock = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_CLOCK); eeprom->reg_chip_select = !!rt2x00_get_field32(reg, CSR21_EEPROM_CHIP_SELECT); } static void rt2500pci_eepromregister_write(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg = 0; rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, !!eeprom->reg_data_in); rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, !!eeprom->reg_data_out); rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, !!eeprom->reg_data_clock); rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, !!eeprom->reg_chip_select); rt2x00pci_register_write(rt2x00dev, CSR21, reg); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS static const struct rt2x00debug rt2500pci_rt2x00debug = { .owner = THIS_MODULE, .csr = { .read = rt2x00pci_register_read, .write = rt2x00pci_register_write, .flags = RT2X00DEBUGFS_OFFSET, .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt2500pci_bbp_read, .write = rt2500pci_bbp_write, .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt2500pci_rf_write, .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); return rt2x00_get_field32(reg, GPIOCSR_VAL0); } #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2500pci_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); unsigned int enabled = brightness != LED_OFF; u32 reg; rt2x00pci_register_read(led->rt2x00dev, LEDCSR, ®); if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) rt2x00_set_field32(®, LEDCSR_LINK, enabled); else if (led->type == LED_TYPE_ACTIVITY) rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); rt2x00pci_register_write(led->rt2x00dev, LEDCSR, reg); } static int rt2500pci_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); u32 reg; rt2x00pci_register_read(led->rt2x00dev, LEDCSR, ®); rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); rt2x00pci_register_write(led->rt2x00dev, LEDCSR, reg); return 0; } static void rt2500pci_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; led->type = type; led->led_dev.brightness_set = rt2500pci_brightness_set; led->led_dev.blink_set = rt2500pci_blink_set; led->flags = LED_INITIALIZED; } #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Configuration handlers. */ static void rt2500pci_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { u32 reg; /* * Start configuration steps. * Note that the version error will always be dropped * and broadcast frames will always be accepted since * there is no filter for it at this time. */ rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); rt2x00_set_field32(®, RXCSR0_DROP_CRC, !(filter_flags & FIF_FCSFAIL)); rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field32(®, RXCSR0_DROP_TODS, !(filter_flags & FIF_PROMISC_IN_BSS) && !rt2x00dev->intf_ap_count); rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); rt2x00_set_field32(®, RXCSR0_DROP_MCAST, !(filter_flags & FIF_ALLMULTI)); rt2x00_set_field32(®, RXCSR0_DROP_BCAST, 0); rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); } static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags) { struct data_queue *queue = rt2x00dev->bcn; unsigned int bcn_preload; u32 reg; if (flags & CONFIG_UPDATE_TYPE) { /* * Enable beacon config */ bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); rt2x00pci_register_read(rt2x00dev, BCNCSR1, ®); rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); rt2x00_set_field32(®, BCNCSR1_BEACON_CWMIN, queue->cw_min); rt2x00pci_register_write(rt2x00dev, BCNCSR1, reg); /* * Enable synchronisation. */ rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); rt2x00pci_register_write(rt2x00dev, CSR14, reg); } if (flags & CONFIG_UPDATE_MAC) rt2x00pci_register_multiwrite(rt2x00dev, CSR3, conf->mac, sizeof(conf->mac)); if (flags & CONFIG_UPDATE_BSSID) rt2x00pci_register_multiwrite(rt2x00dev, CSR5, conf->bssid, sizeof(conf->bssid)); } static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed) { int preamble_mask; u32 reg; /* * When short preamble is enabled, we should set bit 0x08 */ if (changed & BSS_CHANGED_ERP_PREAMBLE) { preamble_mask = erp->short_preamble << 3; rt2x00pci_register_read(rt2x00dev, TXCSR1, ®); rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x162); rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0xa2); rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); rt2x00pci_register_write(rt2x00dev, TXCSR1, reg); rt2x00pci_register_read(rt2x00dev, ARCSR2, ®); rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10)); rt2x00pci_register_write(rt2x00dev, ARCSR2, reg); rt2x00pci_register_read(rt2x00dev, ARCSR3, ®); rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20)); rt2x00pci_register_write(rt2x00dev, ARCSR3, reg); rt2x00pci_register_read(rt2x00dev, ARCSR4, ®); rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55)); rt2x00pci_register_write(rt2x00dev, ARCSR4, reg); rt2x00pci_register_read(rt2x00dev, ARCSR5, ®); rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110)); rt2x00pci_register_write(rt2x00dev, ARCSR5, reg); } if (changed & BSS_CHANGED_BASIC_RATES) rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates); if (changed & BSS_CHANGED_ERP_SLOT) { rt2x00pci_register_read(rt2x00dev, CSR11, ®); rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); rt2x00pci_register_write(rt2x00dev, CSR11, reg); rt2x00pci_register_read(rt2x00dev, CSR18, ®); rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); rt2x00pci_register_write(rt2x00dev, CSR18, reg); rt2x00pci_register_read(rt2x00dev, CSR19, ®); rt2x00_set_field32(®, CSR19_DIFS, erp->difs); rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); rt2x00pci_register_write(rt2x00dev, CSR19, reg); } if (changed & BSS_CHANGED_BEACON_INT) { rt2x00pci_register_read(rt2x00dev, CSR12, ®); rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, erp->beacon_int * 16); rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16); rt2x00pci_register_write(rt2x00dev, CSR12, reg); } } static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u32 reg; u8 r14; u8 r2; /* * We should never come here because rt2x00lib is supposed * to catch this and send us the correct antenna explicitely. */ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || ant->tx == ANTENNA_SW_DIVERSITY); rt2x00pci_register_read(rt2x00dev, BBPCSR1, ®); rt2500pci_bbp_read(rt2x00dev, 14, &r14); rt2500pci_bbp_read(rt2x00dev, 2, &r2); /* * Configure the TX antenna. */ switch (ant->tx) { case ANTENNA_A: rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); rt2x00_set_field32(®, BBPCSR1_CCK, 0); rt2x00_set_field32(®, BBPCSR1_OFDM, 0); break; case ANTENNA_B: default: rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); rt2x00_set_field32(®, BBPCSR1_CCK, 2); rt2x00_set_field32(®, BBPCSR1_OFDM, 2); break; } /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_A: rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); break; case ANTENNA_B: default: rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); break; } /* * RT2525E and RT5222 need to flip TX I/Q */ if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 1); rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 1); /* * RT2525E does not need RX I/Q Flip. */ if (rt2x00_rf(rt2x00dev, RF2525E)) rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); } else { rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 0); rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 0); } rt2x00pci_register_write(rt2x00dev, BBPCSR1, reg); rt2500pci_bbp_write(rt2x00dev, 14, r14); rt2500pci_bbp_write(rt2x00dev, 2, r2); } static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, struct rf_channel *rf, const int txpower) { u8 r70; /* * Set TXpower. */ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); /* * Switch on tuning bits. * For RT2523 devices we do not need to update the R1 register. */ if (!rt2x00_rf(rt2x00dev, RF2523)) rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); /* * For RT2525 we should first set the channel to half band higher. */ if (rt2x00_rf(rt2x00dev, RF2525)) { static const u32 vals[] = { 0x00080cbe, 0x00080d02, 0x00080d06, 0x00080d0a, 0x00080d0e, 0x00080d12, 0x00080d16, 0x00080d1a, 0x00080d1e, 0x00080d22, 0x00080d26, 0x00080d2a, 0x00080d2e, 0x00080d3a }; rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); rt2500pci_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); if (rf->rf4) rt2500pci_rf_write(rt2x00dev, 4, rf->rf4); } rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); rt2500pci_rf_write(rt2x00dev, 2, rf->rf2); rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); if (rf->rf4) rt2500pci_rf_write(rt2x00dev, 4, rf->rf4); /* * Channel 14 requires the Japan filter bit to be set. */ r70 = 0x46; rt2x00_set_field8(&r70, BBP_R70_JAPAN_FILTER, rf->channel == 14); rt2500pci_bbp_write(rt2x00dev, 70, r70); msleep(1); /* * Switch off tuning bits. * For RT2523 devices we do not need to update the R1 register. */ if (!rt2x00_rf(rt2x00dev, RF2523)) { rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); } rt2x00_set_field32(&rf->rf3, RF3_TUNER, 0); rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); /* * Clear false CRC during channel switch. */ rt2x00pci_register_read(rt2x00dev, CNT0, &rf->rf1); } static void rt2500pci_config_txpower(struct rt2x00_dev *rt2x00dev, const int txpower) { u32 rf3; rt2x00_rf_read(rt2x00dev, 3, &rf3); rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); rt2500pci_rf_write(rt2x00dev, 3, rf3); } static void rt2500pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u32 reg; rt2x00pci_register_read(rt2x00dev, CSR11, ®); rt2x00_set_field32(®, CSR11_LONG_RETRY, libconf->conf->long_frame_max_tx_count); rt2x00_set_field32(®, CSR11_SHORT_RETRY, libconf->conf->short_frame_max_tx_count); rt2x00pci_register_write(rt2x00dev, CSR11, reg); } static void rt2500pci_config_ps(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { enum dev_state state = (libconf->conf->flags & IEEE80211_CONF_PS) ? STATE_SLEEP : STATE_AWAKE; u32 reg; if (state == STATE_SLEEP) { rt2x00pci_register_read(rt2x00dev, CSR20, ®); rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, (rt2x00dev->beacon_int - 20) * 16); rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, libconf->conf->listen_interval - 1); /* We must first disable autowake before it can be enabled */ rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); rt2x00pci_register_write(rt2x00dev, CSR20, reg); rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); rt2x00pci_register_write(rt2x00dev, CSR20, reg); } else { rt2x00pci_register_read(rt2x00dev, CSR20, ®); rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); rt2x00pci_register_write(rt2x00dev, CSR20, reg); } rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } static void rt2500pci_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { if (flags & IEEE80211_CONF_CHANGE_CHANNEL) rt2500pci_config_channel(rt2x00dev, &libconf->rf, libconf->conf->power_level); if ((flags & IEEE80211_CONF_CHANGE_POWER) && !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) rt2500pci_config_txpower(rt2x00dev, libconf->conf->power_level); if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) rt2500pci_config_retry_limit(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_PS) rt2500pci_config_ps(rt2x00dev, libconf); } /* * Link tuning */ static void rt2500pci_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { u32 reg; /* * Update FCS error count from register. */ rt2x00pci_register_read(rt2x00dev, CNT0, ®); qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); /* * Update False CCA count from register. */ rt2x00pci_register_read(rt2x00dev, CNT3, ®); qual->false_cca = rt2x00_get_field32(reg, CNT3_FALSE_CCA); } static inline void rt2500pci_set_vgc(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, u8 vgc_level) { if (qual->vgc_level_reg != vgc_level) { rt2500pci_bbp_write(rt2x00dev, 17, vgc_level); qual->vgc_level = vgc_level; qual->vgc_level_reg = vgc_level; } } static void rt2500pci_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { rt2500pci_set_vgc(rt2x00dev, qual, 0x48); } static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count) { /* * To prevent collisions with MAC ASIC on chipsets * up to version C the link tuning should halt after 20 * seconds while being associated. */ if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D && rt2x00dev->intf_associated && count > 20) return; /* * Chipset versions C and lower should directly continue * to the dynamic CCA tuning. Chipset version D and higher * should go straight to dynamic CCA tuning when they * are not associated. */ if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D || !rt2x00dev->intf_associated) goto dynamic_cca_tune; /* * A too low RSSI will cause too much false CCA which will * then corrupt the R17 tuning. To remidy this the tuning should * be stopped (While making sure the R17 value will not exceed limits) */ if (qual->rssi < -80 && count > 20) { if (qual->vgc_level_reg >= 0x41) rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); return; } /* * Special big-R17 for short distance */ if (qual->rssi >= -58) { rt2500pci_set_vgc(rt2x00dev, qual, 0x50); return; } /* * Special mid-R17 for middle distance */ if (qual->rssi >= -74) { rt2500pci_set_vgc(rt2x00dev, qual, 0x41); return; } /* * Leave short or middle distance condition, restore r17 * to the dynamic tuning range. */ if (qual->vgc_level_reg >= 0x41) { rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); return; } dynamic_cca_tune: /* * R17 is inside the dynamic tuning range, * start tuning the link based on the false cca counter. */ if (qual->false_cca > 512 && qual->vgc_level_reg < 0x40) rt2500pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level_reg); else if (qual->false_cca < 100 && qual->vgc_level_reg > 0x32) rt2500pci_set_vgc(rt2x00dev, qual, --qual->vgc_level_reg); } /* * Queue handlers. */ static void rt2500pci_start_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); rt2x00_set_field32(®, CSR14_TBCN, 1); rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, CSR14, reg); break; default: break; } } static void rt2500pci_kick_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_AC_VO: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; case QID_AC_VI: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; case QID_ATIM: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; default: break; } } static void rt2500pci_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_AC_VO: case QID_AC_VI: case QID_ATIM: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_ABORT, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; case QID_RX: rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); rt2x00_set_field32(®, CSR14_TBCN, 0); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); /* * Wait for possibly running tbtt tasklets. */ tasklet_kill(&rt2x00dev->tbtt_tasklet); break; default: break; } } /* * Initialization functions. */ static bool rt2500pci_get_entry_state(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 0, &word); return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); } else { rt2x00_desc_read(entry_priv->desc, 0, &word); return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || rt2x00_get_field32(word, TXD_W0_VALID)); } } static void rt2500pci_clear_entry(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 1, &word); rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); rt2x00_desc_write(entry_priv->desc, 1, word); rt2x00_desc_read(entry_priv->desc, 0, &word); rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); rt2x00_desc_write(entry_priv->desc, 0, word); } else { rt2x00_desc_read(entry_priv->desc, 0, &word); rt2x00_set_field32(&word, TXD_W0_VALID, 0); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); rt2x00_desc_write(entry_priv->desc, 0, word); } } static int rt2500pci_init_queues(struct rt2x00_dev *rt2x00dev) { struct queue_entry_priv_pci *entry_priv; u32 reg; /* * Initialize registers. */ rt2x00pci_register_read(rt2x00dev, TXCSR2, ®); rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); rt2x00pci_register_write(rt2x00dev, TXCSR2, reg); entry_priv = rt2x00dev->tx[1].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR3, ®); rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR3, reg); entry_priv = rt2x00dev->tx[0].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR5, ®); rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR5, reg); entry_priv = rt2x00dev->atim->entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR4, ®); rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR4, reg); entry_priv = rt2x00dev->bcn->entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR6, ®); rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR6, reg); rt2x00pci_register_read(rt2x00dev, RXCSR1, ®); rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); rt2x00pci_register_write(rt2x00dev, RXCSR1, reg); entry_priv = rt2x00dev->rx->entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, RXCSR2, ®); rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, RXCSR2, reg); return 0; } static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00pci_register_write(rt2x00dev, PSCSR0, 0x00020002); rt2x00pci_register_write(rt2x00dev, PSCSR1, 0x00000002); rt2x00pci_register_write(rt2x00dev, PSCSR2, 0x00020002); rt2x00pci_register_write(rt2x00dev, PSCSR3, 0x00000002); rt2x00pci_register_read(rt2x00dev, TIMECSR, ®); rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); rt2x00pci_register_write(rt2x00dev, TIMECSR, reg); rt2x00pci_register_read(rt2x00dev, CSR9, ®); rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, rt2x00dev->rx->data_size / 128); rt2x00pci_register_write(rt2x00dev, CSR9, reg); /* * Always use CWmin and CWmax set in descriptor. */ rt2x00pci_register_read(rt2x00dev, CSR11, ®); rt2x00_set_field32(®, CSR11_CW_SELECT, 0); rt2x00pci_register_write(rt2x00dev, CSR11, reg); rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); rt2x00_set_field32(®, CSR14_TBCN, 0); rt2x00_set_field32(®, CSR14_TCFP, 0); rt2x00_set_field32(®, CSR14_TATIMW, 0); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); rt2x00pci_register_write(rt2x00dev, CNT3, 0); rt2x00pci_register_read(rt2x00dev, TXCSR8, ®); rt2x00_set_field32(®, TXCSR8_BBP_ID0, 10); rt2x00_set_field32(®, TXCSR8_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXCSR8_BBP_ID1, 11); rt2x00_set_field32(®, TXCSR8_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXCSR8_BBP_ID2, 13); rt2x00_set_field32(®, TXCSR8_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXCSR8_BBP_ID3, 12); rt2x00_set_field32(®, TXCSR8_BBP_ID3_VALID, 1); rt2x00pci_register_write(rt2x00dev, TXCSR8, reg); rt2x00pci_register_read(rt2x00dev, ARTCSR0, ®); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_1MBS, 112); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_2MBS, 56); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_5_5MBS, 20); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_11MBS, 10); rt2x00pci_register_write(rt2x00dev, ARTCSR0, reg); rt2x00pci_register_read(rt2x00dev, ARTCSR1, ®); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_6MBS, 45); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_9MBS, 37); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_12MBS, 33); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_18MBS, 29); rt2x00pci_register_write(rt2x00dev, ARTCSR1, reg); rt2x00pci_register_read(rt2x00dev, ARTCSR2, ®); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_24MBS, 29); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_36MBS, 25); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_48MBS, 25); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_54MBS, 25); rt2x00pci_register_write(rt2x00dev, ARTCSR2, reg); rt2x00pci_register_read(rt2x00dev, RXCSR3, ®); rt2x00_set_field32(®, RXCSR3_BBP_ID0, 47); /* CCK Signal */ rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID1, 51); /* Rssi */ rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID2, 42); /* OFDM Rate */ rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID3, 51); /* RSSI */ rt2x00_set_field32(®, RXCSR3_BBP_ID3_VALID, 1); rt2x00pci_register_write(rt2x00dev, RXCSR3, reg); rt2x00pci_register_read(rt2x00dev, PCICSR, ®); rt2x00_set_field32(®, PCICSR_BIG_ENDIAN, 0); rt2x00_set_field32(®, PCICSR_RX_TRESHOLD, 0); rt2x00_set_field32(®, PCICSR_TX_TRESHOLD, 3); rt2x00_set_field32(®, PCICSR_BURST_LENTH, 1); rt2x00_set_field32(®, PCICSR_ENABLE_CLK, 1); rt2x00_set_field32(®, PCICSR_READ_MULTIPLE, 1); rt2x00_set_field32(®, PCICSR_WRITE_INVALID, 1); rt2x00pci_register_write(rt2x00dev, PCICSR, reg); rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); rt2x00pci_register_write(rt2x00dev, GPIOCSR, 0x0000ff00); rt2x00pci_register_write(rt2x00dev, TESTCSR, 0x000000f0); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; rt2x00pci_register_write(rt2x00dev, MACCSR0, 0x00213223); rt2x00pci_register_write(rt2x00dev, MACCSR1, 0x00235518); rt2x00pci_register_read(rt2x00dev, MACCSR2, ®); rt2x00_set_field32(®, MACCSR2_DELAY, 64); rt2x00pci_register_write(rt2x00dev, MACCSR2, reg); rt2x00pci_register_read(rt2x00dev, RALINKCSR, ®); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 26); rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID0, 1); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 26); rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID1, 1); rt2x00pci_register_write(rt2x00dev, RALINKCSR, reg); rt2x00pci_register_write(rt2x00dev, BBPCSR1, 0x82188200); rt2x00pci_register_write(rt2x00dev, TXACKCSR0, 0x00000020); rt2x00pci_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, CSR1_BBP_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 0); rt2x00pci_register_write(rt2x00dev, CSR1, reg); rt2x00pci_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 1); rt2x00pci_register_write(rt2x00dev, CSR1, reg); /* * We must clear the FCS and FIFO error count. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ rt2x00pci_register_read(rt2x00dev, CNT0, ®); rt2x00pci_register_read(rt2x00dev, CNT4, ®); return 0; } static int rt2500pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2500pci_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) return 0; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); return -EACCES; } static int rt2500pci_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; u16 eeprom; u8 reg_id; u8 value; if (unlikely(rt2500pci_wait_bbp_ready(rt2x00dev))) return -EACCES; rt2500pci_bbp_write(rt2x00dev, 3, 0x02); rt2500pci_bbp_write(rt2x00dev, 4, 0x19); rt2500pci_bbp_write(rt2x00dev, 14, 0x1c); rt2500pci_bbp_write(rt2x00dev, 15, 0x30); rt2500pci_bbp_write(rt2x00dev, 16, 0xac); rt2500pci_bbp_write(rt2x00dev, 18, 0x18); rt2500pci_bbp_write(rt2x00dev, 19, 0xff); rt2500pci_bbp_write(rt2x00dev, 20, 0x1e); rt2500pci_bbp_write(rt2x00dev, 21, 0x08); rt2500pci_bbp_write(rt2x00dev, 22, 0x08); rt2500pci_bbp_write(rt2x00dev, 23, 0x08); rt2500pci_bbp_write(rt2x00dev, 24, 0x70); rt2500pci_bbp_write(rt2x00dev, 25, 0x40); rt2500pci_bbp_write(rt2x00dev, 26, 0x08); rt2500pci_bbp_write(rt2x00dev, 27, 0x23); rt2500pci_bbp_write(rt2x00dev, 30, 0x10); rt2500pci_bbp_write(rt2x00dev, 31, 0x2b); rt2500pci_bbp_write(rt2x00dev, 32, 0xb9); rt2500pci_bbp_write(rt2x00dev, 34, 0x12); rt2500pci_bbp_write(rt2x00dev, 35, 0x50); rt2500pci_bbp_write(rt2x00dev, 39, 0xc4); rt2500pci_bbp_write(rt2x00dev, 40, 0x02); rt2500pci_bbp_write(rt2x00dev, 41, 0x60); rt2500pci_bbp_write(rt2x00dev, 53, 0x10); rt2500pci_bbp_write(rt2x00dev, 54, 0x18); rt2500pci_bbp_write(rt2x00dev, 56, 0x08); rt2500pci_bbp_write(rt2x00dev, 57, 0x10); rt2500pci_bbp_write(rt2x00dev, 58, 0x08); rt2500pci_bbp_write(rt2x00dev, 61, 0x6d); rt2500pci_bbp_write(rt2x00dev, 62, 0x10); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); rt2500pci_bbp_write(rt2x00dev, reg_id, value); } } return 0; } /* * Device state switch handlers. */ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int mask = (state == STATE_RADIO_IRQ_OFF); u32 reg; unsigned long flags; /* * When interrupts are being enabled, the interrupt registers * should clear the register to assure a clean state. */ if (state == STATE_RADIO_IRQ_ON) { rt2x00pci_register_read(rt2x00dev, CSR7, ®); rt2x00pci_register_write(rt2x00dev, CSR7, reg); } /* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); rt2x00pci_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); rt2x00_set_field32(®, CSR8_RXDONE, mask); rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); if (state == STATE_RADIO_IRQ_OFF) { /* * Ensure that all tasklets are finished. */ tasklet_kill(&rt2x00dev->txstatus_tasklet); tasklet_kill(&rt2x00dev->rxdone_tasklet); tasklet_kill(&rt2x00dev->tbtt_tasklet); } } static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) { /* * Initialize all registers. */ if (unlikely(rt2500pci_init_queues(rt2x00dev) || rt2500pci_init_registers(rt2x00dev) || rt2500pci_init_bbp(rt2x00dev))) return -EIO; return 0; } static void rt2500pci_disable_radio(struct rt2x00_dev *rt2x00dev) { /* * Disable power */ rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0); } static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u32 reg, reg2; unsigned int i; char put_to_sleep; char bbp_state; char rf_state; put_to_sleep = (state != STATE_AWAKE); rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®); rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); rt2x00pci_register_write(rt2x00dev, PWRCSR1, reg); /* * Device is not guaranteed to be in the requested state yet. * We must wait until the register indicates that the * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®2); bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); if (bbp_state == state && rf_state == state) return 0; rt2x00pci_register_write(rt2x00dev, PWRCSR1, reg); msleep(10); } return -EBUSY; } static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int retval = 0; switch (state) { case STATE_RADIO_ON: retval = rt2500pci_enable_radio(rt2x00dev); break; case STATE_RADIO_OFF: rt2500pci_disable_radio(rt2x00dev); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: rt2500pci_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: case STATE_STANDBY: case STATE_AWAKE: retval = rt2500pci_set_state(rt2x00dev, state); break; default: retval = -ENOTSUPP; break; } if (unlikely(retval)) ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", state, retval); return retval; } /* * TX descriptor initialization */ static void rt2500pci_write_tx_desc(struct queue_entry *entry, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct queue_entry_priv_pci *entry_priv = entry->priv_data; __le32 *txd = entry_priv->desc; u32 word; /* * Start writing the descriptor words. */ rt2x00_desc_read(txd, 1, &word); rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); rt2x00_desc_write(txd, 1, word); rt2x00_desc_read(txd, 2, &word); rt2x00_set_field32(&word, TXD_W2_IV_OFFSET, IEEE80211_HEADER); rt2x00_set_field32(&word, TXD_W2_AIFS, entry->queue->aifs); rt2x00_set_field32(&word, TXD_W2_CWMIN, entry->queue->cw_min); rt2x00_set_field32(&word, TXD_W2_CWMAX, entry->queue->cw_max); rt2x00_desc_write(txd, 2, word); rt2x00_desc_read(txd, 3, &word); rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW, txdesc->u.plcp.length_low); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH, txdesc->u.plcp.length_high); rt2x00_desc_write(txd, 3, word); rt2x00_desc_read(txd, 10, &word); rt2x00_set_field32(&word, TXD_W10_RTS, test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); rt2x00_desc_write(txd, 10, word); /* * Writing TXD word 0 must the last to prevent a race condition with * the device, whereby the device may take hold of the TXD before we * finished updating it. */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, (txdesc->rate_mode == RATE_MODE_OFDM)); rt2x00_set_field32(&word, TXD_W0_CIPHER_OWNER, 1); rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); rt2x00_desc_write(txd, 0, word); /* * Register descriptor details in skb frame descriptor. */ skbdesc->desc = txd; skbdesc->desc_len = TXD_DESC_SIZE; } /* * TX data initialization */ static void rt2500pci_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; u32 reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); rt2x00queue_map_txskb(entry); /* * Write the TX descriptor for the beacon. */ rt2500pci_write_tx_desc(entry, txdesc); /* * Dump beacon to userspace through debugfs. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); /* * Enable beaconing again. */ rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, CSR14, reg); } /* * RX control handlers */ static void rt2500pci_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word0; u32 word2; rt2x00_desc_read(entry_priv->desc, 0, &word0); rt2x00_desc_read(entry_priv->desc, 2, &word2); if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; /* * Obtain the status about this packet. * When frame was received with an OFDM bitrate, * the signal is the PLCP value. If it was received with * a CCK bitrate the signal is the rate in 100kbit/s. */ rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); rxdesc->rssi = rt2x00_get_field32(word2, RXD_W2_RSSI) - entry->queue->rt2x00dev->rssi_offset; rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); if (rt2x00_get_field32(word0, RXD_W0_OFDM)) rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; else rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; } /* * Interrupt functions. */ static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, const enum data_queue_qid queue_idx) { struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); struct queue_entry_priv_pci *entry_priv; struct queue_entry *entry; struct txdone_entry_desc txdesc; u32 word; while (!rt2x00queue_empty(queue)) { entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); entry_priv = entry->priv_data; rt2x00_desc_read(entry_priv->desc, 0, &word); if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || !rt2x00_get_field32(word, TXD_W0_VALID)) break; /* * Obtain the status about this packet. */ txdesc.flags = 0; switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { case 0: /* Success */ case 1: /* Success with retry */ __set_bit(TXDONE_SUCCESS, &txdesc.flags); break; case 2: /* Failure, excessive retries */ __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); /* Don't break, this is a failed frame! */ default: /* Failure */ __set_bit(TXDONE_FAILURE, &txdesc.flags); } txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); rt2x00lib_txdone(entry, &txdesc); } } static inline void rt2500pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, struct rt2x00_field32 irq_field) { u32 reg; /* * Enable a single interrupt. The interrupt mask register * access needs locking. */ spin_lock_irq(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, irq_field, 0); rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock_irq(&rt2x00dev->irqmask_lock); } static void rt2500pci_txstatus_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; u32 reg; /* * Handle all tx queues. */ rt2500pci_txdone(rt2x00dev, QID_ATIM); rt2500pci_txdone(rt2x00dev, QID_AC_VO); rt2500pci_txdone(rt2x00dev, QID_AC_VI); /* * Enable all TXDONE interrupts again. */ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { spin_lock_irq(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock_irq(&rt2x00dev->irqmask_lock); } } static void rt2500pci_tbtt_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; rt2x00lib_beacondone(rt2x00dev); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); } static void rt2500pci_rxdone_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; if (rt2x00pci_rxdone(rt2x00dev)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); } static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; u32 reg, mask; /* * Get the interrupt sources & saved to local variable. * Write register value back to clear pending interrupts. */ rt2x00pci_register_read(rt2x00dev, CSR7, ®); rt2x00pci_register_write(rt2x00dev, CSR7, reg); if (!reg) return IRQ_NONE; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return IRQ_HANDLED; mask = reg; /* * Schedule tasklets for interrupt handling. */ if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); if (rt2x00_get_field32(reg, CSR7_RXDONE)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { tasklet_schedule(&rt2x00dev->txstatus_tasklet); /* * Mask out all txdone interrupts. */ rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); } /* * Disable all interrupts for which a tasklet was scheduled right now, * the tasklet will reenable the appropriate interrupts. */ spin_lock(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, CSR8, ®); reg |= mask; rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock(&rt2x00dev->irqmask_lock); return IRQ_HANDLED; } /* * Device probe functions. */ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) { struct eeprom_93cx6 eeprom; u32 reg; u16 word; u8 *mac; rt2x00pci_register_read(rt2x00dev, CSR21, ®); eeprom.data = rt2x00dev; eeprom.register_read = rt2500pci_eepromregister_read; eeprom.register_write = rt2500pci_eepromregister_write; eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; eeprom.reg_data_in = 0; eeprom.reg_data_out = 0; eeprom.reg_data_clock = 0; eeprom.reg_chip_select = 0; eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, EEPROM_SIZE / sizeof(u16)); /* * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { eth_random_addr(mac); EEPROM(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, ANTENNA_SW_DIVERSITY); rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, ANTENNA_SW_DIVERSITY); rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE, LED_MODE_DEFAULT); rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, DEFAULT_RSSI_OFFSET); rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); EEPROM(rt2x00dev, "Calibrate offset: 0x%04x\n", word); } return 0; } static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 value; u16 eeprom; /* * Read EEPROM word for configuration. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); /* * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2560, value, rt2x00_get_field32(reg, CSR0_REVISION)); if (!rt2x00_rf(rt2x00dev, RF2522) && !rt2x00_rf(rt2x00dev, RF2523) && !rt2x00_rf(rt2x00dev, RF2524) && !rt2x00_rf(rt2x00dev, RF2525) && !rt2x00_rf(rt2x00dev, RF2525E) && !rt2x00_rf(rt2x00dev, RF5222)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } /* * Identify default antenna configuration. */ rt2x00dev->default_ant.tx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); rt2x00dev->default_ant.rx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); /* * Store led mode, for correct led behaviour. */ #ifdef CONFIG_RT2X00_LIB_LEDS value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); if (value == LED_MODE_TXRX_ACTIVITY || value == LED_MODE_DEFAULT || value == LED_MODE_ASUS) rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_ACTIVITY); #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Detect if this device has an hardware controlled radio. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Check if the BBP tuning should be enabled. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); if (!rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); /* * Read the RSSI <-> dBm offset information. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); rt2x00dev->rssi_offset = rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); return 0; } /* * RF value list for RF2522 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2522[] = { { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 }, { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 }, { 3, 0x00002050, 0x000c2002, 0x00000101, 0 }, { 4, 0x00002050, 0x000c2016, 0x00000101, 0 }, { 5, 0x00002050, 0x000c202a, 0x00000101, 0 }, { 6, 0x00002050, 0x000c203e, 0x00000101, 0 }, { 7, 0x00002050, 0x000c2052, 0x00000101, 0 }, { 8, 0x00002050, 0x000c2066, 0x00000101, 0 }, { 9, 0x00002050, 0x000c207a, 0x00000101, 0 }, { 10, 0x00002050, 0x000c208e, 0x00000101, 0 }, { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 }, { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 }, { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 }, { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 }, }; /* * RF value list for RF2523 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2523[] = { { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b }, { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b }, { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b }, { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b }, { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b }, { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b }, { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b }, { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b }, { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b }, { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b }, { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b }, { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b }, { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b }, { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 }, }; /* * RF value list for RF2524 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2524[] = { { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b }, { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b }, { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b }, { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b }, { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b }, { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b }, { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b }, { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b }, { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b }, { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b }, { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b }, { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b }, { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b }, { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 }, }; /* * RF value list for RF2525 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2525[] = { { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b }, { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b }, { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b }, { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b }, { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b }, { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b }, { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b }, { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b }, { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b }, { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b }, { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b }, { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b }, { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b }, { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 }, }; /* * RF value list for RF2525e * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_bg_2525e[] = { { 1, 0x00022020, 0x00081136, 0x00060111, 0x00000a0b }, { 2, 0x00022020, 0x0008113a, 0x00060111, 0x00000a0b }, { 3, 0x00022020, 0x0008113e, 0x00060111, 0x00000a0b }, { 4, 0x00022020, 0x00081182, 0x00060111, 0x00000a0b }, { 5, 0x00022020, 0x00081186, 0x00060111, 0x00000a0b }, { 6, 0x00022020, 0x0008118a, 0x00060111, 0x00000a0b }, { 7, 0x00022020, 0x0008118e, 0x00060111, 0x00000a0b }, { 8, 0x00022020, 0x00081192, 0x00060111, 0x00000a0b }, { 9, 0x00022020, 0x00081196, 0x00060111, 0x00000a0b }, { 10, 0x00022020, 0x0008119a, 0x00060111, 0x00000a0b }, { 11, 0x00022020, 0x0008119e, 0x00060111, 0x00000a0b }, { 12, 0x00022020, 0x000811a2, 0x00060111, 0x00000a0b }, { 13, 0x00022020, 0x000811a6, 0x00060111, 0x00000a0b }, { 14, 0x00022020, 0x000811ae, 0x00060111, 0x00000a1b }, }; /* * RF value list for RF5222 * Supports: 2.4 GHz & 5.2 GHz */ static const struct rf_channel rf_vals_5222[] = { { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b }, { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b }, { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b }, { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b }, { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b }, { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b }, { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b }, { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b }, { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b }, { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b }, { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b }, { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b }, { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b }, { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b }, /* 802.11 UNI / HyperLan 2 */ { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f }, { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f }, { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f }, { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f }, { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f }, { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f }, { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f }, { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f }, /* 802.11 HyperLan 2 */ { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f }, { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f }, { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f }, { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f }, { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f }, { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f }, { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f }, { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f }, { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f }, { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f }, /* 802.11 UNII */ { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f }, { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 }, { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 }, { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 }, { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, }; static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *tx_power; unsigned int i; /* * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; if (rt2x00_rf(rt2x00dev, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); spec->channels = rf_vals_bg_2522; } else if (rt2x00_rf(rt2x00dev, RF2523)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); spec->channels = rf_vals_bg_2523; } else if (rt2x00_rf(rt2x00dev, RF2524)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); spec->channels = rf_vals_bg_2524; } else if (rt2x00_rf(rt2x00dev, RF2525)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); spec->channels = rf_vals_bg_2525; } else if (rt2x00_rf(rt2x00dev, RF2525E)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); spec->channels = rf_vals_bg_2525e; } else if (rt2x00_rf(rt2x00dev, RF5222)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; } /* * Create channel information array */ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; spec->channels_info = info; tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); for (i = 0; i < 14; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } if (spec->num_channels > 14) { for (i = 14; i < spec->num_channels; i++) { info[i].max_power = MAX_TXPOWER; info[i].default_power1 = DEFAULT_TXPOWER; } } return 0; } static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; u32 reg; /* * Allocate eeprom data. */ retval = rt2500pci_validate_eeprom(rt2x00dev); if (retval) return retval; retval = rt2500pci_init_eeprom(rt2x00dev); if (retval) return retval; /* * Enable rfkill polling by setting GPIO direction of the * rfkill switch GPIO pin correctly. */ rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); rt2x00_set_field32(®, GPIOCSR_DIR0, 1); rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg); /* * Initialize hw specifications. */ retval = rt2500pci_probe_hw_mode(rt2x00dev); if (retval) return retval; /* * This device requires the atim queue and DMA-mapped skbs. */ __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); /* * Set the rssi offset. */ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; return 0; } /* * IEEE80211 stack callback functions. */ static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; rt2x00pci_register_read(rt2x00dev, CSR17, ®); tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; rt2x00pci_register_read(rt2x00dev, CSR16, ®); tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); return tsf; } static int rt2500pci_tx_last_beacon(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; u32 reg; rt2x00pci_register_read(rt2x00dev, CSR15, ®); return rt2x00_get_field32(reg, CSR15_BEACON_SENT); } static const struct ieee80211_ops rt2500pci_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, .sw_scan_start = rt2x00mac_sw_scan_start, .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, .get_tsf = rt2500pci_get_tsf, .tx_last_beacon = rt2500pci_tx_last_beacon, .rfkill_poll = rt2x00mac_rfkill_poll, .flush = rt2x00mac_flush, .set_antenna = rt2x00mac_set_antenna, .get_antenna = rt2x00mac_get_antenna, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { .irq_handler = rt2500pci_interrupt, .txstatus_tasklet = rt2500pci_txstatus_tasklet, .tbtt_tasklet = rt2500pci_tbtt_tasklet, .rxdone_tasklet = rt2500pci_rxdone_tasklet, .probe_hw = rt2500pci_probe_hw, .initialize = rt2x00pci_initialize, .uninitialize = rt2x00pci_uninitialize, .get_entry_state = rt2500pci_get_entry_state, .clear_entry = rt2500pci_clear_entry, .set_device_state = rt2500pci_set_device_state, .rfkill_poll = rt2500pci_rfkill_poll, .link_stats = rt2500pci_link_stats, .reset_tuner = rt2500pci_reset_tuner, .link_tuner = rt2500pci_link_tuner, .start_queue = rt2500pci_start_queue, .kick_queue = rt2500pci_kick_queue, .stop_queue = rt2500pci_stop_queue, .flush_queue = rt2x00pci_flush_queue, .write_tx_desc = rt2500pci_write_tx_desc, .write_beacon = rt2500pci_write_beacon, .fill_rxdone = rt2500pci_fill_rxdone, .config_filter = rt2500pci_config_filter, .config_intf = rt2500pci_config_intf, .config_erp = rt2500pci_config_erp, .config_ant = rt2500pci_config_ant, .config = rt2500pci_config, }; static const struct data_queue_desc rt2500pci_queue_rx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = RXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2500pci_queue_tx = { .entry_num = 32, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2500pci_queue_bcn = { .entry_num = 1, .data_size = MGMT_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2500pci_queue_atim = { .entry_num = 8, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct rt2x00_ops rt2500pci_ops = { .name = KBUILD_MODNAME, .max_sta_intf = 1, .max_ap_intf = 1, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = 0, .rx = &rt2500pci_queue_rx, .tx = &rt2500pci_queue_tx, .bcn = &rt2500pci_queue_bcn, .atim = &rt2500pci_queue_atim, .lib = &rt2500pci_rt2x00_ops, .hw = &rt2500pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS .debugfs = &rt2500pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * RT2500pci module information. */ static DEFINE_PCI_DEVICE_TABLE(rt2500pci_device_table) = { { PCI_DEVICE(0x1814, 0x0201) }, { 0, } }; MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2500 PCI & PCMCIA Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2560 PCI & PCMCIA chipset based cards"); MODULE_DEVICE_TABLE(pci, rt2500pci_device_table); MODULE_LICENSE("GPL"); static int rt2500pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { return rt2x00pci_probe(pci_dev, &rt2500pci_ops); } static struct pci_driver rt2500pci_driver = { .name = KBUILD_MODNAME, .id_table = rt2500pci_device_table, .probe = rt2500pci_probe, .remove = __devexit_p(rt2x00pci_remove), .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; module_pci_driver(rt2500pci_driver); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2400pci.h0000644000175000017500000006644512026211315023337 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2400pci Abstract: Data structures and registers for the rt2400pci module. Supported chipsets: RT2460. */ #ifndef RT2400PCI_H #define RT2400PCI_H /* * RF chip defines. */ #define RF2420 0x0000 #define RF2421 0x0001 /* * Signal information. * Default offset is required for RSSI <-> dBm conversion. */ #define DEFAULT_RSSI_OFFSET 100 /* * Register layout information. */ #define CSR_REG_BASE 0x0000 #define CSR_REG_SIZE 0x014c #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0100 #define BBP_BASE 0x0000 #define BBP_SIZE 0x0020 #define RF_BASE 0x0004 #define RF_SIZE 0x000c /* * Number of TX queues. */ #define NUM_TX_QUEUES 2 /* * Control/Status Registers(CSR). * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * CSR0: ASIC revision number. */ #define CSR0 0x0000 #define CSR0_REVISION FIELD32(0x0000ffff) /* * CSR1: System control register. * SOFT_RESET: Software reset, 1: reset, 0: normal. * BBP_RESET: Hardware reset, 1: reset, 0, release. * HOST_READY: Host ready after initialization. */ #define CSR1 0x0004 #define CSR1_SOFT_RESET FIELD32(0x00000001) #define CSR1_BBP_RESET FIELD32(0x00000002) #define CSR1_HOST_READY FIELD32(0x00000004) /* * CSR2: System admin status register (invalid). */ #define CSR2 0x0008 /* * CSR3: STA MAC address register 0. */ #define CSR3 0x000c #define CSR3_BYTE0 FIELD32(0x000000ff) #define CSR3_BYTE1 FIELD32(0x0000ff00) #define CSR3_BYTE2 FIELD32(0x00ff0000) #define CSR3_BYTE3 FIELD32(0xff000000) /* * CSR4: STA MAC address register 1. */ #define CSR4 0x0010 #define CSR4_BYTE4 FIELD32(0x000000ff) #define CSR4_BYTE5 FIELD32(0x0000ff00) /* * CSR5: BSSID register 0. */ #define CSR5 0x0014 #define CSR5_BYTE0 FIELD32(0x000000ff) #define CSR5_BYTE1 FIELD32(0x0000ff00) #define CSR5_BYTE2 FIELD32(0x00ff0000) #define CSR5_BYTE3 FIELD32(0xff000000) /* * CSR6: BSSID register 1. */ #define CSR6 0x0018 #define CSR6_BYTE4 FIELD32(0x000000ff) #define CSR6_BYTE5 FIELD32(0x0000ff00) /* * CSR7: Interrupt source register. * Write 1 to clear interrupt. * TBCN_EXPIRE: Beacon timer expired interrupt. * TWAKE_EXPIRE: Wakeup timer expired interrupt. * TATIMW_EXPIRE: Timer of atim window expired interrupt. * TXDONE_TXRING: Tx ring transmit done interrupt. * TXDONE_ATIMRING: Atim ring transmit done interrupt. * TXDONE_PRIORING: Priority ring transmit done interrupt. * RXDONE: Receive done interrupt. */ #define CSR7 0x001c #define CSR7_TBCN_EXPIRE FIELD32(0x00000001) #define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) #define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) #define CSR7_TXDONE_TXRING FIELD32(0x00000008) #define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) #define CSR7_TXDONE_PRIORING FIELD32(0x00000020) #define CSR7_RXDONE FIELD32(0x00000040) /* * CSR8: Interrupt mask register. * Write 1 to mask interrupt. * TBCN_EXPIRE: Beacon timer expired interrupt. * TWAKE_EXPIRE: Wakeup timer expired interrupt. * TATIMW_EXPIRE: Timer of atim window expired interrupt. * TXDONE_TXRING: Tx ring transmit done interrupt. * TXDONE_ATIMRING: Atim ring transmit done interrupt. * TXDONE_PRIORING: Priority ring transmit done interrupt. * RXDONE: Receive done interrupt. */ #define CSR8 0x0020 #define CSR8_TBCN_EXPIRE FIELD32(0x00000001) #define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) #define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) #define CSR8_TXDONE_TXRING FIELD32(0x00000008) #define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) #define CSR8_TXDONE_PRIORING FIELD32(0x00000020) #define CSR8_RXDONE FIELD32(0x00000040) /* * CSR9: Maximum frame length register. * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. */ #define CSR9 0x0024 #define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) /* * CSR11: Back-off control register. * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). * SLOT_TIME: Slot time, default is 20us for 802.11b. * LONG_RETRY: Long retry count. * SHORT_RETRY: Short retry count. */ #define CSR11 0x002c #define CSR11_CWMIN FIELD32(0x0000000f) #define CSR11_CWMAX FIELD32(0x000000f0) #define CSR11_SLOT_TIME FIELD32(0x00001f00) #define CSR11_LONG_RETRY FIELD32(0x00ff0000) #define CSR11_SHORT_RETRY FIELD32(0xff000000) /* * CSR12: Synchronization configuration register 0. * All units in 1/16 TU. * BEACON_INTERVAL: Beacon interval, default is 100 TU. * CFPMAX_DURATION: Cfp maximum duration, default is 100 TU. */ #define CSR12 0x0030 #define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) #define CSR12_CFP_MAX_DURATION FIELD32(0xffff0000) /* * CSR13: Synchronization configuration register 1. * All units in 1/16 TU. * ATIMW_DURATION: Atim window duration. * CFP_PERIOD: Cfp period, default is 0 TU. */ #define CSR13 0x0034 #define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) #define CSR13_CFP_PERIOD FIELD32(0x00ff0000) /* * CSR14: Synchronization control register. * TSF_COUNT: Enable tsf auto counting. * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. * TBCN: Enable tbcn with reload value. * TCFP: Enable tcfp & cfp / cp switching. * TATIMW: Enable tatimw & atim window switching. * BEACON_GEN: Enable beacon generator. * CFP_COUNT_PRELOAD: Cfp count preload value. * TBCM_PRELOAD: Tbcn preload value in units of 64us. */ #define CSR14 0x0038 #define CSR14_TSF_COUNT FIELD32(0x00000001) #define CSR14_TSF_SYNC FIELD32(0x00000006) #define CSR14_TBCN FIELD32(0x00000008) #define CSR14_TCFP FIELD32(0x00000010) #define CSR14_TATIMW FIELD32(0x00000020) #define CSR14_BEACON_GEN FIELD32(0x00000040) #define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) #define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) /* * CSR15: Synchronization status register. * CFP: ASIC is in contention-free period. * ATIMW: ASIC is in ATIM window. * BEACON_SENT: Beacon is send. */ #define CSR15 0x003c #define CSR15_CFP FIELD32(0x00000001) #define CSR15_ATIMW FIELD32(0x00000002) #define CSR15_BEACON_SENT FIELD32(0x00000004) /* * CSR16: TSF timer register 0. */ #define CSR16 0x0040 #define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) /* * CSR17: TSF timer register 1. */ #define CSR17 0x0044 #define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) /* * CSR18: IFS timer register 0. * SIFS: Sifs, default is 10 us. * PIFS: Pifs, default is 30 us. */ #define CSR18 0x0048 #define CSR18_SIFS FIELD32(0x0000ffff) #define CSR18_PIFS FIELD32(0xffff0000) /* * CSR19: IFS timer register 1. * DIFS: Difs, default is 50 us. * EIFS: Eifs, default is 364 us. */ #define CSR19 0x004c #define CSR19_DIFS FIELD32(0x0000ffff) #define CSR19_EIFS FIELD32(0xffff0000) /* * CSR20: Wakeup timer register. * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. * AUTOWAKE: Enable auto wakeup / sleep mechanism. */ #define CSR20 0x0050 #define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) #define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) #define CSR20_AUTOWAKE FIELD32(0x01000000) /* * CSR21: EEPROM control register. * RELOAD: Write 1 to reload eeprom content. * TYPE_93C46: 1: 93c46, 0:93c66. */ #define CSR21 0x0054 #define CSR21_RELOAD FIELD32(0x00000001) #define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) #define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) #define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) #define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) #define CSR21_TYPE_93C46 FIELD32(0x00000020) /* * CSR22: CFP control register. * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. */ #define CSR22 0x0058 #define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) #define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) /* * Transmit related CSRs. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * TXCSR0: TX Control Register. * KICK_TX: Kick tx ring. * KICK_ATIM: Kick atim ring. * KICK_PRIO: Kick priority ring. * ABORT: Abort all transmit related ring operation. */ #define TXCSR0 0x0060 #define TXCSR0_KICK_TX FIELD32(0x00000001) #define TXCSR0_KICK_ATIM FIELD32(0x00000002) #define TXCSR0_KICK_PRIO FIELD32(0x00000004) #define TXCSR0_ABORT FIELD32(0x00000008) /* * TXCSR1: TX Configuration Register. * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. * TSF_OFFSET: Insert tsf offset. * AUTORESPONDER: Enable auto responder which include ack & cts. */ #define TXCSR1 0x0064 #define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) #define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) #define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) #define TXCSR1_AUTORESPONDER FIELD32(0x01000000) /* * TXCSR2: Tx descriptor configuration register. * TXD_SIZE: Tx descriptor size, default is 48. * NUM_TXD: Number of tx entries in ring. * NUM_ATIM: Number of atim entries in ring. * NUM_PRIO: Number of priority entries in ring. */ #define TXCSR2 0x0068 #define TXCSR2_TXD_SIZE FIELD32(0x000000ff) #define TXCSR2_NUM_TXD FIELD32(0x0000ff00) #define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) #define TXCSR2_NUM_PRIO FIELD32(0xff000000) /* * TXCSR3: TX Ring Base address register. */ #define TXCSR3 0x006c #define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR4: TX Atim Ring Base address register. */ #define TXCSR4 0x0070 #define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR5: TX Prio Ring Base address register. */ #define TXCSR5 0x0074 #define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR6: Beacon Base address register. */ #define TXCSR6 0x0078 #define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) /* * TXCSR7: Auto responder control register. * AR_POWERMANAGEMENT: Auto responder power management bit. */ #define TXCSR7 0x007c #define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) /* * Receive related CSRs. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * RXCSR0: RX Control Register. * DISABLE_RX: Disable rx engine. * DROP_CRC: Drop crc error. * DROP_PHYSICAL: Drop physical error. * DROP_CONTROL: Drop control frame. * DROP_NOT_TO_ME: Drop not to me unicast frame. * DROP_TODS: Drop frame tods bit is true. * DROP_VERSION_ERROR: Drop version error frame. * PASS_CRC: Pass all packets with crc attached. */ #define RXCSR0 0x0080 #define RXCSR0_DISABLE_RX FIELD32(0x00000001) #define RXCSR0_DROP_CRC FIELD32(0x00000002) #define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) #define RXCSR0_DROP_CONTROL FIELD32(0x00000008) #define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) #define RXCSR0_DROP_TODS FIELD32(0x00000020) #define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) #define RXCSR0_PASS_CRC FIELD32(0x00000080) /* * RXCSR1: RX descriptor configuration register. * RXD_SIZE: Rx descriptor size, default is 32b. * NUM_RXD: Number of rx entries in ring. */ #define RXCSR1 0x0084 #define RXCSR1_RXD_SIZE FIELD32(0x000000ff) #define RXCSR1_NUM_RXD FIELD32(0x0000ff00) /* * RXCSR2: RX Ring base address register. */ #define RXCSR2 0x0088 #define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) /* * RXCSR3: BBP ID register for Rx operation. * BBP_ID#: BBP register # id. * BBP_ID#_VALID: BBP register # id is valid or not. */ #define RXCSR3 0x0090 #define RXCSR3_BBP_ID0 FIELD32(0x0000007f) #define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) #define RXCSR3_BBP_ID1 FIELD32(0x00007f00) #define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) #define RXCSR3_BBP_ID2 FIELD32(0x007f0000) #define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) #define RXCSR3_BBP_ID3 FIELD32(0x7f000000) #define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) /* * RXCSR4: BBP ID register for Rx operation. * BBP_ID#: BBP register # id. * BBP_ID#_VALID: BBP register # id is valid or not. */ #define RXCSR4 0x0094 #define RXCSR4_BBP_ID4 FIELD32(0x0000007f) #define RXCSR4_BBP_ID4_VALID FIELD32(0x00000080) #define RXCSR4_BBP_ID5 FIELD32(0x00007f00) #define RXCSR4_BBP_ID5_VALID FIELD32(0x00008000) /* * ARCSR0: Auto Responder PLCP config register 0. * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. */ #define ARCSR0 0x0098 #define ARCSR0_AR_BBP_DATA0 FIELD32(0x000000ff) #define ARCSR0_AR_BBP_ID0 FIELD32(0x0000ff00) #define ARCSR0_AR_BBP_DATA1 FIELD32(0x00ff0000) #define ARCSR0_AR_BBP_ID1 FIELD32(0xff000000) /* * ARCSR1: Auto Responder PLCP config register 1. * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. */ #define ARCSR1 0x009c #define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) #define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) #define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) #define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) /* * Miscellaneous Registers. * Some values are set in TU, whereas 1 TU == 1024 us. */ /* * PCICSR: PCI control register. * BIG_ENDIAN: 1: big endian, 0: little endian. * RX_TRESHOLD: Rx threshold in dw to start pci access * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. * TX_TRESHOLD: Tx threshold in dw to start pci access * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. */ #define PCICSR 0x008c #define PCICSR_BIG_ENDIAN FIELD32(0x00000001) #define PCICSR_RX_TRESHOLD FIELD32(0x00000006) #define PCICSR_TX_TRESHOLD FIELD32(0x00000018) #define PCICSR_BURST_LENTH FIELD32(0x00000060) #define PCICSR_ENABLE_CLK FIELD32(0x00000080) /* * CNT0: FCS error count. * FCS_ERROR: FCS error count, cleared when read. */ #define CNT0 0x00a0 #define CNT0_FCS_ERROR FIELD32(0x0000ffff) /* * Statistic Register. * CNT1: PLCP error count. * CNT2: Long error count. * CNT3: CCA false alarm count. * CNT4: Rx FIFO overflow count. * CNT5: Tx FIFO underrun count. */ #define TIMECSR2 0x00a8 #define CNT1 0x00ac #define CNT2 0x00b0 #define TIMECSR3 0x00b4 #define CNT3 0x00b8 #define CNT4 0x00bc #define CNT5 0x00c0 /* * Baseband Control Register. */ /* * PWRCSR0: Power mode configuration register. */ #define PWRCSR0 0x00c4 /* * Power state transition time registers. */ #define PSCSR0 0x00c8 #define PSCSR1 0x00cc #define PSCSR2 0x00d0 #define PSCSR3 0x00d4 /* * PWRCSR1: Manual power control / status register. * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. * SET_STATE: Set state. Write 1 to trigger, self cleared. * BBP_DESIRE_STATE: BBP desired state. * RF_DESIRE_STATE: RF desired state. * BBP_CURR_STATE: BBP current state. * RF_CURR_STATE: RF current state. * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. */ #define PWRCSR1 0x00d8 #define PWRCSR1_SET_STATE FIELD32(0x00000001) #define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) #define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) #define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) #define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) #define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) /* * TIMECSR: Timer control register. * US_COUNT: 1 us timer count in units of clock cycles. * US_64_COUNT: 64 us timer count in units of 1 us timer. * BEACON_EXPECT: Beacon expect window. */ #define TIMECSR 0x00dc #define TIMECSR_US_COUNT FIELD32(0x000000ff) #define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) #define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) /* * MACCSR0: MAC configuration register 0. */ #define MACCSR0 0x00e0 /* * MACCSR1: MAC configuration register 1. * KICK_RX: Kick one-shot rx in one-shot rx mode. * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. * BBPRX_RESET_MODE: Ralink bbp rx reset mode. * AUTO_TXBBP: Auto tx logic access bbp control register. * AUTO_RXBBP: Auto rx logic access bbp control register. * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. * INTERSIL_IF: Intersil if calibration pin. */ #define MACCSR1 0x00e4 #define MACCSR1_KICK_RX FIELD32(0x00000001) #define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) #define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) #define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) #define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) #define MACCSR1_LOOPBACK FIELD32(0x00000060) #define MACCSR1_INTERSIL_IF FIELD32(0x00000080) /* * RALINKCSR: Ralink Rx auto-reset BBCR. * AR_BBP_DATA#: Auto reset BBP register # data. * AR_BBP_ID#: Auto reset BBP register # id. */ #define RALINKCSR 0x00e8 #define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) #define RALINKCSR_AR_BBP_ID0 FIELD32(0x0000ff00) #define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) #define RALINKCSR_AR_BBP_ID1 FIELD32(0xff000000) /* * BCNCSR: Beacon interval control register. * CHANGE: Write one to change beacon interval. * DELTATIME: The delta time value. * NUM_BEACON: Number of beacon according to mode. * MODE: Please refer to asic specs. * PLUS: Plus or minus delta time value. */ #define BCNCSR 0x00ec #define BCNCSR_CHANGE FIELD32(0x00000001) #define BCNCSR_DELTATIME FIELD32(0x0000001e) #define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) #define BCNCSR_MODE FIELD32(0x00006000) #define BCNCSR_PLUS FIELD32(0x00008000) /* * BBP / RF / IF Control Register. */ /* * BBPCSR: BBP serial control register. * VALUE: Register value to program into BBP. * REGNUM: Selected BBP register. * BUSY: 1: asic is busy execute BBP programming. * WRITE_CONTROL: 1: write BBP, 0: read BBP. */ #define BBPCSR 0x00f0 #define BBPCSR_VALUE FIELD32(0x000000ff) #define BBPCSR_REGNUM FIELD32(0x00007f00) #define BBPCSR_BUSY FIELD32(0x00008000) #define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) /* * RFCSR: RF serial control register. * VALUE: Register value + id to program into rf/if. * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). * IF_SELECT: Chip to program: 0: rf, 1: if. * PLL_LD: Rf pll_ld status. * BUSY: 1: asic is busy execute rf programming. */ #define RFCSR 0x00f4 #define RFCSR_VALUE FIELD32(0x00ffffff) #define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) #define RFCSR_IF_SELECT FIELD32(0x20000000) #define RFCSR_PLL_LD FIELD32(0x40000000) #define RFCSR_BUSY FIELD32(0x80000000) /* * LEDCSR: LED control register. * ON_PERIOD: On period, default 70ms. * OFF_PERIOD: Off period, default 30ms. * LINK: 0: linkoff, 1: linkup. * ACTIVITY: 0: idle, 1: active. */ #define LEDCSR 0x00f8 #define LEDCSR_ON_PERIOD FIELD32(0x000000ff) #define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) #define LEDCSR_LINK FIELD32(0x00010000) #define LEDCSR_ACTIVITY FIELD32(0x00020000) /* * ASIC pointer information. * RXPTR: Current RX ring address. * TXPTR: Current Tx ring address. * PRIPTR: Current Priority ring address. * ATIMPTR: Current ATIM ring address. */ #define RXPTR 0x0100 #define TXPTR 0x0104 #define PRIPTR 0x0108 #define ATIMPTR 0x010c /* * GPIO and others. */ /* * GPIOCSR: GPIO control register. * GPIOCSR_VALx: Actual GPIO pin x value * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input */ #define GPIOCSR 0x0120 #define GPIOCSR_VAL0 FIELD32(0x00000001) #define GPIOCSR_VAL1 FIELD32(0x00000002) #define GPIOCSR_VAL2 FIELD32(0x00000004) #define GPIOCSR_VAL3 FIELD32(0x00000008) #define GPIOCSR_VAL4 FIELD32(0x00000010) #define GPIOCSR_VAL5 FIELD32(0x00000020) #define GPIOCSR_VAL6 FIELD32(0x00000040) #define GPIOCSR_VAL7 FIELD32(0x00000080) #define GPIOCSR_DIR0 FIELD32(0x00000100) #define GPIOCSR_DIR1 FIELD32(0x00000200) #define GPIOCSR_DIR2 FIELD32(0x00000400) #define GPIOCSR_DIR3 FIELD32(0x00000800) #define GPIOCSR_DIR4 FIELD32(0x00001000) #define GPIOCSR_DIR5 FIELD32(0x00002000) #define GPIOCSR_DIR6 FIELD32(0x00004000) #define GPIOCSR_DIR7 FIELD32(0x00008000) /* * BBPPCSR: BBP Pin control register. */ #define BBPPCSR 0x0124 /* * BCNCSR1: Tx BEACON offset time control register. * PRELOAD: Beacon timer offset in units of usec. */ #define BCNCSR1 0x0130 #define BCNCSR1_PRELOAD FIELD32(0x0000ffff) /* * MACCSR2: TX_PE to RX_PE turn-around time control register * DELAY: RX_PE low width, in units of pci clock cycle. */ #define MACCSR2 0x0134 #define MACCSR2_DELAY FIELD32(0x000000ff) /* * ARCSR2: 1 Mbps ACK/CTS PLCP. */ #define ARCSR2 0x013c #define ARCSR2_SIGNAL FIELD32(0x000000ff) #define ARCSR2_SERVICE FIELD32(0x0000ff00) #define ARCSR2_LENGTH_LOW FIELD32(0x00ff0000) #define ARCSR2_LENGTH FIELD32(0xffff0000) /* * ARCSR3: 2 Mbps ACK/CTS PLCP. */ #define ARCSR3 0x0140 #define ARCSR3_SIGNAL FIELD32(0x000000ff) #define ARCSR3_SERVICE FIELD32(0x0000ff00) #define ARCSR3_LENGTH FIELD32(0xffff0000) /* * ARCSR4: 5.5 Mbps ACK/CTS PLCP. */ #define ARCSR4 0x0144 #define ARCSR4_SIGNAL FIELD32(0x000000ff) #define ARCSR4_SERVICE FIELD32(0x0000ff00) #define ARCSR4_LENGTH FIELD32(0xffff0000) /* * ARCSR5: 11 Mbps ACK/CTS PLCP. */ #define ARCSR5 0x0148 #define ARCSR5_SIGNAL FIELD32(0x000000ff) #define ARCSR5_SERVICE FIELD32(0x0000ff00) #define ARCSR5_LENGTH FIELD32(0xffff0000) /* * BBP registers. * The wordsize of the BBP is 8 bits. */ /* * R1: TX antenna control */ #define BBP_R1_TX_ANTENNA FIELD8(0x03) /* * R4: RX antenna control */ #define BBP_R4_RX_ANTENNA FIELD8(0x06) /* * RF registers */ /* * RF 1 */ #define RF1_TUNER FIELD32(0x00020000) /* * RF 3 */ #define RF3_TUNER FIELD32(0x00000100) #define RF3_TXPOWER FIELD32(0x00003e00) /* * EEPROM content. * The wordsize of the EEPROM is 16 bits. */ /* * HW MAC address. */ #define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) #define EEPROM_MAC_ADDR1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) #define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) /* * EEPROM antenna. * ANTENNA_NUM: Number of antenna's. * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RF_TYPE: Rf_type of this adapter. * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. * RX_AGCVGC: 0: disable, 1:enable BBP R13 tuning. * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. */ #define EEPROM_ANTENNA 0x0b #define EEPROM_ANTENNA_NUM FIELD16(0x0003) #define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) #define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) #define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0040) #define EEPROM_ANTENNA_LED_MODE FIELD16(0x0180) #define EEPROM_ANTENNA_RX_AGCVGC_TUNING FIELD16(0x0200) #define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) /* * EEPROM BBP. */ #define EEPROM_BBP_START 0x0c #define EEPROM_BBP_SIZE 7 #define EEPROM_BBP_VALUE FIELD16(0x00ff) #define EEPROM_BBP_REG_ID FIELD16(0xff00) /* * EEPROM TXPOWER */ #define EEPROM_TXPOWER_START 0x13 #define EEPROM_TXPOWER_SIZE 7 #define EEPROM_TXPOWER_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_2 FIELD16(0xff00) /* * DMA descriptor defines. */ #define TXD_DESC_SIZE (8 * sizeof(__le32)) #define RXD_DESC_SIZE (8 * sizeof(__le32)) /* * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. */ /* * Word0 */ #define TXD_W0_OWNER_NIC FIELD32(0x00000001) #define TXD_W0_VALID FIELD32(0x00000002) #define TXD_W0_RESULT FIELD32(0x0000001c) #define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) #define TXD_W0_MORE_FRAG FIELD32(0x00000100) #define TXD_W0_ACK FIELD32(0x00000200) #define TXD_W0_TIMESTAMP FIELD32(0x00000400) #define TXD_W0_RTS FIELD32(0x00000800) #define TXD_W0_IFS FIELD32(0x00006000) #define TXD_W0_RETRY_MODE FIELD32(0x00008000) #define TXD_W0_AGC FIELD32(0x00ff0000) #define TXD_W0_R2 FIELD32(0xff000000) /* * Word1 */ #define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) /* * Word2 */ #define TXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) #define TXD_W2_DATABYTE_COUNT FIELD32(0xffff0000) /* * Word3 & 4: PLCP information * The PLCP values should be treated as if they were BBP values. */ #define TXD_W3_PLCP_SIGNAL FIELD32(0x000000ff) #define TXD_W3_PLCP_SIGNAL_REGNUM FIELD32(0x00007f00) #define TXD_W3_PLCP_SIGNAL_BUSY FIELD32(0x00008000) #define TXD_W3_PLCP_SERVICE FIELD32(0x00ff0000) #define TXD_W3_PLCP_SERVICE_REGNUM FIELD32(0x7f000000) #define TXD_W3_PLCP_SERVICE_BUSY FIELD32(0x80000000) #define TXD_W4_PLCP_LENGTH_LOW FIELD32(0x000000ff) #define TXD_W3_PLCP_LENGTH_LOW_REGNUM FIELD32(0x00007f00) #define TXD_W3_PLCP_LENGTH_LOW_BUSY FIELD32(0x00008000) #define TXD_W4_PLCP_LENGTH_HIGH FIELD32(0x00ff0000) #define TXD_W3_PLCP_LENGTH_HIGH_REGNUM FIELD32(0x7f000000) #define TXD_W3_PLCP_LENGTH_HIGH_BUSY FIELD32(0x80000000) /* * Word5 */ #define TXD_W5_BBCR4 FIELD32(0x0000ffff) #define TXD_W5_AGC_REG FIELD32(0x007f0000) #define TXD_W5_AGC_REG_VALID FIELD32(0x00800000) #define TXD_W5_XXX_REG FIELD32(0x7f000000) #define TXD_W5_XXX_REG_VALID FIELD32(0x80000000) /* * Word6 */ #define TXD_W6_SK_BUFF FIELD32(0xffffffff) /* * Word7 */ #define TXD_W7_RESERVED FIELD32(0xffffffff) /* * RX descriptor format for RX Ring. */ /* * Word0 */ #define RXD_W0_OWNER_NIC FIELD32(0x00000001) #define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) #define RXD_W0_MULTICAST FIELD32(0x00000004) #define RXD_W0_BROADCAST FIELD32(0x00000008) #define RXD_W0_MY_BSS FIELD32(0x00000010) #define RXD_W0_CRC_ERROR FIELD32(0x00000020) #define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) #define RXD_W0_DATABYTE_COUNT FIELD32(0xffff0000) /* * Word1 */ #define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) /* * Word2 */ #define RXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) #define RXD_W2_BBR0 FIELD32(0x00ff0000) #define RXD_W2_SIGNAL FIELD32(0xff000000) /* * Word3 */ #define RXD_W3_RSSI FIELD32(0x000000ff) #define RXD_W3_BBR3 FIELD32(0x0000ff00) #define RXD_W3_BBR4 FIELD32(0x00ff0000) #define RXD_W3_BBR5 FIELD32(0xff000000) /* * Word4 */ #define RXD_W4_RX_END_TIME FIELD32(0xffffffff) /* * Word5 & 6 & 7: Reserved */ #define RXD_W5_RESERVED FIELD32(0xffffffff) #define RXD_W6_RESERVED FIELD32(0xffffffff) #define RXD_W7_RESERVED FIELD32(0xffffffff) /* * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. * NOTE: Logics in rt2400pci for txpower are reversed * compared to the other rt2x00 drivers. A higher txpower * value means that the txpower must be lowered. This is * important when converting the value coming from the * mac80211 stack to the rt2400 acceptable value. */ #define MIN_TXPOWER 31 #define MAX_TXPOWER 62 #define DEFAULT_TXPOWER 39 #define __CLAMP_TX(__txpower) \ clamp_t(char, (__txpower), MIN_TXPOWER, MAX_TXPOWER) #define TXPOWER_FROM_DEV(__txpower) \ ((__CLAMP_TX(__txpower) - MAX_TXPOWER) + MIN_TXPOWER) #define TXPOWER_TO_DEV(__txpower) \ (MAX_TXPOWER - (__CLAMP_TX(__txpower) - MIN_TXPOWER)) #endif /* RT2400PCI_H */ compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/rt2400pci.c0000644000175000017500000015134412026211315023323 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2009 Ivo van Doorn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: rt2400pci Abstract: rt2400pci device specific routines. Supported chipsets: RT2460. */ #include #include #include #include #include #include #include #include #include "rt2x00.h" #include "rt2x00pci.h" #include "rt2400pci.h" /* * Register access. * All access to the CSR registers will go through the methods * rt2x00pci_register_read and rt2x00pci_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay * between each attempt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. */ #define WAIT_FOR_BBP(__dev, __reg) \ rt2x00pci_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) #define WAIT_FOR_RF(__dev, __reg) \ rt2x00pci_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) static void rt2400pci_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, BBPCSR_VALUE, value); rt2x00_set_field32(®, BBPCSR_REGNUM, word); rt2x00_set_field32(®, BBPCSR_BUSY, 1); rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2400pci_bbp_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the BBP becomes available, afterwards we * can safely write the read request into the register. * After the data has been written, we wait until hardware * returns the correct value, if at any time the register * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ if (WAIT_FOR_BBP(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, BBPCSR_REGNUM, word); rt2x00_set_field32(®, BBPCSR_BUSY, 1); rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, BBPCSR_VALUE); mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2400pci_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; mutex_lock(&rt2x00dev->csr_mutex); /* * Wait until the RF becomes available, afterwards we * can safely write the new data into the register. */ if (WAIT_FOR_RF(rt2x00dev, ®)) { reg = 0; rt2x00_set_field32(®, RFCSR_VALUE, value); rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); rt2x00_set_field32(®, RFCSR_BUSY, 1); rt2x00pci_register_write(rt2x00dev, RFCSR, reg); rt2x00_rf_write(rt2x00dev, word, value); } mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2400pci_eepromregister_read(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg; rt2x00pci_register_read(rt2x00dev, CSR21, ®); eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); eeprom->reg_data_clock = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_CLOCK); eeprom->reg_chip_select = !!rt2x00_get_field32(reg, CSR21_EEPROM_CHIP_SELECT); } static void rt2400pci_eepromregister_write(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg = 0; rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, !!eeprom->reg_data_in); rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, !!eeprom->reg_data_out); rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, !!eeprom->reg_data_clock); rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, !!eeprom->reg_chip_select); rt2x00pci_register_write(rt2x00dev, CSR21, reg); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS static const struct rt2x00debug rt2400pci_rt2x00debug = { .owner = THIS_MODULE, .csr = { .read = rt2x00pci_register_read, .write = rt2x00pci_register_write, .flags = RT2X00DEBUGFS_OFFSET, .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt2400pci_bbp_read, .write = rt2400pci_bbp_write, .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt2400pci_rf_write, .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); return rt2x00_get_field32(reg, GPIOCSR_VAL0); } #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2400pci_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); unsigned int enabled = brightness != LED_OFF; u32 reg; rt2x00pci_register_read(led->rt2x00dev, LEDCSR, ®); if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) rt2x00_set_field32(®, LEDCSR_LINK, enabled); else if (led->type == LED_TYPE_ACTIVITY) rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); rt2x00pci_register_write(led->rt2x00dev, LEDCSR, reg); } static int rt2400pci_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct rt2x00_led *led = container_of(led_cdev, struct rt2x00_led, led_dev); u32 reg; rt2x00pci_register_read(led->rt2x00dev, LEDCSR, ®); rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); rt2x00pci_register_write(led->rt2x00dev, LEDCSR, reg); return 0; } static void rt2400pci_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; led->type = type; led->led_dev.brightness_set = rt2400pci_brightness_set; led->led_dev.blink_set = rt2400pci_blink_set; led->flags = LED_INITIALIZED; } #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Configuration handlers. */ static void rt2400pci_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { u32 reg; /* * Start configuration steps. * Note that the version error will always be dropped * since there is no filter for it at this time. */ rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); rt2x00_set_field32(®, RXCSR0_DROP_CRC, !(filter_flags & FIF_FCSFAIL)); rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field32(®, RXCSR0_DROP_TODS, !(filter_flags & FIF_PROMISC_IN_BSS) && !rt2x00dev->intf_ap_count); rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); } static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct rt2x00intf_conf *conf, const unsigned int flags) { unsigned int bcn_preload; u32 reg; if (flags & CONFIG_UPDATE_TYPE) { /* * Enable beacon config */ bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); rt2x00pci_register_read(rt2x00dev, BCNCSR1, ®); rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); rt2x00pci_register_write(rt2x00dev, BCNCSR1, reg); /* * Enable synchronisation. */ rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); rt2x00pci_register_write(rt2x00dev, CSR14, reg); } if (flags & CONFIG_UPDATE_MAC) rt2x00pci_register_multiwrite(rt2x00dev, CSR3, conf->mac, sizeof(conf->mac)); if (flags & CONFIG_UPDATE_BSSID) rt2x00pci_register_multiwrite(rt2x00dev, CSR5, conf->bssid, sizeof(conf->bssid)); } static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, u32 changed) { int preamble_mask; u32 reg; /* * When short preamble is enabled, we should set bit 0x08 */ if (changed & BSS_CHANGED_ERP_PREAMBLE) { preamble_mask = erp->short_preamble << 3; rt2x00pci_register_read(rt2x00dev, TXCSR1, ®); rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x1ff); rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0x13a); rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); rt2x00pci_register_write(rt2x00dev, TXCSR1, reg); rt2x00pci_register_read(rt2x00dev, ARCSR2, ®); rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10)); rt2x00pci_register_write(rt2x00dev, ARCSR2, reg); rt2x00pci_register_read(rt2x00dev, ARCSR3, ®); rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20)); rt2x00pci_register_write(rt2x00dev, ARCSR3, reg); rt2x00pci_register_read(rt2x00dev, ARCSR4, ®); rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55)); rt2x00pci_register_write(rt2x00dev, ARCSR4, reg); rt2x00pci_register_read(rt2x00dev, ARCSR5, ®); rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); rt2x00_set_field32(®, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110)); rt2x00pci_register_write(rt2x00dev, ARCSR5, reg); } if (changed & BSS_CHANGED_BASIC_RATES) rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates); if (changed & BSS_CHANGED_ERP_SLOT) { rt2x00pci_register_read(rt2x00dev, CSR11, ®); rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); rt2x00pci_register_write(rt2x00dev, CSR11, reg); rt2x00pci_register_read(rt2x00dev, CSR18, ®); rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); rt2x00pci_register_write(rt2x00dev, CSR18, reg); rt2x00pci_register_read(rt2x00dev, CSR19, ®); rt2x00_set_field32(®, CSR19_DIFS, erp->difs); rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); rt2x00pci_register_write(rt2x00dev, CSR19, reg); } if (changed & BSS_CHANGED_BEACON_INT) { rt2x00pci_register_read(rt2x00dev, CSR12, ®); rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, erp->beacon_int * 16); rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16); rt2x00pci_register_write(rt2x00dev, CSR12, reg); } } static void rt2400pci_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) { u8 r1; u8 r4; /* * We should never come here because rt2x00lib is supposed * to catch this and send us the correct antenna explicitely. */ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || ant->tx == ANTENNA_SW_DIVERSITY); rt2400pci_bbp_read(rt2x00dev, 4, &r4); rt2400pci_bbp_read(rt2x00dev, 1, &r1); /* * Configure the TX antenna. */ switch (ant->tx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 1); break; case ANTENNA_A: rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 0); break; case ANTENNA_B: default: rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 2); break; } /* * Configure the RX antenna. */ switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 1); break; case ANTENNA_A: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 0); break; case ANTENNA_B: default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 2); break; } rt2400pci_bbp_write(rt2x00dev, 4, r4); rt2400pci_bbp_write(rt2x00dev, 1, r1); } static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, struct rf_channel *rf) { /* * Switch on tuning bits. */ rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); rt2400pci_rf_write(rt2x00dev, 2, rf->rf2); rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); /* * RF2420 chipset don't need any additional actions. */ if (rt2x00_rf(rt2x00dev, RF2420)) return; /* * For the RT2421 chipsets we need to write an invalid * reference clock rate to activate auto_tune. * After that we set the value back to the correct channel. */ rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); rt2400pci_rf_write(rt2x00dev, 2, 0x000c2a32); rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); msleep(1); rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); rt2400pci_rf_write(rt2x00dev, 2, rf->rf2); rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); msleep(1); /* * Switch off tuning bits. */ rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); rt2x00_set_field32(&rf->rf3, RF3_TUNER, 0); rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); /* * Clear false CRC during channel switch. */ rt2x00pci_register_read(rt2x00dev, CNT0, &rf->rf1); } static void rt2400pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) { rt2400pci_bbp_write(rt2x00dev, 3, TXPOWER_TO_DEV(txpower)); } static void rt2400pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { u32 reg; rt2x00pci_register_read(rt2x00dev, CSR11, ®); rt2x00_set_field32(®, CSR11_LONG_RETRY, libconf->conf->long_frame_max_tx_count); rt2x00_set_field32(®, CSR11_SHORT_RETRY, libconf->conf->short_frame_max_tx_count); rt2x00pci_register_write(rt2x00dev, CSR11, reg); } static void rt2400pci_config_ps(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { enum dev_state state = (libconf->conf->flags & IEEE80211_CONF_PS) ? STATE_SLEEP : STATE_AWAKE; u32 reg; if (state == STATE_SLEEP) { rt2x00pci_register_read(rt2x00dev, CSR20, ®); rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, (rt2x00dev->beacon_int - 20) * 16); rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, libconf->conf->listen_interval - 1); /* We must first disable autowake before it can be enabled */ rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); rt2x00pci_register_write(rt2x00dev, CSR20, reg); rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); rt2x00pci_register_write(rt2x00dev, CSR20, reg); } else { rt2x00pci_register_read(rt2x00dev, CSR20, ®); rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); rt2x00pci_register_write(rt2x00dev, CSR20, reg); } rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } static void rt2400pci_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { if (flags & IEEE80211_CONF_CHANGE_CHANNEL) rt2400pci_config_channel(rt2x00dev, &libconf->rf); if (flags & IEEE80211_CONF_CHANGE_POWER) rt2400pci_config_txpower(rt2x00dev, libconf->conf->power_level); if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) rt2400pci_config_retry_limit(rt2x00dev, libconf); if (flags & IEEE80211_CONF_CHANGE_PS) rt2400pci_config_ps(rt2x00dev, libconf); } static void rt2400pci_config_cw(struct rt2x00_dev *rt2x00dev, const int cw_min, const int cw_max) { u32 reg; rt2x00pci_register_read(rt2x00dev, CSR11, ®); rt2x00_set_field32(®, CSR11_CWMIN, cw_min); rt2x00_set_field32(®, CSR11_CWMAX, cw_max); rt2x00pci_register_write(rt2x00dev, CSR11, reg); } /* * Link tuning */ static void rt2400pci_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { u32 reg; u8 bbp; /* * Update FCS error count from register. */ rt2x00pci_register_read(rt2x00dev, CNT0, ®); qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); /* * Update False CCA count from register. */ rt2400pci_bbp_read(rt2x00dev, 39, &bbp); qual->false_cca = bbp; } static inline void rt2400pci_set_vgc(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, u8 vgc_level) { if (qual->vgc_level_reg != vgc_level) { rt2400pci_bbp_write(rt2x00dev, 13, vgc_level); qual->vgc_level = vgc_level; qual->vgc_level_reg = vgc_level; } } static void rt2400pci_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) { rt2400pci_set_vgc(rt2x00dev, qual, 0x08); } static void rt2400pci_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count) { /* * The link tuner should not run longer then 60 seconds, * and should run once every 2 seconds. */ if (count > 60 || !(count & 1)) return; /* * Base r13 link tuning on the false cca count. */ if ((qual->false_cca > 512) && (qual->vgc_level < 0x20)) rt2400pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); else if ((qual->false_cca < 100) && (qual->vgc_level > 0x08)) rt2400pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); } /* * Queue handlers. */ static void rt2400pci_start_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_RX: rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); rt2x00_set_field32(®, CSR14_TBCN, 1); rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, CSR14, reg); break; default: break; } } static void rt2400pci_kick_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_AC_VO: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; case QID_AC_VI: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; case QID_ATIM: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; default: break; } } static void rt2400pci_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; u32 reg; switch (queue->qid) { case QID_AC_VO: case QID_AC_VI: case QID_ATIM: rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); rt2x00_set_field32(®, TXCSR0_ABORT, 1); rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); break; case QID_RX: rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); break; case QID_BEACON: rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); rt2x00_set_field32(®, CSR14_TBCN, 0); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); /* * Wait for possibly running tbtt tasklets. */ tasklet_kill(&rt2x00dev->tbtt_tasklet); break; default: break; } } /* * Initialization functions. */ static bool rt2400pci_get_entry_state(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 0, &word); return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); } else { rt2x00_desc_read(entry_priv->desc, 0, &word); return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || rt2x00_get_field32(word, TXD_W0_VALID)); } } static void rt2400pci_clear_entry(struct queue_entry *entry) { struct queue_entry_priv_pci *entry_priv = entry->priv_data; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); u32 word; if (entry->queue->qid == QID_RX) { rt2x00_desc_read(entry_priv->desc, 2, &word); rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, entry->skb->len); rt2x00_desc_write(entry_priv->desc, 2, word); rt2x00_desc_read(entry_priv->desc, 1, &word); rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); rt2x00_desc_write(entry_priv->desc, 1, word); rt2x00_desc_read(entry_priv->desc, 0, &word); rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); rt2x00_desc_write(entry_priv->desc, 0, word); } else { rt2x00_desc_read(entry_priv->desc, 0, &word); rt2x00_set_field32(&word, TXD_W0_VALID, 0); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); rt2x00_desc_write(entry_priv->desc, 0, word); } } static int rt2400pci_init_queues(struct rt2x00_dev *rt2x00dev) { struct queue_entry_priv_pci *entry_priv; u32 reg; /* * Initialize registers. */ rt2x00pci_register_read(rt2x00dev, TXCSR2, ®); rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); rt2x00pci_register_write(rt2x00dev, TXCSR2, reg); entry_priv = rt2x00dev->tx[1].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR3, ®); rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR3, reg); entry_priv = rt2x00dev->tx[0].entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR5, ®); rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR5, reg); entry_priv = rt2x00dev->atim->entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR4, ®); rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR4, reg); entry_priv = rt2x00dev->bcn->entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, TXCSR6, ®); rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, TXCSR6, reg); rt2x00pci_register_read(rt2x00dev, RXCSR1, ®); rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); rt2x00pci_register_write(rt2x00dev, RXCSR1, reg); entry_priv = rt2x00dev->rx->entries[0].priv_data; rt2x00pci_register_read(rt2x00dev, RXCSR2, ®); rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, entry_priv->desc_dma); rt2x00pci_register_write(rt2x00dev, RXCSR2, reg); return 0; } static int rt2400pci_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; rt2x00pci_register_write(rt2x00dev, PSCSR0, 0x00020002); rt2x00pci_register_write(rt2x00dev, PSCSR1, 0x00000002); rt2x00pci_register_write(rt2x00dev, PSCSR2, 0x00023f20); rt2x00pci_register_write(rt2x00dev, PSCSR3, 0x00000002); rt2x00pci_register_read(rt2x00dev, TIMECSR, ®); rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); rt2x00pci_register_write(rt2x00dev, TIMECSR, reg); rt2x00pci_register_read(rt2x00dev, CSR9, ®); rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, (rt2x00dev->rx->data_size / 128)); rt2x00pci_register_write(rt2x00dev, CSR9, reg); rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); rt2x00_set_field32(®, CSR14_TBCN, 0); rt2x00_set_field32(®, CSR14_TCFP, 0); rt2x00_set_field32(®, CSR14_TATIMW, 0); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); rt2x00pci_register_write(rt2x00dev, CNT3, 0x3f080000); rt2x00pci_register_read(rt2x00dev, ARCSR0, ®); rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA0, 133); rt2x00_set_field32(®, ARCSR0_AR_BBP_ID0, 134); rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA1, 136); rt2x00_set_field32(®, ARCSR0_AR_BBP_ID1, 135); rt2x00pci_register_write(rt2x00dev, ARCSR0, reg); rt2x00pci_register_read(rt2x00dev, RXCSR3, ®); rt2x00_set_field32(®, RXCSR3_BBP_ID0, 3); /* Tx power.*/ rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID1, 32); /* Signal */ rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID2, 36); /* Rssi */ rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); rt2x00pci_register_write(rt2x00dev, RXCSR3, reg); rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; rt2x00pci_register_write(rt2x00dev, MACCSR0, 0x00217223); rt2x00pci_register_write(rt2x00dev, MACCSR1, 0x00235518); rt2x00pci_register_read(rt2x00dev, MACCSR2, ®); rt2x00_set_field32(®, MACCSR2_DELAY, 64); rt2x00pci_register_write(rt2x00dev, MACCSR2, reg); rt2x00pci_register_read(rt2x00dev, RALINKCSR, ®); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 154); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 154); rt2x00pci_register_write(rt2x00dev, RALINKCSR, reg); rt2x00pci_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, CSR1_BBP_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 0); rt2x00pci_register_write(rt2x00dev, CSR1, reg); rt2x00pci_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 1); rt2x00pci_register_write(rt2x00dev, CSR1, reg); /* * We must clear the FCS and FIFO error count. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ rt2x00pci_register_read(rt2x00dev, CNT0, ®); rt2x00pci_register_read(rt2x00dev, CNT4, ®); return 0; } static int rt2400pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2400pci_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) return 0; udelay(REGISTER_BUSY_DELAY); } ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); return -EACCES; } static int rt2400pci_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; u16 eeprom; u8 reg_id; u8 value; if (unlikely(rt2400pci_wait_bbp_ready(rt2x00dev))) return -EACCES; rt2400pci_bbp_write(rt2x00dev, 1, 0x00); rt2400pci_bbp_write(rt2x00dev, 3, 0x27); rt2400pci_bbp_write(rt2x00dev, 4, 0x08); rt2400pci_bbp_write(rt2x00dev, 10, 0x0f); rt2400pci_bbp_write(rt2x00dev, 15, 0x72); rt2400pci_bbp_write(rt2x00dev, 16, 0x74); rt2400pci_bbp_write(rt2x00dev, 17, 0x20); rt2400pci_bbp_write(rt2x00dev, 18, 0x72); rt2400pci_bbp_write(rt2x00dev, 19, 0x0b); rt2400pci_bbp_write(rt2x00dev, 20, 0x00); rt2400pci_bbp_write(rt2x00dev, 28, 0x11); rt2400pci_bbp_write(rt2x00dev, 29, 0x04); rt2400pci_bbp_write(rt2x00dev, 30, 0x21); rt2400pci_bbp_write(rt2x00dev, 31, 0x00); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); rt2400pci_bbp_write(rt2x00dev, reg_id, value); } } return 0; } /* * Device state switch handlers. */ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int mask = (state == STATE_RADIO_IRQ_OFF); u32 reg; unsigned long flags; /* * When interrupts are being enabled, the interrupt registers * should clear the register to assure a clean state. */ if (state == STATE_RADIO_IRQ_ON) { rt2x00pci_register_read(rt2x00dev, CSR7, ®); rt2x00pci_register_write(rt2x00dev, CSR7, reg); } /* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); rt2x00pci_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); rt2x00_set_field32(®, CSR8_RXDONE, mask); rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); if (state == STATE_RADIO_IRQ_OFF) { /* * Ensure that all tasklets are finished before * disabling the interrupts. */ tasklet_kill(&rt2x00dev->txstatus_tasklet); tasklet_kill(&rt2x00dev->rxdone_tasklet); tasklet_kill(&rt2x00dev->tbtt_tasklet); } } static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) { /* * Initialize all registers. */ if (unlikely(rt2400pci_init_queues(rt2x00dev) || rt2400pci_init_registers(rt2x00dev) || rt2400pci_init_bbp(rt2x00dev))) return -EIO; return 0; } static void rt2400pci_disable_radio(struct rt2x00_dev *rt2x00dev) { /* * Disable power */ rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0); } static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u32 reg, reg2; unsigned int i; char put_to_sleep; char bbp_state; char rf_state; put_to_sleep = (state != STATE_AWAKE); rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®); rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); rt2x00pci_register_write(rt2x00dev, PWRCSR1, reg); /* * Device is not guaranteed to be in the requested state yet. * We must wait until the register indicates that the * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®2); bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); if (bbp_state == state && rf_state == state) return 0; rt2x00pci_register_write(rt2x00dev, PWRCSR1, reg); msleep(10); } return -EBUSY; } static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int retval = 0; switch (state) { case STATE_RADIO_ON: retval = rt2400pci_enable_radio(rt2x00dev); break; case STATE_RADIO_OFF: rt2400pci_disable_radio(rt2x00dev); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: rt2400pci_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: case STATE_STANDBY: case STATE_AWAKE: retval = rt2400pci_set_state(rt2x00dev, state); break; default: retval = -ENOTSUPP; break; } if (unlikely(retval)) ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n", state, retval); return retval; } /* * TX descriptor initialization */ static void rt2400pci_write_tx_desc(struct queue_entry *entry, struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct queue_entry_priv_pci *entry_priv = entry->priv_data; __le32 *txd = entry_priv->desc; u32 word; /* * Start writing the descriptor words. */ rt2x00_desc_read(txd, 1, &word); rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); rt2x00_desc_write(txd, 1, word); rt2x00_desc_read(txd, 2, &word); rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, txdesc->length); rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, txdesc->length); rt2x00_desc_write(txd, 2, word); rt2x00_desc_read(txd, 3, &word); rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_REGNUM, 5); rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_BUSY, 1); rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_REGNUM, 6); rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_BUSY, 1); rt2x00_desc_write(txd, 3, word); rt2x00_desc_read(txd, 4, &word); rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_LOW, txdesc->u.plcp.length_low); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_REGNUM, 8); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_BUSY, 1); rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_HIGH, txdesc->u.plcp.length_high); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_REGNUM, 7); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_BUSY, 1); rt2x00_desc_write(txd, 4, word); /* * Writing TXD word 0 must the last to prevent a race condition with * the device, whereby the device may take hold of the TXD before we * finished updating it. */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_RTS, test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); rt2x00_desc_write(txd, 0, word); /* * Register descriptor details in skb frame descriptor. */ skbdesc->desc = txd; skbdesc->desc_len = TXD_DESC_SIZE; } /* * TX data initialization */ static void rt2400pci_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; u32 reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ rt2x00pci_register_read(rt2x00dev, CSR14, ®); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); rt2x00queue_map_txskb(entry); /* * Write the TX descriptor for the beacon. */ rt2400pci_write_tx_desc(entry, txdesc); /* * Dump beacon to userspace through debugfs. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); /* * Enable beaconing again. */ rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); rt2x00pci_register_write(rt2x00dev, CSR14, reg); } /* * RX control handlers */ static void rt2400pci_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word0; u32 word2; u32 word3; u32 word4; u64 tsf; u32 rx_low; u32 rx_high; rt2x00_desc_read(entry_priv->desc, 0, &word0); rt2x00_desc_read(entry_priv->desc, 2, &word2); rt2x00_desc_read(entry_priv->desc, 3, &word3); rt2x00_desc_read(entry_priv->desc, 4, &word4); if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; /* * We only get the lower 32bits from the timestamp, * to get the full 64bits we must complement it with * the timestamp from get_tsf(). * Note that when a wraparound of the lower 32bits * has occurred between the frame arrival and the get_tsf() * call, we must decrease the higher 32bits with 1 to get * to correct value. */ tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw, NULL); rx_low = rt2x00_get_field32(word4, RXD_W4_RX_END_TIME); rx_high = upper_32_bits(tsf); if ((u32)tsf <= rx_low) rx_high--; /* * Obtain the status about this packet. * The signal is the PLCP value, and needs to be stripped * of the preamble bit (0x08). */ rxdesc->timestamp = ((u64)rx_high << 32) | rx_low; rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL) & ~0x08; rxdesc->rssi = rt2x00_get_field32(word2, RXD_W3_RSSI) - entry->queue->rt2x00dev->rssi_offset; rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; } /* * Interrupt functions. */ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, const enum data_queue_qid queue_idx) { struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); struct queue_entry_priv_pci *entry_priv; struct queue_entry *entry; struct txdone_entry_desc txdesc; u32 word; while (!rt2x00queue_empty(queue)) { entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); entry_priv = entry->priv_data; rt2x00_desc_read(entry_priv->desc, 0, &word); if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || !rt2x00_get_field32(word, TXD_W0_VALID)) break; /* * Obtain the status about this packet. */ txdesc.flags = 0; switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { case 0: /* Success */ case 1: /* Success with retry */ __set_bit(TXDONE_SUCCESS, &txdesc.flags); break; case 2: /* Failure, excessive retries */ __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); /* Don't break, this is a failed frame! */ default: /* Failure */ __set_bit(TXDONE_FAILURE, &txdesc.flags); } txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); rt2x00lib_txdone(entry, &txdesc); } } static inline void rt2400pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, struct rt2x00_field32 irq_field) { u32 reg; /* * Enable a single interrupt. The interrupt mask register * access needs locking. */ spin_lock_irq(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, irq_field, 0); rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock_irq(&rt2x00dev->irqmask_lock); } static void rt2400pci_txstatus_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; u32 reg; /* * Handle all tx queues. */ rt2400pci_txdone(rt2x00dev, QID_ATIM); rt2400pci_txdone(rt2x00dev, QID_AC_VO); rt2400pci_txdone(rt2x00dev, QID_AC_VI); /* * Enable all TXDONE interrupts again. */ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { spin_lock_irq(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock_irq(&rt2x00dev->irqmask_lock); } } static void rt2400pci_tbtt_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; rt2x00lib_beacondone(rt2x00dev); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); } static void rt2400pci_rxdone_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; if (rt2x00pci_rxdone(rt2x00dev)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2400pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); } static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; u32 reg, mask; /* * Get the interrupt sources & saved to local variable. * Write register value back to clear pending interrupts. */ rt2x00pci_register_read(rt2x00dev, CSR7, ®); rt2x00pci_register_write(rt2x00dev, CSR7, reg); if (!reg) return IRQ_NONE; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return IRQ_HANDLED; mask = reg; /* * Schedule tasklets for interrupt handling. */ if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); if (rt2x00_get_field32(reg, CSR7_RXDONE)) tasklet_schedule(&rt2x00dev->rxdone_tasklet); if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { tasklet_schedule(&rt2x00dev->txstatus_tasklet); /* * Mask out all txdone interrupts. */ rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); } /* * Disable all interrupts for which a tasklet was scheduled right now, * the tasklet will reenable the appropriate interrupts. */ spin_lock(&rt2x00dev->irqmask_lock); rt2x00pci_register_read(rt2x00dev, CSR8, ®); reg |= mask; rt2x00pci_register_write(rt2x00dev, CSR8, reg); spin_unlock(&rt2x00dev->irqmask_lock); return IRQ_HANDLED; } /* * Device probe functions. */ static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) { struct eeprom_93cx6 eeprom; u32 reg; u16 word; u8 *mac; rt2x00pci_register_read(rt2x00dev, CSR21, ®); eeprom.data = rt2x00dev; eeprom.register_read = rt2400pci_eepromregister_read; eeprom.register_write = rt2400pci_eepromregister_write; eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; eeprom.reg_data_in = 0; eeprom.reg_data_out = 0; eeprom.reg_data_clock = 0; eeprom.reg_chip_select = 0; eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, EEPROM_SIZE / sizeof(u16)); /* * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { eth_random_addr(mac); EEPROM(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { ERROR(rt2x00dev, "Invalid EEPROM data detected.\n"); return -EINVAL; } return 0; } static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) { u32 reg; u16 value; u16 eeprom; /* * Read EEPROM word for configuration. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); /* * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); rt2x00pci_register_read(rt2x00dev, CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2460, value, rt2x00_get_field32(reg, CSR0_REVISION)); if (!rt2x00_rf(rt2x00dev, RF2420) && !rt2x00_rf(rt2x00dev, RF2421)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } /* * Identify default antenna configuration. */ rt2x00dev->default_ant.tx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); rt2x00dev->default_ant.rx = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); /* * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead. * I am not 100% sure about this, but the legacy drivers do not * indicate antenna swapping in software is required when * diversity is enabled. */ if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; /* * Store led mode, for correct led behaviour. */ #ifdef CONFIG_RT2X00_LIB_LEDS value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); if (value == LED_MODE_TXRX_ACTIVITY || value == LED_MODE_DEFAULT || value == LED_MODE_ASUS) rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_ACTIVITY); #endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Detect if this device has an hardware controlled radio. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Check if the BBP tuning should be enabled. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING)) __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); return 0; } /* * RF value list for RF2420 & RF2421 * Supports: 2.4 GHz */ static const struct rf_channel rf_vals_b[] = { { 1, 0x00022058, 0x000c1fda, 0x00000101, 0 }, { 2, 0x00022058, 0x000c1fee, 0x00000101, 0 }, { 3, 0x00022058, 0x000c2002, 0x00000101, 0 }, { 4, 0x00022058, 0x000c2016, 0x00000101, 0 }, { 5, 0x00022058, 0x000c202a, 0x00000101, 0 }, { 6, 0x00022058, 0x000c203e, 0x00000101, 0 }, { 7, 0x00022058, 0x000c2052, 0x00000101, 0 }, { 8, 0x00022058, 0x000c2066, 0x00000101, 0 }, { 9, 0x00022058, 0x000c207a, 0x00000101, 0 }, { 10, 0x00022058, 0x000c208e, 0x00000101, 0 }, { 11, 0x00022058, 0x000c20a2, 0x00000101, 0 }, { 12, 0x00022058, 0x000c20b6, 0x00000101, 0 }, { 13, 0x00022058, 0x000c20ca, 0x00000101, 0 }, { 14, 0x00022058, 0x000c20fa, 0x00000101, 0 }, }; static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *tx_power; unsigned int i; /* * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK; spec->num_channels = ARRAY_SIZE(rf_vals_b); spec->channels = rf_vals_b; /* * Create channel information array */ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; spec->channels_info = info; tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); for (i = 0; i < 14; i++) { info[i].max_power = TXPOWER_FROM_DEV(MAX_TXPOWER); info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } return 0; } static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; u32 reg; /* * Allocate eeprom data. */ retval = rt2400pci_validate_eeprom(rt2x00dev); if (retval) return retval; retval = rt2400pci_init_eeprom(rt2x00dev); if (retval) return retval; /* * Enable rfkill polling by setting GPIO direction of the * rfkill switch GPIO pin correctly. */ rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); rt2x00_set_field32(®, GPIOCSR_DIR0, 1); rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg); /* * Initialize hw specifications. */ retval = rt2400pci_probe_hw_mode(rt2x00dev); if (retval) return retval; /* * This device requires the atim queue and DMA-mapped skbs. */ __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); /* * Set the rssi offset. */ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; return 0; } /* * IEEE80211 stack callback functions. */ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; /* * We don't support variating cw_min and cw_max variables * per queue. So by default we only configure the TX queue, * and ignore all other configurations. */ if (queue != 0) return -EINVAL; if (rt2x00mac_conf_tx(hw, vif, queue, params)) return -EINVAL; /* * Write configuration to register. */ rt2400pci_config_cw(rt2x00dev, rt2x00dev->tx->cw_min, rt2x00dev->tx->cw_max); return 0; } static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; rt2x00pci_register_read(rt2x00dev, CSR17, ®); tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; rt2x00pci_register_read(rt2x00dev, CSR16, ®); tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); return tsf; } static int rt2400pci_tx_last_beacon(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; u32 reg; rt2x00pci_register_read(rt2x00dev, CSR15, ®); return rt2x00_get_field32(reg, CSR15_BEACON_SENT); } static const struct ieee80211_ops rt2400pci_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, .stop = rt2x00mac_stop, .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, .sw_scan_start = rt2x00mac_sw_scan_start, .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2400pci_conf_tx, .get_tsf = rt2400pci_get_tsf, .tx_last_beacon = rt2400pci_tx_last_beacon, .rfkill_poll = rt2x00mac_rfkill_poll, .flush = rt2x00mac_flush, .set_antenna = rt2x00mac_set_antenna, .get_antenna = rt2x00mac_get_antenna, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { .irq_handler = rt2400pci_interrupt, .txstatus_tasklet = rt2400pci_txstatus_tasklet, .tbtt_tasklet = rt2400pci_tbtt_tasklet, .rxdone_tasklet = rt2400pci_rxdone_tasklet, .probe_hw = rt2400pci_probe_hw, .initialize = rt2x00pci_initialize, .uninitialize = rt2x00pci_uninitialize, .get_entry_state = rt2400pci_get_entry_state, .clear_entry = rt2400pci_clear_entry, .set_device_state = rt2400pci_set_device_state, .rfkill_poll = rt2400pci_rfkill_poll, .link_stats = rt2400pci_link_stats, .reset_tuner = rt2400pci_reset_tuner, .link_tuner = rt2400pci_link_tuner, .start_queue = rt2400pci_start_queue, .kick_queue = rt2400pci_kick_queue, .stop_queue = rt2400pci_stop_queue, .flush_queue = rt2x00pci_flush_queue, .write_tx_desc = rt2400pci_write_tx_desc, .write_beacon = rt2400pci_write_beacon, .fill_rxdone = rt2400pci_fill_rxdone, .config_filter = rt2400pci_config_filter, .config_intf = rt2400pci_config_intf, .config_erp = rt2400pci_config_erp, .config_ant = rt2400pci_config_ant, .config = rt2400pci_config, }; static const struct data_queue_desc rt2400pci_queue_rx = { .entry_num = 24, .data_size = DATA_FRAME_SIZE, .desc_size = RXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2400pci_queue_tx = { .entry_num = 24, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2400pci_queue_bcn = { .entry_num = 1, .data_size = MGMT_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct data_queue_desc rt2400pci_queue_atim = { .entry_num = 8, .data_size = DATA_FRAME_SIZE, .desc_size = TXD_DESC_SIZE, .priv_size = sizeof(struct queue_entry_priv_pci), }; static const struct rt2x00_ops rt2400pci_ops = { .name = KBUILD_MODNAME, .max_sta_intf = 1, .max_ap_intf = 1, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = 0, .rx = &rt2400pci_queue_rx, .tx = &rt2400pci_queue_tx, .bcn = &rt2400pci_queue_bcn, .atim = &rt2400pci_queue_atim, .lib = &rt2400pci_rt2x00_ops, .hw = &rt2400pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS .debugfs = &rt2400pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * RT2400pci module information. */ static DEFINE_PCI_DEVICE_TABLE(rt2400pci_device_table) = { { PCI_DEVICE(0x1814, 0x0101) }, { 0, } }; MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2400 PCI & PCMCIA Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2460 PCI & PCMCIA chipset based cards"); MODULE_DEVICE_TABLE(pci, rt2400pci_device_table); MODULE_LICENSE("GPL"); static int rt2400pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { return rt2x00pci_probe(pci_dev, &rt2400pci_ops); } static struct pci_driver rt2400pci_driver = { .name = KBUILD_MODNAME, .id_table = rt2400pci_device_table, .probe = rt2400pci_probe, .remove = __devexit_p(rt2x00pci_remove), .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; module_pci_driver(rt2400pci_driver); compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/Makefile0000644000175000017500000000155512026211315023166 0ustar mcgrofmcgrofrt2x00lib-y += rt2x00dev.o rt2x00lib-y += rt2x00mac.o rt2x00lib-y += rt2x00config.o rt2x00lib-y += rt2x00queue.o rt2x00lib-y += rt2x00link.o rt2x00lib-$(CONFIG_RT2X00_LIB_DEBUGFS) += rt2x00debug.o rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS) += rt2x00leds.o obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o obj-$(CONFIG_RT2800_LIB) += rt2800lib.o obj-$(CONFIG_RT2400PCI) += rt2400pci.o obj-$(CONFIG_RT2500PCI) += rt2500pci.o obj-$(CONFIG_RT61PCI) += rt61pci.o obj-$(CONFIG_RT2800PCI) += rt2800pci.o obj-$(CONFIG_RT2500USB) += rt2500usb.o obj-$(CONFIG_RT73USB) += rt73usb.o obj-$(CONFIG_RT2800USB) += rt2800usb.o compat-drivers-2012-09-18/drivers/net/wireless/rt2x00/Kconfig0000644000175000017500000001426612026211315023034 0ustar mcgrofmcgrofmenuconfig RT2X00 tristate "Ralink driver support" depends on MAC80211 ---help--- This will enable the support for the Ralink drivers, developed in the rt2x00 project . These drivers make use of the mac80211 stack. When building one of the individual drivers, the rt2x00 library will also be created. That library (when the driver is built as a module) will be called rt2x00lib. Additionally PCI and USB libraries will also be build depending on the types of drivers being selected, these libraries will be called rt2x00pci and rt2x00usb. if RT2X00 config RT2400PCI tristate "Ralink rt2400 (PCI/PCMCIA) support" depends on PCI select RT2X00_LIB_PCI select EEPROM_93CX6 ---help--- This adds support for rt2400 wireless chipset family. Supported chips: RT2460. When compiled as a module, this driver will be called rt2400pci. config RT2500PCI tristate "Ralink rt2500 (PCI/PCMCIA) support" depends on PCI select RT2X00_LIB_PCI select EEPROM_93CX6 ---help--- This adds support for rt2500 wireless chipset family. Supported chips: RT2560. When compiled as a module, this driver will be called rt2500pci. config RT61PCI tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support" depends on PCI select RT2X00_LIB_PCI select RT2X00_LIB_FIRMWARE select RT2X00_LIB_CRYPTO select CRC_ITU_T select EEPROM_93CX6 ---help--- This adds support for rt2501 wireless chipset family. Supported chips: RT2561, RT2561S & RT2661. When compiled as a module, this driver will be called rt61pci. config RT2800PCI tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support" depends on PCI || RALINK_RT288X || RALINK_RT305X select RT2800_LIB select RT2X00_LIB_PCI if PCI select RT2X00_LIB_SOC if RALINK_RT288X || RALINK_RT305X select RT2X00_LIB_FIRMWARE select RT2X00_LIB_CRYPTO select CRC_CCITT select EEPROM_93CX6 ---help--- This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. Supported chips: RT2760, RT2790, RT2860, RT2880, RT2890, RT3052, RT3090, RT3091 & RT3092 When compiled as a module, this driver will be called "rt2800pci.ko". if RT2800PCI config RT2800PCI_RT33XX bool "rt2800pci - Include support for rt33xx devices" default y ---help--- This adds support for rt33xx wireless chipset family to the rt2800pci driver. Supported chips: RT3390 config RT2800PCI_RT35XX bool "rt2800pci - Include support for rt35xx devices (EXPERIMENTAL)" depends on EXPERIMENTAL default y ---help--- This adds support for rt35xx wireless chipset family to the rt2800pci driver. Supported chips: RT3060, RT3062, RT3562, RT3592 config RT2800PCI_RT53XX bool "rt2800pci - Include support for rt53xx devices (EXPERIMENTAL)" depends on EXPERIMENTAL default y ---help--- This adds support for rt53xx wireless chipset family to the rt2800pci driver. Supported chips: RT5390 config RT2800PCI_RT3290 bool "rt2800pci - Include support for rt3290 devices (EXPERIMENTAL)" depends on EXPERIMENTAL default y ---help--- This adds support for rt3290 wireless chipset family to the rt2800pci driver. Supported chips: RT3290 endif config RT2500USB tristate "Ralink rt2500 (USB) support" depends on USB select RT2X00_LIB_USB select RT2X00_LIB_CRYPTO ---help--- This adds support for rt2500 wireless chipset family. Supported chips: RT2571 & RT2572. When compiled as a module, this driver will be called rt2500usb. config RT73USB tristate "Ralink rt2501/rt73 (USB) support" depends on USB select RT2X00_LIB_USB select RT2X00_LIB_FIRMWARE select RT2X00_LIB_CRYPTO select CRC_ITU_T ---help--- This adds support for rt2501 wireless chipset family. Supported chips: RT2571W, RT2573 & RT2671. When compiled as a module, this driver will be called rt73usb. config RT2800USB tristate "Ralink rt27xx/rt28xx/rt30xx (USB) support" depends on USB select RT2800_LIB select RT2X00_LIB_USB select RT2X00_LIB_FIRMWARE select RT2X00_LIB_CRYPTO select CRC_CCITT ---help--- This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. Supported chips: RT2770, RT2870 & RT3070, RT3071 & RT3072 When compiled as a module, this driver will be called "rt2800usb.ko". if RT2800USB config RT2800USB_RT33XX bool "rt2800usb - Include support for rt33xx devices" default y ---help--- This adds support for rt33xx wireless chipset family to the rt2800usb driver. Supported chips: RT3370 config RT2800USB_RT35XX bool "rt2800usb - Include support for rt35xx devices (EXPERIMENTAL)" depends on EXPERIMENTAL default y ---help--- This adds support for rt35xx wireless chipset family to the rt2800usb driver. Supported chips: RT3572 config RT2800USB_RT53XX bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)" depends on EXPERIMENTAL ---help--- This adds support for rt53xx wireless chipset family to the rt2800usb driver. Supported chips: RT5370 config RT2800USB_UNKNOWN bool "rt2800usb - Include support for unknown (USB) devices" default n ---help--- This adds support for rt2800usb devices that are known to have a rt28xx family compatible chipset, but for which the exact chipset is unknown. Support status for these devices is unknown, and enabling these devices may or may not work. endif config RT2800_LIB tristate config RT2X00_LIB_PCI tristate select RT2X00_LIB config RT2X00_LIB_SOC tristate select RT2X00_LIB config RT2X00_LIB_USB tristate select RT2X00_LIB config RT2X00_LIB tristate config RT2X00_LIB_FIRMWARE boolean select FW_LOADER config RT2X00_LIB_CRYPTO boolean config RT2X00_LIB_LEDS boolean default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n) config RT2X00_LIB_DEBUGFS bool "Ralink debugfs support" depends on RT2X00_LIB && MAC80211_DEBUGFS ---help--- Enable creation of debugfs files for the rt2x00 drivers. These debugfs files support both reading and writing of the most important register types of the rt2x00 hardware. config RT2X00_DEBUG bool "Ralink debug output" depends on RT2X00_LIB ---help--- Enable debugging output for all rt2x00 modules endif compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/0000755000175000017500000000000012026211315022133 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-debug.c0000644000175000017500000001132412026211315024157 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #define DEBUG #include #include #include #include "iwl-debug.h" #include "iwl-devtrace.h" #define __iwl_fn(fn) \ void __iwl_ ##fn(struct device *dev, const char *fmt, ...) \ { \ struct va_format vaf = { \ .fmt = fmt, \ }; \ va_list args1, args2; \ \ va_start(args1, fmt); \ va_copy(args2, args1); \ vaf.va = &args2; \ dev_ ##fn(dev, "%pV", &vaf); \ va_end(args2); \ vaf.va = &args1; \ trace_iwlwifi_ ##fn(&vaf); \ va_end(args1); \ } __iwl_fn(warn) EXPORT_SYMBOL_GPL(__iwl_warn); __iwl_fn(info) EXPORT_SYMBOL_GPL(__iwl_info); __iwl_fn(crit) EXPORT_SYMBOL_GPL(__iwl_crit); void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); if (!trace_only) { va_list args2; va_copy(args2, args); vaf.va = &args2; if (rfkill_prefix) dev_err(dev, "(RFKILL) %pV", &vaf); else dev_err(dev, "%pV", &vaf); va_end(args2); } vaf.va = &args; trace_iwlwifi_err(&vaf); va_end(args); } EXPORT_SYMBOL_GPL(__iwl_err); #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) void __iwl_dbg(struct device *dev, u32 level, bool limit, const char *function, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(level) && (!limit || net_ratelimit())) { va_list args2; va_copy(args2, args); vaf.va = &args2; dev_printk(KERN_DEBUG, dev, "%c %s %pV", in_interrupt() ? 'I' : 'U', function, &vaf); va_end(args2); } #endif vaf.va = &args; trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); va_end(args); } EXPORT_SYMBOL_GPL(__iwl_dbg); #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-trans.h0000644000175000017500000005130612026211315024231 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __iwl_trans_h__ #define __iwl_trans_h__ #include #include /* for page_address */ #include "iwl-debug.h" #include "iwl-config.h" #include "iwl-fw.h" /** * DOC: Transport layer - what is it ? * * The tranport layer is the layer that deals with the HW directly. It provides * an abstraction of the underlying HW to the upper layer. The transport layer * doesn't provide any policy, algorithm or anything of this kind, but only * mechanisms to make the HW do something.It is not completely stateless but * close to it. * We will have an implementation for each different supported bus. */ /** * DOC: Life cycle of the transport layer * * The transport layer has a very precise life cycle. * * 1) A helper function is called during the module initialization and * registers the bus driver's ops with the transport's alloc function. * 2) Bus's probe calls to the transport layer's allocation functions. * Of course this function is bus specific. * 3) This allocation functions will spawn the upper layer which will * register mac80211. * * 4) At some point (i.e. mac80211's start call), the op_mode will call * the following sequence: * start_hw * start_fw * * 5) Then when finished (or reset): * stop_fw (a.k.a. stop device for the moment) * stop_hw * * 6) Eventually, the free function will be called. */ /** * DOC: Host command section * * A host command is a commaned issued by the upper layer to the fw. There are * several versions of fw that have several APIs. The transport layer is * completely agnostic to these differences. * The transport does provide helper functionnality (i.e. SYNC / ASYNC mode), */ #define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) #define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) #define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) #define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) #define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) #define SEQ_TO_INDEX(s) ((s) & 0xff) #define INDEX_TO_SEQ(i) ((i) & 0xff) #define SEQ_RX_FRAME cpu_to_le16(0x8000) /** * struct iwl_cmd_header * * This header format appears in the beginning of each command sent from the * driver, and each response/notification received from uCode. */ struct iwl_cmd_header { u8 cmd; /* Command ID: REPLY_RXON, etc. */ u8 flags; /* 0:5 reserved, 6 abort, 7 internal */ /* * The driver sets up the sequence number to values of its choosing. * uCode does not use this value, but passes it back to the driver * when sending the response to each driver-originated command, so * the driver can match the response to the command. Since the values * don't get used by uCode, the driver may set up an arbitrary format. * * There is one exception: uCode sets bit 15 when it originates * the response/notification, i.e. when the response/notification * is not a direct response to a command sent by the driver. For * example, uCode issues REPLY_RX when it sends a received frame * to the driver; it is not a direct response to any driver command. * * The Linux driver uses the following format: * * 0:7 tfd index - position within TX queue * 8:12 TX queue id * 13:14 reserved * 15 unsolicited RX or uCode-originated notification */ __le16 sequence; } __packed; /* iwl_cmd_header flags value */ #define IWL_CMD_FAILED_MSK 0x40 #define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ #define FH_RSCSR_FRAME_INVALID 0x55550000 #define FH_RSCSR_FRAME_ALIGN 0x40 struct iwl_rx_packet { /* * The first 4 bytes of the RX frame header contain both the RX frame * size and some flags. * Bit fields: * 31: flag flush RB request * 30: flag ignore TC (terminal counter) request * 29: flag fast IRQ request * 28-14: Reserved * 13-00: RX frame size */ __le32 len_n_flags; struct iwl_cmd_header hdr; u8 data[]; } __packed; /** * enum CMD_MODE - how to send the host commands ? * * @CMD_SYNC: The caller will be stalled until the fw responds to the command * @CMD_ASYNC: Return right away and don't want for the response * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the * response. The caller needs to call iwl_free_resp when done. * @CMD_WANT_HCMD: The caller needs to get the HCMD that was sent in the * response handler. Chunks flagged by %IWL_HCMD_DFL_NOCOPY won't be * copied. The pointer passed to the response handler is in the transport * ownership and don't need to be freed by the op_mode. This also means * that the pointer is invalidated after the op_mode's handler returns. * @CMD_ON_DEMAND: This command is sent by the test mode pipe. */ enum CMD_MODE { CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), CMD_WANT_HCMD = BIT(2), CMD_ON_DEMAND = BIT(3), }; #define DEF_CMD_PAYLOAD_SIZE 320 /** * struct iwl_device_cmd * * For allocation of the command and tx queues, this establishes the overall * size of the largest command we send to uCode, except for commands that * aren't fully copied and use other TFD space. */ struct iwl_device_cmd { struct iwl_cmd_header hdr; /* uCode API */ u8 payload[DEF_CMD_PAYLOAD_SIZE]; } __packed; #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) #define IWL_MAX_CMD_TFDS 2 /** * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command * * IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's * ring. The transport layer doesn't map the command's buffer to DMA, but * rather copies it to an previously allocated DMA buffer. This flag tells * the transport layer not to copy the command, but to map the existing * buffer. This can save memcpy and is worth with very big comamnds. */ enum iwl_hcmd_dataflag { IWL_HCMD_DFL_NOCOPY = BIT(0), }; /** * struct iwl_host_cmd - Host command to the uCode * * @data: array of chunks that composes the data of the host command * @resp_pkt: response packet, if %CMD_WANT_SKB was set * @_rx_page_order: (internally used to free response packet) * @_rx_page_addr: (internally used to free response packet) * @handler_status: return value of the handler of the command * (put in setup_rx_handlers) - valid for SYNC mode only * @flags: can be CMD_* * @len: array of the lenths of the chunks in data * @dataflags: IWL_HCMD_DFL_* * @id: id of the host command */ struct iwl_host_cmd { const void *data[IWL_MAX_CMD_TFDS]; struct iwl_rx_packet *resp_pkt; unsigned long _rx_page_addr; u32 _rx_page_order; int handler_status; u32 flags; u16 len[IWL_MAX_CMD_TFDS]; u8 dataflags[IWL_MAX_CMD_TFDS]; u8 id; }; static inline void iwl_free_resp(struct iwl_host_cmd *cmd) { free_pages(cmd->_rx_page_addr, cmd->_rx_page_order); } struct iwl_rx_cmd_buffer { struct page *_page; int _offset; bool _page_stolen; unsigned int truesize; }; static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r) { return (void *)((unsigned long)page_address(r->_page) + r->_offset); } static inline int rxb_offset(struct iwl_rx_cmd_buffer *r) { return r->_offset; } static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) { r->_page_stolen = true; get_page(r->_page); return r->_page; } #define MAX_NO_RECLAIM_CMDS 6 #define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) /* * Maximum number of HW queues the transport layer * currently supports */ #define IWL_MAX_HW_QUEUES 32 #define IWL_INVALID_STATION 255 #define IWL_MAX_TID_COUNT 8 #define IWL_FRAME_LIMIT 64 /** * struct iwl_trans_config - transport configuration * * @op_mode: pointer to the upper layer. * @cmd_queue: the index of the command queue. * Must be set before start_fw. * @cmd_fifo: the fifo for host commands * @no_reclaim_cmds: Some devices erroneously don't set the * SEQ_RX_FRAME bit on some notifications, this is the * list of such notifications to filter. Max length is * %MAX_NO_RECLAIM_CMDS. * @n_no_reclaim_cmds: # of commands in list * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs, * if unset 4k will be the RX buffer size * @queue_watchdog_timeout: time (in ms) after which queues * are considered stuck and will trigger device restart * @command_names: array of command names, must be 256 entries * (one for each command); for debugging only */ struct iwl_trans_config { struct iwl_op_mode *op_mode; u8 cmd_queue; u8 cmd_fifo; const u8 *no_reclaim_cmds; int n_no_reclaim_cmds; bool rx_buf_size_8k; unsigned int queue_watchdog_timeout; const char **command_names; }; struct iwl_trans; /** * struct iwl_trans_ops - transport specific operations * * All the handlers MUST be implemented * * @start_hw: starts the HW- from that point on, the HW can send interrupts * May sleep * @stop_hw: stops the HW- from that point on, the HW will be in low power but * will still issue interrupt if the HW RF kill is triggered unless * op_mode_leaving is true. * May sleep * @start_fw: allocates and inits all the resources for the transport * layer. Also kick a fw image. * May sleep * @fw_alive: called when the fw sends alive notification * May sleep * @stop_device:stops the whole device (embedded CPU put to reset) * May sleep * @wowlan_suspend: put the device into the correct mode for WoWLAN during * suspend. This is optional, if not implemented WoWLAN will not be * supported. This callback may sleep. * @send_cmd:send a host command * May sleep only if CMD_SYNC is set * @tx: send an skb * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. * Must be atomic * @txq_enable: setup a queue. To setup an AC queue, use the * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before * this one. The op_mode must not configure the HCMD queue. May sleep. * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic * @wait_tx_queue_empty: wait until all tx queues are empty * May sleep * @dbgfs_register: add the dbgfs files under this directory. Files will be * automatically deleted. * @suspend: stop the device unless WoWLAN is configured * @resume: resume activity of the device * @write8: write a u8 to a register at offset ofs from the BAR * @write32: write a u32 to a register at offset ofs from the BAR * @read32: read a u32 register at offset ofs from the BAR * @configure: configure parameters required by the transport layer from * the op_mode. May be called several times before start_fw, can't be * called after that. * @set_pmi: set the power pmi state */ struct iwl_trans_ops { int (*start_hw)(struct iwl_trans *iwl_trans); void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving); int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw); void (*fw_alive)(struct iwl_trans *trans); void (*stop_device)(struct iwl_trans *trans); void (*wowlan_suspend)(struct iwl_trans *trans); int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); int (*tx)(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int queue); void (*reclaim)(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs); void (*txq_enable)(struct iwl_trans *trans, int queue, int fifo, int sta_id, int tid, int frame_limit, u16 ssn); void (*txq_disable)(struct iwl_trans *trans, int queue); int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*wait_tx_queue_empty)(struct iwl_trans *trans); #ifdef CONFIG_PM_SLEEP int (*suspend)(struct iwl_trans *trans); int (*resume)(struct iwl_trans *trans); #endif void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val); u32 (*read32)(struct iwl_trans *trans, u32 ofs); void (*configure)(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg); void (*set_pmi)(struct iwl_trans *trans, bool state); }; /** * enum iwl_trans_state - state of the transport layer * * @IWL_TRANS_NO_FW: no fw has sent an alive response * @IWL_TRANS_FW_ALIVE: a fw has sent an alive response */ enum iwl_trans_state { IWL_TRANS_NO_FW = 0, IWL_TRANS_FW_ALIVE = 1, }; /** * struct iwl_trans - transport common data * * @ops - pointer to iwl_trans_ops * @op_mode - pointer to the op_mode * @cfg - pointer to the configuration * @reg_lock - protect hw register access * @dev - pointer to struct device * that represents the device * @hw_id: a u32 with the ID of the device / subdevice. * Set during transport allocation. * @hw_id_str: a string with info about HW ID. Set during transport allocation. * @pm_support: set to true in start_hw if link pm is supported * @wait_command_queue: the wait_queue for SYNC host commands * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. * The user should use iwl_trans_{alloc,free}_tx_cmd. * @dev_cmd_headroom: room needed for the transport's private use before the * device_cmd for Tx - for internal use only * The user should use iwl_trans_{alloc,free}_tx_cmd. */ struct iwl_trans { const struct iwl_trans_ops *ops; struct iwl_op_mode *op_mode; const struct iwl_cfg *cfg; enum iwl_trans_state state; spinlock_t reg_lock; struct device *dev; u32 hw_rev; u32 hw_id; char hw_id_str[52]; bool pm_support; wait_queue_head_t wait_command_queue; /* The following fields are internal only */ struct kmem_cache *dev_cmd_pool; size_t dev_cmd_headroom; char dev_cmd_pool_name[50]; struct dentry *dbgfs_dir; /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __aligned(sizeof(void *)); }; static inline void iwl_trans_configure(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg) { /* * only set the op_mode for the moment. Later on, this function will do * more */ trans->op_mode = trans_cfg->op_mode; trans->ops->configure(trans, trans_cfg); } static inline int iwl_trans_start_hw(struct iwl_trans *trans) { might_sleep(); return trans->ops->start_hw(trans); } static inline void iwl_trans_stop_hw(struct iwl_trans *trans, bool op_mode_leaving) { might_sleep(); trans->ops->stop_hw(trans, op_mode_leaving); trans->state = IWL_TRANS_NO_FW; } static inline void iwl_trans_fw_alive(struct iwl_trans *trans) { might_sleep(); trans->state = IWL_TRANS_FW_ALIVE; trans->ops->fw_alive(trans); } static inline int iwl_trans_start_fw(struct iwl_trans *trans, const struct fw_img *fw) { might_sleep(); return trans->ops->start_fw(trans, fw); } static inline void iwl_trans_stop_device(struct iwl_trans *trans) { might_sleep(); trans->ops->stop_device(trans); trans->state = IWL_TRANS_NO_FW; } static inline void iwl_trans_wowlan_suspend(struct iwl_trans *trans) { might_sleep(); trans->ops->wowlan_suspend(trans); } static inline int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); return trans->ops->send_cmd(trans, cmd); } static inline struct iwl_device_cmd * iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) { u8 *dev_cmd_ptr = kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC); if (unlikely(dev_cmd_ptr == NULL)) return NULL; return (struct iwl_device_cmd *) (dev_cmd_ptr + trans->dev_cmd_headroom); } static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, struct iwl_device_cmd *dev_cmd) { u8 *dev_cmd_ptr = (u8 *)dev_cmd - trans->dev_cmd_headroom; kmem_cache_free(trans->dev_cmd_pool, dev_cmd_ptr); } static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int queue) { WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); return trans->ops->tx(trans, skb, dev_cmd, queue); } static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs) { WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); trans->ops->reclaim(trans, queue, ssn, skbs); } static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue) { WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); trans->ops->txq_disable(trans, queue); } static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, int fifo, int sta_id, int tid, int frame_limit, u16 ssn) { might_sleep(); WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); trans->ops->txq_enable(trans, queue, fifo, sta_id, tid, frame_limit, ssn); } static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo) { iwl_trans_txq_enable(trans, queue, fifo, IWL_INVALID_STATION, IWL_MAX_TID_COUNT, IWL_FRAME_LIMIT, 0); } static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans) { WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); return trans->ops->wait_tx_queue_empty(trans); } static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, struct dentry *dir) { return trans->ops->dbgfs_register(trans, dir); } #ifdef CONFIG_PM_SLEEP static inline int iwl_trans_suspend(struct iwl_trans *trans) { return trans->ops->suspend(trans); } static inline int iwl_trans_resume(struct iwl_trans *trans) { return trans->ops->resume(trans); } #endif static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val) { trans->ops->write8(trans, ofs, val); } static inline void iwl_trans_write32(struct iwl_trans *trans, u32 ofs, u32 val) { trans->ops->write32(trans, ofs, val); } static inline u32 iwl_trans_read32(struct iwl_trans *trans, u32 ofs) { return trans->ops->read32(trans, ofs); } static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state) { trans->ops->set_pmi(trans, state); } /***************************************************** * driver (transport) register/unregister functions ******************************************************/ int __must_check iwl_pci_register_driver(void); void iwl_pci_unregister_driver(void); #endif /* __iwl_trans_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-testmode.h0000644000175000017500000002727112026211315024732 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __IWL_TESTMODE_H__ #define __IWL_TESTMODE_H__ #include /* * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX). * The command ID is carried with IWL_TM_ATTR_COMMAND. * * @IWL_TM_CMD_APP2DEV_UCODE: * commands from user application to the uCode, * the actual uCode host command ID is carried with * IWL_TM_ATTR_UCODE_CMD_ID * * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: * commands from user applicaiton to access register * * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS * commands fom user space for pure driver level operations * * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE: * @IWL_TM_CMD_APP2DEV_END_TRACE: * @IWL_TM_CMD_APP2DEV_READ_TRACE: * commands fom user space for uCode trace operations * * @IWL_TM_CMD_DEV2APP_SYNC_RSP: * commands from kernel space to carry the synchronous response * to user application * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT: * commands from kernel space to multicast the spontaneous messages * to user application, or reply of host commands * @IWL_TM_CMD_DEV2APP_EEPROM_RSP: * commands from kernel space to carry the eeprom response * to user application * * @IWL_TM_CMD_APP2DEV_OWNERSHIP: * commands from user application to own change the ownership of the uCode * if application has the ownership, the only host command from * testmode will deliver to uCode. Default owner is driver * * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Wake On Wireless LAN uCode image * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device * @IWL_TM_CMD_APP2DEV_GET_FW_INFO: * retrieve information of existing loaded uCode image * * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: * Commands to read/write data from periphery or SRAM memory ranges. * Fore reading, a READ command is sent from the userspace and the data * is returned when the user calls a DUMP command. * For writing, only a WRITE command is used. * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS: * Command to enable/disable notifications (currently RX packets) from the * driver to userspace. */ enum iwl_tm_cmd_t { IWL_TM_CMD_APP2DEV_UCODE = 1, IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 = 2, IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 = 3, IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8 = 4, IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5, IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6, IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7, IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW = 8, IWL_TM_CMD_APP2DEV_GET_EEPROM = 9, IWL_TM_CMD_APP2DEV_FIXRATE_REQ = 10, IWL_TM_CMD_APP2DEV_BEGIN_TRACE = 11, IWL_TM_CMD_APP2DEV_END_TRACE = 12, IWL_TM_CMD_APP2DEV_READ_TRACE = 13, IWL_TM_CMD_DEV2APP_SYNC_RSP = 14, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15, IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16, IWL_TM_CMD_APP2DEV_OWNERSHIP = 17, RESERVED_18 = 18, RESERVED_19 = 19, RESERVED_20 = 20, RESERVED_21 = 21, IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22, IWL_TM_CMD_APP2DEV_GET_FW_VERSION = 23, IWL_TM_CMD_APP2DEV_GET_DEVICE_ID = 24, IWL_TM_CMD_APP2DEV_GET_FW_INFO = 25, IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26, IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27, IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28, IWL_TM_CMD_APP2DEV_NOTIFICATIONS = 29, IWL_TM_CMD_MAX = 30, }; /* * Atrribute filed in testmode command * See enum iwl_tm_cmd_t. * * @IWL_TM_ATTR_NOT_APPLICABLE: * The attribute is not applicable or invalid * @IWL_TM_ATTR_COMMAND: * From user space to kernel space: * the command either destines to ucode, driver, or register; * From kernel space to user space: * the command either carries synchronous response, * or the spontaneous message multicast from the device; * * @IWL_TM_ATTR_UCODE_CMD_ID: * @IWL_TM_ATTR_UCODE_CMD_DATA: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE, * The mandatory fields are : * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID; * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload * to the ucode * * @IWL_TM_ATTR_REG_OFFSET: * @IWL_TM_ATTR_REG_VALUE8: * @IWL_TM_ATTR_REG_VALUE32: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX, * The mandatory fields are: * IWL_TM_ATTR_REG_OFFSET for the offset of the target register; * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value * * @IWL_TM_ATTR_SYNC_RSP: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP, * The mandatory fields are: * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user * application command * * @IWL_TM_ATTR_UCODE_RX_PKT: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT, * The mandatory fields are: * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user * application * * @IWL_TM_ATTR_EEPROM: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM, * The mandatory fields are: * IWL_TM_ATTR_EEPROM for the data content responging to the user * application * * @IWL_TM_ATTR_TRACE_ADDR: * @IWL_TM_ATTR_TRACE_SIZE: * @IWL_TM_ATTR_TRACE_DUMP: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE, * The mandatory fields are: * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address * IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size * IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump * * @IWL_TM_ATTR_FIXRATE: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ, * The mandatory fields are: * IWL_TM_ATTR_FIXRATE for the fixed rate * * @IWL_TM_ATTR_UCODE_OWNER: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP, * The mandatory fields are: * IWL_TM_ATTR_UCODE_OWNER for the new owner * * @IWL_TM_ATTR_MEM_ADDR: * @IWL_TM_ATTR_BUFFER_SIZE: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ * or IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE. * The mandatory fields are: * IWL_TM_ATTR_MEM_ADDR for the address in SRAM/periphery to read/write * IWL_TM_ATTR_BUFFER_SIZE for the buffer size of data to read/write. * * @IWL_TM_ATTR_BUFFER_DUMP: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP, * IWL_TM_ATTR_BUFFER_DUMP is used for the data that was read. * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE, * this attribute contains the data to write. * * @IWL_TM_ATTR_FW_VERSION: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION, * IWL_TM_ATTR_FW_VERSION for the uCode version * * @IWL_TM_ATTR_DEVICE_ID: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID, * IWL_TM_ATTR_DEVICE_ID for the device ID information * * @IWL_TM_ATTR_FW_TYPE: * @IWL_TM_ATTR_FW_INST_SIZE: * @IWL_TM_ATTR_FW_DATA_SIZE: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_INFO, * The mandatory fields are: * IWL_TM_ATTR_FW_TYPE for the uCode type (INIT/RUNTIME/...) * IWL_TM_ATTR_FW_INST_SIZE for the size of instruction section * IWL_TM_ATTR_FW_DATA_SIZE for the size of data section * * @IWL_TM_ATTR_UCODE_CMD_SKB: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag * indicates that the user wants to receive the response of the command * in a reply SKB. If it's not present, the response is not returned. * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS: * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this * flag enables (if present) or disables (if not) the forwarding * to userspace. */ enum iwl_tm_attr_t { IWL_TM_ATTR_NOT_APPLICABLE = 0, IWL_TM_ATTR_COMMAND = 1, IWL_TM_ATTR_UCODE_CMD_ID = 2, IWL_TM_ATTR_UCODE_CMD_DATA = 3, IWL_TM_ATTR_REG_OFFSET = 4, IWL_TM_ATTR_REG_VALUE8 = 5, IWL_TM_ATTR_REG_VALUE32 = 6, IWL_TM_ATTR_SYNC_RSP = 7, IWL_TM_ATTR_UCODE_RX_PKT = 8, IWL_TM_ATTR_EEPROM = 9, IWL_TM_ATTR_TRACE_ADDR = 10, IWL_TM_ATTR_TRACE_SIZE = 11, IWL_TM_ATTR_TRACE_DUMP = 12, IWL_TM_ATTR_FIXRATE = 13, IWL_TM_ATTR_UCODE_OWNER = 14, IWL_TM_ATTR_MEM_ADDR = 15, IWL_TM_ATTR_BUFFER_SIZE = 16, IWL_TM_ATTR_BUFFER_DUMP = 17, IWL_TM_ATTR_FW_VERSION = 18, IWL_TM_ATTR_DEVICE_ID = 19, IWL_TM_ATTR_FW_TYPE = 20, IWL_TM_ATTR_FW_INST_SIZE = 21, IWL_TM_ATTR_FW_DATA_SIZE = 22, IWL_TM_ATTR_UCODE_CMD_SKB = 23, IWL_TM_ATTR_ENABLE_NOTIFICATION = 24, IWL_TM_ATTR_MAX = 25, }; /* uCode trace buffer */ #define TRACE_BUFF_SIZE_MAX 0x200000 #define TRACE_BUFF_SIZE_MIN 0x20000 #define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN #define TRACE_BUFF_PADD 0x2000 /* Maximum data size of each dump it packet */ #define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024) /* Address offset of data segment in SRAM */ #define SRAM_DATA_SEG_OFFSET 0x800000 #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-test.h0000644000175000017500000001324412026211315024060 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __IWL_TEST_H__ #define __IWL_TEST_H__ #include #include "iwl-trans.h" struct iwl_test_trace { u32 size; u32 tsize; u32 nchunks; u8 *cpu_addr; u8 *trace_addr; dma_addr_t dma_addr; bool enabled; }; struct iwl_test_mem { u32 size; u32 nchunks; u8 *addr; bool in_read; }; /* * struct iwl_test_ops: callback to the op mode * * The structure defines the callbacks that the op_mode should handle, * inorder to handle logic that is out of the scope of iwl_test. The * op_mode must set all the callbacks. * @send_cmd: handler that is used by the test object to request the * op_mode to send a command to the fw. * * @valid_hw_addr: handler that is used by the test object to request the * op_mode to check if the given address is a valid address. * * @get_fw_ver: handler used to get the FW version. * * @alloc_reply: handler used by the test object to request the op_mode * to allocate an skb for sending a reply to the user, and initialize * the skb. It is assumed that the test object only fills the required * attributes. * * @reply: handler used by the test object to request the op_mode to reply * to a request. The skb is an skb previously allocated by the the * alloc_reply callback. I * @alloc_event: handler used by the test object to request the op_mode * to allocate an skb for sending an event, and initialize * the skb. It is assumed that the test object only fills the required * attributes. * * @reply: handler used by the test object to request the op_mode to send * an event. The skb is an skb previously allocated by the the * alloc_event callback. */ struct iwl_test_ops { int (*send_cmd)(struct iwl_op_mode *op_modes, struct iwl_host_cmd *cmd); bool (*valid_hw_addr)(u32 addr); u32 (*get_fw_ver)(struct iwl_op_mode *op_mode); struct sk_buff *(*alloc_reply)(struct iwl_op_mode *op_mode, int len); int (*reply)(struct iwl_op_mode *op_mode, struct sk_buff *skb); struct sk_buff* (*alloc_event)(struct iwl_op_mode *op_mode, int len); void (*event)(struct iwl_op_mode *op_mode, struct sk_buff *skb); }; struct iwl_test { struct iwl_trans *trans; struct iwl_test_ops *ops; struct iwl_test_trace trace; struct iwl_test_mem mem; bool notify; }; void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans, struct iwl_test_ops *ops); void iwl_test_free(struct iwl_test *tst); int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, void *data, int len); int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb); int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, struct netlink_callback *cb); void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb); static inline void iwl_test_enable_notifications(struct iwl_test *tst, bool enable) { tst->notify = enable; } #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-test.c0000644000175000017500000005527512026211315024065 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #include #include #include "iwl-io.h" #include "iwl-fh.h" #include "iwl-prph.h" #include "iwl-trans.h" #include "iwl-test.h" #include "iwl-csr.h" #include "iwl-testmode.h" /* * Periphery registers absolute lower bound. This is used in order to * differentiate registery access through HBUS_TARG_PRPH_* and * HBUS_TARG_MEM_* accesses. */ #define IWL_ABS_PRPH_START (0xA00000) /* * The TLVs used in the gnl message policy between the kernel module and * user space application. iwl_testmode_gnl_msg_policy is to be carried * through the NL80211_CMD_TESTMODE channel regulated by nl80211. * See iwl-testmode.h */ static struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = { [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, }, [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, }, [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, }, [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, }, [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, }, [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, }, [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, }, [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, }, [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, }, [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, }, [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, }, [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, }, [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, }, [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, }, [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, }, [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, }, [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, }, [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, }, [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, }, [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, }, [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, }, [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, }, [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, }, }; static inline void iwl_test_trace_clear(struct iwl_test *tst) { memset(&tst->trace, 0, sizeof(struct iwl_test_trace)); } static void iwl_test_trace_stop(struct iwl_test *tst) { if (!tst->trace.enabled) return; if (tst->trace.cpu_addr && tst->trace.dma_addr) dma_free_coherent(tst->trans->dev, tst->trace.tsize, tst->trace.cpu_addr, tst->trace.dma_addr); iwl_test_trace_clear(tst); } static inline void iwl_test_mem_clear(struct iwl_test *tst) { memset(&tst->mem, 0, sizeof(struct iwl_test_mem)); } static inline void iwl_test_mem_stop(struct iwl_test *tst) { if (!tst->mem.in_read) return; iwl_test_mem_clear(tst); } /* * Initializes the test object * During the lifetime of the test object it is assumed that the transport is * started. The test object should be stopped before the transport is stopped. */ void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans, struct iwl_test_ops *ops) { tst->trans = trans; tst->ops = ops; iwl_test_trace_clear(tst); iwl_test_mem_clear(tst); } EXPORT_SYMBOL_GPL(iwl_test_init); /* * Stop the test object */ void iwl_test_free(struct iwl_test *tst) { iwl_test_mem_stop(tst); iwl_test_trace_stop(tst); } EXPORT_SYMBOL_GPL(iwl_test_free); static inline int iwl_test_send_cmd(struct iwl_test *tst, struct iwl_host_cmd *cmd) { return tst->ops->send_cmd(tst->trans->op_mode, cmd); } static inline bool iwl_test_valid_hw_addr(struct iwl_test *tst, u32 addr) { return tst->ops->valid_hw_addr(addr); } static inline u32 iwl_test_fw_ver(struct iwl_test *tst) { return tst->ops->get_fw_ver(tst->trans->op_mode); } static inline struct sk_buff* iwl_test_alloc_reply(struct iwl_test *tst, int len) { return tst->ops->alloc_reply(tst->trans->op_mode, len); } static inline int iwl_test_reply(struct iwl_test *tst, struct sk_buff *skb) { return tst->ops->reply(tst->trans->op_mode, skb); } static inline struct sk_buff* iwl_test_alloc_event(struct iwl_test *tst, int len) { return tst->ops->alloc_event(tst->trans->op_mode, len); } static inline void iwl_test_event(struct iwl_test *tst, struct sk_buff *skb) { return tst->ops->event(tst->trans->op_mode, skb); } /* * This function handles the user application commands to the fw. The fw * commands are sent in a synchronuous manner. In case that the user requested * to get commands response, it is send to the user. */ static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb) { struct iwl_host_cmd cmd; struct iwl_rx_packet *pkt; struct sk_buff *skb; void *reply_buf; u32 reply_len; int ret; bool cmd_want_skb; memset(&cmd, 0, sizeof(struct iwl_host_cmd)); if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] || !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) { IWL_ERR(tst->trans, "Missing fw command mandatory fields\n"); return -ENOMSG; } cmd.flags = CMD_ON_DEMAND | CMD_SYNC; cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]); if (cmd_want_skb) cmd.flags |= CMD_WANT_SKB; cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]); cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n", cmd.id, cmd.flags, cmd.len[0]); ret = iwl_test_send_cmd(tst, &cmd); if (ret) { IWL_ERR(tst->trans, "Failed to send hcmd\n"); return ret; } if (!cmd_want_skb) return ret; /* Handling return of SKB to the user */ pkt = cmd.resp_pkt; if (!pkt) { IWL_ERR(tst->trans, "HCMD received a null response packet\n"); return ret; } reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; skb = iwl_test_alloc_reply(tst, reply_len + 20); reply_buf = kmalloc(reply_len, GFP_KERNEL); if (!skb || !reply_buf) { kfree_skb(skb); kfree(reply_buf); return -ENOMEM; } /* The reply is in a page, that we cannot send to user space. */ memcpy(reply_buf, &(pkt->hdr), reply_len); iwl_free_resp(&cmd); if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf)) goto nla_put_failure; return iwl_test_reply(tst, skb); nla_put_failure: IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n"); kfree(reply_buf); kfree_skb(skb); return -ENOMSG; } /* * Handles the user application commands for register access. */ static int iwl_test_reg(struct iwl_test *tst, struct nlattr **tb) { u32 ofs, val32, cmd; u8 val8; struct sk_buff *skb; int status = 0; struct iwl_trans *trans = tst->trans; if (!tb[IWL_TM_ATTR_REG_OFFSET]) { IWL_ERR(trans, "Missing reg offset\n"); return -ENOMSG; } ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]); IWL_DEBUG_INFO(trans, "test reg access cmd offset=0x%x\n", ofs); cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); /* * Allow access only to FH/CSR/HBUS in direct mode. * Since we don't have the upper bounds for the CSR and HBUS segments, * we will use only the upper bound of FH for sanity check. */ if (ofs >= FH_MEM_UPPER_BOUND) { IWL_ERR(trans, "offset out of segment (0x0 - 0x%x)\n", FH_MEM_UPPER_BOUND); return -EINVAL; } switch (cmd) { case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: val32 = iwl_read_direct32(tst->trans, ofs); IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32); skb = iwl_test_alloc_reply(tst, 20); if (!skb) { IWL_ERR(trans, "Memory allocation fail\n"); return -ENOMEM; } if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32)) goto nla_put_failure; status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(trans, "Error sending msg : %d\n", status); break; case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: if (!tb[IWL_TM_ATTR_REG_VALUE32]) { IWL_ERR(trans, "Missing value to write\n"); return -ENOMSG; } else { val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]); IWL_DEBUG_INFO(trans, "32b write val=0x%x\n", val32); iwl_write_direct32(tst->trans, ofs, val32); } break; case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: if (!tb[IWL_TM_ATTR_REG_VALUE8]) { IWL_ERR(trans, "Missing value to write\n"); return -ENOMSG; } else { val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]); IWL_DEBUG_INFO(trans, "8b write val=0x%x\n", val8); iwl_write8(tst->trans, ofs, val8); } break; default: IWL_ERR(trans, "Unknown test register cmd ID\n"); return -ENOMSG; } return status; nla_put_failure: kfree_skb(skb); return -EMSGSIZE; } /* * Handles the request to start FW tracing. Allocates of the trace buffer * and sends a reply to user space with the address of the allocated buffer. */ static int iwl_test_trace_begin(struct iwl_test *tst, struct nlattr **tb) { struct sk_buff *skb; int status = 0; if (tst->trace.enabled) return -EBUSY; if (!tb[IWL_TM_ATTR_TRACE_SIZE]) tst->trace.size = TRACE_BUFF_SIZE_DEF; else tst->trace.size = nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]); if (!tst->trace.size) return -EINVAL; if (tst->trace.size < TRACE_BUFF_SIZE_MIN || tst->trace.size > TRACE_BUFF_SIZE_MAX) return -EINVAL; tst->trace.tsize = tst->trace.size + TRACE_BUFF_PADD; tst->trace.cpu_addr = dma_alloc_coherent(tst->trans->dev, tst->trace.tsize, &tst->trace.dma_addr, GFP_KERNEL); if (!tst->trace.cpu_addr) return -ENOMEM; tst->trace.enabled = true; tst->trace.trace_addr = (u8 *)PTR_ALIGN(tst->trace.cpu_addr, 0x100); memset(tst->trace.trace_addr, 0x03B, tst->trace.size); skb = iwl_test_alloc_reply(tst, sizeof(tst->trace.dma_addr) + 20); if (!skb) { IWL_ERR(tst->trans, "Memory allocation fail\n"); iwl_test_trace_stop(tst); return -ENOMEM; } if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR, sizeof(tst->trace.dma_addr), (u64 *)&tst->trace.dma_addr)) goto nla_put_failure; status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(tst->trans, "Error sending msg : %d\n", status); tst->trace.nchunks = DIV_ROUND_UP(tst->trace.size, DUMP_CHUNK_SIZE); return status; nla_put_failure: kfree_skb(skb); if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) == IWL_TM_CMD_APP2DEV_BEGIN_TRACE) iwl_test_trace_stop(tst); return -EMSGSIZE; } /* * Handles indirect read from the periphery or the SRAM. The read is performed * to a temporary buffer. The user space application should later issue a dump */ static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size) { struct iwl_trans *trans = tst->trans; unsigned long flags; int i; if (size & 0x3) return -EINVAL; tst->mem.size = size; tst->mem.addr = kmalloc(tst->mem.size, GFP_KERNEL); if (tst->mem.addr == NULL) return -ENOMEM; /* Hard-coded periphery absolute address */ if (IWL_ABS_PRPH_START <= addr && addr < IWL_ABS_PRPH_START + PRPH_END) { spin_lock_irqsave(&trans->reg_lock, flags); iwl_grab_nic_access(trans); iwl_write32(trans, HBUS_TARG_PRPH_RADDR, addr | (3 << 24)); for (i = 0; i < size; i += 4) *(u32 *)(tst->mem.addr + i) = iwl_read32(trans, HBUS_TARG_PRPH_RDAT); iwl_release_nic_access(trans); spin_unlock_irqrestore(&trans->reg_lock, flags); } else { /* target memory (SRAM) */ _iwl_read_targ_mem_dwords(trans, addr, tst->mem.addr, tst->mem.size / 4); } tst->mem.nchunks = DIV_ROUND_UP(tst->mem.size, DUMP_CHUNK_SIZE); tst->mem.in_read = true; return 0; } /* * Handles indirect write to the periphery or SRAM. The is performed to a * temporary buffer. */ static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr, u32 size, unsigned char *buf) { struct iwl_trans *trans = tst->trans; u32 val, i; unsigned long flags; if (IWL_ABS_PRPH_START <= addr && addr < IWL_ABS_PRPH_START + PRPH_END) { /* Periphery writes can be 1-3 bytes long, or DWORDs */ if (size < 4) { memcpy(&val, buf, size); spin_lock_irqsave(&trans->reg_lock, flags); iwl_grab_nic_access(trans); iwl_write32(trans, HBUS_TARG_PRPH_WADDR, (addr & 0x0000FFFF) | ((size - 1) << 24)); iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); iwl_release_nic_access(trans); /* needed after consecutive writes w/o read */ mmiowb(); spin_unlock_irqrestore(&trans->reg_lock, flags); } else { if (size % 4) return -EINVAL; for (i = 0; i < size; i += 4) iwl_write_prph(trans, addr+i, *(u32 *)(buf+i)); } } else if (iwl_test_valid_hw_addr(tst, addr)) { _iwl_write_targ_mem_dwords(trans, addr, buf, size / 4); } else { return -EINVAL; } return 0; } /* * Handles the user application commands for indirect read/write * to/from the periphery or the SRAM. */ static int iwl_test_indirect_mem(struct iwl_test *tst, struct nlattr **tb) { u32 addr, size, cmd; unsigned char *buf; /* Both read and write should be blocked, for atomicity */ if (tst->mem.in_read) return -EBUSY; cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); if (!tb[IWL_TM_ATTR_MEM_ADDR]) { IWL_ERR(tst->trans, "Error finding memory offset address\n"); return -ENOMSG; } addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]); if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) { IWL_ERR(tst->trans, "Error finding size for memory reading\n"); return -ENOMSG; } size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]); if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) { return iwl_test_indirect_read(tst, addr, size); } else { if (!tb[IWL_TM_ATTR_BUFFER_DUMP]) return -EINVAL; buf = (unsigned char *)nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]); return iwl_test_indirect_write(tst, addr, size, buf); } } /* * Enable notifications to user space */ static int iwl_test_notifications(struct iwl_test *tst, struct nlattr **tb) { tst->notify = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]); return 0; } /* * Handles the request to get the device id */ static int iwl_test_get_dev_id(struct iwl_test *tst, struct nlattr **tb) { u32 devid = tst->trans->hw_id; struct sk_buff *skb; int status; IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid); skb = iwl_test_alloc_reply(tst, 20); if (!skb) { IWL_ERR(tst->trans, "Memory allocation fail\n"); return -ENOMEM; } if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid)) goto nla_put_failure; status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(tst->trans, "Error sending msg : %d\n", status); return 0; nla_put_failure: kfree_skb(skb); return -EMSGSIZE; } /* * Handles the request to get the FW version */ static int iwl_test_get_fw_ver(struct iwl_test *tst, struct nlattr **tb) { struct sk_buff *skb; int status; u32 ver = iwl_test_fw_ver(tst); IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver); skb = iwl_test_alloc_reply(tst, 20); if (!skb) { IWL_ERR(tst->trans, "Memory allocation fail\n"); return -ENOMEM; } if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver)) goto nla_put_failure; status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(tst->trans, "Error sending msg : %d\n", status); return 0; nla_put_failure: kfree_skb(skb); return -EMSGSIZE; } /* * Parse the netlink message and validate that the IWL_TM_ATTR_CMD exists */ int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, void *data, int len) { int result; result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, iwl_testmode_gnl_msg_policy); if (result) { IWL_ERR(tst->trans, "Fail parse gnl msg: %d\n", result); return result; } /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ if (!tb[IWL_TM_ATTR_COMMAND]) { IWL_ERR(tst->trans, "Missing testmode command type\n"); return -ENOMSG; } return 0; } EXPORT_SYMBOL_GPL(iwl_test_parse); /* * Handle test commands. * Returns 1 for unknown commands (not handled by the test object); negative * value in case of error. */ int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb) { int result; switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { case IWL_TM_CMD_APP2DEV_UCODE: IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n"); result = iwl_test_fw_cmd(tst, tb); break; case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: IWL_DEBUG_INFO(tst->trans, "test cmd to register\n"); result = iwl_test_reg(tst, tb); break; case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n"); result = iwl_test_trace_begin(tst, tb); break; case IWL_TM_CMD_APP2DEV_END_TRACE: iwl_test_trace_stop(tst); result = 0; break; case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: IWL_DEBUG_INFO(tst->trans, "test indirect memory cmd\n"); result = iwl_test_indirect_mem(tst, tb); break; case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: IWL_DEBUG_INFO(tst->trans, "test notifications cmd\n"); result = iwl_test_notifications(tst, tb); break; case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n"); result = iwl_test_get_fw_ver(tst, tb); break; case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n"); result = iwl_test_get_dev_id(tst, tb); break; default: IWL_DEBUG_INFO(tst->trans, "Unknown test command\n"); result = 1; break; } return result; } EXPORT_SYMBOL_GPL(iwl_test_handle_cmd); static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb, struct netlink_callback *cb) { int idx, length; if (!tst->trace.enabled || !tst->trace.trace_addr) return -EFAULT; idx = cb->args[4]; if (idx >= tst->trace.nchunks) return -ENOENT; length = DUMP_CHUNK_SIZE; if (((idx + 1) == tst->trace.nchunks) && (tst->trace.size % DUMP_CHUNK_SIZE)) length = tst->trace.size % DUMP_CHUNK_SIZE; if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length, tst->trace.trace_addr + (DUMP_CHUNK_SIZE * idx))) goto nla_put_failure; cb->args[4] = ++idx; return 0; nla_put_failure: return -ENOBUFS; } static int iwl_test_buffer_dump(struct iwl_test *tst, struct sk_buff *skb, struct netlink_callback *cb) { int idx, length; if (!tst->mem.in_read) return -EFAULT; idx = cb->args[4]; if (idx >= tst->mem.nchunks) { iwl_test_mem_stop(tst); return -ENOENT; } length = DUMP_CHUNK_SIZE; if (((idx + 1) == tst->mem.nchunks) && (tst->mem.size % DUMP_CHUNK_SIZE)) length = tst->mem.size % DUMP_CHUNK_SIZE; if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length, tst->mem.addr + (DUMP_CHUNK_SIZE * idx))) goto nla_put_failure; cb->args[4] = ++idx; return 0; nla_put_failure: return -ENOBUFS; } /* * Handle dump commands. * Returns 1 for unknown commands (not handled by the test object); negative * value in case of error. */ int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, struct netlink_callback *cb) { int result; switch (cmd) { case IWL_TM_CMD_APP2DEV_READ_TRACE: IWL_DEBUG_INFO(tst->trans, "uCode trace cmd\n"); result = iwl_test_trace_dump(tst, skb, cb); break; case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: IWL_DEBUG_INFO(tst->trans, "testmode sram dump cmd\n"); result = iwl_test_buffer_dump(tst, skb, cb); break; default: result = 1; break; } return result; } EXPORT_SYMBOL_GPL(iwl_test_dump); /* * Multicast a spontaneous messages from the device to the user space. */ static void iwl_test_send_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb) { struct sk_buff *skb; struct iwl_rx_packet *data; int length; data = rxb_addr(rxb); length = le32_to_cpu(data->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; /* the length doesn't include len_n_flags field, so add it manually */ length += sizeof(__le32); skb = iwl_test_alloc_event(tst, length + 20); if (skb == NULL) { IWL_ERR(tst->trans, "Out of memory for message to user\n"); return; } if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data)) goto nla_put_failure; iwl_test_event(tst, skb); return; nla_put_failure: kfree_skb(skb); IWL_ERR(tst->trans, "Ouch, overran buffer, check allocation!\n"); } /* * Called whenever a Rx frames is recevied from the device. If notifications to * the user space are requested, sends the frames to the user. */ void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb) { if (tst->notify) iwl_test_send_rx(tst, rxb); } EXPORT_SYMBOL_GPL(iwl_test_rx); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-prph.h0000644000175000017500000002404212026211315024050 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_prph_h__ #define __iwl_prph_h__ /* * Registers in this file are internal, not PCI bus memory mapped. * Driver accesses these via HBUS_TARG_PRPH_* registers. */ #define PRPH_BASE (0x00000) #define PRPH_END (0xFFFFF) /* APMG (power management) constants */ #define APMG_BASE (PRPH_BASE + 0x3000) #define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) #define APMG_CLK_EN_REG (APMG_BASE + 0x0004) #define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) #define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) #define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) #define APMG_RFKILL_REG (APMG_BASE + 0x0014) #define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) #define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) #define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) #define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) #define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) #define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) #define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) #define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) #define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) #define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) #define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) #define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) #define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ #define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) /** * Tx Scheduler * * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in * host DRAM. It steers each frame's Tx command (which contains the frame * data) into one of up to 7 prioritized Tx DMA FIFO channels within the * device. A queue maps to only one (selectable by driver) Tx DMA channel, * but one DMA channel may take input from several queues. * * Tx DMA FIFOs have dedicated purposes. * * For 5000 series and up, they are used differently * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c): * * 0 -- EDCA BK (background) frames, lowest priority * 1 -- EDCA BE (best effort) frames, normal priority * 2 -- EDCA VI (video) frames, higher priority * 3 -- EDCA VO (voice) and management frames, highest priority * 4 -- unused * 5 -- unused * 6 -- unused * 7 -- Commands * * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. * In addition, driver can map the remaining queues to Tx DMA/FIFO * channels 0-3 to support 11n aggregation via EDCA DMA channels. * * The driver sets up each queue to work in one of two modes: * * 1) Scheduler-Ack, in which the scheduler automatically supports a * block-ack (BA) window of up to 64 TFDs. In this mode, each queue * contains TFDs for a unique combination of Recipient Address (RA) * and Traffic Identifier (TID), that is, traffic of a given * Quality-Of-Service (QOS) priority, destined for a single station. * * In scheduler-ack mode, the scheduler keeps track of the Tx status of * each frame within the BA window, including whether it's been transmitted, * and whether it's been acknowledged by the receiving station. The device * automatically processes block-acks received from the receiving STA, * and reschedules un-acked frames to be retransmitted (successful * Tx completion may end up being out-of-order). * * The driver must maintain the queue's Byte Count table in host DRAM * for this mode. * This mode does not support fragmentation. * * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. * The device may automatically retry Tx, but will retry only one frame * at a time, until receiving ACK from receiving station, or reaching * retry limit and giving up. * * The command queue (#4/#9) must use this mode! * This mode does not require use of the Byte Count table in host DRAM. * * Driver controls scheduler operation via 3 means: * 1) Scheduler registers * 2) Shared scheduler data base in internal SRAM * 3) Shared data in host DRAM * * Initialization: * * When loading, driver should allocate memory for: * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory * (1024 bytes for each queue). * * After receiving "Alive" response from uCode, driver must initialize * the scheduler (especially for queue #4/#9, the command queue, otherwise * the driver can't issue commands!): */ #define SCD_MEM_LOWER_BOUND (0x0000) /** * Max Tx window size is the max number of contiguous TFDs that the scheduler * can keep track of at one time when creating block-ack chains of frames. * Note that "64" matches the number of ack bits in a block-ack packet. */ #define SCD_WIN_SIZE 64 #define SCD_FRAME_LIMIT 64 #define SCD_TXFIFO_POS_TID (0) #define SCD_TXFIFO_POS_RA (4) #define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) /* agn SCD */ #define SCD_QUEUE_STTS_REG_POS_TXF (0) #define SCD_QUEUE_STTS_REG_POS_ACTIVE (3) #define SCD_QUEUE_STTS_REG_POS_WSL (4) #define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19) #define SCD_QUEUE_STTS_REG_MSK (0x017F0000) #define SCD_QUEUE_CTX_REG1_CREDIT_POS (8) #define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00) #define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24) #define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000) #define SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0) #define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F) #define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) #define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) /* Context Data */ #define SCD_CONTEXT_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x600) #define SCD_CONTEXT_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0) /* Tx status */ #define SCD_TX_STTS_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0) #define SCD_TX_STTS_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0) /* Translation Data */ #define SCD_TRANS_TBL_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0) #define SCD_TRANS_TBL_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x808) #define SCD_CONTEXT_QUEUE_OFFSET(x)\ (SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8)) #define SCD_TRANS_TBL_OFFSET_QUEUE(x) \ ((SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc) #define SCD_BASE (PRPH_BASE + 0xa02c00) #define SCD_SRAM_BASE_ADDR (SCD_BASE + 0x0) #define SCD_DRAM_BASE_ADDR (SCD_BASE + 0x8) #define SCD_AIT (SCD_BASE + 0x0c) #define SCD_TXFACT (SCD_BASE + 0x10) #define SCD_ACTIVE (SCD_BASE + 0x14) #define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8) #define SCD_CHAINEXT_EN (SCD_BASE + 0x244) #define SCD_AGGR_SEL (SCD_BASE + 0x248) #define SCD_INTERRUPT_MASK (SCD_BASE + 0x108) static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl) { if (chnl < 20) return SCD_BASE + 0x18 + chnl * 4; WARN_ON_ONCE(chnl >= 32); return SCD_BASE + 0x284 + (chnl - 20) * 4; } static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl) { if (chnl < 20) return SCD_BASE + 0x68 + chnl * 4; WARN_ON_ONCE(chnl >= 32); return SCD_BASE + 0x2B4 + (chnl - 20) * 4; } static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) { if (chnl < 20) return SCD_BASE + 0x10c + chnl * 4; WARN_ON_ONCE(chnl >= 32); return SCD_BASE + 0x384 + (chnl - 20) * 4; } /*********************** END TX SCHEDULER *************************************/ #endif /* __iwl_prph_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-op-mode.h0000644000175000017500000002000012026211315024425 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __iwl_op_mode_h__ #define __iwl_op_mode_h__ struct iwl_op_mode; struct iwl_trans; struct sk_buff; struct iwl_device_cmd; struct iwl_rx_cmd_buffer; struct iwl_fw; struct iwl_cfg; /** * DOC: Operational mode - what is it ? * * The operational mode (a.k.a. op_mode) is the layer that implements * mac80211's handlers. It knows two APIs: mac80211's and the fw's. It uses * the transport API to access the HW. The op_mode doesn't need to know how the * underlying HW works, since the transport layer takes care of that. * * There can be several op_mode: i.e. different fw APIs will require two * different op_modes. This is why the op_mode is virtualized. */ /** * DOC: Life cycle of the Operational mode * * The operational mode has a very simple life cycle. * * 1) The driver layer (iwl-drv.c) chooses the op_mode based on the * capabilities advertized by the fw file (in TLV format). * 2) The driver layer starts the op_mode (ops->start) * 3) The op_mode registers registers mac80211 * 4) The op_mode is governed by mac80211 * 5) The driver layer stops the op_mode */ /** * struct iwl_op_mode_ops - op_mode specific operations * * The op_mode exports its ops so that external components can start it and * interact with it. The driver layer typically calls the start and stop * handlers, the transport layer calls the others. * * All the handlers MUST be implemented * * @start: start the op_mode. The transport layer is already allocated. * May sleep * @stop: stop the op_mode. Must free all the memory allocated. * May sleep * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the * HCMD the this Rx responds to. * Must be atomic and called with BH disabled. * @queue_full: notifies that a HW queue is full. * Must be atomic and called with BH disabled. * @queue_not_full: notifies that a HW queue is not full any more. * Must be atomic and called with BH disabled. * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that * the radio is killed. Must be atomic. * @free_skb: allows the transport layer to free skbs that haven't been * reclaimed by the op_mode. This can happen when the driver is freed and * there are Tx packets pending in the transport layer. * Must be atomic * @nic_error: error notification. Must be atomic and must be called with BH * disabled. * @cmd_queue_full: Called when the command queue gets full. Must be atomic and * called with BH disabled. * @nic_config: configure NIC, called before firmware is started. * May sleep * @wimax_active: invoked when WiMax becomes active. Must be atomic and called * with BH disabled. */ struct iwl_op_mode_ops { struct iwl_op_mode *(*start)(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir); void (*stop)(struct iwl_op_mode *op_mode); int (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); void (*queue_full)(struct iwl_op_mode *op_mode, int queue); void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue); void (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb); void (*nic_error)(struct iwl_op_mode *op_mode); void (*cmd_queue_full)(struct iwl_op_mode *op_mode); void (*nic_config)(struct iwl_op_mode *op_mode); void (*wimax_active)(struct iwl_op_mode *op_mode); }; int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops); void iwl_opmode_deregister(const char *name); /** * struct iwl_op_mode - operational mode * * This holds an implementation of the mac80211 / fw API. * * @ops - pointer to its own ops */ struct iwl_op_mode { const struct iwl_op_mode_ops *ops; const struct iwl_trans *trans; char op_mode_specific[0] __aligned(sizeof(void *)); }; static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode) { might_sleep(); op_mode->ops->stop(op_mode); } static inline int iwl_op_mode_rx(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { return op_mode->ops->rx(op_mode, rxb, cmd); } static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode, int queue) { op_mode->ops->queue_full(op_mode, queue); } static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode, int queue) { op_mode->ops->queue_not_full(op_mode, queue); } static inline void iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, bool state) { op_mode->ops->hw_rf_kill(op_mode, state); } static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) { op_mode->ops->free_skb(op_mode, skb); } static inline void iwl_op_mode_nic_error(struct iwl_op_mode *op_mode) { op_mode->ops->nic_error(op_mode); } static inline void iwl_op_mode_cmd_queue_full(struct iwl_op_mode *op_mode) { op_mode->ops->cmd_queue_full(op_mode); } static inline void iwl_op_mode_nic_config(struct iwl_op_mode *op_mode) { might_sleep(); op_mode->ops->nic_config(op_mode); } static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode) { op_mode->ops->wimax_active(op_mode); } #endif /* __iwl_op_mode_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-notif-wait.h0000644000175000017500000001224612026211315025163 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __iwl_notif_wait_h__ #define __iwl_notif_wait_h__ #include #include "iwl-trans.h" struct iwl_notif_wait_data { struct list_head notif_waits; spinlock_t notif_wait_lock; wait_queue_head_t notif_waitq; }; #define MAX_NOTIF_CMDS 5 /** * struct iwl_notification_wait - notification wait entry * @list: list head for global list * @fn: Function called with the notification. If the function * returns true, the wait is over, if it returns false then * the waiter stays blocked. If no function is given, any * of the listed commands will unblock the waiter. * @cmds: command IDs * @n_cmds: number of command IDs * @triggered: waiter should be woken up * @aborted: wait was aborted * * This structure is not used directly, to wait for a * notification declare it on the stack, and call * iwlagn_init_notification_wait() with appropriate * parameters. Then do whatever will cause the ucode * to notify the driver, and to wait for that then * call iwlagn_wait_notification(). * * Each notification is one-shot. If at some point we * need to support multi-shot notifications (which * can't be allocated on the stack) we need to modify * the code for them. */ struct iwl_notification_wait { struct list_head list; bool (*fn)(struct iwl_notif_wait_data *notif_data, struct iwl_rx_packet *pkt, void *data); void *fn_data; u8 cmds[MAX_NOTIF_CMDS]; u8 n_cmds; bool triggered, aborted; }; /* caller functions */ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data); void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data, struct iwl_rx_packet *pkt); void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data); /* user functions */ void __acquires(wait_entry) iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data, struct iwl_notification_wait *wait_entry, const u8 *cmds, int n_cmds, bool (*fn)(struct iwl_notif_wait_data *notif_data, struct iwl_rx_packet *pkt, void *data), void *fn_data); int __must_check __releases(wait_entry) iwl_wait_notification(struct iwl_notif_wait_data *notif_data, struct iwl_notification_wait *wait_entry, unsigned long timeout); void __releases(wait_entry) iwl_remove_notification(struct iwl_notif_wait_data *notif_data, struct iwl_notification_wait *wait_entry); #endif /* __iwl_notif_wait_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-notif-wait.c0000644000175000017500000001424512026211315025157 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #include #include #include "iwl-notif-wait.h" void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) { spin_lock_init(¬if_wait->notif_wait_lock); INIT_LIST_HEAD(¬if_wait->notif_waits); init_waitqueue_head(¬if_wait->notif_waitq); } EXPORT_SYMBOL_GPL(iwl_notification_wait_init); void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt) { bool triggered = false; if (!list_empty(¬if_wait->notif_waits)) { struct iwl_notification_wait *w; spin_lock(¬if_wait->notif_wait_lock); list_for_each_entry(w, ¬if_wait->notif_waits, list) { int i; bool found = false; /* * If it already finished (triggered) or has been * aborted then don't evaluate it again to avoid races, * Otherwise the function could be called again even * though it returned true before */ if (w->triggered || w->aborted) continue; for (i = 0; i < w->n_cmds; i++) { if (w->cmds[i] == pkt->hdr.cmd) { found = true; break; } } if (!found) continue; if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) { w->triggered = true; triggered = true; } } spin_unlock(¬if_wait->notif_wait_lock); } if (triggered) wake_up_all(¬if_wait->notif_waitq); } EXPORT_SYMBOL_GPL(iwl_notification_wait_notify); void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) { struct iwl_notification_wait *wait_entry; spin_lock(¬if_wait->notif_wait_lock); list_for_each_entry(wait_entry, ¬if_wait->notif_waits, list) wait_entry->aborted = true; spin_unlock(¬if_wait->notif_wait_lock); wake_up_all(¬if_wait->notif_waitq); } EXPORT_SYMBOL_GPL(iwl_abort_notification_waits); void iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry, const u8 *cmds, int n_cmds, bool (*fn)(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data), void *fn_data) { if (WARN_ON(n_cmds > MAX_NOTIF_CMDS)) n_cmds = MAX_NOTIF_CMDS; wait_entry->fn = fn; wait_entry->fn_data = fn_data; wait_entry->n_cmds = n_cmds; memcpy(wait_entry->cmds, cmds, n_cmds); wait_entry->triggered = false; wait_entry->aborted = false; spin_lock_bh(¬if_wait->notif_wait_lock); list_add(&wait_entry->list, ¬if_wait->notif_waits); spin_unlock_bh(¬if_wait->notif_wait_lock); } EXPORT_SYMBOL_GPL(iwl_init_notification_wait); int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry, unsigned long timeout) { int ret; ret = wait_event_timeout(notif_wait->notif_waitq, wait_entry->triggered || wait_entry->aborted, timeout); spin_lock_bh(¬if_wait->notif_wait_lock); list_del(&wait_entry->list); spin_unlock_bh(¬if_wait->notif_wait_lock); if (wait_entry->aborted) return -EIO; /* return value is always >= 0 */ if (ret <= 0) return -ETIMEDOUT; return 0; } EXPORT_SYMBOL_GPL(iwl_wait_notification); void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry) { spin_lock_bh(¬if_wait->notif_wait_lock); list_del(&wait_entry->list); spin_unlock_bh(¬if_wait->notif_wait_lock); } EXPORT_SYMBOL_GPL(iwl_remove_notification); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-modparams.h0000644000175000017500000001102112026211315025053 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __iwl_modparams_h__ #define __iwl_modparams_h__ #include #include #include #include extern struct iwl_mod_params iwlwifi_mod_params; enum iwl_power_level { IWL_POWER_INDEX_1, IWL_POWER_INDEX_2, IWL_POWER_INDEX_3, IWL_POWER_INDEX_4, IWL_POWER_INDEX_5, IWL_POWER_NUM }; #define IWL_DISABLE_HT_ALL BIT(0) #define IWL_DISABLE_HT_TXAGG BIT(1) #define IWL_DISABLE_HT_RXAGG BIT(2) /** * struct iwl_mod_params * * Holds the module parameters * * @sw_crypto: using hardware encryption, default = 0 * @disable_11n: disable 11n capabilities, default = 0, * use IWL_DISABLE_HT_* constants * @amsdu_size_8K: enable 8K amsdu size, default = 1 * @restart_fw: restart firmware, default = 1 * @plcp_check: enable plcp health check, default = true * @wd_disable: enable stuck queue check, default = 0 * @bt_coex_active: enable bt coex, default = true * @led_mode: system default, default = 0 * @power_save: disable power save, default = false * @power_level: power level, default = 1 * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 * @bt_ch_announce: BT channel inhibition, default = enable * @auto_agg: enable agg. without check, default = true * @disable_5ghz: disable 5GHz capability, default = false */ struct iwl_mod_params { int sw_crypto; unsigned int disable_11n; int amsdu_size_8K; int restart_fw; bool plcp_check; int wd_disable; bool bt_coex_active; int led_mode; bool power_save; int power_level; u32 debug_level; int ant_coupling; bool bt_ch_announce; bool auto_agg; bool disable_5ghz; }; #endif /* #__iwl_modparams_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-io.h0000644000175000017500000000646512026211315023517 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __iwl_io_h__ #define __iwl_io_h__ #include "iwl-devtrace.h" #include "iwl-trans.h" static inline void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) { trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val); iwl_trans_write8(trans, ofs, val); } static inline void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val) { trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val); iwl_trans_write32(trans, ofs, val); } static inline u32 iwl_read32(struct iwl_trans *trans, u32 ofs) { u32 val = iwl_trans_read32(trans, ofs); trace_iwlwifi_dev_ioread32(trans->dev, ofs, val); return val; } void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask); void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask); void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value); int iwl_poll_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout); int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, int timeout); int iwl_grab_nic_access_silent(struct iwl_trans *trans); bool iwl_grab_nic_access(struct iwl_trans *trans); void iwl_release_nic_access(struct iwl_trans *trans); u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg); void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value); u32 iwl_read_prph(struct iwl_trans *trans, u32 reg); void iwl_write_prph(struct iwl_trans *trans, u32 addr, u32 val); void iwl_set_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask); void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 reg, u32 bits, u32 mask); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask); void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr, void *buf, int dwords); #define iwl_read_targ_mem_bytes(trans, addr, buf, bufsize) \ do { \ BUILD_BUG_ON((bufsize) % sizeof(u32)); \ _iwl_read_targ_mem_dwords(trans, addr, buf, \ (bufsize) / sizeof(u32));\ } while (0) int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr, void *buf, int dwords); u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr); int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val); #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-io.c0000644000175000017500000002343012026211315023501 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include "iwl-io.h" #include "iwl-csr.h" #include "iwl-debug.h" #define IWL_POLL_INTERVAL 10 /* microseconds */ static inline void __iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask) { iwl_write32(trans, reg, iwl_read32(trans, reg) | mask); } static inline void __iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask) { iwl_write32(trans, reg, iwl_read32(trans, reg) & ~mask); } void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask) { unsigned long flags; spin_lock_irqsave(&trans->reg_lock, flags); __iwl_set_bit(trans, reg, mask); spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_set_bit); void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask) { unsigned long flags; spin_lock_irqsave(&trans->reg_lock, flags); __iwl_clear_bit(trans, reg, mask); spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_clear_bit); void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value) { unsigned long flags; u32 v; #ifdef CONFIG_IWLWIFI_DEBUG WARN_ON_ONCE(value & ~mask); #endif spin_lock_irqsave(&trans->reg_lock, flags); v = iwl_read32(trans, reg); v &= ~mask; v |= value; iwl_write32(trans, reg, v); spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_set_bits_mask); int iwl_poll_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout) { int t = 0; do { if ((iwl_read32(trans, addr) & mask) == (bits & mask)) return t; udelay(IWL_POLL_INTERVAL); t += IWL_POLL_INTERVAL; } while (t < timeout); return -ETIMEDOUT; } EXPORT_SYMBOL_GPL(iwl_poll_bit); int iwl_grab_nic_access_silent(struct iwl_trans *trans) { int ret; lockdep_assert_held(&trans->reg_lock); /* this bit wakes up the NIC */ __iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* * These bits say the device is running, and should keep running for * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), * but they do not indicate that embedded SRAM is restored yet; * 3945 and 4965 have volatile SRAM, and must save/restore contents * to/from host DRAM when sleeping/waking for power-saving. * Each direction takes approximately 1/4 millisecond; with this * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a * series of register accesses are expected (e.g. reading Event Log), * to keep device from sleeping. * * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that * SRAM is okay/restored. We don't check that here because this call * is just for hardware register access; but GP1 MAC_SLEEP check is a * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). * * 5000 series and later (including 1000 series) have non-volatile SRAM, * and do not save/restore SRAM when power cycling. */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); if (ret < 0) { iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); return -EIO; } return 0; } EXPORT_SYMBOL_GPL(iwl_grab_nic_access_silent); bool iwl_grab_nic_access(struct iwl_trans *trans) { int ret = iwl_grab_nic_access_silent(trans); if (unlikely(ret)) { u32 val = iwl_read32(trans, CSR_GP_CNTRL); WARN_ONCE(1, "Timeout waiting for hardware access " "(CSR_GP_CNTRL 0x%08x)\n", val); return false; } return true; } EXPORT_SYMBOL_GPL(iwl_grab_nic_access); void iwl_release_nic_access(struct iwl_trans *trans) { lockdep_assert_held(&trans->reg_lock); __iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* * Above we read the CSR_GP_CNTRL register, which will flush * any previous writes, but we need the write that clears the * MAC_ACCESS_REQ bit to be performed before any other writes * scheduled on different CPUs (after we drop reg_lock). */ mmiowb(); } EXPORT_SYMBOL_GPL(iwl_release_nic_access); u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) { u32 value; unsigned long flags; spin_lock_irqsave(&trans->reg_lock, flags); iwl_grab_nic_access(trans); value = iwl_read32(trans, reg); iwl_release_nic_access(trans); spin_unlock_irqrestore(&trans->reg_lock, flags); return value; } EXPORT_SYMBOL_GPL(iwl_read_direct32); void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) { unsigned long flags; spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { iwl_write32(trans, reg, value); iwl_release_nic_access(trans); } spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_write_direct32); int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, int timeout) { int t = 0; do { if ((iwl_read_direct32(trans, addr) & mask) == mask) return t; udelay(IWL_POLL_INTERVAL); t += IWL_POLL_INTERVAL; } while (t < timeout); return -ETIMEDOUT; } EXPORT_SYMBOL_GPL(iwl_poll_direct_bit); static inline u32 __iwl_read_prph(struct iwl_trans *trans, u32 reg) { iwl_write32(trans, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); return iwl_read32(trans, HBUS_TARG_PRPH_RDAT); } static inline void __iwl_write_prph(struct iwl_trans *trans, u32 addr, u32 val) { iwl_write32(trans, HBUS_TARG_PRPH_WADDR, ((addr & 0x0000FFFF) | (3 << 24))); iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); } u32 iwl_read_prph(struct iwl_trans *trans, u32 reg) { unsigned long flags; u32 val; spin_lock_irqsave(&trans->reg_lock, flags); iwl_grab_nic_access(trans); val = __iwl_read_prph(trans, reg); iwl_release_nic_access(trans); spin_unlock_irqrestore(&trans->reg_lock, flags); return val; } EXPORT_SYMBOL_GPL(iwl_read_prph); void iwl_write_prph(struct iwl_trans *trans, u32 addr, u32 val) { unsigned long flags; spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { __iwl_write_prph(trans, addr, val); iwl_release_nic_access(trans); } spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_write_prph); void iwl_set_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask) { unsigned long flags; spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { __iwl_write_prph(trans, reg, __iwl_read_prph(trans, reg) | mask); iwl_release_nic_access(trans); } spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_set_bits_prph); void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 reg, u32 bits, u32 mask) { unsigned long flags; spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { __iwl_write_prph(trans, reg, (__iwl_read_prph(trans, reg) & mask) | bits); iwl_release_nic_access(trans); } spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_set_bits_mask_prph); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask) { unsigned long flags; u32 val; spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { val = __iwl_read_prph(trans, reg); __iwl_write_prph(trans, reg, (val & ~mask)); iwl_release_nic_access(trans); } spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(iwl_clear_bits_prph); void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr, void *buf, int dwords) { unsigned long flags; int offs; u32 *vals = buf; spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr); for (offs = 0; offs < dwords; offs++) vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); iwl_release_nic_access(trans); } spin_unlock_irqrestore(&trans->reg_lock, flags); } EXPORT_SYMBOL_GPL(_iwl_read_targ_mem_dwords); u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr) { u32 value; _iwl_read_targ_mem_dwords(trans, addr, &value, 1); return value; } EXPORT_SYMBOL_GPL(iwl_read_targ_mem); int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr, void *buf, int dwords) { unsigned long flags; int offs, result = 0; u32 *vals = buf; spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); for (offs = 0; offs < dwords; offs++) iwl_write32(trans, HBUS_TARG_MEM_WDAT, vals[offs]); iwl_release_nic_access(trans); } else result = -EBUSY; spin_unlock_irqrestore(&trans->reg_lock, flags); return result; } EXPORT_SYMBOL_GPL(_iwl_write_targ_mem_dwords); int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val) { return _iwl_write_targ_mem_dwords(trans, addr, &val, 1); } EXPORT_SYMBOL_GPL(iwl_write_targ_mem); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-fw.h0000644000175000017500000001422712026211315023517 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_fw_h__ #define __iwl_fw_h__ #include #include /** * enum iwl_ucode_tlv_flag - ucode API flags * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously * was a separate TLV but moved here to save space. * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID, * treats good CRC threshold as a boolean * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. */ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_PAN = BIT(0), IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1), IWL_UCODE_TLV_FLAGS_MFP = BIT(2), IWL_UCODE_TLV_FLAGS_P2P = BIT(3), }; /* The default calibrate table size if not specified by firmware file */ #define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 #define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19 #define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253 /** * enum iwl_ucode_type * * The type of ucode. * * @IWL_UCODE_REGULAR: Normal runtime ucode * @IWL_UCODE_INIT: Initial ucode * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode */ enum iwl_ucode_type { IWL_UCODE_REGULAR, IWL_UCODE_INIT, IWL_UCODE_WOWLAN, IWL_UCODE_TYPE_MAX, }; /* * enumeration of ucode section. * This enumeration is used for legacy tlv style (before 16.0 uCode). */ enum iwl_ucode_sec { IWL_UCODE_SECTION_INST, IWL_UCODE_SECTION_DATA, }; /* * For 16.0 uCode and above, there is no differentiation between sections, * just an offset to the HW address. */ #define IWL_UCODE_SECTION_MAX 4 struct iwl_ucode_capabilities { u32 max_probe_length; u32 standard_phy_calibration_size; u32 flags; }; /* one for each uCode image (inst/data, init/runtime/wowlan) */ struct fw_desc { dma_addr_t p_addr; /* hardware address */ void *v_addr; /* software address */ u32 len; /* size in bytes */ u32 offset; /* offset in the device */ }; struct fw_img { struct fw_desc sec[IWL_UCODE_SECTION_MAX]; }; /* uCode version contains 4 values: Major/Minor/API/Serial */ #define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) #define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) #define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) #define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF) /** * struct iwl_fw - variables associated with the firmware * * @ucode_ver: ucode version from the ucode file * @fw_version: firmware version string * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan. * @ucode_capa: capabilities parsed from the ucode file. * @enhance_sensitivity_table: device can do enhanced sensitivity. * @init_evtlog_ptr: event log offset for init ucode. * @init_evtlog_size: event log size for init ucode. * @init_errlog_ptr: error log offfset for init ucode. * @inst_evtlog_ptr: event log offset for runtime ucode. * @inst_evtlog_size: event log size for runtime ucode. * @inst_errlog_ptr: error log offfset for runtime ucode. */ struct iwl_fw { u32 ucode_ver; char fw_version[ETHTOOL_BUSINFO_LEN]; /* ucode images */ struct fw_img img[IWL_UCODE_TYPE_MAX]; struct iwl_ucode_capabilities ucode_capa; bool enhance_sensitivity_table; u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; u64 default_calib[IWL_UCODE_TYPE_MAX]; u32 phy_config; bool mvm_fw; }; #endif /* __iwl_fw_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-fw-file.h0000644000175000017500000001261412026211315024432 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_fw_file_h__ #define __iwl_fw_file_h__ #include /* v1/v2 uCode file layout */ struct iwl_ucode_header { __le32 ver; /* major/minor/API/serial */ union { struct { __le32 inst_size; /* bytes of runtime code */ __le32 data_size; /* bytes of runtime data */ __le32 init_size; /* bytes of init code */ __le32 init_data_size; /* bytes of init data */ __le32 boot_size; /* bytes of bootstrap code */ u8 data[0]; /* in same order as sizes */ } v1; struct { __le32 build; /* build number */ __le32 inst_size; /* bytes of runtime code */ __le32 data_size; /* bytes of runtime data */ __le32 init_size; /* bytes of init code */ __le32 init_data_size; /* bytes of init data */ __le32 boot_size; /* bytes of bootstrap code */ u8 data[0]; /* in same order as sizes */ } v2; } u; }; /* * new TLV uCode file layout * * The new TLV file format contains TLVs, that each specify * some piece of data. */ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_INVALID = 0, /* unused */ IWL_UCODE_TLV_INST = 1, IWL_UCODE_TLV_DATA = 2, IWL_UCODE_TLV_INIT = 3, IWL_UCODE_TLV_INIT_DATA = 4, IWL_UCODE_TLV_BOOT = 5, IWL_UCODE_TLV_PROBE_MAX_LEN = 6, /* a u32 value */ IWL_UCODE_TLV_PAN = 7, IWL_UCODE_TLV_RUNT_EVTLOG_PTR = 8, IWL_UCODE_TLV_RUNT_EVTLOG_SIZE = 9, IWL_UCODE_TLV_RUNT_ERRLOG_PTR = 10, IWL_UCODE_TLV_INIT_EVTLOG_PTR = 11, IWL_UCODE_TLV_INIT_EVTLOG_SIZE = 12, IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13, IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14, IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15, IWL_UCODE_TLV_WOWLAN_INST = 16, IWL_UCODE_TLV_WOWLAN_DATA = 17, IWL_UCODE_TLV_FLAGS = 18, IWL_UCODE_TLV_SEC_RT = 19, IWL_UCODE_TLV_SEC_INIT = 20, IWL_UCODE_TLV_SEC_WOWLAN = 21, IWL_UCODE_TLV_DEF_CALIB = 22, IWL_UCODE_TLV_PHY_SKU = 23, }; struct iwl_ucode_tlv { __le32 type; /* see above */ __le32 length; /* not including type/length fields */ u8 data[0]; }; #define IWL_TLV_UCODE_MAGIC 0x0a4c5749 struct iwl_tlv_ucode_header { /* * The TLV style ucode header is distinguished from * the v1/v2 style header by first four bytes being * zero, as such is an invalid combination of * major/minor/API/serial versions. */ __le32 zero; __le32 magic; u8 human_readable[64]; __le32 ver; /* major/minor/API/serial */ __le32 build; __le64 ignore; /* * The data contained herein has a TLV layout, * see above for the TLV header and types. * Note that each TLV is padded to a length * that is a multiple of 4 for alignment. */ u8 data[0]; }; #endif /* __iwl_fw_file_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-fh.h0000644000175000017500000005264712026211315023510 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __iwl_fh_h__ #define __iwl_fh_h__ #include /****************************/ /* Flow Handler Definitions */ /****************************/ /** * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) * Addresses are offsets from device's PCI hardware base address. */ #define FH_MEM_LOWER_BOUND (0x1000) #define FH_MEM_UPPER_BOUND (0x2000) /** * Keep-Warm (KW) buffer base address. * * Driver must allocate a 4KByte buffer that is for keeping the * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency * DRAM access when doing Txing or Rxing. The dummy accesses prevent host * from going into a power-savings mode that would cause higher DRAM latency, * and possible data over/under-runs, before all Tx/Rx is complete. * * Driver loads FH_KW_MEM_ADDR_REG with the physical address (bits 35:4) * of the buffer, which must be 4K aligned. Once this is set up, the device * automatically invokes keep-warm accesses when normal accesses might not * be sufficient to maintain fast DRAM response. * * Bit fields: * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned */ #define FH_KW_MEM_ADDR_REG (FH_MEM_LOWER_BOUND + 0x97C) /** * TFD Circular Buffers Base (CBBC) addresses * * Device has 16 base pointer registers, one for each of 16 host-DRAM-resident * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04 * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte * aligned (address bits 0-7 must be 0). * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers * for them are in different places. * * Bit fields in each pointer register: * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned */ #define FH_MEM_CBBC_0_15_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) #define FH_MEM_CBBC_0_15_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10) #define FH_MEM_CBBC_16_19_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBF0) #define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) #define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20) #define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80) /* Find TFD CB base pointer for given queue */ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) { if (chnl < 16) return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl; if (chnl < 20) return FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16); WARN_ON_ONCE(chnl >= 32); return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20); } /** * Rx SRAM Control and Status Registers (RSCSR) * * These registers provide handshake between driver and device for the Rx queue * (this queue handles *all* command responses, notifications, Rx data, etc. * sent from uCode to host driver). Unlike Tx, there is only one Rx * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 * mapping between RBDs and RBs. * * Driver must allocate host DRAM memory for the following, and set the * physical address of each into device registers: * * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 * entries (although any power of 2, up to 4096, is selectable by driver). * Each entry (1 dword) points to a receive buffer (RB) of consistent size * (typically 4K, although 8K or 16K are also selectable by driver). * Driver sets up RB size and number of RBDs in the CB via Rx config * register FH_MEM_RCSR_CHNL0_CONFIG_REG. * * Bit fields within one RBD: * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned * * Driver sets physical address [35:8] of base of RBD circular buffer * into FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. * * 2) Rx status buffer, 8 bytes, in which uCode indicates which Rx Buffers * (RBs) have been filled, via a "write pointer", actually the index of * the RB's corresponding RBD within the circular buffer. Driver sets * physical address [35:4] into FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. * * Bit fields in lower dword of Rx status buffer (upper dword not used * by driver: * 31-12: Not used by driver * 11- 0: Index of last filled Rx buffer descriptor * (device writes, driver reads this value) * * As the driver prepares Receive Buffers (RBs) for device to fill, driver must * enter pointers to these RBs into contiguous RBD circular buffer entries, * and update the device's "write" index register, * FH_RSCSR_CHNL0_RBDCB_WPTR_REG. * * This "write" index corresponds to the *next* RBD that the driver will make * available, i.e. one RBD past the tail of the ready-to-fill RBDs within * the circular buffer. This value should initially be 0 (before preparing any * RBs), should be 8 after preparing the first 8 RBs (for example), and must * wrap back to 0 at the end of the circular buffer (but don't wrap before * "read" index has advanced past 1! See below). * NOTE: DEVICE EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8. * * As the device fills RBs (referenced from contiguous RBDs within the circular * buffer), it updates the Rx status buffer in host DRAM, 2) described above, * to tell the driver the index of the latest filled RBD. The driver must * read this "read" index from DRAM after receiving an Rx interrupt from device * * The driver must also internally keep track of a third index, which is the * next RBD to process. When receiving an Rx interrupt, driver should process * all filled but unprocessed RBs up to, but not including, the RB * corresponding to the "read" index. For example, if "read" index becomes "1", * driver may process the RB pointed to by RBD 0. Depending on volume of * traffic, there may be many RBs to process. * * If read index == write index, device thinks there is no room to put new data. * Due to this, the maximum number of filled RBs is 255, instead of 256. To * be safe, make sure that there is a gap of at least 2 RBDs between "write" * and "read" indexes; that is, make sure that there are no more than 254 * buffers waiting to be filled. */ #define FH_MEM_RSCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0) #define FH_MEM_RSCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) #define FH_MEM_RSCSR_CHNL0 (FH_MEM_RSCSR_LOWER_BOUND) /** * Physical base address of 8-byte Rx Status buffer. * Bit fields: * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. */ #define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0) /** * Physical base address of Rx Buffer Descriptor Circular Buffer. * Bit fields: * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. */ #define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004) /** * Rx write pointer (index, really!). * Bit fields: * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. * NOTE: For 256-entry circular buffer, use only bits [7:0]. */ #define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008) #define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG) /** * Rx Config/Status Registers (RCSR) * Rx Config Reg for channel 0 (only channel used) * * Driver must initialize FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for * normal operation (see bit fields). * * Clearing FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. * Driver should poll FH_MEM_RSSR_RX_STATUS_REG for * FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. * * Bit fields: * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, * '10' operate normally * 29-24: reserved * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), * min "5" for 32 RBDs, max "12" for 4096 RBDs. * 19-18: reserved * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, * '10' 12K, '11' 16K. * 15-14: reserved * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) * typical value 0x10 (about 1/2 msec) * 3- 0: reserved */ #define FH_MEM_RCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) #define FH_MEM_RCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xCC0) #define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND) #define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0) #define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ #define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ #define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ #define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ #define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ #define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31*/ #define FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) #define FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) #define RX_RB_TIMEOUT (0x10) #define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) #define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) #define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) #define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) #define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) #define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) #define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) #define FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) #define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) #define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) /** * Rx Shared Status Registers (RSSR) * * After stopping Rx DMA channel (writing 0 to * FH_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll * FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. * * Bit fields: * 24: 1 = Channel 0 is idle * * FH_MEM_RSSR_SHARED_CTRL_REG and FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV * contain default values that should not be altered by the driver. */ #define FH_MEM_RSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC40) #define FH_MEM_RSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) #define FH_MEM_RSSR_SHARED_CTRL_REG (FH_MEM_RSSR_LOWER_BOUND) #define FH_MEM_RSSR_RX_STATUS_REG (FH_MEM_RSSR_LOWER_BOUND + 0x004) #define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ (FH_MEM_RSSR_LOWER_BOUND + 0x008) #define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) #define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 /* TFDB Area - TFDs buffer table */ #define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) #define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900) #define FH_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958) #define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) #define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) /** * Transmit DMA Channel Control/Status Registers (TCSR) * * Device has one configuration register for each of 8 Tx DMA/FIFO channels * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. * * To use a Tx DMA channel, driver must initialize its * FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with: * * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL * * All other bits should be 0. * * Bit fields: * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, * '10' operate normally * 29- 4: Reserved, set to "0" * 3: Enable internal DMA requests (1, normal operation), disable (0) * 2- 0: Reserved, set to "0" */ #define FH_TCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) #define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60) /* Find Control/Status reg for given Tx DMA/FIFO channel */ #define FH_TCSR_CHNL_NUM (8) /* TCSR: tx_config register values */ #define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl)) #define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) #define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) #define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) #define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) #define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) #define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) #define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) #define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) #define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) #define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) #define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) #define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) #define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) #define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) #define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) #define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) #define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) #define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) #define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) #define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) /** * Tx Shared Status Registers (TSSR) * * After stopping Tx DMA channel (writing 0 to * FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll * FH_TSSR_TX_STATUS_REG until selected Tx channel is idle * (channel's buffers empty | no pending requests). * * Bit fields: * 31-24: 1 = Channel buffers empty (channel 7:0) * 23-16: 1 = No pending requests (channel 7:0) */ #define FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0) #define FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0) #define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010) /** * Bit fields for TSSR(Tx Shared Status & Control) error status register: * 31: Indicates an address error when accessed to internal memory * uCode/driver must write "1" in order to clear this flag * 30: Indicates that Host did not send the expected number of dwords to FH * uCode/driver must write "1" in order to clear this flag * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA * command was received from the scheduler while the TRB was already full * with previous command * uCode/driver must write "1" in order to clear this flag * 7-0: Each status bit indicates a channel's TxCredit error. When an error * bit is set, it indicates that the FH has received a full indication * from the RTC TxFIFO and the current value of the TxCredit counter was * not equal to zero. This mean that the credit mechanism was not * synchronized to the TxFIFO status * uCode/driver must write "1" in order to clear this flag */ #define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018) #define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) /* Tx service channels */ #define FH_SRVC_CHNL (9) #define FH_SRVC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9C8) #define FH_SRVC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) #define FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ (FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) #define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98) #define FH_TX_TRB_REG(_chan) (FH_MEM_LOWER_BOUND + 0x958 + (_chan) * 4) /* Instruct FH to increment the retry count of a packet when * it is brought from the memory to TX-FIFO */ #define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) #define RX_QUEUE_SIZE 256 #define RX_QUEUE_MASK 255 #define RX_QUEUE_SIZE_LOG 8 /* * RX related structures and functions */ #define RX_FREE_BUFFERS 64 #define RX_LOW_WATERMARK 8 /** * struct iwl_rb_status - reseve buffer status * host memory mapped FH registers * @closed_rb_num [0:11] - Indicates the index of the RB which was closed * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed * @finished_rb_num [0:11] - Indicates the index of the current RB * in which the last frame was written to * @finished_fr_num [0:11] - Indicates the index of the RX Frame * which was transferred */ struct iwl_rb_status { __le16 closed_rb_num; __le16 closed_fr_num; __le16 finished_rb_num; __le16 finished_fr_nam; __le32 __unused; } __packed; #define TFD_QUEUE_SIZE_MAX (256) #define TFD_QUEUE_SIZE_BC_DUP (64) #define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) #define IWL_TX_DMA_MASK DMA_BIT_MASK(36) #define IWL_NUM_OF_TBS 20 static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr) { return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; } /** * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor * * This structure contains dma address and length of transmission address * * @lo: low [31:0] portion of the dma address of TX buffer * every even is unaligned on 16 bit boundary * @hi_n_len 0-3 [35:32] portion of dma * 4-15 length of the tx buffer */ struct iwl_tfd_tb { __le32 lo; __le16 hi_n_len; } __packed; /** * struct iwl_tfd * * Transmit Frame Descriptor (TFD) * * @ __reserved1[3] reserved * @ num_tbs 0-4 number of active tbs * 5 reserved * 6-7 padding (not used) * @ tbs[20] transmit frame buffer descriptors * @ __pad padding * * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. * Both driver and device share these circular buffers, each of which must be * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes * * Driver must indicate the physical address of the base of each * circular buffer via the FH_MEM_CBBC_QUEUE registers. * * Each TFD contains pointer/size information for up to 20 data buffers * in host DRAM. These buffers collectively contain the (one) frame described * by the TFD. Each buffer must be a single contiguous block of memory within * itself, but buffers may be scattered in host DRAM. Each buffer has max size * of (4K - 4). The concatenates all of a TFD's buffers into a single * Tx frame, up to 8 KBytes in size. * * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. */ struct iwl_tfd { u8 __reserved1[3]; u8 num_tbs; struct iwl_tfd_tb tbs[IWL_NUM_OF_TBS]; __le32 __pad; } __packed; /* Keep Warm Size */ #define IWL_KW_SIZE 0x1000 /* 4k */ /* Fixed (non-configurable) rx data from phy */ /** * struct iwlagn_schedq_bc_tbl scheduler byte count table * base physical address provided by SCD_DRAM_BASE_ADDR * @tfd_offset 0-12 - tx command byte count * 12-16 - station index */ struct iwlagn_scd_bc_tbl { __le16 tfd_offset[TFD_QUEUE_BC_SIZE]; } __packed; #endif /* !__iwl_fh_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h0000644000175000017500000000607012026211315025300 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_eeprom_h__ #define __iwl_eeprom_h__ #include "iwl-trans.h" int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size); #endif /* __iwl_eeprom_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c0000644000175000017500000003265212026211315025300 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include #include #include #include "iwl-debug.h" #include "iwl-eeprom-read.h" #include "iwl-io.h" #include "iwl-prph.h" #include "iwl-csr.h" /* * EEPROM access time values: * * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. */ #define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ #define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ #define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ /* * The device's EEPROM semaphore prevents conflicts between driver and uCode * when accessing the EEPROM; each access is a series of pulses to/from the * EEPROM chip, not a single event, so even reads could conflict if they * weren't arbitrated by the semaphore. */ #define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ #define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) { u16 count; int ret; for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { /* Request semaphore */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); /* See if we got it */ ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, EEPROM_SEM_TIMEOUT); if (ret >= 0) { IWL_DEBUG_EEPROM(trans->dev, "Acquired semaphore after %d tries.\n", count+1); return ret; } } return ret; } static void iwl_eeprom_release_semaphore(struct iwl_trans *trans) { iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); } static int iwl_eeprom_verify_signature(struct iwl_trans *trans, bool nvm_is_otp) { u32 gp = iwl_read32(trans, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; IWL_DEBUG_EEPROM(trans->dev, "EEPROM signature=0x%08x\n", gp); switch (gp) { case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: if (!nvm_is_otp) { IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n", gp); return -ENOENT; } return 0; case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: if (nvm_is_otp) { IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp); return -ENOENT; } return 0; case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: default: IWL_ERR(trans, "bad EEPROM/OTP signature, type=%s, EEPROM_GP=0x%08x\n", nvm_is_otp ? "OTP" : "EEPROM", gp); return -ENOENT; } } /****************************************************************************** * * OTP related functions * ******************************************************************************/ static void iwl_set_otp_access_absolute(struct iwl_trans *trans) { iwl_read32(trans, CSR_OTP_GP_REG); iwl_clear_bit(trans, CSR_OTP_GP_REG, CSR_OTP_GP_REG_OTP_ACCESS_MODE); } static int iwl_nvm_is_otp(struct iwl_trans *trans) { u32 otpgp; /* OTP only valid for CP/PP and after */ switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) { case CSR_HW_REV_TYPE_NONE: IWL_ERR(trans, "Unknown hardware type\n"); return -EIO; case CSR_HW_REV_TYPE_5300: case CSR_HW_REV_TYPE_5350: case CSR_HW_REV_TYPE_5100: case CSR_HW_REV_TYPE_5150: return 0; default: otpgp = iwl_read32(trans, CSR_OTP_GP_REG); if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) return 1; return 0; } } static int iwl_init_otp_access(struct iwl_trans *trans) { int ret; /* Enable 40MHz radio clock */ iwl_write32(trans, CSR_GP_CNTRL, iwl_read32(trans, CSR_GP_CNTRL) | CSR_GP_CNTRL_REG_FLAG_INIT_DONE); /* wait for clock to be ready */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (ret < 0) { IWL_ERR(trans, "Time out access OTP\n"); } else { iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); udelay(5); iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); /* * CSR auto clock gate disable bit - * this is only applicable for HW with OTP shadow RAM */ if (trans->cfg->base_params->shadow_ram_support) iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); } return ret; } static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, __le16 *eeprom_data) { int ret = 0; u32 r; u32 otpgp; iwl_write32(trans, CSR_EEPROM_REG, CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); ret = iwl_poll_bit(trans, CSR_EEPROM_REG, CSR_EEPROM_REG_READ_VALID_MSK, CSR_EEPROM_REG_READ_VALID_MSK, IWL_EEPROM_ACCESS_TIMEOUT); if (ret < 0) { IWL_ERR(trans, "Time out reading OTP[%d]\n", addr); return ret; } r = iwl_read32(trans, CSR_EEPROM_REG); /* check for ECC errors: */ otpgp = iwl_read32(trans, CSR_OTP_GP_REG); if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { /* stop in this case */ /* set the uncorrectable OTP ECC bit for acknowledgement */ iwl_set_bit(trans, CSR_OTP_GP_REG, CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n"); return -EINVAL; } if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { /* continue in this case */ /* set the correctable OTP ECC bit for acknowledgement */ iwl_set_bit(trans, CSR_OTP_GP_REG, CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); IWL_ERR(trans, "Correctable OTP ECC error, continue read\n"); } *eeprom_data = cpu_to_le16(r >> 16); return 0; } /* * iwl_is_otp_empty: check for empty OTP */ static bool iwl_is_otp_empty(struct iwl_trans *trans) { u16 next_link_addr = 0; __le16 link_value; bool is_empty = false; /* locate the beginning of OTP link list */ if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) { if (!link_value) { IWL_ERR(trans, "OTP is empty\n"); is_empty = true; } } else { IWL_ERR(trans, "Unable to read first block of OTP list.\n"); is_empty = true; } return is_empty; } /* * iwl_find_otp_image: find EEPROM image in OTP * finding the OTP block that contains the EEPROM image. * the last valid block on the link list (the block _before_ the last block) * is the block we should read and used to configure the device. * If all the available OTP blocks are full, the last block will be the block * we should read and used to configure the device. * only perform this operation if shadow RAM is disabled */ static int iwl_find_otp_image(struct iwl_trans *trans, u16 *validblockaddr) { u16 next_link_addr = 0, valid_addr; __le16 link_value = 0; int usedblocks = 0; /* set addressing mode to absolute to traverse the link list */ iwl_set_otp_access_absolute(trans); /* checking for empty OTP or error */ if (iwl_is_otp_empty(trans)) return -EINVAL; /* * start traverse link list * until reach the max number of OTP blocks * different devices have different number of OTP blocks */ do { /* save current valid block address * check for more block on the link list */ valid_addr = next_link_addr; next_link_addr = le16_to_cpu(link_value) * sizeof(u16); IWL_DEBUG_EEPROM(trans->dev, "OTP blocks %d addr 0x%x\n", usedblocks, next_link_addr); if (iwl_read_otp_word(trans, next_link_addr, &link_value)) return -EINVAL; if (!link_value) { /* * reach the end of link list, return success and * set address point to the starting address * of the image */ *validblockaddr = valid_addr; /* skip first 2 bytes (link list pointer) */ *validblockaddr += 2; return 0; } /* more in the link list, continue */ usedblocks++; } while (usedblocks <= trans->cfg->base_params->max_ll_items); /* OTP has no valid blocks */ IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n"); return -EINVAL; } /** * iwl_read_eeprom - read EEPROM contents * * Load the EEPROM contents from adapter and return it * and its size. * * NOTE: This routine uses the non-debug IO access functions. */ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) { __le16 *e; u32 gp = iwl_read32(trans, CSR_EEPROM_GP); int sz; int ret; u16 addr; u16 validblockaddr = 0; u16 cache_addr = 0; int nvm_is_otp; if (!eeprom || !eeprom_size) return -EINVAL; nvm_is_otp = iwl_nvm_is_otp(trans); if (nvm_is_otp < 0) return nvm_is_otp; sz = trans->cfg->base_params->eeprom_size; IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz); e = kmalloc(sz, GFP_KERNEL); if (!e) return -ENOMEM; ret = iwl_eeprom_verify_signature(trans, nvm_is_otp); if (ret < 0) { IWL_ERR(trans, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); goto err_free; } /* Make sure driver (instead of uCode) is allowed to read EEPROM */ ret = iwl_eeprom_acquire_semaphore(trans); if (ret < 0) { IWL_ERR(trans, "Failed to acquire EEPROM semaphore.\n"); goto err_free; } if (nvm_is_otp) { ret = iwl_init_otp_access(trans); if (ret) { IWL_ERR(trans, "Failed to initialize OTP access.\n"); goto err_unlock; } iwl_write32(trans, CSR_EEPROM_GP, iwl_read32(trans, CSR_EEPROM_GP) & ~CSR_EEPROM_GP_IF_OWNER_MSK); iwl_set_bit(trans, CSR_OTP_GP_REG, CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); /* traversing the linked list if no shadow ram supported */ if (!trans->cfg->base_params->shadow_ram_support) { ret = iwl_find_otp_image(trans, &validblockaddr); if (ret) goto err_unlock; } for (addr = validblockaddr; addr < validblockaddr + sz; addr += sizeof(u16)) { __le16 eeprom_data; ret = iwl_read_otp_word(trans, addr, &eeprom_data); if (ret) goto err_unlock; e[cache_addr / 2] = eeprom_data; cache_addr += sizeof(u16); } } else { /* eeprom is an array of 16bit values */ for (addr = 0; addr < sz; addr += sizeof(u16)) { u32 r; iwl_write32(trans, CSR_EEPROM_REG, CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); ret = iwl_poll_bit(trans, CSR_EEPROM_REG, CSR_EEPROM_REG_READ_VALID_MSK, CSR_EEPROM_REG_READ_VALID_MSK, IWL_EEPROM_ACCESS_TIMEOUT); if (ret < 0) { IWL_ERR(trans, "Time out reading EEPROM[%d]\n", addr); goto err_unlock; } r = iwl_read32(trans, CSR_EEPROM_REG); e[addr / 2] = cpu_to_le16(r >> 16); } } IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n", nvm_is_otp ? "OTP" : "EEPROM"); iwl_eeprom_release_semaphore(trans); *eeprom_size = sz; *eeprom = (u8 *)e; return 0; err_unlock: iwl_eeprom_release_semaphore(trans); err_free: kfree(e); return ret; } EXPORT_SYMBOL_GPL(iwl_read_eeprom); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h0000644000175000017500000001207712026211315025503 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_eeprom_parse_h__ #define __iwl_eeprom_parse_h__ #include #include #include "iwl-trans.h" /* SKU Capabilities (actual values from EEPROM definition) */ #define EEPROM_SKU_CAP_BAND_24GHZ (1 << 4) #define EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) #define EEPROM_SKU_CAP_11N_ENABLE (1 << 6) #define EEPROM_SKU_CAP_AMT_ENABLE (1 << 7) #define EEPROM_SKU_CAP_IPAN_ENABLE (1 << 8) /* radio config bits (actual values from EEPROM definition) */ #define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ #define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ #define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ #define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ #define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ #define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ struct iwl_eeprom_data { int n_hw_addrs; u8 hw_addr[ETH_ALEN]; u8 calib_version; __le16 calib_voltage; __le16 raw_temperature; __le16 kelvin_temperature; __le16 kelvin_voltage; __le16 xtal_calib[2]; u16 sku; u16 radio_cfg; u16 eeprom_version; s8 max_tx_pwr_half_dbm; u8 valid_tx_ant, valid_rx_ant; struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; struct ieee80211_channel channels[]; }; /** * iwl_parse_eeprom_data - parse EEPROM data and return values * * @dev: device pointer we're parsing for, for debug only * @cfg: device configuration for parsing and overrides * @eeprom: the EEPROM data * @eeprom_size: length of the EEPROM data * * This function parses all EEPROM values we need and then * returns a (newly allocated) struct containing all the * relevant values for driver use. The struct must be freed * later with iwl_free_eeprom_data(). */ struct iwl_eeprom_data * iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, const u8 *eeprom, size_t eeprom_size); /** * iwl_free_eeprom_data - free EEPROM data * @data: the data to free */ static inline void iwl_free_eeprom_data(struct iwl_eeprom_data *data) { kfree(data); } int iwl_eeprom_check_version(struct iwl_eeprom_data *data, struct iwl_trans *trans); #endif /* __iwl_eeprom_parse_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c0000644000175000017500000006752012026211315025501 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include #include #include #include "iwl-modparams.h" #include "iwl-eeprom-parse.h" /* EEPROM offset definitions */ /* indirect access definitions */ #define ADDRESS_MSK 0x0000FFFF #define INDIRECT_TYPE_MSK 0x000F0000 #define INDIRECT_HOST 0x00010000 #define INDIRECT_GENERAL 0x00020000 #define INDIRECT_REGULATORY 0x00030000 #define INDIRECT_CALIBRATION 0x00040000 #define INDIRECT_PROCESS_ADJST 0x00050000 #define INDIRECT_OTHERS 0x00060000 #define INDIRECT_TXP_LIMIT 0x00070000 #define INDIRECT_TXP_LIMIT_SIZE 0x00080000 #define INDIRECT_ADDRESS 0x00100000 /* corresponding link offsets in EEPROM */ #define EEPROM_LINK_HOST (2*0x64) #define EEPROM_LINK_GENERAL (2*0x65) #define EEPROM_LINK_REGULATORY (2*0x66) #define EEPROM_LINK_CALIBRATION (2*0x67) #define EEPROM_LINK_PROCESS_ADJST (2*0x68) #define EEPROM_LINK_OTHERS (2*0x69) #define EEPROM_LINK_TXP_LIMIT (2*0x6a) #define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) /* General */ #define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ #define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ #define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ #define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ #define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ #define EEPROM_VERSION (2*0x44) /* 2 bytes */ #define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ #define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ #define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ #define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ /* calibration */ struct iwl_eeprom_calib_hdr { u8 version; u8 pa_type; __le16 voltage; } __packed; #define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) #define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) /* temperature */ #define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) #define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) /* * EEPROM bands * These are the channel numbers from each band in the order * that they are stored in the EEPROM band information. Note * that EEPROM bands aren't the same as mac80211 bands, and * there are even special "ht40 bands" in the EEPROM. */ static const u8 iwl_eeprom_band_1[14] = { /* 2.4 GHz */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 }; static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 }; static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 }; static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ 145, 149, 153, 157, 161, 165 }; static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ 1, 2, 3, 4, 5, 6, 7 }; static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 }; #define IWL_NUM_CHANNELS (ARRAY_SIZE(iwl_eeprom_band_1) + \ ARRAY_SIZE(iwl_eeprom_band_2) + \ ARRAY_SIZE(iwl_eeprom_band_3) + \ ARRAY_SIZE(iwl_eeprom_band_4) + \ ARRAY_SIZE(iwl_eeprom_band_5)) /* rate data (static) */ static struct ieee80211_rate iwl_cfg80211_rates[] = { { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, }; #define RATES_24_OFFS 0 #define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) #define RATES_52_OFFS 4 #define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) /* EEPROM reading functions */ static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset) { if (WARN_ON(offset + sizeof(u16) > eeprom_size)) return 0; return le16_to_cpup((__le16 *)(eeprom + offset)); } static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size, u32 address) { u16 offset = 0; if ((address & INDIRECT_ADDRESS) == 0) return address; switch (address & INDIRECT_TYPE_MSK) { case INDIRECT_HOST: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_HOST); break; case INDIRECT_GENERAL: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_GENERAL); break; case INDIRECT_REGULATORY: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_REGULATORY); break; case INDIRECT_TXP_LIMIT: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_TXP_LIMIT); break; case INDIRECT_TXP_LIMIT_SIZE: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_TXP_LIMIT_SIZE); break; case INDIRECT_CALIBRATION: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_CALIBRATION); break; case INDIRECT_PROCESS_ADJST: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_PROCESS_ADJST); break; case INDIRECT_OTHERS: offset = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_LINK_OTHERS); break; default: WARN_ON(1); break; } /* translate the offset from words to byte */ return (address & ADDRESS_MSK) + (offset << 1); } static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, u32 offset) { u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset); if (WARN_ON(address >= eeprom_size)) return NULL; return &eeprom[address]; } static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size, struct iwl_eeprom_data *data) { struct iwl_eeprom_calib_hdr *hdr; hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_CALIB_ALL); if (!hdr) return -ENODATA; data->calib_version = hdr->version; data->calib_voltage = hdr->voltage; return 0; } /** * enum iwl_eeprom_channel_flags - channel flags in EEPROM * @EEPROM_CHANNEL_VALID: channel is usable for this SKU/geo * @EEPROM_CHANNEL_IBSS: usable as an IBSS channel * @EEPROM_CHANNEL_ACTIVE: active scanning allowed * @EEPROM_CHANNEL_RADAR: radar detection required * @EEPROM_CHANNEL_WIDE: 20 MHz channel okay (?) * @EEPROM_CHANNEL_DFS: dynamic freq selection candidate */ enum iwl_eeprom_channel_flags { EEPROM_CHANNEL_VALID = BIT(0), EEPROM_CHANNEL_IBSS = BIT(1), EEPROM_CHANNEL_ACTIVE = BIT(3), EEPROM_CHANNEL_RADAR = BIT(4), EEPROM_CHANNEL_WIDE = BIT(5), EEPROM_CHANNEL_DFS = BIT(7), }; /** * struct iwl_eeprom_channel - EEPROM channel data * @flags: %EEPROM_CHANNEL_* flags * @max_power_avg: max power (in dBm) on this channel, at most 31 dBm */ struct iwl_eeprom_channel { u8 flags; s8 max_power_avg; } __packed; enum iwl_eeprom_enhanced_txpwr_flags { IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), }; /** * iwl_eeprom_enhanced_txpwr structure * @flags: entry flags * @channel: channel number * @chain_a_max_pwr: chain a max power in 1/2 dBm * @chain_b_max_pwr: chain b max power in 1/2 dBm * @chain_c_max_pwr: chain c max power in 1/2 dBm * @delta_20_in_40: 20-in-40 deltas (hi/lo) * @mimo2_max_pwr: mimo2 max power in 1/2 dBm * @mimo3_max_pwr: mimo3 max power in 1/2 dBm * * This structure presents the enhanced regulatory tx power limit layout * in an EEPROM image. */ struct iwl_eeprom_enhanced_txpwr { u8 flags; u8 channel; s8 chain_a_max; s8 chain_b_max; s8 chain_c_max; u8 delta_20_in_40; s8 mimo2_max; s8 mimo3_max; } __packed; static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_eeprom_data *data, struct iwl_eeprom_enhanced_txpwr *txp) { s8 result = 0; /* (.5 dBm) */ /* Take the highest tx power from any valid chains */ if (data->valid_tx_ant & ANT_A && txp->chain_a_max > result) result = txp->chain_a_max; if (data->valid_tx_ant & ANT_B && txp->chain_b_max > result) result = txp->chain_b_max; if (data->valid_tx_ant & ANT_C && txp->chain_c_max > result) result = txp->chain_c_max; if ((data->valid_tx_ant == ANT_AB || data->valid_tx_ant == ANT_BC || data->valid_tx_ant == ANT_AC) && txp->mimo2_max > result) result = txp->mimo2_max; if (data->valid_tx_ant == ANT_ABC && txp->mimo3_max > result) result = txp->mimo3_max; return result; } #define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) #define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) #define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) #define TXP_CHECK_AND_PRINT(x) \ ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) ? # x " " : "") static void iwl_eeprom_enh_txp_read_element(struct iwl_eeprom_data *data, struct iwl_eeprom_enhanced_txpwr *txp, int n_channels, s8 max_txpower_avg) { int ch_idx; enum ieee80211_band band; band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; for (ch_idx = 0; ch_idx < n_channels; ch_idx++) { struct ieee80211_channel *chan = &data->channels[ch_idx]; /* update matching channel or from common data only */ if (txp->channel != 0 && chan->hw_value != txp->channel) continue; /* update matching band only */ if (band != chan->band) continue; if (chan->max_power < max_txpower_avg && !(txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ)) chan->max_power = max_txpower_avg; } } static void iwl_eeprom_enhanced_txpower(struct device *dev, struct iwl_eeprom_data *data, const u8 *eeprom, size_t eeprom_size, int n_channels) { struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; int idx, entries; __le16 *txp_len; s8 max_txp_avg_halfdbm; BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); /* the length is in 16-bit words, but we want entries */ txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_TXP_SZ_OFFS); entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_TXP_OFFS); for (idx = 0; idx < entries; idx++) { txp = &txp_array[idx]; /* skip invalid entries */ if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) continue; IWL_DEBUG_EEPROM(dev, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", (txp->channel && (txp->flags & IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? "Common " : (txp->channel) ? "Channel" : "Common", (txp->channel), TXP_CHECK_AND_PRINT(VALID), TXP_CHECK_AND_PRINT(BAND_52G), TXP_CHECK_AND_PRINT(OFDM), TXP_CHECK_AND_PRINT(40MHZ), TXP_CHECK_AND_PRINT(HT_AP), TXP_CHECK_AND_PRINT(RES1), TXP_CHECK_AND_PRINT(RES2), TXP_CHECK_AND_PRINT(COMMON_TYPE), txp->flags); IWL_DEBUG_EEPROM(dev, "\t\t chain_A: 0x%02x chain_B: 0X%02x chain_C: 0X%02x\n", txp->chain_a_max, txp->chain_b_max, txp->chain_c_max); IWL_DEBUG_EEPROM(dev, "\t\t MIMO2: 0x%02x MIMO3: 0x%02x High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n", txp->mimo2_max, txp->mimo3_max, ((txp->delta_20_in_40 & 0xf0) >> 4), (txp->delta_20_in_40 & 0x0f)); max_txp_avg_halfdbm = iwl_get_max_txpwr_half_dbm(data, txp); iwl_eeprom_enh_txp_read_element(data, txp, n_channels, DIV_ROUND_UP(max_txp_avg_halfdbm, 2)); if (max_txp_avg_halfdbm > data->max_tx_pwr_half_dbm) data->max_tx_pwr_half_dbm = max_txp_avg_halfdbm; } } static void iwl_init_band_reference(const struct iwl_cfg *cfg, const u8 *eeprom, size_t eeprom_size, int eeprom_band, int *eeprom_ch_count, const struct iwl_eeprom_channel **ch_info, const u8 **eeprom_ch_array) { u32 offset = cfg->eeprom_params->regulatory_bands[eeprom_band - 1]; offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY; *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset); switch (eeprom_band) { case 1: /* 2.4GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); *eeprom_ch_array = iwl_eeprom_band_1; break; case 2: /* 4.9GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); *eeprom_ch_array = iwl_eeprom_band_2; break; case 3: /* 5.2GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); *eeprom_ch_array = iwl_eeprom_band_3; break; case 4: /* 5.5GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); *eeprom_ch_array = iwl_eeprom_band_4; break; case 5: /* 5.7GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); *eeprom_ch_array = iwl_eeprom_band_5; break; case 6: /* 2.4GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); *eeprom_ch_array = iwl_eeprom_band_6; break; case 7: /* 5 GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); *eeprom_ch_array = iwl_eeprom_band_7; break; default: *eeprom_ch_count = 0; *eeprom_ch_array = NULL; WARN_ON(1); } } #define CHECK_AND_PRINT(x) \ ((eeprom_ch->flags & EEPROM_CHANNEL_##x) ? # x " " : "") static void iwl_mod_ht40_chan_info(struct device *dev, struct iwl_eeprom_data *data, int n_channels, enum ieee80211_band band, u16 channel, const struct iwl_eeprom_channel *eeprom_ch, u8 clear_ht40_extension_channel) { struct ieee80211_channel *chan = NULL; int i; for (i = 0; i < n_channels; i++) { if (data->channels[i].band != band) continue; if (data->channels[i].hw_value != channel) continue; chan = &data->channels[i]; break; } if (!chan) return; IWL_DEBUG_EEPROM(dev, "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", channel, band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE), CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE), CHECK_AND_PRINT(DFS), eeprom_ch->flags, eeprom_ch->max_power_avg, ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) chan->flags &= ~clear_ht40_extension_channel; } #define CHECK_AND_PRINT_I(x) \ ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "") static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, struct iwl_eeprom_data *data, const u8 *eeprom, size_t eeprom_size) { int band, ch_idx; const struct iwl_eeprom_channel *eeprom_ch_info; const u8 *eeprom_ch_array; int eeprom_ch_count; int n_channels = 0; /* * Loop through the 5 EEPROM bands and add them to the parse list */ for (band = 1; band <= 5; band++) { struct ieee80211_channel *channel; iwl_init_band_reference(cfg, eeprom, eeprom_size, band, &eeprom_ch_count, &eeprom_ch_info, &eeprom_ch_array); /* Loop through each band adding each of the channels */ for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { const struct iwl_eeprom_channel *eeprom_ch; eeprom_ch = &eeprom_ch_info[ch_idx]; if (!(eeprom_ch->flags & EEPROM_CHANNEL_VALID)) { IWL_DEBUG_EEPROM(dev, "Ch. %d Flags %x [%sGHz] - No traffic\n", eeprom_ch_array[ch_idx], eeprom_ch_info[ch_idx].flags, (band != 1) ? "5.2" : "2.4"); continue; } channel = &data->channels[n_channels]; n_channels++; channel->hw_value = eeprom_ch_array[ch_idx]; channel->band = (band == 1) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; channel->center_freq = ieee80211_channel_to_frequency( channel->hw_value, channel->band); /* set no-HT40, will enable as appropriate later */ channel->flags = IEEE80211_CHAN_NO_HT40; if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS)) channel->flags |= IEEE80211_CHAN_NO_IBSS; if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE)) channel->flags |= IEEE80211_CHAN_PASSIVE_SCAN; if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR) channel->flags |= IEEE80211_CHAN_RADAR; /* Initialize regulatory-based run-time data */ channel->max_power = eeprom_ch_info[ch_idx].max_power_avg; IWL_DEBUG_EEPROM(dev, "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", channel->hw_value, (band != 1) ? "5.2" : "2.4", CHECK_AND_PRINT_I(VALID), CHECK_AND_PRINT_I(IBSS), CHECK_AND_PRINT_I(ACTIVE), CHECK_AND_PRINT_I(RADAR), CHECK_AND_PRINT_I(WIDE), CHECK_AND_PRINT_I(DFS), eeprom_ch_info[ch_idx].flags, eeprom_ch_info[ch_idx].max_power_avg, ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_IBSS) && !(eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); } } if (cfg->eeprom_params->enhanced_txpower) { /* * for newer device (6000 series and up) * EEPROM contain enhanced tx power information * driver need to process addition information * to determine the max channel tx power limits */ iwl_eeprom_enhanced_txpower(dev, data, eeprom, eeprom_size, n_channels); } else { /* All others use data from channel map */ int i; data->max_tx_pwr_half_dbm = -128; for (i = 0; i < n_channels; i++) data->max_tx_pwr_half_dbm = max_t(s8, data->max_tx_pwr_half_dbm, data->channels[i].max_power * 2); } /* Check if we do have HT40 channels */ if (cfg->eeprom_params->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 && cfg->eeprom_params->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) return n_channels; /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ for (band = 6; band <= 7; band++) { enum ieee80211_band ieeeband; iwl_init_band_reference(cfg, eeprom, eeprom_size, band, &eeprom_ch_count, &eeprom_ch_info, &eeprom_ch_array); /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ ieeeband = (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; /* Loop through each band adding each of the channels */ for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { /* Set up driver's info for lower half */ iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, eeprom_ch_array[ch_idx], &eeprom_ch_info[ch_idx], IEEE80211_CHAN_NO_HT40PLUS); /* Set up driver's info for upper half */ iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, eeprom_ch_array[ch_idx] + 4, &eeprom_ch_info[ch_idx], IEEE80211_CHAN_NO_HT40MINUS); } } return n_channels; } static int iwl_init_sband_channels(struct iwl_eeprom_data *data, struct ieee80211_supported_band *sband, int n_channels, enum ieee80211_band band) { struct ieee80211_channel *chan = &data->channels[0]; int n = 0, idx = 0; while (chan->band != band && idx < n_channels) chan = &data->channels[++idx]; sband->channels = &data->channels[idx]; while (chan->band == band && idx < n_channels) { chan = &data->channels[++idx]; n++; } sband->n_channels = n; return n; } #define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ #define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ static void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, struct iwl_eeprom_data *data, struct ieee80211_sta_ht_cap *ht_info, enum ieee80211_band band) { int max_bit_rate = 0; u8 rx_chains; u8 tx_chains; tx_chains = hweight8(data->valid_tx_ant); if (cfg->rx_with_siso_diversity) rx_chains = 1; else rx_chains = hweight8(data->valid_rx_ant); if (!(data->sku & EEPROM_SKU_CAP_11N_ENABLE) || !cfg->ht_params) { ht_info->ht_supported = false; return; } ht_info->ht_supported = true; ht_info->cap = 0; if (iwlwifi_mod_params.amsdu_size_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; ht_info->mcs.rx_mask[0] = 0xFF; if (rx_chains >= 2) ht_info->mcs.rx_mask[1] = 0xFF; if (rx_chains >= 3) ht_info->mcs.rx_mask[2] = 0xFF; if (cfg->ht_params->ht_greenfield_support) ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; max_bit_rate = MAX_BIT_RATE_20_MHZ; if (cfg->ht_params->ht40_bands & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; ht_info->mcs.rx_mask[4] = 0x01; max_bit_rate = MAX_BIT_RATE_40_MHZ; } /* Highest supported Rx data rate */ max_bit_rate *= rx_chains; WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); /* Tx MCS capabilities */ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; if (tx_chains != rx_chains) { ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((tx_chains - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); } } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_eeprom_data *data, const u8 *eeprom, size_t eeprom_size) { int n_channels = iwl_init_channel_map(dev, cfg, data, eeprom, eeprom_size); int n_used = 0; struct ieee80211_supported_band *sband; sband = &data->bands[IEEE80211_BAND_2GHZ]; sband->band = IEEE80211_BAND_2GHZ; sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; sband->n_bitrates = N_RATES_24; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_2GHZ); iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); sband = &data->bands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; sband->n_bitrates = N_RATES_52; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); if (n_channels != n_used) IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", n_used, n_channels); } /* EEPROM data functions */ struct iwl_eeprom_data * iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, const u8 *eeprom, size_t eeprom_size) { struct iwl_eeprom_data *data; const void *tmp; if (WARN_ON(!cfg || !cfg->eeprom_params)) return NULL; data = kzalloc(sizeof(*data) + sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, GFP_KERNEL); if (!data) return NULL; /* get MAC address(es) */ tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_MAC_ADDRESS); if (!tmp) goto err_free; memcpy(data->hw_addr, tmp, ETH_ALEN); data->n_hw_addrs = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_NUM_MAC_ADDRESS); if (iwl_eeprom_read_calib(eeprom, eeprom_size, data)) goto err_free; tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_XTAL); if (!tmp) goto err_free; memcpy(data->xtal_calib, tmp, sizeof(data->xtal_calib)); tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_RAW_TEMPERATURE); if (!tmp) goto err_free; data->raw_temperature = *(__le16 *)tmp; tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_KELVIN_TEMPERATURE); if (!tmp) goto err_free; data->kelvin_temperature = *(__le16 *)tmp; data->kelvin_voltage = *((__le16 *)tmp + 1); data->radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_RADIO_CONFIG); data->sku = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_SKU_CAP); if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) data->sku &= ~EEPROM_SKU_CAP_11N_ENABLE; data->eeprom_version = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_VERSION); data->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(data->radio_cfg); data->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(data->radio_cfg); /* check overrides (some devices have wrong EEPROM) */ if (cfg->valid_tx_ant) data->valid_tx_ant = cfg->valid_tx_ant; if (cfg->valid_rx_ant) data->valid_rx_ant = cfg->valid_rx_ant; if (!data->valid_tx_ant || !data->valid_rx_ant) { IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n", data->valid_tx_ant, data->valid_rx_ant); goto err_free; } iwl_init_sbands(dev, cfg, data, eeprom, eeprom_size); return data; err_free: kfree(data); return NULL; } EXPORT_SYMBOL_GPL(iwl_parse_eeprom_data); /* helper functions */ int iwl_eeprom_check_version(struct iwl_eeprom_data *data, struct iwl_trans *trans) { if (data->eeprom_version >= trans->cfg->eeprom_ver || data->calib_version >= trans->cfg->eeprom_calib_ver) { IWL_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", data->eeprom_version, data->calib_version); return 0; } IWL_ERR(trans, "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n", data->eeprom_version, trans->cfg->eeprom_ver, data->calib_version, trans->cfg->eeprom_calib_ver); return -EINVAL; } EXPORT_SYMBOL_GPL(iwl_eeprom_check_version); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-drv.h0000644000175000017500000001214712026211315023675 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_drv_h__ #define __iwl_drv_h__ /* for all modules */ #define DRV_NAME "iwlwifi" #define IWLWIFI_VERSION "in-tree:" #define DRV_COPYRIGHT "Copyright(c) 2003-2012 Intel Corporation" #define DRV_AUTHOR "" /** * DOC: Driver system flows - drv component * * This component implements the system flows such as bus enumeration, bus * removal. Bus dependent parts of system flows (such as iwl_pci_probe) are in * bus specific files (transport files). This is the code that is common among * different buses. * * This component is also in charge of managing the several implementations of * the wifi flows: it will allow to have several fw API implementation. These * different implementations will differ in the way they implement mac80211's * handlers too. * The init flow wrt to the drv component looks like this: * 1) The bus specific component is called from module_init * 2) The bus specific component registers the bus driver * 3) The bus driver calls the probe function * 4) The bus specific component configures the bus * 5) The bus specific component calls to the drv bus agnostic part * (iwl_drv_start) * 6) iwl_drv_start fetches the fw ASYNC, iwl_req_fw_callback * 7) iwl_req_fw_callback parses the fw file * 8) iwl_req_fw_callback starts the wifi implementation to matches the fw */ struct iwl_drv; struct iwl_trans; struct iwl_cfg; /** * iwl_drv_start - start the drv * * @trans_ops: the ops of the transport * @cfg: device specific constants / virtual functions * * starts the driver: fetches the firmware. This should be called by bus * specific system flows implementations. For example, the bus specific probe * function should do bus related operations only, and then call to this * function. It returns the driver object or %NULL if an error occured. */ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, const struct iwl_cfg *cfg); /** * iwl_drv_stop - stop the drv * * @drv: * * Stop the driver. This should be called by bus specific system flows * implementations. For example, the bus specific remove function should first * call this function and then do the bus related operations only. */ void iwl_drv_stop(struct iwl_drv *drv); #endif /* __iwl_drv_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-drv.c0000644000175000017500000010745512026211315023677 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #include #include #include #include #include "iwl-drv.h" #include "iwl-debug.h" #include "iwl-trans.h" #include "iwl-op-mode.h" #include "iwl-agn-hw.h" #include "iwl-fw.h" #include "iwl-config.h" #include "iwl-modparams.h" /* private includes */ #include "iwl-fw-file.h" /****************************************************************************** * * module boiler plate * ******************************************************************************/ /* * module name, copyright, version, etc. */ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux" #ifdef CONFIG_IWLWIFI_DEBUG #define VD "d" #else #define VD #endif #define DRV_VERSION IWLWIFI_VERSION VD MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); #ifdef CONFIG_IWLWIFI_DEBUGFS static struct dentry *iwl_dbgfs_root; #endif /** * struct iwl_drv - drv common data * @list: list of drv structures using this opmode * @fw: the iwl_fw structure * @op_mode: the running op_mode * @trans: transport layer * @dev: for debug prints only * @cfg: configuration struct * @fw_index: firmware revision to try loading * @firmware_name: composite filename of ucode file to load * @request_firmware_complete: the firmware has been obtained from user space */ struct iwl_drv { struct list_head list; struct iwl_fw fw; struct iwl_op_mode *op_mode; struct iwl_trans *trans; struct device *dev; const struct iwl_cfg *cfg; int fw_index; /* firmware we're trying to load */ char firmware_name[25]; /* name of firmware file to load */ struct completion request_firmware_complete; #ifdef CONFIG_IWLWIFI_DEBUGFS struct dentry *dbgfs_drv; struct dentry *dbgfs_trans; struct dentry *dbgfs_op_mode; #endif }; #define DVM_OP_MODE 0 #define MVM_OP_MODE 1 /* Protects the table contents, i.e. the ops pointer & drv list */ static struct mutex iwlwifi_opmode_table_mtx; static struct iwlwifi_opmode_table { const char *name; /* name: iwldvm, iwlmvm, etc */ const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */ struct list_head drv; /* list of devices using this op_mode */ } iwlwifi_opmode_table[] = { /* ops set when driver is initialized */ { .name = "iwldvm", .ops = NULL }, { .name = "iwlmvm", .ops = NULL }, }; /* * struct fw_sec: Just for the image parsing proccess. * For the fw storage we are using struct fw_desc. */ struct fw_sec { const void *data; /* the sec data */ size_t size; /* section size */ u32 offset; /* offset of writing in the device */ }; static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc) { if (desc->v_addr) dma_free_coherent(drv->trans->dev, desc->len, desc->v_addr, desc->p_addr); desc->v_addr = NULL; desc->len = 0; } static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img) { int i; for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) iwl_free_fw_desc(drv, &img->sec[i]); } static void iwl_dealloc_ucode(struct iwl_drv *drv) { int i; for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) iwl_free_fw_img(drv, drv->fw.img + i); } static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, struct fw_sec *sec) { if (!sec || !sec->size) { desc->v_addr = NULL; return -EINVAL; } desc->v_addr = dma_alloc_coherent(drv->trans->dev, sec->size, &desc->p_addr, GFP_KERNEL); if (!desc->v_addr) return -ENOMEM; desc->len = sec->size; desc->offset = sec->offset; memcpy(desc->v_addr, sec->data, sec->size); return 0; } static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context); #define UCODE_EXPERIMENTAL_INDEX 100 #define UCODE_EXPERIMENTAL_TAG "exp" static int iwl_request_firmware(struct iwl_drv *drv, bool first) { const char *name_pre = drv->cfg->fw_name_pre; char tag[8]; if (first) { #ifdef CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE drv->fw_index = UCODE_EXPERIMENTAL_INDEX; strcpy(tag, UCODE_EXPERIMENTAL_TAG); } else if (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) { #endif drv->fw_index = drv->cfg->ucode_api_max; sprintf(tag, "%d", drv->fw_index); } else { drv->fw_index--; sprintf(tag, "%d", drv->fw_index); } if (drv->fw_index < drv->cfg->ucode_api_min) { IWL_ERR(drv, "no suitable firmware found!\n"); return -ENOENT; } sprintf(drv->firmware_name, "%s%s%s", name_pre, tag, ".ucode"); IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n", (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) ? "EXPERIMENTAL " : "", drv->firmware_name); return request_firmware_nowait(THIS_MODULE, 1, drv->firmware_name, drv->trans->dev, GFP_KERNEL, drv, iwl_req_fw_callback); } struct fw_img_parsing { struct fw_sec sec[IWL_UCODE_SECTION_MAX]; int sec_counter; }; /* * struct fw_sec_parsing: to extract fw section and it's offset from tlv */ struct fw_sec_parsing { __le32 offset; const u8 data[]; } __packed; /** * struct iwl_tlv_calib_data - parse the default calib data from TLV * * @ucode_type: the uCode to which the following default calib relates. * @calib: default calibrations. */ struct iwl_tlv_calib_data { __le32 ucode_type; __le64 calib; } __packed; struct iwl_firmware_pieces { struct fw_img_parsing img[IWL_UCODE_TYPE_MAX]; u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; }; /* * These functions are just to extract uCode section data from the pieces * structure. */ static struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces, enum iwl_ucode_type type, int sec) { return &pieces->img[type].sec[sec]; } static void set_sec_data(struct iwl_firmware_pieces *pieces, enum iwl_ucode_type type, int sec, const void *data) { pieces->img[type].sec[sec].data = data; } static void set_sec_size(struct iwl_firmware_pieces *pieces, enum iwl_ucode_type type, int sec, size_t size) { pieces->img[type].sec[sec].size = size; } static size_t get_sec_size(struct iwl_firmware_pieces *pieces, enum iwl_ucode_type type, int sec) { return pieces->img[type].sec[sec].size; } static void set_sec_offset(struct iwl_firmware_pieces *pieces, enum iwl_ucode_type type, int sec, u32 offset) { pieces->img[type].sec[sec].offset = offset; } /* * Gets uCode section from tlv. */ static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces, const void *data, enum iwl_ucode_type type, int size) { struct fw_img_parsing *img; struct fw_sec *sec; struct fw_sec_parsing *sec_parse; if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX)) return -1; sec_parse = (struct fw_sec_parsing *)data; img = &pieces->img[type]; sec = &img->sec[img->sec_counter]; sec->offset = le32_to_cpu(sec_parse->offset); sec->data = sec_parse->data; sec->size = size - sizeof(sec_parse->offset); ++img->sec_counter; return 0; } static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) { struct iwl_tlv_calib_data *def_calib = (struct iwl_tlv_calib_data *)data; u32 ucode_type = le32_to_cpu(def_calib->ucode_type); if (ucode_type >= IWL_UCODE_TYPE_MAX) { IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n", ucode_type); return -EINVAL; } drv->fw.default_calib[ucode_type] = le64_to_cpu(def_calib->calib); return 0; } static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, const struct firmware *ucode_raw, struct iwl_firmware_pieces *pieces) { struct iwl_ucode_header *ucode = (void *)ucode_raw->data; u32 api_ver, hdr_size, build; char buildstr[25]; const u8 *src; drv->fw.ucode_ver = le32_to_cpu(ucode->ver); api_ver = IWL_UCODE_API(drv->fw.ucode_ver); switch (api_ver) { default: hdr_size = 28; if (ucode_raw->size < hdr_size) { IWL_ERR(drv, "File size too small!\n"); return -EINVAL; } build = le32_to_cpu(ucode->u.v2.build); set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, le32_to_cpu(ucode->u.v2.inst_size)); set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, le32_to_cpu(ucode->u.v2.data_size)); set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, le32_to_cpu(ucode->u.v2.init_size)); set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, le32_to_cpu(ucode->u.v2.init_data_size)); src = ucode->u.v2.data; break; case 0: case 1: case 2: hdr_size = 24; if (ucode_raw->size < hdr_size) { IWL_ERR(drv, "File size too small!\n"); return -EINVAL; } build = 0; set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, le32_to_cpu(ucode->u.v1.inst_size)); set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, le32_to_cpu(ucode->u.v1.data_size)); set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, le32_to_cpu(ucode->u.v1.init_size)); set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, le32_to_cpu(ucode->u.v1.init_data_size)); src = ucode->u.v1.data; break; } if (build) sprintf(buildstr, " build %u%s", build, (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) ? " (EXP)" : ""); else buildstr[0] = '\0'; snprintf(drv->fw.fw_version, sizeof(drv->fw.fw_version), "%u.%u.%u.%u%s", IWL_UCODE_MAJOR(drv->fw.ucode_ver), IWL_UCODE_MINOR(drv->fw.ucode_ver), IWL_UCODE_API(drv->fw.ucode_ver), IWL_UCODE_SERIAL(drv->fw.ucode_ver), buildstr); /* Verify size of file vs. image size info in file's header */ if (ucode_raw->size != hdr_size + get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) + get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) + get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) + get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)) { IWL_ERR(drv, "uCode file size %d does not match expected size\n", (int)ucode_raw->size); return -EINVAL; } set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, src); src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST); set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, IWLAGN_RTC_INST_LOWER_BOUND); set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, src); src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA); set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, IWLAGN_RTC_DATA_LOWER_BOUND); set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, src); src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST); set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, IWLAGN_RTC_INST_LOWER_BOUND); set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, src); src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA); set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, IWLAGN_RTC_DATA_LOWER_BOUND); return 0; } static int iwl_parse_tlv_firmware(struct iwl_drv *drv, const struct firmware *ucode_raw, struct iwl_firmware_pieces *pieces, struct iwl_ucode_capabilities *capa) { struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data; struct iwl_ucode_tlv *tlv; size_t len = ucode_raw->size; const u8 *data; u32 tlv_len; enum iwl_ucode_tlv_type tlv_type; const u8 *tlv_data; char buildstr[25]; u32 build; if (len < sizeof(*ucode)) { IWL_ERR(drv, "uCode has invalid length: %zd\n", len); return -EINVAL; } if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) { IWL_ERR(drv, "invalid uCode magic: 0X%x\n", le32_to_cpu(ucode->magic)); return -EINVAL; } drv->fw.ucode_ver = le32_to_cpu(ucode->ver); build = le32_to_cpu(ucode->build); if (build) sprintf(buildstr, " build %u%s", build, (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) ? " (EXP)" : ""); else buildstr[0] = '\0'; snprintf(drv->fw.fw_version, sizeof(drv->fw.fw_version), "%u.%u.%u.%u%s", IWL_UCODE_MAJOR(drv->fw.ucode_ver), IWL_UCODE_MINOR(drv->fw.ucode_ver), IWL_UCODE_API(drv->fw.ucode_ver), IWL_UCODE_SERIAL(drv->fw.ucode_ver), buildstr); data = ucode->data; len -= sizeof(*ucode); while (len >= sizeof(*tlv)) { len -= sizeof(*tlv); tlv = (void *)data; tlv_len = le32_to_cpu(tlv->length); tlv_type = le32_to_cpu(tlv->type); tlv_data = tlv->data; if (len < tlv_len) { IWL_ERR(drv, "invalid TLV len: %zd/%u\n", len, tlv_len); return -EINVAL; } len -= ALIGN(tlv_len, 4); data += sizeof(*tlv) + ALIGN(tlv_len, 4); switch (tlv_type) { case IWL_UCODE_TLV_INST: set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, tlv_data); set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, tlv_len); set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, IWLAGN_RTC_INST_LOWER_BOUND); break; case IWL_UCODE_TLV_DATA: set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, tlv_data); set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, tlv_len); set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, IWLAGN_RTC_DATA_LOWER_BOUND); break; case IWL_UCODE_TLV_INIT: set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, tlv_data); set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, tlv_len); set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, IWLAGN_RTC_INST_LOWER_BOUND); break; case IWL_UCODE_TLV_INIT_DATA: set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, tlv_data); set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, tlv_len); set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, IWLAGN_RTC_DATA_LOWER_BOUND); break; case IWL_UCODE_TLV_BOOT: IWL_ERR(drv, "Found unexpected BOOT ucode\n"); break; case IWL_UCODE_TLV_PROBE_MAX_LEN: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; capa->max_probe_length = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_PAN: if (tlv_len) goto invalid_tlv_len; capa->flags |= IWL_UCODE_TLV_FLAGS_PAN; break; case IWL_UCODE_TLV_FLAGS: /* must be at least one u32 */ if (tlv_len < sizeof(u32)) goto invalid_tlv_len; /* and a proper number of u32s */ if (tlv_len % sizeof(u32)) goto invalid_tlv_len; /* * This driver only reads the first u32 as * right now no more features are defined, * if that changes then either the driver * will not work with the new firmware, or * it'll not take advantage of new features. */ capa->flags = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_INIT_EVTLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->init_evtlog_ptr = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_INIT_EVTLOG_SIZE: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->init_evtlog_size = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_INIT_ERRLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->init_errlog_ptr = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_RUNT_EVTLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->inst_evtlog_ptr = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->inst_evtlog_size = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_RUNT_ERRLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; pieces->inst_errlog_ptr = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_ENHANCE_SENS_TBL: if (tlv_len) goto invalid_tlv_len; drv->fw.enhance_sensitivity_table = true; break; case IWL_UCODE_TLV_WOWLAN_INST: set_sec_data(pieces, IWL_UCODE_WOWLAN, IWL_UCODE_SECTION_INST, tlv_data); set_sec_size(pieces, IWL_UCODE_WOWLAN, IWL_UCODE_SECTION_INST, tlv_len); set_sec_offset(pieces, IWL_UCODE_WOWLAN, IWL_UCODE_SECTION_INST, IWLAGN_RTC_INST_LOWER_BOUND); break; case IWL_UCODE_TLV_WOWLAN_DATA: set_sec_data(pieces, IWL_UCODE_WOWLAN, IWL_UCODE_SECTION_DATA, tlv_data); set_sec_size(pieces, IWL_UCODE_WOWLAN, IWL_UCODE_SECTION_DATA, tlv_len); set_sec_offset(pieces, IWL_UCODE_WOWLAN, IWL_UCODE_SECTION_DATA, IWLAGN_RTC_DATA_LOWER_BOUND); break; case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; capa->standard_phy_calibration_size = le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_SEC_RT: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, tlv_len); drv->fw.mvm_fw = true; break; case IWL_UCODE_TLV_SEC_INIT: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, tlv_len); drv->fw.mvm_fw = true; break; case IWL_UCODE_TLV_SEC_WOWLAN: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, tlv_len); drv->fw.mvm_fw = true; break; case IWL_UCODE_TLV_DEF_CALIB: if (tlv_len != sizeof(struct iwl_tlv_calib_data)) goto invalid_tlv_len; if (iwl_set_default_calib(drv, tlv_data)) goto tlv_error; break; case IWL_UCODE_TLV_PHY_SKU: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; } } if (len) { IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len); iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len); return -EINVAL; } return 0; invalid_tlv_len: IWL_ERR(drv, "TLV %d has invalid size: %u\n", tlv_type, tlv_len); tlv_error: iwl_print_hex_dump(drv, IWL_DL_FW, tlv_data, tlv_len); return -EINVAL; } static int iwl_alloc_ucode(struct iwl_drv *drv, struct iwl_firmware_pieces *pieces, enum iwl_ucode_type type) { int i; for (i = 0; i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i); i++) if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]), get_sec(pieces, type, i))) return -ENOMEM; return 0; } static int validate_sec_sizes(struct iwl_drv *drv, struct iwl_firmware_pieces *pieces, const struct iwl_cfg *cfg) { IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST)); IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA)); IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n", get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST)); IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n", get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)); /* Verify that uCode images will fit in card's SRAM. */ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) > cfg->max_inst_size) { IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST)); return -1; } if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) > cfg->max_data_size) { IWL_ERR(drv, "uCode data len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA)); return -1; } if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) > cfg->max_inst_size) { IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST)); return -1; } if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) > cfg->max_data_size) { IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA)); return -1; } return 0; } static struct iwl_op_mode * _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) { const struct iwl_op_mode_ops *ops = op->ops; struct dentry *dbgfs_dir = NULL; struct iwl_op_mode *op_mode = NULL; #ifdef CONFIG_IWLWIFI_DEBUGFS drv->dbgfs_op_mode = debugfs_create_dir(op->name, drv->dbgfs_drv); if (!drv->dbgfs_op_mode) { IWL_ERR(drv, "failed to create opmode debugfs directory\n"); return op_mode; } dbgfs_dir = drv->dbgfs_op_mode; #endif op_mode = ops->start(drv->trans, drv->cfg, &drv->fw, dbgfs_dir); #ifdef CONFIG_IWLWIFI_DEBUGFS if (!op_mode) { debugfs_remove_recursive(drv->dbgfs_op_mode); drv->dbgfs_op_mode = NULL; } #endif return op_mode; } static void _iwl_op_mode_stop(struct iwl_drv *drv) { /* op_mode can be NULL if its start failed */ if (drv->op_mode) { iwl_op_mode_stop(drv->op_mode); drv->op_mode = NULL; #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove_recursive(drv->dbgfs_op_mode); drv->dbgfs_op_mode = NULL; #endif } } /** * iwl_req_fw_callback - callback when firmware was loaded * * If loaded successfully, copies the firmware into buffers * for the card to fetch (via DMA). */ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) { struct iwl_drv *drv = context; struct iwl_fw *fw = &drv->fw; struct iwl_ucode_header *ucode; struct iwlwifi_opmode_table *op; int err; struct iwl_firmware_pieces pieces; const unsigned int api_max = drv->cfg->ucode_api_max; unsigned int api_ok = drv->cfg->ucode_api_ok; const unsigned int api_min = drv->cfg->ucode_api_min; u32 api_ver; int i; bool load_module = false; fw->ucode_capa.max_probe_length = 200; fw->ucode_capa.standard_phy_calibration_size = IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; if (!api_ok) api_ok = api_max; memset(&pieces, 0, sizeof(pieces)); if (!ucode_raw) { if (drv->fw_index <= api_ok) IWL_ERR(drv, "request for firmware file '%s' failed.\n", drv->firmware_name); goto try_again; } IWL_DEBUG_INFO(drv, "Loaded firmware file '%s' (%zd bytes).\n", drv->firmware_name, ucode_raw->size); /* Make sure that we got at least the API version number */ if (ucode_raw->size < 4) { IWL_ERR(drv, "File size way too small!\n"); goto try_again; } /* Data from ucode file: header followed by uCode images */ ucode = (struct iwl_ucode_header *)ucode_raw->data; if (ucode->ver) err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces); else err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces, &fw->ucode_capa); if (err) goto try_again; api_ver = IWL_UCODE_API(drv->fw.ucode_ver); /* * api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely * on the API version read from firmware header from here on forward */ /* no api version check required for experimental uCode */ if (drv->fw_index != UCODE_EXPERIMENTAL_INDEX) { if (api_ver < api_min || api_ver > api_max) { IWL_ERR(drv, "Driver unable to support your firmware API. " "Driver supports v%u, firmware is v%u.\n", api_max, api_ver); goto try_again; } if (api_ver < api_ok) { if (api_ok != api_max) IWL_ERR(drv, "Firmware has old API version, " "expected v%u through v%u, got v%u.\n", api_ok, api_max, api_ver); else IWL_ERR(drv, "Firmware has old API version, " "expected v%u, got v%u.\n", api_max, api_ver); IWL_ERR(drv, "New firmware can be obtained from " "http://www.intellinuxwireless.org/.\n"); } } IWL_INFO(drv, "loaded firmware version %s", drv->fw.fw_version); /* * In mvm uCode there is no difference between data and instructions * sections. */ if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg)) goto try_again; /* Allocate ucode buffers for card's bus-master loading ... */ /* Runtime instructions and 2 copies of data: * 1) unmodified from disk * 2) backup cache for save/restore during power-downs */ for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) if (iwl_alloc_ucode(drv, &pieces, i)) goto out_free_fw; /* Now that we can no longer fail, copy information */ /* * The (size - 16) / 12 formula is based on the information recorded * for each event, which is of mode 1 (including timestamp) for all * new microcodes that include this information. */ fw->init_evtlog_ptr = pieces.init_evtlog_ptr; if (pieces.init_evtlog_size) fw->init_evtlog_size = (pieces.init_evtlog_size - 16)/12; else fw->init_evtlog_size = drv->cfg->base_params->max_event_log_size; fw->init_errlog_ptr = pieces.init_errlog_ptr; fw->inst_evtlog_ptr = pieces.inst_evtlog_ptr; if (pieces.inst_evtlog_size) fw->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12; else fw->inst_evtlog_size = drv->cfg->base_params->max_event_log_size; fw->inst_errlog_ptr = pieces.inst_errlog_ptr; /* * figure out the offset of chain noise reset and gain commands * base on the size of standard phy calibration commands table size */ if (fw->ucode_capa.standard_phy_calibration_size > IWL_MAX_PHY_CALIBRATE_TBL_SIZE) fw->ucode_capa.standard_phy_calibration_size = IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE; /* We have our copies now, allow OS release its copies */ release_firmware(ucode_raw); mutex_lock(&iwlwifi_opmode_table_mtx); op = &iwlwifi_opmode_table[DVM_OP_MODE]; /* add this device to the list of devices using this op_mode */ list_add_tail(&drv->list, &op->drv); if (op->ops) { drv->op_mode = _iwl_op_mode_start(drv, op); if (!drv->op_mode) { mutex_unlock(&iwlwifi_opmode_table_mtx); goto out_unbind; } } else { load_module = true; } mutex_unlock(&iwlwifi_opmode_table_mtx); /* * Complete the firmware request last so that * a driver unbind (stop) doesn't run while we * are doing the start() above. */ complete(&drv->request_firmware_complete); /* * Load the module last so we don't block anything * else from proceeding if the module fails to load * or hangs loading. */ if (load_module) request_module("%s", op->name); return; try_again: /* try next, if any */ release_firmware(ucode_raw); if (iwl_request_firmware(drv, false)) goto out_unbind; return; out_free_fw: IWL_ERR(drv, "failed to allocate pci memory\n"); iwl_dealloc_ucode(drv); release_firmware(ucode_raw); out_unbind: complete(&drv->request_firmware_complete); device_release_driver(drv->trans->dev); } struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, const struct iwl_cfg *cfg) { struct iwl_drv *drv; int ret; drv = kzalloc(sizeof(*drv), GFP_KERNEL); if (!drv) return NULL; drv->trans = trans; drv->dev = trans->dev; drv->cfg = cfg; init_completion(&drv->request_firmware_complete); INIT_LIST_HEAD(&drv->list); #ifdef CONFIG_IWLWIFI_DEBUGFS /* Create the device debugfs entries. */ drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev), iwl_dbgfs_root); if (!drv->dbgfs_drv) { IWL_ERR(drv, "failed to create debugfs directory\n"); goto err_free_drv; } /* Create transport layer debugfs dir */ drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv); if (!drv->trans->dbgfs_dir) { IWL_ERR(drv, "failed to create transport debugfs directory\n"); goto err_free_dbgfs; } #endif ret = iwl_request_firmware(drv, true); if (ret) { IWL_ERR(trans, "Couldn't request the fw\n"); goto err_fw; } return drv; err_fw: #ifdef CONFIG_IWLWIFI_DEBUGFS err_free_dbgfs: debugfs_remove_recursive(drv->dbgfs_drv); err_free_drv: #endif kfree(drv); drv = NULL; return drv; } void iwl_drv_stop(struct iwl_drv *drv) { wait_for_completion(&drv->request_firmware_complete); _iwl_op_mode_stop(drv); iwl_dealloc_ucode(drv); mutex_lock(&iwlwifi_opmode_table_mtx); /* * List is empty (this item wasn't added) * when firmware loading failed -- in that * case we can't remove it from any list. */ if (!list_empty(&drv->list)) list_del(&drv->list); mutex_unlock(&iwlwifi_opmode_table_mtx); #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove_recursive(drv->dbgfs_drv); #endif kfree(drv); } /* shared module parameters */ struct iwl_mod_params iwlwifi_mod_params = { .amsdu_size_8K = 1, .restart_fw = 1, .plcp_check = true, .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, .bt_ch_announce = true, .auto_agg = true, .wd_disable = true, /* the rest are 0 by default */ }; EXPORT_SYMBOL_GPL(iwlwifi_mod_params); int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) { int i; struct iwl_drv *drv; struct iwlwifi_opmode_table *op; mutex_lock(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { op = &iwlwifi_opmode_table[i]; if (strcmp(op->name, name)) continue; op->ops = ops; /* TODO: need to handle exceptional case */ list_for_each_entry(drv, &op->drv, list) drv->op_mode = _iwl_op_mode_start(drv, op); mutex_unlock(&iwlwifi_opmode_table_mtx); return 0; } mutex_unlock(&iwlwifi_opmode_table_mtx); return -EIO; } EXPORT_SYMBOL_GPL(iwl_opmode_register); void iwl_opmode_deregister(const char *name) { int i; struct iwl_drv *drv; mutex_lock(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { if (strcmp(iwlwifi_opmode_table[i].name, name)) continue; iwlwifi_opmode_table[i].ops = NULL; /* call the stop routine for all devices */ list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) _iwl_op_mode_stop(drv); mutex_unlock(&iwlwifi_opmode_table_mtx); return; } mutex_unlock(&iwlwifi_opmode_table_mtx); } EXPORT_SYMBOL_GPL(iwl_opmode_deregister); static int __init iwl_drv_init(void) { int i; mutex_init(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); pr_info(DRV_COPYRIGHT "\n"); #ifdef CONFIG_IWLWIFI_DEBUGFS /* Create the root of iwlwifi debugfs subsystem. */ iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL); if (!iwl_dbgfs_root) return -EFAULT; #endif return iwl_pci_register_driver(); } module_init(iwl_drv_init); static void __exit iwl_drv_exit(void) { iwl_pci_unregister_driver(); #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove_recursive(iwl_dbgfs_root); #endif } module_exit(iwl_drv_exit); #ifdef CONFIG_IWLWIFI_DEBUG module_param_named(debug, iwlwifi_mod_params.debug_level, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(swcrypto, iwlwifi_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO); MODULE_PARM_DESC(11n_disable, "disable 11n functionality, bitmap: 1: full, 2: agg TX, 4: agg RX"); module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K, int, S_IRUGO); MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling, int, S_IRUGO); MODULE_PARM_DESC(antenna_coupling, "specify antenna coupling in dB (defualt: 0 dB)"); module_param_named(bt_ch_inhibition, iwlwifi_mod_params.bt_ch_announce, bool, S_IRUGO); MODULE_PARM_DESC(bt_ch_inhibition, "Enable BT channel inhibition (default: enable)"); module_param_named(plcp_check, iwlwifi_mod_params.plcp_check, bool, S_IRUGO); MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])"); module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO); MODULE_PARM_DESC(wd_disable, "Disable stuck queue watchdog timer 0=system default, " "1=disable, 2=enable (default: 0)"); /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the * priority line in the PCIx). * set bt_coex_active to false, uCode will ignore the BT activity and * perform the normal operation * * User might experience transmit issue on some platform due to WiFi/BT * co-exist problem. The possible behaviors are: * Able to scan and finding all the available AP * Not able to associate with any AP * On those platforms, WiFi communication can be restored by set * "bt_coex_active" module parameter to "false" * * default: bt_coex_active = true (BT_COEX_ENABLE) */ module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active, bool, S_IRUGO); MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)"); module_param_named(led_mode, iwlwifi_mod_params.led_mode, int, S_IRUGO); MODULE_PARM_DESC(led_mode, "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)"); module_param_named(power_save, iwlwifi_mod_params.power_save, bool, S_IRUGO); MODULE_PARM_DESC(power_save, "enable WiFi power management (default: disable)"); module_param_named(power_level, iwlwifi_mod_params.power_level, int, S_IRUGO); MODULE_PARM_DESC(power_level, "default power save level (range from 1 - 5, default: 1)"); module_param_named(auto_agg, iwlwifi_mod_params.auto_agg, bool, S_IRUGO); MODULE_PARM_DESC(auto_agg, "enable agg w/o check traffic load (default: enable)"); module_param_named(5ghz_disable, iwlwifi_mod_params.disable_5ghz, bool, S_IRUGO); MODULE_PARM_DESC(5ghz_disable, "disable 5GHz band (default: 0 [enabled])"); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-devtrace.h0000644000175000017500000002443012026211315024675 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #if !defined(__IWLWIFI_DEVICE_TRACE) || defined(TRACE_HEADER_MULTI_READ) #define __IWLWIFI_DEVICE_TRACE #include #include #include "iwl-trans.h" #if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ static inline void trace_ ## name(proto) {} #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(...) #undef DEFINE_EVENT #define DEFINE_EVENT(evt_class, name, proto, ...) \ static inline void trace_ ## name(proto) {} #endif #define DEV_ENTRY __string(dev, dev_name(dev)) #define DEV_ASSIGN __assign_str(dev, dev_name(dev)) #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi_io TRACE_EVENT(iwlwifi_dev_ioread32, TP_PROTO(const struct device *dev, u32 offs, u32 val), TP_ARGS(dev, offs, val), TP_STRUCT__entry( DEV_ENTRY __field(u32, offs) __field(u32, val) ), TP_fast_assign( DEV_ASSIGN; __entry->offs = offs; __entry->val = val; ), TP_printk("[%s] read io[%#x] = %#x", __get_str(dev), __entry->offs, __entry->val) ); TRACE_EVENT(iwlwifi_dev_iowrite8, TP_PROTO(const struct device *dev, u32 offs, u8 val), TP_ARGS(dev, offs, val), TP_STRUCT__entry( DEV_ENTRY __field(u32, offs) __field(u8, val) ), TP_fast_assign( DEV_ASSIGN; __entry->offs = offs; __entry->val = val; ), TP_printk("[%s] write io[%#x] = %#x)", __get_str(dev), __entry->offs, __entry->val) ); TRACE_EVENT(iwlwifi_dev_iowrite32, TP_PROTO(const struct device *dev, u32 offs, u32 val), TP_ARGS(dev, offs, val), TP_STRUCT__entry( DEV_ENTRY __field(u32, offs) __field(u32, val) ), TP_fast_assign( DEV_ASSIGN; __entry->offs = offs; __entry->val = val; ), TP_printk("[%s] write io[%#x] = %#x)", __get_str(dev), __entry->offs, __entry->val) ); TRACE_EVENT(iwlwifi_dev_irq, TP_PROTO(const struct device *dev), TP_ARGS(dev), TP_STRUCT__entry( DEV_ENTRY ), TP_fast_assign( DEV_ASSIGN; ), /* TP_printk("") doesn't compile */ TP_printk("%d", 0) ); TRACE_EVENT(iwlwifi_dev_ict_read, TP_PROTO(const struct device *dev, u32 index, u32 value), TP_ARGS(dev, index, value), TP_STRUCT__entry( DEV_ENTRY __field(u32, index) __field(u32, value) ), TP_fast_assign( DEV_ASSIGN; __entry->index = index; __entry->value = value; ), TP_printk("[%s] read ict[%d] = %#.8x", __get_str(dev), __entry->index, __entry->value) ); #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi_ucode TRACE_EVENT(iwlwifi_dev_ucode_cont_event, TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), TP_ARGS(dev, time, data, ev), TP_STRUCT__entry( DEV_ENTRY __field(u32, time) __field(u32, data) __field(u32, ev) ), TP_fast_assign( DEV_ASSIGN; __entry->time = time; __entry->data = data; __entry->ev = ev; ), TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", __get_str(dev), __entry->time, __entry->data, __entry->ev) ); TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry), TP_ARGS(dev, wraps, n_entry, p_entry), TP_STRUCT__entry( DEV_ENTRY __field(u32, wraps) __field(u32, n_entry) __field(u32, p_entry) ), TP_fast_assign( DEV_ASSIGN; __entry->wraps = wraps; __entry->n_entry = n_entry; __entry->p_entry = p_entry; ), TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X", __get_str(dev), __entry->wraps, __entry->n_entry, __entry->p_entry) ); #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi_msg #define MAX_MSG_LEN 110 DECLARE_EVENT_CLASS(iwlwifi_msg_event, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf), TP_STRUCT__entry( __dynamic_array(char, msg, MAX_MSG_LEN) ), TP_fast_assign( WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), MAX_MSG_LEN, vaf->fmt, *vaf->va) >= MAX_MSG_LEN); ), TP_printk("%s", __get_str(msg)) ); DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) ); DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) ); DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) ); DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) ); TRACE_EVENT(iwlwifi_dbg, TP_PROTO(u32 level, bool in_interrupt, const char *function, struct va_format *vaf), TP_ARGS(level, in_interrupt, function, vaf), TP_STRUCT__entry( __field(u32, level) __field(u8, in_interrupt) __string(function, function) __dynamic_array(char, msg, MAX_MSG_LEN) ), TP_fast_assign( __entry->level = level; __entry->in_interrupt = in_interrupt; __assign_str(function, function); WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), MAX_MSG_LEN, vaf->fmt, *vaf->va) >= MAX_MSG_LEN); ), TP_printk("%s", (char *)__get_dynamic_array(msg)) ); #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi TRACE_EVENT(iwlwifi_dev_hcmd, TP_PROTO(const struct device *dev, struct iwl_host_cmd *cmd, u16 total_size, const void *hdr, size_t hdr_len), TP_ARGS(dev, cmd, total_size, hdr, hdr_len), TP_STRUCT__entry( DEV_ENTRY __dynamic_array(u8, hcmd, total_size) __field(u32, flags) ), TP_fast_assign( int i, offset = hdr_len; DEV_ASSIGN; __entry->flags = cmd->flags; memcpy(__get_dynamic_array(hcmd), hdr, hdr_len); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) continue; memcpy((u8 *)__get_dynamic_array(hcmd) + offset, cmd->data[i], cmd->len[i]); offset += cmd->len[i]; } ), TP_printk("[%s] hcmd %#.2x (%ssync)", __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[0], __entry->flags & CMD_ASYNC ? "a" : "") ); TRACE_EVENT(iwlwifi_dev_rx, TP_PROTO(const struct device *dev, void *rxbuf, size_t len), TP_ARGS(dev, rxbuf, len), TP_STRUCT__entry( DEV_ENTRY __dynamic_array(u8, rxbuf, len) ), TP_fast_assign( DEV_ASSIGN; memcpy(__get_dynamic_array(rxbuf), rxbuf, len); ), TP_printk("[%s] RX cmd %#.2x", __get_str(dev), ((u8 *)__get_dynamic_array(rxbuf))[4]) ); TRACE_EVENT(iwlwifi_dev_tx, TP_PROTO(const struct device *dev, void *tfd, size_t tfdlen, void *buf0, size_t buf0_len, void *buf1, size_t buf1_len), TP_ARGS(dev, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), TP_STRUCT__entry( DEV_ENTRY __field(size_t, framelen) __dynamic_array(u8, tfd, tfdlen) /* * Do not insert between or below these items, * we want to keep the frame together (except * for the possible padding). */ __dynamic_array(u8, buf0, buf0_len) __dynamic_array(u8, buf1, buf1_len) ), TP_fast_assign( DEV_ASSIGN; __entry->framelen = buf0_len + buf1_len; memcpy(__get_dynamic_array(tfd), tfd, tfdlen); memcpy(__get_dynamic_array(buf0), buf0, buf0_len); memcpy(__get_dynamic_array(buf1), buf1, buf1_len); ), TP_printk("[%s] TX %.2x (%zu bytes)", __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0], __entry->framelen) ); TRACE_EVENT(iwlwifi_dev_ucode_error, TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low, u32 data1, u32 data2, u32 line, u32 blink1, u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, u32 gp1, u32 gp2, u32 gp3, u32 ucode_ver, u32 hw_ver, u32 brd_ver), TP_ARGS(dev, desc, tsf_low, data1, data2, line, blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2, gp3, ucode_ver, hw_ver, brd_ver), TP_STRUCT__entry( DEV_ENTRY __field(u32, desc) __field(u32, tsf_low) __field(u32, data1) __field(u32, data2) __field(u32, line) __field(u32, blink1) __field(u32, blink2) __field(u32, ilink1) __field(u32, ilink2) __field(u32, bcon_time) __field(u32, gp1) __field(u32, gp2) __field(u32, gp3) __field(u32, ucode_ver) __field(u32, hw_ver) __field(u32, brd_ver) ), TP_fast_assign( DEV_ASSIGN; __entry->desc = desc; __entry->tsf_low = tsf_low; __entry->data1 = data1; __entry->data2 = data2; __entry->line = line; __entry->blink1 = blink1; __entry->blink2 = blink2; __entry->ilink1 = ilink1; __entry->ilink2 = ilink2; __entry->bcon_time = bcon_time; __entry->gp1 = gp1; __entry->gp2 = gp2; __entry->gp3 = gp3; __entry->ucode_ver = ucode_ver; __entry->hw_ver = hw_ver; __entry->brd_ver = brd_ver; ), TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X " "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X uCode 0x%08X " "hw 0x%08X brd 0x%08X", __get_str(dev), __entry->desc, __entry->tsf_low, __entry->data1, __entry->data2, __entry->line, __entry->blink1, __entry->blink2, __entry->ilink1, __entry->ilink2, __entry->bcon_time, __entry->gp1, __entry->gp2, __entry->gp3, __entry->ucode_ver, __entry->hw_ver, __entry->brd_ver) ); TRACE_EVENT(iwlwifi_dev_ucode_event, TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), TP_ARGS(dev, time, data, ev), TP_STRUCT__entry( DEV_ENTRY __field(u32, time) __field(u32, data) __field(u32, ev) ), TP_fast_assign( DEV_ASSIGN; __entry->time = time; __entry->data = data; __entry->ev = ev; ), TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", __get_str(dev), __entry->time, __entry->data, __entry->ev) ); #endif /* __IWLWIFI_DEVICE_TRACE */ #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH . #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE iwl-devtrace #include compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-devtrace.c0000644000175000017500000000365312026211315024674 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2009 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include /* sparse doesn't like tracepoint macros */ #ifndef __CHECKER__ #include "iwl-trans.h" #define CREATE_TRACE_POINTS #include "iwl-devtrace.h" EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_tx); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_info); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_warn); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_crit); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_err); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dbg); #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-debug.h0000644000175000017500000001631012026211315024164 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __iwl_debug_h__ #define __iwl_debug_h__ #include "iwl-modparams.h" static inline bool iwl_have_debug_level(u32 level) { return iwlwifi_mod_params.debug_level & level; } void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace, const char *fmt, ...) __printf(4, 5); void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3); void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3); void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3); /* No matter what is m (priv, bus, trans), this will work */ #define IWL_ERR(m, f, a...) __iwl_err((m)->dev, false, false, f, ## a) #define IWL_ERR_DEV(d, f, a...) __iwl_err((d), false, false, f, ## a) #define IWL_WARN(m, f, a...) __iwl_warn((m)->dev, f, ## a) #define IWL_INFO(m, f, a...) __iwl_info((m)->dev, f, ## a) #define IWL_CRIT(m, f, a...) __iwl_crit((m)->dev, f, ## a) #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) void __iwl_dbg(struct device *dev, u32 level, bool limit, const char *function, const char *fmt, ...) __printf(5, 6); #else __printf(5, 6) static inline void __iwl_dbg(struct device *dev, u32 level, bool limit, const char *function, const char *fmt, ...) {} #endif #define iwl_print_hex_error(m, p, len) \ do { \ print_hex_dump(KERN_ERR, "iwl data: ", \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) #define IWL_DEBUG(m, level, fmt, args...) \ __iwl_dbg((m)->dev, level, false, __func__, fmt, ##args) #define IWL_DEBUG_DEV(dev, level, fmt, args...) \ __iwl_dbg((dev), level, false, __func__, fmt, ##args) #define IWL_DEBUG_LIMIT(m, level, fmt, args...) \ __iwl_dbg((m)->dev, level, true, __func__, fmt, ##args) #ifdef CONFIG_IWLWIFI_DEBUG #define iwl_print_hex_dump(m, level, p, len) \ do { \ if (iwl_have_debug_level(level)) \ print_hex_dump(KERN_DEBUG, "iwl data: ", \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) #else #define iwl_print_hex_dump(m, level, p, len) #endif /* CONFIG_IWLWIFI_DEBUG */ /* * To use the debug system: * * If you are defining a new debug classification, simply add it to the #define * list here in the form of * * #define IWL_DL_xxxx VALUE * * where xxxx should be the name of the classification (for example, WEP). * * You then need to either add a IWL_xxxx_DEBUG() macro definition for your * classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want * to send output to that classification. * * The active debug levels can be accessed via files * * /sys/module/iwlwifi/parameters/debug * when CONFIG_IWLWIFI_DEBUG=y. * * /sys/kernel/debug/phy0/iwlwifi/debug/debug_level * when CONFIG_IWLWIFI_DEBUGFS=y. * */ /* 0x0000000F - 0x00000001 */ #define IWL_DL_INFO 0x00000001 #define IWL_DL_MAC80211 0x00000002 #define IWL_DL_HCMD 0x00000004 #define IWL_DL_STATE 0x00000008 /* 0x000000F0 - 0x00000010 */ #define IWL_DL_EEPROM 0x00000040 #define IWL_DL_RADIO 0x00000080 /* 0x00000F00 - 0x00000100 */ #define IWL_DL_POWER 0x00000100 #define IWL_DL_TEMP 0x00000200 #define IWL_DL_SCAN 0x00000800 /* 0x0000F000 - 0x00001000 */ #define IWL_DL_ASSOC 0x00001000 #define IWL_DL_DROP 0x00002000 #define IWL_DL_COEX 0x00008000 /* 0x000F0000 - 0x00010000 */ #define IWL_DL_FW 0x00010000 #define IWL_DL_RF_KILL 0x00020000 #define IWL_DL_FW_ERRORS 0x00040000 #define IWL_DL_LED 0x00080000 /* 0x00F00000 - 0x00100000 */ #define IWL_DL_RATE 0x00100000 #define IWL_DL_CALIB 0x00200000 #define IWL_DL_WEP 0x00400000 #define IWL_DL_TX 0x00800000 /* 0x0F000000 - 0x01000000 */ #define IWL_DL_RX 0x01000000 #define IWL_DL_ISR 0x02000000 #define IWL_DL_HT 0x04000000 /* 0xF0000000 - 0x10000000 */ #define IWL_DL_11H 0x10000000 #define IWL_DL_STATS 0x20000000 #define IWL_DL_TX_REPLY 0x40000000 #define IWL_DL_TX_QUEUES 0x80000000 #define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) #define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) #define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) #define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) #define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) #define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a) #define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a) #define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) #define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) #define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) #define IWL_DEBUG_EEPROM(d, f, a...) IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a) #define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a) #define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a) #define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a) #define IWL_DEBUG_FW_ERRORS(p, f, a...) IWL_DEBUG(p, IWL_DL_FW_ERRORS, f, ## a) #define IWL_DEBUG_DROP(p, f, a...) IWL_DEBUG(p, IWL_DL_DROP, f, ## a) #define IWL_DEBUG_DROP_LIMIT(p, f, a...) \ IWL_DEBUG_LIMIT(p, IWL_DL_DROP, f, ## a) #define IWL_DEBUG_COEX(p, f, a...) IWL_DEBUG(p, IWL_DL_COEX, f, ## a) #define IWL_DEBUG_RATE(p, f, a...) IWL_DEBUG(p, IWL_DL_RATE, f, ## a) #define IWL_DEBUG_RATE_LIMIT(p, f, a...) \ IWL_DEBUG_LIMIT(p, IWL_DL_RATE, f, ## a) #define IWL_DEBUG_ASSOC(p, f, a...) \ IWL_DEBUG(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) #define IWL_DEBUG_ASSOC_LIMIT(p, f, a...) \ IWL_DEBUG_LIMIT(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) #define IWL_DEBUG_HT(p, f, a...) IWL_DEBUG(p, IWL_DL_HT, f, ## a) #define IWL_DEBUG_STATS(p, f, a...) IWL_DEBUG(p, IWL_DL_STATS, f, ## a) #define IWL_DEBUG_STATS_LIMIT(p, f, a...) \ IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a) #define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a) #define IWL_DEBUG_TX_QUEUES(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_QUEUES, f, ## a) #define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a) #define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a) #define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a) #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-csr.h0000644000175000017500000005033112026211315023666 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __iwl_csr_h__ #define __iwl_csr_h__ /* * CSR (control and status registers) * * CSR registers are mapped directly into PCI bus space, and are accessible * whenever platform supplies power to device, even when device is in * low power states due to driver-invoked device resets * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. * * Use iwl_write32() and iwl_read32() family to access these registers; * these provide simple PCI bus access, without waking up the MAC. * Do not use iwl_write_direct32() family for these registers; * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. * The MAC (uCode processor, etc.) does not need to be powered up for accessing * the CSR registers. * * NOTE: Device does need to be awake in order to read this memory * via CSR_EEPROM and CSR_OTP registers */ #define CSR_BASE (0x000) #define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ #define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ #define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ #define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ #define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/ #define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */ #define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/ #define CSR_GP_CNTRL (CSR_BASE+0x024) /* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */ #define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) /* * Hardware revision info * Bit fields: * 31-16: Reserved * 15-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D * 1-0: "Dash" (-) value, as in A-1, etc. */ #define CSR_HW_REV (CSR_BASE+0x028) /* * EEPROM and OTP (one-time-programmable) memory reads * * NOTE: Device must be awake, initialized via apm_ops.init(), * in order to read. */ #define CSR_EEPROM_REG (CSR_BASE+0x02c) #define CSR_EEPROM_GP (CSR_BASE+0x030) #define CSR_OTP_GP_REG (CSR_BASE+0x034) #define CSR_GIO_REG (CSR_BASE+0x03C) #define CSR_GP_UCODE_REG (CSR_BASE+0x048) #define CSR_GP_DRIVER_REG (CSR_BASE+0x050) /* * UCODE-DRIVER GP (general purpose) mailbox registers. * SET/CLR registers set/clear bit(s) if "1" is written. */ #define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) #define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) #define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) #define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) #define CSR_LED_REG (CSR_BASE+0x094) #define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) #define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */ /* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) /* Analog phase-lock-loop configuration */ #define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) /* * CSR Hardware Revision Workaround Register. Indicates hardware rev; * "step" determines CCK backoff for txpower calculation. Used for 4965 only. * See also CSR_HW_REV register. * Bit fields: * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step * 1-0: "Dash" (-) value, as in C-1, etc. */ #define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) #define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) #define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) /* Bits for CSR_HW_IF_CONFIG_REG */ #define CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH (0x00000003) #define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP (0x0000000C) #define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0) #define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) #define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) #define CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00) #define CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000) #define CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000) #define CSR_HW_IF_CONFIG_REG_POS_MAC_DASH (0) #define CSR_HW_IF_CONFIG_REG_POS_MAC_STEP (2) #define CSR_HW_IF_CONFIG_REG_POS_BOARD_VER (6) #define CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE (10) #define CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12) #define CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14) #define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) #define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ #define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ #define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ #define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ /* interrupt flags in INTA, set by uCode or hardware (e.g. dma), * acknowledged (reset) by host writing "1" to flagged bits. */ #define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ #define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ #define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ #define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ #define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ #define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ #define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ #define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ #define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */ #define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ #define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ #define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ CSR_INT_BIT_HW_ERR | \ CSR_INT_BIT_FH_TX | \ CSR_INT_BIT_SW_ERR | \ CSR_INT_BIT_RF_KILL | \ CSR_INT_BIT_SW_RX | \ CSR_INT_BIT_WAKEUP | \ CSR_INT_BIT_ALIVE) /* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ #define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ #define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ #define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ #define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ #define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ #define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ #define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ CSR_FH_INT_BIT_RX_CHNL1 | \ CSR_FH_INT_BIT_RX_CHNL0) #define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ CSR_FH_INT_BIT_TX_CHNL0) /* GPIO */ #define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) #define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) #define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) /* RESET */ #define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) #define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) #define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) #define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) #define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) #define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) /* * GP (general purpose) CONTROL REGISTER * Bit fields: * 27: HW_RF_KILL_SW * Indicates state of (platform's) hardware RF-Kill switch * 26-24: POWER_SAVE_TYPE * Indicates current power-saving mode: * 000 -- No power saving * 001 -- MAC power-down * 010 -- PHY (radio) power-down * 011 -- Error * 9-6: SYS_CONFIG * Indicates current system configuration, reflecting pins on chip * as forced high/low by device circuit board. * 4: GOING_TO_SLEEP * Indicates MAC is entering a power-saving sleep power-down. * Not a good time to access device-internal resources. * 3: MAC_ACCESS_REQ * Host sets this to request and maintain MAC wakeup, to allow host * access to device-internal resources. Host must wait for * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR * device registers. * 2: INIT_DONE * Host sets this to put device into fully operational D0 power mode. * Host resets this after SW_RESET to put device into low power mode. * 0: MAC_CLOCK_READY * Indicates MAC (ucode processor, etc.) is powered up and can run. * Internal resources are accessible. * NOTE: This does not indicate that the processor is actually running. * NOTE: This does not indicate that device has completed * init or post-power-down restore of internal SRAM memory. * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that * SRAM is restored and uCode is in normal operation mode. * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and * do not need to save/restore it. * NOTE: After device reset, this bit remains "0" until host sets * INIT_DONE */ #define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) #define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) #define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) #define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) #define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) #define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) #define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) #define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) /* HW REV */ #define CSR_HW_REV_DASH(_val) (((_val) & 0x0000003) >> 0) #define CSR_HW_REV_STEP(_val) (((_val) & 0x000000C) >> 2) #define CSR_HW_REV_TYPE_MSK (0x000FFF0) #define CSR_HW_REV_TYPE_5300 (0x0000020) #define CSR_HW_REV_TYPE_5350 (0x0000030) #define CSR_HW_REV_TYPE_5100 (0x0000050) #define CSR_HW_REV_TYPE_5150 (0x0000040) #define CSR_HW_REV_TYPE_1000 (0x0000060) #define CSR_HW_REV_TYPE_6x00 (0x0000070) #define CSR_HW_REV_TYPE_6x50 (0x0000080) #define CSR_HW_REV_TYPE_6150 (0x0000084) #define CSR_HW_REV_TYPE_6x05 (0x00000B0) #define CSR_HW_REV_TYPE_6x30 CSR_HW_REV_TYPE_6x05 #define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05 #define CSR_HW_REV_TYPE_2x30 (0x00000C0) #define CSR_HW_REV_TYPE_2x00 (0x0000100) #define CSR_HW_REV_TYPE_105 (0x0000110) #define CSR_HW_REV_TYPE_135 (0x0000120) #define CSR_HW_REV_TYPE_NONE (0x00001F0) /* EEPROM REG */ #define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) #define CSR_EEPROM_REG_BIT_CMD (0x00000002) #define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) #define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) /* EEPROM GP */ #define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ #define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) #define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000) #define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001) #define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) #define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) /* One-time-programmable memory general purpose reg */ #define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */ #define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */ #define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */ #define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */ /* GP REG */ #define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ #define CSR_GP_REG_NO_POWER_SAVE (0x00000000) #define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) #define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) #define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) /* CSR GIO */ #define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) /* * UCODE-DRIVER GP (general purpose) mailbox register 1 * Host driver and uCode write and/or read this register to communicate with * each other. * Bit fields: * 4: UCODE_DISABLE * Host sets this to request permanent halt of uCode, same as * sending CARD_STATE command with "halt" bit set. * 3: CT_KILL_EXIT * Host sets this to request exit from CT_KILL state, i.e. host thinks * device temperature is low enough to continue normal operation. * 2: CMD_BLOCKED * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) * to release uCode to clear all Tx and command queues, enter * unassociated mode, and power down. * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. * 1: SW_BIT_RFKILL * Host sets this when issuing CARD_STATE command to request * device sleep. * 0: MAC_SLEEP * uCode sets this when preparing a power-saving power-down. * uCode resets this when power-up is complete and SRAM is sane. * NOTE: device saves internal SRAM data to host when powering down, * and must restore this data after powering back up. * MAC_SLEEP is the best indication that restore is complete. * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and * do not need to save/restore it. */ #define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) #define CSR_UCODE_SW_BIT_RFKILL (0x00000002) #define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) #define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) #define CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020) /* GP Driver */ #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003) #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000) #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001) #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) #define CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6 (0x00000004) #define CSR_GP_DRIVER_REG_BIT_6050_1x2 (0x00000008) #define CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER (0x00000080) /* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) #define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) /* LED */ #define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) #define CSR_LED_REG_TRUN_ON (0x78) #define CSR_LED_REG_TRUN_OFF (0x38) /* ANA_PLL */ #define CSR50_ANA_PLL_CFG_VAL (0x00880300) /* HPET MEM debug */ #define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) /* DRAM INT TABLE */ #define CSR_DRAM_INT_TBL_ENABLE (1 << 31) #define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) /* * HBUS (Host-side Bus) * * HBUS registers are mapped directly into PCI bus space, but are used * to indirectly access device's internal memory or registers that * may be powered-down. * * Use iwl_write_direct32()/iwl_read_direct32() family for these registers; * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ * to make sure the MAC (uCode processor, etc.) is powered up for accessing * internal resources. * * Do not use iwl_write32()/iwl_read32() family to access these registers; * these provide only simple PCI bus access, without waking up the MAC. */ #define HBUS_BASE (0x400) /* * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM * structures, error log, event log, verifying uCode load). * First write to address register, then read from or write to data register * to complete the job. Once the address register is set up, accesses to * data registers auto-increment the address by one dword. * Bit usage for address registers (read or write): * 0-31: memory address within device */ #define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) #define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) #define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) #define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) /* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ #define HBUS_TARG_MBX_C (HBUS_BASE+0x030) #define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) /* * Registers for accessing device's internal peripheral registers * (e.g. SCD, BSM, etc.). First write to address register, * then read from or write to data register to complete the job. * Bit usage for address registers (read or write): * 0-15: register address (offset) within device * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) */ #define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044) #define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048) #define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c) #define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) /* Used to enable DBGM */ #define HBUS_TARG_TEST_REG (HBUS_BASE+0x05c) /* * Per-Tx-queue write pointer (index, really!) * Indicates index to next TFD that driver will fill (1 past latest filled). * Bit usage: * 0-7: queue write index * 11-8: queue selector */ #define HBUS_TARG_WRPTR (HBUS_BASE+0x060) /********************************************************** * CSR values **********************************************************/ /* * host interrupt timeout value * used with setting interrupt coalescing timer * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit * * default interrupt coalescing timer is 64 x 32 = 2048 usecs * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs */ #define IWL_HOST_INT_TIMEOUT_MAX (0xFF) #define IWL_HOST_INT_TIMEOUT_DEF (0x40) #define IWL_HOST_INT_TIMEOUT_MIN (0x0) #define IWL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) #define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) #define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) #endif /* !__iwl_csr_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-config.h0000644000175000017500000002332612026211315024350 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __IWL_CONFIG_H__ #define __IWL_CONFIG_H__ #include #include enum iwl_device_family { IWL_DEVICE_FAMILY_UNDEFINED, IWL_DEVICE_FAMILY_1000, IWL_DEVICE_FAMILY_100, IWL_DEVICE_FAMILY_2000, IWL_DEVICE_FAMILY_2030, IWL_DEVICE_FAMILY_105, IWL_DEVICE_FAMILY_135, IWL_DEVICE_FAMILY_5000, IWL_DEVICE_FAMILY_5150, IWL_DEVICE_FAMILY_6000, IWL_DEVICE_FAMILY_6000i, IWL_DEVICE_FAMILY_6005, IWL_DEVICE_FAMILY_6030, IWL_DEVICE_FAMILY_6050, IWL_DEVICE_FAMILY_6150, }; /* * LED mode * IWL_LED_DEFAULT: use device default * IWL_LED_RF_STATE: turn LED on/off based on RF state * LED ON = RF ON * LED OFF = RF OFF * IWL_LED_BLINK: adjust led blink rate based on blink table * IWL_LED_DISABLE: led disabled */ enum iwl_led_mode { IWL_LED_DEFAULT, IWL_LED_RF_STATE, IWL_LED_BLINK, IWL_LED_DISABLE, }; /* * This is the threshold value of plcp error rate per 100mSecs. It is * used to set and check for the validity of plcp_delta. */ #define IWL_MAX_PLCP_ERR_THRESHOLD_MIN 1 #define IWL_MAX_PLCP_ERR_THRESHOLD_DEF 50 #define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF 100 #define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF 200 #define IWL_MAX_PLCP_ERR_THRESHOLD_MAX 255 #define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE 0 /* TX queue watchdog timeouts in mSecs */ #define IWL_WATCHDOG_DISABLED 0 #define IWL_DEF_WD_TIMEOUT 2000 #define IWL_LONG_WD_TIMEOUT 10000 #define IWL_MAX_WD_TIMEOUT 120000 /* Antenna presence definitions */ #define ANT_NONE 0x0 #define ANT_A BIT(0) #define ANT_B BIT(1) #define ANT_C BIT(2) #define ANT_AB (ANT_A | ANT_B) #define ANT_AC (ANT_A | ANT_C) #define ANT_BC (ANT_B | ANT_C) #define ANT_ABC (ANT_A | ANT_B | ANT_C) /* * @max_ll_items: max number of OTP blocks * @shadow_ram_support: shadow support for OTP memory * @led_compensation: compensate on the led on/off time per HW according * to the deviation to achieve the desired led frequency. * The detail algorithm is described in iwl-led.c * @chain_noise_num_beacons: number of beacons used to compute chain noise * @adv_thermal_throttle: support advance thermal throttle * @support_ct_kill_exit: support ct kill exit condition * @plcp_delta_threshold: plcp error rate threshold used to trigger * radio tuning when there is a high receiving plcp error rate * @chain_noise_scale: default chain noise scale used for gain computation * @wd_timeout: TX queues watchdog timeout * @max_event_log_size: size of event log buffer size for ucode event logging * @shadow_reg_enable: HW shadow register support * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up * @no_idle_support: do not support idle mode */ struct iwl_base_params { int eeprom_size; int num_of_queues; /* def: HW dependent */ /* for iwl_apm_init() */ u32 pll_cfg_val; const u16 max_ll_items; const bool shadow_ram_support; u16 led_compensation; bool adv_thermal_throttle; bool support_ct_kill_exit; u8 plcp_delta_threshold; s32 chain_noise_scale; unsigned int wd_timeout; u32 max_event_log_size; const bool shadow_reg_enable; const bool hd_v2; const bool no_idle_support; }; /* * @advanced_bt_coexist: support advanced bt coexist * @bt_init_traffic_load: specify initial bt traffic load * @bt_prio_boost: default bt priority boost value * @agg_time_limit: maximum number of uSec in aggregation * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode */ struct iwl_bt_params { bool advanced_bt_coexist; u8 bt_init_traffic_load; u32 bt_prio_boost; u16 agg_time_limit; bool bt_sco_disable; bool bt_session_2; }; /* * @use_rts_for_aggregation: use rts/cts protection for HT traffic * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40 */ struct iwl_ht_params { enum ieee80211_smps_mode smps_mode; const bool ht_greenfield_support; /* if used set to true */ bool use_rts_for_aggregation; u8 ht40_bands; }; /* * information on how to parse the EEPROM */ #define EEPROM_REG_BAND_1_CHANNELS 0x08 #define EEPROM_REG_BAND_2_CHANNELS 0x26 #define EEPROM_REG_BAND_3_CHANNELS 0x42 #define EEPROM_REG_BAND_4_CHANNELS 0x5C #define EEPROM_REG_BAND_5_CHANNELS 0x74 #define EEPROM_REG_BAND_24_HT40_CHANNELS 0x82 #define EEPROM_REG_BAND_52_HT40_CHANNELS 0x92 #define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80 #define EEPROM_REGULATORY_BAND_NO_HT40 0 struct iwl_eeprom_params { const u8 regulatory_bands[7]; bool enhanced_txpower; }; /** * struct iwl_cfg * @name: Offical name of the device * @fw_name_pre: Firmware filename prefix. The api version and extension * (.ucode) will be added to filename before loading from disk. The * filename is constructed as fw_name_pre.ucode. * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_ok: oldest version of the uCode API that is OK to load * without a warning, for use in transitions * @ucode_api_min: Lowest version of uCode API supported by driver. * @max_inst_size: The maximal length of the fw inst section * @max_data_size: The maximal length of the fw data section * @valid_tx_ant: valid transmit antenna * @valid_rx_ant: valid receive antenna * @eeprom_ver: EEPROM version * @eeprom_calib_ver: EEPROM calibration version * @lib: pointer to the lib ops * @base_params: pointer to basic parameters * @ht_params: point to ht patameters * @bt_params: pointer to bt parameters * @need_temp_offset_calib: need to perform temperature offset calibration * @no_xtal_calib: some devices do not need crystal calibration data, * don't send it to those * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) * @adv_pm: advance power management * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device * @temp_offset_v2: support v2 of temperature offset calibration * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs * and/or the uCode API version instead. */ struct iwl_cfg { /* params specific to an individual device within a device family */ const char *name; const char *fw_name_pre; const unsigned int ucode_api_max; const unsigned int ucode_api_ok; const unsigned int ucode_api_min; const enum iwl_device_family device_family; const u32 max_data_size; const u32 max_inst_size; u8 valid_tx_ant; u8 valid_rx_ant; u16 eeprom_ver; u16 eeprom_calib_ver; /* params not likely to change within a device family */ const struct iwl_base_params *base_params; /* params likely to change within a device family */ const struct iwl_ht_params *ht_params; const struct iwl_bt_params *bt_params; const struct iwl_eeprom_params *eeprom_params; const bool need_temp_offset_calib; /* if used set to true */ const bool no_xtal_calib; enum iwl_led_mode led_mode; const bool adv_pm; const bool rx_with_siso_diversity; const bool internal_wimax_coex; const bool temp_offset_v2; }; #endif /* __IWL_CONFIG_H__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/iwl-agn-hw.h0000644000175000017500000001154112026211315024260 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ /* * Please use this file (iwl-agn-hw.h) only for hardware-related definitions. */ #ifndef __iwl_agn_hw_h__ #define __iwl_agn_hw_h__ #define IWLAGN_RTC_INST_LOWER_BOUND (0x000000) #define IWLAGN_RTC_INST_UPPER_BOUND (0x020000) #define IWLAGN_RTC_DATA_LOWER_BOUND (0x800000) #define IWLAGN_RTC_DATA_UPPER_BOUND (0x80C000) #define IWLAGN_RTC_INST_SIZE (IWLAGN_RTC_INST_UPPER_BOUND - \ IWLAGN_RTC_INST_LOWER_BOUND) #define IWLAGN_RTC_DATA_SIZE (IWLAGN_RTC_DATA_UPPER_BOUND - \ IWLAGN_RTC_DATA_LOWER_BOUND) #define IWL60_RTC_INST_LOWER_BOUND (0x000000) #define IWL60_RTC_INST_UPPER_BOUND (0x040000) #define IWL60_RTC_DATA_LOWER_BOUND (0x800000) #define IWL60_RTC_DATA_UPPER_BOUND (0x814000) #define IWL60_RTC_INST_SIZE \ (IWL60_RTC_INST_UPPER_BOUND - IWL60_RTC_INST_LOWER_BOUND) #define IWL60_RTC_DATA_SIZE \ (IWL60_RTC_DATA_UPPER_BOUND - IWL60_RTC_DATA_LOWER_BOUND) /* RSSI to dBm */ #define IWLAGN_RSSI_OFFSET 44 #define IWLAGN_DEFAULT_TX_RETRY 15 #define IWLAGN_MGMT_DFAULT_RETRY_LIMIT 3 #define IWLAGN_RTS_DFAULT_RETRY_LIMIT 60 #define IWLAGN_BAR_DFAULT_RETRY_LIMIT 60 #define IWLAGN_LOW_RETRY_LIMIT 7 /* Limit range of txpower output target to be between these values */ #define IWLAGN_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */ #define IWLAGN_TX_POWER_TARGET_POWER_MAX (16) /* 16 dBm */ /* EEPROM */ #define IWLAGN_EEPROM_IMG_SIZE 2048 /* OTP */ /* lower blocks contain EEPROM image and calibration data */ #define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ /* high blocks contain PAPD data */ #define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ #define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ #define OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */ #define OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */ #define OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */ #define OTP_MAX_LL_ITEMS_2x00 (4) /* OTP blocks for 2x00 */ #define IWLAGN_NUM_QUEUES 20 #endif /* __iwl_agn_hw_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/Makefile0000644000175000017500000000106412026211315023574 0ustar mcgrofmcgrof# common obj-$(CONFIG_IWLWIFI) += iwlwifi.o iwlwifi-objs += iwl-io.o iwlwifi-objs += iwl-drv.o iwlwifi-objs += iwl-debug.o iwlwifi-objs += iwl-notif-wait.o iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o iwlwifi-objs += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src) obj-$(CONFIG_IWLDVM) += dvm/ CFLAGS_iwl-devtrace.o := -I$(src) compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/Kconfig0000644000175000017500000001132612026211315023441 0ustar mcgrofmcgrofconfig IWLWIFI tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) " depends on PCI && MAC80211 && HAS_IOMEM select FW_LOADER select NEW_LEDS select LEDS_CLASS select LEDS_TRIGGERS select MAC80211_LEDS select IWLDVM ---help--- Select to build the driver supporting the: Intel Wireless WiFi Link Next-Gen AGN This option enables support for use with the following hardware: Intel Wireless WiFi Link 6250AGN Adapter Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN) Intel WiFi Link 1000BGN Intel Wireless WiFi 5150AGN Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN Intel 6005 Series Wi-Fi Adapters Intel 6030 Series Wi-Fi Adapters Intel Wireless WiFi Link 6150BGN 2 Adapter Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN) Intel 2000 Series Wi-Fi Adapters This driver uses the kernel's mac80211 subsystem. In order to use this driver, you will need a microcode (uCode) image for it. You can obtain the microcode from: . The microcode is typically installed in /lib/firmware. You can look in the hotplug script /etc/hotplug/firmware.agent to determine which directory FIRMWARE_DIR is set to when the script runs. If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read . The module will be called iwlwifi. config IWLDVM tristate "Intel Wireless WiFi" depends on IWLWIFI menu "Debugging Options" depends on IWLWIFI config IWLWIFI_DEBUG bool "Enable full debugging output in the iwlwifi driver" depends on IWLWIFI ---help--- This option will enable debug tracing output for the iwlwifi drivers This will result in the kernel module being ~100k larger. You can control which debug output is sent to the kernel log by setting the value in /sys/module/iwlwifi/parameters/debug This entry will only exist if this option is enabled. To set a value, simply echo an 8-byte hex value to the same file: % echo 0x43fff > /sys/module/iwlwifi/parameters/debug You can find the list of debug mask values in: drivers/net/wireless/iwlwifi/iwl-debug.h If this is your first time using this driver, you should say Y here as the debug information can assist others in helping you resolve any problems you may encounter. config IWLWIFI_DEBUGFS bool "iwlwifi debugfs support" depends on IWLWIFI && MAC80211_DEBUGFS ---help--- Enable creation of debugfs files for the iwlwifi drivers. This is a low-impact option that allows getting insight into the driver's state at runtime. config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE bool "Experimental uCode support" depends on IWLWIFI && IWLWIFI_DEBUG ---help--- Enable use of experimental ucode for testing and debugging. config IWLWIFI_DEVICE_TRACING bool "iwlwifi device access tracing" depends on IWLWIFI depends on EVENT_TRACING help Say Y here to trace all commands, including TX frames and IO accesses, sent to the device. If you say yes, iwlwifi will register with the ftrace framework for event tracing and dump all this information to the ringbuffer, you may need to increase the ringbuffer size. See the ftrace documentation for more information. When tracing is not enabled, this option still has some (though rather small) overhead. If unsure, say Y so we can help you better when problems occur. endmenu config IWLWIFI_DEVICE_TESTMODE def_bool y depends on IWLWIFI depends on NL80211_TESTMODE help This option enables the testmode support for iwlwifi device through NL80211_TESTMODE. This provide the capabilities of enable user space validation applications to interacts with the device through the generic netlink message via NL80211_TESTMODE channel. config IWLWIFI_P2P def_bool y bool "iwlwifi experimental P2P support" depends on IWLWIFI help This option enables experimental P2P support for some devices based on microcode support. Since P2P support is still under development, this option may even enable it for some devices now that turn out to not support it in the future due to microcode restrictions. To determine if your microcode supports the experimental P2P offered by this option, check if the driver advertises AP support when it is loaded. Say Y only if you want to experiment with P2P. config IWLWIFI_EXPERIMENTAL_MFP bool "support MFP (802.11w) even if uCode doesn't advertise" depends on IWLWIFI help This option enables experimental MFP (802.11W) support even if the microcode doesn't advertise it. Say Y only if you want to experiment with MFP. compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/0000755000175000017500000000000012026211315022721 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/main.c0000644000175000017500000017017412026211315024023 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iwl-eeprom-read.h" #include "iwl-eeprom-parse.h" #include "iwl-io.h" #include "iwl-trans.h" #include "iwl-op-mode.h" #include "iwl-drv.h" #include "iwl-modparams.h" #include "iwl-prph.h" #include "dev.h" #include "calib.h" #include "agn.h" /****************************************************************************** * * module boiler plate * ******************************************************************************/ /* * module name, copyright, version, etc. */ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" #ifdef CONFIG_IWLWIFI_DEBUG #define VD "d" #else #define VD #endif #define DRV_VERSION IWLWIFI_VERSION VD MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); static const struct iwl_op_mode_ops iwl_dvm_ops; void iwl_update_chain_flags(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; for_each_context(priv, ctx) { iwlagn_set_rxon_chain(priv, ctx); if (ctx->active.rx_chain != ctx->staging.rx_chain) iwlagn_commit_rxon(priv, ctx); } } /* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ static void iwl_set_beacon_tim(struct iwl_priv *priv, struct iwl_tx_beacon_cmd *tx_beacon_cmd, u8 *beacon, u32 frame_size) { u16 tim_idx; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; /* * The index is relative to frame start but we start looking at the * variable-length part of the beacon. */ tim_idx = mgmt->u.beacon.variable - beacon; /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ while ((tim_idx < (frame_size - 2)) && (beacon[tim_idx] != WLAN_EID_TIM)) tim_idx += beacon[tim_idx+1] + 2; /* If TIM field was found, set variables */ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); tx_beacon_cmd->tim_size = beacon[tim_idx+1]; } else IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); } int iwlagn_send_beacon_cmd(struct iwl_priv *priv) { struct iwl_tx_beacon_cmd *tx_beacon_cmd; struct iwl_host_cmd cmd = { .id = REPLY_TX_BEACON, .flags = CMD_SYNC, }; struct ieee80211_tx_info *info; u32 frame_size; u32 rate_flags; u32 rate; /* * We have to set up the TX command, the TX Beacon command, and the * beacon contents. */ lockdep_assert_held(&priv->mutex); if (!priv->beacon_ctx) { IWL_ERR(priv, "trying to build beacon w/o beacon context!\n"); return 0; } if (WARN_ON(!priv->beacon_skb)) return -EINVAL; /* Allocate beacon command */ if (!priv->beacon_cmd) priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL); tx_beacon_cmd = priv->beacon_cmd; if (!tx_beacon_cmd) return -ENOMEM; frame_size = priv->beacon_skb->len; /* Set up TX command fields */ tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id; tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; /* Set up TX beacon command fields */ iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data, frame_size); /* Set up packet rate and flags */ info = IEEE80211_SKB_CB(priv->beacon_skb); /* * Let's set up the rate at least somewhat correctly; * it will currently not actually be used by the uCode, * it uses the broadcast station's rate instead. */ if (info->control.rates[0].idx < 0 || info->control.rates[0].flags & IEEE80211_TX_RC_MCS) rate = 0; else rate = info->control.rates[0].idx; priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, priv->eeprom_data->valid_tx_ant); rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); /* In mac80211, rates for 5 GHz start at 0 */ if (info->band == IEEE80211_BAND_5GHZ) rate += IWL_FIRST_OFDM_RATE; else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE) rate_flags |= RATE_MCS_CCK_MSK; tx_beacon_cmd->tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); /* Submit command */ cmd.len[0] = sizeof(*tx_beacon_cmd); cmd.data[0] = tx_beacon_cmd; cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; cmd.len[1] = frame_size; cmd.data[1] = priv->beacon_skb->data; cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; return iwl_dvm_send_cmd(priv, &cmd); } static void iwl_bg_beacon_update(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, beacon_update); struct sk_buff *beacon; mutex_lock(&priv->mutex); if (!priv->beacon_ctx) { IWL_ERR(priv, "updating beacon w/o beacon context!\n"); goto out; } if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) { /* * The ucode will send beacon notifications even in * IBSS mode, but we don't want to process them. But * we need to defer the type check to here due to * requiring locking around the beacon_ctx access. */ goto out; } /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif); if (!beacon) { IWL_ERR(priv, "update beacon failed -- keeping old\n"); goto out; } /* new beacon skb is allocated every time; dispose previous.*/ dev_kfree_skb(priv->beacon_skb); priv->beacon_skb = beacon; iwlagn_send_beacon_cmd(priv); out: mutex_unlock(&priv->mutex); } static void iwl_bg_bt_runtime_config(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, bt_runtime_config); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; /* dont send host command if rf-kill is on */ if (!iwl_is_ready_rf(priv)) return; iwlagn_send_advance_bt_config(priv); } static void iwl_bg_bt_full_concurrency(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, bt_full_concurrency); struct iwl_rxon_context *ctx; mutex_lock(&priv->mutex); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) goto out; /* dont send host command if rf-kill is on */ if (!iwl_is_ready_rf(priv)) goto out; IWL_DEBUG_INFO(priv, "BT coex in %s mode\n", priv->bt_full_concurrent ? "full concurrency" : "3-wire"); /* * LQ & RXON updated cmds must be sent before BT Config cmd * to avoid 3-wire collisions */ for_each_context(priv, ctx) { iwlagn_set_rxon_chain(priv, ctx); iwlagn_commit_rxon(priv, ctx); } iwlagn_send_advance_bt_config(priv); out: mutex_unlock(&priv->mutex); } int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) { struct iwl_statistics_cmd statistics_cmd = { .configuration_flags = clear ? IWL_STATS_CONF_CLEAR_STATS : 0, }; if (flags & CMD_ASYNC) return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, CMD_ASYNC, sizeof(struct iwl_statistics_cmd), &statistics_cmd); else return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, CMD_SYNC, sizeof(struct iwl_statistics_cmd), &statistics_cmd); } /** * iwl_bg_statistics_periodic - Timer callback to queue statistics * * This callback is provided in order to send a statistics request. * * This timer function is continually reset to execute within * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION * was received. We need to ensure we receive the statistics in order * to update the temperature used for calibrating the TXPOWER. */ static void iwl_bg_statistics_periodic(unsigned long data) { struct iwl_priv *priv = (struct iwl_priv *)data; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; /* dont send host command if rf-kill is on */ if (!iwl_is_ready_rf(priv)) return; iwl_send_statistics_request(priv, CMD_ASYNC, false); } static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, u32 start_idx, u32 num_events, u32 capacity, u32 mode) { u32 i; u32 ptr; /* SRAM byte address of log data */ u32 ev, time, data; /* event log data */ unsigned long reg_flags; if (mode == 0) ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32)); else ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); /* Make sure device is powered up for SRAM reads */ spin_lock_irqsave(&priv->trans->reg_lock, reg_flags); if (unlikely(!iwl_grab_nic_access(priv->trans))) { spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags); return; } /* Set starting address; reads will auto-increment */ iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr); /* * Refuse to read more than would have fit into the log from * the current start_idx. This used to happen due to the race * described below, but now WARN because the code below should * prevent it from happening here. */ if (WARN_ON(num_events > capacity - start_idx)) num_events = capacity - start_idx; /* * "time" is actually "data" for mode 0 (no timestamp). * place event id # at far right for easier visual parsing. */ for (i = 0; i < num_events; i++) { ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); if (mode == 0) { trace_iwlwifi_dev_ucode_cont_event( priv->trans->dev, 0, time, ev); } else { data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); trace_iwlwifi_dev_ucode_cont_event( priv->trans->dev, time, data, ev); } } /* Allow device to power down */ iwl_release_nic_access(priv->trans); spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags); } static void iwl_continuous_event_trace(struct iwl_priv *priv) { u32 capacity; /* event log capacity in # entries */ struct { u32 capacity; u32 mode; u32 wrap_counter; u32 write_counter; } __packed read; u32 base; /* SRAM byte address of event log header */ u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ u32 num_wraps; /* # times uCode wrapped to top of log */ u32 next_entry; /* index of next entry to be written by uCode */ base = priv->device_pointers.log_event_table; if (iwlagn_hw_valid_rtc_data_addr(base)) { iwl_read_targ_mem_bytes(priv->trans, base, &read, sizeof(read)); capacity = read.capacity; mode = read.mode; num_wraps = read.wrap_counter; next_entry = read.write_counter; } else return; /* * Unfortunately, the uCode doesn't use temporary variables. * Therefore, it can happen that we read next_entry == capacity, * which really means next_entry == 0. */ if (unlikely(next_entry == capacity)) next_entry = 0; /* * Additionally, the uCode increases the write pointer before * the wraps counter, so if the write pointer is smaller than * the old write pointer (wrap occurred) but we read that no * wrap occurred, we actually read between the next_entry and * num_wraps update (this does happen in practice!!) -- take * that into account by increasing num_wraps. */ if (unlikely(next_entry < priv->event_log.next_entry && num_wraps == priv->event_log.num_wraps)) num_wraps++; if (num_wraps == priv->event_log.num_wraps) { iwl_print_cont_event_trace( priv, base, priv->event_log.next_entry, next_entry - priv->event_log.next_entry, capacity, mode); priv->event_log.non_wraps_count++; } else { if (num_wraps - priv->event_log.num_wraps > 1) priv->event_log.wraps_more_count++; else priv->event_log.wraps_once_count++; trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev, num_wraps - priv->event_log.num_wraps, next_entry, priv->event_log.next_entry); if (next_entry < priv->event_log.next_entry) { iwl_print_cont_event_trace( priv, base, priv->event_log.next_entry, capacity - priv->event_log.next_entry, capacity, mode); iwl_print_cont_event_trace( priv, base, 0, next_entry, capacity, mode); } else { iwl_print_cont_event_trace( priv, base, next_entry, capacity - next_entry, capacity, mode); iwl_print_cont_event_trace( priv, base, 0, next_entry, capacity, mode); } } priv->event_log.num_wraps = num_wraps; priv->event_log.next_entry = next_entry; } /** * iwl_bg_ucode_trace - Timer callback to log ucode event * * The timer is continually set to execute every * UCODE_TRACE_PERIOD milliseconds after the last timer expired * this function is to perform continuous uCode event logging operation * if enabled */ static void iwl_bg_ucode_trace(unsigned long data) { struct iwl_priv *priv = (struct iwl_priv *)data; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (priv->event_log.ucode_trace) { iwl_continuous_event_trace(priv); /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */ mod_timer(&priv->ucode_trace, jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); } } static void iwl_bg_tx_flush(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, tx_flush); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; /* do nothing if rf-kill is on */ if (!iwl_is_ready_rf(priv)) return; IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n"); iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); } /* * queue/FIFO/AC mapping definitions */ static const u8 iwlagn_bss_ac_to_fifo[] = { IWL_TX_FIFO_VO, IWL_TX_FIFO_VI, IWL_TX_FIFO_BE, IWL_TX_FIFO_BK, }; static const u8 iwlagn_bss_ac_to_queue[] = { 0, 1, 2, 3, }; static const u8 iwlagn_pan_ac_to_fifo[] = { IWL_TX_FIFO_VO_IPAN, IWL_TX_FIFO_VI_IPAN, IWL_TX_FIFO_BE_IPAN, IWL_TX_FIFO_BK_IPAN, }; static const u8 iwlagn_pan_ac_to_queue[] = { 7, 6, 5, 4, }; static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) { int i; /* * The default context is always valid, * the PAN context depends on uCode. */ priv->valid_contexts = BIT(IWL_RXON_CTX_BSS); if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN); for (i = 0; i < NUM_IWL_RXON_CTX; i++) priv->contexts[i].ctxid = i; priv->contexts[IWL_RXON_CTX_BSS].always_active = true; priv->contexts[IWL_RXON_CTX_BSS].is_active = true; priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON; priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING; priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC; priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM; priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID; priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY; priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes = BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR); priv->contexts[IWL_RXON_CTX_BSS].interface_modes = BIT(NL80211_IFTYPE_STATION); priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP; priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS; priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS; priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS; memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue, iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue)); memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo, iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo)); priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON; priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd = REPLY_WIPAN_RXON_TIMING; priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd = REPLY_WIPAN_RXON_ASSOC; priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM; priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN; priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY; priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID; priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION; priv->contexts[IWL_RXON_CTX_PAN].interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P) priv->contexts[IWL_RXON_CTX_PAN].interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP; priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA; priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P; memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue, iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue)); memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo, iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo)); priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE; BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); } static void iwl_rf_kill_ct_config(struct iwl_priv *priv) { struct iwl_ct_kill_config cmd; struct iwl_ct_kill_throttling_config adv_cmd; int ret = 0; iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); priv->thermal_throttle.ct_kill_toggle = false; if (priv->cfg->base_params->support_ct_kill_exit) { adv_cmd.critical_temperature_enter = cpu_to_le32(priv->hw_params.ct_kill_threshold); adv_cmd.critical_temperature_exit = cpu_to_le32(priv->hw_params.ct_kill_exit_threshold); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, CMD_SYNC, sizeof(adv_cmd), &adv_cmd); if (ret) IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); else IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " "succeeded, critical temperature enter is %d," "exit is %d\n", priv->hw_params.ct_kill_threshold, priv->hw_params.ct_kill_exit_threshold); } else { cmd.critical_temperature_R = cpu_to_le32(priv->hw_params.ct_kill_threshold); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, CMD_SYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); else IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " "succeeded, " "critical temperature is %d\n", priv->hw_params.ct_kill_threshold); } } static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg) { struct iwl_calib_cfg_cmd calib_cfg_cmd; struct iwl_host_cmd cmd = { .id = CALIBRATION_CFG_CMD, .len = { sizeof(struct iwl_calib_cfg_cmd), }, .data = { &calib_cfg_cmd, }, }; memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL; calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg); return iwl_dvm_send_cmd(priv, &cmd); } static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) { struct iwl_tx_ant_config_cmd tx_ant_cmd = { .valid = cpu_to_le32(valid_tx_ant), }; if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) { IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, CMD_SYNC, sizeof(struct iwl_tx_ant_config_cmd), &tx_ant_cmd); } else { IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n"); return -EOPNOTSUPP; } } static void iwl_send_bt_config(struct iwl_priv *priv) { struct iwl_bt_cmd bt_cmd = { .lead_time = BT_LEAD_TIME_DEF, .max_kill = BT_MAX_KILL_DEF, .kill_ack_mask = 0, .kill_cts_mask = 0, }; if (!iwlwifi_mod_params.bt_coex_active) bt_cmd.flags = BT_COEX_DISABLE; else bt_cmd.flags = BT_COEX_ENABLE; priv->bt_enable_flag = bt_cmd.flags; IWL_DEBUG_INFO(priv, "BT coex %s\n", (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd)) IWL_ERR(priv, "failed to send BT Coex Config\n"); } /** * iwl_alive_start - called after REPLY_ALIVE notification received * from protocol/runtime uCode (initialization uCode's * Alive gets handled by iwl_init_alive_start()). */ int iwl_alive_start(struct iwl_priv *priv) { int ret = 0; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); /* After the ALIVE response, we can send host commands to the uCode */ set_bit(STATUS_ALIVE, &priv->status); if (iwl_is_rfkill(priv)) return -ERFKILL; if (priv->event_log.ucode_trace) { /* start collecting data now */ mod_timer(&priv->ucode_trace, jiffies); } /* download priority table before any calibration request */ if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) { /* Configure Bluetooth device coexistence support */ if (priv->cfg->bt_params->bt_sco_disable) priv->bt_enable_pspoll = false; else priv->bt_enable_pspoll = true; priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; iwlagn_send_advance_bt_config(priv); priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS; priv->cur_rssi_ctx = NULL; iwl_send_prio_tbl(priv); /* FIXME: w/a to force change uCode BT state machine */ ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret; ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE, BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret; } else { /* * default is 2-wire BT coexexistence support */ iwl_send_bt_config(priv); } /* * Perform runtime calibrations, including DC calibration. */ iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX); ieee80211_wake_queues(priv->hw); /* Configure Tx antenna selection based on H/W config */ iwlagn_send_tx_ant_config(priv, priv->eeprom_data->valid_tx_ant); if (iwl_is_associated_ctx(ctx) && !priv->wowlan) { struct iwl_rxon_cmd *active_rxon = (struct iwl_rxon_cmd *)&ctx->active; /* apply any changes in staging */ ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; } else { struct iwl_rxon_context *tmp; /* Initialize our rx_config data */ for_each_context(priv, tmp) iwl_connection_init_rx_config(priv, tmp); iwlagn_set_rxon_chain(priv, ctx); } if (!priv->wowlan) { /* WoWLAN ucode will not reply in the same way, skip it */ iwl_reset_run_time_calib(priv); } set_bit(STATUS_READY, &priv->status); /* Configure the adapter for unassociated operation */ ret = iwlagn_commit_rxon(priv, ctx); if (ret) return ret; /* At this point, the NIC is initialized and operational */ iwl_rf_kill_ct_config(priv); IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); return iwl_power_update_mode(priv, true); } /** * iwl_clear_driver_stations - clear knowledge of all stations from driver * @priv: iwl priv struct * * This is called during iwl_down() to make sure that in the case * we're coming there from a hardware restart mac80211 will be * able to reconfigure stations -- if we're getting there in the * normal down flow then the stations will already be cleared. */ static void iwl_clear_driver_stations(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; spin_lock_bh(&priv->sta_lock); memset(priv->stations, 0, sizeof(priv->stations)); priv->num_stations = 0; priv->ucode_key_table = 0; for_each_context(priv, ctx) { /* * Remove all key information that is not stored as part * of station information since mac80211 may not have had * a chance to remove all the keys. When device is * reconfigured by mac80211 after an error all keys will * be reconfigured. */ memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys)); ctx->key_mapping_keys = 0; } spin_unlock_bh(&priv->sta_lock); } void iwl_down(struct iwl_priv *priv) { int exit_pending; IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n"); lockdep_assert_held(&priv->mutex); iwl_scan_cancel_timeout(priv, 200); /* * If active, scanning won't cancel it, so say it expired. * No race since we hold the mutex here and a new one * can't come in at this time. */ if (priv->ucode_loaded && priv->cur_ucode != IWL_UCODE_INIT) ieee80211_remain_on_channel_expired(priv->hw); exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); iwl_clear_ucode_stations(priv, NULL); iwl_dealloc_bcast_stations(priv); iwl_clear_driver_stations(priv); /* reset BT coex data */ priv->bt_status = 0; priv->cur_rssi_ctx = NULL; priv->bt_is_sco = 0; if (priv->cfg->bt_params) priv->bt_traffic_load = priv->cfg->bt_params->bt_init_traffic_load; else priv->bt_traffic_load = 0; priv->bt_full_concurrent = false; priv->bt_ci_compliance = 0; /* Wipe out the EXIT_PENDING status bit if we are not actually * exiting the module */ if (!exit_pending) clear_bit(STATUS_EXIT_PENDING, &priv->status); if (priv->mac80211_registered) ieee80211_stop_queues(priv->hw); priv->ucode_loaded = false; iwl_trans_stop_device(priv->trans); /* Set num_aux_in_flight must be done after the transport is stopped */ atomic_set(&priv->num_aux_in_flight, 0); /* Clear out all status bits but a few that are stable across reset */ priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << STATUS_RF_KILL_HW | test_bit(STATUS_FW_ERROR, &priv->status) << STATUS_FW_ERROR | test_bit(STATUS_EXIT_PENDING, &priv->status) << STATUS_EXIT_PENDING; dev_kfree_skb(priv->beacon_skb); priv->beacon_skb = NULL; } /***************************************************************************** * * Workqueue callbacks * *****************************************************************************/ static void iwl_bg_run_time_calib_work(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, run_time_calib_work); mutex_lock(&priv->mutex); if (test_bit(STATUS_EXIT_PENDING, &priv->status) || test_bit(STATUS_SCANNING, &priv->status)) { mutex_unlock(&priv->mutex); return; } if (priv->start_calib) { iwl_chain_noise_calibration(priv); iwl_sensitivity_calibration(priv); } mutex_unlock(&priv->mutex); } void iwlagn_prepare_restart(struct iwl_priv *priv) { bool bt_full_concurrent; u8 bt_ci_compliance; u8 bt_load; u8 bt_status; bool bt_is_sco; int i; lockdep_assert_held(&priv->mutex); priv->is_open = 0; /* * __iwl_down() will clear the BT status variables, * which is correct, but when we restart we really * want to keep them so restore them afterwards. * * The restart process will later pick them up and * re-configure the hw when we reconfigure the BT * command. */ bt_full_concurrent = priv->bt_full_concurrent; bt_ci_compliance = priv->bt_ci_compliance; bt_load = priv->bt_traffic_load; bt_status = priv->bt_status; bt_is_sco = priv->bt_is_sco; iwl_down(priv); priv->bt_full_concurrent = bt_full_concurrent; priv->bt_ci_compliance = bt_ci_compliance; priv->bt_traffic_load = bt_load; priv->bt_status = bt_status; priv->bt_is_sco = bt_is_sco; /* reset aggregation queues */ for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++) priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; /* and stop counts */ for (i = 0; i < IWL_MAX_HW_QUEUES; i++) atomic_set(&priv->queue_stop_count[i], 0); memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc)); } static void iwl_bg_restart(struct work_struct *data) { struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) { mutex_lock(&priv->mutex); iwlagn_prepare_restart(priv); mutex_unlock(&priv->mutex); iwl_cancel_deferred_work(priv); if (priv->mac80211_registered) ieee80211_restart_hw(priv->hw); else IWL_ERR(priv, "Cannot request restart before registrating with mac80211"); } else { WARN_ON(1); } } void iwlagn_disable_roc(struct iwl_priv *priv) { struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; lockdep_assert_held(&priv->mutex); if (!priv->hw_roc_setup) return; ctx->staging.dev_type = RXON_DEV_TYPE_P2P; ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; priv->hw_roc_channel = NULL; memset(ctx->staging.node_addr, 0, ETH_ALEN); iwlagn_commit_rxon(priv, ctx); ctx->is_active = false; priv->hw_roc_setup = false; } static void iwlagn_disable_roc_work(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, hw_roc_disable_work.work); mutex_lock(&priv->mutex); iwlagn_disable_roc(priv); mutex_unlock(&priv->mutex); } /***************************************************************************** * * driver setup and teardown * *****************************************************************************/ static void iwl_setup_deferred_work(struct iwl_priv *priv) { priv->workqueue = create_singlethread_workqueue(DRV_NAME); INIT_WORK(&priv->restart, iwl_bg_restart); INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update); INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush); INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency); INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config); INIT_DELAYED_WORK(&priv->hw_roc_disable_work, iwlagn_disable_roc_work); iwl_setup_scan_deferred_work(priv); if (priv->cfg->bt_params) iwlagn_bt_setup_deferred_work(priv); init_timer(&priv->statistics_periodic); priv->statistics_periodic.data = (unsigned long)priv; priv->statistics_periodic.function = iwl_bg_statistics_periodic; init_timer(&priv->ucode_trace); priv->ucode_trace.data = (unsigned long)priv; priv->ucode_trace.function = iwl_bg_ucode_trace; } void iwl_cancel_deferred_work(struct iwl_priv *priv) { if (priv->cfg->bt_params) iwlagn_bt_cancel_deferred_work(priv); cancel_work_sync(&priv->run_time_calib_work); cancel_work_sync(&priv->beacon_update); iwl_cancel_scan_deferred_work(priv); cancel_work_sync(&priv->bt_full_concurrency); cancel_work_sync(&priv->bt_runtime_config); cancel_delayed_work_sync(&priv->hw_roc_disable_work); del_timer_sync(&priv->statistics_periodic); del_timer_sync(&priv->ucode_trace); } static int iwl_init_drv(struct iwl_priv *priv) { spin_lock_init(&priv->sta_lock); mutex_init(&priv->mutex); INIT_LIST_HEAD(&priv->calib_results); priv->band = IEEE80211_BAND_2GHZ; priv->plcp_delta_threshold = priv->cfg->base_params->plcp_delta_threshold; priv->iw_mode = NL80211_IFTYPE_STATION; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; priv->agg_tids_count = 0; priv->ucode_owner = IWL_OWNERSHIP_DRIVER; priv->rx_statistics_jiffies = jiffies; /* Choose which receivers/antennas to use */ iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]); iwl_init_scan_params(priv); /* init bt coex */ if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) { priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; priv->bt_on_thresh = BT_ON_THRESHOLD_DEF; priv->bt_duration = BT_DURATION_LIMIT_DEF; priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; } return 0; } static void iwl_uninit_drv(struct iwl_priv *priv) { kfree(priv->scan_cmd); kfree(priv->beacon_cmd); kfree(rcu_dereference_raw(priv->noa_data)); iwl_calib_free_results(priv); #ifdef CONFIG_IWLWIFI_DEBUGFS kfree(priv->wowlan_sram); #endif } static void iwl_set_hw_params(struct iwl_priv *priv) { if (priv->cfg->ht_params) priv->hw_params.use_rts_for_aggregation = priv->cfg->ht_params->use_rts_for_aggregation; /* Device-specific setup */ priv->lib->set_hw_params(priv); } /* show what optional capabilities we have */ static void iwl_option_config(struct iwl_priv *priv) { #ifdef CONFIG_IWLWIFI_DEBUG IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n"); #else IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n"); #endif #ifdef CONFIG_IWLWIFI_DEBUGFS IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n"); #else IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n"); #endif #ifdef CONFIG_IWLWIFI_DEVICE_TRACING IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n"); #else IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); #endif #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE enabled\n"); #else IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE disabled\n"); #endif #ifdef CONFIG_IWLWIFI_P2P IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n"); #else IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n"); #endif } static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) { u16 radio_cfg; priv->eeprom_data->sku = priv->eeprom_data->sku; if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE && !priv->cfg->ht_params) { IWL_ERR(priv, "Invalid 11n configuration\n"); return -EINVAL; } if (!priv->eeprom_data->sku) { IWL_ERR(priv, "Invalid device sku\n"); return -EINVAL; } IWL_INFO(priv, "Device SKU: 0x%X\n", priv->eeprom_data->sku); radio_cfg = priv->eeprom_data->radio_cfg; priv->hw_params.tx_chains_num = num_of_ant(priv->eeprom_data->valid_tx_ant); if (priv->cfg->rx_with_siso_diversity) priv->hw_params.rx_chains_num = 1; else priv->hw_params.rx_chains_num = num_of_ant(priv->eeprom_data->valid_rx_ant); IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", priv->eeprom_data->valid_tx_ant, priv->eeprom_data->valid_rx_ant); return 0; } static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { struct iwl_priv *priv; struct ieee80211_hw *hw; struct iwl_op_mode *op_mode; u16 num_mac; u32 ucode_flags; struct iwl_trans_config trans_cfg; static const u8 no_reclaim_cmds[] = { REPLY_RX_PHY_CMD, REPLY_RX_MPDU_CMD, REPLY_COMPRESSED_BA, STATISTICS_NOTIFICATION, REPLY_TX, }; int i; /************************ * 1. Allocating HW data ************************/ hw = iwl_alloc_all(); if (!hw) { pr_err("%s: Cannot allocate network device\n", cfg->name); goto out; } op_mode = hw->priv; op_mode->ops = &iwl_dvm_ops; priv = IWL_OP_MODE_GET_DVM(op_mode); priv->trans = trans; priv->dev = trans->dev; priv->cfg = cfg; priv->fw = fw; switch (priv->cfg->device_family) { case IWL_DEVICE_FAMILY_1000: case IWL_DEVICE_FAMILY_100: priv->lib = &iwl1000_lib; break; case IWL_DEVICE_FAMILY_2000: case IWL_DEVICE_FAMILY_105: priv->lib = &iwl2000_lib; break; case IWL_DEVICE_FAMILY_2030: case IWL_DEVICE_FAMILY_135: priv->lib = &iwl2030_lib; break; case IWL_DEVICE_FAMILY_5000: priv->lib = &iwl5000_lib; break; case IWL_DEVICE_FAMILY_5150: priv->lib = &iwl5150_lib; break; case IWL_DEVICE_FAMILY_6000: case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6000i: case IWL_DEVICE_FAMILY_6050: case IWL_DEVICE_FAMILY_6150: priv->lib = &iwl6000_lib; break; case IWL_DEVICE_FAMILY_6030: priv->lib = &iwl6030_lib; break; default: break; } if (WARN_ON(!priv->lib)) goto out_free_hw; /* * Populate the state variables that the transport layer needs * to know about. */ trans_cfg.op_mode = op_mode; trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; if (!iwlwifi_mod_params.wd_disable) trans_cfg.queue_watchdog_timeout = priv->cfg->base_params->wd_timeout; else trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; trans_cfg.command_names = iwl_dvm_cmd_strings; trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < priv->cfg->base_params->num_of_queues); ucode_flags = fw->ucode_capa.flags; #ifndef CONFIG_IWLWIFI_P2P ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; #endif if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; } else { priv->sta_key_max_num = STA_KEY_MAX_NUM; trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; } /* Configure transport layer */ iwl_trans_configure(priv->trans, &trans_cfg); /* At this point both hw and priv are allocated. */ SET_IEEE80211_DEV(priv->hw, priv->trans->dev); iwl_option_config(priv); IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); /* is antenna coupling more than 35dB ? */ priv->bt_ant_couple_ok = (iwlwifi_mod_params.ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD) ? true : false; /* enable/disable bt channel inhibition */ priv->bt_ch_announce = iwlwifi_mod_params.bt_ch_announce; IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n", (priv->bt_ch_announce) ? "On" : "Off"); /* these spin locks will be used in apm_ops.init and EEPROM access * we should init now */ spin_lock_init(&priv->statistics.lock); /*********************** * 2. Read REV register ***********************/ IWL_INFO(priv, "Detected %s, REV=0x%X\n", priv->cfg->name, priv->trans->hw_rev); if (iwl_trans_start_hw(priv->trans)) goto out_free_hw; /* Read the EEPROM */ if (iwl_read_eeprom(priv->trans, &priv->eeprom_blob, &priv->eeprom_blob_size)) { IWL_ERR(priv, "Unable to init EEPROM\n"); goto out_free_hw; } /* Reset chip to save power until we load uCode during "up". */ iwl_trans_stop_hw(priv->trans, false); priv->eeprom_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg, priv->eeprom_blob, priv->eeprom_blob_size); if (!priv->eeprom_data) goto out_free_eeprom_blob; if (iwl_eeprom_check_version(priv->eeprom_data, priv->trans)) goto out_free_eeprom; if (iwl_eeprom_init_hw_params(priv)) goto out_free_eeprom; /* extract MAC Address */ memcpy(priv->addresses[0].addr, priv->eeprom_data->hw_addr, ETH_ALEN); IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); priv->hw->wiphy->addresses = priv->addresses; priv->hw->wiphy->n_addresses = 1; num_mac = priv->eeprom_data->n_hw_addrs; if (num_mac > 1) { memcpy(priv->addresses[1].addr, priv->addresses[0].addr, ETH_ALEN); priv->addresses[1].addr[5]++; priv->hw->wiphy->n_addresses++; } /************************ * 4. Setup HW constants ************************/ iwl_set_hw_params(priv); if (!(priv->eeprom_data->sku & EEPROM_SKU_CAP_IPAN_ENABLE)) { IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN"); ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; /* * if not PAN, then don't support P2P -- might be a uCode * packaging bug or due to the eeprom check above */ ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; priv->sta_key_max_num = STA_KEY_MAX_NUM; trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; /* Configure transport layer again*/ iwl_trans_configure(priv->trans, &trans_cfg); } /******************* * 5. Setup priv *******************/ for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; if (i < IWLAGN_FIRST_AMPDU_QUEUE && i != IWL_DEFAULT_CMD_QUEUE_NUM && i != IWL_IPAN_CMD_QUEUE_NUM) priv->queue_to_mac80211[i] = i; atomic_set(&priv->queue_stop_count[i], 0); } if (iwl_init_drv(priv)) goto out_free_eeprom; /* At this point both hw and priv are initialized. */ /******************** * 6. Setup services ********************/ iwl_setup_deferred_work(priv); iwl_setup_rx_handlers(priv); iwl_testmode_init(priv); iwl_power_initialize(priv); iwl_tt_initialize(priv); snprintf(priv->hw->wiphy->fw_version, sizeof(priv->hw->wiphy->fw_version), "%s", fw->fw_version); priv->new_scan_threshold_behaviour = !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN); priv->phy_calib_chain_noise_reset_cmd = fw->ucode_capa.standard_phy_calibration_size; priv->phy_calib_chain_noise_gain_cmd = fw->ucode_capa.standard_phy_calibration_size + 1; /* initialize all valid contexts */ iwl_init_context(priv, ucode_flags); /************************************************** * This is still part of probe() in a sense... * * 7. Setup and register with mac80211 and debugfs **************************************************/ if (iwlagn_mac_setup_register(priv, &fw->ucode_capa)) goto out_destroy_workqueue; if (iwl_dbgfs_register(priv, dbgfs_dir)) goto out_mac80211_unregister; return op_mode; out_mac80211_unregister: iwlagn_mac_unregister(priv); out_destroy_workqueue: iwl_tt_exit(priv); iwl_testmode_free(priv); iwl_cancel_deferred_work(priv); destroy_workqueue(priv->workqueue); priv->workqueue = NULL; iwl_uninit_drv(priv); out_free_eeprom_blob: kfree(priv->eeprom_blob); out_free_eeprom: iwl_free_eeprom_data(priv->eeprom_data); out_free_hw: ieee80211_free_hw(priv->hw); out: op_mode = NULL; return op_mode; } static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); iwl_testmode_free(priv); iwlagn_mac_unregister(priv); iwl_tt_exit(priv); /*This will stop the queues, move the device to low power state */ priv->ucode_loaded = false; iwl_trans_stop_device(priv->trans); kfree(priv->eeprom_blob); iwl_free_eeprom_data(priv->eeprom_data); /*netif_stop_queue(dev); */ flush_workqueue(priv->workqueue); /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes * priv->workqueue... so we can't take down the workqueue * until now... */ destroy_workqueue(priv->workqueue); priv->workqueue = NULL; iwl_uninit_drv(priv); dev_kfree_skb(priv->beacon_skb); iwl_trans_stop_hw(priv->trans, true); ieee80211_free_hw(priv->hw); } static const char * const desc_lookup_text[] = { "OK", "FAIL", "BAD_PARAM", "BAD_CHECKSUM", "NMI_INTERRUPT_WDG", "SYSASSERT", "FATAL_ERROR", "BAD_COMMAND", "HW_ERROR_TUNE_LOCK", "HW_ERROR_TEMPERATURE", "ILLEGAL_CHAN_FREQ", "VCC_NOT_STABLE", "FH_ERROR", "NMI_INTERRUPT_HOST", "NMI_INTERRUPT_ACTION_PT", "NMI_INTERRUPT_UNKNOWN", "UCODE_VERSION_MISMATCH", "HW_ERROR_ABS_LOCK", "HW_ERROR_CAL_LOCK_FAIL", "NMI_INTERRUPT_INST_ACTION_PT", "NMI_INTERRUPT_DATA_ACTION_PT", "NMI_TRM_HW_ER", "NMI_INTERRUPT_TRM", "NMI_INTERRUPT_BREAK_POINT", "DEBUG_0", "DEBUG_1", "DEBUG_2", "DEBUG_3", }; static struct { char *name; u8 num; } advanced_lookup[] = { { "NMI_INTERRUPT_WDG", 0x34 }, { "SYSASSERT", 0x35 }, { "UCODE_VERSION_MISMATCH", 0x37 }, { "BAD_COMMAND", 0x38 }, { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, { "FATAL_ERROR", 0x3D }, { "NMI_TRM_HW_ERR", 0x46 }, { "NMI_INTERRUPT_TRM", 0x4C }, { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, { "NMI_INTERRUPT_HOST", 0x66 }, { "NMI_INTERRUPT_ACTION_PT", 0x7C }, { "NMI_INTERRUPT_UNKNOWN", 0x84 }, { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, { "ADVANCED_SYSASSERT", 0 }, }; static const char *desc_lookup(u32 num) { int i; int max = ARRAY_SIZE(desc_lookup_text); if (num < max) return desc_lookup_text[num]; max = ARRAY_SIZE(advanced_lookup) - 1; for (i = 0; i < max; i++) { if (advanced_lookup[i].num == num) break; } return advanced_lookup[i].name; } #define ERROR_START_OFFSET (1 * sizeof(u32)) #define ERROR_ELEM_SIZE (7 * sizeof(u32)) static void iwl_dump_nic_error_log(struct iwl_priv *priv) { struct iwl_trans *trans = priv->trans; u32 base; struct iwl_error_event_table table; base = priv->device_pointers.error_event_table; if (priv->cur_ucode == IWL_UCODE_INIT) { if (!base) base = priv->fw->init_errlog_ptr; } else { if (!base) base = priv->fw->inst_errlog_ptr; } if (!iwlagn_hw_valid_rtc_data_addr(base)) { IWL_ERR(priv, "Not valid error log pointer 0x%08X for %s uCode\n", base, (priv->cur_ucode == IWL_UCODE_INIT) ? "Init" : "RT"); return; } /*TODO: Update dbgfs with ISR error stats obtained below */ iwl_read_targ_mem_bytes(trans, base, &table, sizeof(table)); if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { IWL_ERR(trans, "Start IWL Error Log Dump:\n"); IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", priv->status, table.valid); } trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, table.data1, table.data2, table.line, table.blink1, table.blink2, table.ilink1, table.ilink2, table.bcon_time, table.gp1, table.gp2, table.gp3, table.ucode_ver, table.hw_ver, table.brd_ver); IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, desc_lookup(table.error_id)); IWL_ERR(priv, "0x%08X | uPc\n", table.pc); IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1); IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2); IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1); IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2); IWL_ERR(priv, "0x%08X | data1\n", table.data1); IWL_ERR(priv, "0x%08X | data2\n", table.data2); IWL_ERR(priv, "0x%08X | line\n", table.line); IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time); IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low); IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi); IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1); IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2); IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3); IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver); IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver); IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver); IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd); IWL_ERR(priv, "0x%08X | isr0\n", table.isr0); IWL_ERR(priv, "0x%08X | isr1\n", table.isr1); IWL_ERR(priv, "0x%08X | isr2\n", table.isr2); IWL_ERR(priv, "0x%08X | isr3\n", table.isr3); IWL_ERR(priv, "0x%08X | isr4\n", table.isr4); IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref); IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event); IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control); IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration); IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp); IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler); } #define EVENT_START_OFFSET (4 * sizeof(u32)) /** * iwl_print_event_log - Dump error event log to syslog * */ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, u32 num_events, u32 mode, int pos, char **buf, size_t bufsz) { u32 i; u32 base; /* SRAM byte address of event log header */ u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ u32 ptr; /* SRAM byte address of log data */ u32 ev, time, data; /* event log data */ unsigned long reg_flags; struct iwl_trans *trans = priv->trans; if (num_events == 0) return pos; base = priv->device_pointers.log_event_table; if (priv->cur_ucode == IWL_UCODE_INIT) { if (!base) base = priv->fw->init_evtlog_ptr; } else { if (!base) base = priv->fw->inst_evtlog_ptr; } if (mode == 0) event_size = 2 * sizeof(u32); else event_size = 3 * sizeof(u32); ptr = base + EVENT_START_OFFSET + (start_idx * event_size); /* Make sure device is powered up for SRAM reads */ spin_lock_irqsave(&trans->reg_lock, reg_flags); if (unlikely(!iwl_grab_nic_access(trans))) goto out_unlock; /* Set starting address; reads will auto-increment */ iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr); /* "time" is actually "data" for mode 0 (no timestamp). * place event id # at far right for easier visual parsing. */ for (i = 0; i < num_events; i++) { ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT); time = iwl_read32(trans, HBUS_TARG_MEM_RDAT); if (mode == 0) { /* data, ev */ if (bufsz) { pos += scnprintf(*buf + pos, bufsz - pos, "EVT_LOG:0x%08x:%04u\n", time, ev); } else { trace_iwlwifi_dev_ucode_event(trans->dev, 0, time, ev); IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); } } else { data = iwl_read32(trans, HBUS_TARG_MEM_RDAT); if (bufsz) { pos += scnprintf(*buf + pos, bufsz - pos, "EVT_LOGT:%010u:0x%08x:%04u\n", time, data, ev); } else { IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", time, data, ev); trace_iwlwifi_dev_ucode_event(trans->dev, time, data, ev); } } } /* Allow device to power down */ iwl_release_nic_access(trans); out_unlock: spin_unlock_irqrestore(&trans->reg_lock, reg_flags); return pos; } /** * iwl_print_last_event_logs - Dump the newest # of event log to syslog */ static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, u32 num_wraps, u32 next_entry, u32 size, u32 mode, int pos, char **buf, size_t bufsz) { /* * display the newest DEFAULT_LOG_ENTRIES entries * i.e the entries just before the next ont that uCode would fill. */ if (num_wraps) { if (next_entry < size) { pos = iwl_print_event_log(priv, capacity - (size - next_entry), size - next_entry, mode, pos, buf, bufsz); pos = iwl_print_event_log(priv, 0, next_entry, mode, pos, buf, bufsz); } else pos = iwl_print_event_log(priv, next_entry - size, size, mode, pos, buf, bufsz); } else { if (next_entry < size) { pos = iwl_print_event_log(priv, 0, next_entry, mode, pos, buf, bufsz); } else { pos = iwl_print_event_log(priv, next_entry - size, size, mode, pos, buf, bufsz); } } return pos; } #define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, char **buf, bool display) { u32 base; /* SRAM byte address of event log header */ u32 capacity; /* event log capacity in # entries */ u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ u32 num_wraps; /* # times uCode wrapped to top of log */ u32 next_entry; /* index of next entry to be written by uCode */ u32 size; /* # entries that we'll print */ u32 logsize; int pos = 0; size_t bufsz = 0; struct iwl_trans *trans = priv->trans; base = priv->device_pointers.log_event_table; if (priv->cur_ucode == IWL_UCODE_INIT) { logsize = priv->fw->init_evtlog_size; if (!base) base = priv->fw->init_evtlog_ptr; } else { logsize = priv->fw->inst_evtlog_size; if (!base) base = priv->fw->inst_evtlog_ptr; } if (!iwlagn_hw_valid_rtc_data_addr(base)) { IWL_ERR(priv, "Invalid event log pointer 0x%08X for %s uCode\n", base, (priv->cur_ucode == IWL_UCODE_INIT) ? "Init" : "RT"); return -EINVAL; } /* event log header */ capacity = iwl_read_targ_mem(trans, base); mode = iwl_read_targ_mem(trans, base + (1 * sizeof(u32))); num_wraps = iwl_read_targ_mem(trans, base + (2 * sizeof(u32))); next_entry = iwl_read_targ_mem(trans, base + (3 * sizeof(u32))); if (capacity > logsize) { IWL_ERR(priv, "Log capacity %d is bogus, limit to %d " "entries\n", capacity, logsize); capacity = logsize; } if (next_entry > logsize) { IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n", next_entry, logsize); next_entry = logsize; } size = num_wraps ? capacity : next_entry; /* bail out if nothing in log */ if (size == 0) { IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n"); return pos; } #ifdef CONFIG_IWLWIFI_DEBUG if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log) size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; #else size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; #endif IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", size); #ifdef CONFIG_IWLWIFI_DEBUG if (display) { if (full_log) bufsz = capacity * 48; else bufsz = size * 48; *buf = kmalloc(bufsz, GFP_KERNEL); if (!*buf) return -ENOMEM; } if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) { /* * if uCode has wrapped back to top of log, * start at the oldest entry, * i.e the next one that uCode would fill. */ if (num_wraps) pos = iwl_print_event_log(priv, next_entry, capacity - next_entry, mode, pos, buf, bufsz); /* (then/else) start at top of log */ pos = iwl_print_event_log(priv, 0, next_entry, mode, pos, buf, bufsz); } else pos = iwl_print_last_event_logs(priv, capacity, num_wraps, next_entry, size, mode, pos, buf, bufsz); #else pos = iwl_print_last_event_logs(priv, capacity, num_wraps, next_entry, size, mode, pos, buf, bufsz); #endif return pos; } static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) { unsigned int reload_msec; unsigned long reload_jiffies; #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(IWL_DL_FW_ERRORS)) iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS); #endif /* uCode is no longer loaded. */ priv->ucode_loaded = false; /* Set the FW error flag -- cleared on iwl_down */ set_bit(STATUS_FW_ERROR, &priv->status); iwl_abort_notification_waits(&priv->notif_wait); /* Keep the restart process from trying to send host * commands by clearing the ready bit */ clear_bit(STATUS_READY, &priv->status); wake_up(&priv->trans->wait_command_queue); if (!ondemand) { /* * If firmware keep reloading, then it indicate something * serious wrong and firmware having problem to recover * from it. Instead of keep trying which will fill the syslog * and hang the system, let's just stop it */ reload_jiffies = jiffies; reload_msec = jiffies_to_msecs((long) reload_jiffies - (long) priv->reload_jiffies); priv->reload_jiffies = reload_jiffies; if (reload_msec <= IWL_MIN_RELOAD_DURATION) { priv->reload_count++; if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) { IWL_ERR(priv, "BUG_ON, Stop restarting\n"); return; } } else priv->reload_count = 0; } if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { if (iwlwifi_mod_params.restart_fw) { IWL_DEBUG_FW_ERRORS(priv, "Restarting adapter due to uCode error.\n"); queue_work(priv->workqueue, &priv->restart); } else IWL_DEBUG_FW_ERRORS(priv, "Detected FW error, but not restarting\n"); } } static void iwl_nic_error(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); IWL_ERR(priv, "Loaded firmware version: %s\n", priv->fw->fw_version); iwl_dump_nic_error_log(priv); iwl_dump_nic_event_log(priv, false, NULL, false); iwlagn_fw_error(priv, false); } static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); if (!iwl_check_for_ct_kill(priv)) { IWL_ERR(priv, "Restarting adapter queue is full\n"); iwlagn_fw_error(priv, false); } } #define EEPROM_RF_CONFIG_TYPE_MAX 0x3 static void iwl_nic_config(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); u16 radio_cfg = priv->eeprom_data->radio_cfg; /* SKU Control */ iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP, (CSR_HW_REV_STEP(priv->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) | (CSR_HW_REV_DASH(priv->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_DASH)); /* write radio config values to register */ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { u32 reg_val = EEPROM_RF_CFG_TYPE_MSK(radio_cfg) << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | EEPROM_RF_CFG_STEP_MSK(radio_cfg) << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | EEPROM_RF_CFG_DASH_MSK(radio_cfg) << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val); IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", EEPROM_RF_CFG_TYPE_MSK(radio_cfg), EEPROM_RF_CFG_STEP_MSK(radio_cfg), EEPROM_RF_CFG_DASH_MSK(radio_cfg)); } else { WARN_ON(1); } /* set CSR_HW_CONFIG_REG for uCode use */ iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); /* W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), * causing ME FW to lose ownership and not being able to obtain it back. */ iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); if (priv->lib->nic_config) priv->lib->nic_config(priv); } static void iwl_wimax_active(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); clear_bit(STATUS_READY, &priv->status); IWL_ERR(priv, "RF is used by WiMAX\n"); } static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); int mq = priv->queue_to_mac80211[queue]; if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) return; if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) { IWL_DEBUG_TX_QUEUES(priv, "queue %d (mac80211 %d) already stopped\n", queue, mq); return; } set_bit(mq, &priv->transport_queue_stop); ieee80211_stop_queue(priv->hw, mq); } static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); int mq = priv->queue_to_mac80211[queue]; if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) return; if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) { IWL_DEBUG_TX_QUEUES(priv, "queue %d (mac80211 %d) already awake\n", queue, mq); return; } clear_bit(mq, &priv->transport_queue_stop); if (!priv->passive_no_rx) ieee80211_wake_queue(priv->hw, mq); } void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) { int mq; if (!priv->passive_no_rx) return; for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) { if (!test_bit(mq, &priv->transport_queue_stop)) { IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d", mq); ieee80211_wake_queue(priv->hw, mq); } else { IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d", mq); } } priv->passive_no_rx = false; } static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); struct ieee80211_tx_info *info; info = IEEE80211_SKB_CB(skb); iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); dev_kfree_skb_any(skb); } static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); if (state) set_bit(STATUS_RF_KILL_HW, &priv->status); else clear_bit(STATUS_RF_KILL_HW, &priv->status); wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); } static const struct iwl_op_mode_ops iwl_dvm_ops = { .start = iwl_op_mode_dvm_start, .stop = iwl_op_mode_dvm_stop, .rx = iwl_rx_dispatch, .queue_full = iwl_stop_sw_queue, .queue_not_full = iwl_wake_sw_queue, .hw_rf_kill = iwl_set_hw_rfkill_state, .free_skb = iwl_free_skb, .nic_error = iwl_nic_error, .cmd_queue_full = iwl_cmd_queue_full, .nic_config = iwl_nic_config, .wimax_active = iwl_wimax_active, }; /***************************************************************************** * * driver and module entry point * *****************************************************************************/ static int __init iwl_init(void) { int ret; pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); pr_info(DRV_COPYRIGHT "\n"); ret = iwlagn_rate_control_register(); if (ret) { pr_err("Unable to register rate control algorithm: %d\n", ret); return ret; } ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops); if (ret) { pr_err("Unable to register op_mode: %d\n", ret); iwlagn_rate_control_unregister(); } return ret; } module_init(iwl_init); static void __exit iwl_exit(void) { iwl_opmode_deregister("iwldvm"); iwlagn_rate_control_unregister(); } module_exit(iwl_exit); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/led.c0000644000175000017500000001433612026211315023640 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "iwl-io.h" #include "iwl-trans.h" #include "iwl-modparams.h" #include "dev.h" #include "agn.h" /* Throughput OFF time(ms) ON time (ms) * >300 25 25 * >200 to 300 40 40 * >100 to 200 55 55 * >70 to 100 65 65 * >50 to 70 75 75 * >20 to 50 85 85 * >10 to 20 95 95 * >5 to 10 110 110 * >1 to 5 130 130 * >0 to 1 167 167 * <=0 SOLID ON */ static const struct ieee80211_tpt_blink iwl_blink[] = { { .throughput = 0, .blink_time = 334 }, { .throughput = 1 * 1024 - 1, .blink_time = 260 }, { .throughput = 5 * 1024 - 1, .blink_time = 220 }, { .throughput = 10 * 1024 - 1, .blink_time = 190 }, { .throughput = 20 * 1024 - 1, .blink_time = 170 }, { .throughput = 50 * 1024 - 1, .blink_time = 150 }, { .throughput = 70 * 1024 - 1, .blink_time = 130 }, { .throughput = 100 * 1024 - 1, .blink_time = 110 }, { .throughput = 200 * 1024 - 1, .blink_time = 80 }, { .throughput = 300 * 1024 - 1, .blink_time = 50 }, }; /* Set led register off */ void iwlagn_led_enable(struct iwl_priv *priv) { iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TRUN_ON); } /* * Adjust led blink rate to compensate on a MAC Clock difference on every HW * Led blink rate analysis showed an average deviation of 20% on 5000 series * and up. * Need to compensate on the led on/off time per HW according to the deviation * to achieve the desired led frequency * The calculation is: (100-averageDeviation)/100 * blinkTime * For code efficiency the calculation will be: * compensation = (100 - averageDeviation) * 64 / 100 * NewBlinkTime = (compensation * BlinkTime) / 64 */ static inline u8 iwl_blink_compensation(struct iwl_priv *priv, u8 time, u16 compensation) { if (!compensation) { IWL_ERR(priv, "undefined blink compensation: " "use pre-defined blinking time\n"); return time; } return (u8)((time * compensation) >> 6); } static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) { struct iwl_host_cmd cmd = { .id = REPLY_LEDS_CMD, .len = { sizeof(struct iwl_led_cmd), }, .data = { led_cmd, }, .flags = CMD_ASYNC, }; u32 reg; reg = iwl_read32(priv->trans, CSR_LED_REG); if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) iwl_write32(priv->trans, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); return iwl_dvm_send_cmd(priv, &cmd); } /* Set led pattern command */ static int iwl_led_cmd(struct iwl_priv *priv, unsigned long on, unsigned long off) { struct iwl_led_cmd led_cmd = { .id = IWL_LED_LINK, .interval = IWL_DEF_LED_INTRVL }; int ret; if (!test_bit(STATUS_READY, &priv->status)) return -EBUSY; if (priv->blink_on == on && priv->blink_off == off) return 0; if (off == 0) { /* led is SOLID_ON */ on = IWL_LED_SOLID; } IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", priv->cfg->base_params->led_compensation); led_cmd.on = iwl_blink_compensation(priv, on, priv->cfg->base_params->led_compensation); led_cmd.off = iwl_blink_compensation(priv, off, priv->cfg->base_params->led_compensation); ret = iwl_send_led_cmd(priv, &led_cmd); if (!ret) { priv->blink_on = on; priv->blink_off = off; } return ret; } static void iwl_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); unsigned long on = 0; if (brightness > 0) on = IWL_LED_SOLID; iwl_led_cmd(priv, on, 0); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) static int iwl_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); return iwl_led_cmd(priv, *delay_on, *delay_off); } #endif void iwl_leds_init(struct iwl_priv *priv) { int mode = iwlwifi_mod_params.led_mode; int ret; if (mode == IWL_LED_DISABLE) { IWL_INFO(priv, "Led disabled\n"); return; } if (mode == IWL_LED_DEFAULT) mode = priv->cfg->led_mode; priv->led.name = kasprintf(GFP_KERNEL, "%s-led", wiphy_name(priv->hw->wiphy)); priv->led.brightness_set = iwl_led_brightness_set; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) priv->led.blink_set = iwl_led_blink_set; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) priv->led.max_brightness = 1; #endif switch (mode) { case IWL_LED_DEFAULT: WARN_ON(1); break; case IWL_LED_BLINK: priv->led.default_trigger = ieee80211_create_tpt_led_trigger(priv->hw, IEEE80211_TPT_LEDTRIG_FL_CONNECTED, iwl_blink, ARRAY_SIZE(iwl_blink)); break; case IWL_LED_RF_STATE: priv->led.default_trigger = ieee80211_get_radio_led_name(priv->hw); break; } ret = led_classdev_register(priv->trans->dev, &priv->led); if (ret) { kfree(priv->led.name); return; } priv->led_registered = true; } void iwl_leds_exit(struct iwl_priv *priv) { if (!priv->led_registered) return; led_classdev_unregister(&priv->led); kfree(priv->led.name); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/ucode.c0000644000175000017500000003553612026211315024200 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include "iwl-io.h" #include "iwl-agn-hw.h" #include "iwl-trans.h" #include "iwl-fh.h" #include "iwl-op-mode.h" #include "dev.h" #include "agn.h" #include "calib.h" /****************************************************************************** * * uCode download functions * ******************************************************************************/ static inline const struct fw_img * iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) { if (ucode_type >= IWL_UCODE_TYPE_MAX) return NULL; return &priv->fw->img[ucode_type]; } /* * Calibration */ static int iwl_set_Xtal_calib(struct iwl_priv *priv) { struct iwl_calib_xtal_freq_cmd cmd; __le16 *xtal_calib = priv->eeprom_data->xtal_calib; iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD); cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]); cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]); return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); } static int iwl_set_temperature_offset_calib(struct iwl_priv *priv) { struct iwl_calib_temperature_offset_cmd cmd; memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); cmd.radio_sensor_offset = priv->eeprom_data->raw_temperature; if (!(cmd.radio_sensor_offset)) cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET; IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n", le16_to_cpu(cmd.radio_sensor_offset)); return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); } static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv) { struct iwl_calib_temperature_offset_v2_cmd cmd; memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); cmd.radio_sensor_offset_high = priv->eeprom_data->kelvin_temperature; cmd.radio_sensor_offset_low = priv->eeprom_data->raw_temperature; if (!cmd.radio_sensor_offset_low) { IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; } cmd.burntVoltageRef = priv->eeprom_data->calib_voltage; IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", le16_to_cpu(cmd.radio_sensor_offset_high)); IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n", le16_to_cpu(cmd.radio_sensor_offset_low)); IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n", le16_to_cpu(cmd.burntVoltageRef)); return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); } static int iwl_send_calib_cfg(struct iwl_priv *priv) { struct iwl_calib_cfg_cmd calib_cfg_cmd; struct iwl_host_cmd cmd = { .id = CALIBRATION_CFG_CMD, .len = { sizeof(struct iwl_calib_cfg_cmd), }, .data = { &calib_cfg_cmd, }, }; memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL; calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL; calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL; calib_cfg_cmd.ucd_calib_cfg.flags = IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK; return iwl_dvm_send_cmd(priv, &cmd); } int iwl_init_alive_start(struct iwl_priv *priv) { int ret; if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) { /* * Tell uCode we are ready to perform calibration * need to perform this before any calibration * no need to close the envlope since we are going * to load the runtime uCode later. */ ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret; } ret = iwl_send_calib_cfg(priv); if (ret) return ret; /** * temperature offset calibration is only needed for runtime ucode, * so prepare the value now. */ if (priv->cfg->need_temp_offset_calib) { if (priv->cfg->temp_offset_v2) return iwl_set_temperature_offset_calib_v2(priv); else return iwl_set_temperature_offset_calib(priv); } return 0; } static int iwl_send_wimax_coex(struct iwl_priv *priv) { struct iwl_wimax_coex_cmd coex_cmd; /* coexistence is disabled */ memset(&coex_cmd, 0, sizeof(coex_cmd)); return iwl_dvm_send_cmd_pdu(priv, COEX_PRIORITY_TABLE_CMD, CMD_SYNC, sizeof(coex_cmd), &coex_cmd); } static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), 0, 0, 0, 0, 0, 0, 0 }; void iwl_send_prio_tbl(struct iwl_priv *priv) { struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd; memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, sizeof(iwl_bt_prio_tbl)); if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC, sizeof(prio_tbl_cmd), &prio_tbl_cmd)) IWL_ERR(priv, "failed to send BT prio tbl command\n"); } int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) { struct iwl_bt_coex_prot_env_cmd env_cmd; int ret; env_cmd.action = action; env_cmd.type = type; ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_COEX_PROT_ENV, CMD_SYNC, sizeof(env_cmd), &env_cmd); if (ret) IWL_ERR(priv, "failed to send BT env command\n"); return ret; } static const u8 iwlagn_default_queue_to_tx_fifo[] = { IWL_TX_FIFO_VO, IWL_TX_FIFO_VI, IWL_TX_FIFO_BE, IWL_TX_FIFO_BK, }; static const u8 iwlagn_ipan_queue_to_tx_fifo[] = { IWL_TX_FIFO_VO, IWL_TX_FIFO_VI, IWL_TX_FIFO_BE, IWL_TX_FIFO_BK, IWL_TX_FIFO_BK_IPAN, IWL_TX_FIFO_BE_IPAN, IWL_TX_FIFO_VI_IPAN, IWL_TX_FIFO_VO_IPAN, IWL_TX_FIFO_BE_IPAN, IWL_TX_FIFO_UNUSED, IWL_TX_FIFO_AUX, }; static int iwl_alive_notify(struct iwl_priv *priv) { const u8 *queue_to_txf; u8 n_queues; int ret; int i; iwl_trans_fw_alive(priv->trans); if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN && priv->eeprom_data->sku & EEPROM_SKU_CAP_IPAN_ENABLE) { n_queues = ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo); queue_to_txf = iwlagn_ipan_queue_to_tx_fifo; } else { n_queues = ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); queue_to_txf = iwlagn_default_queue_to_tx_fifo; } for (i = 0; i < n_queues; i++) if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED) iwl_trans_ac_txq_enable(priv->trans, i, queue_to_txf[i]); priv->passive_no_rx = false; priv->transport_queue_stop = 0; ret = iwl_send_wimax_coex(priv); if (ret) return ret; if (!priv->cfg->no_xtal_calib) { ret = iwl_set_Xtal_calib(priv); if (ret) return ret; } return iwl_send_calib_results(priv); } /** * iwl_verify_inst_sparse - verify runtime uCode image in card vs. host, * using sample data 100 bytes apart. If these sample points are good, * it's a pretty good bet that everything between them is good, too. */ static int iwl_verify_sec_sparse(struct iwl_priv *priv, const struct fw_desc *fw_desc) { __le32 *image = (__le32 *)fw_desc->v_addr; u32 len = fw_desc->len; u32 val; u32 i; IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) { /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IWL_DL_IO is set */ iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR, i + fw_desc->offset); val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) return -EIO; } return 0; } static void iwl_print_mismatch_sec(struct iwl_priv *priv, const struct fw_desc *fw_desc) { __le32 *image = (__le32 *)fw_desc->v_addr; u32 len = fw_desc->len; u32 val; u32 offs; int errors = 0; IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR, fw_desc->offset); for (offs = 0; offs < len && errors < 20; offs += sizeof(u32), image++) { /* read data comes through single port, auto-incr addr */ val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { IWL_ERR(priv, "uCode INST section at " "offset 0x%x, is 0x%x, s/b 0x%x\n", offs, val, le32_to_cpu(*image)); errors++; } } } /** * iwl_verify_ucode - determine which instruction image is in SRAM, * and verify its contents */ static int iwl_verify_ucode(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) { const struct fw_img *img = iwl_get_ucode_image(priv, ucode_type); if (!img) { IWL_ERR(priv, "Invalid ucode requested (%d)\n", ucode_type); return -EINVAL; } if (!iwl_verify_sec_sparse(priv, &img->sec[IWL_UCODE_SECTION_INST])) { IWL_DEBUG_FW(priv, "uCode is good in inst SRAM\n"); return 0; } IWL_ERR(priv, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n"); iwl_print_mismatch_sec(priv, &img->sec[IWL_UCODE_SECTION_INST]); return -EIO; } struct iwl_alive_data { bool valid; u8 subtype; }; static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data) { struct iwl_priv *priv = container_of(notif_wait, struct iwl_priv, notif_wait); struct iwl_alive_data *alive_data = data; struct iwl_alive_resp *palive; palive = (void *)pkt->data; IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", palive->is_valid, palive->ver_type, palive->ver_subtype); priv->device_pointers.error_event_table = le32_to_cpu(palive->error_event_table_ptr); priv->device_pointers.log_event_table = le32_to_cpu(palive->log_event_table_ptr); alive_data->subtype = palive->ver_subtype; alive_data->valid = palive->is_valid == UCODE_VALID_OK; return true; } #define UCODE_ALIVE_TIMEOUT HZ #define UCODE_CALIB_TIMEOUT (2*HZ) int iwl_load_ucode_wait_alive(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) { struct iwl_notification_wait alive_wait; struct iwl_alive_data alive_data; const struct fw_img *fw; int ret; enum iwl_ucode_type old_type; static const u8 alive_cmd[] = { REPLY_ALIVE }; old_type = priv->cur_ucode; priv->cur_ucode = ucode_type; fw = iwl_get_ucode_image(priv, ucode_type); priv->ucode_loaded = false; if (!fw) return -EINVAL; iwl_init_notification_wait(&priv->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), iwl_alive_fn, &alive_data); ret = iwl_trans_start_fw(priv->trans, fw); if (ret) { priv->cur_ucode = old_type; iwl_remove_notification(&priv->notif_wait, &alive_wait); return ret; } /* * Some things may run in the background now, but we * just wait for the ALIVE notification here. */ ret = iwl_wait_notification(&priv->notif_wait, &alive_wait, UCODE_ALIVE_TIMEOUT); if (ret) { priv->cur_ucode = old_type; return ret; } if (!alive_data.valid) { IWL_ERR(priv, "Loaded ucode is not valid!\n"); priv->cur_ucode = old_type; return -EIO; } /* * This step takes a long time (60-80ms!!) and * WoWLAN image should be loaded quickly, so * skip it for WoWLAN. */ if (ucode_type != IWL_UCODE_WOWLAN) { ret = iwl_verify_ucode(priv, ucode_type); if (ret) { priv->cur_ucode = old_type; return ret; } /* delay a bit to give rfkill time to run */ msleep(5); } ret = iwl_alive_notify(priv); if (ret) { IWL_WARN(priv, "Could not complete ALIVE transition: %d\n", ret); priv->cur_ucode = old_type; return ret; } priv->ucode_loaded = true; return 0; } static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data) { struct iwl_priv *priv = data; struct iwl_calib_hdr *hdr; int len; if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); return true; } hdr = (struct iwl_calib_hdr *)pkt->data; len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; /* reduce the size by the length field itself */ len -= sizeof(__le32); if (iwl_calib_set(priv, hdr, len)) IWL_ERR(priv, "Failed to record calibration data %d\n", hdr->op_code); return false; } int iwl_run_init_ucode(struct iwl_priv *priv) { struct iwl_notification_wait calib_wait; static const u8 calib_complete[] = { CALIBRATION_RES_NOTIFICATION, CALIBRATION_COMPLETE_NOTIFICATION }; int ret; lockdep_assert_held(&priv->mutex); /* No init ucode required? Curious, but maybe ok */ if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len) return 0; if (priv->init_ucode_run) return 0; iwl_init_notification_wait(&priv->notif_wait, &calib_wait, calib_complete, ARRAY_SIZE(calib_complete), iwlagn_wait_calib, priv); /* Will also start the device */ ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); if (ret) goto error; ret = iwl_init_alive_start(priv); if (ret) goto error; /* * Some things may run in the background now, but we * just wait for the calibration complete notification. */ ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, UCODE_CALIB_TIMEOUT); if (!ret) priv->init_ucode_run = true; goto out; error: iwl_remove_notification(&priv->notif_wait, &calib_wait); out: /* Whatever happened, stop the device */ iwl_trans_stop_device(priv->trans); priv->ucode_loaded = false; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/tx.c0000644000175000017500000011703712026211315023531 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include "iwl-io.h" #include "iwl-trans.h" #include "iwl-agn-hw.h" #include "dev.h" #include "agn.h" static const u8 tid_to_ac[] = { IEEE80211_AC_BE, IEEE80211_AC_BK, IEEE80211_AC_BK, IEEE80211_AC_BE, IEEE80211_AC_VI, IEEE80211_AC_VI, IEEE80211_AC_VO, IEEE80211_AC_VO, }; static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, struct ieee80211_tx_info *info, __le16 fc, __le32 *tx_flags) { if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS || info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT || info->flags & IEEE80211_TX_CTL_AMPDU) *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK; } /* * handle build REPLY_TX command notification. */ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, struct sk_buff *skb, struct iwl_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, u8 sta_id) { __le16 fc = hdr->frame_control; __le32 tx_flags = tx_cmd->tx_flags; tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) tx_flags |= TX_CMD_FLG_ACK_MSK; else tx_flags &= ~TX_CMD_FLG_ACK_MSK; if (ieee80211_is_probe_resp(fc)) tx_flags |= TX_CMD_FLG_TSF_MSK; else if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; else if (info->band == IEEE80211_BAND_2GHZ && priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || skb->protocol == cpu_to_be16(ETH_P_PAE))) tx_flags |= TX_CMD_FLG_IGNORE_BT; tx_cmd->sta_id = sta_id; if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { u8 *qc = ieee80211_get_qos_ctl(hdr); tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { tx_cmd->tid_tspec = IWL_TID_NON_QOS; if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; else tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags); tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); if (ieee80211_is_mgmt(fc)) { if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); else tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); } else { tx_cmd->timeout.pm_frame_timeout = 0; } tx_cmd->driver_txop = 0; tx_cmd->tx_flags = tx_flags; tx_cmd->next_frame_len = 0; } static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, struct iwl_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, __le16 fc) { u32 rate_flags; int rate_idx; u8 rts_retry_limit; u8 data_retry_limit; u8 rate_plcp; if (priv->wowlan) { rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT; data_retry_limit = IWLAGN_LOW_RETRY_LIMIT; } else { /* Set retry limit on RTS packets */ rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT; /* Set retry limit on DATA packets and Probe Responses*/ if (ieee80211_is_probe_resp(fc)) { data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT; rts_retry_limit = min(data_retry_limit, rts_retry_limit); } else if (ieee80211_is_back_req(fc)) data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT; else data_retry_limit = IWLAGN_DEFAULT_TX_RETRY; } tx_cmd->data_retry_limit = data_retry_limit; tx_cmd->rts_retry_limit = rts_retry_limit; /* DATA packets will use the uCode station table for rate/antenna * selection */ if (ieee80211_is_data(fc)) { tx_cmd->initial_rate_index = 0; tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE if (priv->tm_fixed_rate) { /* * rate overwrite by testmode * we not only send lq command to change rate * we also re-enforce per data pkt base. */ tx_cmd->tx_flags &= ~TX_CMD_FLG_STA_RATE_MSK; memcpy(&tx_cmd->rate_n_flags, &priv->tm_fixed_rate, sizeof(tx_cmd->rate_n_flags)); } #endif return; } else if (ieee80211_is_back_req(fc)) tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; /** * If the current TX rate stored in mac80211 has the MCS bit set, it's * not really a TX rate. Thus, we use the lowest supported rate for * this band. Also use the lowest supported rate if the stored rate * index is invalid. */ rate_idx = info->control.rates[0].idx; if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) rate_idx = rate_lowest_index( &priv->eeprom_data->bands[info->band], sta); /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ if (info->band == IEEE80211_BAND_5GHZ) rate_idx += IWL_FIRST_OFDM_RATE; /* Get PLCP rate for tx_cmd->rate_n_flags */ rate_plcp = iwl_rates[rate_idx].plcp; /* Zero out flags for this packet */ rate_flags = 0; /* Set CCK flag as needed */ if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) rate_flags |= RATE_MCS_CCK_MSK; /* Set up antennas */ if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, first_antenna(priv->eeprom_data->valid_tx_ant)); } else priv->mgmt_tx_ant = iwl_toggle_tx_ant( priv, priv->mgmt_tx_ant, priv->eeprom_data->valid_tx_ant); rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant); /* Set the rate in the TX cmd */ tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags); } static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, struct ieee80211_tx_info *info, struct iwl_tx_cmd *tx_cmd, struct sk_buff *skb_frag) { struct ieee80211_key_conf *keyconf = info->control.hw_key; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_CCMP: tx_cmd->sec_ctl = TX_CMD_SEC_CCM; memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); if (info->flags & IEEE80211_TX_CTL_AMPDU) tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); break; case WLAN_CIPHER_SUITE_TKIP: tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n"); break; case WLAN_CIPHER_SUITE_WEP104: tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; /* fall through */ case WLAN_CIPHER_SUITE_WEP40: tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " "with key %d\n", keyconf->keyidx); break; default: IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); break; } } /** * iwl_sta_id_or_broadcast - return sta_id or broadcast sta * @context: the current context * @sta: mac80211 station * * In certain circumstances mac80211 passes a station pointer * that may be %NULL, for example during TX or key setup. In * that case, we need to use the broadcast station, so this * inline wraps that pattern. */ static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, struct ieee80211_sta *sta) { int sta_id; if (!sta) return context->bcast_sta_id; sta_id = iwl_sta_id(sta); /* * mac80211 should not be passing a partially * initialised station! */ WARN_ON(sta_id == IWL_INVALID_STATION); return sta_id; } /* * start REPLY_TX command process */ int iwlagn_tx_skb(struct iwl_priv *priv, struct ieee80211_sta *sta, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_station_priv *sta_priv = NULL; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; __le16 fc; u8 hdr_len; u16 len, seq_number = 0; u8 sta_id, tid = IWL_MAX_TID_COUNT; bool is_agg = false; int txq_id; if (info->control.vif) ctx = iwl_rxon_ctx_from_vif(info->control.vif); if (iwl_is_rfkill(priv)) { IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); goto drop_unlock_priv; } fc = hdr->frame_control; #ifdef CONFIG_IWLWIFI_DEBUG if (ieee80211_is_auth(fc)) IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); else if (ieee80211_is_assoc_req(fc)) IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); else if (ieee80211_is_reassoc_req(fc)) IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); #endif if (unlikely(ieee80211_is_probe_resp(fc))) { struct iwl_wipan_noa_data *noa_data = rcu_dereference(priv->noa_data); if (noa_data && pskb_expand_head(skb, 0, noa_data->length, GFP_ATOMIC) == 0) { memcpy(skb_put(skb, noa_data->length), noa_data->data, noa_data->length); hdr = (struct ieee80211_hdr *)skb->data; } } hdr_len = ieee80211_hdrlen(fc); /* For management frames use broadcast id to do not break aggregation */ if (!ieee80211_is_data(fc)) sta_id = ctx->bcast_sta_id; else { /* Find index into station table for destination station */ sta_id = iwl_sta_id_or_broadcast(ctx, sta); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); goto drop_unlock_priv; } } IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); if (sta) sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the * next frame is processed -- and the next frame to * this station is the one that will consume this * counter. * For now set the counter to just 1 since we do not * support uAPSD yet. * * FIXME: If we get two non-bufferable frames one * after the other, we might only send out one of * them because this is racy. */ iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); } if (info->flags & IEEE80211_TX_CTL_AMPDU) is_agg = true; dev_cmd = iwl_trans_alloc_tx_cmd(priv->trans); if (unlikely(!dev_cmd)) goto drop_unlock_priv; memset(dev_cmd, 0, sizeof(*dev_cmd)); tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; /* Total # bytes to be transmitted */ len = (u16)skb->len; tx_cmd->len = cpu_to_le16(len); if (info->control.hw_key) iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb); /* TODO need this for burst mode later on */ iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc); memset(&info->status, 0, sizeof(info->status)); info->driver_data[0] = ctx; info->driver_data[1] = dev_cmd; /* From now on, we cannot access info->control */ spin_lock(&priv->sta_lock); if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { u8 *qc = NULL; struct iwl_tid_data *tid_data; qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) goto drop_unlock_sta; tid_data = &priv->tid_data[sta_id][tid]; /* aggregation is on for this */ if (info->flags & IEEE80211_TX_CTL_AMPDU && tid_data->agg.state != IWL_AGG_ON) { IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:" " Tx flags = 0x%08x, agg.state = %d", info->flags, tid_data->agg.state); IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d", sta_id, tid, SEQ_TO_SN(tid_data->seq_number)); goto drop_unlock_sta; } /* We can receive packets from the stack in IWL_AGG_{ON,OFF} * only. Check this here. */ if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && tid_data->agg.state != IWL_AGG_OFF, "Tx while agg.state = %d", tid_data->agg.state)) goto drop_unlock_sta; seq_number = tid_data->seq_number; seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; } /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, hdr_len); if (is_agg) txq_id = priv->tid_data[sta_id][tid].agg.txq_id; else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* * Send this frame after DTIM -- there's a special queue * reserved for this for contexts that support AP mode. */ txq_id = ctx->mcast_queue; /* * The microcode will clear the more data * bit in the last frame it transmits. */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) txq_id = IWL_AUX_QUEUE; else txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)]; WARN_ON_ONCE(!is_agg && txq_id != info->hw_queue); WARN_ON_ONCE(is_agg && priv->queue_to_mac80211[txq_id] != info->hw_queue); if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta; if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc) && !ieee80211_has_morefrags(fc)) priv->tid_data[sta_id][tid].seq_number = seq_number; spin_unlock(&priv->sta_lock); /* * Avoid atomic ops if it isn't an associated client. * Also, if this is a packet for aggregation, don't * increase the counter because the ucode will stop * aggregation queues when their respective station * goes to sleep. */ if (sta_priv && sta_priv->client && !is_agg) atomic_inc(&sta_priv->pending_frames); if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) iwl_scan_offchannel_skb(priv); return 0; drop_unlock_sta: if (dev_cmd) iwl_trans_free_tx_cmd(priv->trans, dev_cmd); spin_unlock(&priv->sta_lock); drop_unlock_priv: return -1; } static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq) { int q; for (q = IWLAGN_FIRST_AMPDU_QUEUE; q < priv->cfg->base_params->num_of_queues; q++) { if (!test_and_set_bit(q, priv->agg_q_alloc)) { priv->queue_to_mac80211[q] = mq; return q; } } return -ENOSPC; } static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q) { clear_bit(q, priv->agg_q_alloc); priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE; } int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { struct iwl_tid_data *tid_data; int sta_id, txq_id; enum iwl_agg_state agg_state; sta_id = iwl_sta_id(sta); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); return -ENXIO; } spin_lock_bh(&priv->sta_lock); tid_data = &priv->tid_data[sta_id][tid]; txq_id = priv->tid_data[sta_id][tid].agg.txq_id; switch (priv->tid_data[sta_id][tid].agg.state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: /* * This can happen if the peer stops aggregation * again before we've had a chance to drain the * queue we selected previously, i.e. before the * session was really started completely. */ IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); goto turn_off; case IWL_AGG_STARTING: /* * This can happen when the session is stopped before * we receive ADDBA response */ IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n"); goto turn_off; case IWL_AGG_ON: break; default: IWL_WARN(priv, "Stopping AGG while state not ON " "or starting for %d on %d (%d)\n", sta_id, tid, priv->tid_data[sta_id][tid].agg.state); spin_unlock_bh(&priv->sta_lock); return 0; } tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); /* There are still packets for this RA / TID in the HW */ if (!test_bit(txq_id, priv->agg_q_alloc)) { IWL_DEBUG_TX_QUEUES(priv, "stopping AGG on STA/TID %d/%d but hwq %d not used\n", sta_id, tid, txq_id); } else if (tid_data->agg.ssn != tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " "next_recl = %d\n", tid_data->agg.ssn, tid_data->next_reclaimed); priv->tid_data[sta_id][tid].agg.state = IWL_EMPTYING_HW_QUEUE_DELBA; spin_unlock_bh(&priv->sta_lock); return 0; } IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", tid_data->agg.ssn); turn_off: agg_state = priv->tid_data[sta_id][tid].agg.state; priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF; spin_unlock_bh(&priv->sta_lock); if (test_bit(txq_id, priv->agg_q_alloc)) { /* * If the transport didn't know that we wanted to start * agreggation, don't tell it that we want to stop them. * This can happen when we don't get the addBA response on * time, or we hadn't time to drain the AC queues. */ if (agg_state == IWL_AGG_ON) iwl_trans_txq_disable(priv->trans, txq_id); else IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", agg_state); iwlagn_dealloc_agg_txq(priv, txq_id); } ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); return 0; } int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); struct iwl_tid_data *tid_data; int sta_id, txq_id, ret; IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n", sta->addr, tid); sta_id = iwl_sta_id(sta); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Start AGG on invalid station\n"); return -ENXIO; } if (unlikely(tid >= IWL_MAX_TID_COUNT)) return -EINVAL; if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); return -ENXIO; } txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]); if (txq_id < 0) { IWL_DEBUG_TX_QUEUES(priv, "No free aggregation queue for %pM/%d\n", sta->addr, tid); return txq_id; } ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); if (ret) return ret; spin_lock_bh(&priv->sta_lock); tid_data = &priv->tid_data[sta_id][tid]; tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); tid_data->agg.txq_id = txq_id; *ssn = tid_data->agg.ssn; if (*ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", tid_data->agg.ssn); tid_data->agg.state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); } else { IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " "next_reclaimed = %d\n", tid_data->agg.ssn, tid_data->next_reclaimed); tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; } spin_unlock_bh(&priv->sta_lock); return ret; } int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u8 buf_size) { struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); int q, fifo; u16 ssn; buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); spin_lock_bh(&priv->sta_lock); ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn; q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id; priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON; spin_unlock_bh(&priv->sta_lock); fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid, buf_size, ssn); /* * If the limit is 0, then it wasn't initialised yet, * use the default. We can do that since we take the * minimum below, and we don't want to go above our * default due to hardware restrictions. */ if (sta_priv->max_agg_bufsize == 0) sta_priv->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; /* * Even though in theory the peer could have different * aggregation reorder buffer sizes for different sessions, * our ucode doesn't allow for that and has a global limit * for each station. Therefore, use the minimum of all the * aggregation sessions and our default value. */ sta_priv->max_agg_bufsize = min(sta_priv->max_agg_bufsize, buf_size); if (priv->hw_params.use_rts_for_aggregation) { /* * switch to RTS/CTS if it is the prefer protection * method for HT traffic */ sta_priv->lq_sta.lq.general_params.flags |= LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; } priv->agg_tids_count++; IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", priv->agg_tids_count); sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = sta_priv->max_agg_bufsize; IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", sta->addr, tid); return iwl_send_lq_cmd(priv, ctx, &sta_priv->lq_sta.lq, CMD_ASYNC, false); } static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) { struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; enum iwl_rxon_context_id ctx; struct ieee80211_vif *vif; u8 *addr; lockdep_assert_held(&priv->sta_lock); addr = priv->stations[sta_id].sta.sta.addr; ctx = priv->stations[sta_id].ctxid; vif = priv->contexts[ctx].vif; switch (priv->tid_data[sta_id][tid].agg.state) { case IWL_EMPTYING_HW_QUEUE_DELBA: /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, "Can continue DELBA flow ssn = next_recl =" " %d", tid_data->next_reclaimed); iwl_trans_txq_disable(priv->trans, tid_data->agg.txq_id); iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); tid_data->agg.state = IWL_AGG_OFF; ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); } break; case IWL_EMPTYING_HW_QUEUE_ADDBA: /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, "Can continue ADDBA flow ssn = next_recl =" " %d", tid_data->next_reclaimed); tid_data->agg.state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); } break; default: break; } } static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr1) { struct ieee80211_sta *sta; struct iwl_station_priv *sta_priv; rcu_read_lock(); sta = ieee80211_find_sta(ctx->vif, addr1); if (sta) { sta_priv = (void *)sta->drv_priv; /* avoid atomic ops if this isn't a client */ if (sta_priv->client && atomic_dec_return(&sta_priv->pending_frames) == 0) ieee80211_sta_block_awake(priv->hw, sta, false); } rcu_read_unlock(); } /** * translate ucode response to mac80211 tx status control values */ static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, struct ieee80211_tx_info *info) { struct ieee80211_tx_rate *r = &info->status.rates[0]; info->status.antenna = ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); if (rate_n_flags & RATE_MCS_HT_MSK) r->flags |= IEEE80211_TX_RC_MCS; if (rate_n_flags & RATE_MCS_GF_MSK) r->flags |= IEEE80211_TX_RC_GREEN_FIELD; if (rate_n_flags & RATE_MCS_HT40_MSK) r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (rate_n_flags & RATE_MCS_DUP_MSK) r->flags |= IEEE80211_TX_RC_DUP_DATA; if (rate_n_flags & RATE_MCS_SGI_MSK) r->flags |= IEEE80211_TX_RC_SHORT_GI; r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); } #ifdef CONFIG_IWLWIFI_DEBUG const char *iwl_get_tx_fail_reason(u32 status) { #define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x #define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x switch (status & TX_STATUS_MSK) { case TX_STATUS_SUCCESS: return "SUCCESS"; TX_STATUS_POSTPONE(DELAY); TX_STATUS_POSTPONE(FEW_BYTES); TX_STATUS_POSTPONE(BT_PRIO); TX_STATUS_POSTPONE(QUIET_PERIOD); TX_STATUS_POSTPONE(CALC_TTAK); TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); TX_STATUS_FAIL(SHORT_LIMIT); TX_STATUS_FAIL(LONG_LIMIT); TX_STATUS_FAIL(FIFO_UNDERRUN); TX_STATUS_FAIL(DRAIN_FLOW); TX_STATUS_FAIL(RFKILL_FLUSH); TX_STATUS_FAIL(LIFE_EXPIRE); TX_STATUS_FAIL(DEST_PS); TX_STATUS_FAIL(HOST_ABORTED); TX_STATUS_FAIL(BT_RETRY); TX_STATUS_FAIL(STA_INVALID); TX_STATUS_FAIL(FRAG_DROPPED); TX_STATUS_FAIL(TID_DISABLE); TX_STATUS_FAIL(FIFO_FLUSHED); TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); TX_STATUS_FAIL(PASSIVE_NO_RX); TX_STATUS_FAIL(NO_BEACON_ON_RADAR); } return "UNKNOWN"; #undef TX_STATUS_FAIL #undef TX_STATUS_POSTPONE } #endif /* CONFIG_IWLWIFI_DEBUG */ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) { status &= AGG_TX_STATUS_MSK; switch (status) { case AGG_TX_STATE_UNDERRUN_MSK: priv->reply_agg_tx_stats.underrun++; break; case AGG_TX_STATE_BT_PRIO_MSK: priv->reply_agg_tx_stats.bt_prio++; break; case AGG_TX_STATE_FEW_BYTES_MSK: priv->reply_agg_tx_stats.few_bytes++; break; case AGG_TX_STATE_ABORT_MSK: priv->reply_agg_tx_stats.abort++; break; case AGG_TX_STATE_LAST_SENT_TTL_MSK: priv->reply_agg_tx_stats.last_sent_ttl++; break; case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK: priv->reply_agg_tx_stats.last_sent_try++; break; case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK: priv->reply_agg_tx_stats.last_sent_bt_kill++; break; case AGG_TX_STATE_SCD_QUERY_MSK: priv->reply_agg_tx_stats.scd_query++; break; case AGG_TX_STATE_TEST_BAD_CRC32_MSK: priv->reply_agg_tx_stats.bad_crc32++; break; case AGG_TX_STATE_RESPONSE_MSK: priv->reply_agg_tx_stats.response++; break; case AGG_TX_STATE_DUMP_TX_MSK: priv->reply_agg_tx_stats.dump_tx++; break; case AGG_TX_STATE_DELAY_TX_MSK: priv->reply_agg_tx_stats.delay_tx++; break; default: priv->reply_agg_tx_stats.unknown++; break; } } static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, struct iwlagn_tx_resp *tx_resp) { struct agg_tx_status *frame_status = &tx_resp->status; int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> IWLAGN_TX_RES_TID_POS; int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> IWLAGN_TX_RES_RA_POS; struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg; u32 status = le16_to_cpu(tx_resp->status.status); int i; WARN_ON(tid == IWL_TID_NON_QOS); if (agg->wait_for_ba) IWL_DEBUG_TX_REPLY(priv, "got tx response w/o block-ack\n"); agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); agg->wait_for_ba = (tx_resp->frame_count > 1); /* * If the BT kill count is non-zero, we'll get this * notification again. */ if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) { IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); } if (tx_resp->frame_count == 1) return; /* Construct bit-map of pending frames within Tx window */ for (i = 0; i < tx_resp->frame_count; i++) { u16 fstatus = le16_to_cpu(frame_status[i].status); if (status & AGG_TX_STATUS_MSK) iwlagn_count_agg_tx_err_status(priv, fstatus); if (status & (AGG_TX_STATE_FEW_BYTES_MSK | AGG_TX_STATE_ABORT_MSK)) continue; IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), " "try-count (0x%08x)\n", iwl_get_agg_tx_fail_reason(fstatus), fstatus & AGG_TX_STATUS_MSK, fstatus & AGG_TX_TRY_MSK); } } #ifdef CONFIG_IWLWIFI_DEBUG #define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x const char *iwl_get_agg_tx_fail_reason(u16 status) { status &= AGG_TX_STATUS_MSK; switch (status) { case AGG_TX_STATE_TRANSMITTED: return "SUCCESS"; AGG_TX_STATE_FAIL(UNDERRUN_MSK); AGG_TX_STATE_FAIL(BT_PRIO_MSK); AGG_TX_STATE_FAIL(FEW_BYTES_MSK); AGG_TX_STATE_FAIL(ABORT_MSK); AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK); AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK); AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK); AGG_TX_STATE_FAIL(SCD_QUERY_MSK); AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK); AGG_TX_STATE_FAIL(RESPONSE_MSK); AGG_TX_STATE_FAIL(DUMP_TX_MSK); AGG_TX_STATE_FAIL(DELAY_TX_MSK); } return "UNKNOWN"; } #endif /* CONFIG_IWLWIFI_DEBUG */ static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) { return le32_to_cpup((__le32 *)&tx_resp->status + tx_resp->frame_count) & MAX_SN; } static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) { status &= TX_STATUS_MSK; switch (status) { case TX_STATUS_POSTPONE_DELAY: priv->reply_tx_stats.pp_delay++; break; case TX_STATUS_POSTPONE_FEW_BYTES: priv->reply_tx_stats.pp_few_bytes++; break; case TX_STATUS_POSTPONE_BT_PRIO: priv->reply_tx_stats.pp_bt_prio++; break; case TX_STATUS_POSTPONE_QUIET_PERIOD: priv->reply_tx_stats.pp_quiet_period++; break; case TX_STATUS_POSTPONE_CALC_TTAK: priv->reply_tx_stats.pp_calc_ttak++; break; case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: priv->reply_tx_stats.int_crossed_retry++; break; case TX_STATUS_FAIL_SHORT_LIMIT: priv->reply_tx_stats.short_limit++; break; case TX_STATUS_FAIL_LONG_LIMIT: priv->reply_tx_stats.long_limit++; break; case TX_STATUS_FAIL_FIFO_UNDERRUN: priv->reply_tx_stats.fifo_underrun++; break; case TX_STATUS_FAIL_DRAIN_FLOW: priv->reply_tx_stats.drain_flow++; break; case TX_STATUS_FAIL_RFKILL_FLUSH: priv->reply_tx_stats.rfkill_flush++; break; case TX_STATUS_FAIL_LIFE_EXPIRE: priv->reply_tx_stats.life_expire++; break; case TX_STATUS_FAIL_DEST_PS: priv->reply_tx_stats.dest_ps++; break; case TX_STATUS_FAIL_HOST_ABORTED: priv->reply_tx_stats.host_abort++; break; case TX_STATUS_FAIL_BT_RETRY: priv->reply_tx_stats.bt_retry++; break; case TX_STATUS_FAIL_STA_INVALID: priv->reply_tx_stats.sta_invalid++; break; case TX_STATUS_FAIL_FRAG_DROPPED: priv->reply_tx_stats.frag_drop++; break; case TX_STATUS_FAIL_TID_DISABLE: priv->reply_tx_stats.tid_disable++; break; case TX_STATUS_FAIL_FIFO_FLUSHED: priv->reply_tx_stats.fifo_flush++; break; case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL: priv->reply_tx_stats.insuff_cf_poll++; break; case TX_STATUS_FAIL_PASSIVE_NO_RX: priv->reply_tx_stats.fail_hw_drop++; break; case TX_STATUS_FAIL_NO_BEACON_ON_RADAR: priv->reply_tx_stats.sta_color_mismatch++; break; default: priv->reply_tx_stats.unknown++; break; } } static void iwlagn_set_tx_status(struct iwl_priv *priv, struct ieee80211_tx_info *info, struct iwlagn_tx_resp *tx_resp, bool is_agg) { u16 status = le16_to_cpu(tx_resp->status.status); info->status.rates[0].count = tx_resp->failure_frame + 1; if (is_agg) info->flags &= ~IEEE80211_TX_CTL_AMPDU; info->flags |= iwl_tx_status_to_mac80211(status); iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), info); if (!iwl_is_tx_success(status)) iwlagn_count_tx_err_status(priv, status); } static void iwl_check_abort_status(struct iwl_priv *priv, u8 frame_count, u32 status) { if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { IWL_ERR(priv, "Tx flush command to flush out all frames\n"); if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) queue_work(priv->workqueue, &priv->tx_flush); } } static int iwl_reclaim(struct iwl_priv *priv, int sta_id, int tid, int txq_id, int ssn, struct sk_buff_head *skbs) { if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE && tid != IWL_TID_NON_QOS && txq_id != priv->tid_data[sta_id][tid].agg.txq_id)) { /* * FIXME: this is a uCode bug which need to be addressed, * log the information and return for now. * Since it is can possibly happen very often and in order * not to fill the syslog, don't use IWL_ERR or IWL_WARN */ IWL_DEBUG_TX_QUEUES(priv, "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n", txq_id, sta_id, tid, priv->tid_data[sta_id][tid].agg.txq_id); return 1; } iwl_trans_reclaim(priv->trans, txq_id, ssn, skbs); return 0; } int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); struct iwlagn_tx_resp *tx_resp = (void *)pkt->data; struct ieee80211_hdr *hdr; u32 status = le16_to_cpu(tx_resp->status.status); u16 ssn = iwlagn_get_scd_ssn(tx_resp); int tid; int sta_id; int freed; struct ieee80211_tx_info *info; struct sk_buff_head skbs; struct sk_buff *skb; struct iwl_rxon_context *ctx; bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); bool is_offchannel_skb; tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> IWLAGN_TX_RES_TID_POS; sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> IWLAGN_TX_RES_RA_POS; spin_lock(&priv->sta_lock); if (is_agg) iwl_rx_reply_tx_agg(priv, tx_resp); __skb_queue_head_init(&skbs); is_offchannel_skb = false; if (tx_resp->frame_count == 1) { u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); if (is_agg) { /* If this is an aggregation queue, we can rely on the * ssn since the wifi sequence number corresponds to * the index in the TFD ring (%256). * The seq_ctl is the sequence control of the packet * to which this Tx response relates. But if there is a * hole in the bitmap of the BA we received, this Tx * response may allow to reclaim the hole and all the * subsequent packets that were already acked. * In that case, seq_ctl != ssn, and the next packet * to be reclaimed will be ssn and not seq_ctl. */ next_reclaimed = ssn; } if (tid != IWL_TID_NON_QOS) { priv->tid_data[sta_id][tid].next_reclaimed = next_reclaimed; IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", next_reclaimed); } /*we can free until ssn % q.n_bd not inclusive */ WARN_ON_ONCE(iwl_reclaim(priv, sta_id, tid, txq_id, ssn, &skbs)); iwlagn_check_ratid_empty(priv, sta_id, tid); freed = 0; /* process frames */ skb_queue_walk(&skbs, skb) { hdr = (struct ieee80211_hdr *)skb->data; if (!ieee80211_is_data_qos(hdr->frame_control)) priv->last_seq_ctl = tx_resp->seq_ctl; info = IEEE80211_SKB_CB(skb); ctx = info->driver_data[0]; iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); memset(&info->status, 0, sizeof(info->status)); if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && iwl_is_associated_ctx(ctx) && ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) { /* block and stop all queues */ priv->passive_no_rx = true; IWL_DEBUG_TX_QUEUES(priv, "stop all queues: " "passive channel"); ieee80211_stop_queues(priv->hw); IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) " "rate_n_flags 0x%x retries %d\n", txq_id, iwl_get_tx_fail_reason(status), status, le32_to_cpu(tx_resp->rate_n_flags), tx_resp->failure_frame); IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, idx=%d\n", tx_resp->frame_count, cmd_index); } /* check if BAR is needed */ if (is_agg && !iwl_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), tx_resp, is_agg); if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); is_offchannel_skb = (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } WARN_ON(!is_agg && freed != 1); /* * An offchannel frame can be send only on the AUX queue, where * there is no aggregation (and reordering) so it only is single * skb is expected to be processed. */ WARN_ON(is_offchannel_skb && freed != 1); } iwl_check_abort_status(priv, tx_resp->frame_count, status); spin_unlock(&priv->sta_lock); while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); ieee80211_tx_status(priv->hw, skb); } if (is_offchannel_skb) iwl_scan_offchannel_skb_status(priv); return 0; } /** * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA * * Handles block-acknowledge notification from device, which reports success * of frames sent via aggregation. */ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; struct iwl_ht_agg *agg; struct sk_buff_head reclaimed_skbs; struct ieee80211_tx_info *info; struct ieee80211_hdr *hdr; struct sk_buff *skb; int sta_id; int tid; int freed; /* "flow" corresponds to Tx queue */ u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); /* "ssn" is start of block-ack Tx window, corresponds to index * (in Tx queue's circular buffer) of first TFD/frame in window */ u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); if (scd_flow >= priv->cfg->base_params->num_of_queues) { IWL_ERR(priv, "BUG_ON scd_flow is bigger than number of queues\n"); return 0; } sta_id = ba_resp->sta_id; tid = ba_resp->tid; agg = &priv->tid_data[sta_id][tid].agg; spin_lock(&priv->sta_lock); if (unlikely(!agg->wait_for_ba)) { if (unlikely(ba_resp->bitmap)) IWL_ERR(priv, "Received BA when not expected\n"); spin_unlock(&priv->sta_lock); return 0; } __skb_queue_head_init(&reclaimed_skbs); /* Release all TFDs before the SSN, i.e. all TFDs in front of * block-ack window (we assume that they've been successfully * transmitted ... if not, it's too late anyway). */ if (iwl_reclaim(priv, sta_id, tid, scd_flow, ba_resp_scd_ssn, &reclaimed_skbs)) { spin_unlock(&priv->sta_lock); return 0; } IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n", agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32, ba_resp->sta_id); IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl), (unsigned long long)le64_to_cpu(ba_resp->bitmap), scd_flow, ba_resp_scd_ssn, ba_resp->txed, ba_resp->txed_2_done); /* Mark that the expected block-ack response arrived */ agg->wait_for_ba = false; /* Sanity check values reported by uCode */ if (ba_resp->txed_2_done > ba_resp->txed) { IWL_DEBUG_TX_REPLY(priv, "bogus sent(%d) and ack(%d) count\n", ba_resp->txed, ba_resp->txed_2_done); /* * set txed_2_done = txed, * so it won't impact rate scale */ ba_resp->txed = ba_resp->txed_2_done; } priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; iwlagn_check_ratid_empty(priv, sta_id, tid); freed = 0; skb_queue_walk(&reclaimed_skbs, skb) { hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_data_qos(hdr->frame_control)) freed++; else WARN_ON_ONCE(1); info = IEEE80211_SKB_CB(skb); iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */ info = IEEE80211_SKB_CB(skb); memset(&info->status, 0, sizeof(info->status)); info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = ba_resp->txed_2_done; info->status.ampdu_len = ba_resp->txed; iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info); } } spin_unlock(&priv->sta_lock); while (!skb_queue_empty(&reclaimed_skbs)) { skb = __skb_dequeue(&reclaimed_skbs); ieee80211_tx_status(priv->hw, skb); } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/tt.h0000644000175000017500000001103412026211315023520 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #ifndef __iwl_tt_setting_h__ #define __iwl_tt_setting_h__ #include "commands.h" #define IWL_ABSOLUTE_ZERO 0 #define IWL_ABSOLUTE_MAX 0xFFFFFFFF #define IWL_TT_INCREASE_MARGIN 5 #define IWL_TT_CT_KILL_MARGIN 3 enum iwl_antenna_ok { IWL_ANT_OK_NONE, IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, }; /* Thermal Throttling State Machine states */ enum iwl_tt_state { IWL_TI_0, /* normal temperature, system power state */ IWL_TI_1, /* high temperature detect, low power state */ IWL_TI_2, /* higher temperature detected, lower power state */ IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */ IWL_TI_STATE_MAX }; /** * struct iwl_tt_restriction - Thermal Throttling restriction table * @tx_stream: number of tx stream allowed * @is_ht: ht enable/disable * @rx_stream: number of rx stream allowed * * This table is used by advance thermal throttling management * based on the current thermal throttling state, and determines * the number of tx/rx streams and the status of HT operation. */ struct iwl_tt_restriction { enum iwl_antenna_ok tx_stream; enum iwl_antenna_ok rx_stream; bool is_ht; }; /** * struct iwl_tt_trans - Thermal Throttling transaction table * @next_state: next thermal throttling mode * @tt_low: low temperature threshold to change state * @tt_high: high temperature threshold to change state * * This is used by the advanced thermal throttling algorithm * to determine the next thermal state to go based on the * current temperature. */ struct iwl_tt_trans { enum iwl_tt_state next_state; u32 tt_low; u32 tt_high; }; /** * struct iwl_tt_mgnt - Thermal Throttling Management structure * @advanced_tt: advanced thermal throttle required * @state: current Thermal Throttling state * @tt_power_mode: Thermal Throttling power mode index * being used to set power level when * when thermal throttling state != IWL_TI_0 * the tt_power_mode should set to different * power mode based on the current tt state * @tt_previous_temperature: last measured temperature * @iwl_tt_restriction: ptr to restriction tbl, used by advance * thermal throttling to determine how many tx/rx streams * should be used in tt state; and can HT be enabled or not * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling * state transaction * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature * @ct_kill_exit_tm: timer to exit thermal kill */ struct iwl_tt_mgmt { enum iwl_tt_state state; bool advanced_tt; u8 tt_power_mode; bool ct_kill_toggle; #ifdef CONFIG_IWLWIFI_DEBUG s32 tt_previous_temp; #endif struct iwl_tt_restriction *restriction; struct iwl_tt_trans *transaction; struct timer_list ct_kill_exit_tm; struct timer_list ct_kill_waiting_tm; }; u8 iwl_tt_current_power_mode(struct iwl_priv *priv); bool iwl_tt_is_low_power_state(struct iwl_priv *priv); bool iwl_ht_enabled(struct iwl_priv *priv); enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); void iwl_tt_enter_ct_kill(struct iwl_priv *priv); void iwl_tt_exit_ct_kill(struct iwl_priv *priv); void iwl_tt_handler(struct iwl_priv *priv); void iwl_tt_initialize(struct iwl_priv *priv); void iwl_tt_exit(struct iwl_priv *priv); #endif /* __iwl_tt_setting_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/tt.c0000644000175000017500000005366012026211315023526 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include #include #include #include #include #include "iwl-io.h" #include "iwl-modparams.h" #include "iwl-debug.h" #include "agn.h" #include "dev.h" #include "commands.h" #include "tt.h" /* default Thermal Throttling transaction table * Current state | Throttling Down | Throttling Up *============================================================================= * Condition Nxt State Condition Nxt State Condition Nxt State *----------------------------------------------------------------------------- * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 *============================================================================= */ static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} }; static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} }; static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} }; static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} }; /* Advance Thermal Throttling default restriction table */ static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } }; bool iwl_tt_is_low_power_state(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; if (tt->state >= IWL_TI_1) return true; return false; } u8 iwl_tt_current_power_mode(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; return tt->tt_power_mode; } bool iwl_ht_enabled(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; struct iwl_tt_restriction *restriction; if (!priv->thermal_throttle.advanced_tt) return true; restriction = tt->restriction + tt->state; return restriction->is_ht; } static bool iwl_within_ct_kill_margin(struct iwl_priv *priv) { s32 temp = priv->temperature; /* degrees CELSIUS except specified */ bool within_margin = false; if (!priv->thermal_throttle.advanced_tt) within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= CT_KILL_THRESHOLD_LEGACY) ? true : false; else within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= CT_KILL_THRESHOLD) ? true : false; return within_margin; } bool iwl_check_for_ct_kill(struct iwl_priv *priv) { bool is_ct_kill = false; if (iwl_within_ct_kill_margin(priv)) { iwl_tt_enter_ct_kill(priv); is_ct_kill = true; } return is_ct_kill; } enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; struct iwl_tt_restriction *restriction; if (!priv->thermal_throttle.advanced_tt) return IWL_ANT_OK_MULTI; restriction = tt->restriction + tt->state; return restriction->tx_stream; } enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; struct iwl_tt_restriction *restriction; if (!priv->thermal_throttle.advanced_tt) return IWL_ANT_OK_MULTI; restriction = tt->restriction + tt->state; return restriction->rx_stream; } #define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ #define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ /* * toggle the bit to wake up uCode and check the temperature * if the temperature is below CT, uCode will stay awake and send card * state notification with CT_KILL bit clear to inform Thermal Throttling * Management to change state. Otherwise, uCode will go back to sleep * without doing anything, driver should continue the 5 seconds timer * to wake up uCode for temperature check until temperature drop below CT */ static void iwl_tt_check_exit_ct_kill(unsigned long data) { struct iwl_priv *priv = (struct iwl_priv *)data; struct iwl_tt_mgmt *tt = &priv->thermal_throttle; unsigned long flags; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (tt->state == IWL_TI_CT_KILL) { if (priv->thermal_throttle.ct_kill_toggle) { iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); priv->thermal_throttle.ct_kill_toggle = false; } else { iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); priv->thermal_throttle.ct_kill_toggle = true; } iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); spin_lock_irqsave(&priv->trans->reg_lock, flags); if (likely(iwl_grab_nic_access(priv->trans))) iwl_release_nic_access(priv->trans); spin_unlock_irqrestore(&priv->trans->reg_lock, flags); /* Reschedule the ct_kill timer to occur in * CT_KILL_EXIT_DURATION seconds to ensure we get a * thermal update */ IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + CT_KILL_EXIT_DURATION * HZ); } } static void iwl_perform_ct_kill_task(struct iwl_priv *priv, bool stop) { if (stop) { IWL_DEBUG_TEMP(priv, "Stop all queues\n"); if (priv->mac80211_registered) ieee80211_stop_queues(priv->hw); IWL_DEBUG_TEMP(priv, "Schedule 5 seconds CT_KILL Timer\n"); mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + CT_KILL_EXIT_DURATION * HZ); } else { IWL_DEBUG_TEMP(priv, "Wake all queues\n"); if (priv->mac80211_registered) ieee80211_wake_queues(priv->hw); } } static void iwl_tt_ready_for_ct_kill(unsigned long data) { struct iwl_priv *priv = (struct iwl_priv *)data; struct iwl_tt_mgmt *tt = &priv->thermal_throttle; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; /* temperature timer expired, ready to go into CT_KILL state */ if (tt->state != IWL_TI_CT_KILL) { IWL_DEBUG_TEMP(priv, "entering CT_KILL state when " "temperature timer expired\n"); tt->state = IWL_TI_CT_KILL; set_bit(STATUS_CT_KILL, &priv->status); iwl_perform_ct_kill_task(priv, true); } } static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) { IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); /* make request to retrieve statistics information */ iwl_send_statistics_request(priv, CMD_SYNC, false); /* Reschedule the ct_kill wait timer */ mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); } #define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) #define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) #define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) /* * Legacy thermal throttling * 1) Avoid NIC destruction due to high temperatures * Chip will identify dangerously high temperatures that can * harm the device and will power down * 2) Avoid the NIC power down due to high temperature * Throttle early enough to lower the power consumption before * drastic steps are needed */ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; enum iwl_tt_state old_state; #ifdef CONFIG_IWLWIFI_DEBUG if ((tt->tt_previous_temp) && (temp > tt->tt_previous_temp) && ((temp - tt->tt_previous_temp) > IWL_TT_INCREASE_MARGIN)) { IWL_DEBUG_TEMP(priv, "Temperature increase %d degree Celsius\n", (temp - tt->tt_previous_temp)); } #endif old_state = tt->state; /* in Celsius */ if (temp >= IWL_MINIMAL_POWER_THRESHOLD) tt->state = IWL_TI_CT_KILL; else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) tt->state = IWL_TI_2; else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) tt->state = IWL_TI_1; else tt->state = IWL_TI_0; #ifdef CONFIG_IWLWIFI_DEBUG tt->tt_previous_temp = temp; #endif /* stop ct_kill_waiting_tm timer */ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (tt->state != old_state) { switch (tt->state) { case IWL_TI_0: /* * When the system is ready to go back to IWL_TI_0 * we only have to call iwl_power_update_mode() to * do so. */ break; case IWL_TI_1: tt->tt_power_mode = IWL_POWER_INDEX_3; break; case IWL_TI_2: tt->tt_power_mode = IWL_POWER_INDEX_4; break; default: tt->tt_power_mode = IWL_POWER_INDEX_5; break; } mutex_lock(&priv->mutex); if (old_state == IWL_TI_CT_KILL) clear_bit(STATUS_CT_KILL, &priv->status); if (tt->state != IWL_TI_CT_KILL && iwl_power_update_mode(priv, true)) { /* TT state not updated * try again during next temperature read */ if (old_state == IWL_TI_CT_KILL) set_bit(STATUS_CT_KILL, &priv->status); tt->state = old_state; IWL_ERR(priv, "Cannot update power mode, " "TT state not updated\n"); } else { if (tt->state == IWL_TI_CT_KILL) { if (force) { set_bit(STATUS_CT_KILL, &priv->status); iwl_perform_ct_kill_task(priv, true); } else { iwl_prepare_ct_kill_task(priv); tt->state = old_state; } } else if (old_state == IWL_TI_CT_KILL && tt->state != IWL_TI_CT_KILL) iwl_perform_ct_kill_task(priv, false); IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n", tt->state); IWL_DEBUG_TEMP(priv, "Power Index change to %u\n", tt->tt_power_mode); } mutex_unlock(&priv->mutex); } } /* * Advance thermal throttling * 1) Avoid NIC destruction due to high temperatures * Chip will identify dangerously high temperatures that can * harm the device and will power down * 2) Avoid the NIC power down due to high temperature * Throttle early enough to lower the power consumption before * drastic steps are needed * Actions include relaxing the power down sleep thresholds and * decreasing the number of TX streams * 3) Avoid throughput performance impact as much as possible * *============================================================================= * Condition Nxt State Condition Nxt State Condition Nxt State *----------------------------------------------------------------------------- * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 *============================================================================= */ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; int i; bool changed = false; enum iwl_tt_state old_state; struct iwl_tt_trans *transaction; old_state = tt->state; for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { /* based on the current TT state, * find the curresponding transaction table * each table has (IWL_TI_STATE_MAX - 1) entries * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) * will advance to the correct table. * then based on the current temperature * find the next state need to transaction to * go through all the possible (IWL_TI_STATE_MAX - 1) entries * in the current table to see if transaction is needed */ transaction = tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) + i); if (temp >= transaction->tt_low && temp <= transaction->tt_high) { #ifdef CONFIG_IWLWIFI_DEBUG if ((tt->tt_previous_temp) && (temp > tt->tt_previous_temp) && ((temp - tt->tt_previous_temp) > IWL_TT_INCREASE_MARGIN)) { IWL_DEBUG_TEMP(priv, "Temperature increase %d " "degree Celsius\n", (temp - tt->tt_previous_temp)); } tt->tt_previous_temp = temp; #endif if (old_state != transaction->next_state) { changed = true; tt->state = transaction->next_state; } break; } } /* stop ct_kill_waiting_tm timer */ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (changed) { if (tt->state >= IWL_TI_1) { /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ tt->tt_power_mode = IWL_POWER_INDEX_5; if (!iwl_ht_enabled(priv)) { struct iwl_rxon_context *ctx; for_each_context(priv, ctx) { struct iwl_rxon_cmd *rxon; rxon = &ctx->staging; /* disable HT */ rxon->flags &= ~( RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK | RXON_FLG_HT_PROT_MSK); } } else { /* check HT capability and set * according to the system HT capability * in case get disabled before */ iwl_set_rxon_ht(priv, &priv->current_ht_config); } } else { /* * restore system power setting -- it will be * recalculated automatically. */ /* check HT capability and set * according to the system HT capability * in case get disabled before */ iwl_set_rxon_ht(priv, &priv->current_ht_config); } mutex_lock(&priv->mutex); if (old_state == IWL_TI_CT_KILL) clear_bit(STATUS_CT_KILL, &priv->status); if (tt->state != IWL_TI_CT_KILL && iwl_power_update_mode(priv, true)) { /* TT state not updated * try again during next temperature read */ IWL_ERR(priv, "Cannot update power mode, " "TT state not updated\n"); if (old_state == IWL_TI_CT_KILL) set_bit(STATUS_CT_KILL, &priv->status); tt->state = old_state; } else { IWL_DEBUG_TEMP(priv, "Thermal Throttling to new state: %u\n", tt->state); if (old_state != IWL_TI_CT_KILL && tt->state == IWL_TI_CT_KILL) { if (force) { IWL_DEBUG_TEMP(priv, "Enter IWL_TI_CT_KILL\n"); set_bit(STATUS_CT_KILL, &priv->status); iwl_perform_ct_kill_task(priv, true); } else { iwl_prepare_ct_kill_task(priv); tt->state = old_state; } } else if (old_state == IWL_TI_CT_KILL && tt->state != IWL_TI_CT_KILL) { IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n"); iwl_perform_ct_kill_task(priv, false); } } mutex_unlock(&priv->mutex); } } /* Card State Notification indicated reach critical temperature * if PSP not enable, no Thermal Throttling function will be performed * just set the GP1 bit to acknowledge the event * otherwise, go into IWL_TI_CT_KILL state * since Card State Notification will not provide any temperature reading * for Legacy mode * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() * for advance mode * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state */ static void iwl_bg_ct_enter(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); struct iwl_tt_mgmt *tt = &priv->thermal_throttle; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (!iwl_is_ready(priv)) return; if (tt->state != IWL_TI_CT_KILL) { IWL_ERR(priv, "Device reached critical temperature " "- ucode going to sleep!\n"); if (!priv->thermal_throttle.advanced_tt) iwl_legacy_tt_handler(priv, IWL_MINIMAL_POWER_THRESHOLD, true); else iwl_advance_tt_handler(priv, CT_KILL_THRESHOLD + 1, true); } } /* Card State Notification indicated out of critical temperature * since Card State Notification will not provide any temperature reading * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state */ static void iwl_bg_ct_exit(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); struct iwl_tt_mgmt *tt = &priv->thermal_throttle; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (!iwl_is_ready(priv)) return; /* stop ct_kill_exit_tm timer */ del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); if (tt->state == IWL_TI_CT_KILL) { IWL_ERR(priv, "Device temperature below critical" "- ucode awake!\n"); /* * exit from CT_KILL state * reset the current temperature reading */ priv->temperature = 0; if (!priv->thermal_throttle.advanced_tt) iwl_legacy_tt_handler(priv, IWL_REDUCED_PERFORMANCE_THRESHOLD_2, true); else iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, true); } } void iwl_tt_enter_ct_kill(struct iwl_priv *priv) { if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n"); queue_work(priv->workqueue, &priv->ct_enter); } void iwl_tt_exit_ct_kill(struct iwl_priv *priv) { if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n"); queue_work(priv->workqueue, &priv->ct_exit); } static void iwl_bg_tt_work(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); s32 temp = priv->temperature; /* degrees CELSIUS except specified */ if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (!priv->thermal_throttle.advanced_tt) iwl_legacy_tt_handler(priv, temp, false); else iwl_advance_tt_handler(priv, temp, false); } void iwl_tt_handler(struct iwl_priv *priv) { if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n"); queue_work(priv->workqueue, &priv->tt_work); } /* Thermal throttling initialization * For advance thermal throttling: * Initialize Thermal Index and temperature threshold table * Initialize thermal throttling restriction table */ void iwl_tt_initialize(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); struct iwl_tt_trans *transaction; IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n"); memset(tt, 0, sizeof(struct iwl_tt_mgmt)); tt->state = IWL_TI_0; init_timer(&priv->thermal_throttle.ct_kill_exit_tm); priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; priv->thermal_throttle.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill; init_timer(&priv->thermal_throttle.ct_kill_waiting_tm); priv->thermal_throttle.ct_kill_waiting_tm.data = (unsigned long)priv; priv->thermal_throttle.ct_kill_waiting_tm.function = iwl_tt_ready_for_ct_kill; /* setup deferred ct kill work */ INIT_WORK(&priv->tt_work, iwl_bg_tt_work); INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); if (priv->cfg->base_params->adv_thermal_throttle) { IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); tt->restriction = kcalloc(IWL_TI_STATE_MAX, sizeof(struct iwl_tt_restriction), GFP_KERNEL); tt->transaction = kcalloc(IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1), sizeof(struct iwl_tt_trans), GFP_KERNEL); if (!tt->restriction || !tt->transaction) { IWL_ERR(priv, "Fallback to Legacy Throttling\n"); priv->thermal_throttle.advanced_tt = false; kfree(tt->restriction); tt->restriction = NULL; kfree(tt->transaction); tt->transaction = NULL; } else { transaction = tt->transaction + (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); memcpy(transaction, &tt_range_0[0], size); transaction = tt->transaction + (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); memcpy(transaction, &tt_range_1[0], size); transaction = tt->transaction + (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); memcpy(transaction, &tt_range_2[0], size); transaction = tt->transaction + (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); memcpy(transaction, &tt_range_3[0], size); size = sizeof(struct iwl_tt_restriction) * IWL_TI_STATE_MAX; memcpy(tt->restriction, &restriction_range[0], size); priv->thermal_throttle.advanced_tt = true; } } else { IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n"); priv->thermal_throttle.advanced_tt = false; } } /* cleanup thermal throttling management related memory and timer */ void iwl_tt_exit(struct iwl_priv *priv) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; /* stop ct_kill_exit_tm timer if activated */ del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); /* stop ct_kill_waiting_tm timer if activated */ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); cancel_work_sync(&priv->tt_work); cancel_work_sync(&priv->ct_enter); cancel_work_sync(&priv->ct_exit); if (priv->thermal_throttle.advanced_tt) { /* free advance thermal throttling memory */ kfree(tt->restriction); tt->restriction = NULL; kfree(tt->transaction); tt->transaction = NULL; } } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/testmode.c0000644000175000017500000003442612026211315024722 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "iwl-debug.h" #include "iwl-trans.h" #include "dev.h" #include "agn.h" #include "iwl-test.h" #include "iwl-testmode.h" static int iwl_testmode_send_cmd(struct iwl_op_mode *op_mode, struct iwl_host_cmd *cmd) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); return iwl_dvm_send_cmd(priv, cmd); } static bool iwl_testmode_valid_hw_addr(u32 addr) { if (iwlagn_hw_valid_rtc_data_addr(addr)) return true; if (IWLAGN_RTC_INST_LOWER_BOUND <= addr && addr < IWLAGN_RTC_INST_UPPER_BOUND) return true; return false; } static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); return priv->fw->ucode_ver; } static struct sk_buff* iwl_testmode_alloc_reply(struct iwl_op_mode *op_mode, int len) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); return cfg80211_testmode_alloc_reply_skb(priv->hw->wiphy, len); } static int iwl_testmode_reply(struct iwl_op_mode *op_mode, struct sk_buff *skb) { return cfg80211_testmode_reply(skb); } static struct sk_buff *iwl_testmode_alloc_event(struct iwl_op_mode *op_mode, int len) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); return cfg80211_testmode_alloc_event_skb(priv->hw->wiphy, len, GFP_ATOMIC); } static void iwl_testmode_event(struct iwl_op_mode *op_mode, struct sk_buff *skb) { return cfg80211_testmode_event(skb, GFP_ATOMIC); } static struct iwl_test_ops tst_ops = { .send_cmd = iwl_testmode_send_cmd, .valid_hw_addr = iwl_testmode_valid_hw_addr, .get_fw_ver = iwl_testmode_get_fw_ver, .alloc_reply = iwl_testmode_alloc_reply, .reply = iwl_testmode_reply, .alloc_event = iwl_testmode_alloc_event, .event = iwl_testmode_event, }; void iwl_testmode_init(struct iwl_priv *priv) { iwl_test_init(&priv->tst, priv->trans, &tst_ops); } void iwl_testmode_free(struct iwl_priv *priv) { iwl_test_free(&priv->tst); } static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv) { struct iwl_notification_wait calib_wait; static const u8 calib_complete[] = { CALIBRATION_COMPLETE_NOTIFICATION }; int ret; iwl_init_notification_wait(&priv->notif_wait, &calib_wait, calib_complete, ARRAY_SIZE(calib_complete), NULL, NULL); ret = iwl_init_alive_start(priv); if (ret) { IWL_ERR(priv, "Fail init calibration: %d\n", ret); goto cfg_init_calib_error; } ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, 2 * HZ); if (ret) IWL_ERR(priv, "Error detecting" " CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret); return ret; cfg_init_calib_error: iwl_remove_notification(&priv->notif_wait, &calib_wait); return ret; } /* * This function handles the user application commands for driver. * * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the * handlers respectively. * * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned * value of the actual command execution is replied to the user application. * * If there's any message responding to the user space, IWL_TM_ATTR_SYNC_RSP * is used for carry the message while IWL_TM_ATTR_COMMAND must set to * IWL_TM_CMD_DEV2APP_SYNC_RSP. * * @hw: ieee80211_hw object that represents the device * @tb: gnl message fields from the user space */ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_trans *trans = priv->trans; struct sk_buff *skb; unsigned char *rsp_data_ptr = NULL; int status = 0, rsp_data_len = 0; u32 inst_size = 0, data_size = 0; const struct fw_img *img; switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: rsp_data_ptr = (unsigned char *)priv->cfg->name; rsp_data_len = strlen(priv->cfg->name); skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, rsp_data_len + 20); if (!skb) { IWL_ERR(priv, "Memory allocation fail\n"); return -ENOMEM; } if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_SYNC_RSP) || nla_put(skb, IWL_TM_ATTR_SYNC_RSP, rsp_data_len, rsp_data_ptr)) goto nla_put_failure; status = cfg80211_testmode_reply(skb); if (status < 0) IWL_ERR(priv, "Error sending msg : %d\n", status); break; case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); if (status) IWL_ERR(priv, "Error loading init ucode: %d\n", status); break; case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: iwl_testmode_cfg_init_calib(priv); priv->ucode_loaded = false; iwl_trans_stop_device(trans); break; case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); if (status) { IWL_ERR(priv, "Error loading runtime ucode: %d\n", status); break; } status = iwl_alive_start(priv); if (status) IWL_ERR(priv, "Error starting the device: %d\n", status); break; case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: iwl_scan_cancel_timeout(priv, 200); priv->ucode_loaded = false; iwl_trans_stop_device(trans); status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); if (status) { IWL_ERR(priv, "Error loading WOWLAN ucode: %d\n", status); break; } status = iwl_alive_start(priv); if (status) IWL_ERR(priv, "Error starting the device: %d\n", status); break; case IWL_TM_CMD_APP2DEV_GET_EEPROM: if (priv->eeprom_blob) { skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, priv->eeprom_blob_size + 20); if (!skb) { IWL_ERR(priv, "Memory allocation fail\n"); return -ENOMEM; } if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_EEPROM_RSP) || nla_put(skb, IWL_TM_ATTR_EEPROM, priv->eeprom_blob_size, priv->eeprom_blob)) goto nla_put_failure; status = cfg80211_testmode_reply(skb); if (status < 0) IWL_ERR(priv, "Error sending msg : %d\n", status); } else return -ENODATA; break; case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: if (!tb[IWL_TM_ATTR_FIXRATE]) { IWL_ERR(priv, "Missing fixrate setting\n"); return -ENOMSG; } priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]); break; case IWL_TM_CMD_APP2DEV_GET_FW_INFO: skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8); if (!skb) { IWL_ERR(priv, "Memory allocation fail\n"); return -ENOMEM; } if (!priv->ucode_loaded) { IWL_ERR(priv, "No uCode has not been loaded\n"); return -EINVAL; } else { img = &priv->fw->img[priv->cur_ucode]; inst_size = img->sec[IWL_UCODE_SECTION_INST].len; data_size = img->sec[IWL_UCODE_SECTION_DATA].len; } if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->cur_ucode) || nla_put_u32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size) || nla_put_u32(skb, IWL_TM_ATTR_FW_DATA_SIZE, data_size)) goto nla_put_failure; status = cfg80211_testmode_reply(skb); if (status < 0) IWL_ERR(priv, "Error sending msg : %d\n", status); break; default: IWL_ERR(priv, "Unknown testmode driver command ID\n"); return -ENOSYS; } return status; nla_put_failure: kfree_skb(skb); return -EMSGSIZE; } /* * This function handles the user application switch ucode ownership. * * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and * decide who the current owner of the uCode * * If the current owner is OWNERSHIP_TM, then the only host command * can deliver to uCode is from testmode, all the other host commands * will dropped. * * default driver is the owner of uCode in normal operational mode * * @hw: ieee80211_hw object that represents the device * @tb: gnl message fields from the user space */ static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); u8 owner; if (!tb[IWL_TM_ATTR_UCODE_OWNER]) { IWL_ERR(priv, "Missing ucode owner\n"); return -ENOMSG; } owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]); if (owner == IWL_OWNERSHIP_DRIVER) { priv->ucode_owner = owner; iwl_test_enable_notifications(&priv->tst, false); } else if (owner == IWL_OWNERSHIP_TM) { priv->ucode_owner = owner; iwl_test_enable_notifications(&priv->tst, true); } else { IWL_ERR(priv, "Invalid owner\n"); return -EINVAL; } return 0; } /* The testmode gnl message handler that takes the gnl message from the * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then * invoke the corresponding handlers. * * This function is invoked when there is user space application sending * gnl message through the testmode tunnel NL80211_CMD_TESTMODE regulated * by nl80211. * * It retrieves the mandatory field, IWL_TM_ATTR_COMMAND, before * dispatching it to the corresponding handler. * * If IWL_TM_ATTR_COMMAND is missing, -ENOMSG is replied to user application; * -ENOSYS is replied to the user application if the command is unknown; * Otherwise, the command is dispatched to the respective handler. * * @hw: ieee80211_hw object that represents the device * @data: pointer to user space message * @len: length in byte of @data */ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) { struct nlattr *tb[IWL_TM_ATTR_MAX]; struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int result; result = iwl_test_parse(&priv->tst, tb, data, len); if (result) return result; /* in case multiple accesses to the device happens */ mutex_lock(&priv->mutex); switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { case IWL_TM_CMD_APP2DEV_UCODE: case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: case IWL_TM_CMD_APP2DEV_END_TRACE: case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: result = iwl_test_handle_cmd(&priv->tst, tb); break; case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: case IWL_TM_CMD_APP2DEV_GET_EEPROM: case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: case IWL_TM_CMD_APP2DEV_GET_FW_INFO: IWL_DEBUG_INFO(priv, "testmode cmd to driver\n"); result = iwl_testmode_driver(hw, tb); break; case IWL_TM_CMD_APP2DEV_OWNERSHIP: IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n"); result = iwl_testmode_ownership(hw, tb); break; default: IWL_ERR(priv, "Unknown testmode command\n"); result = -ENOSYS; break; } mutex_unlock(&priv->mutex); if (result) IWL_ERR(priv, "Test cmd failed result=%d\n", result); return result; } int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int result; u32 cmd; if (cb->args[3]) { /* offset by 1 since commands start at 0 */ cmd = cb->args[3] - 1; } else { struct nlattr *tb[IWL_TM_ATTR_MAX]; result = iwl_test_parse(&priv->tst, tb, data, len); if (result) return result; cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); cb->args[3] = cmd + 1; } /* in case multiple accesses to the device happens */ mutex_lock(&priv->mutex); result = iwl_test_dump(&priv->tst, cmd, skb, cb); mutex_unlock(&priv->mutex); return result; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/sta.c0000644000175000017500000011712612026211315023664 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include "iwl-trans.h" #include "dev.h" #include "agn.h" const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) { lockdep_assert_held(&priv->sta_lock); if (sta_id >= IWLAGN_STATION_COUNT) { IWL_ERR(priv, "invalid sta_id %u", sta_id); return -EINVAL; } if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " "addr %pM\n", sta_id, priv->stations[sta_id].sta.sta.addr); if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { IWL_DEBUG_ASSOC(priv, "STA id %u addr %pM already present in uCode " "(according to driver)\n", sta_id, priv->stations[sta_id].sta.sta.addr); } else { priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", sta_id, priv->stations[sta_id].sta.sta.addr); } return 0; } static int iwl_process_add_sta_resp(struct iwl_priv *priv, struct iwl_addsta_cmd *addsta, struct iwl_rx_packet *pkt) { struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; u8 sta_id = addsta->sta.sta_id; int ret = -EIO; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", pkt->hdr.flags); return ret; } IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n", sta_id); spin_lock(&priv->sta_lock); switch (add_sta_resp->status) { case ADD_STA_SUCCESS_MSK: IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); ret = iwl_sta_ucode_activate(priv, sta_id); break; case ADD_STA_NO_ROOM_IN_TABLE: IWL_ERR(priv, "Adding station %d failed, no room in table.\n", sta_id); break; case ADD_STA_NO_BLOCK_ACK_RESOURCE: IWL_ERR(priv, "Adding station %d failed, no block ack " "resource.\n", sta_id); break; case ADD_STA_MODIFY_NON_EXIST_STA: IWL_ERR(priv, "Attempting to modify non-existing station %d\n", sta_id); break; default: IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", add_sta_resp->status); break; } IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n", priv->stations[sta_id].sta.mode == STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id, priv->stations[sta_id].sta.sta.addr); /* * XXX: The MAC address in the command buffer is often changed from * the original sent to the device. That is, the MAC address * written to the command buffer often is not the same MAC address * read from the command buffer when the command returns. This * issue has not yet been resolved and this debugging is left to * observe the problem. */ IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n", priv->stations[sta_id].sta.mode == STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); spin_unlock(&priv->sta_lock); return ret; } int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_addsta_cmd *addsta = (struct iwl_addsta_cmd *) cmd->payload; return iwl_process_add_sta_resp(priv, addsta, pkt); } int iwl_send_add_sta(struct iwl_priv *priv, struct iwl_addsta_cmd *sta, u8 flags) { int ret = 0; struct iwl_host_cmd cmd = { .id = REPLY_ADD_STA, .flags = flags, .data = { sta, }, .len = { sizeof(*sta), }, }; u8 sta_id __maybe_unused = sta->sta.sta_id; IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); if (!(flags & CMD_ASYNC)) { cmd.flags |= CMD_WANT_SKB | CMD_WANT_HCMD; might_sleep(); } ret = iwl_dvm_send_cmd(priv, &cmd); if (ret || (flags & CMD_ASYNC)) return ret; /*else the command was successfully sent in SYNC mode, need to free * the reply page */ iwl_free_resp(&cmd); if (cmd.handler_status) IWL_ERR(priv, "%s - error in the CMD response %d", __func__, cmd.handler_status); return cmd.handler_status; } bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_sta_ht_cap *ht_cap) { if (!ctx->ht.enabled || !ctx->ht.is_40mhz) return false; #ifdef CONFIG_IWLWIFI_DEBUGFS if (priv->disable_ht40) return false; #endif /* * Remainder of this function checks ht_cap, but if it's * NULL then we can do HT40 (special case for RXON) */ if (!ht_cap) return true; if (!ht_cap->ht_supported) return false; if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) return false; return true; } static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, struct ieee80211_sta *sta, struct iwl_rxon_context *ctx, __le32 *flags, __le32 *mask) { struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; u8 mimo_ps_mode; *mask = STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK | STA_FLG_HT40_EN_MSK | STA_FLG_MAX_AGG_SIZE_MSK | STA_FLG_AGG_MPDU_DENSITY_MSK; *flags = 0; if (!sta || !sta_ht_inf->ht_supported) return; mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", sta->addr, (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? "static" : (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? "dynamic" : "disabled"); switch (mimo_ps_mode) { case WLAN_HT_CAP_SM_PS_STATIC: *flags |= STA_FLG_MIMO_DIS_MSK; break; case WLAN_HT_CAP_SM_PS_DYNAMIC: *flags |= STA_FLG_RTS_MIMO_PROT_MSK; break; case WLAN_HT_CAP_SM_PS_DISABLED: break; default: IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode); break; } *flags |= cpu_to_le32( (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); *flags |= cpu_to_le32( (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) *flags |= STA_FLG_HT40_EN_MSK; } int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_sta *sta) { u8 sta_id = iwl_sta_id(sta); __le32 flags, mask; struct iwl_addsta_cmd cmd; if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) return -EINVAL; iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); spin_lock_bh(&priv->sta_lock); priv->stations[sta_id].sta.station_flags &= ~mask; priv->stations[sta_id].sta.station_flags |= flags; spin_unlock_bh(&priv->sta_lock); memset(&cmd, 0, sizeof(cmd)); cmd.mode = STA_CONTROL_MODIFY_MSK; cmd.station_flags_msk = mask; cmd.station_flags = flags; cmd.sta.sta_id = sta_id; return iwl_send_add_sta(priv, &cmd, CMD_SYNC); } static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, struct ieee80211_sta *sta, struct iwl_rxon_context *ctx) { __le32 flags, mask; iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); lockdep_assert_held(&priv->sta_lock); priv->stations[index].sta.station_flags &= ~mask; priv->stations[index].sta.station_flags |= flags; } /** * iwl_prep_station - Prepare station information for addition * * should be called with sta_lock held */ u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, bool is_ap, struct ieee80211_sta *sta) { struct iwl_station_entry *station; int i; u8 sta_id = IWL_INVALID_STATION; if (is_ap) sta_id = ctx->ap_sta_id; else if (is_broadcast_ether_addr(addr)) sta_id = ctx->bcast_sta_id; else for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { if (ether_addr_equal(priv->stations[i].sta.sta.addr, addr)) { sta_id = i; break; } if (!priv->stations[i].used && sta_id == IWL_INVALID_STATION) sta_id = i; } /* * These two conditions have the same outcome, but keep them * separate */ if (unlikely(sta_id == IWL_INVALID_STATION)) return sta_id; /* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another. */ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { IWL_DEBUG_INFO(priv, "STA %d already in process of being " "added.\n", sta_id); return sta_id; } if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) { IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " "adding again.\n", sta_id, addr); return sta_id; } station = &priv->stations[sta_id]; station->used = IWL_STA_DRIVER_ACTIVE; IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", sta_id, addr); priv->num_stations++; /* Set up the REPLY_ADD_STA command to send to device */ memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); memcpy(station->sta.sta.addr, addr, ETH_ALEN); station->sta.mode = 0; station->sta.sta.sta_id = sta_id; station->sta.station_flags = ctx->station_flags; station->ctxid = ctx->ctxid; if (sta) { struct iwl_station_priv *sta_priv; sta_priv = (void *)sta->drv_priv; sta_priv->ctx = ctx; } /* * OK to call unconditionally, since local stations (IBSS BSSID * STA and broadcast STA) pass in a NULL sta, and mac80211 * doesn't allow HT IBSS. */ iwl_set_ht_add_station(priv, sta_id, sta, ctx); return sta_id; } #define STA_WAIT_TIMEOUT (HZ/2) /** * iwl_add_station_common - */ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, bool is_ap, struct ieee80211_sta *sta, u8 *sta_id_r) { int ret = 0; u8 sta_id; struct iwl_addsta_cmd sta_cmd; *sta_id_r = 0; spin_lock_bh(&priv->sta_lock); sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Unable to prepare station %pM for addition\n", addr); spin_unlock_bh(&priv->sta_lock); return -EINVAL; } /* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another. */ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { IWL_DEBUG_INFO(priv, "STA %d already in process of being " "added.\n", sta_id); spin_unlock_bh(&priv->sta_lock); return -EEXIST; } if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " "adding again.\n", sta_id, addr); spin_unlock_bh(&priv->sta_lock); return -EEXIST; } priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); /* Add station to device's station table */ ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); if (ret) { spin_lock_bh(&priv->sta_lock); IWL_ERR(priv, "Adding station %pM failed.\n", priv->stations[sta_id].sta.sta.addr); priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; spin_unlock_bh(&priv->sta_lock); } *sta_id_r = sta_id; return ret; } /** * iwl_sta_ucode_deactivate - deactivate ucode status for a station */ static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) { lockdep_assert_held(&priv->sta_lock); /* Ucode must be active and driver must be non active */ if ((priv->stations[sta_id].used & (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != IWL_STA_UCODE_ACTIVE) IWL_ERR(priv, "removed non active STA %u\n", sta_id); priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); } static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, int sta_id, bool temporary) { struct iwl_rx_packet *pkt; int ret; struct iwl_rem_sta_cmd rm_sta_cmd; struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = { sizeof(struct iwl_rem_sta_cmd), }, .flags = CMD_SYNC, .data = { &rm_sta_cmd, }, }; memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); rm_sta_cmd.num_sta = 1; memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); cmd.flags |= CMD_WANT_SKB; ret = iwl_dvm_send_cmd(priv, &cmd); if (ret) return ret; pkt = cmd.resp_pkt; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", pkt->hdr.flags); ret = -EIO; } if (!ret) { struct iwl_rem_sta_resp *rem_sta_resp = (void *)pkt->data; switch (rem_sta_resp->status) { case REM_STA_SUCCESS_MSK: if (!temporary) { spin_lock_bh(&priv->sta_lock); iwl_sta_ucode_deactivate(priv, sta_id); spin_unlock_bh(&priv->sta_lock); } IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); break; default: ret = -EIO; IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); break; } } iwl_free_resp(&cmd); return ret; } /** * iwl_remove_station - Remove driver's knowledge of station. */ int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, const u8 *addr) { u8 tid; if (!iwl_is_ready(priv)) { IWL_DEBUG_INFO(priv, "Unable to remove station %pM, device not ready.\n", addr); /* * It is typical for stations to be removed when we are * going down. Return success since device will be down * soon anyway */ return 0; } IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", sta_id, addr); if (WARN_ON(sta_id == IWL_INVALID_STATION)) return -EINVAL; spin_lock_bh(&priv->sta_lock); if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", addr); goto out_err; } if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", addr); goto out_err; } if (priv->stations[sta_id].used & IWL_STA_LOCAL) { kfree(priv->stations[sta_id].lq); priv->stations[sta_id].lq = NULL; } for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) memset(&priv->tid_data[sta_id][tid], 0, sizeof(priv->tid_data[sta_id][tid])); priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; priv->num_stations--; if (WARN_ON(priv->num_stations < 0)) priv->num_stations = 0; spin_unlock_bh(&priv->sta_lock); return iwl_send_remove_station(priv, addr, sta_id, false); out_err: spin_unlock_bh(&priv->sta_lock); return -EINVAL; } void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, const u8 *addr) { u8 tid; if (!iwl_is_ready(priv)) { IWL_DEBUG_INFO(priv, "Unable to remove station %pM, device not ready.\n", addr); return; } IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id); if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) return; spin_lock_bh(&priv->sta_lock); WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)); for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) memset(&priv->tid_data[sta_id][tid], 0, sizeof(priv->tid_data[sta_id][tid])); priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; priv->num_stations--; if (WARN_ON_ONCE(priv->num_stations < 0)) priv->num_stations = 0; spin_unlock_bh(&priv->sta_lock); } static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, u8 sta_id, struct iwl_link_quality_cmd *link_cmd) { int i, r; u32 rate_flags = 0; __le32 rate_n_flags; lockdep_assert_held(&priv->mutex); memset(link_cmd, 0, sizeof(*link_cmd)); /* Set up the rate scaling to start at selected rate, fall back * all the way down to 1M in IEEE order, and then spin on 1M */ if (priv->band == IEEE80211_BAND_5GHZ) r = IWL_RATE_6M_INDEX; else if (ctx && ctx->vif && ctx->vif->p2p) r = IWL_RATE_6M_INDEX; else r = IWL_RATE_1M_INDEX; if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) rate_flags |= RATE_MCS_CCK_MSK; rate_flags |= first_antenna(priv->eeprom_data->valid_tx_ant) << RATE_MCS_ANT_POS; rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) link_cmd->rs_table[i].rate_n_flags = rate_n_flags; link_cmd->general_params.single_stream_ant_msk = first_antenna(priv->eeprom_data->valid_tx_ant); link_cmd->general_params.dual_stream_ant_msk = priv->eeprom_data->valid_tx_ant & ~first_antenna(priv->eeprom_data->valid_tx_ant); if (!link_cmd->general_params.dual_stream_ant_msk) { link_cmd->general_params.dual_stream_ant_msk = ANT_AB; } else if (num_of_ant(priv->eeprom_data->valid_tx_ant) == 2) { link_cmd->general_params.dual_stream_ant_msk = priv->eeprom_data->valid_tx_ant; } link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; link_cmd->agg_params.agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); link_cmd->sta_id = sta_id; } /** * iwl_clear_ucode_stations - clear ucode station table bits * * This function clears all the bits in the driver indicating * which stations are active in the ucode. Call when something * other than explicit station management would cause this in * the ucode, e.g. unassociated RXON. */ void iwl_clear_ucode_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int i; bool cleared = false; IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); spin_lock_bh(&priv->sta_lock); for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if (ctx && ctx->ctxid != priv->stations[i].ctxid) continue; if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { IWL_DEBUG_INFO(priv, "Clearing ucode active for station %d\n", i); priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; cleared = true; } } spin_unlock_bh(&priv->sta_lock); if (!cleared) IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n"); } /** * iwl_restore_stations() - Restore driver known stations to device * * All stations considered active by driver, but not present in ucode, is * restored. * * Function sleeps. */ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { struct iwl_addsta_cmd sta_cmd; struct iwl_link_quality_cmd lq; int i; bool found = false; int ret; bool send_lq; if (!iwl_is_ready(priv)) { IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n"); return; } IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); spin_lock_bh(&priv->sta_lock); for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if (ctx->ctxid != priv->stations[i].ctxid) continue; if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", priv->stations[i].sta.sta.addr); priv->stations[i].sta.mode = 0; priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; found = true; } } for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { memcpy(&sta_cmd, &priv->stations[i].sta, sizeof(struct iwl_addsta_cmd)); send_lq = false; if (priv->stations[i].lq) { if (priv->wowlan) iwl_sta_fill_lq(priv, ctx, i, &lq); else memcpy(&lq, priv->stations[i].lq, sizeof(struct iwl_link_quality_cmd)); send_lq = true; } spin_unlock_bh(&priv->sta_lock); ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); if (ret) { spin_lock_bh(&priv->sta_lock); IWL_ERR(priv, "Adding station %pM failed.\n", priv->stations[i].sta.sta.addr); priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE; priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; continue; } /* * Rate scaling has already been initialized, send * current LQ command */ if (send_lq) iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true); spin_lock_bh(&priv->sta_lock); priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; } } spin_unlock_bh(&priv->sta_lock); if (!found) IWL_DEBUG_INFO(priv, "Restoring all known stations .... " "no stations to be restored.\n"); else IWL_DEBUG_INFO(priv, "Restoring all known stations .... " "complete.\n"); } int iwl_get_free_ucode_key_offset(struct iwl_priv *priv) { int i; for (i = 0; i < priv->sta_key_max_num; i++) if (!test_and_set_bit(i, &priv->ucode_key_table)) return i; return WEP_INVALID_OFFSET; } void iwl_dealloc_bcast_stations(struct iwl_priv *priv) { int i; spin_lock_bh(&priv->sta_lock); for (i = 0; i < IWLAGN_STATION_COUNT; i++) { if (!(priv->stations[i].used & IWL_STA_BCAST)) continue; priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; priv->num_stations--; if (WARN_ON(priv->num_stations < 0)) priv->num_stations = 0; kfree(priv->stations[i].lq); priv->stations[i].lq = NULL; } spin_unlock_bh(&priv->sta_lock); } #ifdef CONFIG_IWLWIFI_DEBUG static void iwl_dump_lq_cmd(struct iwl_priv *priv, struct iwl_link_quality_cmd *lq) { int i; IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk, lq->general_params.dual_stream_ant_msk); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", i, lq->rs_table[i].rate_n_flags); } #else static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, struct iwl_link_quality_cmd *lq) { } #endif /** * is_lq_table_valid() - Test one aspect of LQ cmd for validity * * It sometimes happens when a HT rate has been in use and we * loose connectivity with AP then mac80211 will first tell us that the * current channel is not HT anymore before removing the station. In such a * scenario the RXON flags will be updated to indicate we are not * communicating HT anymore, but the LQ command may still contain HT rates. * Test for this to prevent driver from sending LQ command between the time * RXON flags are updated and when LQ command is updated. */ static bool is_lq_table_valid(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_link_quality_cmd *lq) { int i; if (ctx->ht.enabled) return true; IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", ctx->active.channel); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) { IWL_DEBUG_INFO(priv, "index %d of LQ expects HT channel\n", i); return false; } } return true; } /** * iwl_send_lq_cmd() - Send link quality command * @init: This command is sent as part of station initialization right * after station has been added. * * The link quality command is sent as the last step of station creation. * This is the special case in which init is set and we call a callback in * this case to clear the state indicating that station creation is in * progress. */ int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_link_quality_cmd *lq, u8 flags, bool init) { int ret = 0; struct iwl_host_cmd cmd = { .id = REPLY_TX_LINK_QUALITY_CMD, .len = { sizeof(struct iwl_link_quality_cmd), }, .flags = flags, .data = { lq, }, }; if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) return -EINVAL; spin_lock_bh(&priv->sta_lock); if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { spin_unlock_bh(&priv->sta_lock); return -EINVAL; } spin_unlock_bh(&priv->sta_lock); iwl_dump_lq_cmd(priv, lq); if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) return -EINVAL; if (is_lq_table_valid(priv, ctx, lq)) ret = iwl_dvm_send_cmd(priv, &cmd); else ret = -EINVAL; if (cmd.flags & CMD_ASYNC) return ret; if (init) { IWL_DEBUG_INFO(priv, "init LQ command complete, " "clearing sta addition status for sta %d\n", lq->sta_id); spin_lock_bh(&priv->sta_lock); priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; spin_unlock_bh(&priv->sta_lock); } return ret; } static struct iwl_link_quality_cmd * iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, u8 sta_id) { struct iwl_link_quality_cmd *link_cmd; link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); if (!link_cmd) { IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); return NULL; } iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd); return link_cmd; } /* * iwlagn_add_bssid_station - Add the special IBSS BSSID station * * Function sleeps. */ int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, u8 *sta_id_r) { int ret; u8 sta_id; struct iwl_link_quality_cmd *link_cmd; if (sta_id_r) *sta_id_r = IWL_INVALID_STATION; ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); if (ret) { IWL_ERR(priv, "Unable to add station %pM\n", addr); return ret; } if (sta_id_r) *sta_id_r = sta_id; spin_lock_bh(&priv->sta_lock); priv->stations[sta_id].used |= IWL_STA_LOCAL; spin_unlock_bh(&priv->sta_lock); /* Set up default rate scaling table in device's station table */ link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); if (!link_cmd) { IWL_ERR(priv, "Unable to initialize rate scaling for station %pM.\n", addr); return -ENOMEM; } ret = iwl_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true); if (ret) IWL_ERR(priv, "Link quality command failed (%d)\n", ret); spin_lock_bh(&priv->sta_lock); priv->stations[sta_id].lq = link_cmd; spin_unlock_bh(&priv->sta_lock); return 0; } /* * static WEP keys * * For each context, the device has a table of 4 static WEP keys * (one for each key index) that is updated with the following * commands. */ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, bool send_if_empty) { int i, not_empty = 0; u8 buff[sizeof(struct iwl_wep_cmd) + sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; size_t cmd_size = sizeof(struct iwl_wep_cmd); struct iwl_host_cmd cmd = { .id = ctx->wep_key_cmd, .data = { wep_cmd, }, .flags = CMD_SYNC, }; might_sleep(); memset(wep_cmd, 0, cmd_size + (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); for (i = 0; i < WEP_KEYS_MAX ; i++) { wep_cmd->key[i].key_index = i; if (ctx->wep_keys[i].key_size) { wep_cmd->key[i].key_offset = i; not_empty = 1; } else { wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; } wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, ctx->wep_keys[i].key_size); } wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; wep_cmd->num_keys = WEP_KEYS_MAX; cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; cmd.len[0] = cmd_size; if (not_empty || send_if_empty) return iwl_dvm_send_cmd(priv, &cmd); else return 0; } int iwl_restore_default_wep_keys(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { lockdep_assert_held(&priv->mutex); return iwl_send_static_wepkey_cmd(priv, ctx, false); } int iwl_remove_default_wep_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *keyconf) { int ret; lockdep_assert_held(&priv->mutex); IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", keyconf->keyidx); memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); if (iwl_is_rfkill(priv)) { IWL_DEBUG_WEP(priv, "Not sending REPLY_WEPKEY command due to RFKILL.\n"); /* but keys in device are clear anyway so return success */ return 0; } ret = iwl_send_static_wepkey_cmd(priv, ctx, 1); IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", keyconf->keyidx, ret); return ret; } int iwl_set_default_wep_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *keyconf) { int ret; lockdep_assert_held(&priv->mutex); if (keyconf->keylen != WEP_KEY_LEN_128 && keyconf->keylen != WEP_KEY_LEN_64) { IWL_DEBUG_WEP(priv, "Bad WEP key length %d\n", keyconf->keylen); return -EINVAL; } keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT; ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, keyconf->keylen); ret = iwl_send_static_wepkey_cmd(priv, ctx, false); IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", keyconf->keylen, keyconf->keyidx, ret); return ret; } /* * dynamic (per-station) keys * * The dynamic keys are a little more complicated. The device has * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. * These are linked to stations by a table that contains an index * into the key table for each station/key index/{mcast,unicast}, * i.e. it's basically an array of pointers like this: * key_offset_t key_mapping[NUM_STATIONS][4][2]; * (it really works differently, but you can think of it as such) * * The key uploading and linking happens in the same command, the * add station command with STA_MODIFY_KEY_MASK. */ static u8 iwlagn_key_sta_id(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; if (sta) return iwl_sta_id(sta); /* * The device expects GTKs for station interfaces to be * installed as GTKs for the AP station. If we have no * station ID, then use the ap_sta_id in that case. */ if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) return vif_priv->ctx->ap_sta_id; return IWL_INVALID_STATION; } static int iwlagn_send_sta_key(struct iwl_priv *priv, struct ieee80211_key_conf *keyconf, u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags) { __le16 key_flags; struct iwl_addsta_cmd sta_cmd; int i; spin_lock_bh(&priv->sta_lock); memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); spin_unlock_bh(&priv->sta_lock); key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); key_flags |= STA_KEY_FLG_MAP_KEY_MSK; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_CCMP: key_flags |= STA_KEY_FLG_CCMP; memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); break; case WLAN_CIPHER_SUITE_TKIP: key_flags |= STA_KEY_FLG_TKIP; sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; for (i = 0; i < 5; i++) sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); break; case WLAN_CIPHER_SUITE_WEP104: key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; /* fall through */ case WLAN_CIPHER_SUITE_WEP40: key_flags |= STA_KEY_FLG_WEP; memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); break; default: WARN_ON(1); return -EINVAL; } if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) key_flags |= STA_KEY_MULTICAST_MSK; /* key pointer (offset) */ sta_cmd.key.key_offset = keyconf->hw_key_idx; sta_cmd.key.key_flags = key_flags; sta_cmd.mode = STA_CONTROL_MODIFY_MSK; sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); } void iwl_update_tkip_key(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) { u8 sta_id = iwlagn_key_sta_id(priv, vif, sta); if (sta_id == IWL_INVALID_STATION) return; if (iwl_scan_cancel(priv)) { /* cancel scan failed, just live w/ bad key and rely briefly on SW decryption */ return; } iwlagn_send_sta_key(priv, keyconf, sta_id, iv32, phase1key, CMD_ASYNC); } int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta) { struct iwl_addsta_cmd sta_cmd; u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); __le16 key_flags; /* if station isn't there, neither is the key */ if (sta_id == IWL_INVALID_STATION) return -ENOENT; spin_lock_bh(&priv->sta_lock); memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) sta_id = IWL_INVALID_STATION; spin_unlock_bh(&priv->sta_lock); if (sta_id == IWL_INVALID_STATION) return 0; lockdep_assert_held(&priv->mutex); ctx->key_mapping_keys--; IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) IWL_ERR(priv, "offset %d not used in uCode key table.\n", keyconf->hw_key_idx); key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) key_flags |= STA_KEY_MULTICAST_MSK; sta_cmd.key.key_flags = key_flags; sta_cmd.key.key_offset = keyconf->hw_key_idx; sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; sta_cmd.mode = STA_CONTROL_MODIFY_MSK; return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); } int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta) { struct ieee80211_key_seq seq; u16 p1k[5]; int ret; u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); const u8 *addr; if (sta_id == IWL_INVALID_STATION) return -EINVAL; lockdep_assert_held(&priv->mutex); keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv); if (keyconf->hw_key_idx == WEP_INVALID_OFFSET) return -ENOSPC; ctx->key_mapping_keys++; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_TKIP: if (sta) addr = sta->addr; else /* station mode case only */ addr = ctx->active.bssid_addr; /* pre-fill phase 1 key into device cache */ ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); ret = iwlagn_send_sta_key(priv, keyconf, sta_id, seq.tkip.iv32, p1k, CMD_SYNC); break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ret = iwlagn_send_sta_key(priv, keyconf, sta_id, 0, NULL, CMD_SYNC); break; default: IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); ret = -EINVAL; } if (ret) { ctx->key_mapping_keys--; clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table); } IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta ? sta->addr : NULL, ret); return ret; } /** * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. * * This adds the broadcast station into the driver's station table * and marks it driver active, so that it will be restored to the * device at the next best time. */ int iwlagn_alloc_bcast_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { struct iwl_link_quality_cmd *link_cmd; u8 sta_id; spin_lock_bh(&priv->sta_lock); sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Unable to prepare broadcast station\n"); spin_unlock_bh(&priv->sta_lock); return -EINVAL; } priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; priv->stations[sta_id].used |= IWL_STA_BCAST; spin_unlock_bh(&priv->sta_lock); link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); if (!link_cmd) { IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); return -ENOMEM; } spin_lock_bh(&priv->sta_lock); priv->stations[sta_id].lq = link_cmd; spin_unlock_bh(&priv->sta_lock); return 0; } /** * iwl_update_bcast_station - update broadcast station's LQ command * * Only used by iwlagn. Placed here to have all bcast station management * code together. */ int iwl_update_bcast_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { struct iwl_link_quality_cmd *link_cmd; u8 sta_id = ctx->bcast_sta_id; link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); if (!link_cmd) { IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); return -ENOMEM; } spin_lock_bh(&priv->sta_lock); if (priv->stations[sta_id].lq) kfree(priv->stations[sta_id].lq); else IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n"); priv->stations[sta_id].lq = link_cmd; spin_unlock_bh(&priv->sta_lock); return 0; } int iwl_update_bcast_stations(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; int ret = 0; for_each_context(priv, ctx) { ret = iwl_update_bcast_station(priv, ctx); if (ret) break; } return ret; } /** * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table */ int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) { struct iwl_addsta_cmd sta_cmd; lockdep_assert_held(&priv->mutex); /* Remove "disable" flag, to enable Tx for this TID */ spin_lock_bh(&priv->sta_lock); priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); } int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, int tid, u16 ssn) { int sta_id; struct iwl_addsta_cmd sta_cmd; lockdep_assert_held(&priv->mutex); sta_id = iwl_sta_id(sta); if (sta_id == IWL_INVALID_STATION) return -ENXIO; spin_lock_bh(&priv->sta_lock); priv->stations[sta_id].sta.station_flags_msk = 0; priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); } int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, int tid) { int sta_id; struct iwl_addsta_cmd sta_cmd; lockdep_assert_held(&priv->mutex); sta_id = iwl_sta_id(sta); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); return -ENXIO; } spin_lock_bh(&priv->sta_lock); priv->stations[sta_id].sta.station_flags_msk = 0; priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); } void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) { struct iwl_addsta_cmd cmd = { .mode = STA_CONTROL_MODIFY_MSK, .station_flags = STA_FLG_PWR_SAVE_MSK, .station_flags_msk = STA_FLG_PWR_SAVE_MSK, .sta.sta_id = sta_id, .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK, .sleep_tx_count = cpu_to_le16(cnt), }; iwl_send_add_sta(priv, &cmd, CMD_ASYNC); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/scan.c0000644000175000017500000010062712026211315024017 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include #include #include #include #include "dev.h" #include "agn.h" /* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after * sending probe req. This should be set long enough to hear probe responses * from more than one AP. */ #define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ #define IWL_ACTIVE_DWELL_TIME_52 (20) #define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) #define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) /* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. * Must be set longer than active dwell time. * For the most reliable scan, set > AP beacon interval (typically 100msec). */ #define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ #define IWL_PASSIVE_DWELL_TIME_52 (10) #define IWL_PASSIVE_DWELL_BASE (100) #define IWL_CHANNEL_TUNE_TIME 5 #define MAX_SCAN_CHANNEL 50 /* For reset radio, need minimal dwell time only */ #define IWL_RADIO_RESET_DWELL_TIME 5 static int iwl_send_scan_abort(struct iwl_priv *priv) { int ret; struct iwl_host_cmd cmd = { .id = REPLY_SCAN_ABORT_CMD, .flags = CMD_SYNC | CMD_WANT_SKB, }; __le32 *status; /* Exit instantly with error when device is not ready * to receive scan abort command or it does not perform * hardware scan currently */ if (!test_bit(STATUS_READY, &priv->status) || !test_bit(STATUS_SCAN_HW, &priv->status) || test_bit(STATUS_FW_ERROR, &priv->status)) return -EIO; ret = iwl_dvm_send_cmd(priv, &cmd); if (ret) return ret; status = (void *)cmd.resp_pkt->data; if (*status != CAN_ABORT_STATUS) { /* The scan abort will return 1 for success or * 2 for "failure". A failure condition can be * due to simply not being in an active scan which * can occur if we send the scan abort before we * the microcode has notified us that a scan is * completed. */ IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", le32_to_cpu(*status)); ret = -EIO; } iwl_free_resp(&cmd); return ret; } static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) { /* check if scan was requested from mac80211 */ if (priv->scan_request) { IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); ieee80211_scan_completed(priv->hw, aborted); } if (priv->scan_type == IWL_SCAN_ROC) iwl_scan_roc_expired(priv); priv->scan_type = IWL_SCAN_NORMAL; priv->scan_vif = NULL; priv->scan_request = NULL; } static void iwl_process_scan_complete(struct iwl_priv *priv) { bool aborted; lockdep_assert_held(&priv->mutex); if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status)) return; IWL_DEBUG_SCAN(priv, "Completed scan.\n"); cancel_delayed_work(&priv->scan_check); aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); if (aborted) IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); goto out_settings; } if (priv->scan_type == IWL_SCAN_ROC) iwl_scan_roc_expired(priv); if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { int err; /* Check if mac80211 requested scan during our internal scan */ if (priv->scan_request == NULL) goto out_complete; /* If so request a new scan */ err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, priv->scan_request->channels[0]->band); if (err) { IWL_DEBUG_SCAN(priv, "failed to initiate pending scan: %d\n", err); aborted = true; goto out_complete; } return; } out_complete: iwl_complete_scan(priv, aborted); out_settings: /* Can we still talk to firmware ? */ if (!iwl_is_ready_rf(priv)) return; iwlagn_post_scan(priv); } void iwl_force_scan_end(struct iwl_priv *priv) { lockdep_assert_held(&priv->mutex); if (!test_bit(STATUS_SCANNING, &priv->status)) { IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); return; } IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); clear_bit(STATUS_SCANNING, &priv->status); clear_bit(STATUS_SCAN_HW, &priv->status); clear_bit(STATUS_SCAN_ABORTING, &priv->status); clear_bit(STATUS_SCAN_COMPLETE, &priv->status); iwl_complete_scan(priv, true); } static void iwl_do_scan_abort(struct iwl_priv *priv) { int ret; lockdep_assert_held(&priv->mutex); if (!test_bit(STATUS_SCANNING, &priv->status)) { IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); return; } if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); return; } ret = iwl_send_scan_abort(priv); if (ret) { IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); iwl_force_scan_end(priv); } else IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n"); } /** * iwl_scan_cancel - Cancel any currently executing HW scan */ int iwl_scan_cancel(struct iwl_priv *priv) { IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); queue_work(priv->workqueue, &priv->abort_scan); return 0; } /** * iwl_scan_cancel_timeout - Cancel any currently executing HW scan * @ms: amount of time to wait (in milliseconds) for scan to abort * */ void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) { unsigned long timeout = jiffies + msecs_to_jiffies(ms); lockdep_assert_held(&priv->mutex); IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); iwl_do_scan_abort(priv); while (time_before_eq(jiffies, timeout)) { if (!test_bit(STATUS_SCAN_HW, &priv->status)) goto finished; msleep(20); } return; finished: /* * Now STATUS_SCAN_HW is clear. This means that the * device finished, but the background work is going * to execute at best as soon as we release the mutex. * Since we need to be able to issue a new scan right * after this function returns, run the complete here. * The STATUS_SCAN_COMPLETE bit will then be cleared * and prevent the background work from "completing" * a possible new scan. */ iwl_process_scan_complete(priv); } /* Service response to REPLY_SCAN_CMD (0x80) */ static int iwl_rx_reply_scan(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { #ifdef CONFIG_IWLWIFI_DEBUG struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scanreq_notification *notif = (void *)pkt->data; IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); #endif return 0; } /* Service SCAN_START_NOTIFICATION (0x82) */ static int iwl_rx_scan_start_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scanstart_notification *notif = (void *)pkt->data; priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); IWL_DEBUG_SCAN(priv, "Scan start: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel, notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); if (priv->scan_type == IWL_SCAN_ROC && !priv->hw_roc_start_notified) { ieee80211_ready_on_channel(priv->hw); priv->hw_roc_start_notified = true; } return 0; } /* Service SCAN_RESULTS_NOTIFICATION (0x83) */ static int iwl_rx_scan_results_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { #ifdef CONFIG_IWLWIFI_DEBUG struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scanresults_notification *notif = (void *)pkt->data; IWL_DEBUG_SCAN(priv, "Scan ch.res: " "%d [802.11%s] " "probe status: %u:%u " "(TSF: 0x%08X:%08X) - %d " "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", notif->probe_status, notif->num_probe_not_sent, le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), le32_to_cpu(notif->statistics[0]), le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); #endif return 0; } /* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ static int iwl_rx_scan_complete_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", scan_notif->scanned_channels, scan_notif->tsf_low, scan_notif->tsf_high, scan_notif->status); IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", jiffies_to_msecs(jiffies - priv->scan_start)); /* * When aborting, we run the scan completed background work inline * and the background work must then do nothing. The SCAN_COMPLETE * bit helps implement that logic and thus needs to be set before * queueing the work. Also, since the scan abort waits for SCAN_HW * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW * to avoid a race there. */ set_bit(STATUS_SCAN_COMPLETE, &priv->status); clear_bit(STATUS_SCAN_HW, &priv->status); queue_work(priv->workqueue, &priv->scan_completed); if (priv->iw_mode != NL80211_IFTYPE_ADHOC && iwl_advanced_bt_coexist(priv) && priv->bt_status != scan_notif->bt_status) { if (scan_notif->bt_status) { /* BT on */ if (!priv->bt_ch_announce) priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; /* * otherwise, no traffic load information provided * no changes made */ } else { /* BT off */ priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE; } priv->bt_status = scan_notif->bt_status; queue_work(priv->workqueue, &priv->bt_traffic_change_work); } return 0; } void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) { /* scan handlers */ priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan; priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif; priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = iwl_rx_scan_results_notif; priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = iwl_rx_scan_complete_notif; } static u16 iwl_get_active_dwell_time(struct iwl_priv *priv, enum ieee80211_band band, u8 n_probes) { if (band == IEEE80211_BAND_5GHZ) return IWL_ACTIVE_DWELL_TIME_52 + IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); else return IWL_ACTIVE_DWELL_TIME_24 + IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); } static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) { struct iwl_rxon_context *ctx; int limits[NUM_IWL_RXON_CTX] = {}; int n_active = 0; u16 limit; BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); /* * If we're associated, we clamp the dwell time 98% * of the beacon interval (minus 2 * channel tune time) * If both contexts are active, we have to restrict to * 1/2 of the minimum of them, because they might be in * lock-step with the time inbetween only half of what * time we'd have in each of them. */ for_each_context(priv, ctx) { switch (ctx->staging.dev_type) { case RXON_DEV_TYPE_P2P: /* no timing constraints */ continue; case RXON_DEV_TYPE_ESS: default: /* timing constraints if associated */ if (!iwl_is_associated_ctx(ctx)) continue; break; case RXON_DEV_TYPE_CP: case RXON_DEV_TYPE_2STA: /* * These seem to always have timers for TBTT * active in uCode even when not associated yet. */ break; } limits[n_active++] = ctx->beacon_int ?: IWL_PASSIVE_DWELL_BASE; } switch (n_active) { case 0: return dwell_time; case 2: limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; limit /= 2; dwell_time = min(limit, dwell_time); /* fall through to limit further */ case 1: limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; limit /= n_active; return min(limit, dwell_time); default: WARN_ON_ONCE(1); return dwell_time; } } static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, enum ieee80211_band band) { u16 passive = (band == IEEE80211_BAND_2GHZ) ? IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; return iwl_limit_dwell(priv, passive); } /* Return valid, unused, channel for a passive scan to reset the RF */ static u8 iwl_get_single_channel_number(struct iwl_priv *priv, enum ieee80211_band band) { struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band]; struct iwl_rxon_context *ctx; int i; for (i = 0; i < sband->n_channels; i++) { bool busy = false; for_each_context(priv, ctx) { busy = sband->channels[i].hw_value == le16_to_cpu(ctx->staging.channel); if (busy) break; } if (busy) continue; if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED)) return sband->channels[i].hw_value; } return 0; } static int iwl_get_channel_for_reset_scan(struct iwl_priv *priv, struct ieee80211_vif *vif, enum ieee80211_band band, struct iwl_scan_channel *scan_ch) { const struct ieee80211_supported_band *sband; u16 channel; sband = iwl_get_hw_mode(priv, band); if (!sband) { IWL_ERR(priv, "invalid band\n"); return 0; } channel = iwl_get_single_channel_number(priv, band); if (channel) { scan_ch->channel = cpu_to_le16(channel); scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; scan_ch->active_dwell = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); scan_ch->passive_dwell = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); /* Set txpower levels to defaults */ scan_ch->dsp_atten = 110; if (band == IEEE80211_BAND_5GHZ) scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; else scan_ch->tx_gain = ((1 << 5) | (5 << 3)); return 1; } IWL_ERR(priv, "no valid channel found\n"); return 0; } static int iwl_get_channels_for_scan(struct iwl_priv *priv, struct ieee80211_vif *vif, enum ieee80211_band band, u8 is_active, u8 n_probes, struct iwl_scan_channel *scan_ch) { struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; u16 passive_dwell = 0; u16 active_dwell = 0; int added, i; u16 channel; sband = iwl_get_hw_mode(priv, band); if (!sband) return 0; active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); passive_dwell = iwl_get_passive_dwell_time(priv, band); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { chan = priv->scan_request->channels[i]; if (chan->band != band) continue; channel = chan->hw_value; scan_ch->channel = cpu_to_le16(channel); if (!is_active || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; if (n_probes) scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); scan_ch->active_dwell = cpu_to_le16(active_dwell); scan_ch->passive_dwell = cpu_to_le16(passive_dwell); /* Set txpower levels to defaults */ scan_ch->dsp_atten = 110; /* NOTE: if we were doing 6Mb OFDM for scans we'd use * power level: * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; */ if (band == IEEE80211_BAND_5GHZ) scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; else scan_ch->tx_gain = ((1 << 5) | (5 << 3)); IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", channel, le32_to_cpu(scan_ch->type), (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE", (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell : passive_dwell); scan_ch++; added++; } IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); return added; } /** * iwl_fill_probe_req - fill in all required fields and IE for probe request */ static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, const u8 *ies, int ie_len, const u8 *ssid, u8 ssid_len, int left) { int len = 0; u8 *pos = NULL; /* Make sure there is enough space for the probe request, * two mandatory IEs and the data */ left -= 24; if (left < 0) return 0; frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); memcpy(frame->da, iwl_bcast_addr, ETH_ALEN); memcpy(frame->sa, ta, ETH_ALEN); memcpy(frame->bssid, iwl_bcast_addr, ETH_ALEN); frame->seq_ctrl = 0; len += 24; /* ...next IE... */ pos = &frame->u.probe_req.variable[0]; /* fill in our SSID IE */ left -= ssid_len + 2; if (left < 0) return 0; *pos++ = WLAN_EID_SSID; *pos++ = ssid_len; if (ssid && ssid_len) { memcpy(pos, ssid, ssid_len); pos += ssid_len; } len += ssid_len + 2; if (WARN_ON(left < ie_len)) return len; if (ies && ie_len) { memcpy(pos, ies, ie_len); len += ie_len; } return (u16)len; } static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) { struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = { sizeof(struct iwl_scan_cmd), }, .flags = CMD_SYNC, }; struct iwl_scan_cmd *scan; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; u32 rate_flags = 0; u16 cmd_len = 0; u16 rx_chain = 0; enum ieee80211_band band; u8 n_probes = 0; u8 rx_ant = priv->eeprom_data->valid_rx_ant; u8 rate; bool is_active = false; int chan_mod; u8 active_chains; u8 scan_tx_antennas = priv->eeprom_data->valid_tx_ant; int ret; int scan_cmd_size = sizeof(struct iwl_scan_cmd) + MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) + priv->fw->ucode_capa.max_probe_length; const u8 *ssid = NULL; u8 ssid_len = 0; if (WARN_ON_ONCE(priv->scan_request && priv->scan_request->n_channels > MAX_SCAN_CHANNEL)) return -EINVAL; lockdep_assert_held(&priv->mutex); if (vif) ctx = iwl_rxon_ctx_from_vif(vif); if (!priv->scan_cmd) { priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL); if (!priv->scan_cmd) { IWL_DEBUG_SCAN(priv, "fail to allocate memory for scan\n"); return -ENOMEM; } } scan = priv->scan_cmd; memset(scan, 0, scan_cmd_size); scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; scan->quiet_time = IWL_ACTIVE_QUIET_TIME; if (priv->scan_type != IWL_SCAN_ROC && iwl_is_any_associated(priv)) { u16 interval = 0; u32 extra; u32 suspend_time = 100; u32 scan_suspend_time = 100; IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); switch (priv->scan_type) { case IWL_SCAN_ROC: WARN_ON(1); break; case IWL_SCAN_RADIO_RESET: interval = 0; break; case IWL_SCAN_NORMAL: interval = vif->bss_conf.beacon_int; break; } scan->suspend_time = 0; scan->max_out_time = cpu_to_le32(200 * 1024); if (!interval) interval = suspend_time; extra = (suspend_time / interval) << 22; scan_suspend_time = (extra | ((suspend_time % interval) * 1024)); scan->suspend_time = cpu_to_le32(scan_suspend_time); IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", scan_suspend_time, interval); } else if (priv->scan_type == IWL_SCAN_ROC) { scan->suspend_time = 0; scan->max_out_time = 0; scan->quiet_time = 0; scan->quiet_plcp_th = 0; } switch (priv->scan_type) { case IWL_SCAN_RADIO_RESET: IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); /* * Override quiet time as firmware checks that active * dwell is >= quiet; since we use passive scan it'll * not actually be used. */ scan->quiet_time = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); break; case IWL_SCAN_NORMAL: if (priv->scan_request->n_ssids) { int i, p = 0; IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); /* * The highest priority SSID is inserted to the * probe request template. */ ssid_len = priv->scan_request->ssids[0].ssid_len; ssid = priv->scan_request->ssids[0].ssid; /* * Invert the order of ssids, the firmware will invert * it back. */ for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) { scan->direct_scan[p].id = WLAN_EID_SSID; scan->direct_scan[p].len = priv->scan_request->ssids[i].ssid_len; memcpy(scan->direct_scan[p].ssid, priv->scan_request->ssids[i].ssid, priv->scan_request->ssids[i].ssid_len); n_probes++; p++; } is_active = true; } else IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); break; case IWL_SCAN_ROC: IWL_DEBUG_SCAN(priv, "Start ROC scan.\n"); break; } scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; scan->tx_cmd.sta_id = ctx->bcast_sta_id; scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; switch (priv->scan_band) { case IEEE80211_BAND_2GHZ: scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; chan_mod = le32_to_cpu( priv->contexts[IWL_RXON_CTX_BSS].active.flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; if ((priv->scan_request && priv->scan_request->no_cck) || chan_mod == CHANNEL_MODE_PURE_40) { rate = IWL_RATE_6M_PLCP; } else { rate = IWL_RATE_1M_PLCP; rate_flags = RATE_MCS_CCK_MSK; } /* * Internal scans are passive, so we can indiscriminately set * the BT ignore flag on 2.4 GHz since it applies to TX only. */ if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; break; case IEEE80211_BAND_5GHZ: rate = IWL_RATE_6M_PLCP; break; default: IWL_WARN(priv, "Invalid scan band\n"); return -EIO; } /* * If active scanning is requested but a certain channel is * marked passive, we can do active scanning if we detect * transmissions. * * There is an issue with some firmware versions that triggers * a sysassert on a "good CRC threshold" of zero (== disabled), * on a radar channel even though this means that we should NOT * send probes. * * The "good CRC threshold" is the number of frames that we * need to receive during our dwell time on a channel before * sending out probes -- setting this to a huge value will * mean we never reach it, but at the same time work around * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER * here instead of IWL_GOOD_CRC_TH_DISABLED. * * This was fixed in later versions along with some other * scan changes, and the threshold behaves as a flag in those * versions. */ if (priv->new_scan_threshold_behaviour) scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : IWL_GOOD_CRC_TH_DISABLED; else scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : IWL_GOOD_CRC_TH_NEVER; band = priv->scan_band; if (band == IEEE80211_BAND_2GHZ && priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) { /* transmit 2.4 GHz probes only on first antenna */ scan_tx_antennas = first_antenna(scan_tx_antennas); } priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band], scan_tx_antennas); rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); /* * In power save mode while associated use one chain, * otherwise use all chains */ if (test_bit(STATUS_POWER_PMI, &priv->status) && !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { /* rx_ant has been set to all valid chains previously */ active_chains = rx_ant & ((u8)(priv->chain_noise_data.active_chains)); if (!active_chains) active_chains = rx_ant; IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", priv->chain_noise_data.active_chains); rx_ant = first_antenna(active_chains); } if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ rx_ant = first_antenna(rx_ant); } /* MIMO is not used here, but value is required */ rx_chain |= priv->eeprom_data->valid_rx_ant << RXON_RX_CHAIN_VALID_POS; rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; scan->rx_chain = cpu_to_le16(rx_chain); switch (priv->scan_type) { case IWL_SCAN_NORMAL: cmd_len = iwl_fill_probe_req( (struct ieee80211_mgmt *)scan->data, vif->addr, priv->scan_request->ie, priv->scan_request->ie_len, ssid, ssid_len, scan_cmd_size - sizeof(*scan)); break; case IWL_SCAN_RADIO_RESET: case IWL_SCAN_ROC: /* use bcast addr, will not be transmitted but must be valid */ cmd_len = iwl_fill_probe_req( (struct ieee80211_mgmt *)scan->data, iwl_bcast_addr, NULL, 0, NULL, 0, scan_cmd_size - sizeof(*scan)); break; default: BUG(); } scan->tx_cmd.len = cpu_to_le16(cmd_len); scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK); switch (priv->scan_type) { case IWL_SCAN_RADIO_RESET: scan->channel_count = iwl_get_channel_for_reset_scan(priv, vif, band, (void *)&scan->data[cmd_len]); break; case IWL_SCAN_NORMAL: scan->channel_count = iwl_get_channels_for_scan(priv, vif, band, is_active, n_probes, (void *)&scan->data[cmd_len]); break; case IWL_SCAN_ROC: { struct iwl_scan_channel *scan_ch; int n_chan, i; u16 dwell; dwell = iwl_limit_dwell(priv, priv->hw_roc_duration); n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell); scan->channel_count = n_chan; scan_ch = (void *)&scan->data[cmd_len]; for (i = 0; i < n_chan; i++) { scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; scan_ch->channel = cpu_to_le16(priv->hw_roc_channel->hw_value); if (i == n_chan - 1) dwell = priv->hw_roc_duration - i * dwell; scan_ch->active_dwell = scan_ch->passive_dwell = cpu_to_le16(dwell); /* Set txpower levels to defaults */ scan_ch->dsp_atten = 110; /* NOTE: if we were doing 6Mb OFDM for scans we'd use * power level: * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; */ if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ) scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; else scan_ch->tx_gain = ((1 << 5) | (5 << 3)); scan_ch++; } } break; } if (scan->channel_count == 0) { IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); return -EIO; } cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + scan->channel_count * sizeof(struct iwl_scan_channel); cmd.data[0] = scan; cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; scan->len = cpu_to_le16(cmd.len[0]); /* set scan bit here for PAN params */ set_bit(STATUS_SCAN_HW, &priv->status); ret = iwlagn_set_pan_params(priv); if (ret) { clear_bit(STATUS_SCAN_HW, &priv->status); return ret; } ret = iwl_dvm_send_cmd(priv, &cmd); if (ret) { clear_bit(STATUS_SCAN_HW, &priv->status); iwlagn_set_pan_params(priv); } return ret; } void iwl_init_scan_params(struct iwl_priv *priv) { u8 ant_idx = fls(priv->eeprom_data->valid_tx_ant) - 1; if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; } int __must_check iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif, enum iwl_scan_type scan_type, enum ieee80211_band band) { int ret; lockdep_assert_held(&priv->mutex); cancel_delayed_work(&priv->scan_check); if (!iwl_is_ready_rf(priv)) { IWL_WARN(priv, "Request scan called when driver not ready.\n"); return -EIO; } if (test_bit(STATUS_SCAN_HW, &priv->status)) { IWL_DEBUG_SCAN(priv, "Multiple concurrent scan requests in parallel.\n"); return -EBUSY; } if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); return -EBUSY; } IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", scan_type == IWL_SCAN_NORMAL ? "" : scan_type == IWL_SCAN_ROC ? "remain-on-channel " : "internal short "); set_bit(STATUS_SCANNING, &priv->status); priv->scan_type = scan_type; priv->scan_start = jiffies; priv->scan_band = band; ret = iwlagn_request_scan(priv, vif); if (ret) { clear_bit(STATUS_SCANNING, &priv->status); priv->scan_type = IWL_SCAN_NORMAL; return ret; } queue_delayed_work(priv->workqueue, &priv->scan_check, IWL_SCAN_CHECK_WATCHDOG); return 0; } /* * internal short scan, this function should only been called while associated. * It will reset and tune the radio to prevent possible RF related problem */ void iwl_internal_short_hw_scan(struct iwl_priv *priv) { queue_work(priv->workqueue, &priv->start_internal_scan); } static void iwl_bg_start_internal_scan(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, start_internal_scan); IWL_DEBUG_SCAN(priv, "Start internal scan\n"); mutex_lock(&priv->mutex); if (priv->scan_type == IWL_SCAN_RADIO_RESET) { IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); goto unlock; } if (test_bit(STATUS_SCANNING, &priv->status)) { IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); goto unlock; } if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band)) IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); unlock: mutex_unlock(&priv->mutex); } static void iwl_bg_scan_check(struct work_struct *data) { struct iwl_priv *priv = container_of(data, struct iwl_priv, scan_check.work); IWL_DEBUG_SCAN(priv, "Scan check work\n"); /* Since we are here firmware does not finish scan and * most likely is in bad shape, so we don't bother to * send abort command, just force scan complete to mac80211 */ mutex_lock(&priv->mutex); iwl_force_scan_end(priv); mutex_unlock(&priv->mutex); } static void iwl_bg_abort_scan(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); IWL_DEBUG_SCAN(priv, "Abort scan work\n"); /* We keep scan_check work queued in case when firmware will not * report back scan completed notification */ mutex_lock(&priv->mutex); iwl_scan_cancel_timeout(priv, 200); mutex_unlock(&priv->mutex); } static void iwl_bg_scan_completed(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, scan_completed); mutex_lock(&priv->mutex); iwl_process_scan_complete(priv); mutex_unlock(&priv->mutex); } void iwl_setup_scan_deferred_work(struct iwl_priv *priv) { INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan); INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); } void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) { cancel_work_sync(&priv->start_internal_scan); cancel_work_sync(&priv->abort_scan); cancel_work_sync(&priv->scan_completed); if (cancel_delayed_work_sync(&priv->scan_check)) { mutex_lock(&priv->mutex); iwl_force_scan_end(priv); mutex_unlock(&priv->mutex); } } void iwl_scan_roc_expired(struct iwl_priv *priv) { /* * The status bit should be set here, to prevent a race * where the atomic_read returns 1, but before the execution continues * iwl_scan_offchannel_skb_status() checks if the status bit is set */ set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); if (atomic_read(&priv->num_aux_in_flight) == 0) { ieee80211_remain_on_channel_expired(priv->hw); priv->hw_roc_channel = NULL; schedule_delayed_work(&priv->hw_roc_disable_work, 10 * HZ); clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); } else { IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n", atomic_read(&priv->num_aux_in_flight)); } } void iwl_scan_offchannel_skb(struct iwl_priv *priv) { WARN_ON(!priv->hw_roc_start_notified); atomic_inc(&priv->num_aux_in_flight); } void iwl_scan_offchannel_skb_status(struct iwl_priv *priv) { if (atomic_dec_return(&priv->num_aux_in_flight) == 0 && test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) { IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n"); iwl_scan_roc_expired(priv); } } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/rxon.c0000644000175000017500000013206212026211315024057 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include "iwl-trans.h" #include "iwl-modparams.h" #include "dev.h" #include "agn.h" #include "calib.h" /* * initialize rxon structure with default values from eeprom */ void iwl_connection_init_rx_config(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { memset(&ctx->staging, 0, sizeof(ctx->staging)); if (!ctx->vif) { ctx->staging.dev_type = ctx->unused_devtype; } else switch (ctx->vif->type) { case NL80211_IFTYPE_AP: ctx->staging.dev_type = ctx->ap_devtype; break; case NL80211_IFTYPE_STATION: ctx->staging.dev_type = ctx->station_devtype; ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; break; case NL80211_IFTYPE_ADHOC: ctx->staging.dev_type = ctx->ibss_devtype; ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; break; case NL80211_IFTYPE_MONITOR: ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER; break; default: IWL_ERR(priv, "Unsupported interface type %d\n", ctx->vif->type); break; } #if 0 /* TODO: Figure out when short_preamble would be set and cache from * that */ if (!hw_to_local(priv->hw)->short_preamble) ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; else ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; #endif ctx->staging.channel = cpu_to_le16(priv->hw->conf.channel->hw_value); priv->band = priv->hw->conf.channel->band; iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif); /* clear both MIX and PURE40 mode flag */ ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); if (ctx->vif) memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN); ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff; ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff; } static int iwlagn_disable_bss(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_rxon_cmd *send) { __le32 old_filter = send->filter_flags; int ret; send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, sizeof(*send), send); send->filter_flags = old_filter; if (ret) IWL_DEBUG_QUIET_RFKILL(priv, "Error clearing ASSOC_MSK on BSS (%d)\n", ret); return ret; } static int iwlagn_disable_pan(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_rxon_cmd *send) { struct iwl_notification_wait disable_wait; __le32 old_filter = send->filter_flags; u8 old_dev_type = send->dev_type; int ret; static const u8 deactivate_cmd[] = { REPLY_WIPAN_DEACTIVATION_COMPLETE }; iwl_init_notification_wait(&priv->notif_wait, &disable_wait, deactivate_cmd, ARRAY_SIZE(deactivate_cmd), NULL, NULL); send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; send->dev_type = RXON_DEV_TYPE_P2P; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, sizeof(*send), send); send->filter_flags = old_filter; send->dev_type = old_dev_type; if (ret) { IWL_ERR(priv, "Error disabling PAN (%d)\n", ret); iwl_remove_notification(&priv->notif_wait, &disable_wait); } else { ret = iwl_wait_notification(&priv->notif_wait, &disable_wait, HZ); if (ret) IWL_ERR(priv, "Timed out waiting for PAN disable\n"); } return ret; } static int iwlagn_disconn_pan(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_rxon_cmd *send) { __le32 old_filter = send->filter_flags; int ret; send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, sizeof(*send), send); send->filter_flags = old_filter; return ret; } static void iwlagn_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int ret; if (!ctx->is_active) return; ctx->qos_data.def_qos_parm.qos_flags = 0; if (ctx->qos_data.qos_active) ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_UPDATE_EDCA_MSK; if (ctx->ht.enabled) ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", ctx->qos_data.qos_active, ctx->qos_data.def_qos_parm.qos_flags); ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, CMD_SYNC, sizeof(struct iwl_qosparam_cmd), &ctx->qos_data.def_qos_parm); if (ret) IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n"); } static int iwlagn_update_beacon(struct iwl_priv *priv, struct ieee80211_vif *vif) { lockdep_assert_held(&priv->mutex); dev_kfree_skb(priv->beacon_skb); priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif); if (!priv->beacon_skb) return -ENOMEM; return iwlagn_send_beacon_cmd(priv); } static int iwlagn_send_rxon_assoc(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int ret = 0; struct iwl_rxon_assoc_cmd rxon_assoc; const struct iwl_rxon_cmd *rxon1 = &ctx->staging; const struct iwl_rxon_cmd *rxon2 = &ctx->active; if ((rxon1->flags == rxon2->flags) && (rxon1->filter_flags == rxon2->filter_flags) && (rxon1->cck_basic_rates == rxon2->cck_basic_rates) && (rxon1->ofdm_ht_single_stream_basic_rates == rxon2->ofdm_ht_single_stream_basic_rates) && (rxon1->ofdm_ht_dual_stream_basic_rates == rxon2->ofdm_ht_dual_stream_basic_rates) && (rxon1->ofdm_ht_triple_stream_basic_rates == rxon2->ofdm_ht_triple_stream_basic_rates) && (rxon1->acquisition_data == rxon2->acquisition_data) && (rxon1->rx_chain == rxon2->rx_chain) && (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) { IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC. Not resending.\n"); return 0; } rxon_assoc.flags = ctx->staging.flags; rxon_assoc.filter_flags = ctx->staging.filter_flags; rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates; rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates; rxon_assoc.reserved1 = 0; rxon_assoc.reserved2 = 0; rxon_assoc.reserved3 = 0; rxon_assoc.ofdm_ht_single_stream_basic_rates = ctx->staging.ofdm_ht_single_stream_basic_rates; rxon_assoc.ofdm_ht_dual_stream_basic_rates = ctx->staging.ofdm_ht_dual_stream_basic_rates; rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain; rxon_assoc.ofdm_ht_triple_stream_basic_rates = ctx->staging.ofdm_ht_triple_stream_basic_rates; rxon_assoc.acquisition_data = ctx->staging.acquisition_data; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd, CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc); return ret; } static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) { u16 new_val; u16 beacon_factor; /* * If mac80211 hasn't given us a beacon interval, program * the default into the device (not checking this here * would cause the adjustment below to return the maximum * value, which may break PAN.) */ if (!beacon_val) return DEFAULT_BEACON_INTERVAL; /* * If the beacon interval we obtained from the peer * is too large, we'll have to wake up more often * (and in IBSS case, we'll beacon too much) * * For example, if max_beacon_val is 4096, and the * requested beacon interval is 7000, we'll have to * use 3500 to be able to wake up on the beacons. * * This could badly influence beacon detection stats. */ beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; new_val = beacon_val / beacon_factor; if (!new_val) new_val = max_beacon_val; return new_val; } static int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { u64 tsf; s32 interval_tm, rem; struct ieee80211_conf *conf = NULL; u16 beacon_int; struct ieee80211_vif *vif = ctx->vif; conf = &priv->hw->conf; lockdep_assert_held(&priv->mutex); memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd)); ctx->timing.timestamp = cpu_to_le64(priv->timestamp); ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval); beacon_int = vif ? vif->bss_conf.beacon_int : 0; /* * TODO: For IBSS we need to get atim_window from mac80211, * for now just always use 0 */ ctx->timing.atim_window = 0; if (ctx->ctxid == IWL_RXON_CTX_PAN && (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) && iwl_is_associated(priv, IWL_RXON_CTX_BSS) && priv->contexts[IWL_RXON_CTX_BSS].vif && priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) { ctx->timing.beacon_interval = priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval; beacon_int = le16_to_cpu(ctx->timing.beacon_interval); } else if (ctx->ctxid == IWL_RXON_CTX_BSS && iwl_is_associated(priv, IWL_RXON_CTX_PAN) && priv->contexts[IWL_RXON_CTX_PAN].vif && priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int && (!iwl_is_associated_ctx(ctx) || !ctx->vif || !ctx->vif->bss_conf.beacon_int)) { ctx->timing.beacon_interval = priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval; beacon_int = le16_to_cpu(ctx->timing.beacon_interval); } else { beacon_int = iwl_adjust_beacon_interval(beacon_int, IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT); ctx->timing.beacon_interval = cpu_to_le16(beacon_int); } ctx->beacon_int = beacon_int; tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ interval_tm = beacon_int * TIME_UNIT; rem = do_div(tsf, interval_tm); ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1; IWL_DEBUG_ASSOC(priv, "beacon interval %d beacon timer %d beacon tim %d\n", le16_to_cpu(ctx->timing.beacon_interval), le32_to_cpu(ctx->timing.beacon_init_val), le16_to_cpu(ctx->timing.atim_window)); return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd, CMD_SYNC, sizeof(ctx->timing), &ctx->timing); } static int iwlagn_rxon_disconn(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int ret; struct iwl_rxon_cmd *active = (void *)&ctx->active; if (ctx->ctxid == IWL_RXON_CTX_BSS) { ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); } else { ret = iwlagn_disable_pan(priv, ctx, &ctx->staging); if (ret) return ret; if (ctx->vif) { ret = iwl_send_rxon_timing(priv, ctx); if (ret) { IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); return ret; } ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging); } } if (ret) return ret; /* * Un-assoc RXON clears the station table and WEP * keys, so we have to restore those afterwards. */ iwl_clear_ucode_stations(priv, ctx); /* update -- might need P2P now */ iwl_update_bcast_station(priv, ctx); iwl_restore_stations(priv, ctx); ret = iwl_restore_default_wep_keys(priv, ctx); if (ret) { IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); return ret; } memcpy(active, &ctx->staging, sizeof(*active)); return 0; } static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) { int ret; s8 prev_tx_power; bool defer; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED) return 0; lockdep_assert_held(&priv->mutex); if (priv->tx_power_user_lmt == tx_power && !force) return 0; if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) { IWL_WARN(priv, "Requested user TXPOWER %d below lower limit %d.\n", tx_power, IWLAGN_TX_POWER_TARGET_POWER_MIN); return -EINVAL; } if (tx_power > DIV_ROUND_UP(priv->eeprom_data->max_tx_pwr_half_dbm, 2)) { IWL_WARN(priv, "Requested user TXPOWER %d above upper limit %d.\n", tx_power, priv->eeprom_data->max_tx_pwr_half_dbm); return -EINVAL; } if (!iwl_is_ready_rf(priv)) return -EIO; /* scan complete and commit_rxon use tx_power_next value, * it always need to be updated for newest request */ priv->tx_power_next = tx_power; /* do not set tx power when scanning or channel changing */ defer = test_bit(STATUS_SCANNING, &priv->status) || memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); if (defer && !force) { IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); return 0; } prev_tx_power = priv->tx_power_user_lmt; priv->tx_power_user_lmt = tx_power; ret = iwlagn_send_tx_power(priv); /* if fail to set tx_power, restore the orig. tx power */ if (ret) { priv->tx_power_user_lmt = prev_tx_power; priv->tx_power_next = prev_tx_power; } return ret; } static int iwlagn_rxon_connect(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int ret; struct iwl_rxon_cmd *active = (void *)&ctx->active; /* RXON timing must be before associated RXON */ if (ctx->ctxid == IWL_RXON_CTX_BSS) { ret = iwl_send_rxon_timing(priv, ctx); if (ret) { IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); return ret; } } /* QoS info may be cleared by previous un-assoc RXON */ iwlagn_update_qos(priv, ctx); /* * We'll run into this code path when beaconing is * enabled, but then we also need to send the beacon * to the device. */ if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) { ret = iwlagn_update_beacon(priv, ctx->vif); if (ret) { IWL_ERR(priv, "Error sending required beacon (%d)!\n", ret); return ret; } } priv->start_calib = 0; /* * Apply the new configuration. * * Associated RXON doesn't clear the station table in uCode, * so we don't need to restore stations etc. after this. */ ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, sizeof(struct iwl_rxon_cmd), &ctx->staging); if (ret) { IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); return ret; } memcpy(active, &ctx->staging, sizeof(*active)); /* IBSS beacon needs to be sent after setting assoc */ if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC)) if (iwlagn_update_beacon(priv, ctx->vif)) IWL_ERR(priv, "Error sending IBSS beacon\n"); iwl_init_sensitivity(priv); /* * If we issue a new RXON command which required a tune then * we must send a new TXPOWER command or we won't be able to * Tx any frames. * * It's expected we set power here if channel is changing. */ ret = iwl_set_tx_power(priv, priv->tx_power_next, true); if (ret) { IWL_ERR(priv, "Error sending TX power (%d)\n", ret); return ret; } if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && priv->cfg->ht_params && priv->cfg->ht_params->smps_mode) ieee80211_request_smps(ctx->vif, priv->cfg->ht_params->smps_mode); return 0; } int iwlagn_set_pan_params(struct iwl_priv *priv) { struct iwl_wipan_params_cmd cmd; struct iwl_rxon_context *ctx_bss, *ctx_pan; int slot0 = 300, slot1 = 0; int ret; if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS)) return 0; BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); lockdep_assert_held(&priv->mutex); ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS]; ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN]; /* * If the PAN context is inactive, then we don't need * to update the PAN parameters, the last thing we'll * have done before it goes inactive is making the PAN * parameters be WLAN-only. */ if (!ctx_pan->is_active) return 0; memset(&cmd, 0, sizeof(cmd)); /* only 2 slots are currently allowed */ cmd.num_slots = 2; cmd.slots[0].type = 0; /* BSS */ cmd.slots[1].type = 1; /* PAN */ if (priv->hw_roc_setup) { /* both contexts must be used for this to happen */ slot1 = IWL_MIN_SLOT_TIME; slot0 = 3000; } else if (ctx_bss->vif && ctx_pan->vif) { int bcnint = ctx_pan->beacon_int; int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; /* should be set, but seems unused?? */ cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE); if (ctx_pan->vif->type == NL80211_IFTYPE_AP && bcnint && bcnint != ctx_bss->beacon_int) { IWL_ERR(priv, "beacon intervals don't match (%d, %d)\n", ctx_bss->beacon_int, ctx_pan->beacon_int); } else bcnint = max_t(int, bcnint, ctx_bss->beacon_int); if (!bcnint) bcnint = DEFAULT_BEACON_INTERVAL; slot0 = bcnint / 2; slot1 = bcnint - slot0; if (test_bit(STATUS_SCAN_HW, &priv->status) || (!ctx_bss->vif->bss_conf.idle && !ctx_bss->vif->bss_conf.assoc)) { slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; slot1 = IWL_MIN_SLOT_TIME; } else if (!ctx_pan->vif->bss_conf.idle && !ctx_pan->vif->bss_conf.assoc) { slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; slot0 = IWL_MIN_SLOT_TIME; } } else if (ctx_pan->vif) { slot0 = 0; slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) * ctx_pan->beacon_int; slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1); if (test_bit(STATUS_SCAN_HW, &priv->status)) { slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME; slot1 = IWL_MIN_SLOT_TIME; } } cmd.slots[0].width = cpu_to_le16(slot0); cmd.slots[1].width = cpu_to_le16(slot1); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, CMD_SYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret); return ret; } static void _iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf, struct iwl_rxon_context *ctx) { struct iwl_rxon_cmd *rxon = &ctx->staging; if (!ctx->ht.enabled) { rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK | RXON_FLG_HT_PROT_MSK); return; } /* FIXME: if the definition of ht.protection changed, the "translation" * will be needed for rxon->flags */ rxon->flags |= cpu_to_le32(ctx->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS); /* Set up channel bandwidth: * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ /* clear the HT channel mode before set the mode */ rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) { /* pure ht40 */ if (ctx->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; /* * Note: control channel is opposite of extension * channel */ switch (ctx->ht.extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; } } else { /* * Note: control channel is opposite of extension * channel */ switch (ctx->ht.extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; break; case IEEE80211_HT_PARAM_CHA_SEC_NONE: default: /* * channel location only valid if in Mixed * mode */ IWL_ERR(priv, "invalid extension channel offset\n"); break; } } } else { rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; } iwlagn_set_rxon_chain(priv, ctx); IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags), ctx->ht.protection, ctx->ht.extension_chan_offset); } void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) { struct iwl_rxon_context *ctx; for_each_context(priv, ctx) _iwl_set_rxon_ht(priv, ht_conf, ctx); } /** * iwl_set_rxon_channel - Set the band and channel values in staging RXON * @ch: requested channel as a pointer to struct ieee80211_channel * NOTE: Does not commit to the hardware; it sets appropriate bit fields * in the staging RXON flag structure based on the ch->band */ void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, struct iwl_rxon_context *ctx) { enum ieee80211_band band = ch->band; u16 channel = ch->hw_value; if ((le16_to_cpu(ctx->staging.channel) == channel) && (priv->band == band)) return; ctx->staging.channel = cpu_to_le16(channel); if (band == IEEE80211_BAND_5GHZ) ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK; else ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; priv->band = band; IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band); } void iwl_set_flags_for_band(struct iwl_priv *priv, struct iwl_rxon_context *ctx, enum ieee80211_band band, struct ieee80211_vif *vif) { if (band == IEEE80211_BAND_5GHZ) { ctx->staging.flags &= ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_CCK_MSK); ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; } else { /* Copied from iwl_post_associate() */ if (vif && vif->bss_conf.use_short_slot) ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; ctx->staging.flags &= ~RXON_FLG_CCK_MSK; } } static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx, int hw_decrypt) { struct iwl_rxon_cmd *rxon = &ctx->staging; if (hw_decrypt) rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; else rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; } /* validate RXON structure is valid */ static int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { struct iwl_rxon_cmd *rxon = &ctx->staging; u32 errors = 0; if (rxon->flags & RXON_FLG_BAND_24G_MSK) { if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { IWL_WARN(priv, "check 2.4G: wrong narrow\n"); errors |= BIT(0); } if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { IWL_WARN(priv, "check 2.4G: wrong radar\n"); errors |= BIT(1); } } else { if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { IWL_WARN(priv, "check 5.2G: not short slot!\n"); errors |= BIT(2); } if (rxon->flags & RXON_FLG_CCK_MSK) { IWL_WARN(priv, "check 5.2G: CCK!\n"); errors |= BIT(3); } } if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { IWL_WARN(priv, "mac/bssid mcast!\n"); errors |= BIT(4); } /* make sure basic rates 6Mbps and 1Mbps are supported */ if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 && (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) { IWL_WARN(priv, "neither 1 nor 6 are basic\n"); errors |= BIT(5); } if (le16_to_cpu(rxon->assoc_id) > 2007) { IWL_WARN(priv, "aid > 2007\n"); errors |= BIT(6); } if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { IWL_WARN(priv, "CCK and short slot\n"); errors |= BIT(7); } if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { IWL_WARN(priv, "CCK and auto detect"); errors |= BIT(8); } if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) == RXON_FLG_TGG_PROTECT_MSK) { IWL_WARN(priv, "TGg but no auto-detect\n"); errors |= BIT(9); } if (rxon->channel == 0) { IWL_WARN(priv, "zero channel is invalid\n"); errors |= BIT(10); } WARN(errors, "Invalid RXON (%#x), channel %d", errors, le16_to_cpu(rxon->channel)); return errors ? -EINVAL : 0; } /** * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed * @priv: staging_rxon is compared to active_rxon * * If the RXON structure is changing enough to require a new tune, * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. */ static int iwl_full_rxon_required(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { const struct iwl_rxon_cmd *staging = &ctx->staging; const struct iwl_rxon_cmd *active = &ctx->active; #define CHK(cond) \ if ((cond)) { \ IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \ return 1; \ } #define CHK_NEQ(c1, c2) \ if ((c1) != (c2)) { \ IWL_DEBUG_INFO(priv, "need full RXON - " \ #c1 " != " #c2 " - %d != %d\n", \ (c1), (c2)); \ return 1; \ } /* These items are only settable from the full RXON command */ CHK(!iwl_is_associated_ctx(ctx)); CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr)); CHK(!ether_addr_equal(staging->node_addr, active->node_addr)); CHK(!ether_addr_equal(staging->wlap_bssid_addr, active->wlap_bssid_addr)); CHK_NEQ(staging->dev_type, active->dev_type); CHK_NEQ(staging->channel, active->channel); CHK_NEQ(staging->air_propagation, active->air_propagation); CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, active->ofdm_ht_single_stream_basic_rates); CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, active->ofdm_ht_dual_stream_basic_rates); CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates, active->ofdm_ht_triple_stream_basic_rates); CHK_NEQ(staging->assoc_id, active->assoc_id); /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can * be updated with the RXON_ASSOC command -- however only some * flag transitions are allowed using RXON_ASSOC */ /* Check if we are not switching bands */ CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, active->flags & RXON_FLG_BAND_24G_MSK); /* Check if we are switching association toggle */ CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, active->filter_flags & RXON_FILTER_ASSOC_MSK); #undef CHK #undef CHK_NEQ return 0; } #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv, enum iwl_rxon_context_id ctxid) { struct iwl_rxon_context *ctx = &priv->contexts[ctxid]; struct iwl_rxon_cmd *rxon = &ctx->staging; IWL_DEBUG_RADIO(priv, "RX CONFIG:\n"); iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags)); IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type); IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates); IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr); IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); } #endif static void iwl_calc_basic_rates(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int lowest_present_ofdm = 100; int lowest_present_cck = 100; u8 cck = 0; u8 ofdm = 0; if (ctx->vif) { struct ieee80211_supported_band *sband; unsigned long basic = ctx->vif->bss_conf.basic_rates; int i; sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band]; for_each_set_bit(i, &basic, BITS_PER_LONG) { int hw = sband->bitrates[i].hw_value; if (hw >= IWL_FIRST_OFDM_RATE) { ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); if (lowest_present_ofdm > hw) lowest_present_ofdm = hw; } else { BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); cck |= BIT(hw); if (lowest_present_cck > hw) lowest_present_cck = hw; } } } /* * Now we've got the basic rates as bitmaps in the ofdm and cck * variables. This isn't sufficient though, as there might not * be all the right rates in the bitmap. E.g. if the only basic * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps * and 6 Mbps because the 802.11-2007 standard says in 9.6: * * [...] a STA responding to a received frame shall transmit * its Control Response frame [...] at the highest rate in the * BSSBasicRateSet parameter that is less than or equal to the * rate of the immediately previous frame in the frame exchange * sequence ([...]) and that is of the same modulation class * ([...]) as the received frame. If no rate contained in the * BSSBasicRateSet parameter meets these conditions, then the * control frame sent in response to a received frame shall be * transmitted at the highest mandatory rate of the PHY that is * less than or equal to the rate of the received frame, and * that is of the same modulation class as the received frame. * * As a consequence, we need to add all mandatory rates that are * lower than all of the basic rates to these bitmaps. */ if (IWL_RATE_24M_INDEX < lowest_present_ofdm) ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE; if (IWL_RATE_12M_INDEX < lowest_present_ofdm) ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE; /* 6M already there or needed so always add */ ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE; /* * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. * Note, however: * - if no CCK rates are basic, it must be ERP since there must * be some basic rates at all, so they're OFDM => ERP PHY * (or we're in 5 GHz, and the cck bitmap will never be used) * - if 11M is a basic rate, it must be ERP as well, so add 5.5M * - if 5.5M is basic, 1M and 2M are mandatory * - if 2M is basic, 1M is mandatory * - if 1M is basic, that's the only valid ACK rate. * As a consequence, it's not as complicated as it sounds, just add * any lower rates to the ACK rate bitmap. */ if (IWL_RATE_11M_INDEX < lowest_present_ofdm) ofdm |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE; if (IWL_RATE_5M_INDEX < lowest_present_ofdm) ofdm |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE; if (IWL_RATE_2M_INDEX < lowest_present_ofdm) ofdm |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE; /* 1M already there or needed so always add */ cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE; IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n", cck, ofdm); /* "basic_rates" is a misnomer here -- should be called ACK rates */ ctx->staging.cck_basic_rates = cck; ctx->staging.ofdm_basic_rates = ofdm; } /** * iwlagn_commit_rxon - commit staging_rxon to hardware * * The RXON command in staging_rxon is committed to the hardware and * the active_rxon structure is updated with the new data. This * function correctly transitions out of the RXON_ASSOC_MSK state if * a HW tune is required based on the RXON structure changes. * * The connect/disconnect flow should be as the following: * * 1. make sure send RXON command with association bit unset if not connect * this should include the channel and the band for the candidate * to be connected to * 2. Add Station before RXON association with the AP * 3. RXON_timing has to send before RXON for connection * 4. full RXON command - associated bit set * 5. use RXON_ASSOC command to update any flags changes */ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { /* cast away the const for active_rxon in this function */ struct iwl_rxon_cmd *active = (void *)&ctx->active; bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK); int ret; lockdep_assert_held(&priv->mutex); if (!iwl_is_alive(priv)) return -EBUSY; /* This function hardcodes a bunch of dual-mode assumptions */ BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); if (!ctx->is_active) return 0; /* always get timestamp with Rx frame */ ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; /* recalculate basic rates */ iwl_calc_basic_rates(priv, ctx); /* * force CTS-to-self frames protection if RTS-CTS is not preferred * one aggregation protection method */ if (!priv->hw_params.use_rts_for_aggregation) ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; iwl_print_rx_config_cmd(priv, ctx->ctxid); ret = iwl_check_rxon_cmd(priv, ctx); if (ret) { IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); return -EINVAL; } /* * receive commit_rxon request * abort any previous channel switch if still in process */ if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) && (priv->switch_channel != ctx->staging.channel)) { IWL_DEBUG_11H(priv, "abort channel switch on %d\n", le16_to_cpu(priv->switch_channel)); iwl_chswitch_done(priv, false); } /* * If we don't need to send a full RXON, we can use * iwl_rxon_assoc_cmd which is used to reconfigure filter * and other flags for the current radio configuration. */ if (!iwl_full_rxon_required(priv, ctx)) { ret = iwlagn_send_rxon_assoc(priv, ctx); if (ret) { IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret); return ret; } memcpy(active, &ctx->staging, sizeof(*active)); /* * We do not commit tx power settings while channel changing, * do it now if after settings changed. */ iwl_set_tx_power(priv, priv->tx_power_next, false); /* make sure we are in the right PS state */ iwl_power_update_mode(priv, true); return 0; } iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.sw_crypto); IWL_DEBUG_INFO(priv, "Going to commit RXON\n" " * with%s RXON_FILTER_ASSOC_MSK\n" " * channel = %d\n" " * bssid = %pM\n", (new_assoc ? "" : "out"), le16_to_cpu(ctx->staging.channel), ctx->staging.bssid_addr); /* * Always clear associated first, but with the correct config. * This is required as for example station addition for the * AP station must be done after the BSSID is set to correctly * set up filters in the device. */ ret = iwlagn_rxon_disconn(priv, ctx); if (ret) return ret; ret = iwlagn_set_pan_params(priv); if (ret) return ret; if (new_assoc) return iwlagn_rxon_connect(priv, ctx); return 0; } void iwlagn_config_ht40(struct ieee80211_conf *conf, struct iwl_rxon_context *ctx) { if (conf_is_ht40_minus(conf)) { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; ctx->ht.is_40mhz = true; } else if (conf_is_ht40_plus(conf)) { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; ctx->ht.is_40mhz = true; } else { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; ctx->ht.is_40mhz = false; } } int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = conf->channel; int ret = 0; IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed); mutex_lock(&priv->mutex); if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); goto out; } if (!iwl_is_ready(priv)) { IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); goto out; } if (changed & (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { /* mac80211 uses static for non-HT which is what we want */ priv->current_ht_config.smps = conf->smps_mode; /* * Recalculate chain counts. * * If monitor mode is enabled then mac80211 will * set up the SM PS mode to OFF if an HT channel is * configured. */ for_each_context(priv, ctx) iwlagn_set_rxon_chain(priv, ctx); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { for_each_context(priv, ctx) { /* Configure HT40 channels */ if (ctx->ht.enabled != conf_is_ht(conf)) ctx->ht.enabled = conf_is_ht(conf); if (ctx->ht.enabled) { /* if HT40 is used, it should not change * after associated except channel switch */ if (!ctx->ht.is_40mhz || !iwl_is_associated_ctx(ctx)) iwlagn_config_ht40(conf, ctx); } else ctx->ht.is_40mhz = false; /* * Default to no protection. Protection mode will * later be set from BSS config in iwl_ht_conf */ ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; /* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not * support ht */ if (le16_to_cpu(ctx->staging.channel) != channel->hw_value) ctx->staging.flags = 0; iwl_set_rxon_channel(priv, channel, ctx); iwl_set_rxon_ht(priv, &priv->current_ht_config); iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); } iwl_update_bcast_stations(priv); } if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { ret = iwl_power_update_mode(priv, false); if (ret) IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); } if (changed & IEEE80211_CONF_CHANGE_POWER) { IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", priv->tx_power_user_lmt, conf->power_level); iwl_set_tx_power(priv, conf->power_level, false); } for_each_context(priv, ctx) { if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) continue; iwlagn_commit_rxon(priv, ctx); } out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return ret; } static void iwlagn_check_needed_chains(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_bss_conf *bss_conf) { struct ieee80211_vif *vif = ctx->vif; struct iwl_rxon_context *tmp; struct ieee80211_sta *sta; struct iwl_ht_config *ht_conf = &priv->current_ht_config; struct ieee80211_sta_ht_cap *ht_cap; bool need_multiple; lockdep_assert_held(&priv->mutex); switch (vif->type) { case NL80211_IFTYPE_STATION: rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!sta) { /* * If at all, this can only happen through a race * when the AP disconnects us while we're still * setting up the connection, in that case mac80211 * will soon tell us about that. */ need_multiple = false; rcu_read_unlock(); break; } ht_cap = &sta->ht_cap; need_multiple = true; /* * If the peer advertises no support for receiving 2 and 3 * stream MCS rates, it can't be transmitting them either. */ if (ht_cap->mcs.rx_mask[1] == 0 && ht_cap->mcs.rx_mask[2] == 0) { need_multiple = false; } else if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_DEFINED)) { /* If it can't TX MCS at all ... */ need_multiple = false; } else if (ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) { int maxstreams; /* * But if it can receive them, it might still not * be able to transmit them, which is what we need * to check here -- so check the number of streams * it advertises for TX (if different from RX). */ maxstreams = (ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK); maxstreams >>= IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; maxstreams += 1; if (maxstreams <= 1) need_multiple = false; } rcu_read_unlock(); break; case NL80211_IFTYPE_ADHOC: /* currently */ need_multiple = false; break; default: /* only AP really */ need_multiple = true; break; } ctx->ht_need_multiple_chains = need_multiple; if (!need_multiple) { /* check all contexts */ for_each_context(priv, tmp) { if (!tmp->vif) continue; if (tmp->ht_need_multiple_chains) { need_multiple = true; break; } } } ht_conf->single_chain_sufficient = !need_multiple; } static void iwlagn_chain_noise_reset(struct iwl_priv *priv) { struct iwl_chain_noise_data *data = &priv->chain_noise_data; int ret; if (!(priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)) return; if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_any_associated(priv)) { struct iwl_calib_chain_noise_reset_cmd cmd; /* clear data for chain noise calibration algorithm */ data->chain_noise_a = 0; data->chain_noise_b = 0; data->chain_noise_c = 0; data->chain_signal_a = 0; data->chain_signal_b = 0; data->chain_signal_c = 0; data->beacon_count = 0; memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, priv->phy_calib_chain_noise_reset_cmd); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, CMD_SYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "Could not send REPLY_PHY_CALIBRATION_CMD\n"); data->state = IWL_CHAIN_NOISE_ACCUMULATE; IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n"); } } void iwlagn_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); int ret; bool force = false; mutex_lock(&priv->mutex); if (unlikely(!iwl_is_ready(priv))) { IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); mutex_unlock(&priv->mutex); return; } if (unlikely(!ctx->vif)) { IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n"); mutex_unlock(&priv->mutex); return; } if (changes & BSS_CHANGED_BEACON_INT) force = true; if (changes & BSS_CHANGED_QOS) { ctx->qos_data.qos_active = bss_conf->qos; iwlagn_update_qos(priv, ctx); } ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); if (vif->bss_conf.use_short_preamble) ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; if (changes & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { priv->timestamp = bss_conf->sync_tsf; ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; } else { /* * If we disassociate while there are pending * frames, just wake up the queues and let the * frames "escape" ... This shouldn't really * be happening to start with, but we should * not get stuck in this case either since it * can happen if userspace gets confused. */ iwlagn_lift_passive_no_rx(priv); ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; if (ctx->ctxid == IWL_RXON_CTX_BSS) priv->have_rekey_data = false; } iwlagn_bt_coex_rssi_monitor(priv); } if (ctx->ht.enabled) { ctx->ht.protection = bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); iwlagn_check_needed_chains(priv, ctx, bss_conf); iwl_set_rxon_ht(priv, &priv->current_ht_config); } iwlagn_set_rxon_chain(priv, ctx); if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; else ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; if (bss_conf->use_cts_prot) ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; else ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) { if (vif->bss_conf.enable_beacon) { ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; priv->beacon_ctx = ctx; } else { ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; priv->beacon_ctx = NULL; } } /* * If the ucode decides to do beacon filtering before * association, it will lose beacons that are needed * before sending frames out on passive channels. This * causes association failures on those channels. Enable * receiving beacons in such cases. */ if (vif->type == NL80211_IFTYPE_STATION) { if (!bss_conf->assoc) ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK; else ctx->staging.filter_flags &= ~RXON_FILTER_BCON_AWARE_MSK; } if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) iwlagn_commit_rxon(priv, ctx); if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) { /* * The chain noise calibration will enable PM upon * completion. If calibration has already been run * then we need to enable power management here. */ if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) iwl_power_update_mode(priv, false); /* Enable RX differential gain and sensitivity calibrations */ iwlagn_chain_noise_reset(priv); priv->start_calib = 1; } if (changes & BSS_CHANGED_IBSS) { ret = iwlagn_manage_ibss_station(priv, vif, bss_conf->ibss_joined); if (ret) IWL_ERR(priv, "failed to %s IBSS station %pM\n", bss_conf->ibss_joined ? "add" : "remove", bss_conf->bssid); } if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_ADHOC && priv->beacon_ctx) { if (iwlagn_update_beacon(priv, vif)) IWL_ERR(priv, "Error sending IBSS beacon\n"); } mutex_unlock(&priv->mutex); } void iwlagn_post_scan(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; /* * We do not commit power settings while scan is pending, * do it now if the settings changed. */ iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false); iwl_set_tx_power(priv, priv->tx_power_next, false); /* * Since setting the RXON may have been deferred while * performing the scan, fire one off if needed */ for_each_context(priv, ctx) if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) iwlagn_commit_rxon(priv, ctx); iwlagn_set_pan_params(priv); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/rx.c0000644000175000017500000010660612026211315023527 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portionhelp of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include "iwl-io.h" #include "dev.h" #include "calib.h" #include "agn.h" #define IWL_CMD_ENTRY(x) [x] = #x const char *iwl_dvm_cmd_strings[REPLY_MAX] = { IWL_CMD_ENTRY(REPLY_ALIVE), IWL_CMD_ENTRY(REPLY_ERROR), IWL_CMD_ENTRY(REPLY_ECHO), IWL_CMD_ENTRY(REPLY_RXON), IWL_CMD_ENTRY(REPLY_RXON_ASSOC), IWL_CMD_ENTRY(REPLY_QOS_PARAM), IWL_CMD_ENTRY(REPLY_RXON_TIMING), IWL_CMD_ENTRY(REPLY_ADD_STA), IWL_CMD_ENTRY(REPLY_REMOVE_STA), IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA), IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH), IWL_CMD_ENTRY(REPLY_WEPKEY), IWL_CMD_ENTRY(REPLY_TX), IWL_CMD_ENTRY(REPLY_LEDS_CMD), IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD), IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD), IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION), IWL_CMD_ENTRY(COEX_EVENT_CMD), IWL_CMD_ENTRY(REPLY_QUIET_CMD), IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH), IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION), IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD), IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION), IWL_CMD_ENTRY(POWER_TABLE_CMD), IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION), IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC), IWL_CMD_ENTRY(REPLY_SCAN_CMD), IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD), IWL_CMD_ENTRY(SCAN_START_NOTIFICATION), IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION), IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION), IWL_CMD_ENTRY(BEACON_NOTIFICATION), IWL_CMD_ENTRY(REPLY_TX_BEACON), IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION), IWL_CMD_ENTRY(QUIET_NOTIFICATION), IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD), IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION), IWL_CMD_ENTRY(REPLY_BT_CONFIG), IWL_CMD_ENTRY(REPLY_STATISTICS_CMD), IWL_CMD_ENTRY(STATISTICS_NOTIFICATION), IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD), IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION), IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION), IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD), IWL_CMD_ENTRY(SENSITIVITY_CMD), IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD), IWL_CMD_ENTRY(REPLY_RX_PHY_CMD), IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD), IWL_CMD_ENTRY(REPLY_COMPRESSED_BA), IWL_CMD_ENTRY(CALIBRATION_CFG_CMD), IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION), IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION), IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD), IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION), IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD), IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF), IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE), IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV), IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS), IWL_CMD_ENTRY(REPLY_WIPAN_RXON), IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING), IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC), IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM), IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY), IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH), IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION), IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE), IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS), IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER), IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS), IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS), IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL), IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS), IWL_CMD_ENTRY(REPLY_D3_CONFIG), }; #undef IWL_CMD_ENTRY /****************************************************************************** * * Generic RX handler implementations * ******************************************************************************/ static int iwlagn_rx_reply_error(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_error_resp *err_resp = (void *)pkt->data; IWL_ERR(priv, "Error Reply type 0x%08X cmd REPLY_ERROR (0x%02X) " "seq 0x%04X ser 0x%08X\n", le32_to_cpu(err_resp->error_type), err_resp->cmd_id, le16_to_cpu(err_resp->bad_cmd_seq_num), le32_to_cpu(err_resp->error_info)); return 0; } static int iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_csa_notification *csa = (void *)pkt->data; /* * MULTI-FIXME * See iwlagn_mac_channel_switch. */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwl_rxon_cmd *rxon = (void *)&ctx->active; if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) return 0; if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) { rxon->channel = csa->channel; ctx->staging.channel = csa->channel; IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", le16_to_cpu(csa->channel)); iwl_chswitch_done(priv, true); } else { IWL_ERR(priv, "CSA notif (fail) : channel %d\n", le16_to_cpu(csa->channel)); iwl_chswitch_done(priv, false); } return 0; } static int iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_spectrum_notification *report = (void *)pkt->data; if (!report->state) { IWL_DEBUG_11H(priv, "Spectrum Measure Notification: Start\n"); return 0; } memcpy(&priv->measure_report, report, sizeof(*report)); priv->measurement_status |= MEASUREMENT_READY; return 0; } static int iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { #ifdef CONFIG_IWLWIFI_DEBUG struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_sleep_notification *sleep = (void *)pkt->data; IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", sleep->pm_sleep_mode, sleep->pm_wakeup_src); #endif return 0; } static int iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 __maybe_unused len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len); iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len); return 0; } static int iwlagn_rx_beacon_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwlagn_beacon_notif *beacon = (void *)pkt->data; #ifdef CONFIG_IWLWIFI_DEBUG u16 status = le16_to_cpu(beacon->beacon_notify_hdr.status.status); u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d " "tsf:0x%.8x%.8x rate:%d\n", status & TX_STATUS_MSK, beacon->beacon_notify_hdr.failure_frame, le32_to_cpu(beacon->ibss_mgr_status), le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); #endif priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); return 0; } /** * iwl_good_plcp_health - checks for plcp error. * * When the plcp error is exceeding the thresholds, reset the radio * to improve the throughput. */ static bool iwlagn_good_plcp_health(struct iwl_priv *priv, struct statistics_rx_phy *cur_ofdm, struct statistics_rx_ht_phy *cur_ofdm_ht, unsigned int msecs) { int delta; int threshold = priv->plcp_delta_threshold; if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); return true; } delta = le32_to_cpu(cur_ofdm->plcp_err) - le32_to_cpu(priv->statistics.rx_ofdm.plcp_err) + le32_to_cpu(cur_ofdm_ht->plcp_err) - le32_to_cpu(priv->statistics.rx_ofdm_ht.plcp_err); /* Can be negative if firmware reset statistics */ if (delta <= 0) return true; if ((delta * 100 / msecs) > threshold) { IWL_DEBUG_RADIO(priv, "plcp health threshold %u delta %d msecs %u\n", threshold, delta, msecs); return false; } return true; } int iwl_force_rf_reset(struct iwl_priv *priv, bool external) { struct iwl_rf_reset *rf_reset; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return -EAGAIN; if (!iwl_is_any_associated(priv)) { IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); return -ENOLINK; } rf_reset = &priv->rf_reset; rf_reset->reset_request_count++; if (!external && rf_reset->last_reset_jiffies && time_after(rf_reset->last_reset_jiffies + IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) { IWL_DEBUG_INFO(priv, "RF reset rejected\n"); rf_reset->reset_reject_count++; return -EAGAIN; } rf_reset->reset_success_count++; rf_reset->last_reset_jiffies = jiffies; /* * There is no easy and better way to force reset the radio, * the only known method is switching channel which will force to * reset and tune the radio. * Use internal short scan (single channel) operation to should * achieve this objective. * Driver should reset the radio when number of consecutive missed * beacon, or any other uCode error condition detected. */ IWL_DEBUG_INFO(priv, "perform radio reset.\n"); iwl_internal_short_hw_scan(priv); return 0; } static void iwlagn_recover_from_statistics(struct iwl_priv *priv, struct statistics_rx_phy *cur_ofdm, struct statistics_rx_ht_phy *cur_ofdm_ht, struct statistics_tx *tx, unsigned long stamp) { unsigned int msecs; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies); /* Only gather statistics and update time stamp when not associated */ if (!iwl_is_any_associated(priv)) return; /* Do not check/recover when do not have enough statistics data */ if (msecs < 99) return; if (iwlwifi_mod_params.plcp_check && !iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs)) iwl_force_rf_reset(priv, false); } /* Calculate noise level, based on measurements during network silence just * before arriving beacon. This measurement can be done only if we know * exactly when to expect beacons, therefore only when we're associated. */ static void iwlagn_rx_calc_noise(struct iwl_priv *priv) { struct statistics_rx_non_phy *rx_info; int num_active_rx = 0; int total_silence = 0; int bcn_silence_a, bcn_silence_b, bcn_silence_c; int last_rx_noise; rx_info = &priv->statistics.rx_non_phy; bcn_silence_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; bcn_silence_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; bcn_silence_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; if (bcn_silence_a) { total_silence += bcn_silence_a; num_active_rx++; } if (bcn_silence_b) { total_silence += bcn_silence_b; num_active_rx++; } if (bcn_silence_c) { total_silence += bcn_silence_c; num_active_rx++; } /* Average among active antennas */ if (num_active_rx) last_rx_noise = (total_silence / num_active_rx) - 107; else last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a, bcn_silence_b, bcn_silence_c, last_rx_noise); } #ifdef CONFIG_IWLWIFI_DEBUGFS /* * based on the assumption of all statistics counter are in DWORD * FIXME: This function is for debugging, do not deal with * the case of counters roll-over. */ static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta, __le32 *max_delta, __le32 *accum, int size) { int i; for (i = 0; i < size / sizeof(__le32); i++, prev++, cur++, delta++, max_delta++, accum++) { if (le32_to_cpu(*cur) > le32_to_cpu(*prev)) { *delta = cpu_to_le32( le32_to_cpu(*cur) - le32_to_cpu(*prev)); le32_add_cpu(accum, le32_to_cpu(*delta)); if (le32_to_cpu(*delta) > le32_to_cpu(*max_delta)) *max_delta = *delta; } } } static void iwlagn_accumulative_statistics(struct iwl_priv *priv, struct statistics_general_common *common, struct statistics_rx_non_phy *rx_non_phy, struct statistics_rx_phy *rx_ofdm, struct statistics_rx_ht_phy *rx_ofdm_ht, struct statistics_rx_phy *rx_cck, struct statistics_tx *tx, struct statistics_bt_activity *bt_activity) { #define ACCUM(_name) \ accum_stats((__le32 *)&priv->statistics._name, \ (__le32 *)_name, \ (__le32 *)&priv->delta_stats._name, \ (__le32 *)&priv->max_delta_stats._name, \ (__le32 *)&priv->accum_stats._name, \ sizeof(*_name)); ACCUM(common); ACCUM(rx_non_phy); ACCUM(rx_ofdm); ACCUM(rx_ofdm_ht); ACCUM(rx_cck); ACCUM(tx); if (bt_activity) ACCUM(bt_activity); #undef ACCUM } #else static inline void iwlagn_accumulative_statistics(struct iwl_priv *priv, struct statistics_general_common *common, struct statistics_rx_non_phy *rx_non_phy, struct statistics_rx_phy *rx_ofdm, struct statistics_rx_ht_phy *rx_ofdm_ht, struct statistics_rx_phy *rx_cck, struct statistics_tx *tx, struct statistics_bt_activity *bt_activity) { } #endif static int iwlagn_rx_statistics(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { unsigned long stamp = jiffies; const int reg_recalib_period = 60; int change; struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; __le32 *flag; struct statistics_general_common *common; struct statistics_rx_non_phy *rx_non_phy; struct statistics_rx_phy *rx_ofdm; struct statistics_rx_ht_phy *rx_ofdm_ht; struct statistics_rx_phy *rx_cck; struct statistics_tx *tx; struct statistics_bt_activity *bt_activity; len -= sizeof(struct iwl_cmd_header); /* skip header */ IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", len); spin_lock(&priv->statistics.lock); if (len == sizeof(struct iwl_bt_notif_statistics)) { struct iwl_bt_notif_statistics *stats; stats = (void *)&pkt->data; flag = &stats->flag; common = &stats->general.common; rx_non_phy = &stats->rx.general.common; rx_ofdm = &stats->rx.ofdm; rx_ofdm_ht = &stats->rx.ofdm_ht; rx_cck = &stats->rx.cck; tx = &stats->tx; bt_activity = &stats->general.activity; #ifdef CONFIG_IWLWIFI_DEBUGFS /* handle this exception directly */ priv->statistics.num_bt_kills = stats->rx.general.num_bt_kills; le32_add_cpu(&priv->statistics.accum_num_bt_kills, le32_to_cpu(stats->rx.general.num_bt_kills)); #endif } else if (len == sizeof(struct iwl_notif_statistics)) { struct iwl_notif_statistics *stats; stats = (void *)&pkt->data; flag = &stats->flag; common = &stats->general.common; rx_non_phy = &stats->rx.general; rx_ofdm = &stats->rx.ofdm; rx_ofdm_ht = &stats->rx.ofdm_ht; rx_cck = &stats->rx.cck; tx = &stats->tx; bt_activity = NULL; } else { WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n", len, sizeof(struct iwl_bt_notif_statistics), sizeof(struct iwl_notif_statistics)); spin_unlock(&priv->statistics.lock); return 0; } change = common->temperature != priv->statistics.common.temperature || (*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) != (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK); iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm, rx_ofdm_ht, rx_cck, tx, bt_activity); iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp); priv->statistics.flag = *flag; memcpy(&priv->statistics.common, common, sizeof(*common)); memcpy(&priv->statistics.rx_non_phy, rx_non_phy, sizeof(*rx_non_phy)); memcpy(&priv->statistics.rx_ofdm, rx_ofdm, sizeof(*rx_ofdm)); memcpy(&priv->statistics.rx_ofdm_ht, rx_ofdm_ht, sizeof(*rx_ofdm_ht)); memcpy(&priv->statistics.rx_cck, rx_cck, sizeof(*rx_cck)); memcpy(&priv->statistics.tx, tx, sizeof(*tx)); #ifdef CONFIG_IWLWIFI_DEBUGFS if (bt_activity) memcpy(&priv->statistics.bt_activity, bt_activity, sizeof(*bt_activity)); #endif priv->rx_statistics_jiffies = stamp; set_bit(STATUS_STATISTICS, &priv->status); /* Reschedule the statistics timer to occur in * reg_recalib_period seconds to ensure we get a * thermal update even if the uCode doesn't give * us one */ mod_timer(&priv->statistics_periodic, jiffies + msecs_to_jiffies(reg_recalib_period * 1000)); if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { iwlagn_rx_calc_noise(priv); queue_work(priv->workqueue, &priv->run_time_calib_work); } if (priv->lib->temperature && change) priv->lib->temperature(priv); spin_unlock(&priv->statistics.lock); return 0; } static int iwlagn_rx_reply_statistics(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_notif_statistics *stats = (void *)pkt->data; if (le32_to_cpu(stats->flag) & UCODE_STATISTICS_CLEAR_MSK) { #ifdef CONFIG_IWLWIFI_DEBUGFS memset(&priv->accum_stats, 0, sizeof(priv->accum_stats)); memset(&priv->delta_stats, 0, sizeof(priv->delta_stats)); memset(&priv->max_delta_stats, 0, sizeof(priv->max_delta_stats)); #endif IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); } iwlagn_rx_statistics(priv, rxb, cmd); return 0; } /* Handle notification from uCode that card's power state is changing * due to software, hardware, or critical temperature RFKILL */ static int iwlagn_rx_card_state_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; u32 flags = le32_to_cpu(card_state_notif->flags); unsigned long status = priv->status; IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", (flags & SW_CARD_DISABLED) ? "Kill" : "On", (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached"); if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) { iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); if (!(flags & RXON_CARD_DISABLED)) { iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); } if (flags & CT_CARD_DISABLED) iwl_tt_enter_ct_kill(priv); } if (!(flags & CT_CARD_DISABLED)) iwl_tt_exit_ct_kill(priv); if (flags & HW_CARD_DISABLED) set_bit(STATUS_RF_KILL_HW, &priv->status); else clear_bit(STATUS_RF_KILL_HW, &priv->status); if (!(flags & RXON_CARD_DISABLED)) iwl_scan_cancel(priv); if ((test_bit(STATUS_RF_KILL_HW, &status) != test_bit(STATUS_RF_KILL_HW, &priv->status))) wiphy_rfkill_set_hw_state(priv->hw->wiphy, test_bit(STATUS_RF_KILL_HW, &priv->status)); else wake_up(&priv->trans->wait_command_queue); return 0; } static int iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_missed_beacon_notif *missed_beacon = (void *)pkt->data; if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > priv->missed_beacon_threshold) { IWL_DEBUG_CALIB(priv, "missed bcn cnsq %d totl %d rcd %d expctd %d\n", le32_to_cpu(missed_beacon->consecutive_missed_beacons), le32_to_cpu(missed_beacon->total_missed_becons), le32_to_cpu(missed_beacon->num_recvd_beacons), le32_to_cpu(missed_beacon->num_expected_beacons)); if (!test_bit(STATUS_SCANNING, &priv->status)) iwl_init_sensitivity(priv); } return 0; } /* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD). * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */ static int iwlagn_rx_reply_rx_phy(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); priv->last_phy_res_valid = true; priv->ampdu_ref++; memcpy(&priv->last_phy_res, pkt->data, sizeof(struct iwl_rx_phy_res)); return 0; } /* * returns non-zero if packet should be dropped */ static int iwlagn_set_decrypted_flag(struct iwl_priv *priv, struct ieee80211_hdr *hdr, u32 decrypt_res, struct ieee80211_rx_status *stats) { u16 fc = le16_to_cpu(hdr->frame_control); /* * All contexts have the same setting here due to it being * a module parameter, so OK to check any context. */ if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) return 0; if (!(fc & IEEE80211_FCTL_PROTECTED)) return 0; IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res); switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { case RX_RES_STATUS_SEC_TYPE_TKIP: /* The uCode has got a bad phase 1 Key, pushes the packet. * Decryption will be done in SW. */ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_BAD_KEY_TTAK) break; case RX_RES_STATUS_SEC_TYPE_WEP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_BAD_ICV_MIC) { /* bad ICV, the packet is destroyed since the * decryption is inplace, drop it */ IWL_DEBUG_RX(priv, "Packet destroyed\n"); return -1; } case RX_RES_STATUS_SEC_TYPE_CCMP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_DECRYPT_OK) { IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n"); stats->flag |= RX_FLAG_DECRYPTED; } break; default: break; } return 0; } static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, struct ieee80211_hdr *hdr, u16 len, u32 ampdu_status, struct iwl_rx_cmd_buffer *rxb, struct ieee80211_rx_status *stats) { struct sk_buff *skb; __le16 fc = hdr->frame_control; struct iwl_rxon_context *ctx; unsigned int hdrlen, fraglen; /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { IWL_DEBUG_DROP_LIMIT(priv, "Dropping packet while interface is not open.\n"); return; } /* In case of HW accelerated crypto and bad decryption, drop */ if (!iwlwifi_mod_params.sw_crypto && iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats)) return; /* Dont use dev_alloc_skb(), we'll have enough headroom once * ieee80211_hdr pulled. */ skb = alloc_skb(128, GFP_ATOMIC); if (!skb) { IWL_ERR(priv, "alloc_skb failed\n"); return; } /* If frame is small enough to fit in skb->head, pull it completely. * If not, only pull ieee80211_hdr so that splice() or TCP coalesce * are more efficient. */ hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr); memcpy(skb_put(skb, hdrlen), hdr, hdrlen); fraglen = len - hdrlen; if (fraglen) { int offset = (void *)hdr + hdrlen - rxb_addr(rxb) + rxb_offset(rxb); skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, fraglen, rxb->truesize); } /* * Wake any queues that were stopped due to a passive channel tx * failure. This can happen because the regulatory enforcement in * the device waits for a beacon before allowing transmission, * sometimes even after already having transmitted frames for the * association because the new RXON may reset the information. */ if (unlikely(ieee80211_is_beacon(fc) && priv->passive_no_rx)) { for_each_context(priv, ctx) { if (!ether_addr_equal(hdr->addr3, ctx->active.bssid_addr)) continue; iwlagn_lift_passive_no_rx(priv); } } memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); ieee80211_rx(priv->hw, skb); } static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) { u32 decrypt_out = 0; if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == RX_RES_STATUS_STATION_FOUND) decrypt_out |= (RX_RES_STATUS_STATION_FOUND | RX_RES_STATUS_NO_STATION_INFO_MISMATCH); decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); /* packet was not encrypted */ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == RX_RES_STATUS_SEC_TYPE_NONE) return decrypt_out; /* packet was encrypted with unknown alg */ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == RX_RES_STATUS_SEC_TYPE_ERR) return decrypt_out; /* decryption was not done in HW */ if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != RX_MPDU_RES_STATUS_DEC_DONE_MSK) return decrypt_out; switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { case RX_RES_STATUS_SEC_TYPE_CCMP: /* alg is CCM: check MIC only */ if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) /* Bad MIC */ decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; else decrypt_out |= RX_RES_STATUS_DECRYPT_OK; break; case RX_RES_STATUS_SEC_TYPE_TKIP: if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { /* Bad TTAK */ decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; break; } /* fall through if TTAK OK */ default: if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; else decrypt_out |= RX_RES_STATUS_DECRYPT_OK; break; } IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out); return decrypt_out; } /* Calc max signal level (dBm) among 3 possible receivers */ static int iwlagn_calc_rssi(struct iwl_priv *priv, struct iwl_rx_phy_res *rx_resp) { /* data from PHY/DSP regarding signal strength, etc., * contents are always there, not configurable by host */ struct iwlagn_non_cfg_phy *ncphy = (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf; u32 val, rssi_a, rssi_b, rssi_c, max_rssi; u8 agc; val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]); agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS; /* Find max rssi among 3 possible receivers. * These values are measured by the digital signal processor (DSP). * They should stay fairly constant even as the signal strength varies, * if the radio's automatic gain control (AGC) is working right. * AGC value (see below) will provide the "interesting" info. */ val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]); rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >> IWLAGN_OFDM_RSSI_A_BIT_POS; rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >> IWLAGN_OFDM_RSSI_B_BIT_POS; val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]); rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >> IWLAGN_OFDM_RSSI_C_BIT_POS; max_rssi = max_t(u32, rssi_a, rssi_b); max_rssi = max_t(u32, max_rssi, rssi_c); IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", rssi_a, rssi_b, rssi_c, max_rssi, agc); /* dBm = max_rssi dB - agc dB - constant. * Higher AGC (higher radio gain) means lower signal. */ return max_rssi - agc - IWLAGN_RSSI_OFFSET; } /* Called for REPLY_RX_MPDU_CMD */ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_phy_res *phy_res; __le32 rx_pkt_status; struct iwl_rx_mpdu_res_start *amsdu; u32 len; u32 ampdu_status; u32 rate_n_flags; if (!priv->last_phy_res_valid) { IWL_ERR(priv, "MPDU frame without cached PHY data\n"); return 0; } phy_res = &priv->last_phy_res; amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data; header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu)); len = le16_to_cpu(amsdu->byte_count); rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len); ampdu_status = iwlagn_translate_rx_status(priv, le32_to_cpu(rx_pkt_status)); if ((unlikely(phy_res->cfg_phy_cnt > 20))) { IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n", phy_res->cfg_phy_cnt); return 0; } if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status)); return 0; } /* This will be used in several places later */ rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); /* rx_status carries information about the packet to mac80211 */ rx_status.mactime = le64_to_cpu(phy_res->timestamp); rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; rx_status.freq = ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), rx_status.band); rx_status.rate_idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); rx_status.flag = 0; /* TSF isn't reliable. In order to allow smooth user experience, * this W/A doesn't propagate it to the mac80211 */ /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); /* Find max signal strength (dBm) among 3 antenna/receiver chains */ rx_status.signal = iwlagn_calc_rssi(priv, phy_res); IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n", rx_status.signal, (unsigned long long)rx_status.mactime); /* * "antenna number" * * It seems that the antenna field in the phy flags value * is actually a bit field. This is undefined by radiotap, * it wants an actual antenna number but I always get "7" * for most legacy frames I receive indicating that the * same frame was received on all three RX chains. * * I think this field should be removed in favor of a * new 802.11n radiotap field "RX chains" that is defined * as a bitmask. */ rx_status.antenna = (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> RX_RES_PHY_FLAGS_ANTENNA_POS; /* set the preamble flag if appropriate */ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) rx_status.flag |= RX_FLAG_SHORTPRE; if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { /* * We know which subframes of an A-MPDU belong * together since we get a single PHY response * from the firmware for all of them */ rx_status.flag |= RX_FLAG_AMPDU_DETAILS; rx_status.ampdu_reference = priv->ampdu_ref; } /* Set up the HT phy flags */ if (rate_n_flags & RATE_MCS_HT_MSK) rx_status.flag |= RX_FLAG_HT; if (rate_n_flags & RATE_MCS_HT40_MSK) rx_status.flag |= RX_FLAG_40MHZ; if (rate_n_flags & RATE_MCS_SGI_MSK) rx_status.flag |= RX_FLAG_SHORT_GI; if (rate_n_flags & RATE_MCS_GF_MSK) rx_status.flag |= RX_FLAG_HT_GF; iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status, rxb, &rx_status); return 0; } static int iwlagn_rx_noa_notification(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_wipan_noa_data *new_data, *old_data; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->data; /* no condition -- we're in softirq */ old_data = rcu_dereference_protected(priv->noa_data, true); if (noa_notif->noa_active) { u32 len = le16_to_cpu(noa_notif->noa_attribute.length); u32 copylen = len; /* EID, len, OUI, subtype */ len += 1 + 1 + 3 + 1; /* P2P id, P2P length */ len += 1 + 2; copylen += 1 + 2; new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC); if (new_data) { new_data->length = len; new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC; new_data->data[1] = len - 2; /* not counting EID, len */ new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff; new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff; new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff; new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P; memcpy(&new_data->data[6], &noa_notif->noa_attribute, copylen); } } else new_data = NULL; rcu_assign_pointer(priv->noa_data, new_data); if (old_data) kfree_rcu(old_data, rcu_head); return 0; } /** * iwl_setup_rx_handlers - Initialize Rx handler callbacks * * Setup the RX handlers for each of the reply types sent from the uCode * to the host. */ void iwl_setup_rx_handlers(struct iwl_priv *priv) { int (**handlers)(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); handlers = priv->rx_handlers; handlers[REPLY_ERROR] = iwlagn_rx_reply_error; handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa; handlers[SPECTRUM_MEASURE_NOTIFICATION] = iwlagn_rx_spectrum_measure_notif; handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif; handlers[PM_DEBUG_STATISTIC_NOTIFIC] = iwlagn_rx_pm_debug_statistics_notif; handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif; handlers[REPLY_ADD_STA] = iwl_add_sta_callback; handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification; /* * The same handler is used for both the REPLY to a discrete * statistics request from the host as well as for the periodic * statistics notifications (after received beacons) from the uCode. */ handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics; handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics; iwl_setup_rx_scan_handlers(priv); handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif; handlers[MISSED_BEACONS_NOTIFICATION] = iwlagn_rx_missed_beacon_notif; /* Rx handlers */ handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy; handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx; /* block ack */ handlers[REPLY_COMPRESSED_BA] = iwlagn_rx_reply_compressed_ba; priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; /* set up notification wait support */ iwl_notification_wait_init(&priv->notif_wait); /* Set up BT Rx handlers */ if (priv->cfg->bt_params) iwlagn_bt_rx_handler_setup(priv); } int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); int err = 0; /* * Do the notification wait before RX handlers so * even if the RX handler consumes the RXB we have * access to it in the notification wait entry. */ iwl_notification_wait_notify(&priv->notif_wait, pkt); #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE /* * RX data may be forwarded to userspace in one * of two cases: the user owns the fw through testmode or when * the user requested to monitor the rx w/o affecting the regular flow. * In these cases the iwl_test object will handle forwarding the rx * data to user space. * Note that if the ownership flag != IWL_OWNERSHIP_TM the flow * continues. */ iwl_test_rx(&priv->tst, rxb); #endif if (priv->ucode_owner != IWL_OWNERSHIP_TM) { /* Based on type of command response or notification, * handle those that need handling via function in * rx_handlers table. See iwl_setup_rx_handlers() */ if (priv->rx_handlers[pkt->hdr.cmd]) { priv->rx_handlers_stats[pkt->hdr.cmd]++; err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd); } else { /* No handling needed */ IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", iwl_dvm_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); } } return err; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/rs.h0000644000175000017500000003277612026211315023535 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __iwl_agn_rs_h__ #define __iwl_agn_rs_h__ #include #include "iwl-config.h" #include "commands.h" struct iwl_rate_info { u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ u8 prev_ieee; /* previous rate in IEEE speeds */ u8 next_ieee; /* next rate in IEEE speeds */ u8 prev_rs; /* previous rate used in rs algo */ u8 next_rs; /* next rate used in rs algo */ u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ u8 next_rs_tgg; /* next rate used in TGG rs algo */ }; /* * These serve as indexes into * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; */ enum { IWL_RATE_1M_INDEX = 0, IWL_RATE_2M_INDEX, IWL_RATE_5M_INDEX, IWL_RATE_11M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX, IWL_RATE_60M_INDEX, IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/ IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */ IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, IWL_RATE_INVALID = IWL_RATE_COUNT, }; enum { IWL_RATE_6M_INDEX_TABLE = 0, IWL_RATE_9M_INDEX_TABLE, IWL_RATE_12M_INDEX_TABLE, IWL_RATE_18M_INDEX_TABLE, IWL_RATE_24M_INDEX_TABLE, IWL_RATE_36M_INDEX_TABLE, IWL_RATE_48M_INDEX_TABLE, IWL_RATE_54M_INDEX_TABLE, IWL_RATE_1M_INDEX_TABLE, IWL_RATE_2M_INDEX_TABLE, IWL_RATE_5M_INDEX_TABLE, IWL_RATE_11M_INDEX_TABLE, IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, }; enum { IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, }; /* #define vs. enum to keep from defaulting to 'large integer' */ #define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) #define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) #define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) #define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) #define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) #define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) #define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) #define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) #define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) #define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) #define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) #define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) #define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) /* uCode API values for legacy bit rates, both OFDM and CCK */ enum { IWL_RATE_6M_PLCP = 13, IWL_RATE_9M_PLCP = 15, IWL_RATE_12M_PLCP = 5, IWL_RATE_18M_PLCP = 7, IWL_RATE_24M_PLCP = 9, IWL_RATE_36M_PLCP = 11, IWL_RATE_48M_PLCP = 1, IWL_RATE_54M_PLCP = 3, IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ IWL_RATE_1M_PLCP = 10, IWL_RATE_2M_PLCP = 20, IWL_RATE_5M_PLCP = 55, IWL_RATE_11M_PLCP = 110, /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */ /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ }; /* uCode API values for OFDM high-throughput (HT) bit rates */ enum { IWL_RATE_SISO_6M_PLCP = 0, IWL_RATE_SISO_12M_PLCP = 1, IWL_RATE_SISO_18M_PLCP = 2, IWL_RATE_SISO_24M_PLCP = 3, IWL_RATE_SISO_36M_PLCP = 4, IWL_RATE_SISO_48M_PLCP = 5, IWL_RATE_SISO_54M_PLCP = 6, IWL_RATE_SISO_60M_PLCP = 7, IWL_RATE_MIMO2_6M_PLCP = 0x8, IWL_RATE_MIMO2_12M_PLCP = 0x9, IWL_RATE_MIMO2_18M_PLCP = 0xa, IWL_RATE_MIMO2_24M_PLCP = 0xb, IWL_RATE_MIMO2_36M_PLCP = 0xc, IWL_RATE_MIMO2_48M_PLCP = 0xd, IWL_RATE_MIMO2_54M_PLCP = 0xe, IWL_RATE_MIMO2_60M_PLCP = 0xf, IWL_RATE_MIMO3_6M_PLCP = 0x10, IWL_RATE_MIMO3_12M_PLCP = 0x11, IWL_RATE_MIMO3_18M_PLCP = 0x12, IWL_RATE_MIMO3_24M_PLCP = 0x13, IWL_RATE_MIMO3_36M_PLCP = 0x14, IWL_RATE_MIMO3_48M_PLCP = 0x15, IWL_RATE_MIMO3_54M_PLCP = 0x16, IWL_RATE_MIMO3_60M_PLCP = 0x17, IWL_RATE_SISO_INVM_PLCP, IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, }; /* MAC header values for bit rates */ enum { IWL_RATE_6M_IEEE = 12, IWL_RATE_9M_IEEE = 18, IWL_RATE_12M_IEEE = 24, IWL_RATE_18M_IEEE = 36, IWL_RATE_24M_IEEE = 48, IWL_RATE_36M_IEEE = 72, IWL_RATE_48M_IEEE = 96, IWL_RATE_54M_IEEE = 108, IWL_RATE_60M_IEEE = 120, IWL_RATE_1M_IEEE = 2, IWL_RATE_2M_IEEE = 4, IWL_RATE_5M_IEEE = 11, IWL_RATE_11M_IEEE = 22, }; #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) #define IWL_INVALID_VALUE -1 #define IWL_MIN_RSSI_VAL -100 #define IWL_MAX_RSSI_VAL 0 /* These values specify how many Tx frame attempts before * searching for a new modulation mode */ #define IWL_LEGACY_FAILURE_LIMIT 160 #define IWL_LEGACY_SUCCESS_LIMIT 480 #define IWL_LEGACY_TABLE_COUNT 160 #define IWL_NONE_LEGACY_FAILURE_LIMIT 400 #define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 #define IWL_NONE_LEGACY_TABLE_COUNT 1500 /* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ #define IWL_RS_GOOD_RATIO 12800 /* 100% */ #define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ #define IWL_RATE_HIGH_TH 10880 /* 85% */ #define IWL_RATE_INCREASE_TH 6400 /* 50% */ #define IWL_RATE_DECREASE_TH 1920 /* 15% */ /* possible actions when in legacy mode */ #define IWL_LEGACY_SWITCH_ANTENNA1 0 #define IWL_LEGACY_SWITCH_ANTENNA2 1 #define IWL_LEGACY_SWITCH_SISO 2 #define IWL_LEGACY_SWITCH_MIMO2_AB 3 #define IWL_LEGACY_SWITCH_MIMO2_AC 4 #define IWL_LEGACY_SWITCH_MIMO2_BC 5 #define IWL_LEGACY_SWITCH_MIMO3_ABC 6 /* possible actions when in siso mode */ #define IWL_SISO_SWITCH_ANTENNA1 0 #define IWL_SISO_SWITCH_ANTENNA2 1 #define IWL_SISO_SWITCH_MIMO2_AB 2 #define IWL_SISO_SWITCH_MIMO2_AC 3 #define IWL_SISO_SWITCH_MIMO2_BC 4 #define IWL_SISO_SWITCH_GI 5 #define IWL_SISO_SWITCH_MIMO3_ABC 6 /* possible actions when in mimo mode */ #define IWL_MIMO2_SWITCH_ANTENNA1 0 #define IWL_MIMO2_SWITCH_ANTENNA2 1 #define IWL_MIMO2_SWITCH_SISO_A 2 #define IWL_MIMO2_SWITCH_SISO_B 3 #define IWL_MIMO2_SWITCH_SISO_C 4 #define IWL_MIMO2_SWITCH_GI 5 #define IWL_MIMO2_SWITCH_MIMO3_ABC 6 /* possible actions when in mimo3 mode */ #define IWL_MIMO3_SWITCH_ANTENNA1 0 #define IWL_MIMO3_SWITCH_ANTENNA2 1 #define IWL_MIMO3_SWITCH_SISO_A 2 #define IWL_MIMO3_SWITCH_SISO_B 3 #define IWL_MIMO3_SWITCH_SISO_C 4 #define IWL_MIMO3_SWITCH_MIMO2_AB 5 #define IWL_MIMO3_SWITCH_MIMO2_AC 6 #define IWL_MIMO3_SWITCH_MIMO2_BC 7 #define IWL_MIMO3_SWITCH_GI 8 #define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI #define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC /*FIXME:RS:add possible actions for MIMO3*/ #define IWL_ACTION_LIMIT 3 /* # possible actions */ #define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ /* load per tid defines for A-MPDU activation */ #define IWL_AGG_TPT_THREHOLD 0 #define IWL_AGG_LOAD_THRESHOLD 10 #define IWL_AGG_ALL_TID 0xff #define TID_QUEUE_CELL_SPACING 50 /*mS */ #define TID_QUEUE_MAX_SIZE 20 #define TID_ROUND_VALUE 5 /* mS */ #define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) #define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; enum iwl_table_type { LQ_NONE, LQ_G, /* legacy types */ LQ_A, LQ_SISO, /* high-throughput types */ LQ_MIMO2, LQ_MIMO3, LQ_MAX, }; #define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) #define is_siso(tbl) ((tbl) == LQ_SISO) #define is_mimo2(tbl) ((tbl) == LQ_MIMO2) #define is_mimo3(tbl) ((tbl) == LQ_MIMO3) #define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) #define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) #define is_a_band(tbl) ((tbl) == LQ_A) #define is_g_and(tbl) ((tbl) == LQ_G) #define IWL_MAX_MCS_DISPLAY_SIZE 12 struct iwl_rate_mcs_info { char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; }; /** * struct iwl_rate_scale_data -- tx success history for one rate */ struct iwl_rate_scale_data { u64 data; /* bitmap of successful frames */ s32 success_counter; /* number of frames successful */ s32 success_ratio; /* per-cent * 128 */ s32 counter; /* number of frames attempted */ s32 average_tpt; /* success ratio * expected throughput */ unsigned long stamp; }; /** * struct iwl_scale_tbl_info -- tx params and success history for all rates * * There are two of these in struct iwl_lq_sta, * one for "active", and one for "search". */ struct iwl_scale_tbl_info { enum iwl_table_type lq_type; u8 ant_type; u8 is_SGI; /* 1 = short guard interval */ u8 is_ht40; /* 1 = 40 MHz channel width */ u8 is_dup; /* 1 = duplicated data streams */ u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ u8 max_search; /* maximun number of tables we can search */ s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ u32 current_rate; /* rate_n_flags, uCode API format */ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ }; struct iwl_traffic_load { unsigned long time_stamp; /* age of the oldest statistics */ u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time * slice */ u32 total; /* total num of packets during the * last TID_MAX_TIME_DIFF */ u8 queue_count; /* number of queues that has * been used since the last cleanup */ u8 head; /* start of the circular buffer */ }; /** * struct iwl_lq_sta -- driver's rate scaling private structure * * Pointer to this gets passed back and forth between driver and mac80211. */ struct iwl_lq_sta { u8 active_tbl; /* index of active table, range 0-1 */ u8 enable_counter; /* indicates HT mode */ u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ u8 search_better_tbl; /* 1: currently trying alternate mode */ s32 last_tpt; /* The following determine when to search for a new mode */ u32 table_count_limit; u32 max_failure_limit; /* # failed frames before new search */ u32 max_success_limit; /* # successful frames before new search */ u32 table_count; u32 total_failed; /* total failed frames, any/all rates */ u32 total_success; /* total successful frames, any/all rates */ u64 flush_timer; /* time staying in mode before new search */ u8 action_counter; /* # mode-switch actions tried */ u8 is_green; u8 is_dup; enum ieee80211_band band; /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ u32 supp_rates; u16 active_legacy_rate; u16 active_siso_rate; u16 active_mimo2_rate; u16 active_mimo3_rate; s8 max_rate_idx; /* Max rate set by user */ u8 missed_rate_counter; struct iwl_link_quality_cmd lq; struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ struct iwl_traffic_load load[IWL_MAX_TID_COUNT]; u8 tx_agg_tid_en; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *rs_sta_dbgfs_scale_table_file; struct dentry *rs_sta_dbgfs_stats_table_file; struct dentry *rs_sta_dbgfs_rate_scale_data_file; struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; u32 dbg_fixed_rate; #endif struct iwl_priv *drv; /* used to be in sta_info */ int last_txrate_idx; /* last tx rate_n_flags */ u32 last_rate_n_flags; /* packets destined for this STA are aggregated */ u8 is_agg; /* BT traffic this sta was last updated in */ u8 last_bt_traffic; }; static inline u8 num_of_ant(u8 mask) { return !!((mask) & ANT_A) + !!((mask) & ANT_B) + !!((mask) & ANT_C); } static inline u8 first_antenna(u8 mask) { if (mask & ANT_A) return ANT_A; if (mask & ANT_B) return ANT_B; return ANT_C; } /* Initialize station's rate scaling information after adding station */ extern void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id); /** * iwl_rate_control_register - Register the rate control algorithm callbacks * * Since the rate control algorithm is hardware specific, there is no need * or reason to place it as a stand alone module. The driver can call * iwl_rate_control_register in order to register the rate control callbacks * with the mac80211 subsystem. This should be performed prior to calling * ieee80211_register_hw * */ extern int iwlagn_rate_control_register(void); /** * iwl_rate_control_unregister - Unregister the rate control callbacks * * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ extern void iwlagn_rate_control_unregister(void); #endif /* __iwl_agn__rs__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/rs.c0000644000175000017500000030643612026211315023525 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "dev.h" #include "agn.h" #define RS_NAME "iwl-agn-rs" #define NUM_TRY_BEFORE_ANT_TOGGLE 1 #define IWL_NUMBER_TRY 1 #define IWL_HT_NUMBER_TRY 3 #define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ #define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ #define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ /* max allowed rate miss before sync LQ cmd */ #define IWL_MISSED_RATE_MAX 15 /* max time to accum history 2 seconds */ #define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) static u8 rs_ht_to_legacy[] = { IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX }; static const u8 ant_toggle_lookup[] = { /*ANT_NONE -> */ ANT_NONE, /*ANT_A -> */ ANT_B, /*ANT_B -> */ ANT_C, /*ANT_AB -> */ ANT_BC, /*ANT_C -> */ ANT_A, /*ANT_AC -> */ ANT_AB, /*ANT_BC -> */ ANT_AC, /*ANT_ABC -> */ ANT_ABC, }; #define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ IWL_RATE_SISO_##s##M_PLCP, \ IWL_RATE_MIMO2_##s##M_PLCP,\ IWL_RATE_MIMO3_##s##M_PLCP,\ IWL_RATE_##r##M_IEEE, \ IWL_RATE_##ip##M_INDEX, \ IWL_RATE_##in##M_INDEX, \ IWL_RATE_##rp##M_INDEX, \ IWL_RATE_##rn##M_INDEX, \ IWL_RATE_##pp##M_INDEX, \ IWL_RATE_##np##M_INDEX } /* * Parameter order: * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate * * If there isn't a valid next or previous rate then INV is used which * maps to IWL_RATE_INVALID * */ const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ /* FIXME:RS: ^^ should be INV (legacy) */ }; static inline u8 rs_extract_rate(u32 rate_n_flags) { return (u8)(rate_n_flags & RATE_MCS_RATE_MSK); } static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) { int idx = 0; /* HT rate format */ if (rate_n_flags & RATE_MCS_HT_MSK) { idx = rs_extract_rate(rate_n_flags); if (idx >= IWL_RATE_MIMO3_6M_PLCP) idx = idx - IWL_RATE_MIMO3_6M_PLCP; else if (idx >= IWL_RATE_MIMO2_6M_PLCP) idx = idx - IWL_RATE_MIMO2_6M_PLCP; idx += IWL_FIRST_OFDM_RATE; /* skip 9M not supported in ht*/ if (idx >= IWL_RATE_9M_INDEX) idx += 1; if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) return idx; /* legacy rate format, search for match in table */ } else { for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) if (iwl_rates[idx].plcp == rs_extract_rate(rate_n_flags)) return idx; } return -1; } static void rs_rate_scale_perform(struct iwl_priv *priv, struct sk_buff *skb, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta); static void rs_fill_link_cmd(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, u32 rate_n_flags); static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); #ifdef CONFIG_MAC80211_DEBUGFS static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, u32 *rate_n_flags, int index); #else static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, u32 *rate_n_flags, int index) {} #endif /** * The following tables contain the expected throughput metrics for all rates * * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits * * where invalid entries are zeros. * * CCK rates are only valid in legacy table and will only be used in G * (2.4 GHz) band. */ static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 }; static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ }; static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ }; static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ }; static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ }; static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ }; static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ }; /* mbps, mcs */ static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { { "1", "BPSK DSSS"}, { "2", "QPSK DSSS"}, {"5.5", "BPSK CCK"}, { "11", "QPSK CCK"}, { "6", "BPSK 1/2"}, { "9", "BPSK 1/2"}, { "12", "QPSK 1/2"}, { "18", "QPSK 3/4"}, { "24", "16QAM 1/2"}, { "36", "16QAM 3/4"}, { "48", "64QAM 2/3"}, { "54", "64QAM 3/4"}, { "60", "64QAM 5/6"}, }; #define MCS_INDEX_PER_STREAM (8) static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) { window->data = 0; window->success_counter = 0; window->success_ratio = IWL_INVALID_VALUE; window->counter = 0; window->average_tpt = IWL_INVALID_VALUE; window->stamp = 0; } static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) { return (ant_type & valid_antenna) == ant_type; } /* * removes the old data from the statistics. All data that is older than * TID_MAX_TIME_DIFF, will be deleted. */ static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) { /* The oldest age we want to keep */ u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; while (tl->queue_count && (tl->time_stamp < oldest_time)) { tl->total -= tl->packet_count[tl->head]; tl->packet_count[tl->head] = 0; tl->time_stamp += TID_QUEUE_CELL_SPACING; tl->queue_count--; tl->head++; if (tl->head >= TID_QUEUE_MAX_SIZE) tl->head = 0; } } /* * increment traffic load value for tid and also remove * any old values if passed the certain time period */ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, struct ieee80211_hdr *hdr) { u32 curr_time = jiffies_to_msecs(jiffies); u32 time_diff; s32 index; struct iwl_traffic_load *tl = NULL; u8 tid; if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } else return IWL_MAX_TID_COUNT; if (unlikely(tid >= IWL_MAX_TID_COUNT)) return IWL_MAX_TID_COUNT; tl = &lq_data->load[tid]; curr_time -= curr_time % TID_ROUND_VALUE; /* Happens only for the first packet. Initialize the data */ if (!(tl->queue_count)) { tl->total = 1; tl->time_stamp = curr_time; tl->queue_count = 1; tl->head = 0; tl->packet_count[0] = 1; return IWL_MAX_TID_COUNT; } time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); index = time_diff / TID_QUEUE_CELL_SPACING; /* The history is too long: remove data that is older than */ /* TID_MAX_TIME_DIFF */ if (index >= TID_QUEUE_MAX_SIZE) rs_tl_rm_old_stats(tl, curr_time); index = (tl->head + index) % TID_QUEUE_MAX_SIZE; tl->packet_count[index] = tl->packet_count[index] + 1; tl->total = tl->total + 1; if ((index + 1) > tl->queue_count) tl->queue_count = index + 1; return tid; } #ifdef CONFIG_MAC80211_DEBUGFS /** * Program the device to use fixed rate for frame transmit * This is for debugging/testing only * once the device start use fixed rate, we need to reload the module * to being back the normal operation. */ static void rs_program_fix_rate(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta) { struct iwl_station_priv *sta_priv = container_of(lq_sta, struct iwl_station_priv, lq_sta); struct iwl_rxon_context *ctx = sta_priv->ctx; lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE /* testmode has higher priority to overwirte the fixed rate */ if (priv->tm_fixed_rate) lq_sta->dbg_fixed_rate = priv->tm_fixed_rate; #endif IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); if (lq_sta->dbg_fixed_rate) { rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC, false); } } #endif /* get the traffic load value for tid */ static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) { u32 curr_time = jiffies_to_msecs(jiffies); u32 time_diff; s32 index; struct iwl_traffic_load *tl = NULL; if (tid >= IWL_MAX_TID_COUNT) return 0; tl = &(lq_data->load[tid]); curr_time -= curr_time % TID_ROUND_VALUE; if (!(tl->queue_count)) return 0; time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); index = time_diff / TID_QUEUE_CELL_SPACING; /* The history is too long: remove data that is older than */ /* TID_MAX_TIME_DIFF */ if (index >= TID_QUEUE_MAX_SIZE) rs_tl_rm_old_stats(tl, curr_time); return tl->total; } static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, struct iwl_lq_sta *lq_data, u8 tid, struct ieee80211_sta *sta) { int ret = -EAGAIN; u32 load; /* * Don't create TX aggregation sessions when in high * BT traffic, as they would just be disrupted by BT. */ if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) { IWL_ERR(priv, "BT traffic (%d), no aggregation allowed\n", priv->bt_traffic_load); return ret; } load = rs_tl_get_load(lq_data, tid); if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) { IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); ret = ieee80211_start_tx_ba_session(sta, tid, 5000); if (ret == -EAGAIN) { /* * driver and mac80211 is out of sync * this might be cause by reloading firmware * stop the tx ba session here */ IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", tid); ieee80211_stop_tx_ba_session(sta, tid); } } else { IWL_DEBUG_HT(priv, "Aggregation not enabled for tid %d " "because load = %u\n", tid, load); } return ret; } static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, struct iwl_lq_sta *lq_data, struct ieee80211_sta *sta) { if (tid < IWL_MAX_TID_COUNT) rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); else IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n", tid, IWL_MAX_TID_COUNT); } static inline int get_num_of_ant_from_rate(u32 rate_n_flags) { return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); } /* * Static function to get the expected throughput from an iwl_scale_tbl_info * that wraps a NULL pointer check */ static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) { if (tbl->expected_tpt) return tbl->expected_tpt[rs_index]; return 0; } /** * rs_collect_tx_data - Update the success/failure sliding window * * We keep a sliding window of the last 62 packets transmitted * at this rate. window->data contains the bitmask of successful * packets. */ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, int scale_index, int attempts, int successes) { struct iwl_rate_scale_data *window = NULL; static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); s32 fail_count, tpt; if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) return -EINVAL; /* Select window for current tx bit rate */ window = &(tbl->win[scale_index]); /* Get expected throughput */ tpt = get_expected_tpt(tbl, scale_index); /* * Keep track of only the latest 62 tx frame attempts in this rate's * history window; anything older isn't really relevant any more. * If we have filled up the sliding window, drop the oldest attempt; * if the oldest attempt (highest bit in bitmap) shows "success", * subtract "1" from the success counter (this is the main reason * we keep these bitmaps!). */ while (attempts > 0) { if (window->counter >= IWL_RATE_MAX_WINDOW) { /* remove earliest */ window->counter = IWL_RATE_MAX_WINDOW - 1; if (window->data & mask) { window->data &= ~mask; window->success_counter--; } } /* Increment frames-attempted counter */ window->counter++; /* Shift bitmap by one frame to throw away oldest history */ window->data <<= 1; /* Mark the most recent #successes attempts as successful */ if (successes > 0) { window->success_counter++; window->data |= 0x1; successes--; } attempts--; } /* Calculate current success ratio, avoid divide-by-0! */ if (window->counter > 0) window->success_ratio = 128 * (100 * window->success_counter) / window->counter; else window->success_ratio = IWL_INVALID_VALUE; fail_count = window->counter - window->success_counter; /* Calculate average throughput, if we have enough history. */ if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) window->average_tpt = (window->success_ratio * tpt + 64) / 128; else window->average_tpt = IWL_INVALID_VALUE; /* Tag this window as having been updated */ window->stamp = jiffies; return 0; } /* * Fill uCode API rate_n_flags field, based on "search" or "active" table. */ /* FIXME:RS:remove this function and put the flags statically in the table */ static u32 rate_n_flags_from_tbl(struct iwl_priv *priv, struct iwl_scale_tbl_info *tbl, int index, u8 use_green) { u32 rate_n_flags = 0; if (is_legacy(tbl->lq_type)) { rate_n_flags = iwl_rates[index].plcp; if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) rate_n_flags |= RATE_MCS_CCK_MSK; } else if (is_Ht(tbl->lq_type)) { if (index > IWL_LAST_OFDM_RATE) { IWL_ERR(priv, "Invalid HT rate index %d\n", index); index = IWL_LAST_OFDM_RATE; } rate_n_flags = RATE_MCS_HT_MSK; if (is_siso(tbl->lq_type)) rate_n_flags |= iwl_rates[index].plcp_siso; else if (is_mimo2(tbl->lq_type)) rate_n_flags |= iwl_rates[index].plcp_mimo2; else rate_n_flags |= iwl_rates[index].plcp_mimo3; } else { IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type); } rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & RATE_MCS_ANT_ABC_MSK); if (is_Ht(tbl->lq_type)) { if (tbl->is_ht40) { if (tbl->is_dup) rate_n_flags |= RATE_MCS_DUP_MSK; else rate_n_flags |= RATE_MCS_HT40_MSK; } if (tbl->is_SGI) rate_n_flags |= RATE_MCS_SGI_MSK; if (use_green) { rate_n_flags |= RATE_MCS_GF_MSK; if (is_siso(tbl->lq_type) && tbl->is_SGI) { rate_n_flags &= ~RATE_MCS_SGI_MSK; IWL_ERR(priv, "GF was set with SGI:SISO\n"); } } } return rate_n_flags; } /* * Interpret uCode API's rate_n_flags format, * fill "search" or "active" tx mode table. */ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, enum ieee80211_band band, struct iwl_scale_tbl_info *tbl, int *rate_idx) { u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); u8 mcs; memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); if (*rate_idx == IWL_RATE_INVALID) { *rate_idx = -1; return -EINVAL; } tbl->is_SGI = 0; /* default legacy setup */ tbl->is_ht40 = 0; tbl->is_dup = 0; tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); tbl->lq_type = LQ_NONE; tbl->max_search = IWL_MAX_SEARCH; /* legacy rate format */ if (!(rate_n_flags & RATE_MCS_HT_MSK)) { if (num_of_ant == 1) { if (band == IEEE80211_BAND_5GHZ) tbl->lq_type = LQ_A; else tbl->lq_type = LQ_G; } /* HT rate format */ } else { if (rate_n_flags & RATE_MCS_SGI_MSK) tbl->is_SGI = 1; if ((rate_n_flags & RATE_MCS_HT40_MSK) || (rate_n_flags & RATE_MCS_DUP_MSK)) tbl->is_ht40 = 1; if (rate_n_flags & RATE_MCS_DUP_MSK) tbl->is_dup = 1; mcs = rs_extract_rate(rate_n_flags); /* SISO */ if (mcs <= IWL_RATE_SISO_60M_PLCP) { if (num_of_ant == 1) tbl->lq_type = LQ_SISO; /*else NONE*/ /* MIMO2 */ } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { if (num_of_ant == 2) tbl->lq_type = LQ_MIMO2; /* MIMO3 */ } else { if (num_of_ant == 3) { tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; tbl->lq_type = LQ_MIMO3; } } } return 0; } /* switch to another antenna/antennas and return 1 */ /* if no other valid antenna found, return 0 */ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, struct iwl_scale_tbl_info *tbl) { u8 new_ant_type; if (!tbl->ant_type || tbl->ant_type > ANT_ABC) return 0; if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) return 0; new_ant_type = ant_toggle_lookup[tbl->ant_type]; while ((new_ant_type != tbl->ant_type) && !rs_is_valid_ant(valid_ant, new_ant_type)) new_ant_type = ant_toggle_lookup[new_ant_type]; if (new_ant_type == tbl->ant_type) return 0; tbl->ant_type = new_ant_type; *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; return 1; } /** * Green-field mode is valid if the station supports it and * there are no non-GF stations present in the BSS. */ static bool rs_use_green(struct ieee80211_sta *sta) { /* * There's a bug somewhere in this code that causes the * scaling to get stuck because GF+SGI can't be combined * in SISO rates. Until we find that bug, disable GF, it * has only limited benefit and we still interoperate with * GF APs since we can always receive GF transmissions. */ return false; } /** * rs_get_supported_rates - get the available rates * * if management frame or broadcast frame only return * basic available rates. * */ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, struct ieee80211_hdr *hdr, enum iwl_table_type rate_type) { if (is_legacy(rate_type)) { return lq_sta->active_legacy_rate; } else { if (is_siso(rate_type)) return lq_sta->active_siso_rate; else if (is_mimo2(rate_type)) return lq_sta->active_mimo2_rate; else return lq_sta->active_mimo3_rate; } } static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, int rate_type) { u8 high = IWL_RATE_INVALID; u8 low = IWL_RATE_INVALID; /* 802.11A or ht walks to the next literal adjacent rate in * the rate table */ if (is_a_band(rate_type) || !is_legacy(rate_type)) { int i; u32 mask; /* Find the previous rate that is in the rate mask */ i = index - 1; for (mask = (1 << i); i >= 0; i--, mask >>= 1) { if (rate_mask & mask) { low = i; break; } } /* Find the next rate that is in the rate mask */ i = index + 1; for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { if (rate_mask & mask) { high = i; break; } } return (high << 8) | low; } low = index; while (low != IWL_RATE_INVALID) { low = iwl_rates[low].prev_rs; if (low == IWL_RATE_INVALID) break; if (rate_mask & (1 << low)) break; IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low); } high = index; while (high != IWL_RATE_INVALID) { high = iwl_rates[high].next_rs; if (high == IWL_RATE_INVALID) break; if (rate_mask & (1 << high)) break; IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high); } return (high << 8) | low; } static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, u8 scale_index, u8 ht_possible) { s32 low; u16 rate_mask; u16 high_low; u8 switch_to_legacy = 0; u8 is_green = lq_sta->is_green; struct iwl_priv *priv = lq_sta->drv; /* check if we need to switch from HT to legacy rates. * assumption is that mandatory rates (1Mbps or 6Mbps) * are always supported (spec demand) */ if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { switch_to_legacy = 1; scale_index = rs_ht_to_legacy[scale_index]; if (lq_sta->band == IEEE80211_BAND_5GHZ) tbl->lq_type = LQ_A; else tbl->lq_type = LQ_G; if (num_of_ant(tbl->ant_type) > 1) tbl->ant_type = first_antenna(priv->eeprom_data->valid_tx_ant); tbl->is_ht40 = 0; tbl->is_SGI = 0; tbl->max_search = IWL_MAX_SEARCH; } rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); /* Mask with station rate restriction */ if (is_legacy(tbl->lq_type)) { /* supp_rates has no CCK bits in A mode */ if (lq_sta->band == IEEE80211_BAND_5GHZ) rate_mask = (u16)(rate_mask & (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); else rate_mask = (u16)(rate_mask & lq_sta->supp_rates); } /* If we switched from HT to legacy, check current rate */ if (switch_to_legacy && (rate_mask & (1 << scale_index))) { low = scale_index; goto out; } high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, tbl->lq_type); low = high_low & 0xff; if (low == IWL_RATE_INVALID) low = scale_index; out: return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); } /* * Simple function to compare two rate scale table types */ static bool table_type_matches(struct iwl_scale_tbl_info *a, struct iwl_scale_tbl_info *b) { return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && (a->is_SGI == b->is_SGI); } static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_lq_sta *lq_sta) { struct iwl_scale_tbl_info *tbl; bool full_concurrent = priv->bt_full_concurrent; if (priv->bt_ant_couple_ok) { /* * Is there a need to switch between * full concurrency and 3-wire? */ if (priv->bt_ci_compliance && priv->bt_ant_couple_ok) full_concurrent = true; else full_concurrent = false; } if ((priv->bt_traffic_load != priv->last_bt_traffic_load) || (priv->bt_full_concurrent != full_concurrent)) { priv->bt_full_concurrent = full_concurrent; priv->last_bt_traffic_load = priv->bt_traffic_load; /* Update uCode's rate table. */ tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); queue_work(priv->workqueue, &priv->bt_full_concurrency); } } /* * mac80211 sends us Tx status */ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { int legacy_success; int retries; int rs_index, mac_index, i; struct iwl_lq_sta *lq_sta = priv_sta; struct iwl_link_quality_cmd *table; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r; struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); enum mac80211_rate_control_flags mac_flags; u32 tx_rate; struct iwl_scale_tbl_info tbl_type; struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; struct iwl_rxon_context *ctx = sta_priv->ctx; IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); /* Treat uninitialized rate scaling data same as non-existing. */ if (!lq_sta) { IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); return; } else if (!lq_sta->drv) { IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); return; } if (!ieee80211_is_data(hdr->frame_control) || info->flags & IEEE80211_TX_CTL_NO_ACK) return; /* This packet was aggregated but doesn't carry status info */ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && !(info->flags & IEEE80211_TX_STAT_AMPDU)) return; /* * Ignore this Tx frame response if its initial rate doesn't match * that of latest Link Quality command. There may be stragglers * from a previous Link Quality command, but we're no longer interested * in those; they're either from the "active" mode while we're trying * to check "search" mode, or a prior "search" mode after we've moved * to a new "search" mode (which might become the new "active" mode). */ table = &lq_sta->lq; tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); if (priv->band == IEEE80211_BAND_5GHZ) rs_index -= IWL_FIRST_OFDM_RATE; mac_flags = info->status.rates[0].flags; mac_index = info->status.rates[0].idx; /* For HT packets, map MCS to PLCP */ if (mac_flags & IEEE80211_TX_RC_MCS) { mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) mac_index++; /* * mac80211 HT index is always zero-indexed; we need to move * HT OFDM rates after CCK rates in 2.4 GHz band */ if (priv->band == IEEE80211_BAND_2GHZ) mac_index += IWL_FIRST_OFDM_RATE; } /* Here we actually compare this rate to the latest LQ command */ if ((mac_index < 0) || (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) || (tbl_type.ant_type != info->status.antenna) || (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) || (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || (rs_index != mac_index)) { IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate); /* * Since rates mis-match, the last LQ command may have failed. * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with * ... driver. */ lq_sta->missed_rate_counter++; if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); } /* Regardless, ignore this status info for outdated rate */ return; } else /* Rate did match, so reset the missed_rate_counter */ lq_sta->missed_rate_counter = 0; /* Figure out if rate scale algorithm is in active or search table */ if (table_type_matches(&tbl_type, &(lq_sta->lq_info[lq_sta->active_tbl]))) { curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); } else if (table_type_matches(&tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) { curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); } else { IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n", tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); /* * no matching table found, let's by-pass the data collection * and continue to perform rate scale to find the rate table */ rs_stay_in_table(lq_sta, true); goto done; } /* * Updating the frame history depends on whether packets were * aggregated. * * For aggregation, all packets were transmitted at the same rate, the * first index into rate scale table. */ if (info->flags & IEEE80211_TX_STAT_AMPDU) { tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); rs_collect_tx_data(curr_tbl, rs_index, info->status.ampdu_len, info->status.ampdu_ack_len); /* Update success/fail counts if not searching for new mode */ if (lq_sta->stay_in_tbl) { lq_sta->total_success += info->status.ampdu_ack_len; lq_sta->total_failed += (info->status.ampdu_len - info->status.ampdu_ack_len); } } else { /* * For legacy, update frame history with for each Tx retry. */ retries = info->status.rates[0].count - 1; /* HW doesn't send more than 15 retries */ retries = min(retries, 15); /* The last transmission may have been successful */ legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); /* Collect data for each rate used during failed TX attempts */ for (i = 0; i <= retries; ++i) { tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); /* * Only collect stats if retried rate is in the same RS * table as active/search. */ if (table_type_matches(&tbl_type, curr_tbl)) tmp_tbl = curr_tbl; else if (table_type_matches(&tbl_type, other_tbl)) tmp_tbl = other_tbl; else continue; rs_collect_tx_data(tmp_tbl, rs_index, 1, i < retries ? 0 : legacy_success); } /* Update success/fail counts if not searching for new mode */ if (lq_sta->stay_in_tbl) { lq_sta->total_success += legacy_success; lq_sta->total_failed += retries + (1 - legacy_success); } } /* The last TX rate is cached in lq_sta; it's set in if/else above */ lq_sta->last_rate_n_flags = tx_rate; done: /* See if there's a better rate or modulation mode to try. */ if (sta && sta->supp_rates[sband->band]) rs_rate_scale_perform(priv, skb, sta, lq_sta); #if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_TESTMODE) if ((priv->tm_fixed_rate) && (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) rs_program_fix_rate(priv, lq_sta); #endif if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) rs_bt_update_lq(priv, ctx, lq_sta); } /* * Begin a period of staying with a selected modulation mode. * Set "stay_in_tbl" flag to prevent any mode switches. * Set frame tx success limits according to legacy vs. high-throughput, * and reset overall (spanning all rates) tx success history statistics. * These control how long we stay using same modulation mode before * searching for a new mode. */ static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, struct iwl_lq_sta *lq_sta) { IWL_DEBUG_RATE(priv, "we are staying in the same table\n"); lq_sta->stay_in_tbl = 1; /* only place this gets set */ if (is_legacy) { lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; } else { lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; } lq_sta->table_count = 0; lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = jiffies; lq_sta->action_counter = 0; } /* * Find correct throughput table for given mode of modulation */ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl) { /* Used to choose among HT tables */ s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; /* Check for invalid LQ type */ if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { tbl->expected_tpt = expected_tpt_legacy; return; } /* Legacy rates have only one table */ if (is_legacy(tbl->lq_type)) { tbl->expected_tpt = expected_tpt_legacy; return; } /* Choose among many HT tables depending on number of streams * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation * status */ if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) ht_tbl_pointer = expected_tpt_siso20MHz; else if (is_siso(tbl->lq_type)) ht_tbl_pointer = expected_tpt_siso40MHz; else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) ht_tbl_pointer = expected_tpt_mimo2_20MHz; else if (is_mimo2(tbl->lq_type)) ht_tbl_pointer = expected_tpt_mimo2_40MHz; else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) ht_tbl_pointer = expected_tpt_mimo3_20MHz; else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ ht_tbl_pointer = expected_tpt_mimo3_40MHz; if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ tbl->expected_tpt = ht_tbl_pointer[0]; else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ tbl->expected_tpt = ht_tbl_pointer[1]; else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ tbl->expected_tpt = ht_tbl_pointer[2]; else /* AGG+SGI */ tbl->expected_tpt = ht_tbl_pointer[3]; } /* * Find starting rate for new "search" high-throughput mode of modulation. * Goal is to find lowest expected rate (under perfect conditions) that is * above the current measured throughput of "active" mode, to give new mode * a fair chance to prove itself without too many challenges. * * This gets called when transitioning to more aggressive modulation * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need * to decrease to match "active" throughput. When moving from MIMO to SISO, * bit rate will typically need to increase, but not if performance was bad. */ static s32 rs_get_best_rate(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, /* "search" */ u16 rate_mask, s8 index) { /* "active" values */ struct iwl_scale_tbl_info *active_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); s32 active_sr = active_tbl->win[index].success_ratio; s32 active_tpt = active_tbl->expected_tpt[index]; /* expected "search" throughput */ s32 *tpt_tbl = tbl->expected_tpt; s32 new_rate, high, low, start_hi; u16 high_low; s8 rate = index; new_rate = high = low = start_hi = IWL_RATE_INVALID; for (; ;) { high_low = rs_get_adjacent_rate(priv, rate, rate_mask, tbl->lq_type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; /* * Lower the "search" bit rate, to give new "search" mode * approximately the same throughput as "active" if: * * 1) "Active" mode has been working modestly well (but not * great), and expected "search" throughput (under perfect * conditions) at candidate rate is above the actual * measured "active" throughput (but less than expected * "active" throughput under perfect conditions). * OR * 2) "Active" mode has been working perfectly or very well * and expected "search" throughput (under perfect * conditions) at candidate rate is above expected * "active" throughput (under perfect conditions). */ if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && ((active_sr > IWL_RATE_DECREASE_TH) && (active_sr <= IWL_RATE_HIGH_TH) && (tpt_tbl[rate] <= active_tpt))) || ((active_sr >= IWL_RATE_SCALE_SWITCH) && (tpt_tbl[rate] > active_tpt))) { /* (2nd or later pass) * If we've already tried to raise the rate, and are * now trying to lower it, use the higher rate. */ if (start_hi != IWL_RATE_INVALID) { new_rate = start_hi; break; } new_rate = rate; /* Loop again with lower rate */ if (low != IWL_RATE_INVALID) rate = low; /* Lower rate not available, use the original */ else break; /* Else try to raise the "search" rate to match "active" */ } else { /* (2nd or later pass) * If we've already tried to lower the rate, and are * now trying to raise it, use the lower rate. */ if (new_rate != IWL_RATE_INVALID) break; /* Loop again with higher rate */ else if (high != IWL_RATE_INVALID) { start_hi = high; rate = high; /* Higher rate not available, use the original */ } else { new_rate = rate; break; } } } return new_rate; } /* * Set up search table for MIMO2 */ static int rs_switch_to_mimo2(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, struct iwl_scale_tbl_info *tbl, int index) { u16 rate_mask; s32 rate; s8 is_green = lq_sta->is_green; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; struct iwl_rxon_context *ctx = sta_priv->ctx; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) == WLAN_HT_CAP_SM_PS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ if (priv->hw_params.tx_chains_num < 2) return -1; IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n"); tbl->lq_type = LQ_MIMO2; tbl->is_dup = lq_sta->is_dup; tbl->action = 0; tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_mimo2_rate; if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; rs_set_expected_tpt_table(lq_sta, tbl); rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", rate, rate_mask); return -1; } tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", tbl->current_rate, is_green); return 0; } /* * Set up search table for MIMO3 */ static int rs_switch_to_mimo3(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, struct iwl_scale_tbl_info *tbl, int index) { u16 rate_mask; s32 rate; s8 is_green = lq_sta->is_green; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; struct iwl_rxon_context *ctx = sta_priv->ctx; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) == WLAN_HT_CAP_SM_PS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ if (priv->hw_params.tx_chains_num < 3) return -1; IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n"); tbl->lq_type = LQ_MIMO3; tbl->is_dup = lq_sta->is_dup; tbl->action = 0; tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; rate_mask = lq_sta->active_mimo3_rate; if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; rs_set_expected_tpt_table(lq_sta, tbl); rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n", rate, rate_mask); if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", rate, rate_mask); return -1; } tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", tbl->current_rate, is_green); return 0; } /* * Set up search table for SISO */ static int rs_switch_to_siso(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, struct iwl_scale_tbl_info *tbl, int index) { u16 rate_mask; u8 is_green = lq_sta->is_green; s32 rate; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; struct iwl_rxon_context *ctx = sta_priv->ctx; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n"); tbl->is_dup = lq_sta->is_dup; tbl->lq_type = LQ_SISO; tbl->action = 0; tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_siso_rate; if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; if (is_green) tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ rs_set_expected_tpt_table(lq_sta, tbl); rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask); if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n", rate, rate_mask); return -1; } tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", tbl->current_rate, is_green); return 0; } /* * Try to switch to new modulation mode from legacy */ static int rs_move_legacy_other(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, int index) { struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; int ret = 0; u8 update_search_tbl_counter = 0; switch (priv->bt_traffic_load) { case IWL_BT_COEX_TRAFFIC_LOAD_NONE: /* nothing */ break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2) tbl->action = IWL_LEGACY_SWITCH_SISO; break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: /* avoid antenna B and MIMO */ valid_tx_ant = first_antenna(priv->eeprom_data->valid_tx_ant); if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 && tbl->action != IWL_LEGACY_SWITCH_SISO) tbl->action = IWL_LEGACY_SWITCH_SISO; break; default: IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); break; } if (!iwl_ht_enabled(priv)) /* stay in Legacy */ tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && tbl->action > IWL_LEGACY_SWITCH_SISO) tbl->action = IWL_LEGACY_SWITCH_SISO; /* configure as 1x1 if bt full concurrency */ if (priv->bt_full_concurrent) { if (!iwl_ht_enabled(priv)) tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) tbl->action = IWL_LEGACY_SWITCH_SISO; valid_tx_ant = first_antenna(priv->eeprom_data->valid_tx_ant); } start_action = tbl->action; for (; ;) { lq_sta->action_counter++; switch (tbl->action) { case IWL_LEGACY_SWITCH_ANTENNA1: case IWL_LEGACY_SWITCH_ANTENNA2: IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n"); if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && tx_chains_num <= 1) || (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && tx_chains_num <= 2)) break; /* Don't change antenna if success has been great */ if (window->success_ratio >= IWL_RS_GOOD_RATIO && !priv->bt_full_concurrent && priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_NONE) break; /* Set up search table to try other antenna */ memcpy(search_tbl, tbl, sz); if (rs_toggle_antenna(valid_tx_ant, &search_tbl->current_rate, search_tbl)) { update_search_tbl_counter = 1; rs_set_expected_tpt_table(lq_sta, search_tbl); goto out; } break; case IWL_LEGACY_SWITCH_SISO: IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n"); /* Set up search table to try SISO */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; ret = rs_switch_to_siso(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) { lq_sta->action_counter = 0; goto out; } break; case IWL_LEGACY_SWITCH_MIMO2_AB: case IWL_LEGACY_SWITCH_MIMO2_AC: case IWL_LEGACY_SWITCH_MIMO2_BC: IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n"); /* Set up search table to try MIMO */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) search_tbl->ant_type = ANT_AB; else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) search_tbl->ant_type = ANT_AC; else search_tbl->ant_type = ANT_BC; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) { lq_sta->action_counter = 0; goto out; } break; case IWL_LEGACY_SWITCH_MIMO3_ABC: IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n"); /* Set up search table to try MIMO3 */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; search_tbl->ant_type = ANT_ABC; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) { lq_sta->action_counter = 0; goto out; } break; } tbl->action++; if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; if (tbl->action == start_action) break; } search_tbl->lq_type = LQ_NONE; return 0; out: lq_sta->search_better_tbl = 1; tbl->action++; if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; } /* * Try to switch to new modulation mode from SISO */ static int rs_move_siso_to_other(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, int index) { u8 is_green = lq_sta->is_green; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 update_search_tbl_counter = 0; int ret; switch (priv->bt_traffic_load) { case IWL_BT_COEX_TRAFFIC_LOAD_NONE: /* nothing */ break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) tbl->action = IWL_SISO_SWITCH_MIMO2_AB; break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: /* avoid antenna B and MIMO */ valid_tx_ant = first_antenna(priv->eeprom_data->valid_tx_ant); if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) tbl->action = IWL_SISO_SWITCH_ANTENNA1; break; default: IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); break; } if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && tbl->action > IWL_SISO_SWITCH_ANTENNA2) { /* stay in SISO */ tbl->action = IWL_SISO_SWITCH_ANTENNA1; } /* configure as 1x1 if bt full concurrency */ if (priv->bt_full_concurrent) { valid_tx_ant = first_antenna(priv->eeprom_data->valid_tx_ant); if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) tbl->action = IWL_SISO_SWITCH_ANTENNA1; } start_action = tbl->action; for (;;) { lq_sta->action_counter++; switch (tbl->action) { case IWL_SISO_SWITCH_ANTENNA1: case IWL_SISO_SWITCH_ANTENNA2: IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n"); if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && tx_chains_num <= 1) || (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && tx_chains_num <= 2)) break; if (window->success_ratio >= IWL_RS_GOOD_RATIO && !priv->bt_full_concurrent && priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_NONE) break; memcpy(search_tbl, tbl, sz); if (rs_toggle_antenna(valid_tx_ant, &search_tbl->current_rate, search_tbl)) { update_search_tbl_counter = 1; goto out; } break; case IWL_SISO_SWITCH_MIMO2_AB: case IWL_SISO_SWITCH_MIMO2_AC: case IWL_SISO_SWITCH_MIMO2_BC: IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n"); memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) search_tbl->ant_type = ANT_AB; else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) search_tbl->ant_type = ANT_AC; else search_tbl->ant_type = ANT_BC; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) goto out; break; case IWL_SISO_SWITCH_GI: if (!tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) break; if (tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); memcpy(search_tbl, tbl, sz); if (is_green) { if (!tbl->is_SGI) break; else IWL_ERR(priv, "SGI was set in GF+SISO\n"); } search_tbl->is_SGI = !tbl->is_SGI; rs_set_expected_tpt_table(lq_sta, search_tbl); if (tbl->is_SGI) { s32 tpt = lq_sta->last_tpt / 100; if (tpt >= search_tbl->expected_tpt[index]) break; } search_tbl->current_rate = rate_n_flags_from_tbl(priv, search_tbl, index, is_green); update_search_tbl_counter = 1; goto out; case IWL_SISO_SWITCH_MIMO3_ABC: IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n"); memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; search_tbl->ant_type = ANT_ABC; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) goto out; break; } tbl->action++; if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) tbl->action = IWL_SISO_SWITCH_ANTENNA1; if (tbl->action == start_action) break; } search_tbl->lq_type = LQ_NONE; return 0; out: lq_sta->search_better_tbl = 1; tbl->action++; if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC) tbl->action = IWL_SISO_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; } /* * Try to switch to new modulation mode from MIMO2 */ static int rs_move_mimo2_to_other(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, int index) { s8 is_green = lq_sta->is_green; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 update_search_tbl_counter = 0; int ret; switch (priv->bt_traffic_load) { case IWL_BT_COEX_TRAFFIC_LOAD_NONE: /* nothing */ break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: /* avoid antenna B and MIMO */ if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) tbl->action = IWL_MIMO2_SWITCH_SISO_A; break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || tbl->action == IWL_MIMO2_SWITCH_SISO_C) tbl->action = IWL_MIMO2_SWITCH_SISO_A; break; default: IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); break; } if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && (tbl->action < IWL_MIMO2_SWITCH_SISO_A || tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { /* switch in SISO */ tbl->action = IWL_MIMO2_SWITCH_SISO_A; } /* configure as 1x1 if bt full concurrency */ if (priv->bt_full_concurrent && (tbl->action < IWL_MIMO2_SWITCH_SISO_A || tbl->action > IWL_MIMO2_SWITCH_SISO_C)) tbl->action = IWL_MIMO2_SWITCH_SISO_A; start_action = tbl->action; for (;;) { lq_sta->action_counter++; switch (tbl->action) { case IWL_MIMO2_SWITCH_ANTENNA1: case IWL_MIMO2_SWITCH_ANTENNA2: IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n"); if (tx_chains_num <= 2) break; if (window->success_ratio >= IWL_RS_GOOD_RATIO) break; memcpy(search_tbl, tbl, sz); if (rs_toggle_antenna(valid_tx_ant, &search_tbl->current_rate, search_tbl)) { update_search_tbl_counter = 1; goto out; } break; case IWL_MIMO2_SWITCH_SISO_A: case IWL_MIMO2_SWITCH_SISO_B: case IWL_MIMO2_SWITCH_SISO_C: IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n"); /* Set up new search table for SISO */ memcpy(search_tbl, tbl, sz); if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) search_tbl->ant_type = ANT_A; else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) search_tbl->ant_type = ANT_B; else search_tbl->ant_type = ANT_C; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_siso(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) goto out; break; case IWL_MIMO2_SWITCH_GI: if (!tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) break; if (tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); /* Set up new search table for MIMO2 */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = !tbl->is_SGI; rs_set_expected_tpt_table(lq_sta, search_tbl); /* * If active table already uses the fastest possible * modulation (dual stream with short guard interval), * and it's working well, there's no need to look * for a better type of modulation! */ if (tbl->is_SGI) { s32 tpt = lq_sta->last_tpt / 100; if (tpt >= search_tbl->expected_tpt[index]) break; } search_tbl->current_rate = rate_n_flags_from_tbl(priv, search_tbl, index, is_green); update_search_tbl_counter = 1; goto out; case IWL_MIMO2_SWITCH_MIMO3_ABC: IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n"); memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; search_tbl->ant_type = ANT_ABC; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) goto out; break; } tbl->action++; if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; if (tbl->action == start_action) break; } search_tbl->lq_type = LQ_NONE; return 0; out: lq_sta->search_better_tbl = 1; tbl->action++; if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; } /* * Try to switch to new modulation mode from MIMO3 */ static int rs_move_mimo3_to_other(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, int index) { s8 is_green = lq_sta->is_green; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; int ret; u8 update_search_tbl_counter = 0; switch (priv->bt_traffic_load) { case IWL_BT_COEX_TRAFFIC_LOAD_NONE: /* nothing */ break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: /* avoid antenna B and MIMO */ if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) tbl->action = IWL_MIMO3_SWITCH_SISO_A; break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || tbl->action == IWL_MIMO3_SWITCH_SISO_C) tbl->action = IWL_MIMO3_SWITCH_SISO_A; break; default: IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); break; } if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && (tbl->action < IWL_MIMO3_SWITCH_SISO_A || tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { /* switch in SISO */ tbl->action = IWL_MIMO3_SWITCH_SISO_A; } /* configure as 1x1 if bt full concurrency */ if (priv->bt_full_concurrent && (tbl->action < IWL_MIMO3_SWITCH_SISO_A || tbl->action > IWL_MIMO3_SWITCH_SISO_C)) tbl->action = IWL_MIMO3_SWITCH_SISO_A; start_action = tbl->action; for (;;) { lq_sta->action_counter++; switch (tbl->action) { case IWL_MIMO3_SWITCH_ANTENNA1: case IWL_MIMO3_SWITCH_ANTENNA2: IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n"); if (tx_chains_num <= 3) break; if (window->success_ratio >= IWL_RS_GOOD_RATIO) break; memcpy(search_tbl, tbl, sz); if (rs_toggle_antenna(valid_tx_ant, &search_tbl->current_rate, search_tbl)) goto out; break; case IWL_MIMO3_SWITCH_SISO_A: case IWL_MIMO3_SWITCH_SISO_B: case IWL_MIMO3_SWITCH_SISO_C: IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n"); /* Set up new search table for SISO */ memcpy(search_tbl, tbl, sz); if (tbl->action == IWL_MIMO3_SWITCH_SISO_A) search_tbl->ant_type = ANT_A; else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B) search_tbl->ant_type = ANT_B; else search_tbl->ant_type = ANT_C; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_siso(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) goto out; break; case IWL_MIMO3_SWITCH_MIMO2_AB: case IWL_MIMO3_SWITCH_MIMO2_AC: case IWL_MIMO3_SWITCH_MIMO2_BC: IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n"); memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB) search_tbl->ant_type = ANT_AB; else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC) search_tbl->ant_type = ANT_AC; else search_tbl->ant_type = ANT_BC; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) break; ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, search_tbl, index); if (!ret) goto out; break; case IWL_MIMO3_SWITCH_GI: if (!tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) break; if (tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); /* Set up new search table for MIMO */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = !tbl->is_SGI; rs_set_expected_tpt_table(lq_sta, search_tbl); /* * If active table already uses the fastest possible * modulation (dual stream with short guard interval), * and it's working well, there's no need to look * for a better type of modulation! */ if (tbl->is_SGI) { s32 tpt = lq_sta->last_tpt / 100; if (tpt >= search_tbl->expected_tpt[index]) break; } search_tbl->current_rate = rate_n_flags_from_tbl(priv, search_tbl, index, is_green); update_search_tbl_counter = 1; goto out; } tbl->action++; if (tbl->action > IWL_MIMO3_SWITCH_GI) tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; if (tbl->action == start_action) break; } search_tbl->lq_type = LQ_NONE; return 0; out: lq_sta->search_better_tbl = 1; tbl->action++; if (tbl->action > IWL_MIMO3_SWITCH_GI) tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; } /* * Check whether we should continue using same modulation mode, or * begin search for a new mode, based on: * 1) # tx successes or failures while using this mode * 2) # times calling this function * 3) elapsed time in this mode (not used, for now) */ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) { struct iwl_scale_tbl_info *tbl; int i; int active_tbl; int flush_interval_passed = 0; struct iwl_priv *priv; priv = lq_sta->drv; active_tbl = lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); /* If we've been disallowing search, see if we should now allow it */ if (lq_sta->stay_in_tbl) { /* Elapsed time using current modulation mode */ if (lq_sta->flush_timer) flush_interval_passed = time_after(jiffies, (unsigned long)(lq_sta->flush_timer + IWL_RATE_SCALE_FLUSH_INTVL)); /* * Check if we should allow search for new modulation mode. * If many frames have failed or succeeded, or we've used * this same modulation for a long time, allow search, and * reset history stats that keep track of whether we should * allow a new search. Also (below) reset all bitmaps and * stats in active history. */ if (force_search || (lq_sta->total_failed > lq_sta->max_failure_limit) || (lq_sta->total_success > lq_sta->max_success_limit) || ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) && (flush_interval_passed))) { IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n", lq_sta->total_failed, lq_sta->total_success, flush_interval_passed); /* Allow search for new mode */ lq_sta->stay_in_tbl = 0; /* only place reset */ lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = 0; /* * Else if we've used this modulation mode enough repetitions * (regardless of elapsed time or success/failure), reset * history bitmaps and rate-specific stats for all rates in * active table. */ } else { lq_sta->table_count++; if (lq_sta->table_count >= lq_sta->table_count_limit) { lq_sta->table_count = 0; IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n"); for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window( &(tbl->win[i])); } } /* If transitioning to allow "search", reset all history * bitmaps and stats in active table (this will become the new * "search" table). */ if (!lq_sta->stay_in_tbl) { for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&(tbl->win[i])); } } } /* * setup rate table in uCode */ static void rs_update_rate_tbl(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, int index, u8 is_green) { u32 rate; /* Update uCode's rate table. */ rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); rs_fill_link_cmd(priv, lq_sta, rate); iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); } /* * Do rate scaling and search for new modulation mode. */ static void rs_rate_scale_perform(struct iwl_priv *priv, struct sk_buff *skb, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta) { struct ieee80211_hw *hw = priv->hw; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; int low = IWL_RATE_INVALID; int high = IWL_RATE_INVALID; int index; int i; struct iwl_rate_scale_data *window = NULL; int current_tpt = IWL_INVALID_VALUE; int low_tpt = IWL_INVALID_VALUE; int high_tpt = IWL_INVALID_VALUE; u32 fail_count; s8 scale_action = 0; u16 rate_mask; u8 update_lq = 0; struct iwl_scale_tbl_info *tbl, *tbl1; u16 rate_scale_index_msk = 0; u8 is_green = 0; u8 active_tbl = 0; u8 done_search = 0; u16 high_low; s32 sr; u8 tid = IWL_MAX_TID_COUNT; struct iwl_tid_data *tid_data; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; struct iwl_rxon_context *ctx = sta_priv->ctx; IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); /* Send management frames and NO_ACK data using lowest rate. */ /* TODO: this could probably be improved.. */ if (!ieee80211_is_data(hdr->frame_control) || info->flags & IEEE80211_TX_CTL_NO_ACK) return; lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; tid = rs_tl_add_packet(lq_sta, hdr); if ((tid != IWL_MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) { tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid]; if (tid_data->agg.state == IWL_AGG_OFF) lq_sta->is_agg = 0; else lq_sta->is_agg = 1; } else lq_sta->is_agg = 0; /* * Select rate-scale / modulation-mode table to work with in * the rest of this function: "search" if searching for better * modulation mode, or "active" if doing rate scaling within a mode. */ if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; else active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); if (is_legacy(tbl->lq_type)) lq_sta->is_green = 0; else lq_sta->is_green = rs_use_green(sta); is_green = lq_sta->is_green; /* current tx rate */ index = lq_sta->last_txrate_idx; IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index, tbl->lq_type); /* rates available for this association, and for modulation mode */ rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask); /* mask with station rate restriction */ if (is_legacy(tbl->lq_type)) { if (lq_sta->band == IEEE80211_BAND_5GHZ) /* supp_rates has no CCK bits in A mode */ rate_scale_index_msk = (u16) (rate_mask & (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); else rate_scale_index_msk = (u16) (rate_mask & lq_sta->supp_rates); } else rate_scale_index_msk = rate_mask; if (!rate_scale_index_msk) rate_scale_index_msk = rate_mask; if (!((1 << index) & rate_scale_index_msk)) { IWL_ERR(priv, "Current Rate is not valid\n"); if (lq_sta->search_better_tbl) { /* revert to active table if search table is not valid*/ tbl->lq_type = LQ_NONE; lq_sta->search_better_tbl = 0; tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); /* get "active" rate info */ index = iwl_hwrate_to_plcp_idx(tbl->current_rate); rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green); } return; } /* Get expected throughput table and history window for current rate */ if (!tbl->expected_tpt) { IWL_ERR(priv, "tbl->expected_tpt is NULL\n"); return; } /* force user max rate if set by user */ if ((lq_sta->max_rate_idx != -1) && (lq_sta->max_rate_idx < index)) { index = lq_sta->max_rate_idx; update_lq = 1; window = &(tbl->win[index]); goto lq_update; } window = &(tbl->win[index]); /* * If there is not enough history to calculate actual average * throughput, keep analyzing results of more tx frames, without * changing rate or mode (bypass most of the rest of this function). * Set up new rate table in uCode only if old rate is not supported * in current association (use new rate found above). */ fail_count = window->counter - window->success_counter; if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d " "for index %d\n", window->success_counter, window->counter, index); /* Can't calculate this yet; not enough history */ window->average_tpt = IWL_INVALID_VALUE; /* Should we stay with this modulation mode, * or search for a new one? */ rs_stay_in_table(lq_sta, false); goto out; } /* Else we have enough samples; calculate estimate of * actual average throughput */ if (window->average_tpt != ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128)) { IWL_ERR(priv, "expected_tpt should have been calculated by now\n"); window->average_tpt = ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128); } /* If we are searching for better modulation mode, check success. */ if (lq_sta->search_better_tbl && (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { /* If good success, continue using the "search" mode; * no need to send new link quality command, since we're * continuing to use the setup that we've been trying. */ if (window->average_tpt > lq_sta->last_tpt) { IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE " "suc=%d cur-tpt=%d old-tpt=%d\n", window->success_ratio, window->average_tpt, lq_sta->last_tpt); if (!is_legacy(tbl->lq_type)) lq_sta->enable_counter = 1; /* Swap tables; "search" becomes "active" */ lq_sta->active_tbl = active_tbl; current_tpt = window->average_tpt; /* Else poor success; go back to mode in "active" table */ } else { IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE " "suc=%d cur-tpt=%d old-tpt=%d\n", window->success_ratio, window->average_tpt, lq_sta->last_tpt); /* Nullify "search" table */ tbl->lq_type = LQ_NONE; /* Revert to "active" table */ active_tbl = lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); /* Revert to "active" rate and throughput info */ index = iwl_hwrate_to_plcp_idx(tbl->current_rate); current_tpt = lq_sta->last_tpt; /* Need to set up a new rate table in uCode */ update_lq = 1; } /* Either way, we've made a decision; modulation mode * search is done, allow rate adjustment next time. */ lq_sta->search_better_tbl = 0; done_search = 1; /* Don't switch modes below! */ goto lq_update; } /* (Else) not in search of better modulation mode, try for better * starting rate, while staying in this mode. */ high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, tbl->lq_type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; /* If user set max rate, dont allow higher than user constrain */ if ((lq_sta->max_rate_idx != -1) && (lq_sta->max_rate_idx < high)) high = IWL_RATE_INVALID; sr = window->success_ratio; /* Collect measured throughputs for current and adjacent rates */ current_tpt = window->average_tpt; if (low != IWL_RATE_INVALID) low_tpt = tbl->win[low].average_tpt; if (high != IWL_RATE_INVALID) high_tpt = tbl->win[high].average_tpt; scale_action = 0; /* Too many failures, decrease rate */ if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n"); scale_action = -1; /* No throughput measured yet for adjacent rates; try increase. */ } else if ((low_tpt == IWL_INVALID_VALUE) && (high_tpt == IWL_INVALID_VALUE)) { if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) scale_action = 1; else if (low != IWL_RATE_INVALID) scale_action = 0; } /* Both adjacent throughputs are measured, but neither one has better * throughput; we're using the best rate, don't change it! */ else if ((low_tpt != IWL_INVALID_VALUE) && (high_tpt != IWL_INVALID_VALUE) && (low_tpt < current_tpt) && (high_tpt < current_tpt)) scale_action = 0; /* At least one adjacent rate's throughput is measured, * and may have better performance. */ else { /* Higher adjacent rate's throughput is measured */ if (high_tpt != IWL_INVALID_VALUE) { /* Higher rate has better throughput */ if (high_tpt > current_tpt && sr >= IWL_RATE_INCREASE_TH) { scale_action = 1; } else { scale_action = 0; } /* Lower adjacent rate's throughput is measured */ } else if (low_tpt != IWL_INVALID_VALUE) { /* Lower rate has better throughput */ if (low_tpt > current_tpt) { IWL_DEBUG_RATE(priv, "decrease rate because of low tpt\n"); scale_action = -1; } else if (sr >= IWL_RATE_INCREASE_TH) { scale_action = 1; } } } /* Sanity check; asked for decrease, but success rate or throughput * has been good at old rate. Don't change it. */ if ((scale_action == -1) && (low != IWL_RATE_INVALID) && ((sr > IWL_RATE_HIGH_TH) || (current_tpt > (100 * tbl->expected_tpt[low])))) scale_action = 0; if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) scale_action = -1; if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) scale_action = -1; if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { if (lq_sta->last_bt_traffic > priv->bt_traffic_load) { /* * don't set scale_action, don't want to scale up if * the rate scale doesn't otherwise think that is a * good idea. */ } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) { scale_action = -1; } } lq_sta->last_bt_traffic = priv->bt_traffic_load; if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { /* search for a new modulation */ rs_stay_in_table(lq_sta, true); goto lq_update; } switch (scale_action) { case -1: /* Decrease starting rate, update uCode's rate table */ if (low != IWL_RATE_INVALID) { update_lq = 1; index = low; } break; case 1: /* Increase starting rate, update uCode's rate table */ if (high != IWL_RATE_INVALID) { update_lq = 1; index = high; } break; case 0: /* No change */ default: break; } IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d " "high %d type %d\n", index, scale_action, low, high, tbl->lq_type); lq_update: /* Replace uCode's rate table for the destination station. */ if (update_lq) rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green); if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { /* Should we stay with this modulation mode, * or search for a new one? */ rs_stay_in_table(lq_sta, false); } /* * Search for new modulation mode if we're: * 1) Not changing rates right now * 2) Not just finishing up a search * 3) Allowing a new search */ if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { /* Save current throughput to compare with "search" throughput*/ lq_sta->last_tpt = current_tpt; /* Select a new "search" modulation mode to try. * If one is found, set up the new "search" table. */ if (is_legacy(tbl->lq_type)) rs_move_legacy_other(priv, lq_sta, conf, sta, index); else if (is_siso(tbl->lq_type)) rs_move_siso_to_other(priv, lq_sta, conf, sta, index); else if (is_mimo2(tbl->lq_type)) rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index); else rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index); /* If new "search" mode was selected, set up in uCode table */ if (lq_sta->search_better_tbl) { /* Access the "search" table, clear its history. */ tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&(tbl->win[i])); /* Use new "search" start rate */ index = iwl_hwrate_to_plcp_idx(tbl->current_rate); IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", tbl->current_rate, index); rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); } else done_search = 1; } if (done_search && !lq_sta->stay_in_tbl) { /* If the "active" (non-search) mode was legacy, * and we've tried switching antennas, * but we haven't been able to try HT modes (not available), * stay with best antenna legacy modulation for a while * before next round of mode comparisons. */ tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && lq_sta->action_counter > tbl1->max_search) { IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n"); rs_set_stay_in_table(priv, 1, lq_sta); } /* If we're in an HT mode, and all 3 mode switch actions * have been tried and compared, stay in this best modulation * mode for a while before next round of mode comparisons. */ if (lq_sta->enable_counter && (lq_sta->action_counter >= tbl1->max_search) && iwl_ht_enabled(priv)) { if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != IWL_MAX_TID_COUNT)) { u8 sta_id = lq_sta->lq.sta_id; tid_data = &priv->tid_data[sta_id][tid]; if (tid_data->agg.state == IWL_AGG_OFF) { IWL_DEBUG_RATE(priv, "try to aggregate tid %d\n", tid); rs_tl_turn_on_agg(priv, tid, lq_sta, sta); } } rs_set_stay_in_table(priv, 0, lq_sta); } } out: tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); lq_sta->last_txrate_idx = index; } /** * rs_initialize_lq - Initialize a station's hardware rate table * * The uCode's station table contains a table of fallback rates * for automatic fallback during transmission. * * NOTE: This sets up a default set of values. These will be replaced later * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of * rc80211_simple. * * NOTE: Run REPLY_ADD_STA command to set up station table entry, before * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, * which requires station table entry to exist). */ static void rs_initialize_lq(struct iwl_priv *priv, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta) { struct iwl_scale_tbl_info *tbl; int rate_idx; int i; u32 rate; u8 use_green = rs_use_green(sta); u8 active_tbl = 0; u8 valid_tx_ant; struct iwl_station_priv *sta_priv; struct iwl_rxon_context *ctx; if (!sta || !lq_sta) return; sta_priv = (void *)sta->drv_priv; ctx = sta_priv->ctx; i = lq_sta->last_txrate_idx; valid_tx_ant = priv->eeprom_data->valid_tx_ant; if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; else active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); if ((i < 0) || (i >= IWL_RATE_COUNT)) i = 0; rate = iwl_rates[i].plcp; tbl->ant_type = first_antenna(valid_tx_ant); rate |= tbl->ant_type << RATE_MCS_ANT_POS; if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) rate |= RATE_MCS_CCK_MSK; rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) rs_toggle_antenna(valid_tx_ant, &rate, tbl); rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green); tbl->current_rate = rate; rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_link_cmd(NULL, lq_sta, rate); priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true); } static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_supported_band *sband = txrc->sband; struct iwl_op_mode *op_mode __maybe_unused = (struct iwl_op_mode *)priv_r; struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = priv_sta; int rate_idx; IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); /* Get max rate if user set max rate */ if (lq_sta) { lq_sta->max_rate_idx = txrc->max_rate_idx; if ((sband->band == IEEE80211_BAND_5GHZ) && (lq_sta->max_rate_idx != -1)) lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; if ((lq_sta->max_rate_idx < 0) || (lq_sta->max_rate_idx >= IWL_RATE_COUNT)) lq_sta->max_rate_idx = -1; } /* Treat uninitialized rate scaling data same as non-existing. */ if (lq_sta && !lq_sta->drv) { IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); priv_sta = NULL; } /* Send management frames and NO_ACK data using lowest rate. */ if (rate_control_send_low(sta, priv_sta, txrc)) return; rate_idx = lq_sta->last_txrate_idx; if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { rate_idx -= IWL_FIRST_OFDM_RATE; /* 6M and 9M shared same MCS index */ rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; if (rs_extract_rate(lq_sta->last_rate_n_flags) >= IWL_RATE_MIMO3_6M_PLCP) rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM); else if (rs_extract_rate(lq_sta->last_rate_n_flags) >= IWL_RATE_MIMO2_6M_PLCP) rate_idx = rate_idx + MCS_INDEX_PER_STREAM; info->control.rates[0].flags = IEEE80211_TX_RC_MCS; if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; } else { /* Check for invalid rates */ if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || ((sband->band == IEEE80211_BAND_5GHZ) && (rate_idx < IWL_FIRST_OFDM_RATE))) rate_idx = rate_lowest_index(sband, sta); /* On valid 5 GHz rate, adjust index */ else if (sband->band == IEEE80211_BAND_5GHZ) rate_idx -= IWL_FIRST_OFDM_RATE; info->control.rates[0].flags = 0; } info->control.rates[0].idx = rate_idx; } static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, gfp_t gfp) { struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv; struct iwl_op_mode *op_mode __maybe_unused = (struct iwl_op_mode *)priv_rate; struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); IWL_DEBUG_RATE(priv, "create station rate scale window\n"); return &sta_priv->lq_sta; } /* * Called after adding a new station to initialize rate scaling */ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) { int i, j; struct ieee80211_hw *hw = priv->hw; struct ieee80211_conf *conf = &priv->hw->conf; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct iwl_station_priv *sta_priv; struct iwl_lq_sta *lq_sta; struct ieee80211_supported_band *sband; unsigned long supp; /* must be unsigned long for for_each_set_bit */ sta_priv = (struct iwl_station_priv *) sta->drv_priv; lq_sta = &sta_priv->lq_sta; sband = hw->wiphy->bands[conf->channel->band]; lq_sta->lq.sta_id = sta_id; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); lq_sta->flush_timer = 0; lq_sta->supp_rates = sta->supp_rates[sband->band]; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", sta_id); /* TODO: what is a good starting rate for STA? About middle? Maybe not * the lowest or the highest rate.. Could consider using RSSI from * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ lq_sta->is_dup = 0; lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; lq_sta->is_green = rs_use_green(sta); lq_sta->band = sband->band; /* * active legacy rates as per supported rates bitmap */ supp = sta->supp_rates[sband->band]; lq_sta->active_legacy_rate = 0; for_each_set_bit(i, &supp, BITS_PER_LONG) lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); /* * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), * supp_rates[] does not; shift to convert format, force 9 MBits off. */ lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; lq_sta->active_siso_rate &= ~((u16)0x2); lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; /* Same here */ lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; lq_sta->active_mimo2_rate &= ~((u16)0x2); lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; lq_sta->active_mimo3_rate &= ~((u16)0x2); lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", lq_sta->active_siso_rate, lq_sta->active_mimo2_rate, lq_sta->active_mimo3_rate); /* These values will be overridden later */ lq_sta->lq.general_params.single_stream_ant_msk = first_antenna(priv->eeprom_data->valid_tx_ant); lq_sta->lq.general_params.dual_stream_ant_msk = priv->eeprom_data->valid_tx_ant & ~first_antenna(priv->eeprom_data->valid_tx_ant); if (!lq_sta->lq.general_params.dual_stream_ant_msk) { lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; } else if (num_of_ant(priv->eeprom_data->valid_tx_ant) == 2) { lq_sta->lq.general_params.dual_stream_ant_msk = priv->eeprom_data->valid_tx_ant; } /* as default allow aggregation for all tids */ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; lq_sta->drv = priv; /* Set last_txrate_idx to lowest rate */ lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); if (sband->band == IEEE80211_BAND_5GHZ) lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; lq_sta->is_agg = 0; #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE priv->tm_fixed_rate = 0; #endif #ifdef CONFIG_MAC80211_DEBUGFS lq_sta->dbg_fixed_rate = 0; #endif rs_initialize_lq(priv, sta, lq_sta); } static void rs_fill_link_cmd(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, u32 new_rate) { struct iwl_scale_tbl_info tbl_type; int index = 0; int rate_idx; int repeat_rate = 0; u8 ant_toggle_cnt = 0; u8 use_ht_possible = 1; u8 valid_tx_ant = 0; struct iwl_station_priv *sta_priv = container_of(lq_sta, struct iwl_station_priv, lq_sta); struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; /* Override starting rate (index 0) if needed for debug purposes */ rs_dbgfs_set_mcs(lq_sta, &new_rate, index); /* Interpret new_rate (rate_n_flags) */ rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, &rate_idx); if (priv && priv->bt_full_concurrent) { /* 1x1 only */ tbl_type.ant_type = first_antenna(priv->eeprom_data->valid_tx_ant); } /* How many times should we repeat the initial rate? */ if (is_legacy(tbl_type.lq_type)) { ant_toggle_cnt = 1; repeat_rate = IWL_NUMBER_TRY; } else { repeat_rate = min(IWL_HT_NUMBER_TRY, LINK_QUAL_AGG_DISABLE_START_DEF - 1); } lq_cmd->general_params.mimo_delimiter = is_mimo(tbl_type.lq_type) ? 1 : 0; /* Fill 1st table entry (index 0) */ lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); if (num_of_ant(tbl_type.ant_type) == 1) { lq_cmd->general_params.single_stream_ant_msk = tbl_type.ant_type; } else if (num_of_ant(tbl_type.ant_type) == 2) { lq_cmd->general_params.dual_stream_ant_msk = tbl_type.ant_type; } /* otherwise we don't modify the existing value */ index++; repeat_rate--; if (priv) { if (priv->bt_full_concurrent) valid_tx_ant = ANT_A; else valid_tx_ant = priv->eeprom_data->valid_tx_ant; } /* Fill rest of rate table */ while (index < LINK_QUAL_MAX_RETRY_NUM) { /* Repeat initial/next rate. * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { if (is_legacy(tbl_type.lq_type)) { if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) ant_toggle_cnt++; else if (priv && rs_toggle_antenna(valid_tx_ant, &new_rate, &tbl_type)) ant_toggle_cnt = 1; } /* Override next rate if needed for debug purposes */ rs_dbgfs_set_mcs(lq_sta, &new_rate, index); /* Fill next table entry */ lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); repeat_rate--; index++; } rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, &rate_idx); if (priv && priv->bt_full_concurrent) { /* 1x1 only */ tbl_type.ant_type = first_antenna(priv->eeprom_data->valid_tx_ant); } /* Indicate to uCode which entries might be MIMO. * If initial rate was MIMO, this will finally end up * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ if (is_mimo(tbl_type.lq_type)) lq_cmd->general_params.mimo_delimiter = index; /* Get next rate */ new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, use_ht_possible); /* How many times should we repeat the next rate? */ if (is_legacy(tbl_type.lq_type)) { if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) ant_toggle_cnt++; else if (priv && rs_toggle_antenna(valid_tx_ant, &new_rate, &tbl_type)) ant_toggle_cnt = 1; repeat_rate = IWL_NUMBER_TRY; } else { repeat_rate = IWL_HT_NUMBER_TRY; } /* Don't allow HT rates after next pass. * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ use_ht_possible = 0; /* Override next rate if needed for debug purposes */ rs_dbgfs_set_mcs(lq_sta, &new_rate, index); /* Fill next table entry */ lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); index++; repeat_rate--; } lq_cmd->agg_params.agg_frame_cnt_limit = sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF; lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; lq_cmd->agg_params.agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); /* * overwrite if needed, pass aggregation time limit * to uCode in uSec */ if (priv && priv->cfg->bt_params && priv->cfg->bt_params->agg_time_limit && priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) lq_cmd->agg_params.agg_time_limit = cpu_to_le16(priv->cfg->bt_params->agg_time_limit); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { return hw->priv; } /* rate scale requires free function to be implemented */ static void rs_free(void *priv_rate) { return; } static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, void *priv_sta) { struct iwl_op_mode *op_mode __maybe_unused = priv_r; struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); IWL_DEBUG_RATE(priv, "enter\n"); IWL_DEBUG_RATE(priv, "leave\n"); } #ifdef CONFIG_MAC80211_DEBUGFS static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, u32 *rate_n_flags, int index) { struct iwl_priv *priv; u8 valid_tx_ant; u8 ant_sel_tx; priv = lq_sta->drv; valid_tx_ant = priv->eeprom_data->valid_tx_ant; if (lq_sta->dbg_fixed_rate) { ant_sel_tx = ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { *rate_n_flags = lq_sta->dbg_fixed_rate; IWL_DEBUG_RATE(priv, "Fixed rate ON\n"); } else { lq_sta->dbg_fixed_rate = 0; IWL_ERR(priv, "Invalid antenna selection 0x%X, Valid is 0x%X\n", ant_sel_tx, valid_tx_ant); IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); } } else { IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); } } static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_lq_sta *lq_sta = file->private_data; struct iwl_priv *priv; char buf[64]; size_t buf_size; u32 parsed_rate; priv = lq_sta->drv; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &parsed_rate) == 1) lq_sta->dbg_fixed_rate = parsed_rate; else lq_sta->dbg_fixed_rate = 0; rs_program_fix_rate(priv, lq_sta); return count; } static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buff; int desc = 0; int i = 0; int index = 0; ssize_t ret; struct iwl_lq_sta *lq_sta = file->private_data; struct iwl_priv *priv; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); priv = lq_sta->drv; buff = kmalloc(1024, GFP_KERNEL); if (!buff) return -ENOMEM; desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", lq_sta->total_failed, lq_sta->total_success, lq_sta->active_legacy_rate); desc += sprintf(buff+desc, "fixed rate 0x%X\n", lq_sta->dbg_fixed_rate); desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", (priv->eeprom_data->valid_tx_ant & ANT_A) ? "ANT_A," : "", (priv->eeprom_data->valid_tx_ant & ANT_B) ? "ANT_B," : "", (priv->eeprom_data->valid_tx_ant & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff+desc, "lq type %s\n", (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); if (is_Ht(tbl->lq_type)) { desc += sprintf(buff+desc, " %s", (is_siso(tbl->lq_type)) ? "SISO" : ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); desc += sprintf(buff+desc, " %s", (tbl->is_ht40) ? "40MHz" : "20MHz"); desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "", (lq_sta->is_green) ? "GF enabled" : "", (lq_sta->is_agg) ? "AGG on" : ""); } desc += sprintf(buff+desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); desc += sprintf(buff+desc, "general:" "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", lq_sta->lq.general_params.flags, lq_sta->lq.general_params.mimo_delimiter, lq_sta->lq.general_params.single_stream_ant_msk, lq_sta->lq.general_params.dual_stream_ant_msk); desc += sprintf(buff+desc, "agg:" "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), lq_sta->lq.agg_params.agg_dis_start_th, lq_sta->lq.agg_params.agg_frame_cnt_limit); desc += sprintf(buff+desc, "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", lq_sta->lq.general_params.start_rate_index[0], lq_sta->lq.general_params.start_rate_index[1], lq_sta->lq.general_params.start_rate_index[2], lq_sta->lq.general_params.start_rate_index[3]); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { index = iwl_hwrate_to_plcp_idx( le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); if (is_legacy(tbl->lq_type)) { desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), iwl_rate_mcs[index].mbps); } else { desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n", i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs); } } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); kfree(buff); return ret; } static const struct file_operations rs_sta_dbgfs_scale_table_ops = { .write = rs_sta_dbgfs_scale_table_write, .read = rs_sta_dbgfs_scale_table_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buff; int desc = 0; int i, j; ssize_t ret; struct iwl_lq_sta *lq_sta = file->private_data; buff = kmalloc(1024, GFP_KERNEL); if (!buff) return -ENOMEM; for (i = 0; i < LQ_SIZE; i++) { desc += sprintf(buff+desc, "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" "rate=0x%X\n", lq_sta->active_tbl == i ? "*" : "x", lq_sta->lq_info[i].lq_type, lq_sta->lq_info[i].is_SGI, lq_sta->lq_info[i].is_ht40, lq_sta->lq_info[i].is_dup, lq_sta->is_green, lq_sta->lq_info[i].current_rate); for (j = 0; j < IWL_RATE_COUNT; j++) { desc += sprintf(buff+desc, "counter=%d success=%d %%=%d\n", lq_sta->lq_info[i].win[j].counter, lq_sta->lq_info[i].win[j].success_counter, lq_sta->lq_info[i].win[j].success_ratio); } } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); kfree(buff); return ret; } static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .read = rs_sta_dbgfs_stats_table_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_lq_sta *lq_sta = file->private_data; struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; char buff[120]; int desc = 0; if (is_Ht(tbl->lq_type)) desc += sprintf(buff+desc, "Bit Rate= %d Mb/s\n", tbl->expected_tpt[lq_sta->last_txrate_idx]); else desc += sprintf(buff+desc, "Bit Rate= %d Mb/s\n", iwl_rates[lq_sta->last_txrate_idx].ieee >> 1); return simple_read_from_buffer(user_buf, count, ppos, buff, desc); } static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { .read = rs_sta_dbgfs_rate_scale_data_read, .open = simple_open, .llseek = default_llseek, }; static void rs_add_debugfs(void *priv, void *priv_sta, struct dentry *dir) { struct iwl_lq_sta *lq_sta = priv_sta; lq_sta->rs_sta_dbgfs_scale_table_file = debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_scale_table_ops); lq_sta->rs_sta_dbgfs_stats_table_file = debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_stats_table_ops); lq_sta->rs_sta_dbgfs_rate_scale_data_file = debugfs_create_file("rate_scale_data", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, &lq_sta->tx_agg_tid_en); } static void rs_remove_debugfs(void *priv, void *priv_sta) { struct iwl_lq_sta *lq_sta = priv_sta; debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); } #endif /* * Initialization of rate scaling information is done by driver after * the station is added. Since mac80211 calls this function before a * station is added we ignore it. */ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { } static struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, .rate_init = rs_rate_init_stub, .alloc = rs_alloc, .free = rs_free, .alloc_sta = rs_alloc_sta, .free_sta = rs_free_sta, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = rs_add_debugfs, .remove_sta_debugfs = rs_remove_debugfs, #endif }; int iwlagn_rate_control_register(void) { return ieee80211_rate_control_register(&rs_ops); } void iwlagn_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rs_ops); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/power.h0000644000175000017500000000340712026211315024232 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #ifndef __iwl_power_setting_h__ #define __iwl_power_setting_h__ #include "commands.h" struct iwl_power_mgr { struct iwl_powertable_cmd sleep_cmd; struct iwl_powertable_cmd sleep_cmd_next; int debug_sleep_level_override; bool bus_pm; }; int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, bool force); int iwl_power_update_mode(struct iwl_priv *priv, bool force); void iwl_power_initialize(struct iwl_priv *priv); extern bool no_sleep_autoadjust; #endif /* __iwl_power_setting_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/power.c0000644000175000017500000003065712026211315024234 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include #include #include #include #include #include "iwl-io.h" #include "iwl-debug.h" #include "iwl-trans.h" #include "iwl-modparams.h" #include "dev.h" #include "agn.h" #include "commands.h" #include "power.h" /* * Setting power level allows the card to go to sleep when not busy. * * We calculate a sleep command based on the required latency, which * we get from mac80211. In order to handle thermal throttling, we can * also use pre-defined power levels. */ /* * This defines the old power levels. They are still used by default * (level 1) and for thermal throttle (levels 3 through 5) */ struct iwl_power_vec_entry { struct iwl_powertable_cmd cmd; u8 no_dtim; /* number of skip dtim */ }; #define IWL_DTIM_RANGE_0_MAX 2 #define IWL_DTIM_RANGE_1_MAX 10 #define NOSLP cpu_to_le16(0), 0, 0 #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 #define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \ IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \ IWL_POWER_ADVANCE_PM_ENA_MSK) #define ASLP_TOUT(T) cpu_to_le32(T) #define TU_TO_USEC 1024 #define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC) #define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \ cpu_to_le32(X1), \ cpu_to_le32(X2), \ cpu_to_le32(X3), \ cpu_to_le32(X4)} /* default power management (not Tx power) table values */ /* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ /* DTIM 0 - 2 */ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} }; /* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ /* DTIM 3 - 10 */ static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} }; /* for DTIM period > IWL_DTIM_RANGE_1_MAX */ /* DTIM 11 - */ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} }; /* advance power management */ /* DTIM 0 - 2 */ static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = { {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} }; /* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ /* DTIM 3 - 10 */ static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = { {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2} }; /* for DTIM period > IWL_DTIM_RANGE_1_MAX */ /* DTIM 11 - */ static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = { {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} }; static void iwl_static_sleep_cmd(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, enum iwl_power_level lvl, int period) { const struct iwl_power_vec_entry *table; int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; int i; u8 skip; u32 slp_itrvl; if (priv->cfg->adv_pm) { table = apm_range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = apm_range_1; if (period <= IWL_DTIM_RANGE_0_MAX) table = apm_range_0; } else { table = range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = range_1; if (period <= IWL_DTIM_RANGE_0_MAX) table = range_0; } if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM)) memset(cmd, 0, sizeof(*cmd)); else *cmd = table[lvl].cmd; if (period == 0) { skip = 0; period = 1; for (i = 0; i < IWL_POWER_VEC_SIZE; i++) max_sleep[i] = 1; } else { skip = table[lvl].no_dtim; for (i = 0; i < IWL_POWER_VEC_SIZE; i++) max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; } slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); /* figure out the listen interval based on dtim period and skip */ if (slp_itrvl == 0xFF) cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = cpu_to_le32(period * (skip + 1)); slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); if (slp_itrvl > period) cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = cpu_to_le32((slp_itrvl / period) * period); if (skip) cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; else cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; if (priv->cfg->base_params->shadow_reg_enable) cmd->flags |= IWL_POWER_SHADOW_REG_ENA; else cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { if (!priv->cfg->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; } slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); /* enforce max sleep interval */ for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { if (le32_to_cpu(cmd->sleep_interval[i]) > (max_sleep[i] * period)) cmd->sleep_interval[i] = cpu_to_le32(max_sleep[i] * period); if (i != (IWL_POWER_VEC_SIZE - 1)) { if (le32_to_cpu(cmd->sleep_interval[i]) > le32_to_cpu(cmd->sleep_interval[i+1])) cmd->sleep_interval[i] = cmd->sleep_interval[i+1]; } } if (priv->power_data.bus_pm) cmd->flags |= IWL_POWER_PCI_PM_MSK; else cmd->flags &= ~IWL_POWER_PCI_PM_MSK; IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", skip, period); /* The power level here is 0-4 (used as array index), but user expects to see 1-5 (according to spec). */ IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); } static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) { memset(cmd, 0, sizeof(*cmd)); if (priv->power_data.bus_pm) cmd->flags |= IWL_POWER_PCI_PM_MSK; IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); } static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) { IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n", le32_to_cpu(cmd->sleep_interval[0]), le32_to_cpu(cmd->sleep_interval[1]), le32_to_cpu(cmd->sleep_interval[2]), le32_to_cpu(cmd->sleep_interval[3]), le32_to_cpu(cmd->sleep_interval[4])); return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, CMD_SYNC, sizeof(struct iwl_powertable_cmd), cmd); } static void iwl_power_build_cmd(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) { bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; int dtimper; dtimper = priv->hw->conf.ps_dtim_period ?: 1; if (priv->wowlan) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); else if (!priv->cfg->base_params->no_idle_support && priv->hw->conf.flags & IEEE80211_CONF_IDLE) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); else if (iwl_tt_is_low_power_state(priv)) { /* in thermal throttling low power state */ iwl_static_sleep_cmd(priv, cmd, iwl_tt_current_power_mode(priv), dtimper); } else if (!enabled) iwl_power_sleep_cam_cmd(priv, cmd); else if (priv->power_data.debug_sleep_level_override >= 0) iwl_static_sleep_cmd(priv, cmd, priv->power_data.debug_sleep_level_override, dtimper); else { /* Note that the user parameter is 1-5 (according to spec), but we pass 0-4 because it acts as an array index. */ if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 && iwlwifi_mod_params.power_level <= IWL_POWER_NUM) iwl_static_sleep_cmd(priv, cmd, iwlwifi_mod_params.power_level - 1, dtimper); else iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_1, dtimper); } } int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, bool force) { int ret; bool update_chains; lockdep_assert_held(&priv->mutex); /* Don't update the RX chain when chain noise calibration is running */ update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) return 0; if (!iwl_is_ready_rf(priv)) return -EIO; /* scan complete use sleep_power_next, need to be updated */ memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); if (test_bit(STATUS_SCANNING, &priv->status) && !force) { IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); return 0; } if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) iwl_dvm_set_pmi(priv, true); ret = iwl_set_power(priv, cmd); if (!ret) { if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) iwl_dvm_set_pmi(priv, false); if (update_chains) iwl_update_chain_flags(priv); else IWL_DEBUG_POWER(priv, "Cannot update the power, chain noise " "calibration running: %d\n", priv->chain_noise_data.state); memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); } else IWL_ERR(priv, "set power fail, ret = %d", ret); return ret; } int iwl_power_update_mode(struct iwl_priv *priv, bool force) { struct iwl_powertable_cmd cmd; iwl_power_build_cmd(priv, &cmd); return iwl_power_set_mode(priv, &cmd, force); } /* initialize to default */ void iwl_power_initialize(struct iwl_priv *priv) { priv->power_data.bus_pm = priv->trans->pm_support; priv->power_data.debug_sleep_level_override = -1; memset(&priv->power_data.sleep_cmd, 0, sizeof(priv->power_data.sleep_cmd)); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/mac80211.c0000644000175000017500000012402112026211315024221 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iwl-io.h" #include "iwl-trans.h" #include "iwl-op-mode.h" #include "iwl-modparams.h" #include "dev.h" #include "calib.h" #include "agn.h" /***************************************************************************** * * mac80211 entry point functions * *****************************************************************************/ static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP), }, }; static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION), }, }; static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_AP), }, }; static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT), }, }; static const struct ieee80211_iface_combination iwlagn_iface_combinations_dualmode[] = { { .num_different_channels = 1, .max_interfaces = 2, .beacon_int_infra_match = true, .limits = iwlagn_sta_ap_limits, .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), }, { .num_different_channels = 1, .max_interfaces = 2, .limits = iwlagn_2sta_limits, .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), }, }; static const struct ieee80211_iface_combination iwlagn_iface_combinations_p2p[] = { { .num_different_channels = 1, .max_interfaces = 2, .beacon_int_infra_match = true, .limits = iwlagn_p2p_sta_go_limits, .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits), }, { .num_different_channels = 1, .max_interfaces = 2, .limits = iwlagn_p2p_2sta_limits, .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits), }, }; /* * Not a mac80211 entry point function, but it fits in with all the * other mac80211 functions grouped here. */ int iwlagn_mac_setup_register(struct iwl_priv *priv, const struct iwl_ucode_capabilities *capa) { int ret; struct ieee80211_hw *hw = priv->hw; struct iwl_rxon_context *ctx; hw->rate_control_algorithm = "iwl-agn-rs"; /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_NEED_DTIM_PERIOD | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_QUEUE_CONTROL | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_WANT_MONITOR_VIF | IEEE80211_HW_SCAN_WHILE_IDLE; hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; /* * Including the following line will crash some AP's. This * workaround removes the stimulus which causes the crash until * the AP software can be fixed. hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; */ if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE) hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_SUPPORTS_STATIC_SMPS; #ifndef CONFIG_IWLWIFI_EXPERIMENTAL_MFP /* enable 11w if the uCode advertise */ if (capa->flags & IWL_UCODE_TLV_FLAGS_MFP) #endif /* !CONFIG_IWLWIFI_EXPERIMENTAL_MFP */ hw->flags |= IEEE80211_HW_MFP_CAPABLE; hw->sta_data_size = sizeof(struct iwl_station_priv); hw->vif_data_size = sizeof(struct iwl_vif_priv); for_each_context(priv, ctx) { hw->wiphy->interface_modes |= ctx->interface_modes; hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; } BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) { hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p; hw->wiphy->n_iface_combinations = ARRAY_SIZE(iwlagn_iface_combinations_p2p); } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { hw->wiphy->iface_combinations = iwlagn_iface_combinations_dualmode; hw->wiphy->n_iface_combinations = ARRAY_SIZE(iwlagn_iface_combinations_dualmode); } hw->wiphy->max_remain_on_channel_duration = 500; hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | WIPHY_FLAG_IBSS_RSN; #ifdef CONFIG_PM_SLEEP if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && priv->trans->ops->wowlan_suspend && device_can_wakeup(priv->trans->dev)) { hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_EAP_IDENTITY_REQ | WIPHY_WOWLAN_RFKILL_RELEASE; if (!iwlwifi_mod_params.sw_crypto) hw->wiphy->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_GTK_REKEY_FAILURE; hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; hw->wiphy->wowlan.pattern_min_len = IWLAGN_WOWLAN_MIN_PATTERN_LEN; hw->wiphy->wowlan.pattern_max_len = IWLAGN_WOWLAN_MAX_PATTERN_LEN; } #endif if (iwlwifi_mod_params.power_save) hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; else hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; /* we create the 802.11 header and a max-length SSID element */ hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34; /* * We don't use all queues: 4 and 9 are unused and any * aggregation queue gets mapped down to the AC queue. */ hw->queues = IWLAGN_FIRST_AMPDU_QUEUE; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; if (priv->eeprom_data->bands[IEEE80211_BAND_2GHZ].n_channels) priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->eeprom_data->bands[IEEE80211_BAND_2GHZ]; if (priv->eeprom_data->bands[IEEE80211_BAND_5GHZ].n_channels) priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->eeprom_data->bands[IEEE80211_BAND_5GHZ]; hw->wiphy->hw_version = priv->trans->hw_id; iwl_leds_init(priv); ret = ieee80211_register_hw(priv->hw); if (ret) { IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); iwl_leds_exit(priv); return ret; } priv->mac80211_registered = 1; return 0; } void iwlagn_mac_unregister(struct iwl_priv *priv) { if (!priv->mac80211_registered) return; iwl_leds_exit(priv); ieee80211_unregister_hw(priv->hw); priv->mac80211_registered = 0; } static int __iwl_up(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; int ret; lockdep_assert_held(&priv->mutex); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); return -EIO; } for_each_context(priv, ctx) { ret = iwlagn_alloc_bcast_station(priv, ctx); if (ret) { iwl_dealloc_bcast_stations(priv); return ret; } } ret = iwl_run_init_ucode(priv); if (ret) { IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); goto error; } ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); if (ret) { IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); goto error; } ret = iwl_alive_start(priv); if (ret) goto error; return 0; error: set_bit(STATUS_EXIT_PENDING, &priv->status); iwl_down(priv); clear_bit(STATUS_EXIT_PENDING, &priv->status); IWL_ERR(priv, "Unable to initialize device.\n"); return ret; } static int iwlagn_mac_start(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret; IWL_DEBUG_MAC80211(priv, "enter\n"); /* we should be verifying the device is ready to be opened */ mutex_lock(&priv->mutex); ret = __iwl_up(priv); mutex_unlock(&priv->mutex); if (ret) return ret; IWL_DEBUG_INFO(priv, "Start UP work done.\n"); /* Now we should be done, and the READY bit should be set. */ if (WARN_ON(!test_bit(STATUS_READY, &priv->status))) ret = -EIO; iwlagn_led_enable(priv); priv->is_open = 1; IWL_DEBUG_MAC80211(priv, "leave\n"); return 0; } static void iwlagn_mac_stop(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); IWL_DEBUG_MAC80211(priv, "enter\n"); if (!priv->is_open) return; priv->is_open = 0; mutex_lock(&priv->mutex); iwl_down(priv); mutex_unlock(&priv->mutex); iwl_cancel_deferred_work(priv); flush_workqueue(priv->workqueue); /* User space software may expect getting rfkill changes * even if interface is down, trans->down will leave the RF * kill interrupt enabled */ iwl_trans_stop_hw(priv->trans, false); IWL_DEBUG_MAC80211(priv, "leave\n"); } static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *data) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); if (iwlwifi_mod_params.sw_crypto) return; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) goto out; memcpy(priv->kek, data->kek, NL80211_KEK_LEN); memcpy(priv->kck, data->kck, NL80211_KCK_LEN); priv->replay_ctr = cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); priv->have_rekey_data = true; out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); } #ifdef CONFIG_PM_SLEEP static int iwlagn_mac_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; int ret; if (WARN_ON(!wowlan)) return -EINVAL; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); /* Don't attempt WoWLAN when not associated, tear down instead. */ if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || !iwl_is_associated_ctx(ctx)) { ret = 1; goto out; } ret = iwlagn_suspend(priv, wowlan); if (ret) goto error; iwl_trans_wowlan_suspend(priv->trans); goto out; error: priv->wowlan = false; iwlagn_prepare_restart(priv); ieee80211_restart_hw(priv->hw); out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return ret; } static int iwlagn_mac_resume(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct ieee80211_vif *vif; unsigned long flags; u32 base, status = 0xffffffff; int ret = -EIO; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); base = priv->device_pointers.error_event_table; if (iwlagn_hw_valid_rtc_data_addr(base)) { spin_lock_irqsave(&priv->trans->reg_lock, flags); ret = iwl_grab_nic_access_silent(priv->trans); if (likely(ret == 0)) { iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, base); status = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); iwl_release_nic_access(priv->trans); } spin_unlock_irqrestore(&priv->trans->reg_lock, flags); #ifdef CONFIG_IWLWIFI_DEBUGFS if (ret == 0) { const struct fw_img *img; img = &(priv->fw->img[IWL_UCODE_WOWLAN]); if (!priv->wowlan_sram) { priv->wowlan_sram = kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len, GFP_KERNEL); } if (priv->wowlan_sram) _iwl_read_targ_mem_dwords( priv->trans, 0x800000, priv->wowlan_sram, img->sec[IWL_UCODE_SECTION_DATA].len / 4); } #endif } /* we'll clear ctx->vif during iwlagn_prepare_restart() */ vif = ctx->vif; priv->wowlan = false; iwlagn_prepare_restart(priv); memset((void *)&ctx->active, 0, sizeof(ctx->active)); iwl_connection_init_rx_config(priv, ctx); iwlagn_set_rxon_chain(priv, ctx); mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); ieee80211_resume_disconnect(vif); return 1; } static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); device_set_wakeup_enable(priv->trans->dev, enabled); } #endif static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); if (iwlagn_tx_skb(priv, control->sta, skb)) dev_kfree_skb_any(skb); } static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); } static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; struct iwl_rxon_context *ctx = vif_priv->ctx; int ret; bool is_default_wep_key = false; IWL_DEBUG_MAC80211(priv, "enter\n"); if (iwlwifi_mod_params.sw_crypto) { IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); return -EOPNOTSUPP; } switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; /* fall through */ case WLAN_CIPHER_SUITE_CCMP: key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; break; default: break; } /* * We could program these keys into the hardware as well, but we * don't expect much multicast traffic in IBSS and having keys * for more stations is probably more useful. * * Mark key TX-only and return 0. */ if (vif->type == NL80211_IFTYPE_ADHOC && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { key->hw_key_idx = WEP_INVALID_OFFSET; return 0; } /* If they key was TX-only, accept deletion */ if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) return 0; mutex_lock(&priv->mutex); iwl_scan_cancel_timeout(priv, 100); BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); /* * If we are getting WEP group key and we didn't receive any key mapping * so far, we are in legacy wep mode (group key only), otherwise we are * in 1X mode. * In legacy wep mode, we use another host command to the uCode. */ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { if (cmd == SET_KEY) is_default_wep_key = !ctx->key_mapping_keys; else is_default_wep_key = key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; } switch (cmd) { case SET_KEY: if (is_default_wep_key) { ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); break; } ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); if (ret) { /* * can't add key for RX, but we don't need it * in the device for TX so still return 0 */ ret = 0; key->hw_key_idx = WEP_INVALID_OFFSET; } IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); break; case DISABLE_KEY: if (is_default_wep_key) ret = iwl_remove_default_wep_key(priv, ctx, key); else ret = iwl_remove_dynamic_key(priv, ctx, key, sta); IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); break; default: ret = -EINVAL; } mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return ret; } static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", sta->addr, tid); if (!(priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE)) return -EACCES; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); switch (action) { case IEEE80211_AMPDU_RX_START: if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) break; IWL_DEBUG_HT(priv, "start Rx\n"); ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); break; case IEEE80211_AMPDU_RX_STOP: IWL_DEBUG_HT(priv, "stop Rx\n"); ret = iwl_sta_rx_agg_stop(priv, sta, tid); break; case IEEE80211_AMPDU_TX_START: if (!priv->trans->ops->txq_enable) break; if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) break; IWL_DEBUG_HT(priv, "start Tx\n"); ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); break; case IEEE80211_AMPDU_TX_STOP: IWL_DEBUG_HT(priv, "stop Tx\n"); ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); if ((ret == 0) && (priv->agg_tids_count > 0)) { priv->agg_tids_count--; IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", priv->agg_tids_count); } if (!priv->agg_tids_count && priv->hw_params.use_rts_for_aggregation) { /* * switch off RTS/CTS if it was previously enabled */ sta_priv->lq_sta.lq.general_params.flags &= ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), &sta_priv->lq_sta.lq, CMD_ASYNC, false); } break; case IEEE80211_AMPDU_TX_OPERATIONAL: ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size); break; } mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return ret; } static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; bool is_ap = vif->type == NL80211_IFTYPE_STATION; int ret; u8 sta_id; IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", sta->addr); sta_priv->sta_id = IWL_INVALID_STATION; atomic_set(&sta_priv->pending_frames, 0); if (vif->type == NL80211_IFTYPE_AP) sta_priv->client = true; ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, is_ap, sta, &sta_id); if (ret) { IWL_ERR(priv, "Unable to add station %pM (%d)\n", sta->addr, ret); /* Should we return success if return code is EEXIST ? */ return ret; } sta_priv->sta_id = sta_id; return 0; } static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; int ret; IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr); if (vif->type == NL80211_IFTYPE_STATION) { /* * Station will be removed from device when the RXON * is set to unassociated -- just deactivate it here * to avoid re-programming it. */ ret = 0; iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr); } else { ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); if (ret) IWL_DEBUG_QUIET_RFKILL(priv, "Error removing station %pM\n", sta->addr); } return ret; } static int iwlagn_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; enum { NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT, } op = NONE; int ret; IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n", sta->addr, old_state, new_state); mutex_lock(&priv->mutex); if (vif->type == NL80211_IFTYPE_STATION) { if (old_state == IEEE80211_STA_NOTEXIST && new_state == IEEE80211_STA_NONE) op = ADD; else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST) op = REMOVE; else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) op = HT_RATE_INIT; } else { if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) op = ADD_RATE_INIT; else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) op = REMOVE; } switch (op) { case ADD: ret = iwlagn_mac_sta_add(hw, vif, sta); if (ret) break; /* * Clear the in-progress flag, the AP station entry was added * but we'll initialize LQ only when we've associated (which * would also clear the in-progress flag). This is necessary * in case we never initialize LQ because association fails. */ spin_lock_bh(&priv->sta_lock); priv->stations[iwl_sta_id(sta)].used &= ~IWL_STA_UCODE_INPROGRESS; spin_unlock_bh(&priv->sta_lock); break; case REMOVE: ret = iwlagn_mac_sta_remove(hw, vif, sta); break; case ADD_RATE_INIT: ret = iwlagn_mac_sta_add(hw, vif, sta); if (ret) break; /* Initialize rate scaling */ IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM\n", sta->addr); iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); ret = 0; break; case HT_RATE_INIT: /* Initialize rate scaling */ ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta); if (ret) break; IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM\n", sta->addr); iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); ret = 0; break; default: ret = 0; break; } /* * mac80211 might WARN if we fail, but due the way we * (badly) handle hard rfkill, we might fail here */ if (iwl_is_rfkill(priv)) ret = 0; mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return ret; } static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = ch_switch->channel; struct iwl_ht_config *ht_conf = &priv->current_ht_config; /* * MULTI-FIXME * When we add support for multiple interfaces, we need to * revisit this. The channel switch command in the device * only affects the BSS context, but what does that really * mean? And what if we get a CSA on the second interface? * This needs a lot of work. */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; u16 ch; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); if (iwl_is_rfkill(priv)) goto out; if (test_bit(STATUS_EXIT_PENDING, &priv->status) || test_bit(STATUS_SCANNING, &priv->status) || test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) goto out; if (!iwl_is_associated_ctx(ctx)) goto out; if (!priv->lib->set_channel_switch) goto out; ch = channel->hw_value; if (le16_to_cpu(ctx->active.channel) == ch) goto out; priv->current_ht_config.smps = conf->smps_mode; /* Configure HT40 channels */ ctx->ht.enabled = conf_is_ht(conf); if (ctx->ht.enabled) iwlagn_config_ht40(conf, ctx); else ctx->ht.is_40mhz = false; if ((le16_to_cpu(ctx->staging.channel) != ch)) ctx->staging.flags = 0; iwl_set_rxon_channel(priv, channel, ctx); iwl_set_rxon_ht(priv, ht_conf); iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); /* * at this point, staging_rxon has the * configuration for channel switch */ set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); priv->switch_channel = cpu_to_le16(ch); if (priv->lib->set_channel_switch(priv, ch_switch)) { clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); priv->switch_channel = 0; ieee80211_chswitch_done(ctx->vif, false); } out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); } void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) { /* * MULTI-FIXME * See iwlagn_mac_channel_switch. */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) ieee80211_chswitch_done(ctx->vif, is_success); } static void iwlagn_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); __le32 filter_or = 0, filter_nand = 0; struct iwl_rxon_context *ctx; #define CHK(test, flag) do { \ if (*total_flags & (test)) \ filter_or |= (flag); \ else \ filter_nand |= (flag); \ } while (0) IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", changed_flags, *total_flags); CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); #undef CHK mutex_lock(&priv->mutex); for_each_context(priv, ctx) { ctx->staging.filter_flags &= ~filter_nand; ctx->staging.filter_flags |= filter_or; /* * Not committing directly because hardware can perform a scan, * but we'll eventually commit the filter flags change anyway. */ } mutex_unlock(&priv->mutex); /* * Receiving all multicast frames is always enabled by the * default flags setup in iwl_connection_init_rx_config() * since we currently do not support programming multicast * filters into the device. */ *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; } static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); mutex_lock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "enter\n"); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); goto done; } if (iwl_is_rfkill(priv)) { IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); goto done; } /* * mac80211 will not push any more frames for transmit * until the flush is completed */ if (drop) { IWL_DEBUG_MAC80211(priv, "send flush command\n"); if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { IWL_ERR(priv, "flush request fail\n"); goto done; } } IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n"); iwl_trans_wait_tx_queue_empty(priv->trans); done: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); } static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type, int duration) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; int err = 0; if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) return -EOPNOTSUPP; if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT))) return -EOPNOTSUPP; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); if (test_bit(STATUS_SCAN_HW, &priv->status)) { /* mac80211 should not scan while ROC or ROC while scanning */ if (WARN_ON_ONCE(priv->scan_type != IWL_SCAN_RADIO_RESET)) { err = -EBUSY; goto out; } iwl_scan_cancel_timeout(priv, 100); if (test_bit(STATUS_SCAN_HW, &priv->status)) { err = -EBUSY; goto out; } } priv->hw_roc_channel = channel; priv->hw_roc_chantype = channel_type; /* convert from ms to TU */ priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024); priv->hw_roc_start_notified = false; cancel_delayed_work(&priv->hw_roc_disable_work); if (!ctx->is_active) { static const struct iwl_qos_info default_qos_data = { .def_qos_parm = { .ac[0] = { .cw_min = cpu_to_le16(3), .cw_max = cpu_to_le16(7), .aifsn = 2, .edca_txop = cpu_to_le16(1504), }, .ac[1] = { .cw_min = cpu_to_le16(7), .cw_max = cpu_to_le16(15), .aifsn = 2, .edca_txop = cpu_to_le16(3008), }, .ac[2] = { .cw_min = cpu_to_le16(15), .cw_max = cpu_to_le16(1023), .aifsn = 3, }, .ac[3] = { .cw_min = cpu_to_le16(15), .cw_max = cpu_to_le16(1023), .aifsn = 7, }, }, }; ctx->is_active = true; ctx->qos_data = default_qos_data; ctx->staging.dev_type = RXON_DEV_TYPE_P2P; memcpy(ctx->staging.node_addr, priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, ETH_ALEN); memcpy(ctx->staging.bssid_addr, priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, ETH_ALEN); err = iwlagn_commit_rxon(priv, ctx); if (err) goto out; ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK | RXON_FILTER_PROMISC_MSK | RXON_FILTER_CTL2HOST_MSK; err = iwlagn_commit_rxon(priv, ctx); if (err) { iwlagn_disable_roc(priv); goto out; } priv->hw_roc_setup = true; } err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band); if (err) iwlagn_disable_roc(priv); out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return err; } static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) return -EOPNOTSUPP; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); iwl_scan_cancel_timeout(priv, priv->hw_roc_duration); iwlagn_disable_roc(priv); mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return 0; } static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, enum ieee80211_rssi_event rssi_event) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) { if (rssi_event == RSSI_EVENT_LOW) priv->bt_enable_pspoll = true; else if (rssi_event == RSSI_EVENT_HIGH) priv->bt_enable_pspoll = false; iwlagn_send_advance_bt_config(priv); } else { IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," "ignoring RSSI callback\n"); } mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); } static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); queue_work(priv->workqueue, &priv->beacon_update); return 0; } static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; struct iwl_rxon_context *ctx = vif_priv->ctx; int q; if (WARN_ON(!ctx)) return -EINVAL; IWL_DEBUG_MAC80211(priv, "enter\n"); if (!iwl_is_ready_rf(priv)) { IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); return -EIO; } if (queue >= AC_NUM) { IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); return 0; } q = AC_NUM - 1 - queue; mutex_lock(&priv->mutex); ctx->qos_data.def_qos_parm.ac[q].cw_min = cpu_to_le16(params->cw_min); ctx->qos_data.def_qos_parm.ac[q].cw_max = cpu_to_le16(params->cw_max); ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; ctx->qos_data.def_qos_parm.ac[q].edca_txop = cpu_to_le16((params->txop * 32)); ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return 0; } static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); return priv->ibss_manager == IWL_IBSS_MANAGER; } static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { iwl_connection_init_rx_config(priv, ctx); iwlagn_set_rxon_chain(priv, ctx); return iwlagn_commit_rxon(priv, ctx); } static int iwl_setup_interface(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { struct ieee80211_vif *vif = ctx->vif; int err, ac; lockdep_assert_held(&priv->mutex); /* * This variable will be correct only when there's just * a single context, but all code using it is for hardware * that supports only one context. */ priv->iw_mode = vif->type; ctx->is_active = true; err = iwl_set_mode(priv, ctx); if (err) { if (!ctx->always_active) ctx->is_active = false; return err; } if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && vif->type == NL80211_IFTYPE_ADHOC) { /* * pretend to have high BT traffic as long as we * are operating in IBSS mode, as this will cause * the rate scaling etc. to behave as intended. */ priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; } /* set up queue mappings */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) vif->hw_queue[ac] = ctx->ac_to_queue[ac]; if (vif->type == NL80211_IFTYPE_AP) vif->cab_queue = ctx->mcast_queue; else vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; return 0; } static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; struct iwl_rxon_context *tmp, *ctx = NULL; int err; enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); bool reset = false; IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", viftype, vif->addr); cancel_delayed_work_sync(&priv->hw_roc_disable_work); mutex_lock(&priv->mutex); iwlagn_disable_roc(priv); if (!iwl_is_ready_rf(priv)) { IWL_WARN(priv, "Try to add interface when device not ready\n"); err = -EINVAL; goto out; } for_each_context(priv, tmp) { u32 possible_modes = tmp->interface_modes | tmp->exclusive_interface_modes; if (tmp->vif) { /* On reset we need to add the same interface again */ if (tmp->vif == vif) { reset = true; ctx = tmp; break; } /* check if this busy context is exclusive */ if (tmp->exclusive_interface_modes & BIT(tmp->vif->type)) { err = -EINVAL; goto out; } continue; } if (!(possible_modes & BIT(viftype))) continue; /* have maybe usable context w/o interface */ ctx = tmp; break; } if (!ctx) { err = -EOPNOTSUPP; goto out; } vif_priv->ctx = ctx; ctx->vif = vif; err = iwl_setup_interface(priv, ctx); if (!err || reset) goto out; ctx->vif = NULL; priv->iw_mode = NL80211_IFTYPE_STATION; out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return err; } static void iwl_teardown_interface(struct iwl_priv *priv, struct ieee80211_vif *vif, bool mode_change) { struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); lockdep_assert_held(&priv->mutex); if (priv->scan_vif == vif) { iwl_scan_cancel_timeout(priv, 200); iwl_force_scan_end(priv); } if (!mode_change) { iwl_set_mode(priv, ctx); if (!ctx->always_active) ctx->is_active = false; } /* * When removing the IBSS interface, overwrite the * BT traffic load with the stored one from the last * notification, if any. If this is a device that * doesn't implement this, this has no effect since * both values are the same and zero. */ if (vif->type == NL80211_IFTYPE_ADHOC) priv->bt_traffic_load = priv->last_bt_traffic_load; } static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); if (WARN_ON(ctx->vif != vif)) { struct iwl_rxon_context *tmp; IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); for_each_context(priv, tmp) IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", tmp->ctxid, tmp, tmp->vif); } ctx->vif = NULL; iwl_teardown_interface(priv, vif, false); mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); } static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx, *tmp; enum nl80211_iftype newviftype = newtype; u32 interface_modes; int err; IWL_DEBUG_MAC80211(priv, "enter\n"); newtype = ieee80211_iftype_p2p(newtype, newp2p); mutex_lock(&priv->mutex); ctx = iwl_rxon_ctx_from_vif(vif); /* * To simplify this code, only support changes on the * BSS context. The PAN context is usually reassigned * by creating/removing P2P interfaces anyway. */ if (ctx->ctxid != IWL_RXON_CTX_BSS) { err = -EBUSY; goto out; } if (!ctx->vif || !iwl_is_ready_rf(priv)) { /* * Huh? But wait ... this can maybe happen when * we're in the middle of a firmware restart! */ err = -EBUSY; goto out; } /* Check if the switch is supported in the same context */ interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; if (!(interface_modes & BIT(newtype))) { err = -EBUSY; goto out; } if (ctx->exclusive_interface_modes & BIT(newtype)) { for_each_context(priv, tmp) { if (ctx == tmp) continue; if (!tmp->is_active) continue; /* * The current mode switch would be exclusive, but * another context is active ... refuse the switch. */ err = -EBUSY; goto out; } } /* success */ iwl_teardown_interface(priv, vif, true); vif->type = newviftype; vif->p2p = newp2p; err = iwl_setup_interface(priv, ctx); WARN_ON(err); /* * We've switched internally, but submitting to the * device may have failed for some reason. Mask this * error, because otherwise mac80211 will not switch * (and set the interface type back) and we'll be * out of sync with it. */ err = 0; out: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); return err; } static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret; IWL_DEBUG_MAC80211(priv, "enter\n"); if (req->n_channels == 0) return -EINVAL; mutex_lock(&priv->mutex); /* * If an internal scan is in progress, just set * up the scan_request as per above. */ if (priv->scan_type != IWL_SCAN_NORMAL) { IWL_DEBUG_SCAN(priv, "SCAN request during internal scan - defer\n"); priv->scan_request = req; priv->scan_vif = vif; ret = 0; } else { priv->scan_request = req; priv->scan_vif = vif; /* * mac80211 will only ask for one band at a time * so using channels[0] here is ok */ ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, req->channels[0]->band); if (ret) { priv->scan_request = NULL; priv->scan_vif = NULL; } } IWL_DEBUG_MAC80211(priv, "leave\n"); mutex_unlock(&priv->mutex); return ret; } static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) { struct iwl_addsta_cmd cmd = { .mode = STA_CONTROL_MODIFY_MSK, .station_flags_msk = STA_FLG_PWR_SAVE_MSK, .sta.sta_id = sta_id, }; iwl_send_add_sta(priv, &cmd, CMD_ASYNC); } static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; int sta_id; IWL_DEBUG_MAC80211(priv, "enter\n"); switch (cmd) { case STA_NOTIFY_SLEEP: WARN_ON(!sta_priv->client); sta_priv->asleep = true; if (atomic_read(&sta_priv->pending_frames) > 0) ieee80211_sta_block_awake(hw, sta, true); break; case STA_NOTIFY_AWAKE: WARN_ON(!sta_priv->client); if (!sta_priv->asleep) break; sta_priv->asleep = false; sta_id = iwl_sta_id(sta); if (sta_id != IWL_INVALID_STATION) iwl_sta_modify_ps_wake(priv, sta_id); break; default: break; } IWL_DEBUG_MAC80211(priv, "leave\n"); } struct ieee80211_ops iwlagn_hw_ops = { .tx = iwlagn_mac_tx, .start = iwlagn_mac_start, .stop = iwlagn_mac_stop, #ifdef CONFIG_PM_SLEEP .suspend = iwlagn_mac_suspend, .resume = iwlagn_mac_resume, .set_wakeup = iwlagn_mac_set_wakeup, #endif .add_interface = iwlagn_mac_add_interface, .remove_interface = iwlagn_mac_remove_interface, .change_interface = iwlagn_mac_change_interface, .config = iwlagn_mac_config, .configure_filter = iwlagn_configure_filter, .set_key = iwlagn_mac_set_key, .update_tkip_key = iwlagn_mac_update_tkip_key, .set_rekey_data = iwlagn_mac_set_rekey_data, .conf_tx = iwlagn_mac_conf_tx, .bss_info_changed = iwlagn_bss_info_changed, .ampdu_action = iwlagn_mac_ampdu_action, .hw_scan = iwlagn_mac_hw_scan, .sta_notify = iwlagn_mac_sta_notify, .sta_state = iwlagn_mac_sta_state, .channel_switch = iwlagn_mac_channel_switch, .flush = iwlagn_mac_flush, .tx_last_beacon = iwlagn_mac_tx_last_beacon, .remain_on_channel = iwlagn_mac_remain_on_channel, .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel, .rssi_callback = iwlagn_mac_rssi_callback, CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd) CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump) .set_tim = iwlagn_mac_set_tim, }; /* This function both allocates and initializes hw and priv. */ struct ieee80211_hw *iwl_alloc_all(void) { struct iwl_priv *priv; struct iwl_op_mode *op_mode; /* mac80211 allocates memory for this device instance, including * space for this driver's private structure */ struct ieee80211_hw *hw; hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) + sizeof(struct iwl_op_mode), &iwlagn_hw_ops); if (!hw) goto out; op_mode = hw->priv; priv = IWL_OP_MODE_GET_DVM(op_mode); priv->hw = hw; out: return hw; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/lib.c0000644000175000017500000011212612026211315023636 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include "iwl-io.h" #include "iwl-agn-hw.h" #include "iwl-trans.h" #include "iwl-modparams.h" #include "dev.h" #include "agn.h" int iwlagn_hw_valid_rtc_data_addr(u32 addr) { return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && (addr < IWLAGN_RTC_DATA_UPPER_BOUND); } int iwlagn_send_tx_power(struct iwl_priv *priv) { struct iwlagn_tx_power_dbm_cmd tx_power_cmd; u8 tx_ant_cfg_cmd; if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status), "TX Power requested while scanning!\n")) return -EAGAIN; /* half dBm need to multiply */ tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); if (tx_power_cmd.global_lmt > priv->eeprom_data->max_tx_pwr_half_dbm) { /* * For the newer devices which using enhanced/extend tx power * table in EEPROM, the format is in half dBm. driver need to * convert to dBm format before report to mac80211. * By doing so, there is a possibility of 1/2 dBm resolution * lost. driver will perform "round-up" operation before * reporting, but it will cause 1/2 dBm tx power over the * regulatory limit. Perform the checking here, if the * "tx_power_user_lmt" is higher than EEPROM value (in * half-dBm format), lower the tx power based on EEPROM */ tx_power_cmd.global_lmt = priv->eeprom_data->max_tx_pwr_half_dbm; } tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; if (IWL_UCODE_API(priv->fw->ucode_ver) == 1) tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1; else tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, CMD_SYNC, sizeof(tx_power_cmd), &tx_power_cmd); } void iwlagn_temperature(struct iwl_priv *priv) { lockdep_assert_held(&priv->statistics.lock); /* store temperature from correct statistics (in Celsius) */ priv->temperature = le32_to_cpu(priv->statistics.common.temperature); iwl_tt_handler(priv); } int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) { int idx = 0; int band_offset = 0; /* HT rate format: mac80211 wants an MCS number, which is just LSB */ if (rate_n_flags & RATE_MCS_HT_MSK) { idx = (rate_n_flags & 0xff); return idx; /* Legacy rate format, search for match in table */ } else { if (band == IEEE80211_BAND_5GHZ) band_offset = IWL_FIRST_OFDM_RATE; for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF)) return idx - band_offset; } return -1; } int iwlagn_manage_ibss_station(struct iwl_priv *priv, struct ieee80211_vif *vif, bool add) { struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; if (add) return iwlagn_add_bssid_station(priv, vif_priv->ctx, vif->bss_conf.bssid, &vif_priv->ibss_bssid_sta_id); return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, vif->bss_conf.bssid); } /** * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode * * pre-requirements: * 1. acquire mutex before calling * 2. make sure rf is on and not in exit state */ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) { struct iwl_txfifo_flush_cmd flush_cmd; struct iwl_host_cmd cmd = { .id = REPLY_TXFIFO_FLUSH, .len = { sizeof(struct iwl_txfifo_flush_cmd), }, .flags = CMD_SYNC, .data = { &flush_cmd, }, }; might_sleep(); memset(&flush_cmd, 0, sizeof(flush_cmd)); if (flush_control & BIT(IWL_RXON_CTX_BSS)) flush_cmd.fifo_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK; if ((flush_control & BIT(IWL_RXON_CTX_PAN)) && (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) flush_cmd.fifo_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | IWL_PAN_SCD_MULTICAST_MSK; if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE) flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", flush_cmd.fifo_control); flush_cmd.flush_control = cpu_to_le16(flush_control); return iwl_dvm_send_cmd(priv, &cmd); } void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control) { mutex_lock(&priv->mutex); ieee80211_stop_queues(priv->hw); if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { IWL_ERR(priv, "flush request fail\n"); goto done; } IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); iwl_trans_wait_tx_queue_empty(priv->trans); done: ieee80211_wake_queues(priv->hw); mutex_unlock(&priv->mutex); } /* * BT coex */ /* Notmal TDM */ static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaeaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xcc00ff28), cpu_to_le32(0x0000aaaa), cpu_to_le32(0xcc00aaaa), cpu_to_le32(0x0000aaaa), cpu_to_le32(0xc0004000), cpu_to_le32(0x00004000), cpu_to_le32(0xf0005000), cpu_to_le32(0xf0005000), }; /* Loose Coex */ static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaeaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xcc00ff28), cpu_to_le32(0x0000aaaa), cpu_to_le32(0xcc00aaaa), cpu_to_le32(0x0000aaaa), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0xf0005000), cpu_to_le32(0xf0005000), }; /* Full concurrency */ static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), }; void iwlagn_send_advance_bt_config(struct iwl_priv *priv) { struct iwl_basic_bt_cmd basic = { .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, }; struct iwl_bt_cmd_v1 bt_cmd_v1; struct iwl_bt_cmd_v2 bt_cmd_v2; int ret; BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != sizeof(basic.bt3_lookup_table)); if (priv->cfg->bt_params) { /* * newer generation of devices (2000 series and newer) * use the version 2 of the bt command * we need to make sure sending the host command * with correct data structure to avoid uCode assert */ if (priv->cfg->bt_params->bt_session_2) { bt_cmd_v2.prio_boost = cpu_to_le32( priv->cfg->bt_params->bt_prio_boost); bt_cmd_v2.tx_prio_boost = 0; bt_cmd_v2.rx_prio_boost = 0; } else { /* older version only has 8 bits */ WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF); bt_cmd_v1.prio_boost = priv->cfg->bt_params->bt_prio_boost; bt_cmd_v1.tx_prio_boost = 0; bt_cmd_v1.rx_prio_boost = 0; } } else { IWL_ERR(priv, "failed to construct BT Coex Config\n"); return; } /* * Possible situations when BT needs to take over for receive, * at the same time where STA needs to response to AP's frame(s), * reduce the tx power of the required response frames, by that, * allow the concurrent BT receive & WiFi transmit * (BT - ANT A, WiFi -ANT B), without interference to one another * * Reduced tx power apply to control frames only (ACK/Back/CTS) * when indicated by the BT config command */ basic.kill_ack_mask = priv->kill_ack_mask; basic.kill_cts_mask = priv->kill_cts_mask; if (priv->reduced_txpower) basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR; basic.valid = priv->bt_valid; /* * Configure BT coex mode to "no coexistence" when the * user disabled BT coexistence, we have no interface * (might be in monitor mode), or the interface is in * IBSS mode (no proper uCode support for coex then). */ if (!iwlwifi_mod_params.bt_coex_active || priv->iw_mode == NL80211_IFTYPE_ADHOC) { basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; } else { basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << IWLAGN_BT_FLAG_COEX_MODE_SHIFT; if (!priv->bt_enable_pspoll) basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; else basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; if (priv->bt_ch_announce) basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags); } priv->bt_enable_flag = basic.flags; if (priv->bt_full_concurrent) memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, sizeof(iwlagn_concurrent_lookup)); else memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, sizeof(iwlagn_def_3w_lookup)); IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n", basic.flags ? "active" : "disabled", priv->bt_full_concurrent ? "full concurrency" : "3-wire"); if (priv->cfg->bt_params->bt_session_2) { memcpy(&bt_cmd_v2.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, CMD_SYNC, sizeof(bt_cmd_v2), &bt_cmd_v2); } else { memcpy(&bt_cmd_v1.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, CMD_SYNC, sizeof(bt_cmd_v1), &bt_cmd_v1); } if (ret) IWL_ERR(priv, "failed to send BT Coex Config\n"); } void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena) { struct iwl_rxon_context *ctx, *found_ctx = NULL; bool found_ap = false; lockdep_assert_held(&priv->mutex); /* Check whether AP or GO mode is active. */ if (rssi_ena) { for_each_context(priv, ctx) { if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP && iwl_is_associated_ctx(ctx)) { found_ap = true; break; } } } /* * If disable was received or If GO/AP mode, disable RSSI * measurements. */ if (!rssi_ena || found_ap) { if (priv->cur_rssi_ctx) { ctx = priv->cur_rssi_ctx; ieee80211_disable_rssi_reports(ctx->vif); priv->cur_rssi_ctx = NULL; } return; } /* * If rssi measurements need to be enabled, consider all cases now. * Figure out how many contexts are active. */ for_each_context(priv, ctx) { if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && iwl_is_associated_ctx(ctx)) { found_ctx = ctx; break; } } /* * rssi monitor already enabled for the correct interface...nothing * to do. */ if (found_ctx == priv->cur_rssi_ctx) return; /* * Figure out if rssi monitor is currently enabled, and needs * to be changed. If rssi monitor is already enabled, disable * it first else just enable rssi measurements on the * interface found above. */ if (priv->cur_rssi_ctx) { ctx = priv->cur_rssi_ctx; if (ctx->vif) ieee80211_disable_rssi_reports(ctx->vif); } priv->cur_rssi_ctx = found_ctx; if (!found_ctx) return; ieee80211_enable_rssi_reports(found_ctx->vif, IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD, IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD); } static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg) { return BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3 >> BT_UART_MSG_FRAME3SCOESCO_POS; } static void iwlagn_bt_traffic_change_work(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, bt_traffic_change_work); struct iwl_rxon_context *ctx; int smps_request = -1; if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { /* bt coex disabled */ return; } /* * Note: bt_traffic_load can be overridden by scan complete and * coex profile notifications. Ignore that since only bad consequence * can be not matching debug print with actual state. */ IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n", priv->bt_traffic_load); switch (priv->bt_traffic_load) { case IWL_BT_COEX_TRAFFIC_LOAD_NONE: if (priv->bt_status) smps_request = IEEE80211_SMPS_DYNAMIC; else smps_request = IEEE80211_SMPS_AUTOMATIC; break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: smps_request = IEEE80211_SMPS_DYNAMIC; break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: smps_request = IEEE80211_SMPS_STATIC; break; default: IWL_ERR(priv, "Invalid BT traffic load: %d\n", priv->bt_traffic_load); break; } mutex_lock(&priv->mutex); /* * We can not send command to firmware while scanning. When the scan * complete we will schedule this work again. We do check with mutex * locked to prevent new scan request to arrive. We do not check * STATUS_SCANNING to avoid race when queue_work two times from * different notifications, but quit and not perform any work at all. */ if (test_bit(STATUS_SCAN_HW, &priv->status)) goto out; iwl_update_chain_flags(priv); if (smps_request != -1) { priv->current_ht_config.smps = smps_request; for_each_context(priv, ctx) { if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) ieee80211_request_smps(ctx->vif, smps_request); } } /* * Dynamic PS poll related functionality. Adjust RSSI measurements if * necessary. */ iwlagn_bt_coex_rssi_monitor(priv); out: mutex_unlock(&priv->mutex); } /* * If BT sco traffic, and RSSI monitor is enabled, move measurements to the * correct interface or disable it if this is the last interface to be * removed. */ void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv) { if (priv->bt_is_sco && priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS) iwlagn_bt_adjust_rssi_monitor(priv, true); else iwlagn_bt_adjust_rssi_monitor(priv, false); } static void iwlagn_print_uartmsg(struct iwl_priv *priv, struct iwl_bt_uart_msg *uart_msg) { IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, " "Update Req = 0x%X\n", (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >> BT_UART_MSG_FRAME1MSGTYPE_POS, (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >> BT_UART_MSG_FRAME1SSN_POS, (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >> BT_UART_MSG_FRAME1UPDATEREQ_POS); IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, " "Chl_SeqN = 0x%X, In band = 0x%X\n", (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >> BT_UART_MSG_FRAME2OPENCONNECTIONS_POS, (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >> BT_UART_MSG_FRAME2TRAFFICLOAD_POS, (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >> BT_UART_MSG_FRAME2CHLSEQN_POS, (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >> BT_UART_MSG_FRAME2INBAND_POS); IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, " "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n", (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> BT_UART_MSG_FRAME3SCOESCO_POS, (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >> BT_UART_MSG_FRAME3SNIFF_POS, (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >> BT_UART_MSG_FRAME3A2DP_POS, (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >> BT_UART_MSG_FRAME3ACL_POS, (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >> BT_UART_MSG_FRAME3MASTER_POS, (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >> BT_UART_MSG_FRAME3OBEX_POS); IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n", (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >> BT_UART_MSG_FRAME4IDLEDURATION_POS); IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, " "eSCO Retransmissions = 0x%X\n", (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >> BT_UART_MSG_FRAME5TXACTIVITY_POS, (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >> BT_UART_MSG_FRAME5RXACTIVITY_POS, (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >> BT_UART_MSG_FRAME5ESCORETRANSMIT_POS); IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n", (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >> BT_UART_MSG_FRAME6SNIFFINTERVAL_POS, (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> BT_UART_MSG_FRAME6DISCOVERABLE_POS); IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = " "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n", (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> BT_UART_MSG_FRAME7PAGE_POS, (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> BT_UART_MSG_FRAME7INQUIRY_POS, (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> BT_UART_MSG_FRAME7CONNECTABLE_POS); } static bool iwlagn_set_kill_msk(struct iwl_priv *priv, struct iwl_bt_uart_msg *uart_msg) { bool need_update = false; u8 kill_msk = IWL_BT_KILL_REDUCE; static const __le32 bt_kill_ack_msg[3] = { IWLAGN_BT_KILL_ACK_MASK_DEFAULT, IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; static const __le32 bt_kill_cts_msg[3] = { IWLAGN_BT_KILL_CTS_MASK_DEFAULT, IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; if (!priv->reduced_txpower) kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; need_update = true; } return need_update; } /* * Upon RSSI changes, sends a bt config command with following changes * 1. enable/disable "reduced control frames tx power * 2. update the "kill)ack_mask" and "kill_cts_mask" * * If "reduced tx power" is enabled, uCode shall * 1. ACK/Back/CTS rate shall reduced to 6Mbps * 2. not use duplciate 20/40MHz mode */ static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, struct iwl_bt_uart_msg *uart_msg) { bool need_update = false; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; int ave_rssi; if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); return false; } ave_rssi = ieee80211_ave_rssi(ctx->vif); if (!ave_rssi) { /* no rssi data, no changes to reduce tx power */ IWL_DEBUG_COEX(priv, "no rssi data available\n"); return need_update; } if (!priv->reduced_txpower && !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) && (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | BT_UART_MSG_FRAME3OBEX_MSK)) && !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { /* enabling reduced tx power */ priv->reduced_txpower = true; priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; need_update = true; } else if (priv->reduced_txpower && (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) || (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | BT_UART_MSG_FRAME3OBEX_MSK)))) { /* disable reduced tx power */ priv->reduced_txpower = false; priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; need_update = true; } return need_update; } int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data; struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg; if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { /* bt coex disabled */ return 0; } IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status); IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load); IWL_DEBUG_COEX(priv, " CI compliance: %d\n", coex->bt_ci_compliance); iwlagn_print_uartmsg(priv, uart_msg); priv->last_bt_traffic_load = priv->bt_traffic_load; priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg); if (priv->iw_mode != NL80211_IFTYPE_ADHOC) { if (priv->bt_status != coex->bt_status || priv->last_bt_traffic_load != coex->bt_traffic_load) { if (coex->bt_status) { /* BT on */ if (!priv->bt_ch_announce) priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; else priv->bt_traffic_load = coex->bt_traffic_load; } else { /* BT off */ priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE; } priv->bt_status = coex->bt_status; queue_work(priv->workqueue, &priv->bt_traffic_change_work); } } /* schedule to send runtime bt_config */ /* check reduce power before change ack/cts kill mask */ if (iwlagn_fill_txpower_mode(priv, uart_msg) || iwlagn_set_kill_msk(priv, uart_msg)) queue_work(priv->workqueue, &priv->bt_runtime_config); /* FIXME: based on notification, adjust the prio_boost */ priv->bt_ci_compliance = coex->bt_ci_compliance; return 0; } void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) { priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] = iwlagn_bt_coex_profile_notif; } void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv) { INIT_WORK(&priv->bt_traffic_change_work, iwlagn_bt_traffic_change_work); } void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv) { cancel_work_sync(&priv->bt_traffic_change_work); } static bool is_single_rx_stream(struct iwl_priv *priv) { return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || priv->current_ht_config.single_chain_sufficient; } #define IWL_NUM_RX_CHAINS_MULTIPLE 3 #define IWL_NUM_RX_CHAINS_SINGLE 2 #define IWL_NUM_IDLE_CHAINS_DUAL 2 #define IWL_NUM_IDLE_CHAINS_SINGLE 1 /* * Determine how many receiver/antenna chains to use. * * More provides better reception via diversity. Fewer saves power * at the expense of throughput, but only when not in powersave to * start with. * * MIMO (dual stream) requires at least 2, but works better with 3. * This does not determine *which* chains to use, just how many. */ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* * only use chain 'A' in bt high traffic load or * full concurrency mode */ return IWL_NUM_RX_CHAINS_SINGLE; } /* # of Rx chains to use when expecting MIMO. */ if (is_single_rx_stream(priv)) return IWL_NUM_RX_CHAINS_SINGLE; else return IWL_NUM_RX_CHAINS_MULTIPLE; } /* * When we are in power saving mode, unless device support spatial * multiplexing power save, use the active count for rx chain count. */ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) { /* # Rx chains when idling, depending on SMPS mode */ switch (priv->current_ht_config.smps) { case IEEE80211_SMPS_STATIC: case IEEE80211_SMPS_DYNAMIC: return IWL_NUM_IDLE_CHAINS_SINGLE; case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_OFF: return active_cnt; default: WARN(1, "invalid SMPS mode %d", priv->current_ht_config.smps); return active_cnt; } } /* up to 4 chains */ static u8 iwl_count_chain_bitmap(u32 chain_bitmap) { u8 res; res = (chain_bitmap & BIT(0)) >> 0; res += (chain_bitmap & BIT(1)) >> 1; res += (chain_bitmap & BIT(2)) >> 2; res += (chain_bitmap & BIT(3)) >> 3; return res; } /** * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image * * Selects how many and which Rx receivers/antennas/chains to use. * This should not be used for scan command ... it puts data in wrong place. */ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { bool is_single = is_single_rx_stream(priv); bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; u32 active_chains; u16 rx_chain; /* Tell uCode which antennas are actually connected. * Before first association, we assume all antennas are connected. * Just after first association, iwl_chain_noise_calibration() * checks which antennas actually *are* connected. */ if (priv->chain_noise_data.active_chains) active_chains = priv->chain_noise_data.active_chains; else active_chains = priv->eeprom_data->valid_rx_ant; if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* * only use chain 'A' in bt high traffic load or * full concurrency mode */ active_chains = first_antenna(active_chains); } rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; /* How many receivers should we use? */ active_rx_cnt = iwl_get_active_rx_chain_count(priv); idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); /* correct rx chain count according hw settings * and chain noise calibration */ valid_rx_cnt = iwl_count_chain_bitmap(active_chains); if (valid_rx_cnt < active_rx_cnt) active_rx_cnt = valid_rx_cnt; if (valid_rx_cnt < idle_rx_cnt) idle_rx_cnt = valid_rx_cnt; rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; ctx->staging.rx_chain = cpu_to_le16(rx_chain); if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; else ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", ctx->staging.rx_chain, active_rx_cnt, idle_rx_cnt); WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || active_rx_cnt < idle_rx_cnt); } u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) { int i; u8 ind = ant; if (priv->band == IEEE80211_BAND_2GHZ && priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) return 0; for (i = 0; i < RATE_ANT_NUM - 1; i++) { ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; if (valid & BIT(ind)) return ind; } return ant; } #ifdef CONFIG_PM_SLEEP static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) { int i; for (i = 0; i < IWLAGN_P1K_SIZE; i++) out[i] = cpu_to_le16(p1k[i]); } struct wowlan_key_data { struct iwl_rxon_context *ctx; struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; struct iwlagn_wowlan_tkip_params_cmd *tkip; const u8 *bssid; bool error, use_rsc_tsc, use_tkip; }; static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, void *_data) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct wowlan_key_data *data = _data; struct iwl_rxon_context *ctx = data->ctx; struct aes_sc *aes_sc, *aes_tx_sc = NULL; struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; struct iwlagn_p1k_cache *rx_p1ks; u8 *rx_mic_key; struct ieee80211_key_seq seq; u32 cur_rx_iv32 = 0; u16 p1k[IWLAGN_P1K_SIZE]; int ret, i; mutex_lock(&priv->mutex); if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta && !ctx->key_mapping_keys) ret = iwl_set_default_wep_key(priv, ctx, key); else ret = iwl_set_dynamic_key(priv, ctx, key, sta); if (ret) { IWL_ERR(priv, "Error setting key during suspend!\n"); data->error = true; } switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: if (sta) { tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; rx_p1ks = data->tkip->rx_uni; ieee80211_get_key_tx_seq(key, &seq); tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); memcpy(data->tkip->mic_keys.tx, &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], IWLAGN_MIC_KEY_SIZE); rx_mic_key = data->tkip->mic_keys.rx_unicast; } else { tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; rx_p1ks = data->tkip->rx_multi; rx_mic_key = data->tkip->mic_keys.rx_mcast; } /* * For non-QoS this relies on the fact that both the uCode and * mac80211 use TID 0 (as they need to to avoid replay attacks) * for checking the IV in the frames. */ for (i = 0; i < IWLAGN_NUM_RSC; i++) { ieee80211_get_key_rx_seq(key, i, &seq); tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); /* wrapping isn't allowed, AP must rekey */ if (seq.tkip.iv32 > cur_rx_iv32) cur_rx_iv32 = seq.tkip.iv32; } ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32 + 1, p1k); iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); memcpy(rx_mic_key, &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], IWLAGN_MIC_KEY_SIZE); data->use_tkip = true; data->use_rsc_tsc = true; break; case WLAN_CIPHER_SUITE_CCMP: if (sta) { u8 *pn = seq.ccmp.pn; aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; ieee80211_get_key_tx_seq(key, &seq); aes_tx_sc->pn = cpu_to_le64( (u64)pn[5] | ((u64)pn[4] << 8) | ((u64)pn[3] << 16) | ((u64)pn[2] << 24) | ((u64)pn[1] << 32) | ((u64)pn[0] << 40)); } else aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; /* * For non-QoS this relies on the fact that both the uCode and * mac80211 use TID 0 for checking the IV in the frames. */ for (i = 0; i < IWLAGN_NUM_RSC; i++) { u8 *pn = seq.ccmp.pn; ieee80211_get_key_rx_seq(key, i, &seq); aes_sc->pn = cpu_to_le64( (u64)pn[5] | ((u64)pn[4] << 8) | ((u64)pn[3] << 16) | ((u64)pn[2] << 24) | ((u64)pn[1] << 32) | ((u64)pn[0] << 40)); } data->use_rsc_tsc = true; break; } mutex_unlock(&priv->mutex); } int iwlagn_send_patterns(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) { struct iwlagn_wowlan_patterns_cmd *pattern_cmd; struct iwl_host_cmd cmd = { .id = REPLY_WOWLAN_PATTERNS, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .flags = CMD_SYNC, }; int i, err; if (!wowlan->n_patterns) return 0; cmd.len[0] = sizeof(*pattern_cmd) + wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); if (!pattern_cmd) return -ENOMEM; pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); for (i = 0; i < wowlan->n_patterns; i++) { int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); memcpy(&pattern_cmd->patterns[i].mask, wowlan->patterns[i].mask, mask_len); memcpy(&pattern_cmd->patterns[i].pattern, wowlan->patterns[i].pattern, wowlan->patterns[i].pattern_len); pattern_cmd->patterns[i].mask_size = mask_len; pattern_cmd->patterns[i].pattern_size = wowlan->patterns[i].pattern_len; } cmd.data[0] = pattern_cmd; err = iwl_dvm_send_cmd(priv, &cmd); kfree(pattern_cmd); return err; } int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) { struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; struct iwl_rxon_cmd rxon; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; struct iwlagn_d3_config_cmd d3_cfg_cmd = {}; struct wowlan_key_data key_data = { .ctx = ctx, .bssid = ctx->active.bssid_addr, .use_rsc_tsc = false, .tkip = &tkip_cmd, .use_tkip = false, }; int ret, i; u16 seq; key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); if (!key_data.rsc_tsc) return -ENOMEM; memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); /* * We know the last used seqno, and the uCode expects to know that * one, it will increment before TX. */ seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); /* * For QoS counters, we store the one to use next, so subtract 0x10 * since the uCode will add 0x10 before using the value. */ for (i = 0; i < IWL_MAX_TID_COUNT; i++) { seq = priv->tid_data[IWL_AP_ID][i].seq_number; seq -= 0x10; wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); } if (wowlan->disconnect) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); if (wowlan->magic_pkt) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); if (wowlan->gtk_rekey_failure) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); if (wowlan->eap_identity_req) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); if (wowlan->four_way_handshake) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); if (wowlan->n_patterns) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); if (wowlan->rfkill_release) d3_cfg_cmd.wakeup_flags |= cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); iwl_scan_cancel_timeout(priv, 200); memcpy(&rxon, &ctx->active, sizeof(rxon)); priv->ucode_loaded = false; iwl_trans_stop_device(priv->trans); priv->wowlan = true; ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); if (ret) goto out; /* now configure WoWLAN ucode */ ret = iwl_alive_start(priv); if (ret) goto out; memcpy(&ctx->staging, &rxon, sizeof(rxon)); ret = iwlagn_commit_rxon(priv, ctx); if (ret) goto out; ret = iwl_power_update_mode(priv, true); if (ret) goto out; if (!iwlwifi_mod_params.sw_crypto) { /* mark all keys clear */ priv->ucode_key_table = 0; ctx->key_mapping_keys = 0; /* * This needs to be unlocked due to lock ordering * constraints. Since we're in the suspend path * that isn't really a problem though. */ mutex_unlock(&priv->mutex); ieee80211_iter_keys(priv->hw, ctx->vif, iwlagn_wowlan_program_keys, &key_data); mutex_lock(&priv->mutex); if (key_data.error) { ret = -EIO; goto out; } if (key_data.use_rsc_tsc) { struct iwl_host_cmd rsc_tsc_cmd = { .id = REPLY_WOWLAN_TSC_RSC_PARAMS, .flags = CMD_SYNC, .data[0] = key_data.rsc_tsc, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .len[0] = sizeof(*key_data.rsc_tsc), }; ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd); if (ret) goto out; } if (key_data.use_tkip) { ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_TKIP_PARAMS, CMD_SYNC, sizeof(tkip_cmd), &tkip_cmd); if (ret) goto out; } if (priv->have_rekey_data) { memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); kek_kck_cmd.replay_ctr = priv->replay_ctr; ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_KEK_KCK_MATERIAL, CMD_SYNC, sizeof(kek_kck_cmd), &kek_kck_cmd); if (ret) goto out; } } ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, CMD_SYNC, sizeof(d3_cfg_cmd), &d3_cfg_cmd); if (ret) goto out; ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, CMD_SYNC, sizeof(wakeup_filter_cmd), &wakeup_filter_cmd); if (ret) goto out; ret = iwlagn_send_patterns(priv, wowlan); out: kfree(key_data.rsc_tsc); return ret; } #endif int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { IWL_WARN(priv, "Not sending command - %s KILL\n", iwl_is_rfkill(priv) ? "RF" : "CT"); return -EIO; } if (test_bit(STATUS_FW_ERROR, &priv->status)) { IWL_ERR(priv, "Command %s failed: FW Error\n", iwl_dvm_get_cmd_string(cmd->id)); return -EIO; } /* * Synchronous commands from this op-mode must hold * the mutex, this ensures we don't try to send two * (or more) synchronous commands at a time. */ if (!(cmd->flags & CMD_ASYNC)) lockdep_assert_held(&priv->mutex); if (priv->ucode_owner == IWL_OWNERSHIP_TM && !(cmd->flags & CMD_ON_DEMAND)) { IWL_DEBUG_HC(priv, "tm own the uCode, no regular hcmd send\n"); return -EIO; } return iwl_trans_send_cmd(priv->trans, cmd); } int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, u32 flags, u16 len, const void *data) { struct iwl_host_cmd cmd = { .id = id, .len = { len, }, .data = { data, }, .flags = flags, }; return iwl_dvm_send_cmd(priv, &cmd); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/led.h0000644000175000017500000000276112026211315023644 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __iwl_leds_h__ #define __iwl_leds_h__ struct iwl_priv; #define IWL_LED_SOLID 11 #define IWL_DEF_LED_INTRVL cpu_to_le32(1000) #define IWL_LED_ACTIVITY (0<<1) #define IWL_LED_LINK (1<<1) void iwlagn_led_enable(struct iwl_priv *priv); void iwl_leds_init(struct iwl_priv *priv); void iwl_leds_exit(struct iwl_priv *priv); #endif /* __iwl_leds_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/devices.c0000644000175000017500000004022512026211315024512 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ /* * DVM device-specific data & functions */ #include "iwl-io.h" #include "iwl-prph.h" #include "iwl-eeprom-parse.h" #include "agn.h" #include "dev.h" #include "commands.h" /* * 1000 series * =========== */ /* * For 1000, use advance thermal throttling critical temperature threshold, * but legacy thermal management implementation for now. * This is for the reason of 1000 uCode using advance thermal throttling API * but not implement ct_kill_exit based on ct_kill exit temperature * so the thermal throttling will still based on legacy thermal throttling * management. * The code here need to be modified once 1000 uCode has the advanced thermal * throttling algorithm in place */ static void iwl1000_set_ct_threshold(struct iwl_priv *priv) { /* want Celsius */ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; } /* NIC configuration for 1000 series */ static void iwl1000_nic_config(struct iwl_priv *priv) { /* Setting digital SVR for 1000 card to 1.32V */ /* locking is acquired in iwl_set_bits_mask_prph() function */ iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG, APMG_SVR_DIGITAL_VOLTAGE_1_32, ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); } /** * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time * @priv -- pointer to iwl_priv data structure * @tsf_bits -- number of bits need to shift for masking) */ static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv, u16 tsf_bits) { return (1 << tsf_bits) - 1; } /** * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time * @priv -- pointer to iwl_priv data structure * @tsf_bits -- number of bits need to shift for masking) */ static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv, u16 tsf_bits) { return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; } /* * extended beacon time format * time in usec will be changed into a 32-bit value in extended:internal format * the extended part is the beacon counts * the internal part is the time in usec within one beacon interval */ static u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval) { u32 quot; u32 rem; u32 interval = beacon_interval * TIME_UNIT; if (!interval || !usec) return 0; quot = (usec / interval) & (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >> IWLAGN_EXT_BEACON_TIME_POS); rem = (usec % interval) & iwl_beacon_time_mask_low(priv, IWLAGN_EXT_BEACON_TIME_POS); return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem; } /* base is usually what we get from ucode with each received frame, * the same as HW timer counter counting down */ static __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, u32 addon, u32 beacon_interval) { u32 base_low = base & iwl_beacon_time_mask_low(priv, IWLAGN_EXT_BEACON_TIME_POS); u32 addon_low = addon & iwl_beacon_time_mask_low(priv, IWLAGN_EXT_BEACON_TIME_POS); u32 interval = beacon_interval * TIME_UNIT; u32 res = (base & iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS)) + (addon & iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS)); if (base_low > addon_low) res += base_low - addon_low; else if (base_low < addon_low) { res += interval + base_low - addon_low; res += (1 << IWLAGN_EXT_BEACON_TIME_POS); } else res += (1 << IWLAGN_EXT_BEACON_TIME_POS); return cpu_to_le32(res); } static const struct iwl_sensitivity_ranges iwl1000_sensitivity = { .min_nrg_cck = 95, .auto_corr_min_ofdm = 90, .auto_corr_min_ofdm_mrc = 170, .auto_corr_min_ofdm_x1 = 120, .auto_corr_min_ofdm_mrc_x1 = 240, .auto_corr_max_ofdm = 120, .auto_corr_max_ofdm_mrc = 210, .auto_corr_max_ofdm_x1 = 155, .auto_corr_max_ofdm_mrc_x1 = 290, .auto_corr_min_cck = 125, .auto_corr_max_cck = 200, .auto_corr_min_cck_mrc = 170, .auto_corr_max_cck_mrc = 400, .nrg_th_cck = 95, .nrg_th_ofdm = 95, .barker_corr_th_min = 190, .barker_corr_th_min_mrc = 390, .nrg_th_cca = 62, }; static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) { iwl1000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ priv->hw_params.sens = &iwl1000_sensitivity; } struct iwl_lib_ops iwl1000_lib = { .set_hw_params = iwl1000_hw_set_hw_params, .nic_config = iwl1000_nic_config, .temperature = iwlagn_temperature, }; /* * 2000 series * =========== */ static void iwl2000_set_ct_threshold(struct iwl_priv *priv) { /* want Celsius */ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; } /* NIC configuration for 2000 series */ static void iwl2000_nic_config(struct iwl_priv *priv) { iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER); } static const struct iwl_sensitivity_ranges iwl2000_sensitivity = { .min_nrg_cck = 97, .auto_corr_min_ofdm = 80, .auto_corr_min_ofdm_mrc = 128, .auto_corr_min_ofdm_x1 = 105, .auto_corr_min_ofdm_mrc_x1 = 192, .auto_corr_max_ofdm = 145, .auto_corr_max_ofdm_mrc = 232, .auto_corr_max_ofdm_x1 = 110, .auto_corr_max_ofdm_mrc_x1 = 232, .auto_corr_min_cck = 125, .auto_corr_max_cck = 175, .auto_corr_min_cck_mrc = 160, .auto_corr_max_cck_mrc = 310, .nrg_th_cck = 97, .nrg_th_ofdm = 100, .barker_corr_th_min = 190, .barker_corr_th_min_mrc = 390, .nrg_th_cca = 62, }; static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) { iwl2000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ priv->hw_params.sens = &iwl2000_sensitivity; } struct iwl_lib_ops iwl2000_lib = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, }; struct iwl_lib_ops iwl2030_lib = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, }; /* * 5000 series * =========== */ /* NIC configuration for 5000 series */ static const struct iwl_sensitivity_ranges iwl5000_sensitivity = { .min_nrg_cck = 100, .auto_corr_min_ofdm = 90, .auto_corr_min_ofdm_mrc = 170, .auto_corr_min_ofdm_x1 = 105, .auto_corr_min_ofdm_mrc_x1 = 220, .auto_corr_max_ofdm = 120, .auto_corr_max_ofdm_mrc = 210, .auto_corr_max_ofdm_x1 = 120, .auto_corr_max_ofdm_mrc_x1 = 240, .auto_corr_min_cck = 125, .auto_corr_max_cck = 200, .auto_corr_min_cck_mrc = 200, .auto_corr_max_cck_mrc = 400, .nrg_th_cck = 100, .nrg_th_ofdm = 100, .barker_corr_th_min = 190, .barker_corr_th_min_mrc = 390, .nrg_th_cca = 62, }; static struct iwl_sensitivity_ranges iwl5150_sensitivity = { .min_nrg_cck = 95, .auto_corr_min_ofdm = 90, .auto_corr_min_ofdm_mrc = 170, .auto_corr_min_ofdm_x1 = 105, .auto_corr_min_ofdm_mrc_x1 = 220, .auto_corr_max_ofdm = 120, .auto_corr_max_ofdm_mrc = 210, /* max = min for performance bug in 5150 DSP */ .auto_corr_max_ofdm_x1 = 105, .auto_corr_max_ofdm_mrc_x1 = 220, .auto_corr_min_cck = 125, .auto_corr_max_cck = 200, .auto_corr_min_cck_mrc = 170, .auto_corr_max_cck_mrc = 400, .nrg_th_cck = 95, .nrg_th_ofdm = 95, .barker_corr_th_min = 190, .barker_corr_th_min_mrc = 390, .nrg_th_cca = 62, }; #define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5) static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) { u16 temperature, voltage; temperature = le16_to_cpu(priv->eeprom_data->kelvin_temperature); voltage = le16_to_cpu(priv->eeprom_data->kelvin_voltage); /* offset = temp - volt / coeff */ return (s32)(temperature - voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF); } static void iwl5150_set_ct_threshold(struct iwl_priv *priv) { const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - iwl_temp_calib_to_offset(priv); priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef; } static void iwl5000_set_ct_threshold(struct iwl_priv *priv) { /* want Celsius */ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; } static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) { iwl5000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ priv->hw_params.sens = &iwl5000_sensitivity; } static void iwl5150_hw_set_hw_params(struct iwl_priv *priv) { iwl5150_set_ct_threshold(priv); /* Set initial sensitivity parameters */ priv->hw_params.sens = &iwl5150_sensitivity; } static void iwl5150_temperature(struct iwl_priv *priv) { u32 vt = 0; s32 offset = iwl_temp_calib_to_offset(priv); vt = le32_to_cpu(priv->statistics.common.temperature); vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; /* now vt hold the temperature in Kelvin */ priv->temperature = KELVIN_TO_CELSIUS(vt); iwl_tt_handler(priv); } static int iwl5000_hw_channel_switch(struct iwl_priv *priv, struct ieee80211_channel_switch *ch_switch) { /* * MULTI-FIXME * See iwlagn_mac_channel_switch. */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwl5000_channel_switch_cmd cmd; u32 switch_time_in_usec, ucode_switch_time; u16 ch; u32 tsf_low; u8 switch_count; u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); struct ieee80211_vif *vif = ctx->vif; struct iwl_host_cmd hcmd = { .id = REPLY_CHANNEL_SWITCH, .len = { sizeof(cmd), }, .flags = CMD_SYNC, .data = { &cmd, }, }; cmd.band = priv->band == IEEE80211_BAND_2GHZ; ch = ch_switch->channel->hw_value; IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", ctx->active.channel, ch); cmd.channel = cpu_to_le16(ch); cmd.rxon_flags = ctx->staging.flags; cmd.rxon_filter_flags = ctx->staging.filter_flags; switch_count = ch_switch->count; tsf_low = ch_switch->timestamp & 0x0ffffffff; /* * calculate the ucode channel switch time * adding TSF as one of the factor for when to switch */ if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { if (switch_count > ((priv->ucode_beacon_time - tsf_low) / beacon_interval)) { switch_count -= (priv->ucode_beacon_time - tsf_low) / beacon_interval; } else switch_count = 0; } if (switch_count <= 1) cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); else { switch_time_in_usec = vif->bss_conf.beacon_int * switch_count * TIME_UNIT; ucode_switch_time = iwl_usecs_to_beacons(priv, switch_time_in_usec, beacon_interval); cmd.switch_time = iwl_add_beacon_time(priv, priv->ucode_beacon_time, ucode_switch_time, beacon_interval); } IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", cmd.switch_time); cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; return iwl_dvm_send_cmd(priv, &hcmd); } struct iwl_lib_ops iwl5000_lib = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwlagn_temperature, }; struct iwl_lib_ops iwl5150_lib = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwl5150_temperature, }; /* * 6000 series * =========== */ static void iwl6000_set_ct_threshold(struct iwl_priv *priv) { /* want Celsius */ priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; } /* NIC configuration for 6000 series */ static void iwl6000_nic_config(struct iwl_priv *priv) { switch (priv->cfg->device_family) { case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6030: case IWL_DEVICE_FAMILY_6000: break; case IWL_DEVICE_FAMILY_6000i: /* 2x2 IPA phy type */ iwl_write32(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); break; case IWL_DEVICE_FAMILY_6050: /* Indicate calibration version to uCode. */ if (priv->eeprom_data->calib_version >= 6) iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); break; case IWL_DEVICE_FAMILY_6150: /* Indicate calibration version to uCode. */ if (priv->eeprom_data->calib_version >= 6) iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_6050_1x2); break; default: WARN_ON(1); } } static const struct iwl_sensitivity_ranges iwl6000_sensitivity = { .min_nrg_cck = 110, .auto_corr_min_ofdm = 80, .auto_corr_min_ofdm_mrc = 128, .auto_corr_min_ofdm_x1 = 105, .auto_corr_min_ofdm_mrc_x1 = 192, .auto_corr_max_ofdm = 145, .auto_corr_max_ofdm_mrc = 232, .auto_corr_max_ofdm_x1 = 110, .auto_corr_max_ofdm_mrc_x1 = 232, .auto_corr_min_cck = 125, .auto_corr_max_cck = 175, .auto_corr_min_cck_mrc = 160, .auto_corr_max_cck_mrc = 310, .nrg_th_cck = 110, .nrg_th_ofdm = 110, .barker_corr_th_min = 190, .barker_corr_th_min_mrc = 336, .nrg_th_cca = 62, }; static void iwl6000_hw_set_hw_params(struct iwl_priv *priv) { iwl6000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ priv->hw_params.sens = &iwl6000_sensitivity; } static int iwl6000_hw_channel_switch(struct iwl_priv *priv, struct ieee80211_channel_switch *ch_switch) { /* * MULTI-FIXME * See iwlagn_mac_channel_switch. */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwl6000_channel_switch_cmd cmd; u32 switch_time_in_usec, ucode_switch_time; u16 ch; u32 tsf_low; u8 switch_count; u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); struct ieee80211_vif *vif = ctx->vif; struct iwl_host_cmd hcmd = { .id = REPLY_CHANNEL_SWITCH, .len = { sizeof(cmd), }, .flags = CMD_SYNC, .data = { &cmd, }, }; cmd.band = priv->band == IEEE80211_BAND_2GHZ; ch = ch_switch->channel->hw_value; IWL_DEBUG_11H(priv, "channel switch from %u to %u\n", ctx->active.channel, ch); cmd.channel = cpu_to_le16(ch); cmd.rxon_flags = ctx->staging.flags; cmd.rxon_filter_flags = ctx->staging.filter_flags; switch_count = ch_switch->count; tsf_low = ch_switch->timestamp & 0x0ffffffff; /* * calculate the ucode channel switch time * adding TSF as one of the factor for when to switch */ if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { if (switch_count > ((priv->ucode_beacon_time - tsf_low) / beacon_interval)) { switch_count -= (priv->ucode_beacon_time - tsf_low) / beacon_interval; } else switch_count = 0; } if (switch_count <= 1) cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); else { switch_time_in_usec = vif->bss_conf.beacon_int * switch_count * TIME_UNIT; ucode_switch_time = iwl_usecs_to_beacons(priv, switch_time_in_usec, beacon_interval); cmd.switch_time = iwl_add_beacon_time(priv, priv->ucode_beacon_time, ucode_switch_time, beacon_interval); } IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", cmd.switch_time); cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; return iwl_dvm_send_cmd(priv, &hcmd); } struct iwl_lib_ops iwl6000_lib = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, }; struct iwl_lib_ops iwl6030_lib = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, }; compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/dev.h0000644000175000017500000005572312026211315023664 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ /* * Please use this file (dev.h) for driver implementation definitions. * Please use commands.h for uCode API definitions. */ #ifndef __iwl_dev_h__ #define __iwl_dev_h__ #include #include #include #include #include #include #include "iwl-fw.h" #include "iwl-eeprom-parse.h" #include "iwl-csr.h" #include "iwl-debug.h" #include "iwl-agn-hw.h" #include "iwl-op-mode.h" #include "iwl-notif-wait.h" #include "iwl-trans.h" #include "led.h" #include "power.h" #include "rs.h" #include "tt.h" #include "iwl-test.h" /* CT-KILL constants */ #define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ #define CT_KILL_THRESHOLD 114 /* in Celsius */ #define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ /* Default noise level to report when noise measurement is not available. * This may be because we're: * 1) Not associated no beacon statistics being sent to driver) * 2) Scanning (noise measurement does not apply to associated channel) * Use default noise value of -127 ... this is below the range of measurable * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user. * Also, -127 works better than 0 when averaging frames with/without * noise info (e.g. averaging might be done in app); measured dBm values are * always negative ... using a negative value as the default keeps all * averages within an s8's (used in some apps) range of negative values. */ #define IWL_NOISE_MEAS_NOT_AVAILABLE (-127) /* * RTS threshold here is total size [2347] minus 4 FCS bytes * Per spec: * a value of 0 means RTS on all data/management packets * a value > max MSDU size means no RTS * else RTS for data/management frames where MPDU is larger * than RTS value. */ #define DEFAULT_RTS_THRESHOLD 2347U #define MIN_RTS_THRESHOLD 0U #define MAX_RTS_THRESHOLD 2347U #define MAX_MSDU_SIZE 2304U #define MAX_MPDU_SIZE 2346U #define DEFAULT_BEACON_INTERVAL 200U #define DEFAULT_SHORT_RETRY_LIMIT 7U #define DEFAULT_LONG_RETRY_LIMIT 4U #define IWL_NUM_SCAN_RATES (2) #define IEEE80211_DATA_LEN 2304 #define IEEE80211_4ADDR_LEN 30 #define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) #define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 #define IWL_SUPPORTED_RATES_IE_LEN 8 #define IWL_INVALID_RATE 0xFF #define IWL_INVALID_VALUE -1 union iwl_ht_rate_supp { u16 rates; struct { u8 siso_rate; u8 mimo_rate; }; }; struct iwl_ht_config { bool single_chain_sufficient; enum ieee80211_smps_mode smps; /* current smps mode */ }; /* QoS structures */ struct iwl_qos_info { int qos_active; struct iwl_qosparam_cmd def_qos_parm; }; /** * enum iwl_agg_state * * The state machine of the BA agreement establishment / tear down. * These states relate to a specific RA / TID. * * @IWL_AGG_OFF: aggregation is not used * @IWL_AGG_STARTING: aggregation are starting (between start and oper) * @IWL_AGG_ON: aggregation session is up * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the * HW queue to be empty from packets for this RA /TID. * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the * HW queue to be empty from packets for this RA /TID. */ enum iwl_agg_state { IWL_AGG_OFF = 0, IWL_AGG_STARTING, IWL_AGG_ON, IWL_EMPTYING_HW_QUEUE_ADDBA, IWL_EMPTYING_HW_QUEUE_DELBA, }; /** * struct iwl_ht_agg - aggregation state machine * This structs holds the states for the BA agreement establishment and tear * down. It also holds the state during the BA session itself. This struct is * duplicated for each RA / TID. * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the * Tx response (REPLY_TX), and the block ack notification * (REPLY_COMPRESSED_BA). * @state: state of the BA agreement establishment / tear down. * @txq_id: Tx queue used by the BA session * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or * the first packet to be sent in legacy HW queue in Tx AGG stop flow. * Basically when next_reclaimed reaches ssn, we can tell mac80211 that * we are ready to finish the Tx AGG stop / start flow. * @wait_for_ba: Expect block-ack before next Tx reply */ struct iwl_ht_agg { u32 rate_n_flags; enum iwl_agg_state state; u16 txq_id; u16 ssn; bool wait_for_ba; }; /** * struct iwl_tid_data - one for each RA / TID * This structs holds the states for each RA / TID. * @seq_number: the next WiFi sequence number to use * @next_reclaimed: the WiFi sequence number of the next packet to be acked. * This is basically (last acked packet++). * @agg: aggregation state machine */ struct iwl_tid_data { u16 seq_number; u16 next_reclaimed; struct iwl_ht_agg agg; }; /* * Structure should be accessed with sta_lock held. When station addition * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock * held. */ struct iwl_station_entry { struct iwl_addsta_cmd sta; u8 used, ctxid; struct iwl_link_quality_cmd *lq; }; /* * iwl_station_priv: Driver's private station information * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is places in that * space. */ struct iwl_station_priv { struct iwl_rxon_context *ctx; struct iwl_lq_sta lq_sta; atomic_t pending_frames; bool client; bool asleep; u8 max_agg_bufsize; u8 sta_id; }; /** * struct iwl_vif_priv - driver's private per-interface information * * When mac80211 allocates a virtual interface, it can allocate * space for us to put data into. */ struct iwl_vif_priv { struct iwl_rxon_context *ctx; u8 ibss_bssid_sta_id; }; struct iwl_sensitivity_ranges { u16 min_nrg_cck; u16 nrg_th_cck; u16 nrg_th_ofdm; u16 auto_corr_min_ofdm; u16 auto_corr_min_ofdm_mrc; u16 auto_corr_min_ofdm_x1; u16 auto_corr_min_ofdm_mrc_x1; u16 auto_corr_max_ofdm; u16 auto_corr_max_ofdm_mrc; u16 auto_corr_max_ofdm_x1; u16 auto_corr_max_ofdm_mrc_x1; u16 auto_corr_max_cck; u16 auto_corr_max_cck_mrc; u16 auto_corr_min_cck; u16 auto_corr_min_cck_mrc; u16 barker_corr_th_min; u16 barker_corr_th_min_mrc; u16 nrg_th_cca; }; #define KELVIN_TO_CELSIUS(x) ((x)-273) #define CELSIUS_TO_KELVIN(x) ((x)+273) /****************************************************************************** * * Functions implemented in core module which are forward declared here * for use by iwl-[4-5].c * * NOTE: The implementation of these functions are not hardware specific * which is why they are in the core module files. * * Naming convention -- * iwl_ <-- Is part of iwlwifi * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) * ****************************************************************************/ extern void iwl_update_chain_flags(struct iwl_priv *priv); extern const u8 iwl_bcast_addr[ETH_ALEN]; #define IWL_OPERATION_MODE_AUTO 0 #define IWL_OPERATION_MODE_HT_ONLY 1 #define IWL_OPERATION_MODE_MIXED 2 #define IWL_OPERATION_MODE_20MHZ 3 #define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 /* Sensitivity and chain noise calibration */ #define INITIALIZATION_VALUE 0xFFFF #define IWL_CAL_NUM_BEACONS 16 #define MAXIMUM_ALLOWED_PATHLOSS 15 #define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 #define MAX_FA_OFDM 50 #define MIN_FA_OFDM 5 #define MAX_FA_CCK 50 #define MIN_FA_CCK 5 #define AUTO_CORR_STEP_OFDM 1 #define AUTO_CORR_STEP_CCK 3 #define AUTO_CORR_MAX_TH_CCK 160 #define NRG_DIFF 2 #define NRG_STEP_CCK 2 #define NRG_MARGIN 8 #define MAX_NUMBER_CCK_NO_FA 100 #define AUTO_CORR_CCK_MIN_VAL_DEF (125) #define CHAIN_A 0 #define CHAIN_B 1 #define CHAIN_C 2 #define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 #define ALL_BAND_FILTER 0xFF00 #define IN_BAND_FILTER 0xFF #define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF #define NRG_NUM_PREV_STAT_L 20 #define NUM_RX_CHAINS 3 enum iwlagn_false_alarm_state { IWL_FA_TOO_MANY = 0, IWL_FA_TOO_FEW = 1, IWL_FA_GOOD_RANGE = 2, }; enum iwlagn_chain_noise_state { IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ IWL_CHAIN_NOISE_ACCUMULATE, IWL_CHAIN_NOISE_CALIBRATED, IWL_CHAIN_NOISE_DONE, }; /* Sensitivity calib data */ struct iwl_sensitivity_data { u32 auto_corr_ofdm; u32 auto_corr_ofdm_mrc; u32 auto_corr_ofdm_x1; u32 auto_corr_ofdm_mrc_x1; u32 auto_corr_cck; u32 auto_corr_cck_mrc; u32 last_bad_plcp_cnt_ofdm; u32 last_fa_cnt_ofdm; u32 last_bad_plcp_cnt_cck; u32 last_fa_cnt_cck; u32 nrg_curr_state; u32 nrg_prev_state; u32 nrg_value[10]; u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; u32 nrg_silence_ref; u32 nrg_energy_idx; u32 nrg_silence_idx; u32 nrg_th_cck; s32 nrg_auto_corr_silence_diff; u32 num_in_cck_no_fa; u32 nrg_th_ofdm; u16 barker_corr_th_min; u16 barker_corr_th_min_mrc; u16 nrg_th_cca; }; /* Chain noise (differential Rx gain) calib data */ struct iwl_chain_noise_data { u32 active_chains; u32 chain_noise_a; u32 chain_noise_b; u32 chain_noise_c; u32 chain_signal_a; u32 chain_signal_b; u32 chain_signal_c; u16 beacon_count; u8 disconn_array[NUM_RX_CHAINS]; u8 delta_gain_code[NUM_RX_CHAINS]; u8 radio_write; u8 state; }; enum { MEASUREMENT_READY = (1 << 0), MEASUREMENT_ACTIVE = (1 << 1), }; /* reply_tx_statistics (for _agn devices) */ struct reply_tx_error_statistics { u32 pp_delay; u32 pp_few_bytes; u32 pp_bt_prio; u32 pp_quiet_period; u32 pp_calc_ttak; u32 int_crossed_retry; u32 short_limit; u32 long_limit; u32 fifo_underrun; u32 drain_flow; u32 rfkill_flush; u32 life_expire; u32 dest_ps; u32 host_abort; u32 bt_retry; u32 sta_invalid; u32 frag_drop; u32 tid_disable; u32 fifo_flush; u32 insuff_cf_poll; u32 fail_hw_drop; u32 sta_color_mismatch; u32 unknown; }; /* reply_agg_tx_statistics (for _agn devices) */ struct reply_agg_tx_error_statistics { u32 underrun; u32 bt_prio; u32 few_bytes; u32 abort; u32 last_sent_ttl; u32 last_sent_try; u32 last_sent_bt_kill; u32 scd_query; u32 bad_crc32; u32 response; u32 dump_tx; u32 delay_tx; u32 unknown; }; /* * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds * to perform continuous uCode event logging operation if enabled */ #define UCODE_TRACE_PERIOD (10) /* * iwl_event_log: current uCode event log position * * @ucode_trace: enable/disable ucode continuous trace timer * @num_wraps: how many times the event buffer wraps * @next_entry: the entry just before the next one that uCode would fill * @non_wraps_count: counter for no wrap detected when dump ucode events * @wraps_once_count: counter for wrap once detected when dump ucode events * @wraps_more_count: counter for wrap more than once detected * when dump ucode events */ struct iwl_event_log { bool ucode_trace; u32 num_wraps; u32 next_entry; int non_wraps_count; int wraps_once_count; int wraps_more_count; }; #define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) /* BT Antenna Coupling Threshold (dB) */ #define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) /* Firmware reload counter and Timestamp */ #define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */ #define IWL_MAX_CONTINUE_RELOAD_CNT 4 struct iwl_rf_reset { int reset_request_count; int reset_success_count; int reset_reject_count; unsigned long last_reset_jiffies; }; enum iwl_rxon_context_id { IWL_RXON_CTX_BSS, IWL_RXON_CTX_PAN, NUM_IWL_RXON_CTX }; /* extend beacon time format bit shifting */ /* * for _agn devices * bits 31:22 - extended * bits 21:0 - interval */ #define IWLAGN_EXT_BEACON_TIME_POS 22 struct iwl_rxon_context { struct ieee80211_vif *vif; u8 mcast_queue; u8 ac_to_queue[IEEE80211_NUM_ACS]; u8 ac_to_fifo[IEEE80211_NUM_ACS]; /* * We could use the vif to indicate active, but we * also need it to be active during disabling when * we already removed the vif for type setting. */ bool always_active, is_active; bool ht_need_multiple_chains; enum iwl_rxon_context_id ctxid; u32 interface_modes, exclusive_interface_modes; u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype; /* * We declare this const so it can only be * changed via explicit cast within the * routines that actually update the physical * hardware. */ const struct iwl_rxon_cmd active; struct iwl_rxon_cmd staging; struct iwl_rxon_time_cmd timing; struct iwl_qos_info qos_data; u8 bcast_sta_id, ap_sta_id; u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd; u8 qos_cmd; u8 wep_key_cmd; struct iwl_wep_key wep_keys[WEP_KEYS_MAX]; u8 key_mapping_keys; __le32 station_flags; int beacon_int; struct { bool non_gf_sta_present; u8 protection; bool enabled, is_40mhz; u8 extension_chan_offset; } ht; }; enum iwl_scan_type { IWL_SCAN_NORMAL, IWL_SCAN_RADIO_RESET, IWL_SCAN_ROC, }; /** * struct iwl_hw_params * * Holds the module parameters * * @tx_chains_num: Number of TX chains * @rx_chains_num: Number of RX chains * @ct_kill_threshold: temperature threshold - in hw dependent unit * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit * relevant for 1000, 6000 and up * @struct iwl_sensitivity_ranges: range of sensitivity values * @use_rts_for_aggregation: use rts/cts protection for HT traffic */ struct iwl_hw_params { u8 tx_chains_num; u8 rx_chains_num; bool use_rts_for_aggregation; u32 ct_kill_threshold; u32 ct_kill_exit_threshold; const struct iwl_sensitivity_ranges *sens; }; struct iwl_lib_ops { /* set hw dependent parameters */ void (*set_hw_params)(struct iwl_priv *priv); int (*set_channel_switch)(struct iwl_priv *priv, struct ieee80211_channel_switch *ch_switch); /* device specific configuration */ void (*nic_config)(struct iwl_priv *priv); /* temperature */ void (*temperature)(struct iwl_priv *priv); }; struct iwl_wipan_noa_data { struct rcu_head rcu_head; u32 length; u8 data[]; }; /* Calibration disabling bit mask */ enum { IWL_CALIB_ENABLE_ALL = 0, IWL_SENSITIVITY_CALIB_DISABLED = BIT(0), IWL_CHAIN_NOISE_CALIB_DISABLED = BIT(1), IWL_TX_POWER_CALIB_DISABLED = BIT(2), IWL_CALIB_DISABLE_ALL = 0xFFFFFFFF, }; #define IWL_OP_MODE_GET_DVM(_iwl_op_mode) \ ((struct iwl_priv *) ((_iwl_op_mode)->op_mode_specific)) #define IWL_MAC80211_GET_DVM(_hw) \ ((struct iwl_priv *) ((struct iwl_op_mode *) \ (_hw)->priv)->op_mode_specific) struct iwl_priv { struct iwl_trans *trans; struct device *dev; /* for debug prints only */ const struct iwl_cfg *cfg; const struct iwl_fw *fw; const struct iwl_lib_ops *lib; unsigned long status; spinlock_t sta_lock; struct mutex mutex; unsigned long transport_queue_stop; bool passive_no_rx; #define IWL_INVALID_MAC80211_QUEUE 0xff u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; /* ieee device used by generic ieee processing code */ struct ieee80211_hw *hw; struct list_head calib_results; struct workqueue_struct *workqueue; struct iwl_hw_params hw_params; enum ieee80211_band band; u8 valid_contexts; int (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); struct iwl_notif_wait_data notif_wait; /* spectrum measurement report caching */ struct iwl_spectrum_notification measure_report; u8 measurement_status; #define IWL_OWNERSHIP_DRIVER 0 #define IWL_OWNERSHIP_TM 1 u8 ucode_owner; /* ucode beacon time */ u32 ucode_beacon_time; int missed_beacon_threshold; /* track IBSS manager (last beacon) status */ u32 ibss_manager; /* jiffies when last recovery from statistics was performed */ unsigned long rx_statistics_jiffies; /*counters */ u32 rx_handlers_stats[REPLY_MAX]; /* rf reset */ struct iwl_rf_reset rf_reset; /* firmware reload counter and timestamp */ unsigned long reload_jiffies; int reload_count; bool ucode_loaded; bool init_ucode_run; /* Don't run init uCode again */ u8 plcp_delta_threshold; /* thermal calibration */ s32 temperature; /* Celsius */ s32 last_temperature; struct iwl_wipan_noa_data __rcu *noa_data; /* Scan related variables */ unsigned long scan_start; unsigned long scan_start_tsf; void *scan_cmd; enum ieee80211_band scan_band; struct cfg80211_scan_request *scan_request; struct ieee80211_vif *scan_vif; enum iwl_scan_type scan_type; u8 scan_tx_ant[IEEE80211_NUM_BANDS]; u8 mgmt_tx_ant; /* max number of station keys */ u8 sta_key_max_num; bool new_scan_threshold_behaviour; bool wowlan; /* EEPROM MAC addresses */ struct mac_address addresses[2]; struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; __le16 switch_channel; u8 start_calib; struct iwl_sensitivity_data sensitivity_data; struct iwl_chain_noise_data chain_noise_data; __le16 sensitivity_tbl[HD_TABLE_SIZE]; __le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES]; struct iwl_ht_config current_ht_config; /* Rate scaling data */ u8 retry_rate; int activity_timer_active; struct iwl_power_mgr power_data; struct iwl_tt_mgmt thermal_throttle; /* station table variables */ int num_stations; struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; unsigned long ucode_key_table; struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; atomic_t num_aux_in_flight; u8 mac80211_registered; /* Indication if ieee80211_ops->open has been called */ u8 is_open; enum nl80211_iftype iw_mode; /* Last Rx'd beacon timestamp */ u64 timestamp; struct { __le32 flag; struct statistics_general_common common; struct statistics_rx_non_phy rx_non_phy; struct statistics_rx_phy rx_ofdm; struct statistics_rx_ht_phy rx_ofdm_ht; struct statistics_rx_phy rx_cck; struct statistics_tx tx; #ifdef CONFIG_IWLWIFI_DEBUGFS struct statistics_bt_activity bt_activity; __le32 num_bt_kills, accum_num_bt_kills; #endif spinlock_t lock; } statistics; #ifdef CONFIG_IWLWIFI_DEBUGFS struct { struct statistics_general_common common; struct statistics_rx_non_phy rx_non_phy; struct statistics_rx_phy rx_ofdm; struct statistics_rx_ht_phy rx_ofdm_ht; struct statistics_rx_phy rx_cck; struct statistics_tx tx; struct statistics_bt_activity bt_activity; } accum_stats, delta_stats, max_delta_stats; #endif /* * reporting the number of tids has AGG on. 0 means * no AGGREGATION */ u8 agg_tids_count; struct iwl_rx_phy_res last_phy_res; u32 ampdu_ref; bool last_phy_res_valid; /* * chain noise reset and gain commands are the * two extra calibration commands follows the standard * phy calibration commands */ u8 phy_calib_chain_noise_reset_cmd; u8 phy_calib_chain_noise_gain_cmd; /* counts reply_tx error */ struct reply_tx_error_statistics reply_tx_stats; struct reply_agg_tx_error_statistics reply_agg_tx_stats; /* remain-on-channel offload support */ struct ieee80211_channel *hw_roc_channel; struct delayed_work hw_roc_disable_work; enum nl80211_channel_type hw_roc_chantype; int hw_roc_duration; bool hw_roc_setup, hw_roc_start_notified; /* bt coex */ u8 bt_enable_flag; u8 bt_status; u8 bt_traffic_load, last_bt_traffic_load; bool bt_ch_announce; bool bt_full_concurrent; bool bt_ant_couple_ok; __le32 kill_ack_mask; __le32 kill_cts_mask; __le16 bt_valid; bool reduced_txpower; u16 bt_on_thresh; u16 bt_duration; u16 dynamic_frag_thresh; u8 bt_ci_compliance; struct work_struct bt_traffic_change_work; bool bt_enable_pspoll; struct iwl_rxon_context *cur_rssi_ctx; bool bt_is_sco; struct work_struct restart; struct work_struct scan_completed; struct work_struct abort_scan; struct work_struct beacon_update; struct iwl_rxon_context *beacon_ctx; struct sk_buff *beacon_skb; void *beacon_cmd; struct work_struct tt_work; struct work_struct ct_enter; struct work_struct ct_exit; struct work_struct start_internal_scan; struct work_struct tx_flush; struct work_struct bt_full_concurrency; struct work_struct bt_runtime_config; struct delayed_work scan_check; /* TX Power settings */ s8 tx_power_user_lmt; s8 tx_power_next; #ifdef CONFIG_IWLWIFI_DEBUGFS /* debugfs */ struct dentry *debugfs_dir; u32 dbgfs_sram_offset, dbgfs_sram_len; bool disable_ht40; void *wowlan_sram; #endif /* CONFIG_IWLWIFI_DEBUGFS */ struct iwl_eeprom_data *eeprom_data; /* eeprom blob for debugfs/testmode */ u8 *eeprom_blob; size_t eeprom_blob_size; struct work_struct txpower_work; u32 calib_disabled; struct work_struct run_time_calib_work; struct timer_list statistics_periodic; struct timer_list ucode_trace; struct iwl_event_log event_log; struct led_classdev led; unsigned long blink_on, blink_off; bool led_registered; #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE struct iwl_test tst; u32 tm_fixed_rate; #endif /* WoWLAN GTK rekey data */ u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; __le64 replay_ctr; __le16 last_seq_ctl; bool have_rekey_data; /* device_pointers: pointers to ucode event tables */ struct { u32 error_event_table; u32 log_event_table; } device_pointers; /* indicator of loaded ucode image */ enum iwl_ucode_type cur_ucode; }; /*iwl_priv */ static inline struct iwl_rxon_context * iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif) { struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; return vif_priv->ctx; } #define for_each_context(priv, ctx) \ for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \ ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \ if (priv->valid_contexts & BIT(ctx->ctxid)) static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx) { return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; } static inline int iwl_is_associated(struct iwl_priv *priv, enum iwl_rxon_context_id ctxid) { return iwl_is_associated_ctx(&priv->contexts[ctxid]); } static inline int iwl_is_any_associated(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; for_each_context(priv, ctx) if (iwl_is_associated_ctx(ctx)) return true; return false; } #endif /* __iwl_dev_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/debugfs.c0000644000175000017500000023524112026211315024513 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include #include #include #include #include #include #include "iwl-debug.h" #include "iwl-io.h" #include "dev.h" #include "agn.h" /* create and remove of files */ #define DEBUGFS_ADD_FILE(name, parent, mode) do { \ if (!debugfs_create_file(#name, mode, parent, priv, \ &iwl_dbgfs_##name##_ops)) \ goto err; \ } while (0) #define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ struct dentry *__tmp; \ __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ parent, ptr); \ if (IS_ERR(__tmp) || !__tmp) \ goto err; \ } while (0) #define DEBUGFS_ADD_X32(name, parent, ptr) do { \ struct dentry *__tmp; \ __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ parent, ptr); \ if (IS_ERR(__tmp) || !__tmp) \ goto err; \ } while (0) #define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \ struct dentry *__tmp; \ __tmp = debugfs_create_u32(#name, mode, \ parent, ptr); \ if (IS_ERR(__tmp) || !__tmp) \ goto err; \ } while (0) /* file operation */ #define DEBUGFS_READ_FUNC(name) \ static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ char __user *user_buf, \ size_t count, loff_t *ppos); #define DEBUGFS_WRITE_FUNC(name) \ static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos); #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_WRITE_FILE_OPS(name) \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_READ_WRITE_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .read = iwl_dbgfs_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { u32 val = 0; char *buf; ssize_t ret; int i = 0; bool device_format = false; int offset = 0; int len = 0; int pos = 0; int sram; struct iwl_priv *priv = file->private_data; const struct fw_img *img; size_t bufsz; if (!iwl_is_ready_rf(priv)) return -EAGAIN; /* default is to dump the entire data segment */ if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { priv->dbgfs_sram_offset = 0x800000; if (!priv->ucode_loaded) return -EINVAL; img = &priv->fw->img[priv->cur_ucode]; priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; } len = priv->dbgfs_sram_len; if (len == -4) { device_format = true; len = 4; } bufsz = 50 + len * 4; buf = kmalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len); pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", priv->dbgfs_sram_offset); /* adjust sram address since reads are only on even u32 boundaries */ offset = priv->dbgfs_sram_offset & 0x3; sram = priv->dbgfs_sram_offset & ~0x3; /* read the first u32 from sram */ val = iwl_read_targ_mem(priv->trans, sram); for (; len; len--) { /* put the address at the start of every line */ if (i == 0) pos += scnprintf(buf + pos, bufsz - pos, "%08X: ", sram + offset); if (device_format) pos += scnprintf(buf + pos, bufsz - pos, "%02x", (val >> (8 * (3 - offset))) & 0xff); else pos += scnprintf(buf + pos, bufsz - pos, "%02x ", (val >> (8 * offset)) & 0xff); /* if all bytes processed, read the next u32 from sram */ if (++offset == 4) { sram += 4; offset = 0; val = iwl_read_targ_mem(priv->trans, sram); } /* put in extra spaces and split lines for human readability */ if (++i == 16) { i = 0; pos += scnprintf(buf + pos, bufsz - pos, "\n"); } else if (!(i & 7)) { pos += scnprintf(buf + pos, bufsz - pos, " "); } else if (!(i & 3)) { pos += scnprintf(buf + pos, bufsz - pos, " "); } } if (i) pos += scnprintf(buf + pos, bufsz - pos, "\n"); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_sram_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[64]; int buf_size; u32 offset, len; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x,%x", &offset, &len) == 2) { priv->dbgfs_sram_offset = offset; priv->dbgfs_sram_len = len; } else if (sscanf(buf, "%x", &offset) == 1) { priv->dbgfs_sram_offset = offset; priv->dbgfs_sram_len = -4; } else { priv->dbgfs_sram_offset = 0; priv->dbgfs_sram_len = 0; } return count; } static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN]; if (!priv->wowlan_sram) return -ENODATA; return simple_read_from_buffer(user_buf, count, ppos, priv->wowlan_sram, img->sec[IWL_UCODE_SECTION_DATA].len); } static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; struct iwl_station_entry *station; struct iwl_tid_data *tid_data; char *buf; int i, j, pos = 0; ssize_t ret; /* Add 30 for initial string */ const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations); buf = kmalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", priv->num_stations); for (i = 0; i < IWLAGN_STATION_COUNT; i++) { station = &priv->stations[i]; if (!station->used) continue; pos += scnprintf(buf + pos, bufsz - pos, "station %d - addr: %pM, flags: %#x\n", i, station->sta.sta.addr, station->sta.station_flags_msk); pos += scnprintf(buf + pos, bufsz - pos, "TID seqno next_rclmd " "rate_n_flags state txq\n"); for (j = 0; j < IWL_MAX_TID_COUNT; j++) { tid_data = &priv->tid_data[i][j]; pos += scnprintf(buf + pos, bufsz - pos, "%d: 0x%.4x 0x%.4x 0x%.8x " "%d %.2d", j, tid_data->seq_number, tid_data->next_reclaimed, tid_data->agg.rate_n_flags, tid_data->agg.state, tid_data->agg.txq_id); if (tid_data->agg.wait_for_ba) pos += scnprintf(buf + pos, bufsz - pos, " - waitforba"); pos += scnprintf(buf + pos, bufsz - pos, "\n"); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_nvm_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { ssize_t ret; struct iwl_priv *priv = file->private_data; int pos = 0, ofs = 0, buf_size = 0; const u8 *ptr; char *buf; u16 eeprom_ver; size_t eeprom_len = priv->eeprom_blob_size; buf_size = 4 * eeprom_len + 256; if (eeprom_len % 16) return -ENODATA; ptr = priv->eeprom_blob; if (!ptr) return -ENOMEM; /* 4 characters for byte 0xYY */ buf = kzalloc(buf_size, GFP_KERNEL); if (!buf) return -ENOMEM; eeprom_ver = priv->eeprom_data->eeprom_version; pos += scnprintf(buf + pos, buf_size - pos, "NVM version: 0x%x\n", eeprom_ver); for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos, buf_size - pos, 0); pos += strlen(buf + pos); if (buf_size - pos > 0) buf[pos++] = '\n'; } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; struct ieee80211_channel *channels = NULL; const struct ieee80211_supported_band *supp_band = NULL; int pos = 0, i, bufsz = PAGE_SIZE; char *buf; ssize_t ret; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); if (supp_band) { channels = supp_band->channels; pos += scnprintf(buf + pos, bufsz - pos, "Displaying %d channels in 2.4GHz band 802.11bg):\n", supp_band->n_channels); for (i = 0; i < supp_band->n_channels; i++) pos += scnprintf(buf + pos, bufsz - pos, "%d: %ddBm: BSS%s%s, %s.\n", channels[i].hw_value, channels[i].max_power, channels[i].flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) || (channels[i].flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN ? "passive only" : "active/passive"); } supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); if (supp_band) { channels = supp_band->channels; pos += scnprintf(buf + pos, bufsz - pos, "Displaying %d channels in 5.2GHz band (802.11a)\n", supp_band->n_channels); for (i = 0; i < supp_band->n_channels; i++) pos += scnprintf(buf + pos, bufsz - pos, "%d: %ddBm: BSS%s%s, %s.\n", channels[i].hw_value, channels[i].max_power, channels[i].flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) || (channels[i].flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN ? "passive only" : "active/passive"); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[512]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", test_bit(STATUS_RF_KILL_HW, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n", test_bit(STATUS_CT_KILL, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", test_bit(STATUS_ALIVE, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", test_bit(STATUS_READY, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", test_bit(STATUS_EXIT_PENDING, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", test_bit(STATUS_STATISTICS, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n", test_bit(STATUS_SCANNING, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n", test_bit(STATUS_SCAN_ABORTING, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n", test_bit(STATUS_SCAN_HW, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n", test_bit(STATUS_POWER_PMI, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n", test_bit(STATUS_FW_ERROR, &priv->status)); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; int bufsz = 24 * 64; /* 24 items * 64 char per item */ ssize_t ret; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; for (cnt = 0; cnt < REPLY_MAX; cnt++) { if (priv->rx_handlers_stats[cnt] > 0) pos += scnprintf(buf + pos, bufsz - pos, "\tRx handler[%36s]:\t\t %u\n", iwl_dvm_get_cmd_string(cnt), priv->rx_handlers_stats[cnt]); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; u32 reset_flag; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &reset_flag) != 1) return -EFAULT; if (reset_flag == 0) memset(&priv->rx_handlers_stats[0], 0, sizeof(priv->rx_handlers_stats)); return count; } static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; struct iwl_rxon_context *ctx; int pos = 0, i; char buf[256 * NUM_IWL_RXON_CTX]; const size_t bufsz = sizeof(buf); for_each_context(priv, ctx) { pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n", ctx->ctxid); for (i = 0; i < AC_NUM; i++) { pos += scnprintf(buf + pos, bufsz - pos, "\tcw_min\tcw_max\taifsn\ttxop\n"); pos += scnprintf(buf + pos, bufsz - pos, "AC[%d]\t%u\t%u\t%u\t%u\n", i, ctx->qos_data.def_qos_parm.ac[i].cw_min, ctx->qos_data.def_qos_parm.ac[i].cw_max, ctx->qos_data.def_qos_parm.ac[i].aifsn, ctx->qos_data.def_qos_parm.ac[i].edca_txop); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); } return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; struct iwl_tt_mgmt *tt = &priv->thermal_throttle; struct iwl_tt_restriction *restriction; char buf[100]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "Thermal Throttling Mode: %s\n", tt->advanced_tt ? "Advance" : "Legacy"); pos += scnprintf(buf + pos, bufsz - pos, "Thermal Throttling State: %d\n", tt->state); if (tt->advanced_tt) { restriction = tt->restriction + tt->state; pos += scnprintf(buf + pos, bufsz - pos, "Tx mode: %d\n", restriction->tx_stream); pos += scnprintf(buf + pos, bufsz - pos, "Rx mode: %d\n", restriction->rx_stream); pos += scnprintf(buf + pos, bufsz - pos, "HT mode: %d\n", restriction->is_ht); } return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int ht40; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &ht40) != 1) return -EFAULT; if (!iwl_is_any_associated(priv)) priv->disable_ht40 = ht40 ? true : false; else return -EINVAL; return count; } static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[100]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "11n 40MHz Mode: %s\n", priv->disable_ht40 ? "Disabled" : "Enabled"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_temperature_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int value; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &value) != 1) return -EINVAL; /* * Our users expect 0 to be "CAM", but 0 isn't actually * valid here. However, let's not confuse them and present * IWL_POWER_INDEX_1 as "1", not "0". */ if (value == 0) return -EINVAL; else if (value > 0) value -= 1; if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) return -EINVAL; if (!iwl_is_ready_rf(priv)) return -EAGAIN; priv->power_data.debug_sleep_level_override = value; mutex_lock(&priv->mutex); iwl_power_update_mode(priv, true); mutex_unlock(&priv->mutex); return count; } static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[10]; int pos, value; const size_t bufsz = sizeof(buf); /* see the write function */ value = priv->power_data.debug_sleep_level_override; if (value >= 0) value += 1; pos = scnprintf(buf, bufsz, "%d\n", value); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[200]; int pos = 0, i; const size_t bufsz = sizeof(buf); struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd; pos += scnprintf(buf + pos, bufsz - pos, "flags: %#.2x\n", le16_to_cpu(cmd->flags)); pos += scnprintf(buf + pos, bufsz - pos, "RX/TX timeout: %d/%d usec\n", le32_to_cpu(cmd->rx_data_timeout), le32_to_cpu(cmd->tx_data_timeout)); for (i = 0; i < IWL_POWER_VEC_SIZE; i++) pos += scnprintf(buf + pos, bufsz - pos, "sleep_interval[%d]: %d\n", i, le32_to_cpu(cmd->sleep_interval[i])); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } DEBUGFS_READ_WRITE_FILE_OPS(sram); DEBUGFS_READ_FILE_OPS(wowlan_sram); DEBUGFS_READ_FILE_OPS(nvm); DEBUGFS_READ_FILE_OPS(stations); DEBUGFS_READ_FILE_OPS(channels); DEBUGFS_READ_FILE_OPS(status); DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers); DEBUGFS_READ_FILE_OPS(qos); DEBUGFS_READ_FILE_OPS(thermal_throttling); DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); DEBUGFS_READ_FILE_OPS(temperature); DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); DEBUGFS_READ_FILE_OPS(current_sleep_command); static const char *fmt_value = " %-30s %10u\n"; static const char *fmt_hex = " %-30s 0x%02X\n"; static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; static const char *fmt_header = "%-32s current cumulative delta max\n"; static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) { int p = 0; u32 flag; lockdep_assert_held(&priv->statistics.lock); flag = le32_to_cpu(priv->statistics.flag); p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); if (flag & UCODE_STATISTICS_CLEAR_MSK) p += scnprintf(buf + p, bufsz - p, "\tStatistics have been cleared\n"); p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", (flag & UCODE_STATISTICS_FREQUENCY_MSK) ? "2.4 GHz" : "5.2 GHz"); p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", (flag & UCODE_STATISTICS_NARROW_BAND_MSK) ? "enabled" : "disabled"); return p; } static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct statistics_rx_phy) * 40 + sizeof(struct statistics_rx_non_phy) * 40 + sizeof(struct statistics_rx_ht_phy) * 40 + 400; ssize_t ret; struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; struct statistics_rx_non_phy *general, *accum_general; struct statistics_rx_non_phy *delta_general, *max_general; struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; if (!iwl_is_alive(priv)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; /* * the statistic information display here is based on * the last statistics notification from uCode * might not reflect the current uCode activity */ spin_lock_bh(&priv->statistics.lock); ofdm = &priv->statistics.rx_ofdm; cck = &priv->statistics.rx_cck; general = &priv->statistics.rx_non_phy; ht = &priv->statistics.rx_ofdm_ht; accum_ofdm = &priv->accum_stats.rx_ofdm; accum_cck = &priv->accum_stats.rx_cck; accum_general = &priv->accum_stats.rx_non_phy; accum_ht = &priv->accum_stats.rx_ofdm_ht; delta_ofdm = &priv->delta_stats.rx_ofdm; delta_cck = &priv->delta_stats.rx_cck; delta_general = &priv->delta_stats.rx_non_phy; delta_ht = &priv->delta_stats.rx_ofdm_ht; max_ofdm = &priv->max_delta_stats.rx_ofdm; max_cck = &priv->max_delta_stats.rx_cck; max_general = &priv->max_delta_stats.rx_non_phy; max_ht = &priv->max_delta_stats.rx_ofdm_ht; pos += iwl_statistics_flag(priv, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - OFDM:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt, delta_ofdm->ina_cnt, max_ofdm->ina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, delta_ofdm->fina_cnt, max_ofdm->fina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, delta_ofdm->plcp_err, max_ofdm->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, delta_ofdm->crc32_err, max_ofdm->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", le32_to_cpu(ofdm->overrun_err), accum_ofdm->overrun_err, delta_ofdm->overrun_err, max_ofdm->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", le32_to_cpu(ofdm->early_overrun_err), accum_ofdm->early_overrun_err, delta_ofdm->early_overrun_err, max_ofdm->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", le32_to_cpu(ofdm->crc32_good), accum_ofdm->crc32_good, delta_ofdm->crc32_good, max_ofdm->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", le32_to_cpu(ofdm->false_alarm_cnt), accum_ofdm->false_alarm_cnt, delta_ofdm->false_alarm_cnt, max_ofdm->false_alarm_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", le32_to_cpu(ofdm->fina_sync_err_cnt), accum_ofdm->fina_sync_err_cnt, delta_ofdm->fina_sync_err_cnt, max_ofdm->fina_sync_err_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", le32_to_cpu(ofdm->sfd_timeout), accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, max_ofdm->sfd_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", le32_to_cpu(ofdm->fina_timeout), accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, max_ofdm->fina_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", le32_to_cpu(ofdm->unresponded_rts), accum_ofdm->unresponded_rts, delta_ofdm->unresponded_rts, max_ofdm->unresponded_rts); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", le32_to_cpu(ofdm->rxe_frame_limit_overrun), accum_ofdm->rxe_frame_limit_overrun, delta_ofdm->rxe_frame_limit_overrun, max_ofdm->rxe_frame_limit_overrun); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", le32_to_cpu(ofdm->sent_ack_cnt), accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, max_ofdm->sent_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", le32_to_cpu(ofdm->sent_cts_cnt), accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", le32_to_cpu(ofdm->sent_ba_rsp_cnt), accum_ofdm->sent_ba_rsp_cnt, delta_ofdm->sent_ba_rsp_cnt, max_ofdm->sent_ba_rsp_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", le32_to_cpu(ofdm->dsp_self_kill), accum_ofdm->dsp_self_kill, delta_ofdm->dsp_self_kill, max_ofdm->dsp_self_kill); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", le32_to_cpu(ofdm->mh_format_err), accum_ofdm->mh_format_err, delta_ofdm->mh_format_err, max_ofdm->mh_format_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "re_acq_main_rssi_sum:", le32_to_cpu(ofdm->re_acq_main_rssi_sum), accum_ofdm->re_acq_main_rssi_sum, delta_ofdm->re_acq_main_rssi_sum, max_ofdm->re_acq_main_rssi_sum); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - CCK:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, delta_cck->ina_cnt, max_cck->ina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, delta_cck->fina_cnt, max_cck->fina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, delta_cck->plcp_err, max_cck->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, delta_cck->crc32_err, max_cck->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", le32_to_cpu(cck->overrun_err), accum_cck->overrun_err, delta_cck->overrun_err, max_cck->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", le32_to_cpu(cck->early_overrun_err), accum_cck->early_overrun_err, delta_cck->early_overrun_err, max_cck->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, delta_cck->crc32_good, max_cck->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", le32_to_cpu(cck->false_alarm_cnt), accum_cck->false_alarm_cnt, delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", le32_to_cpu(cck->fina_sync_err_cnt), accum_cck->fina_sync_err_cnt, delta_cck->fina_sync_err_cnt, max_cck->fina_sync_err_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", le32_to_cpu(cck->sfd_timeout), accum_cck->sfd_timeout, delta_cck->sfd_timeout, max_cck->sfd_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", le32_to_cpu(cck->fina_timeout), accum_cck->fina_timeout, delta_cck->fina_timeout, max_cck->fina_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", le32_to_cpu(cck->unresponded_rts), accum_cck->unresponded_rts, delta_cck->unresponded_rts, max_cck->unresponded_rts); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", le32_to_cpu(cck->rxe_frame_limit_overrun), accum_cck->rxe_frame_limit_overrun, delta_cck->rxe_frame_limit_overrun, max_cck->rxe_frame_limit_overrun); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", le32_to_cpu(cck->sent_ack_cnt), accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, max_cck->sent_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", le32_to_cpu(cck->sent_cts_cnt), accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, max_cck->sent_cts_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", le32_to_cpu(cck->sent_ba_rsp_cnt), accum_cck->sent_ba_rsp_cnt, delta_cck->sent_ba_rsp_cnt, max_cck->sent_ba_rsp_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", le32_to_cpu(cck->dsp_self_kill), accum_cck->dsp_self_kill, delta_cck->dsp_self_kill, max_cck->dsp_self_kill); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", le32_to_cpu(cck->mh_format_err), accum_cck->mh_format_err, delta_cck->mh_format_err, max_cck->mh_format_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "re_acq_main_rssi_sum:", le32_to_cpu(cck->re_acq_main_rssi_sum), accum_cck->re_acq_main_rssi_sum, delta_cck->re_acq_main_rssi_sum, max_cck->re_acq_main_rssi_sum); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - GENERAL:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_cts:", le32_to_cpu(general->bogus_cts), accum_general->bogus_cts, delta_general->bogus_cts, max_general->bogus_cts); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_ack:", le32_to_cpu(general->bogus_ack), accum_general->bogus_ack, delta_general->bogus_ack, max_general->bogus_ack); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "non_bssid_frames:", le32_to_cpu(general->non_bssid_frames), accum_general->non_bssid_frames, delta_general->non_bssid_frames, max_general->non_bssid_frames); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "filtered_frames:", le32_to_cpu(general->filtered_frames), accum_general->filtered_frames, delta_general->filtered_frames, max_general->filtered_frames); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "non_channel_beacons:", le32_to_cpu(general->non_channel_beacons), accum_general->non_channel_beacons, delta_general->non_channel_beacons, max_general->non_channel_beacons); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_beacons:", le32_to_cpu(general->channel_beacons), accum_general->channel_beacons, delta_general->channel_beacons, max_general->channel_beacons); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "num_missed_bcon:", le32_to_cpu(general->num_missed_bcon), accum_general->num_missed_bcon, delta_general->num_missed_bcon, max_general->num_missed_bcon); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "adc_rx_saturation_time:", le32_to_cpu(general->adc_rx_saturation_time), accum_general->adc_rx_saturation_time, delta_general->adc_rx_saturation_time, max_general->adc_rx_saturation_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_detect_search_tm:", le32_to_cpu(general->ina_detection_search_time), accum_general->ina_detection_search_time, delta_general->ina_detection_search_time, max_general->ina_detection_search_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_silence_rssi_a:", le32_to_cpu(general->beacon_silence_rssi_a), accum_general->beacon_silence_rssi_a, delta_general->beacon_silence_rssi_a, max_general->beacon_silence_rssi_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_silence_rssi_b:", le32_to_cpu(general->beacon_silence_rssi_b), accum_general->beacon_silence_rssi_b, delta_general->beacon_silence_rssi_b, max_general->beacon_silence_rssi_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_silence_rssi_c:", le32_to_cpu(general->beacon_silence_rssi_c), accum_general->beacon_silence_rssi_c, delta_general->beacon_silence_rssi_c, max_general->beacon_silence_rssi_c); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "interference_data_flag:", le32_to_cpu(general->interference_data_flag), accum_general->interference_data_flag, delta_general->interference_data_flag, max_general->interference_data_flag); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_load:", le32_to_cpu(general->channel_load), accum_general->channel_load, delta_general->channel_load, max_general->channel_load); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_false_alarms:", le32_to_cpu(general->dsp_false_alarms), accum_general->dsp_false_alarms, delta_general->dsp_false_alarms, max_general->dsp_false_alarms); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_a:", le32_to_cpu(general->beacon_rssi_a), accum_general->beacon_rssi_a, delta_general->beacon_rssi_a, max_general->beacon_rssi_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_b:", le32_to_cpu(general->beacon_rssi_b), accum_general->beacon_rssi_b, delta_general->beacon_rssi_b, max_general->beacon_rssi_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_c:", le32_to_cpu(general->beacon_rssi_c), accum_general->beacon_rssi_c, delta_general->beacon_rssi_c, max_general->beacon_rssi_c); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_a:", le32_to_cpu(general->beacon_energy_a), accum_general->beacon_energy_a, delta_general->beacon_energy_a, max_general->beacon_energy_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_b:", le32_to_cpu(general->beacon_energy_b), accum_general->beacon_energy_b, delta_general->beacon_energy_b, max_general->beacon_energy_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_c:", le32_to_cpu(general->beacon_energy_c), accum_general->beacon_energy_c, delta_general->beacon_energy_c, max_general->beacon_energy_c); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - OFDM_HT:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, delta_ht->plcp_err, max_ht->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, delta_ht->overrun_err, max_ht->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", le32_to_cpu(ht->early_overrun_err), accum_ht->early_overrun_err, delta_ht->early_overrun_err, max_ht->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, delta_ht->crc32_good, max_ht->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, delta_ht->crc32_err, max_ht->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", le32_to_cpu(ht->mh_format_err), accum_ht->mh_format_err, delta_ht->mh_format_err, max_ht->mh_format_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_crc32_good:", le32_to_cpu(ht->agg_crc32_good), accum_ht->agg_crc32_good, delta_ht->agg_crc32_good, max_ht->agg_crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_mpdu_cnt:", le32_to_cpu(ht->agg_mpdu_cnt), accum_ht->agg_mpdu_cnt, delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_cnt:", le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, delta_ht->agg_cnt, max_ht->agg_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "unsupport_mcs:", le32_to_cpu(ht->unsupport_mcs), accum_ht->unsupport_mcs, delta_ht->unsupport_mcs, max_ht->unsupport_mcs); spin_unlock_bh(&priv->statistics.lock); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; int bufsz = (sizeof(struct statistics_tx) * 48) + 250; ssize_t ret; struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; if (!iwl_is_alive(priv)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; /* the statistic information display here is based on * the last statistics notification from uCode * might not reflect the current uCode activity */ spin_lock_bh(&priv->statistics.lock); tx = &priv->statistics.tx; accum_tx = &priv->accum_stats.tx; delta_tx = &priv->delta_stats.tx; max_tx = &priv->max_delta_stats.tx; pos += iwl_statistics_flag(priv, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Tx:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "preamble:", le32_to_cpu(tx->preamble_cnt), accum_tx->preamble_cnt, delta_tx->preamble_cnt, max_tx->preamble_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_detected_cnt:", le32_to_cpu(tx->rx_detected_cnt), accum_tx->rx_detected_cnt, delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_defer_cnt:", le32_to_cpu(tx->bt_prio_defer_cnt), accum_tx->bt_prio_defer_cnt, delta_tx->bt_prio_defer_cnt, max_tx->bt_prio_defer_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_kill_cnt:", le32_to_cpu(tx->bt_prio_kill_cnt), accum_tx->bt_prio_kill_cnt, delta_tx->bt_prio_kill_cnt, max_tx->bt_prio_kill_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "few_bytes_cnt:", le32_to_cpu(tx->few_bytes_cnt), accum_tx->few_bytes_cnt, delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "cts_timeout:", le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, delta_tx->cts_timeout, max_tx->cts_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ack_timeout:", le32_to_cpu(tx->ack_timeout), accum_tx->ack_timeout, delta_tx->ack_timeout, max_tx->ack_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "expected_ack_cnt:", le32_to_cpu(tx->expected_ack_cnt), accum_tx->expected_ack_cnt, delta_tx->expected_ack_cnt, max_tx->expected_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "actual_ack_cnt:", le32_to_cpu(tx->actual_ack_cnt), accum_tx->actual_ack_cnt, delta_tx->actual_ack_cnt, max_tx->actual_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dump_msdu_cnt:", le32_to_cpu(tx->dump_msdu_cnt), accum_tx->dump_msdu_cnt, delta_tx->dump_msdu_cnt, max_tx->dump_msdu_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "abort_nxt_frame_mismatch:", le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), accum_tx->burst_abort_next_frame_mismatch_cnt, delta_tx->burst_abort_next_frame_mismatch_cnt, max_tx->burst_abort_next_frame_mismatch_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "abort_missing_nxt_frame:", le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), accum_tx->burst_abort_missing_next_frame_cnt, delta_tx->burst_abort_missing_next_frame_cnt, max_tx->burst_abort_missing_next_frame_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "cts_timeout_collision:", le32_to_cpu(tx->cts_timeout_collision), accum_tx->cts_timeout_collision, delta_tx->cts_timeout_collision, max_tx->cts_timeout_collision); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ack_ba_timeout_collision:", le32_to_cpu(tx->ack_or_ba_timeout_collision), accum_tx->ack_or_ba_timeout_collision, delta_tx->ack_or_ba_timeout_collision, max_tx->ack_or_ba_timeout_collision); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg ba_timeout:", le32_to_cpu(tx->agg.ba_timeout), accum_tx->agg.ba_timeout, delta_tx->agg.ba_timeout, max_tx->agg.ba_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg ba_resched_frames:", le32_to_cpu(tx->agg.ba_reschedule_frames), accum_tx->agg.ba_reschedule_frames, delta_tx->agg.ba_reschedule_frames, max_tx->agg.ba_reschedule_frames); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_agg_frame:", le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), accum_tx->agg.scd_query_agg_frame_cnt, delta_tx->agg.scd_query_agg_frame_cnt, max_tx->agg.scd_query_agg_frame_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_no_agg:", le32_to_cpu(tx->agg.scd_query_no_agg), accum_tx->agg.scd_query_no_agg, delta_tx->agg.scd_query_no_agg, max_tx->agg.scd_query_no_agg); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_agg:", le32_to_cpu(tx->agg.scd_query_agg), accum_tx->agg.scd_query_agg, delta_tx->agg.scd_query_agg, max_tx->agg.scd_query_agg); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_mismatch:", le32_to_cpu(tx->agg.scd_query_mismatch), accum_tx->agg.scd_query_mismatch, delta_tx->agg.scd_query_mismatch, max_tx->agg.scd_query_mismatch); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg frame_not_ready:", le32_to_cpu(tx->agg.frame_not_ready), accum_tx->agg.frame_not_ready, delta_tx->agg.frame_not_ready, max_tx->agg.frame_not_ready); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg underrun:", le32_to_cpu(tx->agg.underrun), accum_tx->agg.underrun, delta_tx->agg.underrun, max_tx->agg.underrun); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg bt_prio_kill:", le32_to_cpu(tx->agg.bt_prio_kill), accum_tx->agg.bt_prio_kill, delta_tx->agg.bt_prio_kill, max_tx->agg.bt_prio_kill); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg rx_ba_rsp_cnt:", le32_to_cpu(tx->agg.rx_ba_rsp_cnt), accum_tx->agg.rx_ba_rsp_cnt, delta_tx->agg.rx_ba_rsp_cnt, max_tx->agg.rx_ba_rsp_cnt); if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) { pos += scnprintf(buf + pos, bufsz - pos, "tx power: (1/2 dB step)\n"); if ((priv->eeprom_data->valid_tx_ant & ANT_A) && tx->tx_power.ant_a) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna A:", tx->tx_power.ant_a); if ((priv->eeprom_data->valid_tx_ant & ANT_B) && tx->tx_power.ant_b) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna B:", tx->tx_power.ant_b); if ((priv->eeprom_data->valid_tx_ant & ANT_C) && tx->tx_power.ant_c) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna C:", tx->tx_power.ant_c); } spin_unlock_bh(&priv->statistics.lock); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct statistics_general) * 10 + 300; ssize_t ret; struct statistics_general_common *general, *accum_general; struct statistics_general_common *delta_general, *max_general; struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; struct statistics_div *div, *accum_div, *delta_div, *max_div; if (!iwl_is_alive(priv)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; /* the statistic information display here is based on * the last statistics notification from uCode * might not reflect the current uCode activity */ spin_lock_bh(&priv->statistics.lock); general = &priv->statistics.common; dbg = &priv->statistics.common.dbg; div = &priv->statistics.common.div; accum_general = &priv->accum_stats.common; accum_dbg = &priv->accum_stats.common.dbg; accum_div = &priv->accum_stats.common.div; delta_general = &priv->delta_stats.common; max_general = &priv->max_delta_stats.common; delta_dbg = &priv->delta_stats.common.dbg; max_dbg = &priv->max_delta_stats.common.dbg; delta_div = &priv->delta_stats.common.div; max_div = &priv->max_delta_stats.common.div; pos += iwl_statistics_flag(priv, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_General:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_value, "temperature:", le32_to_cpu(general->temperature)); pos += scnprintf(buf + pos, bufsz - pos, fmt_value, "temperature_m:", le32_to_cpu(general->temperature_m)); pos += scnprintf(buf + pos, bufsz - pos, fmt_value, "ttl_timestamp:", le32_to_cpu(general->ttl_timestamp)); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_check:", le32_to_cpu(dbg->burst_check), accum_dbg->burst_check, delta_dbg->burst_check, max_dbg->burst_check); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_count:", le32_to_cpu(dbg->burst_count), accum_dbg->burst_count, delta_dbg->burst_count, max_dbg->burst_count); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "wait_for_silence_timeout_count:", le32_to_cpu(dbg->wait_for_silence_timeout_cnt), accum_dbg->wait_for_silence_timeout_cnt, delta_dbg->wait_for_silence_timeout_cnt, max_dbg->wait_for_silence_timeout_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sleep_time:", le32_to_cpu(general->sleep_time), accum_general->sleep_time, delta_general->sleep_time, max_general->sleep_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_out:", le32_to_cpu(general->slots_out), accum_general->slots_out, delta_general->slots_out, max_general->slots_out); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_idle:", le32_to_cpu(general->slots_idle), accum_general->slots_idle, delta_general->slots_idle, max_general->slots_idle); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_a:", le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, delta_div->tx_on_a, max_div->tx_on_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_b:", le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, delta_div->tx_on_b, max_div->tx_on_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "exec_time:", le32_to_cpu(div->exec_time), accum_div->exec_time, delta_div->exec_time, max_div->exec_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "probe_time:", le32_to_cpu(div->probe_time), accum_div->probe_time, delta_div->probe_time, max_div->probe_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_enable_counter:", le32_to_cpu(general->rx_enable_counter), accum_general->rx_enable_counter, delta_general->rx_enable_counter, max_general->rx_enable_counter); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "num_of_sos_states:", le32_to_cpu(general->num_of_sos_states), accum_general->num_of_sos_states, delta_general->num_of_sos_states, max_general->num_of_sos_states); spin_unlock_bh(&priv->statistics.lock); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; int pos = 0; char *buf; int bufsz = (sizeof(struct statistics_bt_activity) * 24) + 200; ssize_t ret; struct statistics_bt_activity *bt, *accum_bt; if (!iwl_is_alive(priv)) return -EAGAIN; if (!priv->bt_enable_flag) return -EINVAL; /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); ret = iwl_send_statistics_request(priv, CMD_SYNC, false); mutex_unlock(&priv->mutex); if (ret) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; /* * the statistic information display here is based on * the last statistics notification from uCode * might not reflect the current uCode activity */ spin_lock_bh(&priv->statistics.lock); bt = &priv->statistics.bt_activity; accum_bt = &priv->accum_stats.bt_activity; pos += iwl_statistics_flag(priv, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "Statistics_BT:\n"); pos += scnprintf(buf + pos, bufsz - pos, "\t\t\tcurrent\t\t\taccumulative\n"); pos += scnprintf(buf + pos, bufsz - pos, "hi_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", le32_to_cpu(bt->hi_priority_tx_req_cnt), accum_bt->hi_priority_tx_req_cnt); pos += scnprintf(buf + pos, bufsz - pos, "hi_priority_tx_denied_cnt:\t%u\t\t\t%u\n", le32_to_cpu(bt->hi_priority_tx_denied_cnt), accum_bt->hi_priority_tx_denied_cnt); pos += scnprintf(buf + pos, bufsz - pos, "lo_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", le32_to_cpu(bt->lo_priority_tx_req_cnt), accum_bt->lo_priority_tx_req_cnt); pos += scnprintf(buf + pos, bufsz - pos, "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n", le32_to_cpu(bt->lo_priority_tx_denied_cnt), accum_bt->lo_priority_tx_denied_cnt); pos += scnprintf(buf + pos, bufsz - pos, "hi_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", le32_to_cpu(bt->hi_priority_rx_req_cnt), accum_bt->hi_priority_rx_req_cnt); pos += scnprintf(buf + pos, bufsz - pos, "hi_priority_rx_denied_cnt:\t%u\t\t\t%u\n", le32_to_cpu(bt->hi_priority_rx_denied_cnt), accum_bt->hi_priority_rx_denied_cnt); pos += scnprintf(buf + pos, bufsz - pos, "lo_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", le32_to_cpu(bt->lo_priority_rx_req_cnt), accum_bt->lo_priority_rx_req_cnt); pos += scnprintf(buf + pos, bufsz - pos, "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n", le32_to_cpu(bt->lo_priority_rx_denied_cnt), accum_bt->lo_priority_rx_denied_cnt); pos += scnprintf(buf + pos, bufsz - pos, "(rx)num_bt_kills:\t\t%u\t\t\t%u\n", le32_to_cpu(priv->statistics.num_bt_kills), priv->statistics.accum_num_bt_kills); spin_unlock_bh(&priv->statistics.lock); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; int pos = 0; char *buf; int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) + (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200; ssize_t ret; if (!iwl_is_alive(priv)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n"); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY), priv->reply_tx_stats.pp_delay); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES), priv->reply_tx_stats.pp_few_bytes); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO), priv->reply_tx_stats.pp_bt_prio); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD), priv->reply_tx_stats.pp_quiet_period); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK), priv->reply_tx_stats.pp_calc_ttak); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", iwl_get_tx_fail_reason( TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY), priv->reply_tx_stats.int_crossed_retry); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT), priv->reply_tx_stats.short_limit); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT), priv->reply_tx_stats.long_limit); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN), priv->reply_tx_stats.fifo_underrun); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW), priv->reply_tx_stats.drain_flow); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH), priv->reply_tx_stats.rfkill_flush); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE), priv->reply_tx_stats.life_expire); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS), priv->reply_tx_stats.dest_ps); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED), priv->reply_tx_stats.host_abort); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY), priv->reply_tx_stats.pp_delay); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID), priv->reply_tx_stats.sta_invalid); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED), priv->reply_tx_stats.frag_drop); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE), priv->reply_tx_stats.tid_disable); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED), priv->reply_tx_stats.fifo_flush); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", iwl_get_tx_fail_reason( TX_STATUS_FAIL_INSUFFICIENT_CF_POLL), priv->reply_tx_stats.insuff_cf_poll); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX), priv->reply_tx_stats.fail_hw_drop); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", iwl_get_tx_fail_reason( TX_STATUS_FAIL_NO_BEACON_ON_RADAR), priv->reply_tx_stats.sta_color_mismatch); pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", priv->reply_tx_stats.unknown); pos += scnprintf(buf + pos, bufsz - pos, "\nStatistics_Agg_TX_Error:\n"); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK), priv->reply_agg_tx_stats.underrun); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK), priv->reply_agg_tx_stats.bt_prio); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK), priv->reply_agg_tx_stats.few_bytes); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK), priv->reply_agg_tx_stats.abort); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", iwl_get_agg_tx_fail_reason( AGG_TX_STATE_LAST_SENT_TTL_MSK), priv->reply_agg_tx_stats.last_sent_ttl); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", iwl_get_agg_tx_fail_reason( AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK), priv->reply_agg_tx_stats.last_sent_try); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", iwl_get_agg_tx_fail_reason( AGG_TX_STATE_LAST_SENT_BT_KILL_MSK), priv->reply_agg_tx_stats.last_sent_bt_kill); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK), priv->reply_agg_tx_stats.scd_query); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", iwl_get_agg_tx_fail_reason( AGG_TX_STATE_TEST_BAD_CRC32_MSK), priv->reply_agg_tx_stats.bad_crc32); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK), priv->reply_agg_tx_stats.response); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK), priv->reply_agg_tx_stats.dump_tx); pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK), priv->reply_agg_tx_stats.delay_tx); pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", priv->reply_agg_tx_stats.unknown); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; ssize_t ret; struct iwl_sensitivity_data *data; data = &priv->sensitivity_data; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", data->auto_corr_ofdm); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc:\t\t %u\n", data->auto_corr_ofdm_mrc); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", data->auto_corr_ofdm_x1); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc_x1:\t\t %u\n", data->auto_corr_ofdm_mrc_x1); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", data->auto_corr_cck); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", data->auto_corr_cck_mrc); pos += scnprintf(buf + pos, bufsz - pos, "last_bad_plcp_cnt_ofdm:\t\t %u\n", data->last_bad_plcp_cnt_ofdm); pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", data->last_fa_cnt_ofdm); pos += scnprintf(buf + pos, bufsz - pos, "last_bad_plcp_cnt_cck:\t\t %u\n", data->last_bad_plcp_cnt_cck); pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", data->last_fa_cnt_cck); pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", data->nrg_curr_state); pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", data->nrg_prev_state); pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); for (cnt = 0; cnt < 10; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->nrg_value[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->nrg_silence_rssi[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", data->nrg_silence_ref); pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", data->nrg_energy_idx); pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", data->nrg_silence_idx); pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", data->nrg_th_cck); pos += scnprintf(buf + pos, bufsz - pos, "nrg_auto_corr_silence_diff:\t %u\n", data->nrg_auto_corr_silence_diff); pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", data->num_in_cck_no_fa); pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", data->nrg_th_ofdm); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; ssize_t ret; struct iwl_chain_noise_data *data; data = &priv->chain_noise_data; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", data->active_chains); pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", data->chain_noise_a); pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", data->chain_noise_b); pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", data->chain_noise_c); pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", data->chain_signal_a); pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", data->chain_signal_b); pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", data->chain_signal_c); pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", data->beacon_count); pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->disconn_array[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->delta_gain_code[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", data->radio_write); pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", data->state); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[60]; int pos = 0; const size_t bufsz = sizeof(buf); u32 pwrsave_status; pwrsave_status = iwl_read32(priv->trans, CSR_GP_CNTRL) & CSR_GP_REG_POWER_SAVE_STATUS_MSK; pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); pos += scnprintf(buf + pos, bufsz - pos, "%s\n", (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : "error"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int clear; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &clear) != 1) return -EFAULT; /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); iwl_send_statistics_request(priv, CMD_SYNC, true); mutex_unlock(&priv->mutex); return count; } static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; char buf[128]; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n", priv->event_log.ucode_trace ? "On" : "Off"); pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n", priv->event_log.non_wraps_count); pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n", priv->event_log.wraps_once_count); pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n", priv->event_log.wraps_more_count); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int trace; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &trace) != 1) return -EFAULT; if (trace) { priv->event_log.ucode_trace = true; if (iwl_is_alive(priv)) { /* start collecting data now */ mod_timer(&priv->ucode_trace, jiffies); } } else { priv->event_log.ucode_trace = false; del_timer_sync(&priv->ucode_trace); } return count; } static ssize_t iwl_dbgfs_rxon_flags_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int len = 0; char buf[20]; len = sprintf(buf, "0x%04X\n", le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags)); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int len = 0; char buf[20]; len = sprintf(buf, "0x%04X\n", le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags)); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; char buf[12]; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->missed_beacon_threshold); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int missed; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &missed) != 1) return -EINVAL; if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN || missed > IWL_MISSED_BEACON_THRESHOLD_MAX) priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; else priv->missed_beacon_threshold = missed; return count; } static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; char buf[12]; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "%u\n", priv->plcp_delta_threshold); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int plcp; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &plcp) != 1) return -EINVAL; if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) priv->plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE; else priv->plcp_delta_threshold = plcp; return count; } static ssize_t iwl_dbgfs_rf_reset_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int pos = 0; char buf[300]; const size_t bufsz = sizeof(buf); struct iwl_rf_reset *rf_reset = &priv->rf_reset; pos += scnprintf(buf + pos, bufsz - pos, "RF reset statistics\n"); pos += scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request: %d\n", rf_reset->reset_request_count); pos += scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request success: %d\n", rf_reset->reset_success_count); pos += scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request reject: %d\n", rf_reset->reset_reject_count); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_rf_reset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; int ret; ret = iwl_force_rf_reset(priv, true); return ret ? ret : count; } static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int flush; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &flush) != 1) return -EINVAL; if (iwl_is_rfkill(priv)) return -EFAULT; iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); return count; } static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; int pos = 0; char buf[200]; const size_t bufsz = sizeof(buf); if (!priv->bt_enable_flag) { pos += scnprintf(buf + pos, bufsz - pos, "BT coex disabled\n"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } pos += scnprintf(buf + pos, bufsz - pos, "BT enable flag: 0x%x\n", priv->bt_enable_flag); pos += scnprintf(buf + pos, bufsz - pos, "BT in %s mode\n", priv->bt_full_concurrent ? "full concurrency" : "3-wire"); pos += scnprintf(buf + pos, bufsz - pos, "BT status: %s, " "last traffic notif: %d\n", priv->bt_status ? "On" : "Off", priv->last_bt_traffic_load); pos += scnprintf(buf + pos, bufsz - pos, "ch_announcement: %d, " "kill_ack_mask: %x, kill_cts_mask: %x\n", priv->bt_ch_announce, priv->kill_ack_mask, priv->kill_cts_mask); pos += scnprintf(buf + pos, bufsz - pos, "bluetooth traffic load: "); switch (priv->bt_traffic_load) { case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: pos += scnprintf(buf + pos, bufsz - pos, "Continuous\n"); break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: pos += scnprintf(buf + pos, bufsz - pos, "High\n"); break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: pos += scnprintf(buf + pos, bufsz - pos, "Low\n"); break; case IWL_BT_COEX_TRAFFIC_LOAD_NONE: default: pos += scnprintf(buf + pos, bufsz - pos, "None\n"); break; } return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_protection_mode_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; int pos = 0; char buf[40]; const size_t bufsz = sizeof(buf); if (priv->cfg->ht_params) pos += scnprintf(buf + pos, bufsz - pos, "use %s for aggregation\n", (priv->hw_params.use_rts_for_aggregation) ? "rts/cts" : "cts-to-self"); else pos += scnprintf(buf + pos, bufsz - pos, "N/A"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; int rts; if (!priv->cfg->ht_params) return -EINVAL; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &rts) != 1) return -EINVAL; if (rts) priv->hw_params.use_rts_for_aggregation = true; else priv->hw_params.use_rts_for_aggregation = false; return count; } static int iwl_cmd_echo_test(struct iwl_priv *priv) { int ret; struct iwl_host_cmd cmd = { .id = REPLY_ECHO, .len = { 0 }, .flags = CMD_SYNC, }; ret = iwl_dvm_send_cmd(priv, &cmd); if (ret) IWL_ERR(priv, "echo testing fail: 0X%x\n", ret); else IWL_DEBUG_INFO(priv, "echo testing pass\n"); return ret; } static ssize_t iwl_dbgfs_echo_test_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; iwl_cmd_echo_test(priv); return count; } #ifdef CONFIG_IWLWIFI_DEBUG static ssize_t iwl_dbgfs_log_event_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char *buf; int pos = 0; ssize_t ret = -ENOMEM; ret = pos = iwl_dump_nic_event_log(priv, true, &buf, true); if (buf) { ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); } return ret; } static ssize_t iwl_dbgfs_log_event_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; u32 event_log_flag; char buf[8]; int buf_size; /* check that the interface is up */ if (!iwl_is_ready(priv)) return -EAGAIN; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &event_log_flag) != 1) return -EFAULT; if (event_log_flag == 1) iwl_dump_nic_event_log(priv, true, NULL, false); return count; } #endif static ssize_t iwl_dbgfs_calib_disabled_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[120]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "Sensitivity calibrations %s\n", (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) ? "DISABLED" : "ENABLED"); pos += scnprintf(buf + pos, bufsz - pos, "Chain noise calibrations %s\n", (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) ? "DISABLED" : "ENABLED"); pos += scnprintf(buf + pos, bufsz - pos, "Tx power calibrations %s\n", (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED) ? "DISABLED" : "ENABLED"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_priv *priv = file->private_data; char buf[8]; u32 calib_disabled; int buf_size; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &calib_disabled) != 1) return -EFAULT; priv->calib_disabled = calib_disabled; return count; } DEBUGFS_READ_FILE_OPS(ucode_rx_stats); DEBUGFS_READ_FILE_OPS(ucode_tx_stats); DEBUGFS_READ_FILE_OPS(ucode_general_stats); DEBUGFS_READ_FILE_OPS(sensitivity); DEBUGFS_READ_FILE_OPS(chain_noise); DEBUGFS_READ_FILE_OPS(power_save_status); DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta); DEBUGFS_READ_WRITE_FILE_OPS(rf_reset); DEBUGFS_READ_FILE_OPS(rxon_flags); DEBUGFS_READ_FILE_OPS(rxon_filter_flags); DEBUGFS_WRITE_FILE_OPS(txfifo_flush); DEBUGFS_READ_FILE_OPS(ucode_bt_stats); DEBUGFS_READ_FILE_OPS(bt_traffic); DEBUGFS_READ_WRITE_FILE_OPS(protection_mode); DEBUGFS_READ_FILE_OPS(reply_tx_error); DEBUGFS_WRITE_FILE_OPS(echo_test); #ifdef CONFIG_IWLWIFI_DEBUG DEBUGFS_READ_WRITE_FILE_OPS(log_event); #endif DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled); /* * Create the debugfs files and directories * */ int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir) { struct dentry *dir_data, *dir_rf, *dir_debug; priv->debugfs_dir = dbgfs_dir; dir_data = debugfs_create_dir("data", dbgfs_dir); if (!dir_data) goto err; dir_rf = debugfs_create_dir("rf", dbgfs_dir); if (!dir_rf) goto err; dir_debug = debugfs_create_dir("debug", dbgfs_dir); if (!dir_debug) goto err; DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR); #ifdef CONFIG_IWLWIFI_DEBUG DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR); #endif if (iwl_advanced_bt_coexist(priv)) DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR); /* Calibrations disabled/enabled status*/ DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR); /* * Create a symlink with mac80211. This is not very robust, as it does * not remove the symlink created. The implicit assumption is that * when the opmode exits, mac80211 will also exit, and will remove * this symlink as part of its cleanup. */ if (priv->mac80211_registered) { char buf[100]; struct dentry *mac80211_dir, *dev_dir, *root_dir; dev_dir = dbgfs_dir->d_parent; root_dir = dev_dir->d_parent; mac80211_dir = priv->hw->wiphy->debugfsdir; snprintf(buf, 100, "../../%s/%s", root_dir->d_name.name, dev_dir->d_name.name); if (!debugfs_create_symlink("iwlwifi", mac80211_dir, buf)) goto err; } return 0; err: IWL_ERR(priv, "failed to create the dvm debugfs entries\n"); return -ENOMEM; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/commands.h0000644000175000017500000040433212026211315024701 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ /* * Please use this file (commands.h) only for uCode API definitions. * Please use iwl-xxxx-hw.h for hardware-related definitions. * Please use dev.h for driver implementation definitions. */ #ifndef __iwl_commands_h__ #define __iwl_commands_h__ #include #include enum { REPLY_ALIVE = 0x1, REPLY_ERROR = 0x2, REPLY_ECHO = 0x3, /* test command */ /* RXON and QOS commands */ REPLY_RXON = 0x10, REPLY_RXON_ASSOC = 0x11, REPLY_QOS_PARAM = 0x13, REPLY_RXON_TIMING = 0x14, /* Multi-Station support */ REPLY_ADD_STA = 0x18, REPLY_REMOVE_STA = 0x19, REPLY_REMOVE_ALL_STA = 0x1a, /* not used */ REPLY_TXFIFO_FLUSH = 0x1e, /* Security */ REPLY_WEPKEY = 0x20, /* RX, TX, LEDs */ REPLY_TX = 0x1c, REPLY_LEDS_CMD = 0x48, REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* WiMAX coexistence */ COEX_PRIORITY_TABLE_CMD = 0x5a, COEX_MEDIUM_NOTIFICATION = 0x5b, COEX_EVENT_CMD = 0x5c, /* Calibration */ TEMPERATURE_NOTIFICATION = 0x62, CALIBRATION_CFG_CMD = 0x65, CALIBRATION_RES_NOTIFICATION = 0x66, CALIBRATION_COMPLETE_NOTIFICATION = 0x67, /* 802.11h related */ REPLY_QUIET_CMD = 0x71, /* not used */ REPLY_CHANNEL_SWITCH = 0x72, CHANNEL_SWITCH_NOTIFICATION = 0x73, REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74, SPECTRUM_MEASURE_NOTIFICATION = 0x75, /* Power Management */ POWER_TABLE_CMD = 0x77, PM_SLEEP_NOTIFICATION = 0x7A, PM_DEBUG_STATISTIC_NOTIFIC = 0x7B, /* Scan commands and notifications */ REPLY_SCAN_CMD = 0x80, REPLY_SCAN_ABORT_CMD = 0x81, SCAN_START_NOTIFICATION = 0x82, SCAN_RESULTS_NOTIFICATION = 0x83, SCAN_COMPLETE_NOTIFICATION = 0x84, /* IBSS/AP commands */ BEACON_NOTIFICATION = 0x90, REPLY_TX_BEACON = 0x91, WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */ /* Miscellaneous commands */ REPLY_TX_POWER_DBM_CMD = 0x95, QUIET_NOTIFICATION = 0x96, /* not used */ REPLY_TX_PWR_TABLE_CMD = 0x97, REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */ TX_ANT_CONFIGURATION_CMD = 0x98, MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */ /* Bluetooth device coexistence config command */ REPLY_BT_CONFIG = 0x9b, /* Statistics */ REPLY_STATISTICS_CMD = 0x9c, STATISTICS_NOTIFICATION = 0x9d, /* RF-KILL commands and notifications */ REPLY_CARD_STATE_CMD = 0xa0, CARD_STATE_NOTIFICATION = 0xa1, /* Missed beacons notification */ MISSED_BEACONS_NOTIFICATION = 0xa2, REPLY_CT_KILL_CONFIG_CMD = 0xa4, SENSITIVITY_CMD = 0xa8, REPLY_PHY_CALIBRATION_CMD = 0xb0, REPLY_RX_PHY_CMD = 0xc0, REPLY_RX_MPDU_CMD = 0xc1, REPLY_RX = 0xc3, REPLY_COMPRESSED_BA = 0xc5, /* BT Coex */ REPLY_BT_COEX_PRIO_TABLE = 0xcc, REPLY_BT_COEX_PROT_ENV = 0xcd, REPLY_BT_COEX_PROFILE_NOTIF = 0xce, /* PAN commands */ REPLY_WIPAN_PARAMS = 0xb2, REPLY_WIPAN_RXON = 0xb3, /* use REPLY_RXON structure */ REPLY_WIPAN_RXON_TIMING = 0xb4, /* use REPLY_RXON_TIMING structure */ REPLY_WIPAN_RXON_ASSOC = 0xb6, /* use REPLY_RXON_ASSOC structure */ REPLY_WIPAN_QOS_PARAM = 0xb7, /* use REPLY_QOS_PARAM structure */ REPLY_WIPAN_WEPKEY = 0xb8, /* use REPLY_WEPKEY structure */ REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9, REPLY_WIPAN_NOA_NOTIFICATION = 0xbc, REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd, REPLY_WOWLAN_PATTERNS = 0xe0, REPLY_WOWLAN_WAKEUP_FILTER = 0xe1, REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2, REPLY_WOWLAN_TKIP_PARAMS = 0xe3, REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4, REPLY_WOWLAN_GET_STATUS = 0xe5, REPLY_D3_CONFIG = 0xd3, REPLY_MAX = 0xff }; /* * Minimum number of queues. MAX_NUM is defined in hw specific files. * Set the minimum to accommodate * - 4 standard TX queues * - the command queue * - 4 PAN TX queues * - the PAN multicast queue, and * - the AUX (TX during scan dwell) queue. */ #define IWL_MIN_NUM_QUEUES 11 /* * Command queue depends on iPAN support. */ #define IWL_DEFAULT_CMD_QUEUE_NUM 4 #define IWL_IPAN_CMD_QUEUE_NUM 9 #define IWL_TX_FIFO_BK 0 /* shared */ #define IWL_TX_FIFO_BE 1 #define IWL_TX_FIFO_VI 2 /* shared */ #define IWL_TX_FIFO_VO 3 #define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK #define IWL_TX_FIFO_BE_IPAN 4 #define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI #define IWL_TX_FIFO_VO_IPAN 5 /* re-uses the VO FIFO, uCode will properly flush/schedule */ #define IWL_TX_FIFO_AUX 5 #define IWL_TX_FIFO_UNUSED 255 #define IWLAGN_CMD_FIFO_NUM 7 /* * This queue number is required for proper operation * because the ucode will stop/start the scheduler as * required. */ #define IWL_IPAN_MCAST_QUEUE 8 /****************************************************************************** * (0) * Commonly used structures and definitions: * Command header, rate_n_flags, txpower * *****************************************************************************/ /** * iwlagn rate_n_flags bit fields * * rate_n_flags format is used in following iwlagn commands: * REPLY_RX (response only) * REPLY_RX_MPDU (response only) * REPLY_TX (both command and response) * REPLY_TX_LINK_QUALITY_CMD * * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): * 2-0: 0) 6 Mbps * 1) 12 Mbps * 2) 18 Mbps * 3) 24 Mbps * 4) 36 Mbps * 5) 48 Mbps * 6) 54 Mbps * 7) 60 Mbps * * 4-3: 0) Single stream (SISO) * 1) Dual stream (MIMO) * 2) Triple stream (MIMO) * * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data * * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): * 3-0: 0xD) 6 Mbps * 0xF) 9 Mbps * 0x5) 12 Mbps * 0x7) 18 Mbps * 0x9) 24 Mbps * 0xB) 36 Mbps * 0x1) 48 Mbps * 0x3) 54 Mbps * * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): * 6-0: 10) 1 Mbps * 20) 2 Mbps * 55) 5.5 Mbps * 110) 11 Mbps */ #define RATE_MCS_CODE_MSK 0x7 #define RATE_MCS_SPATIAL_POS 3 #define RATE_MCS_SPATIAL_MSK 0x18 #define RATE_MCS_HT_DUP_POS 5 #define RATE_MCS_HT_DUP_MSK 0x20 /* Both legacy and HT use bits 7:0 as the CCK/OFDM rate or HT MCS */ #define RATE_MCS_RATE_MSK 0xff /* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ #define RATE_MCS_FLAGS_POS 8 #define RATE_MCS_HT_POS 8 #define RATE_MCS_HT_MSK 0x100 /* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ #define RATE_MCS_CCK_POS 9 #define RATE_MCS_CCK_MSK 0x200 /* Bit 10: (1) Use Green Field preamble */ #define RATE_MCS_GF_POS 10 #define RATE_MCS_GF_MSK 0x400 /* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ #define RATE_MCS_HT40_POS 11 #define RATE_MCS_HT40_MSK 0x800 /* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ #define RATE_MCS_DUP_POS 12 #define RATE_MCS_DUP_MSK 0x1000 /* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ #define RATE_MCS_SGI_POS 13 #define RATE_MCS_SGI_MSK 0x2000 /** * rate_n_flags Tx antenna masks * 4965 has 2 transmitters * 5100 has 1 transmitter B * 5150 has 1 transmitter A * 5300 has 3 transmitters * 5350 has 3 transmitters * bit14:16 */ #define RATE_MCS_ANT_POS 14 #define RATE_MCS_ANT_A_MSK 0x04000 #define RATE_MCS_ANT_B_MSK 0x08000 #define RATE_MCS_ANT_C_MSK 0x10000 #define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) #define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) #define RATE_ANT_NUM 3 #define POWER_TABLE_NUM_ENTRIES 33 #define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 #define POWER_TABLE_CCK_ENTRY 32 #define IWL_PWR_NUM_HT_OFDM_ENTRIES 24 #define IWL_PWR_CCK_ENTRIES 2 /** * struct tx_power_dual_stream * * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH * * Same format as iwl_tx_power_dual_stream, but __le32 */ struct tx_power_dual_stream { __le32 dw; } __packed; /** * Command REPLY_TX_POWER_DBM_CMD = 0x98 * struct iwlagn_tx_power_dbm_cmd */ #define IWLAGN_TX_POWER_AUTO 0x7f #define IWLAGN_TX_POWER_NO_CLOSED (0x1 << 6) struct iwlagn_tx_power_dbm_cmd { s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ u8 flags; s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ u8 reserved; } __packed; /** * Command TX_ANT_CONFIGURATION_CMD = 0x98 * This command is used to configure valid Tx antenna. * By default uCode concludes the valid antenna according to the radio flavor. * This command enables the driver to override/modify this conclusion. */ struct iwl_tx_ant_config_cmd { __le32 valid; } __packed; /****************************************************************************** * (0a) * Alive and Error Commands & Responses: * *****************************************************************************/ #define UCODE_VALID_OK cpu_to_le32(0x1) /** * REPLY_ALIVE = 0x1 (response only, not a command) * * uCode issues this "alive" notification once the runtime image is ready * to receive commands from the driver. This is the *second* "alive" * notification that the driver will receive after rebooting uCode; * this "alive" is indicated by subtype field != 9. * * See comments documenting "BSM" (bootstrap state machine). * * This response includes two pointers to structures within the device's * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: * * 1) log_event_table_ptr indicates base of the event log. This traces * a 256-entry history of uCode execution within a circular buffer. * Its header format is: * * __le32 log_size; log capacity (in number of entries) * __le32 type; (1) timestamp with each entry, (0) no timestamp * __le32 wraps; # times uCode has wrapped to top of circular buffer * __le32 write_index; next circular buffer entry that uCode would fill * * The header is followed by the circular buffer of log entries. Entries * with timestamps have the following format: * * __le32 event_id; range 0 - 1500 * __le32 timestamp; low 32 bits of TSF (of network, if associated) * __le32 data; event_id-specific data value * * Entries without timestamps contain only event_id and data. * * * 2) error_event_table_ptr indicates base of the error log. This contains * information about any uCode error that occurs. For agn, the format * of the error log is defined by struct iwl_error_event_table. * * The Linux driver can print both logs to the system log when a uCode error * occurs. */ /* * Note: This structure is read from the device with IO accesses, * and the reading already does the endian conversion. As it is * read with u32-sized accesses, any members with a different size * need to be ordered correctly though! */ struct iwl_error_event_table { u32 valid; /* (nonzero) valid, (0) log is empty */ u32 error_id; /* type of error */ u32 pc; /* program counter */ u32 blink1; /* branch link */ u32 blink2; /* branch link */ u32 ilink1; /* interrupt link */ u32 ilink2; /* interrupt link */ u32 data1; /* error-specific data */ u32 data2; /* error-specific data */ u32 line; /* source code line of error */ u32 bcon_time; /* beacon timer */ u32 tsf_low; /* network timestamp function timer */ u32 tsf_hi; /* network timestamp function timer */ u32 gp1; /* GP1 timer register */ u32 gp2; /* GP2 timer register */ u32 gp3; /* GP3 timer register */ u32 ucode_ver; /* uCode version */ u32 hw_ver; /* HW Silicon version */ u32 brd_ver; /* HW board version */ u32 log_pc; /* log program counter */ u32 frame_ptr; /* frame pointer */ u32 stack_ptr; /* stack pointer */ u32 hcmd; /* last host command header */ u32 isr0; /* isr status register LMPM_NIC_ISR0: * rxtx_flag */ u32 isr1; /* isr status register LMPM_NIC_ISR1: * host_flag */ u32 isr2; /* isr status register LMPM_NIC_ISR2: * enc_flag */ u32 isr3; /* isr status register LMPM_NIC_ISR3: * time_flag */ u32 isr4; /* isr status register LMPM_NIC_ISR4: * wico interrupt */ u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ u32 wait_event; /* wait event() caller address */ u32 l2p_control; /* L2pControlField */ u32 l2p_duration; /* L2pDurationField */ u32 l2p_mhvalid; /* L2pMhValidBits */ u32 l2p_addr_match; /* L2pAddrMatchStat */ u32 lmpm_pmg_sel; /* indicate which clocks are turned on * (LMPM_PMG_SEL) */ u32 u_timestamp; /* indicate when the date and time of the * compilation */ u32 flow_handler; /* FH read/write pointers, RX credit */ } __packed; struct iwl_alive_resp { u8 ucode_minor; u8 ucode_major; __le16 reserved1; u8 sw_rev[8]; u8 ver_type; u8 ver_subtype; /* not "9" for runtime alive */ __le16 reserved2; __le32 log_event_table_ptr; /* SRAM address for event log */ __le32 error_event_table_ptr; /* SRAM address for error log */ __le32 timestamp; __le32 is_valid; } __packed; /* * REPLY_ERROR = 0x2 (response only, not a command) */ struct iwl_error_resp { __le32 error_type; u8 cmd_id; u8 reserved1; __le16 bad_cmd_seq_num; __le32 error_info; __le64 timestamp; } __packed; /****************************************************************************** * (1) * RXON Commands & Responses: * *****************************************************************************/ /* * Rx config defines & structure */ /* rx_config device types */ enum { RXON_DEV_TYPE_AP = 1, RXON_DEV_TYPE_ESS = 3, RXON_DEV_TYPE_IBSS = 4, RXON_DEV_TYPE_SNIFFER = 6, RXON_DEV_TYPE_CP = 7, RXON_DEV_TYPE_2STA = 8, RXON_DEV_TYPE_P2P = 9, }; #define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) #define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) #define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) #define RXON_RX_CHAIN_VALID_POS (1) #define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) #define RXON_RX_CHAIN_FORCE_SEL_POS (4) #define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) #define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) #define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) #define RXON_RX_CHAIN_CNT_POS (10) #define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) #define RXON_RX_CHAIN_MIMO_CNT_POS (12) #define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) #define RXON_RX_CHAIN_MIMO_FORCE_POS (14) /* rx_config flags */ /* band & modulation selection */ #define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) #define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) /* auto detection enable */ #define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) /* TGg protection when tx */ #define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) /* cck short slot & preamble */ #define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) #define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) /* antenna selection */ #define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) #define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) #define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) #define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) /* radar detection enable */ #define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) #define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) /* rx response to host with 8-byte TSF * (according to ON_AIR deassertion) */ #define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) /* HT flags */ #define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) #define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) #define RXON_FLG_HT_OPERATING_MODE_POS (23) #define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) #define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) #define RXON_FLG_CHANNEL_MODE_POS (25) #define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) /* channel mode */ enum { CHANNEL_MODE_LEGACY = 0, CHANNEL_MODE_PURE_40 = 1, CHANNEL_MODE_MIXED = 2, CHANNEL_MODE_RESERVED = 3, }; #define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) #define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) #define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) /* CTS to self (if spec allows) flag */ #define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) /* rx_config filter flags */ /* accept all data frames */ #define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) /* pass control & management to host */ #define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) /* accept multi-cast */ #define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) /* don't decrypt uni-cast frames */ #define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) /* don't decrypt multi-cast frames */ #define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) /* STA is associated */ #define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) /* transfer to host non bssid beacons in associated state */ #define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) /** * REPLY_RXON = 0x10 (command, has simple generic response) * * RXON tunes the radio tuner to a service channel, and sets up a number * of parameters that are used primarily for Rx, but also for Tx operations. * * NOTE: When tuning to a new channel, driver must set the * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent * info within the device, including the station tables, tx retry * rate tables, and txpower tables. Driver must build a new station * table and txpower table before transmitting anything on the RXON * channel. * * NOTE: All RXONs wipe clean the internal txpower table. Driver must * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), * regardless of whether RXON_FILTER_ASSOC_MSK is set. */ struct iwl_rxon_cmd { u8 node_addr[6]; __le16 reserved1; u8 bssid_addr[6]; __le16 reserved2; u8 wlap_bssid_addr[6]; __le16 reserved3; u8 dev_type; u8 air_propagation; __le16 rx_chain; u8 ofdm_basic_rates; u8 cck_basic_rates; __le16 assoc_id; __le32 flags; __le32 filter_flags; __le16 channel; u8 ofdm_ht_single_stream_basic_rates; u8 ofdm_ht_dual_stream_basic_rates; u8 ofdm_ht_triple_stream_basic_rates; u8 reserved5; __le16 acquisition_data; __le16 reserved6; } __packed; /* * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) */ struct iwl_rxon_assoc_cmd { __le32 flags; __le32 filter_flags; u8 ofdm_basic_rates; u8 cck_basic_rates; __le16 reserved1; u8 ofdm_ht_single_stream_basic_rates; u8 ofdm_ht_dual_stream_basic_rates; u8 ofdm_ht_triple_stream_basic_rates; u8 reserved2; __le16 rx_chain_select_flags; __le16 acquisition_data; __le32 reserved3; } __packed; #define IWL_CONN_MAX_LISTEN_INTERVAL 10 #define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ /* * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) */ struct iwl_rxon_time_cmd { __le64 timestamp; __le16 beacon_interval; __le16 atim_window; __le32 beacon_init_val; __le16 listen_interval; u8 dtim_period; u8 delta_cp_bss_tbtts; } __packed; /* * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) */ /** * struct iwl5000_channel_switch_cmd * @band: 0- 5.2GHz, 1- 2.4GHz * @expect_beacon: 0- resume transmits after channel switch * 1- wait for beacon to resume transmits * @channel: new channel number * @rxon_flags: Rx on flags * @rxon_filter_flags: filtering parameters * @switch_time: switch time in extended beacon format * @reserved: reserved bytes */ struct iwl5000_channel_switch_cmd { u8 band; u8 expect_beacon; __le16 channel; __le32 rxon_flags; __le32 rxon_filter_flags; __le32 switch_time; __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; } __packed; /** * struct iwl6000_channel_switch_cmd * @band: 0- 5.2GHz, 1- 2.4GHz * @expect_beacon: 0- resume transmits after channel switch * 1- wait for beacon to resume transmits * @channel: new channel number * @rxon_flags: Rx on flags * @rxon_filter_flags: filtering parameters * @switch_time: switch time in extended beacon format * @reserved: reserved bytes */ struct iwl6000_channel_switch_cmd { u8 band; u8 expect_beacon; __le16 channel; __le32 rxon_flags; __le32 rxon_filter_flags; __le32 switch_time; __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; } __packed; /* * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) */ struct iwl_csa_notification { __le16 band; __le16 channel; __le32 status; /* 0 - OK, 1 - fail */ } __packed; /****************************************************************************** * (2) * Quality-of-Service (QOS) Commands & Responses: * *****************************************************************************/ /** * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd * * @cw_min: Contention window, start value in numbers of slots. * Should be a power-of-2, minus 1. Device's default is 0x0f. * @cw_max: Contention window, max value in numbers of slots. * Should be a power-of-2, minus 1. Device's default is 0x3f. * @aifsn: Number of slots in Arbitration Interframe Space (before * performing random backoff timing prior to Tx). Device default 1. * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. * * Device will automatically increase contention window by (2*CW) + 1 for each * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW * value, to cap the CW value. */ struct iwl_ac_qos { __le16 cw_min; __le16 cw_max; u8 aifsn; u8 reserved1; __le16 edca_txop; } __packed; /* QoS flags defines */ #define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) #define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) #define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) /* Number of Access Categories (AC) (EDCA), queues 0..3 */ #define AC_NUM 4 /* * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) * * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs * 0: Background, 1: Best Effort, 2: Video, 3: Voice. */ struct iwl_qosparam_cmd { __le32 qos_flags; struct iwl_ac_qos ac[AC_NUM]; } __packed; /****************************************************************************** * (3) * Add/Modify Stations Commands & Responses: * *****************************************************************************/ /* * Multi station support */ /* Special, dedicated locations within device's station table */ #define IWL_AP_ID 0 #define IWL_AP_ID_PAN 1 #define IWL_STA_ID 2 #define IWLAGN_PAN_BCAST_ID 14 #define IWLAGN_BROADCAST_ID 15 #define IWLAGN_STATION_COUNT 16 #define IWL_TID_NON_QOS IWL_MAX_TID_COUNT #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) #define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) #define STA_FLG_PAN_STATION cpu_to_le32(1 << 13) #define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) #define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) #define STA_FLG_MAX_AGG_SIZE_POS (19) #define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) #define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) #define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) #define STA_FLG_AGG_MPDU_DENSITY_POS (23) #define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) /* Use in mode field. 1: modify existing entry, 0: add new station entry */ #define STA_CONTROL_MODIFY_MSK 0x01 /* key flags __le16*/ #define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) #define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) #define STA_KEY_FLG_WEP cpu_to_le16(0x0001) #define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) #define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) #define STA_KEY_FLG_KEYID_POS 8 #define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) /* wep key is either from global key (0) or from station info array (1) */ #define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) /* wep key in STA: 5-bytes (0) or 13-bytes (1) */ #define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) #define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) #define STA_KEY_MAX_NUM 8 #define STA_KEY_MAX_NUM_PAN 16 /* must not match WEP_INVALID_OFFSET */ #define IWLAGN_HW_KEY_DEFAULT 0xfe /* Flags indicate whether to modify vs. don't change various station params */ #define STA_MODIFY_KEY_MASK 0x01 #define STA_MODIFY_TID_DISABLE_TX 0x02 #define STA_MODIFY_TX_RATE_MSK 0x04 #define STA_MODIFY_ADDBA_TID_MSK 0x08 #define STA_MODIFY_DELBA_TID_MSK 0x10 #define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 /* Receiver address (actually, Rx station's index into station table), * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ #define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) /* agn */ struct iwl_keyinfo { __le16 key_flags; u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ u8 reserved1; __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ u8 key_offset; u8 reserved2; u8 key[16]; /* 16-byte unicast decryption key */ __le64 tx_secur_seq_cnt; __le64 hw_tkip_mic_rx_key; __le64 hw_tkip_mic_tx_key; } __packed; /** * struct sta_id_modify * @addr[ETH_ALEN]: station's MAC address * @sta_id: index of station in uCode's station table * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change * * Driver selects unused table index when adding new station, * or the index to a pre-existing station entry when modifying that station. * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). * * modify_mask flags select which parameters to modify vs. leave alone. */ struct sta_id_modify { u8 addr[ETH_ALEN]; __le16 reserved1; u8 sta_id; u8 modify_mask; __le16 reserved2; } __packed; /* * REPLY_ADD_STA = 0x18 (command) * * The device contains an internal table of per-station information, * with info on security keys, aggregation parameters, and Tx rates for * initial Tx attempt and any retries (agn devices uses * REPLY_TX_LINK_QUALITY_CMD, * * REPLY_ADD_STA sets up the table entry for one station, either creating * a new entry, or modifying a pre-existing one. * * NOTE: RXON command (without "associated" bit set) wipes the station table * clean. Moving into RF_KILL state does this also. Driver must set up * new station table before transmitting anything on the RXON channel * (except active scans or active measurements; those commands carry * their own txpower/rate setup data). * * When getting started on a new channel, driver must set up the * IWL_BROADCAST_ID entry (last entry in the table). For a client * station in a BSS, once an AP is selected, driver sets up the AP STA * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP * are all that are needed for a BSS client station. If the device is * used as AP, or in an IBSS network, driver must set up station table * entries for all STAs in network, starting with index IWL_STA_ID. */ struct iwl_addsta_cmd { u8 mode; /* 1: modify existing, 0: add new station */ u8 reserved[3]; struct sta_id_modify sta; struct iwl_keyinfo key; __le32 station_flags; /* STA_FLG_* */ __le32 station_flags_msk; /* STA_FLG_* */ /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) * corresponding to bit (e.g. bit 5 controls TID 5). * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ __le16 tid_disable_tx; __le16 legacy_reserved; /* TID for which to add block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ u8 add_immediate_ba_tid; /* TID for which to remove block-ack support. * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ u8 remove_immediate_ba_tid; /* Starting Sequence Number for added block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ __le16 add_immediate_ba_ssn; /* * Number of packets OK to transmit to station even though * it is asleep -- used to synchronise PS-poll and u-APSD * responses while ucode keeps track of STA sleep state. */ __le16 sleep_tx_count; __le16 reserved2; } __packed; #define ADD_STA_SUCCESS_MSK 0x1 #define ADD_STA_NO_ROOM_IN_TABLE 0x2 #define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 #define ADD_STA_MODIFY_NON_EXIST_STA 0x8 /* * REPLY_ADD_STA = 0x18 (response) */ struct iwl_add_sta_resp { u8 status; /* ADD_STA_* */ } __packed; #define REM_STA_SUCCESS_MSK 0x1 /* * REPLY_REM_STA = 0x19 (response) */ struct iwl_rem_sta_resp { u8 status; } __packed; /* * REPLY_REM_STA = 0x19 (command) */ struct iwl_rem_sta_cmd { u8 num_sta; /* number of removed stations */ u8 reserved[3]; u8 addr[ETH_ALEN]; /* MAC addr of the first station */ u8 reserved2[2]; } __packed; /* WiFi queues mask */ #define IWL_SCD_BK_MSK cpu_to_le32(BIT(0)) #define IWL_SCD_BE_MSK cpu_to_le32(BIT(1)) #define IWL_SCD_VI_MSK cpu_to_le32(BIT(2)) #define IWL_SCD_VO_MSK cpu_to_le32(BIT(3)) #define IWL_SCD_MGMT_MSK cpu_to_le32(BIT(3)) /* PAN queues mask */ #define IWL_PAN_SCD_BK_MSK cpu_to_le32(BIT(4)) #define IWL_PAN_SCD_BE_MSK cpu_to_le32(BIT(5)) #define IWL_PAN_SCD_VI_MSK cpu_to_le32(BIT(6)) #define IWL_PAN_SCD_VO_MSK cpu_to_le32(BIT(7)) #define IWL_PAN_SCD_MGMT_MSK cpu_to_le32(BIT(7)) #define IWL_PAN_SCD_MULTICAST_MSK cpu_to_le32(BIT(8)) #define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) #define IWL_DROP_SINGLE 0 #define IWL_DROP_ALL (BIT(IWL_RXON_CTX_BSS) | BIT(IWL_RXON_CTX_PAN)) /* * REPLY_TXFIFO_FLUSH = 0x1e(command and response) * * When using full FIFO flush this command checks the scheduler HW block WR/RD * pointers to check if all the frames were transferred by DMA into the * relevant TX FIFO queue. Only when the DMA is finished and the queue is * empty the command can finish. * This command is used to flush the TXFIFO from transmit commands, it may * operate on single or multiple queues, the command queue can't be flushed by * this command. The command response is returned when all the queue flush * operations are done. Each TX command flushed return response with the FLUSH * status set in the TX response status. When FIFO flush operation is used, * the flush operation ends when both the scheduler DMA done and TXFIFO empty * are set. * * @fifo_control: bit mask for which queues to flush * @flush_control: flush controls * 0: Dump single MSDU * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable. * 2: Dump all FIFO */ struct iwl_txfifo_flush_cmd { __le32 fifo_control; __le16 flush_control; __le16 reserved; } __packed; /* * REPLY_WEP_KEY = 0x20 */ struct iwl_wep_key { u8 key_index; u8 key_offset; u8 reserved1[2]; u8 key_size; u8 reserved2[3]; u8 key[16]; } __packed; struct iwl_wep_cmd { u8 num_keys; u8 global_key_type; u8 flags; u8 reserved; struct iwl_wep_key key[0]; } __packed; #define WEP_KEY_WEP_TYPE 1 #define WEP_KEYS_MAX 4 #define WEP_INVALID_OFFSET 0xff #define WEP_KEY_LEN_64 5 #define WEP_KEY_LEN_128 13 /****************************************************************************** * (4) * Rx Responses: * *****************************************************************************/ #define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) #define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) #define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) #define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) #define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70 #define RX_RES_PHY_FLAGS_ANTENNA_POS 4 #define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7) #define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) #define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) #define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) #define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) #define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) #define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) #define RX_RES_STATUS_STATION_FOUND (1<<6) #define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) #define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) #define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) #define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) #define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) #define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) #define RX_MPDU_RES_STATUS_ICV_OK (0x20) #define RX_MPDU_RES_STATUS_MIC_OK (0x40) #define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) #define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) #define IWLAGN_RX_RES_PHY_CNT 8 #define IWLAGN_RX_RES_AGC_IDX 1 #define IWLAGN_RX_RES_RSSI_AB_IDX 2 #define IWLAGN_RX_RES_RSSI_C_IDX 3 #define IWLAGN_OFDM_AGC_MSK 0xfe00 #define IWLAGN_OFDM_AGC_BIT_POS 9 #define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff #define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00 #define IWLAGN_OFDM_RSSI_A_BIT_POS 0 #define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000 #define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000 #define IWLAGN_OFDM_RSSI_B_BIT_POS 16 #define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff #define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00 #define IWLAGN_OFDM_RSSI_C_BIT_POS 0 struct iwlagn_non_cfg_phy { __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */ } __packed; /* * REPLY_RX = 0xc3 (response only, not a command) * Used only for legacy (non 11n) frames. */ struct iwl_rx_phy_res { u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ u8 stat_id; /* configurable DSP phy data set ID */ u8 reserved1; __le64 timestamp; /* TSF at on air rise */ __le32 beacon_time_stamp; /* beacon at on-air rise */ __le16 phy_flags; /* general phy flags: band, modulation, ... */ __le16 channel; /* channel number */ u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ __le32 rate_n_flags; /* RATE_MCS_* */ __le16 byte_count; /* frame's byte-count */ __le16 frame_time; /* frame's time on the air */ } __packed; struct iwl_rx_mpdu_res_start { __le16 byte_count; __le16 reserved; } __packed; /****************************************************************************** * (5) * Tx Commands & Responses: * * Driver must place each REPLY_TX command into one of the prioritized Tx * queues in host DRAM, shared between driver and device (see comments for * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode * are preparing to transmit, the device pulls the Tx command over the PCI * bus via one of the device's Tx DMA channels, to fill an internal FIFO * from which data will be transmitted. * * uCode handles all timing and protocol related to control frames * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler * handle reception of block-acks; uCode updates the host driver via * REPLY_COMPRESSED_BA. * * uCode handles retrying Tx when an ACK is expected but not received. * This includes trying lower data rates than the one requested in the Tx * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn). * * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. * This command must be executed after every RXON command, before Tx can occur. *****************************************************************************/ /* REPLY_TX Tx flags field */ /* * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it * before this frame. if CTS-to-self required check * RXON_FLG_SELF_CTS_EN status. */ #define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0) /* 1: Expect ACK from receiving station * 0: Don't expect ACK (MAC header's duration field s/b 0) * Set this for unicast frames, but not broadcast/multicast. */ #define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) /* For agn devices: * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). * Tx command's initial_rate_index indicates first rate to try; * uCode walks through table for additional Tx attempts. * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. * This rate will be used for all Tx attempts; it will not be scaled. */ #define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) /* 1: Expect immediate block-ack. * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ #define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) /* Tx antenna selection field; reserved (0) for agn devices. */ #define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) /* 1: Ignore Bluetooth priority for this frame. * 0: Delay Tx until Bluetooth device is done (normal usage). */ #define TX_CMD_FLG_IGNORE_BT cpu_to_le32(1 << 12) /* 1: uCode overrides sequence control field in MAC header. * 0: Driver provides sequence control field in MAC header. * Set this for management frames, non-QOS data frames, non-unicast frames, * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ #define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) /* 1: This frame is non-last MPDU; more fragments are coming. * 0: Last fragment, or not using fragmentation. */ #define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) /* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. * 0: No TSF required in outgoing frame. * Set this for transmitting beacons and probe responses. */ #define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) /* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword * alignment of frame's payload data field. * 0: No pad * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 * field (but not both). Driver must align frame data (i.e. data following * MAC header) to DWORD boundary. */ #define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) /* accelerate aggregation support * 0 - no CCMP encryption; 1 - CCMP encryption */ #define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) /* HCCA-AP - disable duration overwriting. */ #define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) /* * TX command security control */ #define TX_CMD_SEC_WEP 0x01 #define TX_CMD_SEC_CCM 0x02 #define TX_CMD_SEC_TKIP 0x03 #define TX_CMD_SEC_MSK 0x03 #define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_KEY128 0x08 /* * security overhead sizes */ #define WEP_IV_LEN 4 #define WEP_ICV_LEN 4 #define CCMP_MIC_LEN 8 #define TKIP_ICV_LEN 4 /* * REPLY_TX = 0x1c (command) */ /* * 4965 uCode updates these Tx attempt count values in host DRAM. * Used for managing Tx retries when expecting block-acks. * Driver should set these fields to 0. */ struct iwl_dram_scratch { u8 try_cnt; /* Tx attempts */ u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ __le16 reserved; } __packed; struct iwl_tx_cmd { /* * MPDU byte count: * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, * + 8 byte IV for CCM or TKIP (not used for WEP) * + Data payload * + 8-byte MIC (not used for CCM/WEP) * NOTE: Does not include Tx command bytes, post-MAC pad bytes, * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i * Range: 14-2342 bytes. */ __le16 len; /* * MPDU or MSDU byte count for next frame. * Used for fragmentation and bursting, but not 11n aggregation. * Same as "len", but for next frame. Set to 0 if not applicable. */ __le16 next_frame_len; __le32 tx_flags; /* TX_CMD_FLG_* */ /* uCode may modify this field of the Tx command (in host DRAM!). * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ struct iwl_dram_scratch scratch; /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ __le32 rate_n_flags; /* RATE_MCS_* */ /* Index of destination station in uCode's station table */ u8 sta_id; /* Type of security encryption: CCM or TKIP */ u8 sec_ctl; /* TX_CMD_SEC_* */ /* * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for * data frames, this field may be used to selectively reduce initial * rate (via non-0 value) for special frames (e.g. management), while * still supporting rate scaling for all frames. */ u8 initial_rate_index; u8 reserved; u8 key[16]; __le16 next_frame_flags; __le16 reserved2; union { __le32 life_time; __le32 attempt; } stop_time; /* Host DRAM physical address pointer to "scratch" in this command. * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ __le32 dram_lsb_ptr; u8 dram_msb_ptr; u8 rts_retry_limit; /*byte 50 */ u8 data_retry_limit; /*byte 51 */ u8 tid_tspec; union { __le16 pm_frame_timeout; __le16 attempt_duration; } timeout; /* * Duration of EDCA burst Tx Opportunity, in 32-usec units. * Set this if txop time is not specified by HCCA protocol (e.g. by AP). */ __le16 driver_txop; /* * MAC header goes here, followed by 2 bytes padding if MAC header * length is 26 or 30 bytes, followed by payload data */ u8 payload[0]; struct ieee80211_hdr hdr[0]; } __packed; /* * TX command response is sent after *agn* transmission attempts. * * both postpone and abort status are expected behavior from uCode. there is * no special operation required from driver; except for RFKILL_FLUSH, * which required tx flush host command to flush all the tx frames in queues */ enum { TX_STATUS_SUCCESS = 0x01, TX_STATUS_DIRECT_DONE = 0x02, /* postpone TX */ TX_STATUS_POSTPONE_DELAY = 0x40, TX_STATUS_POSTPONE_FEW_BYTES = 0x41, TX_STATUS_POSTPONE_BT_PRIO = 0x42, TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, TX_STATUS_POSTPONE_CALC_TTAK = 0x44, /* abort TX */ TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, TX_STATUS_FAIL_SHORT_LIMIT = 0x82, TX_STATUS_FAIL_LONG_LIMIT = 0x83, TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, TX_STATUS_FAIL_DRAIN_FLOW = 0x85, TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, TX_STATUS_FAIL_DEST_PS = 0x88, TX_STATUS_FAIL_HOST_ABORTED = 0x89, TX_STATUS_FAIL_BT_RETRY = 0x8a, TX_STATUS_FAIL_STA_INVALID = 0x8b, TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, TX_STATUS_FAIL_TID_DISABLE = 0x8d, TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, }; #define TX_PACKET_MODE_REGULAR 0x0000 #define TX_PACKET_MODE_BURST_SEQ 0x0100 #define TX_PACKET_MODE_BURST_FIRST 0x0200 enum { TX_POWER_PA_NOT_ACTIVE = 0x0, }; enum { TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ TX_STATUS_DELAY_MSK = 0x00000040, TX_STATUS_ABORT_MSK = 0x00000080, TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ TX_RESERVED = 0x00780000, /* bits 19:22 */ TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ }; /* ******************************* * TX aggregation status ******************************* */ enum { AGG_TX_STATE_TRANSMITTED = 0x00, AGG_TX_STATE_UNDERRUN_MSK = 0x01, AGG_TX_STATE_BT_PRIO_MSK = 0x02, AGG_TX_STATE_FEW_BYTES_MSK = 0x04, AGG_TX_STATE_ABORT_MSK = 0x08, AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40, AGG_TX_STATE_SCD_QUERY_MSK = 0x80, AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, AGG_TX_STATE_RESPONSE_MSK = 0x1ff, AGG_TX_STATE_DUMP_TX_MSK = 0x200, AGG_TX_STATE_DELAY_TX_MSK = 0x400 }; #define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ #define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ #define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \ AGG_TX_STATE_LAST_SENT_BT_KILL_MSK) /* # tx attempts for first frame in aggregation */ #define AGG_TX_STATE_TRY_CNT_POS 12 #define AGG_TX_STATE_TRY_CNT_MSK 0xf000 /* Command ID and sequence number of Tx command for this frame */ #define AGG_TX_STATE_SEQ_NUM_POS 16 #define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 /* * REPLY_TX = 0x1c (response) * * This response may be in one of two slightly different formats, indicated * by the frame_count field: * * 1) No aggregation (frame_count == 1). This reports Tx results for * a single frame. Multiple attempts, at various bit rates, may have * been made for this frame. * * 2) Aggregation (frame_count > 1). This reports Tx results for * 2 or more frames that used block-acknowledge. All frames were * transmitted at same rate. Rate scaling may have been used if first * frame in this new agg block failed in previous agg block(s). * * Note that, for aggregation, ACK (block-ack) status is not delivered here; * block-ack has not been received by the time the agn device records * this status. * This status relates to reasons the tx might have been blocked or aborted * within the sending station (this agn device), rather than whether it was * received successfully by the destination station. */ struct agg_tx_status { __le16 status; __le16 sequence; } __packed; /* * definitions for initial rate index field * bits [3:0] initial rate index * bits [6:4] rate table color, used for the initial rate * bit-7 invalid rate indication * i.e. rate was not chosen from rate table * or rate table color was changed during frame retries * refer tlc rate info */ #define IWL50_TX_RES_INIT_RATE_INDEX_POS 0 #define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f #define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4 #define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70 #define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80 /* refer to ra_tid */ #define IWLAGN_TX_RES_TID_POS 0 #define IWLAGN_TX_RES_TID_MSK 0x0f #define IWLAGN_TX_RES_RA_POS 4 #define IWLAGN_TX_RES_RA_MSK 0xf0 struct iwlagn_tx_resp { u8 frame_count; /* 1 no aggregation, >1 aggregation */ u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ u8 failure_rts; /* # failures due to unsuccessful RTS */ u8 failure_frame; /* # failures due to no ACK (unused for agg) */ /* For non-agg: Rate at which frame was successful. * For agg: Rate at which all frames were transmitted. */ __le32 rate_n_flags; /* RATE_MCS_* */ /* For non-agg: RTS + CTS + frame tx attempts time + ACK. * For agg: RTS + CTS + aggregation tx time + block-ack time. */ __le16 wireless_media_time; /* uSecs */ u8 pa_status; /* RF power amplifier measurement (not used) */ u8 pa_integ_res_a[3]; u8 pa_integ_res_b[3]; u8 pa_integ_res_C[3]; __le32 tfd_info; __le16 seq_ctl; __le16 byte_cnt; u8 tlc_info; u8 ra_tid; /* tid (0:3), sta_id (4:7) */ __le16 frame_ctrl; /* * For non-agg: frame status TX_STATUS_* * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status * fields follow this one, up to frame_count. * Bit fields: * 11- 0: AGG_TX_STATE_* status code * 15-12: Retry count for 1st frame in aggregation (retries * occur if tx failed for this frame when it was a * member of a previous aggregation block). If rate * scaling is used, retry count indicates the rate * table entry used for all frames in the new agg. * 31-16: Sequence # for this frame's Tx cmd (not SSN!) */ struct agg_tx_status status; /* TX status (in aggregation - * status of 1st frame) */ } __packed; /* * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command) * * Reports Block-Acknowledge from recipient station */ struct iwl_compressed_ba_resp { __le32 sta_addr_lo32; __le16 sta_addr_hi16; __le16 reserved; /* Index of recipient (BA-sending) station in uCode's station table */ u8 sta_id; u8 tid; __le16 seq_ctl; __le64 bitmap; __le16 scd_flow; __le16 scd_ssn; u8 txed; /* number of frames sent */ u8 txed_2_done; /* number of frames acked */ } __packed; /* * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) * */ /*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ #define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) /* # of EDCA prioritized tx fifos */ #define LINK_QUAL_AC_NUM AC_NUM /* # entries in rate scale table to support Tx retries */ #define LINK_QUAL_MAX_RETRY_NUM 16 /* Tx antenna selection values */ #define LINK_QUAL_ANT_A_MSK (1 << 0) #define LINK_QUAL_ANT_B_MSK (1 << 1) #define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) /** * struct iwl_link_qual_general_params * * Used in REPLY_TX_LINK_QUALITY_CMD */ struct iwl_link_qual_general_params { u8 flags; /* No entries at or above this (driver chosen) index contain MIMO */ u8 mimo_delimiter; /* Best single antenna to use for single stream (legacy, SISO). */ u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ /* Best antennas to use for MIMO (unused for 4965, assumes both). */ u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ /* * If driver needs to use different initial rates for different * EDCA QOS access categories (as implemented by tx fifos 0-3), * this table will set that up, by indicating the indexes in the * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. * Otherwise, driver should set all entries to 0. * * Entry usage: * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. */ u8 start_rate_index[LINK_QUAL_AC_NUM]; } __packed; #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ #define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) #define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) #define LINK_QUAL_AGG_DISABLE_START_DEF (3) #define LINK_QUAL_AGG_DISABLE_START_MAX (255) #define LINK_QUAL_AGG_DISABLE_START_MIN (0) #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) /** * struct iwl_link_qual_agg_params * * Used in REPLY_TX_LINK_QUALITY_CMD */ struct iwl_link_qual_agg_params { /* *Maximum number of uSec in aggregation. * default set to 4000 (4 milliseconds) if not configured in .cfg */ __le16 agg_time_limit; /* * Number of Tx retries allowed for a frame, before that frame will * no longer be considered for the start of an aggregation sequence * (scheduler will then try to tx it as single frame). * Driver should set this to 3. */ u8 agg_dis_start_th; /* * Maximum number of frames in aggregation. * 0 = no limit (default). 1 = no aggregation. * Other values = max # frames in aggregation. */ u8 agg_frame_cnt_limit; __le32 reserved; } __packed; /* * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) * * For agn devices * * Each station in the agn device's internal station table has its own table * of 16 * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when * an ACK is not received. This command replaces the entire table for * one station. * * NOTE: Station must already be in agn device's station table. * Use REPLY_ADD_STA. * * The rate scaling procedures described below work well. Of course, other * procedures are possible, and may work better for particular environments. * * * FILLING THE RATE TABLE * * Given a particular initial rate and mode, as determined by the rate * scaling algorithm described below, the Linux driver uses the following * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the * Link Quality command: * * * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: * a) Use this same initial rate for first 3 entries. * b) Find next lower available rate using same mode (SISO or MIMO), * use for next 3 entries. If no lower rate available, switch to * legacy mode (no HT40 channel, no MIMO, no short guard interval). * c) If using MIMO, set command's mimo_delimiter to number of entries * using MIMO (3 or 6). * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, * no MIMO, no short guard interval), at the next lower bit rate * (e.g. if second HT bit rate was 54, try 48 legacy), and follow * legacy procedure for remaining table entries. * * 2) If using legacy initial rate: * a) Use the initial rate for only one entry. * b) For each following entry, reduce the rate to next lower available * rate, until reaching the lowest available rate. * c) When reducing rate, also switch antenna selection. * d) Once lowest available rate is reached, repeat this rate until * rate table is filled (16 entries), switching antenna each entry. * * * ACCUMULATING HISTORY * * The rate scaling algorithm for agn devices, as implemented in Linux driver, * uses two sets of frame Tx success history: One for the current/active * modulation mode, and one for a speculative/search mode that is being * attempted. If the speculative mode turns out to be more effective (i.e. * actual transfer rate is better), then the driver continues to use the * speculative mode as the new current active mode. * * Each history set contains, separately for each possible rate, data for a * sliding window of the 62 most recent tx attempts at that rate. The data * includes a shifting bitmap of success(1)/failure(0), and sums of successful * and attempted frames, from which the driver can additionally calculate a * success ratio (success / attempted) and number of failures * (attempted - success), and control the size of the window (attempted). * The driver uses the bit map to remove successes from the success sum, as * the oldest tx attempts fall out of the window. * * When the agn device makes multiple tx attempts for a given frame, each * attempt might be at a different rate, and have different modulation * characteristics (e.g. antenna, fat channel, short guard interval), as set * up in the rate scaling table in the Link Quality command. The driver must * determine which rate table entry was used for each tx attempt, to determine * which rate-specific history to update, and record only those attempts that * match the modulation characteristics of the history set. * * When using block-ack (aggregation), all frames are transmitted at the same * rate, since there is no per-attempt acknowledgment from the destination * station. The Tx response struct iwl_tx_resp indicates the Tx rate in * rate_n_flags field. After receiving a block-ack, the driver can update * history for the entire block all at once. * * * FINDING BEST STARTING RATE: * * When working with a selected initial modulation mode (see below), the * driver attempts to find a best initial rate. The initial rate is the * first entry in the Link Quality command's rate table. * * 1) Calculate actual throughput (success ratio * expected throughput, see * table below) for current initial rate. Do this only if enough frames * have been attempted to make the value meaningful: at least 6 failed * tx attempts, or at least 8 successes. If not enough, don't try rate * scaling yet. * * 2) Find available rates adjacent to current initial rate. Available means: * a) supported by hardware && * b) supported by association && * c) within any constraints selected by user * * 3) Gather measured throughputs for adjacent rates. These might not have * enough history to calculate a throughput. That's okay, we might try * using one of them anyway! * * 4) Try decreasing rate if, for current rate: * a) success ratio is < 15% || * b) lower adjacent rate has better measured throughput || * c) higher adjacent rate has worse throughput, and lower is unmeasured * * As a sanity check, if decrease was determined above, leave rate * unchanged if: * a) lower rate unavailable * b) success ratio at current rate > 85% (very good) * c) current measured throughput is better than expected throughput * of lower rate (under perfect 100% tx conditions, see table below) * * 5) Try increasing rate if, for current rate: * a) success ratio is < 15% || * b) both adjacent rates' throughputs are unmeasured (try it!) || * b) higher adjacent rate has better measured throughput || * c) lower adjacent rate has worse throughput, and higher is unmeasured * * As a sanity check, if increase was determined above, leave rate * unchanged if: * a) success ratio at current rate < 70%. This is not particularly * good performance; higher rate is sure to have poorer success. * * 6) Re-evaluate the rate after each tx frame. If working with block- * acknowledge, history and statistics may be calculated for the entire * block (including prior history that fits within the history windows), * before re-evaluation. * * FINDING BEST STARTING MODULATION MODE: * * After working with a modulation mode for a "while" (and doing rate scaling), * the driver searches for a new initial mode in an attempt to improve * throughput. The "while" is measured by numbers of attempted frames: * * For legacy mode, search for new mode after: * 480 successful frames, or 160 failed frames * For high-throughput modes (SISO or MIMO), search for new mode after: * 4500 successful frames, or 400 failed frames * * Mode switch possibilities are (3 for each mode): * * For legacy: * Change antenna, try SISO (if HT association), try MIMO (if HT association) * For SISO: * Change antenna, try MIMO, try shortened guard interval (SGI) * For MIMO: * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) * * When trying a new mode, use the same bit rate as the old/current mode when * trying antenna switches and shortened guard interval. When switching to * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate * for which the expected throughput (under perfect conditions) is about the * same or slightly better than the actual measured throughput delivered by * the old/current mode. * * Actual throughput can be estimated by multiplying the expected throughput * by the success ratio (successful / attempted tx frames). Frame size is * not considered in this calculation; it assumes that frame size will average * out to be fairly consistent over several samples. The following are * metric values for expected throughput assuming 100% success ratio. * Only G band has support for CCK rates: * * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 * * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 * * After the new mode has been tried for a short while (minimum of 6 failed * frames or 8 successful frames), compare success ratio and actual throughput * estimate of the new mode with the old. If either is better with the new * mode, continue to use the new mode. * * Continue comparing modes until all 3 possibilities have been tried. * If moving from legacy to HT, try all 3 possibilities from the new HT * mode. After trying all 3, a best mode is found. Continue to use this mode * for the longer "while" described above (e.g. 480 successful frames for * legacy), and then repeat the search process. * */ struct iwl_link_quality_cmd { /* Index of destination/recipient station in uCode's station table */ u8 sta_id; u8 reserved1; __le16 control; /* not used */ struct iwl_link_qual_general_params general_params; struct iwl_link_qual_agg_params agg_params; /* * Rate info; when using rate-scaling, Tx command's initial_rate_index * specifies 1st Tx rate attempted, via index into this table. * agn devices works its way through table when retrying Tx. */ struct { __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */ } rs_table[LINK_QUAL_MAX_RETRY_NUM]; __le32 reserved2; } __packed; /* * BT configuration enable flags: * bit 0 - 1: BT channel announcement enabled * 0: disable * bit 1 - 1: priority of BT device enabled * 0: disable * bit 2 - 1: BT 2 wire support enabled * 0: disable */ #define BT_COEX_DISABLE (0x0) #define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) #define BT_ENABLE_PRIORITY BIT(1) #define BT_ENABLE_2_WIRE BIT(2) #define BT_COEX_DISABLE (0x0) #define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) #define BT_LEAD_TIME_MIN (0x0) #define BT_LEAD_TIME_DEF (0x1E) #define BT_LEAD_TIME_MAX (0xFF) #define BT_MAX_KILL_MIN (0x1) #define BT_MAX_KILL_DEF (0x5) #define BT_MAX_KILL_MAX (0xFF) #define BT_DURATION_LIMIT_DEF 625 #define BT_DURATION_LIMIT_MAX 1250 #define BT_DURATION_LIMIT_MIN 625 #define BT_ON_THRESHOLD_DEF 4 #define BT_ON_THRESHOLD_MAX 1000 #define BT_ON_THRESHOLD_MIN 1 #define BT_FRAG_THRESHOLD_DEF 0 #define BT_FRAG_THRESHOLD_MAX 0 #define BT_FRAG_THRESHOLD_MIN 0 #define BT_AGG_THRESHOLD_DEF 1200 #define BT_AGG_THRESHOLD_MAX 8000 #define BT_AGG_THRESHOLD_MIN 400 /* * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) * * agn devices support hardware handshake with Bluetooth device on * same platform. Bluetooth device alerts wireless device when it will Tx; * wireless device can delay or kill its own Tx to accommodate. */ struct iwl_bt_cmd { u8 flags; u8 lead_time; u8 max_kill; u8 reserved; __le32 kill_ack_mask; __le32 kill_cts_mask; } __packed; #define IWLAGN_BT_FLAG_CHANNEL_INHIBITION BIT(0) #define IWLAGN_BT_FLAG_COEX_MODE_MASK (BIT(3)|BIT(4)|BIT(5)) #define IWLAGN_BT_FLAG_COEX_MODE_SHIFT 3 #define IWLAGN_BT_FLAG_COEX_MODE_DISABLED 0 #define IWLAGN_BT_FLAG_COEX_MODE_LEGACY_2W 1 #define IWLAGN_BT_FLAG_COEX_MODE_3W 2 #define IWLAGN_BT_FLAG_COEX_MODE_4W 3 #define IWLAGN_BT_FLAG_UCODE_DEFAULT BIT(6) /* Disable Sync PSPoll on SCO/eSCO */ #define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7) #define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */ #define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */ #define IWLAGN_BT_PRIO_BOOST_MAX 0xFF #define IWLAGN_BT_PRIO_BOOST_MIN 0x00 #define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0 #define IWLAGN_BT_PRIO_BOOST_DEFAULT32 0xF0F0F0F0 #define IWLAGN_BT_MAX_KILL_DEFAULT 5 #define IWLAGN_BT3_T7_DEFAULT 1 enum iwl_bt_kill_idx { IWL_BT_KILL_DEFAULT = 0, IWL_BT_KILL_OVERRIDE = 1, IWL_BT_KILL_REDUCE = 2, }; #define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000) #define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000) #define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff) #define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0) #define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2 #define IWLAGN_BT3_T2_DEFAULT 0xc #define IWLAGN_BT_VALID_ENABLE_FLAGS cpu_to_le16(BIT(0)) #define IWLAGN_BT_VALID_BOOST cpu_to_le16(BIT(1)) #define IWLAGN_BT_VALID_MAX_KILL cpu_to_le16(BIT(2)) #define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3)) #define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4)) #define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5)) #define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6)) #define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7)) #define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \ IWLAGN_BT_VALID_BOOST | \ IWLAGN_BT_VALID_MAX_KILL | \ IWLAGN_BT_VALID_3W_TIMERS | \ IWLAGN_BT_VALID_KILL_ACK_MASK | \ IWLAGN_BT_VALID_KILL_CTS_MASK | \ IWLAGN_BT_VALID_REDUCED_TX_PWR | \ IWLAGN_BT_VALID_3W_LUT) #define IWLAGN_BT_REDUCED_TX_PWR BIT(0) #define IWLAGN_BT_DECISION_LUT_SIZE 12 struct iwl_basic_bt_cmd { u8 flags; u8 ledtime; /* unused */ u8 max_kill; u8 bt3_timer_t7_value; __le32 kill_ack_mask; __le32 kill_cts_mask; u8 bt3_prio_sample_time; u8 bt3_timer_t2_value; __le16 bt4_reaction_time; /* unused */ __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE]; /* * bit 0: use reduced tx power for control frame * bit 1 - 7: reserved */ u8 reduce_txpower; u8 reserved; __le16 valid; }; struct iwl_bt_cmd_v1 { struct iwl_basic_bt_cmd basic; u8 prio_boost; /* * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask * if configure the following patterns */ u8 tx_prio_boost; /* SW boost of WiFi tx priority */ __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ }; struct iwl_bt_cmd_v2 { struct iwl_basic_bt_cmd basic; __le32 prio_boost; /* * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask * if configure the following patterns */ u8 reserved; u8 tx_prio_boost; /* SW boost of WiFi tx priority */ __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ }; #define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0)) struct iwlagn_bt_sco_cmd { __le32 flags; }; /****************************************************************************** * (6) * Spectrum Management (802.11h) Commands, Responses, Notifications: * *****************************************************************************/ /* * Spectrum Management */ #define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ RXON_FILTER_CTL2HOST_MSK | \ RXON_FILTER_ACCEPT_GRP_MSK | \ RXON_FILTER_DIS_DECRYPT_MSK | \ RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ RXON_FILTER_ASSOC_MSK | \ RXON_FILTER_BCON_AWARE_MSK) struct iwl_measure_channel { __le32 duration; /* measurement duration in extended beacon * format */ u8 channel; /* channel to measure */ u8 type; /* see enum iwl_measure_type */ __le16 reserved; } __packed; /* * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) */ struct iwl_spectrum_cmd { __le16 len; /* number of bytes starting from token */ u8 token; /* token id */ u8 id; /* measurement id -- 0 or 1 */ u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ u8 periodic; /* 1 = periodic */ __le16 path_loss_timeout; __le32 start_time; /* start time in extended beacon format */ __le32 reserved2; __le32 flags; /* rxon flags */ __le32 filter_flags; /* rxon filter flags */ __le16 channel_count; /* minimum 1, maximum 10 */ __le16 reserved3; struct iwl_measure_channel channels[10]; } __packed; /* * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) */ struct iwl_spectrum_resp { u8 token; u8 id; /* id of the prior command replaced, or 0xff */ __le16 status; /* 0 - command will be handled * 1 - cannot handle (conflicts with another * measurement) */ } __packed; enum iwl_measurement_state { IWL_MEASUREMENT_START = 0, IWL_MEASUREMENT_STOP = 1, }; enum iwl_measurement_status { IWL_MEASUREMENT_OK = 0, IWL_MEASUREMENT_CONCURRENT = 1, IWL_MEASUREMENT_CSA_CONFLICT = 2, IWL_MEASUREMENT_TGH_CONFLICT = 3, /* 4-5 reserved */ IWL_MEASUREMENT_STOPPED = 6, IWL_MEASUREMENT_TIMEOUT = 7, IWL_MEASUREMENT_PERIODIC_FAILED = 8, }; #define NUM_ELEMENTS_IN_HISTOGRAM 8 struct iwl_measurement_histogram { __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ } __packed; /* clear channel availability counters */ struct iwl_measurement_cca_counters { __le32 ofdm; __le32 cck; } __packed; enum iwl_measure_type { IWL_MEASURE_BASIC = (1 << 0), IWL_MEASURE_CHANNEL_LOAD = (1 << 1), IWL_MEASURE_HISTOGRAM_RPI = (1 << 2), IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3), IWL_MEASURE_FRAME = (1 << 4), /* bits 5:6 are reserved */ IWL_MEASURE_IDLE = (1 << 7), }; /* * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command) */ struct iwl_spectrum_notification { u8 id; /* measurement id -- 0 or 1 */ u8 token; u8 channel_index; /* index in measurement channel list */ u8 state; /* 0 - start, 1 - stop */ __le32 start_time; /* lower 32-bits of TSF */ u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ u8 channel; u8 type; /* see enum iwl_measurement_type */ u8 reserved1; /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only * valid if applicable for measurement type requested. */ __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ __le32 cca_time; /* channel load time in usecs */ u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - * unidentified */ u8 reserved2[3]; struct iwl_measurement_histogram histogram; __le32 stop_time; /* lower 32-bits of TSF */ __le32 status; /* see iwl_measurement_status */ } __packed; /****************************************************************************** * (7) * Power Management Commands, Responses, Notifications: * *****************************************************************************/ /** * struct iwl_powertable_cmd - Power Table Command * @flags: See below: * * POWER_TABLE_CMD = 0x77 (command, has simple generic response) * * PM allow: * bit 0 - '0' Driver not allow power management * '1' Driver allow PM (use rest of parameters) * * uCode send sleep notifications: * bit 1 - '0' Don't send sleep notification * '1' send sleep notification (SEND_PM_NOTIFICATION) * * Sleep over DTIM * bit 2 - '0' PM have to walk up every DTIM * '1' PM could sleep over DTIM till listen Interval. * * PCI power managed * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) * '1' !(PCI_CFG_LINK_CTRL & 0x1) * * Fast PD * bit 4 - '1' Put radio to sleep when receiving frame for others * * Force sleep Modes * bit 31/30- '00' use both mac/xtal sleeps * '01' force Mac sleep * '10' force xtal sleep * '11' Illegal set * * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then * ucode assume sleep over DTIM is allowed and we don't need to wake up * for every DTIM. */ #define IWL_POWER_VEC_SIZE 5 #define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) #define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0)) #define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1)) #define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) #define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) #define IWL_POWER_FAST_PD cpu_to_le16(BIT(4)) #define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5)) #define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6)) #define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7)) #define IWL_POWER_BT_SCO_ENA cpu_to_le16(BIT(8)) #define IWL_POWER_ADVANCE_PM_ENA_MSK cpu_to_le16(BIT(9)) struct iwl_powertable_cmd { __le16 flags; u8 keep_alive_seconds; u8 debug_flags; __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IWL_POWER_VEC_SIZE]; __le32 keep_alive_beacons; } __packed; /* * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) * all devices identical. */ struct iwl_sleep_notification { u8 pm_sleep_mode; u8 pm_wakeup_src; __le16 reserved; __le32 sleep_time; __le32 tsf_low; __le32 bcon_timer; } __packed; /* Sleep states. all devices identical. */ enum { IWL_PM_NO_SLEEP = 0, IWL_PM_SLP_MAC = 1, IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, IWL_PM_SLP_FULL_MAC_CARD_STATE = 3, IWL_PM_SLP_PHY = 4, IWL_PM_SLP_REPENT = 5, IWL_PM_WAKEUP_BY_TIMER = 6, IWL_PM_WAKEUP_BY_DRIVER = 7, IWL_PM_WAKEUP_BY_RFKILL = 8, /* 3 reserved */ IWL_PM_NUM_OF_MODES = 12, }; /* * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response) */ #define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */ #define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */ #define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */ struct iwl_card_state_cmd { __le32 status; /* CARD_STATE_CMD_* request new power state */ } __packed; /* * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) */ struct iwl_card_state_notif { __le32 flags; } __packed; #define HW_CARD_DISABLED 0x01 #define SW_CARD_DISABLED 0x02 #define CT_CARD_DISABLED 0x04 #define RXON_CARD_DISABLED 0x10 struct iwl_ct_kill_config { __le32 reserved; __le32 critical_temperature_M; __le32 critical_temperature_R; } __packed; /* 1000, and 6x00 */ struct iwl_ct_kill_throttling_config { __le32 critical_temperature_exit; __le32 reserved; __le32 critical_temperature_enter; } __packed; /****************************************************************************** * (8) * Scan Commands, Responses, Notifications: * *****************************************************************************/ #define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) #define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) /** * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table * * One for each channel in the scan list. * Each channel can independently select: * 1) SSID for directed active scans * 2) Txpower setting (for rate specified within Tx command) * 3) How long to stay on-channel (behavior may be modified by quiet_time, * quiet_plcp_th, good_CRC_th) * * To avoid uCode errors, make sure the following are true (see comments * under struct iwl_scan_cmd about max_out_time and quiet_time): * 1) If using passive_dwell (i.e. passive_dwell != 0): * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) * 2) quiet_time <= active_dwell * 3) If restricting off-channel time (i.e. max_out_time !=0): * passive_dwell < max_out_time * active_dwell < max_out_time */ struct iwl_scan_channel { /* * type is defined as: * 0:0 1 = active, 0 = passive * 1:20 SSID direct bit map; if a bit is set, then corresponding * SSID IE is transmitted in probe request. * 21:31 reserved */ __le32 type; __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */ u8 tx_gain; /* gain for analog radio */ u8 dsp_atten; /* gain for DSP */ __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ } __packed; /* set number of direct probes __le32 type */ #define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) /** * struct iwl_ssid_ie - directed scan network information element * * Up to 20 of these may appear in REPLY_SCAN_CMD, * selected by "type" bit field in struct iwl_scan_channel; * each channel may select different ssids from among the 20 entries. * SSID IEs get transmitted in reverse order of entry. */ struct iwl_ssid_ie { u8 id; u8 len; u8 ssid[32]; } __packed; #define PROBE_OPTION_MAX 20 #define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) #define IWL_GOOD_CRC_TH_DISABLED 0 #define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) #define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) #define IWL_MAX_CMD_SIZE 4096 /* * REPLY_SCAN_CMD = 0x80 (command) * * The hardware scan command is very powerful; the driver can set it up to * maintain (relatively) normal network traffic while doing a scan in the * background. The max_out_time and suspend_time control the ratio of how * long the device stays on an associated network channel ("service channel") * vs. how long it's away from the service channel, i.e. tuned to other channels * for scanning. * * max_out_time is the max time off-channel (in usec), and suspend_time * is how long (in "extended beacon" format) that the scan is "suspended" * after returning to the service channel. That is, suspend_time is the * time that we stay on the service channel, doing normal work, between * scan segments. The driver may set these parameters differently to support * scanning when associated vs. not associated, and light vs. heavy traffic * loads when associated. * * After receiving this command, the device's scan engine does the following; * * 1) Sends SCAN_START notification to driver * 2) Checks to see if it has time to do scan for one channel * 3) Sends NULL packet, with power-save (PS) bit set to 1, * to tell AP that we're going off-channel * 4) Tunes to first channel in scan list, does active or passive scan * 5) Sends SCAN_RESULT notification to driver * 6) Checks to see if it has time to do scan on *next* channel in list * 7) Repeats 4-6 until it no longer has time to scan the next channel * before max_out_time expires * 8) Returns to service channel * 9) Sends NULL packet with PS=0 to tell AP that we're back * 10) Stays on service channel until suspend_time expires * 11) Repeats entire process 2-10 until list is complete * 12) Sends SCAN_COMPLETE notification * * For fast, efficient scans, the scan command also has support for staying on * a channel for just a short time, if doing active scanning and getting no * responses to the transmitted probe request. This time is controlled by * quiet_time, and the number of received packets below which a channel is * considered "quiet" is controlled by quiet_plcp_threshold. * * For active scanning on channels that have regulatory restrictions against * blindly transmitting, the scan can listen before transmitting, to make sure * that there is already legitimate activity on the channel. If enough * packets are cleanly received on the channel (controlled by good_CRC_th, * typical value 1), the scan engine starts transmitting probe requests. * * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. * * To avoid uCode errors, see timing restrictions described under * struct iwl_scan_channel. */ enum iwl_scan_flags { /* BIT(0) currently unused */ IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1), /* bits 2-7 reserved */ }; struct iwl_scan_cmd { __le16 len; u8 scan_flags; /* scan flags: see enum iwl_scan_flags */ u8 channel_count; /* # channels in channel list */ __le16 quiet_time; /* dwell only this # millisecs on quiet channel * (only for active scan) */ __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ __le16 good_CRC_th; /* passive -> active promotion threshold */ __le16 rx_chain; /* RXON_RX_CHAIN_* */ __le32 max_out_time; /* max usec to be away from associated (service) * channel */ __le32 suspend_time; /* pause scan this long (in "extended beacon * format") when returning to service chnl: */ __le32 flags; /* RXON_FLG_* */ __le32 filter_flags; /* RXON_FILTER_* */ /* For active scans (set to all-0s for passive scans). * Does not include payload. Must specify Tx rate; no rate scaling. */ struct iwl_tx_cmd tx_cmd; /* For directed active scans (set to all-0s otherwise) */ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; /* * Probe request frame, followed by channel list. * * Size of probe request frame is specified by byte count in tx_cmd. * Channel list follows immediately after probe request frame. * Number of channels in list is specified by channel_count. * Each channel in list is of type: * * struct iwl_scan_channel channels[0]; * * NOTE: Only one band of channels can be scanned per pass. You * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION) * before requesting another scan. */ u8 data[0]; } __packed; /* Can abort will notify by complete notification with abort status. */ #define CAN_ABORT_STATUS cpu_to_le32(0x1) /* complete notification statuses */ #define ABORT_STATUS 0x2 /* * REPLY_SCAN_CMD = 0x80 (response) */ struct iwl_scanreq_notification { __le32 status; /* 1: okay, 2: cannot fulfill request */ } __packed; /* * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) */ struct iwl_scanstart_notification { __le32 tsf_low; __le32 tsf_high; __le32 beacon_timer; u8 channel; u8 band; u8 reserved[2]; __le32 status; } __packed; #define SCAN_OWNER_STATUS 0x1 #define MEASURE_OWNER_STATUS 0x2 #define IWL_PROBE_STATUS_OK 0 #define IWL_PROBE_STATUS_TX_FAILED BIT(0) /* error statuses combined with TX_FAILED */ #define IWL_PROBE_STATUS_FAIL_TTL BIT(1) #define IWL_PROBE_STATUS_FAIL_BT BIT(2) #define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */ /* * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command) */ struct iwl_scanresults_notification { u8 channel; u8 band; u8 probe_status; u8 num_probe_not_sent; /* not enough time to send */ __le32 tsf_low; __le32 tsf_high; __le32 statistics[NUMBER_OF_STATISTICS]; } __packed; /* * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command) */ struct iwl_scancomplete_notification { u8 scanned_channels; u8 status; u8 bt_status; /* BT On/Off status */ u8 last_channel; __le32 tsf_low; __le32 tsf_high; } __packed; /****************************************************************************** * (9) * IBSS/AP Commands and Notifications: * *****************************************************************************/ enum iwl_ibss_manager { IWL_NOT_IBSS_MANAGER = 0, IWL_IBSS_MANAGER = 1, }; /* * BEACON_NOTIFICATION = 0x90 (notification only, not a command) */ struct iwlagn_beacon_notif { struct iwlagn_tx_resp beacon_notify_hdr; __le32 low_tsf; __le32 high_tsf; __le32 ibss_mgr_status; } __packed; /* * REPLY_TX_BEACON = 0x91 (command, has simple generic response) */ struct iwl_tx_beacon_cmd { struct iwl_tx_cmd tx; __le16 tim_idx; u8 tim_size; u8 reserved1; struct ieee80211_hdr frame[0]; /* beacon frame */ } __packed; /****************************************************************************** * (10) * Statistics Commands and Notifications: * *****************************************************************************/ #define IWL_TEMP_CONVERT 260 #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 /* Used for passing to driver number of successes and failures per rate */ struct rate_histogram { union { __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; } success; union { __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; } failed; } __packed; /* statistics command response */ struct statistics_dbg { __le32 burst_check; __le32 burst_count; __le32 wait_for_silence_timeout_cnt; __le32 reserved[3]; } __packed; struct statistics_rx_phy { __le32 ina_cnt; __le32 fina_cnt; __le32 plcp_err; __le32 crc32_err; __le32 overrun_err; __le32 early_overrun_err; __le32 crc32_good; __le32 false_alarm_cnt; __le32 fina_sync_err_cnt; __le32 sfd_timeout; __le32 fina_timeout; __le32 unresponded_rts; __le32 rxe_frame_limit_overrun; __le32 sent_ack_cnt; __le32 sent_cts_cnt; __le32 sent_ba_rsp_cnt; __le32 dsp_self_kill; __le32 mh_format_err; __le32 re_acq_main_rssi_sum; __le32 reserved3; } __packed; struct statistics_rx_ht_phy { __le32 plcp_err; __le32 overrun_err; __le32 early_overrun_err; __le32 crc32_good; __le32 crc32_err; __le32 mh_format_err; __le32 agg_crc32_good; __le32 agg_mpdu_cnt; __le32 agg_cnt; __le32 unsupport_mcs; } __packed; #define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) struct statistics_rx_non_phy { __le32 bogus_cts; /* CTS received when not expecting CTS */ __le32 bogus_ack; /* ACK received when not expecting ACK */ __le32 non_bssid_frames; /* number of frames with BSSID that * doesn't belong to the STA BSSID */ __le32 filtered_frames; /* count frames that were dumped in the * filtering process */ __le32 non_channel_beacons; /* beacons with our bss id but not on * our serving channel */ __le32 channel_beacons; /* beacons with our bss id and in our * serving channel */ __le32 num_missed_bcon; /* number of missed beacons */ __le32 adc_rx_saturation_time; /* count in 0.8us units the time the * ADC was in saturation */ __le32 ina_detection_search_time;/* total time (in 0.8us) searched * for INA */ __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ __le32 interference_data_flag; /* flag for interference data * availability. 1 when data is * available. */ __le32 channel_load; /* counts RX Enable time in uSec */ __le32 dsp_false_alarms; /* DSP false alarm (both OFDM * and CCK) counter */ __le32 beacon_rssi_a; __le32 beacon_rssi_b; __le32 beacon_rssi_c; __le32 beacon_energy_a; __le32 beacon_energy_b; __le32 beacon_energy_c; } __packed; struct statistics_rx_non_phy_bt { struct statistics_rx_non_phy common; /* additional stats for bt */ __le32 num_bt_kills; __le32 reserved[2]; } __packed; struct statistics_rx { struct statistics_rx_phy ofdm; struct statistics_rx_phy cck; struct statistics_rx_non_phy general; struct statistics_rx_ht_phy ofdm_ht; } __packed; struct statistics_rx_bt { struct statistics_rx_phy ofdm; struct statistics_rx_phy cck; struct statistics_rx_non_phy_bt general; struct statistics_rx_ht_phy ofdm_ht; } __packed; /** * struct statistics_tx_power - current tx power * * @ant_a: current tx power on chain a in 1/2 dB step * @ant_b: current tx power on chain b in 1/2 dB step * @ant_c: current tx power on chain c in 1/2 dB step */ struct statistics_tx_power { u8 ant_a; u8 ant_b; u8 ant_c; u8 reserved; } __packed; struct statistics_tx_non_phy_agg { __le32 ba_timeout; __le32 ba_reschedule_frames; __le32 scd_query_agg_frame_cnt; __le32 scd_query_no_agg; __le32 scd_query_agg; __le32 scd_query_mismatch; __le32 frame_not_ready; __le32 underrun; __le32 bt_prio_kill; __le32 rx_ba_rsp_cnt; } __packed; struct statistics_tx { __le32 preamble_cnt; __le32 rx_detected_cnt; __le32 bt_prio_defer_cnt; __le32 bt_prio_kill_cnt; __le32 few_bytes_cnt; __le32 cts_timeout; __le32 ack_timeout; __le32 expected_ack_cnt; __le32 actual_ack_cnt; __le32 dump_msdu_cnt; __le32 burst_abort_next_frame_mismatch_cnt; __le32 burst_abort_missing_next_frame_cnt; __le32 cts_timeout_collision; __le32 ack_or_ba_timeout_collision; struct statistics_tx_non_phy_agg agg; /* * "tx_power" are optional parameters provided by uCode, * 6000 series is the only device provide the information, * Those are reserved fields for all the other devices */ struct statistics_tx_power tx_power; __le32 reserved1; } __packed; struct statistics_div { __le32 tx_on_a; __le32 tx_on_b; __le32 exec_time; __le32 probe_time; __le32 reserved1; __le32 reserved2; } __packed; struct statistics_general_common { __le32 temperature; /* radio temperature */ __le32 temperature_m; /* radio voltage */ struct statistics_dbg dbg; __le32 sleep_time; __le32 slots_out; __le32 slots_idle; __le32 ttl_timestamp; struct statistics_div div; __le32 rx_enable_counter; /* * num_of_sos_states: * count the number of times we have to re-tune * in order to get out of bad PHY status */ __le32 num_of_sos_states; } __packed; struct statistics_bt_activity { /* Tx statistics */ __le32 hi_priority_tx_req_cnt; __le32 hi_priority_tx_denied_cnt; __le32 lo_priority_tx_req_cnt; __le32 lo_priority_tx_denied_cnt; /* Rx statistics */ __le32 hi_priority_rx_req_cnt; __le32 hi_priority_rx_denied_cnt; __le32 lo_priority_rx_req_cnt; __le32 lo_priority_rx_denied_cnt; } __packed; struct statistics_general { struct statistics_general_common common; __le32 reserved2; __le32 reserved3; } __packed; struct statistics_general_bt { struct statistics_general_common common; struct statistics_bt_activity activity; __le32 reserved2; __le32 reserved3; } __packed; #define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) #define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) #define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) /* * REPLY_STATISTICS_CMD = 0x9c, * all devices identical. * * This command triggers an immediate response containing uCode statistics. * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below. * * If the CLEAR_STATS configuration flag is set, uCode will clear its * internal copy of the statistics (counters) after issuing the response. * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below). * * If the DISABLE_NOTIF configuration flag is set, uCode will not issue * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself. */ #define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ #define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */ struct iwl_statistics_cmd { __le32 configuration_flags; /* IWL_STATS_CONF_* */ } __packed; /* * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) * * By default, uCode issues this notification after receiving a beacon * while associated. To disable this behavior, set DISABLE_NOTIF flag in the * REPLY_STATISTICS_CMD 0x9c, above. * * Statistics counters continue to increment beacon after beacon, but are * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD * 0x9c with CLEAR_STATS bit set (see above). * * uCode also issues this notification during scans. uCode clears statistics * appropriately so that each notification contains statistics for only the * one channel that has just been scanned. */ #define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) #define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) struct iwl_notif_statistics { __le32 flag; struct statistics_rx rx; struct statistics_tx tx; struct statistics_general general; } __packed; struct iwl_bt_notif_statistics { __le32 flag; struct statistics_rx_bt rx; struct statistics_tx tx; struct statistics_general_bt general; } __packed; /* * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command) * * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed * in regardless of how many missed beacons, which mean when driver receive the * notification, inside the command, it can find all the beacons information * which include number of total missed beacons, number of consecutive missed * beacons, number of beacons received and number of beacons expected to * receive. * * If uCode detected consecutive_missed_beacons > 5, it will reset the radio * in order to bring the radio/PHY back to working state; which has no relation * to when driver will perform sensitivity calibration. * * Driver should set it own missed_beacon_threshold to decide when to perform * sensitivity calibration based on number of consecutive missed beacons in * order to improve overall performance, especially in noisy environment. * */ #define IWL_MISSED_BEACON_THRESHOLD_MIN (1) #define IWL_MISSED_BEACON_THRESHOLD_DEF (5) #define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF struct iwl_missed_beacon_notif { __le32 consecutive_missed_beacons; __le32 total_missed_becons; __le32 num_expected_beacons; __le32 num_recvd_beacons; } __packed; /****************************************************************************** * (11) * Rx Calibration Commands: * * With the uCode used for open source drivers, most Tx calibration (except * for Tx Power) and most Rx calibration is done by uCode during the * "initialize" phase of uCode boot. Driver must calibrate only: * * 1) Tx power (depends on temperature), described elsewhere * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) * 3) Receiver sensitivity (to optimize signal detection) * *****************************************************************************/ /** * SENSITIVITY_CMD = 0xa8 (command, has simple generic response) * * This command sets up the Rx signal detector for a sensitivity level that * is high enough to lock onto all signals within the associated network, * but low enough to ignore signals that are below a certain threshold, so as * not to have too many "false alarms". False alarms are signals that the * Rx DSP tries to lock onto, but then discards after determining that they * are noise. * * The optimum number of false alarms is between 5 and 50 per 200 TUs * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. * time listening, not transmitting). Driver must adjust sensitivity so that * the ratio of actual false alarms to actual Rx time falls within this range. * * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each * received beacon. These provide information to the driver to analyze the * sensitivity. Don't analyze statistics that come in from scanning, or any * other non-associated-network source. Pertinent statistics include: * * From "general" statistics (struct statistics_rx_non_phy): * * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) * Measure of energy of desired signal. Used for establishing a level * below which the device does not detect signals. * * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) * Measure of background noise in silent period after beacon. * * channel_load * uSecs of actual Rx time during beacon period (varies according to * how much time was spent transmitting). * * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: * * false_alarm_cnt * Signal locks abandoned early (before phy-level header). * * plcp_err * Signal locks abandoned late (during phy-level header). * * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from * beacon to beacon, i.e. each value is an accumulation of all errors * before and including the latest beacon. Values will wrap around to 0 * after counting up to 2^32 - 1. Driver must differentiate vs. * previous beacon's values to determine # false alarms in the current * beacon period. * * Total number of false alarms = false_alarms + plcp_errs * * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd * (notice that the start points for OFDM are at or close to settings for * maximum sensitivity): * * START / MIN / MAX * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 * * If actual rate of OFDM false alarms (+ plcp_errors) is too high * (greater than 50 for each 204.8 msecs listening), reduce sensitivity * by *adding* 1 to all 4 of the table entries above, up to the max for * each entry. Conversely, if false alarm rate is too low (less than 5 * for each 204.8 msecs listening), *subtract* 1 from each entry to * increase sensitivity. * * For CCK sensitivity, keep track of the following: * * 1). 20-beacon history of maximum background noise, indicated by * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the * 3 receivers. For any given beacon, the "silence reference" is * the maximum of last 60 samples (20 beacons * 3 receivers). * * 2). 10-beacon history of strongest signal level, as indicated * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, * i.e. the strength of the signal through the best receiver at the * moment. These measurements are "upside down", with lower values * for stronger signals, so max energy will be *minimum* value. * * Then for any given beacon, the driver must determine the *weakest* * of the strongest signals; this is the minimum level that needs to be * successfully detected, when using the best receiver at the moment. * "Max cck energy" is the maximum (higher value means lower energy!) * of the last 10 minima. Once this is determined, driver must add * a little margin by adding "6" to it. * * 3). Number of consecutive beacon periods with too few false alarms. * Reset this to 0 at the first beacon period that falls within the * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). * * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd * (notice that the start points for CCK are at maximum sensitivity): * * START / MIN / MAX * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 * * If actual rate of CCK false alarms (+ plcp_errors) is too high * (greater than 50 for each 204.8 msecs listening), method for reducing * sensitivity is: * * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, * up to max 400. * * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, * sensitivity has been reduced a significant amount; bring it up to * a moderate 161. Otherwise, *add* 3, up to max 200. * * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, * sensitivity has been reduced only a moderate or small amount; * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, * down to min 0. Otherwise (if gain has been significantly reduced), * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. * * b) Save a snapshot of the "silence reference". * * If actual rate of CCK false alarms (+ plcp_errors) is too low * (less than 5 for each 204.8 msecs listening), method for increasing * sensitivity is used only if: * * 1a) Previous beacon did not have too many false alarms * 1b) AND difference between previous "silence reference" and current * "silence reference" (prev - current) is 2 or more, * OR 2) 100 or more consecutive beacon periods have had rate of * less than 5 false alarms per 204.8 milliseconds rx time. * * Method for increasing sensitivity: * * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, * down to min 125. * * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, * down to min 200. * * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. * * If actual rate of CCK false alarms (+ plcp_errors) is within good range * (between 5 and 50 for each 204.8 msecs listening): * * 1) Save a snapshot of the silence reference. * * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), * give some extra margin to energy threshold by *subtracting* 8 * from value in HD_MIN_ENERGY_CCK_DET_INDEX. * * For all cases (too few, too many, good range), make sure that the CCK * detection threshold (energy) is below the energy level for robust * detection over the past 10 beacon periods, the "Max cck energy". * Lower values mean higher energy; this means making sure that the value * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". * */ /* * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd) */ #define HD_TABLE_SIZE (11) /* number of entries */ #define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ #define HD_MIN_ENERGY_OFDM_DET_INDEX (1) #define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2) #define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3) #define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4) #define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5) #define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6) #define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7) #define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8) #define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) #define HD_OFDM_ENERGY_TH_IN_INDEX (10) /* * Additional table entries in enhance SENSITIVITY_CMD */ #define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11) #define HD_INA_NON_SQUARE_DET_CCK_INDEX (12) #define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13) #define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14) #define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15) #define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16) #define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17) #define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18) #define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19) #define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20) #define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21) #define HD_RESERVED (22) /* number of entries for enhanced tbl */ #define ENHANCE_HD_TABLE_SIZE (23) /* number of additional entries for enhanced tbl */ #define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE) #define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0) #define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0) #define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0) #define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668) #define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) #define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486) #define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37) #define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853) #define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) #define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476) #define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99) #define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1) #define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1) #define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1) #define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600) #define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40) #define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486) #define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45) #define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853) #define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60) #define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476) #define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99) /* Control field in struct iwl_sensitivity_cmd */ #define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0) #define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1) /** * struct iwl_sensitivity_cmd * @control: (1) updates working table, (0) updates default table * @table: energy threshold values, use HD_* as index into table * * Always use "1" in "control" to update uCode's working table and DSP. */ struct iwl_sensitivity_cmd { __le16 control; /* always use "1" */ __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ } __packed; /* * */ struct iwl_enhance_sensitivity_cmd { __le16 control; /* always use "1" */ __le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */ } __packed; /** * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) * * This command sets the relative gains of agn device's 3 radio receiver chains. * * After the first association, driver should accumulate signal and noise * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 * beacons from the associated network (don't collect statistics that come * in from scanning, or any other non-network source). * * DISCONNECTED ANTENNA: * * Driver should determine which antennas are actually connected, by comparing * average beacon signal levels for the 3 Rx chains. Accumulate (add) the * following values over 20 beacons, one accumulator for each of the chains * a/b/c, from struct statistics_rx_non_phy: * * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) * * Find the strongest signal from among a/b/c. Compare the other two to the * strongest. If any signal is more than 15 dB (times 20, unless you * divide the accumulated values by 20) below the strongest, the driver * considers that antenna to be disconnected, and should not try to use that * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, * driver should declare the stronger one as connected, and attempt to use it * (A and B are the only 2 Tx chains!). * * * RX BALANCE: * * Driver should balance the 3 receivers (but just the ones that are connected * to antennas, see above) for gain, by comparing the average signal levels * detected during the silence after each beacon (background noise). * Accumulate (add) the following values over 20 beacons, one accumulator for * each of the chains a/b/c, from struct statistics_rx_non_phy: * * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) * * Find the weakest background noise level from among a/b/c. This Rx chain * will be the reference, with 0 gain adjustment. Attenuate other channels by * finding noise difference: * * (accum_noise[i] - accum_noise[reference]) / 30 * * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the * driver should limit the difference results to a range of 0-3 (0-4.5 dB), * and set bit 2 to indicate "reduce gain". The value for the reference * (weakest) chain should be "0". * * diff_gain_[abc] bit fields: * 2: (1) reduce gain, (0) increase gain * 1-0: amount of gain, units of 1.5 dB */ /* Phy calibration command for series */ enum { IWL_PHY_CALIBRATE_DC_CMD = 8, IWL_PHY_CALIBRATE_LO_CMD = 9, IWL_PHY_CALIBRATE_TX_IQ_CMD = 11, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16, IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD = 18, }; /* This enum defines the bitmap of various calibrations to enable in both * init ucode and runtime ucode through CALIBRATION_CFG_CMD. */ enum iwl_ucode_calib_cfg { IWL_CALIB_CFG_RX_BB_IDX = BIT(0), IWL_CALIB_CFG_DC_IDX = BIT(1), IWL_CALIB_CFG_LO_IDX = BIT(2), IWL_CALIB_CFG_TX_IQ_IDX = BIT(3), IWL_CALIB_CFG_RX_IQ_IDX = BIT(4), IWL_CALIB_CFG_NOISE_IDX = BIT(5), IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6), IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7), IWL_CALIB_CFG_PAPD_IDX = BIT(8), IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9), IWL_CALIB_CFG_TX_PWR_IDX = BIT(10), }; #define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ IWL_CALIB_CFG_DC_IDX | \ IWL_CALIB_CFG_LO_IDX | \ IWL_CALIB_CFG_TX_IQ_IDX | \ IWL_CALIB_CFG_RX_IQ_IDX | \ IWL_CALIB_CFG_CRYSTAL_IDX) #define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ IWL_CALIB_CFG_DC_IDX | \ IWL_CALIB_CFG_LO_IDX | \ IWL_CALIB_CFG_TX_IQ_IDX | \ IWL_CALIB_CFG_RX_IQ_IDX | \ IWL_CALIB_CFG_TEMPERATURE_IDX | \ IWL_CALIB_CFG_PAPD_IDX | \ IWL_CALIB_CFG_TX_PWR_IDX | \ IWL_CALIB_CFG_CRYSTAL_IDX) #define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0)) struct iwl_calib_cfg_elmnt_s { __le32 is_enable; __le32 start; __le32 send_res; __le32 apply_res; __le32 reserved; } __packed; struct iwl_calib_cfg_status_s { struct iwl_calib_cfg_elmnt_s once; struct iwl_calib_cfg_elmnt_s perd; __le32 flags; } __packed; struct iwl_calib_cfg_cmd { struct iwl_calib_cfg_status_s ucd_calib_cfg; struct iwl_calib_cfg_status_s drv_calib_cfg; __le32 reserved1; } __packed; struct iwl_calib_hdr { u8 op_code; u8 first_group; u8 groups_num; u8 data_valid; } __packed; struct iwl_calib_cmd { struct iwl_calib_hdr hdr; u8 data[0]; } __packed; struct iwl_calib_xtal_freq_cmd { struct iwl_calib_hdr hdr; u8 cap_pin1; u8 cap_pin2; u8 pad[2]; } __packed; #define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700) struct iwl_calib_temperature_offset_cmd { struct iwl_calib_hdr hdr; __le16 radio_sensor_offset; __le16 reserved; } __packed; struct iwl_calib_temperature_offset_v2_cmd { struct iwl_calib_hdr hdr; __le16 radio_sensor_offset_high; __le16 radio_sensor_offset_low; __le16 burntVoltageRef; __le16 reserved; } __packed; /* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ struct iwl_calib_chain_noise_reset_cmd { struct iwl_calib_hdr hdr; u8 data[0]; }; /* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */ struct iwl_calib_chain_noise_gain_cmd { struct iwl_calib_hdr hdr; u8 delta_gain_1; u8 delta_gain_2; u8 pad[2]; } __packed; /****************************************************************************** * (12) * Miscellaneous Commands: * *****************************************************************************/ /* * LEDs Command & Response * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) * * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), * this command turns it on or off, or sets up a periodic blinking cycle. */ struct iwl_led_cmd { __le32 interval; /* "interval" in uSec */ u8 id; /* 1: Activity, 2: Link, 3: Tech */ u8 off; /* # intervals off while blinking; * "0", with >0 "on" value, turns LED on */ u8 on; /* # intervals on while blinking; * "0", regardless of "off", turns LED off */ u8 reserved; } __packed; /* * station priority table entries * also used as potential "events" value for both * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD */ /* * COEX events entry flag masks * RP - Requested Priority * WP - Win Medium Priority: priority assigned when the contention has been won */ #define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1) #define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2) #define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4) #define COEX_CU_UNASSOC_IDLE_RP 4 #define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4 #define COEX_CU_UNASSOC_AUTO_SCAN_RP 4 #define COEX_CU_CALIBRATION_RP 4 #define COEX_CU_PERIODIC_CALIBRATION_RP 4 #define COEX_CU_CONNECTION_ESTAB_RP 4 #define COEX_CU_ASSOCIATED_IDLE_RP 4 #define COEX_CU_ASSOC_MANUAL_SCAN_RP 4 #define COEX_CU_ASSOC_AUTO_SCAN_RP 4 #define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4 #define COEX_CU_RF_ON_RP 6 #define COEX_CU_RF_OFF_RP 4 #define COEX_CU_STAND_ALONE_DEBUG_RP 6 #define COEX_CU_IPAN_ASSOC_LEVEL_RP 4 #define COEX_CU_RSRVD1_RP 4 #define COEX_CU_RSRVD2_RP 4 #define COEX_CU_UNASSOC_IDLE_WP 3 #define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3 #define COEX_CU_UNASSOC_AUTO_SCAN_WP 3 #define COEX_CU_CALIBRATION_WP 3 #define COEX_CU_PERIODIC_CALIBRATION_WP 3 #define COEX_CU_CONNECTION_ESTAB_WP 3 #define COEX_CU_ASSOCIATED_IDLE_WP 3 #define COEX_CU_ASSOC_MANUAL_SCAN_WP 3 #define COEX_CU_ASSOC_AUTO_SCAN_WP 3 #define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3 #define COEX_CU_RF_ON_WP 3 #define COEX_CU_RF_OFF_WP 3 #define COEX_CU_STAND_ALONE_DEBUG_WP 6 #define COEX_CU_IPAN_ASSOC_LEVEL_WP 3 #define COEX_CU_RSRVD1_WP 3 #define COEX_CU_RSRVD2_WP 3 #define COEX_UNASSOC_IDLE_FLAGS 0 #define COEX_UNASSOC_MANUAL_SCAN_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) #define COEX_UNASSOC_AUTO_SCAN_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) #define COEX_CALIBRATION_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) #define COEX_PERIODIC_CALIBRATION_FLAGS 0 /* * COEX_CONNECTION_ESTAB: * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */ #define COEX_CONNECTION_ESTAB_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) #define COEX_ASSOCIATED_IDLE_FLAGS 0 #define COEX_ASSOC_MANUAL_SCAN_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) #define COEX_ASSOC_AUTO_SCAN_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) #define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0 #define COEX_RF_ON_FLAGS 0 #define COEX_RF_OFF_FLAGS 0 #define COEX_STAND_ALONE_DEBUG_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) #define COEX_IPAN_ASSOC_LEVEL_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) #define COEX_RSRVD1_FLAGS 0 #define COEX_RSRVD2_FLAGS 0 /* * COEX_CU_RF_ON is the event wrapping all radio ownership. * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */ #define COEX_CU_RF_ON_FLAGS \ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) enum { /* un-association part */ COEX_UNASSOC_IDLE = 0, COEX_UNASSOC_MANUAL_SCAN = 1, COEX_UNASSOC_AUTO_SCAN = 2, /* calibration */ COEX_CALIBRATION = 3, COEX_PERIODIC_CALIBRATION = 4, /* connection */ COEX_CONNECTION_ESTAB = 5, /* association part */ COEX_ASSOCIATED_IDLE = 6, COEX_ASSOC_MANUAL_SCAN = 7, COEX_ASSOC_AUTO_SCAN = 8, COEX_ASSOC_ACTIVE_LEVEL = 9, /* RF ON/OFF */ COEX_RF_ON = 10, COEX_RF_OFF = 11, COEX_STAND_ALONE_DEBUG = 12, /* IPAN */ COEX_IPAN_ASSOC_LEVEL = 13, /* reserved */ COEX_RSRVD1 = 14, COEX_RSRVD2 = 15, COEX_NUM_OF_EVENTS = 16 }; /* * Coexistence WIFI/WIMAX Command * COEX_PRIORITY_TABLE_CMD = 0x5a * */ struct iwl_wimax_coex_event_entry { u8 request_prio; u8 win_medium_prio; u8 reserved; u8 flags; } __packed; /* COEX flag masks */ /* Station table is valid */ #define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1) /* UnMask wake up src at unassociated sleep */ #define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4) /* UnMask wake up src at associated sleep */ #define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8) /* Enable CoEx feature. */ #define COEX_FLAGS_COEX_ENABLE_MSK (0x80) struct iwl_wimax_coex_cmd { u8 flags; u8 reserved[3]; struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS]; } __packed; /* * Coexistence MEDIUM NOTIFICATION * COEX_MEDIUM_NOTIFICATION = 0x5b * * notification from uCode to host to indicate medium changes * */ /* * status field * bit 0 - 2: medium status * bit 3: medium change indication * bit 4 - 31: reserved */ /* status option values, (0 - 2 bits) */ #define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */ #define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */ #define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */ #define COEX_MEDIUM_MSK (0x7) /* send notification status (1 bit) */ #define COEX_MEDIUM_CHANGED (0x8) #define COEX_MEDIUM_CHANGED_MSK (0x8) #define COEX_MEDIUM_SHIFT (3) struct iwl_coex_medium_notification { __le32 status; __le32 events; } __packed; /* * Coexistence EVENT Command * COEX_EVENT_CMD = 0x5c * * send from host to uCode for coex event request. */ /* flags options */ #define COEX_EVENT_REQUEST_MSK (0x1) struct iwl_coex_event_cmd { u8 flags; u8 event; __le16 reserved; } __packed; struct iwl_coex_event_resp { __le32 status; } __packed; /****************************************************************************** * Bluetooth Coexistence commands * *****************************************************************************/ /* * BT Status notification * REPLY_BT_COEX_PROFILE_NOTIF = 0xce */ enum iwl_bt_coex_profile_traffic_load { IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, /* * There are no more even though below is a u8, the * indication from the BT device only has two bits. */ }; #define BT_SESSION_ACTIVITY_1_UART_MSG 0x1 #define BT_SESSION_ACTIVITY_2_UART_MSG 0x2 /* BT UART message - Share Part (BT -> WiFi) */ #define BT_UART_MSG_FRAME1MSGTYPE_POS (0) #define BT_UART_MSG_FRAME1MSGTYPE_MSK \ (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS) #define BT_UART_MSG_FRAME1SSN_POS (3) #define BT_UART_MSG_FRAME1SSN_MSK \ (0x3 << BT_UART_MSG_FRAME1SSN_POS) #define BT_UART_MSG_FRAME1UPDATEREQ_POS (5) #define BT_UART_MSG_FRAME1UPDATEREQ_MSK \ (0x1 << BT_UART_MSG_FRAME1UPDATEREQ_POS) #define BT_UART_MSG_FRAME1RESERVED_POS (6) #define BT_UART_MSG_FRAME1RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME1RESERVED_POS) #define BT_UART_MSG_FRAME2OPENCONNECTIONS_POS (0) #define BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK \ (0x3 << BT_UART_MSG_FRAME2OPENCONNECTIONS_POS) #define BT_UART_MSG_FRAME2TRAFFICLOAD_POS (2) #define BT_UART_MSG_FRAME2TRAFFICLOAD_MSK \ (0x3 << BT_UART_MSG_FRAME2TRAFFICLOAD_POS) #define BT_UART_MSG_FRAME2CHLSEQN_POS (4) #define BT_UART_MSG_FRAME2CHLSEQN_MSK \ (0x1 << BT_UART_MSG_FRAME2CHLSEQN_POS) #define BT_UART_MSG_FRAME2INBAND_POS (5) #define BT_UART_MSG_FRAME2INBAND_MSK \ (0x1 << BT_UART_MSG_FRAME2INBAND_POS) #define BT_UART_MSG_FRAME2RESERVED_POS (6) #define BT_UART_MSG_FRAME2RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME2RESERVED_POS) #define BT_UART_MSG_FRAME3SCOESCO_POS (0) #define BT_UART_MSG_FRAME3SCOESCO_MSK \ (0x1 << BT_UART_MSG_FRAME3SCOESCO_POS) #define BT_UART_MSG_FRAME3SNIFF_POS (1) #define BT_UART_MSG_FRAME3SNIFF_MSK \ (0x1 << BT_UART_MSG_FRAME3SNIFF_POS) #define BT_UART_MSG_FRAME3A2DP_POS (2) #define BT_UART_MSG_FRAME3A2DP_MSK \ (0x1 << BT_UART_MSG_FRAME3A2DP_POS) #define BT_UART_MSG_FRAME3ACL_POS (3) #define BT_UART_MSG_FRAME3ACL_MSK \ (0x1 << BT_UART_MSG_FRAME3ACL_POS) #define BT_UART_MSG_FRAME3MASTER_POS (4) #define BT_UART_MSG_FRAME3MASTER_MSK \ (0x1 << BT_UART_MSG_FRAME3MASTER_POS) #define BT_UART_MSG_FRAME3OBEX_POS (5) #define BT_UART_MSG_FRAME3OBEX_MSK \ (0x1 << BT_UART_MSG_FRAME3OBEX_POS) #define BT_UART_MSG_FRAME3RESERVED_POS (6) #define BT_UART_MSG_FRAME3RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME3RESERVED_POS) #define BT_UART_MSG_FRAME4IDLEDURATION_POS (0) #define BT_UART_MSG_FRAME4IDLEDURATION_MSK \ (0x3F << BT_UART_MSG_FRAME4IDLEDURATION_POS) #define BT_UART_MSG_FRAME4RESERVED_POS (6) #define BT_UART_MSG_FRAME4RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME4RESERVED_POS) #define BT_UART_MSG_FRAME5TXACTIVITY_POS (0) #define BT_UART_MSG_FRAME5TXACTIVITY_MSK \ (0x3 << BT_UART_MSG_FRAME5TXACTIVITY_POS) #define BT_UART_MSG_FRAME5RXACTIVITY_POS (2) #define BT_UART_MSG_FRAME5RXACTIVITY_MSK \ (0x3 << BT_UART_MSG_FRAME5RXACTIVITY_POS) #define BT_UART_MSG_FRAME5ESCORETRANSMIT_POS (4) #define BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK \ (0x3 << BT_UART_MSG_FRAME5ESCORETRANSMIT_POS) #define BT_UART_MSG_FRAME5RESERVED_POS (6) #define BT_UART_MSG_FRAME5RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME5RESERVED_POS) #define BT_UART_MSG_FRAME6SNIFFINTERVAL_POS (0) #define BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK \ (0x1F << BT_UART_MSG_FRAME6SNIFFINTERVAL_POS) #define BT_UART_MSG_FRAME6DISCOVERABLE_POS (5) #define BT_UART_MSG_FRAME6DISCOVERABLE_MSK \ (0x1 << BT_UART_MSG_FRAME6DISCOVERABLE_POS) #define BT_UART_MSG_FRAME6RESERVED_POS (6) #define BT_UART_MSG_FRAME6RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME6RESERVED_POS) #define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0) #define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \ (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS) #define BT_UART_MSG_FRAME7PAGE_POS (3) #define BT_UART_MSG_FRAME7PAGE_MSK \ (0x1 << BT_UART_MSG_FRAME7PAGE_POS) #define BT_UART_MSG_FRAME7INQUIRY_POS (4) #define BT_UART_MSG_FRAME7INQUIRY_MSK \ (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS) #define BT_UART_MSG_FRAME7CONNECTABLE_POS (5) #define BT_UART_MSG_FRAME7CONNECTABLE_MSK \ (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS) #define BT_UART_MSG_FRAME7RESERVED_POS (6) #define BT_UART_MSG_FRAME7RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME7RESERVED_POS) /* BT Session Activity 2 UART message (BT -> WiFi) */ #define BT_UART_MSG_2_FRAME1RESERVED1_POS (5) #define BT_UART_MSG_2_FRAME1RESERVED1_MSK \ (0x1< * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_calib_h__ #define __iwl_calib_h__ #include "dev.h" #include "commands.h" void iwl_chain_noise_calibration(struct iwl_priv *priv); void iwl_sensitivity_calibration(struct iwl_priv *priv); void iwl_init_sensitivity(struct iwl_priv *priv); void iwl_reset_run_time_calib(struct iwl_priv *priv); #endif /* __iwl_calib_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/calib.c0000644000175000017500000011042412026211315024141 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include #include #include "iwl-trans.h" #include "dev.h" #include "calib.h" #include "agn.h" /***************************************************************************** * INIT calibrations framework *****************************************************************************/ /* Opaque calibration results */ struct iwl_calib_result { struct list_head list; size_t cmd_len; struct iwl_calib_hdr hdr; /* data follows */ }; struct statistics_general_data { u32 beacon_silence_rssi_a; u32 beacon_silence_rssi_b; u32 beacon_silence_rssi_c; u32 beacon_energy_a; u32 beacon_energy_b; u32 beacon_energy_c; }; int iwl_send_calib_results(struct iwl_priv *priv) { struct iwl_host_cmd hcmd = { .id = REPLY_PHY_CALIBRATION_CMD, .flags = CMD_SYNC, }; struct iwl_calib_result *res; list_for_each_entry(res, &priv->calib_results, list) { int ret; hcmd.len[0] = res->cmd_len; hcmd.data[0] = &res->hdr; hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; ret = iwl_dvm_send_cmd(priv, &hcmd); if (ret) { IWL_ERR(priv, "Error %d on calib cmd %d\n", ret, res->hdr.op_code); return ret; } } return 0; } int iwl_calib_set(struct iwl_priv *priv, const struct iwl_calib_hdr *cmd, int len) { struct iwl_calib_result *res, *tmp; res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr), GFP_ATOMIC); if (!res) return -ENOMEM; memcpy(&res->hdr, cmd, len); res->cmd_len = len; list_for_each_entry(tmp, &priv->calib_results, list) { if (tmp->hdr.op_code == res->hdr.op_code) { list_replace(&tmp->list, &res->list); kfree(tmp); return 0; } } /* wasn't in list already */ list_add_tail(&res->list, &priv->calib_results); return 0; } void iwl_calib_free_results(struct iwl_priv *priv) { struct iwl_calib_result *res, *tmp; list_for_each_entry_safe(res, tmp, &priv->calib_results, list) { list_del(&res->list); kfree(res); } } /***************************************************************************** * RUNTIME calibrations framework *****************************************************************************/ /* "false alarms" are signals that our DSP tries to lock onto, * but then determines that they are either noise, or transmissions * from a distant wireless network (also "noise", really) that get * "stepped on" by stronger transmissions within our own network. * This algorithm attempts to set a sensitivity level that is high * enough to receive all of our own network traffic, but not so * high that our DSP gets too busy trying to lock onto non-network * activity/noise. */ static int iwl_sens_energy_cck(struct iwl_priv *priv, u32 norm_fa, u32 rx_enable_time, struct statistics_general_data *rx_info) { u32 max_nrg_cck = 0; int i = 0; u8 max_silence_rssi = 0; u32 silence_ref = 0; u8 silence_rssi_a = 0; u8 silence_rssi_b = 0; u8 silence_rssi_c = 0; u32 val; /* "false_alarms" values below are cross-multiplications to assess the * numbers of false alarms within the measured period of actual Rx * (Rx is off when we're txing), vs the min/max expected false alarms * (some should be expected if rx is sensitive enough) in a * hypothetical listening period of 200 time units (TU), 204.8 msec: * * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time * * */ u32 false_alarms = norm_fa * 200 * 1024; u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; struct iwl_sensitivity_data *data = NULL; const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; data = &(priv->sensitivity_data); data->nrg_auto_corr_silence_diff = 0; /* Find max silence rssi among all 3 receivers. * This is background noise, which may include transmissions from other * networks, measured during silence before our network's beacon */ silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8); silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8); silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8); val = max(silence_rssi_b, silence_rssi_c); max_silence_rssi = max(silence_rssi_a, (u8) val); /* Store silence rssi in 20-beacon history table */ data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; data->nrg_silence_idx++; if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) data->nrg_silence_idx = 0; /* Find max silence rssi across 20 beacon history */ for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { val = data->nrg_silence_rssi[i]; silence_ref = max(silence_ref, val); } IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a, silence_rssi_b, silence_rssi_c, silence_ref); /* Find max rx energy (min value!) among all 3 receivers, * measured during beacon frame. * Save it in 10-beacon history table. */ i = data->nrg_energy_idx; val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); data->nrg_value[i] = min(rx_info->beacon_energy_a, val); data->nrg_energy_idx++; if (data->nrg_energy_idx >= 10) data->nrg_energy_idx = 0; /* Find min rx energy (max value) across 10 beacon history. * This is the minimum signal level that we want to receive well. * Add backoff (margin so we don't miss slightly lower energy frames). * This establishes an upper bound (min value) for energy threshold. */ max_nrg_cck = data->nrg_value[0]; for (i = 1; i < 10; i++) max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); max_nrg_cck += 6; IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", rx_info->beacon_energy_a, rx_info->beacon_energy_b, rx_info->beacon_energy_c, max_nrg_cck - 6); /* Count number of consecutive beacons with fewer-than-desired * false alarms. */ if (false_alarms < min_false_alarms) data->num_in_cck_no_fa++; else data->num_in_cck_no_fa = 0; IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", data->num_in_cck_no_fa); /* If we got too many false alarms this time, reduce sensitivity */ if ((false_alarms > max_false_alarms) && (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", false_alarms, max_false_alarms); IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); data->nrg_curr_state = IWL_FA_TOO_MANY; /* Store for "fewer than desired" on later beacon */ data->nrg_silence_ref = silence_ref; /* increase energy threshold (reduce nrg value) * to decrease sensitivity */ data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; /* Else if we got fewer than desired, increase sensitivity */ } else if (false_alarms < min_false_alarms) { data->nrg_curr_state = IWL_FA_TOO_FEW; /* Compare silence level with silence level for most recent * healthy number or too many false alarms */ data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - (s32)silence_ref; IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n", false_alarms, min_false_alarms, data->nrg_auto_corr_silence_diff); /* Increase value to increase sensitivity, but only if: * 1a) previous beacon did *not* have *too many* false alarms * 1b) AND there's a significant difference in Rx levels * from a previous beacon with too many, or healthy # FAs * OR 2) We've seen a lot of beacons (100) with too few * false alarms */ if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); /* Increase nrg value to increase sensitivity */ val = data->nrg_th_cck + NRG_STEP_CCK; data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); } else { IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n"); } /* Else we got a healthy number of false alarms, keep status quo */ } else { IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); data->nrg_curr_state = IWL_FA_GOOD_RANGE; /* Store for use in "fewer than desired" with later beacon */ data->nrg_silence_ref = silence_ref; /* If previous beacon had too many false alarms, * give it some extra margin by reducing sensitivity again * (but don't go below measured energy of desired Rx) */ if (IWL_FA_TOO_MANY == data->nrg_prev_state) { IWL_DEBUG_CALIB(priv, "... increasing margin\n"); if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) data->nrg_th_cck -= NRG_MARGIN; else data->nrg_th_cck = max_nrg_cck; } } /* Make sure the energy threshold does not go above the measured * energy of the desired Rx signals (reduced by backoff margin), * or else we might start missing Rx frames. * Lower value is higher energy, so we use max()! */ data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); data->nrg_prev_state = data->nrg_curr_state; /* Auto-correlation CCK algorithm */ if (false_alarms > min_false_alarms) { /* increase auto_corr values to decrease sensitivity * so the DSP won't be disturbed by the noise */ if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; else { val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; data->auto_corr_cck = min((u32)ranges->auto_corr_max_cck, val); } val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; data->auto_corr_cck_mrc = min((u32)ranges->auto_corr_max_cck_mrc, val); } else if ((false_alarms < min_false_alarms) && ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { /* Decrease auto_corr values to increase sensitivity */ val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; data->auto_corr_cck = max((u32)ranges->auto_corr_min_cck, val); val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; data->auto_corr_cck_mrc = max((u32)ranges->auto_corr_min_cck_mrc, val); } return 0; } static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv, u32 norm_fa, u32 rx_enable_time) { u32 val; u32 false_alarms = norm_fa * 200 * 1024; u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; struct iwl_sensitivity_data *data = NULL; const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; data = &(priv->sensitivity_data); /* If we got too many false alarms this time, reduce sensitivity */ if (false_alarms > max_false_alarms) { IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", false_alarms, max_false_alarms); val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm = min((u32)ranges->auto_corr_max_ofdm, val); val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc = min((u32)ranges->auto_corr_max_ofdm_mrc, val); val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_x1 = min((u32)ranges->auto_corr_max_ofdm_x1, val); val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc_x1 = min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); } /* Else if we got fewer than desired, increase sensitivity */ else if (false_alarms < min_false_alarms) { IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", false_alarms, min_false_alarms); val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm = max((u32)ranges->auto_corr_min_ofdm, val); val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc = max((u32)ranges->auto_corr_min_ofdm_mrc, val); val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_x1 = max((u32)ranges->auto_corr_min_ofdm_x1, val); val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc_x1 = max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); } else { IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", min_false_alarms, false_alarms, max_false_alarms); } return 0; } static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, struct iwl_sensitivity_data *data, __le16 *tbl) { tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = cpu_to_le16((u16)data->auto_corr_ofdm); tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = cpu_to_le16((u16)data->auto_corr_ofdm_mrc); tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = cpu_to_le16((u16)data->auto_corr_ofdm_x1); tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = cpu_to_le16((u16)data->auto_corr_cck); tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = cpu_to_le16((u16)data->auto_corr_cck_mrc); tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = cpu_to_le16((u16)data->nrg_th_cck); tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = cpu_to_le16((u16)data->nrg_th_ofdm); tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = cpu_to_le16(data->barker_corr_th_min); tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = cpu_to_le16(data->barker_corr_th_min_mrc); tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = cpu_to_le16(data->nrg_th_cca); IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, data->nrg_th_ofdm); IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck, data->auto_corr_cck_mrc, data->nrg_th_cck); } /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ static int iwl_sensitivity_write(struct iwl_priv *priv) { struct iwl_sensitivity_cmd cmd; struct iwl_sensitivity_data *data = NULL; struct iwl_host_cmd cmd_out = { .id = SENSITIVITY_CMD, .len = { sizeof(struct iwl_sensitivity_cmd), }, .flags = CMD_ASYNC, .data = { &cmd, }, }; data = &(priv->sensitivity_data); memset(&cmd, 0, sizeof(cmd)); iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); /* Update uCode's "work" table, and copy it to DSP */ cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; /* Don't send command to uCode if nothing has changed */ if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), sizeof(u16)*HD_TABLE_SIZE)) { IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); return 0; } /* Copy table for comparison next time */ memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), sizeof(u16)*HD_TABLE_SIZE); return iwl_dvm_send_cmd(priv, &cmd_out); } /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) { struct iwl_enhance_sensitivity_cmd cmd; struct iwl_sensitivity_data *data = NULL; struct iwl_host_cmd cmd_out = { .id = SENSITIVITY_CMD, .len = { sizeof(struct iwl_enhance_sensitivity_cmd), }, .flags = CMD_ASYNC, .data = { &cmd, }, }; data = &(priv->sensitivity_data); memset(&cmd, 0, sizeof(cmd)); iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); if (priv->cfg->base_params->hd_v2) { cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = HD_INA_NON_SQUARE_DET_CCK_DATA_V2; cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2; } else { cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = HD_INA_NON_SQUARE_DET_OFDM_DATA_V1; cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = HD_INA_NON_SQUARE_DET_CCK_DATA_V1; cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1; cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1; cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1; } /* Update uCode's "work" table, and copy it to DSP */ cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; /* Don't send command to uCode if nothing has changed */ if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]), sizeof(u16)*HD_TABLE_SIZE) && !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX], &(priv->enhance_sensitivity_tbl[0]), sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) { IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); return 0; } /* Copy table for comparison next time */ memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]), sizeof(u16)*HD_TABLE_SIZE); memcpy(&(priv->enhance_sensitivity_tbl[0]), &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]), sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES); return iwl_dvm_send_cmd(priv, &cmd_out); } void iwl_init_sensitivity(struct iwl_priv *priv) { int ret = 0; int i; struct iwl_sensitivity_data *data = NULL; const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) return; IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n"); /* Clear driver's sensitivity algo data */ data = &(priv->sensitivity_data); if (ranges == NULL) return; memset(data, 0, sizeof(struct iwl_sensitivity_data)); data->num_in_cck_no_fa = 0; data->nrg_curr_state = IWL_FA_TOO_MANY; data->nrg_prev_state = IWL_FA_TOO_MANY; data->nrg_silence_ref = 0; data->nrg_silence_idx = 0; data->nrg_energy_idx = 0; for (i = 0; i < 10; i++) data->nrg_value[i] = 0; for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) data->nrg_silence_rssi[i] = 0; data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; data->nrg_th_cck = ranges->nrg_th_cck; data->nrg_th_ofdm = ranges->nrg_th_ofdm; data->barker_corr_th_min = ranges->barker_corr_th_min; data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; data->nrg_th_cca = ranges->nrg_th_cca; data->last_bad_plcp_cnt_ofdm = 0; data->last_fa_cnt_ofdm = 0; data->last_bad_plcp_cnt_cck = 0; data->last_fa_cnt_cck = 0; if (priv->fw->enhance_sensitivity_table) ret |= iwl_enhance_sensitivity_write(priv); else ret |= iwl_sensitivity_write(priv); IWL_DEBUG_CALIB(priv, "<calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) return; data = &(priv->sensitivity_data); if (!iwl_is_any_associated(priv)) { IWL_DEBUG_CALIB(priv, "<< - not associated\n"); return; } spin_lock_bh(&priv->statistics.lock); rx_info = &priv->statistics.rx_non_phy; ofdm = &priv->statistics.rx_ofdm; cck = &priv->statistics.rx_cck; if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); spin_unlock_bh(&priv->statistics.lock); return; } /* Extract Statistics: */ rx_enable_time = le32_to_cpu(rx_info->channel_load); fa_cck = le32_to_cpu(cck->false_alarm_cnt); fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); bad_plcp_cck = le32_to_cpu(cck->plcp_err); bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); statis.beacon_silence_rssi_a = le32_to_cpu(rx_info->beacon_silence_rssi_a); statis.beacon_silence_rssi_b = le32_to_cpu(rx_info->beacon_silence_rssi_b); statis.beacon_silence_rssi_c = le32_to_cpu(rx_info->beacon_silence_rssi_c); statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a); statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b); statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c); spin_unlock_bh(&priv->statistics.lock); IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); if (!rx_enable_time) { IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); return; } /* These statistics increase monotonically, and do not reset * at each beacon. Calculate difference from last value, or just * use the new statistics value if it has reset or wrapped around. */ if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) data->last_bad_plcp_cnt_cck = bad_plcp_cck; else { bad_plcp_cck -= data->last_bad_plcp_cnt_cck; data->last_bad_plcp_cnt_cck += bad_plcp_cck; } if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; else { bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; } if (data->last_fa_cnt_ofdm > fa_ofdm) data->last_fa_cnt_ofdm = fa_ofdm; else { fa_ofdm -= data->last_fa_cnt_ofdm; data->last_fa_cnt_ofdm += fa_ofdm; } if (data->last_fa_cnt_cck > fa_cck) data->last_fa_cnt_cck = fa_cck; else { fa_cck -= data->last_fa_cnt_cck; data->last_fa_cnt_cck += fa_cck; } /* Total aborted signal locks */ norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; norm_fa_cck = fa_cck + bad_plcp_cck; IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); if (priv->fw->enhance_sensitivity_table) iwl_enhance_sensitivity_write(priv); else iwl_sensitivity_write(priv); } static inline u8 find_first_chain(u8 mask) { if (mask & ANT_A) return CHAIN_A; if (mask & ANT_B) return CHAIN_B; return CHAIN_C; } /** * Run disconnected antenna algorithm to find out which antennas are * disconnected. */ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, struct iwl_chain_noise_data *data) { u32 active_chains = 0; u32 max_average_sig; u16 max_average_sig_antenna_i; u8 num_tx_chains; u8 first_chain; u16 i = 0; average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS; average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS; average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS; if (average_sig[0] >= average_sig[1]) { max_average_sig = average_sig[0]; max_average_sig_antenna_i = 0; active_chains = (1 << max_average_sig_antenna_i); } else { max_average_sig = average_sig[1]; max_average_sig_antenna_i = 1; active_chains = (1 << max_average_sig_antenna_i); } if (average_sig[2] >= max_average_sig) { max_average_sig = average_sig[2]; max_average_sig_antenna_i = 2; active_chains = (1 << max_average_sig_antenna_i); } IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1], average_sig[2]); IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", max_average_sig, max_average_sig_antenna_i); /* Compare signal strengths for all 3 receivers. */ for (i = 0; i < NUM_RX_CHAINS; i++) { if (i != max_average_sig_antenna_i) { s32 rssi_delta = (max_average_sig - average_sig[i]); /* If signal is very weak, compared with * strongest, mark it as disconnected. */ if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) data->disconn_array[i] = 1; else active_chains |= (1 << i); IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " "disconn_array[i] = %d\n", i, rssi_delta, data->disconn_array[i]); } } /* * The above algorithm sometimes fails when the ucode * reports 0 for all chains. It's not clear why that * happens to start with, but it is then causing trouble * because this can make us enable more chains than the * hardware really has. * * To be safe, simply mask out any chains that we know * are not on the device. */ active_chains &= priv->eeprom_data->valid_rx_ant; num_tx_chains = 0; for (i = 0; i < NUM_RX_CHAINS; i++) { /* loops on all the bits of * priv->hw_setting.valid_tx_ant */ u8 ant_msk = (1 << i); if (!(priv->eeprom_data->valid_tx_ant & ant_msk)) continue; num_tx_chains++; if (data->disconn_array[i] == 0) /* there is a Tx antenna connected */ break; if (num_tx_chains == priv->hw_params.tx_chains_num && data->disconn_array[i]) { /* * If all chains are disconnected * connect the first valid tx chain */ first_chain = find_first_chain(priv->eeprom_data->valid_tx_ant); data->disconn_array[first_chain] = 0; active_chains |= BIT(first_chain); IWL_DEBUG_CALIB(priv, "All Tx chains are disconnected W/A - declare %d as connected\n", first_chain); break; } } if (active_chains != priv->eeprom_data->valid_rx_ant && active_chains != priv->chain_noise_data.active_chains) IWL_DEBUG_CALIB(priv, "Detected that not all antennas are connected! " "Connected: %#x, valid: %#x.\n", active_chains, priv->eeprom_data->valid_rx_ant); /* Save for use within RXON, TX, SCAN commands, etc. */ data->active_chains = active_chains; IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", active_chains); } static void iwlagn_gain_computation(struct iwl_priv *priv, u32 average_noise[NUM_RX_CHAINS], u8 default_chain) { int i; s32 delta_g; struct iwl_chain_noise_data *data = &priv->chain_noise_data; /* * Find Gain Code for the chains based on "default chain" */ for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) { if ((data->disconn_array[i])) { data->delta_gain_code[i] = 0; continue; } delta_g = (priv->cfg->base_params->chain_noise_scale * ((s32)average_noise[default_chain] - (s32)average_noise[i])) / 1500; /* bound gain by 2 bits value max, 3rd bit is sign */ data->delta_gain_code[i] = min(abs(delta_g), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); if (delta_g < 0) /* * set negative sign ... * note to Intel developers: This is uCode API format, * not the format of any internal device registers. * Do not change this format for e.g. 6050 or similar * devices. Change format only if more resolution * (i.e. more than 2 bits magnitude) is needed. */ data->delta_gain_code[i] |= (1 << 2); } IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n", data->delta_gain_code[1], data->delta_gain_code[2]); if (!data->radio_write) { struct iwl_calib_chain_noise_gain_cmd cmd; memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, priv->phy_calib_chain_noise_gain_cmd); cmd.delta_gain_1 = data->delta_gain_code[1]; cmd.delta_gain_2 = data->delta_gain_code[2]; iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, CMD_ASYNC, sizeof(cmd), &cmd); data->radio_write = 1; data->state = IWL_CHAIN_NOISE_CALIBRATED; } } /* * Accumulate 16 beacons of signal and noise statistics for each of * 3 receivers/antennas/rx-chains, then figure out: * 1) Which antennas are connected. * 2) Differential rx gain settings to balance the 3 receivers. */ void iwl_chain_noise_calibration(struct iwl_priv *priv) { struct iwl_chain_noise_data *data = NULL; u32 chain_noise_a; u32 chain_noise_b; u32 chain_noise_c; u32 chain_sig_a; u32 chain_sig_b; u32 chain_sig_c; u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; u16 i = 0; u16 rxon_chnum = INITIALIZATION_VALUE; u16 stat_chnum = INITIALIZATION_VALUE; u8 rxon_band24; u8 stat_band24; struct statistics_rx_non_phy *rx_info; /* * MULTI-FIXME: * When we support multiple interfaces on different channels, * this must be modified/fixed. */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) return; data = &(priv->chain_noise_data); /* * Accumulate just the first "chain_noise_num_beacons" after * the first association, then we're done forever. */ if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { if (data->state == IWL_CHAIN_NOISE_ALIVE) IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); return; } spin_lock_bh(&priv->statistics.lock); rx_info = &priv->statistics.rx_non_phy; if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); spin_unlock_bh(&priv->statistics.lock); return; } rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); rxon_chnum = le16_to_cpu(ctx->staging.channel); stat_band24 = !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK); stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16; /* Make sure we accumulate data for just the associated channel * (even if scanning). */ if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", rxon_chnum, rxon_band24); spin_unlock_bh(&priv->statistics.lock); return; } /* * Accumulate beacon statistics values across * "chain_noise_num_beacons" */ chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; spin_unlock_bh(&priv->statistics.lock); data->beacon_count++; data->chain_noise_a = (chain_noise_a + data->chain_noise_a); data->chain_noise_b = (chain_noise_b + data->chain_noise_b); data->chain_noise_c = (chain_noise_c + data->chain_noise_c); data->chain_signal_a = (chain_sig_a + data->chain_signal_a); data->chain_signal_b = (chain_sig_b + data->chain_signal_b); data->chain_signal_c = (chain_sig_c + data->chain_signal_c); IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24, data->beacon_count); IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b, chain_sig_c); IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b, chain_noise_c); /* If this is the "chain_noise_num_beacons", determine: * 1) Disconnected antennas (using signal strengths) * 2) Differential gain (using silence noise) to balance receivers */ if (data->beacon_count != IWL_CAL_NUM_BEACONS) return; /* Analyze signal for disconnected antenna */ if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) { /* Disable disconnected antenna algorithm for advanced bt coex, assuming valid antennas are connected */ data->active_chains = priv->eeprom_data->valid_rx_ant; for (i = 0; i < NUM_RX_CHAINS; i++) if (!(data->active_chains & (1<disconn_array[i] = 1; } else iwl_find_disconn_antenna(priv, average_sig, data); /* Analyze noise for rx balance */ average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS; average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS; average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS; for (i = 0; i < NUM_RX_CHAINS; i++) { if (!(data->disconn_array[i]) && (average_noise[i] <= min_average_noise)) { /* This means that chain i is active and has * lower noise values so far: */ min_average_noise = average_noise[i]; min_average_noise_antenna_i = i; } } IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", average_noise[0], average_noise[1], average_noise[2]); IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", min_average_noise, min_average_noise_antenna_i); iwlagn_gain_computation( priv, average_noise, find_first_chain(priv->eeprom_data->valid_rx_ant)); /* Some power changes may have been made during the calibration. * Update and commit the RXON */ iwl_update_chain_flags(priv); data->state = IWL_CHAIN_NOISE_DONE; iwl_power_update_mode(priv, false); } void iwl_reset_run_time_calib(struct iwl_priv *priv) { int i; memset(&(priv->sensitivity_data), 0, sizeof(struct iwl_sensitivity_data)); memset(&(priv->chain_noise_data), 0, sizeof(struct iwl_chain_noise_data)); for (i = 0; i < NUM_RX_CHAINS; i++) priv->chain_noise_data.delta_gain_code[i] = CHAIN_NOISE_DELTA_GAIN_INIT_VAL; /* Ask for statistics now, the uCode will send notification * periodically after association */ iwl_send_statistics_request(priv, CMD_ASYNC, true); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/agn.h0000644000175000017500000004374412026211315023653 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __iwl_agn_h__ #define __iwl_agn_h__ #include "iwl-config.h" #include "dev.h" /* The first 11 queues (0-10) are used otherwise */ #define IWLAGN_FIRST_AMPDU_QUEUE 11 /* AUX (TX during scan dwell) queue */ #define IWL_AUX_QUEUE 10 /* device operations */ extern struct iwl_lib_ops iwl1000_lib; extern struct iwl_lib_ops iwl2000_lib; extern struct iwl_lib_ops iwl2030_lib; extern struct iwl_lib_ops iwl5000_lib; extern struct iwl_lib_ops iwl5150_lib; extern struct iwl_lib_ops iwl6000_lib; extern struct iwl_lib_ops iwl6030_lib; #define TIME_UNIT 1024 /***************************************************** * DRIVER STATUS FUNCTIONS ******************************************************/ #define STATUS_RF_KILL_HW 0 #define STATUS_CT_KILL 1 #define STATUS_ALIVE 2 #define STATUS_READY 3 #define STATUS_EXIT_PENDING 5 #define STATUS_STATISTICS 6 #define STATUS_SCANNING 7 #define STATUS_SCAN_ABORTING 8 #define STATUS_SCAN_HW 9 #define STATUS_FW_ERROR 10 #define STATUS_CHANNEL_SWITCH_PENDING 11 #define STATUS_SCAN_COMPLETE 12 #define STATUS_POWER_PMI 13 #define STATUS_SCAN_ROC_EXPIRED 14 struct iwl_ucode_capabilities; extern struct ieee80211_ops iwlagn_hw_ops; static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) { hdr->op_code = cmd; hdr->first_group = 0; hdr->groups_num = 1; hdr->data_valid = 1; } void iwl_down(struct iwl_priv *priv); void iwl_cancel_deferred_work(struct iwl_priv *priv); void iwlagn_prepare_restart(struct iwl_priv *priv); int __must_check iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); bool iwl_check_for_ct_kill(struct iwl_priv *priv); void iwlagn_lift_passive_no_rx(struct iwl_priv *priv); /* MAC80211 */ struct ieee80211_hw *iwl_alloc_all(void); int iwlagn_mac_setup_register(struct iwl_priv *priv, const struct iwl_ucode_capabilities *capa); void iwlagn_mac_unregister(struct iwl_priv *priv); /* commands */ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, u32 flags, u16 len, const void *data); /* RXON */ void iwl_connection_init_rx_config(struct iwl_priv *priv, struct iwl_rxon_context *ctx); int iwlagn_set_pan_params(struct iwl_priv *priv); int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx); void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx); int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed); void iwlagn_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); void iwlagn_config_ht40(struct ieee80211_conf *conf, struct iwl_rxon_context *ctx); void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf); void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, struct iwl_rxon_context *ctx); void iwl_set_flags_for_band(struct iwl_priv *priv, struct iwl_rxon_context *ctx, enum ieee80211_band band, struct ieee80211_vif *vif); /* uCode */ int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); void iwl_send_prio_tbl(struct iwl_priv *priv); int iwl_init_alive_start(struct iwl_priv *priv); int iwl_run_init_ucode(struct iwl_priv *priv); int iwl_load_ucode_wait_alive(struct iwl_priv *priv, enum iwl_ucode_type ucode_type); int iwl_send_calib_results(struct iwl_priv *priv); int iwl_calib_set(struct iwl_priv *priv, const struct iwl_calib_hdr *cmd, int len); void iwl_calib_free_results(struct iwl_priv *priv); int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, char **buf, bool display); int iwlagn_hw_valid_rtc_data_addr(u32 addr); /* lib */ int iwlagn_send_tx_power(struct iwl_priv *priv); void iwlagn_temperature(struct iwl_priv *priv); int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); int iwlagn_send_beacon_cmd(struct iwl_priv *priv); int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear); static inline const struct ieee80211_supported_band *iwl_get_hw_mode( struct iwl_priv *priv, enum ieee80211_band band) { return priv->hw->wiphy->bands[band]; } #ifdef CONFIG_PM_SLEEP int iwlagn_send_patterns(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan); int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan); #endif /* rx */ int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); void iwl_setup_rx_handlers(struct iwl_priv *priv); void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); /* tx */ int iwlagn_tx_skb(struct iwl_priv *priv, struct ieee80211_sta *sta, struct sk_buff *skb); int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn); int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u8 buf_size); int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); static inline u32 iwl_tx_status_to_mac80211(u32 status) { status &= TX_STATUS_MSK; switch (status) { case TX_STATUS_SUCCESS: case TX_STATUS_DIRECT_DONE: return IEEE80211_TX_STAT_ACK; case TX_STATUS_FAIL_DEST_PS: case TX_STATUS_FAIL_PASSIVE_NO_RX: return IEEE80211_TX_STAT_TX_FILTERED; default: return 0; } } static inline bool iwl_is_tx_success(u32 status) { status &= TX_STATUS_MSK; return (status == TX_STATUS_SUCCESS) || (status == TX_STATUS_DIRECT_DONE); } u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); /* scan */ void iwlagn_post_scan(struct iwl_priv *priv); void iwlagn_disable_roc(struct iwl_priv *priv); int iwl_force_rf_reset(struct iwl_priv *priv, bool external); void iwl_init_scan_params(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv); void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); void iwl_force_scan_end(struct iwl_priv *priv); void iwl_internal_short_hw_scan(struct iwl_priv *priv); void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); void iwl_setup_scan_deferred_work(struct iwl_priv *priv); void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); int __must_check iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif, enum iwl_scan_type scan_type, enum ieee80211_band band); void iwl_scan_roc_expired(struct iwl_priv *priv); void iwl_scan_offchannel_skb(struct iwl_priv *priv); void iwl_scan_offchannel_skb_status(struct iwl_priv *priv); /* For faster active scanning, scan will move to the next channel if fewer than * PLCP_QUIET_THRESH packets are heard on this channel within * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell * time if it's a quiet channel (nothing responded to our probe, and there's * no other traffic). * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ #define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ #define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ #define IWL_SCAN_CHECK_WATCHDOG (HZ * 15) /* bt coex */ void iwlagn_send_advance_bt_config(struct iwl_priv *priv); int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv); void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv); void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv); void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv); void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) { return priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist; } #ifdef CONFIG_IWLWIFI_DEBUG const char *iwl_get_tx_fail_reason(u32 status); const char *iwl_get_agg_tx_fail_reason(u16 status); #else static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; } static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; } #endif /* station management */ int iwlagn_manage_ibss_station(struct iwl_priv *priv, struct ieee80211_vif *vif, bool add); #define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ #define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ #define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of being activated */ #define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211; (this is for the IBSS BSSID stations) */ #define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx); void iwl_clear_ucode_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx); void iwl_dealloc_bcast_stations(struct iwl_priv *priv); int iwl_get_free_ucode_key_offset(struct iwl_priv *priv); int iwl_send_add_sta(struct iwl_priv *priv, struct iwl_addsta_cmd *sta, u8 flags); int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, bool is_ap, struct ieee80211_sta *sta, u8 *sta_id_r); int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, const u8 *addr); void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, const u8 *addr); u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, bool is_ap, struct ieee80211_sta *sta); int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_link_quality_cmd *lq, u8 flags, bool init); int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_sta *sta); bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_sta_ht_cap *ht_cap); static inline int iwl_sta_id(struct ieee80211_sta *sta) { if (WARN_ON(!sta)) return IWL_INVALID_STATION; return ((struct iwl_station_priv *)sta->drv_priv)->sta_id; } int iwlagn_alloc_bcast_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx); int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, u8 *sta_id_r); int iwl_remove_default_wep_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *key); int iwl_set_default_wep_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *key); int iwl_restore_default_wep_keys(struct iwl_priv *priv, struct iwl_rxon_context *ctx); int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *key, struct ieee80211_sta *sta); int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *key, struct ieee80211_sta *sta); void iwl_update_tkip_key(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, int tid, u16 ssn); int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, int tid); void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); int iwl_update_bcast_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx); int iwl_update_bcast_stations(struct iwl_priv *priv); /* rate */ static inline u32 iwl_ant_idx_to_flags(u8 ant_idx) { return BIT(ant_idx) << RATE_MCS_ANT_POS; } static inline u8 iwl_hw_get_rate(__le32 rate_n_flags) { return le32_to_cpu(rate_n_flags) & RATE_MCS_RATE_MSK; } static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) { return cpu_to_le32(flags|(u32)rate); } extern int iwl_alive_start(struct iwl_priv *priv); /* testmode support */ #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len); extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len); extern void iwl_testmode_init(struct iwl_priv *priv); extern void iwl_testmode_free(struct iwl_priv *priv); #else static inline int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) { return -ENOSYS; } static inline int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len) { return -ENOSYS; } static inline void iwl_testmode_init(struct iwl_priv *priv) { } static inline void iwl_testmode_free(struct iwl_priv *priv) { } #endif #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv, enum iwl_rxon_context_id ctxid); #else static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, enum iwl_rxon_context_id ctxid) { } #endif /* status checks */ static inline int iwl_is_ready(struct iwl_priv *priv) { /* The adapter is 'ready' if READY EXIT_PENDING is not set */ return test_bit(STATUS_READY, &priv->status) && !test_bit(STATUS_EXIT_PENDING, &priv->status); } static inline int iwl_is_alive(struct iwl_priv *priv) { return test_bit(STATUS_ALIVE, &priv->status); } static inline int iwl_is_rfkill(struct iwl_priv *priv) { return test_bit(STATUS_RF_KILL_HW, &priv->status); } static inline int iwl_is_ctkill(struct iwl_priv *priv) { return test_bit(STATUS_CT_KILL, &priv->status); } static inline int iwl_is_ready_rf(struct iwl_priv *priv) { if (iwl_is_rfkill(priv)) return 0; return iwl_is_ready(priv); } static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state) { if (state) set_bit(STATUS_POWER_PMI, &priv->status); else clear_bit(STATUS_POWER_PMI, &priv->status); iwl_trans_set_pmi(priv->trans, state); } #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir); #else static inline int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir) { return 0; } #endif /* CONFIG_IWLWIFI_DEBUGFS */ #ifdef CONFIG_IWLWIFI_DEBUG #define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ do { \ if (!iwl_is_rfkill((m))) \ IWL_ERR(m, fmt, ##args); \ else \ __iwl_err((m)->dev, true, \ !iwl_have_debug_level(IWL_DL_RADIO), \ fmt, ##args); \ } while (0) #else #define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ do { \ if (!iwl_is_rfkill((m))) \ IWL_ERR(m, fmt, ##args); \ else \ __iwl_err((m)->dev, true, true, fmt, ##args); \ } while (0) #endif /* CONFIG_IWLWIFI_DEBUG */ extern const char *iwl_dvm_cmd_strings[REPLY_MAX]; static inline const char *iwl_dvm_get_cmd_string(u8 cmd) { const char *s = iwl_dvm_cmd_strings[cmd]; if (s) return s; return "UNKNOWN"; } #endif /* __iwl_agn_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/dvm/Makefile0000644000175000017500000000056412026211315024366 0ustar mcgrofmcgrof# DVM obj-$(CONFIG_IWLDVM) += iwldvm.o iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o iwldvm-objs += lib.o calib.o tt.o sta.o rx.o iwldvm-objs += power.o iwldvm-objs += scan.o led.o iwldvm-objs += rxon.o devices.o iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwldvm-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += testmode.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/0000755000175000017500000000000012026211315023053 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/drv.c0000644000175000017500000003620712026211315024022 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include "iwl-trans.h" #include "iwl-drv.h" #include "iwl-trans.h" #include "cfg.h" #include "internal.h" #define IWL_PCI_DEVICE(dev, subdev, cfg) \ .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ .driver_data = (kernel_ulong_t)&(cfg) /* Hardware specific file defines the PCI IDs table for that hardware module */ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ /* 5300 Series WiFi */ {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ /* 5350 Series WiFi/WiMax */ {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ /* 5150 Series Wifi/WiMax */ {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ /* 6x00 Series */ {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)}, {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)}, {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)}, {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)}, {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)}, {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)}, {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)}, {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)}, {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, /* 6x05 Series */ {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)}, {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)}, {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)}, {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)}, {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)}, {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)}, {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)}, {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)}, {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */ {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */ /* 6x30 Series */ {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)}, {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)}, {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)}, {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)}, {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)}, {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)}, {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)}, {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)}, {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)}, {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)}, {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)}, {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)}, /* 6x50 WiFi/WiMax Series */ {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, /* 6150 WiFi/WiMax Series */ {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)}, {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)}, {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)}, {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)}, {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)}, {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)}, /* 1000 Series WiFi */ {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)}, {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)}, {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)}, /* 100 Series WiFi */ {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)}, {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)}, {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)}, {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)}, {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)}, {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)}, /* 130 Series WiFi */ {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)}, {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)}, {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)}, {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)}, {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)}, {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)}, /* 2x00 Series */ {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)}, /* 2x30 Series */ {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)}, /* 6x35 Series */ {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)}, {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)}, /* 105 Series */ {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)}, {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)}, {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)}, /* 135 Series */ {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, {0} }; MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 #ifndef CONFIG_IWLWIFI_IDI static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); struct iwl_trans *iwl_trans; struct iwl_trans_pcie *trans_pcie; iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); if (iwl_trans == NULL) return -ENOMEM; pci_set_drvdata(pdev, iwl_trans); trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); trans_pcie->drv = iwl_drv_start(iwl_trans, cfg); if (!trans_pcie->drv) goto out_free_trans; /* register transport layer debugfs here */ if (iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir)) goto out_free_drv; return 0; out_free_drv: iwl_drv_stop(trans_pcie->drv); out_free_trans: iwl_trans_pcie_free(iwl_trans); pci_set_drvdata(pdev, NULL); return -EFAULT; } static void __devexit iwl_pci_remove(struct pci_dev *pdev) { struct iwl_trans *trans = pci_get_drvdata(pdev); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); iwl_drv_stop(trans_pcie->drv); iwl_trans_pcie_free(trans); pci_set_drvdata(pdev, NULL); } #endif /* CONFIG_IWLWIFI_IDI */ #ifdef CONFIG_PM_SLEEP static int iwl_pci_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); /* Before you put code here, think about WoWLAN. You cannot check here * whether WoWLAN is enabled or not, and your code will run even if * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx. */ return iwl_trans_suspend(iwl_trans); } static int iwl_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); /* Before you put code here, think about WoWLAN. You cannot check here * whether WoWLAN is enabled or not, and your code will run even if * WoWLAN is enabled - the NIC may be alive. */ /* * We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state. */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); return iwl_trans_resume(iwl_trans); } compat_pci_suspend(iwl_pci_suspend) compat_pci_resume(iwl_pci_resume) static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); #define IWL_PM_OPS (&iwl_dev_pm_ops) #else #define IWL_PM_OPS NULL #endif #ifdef CONFIG_IWLWIFI_IDI /* * Defined externally in iwl-idi.c */ int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); void __devexit iwl_pci_remove(struct pci_dev *pdev); #endif /* CONFIG_IWLWIFI_IDI */ static struct pci_driver iwl_pci_driver = { .name = DRV_NAME, .id_table = iwl_hw_card_ids, .probe = iwl_pci_probe, .remove = __devexit_p(iwl_pci_remove), #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = IWL_PM_OPS, #elif defined(CONFIG_PM) .suspend = iwl_pci_suspend_compat, .resume = iwl_pci_resume_compat, #endif }; int __must_check iwl_pci_register_driver(void) { int ret; ret = pci_register_driver(&iwl_pci_driver); if (ret) pr_err("Unable to initialize PCI module\n"); return ret; } void iwl_pci_unregister_driver(void) { pci_unregister_driver(&iwl_pci_driver); } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/tx.c0000644000175000017500000006627712026211315023674 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include "iwl-debug.h" #include "iwl-csr.h" #include "iwl-prph.h" #include "iwl-io.h" #include "iwl-op-mode.h" #include "internal.h" /* FIXME: need to abstract out TX command (once we know what it looks like) */ #include "dvm/commands.h" #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 /** * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array */ void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq, u16 byte_cnt) { struct iwlagn_scd_bc_tbl *scd_bc_tbl; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int write_ptr = txq->q.write_ptr; int txq_id = txq->q.id; u8 sec_ctl = 0; u8 sta_id = 0; u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; __le16 bc_ent; struct iwl_tx_cmd *tx_cmd = (void *) txq->entries[txq->q.write_ptr].cmd->payload; scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); sta_id = tx_cmd->sta_id; sec_ctl = tx_cmd->sec_ctl; switch (sec_ctl & TX_CMD_SEC_MSK) { case TX_CMD_SEC_CCM: len += CCMP_MIC_LEN; break; case TX_CMD_SEC_TKIP: len += TKIP_ICV_LEN; break; case TX_CMD_SEC_WEP: len += WEP_IV_LEN + WEP_ICV_LEN; break; } bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) scd_bc_tbl[txq_id]. tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } /** * iwl_txq_update_write_ptr - Send new write index to hardware */ void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq) { u32 reg = 0; int txq_id = txq->q.id; if (txq->need_update == 0) return; if (trans->cfg->base_params->shadow_reg_enable) { /* shadow register enabled */ iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); } else { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); /* if we're trying to save power */ if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { /* wake up nic if it's powered down ... * uCode will wake up, and interrupt us again, so next * time we'll skip this part. */ reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup," " GP1 = 0x%x\n", txq_id, reg); iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); return; } iwl_write_direct32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); /* * else not in power-save mode, * uCode will never sleep when we're * trying to tx (during RFKILL, we're not trying to tx). */ } else iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); } txq->need_update = 0; } static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) { struct iwl_tfd_tb *tb = &tfd->tbs[idx]; dma_addr_t addr = get_unaligned_le32(&tb->lo); if (sizeof(dma_addr_t) > sizeof(u32)) addr |= ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; return addr; } static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) { struct iwl_tfd_tb *tb = &tfd->tbs[idx]; return le16_to_cpu(tb->hi_n_len) >> 4; } static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, dma_addr_t addr, u16 len) { struct iwl_tfd_tb *tb = &tfd->tbs[idx]; u16 hi_n_len = len << 4; put_unaligned_le32(addr, &tb->lo); if (sizeof(dma_addr_t) > sizeof(u32)) hi_n_len |= ((addr >> 16) >> 16) & 0xF; tb->hi_n_len = cpu_to_le16(hi_n_len); tfd->num_tbs = idx + 1; } static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd) { return tfd->num_tbs & 0x1f; } static void iwl_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, struct iwl_tfd *tfd, enum dma_data_direction dma_dir) { int i; int num_tbs; /* Sanity check on number of chunks */ num_tbs = iwl_tfd_get_num_tbs(tfd); if (num_tbs >= IWL_NUM_OF_TBS) { IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); /* @todo issue fatal error, it is quite serious situation */ return; } /* Unmap tx_cmd */ if (num_tbs) dma_unmap_single(trans->dev, dma_unmap_addr(meta, mapping), dma_unmap_len(meta, len), DMA_BIDIRECTIONAL); /* Unmap chunks, if any. */ for (i = 1; i < num_tbs; i++) dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i), iwl_tfd_tb_get_len(tfd, i), dma_dir); tfd->num_tbs = 0; } /** * iwl_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] * @trans - transport private data * @txq - tx queue * @dma_dir - the direction of the DMA mapping * * Does NOT advance any TFD circular buffer read/write indexes * Does NOT free the TFD itself (which is within circular buffer) */ void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, enum dma_data_direction dma_dir) { struct iwl_tfd *tfd_tmp = txq->tfds; /* rd_ptr is bounded by n_bd and idx is bounded by n_window */ int rd_ptr = txq->q.read_ptr; int idx = get_cmd_index(&txq->q, rd_ptr); lockdep_assert_held(&txq->lock); /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ iwl_unmap_tfd(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], dma_dir); /* free SKB */ if (txq->entries) { struct sk_buff *skb; skb = txq->entries[idx].skb; /* Can be called from irqs-disabled context * If skb is not NULL, it means that the whole queue is being * freed and that the queue is not empty - free the skb */ if (skb) { iwl_op_mode_free_skb(trans->op_mode, skb); txq->entries[idx].skb = NULL; } } } int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset) { struct iwl_queue *q; struct iwl_tfd *tfd, *tfd_tmp; u32 num_tbs; q = &txq->q; tfd_tmp = txq->tfds; tfd = &tfd_tmp[q->write_ptr]; if (reset) memset(tfd, 0, sizeof(*tfd)); num_tbs = iwl_tfd_get_num_tbs(tfd); /* Each TFD can point to a maximum 20 Tx buffers */ if (num_tbs >= IWL_NUM_OF_TBS) { IWL_ERR(trans, "Error can not send more than %d chunks\n", IWL_NUM_OF_TBS); return -EINVAL; } if (WARN_ON(addr & ~DMA_BIT_MASK(36))) return -EINVAL; if (unlikely(addr & ~IWL_TX_DMA_MASK)) IWL_ERR(trans, "Unaligned address = %llx\n", (unsigned long long)addr); iwl_tfd_set_tb(tfd, num_tbs, addr, len); return 0; } /*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** * DMA services * * Theory of operation * * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer * of buffer descriptors, each of which points to one or more data buffers for * the device to read from or fill. Driver and device exchange status of each * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty * entries in each circular buffer, to protect against confusing empty and full * queue states. * * The device reads or writes the data in the queues via the device's several * DMA/FIFO channels. Each queue is mapped to a single DMA channel. * * For Tx queue, there are low mark and high mark limits. If, after queuing * the packet for Tx, free space become < low mark, Tx queue stopped. When * reclaiming packets (on 'tx done IRQ), if free space become > high mark, * Tx queue resumed. * ***************************************************/ int iwl_queue_space(const struct iwl_queue *q) { int s = q->read_ptr - q->write_ptr; if (q->read_ptr > q->write_ptr) s -= q->n_bd; if (s <= 0) s += q->n_window; /* keep some reserve to not confuse empty and full situations */ s -= 2; if (s < 0) s = 0; return s; } /** * iwl_queue_init - Initialize queue's high/low-water and read/write indexes */ int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id) { q->n_bd = count; q->n_window = slots_num; q->id = id; /* count must be power-of-two size, otherwise iwl_queue_inc_wrap * and iwl_queue_dec_wrap are broken. */ if (WARN_ON(!is_power_of_2(count))) return -EINVAL; /* slots_num must be power-of-two size, otherwise * get_cmd_index is broken. */ if (WARN_ON(!is_power_of_2(slots_num))) return -EINVAL; q->low_mark = q->n_window / 4; if (q->low_mark < 4) q->low_mark = 4; q->high_mark = q->n_window / 8; if (q->high_mark < 2) q->high_mark = 2; q->write_ptr = q->read_ptr = 0; return 0; } static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; int txq_id = txq->q.id; int read_ptr = txq->q.read_ptr; u8 sta_id = 0; __le16 bc_ent; struct iwl_tx_cmd *tx_cmd = (void *)txq->entries[txq->q.read_ptr].cmd->payload; WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); if (txq_id != trans_pcie->cmd_queue) sta_id = tx_cmd->sta_id; bc_ent = cpu_to_le16(1 | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) scd_bc_tbl[txq_id]. tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; } static int iwl_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, u16 txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 tbl_dw_addr; u32 tbl_dw; u16 scd_q2ratid; scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK; tbl_dw_addr = trans_pcie->scd_base_addr + SCD_TRANS_TBL_OFFSET_QUEUE(txq_id); tbl_dw = iwl_read_targ_mem(trans, tbl_dw_addr); if (txq_id & 0x1) tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); else tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); iwl_write_targ_mem(trans, tbl_dw_addr, tbl_dw); return 0; } static inline void iwl_txq_set_inactive(struct iwl_trans *trans, u16 txq_id) { /* Simply stop the queue, but don't change any configuration; * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (test_and_set_bit(txq_id, trans_pcie->queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); /* Stop this Tx queue before configuring it */ iwl_txq_set_inactive(trans, txq_id); /* Set this queue as a chain-building queue unless it is CMD queue */ if (txq_id != trans_pcie->cmd_queue) iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); /* If this queue is mapped to a certain station: it is an AGG queue */ if (sta_id != IWL_INVALID_STATION) { u16 ra_tid = BUILD_RAxTID(sta_id, tid); /* Map receiver-address / traffic-ID to this queue */ iwl_txq_set_ratid_map(trans, ra_tid, txq_id); /* enable aggregations for the queue */ iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); } else { /* * disable aggregations for the queue, this will also make the * ra_tid mapping configuration irrelevant since it is now a * non-AGG queue. */ iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); } /* Place first TFD at index corresponding to start sequence number. * Assumes that ssn_idx is valid (!= 0xFFF) */ trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff); trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff); iwl_write_direct32(trans, HBUS_TARG_WRPTR, (ssn & 0xff) | (txq_id << 8)); iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); /* Set up Tx window size and frame limit for this queue */ iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0); iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) | (fifo << SCD_QUEUE_STTS_REG_POS_TXF) | (1 << SCD_QUEUE_STTS_REG_POS_WSL) | SCD_QUEUE_STTS_REG_MSK); IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n", txq_id, fifo, ssn & 0xff); } void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u16 rd_ptr, wr_ptr; int n_bd = trans_pcie->txq[txq_id].q.n_bd; if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) { WARN_ONCE(1, "queue %d not used", txq_id); return; } rd_ptr = iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id)) & (n_bd - 1); wr_ptr = iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq_id)); WARN_ONCE(rd_ptr != wr_ptr, "queue %d isn't empty: [%d,%d]", txq_id, rd_ptr, wr_ptr); iwl_txq_set_inactive(trans, txq_id); IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); } /*************** HOST COMMAND QUEUE FUNCTIONS *****/ /** * iwl_enqueue_hcmd - enqueue a uCode command * @priv: device private data point * @cmd: a point to the ucode command structure * * The function returns < 0 values to indicate the operation is * failed. On success, it turns the index (> 0) of command in the * command queue. */ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; u32 idx; u16 copy_size, cmd_size; bool had_nocopy = false; int i; u32 cmd_pos; copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); /* need one for the header if the first is NOCOPY */ BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; } else { /* NOCOPY must not be followed by normal! */ if (WARN_ON(had_nocopy)) return -EINVAL; copy_size += cmd->len[i]; } cmd_size += cmd->len[i]; } /* * If any of the command structures end up being larger than * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically * allocated into separate TFDs, then we will need to * increase the size of the buffers. */ if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) return -EINVAL; spin_lock_bh(&txq->lock); if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_bh(&txq->lock); IWL_ERR(trans, "No space in command queue\n"); iwl_op_mode_cmd_queue_full(trans->op_mode); return -ENOSPC; } idx = get_cmd_index(q, q->write_ptr); out_cmd = txq->entries[idx].cmd; out_meta = &txq->entries[idx].meta; memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; /* set up the header */ out_cmd->hdr.cmd = cmd->id; out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | INDEX_TO_SEQ(q->write_ptr)); /* and copy the data that needs to be copied */ cmd_pos = offsetof(struct iwl_device_cmd, payload); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) break; memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], cmd->len[i]); cmd_pos += cmd->len[i]; } WARN_ON_ONCE(txq->entries[idx].copy_cmd); /* * since out_cmd will be the source address of the FH, it will write * the retry count there. So when the user needs to receivce the HCMD * that corresponds to the response in the response handler, it needs * to set CMD_WANT_HCMD. */ if (cmd->flags & CMD_WANT_HCMD) { txq->entries[idx].copy_cmd = kmemdup(out_cmd, cmd_pos, GFP_ATOMIC); if (unlikely(!txq->entries[idx].copy_cmd)) { idx = -ENOMEM; goto out; } } IWL_DEBUG_HC(trans, "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", trans_pcie_get_cmd_string(trans_pcie, out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { idx = -ENOMEM; goto out; } dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, copy_size); iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) continue; phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { iwl_unmap_tfd(trans, out_meta, &txq->tfds[q->write_ptr], DMA_BIDIRECTIONAL); idx = -ENOMEM; goto out; } iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, cmd->len[i], 0); } out_meta->flags = cmd->flags; txq->need_update = 1; trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr, copy_size); /* start timer if queue currently empty */ if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); /* Increment and update queue's write index */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_txq_update_write_ptr(trans, txq); out: spin_unlock_bh(&txq->lock); return idx; } static inline void iwl_queue_progress(struct iwl_trans_pcie *trans_pcie, struct iwl_tx_queue *txq) { if (!trans_pcie->wd_timeout) return; /* * if empty delete timer, otherwise move timer forward * since we're making progress on this queue */ if (txq->q.read_ptr == txq->q.write_ptr) del_timer(&txq->stuck_timer); else mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); } /** * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd * * When FW advances 'R' index, all entries between old and new 'R' index * need to be reclaimed. As result, some free space forms. If there is * enough free space (> low mark), wake the stack that feeds us. */ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, int idx) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; struct iwl_queue *q = &txq->q; int nfreed = 0; lockdep_assert_held(&txq->lock); if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) { IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", __func__, txq_id, idx, q->n_bd, q->write_ptr, q->read_ptr); return; } for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { if (nfreed++ > 0) { IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", idx, q->write_ptr, q->read_ptr); iwl_op_mode_nic_error(trans->op_mode); } } iwl_queue_progress(trans_pcie, txq); } /** * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim * @handler_status: return value of the handler of the command * (put in setup_rx_handlers) * * If an Rx buffer has an async callback associated with it the callback * will be executed. The attached skb (if present) will only be freed * if the callback returns 1 */ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, int handler_status) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); int cmd_index; struct iwl_device_cmd *cmd; struct iwl_cmd_meta *meta; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; /* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced * in the queue management code. */ if (WARN(txq_id != trans_pcie->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", txq_id, trans_pcie->cmd_queue, sequence, trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { iwl_print_hex_error(trans, pkt, 32); return; } spin_lock(&txq->lock); cmd_index = get_cmd_index(&txq->q, index); cmd = txq->entries[cmd_index].cmd; meta = &txq->entries[cmd_index].meta; iwl_unmap_tfd(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { struct page *p = rxb_steal_page(rxb); meta->source->resp_pkt = pkt; meta->source->_rx_page_addr = (unsigned long)page_address(p); meta->source->_rx_page_order = trans_pcie->rx_page_order; meta->source->handler_status = handler_status; } iwl_hcmd_queue_reclaim(trans, txq_id, index); if (!(meta->flags & CMD_ASYNC)) { if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { IWL_WARN(trans, "HCMD_ACTIVE already clear for command %s\n", trans_pcie_get_cmd_string(trans_pcie, cmd->hdr.cmd)); } clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", trans_pcie_get_cmd_string(trans_pcie, cmd->hdr.cmd)); wake_up(&trans->wait_command_queue); } meta->flags = 0; spin_unlock(&txq->lock); } #define HOST_COMPLETE_TIMEOUT (2 * HZ) static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; /* An asynchronous command can not expect an SKB to be set. */ if (WARN_ON(cmd->flags & CMD_WANT_SKB)) return -EINVAL; ret = iwl_enqueue_hcmd(trans, cmd); if (ret < 0) { IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); return ret; } return 0; } static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int cmd_idx; int ret; IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id)); if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status))) { IWL_ERR(trans, "Command %s: a command is already active!\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id)); return -EIO; } IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id)); cmd_idx = iwl_enqueue_hcmd(trans, cmd); if (cmd_idx < 0) { ret = cmd_idx; clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); return ret; } ret = wait_event_timeout(trans->wait_command_queue, !test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status), HOST_COMPLETE_TIMEOUT); if (!ret) { if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; IWL_ERR(trans, "Error sending %s: time out after %dms.\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", q->read_ptr, q->write_ptr); clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id)); ret = -ETIMEDOUT; goto cancel; } } if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { IWL_ERR(trans, "Error: Response NULL in '%s'\n", trans_pcie_get_cmd_string(trans_pcie, cmd->id)); ret = -EIO; goto cancel; } return 0; cancel: if (cmd->flags & CMD_WANT_SKB) { /* * Cancel the CMD_WANT_SKB flag for the cmd in the * TX cmd queue. Otherwise in case the cmd comes * in later, it will possibly set an invalid * address (cmd->meta.source). */ trans_pcie->txq[trans_pcie->cmd_queue]. entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; } if (cmd->resp_pkt) { iwl_free_resp(cmd); cmd->resp_pkt = NULL; } return ret; } int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { if (cmd->flags & CMD_ASYNC) return iwl_send_cmd_async(trans, cmd); return iwl_send_cmd_sync(trans, cmd); } /* Frees buffers until index _not_ inclusive */ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, struct sk_buff_head *skbs) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; struct iwl_queue *q = &txq->q; int last_to_free; int freed = 0; /* This function is not meant to release cmd queue*/ if (WARN_ON(txq_id == trans_pcie->cmd_queue)) return 0; lockdep_assert_held(&txq->lock); /*Since we free until index _not_ inclusive, the one before index is * the last we will free. This one must be used */ last_to_free = iwl_queue_dec_wrap(index, q->n_bd); if ((index >= q->n_bd) || (iwl_queue_used(q, last_to_free) == 0)) { IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", __func__, txq_id, last_to_free, q->n_bd, q->write_ptr, q->read_ptr); return 0; } if (WARN_ON(!skb_queue_empty(skbs))) return 0; for (; q->read_ptr != index; q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) continue; __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); txq->entries[txq->q.read_ptr].skb = NULL; iwlagn_txq_inval_byte_cnt_tbl(trans, txq); iwl_txq_free_tfd(trans, txq, DMA_TO_DEVICE); freed++; } iwl_queue_progress(trans_pcie, txq); return freed; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/trans.c0000644000175000017500000017173012026211315024357 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #include #include #include #include #include #include #include #include "iwl-drv.h" #include "iwl-trans.h" #include "iwl-csr.h" #include "iwl-prph.h" #include "iwl-agn-hw.h" #include "internal.h" /* FIXME: need to abstract out TX command (once we know what it looks like) */ #include "dvm/commands.h" #define SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie) \ (((1<cfg->base_params->num_of_queues) - 1) &\ (~(1<<(trans_pcie)->cmd_queue))) static int iwl_trans_rx_alloc(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; struct device *dev = trans->dev; memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); spin_lock_init(&rxq->lock); if (WARN_ON(rxq->bd || rxq->rb_stts)) return -EINVAL; /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, &rxq->bd_dma, GFP_KERNEL); if (!rxq->bd) goto err_bd; /*Allocate the driver's pointer to receive buffer status */ rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), &rxq->rb_stts_dma, GFP_KERNEL); if (!rxq->rb_stts) goto err_rb_stts; return 0; err_rb_stts: dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); rxq->bd = NULL; err_bd: return -ENOMEM; } static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; int i; /* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { /* In the reset function, these buffers may have been allocated * to an SKB, so we need to unmap and free potential storage */ if (rxq->pool[i].page != NULL) { dma_unmap_page(trans->dev, rxq->pool[i].page_dma, PAGE_SIZE << trans_pcie->rx_page_order, DMA_FROM_DEVICE); __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order); rxq->pool[i].page = NULL; } list_add_tail(&rxq->pool[i].list, &rxq->rx_used); } } static void iwl_trans_rx_hw_init(struct iwl_trans *trans, struct iwl_rx_queue *rxq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 rb_size; const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ u32 rb_timeout = RX_RB_TIMEOUT; /* FIXME: RX_RB_TIMEOUT for all devices? */ if (trans_pcie->rx_buf_size_8k) rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; else rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; /* Stop Rx DMA */ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); /* Reset driver's Rx queue write index */ iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); /* Tell device where to find RBD circular buffer in DRAM */ iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG, (u32)(rxq->bd_dma >> 8)); /* Tell device where in DRAM to update its Rx status */ iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4); /* Enable Rx DMA * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in * the credit mechanism in 5000 HW RX FIFO * Direct rx interrupts to hosts * Rx buffer size 4 or 8k * RB timeout 0x10 * 256 RBDs */ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | rb_size| (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); /* Set interrupt coalescing timer to default (2048 usecs) */ iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); } static int iwl_rx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; int i, err; unsigned long flags; if (!rxq->bd) { err = iwl_trans_rx_alloc(trans); if (err) return err; } spin_lock_irqsave(&rxq->lock, flags); INIT_LIST_HEAD(&rxq->rx_free); INIT_LIST_HEAD(&rxq->rx_used); iwl_trans_rxq_free_rx_bufs(trans); for (i = 0; i < RX_QUEUE_SIZE; i++) rxq->queue[i] = NULL; /* Set us so that we have processed and used all buffers, but have * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; rxq->write_actual = 0; rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); iwlagn_rx_replenish(trans); iwl_trans_rx_hw_init(trans, rxq); spin_lock_irqsave(&trans_pcie->irq_lock, flags); rxq->need_update = 1; iwl_rx_queue_update_write_ptr(trans, rxq); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return 0; } static void iwl_trans_pcie_rx_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; unsigned long flags; /*if rxq->bd is NULL, it means that nothing has been allocated, * exit now */ if (!rxq->bd) { IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); return; } spin_lock_irqsave(&rxq->lock, flags); iwl_trans_rxq_free_rx_bufs(trans); spin_unlock_irqrestore(&rxq->lock, flags); dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); rxq->bd = NULL; if (rxq->rb_stts) dma_free_coherent(trans->dev, sizeof(struct iwl_rb_status), rxq->rb_stts, rxq->rb_stts_dma); else IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); rxq->rb_stts = NULL; } static int iwl_trans_rx_stop(struct iwl_trans *trans) { /* stop Rx DMA */ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); } static int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr, size_t size) { if (WARN_ON(ptr->addr)) return -EINVAL; ptr->addr = dma_alloc_coherent(trans->dev, size, &ptr->dma, GFP_KERNEL); if (!ptr->addr) return -ENOMEM; ptr->size = size; return 0; } static void iwlagn_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr) { if (unlikely(!ptr->addr)) return; dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma); memset(ptr, 0, sizeof(*ptr)); } static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) { struct iwl_tx_queue *txq = (void *)data; struct iwl_queue *q = &txq->q; struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); u32 scd_sram_addr = trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_LOWER_BOUND + (16 * txq->q.id); u8 buf[16]; int i; spin_lock(&txq->lock); /* check if triggered erroneously */ if (txq->q.read_ptr == txq->q.write_ptr) { spin_unlock(&txq->lock); return; } spin_unlock(&txq->lock); IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, jiffies_to_msecs(trans_pcie->wd_timeout)); IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", txq->q.read_ptr, txq->q.write_ptr); iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); iwl_print_hex_error(trans, buf, sizeof(buf)); for (i = 0; i < FH_TCSR_CHNL_NUM; i++) IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, iwl_read_direct32(trans, FH_TX_TRB_REG(i))); for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i)); u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); u32 tbl_dw = iwl_read_targ_mem(trans, trans_pcie->scd_base_addr + SCD_TRANS_TBL_OFFSET_QUEUE(i)); if (i & 0x1) tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; else tbl_dw = tbl_dw & 0x0000FFFF; IWL_ERR(trans, "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", i, active ? "" : "in", fifo, tbl_dw, iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1), iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); } for (i = q->read_ptr; i != q->write_ptr; i = iwl_queue_inc_wrap(i, q->n_bd)) { struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)txq->entries[i].cmd->payload; IWL_ERR(trans, "scratch %d = 0x%08x\n", i, get_unaligned_le32(&tx_cmd->scratch)); } iwl_op_mode_nic_error(trans->op_mode); } static int iwl_trans_txq_alloc(struct iwl_trans *trans, struct iwl_tx_queue *txq, int slots_num, u32 txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; int i; if (WARN_ON(txq->entries || txq->tfds)) return -EINVAL; setup_timer(&txq->stuck_timer, iwl_trans_pcie_queue_stuck_timer, (unsigned long)txq); txq->trans_pcie = trans_pcie; txq->q.n_window = slots_num; txq->entries = kcalloc(slots_num, sizeof(struct iwl_pcie_tx_queue_entry), GFP_KERNEL); if (!txq->entries) goto error; if (txq_id == trans_pcie->cmd_queue) for (i = 0; i < slots_num; i++) { txq->entries[i].cmd = kmalloc(sizeof(struct iwl_device_cmd), GFP_KERNEL); if (!txq->entries[i].cmd) goto error; } /* Circular buffer of transmit frame descriptors (TFDs), * shared with device */ txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL); if (!txq->tfds) { IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); goto error; } txq->q.id = txq_id; return 0; error: if (txq->entries && txq_id == trans_pcie->cmd_queue) for (i = 0; i < slots_num; i++) kfree(txq->entries[i].cmd); kfree(txq->entries); txq->entries = NULL; return -ENOMEM; } static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq, int slots_num, u32 txq_id) { int ret; txq->need_update = 0; /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); /* Initialize queue's high/low-water marks, and head/tail indexes */ ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id); if (ret) return ret; spin_lock_init(&txq->lock); /* * Tell nic where to find circular buffer of Tx Frame Descriptors for * given Tx queue, and enable the DMA channel used for that queue. * Circular buffer (TFD queue in DRAM) physical base address */ iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8); return 0; } /** * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's */ static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; struct iwl_queue *q = &txq->q; enum dma_data_direction dma_dir; if (!q->n_bd) return; /* In the command queue, all the TBs are mapped as BIDI * so unmap them as such. */ if (txq_id == trans_pcie->cmd_queue) dma_dir = DMA_BIDIRECTIONAL; else dma_dir = DMA_TO_DEVICE; spin_lock_bh(&txq->lock); while (q->write_ptr != q->read_ptr) { iwl_txq_free_tfd(trans, txq, dma_dir); q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); } spin_unlock_bh(&txq->lock); } /** * iwl_tx_queue_free - Deallocate DMA queue. * @txq: Transmit queue to deallocate. * * Empty queue by removing and destroying all BD's. * Free all buffers. * 0-fill, but do not free "txq" descriptor structure. */ static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; struct device *dev = trans->dev; int i; if (WARN_ON(!txq)) return; iwl_tx_queue_unmap(trans, txq_id); /* De-alloc array of command/tx buffers */ if (txq_id == trans_pcie->cmd_queue) for (i = 0; i < txq->q.n_window; i++) { kfree(txq->entries[i].cmd); kfree(txq->entries[i].copy_cmd); } /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) { dma_free_coherent(dev, sizeof(struct iwl_tfd) * txq->q.n_bd, txq->tfds, txq->q.dma_addr); memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); } kfree(txq->entries); txq->entries = NULL; del_timer_sync(&txq->stuck_timer); /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } /** * iwl_trans_tx_free - Free TXQ Context * * Destroy all TX DMA queues and structures */ static void iwl_trans_pcie_tx_free(struct iwl_trans *trans) { int txq_id; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); /* Tx queues */ if (trans_pcie->txq) { for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) iwl_tx_queue_free(trans, txq_id); } kfree(trans_pcie->txq); trans_pcie->txq = NULL; iwlagn_free_dma_ptr(trans, &trans_pcie->kw); iwlagn_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); } /** * iwl_trans_tx_alloc - allocate TX context * Allocate all Tx DMA structures and initialize them * * @param priv * @return error code */ static int iwl_trans_tx_alloc(struct iwl_trans *trans) { int ret; int txq_id, slots_num; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues * sizeof(struct iwlagn_scd_bc_tbl); /*It is not allowed to alloc twice, so warn when this happens. * We cannot rely on the previous allocation, so free and fail */ if (WARN_ON(trans_pcie->txq)) { ret = -EINVAL; goto error; } ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, scd_bc_tbls_size); if (ret) { IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); goto error; } /* Alloc keep-warm buffer */ ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); if (ret) { IWL_ERR(trans, "Keep Warm allocation failed\n"); goto error; } trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, sizeof(struct iwl_tx_queue), GFP_KERNEL); if (!trans_pcie->txq) { IWL_ERR(trans, "Not enough memory for txq\n"); ret = ENOMEM; goto error; } /* Alloc and init all Tx queues, including the command queue (#4/#9) */ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) { slots_num = (txq_id == trans_pcie->cmd_queue) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; ret = iwl_trans_txq_alloc(trans, &trans_pcie->txq[txq_id], slots_num, txq_id); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); goto error; } } return 0; error: iwl_trans_pcie_tx_free(trans); return ret; } static int iwl_tx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; int txq_id, slots_num; unsigned long flags; bool alloc = false; if (!trans_pcie->txq) { ret = iwl_trans_tx_alloc(trans); if (ret) goto error; alloc = true; } spin_lock_irqsave(&trans_pcie->irq_lock, flags); /* Turn off all Tx DMA fifos */ iwl_write_prph(trans, SCD_TXFACT, 0); /* Tell NIC where to find the "keep warm" buffer */ iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, trans_pcie->kw.dma >> 4); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); /* Alloc and init all Tx queues, including the command queue (#4/#9) */ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) { slots_num = (txq_id == trans_pcie->cmd_queue) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; ret = iwl_trans_txq_init(trans, &trans_pcie->txq[txq_id], slots_num, txq_id); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); goto error; } } return 0; error: /*Upon error, free only if we allocated something */ if (alloc) iwl_trans_pcie_tx_free(trans); return ret; } static void iwl_set_pwr_vmain(struct iwl_trans *trans) { /* * (for documentation purposes) * to set power to V_AUX, do: if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VAUX, ~APMG_PS_CTRL_MSK_PWR_SRC); */ iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, ~APMG_PS_CTRL_MSK_PWR_SRC); } /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 #define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01 #define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02 static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u16 pci_lnk_ctl; pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &pci_lnk_ctl); return pci_lnk_ctl; } static void iwl_apm_config(struct iwl_trans *trans) { /* * HW bug W/A for instability in PCIe bus L0S->L1 transition. * Check if BIOS (or OS) enabled L1-ASPM on this device. * If so (likely), disable L0S, so device moves directly L0->L1; * costs negligible amount of power savings. * If not (unlikely), enable L0S, so there is at least some * power savings, even without L1. */ u16 lctl = iwl_pciexp_link_ctrl(trans); if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN) { /* L1-ASPM enabled; disable(!) L0S */ iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); dev_printk(KERN_INFO, trans->dev, "L1 Enabled; Disabling L0S\n"); } else { /* L1-ASPM disabled; enable(!) L0S */ iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); dev_printk(KERN_INFO, trans->dev, "L1 Disabled; Enabling L0S\n"); } trans->pm_support = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); } /* * Start up NIC's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via iwl_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor */ static int iwl_apm_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = 0; IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); /* * Use "set_bit" below rather than "write", to preserve any hardware * bits already set by default after reset. */ /* Disable L0S exit timer (platform NMI Work/Around) */ iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); /* * Disable L0s without affecting L1; * don't wait for ICH L0s (ICH bug W/A) */ iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); /* Set FH wait threshold to maximum (HW error during stress W/A) */ iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); /* * Enable HAP INTA (interrupt from management bus) to * wake device's PCI Express link L1a -> L0s */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); iwl_apm_config(trans); /* Configure analog phase-lock-loop before activating to D0A */ if (trans->cfg->base_params->pll_cfg_val) iwl_set_bit(trans, CSR_ANA_PLL_CFG, trans->cfg->base_params->pll_cfg_val); /* * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. */ iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); /* * Wait for clock stabilization; once stabilized, access to * device-internal resources is supported, e.g. iwl_write_prph() * and accesses to uCode SRAM. */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (ret < 0) { IWL_DEBUG_INFO(trans, "Failed to init the card\n"); goto out; } /* * Enable DMA clock and wait for it to stabilize. * * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits * do not disable clocks. This preserves any hardware bits already * set by default in "CLK_CTRL_REG" after reset. */ iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(20); /* Disable L1-Active */ iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); out: return ret; } static int iwl_apm_stop_master(struct iwl_trans *trans) { int ret = 0; /* stop device's busmaster DMA activity */ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); ret = iwl_poll_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); if (ret) IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); IWL_DEBUG_INFO(trans, "stop master\n"); return ret; } static void iwl_apm_stop(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); /* Stop device's DMA activity */ iwl_apm_stop_master(trans); /* Reset the entire device */ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); udelay(10); /* * Clear "initialization complete" bit to move adapter from * D0A* (powered-up Active) --> D0U* (Uninitialized) state. */ iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } static int iwl_nic_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; /* nic_init */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_apm_init(trans); /* Set interrupt coalescing calibration timer to default (512 usecs) */ iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); iwl_set_pwr_vmain(trans); iwl_op_mode_nic_config(trans->op_mode); #ifndef CONFIG_IWLWIFI_IDI /* Allocate the RX queue, or reset if it is already allocated */ iwl_rx_init(trans); #endif /* Allocate or reset and init all Tx and Command queues */ if (iwl_tx_init(trans)) return -ENOMEM; if (trans->cfg->base_params->shadow_reg_enable) { /* enable shadow regs in HW */ iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); } return 0; } #define HW_READY_TIMEOUT (50) /* Note: returns poll_bit return value, which is >= 0 if success */ static int iwl_set_hw_ready(struct iwl_trans *trans) { int ret; iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); /* See if we got it */ ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, HW_READY_TIMEOUT); IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); return ret; } /* Note: returns standard 0/-ERROR code */ static int iwl_prepare_card_hw(struct iwl_trans *trans) { int ret; int t = 0; IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); ret = iwl_set_hw_ready(trans); /* If the card is ready, exit 0 */ if (ret >= 0) return 0; /* If HW is not ready, prepare the conditions to check again */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); do { ret = iwl_set_hw_ready(trans); if (ret >= 0) return 0; usleep_range(200, 1000); t += 200; } while (t < 150000); return ret; } /* * ucode */ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, const struct fw_desc *section) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); dma_addr_t phy_addr = section->p_addr; u32 byte_cnt = section->len; u32 dst_addr = section->offset; int ret; trans_pcie->ucode_write_complete = false; iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); iwl_write_direct32(trans, FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), dst_addr); iwl_write_direct32(trans, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); iwl_write_direct32(trans, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), (iwl_get_dma_hi_addr(phy_addr) << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); iwl_write_direct32(trans, FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", section_num); ret = wait_event_timeout(trans_pcie->ucode_write_waitq, trans_pcie->ucode_write_complete, 5 * HZ); if (!ret) { IWL_ERR(trans, "Could not load the [%d] uCode section\n", section_num); return -ETIMEDOUT; } return 0; } static int iwl_load_given_ucode(struct iwl_trans *trans, const struct fw_img *image) { int ret = 0; int i; for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { if (!image->sec[i].p_addr) break; ret = iwl_load_section(trans, i, &image->sec[i]); if (ret) return ret; } /* Remove all resets to allow NIC to operate */ iwl_write32(trans, CSR_RESET, 0); return 0; } static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw) { int ret; bool hw_rfkill; /* This may fail if AMT took ownership of the device */ if (iwl_prepare_card_hw(trans)) { IWL_WARN(trans, "Exit HW not ready\n"); return -EIO; } iwl_enable_rfkill_int(trans); /* If platform's RF_KILL switch is NOT set to KILL */ hw_rfkill = iwl_is_rfkill_set(trans); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); if (hw_rfkill) return -ERFKILL; iwl_write32(trans, CSR_INT, 0xFFFFFFFF); ret = iwl_nic_init(trans); if (ret) { IWL_ERR(trans, "Unable to init nic\n"); return ret; } /* make sure rfkill handshake bits are cleared */ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); /* clear (again), then enable host interrupts */ iwl_write32(trans, CSR_INT, 0xFFFFFFFF); iwl_enable_interrupts(trans); /* really make sure rfkill handshake bits are cleared */ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); /* Load the given image to the HW */ return iwl_load_given_ucode(trans, fw); } /* * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask */ static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) { struct iwl_trans_pcie __maybe_unused *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); iwl_write_prph(trans, SCD_TXFACT, mask); } static void iwl_tx_start(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 a; int chan; u32 reg_val; /* make sure all queue are not stopped/used */ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); trans_pcie->scd_base_addr = iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; /* reset conext data memory */ for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; a += 4) iwl_write_targ_mem(trans, a, 0); /* reset tx status memory */ for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; a += 4) iwl_write_targ_mem(trans, a, 0); for (; a < trans_pcie->scd_base_addr + SCD_TRANS_TBL_OFFSET_QUEUE( trans->cfg->base_params->num_of_queues); a += 4) iwl_write_targ_mem(trans, a, 0); iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, trans_pcie->scd_bc_tbls.dma >> 10); /* The chain extension of the SCD doesn't work well. This feature is * enabled by default by the HW, so we need to disable it manually. */ iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, trans_pcie->cmd_fifo); /* Activate all Tx DMA/FIFO channels */ iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); /* Enable DMA channel */ for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan), FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); /* Update FH chicken bits */ reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG); iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); /* Enable L1-Active */ iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) { iwl_reset_ict(trans); iwl_tx_start(trans); } /** * iwlagn_txq_ctx_stop - Stop all Tx DMA channels */ static int iwl_trans_tx_stop(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ch, txq_id, ret; unsigned long flags; /* Turn off all Tx DMA fifos */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_trans_txq_set_sched(trans, 0); /* Stop each Tx DMA channel, and wait for it to be idle */ for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG, FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); if (ret < 0) IWL_ERR(trans, "Failing on timeout while stopping DMA channel %d [0x%08x]\n", ch, iwl_read_direct32(trans, FH_TSSR_TX_STATUS_REG)); } spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); if (!trans_pcie->txq) { IWL_WARN(trans, "Stopping tx queues that aren't allocated...\n"); return 0; } /* Unmap DMA from host system and free skb's */ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) iwl_tx_queue_unmap(trans, txq_id); return 0; } static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; /* tell the device to stop sending interrupts */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); /* device going down, Stop using ICT table */ iwl_disable_ict(trans); /* * If a HW restart happens during firmware loading, * then the firmware loading might call this function * and later it might be called again due to the * restart. So don't process again if the device is * already dead. */ if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { iwl_trans_tx_stop(trans); #ifndef CONFIG_IWLWIFI_IDI iwl_trans_rx_stop(trans); #endif /* Power-down device's busmaster DMA clocks */ iwl_write_prph(trans, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(5); } /* Make sure (redundant) we've released our request to stay awake */ iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* Stop the device, and put it in low power state */ iwl_apm_stop(trans); /* Upon stop, the APM issues an interrupt if HW RF kill is set. * Clean again the interrupt here */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); iwl_enable_rfkill_int(trans); /* wait to make sure we flush pending tasklet*/ synchronize_irq(trans_pcie->irq); tasklet_kill(&trans_pcie->irq_tasklet); cancel_work_sync(&trans_pcie->rx_replenish); /* stop and reset the on-board processor */ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); /* clear all status bits */ clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); } static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) { /* let the ucode operate on its own */ iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); iwl_disable_interrupts(trans); iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); } static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; struct iwl_cmd_meta *out_meta; struct iwl_tx_queue *txq; struct iwl_queue *q; dma_addr_t phys_addr = 0; dma_addr_t txcmd_phys; dma_addr_t scratch_phys; u16 len, firstlen, secondlen; u8 wait_write_ptr = 0; __le16 fc = hdr->frame_control; u8 hdr_len = ieee80211_hdrlen(fc); u16 __maybe_unused wifi_seq; txq = &trans_pcie->txq[txq_id]; q = &txq->q; if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) { WARN_ON_ONCE(1); return -EINVAL; } spin_lock(&txq->lock); /* In AGG mode, the index in the ring must correspond to the WiFi * sequence number. This is a HW requirements to help the SCD to parse * the BA. * Check here that the packets are in the right place on the ring. */ #ifdef CONFIG_IWLWIFI_DEBUG wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) && ((wifi_seq & 0xff) != q->write_ptr), "Q: %d WiFi Seq %d tfdNum %d", txq_id, wifi_seq, q->write_ptr); #endif /* Set up driver data for this TFD */ txq->entries[q->write_ptr].skb = skb; txq->entries[q->write_ptr].cmd = dev_cmd; dev_cmd->hdr.cmd = REPLY_TX; dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | INDEX_TO_SEQ(q->write_ptr))); /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_meta = &txq->entries[q->write_ptr].meta; /* * Use the first empty entry in this queue's command buffer array * to contain the Tx command and MAC header concatenated together * (payload data will be in another buffer). * Size of this varies, due to varying MAC header length. * If end is not dword aligned, we'll have 2 extra bytes at the end * of the MAC header (device reads on dword boundaries). * We'll tell device about this padding later. */ len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + hdr_len; firstlen = (len + 3) & ~3; /* Tell NIC about any 2-byte padding after MAC header */ if (firstlen != len) tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; /* Physical address of this Tx command's header (not MAC header!), * within command buffer array. */ txcmd_phys = dma_map_single(trans->dev, &dev_cmd->hdr, firstlen, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(trans->dev, txcmd_phys))) goto out_err; dma_unmap_addr_set(out_meta, mapping, txcmd_phys); dma_unmap_len_set(out_meta, len, firstlen); if (!ieee80211_has_morefrags(fc)) { txq->need_update = 1; } else { wait_write_ptr = 1; txq->need_update = 0; } /* Set up TFD's 2nd entry to point directly to remainder of skb, * if any (802.11 null frames have no payload). */ secondlen = skb->len - hdr_len; if (secondlen > 0) { phys_addr = dma_map_single(trans->dev, skb->data + hdr_len, secondlen, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { dma_unmap_single(trans->dev, dma_unmap_addr(out_meta, mapping), dma_unmap_len(out_meta, len), DMA_BIDIRECTIONAL); goto out_err; } } /* Attach buffers to TFD */ iwlagn_txq_attach_buf_to_tfd(trans, txq, txcmd_phys, firstlen, 1); if (secondlen > 0) iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, secondlen, 0); scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + offsetof(struct iwl_tx_cmd, scratch); /* take back ownership of DMA buffer to enable update */ dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, DMA_BIDIRECTIONAL); tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", le16_to_cpu(dev_cmd->hdr.sequence)); IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); /* Set up entry for this TFD in Tx byte-count array */ iwl_trans_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, DMA_BIDIRECTIONAL); trace_iwlwifi_dev_tx(trans->dev, &txq->tfds[txq->q.write_ptr], sizeof(struct iwl_tfd), &dev_cmd->hdr, firstlen, skb->data + hdr_len, secondlen); /* start timer if queue currently empty */ if (txq->need_update && q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_txq_update_write_ptr(trans, txq); /* * At this point the frame is "transmitted" successfully * and we will get a TX status notification eventually, * regardless of the value of ret. "ret" only indicates * whether or not we should update the write pointer. */ if (iwl_queue_space(q) < q->high_mark) { if (wait_write_ptr) { txq->need_update = 1; iwl_txq_update_write_ptr(trans, txq); } else { iwl_stop_queue(trans, txq); } } spin_unlock(&txq->lock); return 0; out_err: spin_unlock(&txq->lock); return -1; } static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int err; bool hw_rfkill; trans_pcie->inta_mask = CSR_INI_SET_MASK; if (!trans_pcie->irq_requested) { tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long)) iwl_irq_tasklet, (unsigned long)trans); iwl_alloc_isr_ict(trans); err = request_irq(trans_pcie->irq, iwl_isr_ict, IRQF_SHARED, DRV_NAME, trans); if (err) { IWL_ERR(trans, "Error allocating IRQ %d\n", trans_pcie->irq); goto error; } INIT_WORK(&trans_pcie->rx_replenish, iwl_bg_rx_replenish); trans_pcie->irq_requested = true; } err = iwl_prepare_card_hw(trans); if (err) { IWL_ERR(trans, "Error while preparing HW: %d\n", err); goto err_free_irq; } iwl_apm_init(trans); /* From now on, the op_mode will be kept updated about RF kill state */ iwl_enable_rfkill_int(trans); hw_rfkill = iwl_is_rfkill_set(trans); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); return err; err_free_irq: free_irq(trans_pcie->irq, trans); error: iwl_free_isr_ict(trans); tasklet_kill(&trans_pcie->irq_tasklet); return err; } static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, bool op_mode_leaving) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); bool hw_rfkill; unsigned long flags; iwl_apm_stop(trans); spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); iwl_write32(trans, CSR_INT, 0xFFFFFFFF); if (!op_mode_leaving) { /* * Even if we stop the HW, we still want the RF kill * interrupt */ iwl_enable_rfkill_int(trans); /* * Check again since the RF kill state may have changed while * all the interrupts were disabled, in this case we couldn't * receive the RF kill interrupt and update the state in the * op_mode. */ hw_rfkill = iwl_is_rfkill_set(trans); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); } } static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; /* n_bd is usually 256 => n_bd - 1 = 0xff */ int tfd_num = ssn & (txq->q.n_bd - 1); int freed = 0; spin_lock(&txq->lock); if (txq->q.read_ptr != tfd_num) { IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", txq_id, txq->q.read_ptr, tfd_num, ssn); freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); if (iwl_queue_space(&txq->q) > txq->q.low_mark) iwl_wake_queue(trans, txq); } spin_unlock(&txq->lock); } static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) { writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); } static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val) { writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); } static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) { return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); } static void iwl_trans_pcie_configure(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans_pcie->cmd_queue = trans_cfg->cmd_queue; trans_pcie->cmd_fifo = trans_cfg->cmd_fifo; if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) trans_pcie->n_no_reclaim_cmds = 0; else trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; if (trans_pcie->n_no_reclaim_cmds) memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, trans_pcie->n_no_reclaim_cmds * sizeof(u8)); trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k; if (trans_pcie->rx_buf_size_8k) trans_pcie->rx_page_order = get_order(8 * 1024); else trans_pcie->rx_page_order = get_order(4 * 1024); trans_pcie->wd_timeout = msecs_to_jiffies(trans_cfg->queue_watchdog_timeout); trans_pcie->command_names = trans_cfg->command_names; } void iwl_trans_pcie_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); iwl_trans_pcie_tx_free(trans); #ifndef CONFIG_IWLWIFI_IDI iwl_trans_pcie_rx_free(trans); #endif if (trans_pcie->irq_requested == true) { free_irq(trans_pcie->irq, trans); iwl_free_isr_ict(trans); } pci_disable_msi(trans_pcie->pci_dev); iounmap(trans_pcie->hw_base); pci_release_regions(trans_pcie->pci_dev); pci_disable_device(trans_pcie->pci_dev); kmem_cache_destroy(trans->dev_cmd_pool); kfree(trans); } static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (state) set_bit(STATUS_TPOWER_PMI, &trans_pcie->status); else clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); } #ifdef CONFIG_PM_SLEEP static int iwl_trans_pcie_suspend(struct iwl_trans *trans) { return 0; } static int iwl_trans_pcie_resume(struct iwl_trans *trans) { bool hw_rfkill; iwl_enable_rfkill_int(trans); hw_rfkill = iwl_is_rfkill_set(trans); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); if (!hw_rfkill) iwl_enable_interrupts(trans); return 0; } #endif /* CONFIG_PM_SLEEP */ #define IWL_FLUSH_WAIT_MS 2000 static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq; struct iwl_queue *q; int cnt; unsigned long now = jiffies; int ret = 0; /* waiting for all the tx frames complete might take a while */ for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { if (cnt == trans_pcie->cmd_queue) continue; txq = &trans_pcie->txq[cnt]; q = &txq->q; while (q->read_ptr != q->write_ptr && !time_after(jiffies, now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) msleep(1); if (q->read_ptr != q->write_ptr) { IWL_ERR(trans, "fail to flush all tx fifo queues\n"); ret = -ETIMEDOUT; break; } } return ret; } static const char *get_fh_string(int cmd) { #define IWL_CMD(x) case x: return #x switch (cmd) { IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); IWL_CMD(FH_RSCSR_CHNL0_WPTR); IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); IWL_CMD(FH_TSSR_TX_STATUS_REG); IWL_CMD(FH_TSSR_TX_ERROR_REG); default: return "UNKNOWN"; } #undef IWL_CMD } int iwl_dump_fh(struct iwl_trans *trans, char **buf) { int i; static const u32 fh_tbl[] = { FH_RSCSR_CHNL0_STTS_WPTR_REG, FH_RSCSR_CHNL0_RBDCB_BASE_REG, FH_RSCSR_CHNL0_WPTR, FH_MEM_RCSR_CHNL0_CONFIG_REG, FH_MEM_RSSR_SHARED_CTRL_REG, FH_MEM_RSSR_RX_STATUS_REG, FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, FH_TSSR_TX_STATUS_REG, FH_TSSR_TX_ERROR_REG }; #ifdef CONFIG_IWLWIFI_DEBUGFS if (buf) { int pos = 0; size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; *buf = kmalloc(bufsz, GFP_KERNEL); if (!*buf) return -ENOMEM; pos += scnprintf(*buf + pos, bufsz - pos, "FH register values:\n"); for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) pos += scnprintf(*buf + pos, bufsz - pos, " %34s: 0X%08x\n", get_fh_string(fh_tbl[i]), iwl_read_direct32(trans, fh_tbl[i])); return pos; } #endif IWL_ERR(trans, "FH register values:\n"); for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) IWL_ERR(trans, " %34s: 0X%08x\n", get_fh_string(fh_tbl[i]), iwl_read_direct32(trans, fh_tbl[i])); return 0; } static const char *get_csr_string(int cmd) { #define IWL_CMD(x) case x: return #x switch (cmd) { IWL_CMD(CSR_HW_IF_CONFIG_REG); IWL_CMD(CSR_INT_COALESCING); IWL_CMD(CSR_INT); IWL_CMD(CSR_INT_MASK); IWL_CMD(CSR_FH_INT_STATUS); IWL_CMD(CSR_GPIO_IN); IWL_CMD(CSR_RESET); IWL_CMD(CSR_GP_CNTRL); IWL_CMD(CSR_HW_REV); IWL_CMD(CSR_EEPROM_REG); IWL_CMD(CSR_EEPROM_GP); IWL_CMD(CSR_OTP_GP_REG); IWL_CMD(CSR_GIO_REG); IWL_CMD(CSR_GP_UCODE_REG); IWL_CMD(CSR_GP_DRIVER_REG); IWL_CMD(CSR_UCODE_DRV_GP1); IWL_CMD(CSR_UCODE_DRV_GP2); IWL_CMD(CSR_LED_REG); IWL_CMD(CSR_DRAM_INT_TBL_REG); IWL_CMD(CSR_GIO_CHICKEN_BITS); IWL_CMD(CSR_ANA_PLL_CFG); IWL_CMD(CSR_HW_REV_WA_REG); IWL_CMD(CSR_DBG_HPET_MEM_REG); default: return "UNKNOWN"; } #undef IWL_CMD } void iwl_dump_csr(struct iwl_trans *trans) { int i; static const u32 csr_tbl[] = { CSR_HW_IF_CONFIG_REG, CSR_INT_COALESCING, CSR_INT, CSR_INT_MASK, CSR_FH_INT_STATUS, CSR_GPIO_IN, CSR_RESET, CSR_GP_CNTRL, CSR_HW_REV, CSR_EEPROM_REG, CSR_EEPROM_GP, CSR_OTP_GP_REG, CSR_GIO_REG, CSR_GP_UCODE_REG, CSR_GP_DRIVER_REG, CSR_UCODE_DRV_GP1, CSR_UCODE_DRV_GP2, CSR_LED_REG, CSR_DRAM_INT_TBL_REG, CSR_GIO_CHICKEN_BITS, CSR_ANA_PLL_CFG, CSR_HW_REV_WA_REG, CSR_DBG_HPET_MEM_REG }; IWL_ERR(trans, "CSR values:\n"); IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " "CSR_INT_PERIODIC_REG)\n"); for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { IWL_ERR(trans, " %25s: 0X%08x\n", get_csr_string(csr_tbl[i]), iwl_read32(trans, csr_tbl[i])); } } #ifdef CONFIG_IWLWIFI_DEBUGFS /* create and remove of files */ #define DEBUGFS_ADD_FILE(name, parent, mode) do { \ if (!debugfs_create_file(#name, mode, parent, trans, \ &iwl_dbgfs_##name##_ops)) \ goto err; \ } while (0) /* file operation */ #define DEBUGFS_READ_FUNC(name) \ static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ char __user *user_buf, \ size_t count, loff_t *ppos); #define DEBUGFS_WRITE_FUNC(name) \ static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos); #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_WRITE_FILE_OPS(name) \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_READ_WRITE_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .read = iwl_dbgfs_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq; struct iwl_queue *q; char *buf; int pos = 0; int cnt; int ret; size_t bufsz; bufsz = sizeof(char) * 64 * trans->cfg->base_params->num_of_queues; if (!trans_pcie->txq) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { txq = &trans_pcie->txq[cnt]; q = &txq->q; pos += scnprintf(buf + pos, bufsz - pos, "hwq %.2d: read=%u write=%u use=%d stop=%d\n", cnt, q->read_ptr, q->write_ptr, !!test_bit(cnt, trans_pcie->queue_used), !!test_bit(cnt, trans_pcie->queue_stopped)); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; char buf[256]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", rxq->read); pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", rxq->write); pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", rxq->free_count); if (rxq->rb_stts) { pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); } else { pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: Not Allocated\n"); } return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t iwl_dbgfs_interrupt_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; int pos = 0; char *buf; int bufsz = 24 * 64; /* 24 items * 64 char per item */ ssize_t ret; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "Interrupt Statistics Report:\n"); pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", isr_stats->hw); pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", isr_stats->sw); if (isr_stats->sw || isr_stats->hw) { pos += scnprintf(buf + pos, bufsz - pos, "\tLast Restarting Code: 0x%X\n", isr_stats->err_code); } #ifdef CONFIG_IWLWIFI_DEBUG pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", isr_stats->sch); pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", isr_stats->alive); #endif pos += scnprintf(buf + pos, bufsz - pos, "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", isr_stats->ctkill); pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", isr_stats->wakeup); pos += scnprintf(buf + pos, bufsz - pos, "Rx command responses:\t\t %u\n", isr_stats->rx); pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", isr_stats->tx); pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", isr_stats->unhandled); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t iwl_dbgfs_interrupt_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; char buf[8]; int buf_size; u32 reset_flag; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &reset_flag) != 1) return -EFAULT; if (reset_flag == 0) memset(isr_stats, 0, sizeof(*isr_stats)); return count; } static ssize_t iwl_dbgfs_csr_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; char buf[8]; int buf_size; int csr; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &csr) != 1) return -EFAULT; iwl_dump_csr(trans); return count; } static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; char *buf = NULL; int pos = 0; ssize_t ret = -EFAULT; ret = pos = iwl_dump_fh(trans, &buf); if (buf) { ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); } return ret; } static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; if (!trans->op_mode) return -EAGAIN; local_bh_disable(); iwl_op_mode_nic_error(trans->op_mode); local_bh_enable(); return count; } DEBUGFS_READ_WRITE_FILE_OPS(interrupt); DEBUGFS_READ_FILE_OPS(fh_reg); DEBUGFS_READ_FILE_OPS(rx_queue); DEBUGFS_READ_FILE_OPS(tx_queue); DEBUGFS_WRITE_FILE_OPS(csr); DEBUGFS_WRITE_FILE_OPS(fw_restart); /* * Create the debugfs files and directories * */ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, struct dentry *dir) { DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); DEBUGFS_ADD_FILE(fw_restart, dir, S_IWUSR); return 0; err: IWL_ERR(trans, "failed to create the trans debugfs entry\n"); return -ENOMEM; } #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, struct dentry *dir) { return 0; } #endif /*CONFIG_IWLWIFI_DEBUGFS */ static const struct iwl_trans_ops trans_ops_pcie = { .start_hw = iwl_trans_pcie_start_hw, .stop_hw = iwl_trans_pcie_stop_hw, .fw_alive = iwl_trans_pcie_fw_alive, .start_fw = iwl_trans_pcie_start_fw, .stop_device = iwl_trans_pcie_stop_device, .wowlan_suspend = iwl_trans_pcie_wowlan_suspend, .send_cmd = iwl_trans_pcie_send_cmd, .tx = iwl_trans_pcie_tx, .reclaim = iwl_trans_pcie_reclaim, .txq_disable = iwl_trans_pcie_txq_disable, .txq_enable = iwl_trans_pcie_txq_enable, .dbgfs_register = iwl_trans_pcie_dbgfs_register, .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, #ifdef CONFIG_PM_SLEEP .suspend = iwl_trans_pcie_suspend, .resume = iwl_trans_pcie_resume, #endif .write8 = iwl_trans_pcie_write8, .write32 = iwl_trans_pcie_write32, .read32 = iwl_trans_pcie_read32, .configure = iwl_trans_pcie_configure, .set_pmi = iwl_trans_pcie_set_pmi, }; struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, const struct pci_device_id *ent, const struct iwl_cfg *cfg) { struct iwl_trans_pcie *trans_pcie; struct iwl_trans *trans; u16 pci_cmd; int err; trans = kzalloc(sizeof(struct iwl_trans) + sizeof(struct iwl_trans_pcie), GFP_KERNEL); if (WARN_ON(!trans)) return NULL; trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans->ops = &trans_ops_pcie; trans->cfg = cfg; trans_pcie->trans = trans; spin_lock_init(&trans_pcie->irq_lock); init_waitqueue_head(&trans_pcie->ucode_write_waitq); /* W/A - seems to solve weird behavior. We need to remove this if we * don't want to stay in L1 all the time. This wastes a lot of power */ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); if (pci_enable_device(pdev)) { err = -ENODEV; goto out_no_pci; } pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); if (!err) err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); if (err) { err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (!err) err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); /* both attempts failed: */ if (err) { dev_printk(KERN_ERR, &pdev->dev, "No suitable DMA available.\n"); goto out_pci_disable_device; } } err = pci_request_regions(pdev, DRV_NAME); if (err) { dev_printk(KERN_ERR, &pdev->dev, "pci_request_regions failed\n"); goto out_pci_disable_device; } trans_pcie->hw_base = pci_ioremap_bar(pdev, 0); if (!trans_pcie->hw_base) { dev_printk(KERN_ERR, &pdev->dev, "pci_ioremap_bar failed\n"); err = -ENODEV; goto out_pci_release_regions; } dev_printk(KERN_INFO, &pdev->dev, "pci_resource_len = 0x%08llx\n", (unsigned long long) pci_resource_len(pdev, 0)); dev_printk(KERN_INFO, &pdev->dev, "pci_resource_base = %p\n", trans_pcie->hw_base); dev_printk(KERN_INFO, &pdev->dev, "HW Revision ID = 0x%X\n", pdev->revision); /* We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); err = pci_enable_msi(pdev); if (err) dev_printk(KERN_ERR, &pdev->dev, "pci_enable_msi failed(0X%x)\n", err); trans->dev = &pdev->dev; trans_pcie->irq = pdev->irq; trans_pcie->pci_dev = pdev; trans->hw_rev = iwl_read32(trans, CSR_HW_REV); trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); /* TODO: Move this away, not needed if not MSI */ /* enable rfkill interrupt: hw bug w/a */ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); } /* Initialize the wait queue for commands */ init_waitqueue_head(&trans->wait_command_queue); spin_lock_init(&trans->reg_lock); snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), "iwl_cmd_pool:%s", dev_name(trans->dev)); trans->dev_cmd_headroom = 0; trans->dev_cmd_pool = kmem_cache_create(trans->dev_cmd_pool_name, sizeof(struct iwl_device_cmd) + trans->dev_cmd_headroom, sizeof(void *), SLAB_HWCACHE_ALIGN, NULL); if (!trans->dev_cmd_pool) goto out_pci_disable_msi; return trans; out_pci_disable_msi: pci_disable_msi(pdev); out_pci_release_regions: pci_release_regions(pdev); out_pci_disable_device: pci_disable_device(pdev); out_no_pci: kfree(trans); return NULL; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/rx.c0000644000175000017500000007776212026211315023673 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include "iwl-prph.h" #include "iwl-io.h" #include "internal.h" #include "iwl-op-mode.h" #ifdef CONFIG_IWLWIFI_IDI #include "iwl-amfh.h" #endif /****************************************************************************** * * RX path functions * ******************************************************************************/ /* * Rx theory of operation * * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), * each of which point to Receive Buffers to be filled by the NIC. These get * used not only for Rx frames, but for any command response or notification * from the NIC. The driver and NIC manage the Rx buffers by means * of indexes into the circular buffer. * * Rx Queue Indexes * The host/firmware share two index registers for managing the Rx buffers. * * The READ index maps to the first position that the firmware may be writing * to -- the driver can read up to (but not including) this position and get * good data. * The READ index is managed by the firmware once the card is enabled. * * The WRITE index maps to the last position the driver has read from -- the * position preceding WRITE is the last slot the firmware can place a packet. * * The queue is empty (no good data) if WRITE = READ - 1, and is full if * WRITE = READ. * * During initialization, the host sets up the READ queue position to the first * INDEX position, and WRITE to the last (READ - 1 wrapped) * * When the firmware places a packet in a buffer, it will advance the READ index * and fire the RX interrupt. The driver can then query the READ index and * process as many packets as possible, moving the WRITE index forward as it * resets the Rx queue buffers with new memory. * * The management in the driver is as follows: * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled * to replenish the iwl->rxq->rx_free. * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the * iwl->rxq is replenished and the READ INDEX is updated (updating the * 'processed' and 'read' driver indexes as well) * + A received packet is processed and handed to the kernel network stack, * detached from the iwl->rxq. The driver 'processed' index is updated. * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there * were enough free buffers and RX_STALLED is set it is cleared. * * * Driver sequence: * * iwl_rx_queue_alloc() Allocates rx_free * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls * iwl_rx_queue_restock * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx * queue, updates firmware pointers, and updates * the WRITE index. If insufficient rx_free buffers * are available, schedules iwl_rx_replenish * * -- enable interrupts -- * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the * READ INDEX, detaching the SKB from the pool. * Moves the packet buffer from queue to rx_used. * Calls iwl_rx_queue_restock to refill any empty * slots. * ... * */ /** * iwl_rx_queue_space - Return number of free slots available in queue. */ static int iwl_rx_queue_space(const struct iwl_rx_queue *q) { int s = q->read - q->write; if (s <= 0) s += RX_QUEUE_SIZE; /* keep some buffer to not confuse full and empty queue */ s -= 2; if (s < 0) s = 0; return s; } /** * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue */ void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, struct iwl_rx_queue *q) { unsigned long flags; u32 reg; spin_lock_irqsave(&q->lock, flags); if (q->need_update == 0) goto exit_unlock; if (trans->cfg->base_params->shadow_reg_enable) { /* shadow register enabled */ /* Device expects a multiple of 8 */ q->write_actual = (q->write & ~0x7); iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual); } else { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); /* If power-saving is in use, make sure device is awake */ if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup," " GP1 = 0x%x\n", reg); iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); goto exit_unlock; } q->write_actual = (q->write & ~0x7); iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual); /* Else device is assumed to be awake */ } else { /* Device expects a multiple of 8 */ q->write_actual = (q->write & ~0x7); iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual); } } q->need_update = 0; exit_unlock: spin_unlock_irqrestore(&q->lock, flags); } /** * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr */ static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) { return cpu_to_le32((u32)(dma_addr >> 8)); } /** * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool * * If there are slots in the RX queue that need to be restocked, * and we have free pre-allocated buffers, fill the ranks as much * as we can, pulling from rx_free. * * This moves the 'write' index forward to catch up with 'processed', and * also updates the memory address in the firmware to reference the new * target buffer. */ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; unsigned long flags; spin_lock_irqsave(&rxq->lock, flags); while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) { /* The overwritten rxb must be a used one */ rxb = rxq->queue[rxq->write]; BUG_ON(rxb && rxb->page); /* Get next free Rx buffer, remove from free list */ element = rxq->rx_free.next; rxb = list_entry(element, struct iwl_rx_mem_buffer, list); list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; } spin_unlock_irqrestore(&rxq->lock, flags); /* If the pre-allocated buffer pool is dropping low, schedule to * refill it */ if (rxq->free_count <= RX_LOW_WATERMARK) schedule_work(&trans_pcie->rx_replenish); /* If we've added more space for the firmware to place data, tell it. * Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7)) { spin_lock_irqsave(&rxq->lock, flags); rxq->need_update = 1; spin_unlock_irqrestore(&rxq->lock, flags); iwl_rx_queue_update_write_ptr(trans, rxq); } } /** * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free * * When moving to rx_free an SKB is allocated for the slot. * * Also restock the Rx queue via iwl_rx_queue_restock. * This is called as a scheduled work item (except for during initialization) */ static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; struct page *page; unsigned long flags; gfp_t gfp_mask = priority; while (1) { spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); return; } spin_unlock_irqrestore(&rxq->lock, flags); if (rxq->free_count > RX_LOW_WATERMARK) gfp_mask |= __GFP_NOWARN; if (trans_pcie->rx_page_order > 0) gfp_mask |= __GFP_COMP; /* Alloc a new receive buffer */ page = alloc_pages(gfp_mask, trans_pcie->rx_page_order); if (!page) { if (net_ratelimit()) IWL_DEBUG_INFO(trans, "alloc_pages failed, " "order: %d\n", trans_pcie->rx_page_order); if ((rxq->free_count <= RX_LOW_WATERMARK) && net_ratelimit()) IWL_CRIT(trans, "Failed to alloc_pages with %s." "Only %u free buffers remaining.\n", priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ return; } spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); __free_pages(page, trans_pcie->rx_page_order); return; } element = rxq->rx_used.next; rxb = list_entry(element, struct iwl_rx_mem_buffer, list); list_del(element); spin_unlock_irqrestore(&rxq->lock, flags); BUG_ON(rxb->page); rxb->page = page; /* Get physical address of the RB */ rxb->page_dma = dma_map_page(trans->dev, page, 0, PAGE_SIZE << trans_pcie->rx_page_order, DMA_FROM_DEVICE); /* dma address must be no more than 36 bits */ BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); /* and also 256 byte aligned! */ BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); spin_lock_irqsave(&rxq->lock, flags); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; spin_unlock_irqrestore(&rxq->lock, flags); } } void iwlagn_rx_replenish(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; iwlagn_rx_allocate(trans, GFP_KERNEL); spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwlagn_rx_queue_restock(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } static void iwlagn_rx_replenish_now(struct iwl_trans *trans) { iwlagn_rx_allocate(trans, GFP_ATOMIC); iwlagn_rx_queue_restock(trans); } void iwl_bg_rx_replenish(struct work_struct *data) { struct iwl_trans_pcie *trans_pcie = container_of(data, struct iwl_trans_pcie, rx_replenish); iwlagn_rx_replenish(trans_pcie->trans); } static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; unsigned long flags; bool page_stolen = false; int max_len = PAGE_SIZE << trans_pcie->rx_page_order; u32 offset = 0; if (WARN_ON(!rxb)) return; dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE); while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) { struct iwl_rx_packet *pkt; struct iwl_device_cmd *cmd; u16 sequence; bool reclaim; int index, cmd_index, err, len; struct iwl_rx_cmd_buffer rxcb = { ._offset = offset, ._page = rxb->page, ._page_stolen = false, .truesize = max_len, }; pkt = rxb_addr(&rxcb); if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) break; IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n", rxcb._offset, trans_pcie_get_cmd_string(trans_pcie, pkt->hdr.cmd), pkt->hdr.cmd); len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; len += sizeof(u32); /* account for status word */ trace_iwlwifi_dev_rx(trans->dev, pkt, len); /* Reclaim a command buffer only if this packet is a response * to a (driver-originated) command. * If the packet (e.g. Rx frame) originated from uCode, * there is no command buffer to reclaim. * Ucode should set SEQ_RX_FRAME bit if ucode-originated, * but apparently a few don't get set; catch them here. */ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); if (reclaim) { int i; for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { if (trans_pcie->no_reclaim_cmds[i] == pkt->hdr.cmd) { reclaim = false; break; } } } sequence = le16_to_cpu(pkt->hdr.sequence); index = SEQ_TO_INDEX(sequence); cmd_index = get_cmd_index(&txq->q, index); if (reclaim) { struct iwl_pcie_tx_queue_entry *ent; ent = &txq->entries[cmd_index]; cmd = ent->copy_cmd; WARN_ON_ONCE(!cmd && ent->meta.flags & CMD_WANT_HCMD); } else { cmd = NULL; } err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd); if (reclaim) { /* The original command isn't needed any more */ kfree(txq->entries[cmd_index].copy_cmd); txq->entries[cmd_index].copy_cmd = NULL; } /* * After here, we should always check rxcb._page_stolen, * if it is true then one of the handlers took the page. */ if (reclaim) { /* Invoke any callbacks, transfer the buffer to caller, * and fire off the (possibly) blocking * iwl_trans_send_cmd() * as we reclaim the driver command queue */ if (!rxcb._page_stolen) iwl_tx_cmd_complete(trans, &rxcb, err); else IWL_WARN(trans, "Claim null rxb?\n"); } page_stolen |= rxcb._page_stolen; offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN); } /* page was stolen from us -- free our reference */ if (page_stolen) { __free_pages(rxb->page, trans_pcie->rx_page_order); rxb->page = NULL; } /* Reuse the page if possible. For notification packets and * SKBs that fail to Rx correctly, add them back into the * rx_free list for reuse later. */ spin_lock_irqsave(&rxq->lock, flags); if (rxb->page != NULL) { rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0, PAGE_SIZE << trans_pcie->rx_page_order, DMA_FROM_DEVICE); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; } else list_add_tail(&rxb->list, &rxq->rx_used); spin_unlock_irqrestore(&rxq->lock, flags); } /** * iwl_rx_handle - Main entry function for receiving responses from uCode * * Uses the priv->rx_handlers callback function array to invoke * the appropriate handlers, including command responses, * frame-received notifications, and other notifications. */ static void iwl_rx_handle(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; u32 r, i; u8 fill_rx = 0; u32 count = 8; int total_empty; /* uCode's read index (stored in shared DRAM) indicates the last Rx * buffer that the driver may process (last buffer filled by ucode). */ r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; i = rxq->read; /* Rx interrupt, but nothing sent from uCode */ if (i == r) IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); /* calculate total frames need to be restock after handling RX */ total_empty = r - rxq->write_actual; if (total_empty < 0) total_empty += RX_QUEUE_SIZE; if (total_empty > (RX_QUEUE_SIZE / 2)) fill_rx = 1; while (i != r) { struct iwl_rx_mem_buffer *rxb; rxb = rxq->queue[i]; rxq->queue[i] = NULL; IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", r, i, rxb); iwl_rx_handle_rxbuf(trans, rxb); i = (i + 1) & RX_QUEUE_MASK; /* If there are a lot of unused frames, * restock the Rx queue so ucode wont assert. */ if (fill_rx) { count++; if (count >= 8) { rxq->read = i; iwlagn_rx_replenish_now(trans); count = 0; } } } /* Backtrack one entry */ rxq->read = i; if (fill_rx) iwlagn_rx_replenish_now(trans); else iwlagn_rx_queue_restock(trans); } /** * iwl_irq_handle_error - called for HW or SW error interrupt from card */ static void iwl_irq_handle_error(struct iwl_trans *trans) { /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ if (trans->cfg->internal_wimax_coex && (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(trans, APMG_PS_CTRL_REG) & APMG_PS_CTRL_VAL_RESET_REQ))) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); iwl_op_mode_wimax_active(trans->op_mode); wake_up(&trans->wait_command_queue); return; } iwl_dump_csr(trans); iwl_dump_fh(trans, NULL); iwl_op_mode_nic_error(trans->op_mode); } /* tasklet for iwlagn interrupt */ void iwl_irq_tasklet(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; u32 inta = 0; u32 handled = 0; unsigned long flags; u32 i; #ifdef CONFIG_IWLWIFI_DEBUG u32 inta_mask; #endif spin_lock_irqsave(&trans_pcie->irq_lock, flags); /* Ack/clear/reset pending uCode interrupts. * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, */ /* There is a hardware bug in the interrupt mask function that some * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if * they are disabled in the CSR_INT_MASK register. Furthermore the * ICT interrupt handling mechanism has another bug that might cause * these unmasked interrupts fail to be detected. We workaround the * hardware bugs here by ACKing all the possible interrupts so that * interrupt coalescing can still be achieved. */ iwl_write32(trans, CSR_INT, trans_pcie->inta | ~trans_pcie->inta_mask); inta = trans_pcie->inta; #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(IWL_DL_ISR)) { /* just for debug */ inta_mask = iwl_read32(trans, CSR_INT_MASK); IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", inta, inta_mask); } #endif /* saved interrupt in inta variable now we can reset trans_pcie->inta */ trans_pcie->inta = 0; spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { IWL_ERR(trans, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ iwl_disable_interrupts(trans); isr_stats->hw++; iwl_irq_handle_error(trans); handled |= CSR_INT_BIT_HW_ERR; return; } #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(trans, "Scheduler finished to transmit " "the frame/frames.\n"); isr_stats->sch++; } /* Alive notification via Rx interrupt will do the real work */ if (inta & CSR_INT_BIT_ALIVE) { IWL_DEBUG_ISR(trans, "Alive interrupt\n"); isr_stats->alive++; } } #endif /* Safely ignore these bits for debug checks below */ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); /* HW RF KILL switch toggled */ if (inta & CSR_INT_BIT_RF_KILL) { bool hw_rfkill; hw_rfkill = iwl_is_rfkill_set(trans); IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", hw_rfkill ? "disable radio" : "enable radio"); isr_stats->rfkill++; iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); handled |= CSR_INT_BIT_RF_KILL; } /* Chip got too hot and stopped itself */ if (inta & CSR_INT_BIT_CT_KILL) { IWL_ERR(trans, "Microcode CT kill error detected.\n"); isr_stats->ctkill++; handled |= CSR_INT_BIT_CT_KILL; } /* Error detected by uCode */ if (inta & CSR_INT_BIT_SW_ERR) { IWL_ERR(trans, "Microcode SW error detected. " " Restarting 0x%X.\n", inta); isr_stats->sw++; iwl_irq_handle_error(trans); handled |= CSR_INT_BIT_SW_ERR; } /* uCode wakes up after power-down sleep */ if (inta & CSR_INT_BIT_WAKEUP) { IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); iwl_rx_queue_update_write_ptr(trans, &trans_pcie->rxq); for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) iwl_txq_update_write_ptr(trans, &trans_pcie->txq[i]); isr_stats->wakeup++; handled |= CSR_INT_BIT_WAKEUP; } /* All uCode command responses, including Tx command responses, * Rx "responses" (frame-received notification), and other * notifications from uCode come through here*/ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX | CSR_INT_BIT_RX_PERIODIC)) { IWL_DEBUG_ISR(trans, "Rx interrupt\n"); if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_RX_MASK); } if (inta & CSR_INT_BIT_RX_PERIODIC) { handled |= CSR_INT_BIT_RX_PERIODIC; iwl_write32(trans, CSR_INT, CSR_INT_BIT_RX_PERIODIC); } /* Sending RX interrupt require many steps to be done in the * the device: * 1- write interrupt to current index in ICT table. * 2- dma RX frame. * 3- update RX shared data to indicate last write index. * 4- send interrupt. * This could lead to RX race, driver could receive RX interrupt * but the shared data changes does not reflect this; * periodic interrupt will detect any dangling Rx activity. */ /* Disable periodic interrupt; we use it as just a one-shot. */ iwl_write8(trans, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_DIS); #ifdef CONFIG_IWLWIFI_IDI iwl_amfh_rx_handler(); #else iwl_rx_handle(trans); #endif /* * Enable periodic interrupt in 8 msec only if we received * real RX interrupt (instead of just periodic int), to catch * any dangling Rx interrupt. If it was just the periodic * interrupt, there was no dangling Rx activity, and no need * to extend the periodic interrupt; one-shot is enough. */ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) iwl_write8(trans, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_ENA); isr_stats->rx++; } /* This "Tx" DMA channel is used only for loading uCode */ if (inta & CSR_INT_BIT_FH_TX) { iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK); IWL_DEBUG_ISR(trans, "uCode load interrupt\n"); isr_stats->tx++; handled |= CSR_INT_BIT_FH_TX; /* Wake up uCode load routine, now that load is complete */ trans_pcie->ucode_write_complete = true; wake_up(&trans_pcie->ucode_write_waitq); } if (inta & ~handled) { IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled); isr_stats->unhandled++; } if (inta & ~(trans_pcie->inta_mask)) { IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n", inta & ~trans_pcie->inta_mask); } /* Re-enable all interrupts */ /* only Re-enable if disabled by irq */ if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status)) iwl_enable_interrupts(trans); /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) iwl_enable_rfkill_int(trans); } /****************************************************************************** * * ICT functions * ******************************************************************************/ /* a device (PCI-E) page is 4096 bytes long */ #define ICT_SHIFT 12 #define ICT_SIZE (1 << ICT_SHIFT) #define ICT_COUNT (ICT_SIZE / sizeof(u32)) /* Free dram table */ void iwl_free_isr_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (trans_pcie->ict_tbl) { dma_free_coherent(trans->dev, ICT_SIZE, trans_pcie->ict_tbl, trans_pcie->ict_tbl_dma); trans_pcie->ict_tbl = NULL; trans_pcie->ict_tbl_dma = 0; } } /* * allocate dram shared table, it is an aligned memory * block of ICT_SIZE. * also reset all data related to ICT table interrupt. */ int iwl_alloc_isr_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans_pcie->ict_tbl = dma_alloc_coherent(trans->dev, ICT_SIZE, &trans_pcie->ict_tbl_dma, GFP_KERNEL); if (!trans_pcie->ict_tbl) return -ENOMEM; /* just an API sanity check ... it is guaranteed to be aligned */ if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) { iwl_free_isr_ict(trans); return -EINVAL; } IWL_DEBUG_ISR(trans, "ict dma addr %Lx\n", (unsigned long long)trans_pcie->ict_tbl_dma); IWL_DEBUG_ISR(trans, "ict vir addr %p\n", trans_pcie->ict_tbl); /* reset table and index to all 0 */ memset(trans_pcie->ict_tbl, 0, ICT_SIZE); trans_pcie->ict_index = 0; /* add periodic RX interrupt */ trans_pcie->inta_mask |= CSR_INT_BIT_RX_PERIODIC; return 0; } /* Device is going up inform it about using ICT interrupt table, * also we need to tell the driver to start using ICT interrupt. */ void iwl_reset_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 val; unsigned long flags; if (!trans_pcie->ict_tbl) return; spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); memset(trans_pcie->ict_tbl, 0, ICT_SIZE); val = trans_pcie->ict_tbl_dma >> ICT_SHIFT; val |= CSR_DRAM_INT_TBL_ENABLE; val |= CSR_DRAM_INIT_TBL_WRAP_CHECK; IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val); iwl_write32(trans, CSR_DRAM_INT_TBL_REG, val); trans_pcie->use_ict = true; trans_pcie->ict_index = 0; iwl_write32(trans, CSR_INT, trans_pcie->inta_mask); iwl_enable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } /* Device is going down disable ict interrupt usage */ void iwl_disable_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; spin_lock_irqsave(&trans_pcie->irq_lock, flags); trans_pcie->use_ict = false; spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } /* legacy (non-ICT) ISR. Assumes that trans_pcie->irq_lock is held */ static irqreturn_t iwl_isr(int irq, void *data) { struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 inta, inta_mask; #ifdef CONFIG_IWLWIFI_DEBUG u32 inta_fh; #endif lockdep_assert_held(&trans_pcie->irq_lock); trace_iwlwifi_dev_irq(trans->dev); /* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. * If we have something to service, the tasklet will re-enable ints. * If we *don't* have something, we'll re-enable before leaving here. */ inta_mask = iwl_read32(trans, CSR_INT_MASK); /* just for debug */ iwl_write32(trans, CSR_INT_MASK, 0x00000000); /* Discover which interrupts are active/pending */ inta = iwl_read32(trans, CSR_INT); /* Ignore interrupt if there's nothing in NIC to service. * This may be due to IRQ shared with another device, * or due to sporadic interrupts thrown from our NIC. */ if (!inta) { IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); goto none; } if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { /* Hardware disappeared. It might have already raised * an interrupt */ IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); return IRQ_HANDLED; } #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(IWL_DL_ISR)) { inta_fh = iwl_read32(trans, CSR_FH_INT_STATUS); IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, " "fh 0x%08x\n", inta, inta_mask, inta_fh); } #endif trans_pcie->inta |= inta; /* iwl_irq_tasklet() will service interrupts and re-enable them */ if (likely(inta)) tasklet_schedule(&trans_pcie->irq_tasklet); else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); none: /* re-enable interrupts here since we don't have anything to service. */ /* only Re-enable if disabled by irq and no schedules tasklet. */ if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); return IRQ_NONE; } /* interrupt handler using ict table, with this interrupt driver will * stop using INTA register to get device's interrupt, reading this register * is expensive, device will write interrupts in ICT dram table, increment * index then will fire interrupt to driver, driver will OR all ICT table * entries from current index up to table entry with 0 value. the result is * the interrupt we need to service, driver will set the entries back to 0 and * set index. */ irqreturn_t iwl_isr_ict(int irq, void *data) { struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie; u32 inta, inta_mask; u32 val = 0; u32 read; unsigned long flags; if (!trans) return IRQ_NONE; trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); spin_lock_irqsave(&trans_pcie->irq_lock, flags); /* dram interrupt table not set yet, * use legacy interrupt. */ if (unlikely(!trans_pcie->use_ict)) { irqreturn_t ret = iwl_isr(irq, data); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return ret; } trace_iwlwifi_dev_irq(trans->dev); /* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. * If we have something to service, the tasklet will re-enable ints. * If we *don't* have something, we'll re-enable before leaving here. */ inta_mask = iwl_read32(trans, CSR_INT_MASK); /* just for debug */ iwl_write32(trans, CSR_INT_MASK, 0x00000000); /* Ignore interrupt if there's nothing in NIC to service. * This may be due to IRQ shared with another device, * or due to sporadic interrupts thrown from our NIC. */ read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); if (!read) { IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); goto none; } /* * Collect all entries up to the first 0, starting from ict_index; * note we already read at ict_index. */ do { val |= read; IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", trans_pcie->ict_index, read); trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; trans_pcie->ict_index = iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT); read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); } while (read); /* We should not get this value, just ignore it. */ if (val == 0xffffffff) val = 0; /* * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit * (bit 15 before shifting it to 31) to clear when using interrupt * coalescing. fortunately, bits 18 and 19 stay set when this happens * so we use them to decide on the real state of the Rx bit. * In order words, bit 15 is set if bit 18 or bit 19 are set. */ if (val & 0xC0000) val |= 0x8000; inta = (0xff & val) | ((0xff00 & val) << 16); IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", inta, inta_mask, val); inta &= trans_pcie->inta_mask; trans_pcie->inta |= inta; /* iwl_irq_tasklet() will service interrupts and re-enable them */ if (likely(inta)) tasklet_schedule(&trans_pcie->irq_tasklet); else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) { /* Allow interrupt if was disabled by this handler and * no tasklet was schedules, We should not enable interrupt, * tasklet will enable it. */ iwl_enable_interrupts(trans); } spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return IRQ_HANDLED; none: /* re-enable interrupts here since we don't have anything to service. * only Re-enable if disabled by irq. */ if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return IRQ_NONE; } compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/internal.h0000644000175000017500000003311212026211315025040 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __iwl_trans_int_pcie_h__ #define __iwl_trans_int_pcie_h__ #include #include #include #include #include #include #include "iwl-fh.h" #include "iwl-csr.h" #include "iwl-trans.h" #include "iwl-debug.h" #include "iwl-io.h" #include "iwl-op-mode.h" struct iwl_host_cmd; /*This file includes the declaration that are internal to the * trans_pcie layer */ struct iwl_rx_mem_buffer { dma_addr_t page_dma; struct page *page; struct list_head list; }; /** * struct isr_statistics - interrupt statistics * */ struct isr_statistics { u32 hw; u32 sw; u32 err_code; u32 sch; u32 alive; u32 rfkill; u32 ctkill; u32 wakeup; u32 rx; u32 tx; u32 unhandled; }; /** * struct iwl_rx_queue - Rx queue * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) * @pool: * @queue: * @read: Shared index to newest available Rx buffer * @write: Shared index to oldest written Rx packet * @free_count: Number of pre-allocated buffers in rx_free * @write_actual: * @rx_free: list of free SKBs for use * @rx_used: List of Rx buffers with no SKB * @need_update: flag to indicate we need to update read/write index * @rb_stts: driver's pointer to receive buffer status * @rb_stts_dma: bus address of receive buffer status * @lock: * * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers */ struct iwl_rx_queue { __le32 *bd; dma_addr_t bd_dma; struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE]; u32 read; u32 write; u32 free_count; u32 write_actual; struct list_head rx_free; struct list_head rx_used; int need_update; struct iwl_rb_status *rb_stts; dma_addr_t rb_stts_dma; spinlock_t lock; }; struct iwl_dma_ptr { dma_addr_t dma; void *addr; size_t size; }; /** * iwl_queue_inc_wrap - increment queue index, wrap back to beginning * @index -- current index * @n_bd -- total number of entries in queue (must be power of 2) */ static inline int iwl_queue_inc_wrap(int index, int n_bd) { return ++index & (n_bd - 1); } /** * iwl_queue_dec_wrap - decrement queue index, wrap back to end * @index -- current index * @n_bd -- total number of entries in queue (must be power of 2) */ static inline int iwl_queue_dec_wrap(int index, int n_bd) { return --index & (n_bd - 1); } struct iwl_cmd_meta { /* only for SYNC commands, iff the reply skb is wanted */ struct iwl_host_cmd *source; DEFINE_DMA_UNMAP_ADDR(mapping); DEFINE_DMA_UNMAP_LEN(len); u32 flags; }; /* * Generic queue structure * * Contains common data for Rx and Tx queues. * * Note the difference between n_bd and n_window: the hardware * always assumes 256 descriptors, so n_bd is always 256 (unless * there might be HW changes in the future). For the normal TX * queues, n_window, which is the size of the software queue data * is also 256; however, for the command queue, n_window is only * 32 since we don't need so many commands pending. Since the HW * still uses 256 BDs for DMA though, n_bd stays 256. As a result, * the software buffers (in the variables @meta, @txb in struct * iwl_tx_queue) only have 32 entries, while the HW buffers (@tfds * in the same struct) have 256. * This means that we end up with the following: * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | * SW entries: | 0 | ... | 31 | * where N is a number between 0 and 7. This means that the SW * data is a window overlayed over the HW queue. */ struct iwl_queue { int n_bd; /* number of BDs in this queue */ int write_ptr; /* 1-st empty entry (index) host_w*/ int read_ptr; /* last used entry (index) host_r*/ /* use for monitoring and recovering the stuck queue */ dma_addr_t dma_addr; /* physical addr for BD's */ int n_window; /* safe queue window */ u32 id; int low_mark; /* low watermark, resume queue if free * space more than this */ int high_mark; /* high watermark, stop queue if free * space less than this */ }; #define TFD_TX_CMD_SLOTS 256 #define TFD_CMD_SLOTS 32 struct iwl_pcie_tx_queue_entry { struct iwl_device_cmd *cmd; struct iwl_device_cmd *copy_cmd; struct sk_buff *skb; struct iwl_cmd_meta meta; }; /** * struct iwl_tx_queue - Tx Queue for DMA * @q: generic Rx/Tx queue descriptor * @tfds: transmit frame descriptors (DMA memory) * @entries: transmit entries (driver state) * @lock: queue lock * @stuck_timer: timer that fires if queue gets stuck * @trans_pcie: pointer back to transport (for timer) * @need_update: indicates need to update read/write index * @active: stores if queue is active * * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame * descriptors) and required locking structures. */ struct iwl_tx_queue { struct iwl_queue q; struct iwl_tfd *tfds; struct iwl_pcie_tx_queue_entry *entries; spinlock_t lock; struct timer_list stuck_timer; struct iwl_trans_pcie *trans_pcie; u8 need_update; u8 active; }; /** * struct iwl_trans_pcie - PCIe transport specific data * @rxq: all the RX queue data * @rx_replenish: work that will be called when buffers need to be allocated * @drv - pointer to iwl_drv * @trans: pointer to the generic transport area * @irq - the irq number for the device * @irq_requested: true when the irq has been requested * @scd_base_addr: scheduler sram base address in SRAM * @scd_bc_tbls: pointer to the byte count table of the scheduler * @kw: keep warm address * @pci_dev: basic pci-network driver stuff * @hw_base: pci hardware address support * @ucode_write_complete: indicates that the ucode has been copied. * @ucode_write_waitq: wait queue for uCode load * @status - transport specific status flags * @cmd_queue - command queue number * @rx_buf_size_8k: 8 kB RX buffer size * @rx_page_order: page order for receive buffer size * @wd_timeout: queue watchdog timeout (jiffies) */ struct iwl_trans_pcie { struct iwl_rx_queue rxq; struct work_struct rx_replenish; struct iwl_trans *trans; struct iwl_drv *drv; /* INT ICT Table */ __le32 *ict_tbl; dma_addr_t ict_tbl_dma; int ict_index; u32 inta; bool use_ict; bool irq_requested; struct tasklet_struct irq_tasklet; struct isr_statistics isr_stats; unsigned int irq; spinlock_t irq_lock; u32 inta_mask; u32 scd_base_addr; struct iwl_dma_ptr scd_bc_tbls; struct iwl_dma_ptr kw; struct iwl_tx_queue *txq; unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; /* PCI bus related data */ struct pci_dev *pci_dev; void __iomem *hw_base; bool ucode_write_complete; wait_queue_head_t ucode_write_waitq; unsigned long status; u8 cmd_queue; u8 cmd_fifo; u8 n_no_reclaim_cmds; u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; bool rx_buf_size_8k; u32 rx_page_order; const char **command_names; /* queue watchdog */ unsigned long wd_timeout; }; /***************************************************** * DRIVER STATUS FUNCTIONS ******************************************************/ #define STATUS_HCMD_ACTIVE 0 #define STATUS_DEVICE_ENABLED 1 #define STATUS_TPOWER_PMI 2 #define STATUS_INT_ENABLED 3 #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) static inline struct iwl_trans * iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) { return container_of((void *)trans_pcie, struct iwl_trans, trans_specific); } struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, const struct pci_device_id *ent, const struct iwl_cfg *cfg); void iwl_trans_pcie_free(struct iwl_trans *trans); /***************************************************** * RX ******************************************************/ void iwl_bg_rx_replenish(struct work_struct *data); void iwl_irq_tasklet(struct iwl_trans *trans); void iwlagn_rx_replenish(struct iwl_trans *trans); void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, struct iwl_rx_queue *q); /***************************************************** * ICT ******************************************************/ void iwl_reset_ict(struct iwl_trans *trans); void iwl_disable_ict(struct iwl_trans *trans); int iwl_alloc_isr_ict(struct iwl_trans *trans); void iwl_free_isr_ict(struct iwl_trans *trans); irqreturn_t iwl_isr_ict(int irq, void *data); /***************************************************** * TX / HCMD ******************************************************/ void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq); int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset); int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id); int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, int handler_status); void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq, u16 byte_cnt); void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn); void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, enum dma_data_direction dma_dir); int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, struct sk_buff_head *skbs); int iwl_queue_space(const struct iwl_queue *q); /***************************************************** * Error handling ******************************************************/ int iwl_dump_fh(struct iwl_trans *trans, char **buf); void iwl_dump_csr(struct iwl_trans *trans); /***************************************************** * Helpers ******************************************************/ static inline void iwl_disable_interrupts(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); /* disable interrupts from uCode/NIC to host */ iwl_write32(trans, CSR_INT_MASK, 0x00000000); /* acknowledge/clear/reset any interrupts still pending * from uCode or flow handler (Rx/Tx DMA) */ iwl_write32(trans, CSR_INT, 0xffffffff); iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff); IWL_DEBUG_ISR(trans, "Disabled interrupts\n"); } static inline void iwl_enable_interrupts(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); set_bit(STATUS_INT_ENABLED, &trans_pcie->status); iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); } static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) { IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n"); iwl_write32(trans, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); } static inline void iwl_wake_queue(struct iwl_trans *trans, struct iwl_tx_queue *txq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) { IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id); iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id); } } static inline void iwl_stop_queue(struct iwl_trans *trans, struct iwl_tx_queue *txq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) { iwl_op_mode_queue_full(trans->op_mode, txq->q.id); IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id); } else IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n", txq->q.id); } static inline int iwl_queue_used(const struct iwl_queue *q, int i) { return q->write_ptr >= q->read_ptr ? (i >= q->read_ptr && i < q->write_ptr) : !(i < q->read_ptr && i >= q->write_ptr); } static inline u8 get_cmd_index(struct iwl_queue *q, u32 index) { return index & (q->n_window - 1); } static inline const char * trans_pcie_get_cmd_string(struct iwl_trans_pcie *trans_pcie, u8 cmd) { if (!trans_pcie->command_names || !trans_pcie->command_names[cmd]) return "UNKNOWN"; return trans_pcie->command_names[cmd]; } static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) { return !(iwl_read32(trans, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); } #endif /* __iwl_trans_int_pcie_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/cfg.h0000644000175000017500000001154012026211315023764 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __iwl_pci_h__ #define __iwl_pci_h__ /* * This file declares the config structures for all devices. */ extern const struct iwl_cfg iwl5300_agn_cfg; extern const struct iwl_cfg iwl5100_agn_cfg; extern const struct iwl_cfg iwl5350_agn_cfg; extern const struct iwl_cfg iwl5100_bgn_cfg; extern const struct iwl_cfg iwl5100_abg_cfg; extern const struct iwl_cfg iwl5150_agn_cfg; extern const struct iwl_cfg iwl5150_abg_cfg; extern const struct iwl_cfg iwl6005_2agn_cfg; extern const struct iwl_cfg iwl6005_2abg_cfg; extern const struct iwl_cfg iwl6005_2bg_cfg; extern const struct iwl_cfg iwl6005_2agn_sff_cfg; extern const struct iwl_cfg iwl6005_2agn_d_cfg; extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; extern const struct iwl_cfg iwl1030_bgn_cfg; extern const struct iwl_cfg iwl1030_bg_cfg; extern const struct iwl_cfg iwl6030_2agn_cfg; extern const struct iwl_cfg iwl6030_2abg_cfg; extern const struct iwl_cfg iwl6030_2bgn_cfg; extern const struct iwl_cfg iwl6030_2bg_cfg; extern const struct iwl_cfg iwl6000i_2agn_cfg; extern const struct iwl_cfg iwl6000i_2abg_cfg; extern const struct iwl_cfg iwl6000i_2bg_cfg; extern const struct iwl_cfg iwl6000_3agn_cfg; extern const struct iwl_cfg iwl6050_2agn_cfg; extern const struct iwl_cfg iwl6050_2abg_cfg; extern const struct iwl_cfg iwl6150_bgn_cfg; extern const struct iwl_cfg iwl6150_bg_cfg; extern const struct iwl_cfg iwl1000_bgn_cfg; extern const struct iwl_cfg iwl1000_bg_cfg; extern const struct iwl_cfg iwl100_bgn_cfg; extern const struct iwl_cfg iwl100_bg_cfg; extern const struct iwl_cfg iwl130_bgn_cfg; extern const struct iwl_cfg iwl130_bg_cfg; extern const struct iwl_cfg iwl2000_2bgn_cfg; extern const struct iwl_cfg iwl2000_2bgn_d_cfg; extern const struct iwl_cfg iwl2030_2bgn_cfg; extern const struct iwl_cfg iwl6035_2agn_cfg; extern const struct iwl_cfg iwl105_bgn_cfg; extern const struct iwl_cfg iwl105_bgn_d_cfg; extern const struct iwl_cfg iwl135_bgn_cfg; #endif /* __iwl_pci_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/6000.c0000644000175000017500000003172112026211315023610 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include "iwl-config.h" #include "iwl-agn-hw.h" #include "cfg.h" #include "dvm/commands.h" /* needed for BT for now */ /* Highest firmware API version supported */ #define IWL6000_UCODE_API_MAX 6 #define IWL6050_UCODE_API_MAX 5 #define IWL6000G2_UCODE_API_MAX 6 #define IWL6035_UCODE_API_MAX 6 /* Oldest version we won't warn about */ #define IWL6000_UCODE_API_OK 4 #define IWL6000G2_UCODE_API_OK 5 #define IWL6050_UCODE_API_OK 5 #define IWL6000G2B_UCODE_API_OK 6 #define IWL6035_UCODE_API_OK 6 /* Lowest firmware API version supported */ #define IWL6000_UCODE_API_MIN 4 #define IWL6050_UCODE_API_MIN 4 #define IWL6000G2_UCODE_API_MIN 5 #define IWL6035_UCODE_API_MIN 6 /* EEPROM versions */ #define EEPROM_6000_TX_POWER_VERSION (4) #define EEPROM_6000_EEPROM_VERSION (0x423) #define EEPROM_6050_TX_POWER_VERSION (4) #define EEPROM_6050_EEPROM_VERSION (0x532) #define EEPROM_6150_TX_POWER_VERSION (6) #define EEPROM_6150_EEPROM_VERSION (0x553) #define EEPROM_6005_TX_POWER_VERSION (6) #define EEPROM_6005_EEPROM_VERSION (0x709) #define EEPROM_6030_TX_POWER_VERSION (6) #define EEPROM_6030_EEPROM_VERSION (0x709) #define EEPROM_6035_TX_POWER_VERSION (6) #define EEPROM_6035_EEPROM_VERSION (0x753) #define IWL6000_FW_PRE "iwlwifi-6000-" #define IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE __stringify(api) ".ucode" #define IWL6050_FW_PRE "iwlwifi-6050-" #define IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE __stringify(api) ".ucode" #define IWL6005_FW_PRE "iwlwifi-6000g2a-" #define IWL6005_MODULE_FIRMWARE(api) IWL6005_FW_PRE __stringify(api) ".ucode" #define IWL6030_FW_PRE "iwlwifi-6000g2b-" #define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode" static const struct iwl_base_params iwl6000_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 51, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ }; static const struct iwl_base_params iwl6050_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .max_ll_items = OTP_MAX_LL_ITEMS_6x50, .shadow_ram_support = true, .led_compensation = 51, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1500, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 1024, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ }; static const struct iwl_base_params iwl6000_g2_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 57, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ }; static const struct iwl_ht_params iwl6000_ht_params = { .ht_greenfield_support = true, .use_rts_for_aggregation = true, /* use rts/cts protection */ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; static const struct iwl_bt_params iwl6000_bt_params = { /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ .advanced_bt_coexist = true, .agg_time_limit = BT_AGG_THRESHOLD_DEF, .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, .bt_sco_disable = true, }; static const struct iwl_eeprom_params iwl6000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, EEPROM_REG_BAND_2_CHANNELS, EEPROM_REG_BAND_3_CHANNELS, EEPROM_REG_BAND_4_CHANNELS, EEPROM_REG_BAND_5_CHANNELS, EEPROM_6000_REG_BAND_24_HT40_CHANNELS, EEPROM_REG_BAND_52_HT40_CHANNELS }, .enhanced_txpower = true, }; #define IWL_DEVICE_6005 \ .fw_name_pre = IWL6005_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6005, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_6005_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6005_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", IWL_DEVICE_6005, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6005_2abg_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG", IWL_DEVICE_6005, }; const struct iwl_cfg iwl6005_2bg_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205 BG", IWL_DEVICE_6005, }; const struct iwl_cfg iwl6005_2agn_sff_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN", IWL_DEVICE_6005, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6005_2agn_d_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN", IWL_DEVICE_6005, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6005_2agn_mow1_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN", IWL_DEVICE_6005, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN", IWL_DEVICE_6005, .ht_params = &iwl6000_ht_params, }; #define IWL_DEVICE_6030 \ .fw_name_pre = IWL6030_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6030, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_6030_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true \ const struct iwl_cfg iwl6030_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", IWL_DEVICE_6030, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6030_2abg_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG", IWL_DEVICE_6030, }; const struct iwl_cfg iwl6030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN", IWL_DEVICE_6030, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6030_2bg_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 BG", IWL_DEVICE_6030, }; #define IWL_DEVICE_6035 \ .fw_name_pre = IWL6030_FW_PRE, \ .ucode_api_max = IWL6035_UCODE_API_MAX, \ .ucode_api_ok = IWL6035_UCODE_API_OK, \ .ucode_api_min = IWL6035_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6030, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_6030_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true const struct iwl_cfg iwl6035_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", IWL_DEVICE_6035, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl1030_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN", IWL_DEVICE_6030, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl1030_bg_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1030 BG", IWL_DEVICE_6030, }; const struct iwl_cfg iwl130_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 130 BGN", IWL_DEVICE_6030, .ht_params = &iwl6000_ht_params, .rx_with_siso_diversity = true, }; const struct iwl_cfg iwl130_bg_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 130 BG", IWL_DEVICE_6030, .rx_with_siso_diversity = true, }; /* * "i": Internal configuration, use internal Power Amplifier */ #define IWL_DEVICE_6000i \ .fw_name_pre = IWL6000_FW_PRE, \ .ucode_api_max = IWL6000_UCODE_API_MAX, \ .ucode_api_ok = IWL6000_UCODE_API_OK, \ .ucode_api_min = IWL6000_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6000i, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \ .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \ .eeprom_ver = EEPROM_6000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ .base_params = &iwl6000_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK const struct iwl_cfg iwl6000i_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", IWL_DEVICE_6000i, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6000i_2abg_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", IWL_DEVICE_6000i, }; const struct iwl_cfg iwl6000i_2bg_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", IWL_DEVICE_6000i, }; #define IWL_DEVICE_6050 \ .fw_name_pre = IWL6050_FW_PRE, \ .ucode_api_max = IWL6050_UCODE_API_MAX, \ .ucode_api_min = IWL6050_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6050, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \ .eeprom_ver = EEPROM_6050_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ .base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true const struct iwl_cfg iwl6050_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", IWL_DEVICE_6050, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6050_2abg_cfg = { .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", IWL_DEVICE_6050, }; #define IWL_DEVICE_6150 \ .fw_name_pre = IWL6050_FW_PRE, \ .ucode_api_max = IWL6050_UCODE_API_MAX, \ .ucode_api_min = IWL6050_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6150, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_6150_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ .base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true const struct iwl_cfg iwl6150_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", IWL_DEVICE_6150, .ht_params = &iwl6000_ht_params, }; const struct iwl_cfg iwl6150_bg_cfg = { .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG", IWL_DEVICE_6150, }; const struct iwl_cfg iwl6000_3agn_cfg = { .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_ok = IWL6000_UCODE_API_OK, .ucode_api_min = IWL6000_UCODE_API_MIN, .device_family = IWL_DEVICE_FAMILY_6000, .max_inst_size = IWL60_RTC_INST_SIZE, .max_data_size = IWL60_RTC_DATA_SIZE, .eeprom_ver = EEPROM_6000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, .base_params = &iwl6000_base_params, .eeprom_params = &iwl6000_eeprom_params, .ht_params = &iwl6000_ht_params, .led_mode = IWL_LED_BLINK, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK)); MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK)); MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK)); MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK)); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/5000.c0000644000175000017500000001360012026211315023603 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include "iwl-config.h" #include "iwl-agn-hw.h" #include "iwl-csr.h" #include "cfg.h" /* Highest firmware API version supported */ #define IWL5000_UCODE_API_MAX 5 #define IWL5150_UCODE_API_MAX 2 /* Oldest version we won't warn about */ #define IWL5000_UCODE_API_OK 5 #define IWL5150_UCODE_API_OK 2 /* Lowest firmware API version supported */ #define IWL5000_UCODE_API_MIN 1 #define IWL5150_UCODE_API_MIN 1 /* EEPROM versions */ #define EEPROM_5000_TX_POWER_VERSION (4) #define EEPROM_5000_EEPROM_VERSION (0x11A) #define EEPROM_5050_TX_POWER_VERSION (4) #define EEPROM_5050_EEPROM_VERSION (0x21E) #define IWL5000_FW_PRE "iwlwifi-5000-" #define IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE __stringify(api) ".ucode" #define IWL5150_FW_PRE "iwlwifi-5150-" #define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE __stringify(api) ".ucode" static const struct iwl_base_params iwl5000_base_params = { .eeprom_size = IWLAGN_EEPROM_IMG_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, .led_compensation = 51, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 512, .no_idle_support = true, }; static const struct iwl_ht_params iwl5000_ht_params = { .ht_greenfield_support = true, .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; static const struct iwl_eeprom_params iwl5000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, EEPROM_REG_BAND_2_CHANNELS, EEPROM_REG_BAND_3_CHANNELS, EEPROM_REG_BAND_4_CHANNELS, EEPROM_REG_BAND_5_CHANNELS, EEPROM_REG_BAND_24_HT40_CHANNELS, EEPROM_REG_BAND_52_HT40_CHANNELS }, }; #define IWL_DEVICE_5000 \ .fw_name_pre = IWL5000_FW_PRE, \ .ucode_api_max = IWL5000_UCODE_API_MAX, \ .ucode_api_ok = IWL5000_UCODE_API_OK, \ .ucode_api_min = IWL5000_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_5000, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_5000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK const struct iwl_cfg iwl5300_agn_cfg = { .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", IWL_DEVICE_5000, /* at least EEPROM 0x11A has wrong info */ .valid_tx_ant = ANT_ABC, /* .cfg overwrite */ .valid_rx_ant = ANT_ABC, /* .cfg overwrite */ .ht_params = &iwl5000_ht_params, }; const struct iwl_cfg iwl5100_bgn_cfg = { .name = "Intel(R) WiFi Link 5100 BGN", IWL_DEVICE_5000, .valid_tx_ant = ANT_B, /* .cfg overwrite */ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ .ht_params = &iwl5000_ht_params, }; const struct iwl_cfg iwl5100_abg_cfg = { .name = "Intel(R) WiFi Link 5100 ABG", IWL_DEVICE_5000, .valid_tx_ant = ANT_B, /* .cfg overwrite */ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ }; const struct iwl_cfg iwl5100_agn_cfg = { .name = "Intel(R) WiFi Link 5100 AGN", IWL_DEVICE_5000, .valid_tx_ant = ANT_B, /* .cfg overwrite */ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ .ht_params = &iwl5000_ht_params, }; const struct iwl_cfg iwl5350_agn_cfg = { .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_ok = IWL5000_UCODE_API_OK, .ucode_api_min = IWL5000_UCODE_API_MIN, .device_family = IWL_DEVICE_FAMILY_5000, .max_inst_size = IWLAGN_RTC_INST_SIZE, .max_data_size = IWLAGN_RTC_DATA_SIZE, .eeprom_ver = EEPROM_5050_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, .base_params = &iwl5000_base_params, .eeprom_params = &iwl5000_eeprom_params, .ht_params = &iwl5000_ht_params, .led_mode = IWL_LED_BLINK, .internal_wimax_coex = true, }; #define IWL_DEVICE_5150 \ .fw_name_pre = IWL5150_FW_PRE, \ .ucode_api_max = IWL5150_UCODE_API_MAX, \ .ucode_api_ok = IWL5150_UCODE_API_OK, \ .ucode_api_min = IWL5150_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_5150, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_5050_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ .no_xtal_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true const struct iwl_cfg iwl5150_agn_cfg = { .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", IWL_DEVICE_5150, .ht_params = &iwl5000_ht_params, }; const struct iwl_cfg iwl5150_abg_cfg = { .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", IWL_DEVICE_5150, }; MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK)); MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK)); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/2000.c0000644000175000017500000002004012026211315023574 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include "iwl-config.h" #include "iwl-agn-hw.h" #include "cfg.h" #include "dvm/commands.h" /* needed for BT for now */ /* Highest firmware API version supported */ #define IWL2030_UCODE_API_MAX 6 #define IWL2000_UCODE_API_MAX 6 #define IWL105_UCODE_API_MAX 6 #define IWL135_UCODE_API_MAX 6 /* Oldest version we won't warn about */ #define IWL2030_UCODE_API_OK 6 #define IWL2000_UCODE_API_OK 6 #define IWL105_UCODE_API_OK 6 #define IWL135_UCODE_API_OK 6 /* Lowest firmware API version supported */ #define IWL2030_UCODE_API_MIN 5 #define IWL2000_UCODE_API_MIN 5 #define IWL105_UCODE_API_MIN 5 #define IWL135_UCODE_API_MIN 5 /* EEPROM version */ #define EEPROM_2000_TX_POWER_VERSION (6) #define EEPROM_2000_EEPROM_VERSION (0x805) #define IWL2030_FW_PRE "iwlwifi-2030-" #define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode" #define IWL2000_FW_PRE "iwlwifi-2000-" #define IWL2000_MODULE_FIRMWARE(api) IWL2000_FW_PRE __stringify(api) ".ucode" #define IWL105_FW_PRE "iwlwifi-105-" #define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode" #define IWL135_FW_PRE "iwlwifi-135-" #define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode" static const struct iwl_base_params iwl2000_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 51, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .hd_v2 = true, }; static const struct iwl_base_params iwl2030_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 57, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ .hd_v2 = true, }; static const struct iwl_ht_params iwl2000_ht_params = { .ht_greenfield_support = true, .use_rts_for_aggregation = true, /* use rts/cts protection */ .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; static const struct iwl_bt_params iwl2030_bt_params = { /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ .advanced_bt_coexist = true, .agg_time_limit = BT_AGG_THRESHOLD_DEF, .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, .bt_sco_disable = true, .bt_session_2 = true, }; static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, EEPROM_REG_BAND_2_CHANNELS, EEPROM_REG_BAND_3_CHANNELS, EEPROM_REG_BAND_4_CHANNELS, EEPROM_REG_BAND_5_CHANNELS, EEPROM_6000_REG_BAND_24_HT40_CHANNELS, EEPROM_REGULATORY_BAND_NO_HT40, }, .enhanced_txpower = true, }; #define IWL_DEVICE_2000 \ .fw_name_pre = IWL2000_FW_PRE, \ .ucode_api_max = IWL2000_UCODE_API_MAX, \ .ucode_api_ok = IWL2000_UCODE_API_OK, \ .ucode_api_min = IWL2000_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_2000, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl2000_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", IWL_DEVICE_2000, .ht_params = &iwl2000_ht_params, }; const struct iwl_cfg iwl2000_2bgn_d_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN", IWL_DEVICE_2000, .ht_params = &iwl2000_ht_params, }; #define IWL_DEVICE_2030 \ .fw_name_pre = IWL2030_FW_PRE, \ .ucode_api_max = IWL2030_UCODE_API_MAX, \ .ucode_api_ok = IWL2030_UCODE_API_OK, \ .ucode_api_min = IWL2030_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_2030, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true const struct iwl_cfg iwl2030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", IWL_DEVICE_2030, .ht_params = &iwl2000_ht_params, }; #define IWL_DEVICE_105 \ .fw_name_pre = IWL105_FW_PRE, \ .ucode_api_max = IWL105_UCODE_API_MAX, \ .ucode_api_ok = IWL105_UCODE_API_OK, \ .ucode_api_min = IWL105_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_105, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl105_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", IWL_DEVICE_105, .ht_params = &iwl2000_ht_params, }; const struct iwl_cfg iwl105_bgn_d_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 105D BGN", IWL_DEVICE_105, .ht_params = &iwl2000_ht_params, }; #define IWL_DEVICE_135 \ .fw_name_pre = IWL135_FW_PRE, \ .ucode_api_max = IWL135_UCODE_API_MAX, \ .ucode_api_ok = IWL135_UCODE_API_OK, \ .ucode_api_min = IWL135_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_135, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl135_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", IWL_DEVICE_135, .ht_params = &iwl2000_ht_params, }; MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK)); MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK)); MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK)); MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK)); compat-drivers-2012-09-18/drivers/net/wireless/iwlwifi/pcie/1000.c0000644000175000017500000001113212026211315023575 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include "iwl-config.h" #include "iwl-csr.h" #include "iwl-agn-hw.h" #include "cfg.h" /* Highest firmware API version supported */ #define IWL1000_UCODE_API_MAX 5 #define IWL100_UCODE_API_MAX 5 /* Oldest version we won't warn about */ #define IWL1000_UCODE_API_OK 5 #define IWL100_UCODE_API_OK 5 /* Lowest firmware API version supported */ #define IWL1000_UCODE_API_MIN 1 #define IWL100_UCODE_API_MIN 5 /* EEPROM version */ #define EEPROM_1000_TX_POWER_VERSION (4) #define EEPROM_1000_EEPROM_VERSION (0x15C) #define IWL1000_FW_PRE "iwlwifi-1000-" #define IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE __stringify(api) ".ucode" #define IWL100_FW_PRE "iwlwifi-100-" #define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE __stringify(api) ".ucode" static const struct iwl_base_params iwl1000_base_params = { .num_of_queues = IWLAGN_NUM_QUEUES, .eeprom_size = OTP_LOW_IMAGE_SIZE, .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, .max_ll_items = OTP_MAX_LL_ITEMS_1000, .shadow_ram_support = false, .led_compensation = 51, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 128, }; static const struct iwl_ht_params iwl1000_ht_params = { .ht_greenfield_support = true, .use_rts_for_aggregation = true, /* use rts/cts protection */ .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; static const struct iwl_eeprom_params iwl1000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, EEPROM_REG_BAND_2_CHANNELS, EEPROM_REG_BAND_3_CHANNELS, EEPROM_REG_BAND_4_CHANNELS, EEPROM_REG_BAND_5_CHANNELS, EEPROM_REG_BAND_24_HT40_CHANNELS, EEPROM_REGULATORY_BAND_NO_HT40, } }; #define IWL_DEVICE_1000 \ .fw_name_pre = IWL1000_FW_PRE, \ .ucode_api_max = IWL1000_UCODE_API_MAX, \ .ucode_api_ok = IWL1000_UCODE_API_OK, \ .ucode_api_min = IWL1000_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_1000, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ .base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_BLINK const struct iwl_cfg iwl1000_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", IWL_DEVICE_1000, .ht_params = &iwl1000_ht_params, }; const struct iwl_cfg iwl1000_bg_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", IWL_DEVICE_1000, }; #define IWL_DEVICE_100 \ .fw_name_pre = IWL100_FW_PRE, \ .ucode_api_max = IWL100_UCODE_API_MAX, \ .ucode_api_ok = IWL100_UCODE_API_OK, \ .ucode_api_min = IWL100_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_100, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ .base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl100_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", IWL_DEVICE_100, .ht_params = &iwl1000_ht_params, }; const struct iwl_cfg iwl100_bg_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 100 BG", IWL_DEVICE_100, }; MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK)); MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK)); compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/0000755000175000017500000000000012026211315022265 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/4965-mac.c0000644000175000017500000056016112026211315023607 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRV_NAME "iwl4965" #include "common.h" #include "4965.h" /****************************************************************************** * * module boiler plate * ******************************************************************************/ /* * module name, copyright, version, etc. */ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux" #ifdef CONFIG_IWLEGACY_DEBUG #define VD "d" #else #define VD #endif #define DRV_VERSION IWLWIFI_VERSION VD MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); MODULE_ALIAS("iwl4965"); void il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status) { if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { IL_ERR("Tx flush command to flush out all frames\n"); if (!test_bit(S_EXIT_PENDING, &il->status)) queue_work(il->workqueue, &il->tx_flush); } } /* * EEPROM */ struct il_mod_params il4965_mod_params = { .amsdu_size_8K = 1, .restart_fw = 1, /* the rest are 0 by default */ }; void il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq) { unsigned long flags; int i; spin_lock_irqsave(&rxq->lock, flags); INIT_LIST_HEAD(&rxq->rx_free); INIT_LIST_HEAD(&rxq->rx_used); /* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { /* In the reset function, these buffers may have been allocated * to an SKB, so we need to unmap and free potential storage */ if (rxq->pool[i].page != NULL) { pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); __il_free_pages(il, rxq->pool[i].page); rxq->pool[i].page = NULL; } list_add_tail(&rxq->pool[i].list, &rxq->rx_used); } for (i = 0; i < RX_QUEUE_SIZE; i++) rxq->queue[i] = NULL; /* Set us so that we have processed and used all buffers, but have * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; rxq->write_actual = 0; rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); } int il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq) { u32 rb_size; const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ u32 rb_timeout = 0; if (il->cfg->mod_params->amsdu_size_8K) rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; else rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; /* Stop Rx DMA */ il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); /* Reset driver's Rx queue write idx */ il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); /* Tell device where to find RBD circular buffer in DRAM */ il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8)); /* Tell device where in DRAM to update its Rx status */ il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4); /* Enable Rx DMA * Direct rx interrupts to hosts * Rx buffer size 4 or 8k * RB timeout 0x10 * 256 RBDs */ il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | rb_size | (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); /* Set interrupt coalescing timer to default (2048 usecs) */ il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF); return 0; } static void il4965_set_pwr_vmain(struct il_priv *il) { /* * (for documentation purposes) * to set power to V_AUX, do: if (pci_pme_capable(il->pci_dev, PCI_D3cold)) il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VAUX, ~APMG_PS_CTRL_MSK_PWR_SRC); */ il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, ~APMG_PS_CTRL_MSK_PWR_SRC); } int il4965_hw_nic_init(struct il_priv *il) { unsigned long flags; struct il_rx_queue *rxq = &il->rxq; int ret; spin_lock_irqsave(&il->lock, flags); il_apm_init(il); /* Set interrupt coalescing calibration timer to default (512 usecs) */ il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF); spin_unlock_irqrestore(&il->lock, flags); il4965_set_pwr_vmain(il); il4965_nic_config(il); /* Allocate the RX queue, or reset if it is already allocated */ if (!rxq->bd) { ret = il_rx_queue_alloc(il); if (ret) { IL_ERR("Unable to initialize Rx queue\n"); return -ENOMEM; } } else il4965_rx_queue_reset(il, rxq); il4965_rx_replenish(il); il4965_rx_init(il, rxq); spin_lock_irqsave(&il->lock, flags); rxq->need_update = 1; il_rx_queue_update_write_ptr(il, rxq); spin_unlock_irqrestore(&il->lock, flags); /* Allocate or reset and init all Tx and Command queues */ if (!il->txq) { ret = il4965_txq_ctx_alloc(il); if (ret) return ret; } else il4965_txq_ctx_reset(il); set_bit(S_INIT, &il->status); return 0; } /** * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr */ static inline __le32 il4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr) { return cpu_to_le32((u32) (dma_addr >> 8)); } /** * il4965_rx_queue_restock - refill RX queue from pre-allocated pool * * If there are slots in the RX queue that need to be restocked, * and we have free pre-allocated buffers, fill the ranks as much * as we can, pulling from rx_free. * * This moves the 'write' idx forward to catch up with 'processed', and * also updates the memory address in the firmware to reference the new * target buffer. */ void il4965_rx_queue_restock(struct il_priv *il) { struct il_rx_queue *rxq = &il->rxq; struct list_head *element; struct il_rx_buf *rxb; unsigned long flags; spin_lock_irqsave(&rxq->lock, flags); while (il_rx_queue_space(rxq) > 0 && rxq->free_count) { /* The overwritten rxb must be a used one */ rxb = rxq->queue[rxq->write]; BUG_ON(rxb && rxb->page); /* Get next free Rx buffer, remove from free list */ element = rxq->rx_free.next; rxb = list_entry(element, struct il_rx_buf, list); list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ rxq->bd[rxq->write] = il4965_dma_addr2rbd_ptr(il, rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; } spin_unlock_irqrestore(&rxq->lock, flags); /* If the pre-allocated buffer pool is dropping low, schedule to * refill it */ if (rxq->free_count <= RX_LOW_WATERMARK) queue_work(il->workqueue, &il->rx_replenish); /* If we've added more space for the firmware to place data, tell it. * Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7)) { spin_lock_irqsave(&rxq->lock, flags); rxq->need_update = 1; spin_unlock_irqrestore(&rxq->lock, flags); il_rx_queue_update_write_ptr(il, rxq); } } /** * il4965_rx_replenish - Move all used packet from rx_used to rx_free * * When moving to rx_free an SKB is allocated for the slot. * * Also restock the Rx queue via il_rx_queue_restock. * This is called as a scheduled work item (except for during initialization) */ static void il4965_rx_allocate(struct il_priv *il, gfp_t priority) { struct il_rx_queue *rxq = &il->rxq; struct list_head *element; struct il_rx_buf *rxb; struct page *page; unsigned long flags; gfp_t gfp_mask = priority; while (1) { spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); return; } spin_unlock_irqrestore(&rxq->lock, flags); if (rxq->free_count > RX_LOW_WATERMARK) gfp_mask |= __GFP_NOWARN; if (il->hw_params.rx_page_order > 0) gfp_mask |= __GFP_COMP; /* Alloc a new receive buffer */ page = alloc_pages(gfp_mask, il->hw_params.rx_page_order); if (!page) { if (net_ratelimit()) D_INFO("alloc_pages failed, " "order: %d\n", il->hw_params.rx_page_order); if (rxq->free_count <= RX_LOW_WATERMARK && net_ratelimit()) IL_ERR("Failed to alloc_pages with %s. " "Only %u free buffers remaining.\n", priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ return; } spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); __free_pages(page, il->hw_params.rx_page_order); return; } element = rxq->rx_used.next; rxb = list_entry(element, struct il_rx_buf, list); list_del(element); spin_unlock_irqrestore(&rxq->lock, flags); BUG_ON(rxb->page); rxb->page = page; /* Get physical address of the RB */ rxb->page_dma = pci_map_page(il->pci_dev, page, 0, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); /* dma address must be no more than 36 bits */ BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); /* and also 256 byte aligned! */ BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); spin_lock_irqsave(&rxq->lock, flags); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; il->alloc_rxb_page++; spin_unlock_irqrestore(&rxq->lock, flags); } } void il4965_rx_replenish(struct il_priv *il) { unsigned long flags; il4965_rx_allocate(il, GFP_KERNEL); spin_lock_irqsave(&il->lock, flags); il4965_rx_queue_restock(il); spin_unlock_irqrestore(&il->lock, flags); } void il4965_rx_replenish_now(struct il_priv *il) { il4965_rx_allocate(il, GFP_ATOMIC); il4965_rx_queue_restock(il); } /* Assumes that the skb field of the buffers in 'pool' is kept accurate. * If an SKB has been detached, the POOL needs to have its SKB set to NULL * This free routine walks the list of POOL entries and if SKB is set to * non NULL it is unmapped and freed */ void il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq) { int i; for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { if (rxq->pool[i].page != NULL) { pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); __il_free_pages(il, rxq->pool[i].page); rxq->pool[i].page = NULL; } } dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status), rxq->rb_stts, rxq->rb_stts_dma); rxq->bd = NULL; rxq->rb_stts = NULL; } int il4965_rxq_stop(struct il_priv *il) { int ret; _il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG, FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); if (ret < 0) IL_ERR("Can't stop Rx DMA.\n"); return 0; } int il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) { int idx = 0; int band_offset = 0; /* HT rate format: mac80211 wants an MCS number, which is just LSB */ if (rate_n_flags & RATE_MCS_HT_MSK) { idx = (rate_n_flags & 0xff); return idx; /* Legacy rate format, search for match in table */ } else { if (band == IEEE80211_BAND_5GHZ) band_offset = IL_FIRST_OFDM_RATE; for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++) if (il_rates[idx].plcp == (rate_n_flags & 0xFF)) return idx - band_offset; } return -1; } static int il4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp) { /* data from PHY/DSP regarding signal strength, etc., * contents are always there, not configurable by host. */ struct il4965_rx_non_cfg_phy *ncphy = (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf; u32 agc = (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >> IL49_AGC_DB_POS; u32 valid_antennae = (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK) >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET; u8 max_rssi = 0; u32 i; /* Find max rssi among 3 possible receivers. * These values are measured by the digital signal processor (DSP). * They should stay fairly constant even as the signal strength varies, * if the radio's automatic gain control (AGC) is working right. * AGC value (see below) will provide the "interesting" info. */ for (i = 0; i < 3; i++) if (valid_antennae & (1 << i)) max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n", ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], max_rssi, agc); /* dBm = max_rssi dB - agc dB - constant. * Higher AGC (higher radio gain) means lower signal. */ return max_rssi - agc - IL4965_RSSI_OFFSET; } static u32 il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in) { u32 decrypt_out = 0; if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == RX_RES_STATUS_STATION_FOUND) decrypt_out |= (RX_RES_STATUS_STATION_FOUND | RX_RES_STATUS_NO_STATION_INFO_MISMATCH); decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); /* packet was not encrypted */ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == RX_RES_STATUS_SEC_TYPE_NONE) return decrypt_out; /* packet was encrypted with unknown alg */ if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == RX_RES_STATUS_SEC_TYPE_ERR) return decrypt_out; /* decryption was not done in HW */ if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != RX_MPDU_RES_STATUS_DEC_DONE_MSK) return decrypt_out; switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { case RX_RES_STATUS_SEC_TYPE_CCMP: /* alg is CCM: check MIC only */ if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) /* Bad MIC */ decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; else decrypt_out |= RX_RES_STATUS_DECRYPT_OK; break; case RX_RES_STATUS_SEC_TYPE_TKIP: if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { /* Bad TTAK */ decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; break; } /* fall through if TTAK OK */ default: if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; else decrypt_out |= RX_RES_STATUS_DECRYPT_OK; break; } D_RX("decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out); return decrypt_out; } static void il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, u16 len, u32 ampdu_status, struct il_rx_buf *rxb, struct ieee80211_rx_status *stats) { struct sk_buff *skb; __le16 fc = hdr->frame_control; /* We only process data packets if the interface is open */ if (unlikely(!il->is_open)) { D_DROP("Dropping packet while interface is not open.\n"); return; } /* In case of HW accelerated crypto and bad decryption, drop */ if (!il->cfg->mod_params->sw_crypto && il_set_decrypted_flag(il, hdr, ampdu_status, stats)) return; skb = dev_alloc_skb(128); if (!skb) { IL_ERR("dev_alloc_skb failed\n"); return; } skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len, len); il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); ieee80211_rx(il->hw, skb); il->alloc_rxb_page--; rxb->page = NULL; } /* Called for N_RX (legacy ABG frames), or * N_RX_MPDU (HT high-throughput N frames). */ void il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) { struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_rx_phy_res *phy_res; __le32 rx_pkt_status; struct il_rx_mpdu_res_start *amsdu; u32 len; u32 ampdu_status; u32 rate_n_flags; /** * N_RX and N_RX_MPDU are handled differently. * N_RX: physical layer info is in this buffer * N_RX_MPDU: physical layer info was sent in separate * command and cached in il->last_phy_res * * Here we set up local variables depending on which command is * received. */ if (pkt->hdr.cmd == N_RX) { phy_res = (struct il_rx_phy_res *)pkt->u.raw; header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) + phy_res->cfg_phy_cnt); len = le16_to_cpu(phy_res->byte_count); rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*phy_res) + phy_res->cfg_phy_cnt + len); ampdu_status = le32_to_cpu(rx_pkt_status); } else { if (!il->_4965.last_phy_res_valid) { IL_ERR("MPDU frame without cached PHY data\n"); return; } phy_res = &il->_4965.last_phy_res; amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw; header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu)); len = le16_to_cpu(amsdu->byte_count); rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len); ampdu_status = il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status)); } if ((unlikely(phy_res->cfg_phy_cnt > 20))) { D_DROP("dsp size out of range [0,20]: %d/n", phy_res->cfg_phy_cnt); return; } if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status)); return; } /* This will be used in several places later */ rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); /* rx_status carries information about the packet to mac80211 */ rx_status.mactime = le64_to_cpu(phy_res->timestamp); rx_status.band = (phy_res-> phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; rx_status.freq = ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), rx_status.band); rx_status.rate_idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); rx_status.flag = 0; /* TSF isn't reliable. In order to allow smooth user experience, * this W/A doesn't propagate it to the mac80211 */ /*rx_status.flag |= RX_FLAG_MACTIME_MPDU; */ il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); /* Find max signal strength (dBm) among 3 antenna/receiver chains */ rx_status.signal = il4965_calc_rssi(il, phy_res); D_STATS("Rssi %d, TSF %llu\n", rx_status.signal, (unsigned long long)rx_status.mactime); /* * "antenna number" * * It seems that the antenna field in the phy flags value * is actually a bit field. This is undefined by radiotap, * it wants an actual antenna number but I always get "7" * for most legacy frames I receive indicating that the * same frame was received on all three RX chains. * * I think this field should be removed in favor of a * new 802.11n radiotap field "RX chains" that is defined * as a bitmask. */ rx_status.antenna = (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> RX_RES_PHY_FLAGS_ANTENNA_POS; /* set the preamble flag if appropriate */ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) rx_status.flag |= RX_FLAG_SHORTPRE; /* Set up the HT phy flags */ if (rate_n_flags & RATE_MCS_HT_MSK) rx_status.flag |= RX_FLAG_HT; if (rate_n_flags & RATE_MCS_HT40_MSK) rx_status.flag |= RX_FLAG_40MHZ; if (rate_n_flags & RATE_MCS_SGI_MSK) rx_status.flag |= RX_FLAG_SHORT_GI; il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb, &rx_status); } /* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY). * This will be used later in il_hdl_rx() for N_RX_MPDU. */ void il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); il->_4965.last_phy_res_valid = true; memcpy(&il->_4965.last_phy_res, pkt->u.raw, sizeof(struct il_rx_phy_res)); } static int il4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif, enum ieee80211_band band, u8 is_active, u8 n_probes, struct il_scan_channel *scan_ch) { struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; const struct il_channel_info *ch_info; u16 passive_dwell = 0; u16 active_dwell = 0; int added, i; u16 channel; sband = il_get_hw_mode(il, band); if (!sband) return 0; active_dwell = il_get_active_dwell_time(il, band, n_probes); passive_dwell = il_get_passive_dwell_time(il, band, vif); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; for (i = 0, added = 0; i < il->scan_request->n_channels; i++) { chan = il->scan_request->channels[i]; if (chan->band != band) continue; channel = chan->hw_value; scan_ch->channel = cpu_to_le16(channel); ch_info = il_get_channel_info(il, band, channel); if (!il_is_channel_valid(ch_info)) { D_SCAN("Channel %d is INVALID for this band.\n", channel); continue; } if (!is_active || il_is_channel_passive(ch_info) || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; if (n_probes) scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes); scan_ch->active_dwell = cpu_to_le16(active_dwell); scan_ch->passive_dwell = cpu_to_le16(passive_dwell); /* Set txpower levels to defaults */ scan_ch->dsp_atten = 110; /* NOTE: if we were doing 6Mb OFDM for scans we'd use * power level: * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; */ if (band == IEEE80211_BAND_5GHZ) scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; else scan_ch->tx_gain = ((1 << 5) | (5 << 3)); D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel, le32_to_cpu(scan_ch->type), (scan_ch-> type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE", (scan_ch-> type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell : passive_dwell); scan_ch++; added++; } D_SCAN("total channels to scan %d\n", added); return added; } static void il4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid) { int i; u8 ind = *ant; for (i = 0; i < RATE_ANT_NUM - 1; i++) { ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; if (valid & BIT(ind)) { *ant = ind; return; } } } int il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif) { struct il_host_cmd cmd = { .id = C_SCAN, .len = sizeof(struct il_scan_cmd), .flags = CMD_SIZE_HUGE, }; struct il_scan_cmd *scan; u32 rate_flags = 0; u16 cmd_len; u16 rx_chain = 0; enum ieee80211_band band; u8 n_probes = 0; u8 rx_ant = il->hw_params.valid_rx_ant; u8 rate; bool is_active = false; int chan_mod; u8 active_chains; u8 scan_tx_antennas = il->hw_params.valid_tx_ant; int ret; lockdep_assert_held(&il->mutex); if (!il->scan_cmd) { il->scan_cmd = kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE, GFP_KERNEL); if (!il->scan_cmd) { D_SCAN("fail to allocate memory for scan\n"); return -ENOMEM; } } scan = il->scan_cmd; memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE); scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH; scan->quiet_time = IL_ACTIVE_QUIET_TIME; if (il_is_any_associated(il)) { u16 interval; u32 extra; u32 suspend_time = 100; u32 scan_suspend_time = 100; D_INFO("Scanning while associated...\n"); interval = vif->bss_conf.beacon_int; scan->suspend_time = 0; scan->max_out_time = cpu_to_le32(200 * 1024); if (!interval) interval = suspend_time; extra = (suspend_time / interval) << 22; scan_suspend_time = (extra | ((suspend_time % interval) * 1024)); scan->suspend_time = cpu_to_le32(scan_suspend_time); D_SCAN("suspend_time 0x%X beacon interval %d\n", scan_suspend_time, interval); } if (il->scan_request->n_ssids) { int i, p = 0; D_SCAN("Kicking off active scan\n"); for (i = 0; i < il->scan_request->n_ssids; i++) { /* always does wildcard anyway */ if (!il->scan_request->ssids[i].ssid_len) continue; scan->direct_scan[p].id = WLAN_EID_SSID; scan->direct_scan[p].len = il->scan_request->ssids[i].ssid_len; memcpy(scan->direct_scan[p].ssid, il->scan_request->ssids[i].ssid, il->scan_request->ssids[i].ssid_len); n_probes++; p++; } is_active = true; } else D_SCAN("Start passive scan.\n"); scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; scan->tx_cmd.sta_id = il->hw_params.bcast_id; scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; switch (il->scan_band) { case IEEE80211_BAND_2GHZ: scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; chan_mod = le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; if (chan_mod == CHANNEL_MODE_PURE_40) { rate = RATE_6M_PLCP; } else { rate = RATE_1M_PLCP; rate_flags = RATE_MCS_CCK_MSK; } break; case IEEE80211_BAND_5GHZ: rate = RATE_6M_PLCP; break; default: IL_WARN("Invalid scan band\n"); return -EIO; } /* * If active scanning is requested but a certain channel is * marked passive, we can do active scanning if we detect * transmissions. * * There is an issue with some firmware versions that triggers * a sysassert on a "good CRC threshold" of zero (== disabled), * on a radar channel even though this means that we should NOT * send probes. * * The "good CRC threshold" is the number of frames that we * need to receive during our dwell time on a channel before * sending out probes -- setting this to a huge value will * mean we never reach it, but at the same time work around * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER * here instead of IL_GOOD_CRC_TH_DISABLED. */ scan->good_CRC_th = is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER; band = il->scan_band; if (il->cfg->scan_rx_antennas[band]) rx_ant = il->cfg->scan_rx_antennas[band]; il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas); rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS; scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags); /* In power save mode use one chain, otherwise use all chains */ if (test_bit(S_POWER_PMI, &il->status)) { /* rx_ant has been set to all valid chains previously */ active_chains = rx_ant & ((u8) (il->chain_noise_data.active_chains)); if (!active_chains) active_chains = rx_ant; D_SCAN("chain_noise_data.active_chains: %u\n", il->chain_noise_data.active_chains); rx_ant = il4965_first_antenna(active_chains); } /* MIMO is not used here, but value is required */ rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; scan->rx_chain = cpu_to_le16(rx_chain); cmd_len = il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data, vif->addr, il->scan_request->ie, il->scan_request->ie_len, IL_MAX_SCAN_SIZE - sizeof(*scan)); scan->tx_cmd.len = cpu_to_le16(cmd_len); scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK); scan->channel_count = il4965_get_channels_for_scan(il, vif, band, is_active, n_probes, (void *)&scan->data[cmd_len]); if (scan->channel_count == 0) { D_SCAN("channel count %d\n", scan->channel_count); return -EIO; } cmd.len += le16_to_cpu(scan->tx_cmd.len) + scan->channel_count * sizeof(struct il_scan_channel); cmd.data = scan; scan->len = cpu_to_le16(cmd.len); set_bit(S_SCAN_HW, &il->status); ret = il_send_cmd_sync(il, &cmd); if (ret) clear_bit(S_SCAN_HW, &il->status); return ret; } int il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, bool add) { struct il_vif_priv *vif_priv = (void *)vif->drv_priv; if (add) return il4965_add_bssid_station(il, vif->bss_conf.bssid, &vif_priv->ibss_bssid_sta_id); return il_remove_station(il, vif_priv->ibss_bssid_sta_id, vif->bss_conf.bssid); } void il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed) { lockdep_assert_held(&il->sta_lock); if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed) il->stations[sta_id].tid[tid].tfds_in_queue -= freed; else { D_TX("free more than tfds_in_queue (%u:%d)\n", il->stations[sta_id].tid[tid].tfds_in_queue, freed); il->stations[sta_id].tid[tid].tfds_in_queue = 0; } } #define IL_TX_QUEUE_MSK 0xfffff static bool il4965_is_single_rx_stream(struct il_priv *il) { return il->current_ht_config.smps == IEEE80211_SMPS_STATIC || il->current_ht_config.single_chain_sufficient; } #define IL_NUM_RX_CHAINS_MULTIPLE 3 #define IL_NUM_RX_CHAINS_SINGLE 2 #define IL_NUM_IDLE_CHAINS_DUAL 2 #define IL_NUM_IDLE_CHAINS_SINGLE 1 /* * Determine how many receiver/antenna chains to use. * * More provides better reception via diversity. Fewer saves power * at the expense of throughput, but only when not in powersave to * start with. * * MIMO (dual stream) requires at least 2, but works better with 3. * This does not determine *which* chains to use, just how many. */ static int il4965_get_active_rx_chain_count(struct il_priv *il) { /* # of Rx chains to use when expecting MIMO. */ if (il4965_is_single_rx_stream(il)) return IL_NUM_RX_CHAINS_SINGLE; else return IL_NUM_RX_CHAINS_MULTIPLE; } /* * When we are in power saving mode, unless device support spatial * multiplexing power save, use the active count for rx chain count. */ static int il4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt) { /* # Rx chains when idling, depending on SMPS mode */ switch (il->current_ht_config.smps) { case IEEE80211_SMPS_STATIC: case IEEE80211_SMPS_DYNAMIC: return IL_NUM_IDLE_CHAINS_SINGLE; case IEEE80211_SMPS_OFF: return active_cnt; default: WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps); return active_cnt; } } /* up to 4 chains */ static u8 il4965_count_chain_bitmap(u32 chain_bitmap) { u8 res; res = (chain_bitmap & BIT(0)) >> 0; res += (chain_bitmap & BIT(1)) >> 1; res += (chain_bitmap & BIT(2)) >> 2; res += (chain_bitmap & BIT(3)) >> 3; return res; } /** * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image * * Selects how many and which Rx receivers/antennas/chains to use. * This should not be used for scan command ... it puts data in wrong place. */ void il4965_set_rxon_chain(struct il_priv *il) { bool is_single = il4965_is_single_rx_stream(il); bool is_cam = !test_bit(S_POWER_PMI, &il->status); u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; u32 active_chains; u16 rx_chain; /* Tell uCode which antennas are actually connected. * Before first association, we assume all antennas are connected. * Just after first association, il4965_chain_noise_calibration() * checks which antennas actually *are* connected. */ if (il->chain_noise_data.active_chains) active_chains = il->chain_noise_data.active_chains; else active_chains = il->hw_params.valid_rx_ant; rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; /* How many receivers should we use? */ active_rx_cnt = il4965_get_active_rx_chain_count(il); idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt); /* correct rx chain count according hw settings * and chain noise calibration */ valid_rx_cnt = il4965_count_chain_bitmap(active_chains); if (valid_rx_cnt < active_rx_cnt) active_rx_cnt = valid_rx_cnt; if (valid_rx_cnt < idle_rx_cnt) idle_rx_cnt = valid_rx_cnt; rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; il->staging.rx_chain = cpu_to_le16(rx_chain); if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam) il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; else il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain, active_rx_cnt, idle_rx_cnt); WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || active_rx_cnt < idle_rx_cnt); } static const char * il4965_get_fh_string(int cmd) { switch (cmd) { IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG); IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG); IL_CMD(FH49_RSCSR_CHNL0_WPTR); IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG); IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG); IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG); IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); IL_CMD(FH49_TSSR_TX_STATUS_REG); IL_CMD(FH49_TSSR_TX_ERROR_REG); default: return "UNKNOWN"; } } int il4965_dump_fh(struct il_priv *il, char **buf, bool display) { int i; #ifdef CONFIG_IWLEGACY_DEBUG int pos = 0; size_t bufsz = 0; #endif static const u32 fh_tbl[] = { FH49_RSCSR_CHNL0_STTS_WPTR_REG, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, FH49_RSCSR_CHNL0_WPTR, FH49_MEM_RCSR_CHNL0_CONFIG_REG, FH49_MEM_RSSR_SHARED_CTRL_REG, FH49_MEM_RSSR_RX_STATUS_REG, FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, FH49_TSSR_TX_STATUS_REG, FH49_TSSR_TX_ERROR_REG }; #ifdef CONFIG_IWLEGACY_DEBUG if (display) { bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; *buf = kmalloc(bufsz, GFP_KERNEL); if (!*buf) return -ENOMEM; pos += scnprintf(*buf + pos, bufsz - pos, "FH register values:\n"); for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { pos += scnprintf(*buf + pos, bufsz - pos, " %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]), il_rd(il, fh_tbl[i])); } return pos; } #endif IL_ERR("FH register values:\n"); for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { IL_ERR(" %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]), il_rd(il, fh_tbl[i])); } return 0; } void il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_missed_beacon_notif *missed_beacon; missed_beacon = &pkt->u.missed_beacon; if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > il->missed_beacon_threshold) { D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n", le32_to_cpu(missed_beacon->consecutive_missed_beacons), le32_to_cpu(missed_beacon->total_missed_becons), le32_to_cpu(missed_beacon->num_recvd_beacons), le32_to_cpu(missed_beacon->num_expected_beacons)); if (!test_bit(S_SCANNING, &il->status)) il4965_init_sensitivity(il); } } /* Calculate noise level, based on measurements during network silence just * before arriving beacon. This measurement can be done only if we know * exactly when to expect beacons, therefore only when we're associated. */ static void il4965_rx_calc_noise(struct il_priv *il) { struct stats_rx_non_phy *rx_info; int num_active_rx = 0; int total_silence = 0; int bcn_silence_a, bcn_silence_b, bcn_silence_c; int last_rx_noise; rx_info = &(il->_4965.stats.rx.general); bcn_silence_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; bcn_silence_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; bcn_silence_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; if (bcn_silence_a) { total_silence += bcn_silence_a; num_active_rx++; } if (bcn_silence_b) { total_silence += bcn_silence_b; num_active_rx++; } if (bcn_silence_c) { total_silence += bcn_silence_c; num_active_rx++; } /* Average among active antennas */ if (num_active_rx) last_rx_noise = (total_silence / num_active_rx) - 107; else last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE; D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a, bcn_silence_b, bcn_silence_c, last_rx_noise); } #ifdef CONFIG_IWLEGACY_DEBUGFS /* * based on the assumption of all stats counter are in DWORD * FIXME: This function is for debugging, do not deal with * the case of counters roll-over. */ static void il4965_accumulative_stats(struct il_priv *il, __le32 * stats) { int i, size; __le32 *prev_stats; u32 *accum_stats; u32 *delta, *max_delta; struct stats_general_common *general, *accum_general; struct stats_tx *tx, *accum_tx; prev_stats = (__le32 *) &il->_4965.stats; accum_stats = (u32 *) &il->_4965.accum_stats; size = sizeof(struct il_notif_stats); general = &il->_4965.stats.general.common; accum_general = &il->_4965.accum_stats.general.common; tx = &il->_4965.stats.tx; accum_tx = &il->_4965.accum_stats.tx; delta = (u32 *) &il->_4965.delta_stats; max_delta = (u32 *) &il->_4965.max_delta; for (i = sizeof(__le32); i < size; i += sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, accum_stats++) { if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { *delta = (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); *accum_stats += *delta; if (*delta > *max_delta) *max_delta = *delta; } } /* reset accumulative stats for "no-counter" type stats */ accum_general->temperature = general->temperature; accum_general->ttl_timestamp = general->ttl_timestamp; } #endif void il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb) { const int recalib_seconds = 60; bool change; struct il_rx_pkt *pkt = rxb_addr(rxb); D_RX("Statistics notification received (%d vs %d).\n", (int)sizeof(struct il_notif_stats), le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK); change = ((il->_4965.stats.general.common.temperature != pkt->u.stats.general.common.temperature) || ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) != (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK))); #ifdef CONFIG_IWLEGACY_DEBUGFS il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats); #endif /* TODO: reading some of stats is unneeded */ memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats)); set_bit(S_STATS, &il->status); /* * Reschedule the stats timer to occur in recalib_seconds to ensure * we get a thermal update even if the uCode doesn't give us one */ mod_timer(&il->stats_periodic, jiffies + msecs_to_jiffies(recalib_seconds * 1000)); if (unlikely(!test_bit(S_SCANNING, &il->status)) && (pkt->hdr.cmd == N_STATS)) { il4965_rx_calc_noise(il); queue_work(il->workqueue, &il->run_time_calib_work); } if (change) il4965_temperature_calib(il); } void il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) { #ifdef CONFIG_IWLEGACY_DEBUGFS memset(&il->_4965.accum_stats, 0, sizeof(struct il_notif_stats)); memset(&il->_4965.delta_stats, 0, sizeof(struct il_notif_stats)); memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats)); #endif D_RX("Statistics have been cleared\n"); } il4965_hdl_stats(il, rxb); } /* * mac80211 queues, ACs, hardware queues, FIFOs. * * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues * * Mac80211 uses the following numbers, which we get as from it * by way of skb_get_queue_mapping(skb): * * VO 0 * VI 1 * BE 2 * BK 3 * * * Regular (not A-MPDU) frames are put into hardware queues corresponding * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their * own queue per aggregation session (RA/TID combination), such queues are * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In * order to map frames to the right queue, we also need an AC->hw queue * mapping. This is implemented here. * * Due to the way hw queues are set up (by the hw specific modules like * 4965.c), the AC->hw queue mapping is the identity * mapping. */ static const u8 tid_to_ac[] = { IEEE80211_AC_BE, IEEE80211_AC_BK, IEEE80211_AC_BK, IEEE80211_AC_BE, IEEE80211_AC_VI, IEEE80211_AC_VI, IEEE80211_AC_VO, IEEE80211_AC_VO }; static inline int il4965_get_ac_from_tid(u16 tid) { if (likely(tid < ARRAY_SIZE(tid_to_ac))) return tid_to_ac[tid]; /* no support for TIDs 8-15 yet */ return -EINVAL; } static inline int il4965_get_fifo_from_tid(u16 tid) { const u8 ac_to_fifo[] = { IL_TX_FIFO_VO, IL_TX_FIFO_VI, IL_TX_FIFO_BE, IL_TX_FIFO_BK, }; if (likely(tid < ARRAY_SIZE(tid_to_ac))) return ac_to_fifo[tid_to_ac[tid]]; /* no support for TIDs 8-15 yet */ return -EINVAL; } /* * handle build C_TX command notification. */ static void il4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb, struct il_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, u8 std_id) { __le16 fc = hdr->frame_control; __le32 tx_flags = tx_cmd->tx_flags; tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { tx_flags |= TX_CMD_FLG_ACK_MSK; if (ieee80211_is_mgmt(fc)) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; if (ieee80211_is_probe_resp(fc) && !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) tx_flags |= TX_CMD_FLG_TSF_MSK; } else { tx_flags &= (~TX_CMD_FLG_ACK_MSK); tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; tx_cmd->sta_id = std_id; if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { u8 *qc = ieee80211_get_qos_ctl(hdr); tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } il_tx_cmd_protection(il, info, fc, &tx_flags); tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); if (ieee80211_is_mgmt(fc)) { if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); else tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); } else { tx_cmd->timeout.pm_frame_timeout = 0; } tx_cmd->driver_txop = 0; tx_cmd->tx_flags = tx_flags; tx_cmd->next_frame_len = 0; } static void il4965_tx_cmd_build_rate(struct il_priv *il, struct il_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, __le16 fc) { const u8 rts_retry_limit = 60; u32 rate_flags; int rate_idx; u8 data_retry_limit; u8 rate_plcp; /* Set retry limit on DATA packets and Probe Responses */ if (ieee80211_is_probe_resp(fc)) data_retry_limit = 3; else data_retry_limit = IL4965_DEFAULT_TX_RETRY; tx_cmd->data_retry_limit = data_retry_limit; /* Set retry limit on RTS packets */ tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit); /* DATA packets will use the uCode station table for rate/antenna * selection */ if (ieee80211_is_data(fc)) { tx_cmd->initial_rate_idx = 0; tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; return; } /** * If the current TX rate stored in mac80211 has the MCS bit set, it's * not really a TX rate. Thus, we use the lowest supported rate for * this band. Also use the lowest supported rate if the stored rate * idx is invalid. */ rate_idx = info->control.rates[0].idx; if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0 || rate_idx > RATE_COUNT_LEGACY) rate_idx = rate_lowest_index(&il->bands[info->band], sta); /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ if (info->band == IEEE80211_BAND_5GHZ) rate_idx += IL_FIRST_OFDM_RATE; /* Get PLCP rate for tx_cmd->rate_n_flags */ rate_plcp = il_rates[rate_idx].plcp; /* Zero out flags for this packet */ rate_flags = 0; /* Set CCK flag as needed */ if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE) rate_flags |= RATE_MCS_CCK_MSK; /* Set up antennas */ il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; /* Set the rate in the TX cmd */ tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags); } static void il4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag, int sta_id) { struct ieee80211_key_conf *keyconf = info->control.hw_key; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_CCMP: tx_cmd->sec_ctl = TX_CMD_SEC_CCM; memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); if (info->flags & IEEE80211_TX_CTL_AMPDU) tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; D_TX("tx_cmd with AES hwcrypto\n"); break; case WLAN_CIPHER_SUITE_TKIP: tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); D_TX("tx_cmd with tkip hwcrypto\n"); break; case WLAN_CIPHER_SUITE_WEP104: tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; /* fall through */ case WLAN_CIPHER_SUITE_WEP40: tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); D_TX("Configuring packet for WEP encryption " "with key %d\n", keyconf->keyidx); break; default: IL_ERR("Unknown encode cipher %x\n", keyconf->cipher); break; } } /* * start C_TX command process */ int il4965_tx_skb(struct il_priv *il, struct ieee80211_sta *sta, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct il_station_priv *sta_priv = NULL; struct il_tx_queue *txq; struct il_queue *q; struct il_device_cmd *out_cmd; struct il_cmd_meta *out_meta; struct il_tx_cmd *tx_cmd; int txq_id; dma_addr_t phys_addr; dma_addr_t txcmd_phys; dma_addr_t scratch_phys; u16 len, firstlen, secondlen; u16 seq_number = 0; __le16 fc; u8 hdr_len; u8 sta_id; u8 wait_write_ptr = 0; u8 tid = 0; u8 *qc = NULL; unsigned long flags; bool is_agg = false; spin_lock_irqsave(&il->lock, flags); if (il_is_rfkill(il)) { D_DROP("Dropping - RF KILL\n"); goto drop_unlock; } fc = hdr->frame_control; #ifdef CONFIG_IWLEGACY_DEBUG if (ieee80211_is_auth(fc)) D_TX("Sending AUTH frame\n"); else if (ieee80211_is_assoc_req(fc)) D_TX("Sending ASSOC frame\n"); else if (ieee80211_is_reassoc_req(fc)) D_TX("Sending REASSOC frame\n"); #endif hdr_len = ieee80211_hdrlen(fc); /* For management frames use broadcast id to do not break aggregation */ if (!ieee80211_is_data(fc)) sta_id = il->hw_params.bcast_id; else { /* Find idx into station table for destination station */ sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) { D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); goto drop_unlock; } } D_TX("station Id %d\n", sta_id); if (sta) sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the * next frame is processed -- and the next frame to * this station is the one that will consume this * counter. * For now set the counter to just 1 since we do not * support uAPSD yet. */ il4965_sta_modify_sleep_tx_count(il, sta_id, 1); } /* FIXME: remove me ? */ WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); /* Access category (AC) is also the queue number */ txq_id = skb_get_queue_mapping(skb); /* irqs already disabled/saved above when locking il->lock */ spin_lock(&il->sta_lock); if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) { spin_unlock(&il->sta_lock); goto drop_unlock; } seq_number = il->stations[sta_id].tid[tid].seq_number; seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl = hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; /* aggregation is on for this */ if (info->flags & IEEE80211_TX_CTL_AMPDU && il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) { txq_id = il->stations[sta_id].tid[tid].agg.txq_id; is_agg = true; } } txq = &il->txq[txq_id]; q = &txq->q; if (unlikely(il_queue_space(q) < q->high_mark)) { spin_unlock(&il->sta_lock); goto drop_unlock; } if (ieee80211_is_data_qos(fc)) { il->stations[sta_id].tid[tid].tfds_in_queue++; if (!ieee80211_has_morefrags(fc)) il->stations[sta_id].tid[tid].seq_number = seq_number; } spin_unlock(&il->sta_lock); txq->skbs[q->write_ptr] = skb; /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_cmd = txq->cmd[q->write_ptr]; out_meta = &txq->meta[q->write_ptr]; tx_cmd = &out_cmd->cmd.tx; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); memset(tx_cmd, 0, sizeof(struct il_tx_cmd)); /* * Set up the Tx-command (not MAC!) header. * Store the chosen Tx queue and TFD idx within the sequence field; * after Tx, uCode's Tx response will return this value so driver can * locate the frame within the tx queue and do post-tx processing. */ out_cmd->hdr.cmd = C_TX; out_cmd->hdr.sequence = cpu_to_le16((u16) (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr))); /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, hdr_len); /* Total # bytes to be transmitted */ len = (u16) skb->len; tx_cmd->len = cpu_to_le16(len); if (info->control.hw_key) il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id); /* TODO need this for burst mode later on */ il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id); il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc); il_update_stats(il, true, fc, len); /* * Use the first empty entry in this queue's command buffer array * to contain the Tx command and MAC header concatenated together * (payload data will be in another buffer). * Size of this varies, due to varying MAC header length. * If end is not dword aligned, we'll have 2 extra bytes at the end * of the MAC header (device reads on dword boundaries). * We'll tell device about this padding later. */ len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len; firstlen = (len + 3) & ~3; /* Tell NIC about any 2-byte padding after MAC header */ if (firstlen != len) tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; /* Physical address of this Tx command's header (not MAC header!), * within command buffer array. */ txcmd_phys = pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen, PCI_DMA_BIDIRECTIONAL); dma_unmap_addr_set(out_meta, mapping, txcmd_phys); dma_unmap_len_set(out_meta, len, firstlen); /* Add buffer containing Tx command and MAC(!) header to TFD's * first entry */ il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0); if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; } else { wait_write_ptr = 1; txq->need_update = 0; } /* Set up TFD's 2nd entry to point directly to remainder of skb, * if any (802.11 null frames have no payload). */ secondlen = skb->len - hdr_len; if (secondlen > 0) { phys_addr = pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen, PCI_DMA_TODEVICE); il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen, 0, 0); } scratch_phys = txcmd_phys + sizeof(struct il_cmd_header) + offsetof(struct il_tx_cmd, scratch); /* take back ownership of DMA buffer to enable update */ pci_dma_sync_single_for_cpu(il->pci_dev, txcmd_phys, firstlen, PCI_DMA_BIDIRECTIONAL); tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys); D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence)); D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd)); il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len); /* Set up entry for this TFD in Tx byte-count array */ if (info->flags & IEEE80211_TX_CTL_AMPDU) il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len)); pci_dma_sync_single_for_device(il->pci_dev, txcmd_phys, firstlen, PCI_DMA_BIDIRECTIONAL); /* Tell device the write idx *just past* this latest filled TFD */ q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); il_txq_update_write_ptr(il, txq); spin_unlock_irqrestore(&il->lock, flags); /* * At this point the frame is "transmitted" successfully * and we will get a TX status notification eventually, * regardless of the value of ret. "ret" only indicates * whether or not we should update the write pointer. */ /* * Avoid atomic ops if it isn't an associated client. * Also, if this is a packet for aggregation, don't * increase the counter because the ucode will stop * aggregation queues when their respective station * goes to sleep. */ if (sta_priv && sta_priv->client && !is_agg) atomic_inc(&sta_priv->pending_frames); if (il_queue_space(q) < q->high_mark && il->mac80211_registered) { if (wait_write_ptr) { spin_lock_irqsave(&il->lock, flags); txq->need_update = 1; il_txq_update_write_ptr(il, txq); spin_unlock_irqrestore(&il->lock, flags); } else { il_stop_queue(il, txq); } } return 0; drop_unlock: spin_unlock_irqrestore(&il->lock, flags); return -1; } static inline int il4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size) { ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma, GFP_KERNEL); if (!ptr->addr) return -ENOMEM; ptr->size = size; return 0; } static inline void il4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr) { if (unlikely(!ptr->addr)) return; dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma); memset(ptr, 0, sizeof(*ptr)); } /** * il4965_hw_txq_ctx_free - Free TXQ Context * * Destroy all TX DMA queues and structures */ void il4965_hw_txq_ctx_free(struct il_priv *il) { int txq_id; /* Tx queues */ if (il->txq) { for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) if (txq_id == il->cmd_queue) il_cmd_queue_free(il); else il_tx_queue_free(il, txq_id); } il4965_free_dma_ptr(il, &il->kw); il4965_free_dma_ptr(il, &il->scd_bc_tbls); /* free tx queue structure */ il_free_txq_mem(il); } /** * il4965_txq_ctx_alloc - allocate TX queue context * Allocate all Tx DMA structures and initialize them * * @param il * @return error code */ int il4965_txq_ctx_alloc(struct il_priv *il) { int ret, txq_id; unsigned long flags; /* Free all tx/cmd queues and keep-warm buffer */ il4965_hw_txq_ctx_free(il); ret = il4965_alloc_dma_ptr(il, &il->scd_bc_tbls, il->hw_params.scd_bc_tbls_size); if (ret) { IL_ERR("Scheduler BC Table allocation failed\n"); goto error_bc_tbls; } /* Alloc keep-warm buffer */ ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE); if (ret) { IL_ERR("Keep Warm allocation failed\n"); goto error_kw; } /* allocate tx queue structure */ ret = il_alloc_txq_mem(il); if (ret) goto error; spin_lock_irqsave(&il->lock, flags); /* Turn off all Tx DMA fifos */ il4965_txq_set_sched(il, 0); /* Tell NIC where to find the "keep warm" buffer */ il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); spin_unlock_irqrestore(&il->lock, flags); /* Alloc and init all Tx queues, including the command queue (#4/#9) */ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { ret = il_tx_queue_init(il, txq_id); if (ret) { IL_ERR("Tx %d queue init failed\n", txq_id); goto error; } } return ret; error: il4965_hw_txq_ctx_free(il); il4965_free_dma_ptr(il, &il->kw); error_kw: il4965_free_dma_ptr(il, &il->scd_bc_tbls); error_bc_tbls: return ret; } void il4965_txq_ctx_reset(struct il_priv *il) { int txq_id; unsigned long flags; spin_lock_irqsave(&il->lock, flags); /* Turn off all Tx DMA fifos */ il4965_txq_set_sched(il, 0); /* Tell NIC where to find the "keep warm" buffer */ il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); spin_unlock_irqrestore(&il->lock, flags); /* Alloc and init all Tx queues, including the command queue (#4) */ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) il_tx_queue_reset(il, txq_id); } void il4965_txq_ctx_unmap(struct il_priv *il) { int txq_id; if (!il->txq) return; /* Unmap DMA from host system and free skb's */ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) if (txq_id == il->cmd_queue) il_cmd_queue_unmap(il); else il_tx_queue_unmap(il, txq_id); } /** * il4965_txq_ctx_stop - Stop all Tx DMA channels */ void il4965_txq_ctx_stop(struct il_priv *il) { int ch, ret; _il_wr_prph(il, IL49_SCD_TXFACT, 0); /* Stop each Tx DMA channel, and wait for it to be idle */ for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) { _il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); ret = _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG, FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); if (ret < 0) IL_ERR("Timeout stopping DMA channel %d [0x%08x]", ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG)); } } /* * Find first available (lowest unused) Tx Queue, mark it "active". * Called only when finding queue for aggregation. * Should never return anything < 7, because they should already * be in use as EDCA AC (0-3), Command (4), reserved (5, 6) */ static int il4965_txq_ctx_activate_free(struct il_priv *il) { int txq_id; for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk)) return txq_id; return -1; } /** * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration */ static void il4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id) { /* Simply stop the queue, but don't change any configuration; * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } /** * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue */ static int il4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id) { u32 tbl_dw_addr; u32 tbl_dw; u16 scd_q2ratid; scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; tbl_dw_addr = il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); tbl_dw = il_read_targ_mem(il, tbl_dw_addr); if (txq_id & 0x1) tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); else tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); il_write_targ_mem(il, tbl_dw_addr, tbl_dw); return 0; } /** * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue * * NOTE: txq_id must be greater than IL49_FIRST_AMPDU_QUEUE, * i.e. it must be one of the higher queues used for aggregation */ static int il4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id, int tid, u16 ssn_idx) { unsigned long flags; u16 ra_tid; int ret; if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || (IL49_FIRST_AMPDU_QUEUE + il->cfg->num_of_ampdu_queues <= txq_id)) { IL_WARN("queue number out of range: %d, must be %d to %d\n", txq_id, IL49_FIRST_AMPDU_QUEUE, IL49_FIRST_AMPDU_QUEUE + il->cfg->num_of_ampdu_queues - 1); return -EINVAL; } ra_tid = BUILD_RAxTID(sta_id, tid); /* Modify device's station table to Tx this TID */ ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid); if (ret) return ret; spin_lock_irqsave(&il->lock, flags); /* Stop this Tx queue before configuring it */ il4965_tx_queue_stop_scheduler(il, txq_id); /* Map receiver-address / traffic-ID to this queue */ il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id); /* Set this queue as a chain-building queue */ il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); /* Place first TFD at idx corresponding to start sequence number. * Assumes that ssn_idx is valid (!= 0xFFF) */ il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); il4965_set_wr_ptrs(il, txq_id, ssn_idx); /* Set up Tx win size and frame limit for this queue */ il_write_targ_mem(il, il->scd_base_addr + IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id), (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); il_write_targ_mem(il, il->scd_base_addr + IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), (SCD_FRAME_LIMIT << IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1); spin_unlock_irqrestore(&il->lock, flags); return 0; } int il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 * ssn) { int sta_id; int tx_fifo; int txq_id; int ret; unsigned long flags; struct il_tid_data *tid_data; /* FIXME: warning if tx fifo not found ? */ tx_fifo = il4965_get_fifo_from_tid(tid); if (unlikely(tx_fifo < 0)) return tx_fifo; D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid); sta_id = il_sta_id(sta); if (sta_id == IL_INVALID_STATION) { IL_ERR("Start AGG on invalid station\n"); return -ENXIO; } if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) { IL_ERR("Start AGG when state is not IL_AGG_OFF !\n"); return -ENXIO; } txq_id = il4965_txq_ctx_activate_free(il); if (txq_id == -1) { IL_ERR("No free aggregation queue available\n"); return -ENXIO; } spin_lock_irqsave(&il->sta_lock, flags); tid_data = &il->stations[sta_id].tid[tid]; *ssn = SEQ_TO_SN(tid_data->seq_number); tid_data->agg.txq_id = txq_id; il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id); spin_unlock_irqrestore(&il->sta_lock, flags); ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn); if (ret) return ret; spin_lock_irqsave(&il->sta_lock, flags); tid_data = &il->stations[sta_id].tid[tid]; if (tid_data->tfds_in_queue == 0) { D_HT("HW queue is empty\n"); tid_data->agg.state = IL_AGG_ON; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); } else { D_HT("HW queue is NOT empty: %d packets in HW queue\n", tid_data->tfds_in_queue); tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA; } spin_unlock_irqrestore(&il->sta_lock, flags); return ret; } /** * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE * il->lock must be held by the caller */ static int il4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo) { if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || (IL49_FIRST_AMPDU_QUEUE + il->cfg->num_of_ampdu_queues <= txq_id)) { IL_WARN("queue number out of range: %d, must be %d to %d\n", txq_id, IL49_FIRST_AMPDU_QUEUE, IL49_FIRST_AMPDU_QUEUE + il->cfg->num_of_ampdu_queues - 1); return -EINVAL; } il4965_tx_queue_stop_scheduler(il, txq_id); il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); /* supposes that ssn_idx is valid (!= 0xFFF) */ il4965_set_wr_ptrs(il, txq_id, ssn_idx); il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); il_txq_ctx_deactivate(il, txq_id); il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0); return 0; } int il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { int tx_fifo_id, txq_id, sta_id, ssn; struct il_tid_data *tid_data; int write_ptr, read_ptr; unsigned long flags; /* FIXME: warning if tx_fifo_id not found ? */ tx_fifo_id = il4965_get_fifo_from_tid(tid); if (unlikely(tx_fifo_id < 0)) return tx_fifo_id; sta_id = il_sta_id(sta); if (sta_id == IL_INVALID_STATION) { IL_ERR("Invalid station for AGG tid %d\n", tid); return -ENXIO; } spin_lock_irqsave(&il->sta_lock, flags); tid_data = &il->stations[sta_id].tid[tid]; ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; txq_id = tid_data->agg.txq_id; switch (il->stations[sta_id].tid[tid].agg.state) { case IL_EMPTYING_HW_QUEUE_ADDBA: /* * This can happen if the peer stops aggregation * again before we've had a chance to drain the * queue we selected previously, i.e. before the * session was really started completely. */ D_HT("AGG stop before setup done\n"); goto turn_off; case IL_AGG_ON: break; default: IL_WARN("Stopping AGG while state not ON or starting\n"); } write_ptr = il->txq[txq_id].q.write_ptr; read_ptr = il->txq[txq_id].q.read_ptr; /* The queue is not empty */ if (write_ptr != read_ptr) { D_HT("Stopping a non empty AGG HW QUEUE\n"); il->stations[sta_id].tid[tid].agg.state = IL_EMPTYING_HW_QUEUE_DELBA; spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } D_HT("HW queue is empty\n"); turn_off: il->stations[sta_id].tid[tid].agg.state = IL_AGG_OFF; /* do not restore/save irqs */ spin_unlock(&il->sta_lock); spin_lock(&il->lock); /* * the only reason this call can fail is queue number out of range, * which can happen if uCode is reloaded and all the station * information are lost. if it is outside the range, there is no need * to deactivate the uCode queue, just return "success" to allow * mac80211 to clean up it own data. */ il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo_id); spin_unlock_irqrestore(&il->lock, flags); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); return 0; } int il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id) { struct il_queue *q = &il->txq[txq_id].q; u8 *addr = il->stations[sta_id].sta.sta.addr; struct il_tid_data *tid_data = &il->stations[sta_id].tid[tid]; lockdep_assert_held(&il->sta_lock); switch (il->stations[sta_id].tid[tid].agg.state) { case IL_EMPTYING_HW_QUEUE_DELBA: /* We are reclaiming the last packet of the */ /* aggregated HW queue */ if (txq_id == tid_data->agg.txq_id && q->read_ptr == q->write_ptr) { u16 ssn = SEQ_TO_SN(tid_data->seq_number); int tx_fifo = il4965_get_fifo_from_tid(tid); D_HT("HW queue empty: continue DELBA flow\n"); il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo); tid_data->agg.state = IL_AGG_OFF; ieee80211_stop_tx_ba_cb_irqsafe(il->vif, addr, tid); } break; case IL_EMPTYING_HW_QUEUE_ADDBA: /* We are reclaiming the last packet of the queue */ if (tid_data->tfds_in_queue == 0) { D_HT("HW queue empty: continue ADDBA flow\n"); tid_data->agg.state = IL_AGG_ON; ieee80211_start_tx_ba_cb_irqsafe(il->vif, addr, tid); } break; } return 0; } static void il4965_non_agg_tx_status(struct il_priv *il, const u8 *addr1) { struct ieee80211_sta *sta; struct il_station_priv *sta_priv; rcu_read_lock(); sta = ieee80211_find_sta(il->vif, addr1); if (sta) { sta_priv = (void *)sta->drv_priv; /* avoid atomic ops if this isn't a client */ if (sta_priv->client && atomic_dec_return(&sta_priv->pending_frames) == 0) ieee80211_sta_block_awake(il->hw, sta, false); } rcu_read_unlock(); } static void il4965_tx_status(struct il_priv *il, struct sk_buff *skb, bool is_agg) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (!is_agg) il4965_non_agg_tx_status(il, hdr->addr1); ieee80211_tx_status_irqsafe(il->hw, skb); } int il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx) { struct il_tx_queue *txq = &il->txq[txq_id]; struct il_queue *q = &txq->q; int nfreed = 0; struct ieee80211_hdr *hdr; struct sk_buff *skb; if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, q->write_ptr, q->read_ptr); return 0; } for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { skb = txq->skbs[txq->q.read_ptr]; if (WARN_ON_ONCE(skb == NULL)) continue; hdr = (struct ieee80211_hdr *) skb->data; if (ieee80211_is_data_qos(hdr->frame_control)) nfreed++; il4965_tx_status(il, skb, txq_id >= IL4965_FIRST_AMPDU_QUEUE); txq->skbs[txq->q.read_ptr] = NULL; il->ops->txq_free_tfd(il, txq); } return nfreed; } /** * il4965_tx_status_reply_compressed_ba - Update tx status from block-ack * * Go through block-ack's bitmap of ACK'd frames, update driver's record of * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. */ static int il4965_tx_status_reply_compressed_ba(struct il_priv *il, struct il_ht_agg *agg, struct il_compressed_ba_resp *ba_resp) { int i, sh, ack; u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); int successes = 0; struct ieee80211_tx_info *info; u64 bitmap, sent_bitmap; if (unlikely(!agg->wait_for_ba)) { if (unlikely(ba_resp->bitmap)) IL_ERR("Received BA when not expected\n"); return -EINVAL; } /* Mark that the expected block-ack response arrived */ agg->wait_for_ba = 0; D_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl); /* Calculate shift to align block-ack bits with our Tx win bits */ sh = agg->start_idx - SEQ_TO_IDX(seq_ctl >> 4); if (sh < 0) /* tbw something is wrong with indices */ sh += 0x100; if (agg->frame_count > (64 - sh)) { D_TX_REPLY("more frames than bitmap size"); return -1; } /* don't use 64-bit values for now */ bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; /* check for success or failure according to the * transmitted bitmap and block-ack bitmap */ sent_bitmap = bitmap & agg->bitmap; /* For each frame attempted in aggregation, * update driver's record of tx frame's status. */ i = 0; while (sent_bitmap) { ack = sent_bitmap & 1ULL; successes += ack; D_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack ? "ACK" : "NACK", i, (agg->start_idx + i) & 0xff, agg->start_idx + i); sent_bitmap >>= 1; ++i; } D_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap); info = IEEE80211_SKB_CB(il->txq[scd_flow].skbs[agg->start_idx]); memset(&info->status, 0, sizeof(info->status)); info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = successes; info->status.ampdu_len = agg->frame_count; il4965_hwrate_to_tx_control(il, agg->rate_n_flags, info); return 0; } static inline bool il4965_is_tx_success(u32 status) { status &= TX_STATUS_MSK; return (status == TX_STATUS_SUCCESS || status == TX_STATUS_DIRECT_DONE); } static u8 il4965_find_station(struct il_priv *il, const u8 *addr) { int i; int start = 0; int ret = IL_INVALID_STATION; unsigned long flags; if (il->iw_mode == NL80211_IFTYPE_ADHOC) start = IL_STA_ID; if (is_broadcast_ether_addr(addr)) return il->hw_params.bcast_id; spin_lock_irqsave(&il->sta_lock, flags); for (i = start; i < il->hw_params.max_stations; i++) if (il->stations[i].used && ether_addr_equal(il->stations[i].sta.sta.addr, addr)) { ret = i; goto out; } D_ASSOC("can not find STA %pM total %d\n", addr, il->num_stations); out: /* * It may be possible that more commands interacting with stations * arrive before we completed processing the adding of * station */ if (ret != IL_INVALID_STATION && (!(il->stations[ret].used & IL_STA_UCODE_ACTIVE) || ((il->stations[ret].used & IL_STA_UCODE_ACTIVE) && (il->stations[ret].used & IL_STA_UCODE_INPROGRESS)))) { IL_ERR("Requested station info for sta %d before ready.\n", ret); ret = IL_INVALID_STATION; } spin_unlock_irqrestore(&il->sta_lock, flags); return ret; } static int il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr) { if (il->iw_mode == NL80211_IFTYPE_STATION) return IL_AP_ID; else { u8 *da = ieee80211_get_DA(hdr); return il4965_find_station(il, da); } } static inline u32 il4965_get_scd_ssn(struct il4965_tx_resp *tx_resp) { return le32_to_cpup(&tx_resp->u.status + tx_resp->frame_count) & MAX_SN; } static inline u32 il4965_tx_status_to_mac80211(u32 status) { status &= TX_STATUS_MSK; switch (status) { case TX_STATUS_SUCCESS: case TX_STATUS_DIRECT_DONE: return IEEE80211_TX_STAT_ACK; case TX_STATUS_FAIL_DEST_PS: return IEEE80211_TX_STAT_TX_FILTERED; default: return 0; } } /** * il4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue */ static int il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg, struct il4965_tx_resp *tx_resp, int txq_id, u16 start_idx) { u16 status; struct agg_tx_status *frame_status = tx_resp->u.agg_status; struct ieee80211_tx_info *info = NULL; struct ieee80211_hdr *hdr = NULL; u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); int i, sh, idx; u16 seq; if (agg->wait_for_ba) D_TX_REPLY("got tx response w/o block-ack\n"); agg->frame_count = tx_resp->frame_count; agg->start_idx = start_idx; agg->rate_n_flags = rate_n_flags; agg->bitmap = 0; /* num frames attempted by Tx command */ if (agg->frame_count == 1) { /* Only one frame was attempted; no block-ack will arrive */ status = le16_to_cpu(frame_status[0].status); idx = start_idx; D_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n", agg->frame_count, agg->start_idx, idx); info = IEEE80211_SKB_CB(il->txq[txq_id].skbs[idx]); info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags &= ~IEEE80211_TX_CTL_AMPDU; info->flags |= il4965_tx_status_to_mac80211(status); il4965_hwrate_to_tx_control(il, rate_n_flags, info); D_TX_REPLY("1 Frame 0x%x failure :%d\n", status & 0xff, tx_resp->failure_frame); D_TX_REPLY("Rate Info rate_n_flags=%x\n", rate_n_flags); agg->wait_for_ba = 0; } else { /* Two or more frames were attempted; expect block-ack */ u64 bitmap = 0; int start = agg->start_idx; struct sk_buff *skb; /* Construct bit-map of pending frames within Tx win */ for (i = 0; i < agg->frame_count; i++) { u16 sc; status = le16_to_cpu(frame_status[i].status); seq = le16_to_cpu(frame_status[i].sequence); idx = SEQ_TO_IDX(seq); txq_id = SEQ_TO_QUEUE(seq); if (status & (AGG_TX_STATE_FEW_BYTES_MSK | AGG_TX_STATE_ABORT_MSK)) continue; D_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n", agg->frame_count, txq_id, idx); skb = il->txq[txq_id].skbs[idx]; if (WARN_ON_ONCE(skb == NULL)) return -1; hdr = (struct ieee80211_hdr *) skb->data; sc = le16_to_cpu(hdr->seq_ctrl); if (idx != (SEQ_TO_SN(sc) & 0xff)) { IL_ERR("BUG_ON idx doesn't match seq control" " idx=%d, seq_idx=%d, seq=%d\n", idx, SEQ_TO_SN(sc), hdr->seq_ctrl); return -1; } D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx, SEQ_TO_SN(sc)); sh = idx - start; if (sh > 64) { sh = (start - idx) + 0xff; bitmap = bitmap << sh; sh = 0; start = idx; } else if (sh < -64) sh = 0xff - (start - idx); else if (sh < 0) { sh = start - idx; start = idx; bitmap = bitmap << sh; sh = 0; } bitmap |= 1ULL << sh; D_TX_REPLY("start=%d bitmap=0x%llx\n", start, (unsigned long long)bitmap); } agg->bitmap = bitmap; agg->start_idx = start; D_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n", agg->frame_count, agg->start_idx, (unsigned long long)agg->bitmap); if (bitmap) agg->wait_for_ba = 1; } return 0; } /** * il4965_hdl_tx - Handle standard (non-aggregation) Tx response */ static void il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int idx = SEQ_TO_IDX(sequence); struct il_tx_queue *txq = &il->txq[txq_id]; struct sk_buff *skb; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *info; struct il4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le32_to_cpu(tx_resp->u.status); int uninitialized_var(tid); int sta_id; int freed; u8 *qc = NULL; unsigned long flags; if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " "is out of range [0-%d] %d %d\n", txq_id, idx, txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); return; } txq->time_stamp = jiffies; skb = txq->skbs[txq->q.read_ptr]; info = IEEE80211_SKB_CB(skb); memset(&info->status, 0, sizeof(info->status)); hdr = (struct ieee80211_hdr *) skb->data; if (ieee80211_is_data_qos(hdr->frame_control)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } sta_id = il4965_get_ra_sta_id(il, hdr); if (txq->sched_retry && unlikely(sta_id == IL_INVALID_STATION)) { IL_ERR("Station not known\n"); return; } spin_lock_irqsave(&il->sta_lock, flags); if (txq->sched_retry) { const u32 scd_ssn = il4965_get_scd_ssn(tx_resp); struct il_ht_agg *agg = NULL; WARN_ON(!qc); agg = &il->stations[sta_id].tid[tid].agg; il4965_tx_status_reply_tx(il, agg, tx_resp, txq_id, idx); /* check if BAR is needed */ if (tx_resp->frame_count == 1 && !il4965_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; if (txq->q.read_ptr != (scd_ssn & 0xff)) { idx = il_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd); D_TX_REPLY("Retry scheduler reclaim scd_ssn " "%d idx %d\n", scd_ssn, idx); freed = il4965_tx_queue_reclaim(il, txq_id, idx); if (qc) il4965_free_tfds_in_queue(il, sta_id, tid, freed); if (il->mac80211_registered && il_queue_space(&txq->q) > txq->q.low_mark && agg->state != IL_EMPTYING_HW_QUEUE_DELBA) il_wake_queue(il, txq); } } else { info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags |= il4965_tx_status_to_mac80211(status); il4965_hwrate_to_tx_control(il, le32_to_cpu(tx_resp->rate_n_flags), info); D_TX_REPLY("TXQ %d status %s (0x%08x) " "rate_n_flags 0x%x retries %d\n", txq_id, il4965_get_tx_fail_reason(status), status, le32_to_cpu(tx_resp->rate_n_flags), tx_resp->failure_frame); freed = il4965_tx_queue_reclaim(il, txq_id, idx); if (qc && likely(sta_id != IL_INVALID_STATION)) il4965_free_tfds_in_queue(il, sta_id, tid, freed); else if (sta_id == IL_INVALID_STATION) D_TX_REPLY("Station not known\n"); if (il->mac80211_registered && il_queue_space(&txq->q) > txq->q.low_mark) il_wake_queue(il, txq); } if (qc && likely(sta_id != IL_INVALID_STATION)) il4965_txq_check_empty(il, sta_id, tid, txq_id); il4965_check_abort_status(il, tx_resp->frame_count, status); spin_unlock_irqrestore(&il->sta_lock, flags); } /** * translate ucode response to mac80211 tx status control values */ void il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, struct ieee80211_tx_info *info) { struct ieee80211_tx_rate *r = &info->status.rates[0]; info->status.antenna = ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); if (rate_n_flags & RATE_MCS_HT_MSK) r->flags |= IEEE80211_TX_RC_MCS; if (rate_n_flags & RATE_MCS_GF_MSK) r->flags |= IEEE80211_TX_RC_GREEN_FIELD; if (rate_n_flags & RATE_MCS_HT40_MSK) r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (rate_n_flags & RATE_MCS_DUP_MSK) r->flags |= IEEE80211_TX_RC_DUP_DATA; if (rate_n_flags & RATE_MCS_SGI_MSK) r->flags |= IEEE80211_TX_RC_SHORT_GI; r->idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, info->band); } /** * il4965_hdl_compressed_ba - Handler for N_COMPRESSED_BA * * Handles block-acknowledge notification from device, which reports success * of frames sent via aggregation. */ void il4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; struct il_tx_queue *txq = NULL; struct il_ht_agg *agg; int idx; int sta_id; int tid; unsigned long flags; /* "flow" corresponds to Tx queue */ u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); /* "ssn" is start of block-ack Tx win, corresponds to idx * (in Tx queue's circular buffer) of first TFD/frame in win */ u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); if (scd_flow >= il->hw_params.max_txq_num) { IL_ERR("BUG_ON scd_flow is bigger than number of queues\n"); return; } txq = &il->txq[scd_flow]; sta_id = ba_resp->sta_id; tid = ba_resp->tid; agg = &il->stations[sta_id].tid[tid].agg; if (unlikely(agg->txq_id != scd_flow)) { /* * FIXME: this is a uCode bug which need to be addressed, * log the information and return for now! * since it is possible happen very often and in order * not to fill the syslog, don't enable the logging by default */ D_TX_REPLY("BA scd_flow %d does not match txq_id %d\n", scd_flow, agg->txq_id); return; } /* Find idx just before block-ack win */ idx = il_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); spin_lock_irqsave(&il->sta_lock, flags); D_TX_REPLY("N_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n", agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32, ba_resp->sta_id); D_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx," "scd_flow = " "%d, scd_ssn = %d\n", ba_resp->tid, ba_resp->seq_ctl, (unsigned long long)le64_to_cpu(ba_resp->bitmap), ba_resp->scd_flow, ba_resp->scd_ssn); D_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx\n", agg->start_idx, (unsigned long long)agg->bitmap); /* Update driver's record of ACK vs. not for each frame in win */ il4965_tx_status_reply_compressed_ba(il, agg, ba_resp); /* Release all TFDs before the SSN, i.e. all TFDs in front of * block-ack win (we assume that they've been successfully * transmitted ... if not, it's too late anyway). */ if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { /* calculate mac80211 ampdu sw queue to wake */ int freed = il4965_tx_queue_reclaim(il, scd_flow, idx); il4965_free_tfds_in_queue(il, sta_id, tid, freed); if (il_queue_space(&txq->q) > txq->q.low_mark && il->mac80211_registered && agg->state != IL_EMPTYING_HW_QUEUE_DELBA) il_wake_queue(il, txq); il4965_txq_check_empty(il, sta_id, tid, scd_flow); } spin_unlock_irqrestore(&il->sta_lock, flags); } #ifdef CONFIG_IWLEGACY_DEBUG const char * il4965_get_tx_fail_reason(u32 status) { #define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x #define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x switch (status & TX_STATUS_MSK) { case TX_STATUS_SUCCESS: return "SUCCESS"; TX_STATUS_POSTPONE(DELAY); TX_STATUS_POSTPONE(FEW_BYTES); TX_STATUS_POSTPONE(QUIET_PERIOD); TX_STATUS_POSTPONE(CALC_TTAK); TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); TX_STATUS_FAIL(SHORT_LIMIT); TX_STATUS_FAIL(LONG_LIMIT); TX_STATUS_FAIL(FIFO_UNDERRUN); TX_STATUS_FAIL(DRAIN_FLOW); TX_STATUS_FAIL(RFKILL_FLUSH); TX_STATUS_FAIL(LIFE_EXPIRE); TX_STATUS_FAIL(DEST_PS); TX_STATUS_FAIL(HOST_ABORTED); TX_STATUS_FAIL(BT_RETRY); TX_STATUS_FAIL(STA_INVALID); TX_STATUS_FAIL(FRAG_DROPPED); TX_STATUS_FAIL(TID_DISABLE); TX_STATUS_FAIL(FIFO_FLUSHED); TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); TX_STATUS_FAIL(PASSIVE_NO_RX); TX_STATUS_FAIL(NO_BEACON_ON_RADAR); } return "UNKNOWN"; #undef TX_STATUS_FAIL #undef TX_STATUS_POSTPONE } #endif /* CONFIG_IWLEGACY_DEBUG */ static struct il_link_quality_cmd * il4965_sta_alloc_lq(struct il_priv *il, u8 sta_id) { int i, r; struct il_link_quality_cmd *link_cmd; u32 rate_flags = 0; __le32 rate_n_flags; link_cmd = kzalloc(sizeof(struct il_link_quality_cmd), GFP_KERNEL); if (!link_cmd) { IL_ERR("Unable to allocate memory for LQ cmd.\n"); return NULL; } /* Set up the rate scaling to start at selected rate, fall back * all the way down to 1M in IEEE order, and then spin on 1M */ if (il->band == IEEE80211_BAND_5GHZ) r = RATE_6M_IDX; else r = RATE_1M_IDX; if (r >= IL_FIRST_CCK_RATE && r <= IL_LAST_CCK_RATE) rate_flags |= RATE_MCS_CCK_MSK; rate_flags |= il4965_first_antenna(il->hw_params. valid_tx_ant) << RATE_MCS_ANT_POS; rate_n_flags = cpu_to_le32(il_rates[r].plcp | rate_flags); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) link_cmd->rs_table[i].rate_n_flags = rate_n_flags; link_cmd->general_params.single_stream_ant_msk = il4965_first_antenna(il->hw_params.valid_tx_ant); link_cmd->general_params.dual_stream_ant_msk = il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params. valid_tx_ant); if (!link_cmd->general_params.dual_stream_ant_msk) { link_cmd->general_params.dual_stream_ant_msk = ANT_AB; } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) { link_cmd->general_params.dual_stream_ant_msk = il->hw_params.valid_tx_ant; } link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; link_cmd->agg_params.agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); link_cmd->sta_id = sta_id; return link_cmd; } /* * il4965_add_bssid_station - Add the special IBSS BSSID station * * Function sleeps. */ int il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r) { int ret; u8 sta_id; struct il_link_quality_cmd *link_cmd; unsigned long flags; if (sta_id_r) *sta_id_r = IL_INVALID_STATION; ret = il_add_station_common(il, addr, 0, NULL, &sta_id); if (ret) { IL_ERR("Unable to add station %pM\n", addr); return ret; } if (sta_id_r) *sta_id_r = sta_id; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].used |= IL_STA_LOCAL; spin_unlock_irqrestore(&il->sta_lock, flags); /* Set up default rate scaling table in device's station table */ link_cmd = il4965_sta_alloc_lq(il, sta_id); if (!link_cmd) { IL_ERR("Unable to initialize rate scaling for station %pM.\n", addr); return -ENOMEM; } ret = il_send_lq_cmd(il, link_cmd, CMD_SYNC, true); if (ret) IL_ERR("Link quality command failed (%d)\n", ret); spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].lq = link_cmd; spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } static int il4965_static_wepkey_cmd(struct il_priv *il, bool send_if_empty) { int i; u8 buff[sizeof(struct il_wep_cmd) + sizeof(struct il_wep_key) * WEP_KEYS_MAX]; struct il_wep_cmd *wep_cmd = (struct il_wep_cmd *)buff; size_t cmd_size = sizeof(struct il_wep_cmd); struct il_host_cmd cmd = { .id = C_WEPKEY, .data = wep_cmd, .flags = CMD_SYNC, }; bool not_empty = false; might_sleep(); memset(wep_cmd, 0, cmd_size + (sizeof(struct il_wep_key) * WEP_KEYS_MAX)); for (i = 0; i < WEP_KEYS_MAX; i++) { u8 key_size = il->_4965.wep_keys[i].key_size; wep_cmd->key[i].key_idx = i; if (key_size) { wep_cmd->key[i].key_offset = i; not_empty = true; } else wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; wep_cmd->key[i].key_size = key_size; memcpy(&wep_cmd->key[i].key[3], il->_4965.wep_keys[i].key, key_size); } wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; wep_cmd->num_keys = WEP_KEYS_MAX; cmd_size += sizeof(struct il_wep_key) * WEP_KEYS_MAX; cmd.len = cmd_size; if (not_empty || send_if_empty) return il_send_cmd(il, &cmd); else return 0; } int il4965_restore_default_wep_keys(struct il_priv *il) { lockdep_assert_held(&il->mutex); return il4965_static_wepkey_cmd(il, false); } int il4965_remove_default_wep_key(struct il_priv *il, struct ieee80211_key_conf *keyconf) { int ret; int idx = keyconf->keyidx; lockdep_assert_held(&il->mutex); D_WEP("Removing default WEP key: idx=%d\n", idx); memset(&il->_4965.wep_keys[idx], 0, sizeof(struct il_wep_key)); if (il_is_rfkill(il)) { D_WEP("Not sending C_WEPKEY command due to RFKILL.\n"); /* but keys in device are clear anyway so return success */ return 0; } ret = il4965_static_wepkey_cmd(il, 1); D_WEP("Remove default WEP key: idx=%d ret=%d\n", idx, ret); return ret; } int il4965_set_default_wep_key(struct il_priv *il, struct ieee80211_key_conf *keyconf) { int ret; int len = keyconf->keylen; int idx = keyconf->keyidx; lockdep_assert_held(&il->mutex); if (len != WEP_KEY_LEN_128 && len != WEP_KEY_LEN_64) { D_WEP("Bad WEP key length %d\n", keyconf->keylen); return -EINVAL; } keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; keyconf->hw_key_idx = HW_KEY_DEFAULT; il->stations[IL_AP_ID].keyinfo.cipher = keyconf->cipher; il->_4965.wep_keys[idx].key_size = len; memcpy(&il->_4965.wep_keys[idx].key, &keyconf->key, len); ret = il4965_static_wepkey_cmd(il, false); D_WEP("Set default WEP key: len=%d idx=%d ret=%d\n", len, idx, ret); return ret; } static int il4965_set_wep_dynamic_key_info(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { unsigned long flags; __le16 key_flags = 0; struct il_addsta_cmd sta_cmd; lockdep_assert_held(&il->mutex); keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); key_flags &= ~STA_KEY_FLG_INVALID; if (keyconf->keylen == WEP_KEY_LEN_128) key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; if (sta_id == il->hw_params.bcast_id) key_flags |= STA_KEY_MULTICAST_MSK; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].keyinfo.cipher = keyconf->cipher; il->stations[sta_id].keyinfo.keylen = keyconf->keylen; il->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); memcpy(&il->stations[sta_id].sta.key.key[3], keyconf->key, keyconf->keylen); if ((il->stations[sta_id].sta.key. key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) il->stations[sta_id].sta.key.key_offset = il_get_free_ucode_key_idx(il); /* else, we are overriding an existing key => no need to allocated room * in uCode. */ WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, "no space for a new key"); il->stations[sta_id].sta.key.key_flags = key_flags; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags); return il_send_add_sta(il, &sta_cmd, CMD_SYNC); } static int il4965_set_ccmp_dynamic_key_info(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { unsigned long flags; __le16 key_flags = 0; struct il_addsta_cmd sta_cmd; lockdep_assert_held(&il->mutex); key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); key_flags &= ~STA_KEY_FLG_INVALID; if (sta_id == il->hw_params.bcast_id) key_flags |= STA_KEY_MULTICAST_MSK; keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].keyinfo.cipher = keyconf->cipher; il->stations[sta_id].keyinfo.keylen = keyconf->keylen; memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen); if ((il->stations[sta_id].sta.key. key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) il->stations[sta_id].sta.key.key_offset = il_get_free_ucode_key_idx(il); /* else, we are overriding an existing key => no need to allocated room * in uCode. */ WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, "no space for a new key"); il->stations[sta_id].sta.key.key_flags = key_flags; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags); return il_send_add_sta(il, &sta_cmd, CMD_SYNC); } static int il4965_set_tkip_dynamic_key_info(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { unsigned long flags; int ret = 0; __le16 key_flags = 0; key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); key_flags &= ~STA_KEY_FLG_INVALID; if (sta_id == il->hw_params.bcast_id) key_flags |= STA_KEY_MULTICAST_MSK; keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].keyinfo.cipher = keyconf->cipher; il->stations[sta_id].keyinfo.keylen = 16; if ((il->stations[sta_id].sta.key. key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) il->stations[sta_id].sta.key.key_offset = il_get_free_ucode_key_idx(il); /* else, we are overriding an existing key => no need to allocated room * in uCode. */ WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, "no space for a new key"); il->stations[sta_id].sta.key.key_flags = key_flags; /* This copy is acutally not needed: we get the key with each TX */ memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, 16); memcpy(il->stations[sta_id].sta.key.key, keyconf->key, 16); spin_unlock_irqrestore(&il->sta_lock, flags); return ret; } void il4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) { u8 sta_id; unsigned long flags; int i; if (il_scan_cancel(il)) { /* cancel scan failed, just live w/ bad key and rely briefly on SW decryption */ return; } sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) return; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; for (i = 0; i < 5; i++) il->stations[sta_id].sta.key.tkip_rx_ttak[i] = cpu_to_le16(phase1key[i]); il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); spin_unlock_irqrestore(&il->sta_lock, flags); } int il4965_remove_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { unsigned long flags; u16 key_flags; u8 keyidx; struct il_addsta_cmd sta_cmd; lockdep_assert_held(&il->mutex); il->_4965.key_mapping_keys--; spin_lock_irqsave(&il->sta_lock, flags); key_flags = le16_to_cpu(il->stations[sta_id].sta.key.key_flags); keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3; D_WEP("Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); if (keyconf->keyidx != keyidx) { /* We need to remove a key with idx different that the one * in the uCode. This means that the key we need to remove has * been replaced by another one with different idx. * Don't do anything and return ok */ spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } if (il->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_INVALID) { IL_WARN("Removing wrong key %d 0x%x\n", keyconf->keyidx, key_flags); spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } if (!test_and_clear_bit (il->stations[sta_id].sta.key.key_offset, &il->ucode_key_table)) IL_ERR("idx %d not used in uCode key table.\n", il->stations[sta_id].sta.key.key_offset); memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key)); memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo)); il->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; il->stations[sta_id].sta.key.key_offset = keyconf->hw_key_idx; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; if (il_is_rfkill(il)) { D_WEP ("Not sending C_ADD_STA command because RFKILL enabled.\n"); spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags); return il_send_add_sta(il, &sta_cmd, CMD_SYNC); } int il4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { int ret; lockdep_assert_held(&il->mutex); il->_4965.key_mapping_keys++; keyconf->hw_key_idx = HW_KEY_DYNAMIC; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_CCMP: ret = il4965_set_ccmp_dynamic_key_info(il, keyconf, sta_id); break; case WLAN_CIPHER_SUITE_TKIP: ret = il4965_set_tkip_dynamic_key_info(il, keyconf, sta_id); break; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ret = il4965_set_wep_dynamic_key_info(il, keyconf, sta_id); break; default: IL_ERR("Unknown alg: %s cipher = %x\n", __func__, keyconf->cipher); ret = -EINVAL; } D_WEP("Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n", keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); return ret; } /** * il4965_alloc_bcast_station - add broadcast station into driver's station table. * * This adds the broadcast station into the driver's station table * and marks it driver active, so that it will be restored to the * device at the next best time. */ int il4965_alloc_bcast_station(struct il_priv *il) { struct il_link_quality_cmd *link_cmd; unsigned long flags; u8 sta_id; spin_lock_irqsave(&il->sta_lock, flags); sta_id = il_prep_station(il, il_bcast_addr, false, NULL); if (sta_id == IL_INVALID_STATION) { IL_ERR("Unable to prepare broadcast station\n"); spin_unlock_irqrestore(&il->sta_lock, flags); return -EINVAL; } il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE; il->stations[sta_id].used |= IL_STA_BCAST; spin_unlock_irqrestore(&il->sta_lock, flags); link_cmd = il4965_sta_alloc_lq(il, sta_id); if (!link_cmd) { IL_ERR ("Unable to initialize rate scaling for bcast station.\n"); return -ENOMEM; } spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].lq = link_cmd; spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } /** * il4965_update_bcast_station - update broadcast station's LQ command * * Only used by iwl4965. Placed here to have all bcast station management * code together. */ static int il4965_update_bcast_station(struct il_priv *il) { unsigned long flags; struct il_link_quality_cmd *link_cmd; u8 sta_id = il->hw_params.bcast_id; link_cmd = il4965_sta_alloc_lq(il, sta_id); if (!link_cmd) { IL_ERR("Unable to initialize rate scaling for bcast sta.\n"); return -ENOMEM; } spin_lock_irqsave(&il->sta_lock, flags); if (il->stations[sta_id].lq) kfree(il->stations[sta_id].lq); else D_INFO("Bcast sta rate scaling has not been initialized.\n"); il->stations[sta_id].lq = link_cmd; spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } int il4965_update_bcast_stations(struct il_priv *il) { return il4965_update_bcast_station(il); } /** * il4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table */ int il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid) { unsigned long flags; struct il_addsta_cmd sta_cmd; lockdep_assert_held(&il->mutex); /* Remove "disable" flag, to enable Tx for this TID */ spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; il->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags); return il_send_add_sta(il, &sta_cmd, CMD_SYNC); } int il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid, u16 ssn) { unsigned long flags; int sta_id; struct il_addsta_cmd sta_cmd; lockdep_assert_held(&il->mutex); sta_id = il_sta_id(sta); if (sta_id == IL_INVALID_STATION) return -ENXIO; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].sta.station_flags_msk = 0; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; il->stations[sta_id].sta.add_immediate_ba_tid = (u8) tid; il->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags); return il_send_add_sta(il, &sta_cmd, CMD_SYNC); } int il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid) { unsigned long flags; int sta_id; struct il_addsta_cmd sta_cmd; lockdep_assert_held(&il->mutex); sta_id = il_sta_id(sta); if (sta_id == IL_INVALID_STATION) { IL_ERR("Invalid station for AGG tid %d\n", tid); return -ENXIO; } spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].sta.station_flags_msk = 0; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; il->stations[sta_id].sta.remove_immediate_ba_tid = (u8) tid; il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags); return il_send_add_sta(il, &sta_cmd, CMD_SYNC); } void il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt) { unsigned long flags; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; il->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK; il->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); spin_unlock_irqrestore(&il->sta_lock, flags); } void il4965_update_chain_flags(struct il_priv *il) { if (il->ops->set_rxon_chain) { il->ops->set_rxon_chain(il); if (il->active.rx_chain != il->staging.rx_chain) il_commit_rxon(il); } } static void il4965_clear_free_frames(struct il_priv *il) { struct list_head *element; D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count); while (!list_empty(&il->free_frames)) { element = il->free_frames.next; list_del(element); kfree(list_entry(element, struct il_frame, list)); il->frames_count--; } if (il->frames_count) { IL_WARN("%d frames still in use. Did we lose one?\n", il->frames_count); il->frames_count = 0; } } static struct il_frame * il4965_get_free_frame(struct il_priv *il) { struct il_frame *frame; struct list_head *element; if (list_empty(&il->free_frames)) { frame = kzalloc(sizeof(*frame), GFP_KERNEL); if (!frame) { IL_ERR("Could not allocate frame!\n"); return NULL; } il->frames_count++; return frame; } element = il->free_frames.next; list_del(element); return list_entry(element, struct il_frame, list); } static void il4965_free_frame(struct il_priv *il, struct il_frame *frame) { memset(frame, 0, sizeof(*frame)); list_add(&frame->list, &il->free_frames); } static u32 il4965_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, int left) { lockdep_assert_held(&il->mutex); if (!il->beacon_skb) return 0; if (il->beacon_skb->len > left) return 0; memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len); return il->beacon_skb->len; } /* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ static void il4965_set_beacon_tim(struct il_priv *il, struct il_tx_beacon_cmd *tx_beacon_cmd, u8 * beacon, u32 frame_size) { u16 tim_idx; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; /* * The idx is relative to frame start but we start looking at the * variable-length part of the beacon. */ tim_idx = mgmt->u.beacon.variable - beacon; /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ while ((tim_idx < (frame_size - 2)) && (beacon[tim_idx] != WLAN_EID_TIM)) tim_idx += beacon[tim_idx + 1] + 2; /* If TIM field was found, set variables */ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); tx_beacon_cmd->tim_size = beacon[tim_idx + 1]; } else IL_WARN("Unable to find TIM Element in beacon\n"); } static unsigned int il4965_hw_get_beacon_cmd(struct il_priv *il, struct il_frame *frame) { struct il_tx_beacon_cmd *tx_beacon_cmd; u32 frame_size; u32 rate_flags; u32 rate; /* * We have to set up the TX command, the TX Beacon command, and the * beacon contents. */ lockdep_assert_held(&il->mutex); if (!il->beacon_enabled) { IL_ERR("Trying to build beacon without beaconing enabled\n"); return 0; } /* Initialize memory */ tx_beacon_cmd = &frame->u.beacon; memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); /* Set up TX beacon contents */ frame_size = il4965_fill_beacon_frame(il, tx_beacon_cmd->frame, sizeof(frame->u) - sizeof(*tx_beacon_cmd)); if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE)) return 0; if (!frame_size) return 0; /* Set up TX command fields */ tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; /* Set up TX beacon command fields */ il4965_set_beacon_tim(il, tx_beacon_cmd, (u8 *) tx_beacon_cmd->frame, frame_size); /* Set up packet rate and flags */ rate = il_get_lowest_plcp(il); il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); rate_flags = BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; if ((rate >= IL_FIRST_CCK_RATE) && (rate <= IL_LAST_CCK_RATE)) rate_flags |= RATE_MCS_CCK_MSK; tx_beacon_cmd->tx.rate_n_flags = cpu_to_le32(rate | rate_flags); return sizeof(*tx_beacon_cmd) + frame_size; } int il4965_send_beacon_cmd(struct il_priv *il) { struct il_frame *frame; unsigned int frame_size; int rc; frame = il4965_get_free_frame(il); if (!frame) { IL_ERR("Could not obtain free frame buffer for beacon " "command.\n"); return -ENOMEM; } frame_size = il4965_hw_get_beacon_cmd(il, frame); if (!frame_size) { IL_ERR("Error configuring the beacon command\n"); il4965_free_frame(il, frame); return -EINVAL; } rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]); il4965_free_frame(il, frame); return rc; } static inline dma_addr_t il4965_tfd_tb_get_addr(struct il_tfd *tfd, u8 idx) { struct il_tfd_tb *tb = &tfd->tbs[idx]; dma_addr_t addr = get_unaligned_le32(&tb->lo); if (sizeof(dma_addr_t) > sizeof(u32)) addr |= ((dma_addr_t) (le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; return addr; } static inline u16 il4965_tfd_tb_get_len(struct il_tfd *tfd, u8 idx) { struct il_tfd_tb *tb = &tfd->tbs[idx]; return le16_to_cpu(tb->hi_n_len) >> 4; } static inline void il4965_tfd_set_tb(struct il_tfd *tfd, u8 idx, dma_addr_t addr, u16 len) { struct il_tfd_tb *tb = &tfd->tbs[idx]; u16 hi_n_len = len << 4; put_unaligned_le32(addr, &tb->lo); if (sizeof(dma_addr_t) > sizeof(u32)) hi_n_len |= ((addr >> 16) >> 16) & 0xF; tb->hi_n_len = cpu_to_le16(hi_n_len); tfd->num_tbs = idx + 1; } static inline u8 il4965_tfd_get_num_tbs(struct il_tfd *tfd) { return tfd->num_tbs & 0x1f; } /** * il4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] * @il - driver ilate data * @txq - tx queue * * Does NOT advance any TFD circular buffer read/write idxes * Does NOT free the TFD itself (which is within circular buffer) */ void il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq) { struct il_tfd *tfd_tmp = (struct il_tfd *)txq->tfds; struct il_tfd *tfd; struct pci_dev *dev = il->pci_dev; int idx = txq->q.read_ptr; int i; int num_tbs; tfd = &tfd_tmp[idx]; /* Sanity check on number of chunks */ num_tbs = il4965_tfd_get_num_tbs(tfd); if (num_tbs >= IL_NUM_OF_TBS) { IL_ERR("Too many chunks: %i\n", num_tbs); /* @todo issue fatal error, it is quite serious situation */ return; } /* Unmap tx_cmd */ if (num_tbs) pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping), dma_unmap_len(&txq->meta[idx], len), PCI_DMA_BIDIRECTIONAL); /* Unmap chunks, if any. */ for (i = 1; i < num_tbs; i++) pci_unmap_single(dev, il4965_tfd_tb_get_addr(tfd, i), il4965_tfd_tb_get_len(tfd, i), PCI_DMA_TODEVICE); /* free SKB */ if (txq->skbs) { struct sk_buff *skb = txq->skbs[txq->q.read_ptr]; /* can be called from irqs-disabled context */ if (skb) { dev_kfree_skb_any(skb); txq->skbs[txq->q.read_ptr] = NULL; } } } int il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset, u8 pad) { struct il_queue *q; struct il_tfd *tfd, *tfd_tmp; u32 num_tbs; q = &txq->q; tfd_tmp = (struct il_tfd *)txq->tfds; tfd = &tfd_tmp[q->write_ptr]; if (reset) memset(tfd, 0, sizeof(*tfd)); num_tbs = il4965_tfd_get_num_tbs(tfd); /* Each TFD can point to a maximum 20 Tx buffers */ if (num_tbs >= IL_NUM_OF_TBS) { IL_ERR("Error can not send more than %d chunks\n", IL_NUM_OF_TBS); return -EINVAL; } BUG_ON(addr & ~DMA_BIT_MASK(36)); if (unlikely(addr & ~IL_TX_DMA_MASK)) IL_ERR("Unaligned address = %llx\n", (unsigned long long)addr); il4965_tfd_set_tb(tfd, num_tbs, addr, len); return 0; } /* * Tell nic where to find circular buffer of Tx Frame Descriptors for * given Tx queue, and enable the DMA channel used for that queue. * * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA * channels supported in hardware. */ int il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) { int txq_id = txq->q.id; /* Circular buffer (TFD queue in DRAM) physical base address */ il_wr(il, FH49_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8); return 0; } /****************************************************************************** * * Generic RX handler implementations * ******************************************************************************/ static void il4965_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_alive_resp *palive; struct delayed_work *pwork; palive = &pkt->u.alive_frame; D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", palive->is_valid, palive->ver_type, palive->ver_subtype); if (palive->ver_subtype == INITIALIZE_SUBTYPE) { D_INFO("Initialization Alive received.\n"); memcpy(&il->card_alive_init, &pkt->u.alive_frame, sizeof(struct il_init_alive_resp)); pwork = &il->init_alive_start; } else { D_INFO("Runtime Alive received.\n"); memcpy(&il->card_alive, &pkt->u.alive_frame, sizeof(struct il_alive_resp)); pwork = &il->alive_start; } /* We delay the ALIVE response by 5ms to * give the HW RF Kill time to activate... */ if (palive->is_valid == UCODE_VALID_OK) queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5)); else IL_WARN("uCode did not respond OK.\n"); } /** * il4965_bg_stats_periodic - Timer callback to queue stats * * This callback is provided in order to send a stats request. * * This timer function is continually reset to execute within * 60 seconds since the last N_STATS was received. We need to * ensure we receive the stats in order to update the temperature * used for calibrating the TXPOWER. */ static void il4965_bg_stats_periodic(unsigned long data) { struct il_priv *il = (struct il_priv *)data; if (test_bit(S_EXIT_PENDING, &il->status)) return; /* dont send host command if rf-kill is on */ if (!il_is_ready_rf(il)) return; il_send_stats_request(il, CMD_ASYNC, false); } static void il4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il4965_beacon_notif *beacon = (struct il4965_beacon_notif *)pkt->u.raw; #ifdef CONFIG_IWLEGACY_DEBUG u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n", le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK, beacon->beacon_notify_hdr.failure_frame, le32_to_cpu(beacon->ibss_mgr_status), le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); #endif il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); } static void il4965_perform_ct_kill_task(struct il_priv *il) { unsigned long flags; D_POWER("Stop all queues\n"); if (il->mac80211_registered) ieee80211_stop_queues(il->hw); _il_wr(il, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); _il_rd(il, CSR_UCODE_DRV_GP1); spin_lock_irqsave(&il->reg_lock, flags); if (likely(_il_grab_nic_access(il))) _il_release_nic_access(il); spin_unlock_irqrestore(&il->reg_lock, flags); } /* Handle notification from uCode that card's power state is changing * due to software, hardware, or critical temperature RFKILL */ static void il4965_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = il->status; D_RF_KILL("Card state received: HW:%s SW:%s CT:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", (flags & SW_CARD_DISABLED) ? "Kill" : "On", (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached"); if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) { _il_wr(il, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); if (!(flags & RXON_CARD_DISABLED)) { _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); } } if (flags & CT_CARD_DISABLED) il4965_perform_ct_kill_task(il); if (flags & HW_CARD_DISABLED) set_bit(S_RFKILL, &il->status); else clear_bit(S_RFKILL, &il->status); if (!(flags & RXON_CARD_DISABLED)) il_scan_cancel(il); if ((test_bit(S_RFKILL, &status) != test_bit(S_RFKILL, &il->status))) wiphy_rfkill_set_hw_state(il->hw->wiphy, test_bit(S_RFKILL, &il->status)); else wake_up(&il->wait_command_queue); } /** * il4965_setup_handlers - Initialize Rx handler callbacks * * Setup the RX handlers for each of the reply types sent from the uCode * to the host. * * This function chains into the hardware specific files for them to setup * any hardware specific handlers as well. */ static void il4965_setup_handlers(struct il_priv *il) { il->handlers[N_ALIVE] = il4965_hdl_alive; il->handlers[N_ERROR] = il_hdl_error; il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa; il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement; il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep; il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats; il->handlers[N_BEACON] = il4965_hdl_beacon; /* * The same handler is used for both the REPLY to a discrete * stats request from the host as well as for the periodic * stats notifications (after received beacons) from the uCode. */ il->handlers[C_STATS] = il4965_hdl_c_stats; il->handlers[N_STATS] = il4965_hdl_stats; il_setup_rx_scan_handlers(il); /* status change handler */ il->handlers[N_CARD_STATE] = il4965_hdl_card_state; il->handlers[N_MISSED_BEACONS] = il4965_hdl_missed_beacon; /* Rx handlers */ il->handlers[N_RX_PHY] = il4965_hdl_rx_phy; il->handlers[N_RX_MPDU] = il4965_hdl_rx; il->handlers[N_RX] = il4965_hdl_rx; /* block ack */ il->handlers[N_COMPRESSED_BA] = il4965_hdl_compressed_ba; /* Tx response */ il->handlers[C_TX] = il4965_hdl_tx; } /** * il4965_rx_handle - Main entry function for receiving responses from uCode * * Uses the il->handlers callback function array to invoke * the appropriate handlers, including command responses, * frame-received notifications, and other notifications. */ void il4965_rx_handle(struct il_priv *il) { struct il_rx_buf *rxb; struct il_rx_pkt *pkt; struct il_rx_queue *rxq = &il->rxq; u32 r, i; int reclaim; unsigned long flags; u8 fill_rx = 0; u32 count = 8; int total_empty; /* uCode's read idx (stored in shared DRAM) indicates the last Rx * buffer that the driver may process (last buffer filled by ucode). */ r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; i = rxq->read; /* Rx interrupt, but nothing sent from uCode */ if (i == r) D_RX("r = %d, i = %d\n", r, i); /* calculate total frames need to be restock after handling RX */ total_empty = r - rxq->write_actual; if (total_empty < 0) total_empty += RX_QUEUE_SIZE; if (total_empty > (RX_QUEUE_SIZE / 2)) fill_rx = 1; while (i != r) { int len; rxb = rxq->queue[i]; /* If an RXB doesn't have a Rx queue slot associated with it, * then a bug has been introduced in the queue refilling * routines -- catch it here */ BUG_ON(rxb == NULL); rxq->queue[i] = NULL; pci_unmap_page(il->pci_dev, rxb->page_dma, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); pkt = rxb_addr(rxb); len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; len += sizeof(u32); /* account for status word */ /* Reclaim a command buffer only if this packet is a response * to a (driver-originated) command. * If the packet (e.g. Rx frame) originated from uCode, * there is no command buffer to reclaim. * Ucode should set SEQ_RX_FRAME bit if ucode-originated, * but apparently a few don't get set; catch them here. */ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && (pkt->hdr.cmd != N_RX_PHY) && (pkt->hdr.cmd != N_RX) && (pkt->hdr.cmd != N_RX_MPDU) && (pkt->hdr.cmd != N_COMPRESSED_BA) && (pkt->hdr.cmd != N_STATS) && (pkt->hdr.cmd != C_TX); /* Based on type of command response or notification, * handle those that need handling via function in * handlers table. See il4965_setup_handlers() */ if (il->handlers[pkt->hdr.cmd]) { D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); il->isr_stats.handlers[pkt->hdr.cmd]++; il->handlers[pkt->hdr.cmd] (il, rxb); } else { /* No handling needed */ D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r, i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); } /* * XXX: After here, we should always check rxb->page * against NULL before touching it or its virtual * memory (pkt). Because some handler might have * already taken or freed the pages. */ if (reclaim) { /* Invoke any callbacks, transfer the buffer to caller, * and fire off the (possibly) blocking il_send_cmd() * as we reclaim the driver command queue */ if (rxb->page) il_tx_cmd_complete(il, rxb); else IL_WARN("Claim null rxb?\n"); } /* Reuse the page if possible. For notification packets and * SKBs that fail to Rx correctly, add them back into the * rx_free list for reuse later. */ spin_lock_irqsave(&rxq->lock, flags); if (rxb->page != NULL) { rxb->page_dma = pci_map_page(il->pci_dev, rxb->page, 0, PAGE_SIZE << il->hw_params. rx_page_order, PCI_DMA_FROMDEVICE); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; } else list_add_tail(&rxb->list, &rxq->rx_used); spin_unlock_irqrestore(&rxq->lock, flags); i = (i + 1) & RX_QUEUE_MASK; /* If there are a lot of unused frames, * restock the Rx queue so ucode wont assert. */ if (fill_rx) { count++; if (count >= 8) { rxq->read = i; il4965_rx_replenish_now(il); count = 0; } } } /* Backtrack one entry */ rxq->read = i; if (fill_rx) il4965_rx_replenish_now(il); else il4965_rx_queue_restock(il); } /* call this function to flush any scheduled tasklet */ static inline void il4965_synchronize_irq(struct il_priv *il) { /* wait to make sure we flush pending tasklet */ synchronize_irq(il->pci_dev->irq); tasklet_kill(&il->irq_tasklet); } static void il4965_irq_tasklet(struct il_priv *il) { u32 inta, handled = 0; u32 inta_fh; unsigned long flags; u32 i; #ifdef CONFIG_IWLEGACY_DEBUG u32 inta_mask; #endif spin_lock_irqsave(&il->lock, flags); /* Ack/clear/reset pending uCode interrupts. * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, * and will clear only when CSR_FH_INT_STATUS gets cleared. */ inta = _il_rd(il, CSR_INT); _il_wr(il, CSR_INT, inta); /* Ack/clear/reset pending flow-handler (DMA) interrupts. * Any new interrupts that happen after this, either while we're * in this tasklet, or later, will show up in next ISR/tasklet. */ inta_fh = _il_rd(il, CSR_FH_INT_STATUS); _il_wr(il, CSR_FH_INT_STATUS, inta_fh); #ifdef CONFIG_IWLEGACY_DEBUG if (il_get_debug_level(il) & IL_DL_ISR) { /* just for debug */ inta_mask = _il_rd(il, CSR_INT_MASK); D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask, inta_fh); } #endif spin_unlock_irqrestore(&il->lock, flags); /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not * atomic, make sure that inta covers all the interrupts that * we've discovered, even if FH interrupt came in just after * reading CSR_INT. */ if (inta_fh & CSR49_FH_INT_RX_MASK) inta |= CSR_INT_BIT_FH_RX; if (inta_fh & CSR49_FH_INT_TX_MASK) inta |= CSR_INT_BIT_FH_TX; /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { IL_ERR("Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ il_disable_interrupts(il); il->isr_stats.hw++; il_irq_handle_error(il); handled |= CSR_INT_BIT_HW_ERR; return; } #ifdef CONFIG_IWLEGACY_DEBUG if (il_get_debug_level(il) & (IL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { D_ISR("Scheduler finished to transmit " "the frame/frames.\n"); il->isr_stats.sch++; } /* Alive notification via Rx interrupt will do the real work */ if (inta & CSR_INT_BIT_ALIVE) { D_ISR("Alive interrupt\n"); il->isr_stats.alive++; } } #endif /* Safely ignore these bits for debug checks below */ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); /* HW RF KILL switch toggled */ if (inta & CSR_INT_BIT_RF_KILL) { int hw_rf_kill = 0; if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) hw_rf_kill = 1; IL_WARN("RF_KILL bit toggled to %s.\n", hw_rf_kill ? "disable radio" : "enable radio"); il->isr_stats.rfkill++; /* driver only loads ucode once setting the interface up. * the driver allows loading the ucode even if the radio * is killed. Hence update the killswitch state here. The * rfkill handler will care about restarting if needed. */ if (!test_bit(S_ALIVE, &il->status)) { if (hw_rf_kill) set_bit(S_RFKILL, &il->status); else clear_bit(S_RFKILL, &il->status); wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); } handled |= CSR_INT_BIT_RF_KILL; } /* Chip got too hot and stopped itself */ if (inta & CSR_INT_BIT_CT_KILL) { IL_ERR("Microcode CT kill error detected.\n"); il->isr_stats.ctkill++; handled |= CSR_INT_BIT_CT_KILL; } /* Error detected by uCode */ if (inta & CSR_INT_BIT_SW_ERR) { IL_ERR("Microcode SW error detected. " " Restarting 0x%X.\n", inta); il->isr_stats.sw++; il_irq_handle_error(il); handled |= CSR_INT_BIT_SW_ERR; } /* * uCode wakes up after power-down sleep. * Tell device about any new tx or host commands enqueued, * and about any Rx buffers made available while asleep. */ if (inta & CSR_INT_BIT_WAKEUP) { D_ISR("Wakeup interrupt\n"); il_rx_queue_update_write_ptr(il, &il->rxq); for (i = 0; i < il->hw_params.max_txq_num; i++) il_txq_update_write_ptr(il, &il->txq[i]); il->isr_stats.wakeup++; handled |= CSR_INT_BIT_WAKEUP; } /* All uCode command responses, including Tx command responses, * Rx "responses" (frame-received notification), and other * notifications from uCode come through here*/ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { il4965_rx_handle(il); il->isr_stats.rx++; handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); } /* This "Tx" DMA channel is used only for loading uCode */ if (inta & CSR_INT_BIT_FH_TX) { D_ISR("uCode load interrupt\n"); il->isr_stats.tx++; handled |= CSR_INT_BIT_FH_TX; /* Wake up uCode load routine, now that load is complete */ il->ucode_write_complete = 1; wake_up(&il->wait_command_queue); } if (inta & ~handled) { IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled); il->isr_stats.unhandled++; } if (inta & ~(il->inta_mask)) { IL_WARN("Disabled INTA bits 0x%08x were pending\n", inta & ~il->inta_mask); IL_WARN(" with FH49_INT = 0x%08x\n", inta_fh); } /* Re-enable all interrupts */ /* only Re-enable if disabled by irq */ if (test_bit(S_INT_ENABLED, &il->status)) il_enable_interrupts(il); /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) il_enable_rfkill_int(il); #ifdef CONFIG_IWLEGACY_DEBUG if (il_get_debug_level(il) & (IL_DL_ISR)) { inta = _il_rd(il, CSR_INT); inta_mask = _il_rd(il, CSR_INT_MASK); inta_fh = _il_rd(il, CSR_FH_INT_STATUS); D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); } #endif } /***************************************************************************** * * sysfs attributes * *****************************************************************************/ #ifdef CONFIG_IWLEGACY_DEBUG /* * The following adds a new attribute to the sysfs representation * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) * used for controlling the debug level. * * See the level definitions in iwl for details. * * The debug_level being managed using sysfs below is a per device debug * level that is used instead of the global debug level if it (the per * device debug level) is set. */ static ssize_t il4965_show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); return sprintf(buf, "0x%08X\n", il_get_debug_level(il)); } static ssize_t il4965_store_debug_level(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); unsigned long val; int ret; ret = strict_strtoul(buf, 0, &val); if (ret) IL_ERR("%s is not in hex or decimal form.\n", buf); else il->debug_level = val; return strnlen(buf, count); } static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il4965_show_debug_level, il4965_store_debug_level); #endif /* CONFIG_IWLEGACY_DEBUG */ static ssize_t il4965_show_temperature(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); if (!il_is_alive(il)) return -EAGAIN; return sprintf(buf, "%d\n", il->temperature); } static DEVICE_ATTR(temperature, S_IRUGO, il4965_show_temperature, NULL); static ssize_t il4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); if (!il_is_ready_rf(il)) return sprintf(buf, "off\n"); else return sprintf(buf, "%d\n", il->tx_power_user_lmt); } static ssize_t il4965_store_tx_power(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); unsigned long val; int ret; ret = strict_strtoul(buf, 10, &val); if (ret) IL_INFO("%s is not in decimal form.\n", buf); else { ret = il_set_tx_power(il, val, false); if (ret) IL_ERR("failed setting tx power (0x%d).\n", ret); else ret = count; } return ret; } static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il4965_show_tx_power, il4965_store_tx_power); static struct attribute *il_sysfs_entries[] = { &dev_attr_temperature.attr, &dev_attr_tx_power.attr, #ifdef CONFIG_IWLEGACY_DEBUG &dev_attr_debug_level.attr, #endif NULL }; static struct attribute_group il_attribute_group = { .name = NULL, /* put in device directory */ .attrs = il_sysfs_entries, }; /****************************************************************************** * * uCode download functions * ******************************************************************************/ static void il4965_dealloc_ucode_pci(struct il_priv *il) { il_free_fw_desc(il->pci_dev, &il->ucode_code); il_free_fw_desc(il->pci_dev, &il->ucode_data); il_free_fw_desc(il->pci_dev, &il->ucode_data_backup); il_free_fw_desc(il->pci_dev, &il->ucode_init); il_free_fw_desc(il->pci_dev, &il->ucode_init_data); il_free_fw_desc(il->pci_dev, &il->ucode_boot); } static void il4965_nic_start(struct il_priv *il) { /* Remove all resets to allow NIC to operate */ _il_wr(il, CSR_RESET, 0); } static void il4965_ucode_callback(const struct firmware *ucode_raw, void *context); static int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length); static int __must_check il4965_request_firmware(struct il_priv *il, bool first) { const char *name_pre = il->cfg->fw_name_pre; char tag[8]; if (first) { il->fw_idx = il->cfg->ucode_api_max; sprintf(tag, "%d", il->fw_idx); } else { il->fw_idx--; sprintf(tag, "%d", il->fw_idx); } if (il->fw_idx < il->cfg->ucode_api_min) { IL_ERR("no suitable firmware found!\n"); return -ENOENT; } sprintf(il->firmware_name, "%s%s%s", name_pre, tag, ".ucode"); D_INFO("attempting to load firmware '%s'\n", il->firmware_name); return request_firmware_nowait(THIS_MODULE, 1, il->firmware_name, &il->pci_dev->dev, GFP_KERNEL, il, il4965_ucode_callback); } struct il4965_firmware_pieces { const void *inst, *data, *init, *init_data, *boot; size_t inst_size, data_size, init_size, init_data_size, boot_size; }; static int il4965_load_firmware(struct il_priv *il, const struct firmware *ucode_raw, struct il4965_firmware_pieces *pieces) { struct il_ucode_header *ucode = (void *)ucode_raw->data; u32 api_ver, hdr_size; const u8 *src; il->ucode_ver = le32_to_cpu(ucode->ver); api_ver = IL_UCODE_API(il->ucode_ver); switch (api_ver) { default: case 0: case 1: case 2: hdr_size = 24; if (ucode_raw->size < hdr_size) { IL_ERR("File size too small!\n"); return -EINVAL; } pieces->inst_size = le32_to_cpu(ucode->v1.inst_size); pieces->data_size = le32_to_cpu(ucode->v1.data_size); pieces->init_size = le32_to_cpu(ucode->v1.init_size); pieces->init_data_size = le32_to_cpu(ucode->v1.init_data_size); pieces->boot_size = le32_to_cpu(ucode->v1.boot_size); src = ucode->v1.data; break; } /* Verify size of file vs. image size info in file's header */ if (ucode_raw->size != hdr_size + pieces->inst_size + pieces->data_size + pieces->init_size + pieces->init_data_size + pieces->boot_size) { IL_ERR("uCode file size %d does not match expected size\n", (int)ucode_raw->size); return -EINVAL; } pieces->inst = src; src += pieces->inst_size; pieces->data = src; src += pieces->data_size; pieces->init = src; src += pieces->init_size; pieces->init_data = src; src += pieces->init_data_size; pieces->boot = src; src += pieces->boot_size; return 0; } /** * il4965_ucode_callback - callback when firmware was loaded * * If loaded successfully, copies the firmware into buffers * for the card to fetch (via DMA). */ static void il4965_ucode_callback(const struct firmware *ucode_raw, void *context) { struct il_priv *il = context; struct il_ucode_header *ucode; int err; struct il4965_firmware_pieces pieces; const unsigned int api_max = il->cfg->ucode_api_max; const unsigned int api_min = il->cfg->ucode_api_min; u32 api_ver; u32 max_probe_length = 200; u32 standard_phy_calibration_size = IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; memset(&pieces, 0, sizeof(pieces)); if (!ucode_raw) { if (il->fw_idx <= il->cfg->ucode_api_max) IL_ERR("request for firmware file '%s' failed.\n", il->firmware_name); goto try_again; } D_INFO("Loaded firmware file '%s' (%zd bytes).\n", il->firmware_name, ucode_raw->size); /* Make sure that we got at least the API version number */ if (ucode_raw->size < 4) { IL_ERR("File size way too small!\n"); goto try_again; } /* Data from ucode file: header followed by uCode images */ ucode = (struct il_ucode_header *)ucode_raw->data; err = il4965_load_firmware(il, ucode_raw, &pieces); if (err) goto try_again; api_ver = IL_UCODE_API(il->ucode_ver); /* * api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely * on the API version read from firmware header from here on forward */ if (api_ver < api_min || api_ver > api_max) { IL_ERR("Driver unable to support your firmware API. " "Driver supports v%u, firmware is v%u.\n", api_max, api_ver); goto try_again; } if (api_ver != api_max) IL_ERR("Firmware has old API version. Expected v%u, " "got v%u. New firmware can be obtained " "from http://www.intellinuxwireless.org.\n", api_max, api_ver); IL_INFO("loaded firmware version %u.%u.%u.%u\n", IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version), "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); /* * For any of the failures below (before allocating pci memory) * we will try to load a version with a smaller API -- maybe the * user just got a corrupted version of the latest API. */ D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); D_INFO("f/w package hdr runtime inst size = %Zd\n", pieces.inst_size); D_INFO("f/w package hdr runtime data size = %Zd\n", pieces.data_size); D_INFO("f/w package hdr init inst size = %Zd\n", pieces.init_size); D_INFO("f/w package hdr init data size = %Zd\n", pieces.init_data_size); D_INFO("f/w package hdr boot inst size = %Zd\n", pieces.boot_size); /* Verify that uCode images will fit in card's SRAM */ if (pieces.inst_size > il->hw_params.max_inst_size) { IL_ERR("uCode instr len %Zd too large to fit in\n", pieces.inst_size); goto try_again; } if (pieces.data_size > il->hw_params.max_data_size) { IL_ERR("uCode data len %Zd too large to fit in\n", pieces.data_size); goto try_again; } if (pieces.init_size > il->hw_params.max_inst_size) { IL_ERR("uCode init instr len %Zd too large to fit in\n", pieces.init_size); goto try_again; } if (pieces.init_data_size > il->hw_params.max_data_size) { IL_ERR("uCode init data len %Zd too large to fit in\n", pieces.init_data_size); goto try_again; } if (pieces.boot_size > il->hw_params.max_bsm_size) { IL_ERR("uCode boot instr len %Zd too large to fit in\n", pieces.boot_size); goto try_again; } /* Allocate ucode buffers for card's bus-master loading ... */ /* Runtime instructions and 2 copies of data: * 1) unmodified from disk * 2) backup cache for save/restore during power-downs */ il->ucode_code.len = pieces.inst_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_code); il->ucode_data.len = pieces.data_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_data); il->ucode_data_backup.len = pieces.data_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup); if (!il->ucode_code.v_addr || !il->ucode_data.v_addr || !il->ucode_data_backup.v_addr) goto err_pci_alloc; /* Initialization instructions and data */ if (pieces.init_size && pieces.init_data_size) { il->ucode_init.len = pieces.init_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_init); il->ucode_init_data.len = pieces.init_data_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data); if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr) goto err_pci_alloc; } /* Bootstrap (instructions only, no data) */ if (pieces.boot_size) { il->ucode_boot.len = pieces.boot_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_boot); if (!il->ucode_boot.v_addr) goto err_pci_alloc; } /* Now that we can no longer fail, copy information */ il->sta_key_max_num = STA_KEY_MAX_NUM; /* Copy images into buffers for card's bus-master reads ... */ /* Runtime instructions (first block of data in file) */ D_INFO("Copying (but not loading) uCode instr len %Zd\n", pieces.inst_size); memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size); D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", il->ucode_code.v_addr, (u32) il->ucode_code.p_addr); /* * Runtime data * NOTE: Copy into backup buffer will be done in il_up() */ D_INFO("Copying (but not loading) uCode data len %Zd\n", pieces.data_size); memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size); memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size); /* Initialization instructions */ if (pieces.init_size) { D_INFO("Copying (but not loading) init instr len %Zd\n", pieces.init_size); memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size); } /* Initialization data */ if (pieces.init_data_size) { D_INFO("Copying (but not loading) init data len %Zd\n", pieces.init_data_size); memcpy(il->ucode_init_data.v_addr, pieces.init_data, pieces.init_data_size); } /* Bootstrap instructions */ D_INFO("Copying (but not loading) boot instr len %Zd\n", pieces.boot_size); memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size); /* * figure out the offset of chain noise reset and gain commands * base on the size of standard phy calibration commands table size */ il->_4965.phy_calib_chain_noise_reset_cmd = standard_phy_calibration_size; il->_4965.phy_calib_chain_noise_gain_cmd = standard_phy_calibration_size + 1; /************************************************** * This is still part of probe() in a sense... * * 9. Setup and register with mac80211 and debugfs **************************************************/ err = il4965_mac_setup_register(il, max_probe_length); if (err) goto out_unbind; err = il_dbgfs_register(il, DRV_NAME); if (err) IL_ERR("failed to create debugfs files. Ignoring error: %d\n", err); err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group); if (err) { IL_ERR("failed to create sysfs device attributes\n"); goto out_unbind; } /* We have our copies now, allow OS release its copies */ release_firmware(ucode_raw); complete(&il->_4965.firmware_loading_complete); return; try_again: /* try next, if any */ if (il4965_request_firmware(il, false)) goto out_unbind; release_firmware(ucode_raw); return; err_pci_alloc: IL_ERR("failed to allocate pci memory\n"); il4965_dealloc_ucode_pci(il); out_unbind: complete(&il->_4965.firmware_loading_complete); device_release_driver(&il->pci_dev->dev); release_firmware(ucode_raw); } static const char *const desc_lookup_text[] = { "OK", "FAIL", "BAD_PARAM", "BAD_CHECKSUM", "NMI_INTERRUPT_WDG", "SYSASSERT", "FATAL_ERROR", "BAD_COMMAND", "HW_ERROR_TUNE_LOCK", "HW_ERROR_TEMPERATURE", "ILLEGAL_CHAN_FREQ", "VCC_NOT_STBL", "FH49_ERROR", "NMI_INTERRUPT_HOST", "NMI_INTERRUPT_ACTION_PT", "NMI_INTERRUPT_UNKNOWN", "UCODE_VERSION_MISMATCH", "HW_ERROR_ABS_LOCK", "HW_ERROR_CAL_LOCK_FAIL", "NMI_INTERRUPT_INST_ACTION_PT", "NMI_INTERRUPT_DATA_ACTION_PT", "NMI_TRM_HW_ER", "NMI_INTERRUPT_TRM", "NMI_INTERRUPT_BREAK_POINT", "DEBUG_0", "DEBUG_1", "DEBUG_2", "DEBUG_3", }; static struct { char *name; u8 num; } advanced_lookup[] = { { "NMI_INTERRUPT_WDG", 0x34}, { "SYSASSERT", 0x35}, { "UCODE_VERSION_MISMATCH", 0x37}, { "BAD_COMMAND", 0x38}, { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C}, { "FATAL_ERROR", 0x3D}, { "NMI_TRM_HW_ERR", 0x46}, { "NMI_INTERRUPT_TRM", 0x4C}, { "NMI_INTERRUPT_BREAK_POINT", 0x54}, { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C}, { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64}, { "NMI_INTERRUPT_HOST", 0x66}, { "NMI_INTERRUPT_ACTION_PT", 0x7C}, { "NMI_INTERRUPT_UNKNOWN", 0x84}, { "NMI_INTERRUPT_INST_ACTION_PT", 0x86}, { "ADVANCED_SYSASSERT", 0},}; static const char * il4965_desc_lookup(u32 num) { int i; int max = ARRAY_SIZE(desc_lookup_text); if (num < max) return desc_lookup_text[num]; max = ARRAY_SIZE(advanced_lookup) - 1; for (i = 0; i < max; i++) { if (advanced_lookup[i].num == num) break; } return advanced_lookup[i].name; } #define ERROR_START_OFFSET (1 * sizeof(u32)) #define ERROR_ELEM_SIZE (7 * sizeof(u32)) void il4965_dump_nic_error_log(struct il_priv *il) { u32 data2, line; u32 desc, time, count, base, data1; u32 blink1, blink2, ilink1, ilink2; u32 pc, hcmd; if (il->ucode_type == UCODE_INIT) base = le32_to_cpu(il->card_alive_init.error_event_table_ptr); else base = le32_to_cpu(il->card_alive.error_event_table_ptr); if (!il->ops->is_valid_rtc_data_addr(base)) { IL_ERR("Not valid error log pointer 0x%08X for %s uCode\n", base, (il->ucode_type == UCODE_INIT) ? "Init" : "RT"); return; } count = il_read_targ_mem(il, base); if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { IL_ERR("Start IWL Error Log Dump:\n"); IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count); } desc = il_read_targ_mem(il, base + 1 * sizeof(u32)); il->isr_stats.err_code = desc; pc = il_read_targ_mem(il, base + 2 * sizeof(u32)); blink1 = il_read_targ_mem(il, base + 3 * sizeof(u32)); blink2 = il_read_targ_mem(il, base + 4 * sizeof(u32)); ilink1 = il_read_targ_mem(il, base + 5 * sizeof(u32)); ilink2 = il_read_targ_mem(il, base + 6 * sizeof(u32)); data1 = il_read_targ_mem(il, base + 7 * sizeof(u32)); data2 = il_read_targ_mem(il, base + 8 * sizeof(u32)); line = il_read_targ_mem(il, base + 9 * sizeof(u32)); time = il_read_targ_mem(il, base + 11 * sizeof(u32)); hcmd = il_read_targ_mem(il, base + 22 * sizeof(u32)); IL_ERR("Desc Time " "data1 data2 line\n"); IL_ERR("%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n", il4965_desc_lookup(desc), desc, time, data1, data2, line); IL_ERR("pc blink1 blink2 ilink1 ilink2 hcmd\n"); IL_ERR("0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", pc, blink1, blink2, ilink1, ilink2, hcmd); } static void il4965_rf_kill_ct_config(struct il_priv *il) { struct il_ct_kill_config cmd; unsigned long flags; int ret = 0; spin_lock_irqsave(&il->lock, flags); _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); spin_unlock_irqrestore(&il->lock, flags); cmd.critical_temperature_R = cpu_to_le32(il->hw_params.ct_kill_threshold); ret = il_send_cmd_pdu(il, C_CT_KILL_CONFIG, sizeof(cmd), &cmd); if (ret) IL_ERR("C_CT_KILL_CONFIG failed\n"); else D_INFO("C_CT_KILL_CONFIG " "succeeded, " "critical temperature is %d\n", il->hw_params.ct_kill_threshold); } static const s8 default_queue_to_tx_fifo[] = { IL_TX_FIFO_VO, IL_TX_FIFO_VI, IL_TX_FIFO_BE, IL_TX_FIFO_BK, IL49_CMD_FIFO_NUM, IL_TX_FIFO_UNUSED, IL_TX_FIFO_UNUSED, }; #define IL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) static int il4965_alive_notify(struct il_priv *il) { u32 a; unsigned long flags; int i, chan; u32 reg_val; spin_lock_irqsave(&il->lock, flags); /* Clear 4965's internal Tx Scheduler data base */ il->scd_base_addr = il_rd_prph(il, IL49_SCD_SRAM_BASE_ADDR); a = il->scd_base_addr + IL49_SCD_CONTEXT_DATA_OFFSET; for (; a < il->scd_base_addr + IL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4) il_write_targ_mem(il, a, 0); for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET; a += 4) il_write_targ_mem(il, a, 0); for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(il->hw_params.max_txq_num); a += 4) il_write_targ_mem(il, a, 0); /* Tel 4965 where to find Tx byte count tables */ il_wr_prph(il, IL49_SCD_DRAM_BASE_ADDR, il->scd_bc_tbls.dma >> 10); /* Enable DMA channel */ for (chan = 0; chan < FH49_TCSR_CHNL_NUM; chan++) il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(chan), FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); /* Update FH chicken bits */ reg_val = il_rd(il, FH49_TX_CHICKEN_BITS_REG); il_wr(il, FH49_TX_CHICKEN_BITS_REG, reg_val | FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); /* Disable chain mode for all queues */ il_wr_prph(il, IL49_SCD_QUEUECHAIN_SEL, 0); /* Initialize each Tx queue (including the command queue) */ for (i = 0; i < il->hw_params.max_txq_num; i++) { /* TFD circular buffer read/write idxes */ il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(i), 0); il_wr(il, HBUS_TARG_WRPTR, 0 | (i << 8)); /* Max Tx Window size for Scheduler-ACK mode */ il_write_targ_mem(il, il->scd_base_addr + IL49_SCD_CONTEXT_QUEUE_OFFSET(i), (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); /* Frame limit */ il_write_targ_mem(il, il->scd_base_addr + IL49_SCD_CONTEXT_QUEUE_OFFSET(i) + sizeof(u32), (SCD_FRAME_LIMIT << IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); } il_wr_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << il->hw_params.max_txq_num) - 1); /* Activate all Tx DMA/FIFO channels */ il4965_txq_set_sched(il, IL_MASK(0, 6)); il4965_set_wr_ptrs(il, IL_DEFAULT_CMD_QUEUE_NUM, 0); /* make sure all queue are not stopped */ memset(&il->queue_stopped[0], 0, sizeof(il->queue_stopped)); for (i = 0; i < 4; i++) atomic_set(&il->queue_stop_count[i], 0); /* reset to 0 to enable all the queue first */ il->txq_ctx_active_msk = 0; /* Map each Tx/cmd queue to its corresponding fifo */ BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7); for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { int ac = default_queue_to_tx_fifo[i]; il_txq_ctx_activate(il, i); if (ac == IL_TX_FIFO_UNUSED) continue; il4965_tx_queue_set_status(il, &il->txq[i], ac, 0); } spin_unlock_irqrestore(&il->lock, flags); return 0; } /** * il4965_alive_start - called after N_ALIVE notification received * from protocol/runtime uCode (initialization uCode's * Alive gets handled by il_init_alive_start()). */ static void il4965_alive_start(struct il_priv *il) { int ret = 0; D_INFO("Runtime Alive received.\n"); if (il->card_alive.is_valid != UCODE_VALID_OK) { /* We had an error bringing up the hardware, so take it * all the way back down so we can try again */ D_INFO("Alive failed.\n"); goto restart; } /* Initialize uCode has loaded Runtime uCode ... verify inst image. * This is a paranoid check, because we would not have gotten the * "runtime" alive if code weren't properly loaded. */ if (il4965_verify_ucode(il)) { /* Runtime instruction load was bad; * take it all the way back down so we can try again */ D_INFO("Bad runtime uCode load.\n"); goto restart; } ret = il4965_alive_notify(il); if (ret) { IL_WARN("Could not complete ALIVE transition [ntf]: %d\n", ret); goto restart; } /* After the ALIVE response, we can send host commands to the uCode */ set_bit(S_ALIVE, &il->status); /* Enable watchdog to monitor the driver tx queues */ il_setup_watchdog(il); if (il_is_rfkill(il)) return; ieee80211_wake_queues(il->hw); il->active_rate = RATES_MASK; if (il_is_associated(il)) { struct il_rxon_cmd *active_rxon = (struct il_rxon_cmd *)&il->active; /* apply any changes in staging */ il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; } else { /* Initialize our rx_config data */ il_connection_init_rx_config(il); if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); } /* Configure bluetooth coexistence if enabled */ il_send_bt_config(il); il4965_reset_run_time_calib(il); set_bit(S_READY, &il->status); /* Configure the adapter for unassociated operation */ il_commit_rxon(il); /* At this point, the NIC is initialized and operational */ il4965_rf_kill_ct_config(il); D_INFO("ALIVE processing complete.\n"); wake_up(&il->wait_command_queue); il_power_update_mode(il, true); D_INFO("Updated power mode\n"); return; restart: queue_work(il->workqueue, &il->restart); } static void il4965_cancel_deferred_work(struct il_priv *il); static void __il4965_down(struct il_priv *il) { unsigned long flags; int exit_pending; D_INFO(DRV_NAME " is going down\n"); il_scan_cancel_timeout(il, 200); exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status); /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set * to prevent rearm timer */ del_timer_sync(&il->watchdog); il_clear_ucode_stations(il); /* FIXME: race conditions ? */ spin_lock_irq(&il->sta_lock); /* * Remove all key information that is not stored as part * of station information since mac80211 may not have had * a chance to remove all the keys. When device is * reconfigured by mac80211 after an error all keys will * be reconfigured. */ memset(il->_4965.wep_keys, 0, sizeof(il->_4965.wep_keys)); il->_4965.key_mapping_keys = 0; spin_unlock_irq(&il->sta_lock); il_dealloc_bcast_stations(il); il_clear_driver_stations(il); /* Unblock any waiting calls */ wake_up_all(&il->wait_command_queue); /* Wipe out the EXIT_PENDING status bit if we are not actually * exiting the module */ if (!exit_pending) clear_bit(S_EXIT_PENDING, &il->status); /* stop and reset the on-board processor */ _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); /* tell the device to stop sending interrupts */ spin_lock_irqsave(&il->lock, flags); il_disable_interrupts(il); spin_unlock_irqrestore(&il->lock, flags); il4965_synchronize_irq(il); if (il->mac80211_registered) ieee80211_stop_queues(il->hw); /* If we have not previously called il_init() then * clear all bits but the RF Kill bit and return */ if (!il_is_init(il)) { il->status = test_bit(S_RFKILL, &il->status) << S_RFKILL | test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; goto exit; } /* ...otherwise clear out all the status bits but the RF Kill * bit and continue taking the NIC down. */ il->status &= test_bit(S_RFKILL, &il->status) << S_RFKILL | test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR | test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; /* * We disabled and synchronized interrupt, and priv->mutex is taken, so * here is the only thread which will program device registers, but * still have lockdep assertions, so we are taking reg_lock. */ spin_lock_irq(&il->reg_lock); /* FIXME: il_grab_nic_access if rfkill is off ? */ il4965_txq_ctx_stop(il); il4965_rxq_stop(il); /* Power-down device's busmaster DMA clocks */ _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(5); /* Make sure (redundant) we've released our request to stay awake */ _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* Stop the device, and put it in low power state */ _il_apm_stop(il); spin_unlock_irq(&il->reg_lock); il4965_txq_ctx_unmap(il); exit: memset(&il->card_alive, 0, sizeof(struct il_alive_resp)); dev_kfree_skb(il->beacon_skb); il->beacon_skb = NULL; /* clear out any free frames */ il4965_clear_free_frames(il); } static void il4965_down(struct il_priv *il) { mutex_lock(&il->mutex); __il4965_down(il); mutex_unlock(&il->mutex); il4965_cancel_deferred_work(il); } static void il4965_set_hw_ready(struct il_priv *il) { int ret; il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); /* See if we got it */ ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, 100); if (ret >= 0) il->hw_ready = true; D_INFO("hardware %s ready\n", (il->hw_ready) ? "" : "not"); } static void il4965_prepare_card_hw(struct il_priv *il) { int ret; il->hw_ready = false; il4965_set_hw_ready(il); if (il->hw_ready) return; /* If HW is not ready, prepare the conditions to check again */ il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); /* HW should be ready by now, check again. */ if (ret != -ETIMEDOUT) il4965_set_hw_ready(il); } #define MAX_HW_RESTARTS 5 static int __il4965_up(struct il_priv *il) { int i; int ret; if (test_bit(S_EXIT_PENDING, &il->status)) { IL_WARN("Exit pending; will not bring the NIC up\n"); return -EIO; } if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) { IL_ERR("ucode not available for device bringup\n"); return -EIO; } ret = il4965_alloc_bcast_station(il); if (ret) { il_dealloc_bcast_stations(il); return ret; } il4965_prepare_card_hw(il); if (!il->hw_ready) { IL_ERR("HW not ready\n"); return -EIO; } /* If platform's RF_KILL switch is NOT set to KILL */ if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) clear_bit(S_RFKILL, &il->status); else { set_bit(S_RFKILL, &il->status); wiphy_rfkill_set_hw_state(il->hw->wiphy, true); il_enable_rfkill_int(il); IL_WARN("Radio disabled by HW RF Kill switch\n"); return 0; } _il_wr(il, CSR_INT, 0xFFFFFFFF); /* must be initialised before il_hw_nic_init */ il->cmd_queue = IL_DEFAULT_CMD_QUEUE_NUM; ret = il4965_hw_nic_init(il); if (ret) { IL_ERR("Unable to init nic\n"); return ret; } /* make sure rfkill handshake bits are cleared */ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); /* clear (again), then enable host interrupts */ _il_wr(il, CSR_INT, 0xFFFFFFFF); il_enable_interrupts(il); /* really make sure rfkill handshake bits are cleared */ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); /* Copy original ucode data image from disk into backup cache. * This will be used to initialize the on-board processor's * data SRAM for a clean start when the runtime program first loads. */ memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr, il->ucode_data.len); for (i = 0; i < MAX_HW_RESTARTS; i++) { /* load bootstrap state machine, * load bootstrap program into processor's memory, * prepare to load the "initialize" uCode */ ret = il->ops->load_ucode(il); if (ret) { IL_ERR("Unable to set up bootstrap uCode: %d\n", ret); continue; } /* start card; "initialize" will load runtime ucode */ il4965_nic_start(il); D_INFO(DRV_NAME " is coming up\n"); return 0; } set_bit(S_EXIT_PENDING, &il->status); __il4965_down(il); clear_bit(S_EXIT_PENDING, &il->status); /* tried to restart and config the device for as long as our * patience could withstand */ IL_ERR("Unable to initialize device after %d attempts.\n", i); return -EIO; } /***************************************************************************** * * Workqueue callbacks * *****************************************************************************/ static void il4965_bg_init_alive_start(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, init_alive_start.work); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status)) goto out; il->ops->init_alive_start(il); out: mutex_unlock(&il->mutex); } static void il4965_bg_alive_start(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, alive_start.work); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status)) goto out; il4965_alive_start(il); out: mutex_unlock(&il->mutex); } static void il4965_bg_run_time_calib_work(struct work_struct *work) { struct il_priv *il = container_of(work, struct il_priv, run_time_calib_work); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status) || test_bit(S_SCANNING, &il->status)) { mutex_unlock(&il->mutex); return; } if (il->start_calib) { il4965_chain_noise_calibration(il, (void *)&il->_4965.stats); il4965_sensitivity_calibration(il, (void *)&il->_4965.stats); } mutex_unlock(&il->mutex); } static void il4965_bg_restart(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, restart); if (test_bit(S_EXIT_PENDING, &il->status)) return; if (test_and_clear_bit(S_FW_ERROR, &il->status)) { mutex_lock(&il->mutex); il->is_open = 0; __il4965_down(il); mutex_unlock(&il->mutex); il4965_cancel_deferred_work(il); ieee80211_restart_hw(il->hw); } else { il4965_down(il); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status)) { mutex_unlock(&il->mutex); return; } __il4965_up(il); mutex_unlock(&il->mutex); } } static void il4965_bg_rx_replenish(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, rx_replenish); if (test_bit(S_EXIT_PENDING, &il->status)) return; mutex_lock(&il->mutex); il4965_rx_replenish(il); mutex_unlock(&il->mutex); } /***************************************************************************** * * mac80211 entry point functions * *****************************************************************************/ #define UCODE_READY_TIMEOUT (4 * HZ) /* * Not a mac80211 entry point function, but it fits in with all the * other mac80211 functions grouped here. */ static int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) { int ret; struct ieee80211_hw *hw = il->hw; hw->rate_control_algorithm = "iwl-4965-rs"; /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_NEED_DTIM_PERIOD | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS; if (il->cfg->sku & IL_SKU_N) hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_SUPPORTS_STATIC_SMPS; hw->sta_data_size = sizeof(struct il_station_priv); hw->vif_data_size = sizeof(struct il_vif_priv); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | WIPHY_FLAG_IBSS_RSN; /* * For now, disable PS by default because it affects * RX performance significantly. */ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; /* we create the 802.11 header and a zero-length SSID element */ hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2; /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; hw->max_listen_interval = IL_CONN_MAX_LISTEN_INTERVAL; if (il->bands[IEEE80211_BAND_2GHZ].n_channels) il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &il->bands[IEEE80211_BAND_2GHZ]; if (il->bands[IEEE80211_BAND_5GHZ].n_channels) il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &il->bands[IEEE80211_BAND_5GHZ]; il_leds_init(il); ret = ieee80211_register_hw(il->hw); if (ret) { IL_ERR("Failed to register hw (error %d)\n", ret); return ret; } il->mac80211_registered = 1; return 0; } int il4965_mac_start(struct ieee80211_hw *hw) { struct il_priv *il = hw->priv; int ret; D_MAC80211("enter\n"); /* we should be verifying the device is ready to be opened */ mutex_lock(&il->mutex); ret = __il4965_up(il); mutex_unlock(&il->mutex); if (ret) return ret; if (il_is_rfkill(il)) goto out; D_INFO("Start UP work done.\n"); /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from * mac80211 will not be run successfully. */ ret = wait_event_timeout(il->wait_command_queue, test_bit(S_READY, &il->status), UCODE_READY_TIMEOUT); if (!ret) { if (!test_bit(S_READY, &il->status)) { IL_ERR("START_ALIVE timeout after %dms.\n", jiffies_to_msecs(UCODE_READY_TIMEOUT)); return -ETIMEDOUT; } } il4965_led_enable(il); out: il->is_open = 1; D_MAC80211("leave\n"); return 0; } void il4965_mac_stop(struct ieee80211_hw *hw) { struct il_priv *il = hw->priv; D_MAC80211("enter\n"); if (!il->is_open) return; il->is_open = 0; il4965_down(il); flush_workqueue(il->workqueue); /* User space software may expect getting rfkill changes * even if interface is down */ _il_wr(il, CSR_INT, 0xFFFFFFFF); il_enable_rfkill_int(il); D_MAC80211("leave\n"); } void il4965_mac_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct il_priv *il = hw->priv; D_MACDUMP("enter\n"); D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); if (il4965_tx_skb(il, control->sta, skb)) dev_kfree_skb_any(skb); D_MACDUMP("leave\n"); } void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 * phase1key) { struct il_priv *il = hw->priv; D_MAC80211("enter\n"); il4965_update_tkip_key(il, keyconf, sta, iv32, phase1key); D_MAC80211("leave\n"); } int il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct il_priv *il = hw->priv; int ret; u8 sta_id; bool is_default_wep_key = false; D_MAC80211("enter\n"); if (il->cfg->mod_params->sw_crypto) { D_MAC80211("leave - hwcrypto disabled\n"); return -EOPNOTSUPP; } /* * To support IBSS RSN, don't program group keys in IBSS, the * hardware will then not attempt to decrypt the frames. */ if (vif->type == NL80211_IFTYPE_ADHOC && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { D_MAC80211("leave - ad-hoc group key\n"); return -EOPNOTSUPP; } sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) return -EINVAL; mutex_lock(&il->mutex); il_scan_cancel_timeout(il, 100); /* * If we are getting WEP group key and we didn't receive any key mapping * so far, we are in legacy wep mode (group key only), otherwise we are * in 1X mode. * In legacy wep mode, we use another host command to the uCode. */ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { if (cmd == SET_KEY) is_default_wep_key = !il->_4965.key_mapping_keys; else is_default_wep_key = (key->hw_key_idx == HW_KEY_DEFAULT); } switch (cmd) { case SET_KEY: if (is_default_wep_key) ret = il4965_set_default_wep_key(il, key); else ret = il4965_set_dynamic_key(il, key, sta_id); D_MAC80211("enable hwcrypto key\n"); break; case DISABLE_KEY: if (is_default_wep_key) ret = il4965_remove_default_wep_key(il, key); else ret = il4965_remove_dynamic_key(il, key, sta_id); D_MAC80211("disable hwcrypto key\n"); break; default: ret = -EINVAL; } mutex_unlock(&il->mutex); D_MAC80211("leave\n"); return ret; } int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 * ssn, u8 buf_size) { struct il_priv *il = hw->priv; int ret = -EINVAL; D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); if (!(il->cfg->sku & IL_SKU_N)) return -EACCES; mutex_lock(&il->mutex); switch (action) { case IEEE80211_AMPDU_RX_START: D_HT("start Rx\n"); ret = il4965_sta_rx_agg_start(il, sta, tid, *ssn); break; case IEEE80211_AMPDU_RX_STOP: D_HT("stop Rx\n"); ret = il4965_sta_rx_agg_stop(il, sta, tid); if (test_bit(S_EXIT_PENDING, &il->status)) ret = 0; break; case IEEE80211_AMPDU_TX_START: D_HT("start Tx\n"); ret = il4965_tx_agg_start(il, vif, sta, tid, ssn); break; case IEEE80211_AMPDU_TX_STOP: D_HT("stop Tx\n"); ret = il4965_tx_agg_stop(il, vif, sta, tid); if (test_bit(S_EXIT_PENDING, &il->status)) ret = 0; break; case IEEE80211_AMPDU_TX_OPERATIONAL: ret = 0; break; } mutex_unlock(&il->mutex); return ret; } int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct il_priv *il = hw->priv; struct il_station_priv *sta_priv = (void *)sta->drv_priv; bool is_ap = vif->type == NL80211_IFTYPE_STATION; int ret; u8 sta_id; D_INFO("received request to add station %pM\n", sta->addr); mutex_lock(&il->mutex); D_INFO("proceeding to add station %pM\n", sta->addr); sta_priv->common.sta_id = IL_INVALID_STATION; atomic_set(&sta_priv->pending_frames, 0); ret = il_add_station_common(il, sta->addr, is_ap, sta, &sta_id); if (ret) { IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret); /* Should we return success if return code is EEXIST ? */ mutex_unlock(&il->mutex); return ret; } sta_priv->common.sta_id = sta_id; /* Initialize rate scaling */ D_INFO("Initializing rate scaling for station %pM\n", sta->addr); il4965_rs_rate_init(il, sta, sta_id); mutex_unlock(&il->mutex); return 0; } void il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch) { struct il_priv *il = hw->priv; const struct il_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = ch_switch->channel; struct il_ht_config *ht_conf = &il->current_ht_config; u16 ch; D_MAC80211("enter\n"); mutex_lock(&il->mutex); if (il_is_rfkill(il)) goto out; if (test_bit(S_EXIT_PENDING, &il->status) || test_bit(S_SCANNING, &il->status) || test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) goto out; if (!il_is_associated(il)) goto out; if (!il->ops->set_channel_switch) goto out; ch = channel->hw_value; if (le16_to_cpu(il->active.channel) == ch) goto out; ch_info = il_get_channel_info(il, channel->band, ch); if (!il_is_channel_valid(ch_info)) { D_MAC80211("invalid channel\n"); goto out; } spin_lock_irq(&il->lock); il->current_ht_config.smps = conf->smps_mode; /* Configure HT40 channels */ il->ht.enabled = conf_is_ht(conf); if (il->ht.enabled) { if (conf_is_ht40_minus(conf)) { il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; il->ht.is_40mhz = true; } else if (conf_is_ht40_plus(conf)) { il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; il->ht.is_40mhz = true; } else { il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; il->ht.is_40mhz = false; } } else il->ht.is_40mhz = false; if ((le16_to_cpu(il->staging.channel) != ch)) il->staging.flags = 0; il_set_rxon_channel(il, channel); il_set_rxon_ht(il, ht_conf); il_set_flags_for_band(il, channel->band, il->vif); spin_unlock_irq(&il->lock); il_set_rate(il); /* * at this point, staging_rxon has the * configuration for channel switch */ set_bit(S_CHANNEL_SWITCH_PENDING, &il->status); il->switch_channel = cpu_to_le16(ch); if (il->ops->set_channel_switch(il, ch_switch)) { clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status); il->switch_channel = 0; ieee80211_chswitch_done(il->vif, false); } out: mutex_unlock(&il->mutex); D_MAC80211("leave\n"); } void il4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct il_priv *il = hw->priv; __le32 filter_or = 0, filter_nand = 0; #define CHK(test, flag) do { \ if (*total_flags & (test)) \ filter_or |= (flag); \ else \ filter_nand |= (flag); \ } while (0) D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags, *total_flags); CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); #undef CHK mutex_lock(&il->mutex); il->staging.filter_flags &= ~filter_nand; il->staging.filter_flags |= filter_or; /* * Not committing directly because hardware can perform a scan, * but we'll eventually commit the filter flags change anyway. */ mutex_unlock(&il->mutex); /* * Receiving all multicast frames is always enabled by the * default flags setup in il_connection_init_rx_config() * since we currently do not support programming multicast * filters into the device. */ *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; } /***************************************************************************** * * driver setup and teardown * *****************************************************************************/ static void il4965_bg_txpower_work(struct work_struct *work) { struct il_priv *il = container_of(work, struct il_priv, txpower_work); mutex_lock(&il->mutex); /* If a scan happened to start before we got here * then just return; the stats notification will * kick off another scheduled work to compensate for * any temperature delta we missed here. */ if (test_bit(S_EXIT_PENDING, &il->status) || test_bit(S_SCANNING, &il->status)) goto out; /* Regardless of if we are associated, we must reconfigure the * TX power since frames can be sent on non-radar channels while * not associated */ il->ops->send_tx_power(il); /* Update last_temperature to keep is_calib_needed from running * when it isn't needed... */ il->last_temperature = il->temperature; out: mutex_unlock(&il->mutex); } static void il4965_setup_deferred_work(struct il_priv *il) { il->workqueue = create_singlethread_workqueue(DRV_NAME); init_waitqueue_head(&il->wait_command_queue); INIT_WORK(&il->restart, il4965_bg_restart); INIT_WORK(&il->rx_replenish, il4965_bg_rx_replenish); INIT_WORK(&il->run_time_calib_work, il4965_bg_run_time_calib_work); INIT_DELAYED_WORK(&il->init_alive_start, il4965_bg_init_alive_start); INIT_DELAYED_WORK(&il->alive_start, il4965_bg_alive_start); il_setup_scan_deferred_work(il); INIT_WORK(&il->txpower_work, il4965_bg_txpower_work); init_timer(&il->stats_periodic); il->stats_periodic.data = (unsigned long)il; il->stats_periodic.function = il4965_bg_stats_periodic; init_timer(&il->watchdog); il->watchdog.data = (unsigned long)il; il->watchdog.function = il_bg_watchdog; tasklet_init(&il->irq_tasklet, (void (*)(unsigned long))il4965_irq_tasklet, (unsigned long)il); } static void il4965_cancel_deferred_work(struct il_priv *il) { cancel_work_sync(&il->txpower_work); cancel_delayed_work_sync(&il->init_alive_start); cancel_delayed_work(&il->alive_start); cancel_work_sync(&il->run_time_calib_work); il_cancel_scan_deferred_work(il); del_timer_sync(&il->stats_periodic); } static void il4965_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates) { int i; for (i = 0; i < RATE_COUNT_LEGACY; i++) { rates[i].bitrate = il_rates[i].ieee * 5; rates[i].hw_value = i; /* Rate scaling will work on idxes */ rates[i].hw_value_short = i; rates[i].flags = 0; if ((i >= IL_FIRST_CCK_RATE) && (i <= IL_LAST_CCK_RATE)) { /* * If CCK != 1M then set short preamble rate flag. */ rates[i].flags |= (il_rates[i].plcp == RATE_1M_PLCP) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE; } } } /* * Acquire il->lock before calling this function ! */ void il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx) { il_wr(il, HBUS_TARG_WRPTR, (idx & 0xff) | (txq_id << 8)); il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(txq_id), idx); } void il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, int tx_fifo_id, int scd_retry) { int txq_id = txq->q.id; /* Find out whether to activate Tx queue */ int active = test_bit(txq_id, &il->txq_ctx_active_msk) ? 1 : 0; /* Set up and activate */ il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), (active << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | (tx_fifo_id << IL49_SCD_QUEUE_STTS_REG_POS_TXF) | (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_WSL) | (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) | IL49_SCD_QUEUE_STTS_REG_MSK); txq->sched_retry = scd_retry; D_INFO("%s %s Queue %d on AC %d\n", active ? "Activate" : "Deactivate", scd_retry ? "BA" : "AC", txq_id, tx_fifo_id); } const struct ieee80211_ops il4965_mac_ops = { .tx = il4965_mac_tx, .start = il4965_mac_start, .stop = il4965_mac_stop, .add_interface = il_mac_add_interface, .remove_interface = il_mac_remove_interface, .change_interface = il_mac_change_interface, .config = il_mac_config, .configure_filter = il4965_configure_filter, .set_key = il4965_mac_set_key, .update_tkip_key = il4965_mac_update_tkip_key, .conf_tx = il_mac_conf_tx, .reset_tsf = il_mac_reset_tsf, .bss_info_changed = il_mac_bss_info_changed, .ampdu_action = il4965_mac_ampdu_action, .hw_scan = il_mac_hw_scan, .sta_add = il4965_mac_sta_add, .sta_remove = il_mac_sta_remove, .channel_switch = il4965_mac_channel_switch, .tx_last_beacon = il_mac_tx_last_beacon, }; static int il4965_init_drv(struct il_priv *il) { int ret; spin_lock_init(&il->sta_lock); spin_lock_init(&il->hcmd_lock); INIT_LIST_HEAD(&il->free_frames); mutex_init(&il->mutex); il->ieee_channels = NULL; il->ieee_rates = NULL; il->band = IEEE80211_BAND_2GHZ; il->iw_mode = NL80211_IFTYPE_STATION; il->current_ht_config.smps = IEEE80211_SMPS_STATIC; il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; /* initialize force reset */ il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD; /* Choose which receivers/antennas to use */ if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); il_init_scan_params(il); ret = il_init_channel_map(il); if (ret) { IL_ERR("initializing regulatory failed: %d\n", ret); goto err; } ret = il_init_geos(il); if (ret) { IL_ERR("initializing geos failed: %d\n", ret); goto err_free_channel_map; } il4965_init_hw_rates(il, il->ieee_rates); return 0; err_free_channel_map: il_free_channel_map(il); err: return ret; } static void il4965_uninit_drv(struct il_priv *il) { il_free_geos(il); il_free_channel_map(il); kfree(il->scan_cmd); } static void il4965_hw_detect(struct il_priv *il) { il->hw_rev = _il_rd(il, CSR_HW_REV); il->hw_wa_rev = _il_rd(il, CSR_HW_REV_WA_REG); il->rev_id = il->pci_dev->revision; D_INFO("HW Revision ID = 0x%X\n", il->rev_id); } static struct il_sensitivity_ranges il4965_sensitivity = { .min_nrg_cck = 97, .max_nrg_cck = 0, /* not used, set to 0 */ .auto_corr_min_ofdm = 85, .auto_corr_min_ofdm_mrc = 170, .auto_corr_min_ofdm_x1 = 105, .auto_corr_min_ofdm_mrc_x1 = 220, .auto_corr_max_ofdm = 120, .auto_corr_max_ofdm_mrc = 210, .auto_corr_max_ofdm_x1 = 140, .auto_corr_max_ofdm_mrc_x1 = 270, .auto_corr_min_cck = 125, .auto_corr_max_cck = 200, .auto_corr_min_cck_mrc = 200, .auto_corr_max_cck_mrc = 400, .nrg_th_cck = 100, .nrg_th_ofdm = 100, .barker_corr_th_min = 190, .barker_corr_th_min_mrc = 390, .nrg_th_cca = 62, }; static void il4965_set_hw_params(struct il_priv *il) { il->hw_params.bcast_id = IL4965_BROADCAST_ID; il->hw_params.max_rxq_size = RX_QUEUE_SIZE; il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; if (il->cfg->mod_params->amsdu_size_8K) il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_8K); else il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_4K); il->hw_params.max_beacon_itrvl = IL_MAX_UCODE_BEACON_INTERVAL; if (il->cfg->mod_params->disable_11n) il->cfg->sku &= ~IL_SKU_N; if (il->cfg->mod_params->num_of_queues >= IL_MIN_NUM_QUEUES && il->cfg->mod_params->num_of_queues <= IL49_NUM_QUEUES) il->cfg->num_of_queues = il->cfg->mod_params->num_of_queues; il->hw_params.max_txq_num = il->cfg->num_of_queues; il->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM; il->hw_params.scd_bc_tbls_size = il->cfg->num_of_queues * sizeof(struct il4965_scd_bc_tbl); il->hw_params.tfd_size = sizeof(struct il_tfd); il->hw_params.max_stations = IL4965_STATION_COUNT; il->hw_params.max_data_size = IL49_RTC_DATA_SIZE; il->hw_params.max_inst_size = IL49_RTC_INST_SIZE; il->hw_params.max_bsm_size = BSM_SRAM_SIZE; il->hw_params.ht40_channel = BIT(IEEE80211_BAND_5GHZ); il->hw_params.rx_wrt_ptr_reg = FH49_RSCSR_CHNL0_WPTR; il->hw_params.tx_chains_num = il4965_num_of_ant(il->cfg->valid_tx_ant); il->hw_params.rx_chains_num = il4965_num_of_ant(il->cfg->valid_rx_ant); il->hw_params.valid_tx_ant = il->cfg->valid_tx_ant; il->hw_params.valid_rx_ant = il->cfg->valid_rx_ant; il->hw_params.ct_kill_threshold = CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY); il->hw_params.sens = &il4965_sensitivity; il->hw_params.beacon_time_tsf_bits = IL4965_EXT_BEACON_TIME_POS; } static int il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err = 0; struct il_priv *il; struct ieee80211_hw *hw; struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data); unsigned long flags; u16 pci_cmd; /************************ * 1. Allocating HW data ************************/ hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il4965_mac_ops); if (!hw) { err = -ENOMEM; goto out; } il = hw->priv; il->hw = hw; SET_IEEE80211_DEV(hw, &pdev->dev); D_INFO("*** LOAD DRIVER ***\n"); il->cfg = cfg; il->ops = &il4965_ops; #ifdef CONFIG_IWLEGACY_DEBUGFS il->debugfs_ops = &il4965_debugfs_ops; #endif il->pci_dev = pdev; il->inta_mask = CSR_INI_SET_MASK; /************************** * 2. Initializing PCI bus **************************/ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); if (pci_enable_device(pdev)) { err = -ENODEV; goto out_ieee80211_free_hw; } pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); if (!err) err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); if (err) { err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (!err) err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); /* both attempts failed: */ if (err) { IL_WARN("No suitable DMA available.\n"); goto out_pci_disable_device; } } err = pci_request_regions(pdev, DRV_NAME); if (err) goto out_pci_disable_device; pci_set_drvdata(pdev, il); /*********************** * 3. Read REV register ***********************/ il->hw_base = pci_ioremap_bar(pdev, 0); if (!il->hw_base) { err = -ENODEV; goto out_pci_release_regions; } D_INFO("pci_resource_len = 0x%08llx\n", (unsigned long long)pci_resource_len(pdev, 0)); D_INFO("pci_resource_base = %p\n", il->hw_base); /* these spin locks will be used in apm_ops.init and EEPROM access * we should init now */ spin_lock_init(&il->reg_lock); spin_lock_init(&il->lock); /* * stop and reset the on-board processor just in case it is in a * strange state ... like being left stranded by a primary kernel * and this is now the kdump kernel trying to start up */ _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); il4965_hw_detect(il); IL_INFO("Detected %s, REV=0x%X\n", il->cfg->name, il->hw_rev); /* We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); il4965_prepare_card_hw(il); if (!il->hw_ready) { IL_WARN("Failed, HW not ready\n"); goto out_iounmap; } /***************** * 4. Read EEPROM *****************/ /* Read the EEPROM */ err = il_eeprom_init(il); if (err) { IL_ERR("Unable to init EEPROM\n"); goto out_iounmap; } err = il4965_eeprom_check_version(il); if (err) goto out_free_eeprom; if (err) goto out_free_eeprom; /* extract MAC Address */ il4965_eeprom_get_mac(il, il->addresses[0].addr); D_INFO("MAC address: %pM\n", il->addresses[0].addr); il->hw->wiphy->addresses = il->addresses; il->hw->wiphy->n_addresses = 1; /************************ * 5. Setup HW constants ************************/ il4965_set_hw_params(il); /******************* * 6. Setup il *******************/ err = il4965_init_drv(il); if (err) goto out_free_eeprom; /* At this point both hw and il are initialized. */ /******************** * 7. Setup services ********************/ spin_lock_irqsave(&il->lock, flags); il_disable_interrupts(il); spin_unlock_irqrestore(&il->lock, flags); pci_enable_msi(il->pci_dev); err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il); if (err) { IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq); goto out_disable_msi; } il4965_setup_deferred_work(il); il4965_setup_handlers(il); /********************************************* * 8. Enable interrupts and read RFKILL state *********************************************/ /* enable rfkill interrupt: hw bug w/a */ pci_read_config_word(il->pci_dev, PCI_COMMAND, &pci_cmd); if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; pci_write_config_word(il->pci_dev, PCI_COMMAND, pci_cmd); } il_enable_rfkill_int(il); /* If platform's RF_KILL switch is NOT set to KILL */ if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) clear_bit(S_RFKILL, &il->status); else set_bit(S_RFKILL, &il->status); wiphy_rfkill_set_hw_state(il->hw->wiphy, test_bit(S_RFKILL, &il->status)); il_power_initialize(il); init_completion(&il->_4965.firmware_loading_complete); err = il4965_request_firmware(il, true); if (err) goto out_destroy_workqueue; return 0; out_destroy_workqueue: destroy_workqueue(il->workqueue); il->workqueue = NULL; free_irq(il->pci_dev->irq, il); out_disable_msi: pci_disable_msi(il->pci_dev); il4965_uninit_drv(il); out_free_eeprom: il_eeprom_free(il); out_iounmap: iounmap(il->hw_base); out_pci_release_regions: pci_set_drvdata(pdev, NULL); pci_release_regions(pdev); out_pci_disable_device: pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(il->hw); out: return err; } static void __devexit il4965_pci_remove(struct pci_dev *pdev) { struct il_priv *il = pci_get_drvdata(pdev); unsigned long flags; if (!il) return; wait_for_completion(&il->_4965.firmware_loading_complete); D_INFO("*** UNLOAD DRIVER ***\n"); il_dbgfs_unregister(il); sysfs_remove_group(&pdev->dev.kobj, &il_attribute_group); /* ieee80211_unregister_hw call wil cause il_mac_stop to * to be called and il4965_down since we are removing the device * we need to set S_EXIT_PENDING bit. */ set_bit(S_EXIT_PENDING, &il->status); il_leds_exit(il); if (il->mac80211_registered) { ieee80211_unregister_hw(il->hw); il->mac80211_registered = 0; } else { il4965_down(il); } /* * Make sure device is reset to low power before unloading driver. * This may be redundant with il4965_down(), but there are paths to * run il4965_down() without calling apm_ops.stop(), and there are * paths to avoid running il4965_down() at all before leaving driver. * This (inexpensive) call *makes sure* device is reset. */ il_apm_stop(il); /* make sure we flush any pending irq or * tasklet for the driver */ spin_lock_irqsave(&il->lock, flags); il_disable_interrupts(il); spin_unlock_irqrestore(&il->lock, flags); il4965_synchronize_irq(il); il4965_dealloc_ucode_pci(il); if (il->rxq.bd) il4965_rx_queue_free(il, &il->rxq); il4965_hw_txq_ctx_free(il); il_eeprom_free(il); /*netif_stop_queue(dev); */ flush_workqueue(il->workqueue); /* ieee80211_unregister_hw calls il_mac_stop, which flushes * il->workqueue... so we can't take down the workqueue * until now... */ destroy_workqueue(il->workqueue); il->workqueue = NULL; free_irq(il->pci_dev->irq, il); pci_disable_msi(il->pci_dev); iounmap(il->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); il4965_uninit_drv(il); dev_kfree_skb(il->beacon_skb); ieee80211_free_hw(il->hw); } /* * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask * must be called under il->lock and mac access */ void il4965_txq_set_sched(struct il_priv *il, u32 mask) { il_wr_prph(il, IL49_SCD_TXFACT, mask); } /***************************************************************************** * * driver and module entry point * *****************************************************************************/ /* Hardware specific file defines the PCI IDs table for that hardware module */ static DEFINE_PCI_DEVICE_TABLE(il4965_hw_card_ids) = { {IL_PCI_DEVICE(0x4229, PCI_ANY_ID, il4965_cfg)}, {IL_PCI_DEVICE(0x4230, PCI_ANY_ID, il4965_cfg)}, {0} }; MODULE_DEVICE_TABLE(pci, il4965_hw_card_ids); static struct pci_driver il4965_driver = { .name = DRV_NAME, .id_table = il4965_hw_card_ids, .probe = il4965_pci_probe, .remove = __devexit_p(il4965_pci_remove), #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = IL_LEGACY_PM_OPS, #elif defined(CONFIG_PM) .suspend = il_pci_suspend_compat, .resume = il_pci_resume_compat, #endif }; static int __init il4965_init(void) { int ret; pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); pr_info(DRV_COPYRIGHT "\n"); ret = il4965_rate_control_register(); if (ret) { pr_err("Unable to register rate control algorithm: %d\n", ret); return ret; } ret = pci_register_driver(&il4965_driver); if (ret) { pr_err("Unable to initialize PCI module\n"); goto error_register; } return ret; error_register: il4965_rate_control_unregister(); return ret; } static void __exit il4965_exit(void) { pci_unregister_driver(&il4965_driver); il4965_rate_control_unregister(); } module_exit(il4965_exit); module_init(il4965_init); #ifdef CONFIG_IWLEGACY_DEBUG module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(swcrypto, il4965_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); module_param_named(queues_num, il4965_mod_params.num_of_queues, int, S_IRUGO); MODULE_PARM_DESC(queues_num, "number of hw queues."); module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO); MODULE_PARM_DESC(11n_disable, "disable 11n functionality"); module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int, S_IRUGO); MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/3945-mac.c0000644000175000017500000032240512026211315023601 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRV_NAME "iwl3945" #include "commands.h" #include "common.h" #include "3945.h" #include "iwl-spectrum.h" /* * module name, copyright, version, etc. */ #define DRV_DESCRIPTION \ "Intel(R) PRO/Wireless 3945ABG/BG Network Connection driver for Linux" #ifdef CONFIG_IWLEGACY_DEBUG #define VD "d" #else #define VD #endif /* * add "s" to indicate spectrum measurement included. * we add it here to be consistent with previous releases in which * this was configurable. */ #define DRV_VERSION IWLWIFI_VERSION VD "s" #define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" #define DRV_AUTHOR "" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); /* module parameters */ struct il_mod_params il3945_mod_params = { .sw_crypto = 1, .restart_fw = 1, .disable_hw_scan = 1, /* the rest are 0 by default */ }; /** * il3945_get_antenna_flags - Get antenna flags for RXON command * @il: eeprom and antenna fields are used to determine antenna flags * * il->eeprom39 is used to determine if antenna AUX/MAIN are reversed * il3945_mod_params.antenna specifies the antenna diversity mode: * * IL_ANTENNA_DIVERSITY - NIC selects best antenna by itself * IL_ANTENNA_MAIN - Force MAIN antenna * IL_ANTENNA_AUX - Force AUX antenna */ __le32 il3945_get_antenna_flags(const struct il_priv *il) { struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; switch (il3945_mod_params.antenna) { case IL_ANTENNA_DIVERSITY: return 0; case IL_ANTENNA_MAIN: if (eeprom->antenna_switch_type) return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; case IL_ANTENNA_AUX: if (eeprom->antenna_switch_type) return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; } /* bad antenna selector value */ IL_ERR("Bad antenna selector value (0x%x)\n", il3945_mod_params.antenna); return 0; /* "diversity" is default if error */ } static int il3945_set_ccmp_dynamic_key_info(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { unsigned long flags; __le16 key_flags = 0; int ret; key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); if (sta_id == il->hw_params.bcast_id) key_flags |= STA_KEY_MULTICAST_MSK; keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; keyconf->hw_key_idx = keyconf->keyidx; key_flags &= ~STA_KEY_FLG_INVALID; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].keyinfo.cipher = keyconf->cipher; il->stations[sta_id].keyinfo.keylen = keyconf->keylen; memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen); if ((il->stations[sta_id].sta.key. key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) il->stations[sta_id].sta.key.key_offset = il_get_free_ucode_key_idx(il); /* else, we are overriding an existing key => no need to allocated room * in uCode. */ WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, "no space for a new key"); il->stations[sta_id].sta.key.key_flags = key_flags; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; D_INFO("hwcrypto: modify ucode station key info\n"); ret = il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); spin_unlock_irqrestore(&il->sta_lock, flags); return ret; } static int il3945_set_tkip_dynamic_key_info(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { return -EOPNOTSUPP; } static int il3945_set_wep_dynamic_key_info(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { return -EOPNOTSUPP; } static int il3945_clear_sta_key_info(struct il_priv *il, u8 sta_id) { unsigned long flags; struct il_addsta_cmd sta_cmd; spin_lock_irqsave(&il->sta_lock, flags); memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key)); memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo)); il->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC; il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags); D_INFO("hwcrypto: clear ucode station key info\n"); return il_send_add_sta(il, &sta_cmd, CMD_SYNC); } static int il3945_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, u8 sta_id) { int ret = 0; keyconf->hw_key_idx = HW_KEY_DYNAMIC; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_CCMP: ret = il3945_set_ccmp_dynamic_key_info(il, keyconf, sta_id); break; case WLAN_CIPHER_SUITE_TKIP: ret = il3945_set_tkip_dynamic_key_info(il, keyconf, sta_id); break; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ret = il3945_set_wep_dynamic_key_info(il, keyconf, sta_id); break; default: IL_ERR("Unknown alg: %s alg=%x\n", __func__, keyconf->cipher); ret = -EINVAL; } D_WEP("Set dynamic key: alg=%x len=%d idx=%d sta=%d ret=%d\n", keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); return ret; } static int il3945_remove_static_key(struct il_priv *il) { int ret = -EOPNOTSUPP; return ret; } static int il3945_set_static_key(struct il_priv *il, struct ieee80211_key_conf *key) { if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) return -EOPNOTSUPP; IL_ERR("Static key invalid: cipher %x\n", key->cipher); return -EINVAL; } static void il3945_clear_free_frames(struct il_priv *il) { struct list_head *element; D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count); while (!list_empty(&il->free_frames)) { element = il->free_frames.next; list_del(element); kfree(list_entry(element, struct il3945_frame, list)); il->frames_count--; } if (il->frames_count) { IL_WARN("%d frames still in use. Did we lose one?\n", il->frames_count); il->frames_count = 0; } } static struct il3945_frame * il3945_get_free_frame(struct il_priv *il) { struct il3945_frame *frame; struct list_head *element; if (list_empty(&il->free_frames)) { frame = kzalloc(sizeof(*frame), GFP_KERNEL); if (!frame) { IL_ERR("Could not allocate frame!\n"); return NULL; } il->frames_count++; return frame; } element = il->free_frames.next; list_del(element); return list_entry(element, struct il3945_frame, list); } static void il3945_free_frame(struct il_priv *il, struct il3945_frame *frame) { memset(frame, 0, sizeof(*frame)); list_add(&frame->list, &il->free_frames); } unsigned int il3945_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, int left) { if (!il_is_associated(il) || !il->beacon_skb) return 0; if (il->beacon_skb->len > left) return 0; memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len); return il->beacon_skb->len; } static int il3945_send_beacon_cmd(struct il_priv *il) { struct il3945_frame *frame; unsigned int frame_size; int rc; u8 rate; frame = il3945_get_free_frame(il); if (!frame) { IL_ERR("Could not obtain free frame buffer for beacon " "command.\n"); return -ENOMEM; } rate = il_get_lowest_plcp(il); frame_size = il3945_hw_get_beacon_cmd(il, frame, rate); rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]); il3945_free_frame(il, frame); return rc; } static void il3945_unset_hw_params(struct il_priv *il) { if (il->_3945.shared_virt) dma_free_coherent(&il->pci_dev->dev, sizeof(struct il3945_shared), il->_3945.shared_virt, il->_3945.shared_phys); } static void il3945_build_tx_cmd_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, struct il_device_cmd *cmd, struct sk_buff *skb_frag, int sta_id) { struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; struct il_hw_key *keyinfo = &il->stations[sta_id].keyinfo; tx_cmd->sec_ctl = 0; switch (keyinfo->cipher) { case WLAN_CIPHER_SUITE_CCMP: tx_cmd->sec_ctl = TX_CMD_SEC_CCM; memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen); D_TX("tx_cmd with AES hwcrypto\n"); break; case WLAN_CIPHER_SUITE_TKIP: break; case WLAN_CIPHER_SUITE_WEP104: tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; /* fall through */ case WLAN_CIPHER_SUITE_WEP40: tx_cmd->sec_ctl |= TX_CMD_SEC_WEP | (info->control.hw_key-> hw_key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT; memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen); D_TX("Configuring packet for WEP encryption " "with key %d\n", info->control.hw_key->hw_key_idx); break; default: IL_ERR("Unknown encode cipher %x\n", keyinfo->cipher); break; } } /* * handle build C_TX command notification. */ static void il3945_build_tx_cmd_basic(struct il_priv *il, struct il_device_cmd *cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, u8 std_id) { struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; __le32 tx_flags = tx_cmd->tx_flags; __le16 fc = hdr->frame_control; tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { tx_flags |= TX_CMD_FLG_ACK_MSK; if (ieee80211_is_mgmt(fc)) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; if (ieee80211_is_probe_resp(fc) && !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) tx_flags |= TX_CMD_FLG_TSF_MSK; } else { tx_flags &= (~TX_CMD_FLG_ACK_MSK); tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } tx_cmd->sta_id = std_id; if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { u8 *qc = ieee80211_get_qos_ctl(hdr); tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } il_tx_cmd_protection(il, info, fc, &tx_flags); tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); if (ieee80211_is_mgmt(fc)) { if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); else tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); } else { tx_cmd->timeout.pm_frame_timeout = 0; } tx_cmd->driver_txop = 0; tx_cmd->tx_flags = tx_flags; tx_cmd->next_frame_len = 0; } /* * start C_TX command process */ static int il3945_tx_skb(struct il_priv *il, struct ieee80211_sta *sta, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct il3945_tx_cmd *tx_cmd; struct il_tx_queue *txq = NULL; struct il_queue *q = NULL; struct il_device_cmd *out_cmd; struct il_cmd_meta *out_meta; dma_addr_t phys_addr; dma_addr_t txcmd_phys; int txq_id = skb_get_queue_mapping(skb); u16 len, idx, hdr_len; u8 id; u8 unicast; u8 sta_id; u8 tid = 0; __le16 fc; u8 wait_write_ptr = 0; unsigned long flags; spin_lock_irqsave(&il->lock, flags); if (il_is_rfkill(il)) { D_DROP("Dropping - RF KILL\n"); goto drop_unlock; } if ((ieee80211_get_tx_rate(il->hw, info)->hw_value & 0xFF) == IL_INVALID_RATE) { IL_ERR("ERROR: No TX rate available.\n"); goto drop_unlock; } unicast = !is_multicast_ether_addr(hdr->addr1); id = 0; fc = hdr->frame_control; #ifdef CONFIG_IWLEGACY_DEBUG if (ieee80211_is_auth(fc)) D_TX("Sending AUTH frame\n"); else if (ieee80211_is_assoc_req(fc)) D_TX("Sending ASSOC frame\n"); else if (ieee80211_is_reassoc_req(fc)) D_TX("Sending REASSOC frame\n"); #endif spin_unlock_irqrestore(&il->lock, flags); hdr_len = ieee80211_hdrlen(fc); /* Find idx into station table for destination station */ sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) { D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); goto drop; } D_RATE("station Id %d\n", sta_id); if (ieee80211_is_data_qos(fc)) { u8 *qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; if (unlikely(tid >= MAX_TID_COUNT)) goto drop; } /* Descriptor for chosen Tx queue */ txq = &il->txq[txq_id]; q = &txq->q; if ((il_queue_space(q) < q->high_mark)) goto drop; spin_lock_irqsave(&il->lock, flags); idx = il_get_cmd_idx(q, q->write_ptr, 0); txq->skbs[q->write_ptr] = skb; /* Init first empty entry in queue's array of Tx/cmd buffers */ out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; tx_cmd = (struct il3945_tx_cmd *)out_cmd->cmd.payload; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); memset(tx_cmd, 0, sizeof(*tx_cmd)); /* * Set up the Tx-command (not MAC!) header. * Store the chosen Tx queue and TFD idx within the sequence field; * after Tx, uCode's Tx response will return this value so driver can * locate the frame within the tx queue and do post-tx processing. */ out_cmd->hdr.cmd = C_TX; out_cmd->hdr.sequence = cpu_to_le16((u16) (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr))); /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, hdr_len); if (info->control.hw_key) il3945_build_tx_cmd_hwcrypto(il, info, out_cmd, skb, sta_id); /* TODO need this for burst mode later on */ il3945_build_tx_cmd_basic(il, out_cmd, info, hdr, sta_id); il3945_hw_build_tx_cmd_rate(il, out_cmd, info, hdr, sta_id); /* Total # bytes to be transmitted */ len = (u16) skb->len; tx_cmd->len = cpu_to_le16(len); il_update_stats(il, true, fc, len); tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; } else { wait_write_ptr = 1; txq->need_update = 0; } D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence)); D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); il_print_hex_dump(il, IL_DL_TX, tx_cmd, sizeof(*tx_cmd)); il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, ieee80211_hdrlen(fc)); /* * Use the first empty entry in this queue's command buffer array * to contain the Tx command and MAC header concatenated together * (payload data will be in another buffer). * Size of this varies, due to varying MAC header length. * If end is not dword aligned, we'll have 2 extra bytes at the end * of the MAC header (device reads on dword boundaries). * We'll tell device about this padding later. */ len = sizeof(struct il3945_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len; len = (len + 3) & ~3; /* Physical address of this Tx command's header (not MAC header!), * within command buffer array. */ txcmd_phys = pci_map_single(il->pci_dev, &out_cmd->hdr, len, PCI_DMA_TODEVICE); /* we do not map meta data ... so we can safely access address to * provide to unmap command*/ dma_unmap_addr_set(out_meta, mapping, txcmd_phys); dma_unmap_len_set(out_meta, len, len); /* Add buffer containing Tx command and MAC(!) header to TFD's * first entry */ il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, len, 1, 0); /* Set up TFD's 2nd entry to point directly to remainder of skb, * if any (802.11 null frames have no payload). */ len = skb->len - hdr_len; if (len) { phys_addr = pci_map_single(il->pci_dev, skb->data + hdr_len, len, PCI_DMA_TODEVICE); il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, len, 0, U32_PAD(len)); } /* Tell device the write idx *just past* this latest filled TFD */ q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); il_txq_update_write_ptr(il, txq); spin_unlock_irqrestore(&il->lock, flags); if (il_queue_space(q) < q->high_mark && il->mac80211_registered) { if (wait_write_ptr) { spin_lock_irqsave(&il->lock, flags); txq->need_update = 1; il_txq_update_write_ptr(il, txq); spin_unlock_irqrestore(&il->lock, flags); } il_stop_queue(il, txq); } return 0; drop_unlock: spin_unlock_irqrestore(&il->lock, flags); drop: return -1; } static int il3945_get_measurement(struct il_priv *il, struct ieee80211_measurement_params *params, u8 type) { struct il_spectrum_cmd spectrum; struct il_rx_pkt *pkt; struct il_host_cmd cmd = { .id = C_SPECTRUM_MEASUREMENT, .data = (void *)&spectrum, .flags = CMD_WANT_SKB, }; u32 add_time = le64_to_cpu(params->start_time); int rc; int spectrum_resp_status; int duration = le16_to_cpu(params->duration); if (il_is_associated(il)) add_time = il_usecs_to_beacons(il, le64_to_cpu(params->start_time) - il->_3945.last_tsf, le16_to_cpu(il->timing.beacon_interval)); memset(&spectrum, 0, sizeof(spectrum)); spectrum.channel_count = cpu_to_le16(1); spectrum.flags = RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK; spectrum.filter_flags = MEASUREMENT_FILTER_FLAG; cmd.len = sizeof(spectrum); spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len)); if (il_is_associated(il)) spectrum.start_time = il_add_beacon_time(il, il->_3945.last_beacon_time, add_time, le16_to_cpu(il->timing.beacon_interval)); else spectrum.start_time = 0; spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT); spectrum.channels[0].channel = params->channel; spectrum.channels[0].type = type; if (il->active.flags & RXON_FLG_BAND_24G_MSK) spectrum.flags |= RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK; rc = il_send_cmd_sync(il, &cmd); if (rc) return rc; pkt = (struct il_rx_pkt *)cmd.reply_page; if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { IL_ERR("Bad return from N_RX_ON_ASSOC command\n"); rc = -EIO; } spectrum_resp_status = le16_to_cpu(pkt->u.spectrum.status); switch (spectrum_resp_status) { case 0: /* Command will be handled */ if (pkt->u.spectrum.id != 0xff) { D_INFO("Replaced existing measurement: %d\n", pkt->u.spectrum.id); il->measurement_status &= ~MEASUREMENT_READY; } il->measurement_status |= MEASUREMENT_ACTIVE; rc = 0; break; case 1: /* Command will not be handled */ rc = -EAGAIN; break; } il_free_pages(il, cmd.reply_page); return rc; } static void il3945_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_alive_resp *palive; struct delayed_work *pwork; palive = &pkt->u.alive_frame; D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", palive->is_valid, palive->ver_type, palive->ver_subtype); if (palive->ver_subtype == INITIALIZE_SUBTYPE) { D_INFO("Initialization Alive received.\n"); memcpy(&il->card_alive_init, &pkt->u.alive_frame, sizeof(struct il_alive_resp)); pwork = &il->init_alive_start; } else { D_INFO("Runtime Alive received.\n"); memcpy(&il->card_alive, &pkt->u.alive_frame, sizeof(struct il_alive_resp)); pwork = &il->alive_start; il3945_disable_events(il); } /* We delay the ALIVE response by 5ms to * give the HW RF Kill time to activate... */ if (palive->is_valid == UCODE_VALID_OK) queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5)); else IL_WARN("uCode did not respond OK.\n"); } static void il3945_hdl_add_sta(struct il_priv *il, struct il_rx_buf *rxb) { #ifdef CONFIG_IWLEGACY_DEBUG struct il_rx_pkt *pkt = rxb_addr(rxb); #endif D_RX("Received C_ADD_STA: 0x%02X\n", pkt->u.status); } static void il3945_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il3945_beacon_notif *beacon = &(pkt->u.beacon_status); #ifdef CONFIG_IWLEGACY_DEBUG u8 rate = beacon->beacon_notify_hdr.rate; D_RX("beacon status %x retries %d iss %d " "tsf %d %d rate %d\n", le32_to_cpu(beacon->beacon_notify_hdr.status) & TX_STATUS_MSK, beacon->beacon_notify_hdr.failure_frame, le32_to_cpu(beacon->ibss_mgr_status), le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); #endif il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); } /* Handle notification from uCode that card's power state is changing * due to software, hardware, or critical temperature RFKILL */ static void il3945_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = il->status; IL_WARN("Card state received: HW:%s SW:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", (flags & SW_CARD_DISABLED) ? "Kill" : "On"); _il_wr(il, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); if (flags & HW_CARD_DISABLED) set_bit(S_RFKILL, &il->status); else clear_bit(S_RFKILL, &il->status); il_scan_cancel(il); if ((test_bit(S_RFKILL, &status) != test_bit(S_RFKILL, &il->status))) wiphy_rfkill_set_hw_state(il->hw->wiphy, test_bit(S_RFKILL, &il->status)); else wake_up(&il->wait_command_queue); } /** * il3945_setup_handlers - Initialize Rx handler callbacks * * Setup the RX handlers for each of the reply types sent from the uCode * to the host. * * This function chains into the hardware specific files for them to setup * any hardware specific handlers as well. */ static void il3945_setup_handlers(struct il_priv *il) { il->handlers[N_ALIVE] = il3945_hdl_alive; il->handlers[C_ADD_STA] = il3945_hdl_add_sta; il->handlers[N_ERROR] = il_hdl_error; il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa; il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement; il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep; il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats; il->handlers[N_BEACON] = il3945_hdl_beacon; /* * The same handler is used for both the REPLY to a discrete * stats request from the host as well as for the periodic * stats notifications (after received beacons) from the uCode. */ il->handlers[C_STATS] = il3945_hdl_c_stats; il->handlers[N_STATS] = il3945_hdl_stats; il_setup_rx_scan_handlers(il); il->handlers[N_CARD_STATE] = il3945_hdl_card_state; /* Set up hardware specific Rx handlers */ il3945_hw_handler_setup(il); } /************************** RX-FUNCTIONS ****************************/ /* * Rx theory of operation * * The host allocates 32 DMA target addresses and passes the host address * to the firmware at register IL_RFDS_TBL_LOWER + N * RFD_SIZE where N is * 0 to 31 * * Rx Queue Indexes * The host/firmware share two idx registers for managing the Rx buffers. * * The READ idx maps to the first position that the firmware may be writing * to -- the driver can read up to (but not including) this position and get * good data. * The READ idx is managed by the firmware once the card is enabled. * * The WRITE idx maps to the last position the driver has read from -- the * position preceding WRITE is the last slot the firmware can place a packet. * * The queue is empty (no good data) if WRITE = READ - 1, and is full if * WRITE = READ. * * During initialization, the host sets up the READ queue position to the first * IDX position, and WRITE to the last (READ - 1 wrapped) * * When the firmware places a packet in a buffer, it will advance the READ idx * and fire the RX interrupt. The driver can then query the READ idx and * process as many packets as possible, moving the WRITE idx forward as it * resets the Rx queue buffers with new memory. * * The management in the driver is as follows: * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled * to replenish the iwl->rxq->rx_free. * + In il3945_rx_replenish (scheduled) if 'processed' != 'read' then the * iwl->rxq is replenished and the READ IDX is updated (updating the * 'processed' and 'read' driver idxes as well) * + A received packet is processed and handed to the kernel network stack, * detached from the iwl->rxq. The driver 'processed' idx is updated. * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ * IDX is not incremented and iwl->status(RX_STALLED) is set. If there * were enough free buffers and RX_STALLED is set it is cleared. * * * Driver sequence: * * il3945_rx_replenish() Replenishes rx_free list from rx_used, and calls * il3945_rx_queue_restock * il3945_rx_queue_restock() Moves available buffers from rx_free into Rx * queue, updates firmware pointers, and updates * the WRITE idx. If insufficient rx_free buffers * are available, schedules il3945_rx_replenish * * -- enable interrupts -- * ISR - il3945_rx() Detach il_rx_bufs from pool up to the * READ IDX, detaching the SKB from the pool. * Moves the packet buffer from queue to rx_used. * Calls il3945_rx_queue_restock to refill any empty * slots. * ... * */ /** * il3945_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr */ static inline __le32 il3945_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr) { return cpu_to_le32((u32) dma_addr); } /** * il3945_rx_queue_restock - refill RX queue from pre-allocated pool * * If there are slots in the RX queue that need to be restocked, * and we have free pre-allocated buffers, fill the ranks as much * as we can, pulling from rx_free. * * This moves the 'write' idx forward to catch up with 'processed', and * also updates the memory address in the firmware to reference the new * target buffer. */ static void il3945_rx_queue_restock(struct il_priv *il) { struct il_rx_queue *rxq = &il->rxq; struct list_head *element; struct il_rx_buf *rxb; unsigned long flags; int write; spin_lock_irqsave(&rxq->lock, flags); write = rxq->write & ~0x7; while (il_rx_queue_space(rxq) > 0 && rxq->free_count) { /* Get next free Rx buffer, remove from free list */ element = rxq->rx_free.next; rxb = list_entry(element, struct il_rx_buf, list); list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ rxq->bd[rxq->write] = il3945_dma_addr2rbd_ptr(il, rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; } spin_unlock_irqrestore(&rxq->lock, flags); /* If the pre-allocated buffer pool is dropping low, schedule to * refill it */ if (rxq->free_count <= RX_LOW_WATERMARK) queue_work(il->workqueue, &il->rx_replenish); /* If we've added more space for the firmware to place data, tell it. * Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7) || abs(rxq->write - rxq->read) > 7) { spin_lock_irqsave(&rxq->lock, flags); rxq->need_update = 1; spin_unlock_irqrestore(&rxq->lock, flags); il_rx_queue_update_write_ptr(il, rxq); } } /** * il3945_rx_replenish - Move all used packet from rx_used to rx_free * * When moving to rx_free an SKB is allocated for the slot. * * Also restock the Rx queue via il3945_rx_queue_restock. * This is called as a scheduled work item (except for during initialization) */ static void il3945_rx_allocate(struct il_priv *il, gfp_t priority) { struct il_rx_queue *rxq = &il->rxq; struct list_head *element; struct il_rx_buf *rxb; struct page *page; unsigned long flags; gfp_t gfp_mask = priority; while (1) { spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); return; } spin_unlock_irqrestore(&rxq->lock, flags); if (rxq->free_count > RX_LOW_WATERMARK) gfp_mask |= __GFP_NOWARN; if (il->hw_params.rx_page_order > 0) gfp_mask |= __GFP_COMP; /* Alloc a new receive buffer */ page = alloc_pages(gfp_mask, il->hw_params.rx_page_order); if (!page) { if (net_ratelimit()) D_INFO("Failed to allocate SKB buffer.\n"); if (rxq->free_count <= RX_LOW_WATERMARK && net_ratelimit()) IL_ERR("Failed to allocate SKB buffer with %0x." "Only %u free buffers remaining.\n", priority, rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ break; } spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); __free_pages(page, il->hw_params.rx_page_order); return; } element = rxq->rx_used.next; rxb = list_entry(element, struct il_rx_buf, list); list_del(element); spin_unlock_irqrestore(&rxq->lock, flags); rxb->page = page; /* Get physical address of RB/SKB */ rxb->page_dma = pci_map_page(il->pci_dev, page, 0, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); spin_lock_irqsave(&rxq->lock, flags); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; il->alloc_rxb_page++; spin_unlock_irqrestore(&rxq->lock, flags); } } void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq) { unsigned long flags; int i; spin_lock_irqsave(&rxq->lock, flags); INIT_LIST_HEAD(&rxq->rx_free); INIT_LIST_HEAD(&rxq->rx_used); /* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { /* In the reset function, these buffers may have been allocated * to an SKB, so we need to unmap and free potential storage */ if (rxq->pool[i].page != NULL) { pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); __il_free_pages(il, rxq->pool[i].page); rxq->pool[i].page = NULL; } list_add_tail(&rxq->pool[i].list, &rxq->rx_used); } /* Set us so that we have processed and used all buffers, but have * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; rxq->write_actual = 0; rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); } void il3945_rx_replenish(void *data) { struct il_priv *il = data; unsigned long flags; il3945_rx_allocate(il, GFP_KERNEL); spin_lock_irqsave(&il->lock, flags); il3945_rx_queue_restock(il); spin_unlock_irqrestore(&il->lock, flags); } static void il3945_rx_replenish_now(struct il_priv *il) { il3945_rx_allocate(il, GFP_ATOMIC); il3945_rx_queue_restock(il); } /* Assumes that the skb field of the buffers in 'pool' is kept accurate. * If an SKB has been detached, the POOL needs to have its SKB set to NULL * This free routine walks the list of POOL entries and if SKB is set to * non NULL it is unmapped and freed */ static void il3945_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq) { int i; for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { if (rxq->pool[i].page != NULL) { pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); __il_free_pages(il, rxq->pool[i].page); rxq->pool[i].page = NULL; } } dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status), rxq->rb_stts, rxq->rb_stts_dma); rxq->bd = NULL; rxq->rb_stts = NULL; } /* Convert linear signal-to-noise ratio into dB */ static u8 ratio2dB[100] = { /* 0 1 2 3 4 5 6 7 8 9 */ 0, 0, 6, 10, 12, 14, 16, 17, 18, 19, /* 00 - 09 */ 20, 21, 22, 22, 23, 23, 24, 25, 26, 26, /* 10 - 19 */ 26, 26, 26, 27, 27, 28, 28, 28, 29, 29, /* 20 - 29 */ 29, 30, 30, 30, 31, 31, 31, 31, 32, 32, /* 30 - 39 */ 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, /* 40 - 49 */ 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, /* 50 - 59 */ 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, /* 60 - 69 */ 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, /* 70 - 79 */ 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, /* 80 - 89 */ 39, 39, 39, 39, 39, 40, 40, 40, 40, 40 /* 90 - 99 */ }; /* Calculates a relative dB value from a ratio of linear * (i.e. not dB) signal levels. * Conversion assumes that levels are voltages (20*log), not powers (10*log). */ int il3945_calc_db_from_ratio(int sig_ratio) { /* 1000:1 or higher just report as 60 dB */ if (sig_ratio >= 1000) return 60; /* 100:1 or higher, divide by 10 and use table, * add 20 dB to make up for divide by 10 */ if (sig_ratio >= 100) return 20 + (int)ratio2dB[sig_ratio / 10]; /* We shouldn't see this */ if (sig_ratio < 1) return 0; /* Use table for ratios 1:1 - 99:1 */ return (int)ratio2dB[sig_ratio]; } /** * il3945_rx_handle - Main entry function for receiving responses from uCode * * Uses the il->handlers callback function array to invoke * the appropriate handlers, including command responses, * frame-received notifications, and other notifications. */ static void il3945_rx_handle(struct il_priv *il) { struct il_rx_buf *rxb; struct il_rx_pkt *pkt; struct il_rx_queue *rxq = &il->rxq; u32 r, i; int reclaim; unsigned long flags; u8 fill_rx = 0; u32 count = 8; int total_empty = 0; /* uCode's read idx (stored in shared DRAM) indicates the last Rx * buffer that the driver may process (last buffer filled by ucode). */ r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; i = rxq->read; /* calculate total frames need to be restock after handling RX */ total_empty = r - rxq->write_actual; if (total_empty < 0) total_empty += RX_QUEUE_SIZE; if (total_empty > (RX_QUEUE_SIZE / 2)) fill_rx = 1; /* Rx interrupt, but nothing sent from uCode */ if (i == r) D_RX("r = %d, i = %d\n", r, i); while (i != r) { int len; rxb = rxq->queue[i]; /* If an RXB doesn't have a Rx queue slot associated with it, * then a bug has been introduced in the queue refilling * routines -- catch it here */ BUG_ON(rxb == NULL); rxq->queue[i] = NULL; pci_unmap_page(il->pci_dev, rxb->page_dma, PAGE_SIZE << il->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); pkt = rxb_addr(rxb); len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; len += sizeof(u32); /* account for status word */ /* Reclaim a command buffer only if this packet is a response * to a (driver-originated) command. * If the packet (e.g. Rx frame) originated from uCode, * there is no command buffer to reclaim. * Ucode should set SEQ_RX_FRAME bit if ucode-originated, * but apparently a few don't get set; catch them here. */ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && pkt->hdr.cmd != N_STATS && pkt->hdr.cmd != C_TX; /* Based on type of command response or notification, * handle those that need handling via function in * handlers table. See il3945_setup_handlers() */ if (il->handlers[pkt->hdr.cmd]) { D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); il->isr_stats.handlers[pkt->hdr.cmd]++; il->handlers[pkt->hdr.cmd] (il, rxb); } else { /* No handling needed */ D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r, i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); } /* * XXX: After here, we should always check rxb->page * against NULL before touching it or its virtual * memory (pkt). Because some handler might have * already taken or freed the pages. */ if (reclaim) { /* Invoke any callbacks, transfer the buffer to caller, * and fire off the (possibly) blocking il_send_cmd() * as we reclaim the driver command queue */ if (rxb->page) il_tx_cmd_complete(il, rxb); else IL_WARN("Claim null rxb?\n"); } /* Reuse the page if possible. For notification packets and * SKBs that fail to Rx correctly, add them back into the * rx_free list for reuse later. */ spin_lock_irqsave(&rxq->lock, flags); if (rxb->page != NULL) { rxb->page_dma = pci_map_page(il->pci_dev, rxb->page, 0, PAGE_SIZE << il->hw_params. rx_page_order, PCI_DMA_FROMDEVICE); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; } else list_add_tail(&rxb->list, &rxq->rx_used); spin_unlock_irqrestore(&rxq->lock, flags); i = (i + 1) & RX_QUEUE_MASK; /* If there are a lot of unused frames, * restock the Rx queue so ucode won't assert. */ if (fill_rx) { count++; if (count >= 8) { rxq->read = i; il3945_rx_replenish_now(il); count = 0; } } } /* Backtrack one entry */ rxq->read = i; if (fill_rx) il3945_rx_replenish_now(il); else il3945_rx_queue_restock(il); } /* call this function to flush any scheduled tasklet */ static inline void il3945_synchronize_irq(struct il_priv *il) { /* wait to make sure we flush pending tasklet */ synchronize_irq(il->pci_dev->irq); tasklet_kill(&il->irq_tasklet); } static const char * il3945_desc_lookup(int i) { switch (i) { case 1: return "FAIL"; case 2: return "BAD_PARAM"; case 3: return "BAD_CHECKSUM"; case 4: return "NMI_INTERRUPT"; case 5: return "SYSASSERT"; case 6: return "FATAL_ERROR"; } return "UNKNOWN"; } #define ERROR_START_OFFSET (1 * sizeof(u32)) #define ERROR_ELEM_SIZE (7 * sizeof(u32)) void il3945_dump_nic_error_log(struct il_priv *il) { u32 i; u32 desc, time, count, base, data1; u32 blink1, blink2, ilink1, ilink2; base = le32_to_cpu(il->card_alive.error_event_table_ptr); if (!il3945_hw_valid_rtc_data_addr(base)) { IL_ERR("Not valid error log pointer 0x%08X\n", base); return; } count = il_read_targ_mem(il, base); if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { IL_ERR("Start IWL Error Log Dump:\n"); IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count); } IL_ERR("Desc Time asrtPC blink2 " "ilink1 nmiPC Line\n"); for (i = ERROR_START_OFFSET; i < (count * ERROR_ELEM_SIZE) + ERROR_START_OFFSET; i += ERROR_ELEM_SIZE) { desc = il_read_targ_mem(il, base + i); time = il_read_targ_mem(il, base + i + 1 * sizeof(u32)); blink1 = il_read_targ_mem(il, base + i + 2 * sizeof(u32)); blink2 = il_read_targ_mem(il, base + i + 3 * sizeof(u32)); ilink1 = il_read_targ_mem(il, base + i + 4 * sizeof(u32)); ilink2 = il_read_targ_mem(il, base + i + 5 * sizeof(u32)); data1 = il_read_targ_mem(il, base + i + 6 * sizeof(u32)); IL_ERR("%-13s (0x%X) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n", il3945_desc_lookup(desc), desc, time, blink1, blink2, ilink1, ilink2, data1); } } static void il3945_irq_tasklet(struct il_priv *il) { u32 inta, handled = 0; u32 inta_fh; unsigned long flags; #ifdef CONFIG_IWLEGACY_DEBUG u32 inta_mask; #endif spin_lock_irqsave(&il->lock, flags); /* Ack/clear/reset pending uCode interrupts. * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, * and will clear only when CSR_FH_INT_STATUS gets cleared. */ inta = _il_rd(il, CSR_INT); _il_wr(il, CSR_INT, inta); /* Ack/clear/reset pending flow-handler (DMA) interrupts. * Any new interrupts that happen after this, either while we're * in this tasklet, or later, will show up in next ISR/tasklet. */ inta_fh = _il_rd(il, CSR_FH_INT_STATUS); _il_wr(il, CSR_FH_INT_STATUS, inta_fh); #ifdef CONFIG_IWLEGACY_DEBUG if (il_get_debug_level(il) & IL_DL_ISR) { /* just for debug */ inta_mask = _il_rd(il, CSR_INT_MASK); D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask, inta_fh); } #endif spin_unlock_irqrestore(&il->lock, flags); /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not * atomic, make sure that inta covers all the interrupts that * we've discovered, even if FH interrupt came in just after * reading CSR_INT. */ if (inta_fh & CSR39_FH_INT_RX_MASK) inta |= CSR_INT_BIT_FH_RX; if (inta_fh & CSR39_FH_INT_TX_MASK) inta |= CSR_INT_BIT_FH_TX; /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { IL_ERR("Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ il_disable_interrupts(il); il->isr_stats.hw++; il_irq_handle_error(il); handled |= CSR_INT_BIT_HW_ERR; return; } #ifdef CONFIG_IWLEGACY_DEBUG if (il_get_debug_level(il) & (IL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { D_ISR("Scheduler finished to transmit " "the frame/frames.\n"); il->isr_stats.sch++; } /* Alive notification via Rx interrupt will do the real work */ if (inta & CSR_INT_BIT_ALIVE) { D_ISR("Alive interrupt\n"); il->isr_stats.alive++; } } #endif /* Safely ignore these bits for debug checks below */ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); /* Error detected by uCode */ if (inta & CSR_INT_BIT_SW_ERR) { IL_ERR("Microcode SW error detected. " "Restarting 0x%X.\n", inta); il->isr_stats.sw++; il_irq_handle_error(il); handled |= CSR_INT_BIT_SW_ERR; } /* uCode wakes up after power-down sleep */ if (inta & CSR_INT_BIT_WAKEUP) { D_ISR("Wakeup interrupt\n"); il_rx_queue_update_write_ptr(il, &il->rxq); il_txq_update_write_ptr(il, &il->txq[0]); il_txq_update_write_ptr(il, &il->txq[1]); il_txq_update_write_ptr(il, &il->txq[2]); il_txq_update_write_ptr(il, &il->txq[3]); il_txq_update_write_ptr(il, &il->txq[4]); il_txq_update_write_ptr(il, &il->txq[5]); il->isr_stats.wakeup++; handled |= CSR_INT_BIT_WAKEUP; } /* All uCode command responses, including Tx command responses, * Rx "responses" (frame-received notification), and other * notifications from uCode come through here*/ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { il3945_rx_handle(il); il->isr_stats.rx++; handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); } if (inta & CSR_INT_BIT_FH_TX) { D_ISR("Tx interrupt\n"); il->isr_stats.tx++; _il_wr(il, CSR_FH_INT_STATUS, (1 << 6)); il_wr(il, FH39_TCSR_CREDIT(FH39_SRVC_CHNL), 0x0); handled |= CSR_INT_BIT_FH_TX; } if (inta & ~handled) { IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled); il->isr_stats.unhandled++; } if (inta & ~il->inta_mask) { IL_WARN("Disabled INTA bits 0x%08x were pending\n", inta & ~il->inta_mask); IL_WARN(" with inta_fh = 0x%08x\n", inta_fh); } /* Re-enable all interrupts */ /* only Re-enable if disabled by irq */ if (test_bit(S_INT_ENABLED, &il->status)) il_enable_interrupts(il); #ifdef CONFIG_IWLEGACY_DEBUG if (il_get_debug_level(il) & (IL_DL_ISR)) { inta = _il_rd(il, CSR_INT); inta_mask = _il_rd(il, CSR_INT_MASK); inta_fh = _il_rd(il, CSR_FH_INT_STATUS); D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); } #endif } static int il3945_get_channels_for_scan(struct il_priv *il, enum ieee80211_band band, u8 is_active, u8 n_probes, struct il3945_scan_channel *scan_ch, struct ieee80211_vif *vif) { struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; const struct il_channel_info *ch_info; u16 passive_dwell = 0; u16 active_dwell = 0; int added, i; sband = il_get_hw_mode(il, band); if (!sband) return 0; active_dwell = il_get_active_dwell_time(il, band, n_probes); passive_dwell = il_get_passive_dwell_time(il, band, vif); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; for (i = 0, added = 0; i < il->scan_request->n_channels; i++) { chan = il->scan_request->channels[i]; if (chan->band != band) continue; scan_ch->channel = chan->hw_value; ch_info = il_get_channel_info(il, band, scan_ch->channel); if (!il_is_channel_valid(ch_info)) { D_SCAN("Channel %d is INVALID for this band.\n", scan_ch->channel); continue; } scan_ch->active_dwell = cpu_to_le16(active_dwell); scan_ch->passive_dwell = cpu_to_le16(passive_dwell); /* If passive , set up for auto-switch * and use long active_dwell time. */ if (!is_active || il_is_channel_passive(ch_info) || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) { scan_ch->type = 0; /* passive */ if (IL_UCODE_API(il->ucode_ver) == 1) scan_ch->active_dwell = cpu_to_le16(passive_dwell - 1); } else { scan_ch->type = 1; /* active */ } /* Set direct probe bits. These may be used both for active * scan channels (probes gets sent right away), * or for passive channels (probes get se sent only after * hearing clear Rx packet).*/ if (IL_UCODE_API(il->ucode_ver) >= 2) { if (n_probes) scan_ch->type |= IL39_SCAN_PROBE_MASK(n_probes); } else { /* uCode v1 does not allow setting direct probe bits on * passive channel. */ if ((scan_ch->type & 1) && n_probes) scan_ch->type |= IL39_SCAN_PROBE_MASK(n_probes); } /* Set txpower levels to defaults */ scan_ch->tpc.dsp_atten = 110; /* scan_pwr_info->tpc.dsp_atten; */ /*scan_pwr_info->tpc.tx_gain; */ if (band == IEEE80211_BAND_5GHZ) scan_ch->tpc.tx_gain = ((1 << 5) | (3 << 3)) | 3; else { scan_ch->tpc.tx_gain = ((1 << 5) | (5 << 3)); /* NOTE: if we were doing 6Mb OFDM for scans we'd use * power level: * scan_ch->tpc.tx_gain = ((1 << 5) | (2 << 3)) | 3; */ } D_SCAN("Scanning %d [%s %d]\n", scan_ch->channel, (scan_ch->type & 1) ? "ACTIVE" : "PASSIVE", (scan_ch->type & 1) ? active_dwell : passive_dwell); scan_ch++; added++; } D_SCAN("total channels to scan %d\n", added); return added; } static void il3945_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates) { int i; for (i = 0; i < RATE_COUNT_LEGACY; i++) { rates[i].bitrate = il3945_rates[i].ieee * 5; rates[i].hw_value = i; /* Rate scaling will work on idxes */ rates[i].hw_value_short = i; rates[i].flags = 0; if (i > IL39_LAST_OFDM_RATE || i < IL_FIRST_OFDM_RATE) { /* * If CCK != 1M then set short preamble rate flag. */ rates[i].flags |= (il3945_rates[i].plcp == 10) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE; } } } /****************************************************************************** * * uCode download functions * ******************************************************************************/ static void il3945_dealloc_ucode_pci(struct il_priv *il) { il_free_fw_desc(il->pci_dev, &il->ucode_code); il_free_fw_desc(il->pci_dev, &il->ucode_data); il_free_fw_desc(il->pci_dev, &il->ucode_data_backup); il_free_fw_desc(il->pci_dev, &il->ucode_init); il_free_fw_desc(il->pci_dev, &il->ucode_init_data); il_free_fw_desc(il->pci_dev, &il->ucode_boot); } /** * il3945_verify_inst_full - verify runtime uCode image in card vs. host, * looking at all data. */ static int il3945_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) { u32 val; u32 save_len = len; int rc = 0; u32 errcnt; D_INFO("ucode inst image size is %u\n", len); il_wr(il, HBUS_TARG_MEM_RADDR, IL39_RTC_INST_LOWER_BOUND); errcnt = 0; for (; len > 0; len -= sizeof(u32), image++) { /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IL_DL_IO is set */ val = _il_rd(il, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { IL_ERR("uCode INST section is invalid at " "offset 0x%x, is 0x%x, s/b 0x%x\n", save_len - len, val, le32_to_cpu(*image)); rc = -EIO; errcnt++; if (errcnt >= 20) break; } } if (!errcnt) D_INFO("ucode image in INSTRUCTION memory is good\n"); return rc; } /** * il3945_verify_inst_sparse - verify runtime uCode image in card vs. host, * using sample data 100 bytes apart. If these sample points are good, * it's a pretty good bet that everything between them is good, too. */ static int il3945_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) { u32 val; int rc = 0; u32 errcnt = 0; u32 i; D_INFO("ucode inst image size is %u\n", len); for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IL_DL_IO is set */ il_wr(il, HBUS_TARG_MEM_RADDR, i + IL39_RTC_INST_LOWER_BOUND); val = _il_rd(il, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { #if 0 /* Enable this if you want to see details */ IL_ERR("uCode INST section is invalid at " "offset 0x%x, is 0x%x, s/b 0x%x\n", i, val, *image); #endif rc = -EIO; errcnt++; if (errcnt >= 3) break; } } return rc; } /** * il3945_verify_ucode - determine which instruction image is in SRAM, * and verify its contents */ static int il3945_verify_ucode(struct il_priv *il) { __le32 *image; u32 len; int rc = 0; /* Try bootstrap */ image = (__le32 *) il->ucode_boot.v_addr; len = il->ucode_boot.len; rc = il3945_verify_inst_sparse(il, image, len); if (rc == 0) { D_INFO("Bootstrap uCode is good in inst SRAM\n"); return 0; } /* Try initialize */ image = (__le32 *) il->ucode_init.v_addr; len = il->ucode_init.len; rc = il3945_verify_inst_sparse(il, image, len); if (rc == 0) { D_INFO("Initialize uCode is good in inst SRAM\n"); return 0; } /* Try runtime/protocol */ image = (__le32 *) il->ucode_code.v_addr; len = il->ucode_code.len; rc = il3945_verify_inst_sparse(il, image, len); if (rc == 0) { D_INFO("Runtime uCode is good in inst SRAM\n"); return 0; } IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); /* Since nothing seems to match, show first several data entries in * instruction SRAM, so maybe visual inspection will give a clue. * Selection of bootstrap image (vs. other images) is arbitrary. */ image = (__le32 *) il->ucode_boot.v_addr; len = il->ucode_boot.len; rc = il3945_verify_inst_full(il, image, len); return rc; } static void il3945_nic_start(struct il_priv *il) { /* Remove all resets to allow NIC to operate */ _il_wr(il, CSR_RESET, 0); } #define IL3945_UCODE_GET(item) \ static u32 il3945_ucode_get_##item(const struct il_ucode_header *ucode)\ { \ return le32_to_cpu(ucode->v1.item); \ } static u32 il3945_ucode_get_header_size(u32 api_ver) { return 24; } static u8 * il3945_ucode_get_data(const struct il_ucode_header *ucode) { return (u8 *) ucode->v1.data; } IL3945_UCODE_GET(inst_size); IL3945_UCODE_GET(data_size); IL3945_UCODE_GET(init_size); IL3945_UCODE_GET(init_data_size); IL3945_UCODE_GET(boot_size); /** * il3945_read_ucode - Read uCode images from disk file. * * Copy into buffers for card to fetch via bus-mastering */ static int il3945_read_ucode(struct il_priv *il) { const struct il_ucode_header *ucode; int ret = -EINVAL, idx; const struct firmware *ucode_raw; /* firmware file name contains uCode/driver compatibility version */ const char *name_pre = il->cfg->fw_name_pre; const unsigned int api_max = il->cfg->ucode_api_max; const unsigned int api_min = il->cfg->ucode_api_min; char buf[25]; u8 *src; size_t len; u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size; /* Ask kernel firmware_class module to get the boot firmware off disk. * request_firmware() is synchronous, file is in memory on return. */ for (idx = api_max; idx >= api_min; idx--) { sprintf(buf, "%s%u%s", name_pre, idx, ".ucode"); ret = request_firmware(&ucode_raw, buf, &il->pci_dev->dev); if (ret < 0) { IL_ERR("%s firmware file req failed: %d\n", buf, ret); if (ret == -ENOENT) continue; else goto error; } else { if (idx < api_max) IL_ERR("Loaded firmware %s, " "which is deprecated. " " Please use API v%u instead.\n", buf, api_max); D_INFO("Got firmware '%s' file " "(%zd bytes) from disk\n", buf, ucode_raw->size); break; } } if (ret < 0) goto error; /* Make sure that we got at least our header! */ if (ucode_raw->size < il3945_ucode_get_header_size(1)) { IL_ERR("File size way too small!\n"); ret = -EINVAL; goto err_release; } /* Data from ucode file: header followed by uCode images */ ucode = (struct il_ucode_header *)ucode_raw->data; il->ucode_ver = le32_to_cpu(ucode->ver); api_ver = IL_UCODE_API(il->ucode_ver); inst_size = il3945_ucode_get_inst_size(ucode); data_size = il3945_ucode_get_data_size(ucode); init_size = il3945_ucode_get_init_size(ucode); init_data_size = il3945_ucode_get_init_data_size(ucode); boot_size = il3945_ucode_get_boot_size(ucode); src = il3945_ucode_get_data(ucode); /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely * on the API version read from firmware header from here on forward */ if (api_ver < api_min || api_ver > api_max) { IL_ERR("Driver unable to support your firmware API. " "Driver supports v%u, firmware is v%u.\n", api_max, api_ver); il->ucode_ver = 0; ret = -EINVAL; goto err_release; } if (api_ver != api_max) IL_ERR("Firmware has old API version. Expected %u, " "got %u. New firmware can be obtained " "from http://www.intellinuxwireless.org.\n", api_max, api_ver); IL_INFO("loaded firmware version %u.%u.%u.%u\n", IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version), "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); D_INFO("f/w package hdr runtime inst size = %u\n", inst_size); D_INFO("f/w package hdr runtime data size = %u\n", data_size); D_INFO("f/w package hdr init inst size = %u\n", init_size); D_INFO("f/w package hdr init data size = %u\n", init_data_size); D_INFO("f/w package hdr boot inst size = %u\n", boot_size); /* Verify size of file vs. image size info in file's header */ if (ucode_raw->size != il3945_ucode_get_header_size(api_ver) + inst_size + data_size + init_size + init_data_size + boot_size) { D_INFO("uCode file size %zd does not match expected size\n", ucode_raw->size); ret = -EINVAL; goto err_release; } /* Verify that uCode images will fit in card's SRAM */ if (inst_size > IL39_MAX_INST_SIZE) { D_INFO("uCode instr len %d too large to fit in\n", inst_size); ret = -EINVAL; goto err_release; } if (data_size > IL39_MAX_DATA_SIZE) { D_INFO("uCode data len %d too large to fit in\n", data_size); ret = -EINVAL; goto err_release; } if (init_size > IL39_MAX_INST_SIZE) { D_INFO("uCode init instr len %d too large to fit in\n", init_size); ret = -EINVAL; goto err_release; } if (init_data_size > IL39_MAX_DATA_SIZE) { D_INFO("uCode init data len %d too large to fit in\n", init_data_size); ret = -EINVAL; goto err_release; } if (boot_size > IL39_MAX_BSM_SIZE) { D_INFO("uCode boot instr len %d too large to fit in\n", boot_size); ret = -EINVAL; goto err_release; } /* Allocate ucode buffers for card's bus-master loading ... */ /* Runtime instructions and 2 copies of data: * 1) unmodified from disk * 2) backup cache for save/restore during power-downs */ il->ucode_code.len = inst_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_code); il->ucode_data.len = data_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_data); il->ucode_data_backup.len = data_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup); if (!il->ucode_code.v_addr || !il->ucode_data.v_addr || !il->ucode_data_backup.v_addr) goto err_pci_alloc; /* Initialization instructions and data */ if (init_size && init_data_size) { il->ucode_init.len = init_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_init); il->ucode_init_data.len = init_data_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data); if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr) goto err_pci_alloc; } /* Bootstrap (instructions only, no data) */ if (boot_size) { il->ucode_boot.len = boot_size; il_alloc_fw_desc(il->pci_dev, &il->ucode_boot); if (!il->ucode_boot.v_addr) goto err_pci_alloc; } /* Copy images into buffers for card's bus-master reads ... */ /* Runtime instructions (first block of data in file) */ len = inst_size; D_INFO("Copying (but not loading) uCode instr len %zd\n", len); memcpy(il->ucode_code.v_addr, src, len); src += len; D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", il->ucode_code.v_addr, (u32) il->ucode_code.p_addr); /* Runtime data (2nd block) * NOTE: Copy into backup buffer will be done in il3945_up() */ len = data_size; D_INFO("Copying (but not loading) uCode data len %zd\n", len); memcpy(il->ucode_data.v_addr, src, len); memcpy(il->ucode_data_backup.v_addr, src, len); src += len; /* Initialization instructions (3rd block) */ if (init_size) { len = init_size; D_INFO("Copying (but not loading) init instr len %zd\n", len); memcpy(il->ucode_init.v_addr, src, len); src += len; } /* Initialization data (4th block) */ if (init_data_size) { len = init_data_size; D_INFO("Copying (but not loading) init data len %zd\n", len); memcpy(il->ucode_init_data.v_addr, src, len); src += len; } /* Bootstrap instructions (5th block) */ len = boot_size; D_INFO("Copying (but not loading) boot instr len %zd\n", len); memcpy(il->ucode_boot.v_addr, src, len); /* We have our copies now, allow OS release its copies */ release_firmware(ucode_raw); return 0; err_pci_alloc: IL_ERR("failed to allocate pci memory\n"); ret = -ENOMEM; il3945_dealloc_ucode_pci(il); err_release: release_firmware(ucode_raw); error: return ret; } /** * il3945_set_ucode_ptrs - Set uCode address location * * Tell initialization uCode where to find runtime uCode. * * BSM registers initially contain pointers to initialization uCode. * We need to replace them to load runtime uCode inst and data, * and to save runtime data when powering down. */ static int il3945_set_ucode_ptrs(struct il_priv *il) { dma_addr_t pinst; dma_addr_t pdata; /* bits 31:0 for 3945 */ pinst = il->ucode_code.p_addr; pdata = il->ucode_data_backup.p_addr; /* Tell bootstrap uCode where to find image to load */ il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); /* Inst byte count must be last to set up, bit 31 signals uCode * that all new ptr/size info is in place */ il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, il->ucode_code.len | BSM_DRAM_INST_LOAD); D_INFO("Runtime uCode pointers are set.\n"); return 0; } /** * il3945_init_alive_start - Called after N_ALIVE notification received * * Called after N_ALIVE notification received from "initialize" uCode. * * Tell "initialize" uCode to go ahead and load the runtime uCode. */ static void il3945_init_alive_start(struct il_priv *il) { /* Check alive response for "valid" sign from uCode */ if (il->card_alive_init.is_valid != UCODE_VALID_OK) { /* We had an error bringing up the hardware, so take it * all the way back down so we can try again */ D_INFO("Initialize Alive failed.\n"); goto restart; } /* Bootstrap uCode has loaded initialize uCode ... verify inst image. * This is a paranoid check, because we would not have gotten the * "initialize" alive if code weren't properly loaded. */ if (il3945_verify_ucode(il)) { /* Runtime instruction load was bad; * take it all the way back down so we can try again */ D_INFO("Bad \"initialize\" uCode load.\n"); goto restart; } /* Send pointers to protocol/runtime uCode image ... init code will * load and launch runtime uCode, which will send us another "Alive" * notification. */ D_INFO("Initialization Alive received.\n"); if (il3945_set_ucode_ptrs(il)) { /* Runtime instruction load won't happen; * take it all the way back down so we can try again */ D_INFO("Couldn't set up uCode pointers.\n"); goto restart; } return; restart: queue_work(il->workqueue, &il->restart); } /** * il3945_alive_start - called after N_ALIVE notification received * from protocol/runtime uCode (initialization uCode's * Alive gets handled by il3945_init_alive_start()). */ static void il3945_alive_start(struct il_priv *il) { int thermal_spin = 0; u32 rfkill; D_INFO("Runtime Alive received.\n"); if (il->card_alive.is_valid != UCODE_VALID_OK) { /* We had an error bringing up the hardware, so take it * all the way back down so we can try again */ D_INFO("Alive failed.\n"); goto restart; } /* Initialize uCode has loaded Runtime uCode ... verify inst image. * This is a paranoid check, because we would not have gotten the * "runtime" alive if code weren't properly loaded. */ if (il3945_verify_ucode(il)) { /* Runtime instruction load was bad; * take it all the way back down so we can try again */ D_INFO("Bad runtime uCode load.\n"); goto restart; } rfkill = il_rd_prph(il, APMG_RFKILL_REG); D_INFO("RFKILL status: 0x%x\n", rfkill); if (rfkill & 0x1) { clear_bit(S_RFKILL, &il->status); /* if RFKILL is not on, then wait for thermal * sensor in adapter to kick in */ while (il3945_hw_get_temperature(il) == 0) { thermal_spin++; udelay(10); } if (thermal_spin) D_INFO("Thermal calibration took %dus\n", thermal_spin * 10); } else set_bit(S_RFKILL, &il->status); /* After the ALIVE response, we can send commands to 3945 uCode */ set_bit(S_ALIVE, &il->status); /* Enable watchdog to monitor the driver tx queues */ il_setup_watchdog(il); if (il_is_rfkill(il)) return; ieee80211_wake_queues(il->hw); il->active_rate = RATES_MASK_3945; il_power_update_mode(il, true); if (il_is_associated(il)) { struct il3945_rxon_cmd *active_rxon = (struct il3945_rxon_cmd *)(&il->active); il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; } else { /* Initialize our rx_config data */ il_connection_init_rx_config(il); } /* Configure Bluetooth device coexistence support */ il_send_bt_config(il); set_bit(S_READY, &il->status); /* Configure the adapter for unassociated operation */ il3945_commit_rxon(il); il3945_reg_txpower_periodic(il); D_INFO("ALIVE processing complete.\n"); wake_up(&il->wait_command_queue); return; restart: queue_work(il->workqueue, &il->restart); } static void il3945_cancel_deferred_work(struct il_priv *il); static void __il3945_down(struct il_priv *il) { unsigned long flags; int exit_pending; D_INFO(DRV_NAME " is going down\n"); il_scan_cancel_timeout(il, 200); exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status); /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set * to prevent rearm timer */ del_timer_sync(&il->watchdog); /* Station information will now be cleared in device */ il_clear_ucode_stations(il); il_dealloc_bcast_stations(il); il_clear_driver_stations(il); /* Unblock any waiting calls */ wake_up_all(&il->wait_command_queue); /* Wipe out the EXIT_PENDING status bit if we are not actually * exiting the module */ if (!exit_pending) clear_bit(S_EXIT_PENDING, &il->status); /* stop and reset the on-board processor */ _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); /* tell the device to stop sending interrupts */ spin_lock_irqsave(&il->lock, flags); il_disable_interrupts(il); spin_unlock_irqrestore(&il->lock, flags); il3945_synchronize_irq(il); if (il->mac80211_registered) ieee80211_stop_queues(il->hw); /* If we have not previously called il3945_init() then * clear all bits but the RF Kill bits and return */ if (!il_is_init(il)) { il->status = test_bit(S_RFKILL, &il->status) << S_RFKILL | test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; goto exit; } /* ...otherwise clear out all the status bits but the RF Kill * bit and continue taking the NIC down. */ il->status &= test_bit(S_RFKILL, &il->status) << S_RFKILL | test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR | test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; /* * We disabled and synchronized interrupt, and priv->mutex is taken, so * here is the only thread which will program device registers, but * still have lockdep assertions, so we are taking reg_lock. */ spin_lock_irq(&il->reg_lock); /* FIXME: il_grab_nic_access if rfkill is off ? */ il3945_hw_txq_ctx_stop(il); il3945_hw_rxq_stop(il); /* Power-down device's busmaster DMA clocks */ _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(5); /* Stop the device, and put it in low power state */ _il_apm_stop(il); spin_unlock_irq(&il->reg_lock); il3945_hw_txq_ctx_free(il); exit: memset(&il->card_alive, 0, sizeof(struct il_alive_resp)); if (il->beacon_skb) dev_kfree_skb(il->beacon_skb); il->beacon_skb = NULL; /* clear out any free frames */ il3945_clear_free_frames(il); } static void il3945_down(struct il_priv *il) { mutex_lock(&il->mutex); __il3945_down(il); mutex_unlock(&il->mutex); il3945_cancel_deferred_work(il); } #define MAX_HW_RESTARTS 5 static int il3945_alloc_bcast_station(struct il_priv *il) { unsigned long flags; u8 sta_id; spin_lock_irqsave(&il->sta_lock, flags); sta_id = il_prep_station(il, il_bcast_addr, false, NULL); if (sta_id == IL_INVALID_STATION) { IL_ERR("Unable to prepare broadcast station\n"); spin_unlock_irqrestore(&il->sta_lock, flags); return -EINVAL; } il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE; il->stations[sta_id].used |= IL_STA_BCAST; spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } static int __il3945_up(struct il_priv *il) { int rc, i; rc = il3945_alloc_bcast_station(il); if (rc) return rc; if (test_bit(S_EXIT_PENDING, &il->status)) { IL_WARN("Exit pending; will not bring the NIC up\n"); return -EIO; } if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) { IL_ERR("ucode not available for device bring up\n"); return -EIO; } /* If platform's RF_KILL switch is NOT set to KILL */ if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) clear_bit(S_RFKILL, &il->status); else { set_bit(S_RFKILL, &il->status); IL_WARN("Radio disabled by HW RF Kill switch\n"); return -ENODEV; } _il_wr(il, CSR_INT, 0xFFFFFFFF); rc = il3945_hw_nic_init(il); if (rc) { IL_ERR("Unable to int nic\n"); return rc; } /* make sure rfkill handshake bits are cleared */ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); /* clear (again), then enable host interrupts */ _il_wr(il, CSR_INT, 0xFFFFFFFF); il_enable_interrupts(il); /* really make sure rfkill handshake bits are cleared */ _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); /* Copy original ucode data image from disk into backup cache. * This will be used to initialize the on-board processor's * data SRAM for a clean start when the runtime program first loads. */ memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr, il->ucode_data.len); /* We return success when we resume from suspend and rf_kill is on. */ if (test_bit(S_RFKILL, &il->status)) return 0; for (i = 0; i < MAX_HW_RESTARTS; i++) { /* load bootstrap state machine, * load bootstrap program into processor's memory, * prepare to load the "initialize" uCode */ rc = il->ops->load_ucode(il); if (rc) { IL_ERR("Unable to set up bootstrap uCode: %d\n", rc); continue; } /* start card; "initialize" will load runtime ucode */ il3945_nic_start(il); D_INFO(DRV_NAME " is coming up\n"); return 0; } set_bit(S_EXIT_PENDING, &il->status); __il3945_down(il); clear_bit(S_EXIT_PENDING, &il->status); /* tried to restart and config the device for as long as our * patience could withstand */ IL_ERR("Unable to initialize device after %d attempts.\n", i); return -EIO; } /***************************************************************************** * * Workqueue callbacks * *****************************************************************************/ static void il3945_bg_init_alive_start(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, init_alive_start.work); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status)) goto out; il3945_init_alive_start(il); out: mutex_unlock(&il->mutex); } static void il3945_bg_alive_start(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, alive_start.work); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status) || il->txq == NULL) goto out; il3945_alive_start(il); out: mutex_unlock(&il->mutex); } /* * 3945 cannot interrupt driver when hardware rf kill switch toggles; * driver must poll CSR_GP_CNTRL_REG register for change. This register * *is* readable even when device has been SW_RESET into low power mode * (e.g. during RF KILL). */ static void il3945_rfkill_poll(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, _3945.rfkill_poll.work); bool old_rfkill = test_bit(S_RFKILL, &il->status); bool new_rfkill = !(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); if (new_rfkill != old_rfkill) { if (new_rfkill) set_bit(S_RFKILL, &il->status); else clear_bit(S_RFKILL, &il->status); wiphy_rfkill_set_hw_state(il->hw->wiphy, new_rfkill); D_RF_KILL("RF_KILL bit toggled to %s.\n", new_rfkill ? "disable radio" : "enable radio"); } /* Keep this running, even if radio now enabled. This will be * cancelled in mac_start() if system decides to start again */ queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, round_jiffies_relative(2 * HZ)); } int il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif) { struct il_host_cmd cmd = { .id = C_SCAN, .len = sizeof(struct il3945_scan_cmd), .flags = CMD_SIZE_HUGE, }; struct il3945_scan_cmd *scan; u8 n_probes = 0; enum ieee80211_band band; bool is_active = false; int ret; u16 len; lockdep_assert_held(&il->mutex); if (!il->scan_cmd) { il->scan_cmd = kmalloc(sizeof(struct il3945_scan_cmd) + IL_MAX_SCAN_SIZE, GFP_KERNEL); if (!il->scan_cmd) { D_SCAN("Fail to allocate scan memory\n"); return -ENOMEM; } } scan = il->scan_cmd; memset(scan, 0, sizeof(struct il3945_scan_cmd) + IL_MAX_SCAN_SIZE); scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH; scan->quiet_time = IL_ACTIVE_QUIET_TIME; if (il_is_associated(il)) { u16 interval; u32 extra; u32 suspend_time = 100; u32 scan_suspend_time = 100; D_INFO("Scanning while associated...\n"); interval = vif->bss_conf.beacon_int; scan->suspend_time = 0; scan->max_out_time = cpu_to_le32(200 * 1024); if (!interval) interval = suspend_time; /* * suspend time format: * 0-19: beacon interval in usec (time before exec.) * 20-23: 0 * 24-31: number of beacons (suspend between channels) */ extra = (suspend_time / interval) << 24; scan_suspend_time = 0xFF0FFFFF & (extra | ((suspend_time % interval) * 1024)); scan->suspend_time = cpu_to_le32(scan_suspend_time); D_SCAN("suspend_time 0x%X beacon interval %d\n", scan_suspend_time, interval); } if (il->scan_request->n_ssids) { int i, p = 0; D_SCAN("Kicking off active scan\n"); for (i = 0; i < il->scan_request->n_ssids; i++) { /* always does wildcard anyway */ if (!il->scan_request->ssids[i].ssid_len) continue; scan->direct_scan[p].id = WLAN_EID_SSID; scan->direct_scan[p].len = il->scan_request->ssids[i].ssid_len; memcpy(scan->direct_scan[p].ssid, il->scan_request->ssids[i].ssid, il->scan_request->ssids[i].ssid_len); n_probes++; p++; } is_active = true; } else D_SCAN("Kicking off passive scan.\n"); /* We don't build a direct scan probe request; the uCode will do * that based on the direct_mask added to each channel entry */ scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; scan->tx_cmd.sta_id = il->hw_params.bcast_id; scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; /* flags + rate selection */ switch (il->scan_band) { case IEEE80211_BAND_2GHZ: scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; scan->tx_cmd.rate = RATE_1M_PLCP; band = IEEE80211_BAND_2GHZ; break; case IEEE80211_BAND_5GHZ: scan->tx_cmd.rate = RATE_6M_PLCP; band = IEEE80211_BAND_5GHZ; break; default: IL_WARN("Invalid scan band\n"); return -EIO; } /* * If active scaning is requested but a certain channel is marked * passive, we can do active scanning if we detect transmissions. For * passive only scanning disable switching to active on any channel. */ scan->good_CRC_th = is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER; len = il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data, vif->addr, il->scan_request->ie, il->scan_request->ie_len, IL_MAX_SCAN_SIZE - sizeof(*scan)); scan->tx_cmd.len = cpu_to_le16(len); /* select Rx antennas */ scan->flags |= il3945_get_antenna_flags(il); scan->channel_count = il3945_get_channels_for_scan(il, band, is_active, n_probes, (void *)&scan->data[len], vif); if (scan->channel_count == 0) { D_SCAN("channel count %d\n", scan->channel_count); return -EIO; } cmd.len += le16_to_cpu(scan->tx_cmd.len) + scan->channel_count * sizeof(struct il3945_scan_channel); cmd.data = scan; scan->len = cpu_to_le16(cmd.len); set_bit(S_SCAN_HW, &il->status); ret = il_send_cmd_sync(il, &cmd); if (ret) clear_bit(S_SCAN_HW, &il->status); return ret; } void il3945_post_scan(struct il_priv *il) { /* * Since setting the RXON may have been deferred while * performing the scan, fire one off if needed */ if (memcmp(&il->staging, &il->active, sizeof(il->staging))) il3945_commit_rxon(il); } static void il3945_bg_restart(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, restart); if (test_bit(S_EXIT_PENDING, &il->status)) return; if (test_and_clear_bit(S_FW_ERROR, &il->status)) { mutex_lock(&il->mutex); il->is_open = 0; mutex_unlock(&il->mutex); il3945_down(il); ieee80211_restart_hw(il->hw); } else { il3945_down(il); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status)) { mutex_unlock(&il->mutex); return; } __il3945_up(il); mutex_unlock(&il->mutex); } } static void il3945_bg_rx_replenish(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, rx_replenish); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status)) goto out; il3945_rx_replenish(il); out: mutex_unlock(&il->mutex); } void il3945_post_associate(struct il_priv *il) { int rc = 0; struct ieee80211_conf *conf = NULL; if (!il->vif || !il->is_open) return; D_ASSOC("Associated as %d to: %pM\n", il->vif->bss_conf.aid, il->active.bssid_addr); if (test_bit(S_EXIT_PENDING, &il->status)) return; il_scan_cancel_timeout(il, 200); conf = &il->hw->conf; il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; il3945_commit_rxon(il); rc = il_send_rxon_timing(il); if (rc) IL_WARN("C_RXON_TIMING failed - " "Attempting to continue.\n"); il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; il->staging.assoc_id = cpu_to_le16(il->vif->bss_conf.aid); D_ASSOC("assoc id %d beacon interval %d\n", il->vif->bss_conf.aid, il->vif->bss_conf.beacon_int); if (il->vif->bss_conf.use_short_preamble) il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { if (il->vif->bss_conf.use_short_slot) il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; } il3945_commit_rxon(il); switch (il->vif->type) { case NL80211_IFTYPE_STATION: il3945_rate_scale_init(il->hw, IL_AP_ID); break; case NL80211_IFTYPE_ADHOC: il3945_send_beacon_cmd(il); break; default: IL_ERR("%s Should not be called in %d mode\n", __func__, il->vif->type); break; } } /***************************************************************************** * * mac80211 entry point functions * *****************************************************************************/ #define UCODE_READY_TIMEOUT (2 * HZ) static int il3945_mac_start(struct ieee80211_hw *hw) { struct il_priv *il = hw->priv; int ret; /* we should be verifying the device is ready to be opened */ mutex_lock(&il->mutex); D_MAC80211("enter\n"); /* fetch ucode file from disk, alloc and copy to bus-master buffers ... * ucode filename and max sizes are card-specific. */ if (!il->ucode_code.len) { ret = il3945_read_ucode(il); if (ret) { IL_ERR("Could not read microcode: %d\n", ret); mutex_unlock(&il->mutex); goto out_release_irq; } } ret = __il3945_up(il); mutex_unlock(&il->mutex); if (ret) goto out_release_irq; D_INFO("Start UP work.\n"); /* Wait for START_ALIVE from ucode. Otherwise callbacks from * mac80211 will not be run successfully. */ ret = wait_event_timeout(il->wait_command_queue, test_bit(S_READY, &il->status), UCODE_READY_TIMEOUT); if (!ret) { if (!test_bit(S_READY, &il->status)) { IL_ERR("Wait for START_ALIVE timeout after %dms.\n", jiffies_to_msecs(UCODE_READY_TIMEOUT)); ret = -ETIMEDOUT; goto out_release_irq; } } /* ucode is running and will send rfkill notifications, * no need to poll the killswitch state anymore */ cancel_delayed_work(&il->_3945.rfkill_poll); il->is_open = 1; D_MAC80211("leave\n"); return 0; out_release_irq: il->is_open = 0; D_MAC80211("leave - failed\n"); return ret; } static void il3945_mac_stop(struct ieee80211_hw *hw) { struct il_priv *il = hw->priv; D_MAC80211("enter\n"); if (!il->is_open) { D_MAC80211("leave - skip\n"); return; } il->is_open = 0; il3945_down(il); flush_workqueue(il->workqueue); /* start polling the killswitch state again */ queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, round_jiffies_relative(2 * HZ)); D_MAC80211("leave\n"); } static void il3945_mac_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct il_priv *il = hw->priv; D_MAC80211("enter\n"); D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); if (il3945_tx_skb(il, control->sta, skb)) dev_kfree_skb_any(skb); D_MAC80211("leave\n"); } void il3945_config_ap(struct il_priv *il) { struct ieee80211_vif *vif = il->vif; int rc = 0; if (test_bit(S_EXIT_PENDING, &il->status)) return; /* The following should be done only at AP bring up */ if (!(il_is_associated(il))) { /* RXON - unassoc (to set timing command) */ il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; il3945_commit_rxon(il); /* RXON Timing */ rc = il_send_rxon_timing(il); if (rc) IL_WARN("C_RXON_TIMING failed - " "Attempting to continue.\n"); il->staging.assoc_id = 0; if (vif->bss_conf.use_short_preamble) il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { if (vif->bss_conf.use_short_slot) il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; } /* restore RXON assoc */ il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; il3945_commit_rxon(il); } il3945_send_beacon_cmd(il); } static int il3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct il_priv *il = hw->priv; int ret = 0; u8 sta_id = IL_INVALID_STATION; u8 static_key; D_MAC80211("enter\n"); if (il3945_mod_params.sw_crypto) { D_MAC80211("leave - hwcrypto disabled\n"); return -EOPNOTSUPP; } /* * To support IBSS RSN, don't program group keys in IBSS, the * hardware will then not attempt to decrypt the frames. */ if (vif->type == NL80211_IFTYPE_ADHOC && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { D_MAC80211("leave - IBSS RSN\n"); return -EOPNOTSUPP; } static_key = !il_is_associated(il); if (!static_key) { sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) { D_MAC80211("leave - station not found\n"); return -EINVAL; } } mutex_lock(&il->mutex); il_scan_cancel_timeout(il, 100); switch (cmd) { case SET_KEY: if (static_key) ret = il3945_set_static_key(il, key); else ret = il3945_set_dynamic_key(il, key, sta_id); D_MAC80211("enable hwcrypto key\n"); break; case DISABLE_KEY: if (static_key) ret = il3945_remove_static_key(il); else ret = il3945_clear_sta_key_info(il, sta_id); D_MAC80211("disable hwcrypto key\n"); break; default: ret = -EINVAL; } D_MAC80211("leave ret %d\n", ret); mutex_unlock(&il->mutex); return ret; } static int il3945_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct il_priv *il = hw->priv; struct il3945_sta_priv *sta_priv = (void *)sta->drv_priv; int ret; bool is_ap = vif->type == NL80211_IFTYPE_STATION; u8 sta_id; mutex_lock(&il->mutex); D_INFO("station %pM\n", sta->addr); sta_priv->common.sta_id = IL_INVALID_STATION; ret = il_add_station_common(il, sta->addr, is_ap, sta, &sta_id); if (ret) { IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret); /* Should we return success if return code is EEXIST ? */ mutex_unlock(&il->mutex); return ret; } sta_priv->common.sta_id = sta_id; /* Initialize rate scaling */ D_INFO("Initializing rate scaling for station %pM\n", sta->addr); il3945_rs_rate_init(il, sta, sta_id); mutex_unlock(&il->mutex); return 0; } static void il3945_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct il_priv *il = hw->priv; __le32 filter_or = 0, filter_nand = 0; #define CHK(test, flag) do { \ if (*total_flags & (test)) \ filter_or |= (flag); \ else \ filter_nand |= (flag); \ } while (0) D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags, *total_flags); CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK); CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); #undef CHK mutex_lock(&il->mutex); il->staging.filter_flags &= ~filter_nand; il->staging.filter_flags |= filter_or; /* * Not committing directly because hardware can perform a scan, * but even if hw is ready, committing here breaks for some reason, * we'll eventually commit the filter flags change anyway. */ mutex_unlock(&il->mutex); /* * Receiving all multicast frames is always enabled by the * default flags setup in il_connection_init_rx_config() * since we currently do not support programming multicast * filters into the device. */ *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; } /***************************************************************************** * * sysfs attributes * *****************************************************************************/ #ifdef CONFIG_IWLEGACY_DEBUG /* * The following adds a new attribute to the sysfs representation * of this device driver (i.e. a new file in /sys/bus/pci/drivers/iwl/) * used for controlling the debug level. * * See the level definitions in iwl for details. * * The debug_level being managed using sysfs below is a per device debug * level that is used instead of the global debug level if it (the per * device debug level) is set. */ static ssize_t il3945_show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); return sprintf(buf, "0x%08X\n", il_get_debug_level(il)); } static ssize_t il3945_store_debug_level(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); unsigned long val; int ret; ret = strict_strtoul(buf, 0, &val); if (ret) IL_INFO("%s is not in hex or decimal form.\n", buf); else il->debug_level = val; return strnlen(buf, count); } static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il3945_show_debug_level, il3945_store_debug_level); #endif /* CONFIG_IWLEGACY_DEBUG */ static ssize_t il3945_show_temperature(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); if (!il_is_alive(il)) return -EAGAIN; return sprintf(buf, "%d\n", il3945_hw_get_temperature(il)); } static DEVICE_ATTR(temperature, S_IRUGO, il3945_show_temperature, NULL); static ssize_t il3945_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); return sprintf(buf, "%d\n", il->tx_power_user_lmt); } static ssize_t il3945_store_tx_power(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); char *p = (char *)buf; u32 val; val = simple_strtoul(p, &p, 10); if (p == buf) IL_INFO(": %s is not in decimal form.\n", buf); else il3945_hw_reg_set_txpower(il, val); return count; } static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il3945_show_tx_power, il3945_store_tx_power); static ssize_t il3945_show_flags(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); return sprintf(buf, "0x%04X\n", il->active.flags); } static ssize_t il3945_store_flags(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); u32 flags = simple_strtoul(buf, NULL, 0); mutex_lock(&il->mutex); if (le32_to_cpu(il->staging.flags) != flags) { /* Cancel any currently running scans... */ if (il_scan_cancel_timeout(il, 100)) IL_WARN("Could not cancel scan.\n"); else { D_INFO("Committing rxon.flags = 0x%04X\n", flags); il->staging.flags = cpu_to_le32(flags); il3945_commit_rxon(il); } } mutex_unlock(&il->mutex); return count; } static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, il3945_show_flags, il3945_store_flags); static ssize_t il3945_show_filter_flags(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); return sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.filter_flags)); } static ssize_t il3945_store_filter_flags(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); u32 filter_flags = simple_strtoul(buf, NULL, 0); mutex_lock(&il->mutex); if (le32_to_cpu(il->staging.filter_flags) != filter_flags) { /* Cancel any currently running scans... */ if (il_scan_cancel_timeout(il, 100)) IL_WARN("Could not cancel scan.\n"); else { D_INFO("Committing rxon.filter_flags = " "0x%04X\n", filter_flags); il->staging.filter_flags = cpu_to_le32(filter_flags); il3945_commit_rxon(il); } } mutex_unlock(&il->mutex); return count; } static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, il3945_show_filter_flags, il3945_store_filter_flags); static ssize_t il3945_show_measurement(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); struct il_spectrum_notification measure_report; u32 size = sizeof(measure_report), len = 0, ofs = 0; u8 *data = (u8 *) &measure_report; unsigned long flags; spin_lock_irqsave(&il->lock, flags); if (!(il->measurement_status & MEASUREMENT_READY)) { spin_unlock_irqrestore(&il->lock, flags); return 0; } memcpy(&measure_report, &il->measure_report, size); il->measurement_status = 0; spin_unlock_irqrestore(&il->lock, flags); while (size && PAGE_SIZE - len) { hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len, PAGE_SIZE - len, 1); len = strlen(buf); if (PAGE_SIZE - len) buf[len++] = '\n'; ofs += 16; size -= min(size, 16U); } return len; } static ssize_t il3945_store_measurement(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); struct ieee80211_measurement_params params = { .channel = le16_to_cpu(il->active.channel), .start_time = cpu_to_le64(il->_3945.last_tsf), .duration = cpu_to_le16(1), }; u8 type = IL_MEASURE_BASIC; u8 buffer[32]; u8 channel; if (count) { char *p = buffer; strncpy(buffer, buf, min(sizeof(buffer), count)); channel = simple_strtoul(p, NULL, 0); if (channel) params.channel = channel; p = buffer; while (*p && *p != ' ') p++; if (*p) type = simple_strtoul(p + 1, NULL, 0); } D_INFO("Invoking measurement of type %d on " "channel %d (for '%s')\n", type, params.channel, buf); il3945_get_measurement(il, ¶ms, type); return count; } static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, il3945_show_measurement, il3945_store_measurement); static ssize_t il3945_store_retry_rate(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); il->retry_rate = simple_strtoul(buf, NULL, 0); if (il->retry_rate <= 0) il->retry_rate = 1; return count; } static ssize_t il3945_show_retry_rate(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); return sprintf(buf, "%d", il->retry_rate); } static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, il3945_show_retry_rate, il3945_store_retry_rate); static ssize_t il3945_show_channels(struct device *d, struct device_attribute *attr, char *buf) { /* all this shit doesn't belong into sysfs anyway */ return 0; } static DEVICE_ATTR(channels, S_IRUSR, il3945_show_channels, NULL); static ssize_t il3945_show_antenna(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); if (!il_is_alive(il)) return -EAGAIN; return sprintf(buf, "%d\n", il3945_mod_params.antenna); } static ssize_t il3945_store_antenna(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il __maybe_unused = dev_get_drvdata(d); int ant; if (count == 0) return 0; if (sscanf(buf, "%1i", &ant) != 1) { D_INFO("not in hex or decimal form.\n"); return count; } if (ant >= 0 && ant <= 2) { D_INFO("Setting antenna select to %d.\n", ant); il3945_mod_params.antenna = (enum il3945_antenna)ant; } else D_INFO("Bad antenna select value %d.\n", ant); return count; } static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, il3945_show_antenna, il3945_store_antenna); static ssize_t il3945_show_status(struct device *d, struct device_attribute *attr, char *buf) { struct il_priv *il = dev_get_drvdata(d); if (!il_is_alive(il)) return -EAGAIN; return sprintf(buf, "0x%08x\n", (int)il->status); } static DEVICE_ATTR(status, S_IRUGO, il3945_show_status, NULL); static ssize_t il3945_dump_error_log(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { struct il_priv *il = dev_get_drvdata(d); char *p = (char *)buf; if (p[0] == '1') il3945_dump_nic_error_log(il); return strnlen(buf, count); } static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, il3945_dump_error_log); /***************************************************************************** * * driver setup and tear down * *****************************************************************************/ static void il3945_setup_deferred_work(struct il_priv *il) { il->workqueue = create_singlethread_workqueue(DRV_NAME); init_waitqueue_head(&il->wait_command_queue); INIT_WORK(&il->restart, il3945_bg_restart); INIT_WORK(&il->rx_replenish, il3945_bg_rx_replenish); INIT_DELAYED_WORK(&il->init_alive_start, il3945_bg_init_alive_start); INIT_DELAYED_WORK(&il->alive_start, il3945_bg_alive_start); INIT_DELAYED_WORK(&il->_3945.rfkill_poll, il3945_rfkill_poll); il_setup_scan_deferred_work(il); il3945_hw_setup_deferred_work(il); init_timer(&il->watchdog); il->watchdog.data = (unsigned long)il; il->watchdog.function = il_bg_watchdog; tasklet_init(&il->irq_tasklet, (void (*)(unsigned long))il3945_irq_tasklet, (unsigned long)il); } static void il3945_cancel_deferred_work(struct il_priv *il) { il3945_hw_cancel_deferred_work(il); cancel_delayed_work_sync(&il->init_alive_start); cancel_delayed_work(&il->alive_start); il_cancel_scan_deferred_work(il); } static struct attribute *il3945_sysfs_entries[] = { &dev_attr_antenna.attr, &dev_attr_channels.attr, &dev_attr_dump_errors.attr, &dev_attr_flags.attr, &dev_attr_filter_flags.attr, &dev_attr_measurement.attr, &dev_attr_retry_rate.attr, &dev_attr_status.attr, &dev_attr_temperature.attr, &dev_attr_tx_power.attr, #ifdef CONFIG_IWLEGACY_DEBUG &dev_attr_debug_level.attr, #endif NULL }; static struct attribute_group il3945_attribute_group = { .name = NULL, /* put in device directory */ .attrs = il3945_sysfs_entries, }; struct ieee80211_ops il3945_mac_ops = { .tx = il3945_mac_tx, .start = il3945_mac_start, .stop = il3945_mac_stop, .add_interface = il_mac_add_interface, .remove_interface = il_mac_remove_interface, .change_interface = il_mac_change_interface, .config = il_mac_config, .configure_filter = il3945_configure_filter, .set_key = il3945_mac_set_key, .conf_tx = il_mac_conf_tx, .reset_tsf = il_mac_reset_tsf, .bss_info_changed = il_mac_bss_info_changed, .hw_scan = il_mac_hw_scan, .sta_add = il3945_mac_sta_add, .sta_remove = il_mac_sta_remove, .tx_last_beacon = il_mac_tx_last_beacon, }; static int il3945_init_drv(struct il_priv *il) { int ret; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; il->retry_rate = 1; il->beacon_skb = NULL; spin_lock_init(&il->sta_lock); spin_lock_init(&il->hcmd_lock); INIT_LIST_HEAD(&il->free_frames); mutex_init(&il->mutex); il->ieee_channels = NULL; il->ieee_rates = NULL; il->band = IEEE80211_BAND_2GHZ; il->iw_mode = NL80211_IFTYPE_STATION; il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; /* initialize force reset */ il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD; if (eeprom->version < EEPROM_3945_EEPROM_VERSION) { IL_WARN("Unsupported EEPROM version: 0x%04X\n", eeprom->version); ret = -EINVAL; goto err; } ret = il_init_channel_map(il); if (ret) { IL_ERR("initializing regulatory failed: %d\n", ret); goto err; } /* Set up txpower settings in driver for all channels */ if (il3945_txpower_set_from_eeprom(il)) { ret = -EIO; goto err_free_channel_map; } ret = il_init_geos(il); if (ret) { IL_ERR("initializing geos failed: %d\n", ret); goto err_free_channel_map; } il3945_init_hw_rates(il, il->ieee_rates); return 0; err_free_channel_map: il_free_channel_map(il); err: return ret; } #define IL3945_MAX_PROBE_REQUEST 200 static int il3945_setup_mac(struct il_priv *il) { int ret; struct ieee80211_hw *hw = il->hw; hw->rate_control_algorithm = "iwl-3945-rs"; hw->sta_data_size = sizeof(struct il3945_sta_priv); hw->vif_data_size = sizeof(struct il_vif_priv); /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SPECTRUM_MGMT; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | WIPHY_FLAG_IBSS_RSN; hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945; /* we create the 802.11 header and a zero-length SSID element */ hw->wiphy->max_scan_ie_len = IL3945_MAX_PROBE_REQUEST - 24 - 2; /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; if (il->bands[IEEE80211_BAND_2GHZ].n_channels) il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &il->bands[IEEE80211_BAND_2GHZ]; if (il->bands[IEEE80211_BAND_5GHZ].n_channels) il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &il->bands[IEEE80211_BAND_5GHZ]; il_leds_init(il); ret = ieee80211_register_hw(il->hw); if (ret) { IL_ERR("Failed to register hw (error %d)\n", ret); return ret; } il->mac80211_registered = 1; return 0; } static int il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err = 0; struct il_priv *il; struct ieee80211_hw *hw; struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data); struct il3945_eeprom *eeprom; unsigned long flags; /*********************** * 1. Allocating HW data * ********************/ hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il3945_mac_ops); if (!hw) { err = -ENOMEM; goto out; } il = hw->priv; il->hw = hw; SET_IEEE80211_DEV(hw, &pdev->dev); il->cmd_queue = IL39_CMD_QUEUE_NUM; /* * Disabling hardware scan means that mac80211 will perform scans * "the hard way", rather than using device's scan. */ if (il3945_mod_params.disable_hw_scan) { D_INFO("Disabling hw_scan\n"); il3945_mac_ops.hw_scan = NULL; } D_INFO("*** LOAD DRIVER ***\n"); il->cfg = cfg; il->ops = &il3945_ops; #ifdef CONFIG_IWLEGACY_DEBUGFS il->debugfs_ops = &il3945_debugfs_ops; #endif il->pci_dev = pdev; il->inta_mask = CSR_INI_SET_MASK; /*************************** * 2. Initializing PCI bus * *************************/ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); if (pci_enable_device(pdev)) { err = -ENODEV; goto out_ieee80211_free_hw; } pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (!err) err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { IL_WARN("No suitable DMA available.\n"); goto out_pci_disable_device; } pci_set_drvdata(pdev, il); err = pci_request_regions(pdev, DRV_NAME); if (err) goto out_pci_disable_device; /*********************** * 3. Read REV Register * ********************/ il->hw_base = pci_ioremap_bar(pdev, 0); if (!il->hw_base) { err = -ENODEV; goto out_pci_release_regions; } D_INFO("pci_resource_len = 0x%08llx\n", (unsigned long long)pci_resource_len(pdev, 0)); D_INFO("pci_resource_base = %p\n", il->hw_base); /* We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, 0x41, 0x00); /* these spin locks will be used in apm_init and EEPROM access * we should init now */ spin_lock_init(&il->reg_lock); spin_lock_init(&il->lock); /* * stop and reset the on-board processor just in case it is in a * strange state ... like being left stranded by a primary kernel * and this is now the kdump kernel trying to start up */ _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); /*********************** * 4. Read EEPROM * ********************/ /* Read the EEPROM */ err = il_eeprom_init(il); if (err) { IL_ERR("Unable to init EEPROM\n"); goto out_iounmap; } /* MAC Address location in EEPROM same for 3945/4965 */ eeprom = (struct il3945_eeprom *)il->eeprom; D_INFO("MAC address: %pM\n", eeprom->mac_address); SET_IEEE80211_PERM_ADDR(il->hw, eeprom->mac_address); /*********************** * 5. Setup HW Constants * ********************/ /* Device-specific setup */ if (il3945_hw_set_hw_params(il)) { IL_ERR("failed to set hw settings\n"); goto out_eeprom_free; } /*********************** * 6. Setup il * ********************/ err = il3945_init_drv(il); if (err) { IL_ERR("initializing driver failed\n"); goto out_unset_hw_params; } IL_INFO("Detected Intel Wireless WiFi Link %s\n", il->cfg->name); /*********************** * 7. Setup Services * ********************/ spin_lock_irqsave(&il->lock, flags); il_disable_interrupts(il); spin_unlock_irqrestore(&il->lock, flags); pci_enable_msi(il->pci_dev); err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il); if (err) { IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq); goto out_disable_msi; } err = sysfs_create_group(&pdev->dev.kobj, &il3945_attribute_group); if (err) { IL_ERR("failed to create sysfs device attributes\n"); goto out_release_irq; } il_set_rxon_channel(il, &il->bands[IEEE80211_BAND_2GHZ].channels[5]); il3945_setup_deferred_work(il); il3945_setup_handlers(il); il_power_initialize(il); /********************************* * 8. Setup and Register mac80211 * *******************************/ il_enable_interrupts(il); err = il3945_setup_mac(il); if (err) goto out_remove_sysfs; err = il_dbgfs_register(il, DRV_NAME); if (err) IL_ERR("failed to create debugfs files. Ignoring error: %d\n", err); /* Start monitoring the killswitch */ queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, 2 * HZ); return 0; out_remove_sysfs: destroy_workqueue(il->workqueue); il->workqueue = NULL; sysfs_remove_group(&pdev->dev.kobj, &il3945_attribute_group); out_release_irq: free_irq(il->pci_dev->irq, il); out_disable_msi: pci_disable_msi(il->pci_dev); il_free_geos(il); il_free_channel_map(il); out_unset_hw_params: il3945_unset_hw_params(il); out_eeprom_free: il_eeprom_free(il); out_iounmap: iounmap(il->hw_base); out_pci_release_regions: pci_release_regions(pdev); out_pci_disable_device: pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(il->hw); out: return err; } static void __devexit il3945_pci_remove(struct pci_dev *pdev) { struct il_priv *il = pci_get_drvdata(pdev); unsigned long flags; if (!il) return; D_INFO("*** UNLOAD DRIVER ***\n"); il_dbgfs_unregister(il); set_bit(S_EXIT_PENDING, &il->status); il_leds_exit(il); if (il->mac80211_registered) { ieee80211_unregister_hw(il->hw); il->mac80211_registered = 0; } else { il3945_down(il); } /* * Make sure device is reset to low power before unloading driver. * This may be redundant with il_down(), but there are paths to * run il_down() without calling apm_ops.stop(), and there are * paths to avoid running il_down() at all before leaving driver. * This (inexpensive) call *makes sure* device is reset. */ il_apm_stop(il); /* make sure we flush any pending irq or * tasklet for the driver */ spin_lock_irqsave(&il->lock, flags); il_disable_interrupts(il); spin_unlock_irqrestore(&il->lock, flags); il3945_synchronize_irq(il); sysfs_remove_group(&pdev->dev.kobj, &il3945_attribute_group); cancel_delayed_work_sync(&il->_3945.rfkill_poll); il3945_dealloc_ucode_pci(il); if (il->rxq.bd) il3945_rx_queue_free(il, &il->rxq); il3945_hw_txq_ctx_free(il); il3945_unset_hw_params(il); /*netif_stop_queue(dev); */ flush_workqueue(il->workqueue); /* ieee80211_unregister_hw calls il3945_mac_stop, which flushes * il->workqueue... so we can't take down the workqueue * until now... */ destroy_workqueue(il->workqueue); il->workqueue = NULL; free_irq(pdev->irq, il); pci_disable_msi(pdev); iounmap(il->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); il_free_channel_map(il); il_free_geos(il); kfree(il->scan_cmd); if (il->beacon_skb) dev_kfree_skb(il->beacon_skb); ieee80211_free_hw(il->hw); } /***************************************************************************** * * driver and module entry point * *****************************************************************************/ static struct pci_driver il3945_driver = { .name = DRV_NAME, .id_table = il3945_hw_card_ids, .probe = il3945_pci_probe, .remove = __devexit_p(il3945_pci_remove), #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = IL_LEGACY_PM_OPS, #elif defined(CONFIG_PM) .suspend = il_pci_suspend_compat, .resume = il_pci_resume_compat, #endif }; static int __init il3945_init(void) { int ret; pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); pr_info(DRV_COPYRIGHT "\n"); ret = il3945_rate_control_register(); if (ret) { pr_err("Unable to register rate control algorithm: %d\n", ret); return ret; } ret = pci_register_driver(&il3945_driver); if (ret) { pr_err("Unable to initialize PCI module\n"); goto error_register; } return ret; error_register: il3945_rate_control_unregister(); return ret; } static void __exit il3945_exit(void) { pci_unregister_driver(&il3945_driver); il3945_rate_control_unregister(); } MODULE_FIRMWARE(IL3945_MODULE_FIRMWARE(IL3945_UCODE_API_MAX)); module_param_named(antenna, il3945_mod_params.antenna, int, S_IRUGO); MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); module_param_named(swcrypto, il3945_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, "using software crypto (default 1 [software])"); module_param_named(disable_hw_scan, il3945_mod_params.disable_hw_scan, int, S_IRUGO); MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 1)"); #ifdef CONFIG_IWLEGACY_DEBUG module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(fw_restart, il3945_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); module_exit(il3945_exit); module_init(il3945_init); compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/common.c0000644000175000017500000043255312026211315023735 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout) { const int interval = 10; /* microseconds */ int t = 0; do { if ((_il_rd(il, addr) & mask) == (bits & mask)) return t; udelay(interval); t += interval; } while (t < timeout); return -ETIMEDOUT; } EXPORT_SYMBOL(_il_poll_bit); void il_set_bit(struct il_priv *p, u32 r, u32 m) { unsigned long reg_flags; spin_lock_irqsave(&p->reg_lock, reg_flags); _il_set_bit(p, r, m); spin_unlock_irqrestore(&p->reg_lock, reg_flags); } EXPORT_SYMBOL(il_set_bit); void il_clear_bit(struct il_priv *p, u32 r, u32 m) { unsigned long reg_flags; spin_lock_irqsave(&p->reg_lock, reg_flags); _il_clear_bit(p, r, m); spin_unlock_irqrestore(&p->reg_lock, reg_flags); } EXPORT_SYMBOL(il_clear_bit); bool _il_grab_nic_access(struct il_priv *il) { int ret; u32 val; /* this bit wakes up the NIC */ _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* * These bits say the device is running, and should keep running for * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), * but they do not indicate that embedded SRAM is restored yet; * 3945 and 4965 have volatile SRAM, and must save/restore contents * to/from host DRAM when sleeping/waking for power-saving. * Each direction takes approximately 1/4 millisecond; with this * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a * series of register accesses are expected (e.g. reading Event Log), * to keep device from sleeping. * * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that * SRAM is okay/restored. We don't check that here because this call * is just for hardware register access; but GP1 MAC_SLEEP check is a * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). * */ ret = _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); if (unlikely(ret < 0)) { val = _il_rd(il, CSR_GP_CNTRL); WARN_ONCE(1, "Timeout waiting for ucode processor access " "(CSR_GP_CNTRL 0x%08x)\n", val); _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); return false; } return true; } EXPORT_SYMBOL_GPL(_il_grab_nic_access); int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout) { const int interval = 10; /* microseconds */ int t = 0; do { if ((il_rd(il, addr) & mask) == mask) return t; udelay(interval); t += interval; } while (t < timeout); return -ETIMEDOUT; } EXPORT_SYMBOL(il_poll_bit); u32 il_rd_prph(struct il_priv *il, u32 reg) { unsigned long reg_flags; u32 val; spin_lock_irqsave(&il->reg_lock, reg_flags); _il_grab_nic_access(il); val = _il_rd_prph(il, reg); _il_release_nic_access(il); spin_unlock_irqrestore(&il->reg_lock, reg_flags); return val; } EXPORT_SYMBOL(il_rd_prph); void il_wr_prph(struct il_priv *il, u32 addr, u32 val) { unsigned long reg_flags; spin_lock_irqsave(&il->reg_lock, reg_flags); if (likely(_il_grab_nic_access(il))) { _il_wr_prph(il, addr, val); _il_release_nic_access(il); } spin_unlock_irqrestore(&il->reg_lock, reg_flags); } EXPORT_SYMBOL(il_wr_prph); u32 il_read_targ_mem(struct il_priv *il, u32 addr) { unsigned long reg_flags; u32 value; spin_lock_irqsave(&il->reg_lock, reg_flags); _il_grab_nic_access(il); _il_wr(il, HBUS_TARG_MEM_RADDR, addr); value = _il_rd(il, HBUS_TARG_MEM_RDAT); _il_release_nic_access(il); spin_unlock_irqrestore(&il->reg_lock, reg_flags); return value; } EXPORT_SYMBOL(il_read_targ_mem); void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val) { unsigned long reg_flags; spin_lock_irqsave(&il->reg_lock, reg_flags); if (likely(_il_grab_nic_access(il))) { _il_wr(il, HBUS_TARG_MEM_WADDR, addr); _il_wr(il, HBUS_TARG_MEM_WDAT, val); _il_release_nic_access(il); } spin_unlock_irqrestore(&il->reg_lock, reg_flags); } EXPORT_SYMBOL(il_write_targ_mem); const char * il_get_cmd_string(u8 cmd) { switch (cmd) { IL_CMD(N_ALIVE); IL_CMD(N_ERROR); IL_CMD(C_RXON); IL_CMD(C_RXON_ASSOC); IL_CMD(C_QOS_PARAM); IL_CMD(C_RXON_TIMING); IL_CMD(C_ADD_STA); IL_CMD(C_REM_STA); IL_CMD(C_WEPKEY); IL_CMD(N_3945_RX); IL_CMD(C_TX); IL_CMD(C_RATE_SCALE); IL_CMD(C_LEDS); IL_CMD(C_TX_LINK_QUALITY_CMD); IL_CMD(C_CHANNEL_SWITCH); IL_CMD(N_CHANNEL_SWITCH); IL_CMD(C_SPECTRUM_MEASUREMENT); IL_CMD(N_SPECTRUM_MEASUREMENT); IL_CMD(C_POWER_TBL); IL_CMD(N_PM_SLEEP); IL_CMD(N_PM_DEBUG_STATS); IL_CMD(C_SCAN); IL_CMD(C_SCAN_ABORT); IL_CMD(N_SCAN_START); IL_CMD(N_SCAN_RESULTS); IL_CMD(N_SCAN_COMPLETE); IL_CMD(N_BEACON); IL_CMD(C_TX_BEACON); IL_CMD(C_TX_PWR_TBL); IL_CMD(C_BT_CONFIG); IL_CMD(C_STATS); IL_CMD(N_STATS); IL_CMD(N_CARD_STATE); IL_CMD(N_MISSED_BEACONS); IL_CMD(C_CT_KILL_CONFIG); IL_CMD(C_SENSITIVITY); IL_CMD(C_PHY_CALIBRATION); IL_CMD(N_RX_PHY); IL_CMD(N_RX_MPDU); IL_CMD(N_RX); IL_CMD(N_COMPRESSED_BA); default: return "UNKNOWN"; } } EXPORT_SYMBOL(il_get_cmd_string); #define HOST_COMPLETE_TIMEOUT (HZ / 2) static void il_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd, struct il_rx_pkt *pkt) { if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { IL_ERR("Bad return from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); return; } #ifdef CONFIG_IWLEGACY_DEBUG switch (cmd->hdr.cmd) { case C_TX_LINK_QUALITY_CMD: case C_SENSITIVITY: D_HC_DUMP("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); break; default: D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); } #endif } static int il_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd) { int ret; BUG_ON(!(cmd->flags & CMD_ASYNC)); /* An asynchronous command can not expect an SKB to be set. */ BUG_ON(cmd->flags & CMD_WANT_SKB); /* Assign a generic callback if one is not provided */ if (!cmd->callback) cmd->callback = il_generic_cmd_callback; if (test_bit(S_EXIT_PENDING, &il->status)) return -EBUSY; ret = il_enqueue_hcmd(il, cmd); if (ret < 0) { IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", il_get_cmd_string(cmd->id), ret); return ret; } return 0; } int il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd) { int cmd_idx; int ret; lockdep_assert_held(&il->mutex); BUG_ON(cmd->flags & CMD_ASYNC); /* A synchronous command can not have a callback set. */ BUG_ON(cmd->callback); D_INFO("Attempting to send sync command %s\n", il_get_cmd_string(cmd->id)); set_bit(S_HCMD_ACTIVE, &il->status); D_INFO("Setting HCMD_ACTIVE for command %s\n", il_get_cmd_string(cmd->id)); cmd_idx = il_enqueue_hcmd(il, cmd); if (cmd_idx < 0) { ret = cmd_idx; IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", il_get_cmd_string(cmd->id), ret); goto out; } ret = wait_event_timeout(il->wait_command_queue, !test_bit(S_HCMD_ACTIVE, &il->status), HOST_COMPLETE_TIMEOUT); if (!ret) { if (test_bit(S_HCMD_ACTIVE, &il->status)) { IL_ERR("Error sending %s: time out after %dms.\n", il_get_cmd_string(cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); clear_bit(S_HCMD_ACTIVE, &il->status); D_INFO("Clearing HCMD_ACTIVE for command %s\n", il_get_cmd_string(cmd->id)); ret = -ETIMEDOUT; goto cancel; } } if (test_bit(S_RFKILL, &il->status)) { IL_ERR("Command %s aborted: RF KILL Switch\n", il_get_cmd_string(cmd->id)); ret = -ECANCELED; goto fail; } if (test_bit(S_FW_ERROR, &il->status)) { IL_ERR("Command %s failed: FW Error\n", il_get_cmd_string(cmd->id)); ret = -EIO; goto fail; } if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { IL_ERR("Error: Response NULL in '%s'\n", il_get_cmd_string(cmd->id)); ret = -EIO; goto cancel; } ret = 0; goto out; cancel: if (cmd->flags & CMD_WANT_SKB) { /* * Cancel the CMD_WANT_SKB flag for the cmd in the * TX cmd queue. Otherwise in case the cmd comes * in later, it will possibly set an invalid * address (cmd->meta.source). */ il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; } fail: if (cmd->reply_page) { il_free_pages(il, cmd->reply_page); cmd->reply_page = 0; } out: return ret; } EXPORT_SYMBOL(il_send_cmd_sync); int il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd) { if (cmd->flags & CMD_ASYNC) return il_send_cmd_async(il, cmd); return il_send_cmd_sync(il, cmd); } EXPORT_SYMBOL(il_send_cmd); int il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data) { struct il_host_cmd cmd = { .id = id, .len = len, .data = data, }; return il_send_cmd_sync(il, &cmd); } EXPORT_SYMBOL(il_send_cmd_pdu); int il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, struct il_rx_pkt *pkt)) { struct il_host_cmd cmd = { .id = id, .len = len, .data = data, }; cmd.flags |= CMD_ASYNC; cmd.callback = callback; return il_send_cmd_async(il, &cmd); } EXPORT_SYMBOL(il_send_cmd_pdu_async); /* default: IL_LED_BLINK(0) using blinking idx table */ static int led_mode; module_param(led_mode, int, S_IRUGO); MODULE_PARM_DESC(led_mode, "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking"); /* Throughput OFF time(ms) ON time (ms) * >300 25 25 * >200 to 300 40 40 * >100 to 200 55 55 * >70 to 100 65 65 * >50 to 70 75 75 * >20 to 50 85 85 * >10 to 20 95 95 * >5 to 10 110 110 * >1 to 5 130 130 * >0 to 1 167 167 * <=0 SOLID ON */ static const struct ieee80211_tpt_blink il_blink[] = { {.throughput = 0, .blink_time = 334}, {.throughput = 1 * 1024 - 1, .blink_time = 260}, {.throughput = 5 * 1024 - 1, .blink_time = 220}, {.throughput = 10 * 1024 - 1, .blink_time = 190}, {.throughput = 20 * 1024 - 1, .blink_time = 170}, {.throughput = 50 * 1024 - 1, .blink_time = 150}, {.throughput = 70 * 1024 - 1, .blink_time = 130}, {.throughput = 100 * 1024 - 1, .blink_time = 110}, {.throughput = 200 * 1024 - 1, .blink_time = 80}, {.throughput = 300 * 1024 - 1, .blink_time = 50}, }; /* * Adjust led blink rate to compensate on a MAC Clock difference on every HW * Led blink rate analysis showed an average deviation of 0% on 3945, * 5% on 4965 HW. * Need to compensate on the led on/off time per HW according to the deviation * to achieve the desired led frequency * The calculation is: (100-averageDeviation)/100 * blinkTime * For code efficiency the calculation will be: * compensation = (100 - averageDeviation) * 64 / 100 * NewBlinkTime = (compensation * BlinkTime) / 64 */ static inline u8 il_blink_compensation(struct il_priv *il, u8 time, u16 compensation) { if (!compensation) { IL_ERR("undefined blink compensation: " "use pre-defined blinking time\n"); return time; } return (u8) ((time * compensation) >> 6); } /* Set led pattern command */ static int il_led_cmd(struct il_priv *il, unsigned long on, unsigned long off) { struct il_led_cmd led_cmd = { .id = IL_LED_LINK, .interval = IL_DEF_LED_INTRVL }; int ret; if (!test_bit(S_READY, &il->status)) return -EBUSY; if (il->blink_on == on && il->blink_off == off) return 0; if (off == 0) { /* led is SOLID_ON */ on = IL_LED_SOLID; } D_LED("Led blink time compensation=%u\n", il->cfg->led_compensation); led_cmd.on = il_blink_compensation(il, on, il->cfg->led_compensation); led_cmd.off = il_blink_compensation(il, off, il->cfg->led_compensation); ret = il->ops->send_led_cmd(il, &led_cmd); if (!ret) { il->blink_on = on; il->blink_off = off; } return ret; } static void il_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct il_priv *il = container_of(led_cdev, struct il_priv, led); unsigned long on = 0; if (brightness > 0) on = IL_LED_SOLID; il_led_cmd(il, on, 0); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) static int il_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct il_priv *il = container_of(led_cdev, struct il_priv, led); return il_led_cmd(il, *delay_on, *delay_off); } #endif void il_leds_init(struct il_priv *il) { int mode = led_mode; int ret; if (mode == IL_LED_DEFAULT) mode = il->cfg->led_mode; il->led.name = kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy)); il->led.brightness_set = il_led_brightness_set; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) il->led.blink_set = il_led_blink_set; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) il->led.max_brightness = 1; #endif switch (mode) { case IL_LED_DEFAULT: WARN_ON(1); break; case IL_LED_BLINK: il->led.default_trigger = ieee80211_create_tpt_led_trigger(il->hw, IEEE80211_TPT_LEDTRIG_FL_CONNECTED, il_blink, ARRAY_SIZE(il_blink)); break; case IL_LED_RF_STATE: il->led.default_trigger = ieee80211_get_radio_led_name(il->hw); break; } ret = led_classdev_register(&il->pci_dev->dev, &il->led); if (ret) { kfree(il->led.name); return; } il->led_registered = true; } EXPORT_SYMBOL(il_leds_init); void il_leds_exit(struct il_priv *il) { if (!il->led_registered) return; led_classdev_unregister(&il->led); kfree(il->led.name); } EXPORT_SYMBOL(il_leds_exit); /************************** EEPROM BANDS **************************** * * The il_eeprom_band definitions below provide the mapping from the * EEPROM contents to the specific channel number supported for each * band. * * For example, il_priv->eeprom.band_3_channels[4] from the band_3 * definition below maps to physical channel 42 in the 5.2GHz spectrum. * The specific geography and calibration information for that channel * is contained in the eeprom map itself. * * During init, we copy the eeprom information and channel map * information into il->channel_info_24/52 and il->channel_map_24/52 * * channel_map_24/52 provides the idx in the channel_info array for a * given channel. We have to have two separate maps as there is channel * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and * band_2 * * A value of 0xff stored in the channel_map indicates that the channel * is not supported by the hardware at all. * * A value of 0xfe in the channel_map indicates that the channel is not * valid for Tx with the current hardware. This means that * while the system can tune and receive on a given channel, it may not * be able to associate or transmit any frames on that * channel. There is no corresponding channel information for that * entry. * *********************************************************************/ /* 2.4 GHz */ const u8 il_eeprom_band_1[14] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; /* 5.2 GHz bands */ static const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */ 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 }; static const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */ 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 }; static const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 }; static const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */ 145, 149, 153, 157, 161, 165 }; static const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */ 1, 2, 3, 4, 5, 6, 7 }; static const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */ 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 }; /****************************************************************************** * * EEPROM related functions * ******************************************************************************/ static int il_eeprom_verify_signature(struct il_priv *il) { u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; int ret = 0; D_EEPROM("EEPROM signature=0x%08x\n", gp); switch (gp) { case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: break; default: IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp); ret = -ENOENT; break; } return ret; } const u8 * il_eeprom_query_addr(const struct il_priv *il, size_t offset) { BUG_ON(offset >= il->cfg->eeprom_size); return &il->eeprom[offset]; } EXPORT_SYMBOL(il_eeprom_query_addr); u16 il_eeprom_query16(const struct il_priv *il, size_t offset) { if (!il->eeprom) return 0; return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8); } EXPORT_SYMBOL(il_eeprom_query16); /** * il_eeprom_init - read EEPROM contents * * Load the EEPROM contents from adapter into il->eeprom * * NOTE: This routine uses the non-debug IO access functions. */ int il_eeprom_init(struct il_priv *il) { __le16 *e; u32 gp = _il_rd(il, CSR_EEPROM_GP); int sz; int ret; u16 addr; /* allocate eeprom */ sz = il->cfg->eeprom_size; D_EEPROM("NVM size = %d\n", sz); il->eeprom = kzalloc(sz, GFP_KERNEL); if (!il->eeprom) { ret = -ENOMEM; goto alloc_err; } e = (__le16 *) il->eeprom; il->ops->apm_init(il); ret = il_eeprom_verify_signature(il); if (ret < 0) { IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); ret = -ENOENT; goto err; } /* Make sure driver (instead of uCode) is allowed to read EEPROM */ ret = il->ops->eeprom_acquire_semaphore(il); if (ret < 0) { IL_ERR("Failed to acquire EEPROM semaphore.\n"); ret = -ENOENT; goto err; } /* eeprom is an array of 16bit values */ for (addr = 0; addr < sz; addr += sizeof(u16)) { u32 r; _il_wr(il, CSR_EEPROM_REG, CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); ret = _il_poll_bit(il, CSR_EEPROM_REG, CSR_EEPROM_REG_READ_VALID_MSK, CSR_EEPROM_REG_READ_VALID_MSK, IL_EEPROM_ACCESS_TIMEOUT); if (ret < 0) { IL_ERR("Time out reading EEPROM[%d]\n", addr); goto done; } r = _il_rd(il, CSR_EEPROM_REG); e[addr / 2] = cpu_to_le16(r >> 16); } D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM", il_eeprom_query16(il, EEPROM_VERSION)); ret = 0; done: il->ops->eeprom_release_semaphore(il); err: if (ret) il_eeprom_free(il); /* Reset chip to save power until we load uCode during "up". */ il_apm_stop(il); alloc_err: return ret; } EXPORT_SYMBOL(il_eeprom_init); void il_eeprom_free(struct il_priv *il) { kfree(il->eeprom); il->eeprom = NULL; } EXPORT_SYMBOL(il_eeprom_free); static void il_init_band_reference(const struct il_priv *il, int eep_band, int *eeprom_ch_count, const struct il_eeprom_channel **eeprom_ch_info, const u8 **eeprom_ch_idx) { u32 offset = il->cfg->regulatory_bands[eep_band - 1]; switch (eep_band) { case 1: /* 2.4GHz band */ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1); *eeprom_ch_info = (struct il_eeprom_channel *)il_eeprom_query_addr(il, offset); *eeprom_ch_idx = il_eeprom_band_1; break; case 2: /* 4.9GHz band */ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2); *eeprom_ch_info = (struct il_eeprom_channel *)il_eeprom_query_addr(il, offset); *eeprom_ch_idx = il_eeprom_band_2; break; case 3: /* 5.2GHz band */ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3); *eeprom_ch_info = (struct il_eeprom_channel *)il_eeprom_query_addr(il, offset); *eeprom_ch_idx = il_eeprom_band_3; break; case 4: /* 5.5GHz band */ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4); *eeprom_ch_info = (struct il_eeprom_channel *)il_eeprom_query_addr(il, offset); *eeprom_ch_idx = il_eeprom_band_4; break; case 5: /* 5.7GHz band */ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5); *eeprom_ch_info = (struct il_eeprom_channel *)il_eeprom_query_addr(il, offset); *eeprom_ch_idx = il_eeprom_band_5; break; case 6: /* 2.4GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6); *eeprom_ch_info = (struct il_eeprom_channel *)il_eeprom_query_addr(il, offset); *eeprom_ch_idx = il_eeprom_band_6; break; case 7: /* 5 GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7); *eeprom_ch_info = (struct il_eeprom_channel *)il_eeprom_query_addr(il, offset); *eeprom_ch_idx = il_eeprom_band_7; break; default: BUG(); } } #define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ ? # x " " : "") /** * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il. * * Does not set up a command, or touch hardware. */ static int il_mod_ht40_chan_info(struct il_priv *il, enum ieee80211_band band, u16 channel, const struct il_eeprom_channel *eeprom_ch, u8 clear_ht40_extension_channel) { struct il_channel_info *ch_info; ch_info = (struct il_channel_info *)il_get_channel_info(il, band, channel); if (!il_is_channel_valid(ch_info)) return -1; D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" " Ad-Hoc %ssupported\n", ch_info->channel, il_is_channel_a_band(ch_info) ? "5.2" : "2.4", CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE), CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE), CHECK_AND_PRINT(DFS), eeprom_ch->flags, eeprom_ch->max_power_avg, ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); ch_info->ht40_eeprom = *eeprom_ch; ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; ch_info->ht40_flags = eeprom_ch->flags; if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel; return 0; } #define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ ? # x " " : "") /** * il_init_channel_map - Set up driver's info for all possible channels */ int il_init_channel_map(struct il_priv *il) { int eeprom_ch_count = 0; const u8 *eeprom_ch_idx = NULL; const struct il_eeprom_channel *eeprom_ch_info = NULL; int band, ch; struct il_channel_info *ch_info; if (il->channel_count) { D_EEPROM("Channel map already initialized.\n"); return 0; } D_EEPROM("Initializing regulatory info from EEPROM\n"); il->channel_count = ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) + ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) + ARRAY_SIZE(il_eeprom_band_5); D_EEPROM("Parsing data for %d channels.\n", il->channel_count); il->channel_info = kzalloc(sizeof(struct il_channel_info) * il->channel_count, GFP_KERNEL); if (!il->channel_info) { IL_ERR("Could not allocate channel_info\n"); il->channel_count = 0; return -ENOMEM; } ch_info = il->channel_info; /* Loop through the 5 EEPROM bands adding them in order to the * channel map we maintain (that contains additional information than * what just in the EEPROM) */ for (band = 1; band <= 5; band++) { il_init_band_reference(il, band, &eeprom_ch_count, &eeprom_ch_info, &eeprom_ch_idx); /* Loop through each band adding each of the channels */ for (ch = 0; ch < eeprom_ch_count; ch++) { ch_info->channel = eeprom_ch_idx[ch]; ch_info->band = (band == 1) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; /* permanently store EEPROM's channel regulatory flags * and max power in channel info database. */ ch_info->eeprom = eeprom_ch_info[ch]; /* Copy the run-time flags so they are there even on * invalid channels */ ch_info->flags = eeprom_ch_info[ch].flags; /* First write that ht40 is not enabled, and then enable * one by one */ ch_info->ht40_extension_channel = IEEE80211_CHAN_NO_HT40; if (!(il_is_channel_valid(ch_info))) { D_EEPROM("Ch. %d Flags %x [%sGHz] - " "No traffic\n", ch_info->channel, ch_info->flags, il_is_channel_a_band(ch_info) ? "5.2" : "2.4"); ch_info++; continue; } /* Initialize regulatory-based run-time data */ ch_info->max_power_avg = ch_info->curr_txpow = eeprom_ch_info[ch].max_power_avg; ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; ch_info->min_power = 0; D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):" " Ad-Hoc %ssupported\n", ch_info->channel, il_is_channel_a_band(ch_info) ? "5.2" : "2.4", CHECK_AND_PRINT_I(VALID), CHECK_AND_PRINT_I(IBSS), CHECK_AND_PRINT_I(ACTIVE), CHECK_AND_PRINT_I(RADAR), CHECK_AND_PRINT_I(WIDE), CHECK_AND_PRINT_I(DFS), eeprom_ch_info[ch].flags, eeprom_ch_info[ch].max_power_avg, ((eeprom_ch_info[ch]. flags & EEPROM_CHANNEL_IBSS) && !(eeprom_ch_info[ch]. flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); ch_info++; } } /* Check if we do have HT40 channels */ if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 && il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) return 0; /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ for (band = 6; band <= 7; band++) { enum ieee80211_band ieeeband; il_init_band_reference(il, band, &eeprom_ch_count, &eeprom_ch_info, &eeprom_ch_idx); /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ ieeeband = (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; /* Loop through each band adding each of the channels */ for (ch = 0; ch < eeprom_ch_count; ch++) { /* Set up driver's info for lower half */ il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch], &eeprom_ch_info[ch], IEEE80211_CHAN_NO_HT40PLUS); /* Set up driver's info for upper half */ il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch] + 4, &eeprom_ch_info[ch], IEEE80211_CHAN_NO_HT40MINUS); } } return 0; } EXPORT_SYMBOL(il_init_channel_map); /* * il_free_channel_map - undo allocations in il_init_channel_map */ void il_free_channel_map(struct il_priv *il) { kfree(il->channel_info); il->channel_count = 0; } EXPORT_SYMBOL(il_free_channel_map); /** * il_get_channel_info - Find driver's ilate channel info * * Based on band and channel number. */ const struct il_channel_info * il_get_channel_info(const struct il_priv *il, enum ieee80211_band band, u16 channel) { int i; switch (band) { case IEEE80211_BAND_5GHZ: for (i = 14; i < il->channel_count; i++) { if (il->channel_info[i].channel == channel) return &il->channel_info[i]; } break; case IEEE80211_BAND_2GHZ: if (channel >= 1 && channel <= 14) return &il->channel_info[channel - 1]; break; default: BUG(); } return NULL; } EXPORT_SYMBOL(il_get_channel_info); /* * Setting power level allows the card to go to sleep when not busy. * * We calculate a sleep command based on the required latency, which * we get from mac80211. In order to handle thermal throttling, we can * also use pre-defined power levels. */ /* * This defines the old power levels. They are still used by default * (level 1) and for thermal throttle (levels 3 through 5) */ struct il_power_vec_entry { struct il_powertable_cmd cmd; u8 no_dtim; /* number of skip dtim */ }; static void il_power_sleep_cam_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) { memset(cmd, 0, sizeof(*cmd)); if (il->power_data.pci_pm) cmd->flags |= IL_POWER_PCI_PM_MSK; D_POWER("Sleep command for CAM\n"); } static int il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd) { D_POWER("Sending power/sleep command\n"); D_POWER("Flags value = 0x%08X\n", cmd->flags); D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", le32_to_cpu(cmd->sleep_interval[0]), le32_to_cpu(cmd->sleep_interval[1]), le32_to_cpu(cmd->sleep_interval[2]), le32_to_cpu(cmd->sleep_interval[3]), le32_to_cpu(cmd->sleep_interval[4])); return il_send_cmd_pdu(il, C_POWER_TBL, sizeof(struct il_powertable_cmd), cmd); } int il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force) { int ret; bool update_chains; lockdep_assert_held(&il->mutex); /* Don't update the RX chain when chain noise calibration is running */ update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE || il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE; if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) return 0; if (!il_is_ready_rf(il)) return -EIO; /* scan complete use sleep_power_next, need to be updated */ memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); if (test_bit(S_SCANNING, &il->status) && !force) { D_INFO("Defer power set mode while scanning\n"); return 0; } if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK) set_bit(S_POWER_PMI, &il->status); ret = il_set_power(il, cmd); if (!ret) { if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)) clear_bit(S_POWER_PMI, &il->status); if (il->ops->update_chain_flags && update_chains) il->ops->update_chain_flags(il); else if (il->ops->update_chain_flags) D_POWER("Cannot update the power, chain noise " "calibration running: %d\n", il->chain_noise_data.state); memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)); } else IL_ERR("set power fail, ret = %d", ret); return ret; } int il_power_update_mode(struct il_priv *il, bool force) { struct il_powertable_cmd cmd; il_power_sleep_cam_cmd(il, &cmd); return il_power_set_mode(il, &cmd, force); } EXPORT_SYMBOL(il_power_update_mode); /* initialize to default */ void il_power_initialize(struct il_priv *il) { u16 lctl = il_pcie_link_ctl(il); il->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); il->power_data.debug_sleep_level_override = -1; memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd)); } EXPORT_SYMBOL(il_power_initialize); /* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after * sending probe req. This should be set long enough to hear probe responses * from more than one AP. */ #define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ #define IL_ACTIVE_DWELL_TIME_52 (20) #define IL_ACTIVE_DWELL_FACTOR_24GHZ (3) #define IL_ACTIVE_DWELL_FACTOR_52GHZ (2) /* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. * Must be set longer than active dwell time. * For the most reliable scan, set > AP beacon interval (typically 100msec). */ #define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ #define IL_PASSIVE_DWELL_TIME_52 (10) #define IL_PASSIVE_DWELL_BASE (100) #define IL_CHANNEL_TUNE_TIME 5 static int il_send_scan_abort(struct il_priv *il) { int ret; struct il_rx_pkt *pkt; struct il_host_cmd cmd = { .id = C_SCAN_ABORT, .flags = CMD_WANT_SKB, }; /* Exit instantly with error when device is not ready * to receive scan abort command or it does not perform * hardware scan currently */ if (!test_bit(S_READY, &il->status) || !test_bit(S_GEO_CONFIGURED, &il->status) || !test_bit(S_SCAN_HW, &il->status) || test_bit(S_FW_ERROR, &il->status) || test_bit(S_EXIT_PENDING, &il->status)) return -EIO; ret = il_send_cmd_sync(il, &cmd); if (ret) return ret; pkt = (struct il_rx_pkt *)cmd.reply_page; if (pkt->u.status != CAN_ABORT_STATUS) { /* The scan abort will return 1 for success or * 2 for "failure". A failure condition can be * due to simply not being in an active scan which * can occur if we send the scan abort before we * the microcode has notified us that a scan is * completed. */ D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status); ret = -EIO; } il_free_pages(il, cmd.reply_page); return ret; } static void il_complete_scan(struct il_priv *il, bool aborted) { /* check if scan was requested from mac80211 */ if (il->scan_request) { D_SCAN("Complete scan in mac80211\n"); ieee80211_scan_completed(il->hw, aborted); } il->scan_vif = NULL; il->scan_request = NULL; } void il_force_scan_end(struct il_priv *il) { lockdep_assert_held(&il->mutex); if (!test_bit(S_SCANNING, &il->status)) { D_SCAN("Forcing scan end while not scanning\n"); return; } D_SCAN("Forcing scan end\n"); clear_bit(S_SCANNING, &il->status); clear_bit(S_SCAN_HW, &il->status); clear_bit(S_SCAN_ABORTING, &il->status); il_complete_scan(il, true); } static void il_do_scan_abort(struct il_priv *il) { int ret; lockdep_assert_held(&il->mutex); if (!test_bit(S_SCANNING, &il->status)) { D_SCAN("Not performing scan to abort\n"); return; } if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) { D_SCAN("Scan abort in progress\n"); return; } ret = il_send_scan_abort(il); if (ret) { D_SCAN("Send scan abort failed %d\n", ret); il_force_scan_end(il); } else D_SCAN("Successfully send scan abort\n"); } /** * il_scan_cancel - Cancel any currently executing HW scan */ int il_scan_cancel(struct il_priv *il) { D_SCAN("Queuing abort scan\n"); queue_work(il->workqueue, &il->abort_scan); return 0; } EXPORT_SYMBOL(il_scan_cancel); /** * il_scan_cancel_timeout - Cancel any currently executing HW scan * @ms: amount of time to wait (in milliseconds) for scan to abort * */ int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms) { unsigned long timeout = jiffies + msecs_to_jiffies(ms); lockdep_assert_held(&il->mutex); D_SCAN("Scan cancel timeout\n"); il_do_scan_abort(il); while (time_before_eq(jiffies, timeout)) { if (!test_bit(S_SCAN_HW, &il->status)) break; msleep(20); } return test_bit(S_SCAN_HW, &il->status); } EXPORT_SYMBOL(il_scan_cancel_timeout); /* Service response to C_SCAN (0x80) */ static void il_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb) { #ifdef CONFIG_IWLEGACY_DEBUG struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_scanreq_notification *notif = (struct il_scanreq_notification *)pkt->u.raw; D_SCAN("Scan request status = 0x%x\n", notif->status); #endif } /* Service N_SCAN_START (0x82) */ static void il_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_scanstart_notification *notif = (struct il_scanstart_notification *)pkt->u.raw; il->scan_start_tsf = le32_to_cpu(notif->tsf_low); D_SCAN("Scan start: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel, notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); } /* Service N_SCAN_RESULTS (0x83) */ static void il_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb) { #ifdef CONFIG_IWLEGACY_DEBUG struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_scanresults_notification *notif = (struct il_scanresults_notification *)pkt->u.raw; D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d " "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), le32_to_cpu(notif->stats[0]), le32_to_cpu(notif->tsf_low) - il->scan_start_tsf); #endif } /* Service N_SCAN_COMPLETE (0x84) */ static void il_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb) { #ifdef CONFIG_IWLEGACY_DEBUG struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw; #endif D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", scan_notif->scanned_channels, scan_notif->tsf_low, scan_notif->tsf_high, scan_notif->status); /* The HW is no longer scanning */ clear_bit(S_SCAN_HW, &il->status); D_SCAN("Scan on %sGHz took %dms\n", (il->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", jiffies_to_msecs(jiffies - il->scan_start)); queue_work(il->workqueue, &il->scan_completed); } void il_setup_rx_scan_handlers(struct il_priv *il) { /* scan handlers */ il->handlers[C_SCAN] = il_hdl_scan; il->handlers[N_SCAN_START] = il_hdl_scan_start; il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results; il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete; } EXPORT_SYMBOL(il_setup_rx_scan_handlers); inline u16 il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band, u8 n_probes) { if (band == IEEE80211_BAND_5GHZ) return IL_ACTIVE_DWELL_TIME_52 + IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); else return IL_ACTIVE_DWELL_TIME_24 + IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); } EXPORT_SYMBOL(il_get_active_dwell_time); u16 il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band, struct ieee80211_vif *vif) { u16 value; u16 passive = (band == IEEE80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE + IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE + IL_PASSIVE_DWELL_TIME_52; if (il_is_any_associated(il)) { /* * If we're associated, we clamp the maximum passive * dwell time to be 98% of the smallest beacon interval * (minus 2 * channel tune time) */ value = il->vif ? il->vif->bss_conf.beacon_int : 0; if (value > IL_PASSIVE_DWELL_BASE || !value) value = IL_PASSIVE_DWELL_BASE; value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2; passive = min(value, passive); } return passive; } EXPORT_SYMBOL(il_get_passive_dwell_time); void il_init_scan_params(struct il_priv *il) { u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1; if (!il->scan_tx_ant[IEEE80211_BAND_5GHZ]) il->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; if (!il->scan_tx_ant[IEEE80211_BAND_2GHZ]) il->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; } EXPORT_SYMBOL(il_init_scan_params); static int il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif) { int ret; lockdep_assert_held(&il->mutex); cancel_delayed_work(&il->scan_check); if (!il_is_ready_rf(il)) { IL_WARN("Request scan called when driver not ready.\n"); return -EIO; } if (test_bit(S_SCAN_HW, &il->status)) { D_SCAN("Multiple concurrent scan requests in parallel.\n"); return -EBUSY; } if (test_bit(S_SCAN_ABORTING, &il->status)) { D_SCAN("Scan request while abort pending.\n"); return -EBUSY; } D_SCAN("Starting scan...\n"); set_bit(S_SCANNING, &il->status); il->scan_start = jiffies; ret = il->ops->request_scan(il, vif); if (ret) { clear_bit(S_SCANNING, &il->status); return ret; } queue_delayed_work(il->workqueue, &il->scan_check, IL_SCAN_CHECK_WATCHDOG); return 0; } int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct il_priv *il = hw->priv; int ret; if (req->n_channels == 0) { IL_ERR("Can not scan on no channels.\n"); return -EINVAL; } mutex_lock(&il->mutex); D_MAC80211("enter\n"); if (test_bit(S_SCANNING, &il->status)) { D_SCAN("Scan already in progress.\n"); ret = -EAGAIN; goto out_unlock; } /* mac80211 will only ask for one band at a time */ il->scan_request = req; il->scan_vif = vif; il->scan_band = req->channels[0]->band; ret = il_scan_initiate(il, vif); out_unlock: D_MAC80211("leave ret %d\n", ret); mutex_unlock(&il->mutex); return ret; } EXPORT_SYMBOL(il_mac_hw_scan); static void il_bg_scan_check(struct work_struct *data) { struct il_priv *il = container_of(data, struct il_priv, scan_check.work); D_SCAN("Scan check work\n"); /* Since we are here firmware does not finish scan and * most likely is in bad shape, so we don't bother to * send abort command, just force scan complete to mac80211 */ mutex_lock(&il->mutex); il_force_scan_end(il); mutex_unlock(&il->mutex); } /** * il_fill_probe_req - fill in all required fields and IE for probe request */ u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, const u8 *ta, const u8 *ies, int ie_len, int left) { int len = 0; u8 *pos = NULL; /* Make sure there is enough space for the probe request, * two mandatory IEs and the data */ left -= 24; if (left < 0) return 0; frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); memcpy(frame->da, il_bcast_addr, ETH_ALEN); memcpy(frame->sa, ta, ETH_ALEN); memcpy(frame->bssid, il_bcast_addr, ETH_ALEN); frame->seq_ctrl = 0; len += 24; /* ...next IE... */ pos = &frame->u.probe_req.variable[0]; /* fill in our indirect SSID IE */ left -= 2; if (left < 0) return 0; *pos++ = WLAN_EID_SSID; *pos++ = 0; len += 2; if (WARN_ON(left < ie_len)) return len; if (ies && ie_len) { memcpy(pos, ies, ie_len); len += ie_len; } return (u16) len; } EXPORT_SYMBOL(il_fill_probe_req); static void il_bg_abort_scan(struct work_struct *work) { struct il_priv *il = container_of(work, struct il_priv, abort_scan); D_SCAN("Abort scan work\n"); /* We keep scan_check work queued in case when firmware will not * report back scan completed notification */ mutex_lock(&il->mutex); il_scan_cancel_timeout(il, 200); mutex_unlock(&il->mutex); } static void il_bg_scan_completed(struct work_struct *work) { struct il_priv *il = container_of(work, struct il_priv, scan_completed); bool aborted; D_SCAN("Completed scan.\n"); cancel_delayed_work(&il->scan_check); mutex_lock(&il->mutex); aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status); if (aborted) D_SCAN("Aborted scan completed.\n"); if (!test_and_clear_bit(S_SCANNING, &il->status)) { D_SCAN("Scan already completed.\n"); goto out_settings; } il_complete_scan(il, aborted); out_settings: /* Can we still talk to firmware ? */ if (!il_is_ready_rf(il)) goto out; /* * We do not commit power settings while scan is pending, * do it now if the settings changed. */ il_power_set_mode(il, &il->power_data.sleep_cmd_next, false); il_set_tx_power(il, il->tx_power_next, false); il->ops->post_scan(il); out: mutex_unlock(&il->mutex); } void il_setup_scan_deferred_work(struct il_priv *il) { INIT_WORK(&il->scan_completed, il_bg_scan_completed); INIT_WORK(&il->abort_scan, il_bg_abort_scan); INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check); } EXPORT_SYMBOL(il_setup_scan_deferred_work); void il_cancel_scan_deferred_work(struct il_priv *il) { cancel_work_sync(&il->abort_scan); cancel_work_sync(&il->scan_completed); if (cancel_delayed_work_sync(&il->scan_check)) { mutex_lock(&il->mutex); il_force_scan_end(il); mutex_unlock(&il->mutex); } } EXPORT_SYMBOL(il_cancel_scan_deferred_work); /* il->sta_lock must be held */ static void il_sta_ucode_activate(struct il_priv *il, u8 sta_id) { if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n", sta_id, il->stations[sta_id].sta.sta.addr); if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) { D_ASSOC("STA id %u addr %pM already present" " in uCode (according to driver)\n", sta_id, il->stations[sta_id].sta.sta.addr); } else { il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE; D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id, il->stations[sta_id].sta.sta.addr); } } static int il_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta, struct il_rx_pkt *pkt, bool sync) { u8 sta_id = addsta->sta.sta_id; unsigned long flags; int ret = -EIO; if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags); return ret; } D_INFO("Processing response for adding station %u\n", sta_id); spin_lock_irqsave(&il->sta_lock, flags); switch (pkt->u.add_sta.status) { case ADD_STA_SUCCESS_MSK: D_INFO("C_ADD_STA PASSED\n"); il_sta_ucode_activate(il, sta_id); ret = 0; break; case ADD_STA_NO_ROOM_IN_TBL: IL_ERR("Adding station %d failed, no room in table.\n", sta_id); break; case ADD_STA_NO_BLOCK_ACK_RESOURCE: IL_ERR("Adding station %d failed, no block ack resource.\n", sta_id); break; case ADD_STA_MODIFY_NON_EXIST_STA: IL_ERR("Attempting to modify non-existing station %d\n", sta_id); break; default: D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status); break; } D_INFO("%s station id %u addr %pM\n", il->stations[sta_id].sta.mode == STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id, il->stations[sta_id].sta.sta.addr); /* * XXX: The MAC address in the command buffer is often changed from * the original sent to the device. That is, the MAC address * written to the command buffer often is not the same MAC address * read from the command buffer when the command returns. This * issue has not yet been resolved and this debugging is left to * observe the problem. */ D_INFO("%s station according to cmd buffer %pM\n", il->stations[sta_id].sta.mode == STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); spin_unlock_irqrestore(&il->sta_lock, flags); return ret; } static void il_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd, struct il_rx_pkt *pkt) { struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload; il_process_add_sta_resp(il, addsta, pkt, false); } int il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags) { struct il_rx_pkt *pkt = NULL; int ret = 0; u8 data[sizeof(*sta)]; struct il_host_cmd cmd = { .id = C_ADD_STA, .flags = flags, .data = data, }; u8 sta_id __maybe_unused = sta->sta.sta_id; D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); if (flags & CMD_ASYNC) cmd.callback = il_add_sta_callback; else { cmd.flags |= CMD_WANT_SKB; might_sleep(); } cmd.len = il->ops->build_addsta_hcmd(sta, data); ret = il_send_cmd(il, &cmd); if (ret || (flags & CMD_ASYNC)) return ret; if (ret == 0) { pkt = (struct il_rx_pkt *)cmd.reply_page; ret = il_process_add_sta_resp(il, sta, pkt, true); } il_free_pages(il, cmd.reply_page); return ret; } EXPORT_SYMBOL(il_send_add_sta); static void il_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta) { struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; __le32 sta_flags; u8 mimo_ps_mode; if (!sta || !sta_ht_inf->ht_supported) goto done; mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; D_ASSOC("spatial multiplexing power save mode: %s\n", (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? "static" : (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? "dynamic" : "disabled"); sta_flags = il->stations[idx].sta.station_flags; sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); switch (mimo_ps_mode) { case WLAN_HT_CAP_SM_PS_STATIC: sta_flags |= STA_FLG_MIMO_DIS_MSK; break; case WLAN_HT_CAP_SM_PS_DYNAMIC: sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; break; case WLAN_HT_CAP_SM_PS_DISABLED: break; default: IL_WARN("Invalid MIMO PS mode %d\n", mimo_ps_mode); break; } sta_flags |= cpu_to_le32((u32) sta_ht_inf-> ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); sta_flags |= cpu_to_le32((u32) sta_ht_inf-> ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) sta_flags |= STA_FLG_HT40_EN_MSK; else sta_flags &= ~STA_FLG_HT40_EN_MSK; il->stations[idx].sta.station_flags = sta_flags; done: return; } /** * il_prep_station - Prepare station information for addition * * should be called with sta_lock held */ u8 il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, struct ieee80211_sta *sta) { struct il_station_entry *station; int i; u8 sta_id = IL_INVALID_STATION; u16 rate; if (is_ap) sta_id = IL_AP_ID; else if (is_broadcast_ether_addr(addr)) sta_id = il->hw_params.bcast_id; else for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) { if (ether_addr_equal(il->stations[i].sta.sta.addr, addr)) { sta_id = i; break; } if (!il->stations[i].used && sta_id == IL_INVALID_STATION) sta_id = i; } /* * These two conditions have the same outcome, but keep them * separate */ if (unlikely(sta_id == IL_INVALID_STATION)) return sta_id; /* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another. */ if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { D_INFO("STA %d already in process of being added.\n", sta_id); return sta_id; } if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) && ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) { D_ASSOC("STA %d (%pM) already added, not adding again.\n", sta_id, addr); return sta_id; } station = &il->stations[sta_id]; station->used = IL_STA_DRIVER_ACTIVE; D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr); il->num_stations++; /* Set up the C_ADD_STA command to send to device */ memset(&station->sta, 0, sizeof(struct il_addsta_cmd)); memcpy(station->sta.sta.addr, addr, ETH_ALEN); station->sta.mode = 0; station->sta.sta.sta_id = sta_id; station->sta.station_flags = 0; /* * OK to call unconditionally, since local stations (IBSS BSSID * STA and broadcast STA) pass in a NULL sta, and mac80211 * doesn't allow HT IBSS. */ il_set_ht_add_station(il, sta_id, sta); /* 3945 only */ rate = (il->band == IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP; /* Turn on both antennas for the station... */ station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); return sta_id; } EXPORT_SYMBOL_GPL(il_prep_station); #define STA_WAIT_TIMEOUT (HZ/2) /** * il_add_station_common - */ int il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, struct ieee80211_sta *sta, u8 *sta_id_r) { unsigned long flags_spin; int ret = 0; u8 sta_id; struct il_addsta_cmd sta_cmd; *sta_id_r = 0; spin_lock_irqsave(&il->sta_lock, flags_spin); sta_id = il_prep_station(il, addr, is_ap, sta); if (sta_id == IL_INVALID_STATION) { IL_ERR("Unable to prepare station %pM for addition\n", addr); spin_unlock_irqrestore(&il->sta_lock, flags_spin); return -EINVAL; } /* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another. */ if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { D_INFO("STA %d already in process of being added.\n", sta_id); spin_unlock_irqrestore(&il->sta_lock, flags_spin); return -EEXIST; } if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { D_ASSOC("STA %d (%pM) already added, not adding again.\n", sta_id, addr); spin_unlock_irqrestore(&il->sta_lock, flags_spin); return -EEXIST; } il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS; memcpy(&sta_cmd, &il->stations[sta_id].sta, sizeof(struct il_addsta_cmd)); spin_unlock_irqrestore(&il->sta_lock, flags_spin); /* Add station to device's station table */ ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); if (ret) { spin_lock_irqsave(&il->sta_lock, flags_spin); IL_ERR("Adding station %pM failed.\n", il->stations[sta_id].sta.sta.addr); il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; spin_unlock_irqrestore(&il->sta_lock, flags_spin); } *sta_id_r = sta_id; return ret; } EXPORT_SYMBOL(il_add_station_common); /** * il_sta_ucode_deactivate - deactivate ucode status for a station * * il->sta_lock must be held */ static void il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id) { /* Ucode must be active and driver must be non active */ if ((il->stations[sta_id]. used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) != IL_STA_UCODE_ACTIVE) IL_ERR("removed non active STA %u\n", sta_id); il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE; memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry)); D_ASSOC("Removed STA %u\n", sta_id); } static int il_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id, bool temporary) { struct il_rx_pkt *pkt; int ret; unsigned long flags_spin; struct il_rem_sta_cmd rm_sta_cmd; struct il_host_cmd cmd = { .id = C_REM_STA, .len = sizeof(struct il_rem_sta_cmd), .flags = CMD_SYNC, .data = &rm_sta_cmd, }; memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); rm_sta_cmd.num_sta = 1; memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); cmd.flags |= CMD_WANT_SKB; ret = il_send_cmd(il, &cmd); if (ret) return ret; pkt = (struct il_rx_pkt *)cmd.reply_page; if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags); ret = -EIO; } if (!ret) { switch (pkt->u.rem_sta.status) { case REM_STA_SUCCESS_MSK: if (!temporary) { spin_lock_irqsave(&il->sta_lock, flags_spin); il_sta_ucode_deactivate(il, sta_id); spin_unlock_irqrestore(&il->sta_lock, flags_spin); } D_ASSOC("C_REM_STA PASSED\n"); break; default: ret = -EIO; IL_ERR("C_REM_STA failed\n"); break; } } il_free_pages(il, cmd.reply_page); return ret; } /** * il_remove_station - Remove driver's knowledge of station. */ int il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr) { unsigned long flags; if (!il_is_ready(il)) { D_INFO("Unable to remove station %pM, device not ready.\n", addr); /* * It is typical for stations to be removed when we are * going down. Return success since device will be down * soon anyway */ return 0; } D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr); if (WARN_ON(sta_id == IL_INVALID_STATION)) return -EINVAL; spin_lock_irqsave(&il->sta_lock, flags); if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) { D_INFO("Removing %pM but non DRIVER active\n", addr); goto out_err; } if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { D_INFO("Removing %pM but non UCODE active\n", addr); goto out_err; } if (il->stations[sta_id].used & IL_STA_LOCAL) { kfree(il->stations[sta_id].lq); il->stations[sta_id].lq = NULL; } il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; il->num_stations--; BUG_ON(il->num_stations < 0); spin_unlock_irqrestore(&il->sta_lock, flags); return il_send_remove_station(il, addr, sta_id, false); out_err: spin_unlock_irqrestore(&il->sta_lock, flags); return -EINVAL; } EXPORT_SYMBOL_GPL(il_remove_station); /** * il_clear_ucode_stations - clear ucode station table bits * * This function clears all the bits in the driver indicating * which stations are active in the ucode. Call when something * other than explicit station management would cause this in * the ucode, e.g. unassociated RXON. */ void il_clear_ucode_stations(struct il_priv *il) { int i; unsigned long flags_spin; bool cleared = false; D_INFO("Clearing ucode stations in driver\n"); spin_lock_irqsave(&il->sta_lock, flags_spin); for (i = 0; i < il->hw_params.max_stations; i++) { if (il->stations[i].used & IL_STA_UCODE_ACTIVE) { D_INFO("Clearing ucode active for station %d\n", i); il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; cleared = true; } } spin_unlock_irqrestore(&il->sta_lock, flags_spin); if (!cleared) D_INFO("No active stations found to be cleared\n"); } EXPORT_SYMBOL(il_clear_ucode_stations); /** * il_restore_stations() - Restore driver known stations to device * * All stations considered active by driver, but not present in ucode, is * restored. * * Function sleeps. */ void il_restore_stations(struct il_priv *il) { struct il_addsta_cmd sta_cmd; struct il_link_quality_cmd lq; unsigned long flags_spin; int i; bool found = false; int ret; bool send_lq; if (!il_is_ready(il)) { D_INFO("Not ready yet, not restoring any stations.\n"); return; } D_ASSOC("Restoring all known stations ... start.\n"); spin_lock_irqsave(&il->sta_lock, flags_spin); for (i = 0; i < il->hw_params.max_stations; i++) { if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) && !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) { D_ASSOC("Restoring sta %pM\n", il->stations[i].sta.sta.addr); il->stations[i].sta.mode = 0; il->stations[i].used |= IL_STA_UCODE_INPROGRESS; found = true; } } for (i = 0; i < il->hw_params.max_stations; i++) { if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) { memcpy(&sta_cmd, &il->stations[i].sta, sizeof(struct il_addsta_cmd)); send_lq = false; if (il->stations[i].lq) { memcpy(&lq, il->stations[i].lq, sizeof(struct il_link_quality_cmd)); send_lq = true; } spin_unlock_irqrestore(&il->sta_lock, flags_spin); ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); if (ret) { spin_lock_irqsave(&il->sta_lock, flags_spin); IL_ERR("Adding station %pM failed.\n", il->stations[i].sta.sta.addr); il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE; il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; spin_unlock_irqrestore(&il->sta_lock, flags_spin); } /* * Rate scaling has already been initialized, send * current LQ command */ if (send_lq) il_send_lq_cmd(il, &lq, CMD_SYNC, true); spin_lock_irqsave(&il->sta_lock, flags_spin); il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; } } spin_unlock_irqrestore(&il->sta_lock, flags_spin); if (!found) D_INFO("Restoring all known stations" " .... no stations to be restored.\n"); else D_INFO("Restoring all known stations" " .... complete.\n"); } EXPORT_SYMBOL(il_restore_stations); int il_get_free_ucode_key_idx(struct il_priv *il) { int i; for (i = 0; i < il->sta_key_max_num; i++) if (!test_and_set_bit(i, &il->ucode_key_table)) return i; return WEP_INVALID_OFFSET; } EXPORT_SYMBOL(il_get_free_ucode_key_idx); void il_dealloc_bcast_stations(struct il_priv *il) { unsigned long flags; int i; spin_lock_irqsave(&il->sta_lock, flags); for (i = 0; i < il->hw_params.max_stations; i++) { if (!(il->stations[i].used & IL_STA_BCAST)) continue; il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; il->num_stations--; BUG_ON(il->num_stations < 0); kfree(il->stations[i].lq); il->stations[i].lq = NULL; } spin_unlock_irqrestore(&il->sta_lock, flags); } EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations); #ifdef CONFIG_IWLEGACY_DEBUG static void il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) { int i; D_RATE("lq station id 0x%x\n", lq->sta_id); D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk, lq->general_params.dual_stream_ant_msk); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags); } #else static inline void il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) { } #endif /** * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity * * It sometimes happens when a HT rate has been in use and we * loose connectivity with AP then mac80211 will first tell us that the * current channel is not HT anymore before removing the station. In such a * scenario the RXON flags will be updated to indicate we are not * communicating HT anymore, but the LQ command may still contain HT rates. * Test for this to prevent driver from sending LQ command between the time * RXON flags are updated and when LQ command is updated. */ static bool il_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq) { int i; if (il->ht.enabled) return true; D_INFO("Channel %u is not an HT channel\n", il->active.channel); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) { D_INFO("idx %d of LQ expects HT channel\n", i); return false; } } return true; } /** * il_send_lq_cmd() - Send link quality command * @init: This command is sent as part of station initialization right * after station has been added. * * The link quality command is sent as the last step of station creation. * This is the special case in which init is set and we call a callback in * this case to clear the state indicating that station creation is in * progress. */ int il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, u8 flags, bool init) { int ret = 0; unsigned long flags_spin; struct il_host_cmd cmd = { .id = C_TX_LINK_QUALITY_CMD, .len = sizeof(struct il_link_quality_cmd), .flags = flags, .data = lq, }; if (WARN_ON(lq->sta_id == IL_INVALID_STATION)) return -EINVAL; spin_lock_irqsave(&il->sta_lock, flags_spin); if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) { spin_unlock_irqrestore(&il->sta_lock, flags_spin); return -EINVAL; } spin_unlock_irqrestore(&il->sta_lock, flags_spin); il_dump_lq_cmd(il, lq); BUG_ON(init && (cmd.flags & CMD_ASYNC)); if (il_is_lq_table_valid(il, lq)) ret = il_send_cmd(il, &cmd); else ret = -EINVAL; if (cmd.flags & CMD_ASYNC) return ret; if (init) { D_INFO("init LQ command complete," " clearing sta addition status for sta %d\n", lq->sta_id); spin_lock_irqsave(&il->sta_lock, flags_spin); il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS; spin_unlock_irqrestore(&il->sta_lock, flags_spin); } return ret; } EXPORT_SYMBOL(il_send_lq_cmd); int il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct il_priv *il = hw->priv; struct il_station_priv_common *sta_common = (void *)sta->drv_priv; int ret; mutex_lock(&il->mutex); D_MAC80211("enter station %pM\n", sta->addr); ret = il_remove_station(il, sta_common->sta_id, sta->addr); if (ret) IL_ERR("Error removing station %pM\n", sta->addr); D_MAC80211("leave ret %d\n", ret); mutex_unlock(&il->mutex); return ret; } EXPORT_SYMBOL(il_mac_sta_remove); /************************** RX-FUNCTIONS ****************************/ /* * Rx theory of operation * * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), * each of which point to Receive Buffers to be filled by the NIC. These get * used not only for Rx frames, but for any command response or notification * from the NIC. The driver and NIC manage the Rx buffers by means * of idxes into the circular buffer. * * Rx Queue Indexes * The host/firmware share two idx registers for managing the Rx buffers. * * The READ idx maps to the first position that the firmware may be writing * to -- the driver can read up to (but not including) this position and get * good data. * The READ idx is managed by the firmware once the card is enabled. * * The WRITE idx maps to the last position the driver has read from -- the * position preceding WRITE is the last slot the firmware can place a packet. * * The queue is empty (no good data) if WRITE = READ - 1, and is full if * WRITE = READ. * * During initialization, the host sets up the READ queue position to the first * IDX position, and WRITE to the last (READ - 1 wrapped) * * When the firmware places a packet in a buffer, it will advance the READ idx * and fire the RX interrupt. The driver can then query the READ idx and * process as many packets as possible, moving the WRITE idx forward as it * resets the Rx queue buffers with new memory. * * The management in the driver is as follows: * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled * to replenish the iwl->rxq->rx_free. * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the * iwl->rxq is replenished and the READ IDX is updated (updating the * 'processed' and 'read' driver idxes as well) * + A received packet is processed and handed to the kernel network stack, * detached from the iwl->rxq. The driver 'processed' idx is updated. * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ * IDX is not incremented and iwl->status(RX_STALLED) is set. If there * were enough free buffers and RX_STALLED is set it is cleared. * * * Driver sequence: * * il_rx_queue_alloc() Allocates rx_free * il_rx_replenish() Replenishes rx_free list from rx_used, and calls * il_rx_queue_restock * il_rx_queue_restock() Moves available buffers from rx_free into Rx * queue, updates firmware pointers, and updates * the WRITE idx. If insufficient rx_free buffers * are available, schedules il_rx_replenish * * -- enable interrupts -- * ISR - il_rx() Detach il_rx_bufs from pool up to the * READ IDX, detaching the SKB from the pool. * Moves the packet buffer from queue to rx_used. * Calls il_rx_queue_restock to refill any empty * slots. * ... * */ /** * il_rx_queue_space - Return number of free slots available in queue. */ int il_rx_queue_space(const struct il_rx_queue *q) { int s = q->read - q->write; if (s <= 0) s += RX_QUEUE_SIZE; /* keep some buffer to not confuse full and empty queue */ s -= 2; if (s < 0) s = 0; return s; } EXPORT_SYMBOL(il_rx_queue_space); /** * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue */ void il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q) { unsigned long flags; u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg; u32 reg; spin_lock_irqsave(&q->lock, flags); if (q->need_update == 0) goto exit_unlock; /* If power-saving is in use, make sure device is awake */ if (test_bit(S_POWER_PMI, &il->status)) { reg = _il_rd(il, CSR_UCODE_DRV_GP1); if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n", reg); il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); goto exit_unlock; } q->write_actual = (q->write & ~0x7); il_wr(il, rx_wrt_ptr_reg, q->write_actual); /* Else device is assumed to be awake */ } else { /* Device expects a multiple of 8 */ q->write_actual = (q->write & ~0x7); il_wr(il, rx_wrt_ptr_reg, q->write_actual); } q->need_update = 0; exit_unlock: spin_unlock_irqrestore(&q->lock, flags); } EXPORT_SYMBOL(il_rx_queue_update_write_ptr); int il_rx_queue_alloc(struct il_priv *il) { struct il_rx_queue *rxq = &il->rxq; struct device *dev = &il->pci_dev->dev; int i; spin_lock_init(&rxq->lock); INIT_LIST_HEAD(&rxq->rx_free); INIT_LIST_HEAD(&rxq->rx_used); /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma, GFP_KERNEL); if (!rxq->bd) goto err_bd; rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status), &rxq->rb_stts_dma, GFP_KERNEL); if (!rxq->rb_stts) goto err_rb; /* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) list_add_tail(&rxq->pool[i].list, &rxq->rx_used); /* Set us so that we have processed and used all buffers, but have * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; rxq->write_actual = 0; rxq->free_count = 0; rxq->need_update = 0; return 0; err_rb: dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); err_bd: return -ENOMEM; } EXPORT_SYMBOL(il_rx_queue_alloc); void il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_spectrum_notification *report = &(pkt->u.spectrum_notif); if (!report->state) { D_11H("Spectrum Measure Notification: Start\n"); return; } memcpy(&il->measure_report, report, sizeof(*report)); il->measurement_status |= MEASUREMENT_READY; } EXPORT_SYMBOL(il_hdl_spectrum_measurement); /* * returns non-zero if packet should be dropped */ int il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, u32 decrypt_res, struct ieee80211_rx_status *stats) { u16 fc = le16_to_cpu(hdr->frame_control); /* * All contexts have the same setting here due to it being * a module parameter, so OK to check any context. */ if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) return 0; if (!(fc & IEEE80211_FCTL_PROTECTED)) return 0; D_RX("decrypt_res:0x%x\n", decrypt_res); switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { case RX_RES_STATUS_SEC_TYPE_TKIP: /* The uCode has got a bad phase 1 Key, pushes the packet. * Decryption will be done in SW. */ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_BAD_KEY_TTAK) break; case RX_RES_STATUS_SEC_TYPE_WEP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_BAD_ICV_MIC) { /* bad ICV, the packet is destroyed since the * decryption is inplace, drop it */ D_RX("Packet destroyed\n"); return -1; } case RX_RES_STATUS_SEC_TYPE_CCMP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_DECRYPT_OK) { D_RX("hw decrypt successfully!!!\n"); stats->flag |= RX_FLAG_DECRYPTED; } break; default: break; } return 0; } EXPORT_SYMBOL(il_set_decrypted_flag); /** * il_txq_update_write_ptr - Send new write idx to hardware */ void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq) { u32 reg = 0; int txq_id = txq->q.id; if (txq->need_update == 0) return; /* if we're trying to save power */ if (test_bit(S_POWER_PMI, &il->status)) { /* wake up nic if it's powered down ... * uCode will wake up, and interrupt us again, so next * time we'll skip this part. */ reg = _il_rd(il, CSR_UCODE_DRV_GP1); if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n", txq_id, reg); il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); return; } il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); /* * else not in power-save mode, * uCode will never sleep when we're * trying to tx (during RFKILL, we're not trying to tx). */ } else _il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); txq->need_update = 0; } EXPORT_SYMBOL(il_txq_update_write_ptr); /** * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's */ void il_tx_queue_unmap(struct il_priv *il, int txq_id) { struct il_tx_queue *txq = &il->txq[txq_id]; struct il_queue *q = &txq->q; if (q->n_bd == 0) return; while (q->write_ptr != q->read_ptr) { il->ops->txq_free_tfd(il, txq); q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); } } EXPORT_SYMBOL(il_tx_queue_unmap); /** * il_tx_queue_free - Deallocate DMA queue. * @txq: Transmit queue to deallocate. * * Empty queue by removing and destroying all BD's. * Free all buffers. * 0-fill, but do not free "txq" descriptor structure. */ void il_tx_queue_free(struct il_priv *il, int txq_id) { struct il_tx_queue *txq = &il->txq[txq_id]; struct device *dev = &il->pci_dev->dev; int i; il_tx_queue_unmap(il, txq_id); /* De-alloc array of command/tx buffers */ for (i = 0; i < TFD_TX_CMD_SLOTS; i++) kfree(txq->cmd[i]); /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, txq->tfds, txq->q.dma_addr); /* De-alloc array of per-TFD driver data */ kfree(txq->skbs); txq->skbs = NULL; /* deallocate arrays */ kfree(txq->cmd); kfree(txq->meta); txq->cmd = NULL; txq->meta = NULL; /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } EXPORT_SYMBOL(il_tx_queue_free); /** * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue */ void il_cmd_queue_unmap(struct il_priv *il) { struct il_tx_queue *txq = &il->txq[il->cmd_queue]; struct il_queue *q = &txq->q; int i; if (q->n_bd == 0) return; while (q->read_ptr != q->write_ptr) { i = il_get_cmd_idx(q, q->read_ptr, 0); if (txq->meta[i].flags & CMD_MAPPED) { pci_unmap_single(il->pci_dev, dma_unmap_addr(&txq->meta[i], mapping), dma_unmap_len(&txq->meta[i], len), PCI_DMA_BIDIRECTIONAL); txq->meta[i].flags = 0; } q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); } i = q->n_win; if (txq->meta[i].flags & CMD_MAPPED) { pci_unmap_single(il->pci_dev, dma_unmap_addr(&txq->meta[i], mapping), dma_unmap_len(&txq->meta[i], len), PCI_DMA_BIDIRECTIONAL); txq->meta[i].flags = 0; } } EXPORT_SYMBOL(il_cmd_queue_unmap); /** * il_cmd_queue_free - Deallocate DMA queue. * @txq: Transmit queue to deallocate. * * Empty queue by removing and destroying all BD's. * Free all buffers. * 0-fill, but do not free "txq" descriptor structure. */ void il_cmd_queue_free(struct il_priv *il) { struct il_tx_queue *txq = &il->txq[il->cmd_queue]; struct device *dev = &il->pci_dev->dev; int i; il_cmd_queue_unmap(il); /* De-alloc array of command/tx buffers */ for (i = 0; i <= TFD_CMD_SLOTS; i++) kfree(txq->cmd[i]); /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, txq->tfds, txq->q.dma_addr); /* deallocate arrays */ kfree(txq->cmd); kfree(txq->meta); txq->cmd = NULL; txq->meta = NULL; /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } EXPORT_SYMBOL(il_cmd_queue_free); /*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** * DMA services * * Theory of operation * * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer * of buffer descriptors, each of which points to one or more data buffers for * the device to read from or fill. Driver and device exchange status of each * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty * entries in each circular buffer, to protect against confusing empty and full * queue states. * * The device reads or writes the data in the queues via the device's several * DMA/FIFO channels. Each queue is mapped to a single DMA channel. * * For Tx queue, there are low mark and high mark limits. If, after queuing * the packet for Tx, free space become < low mark, Tx queue stopped. When * reclaiming packets (on 'tx done IRQ), if free space become > high mark, * Tx queue resumed. * * See more detailed info in 4965.h. ***************************************************/ int il_queue_space(const struct il_queue *q) { int s = q->read_ptr - q->write_ptr; if (q->read_ptr > q->write_ptr) s -= q->n_bd; if (s <= 0) s += q->n_win; /* keep some reserve to not confuse empty and full situations */ s -= 2; if (s < 0) s = 0; return s; } EXPORT_SYMBOL(il_queue_space); /** * il_queue_init - Initialize queue's high/low-water and read/write idxes */ static int il_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id) { /* * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise * il_queue_inc_wrap and il_queue_dec_wrap are broken. */ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); /* FIXME: remove q->n_bd */ q->n_bd = TFD_QUEUE_SIZE_MAX; q->n_win = slots; q->id = id; /* slots_must be power-of-two size, otherwise * il_get_cmd_idx is broken. */ BUG_ON(!is_power_of_2(slots)); q->low_mark = q->n_win / 4; if (q->low_mark < 4) q->low_mark = 4; q->high_mark = q->n_win / 8; if (q->high_mark < 2) q->high_mark = 2; q->write_ptr = q->read_ptr = 0; return 0; } /** * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue */ static int il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id) { struct device *dev = &il->pci_dev->dev; size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX; /* Driver ilate data, only for Tx (not command) queues, * not shared with device. */ if (id != il->cmd_queue) { txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, sizeof(struct skb *), GFP_KERNEL); if (!txq->skbs) { IL_ERR("Fail to alloc skbs\n"); goto error; } } else txq->skbs = NULL; /* Circular buffer of transmit frame descriptors (TFDs), * shared with device */ txq->tfds = dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL); if (!txq->tfds) { IL_ERR("Fail to alloc TFDs\n"); goto error; } txq->q.id = id; return 0; error: kfree(txq->skbs); txq->skbs = NULL; return -ENOMEM; } /** * il_tx_queue_init - Allocate and initialize one tx/cmd queue */ int il_tx_queue_init(struct il_priv *il, u32 txq_id) { int i, len, ret; int slots, actual_slots; struct il_tx_queue *txq = &il->txq[txq_id]; /* * Alloc buffer array for commands (Tx or other types of commands). * For the command queue (#4/#9), allocate command space + one big * command for scan, since scan command is very huge; the system will * not have two scans at the same time, so only one is needed. * For normal Tx queues (all other queues), no super-size command * space is needed. */ if (txq_id == il->cmd_queue) { slots = TFD_CMD_SLOTS; actual_slots = slots + 1; } else { slots = TFD_TX_CMD_SLOTS; actual_slots = slots; } txq->meta = kzalloc(sizeof(struct il_cmd_meta) * actual_slots, GFP_KERNEL); txq->cmd = kzalloc(sizeof(struct il_device_cmd *) * actual_slots, GFP_KERNEL); if (!txq->meta || !txq->cmd) goto out_free_arrays; len = sizeof(struct il_device_cmd); for (i = 0; i < actual_slots; i++) { /* only happens for cmd queue */ if (i == slots) len = IL_MAX_CMD_SIZE; txq->cmd[i] = kmalloc(len, GFP_KERNEL); if (!txq->cmd[i]) goto err; } /* Alloc driver data array and TFD circular buffer */ ret = il_tx_queue_alloc(il, txq, txq_id); if (ret) goto err; txq->need_update = 0; /* * For the default queues 0-3, set up the swq_id * already -- all others need to get one later * (if they need one at all). */ if (txq_id < 4) il_set_swq_id(txq, txq_id, txq_id); /* Initialize queue's high/low-water marks, and head/tail idxes */ il_queue_init(il, &txq->q, slots, txq_id); /* Tell device where to find queue */ il->ops->txq_init(il, txq); return 0; err: for (i = 0; i < actual_slots; i++) kfree(txq->cmd[i]); out_free_arrays: kfree(txq->meta); kfree(txq->cmd); return -ENOMEM; } EXPORT_SYMBOL(il_tx_queue_init); void il_tx_queue_reset(struct il_priv *il, u32 txq_id) { int slots, actual_slots; struct il_tx_queue *txq = &il->txq[txq_id]; if (txq_id == il->cmd_queue) { slots = TFD_CMD_SLOTS; actual_slots = TFD_CMD_SLOTS + 1; } else { slots = TFD_TX_CMD_SLOTS; actual_slots = TFD_TX_CMD_SLOTS; } memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots); txq->need_update = 0; /* Initialize queue's high/low-water marks, and head/tail idxes */ il_queue_init(il, &txq->q, slots, txq_id); /* Tell device where to find queue */ il->ops->txq_init(il, txq); } EXPORT_SYMBOL(il_tx_queue_reset); /*************** HOST COMMAND QUEUE FUNCTIONS *****/ /** * il_enqueue_hcmd - enqueue a uCode command * @il: device ilate data point * @cmd: a point to the ucode command structure * * The function returns < 0 values to indicate the operation is * failed. On success, it turns the idx (> 0) of command in the * command queue. */ int il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) { struct il_tx_queue *txq = &il->txq[il->cmd_queue]; struct il_queue *q = &txq->q; struct il_device_cmd *out_cmd; struct il_cmd_meta *out_meta; dma_addr_t phys_addr; unsigned long flags; int len; u32 idx; u16 fix_size; cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len); fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr)); /* If any of the command structures end up being larger than * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then * we will need to increase the size of the TFD entries * Also, check to see if command buffer should not exceed the size * of device_cmd and max_cmd_size. */ BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && !(cmd->flags & CMD_SIZE_HUGE)); BUG_ON(fix_size > IL_MAX_CMD_SIZE); if (il_is_rfkill(il) || il_is_ctkill(il)) { IL_WARN("Not sending command - %s KILL\n", il_is_rfkill(il) ? "RF" : "CT"); return -EIO; } spin_lock_irqsave(&il->hcmd_lock, flags); if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_irqrestore(&il->hcmd_lock, flags); IL_ERR("Restarting adapter due to command queue full\n"); queue_work(il->workqueue, &il->restart); return -ENOSPC; } idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; if (WARN_ON(out_meta->flags & CMD_MAPPED)) { spin_unlock_irqrestore(&il->hcmd_lock, flags); return -ENOSPC; } memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ out_meta->flags = cmd->flags | CMD_MAPPED; if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; if (cmd->flags & CMD_ASYNC) out_meta->callback = cmd->callback; out_cmd->hdr.cmd = cmd->id; memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); /* At this point, the out_cmd now has all of the incoming cmd * information */ out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr)); if (cmd->flags & CMD_SIZE_HUGE) out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; len = sizeof(struct il_device_cmd); if (idx == TFD_CMD_SLOTS) len = IL_MAX_CMD_SIZE; #ifdef CONFIG_IWLEGACY_DEBUG switch (out_cmd->hdr.cmd) { case C_TX_LINK_QUALITY_CMD: case C_SENSITIVITY: D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, " "%d bytes at %d[%d]:%d\n", il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, idx, il->cmd_queue); break; default: D_HC("Sending command %s (#%x), seq: 0x%04X, " "%d bytes at %d[%d]:%d\n", il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, idx, il->cmd_queue); } #endif txq->need_update = 1; if (il->ops->txq_update_byte_cnt_tbl) /* Set up entry in queue's byte count circular buffer */ il->ops->txq_update_byte_cnt_tbl(il, txq, 0); phys_addr = pci_map_single(il->pci_dev, &out_cmd->hdr, fix_size, PCI_DMA_BIDIRECTIONAL); dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, fix_size); il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1, U32_PAD(cmd->len)); /* Increment and update queue's write idx */ q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); il_txq_update_write_ptr(il, txq); spin_unlock_irqrestore(&il->hcmd_lock, flags); return idx; } /** * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd * * When FW advances 'R' idx, all entries between old and new 'R' idx * need to be reclaimed. As result, some free space forms. If there is * enough free space (> low mark), wake the stack that feeds us. */ static void il_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx) { struct il_tx_queue *txq = &il->txq[txq_id]; struct il_queue *q = &txq->q; int nfreed = 0; if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, q->write_ptr, q->read_ptr); return; } for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { if (nfreed++ > 0) { IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx, q->write_ptr, q->read_ptr); queue_work(il->workqueue, &il->restart); } } } /** * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim * * If an Rx buffer has an async callback associated with it the callback * will be executed. The attached skb (if present) will only be freed * if the callback returns 1 */ void il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int idx = SEQ_TO_IDX(sequence); int cmd_idx; bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); struct il_device_cmd *cmd; struct il_cmd_meta *meta; struct il_tx_queue *txq = &il->txq[il->cmd_queue]; unsigned long flags; /* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced * in the queue management code. */ if (WARN (txq_id != il->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr, il->txq[il->cmd_queue].q.write_ptr)) { il_print_hex_error(il, pkt, 32); return; } cmd_idx = il_get_cmd_idx(&txq->q, idx, huge); cmd = txq->cmd[cmd_idx]; meta = &txq->meta[cmd_idx]; txq->time_stamp = jiffies; pci_unmap_single(il->pci_dev, dma_unmap_addr(meta, mapping), dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL); /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { meta->source->reply_page = (unsigned long)rxb_addr(rxb); rxb->page = NULL; } else if (meta->callback) meta->callback(il, cmd, pkt); spin_lock_irqsave(&il->hcmd_lock, flags); il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx); if (!(meta->flags & CMD_ASYNC)) { clear_bit(S_HCMD_ACTIVE, &il->status); D_INFO("Clearing HCMD_ACTIVE for command %s\n", il_get_cmd_string(cmd->hdr.cmd)); wake_up(&il->wait_command_queue); } /* Mark as unmapped */ meta->flags = 0; spin_unlock_irqrestore(&il->hcmd_lock, flags); } EXPORT_SYMBOL(il_tx_cmd_complete); MODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965"); MODULE_VERSION(IWLWIFI_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the * priority line in the PCIx). * set bt_coex_active to false, uCode will ignore the BT activity and * perform the normal operation * * User might experience transmit issue on some platform due to WiFi/BT * co-exist problem. The possible behaviors are: * Able to scan and finding all the available AP * Not able to associate with any AP * On those platforms, WiFi communication can be restored by set * "bt_coex_active" module parameter to "false" * * default: bt_coex_active = true (BT_COEX_ENABLE) */ static bool bt_coex_active = true; module_param(bt_coex_active, bool, S_IRUGO); MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); u32 il_debug_level; EXPORT_SYMBOL(il_debug_level); const u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; EXPORT_SYMBOL(il_bcast_addr); #define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ #define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ static void il_init_ht_hw_capab(const struct il_priv *il, struct ieee80211_sta_ht_cap *ht_info, enum ieee80211_band band) { u16 max_bit_rate = 0; u8 rx_chains_num = il->hw_params.rx_chains_num; u8 tx_chains_num = il->hw_params.tx_chains_num; ht_info->cap = 0; memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); ht_info->ht_supported = true; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; max_bit_rate = MAX_BIT_RATE_20_MHZ; if (il->hw_params.ht40_channel & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; ht_info->mcs.rx_mask[4] = 0x01; max_bit_rate = MAX_BIT_RATE_40_MHZ; } if (il->cfg->mod_params->amsdu_size_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; ht_info->mcs.rx_mask[0] = 0xFF; if (rx_chains_num >= 2) ht_info->mcs.rx_mask[1] = 0xFF; if (rx_chains_num >= 3) ht_info->mcs.rx_mask[2] = 0xFF; /* Highest supported Rx data rate */ max_bit_rate *= rx_chains_num; WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); /* Tx MCS capabilities */ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; if (tx_chains_num != rx_chains_num) { ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((tx_chains_num - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); } } /** * il_init_geos - Initialize mac80211's geo/channel info based from eeprom */ int il_init_geos(struct il_priv *il) { struct il_channel_info *ch; struct ieee80211_supported_band *sband; struct ieee80211_channel *channels; struct ieee80211_channel *geo_ch; struct ieee80211_rate *rates; int i = 0; s8 max_tx_power = 0; if (il->bands[IEEE80211_BAND_2GHZ].n_bitrates || il->bands[IEEE80211_BAND_5GHZ].n_bitrates) { D_INFO("Geography modes already initialized.\n"); set_bit(S_GEO_CONFIGURED, &il->status); return 0; } channels = kzalloc(sizeof(struct ieee80211_channel) * il->channel_count, GFP_KERNEL); if (!channels) return -ENOMEM; rates = kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY), GFP_KERNEL); if (!rates) { kfree(channels); return -ENOMEM; } /* 5.2GHz channels start after the 2.4GHz channels */ sband = &il->bands[IEEE80211_BAND_5GHZ]; sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)]; /* just OFDM */ sband->bitrates = &rates[IL_FIRST_OFDM_RATE]; sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE; if (il->cfg->sku & IL_SKU_N) il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_5GHZ); sband = &il->bands[IEEE80211_BAND_2GHZ]; sband->channels = channels; /* OFDM & CCK */ sband->bitrates = rates; sband->n_bitrates = RATE_COUNT_LEGACY; if (il->cfg->sku & IL_SKU_N) il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_2GHZ); il->ieee_channels = channels; il->ieee_rates = rates; for (i = 0; i < il->channel_count; i++) { ch = &il->channel_info[i]; if (!il_is_channel_valid(ch)) continue; sband = &il->bands[ch->band]; geo_ch = &sband->channels[sband->n_channels++]; geo_ch->center_freq = ieee80211_channel_to_frequency(ch->channel, ch->band); geo_ch->max_power = ch->max_power_avg; geo_ch->max_antenna_gain = 0xff; geo_ch->hw_value = ch->channel; if (il_is_channel_valid(ch)) { if (!(ch->flags & EEPROM_CHANNEL_IBSS)) geo_ch->flags |= IEEE80211_CHAN_NO_IBSS; if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN; if (ch->flags & EEPROM_CHANNEL_RADAR) geo_ch->flags |= IEEE80211_CHAN_RADAR; geo_ch->flags |= ch->ht40_extension_channel; if (ch->max_power_avg > max_tx_power) max_tx_power = ch->max_power_avg; } else { geo_ch->flags |= IEEE80211_CHAN_DISABLED; } D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, geo_ch->center_freq, il_is_channel_a_band(ch) ? "5.2" : "2.4", geo_ch-> flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid", geo_ch->flags); } il->tx_power_device_lmt = max_tx_power; il->tx_power_user_lmt = max_tx_power; il->tx_power_next = max_tx_power; if (il->bands[IEEE80211_BAND_5GHZ].n_channels == 0 && (il->cfg->sku & IL_SKU_A)) { IL_INFO("Incorrectly detected BG card as ABG. " "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n", il->pci_dev->device, il->pci_dev->subsystem_device); il->cfg->sku &= ~IL_SKU_A; } IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n", il->bands[IEEE80211_BAND_2GHZ].n_channels, il->bands[IEEE80211_BAND_5GHZ].n_channels); set_bit(S_GEO_CONFIGURED, &il->status); return 0; } EXPORT_SYMBOL(il_init_geos); /* * il_free_geos - undo allocations in il_init_geos */ void il_free_geos(struct il_priv *il) { kfree(il->ieee_channels); kfree(il->ieee_rates); clear_bit(S_GEO_CONFIGURED, &il->status); } EXPORT_SYMBOL(il_free_geos); static bool il_is_channel_extension(struct il_priv *il, enum ieee80211_band band, u16 channel, u8 extension_chan_offset) { const struct il_channel_info *ch_info; ch_info = il_get_channel_info(il, band, channel); if (!il_is_channel_valid(ch_info)) return false; if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) return !(ch_info-> ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS); else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) return !(ch_info-> ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); return false; } bool il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap) { if (!il->ht.enabled || !il->ht.is_40mhz) return false; /* * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 * the bit will not set if it is pure 40MHz case */ if (ht_cap && !ht_cap->ht_supported) return false; #ifdef CONFIG_IWLEGACY_DEBUGFS if (il->disable_ht40) return false; #endif return il_is_channel_extension(il, il->band, le16_to_cpu(il->staging.channel), il->ht.extension_chan_offset); } EXPORT_SYMBOL(il_is_ht40_tx_allowed); static u16 il_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) { u16 new_val; u16 beacon_factor; /* * If mac80211 hasn't given us a beacon interval, program * the default into the device. */ if (!beacon_val) return DEFAULT_BEACON_INTERVAL; /* * If the beacon interval we obtained from the peer * is too large, we'll have to wake up more often * (and in IBSS case, we'll beacon too much) * * For example, if max_beacon_val is 4096, and the * requested beacon interval is 7000, we'll have to * use 3500 to be able to wake up on the beacons. * * This could badly influence beacon detection stats. */ beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; new_val = beacon_val / beacon_factor; if (!new_val) new_val = max_beacon_val; return new_val; } int il_send_rxon_timing(struct il_priv *il) { u64 tsf; s32 interval_tm, rem; struct ieee80211_conf *conf = NULL; u16 beacon_int; struct ieee80211_vif *vif = il->vif; conf = &il->hw->conf; lockdep_assert_held(&il->mutex); memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd)); il->timing.timestamp = cpu_to_le64(il->timestamp); il->timing.listen_interval = cpu_to_le16(conf->listen_interval); beacon_int = vif ? vif->bss_conf.beacon_int : 0; /* * TODO: For IBSS we need to get atim_win from mac80211, * for now just always use 0 */ il->timing.atim_win = 0; beacon_int = il_adjust_beacon_interval(beacon_int, il->hw_params.max_beacon_itrvl * TIME_UNIT); il->timing.beacon_interval = cpu_to_le16(beacon_int); tsf = il->timestamp; /* tsf is modifed by do_div: copy it */ interval_tm = beacon_int * TIME_UNIT; rem = do_div(tsf, interval_tm); il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1; D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n", le16_to_cpu(il->timing.beacon_interval), le32_to_cpu(il->timing.beacon_init_val), le16_to_cpu(il->timing.atim_win)); return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing), &il->timing); } EXPORT_SYMBOL(il_send_rxon_timing); void il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt) { struct il_rxon_cmd *rxon = &il->staging; if (hw_decrypt) rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; else rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; } EXPORT_SYMBOL(il_set_rxon_hwcrypto); /* validate RXON structure is valid */ int il_check_rxon_cmd(struct il_priv *il) { struct il_rxon_cmd *rxon = &il->staging; bool error = false; if (rxon->flags & RXON_FLG_BAND_24G_MSK) { if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { IL_WARN("check 2.4G: wrong narrow\n"); error = true; } if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { IL_WARN("check 2.4G: wrong radar\n"); error = true; } } else { if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { IL_WARN("check 5.2G: not short slot!\n"); error = true; } if (rxon->flags & RXON_FLG_CCK_MSK) { IL_WARN("check 5.2G: CCK!\n"); error = true; } } if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { IL_WARN("mac/bssid mcast!\n"); error = true; } /* make sure basic rates 6Mbps and 1Mbps are supported */ if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 && (rxon->cck_basic_rates & RATE_1M_MASK) == 0) { IL_WARN("neither 1 nor 6 are basic\n"); error = true; } if (le16_to_cpu(rxon->assoc_id) > 2007) { IL_WARN("aid > 2007\n"); error = true; } if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { IL_WARN("CCK and short slot\n"); error = true; } if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { IL_WARN("CCK and auto detect"); error = true; } if ((rxon-> flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) == RXON_FLG_TGG_PROTECT_MSK) { IL_WARN("TGg but no auto-detect\n"); error = true; } if (error) IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel)); if (error) { IL_ERR("Invalid RXON\n"); return -EINVAL; } return 0; } EXPORT_SYMBOL(il_check_rxon_cmd); /** * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed * @il: staging_rxon is compared to active_rxon * * If the RXON structure is changing enough to require a new tune, * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. */ int il_full_rxon_required(struct il_priv *il) { const struct il_rxon_cmd *staging = &il->staging; const struct il_rxon_cmd *active = &il->active; #define CHK(cond) \ if ((cond)) { \ D_INFO("need full RXON - " #cond "\n"); \ return 1; \ } #define CHK_NEQ(c1, c2) \ if ((c1) != (c2)) { \ D_INFO("need full RXON - " \ #c1 " != " #c2 " - %d != %d\n", \ (c1), (c2)); \ return 1; \ } /* These items are only settable from the full RXON command */ CHK(!il_is_associated(il)); CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr)); CHK(!ether_addr_equal(staging->node_addr, active->node_addr)); CHK(!ether_addr_equal(staging->wlap_bssid_addr, active->wlap_bssid_addr)); CHK_NEQ(staging->dev_type, active->dev_type); CHK_NEQ(staging->channel, active->channel); CHK_NEQ(staging->air_propagation, active->air_propagation); CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, active->ofdm_ht_single_stream_basic_rates); CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, active->ofdm_ht_dual_stream_basic_rates); CHK_NEQ(staging->assoc_id, active->assoc_id); /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can * be updated with the RXON_ASSOC command -- however only some * flag transitions are allowed using RXON_ASSOC */ /* Check if we are not switching bands */ CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, active->flags & RXON_FLG_BAND_24G_MSK); /* Check if we are switching association toggle */ CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, active->filter_flags & RXON_FILTER_ASSOC_MSK); #undef CHK #undef CHK_NEQ return 0; } EXPORT_SYMBOL(il_full_rxon_required); u8 il_get_lowest_plcp(struct il_priv *il) { /* * Assign the lowest rate -- should really get this from * the beacon skb from mac80211. */ if (il->staging.flags & RXON_FLG_BAND_24G_MSK) return RATE_1M_PLCP; else return RATE_6M_PLCP; } EXPORT_SYMBOL(il_get_lowest_plcp); static void _il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) { struct il_rxon_cmd *rxon = &il->staging; if (!il->ht.enabled) { rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK | RXON_FLG_HT_PROT_MSK); return; } rxon->flags |= cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS); /* Set up channel bandwidth: * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ /* clear the HT channel mode before set the mode */ rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); if (il_is_ht40_tx_allowed(il, NULL)) { /* pure ht40 */ if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; /* Note: control channel is opposite of extension channel */ switch (il->ht.extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; } } else { /* Note: control channel is opposite of extension channel */ switch (il->ht.extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; break; case IEEE80211_HT_PARAM_CHA_SEC_NONE: default: /* channel location only valid if in Mixed mode */ IL_ERR("invalid extension channel offset\n"); break; } } } else { rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; } if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); D_ASSOC("rxon flags 0x%X operation mode :0x%X " "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags), il->ht.protection, il->ht.extension_chan_offset); } void il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) { _il_set_rxon_ht(il, ht_conf); } EXPORT_SYMBOL(il_set_rxon_ht); /* Return valid, unused, channel for a passive scan to reset the RF */ u8 il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band) { const struct il_channel_info *ch_info; int i; u8 channel = 0; u8 min, max; if (band == IEEE80211_BAND_5GHZ) { min = 14; max = il->channel_count; } else { min = 0; max = 14; } for (i = min; i < max; i++) { channel = il->channel_info[i].channel; if (channel == le16_to_cpu(il->staging.channel)) continue; ch_info = il_get_channel_info(il, band, channel); if (il_is_channel_valid(ch_info)) break; } return channel; } EXPORT_SYMBOL(il_get_single_channel_number); /** * il_set_rxon_channel - Set the band and channel values in staging RXON * @ch: requested channel as a pointer to struct ieee80211_channel * NOTE: Does not commit to the hardware; it sets appropriate bit fields * in the staging RXON flag structure based on the ch->band */ int il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch) { enum ieee80211_band band = ch->band; u16 channel = ch->hw_value; if (le16_to_cpu(il->staging.channel) == channel && il->band == band) return 0; il->staging.channel = cpu_to_le16(channel); if (band == IEEE80211_BAND_5GHZ) il->staging.flags &= ~RXON_FLG_BAND_24G_MSK; else il->staging.flags |= RXON_FLG_BAND_24G_MSK; il->band = band; D_INFO("Staging channel set to %d [%d]\n", channel, band); return 0; } EXPORT_SYMBOL(il_set_rxon_channel); void il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band, struct ieee80211_vif *vif) { if (band == IEEE80211_BAND_5GHZ) { il->staging.flags &= ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_CCK_MSK); il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; } else { /* Copied from il_post_associate() */ if (vif && vif->bss_conf.use_short_slot) il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; il->staging.flags |= RXON_FLG_BAND_24G_MSK; il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; il->staging.flags &= ~RXON_FLG_CCK_MSK; } } EXPORT_SYMBOL(il_set_flags_for_band); /* * initialize rxon structure with default values from eeprom */ void il_connection_init_rx_config(struct il_priv *il) { const struct il_channel_info *ch_info; memset(&il->staging, 0, sizeof(il->staging)); if (!il->vif) { il->staging.dev_type = RXON_DEV_TYPE_ESS; } else if (il->vif->type == NL80211_IFTYPE_STATION) { il->staging.dev_type = RXON_DEV_TYPE_ESS; il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; } else if (il->vif->type == NL80211_IFTYPE_ADHOC) { il->staging.dev_type = RXON_DEV_TYPE_IBSS; il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; il->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; } else { IL_ERR("Unsupported interface type %d\n", il->vif->type); return; } #if 0 /* TODO: Figure out when short_preamble would be set and cache from * that */ if (!hw_to_local(il->hw)->short_preamble) il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; else il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; #endif ch_info = il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel)); if (!ch_info) ch_info = &il->channel_info[0]; il->staging.channel = cpu_to_le16(ch_info->channel); il->band = ch_info->band; il_set_flags_for_band(il, il->band, il->vif); il->staging.ofdm_basic_rates = (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; il->staging.cck_basic_rates = (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; /* clear both MIX and PURE40 mode flag */ il->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); if (il->vif) memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN); il->staging.ofdm_ht_single_stream_basic_rates = 0xff; il->staging.ofdm_ht_dual_stream_basic_rates = 0xff; } EXPORT_SYMBOL(il_connection_init_rx_config); void il_set_rate(struct il_priv *il) { const struct ieee80211_supported_band *hw = NULL; struct ieee80211_rate *rate; int i; hw = il_get_hw_mode(il, il->band); if (!hw) { IL_ERR("Failed to set rate: unable to get hw mode\n"); return; } il->active_rate = 0; for (i = 0; i < hw->n_bitrates; i++) { rate = &(hw->bitrates[i]); if (rate->hw_value < RATE_COUNT_LEGACY) il->active_rate |= (1 << rate->hw_value); } D_RATE("Set active_rate = %0x\n", il->active_rate); il->staging.cck_basic_rates = (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; il->staging.ofdm_basic_rates = (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; } EXPORT_SYMBOL(il_set_rate); void il_chswitch_done(struct il_priv *il, bool is_success) { if (test_bit(S_EXIT_PENDING, &il->status)) return; if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) ieee80211_chswitch_done(il->vif, is_success); } EXPORT_SYMBOL(il_chswitch_done); void il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_csa_notification *csa = &(pkt->u.csa_notif); struct il_rxon_cmd *rxon = (void *)&il->active; if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) return; if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) { rxon->channel = csa->channel; il->staging.channel = csa->channel; D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel)); il_chswitch_done(il, true); } else { IL_ERR("CSA notif (fail) : channel %d\n", le16_to_cpu(csa->channel)); il_chswitch_done(il, false); } } EXPORT_SYMBOL(il_hdl_csa); #ifdef CONFIG_IWLEGACY_DEBUG void il_print_rx_config_cmd(struct il_priv *il) { struct il_rxon_cmd *rxon = &il->staging; D_RADIO("RX CONFIG:\n"); il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags)); D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates); D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr); D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr); D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); } EXPORT_SYMBOL(il_print_rx_config_cmd); #endif /** * il_irq_handle_error - called for HW or SW error interrupt from card */ void il_irq_handle_error(struct il_priv *il) { /* Set the FW error flag -- cleared on il_down */ set_bit(S_FW_ERROR, &il->status); /* Cancel currently queued command. */ clear_bit(S_HCMD_ACTIVE, &il->status); IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version); il->ops->dump_nic_error_log(il); if (il->ops->dump_fh) il->ops->dump_fh(il, NULL, false); #ifdef CONFIG_IWLEGACY_DEBUG if (il_get_debug_level(il) & IL_DL_FW_ERRORS) il_print_rx_config_cmd(il); #endif wake_up(&il->wait_command_queue); /* Keep the restart process from trying to send host * commands by clearing the INIT status bit */ clear_bit(S_READY, &il->status); if (!test_bit(S_EXIT_PENDING, &il->status)) { IL_DBG(IL_DL_FW_ERRORS, "Restarting adapter due to uCode error.\n"); if (il->cfg->mod_params->restart_fw) queue_work(il->workqueue, &il->restart); } } EXPORT_SYMBOL(il_irq_handle_error); static int _il_apm_stop_master(struct il_priv *il) { int ret = 0; /* stop device's busmaster DMA activity */ _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); ret = _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); if (ret < 0) IL_WARN("Master Disable Timed Out, 100 usec\n"); D_INFO("stop master\n"); return ret; } void _il_apm_stop(struct il_priv *il) { lockdep_assert_held(&il->reg_lock); D_INFO("Stop card, put in low power state\n"); /* Stop device's DMA activity */ _il_apm_stop_master(il); /* Reset the entire device */ _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); udelay(10); /* * Clear "initialization complete" bit to move adapter from * D0A* (powered-up Active) --> D0U* (Uninitialized) state. */ _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } EXPORT_SYMBOL(_il_apm_stop); void il_apm_stop(struct il_priv *il) { unsigned long flags; spin_lock_irqsave(&il->reg_lock, flags); _il_apm_stop(il); spin_unlock_irqrestore(&il->reg_lock, flags); } EXPORT_SYMBOL(il_apm_stop); /* * Start up NIC's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via il_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor */ int il_apm_init(struct il_priv *il) { int ret = 0; u16 lctl; D_INFO("Init card's basic functions\n"); /* * Use "set_bit" below rather than "write", to preserve any hardware * bits already set by default after reset. */ /* Disable L0S exit timer (platform NMI Work/Around) */ il_set_bit(il, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); /* * Disable L0s without affecting L1; * don't wait for ICH L0s (ICH bug W/A) */ il_set_bit(il, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); /* Set FH wait threshold to maximum (HW error during stress W/A) */ il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); /* * Enable HAP INTA (interrupt from management bus) to * wake device's PCI Express link L1a -> L0s * NOTE: This is no-op for 3945 (non-existent bit) */ il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); /* * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. * Check if BIOS (or OS) enabled L1-ASPM on this device. * If so (likely), disable L0S, so device moves directly L0->L1; * costs negligible amount of power savings. * If not (unlikely), enable L0S, so there is at least some * power savings, even without L1. */ if (il->cfg->set_l0s) { lctl = il_pcie_link_ctl(il); if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN) { /* L1-ASPM enabled; disable(!) L0S */ il_set_bit(il, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); D_POWER("L1 Enabled; Disabling L0S\n"); } else { /* L1-ASPM disabled; enable(!) L0S */ il_clear_bit(il, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); D_POWER("L1 Disabled; Enabling L0S\n"); } } /* Configure analog phase-lock-loop before activating to D0A */ if (il->cfg->pll_cfg_val) il_set_bit(il, CSR_ANA_PLL_CFG, il->cfg->pll_cfg_val); /* * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. */ il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); /* * Wait for clock stabilization; once stabilized, access to * device-internal resources is supported, e.g. il_wr_prph() * and accesses to uCode SRAM. */ ret = _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (ret < 0) { D_INFO("Failed to init the card\n"); goto out; } /* * Enable DMA and BSM (if used) clocks, wait for them to stabilize. * BSM (Boostrap State Machine) is only in 3945 and 4965. * * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits * do not disable clocks. This preserves any hardware bits already * set by default in "CLK_CTRL_REG" after reset. */ if (il->cfg->use_bsm) il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); else il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(20); /* Disable L1-Active */ il_set_bits_prph(il, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); out: return ret; } EXPORT_SYMBOL(il_apm_init); int il_set_tx_power(struct il_priv *il, s8 tx_power, bool force) { int ret; s8 prev_tx_power; bool defer; lockdep_assert_held(&il->mutex); if (il->tx_power_user_lmt == tx_power && !force) return 0; if (!il->ops->send_tx_power) return -EOPNOTSUPP; /* 0 dBm mean 1 milliwatt */ if (tx_power < 0) { IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power); return -EINVAL; } if (tx_power > il->tx_power_device_lmt) { IL_WARN("Requested user TXPOWER %d above upper limit %d.\n", tx_power, il->tx_power_device_lmt); return -EINVAL; } if (!il_is_ready_rf(il)) return -EIO; /* scan complete and commit_rxon use tx_power_next value, * it always need to be updated for newest request */ il->tx_power_next = tx_power; /* do not set tx power when scanning or channel changing */ defer = test_bit(S_SCANNING, &il->status) || memcmp(&il->active, &il->staging, sizeof(il->staging)); if (defer && !force) { D_INFO("Deferring tx power set\n"); return 0; } prev_tx_power = il->tx_power_user_lmt; il->tx_power_user_lmt = tx_power; ret = il->ops->send_tx_power(il); /* if fail to set tx_power, restore the orig. tx power */ if (ret) { il->tx_power_user_lmt = prev_tx_power; il->tx_power_next = prev_tx_power; } return ret; } EXPORT_SYMBOL(il_set_tx_power); void il_send_bt_config(struct il_priv *il) { struct il_bt_cmd bt_cmd = { .lead_time = BT_LEAD_TIME_DEF, .max_kill = BT_MAX_KILL_DEF, .kill_ack_mask = 0, .kill_cts_mask = 0, }; if (!bt_coex_active) bt_cmd.flags = BT_COEX_DISABLE; else bt_cmd.flags = BT_COEX_ENABLE; D_INFO("BT coex %s\n", (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd)) IL_ERR("failed to send BT Coex Config\n"); } EXPORT_SYMBOL(il_send_bt_config); int il_send_stats_request(struct il_priv *il, u8 flags, bool clear) { struct il_stats_cmd stats_cmd = { .configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0, }; if (flags & CMD_ASYNC) return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd), &stats_cmd, NULL); else return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd), &stats_cmd); } EXPORT_SYMBOL(il_send_stats_request); void il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb) { #ifdef CONFIG_IWLEGACY_DEBUG struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_sleep_notification *sleep = &(pkt->u.sleep_notif); D_RX("sleep mode: %d, src: %d\n", sleep->pm_sleep_mode, sleep->pm_wakeup_src); #endif } EXPORT_SYMBOL(il_hdl_pm_sleep); void il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len, il_get_cmd_string(pkt->hdr.cmd)); il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len); } EXPORT_SYMBOL(il_hdl_pm_debug_stats); void il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) " "seq 0x%04X ser 0x%08X\n", le32_to_cpu(pkt->u.err_resp.error_type), il_get_cmd_string(pkt->u.err_resp.cmd_id), pkt->u.err_resp.cmd_id, le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), le32_to_cpu(pkt->u.err_resp.error_info)); } EXPORT_SYMBOL(il_hdl_error); void il_clear_isr_stats(struct il_priv *il) { memset(&il->isr_stats, 0, sizeof(il->isr_stats)); } int il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct il_priv *il = hw->priv; unsigned long flags; int q; D_MAC80211("enter\n"); if (!il_is_ready_rf(il)) { D_MAC80211("leave - RF not ready\n"); return -EIO; } if (queue >= AC_NUM) { D_MAC80211("leave - queue >= AC_NUM %d\n", queue); return 0; } q = AC_NUM - 1 - queue; spin_lock_irqsave(&il->lock, flags); il->qos_data.def_qos_parm.ac[q].cw_min = cpu_to_le16(params->cw_min); il->qos_data.def_qos_parm.ac[q].cw_max = cpu_to_le16(params->cw_max); il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; il->qos_data.def_qos_parm.ac[q].edca_txop = cpu_to_le16((params->txop * 32)); il->qos_data.def_qos_parm.ac[q].reserved1 = 0; spin_unlock_irqrestore(&il->lock, flags); D_MAC80211("leave\n"); return 0; } EXPORT_SYMBOL(il_mac_conf_tx); int il_mac_tx_last_beacon(struct ieee80211_hw *hw) { struct il_priv *il = hw->priv; int ret; D_MAC80211("enter\n"); ret = (il->ibss_manager == IL_IBSS_MANAGER); D_MAC80211("leave ret %d\n", ret); return ret; } EXPORT_SYMBOL_GPL(il_mac_tx_last_beacon); static int il_set_mode(struct il_priv *il) { il_connection_init_rx_config(il); if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); return il_commit_rxon(il); } int il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct il_priv *il = hw->priv; int err; bool reset; mutex_lock(&il->mutex); D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); if (!il_is_ready_rf(il)) { IL_WARN("Try to add interface when device not ready\n"); err = -EINVAL; goto out; } /* * We do not support multiple virtual interfaces, but on hardware reset * we have to add the same interface again. */ reset = (il->vif == vif); if (il->vif && !reset) { err = -EOPNOTSUPP; goto out; } il->vif = vif; il->iw_mode = vif->type; err = il_set_mode(il); if (err) { IL_WARN("Fail to set mode %d\n", vif->type); if (!reset) { il->vif = NULL; il->iw_mode = NL80211_IFTYPE_STATION; } } out: D_MAC80211("leave err %d\n", err); mutex_unlock(&il->mutex); return err; } EXPORT_SYMBOL(il_mac_add_interface); static void il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif, bool mode_change) { lockdep_assert_held(&il->mutex); if (il->scan_vif == vif) { il_scan_cancel_timeout(il, 200); il_force_scan_end(il); } if (!mode_change) il_set_mode(il); } void il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct il_priv *il = hw->priv; mutex_lock(&il->mutex); D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); WARN_ON(il->vif != vif); il->vif = NULL; il_teardown_interface(il, vif, false); memset(il->bssid, 0, ETH_ALEN); D_MAC80211("leave\n"); mutex_unlock(&il->mutex); } EXPORT_SYMBOL(il_mac_remove_interface); int il_alloc_txq_mem(struct il_priv *il) { if (!il->txq) il->txq = kzalloc(sizeof(struct il_tx_queue) * il->cfg->num_of_queues, GFP_KERNEL); if (!il->txq) { IL_ERR("Not enough memory for txq\n"); return -ENOMEM; } return 0; } EXPORT_SYMBOL(il_alloc_txq_mem); void il_free_txq_mem(struct il_priv *il) { kfree(il->txq); il->txq = NULL; } EXPORT_SYMBOL(il_free_txq_mem); int il_force_reset(struct il_priv *il, bool external) { struct il_force_reset *force_reset; if (test_bit(S_EXIT_PENDING, &il->status)) return -EINVAL; force_reset = &il->force_reset; force_reset->reset_request_count++; if (!external) { if (force_reset->last_force_reset_jiffies && time_after(force_reset->last_force_reset_jiffies + force_reset->reset_duration, jiffies)) { D_INFO("force reset rejected\n"); force_reset->reset_reject_count++; return -EAGAIN; } } force_reset->reset_success_count++; force_reset->last_force_reset_jiffies = jiffies; /* * if the request is from external(ex: debugfs), * then always perform the request in regardless the module * parameter setting * if the request is from internal (uCode error or driver * detect failure), then fw_restart module parameter * need to be check before performing firmware reload */ if (!external && !il->cfg->mod_params->restart_fw) { D_INFO("Cancel firmware reload based on " "module parameter setting\n"); return 0; } IL_ERR("On demand firmware reload\n"); /* Set the FW error flag -- cleared on il_down */ set_bit(S_FW_ERROR, &il->status); wake_up(&il->wait_command_queue); /* * Keep the restart process from trying to send host * commands by clearing the INIT status bit */ clear_bit(S_READY, &il->status); queue_work(il->workqueue, &il->restart); return 0; } int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p) { struct il_priv *il = hw->priv; int err; mutex_lock(&il->mutex); D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n", vif->type, vif->addr, newtype, newp2p); if (newp2p) { err = -EOPNOTSUPP; goto out; } if (!il->vif || !il_is_ready_rf(il)) { /* * Huh? But wait ... this can maybe happen when * we're in the middle of a firmware restart! */ err = -EBUSY; goto out; } /* success */ il_teardown_interface(il, vif, true); vif->type = newtype; vif->p2p = false; err = il_set_mode(il); WARN_ON(err); /* * We've switched internally, but submitting to the * device may have failed for some reason. Mask this * error, because otherwise mac80211 will not switch * (and set the interface type back) and we'll be * out of sync with it. */ err = 0; out: D_MAC80211("leave err %d\n", err); mutex_unlock(&il->mutex); return err; } EXPORT_SYMBOL(il_mac_change_interface); /* * On every watchdog tick we check (latest) time stamp. If it does not * change during timeout period and queue is not empty we reset firmware. */ static int il_check_stuck_queue(struct il_priv *il, int cnt) { struct il_tx_queue *txq = &il->txq[cnt]; struct il_queue *q = &txq->q; unsigned long timeout; unsigned long now = jiffies; int ret; if (q->read_ptr == q->write_ptr) { txq->time_stamp = now; return 0; } timeout = txq->time_stamp + msecs_to_jiffies(il->cfg->wd_timeout); if (time_after(now, timeout)) { IL_ERR("Queue %d stuck for %u ms.\n", q->id, jiffies_to_msecs(now - txq->time_stamp)); ret = il_force_reset(il, false); return (ret == -EAGAIN) ? 0 : 1; } return 0; } /* * Making watchdog tick be a quarter of timeout assure we will * discover the queue hung between timeout and 1.25*timeout */ #define IL_WD_TICK(timeout) ((timeout) / 4) /* * Watchdog timer callback, we check each tx queue for stuck, if if hung * we reset the firmware. If everything is fine just rearm the timer. */ void il_bg_watchdog(unsigned long data) { struct il_priv *il = (struct il_priv *)data; int cnt; unsigned long timeout; if (test_bit(S_EXIT_PENDING, &il->status)) return; timeout = il->cfg->wd_timeout; if (timeout == 0) return; /* monitor and check for stuck cmd queue */ if (il_check_stuck_queue(il, il->cmd_queue)) return; /* monitor and check for other stuck queues */ for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { /* skip as we already checked the command queue */ if (cnt == il->cmd_queue) continue; if (il_check_stuck_queue(il, cnt)) return; } mod_timer(&il->watchdog, jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); } EXPORT_SYMBOL(il_bg_watchdog); void il_setup_watchdog(struct il_priv *il) { unsigned int timeout = il->cfg->wd_timeout; if (timeout) mod_timer(&il->watchdog, jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); else del_timer(&il->watchdog); } EXPORT_SYMBOL(il_setup_watchdog); /* * extended beacon time format * time in usec will be changed into a 32-bit value in extended:internal format * the extended part is the beacon counts * the internal part is the time in usec within one beacon interval */ u32 il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval) { u32 quot; u32 rem; u32 interval = beacon_interval * TIME_UNIT; if (!interval || !usec) return 0; quot = (usec / interval) & (il_beacon_time_mask_high(il, il->hw_params. beacon_time_tsf_bits) >> il-> hw_params.beacon_time_tsf_bits); rem = (usec % interval) & il_beacon_time_mask_low(il, il->hw_params. beacon_time_tsf_bits); return (quot << il->hw_params.beacon_time_tsf_bits) + rem; } EXPORT_SYMBOL(il_usecs_to_beacons); /* base is usually what we get from ucode with each received frame, * the same as HW timer counter counting down */ __le32 il_add_beacon_time(struct il_priv *il, u32 base, u32 addon, u32 beacon_interval) { u32 base_low = base & il_beacon_time_mask_low(il, il->hw_params. beacon_time_tsf_bits); u32 addon_low = addon & il_beacon_time_mask_low(il, il->hw_params. beacon_time_tsf_bits); u32 interval = beacon_interval * TIME_UNIT; u32 res = (base & il_beacon_time_mask_high(il, il->hw_params. beacon_time_tsf_bits)) + (addon & il_beacon_time_mask_high(il, il->hw_params. beacon_time_tsf_bits)); if (base_low > addon_low) res += base_low - addon_low; else if (base_low < addon_low) { res += interval + base_low - addon_low; res += (1 << il->hw_params.beacon_time_tsf_bits); } else res += (1 << il->hw_params.beacon_time_tsf_bits); return cpu_to_le32(res); } EXPORT_SYMBOL(il_add_beacon_time); #ifdef CONFIG_PM static int il_pci_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct il_priv *il = pci_get_drvdata(pdev); /* * This function is called when system goes into suspend state * mac80211 will call il_mac_stop() from the mac80211 suspend function * first but since il_mac_stop() has no knowledge of who the caller is, * it will not call apm_ops.stop() to stop the DMA operation. * Calling apm_ops.stop here to make sure we stop the DMA. */ il_apm_stop(il); return 0; } static int il_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct il_priv *il = pci_get_drvdata(pdev); bool hw_rfkill = false; /* * We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state. */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); il_enable_interrupts(il); if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) hw_rfkill = true; if (hw_rfkill) set_bit(S_RFKILL, &il->status); else clear_bit(S_RFKILL, &il->status); wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill); return 0; } compat_pci_suspend(il_pci_suspend) compat_pci_resume(il_pci_resume) SIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) EXPORT_SYMBOL(il_pm_ops); #else EXPORT_SYMBOL(il_pci_suspend_compat); EXPORT_SYMBOL(il_pci_resume_compat); #endif #endif /* CONFIG_PM */ static void il_update_qos(struct il_priv *il) { if (test_bit(S_EXIT_PENDING, &il->status)) return; il->qos_data.def_qos_parm.qos_flags = 0; if (il->qos_data.qos_active) il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_UPDATE_EDCA_MSK; if (il->ht.enabled) il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags); il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd), &il->qos_data.def_qos_parm, NULL); } /** * il_mac_config - mac80211 config callback */ int il_mac_config(struct ieee80211_hw *hw, u32 changed) { struct il_priv *il = hw->priv; const struct il_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = conf->channel; struct il_ht_config *ht_conf = &il->current_ht_config; unsigned long flags = 0; int ret = 0; u16 ch; int scan_active = 0; bool ht_changed = false; mutex_lock(&il->mutex); D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value, changed); if (unlikely(test_bit(S_SCANNING, &il->status))) { scan_active = 1; D_MAC80211("scan active\n"); } if (changed & (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { /* mac80211 uses static for non-HT which is what we want */ il->current_ht_config.smps = conf->smps_mode; /* * Recalculate chain counts. * * If monitor mode is enabled then mac80211 will * set up the SM PS mode to OFF if an HT channel is * configured. */ if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); } /* during scanning mac80211 will delay channel setting until * scan finish with changed = 0 */ if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { if (scan_active) goto set_ch_out; ch = channel->hw_value; ch_info = il_get_channel_info(il, channel->band, ch); if (!il_is_channel_valid(ch_info)) { D_MAC80211("leave - invalid channel\n"); ret = -EINVAL; goto set_ch_out; } if (il->iw_mode == NL80211_IFTYPE_ADHOC && !il_is_channel_ibss(ch_info)) { D_MAC80211("leave - not IBSS channel\n"); ret = -EINVAL; goto set_ch_out; } spin_lock_irqsave(&il->lock, flags); /* Configure HT40 channels */ if (il->ht.enabled != conf_is_ht(conf)) { il->ht.enabled = conf_is_ht(conf); ht_changed = true; } if (il->ht.enabled) { if (conf_is_ht40_minus(conf)) { il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; il->ht.is_40mhz = true; } else if (conf_is_ht40_plus(conf)) { il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; il->ht.is_40mhz = true; } else { il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; il->ht.is_40mhz = false; } } else il->ht.is_40mhz = false; /* * Default to no protection. Protection mode will * later be set from BSS config in il_ht_conf */ il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; /* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not * support ht */ if ((le16_to_cpu(il->staging.channel) != ch)) il->staging.flags = 0; il_set_rxon_channel(il, channel); il_set_rxon_ht(il, ht_conf); il_set_flags_for_band(il, channel->band, il->vif); spin_unlock_irqrestore(&il->lock, flags); if (il->ops->update_bcast_stations) ret = il->ops->update_bcast_stations(il); set_ch_out: /* The list of supported rates and rate mask can be different * for each band; since the band may have changed, reset * the rate mask to what mac80211 lists */ il_set_rate(il); } if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { ret = il_power_update_mode(il, false); if (ret) D_MAC80211("Error setting sleep level\n"); } if (changed & IEEE80211_CONF_CHANGE_POWER) { D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt, conf->power_level); il_set_tx_power(il, conf->power_level, false); } if (!il_is_ready(il)) { D_MAC80211("leave - not ready\n"); goto out; } if (scan_active) goto out; if (memcmp(&il->active, &il->staging, sizeof(il->staging))) il_commit_rxon(il); else D_INFO("Not re-sending same RXON configuration.\n"); if (ht_changed) il_update_qos(il); out: D_MAC80211("leave ret %d\n", ret); mutex_unlock(&il->mutex); return ret; } EXPORT_SYMBOL(il_mac_config); void il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct il_priv *il = hw->priv; unsigned long flags; mutex_lock(&il->mutex); D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); spin_lock_irqsave(&il->lock, flags); memset(&il->current_ht_config, 0, sizeof(struct il_ht_config)); /* new association get rid of ibss beacon skb */ if (il->beacon_skb) dev_kfree_skb(il->beacon_skb); il->beacon_skb = NULL; il->timestamp = 0; spin_unlock_irqrestore(&il->lock, flags); il_scan_cancel_timeout(il, 100); if (!il_is_ready_rf(il)) { D_MAC80211("leave - not ready\n"); mutex_unlock(&il->mutex); return; } /* we are restarting association process */ il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; il_commit_rxon(il); il_set_rate(il); D_MAC80211("leave\n"); mutex_unlock(&il->mutex); } EXPORT_SYMBOL(il_mac_reset_tsf); static void il_ht_conf(struct il_priv *il, struct ieee80211_vif *vif) { struct il_ht_config *ht_conf = &il->current_ht_config; struct ieee80211_sta *sta; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; D_ASSOC("enter:\n"); if (!il->ht.enabled) return; il->ht.protection = bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; il->ht.non_gf_sta_present = !!(bss_conf-> ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); ht_conf->single_chain_sufficient = false; switch (vif->type) { case NL80211_IFTYPE_STATION: rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); if (sta) { struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; int maxstreams; maxstreams = (ht_cap->mcs. tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; maxstreams += 1; if (ht_cap->mcs.rx_mask[1] == 0 && ht_cap->mcs.rx_mask[2] == 0) ht_conf->single_chain_sufficient = true; if (maxstreams <= 1) ht_conf->single_chain_sufficient = true; } else { /* * If at all, this can only happen through a race * when the AP disconnects us while we're still * setting up the connection, in that case mac80211 * will soon tell us about that. */ ht_conf->single_chain_sufficient = true; } rcu_read_unlock(); break; case NL80211_IFTYPE_ADHOC: ht_conf->single_chain_sufficient = true; break; default: break; } D_ASSOC("leave\n"); } static inline void il_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif) { /* * inform the ucode that there is no longer an * association and that no more packets should be * sent */ il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; il->staging.assoc_id = 0; il_commit_rxon(il); } static void il_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct il_priv *il = hw->priv; unsigned long flags; __le64 timestamp; struct sk_buff *skb = ieee80211_beacon_get(hw, vif); if (!skb) return; D_MAC80211("enter\n"); lockdep_assert_held(&il->mutex); if (!il->beacon_enabled) { IL_ERR("update beacon with no beaconing enabled\n"); dev_kfree_skb(skb); return; } spin_lock_irqsave(&il->lock, flags); if (il->beacon_skb) dev_kfree_skb(il->beacon_skb); il->beacon_skb = skb; timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; il->timestamp = le64_to_cpu(timestamp); D_MAC80211("leave\n"); spin_unlock_irqrestore(&il->lock, flags); if (!il_is_ready_rf(il)) { D_MAC80211("leave - RF not ready\n"); return; } il->ops->post_associate(il); } void il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct il_priv *il = hw->priv; int ret; mutex_lock(&il->mutex); D_MAC80211("enter: changes 0x%x\n", changes); if (!il_is_alive(il)) { D_MAC80211("leave - not alive\n"); mutex_unlock(&il->mutex); return; } if (changes & BSS_CHANGED_QOS) { unsigned long flags; spin_lock_irqsave(&il->lock, flags); il->qos_data.qos_active = bss_conf->qos; il_update_qos(il); spin_unlock_irqrestore(&il->lock, flags); } if (changes & BSS_CHANGED_BEACON_ENABLED) { /* FIXME: can we remove beacon_enabled ? */ if (vif->bss_conf.enable_beacon) il->beacon_enabled = true; else il->beacon_enabled = false; } if (changes & BSS_CHANGED_BSSID) { D_MAC80211("BSSID %pM\n", bss_conf->bssid); /* * If there is currently a HW scan going on in the background, * then we need to cancel it, otherwise sometimes we are not * able to authenticate (FIXME: why ?) */ if (il_scan_cancel_timeout(il, 100)) { D_MAC80211("leave - scan abort failed\n"); mutex_unlock(&il->mutex); return; } /* mac80211 only sets assoc when in STATION mode */ memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); /* FIXME: currently needed in a few places */ memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); } /* * This needs to be after setting the BSSID in case * mac80211 decides to do both changes at once because * it will invoke post_associate. */ if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON)) il_beacon_update(hw, vif); if (changes & BSS_CHANGED_ERP_PREAMBLE) { D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); if (bss_conf->use_short_preamble) il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; } if (changes & BSS_CHANGED_ERP_CTS_PROT) { D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot); if (bss_conf->use_cts_prot && il->band != IEEE80211_BAND_5GHZ) il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; else il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; if (bss_conf->use_cts_prot) il->staging.flags |= RXON_FLG_SELF_CTS_EN; else il->staging.flags &= ~RXON_FLG_SELF_CTS_EN; } if (changes & BSS_CHANGED_BASIC_RATES) { /* XXX use this information * * To do that, remove code from il_set_rate() and put something * like this here: * if (A-band) il->staging.ofdm_basic_rates = bss_conf->basic_rates; else il->staging.ofdm_basic_rates = bss_conf->basic_rates >> 4; il->staging.cck_basic_rates = bss_conf->basic_rates & 0xF; */ } if (changes & BSS_CHANGED_HT) { il_ht_conf(il, vif); if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); } if (changes & BSS_CHANGED_ASSOC) { D_MAC80211("ASSOC %d\n", bss_conf->assoc); if (bss_conf->assoc) { il->timestamp = bss_conf->sync_tsf; if (!il_is_rfkill(il)) il->ops->post_associate(il); } else il_set_no_assoc(il, vif); } if (changes && il_is_associated(il) && bss_conf->aid) { D_MAC80211("Changes (%#x) while associated\n", changes); ret = il_send_rxon_assoc(il); if (!ret) { /* Sync active_rxon with latest change. */ memcpy((void *)&il->active, &il->staging, sizeof(struct il_rxon_cmd)); } } if (changes & BSS_CHANGED_BEACON_ENABLED) { if (vif->bss_conf.enable_beacon) { memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); il->ops->config_ap(il); } else il_set_no_assoc(il, vif); } if (changes & BSS_CHANGED_IBSS) { ret = il->ops->manage_ibss_station(il, vif, bss_conf->ibss_joined); if (ret) IL_ERR("failed to %s IBSS station %pM\n", bss_conf->ibss_joined ? "add" : "remove", bss_conf->bssid); } D_MAC80211("leave\n"); mutex_unlock(&il->mutex); } EXPORT_SYMBOL(il_mac_bss_info_changed); irqreturn_t il_isr(int irq, void *data) { struct il_priv *il = data; u32 inta, inta_mask; u32 inta_fh; unsigned long flags; if (!il) return IRQ_NONE; spin_lock_irqsave(&il->lock, flags); /* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. * If we have something to service, the tasklet will re-enable ints. * If we *don't* have something, we'll re-enable before leaving here. */ inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */ _il_wr(il, CSR_INT_MASK, 0x00000000); /* Discover which interrupts are active/pending */ inta = _il_rd(il, CSR_INT); inta_fh = _il_rd(il, CSR_FH_INT_STATUS); /* Ignore interrupt if there's nothing in NIC to service. * This may be due to IRQ shared with another device, * or due to sporadic interrupts thrown from our NIC. */ if (!inta && !inta_fh) { D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); goto none; } if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) { /* Hardware disappeared. It might have already raised * an interrupt */ IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta); goto unplugged; } D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask, inta_fh); inta &= ~CSR_INT_BIT_SCD; /* il_irq_tasklet() will service interrupts and re-enable them */ if (likely(inta || inta_fh)) tasklet_schedule(&il->irq_tasklet); unplugged: spin_unlock_irqrestore(&il->lock, flags); return IRQ_HANDLED; none: /* re-enable interrupts here since we don't have anything to service. */ /* only Re-enable if disabled by irq */ if (test_bit(S_INT_ENABLED, &il->status)) il_enable_interrupts(il); spin_unlock_irqrestore(&il->lock, flags); return IRQ_NONE; } EXPORT_SYMBOL(il_isr); /* * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this * function. */ void il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, __le16 fc, __le32 *tx_flags) { if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { *tx_flags |= TX_CMD_FLG_RTS_MSK; *tx_flags &= ~TX_CMD_FLG_CTS_MSK; *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; if (!ieee80211_is_mgmt(fc)) return; switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { case cpu_to_le16(IEEE80211_STYPE_AUTH): case cpu_to_le16(IEEE80211_STYPE_DEAUTH): case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): *tx_flags &= ~TX_CMD_FLG_RTS_MSK; *tx_flags |= TX_CMD_FLG_CTS_MSK; break; } } else if (info->control.rates[0]. flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { *tx_flags &= ~TX_CMD_FLG_RTS_MSK; *tx_flags |= TX_CMD_FLG_CTS_MSK; *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; } } EXPORT_SYMBOL(il_tx_cmd_protection); compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/common.h0000644000175000017500000026064512026211315023743 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __il_core_h__ #define __il_core_h__ #include #include /* for struct pci_device_id */ #include #include #include #include #include #include #include "commands.h" #include "csr.h" #include "prph.h" struct il_host_cmd; struct il_cmd; struct il_tx_queue; #define IL_ERR(f, a...) dev_err(&il->pci_dev->dev, f, ## a) #define IL_WARN(f, a...) dev_warn(&il->pci_dev->dev, f, ## a) #define IL_INFO(f, a...) dev_info(&il->pci_dev->dev, f, ## a) #define RX_QUEUE_SIZE 256 #define RX_QUEUE_MASK 255 #define RX_QUEUE_SIZE_LOG 8 /* * RX related structures and functions */ #define RX_FREE_BUFFERS 64 #define RX_LOW_WATERMARK 8 #define U32_PAD(n) ((4-(n))&0x3) /* CT-KILL constants */ #define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ /* Default noise level to report when noise measurement is not available. * This may be because we're: * 1) Not associated (4965, no beacon stats being sent to driver) * 2) Scanning (noise measurement does not apply to associated channel) * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) * Use default noise value of -127 ... this is below the range of measurable * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. * Also, -127 works better than 0 when averaging frames with/without * noise info (e.g. averaging might be done in app); measured dBm values are * always negative ... using a negative value as the default keeps all * averages within an s8's (used in some apps) range of negative values. */ #define IL_NOISE_MEAS_NOT_AVAILABLE (-127) /* * RTS threshold here is total size [2347] minus 4 FCS bytes * Per spec: * a value of 0 means RTS on all data/management packets * a value > max MSDU size means no RTS * else RTS for data/management frames where MPDU is larger * than RTS value. */ #define DEFAULT_RTS_THRESHOLD 2347U #define MIN_RTS_THRESHOLD 0U #define MAX_RTS_THRESHOLD 2347U #define MAX_MSDU_SIZE 2304U #define MAX_MPDU_SIZE 2346U #define DEFAULT_BEACON_INTERVAL 100U #define DEFAULT_SHORT_RETRY_LIMIT 7U #define DEFAULT_LONG_RETRY_LIMIT 4U struct il_rx_buf { dma_addr_t page_dma; struct page *page; struct list_head list; }; #define rxb_addr(r) page_address(r->page) /* defined below */ struct il_device_cmd; struct il_cmd_meta { /* only for SYNC commands, iff the reply skb is wanted */ struct il_host_cmd *source; /* * only for ASYNC commands * (which is somewhat stupid -- look at common.c for instance * which duplicates a bunch of code because the callback isn't * invoked for SYNC commands, if it were and its result passed * through it would be simpler...) */ void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, struct il_rx_pkt *pkt); /* The CMD_SIZE_HUGE flag bit indicates that the command * structure is stored at the end of the shared queue memory. */ u32 flags; DEFINE_DMA_UNMAP_ADDR(mapping); DEFINE_DMA_UNMAP_LEN(len); }; /* * Generic queue structure * * Contains common data for Rx and Tx queues */ struct il_queue { int n_bd; /* number of BDs in this queue */ int write_ptr; /* 1-st empty entry (idx) host_w */ int read_ptr; /* last used entry (idx) host_r */ /* use for monitoring and recovering the stuck queue */ dma_addr_t dma_addr; /* physical addr for BD's */ int n_win; /* safe queue win */ u32 id; int low_mark; /* low watermark, resume queue if free * space more than this */ int high_mark; /* high watermark, stop queue if free * space less than this */ }; /** * struct il_tx_queue - Tx Queue for DMA * @q: generic Rx/Tx queue descriptor * @bd: base of circular buffer of TFDs * @cmd: array of command/TX buffer pointers * @meta: array of meta data for each command/tx buffer * @dma_addr_cmd: physical address of cmd/tx buffer array * @skbs: array of per-TFD socket buffer pointers * @time_stamp: time (in jiffies) of last read_ptr change * @need_update: indicates need to update read/write idx * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled * * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame * descriptors) and required locking structures. */ #define TFD_TX_CMD_SLOTS 256 #define TFD_CMD_SLOTS 32 struct il_tx_queue { struct il_queue q; void *tfds; struct il_device_cmd **cmd; struct il_cmd_meta *meta; struct sk_buff **skbs; unsigned long time_stamp; u8 need_update; u8 sched_retry; u8 active; u8 swq_id; }; /* * EEPROM access time values: * * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. */ #define IL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ #define IL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ #define IL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ /* * Regulatory channel usage flags in EEPROM struct il4965_eeprom_channel.flags. * * IBSS and/or AP operation is allowed *only* on those channels with * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because * RADAR detection is not supported by the 4965 driver, but is a * requirement for establishing a new network for legal operation on channels * requiring RADAR detection or restricting ACTIVE scanning. * * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. * It only indicates that 20 MHz channel use is supported; HT40 channel * usage is indicated by a separate set of regulatory flags for each * HT40 channel pair. * * NOTE: Using a channel inappropriately will result in a uCode error! */ #define IL_NUM_TX_CALIB_GROUPS 5 enum { EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */ EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */ /* Bit 2 Reserved */ EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */ EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */ EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */ /* Bit 6 Reserved (was Narrow Channel) */ EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ }; /* SKU Capabilities */ /* 3945 only */ #define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE (1 << 0) #define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1) /* *regulatory* channel data format in eeprom, one for each channel. * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ struct il_eeprom_channel { u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ } __packed; /* 3945 Specific */ #define EEPROM_3945_EEPROM_VERSION (0x2f) /* 4965 has two radio transmitters (and 3 radio receivers) */ #define EEPROM_TX_POWER_TX_CHAINS (2) /* 4965 has room for up to 8 sets of txpower calibration data */ #define EEPROM_TX_POWER_BANDS (8) /* 4965 factory calibration measures txpower gain settings for * each of 3 target output levels */ #define EEPROM_TX_POWER_MEASUREMENTS (3) /* 4965 Specific */ /* 4965 driver does not work with txpower calibration version < 5 */ #define EEPROM_4965_TX_POWER_VERSION (5) #define EEPROM_4965_EEPROM_VERSION (0x2f) #define EEPROM_4965_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */ #define EEPROM_4965_CALIB_TXPOWER_OFFSET (2*0xE8) /* 48 bytes */ #define EEPROM_4965_BOARD_REVISION (2*0x4F) /* 2 bytes */ #define EEPROM_4965_BOARD_PBA (2*0x56+1) /* 9 bytes */ /* 2.4 GHz */ extern const u8 il_eeprom_band_1[14]; /* * factory calibration data for one txpower level, on one channel, * measured on one of the 2 tx chains (radio transmitter and associated * antenna). EEPROM contains: * * 1) Temperature (degrees Celsius) of device when measurement was made. * * 2) Gain table idx used to achieve the target measurement power. * This refers to the "well-known" gain tables (see 4965.h). * * 3) Actual measured output power, in half-dBm ("34" = 17 dBm). * * 4) RF power amplifier detector level measurement (not used). */ struct il_eeprom_calib_measure { u8 temperature; /* Device temperature (Celsius) */ u8 gain_idx; /* Index into gain table */ u8 actual_pow; /* Measured RF output power, half-dBm */ s8 pa_det; /* Power amp detector level (not used) */ } __packed; /* * measurement set for one channel. EEPROM contains: * * 1) Channel number measured * * 2) Measurements for each of 3 power levels for each of 2 radio transmitters * (a.k.a. "tx chains") (6 measurements altogether) */ struct il_eeprom_calib_ch_info { u8 ch_num; struct il_eeprom_calib_measure measurements[EEPROM_TX_POWER_TX_CHAINS] [EEPROM_TX_POWER_MEASUREMENTS]; } __packed; /* * txpower subband info. * * For each frequency subband, EEPROM contains the following: * * 1) First and last channels within range of the subband. "0" values * indicate that this sample set is not being used. * * 2) Sample measurement sets for 2 channels close to the range endpoints. */ struct il_eeprom_calib_subband_info { u8 ch_from; /* channel number of lowest channel in subband */ u8 ch_to; /* channel number of highest channel in subband */ struct il_eeprom_calib_ch_info ch1; struct il_eeprom_calib_ch_info ch2; } __packed; /* * txpower calibration info. EEPROM contains: * * 1) Factory-measured saturation power levels (maximum levels at which * tx power amplifier can output a signal without too much distortion). * There is one level for 2.4 GHz band and one for 5 GHz band. These * values apply to all channels within each of the bands. * * 2) Factory-measured power supply voltage level. This is assumed to be * constant (i.e. same value applies to all channels/bands) while the * factory measurements are being made. * * 3) Up to 8 sets of factory-measured txpower calibration values. * These are for different frequency ranges, since txpower gain * characteristics of the analog radio circuitry vary with frequency. * * Not all sets need to be filled with data; * struct il_eeprom_calib_subband_info contains range of channels * (0 if unused) for each set of data. */ struct il_eeprom_calib_info { u8 saturation_power24; /* half-dBm (e.g. "34" = 17 dBm) */ u8 saturation_power52; /* half-dBm */ __le16 voltage; /* signed */ struct il_eeprom_calib_subband_info band_info[EEPROM_TX_POWER_BANDS]; } __packed; /* General */ #define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ #define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ #define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ #define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ #define EEPROM_VERSION (2*0x44) /* 2 bytes */ #define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ #define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ #define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */ #define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ #define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ /* The following masks are to be applied on EEPROM_RADIO_CONFIG */ #define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ #define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ #define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ #define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ #define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ #define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ #define EEPROM_3945_RF_CFG_TYPE_MAX 0x0 #define EEPROM_4965_RF_CFG_TYPE_MAX 0x1 /* * Per-channel regulatory data. * * Each channel that *might* be supported by iwl has a fixed location * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory * txpower (MSB). * * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) * channels (only for 4965, not supported by 3945) appear later in the EEPROM. * * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ #define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */ #define EEPROM_REGULATORY_BAND_1 (2*0x62) /* 2 bytes */ #define EEPROM_REGULATORY_BAND_1_CHANNELS (2*0x63) /* 28 bytes */ /* * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, * 5.0 GHz channels 7, 8, 11, 12, 16 * (4915-5080MHz) (none of these is ever supported) */ #define EEPROM_REGULATORY_BAND_2 (2*0x71) /* 2 bytes */ #define EEPROM_REGULATORY_BAND_2_CHANNELS (2*0x72) /* 26 bytes */ /* * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 * (5170-5320MHz) */ #define EEPROM_REGULATORY_BAND_3 (2*0x7F) /* 2 bytes */ #define EEPROM_REGULATORY_BAND_3_CHANNELS (2*0x80) /* 24 bytes */ /* * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 * (5500-5700MHz) */ #define EEPROM_REGULATORY_BAND_4 (2*0x8C) /* 2 bytes */ #define EEPROM_REGULATORY_BAND_4_CHANNELS (2*0x8D) /* 22 bytes */ /* * 5.7 GHz channels 145, 149, 153, 157, 161, 165 * (5725-5825MHz) */ #define EEPROM_REGULATORY_BAND_5 (2*0x98) /* 2 bytes */ #define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */ /* * 2.4 GHz HT40 channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) * * The channel listed is the center of the lower 20 MHz half of the channel. * The overall center frequency is actually 2 channels (10 MHz) above that, * and the upper half of each HT40 channel is centered 4 channels (20 MHz) away * from the lower half; e.g. the upper half of HT40 channel 1 is channel 5, * and the overall HT40 channel width centers on channel 3. * * NOTE: The RXON command uses 20 MHz channel numbers to specify the * control channel to which to tune. RXON also specifies whether the * control channel is the upper or lower half of a HT40 channel. * * NOTE: 4965 does not support HT40 channels on 2.4 GHz. */ #define EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS (2*0xA0) /* 14 bytes */ /* * 5.2 GHz HT40 channels 36 (40), 44 (48), 52 (56), 60 (64), * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161) */ #define EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS (2*0xA8) /* 22 bytes */ #define EEPROM_REGULATORY_BAND_NO_HT40 (0) int il_eeprom_init(struct il_priv *il); void il_eeprom_free(struct il_priv *il); const u8 *il_eeprom_query_addr(const struct il_priv *il, size_t offset); u16 il_eeprom_query16(const struct il_priv *il, size_t offset); int il_init_channel_map(struct il_priv *il); void il_free_channel_map(struct il_priv *il); const struct il_channel_info *il_get_channel_info(const struct il_priv *il, enum ieee80211_band band, u16 channel); #define IL_NUM_SCAN_RATES (2) struct il4965_channel_tgd_info { u8 type; s8 max_power; }; struct il4965_channel_tgh_info { s64 last_radar_time; }; #define IL4965_MAX_RATE (33) struct il3945_clip_group { /* maximum power level to prevent clipping for each rate, derived by * us from this band's saturation power in EEPROM */ const s8 clip_powers[IL_MAX_RATES]; }; /* current Tx power values to use, one for each rate for each channel. * requested power is limited by: * -- regulatory EEPROM limits for this channel * -- hardware capabilities (clip-powers) * -- spectrum management * -- user preference (e.g. iwconfig) * when requested power is set, base power idx must also be set. */ struct il3945_channel_power_info { struct il3945_tx_power tpc; /* actual radio and DSP gain settings */ s8 power_table_idx; /* actual (compenst'd) idx into gain table */ s8 base_power_idx; /* gain idx for power at factory temp. */ s8 requested_power; /* power (dBm) requested for this chnl/rate */ }; /* current scan Tx power values to use, one for each scan rate for each * channel. */ struct il3945_scan_power_info { struct il3945_tx_power tpc; /* actual radio and DSP gain settings */ s8 power_table_idx; /* actual (compenst'd) idx into gain table */ s8 requested_power; /* scan pwr (dBm) requested for chnl/rate */ }; /* * One for each channel, holds all channel setup data * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant * with one another! */ struct il_channel_info { struct il4965_channel_tgd_info tgd; struct il4965_channel_tgh_info tgh; struct il_eeprom_channel eeprom; /* EEPROM regulatory limit */ struct il_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for * HT40 channel */ u8 channel; /* channel number */ u8 flags; /* flags copied from EEPROM */ s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) limit */ s8 min_power; /* always 0 */ s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */ u8 group_idx; /* 0-4, maps channel to group1/2/3/4/5 */ u8 band_idx; /* 0-4, maps channel to band1/2/3/4/5 */ enum ieee80211_band band; /* HT40 channel info */ s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ u8 ht40_flags; /* flags copied from EEPROM */ u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ /* Radio/DSP gain settings for each "normal" data Tx rate. * These include, in addition to RF and DSP gain, a few fields for * remembering/modifying gain settings (idxes). */ struct il3945_channel_power_info power_info[IL4965_MAX_RATE]; /* Radio/DSP gain settings for each scan rate, for directed scans. */ struct il3945_scan_power_info scan_pwr_info[IL_NUM_SCAN_RATES]; }; #define IL_TX_FIFO_BK 0 /* shared */ #define IL_TX_FIFO_BE 1 #define IL_TX_FIFO_VI 2 /* shared */ #define IL_TX_FIFO_VO 3 #define IL_TX_FIFO_UNUSED -1 /* Minimum number of queues. MAX_NUM is defined in hw specific files. * Set the minimum to accommodate the 4 standard TX queues, 1 command * queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */ #define IL_MIN_NUM_QUEUES 10 #define IL_DEFAULT_CMD_QUEUE_NUM 4 #define IEEE80211_DATA_LEN 2304 #define IEEE80211_4ADDR_LEN 30 #define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) #define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) struct il_frame { union { struct ieee80211_hdr frame; struct il_tx_beacon_cmd beacon; u8 raw[IEEE80211_FRAME_LEN]; u8 cmd[360]; } u; struct list_head list; }; #define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) #define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) #define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) enum { CMD_SYNC = 0, CMD_SIZE_NORMAL = 0, CMD_NO_SKB = 0, CMD_SIZE_HUGE = (1 << 0), CMD_ASYNC = (1 << 1), CMD_WANT_SKB = (1 << 2), CMD_MAPPED = (1 << 3), }; #define DEF_CMD_PAYLOAD_SIZE 320 /** * struct il_device_cmd * * For allocation of the command and tx queues, this establishes the overall * size of the largest command we send to uCode, except for a scan command * (which is relatively huge; space is allocated separately). */ struct il_device_cmd { struct il_cmd_header hdr; /* uCode API */ union { u32 flags; u8 val8; u16 val16; u32 val32; struct il_tx_cmd tx; u8 payload[DEF_CMD_PAYLOAD_SIZE]; } __packed cmd; } __packed; #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct il_device_cmd)) struct il_host_cmd { const void *data; unsigned long reply_page; void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, struct il_rx_pkt *pkt); u32 flags; u16 len; u8 id; }; #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 /** * struct il_rx_queue - Rx queue * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) * @read: Shared idx to newest available Rx buffer * @write: Shared idx to oldest written Rx packet * @free_count: Number of pre-allocated buffers in rx_free * @rx_free: list of free SKBs for use * @rx_used: List of Rx buffers with no SKB * @need_update: flag to indicate we need to update read/write idx * @rb_stts: driver's pointer to receive buffer status * @rb_stts_dma: bus address of receive buffer status * * NOTE: rx_free and rx_used are used as a FIFO for il_rx_bufs */ struct il_rx_queue { __le32 *bd; dma_addr_t bd_dma; struct il_rx_buf pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; struct il_rx_buf *queue[RX_QUEUE_SIZE]; u32 read; u32 write; u32 free_count; u32 write_actual; struct list_head rx_free; struct list_head rx_used; int need_update; struct il_rb_status *rb_stts; dma_addr_t rb_stts_dma; spinlock_t lock; }; #define IL_SUPPORTED_RATES_IE_LEN 8 #define MAX_TID_COUNT 9 #define IL_INVALID_RATE 0xFF #define IL_INVALID_VALUE -1 /** * struct il_ht_agg -- aggregation status while waiting for block-ack * @txq_id: Tx queue used for Tx attempt * @frame_count: # frames attempted by Tx command * @wait_for_ba: Expect block-ack before next Tx reply * @start_idx: Index of 1st Transmit Frame Descriptor (TFD) in Tx win * @bitmap0: Low order bitmap, one bit for each frame pending ACK in Tx win * @bitmap1: High order, one bit for each frame pending ACK in Tx win * @rate_n_flags: Rate at which Tx was attempted * * If C_TX indicates that aggregation was attempted, driver must wait * for block ack (N_COMPRESSED_BA). This struct stores tx reply info * until block ack arrives. */ struct il_ht_agg { u16 txq_id; u16 frame_count; u16 wait_for_ba; u16 start_idx; u64 bitmap; u32 rate_n_flags; #define IL_AGG_OFF 0 #define IL_AGG_ON 1 #define IL_EMPTYING_HW_QUEUE_ADDBA 2 #define IL_EMPTYING_HW_QUEUE_DELBA 3 u8 state; }; struct il_tid_data { u16 seq_number; /* 4965 only */ u16 tfds_in_queue; struct il_ht_agg agg; }; struct il_hw_key { u32 cipher; int keylen; u8 keyidx; u8 key[32]; }; union il_ht_rate_supp { u16 rates; struct { u8 siso_rate; u8 mimo_rate; }; }; #define CFG_HT_RX_AMPDU_FACTOR_8K (0x0) #define CFG_HT_RX_AMPDU_FACTOR_16K (0x1) #define CFG_HT_RX_AMPDU_FACTOR_32K (0x2) #define CFG_HT_RX_AMPDU_FACTOR_64K (0x3) #define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K #define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K #define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K /* * Maximal MPDU density for TX aggregation * 4 - 2us density * 5 - 4us density * 6 - 8us density * 7 - 16us density */ #define CFG_HT_MPDU_DENSITY_2USEC (0x4) #define CFG_HT_MPDU_DENSITY_4USEC (0x5) #define CFG_HT_MPDU_DENSITY_8USEC (0x6) #define CFG_HT_MPDU_DENSITY_16USEC (0x7) #define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC #define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC #define CFG_HT_MPDU_DENSITY_MIN (0x1) struct il_ht_config { bool single_chain_sufficient; enum ieee80211_smps_mode smps; /* current smps mode */ }; /* QoS structures */ struct il_qos_info { int qos_active; struct il_qosparam_cmd def_qos_parm; }; /* * Structure should be accessed with sta_lock held. When station addition * is in progress (IL_STA_UCODE_INPROGRESS) it is possible to access only * the commands (il_addsta_cmd and il_link_quality_cmd) without * sta_lock held. */ struct il_station_entry { struct il_addsta_cmd sta; struct il_tid_data tid[MAX_TID_COUNT]; u8 used; struct il_hw_key keyinfo; struct il_link_quality_cmd *lq; }; struct il_station_priv_common { u8 sta_id; }; /** * struct il_vif_priv - driver's ilate per-interface information * * When mac80211 allocates a virtual interface, it can allocate * space for us to put data into. */ struct il_vif_priv { u8 ibss_bssid_sta_id; }; /* one for each uCode image (inst/data, boot/init/runtime) */ struct fw_desc { void *v_addr; /* access by driver */ dma_addr_t p_addr; /* access by card's busmaster DMA */ u32 len; /* bytes */ }; /* uCode file layout */ struct il_ucode_header { __le32 ver; /* major/minor/API/serial */ struct { __le32 inst_size; /* bytes of runtime code */ __le32 data_size; /* bytes of runtime data */ __le32 init_size; /* bytes of init code */ __le32 init_data_size; /* bytes of init data */ __le32 boot_size; /* bytes of bootstrap code */ u8 data[0]; /* in same order as sizes */ } v1; }; struct il4965_ibss_seq { u8 mac[ETH_ALEN]; u16 seq_num; u16 frag_num; unsigned long packet_time; struct list_head list; }; struct il_sensitivity_ranges { u16 min_nrg_cck; u16 max_nrg_cck; u16 nrg_th_cck; u16 nrg_th_ofdm; u16 auto_corr_min_ofdm; u16 auto_corr_min_ofdm_mrc; u16 auto_corr_min_ofdm_x1; u16 auto_corr_min_ofdm_mrc_x1; u16 auto_corr_max_ofdm; u16 auto_corr_max_ofdm_mrc; u16 auto_corr_max_ofdm_x1; u16 auto_corr_max_ofdm_mrc_x1; u16 auto_corr_max_cck; u16 auto_corr_max_cck_mrc; u16 auto_corr_min_cck; u16 auto_corr_min_cck_mrc; u16 barker_corr_th_min; u16 barker_corr_th_min_mrc; u16 nrg_th_cca; }; #define KELVIN_TO_CELSIUS(x) ((x)-273) #define CELSIUS_TO_KELVIN(x) ((x)+273) /** * struct il_hw_params * @bcast_id: f/w broadcast station ID * @max_txq_num: Max # Tx queues supported * @dma_chnl_num: Number of Tx DMA/FIFO channels * @scd_bc_tbls_size: size of scheduler byte count tables * @tfd_size: TFD size * @tx/rx_chains_num: Number of TX/RX chains * @valid_tx/rx_ant: usable antennas * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2) * @max_rxq_log: Log-base-2 of max_rxq_size * @rx_page_order: Rx buffer page order * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR * @max_stations: * @ht40_channel: is 40MHz width possible in band 2.4 * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ) * @sw_crypto: 0 for hw, 1 for sw * @max_xxx_size: for ucode uses * @ct_kill_threshold: temperature threshold * @beacon_time_tsf_bits: number of valid tsf bits for beacon time * @struct il_sensitivity_ranges: range of sensitivity values */ struct il_hw_params { u8 bcast_id; u8 max_txq_num; u8 dma_chnl_num; u16 scd_bc_tbls_size; u32 tfd_size; u8 tx_chains_num; u8 rx_chains_num; u8 valid_tx_ant; u8 valid_rx_ant; u16 max_rxq_size; u16 max_rxq_log; u32 rx_page_order; u32 rx_wrt_ptr_reg; u8 max_stations; u8 ht40_channel; u8 max_beacon_itrvl; /* in 1024 ms */ u32 max_inst_size; u32 max_data_size; u32 max_bsm_size; u32 ct_kill_threshold; /* value in hw-dependent units */ u16 beacon_time_tsf_bits; const struct il_sensitivity_ranges *sens; }; /****************************************************************************** * * Functions implemented in core module which are forward declared here * for use by iwl-[4-5].c * * NOTE: The implementation of these functions are not hardware specific * which is why they are in the core module files. * * Naming convention -- * il_ <-- Is part of iwlwifi * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) * il4965_bg_ <-- Called from work queue context * il4965_mac_ <-- mac80211 callback * ****************************************************************************/ extern void il4965_update_chain_flags(struct il_priv *il); extern const u8 il_bcast_addr[ETH_ALEN]; extern int il_queue_space(const struct il_queue *q); static inline int il_queue_used(const struct il_queue *q, int i) { return q->write_ptr >= q->read_ptr ? (i >= q->read_ptr && i < q->write_ptr) : !(i < q->read_ptr && i >= q-> write_ptr); } static inline u8 il_get_cmd_idx(struct il_queue *q, u32 idx, int is_huge) { /* * This is for init calibration result and scan command which * required buffer > TFD_MAX_PAYLOAD_SIZE, * the big buffer at end of command array */ if (is_huge) return q->n_win; /* must be power of 2 */ /* Otherwise, use normal size buffers */ return idx & (q->n_win - 1); } struct il_dma_ptr { dma_addr_t dma; void *addr; size_t size; }; #define IL_OPERATION_MODE_AUTO 0 #define IL_OPERATION_MODE_HT_ONLY 1 #define IL_OPERATION_MODE_MIXED 2 #define IL_OPERATION_MODE_20MHZ 3 #define IL_TX_CRC_SIZE 4 #define IL_TX_DELIMITER_SIZE 4 #define TX_POWER_IL_ILLEGAL_VOLTAGE -10000 /* Sensitivity and chain noise calibration */ #define INITIALIZATION_VALUE 0xFFFF #define IL4965_CAL_NUM_BEACONS 20 #define IL_CAL_NUM_BEACONS 16 #define MAXIMUM_ALLOWED_PATHLOSS 15 #define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 #define MAX_FA_OFDM 50 #define MIN_FA_OFDM 5 #define MAX_FA_CCK 50 #define MIN_FA_CCK 5 #define AUTO_CORR_STEP_OFDM 1 #define AUTO_CORR_STEP_CCK 3 #define AUTO_CORR_MAX_TH_CCK 160 #define NRG_DIFF 2 #define NRG_STEP_CCK 2 #define NRG_MARGIN 8 #define MAX_NUMBER_CCK_NO_FA 100 #define AUTO_CORR_CCK_MIN_VAL_DEF (125) #define CHAIN_A 0 #define CHAIN_B 1 #define CHAIN_C 2 #define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 #define ALL_BAND_FILTER 0xFF00 #define IN_BAND_FILTER 0xFF #define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF #define NRG_NUM_PREV_STAT_L 20 #define NUM_RX_CHAINS 3 enum il4965_false_alarm_state { IL_FA_TOO_MANY = 0, IL_FA_TOO_FEW = 1, IL_FA_GOOD_RANGE = 2, }; enum il4965_chain_noise_state { IL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ IL_CHAIN_NOISE_ACCUMULATE, IL_CHAIN_NOISE_CALIBRATED, IL_CHAIN_NOISE_DONE, }; enum ucode_type { UCODE_NONE = 0, UCODE_INIT, UCODE_RT }; /* Sensitivity calib data */ struct il_sensitivity_data { u32 auto_corr_ofdm; u32 auto_corr_ofdm_mrc; u32 auto_corr_ofdm_x1; u32 auto_corr_ofdm_mrc_x1; u32 auto_corr_cck; u32 auto_corr_cck_mrc; u32 last_bad_plcp_cnt_ofdm; u32 last_fa_cnt_ofdm; u32 last_bad_plcp_cnt_cck; u32 last_fa_cnt_cck; u32 nrg_curr_state; u32 nrg_prev_state; u32 nrg_value[10]; u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; u32 nrg_silence_ref; u32 nrg_energy_idx; u32 nrg_silence_idx; u32 nrg_th_cck; s32 nrg_auto_corr_silence_diff; u32 num_in_cck_no_fa; u32 nrg_th_ofdm; u16 barker_corr_th_min; u16 barker_corr_th_min_mrc; u16 nrg_th_cca; }; /* Chain noise (differential Rx gain) calib data */ struct il_chain_noise_data { u32 active_chains; u32 chain_noise_a; u32 chain_noise_b; u32 chain_noise_c; u32 chain_signal_a; u32 chain_signal_b; u32 chain_signal_c; u16 beacon_count; u8 disconn_array[NUM_RX_CHAINS]; u8 delta_gain_code[NUM_RX_CHAINS]; u8 radio_write; u8 state; }; #define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ #define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ #define IL_TRAFFIC_ENTRIES (256) #define IL_TRAFFIC_ENTRY_SIZE (64) enum { MEASUREMENT_READY = (1 << 0), MEASUREMENT_ACTIVE = (1 << 1), }; /* interrupt stats */ struct isr_stats { u32 hw; u32 sw; u32 err_code; u32 sch; u32 alive; u32 rfkill; u32 ctkill; u32 wakeup; u32 rx; u32 handlers[IL_CN_MAX]; u32 tx; u32 unhandled; }; /* management stats */ enum il_mgmt_stats { MANAGEMENT_ASSOC_REQ = 0, MANAGEMENT_ASSOC_RESP, MANAGEMENT_REASSOC_REQ, MANAGEMENT_REASSOC_RESP, MANAGEMENT_PROBE_REQ, MANAGEMENT_PROBE_RESP, MANAGEMENT_BEACON, MANAGEMENT_ATIM, MANAGEMENT_DISASSOC, MANAGEMENT_AUTH, MANAGEMENT_DEAUTH, MANAGEMENT_ACTION, MANAGEMENT_MAX, }; /* control stats */ enum il_ctrl_stats { CONTROL_BACK_REQ = 0, CONTROL_BACK, CONTROL_PSPOLL, CONTROL_RTS, CONTROL_CTS, CONTROL_ACK, CONTROL_CFEND, CONTROL_CFENDACK, CONTROL_MAX, }; struct traffic_stats { #ifdef CONFIG_IWLEGACY_DEBUGFS u32 mgmt[MANAGEMENT_MAX]; u32 ctrl[CONTROL_MAX]; u32 data_cnt; u64 data_bytes; #endif }; /* * host interrupt timeout value * used with setting interrupt coalescing timer * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit * * default interrupt coalescing timer is 64 x 32 = 2048 usecs * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs */ #define IL_HOST_INT_TIMEOUT_MAX (0xFF) #define IL_HOST_INT_TIMEOUT_DEF (0x40) #define IL_HOST_INT_TIMEOUT_MIN (0x0) #define IL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) #define IL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) #define IL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) #define IL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) /* TX queue watchdog timeouts in mSecs */ #define IL_DEF_WD_TIMEOUT (2000) #define IL_LONG_WD_TIMEOUT (10000) #define IL_MAX_WD_TIMEOUT (120000) struct il_force_reset { int reset_request_count; int reset_success_count; int reset_reject_count; unsigned long reset_duration; unsigned long last_force_reset_jiffies; }; /* extend beacon time format bit shifting */ /* * for _3945 devices * bits 31:24 - extended * bits 23:0 - interval */ #define IL3945_EXT_BEACON_TIME_POS 24 /* * for _4965 devices * bits 31:22 - extended * bits 21:0 - interval */ #define IL4965_EXT_BEACON_TIME_POS 22 struct il_rxon_context { struct ieee80211_vif *vif; }; struct il_power_mgr { struct il_powertable_cmd sleep_cmd; struct il_powertable_cmd sleep_cmd_next; int debug_sleep_level_override; bool pci_pm; }; struct il_priv { struct ieee80211_hw *hw; struct ieee80211_channel *ieee_channels; struct ieee80211_rate *ieee_rates; struct il_cfg *cfg; const struct il_ops *ops; #ifdef CONFIG_IWLEGACY_DEBUGFS const struct il_debugfs_ops *debugfs_ops; #endif /* temporary frame storage list */ struct list_head free_frames; int frames_count; enum ieee80211_band band; int alloc_rxb_page; void (*handlers[IL_CN_MAX]) (struct il_priv *il, struct il_rx_buf *rxb); struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; /* spectrum measurement report caching */ struct il_spectrum_notification measure_report; u8 measurement_status; /* ucode beacon time */ u32 ucode_beacon_time; int missed_beacon_threshold; /* track IBSS manager (last beacon) status */ u32 ibss_manager; /* force reset */ struct il_force_reset force_reset; /* we allocate array of il_channel_info for NIC's valid channels. * Access via channel # using indirect idx array */ struct il_channel_info *channel_info; /* channel info array */ u8 channel_count; /* # of channels */ /* thermal calibration */ s32 temperature; /* degrees Kelvin */ s32 last_temperature; /* Scan related variables */ unsigned long scan_start; unsigned long scan_start_tsf; void *scan_cmd; enum ieee80211_band scan_band; struct cfg80211_scan_request *scan_request; struct ieee80211_vif *scan_vif; u8 scan_tx_ant[IEEE80211_NUM_BANDS]; u8 mgmt_tx_ant; /* spinlock */ spinlock_t lock; /* protect general shared data */ spinlock_t hcmd_lock; /* protect hcmd */ spinlock_t reg_lock; /* protect hw register access */ struct mutex mutex; /* basic pci-network driver stuff */ struct pci_dev *pci_dev; /* pci hardware address support */ void __iomem *hw_base; u32 hw_rev; u32 hw_wa_rev; u8 rev_id; /* command queue number */ u8 cmd_queue; /* max number of station keys */ u8 sta_key_max_num; /* EEPROM MAC addresses */ struct mac_address addresses[1]; /* uCode images, save to reload in case of failure */ int fw_idx; /* firmware we're trying to load */ u32 ucode_ver; /* version of ucode, copy of il_ucode.ver */ struct fw_desc ucode_code; /* runtime inst */ struct fw_desc ucode_data; /* runtime data original */ struct fw_desc ucode_data_backup; /* runtime data save/restore */ struct fw_desc ucode_init; /* initialization inst */ struct fw_desc ucode_init_data; /* initialization data */ struct fw_desc ucode_boot; /* bootstrap inst */ enum ucode_type ucode_type; u8 ucode_write_complete; /* the image write is complete */ char firmware_name[25]; struct ieee80211_vif *vif; struct il_qos_info qos_data; struct { bool enabled; bool is_40mhz; bool non_gf_sta_present; u8 protection; u8 extension_chan_offset; } ht; /* * We declare this const so it can only be * changed via explicit cast within the * routines that actually update the physical * hardware. */ const struct il_rxon_cmd active; struct il_rxon_cmd staging; struct il_rxon_time_cmd timing; __le16 switch_channel; /* 1st responses from initialize and runtime uCode images. * _4965's initialize alive response contains some calibration data. */ struct il_init_alive_resp card_alive_init; struct il_alive_resp card_alive; u16 active_rate; u8 start_calib; struct il_sensitivity_data sensitivity_data; struct il_chain_noise_data chain_noise_data; __le16 sensitivity_tbl[HD_TBL_SIZE]; struct il_ht_config current_ht_config; /* Rate scaling data */ u8 retry_rate; wait_queue_head_t wait_command_queue; int activity_timer_active; /* Rx and Tx DMA processing queues */ struct il_rx_queue rxq; struct il_tx_queue *txq; unsigned long txq_ctx_active_msk; struct il_dma_ptr kw; /* keep warm address */ struct il_dma_ptr scd_bc_tbls; u32 scd_base_addr; /* scheduler sram base address */ unsigned long status; /* counts mgmt, ctl, and data packets */ struct traffic_stats tx_stats; struct traffic_stats rx_stats; /* counts interrupts */ struct isr_stats isr_stats; struct il_power_mgr power_data; /* context information */ u8 bssid[ETH_ALEN]; /* used only on 3945 but filled by core */ /* station table variables */ /* Note: if lock and sta_lock are needed, lock must be acquired first */ spinlock_t sta_lock; int num_stations; struct il_station_entry stations[IL_STATION_COUNT]; unsigned long ucode_key_table; /* queue refcounts */ #define IL_MAX_HW_QUEUES 32 unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)]; /* for each AC */ atomic_t queue_stop_count[4]; /* Indication if ieee80211_ops->open has been called */ u8 is_open; u8 mac80211_registered; /* eeprom -- this is in the card's little endian byte order */ u8 *eeprom; struct il_eeprom_calib_info *calib_info; enum nl80211_iftype iw_mode; /* Last Rx'd beacon timestamp */ u64 timestamp; union { #if defined(CONFIG_IWL3945) || defined(CONFIG_IWL3945_MODULE) struct { void *shared_virt; dma_addr_t shared_phys; struct delayed_work thermal_periodic; struct delayed_work rfkill_poll; struct il3945_notif_stats stats; #ifdef CONFIG_IWLEGACY_DEBUGFS struct il3945_notif_stats accum_stats; struct il3945_notif_stats delta_stats; struct il3945_notif_stats max_delta; #endif u32 sta_supp_rates; int last_rx_rssi; /* From Rx packet stats */ /* Rx'd packet timing information */ u32 last_beacon_time; u64 last_tsf; /* * each calibration channel group in the * EEPROM has a derived clip setting for * each rate. */ const struct il3945_clip_group clip_groups[5]; } _3945; #endif #if defined(CONFIG_COMPAT_IWL4965) || defined(CONFIG_COMPAT_IWL4965_MODULE) struct { struct il_rx_phy_res last_phy_res; bool last_phy_res_valid; struct completion firmware_loading_complete; /* * chain noise reset and gain commands are the * two extra calibration commands follows the standard * phy calibration commands */ u8 phy_calib_chain_noise_reset_cmd; u8 phy_calib_chain_noise_gain_cmd; u8 key_mapping_keys; struct il_wep_key wep_keys[WEP_KEYS_MAX]; struct il_notif_stats stats; #ifdef CONFIG_IWLEGACY_DEBUGFS struct il_notif_stats accum_stats; struct il_notif_stats delta_stats; struct il_notif_stats max_delta; #endif } _4965; #endif }; struct il_hw_params hw_params; u32 inta_mask; struct workqueue_struct *workqueue; struct work_struct restart; struct work_struct scan_completed; struct work_struct rx_replenish; struct work_struct abort_scan; bool beacon_enabled; struct sk_buff *beacon_skb; struct work_struct tx_flush; struct tasklet_struct irq_tasklet; struct delayed_work init_alive_start; struct delayed_work alive_start; struct delayed_work scan_check; /* TX Power */ s8 tx_power_user_lmt; s8 tx_power_device_lmt; s8 tx_power_next; #ifdef CONFIG_IWLEGACY_DEBUG /* debugging info */ u32 debug_level; /* per device debugging will override global il_debug_level if set */ #endif /* CONFIG_IWLEGACY_DEBUG */ #ifdef CONFIG_IWLEGACY_DEBUGFS /* debugfs */ u16 tx_traffic_idx; u16 rx_traffic_idx; u8 *tx_traffic; u8 *rx_traffic; struct dentry *debugfs_dir; u32 dbgfs_sram_offset, dbgfs_sram_len; bool disable_ht40; #endif /* CONFIG_IWLEGACY_DEBUGFS */ struct work_struct txpower_work; u32 disable_sens_cal; u32 disable_chain_noise_cal; u32 disable_tx_power_cal; struct work_struct run_time_calib_work; struct timer_list stats_periodic; struct timer_list watchdog; bool hw_ready; struct led_classdev led; unsigned long blink_on, blink_off; bool led_registered; }; /*il_priv */ static inline void il_txq_ctx_activate(struct il_priv *il, int txq_id) { set_bit(txq_id, &il->txq_ctx_active_msk); } static inline void il_txq_ctx_deactivate(struct il_priv *il, int txq_id) { clear_bit(txq_id, &il->txq_ctx_active_msk); } static inline int il_is_associated(struct il_priv *il) { return (il->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; } static inline int il_is_any_associated(struct il_priv *il) { return il_is_associated(il); } static inline int il_is_channel_valid(const struct il_channel_info *ch_info) { if (ch_info == NULL) return 0; return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0; } static inline int il_is_channel_radar(const struct il_channel_info *ch_info) { return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; } static inline u8 il_is_channel_a_band(const struct il_channel_info *ch_info) { return ch_info->band == IEEE80211_BAND_5GHZ; } static inline int il_is_channel_passive(const struct il_channel_info *ch) { return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; } static inline int il_is_channel_ibss(const struct il_channel_info *ch) { return (ch->flags & EEPROM_CHANNEL_IBSS) ? 1 : 0; } static inline void __il_free_pages(struct il_priv *il, struct page *page) { __free_pages(page, il->hw_params.rx_page_order); il->alloc_rxb_page--; } static inline void il_free_pages(struct il_priv *il, unsigned long page) { free_pages(page, il->hw_params.rx_page_order); il->alloc_rxb_page--; } #define IWLWIFI_VERSION "in-tree:" #define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" #define DRV_AUTHOR "" #define IL_PCI_DEVICE(dev, subdev, cfg) \ .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ .driver_data = (kernel_ulong_t)&(cfg) #define TIME_UNIT 1024 #define IL_SKU_G 0x1 #define IL_SKU_A 0x2 #define IL_SKU_N 0x8 #define IL_CMD(x) case x: return #x /* Size of one Rx buffer in host DRAM */ #define IL_RX_BUF_SIZE_3K (3 * 1000) /* 3945 only */ #define IL_RX_BUF_SIZE_4K (4 * 1024) #define IL_RX_BUF_SIZE_8K (8 * 1024) #ifdef CONFIG_IWLEGACY_DEBUGFS struct il_debugfs_ops { ssize_t(*rx_stats_read) (struct file *file, char __user *user_buf, size_t count, loff_t *ppos); ssize_t(*tx_stats_read) (struct file *file, char __user *user_buf, size_t count, loff_t *ppos); ssize_t(*general_stats_read) (struct file *file, char __user *user_buf, size_t count, loff_t *ppos); }; #endif struct il_ops { /* Handling TX */ void (*txq_update_byte_cnt_tbl) (struct il_priv *il, struct il_tx_queue *txq, u16 byte_cnt); int (*txq_attach_buf_to_tfd) (struct il_priv *il, struct il_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset, u8 pad); void (*txq_free_tfd) (struct il_priv *il, struct il_tx_queue *txq); int (*txq_init) (struct il_priv *il, struct il_tx_queue *txq); /* alive notification after init uCode load */ void (*init_alive_start) (struct il_priv *il); /* check validity of rtc data address */ int (*is_valid_rtc_data_addr) (u32 addr); /* 1st ucode load */ int (*load_ucode) (struct il_priv *il); void (*dump_nic_error_log) (struct il_priv *il); int (*dump_fh) (struct il_priv *il, char **buf, bool display); int (*set_channel_switch) (struct il_priv *il, struct ieee80211_channel_switch *ch_switch); /* power management */ int (*apm_init) (struct il_priv *il); /* tx power */ int (*send_tx_power) (struct il_priv *il); void (*update_chain_flags) (struct il_priv *il); /* eeprom operations */ int (*eeprom_acquire_semaphore) (struct il_priv *il); void (*eeprom_release_semaphore) (struct il_priv *il); int (*rxon_assoc) (struct il_priv *il); int (*commit_rxon) (struct il_priv *il); void (*set_rxon_chain) (struct il_priv *il); u16(*get_hcmd_size) (u8 cmd_id, u16 len); u16(*build_addsta_hcmd) (const struct il_addsta_cmd *cmd, u8 *data); int (*request_scan) (struct il_priv *il, struct ieee80211_vif *vif); void (*post_scan) (struct il_priv *il); void (*post_associate) (struct il_priv *il); void (*config_ap) (struct il_priv *il); /* station management */ int (*update_bcast_stations) (struct il_priv *il); int (*manage_ibss_station) (struct il_priv *il, struct ieee80211_vif *vif, bool add); int (*send_led_cmd) (struct il_priv *il, struct il_led_cmd *led_cmd); }; struct il_mod_params { int sw_crypto; /* def: 0 = using hardware encryption */ int disable_hw_scan; /* def: 0 = use h/w scan */ int num_of_queues; /* def: HW dependent */ int disable_11n; /* def: 0 = 11n capabilities enabled */ int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */ int antenna; /* def: 0 = both antennas (use diversity) */ int restart_fw; /* def: 1 = restart firmware */ }; #define IL_LED_SOLID 11 #define IL_DEF_LED_INTRVL cpu_to_le32(1000) #define IL_LED_ACTIVITY (0<<1) #define IL_LED_LINK (1<<1) /* * LED mode * IL_LED_DEFAULT: use device default * IL_LED_RF_STATE: turn LED on/off based on RF state * LED ON = RF ON * LED OFF = RF OFF * IL_LED_BLINK: adjust led blink rate based on blink table */ enum il_led_mode { IL_LED_DEFAULT, IL_LED_RF_STATE, IL_LED_BLINK, }; void il_leds_init(struct il_priv *il); void il_leds_exit(struct il_priv *il); /** * struct il_cfg * @fw_name_pre: Firmware filename prefix. The api version and extension * (.ucode) will be added to filename before loading from disk. The * filename is constructed as fw_name_pre.ucode. * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_min: Lowest version of uCode API supported by driver. * @scan_antennas: available antenna for scan operation * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) * * We enable the driver to be backward compatible wrt API version. The * driver specifies which APIs it supports (with @ucode_api_max being the * highest and @ucode_api_min the lowest). Firmware will only be loaded if * it has a supported API version. The firmware's API version will be * stored in @il_priv, enabling the driver to make runtime changes based * on firmware version used. * * For example, * if (IL_UCODE_API(il->ucode_ver) >= 2) { * Driver interacts with Firmware API version >= 2. * } else { * Driver interacts with Firmware API version 1. * } * * The ideal usage of this infrastructure is to treat a new ucode API * release as a new hardware revision. That is, through utilizing the * il_hcmd_utils_ops etc. we accommodate different command structures * and flows between hardware versions as well as their API * versions. * */ struct il_cfg { /* params specific to an individual device within a device family */ const char *name; const char *fw_name_pre; const unsigned int ucode_api_max; const unsigned int ucode_api_min; u8 valid_tx_ant; u8 valid_rx_ant; unsigned int sku; u16 eeprom_ver; u16 eeprom_calib_ver; /* module based parameters which can be set from modprobe cmd */ const struct il_mod_params *mod_params; /* params not likely to change within a device family */ struct il_base_params *base_params; /* params likely to change within a device family */ u8 scan_rx_antennas[IEEE80211_NUM_BANDS]; enum il_led_mode led_mode; int eeprom_size; int num_of_queues; /* def: HW dependent */ int num_of_ampdu_queues; /* def: HW dependent */ /* for il_apm_init() */ u32 pll_cfg_val; bool set_l0s; bool use_bsm; u16 led_compensation; int chain_noise_num_beacons; unsigned int wd_timeout; bool temperature_kelvin; const bool ucode_tracing; const bool sensitivity_calib_by_driver; const bool chain_noise_calib_by_driver; const u32 regulatory_bands[7]; }; /*************************** * L i b * ***************************/ int il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); int il_mac_tx_last_beacon(struct ieee80211_hw *hw); void il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt); int il_check_rxon_cmd(struct il_priv *il); int il_full_rxon_required(struct il_priv *il); int il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch); void il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band, struct ieee80211_vif *vif); u8 il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band); void il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf); bool il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap); void il_connection_init_rx_config(struct il_priv *il); void il_set_rate(struct il_priv *il); int il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, u32 decrypt_res, struct ieee80211_rx_status *stats); void il_irq_handle_error(struct il_priv *il); int il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p); int il_alloc_txq_mem(struct il_priv *il); void il_free_txq_mem(struct il_priv *il); #ifdef CONFIG_IWLEGACY_DEBUGFS extern void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len); #else static inline void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) { } #endif /***************************************************** * Handlers ***************************************************/ void il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb); void il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb); void il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb); void il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb); /***************************************************** * RX ******************************************************/ void il_cmd_queue_unmap(struct il_priv *il); void il_cmd_queue_free(struct il_priv *il); int il_rx_queue_alloc(struct il_priv *il); void il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q); int il_rx_queue_space(const struct il_rx_queue *q); void il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb); void il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb); void il_recover_from_stats(struct il_priv *il, struct il_rx_pkt *pkt); void il_chswitch_done(struct il_priv *il, bool is_success); /***************************************************** * TX ******************************************************/ extern void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq); extern int il_tx_queue_init(struct il_priv *il, u32 txq_id); extern void il_tx_queue_reset(struct il_priv *il, u32 txq_id); extern void il_tx_queue_unmap(struct il_priv *il, int txq_id); extern void il_tx_queue_free(struct il_priv *il, int txq_id); extern void il_setup_watchdog(struct il_priv *il); /***************************************************** * TX power ****************************************************/ int il_set_tx_power(struct il_priv *il, s8 tx_power, bool force); /******************************************************************************* * Rate ******************************************************************************/ u8 il_get_lowest_plcp(struct il_priv *il); /******************************************************************************* * Scanning ******************************************************************************/ void il_init_scan_params(struct il_priv *il); int il_scan_cancel(struct il_priv *il); int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms); void il_force_scan_end(struct il_priv *il); int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req); void il_internal_short_hw_scan(struct il_priv *il); int il_force_reset(struct il_priv *il, bool external); u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, const u8 *ta, const u8 *ie, int ie_len, int left); void il_setup_rx_scan_handlers(struct il_priv *il); u16 il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band, u8 n_probes); u16 il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band, struct ieee80211_vif *vif); void il_setup_scan_deferred_work(struct il_priv *il); void il_cancel_scan_deferred_work(struct il_priv *il); /* For faster active scanning, scan will move to the next channel if fewer than * PLCP_QUIET_THRESH packets are heard on this channel within * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell * time if it's a quiet channel (nothing responded to our probe, and there's * no other traffic). * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ #define IL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ #define IL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ #define IL_SCAN_CHECK_WATCHDOG (HZ * 7) /***************************************************** * S e n d i n g H o s t C o m m a n d s * *****************************************************/ const char *il_get_cmd_string(u8 cmd); int __must_check il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd); int il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd); int __must_check il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data); int il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, struct il_rx_pkt *pkt)); int il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd); /***************************************************** * PCI * *****************************************************/ static inline u16 il_pcie_link_ctl(struct il_priv *il) { u16 pci_lnk_ctl; pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &pci_lnk_ctl); return pci_lnk_ctl; } void il_bg_watchdog(unsigned long data); u32 il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval); __le32 il_add_beacon_time(struct il_priv *il, u32 base, u32 addon, u32 beacon_interval); #ifdef CONFIG_PM #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) int il_pci_suspend_compat(struct pci_dev *pdev, pm_message_t state); int il_pci_resume_compat(struct pci_dev *pdev); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) extern struct dev_pm_ops il_pm_ops; #else extern const struct dev_pm_ops il_pm_ops; #endif #define IL_LEGACY_PM_OPS (&il_pm_ops) #else /* !CONFIG_PM */ #define IL_LEGACY_PM_OPS NULL #endif /* !CONFIG_PM */ /***************************************************** * Error Handling Debugging ******************************************************/ void il4965_dump_nic_error_log(struct il_priv *il); #ifdef CONFIG_IWLEGACY_DEBUG void il_print_rx_config_cmd(struct il_priv *il); #else static inline void il_print_rx_config_cmd(struct il_priv *il) { } #endif void il_clear_isr_stats(struct il_priv *il); /***************************************************** * GEOS ******************************************************/ int il_init_geos(struct il_priv *il); void il_free_geos(struct il_priv *il); /*************** DRIVER STATUS FUNCTIONS *****/ #define S_HCMD_ACTIVE 0 /* host command in progress */ /* 1 is unused (used to be S_HCMD_SYNC_ACTIVE) */ #define S_INT_ENABLED 2 #define S_RFKILL 3 #define S_CT_KILL 4 #define S_INIT 5 #define S_ALIVE 6 #define S_READY 7 #define S_TEMPERATURE 8 #define S_GEO_CONFIGURED 9 #define S_EXIT_PENDING 10 #define S_STATS 12 #define S_SCANNING 13 #define S_SCAN_ABORTING 14 #define S_SCAN_HW 15 #define S_POWER_PMI 16 #define S_FW_ERROR 17 #define S_CHANNEL_SWITCH_PENDING 18 static inline int il_is_ready(struct il_priv *il) { /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are * set but EXIT_PENDING is not */ return test_bit(S_READY, &il->status) && test_bit(S_GEO_CONFIGURED, &il->status) && !test_bit(S_EXIT_PENDING, &il->status); } static inline int il_is_alive(struct il_priv *il) { return test_bit(S_ALIVE, &il->status); } static inline int il_is_init(struct il_priv *il) { return test_bit(S_INIT, &il->status); } static inline int il_is_rfkill(struct il_priv *il) { return test_bit(S_RFKILL, &il->status); } static inline int il_is_ctkill(struct il_priv *il) { return test_bit(S_CT_KILL, &il->status); } static inline int il_is_ready_rf(struct il_priv *il) { if (il_is_rfkill(il)) return 0; return il_is_ready(il); } extern void il_send_bt_config(struct il_priv *il); extern int il_send_stats_request(struct il_priv *il, u8 flags, bool clear); extern void il_apm_stop(struct il_priv *il); extern void _il_apm_stop(struct il_priv *il); int il_apm_init(struct il_priv *il); int il_send_rxon_timing(struct il_priv *il); static inline int il_send_rxon_assoc(struct il_priv *il) { return il->ops->rxon_assoc(il); } static inline int il_commit_rxon(struct il_priv *il) { return il->ops->commit_rxon(il); } static inline const struct ieee80211_supported_band * il_get_hw_mode(struct il_priv *il, enum ieee80211_band band) { return il->hw->wiphy->bands[band]; } /* mac80211 handlers */ int il_mac_config(struct ieee80211_hw *hw, u32 changed); void il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); void il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, __le16 fc, __le32 *tx_flags); irqreturn_t il_isr(int irq, void *data); extern void il_set_bit(struct il_priv *p, u32 r, u32 m); extern void il_clear_bit(struct il_priv *p, u32 r, u32 m); extern bool _il_grab_nic_access(struct il_priv *il); extern int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout); extern int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout); extern u32 il_rd_prph(struct il_priv *il, u32 reg); extern void il_wr_prph(struct il_priv *il, u32 addr, u32 val); extern u32 il_read_targ_mem(struct il_priv *il, u32 addr); extern void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val); static inline void _il_write8(struct il_priv *il, u32 ofs, u8 val) { writeb(val, il->hw_base + ofs); } #define il_write8(il, ofs, val) _il_write8(il, ofs, val) static inline void _il_wr(struct il_priv *il, u32 ofs, u32 val) { writel(val, il->hw_base + ofs); } static inline u32 _il_rd(struct il_priv *il, u32 ofs) { return readl(il->hw_base + ofs); } static inline void _il_clear_bit(struct il_priv *il, u32 reg, u32 mask) { _il_wr(il, reg, _il_rd(il, reg) & ~mask); } static inline void _il_set_bit(struct il_priv *il, u32 reg, u32 mask) { _il_wr(il, reg, _il_rd(il, reg) | mask); } static inline void _il_release_nic_access(struct il_priv *il) { _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* * In above we are reading CSR_GP_CNTRL register, what will flush any * previous writes, but still want write, which clear MAC_ACCESS_REQ * bit, be performed on PCI bus before any other writes scheduled on * different CPUs (after we drop reg_lock). */ mmiowb(); } static inline u32 il_rd(struct il_priv *il, u32 reg) { u32 value; unsigned long reg_flags; spin_lock_irqsave(&il->reg_lock, reg_flags); _il_grab_nic_access(il); value = _il_rd(il, reg); _il_release_nic_access(il); spin_unlock_irqrestore(&il->reg_lock, reg_flags); return value; } static inline void il_wr(struct il_priv *il, u32 reg, u32 value) { unsigned long reg_flags; spin_lock_irqsave(&il->reg_lock, reg_flags); if (likely(_il_grab_nic_access(il))) { _il_wr(il, reg, value); _il_release_nic_access(il); } spin_unlock_irqrestore(&il->reg_lock, reg_flags); } static inline u32 _il_rd_prph(struct il_priv *il, u32 reg) { _il_wr(il, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); return _il_rd(il, HBUS_TARG_PRPH_RDAT); } static inline void _il_wr_prph(struct il_priv *il, u32 addr, u32 val) { _il_wr(il, HBUS_TARG_PRPH_WADDR, ((addr & 0x0000FFFF) | (3 << 24))); _il_wr(il, HBUS_TARG_PRPH_WDAT, val); } static inline void il_set_bits_prph(struct il_priv *il, u32 reg, u32 mask) { unsigned long reg_flags; spin_lock_irqsave(&il->reg_lock, reg_flags); if (likely(_il_grab_nic_access(il))) { _il_wr_prph(il, reg, (_il_rd_prph(il, reg) | mask)); _il_release_nic_access(il); } spin_unlock_irqrestore(&il->reg_lock, reg_flags); } static inline void il_set_bits_mask_prph(struct il_priv *il, u32 reg, u32 bits, u32 mask) { unsigned long reg_flags; spin_lock_irqsave(&il->reg_lock, reg_flags); if (likely(_il_grab_nic_access(il))) { _il_wr_prph(il, reg, ((_il_rd_prph(il, reg) & mask) | bits)); _il_release_nic_access(il); } spin_unlock_irqrestore(&il->reg_lock, reg_flags); } static inline void il_clear_bits_prph(struct il_priv *il, u32 reg, u32 mask) { unsigned long reg_flags; u32 val; spin_lock_irqsave(&il->reg_lock, reg_flags); if (likely(_il_grab_nic_access(il))) { val = _il_rd_prph(il, reg); _il_wr_prph(il, reg, (val & ~mask)); _il_release_nic_access(il); } spin_unlock_irqrestore(&il->reg_lock, reg_flags); } #define HW_KEY_DYNAMIC 0 #define HW_KEY_DEFAULT 1 #define IL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ #define IL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ #define IL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of being activated */ #define IL_STA_LOCAL BIT(3) /* station state not directed by mac80211; (this is for the IBSS BSSID stations) */ #define IL_STA_BCAST BIT(4) /* this station is the special bcast station */ void il_restore_stations(struct il_priv *il); void il_clear_ucode_stations(struct il_priv *il); void il_dealloc_bcast_stations(struct il_priv *il); int il_get_free_ucode_key_idx(struct il_priv *il); int il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags); int il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, struct ieee80211_sta *sta, u8 *sta_id_r); int il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr); int il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); u8 il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, struct ieee80211_sta *sta); int il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, u8 flags, bool init); /** * il_clear_driver_stations - clear knowledge of all stations from driver * @il: iwl il struct * * This is called during il_down() to make sure that in the case * we're coming there from a hardware restart mac80211 will be * able to reconfigure stations -- if we're getting there in the * normal down flow then the stations will already be cleared. */ static inline void il_clear_driver_stations(struct il_priv *il) { unsigned long flags; spin_lock_irqsave(&il->sta_lock, flags); memset(il->stations, 0, sizeof(il->stations)); il->num_stations = 0; il->ucode_key_table = 0; spin_unlock_irqrestore(&il->sta_lock, flags); } static inline int il_sta_id(struct ieee80211_sta *sta) { if (WARN_ON(!sta)) return IL_INVALID_STATION; return ((struct il_station_priv_common *)sta->drv_priv)->sta_id; } /** * il_sta_id_or_broadcast - return sta_id or broadcast sta * @il: iwl il * @context: the current context * @sta: mac80211 station * * In certain circumstances mac80211 passes a station pointer * that may be %NULL, for example during TX or key setup. In * that case, we need to use the broadcast station, so this * inline wraps that pattern. */ static inline int il_sta_id_or_broadcast(struct il_priv *il, struct ieee80211_sta *sta) { int sta_id; if (!sta) return il->hw_params.bcast_id; sta_id = il_sta_id(sta); /* * mac80211 should not be passing a partially * initialised station! */ WARN_ON(sta_id == IL_INVALID_STATION); return sta_id; } /** * il_queue_inc_wrap - increment queue idx, wrap back to beginning * @idx -- current idx * @n_bd -- total number of entries in queue (must be power of 2) */ static inline int il_queue_inc_wrap(int idx, int n_bd) { return ++idx & (n_bd - 1); } /** * il_queue_dec_wrap - decrement queue idx, wrap back to end * @idx -- current idx * @n_bd -- total number of entries in queue (must be power of 2) */ static inline int il_queue_dec_wrap(int idx, int n_bd) { return --idx & (n_bd - 1); } /* TODO: Move fw_desc functions to iwl-pci.ko */ static inline void il_free_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) { if (desc->v_addr) dma_free_coherent(&pci_dev->dev, desc->len, desc->v_addr, desc->p_addr); desc->v_addr = NULL; desc->len = 0; } static inline int il_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) { if (!desc->len) { desc->v_addr = NULL; return -EINVAL; } desc->v_addr = dma_alloc_coherent(&pci_dev->dev, desc->len, &desc->p_addr, GFP_KERNEL); return (desc->v_addr != NULL) ? 0 : -ENOMEM; } /* * we have 8 bits used like this: * * 7 6 5 4 3 2 1 0 * | | | | | | | | * | | | | | | +-+-------- AC queue (0-3) * | | | | | | * | +-+-+-+-+------------ HW queue ID * | * +---------------------- unused */ static inline void il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq) { BUG_ON(ac > 3); /* only have 2 bits */ BUG_ON(hwq > 31); /* only use 5 bits */ txq->swq_id = (hwq << 2) | ac; } static inline void il_wake_queue(struct il_priv *il, struct il_tx_queue *txq) { u8 queue = txq->swq_id; u8 ac = queue & 3; u8 hwq = (queue >> 2) & 0x1f; if (test_and_clear_bit(hwq, il->queue_stopped)) if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0) ieee80211_wake_queue(il->hw, ac); } static inline void il_stop_queue(struct il_priv *il, struct il_tx_queue *txq) { u8 queue = txq->swq_id; u8 ac = queue & 3; u8 hwq = (queue >> 2) & 0x1f; if (!test_and_set_bit(hwq, il->queue_stopped)) if (atomic_inc_return(&il->queue_stop_count[ac]) > 0) ieee80211_stop_queue(il->hw, ac); } #ifdef ieee80211_stop_queue #undef ieee80211_stop_queue #endif #define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue #ifdef ieee80211_wake_queue #undef ieee80211_wake_queue #endif #define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue static inline void il_disable_interrupts(struct il_priv *il) { clear_bit(S_INT_ENABLED, &il->status); /* disable interrupts from uCode/NIC to host */ _il_wr(il, CSR_INT_MASK, 0x00000000); /* acknowledge/clear/reset any interrupts still pending * from uCode or flow handler (Rx/Tx DMA) */ _il_wr(il, CSR_INT, 0xffffffff); _il_wr(il, CSR_FH_INT_STATUS, 0xffffffff); } static inline void il_enable_rfkill_int(struct il_priv *il) { _il_wr(il, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); } static inline void il_enable_interrupts(struct il_priv *il) { set_bit(S_INT_ENABLED, &il->status); _il_wr(il, CSR_INT_MASK, il->inta_mask); } /** * il_beacon_time_mask_low - mask of lower 32 bit of beacon time * @il -- pointer to il_priv data structure * @tsf_bits -- number of bits need to shift for masking) */ static inline u32 il_beacon_time_mask_low(struct il_priv *il, u16 tsf_bits) { return (1 << tsf_bits) - 1; } /** * il_beacon_time_mask_high - mask of higher 32 bit of beacon time * @il -- pointer to il_priv data structure * @tsf_bits -- number of bits need to shift for masking) */ static inline u32 il_beacon_time_mask_high(struct il_priv *il, u16 tsf_bits) { return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; } /** * struct il_rb_status - reseve buffer status host memory mapped FH registers * * @closed_rb_num [0:11] - Indicates the idx of the RB which was closed * @closed_fr_num [0:11] - Indicates the idx of the RX Frame which was closed * @finished_rb_num [0:11] - Indicates the idx of the current RB * in which the last frame was written to * @finished_fr_num [0:11] - Indicates the idx of the RX Frame * which was transferred */ struct il_rb_status { __le16 closed_rb_num; __le16 closed_fr_num; __le16 finished_rb_num; __le16 finished_fr_nam; __le32 __unused; /* 3945 only */ } __packed; #define TFD_QUEUE_SIZE_MAX 256 #define TFD_QUEUE_SIZE_BC_DUP 64 #define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) #define IL_TX_DMA_MASK DMA_BIT_MASK(36) #define IL_NUM_OF_TBS 20 static inline u8 il_get_dma_hi_addr(dma_addr_t addr) { return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; } /** * struct il_tfd_tb transmit buffer descriptor within transmit frame descriptor * * This structure contains dma address and length of transmission address * * @lo: low [31:0] portion of the dma address of TX buffer every even is * unaligned on 16 bit boundary * @hi_n_len: 0-3 [35:32] portion of dma * 4-15 length of the tx buffer */ struct il_tfd_tb { __le32 lo; __le16 hi_n_len; } __packed; /** * struct il_tfd * * Transmit Frame Descriptor (TFD) * * @ __reserved1[3] reserved * @ num_tbs 0-4 number of active tbs * 5 reserved * 6-7 padding (not used) * @ tbs[20] transmit frame buffer descriptors * @ __pad padding * * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. * Both driver and device share these circular buffers, each of which must be * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes * * Driver must indicate the physical address of the base of each * circular buffer via the FH49_MEM_CBBC_QUEUE registers. * * Each TFD contains pointer/size information for up to 20 data buffers * in host DRAM. These buffers collectively contain the (one) frame described * by the TFD. Each buffer must be a single contiguous block of memory within * itself, but buffers may be scattered in host DRAM. Each buffer has max size * of (4K - 4). The concatenates all of a TFD's buffers into a single * Tx frame, up to 8 KBytes in size. * * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. */ struct il_tfd { u8 __reserved1[3]; u8 num_tbs; struct il_tfd_tb tbs[IL_NUM_OF_TBS]; __le32 __pad; } __packed; /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 /* PCI register values */ #define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01 #define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02 struct il_rate_info { u8 plcp; /* uCode API: RATE_6M_PLCP, etc. */ u8 plcp_siso; /* uCode API: RATE_SISO_6M_PLCP, etc. */ u8 plcp_mimo2; /* uCode API: RATE_MIMO2_6M_PLCP, etc. */ u8 ieee; /* MAC header: RATE_6M_IEEE, etc. */ u8 prev_ieee; /* previous rate in IEEE speeds */ u8 next_ieee; /* next rate in IEEE speeds */ u8 prev_rs; /* previous rate used in rs algo */ u8 next_rs; /* next rate used in rs algo */ u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ u8 next_rs_tgg; /* next rate used in TGG rs algo */ }; struct il3945_rate_info { u8 plcp; /* uCode API: RATE_6M_PLCP, etc. */ u8 ieee; /* MAC header: RATE_6M_IEEE, etc. */ u8 prev_ieee; /* previous rate in IEEE speeds */ u8 next_ieee; /* next rate in IEEE speeds */ u8 prev_rs; /* previous rate used in rs algo */ u8 next_rs; /* next rate used in rs algo */ u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ u8 next_rs_tgg; /* next rate used in TGG rs algo */ u8 table_rs_idx; /* idx in rate scale table cmd */ u8 prev_table_rs; /* prev in rate table cmd */ }; /* * These serve as idxes into * struct il_rate_info il_rates[RATE_COUNT]; */ enum { RATE_1M_IDX = 0, RATE_2M_IDX, RATE_5M_IDX, RATE_11M_IDX, RATE_6M_IDX, RATE_9M_IDX, RATE_12M_IDX, RATE_18M_IDX, RATE_24M_IDX, RATE_36M_IDX, RATE_48M_IDX, RATE_54M_IDX, RATE_60M_IDX, RATE_COUNT, RATE_COUNT_LEGACY = RATE_COUNT - 1, /* Excluding 60M */ RATE_COUNT_3945 = RATE_COUNT - 1, RATE_INVM_IDX = RATE_COUNT, RATE_INVALID = RATE_COUNT, }; enum { RATE_6M_IDX_TBL = 0, RATE_9M_IDX_TBL, RATE_12M_IDX_TBL, RATE_18M_IDX_TBL, RATE_24M_IDX_TBL, RATE_36M_IDX_TBL, RATE_48M_IDX_TBL, RATE_54M_IDX_TBL, RATE_1M_IDX_TBL, RATE_2M_IDX_TBL, RATE_5M_IDX_TBL, RATE_11M_IDX_TBL, RATE_INVM_IDX_TBL = RATE_INVM_IDX - 1, }; enum { IL_FIRST_OFDM_RATE = RATE_6M_IDX, IL39_LAST_OFDM_RATE = RATE_54M_IDX, IL_LAST_OFDM_RATE = RATE_60M_IDX, IL_FIRST_CCK_RATE = RATE_1M_IDX, IL_LAST_CCK_RATE = RATE_11M_IDX, }; /* #define vs. enum to keep from defaulting to 'large integer' */ #define RATE_6M_MASK (1 << RATE_6M_IDX) #define RATE_9M_MASK (1 << RATE_9M_IDX) #define RATE_12M_MASK (1 << RATE_12M_IDX) #define RATE_18M_MASK (1 << RATE_18M_IDX) #define RATE_24M_MASK (1 << RATE_24M_IDX) #define RATE_36M_MASK (1 << RATE_36M_IDX) #define RATE_48M_MASK (1 << RATE_48M_IDX) #define RATE_54M_MASK (1 << RATE_54M_IDX) #define RATE_60M_MASK (1 << RATE_60M_IDX) #define RATE_1M_MASK (1 << RATE_1M_IDX) #define RATE_2M_MASK (1 << RATE_2M_IDX) #define RATE_5M_MASK (1 << RATE_5M_IDX) #define RATE_11M_MASK (1 << RATE_11M_IDX) /* uCode API values for legacy bit rates, both OFDM and CCK */ enum { RATE_6M_PLCP = 13, RATE_9M_PLCP = 15, RATE_12M_PLCP = 5, RATE_18M_PLCP = 7, RATE_24M_PLCP = 9, RATE_36M_PLCP = 11, RATE_48M_PLCP = 1, RATE_54M_PLCP = 3, RATE_60M_PLCP = 3, /*FIXME:RS:should be removed */ RATE_1M_PLCP = 10, RATE_2M_PLCP = 20, RATE_5M_PLCP = 55, RATE_11M_PLCP = 110, /*FIXME:RS:add RATE_LEGACY_INVM_PLCP = 0, */ }; /* uCode API values for OFDM high-throughput (HT) bit rates */ enum { RATE_SISO_6M_PLCP = 0, RATE_SISO_12M_PLCP = 1, RATE_SISO_18M_PLCP = 2, RATE_SISO_24M_PLCP = 3, RATE_SISO_36M_PLCP = 4, RATE_SISO_48M_PLCP = 5, RATE_SISO_54M_PLCP = 6, RATE_SISO_60M_PLCP = 7, RATE_MIMO2_6M_PLCP = 0x8, RATE_MIMO2_12M_PLCP = 0x9, RATE_MIMO2_18M_PLCP = 0xa, RATE_MIMO2_24M_PLCP = 0xb, RATE_MIMO2_36M_PLCP = 0xc, RATE_MIMO2_48M_PLCP = 0xd, RATE_MIMO2_54M_PLCP = 0xe, RATE_MIMO2_60M_PLCP = 0xf, RATE_SISO_INVM_PLCP, RATE_MIMO2_INVM_PLCP = RATE_SISO_INVM_PLCP, }; /* MAC header values for bit rates */ enum { RATE_6M_IEEE = 12, RATE_9M_IEEE = 18, RATE_12M_IEEE = 24, RATE_18M_IEEE = 36, RATE_24M_IEEE = 48, RATE_36M_IEEE = 72, RATE_48M_IEEE = 96, RATE_54M_IEEE = 108, RATE_60M_IEEE = 120, RATE_1M_IEEE = 2, RATE_2M_IEEE = 4, RATE_5M_IEEE = 11, RATE_11M_IEEE = 22, }; #define IL_CCK_BASIC_RATES_MASK \ (RATE_1M_MASK | \ RATE_2M_MASK) #define IL_CCK_RATES_MASK \ (IL_CCK_BASIC_RATES_MASK | \ RATE_5M_MASK | \ RATE_11M_MASK) #define IL_OFDM_BASIC_RATES_MASK \ (RATE_6M_MASK | \ RATE_12M_MASK | \ RATE_24M_MASK) #define IL_OFDM_RATES_MASK \ (IL_OFDM_BASIC_RATES_MASK | \ RATE_9M_MASK | \ RATE_18M_MASK | \ RATE_36M_MASK | \ RATE_48M_MASK | \ RATE_54M_MASK) #define IL_BASIC_RATES_MASK \ (IL_OFDM_BASIC_RATES_MASK | \ IL_CCK_BASIC_RATES_MASK) #define RATES_MASK ((1 << RATE_COUNT) - 1) #define RATES_MASK_3945 ((1 << RATE_COUNT_3945) - 1) #define IL_INVALID_VALUE -1 #define IL_MIN_RSSI_VAL -100 #define IL_MAX_RSSI_VAL 0 /* These values specify how many Tx frame attempts before * searching for a new modulation mode */ #define IL_LEGACY_FAILURE_LIMIT 160 #define IL_LEGACY_SUCCESS_LIMIT 480 #define IL_LEGACY_TBL_COUNT 160 #define IL_NONE_LEGACY_FAILURE_LIMIT 400 #define IL_NONE_LEGACY_SUCCESS_LIMIT 4500 #define IL_NONE_LEGACY_TBL_COUNT 1500 /* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ #define IL_RS_GOOD_RATIO 12800 /* 100% */ #define RATE_SCALE_SWITCH 10880 /* 85% */ #define RATE_HIGH_TH 10880 /* 85% */ #define RATE_INCREASE_TH 6400 /* 50% */ #define RATE_DECREASE_TH 1920 /* 15% */ /* possible actions when in legacy mode */ #define IL_LEGACY_SWITCH_ANTENNA1 0 #define IL_LEGACY_SWITCH_ANTENNA2 1 #define IL_LEGACY_SWITCH_SISO 2 #define IL_LEGACY_SWITCH_MIMO2_AB 3 #define IL_LEGACY_SWITCH_MIMO2_AC 4 #define IL_LEGACY_SWITCH_MIMO2_BC 5 /* possible actions when in siso mode */ #define IL_SISO_SWITCH_ANTENNA1 0 #define IL_SISO_SWITCH_ANTENNA2 1 #define IL_SISO_SWITCH_MIMO2_AB 2 #define IL_SISO_SWITCH_MIMO2_AC 3 #define IL_SISO_SWITCH_MIMO2_BC 4 #define IL_SISO_SWITCH_GI 5 /* possible actions when in mimo mode */ #define IL_MIMO2_SWITCH_ANTENNA1 0 #define IL_MIMO2_SWITCH_ANTENNA2 1 #define IL_MIMO2_SWITCH_SISO_A 2 #define IL_MIMO2_SWITCH_SISO_B 3 #define IL_MIMO2_SWITCH_SISO_C 4 #define IL_MIMO2_SWITCH_GI 5 #define IL_MAX_SEARCH IL_MIMO2_SWITCH_GI #define IL_ACTION_LIMIT 3 /* # possible actions */ #define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ /* load per tid defines for A-MPDU activation */ #define IL_AGG_TPT_THREHOLD 0 #define IL_AGG_LOAD_THRESHOLD 10 #define IL_AGG_ALL_TID 0xff #define TID_QUEUE_CELL_SPACING 50 /*mS */ #define TID_QUEUE_MAX_SIZE 20 #define TID_ROUND_VALUE 5 /* mS */ #define TID_MAX_LOAD_COUNT 8 #define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) #define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) extern const struct il_rate_info il_rates[RATE_COUNT]; enum il_table_type { LQ_NONE, LQ_G, /* legacy types */ LQ_A, LQ_SISO, /* high-throughput types */ LQ_MIMO2, LQ_MAX, }; #define is_legacy(tbl) ((tbl) == LQ_G || (tbl) == LQ_A) #define is_siso(tbl) ((tbl) == LQ_SISO) #define is_mimo2(tbl) ((tbl) == LQ_MIMO2) #define is_mimo(tbl) (is_mimo2(tbl)) #define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) #define is_a_band(tbl) ((tbl) == LQ_A) #define is_g_and(tbl) ((tbl) == LQ_G) #define ANT_NONE 0x0 #define ANT_A BIT(0) #define ANT_B BIT(1) #define ANT_AB (ANT_A | ANT_B) #define ANT_C BIT(2) #define ANT_AC (ANT_A | ANT_C) #define ANT_BC (ANT_B | ANT_C) #define ANT_ABC (ANT_AB | ANT_C) #define IL_MAX_MCS_DISPLAY_SIZE 12 struct il_rate_mcs_info { char mbps[IL_MAX_MCS_DISPLAY_SIZE]; char mcs[IL_MAX_MCS_DISPLAY_SIZE]; }; /** * struct il_rate_scale_data -- tx success history for one rate */ struct il_rate_scale_data { u64 data; /* bitmap of successful frames */ s32 success_counter; /* number of frames successful */ s32 success_ratio; /* per-cent * 128 */ s32 counter; /* number of frames attempted */ s32 average_tpt; /* success ratio * expected throughput */ unsigned long stamp; }; /** * struct il_scale_tbl_info -- tx params and success history for all rates * * There are two of these in struct il_lq_sta, * one for "active", and one for "search". */ struct il_scale_tbl_info { enum il_table_type lq_type; u8 ant_type; u8 is_SGI; /* 1 = short guard interval */ u8 is_ht40; /* 1 = 40 MHz channel width */ u8 is_dup; /* 1 = duplicated data streams */ u8 action; /* change modulation; IL_[LEGACY/SISO/MIMO]_SWITCH_* */ u8 max_search; /* maximun number of tables we can search */ s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ u32 current_rate; /* rate_n_flags, uCode API format */ struct il_rate_scale_data win[RATE_COUNT]; /* rate histories */ }; struct il_traffic_load { unsigned long time_stamp; /* age of the oldest stats */ u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time * slice */ u32 total; /* total num of packets during the * last TID_MAX_TIME_DIFF */ u8 queue_count; /* number of queues that has * been used since the last cleanup */ u8 head; /* start of the circular buffer */ }; /** * struct il_lq_sta -- driver's rate scaling ilate structure * * Pointer to this gets passed back and forth between driver and mac80211. */ struct il_lq_sta { u8 active_tbl; /* idx of active table, range 0-1 */ u8 enable_counter; /* indicates HT mode */ u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ u8 search_better_tbl; /* 1: currently trying alternate mode */ s32 last_tpt; /* The following determine when to search for a new mode */ u32 table_count_limit; u32 max_failure_limit; /* # failed frames before new search */ u32 max_success_limit; /* # successful frames before new search */ u32 table_count; u32 total_failed; /* total failed frames, any/all rates */ u32 total_success; /* total successful frames, any/all rates */ u64 flush_timer; /* time staying in mode before new search */ u8 action_counter; /* # mode-switch actions tried */ u8 is_green; u8 is_dup; enum ieee80211_band band; /* The following are bitmaps of rates; RATE_6M_MASK, etc. */ u32 supp_rates; u16 active_legacy_rate; u16 active_siso_rate; u16 active_mimo2_rate; s8 max_rate_idx; /* Max rate set by user */ u8 missed_rate_counter; struct il_link_quality_cmd lq; struct il_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ struct il_traffic_load load[TID_MAX_LOAD_COUNT]; u8 tx_agg_tid_en; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *rs_sta_dbgfs_scale_table_file; struct dentry *rs_sta_dbgfs_stats_table_file; struct dentry *rs_sta_dbgfs_rate_scale_data_file; struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; u32 dbg_fixed_rate; #endif struct il_priv *drv; /* used to be in sta_info */ int last_txrate_idx; /* last tx rate_n_flags */ u32 last_rate_n_flags; /* packets destined for this STA are aggregated */ u8 is_agg; }; /* * il_station_priv: Driver's ilate station information * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is places in that * space. * * The common struct MUST be first because it is shared between * 3945 and 4965! */ struct il_station_priv { struct il_station_priv_common common; struct il_lq_sta lq_sta; atomic_t pending_frames; bool client; bool asleep; }; static inline u8 il4965_num_of_ant(u8 m) { return !!(m & ANT_A) + !!(m & ANT_B) + !!(m & ANT_C); } static inline u8 il4965_first_antenna(u8 mask) { if (mask & ANT_A) return ANT_A; if (mask & ANT_B) return ANT_B; return ANT_C; } /** * il3945_rate_scale_init - Initialize the rate scale table based on assoc info * * The specific throughput table used is based on the type of network * the associated with, including A, B, G, and G w/ TGG protection */ extern void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); /* Initialize station's rate scaling information after adding station */ extern void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id); extern void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id); /** * il_rate_control_register - Register the rate control algorithm callbacks * * Since the rate control algorithm is hardware specific, there is no need * or reason to place it as a stand alone module. The driver can call * il_rate_control_register in order to register the rate control callbacks * with the mac80211 subsystem. This should be performed prior to calling * ieee80211_register_hw * */ extern int il4965_rate_control_register(void); extern int il3945_rate_control_register(void); /** * il_rate_control_unregister - Unregister the rate control callbacks * * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ extern void il4965_rate_control_unregister(void); extern void il3945_rate_control_unregister(void); extern int il_power_update_mode(struct il_priv *il, bool force); extern void il_power_initialize(struct il_priv *il); extern u32 il_debug_level; #ifdef CONFIG_IWLEGACY_DEBUG /* * il_get_debug_level: Return active debug level for device * * Using sysfs it is possible to set per device debug level. This debug * level will be used if set, otherwise the global debug level which can be * set via module parameter is used. */ static inline u32 il_get_debug_level(struct il_priv *il) { if (il->debug_level) return il->debug_level; else return il_debug_level; } #else static inline u32 il_get_debug_level(struct il_priv *il) { return il_debug_level; } #endif #define il_print_hex_error(il, p, len) \ do { \ print_hex_dump(KERN_ERR, "iwl data: ", \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) #ifdef CONFIG_IWLEGACY_DEBUG #define IL_DBG(level, fmt, args...) \ do { \ if (il_get_debug_level(il) & level) \ dev_printk(KERN_ERR, &il->hw->wiphy->dev, \ "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ __func__ , ## args); \ } while (0) #define il_print_hex_dump(il, level, p, len) \ do { \ if (il_get_debug_level(il) & level) \ print_hex_dump(KERN_DEBUG, "iwl data: ", \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) #else #define IL_DBG(level, fmt, args...) static inline void il_print_hex_dump(struct il_priv *il, int level, const void *p, u32 len) { } #endif /* CONFIG_IWLEGACY_DEBUG */ #ifdef CONFIG_IWLEGACY_DEBUGFS int il_dbgfs_register(struct il_priv *il, const char *name); void il_dbgfs_unregister(struct il_priv *il); #else static inline int il_dbgfs_register(struct il_priv *il, const char *name) { return 0; } static inline void il_dbgfs_unregister(struct il_priv *il) { } #endif /* CONFIG_IWLEGACY_DEBUGFS */ /* * To use the debug system: * * If you are defining a new debug classification, simply add it to the #define * list here in the form of * * #define IL_DL_xxxx VALUE * * where xxxx should be the name of the classification (for example, WEP). * * You then need to either add a IL_xxxx_DEBUG() macro definition for your * classification, or use IL_DBG(IL_DL_xxxx, ...) whenever you want * to send output to that classification. * * The active debug levels can be accessed via files * * /sys/module/iwl4965/parameters/debug * /sys/module/iwl3945/parameters/debug * /sys/class/net/wlan0/device/debug_level * * when CONFIG_IWLEGACY_DEBUG=y. */ /* 0x0000000F - 0x00000001 */ #define IL_DL_INFO (1 << 0) #define IL_DL_MAC80211 (1 << 1) #define IL_DL_HCMD (1 << 2) #define IL_DL_STATE (1 << 3) /* 0x000000F0 - 0x00000010 */ #define IL_DL_MACDUMP (1 << 4) #define IL_DL_HCMD_DUMP (1 << 5) #define IL_DL_EEPROM (1 << 6) #define IL_DL_RADIO (1 << 7) /* 0x00000F00 - 0x00000100 */ #define IL_DL_POWER (1 << 8) #define IL_DL_TEMP (1 << 9) #define IL_DL_NOTIF (1 << 10) #define IL_DL_SCAN (1 << 11) /* 0x0000F000 - 0x00001000 */ #define IL_DL_ASSOC (1 << 12) #define IL_DL_DROP (1 << 13) #define IL_DL_TXPOWER (1 << 14) #define IL_DL_AP (1 << 15) /* 0x000F0000 - 0x00010000 */ #define IL_DL_FW (1 << 16) #define IL_DL_RF_KILL (1 << 17) #define IL_DL_FW_ERRORS (1 << 18) #define IL_DL_LED (1 << 19) /* 0x00F00000 - 0x00100000 */ #define IL_DL_RATE (1 << 20) #define IL_DL_CALIB (1 << 21) #define IL_DL_WEP (1 << 22) #define IL_DL_TX (1 << 23) /* 0x0F000000 - 0x01000000 */ #define IL_DL_RX (1 << 24) #define IL_DL_ISR (1 << 25) #define IL_DL_HT (1 << 26) /* 0xF0000000 - 0x10000000 */ #define IL_DL_11H (1 << 28) #define IL_DL_STATS (1 << 29) #define IL_DL_TX_REPLY (1 << 30) #define IL_DL_QOS (1 << 31) #define D_INFO(f, a...) IL_DBG(IL_DL_INFO, f, ## a) #define D_MAC80211(f, a...) IL_DBG(IL_DL_MAC80211, f, ## a) #define D_MACDUMP(f, a...) IL_DBG(IL_DL_MACDUMP, f, ## a) #define D_TEMP(f, a...) IL_DBG(IL_DL_TEMP, f, ## a) #define D_SCAN(f, a...) IL_DBG(IL_DL_SCAN, f, ## a) #define D_RX(f, a...) IL_DBG(IL_DL_RX, f, ## a) #define D_TX(f, a...) IL_DBG(IL_DL_TX, f, ## a) #define D_ISR(f, a...) IL_DBG(IL_DL_ISR, f, ## a) #define D_LED(f, a...) IL_DBG(IL_DL_LED, f, ## a) #define D_WEP(f, a...) IL_DBG(IL_DL_WEP, f, ## a) #define D_HC(f, a...) IL_DBG(IL_DL_HCMD, f, ## a) #define D_HC_DUMP(f, a...) IL_DBG(IL_DL_HCMD_DUMP, f, ## a) #define D_EEPROM(f, a...) IL_DBG(IL_DL_EEPROM, f, ## a) #define D_CALIB(f, a...) IL_DBG(IL_DL_CALIB, f, ## a) #define D_FW(f, a...) IL_DBG(IL_DL_FW, f, ## a) #define D_RF_KILL(f, a...) IL_DBG(IL_DL_RF_KILL, f, ## a) #define D_DROP(f, a...) IL_DBG(IL_DL_DROP, f, ## a) #define D_AP(f, a...) IL_DBG(IL_DL_AP, f, ## a) #define D_TXPOWER(f, a...) IL_DBG(IL_DL_TXPOWER, f, ## a) #define D_RATE(f, a...) IL_DBG(IL_DL_RATE, f, ## a) #define D_NOTIF(f, a...) IL_DBG(IL_DL_NOTIF, f, ## a) #define D_ASSOC(f, a...) IL_DBG(IL_DL_ASSOC, f, ## a) #define D_HT(f, a...) IL_DBG(IL_DL_HT, f, ## a) #define D_STATS(f, a...) IL_DBG(IL_DL_STATS, f, ## a) #define D_TX_REPLY(f, a...) IL_DBG(IL_DL_TX_REPLY, f, ## a) #define D_QOS(f, a...) IL_DBG(IL_DL_QOS, f, ## a) #define D_RADIO(f, a...) IL_DBG(IL_DL_RADIO, f, ## a) #define D_POWER(f, a...) IL_DBG(IL_DL_POWER, f, ## a) #define D_11H(f, a...) IL_DBG(IL_DL_11H, f, ## a) #endif /* __il_core_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/Makefile0000644000175000017500000000073312026211315023730 0ustar mcgrofmcgrofobj-$(CONFIG_IWLEGACY) += iwlegacy.o iwlegacy-objs := common.o iwlegacy-$(CONFIG_IWLEGACY_DEBUGFS) += debug.o iwlegacy-objs += $(iwlegacy-m) # 4965 obj-$(CONFIG_COMPAT_IWL4965) += iwl4965.o iwl4965-objs := 4965.o 4965-mac.o 4965-rs.o 4965-calib.o iwl4965-$(CONFIG_IWLEGACY_DEBUGFS) += 4965-debug.o # 3945 obj-$(CONFIG_IWL3945) += iwl3945.o iwl3945-objs := 3945-mac.o 3945.o 3945-rs.o iwl3945-$(CONFIG_IWLEGACY_DEBUGFS) += 3945-debug.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/prph.h0000644000175000017500000005455312026211315023423 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifndef __il_prph_h__ #define __il_prph_h__ /* * Registers in this file are internal, not PCI bus memory mapped. * Driver accesses these via HBUS_TARG_PRPH_* registers. */ #define PRPH_BASE (0x00000) #define PRPH_END (0xFFFFF) /* APMG (power management) constants */ #define APMG_BASE (PRPH_BASE + 0x3000) #define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) #define APMG_CLK_EN_REG (APMG_BASE + 0x0004) #define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) #define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) #define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) #define APMG_RFKILL_REG (APMG_BASE + 0x0014) #define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) #define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) #define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) #define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) #define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) #define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) #define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) #define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) #define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) #define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) #define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) #define APMG_PS_CTRL_VAL_PWR_SRC_MAX (0x01000000) /* 3945 only */ #define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) #define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ #define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) /** * BSM (Bootstrap State Machine) * * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program * in special SRAM that does not power down when the embedded control * processor is sleeping (e.g. for periodic power-saving shutdowns of radio). * * When powering back up after sleeps (or during initial uCode load), the BSM * internally loads the short bootstrap program from the special SRAM into the * embedded processor's instruction SRAM, and starts the processor so it runs * the bootstrap program. * * This bootstrap program loads (via PCI busmaster DMA) instructions and data * images for a uCode program from host DRAM locations. The host driver * indicates DRAM locations and sizes for instruction and data images via the * four BSM_DRAM_* registers. Once the bootstrap program loads the new program, * the new program starts automatically. * * The uCode used for open-source drivers includes two programs: * * 1) Initialization -- performs hardware calibration and sets up some * internal data, then notifies host via "initialize alive" notification * (struct il_init_alive_resp) that it has completed all of its work. * After signal from host, it then loads and starts the runtime program. * The initialization program must be used when initially setting up the * NIC after loading the driver. * * 2) Runtime/Protocol -- performs all normal runtime operations. This * notifies host via "alive" notification (struct il_alive_resp) that it * is ready to be used. * * When initializing the NIC, the host driver does the following procedure: * * 1) Load bootstrap program (instructions only, no data image for bootstrap) * into bootstrap memory. Use dword writes starting at BSM_SRAM_LOWER_BOUND * * 2) Point (via BSM_DRAM_*) to the "initialize" uCode data and instruction * images in host DRAM. * * 3) Set up BSM to copy from BSM SRAM into uCode instruction SRAM when asked: * BSM_WR_MEM_SRC_REG = 0 * BSM_WR_MEM_DST_REG = RTC_INST_LOWER_BOUND * BSM_WR_MEM_DWCOUNT_REG = # dwords in bootstrap instruction image * * 4) Load bootstrap into instruction SRAM: * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START * * 5) Wait for load completion: * Poll BSM_WR_CTRL_REG for BSM_WR_CTRL_REG_BIT_START = 0 * * 6) Enable future boot loads whenever NIC's power management triggers it: * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START_EN * * 7) Start the NIC by removing all reset bits: * CSR_RESET = 0 * * The bootstrap uCode (already in instruction SRAM) loads initialization * uCode. Initialization uCode performs data initialization, sends * "initialize alive" notification to host, and waits for a signal from * host to load runtime code. * * 4) Point (via BSM_DRAM_*) to the "runtime" uCode data and instruction * images in host DRAM. The last register loaded must be the instruction * byte count register ("1" in MSbit tells initialization uCode to load * the runtime uCode): * BSM_DRAM_INST_BYTECOUNT_REG = byte count | BSM_DRAM_INST_LOAD * * 5) Wait for "alive" notification, then issue normal runtime commands. * * Data caching during power-downs: * * Just before the embedded controller powers down (e.g for automatic * power-saving modes, or for RFKILL), uCode stores (via PCI busmaster DMA) * a current snapshot of the embedded processor's data SRAM into host DRAM. * This caches the data while the embedded processor's memory is powered down. * Location and size are controlled by BSM_DRAM_DATA_* registers. * * NOTE: Instruction SRAM does not need to be saved, since that doesn't * change during operation; the original image (from uCode distribution * file) can be used for reload. * * When powering back up, the BSM loads the bootstrap program. Bootstrap looks * at the BSM_DRAM_* registers, which now point to the runtime instruction * image and the cached (modified) runtime data (*not* the initialization * uCode). Bootstrap reloads these runtime images into SRAM, and restarts the * uCode from where it left off before the power-down. * * NOTE: Initialization uCode does *not* run as part of the save/restore * procedure. * * This save/restore method is mostly for autonomous power management during * normal operation (result of C_POWER_TBL). Platform suspend/resume and * RFKILL should use complete restarts (with total re-initialization) of uCode, * allowing total shutdown (including BSM memory). * * Note that, during normal operation, the host DRAM that held the initial * startup data for the runtime code is now being used as a backup data cache * for modified data! If you need to completely re-initialize the NIC, make * sure that you use the runtime data image from the uCode distribution file, * not the modified/saved runtime data. You may want to store a separate * "clean" runtime data image in DRAM to avoid disk reads of distribution file. */ /* BSM bit fields */ #define BSM_WR_CTRL_REG_BIT_START (0x80000000) /* start boot load now */ #define BSM_WR_CTRL_REG_BIT_START_EN (0x40000000) /* enable boot after pwrup */ #define BSM_DRAM_INST_LOAD (0x80000000) /* start program load now */ /* BSM addresses */ #define BSM_BASE (PRPH_BASE + 0x3400) #define BSM_END (PRPH_BASE + 0x3800) #define BSM_WR_CTRL_REG (BSM_BASE + 0x000) /* ctl and status */ #define BSM_WR_MEM_SRC_REG (BSM_BASE + 0x004) /* source in BSM mem */ #define BSM_WR_MEM_DST_REG (BSM_BASE + 0x008) /* dest in SRAM mem */ #define BSM_WR_DWCOUNT_REG (BSM_BASE + 0x00C) /* bytes */ #define BSM_WR_STATUS_REG (BSM_BASE + 0x010) /* bit 0: 1 == done */ /* * Pointers and size regs for bootstrap load and data SRAM save/restore. * NOTE: 3945 pointers use bits 31:0 of DRAM address. * 4965 pointers use bits 35:4 of DRAM address. */ #define BSM_DRAM_INST_PTR_REG (BSM_BASE + 0x090) #define BSM_DRAM_INST_BYTECOUNT_REG (BSM_BASE + 0x094) #define BSM_DRAM_DATA_PTR_REG (BSM_BASE + 0x098) #define BSM_DRAM_DATA_BYTECOUNT_REG (BSM_BASE + 0x09C) /* * BSM special memory, stays powered on during power-save sleeps. * Read/write, address range from LOWER_BOUND to (LOWER_BOUND + SIZE -1) */ #define BSM_SRAM_LOWER_BOUND (PRPH_BASE + 0x3800) #define BSM_SRAM_SIZE (1024) /* bytes */ /* 3945 Tx scheduler registers */ #define ALM_SCD_BASE (PRPH_BASE + 0x2E00) #define ALM_SCD_MODE_REG (ALM_SCD_BASE + 0x000) #define ALM_SCD_ARASTAT_REG (ALM_SCD_BASE + 0x004) #define ALM_SCD_TXFACT_REG (ALM_SCD_BASE + 0x010) #define ALM_SCD_TXF4MF_REG (ALM_SCD_BASE + 0x014) #define ALM_SCD_TXF5MF_REG (ALM_SCD_BASE + 0x020) #define ALM_SCD_SBYP_MODE_1_REG (ALM_SCD_BASE + 0x02C) #define ALM_SCD_SBYP_MODE_2_REG (ALM_SCD_BASE + 0x030) /** * Tx Scheduler * * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in * host DRAM. It steers each frame's Tx command (which contains the frame * data) into one of up to 7 prioritized Tx DMA FIFO channels within the * device. A queue maps to only one (selectable by driver) Tx DMA channel, * but one DMA channel may take input from several queues. * * Tx DMA FIFOs have dedicated purposes. For 4965, they are used as follows * (cf. default_queue_to_tx_fifo in 4965.c): * * 0 -- EDCA BK (background) frames, lowest priority * 1 -- EDCA BE (best effort) frames, normal priority * 2 -- EDCA VI (video) frames, higher priority * 3 -- EDCA VO (voice) and management frames, highest priority * 4 -- Commands (e.g. RXON, etc.) * 5 -- unused (HCCA) * 6 -- unused (HCCA) * 7 -- not used by driver (device-internal only) * * * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. * In addition, driver can map the remaining queues to Tx DMA/FIFO * channels 0-3 to support 11n aggregation via EDCA DMA channels. * * The driver sets up each queue to work in one of two modes: * * 1) Scheduler-Ack, in which the scheduler automatically supports a * block-ack (BA) win of up to 64 TFDs. In this mode, each queue * contains TFDs for a unique combination of Recipient Address (RA) * and Traffic Identifier (TID), that is, traffic of a given * Quality-Of-Service (QOS) priority, destined for a single station. * * In scheduler-ack mode, the scheduler keeps track of the Tx status of * each frame within the BA win, including whether it's been transmitted, * and whether it's been acknowledged by the receiving station. The device * automatically processes block-acks received from the receiving STA, * and reschedules un-acked frames to be retransmitted (successful * Tx completion may end up being out-of-order). * * The driver must maintain the queue's Byte Count table in host DRAM * (struct il4965_sched_queue_byte_cnt_tbl) for this mode. * This mode does not support fragmentation. * * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. * The device may automatically retry Tx, but will retry only one frame * at a time, until receiving ACK from receiving station, or reaching * retry limit and giving up. * * The command queue (#4/#9) must use this mode! * This mode does not require use of the Byte Count table in host DRAM. * * Driver controls scheduler operation via 3 means: * 1) Scheduler registers * 2) Shared scheduler data base in internal 4956 SRAM * 3) Shared data in host DRAM * * Initialization: * * When loading, driver should allocate memory for: * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory * (1024 bytes for each queue). * * After receiving "Alive" response from uCode, driver must initialize * the scheduler (especially for queue #4/#9, the command queue, otherwise * the driver can't issue commands!): */ /** * Max Tx win size is the max number of contiguous TFDs that the scheduler * can keep track of at one time when creating block-ack chains of frames. * Note that "64" matches the number of ack bits in a block-ack packet. * Driver should use SCD_WIN_SIZE and SCD_FRAME_LIMIT values to initialize * IL49_SCD_CONTEXT_QUEUE_OFFSET(x) values. */ #define SCD_WIN_SIZE 64 #define SCD_FRAME_LIMIT 64 /* SCD registers are internal, must be accessed via HBUS_TARG_PRPH regs */ #define IL49_SCD_START_OFFSET 0xa02c00 /* * 4965 tells driver SRAM address for internal scheduler structs via this reg. * Value is valid only after "Alive" response from uCode. */ #define IL49_SCD_SRAM_BASE_ADDR (IL49_SCD_START_OFFSET + 0x0) /* * Driver may need to update queue-empty bits after changing queue's * write and read pointers (idxes) during (re-)initialization (i.e. when * scheduler is not tracking what's happening). * Bit fields: * 31-16: Write mask -- 1: update empty bit, 0: don't change empty bit * 15-00: Empty state, one for each queue -- 1: empty, 0: non-empty * NOTE: This register is not used by Linux driver. */ #define IL49_SCD_EMPTY_BITS (IL49_SCD_START_OFFSET + 0x4) /* * Physical base address of array of byte count (BC) circular buffers (CBs). * Each Tx queue has a BC CB in host DRAM to support Scheduler-ACK mode. * This register points to BC CB for queue 0, must be on 1024-byte boundary. * Others are spaced by 1024 bytes. * Each BC CB is 2 bytes * (256 + 64) = 740 bytes, followed by 384 bytes pad. * (Index into a queue's BC CB) = (idx into queue's TFD CB) = (SSN & 0xff). * Bit fields: * 25-00: Byte Count CB physical address [35:10], must be 1024-byte aligned. */ #define IL49_SCD_DRAM_BASE_ADDR (IL49_SCD_START_OFFSET + 0x10) /* * Enables any/all Tx DMA/FIFO channels. * Scheduler generates requests for only the active channels. * Set this to 0xff to enable all 8 channels (normal usage). * Bit fields: * 7- 0: Enable (1), disable (0), one bit for each channel 0-7 */ #define IL49_SCD_TXFACT (IL49_SCD_START_OFFSET + 0x1c) /* * Queue (x) Write Pointers (idxes, really!), one for each Tx queue. * Initialized and updated by driver as new TFDs are added to queue. * NOTE: If using Block Ack, idx must correspond to frame's * Start Sequence Number; idx = (SSN & 0xff) * NOTE: Alternative to HBUS_TARG_WRPTR, which is what Linux driver uses? */ #define IL49_SCD_QUEUE_WRPTR(x) (IL49_SCD_START_OFFSET + 0x24 + (x) * 4) /* * Queue (x) Read Pointers (idxes, really!), one for each Tx queue. * For FIFO mode, idx indicates next frame to transmit. * For Scheduler-ACK mode, idx indicates first frame in Tx win. * Initialized by driver, updated by scheduler. */ #define IL49_SCD_QUEUE_RDPTR(x) (IL49_SCD_START_OFFSET + 0x64 + (x) * 4) /* * Select which queues work in chain mode (1) vs. not (0). * Use chain mode to build chains of aggregated frames. * Bit fields: * 31-16: Reserved * 15-00: Mode, one bit for each queue -- 1: Chain mode, 0: one-at-a-time * NOTE: If driver sets up queue for chain mode, it should be also set up * Scheduler-ACK mode as well, via SCD_QUEUE_STATUS_BITS(x). */ #define IL49_SCD_QUEUECHAIN_SEL (IL49_SCD_START_OFFSET + 0xd0) /* * Select which queues interrupt driver when scheduler increments * a queue's read pointer (idx). * Bit fields: * 31-16: Reserved * 15-00: Interrupt enable, one bit for each queue -- 1: enabled, 0: disabled * NOTE: This functionality is apparently a no-op; driver relies on interrupts * from Rx queue to read Tx command responses and update Tx queues. */ #define IL49_SCD_INTERRUPT_MASK (IL49_SCD_START_OFFSET + 0xe4) /* * Queue search status registers. One for each queue. * Sets up queue mode and assigns queue to Tx DMA channel. * Bit fields: * 19-10: Write mask/enable bits for bits 0-9 * 9: Driver should init to "0" * 8: Scheduler-ACK mode (1), non-Scheduler-ACK (i.e. FIFO) mode (0). * Driver should init to "1" for aggregation mode, or "0" otherwise. * 7-6: Driver should init to "0" * 5: Window Size Left; indicates whether scheduler can request * another TFD, based on win size, etc. Driver should init * this bit to "1" for aggregation mode, or "0" for non-agg. * 4-1: Tx FIFO to use (range 0-7). * 0: Queue is active (1), not active (0). * Other bits should be written as "0" * * NOTE: If enabling Scheduler-ACK mode, chain mode should also be enabled * via SCD_QUEUECHAIN_SEL. */ #define IL49_SCD_QUEUE_STATUS_BITS(x)\ (IL49_SCD_START_OFFSET + 0x104 + (x) * 4) /* Bit field positions */ #define IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE (0) #define IL49_SCD_QUEUE_STTS_REG_POS_TXF (1) #define IL49_SCD_QUEUE_STTS_REG_POS_WSL (5) #define IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK (8) /* Write masks */ #define IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (10) #define IL49_SCD_QUEUE_STTS_REG_MSK (0x0007FC00) /** * 4965 internal SRAM structures for scheduler, shared with driver ... * * Driver should clear and initialize the following areas after receiving * "Alive" response from 4965 uCode, i.e. after initial * uCode load, or after a uCode load done for error recovery: * * SCD_CONTEXT_DATA_OFFSET (size 128 bytes) * SCD_TX_STTS_BITMAP_OFFSET (size 256 bytes) * SCD_TRANSLATE_TBL_OFFSET (size 32 bytes) * * Driver accesses SRAM via HBUS_TARG_MEM_* registers. * Driver reads base address of this scheduler area from SCD_SRAM_BASE_ADDR. * All OFFSET values must be added to this base address. */ /* * Queue context. One 8-byte entry for each of 16 queues. * * Driver should clear this entire area (size 0x80) to 0 after receiving * "Alive" notification from uCode. Additionally, driver should init * each queue's entry as follows: * * LS Dword bit fields: * 0-06: Max Tx win size for Scheduler-ACK. Driver should init to 64. * * MS Dword bit fields: * 16-22: Frame limit. Driver should init to 10 (0xa). * * Driver should init all other bits to 0. * * Init must be done after driver receives "Alive" response from 4965 uCode, * and when setting up queue for aggregation. */ #define IL49_SCD_CONTEXT_DATA_OFFSET 0x380 #define IL49_SCD_CONTEXT_QUEUE_OFFSET(x) \ (IL49_SCD_CONTEXT_DATA_OFFSET + ((x) * 8)) #define IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS (0) #define IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK (0x0000007F) #define IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) #define IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) /* * Tx Status Bitmap * * Driver should clear this entire area (size 0x100) to 0 after receiving * "Alive" notification from uCode. Area is used only by device itself; * no other support (besides clearing) is required from driver. */ #define IL49_SCD_TX_STTS_BITMAP_OFFSET 0x400 /* * RAxTID to queue translation mapping. * * When queue is in Scheduler-ACK mode, frames placed in a that queue must be * for only one combination of receiver address (RA) and traffic ID (TID), i.e. * one QOS priority level destined for one station (for this wireless link, * not final destination). The SCD_TRANSLATE_TBL area provides 16 16-bit * mappings, one for each of the 16 queues. If queue is not in Scheduler-ACK * mode, the device ignores the mapping value. * * Bit fields, for each 16-bit map: * 15-9: Reserved, set to 0 * 8-4: Index into device's station table for recipient station * 3-0: Traffic ID (tid), range 0-15 * * Driver should clear this entire area (size 32 bytes) to 0 after receiving * "Alive" notification from uCode. To update a 16-bit map value, driver * must read a dword-aligned value from device SRAM, replace the 16-bit map * value of interest, and write the dword value back into device SRAM. */ #define IL49_SCD_TRANSLATE_TBL_OFFSET 0x500 /* Find translation table dword to read/write for given queue */ #define IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \ ((IL49_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc) #define IL_SCD_TXFIFO_POS_TID (0) #define IL_SCD_TXFIFO_POS_RA (4) #define IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) /*********************** END TX SCHEDULER *************************************/ #endif /* __il_prph_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/iwl-spectrum.h0000644000175000017500000000503212026211315025071 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __il_spectrum_h__ #define __il_spectrum_h__ enum { /* ieee80211_basic_report.map */ IEEE80211_BASIC_MAP_BSS = (1 << 0), IEEE80211_BASIC_MAP_OFDM = (1 << 1), IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), IEEE80211_BASIC_MAP_RADAR = (1 << 3), IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), /* Bits 5-7 are reserved */ }; struct ieee80211_basic_report { u8 channel; __le64 start_time; __le16 duration; u8 map; } __packed; enum { /* ieee80211_measurement_request.mode */ /* Bit 0 is reserved */ IEEE80211_MEASUREMENT_ENABLE = (1 << 1), IEEE80211_MEASUREMENT_REQUEST = (1 << 2), IEEE80211_MEASUREMENT_REPORT = (1 << 3), /* Bits 4-7 are reserved */ }; enum { IEEE80211_REPORT_BASIC = 0, /* required */ IEEE80211_REPORT_CCA = 1, /* optional */ IEEE80211_REPORT_RPI = 2, /* optional */ /* 3-255 reserved */ }; struct ieee80211_measurement_params { u8 channel; __le64 start_time; __le16 duration; } __packed; struct ieee80211_info_element { u8 id; u8 len; u8 data[0]; } __packed; struct ieee80211_measurement_request { struct ieee80211_info_element ie; u8 token; u8 mode; u8 type; struct ieee80211_measurement_params params[0]; } __packed; struct ieee80211_measurement_report { struct ieee80211_info_element ie; u8 token; u8 mode; u8 type; union { struct ieee80211_basic_report basic[0]; } u; } __packed; #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/debug.c0000644000175000017500000011573712026211315023535 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include #include #include #include "common.h" void il_clear_traffic_stats(struct il_priv *il) { memset(&il->tx_stats, 0, sizeof(struct traffic_stats)); memset(&il->rx_stats, 0, sizeof(struct traffic_stats)); } /* * il_update_stats function record all the MGMT, CTRL and DATA pkt for * both TX and Rx . Use debugfs to display the rx/rx_stats */ void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) { struct traffic_stats *stats; if (is_tx) stats = &il->tx_stats; else stats = &il->rx_stats; if (ieee80211_is_mgmt(fc)) { switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): stats->mgmt[MANAGEMENT_ASSOC_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): stats->mgmt[MANAGEMENT_ASSOC_RESP]++; break; case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): stats->mgmt[MANAGEMENT_REASSOC_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): stats->mgmt[MANAGEMENT_REASSOC_RESP]++; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): stats->mgmt[MANAGEMENT_PROBE_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): stats->mgmt[MANAGEMENT_PROBE_RESP]++; break; case cpu_to_le16(IEEE80211_STYPE_BEACON): stats->mgmt[MANAGEMENT_BEACON]++; break; case cpu_to_le16(IEEE80211_STYPE_ATIM): stats->mgmt[MANAGEMENT_ATIM]++; break; case cpu_to_le16(IEEE80211_STYPE_DISASSOC): stats->mgmt[MANAGEMENT_DISASSOC]++; break; case cpu_to_le16(IEEE80211_STYPE_AUTH): stats->mgmt[MANAGEMENT_AUTH]++; break; case cpu_to_le16(IEEE80211_STYPE_DEAUTH): stats->mgmt[MANAGEMENT_DEAUTH]++; break; case cpu_to_le16(IEEE80211_STYPE_ACTION): stats->mgmt[MANAGEMENT_ACTION]++; break; } } else if (ieee80211_is_ctl(fc)) { switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { case cpu_to_le16(IEEE80211_STYPE_BACK_REQ): stats->ctrl[CONTROL_BACK_REQ]++; break; case cpu_to_le16(IEEE80211_STYPE_BACK): stats->ctrl[CONTROL_BACK]++; break; case cpu_to_le16(IEEE80211_STYPE_PSPOLL): stats->ctrl[CONTROL_PSPOLL]++; break; case cpu_to_le16(IEEE80211_STYPE_RTS): stats->ctrl[CONTROL_RTS]++; break; case cpu_to_le16(IEEE80211_STYPE_CTS): stats->ctrl[CONTROL_CTS]++; break; case cpu_to_le16(IEEE80211_STYPE_ACK): stats->ctrl[CONTROL_ACK]++; break; case cpu_to_le16(IEEE80211_STYPE_CFEND): stats->ctrl[CONTROL_CFEND]++; break; case cpu_to_le16(IEEE80211_STYPE_CFENDACK): stats->ctrl[CONTROL_CFENDACK]++; break; } } else { /* data */ stats->data_cnt++; stats->data_bytes += len; } } EXPORT_SYMBOL(il_update_stats); /* create and remove of files */ #define DEBUGFS_ADD_FILE(name, parent, mode) do { \ if (!debugfs_create_file(#name, mode, parent, il, \ &il_dbgfs_##name##_ops)) \ goto err; \ } while (0) #define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ struct dentry *__tmp; \ __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ parent, ptr); \ if (IS_ERR(__tmp) || !__tmp) \ goto err; \ } while (0) #define DEBUGFS_ADD_X32(name, parent, ptr) do { \ struct dentry *__tmp; \ __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ parent, ptr); \ if (IS_ERR(__tmp) || !__tmp) \ goto err; \ } while (0) /* file operation */ #define DEBUGFS_READ_FUNC(name) \ static ssize_t il_dbgfs_##name##_read(struct file *file, \ char __user *user_buf, \ size_t count, loff_t *ppos); #define DEBUGFS_WRITE_FUNC(name) \ static ssize_t il_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos); #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations il_dbgfs_##name##_ops = { \ .read = il_dbgfs_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_WRITE_FILE_OPS(name) \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations il_dbgfs_##name##_ops = { \ .write = il_dbgfs_##name##_write, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_READ_WRITE_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations il_dbgfs_##name##_ops = { \ .write = il_dbgfs_##name##_write, \ .read = il_dbgfs_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; static const char * il_get_mgmt_string(int cmd) { switch (cmd) { IL_CMD(MANAGEMENT_ASSOC_REQ); IL_CMD(MANAGEMENT_ASSOC_RESP); IL_CMD(MANAGEMENT_REASSOC_REQ); IL_CMD(MANAGEMENT_REASSOC_RESP); IL_CMD(MANAGEMENT_PROBE_REQ); IL_CMD(MANAGEMENT_PROBE_RESP); IL_CMD(MANAGEMENT_BEACON); IL_CMD(MANAGEMENT_ATIM); IL_CMD(MANAGEMENT_DISASSOC); IL_CMD(MANAGEMENT_AUTH); IL_CMD(MANAGEMENT_DEAUTH); IL_CMD(MANAGEMENT_ACTION); default: return "UNKNOWN"; } } static const char * il_get_ctrl_string(int cmd) { switch (cmd) { IL_CMD(CONTROL_BACK_REQ); IL_CMD(CONTROL_BACK); IL_CMD(CONTROL_PSPOLL); IL_CMD(CONTROL_RTS); IL_CMD(CONTROL_CTS); IL_CMD(CONTROL_ACK); IL_CMD(CONTROL_CFEND); IL_CMD(CONTROL_CFENDACK); default: return "UNKNOWN"; } } static ssize_t il_dbgfs_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char *buf; int pos = 0; int cnt; ssize_t ret; const size_t bufsz = 100 + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", il_get_mgmt_string(cnt), il->tx_stats.mgmt[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "Control\n"); for (cnt = 0; cnt < CONTROL_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", il_get_ctrl_string(cnt), il->tx_stats.ctrl[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", il->tx_stats.data_cnt); pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", il->tx_stats.data_bytes); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_clear_traffic_stats_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; u32 clear_flag; char buf[8]; int buf_size; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &clear_flag) != 1) return -EFAULT; il_clear_traffic_stats(il); return count; } static ssize_t il_dbgfs_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char *buf; int pos = 0; int cnt; ssize_t ret; const size_t bufsz = 100 + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", il_get_mgmt_string(cnt), il->rx_stats.mgmt[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "Control:\n"); for (cnt = 0; cnt < CONTROL_MAX; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", il_get_ctrl_string(cnt), il->rx_stats.ctrl[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", il->rx_stats.data_cnt); pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", il->rx_stats.data_bytes); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } #define BYTE1_MASK 0x000000ff; #define BYTE2_MASK 0x0000ffff; #define BYTE3_MASK 0x00ffffff; static ssize_t il_dbgfs_sram_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { u32 val; char *buf; ssize_t ret; int i; int pos = 0; struct il_priv *il = file->private_data; size_t bufsz; /* default is to dump the entire data segment */ if (!il->dbgfs_sram_offset && !il->dbgfs_sram_len) { il->dbgfs_sram_offset = 0x800000; if (il->ucode_type == UCODE_INIT) il->dbgfs_sram_len = il->ucode_init_data.len; else il->dbgfs_sram_len = il->ucode_data.len; } bufsz = 30 + il->dbgfs_sram_len * sizeof(char) * 10; buf = kmalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", il->dbgfs_sram_len); pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", il->dbgfs_sram_offset); for (i = il->dbgfs_sram_len; i > 0; i -= 4) { val = il_read_targ_mem(il, il->dbgfs_sram_offset + il->dbgfs_sram_len - i); if (i < 4) { switch (i) { case 1: val &= BYTE1_MASK; break; case 2: val &= BYTE2_MASK; break; case 3: val &= BYTE3_MASK; break; } } if (!(i % 16)) pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_sram_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[64]; int buf_size; u32 offset, len; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x,%x", &offset, &len) == 2) { il->dbgfs_sram_offset = offset; il->dbgfs_sram_len = len; } else { il->dbgfs_sram_offset = 0; il->dbgfs_sram_len = 0; } return count; } static ssize_t il_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; struct il_station_entry *station; int max_sta = il->hw_params.max_stations; char *buf; int i, j, pos = 0; ssize_t ret; /* Add 30 for initial string */ const size_t bufsz = 30 + sizeof(char) * 500 * (il->num_stations); buf = kmalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", il->num_stations); for (i = 0; i < max_sta; i++) { station = &il->stations[i]; if (!station->used) continue; pos += scnprintf(buf + pos, bufsz - pos, "station %d - addr: %pM, flags: %#x\n", i, station->sta.sta.addr, station->sta.station_flags_msk); pos += scnprintf(buf + pos, bufsz - pos, "TID\tseq_num\ttxq_id\tframes\ttfds\t"); pos += scnprintf(buf + pos, bufsz - pos, "start_idx\tbitmap\t\t\trate_n_flags\n"); for (j = 0; j < MAX_TID_COUNT; j++) { pos += scnprintf(buf + pos, bufsz - pos, "%d:\t%#x\t%#x\t%u\t%u\t%u\t\t%#.16llx\t%#x", j, station->tid[j].seq_number, station->tid[j].agg.txq_id, station->tid[j].agg.frame_count, station->tid[j].tfds_in_queue, station->tid[j].agg.start_idx, station->tid[j].agg.bitmap, station->tid[j].agg.rate_n_flags); if (station->tid[j].agg.wait_for_ba) pos += scnprintf(buf + pos, bufsz - pos, " - waitforba"); pos += scnprintf(buf + pos, bufsz - pos, "\n"); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_nvm_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { ssize_t ret; struct il_priv *il = file->private_data; int pos = 0, ofs = 0, buf_size = 0; const u8 *ptr; char *buf; u16 eeprom_ver; size_t eeprom_len = il->cfg->eeprom_size; buf_size = 4 * eeprom_len + 256; if (eeprom_len % 16) { IL_ERR("NVM size is not multiple of 16.\n"); return -ENODATA; } ptr = il->eeprom; if (!ptr) { IL_ERR("Invalid EEPROM memory\n"); return -ENOMEM; } /* 4 characters for byte 0xYY */ buf = kzalloc(buf_size, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); pos += scnprintf(buf + pos, buf_size - pos, "EEPROM " "version: 0x%x\n", eeprom_ver); for (ofs = 0; ofs < eeprom_len; ofs += 16) { pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16, 16, 2, buf + pos, buf_size - pos, 0); pos += strlen(buf + pos); if (buf_size - pos > 0) buf[pos++] = '\n'; } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; struct ieee80211_channel *channels = NULL; const struct ieee80211_supported_band *supp_band = NULL; int pos = 0, i, bufsz = PAGE_SIZE; char *buf; ssize_t ret; if (!test_bit(S_GEO_CONFIGURED, &il->status)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } supp_band = il_get_hw_mode(il, IEEE80211_BAND_2GHZ); if (supp_band) { channels = supp_band->channels; pos += scnprintf(buf + pos, bufsz - pos, "Displaying %d channels in 2.4GHz band 802.11bg):\n", supp_band->n_channels); for (i = 0; i < supp_band->n_channels; i++) pos += scnprintf(buf + pos, bufsz - pos, "%d: %ddBm: BSS%s%s, %s.\n", channels[i].hw_value, channels[i].max_power, channels[i]. flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", ((channels[i]. flags & IEEE80211_CHAN_NO_IBSS) || (channels[i]. flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i]. flags & IEEE80211_CHAN_PASSIVE_SCAN ? "passive only" : "active/passive"); } supp_band = il_get_hw_mode(il, IEEE80211_BAND_5GHZ); if (supp_band) { channels = supp_band->channels; pos += scnprintf(buf + pos, bufsz - pos, "Displaying %d channels in 5.2GHz band (802.11a)\n", supp_band->n_channels); for (i = 0; i < supp_band->n_channels; i++) pos += scnprintf(buf + pos, bufsz - pos, "%d: %ddBm: BSS%s%s, %s.\n", channels[i].hw_value, channels[i].max_power, channels[i]. flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", ((channels[i]. flags & IEEE80211_CHAN_NO_IBSS) || (channels[i]. flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i]. flags & IEEE80211_CHAN_PASSIVE_SCAN ? "passive only" : "active/passive"); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[512]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "S_HCMD_ACTIVE:\t %d\n", test_bit(S_HCMD_ACTIVE, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_INT_ENABLED:\t %d\n", test_bit(S_INT_ENABLED, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_RFKILL:\t %d\n", test_bit(S_RFKILL, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_CT_KILL:\t\t %d\n", test_bit(S_CT_KILL, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_INIT:\t\t %d\n", test_bit(S_INIT, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_ALIVE:\t\t %d\n", test_bit(S_ALIVE, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_READY:\t\t %d\n", test_bit(S_READY, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_TEMPERATURE:\t %d\n", test_bit(S_TEMPERATURE, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_GEO_CONFIGURED:\t %d\n", test_bit(S_GEO_CONFIGURED, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_EXIT_PENDING:\t %d\n", test_bit(S_EXIT_PENDING, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_STATS:\t %d\n", test_bit(S_STATS, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_SCANNING:\t %d\n", test_bit(S_SCANNING, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_SCAN_ABORTING:\t %d\n", test_bit(S_SCAN_ABORTING, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_SCAN_HW:\t\t %d\n", test_bit(S_SCAN_HW, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_POWER_PMI:\t %d\n", test_bit(S_POWER_PMI, &il->status)); pos += scnprintf(buf + pos, bufsz - pos, "S_FW_ERROR:\t %d\n", test_bit(S_FW_ERROR, &il->status)); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t il_dbgfs_interrupt_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; int cnt = 0; char *buf; int bufsz = 24 * 64; /* 24 items * 64 char per item */ ssize_t ret; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } pos += scnprintf(buf + pos, bufsz - pos, "Interrupt Statistics Report:\n"); pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", il->isr_stats.hw); pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", il->isr_stats.sw); if (il->isr_stats.sw || il->isr_stats.hw) { pos += scnprintf(buf + pos, bufsz - pos, "\tLast Restarting Code: 0x%X\n", il->isr_stats.err_code); } #ifdef CONFIG_IWLEGACY_DEBUG pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", il->isr_stats.sch); pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", il->isr_stats.alive); #endif pos += scnprintf(buf + pos, bufsz - pos, "HW RF KILL switch toggled:\t %u\n", il->isr_stats.rfkill); pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", il->isr_stats.ctkill); pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", il->isr_stats.wakeup); pos += scnprintf(buf + pos, bufsz - pos, "Rx command responses:\t\t %u\n", il->isr_stats.rx); for (cnt = 0; cnt < IL_CN_MAX; cnt++) { if (il->isr_stats.handlers[cnt] > 0) pos += scnprintf(buf + pos, bufsz - pos, "\tRx handler[%36s]:\t\t %u\n", il_get_cmd_string(cnt), il->isr_stats.handlers[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", il->isr_stats.tx); pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", il->isr_stats.unhandled); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_interrupt_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[8]; int buf_size; u32 reset_flag; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &reset_flag) != 1) return -EFAULT; if (reset_flag == 0) il_clear_isr_stats(il); return count; } static ssize_t il_dbgfs_qos_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0, i; char buf[256]; const size_t bufsz = sizeof(buf); for (i = 0; i < AC_NUM; i++) { pos += scnprintf(buf + pos, bufsz - pos, "\tcw_min\tcw_max\taifsn\ttxop\n"); pos += scnprintf(buf + pos, bufsz - pos, "AC[%d]\t%u\t%u\t%u\t%u\n", i, il->qos_data.def_qos_parm.ac[i].cw_min, il->qos_data.def_qos_parm.ac[i].cw_max, il->qos_data.def_qos_parm.ac[i].aifsn, il->qos_data.def_qos_parm.ac[i].edca_txop); } return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t il_dbgfs_disable_ht40_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[8]; int buf_size; int ht40; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &ht40) != 1) return -EFAULT; if (!il_is_any_associated(il)) il->disable_ht40 = ht40 ? true : false; else { IL_ERR("Sta associated with AP - " "Change to 40MHz channel support is not allowed\n"); return -EINVAL; } return count; } static ssize_t il_dbgfs_disable_ht40_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[100]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "11n 40MHz Mode: %s\n", il->disable_ht40 ? "Disabled" : "Enabled"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } DEBUGFS_READ_WRITE_FILE_OPS(sram); DEBUGFS_READ_FILE_OPS(nvm); DEBUGFS_READ_FILE_OPS(stations); DEBUGFS_READ_FILE_OPS(channels); DEBUGFS_READ_FILE_OPS(status); DEBUGFS_READ_WRITE_FILE_OPS(interrupt); DEBUGFS_READ_FILE_OPS(qos); DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); static ssize_t il_dbgfs_tx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; struct il_tx_queue *txq; struct il_queue *q; char *buf; int pos = 0; int cnt; int ret; const size_t bufsz = sizeof(char) * 64 * il->cfg->num_of_queues; if (!il->txq) { IL_ERR("txq not ready\n"); return -EAGAIN; } buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { txq = &il->txq[cnt]; q = &txq->q; pos += scnprintf(buf + pos, bufsz - pos, "hwq %.2d: read=%u write=%u stop=%d" " swq_id=%#.2x (ac %d/hwq %d)\n", cnt, q->read_ptr, q->write_ptr, !!test_bit(cnt, il->queue_stopped), txq->swq_id, txq->swq_id & 3, (txq->swq_id >> 2) & 0x1f); if (cnt >= 4) continue; /* for the ACs, display the stop count too */ pos += scnprintf(buf + pos, bufsz - pos, " stop-count: %d\n", atomic_read(&il->queue_stop_count[cnt])); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_rx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; struct il_rx_queue *rxq = &il->rxq; char buf[256]; int pos = 0; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", rxq->read); pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", rxq->write); pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", rxq->free_count); if (rxq->rb_stts) { pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", le16_to_cpu(rxq->rb_stts-> closed_rb_num) & 0x0FFF); } else { pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: Not Allocated\n"); } return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t il_dbgfs_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; return il->debugfs_ops->rx_stats_read(file, user_buf, count, ppos); } static ssize_t il_dbgfs_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; return il->debugfs_ops->tx_stats_read(file, user_buf, count, ppos); } static ssize_t il_dbgfs_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; return il->debugfs_ops->general_stats_read(file, user_buf, count, ppos); } static ssize_t il_dbgfs_sensitivity_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; int cnt = 0; char *buf; int bufsz = sizeof(struct il_sensitivity_data) * 4 + 100; ssize_t ret; struct il_sensitivity_data *data; data = &il->sensitivity_data; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", data->auto_corr_ofdm); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc:\t\t %u\n", data->auto_corr_ofdm_mrc); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", data->auto_corr_ofdm_x1); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc_x1:\t\t %u\n", data->auto_corr_ofdm_mrc_x1); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", data->auto_corr_cck); pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", data->auto_corr_cck_mrc); pos += scnprintf(buf + pos, bufsz - pos, "last_bad_plcp_cnt_ofdm:\t\t %u\n", data->last_bad_plcp_cnt_ofdm); pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", data->last_fa_cnt_ofdm); pos += scnprintf(buf + pos, bufsz - pos, "last_bad_plcp_cnt_cck:\t\t %u\n", data->last_bad_plcp_cnt_cck); pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", data->last_fa_cnt_cck); pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", data->nrg_curr_state); pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", data->nrg_prev_state); pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); for (cnt = 0; cnt < 10; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->nrg_value[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->nrg_silence_rssi[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", data->nrg_silence_ref); pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", data->nrg_energy_idx); pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", data->nrg_silence_idx); pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", data->nrg_th_cck); pos += scnprintf(buf + pos, bufsz - pos, "nrg_auto_corr_silence_diff:\t %u\n", data->nrg_auto_corr_silence_diff); pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", data->num_in_cck_no_fa); pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", data->nrg_th_ofdm); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_chain_noise_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; int cnt = 0; char *buf; int bufsz = sizeof(struct il_chain_noise_data) * 4 + 100; ssize_t ret; struct il_chain_noise_data *data; data = &il->chain_noise_data; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", data->active_chains); pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", data->chain_noise_a); pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", data->chain_noise_b); pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", data->chain_noise_c); pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", data->chain_signal_a); pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", data->chain_signal_b); pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", data->chain_signal_c); pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", data->beacon_count); pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->disconn_array[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { pos += scnprintf(buf + pos, bufsz - pos, " %u", data->delta_gain_code[cnt]); } pos += scnprintf(buf + pos, bufsz - pos, "\n"); pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", data->radio_write); pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", data->state); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } static ssize_t il_dbgfs_power_save_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[60]; int pos = 0; const size_t bufsz = sizeof(buf); u32 pwrsave_status; pwrsave_status = _il_rd(il, CSR_GP_CNTRL) & CSR_GP_REG_POWER_SAVE_STATUS_MSK; pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); pos += scnprintf(buf + pos, bufsz - pos, "%s\n", (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : "error"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t il_dbgfs_clear_ucode_stats_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[8]; int buf_size; int clear; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &clear) != 1) return -EFAULT; /* make request to uCode to retrieve stats information */ mutex_lock(&il->mutex); il_send_stats_request(il, CMD_SYNC, true); mutex_unlock(&il->mutex); return count; } static ssize_t il_dbgfs_rxon_flags_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int len = 0; char buf[20]; len = sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.flags)); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t il_dbgfs_rxon_filter_flags_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int len = 0; char buf[20]; len = sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.filter_flags)); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t il_dbgfs_fh_reg_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char *buf; int pos = 0; ssize_t ret = -EFAULT; if (il->ops->dump_fh) { ret = pos = il->ops->dump_fh(il, &buf, true); if (buf) { ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); } } return ret; } static ssize_t il_dbgfs_missed_beacon_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char buf[12]; const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "%d\n", il->missed_beacon_threshold); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t il_dbgfs_missed_beacon_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[8]; int buf_size; int missed; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &missed) != 1) return -EINVAL; if (missed < IL_MISSED_BEACON_THRESHOLD_MIN || missed > IL_MISSED_BEACON_THRESHOLD_MAX) il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; else il->missed_beacon_threshold = missed; return count; } static ssize_t il_dbgfs_force_reset_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char buf[300]; const size_t bufsz = sizeof(buf); struct il_force_reset *force_reset; force_reset = &il->force_reset; pos += scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request: %d\n", force_reset->reset_request_count); pos += scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request success: %d\n", force_reset->reset_success_count); pos += scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request reject: %d\n", force_reset->reset_reject_count); pos += scnprintf(buf + pos, bufsz - pos, "\treset duration: %lu\n", force_reset->reset_duration); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t il_dbgfs_force_reset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { int ret; struct il_priv *il = file->private_data; ret = il_force_reset(il, true); return ret ? ret : count; } static ssize_t il_dbgfs_wd_timeout_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; char buf[8]; int buf_size; int timeout; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%d", &timeout) != 1) return -EINVAL; if (timeout < 0 || timeout > IL_MAX_WD_TIMEOUT) timeout = IL_DEF_WD_TIMEOUT; il->cfg->wd_timeout = timeout; il_setup_watchdog(il); return count; } DEBUGFS_READ_FILE_OPS(rx_stats); DEBUGFS_READ_FILE_OPS(tx_stats); DEBUGFS_READ_FILE_OPS(rx_queue); DEBUGFS_READ_FILE_OPS(tx_queue); DEBUGFS_READ_FILE_OPS(ucode_rx_stats); DEBUGFS_READ_FILE_OPS(ucode_tx_stats); DEBUGFS_READ_FILE_OPS(ucode_general_stats); DEBUGFS_READ_FILE_OPS(sensitivity); DEBUGFS_READ_FILE_OPS(chain_noise); DEBUGFS_READ_FILE_OPS(power_save_status); DEBUGFS_WRITE_FILE_OPS(clear_ucode_stats); DEBUGFS_WRITE_FILE_OPS(clear_traffic_stats); DEBUGFS_READ_FILE_OPS(fh_reg); DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); DEBUGFS_READ_WRITE_FILE_OPS(force_reset); DEBUGFS_READ_FILE_OPS(rxon_flags); DEBUGFS_READ_FILE_OPS(rxon_filter_flags); DEBUGFS_WRITE_FILE_OPS(wd_timeout); /* * Create the debugfs files and directories * */ int il_dbgfs_register(struct il_priv *il, const char *name) { struct dentry *phyd = il->hw->wiphy->debugfsdir; struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; dir_drv = debugfs_create_dir(name, phyd); if (!dir_drv) return -ENOMEM; il->debugfs_dir = dir_drv; dir_data = debugfs_create_dir("data", dir_drv); if (!dir_data) goto err; dir_rf = debugfs_create_dir("rf", dir_drv); if (!dir_rf) goto err; dir_debug = debugfs_create_dir("debug", dir_drv); if (!dir_debug) goto err; DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(interrupt, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(rx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(tx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(rx_queue, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(tx_queue, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(clear_ucode_stats, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(clear_traffic_stats, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); if (il->cfg->sensitivity_calib_by_driver) DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); if (il->cfg->chain_noise_calib_by_driver) DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR); if (il->cfg->sensitivity_calib_by_driver) DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf, &il->disable_sens_cal); if (il->cfg->chain_noise_calib_by_driver) DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf, &il->disable_chain_noise_cal); DEBUGFS_ADD_BOOL(disable_tx_power, dir_rf, &il->disable_tx_power_cal); return 0; err: IL_ERR("Can't create the debugfs directory\n"); il_dbgfs_unregister(il); return -ENOMEM; } EXPORT_SYMBOL(il_dbgfs_register); /** * Remove the debugfs files and directories * */ void il_dbgfs_unregister(struct il_priv *il) { if (!il->debugfs_dir) return; debugfs_remove_recursive(il->debugfs_dir); il->debugfs_dir = NULL; } EXPORT_SYMBOL(il_dbgfs_unregister); compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/csr.h0000644000175000017500000004337312026211315023237 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __il_csr_h__ #define __il_csr_h__ /* * CSR (control and status registers) * * CSR registers are mapped directly into PCI bus space, and are accessible * whenever platform supplies power to device, even when device is in * low power states due to driver-invoked device resets * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. * * Use _il_wr() and _il_rd() family to access these registers; * these provide simple PCI bus access, without waking up the MAC. * Do not use il_wr() family for these registers; * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. * The MAC (uCode processor, etc.) does not need to be powered up for accessing * the CSR registers. * * NOTE: Device does need to be awake in order to read this memory * via CSR_EEPROM register */ #define CSR_BASE (0x000) #define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ #define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ #define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ #define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ #define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack */ #define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */ #define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc */ #define CSR_GP_CNTRL (CSR_BASE+0x024) /* 2nd byte of CSR_INT_COALESCING, not accessible via _il_wr()! */ #define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) /* * Hardware revision info * Bit fields: * 31-8: Reserved * 7-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D * 1-0: "Dash" (-) value, as in A-1, etc. * * NOTE: Revision step affects calculation of CCK txpower for 4965. * NOTE: See also CSR_HW_REV_WA_REG (work-around for bug in 4965). */ #define CSR_HW_REV (CSR_BASE+0x028) /* * EEPROM memory reads * * NOTE: Device must be awake, initialized via apm_ops.init(), * in order to read. */ #define CSR_EEPROM_REG (CSR_BASE+0x02c) #define CSR_EEPROM_GP (CSR_BASE+0x030) #define CSR_GIO_REG (CSR_BASE+0x03C) #define CSR_GP_UCODE_REG (CSR_BASE+0x048) #define CSR_GP_DRIVER_REG (CSR_BASE+0x050) /* * UCODE-DRIVER GP (general purpose) mailbox registers. * SET/CLR registers set/clear bit(s) if "1" is written. */ #define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) #define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) #define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) #define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) #define CSR_LED_REG (CSR_BASE+0x094) #define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) /* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) /* Analog phase-lock-loop configuration */ #define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) /* * CSR Hardware Revision Workaround Register. Indicates hardware rev; * "step" determines CCK backoff for txpower calculation. Used for 4965 only. * See also CSR_HW_REV register. * Bit fields: * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step * 1-0: "Dash" (-) value, as in C-1, etc. */ #define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) #define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) #define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) /* Bits for CSR_HW_IF_CONFIG_REG */ #define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010) #define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00) #define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) #define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) #define CSR39_HW_IF_CONFIG_REG_BIT_3945_MB (0x00000100) #define CSR39_HW_IF_CONFIG_REG_BIT_3945_MM (0x00000200) #define CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC (0x00000400) #define CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE (0x00000800) #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000) #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000) #define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) #define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ #define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ #define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int */ #define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec */ /* interrupt flags in INTA, set by uCode or hardware (e.g. dma), * acknowledged (reset) by host writing "1" to flagged bits. */ #define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ #define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ #define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ #define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ #define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ #define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ #define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ #define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ #define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses, 3945 */ #define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ #define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ #define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ CSR_INT_BIT_HW_ERR | \ CSR_INT_BIT_FH_TX | \ CSR_INT_BIT_SW_ERR | \ CSR_INT_BIT_RF_KILL | \ CSR_INT_BIT_SW_RX | \ CSR_INT_BIT_WAKEUP | \ CSR_INT_BIT_ALIVE) /* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ #define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ #define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ #define CSR39_FH_INT_BIT_RX_CHNL2 (1 << 18) /* Rx channel 2 (3945 only) */ #define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ #define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ #define CSR39_FH_INT_BIT_TX_CHNL6 (1 << 6) /* Tx channel 6 (3945 only) */ #define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ #define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ #define CSR39_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ CSR39_FH_INT_BIT_RX_CHNL2 | \ CSR_FH_INT_BIT_RX_CHNL1 | \ CSR_FH_INT_BIT_RX_CHNL0) #define CSR39_FH_INT_TX_MASK (CSR39_FH_INT_BIT_TX_CHNL6 | \ CSR_FH_INT_BIT_TX_CHNL1 | \ CSR_FH_INT_BIT_TX_CHNL0) #define CSR49_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ CSR_FH_INT_BIT_RX_CHNL1 | \ CSR_FH_INT_BIT_RX_CHNL0) #define CSR49_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ CSR_FH_INT_BIT_TX_CHNL0) /* GPIO */ #define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) #define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) #define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) /* RESET */ #define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) #define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) #define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) #define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) #define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) #define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) /* * GP (general purpose) CONTROL REGISTER * Bit fields: * 27: HW_RF_KILL_SW * Indicates state of (platform's) hardware RF-Kill switch * 26-24: POWER_SAVE_TYPE * Indicates current power-saving mode: * 000 -- No power saving * 001 -- MAC power-down * 010 -- PHY (radio) power-down * 011 -- Error * 9-6: SYS_CONFIG * Indicates current system configuration, reflecting pins on chip * as forced high/low by device circuit board. * 4: GOING_TO_SLEEP * Indicates MAC is entering a power-saving sleep power-down. * Not a good time to access device-internal resources. * 3: MAC_ACCESS_REQ * Host sets this to request and maintain MAC wakeup, to allow host * access to device-internal resources. Host must wait for * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR * device registers. * 2: INIT_DONE * Host sets this to put device into fully operational D0 power mode. * Host resets this after SW_RESET to put device into low power mode. * 0: MAC_CLOCK_READY * Indicates MAC (ucode processor, etc.) is powered up and can run. * Internal resources are accessible. * NOTE: This does not indicate that the processor is actually running. * NOTE: This does not indicate that 4965 or 3945 has completed * init or post-power-down restore of internal SRAM memory. * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that * SRAM is restored and uCode is in normal operation mode. * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and * do not need to save/restore it. * NOTE: After device reset, this bit remains "0" until host sets * INIT_DONE */ #define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) #define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) #define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) #define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) #define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) #define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) #define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) #define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) /* EEPROM REG */ #define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) #define CSR_EEPROM_REG_BIT_CMD (0x00000002) #define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) #define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) /* EEPROM GP */ #define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ #define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) #define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) #define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) /* GP REG */ #define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ #define CSR_GP_REG_NO_POWER_SAVE (0x00000000) #define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) #define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) #define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) /* CSR GIO */ #define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) /* * UCODE-DRIVER GP (general purpose) mailbox register 1 * Host driver and uCode write and/or read this register to communicate with * each other. * Bit fields: * 4: UCODE_DISABLE * Host sets this to request permanent halt of uCode, same as * sending CARD_STATE command with "halt" bit set. * 3: CT_KILL_EXIT * Host sets this to request exit from CT_KILL state, i.e. host thinks * device temperature is low enough to continue normal operation. * 2: CMD_BLOCKED * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) * to release uCode to clear all Tx and command queues, enter * unassociated mode, and power down. * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. * 1: SW_BIT_RFKILL * Host sets this when issuing CARD_STATE command to request * device sleep. * 0: MAC_SLEEP * uCode sets this when preparing a power-saving power-down. * uCode resets this when power-up is complete and SRAM is sane. * NOTE: 3945/4965 saves internal SRAM data to host when powering down, * and must restore this data after powering back up. * MAC_SLEEP is the best indication that restore is complete. * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and * do not need to save/restore it. */ #define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) #define CSR_UCODE_SW_BIT_RFKILL (0x00000002) #define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) #define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) /* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) #define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) /* LED */ #define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) #define CSR_LED_REG_TRUN_ON (0x78) #define CSR_LED_REG_TRUN_OFF (0x38) /* ANA_PLL */ #define CSR39_ANA_PLL_CFG_VAL (0x01000000) /* HPET MEM debug */ #define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) /* DRAM INT TBL */ #define CSR_DRAM_INT_TBL_ENABLE (1 << 31) #define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) /* * HBUS (Host-side Bus) * * HBUS registers are mapped directly into PCI bus space, but are used * to indirectly access device's internal memory or registers that * may be powered-down. * * Use il_wr()/il_rd() family * for these registers; * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ * to make sure the MAC (uCode processor, etc.) is powered up for accessing * internal resources. * * Do not use _il_wr()/_il_rd() family to access these registers; * these provide only simple PCI bus access, without waking up the MAC. */ #define HBUS_BASE (0x400) /* * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM * structures, error log, event log, verifying uCode load). * First write to address register, then read from or write to data register * to complete the job. Once the address register is set up, accesses to * data registers auto-increment the address by one dword. * Bit usage for address registers (read or write): * 0-31: memory address within device */ #define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) #define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) #define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) #define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) /* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ #define HBUS_TARG_MBX_C (HBUS_BASE+0x030) #define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) /* * Registers for accessing device's internal peripheral registers * (e.g. SCD, BSM, etc.). First write to address register, * then read from or write to data register to complete the job. * Bit usage for address registers (read or write): * 0-15: register address (offset) within device * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) */ #define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044) #define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048) #define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c) #define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) /* * Per-Tx-queue write pointer (idx, really!) * Indicates idx to next TFD that driver will fill (1 past latest filled). * Bit usage: * 0-7: queue write idx * 11-8: queue selector */ #define HBUS_TARG_WRPTR (HBUS_BASE+0x060) #endif /* !__il_csr_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/commands.h0000644000175000017500000033331412026211315024246 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ #ifndef __il_commands_h__ #define __il_commands_h__ #include struct il_priv; /* uCode version contains 4 values: Major/Minor/API/Serial */ #define IL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) #define IL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) #define IL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) #define IL_UCODE_SERIAL(ver) ((ver) & 0x000000FF) /* Tx rates */ #define IL_CCK_RATES 4 #define IL_OFDM_RATES 8 #define IL_MAX_RATES (IL_CCK_RATES + IL_OFDM_RATES) enum { N_ALIVE = 0x1, N_ERROR = 0x2, /* RXON and QOS commands */ C_RXON = 0x10, C_RXON_ASSOC = 0x11, C_QOS_PARAM = 0x13, C_RXON_TIMING = 0x14, /* Multi-Station support */ C_ADD_STA = 0x18, C_REM_STA = 0x19, /* Security */ C_WEPKEY = 0x20, /* RX, TX, LEDs */ N_3945_RX = 0x1b, /* 3945 only */ C_TX = 0x1c, C_RATE_SCALE = 0x47, /* 3945 only */ C_LEDS = 0x48, C_TX_LINK_QUALITY_CMD = 0x4e, /* for 4965 */ /* 802.11h related */ C_CHANNEL_SWITCH = 0x72, N_CHANNEL_SWITCH = 0x73, C_SPECTRUM_MEASUREMENT = 0x74, N_SPECTRUM_MEASUREMENT = 0x75, /* Power Management */ C_POWER_TBL = 0x77, N_PM_SLEEP = 0x7A, N_PM_DEBUG_STATS = 0x7B, /* Scan commands and notifications */ C_SCAN = 0x80, C_SCAN_ABORT = 0x81, N_SCAN_START = 0x82, N_SCAN_RESULTS = 0x83, N_SCAN_COMPLETE = 0x84, /* IBSS/AP commands */ N_BEACON = 0x90, C_TX_BEACON = 0x91, /* Miscellaneous commands */ C_TX_PWR_TBL = 0x97, /* Bluetooth device coexistence config command */ C_BT_CONFIG = 0x9b, /* Statistics */ C_STATS = 0x9c, N_STATS = 0x9d, /* RF-KILL commands and notifications */ N_CARD_STATE = 0xa1, /* Missed beacons notification */ N_MISSED_BEACONS = 0xa2, C_CT_KILL_CONFIG = 0xa4, C_SENSITIVITY = 0xa8, C_PHY_CALIBRATION = 0xb0, N_RX_PHY = 0xc0, N_RX_MPDU = 0xc1, N_RX = 0xc3, N_COMPRESSED_BA = 0xc5, IL_CN_MAX = 0xff }; /****************************************************************************** * (0) * Commonly used structures and definitions: * Command header, rate_n_flags, txpower * *****************************************************************************/ /* il_cmd_header flags value */ #define IL_CMD_FAILED_MSK 0x40 #define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) #define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) #define SEQ_TO_IDX(s) ((s) & 0xff) #define IDX_TO_SEQ(i) ((i) & 0xff) #define SEQ_HUGE_FRAME cpu_to_le16(0x4000) #define SEQ_RX_FRAME cpu_to_le16(0x8000) /** * struct il_cmd_header * * This header format appears in the beginning of each command sent from the * driver, and each response/notification received from uCode. */ struct il_cmd_header { u8 cmd; /* Command ID: C_RXON, etc. */ u8 flags; /* 0:5 reserved, 6 abort, 7 internal */ /* * The driver sets up the sequence number to values of its choosing. * uCode does not use this value, but passes it back to the driver * when sending the response to each driver-originated command, so * the driver can match the response to the command. Since the values * don't get used by uCode, the driver may set up an arbitrary format. * * There is one exception: uCode sets bit 15 when it originates * the response/notification, i.e. when the response/notification * is not a direct response to a command sent by the driver. For * example, uCode issues N_3945_RX when it sends a received frame * to the driver; it is not a direct response to any driver command. * * The Linux driver uses the following format: * * 0:7 tfd idx - position within TX queue * 8:12 TX queue id * 13 reserved * 14 huge - driver sets this to indicate command is in the * 'huge' storage at the end of the command buffers * 15 unsolicited RX or uCode-originated notification */ __le16 sequence; /* command or response/notification data follows immediately */ u8 data[0]; } __packed; /** * struct il3945_tx_power * * Used in C_TX_PWR_TBL, C_SCAN, C_CHANNEL_SWITCH * * Each entry contains two values: * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained * linear value that multiplies the output of the digital signal processor, * before being sent to the analog radio. * 2) Radio gain. This sets the analog gain of the radio Tx path. * It is a coarser setting, and behaves in a logarithmic (dB) fashion. * * Driver obtains values from struct il3945_tx_power power_gain_table[][]. */ struct il3945_tx_power { u8 tx_gain; /* gain for analog radio */ u8 dsp_atten; /* gain for DSP */ } __packed; /** * struct il3945_power_per_rate * * Used in C_TX_PWR_TBL, C_CHANNEL_SWITCH */ struct il3945_power_per_rate { u8 rate; /* plcp */ struct il3945_tx_power tpc; u8 reserved; } __packed; /** * iwl4965 rate_n_flags bit fields * * rate_n_flags format is used in following iwl4965 commands: * N_RX (response only) * N_RX_MPDU (response only) * C_TX (both command and response) * C_TX_LINK_QUALITY_CMD * * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): * 2-0: 0) 6 Mbps * 1) 12 Mbps * 2) 18 Mbps * 3) 24 Mbps * 4) 36 Mbps * 5) 48 Mbps * 6) 54 Mbps * 7) 60 Mbps * * 4-3: 0) Single stream (SISO) * 1) Dual stream (MIMO) * 2) Triple stream (MIMO) * * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data * * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): * 3-0: 0xD) 6 Mbps * 0xF) 9 Mbps * 0x5) 12 Mbps * 0x7) 18 Mbps * 0x9) 24 Mbps * 0xB) 36 Mbps * 0x1) 48 Mbps * 0x3) 54 Mbps * * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): * 6-0: 10) 1 Mbps * 20) 2 Mbps * 55) 5.5 Mbps * 110) 11 Mbps */ #define RATE_MCS_CODE_MSK 0x7 #define RATE_MCS_SPATIAL_POS 3 #define RATE_MCS_SPATIAL_MSK 0x18 #define RATE_MCS_HT_DUP_POS 5 #define RATE_MCS_HT_DUP_MSK 0x20 /* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ #define RATE_MCS_FLAGS_POS 8 #define RATE_MCS_HT_POS 8 #define RATE_MCS_HT_MSK 0x100 /* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ #define RATE_MCS_CCK_POS 9 #define RATE_MCS_CCK_MSK 0x200 /* Bit 10: (1) Use Green Field preamble */ #define RATE_MCS_GF_POS 10 #define RATE_MCS_GF_MSK 0x400 /* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ #define RATE_MCS_HT40_POS 11 #define RATE_MCS_HT40_MSK 0x800 /* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ #define RATE_MCS_DUP_POS 12 #define RATE_MCS_DUP_MSK 0x1000 /* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ #define RATE_MCS_SGI_POS 13 #define RATE_MCS_SGI_MSK 0x2000 /** * rate_n_flags Tx antenna masks * 4965 has 2 transmitters * bit14:16 */ #define RATE_MCS_ANT_POS 14 #define RATE_MCS_ANT_A_MSK 0x04000 #define RATE_MCS_ANT_B_MSK 0x08000 #define RATE_MCS_ANT_C_MSK 0x10000 #define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) #define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) #define RATE_ANT_NUM 3 #define POWER_TBL_NUM_ENTRIES 33 #define POWER_TBL_NUM_HT_OFDM_ENTRIES 32 #define POWER_TBL_CCK_ENTRY 32 #define IL_PWR_NUM_HT_OFDM_ENTRIES 24 #define IL_PWR_CCK_ENTRIES 2 /** * union il4965_tx_power_dual_stream * * Host format used for C_TX_PWR_TBL, C_CHANNEL_SWITCH * Use __le32 version (struct tx_power_dual_stream) when building command. * * Driver provides radio gain and DSP attenuation settings to device in pairs, * one value for each transmitter chain. The first value is for transmitter A, * second for transmitter B. * * For SISO bit rates, both values in a pair should be identical. * For MIMO rates, one value may be different from the other, * in order to balance the Tx output between the two transmitters. * * See more details in doc for TXPOWER in 4965.h. */ union il4965_tx_power_dual_stream { struct { u8 radio_tx_gain[2]; u8 dsp_predis_atten[2]; } s; u32 dw; }; /** * struct tx_power_dual_stream * * Table entries in C_TX_PWR_TBL, C_CHANNEL_SWITCH * * Same format as il_tx_power_dual_stream, but __le32 */ struct tx_power_dual_stream { __le32 dw; } __packed; /** * struct il4965_tx_power_db * * Entire table within C_TX_PWR_TBL, C_CHANNEL_SWITCH */ struct il4965_tx_power_db { struct tx_power_dual_stream power_tbl[POWER_TBL_NUM_ENTRIES]; } __packed; /****************************************************************************** * (0a) * Alive and Error Commands & Responses: * *****************************************************************************/ #define UCODE_VALID_OK cpu_to_le32(0x1) #define INITIALIZE_SUBTYPE (9) /* * ("Initialize") N_ALIVE = 0x1 (response only, not a command) * * uCode issues this "initialize alive" notification once the initialization * uCode image has completed its work, and is ready to load the runtime image. * This is the *first* "alive" notification that the driver will receive after * rebooting uCode; the "initialize" alive is indicated by subtype field == 9. * * See comments documenting "BSM" (bootstrap state machine). * * For 4965, this notification contains important calibration data for * calculating txpower settings: * * 1) Power supply voltage indication. The voltage sensor outputs higher * values for lower voltage, and vice verse. * * 2) Temperature measurement parameters, for each of two channel widths * (20 MHz and 40 MHz) supported by the radios. Temperature sensing * is done via one of the receiver chains, and channel width influences * the results. * * 3) Tx gain compensation to balance 4965's 2 Tx chains for MIMO operation, * for each of 5 frequency ranges. */ struct il_init_alive_resp { u8 ucode_minor; u8 ucode_major; __le16 reserved1; u8 sw_rev[8]; u8 ver_type; u8 ver_subtype; /* "9" for initialize alive */ __le16 reserved2; __le32 log_event_table_ptr; __le32 error_event_table_ptr; __le32 timestamp; __le32 is_valid; /* calibration values from "initialize" uCode */ __le32 voltage; /* signed, higher value is lower voltage */ __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for HT40 */ __le32 therm_r2[2]; /* signed */ __le32 therm_r3[2]; /* signed */ __le32 therm_r4[2]; /* signed */ __le32 tx_atten[5][2]; /* signed MIMO gain comp, 5 freq groups, * 2 Tx chains */ } __packed; /** * N_ALIVE = 0x1 (response only, not a command) * * uCode issues this "alive" notification once the runtime image is ready * to receive commands from the driver. This is the *second* "alive" * notification that the driver will receive after rebooting uCode; * this "alive" is indicated by subtype field != 9. * * See comments documenting "BSM" (bootstrap state machine). * * This response includes two pointers to structures within the device's * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: * * 1) log_event_table_ptr indicates base of the event log. This traces * a 256-entry history of uCode execution within a circular buffer. * Its header format is: * * __le32 log_size; log capacity (in number of entries) * __le32 type; (1) timestamp with each entry, (0) no timestamp * __le32 wraps; # times uCode has wrapped to top of circular buffer * __le32 write_idx; next circular buffer entry that uCode would fill * * The header is followed by the circular buffer of log entries. Entries * with timestamps have the following format: * * __le32 event_id; range 0 - 1500 * __le32 timestamp; low 32 bits of TSF (of network, if associated) * __le32 data; event_id-specific data value * * Entries without timestamps contain only event_id and data. * * * 2) error_event_table_ptr indicates base of the error log. This contains * information about any uCode error that occurs. For 4965, the format * of the error log is: * * __le32 valid; (nonzero) valid, (0) log is empty * __le32 error_id; type of error * __le32 pc; program counter * __le32 blink1; branch link * __le32 blink2; branch link * __le32 ilink1; interrupt link * __le32 ilink2; interrupt link * __le32 data1; error-specific data * __le32 data2; error-specific data * __le32 line; source code line of error * __le32 bcon_time; beacon timer * __le32 tsf_low; network timestamp function timer * __le32 tsf_hi; network timestamp function timer * __le32 gp1; GP1 timer register * __le32 gp2; GP2 timer register * __le32 gp3; GP3 timer register * __le32 ucode_ver; uCode version * __le32 hw_ver; HW Silicon version * __le32 brd_ver; HW board version * __le32 log_pc; log program counter * __le32 frame_ptr; frame pointer * __le32 stack_ptr; stack pointer * __le32 hcmd; last host command * __le32 isr0; isr status register LMPM_NIC_ISR0: rxtx_flag * __le32 isr1; isr status register LMPM_NIC_ISR1: host_flag * __le32 isr2; isr status register LMPM_NIC_ISR2: enc_flag * __le32 isr3; isr status register LMPM_NIC_ISR3: time_flag * __le32 isr4; isr status register LMPM_NIC_ISR4: wico interrupt * __le32 isr_pref; isr status register LMPM_NIC_PREF_STAT * __le32 wait_event; wait event() caller address * __le32 l2p_control; L2pControlField * __le32 l2p_duration; L2pDurationField * __le32 l2p_mhvalid; L2pMhValidBits * __le32 l2p_addr_match; L2pAddrMatchStat * __le32 lmpm_pmg_sel; indicate which clocks are turned on (LMPM_PMG_SEL) * __le32 u_timestamp; indicate when the date and time of the compilation * __le32 reserved; * * The Linux driver can print both logs to the system log when a uCode error * occurs. */ struct il_alive_resp { u8 ucode_minor; u8 ucode_major; __le16 reserved1; u8 sw_rev[8]; u8 ver_type; u8 ver_subtype; /* not "9" for runtime alive */ __le16 reserved2; __le32 log_event_table_ptr; /* SRAM address for event log */ __le32 error_event_table_ptr; /* SRAM address for error log */ __le32 timestamp; __le32 is_valid; } __packed; /* * N_ERROR = 0x2 (response only, not a command) */ struct il_error_resp { __le32 error_type; u8 cmd_id; u8 reserved1; __le16 bad_cmd_seq_num; __le32 error_info; __le64 timestamp; } __packed; /****************************************************************************** * (1) * RXON Commands & Responses: * *****************************************************************************/ /* * Rx config defines & structure */ /* rx_config device types */ enum { RXON_DEV_TYPE_AP = 1, RXON_DEV_TYPE_ESS = 3, RXON_DEV_TYPE_IBSS = 4, RXON_DEV_TYPE_SNIFFER = 6, }; #define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) #define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) #define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) #define RXON_RX_CHAIN_VALID_POS (1) #define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) #define RXON_RX_CHAIN_FORCE_SEL_POS (4) #define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) #define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) #define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) #define RXON_RX_CHAIN_CNT_POS (10) #define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) #define RXON_RX_CHAIN_MIMO_CNT_POS (12) #define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) #define RXON_RX_CHAIN_MIMO_FORCE_POS (14) /* rx_config flags */ /* band & modulation selection */ #define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) #define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) /* auto detection enable */ #define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) /* TGg protection when tx */ #define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) /* cck short slot & preamble */ #define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) #define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) /* antenna selection */ #define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) #define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) #define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) #define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) /* radar detection enable */ #define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) #define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) /* rx response to host with 8-byte TSF * (according to ON_AIR deassertion) */ #define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) /* HT flags */ #define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) #define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) #define RXON_FLG_HT_OPERATING_MODE_POS (23) #define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) #define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) #define RXON_FLG_CHANNEL_MODE_POS (25) #define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) /* channel mode */ enum { CHANNEL_MODE_LEGACY = 0, CHANNEL_MODE_PURE_40 = 1, CHANNEL_MODE_MIXED = 2, CHANNEL_MODE_RESERVED = 3, }; #define RXON_FLG_CHANNEL_MODE_LEGACY \ cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) #define RXON_FLG_CHANNEL_MODE_PURE_40 \ cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) #define RXON_FLG_CHANNEL_MODE_MIXED \ cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) /* CTS to self (if spec allows) flag */ #define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) /* rx_config filter flags */ /* accept all data frames */ #define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) /* pass control & management to host */ #define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) /* accept multi-cast */ #define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) /* don't decrypt uni-cast frames */ #define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) /* don't decrypt multi-cast frames */ #define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) /* STA is associated */ #define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) /* transfer to host non bssid beacons in associated state */ #define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) /** * C_RXON = 0x10 (command, has simple generic response) * * RXON tunes the radio tuner to a service channel, and sets up a number * of parameters that are used primarily for Rx, but also for Tx operations. * * NOTE: When tuning to a new channel, driver must set the * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent * info within the device, including the station tables, tx retry * rate tables, and txpower tables. Driver must build a new station * table and txpower table before transmitting anything on the RXON * channel. * * NOTE: All RXONs wipe clean the internal txpower table. Driver must * issue a new C_TX_PWR_TBL after each C_RXON (0x10), * regardless of whether RXON_FILTER_ASSOC_MSK is set. */ struct il3945_rxon_cmd { u8 node_addr[6]; __le16 reserved1; u8 bssid_addr[6]; __le16 reserved2; u8 wlap_bssid_addr[6]; __le16 reserved3; u8 dev_type; u8 air_propagation; __le16 reserved4; u8 ofdm_basic_rates; u8 cck_basic_rates; __le16 assoc_id; __le32 flags; __le32 filter_flags; __le16 channel; __le16 reserved5; } __packed; struct il4965_rxon_cmd { u8 node_addr[6]; __le16 reserved1; u8 bssid_addr[6]; __le16 reserved2; u8 wlap_bssid_addr[6]; __le16 reserved3; u8 dev_type; u8 air_propagation; __le16 rx_chain; u8 ofdm_basic_rates; u8 cck_basic_rates; __le16 assoc_id; __le32 flags; __le32 filter_flags; __le16 channel; u8 ofdm_ht_single_stream_basic_rates; u8 ofdm_ht_dual_stream_basic_rates; } __packed; /* Create a common rxon cmd which will be typecast into the 3945 or 4965 * specific rxon cmd, depending on where it is called from. */ struct il_rxon_cmd { u8 node_addr[6]; __le16 reserved1; u8 bssid_addr[6]; __le16 reserved2; u8 wlap_bssid_addr[6]; __le16 reserved3; u8 dev_type; u8 air_propagation; __le16 rx_chain; u8 ofdm_basic_rates; u8 cck_basic_rates; __le16 assoc_id; __le32 flags; __le32 filter_flags; __le16 channel; u8 ofdm_ht_single_stream_basic_rates; u8 ofdm_ht_dual_stream_basic_rates; u8 reserved4; u8 reserved5; } __packed; /* * C_RXON_ASSOC = 0x11 (command, has simple generic response) */ struct il3945_rxon_assoc_cmd { __le32 flags; __le32 filter_flags; u8 ofdm_basic_rates; u8 cck_basic_rates; __le16 reserved; } __packed; struct il4965_rxon_assoc_cmd { __le32 flags; __le32 filter_flags; u8 ofdm_basic_rates; u8 cck_basic_rates; u8 ofdm_ht_single_stream_basic_rates; u8 ofdm_ht_dual_stream_basic_rates; __le16 rx_chain_select_flags; __le16 reserved; } __packed; #define IL_CONN_MAX_LISTEN_INTERVAL 10 #define IL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ #define IL39_MAX_UCODE_BEACON_INTERVAL 1 /* 1024 */ /* * C_RXON_TIMING = 0x14 (command, has simple generic response) */ struct il_rxon_time_cmd { __le64 timestamp; __le16 beacon_interval; __le16 atim_win; __le32 beacon_init_val; __le16 listen_interval; u8 dtim_period; u8 delta_cp_bss_tbtts; } __packed; /* * C_CHANNEL_SWITCH = 0x72 (command, has simple generic response) */ struct il3945_channel_switch_cmd { u8 band; u8 expect_beacon; __le16 channel; __le32 rxon_flags; __le32 rxon_filter_flags; __le32 switch_time; struct il3945_power_per_rate power[IL_MAX_RATES]; } __packed; struct il4965_channel_switch_cmd { u8 band; u8 expect_beacon; __le16 channel; __le32 rxon_flags; __le32 rxon_filter_flags; __le32 switch_time; struct il4965_tx_power_db tx_power; } __packed; /* * N_CHANNEL_SWITCH = 0x73 (notification only, not a command) */ struct il_csa_notification { __le16 band; __le16 channel; __le32 status; /* 0 - OK, 1 - fail */ } __packed; /****************************************************************************** * (2) * Quality-of-Service (QOS) Commands & Responses: * *****************************************************************************/ /** * struct il_ac_qos -- QOS timing params for C_QOS_PARAM * One for each of 4 EDCA access categories in struct il_qosparam_cmd * * @cw_min: Contention win, start value in numbers of slots. * Should be a power-of-2, minus 1. Device's default is 0x0f. * @cw_max: Contention win, max value in numbers of slots. * Should be a power-of-2, minus 1. Device's default is 0x3f. * @aifsn: Number of slots in Arbitration Interframe Space (before * performing random backoff timing prior to Tx). Device default 1. * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. * * Device will automatically increase contention win by (2*CW) + 1 for each * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW * value, to cap the CW value. */ struct il_ac_qos { __le16 cw_min; __le16 cw_max; u8 aifsn; u8 reserved1; __le16 edca_txop; } __packed; /* QoS flags defines */ #define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) #define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) #define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) /* Number of Access Categories (AC) (EDCA), queues 0..3 */ #define AC_NUM 4 /* * C_QOS_PARAM = 0x13 (command, has simple generic response) * * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs * 0: Background, 1: Best Effort, 2: Video, 3: Voice. */ struct il_qosparam_cmd { __le32 qos_flags; struct il_ac_qos ac[AC_NUM]; } __packed; /****************************************************************************** * (3) * Add/Modify Stations Commands & Responses: * *****************************************************************************/ /* * Multi station support */ /* Special, dedicated locations within device's station table */ #define IL_AP_ID 0 #define IL_STA_ID 2 #define IL3945_BROADCAST_ID 24 #define IL3945_STATION_COUNT 25 #define IL4965_BROADCAST_ID 31 #define IL4965_STATION_COUNT 32 #define IL_STATION_COUNT 32 /* MAX(3945,4965) */ #define IL_INVALID_STATION 255 #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) #define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) #define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) #define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) #define STA_FLG_MAX_AGG_SIZE_POS (19) #define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) #define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) #define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) #define STA_FLG_AGG_MPDU_DENSITY_POS (23) #define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) /* Use in mode field. 1: modify existing entry, 0: add new station entry */ #define STA_CONTROL_MODIFY_MSK 0x01 /* key flags __le16*/ #define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) #define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) #define STA_KEY_FLG_WEP cpu_to_le16(0x0001) #define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) #define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) #define STA_KEY_FLG_KEYID_POS 8 #define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) /* wep key is either from global key (0) or from station info array (1) */ #define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) /* wep key in STA: 5-bytes (0) or 13-bytes (1) */ #define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) #define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) #define STA_KEY_MAX_NUM 8 /* Flags indicate whether to modify vs. don't change various station params */ #define STA_MODIFY_KEY_MASK 0x01 #define STA_MODIFY_TID_DISABLE_TX 0x02 #define STA_MODIFY_TX_RATE_MSK 0x04 #define STA_MODIFY_ADDBA_TID_MSK 0x08 #define STA_MODIFY_DELBA_TID_MSK 0x10 #define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 /* Receiver address (actually, Rx station's idx into station table), * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ #define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) struct il4965_keyinfo { __le16 key_flags; u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ u8 reserved1; __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ u8 key_offset; u8 reserved2; u8 key[16]; /* 16-byte unicast decryption key */ } __packed; /** * struct sta_id_modify * @addr[ETH_ALEN]: station's MAC address * @sta_id: idx of station in uCode's station table * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change * * Driver selects unused table idx when adding new station, * or the idx to a pre-existing station entry when modifying that station. * Some idxes have special purposes (IL_AP_ID, idx 0, is for AP). * * modify_mask flags select which parameters to modify vs. leave alone. */ struct sta_id_modify { u8 addr[ETH_ALEN]; __le16 reserved1; u8 sta_id; u8 modify_mask; __le16 reserved2; } __packed; /* * C_ADD_STA = 0x18 (command) * * The device contains an internal table of per-station information, * with info on security keys, aggregation parameters, and Tx rates for * initial Tx attempt and any retries (4965 devices uses * C_TX_LINK_QUALITY_CMD, * 3945 uses C_RATE_SCALE to set up rate tables). * * C_ADD_STA sets up the table entry for one station, either creating * a new entry, or modifying a pre-existing one. * * NOTE: RXON command (without "associated" bit set) wipes the station table * clean. Moving into RF_KILL state does this also. Driver must set up * new station table before transmitting anything on the RXON channel * (except active scans or active measurements; those commands carry * their own txpower/rate setup data). * * When getting started on a new channel, driver must set up the * IL_BROADCAST_ID entry (last entry in the table). For a client * station in a BSS, once an AP is selected, driver sets up the AP STA * in the IL_AP_ID entry (1st entry in the table). BROADCAST and AP * are all that are needed for a BSS client station. If the device is * used as AP, or in an IBSS network, driver must set up station table * entries for all STAs in network, starting with idx IL_STA_ID. */ struct il3945_addsta_cmd { u8 mode; /* 1: modify existing, 0: add new station */ u8 reserved[3]; struct sta_id_modify sta; struct il4965_keyinfo key; __le32 station_flags; /* STA_FLG_* */ __le32 station_flags_msk; /* STA_FLG_* */ /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) * corresponding to bit (e.g. bit 5 controls TID 5). * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ __le16 tid_disable_tx; __le16 rate_n_flags; /* TID for which to add block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ u8 add_immediate_ba_tid; /* TID for which to remove block-ack support. * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ u8 remove_immediate_ba_tid; /* Starting Sequence Number for added block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ __le16 add_immediate_ba_ssn; } __packed; struct il4965_addsta_cmd { u8 mode; /* 1: modify existing, 0: add new station */ u8 reserved[3]; struct sta_id_modify sta; struct il4965_keyinfo key; __le32 station_flags; /* STA_FLG_* */ __le32 station_flags_msk; /* STA_FLG_* */ /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) * corresponding to bit (e.g. bit 5 controls TID 5). * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ __le16 tid_disable_tx; __le16 reserved1; /* TID for which to add block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ u8 add_immediate_ba_tid; /* TID for which to remove block-ack support. * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ u8 remove_immediate_ba_tid; /* Starting Sequence Number for added block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ __le16 add_immediate_ba_ssn; /* * Number of packets OK to transmit to station even though * it is asleep -- used to synchronise PS-poll and u-APSD * responses while ucode keeps track of STA sleep state. */ __le16 sleep_tx_count; __le16 reserved2; } __packed; /* Wrapper struct for 3945 and 4965 addsta_cmd structures */ struct il_addsta_cmd { u8 mode; /* 1: modify existing, 0: add new station */ u8 reserved[3]; struct sta_id_modify sta; struct il4965_keyinfo key; __le32 station_flags; /* STA_FLG_* */ __le32 station_flags_msk; /* STA_FLG_* */ /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) * corresponding to bit (e.g. bit 5 controls TID 5). * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ __le16 tid_disable_tx; __le16 rate_n_flags; /* 3945 only */ /* TID for which to add block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ u8 add_immediate_ba_tid; /* TID for which to remove block-ack support. * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ u8 remove_immediate_ba_tid; /* Starting Sequence Number for added block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ __le16 add_immediate_ba_ssn; /* * Number of packets OK to transmit to station even though * it is asleep -- used to synchronise PS-poll and u-APSD * responses while ucode keeps track of STA sleep state. */ __le16 sleep_tx_count; __le16 reserved2; } __packed; #define ADD_STA_SUCCESS_MSK 0x1 #define ADD_STA_NO_ROOM_IN_TBL 0x2 #define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 #define ADD_STA_MODIFY_NON_EXIST_STA 0x8 /* * C_ADD_STA = 0x18 (response) */ struct il_add_sta_resp { u8 status; /* ADD_STA_* */ } __packed; #define REM_STA_SUCCESS_MSK 0x1 /* * C_REM_STA = 0x19 (response) */ struct il_rem_sta_resp { u8 status; } __packed; /* * C_REM_STA = 0x19 (command) */ struct il_rem_sta_cmd { u8 num_sta; /* number of removed stations */ u8 reserved[3]; u8 addr[ETH_ALEN]; /* MAC addr of the first station */ u8 reserved2[2]; } __packed; #define IL_TX_FIFO_BK_MSK cpu_to_le32(BIT(0)) #define IL_TX_FIFO_BE_MSK cpu_to_le32(BIT(1)) #define IL_TX_FIFO_VI_MSK cpu_to_le32(BIT(2)) #define IL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3)) #define IL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) #define IL_DROP_SINGLE 0 #define IL_DROP_SELECTED 1 #define IL_DROP_ALL 2 /* * REPLY_WEP_KEY = 0x20 */ struct il_wep_key { u8 key_idx; u8 key_offset; u8 reserved1[2]; u8 key_size; u8 reserved2[3]; u8 key[16]; } __packed; struct il_wep_cmd { u8 num_keys; u8 global_key_type; u8 flags; u8 reserved; struct il_wep_key key[0]; } __packed; #define WEP_KEY_WEP_TYPE 1 #define WEP_KEYS_MAX 4 #define WEP_INVALID_OFFSET 0xff #define WEP_KEY_LEN_64 5 #define WEP_KEY_LEN_128 13 /****************************************************************************** * (4) * Rx Responses: * *****************************************************************************/ #define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) #define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) #define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) #define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) #define RX_RES_PHY_FLAGS_ANTENNA_MSK 0xf0 #define RX_RES_PHY_FLAGS_ANTENNA_POS 4 #define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) #define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) #define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) #define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) #define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) #define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) #define RX_RES_STATUS_STATION_FOUND (1<<6) #define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) #define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) #define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) #define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) #define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) #define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) #define RX_MPDU_RES_STATUS_ICV_OK (0x20) #define RX_MPDU_RES_STATUS_MIC_OK (0x40) #define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) #define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) struct il3945_rx_frame_stats { u8 phy_count; u8 id; u8 rssi; u8 agc; __le16 sig_avg; __le16 noise_diff; u8 payload[0]; } __packed; struct il3945_rx_frame_hdr { __le16 channel; __le16 phy_flags; u8 reserved1; u8 rate; __le16 len; u8 payload[0]; } __packed; struct il3945_rx_frame_end { __le32 status; __le64 timestamp; __le32 beacon_timestamp; } __packed; /* * N_3945_RX = 0x1b (response only, not a command) * * NOTE: DO NOT dereference from casts to this structure * It is provided only for calculating minimum data set size. * The actual offsets of the hdr and end are dynamic based on * stats.phy_count */ struct il3945_rx_frame { struct il3945_rx_frame_stats stats; struct il3945_rx_frame_hdr hdr; struct il3945_rx_frame_end end; } __packed; #define IL39_RX_FRAME_SIZE (4 + sizeof(struct il3945_rx_frame)) /* Fixed (non-configurable) rx data from phy */ #define IL49_RX_RES_PHY_CNT 14 #define IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET (4) #define IL49_RX_PHY_FLAGS_ANTENNAE_MASK (0x70) #define IL49_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ #define IL49_AGC_DB_POS (7) struct il4965_rx_non_cfg_phy { __le16 ant_selection; /* ant A bit 4, ant B bit 5, ant C bit 6 */ __le16 agc_info; /* agc code 0:6, agc dB 7:13, reserved 14:15 */ u8 rssi_info[6]; /* we use even entries, 0/2/4 for A/B/C rssi */ u8 pad[0]; } __packed; /* * N_RX = 0xc3 (response only, not a command) * Used only for legacy (non 11n) frames. */ struct il_rx_phy_res { u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ u8 stat_id; /* configurable DSP phy data set ID */ u8 reserved1; __le64 timestamp; /* TSF at on air rise */ __le32 beacon_time_stamp; /* beacon at on-air rise */ __le16 phy_flags; /* general phy flags: band, modulation, ... */ __le16 channel; /* channel number */ u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ __le32 rate_n_flags; /* RATE_MCS_* */ __le16 byte_count; /* frame's byte-count */ __le16 frame_time; /* frame's time on the air */ } __packed; struct il_rx_mpdu_res_start { __le16 byte_count; __le16 reserved; } __packed; /****************************************************************************** * (5) * Tx Commands & Responses: * * Driver must place each C_TX command into one of the prioritized Tx * queues in host DRAM, shared between driver and device (see comments for * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode * are preparing to transmit, the device pulls the Tx command over the PCI * bus via one of the device's Tx DMA channels, to fill an internal FIFO * from which data will be transmitted. * * uCode handles all timing and protocol related to control frames * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler * handle reception of block-acks; uCode updates the host driver via * N_COMPRESSED_BA. * * uCode handles retrying Tx when an ACK is expected but not received. * This includes trying lower data rates than the one requested in the Tx * command, as set up by the C_RATE_SCALE (for 3945) or * C_TX_LINK_QUALITY_CMD (4965). * * Driver sets up transmit power for various rates via C_TX_PWR_TBL. * This command must be executed after every RXON command, before Tx can occur. *****************************************************************************/ /* C_TX Tx flags field */ /* * 1: Use Request-To-Send protocol before this frame. * Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. */ #define TX_CMD_FLG_RTS_MSK cpu_to_le32(1 << 1) /* * 1: Transmit Clear-To-Send to self before this frame. * Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames. * Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. */ #define TX_CMD_FLG_CTS_MSK cpu_to_le32(1 << 2) /* 1: Expect ACK from receiving station * 0: Don't expect ACK (MAC header's duration field s/b 0) * Set this for unicast frames, but not broadcast/multicast. */ #define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) /* For 4965 devices: * 1: Use rate scale table (see C_TX_LINK_QUALITY_CMD). * Tx command's initial_rate_idx indicates first rate to try; * uCode walks through table for additional Tx attempts. * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. * This rate will be used for all Tx attempts; it will not be scaled. */ #define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) /* 1: Expect immediate block-ack. * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ #define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) /* * 1: Frame requires full Tx-Op protection. * Set this if either RTS or CTS Tx Flag gets set. */ #define TX_CMD_FLG_FULL_TXOP_PROT_MSK cpu_to_le32(1 << 7) /* Tx antenna selection field; used only for 3945, reserved (0) for 4965 devices. * Set field to "0" to allow 3945 uCode to select antenna (normal usage). */ #define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) #define TX_CMD_FLG_ANT_A_MSK cpu_to_le32(1 << 8) #define TX_CMD_FLG_ANT_B_MSK cpu_to_le32(1 << 9) /* 1: uCode overrides sequence control field in MAC header. * 0: Driver provides sequence control field in MAC header. * Set this for management frames, non-QOS data frames, non-unicast frames, * and also in Tx command embedded in C_SCAN for active scans. */ #define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) /* 1: This frame is non-last MPDU; more fragments are coming. * 0: Last fragment, or not using fragmentation. */ #define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) /* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. * 0: No TSF required in outgoing frame. * Set this for transmitting beacons and probe responses. */ #define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) /* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword * alignment of frame's payload data field. * 0: No pad * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 * field (but not both). Driver must align frame data (i.e. data following * MAC header) to DWORD boundary. */ #define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) /* accelerate aggregation support * 0 - no CCMP encryption; 1 - CCMP encryption */ #define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) /* HCCA-AP - disable duration overwriting. */ #define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) /* * TX command security control */ #define TX_CMD_SEC_WEP 0x01 #define TX_CMD_SEC_CCM 0x02 #define TX_CMD_SEC_TKIP 0x03 #define TX_CMD_SEC_MSK 0x03 #define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_KEY128 0x08 /* * security overhead sizes */ #define WEP_IV_LEN 4 #define WEP_ICV_LEN 4 #define CCMP_MIC_LEN 8 #define TKIP_ICV_LEN 4 /* * C_TX = 0x1c (command) */ struct il3945_tx_cmd { /* * MPDU byte count: * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, * + 8 byte IV for CCM or TKIP (not used for WEP) * + Data payload * + 8-byte MIC (not used for CCM/WEP) * NOTE: Does not include Tx command bytes, post-MAC pad bytes, * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i * Range: 14-2342 bytes. */ __le16 len; /* * MPDU or MSDU byte count for next frame. * Used for fragmentation and bursting, but not 11n aggregation. * Same as "len", but for next frame. Set to 0 if not applicable. */ __le16 next_frame_len; __le32 tx_flags; /* TX_CMD_FLG_* */ u8 rate; /* Index of recipient station in uCode's station table */ u8 sta_id; u8 tid_tspec; u8 sec_ctl; u8 key[16]; union { u8 byte[8]; __le16 word[4]; __le32 dw[2]; } tkip_mic; __le32 next_frame_info; union { __le32 life_time; __le32 attempt; } stop_time; u8 supp_rates[2]; u8 rts_retry_limit; /*byte 50 */ u8 data_retry_limit; /*byte 51 */ union { __le16 pm_frame_timeout; __le16 attempt_duration; } timeout; /* * Duration of EDCA burst Tx Opportunity, in 32-usec units. * Set this if txop time is not specified by HCCA protocol (e.g. by AP). */ __le16 driver_txop; /* * MAC header goes here, followed by 2 bytes padding if MAC header * length is 26 or 30 bytes, followed by payload data */ u8 payload[0]; struct ieee80211_hdr hdr[0]; } __packed; /* * C_TX = 0x1c (response) */ struct il3945_tx_resp { u8 failure_rts; u8 failure_frame; u8 bt_kill_count; u8 rate; __le32 wireless_media_time; __le32 status; /* TX status */ } __packed; /* * 4965 uCode updates these Tx attempt count values in host DRAM. * Used for managing Tx retries when expecting block-acks. * Driver should set these fields to 0. */ struct il_dram_scratch { u8 try_cnt; /* Tx attempts */ u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ __le16 reserved; } __packed; struct il_tx_cmd { /* * MPDU byte count: * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, * + 8 byte IV for CCM or TKIP (not used for WEP) * + Data payload * + 8-byte MIC (not used for CCM/WEP) * NOTE: Does not include Tx command bytes, post-MAC pad bytes, * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i * Range: 14-2342 bytes. */ __le16 len; /* * MPDU or MSDU byte count for next frame. * Used for fragmentation and bursting, but not 11n aggregation. * Same as "len", but for next frame. Set to 0 if not applicable. */ __le16 next_frame_len; __le32 tx_flags; /* TX_CMD_FLG_* */ /* uCode may modify this field of the Tx command (in host DRAM!). * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ struct il_dram_scratch scratch; /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ __le32 rate_n_flags; /* RATE_MCS_* */ /* Index of destination station in uCode's station table */ u8 sta_id; /* Type of security encryption: CCM or TKIP */ u8 sec_ctl; /* TX_CMD_SEC_* */ /* * Index into rate table (see C_TX_LINK_QUALITY_CMD) for initial * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for * data frames, this field may be used to selectively reduce initial * rate (via non-0 value) for special frames (e.g. management), while * still supporting rate scaling for all frames. */ u8 initial_rate_idx; u8 reserved; u8 key[16]; __le16 next_frame_flags; __le16 reserved2; union { __le32 life_time; __le32 attempt; } stop_time; /* Host DRAM physical address pointer to "scratch" in this command. * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ __le32 dram_lsb_ptr; u8 dram_msb_ptr; u8 rts_retry_limit; /*byte 50 */ u8 data_retry_limit; /*byte 51 */ u8 tid_tspec; union { __le16 pm_frame_timeout; __le16 attempt_duration; } timeout; /* * Duration of EDCA burst Tx Opportunity, in 32-usec units. * Set this if txop time is not specified by HCCA protocol (e.g. by AP). */ __le16 driver_txop; /* * MAC header goes here, followed by 2 bytes padding if MAC header * length is 26 or 30 bytes, followed by payload data */ u8 payload[0]; struct ieee80211_hdr hdr[0]; } __packed; /* TX command response is sent after *3945* transmission attempts. * * NOTES: * * TX_STATUS_FAIL_NEXT_FRAG * * If the fragment flag in the MAC header for the frame being transmitted * is set and there is insufficient time to transmit the next frame, the * TX status will be returned with 'TX_STATUS_FAIL_NEXT_FRAG'. * * TX_STATUS_FIFO_UNDERRUN * * Indicates the host did not provide bytes to the FIFO fast enough while * a TX was in progress. * * TX_STATUS_FAIL_MGMNT_ABORT * * This status is only possible if the ABORT ON MGMT RX parameter was * set to true with the TX command. * * If the MSB of the status parameter is set then an abort sequence is * required. This sequence consists of the host activating the TX Abort * control line, and then waiting for the TX Abort command response. This * indicates that a the device is no longer in a transmit state, and that the * command FIFO has been cleared. The host must then deactivate the TX Abort * control line. Receiving is still allowed in this case. */ enum { TX_3945_STATUS_SUCCESS = 0x01, TX_3945_STATUS_DIRECT_DONE = 0x02, TX_3945_STATUS_FAIL_SHORT_LIMIT = 0x82, TX_3945_STATUS_FAIL_LONG_LIMIT = 0x83, TX_3945_STATUS_FAIL_FIFO_UNDERRUN = 0x84, TX_3945_STATUS_FAIL_MGMNT_ABORT = 0x85, TX_3945_STATUS_FAIL_NEXT_FRAG = 0x86, TX_3945_STATUS_FAIL_LIFE_EXPIRE = 0x87, TX_3945_STATUS_FAIL_DEST_PS = 0x88, TX_3945_STATUS_FAIL_ABORTED = 0x89, TX_3945_STATUS_FAIL_BT_RETRY = 0x8a, TX_3945_STATUS_FAIL_STA_INVALID = 0x8b, TX_3945_STATUS_FAIL_FRAG_DROPPED = 0x8c, TX_3945_STATUS_FAIL_TID_DISABLE = 0x8d, TX_3945_STATUS_FAIL_FRAME_FLUSHED = 0x8e, TX_3945_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, TX_3945_STATUS_FAIL_TX_LOCKED = 0x90, TX_3945_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, }; /* * TX command response is sent after *4965* transmission attempts. * * both postpone and abort status are expected behavior from uCode. there is * no special operation required from driver; except for RFKILL_FLUSH, * which required tx flush host command to flush all the tx frames in queues */ enum { TX_STATUS_SUCCESS = 0x01, TX_STATUS_DIRECT_DONE = 0x02, /* postpone TX */ TX_STATUS_POSTPONE_DELAY = 0x40, TX_STATUS_POSTPONE_FEW_BYTES = 0x41, TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, TX_STATUS_POSTPONE_CALC_TTAK = 0x44, /* abort TX */ TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, TX_STATUS_FAIL_SHORT_LIMIT = 0x82, TX_STATUS_FAIL_LONG_LIMIT = 0x83, TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, TX_STATUS_FAIL_DRAIN_FLOW = 0x85, TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, TX_STATUS_FAIL_DEST_PS = 0x88, TX_STATUS_FAIL_HOST_ABORTED = 0x89, TX_STATUS_FAIL_BT_RETRY = 0x8a, TX_STATUS_FAIL_STA_INVALID = 0x8b, TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, TX_STATUS_FAIL_TID_DISABLE = 0x8d, TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, }; #define TX_PACKET_MODE_REGULAR 0x0000 #define TX_PACKET_MODE_BURST_SEQ 0x0100 #define TX_PACKET_MODE_BURST_FIRST 0x0200 enum { TX_POWER_PA_NOT_ACTIVE = 0x0, }; enum { TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ TX_STATUS_DELAY_MSK = 0x00000040, TX_STATUS_ABORT_MSK = 0x00000080, TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ TX_RESERVED = 0x00780000, /* bits 19:22 */ TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ }; /* ******************************* * TX aggregation status ******************************* */ enum { AGG_TX_STATE_TRANSMITTED = 0x00, AGG_TX_STATE_UNDERRUN_MSK = 0x01, AGG_TX_STATE_FEW_BYTES_MSK = 0x04, AGG_TX_STATE_ABORT_MSK = 0x08, AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, AGG_TX_STATE_SCD_QUERY_MSK = 0x80, AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, AGG_TX_STATE_RESPONSE_MSK = 0x1ff, AGG_TX_STATE_DUMP_TX_MSK = 0x200, AGG_TX_STATE_DELAY_TX_MSK = 0x400 }; #define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ #define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ #define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK) /* # tx attempts for first frame in aggregation */ #define AGG_TX_STATE_TRY_CNT_POS 12 #define AGG_TX_STATE_TRY_CNT_MSK 0xf000 /* Command ID and sequence number of Tx command for this frame */ #define AGG_TX_STATE_SEQ_NUM_POS 16 #define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 /* * C_TX = 0x1c (response) * * This response may be in one of two slightly different formats, indicated * by the frame_count field: * * 1) No aggregation (frame_count == 1). This reports Tx results for * a single frame. Multiple attempts, at various bit rates, may have * been made for this frame. * * 2) Aggregation (frame_count > 1). This reports Tx results for * 2 or more frames that used block-acknowledge. All frames were * transmitted at same rate. Rate scaling may have been used if first * frame in this new agg block failed in previous agg block(s). * * Note that, for aggregation, ACK (block-ack) status is not delivered here; * block-ack has not been received by the time the 4965 device records * this status. * This status relates to reasons the tx might have been blocked or aborted * within the sending station (this 4965 device), rather than whether it was * received successfully by the destination station. */ struct agg_tx_status { __le16 status; __le16 sequence; } __packed; struct il4965_tx_resp { u8 frame_count; /* 1 no aggregation, >1 aggregation */ u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ u8 failure_rts; /* # failures due to unsuccessful RTS */ u8 failure_frame; /* # failures due to no ACK (unused for agg) */ /* For non-agg: Rate at which frame was successful. * For agg: Rate at which all frames were transmitted. */ __le32 rate_n_flags; /* RATE_MCS_* */ /* For non-agg: RTS + CTS + frame tx attempts time + ACK. * For agg: RTS + CTS + aggregation tx time + block-ack time. */ __le16 wireless_media_time; /* uSecs */ __le16 reserved; __le32 pa_power1; /* RF power amplifier measurement (not used) */ __le32 pa_power2; /* * For non-agg: frame status TX_STATUS_* * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status * fields follow this one, up to frame_count. * Bit fields: * 11- 0: AGG_TX_STATE_* status code * 15-12: Retry count for 1st frame in aggregation (retries * occur if tx failed for this frame when it was a * member of a previous aggregation block). If rate * scaling is used, retry count indicates the rate * table entry used for all frames in the new agg. * 31-16: Sequence # for this frame's Tx cmd (not SSN!) */ union { __le32 status; struct agg_tx_status agg_status[0]; /* for each agg frame */ } u; } __packed; /* * N_COMPRESSED_BA = 0xc5 (response only, not a command) * * Reports Block-Acknowledge from recipient station */ struct il_compressed_ba_resp { __le32 sta_addr_lo32; __le16 sta_addr_hi16; __le16 reserved; /* Index of recipient (BA-sending) station in uCode's station table */ u8 sta_id; u8 tid; __le16 seq_ctl; __le64 bitmap; __le16 scd_flow; __le16 scd_ssn; } __packed; /* * C_TX_PWR_TBL = 0x97 (command, has simple generic response) * * See details under "TXPOWER" in 4965.h. */ struct il3945_txpowertable_cmd { u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ u8 reserved; __le16 channel; struct il3945_power_per_rate power[IL_MAX_RATES]; } __packed; struct il4965_txpowertable_cmd { u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ u8 reserved; __le16 channel; struct il4965_tx_power_db tx_power; } __packed; /** * struct il3945_rate_scaling_cmd - Rate Scaling Command & Response * * C_RATE_SCALE = 0x47 (command, has simple generic response) * * NOTE: The table of rates passed to the uCode via the * RATE_SCALE command sets up the corresponding order of * rates used for all related commands, including rate * masks, etc. * * For example, if you set 9MB (PLCP 0x0f) as the first * rate in the rate table, the bit mask for that rate * when passed through ofdm_basic_rates on the C_RXON * command would be bit 0 (1 << 0) */ struct il3945_rate_scaling_info { __le16 rate_n_flags; u8 try_cnt; u8 next_rate_idx; } __packed; struct il3945_rate_scaling_cmd { u8 table_id; u8 reserved[3]; struct il3945_rate_scaling_info table[IL_MAX_RATES]; } __packed; /*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ #define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) /* # of EDCA prioritized tx fifos */ #define LINK_QUAL_AC_NUM AC_NUM /* # entries in rate scale table to support Tx retries */ #define LINK_QUAL_MAX_RETRY_NUM 16 /* Tx antenna selection values */ #define LINK_QUAL_ANT_A_MSK (1 << 0) #define LINK_QUAL_ANT_B_MSK (1 << 1) #define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) /** * struct il_link_qual_general_params * * Used in C_TX_LINK_QUALITY_CMD */ struct il_link_qual_general_params { u8 flags; /* No entries at or above this (driver chosen) idx contain MIMO */ u8 mimo_delimiter; /* Best single antenna to use for single stream (legacy, SISO). */ u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ /* Best antennas to use for MIMO (unused for 4965, assumes both). */ u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ /* * If driver needs to use different initial rates for different * EDCA QOS access categories (as implemented by tx fifos 0-3), * this table will set that up, by indicating the idxes in the * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. * Otherwise, driver should set all entries to 0. * * Entry usage: * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. */ u8 start_rate_idx[LINK_QUAL_AC_NUM]; } __packed; #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ #define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) #define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) #define LINK_QUAL_AGG_DISABLE_START_DEF (3) #define LINK_QUAL_AGG_DISABLE_START_MAX (255) #define LINK_QUAL_AGG_DISABLE_START_MIN (0) #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (31) #define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) /** * struct il_link_qual_agg_params * * Used in C_TX_LINK_QUALITY_CMD */ struct il_link_qual_agg_params { /* *Maximum number of uSec in aggregation. * default set to 4000 (4 milliseconds) if not configured in .cfg */ __le16 agg_time_limit; /* * Number of Tx retries allowed for a frame, before that frame will * no longer be considered for the start of an aggregation sequence * (scheduler will then try to tx it as single frame). * Driver should set this to 3. */ u8 agg_dis_start_th; /* * Maximum number of frames in aggregation. * 0 = no limit (default). 1 = no aggregation. * Other values = max # frames in aggregation. */ u8 agg_frame_cnt_limit; __le32 reserved; } __packed; /* * C_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) * * For 4965 devices only; 3945 uses C_RATE_SCALE. * * Each station in the 4965 device's internal station table has its own table * of 16 * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when * an ACK is not received. This command replaces the entire table for * one station. * * NOTE: Station must already be in 4965 device's station table. * Use C_ADD_STA. * * The rate scaling procedures described below work well. Of course, other * procedures are possible, and may work better for particular environments. * * * FILLING THE RATE TBL * * Given a particular initial rate and mode, as determined by the rate * scaling algorithm described below, the Linux driver uses the following * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the * Link Quality command: * * * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: * a) Use this same initial rate for first 3 entries. * b) Find next lower available rate using same mode (SISO or MIMO), * use for next 3 entries. If no lower rate available, switch to * legacy mode (no HT40 channel, no MIMO, no short guard interval). * c) If using MIMO, set command's mimo_delimiter to number of entries * using MIMO (3 or 6). * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, * no MIMO, no short guard interval), at the next lower bit rate * (e.g. if second HT bit rate was 54, try 48 legacy), and follow * legacy procedure for remaining table entries. * * 2) If using legacy initial rate: * a) Use the initial rate for only one entry. * b) For each following entry, reduce the rate to next lower available * rate, until reaching the lowest available rate. * c) When reducing rate, also switch antenna selection. * d) Once lowest available rate is reached, repeat this rate until * rate table is filled (16 entries), switching antenna each entry. * * * ACCUMULATING HISTORY * * The rate scaling algorithm for 4965 devices, as implemented in Linux driver, * uses two sets of frame Tx success history: One for the current/active * modulation mode, and one for a speculative/search mode that is being * attempted. If the speculative mode turns out to be more effective (i.e. * actual transfer rate is better), then the driver continues to use the * speculative mode as the new current active mode. * * Each history set contains, separately for each possible rate, data for a * sliding win of the 62 most recent tx attempts at that rate. The data * includes a shifting bitmap of success(1)/failure(0), and sums of successful * and attempted frames, from which the driver can additionally calculate a * success ratio (success / attempted) and number of failures * (attempted - success), and control the size of the win (attempted). * The driver uses the bit map to remove successes from the success sum, as * the oldest tx attempts fall out of the win. * * When the 4965 device makes multiple tx attempts for a given frame, each * attempt might be at a different rate, and have different modulation * characteristics (e.g. antenna, fat channel, short guard interval), as set * up in the rate scaling table in the Link Quality command. The driver must * determine which rate table entry was used for each tx attempt, to determine * which rate-specific history to update, and record only those attempts that * match the modulation characteristics of the history set. * * When using block-ack (aggregation), all frames are transmitted at the same * rate, since there is no per-attempt acknowledgment from the destination * station. The Tx response struct il_tx_resp indicates the Tx rate in * rate_n_flags field. After receiving a block-ack, the driver can update * history for the entire block all at once. * * * FINDING BEST STARTING RATE: * * When working with a selected initial modulation mode (see below), the * driver attempts to find a best initial rate. The initial rate is the * first entry in the Link Quality command's rate table. * * 1) Calculate actual throughput (success ratio * expected throughput, see * table below) for current initial rate. Do this only if enough frames * have been attempted to make the value meaningful: at least 6 failed * tx attempts, or at least 8 successes. If not enough, don't try rate * scaling yet. * * 2) Find available rates adjacent to current initial rate. Available means: * a) supported by hardware && * b) supported by association && * c) within any constraints selected by user * * 3) Gather measured throughputs for adjacent rates. These might not have * enough history to calculate a throughput. That's okay, we might try * using one of them anyway! * * 4) Try decreasing rate if, for current rate: * a) success ratio is < 15% || * b) lower adjacent rate has better measured throughput || * c) higher adjacent rate has worse throughput, and lower is unmeasured * * As a sanity check, if decrease was determined above, leave rate * unchanged if: * a) lower rate unavailable * b) success ratio at current rate > 85% (very good) * c) current measured throughput is better than expected throughput * of lower rate (under perfect 100% tx conditions, see table below) * * 5) Try increasing rate if, for current rate: * a) success ratio is < 15% || * b) both adjacent rates' throughputs are unmeasured (try it!) || * b) higher adjacent rate has better measured throughput || * c) lower adjacent rate has worse throughput, and higher is unmeasured * * As a sanity check, if increase was determined above, leave rate * unchanged if: * a) success ratio at current rate < 70%. This is not particularly * good performance; higher rate is sure to have poorer success. * * 6) Re-evaluate the rate after each tx frame. If working with block- * acknowledge, history and stats may be calculated for the entire * block (including prior history that fits within the history wins), * before re-evaluation. * * FINDING BEST STARTING MODULATION MODE: * * After working with a modulation mode for a "while" (and doing rate scaling), * the driver searches for a new initial mode in an attempt to improve * throughput. The "while" is measured by numbers of attempted frames: * * For legacy mode, search for new mode after: * 480 successful frames, or 160 failed frames * For high-throughput modes (SISO or MIMO), search for new mode after: * 4500 successful frames, or 400 failed frames * * Mode switch possibilities are (3 for each mode): * * For legacy: * Change antenna, try SISO (if HT association), try MIMO (if HT association) * For SISO: * Change antenna, try MIMO, try shortened guard interval (SGI) * For MIMO: * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) * * When trying a new mode, use the same bit rate as the old/current mode when * trying antenna switches and shortened guard interval. When switching to * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate * for which the expected throughput (under perfect conditions) is about the * same or slightly better than the actual measured throughput delivered by * the old/current mode. * * Actual throughput can be estimated by multiplying the expected throughput * by the success ratio (successful / attempted tx frames). Frame size is * not considered in this calculation; it assumes that frame size will average * out to be fairly consistent over several samples. The following are * metric values for expected throughput assuming 100% success ratio. * Only G band has support for CCK rates: * * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 * * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 * * After the new mode has been tried for a short while (minimum of 6 failed * frames or 8 successful frames), compare success ratio and actual throughput * estimate of the new mode with the old. If either is better with the new * mode, continue to use the new mode. * * Continue comparing modes until all 3 possibilities have been tried. * If moving from legacy to HT, try all 3 possibilities from the new HT * mode. After trying all 3, a best mode is found. Continue to use this mode * for the longer "while" described above (e.g. 480 successful frames for * legacy), and then repeat the search process. * */ struct il_link_quality_cmd { /* Index of destination/recipient station in uCode's station table */ u8 sta_id; u8 reserved1; __le16 control; /* not used */ struct il_link_qual_general_params general_params; struct il_link_qual_agg_params agg_params; /* * Rate info; when using rate-scaling, Tx command's initial_rate_idx * specifies 1st Tx rate attempted, via idx into this table. * 4965 devices works its way through table when retrying Tx. */ struct { __le32 rate_n_flags; /* RATE_MCS_*, RATE_* */ } rs_table[LINK_QUAL_MAX_RETRY_NUM]; __le32 reserved2; } __packed; /* * BT configuration enable flags: * bit 0 - 1: BT channel announcement enabled * 0: disable * bit 1 - 1: priority of BT device enabled * 0: disable */ #define BT_COEX_DISABLE (0x0) #define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) #define BT_ENABLE_PRIORITY BIT(1) #define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) #define BT_LEAD_TIME_DEF (0x1E) #define BT_MAX_KILL_DEF (0x5) /* * C_BT_CONFIG = 0x9b (command, has simple generic response) * * 3945 and 4965 devices support hardware handshake with Bluetooth device on * same platform. Bluetooth device alerts wireless device when it will Tx; * wireless device can delay or kill its own Tx to accommodate. */ struct il_bt_cmd { u8 flags; u8 lead_time; u8 max_kill; u8 reserved; __le32 kill_ack_mask; __le32 kill_cts_mask; } __packed; /****************************************************************************** * (6) * Spectrum Management (802.11h) Commands, Responses, Notifications: * *****************************************************************************/ /* * Spectrum Management */ #define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ RXON_FILTER_CTL2HOST_MSK | \ RXON_FILTER_ACCEPT_GRP_MSK | \ RXON_FILTER_DIS_DECRYPT_MSK | \ RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ RXON_FILTER_ASSOC_MSK | \ RXON_FILTER_BCON_AWARE_MSK) struct il_measure_channel { __le32 duration; /* measurement duration in extended beacon * format */ u8 channel; /* channel to measure */ u8 type; /* see enum il_measure_type */ __le16 reserved; } __packed; /* * C_SPECTRUM_MEASUREMENT = 0x74 (command) */ struct il_spectrum_cmd { __le16 len; /* number of bytes starting from token */ u8 token; /* token id */ u8 id; /* measurement id -- 0 or 1 */ u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ u8 periodic; /* 1 = periodic */ __le16 path_loss_timeout; __le32 start_time; /* start time in extended beacon format */ __le32 reserved2; __le32 flags; /* rxon flags */ __le32 filter_flags; /* rxon filter flags */ __le16 channel_count; /* minimum 1, maximum 10 */ __le16 reserved3; struct il_measure_channel channels[10]; } __packed; /* * C_SPECTRUM_MEASUREMENT = 0x74 (response) */ struct il_spectrum_resp { u8 token; u8 id; /* id of the prior command replaced, or 0xff */ __le16 status; /* 0 - command will be handled * 1 - cannot handle (conflicts with another * measurement) */ } __packed; enum il_measurement_state { IL_MEASUREMENT_START = 0, IL_MEASUREMENT_STOP = 1, }; enum il_measurement_status { IL_MEASUREMENT_OK = 0, IL_MEASUREMENT_CONCURRENT = 1, IL_MEASUREMENT_CSA_CONFLICT = 2, IL_MEASUREMENT_TGH_CONFLICT = 3, /* 4-5 reserved */ IL_MEASUREMENT_STOPPED = 6, IL_MEASUREMENT_TIMEOUT = 7, IL_MEASUREMENT_PERIODIC_FAILED = 8, }; #define NUM_ELEMENTS_IN_HISTOGRAM 8 struct il_measurement_histogram { __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ } __packed; /* clear channel availability counters */ struct il_measurement_cca_counters { __le32 ofdm; __le32 cck; } __packed; enum il_measure_type { IL_MEASURE_BASIC = (1 << 0), IL_MEASURE_CHANNEL_LOAD = (1 << 1), IL_MEASURE_HISTOGRAM_RPI = (1 << 2), IL_MEASURE_HISTOGRAM_NOISE = (1 << 3), IL_MEASURE_FRAME = (1 << 4), /* bits 5:6 are reserved */ IL_MEASURE_IDLE = (1 << 7), }; /* * N_SPECTRUM_MEASUREMENT = 0x75 (notification only, not a command) */ struct il_spectrum_notification { u8 id; /* measurement id -- 0 or 1 */ u8 token; u8 channel_idx; /* idx in measurement channel list */ u8 state; /* 0 - start, 1 - stop */ __le32 start_time; /* lower 32-bits of TSF */ u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ u8 channel; u8 type; /* see enum il_measurement_type */ u8 reserved1; /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only * valid if applicable for measurement type requested. */ __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ __le32 cca_time; /* channel load time in usecs */ u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - * unidentified */ u8 reserved2[3]; struct il_measurement_histogram histogram; __le32 stop_time; /* lower 32-bits of TSF */ __le32 status; /* see il_measurement_status */ } __packed; /****************************************************************************** * (7) * Power Management Commands, Responses, Notifications: * *****************************************************************************/ /** * struct il_powertable_cmd - Power Table Command * @flags: See below: * * C_POWER_TBL = 0x77 (command, has simple generic response) * * PM allow: * bit 0 - '0' Driver not allow power management * '1' Driver allow PM (use rest of parameters) * * uCode send sleep notifications: * bit 1 - '0' Don't send sleep notification * '1' send sleep notification (SEND_PM_NOTIFICATION) * * Sleep over DTIM * bit 2 - '0' PM have to walk up every DTIM * '1' PM could sleep over DTIM till listen Interval. * * PCI power managed * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) * '1' !(PCI_CFG_LINK_CTRL & 0x1) * * Fast PD * bit 4 - '1' Put radio to sleep when receiving frame for others * * Force sleep Modes * bit 31/30- '00' use both mac/xtal sleeps * '01' force Mac sleep * '10' force xtal sleep * '11' Illegal set * * NOTE: if sleep_interval[SLEEP_INTRVL_TBL_SIZE-1] > DTIM period then * ucode assume sleep over DTIM is allowed and we don't need to wake up * for every DTIM. */ #define IL_POWER_VEC_SIZE 5 #define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) #define IL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) struct il3945_powertable_cmd { __le16 flags; u8 reserved[2]; __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IL_POWER_VEC_SIZE]; } __packed; struct il_powertable_cmd { __le16 flags; u8 keep_alive_seconds; /* 3945 reserved */ u8 debug_flags; /* 3945 reserved */ __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IL_POWER_VEC_SIZE]; __le32 keep_alive_beacons; } __packed; /* * N_PM_SLEEP = 0x7A (notification only, not a command) * all devices identical. */ struct il_sleep_notification { u8 pm_sleep_mode; u8 pm_wakeup_src; __le16 reserved; __le32 sleep_time; __le32 tsf_low; __le32 bcon_timer; } __packed; /* Sleep states. all devices identical. */ enum { IL_PM_NO_SLEEP = 0, IL_PM_SLP_MAC = 1, IL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, IL_PM_SLP_FULL_MAC_CARD_STATE = 3, IL_PM_SLP_PHY = 4, IL_PM_SLP_REPENT = 5, IL_PM_WAKEUP_BY_TIMER = 6, IL_PM_WAKEUP_BY_DRIVER = 7, IL_PM_WAKEUP_BY_RFKILL = 8, /* 3 reserved */ IL_PM_NUM_OF_MODES = 12, }; /* * N_CARD_STATE = 0xa1 (notification only, not a command) */ struct il_card_state_notif { __le32 flags; } __packed; #define HW_CARD_DISABLED 0x01 #define SW_CARD_DISABLED 0x02 #define CT_CARD_DISABLED 0x04 #define RXON_CARD_DISABLED 0x10 struct il_ct_kill_config { __le32 reserved; __le32 critical_temperature_M; __le32 critical_temperature_R; } __packed; /****************************************************************************** * (8) * Scan Commands, Responses, Notifications: * *****************************************************************************/ #define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) #define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) /** * struct il_scan_channel - entry in C_SCAN channel table * * One for each channel in the scan list. * Each channel can independently select: * 1) SSID for directed active scans * 2) Txpower setting (for rate specified within Tx command) * 3) How long to stay on-channel (behavior may be modified by quiet_time, * quiet_plcp_th, good_CRC_th) * * To avoid uCode errors, make sure the following are true (see comments * under struct il_scan_cmd about max_out_time and quiet_time): * 1) If using passive_dwell (i.e. passive_dwell != 0): * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) * 2) quiet_time <= active_dwell * 3) If restricting off-channel time (i.e. max_out_time !=0): * passive_dwell < max_out_time * active_dwell < max_out_time */ struct il3945_scan_channel { /* * type is defined as: * 0:0 1 = active, 0 = passive * 1:4 SSID direct bit map; if a bit is set, then corresponding * SSID IE is transmitted in probe request. * 5:7 reserved */ u8 type; u8 channel; /* band is selected by il3945_scan_cmd "flags" field */ struct il3945_tx_power tpc; __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ } __packed; /* set number of direct probes u8 type */ #define IL39_SCAN_PROBE_MASK(n) ((BIT(n) | (BIT(n) - BIT(1)))) struct il_scan_channel { /* * type is defined as: * 0:0 1 = active, 0 = passive * 1:20 SSID direct bit map; if a bit is set, then corresponding * SSID IE is transmitted in probe request. * 21:31 reserved */ __le32 type; __le16 channel; /* band is selected by il_scan_cmd "flags" field */ u8 tx_gain; /* gain for analog radio */ u8 dsp_atten; /* gain for DSP */ __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ } __packed; /* set number of direct probes __le32 type */ #define IL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) /** * struct il_ssid_ie - directed scan network information element * * Up to 20 of these may appear in C_SCAN (Note: Only 4 are in * 3945 SCAN api), selected by "type" bit field in struct il_scan_channel; * each channel may select different ssids from among the 20 (4) entries. * SSID IEs get transmitted in reverse order of entry. */ struct il_ssid_ie { u8 id; u8 len; u8 ssid[32]; } __packed; #define PROBE_OPTION_MAX_3945 4 #define PROBE_OPTION_MAX 20 #define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) #define IL_GOOD_CRC_TH_DISABLED 0 #define IL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) #define IL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) #define IL_MAX_SCAN_SIZE 1024 #define IL_MAX_CMD_SIZE 4096 /* * C_SCAN = 0x80 (command) * * The hardware scan command is very powerful; the driver can set it up to * maintain (relatively) normal network traffic while doing a scan in the * background. The max_out_time and suspend_time control the ratio of how * long the device stays on an associated network channel ("service channel") * vs. how long it's away from the service channel, i.e. tuned to other channels * for scanning. * * max_out_time is the max time off-channel (in usec), and suspend_time * is how long (in "extended beacon" format) that the scan is "suspended" * after returning to the service channel. That is, suspend_time is the * time that we stay on the service channel, doing normal work, between * scan segments. The driver may set these parameters differently to support * scanning when associated vs. not associated, and light vs. heavy traffic * loads when associated. * * After receiving this command, the device's scan engine does the following; * * 1) Sends SCAN_START notification to driver * 2) Checks to see if it has time to do scan for one channel * 3) Sends NULL packet, with power-save (PS) bit set to 1, * to tell AP that we're going off-channel * 4) Tunes to first channel in scan list, does active or passive scan * 5) Sends SCAN_RESULT notification to driver * 6) Checks to see if it has time to do scan on *next* channel in list * 7) Repeats 4-6 until it no longer has time to scan the next channel * before max_out_time expires * 8) Returns to service channel * 9) Sends NULL packet with PS=0 to tell AP that we're back * 10) Stays on service channel until suspend_time expires * 11) Repeats entire process 2-10 until list is complete * 12) Sends SCAN_COMPLETE notification * * For fast, efficient scans, the scan command also has support for staying on * a channel for just a short time, if doing active scanning and getting no * responses to the transmitted probe request. This time is controlled by * quiet_time, and the number of received packets below which a channel is * considered "quiet" is controlled by quiet_plcp_threshold. * * For active scanning on channels that have regulatory restrictions against * blindly transmitting, the scan can listen before transmitting, to make sure * that there is already legitimate activity on the channel. If enough * packets are cleanly received on the channel (controlled by good_CRC_th, * typical value 1), the scan engine starts transmitting probe requests. * * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. * * To avoid uCode errors, see timing restrictions described under * struct il_scan_channel. */ struct il3945_scan_cmd { __le16 len; u8 reserved0; u8 channel_count; /* # channels in channel list */ __le16 quiet_time; /* dwell only this # millisecs on quiet channel * (only for active scan) */ __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ __le16 good_CRC_th; /* passive -> active promotion threshold */ __le16 reserved1; __le32 max_out_time; /* max usec to be away from associated (service) * channel */ __le32 suspend_time; /* pause scan this long (in "extended beacon * format") when returning to service channel: * 3945; 31:24 # beacons, 19:0 additional usec, * 4965; 31:22 # beacons, 21:0 additional usec. */ __le32 flags; /* RXON_FLG_* */ __le32 filter_flags; /* RXON_FILTER_* */ /* For active scans (set to all-0s for passive scans). * Does not include payload. Must specify Tx rate; no rate scaling. */ struct il3945_tx_cmd tx_cmd; /* For directed active scans (set to all-0s otherwise) */ struct il_ssid_ie direct_scan[PROBE_OPTION_MAX_3945]; /* * Probe request frame, followed by channel list. * * Size of probe request frame is specified by byte count in tx_cmd. * Channel list follows immediately after probe request frame. * Number of channels in list is specified by channel_count. * Each channel in list is of type: * * struct il3945_scan_channel channels[0]; * * NOTE: Only one band of channels can be scanned per pass. You * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait * for one scan to complete (i.e. receive N_SCAN_COMPLETE) * before requesting another scan. */ u8 data[0]; } __packed; struct il_scan_cmd { __le16 len; u8 reserved0; u8 channel_count; /* # channels in channel list */ __le16 quiet_time; /* dwell only this # millisecs on quiet channel * (only for active scan) */ __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ __le16 good_CRC_th; /* passive -> active promotion threshold */ __le16 rx_chain; /* RXON_RX_CHAIN_* */ __le32 max_out_time; /* max usec to be away from associated (service) * channel */ __le32 suspend_time; /* pause scan this long (in "extended beacon * format") when returning to service chnl: * 3945; 31:24 # beacons, 19:0 additional usec, * 4965; 31:22 # beacons, 21:0 additional usec. */ __le32 flags; /* RXON_FLG_* */ __le32 filter_flags; /* RXON_FILTER_* */ /* For active scans (set to all-0s for passive scans). * Does not include payload. Must specify Tx rate; no rate scaling. */ struct il_tx_cmd tx_cmd; /* For directed active scans (set to all-0s otherwise) */ struct il_ssid_ie direct_scan[PROBE_OPTION_MAX]; /* * Probe request frame, followed by channel list. * * Size of probe request frame is specified by byte count in tx_cmd. * Channel list follows immediately after probe request frame. * Number of channels in list is specified by channel_count. * Each channel in list is of type: * * struct il_scan_channel channels[0]; * * NOTE: Only one band of channels can be scanned per pass. You * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait * for one scan to complete (i.e. receive N_SCAN_COMPLETE) * before requesting another scan. */ u8 data[0]; } __packed; /* Can abort will notify by complete notification with abort status. */ #define CAN_ABORT_STATUS cpu_to_le32(0x1) /* complete notification statuses */ #define ABORT_STATUS 0x2 /* * C_SCAN = 0x80 (response) */ struct il_scanreq_notification { __le32 status; /* 1: okay, 2: cannot fulfill request */ } __packed; /* * N_SCAN_START = 0x82 (notification only, not a command) */ struct il_scanstart_notification { __le32 tsf_low; __le32 tsf_high; __le32 beacon_timer; u8 channel; u8 band; u8 reserved[2]; __le32 status; } __packed; #define SCAN_OWNER_STATUS 0x1 #define MEASURE_OWNER_STATUS 0x2 #define IL_PROBE_STATUS_OK 0 #define IL_PROBE_STATUS_TX_FAILED BIT(0) /* error statuses combined with TX_FAILED */ #define IL_PROBE_STATUS_FAIL_TTL BIT(1) #define IL_PROBE_STATUS_FAIL_BT BIT(2) #define NUMBER_OF_STATS 1 /* first __le32 is good CRC */ /* * N_SCAN_RESULTS = 0x83 (notification only, not a command) */ struct il_scanresults_notification { u8 channel; u8 band; u8 probe_status; u8 num_probe_not_sent; /* not enough time to send */ __le32 tsf_low; __le32 tsf_high; __le32 stats[NUMBER_OF_STATS]; } __packed; /* * N_SCAN_COMPLETE = 0x84 (notification only, not a command) */ struct il_scancomplete_notification { u8 scanned_channels; u8 status; u8 last_channel; __le32 tsf_low; __le32 tsf_high; } __packed; /****************************************************************************** * (9) * IBSS/AP Commands and Notifications: * *****************************************************************************/ enum il_ibss_manager { IL_NOT_IBSS_MANAGER = 0, IL_IBSS_MANAGER = 1, }; /* * N_BEACON = 0x90 (notification only, not a command) */ struct il3945_beacon_notif { struct il3945_tx_resp beacon_notify_hdr; __le32 low_tsf; __le32 high_tsf; __le32 ibss_mgr_status; } __packed; struct il4965_beacon_notif { struct il4965_tx_resp beacon_notify_hdr; __le32 low_tsf; __le32 high_tsf; __le32 ibss_mgr_status; } __packed; /* * C_TX_BEACON= 0x91 (command, has simple generic response) */ struct il3945_tx_beacon_cmd { struct il3945_tx_cmd tx; __le16 tim_idx; u8 tim_size; u8 reserved1; struct ieee80211_hdr frame[0]; /* beacon frame */ } __packed; struct il_tx_beacon_cmd { struct il_tx_cmd tx; __le16 tim_idx; u8 tim_size; u8 reserved1; struct ieee80211_hdr frame[0]; /* beacon frame */ } __packed; /****************************************************************************** * (10) * Statistics Commands and Notifications: * *****************************************************************************/ #define IL_TEMP_CONVERT 260 #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 /* Used for passing to driver number of successes and failures per rate */ struct rate_histogram { union { __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; } success; union { __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; } failed; } __packed; /* stats command response */ struct iwl39_stats_rx_phy { __le32 ina_cnt; __le32 fina_cnt; __le32 plcp_err; __le32 crc32_err; __le32 overrun_err; __le32 early_overrun_err; __le32 crc32_good; __le32 false_alarm_cnt; __le32 fina_sync_err_cnt; __le32 sfd_timeout; __le32 fina_timeout; __le32 unresponded_rts; __le32 rxe_frame_limit_overrun; __le32 sent_ack_cnt; __le32 sent_cts_cnt; } __packed; struct iwl39_stats_rx_non_phy { __le32 bogus_cts; /* CTS received when not expecting CTS */ __le32 bogus_ack; /* ACK received when not expecting ACK */ __le32 non_bssid_frames; /* number of frames with BSSID that * doesn't belong to the STA BSSID */ __le32 filtered_frames; /* count frames that were dumped in the * filtering process */ __le32 non_channel_beacons; /* beacons with our bss id but not on * our serving channel */ } __packed; struct iwl39_stats_rx { struct iwl39_stats_rx_phy ofdm; struct iwl39_stats_rx_phy cck; struct iwl39_stats_rx_non_phy general; } __packed; struct iwl39_stats_tx { __le32 preamble_cnt; __le32 rx_detected_cnt; __le32 bt_prio_defer_cnt; __le32 bt_prio_kill_cnt; __le32 few_bytes_cnt; __le32 cts_timeout; __le32 ack_timeout; __le32 expected_ack_cnt; __le32 actual_ack_cnt; } __packed; struct stats_dbg { __le32 burst_check; __le32 burst_count; __le32 wait_for_silence_timeout_cnt; __le32 reserved[3]; } __packed; struct iwl39_stats_div { __le32 tx_on_a; __le32 tx_on_b; __le32 exec_time; __le32 probe_time; } __packed; struct iwl39_stats_general { __le32 temperature; struct stats_dbg dbg; __le32 sleep_time; __le32 slots_out; __le32 slots_idle; __le32 ttl_timestamp; struct iwl39_stats_div div; } __packed; struct stats_rx_phy { __le32 ina_cnt; __le32 fina_cnt; __le32 plcp_err; __le32 crc32_err; __le32 overrun_err; __le32 early_overrun_err; __le32 crc32_good; __le32 false_alarm_cnt; __le32 fina_sync_err_cnt; __le32 sfd_timeout; __le32 fina_timeout; __le32 unresponded_rts; __le32 rxe_frame_limit_overrun; __le32 sent_ack_cnt; __le32 sent_cts_cnt; __le32 sent_ba_rsp_cnt; __le32 dsp_self_kill; __le32 mh_format_err; __le32 re_acq_main_rssi_sum; __le32 reserved3; } __packed; struct stats_rx_ht_phy { __le32 plcp_err; __le32 overrun_err; __le32 early_overrun_err; __le32 crc32_good; __le32 crc32_err; __le32 mh_format_err; __le32 agg_crc32_good; __le32 agg_mpdu_cnt; __le32 agg_cnt; __le32 unsupport_mcs; } __packed; #define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) struct stats_rx_non_phy { __le32 bogus_cts; /* CTS received when not expecting CTS */ __le32 bogus_ack; /* ACK received when not expecting ACK */ __le32 non_bssid_frames; /* number of frames with BSSID that * doesn't belong to the STA BSSID */ __le32 filtered_frames; /* count frames that were dumped in the * filtering process */ __le32 non_channel_beacons; /* beacons with our bss id but not on * our serving channel */ __le32 channel_beacons; /* beacons with our bss id and in our * serving channel */ __le32 num_missed_bcon; /* number of missed beacons */ __le32 adc_rx_saturation_time; /* count in 0.8us units the time the * ADC was in saturation */ __le32 ina_detection_search_time; /* total time (in 0.8us) searched * for INA */ __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ __le32 interference_data_flag; /* flag for interference data * availability. 1 when data is * available. */ __le32 channel_load; /* counts RX Enable time in uSec */ __le32 dsp_false_alarms; /* DSP false alarm (both OFDM * and CCK) counter */ __le32 beacon_rssi_a; __le32 beacon_rssi_b; __le32 beacon_rssi_c; __le32 beacon_energy_a; __le32 beacon_energy_b; __le32 beacon_energy_c; } __packed; struct stats_rx { struct stats_rx_phy ofdm; struct stats_rx_phy cck; struct stats_rx_non_phy general; struct stats_rx_ht_phy ofdm_ht; } __packed; /** * struct stats_tx_power - current tx power * * @ant_a: current tx power on chain a in 1/2 dB step * @ant_b: current tx power on chain b in 1/2 dB step * @ant_c: current tx power on chain c in 1/2 dB step */ struct stats_tx_power { u8 ant_a; u8 ant_b; u8 ant_c; u8 reserved; } __packed; struct stats_tx_non_phy_agg { __le32 ba_timeout; __le32 ba_reschedule_frames; __le32 scd_query_agg_frame_cnt; __le32 scd_query_no_agg; __le32 scd_query_agg; __le32 scd_query_mismatch; __le32 frame_not_ready; __le32 underrun; __le32 bt_prio_kill; __le32 rx_ba_rsp_cnt; } __packed; struct stats_tx { __le32 preamble_cnt; __le32 rx_detected_cnt; __le32 bt_prio_defer_cnt; __le32 bt_prio_kill_cnt; __le32 few_bytes_cnt; __le32 cts_timeout; __le32 ack_timeout; __le32 expected_ack_cnt; __le32 actual_ack_cnt; __le32 dump_msdu_cnt; __le32 burst_abort_next_frame_mismatch_cnt; __le32 burst_abort_missing_next_frame_cnt; __le32 cts_timeout_collision; __le32 ack_or_ba_timeout_collision; struct stats_tx_non_phy_agg agg; __le32 reserved1; } __packed; struct stats_div { __le32 tx_on_a; __le32 tx_on_b; __le32 exec_time; __le32 probe_time; __le32 reserved1; __le32 reserved2; } __packed; struct stats_general_common { __le32 temperature; /* radio temperature */ struct stats_dbg dbg; __le32 sleep_time; __le32 slots_out; __le32 slots_idle; __le32 ttl_timestamp; struct stats_div div; __le32 rx_enable_counter; /* * num_of_sos_states: * count the number of times we have to re-tune * in order to get out of bad PHY status */ __le32 num_of_sos_states; } __packed; struct stats_general { struct stats_general_common common; __le32 reserved2; __le32 reserved3; } __packed; #define UCODE_STATS_CLEAR_MSK (0x1 << 0) #define UCODE_STATS_FREQUENCY_MSK (0x1 << 1) #define UCODE_STATS_NARROW_BAND_MSK (0x1 << 2) /* * C_STATS = 0x9c, * all devices identical. * * This command triggers an immediate response containing uCode stats. * The response is in the same format as N_STATS 0x9d, below. * * If the CLEAR_STATS configuration flag is set, uCode will clear its * internal copy of the stats (counters) after issuing the response. * This flag does not affect N_STATSs after beacons (see below). * * If the DISABLE_NOTIF configuration flag is set, uCode will not issue * N_STATSs after received beacons (see below). This flag * does not affect the response to the C_STATS 0x9c itself. */ #define IL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ #define IL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2) /* see above */ struct il_stats_cmd { __le32 configuration_flags; /* IL_STATS_CONF_* */ } __packed; /* * N_STATS = 0x9d (notification only, not a command) * * By default, uCode issues this notification after receiving a beacon * while associated. To disable this behavior, set DISABLE_NOTIF flag in the * C_STATS 0x9c, above. * * Statistics counters continue to increment beacon after beacon, but are * cleared when changing channels or when driver issues C_STATS * 0x9c with CLEAR_STATS bit set (see above). * * uCode also issues this notification during scans. uCode clears stats * appropriately so that each notification contains stats for only the * one channel that has just been scanned. */ #define STATS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) #define STATS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) struct il3945_notif_stats { __le32 flag; struct iwl39_stats_rx rx; struct iwl39_stats_tx tx; struct iwl39_stats_general general; } __packed; struct il_notif_stats { __le32 flag; struct stats_rx rx; struct stats_tx tx; struct stats_general general; } __packed; /* * N_MISSED_BEACONS = 0xa2 (notification only, not a command) * * uCode send N_MISSED_BEACONS to driver when detect beacon missed * in regardless of how many missed beacons, which mean when driver receive the * notification, inside the command, it can find all the beacons information * which include number of total missed beacons, number of consecutive missed * beacons, number of beacons received and number of beacons expected to * receive. * * If uCode detected consecutive_missed_beacons > 5, it will reset the radio * in order to bring the radio/PHY back to working state; which has no relation * to when driver will perform sensitivity calibration. * * Driver should set it own missed_beacon_threshold to decide when to perform * sensitivity calibration based on number of consecutive missed beacons in * order to improve overall performance, especially in noisy environment. * */ #define IL_MISSED_BEACON_THRESHOLD_MIN (1) #define IL_MISSED_BEACON_THRESHOLD_DEF (5) #define IL_MISSED_BEACON_THRESHOLD_MAX IL_MISSED_BEACON_THRESHOLD_DEF struct il_missed_beacon_notif { __le32 consecutive_missed_beacons; __le32 total_missed_becons; __le32 num_expected_beacons; __le32 num_recvd_beacons; } __packed; /****************************************************************************** * (11) * Rx Calibration Commands: * * With the uCode used for open source drivers, most Tx calibration (except * for Tx Power) and most Rx calibration is done by uCode during the * "initialize" phase of uCode boot. Driver must calibrate only: * * 1) Tx power (depends on temperature), described elsewhere * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) * 3) Receiver sensitivity (to optimize signal detection) * *****************************************************************************/ /** * C_SENSITIVITY = 0xa8 (command, has simple generic response) * * This command sets up the Rx signal detector for a sensitivity level that * is high enough to lock onto all signals within the associated network, * but low enough to ignore signals that are below a certain threshold, so as * not to have too many "false alarms". False alarms are signals that the * Rx DSP tries to lock onto, but then discards after determining that they * are noise. * * The optimum number of false alarms is between 5 and 50 per 200 TUs * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. * time listening, not transmitting). Driver must adjust sensitivity so that * the ratio of actual false alarms to actual Rx time falls within this range. * * While associated, uCode delivers N_STATSs after each * received beacon. These provide information to the driver to analyze the * sensitivity. Don't analyze stats that come in from scanning, or any * other non-associated-network source. Pertinent stats include: * * From "general" stats (struct stats_rx_non_phy): * * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) * Measure of energy of desired signal. Used for establishing a level * below which the device does not detect signals. * * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) * Measure of background noise in silent period after beacon. * * channel_load * uSecs of actual Rx time during beacon period (varies according to * how much time was spent transmitting). * * From "cck" and "ofdm" stats (struct stats_rx_phy), separately: * * false_alarm_cnt * Signal locks abandoned early (before phy-level header). * * plcp_err * Signal locks abandoned late (during phy-level header). * * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from * beacon to beacon, i.e. each value is an accumulation of all errors * before and including the latest beacon. Values will wrap around to 0 * after counting up to 2^32 - 1. Driver must differentiate vs. * previous beacon's values to determine # false alarms in the current * beacon period. * * Total number of false alarms = false_alarms + plcp_errs * * For OFDM, adjust the following table entries in struct il_sensitivity_cmd * (notice that the start points for OFDM are at or close to settings for * maximum sensitivity): * * START / MIN / MAX * HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX 90 / 85 / 120 * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX 170 / 170 / 210 * HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX 105 / 105 / 140 * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX 220 / 220 / 270 * * If actual rate of OFDM false alarms (+ plcp_errors) is too high * (greater than 50 for each 204.8 msecs listening), reduce sensitivity * by *adding* 1 to all 4 of the table entries above, up to the max for * each entry. Conversely, if false alarm rate is too low (less than 5 * for each 204.8 msecs listening), *subtract* 1 from each entry to * increase sensitivity. * * For CCK sensitivity, keep track of the following: * * 1). 20-beacon history of maximum background noise, indicated by * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the * 3 receivers. For any given beacon, the "silence reference" is * the maximum of last 60 samples (20 beacons * 3 receivers). * * 2). 10-beacon history of strongest signal level, as indicated * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, * i.e. the strength of the signal through the best receiver at the * moment. These measurements are "upside down", with lower values * for stronger signals, so max energy will be *minimum* value. * * Then for any given beacon, the driver must determine the *weakest* * of the strongest signals; this is the minimum level that needs to be * successfully detected, when using the best receiver at the moment. * "Max cck energy" is the maximum (higher value means lower energy!) * of the last 10 minima. Once this is determined, driver must add * a little margin by adding "6" to it. * * 3). Number of consecutive beacon periods with too few false alarms. * Reset this to 0 at the first beacon period that falls within the * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). * * Then, adjust the following CCK table entries in struct il_sensitivity_cmd * (notice that the start points for CCK are at maximum sensitivity): * * START / MIN / MAX * HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX 125 / 125 / 200 * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX 200 / 200 / 400 * HD_MIN_ENERGY_CCK_DET_IDX 100 / 0 / 100 * * If actual rate of CCK false alarms (+ plcp_errors) is too high * (greater than 50 for each 204.8 msecs listening), method for reducing * sensitivity is: * * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX, * up to max 400. * * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX is < 160, * sensitivity has been reduced a significant amount; bring it up to * a moderate 161. Otherwise, *add* 3, up to max 200. * * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX is > 160, * sensitivity has been reduced only a moderate or small amount; * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_IDX, * down to min 0. Otherwise (if gain has been significantly reduced), * don't change the HD_MIN_ENERGY_CCK_DET_IDX value. * * b) Save a snapshot of the "silence reference". * * If actual rate of CCK false alarms (+ plcp_errors) is too low * (less than 5 for each 204.8 msecs listening), method for increasing * sensitivity is used only if: * * 1a) Previous beacon did not have too many false alarms * 1b) AND difference between previous "silence reference" and current * "silence reference" (prev - current) is 2 or more, * OR 2) 100 or more consecutive beacon periods have had rate of * less than 5 false alarms per 204.8 milliseconds rx time. * * Method for increasing sensitivity: * * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX, * down to min 125. * * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX, * down to min 200. * * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_IDX, up to max 100. * * If actual rate of CCK false alarms (+ plcp_errors) is within good range * (between 5 and 50 for each 204.8 msecs listening): * * 1) Save a snapshot of the silence reference. * * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), * give some extra margin to energy threshold by *subtracting* 8 * from value in HD_MIN_ENERGY_CCK_DET_IDX. * * For all cases (too few, too many, good range), make sure that the CCK * detection threshold (energy) is below the energy level for robust * detection over the past 10 beacon periods, the "Max cck energy". * Lower values mean higher energy; this means making sure that the value * in HD_MIN_ENERGY_CCK_DET_IDX is at or *above* "Max cck energy". * */ /* * Table entries in C_SENSITIVITY (struct il_sensitivity_cmd) */ #define HD_TBL_SIZE (11) /* number of entries */ #define HD_MIN_ENERGY_CCK_DET_IDX (0) /* table idxes */ #define HD_MIN_ENERGY_OFDM_DET_IDX (1) #define HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX (2) #define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX (3) #define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX (4) #define HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX (5) #define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX (6) #define HD_BARKER_CORR_TH_ADD_MIN_IDX (7) #define HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX (8) #define HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX (9) #define HD_OFDM_ENERGY_TH_IN_IDX (10) /* Control field in struct il_sensitivity_cmd */ #define C_SENSITIVITY_CONTROL_DEFAULT_TBL cpu_to_le16(0) #define C_SENSITIVITY_CONTROL_WORK_TBL cpu_to_le16(1) /** * struct il_sensitivity_cmd * @control: (1) updates working table, (0) updates default table * @table: energy threshold values, use HD_* as idx into table * * Always use "1" in "control" to update uCode's working table and DSP. */ struct il_sensitivity_cmd { __le16 control; /* always use "1" */ __le16 table[HD_TBL_SIZE]; /* use HD_* as idx */ } __packed; /** * C_PHY_CALIBRATION = 0xb0 (command, has simple generic response) * * This command sets the relative gains of 4965 device's 3 radio receiver chains. * * After the first association, driver should accumulate signal and noise * stats from the N_STATSs that follow the first 20 * beacons from the associated network (don't collect stats that come * in from scanning, or any other non-network source). * * DISCONNECTED ANTENNA: * * Driver should determine which antennas are actually connected, by comparing * average beacon signal levels for the 3 Rx chains. Accumulate (add) the * following values over 20 beacons, one accumulator for each of the chains * a/b/c, from struct stats_rx_non_phy: * * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) * * Find the strongest signal from among a/b/c. Compare the other two to the * strongest. If any signal is more than 15 dB (times 20, unless you * divide the accumulated values by 20) below the strongest, the driver * considers that antenna to be disconnected, and should not try to use that * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, * driver should declare the stronger one as connected, and attempt to use it * (A and B are the only 2 Tx chains!). * * * RX BALANCE: * * Driver should balance the 3 receivers (but just the ones that are connected * to antennas, see above) for gain, by comparing the average signal levels * detected during the silence after each beacon (background noise). * Accumulate (add) the following values over 20 beacons, one accumulator for * each of the chains a/b/c, from struct stats_rx_non_phy: * * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) * * Find the weakest background noise level from among a/b/c. This Rx chain * will be the reference, with 0 gain adjustment. Attenuate other channels by * finding noise difference: * * (accum_noise[i] - accum_noise[reference]) / 30 * * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. * For use in diff_gain_[abc] fields of struct il_calibration_cmd, the * driver should limit the difference results to a range of 0-3 (0-4.5 dB), * and set bit 2 to indicate "reduce gain". The value for the reference * (weakest) chain should be "0". * * diff_gain_[abc] bit fields: * 2: (1) reduce gain, (0) increase gain * 1-0: amount of gain, units of 1.5 dB */ /* Phy calibration command for series */ /* The default calibrate table size if not specified by firmware */ #define IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 enum { IL_PHY_CALIBRATE_DIFF_GAIN_CMD = 7, IL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE = 19, }; #define IL_MAX_PHY_CALIBRATE_TBL_SIZE (253) struct il_calib_hdr { u8 op_code; u8 first_group; u8 groups_num; u8 data_valid; } __packed; /* IL_PHY_CALIBRATE_DIFF_GAIN_CMD (7) */ struct il_calib_diff_gain_cmd { struct il_calib_hdr hdr; s8 diff_gain_a; /* see above */ s8 diff_gain_b; s8 diff_gain_c; u8 reserved1; } __packed; /****************************************************************************** * (12) * Miscellaneous Commands: * *****************************************************************************/ /* * LEDs Command & Response * C_LEDS = 0x48 (command, has simple generic response) * * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), * this command turns it on or off, or sets up a periodic blinking cycle. */ struct il_led_cmd { __le32 interval; /* "interval" in uSec */ u8 id; /* 1: Activity, 2: Link, 3: Tech */ u8 off; /* # intervals off while blinking; * "0", with >0 "on" value, turns LED on */ u8 on; /* # intervals on while blinking; * "0", regardless of "off", turns LED off */ u8 reserved; } __packed; /****************************************************************************** * (13) * Union of all expected notifications/responses: * *****************************************************************************/ #define IL_RX_FRAME_SIZE_MSK 0x00003fff struct il_rx_pkt { /* * The first 4 bytes of the RX frame header contain both the RX frame * size and some flags. * Bit fields: * 31: flag flush RB request * 30: flag ignore TC (terminal counter) request * 29: flag fast IRQ request * 28-14: Reserved * 13-00: RX frame size */ __le32 len_n_flags; struct il_cmd_header hdr; union { struct il3945_rx_frame rx_frame; struct il3945_tx_resp tx_resp; struct il3945_beacon_notif beacon_status; struct il_alive_resp alive_frame; struct il_spectrum_notification spectrum_notif; struct il_csa_notification csa_notif; struct il_error_resp err_resp; struct il_card_state_notif card_state_notif; struct il_add_sta_resp add_sta; struct il_rem_sta_resp rem_sta; struct il_sleep_notification sleep_notif; struct il_spectrum_resp spectrum; struct il_notif_stats stats; struct il_compressed_ba_resp compressed_ba; struct il_missed_beacon_notif missed_beacon; __le32 status; u8 raw[0]; } u; } __packed; #endif /* __il_commands_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/4965-rs.c0000644000175000017500000023775012026211315023500 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "common.h" #include "4965.h" #define IL4965_RS_NAME "iwl-4965-rs" #define NUM_TRY_BEFORE_ANT_TOGGLE 1 #define IL_NUMBER_TRY 1 #define IL_HT_NUMBER_TRY 3 #define RATE_MAX_WINDOW 62 /* # tx in history win */ #define RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ #define RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ /* max allowed rate miss before sync LQ cmd */ #define IL_MISSED_RATE_MAX 15 /* max time to accum history 2 seconds */ #define RATE_SCALE_FLUSH_INTVL (3*HZ) static u8 rs_ht_to_legacy[] = { RATE_6M_IDX, RATE_6M_IDX, RATE_6M_IDX, RATE_6M_IDX, RATE_6M_IDX, RATE_6M_IDX, RATE_9M_IDX, RATE_12M_IDX, RATE_18M_IDX, RATE_24M_IDX, RATE_36M_IDX, RATE_48M_IDX, RATE_54M_IDX }; static const u8 ant_toggle_lookup[] = { /*ANT_NONE -> */ ANT_NONE, /*ANT_A -> */ ANT_B, /*ANT_B -> */ ANT_C, /*ANT_AB -> */ ANT_BC, /*ANT_C -> */ ANT_A, /*ANT_AC -> */ ANT_AB, /*ANT_BC -> */ ANT_AC, /*ANT_ABC -> */ ANT_ABC, }; #define IL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ [RATE_##r##M_IDX] = { RATE_##r##M_PLCP, \ RATE_SISO_##s##M_PLCP, \ RATE_MIMO2_##s##M_PLCP,\ RATE_##r##M_IEEE, \ RATE_##ip##M_IDX, \ RATE_##in##M_IDX, \ RATE_##rp##M_IDX, \ RATE_##rn##M_IDX, \ RATE_##pp##M_IDX, \ RATE_##np##M_IDX } /* * Parameter order: * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate * * If there isn't a valid next or previous rate then INV is used which * maps to RATE_INVALID * */ const struct il_rate_info il_rates[RATE_COUNT] = { IL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ IL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ IL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ IL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ IL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ IL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ IL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ IL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ IL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ IL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ IL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ IL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ IL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ }; static int il4965_hwrate_to_plcp_idx(u32 rate_n_flags) { int idx = 0; /* HT rate format */ if (rate_n_flags & RATE_MCS_HT_MSK) { idx = (rate_n_flags & 0xff); if (idx >= RATE_MIMO2_6M_PLCP) idx = idx - RATE_MIMO2_6M_PLCP; idx += IL_FIRST_OFDM_RATE; /* skip 9M not supported in ht */ if (idx >= RATE_9M_IDX) idx += 1; if (idx >= IL_FIRST_OFDM_RATE && idx <= IL_LAST_OFDM_RATE) return idx; /* legacy rate format, search for match in table */ } else { for (idx = 0; idx < ARRAY_SIZE(il_rates); idx++) if (il_rates[idx].plcp == (rate_n_flags & 0xFF)) return idx; } return -1; } static void il4965_rs_rate_scale_perform(struct il_priv *il, struct sk_buff *skb, struct ieee80211_sta *sta, struct il_lq_sta *lq_sta); static void il4965_rs_fill_link_cmd(struct il_priv *il, struct il_lq_sta *lq_sta, u32 rate_n_flags); static void il4965_rs_stay_in_table(struct il_lq_sta *lq_sta, bool force_search); #ifdef CONFIG_MAC80211_DEBUGFS static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 *rate_n_flags, int idx); #else static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) { } #endif /** * The following tables contain the expected throughput metrics for all rates * * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits * * where invalid entries are zeros. * * CCK rates are only valid in legacy table and will only be used in G * (2.4 GHz) band. */ static s32 expected_tpt_legacy[RATE_COUNT] = { 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 }; static s32 expected_tpt_siso20MHz[4][RATE_COUNT] = { {0, 0, 0, 0, 42, 0, 76, 102, 124, 158, 183, 193, 202}, /* Norm */ {0, 0, 0, 0, 46, 0, 82, 110, 132, 167, 192, 202, 210}, /* SGI */ {0, 0, 0, 0, 48, 0, 93, 135, 176, 251, 319, 351, 381}, /* AGG */ {0, 0, 0, 0, 53, 0, 102, 149, 193, 275, 348, 381, 413}, /* AGG+SGI */ }; static s32 expected_tpt_siso40MHz[4][RATE_COUNT] = { {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ {0, 0, 0, 0, 96, 0, 182, 259, 328, 451, 553, 598, 640}, /* AGG */ {0, 0, 0, 0, 106, 0, 199, 282, 357, 487, 593, 640, 683}, /* AGG+SGI */ }; static s32 expected_tpt_mimo2_20MHz[4][RATE_COUNT] = { {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250}, /* Norm */ {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256}, /* SGI */ {0, 0, 0, 0, 92, 0, 175, 250, 317, 436, 534, 578, 619}, /* AGG */ {0, 0, 0, 0, 102, 0, 192, 273, 344, 470, 573, 619, 660}, /* AGG+SGI */ }; static s32 expected_tpt_mimo2_40MHz[4][RATE_COUNT] = { {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ {0, 0, 0, 0, 180, 0, 327, 446, 545, 708, 828, 878, 922}, /* AGG */ {0, 0, 0, 0, 197, 0, 355, 481, 584, 752, 872, 922, 966}, /* AGG+SGI */ }; /* mbps, mcs */ static const struct il_rate_mcs_info il_rate_mcs[RATE_COUNT] = { {"1", "BPSK DSSS"}, {"2", "QPSK DSSS"}, {"5.5", "BPSK CCK"}, {"11", "QPSK CCK"}, {"6", "BPSK 1/2"}, {"9", "BPSK 1/2"}, {"12", "QPSK 1/2"}, {"18", "QPSK 3/4"}, {"24", "16QAM 1/2"}, {"36", "16QAM 3/4"}, {"48", "64QAM 2/3"}, {"54", "64QAM 3/4"}, {"60", "64QAM 5/6"}, }; #define MCS_IDX_PER_STREAM (8) static inline u8 il4965_rs_extract_rate(u32 rate_n_flags) { return (u8) (rate_n_flags & 0xFF); } static void il4965_rs_rate_scale_clear_win(struct il_rate_scale_data *win) { win->data = 0; win->success_counter = 0; win->success_ratio = IL_INVALID_VALUE; win->counter = 0; win->average_tpt = IL_INVALID_VALUE; win->stamp = 0; } static inline u8 il4965_rs_is_valid_ant(u8 valid_antenna, u8 ant_type) { return (ant_type & valid_antenna) == ant_type; } /* * removes the old data from the stats. All data that is older than * TID_MAX_TIME_DIFF, will be deleted. */ static void il4965_rs_tl_rm_old_stats(struct il_traffic_load *tl, u32 curr_time) { /* The oldest age we want to keep */ u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; while (tl->queue_count && tl->time_stamp < oldest_time) { tl->total -= tl->packet_count[tl->head]; tl->packet_count[tl->head] = 0; tl->time_stamp += TID_QUEUE_CELL_SPACING; tl->queue_count--; tl->head++; if (tl->head >= TID_QUEUE_MAX_SIZE) tl->head = 0; } } /* * increment traffic load value for tid and also remove * any old values if passed the certain time period */ static u8 il4965_rs_tl_add_packet(struct il_lq_sta *lq_data, struct ieee80211_hdr *hdr) { u32 curr_time = jiffies_to_msecs(jiffies); u32 time_diff; s32 idx; struct il_traffic_load *tl = NULL; u8 tid; if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } else return MAX_TID_COUNT; if (unlikely(tid >= TID_MAX_LOAD_COUNT)) return MAX_TID_COUNT; tl = &lq_data->load[tid]; curr_time -= curr_time % TID_ROUND_VALUE; /* Happens only for the first packet. Initialize the data */ if (!(tl->queue_count)) { tl->total = 1; tl->time_stamp = curr_time; tl->queue_count = 1; tl->head = 0; tl->packet_count[0] = 1; return MAX_TID_COUNT; } time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); idx = time_diff / TID_QUEUE_CELL_SPACING; /* The history is too long: remove data that is older than */ /* TID_MAX_TIME_DIFF */ if (idx >= TID_QUEUE_MAX_SIZE) il4965_rs_tl_rm_old_stats(tl, curr_time); idx = (tl->head + idx) % TID_QUEUE_MAX_SIZE; tl->packet_count[idx] = tl->packet_count[idx] + 1; tl->total = tl->total + 1; if ((idx + 1) > tl->queue_count) tl->queue_count = idx + 1; return tid; } /* get the traffic load value for tid */ static u32 il4965_rs_tl_get_load(struct il_lq_sta *lq_data, u8 tid) { u32 curr_time = jiffies_to_msecs(jiffies); u32 time_diff; s32 idx; struct il_traffic_load *tl = NULL; if (tid >= TID_MAX_LOAD_COUNT) return 0; tl = &(lq_data->load[tid]); curr_time -= curr_time % TID_ROUND_VALUE; if (!(tl->queue_count)) return 0; time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); idx = time_diff / TID_QUEUE_CELL_SPACING; /* The history is too long: remove data that is older than */ /* TID_MAX_TIME_DIFF */ if (idx >= TID_QUEUE_MAX_SIZE) il4965_rs_tl_rm_old_stats(tl, curr_time); return tl->total; } static int il4965_rs_tl_turn_on_agg_for_tid(struct il_priv *il, struct il_lq_sta *lq_data, u8 tid, struct ieee80211_sta *sta) { int ret = -EAGAIN; u32 load; load = il4965_rs_tl_get_load(lq_data, tid); if (load > IL_AGG_LOAD_THRESHOLD) { D_HT("Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); ret = ieee80211_start_tx_ba_session(sta, tid, 5000); if (ret == -EAGAIN) { /* * driver and mac80211 is out of sync * this might be cause by reloading firmware * stop the tx ba session here */ IL_ERR("Fail start Tx agg on tid: %d\n", tid); ieee80211_stop_tx_ba_session(sta, tid); } } else D_HT("Aggregation not enabled for tid %d because load = %u\n", tid, load); return ret; } static void il4965_rs_tl_turn_on_agg(struct il_priv *il, u8 tid, struct il_lq_sta *lq_data, struct ieee80211_sta *sta) { if (tid < TID_MAX_LOAD_COUNT) il4965_rs_tl_turn_on_agg_for_tid(il, lq_data, tid, sta); else IL_ERR("tid exceeds max load count: %d/%d\n", tid, TID_MAX_LOAD_COUNT); } static inline int il4965_get_il4965_num_of_ant_from_rate(u32 rate_n_flags) { return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); } /* * Static function to get the expected throughput from an il_scale_tbl_info * that wraps a NULL pointer check */ static s32 il4965_get_expected_tpt(struct il_scale_tbl_info *tbl, int rs_idx) { if (tbl->expected_tpt) return tbl->expected_tpt[rs_idx]; return 0; } /** * il4965_rs_collect_tx_data - Update the success/failure sliding win * * We keep a sliding win of the last 62 packets transmitted * at this rate. win->data contains the bitmask of successful * packets. */ static int il4965_rs_collect_tx_data(struct il_scale_tbl_info *tbl, int scale_idx, int attempts, int successes) { struct il_rate_scale_data *win = NULL; static const u64 mask = (((u64) 1) << (RATE_MAX_WINDOW - 1)); s32 fail_count, tpt; if (scale_idx < 0 || scale_idx >= RATE_COUNT) return -EINVAL; /* Select win for current tx bit rate */ win = &(tbl->win[scale_idx]); /* Get expected throughput */ tpt = il4965_get_expected_tpt(tbl, scale_idx); /* * Keep track of only the latest 62 tx frame attempts in this rate's * history win; anything older isn't really relevant any more. * If we have filled up the sliding win, drop the oldest attempt; * if the oldest attempt (highest bit in bitmap) shows "success", * subtract "1" from the success counter (this is the main reason * we keep these bitmaps!). */ while (attempts > 0) { if (win->counter >= RATE_MAX_WINDOW) { /* remove earliest */ win->counter = RATE_MAX_WINDOW - 1; if (win->data & mask) { win->data &= ~mask; win->success_counter--; } } /* Increment frames-attempted counter */ win->counter++; /* Shift bitmap by one frame to throw away oldest history */ win->data <<= 1; /* Mark the most recent #successes attempts as successful */ if (successes > 0) { win->success_counter++; win->data |= 0x1; successes--; } attempts--; } /* Calculate current success ratio, avoid divide-by-0! */ if (win->counter > 0) win->success_ratio = 128 * (100 * win->success_counter) / win->counter; else win->success_ratio = IL_INVALID_VALUE; fail_count = win->counter - win->success_counter; /* Calculate average throughput, if we have enough history. */ if (fail_count >= RATE_MIN_FAILURE_TH || win->success_counter >= RATE_MIN_SUCCESS_TH) win->average_tpt = (win->success_ratio * tpt + 64) / 128; else win->average_tpt = IL_INVALID_VALUE; /* Tag this win as having been updated */ win->stamp = jiffies; return 0; } /* * Fill uCode API rate_n_flags field, based on "search" or "active" table. */ static u32 il4965_rate_n_flags_from_tbl(struct il_priv *il, struct il_scale_tbl_info *tbl, int idx, u8 use_green) { u32 rate_n_flags = 0; if (is_legacy(tbl->lq_type)) { rate_n_flags = il_rates[idx].plcp; if (idx >= IL_FIRST_CCK_RATE && idx <= IL_LAST_CCK_RATE) rate_n_flags |= RATE_MCS_CCK_MSK; } else if (is_Ht(tbl->lq_type)) { if (idx > IL_LAST_OFDM_RATE) { IL_ERR("Invalid HT rate idx %d\n", idx); idx = IL_LAST_OFDM_RATE; } rate_n_flags = RATE_MCS_HT_MSK; if (is_siso(tbl->lq_type)) rate_n_flags |= il_rates[idx].plcp_siso; else rate_n_flags |= il_rates[idx].plcp_mimo2; } else { IL_ERR("Invalid tbl->lq_type %d\n", tbl->lq_type); } rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & RATE_MCS_ANT_ABC_MSK); if (is_Ht(tbl->lq_type)) { if (tbl->is_ht40) { if (tbl->is_dup) rate_n_flags |= RATE_MCS_DUP_MSK; else rate_n_flags |= RATE_MCS_HT40_MSK; } if (tbl->is_SGI) rate_n_flags |= RATE_MCS_SGI_MSK; if (use_green) { rate_n_flags |= RATE_MCS_GF_MSK; if (is_siso(tbl->lq_type) && tbl->is_SGI) { rate_n_flags &= ~RATE_MCS_SGI_MSK; IL_ERR("GF was set with SGI:SISO\n"); } } } return rate_n_flags; } /* * Interpret uCode API's rate_n_flags format, * fill "search" or "active" tx mode table. */ static int il4965_rs_get_tbl_info_from_mcs(const u32 rate_n_flags, enum ieee80211_band band, struct il_scale_tbl_info *tbl, int *rate_idx) { u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); u8 il4965_num_of_ant = il4965_get_il4965_num_of_ant_from_rate(rate_n_flags); u8 mcs; memset(tbl, 0, sizeof(struct il_scale_tbl_info)); *rate_idx = il4965_hwrate_to_plcp_idx(rate_n_flags); if (*rate_idx == RATE_INVALID) { *rate_idx = -1; return -EINVAL; } tbl->is_SGI = 0; /* default legacy setup */ tbl->is_ht40 = 0; tbl->is_dup = 0; tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); tbl->lq_type = LQ_NONE; tbl->max_search = IL_MAX_SEARCH; /* legacy rate format */ if (!(rate_n_flags & RATE_MCS_HT_MSK)) { if (il4965_num_of_ant == 1) { if (band == IEEE80211_BAND_5GHZ) tbl->lq_type = LQ_A; else tbl->lq_type = LQ_G; } /* HT rate format */ } else { if (rate_n_flags & RATE_MCS_SGI_MSK) tbl->is_SGI = 1; if ((rate_n_flags & RATE_MCS_HT40_MSK) || (rate_n_flags & RATE_MCS_DUP_MSK)) tbl->is_ht40 = 1; if (rate_n_flags & RATE_MCS_DUP_MSK) tbl->is_dup = 1; mcs = il4965_rs_extract_rate(rate_n_flags); /* SISO */ if (mcs <= RATE_SISO_60M_PLCP) { if (il4965_num_of_ant == 1) tbl->lq_type = LQ_SISO; /*else NONE */ /* MIMO2 */ } else { if (il4965_num_of_ant == 2) tbl->lq_type = LQ_MIMO2; } } return 0; } /* switch to another antenna/antennas and return 1 */ /* if no other valid antenna found, return 0 */ static int il4965_rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, struct il_scale_tbl_info *tbl) { u8 new_ant_type; if (!tbl->ant_type || tbl->ant_type > ANT_ABC) return 0; if (!il4965_rs_is_valid_ant(valid_ant, tbl->ant_type)) return 0; new_ant_type = ant_toggle_lookup[tbl->ant_type]; while (new_ant_type != tbl->ant_type && !il4965_rs_is_valid_ant(valid_ant, new_ant_type)) new_ant_type = ant_toggle_lookup[new_ant_type]; if (new_ant_type == tbl->ant_type) return 0; tbl->ant_type = new_ant_type; *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; return 1; } /** * Green-field mode is valid if the station supports it and * there are no non-GF stations present in the BSS. */ static bool il4965_rs_use_green(struct il_priv *il, struct ieee80211_sta *sta) { return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && !il->ht.non_gf_sta_present; } /** * il4965_rs_get_supported_rates - get the available rates * * if management frame or broadcast frame only return * basic available rates. * */ static u16 il4965_rs_get_supported_rates(struct il_lq_sta *lq_sta, struct ieee80211_hdr *hdr, enum il_table_type rate_type) { if (is_legacy(rate_type)) { return lq_sta->active_legacy_rate; } else { if (is_siso(rate_type)) return lq_sta->active_siso_rate; else return lq_sta->active_mimo2_rate; } } static u16 il4965_rs_get_adjacent_rate(struct il_priv *il, u8 idx, u16 rate_mask, int rate_type) { u8 high = RATE_INVALID; u8 low = RATE_INVALID; /* 802.11A or ht walks to the next literal adjacent rate in * the rate table */ if (is_a_band(rate_type) || !is_legacy(rate_type)) { int i; u32 mask; /* Find the previous rate that is in the rate mask */ i = idx - 1; for (mask = (1 << i); i >= 0; i--, mask >>= 1) { if (rate_mask & mask) { low = i; break; } } /* Find the next rate that is in the rate mask */ i = idx + 1; for (mask = (1 << i); i < RATE_COUNT; i++, mask <<= 1) { if (rate_mask & mask) { high = i; break; } } return (high << 8) | low; } low = idx; while (low != RATE_INVALID) { low = il_rates[low].prev_rs; if (low == RATE_INVALID) break; if (rate_mask & (1 << low)) break; D_RATE("Skipping masked lower rate: %d\n", low); } high = idx; while (high != RATE_INVALID) { high = il_rates[high].next_rs; if (high == RATE_INVALID) break; if (rate_mask & (1 << high)) break; D_RATE("Skipping masked higher rate: %d\n", high); } return (high << 8) | low; } static u32 il4965_rs_get_lower_rate(struct il_lq_sta *lq_sta, struct il_scale_tbl_info *tbl, u8 scale_idx, u8 ht_possible) { s32 low; u16 rate_mask; u16 high_low; u8 switch_to_legacy = 0; u8 is_green = lq_sta->is_green; struct il_priv *il = lq_sta->drv; /* check if we need to switch from HT to legacy rates. * assumption is that mandatory rates (1Mbps or 6Mbps) * are always supported (spec demand) */ if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_idx)) { switch_to_legacy = 1; scale_idx = rs_ht_to_legacy[scale_idx]; if (lq_sta->band == IEEE80211_BAND_5GHZ) tbl->lq_type = LQ_A; else tbl->lq_type = LQ_G; if (il4965_num_of_ant(tbl->ant_type) > 1) tbl->ant_type = il4965_first_antenna(il->hw_params.valid_tx_ant); tbl->is_ht40 = 0; tbl->is_SGI = 0; tbl->max_search = IL_MAX_SEARCH; } rate_mask = il4965_rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); /* Mask with station rate restriction */ if (is_legacy(tbl->lq_type)) { /* supp_rates has no CCK bits in A mode */ if (lq_sta->band == IEEE80211_BAND_5GHZ) rate_mask = (u16) (rate_mask & (lq_sta->supp_rates << IL_FIRST_OFDM_RATE)); else rate_mask = (u16) (rate_mask & lq_sta->supp_rates); } /* If we switched from HT to legacy, check current rate */ if (switch_to_legacy && (rate_mask & (1 << scale_idx))) { low = scale_idx; goto out; } high_low = il4965_rs_get_adjacent_rate(lq_sta->drv, scale_idx, rate_mask, tbl->lq_type); low = high_low & 0xff; if (low == RATE_INVALID) low = scale_idx; out: return il4965_rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); } /* * Simple function to compare two rate scale table types */ static bool il4965_table_type_matches(struct il_scale_tbl_info *a, struct il_scale_tbl_info *b) { return (a->lq_type == b->lq_type && a->ant_type == b->ant_type && a->is_SGI == b->is_SGI); } /* * mac80211 sends us Tx status */ static void il4965_rs_tx_status(void *il_r, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *il_sta, struct sk_buff *skb) { int legacy_success; int retries; int rs_idx, mac_idx, i; struct il_lq_sta *lq_sta = il_sta; struct il_link_quality_cmd *table; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct il_priv *il = (struct il_priv *)il_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); enum mac80211_rate_control_flags mac_flags; u32 tx_rate; struct il_scale_tbl_info tbl_type; struct il_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; D_RATE("get frame ack response, update rate scale win\n"); /* Treat uninitialized rate scaling data same as non-existing. */ if (!lq_sta) { D_RATE("Station rate scaling not created yet.\n"); return; } else if (!lq_sta->drv) { D_RATE("Rate scaling not initialized yet.\n"); return; } if (!ieee80211_is_data(hdr->frame_control) || (info->flags & IEEE80211_TX_CTL_NO_ACK)) return; /* This packet was aggregated but doesn't carry status info */ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && !(info->flags & IEEE80211_TX_STAT_AMPDU)) return; /* * Ignore this Tx frame response if its initial rate doesn't match * that of latest Link Quality command. There may be stragglers * from a previous Link Quality command, but we're no longer interested * in those; they're either from the "active" mode while we're trying * to check "search" mode, or a prior "search" mode after we've moved * to a new "search" mode (which might become the new "active" mode). */ table = &lq_sta->lq; tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, &tbl_type, &rs_idx); if (il->band == IEEE80211_BAND_5GHZ) rs_idx -= IL_FIRST_OFDM_RATE; mac_flags = info->status.rates[0].flags; mac_idx = info->status.rates[0].idx; /* For HT packets, map MCS to PLCP */ if (mac_flags & IEEE80211_TX_RC_MCS) { mac_idx &= RATE_MCS_CODE_MSK; /* Remove # of streams */ if (mac_idx >= (RATE_9M_IDX - IL_FIRST_OFDM_RATE)) mac_idx++; /* * mac80211 HT idx is always zero-idxed; we need to move * HT OFDM rates after CCK rates in 2.4 GHz band */ if (il->band == IEEE80211_BAND_2GHZ) mac_idx += IL_FIRST_OFDM_RATE; } /* Here we actually compare this rate to the latest LQ command */ if (mac_idx < 0 || tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI) || tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) || tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA) || tbl_type.ant_type != info->status.antenna || !!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS) || !!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD) || rs_idx != mac_idx) { D_RATE("initial rate %d does not match %d (0x%x)\n", mac_idx, rs_idx, tx_rate); /* * Since rates mis-match, the last LQ command may have failed. * After IL_MISSED_RATE_MAX mis-matches, resync the uCode with * ... driver. */ lq_sta->missed_rate_counter++; if (lq_sta->missed_rate_counter > IL_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); } /* Regardless, ignore this status info for outdated rate */ return; } else /* Rate did match, so reset the missed_rate_counter */ lq_sta->missed_rate_counter = 0; /* Figure out if rate scale algorithm is in active or search table */ if (il4965_table_type_matches (&tbl_type, &(lq_sta->lq_info[lq_sta->active_tbl]))) { curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); } else if (il4965_table_type_matches (&tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) { curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); } else { D_RATE("Neither active nor search matches tx rate\n"); tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); D_RATE("active- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); D_RATE("search- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); D_RATE("actual- lq:%x, ant:%x, SGI:%d\n", tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); /* * no matching table found, let's by-pass the data collection * and continue to perform rate scale to find the rate table */ il4965_rs_stay_in_table(lq_sta, true); goto done; } /* * Updating the frame history depends on whether packets were * aggregated. * * For aggregation, all packets were transmitted at the same rate, the * first idx into rate scale table. */ if (info->flags & IEEE80211_TX_STAT_AMPDU) { tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, &tbl_type, &rs_idx); il4965_rs_collect_tx_data(curr_tbl, rs_idx, info->status.ampdu_len, info->status.ampdu_ack_len); /* Update success/fail counts if not searching for new mode */ if (lq_sta->stay_in_tbl) { lq_sta->total_success += info->status.ampdu_ack_len; lq_sta->total_failed += (info->status.ampdu_len - info->status.ampdu_ack_len); } } else { /* * For legacy, update frame history with for each Tx retry. */ retries = info->status.rates[0].count - 1; /* HW doesn't send more than 15 retries */ retries = min(retries, 15); /* The last transmission may have been successful */ legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); /* Collect data for each rate used during failed TX attempts */ for (i = 0; i <= retries; ++i) { tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, &tbl_type, &rs_idx); /* * Only collect stats if retried rate is in the same RS * table as active/search. */ if (il4965_table_type_matches(&tbl_type, curr_tbl)) tmp_tbl = curr_tbl; else if (il4965_table_type_matches (&tbl_type, other_tbl)) tmp_tbl = other_tbl; else continue; il4965_rs_collect_tx_data(tmp_tbl, rs_idx, 1, i < retries ? 0 : legacy_success); } /* Update success/fail counts if not searching for new mode */ if (lq_sta->stay_in_tbl) { lq_sta->total_success += legacy_success; lq_sta->total_failed += retries + (1 - legacy_success); } } /* The last TX rate is cached in lq_sta; it's set in if/else above */ lq_sta->last_rate_n_flags = tx_rate; done: /* See if there's a better rate or modulation mode to try. */ if (sta->supp_rates[sband->band]) il4965_rs_rate_scale_perform(il, skb, sta, lq_sta); } /* * Begin a period of staying with a selected modulation mode. * Set "stay_in_tbl" flag to prevent any mode switches. * Set frame tx success limits according to legacy vs. high-throughput, * and reset overall (spanning all rates) tx success history stats. * These control how long we stay using same modulation mode before * searching for a new mode. */ static void il4965_rs_set_stay_in_table(struct il_priv *il, u8 is_legacy, struct il_lq_sta *lq_sta) { D_RATE("we are staying in the same table\n"); lq_sta->stay_in_tbl = 1; /* only place this gets set */ if (is_legacy) { lq_sta->table_count_limit = IL_LEGACY_TBL_COUNT; lq_sta->max_failure_limit = IL_LEGACY_FAILURE_LIMIT; lq_sta->max_success_limit = IL_LEGACY_SUCCESS_LIMIT; } else { lq_sta->table_count_limit = IL_NONE_LEGACY_TBL_COUNT; lq_sta->max_failure_limit = IL_NONE_LEGACY_FAILURE_LIMIT; lq_sta->max_success_limit = IL_NONE_LEGACY_SUCCESS_LIMIT; } lq_sta->table_count = 0; lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = jiffies; lq_sta->action_counter = 0; } /* * Find correct throughput table for given mode of modulation */ static void il4965_rs_set_expected_tpt_table(struct il_lq_sta *lq_sta, struct il_scale_tbl_info *tbl) { /* Used to choose among HT tables */ s32(*ht_tbl_pointer)[RATE_COUNT]; /* Check for invalid LQ type */ if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { tbl->expected_tpt = expected_tpt_legacy; return; } /* Legacy rates have only one table */ if (is_legacy(tbl->lq_type)) { tbl->expected_tpt = expected_tpt_legacy; return; } /* Choose among many HT tables depending on number of streams * (SISO/MIMO2), channel width (20/40), SGI, and aggregation * status */ if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) ht_tbl_pointer = expected_tpt_siso20MHz; else if (is_siso(tbl->lq_type)) ht_tbl_pointer = expected_tpt_siso40MHz; else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) ht_tbl_pointer = expected_tpt_mimo2_20MHz; else /* if (is_mimo2(tbl->lq_type)) <-- must be true */ ht_tbl_pointer = expected_tpt_mimo2_40MHz; if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ tbl->expected_tpt = ht_tbl_pointer[0]; else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ tbl->expected_tpt = ht_tbl_pointer[1]; else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ tbl->expected_tpt = ht_tbl_pointer[2]; else /* AGG+SGI */ tbl->expected_tpt = ht_tbl_pointer[3]; } /* * Find starting rate for new "search" high-throughput mode of modulation. * Goal is to find lowest expected rate (under perfect conditions) that is * above the current measured throughput of "active" mode, to give new mode * a fair chance to prove itself without too many challenges. * * This gets called when transitioning to more aggressive modulation * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need * to decrease to match "active" throughput. When moving from MIMO to SISO, * bit rate will typically need to increase, but not if performance was bad. */ static s32 il4965_rs_get_best_rate(struct il_priv *il, struct il_lq_sta *lq_sta, struct il_scale_tbl_info *tbl, /* "search" */ u16 rate_mask, s8 idx) { /* "active" values */ struct il_scale_tbl_info *active_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); s32 active_sr = active_tbl->win[idx].success_ratio; s32 active_tpt = active_tbl->expected_tpt[idx]; /* expected "search" throughput */ s32 *tpt_tbl = tbl->expected_tpt; s32 new_rate, high, low, start_hi; u16 high_low; s8 rate = idx; new_rate = high = low = start_hi = RATE_INVALID; for (;;) { high_low = il4965_rs_get_adjacent_rate(il, rate, rate_mask, tbl->lq_type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; /* * Lower the "search" bit rate, to give new "search" mode * approximately the same throughput as "active" if: * * 1) "Active" mode has been working modestly well (but not * great), and expected "search" throughput (under perfect * conditions) at candidate rate is above the actual * measured "active" throughput (but less than expected * "active" throughput under perfect conditions). * OR * 2) "Active" mode has been working perfectly or very well * and expected "search" throughput (under perfect * conditions) at candidate rate is above expected * "active" throughput (under perfect conditions). */ if ((100 * tpt_tbl[rate] > lq_sta->last_tpt && (active_sr > RATE_DECREASE_TH && active_sr <= RATE_HIGH_TH && tpt_tbl[rate] <= active_tpt)) || (active_sr >= RATE_SCALE_SWITCH && tpt_tbl[rate] > active_tpt)) { /* (2nd or later pass) * If we've already tried to raise the rate, and are * now trying to lower it, use the higher rate. */ if (start_hi != RATE_INVALID) { new_rate = start_hi; break; } new_rate = rate; /* Loop again with lower rate */ if (low != RATE_INVALID) rate = low; /* Lower rate not available, use the original */ else break; /* Else try to raise the "search" rate to match "active" */ } else { /* (2nd or later pass) * If we've already tried to lower the rate, and are * now trying to raise it, use the lower rate. */ if (new_rate != RATE_INVALID) break; /* Loop again with higher rate */ else if (high != RATE_INVALID) { start_hi = high; rate = high; /* Higher rate not available, use the original */ } else { new_rate = rate; break; } } } return new_rate; } /* * Set up search table for MIMO2 */ static int il4965_rs_switch_to_mimo2(struct il_priv *il, struct il_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, struct il_scale_tbl_info *tbl, int idx) { u16 rate_mask; s32 rate; s8 is_green = lq_sta->is_green; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) == WLAN_HT_CAP_SM_PS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ if (il->hw_params.tx_chains_num < 2) return -1; D_RATE("LQ: try to switch to MIMO2\n"); tbl->lq_type = LQ_MIMO2; tbl->is_dup = lq_sta->is_dup; tbl->action = 0; tbl->max_search = IL_MAX_SEARCH; rate_mask = lq_sta->active_mimo2_rate; if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; il4965_rs_set_expected_tpt_table(lq_sta, tbl); rate = il4965_rs_get_best_rate(il, lq_sta, tbl, rate_mask, idx); D_RATE("LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); if (rate == RATE_INVALID || !((1 << rate) & rate_mask)) { D_RATE("Can't switch with idx %d rate mask %x\n", rate, rate_mask); return -1; } tbl->current_rate = il4965_rate_n_flags_from_tbl(il, tbl, rate, is_green); D_RATE("LQ: Switch to new mcs %X idx is green %X\n", tbl->current_rate, is_green); return 0; } /* * Set up search table for SISO */ static int il4965_rs_switch_to_siso(struct il_priv *il, struct il_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, struct il_scale_tbl_info *tbl, int idx) { u16 rate_mask; u8 is_green = lq_sta->is_green; s32 rate; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; D_RATE("LQ: try to switch to SISO\n"); tbl->is_dup = lq_sta->is_dup; tbl->lq_type = LQ_SISO; tbl->action = 0; tbl->max_search = IL_MAX_SEARCH; rate_mask = lq_sta->active_siso_rate; if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; if (is_green) tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield */ il4965_rs_set_expected_tpt_table(lq_sta, tbl); rate = il4965_rs_get_best_rate(il, lq_sta, tbl, rate_mask, idx); D_RATE("LQ: get best rate %d mask %X\n", rate, rate_mask); if (rate == RATE_INVALID || !((1 << rate) & rate_mask)) { D_RATE("can not switch with idx %d rate mask %x\n", rate, rate_mask); return -1; } tbl->current_rate = il4965_rate_n_flags_from_tbl(il, tbl, rate, is_green); D_RATE("LQ: Switch to new mcs %X idx is green %X\n", tbl->current_rate, is_green); return 0; } /* * Try to switch to new modulation mode from legacy */ static int il4965_rs_move_legacy_other(struct il_priv *il, struct il_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, int idx) { struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct il_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct il_rate_scale_data *win = &(tbl->win[idx]); u32 sz = (sizeof(struct il_scale_tbl_info) - (sizeof(struct il_rate_scale_data) * RATE_COUNT)); u8 start_action; u8 valid_tx_ant = il->hw_params.valid_tx_ant; u8 tx_chains_num = il->hw_params.tx_chains_num; int ret = 0; u8 update_search_tbl_counter = 0; tbl->action = IL_LEGACY_SWITCH_SISO; start_action = tbl->action; for (;;) { lq_sta->action_counter++; switch (tbl->action) { case IL_LEGACY_SWITCH_ANTENNA1: case IL_LEGACY_SWITCH_ANTENNA2: D_RATE("LQ: Legacy toggle Antenna\n"); if ((tbl->action == IL_LEGACY_SWITCH_ANTENNA1 && tx_chains_num <= 1) || (tbl->action == IL_LEGACY_SWITCH_ANTENNA2 && tx_chains_num <= 2)) break; /* Don't change antenna if success has been great */ if (win->success_ratio >= IL_RS_GOOD_RATIO) break; /* Set up search table to try other antenna */ memcpy(search_tbl, tbl, sz); if (il4965_rs_toggle_antenna (valid_tx_ant, &search_tbl->current_rate, search_tbl)) { update_search_tbl_counter = 1; il4965_rs_set_expected_tpt_table(lq_sta, search_tbl); goto out; } break; case IL_LEGACY_SWITCH_SISO: D_RATE("LQ: Legacy switch to SISO\n"); /* Set up search table to try SISO */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; ret = il4965_rs_switch_to_siso(il, lq_sta, conf, sta, search_tbl, idx); if (!ret) { lq_sta->action_counter = 0; goto out; } break; case IL_LEGACY_SWITCH_MIMO2_AB: case IL_LEGACY_SWITCH_MIMO2_AC: case IL_LEGACY_SWITCH_MIMO2_BC: D_RATE("LQ: Legacy switch to MIMO2\n"); /* Set up search table to try MIMO */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; if (tbl->action == IL_LEGACY_SWITCH_MIMO2_AB) search_tbl->ant_type = ANT_AB; else if (tbl->action == IL_LEGACY_SWITCH_MIMO2_AC) search_tbl->ant_type = ANT_AC; else search_tbl->ant_type = ANT_BC; if (!il4965_rs_is_valid_ant (valid_tx_ant, search_tbl->ant_type)) break; ret = il4965_rs_switch_to_mimo2(il, lq_sta, conf, sta, search_tbl, idx); if (!ret) { lq_sta->action_counter = 0; goto out; } break; } tbl->action++; if (tbl->action > IL_LEGACY_SWITCH_MIMO2_BC) tbl->action = IL_LEGACY_SWITCH_ANTENNA1; if (tbl->action == start_action) break; } search_tbl->lq_type = LQ_NONE; return 0; out: lq_sta->search_better_tbl = 1; tbl->action++; if (tbl->action > IL_LEGACY_SWITCH_MIMO2_BC) tbl->action = IL_LEGACY_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; } /* * Try to switch to new modulation mode from SISO */ static int il4965_rs_move_siso_to_other(struct il_priv *il, struct il_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, int idx) { u8 is_green = lq_sta->is_green; struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct il_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct il_rate_scale_data *win = &(tbl->win[idx]); struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct il_scale_tbl_info) - (sizeof(struct il_rate_scale_data) * RATE_COUNT)); u8 start_action; u8 valid_tx_ant = il->hw_params.valid_tx_ant; u8 tx_chains_num = il->hw_params.tx_chains_num; u8 update_search_tbl_counter = 0; int ret; start_action = tbl->action; for (;;) { lq_sta->action_counter++; switch (tbl->action) { case IL_SISO_SWITCH_ANTENNA1: case IL_SISO_SWITCH_ANTENNA2: D_RATE("LQ: SISO toggle Antenna\n"); if ((tbl->action == IL_SISO_SWITCH_ANTENNA1 && tx_chains_num <= 1) || (tbl->action == IL_SISO_SWITCH_ANTENNA2 && tx_chains_num <= 2)) break; if (win->success_ratio >= IL_RS_GOOD_RATIO) break; memcpy(search_tbl, tbl, sz); if (il4965_rs_toggle_antenna (valid_tx_ant, &search_tbl->current_rate, search_tbl)) { update_search_tbl_counter = 1; goto out; } break; case IL_SISO_SWITCH_MIMO2_AB: case IL_SISO_SWITCH_MIMO2_AC: case IL_SISO_SWITCH_MIMO2_BC: D_RATE("LQ: SISO switch to MIMO2\n"); memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; if (tbl->action == IL_SISO_SWITCH_MIMO2_AB) search_tbl->ant_type = ANT_AB; else if (tbl->action == IL_SISO_SWITCH_MIMO2_AC) search_tbl->ant_type = ANT_AC; else search_tbl->ant_type = ANT_BC; if (!il4965_rs_is_valid_ant (valid_tx_ant, search_tbl->ant_type)) break; ret = il4965_rs_switch_to_mimo2(il, lq_sta, conf, sta, search_tbl, idx); if (!ret) goto out; break; case IL_SISO_SWITCH_GI: if (!tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) break; if (tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) break; D_RATE("LQ: SISO toggle SGI/NGI\n"); memcpy(search_tbl, tbl, sz); if (is_green) { if (!tbl->is_SGI) break; else IL_ERR("SGI was set in GF+SISO\n"); } search_tbl->is_SGI = !tbl->is_SGI; il4965_rs_set_expected_tpt_table(lq_sta, search_tbl); if (tbl->is_SGI) { s32 tpt = lq_sta->last_tpt / 100; if (tpt >= search_tbl->expected_tpt[idx]) break; } search_tbl->current_rate = il4965_rate_n_flags_from_tbl(il, search_tbl, idx, is_green); update_search_tbl_counter = 1; goto out; } tbl->action++; if (tbl->action > IL_SISO_SWITCH_GI) tbl->action = IL_SISO_SWITCH_ANTENNA1; if (tbl->action == start_action) break; } search_tbl->lq_type = LQ_NONE; return 0; out: lq_sta->search_better_tbl = 1; tbl->action++; if (tbl->action > IL_SISO_SWITCH_GI) tbl->action = IL_SISO_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; } /* * Try to switch to new modulation mode from MIMO2 */ static int il4965_rs_move_mimo2_to_other(struct il_priv *il, struct il_lq_sta *lq_sta, struct ieee80211_conf *conf, struct ieee80211_sta *sta, int idx) { s8 is_green = lq_sta->is_green; struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct il_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct il_rate_scale_data *win = &(tbl->win[idx]); struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct il_scale_tbl_info) - (sizeof(struct il_rate_scale_data) * RATE_COUNT)); u8 start_action; u8 valid_tx_ant = il->hw_params.valid_tx_ant; u8 tx_chains_num = il->hw_params.tx_chains_num; u8 update_search_tbl_counter = 0; int ret; start_action = tbl->action; for (;;) { lq_sta->action_counter++; switch (tbl->action) { case IL_MIMO2_SWITCH_ANTENNA1: case IL_MIMO2_SWITCH_ANTENNA2: D_RATE("LQ: MIMO2 toggle Antennas\n"); if (tx_chains_num <= 2) break; if (win->success_ratio >= IL_RS_GOOD_RATIO) break; memcpy(search_tbl, tbl, sz); if (il4965_rs_toggle_antenna (valid_tx_ant, &search_tbl->current_rate, search_tbl)) { update_search_tbl_counter = 1; goto out; } break; case IL_MIMO2_SWITCH_SISO_A: case IL_MIMO2_SWITCH_SISO_B: case IL_MIMO2_SWITCH_SISO_C: D_RATE("LQ: MIMO2 switch to SISO\n"); /* Set up new search table for SISO */ memcpy(search_tbl, tbl, sz); if (tbl->action == IL_MIMO2_SWITCH_SISO_A) search_tbl->ant_type = ANT_A; else if (tbl->action == IL_MIMO2_SWITCH_SISO_B) search_tbl->ant_type = ANT_B; else search_tbl->ant_type = ANT_C; if (!il4965_rs_is_valid_ant (valid_tx_ant, search_tbl->ant_type)) break; ret = il4965_rs_switch_to_siso(il, lq_sta, conf, sta, search_tbl, idx); if (!ret) goto out; break; case IL_MIMO2_SWITCH_GI: if (!tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) break; if (tbl->is_ht40 && !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) break; D_RATE("LQ: MIMO2 toggle SGI/NGI\n"); /* Set up new search table for MIMO2 */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = !tbl->is_SGI; il4965_rs_set_expected_tpt_table(lq_sta, search_tbl); /* * If active table already uses the fastest possible * modulation (dual stream with short guard interval), * and it's working well, there's no need to look * for a better type of modulation! */ if (tbl->is_SGI) { s32 tpt = lq_sta->last_tpt / 100; if (tpt >= search_tbl->expected_tpt[idx]) break; } search_tbl->current_rate = il4965_rate_n_flags_from_tbl(il, search_tbl, idx, is_green); update_search_tbl_counter = 1; goto out; } tbl->action++; if (tbl->action > IL_MIMO2_SWITCH_GI) tbl->action = IL_MIMO2_SWITCH_ANTENNA1; if (tbl->action == start_action) break; } search_tbl->lq_type = LQ_NONE; return 0; out: lq_sta->search_better_tbl = 1; tbl->action++; if (tbl->action > IL_MIMO2_SWITCH_GI) tbl->action = IL_MIMO2_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; } /* * Check whether we should continue using same modulation mode, or * begin search for a new mode, based on: * 1) # tx successes or failures while using this mode * 2) # times calling this function * 3) elapsed time in this mode (not used, for now) */ static void il4965_rs_stay_in_table(struct il_lq_sta *lq_sta, bool force_search) { struct il_scale_tbl_info *tbl; int i; int active_tbl; int flush_interval_passed = 0; struct il_priv *il; il = lq_sta->drv; active_tbl = lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); /* If we've been disallowing search, see if we should now allow it */ if (lq_sta->stay_in_tbl) { /* Elapsed time using current modulation mode */ if (lq_sta->flush_timer) flush_interval_passed = time_after(jiffies, (unsigned long)(lq_sta->flush_timer + RATE_SCALE_FLUSH_INTVL)); /* * Check if we should allow search for new modulation mode. * If many frames have failed or succeeded, or we've used * this same modulation for a long time, allow search, and * reset history stats that keep track of whether we should * allow a new search. Also (below) reset all bitmaps and * stats in active history. */ if (force_search || lq_sta->total_failed > lq_sta->max_failure_limit || lq_sta->total_success > lq_sta->max_success_limit || (!lq_sta->search_better_tbl && lq_sta->flush_timer && flush_interval_passed)) { D_RATE("LQ: stay is expired %d %d %d\n:", lq_sta->total_failed, lq_sta->total_success, flush_interval_passed); /* Allow search for new mode */ lq_sta->stay_in_tbl = 0; /* only place reset */ lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = 0; /* * Else if we've used this modulation mode enough repetitions * (regardless of elapsed time or success/failure), reset * history bitmaps and rate-specific stats for all rates in * active table. */ } else { lq_sta->table_count++; if (lq_sta->table_count >= lq_sta->table_count_limit) { lq_sta->table_count = 0; D_RATE("LQ: stay in table clear win\n"); for (i = 0; i < RATE_COUNT; i++) il4965_rs_rate_scale_clear_win(& (tbl-> win [i])); } } /* If transitioning to allow "search", reset all history * bitmaps and stats in active table (this will become the new * "search" table). */ if (!lq_sta->stay_in_tbl) { for (i = 0; i < RATE_COUNT; i++) il4965_rs_rate_scale_clear_win(&(tbl->win[i])); } } } /* * setup rate table in uCode */ static void il4965_rs_update_rate_tbl(struct il_priv *il, struct il_lq_sta *lq_sta, struct il_scale_tbl_info *tbl, int idx, u8 is_green) { u32 rate; /* Update uCode's rate table. */ rate = il4965_rate_n_flags_from_tbl(il, tbl, idx, is_green); il4965_rs_fill_link_cmd(il, lq_sta, rate); il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); } /* * Do rate scaling and search for new modulation mode. */ static void il4965_rs_rate_scale_perform(struct il_priv *il, struct sk_buff *skb, struct ieee80211_sta *sta, struct il_lq_sta *lq_sta) { struct ieee80211_hw *hw = il->hw; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; int low = RATE_INVALID; int high = RATE_INVALID; int idx; int i; struct il_rate_scale_data *win = NULL; int current_tpt = IL_INVALID_VALUE; int low_tpt = IL_INVALID_VALUE; int high_tpt = IL_INVALID_VALUE; u32 fail_count; s8 scale_action = 0; u16 rate_mask; u8 update_lq = 0; struct il_scale_tbl_info *tbl, *tbl1; u16 rate_scale_idx_msk = 0; u8 is_green = 0; u8 active_tbl = 0; u8 done_search = 0; u16 high_low; s32 sr; u8 tid = MAX_TID_COUNT; struct il_tid_data *tid_data; D_RATE("rate scale calculate new rate for skb\n"); /* Send management frames and NO_ACK data using lowest rate. */ /* TODO: this could probably be improved.. */ if (!ieee80211_is_data(hdr->frame_control) || (info->flags & IEEE80211_TX_CTL_NO_ACK)) return; lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; tid = il4965_rs_tl_add_packet(lq_sta, hdr); if (tid != MAX_TID_COUNT && (lq_sta->tx_agg_tid_en & (1 << tid))) { tid_data = &il->stations[lq_sta->lq.sta_id].tid[tid]; if (tid_data->agg.state == IL_AGG_OFF) lq_sta->is_agg = 0; else lq_sta->is_agg = 1; } else lq_sta->is_agg = 0; /* * Select rate-scale / modulation-mode table to work with in * the rest of this function: "search" if searching for better * modulation mode, or "active" if doing rate scaling within a mode. */ if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; else active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); if (is_legacy(tbl->lq_type)) lq_sta->is_green = 0; else lq_sta->is_green = il4965_rs_use_green(il, sta); is_green = lq_sta->is_green; /* current tx rate */ idx = lq_sta->last_txrate_idx; D_RATE("Rate scale idx %d for type %d\n", idx, tbl->lq_type); /* rates available for this association, and for modulation mode */ rate_mask = il4965_rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); D_RATE("mask 0x%04X\n", rate_mask); /* mask with station rate restriction */ if (is_legacy(tbl->lq_type)) { if (lq_sta->band == IEEE80211_BAND_5GHZ) /* supp_rates has no CCK bits in A mode */ rate_scale_idx_msk = (u16) (rate_mask & (lq_sta->supp_rates << IL_FIRST_OFDM_RATE)); else rate_scale_idx_msk = (u16) (rate_mask & lq_sta->supp_rates); } else rate_scale_idx_msk = rate_mask; if (!rate_scale_idx_msk) rate_scale_idx_msk = rate_mask; if (!((1 << idx) & rate_scale_idx_msk)) { IL_ERR("Current Rate is not valid\n"); if (lq_sta->search_better_tbl) { /* revert to active table if search table is not valid */ tbl->lq_type = LQ_NONE; lq_sta->search_better_tbl = 0; tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); /* get "active" rate info */ idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); il4965_rs_update_rate_tbl(il, lq_sta, tbl, idx, is_green); } return; } /* Get expected throughput table and history win for current rate */ if (!tbl->expected_tpt) { IL_ERR("tbl->expected_tpt is NULL\n"); return; } /* force user max rate if set by user */ if (lq_sta->max_rate_idx != -1 && lq_sta->max_rate_idx < idx) { idx = lq_sta->max_rate_idx; update_lq = 1; win = &(tbl->win[idx]); goto lq_update; } win = &(tbl->win[idx]); /* * If there is not enough history to calculate actual average * throughput, keep analyzing results of more tx frames, without * changing rate or mode (bypass most of the rest of this function). * Set up new rate table in uCode only if old rate is not supported * in current association (use new rate found above). */ fail_count = win->counter - win->success_counter; if (fail_count < RATE_MIN_FAILURE_TH && win->success_counter < RATE_MIN_SUCCESS_TH) { D_RATE("LQ: still below TH. succ=%d total=%d " "for idx %d\n", win->success_counter, win->counter, idx); /* Can't calculate this yet; not enough history */ win->average_tpt = IL_INVALID_VALUE; /* Should we stay with this modulation mode, * or search for a new one? */ il4965_rs_stay_in_table(lq_sta, false); goto out; } /* Else we have enough samples; calculate estimate of * actual average throughput */ if (win->average_tpt != ((win->success_ratio * tbl->expected_tpt[idx] + 64) / 128)) { IL_ERR("expected_tpt should have been calculated by now\n"); win->average_tpt = ((win->success_ratio * tbl->expected_tpt[idx] + 64) / 128); } /* If we are searching for better modulation mode, check success. */ if (lq_sta->search_better_tbl) { /* If good success, continue using the "search" mode; * no need to send new link quality command, since we're * continuing to use the setup that we've been trying. */ if (win->average_tpt > lq_sta->last_tpt) { D_RATE("LQ: SWITCHING TO NEW TBL " "suc=%d cur-tpt=%d old-tpt=%d\n", win->success_ratio, win->average_tpt, lq_sta->last_tpt); if (!is_legacy(tbl->lq_type)) lq_sta->enable_counter = 1; /* Swap tables; "search" becomes "active" */ lq_sta->active_tbl = active_tbl; current_tpt = win->average_tpt; /* Else poor success; go back to mode in "active" table */ } else { D_RATE("LQ: GOING BACK TO THE OLD TBL " "suc=%d cur-tpt=%d old-tpt=%d\n", win->success_ratio, win->average_tpt, lq_sta->last_tpt); /* Nullify "search" table */ tbl->lq_type = LQ_NONE; /* Revert to "active" table */ active_tbl = lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); /* Revert to "active" rate and throughput info */ idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); current_tpt = lq_sta->last_tpt; /* Need to set up a new rate table in uCode */ update_lq = 1; } /* Either way, we've made a decision; modulation mode * search is done, allow rate adjustment next time. */ lq_sta->search_better_tbl = 0; done_search = 1; /* Don't switch modes below! */ goto lq_update; } /* (Else) not in search of better modulation mode, try for better * starting rate, while staying in this mode. */ high_low = il4965_rs_get_adjacent_rate(il, idx, rate_scale_idx_msk, tbl->lq_type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; /* If user set max rate, dont allow higher than user constrain */ if (lq_sta->max_rate_idx != -1 && lq_sta->max_rate_idx < high) high = RATE_INVALID; sr = win->success_ratio; /* Collect measured throughputs for current and adjacent rates */ current_tpt = win->average_tpt; if (low != RATE_INVALID) low_tpt = tbl->win[low].average_tpt; if (high != RATE_INVALID) high_tpt = tbl->win[high].average_tpt; scale_action = 0; /* Too many failures, decrease rate */ if (sr <= RATE_DECREASE_TH || current_tpt == 0) { D_RATE("decrease rate because of low success_ratio\n"); scale_action = -1; /* No throughput measured yet for adjacent rates; try increase. */ } else if (low_tpt == IL_INVALID_VALUE && high_tpt == IL_INVALID_VALUE) { if (high != RATE_INVALID && sr >= RATE_INCREASE_TH) scale_action = 1; else if (low != RATE_INVALID) scale_action = 0; } /* Both adjacent throughputs are measured, but neither one has better * throughput; we're using the best rate, don't change it! */ else if (low_tpt != IL_INVALID_VALUE && high_tpt != IL_INVALID_VALUE && low_tpt < current_tpt && high_tpt < current_tpt) scale_action = 0; /* At least one adjacent rate's throughput is measured, * and may have better performance. */ else { /* Higher adjacent rate's throughput is measured */ if (high_tpt != IL_INVALID_VALUE) { /* Higher rate has better throughput */ if (high_tpt > current_tpt && sr >= RATE_INCREASE_TH) scale_action = 1; else scale_action = 0; /* Lower adjacent rate's throughput is measured */ } else if (low_tpt != IL_INVALID_VALUE) { /* Lower rate has better throughput */ if (low_tpt > current_tpt) { D_RATE("decrease rate because of low tpt\n"); scale_action = -1; } else if (sr >= RATE_INCREASE_TH) { scale_action = 1; } } } /* Sanity check; asked for decrease, but success rate or throughput * has been good at old rate. Don't change it. */ if (scale_action == -1 && low != RATE_INVALID && (sr > RATE_HIGH_TH || current_tpt > 100 * tbl->expected_tpt[low])) scale_action = 0; switch (scale_action) { case -1: /* Decrease starting rate, update uCode's rate table */ if (low != RATE_INVALID) { update_lq = 1; idx = low; } break; case 1: /* Increase starting rate, update uCode's rate table */ if (high != RATE_INVALID) { update_lq = 1; idx = high; } break; case 0: /* No change */ default: break; } D_RATE("choose rate scale idx %d action %d low %d " "high %d type %d\n", idx, scale_action, low, high, tbl->lq_type); lq_update: /* Replace uCode's rate table for the destination station. */ if (update_lq) il4965_rs_update_rate_tbl(il, lq_sta, tbl, idx, is_green); /* Should we stay with this modulation mode, * or search for a new one? */ il4965_rs_stay_in_table(lq_sta, false); /* * Search for new modulation mode if we're: * 1) Not changing rates right now * 2) Not just finishing up a search * 3) Allowing a new search */ if (!update_lq && !done_search && !lq_sta->stay_in_tbl && win->counter) { /* Save current throughput to compare with "search" throughput */ lq_sta->last_tpt = current_tpt; /* Select a new "search" modulation mode to try. * If one is found, set up the new "search" table. */ if (is_legacy(tbl->lq_type)) il4965_rs_move_legacy_other(il, lq_sta, conf, sta, idx); else if (is_siso(tbl->lq_type)) il4965_rs_move_siso_to_other(il, lq_sta, conf, sta, idx); else /* (is_mimo2(tbl->lq_type)) */ il4965_rs_move_mimo2_to_other(il, lq_sta, conf, sta, idx); /* If new "search" mode was selected, set up in uCode table */ if (lq_sta->search_better_tbl) { /* Access the "search" table, clear its history. */ tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); for (i = 0; i < RATE_COUNT; i++) il4965_rs_rate_scale_clear_win(&(tbl->win[i])); /* Use new "search" start rate */ idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); D_RATE("Switch current mcs: %X idx: %d\n", tbl->current_rate, idx); il4965_rs_fill_link_cmd(il, lq_sta, tbl->current_rate); il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); } else done_search = 1; } if (done_search && !lq_sta->stay_in_tbl) { /* If the "active" (non-search) mode was legacy, * and we've tried switching antennas, * but we haven't been able to try HT modes (not available), * stay with best antenna legacy modulation for a while * before next round of mode comparisons. */ tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && lq_sta->action_counter > tbl1->max_search) { D_RATE("LQ: STAY in legacy table\n"); il4965_rs_set_stay_in_table(il, 1, lq_sta); } /* If we're in an HT mode, and all 3 mode switch actions * have been tried and compared, stay in this best modulation * mode for a while before next round of mode comparisons. */ if (lq_sta->enable_counter && lq_sta->action_counter >= tbl1->max_search) { if (lq_sta->last_tpt > IL_AGG_TPT_THREHOLD && (lq_sta->tx_agg_tid_en & (1 << tid)) && tid != MAX_TID_COUNT) { tid_data = &il->stations[lq_sta->lq.sta_id].tid[tid]; if (tid_data->agg.state == IL_AGG_OFF) { D_RATE("try to aggregate tid %d\n", tid); il4965_rs_tl_turn_on_agg(il, tid, lq_sta, sta); } } il4965_rs_set_stay_in_table(il, 0, lq_sta); } } out: tbl->current_rate = il4965_rate_n_flags_from_tbl(il, tbl, idx, is_green); i = idx; lq_sta->last_txrate_idx = i; } /** * il4965_rs_initialize_lq - Initialize a station's hardware rate table * * The uCode's station table contains a table of fallback rates * for automatic fallback during transmission. * * NOTE: This sets up a default set of values. These will be replaced later * if the driver's iwl-4965-rs rate scaling algorithm is used, instead of * rc80211_simple. * * NOTE: Run C_ADD_STA command to set up station table entry, before * calling this function (which runs C_TX_LINK_QUALITY_CMD, * which requires station table entry to exist). */ static void il4965_rs_initialize_lq(struct il_priv *il, struct ieee80211_conf *conf, struct ieee80211_sta *sta, struct il_lq_sta *lq_sta) { struct il_scale_tbl_info *tbl; int rate_idx; int i; u32 rate; u8 use_green = il4965_rs_use_green(il, sta); u8 active_tbl = 0; u8 valid_tx_ant; struct il_station_priv *sta_priv; if (!sta || !lq_sta) return; sta_priv = (void *)sta->drv_priv; i = lq_sta->last_txrate_idx; valid_tx_ant = il->hw_params.valid_tx_ant; if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; else active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); if (i < 0 || i >= RATE_COUNT) i = 0; rate = il_rates[i].plcp; tbl->ant_type = il4965_first_antenna(valid_tx_ant); rate |= tbl->ant_type << RATE_MCS_ANT_POS; if (i >= IL_FIRST_CCK_RATE && i <= IL_LAST_CCK_RATE) rate |= RATE_MCS_CCK_MSK; il4965_rs_get_tbl_info_from_mcs(rate, il->band, tbl, &rate_idx); if (!il4965_rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) il4965_rs_toggle_antenna(valid_tx_ant, &rate, tbl); rate = il4965_rate_n_flags_from_tbl(il, tbl, rate_idx, use_green); tbl->current_rate = rate; il4965_rs_set_expected_tpt_table(lq_sta, tbl); il4965_rs_fill_link_cmd(NULL, lq_sta, rate); il->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; il_send_lq_cmd(il, &lq_sta->lq, CMD_SYNC, true); } static void il4965_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_supported_band *sband = txrc->sband; struct il_priv *il __maybe_unused = (struct il_priv *)il_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct il_lq_sta *lq_sta = il_sta; int rate_idx; D_RATE("rate scale calculate new rate for skb\n"); /* Get max rate if user set max rate */ if (lq_sta) { lq_sta->max_rate_idx = txrc->max_rate_idx; if (sband->band == IEEE80211_BAND_5GHZ && lq_sta->max_rate_idx != -1) lq_sta->max_rate_idx += IL_FIRST_OFDM_RATE; if (lq_sta->max_rate_idx < 0 || lq_sta->max_rate_idx >= RATE_COUNT) lq_sta->max_rate_idx = -1; } /* Treat uninitialized rate scaling data same as non-existing. */ if (lq_sta && !lq_sta->drv) { D_RATE("Rate scaling not initialized yet.\n"); il_sta = NULL; } /* Send management frames and NO_ACK data using lowest rate. */ if (rate_control_send_low(sta, il_sta, txrc)) return; if (!lq_sta) return; rate_idx = lq_sta->last_txrate_idx; if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { rate_idx -= IL_FIRST_OFDM_RATE; /* 6M and 9M shared same MCS idx */ rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; if (il4965_rs_extract_rate(lq_sta->last_rate_n_flags) >= RATE_MIMO2_6M_PLCP) rate_idx = rate_idx + MCS_IDX_PER_STREAM; info->control.rates[0].flags = IEEE80211_TX_RC_MCS; if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; } else { /* Check for invalid rates */ if (rate_idx < 0 || rate_idx >= RATE_COUNT_LEGACY || (sband->band == IEEE80211_BAND_5GHZ && rate_idx < IL_FIRST_OFDM_RATE)) rate_idx = rate_lowest_index(sband, sta); /* On valid 5 GHz rate, adjust idx */ else if (sband->band == IEEE80211_BAND_5GHZ) rate_idx -= IL_FIRST_OFDM_RATE; info->control.rates[0].flags = 0; } info->control.rates[0].idx = rate_idx; } static void * il4965_rs_alloc_sta(void *il_rate, struct ieee80211_sta *sta, gfp_t gfp) { struct il_station_priv *sta_priv = (struct il_station_priv *)sta->drv_priv; struct il_priv *il; il = (struct il_priv *)il_rate; D_RATE("create station rate scale win\n"); return &sta_priv->lq_sta; } /* * Called after adding a new station to initialize rate scaling */ void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id) { int i, j; struct ieee80211_hw *hw = il->hw; struct ieee80211_conf *conf = &il->hw->conf; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct il_station_priv *sta_priv; struct il_lq_sta *lq_sta; struct ieee80211_supported_band *sband; sta_priv = (struct il_station_priv *)sta->drv_priv; lq_sta = &sta_priv->lq_sta; sband = hw->wiphy->bands[conf->channel->band]; lq_sta->lq.sta_id = sta_id; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < RATE_COUNT; i++) il4965_rs_rate_scale_clear_win(&lq_sta->lq_info[j]. win[i]); lq_sta->flush_timer = 0; lq_sta->supp_rates = sta->supp_rates[sband->band]; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < RATE_COUNT; i++) il4965_rs_rate_scale_clear_win(&lq_sta->lq_info[j]. win[i]); D_RATE("LQ:" "*** rate scale station global init for station %d ***\n", sta_id); /* TODO: what is a good starting rate for STA? About middle? Maybe not * the lowest or the highest rate.. Could consider using RSSI from * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ lq_sta->is_dup = 0; lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IL_MISSED_RATE_MAX; lq_sta->is_green = il4965_rs_use_green(il, sta); lq_sta->active_legacy_rate = il->active_rate & ~(0x1000); lq_sta->band = il->band; /* * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), * supp_rates[] does not; shift to convert format, force 9 MBits off. */ lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; lq_sta->active_siso_rate &= ~((u16) 0x2); lq_sta->active_siso_rate <<= IL_FIRST_OFDM_RATE; /* Same here */ lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; lq_sta->active_mimo2_rate &= ~((u16) 0x2); lq_sta->active_mimo2_rate <<= IL_FIRST_OFDM_RATE; /* These values will be overridden later */ lq_sta->lq.general_params.single_stream_ant_msk = il4965_first_antenna(il->hw_params.valid_tx_ant); lq_sta->lq.general_params.dual_stream_ant_msk = il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params. valid_tx_ant); if (!lq_sta->lq.general_params.dual_stream_ant_msk) { lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) { lq_sta->lq.general_params.dual_stream_ant_msk = il->hw_params.valid_tx_ant; } /* as default allow aggregation for all tids */ lq_sta->tx_agg_tid_en = IL_AGG_ALL_TID; lq_sta->drv = il; /* Set last_txrate_idx to lowest rate */ lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); if (sband->band == IEEE80211_BAND_5GHZ) lq_sta->last_txrate_idx += IL_FIRST_OFDM_RATE; lq_sta->is_agg = 0; #ifdef CONFIG_MAC80211_DEBUGFS lq_sta->dbg_fixed_rate = 0; #endif il4965_rs_initialize_lq(il, conf, sta, lq_sta); } static void il4965_rs_fill_link_cmd(struct il_priv *il, struct il_lq_sta *lq_sta, u32 new_rate) { struct il_scale_tbl_info tbl_type; int idx = 0; int rate_idx; int repeat_rate = 0; u8 ant_toggle_cnt = 0; u8 use_ht_possible = 1; u8 valid_tx_ant = 0; struct il_link_quality_cmd *lq_cmd = &lq_sta->lq; /* Override starting rate (idx 0) if needed for debug purposes */ il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); /* Interpret new_rate (rate_n_flags) */ il4965_rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, &rate_idx); /* How many times should we repeat the initial rate? */ if (is_legacy(tbl_type.lq_type)) { ant_toggle_cnt = 1; repeat_rate = IL_NUMBER_TRY; } else { repeat_rate = IL_HT_NUMBER_TRY; } lq_cmd->general_params.mimo_delimiter = is_mimo(tbl_type.lq_type) ? 1 : 0; /* Fill 1st table entry (idx 0) */ lq_cmd->rs_table[idx].rate_n_flags = cpu_to_le32(new_rate); if (il4965_num_of_ant(tbl_type.ant_type) == 1) { lq_cmd->general_params.single_stream_ant_msk = tbl_type.ant_type; } else if (il4965_num_of_ant(tbl_type.ant_type) == 2) { lq_cmd->general_params.dual_stream_ant_msk = tbl_type.ant_type; } /* otherwise we don't modify the existing value */ idx++; repeat_rate--; if (il) valid_tx_ant = il->hw_params.valid_tx_ant; /* Fill rest of rate table */ while (idx < LINK_QUAL_MAX_RETRY_NUM) { /* Repeat initial/next rate. * For legacy IL_NUMBER_TRY == 1, this loop will not execute. * For HT IL_HT_NUMBER_TRY == 3, this executes twice. */ while (repeat_rate > 0 && idx < LINK_QUAL_MAX_RETRY_NUM) { if (is_legacy(tbl_type.lq_type)) { if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) ant_toggle_cnt++; else if (il && il4965_rs_toggle_antenna(valid_tx_ant, &new_rate, &tbl_type)) ant_toggle_cnt = 1; } /* Override next rate if needed for debug purposes */ il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); /* Fill next table entry */ lq_cmd->rs_table[idx].rate_n_flags = cpu_to_le32(new_rate); repeat_rate--; idx++; } il4965_rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, &rate_idx); /* Indicate to uCode which entries might be MIMO. * If initial rate was MIMO, this will finally end up * as (IL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ if (is_mimo(tbl_type.lq_type)) lq_cmd->general_params.mimo_delimiter = idx; /* Get next rate */ new_rate = il4965_rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, use_ht_possible); /* How many times should we repeat the next rate? */ if (is_legacy(tbl_type.lq_type)) { if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) ant_toggle_cnt++; else if (il && il4965_rs_toggle_antenna(valid_tx_ant, &new_rate, &tbl_type)) ant_toggle_cnt = 1; repeat_rate = IL_NUMBER_TRY; } else { repeat_rate = IL_HT_NUMBER_TRY; } /* Don't allow HT rates after next pass. * il4965_rs_get_lower_rate() will change type to LQ_A or LQ_G. */ use_ht_possible = 0; /* Override next rate if needed for debug purposes */ il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); /* Fill next table entry */ lq_cmd->rs_table[idx].rate_n_flags = cpu_to_le32(new_rate); idx++; repeat_rate--; } lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; lq_cmd->agg_params.agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); } static void * il4965_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { return hw->priv; } /* rate scale requires free function to be implemented */ static void il4965_rs_free(void *il_rate) { return; } static void il4965_rs_free_sta(void *il_r, struct ieee80211_sta *sta, void *il_sta) { struct il_priv *il __maybe_unused = il_r; D_RATE("enter\n"); D_RATE("leave\n"); } #ifdef CONFIG_MAC80211_DEBUGFS static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) { struct il_priv *il; u8 valid_tx_ant; u8 ant_sel_tx; il = lq_sta->drv; valid_tx_ant = il->hw_params.valid_tx_ant; if (lq_sta->dbg_fixed_rate) { ant_sel_tx = ((lq_sta-> dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { *rate_n_flags = lq_sta->dbg_fixed_rate; D_RATE("Fixed rate ON\n"); } else { lq_sta->dbg_fixed_rate = 0; IL_ERR ("Invalid antenna selection 0x%X, Valid is 0x%X\n", ant_sel_tx, valid_tx_ant); D_RATE("Fixed rate OFF\n"); } } else { D_RATE("Fixed rate OFF\n"); } } static ssize_t il4965_rs_sta_dbgfs_scale_table_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct il_lq_sta *lq_sta = file->private_data; struct il_priv *il; char buf[64]; size_t buf_size; u32 parsed_rate; il = lq_sta->drv; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; if (sscanf(buf, "%x", &parsed_rate) == 1) lq_sta->dbg_fixed_rate = parsed_rate; else lq_sta->dbg_fixed_rate = 0; lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ D_RATE("sta_id %d rate 0x%X\n", lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); if (lq_sta->dbg_fixed_rate) { il4965_rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); il_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); } return count; } static ssize_t il4965_rs_sta_dbgfs_scale_table_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buff; int desc = 0; int i = 0; int idx = 0; ssize_t ret; struct il_lq_sta *lq_sta = file->private_data; struct il_priv *il; struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); il = lq_sta->drv; buff = kmalloc(1024, GFP_KERNEL); if (!buff) return -ENOMEM; desc += sprintf(buff + desc, "sta_id %d\n", lq_sta->lq.sta_id); desc += sprintf(buff + desc, "failed=%d success=%d rate=0%X\n", lq_sta->total_failed, lq_sta->total_success, lq_sta->active_legacy_rate); desc += sprintf(buff + desc, "fixed rate 0x%X\n", lq_sta->dbg_fixed_rate); desc += sprintf(buff + desc, "valid_tx_ant %s%s%s\n", (il->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "", (il->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "", (il->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff + desc, "lq type %s\n", (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); if (is_Ht(tbl->lq_type)) { desc += sprintf(buff + desc, " %s", (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); desc += sprintf(buff + desc, " %s", (tbl->is_ht40) ? "40MHz" : "20MHz"); desc += sprintf(buff + desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "", (lq_sta->is_green) ? "GF enabled" : "", (lq_sta->is_agg) ? "AGG on" : ""); } desc += sprintf(buff + desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); desc += sprintf(buff + desc, "general:" "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", lq_sta->lq.general_params.flags, lq_sta->lq.general_params.mimo_delimiter, lq_sta->lq.general_params.single_stream_ant_msk, lq_sta->lq.general_params.dual_stream_ant_msk); desc += sprintf(buff + desc, "agg:" "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), lq_sta->lq.agg_params.agg_dis_start_th, lq_sta->lq.agg_params.agg_frame_cnt_limit); desc += sprintf(buff + desc, "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", lq_sta->lq.general_params.start_rate_idx[0], lq_sta->lq.general_params.start_rate_idx[1], lq_sta->lq.general_params.start_rate_idx[2], lq_sta->lq.general_params.start_rate_idx[3]); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { idx = il4965_hwrate_to_plcp_idx(le32_to_cpu (lq_sta->lq.rs_table[i]. rate_n_flags)); if (is_legacy(tbl->lq_type)) { desc += sprintf(buff + desc, " rate[%d] 0x%X %smbps\n", i, le32_to_cpu(lq_sta->lq.rs_table[i]. rate_n_flags), il_rate_mcs[idx].mbps); } else { desc += sprintf(buff + desc, " rate[%d] 0x%X %smbps (%s)\n", i, le32_to_cpu(lq_sta->lq.rs_table[i]. rate_n_flags), il_rate_mcs[idx].mbps, il_rate_mcs[idx].mcs); } } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); kfree(buff); return ret; } static const struct file_operations rs_sta_dbgfs_scale_table_ops = { .write = il4965_rs_sta_dbgfs_scale_table_write, .read = il4965_rs_sta_dbgfs_scale_table_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t il4965_rs_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buff; int desc = 0; int i, j; ssize_t ret; struct il_lq_sta *lq_sta = file->private_data; buff = kmalloc(1024, GFP_KERNEL); if (!buff) return -ENOMEM; for (i = 0; i < LQ_SIZE; i++) { desc += sprintf(buff + desc, "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" "rate=0x%X\n", lq_sta->active_tbl == i ? "*" : "x", lq_sta->lq_info[i].lq_type, lq_sta->lq_info[i].is_SGI, lq_sta->lq_info[i].is_ht40, lq_sta->lq_info[i].is_dup, lq_sta->is_green, lq_sta->lq_info[i].current_rate); for (j = 0; j < RATE_COUNT; j++) { desc += sprintf(buff + desc, "counter=%d success=%d %%=%d\n", lq_sta->lq_info[i].win[j].counter, lq_sta->lq_info[i].win[j].success_counter, lq_sta->lq_info[i].win[j].success_ratio); } } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); kfree(buff); return ret; } static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .read = il4965_rs_sta_dbgfs_stats_table_read, .open = simple_open, .llseek = default_llseek, }; static ssize_t il4965_rs_sta_dbgfs_rate_scale_data_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buff[120]; int desc = 0; struct il_lq_sta *lq_sta = file->private_data; struct il_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; if (is_Ht(tbl->lq_type)) desc += sprintf(buff + desc, "Bit Rate= %d Mb/s\n", tbl->expected_tpt[lq_sta->last_txrate_idx]); else desc += sprintf(buff + desc, "Bit Rate= %d Mb/s\n", il_rates[lq_sta->last_txrate_idx].ieee >> 1); return simple_read_from_buffer(user_buf, count, ppos, buff, desc); } static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { .read = il4965_rs_sta_dbgfs_rate_scale_data_read, .open = simple_open, .llseek = default_llseek, }; static void il4965_rs_add_debugfs(void *il, void *il_sta, struct dentry *dir) { struct il_lq_sta *lq_sta = il_sta; lq_sta->rs_sta_dbgfs_scale_table_file = debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_scale_table_ops); lq_sta->rs_sta_dbgfs_stats_table_file = debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_stats_table_ops); lq_sta->rs_sta_dbgfs_rate_scale_data_file = debugfs_create_file("rate_scale_data", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, &lq_sta->tx_agg_tid_en); } static void il4965_rs_remove_debugfs(void *il, void *il_sta) { struct il_lq_sta *lq_sta = il_sta; debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); } #endif /* * Initialization of rate scaling information is done by driver after * the station is added. Since mac80211 calls this function before a * station is added we ignore it. */ static void il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *il_sta) { } static struct rate_control_ops rs_4965_ops = { .module = NULL, .name = IL4965_RS_NAME, .tx_status = il4965_rs_tx_status, .get_rate = il4965_rs_get_rate, .rate_init = il4965_rs_rate_init_stub, .alloc = il4965_rs_alloc, .free = il4965_rs_free, .alloc_sta = il4965_rs_alloc_sta, .free_sta = il4965_rs_free_sta, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = il4965_rs_add_debugfs, .remove_sta_debugfs = il4965_rs_remove_debugfs, #endif }; int il4965_rate_control_register(void) { return ieee80211_rate_control_register(&rs_4965_ops); } void il4965_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rs_4965_ops); } compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/4965.h0000644000175000017500000014277612026211315023066 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __il_4965_h__ #define __il_4965_h__ struct il_rx_queue; struct il_rx_buf; struct il_rx_pkt; struct il_tx_queue; struct il_rxon_context; /* configuration for the _4965 devices */ extern struct il_cfg il4965_cfg; extern const struct il_ops il4965_ops; extern struct il_mod_params il4965_mod_params; /* tx queue */ void il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed); /* RXON */ void il4965_set_rxon_chain(struct il_priv *il); /* uCode */ int il4965_verify_ucode(struct il_priv *il); /* lib */ void il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status); void il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); int il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq); int il4965_hw_nic_init(struct il_priv *il); int il4965_dump_fh(struct il_priv *il, char **buf, bool display); void il4965_nic_config(struct il_priv *il); /* rx */ void il4965_rx_queue_restock(struct il_priv *il); void il4965_rx_replenish(struct il_priv *il); void il4965_rx_replenish_now(struct il_priv *il); void il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq); int il4965_rxq_stop(struct il_priv *il); int il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); void il4965_rx_handle(struct il_priv *il); /* tx */ void il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); int il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset, u8 pad); int il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); void il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, struct ieee80211_tx_info *info); int il4965_tx_skb(struct il_priv *il, struct ieee80211_sta *sta, struct sk_buff *skb); int il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 * ssn); int il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); int il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id); int il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx); void il4965_hw_txq_ctx_free(struct il_priv *il); int il4965_txq_ctx_alloc(struct il_priv *il); void il4965_txq_ctx_reset(struct il_priv *il); void il4965_txq_ctx_stop(struct il_priv *il); void il4965_txq_set_sched(struct il_priv *il, u32 mask); /* * Acquire il->lock before calling this function ! */ void il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx); /** * il4965_tx_queue_set_status - (optionally) start Tx/Cmd queue * @tx_fifo_id: Tx DMA/FIFO channel (range 0-7) that the queue will feed * @scd_retry: (1) Indicates queue will be used in aggregation mode * * NOTE: Acquire il->lock before calling this function ! */ void il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, int tx_fifo_id, int scd_retry); /* scan */ int il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif); /* station mgmt */ int il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, bool add); /* hcmd */ int il4965_send_beacon_cmd(struct il_priv *il); #ifdef CONFIG_IWLEGACY_DEBUG const char *il4965_get_tx_fail_reason(u32 status); #else static inline const char * il4965_get_tx_fail_reason(u32 status) { return ""; } #endif /* station management */ int il4965_alloc_bcast_station(struct il_priv *il); int il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r); int il4965_remove_default_wep_key(struct il_priv *il, struct ieee80211_key_conf *key); int il4965_set_default_wep_key(struct il_priv *il, struct ieee80211_key_conf *key); int il4965_restore_default_wep_keys(struct il_priv *il); int il4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *key, u8 sta_id); int il4965_remove_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *key, u8 sta_id); void il4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); int il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid); int il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid, u16 ssn); int il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid); void il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt); int il4965_update_bcast_stations(struct il_priv *il); /* rate */ static inline u8 il4965_hw_get_rate(__le32 rate_n_flags) { return le32_to_cpu(rate_n_flags) & 0xFF; } /* eeprom */ void il4965_eeprom_get_mac(const struct il_priv *il, u8 * mac); int il4965_eeprom_acquire_semaphore(struct il_priv *il); void il4965_eeprom_release_semaphore(struct il_priv *il); int il4965_eeprom_check_version(struct il_priv *il); /* mac80211 handlers (for 4965) */ void il4965_mac_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); int il4965_mac_start(struct ieee80211_hw *hw); void il4965_mac_stop(struct ieee80211_hw *hw); void il4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast); int il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 * ssn, u8 buf_size); int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch); void il4965_led_enable(struct il_priv *il); /* EEPROM */ #define IL4965_EEPROM_IMG_SIZE 1024 /* * uCode queue management definitions ... * The first queue used for block-ack aggregation is #7 (4965 only). * All block-ack aggregation queues should map to Tx DMA/FIFO channel 7. */ #define IL49_FIRST_AMPDU_QUEUE 7 /* Sizes and addresses for instruction and data memory (SRAM) in * 4965's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ #define IL49_RTC_INST_LOWER_BOUND (0x000000) #define IL49_RTC_INST_UPPER_BOUND (0x018000) #define IL49_RTC_DATA_LOWER_BOUND (0x800000) #define IL49_RTC_DATA_UPPER_BOUND (0x80A000) #define IL49_RTC_INST_SIZE (IL49_RTC_INST_UPPER_BOUND - \ IL49_RTC_INST_LOWER_BOUND) #define IL49_RTC_DATA_SIZE (IL49_RTC_DATA_UPPER_BOUND - \ IL49_RTC_DATA_LOWER_BOUND) #define IL49_MAX_INST_SIZE IL49_RTC_INST_SIZE #define IL49_MAX_DATA_SIZE IL49_RTC_DATA_SIZE /* Size of uCode instruction memory in bootstrap state machine */ #define IL49_MAX_BSM_SIZE BSM_SRAM_SIZE static inline int il4965_hw_valid_rtc_data_addr(u32 addr) { return (addr >= IL49_RTC_DATA_LOWER_BOUND && addr < IL49_RTC_DATA_UPPER_BOUND); } /********************* START TEMPERATURE *************************************/ /** * 4965 temperature calculation. * * The driver must calculate the device temperature before calculating * a txpower setting (amplifier gain is temperature dependent). The * calculation uses 4 measurements, 3 of which (R1, R2, R3) are calibration * values used for the life of the driver, and one of which (R4) is the * real-time temperature indicator. * * uCode provides all 4 values to the driver via the "initialize alive" * notification (see struct il4965_init_alive_resp). After the runtime uCode * image loads, uCode updates the R4 value via stats notifications * (see N_STATS), which occur after each received beacon * when associated, or can be requested via C_STATS. * * NOTE: uCode provides the R4 value as a 23-bit signed value. Driver * must sign-extend to 32 bits before applying formula below. * * Formula: * * degrees Kelvin = ((97 * 259 * (R4 - R2) / (R3 - R1)) / 100) + 8 * * NOTE: The basic formula is 259 * (R4-R2) / (R3-R1). The 97/100 is * an additional correction, which should be centered around 0 degrees * Celsius (273 degrees Kelvin). The 8 (3 percent of 273) compensates for * centering the 97/100 correction around 0 degrees K. * * Add 273 to Kelvin value to find degrees Celsius, for comparing current * temperature with factory-measured temperatures when calculating txpower * settings. */ #define TEMPERATURE_CALIB_KELVIN_OFFSET 8 #define TEMPERATURE_CALIB_A_VAL 259 /* Limit range of calculated temperature to be between these Kelvin values */ #define IL_TX_POWER_TEMPERATURE_MIN (263) #define IL_TX_POWER_TEMPERATURE_MAX (410) #define IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(t) \ ((t) < IL_TX_POWER_TEMPERATURE_MIN || \ (t) > IL_TX_POWER_TEMPERATURE_MAX) extern void il4965_temperature_calib(struct il_priv *il); /********************* END TEMPERATURE ***************************************/ /********************* START TXPOWER *****************************************/ /** * 4965 txpower calculations rely on information from three sources: * * 1) EEPROM * 2) "initialize" alive notification * 3) stats notifications * * EEPROM data consists of: * * 1) Regulatory information (max txpower and channel usage flags) is provided * separately for each channel that can possibly supported by 4965. * 40 MHz wide (.11n HT40) channels are listed separately from 20 MHz * (legacy) channels. * * See struct il4965_eeprom_channel for format, and struct il4965_eeprom * for locations in EEPROM. * * 2) Factory txpower calibration information is provided separately for * sub-bands of contiguous channels. 2.4GHz has just one sub-band, * but 5 GHz has several sub-bands. * * In addition, per-band (2.4 and 5 Ghz) saturation txpowers are provided. * * See struct il4965_eeprom_calib_info (and the tree of structures * contained within it) for format, and struct il4965_eeprom for * locations in EEPROM. * * "Initialization alive" notification (see struct il4965_init_alive_resp) * consists of: * * 1) Temperature calculation parameters. * * 2) Power supply voltage measurement. * * 3) Tx gain compensation to balance 2 transmitters for MIMO use. * * Statistics notifications deliver: * * 1) Current values for temperature param R4. */ /** * To calculate a txpower setting for a given desired target txpower, channel, * modulation bit rate, and transmitter chain (4965 has 2 transmitters to * support MIMO and transmit diversity), driver must do the following: * * 1) Compare desired txpower vs. (EEPROM) regulatory limit for this channel. * Do not exceed regulatory limit; reduce target txpower if necessary. * * If setting up txpowers for MIMO rates (rate idxes 8-15, 24-31), * 2 transmitters will be used simultaneously; driver must reduce the * regulatory limit by 3 dB (half-power) for each transmitter, so the * combined total output of the 2 transmitters is within regulatory limits. * * * 2) Compare target txpower vs. (EEPROM) saturation txpower *reduced by * backoff for this bit rate*. Do not exceed (saturation - backoff[rate]); * reduce target txpower if necessary. * * Backoff values below are in 1/2 dB units (equivalent to steps in * txpower gain tables): * * OFDM 6 - 36 MBit: 10 steps (5 dB) * OFDM 48 MBit: 15 steps (7.5 dB) * OFDM 54 MBit: 17 steps (8.5 dB) * OFDM 60 MBit: 20 steps (10 dB) * CCK all rates: 10 steps (5 dB) * * Backoff values apply to saturation txpower on a per-transmitter basis; * when using MIMO (2 transmitters), each transmitter uses the same * saturation level provided in EEPROM, and the same backoff values; * no reduction (such as with regulatory txpower limits) is required. * * Saturation and Backoff values apply equally to 20 Mhz (legacy) channel * widths and 40 Mhz (.11n HT40) channel widths; there is no separate * factory measurement for ht40 channels. * * The result of this step is the final target txpower. The rest of * the steps figure out the proper settings for the device to achieve * that target txpower. * * * 3) Determine (EEPROM) calibration sub band for the target channel, by * comparing against first and last channels in each sub band * (see struct il4965_eeprom_calib_subband_info). * * * 4) Linearly interpolate (EEPROM) factory calibration measurement sets, * referencing the 2 factory-measured (sample) channels within the sub band. * * Interpolation is based on difference between target channel's frequency * and the sample channels' frequencies. Since channel numbers are based * on frequency (5 MHz between each channel number), this is equivalent * to interpolating based on channel number differences. * * Note that the sample channels may or may not be the channels at the * edges of the sub band. The target channel may be "outside" of the * span of the sampled channels. * * Driver may choose the pair (for 2 Tx chains) of measurements (see * struct il4965_eeprom_calib_ch_info) for which the actual measured * txpower comes closest to the desired txpower. Usually, though, * the middle set of measurements is closest to the regulatory limits, * and is therefore a good choice for all txpower calculations (this * assumes that high accuracy is needed for maximizing legal txpower, * while lower txpower configurations do not need as much accuracy). * * Driver should interpolate both members of the chosen measurement pair, * i.e. for both Tx chains (radio transmitters), unless the driver knows * that only one of the chains will be used (e.g. only one tx antenna * connected, but this should be unusual). The rate scaling algorithm * switches antennas to find best performance, so both Tx chains will * be used (although only one at a time) even for non-MIMO transmissions. * * Driver should interpolate factory values for temperature, gain table * idx, and actual power. The power amplifier detector values are * not used by the driver. * * Sanity check: If the target channel happens to be one of the sample * channels, the results should agree with the sample channel's * measurements! * * * 5) Find difference between desired txpower and (interpolated) * factory-measured txpower. Using (interpolated) factory gain table idx * (shown elsewhere) as a starting point, adjust this idx lower to * increase txpower, or higher to decrease txpower, until the target * txpower is reached. Each step in the gain table is 1/2 dB. * * For example, if factory measured txpower is 16 dBm, and target txpower * is 13 dBm, add 6 steps to the factory gain idx to reduce txpower * by 3 dB. * * * 6) Find difference between current device temperature and (interpolated) * factory-measured temperature for sub-band. Factory values are in * degrees Celsius. To calculate current temperature, see comments for * "4965 temperature calculation". * * If current temperature is higher than factory temperature, driver must * increase gain (lower gain table idx), and vice verse. * * Temperature affects gain differently for different channels: * * 2.4 GHz all channels: 3.5 degrees per half-dB step * 5 GHz channels 34-43: 4.5 degrees per half-dB step * 5 GHz channels >= 44: 4.0 degrees per half-dB step * * NOTE: Temperature can increase rapidly when transmitting, especially * with heavy traffic at high txpowers. Driver should update * temperature calculations often under these conditions to * maintain strong txpower in the face of rising temperature. * * * 7) Find difference between current power supply voltage indicator * (from "initialize alive") and factory-measured power supply voltage * indicator (EEPROM). * * If the current voltage is higher (indicator is lower) than factory * voltage, gain should be reduced (gain table idx increased) by: * * (eeprom - current) / 7 * * If the current voltage is lower (indicator is higher) than factory * voltage, gain should be increased (gain table idx decreased) by: * * 2 * (current - eeprom) / 7 * * If number of idx steps in either direction turns out to be > 2, * something is wrong ... just use 0. * * NOTE: Voltage compensation is independent of band/channel. * * NOTE: "Initialize" uCode measures current voltage, which is assumed * to be constant after this initial measurement. Voltage * compensation for txpower (number of steps in gain table) * may be calculated once and used until the next uCode bootload. * * * 8) If setting up txpowers for MIMO rates (rate idxes 8-15, 24-31), * adjust txpower for each transmitter chain, so txpower is balanced * between the two chains. There are 5 pairs of tx_atten[group][chain] * values in "initialize alive", one pair for each of 5 channel ranges: * * Group 0: 5 GHz channel 34-43 * Group 1: 5 GHz channel 44-70 * Group 2: 5 GHz channel 71-124 * Group 3: 5 GHz channel 125-200 * Group 4: 2.4 GHz all channels * * Add the tx_atten[group][chain] value to the idx for the target chain. * The values are signed, but are in pairs of 0 and a non-negative number, * so as to reduce gain (if necessary) of the "hotter" channel. This * avoids any need to double-check for regulatory compliance after * this step. * * * 9) If setting up for a CCK rate, lower the gain by adding a CCK compensation * value to the idx: * * Hardware rev B: 9 steps (4.5 dB) * Hardware rev C: 5 steps (2.5 dB) * * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, * bits [3:2], 1 = B, 2 = C. * * NOTE: This compensation is in addition to any saturation backoff that * might have been applied in an earlier step. * * * 10) Select the gain table, based on band (2.4 vs 5 GHz). * * Limit the adjusted idx to stay within the table! * * * 11) Read gain table entries for DSP and radio gain, place into appropriate * location(s) in command (struct il4965_txpowertable_cmd). */ /** * When MIMO is used (2 transmitters operating simultaneously), driver should * limit each transmitter to deliver a max of 3 dB below the regulatory limit * for the device. That is, use half power for each transmitter, so total * txpower is within regulatory limits. * * The value "6" represents number of steps in gain table to reduce power 3 dB. * Each step is 1/2 dB. */ #define IL_TX_POWER_MIMO_REGULATORY_COMPENSATION (6) /** * CCK gain compensation. * * When calculating txpowers for CCK, after making sure that the target power * is within regulatory and saturation limits, driver must additionally * back off gain by adding these values to the gain table idx. * * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, * bits [3:2], 1 = B, 2 = C. */ #define IL_TX_POWER_CCK_COMPENSATION_B_STEP (9) #define IL_TX_POWER_CCK_COMPENSATION_C_STEP (5) /* * 4965 power supply voltage compensation for txpower */ #define TX_POWER_IL_VOLTAGE_CODES_PER_03V (7) /** * Gain tables. * * The following tables contain pair of values for setting txpower, i.e. * gain settings for the output of the device's digital signal processor (DSP), * and for the analog gain structure of the transmitter. * * Each entry in the gain tables represents a step of 1/2 dB. Note that these * are *relative* steps, not indications of absolute output power. Output * power varies with temperature, voltage, and channel frequency, and also * requires consideration of average power (to satisfy regulatory constraints), * and peak power (to avoid distortion of the output signal). * * Each entry contains two values: * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained * linear value that multiplies the output of the digital signal processor, * before being sent to the analog radio. * 2) Radio gain. This sets the analog gain of the radio Tx path. * It is a coarser setting, and behaves in a logarithmic (dB) fashion. * * EEPROM contains factory calibration data for txpower. This maps actual * measured txpower levels to gain settings in the "well known" tables * below ("well-known" means here that both factory calibration *and* the * driver work with the same table). * * There are separate tables for 2.4 GHz and 5 GHz bands. The 5 GHz table * has an extension (into negative idxes), in case the driver needs to * boost power setting for high device temperatures (higher than would be * present during factory calibration). A 5 Ghz EEPROM idx of "40" * corresponds to the 49th entry in the table used by the driver. */ #define MIN_TX_GAIN_IDX (0) /* highest gain, lowest idx, 2.4 */ #define MIN_TX_GAIN_IDX_52GHZ_EXT (-9) /* highest gain, lowest idx, 5 */ /** * 2.4 GHz gain table * * Index Dsp gain Radio gain * 0 110 0x3f (highest gain) * 1 104 0x3f * 2 98 0x3f * 3 110 0x3e * 4 104 0x3e * 5 98 0x3e * 6 110 0x3d * 7 104 0x3d * 8 98 0x3d * 9 110 0x3c * 10 104 0x3c * 11 98 0x3c * 12 110 0x3b * 13 104 0x3b * 14 98 0x3b * 15 110 0x3a * 16 104 0x3a * 17 98 0x3a * 18 110 0x39 * 19 104 0x39 * 20 98 0x39 * 21 110 0x38 * 22 104 0x38 * 23 98 0x38 * 24 110 0x37 * 25 104 0x37 * 26 98 0x37 * 27 110 0x36 * 28 104 0x36 * 29 98 0x36 * 30 110 0x35 * 31 104 0x35 * 32 98 0x35 * 33 110 0x34 * 34 104 0x34 * 35 98 0x34 * 36 110 0x33 * 37 104 0x33 * 38 98 0x33 * 39 110 0x32 * 40 104 0x32 * 41 98 0x32 * 42 110 0x31 * 43 104 0x31 * 44 98 0x31 * 45 110 0x30 * 46 104 0x30 * 47 98 0x30 * 48 110 0x6 * 49 104 0x6 * 50 98 0x6 * 51 110 0x5 * 52 104 0x5 * 53 98 0x5 * 54 110 0x4 * 55 104 0x4 * 56 98 0x4 * 57 110 0x3 * 58 104 0x3 * 59 98 0x3 * 60 110 0x2 * 61 104 0x2 * 62 98 0x2 * 63 110 0x1 * 64 104 0x1 * 65 98 0x1 * 66 110 0x0 * 67 104 0x0 * 68 98 0x0 * 69 97 0 * 70 96 0 * 71 95 0 * 72 94 0 * 73 93 0 * 74 92 0 * 75 91 0 * 76 90 0 * 77 89 0 * 78 88 0 * 79 87 0 * 80 86 0 * 81 85 0 * 82 84 0 * 83 83 0 * 84 82 0 * 85 81 0 * 86 80 0 * 87 79 0 * 88 78 0 * 89 77 0 * 90 76 0 * 91 75 0 * 92 74 0 * 93 73 0 * 94 72 0 * 95 71 0 * 96 70 0 * 97 69 0 * 98 68 0 */ /** * 5 GHz gain table * * Index Dsp gain Radio gain * -9 123 0x3F (highest gain) * -8 117 0x3F * -7 110 0x3F * -6 104 0x3F * -5 98 0x3F * -4 110 0x3E * -3 104 0x3E * -2 98 0x3E * -1 110 0x3D * 0 104 0x3D * 1 98 0x3D * 2 110 0x3C * 3 104 0x3C * 4 98 0x3C * 5 110 0x3B * 6 104 0x3B * 7 98 0x3B * 8 110 0x3A * 9 104 0x3A * 10 98 0x3A * 11 110 0x39 * 12 104 0x39 * 13 98 0x39 * 14 110 0x38 * 15 104 0x38 * 16 98 0x38 * 17 110 0x37 * 18 104 0x37 * 19 98 0x37 * 20 110 0x36 * 21 104 0x36 * 22 98 0x36 * 23 110 0x35 * 24 104 0x35 * 25 98 0x35 * 26 110 0x34 * 27 104 0x34 * 28 98 0x34 * 29 110 0x33 * 30 104 0x33 * 31 98 0x33 * 32 110 0x32 * 33 104 0x32 * 34 98 0x32 * 35 110 0x31 * 36 104 0x31 * 37 98 0x31 * 38 110 0x30 * 39 104 0x30 * 40 98 0x30 * 41 110 0x25 * 42 104 0x25 * 43 98 0x25 * 44 110 0x24 * 45 104 0x24 * 46 98 0x24 * 47 110 0x23 * 48 104 0x23 * 49 98 0x23 * 50 110 0x22 * 51 104 0x18 * 52 98 0x18 * 53 110 0x17 * 54 104 0x17 * 55 98 0x17 * 56 110 0x16 * 57 104 0x16 * 58 98 0x16 * 59 110 0x15 * 60 104 0x15 * 61 98 0x15 * 62 110 0x14 * 63 104 0x14 * 64 98 0x14 * 65 110 0x13 * 66 104 0x13 * 67 98 0x13 * 68 110 0x12 * 69 104 0x08 * 70 98 0x08 * 71 110 0x07 * 72 104 0x07 * 73 98 0x07 * 74 110 0x06 * 75 104 0x06 * 76 98 0x06 * 77 110 0x05 * 78 104 0x05 * 79 98 0x05 * 80 110 0x04 * 81 104 0x04 * 82 98 0x04 * 83 110 0x03 * 84 104 0x03 * 85 98 0x03 * 86 110 0x02 * 87 104 0x02 * 88 98 0x02 * 89 110 0x01 * 90 104 0x01 * 91 98 0x01 * 92 110 0x00 * 93 104 0x00 * 94 98 0x00 * 95 93 0x00 * 96 88 0x00 * 97 83 0x00 * 98 78 0x00 */ /** * Sanity checks and default values for EEPROM regulatory levels. * If EEPROM values fall outside MIN/MAX range, use default values. * * Regulatory limits refer to the maximum average txpower allowed by * regulatory agencies in the geographies in which the device is meant * to be operated. These limits are SKU-specific (i.e. geography-specific), * and channel-specific; each channel has an individual regulatory limit * listed in the EEPROM. * * Units are in half-dBm (i.e. "34" means 17 dBm). */ #define IL_TX_POWER_DEFAULT_REGULATORY_24 (34) #define IL_TX_POWER_DEFAULT_REGULATORY_52 (34) #define IL_TX_POWER_REGULATORY_MIN (0) #define IL_TX_POWER_REGULATORY_MAX (34) /** * Sanity checks and default values for EEPROM saturation levels. * If EEPROM values fall outside MIN/MAX range, use default values. * * Saturation is the highest level that the output power amplifier can produce * without significant clipping distortion. This is a "peak" power level. * Different types of modulation (i.e. various "rates", and OFDM vs. CCK) * require differing amounts of backoff, relative to their average power output, * in order to avoid clipping distortion. * * Driver must make sure that it is violating neither the saturation limit, * nor the regulatory limit, when calculating Tx power settings for various * rates. * * Units are in half-dBm (i.e. "38" means 19 dBm). */ #define IL_TX_POWER_DEFAULT_SATURATION_24 (38) #define IL_TX_POWER_DEFAULT_SATURATION_52 (38) #define IL_TX_POWER_SATURATION_MIN (20) #define IL_TX_POWER_SATURATION_MAX (50) /** * Channel groups used for Tx Attenuation calibration (MIMO tx channel balance) * and thermal Txpower calibration. * * When calculating txpower, driver must compensate for current device * temperature; higher temperature requires higher gain. Driver must calculate * current temperature (see "4965 temperature calculation"), then compare vs. * factory calibration temperature in EEPROM; if current temperature is higher * than factory temperature, driver must *increase* gain by proportions shown * in table below. If current temperature is lower than factory, driver must * *decrease* gain. * * Different frequency ranges require different compensation, as shown below. */ /* Group 0, 5.2 GHz ch 34-43: 4.5 degrees per 1/2 dB. */ #define CALIB_IL_TX_ATTEN_GR1_FCH 34 #define CALIB_IL_TX_ATTEN_GR1_LCH 43 /* Group 1, 5.3 GHz ch 44-70: 4.0 degrees per 1/2 dB. */ #define CALIB_IL_TX_ATTEN_GR2_FCH 44 #define CALIB_IL_TX_ATTEN_GR2_LCH 70 /* Group 2, 5.5 GHz ch 71-124: 4.0 degrees per 1/2 dB. */ #define CALIB_IL_TX_ATTEN_GR3_FCH 71 #define CALIB_IL_TX_ATTEN_GR3_LCH 124 /* Group 3, 5.7 GHz ch 125-200: 4.0 degrees per 1/2 dB. */ #define CALIB_IL_TX_ATTEN_GR4_FCH 125 #define CALIB_IL_TX_ATTEN_GR4_LCH 200 /* Group 4, 2.4 GHz all channels: 3.5 degrees per 1/2 dB. */ #define CALIB_IL_TX_ATTEN_GR5_FCH 1 #define CALIB_IL_TX_ATTEN_GR5_LCH 20 enum { CALIB_CH_GROUP_1 = 0, CALIB_CH_GROUP_2 = 1, CALIB_CH_GROUP_3 = 2, CALIB_CH_GROUP_4 = 3, CALIB_CH_GROUP_5 = 4, CALIB_CH_GROUP_MAX }; /********************* END TXPOWER *****************************************/ /** * Tx/Rx Queues * * Most communication between driver and 4965 is via queues of data buffers. * For example, all commands that the driver issues to device's embedded * controller (uCode) are via the command queue (one of the Tx queues). All * uCode command responses/replies/notifications, including Rx frames, are * conveyed from uCode to driver via the Rx queue. * * Most support for these queues, including handshake support, resides in * structures in host DRAM, shared between the driver and the device. When * allocating this memory, the driver must make sure that data written by * the host CPU updates DRAM immediately (and does not get "stuck" in CPU's * cache memory), so DRAM and cache are consistent, and the device can * immediately see changes made by the driver. * * 4965 supports up to 16 DRAM-based Tx queues, and services these queues via * up to 7 DMA channels (FIFOs). Each Tx queue is supported by a circular array * in DRAM containing 256 Transmit Frame Descriptors (TFDs). */ #define IL49_NUM_FIFOS 7 #define IL49_CMD_FIFO_NUM 4 #define IL49_NUM_QUEUES 16 #define IL49_NUM_AMPDU_QUEUES 8 /** * struct il4965_schedq_bc_tbl * * Byte Count table * * Each Tx queue uses a byte-count table containing 320 entries: * one 16-bit entry for each of 256 TFDs, plus an additional 64 entries that * duplicate the first 64 entries (to avoid wrap-around within a Tx win; * max Tx win is 64 TFDs). * * When driver sets up a new TFD, it must also enter the total byte count * of the frame to be transmitted into the corresponding entry in the byte * count table for the chosen Tx queue. If the TFD idx is 0-63, the driver * must duplicate the byte count entry in corresponding idx 256-319. * * padding puts each byte count table on a 1024-byte boundary; * 4965 assumes tables are separated by 1024 bytes. */ struct il4965_scd_bc_tbl { __le16 tfd_offset[TFD_QUEUE_BC_SIZE]; u8 pad[1024 - (TFD_QUEUE_BC_SIZE) * sizeof(__le16)]; } __packed; #define IL4965_RTC_INST_LOWER_BOUND (0x000000) /* RSSI to dBm */ #define IL4965_RSSI_OFFSET 44 /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 /* PCI register values */ #define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01 #define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02 #define IL4965_DEFAULT_TX_RETRY 15 /* EEPROM */ #define IL4965_FIRST_AMPDU_QUEUE 10 /* Calibration */ void il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp); void il4965_sensitivity_calibration(struct il_priv *il, void *resp); void il4965_init_sensitivity(struct il_priv *il); void il4965_reset_run_time_calib(struct il_priv *il); /* Debug */ #ifdef CONFIG_IWLEGACY_DEBUGFS extern const struct il_debugfs_ops il4965_debugfs_ops; #endif /****************************/ /* Flow Handler Definitions */ /****************************/ /** * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) * Addresses are offsets from device's PCI hardware base address. */ #define FH49_MEM_LOWER_BOUND (0x1000) #define FH49_MEM_UPPER_BOUND (0x2000) /** * Keep-Warm (KW) buffer base address. * * Driver must allocate a 4KByte buffer that is used by 4965 for keeping the * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency * DRAM access when 4965 is Txing or Rxing. The dummy accesses prevent host * from going into a power-savings mode that would cause higher DRAM latency, * and possible data over/under-runs, before all Tx/Rx is complete. * * Driver loads FH49_KW_MEM_ADDR_REG with the physical address (bits 35:4) * of the buffer, which must be 4K aligned. Once this is set up, the 4965 * automatically invokes keep-warm accesses when normal accesses might not * be sufficient to maintain fast DRAM response. * * Bit fields: * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned */ #define FH49_KW_MEM_ADDR_REG (FH49_MEM_LOWER_BOUND + 0x97C) /** * TFD Circular Buffers Base (CBBC) addresses * * 4965 has 16 base pointer registers, one for each of 16 host-DRAM-resident * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) * (see struct il_tfd_frame). These 16 pointer registers are offset by 0x04 * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte * aligned (address bits 0-7 must be 0). * * Bit fields in each pointer register: * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned */ #define FH49_MEM_CBBC_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x9D0) #define FH49_MEM_CBBC_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xA10) /* Find TFD CB base pointer for given queue (range 0-15). */ #define FH49_MEM_CBBC_QUEUE(x) (FH49_MEM_CBBC_LOWER_BOUND + (x) * 0x4) /** * Rx SRAM Control and Status Registers (RSCSR) * * These registers provide handshake between driver and 4965 for the Rx queue * (this queue handles *all* command responses, notifications, Rx data, etc. * sent from 4965 uCode to host driver). Unlike Tx, there is only one Rx * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 * mapping between RBDs and RBs. * * Driver must allocate host DRAM memory for the following, and set the * physical address of each into 4965 registers: * * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 * entries (although any power of 2, up to 4096, is selectable by driver). * Each entry (1 dword) points to a receive buffer (RB) of consistent size * (typically 4K, although 8K or 16K are also selectable by driver). * Driver sets up RB size and number of RBDs in the CB via Rx config * register FH49_MEM_RCSR_CHNL0_CONFIG_REG. * * Bit fields within one RBD: * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned * * Driver sets physical address [35:8] of base of RBD circular buffer * into FH49_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. * * 2) Rx status buffer, 8 bytes, in which 4965 indicates which Rx Buffers * (RBs) have been filled, via a "write pointer", actually the idx of * the RB's corresponding RBD within the circular buffer. Driver sets * physical address [35:4] into FH49_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. * * Bit fields in lower dword of Rx status buffer (upper dword not used * by driver; see struct il4965_shared, val0): * 31-12: Not used by driver * 11- 0: Index of last filled Rx buffer descriptor * (4965 writes, driver reads this value) * * As the driver prepares Receive Buffers (RBs) for 4965 to fill, driver must * enter pointers to these RBs into contiguous RBD circular buffer entries, * and update the 4965's "write" idx register, * FH49_RSCSR_CHNL0_RBDCB_WPTR_REG. * * This "write" idx corresponds to the *next* RBD that the driver will make * available, i.e. one RBD past the tail of the ready-to-fill RBDs within * the circular buffer. This value should initially be 0 (before preparing any * RBs), should be 8 after preparing the first 8 RBs (for example), and must * wrap back to 0 at the end of the circular buffer (but don't wrap before * "read" idx has advanced past 1! See below). * NOTE: 4965 EXPECTS THE WRITE IDX TO BE INCREMENTED IN MULTIPLES OF 8. * * As the 4965 fills RBs (referenced from contiguous RBDs within the circular * buffer), it updates the Rx status buffer in host DRAM, 2) described above, * to tell the driver the idx of the latest filled RBD. The driver must * read this "read" idx from DRAM after receiving an Rx interrupt from 4965. * * The driver must also internally keep track of a third idx, which is the * next RBD to process. When receiving an Rx interrupt, driver should process * all filled but unprocessed RBs up to, but not including, the RB * corresponding to the "read" idx. For example, if "read" idx becomes "1", * driver may process the RB pointed to by RBD 0. Depending on volume of * traffic, there may be many RBs to process. * * If read idx == write idx, 4965 thinks there is no room to put new data. * Due to this, the maximum number of filled RBs is 255, instead of 256. To * be safe, make sure that there is a gap of at least 2 RBDs between "write" * and "read" idxes; that is, make sure that there are no more than 254 * buffers waiting to be filled. */ #define FH49_MEM_RSCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xBC0) #define FH49_MEM_RSCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xC00) #define FH49_MEM_RSCSR_CHNL0 (FH49_MEM_RSCSR_LOWER_BOUND) /** * Physical base address of 8-byte Rx Status buffer. * Bit fields: * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. */ #define FH49_RSCSR_CHNL0_STTS_WPTR_REG (FH49_MEM_RSCSR_CHNL0) /** * Physical base address of Rx Buffer Descriptor Circular Buffer. * Bit fields: * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. */ #define FH49_RSCSR_CHNL0_RBDCB_BASE_REG (FH49_MEM_RSCSR_CHNL0 + 0x004) /** * Rx write pointer (idx, really!). * Bit fields: * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. * NOTE: For 256-entry circular buffer, use only bits [7:0]. */ #define FH49_RSCSR_CHNL0_RBDCB_WPTR_REG (FH49_MEM_RSCSR_CHNL0 + 0x008) #define FH49_RSCSR_CHNL0_WPTR (FH49_RSCSR_CHNL0_RBDCB_WPTR_REG) /** * Rx Config/Status Registers (RCSR) * Rx Config Reg for channel 0 (only channel used) * * Driver must initialize FH49_MEM_RCSR_CHNL0_CONFIG_REG as follows for * normal operation (see bit fields). * * Clearing FH49_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. * Driver should poll FH49_MEM_RSSR_RX_STATUS_REG for * FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. * * Bit fields: * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, * '10' operate normally * 29-24: reserved * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), * min "5" for 32 RBDs, max "12" for 4096 RBDs. * 19-18: reserved * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, * '10' 12K, '11' 16K. * 15-14: reserved * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) * typical value 0x10 (about 1/2 msec) * 3- 0: reserved */ #define FH49_MEM_RCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xC00) #define FH49_MEM_RCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xCC0) #define FH49_MEM_RCSR_CHNL0 (FH49_MEM_RCSR_LOWER_BOUND) #define FH49_MEM_RCSR_CHNL0_CONFIG_REG (FH49_MEM_RCSR_CHNL0) #define FH49_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ #define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ #define FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ #define FH49_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ #define FH49_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ #define FH49_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31 */ #define FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) #define FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) #define RX_RB_TIMEOUT (0x10) #define FH49_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) #define FH49_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) #define FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) #define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) #define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) #define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) #define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) #define FH49_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) #define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) #define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) /** * Rx Shared Status Registers (RSSR) * * After stopping Rx DMA channel (writing 0 to * FH49_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll * FH49_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. * * Bit fields: * 24: 1 = Channel 0 is idle * * FH49_MEM_RSSR_SHARED_CTRL_REG and FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV * contain default values that should not be altered by the driver. */ #define FH49_MEM_RSSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xC40) #define FH49_MEM_RSSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xD00) #define FH49_MEM_RSSR_SHARED_CTRL_REG (FH49_MEM_RSSR_LOWER_BOUND) #define FH49_MEM_RSSR_RX_STATUS_REG (FH49_MEM_RSSR_LOWER_BOUND + 0x004) #define FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ (FH49_MEM_RSSR_LOWER_BOUND + 0x008) #define FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) #define FH49_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 /* TFDB Area - TFDs buffer table */ #define FH49_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) #define FH49_TFDIB_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x900) #define FH49_TFDIB_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0x958) #define FH49_TFDIB_CTRL0_REG(_chnl) (FH49_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) #define FH49_TFDIB_CTRL1_REG(_chnl) (FH49_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) /** * Transmit DMA Channel Control/Status Registers (TCSR) * * 4965 has one configuration register for each of 8 Tx DMA/FIFO channels * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. * * To use a Tx DMA channel, driver must initialize its * FH49_TCSR_CHNL_TX_CONFIG_REG(chnl) with: * * FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | * FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL * * All other bits should be 0. * * Bit fields: * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, * '10' operate normally * 29- 4: Reserved, set to "0" * 3: Enable internal DMA requests (1, normal operation), disable (0) * 2- 0: Reserved, set to "0" */ #define FH49_TCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xD00) #define FH49_TCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xE60) /* Find Control/Status reg for given Tx DMA/FIFO channel */ #define FH49_TCSR_CHNL_NUM (7) #define FH50_TCSR_CHNL_NUM (8) /* TCSR: tx_config register values */ #define FH49_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl)) #define FH49_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) #define FH49_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) #define FH49_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) #define FH49_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) #define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) #define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) #define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) #define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) #define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) #define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) #define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) #define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) #define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) #define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) #define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) #define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) #define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) #define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) #define FH49_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) #define FH49_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) /** * Tx Shared Status Registers (TSSR) * * After stopping Tx DMA channel (writing 0 to * FH49_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll * FH49_TSSR_TX_STATUS_REG until selected Tx channel is idle * (channel's buffers empty | no pending requests). * * Bit fields: * 31-24: 1 = Channel buffers empty (channel 7:0) * 23-16: 1 = No pending requests (channel 7:0) */ #define FH49_TSSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xEA0) #define FH49_TSSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xEC0) #define FH49_TSSR_TX_STATUS_REG (FH49_TSSR_LOWER_BOUND + 0x010) /** * Bit fields for TSSR(Tx Shared Status & Control) error status register: * 31: Indicates an address error when accessed to internal memory * uCode/driver must write "1" in order to clear this flag * 30: Indicates that Host did not send the expected number of dwords to FH * uCode/driver must write "1" in order to clear this flag * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA * command was received from the scheduler while the TRB was already full * with previous command * uCode/driver must write "1" in order to clear this flag * 7-0: Each status bit indicates a channel's TxCredit error. When an error * bit is set, it indicates that the FH has received a full indication * from the RTC TxFIFO and the current value of the TxCredit counter was * not equal to zero. This mean that the credit mechanism was not * synchronized to the TxFIFO status * uCode/driver must write "1" in order to clear this flag */ #define FH49_TSSR_TX_ERROR_REG (FH49_TSSR_LOWER_BOUND + 0x018) #define FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) /* Tx service channels */ #define FH49_SRVC_CHNL (9) #define FH49_SRVC_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x9C8) #define FH49_SRVC_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0x9D0) #define FH49_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ (FH49_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) #define FH49_TX_CHICKEN_BITS_REG (FH49_MEM_LOWER_BOUND + 0xE98) /* Instruct FH to increment the retry count of a packet when * it is brought from the memory to TX-FIFO */ #define FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) /* Keep Warm Size */ #define IL_KW_SIZE 0x1000 /* 4k */ #endif /* __il_4965_h__ */ compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/4965-debug.c0000644000175000017500000007341712026211315024140 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include "common.h" #include "4965.h" static const char *fmt_value = " %-30s %10u\n"; static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; static const char *fmt_header = "%-32s current cumulative delta max\n"; static int il4965_stats_flag(struct il_priv *il, char *buf, int bufsz) { int p = 0; u32 flag; flag = le32_to_cpu(il->_4965.stats.flag); p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); if (flag & UCODE_STATS_CLEAR_MSK) p += scnprintf(buf + p, bufsz - p, "\tStatistics have been cleared\n"); p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", (flag & UCODE_STATS_FREQUENCY_MSK) ? "2.4 GHz" : "5.2 GHz"); p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", (flag & UCODE_STATS_NARROW_BAND_MSK) ? "enabled" : "disabled"); return p; } ssize_t il4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct stats_rx_phy) * 40 + sizeof(struct stats_rx_non_phy) * 40 + sizeof(struct stats_rx_ht_phy) * 40 + 400; ssize_t ret; struct stats_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; struct stats_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; struct stats_rx_non_phy *general, *accum_general; struct stats_rx_non_phy *delta_general, *max_general; struct stats_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; if (!il_is_alive(il)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } /* * the statistic information display here is based on * the last stats notification from uCode * might not reflect the current uCode activity */ ofdm = &il->_4965.stats.rx.ofdm; cck = &il->_4965.stats.rx.cck; general = &il->_4965.stats.rx.general; ht = &il->_4965.stats.rx.ofdm_ht; accum_ofdm = &il->_4965.accum_stats.rx.ofdm; accum_cck = &il->_4965.accum_stats.rx.cck; accum_general = &il->_4965.accum_stats.rx.general; accum_ht = &il->_4965.accum_stats.rx.ofdm_ht; delta_ofdm = &il->_4965.delta_stats.rx.ofdm; delta_cck = &il->_4965.delta_stats.rx.cck; delta_general = &il->_4965.delta_stats.rx.general; delta_ht = &il->_4965.delta_stats.rx.ofdm_ht; max_ofdm = &il->_4965.max_delta.rx.ofdm; max_cck = &il->_4965.max_delta.rx.cck; max_general = &il->_4965.max_delta.rx.general; max_ht = &il->_4965.max_delta.rx.ofdm_ht; pos += il4965_stats_flag(il, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - OFDM:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt, delta_ofdm->ina_cnt, max_ofdm->ina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, delta_ofdm->fina_cnt, max_ofdm->fina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, delta_ofdm->plcp_err, max_ofdm->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, delta_ofdm->crc32_err, max_ofdm->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", le32_to_cpu(ofdm->overrun_err), accum_ofdm->overrun_err, delta_ofdm->overrun_err, max_ofdm->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", le32_to_cpu(ofdm->early_overrun_err), accum_ofdm->early_overrun_err, delta_ofdm->early_overrun_err, max_ofdm->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", le32_to_cpu(ofdm->crc32_good), accum_ofdm->crc32_good, delta_ofdm->crc32_good, max_ofdm->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", le32_to_cpu(ofdm->false_alarm_cnt), accum_ofdm->false_alarm_cnt, delta_ofdm->false_alarm_cnt, max_ofdm->false_alarm_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", le32_to_cpu(ofdm->fina_sync_err_cnt), accum_ofdm->fina_sync_err_cnt, delta_ofdm->fina_sync_err_cnt, max_ofdm->fina_sync_err_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", le32_to_cpu(ofdm->sfd_timeout), accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, max_ofdm->sfd_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", le32_to_cpu(ofdm->fina_timeout), accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, max_ofdm->fina_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", le32_to_cpu(ofdm->unresponded_rts), accum_ofdm->unresponded_rts, delta_ofdm->unresponded_rts, max_ofdm->unresponded_rts); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", le32_to_cpu(ofdm->rxe_frame_limit_overrun), accum_ofdm->rxe_frame_limit_overrun, delta_ofdm->rxe_frame_limit_overrun, max_ofdm->rxe_frame_limit_overrun); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", le32_to_cpu(ofdm->sent_ack_cnt), accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, max_ofdm->sent_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", le32_to_cpu(ofdm->sent_cts_cnt), accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", le32_to_cpu(ofdm->sent_ba_rsp_cnt), accum_ofdm->sent_ba_rsp_cnt, delta_ofdm->sent_ba_rsp_cnt, max_ofdm->sent_ba_rsp_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", le32_to_cpu(ofdm->dsp_self_kill), accum_ofdm->dsp_self_kill, delta_ofdm->dsp_self_kill, max_ofdm->dsp_self_kill); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", le32_to_cpu(ofdm->mh_format_err), accum_ofdm->mh_format_err, delta_ofdm->mh_format_err, max_ofdm->mh_format_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "re_acq_main_rssi_sum:", le32_to_cpu(ofdm->re_acq_main_rssi_sum), accum_ofdm->re_acq_main_rssi_sum, delta_ofdm->re_acq_main_rssi_sum, max_ofdm->re_acq_main_rssi_sum); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - CCK:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, delta_cck->ina_cnt, max_cck->ina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, delta_cck->fina_cnt, max_cck->fina_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, delta_cck->plcp_err, max_cck->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, delta_cck->crc32_err, max_cck->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", le32_to_cpu(cck->overrun_err), accum_cck->overrun_err, delta_cck->overrun_err, max_cck->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", le32_to_cpu(cck->early_overrun_err), accum_cck->early_overrun_err, delta_cck->early_overrun_err, max_cck->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, delta_cck->crc32_good, max_cck->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", le32_to_cpu(cck->false_alarm_cnt), accum_cck->false_alarm_cnt, delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", le32_to_cpu(cck->fina_sync_err_cnt), accum_cck->fina_sync_err_cnt, delta_cck->fina_sync_err_cnt, max_cck->fina_sync_err_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", le32_to_cpu(cck->sfd_timeout), accum_cck->sfd_timeout, delta_cck->sfd_timeout, max_cck->sfd_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", le32_to_cpu(cck->fina_timeout), accum_cck->fina_timeout, delta_cck->fina_timeout, max_cck->fina_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", le32_to_cpu(cck->unresponded_rts), accum_cck->unresponded_rts, delta_cck->unresponded_rts, max_cck->unresponded_rts); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", le32_to_cpu(cck->rxe_frame_limit_overrun), accum_cck->rxe_frame_limit_overrun, delta_cck->rxe_frame_limit_overrun, max_cck->rxe_frame_limit_overrun); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", le32_to_cpu(cck->sent_ack_cnt), accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, max_cck->sent_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", le32_to_cpu(cck->sent_cts_cnt), accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, max_cck->sent_cts_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", le32_to_cpu(cck->sent_ba_rsp_cnt), accum_cck->sent_ba_rsp_cnt, delta_cck->sent_ba_rsp_cnt, max_cck->sent_ba_rsp_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", le32_to_cpu(cck->dsp_self_kill), accum_cck->dsp_self_kill, delta_cck->dsp_self_kill, max_cck->dsp_self_kill); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", le32_to_cpu(cck->mh_format_err), accum_cck->mh_format_err, delta_cck->mh_format_err, max_cck->mh_format_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "re_acq_main_rssi_sum:", le32_to_cpu(cck->re_acq_main_rssi_sum), accum_cck->re_acq_main_rssi_sum, delta_cck->re_acq_main_rssi_sum, max_cck->re_acq_main_rssi_sum); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - GENERAL:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_cts:", le32_to_cpu(general->bogus_cts), accum_general->bogus_cts, delta_general->bogus_cts, max_general->bogus_cts); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_ack:", le32_to_cpu(general->bogus_ack), accum_general->bogus_ack, delta_general->bogus_ack, max_general->bogus_ack); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "non_bssid_frames:", le32_to_cpu(general->non_bssid_frames), accum_general->non_bssid_frames, delta_general->non_bssid_frames, max_general->non_bssid_frames); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "filtered_frames:", le32_to_cpu(general->filtered_frames), accum_general->filtered_frames, delta_general->filtered_frames, max_general->filtered_frames); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "non_channel_beacons:", le32_to_cpu(general->non_channel_beacons), accum_general->non_channel_beacons, delta_general->non_channel_beacons, max_general->non_channel_beacons); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_beacons:", le32_to_cpu(general->channel_beacons), accum_general->channel_beacons, delta_general->channel_beacons, max_general->channel_beacons); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "num_missed_bcon:", le32_to_cpu(general->num_missed_bcon), accum_general->num_missed_bcon, delta_general->num_missed_bcon, max_general->num_missed_bcon); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "adc_rx_saturation_time:", le32_to_cpu(general->adc_rx_saturation_time), accum_general->adc_rx_saturation_time, delta_general->adc_rx_saturation_time, max_general->adc_rx_saturation_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_detect_search_tm:", le32_to_cpu(general->ina_detection_search_time), accum_general->ina_detection_search_time, delta_general->ina_detection_search_time, max_general->ina_detection_search_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_silence_rssi_a:", le32_to_cpu(general->beacon_silence_rssi_a), accum_general->beacon_silence_rssi_a, delta_general->beacon_silence_rssi_a, max_general->beacon_silence_rssi_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_silence_rssi_b:", le32_to_cpu(general->beacon_silence_rssi_b), accum_general->beacon_silence_rssi_b, delta_general->beacon_silence_rssi_b, max_general->beacon_silence_rssi_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_silence_rssi_c:", le32_to_cpu(general->beacon_silence_rssi_c), accum_general->beacon_silence_rssi_c, delta_general->beacon_silence_rssi_c, max_general->beacon_silence_rssi_c); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "interference_data_flag:", le32_to_cpu(general->interference_data_flag), accum_general->interference_data_flag, delta_general->interference_data_flag, max_general->interference_data_flag); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_load:", le32_to_cpu(general->channel_load), accum_general->channel_load, delta_general->channel_load, max_general->channel_load); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_false_alarms:", le32_to_cpu(general->dsp_false_alarms), accum_general->dsp_false_alarms, delta_general->dsp_false_alarms, max_general->dsp_false_alarms); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_a:", le32_to_cpu(general->beacon_rssi_a), accum_general->beacon_rssi_a, delta_general->beacon_rssi_a, max_general->beacon_rssi_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_b:", le32_to_cpu(general->beacon_rssi_b), accum_general->beacon_rssi_b, delta_general->beacon_rssi_b, max_general->beacon_rssi_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_c:", le32_to_cpu(general->beacon_rssi_c), accum_general->beacon_rssi_c, delta_general->beacon_rssi_c, max_general->beacon_rssi_c); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_a:", le32_to_cpu(general->beacon_energy_a), accum_general->beacon_energy_a, delta_general->beacon_energy_a, max_general->beacon_energy_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_b:", le32_to_cpu(general->beacon_energy_b), accum_general->beacon_energy_b, delta_general->beacon_energy_b, max_general->beacon_energy_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_c:", le32_to_cpu(general->beacon_energy_c), accum_general->beacon_energy_c, delta_general->beacon_energy_c, max_general->beacon_energy_c); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - OFDM_HT:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, delta_ht->plcp_err, max_ht->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, delta_ht->overrun_err, max_ht->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", le32_to_cpu(ht->early_overrun_err), accum_ht->early_overrun_err, delta_ht->early_overrun_err, max_ht->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, delta_ht->crc32_good, max_ht->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, delta_ht->crc32_err, max_ht->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", le32_to_cpu(ht->mh_format_err), accum_ht->mh_format_err, delta_ht->mh_format_err, max_ht->mh_format_err); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_crc32_good:", le32_to_cpu(ht->agg_crc32_good), accum_ht->agg_crc32_good, delta_ht->agg_crc32_good, max_ht->agg_crc32_good); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_mpdu_cnt:", le32_to_cpu(ht->agg_mpdu_cnt), accum_ht->agg_mpdu_cnt, delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_cnt:", le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, delta_ht->agg_cnt, max_ht->agg_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "unsupport_mcs:", le32_to_cpu(ht->unsupport_mcs), accum_ht->unsupport_mcs, delta_ht->unsupport_mcs, max_ht->unsupport_mcs); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } ssize_t il4965_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char *buf; int bufsz = (sizeof(struct stats_tx) * 48) + 250; ssize_t ret; struct stats_tx *tx, *accum_tx, *delta_tx, *max_tx; if (!il_is_alive(il)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } /* the statistic information display here is based on * the last stats notification from uCode * might not reflect the current uCode activity */ tx = &il->_4965.stats.tx; accum_tx = &il->_4965.accum_stats.tx; delta_tx = &il->_4965.delta_stats.tx; max_tx = &il->_4965.max_delta.tx; pos += il4965_stats_flag(il, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Tx:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "preamble:", le32_to_cpu(tx->preamble_cnt), accum_tx->preamble_cnt, delta_tx->preamble_cnt, max_tx->preamble_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_detected_cnt:", le32_to_cpu(tx->rx_detected_cnt), accum_tx->rx_detected_cnt, delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_defer_cnt:", le32_to_cpu(tx->bt_prio_defer_cnt), accum_tx->bt_prio_defer_cnt, delta_tx->bt_prio_defer_cnt, max_tx->bt_prio_defer_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_kill_cnt:", le32_to_cpu(tx->bt_prio_kill_cnt), accum_tx->bt_prio_kill_cnt, delta_tx->bt_prio_kill_cnt, max_tx->bt_prio_kill_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "few_bytes_cnt:", le32_to_cpu(tx->few_bytes_cnt), accum_tx->few_bytes_cnt, delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "cts_timeout:", le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, delta_tx->cts_timeout, max_tx->cts_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ack_timeout:", le32_to_cpu(tx->ack_timeout), accum_tx->ack_timeout, delta_tx->ack_timeout, max_tx->ack_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "expected_ack_cnt:", le32_to_cpu(tx->expected_ack_cnt), accum_tx->expected_ack_cnt, delta_tx->expected_ack_cnt, max_tx->expected_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "actual_ack_cnt:", le32_to_cpu(tx->actual_ack_cnt), accum_tx->actual_ack_cnt, delta_tx->actual_ack_cnt, max_tx->actual_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "dump_msdu_cnt:", le32_to_cpu(tx->dump_msdu_cnt), accum_tx->dump_msdu_cnt, delta_tx->dump_msdu_cnt, max_tx->dump_msdu_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "abort_nxt_frame_mismatch:", le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), accum_tx->burst_abort_next_frame_mismatch_cnt, delta_tx->burst_abort_next_frame_mismatch_cnt, max_tx->burst_abort_next_frame_mismatch_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "abort_missing_nxt_frame:", le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), accum_tx->burst_abort_missing_next_frame_cnt, delta_tx->burst_abort_missing_next_frame_cnt, max_tx->burst_abort_missing_next_frame_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "cts_timeout_collision:", le32_to_cpu(tx->cts_timeout_collision), accum_tx->cts_timeout_collision, delta_tx->cts_timeout_collision, max_tx->cts_timeout_collision); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "ack_ba_timeout_collision:", le32_to_cpu(tx->ack_or_ba_timeout_collision), accum_tx->ack_or_ba_timeout_collision, delta_tx->ack_or_ba_timeout_collision, max_tx->ack_or_ba_timeout_collision); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg ba_timeout:", le32_to_cpu(tx->agg.ba_timeout), accum_tx->agg.ba_timeout, delta_tx->agg.ba_timeout, max_tx->agg.ba_timeout); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg ba_resched_frames:", le32_to_cpu(tx->agg.ba_reschedule_frames), accum_tx->agg.ba_reschedule_frames, delta_tx->agg.ba_reschedule_frames, max_tx->agg.ba_reschedule_frames); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_agg_frame:", le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), accum_tx->agg.scd_query_agg_frame_cnt, delta_tx->agg.scd_query_agg_frame_cnt, max_tx->agg.scd_query_agg_frame_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_no_agg:", le32_to_cpu(tx->agg.scd_query_no_agg), accum_tx->agg.scd_query_no_agg, delta_tx->agg.scd_query_no_agg, max_tx->agg.scd_query_no_agg); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_agg:", le32_to_cpu(tx->agg.scd_query_agg), accum_tx->agg.scd_query_agg, delta_tx->agg.scd_query_agg, max_tx->agg.scd_query_agg); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_mismatch:", le32_to_cpu(tx->agg.scd_query_mismatch), accum_tx->agg.scd_query_mismatch, delta_tx->agg.scd_query_mismatch, max_tx->agg.scd_query_mismatch); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg frame_not_ready:", le32_to_cpu(tx->agg.frame_not_ready), accum_tx->agg.frame_not_ready, delta_tx->agg.frame_not_ready, max_tx->agg.frame_not_ready); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg underrun:", le32_to_cpu(tx->agg.underrun), accum_tx->agg.underrun, delta_tx->agg.underrun, max_tx->agg.underrun); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg bt_prio_kill:", le32_to_cpu(tx->agg.bt_prio_kill), accum_tx->agg.bt_prio_kill, delta_tx->agg.bt_prio_kill, max_tx->agg.bt_prio_kill); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "agg rx_ba_rsp_cnt:", le32_to_cpu(tx->agg.rx_ba_rsp_cnt), accum_tx->agg.rx_ba_rsp_cnt, delta_tx->agg.rx_ba_rsp_cnt, max_tx->agg.rx_ba_rsp_cnt); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } ssize_t il4965_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct stats_general) * 10 + 300; ssize_t ret; struct stats_general_common *general, *accum_general; struct stats_general_common *delta_general, *max_general; struct stats_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; struct stats_div *div, *accum_div, *delta_div, *max_div; if (!il_is_alive(il)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } /* the statistic information display here is based on * the last stats notification from uCode * might not reflect the current uCode activity */ general = &il->_4965.stats.general.common; dbg = &il->_4965.stats.general.common.dbg; div = &il->_4965.stats.general.common.div; accum_general = &il->_4965.accum_stats.general.common; accum_dbg = &il->_4965.accum_stats.general.common.dbg; accum_div = &il->_4965.accum_stats.general.common.div; delta_general = &il->_4965.delta_stats.general.common; max_general = &il->_4965.max_delta.general.common; delta_dbg = &il->_4965.delta_stats.general.common.dbg; max_dbg = &il->_4965.max_delta.general.common.dbg; delta_div = &il->_4965.delta_stats.general.common.div; max_div = &il->_4965.max_delta.general.common.div; pos += il4965_stats_flag(il, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_General:"); pos += scnprintf(buf + pos, bufsz - pos, fmt_value, "temperature:", le32_to_cpu(general->temperature)); pos += scnprintf(buf + pos, bufsz - pos, fmt_value, "ttl_timestamp:", le32_to_cpu(general->ttl_timestamp)); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_check:", le32_to_cpu(dbg->burst_check), accum_dbg->burst_check, delta_dbg->burst_check, max_dbg->burst_check); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_count:", le32_to_cpu(dbg->burst_count), accum_dbg->burst_count, delta_dbg->burst_count, max_dbg->burst_count); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "wait_for_silence_timeout_count:", le32_to_cpu(dbg->wait_for_silence_timeout_cnt), accum_dbg->wait_for_silence_timeout_cnt, delta_dbg->wait_for_silence_timeout_cnt, max_dbg->wait_for_silence_timeout_cnt); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "sleep_time:", le32_to_cpu(general->sleep_time), accum_general->sleep_time, delta_general->sleep_time, max_general->sleep_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_out:", le32_to_cpu(general->slots_out), accum_general->slots_out, delta_general->slots_out, max_general->slots_out); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_idle:", le32_to_cpu(general->slots_idle), accum_general->slots_idle, delta_general->slots_idle, max_general->slots_idle); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_a:", le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, delta_div->tx_on_a, max_div->tx_on_a); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_b:", le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, delta_div->tx_on_b, max_div->tx_on_b); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "exec_time:", le32_to_cpu(div->exec_time), accum_div->exec_time, delta_div->exec_time, max_div->exec_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "probe_time:", le32_to_cpu(div->probe_time), accum_div->probe_time, delta_div->probe_time, max_div->probe_time); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_enable_counter:", le32_to_cpu(general->rx_enable_counter), accum_general->rx_enable_counter, delta_general->rx_enable_counter, max_general->rx_enable_counter); pos += scnprintf(buf + pos, bufsz - pos, fmt_table, "num_of_sos_states:", le32_to_cpu(general->num_of_sos_states), accum_general->num_of_sos_states, delta_general->num_of_sos_states, max_general->num_of_sos_states); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } const struct il_debugfs_ops il4965_debugfs_ops = { .rx_stats_read = il4965_ucode_rx_stats_read, .tx_stats_read = il4965_ucode_tx_stats_read, .general_stats_read = il4965_ucode_general_stats_read, }; compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/4965-calib.c0000644000175000017500000007401712026211315024121 0ustar mcgrofmcgrof/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include #include #include "common.h" #include "4965.h" /***************************************************************************** * INIT calibrations framework *****************************************************************************/ struct stats_general_data { u32 beacon_silence_rssi_a; u32 beacon_silence_rssi_b; u32 beacon_silence_rssi_c; u32 beacon_energy_a; u32 beacon_energy_b; u32 beacon_energy_c; }; /***************************************************************************** * RUNTIME calibrations framework *****************************************************************************/ /* "false alarms" are signals that our DSP tries to lock onto, * but then determines that they are either noise, or transmissions * from a distant wireless network (also "noise", really) that get * "stepped on" by stronger transmissions within our own network. * This algorithm attempts to set a sensitivity level that is high * enough to receive all of our own network traffic, but not so * high that our DSP gets too busy trying to lock onto non-network * activity/noise. */ static int il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time, struct stats_general_data *rx_info) { u32 max_nrg_cck = 0; int i = 0; u8 max_silence_rssi = 0; u32 silence_ref = 0; u8 silence_rssi_a = 0; u8 silence_rssi_b = 0; u8 silence_rssi_c = 0; u32 val; /* "false_alarms" values below are cross-multiplications to assess the * numbers of false alarms within the measured period of actual Rx * (Rx is off when we're txing), vs the min/max expected false alarms * (some should be expected if rx is sensitive enough) in a * hypothetical listening period of 200 time units (TU), 204.8 msec: * * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time * * */ u32 false_alarms = norm_fa * 200 * 1024; u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; struct il_sensitivity_data *data = NULL; const struct il_sensitivity_ranges *ranges = il->hw_params.sens; data = &(il->sensitivity_data); data->nrg_auto_corr_silence_diff = 0; /* Find max silence rssi among all 3 receivers. * This is background noise, which may include transmissions from other * networks, measured during silence before our network's beacon */ silence_rssi_a = (u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8); silence_rssi_b = (u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8); silence_rssi_c = (u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8); val = max(silence_rssi_b, silence_rssi_c); max_silence_rssi = max(silence_rssi_a, (u8) val); /* Store silence rssi in 20-beacon history table */ data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; data->nrg_silence_idx++; if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) data->nrg_silence_idx = 0; /* Find max silence rssi across 20 beacon history */ for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { val = data->nrg_silence_rssi[i]; silence_ref = max(silence_ref, val); } D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a, silence_rssi_b, silence_rssi_c, silence_ref); /* Find max rx energy (min value!) among all 3 receivers, * measured during beacon frame. * Save it in 10-beacon history table. */ i = data->nrg_energy_idx; val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); data->nrg_value[i] = min(rx_info->beacon_energy_a, val); data->nrg_energy_idx++; if (data->nrg_energy_idx >= 10) data->nrg_energy_idx = 0; /* Find min rx energy (max value) across 10 beacon history. * This is the minimum signal level that we want to receive well. * Add backoff (margin so we don't miss slightly lower energy frames). * This establishes an upper bound (min value) for energy threshold. */ max_nrg_cck = data->nrg_value[0]; for (i = 1; i < 10; i++) max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); max_nrg_cck += 6; D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", rx_info->beacon_energy_a, rx_info->beacon_energy_b, rx_info->beacon_energy_c, max_nrg_cck - 6); /* Count number of consecutive beacons with fewer-than-desired * false alarms. */ if (false_alarms < min_false_alarms) data->num_in_cck_no_fa++; else data->num_in_cck_no_fa = 0; D_CALIB("consecutive bcns with few false alarms = %u\n", data->num_in_cck_no_fa); /* If we got too many false alarms this time, reduce sensitivity */ if (false_alarms > max_false_alarms && data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) { D_CALIB("norm FA %u > max FA %u\n", false_alarms, max_false_alarms); D_CALIB("... reducing sensitivity\n"); data->nrg_curr_state = IL_FA_TOO_MANY; /* Store for "fewer than desired" on later beacon */ data->nrg_silence_ref = silence_ref; /* increase energy threshold (reduce nrg value) * to decrease sensitivity */ data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; /* Else if we got fewer than desired, increase sensitivity */ } else if (false_alarms < min_false_alarms) { data->nrg_curr_state = IL_FA_TOO_FEW; /* Compare silence level with silence level for most recent * healthy number or too many false alarms */ data->nrg_auto_corr_silence_diff = (s32) data->nrg_silence_ref - (s32) silence_ref; D_CALIB("norm FA %u < min FA %u, silence diff %d\n", false_alarms, min_false_alarms, data->nrg_auto_corr_silence_diff); /* Increase value to increase sensitivity, but only if: * 1a) previous beacon did *not* have *too many* false alarms * 1b) AND there's a significant difference in Rx levels * from a previous beacon with too many, or healthy # FAs * OR 2) We've seen a lot of beacons (100) with too few * false alarms */ if (data->nrg_prev_state != IL_FA_TOO_MANY && (data->nrg_auto_corr_silence_diff > NRG_DIFF || data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { D_CALIB("... increasing sensitivity\n"); /* Increase nrg value to increase sensitivity */ val = data->nrg_th_cck + NRG_STEP_CCK; data->nrg_th_cck = min((u32) ranges->min_nrg_cck, val); } else { D_CALIB("... but not changing sensitivity\n"); } /* Else we got a healthy number of false alarms, keep status quo */ } else { D_CALIB(" FA in safe zone\n"); data->nrg_curr_state = IL_FA_GOOD_RANGE; /* Store for use in "fewer than desired" with later beacon */ data->nrg_silence_ref = silence_ref; /* If previous beacon had too many false alarms, * give it some extra margin by reducing sensitivity again * (but don't go below measured energy of desired Rx) */ if (IL_FA_TOO_MANY == data->nrg_prev_state) { D_CALIB("... increasing margin\n"); if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) data->nrg_th_cck -= NRG_MARGIN; else data->nrg_th_cck = max_nrg_cck; } } /* Make sure the energy threshold does not go above the measured * energy of the desired Rx signals (reduced by backoff margin), * or else we might start missing Rx frames. * Lower value is higher energy, so we use max()! */ data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); D_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck); data->nrg_prev_state = data->nrg_curr_state; /* Auto-correlation CCK algorithm */ if (false_alarms > min_false_alarms) { /* increase auto_corr values to decrease sensitivity * so the DSP won't be disturbed by the noise */ if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; else { val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; data->auto_corr_cck = min((u32) ranges->auto_corr_max_cck, val); } val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; data->auto_corr_cck_mrc = min((u32) ranges->auto_corr_max_cck_mrc, val); } else if (false_alarms < min_false_alarms && (data->nrg_auto_corr_silence_diff > NRG_DIFF || data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { /* Decrease auto_corr values to increase sensitivity */ val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val); val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; data->auto_corr_cck_mrc = max((u32) ranges->auto_corr_min_cck_mrc, val); } return 0; } static int il4965_sens_auto_corr_ofdm(struct il_priv *il, u32 norm_fa, u32 rx_enable_time) { u32 val; u32 false_alarms = norm_fa * 200 * 1024; u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; struct il_sensitivity_data *data = NULL; const struct il_sensitivity_ranges *ranges = il->hw_params.sens; data = &(il->sensitivity_data); /* If we got too many false alarms this time, reduce sensitivity */ if (false_alarms > max_false_alarms) { D_CALIB("norm FA %u > max FA %u)\n", false_alarms, max_false_alarms); val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm = min((u32) ranges->auto_corr_max_ofdm, val); val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc = min((u32) ranges->auto_corr_max_ofdm_mrc, val); val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_x1 = min((u32) ranges->auto_corr_max_ofdm_x1, val); val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc_x1 = min((u32) ranges->auto_corr_max_ofdm_mrc_x1, val); } /* Else if we got fewer than desired, increase sensitivity */ else if (false_alarms < min_false_alarms) { D_CALIB("norm FA %u < min FA %u\n", false_alarms, min_false_alarms); val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm = max((u32) ranges->auto_corr_min_ofdm, val); val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc = max((u32) ranges->auto_corr_min_ofdm_mrc, val); val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_x1 = max((u32) ranges->auto_corr_min_ofdm_x1, val); val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; data->auto_corr_ofdm_mrc_x1 = max((u32) ranges->auto_corr_min_ofdm_mrc_x1, val); } else { D_CALIB("min FA %u < norm FA %u < max FA %u OK\n", min_false_alarms, false_alarms, max_false_alarms); } return 0; } static void il4965_prepare_legacy_sensitivity_tbl(struct il_priv *il, struct il_sensitivity_data *data, __le16 *tbl) { tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX] = cpu_to_le16((u16) data->auto_corr_ofdm); tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] = cpu_to_le16((u16) data->auto_corr_ofdm_mrc); tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX] = cpu_to_le16((u16) data->auto_corr_ofdm_x1); tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] = cpu_to_le16((u16) data->auto_corr_ofdm_mrc_x1); tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX] = cpu_to_le16((u16) data->auto_corr_cck); tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] = cpu_to_le16((u16) data->auto_corr_cck_mrc); tbl[HD_MIN_ENERGY_CCK_DET_IDX] = cpu_to_le16((u16) data->nrg_th_cck); tbl[HD_MIN_ENERGY_OFDM_DET_IDX] = cpu_to_le16((u16) data->nrg_th_ofdm); tbl[HD_BARKER_CORR_TH_ADD_MIN_IDX] = cpu_to_le16(data->barker_corr_th_min); tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX] = cpu_to_le16(data->barker_corr_th_min_mrc); tbl[HD_OFDM_ENERGY_TH_IN_IDX] = cpu_to_le16(data->nrg_th_cca); D_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, data->nrg_th_ofdm); D_CALIB("cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck, data->auto_corr_cck_mrc, data->nrg_th_cck); } /* Prepare a C_SENSITIVITY, send to uCode if values have changed */ static int il4965_sensitivity_write(struct il_priv *il) { struct il_sensitivity_cmd cmd; struct il_sensitivity_data *data = NULL; struct il_host_cmd cmd_out = { .id = C_SENSITIVITY, .len = sizeof(struct il_sensitivity_cmd), .flags = CMD_ASYNC, .data = &cmd, }; data = &(il->sensitivity_data); memset(&cmd, 0, sizeof(cmd)); il4965_prepare_legacy_sensitivity_tbl(il, data, &cmd.table[0]); /* Update uCode's "work" table, and copy it to DSP */ cmd.control = C_SENSITIVITY_CONTROL_WORK_TBL; /* Don't send command to uCode if nothing has changed */ if (!memcmp (&cmd.table[0], &(il->sensitivity_tbl[0]), sizeof(u16) * HD_TBL_SIZE)) { D_CALIB("No change in C_SENSITIVITY\n"); return 0; } /* Copy table for comparison next time */ memcpy(&(il->sensitivity_tbl[0]), &(cmd.table[0]), sizeof(u16) * HD_TBL_SIZE); return il_send_cmd(il, &cmd_out); } void il4965_init_sensitivity(struct il_priv *il) { int ret = 0; int i; struct il_sensitivity_data *data = NULL; const struct il_sensitivity_ranges *ranges = il->hw_params.sens; if (il->disable_sens_cal) return; D_CALIB("Start il4965_init_sensitivity\n"); /* Clear driver's sensitivity algo data */ data = &(il->sensitivity_data); if (ranges == NULL) return; memset(data, 0, sizeof(struct il_sensitivity_data)); data->num_in_cck_no_fa = 0; data->nrg_curr_state = IL_FA_TOO_MANY; data->nrg_prev_state = IL_FA_TOO_MANY; data->nrg_silence_ref = 0; data->nrg_silence_idx = 0; data->nrg_energy_idx = 0; for (i = 0; i < 10; i++) data->nrg_value[i] = 0; for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) data->nrg_silence_rssi[i] = 0; data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; data->nrg_th_cck = ranges->nrg_th_cck; data->nrg_th_ofdm = ranges->nrg_th_ofdm; data->barker_corr_th_min = ranges->barker_corr_th_min; data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; data->nrg_th_cca = ranges->nrg_th_cca; data->last_bad_plcp_cnt_ofdm = 0; data->last_fa_cnt_ofdm = 0; data->last_bad_plcp_cnt_cck = 0; data->last_fa_cnt_cck = 0; ret |= il4965_sensitivity_write(il); D_CALIB("<disable_sens_cal) return; data = &(il->sensitivity_data); if (!il_is_any_associated(il)) { D_CALIB("<< - not associated\n"); return; } spin_lock_irqsave(&il->lock, flags); rx_info = &(((struct il_notif_stats *)resp)->rx.general); ofdm = &(((struct il_notif_stats *)resp)->rx.ofdm); cck = &(((struct il_notif_stats *)resp)->rx.cck); if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { D_CALIB("<< invalid data.\n"); spin_unlock_irqrestore(&il->lock, flags); return; } /* Extract Statistics: */ rx_enable_time = le32_to_cpu(rx_info->channel_load); fa_cck = le32_to_cpu(cck->false_alarm_cnt); fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); bad_plcp_cck = le32_to_cpu(cck->plcp_err); bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); statis.beacon_silence_rssi_a = le32_to_cpu(rx_info->beacon_silence_rssi_a); statis.beacon_silence_rssi_b = le32_to_cpu(rx_info->beacon_silence_rssi_b); statis.beacon_silence_rssi_c = le32_to_cpu(rx_info->beacon_silence_rssi_c); statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a); statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b); statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c); spin_unlock_irqrestore(&il->lock, flags); D_CALIB("rx_enable_time = %u usecs\n", rx_enable_time); if (!rx_enable_time) { D_CALIB("<< RX Enable Time == 0!\n"); return; } /* These stats increase monotonically, and do not reset * at each beacon. Calculate difference from last value, or just * use the new stats value if it has reset or wrapped around. */ if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) data->last_bad_plcp_cnt_cck = bad_plcp_cck; else { bad_plcp_cck -= data->last_bad_plcp_cnt_cck; data->last_bad_plcp_cnt_cck += bad_plcp_cck; } if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; else { bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; } if (data->last_fa_cnt_ofdm > fa_ofdm) data->last_fa_cnt_ofdm = fa_ofdm; else { fa_ofdm -= data->last_fa_cnt_ofdm; data->last_fa_cnt_ofdm += fa_ofdm; } if (data->last_fa_cnt_cck > fa_cck) data->last_fa_cnt_cck = fa_cck; else { fa_cck -= data->last_fa_cnt_cck; data->last_fa_cnt_cck += fa_cck; } /* Total aborted signal locks */ norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; norm_fa_cck = fa_cck + bad_plcp_cck; D_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); il4965_sens_auto_corr_ofdm(il, norm_fa_ofdm, rx_enable_time); il4965_sens_energy_cck(il, norm_fa_cck, rx_enable_time, &statis); il4965_sensitivity_write(il); } static inline u8 il4965_find_first_chain(u8 mask) { if (mask & ANT_A) return CHAIN_A; if (mask & ANT_B) return CHAIN_B; return CHAIN_C; } /** * Run disconnected antenna algorithm to find out which antennas are * disconnected. */ static void il4965_find_disconn_antenna(struct il_priv *il, u32 * average_sig, struct il_chain_noise_data *data) { u32 active_chains = 0; u32 max_average_sig; u16 max_average_sig_antenna_i; u8 num_tx_chains; u8 first_chain; u16 i = 0; average_sig[0] = data->chain_signal_a / il->cfg->chain_noise_num_beacons; average_sig[1] = data->chain_signal_b / il->cfg->chain_noise_num_beacons; average_sig[2] = data->chain_signal_c / il->cfg->chain_noise_num_beacons; if (average_sig[0] >= average_sig[1]) { max_average_sig = average_sig[0]; max_average_sig_antenna_i = 0; active_chains = (1 << max_average_sig_antenna_i); } else { max_average_sig = average_sig[1]; max_average_sig_antenna_i = 1; active_chains = (1 << max_average_sig_antenna_i); } if (average_sig[2] >= max_average_sig) { max_average_sig = average_sig[2]; max_average_sig_antenna_i = 2; active_chains = (1 << max_average_sig_antenna_i); } D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1], average_sig[2]); D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig, max_average_sig_antenna_i); /* Compare signal strengths for all 3 receivers. */ for (i = 0; i < NUM_RX_CHAINS; i++) { if (i != max_average_sig_antenna_i) { s32 rssi_delta = (max_average_sig - average_sig[i]); /* If signal is very weak, compared with * strongest, mark it as disconnected. */ if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) data->disconn_array[i] = 1; else active_chains |= (1 << i); D_CALIB("i = %d rssiDelta = %d " "disconn_array[i] = %d\n", i, rssi_delta, data->disconn_array[i]); } } /* * The above algorithm sometimes fails when the ucode * reports 0 for all chains. It's not clear why that * happens to start with, but it is then causing trouble * because this can make us enable more chains than the * hardware really has. * * To be safe, simply mask out any chains that we know * are not on the device. */ active_chains &= il->hw_params.valid_rx_ant; num_tx_chains = 0; for (i = 0; i < NUM_RX_CHAINS; i++) { /* loops on all the bits of * il->hw_setting.valid_tx_ant */ u8 ant_msk = (1 << i); if (!(il->hw_params.valid_tx_ant & ant_msk)) continue; num_tx_chains++; if (data->disconn_array[i] == 0) /* there is a Tx antenna connected */ break; if (num_tx_chains == il->hw_params.tx_chains_num && data->disconn_array[i]) { /* * If all chains are disconnected * connect the first valid tx chain */ first_chain = il4965_find_first_chain(il->cfg->valid_tx_ant); data->disconn_array[first_chain] = 0; active_chains |= BIT(first_chain); D_CALIB("All Tx chains are disconnected" "- declare %d as connected\n", first_chain); break; } } if (active_chains != il->hw_params.valid_rx_ant && active_chains != il->chain_noise_data.active_chains) D_CALIB("Detected that not all antennas are connected! " "Connected: %#x, valid: %#x.\n", active_chains, il->hw_params.valid_rx_ant); /* Save for use within RXON, TX, SCAN commands, etc. */ data->active_chains = active_chains; D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains); } static void il4965_gain_computation(struct il_priv *il, u32 * average_noise, u16 min_average_noise_antenna_i, u32 min_average_noise, u8 default_chain) { int i, ret; struct il_chain_noise_data *data = &il->chain_noise_data; data->delta_gain_code[min_average_noise_antenna_i] = 0; for (i = default_chain; i < NUM_RX_CHAINS; i++) { s32 delta_g = 0; if (!data->disconn_array[i] && data->delta_gain_code[i] == CHAIN_NOISE_DELTA_GAIN_INIT_VAL) { delta_g = average_noise[i] - min_average_noise; data->delta_gain_code[i] = (u8) ((delta_g * 10) / 15); data->delta_gain_code[i] = min(data->delta_gain_code[i], (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); data->delta_gain_code[i] = (data->delta_gain_code[i] | (1 << 2)); } else { data->delta_gain_code[i] = 0; } } D_CALIB("delta_gain_codes: a %d b %d c %d\n", data->delta_gain_code[0], data->delta_gain_code[1], data->delta_gain_code[2]); /* Differential gain gets sent to uCode only once */ if (!data->radio_write) { struct il_calib_diff_gain_cmd cmd; data->radio_write = 1; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; cmd.diff_gain_a = data->delta_gain_code[0]; cmd.diff_gain_b = data->delta_gain_code[1]; cmd.diff_gain_c = data->delta_gain_code[2]; ret = il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd); if (ret) D_CALIB("fail sending cmd " "C_PHY_CALIBRATION\n"); /* TODO we might want recalculate * rx_chain in rxon cmd */ /* Mark so we run this algo only once! */ data->state = IL_CHAIN_NOISE_CALIBRATED; } } /* * Accumulate 16 beacons of signal and noise stats for each of * 3 receivers/antennas/rx-chains, then figure out: * 1) Which antennas are connected. * 2) Differential rx gain settings to balance the 3 receivers. */ void il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp) { struct il_chain_noise_data *data = NULL; u32 chain_noise_a; u32 chain_noise_b; u32 chain_noise_c; u32 chain_sig_a; u32 chain_sig_b; u32 chain_sig_c; u32 average_sig[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; u32 average_noise[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; u16 i = 0; u16 rxon_chnum = INITIALIZATION_VALUE; u16 stat_chnum = INITIALIZATION_VALUE; u8 rxon_band24; u8 stat_band24; unsigned long flags; struct stats_rx_non_phy *rx_info; if (il->disable_chain_noise_cal) return; data = &(il->chain_noise_data); /* * Accumulate just the first "chain_noise_num_beacons" after * the first association, then we're done forever. */ if (data->state != IL_CHAIN_NOISE_ACCUMULATE) { if (data->state == IL_CHAIN_NOISE_ALIVE) D_CALIB("Wait for noise calib reset\n"); return; } spin_lock_irqsave(&il->lock, flags); rx_info = &(((struct il_notif_stats *)stat_resp)->rx.general); if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { D_CALIB(" << Interference data unavailable\n"); spin_unlock_irqrestore(&il->lock, flags); return; } rxon_band24 = !!(il->staging.flags & RXON_FLG_BAND_24G_MSK); rxon_chnum = le16_to_cpu(il->staging.channel); stat_band24 = !!(((struct il_notif_stats *)stat_resp)-> flag & STATS_REPLY_FLG_BAND_24G_MSK); stat_chnum = le32_to_cpu(((struct il_notif_stats *)stat_resp)->flag) >> 16; /* Make sure we accumulate data for just the associated channel * (even if scanning). */ if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) { D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum, rxon_band24); spin_unlock_irqrestore(&il->lock, flags); return; } /* * Accumulate beacon stats values across * "chain_noise_num_beacons" */ chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; spin_unlock_irqrestore(&il->lock, flags); data->beacon_count++; data->chain_noise_a = (chain_noise_a + data->chain_noise_a); data->chain_noise_b = (chain_noise_b + data->chain_noise_b); data->chain_noise_c = (chain_noise_c + data->chain_noise_c); data->chain_signal_a = (chain_sig_a + data->chain_signal_a); data->chain_signal_b = (chain_sig_b + data->chain_signal_b); data->chain_signal_c = (chain_sig_c + data->chain_signal_c); D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24, data->beacon_count); D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b, chain_sig_c); D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b, chain_noise_c); /* If this is the "chain_noise_num_beacons", determine: * 1) Disconnected antennas (using signal strengths) * 2) Differential gain (using silence noise) to balance receivers */ if (data->beacon_count != il->cfg->chain_noise_num_beacons) return; /* Analyze signal for disconnected antenna */ il4965_find_disconn_antenna(il, average_sig, data); /* Analyze noise for rx balance */ average_noise[0] = data->chain_noise_a / il->cfg->chain_noise_num_beacons; average_noise[1] = data->chain_noise_b / il->cfg->chain_noise_num_beacons; average_noise[2] = data->chain_noise_c / il->cfg->chain_noise_num_beacons; for (i = 0; i < NUM_RX_CHAINS; i++) { if (!data->disconn_array[i] && average_noise[i] <= min_average_noise) { /* This means that chain i is active and has * lower noise values so far: */ min_average_noise = average_noise[i]; min_average_noise_antenna_i = i; } } D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0], average_noise[1], average_noise[2]); D_CALIB("min_average_noise = %d, antenna %d\n", min_average_noise, min_average_noise_antenna_i); il4965_gain_computation(il, average_noise, min_average_noise_antenna_i, min_average_noise, il4965_find_first_chain(il->cfg->valid_rx_ant)); /* Some power changes may have been made during the calibration. * Update and commit the RXON */ if (il->ops->update_chain_flags) il->ops->update_chain_flags(il); data->state = IL_CHAIN_NOISE_DONE; il_power_update_mode(il, false); } void il4965_reset_run_time_calib(struct il_priv *il) { int i; memset(&(il->sensitivity_data), 0, sizeof(struct il_sensitivity_data)); memset(&(il->chain_noise_data), 0, sizeof(struct il_chain_noise_data)); for (i = 0; i < NUM_RX_CHAINS; i++) il->chain_noise_data.delta_gain_code[i] = CHAIN_NOISE_DELTA_GAIN_INIT_VAL; /* Ask for stats now, the uCode will send notification * periodically after association */ il_send_stats_request(il, CMD_ASYNC, true); } compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/4965.c0000644000175000017500000014670112026211315023051 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "4965.h" /** * il_verify_inst_sparse - verify runtime uCode image in card vs. host, * using sample data 100 bytes apart. If these sample points are good, * it's a pretty good bet that everything between them is good, too. */ static int il4965_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) { u32 val; int ret = 0; u32 errcnt = 0; u32 i; D_INFO("ucode inst image size is %u\n", len); for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IL_DL_IO is set */ il_wr(il, HBUS_TARG_MEM_RADDR, i + IL4965_RTC_INST_LOWER_BOUND); val = _il_rd(il, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { ret = -EIO; errcnt++; if (errcnt >= 3) break; } } return ret; } /** * il4965_verify_inst_full - verify runtime uCode image in card vs. host, * looking at all data. */ static int il4965_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) { u32 val; u32 save_len = len; int ret = 0; u32 errcnt; D_INFO("ucode inst image size is %u\n", len); il_wr(il, HBUS_TARG_MEM_RADDR, IL4965_RTC_INST_LOWER_BOUND); errcnt = 0; for (; len > 0; len -= sizeof(u32), image++) { /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IL_DL_IO is set */ val = _il_rd(il, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { IL_ERR("uCode INST section is invalid at " "offset 0x%x, is 0x%x, s/b 0x%x\n", save_len - len, val, le32_to_cpu(*image)); ret = -EIO; errcnt++; if (errcnt >= 20) break; } } if (!errcnt) D_INFO("ucode image in INSTRUCTION memory is good\n"); return ret; } /** * il4965_verify_ucode - determine which instruction image is in SRAM, * and verify its contents */ int il4965_verify_ucode(struct il_priv *il) { __le32 *image; u32 len; int ret; /* Try bootstrap */ image = (__le32 *) il->ucode_boot.v_addr; len = il->ucode_boot.len; ret = il4965_verify_inst_sparse(il, image, len); if (!ret) { D_INFO("Bootstrap uCode is good in inst SRAM\n"); return 0; } /* Try initialize */ image = (__le32 *) il->ucode_init.v_addr; len = il->ucode_init.len; ret = il4965_verify_inst_sparse(il, image, len); if (!ret) { D_INFO("Initialize uCode is good in inst SRAM\n"); return 0; } /* Try runtime/protocol */ image = (__le32 *) il->ucode_code.v_addr; len = il->ucode_code.len; ret = il4965_verify_inst_sparse(il, image, len); if (!ret) { D_INFO("Runtime uCode is good in inst SRAM\n"); return 0; } IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); /* Since nothing seems to match, show first several data entries in * instruction SRAM, so maybe visual inspection will give a clue. * Selection of bootstrap image (vs. other images) is arbitrary. */ image = (__le32 *) il->ucode_boot.v_addr; len = il->ucode_boot.len; ret = il4965_verify_inst_full(il, image, len); return ret; } /****************************************************************************** * * EEPROM related functions * ******************************************************************************/ /* * The device's EEPROM semaphore prevents conflicts between driver and uCode * when accessing the EEPROM; each access is a series of pulses to/from the * EEPROM chip, not a single event, so even reads could conflict if they * weren't arbitrated by the semaphore. */ int il4965_eeprom_acquire_semaphore(struct il_priv *il) { u16 count; int ret; for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { /* Request semaphore */ il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); /* See if we got it */ ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, EEPROM_SEM_TIMEOUT); if (ret >= 0) return ret; } return ret; } void il4965_eeprom_release_semaphore(struct il_priv *il) { il_clear_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); } int il4965_eeprom_check_version(struct il_priv *il) { u16 eeprom_ver; u16 calib_ver; eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); calib_ver = il_eeprom_query16(il, EEPROM_4965_CALIB_VERSION_OFFSET); if (eeprom_ver < il->cfg->eeprom_ver || calib_ver < il->cfg->eeprom_calib_ver) goto err; IL_INFO("device EEPROM VER=0x%x, CALIB=0x%x\n", eeprom_ver, calib_ver); return 0; err: IL_ERR("Unsupported (too old) EEPROM VER=0x%x < 0x%x " "CALIB=0x%x < 0x%x\n", eeprom_ver, il->cfg->eeprom_ver, calib_ver, il->cfg->eeprom_calib_ver); return -EINVAL; } void il4965_eeprom_get_mac(const struct il_priv *il, u8 * mac) { const u8 *addr = il_eeprom_query_addr(il, EEPROM_MAC_ADDRESS); memcpy(mac, addr, ETH_ALEN); } /* Send led command */ static int il4965_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) { struct il_host_cmd cmd = { .id = C_LEDS, .len = sizeof(struct il_led_cmd), .data = led_cmd, .flags = CMD_ASYNC, .callback = NULL, }; u32 reg; reg = _il_rd(il, CSR_LED_REG); if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) _il_wr(il, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); return il_send_cmd(il, &cmd); } /* Set led register off */ void il4965_led_enable(struct il_priv *il) { _il_wr(il, CSR_LED_REG, CSR_LED_REG_TRUN_ON); } static int il4965_send_tx_power(struct il_priv *il); static int il4965_hw_get_temperature(struct il_priv *il); /* Highest firmware API version supported */ #define IL4965_UCODE_API_MAX 2 /* Lowest firmware API version supported */ #define IL4965_UCODE_API_MIN 2 #define IL4965_FW_PRE "iwlwifi-4965-" #define _IL4965_MODULE_FIRMWARE(api) IL4965_FW_PRE #api ".ucode" #define IL4965_MODULE_FIRMWARE(api) _IL4965_MODULE_FIRMWARE(api) /* check contents of special bootstrap uCode SRAM */ static int il4965_verify_bsm(struct il_priv *il) { __le32 *image = il->ucode_boot.v_addr; u32 len = il->ucode_boot.len; u32 reg; u32 val; D_INFO("Begin verify bsm\n"); /* verify BSM SRAM contents */ val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; reg += sizeof(u32), image++) { val = il_rd_prph(il, reg); if (val != le32_to_cpu(*image)) { IL_ERR("BSM uCode verification failed at " "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, len, val, le32_to_cpu(*image)); return -EIO; } } D_INFO("BSM bootstrap uCode image OK\n"); return 0; } /** * il4965_load_bsm - Load bootstrap instructions * * BSM operation: * * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program * in special SRAM that does not power down during RFKILL. When powering back * up after power-saving sleeps (or during initial uCode load), the BSM loads * the bootstrap program into the on-board processor, and starts it. * * The bootstrap program loads (via DMA) instructions and data for a new * program from host DRAM locations indicated by the host driver in the * BSM_DRAM_* registers. Once the new program is loaded, it starts * automatically. * * When initializing the NIC, the host driver points the BSM to the * "initialize" uCode image. This uCode sets up some internal data, then * notifies host via "initialize alive" that it is complete. * * The host then replaces the BSM_DRAM_* pointer values to point to the * normal runtime uCode instructions and a backup uCode data cache buffer * (filled initially with starting data values for the on-board processor), * then triggers the "initialize" uCode to load and launch the runtime uCode, * which begins normal operation. * * When doing a power-save shutdown, runtime uCode saves data SRAM into * the backup data cache in DRAM before SRAM is powered down. * * When powering back up, the BSM loads the bootstrap program. This reloads * the runtime uCode instructions and the backup data cache into SRAM, * and re-launches the runtime uCode from where it left off. */ static int il4965_load_bsm(struct il_priv *il) { __le32 *image = il->ucode_boot.v_addr; u32 len = il->ucode_boot.len; dma_addr_t pinst; dma_addr_t pdata; u32 inst_len; u32 data_len; int i; u32 done; u32 reg_offset; int ret; D_INFO("Begin load bsm\n"); il->ucode_type = UCODE_RT; /* make sure bootstrap program is no larger than BSM's SRAM size */ if (len > IL49_MAX_BSM_SIZE) return -EINVAL; /* Tell bootstrap uCode where to find the "Initialize" uCode * in host DRAM ... host DRAM physical address bits 35:4 for 4965. * NOTE: il_init_alive_start() will replace these values, * after the "initialize" uCode has run, to point to * runtime/protocol instructions and backup data cache. */ pinst = il->ucode_init.p_addr >> 4; pdata = il->ucode_init_data.p_addr >> 4; inst_len = il->ucode_init.len; data_len = il->ucode_init_data.len; il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); /* Fill BSM memory with bootstrap instructions */ for (reg_offset = BSM_SRAM_LOWER_BOUND; reg_offset < BSM_SRAM_LOWER_BOUND + len; reg_offset += sizeof(u32), image++) _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); ret = il4965_verify_bsm(il); if (ret) return ret; /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); il_wr_prph(il, BSM_WR_MEM_DST_REG, IL49_RTC_INST_LOWER_BOUND); il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); /* Load bootstrap code into instruction SRAM now, * to prepare to load "initialize" uCode */ il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); /* Wait for load of bootstrap uCode to finish */ for (i = 0; i < 100; i++) { done = il_rd_prph(il, BSM_WR_CTRL_REG); if (!(done & BSM_WR_CTRL_REG_BIT_START)) break; udelay(10); } if (i < 100) D_INFO("BSM write complete, poll %d iterations\n", i); else { IL_ERR("BSM write did not complete!\n"); return -EIO; } /* Enable future boot loads whenever power management unit triggers it * (e.g. when powering back up after power-save shutdown) */ il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); return 0; } /** * il4965_set_ucode_ptrs - Set uCode address location * * Tell initialization uCode where to find runtime uCode. * * BSM registers initially contain pointers to initialization uCode. * We need to replace them to load runtime uCode inst and data, * and to save runtime data when powering down. */ static int il4965_set_ucode_ptrs(struct il_priv *il) { dma_addr_t pinst; dma_addr_t pdata; int ret = 0; /* bits 35:4 for 4965 */ pinst = il->ucode_code.p_addr >> 4; pdata = il->ucode_data_backup.p_addr >> 4; /* Tell bootstrap uCode where to find image to load */ il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); /* Inst byte count must be last to set up, bit 31 signals uCode * that all new ptr/size info is in place */ il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, il->ucode_code.len | BSM_DRAM_INST_LOAD); D_INFO("Runtime uCode pointers are set.\n"); return ret; } /** * il4965_init_alive_start - Called after N_ALIVE notification received * * Called after N_ALIVE notification received from "initialize" uCode. * * The 4965 "initialize" ALIVE reply contains calibration data for: * Voltage, temperature, and MIMO tx gain correction, now stored in il * (3945 does not contain this data). * * Tell "initialize" uCode to go ahead and load the runtime uCode. */ static void il4965_init_alive_start(struct il_priv *il) { /* Bootstrap uCode has loaded initialize uCode ... verify inst image. * This is a paranoid check, because we would not have gotten the * "initialize" alive if code weren't properly loaded. */ if (il4965_verify_ucode(il)) { /* Runtime instruction load was bad; * take it all the way back down so we can try again */ D_INFO("Bad \"initialize\" uCode load.\n"); goto restart; } /* Calculate temperature */ il->temperature = il4965_hw_get_temperature(il); /* Send pointers to protocol/runtime uCode image ... init code will * load and launch runtime uCode, which will send us another "Alive" * notification. */ D_INFO("Initialization Alive received.\n"); if (il4965_set_ucode_ptrs(il)) { /* Runtime instruction load won't happen; * take it all the way back down so we can try again */ D_INFO("Couldn't set up uCode pointers.\n"); goto restart; } return; restart: queue_work(il->workqueue, &il->restart); } static bool iw4965_is_ht40_channel(__le32 rxon_flags) { int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; return (chan_mod == CHANNEL_MODE_PURE_40 || chan_mod == CHANNEL_MODE_MIXED); } void il4965_nic_config(struct il_priv *il) { unsigned long flags; u16 radio_cfg; spin_lock_irqsave(&il->lock, flags); radio_cfg = il_eeprom_query16(il, EEPROM_RADIO_CONFIG); /* write radio config values to register */ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) == EEPROM_4965_RF_CFG_TYPE_MAX) il_set_bit(il, CSR_HW_IF_CONFIG_REG, EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | EEPROM_RF_CFG_STEP_MSK(radio_cfg) | EEPROM_RF_CFG_DASH_MSK(radio_cfg)); /* set CSR_HW_CONFIG_REG for uCode use */ il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); il->calib_info = (struct il_eeprom_calib_info *) il_eeprom_query_addr(il, EEPROM_4965_CALIB_TXPOWER_OFFSET); spin_unlock_irqrestore(&il->lock, flags); } /* Reset differential Rx gains in NIC to prepare for chain noise calibration. * Called after every association, but this runs only once! * ... once chain noise is calibrated the first time, it's good forever. */ static void il4965_chain_noise_reset(struct il_priv *il) { struct il_chain_noise_data *data = &(il->chain_noise_data); if (data->state == IL_CHAIN_NOISE_ALIVE && il_is_any_associated(il)) { struct il_calib_diff_gain_cmd cmd; /* clear data for chain noise calibration algorithm */ data->chain_noise_a = 0; data->chain_noise_b = 0; data->chain_noise_c = 0; data->chain_signal_a = 0; data->chain_signal_b = 0; data->chain_signal_c = 0; data->beacon_count = 0; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; cmd.diff_gain_a = 0; cmd.diff_gain_b = 0; cmd.diff_gain_c = 0; if (il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd)) IL_ERR("Could not send C_PHY_CALIBRATION\n"); data->state = IL_CHAIN_NOISE_ACCUMULATE; D_CALIB("Run chain_noise_calibrate\n"); } } static s32 il4965_math_div_round(s32 num, s32 denom, s32 * res) { s32 sign = 1; if (num < 0) { sign = -sign; num = -num; } if (denom < 0) { sign = -sign; denom = -denom; } *res = 1; *res = ((num * 2 + denom) / (denom * 2)) * sign; return 1; } /** * il4965_get_voltage_compensation - Power supply voltage comp for txpower * * Determines power supply voltage compensation for txpower calculations. * Returns number of 1/2-dB steps to subtract from gain table idx, * to compensate for difference between power supply voltage during * factory measurements, vs. current power supply voltage. * * Voltage indication is higher for lower voltage. * Lower voltage requires more gain (lower gain table idx). */ static s32 il4965_get_voltage_compensation(s32 eeprom_voltage, s32 current_voltage) { s32 comp = 0; if (TX_POWER_IL_ILLEGAL_VOLTAGE == eeprom_voltage || TX_POWER_IL_ILLEGAL_VOLTAGE == current_voltage) return 0; il4965_math_div_round(current_voltage - eeprom_voltage, TX_POWER_IL_VOLTAGE_CODES_PER_03V, &comp); if (current_voltage > eeprom_voltage) comp *= 2; if ((comp < -2) || (comp > 2)) comp = 0; return comp; } static s32 il4965_get_tx_atten_grp(u16 channel) { if (channel >= CALIB_IL_TX_ATTEN_GR5_FCH && channel <= CALIB_IL_TX_ATTEN_GR5_LCH) return CALIB_CH_GROUP_5; if (channel >= CALIB_IL_TX_ATTEN_GR1_FCH && channel <= CALIB_IL_TX_ATTEN_GR1_LCH) return CALIB_CH_GROUP_1; if (channel >= CALIB_IL_TX_ATTEN_GR2_FCH && channel <= CALIB_IL_TX_ATTEN_GR2_LCH) return CALIB_CH_GROUP_2; if (channel >= CALIB_IL_TX_ATTEN_GR3_FCH && channel <= CALIB_IL_TX_ATTEN_GR3_LCH) return CALIB_CH_GROUP_3; if (channel >= CALIB_IL_TX_ATTEN_GR4_FCH && channel <= CALIB_IL_TX_ATTEN_GR4_LCH) return CALIB_CH_GROUP_4; return -EINVAL; } static u32 il4965_get_sub_band(const struct il_priv *il, u32 channel) { s32 b = -1; for (b = 0; b < EEPROM_TX_POWER_BANDS; b++) { if (il->calib_info->band_info[b].ch_from == 0) continue; if (channel >= il->calib_info->band_info[b].ch_from && channel <= il->calib_info->band_info[b].ch_to) break; } return b; } static s32 il4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) { s32 val; if (x2 == x1) return y1; else { il4965_math_div_round((x2 - x) * (y1 - y2), (x2 - x1), &val); return val + y2; } } /** * il4965_interpolate_chan - Interpolate factory measurements for one channel * * Interpolates factory measurements from the two sample channels within a * sub-band, to apply to channel of interest. Interpolation is proportional to * differences in channel frequencies, which is proportional to differences * in channel number. */ static int il4965_interpolate_chan(struct il_priv *il, u32 channel, struct il_eeprom_calib_ch_info *chan_info) { s32 s = -1; u32 c; u32 m; const struct il_eeprom_calib_measure *m1; const struct il_eeprom_calib_measure *m2; struct il_eeprom_calib_measure *omeas; u32 ch_i1; u32 ch_i2; s = il4965_get_sub_band(il, channel); if (s >= EEPROM_TX_POWER_BANDS) { IL_ERR("Tx Power can not find channel %d\n", channel); return -1; } ch_i1 = il->calib_info->band_info[s].ch1.ch_num; ch_i2 = il->calib_info->band_info[s].ch2.ch_num; chan_info->ch_num = (u8) channel; D_TXPOWER("channel %d subband %d factory cal ch %d & %d\n", channel, s, ch_i1, ch_i2); for (c = 0; c < EEPROM_TX_POWER_TX_CHAINS; c++) { for (m = 0; m < EEPROM_TX_POWER_MEASUREMENTS; m++) { m1 = &(il->calib_info->band_info[s].ch1. measurements[c][m]); m2 = &(il->calib_info->band_info[s].ch2. measurements[c][m]); omeas = &(chan_info->measurements[c][m]); omeas->actual_pow = (u8) il4965_interpolate_value(channel, ch_i1, m1->actual_pow, ch_i2, m2->actual_pow); omeas->gain_idx = (u8) il4965_interpolate_value(channel, ch_i1, m1->gain_idx, ch_i2, m2->gain_idx); omeas->temperature = (u8) il4965_interpolate_value(channel, ch_i1, m1->temperature, ch_i2, m2->temperature); omeas->pa_det = (s8) il4965_interpolate_value(channel, ch_i1, m1->pa_det, ch_i2, m2->pa_det); D_TXPOWER("chain %d meas %d AP1=%d AP2=%d AP=%d\n", c, m, m1->actual_pow, m2->actual_pow, omeas->actual_pow); D_TXPOWER("chain %d meas %d NI1=%d NI2=%d NI=%d\n", c, m, m1->gain_idx, m2->gain_idx, omeas->gain_idx); D_TXPOWER("chain %d meas %d PA1=%d PA2=%d PA=%d\n", c, m, m1->pa_det, m2->pa_det, omeas->pa_det); D_TXPOWER("chain %d meas %d T1=%d T2=%d T=%d\n", c, m, m1->temperature, m2->temperature, omeas->temperature); } } return 0; } /* bit-rate-dependent table to prevent Tx distortion, in half-dB units, * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. */ static s32 back_off_table[] = { 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 20 MHz */ 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 20 MHz */ 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 40 MHz */ 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 40 MHz */ 10 /* CCK */ }; /* Thermal compensation values for txpower for various frequency ranges ... * ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust */ static struct il4965_txpower_comp_entry { s32 degrees_per_05db_a; s32 degrees_per_05db_a_denom; } tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = { { 9, 2}, /* group 0 5.2, ch 34-43 */ { 4, 1}, /* group 1 5.2, ch 44-70 */ { 4, 1}, /* group 2 5.2, ch 71-124 */ { 4, 1}, /* group 3 5.2, ch 125-200 */ { 3, 1} /* group 4 2.4, ch all */ }; static s32 get_min_power_idx(s32 rate_power_idx, u32 band) { if (!band) { if ((rate_power_idx & 7) <= 4) return MIN_TX_GAIN_IDX_52GHZ_EXT; } return MIN_TX_GAIN_IDX; } struct gain_entry { u8 dsp; u8 radio; }; static const struct gain_entry gain_table[2][108] = { /* 5.2GHz power gain idx table */ { {123, 0x3F}, /* highest txpower */ {117, 0x3F}, {110, 0x3F}, {104, 0x3F}, {98, 0x3F}, {110, 0x3E}, {104, 0x3E}, {98, 0x3E}, {110, 0x3D}, {104, 0x3D}, {98, 0x3D}, {110, 0x3C}, {104, 0x3C}, {98, 0x3C}, {110, 0x3B}, {104, 0x3B}, {98, 0x3B}, {110, 0x3A}, {104, 0x3A}, {98, 0x3A}, {110, 0x39}, {104, 0x39}, {98, 0x39}, {110, 0x38}, {104, 0x38}, {98, 0x38}, {110, 0x37}, {104, 0x37}, {98, 0x37}, {110, 0x36}, {104, 0x36}, {98, 0x36}, {110, 0x35}, {104, 0x35}, {98, 0x35}, {110, 0x34}, {104, 0x34}, {98, 0x34}, {110, 0x33}, {104, 0x33}, {98, 0x33}, {110, 0x32}, {104, 0x32}, {98, 0x32}, {110, 0x31}, {104, 0x31}, {98, 0x31}, {110, 0x30}, {104, 0x30}, {98, 0x30}, {110, 0x25}, {104, 0x25}, {98, 0x25}, {110, 0x24}, {104, 0x24}, {98, 0x24}, {110, 0x23}, {104, 0x23}, {98, 0x23}, {110, 0x22}, {104, 0x18}, {98, 0x18}, {110, 0x17}, {104, 0x17}, {98, 0x17}, {110, 0x16}, {104, 0x16}, {98, 0x16}, {110, 0x15}, {104, 0x15}, {98, 0x15}, {110, 0x14}, {104, 0x14}, {98, 0x14}, {110, 0x13}, {104, 0x13}, {98, 0x13}, {110, 0x12}, {104, 0x08}, {98, 0x08}, {110, 0x07}, {104, 0x07}, {98, 0x07}, {110, 0x06}, {104, 0x06}, {98, 0x06}, {110, 0x05}, {104, 0x05}, {98, 0x05}, {110, 0x04}, {104, 0x04}, {98, 0x04}, {110, 0x03}, {104, 0x03}, {98, 0x03}, {110, 0x02}, {104, 0x02}, {98, 0x02}, {110, 0x01}, {104, 0x01}, {98, 0x01}, {110, 0x00}, {104, 0x00}, {98, 0x00}, {93, 0x00}, {88, 0x00}, {83, 0x00}, {78, 0x00}, }, /* 2.4GHz power gain idx table */ { {110, 0x3f}, /* highest txpower */ {104, 0x3f}, {98, 0x3f}, {110, 0x3e}, {104, 0x3e}, {98, 0x3e}, {110, 0x3d}, {104, 0x3d}, {98, 0x3d}, {110, 0x3c}, {104, 0x3c}, {98, 0x3c}, {110, 0x3b}, {104, 0x3b}, {98, 0x3b}, {110, 0x3a}, {104, 0x3a}, {98, 0x3a}, {110, 0x39}, {104, 0x39}, {98, 0x39}, {110, 0x38}, {104, 0x38}, {98, 0x38}, {110, 0x37}, {104, 0x37}, {98, 0x37}, {110, 0x36}, {104, 0x36}, {98, 0x36}, {110, 0x35}, {104, 0x35}, {98, 0x35}, {110, 0x34}, {104, 0x34}, {98, 0x34}, {110, 0x33}, {104, 0x33}, {98, 0x33}, {110, 0x32}, {104, 0x32}, {98, 0x32}, {110, 0x31}, {104, 0x31}, {98, 0x31}, {110, 0x30}, {104, 0x30}, {98, 0x30}, {110, 0x6}, {104, 0x6}, {98, 0x6}, {110, 0x5}, {104, 0x5}, {98, 0x5}, {110, 0x4}, {104, 0x4}, {98, 0x4}, {110, 0x3}, {104, 0x3}, {98, 0x3}, {110, 0x2}, {104, 0x2}, {98, 0x2}, {110, 0x1}, {104, 0x1}, {98, 0x1}, {110, 0x0}, {104, 0x0}, {98, 0x0}, {97, 0}, {96, 0}, {95, 0}, {94, 0}, {93, 0}, {92, 0}, {91, 0}, {90, 0}, {89, 0}, {88, 0}, {87, 0}, {86, 0}, {85, 0}, {84, 0}, {83, 0}, {82, 0}, {81, 0}, {80, 0}, {79, 0}, {78, 0}, {77, 0}, {76, 0}, {75, 0}, {74, 0}, {73, 0}, {72, 0}, {71, 0}, {70, 0}, {69, 0}, {68, 0}, {67, 0}, {66, 0}, {65, 0}, {64, 0}, {63, 0}, {62, 0}, {61, 0}, {60, 0}, {59, 0}, } }; static int il4965_fill_txpower_tbl(struct il_priv *il, u8 band, u16 channel, u8 is_ht40, u8 ctrl_chan_high, struct il4965_tx_power_db *tx_power_tbl) { u8 saturation_power; s32 target_power; s32 user_target_power; s32 power_limit; s32 current_temp; s32 reg_limit; s32 current_regulatory; s32 txatten_grp = CALIB_CH_GROUP_MAX; int i; int c; const struct il_channel_info *ch_info = NULL; struct il_eeprom_calib_ch_info ch_eeprom_info; const struct il_eeprom_calib_measure *measurement; s16 voltage; s32 init_voltage; s32 voltage_compensation; s32 degrees_per_05db_num; s32 degrees_per_05db_denom; s32 factory_temp; s32 temperature_comp[2]; s32 factory_gain_idx[2]; s32 factory_actual_pwr[2]; s32 power_idx; /* tx_power_user_lmt is in dBm, convert to half-dBm (half-dB units * are used for idxing into txpower table) */ user_target_power = 2 * il->tx_power_user_lmt; /* Get current (RXON) channel, band, width */ D_TXPOWER("chan %d band %d is_ht40 %d\n", channel, band, is_ht40); ch_info = il_get_channel_info(il, il->band, channel); if (!il_is_channel_valid(ch_info)) return -EINVAL; /* get txatten group, used to select 1) thermal txpower adjustment * and 2) mimo txpower balance between Tx chains. */ txatten_grp = il4965_get_tx_atten_grp(channel); if (txatten_grp < 0) { IL_ERR("Can't find txatten group for channel %d.\n", channel); return txatten_grp; } D_TXPOWER("channel %d belongs to txatten group %d\n", channel, txatten_grp); if (is_ht40) { if (ctrl_chan_high) channel -= 2; else channel += 2; } /* hardware txpower limits ... * saturation (clipping distortion) txpowers are in half-dBm */ if (band) saturation_power = il->calib_info->saturation_power24; else saturation_power = il->calib_info->saturation_power52; if (saturation_power < IL_TX_POWER_SATURATION_MIN || saturation_power > IL_TX_POWER_SATURATION_MAX) { if (band) saturation_power = IL_TX_POWER_DEFAULT_SATURATION_24; else saturation_power = IL_TX_POWER_DEFAULT_SATURATION_52; } /* regulatory txpower limits ... reg_limit values are in half-dBm, * max_power_avg values are in dBm, convert * 2 */ if (is_ht40) reg_limit = ch_info->ht40_max_power_avg * 2; else reg_limit = ch_info->max_power_avg * 2; if ((reg_limit < IL_TX_POWER_REGULATORY_MIN) || (reg_limit > IL_TX_POWER_REGULATORY_MAX)) { if (band) reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_24; else reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_52; } /* Interpolate txpower calibration values for this channel, * based on factory calibration tests on spaced channels. */ il4965_interpolate_chan(il, channel, &ch_eeprom_info); /* calculate tx gain adjustment based on power supply voltage */ voltage = le16_to_cpu(il->calib_info->voltage); init_voltage = (s32) le32_to_cpu(il->card_alive_init.voltage); voltage_compensation = il4965_get_voltage_compensation(voltage, init_voltage); D_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n", init_voltage, voltage, voltage_compensation); /* get current temperature (Celsius) */ current_temp = max(il->temperature, IL_TX_POWER_TEMPERATURE_MIN); current_temp = min(il->temperature, IL_TX_POWER_TEMPERATURE_MAX); current_temp = KELVIN_TO_CELSIUS(current_temp); /* select thermal txpower adjustment params, based on channel group * (same frequency group used for mimo txatten adjustment) */ degrees_per_05db_num = tx_power_cmp_tble[txatten_grp].degrees_per_05db_a; degrees_per_05db_denom = tx_power_cmp_tble[txatten_grp].degrees_per_05db_a_denom; /* get per-chain txpower values from factory measurements */ for (c = 0; c < 2; c++) { measurement = &ch_eeprom_info.measurements[c][1]; /* txgain adjustment (in half-dB steps) based on difference * between factory and current temperature */ factory_temp = measurement->temperature; il4965_math_div_round((current_temp - factory_temp) * degrees_per_05db_denom, degrees_per_05db_num, &temperature_comp[c]); factory_gain_idx[c] = measurement->gain_idx; factory_actual_pwr[c] = measurement->actual_pow; D_TXPOWER("chain = %d\n", c); D_TXPOWER("fctry tmp %d, " "curr tmp %d, comp %d steps\n", factory_temp, current_temp, temperature_comp[c]); D_TXPOWER("fctry idx %d, fctry pwr %d\n", factory_gain_idx[c], factory_actual_pwr[c]); } /* for each of 33 bit-rates (including 1 for CCK) */ for (i = 0; i < POWER_TBL_NUM_ENTRIES; i++) { u8 is_mimo_rate; union il4965_tx_power_dual_stream tx_power; /* for mimo, reduce each chain's txpower by half * (3dB, 6 steps), so total output power is regulatory * compliant. */ if (i & 0x8) { current_regulatory = reg_limit - IL_TX_POWER_MIMO_REGULATORY_COMPENSATION; is_mimo_rate = 1; } else { current_regulatory = reg_limit; is_mimo_rate = 0; } /* find txpower limit, either hardware or regulatory */ power_limit = saturation_power - back_off_table[i]; if (power_limit > current_regulatory) power_limit = current_regulatory; /* reduce user's txpower request if necessary * for this rate on this channel */ target_power = user_target_power; if (target_power > power_limit) target_power = power_limit; D_TXPOWER("rate %d sat %d reg %d usr %d tgt %d\n", i, saturation_power - back_off_table[i], current_regulatory, user_target_power, target_power); /* for each of 2 Tx chains (radio transmitters) */ for (c = 0; c < 2; c++) { s32 atten_value; if (is_mimo_rate) atten_value = (s32) le32_to_cpu(il->card_alive_init. tx_atten[txatten_grp][c]); else atten_value = 0; /* calculate idx; higher idx means lower txpower */ power_idx = (u8) (factory_gain_idx[c] - (target_power - factory_actual_pwr[c]) - temperature_comp[c] - voltage_compensation + atten_value); /* D_TXPOWER("calculated txpower idx %d\n", power_idx); */ if (power_idx < get_min_power_idx(i, band)) power_idx = get_min_power_idx(i, band); /* adjust 5 GHz idx to support negative idxes */ if (!band) power_idx += 9; /* CCK, rate 32, reduce txpower for CCK */ if (i == POWER_TBL_CCK_ENTRY) power_idx += IL_TX_POWER_CCK_COMPENSATION_C_STEP; /* stay within the table! */ if (power_idx > 107) { IL_WARN("txpower idx %d > 107\n", power_idx); power_idx = 107; } if (power_idx < 0) { IL_WARN("txpower idx %d < 0\n", power_idx); power_idx = 0; } /* fill txpower command for this rate/chain */ tx_power.s.radio_tx_gain[c] = gain_table[band][power_idx].radio; tx_power.s.dsp_predis_atten[c] = gain_table[band][power_idx].dsp; D_TXPOWER("chain %d mimo %d idx %d " "gain 0x%02x dsp %d\n", c, atten_value, power_idx, tx_power.s.radio_tx_gain[c], tx_power.s.dsp_predis_atten[c]); } /* for each chain */ tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw); } /* for each rate */ return 0; } /** * il4965_send_tx_power - Configure the TXPOWER level user limit * * Uses the active RXON for channel, band, and characteristics (ht40, high) * The power limit is taken from il->tx_power_user_lmt. */ static int il4965_send_tx_power(struct il_priv *il) { struct il4965_txpowertable_cmd cmd = { 0 }; int ret; u8 band = 0; bool is_ht40 = false; u8 ctrl_chan_high = 0; if (WARN_ONCE (test_bit(S_SCAN_HW, &il->status), "TX Power requested while scanning!\n")) return -EAGAIN; band = il->band == IEEE80211_BAND_2GHZ; is_ht40 = iw4965_is_ht40_channel(il->active.flags); if (is_ht40 && (il->active.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; cmd.band = band; cmd.channel = il->active.channel; ret = il4965_fill_txpower_tbl(il, band, le16_to_cpu(il->active.channel), is_ht40, ctrl_chan_high, &cmd.tx_power); if (ret) goto out; ret = il_send_cmd_pdu(il, C_TX_PWR_TBL, sizeof(cmd), &cmd); out: return ret; } static int il4965_send_rxon_assoc(struct il_priv *il) { int ret = 0; struct il4965_rxon_assoc_cmd rxon_assoc; const struct il_rxon_cmd *rxon1 = &il->staging; const struct il_rxon_cmd *rxon2 = &il->active; if (rxon1->flags == rxon2->flags && rxon1->filter_flags == rxon2->filter_flags && rxon1->cck_basic_rates == rxon2->cck_basic_rates && rxon1->ofdm_ht_single_stream_basic_rates == rxon2->ofdm_ht_single_stream_basic_rates && rxon1->ofdm_ht_dual_stream_basic_rates == rxon2->ofdm_ht_dual_stream_basic_rates && rxon1->rx_chain == rxon2->rx_chain && rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { D_INFO("Using current RXON_ASSOC. Not resending.\n"); return 0; } rxon_assoc.flags = il->staging.flags; rxon_assoc.filter_flags = il->staging.filter_flags; rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; rxon_assoc.reserved = 0; rxon_assoc.ofdm_ht_single_stream_basic_rates = il->staging.ofdm_ht_single_stream_basic_rates; rxon_assoc.ofdm_ht_dual_stream_basic_rates = il->staging.ofdm_ht_dual_stream_basic_rates; rxon_assoc.rx_chain_select_flags = il->staging.rx_chain; ret = il_send_cmd_pdu_async(il, C_RXON_ASSOC, sizeof(rxon_assoc), &rxon_assoc, NULL); return ret; } static int il4965_commit_rxon(struct il_priv *il) { /* cast away the const for active_rxon in this function */ struct il_rxon_cmd *active_rxon = (void *)&il->active; int ret; bool new_assoc = !!(il->staging.filter_flags & RXON_FILTER_ASSOC_MSK); if (!il_is_alive(il)) return -EBUSY; /* always get timestamp with Rx frame */ il->staging.flags |= RXON_FLG_TSF2HOST_MSK; ret = il_check_rxon_cmd(il); if (ret) { IL_ERR("Invalid RXON configuration. Not committing.\n"); return -EINVAL; } /* * receive commit_rxon request * abort any previous channel switch if still in process */ if (test_bit(S_CHANNEL_SWITCH_PENDING, &il->status) && il->switch_channel != il->staging.channel) { D_11H("abort channel switch on %d\n", le16_to_cpu(il->switch_channel)); il_chswitch_done(il, false); } /* If we don't need to send a full RXON, we can use * il_rxon_assoc_cmd which is used to reconfigure filter * and other flags for the current radio configuration. */ if (!il_full_rxon_required(il)) { ret = il_send_rxon_assoc(il); if (ret) { IL_ERR("Error setting RXON_ASSOC (%d)\n", ret); return ret; } memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); il_print_rx_config_cmd(il); /* * We do not commit tx power settings while channel changing, * do it now if tx power changed. */ il_set_tx_power(il, il->tx_power_next, false); return 0; } /* If we are currently associated and the new config requires * an RXON_ASSOC and the new config wants the associated mask enabled, * we must clear the associated from the active configuration * before we apply the new config */ if (il_is_associated(il) && new_assoc) { D_INFO("Toggling associated bit on current RXON\n"); active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; ret = il_send_cmd_pdu(il, C_RXON, sizeof(struct il_rxon_cmd), active_rxon); /* If the mask clearing failed then we set * active_rxon back to what it was previously */ if (ret) { active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; IL_ERR("Error clearing ASSOC_MSK (%d)\n", ret); return ret; } il_clear_ucode_stations(il); il_restore_stations(il); ret = il4965_restore_default_wep_keys(il); if (ret) { IL_ERR("Failed to restore WEP keys (%d)\n", ret); return ret; } } D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), le16_to_cpu(il->staging.channel), il->staging.bssid_addr); il_set_rxon_hwcrypto(il, !il->cfg->mod_params->sw_crypto); /* Apply the new configuration * RXON unassoc clears the station table in uCode so restoration of * stations is needed after it (the RXON command) completes */ if (!new_assoc) { ret = il_send_cmd_pdu(il, C_RXON, sizeof(struct il_rxon_cmd), &il->staging); if (ret) { IL_ERR("Error setting new RXON (%d)\n", ret); return ret; } D_INFO("Return from !new_assoc RXON.\n"); memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); il_clear_ucode_stations(il); il_restore_stations(il); ret = il4965_restore_default_wep_keys(il); if (ret) { IL_ERR("Failed to restore WEP keys (%d)\n", ret); return ret; } } if (new_assoc) { il->start_calib = 0; /* Apply the new configuration * RXON assoc doesn't clear the station table in uCode, */ ret = il_send_cmd_pdu(il, C_RXON, sizeof(struct il_rxon_cmd), &il->staging); if (ret) { IL_ERR("Error setting new RXON (%d)\n", ret); return ret; } memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); } il_print_rx_config_cmd(il); il4965_init_sensitivity(il); /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ ret = il_set_tx_power(il, il->tx_power_next, true); if (ret) { IL_ERR("Error sending TX power (%d)\n", ret); return ret; } return 0; } static int il4965_hw_channel_switch(struct il_priv *il, struct ieee80211_channel_switch *ch_switch) { int rc; u8 band = 0; bool is_ht40 = false; u8 ctrl_chan_high = 0; struct il4965_channel_switch_cmd cmd; const struct il_channel_info *ch_info; u32 switch_time_in_usec, ucode_switch_time; u16 ch; u32 tsf_low; u8 switch_count; u16 beacon_interval = le16_to_cpu(il->timing.beacon_interval); struct ieee80211_vif *vif = il->vif; band = (il->band == IEEE80211_BAND_2GHZ); if (WARN_ON_ONCE(vif == NULL)) return -EIO; is_ht40 = iw4965_is_ht40_channel(il->staging.flags); if (is_ht40 && (il->staging.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; cmd.band = band; cmd.expect_beacon = 0; ch = ch_switch->channel->hw_value; cmd.channel = cpu_to_le16(ch); cmd.rxon_flags = il->staging.flags; cmd.rxon_filter_flags = il->staging.filter_flags; switch_count = ch_switch->count; tsf_low = ch_switch->timestamp & 0x0ffffffff; /* * calculate the ucode channel switch time * adding TSF as one of the factor for when to switch */ if (il->ucode_beacon_time > tsf_low && beacon_interval) { if (switch_count > ((il->ucode_beacon_time - tsf_low) / beacon_interval)) { switch_count -= (il->ucode_beacon_time - tsf_low) / beacon_interval; } else switch_count = 0; } if (switch_count <= 1) cmd.switch_time = cpu_to_le32(il->ucode_beacon_time); else { switch_time_in_usec = vif->bss_conf.beacon_int * switch_count * TIME_UNIT; ucode_switch_time = il_usecs_to_beacons(il, switch_time_in_usec, beacon_interval); cmd.switch_time = il_add_beacon_time(il, il->ucode_beacon_time, ucode_switch_time, beacon_interval); } D_11H("uCode time for the switch is 0x%x\n", cmd.switch_time); ch_info = il_get_channel_info(il, il->band, ch); if (ch_info) cmd.expect_beacon = il_is_channel_radar(ch_info); else { IL_ERR("invalid channel switch from %u to %u\n", il->active.channel, ch); return -EFAULT; } rc = il4965_fill_txpower_tbl(il, band, ch, is_ht40, ctrl_chan_high, &cmd.tx_power); if (rc) { D_11H("error:%d fill txpower_tbl\n", rc); return rc; } return il_send_cmd_pdu(il, C_CHANNEL_SWITCH, sizeof(cmd), &cmd); } /** * il4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array */ static void il4965_txq_update_byte_cnt_tbl(struct il_priv *il, struct il_tx_queue *txq, u16 byte_cnt) { struct il4965_scd_bc_tbl *scd_bc_tbl = il->scd_bc_tbls.addr; int txq_id = txq->q.id; int write_ptr = txq->q.write_ptr; int len = byte_cnt + IL_TX_CRC_SIZE + IL_TX_DELIMITER_SIZE; __le16 bc_ent; WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); bc_ent = cpu_to_le16(len & 0xFFF); /* Set up byte count within first 256 entries */ scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; /* If within first 64 entries, duplicate at end */ if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } /** * il4965_hw_get_temperature - return the calibrated temperature (in Kelvin) * @stats: Provides the temperature reading from the uCode * * A return of <0 indicates bogus data in the stats */ static int il4965_hw_get_temperature(struct il_priv *il) { s32 temperature; s32 vt; s32 R1, R2, R3; u32 R4; if (test_bit(S_TEMPERATURE, &il->status) && (il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)) { D_TEMP("Running HT40 temperature calibration\n"); R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[1]); R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[1]); R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[1]); R4 = le32_to_cpu(il->card_alive_init.therm_r4[1]); } else { D_TEMP("Running temperature calibration\n"); R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[0]); R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[0]); R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[0]); R4 = le32_to_cpu(il->card_alive_init.therm_r4[0]); } /* * Temperature is only 23 bits, so sign extend out to 32. * * NOTE If we haven't received a stats notification yet * with an updated temperature, use R4 provided to us in the * "initialize" ALIVE response. */ if (!test_bit(S_TEMPERATURE, &il->status)) vt = sign_extend32(R4, 23); else vt = sign_extend32(le32_to_cpu (il->_4965.stats.general.common.temperature), 23); D_TEMP("Calib values R[1-3]: %d %d %d R4: %d\n", R1, R2, R3, vt); if (R3 == R1) { IL_ERR("Calibration conflict R1 == R3\n"); return -1; } /* Calculate temperature in degrees Kelvin, adjust by 97%. * Add offset to center the adjustment around 0 degrees Centigrade. */ temperature = TEMPERATURE_CALIB_A_VAL * (vt - R2); temperature /= (R3 - R1); temperature = (temperature * 97) / 100 + TEMPERATURE_CALIB_KELVIN_OFFSET; D_TEMP("Calibrated temperature: %dK, %dC\n", temperature, KELVIN_TO_CELSIUS(temperature)); return temperature; } /* Adjust Txpower only if temperature variance is greater than threshold. */ #define IL_TEMPERATURE_THRESHOLD 3 /** * il4965_is_temp_calib_needed - determines if new calibration is needed * * If the temperature changed has changed sufficiently, then a recalibration * is needed. * * Assumes caller will replace il->last_temperature once calibration * executed. */ static int il4965_is_temp_calib_needed(struct il_priv *il) { int temp_diff; if (!test_bit(S_STATS, &il->status)) { D_TEMP("Temperature not updated -- no stats.\n"); return 0; } temp_diff = il->temperature - il->last_temperature; /* get absolute value */ if (temp_diff < 0) { D_POWER("Getting cooler, delta %d\n", temp_diff); temp_diff = -temp_diff; } else if (temp_diff == 0) D_POWER("Temperature unchanged\n"); else D_POWER("Getting warmer, delta %d\n", temp_diff); if (temp_diff < IL_TEMPERATURE_THRESHOLD) { D_POWER(" => thermal txpower calib not needed\n"); return 0; } D_POWER(" => thermal txpower calib needed\n"); return 1; } void il4965_temperature_calib(struct il_priv *il) { s32 temp; temp = il4965_hw_get_temperature(il); if (IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(temp)) return; if (il->temperature != temp) { if (il->temperature) D_TEMP("Temperature changed " "from %dC to %dC\n", KELVIN_TO_CELSIUS(il->temperature), KELVIN_TO_CELSIUS(temp)); else D_TEMP("Temperature " "initialized to %dC\n", KELVIN_TO_CELSIUS(temp)); } il->temperature = temp; set_bit(S_TEMPERATURE, &il->status); if (!il->disable_tx_power_cal && unlikely(!test_bit(S_SCANNING, &il->status)) && il4965_is_temp_calib_needed(il)) queue_work(il->workqueue, &il->txpower_work); } static u16 il4965_get_hcmd_size(u8 cmd_id, u16 len) { switch (cmd_id) { case C_RXON: return (u16) sizeof(struct il4965_rxon_cmd); default: return len; } } static u16 il4965_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) { struct il4965_addsta_cmd *addsta = (struct il4965_addsta_cmd *)data; addsta->mode = cmd->mode; memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); addsta->station_flags = cmd->station_flags; addsta->station_flags_msk = cmd->station_flags_msk; addsta->tid_disable_tx = cmd->tid_disable_tx; addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; addsta->sleep_tx_count = cmd->sleep_tx_count; addsta->reserved1 = cpu_to_le16(0); addsta->reserved2 = cpu_to_le16(0); return (u16) sizeof(struct il4965_addsta_cmd); } static void il4965_post_scan(struct il_priv *il) { /* * Since setting the RXON may have been deferred while * performing the scan, fire one off if needed */ if (memcmp(&il->staging, &il->active, sizeof(il->staging))) il_commit_rxon(il); } static void il4965_post_associate(struct il_priv *il) { struct ieee80211_vif *vif = il->vif; struct ieee80211_conf *conf = NULL; int ret = 0; if (!vif || !il->is_open) return; if (test_bit(S_EXIT_PENDING, &il->status)) return; il_scan_cancel_timeout(il, 200); conf = &il->hw->conf; il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; il_commit_rxon(il); ret = il_send_rxon_timing(il); if (ret) IL_WARN("RXON timing - " "Attempting to continue.\n"); il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; il_set_rxon_ht(il, &il->current_ht_config); if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); il->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); D_ASSOC("assoc id %d beacon interval %d\n", vif->bss_conf.aid, vif->bss_conf.beacon_int); if (vif->bss_conf.use_short_preamble) il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { if (vif->bss_conf.use_short_slot) il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; } il_commit_rxon(il); D_ASSOC("Associated as %d to: %pM\n", vif->bss_conf.aid, il->active.bssid_addr); switch (vif->type) { case NL80211_IFTYPE_STATION: break; case NL80211_IFTYPE_ADHOC: il4965_send_beacon_cmd(il); break; default: IL_ERR("%s Should not be called in %d mode\n", __func__, vif->type); break; } /* the chain noise calibration will enabled PM upon completion * If chain noise has already been run, then we need to enable * power management here */ if (il->chain_noise_data.state == IL_CHAIN_NOISE_DONE) il_power_update_mode(il, false); /* Enable Rx differential gain and sensitivity calibrations */ il4965_chain_noise_reset(il); il->start_calib = 1; } static void il4965_config_ap(struct il_priv *il) { struct ieee80211_vif *vif = il->vif; int ret = 0; lockdep_assert_held(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status)) return; /* The following should be done only at AP bring up */ if (!il_is_associated(il)) { /* RXON - unassoc (to set timing command) */ il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; il_commit_rxon(il); /* RXON Timing */ ret = il_send_rxon_timing(il); if (ret) IL_WARN("RXON timing failed - " "Attempting to continue.\n"); /* AP has all antennas */ il->chain_noise_data.active_chains = il->hw_params.valid_rx_ant; il_set_rxon_ht(il, &il->current_ht_config); if (il->ops->set_rxon_chain) il->ops->set_rxon_chain(il); il->staging.assoc_id = 0; if (vif->bss_conf.use_short_preamble) il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { if (vif->bss_conf.use_short_slot) il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; } /* need to send beacon cmd before committing assoc RXON! */ il4965_send_beacon_cmd(il); /* restore RXON assoc */ il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; il_commit_rxon(il); } il4965_send_beacon_cmd(il); } const struct il_ops il4965_ops = { .txq_update_byte_cnt_tbl = il4965_txq_update_byte_cnt_tbl, .txq_attach_buf_to_tfd = il4965_hw_txq_attach_buf_to_tfd, .txq_free_tfd = il4965_hw_txq_free_tfd, .txq_init = il4965_hw_tx_queue_init, .is_valid_rtc_data_addr = il4965_hw_valid_rtc_data_addr, .init_alive_start = il4965_init_alive_start, .load_ucode = il4965_load_bsm, .dump_nic_error_log = il4965_dump_nic_error_log, .dump_fh = il4965_dump_fh, .set_channel_switch = il4965_hw_channel_switch, .apm_init = il_apm_init, .send_tx_power = il4965_send_tx_power, .update_chain_flags = il4965_update_chain_flags, .eeprom_acquire_semaphore = il4965_eeprom_acquire_semaphore, .eeprom_release_semaphore = il4965_eeprom_release_semaphore, .rxon_assoc = il4965_send_rxon_assoc, .commit_rxon = il4965_commit_rxon, .set_rxon_chain = il4965_set_rxon_chain, .get_hcmd_size = il4965_get_hcmd_size, .build_addsta_hcmd = il4965_build_addsta_hcmd, .request_scan = il4965_request_scan, .post_scan = il4965_post_scan, .post_associate = il4965_post_associate, .config_ap = il4965_config_ap, .manage_ibss_station = il4965_manage_ibss_station, .update_bcast_stations = il4965_update_bcast_stations, .send_led_cmd = il4965_send_led_cmd, }; struct il_cfg il4965_cfg = { .name = "Intel(R) Wireless WiFi Link 4965AGN", .fw_name_pre = IL4965_FW_PRE, .ucode_api_max = IL4965_UCODE_API_MAX, .ucode_api_min = IL4965_UCODE_API_MIN, .sku = IL_SKU_A | IL_SKU_G | IL_SKU_N, .valid_tx_ant = ANT_AB, .valid_rx_ant = ANT_ABC, .eeprom_ver = EEPROM_4965_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, .mod_params = &il4965_mod_params, .led_mode = IL_LED_BLINK, /* * Force use of chains B and C for scan RX on 5 GHz band * because the device has off-channel reception on chain A. */ .scan_rx_antennas[IEEE80211_BAND_5GHZ] = ANT_BC, .eeprom_size = IL4965_EEPROM_IMG_SIZE, .num_of_queues = IL49_NUM_QUEUES, .num_of_ampdu_queues = IL49_NUM_AMPDU_QUEUES, .pll_cfg_val = 0, .set_l0s = true, .use_bsm = true, .led_compensation = 61, .chain_noise_num_beacons = IL4965_CAL_NUM_BEACONS, .wd_timeout = IL_DEF_WD_TIMEOUT, .temperature_kelvin = true, .ucode_tracing = true, .sensitivity_calib_by_driver = true, .chain_noise_calib_by_driver = true, .regulatory_bands = { EEPROM_REGULATORY_BAND_1_CHANNELS, EEPROM_REGULATORY_BAND_2_CHANNELS, EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS }, }; /* Module firmware */ MODULE_FIRMWARE(IL4965_MODULE_FIRMWARE(IL4965_UCODE_API_MAX)); compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/3945-rs.c0000644000175000017500000006165012026211315023467 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "commands.h" #include "3945.h" #define RS_NAME "iwl-3945-rs" static s32 il3945_expected_tpt_g[RATE_COUNT_3945] = { 7, 13, 35, 58, 0, 0, 76, 104, 130, 168, 191, 202 }; static s32 il3945_expected_tpt_g_prot[RATE_COUNT_3945] = { 7, 13, 35, 58, 0, 0, 0, 80, 93, 113, 123, 125 }; static s32 il3945_expected_tpt_a[RATE_COUNT_3945] = { 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186 }; static s32 il3945_expected_tpt_b[RATE_COUNT_3945] = { 7, 13, 35, 58, 0, 0, 0, 0, 0, 0, 0, 0 }; struct il3945_tpt_entry { s8 min_rssi; u8 idx; }; static struct il3945_tpt_entry il3945_tpt_table_a[] = { {-60, RATE_54M_IDX}, {-64, RATE_48M_IDX}, {-72, RATE_36M_IDX}, {-80, RATE_24M_IDX}, {-84, RATE_18M_IDX}, {-85, RATE_12M_IDX}, {-87, RATE_9M_IDX}, {-89, RATE_6M_IDX} }; static struct il3945_tpt_entry il3945_tpt_table_g[] = { {-60, RATE_54M_IDX}, {-64, RATE_48M_IDX}, {-68, RATE_36M_IDX}, {-80, RATE_24M_IDX}, {-84, RATE_18M_IDX}, {-85, RATE_12M_IDX}, {-86, RATE_11M_IDX}, {-88, RATE_5M_IDX}, {-90, RATE_2M_IDX}, {-92, RATE_1M_IDX} }; #define RATE_MAX_WINDOW 62 #define RATE_FLUSH (3*HZ) #define RATE_WIN_FLUSH (HZ/2) #define IL39_RATE_HIGH_TH 11520 #define IL_SUCCESS_UP_TH 8960 #define IL_SUCCESS_DOWN_TH 10880 #define RATE_MIN_FAILURE_TH 6 #define RATE_MIN_SUCCESS_TH 8 #define RATE_DECREASE_TH 1920 #define RATE_RETRY_TH 15 static u8 il3945_get_rate_idx_by_rssi(s32 rssi, enum ieee80211_band band) { u32 idx = 0; u32 table_size = 0; struct il3945_tpt_entry *tpt_table = NULL; if (rssi < IL_MIN_RSSI_VAL || rssi > IL_MAX_RSSI_VAL) rssi = IL_MIN_RSSI_VAL; switch (band) { case IEEE80211_BAND_2GHZ: tpt_table = il3945_tpt_table_g; table_size = ARRAY_SIZE(il3945_tpt_table_g); break; case IEEE80211_BAND_5GHZ: tpt_table = il3945_tpt_table_a; table_size = ARRAY_SIZE(il3945_tpt_table_a); break; default: BUG(); break; } while (idx < table_size && rssi < tpt_table[idx].min_rssi) idx++; idx = min(idx, table_size - 1); return tpt_table[idx].idx; } static void il3945_clear_win(struct il3945_rate_scale_data *win) { win->data = 0; win->success_counter = 0; win->success_ratio = -1; win->counter = 0; win->average_tpt = IL_INVALID_VALUE; win->stamp = 0; } /** * il3945_rate_scale_flush_wins - flush out the rate scale wins * * Returns the number of wins that have gathered data but were * not flushed. If there were any that were not flushed, then * reschedule the rate flushing routine. */ static int il3945_rate_scale_flush_wins(struct il3945_rs_sta *rs_sta) { int unflushed = 0; int i; unsigned long flags; struct il_priv *il __maybe_unused = rs_sta->il; /* * For each rate, if we have collected data on that rate * and it has been more than RATE_WIN_FLUSH * since we flushed, clear out the gathered stats */ for (i = 0; i < RATE_COUNT_3945; i++) { if (!rs_sta->win[i].counter) continue; spin_lock_irqsave(&rs_sta->lock, flags); if (time_after(jiffies, rs_sta->win[i].stamp + RATE_WIN_FLUSH)) { D_RATE("flushing %d samples of rate " "idx %d\n", rs_sta->win[i].counter, i); il3945_clear_win(&rs_sta->win[i]); } else unflushed++; spin_unlock_irqrestore(&rs_sta->lock, flags); } return unflushed; } #define RATE_FLUSH_MAX 5000 /* msec */ #define RATE_FLUSH_MIN 50 /* msec */ #define IL_AVERAGE_PACKETS 1500 static void il3945_bg_rate_scale_flush(unsigned long data) { struct il3945_rs_sta *rs_sta = (void *)data; struct il_priv *il __maybe_unused = rs_sta->il; int unflushed = 0; unsigned long flags; u32 packet_count, duration, pps; D_RATE("enter\n"); unflushed = il3945_rate_scale_flush_wins(rs_sta); spin_lock_irqsave(&rs_sta->lock, flags); /* Number of packets Rx'd since last time this timer ran */ packet_count = (rs_sta->tx_packets - rs_sta->last_tx_packets) + 1; rs_sta->last_tx_packets = rs_sta->tx_packets + 1; if (unflushed) { duration = jiffies_to_msecs(jiffies - rs_sta->last_partial_flush); D_RATE("Tx'd %d packets in %dms\n", packet_count, duration); /* Determine packets per second */ if (duration) pps = (packet_count * 1000) / duration; else pps = 0; if (pps) { duration = (IL_AVERAGE_PACKETS * 1000) / pps; if (duration < RATE_FLUSH_MIN) duration = RATE_FLUSH_MIN; else if (duration > RATE_FLUSH_MAX) duration = RATE_FLUSH_MAX; } else duration = RATE_FLUSH_MAX; rs_sta->flush_time = msecs_to_jiffies(duration); D_RATE("new flush period: %d msec ave %d\n", duration, packet_count); mod_timer(&rs_sta->rate_scale_flush, jiffies + rs_sta->flush_time); rs_sta->last_partial_flush = jiffies; } else { rs_sta->flush_time = RATE_FLUSH; rs_sta->flush_pending = 0; } /* If there weren't any unflushed entries, we don't schedule the timer * to run again */ rs_sta->last_flush = jiffies; spin_unlock_irqrestore(&rs_sta->lock, flags); D_RATE("leave\n"); } /** * il3945_collect_tx_data - Update the success/failure sliding win * * We keep a sliding win of the last 64 packets transmitted * at this rate. win->data contains the bitmask of successful * packets. */ static void il3945_collect_tx_data(struct il3945_rs_sta *rs_sta, struct il3945_rate_scale_data *win, int success, int retries, int idx) { unsigned long flags; s32 fail_count; struct il_priv *il __maybe_unused = rs_sta->il; if (!retries) { D_RATE("leave: retries == 0 -- should be at least 1\n"); return; } spin_lock_irqsave(&rs_sta->lock, flags); /* * Keep track of only the latest 62 tx frame attempts in this rate's * history win; anything older isn't really relevant any more. * If we have filled up the sliding win, drop the oldest attempt; * if the oldest attempt (highest bit in bitmap) shows "success", * subtract "1" from the success counter (this is the main reason * we keep these bitmaps!). * */ while (retries > 0) { if (win->counter >= RATE_MAX_WINDOW) { /* remove earliest */ win->counter = RATE_MAX_WINDOW - 1; if (win->data & (1ULL << (RATE_MAX_WINDOW - 1))) { win->data &= ~(1ULL << (RATE_MAX_WINDOW - 1)); win->success_counter--; } } /* Increment frames-attempted counter */ win->counter++; /* Shift bitmap by one frame (throw away oldest history), * OR in "1", and increment "success" if this * frame was successful. */ win->data <<= 1; if (success > 0) { win->success_counter++; win->data |= 0x1; success--; } retries--; } /* Calculate current success ratio, avoid divide-by-0! */ if (win->counter > 0) win->success_ratio = 128 * (100 * win->success_counter) / win->counter; else win->success_ratio = IL_INVALID_VALUE; fail_count = win->counter - win->success_counter; /* Calculate average throughput, if we have enough history. */ if (fail_count >= RATE_MIN_FAILURE_TH || win->success_counter >= RATE_MIN_SUCCESS_TH) win->average_tpt = ((win->success_ratio * rs_sta->expected_tpt[idx] + 64) / 128); else win->average_tpt = IL_INVALID_VALUE; /* Tag this win as having been updated */ win->stamp = jiffies; spin_unlock_irqrestore(&rs_sta->lock, flags); } /* * Called after adding a new station to initialize rate scaling */ void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id) { struct ieee80211_hw *hw = il->hw; struct ieee80211_conf *conf = &il->hw->conf; struct il3945_sta_priv *psta; struct il3945_rs_sta *rs_sta; struct ieee80211_supported_band *sband; int i; D_INFO("enter\n"); if (sta_id == il->hw_params.bcast_id) goto out; psta = (struct il3945_sta_priv *)sta->drv_priv; rs_sta = &psta->rs_sta; sband = hw->wiphy->bands[conf->channel->band]; rs_sta->il = il; rs_sta->start_rate = RATE_INVALID; /* default to just 802.11b */ rs_sta->expected_tpt = il3945_expected_tpt_b; rs_sta->last_partial_flush = jiffies; rs_sta->last_flush = jiffies; rs_sta->flush_time = RATE_FLUSH; rs_sta->last_tx_packets = 0; rs_sta->rate_scale_flush.data = (unsigned long)rs_sta; rs_sta->rate_scale_flush.function = il3945_bg_rate_scale_flush; for (i = 0; i < RATE_COUNT_3945; i++) il3945_clear_win(&rs_sta->win[i]); /* TODO: what is a good starting rate for STA? About middle? Maybe not * the lowest or the highest rate.. Could consider using RSSI from * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ for (i = sband->n_bitrates - 1; i >= 0; i--) { if (sta->supp_rates[sband->band] & (1 << i)) { rs_sta->last_txrate_idx = i; break; } } il->_3945.sta_supp_rates = sta->supp_rates[sband->band]; /* For 5 GHz band it start at IL_FIRST_OFDM_RATE */ if (sband->band == IEEE80211_BAND_5GHZ) { rs_sta->last_txrate_idx += IL_FIRST_OFDM_RATE; il->_3945.sta_supp_rates <<= IL_FIRST_OFDM_RATE; } out: il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; D_INFO("leave\n"); } static void * il3945_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { return hw->priv; } /* rate scale requires free function to be implemented */ static void il3945_rs_free(void *il) { } static void * il3945_rs_alloc_sta(void *il_priv, struct ieee80211_sta *sta, gfp_t gfp) { struct il3945_rs_sta *rs_sta; struct il3945_sta_priv *psta = (void *)sta->drv_priv; struct il_priv *il __maybe_unused = il_priv; D_RATE("enter\n"); rs_sta = &psta->rs_sta; spin_lock_init(&rs_sta->lock); init_timer(&rs_sta->rate_scale_flush); D_RATE("leave\n"); return rs_sta; } static void il3945_rs_free_sta(void *il_priv, struct ieee80211_sta *sta, void *il_sta) { struct il3945_rs_sta *rs_sta = il_sta; /* * Be careful not to use any members of il3945_rs_sta (like trying * to use il_priv to print out debugging) since it may not be fully * initialized at this point. */ del_timer_sync(&rs_sta->rate_scale_flush); } /** * il3945_rs_tx_status - Update rate control values based on Tx results * * NOTE: Uses il_priv->retry_rate for the # of retries attempted by * the hardware for each rate. */ static void il3945_rs_tx_status(void *il_rate, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *il_sta, struct sk_buff *skb) { s8 retries = 0, current_count; int scale_rate_idx, first_idx, last_idx; unsigned long flags; struct il_priv *il = (struct il_priv *)il_rate; struct il3945_rs_sta *rs_sta = il_sta; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); D_RATE("enter\n"); retries = info->status.rates[0].count; /* Sanity Check for retries */ if (retries > RATE_RETRY_TH) retries = RATE_RETRY_TH; first_idx = sband->bitrates[info->status.rates[0].idx].hw_value; if (first_idx < 0 || first_idx >= RATE_COUNT_3945) { D_RATE("leave: Rate out of bounds: %d\n", first_idx); return; } if (!il_sta) { D_RATE("leave: No STA il data to update!\n"); return; } /* Treat uninitialized rate scaling data same as non-existing. */ if (!rs_sta->il) { D_RATE("leave: STA il data uninitialized!\n"); return; } rs_sta->tx_packets++; scale_rate_idx = first_idx; last_idx = first_idx; /* * Update the win for each rate. We determine which rates * were Tx'd based on the total number of retries vs. the number * of retries configured for each rate -- currently set to the * il value 'retry_rate' vs. rate specific * * On exit from this while loop last_idx indicates the rate * at which the frame was finally transmitted (or failed if no * ACK) */ while (retries > 1) { if ((retries - 1) < il->retry_rate) { current_count = (retries - 1); last_idx = scale_rate_idx; } else { current_count = il->retry_rate; last_idx = il3945_rs_next_rate(il, scale_rate_idx); } /* Update this rate accounting for as many retries * as was used for it (per current_count) */ il3945_collect_tx_data(rs_sta, &rs_sta->win[scale_rate_idx], 0, current_count, scale_rate_idx); D_RATE("Update rate %d for %d retries.\n", scale_rate_idx, current_count); retries -= current_count; scale_rate_idx = last_idx; } /* Update the last idx win with success/failure based on ACK */ D_RATE("Update rate %d with %s.\n", last_idx, (info->flags & IEEE80211_TX_STAT_ACK) ? "success" : "failure"); il3945_collect_tx_data(rs_sta, &rs_sta->win[last_idx], info->flags & IEEE80211_TX_STAT_ACK, 1, last_idx); /* We updated the rate scale win -- if its been more than * flush_time since the last run, schedule the flush * again */ spin_lock_irqsave(&rs_sta->lock, flags); if (!rs_sta->flush_pending && time_after(jiffies, rs_sta->last_flush + rs_sta->flush_time)) { rs_sta->last_partial_flush = jiffies; rs_sta->flush_pending = 1; mod_timer(&rs_sta->rate_scale_flush, jiffies + rs_sta->flush_time); } spin_unlock_irqrestore(&rs_sta->lock, flags); D_RATE("leave\n"); } static u16 il3945_get_adjacent_rate(struct il3945_rs_sta *rs_sta, u8 idx, u16 rate_mask, enum ieee80211_band band) { u8 high = RATE_INVALID; u8 low = RATE_INVALID; struct il_priv *il __maybe_unused = rs_sta->il; /* 802.11A walks to the next literal adjacent rate in * the rate table */ if (unlikely(band == IEEE80211_BAND_5GHZ)) { int i; u32 mask; /* Find the previous rate that is in the rate mask */ i = idx - 1; for (mask = (1 << i); i >= 0; i--, mask >>= 1) { if (rate_mask & mask) { low = i; break; } } /* Find the next rate that is in the rate mask */ i = idx + 1; for (mask = (1 << i); i < RATE_COUNT_3945; i++, mask <<= 1) { if (rate_mask & mask) { high = i; break; } } return (high << 8) | low; } low = idx; while (low != RATE_INVALID) { if (rs_sta->tgg) low = il3945_rates[low].prev_rs_tgg; else low = il3945_rates[low].prev_rs; if (low == RATE_INVALID) break; if (rate_mask & (1 << low)) break; D_RATE("Skipping masked lower rate: %d\n", low); } high = idx; while (high != RATE_INVALID) { if (rs_sta->tgg) high = il3945_rates[high].next_rs_tgg; else high = il3945_rates[high].next_rs; if (high == RATE_INVALID) break; if (rate_mask & (1 << high)) break; D_RATE("Skipping masked higher rate: %d\n", high); } return (high << 8) | low; } /** * il3945_rs_get_rate - find the rate for the requested packet * * Returns the ieee80211_rate structure allocated by the driver. * * The rate control algorithm has no internal mapping between hw_mode's * rate ordering and the rate ordering used by the rate control algorithm. * * The rate control algorithm uses a single table of rates that goes across * the entire A/B/G spectrum vs. being limited to just one particular * hw_mode. * * As such, we can't convert the idx obtained below into the hw_mode's * rate table and must reference the driver allocated rate table * */ static void il3945_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, struct ieee80211_tx_rate_control *txrc) { struct ieee80211_supported_band *sband = txrc->sband; struct sk_buff *skb = txrc->skb; u8 low = RATE_INVALID; u8 high = RATE_INVALID; u16 high_low; int idx; struct il3945_rs_sta *rs_sta = il_sta; struct il3945_rate_scale_data *win = NULL; int current_tpt = IL_INVALID_VALUE; int low_tpt = IL_INVALID_VALUE; int high_tpt = IL_INVALID_VALUE; u32 fail_count; s8 scale_action = 0; unsigned long flags; u16 rate_mask; s8 max_rate_idx = -1; struct il_priv *il __maybe_unused = (struct il_priv *)il_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); D_RATE("enter\n"); /* Treat uninitialized rate scaling data same as non-existing. */ if (rs_sta && !rs_sta->il) { D_RATE("Rate scaling information not initialized yet.\n"); il_sta = NULL; } if (rate_control_send_low(sta, il_sta, txrc)) return; rate_mask = sta->supp_rates[sband->band]; /* get user max rate if set */ max_rate_idx = txrc->max_rate_idx; if (sband->band == IEEE80211_BAND_5GHZ && max_rate_idx != -1) max_rate_idx += IL_FIRST_OFDM_RATE; if (max_rate_idx < 0 || max_rate_idx >= RATE_COUNT) max_rate_idx = -1; idx = min(rs_sta->last_txrate_idx & 0xffff, RATE_COUNT_3945 - 1); if (sband->band == IEEE80211_BAND_5GHZ) rate_mask = rate_mask << IL_FIRST_OFDM_RATE; spin_lock_irqsave(&rs_sta->lock, flags); /* for recent assoc, choose best rate regarding * to rssi value */ if (rs_sta->start_rate != RATE_INVALID) { if (rs_sta->start_rate < idx && (rate_mask & (1 << rs_sta->start_rate))) idx = rs_sta->start_rate; rs_sta->start_rate = RATE_INVALID; } /* force user max rate if set by user */ if (max_rate_idx != -1 && max_rate_idx < idx) { if (rate_mask & (1 << max_rate_idx)) idx = max_rate_idx; } win = &(rs_sta->win[idx]); fail_count = win->counter - win->success_counter; if (fail_count < RATE_MIN_FAILURE_TH && win->success_counter < RATE_MIN_SUCCESS_TH) { spin_unlock_irqrestore(&rs_sta->lock, flags); D_RATE("Invalid average_tpt on rate %d: " "counter: %d, success_counter: %d, " "expected_tpt is %sNULL\n", idx, win->counter, win->success_counter, rs_sta->expected_tpt ? "not " : ""); /* Can't calculate this yet; not enough history */ win->average_tpt = IL_INVALID_VALUE; goto out; } current_tpt = win->average_tpt; high_low = il3945_get_adjacent_rate(rs_sta, idx, rate_mask, sband->band); low = high_low & 0xff; high = (high_low >> 8) & 0xff; /* If user set max rate, dont allow higher than user constrain */ if (max_rate_idx != -1 && max_rate_idx < high) high = RATE_INVALID; /* Collect Measured throughputs of adjacent rates */ if (low != RATE_INVALID) low_tpt = rs_sta->win[low].average_tpt; if (high != RATE_INVALID) high_tpt = rs_sta->win[high].average_tpt; spin_unlock_irqrestore(&rs_sta->lock, flags); scale_action = 0; /* Low success ratio , need to drop the rate */ if (win->success_ratio < RATE_DECREASE_TH || !current_tpt) { D_RATE("decrease rate because of low success_ratio\n"); scale_action = -1; /* No throughput measured yet for adjacent rates, * try increase */ } else if (low_tpt == IL_INVALID_VALUE && high_tpt == IL_INVALID_VALUE) { if (high != RATE_INVALID && win->success_ratio >= RATE_INCREASE_TH) scale_action = 1; else if (low != RATE_INVALID) scale_action = 0; /* Both adjacent throughputs are measured, but neither one has * better throughput; we're using the best rate, don't change * it! */ } else if (low_tpt != IL_INVALID_VALUE && high_tpt != IL_INVALID_VALUE && low_tpt < current_tpt && high_tpt < current_tpt) { D_RATE("No action -- low [%d] & high [%d] < " "current_tpt [%d]\n", low_tpt, high_tpt, current_tpt); scale_action = 0; /* At least one of the rates has better throughput */ } else { if (high_tpt != IL_INVALID_VALUE) { /* High rate has better throughput, Increase * rate */ if (high_tpt > current_tpt && win->success_ratio >= RATE_INCREASE_TH) scale_action = 1; else { D_RATE("decrease rate because of high tpt\n"); scale_action = 0; } } else if (low_tpt != IL_INVALID_VALUE) { if (low_tpt > current_tpt) { D_RATE("decrease rate because of low tpt\n"); scale_action = -1; } else if (win->success_ratio >= RATE_INCREASE_TH) { /* Lower rate has better * throughput,decrease rate */ scale_action = 1; } } } /* Sanity check; asked for decrease, but success rate or throughput * has been good at old rate. Don't change it. */ if (scale_action == -1 && low != RATE_INVALID && (win->success_ratio > RATE_HIGH_TH || current_tpt > 100 * rs_sta->expected_tpt[low])) scale_action = 0; switch (scale_action) { case -1: /* Decrese rate */ if (low != RATE_INVALID) idx = low; break; case 1: /* Increase rate */ if (high != RATE_INVALID) idx = high; break; case 0: default: /* No change */ break; } D_RATE("Selected %d (action %d) - low %d high %d\n", idx, scale_action, low, high); out: if (sband->band == IEEE80211_BAND_5GHZ) { if (WARN_ON_ONCE(idx < IL_FIRST_OFDM_RATE)) idx = IL_FIRST_OFDM_RATE; rs_sta->last_txrate_idx = idx; info->control.rates[0].idx = idx - IL_FIRST_OFDM_RATE; } else { rs_sta->last_txrate_idx = idx; info->control.rates[0].idx = rs_sta->last_txrate_idx; } D_RATE("leave: %d\n", idx); } #ifdef CONFIG_MAC80211_DEBUGFS static ssize_t il3945_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buff; int desc = 0; int j; ssize_t ret; struct il3945_rs_sta *lq_sta = file->private_data; buff = kmalloc(1024, GFP_KERNEL); if (!buff) return -ENOMEM; desc += sprintf(buff + desc, "tx packets=%d last rate idx=%d\n" "rate=0x%X flush time %d\n", lq_sta->tx_packets, lq_sta->last_txrate_idx, lq_sta->start_rate, jiffies_to_msecs(lq_sta->flush_time)); for (j = 0; j < RATE_COUNT_3945; j++) { desc += sprintf(buff + desc, "counter=%d success=%d %%=%d\n", lq_sta->win[j].counter, lq_sta->win[j].success_counter, lq_sta->win[j].success_ratio); } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); kfree(buff); return ret; } static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .read = il3945_sta_dbgfs_stats_table_read, .open = simple_open, .llseek = default_llseek, }; static void il3945_add_debugfs(void *il, void *il_sta, struct dentry *dir) { struct il3945_rs_sta *lq_sta = il_sta; lq_sta->rs_sta_dbgfs_stats_table_file = debugfs_create_file("rate_stats_table", 0600, dir, lq_sta, &rs_sta_dbgfs_stats_table_ops); } static void il3945_remove_debugfs(void *il, void *il_sta) { struct il3945_rs_sta *lq_sta = il_sta; debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); } #endif /* * Initialization of rate scaling information is done by driver after * the station is added. Since mac80211 calls this function before a * station is added we ignore it. */ static void il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *il_sta) { } static struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = il3945_rs_tx_status, .get_rate = il3945_rs_get_rate, .rate_init = il3945_rs_rate_init_stub, .alloc = il3945_rs_alloc, .free = il3945_rs_free, .alloc_sta = il3945_rs_alloc_sta, .free_sta = il3945_rs_free_sta, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = il3945_add_debugfs, .remove_sta_debugfs = il3945_remove_debugfs, #endif }; void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) { struct il_priv *il = hw->priv; s32 rssi = 0; unsigned long flags; struct il3945_rs_sta *rs_sta; struct ieee80211_sta *sta; struct il3945_sta_priv *psta; D_RATE("enter\n"); rcu_read_lock(); sta = ieee80211_find_sta(il->vif, il->stations[sta_id].sta.sta.addr); if (!sta) { D_RATE("Unable to find station to initialize rate scaling.\n"); rcu_read_unlock(); return; } psta = (void *)sta->drv_priv; rs_sta = &psta->rs_sta; spin_lock_irqsave(&rs_sta->lock, flags); rs_sta->tgg = 0; switch (il->band) { case IEEE80211_BAND_2GHZ: /* TODO: this always does G, not a regression */ if (il->active.flags & RXON_FLG_TGG_PROTECT_MSK) { rs_sta->tgg = 1; rs_sta->expected_tpt = il3945_expected_tpt_g_prot; } else rs_sta->expected_tpt = il3945_expected_tpt_g; break; case IEEE80211_BAND_5GHZ: rs_sta->expected_tpt = il3945_expected_tpt_a; break; default: BUG(); break; } spin_unlock_irqrestore(&rs_sta->lock, flags); rssi = il->_3945.last_rx_rssi; if (rssi == 0) rssi = IL_MIN_RSSI_VAL; D_RATE("Network RSSI: %d\n", rssi); rs_sta->start_rate = il3945_get_rate_idx_by_rssi(rssi, il->band); D_RATE("leave: rssi %d assign rate idx: " "%d (plcp 0x%x)\n", rssi, rs_sta->start_rate, il3945_rates[rs_sta->start_rate].plcp); rcu_read_unlock(); } int il3945_rate_control_register(void) { return ieee80211_rate_control_register(&rs_ops); } void il3945_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rs_ops); } compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/3945.h0000644000175000017500000005003312026211315023043 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #ifndef __il_3945_h__ #define __il_3945_h__ #include /* for struct pci_device_id */ #include #include /* Hardware specific file defines the PCI IDs table for that hardware module */ extern const struct pci_device_id il3945_hw_card_ids[]; #include "common.h" extern const struct il_ops il3945_ops; /* Highest firmware API version supported */ #define IL3945_UCODE_API_MAX 2 /* Lowest firmware API version supported */ #define IL3945_UCODE_API_MIN 1 #define IL3945_FW_PRE "iwlwifi-3945-" #define _IL3945_MODULE_FIRMWARE(api) IL3945_FW_PRE #api ".ucode" #define IL3945_MODULE_FIRMWARE(api) _IL3945_MODULE_FIRMWARE(api) /* Default noise level to report when noise measurement is not available. * This may be because we're: * 1) Not associated (4965, no beacon stats being sent to driver) * 2) Scanning (noise measurement does not apply to associated channel) * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) * Use default noise value of -127 ... this is below the range of measurable * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. * Also, -127 works better than 0 when averaging frames with/without * noise info (e.g. averaging might be done in app); measured dBm values are * always negative ... using a negative value as the default keeps all * averages within an s8's (used in some apps) range of negative values. */ #define IL_NOISE_MEAS_NOT_AVAILABLE (-127) /* Module parameters accessible from iwl-*.c */ extern struct il_mod_params il3945_mod_params; struct il3945_rate_scale_data { u64 data; s32 success_counter; s32 success_ratio; s32 counter; s32 average_tpt; unsigned long stamp; }; struct il3945_rs_sta { spinlock_t lock; struct il_priv *il; s32 *expected_tpt; unsigned long last_partial_flush; unsigned long last_flush; u32 flush_time; u32 last_tx_packets; u32 tx_packets; u8 tgg; u8 flush_pending; u8 start_rate; struct timer_list rate_scale_flush; struct il3945_rate_scale_data win[RATE_COUNT_3945]; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *rs_sta_dbgfs_stats_table_file; #endif /* used to be in sta_info */ int last_txrate_idx; }; /* * The common struct MUST be first because it is shared between * 3945 and 4965! */ struct il3945_sta_priv { struct il_station_priv_common common; struct il3945_rs_sta rs_sta; }; enum il3945_antenna { IL_ANTENNA_DIVERSITY, IL_ANTENNA_MAIN, IL_ANTENNA_AUX }; /* * RTS threshold here is total size [2347] minus 4 FCS bytes * Per spec: * a value of 0 means RTS on all data/management packets * a value > max MSDU size means no RTS * else RTS for data/management frames where MPDU is larger * than RTS value. */ #define DEFAULT_RTS_THRESHOLD 2347U #define MIN_RTS_THRESHOLD 0U #define MAX_RTS_THRESHOLD 2347U #define MAX_MSDU_SIZE 2304U #define MAX_MPDU_SIZE 2346U #define DEFAULT_BEACON_INTERVAL 100U #define DEFAULT_SHORT_RETRY_LIMIT 7U #define DEFAULT_LONG_RETRY_LIMIT 4U #define IL_TX_FIFO_AC0 0 #define IL_TX_FIFO_AC1 1 #define IL_TX_FIFO_AC2 2 #define IL_TX_FIFO_AC3 3 #define IL_TX_FIFO_HCCA_1 5 #define IL_TX_FIFO_HCCA_2 6 #define IL_TX_FIFO_NONE 7 #define IEEE80211_DATA_LEN 2304 #define IEEE80211_4ADDR_LEN 30 #define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) #define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) struct il3945_frame { union { struct ieee80211_hdr frame; struct il3945_tx_beacon_cmd beacon; u8 raw[IEEE80211_FRAME_LEN]; u8 cmd[360]; } u; struct list_head list; }; #define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) #define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) #define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 #define IL_SUPPORTED_RATES_IE_LEN 8 #define SCAN_INTERVAL 100 #define MAX_TID_COUNT 9 #define IL_INVALID_RATE 0xFF #define IL_INVALID_VALUE -1 #define STA_PS_STATUS_WAKE 0 #define STA_PS_STATUS_SLEEP 1 struct il3945_ibss_seq { u8 mac[ETH_ALEN]; u16 seq_num; u16 frag_num; unsigned long packet_time; struct list_head list; }; #define IL_RX_HDR(x) ((struct il3945_rx_frame_hdr *)(\ x->u.rx_frame.stats.payload + \ x->u.rx_frame.stats.phy_count)) #define IL_RX_END(x) ((struct il3945_rx_frame_end *)(\ IL_RX_HDR(x)->payload + \ le16_to_cpu(IL_RX_HDR(x)->len))) #define IL_RX_STATS(x) (&x->u.rx_frame.stats) #define IL_RX_DATA(x) (IL_RX_HDR(x)->payload) /****************************************************************************** * * Functions implemented in iwl3945-base.c which are forward declared here * for use by iwl-*.c * *****************************************************************************/ extern int il3945_calc_db_from_ratio(int sig_ratio); extern void il3945_rx_replenish(void *data); extern void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); extern unsigned int il3945_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, int left); extern int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, char **buf, bool display); extern void il3945_dump_nic_error_log(struct il_priv *il); /****************************************************************************** * * Functions implemented in iwl-[34]*.c which are forward declared here * for use by iwl3945-base.c * * NOTE: The implementation of these functions are hardware specific * which is why they are in the hardware specific files (vs. iwl-base.c) * * Naming convention -- * il3945_ <-- Its part of iwlwifi (should be changed to il3945_) * il3945_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW) * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) * il3945_bg_ <-- Called from work queue context * il3945_mac_ <-- mac80211 callback * ****************************************************************************/ extern void il3945_hw_handler_setup(struct il_priv *il); extern void il3945_hw_setup_deferred_work(struct il_priv *il); extern void il3945_hw_cancel_deferred_work(struct il_priv *il); extern int il3945_hw_rxq_stop(struct il_priv *il); extern int il3945_hw_set_hw_params(struct il_priv *il); extern int il3945_hw_nic_init(struct il_priv *il); extern int il3945_hw_nic_stop_master(struct il_priv *il); extern void il3945_hw_txq_ctx_free(struct il_priv *il); extern void il3945_hw_txq_ctx_stop(struct il_priv *il); extern int il3945_hw_nic_reset(struct il_priv *il); extern int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset, u8 pad); extern void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); extern int il3945_hw_get_temperature(struct il_priv *il); extern int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); extern unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, struct il3945_frame *frame, u8 rate); void il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, int sta_id); extern int il3945_hw_reg_send_txpower(struct il_priv *il); extern int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power); extern void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb); void il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb); extern void il3945_disable_events(struct il_priv *il); extern int il4965_get_temperature(const struct il_priv *il); extern void il3945_post_associate(struct il_priv *il); extern void il3945_config_ap(struct il_priv *il); extern int il3945_commit_rxon(struct il_priv *il); /** * il3945_hw_find_station - Find station id for a given BSSID * @bssid: MAC address of station ID to find * * NOTE: This should not be hardware specific but the code has * not yet been merged into a single common layer for managing the * station tables. */ extern u8 il3945_hw_find_station(struct il_priv *il, const u8 * bssid); extern __le32 il3945_get_antenna_flags(const struct il_priv *il); extern int il3945_init_hw_rate_table(struct il_priv *il); extern void il3945_reg_txpower_periodic(struct il_priv *il); extern int il3945_txpower_set_from_eeprom(struct il_priv *il); extern int il3945_rs_next_rate(struct il_priv *il, int rate); /* scanning */ int il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif); void il3945_post_scan(struct il_priv *il); /* rates */ extern const struct il3945_rate_info il3945_rates[RATE_COUNT_3945]; /* RSSI to dBm */ #define IL39_RSSI_OFFSET 95 /* * EEPROM related constants, enums, and structures. */ #define EEPROM_SKU_CAP_OP_MODE_MRC (1 << 7) /* * Mapping of a Tx power level, at factory calibration temperature, * to a radio/DSP gain table idx. * One for each of 5 "sample" power levels in each band. * v_det is measured at the factory, using the 3945's built-in power amplifier * (PA) output voltage detector. This same detector is used during Tx of * long packets in normal operation to provide feedback as to proper output * level. * Data copied from EEPROM. * DO NOT ALTER THIS STRUCTURE!!! */ struct il3945_eeprom_txpower_sample { u8 gain_idx; /* idx into power (gain) setup table ... */ s8 power; /* ... for this pwr level for this chnl group */ u16 v_det; /* PA output voltage */ } __packed; /* * Mappings of Tx power levels -> nominal radio/DSP gain table idxes. * One for each channel group (a.k.a. "band") (1 for BG, 4 for A). * Tx power setup code interpolates between the 5 "sample" power levels * to determine the nominal setup for a requested power level. * Data copied from EEPROM. * DO NOT ALTER THIS STRUCTURE!!! */ struct il3945_eeprom_txpower_group { struct il3945_eeprom_txpower_sample samples[5]; /* 5 power levels */ s32 a, b, c, d, e; /* coefficients for voltage->power * formula (signed) */ s32 Fa, Fb, Fc, Fd, Fe; /* these modify coeffs based on * frequency (signed) */ s8 saturation_power; /* highest power possible by h/w in this * band */ u8 group_channel; /* "representative" channel # in this band */ s16 temperature; /* h/w temperature at factory calib this band * (signed) */ } __packed; /* * Temperature-based Tx-power compensation data, not band-specific. * These coefficients are use to modify a/b/c/d/e coeffs based on * difference between current temperature and factory calib temperature. * Data copied from EEPROM. */ struct il3945_eeprom_temperature_corr { u32 Ta; u32 Tb; u32 Tc; u32 Td; u32 Te; } __packed; /* * EEPROM map */ struct il3945_eeprom { u8 reserved0[16]; u16 device_id; /* abs.ofs: 16 */ u8 reserved1[2]; u16 pmc; /* abs.ofs: 20 */ u8 reserved2[20]; u8 mac_address[6]; /* abs.ofs: 42 */ u8 reserved3[58]; u16 board_revision; /* abs.ofs: 106 */ u8 reserved4[11]; u8 board_pba_number[9]; /* abs.ofs: 119 */ u8 reserved5[8]; u16 version; /* abs.ofs: 136 */ u8 sku_cap; /* abs.ofs: 138 */ u8 leds_mode; /* abs.ofs: 139 */ u16 oem_mode; u16 wowlan_mode; /* abs.ofs: 142 */ u16 leds_time_interval; /* abs.ofs: 144 */ u8 leds_off_time; /* abs.ofs: 146 */ u8 leds_on_time; /* abs.ofs: 147 */ u8 almgor_m_version; /* abs.ofs: 148 */ u8 antenna_switch_type; /* abs.ofs: 149 */ u8 reserved6[42]; u8 sku_id[4]; /* abs.ofs: 192 */ /* * Per-channel regulatory data. * * Each channel that *might* be supported by 3945 has a fixed location * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory * txpower (MSB). * * Entries immediately below are for 20 MHz channel width. * * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ u16 band_1_count; /* abs.ofs: 196 */ struct il_eeprom_channel band_1_channels[14]; /* abs.ofs: 198 */ /* * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, * 5.0 GHz channels 7, 8, 11, 12, 16 * (4915-5080MHz) (none of these is ever supported) */ u16 band_2_count; /* abs.ofs: 226 */ struct il_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */ /* * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 * (5170-5320MHz) */ u16 band_3_count; /* abs.ofs: 254 */ struct il_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */ /* * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 * (5500-5700MHz) */ u16 band_4_count; /* abs.ofs: 280 */ struct il_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */ /* * 5.7 GHz channels 145, 149, 153, 157, 161, 165 * (5725-5825MHz) */ u16 band_5_count; /* abs.ofs: 304 */ struct il_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */ u8 reserved9[194]; /* * 3945 Txpower calibration data. */ #define IL_NUM_TX_CALIB_GROUPS 5 struct il3945_eeprom_txpower_group groups[IL_NUM_TX_CALIB_GROUPS]; /* abs.ofs: 512 */ struct il3945_eeprom_temperature_corr corrections; /* abs.ofs: 832 */ u8 reserved16[172]; /* fill out to full 1024 byte block */ } __packed; #define IL3945_EEPROM_IMG_SIZE 1024 /* End of EEPROM */ #define PCI_CFG_REV_ID_BIT_BASIC_SKU (0x40) /* bit 6 */ #define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */ /* 4 DATA + 1 CMD. There are 2 HCCA queues that are not used. */ #define IL39_NUM_QUEUES 5 #define IL39_CMD_QUEUE_NUM 4 #define IL_DEFAULT_TX_RETRY 15 /*********************************************/ #define RFD_SIZE 4 #define NUM_TFD_CHUNKS 4 #define TFD_CTL_COUNT_SET(n) (n << 24) #define TFD_CTL_COUNT_GET(ctl) ((ctl >> 24) & 7) #define TFD_CTL_PAD_SET(n) (n << 28) #define TFD_CTL_PAD_GET(ctl) (ctl >> 28) /* Sizes and addresses for instruction and data memory (SRAM) in * 3945's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ #define IL39_RTC_INST_LOWER_BOUND (0x000000) #define IL39_RTC_INST_UPPER_BOUND (0x014000) #define IL39_RTC_DATA_LOWER_BOUND (0x800000) #define IL39_RTC_DATA_UPPER_BOUND (0x808000) #define IL39_RTC_INST_SIZE (IL39_RTC_INST_UPPER_BOUND - \ IL39_RTC_INST_LOWER_BOUND) #define IL39_RTC_DATA_SIZE (IL39_RTC_DATA_UPPER_BOUND - \ IL39_RTC_DATA_LOWER_BOUND) #define IL39_MAX_INST_SIZE IL39_RTC_INST_SIZE #define IL39_MAX_DATA_SIZE IL39_RTC_DATA_SIZE /* Size of uCode instruction memory in bootstrap state machine */ #define IL39_MAX_BSM_SIZE IL39_RTC_INST_SIZE static inline int il3945_hw_valid_rtc_data_addr(u32 addr) { return (addr >= IL39_RTC_DATA_LOWER_BOUND && addr < IL39_RTC_DATA_UPPER_BOUND); } /* Base physical address of il3945_shared is provided to FH39_TSSR_CBB_BASE * and &il3945_shared.rx_read_ptr[0] is provided to FH39_RCSR_RPTR_ADDR(0) */ struct il3945_shared { __le32 tx_base_ptr[8]; } __packed; /************************************/ /* iwl3945 Flow Handler Definitions */ /************************************/ /** * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) * Addresses are offsets from device's PCI hardware base address. */ #define FH39_MEM_LOWER_BOUND (0x0800) #define FH39_MEM_UPPER_BOUND (0x1000) #define FH39_CBCC_TBL (FH39_MEM_LOWER_BOUND + 0x140) #define FH39_TFDB_TBL (FH39_MEM_LOWER_BOUND + 0x180) #define FH39_RCSR_TBL (FH39_MEM_LOWER_BOUND + 0x400) #define FH39_RSSR_TBL (FH39_MEM_LOWER_BOUND + 0x4c0) #define FH39_TCSR_TBL (FH39_MEM_LOWER_BOUND + 0x500) #define FH39_TSSR_TBL (FH39_MEM_LOWER_BOUND + 0x680) /* TFDB (Transmit Frame Buffer Descriptor) */ #define FH39_TFDB(_ch, buf) (FH39_TFDB_TBL + \ ((_ch) * 2 + (buf)) * 0x28) #define FH39_TFDB_CHNL_BUF_CTRL_REG(_ch) (FH39_TFDB_TBL + 0x50 * (_ch)) /* CBCC channel is [0,2] */ #define FH39_CBCC(_ch) (FH39_CBCC_TBL + (_ch) * 0x8) #define FH39_CBCC_CTRL(_ch) (FH39_CBCC(_ch) + 0x00) #define FH39_CBCC_BASE(_ch) (FH39_CBCC(_ch) + 0x04) /* RCSR channel is [0,2] */ #define FH39_RCSR(_ch) (FH39_RCSR_TBL + (_ch) * 0x40) #define FH39_RCSR_CONFIG(_ch) (FH39_RCSR(_ch) + 0x00) #define FH39_RCSR_RBD_BASE(_ch) (FH39_RCSR(_ch) + 0x04) #define FH39_RCSR_WPTR(_ch) (FH39_RCSR(_ch) + 0x20) #define FH39_RCSR_RPTR_ADDR(_ch) (FH39_RCSR(_ch) + 0x24) #define FH39_RSCSR_CHNL0_WPTR (FH39_RCSR_WPTR(0)) /* RSSR */ #define FH39_RSSR_CTRL (FH39_RSSR_TBL + 0x000) #define FH39_RSSR_STATUS (FH39_RSSR_TBL + 0x004) /* TCSR */ #define FH39_TCSR(_ch) (FH39_TCSR_TBL + (_ch) * 0x20) #define FH39_TCSR_CONFIG(_ch) (FH39_TCSR(_ch) + 0x00) #define FH39_TCSR_CREDIT(_ch) (FH39_TCSR(_ch) + 0x04) #define FH39_TCSR_BUFF_STTS(_ch) (FH39_TCSR(_ch) + 0x08) /* TSSR */ #define FH39_TSSR_CBB_BASE (FH39_TSSR_TBL + 0x000) #define FH39_TSSR_MSG_CONFIG (FH39_TSSR_TBL + 0x008) #define FH39_TSSR_TX_STATUS (FH39_TSSR_TBL + 0x010) /* DBM */ #define FH39_SRVC_CHNL (6) #define FH39_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE (20) #define FH39_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH (4) #define FH39_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN (0x08000000) #define FH39_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE (0x80000000) #define FH39_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE (0x20000000) #define FH39_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 (0x01000000) #define FH39_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST (0x00001000) #define FH39_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH (0x00000000) #define FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) #define FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRIVER (0x00000001) #define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL (0x00000000) #define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL (0x00000008) #define FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) #define FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) #define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) #define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) #define FH39_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00004000) #define FH39_TCSR_CHNL_TX_BUF_STS_REG_BIT_TFDB_WPTR (0x00000001) #define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON (0xFF000000) #define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON (0x00FF0000) #define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B (0x00000400) #define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON (0x00000100) #define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON (0x00000080) #define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH (0x00000020) #define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH (0x00000005) #define FH39_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_ch) (BIT(_ch) << 24) #define FH39_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_ch) (BIT(_ch) << 16) #define FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_ch) \ (FH39_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_ch) | \ FH39_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_ch)) #define FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) struct il3945_tfd_tb { __le32 addr; __le32 len; } __packed; struct il3945_tfd { __le32 control_flags; struct il3945_tfd_tb tbs[4]; u8 __pad[28]; } __packed; #ifdef CONFIG_IWLEGACY_DEBUGFS extern const struct il_debugfs_ops il3945_debugfs_ops; #endif #endif compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/3945-debug.c0000644000175000017500000004615112026211315024130 0ustar mcgrofmcgrof/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include "common.h" #include "3945.h" static int il3945_stats_flag(struct il_priv *il, char *buf, int bufsz) { int p = 0; p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", le32_to_cpu(il->_3945.stats.flag)); if (le32_to_cpu(il->_3945.stats.flag) & UCODE_STATS_CLEAR_MSK) p += scnprintf(buf + p, bufsz - p, "\tStatistics have been cleared\n"); p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", (le32_to_cpu(il->_3945.stats.flag) & UCODE_STATS_FREQUENCY_MSK) ? "2.4 GHz" : "5.2 GHz"); p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", (le32_to_cpu(il->_3945.stats.flag) & UCODE_STATS_NARROW_BAND_MSK) ? "enabled" : "disabled"); return p; } ssize_t il3945_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct iwl39_stats_rx_phy) * 40 + sizeof(struct iwl39_stats_rx_non_phy) * 40 + 400; ssize_t ret; struct iwl39_stats_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; struct iwl39_stats_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; struct iwl39_stats_rx_non_phy *general, *accum_general; struct iwl39_stats_rx_non_phy *delta_general, *max_general; if (!il_is_alive(il)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } /* * The statistic information display here is based on * the last stats notification from uCode * might not reflect the current uCode activity */ ofdm = &il->_3945.stats.rx.ofdm; cck = &il->_3945.stats.rx.cck; general = &il->_3945.stats.rx.general; accum_ofdm = &il->_3945.accum_stats.rx.ofdm; accum_cck = &il->_3945.accum_stats.rx.cck; accum_general = &il->_3945.accum_stats.rx.general; delta_ofdm = &il->_3945.delta_stats.rx.ofdm; delta_cck = &il->_3945.delta_stats.rx.cck; delta_general = &il->_3945.delta_stats.rx.general; max_ofdm = &il->_3945.max_delta.rx.ofdm; max_cck = &il->_3945.max_delta.rx.cck; max_general = &il->_3945.max_delta.rx.general; pos += il3945_stats_flag(il, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "%-32s current" "acumulative delta max\n", "Statistics_Rx - OFDM:"); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "ina_cnt:", le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt, delta_ofdm->ina_cnt, max_ofdm->ina_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "fina_cnt:", le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, delta_ofdm->fina_cnt, max_ofdm->fina_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "plcp_err:", le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, delta_ofdm->plcp_err, max_ofdm->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "crc32_err:", le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, delta_ofdm->crc32_err, max_ofdm->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "overrun_err:", le32_to_cpu(ofdm->overrun_err), accum_ofdm->overrun_err, delta_ofdm->overrun_err, max_ofdm->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "early_overrun_err:", le32_to_cpu(ofdm->early_overrun_err), accum_ofdm->early_overrun_err, delta_ofdm->early_overrun_err, max_ofdm->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "crc32_good:", le32_to_cpu(ofdm->crc32_good), accum_ofdm->crc32_good, delta_ofdm->crc32_good, max_ofdm->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "false_alarm_cnt:", le32_to_cpu(ofdm->false_alarm_cnt), accum_ofdm->false_alarm_cnt, delta_ofdm->false_alarm_cnt, max_ofdm->false_alarm_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "fina_sync_err_cnt:", le32_to_cpu(ofdm->fina_sync_err_cnt), accum_ofdm->fina_sync_err_cnt, delta_ofdm->fina_sync_err_cnt, max_ofdm->fina_sync_err_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "sfd_timeout:", le32_to_cpu(ofdm->sfd_timeout), accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, max_ofdm->sfd_timeout); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "fina_timeout:", le32_to_cpu(ofdm->fina_timeout), accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, max_ofdm->fina_timeout); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "unresponded_rts:", le32_to_cpu(ofdm->unresponded_rts), accum_ofdm->unresponded_rts, delta_ofdm->unresponded_rts, max_ofdm->unresponded_rts); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "rxe_frame_lmt_ovrun:", le32_to_cpu(ofdm->rxe_frame_limit_overrun), accum_ofdm->rxe_frame_limit_overrun, delta_ofdm->rxe_frame_limit_overrun, max_ofdm->rxe_frame_limit_overrun); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "sent_ack_cnt:", le32_to_cpu(ofdm->sent_ack_cnt), accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, max_ofdm->sent_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "sent_cts_cnt:", le32_to_cpu(ofdm->sent_cts_cnt), accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); pos += scnprintf(buf + pos, bufsz - pos, "%-32s current" "acumulative delta max\n", "Statistics_Rx - CCK:"); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "ina_cnt:", le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, delta_cck->ina_cnt, max_cck->ina_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "fina_cnt:", le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, delta_cck->fina_cnt, max_cck->fina_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "plcp_err:", le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, delta_cck->plcp_err, max_cck->plcp_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "crc32_err:", le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, delta_cck->crc32_err, max_cck->crc32_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "overrun_err:", le32_to_cpu(cck->overrun_err), accum_cck->overrun_err, delta_cck->overrun_err, max_cck->overrun_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "early_overrun_err:", le32_to_cpu(cck->early_overrun_err), accum_cck->early_overrun_err, delta_cck->early_overrun_err, max_cck->early_overrun_err); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "crc32_good:", le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, delta_cck->crc32_good, max_cck->crc32_good); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "false_alarm_cnt:", le32_to_cpu(cck->false_alarm_cnt), accum_cck->false_alarm_cnt, delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "fina_sync_err_cnt:", le32_to_cpu(cck->fina_sync_err_cnt), accum_cck->fina_sync_err_cnt, delta_cck->fina_sync_err_cnt, max_cck->fina_sync_err_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "sfd_timeout:", le32_to_cpu(cck->sfd_timeout), accum_cck->sfd_timeout, delta_cck->sfd_timeout, max_cck->sfd_timeout); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "fina_timeout:", le32_to_cpu(cck->fina_timeout), accum_cck->fina_timeout, delta_cck->fina_timeout, max_cck->fina_timeout); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "unresponded_rts:", le32_to_cpu(cck->unresponded_rts), accum_cck->unresponded_rts, delta_cck->unresponded_rts, max_cck->unresponded_rts); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "rxe_frame_lmt_ovrun:", le32_to_cpu(cck->rxe_frame_limit_overrun), accum_cck->rxe_frame_limit_overrun, delta_cck->rxe_frame_limit_overrun, max_cck->rxe_frame_limit_overrun); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "sent_ack_cnt:", le32_to_cpu(cck->sent_ack_cnt), accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, max_cck->sent_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "sent_cts_cnt:", le32_to_cpu(cck->sent_cts_cnt), accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, max_cck->sent_cts_cnt); pos += scnprintf(buf + pos, bufsz - pos, "%-32s current" "acumulative delta max\n", "Statistics_Rx - GENERAL:"); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "bogus_cts:", le32_to_cpu(general->bogus_cts), accum_general->bogus_cts, delta_general->bogus_cts, max_general->bogus_cts); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "bogus_ack:", le32_to_cpu(general->bogus_ack), accum_general->bogus_ack, delta_general->bogus_ack, max_general->bogus_ack); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "non_bssid_frames:", le32_to_cpu(general->non_bssid_frames), accum_general->non_bssid_frames, delta_general->non_bssid_frames, max_general->non_bssid_frames); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "filtered_frames:", le32_to_cpu(general->filtered_frames), accum_general->filtered_frames, delta_general->filtered_frames, max_general->filtered_frames); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "non_channel_beacons:", le32_to_cpu(general->non_channel_beacons), accum_general->non_channel_beacons, delta_general->non_channel_beacons, max_general->non_channel_beacons); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } ssize_t il3945_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char *buf; int bufsz = (sizeof(struct iwl39_stats_tx) * 48) + 250; ssize_t ret; struct iwl39_stats_tx *tx, *accum_tx, *delta_tx, *max_tx; if (!il_is_alive(il)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } /* * The statistic information display here is based on * the last stats notification from uCode * might not reflect the current uCode activity */ tx = &il->_3945.stats.tx; accum_tx = &il->_3945.accum_stats.tx; delta_tx = &il->_3945.delta_stats.tx; max_tx = &il->_3945.max_delta.tx; pos += il3945_stats_flag(il, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "%-32s current" "acumulative delta max\n", "Statistics_Tx:"); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "preamble:", le32_to_cpu(tx->preamble_cnt), accum_tx->preamble_cnt, delta_tx->preamble_cnt, max_tx->preamble_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "rx_detected_cnt:", le32_to_cpu(tx->rx_detected_cnt), accum_tx->rx_detected_cnt, delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "bt_prio_defer_cnt:", le32_to_cpu(tx->bt_prio_defer_cnt), accum_tx->bt_prio_defer_cnt, delta_tx->bt_prio_defer_cnt, max_tx->bt_prio_defer_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "bt_prio_kill_cnt:", le32_to_cpu(tx->bt_prio_kill_cnt), accum_tx->bt_prio_kill_cnt, delta_tx->bt_prio_kill_cnt, max_tx->bt_prio_kill_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "few_bytes_cnt:", le32_to_cpu(tx->few_bytes_cnt), accum_tx->few_bytes_cnt, delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "cts_timeout:", le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, delta_tx->cts_timeout, max_tx->cts_timeout); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "ack_timeout:", le32_to_cpu(tx->ack_timeout), accum_tx->ack_timeout, delta_tx->ack_timeout, max_tx->ack_timeout); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "expected_ack_cnt:", le32_to_cpu(tx->expected_ack_cnt), accum_tx->expected_ack_cnt, delta_tx->expected_ack_cnt, max_tx->expected_ack_cnt); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "actual_ack_cnt:", le32_to_cpu(tx->actual_ack_cnt), accum_tx->actual_ack_cnt, delta_tx->actual_ack_cnt, max_tx->actual_ack_cnt); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } ssize_t il3945_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct il_priv *il = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct iwl39_stats_general) * 10 + 300; ssize_t ret; struct iwl39_stats_general *general, *accum_general; struct iwl39_stats_general *delta_general, *max_general; struct stats_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; struct iwl39_stats_div *div, *accum_div, *delta_div, *max_div; if (!il_is_alive(il)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { IL_ERR("Can not allocate Buffer\n"); return -ENOMEM; } /* * The statistic information display here is based on * the last stats notification from uCode * might not reflect the current uCode activity */ general = &il->_3945.stats.general; dbg = &il->_3945.stats.general.dbg; div = &il->_3945.stats.general.div; accum_general = &il->_3945.accum_stats.general; delta_general = &il->_3945.delta_stats.general; max_general = &il->_3945.max_delta.general; accum_dbg = &il->_3945.accum_stats.general.dbg; delta_dbg = &il->_3945.delta_stats.general.dbg; max_dbg = &il->_3945.max_delta.general.dbg; accum_div = &il->_3945.accum_stats.general.div; delta_div = &il->_3945.delta_stats.general.div; max_div = &il->_3945.max_delta.general.div; pos += il3945_stats_flag(il, buf, bufsz); pos += scnprintf(buf + pos, bufsz - pos, "%-32s current" "acumulative delta max\n", "Statistics_General:"); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "burst_check:", le32_to_cpu(dbg->burst_check), accum_dbg->burst_check, delta_dbg->burst_check, max_dbg->burst_check); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "burst_count:", le32_to_cpu(dbg->burst_count), accum_dbg->burst_count, delta_dbg->burst_count, max_dbg->burst_count); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "sleep_time:", le32_to_cpu(general->sleep_time), accum_general->sleep_time, delta_general->sleep_time, max_general->sleep_time); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "slots_out:", le32_to_cpu(general->slots_out), accum_general->slots_out, delta_general->slots_out, max_general->slots_out); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "slots_idle:", le32_to_cpu(general->slots_idle), accum_general->slots_idle, delta_general->slots_idle, max_general->slots_idle); pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n", le32_to_cpu(general->ttl_timestamp)); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "tx_on_a:", le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, delta_div->tx_on_a, max_div->tx_on_a); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "tx_on_b:", le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, delta_div->tx_on_b, max_div->tx_on_b); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "exec_time:", le32_to_cpu(div->exec_time), accum_div->exec_time, delta_div->exec_time, max_div->exec_time); pos += scnprintf(buf + pos, bufsz - pos, " %-30s %10u %10u %10u %10u\n", "probe_time:", le32_to_cpu(div->probe_time), accum_div->probe_time, delta_div->probe_time, max_div->probe_time); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); return ret; } const struct il_debugfs_ops il3945_debugfs_ops = { .rx_stats_read = il3945_ucode_rx_stats_read, .tx_stats_read = il3945_ucode_tx_stats_read, .general_stats_read = il3945_ucode_general_stats_read, }; compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/3945.c0000644000175000017500000022554112026211315023046 0ustar mcgrofmcgrof/****************************************************************************** * * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "3945.h" /* Send led command */ static int il3945_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) { struct il_host_cmd cmd = { .id = C_LEDS, .len = sizeof(struct il_led_cmd), .data = led_cmd, .flags = CMD_ASYNC, .callback = NULL, }; return il_send_cmd(il, &cmd); } #define IL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \ [RATE_##r##M_IDX] = { RATE_##r##M_PLCP, \ RATE_##r##M_IEEE, \ RATE_##ip##M_IDX, \ RATE_##in##M_IDX, \ RATE_##rp##M_IDX, \ RATE_##rn##M_IDX, \ RATE_##pp##M_IDX, \ RATE_##np##M_IDX, \ RATE_##r##M_IDX_TBL, \ RATE_##ip##M_IDX_TBL } /* * Parameter order: * rate, prev rate, next rate, prev tgg rate, next tgg rate * * If there isn't a valid next or previous rate then INV is used which * maps to RATE_INVALID * */ const struct il3945_rate_info il3945_rates[RATE_COUNT_3945] = { IL_DECLARE_RATE_INFO(1, INV, 2, INV, 2, INV, 2), /* 1mbps */ IL_DECLARE_RATE_INFO(2, 1, 5, 1, 5, 1, 5), /* 2mbps */ IL_DECLARE_RATE_INFO(5, 2, 6, 2, 11, 2, 11), /*5.5mbps */ IL_DECLARE_RATE_INFO(11, 9, 12, 5, 12, 5, 18), /* 11mbps */ IL_DECLARE_RATE_INFO(6, 5, 9, 5, 11, 5, 11), /* 6mbps */ IL_DECLARE_RATE_INFO(9, 6, 11, 5, 11, 5, 11), /* 9mbps */ IL_DECLARE_RATE_INFO(12, 11, 18, 11, 18, 11, 18), /* 12mbps */ IL_DECLARE_RATE_INFO(18, 12, 24, 12, 24, 11, 24), /* 18mbps */ IL_DECLARE_RATE_INFO(24, 18, 36, 18, 36, 18, 36), /* 24mbps */ IL_DECLARE_RATE_INFO(36, 24, 48, 24, 48, 24, 48), /* 36mbps */ IL_DECLARE_RATE_INFO(48, 36, 54, 36, 54, 36, 54), /* 48mbps */ IL_DECLARE_RATE_INFO(54, 48, INV, 48, INV, 48, INV), /* 54mbps */ }; static inline u8 il3945_get_prev_ieee_rate(u8 rate_idx) { u8 rate = il3945_rates[rate_idx].prev_ieee; if (rate == RATE_INVALID) rate = rate_idx; return rate; } /* 1 = enable the il3945_disable_events() function */ #define IL_EVT_DISABLE (0) #define IL_EVT_DISABLE_SIZE (1532/32) /** * il3945_disable_events - Disable selected events in uCode event log * * Disable an event by writing "1"s into "disable" * bitmap in SRAM. Bit position corresponds to Event # (id/type). * Default values of 0 enable uCode events to be logged. * Use for only special debugging. This function is just a placeholder as-is, * you'll need to provide the special bits! ... * ... and set IL_EVT_DISABLE to 1. */ void il3945_disable_events(struct il_priv *il) { int i; u32 base; /* SRAM address of event log header */ u32 disable_ptr; /* SRAM address of event-disable bitmap array */ u32 array_size; /* # of u32 entries in array */ static const u32 evt_disable[IL_EVT_DISABLE_SIZE] = { 0x00000000, /* 31 - 0 Event id numbers */ 0x00000000, /* 63 - 32 */ 0x00000000, /* 95 - 64 */ 0x00000000, /* 127 - 96 */ 0x00000000, /* 159 - 128 */ 0x00000000, /* 191 - 160 */ 0x00000000, /* 223 - 192 */ 0x00000000, /* 255 - 224 */ 0x00000000, /* 287 - 256 */ 0x00000000, /* 319 - 288 */ 0x00000000, /* 351 - 320 */ 0x00000000, /* 383 - 352 */ 0x00000000, /* 415 - 384 */ 0x00000000, /* 447 - 416 */ 0x00000000, /* 479 - 448 */ 0x00000000, /* 511 - 480 */ 0x00000000, /* 543 - 512 */ 0x00000000, /* 575 - 544 */ 0x00000000, /* 607 - 576 */ 0x00000000, /* 639 - 608 */ 0x00000000, /* 671 - 640 */ 0x00000000, /* 703 - 672 */ 0x00000000, /* 735 - 704 */ 0x00000000, /* 767 - 736 */ 0x00000000, /* 799 - 768 */ 0x00000000, /* 831 - 800 */ 0x00000000, /* 863 - 832 */ 0x00000000, /* 895 - 864 */ 0x00000000, /* 927 - 896 */ 0x00000000, /* 959 - 928 */ 0x00000000, /* 991 - 960 */ 0x00000000, /* 1023 - 992 */ 0x00000000, /* 1055 - 1024 */ 0x00000000, /* 1087 - 1056 */ 0x00000000, /* 1119 - 1088 */ 0x00000000, /* 1151 - 1120 */ 0x00000000, /* 1183 - 1152 */ 0x00000000, /* 1215 - 1184 */ 0x00000000, /* 1247 - 1216 */ 0x00000000, /* 1279 - 1248 */ 0x00000000, /* 1311 - 1280 */ 0x00000000, /* 1343 - 1312 */ 0x00000000, /* 1375 - 1344 */ 0x00000000, /* 1407 - 1376 */ 0x00000000, /* 1439 - 1408 */ 0x00000000, /* 1471 - 1440 */ 0x00000000, /* 1503 - 1472 */ }; base = le32_to_cpu(il->card_alive.log_event_table_ptr); if (!il3945_hw_valid_rtc_data_addr(base)) { IL_ERR("Invalid event log pointer 0x%08X\n", base); return; } disable_ptr = il_read_targ_mem(il, base + (4 * sizeof(u32))); array_size = il_read_targ_mem(il, base + (5 * sizeof(u32))); if (IL_EVT_DISABLE && array_size == IL_EVT_DISABLE_SIZE) { D_INFO("Disabling selected uCode log events at 0x%x\n", disable_ptr); for (i = 0; i < IL_EVT_DISABLE_SIZE; i++) il_write_targ_mem(il, disable_ptr + (i * sizeof(u32)), evt_disable[i]); } else { D_INFO("Selected uCode log events may be disabled\n"); D_INFO(" by writing \"1\"s into disable bitmap\n"); D_INFO(" in SRAM at 0x%x, size %d u32s\n", disable_ptr, array_size); } } static int il3945_hwrate_to_plcp_idx(u8 plcp) { int idx; for (idx = 0; idx < RATE_COUNT_3945; idx++) if (il3945_rates[idx].plcp == plcp) return idx; return -1; } #ifdef CONFIG_IWLEGACY_DEBUG #define TX_STATUS_ENTRY(x) case TX_3945_STATUS_FAIL_ ## x: return #x static const char * il3945_get_tx_fail_reason(u32 status) { switch (status & TX_STATUS_MSK) { case TX_3945_STATUS_SUCCESS: return "SUCCESS"; TX_STATUS_ENTRY(SHORT_LIMIT); TX_STATUS_ENTRY(LONG_LIMIT); TX_STATUS_ENTRY(FIFO_UNDERRUN); TX_STATUS_ENTRY(MGMNT_ABORT); TX_STATUS_ENTRY(NEXT_FRAG); TX_STATUS_ENTRY(LIFE_EXPIRE); TX_STATUS_ENTRY(DEST_PS); TX_STATUS_ENTRY(ABORTED); TX_STATUS_ENTRY(BT_RETRY); TX_STATUS_ENTRY(STA_INVALID); TX_STATUS_ENTRY(FRAG_DROPPED); TX_STATUS_ENTRY(TID_DISABLE); TX_STATUS_ENTRY(FRAME_FLUSHED); TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL); TX_STATUS_ENTRY(TX_LOCKED); TX_STATUS_ENTRY(NO_BEACON_ON_RADAR); } return "UNKNOWN"; } #else static inline const char * il3945_get_tx_fail_reason(u32 status) { return ""; } #endif /* * get ieee prev rate from rate scale table. * for A and B mode we need to overright prev * value */ int il3945_rs_next_rate(struct il_priv *il, int rate) { int next_rate = il3945_get_prev_ieee_rate(rate); switch (il->band) { case IEEE80211_BAND_5GHZ: if (rate == RATE_12M_IDX) next_rate = RATE_9M_IDX; else if (rate == RATE_6M_IDX) next_rate = RATE_6M_IDX; break; case IEEE80211_BAND_2GHZ: if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) && il_is_associated(il)) { if (rate == RATE_11M_IDX) next_rate = RATE_5M_IDX; } break; default: break; } return next_rate; } /** * il3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd * * When FW advances 'R' idx, all entries between old and new 'R' idx * need to be reclaimed. As result, some free space forms. If there is * enough free space (> low mark), wake the stack that feeds us. */ static void il3945_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx) { struct il_tx_queue *txq = &il->txq[txq_id]; struct il_queue *q = &txq->q; struct sk_buff *skb; BUG_ON(txq_id == IL39_CMD_QUEUE_NUM); for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { skb = txq->skbs[txq->q.read_ptr]; ieee80211_tx_status_irqsafe(il->hw, skb); txq->skbs[txq->q.read_ptr] = NULL; il->ops->txq_free_tfd(il, txq); } if (il_queue_space(q) > q->low_mark && txq_id >= 0 && txq_id != IL39_CMD_QUEUE_NUM && il->mac80211_registered) il_wake_queue(il, txq); } /** * il3945_hdl_tx - Handle Tx response */ static void il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int idx = SEQ_TO_IDX(sequence); struct il_tx_queue *txq = &il->txq[txq_id]; struct ieee80211_tx_info *info; struct il3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le32_to_cpu(tx_resp->status); int rate_idx; int fail; if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " "is out of range [0-%d] %d %d\n", txq_id, idx, txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); return; } txq->time_stamp = jiffies; info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]); ieee80211_tx_info_clear_status(info); /* Fill the MRR chain with some info about on-chip retransmissions */ rate_idx = il3945_hwrate_to_plcp_idx(tx_resp->rate); if (info->band == IEEE80211_BAND_5GHZ) rate_idx -= IL_FIRST_OFDM_RATE; fail = tx_resp->failure_frame; info->status.rates[0].idx = rate_idx; info->status.rates[0].count = fail + 1; /* add final attempt */ /* tx_status->rts_retry_count = tx_resp->failure_rts; */ info->flags |= ((status & TX_STATUS_MSK) == TX_STATUS_SUCCESS) ? IEEE80211_TX_STAT_ACK : 0; D_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n", txq_id, il3945_get_tx_fail_reason(status), status, tx_resp->rate, tx_resp->failure_frame); D_TX_REPLY("Tx queue reclaim %d\n", idx); il3945_tx_queue_reclaim(il, txq_id, idx); if (status & TX_ABORT_REQUIRED_MSK) IL_ERR("TODO: Implement Tx ABORT REQUIRED!!!\n"); } /***************************************************************************** * * Intel PRO/Wireless 3945ABG/BG Network Connection * * RX handler implementations * *****************************************************************************/ #ifdef CONFIG_IWLEGACY_DEBUGFS static void il3945_accumulative_stats(struct il_priv *il, __le32 * stats) { int i; __le32 *prev_stats; u32 *accum_stats; u32 *delta, *max_delta; prev_stats = (__le32 *) &il->_3945.stats; accum_stats = (u32 *) &il->_3945.accum_stats; delta = (u32 *) &il->_3945.delta_stats; max_delta = (u32 *) &il->_3945.max_delta; for (i = sizeof(__le32); i < sizeof(struct il3945_notif_stats); i += sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, accum_stats++) { if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { *delta = (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); *accum_stats += *delta; if (*delta > *max_delta) *max_delta = *delta; } } /* reset accumulative stats for "no-counter" type stats */ il->_3945.accum_stats.general.temperature = il->_3945.stats.general.temperature; il->_3945.accum_stats.general.ttl_timestamp = il->_3945.stats.general.ttl_timestamp; } #endif void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); D_RX("Statistics notification received (%d vs %d).\n", (int)sizeof(struct il3945_notif_stats), le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK); #ifdef CONFIG_IWLEGACY_DEBUGFS il3945_accumulative_stats(il, (__le32 *) &pkt->u.raw); #endif memcpy(&il->_3945.stats, pkt->u.raw, sizeof(il->_3945.stats)); } void il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); __le32 *flag = (__le32 *) &pkt->u.raw; if (le32_to_cpu(*flag) & UCODE_STATS_CLEAR_MSK) { #ifdef CONFIG_IWLEGACY_DEBUGFS memset(&il->_3945.accum_stats, 0, sizeof(struct il3945_notif_stats)); memset(&il->_3945.delta_stats, 0, sizeof(struct il3945_notif_stats)); memset(&il->_3945.max_delta, 0, sizeof(struct il3945_notif_stats)); #endif D_RX("Statistics have been cleared\n"); } il3945_hdl_stats(il, rxb); } /****************************************************************************** * * Misc. internal state and helper functions * ******************************************************************************/ /* This is necessary only for a number of stats, see the caller. */ static int il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header) { /* Filter incoming packets to determine if they are targeted toward * this network, discarding packets coming from ourselves */ switch (il->iw_mode) { case NL80211_IFTYPE_ADHOC: /* Header: Dest. | Source | BSSID */ /* packets to our IBSS update information */ return ether_addr_equal(header->addr3, il->bssid); case NL80211_IFTYPE_STATION: /* Header: Dest. | AP{BSSID} | Source */ /* packets to our IBSS update information */ return ether_addr_equal(header->addr2, il->bssid); default: return 1; } } static void il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, struct ieee80211_rx_status *stats) { struct il_rx_pkt *pkt = rxb_addr(rxb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IL_RX_DATA(pkt); struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt); u16 len = le16_to_cpu(rx_hdr->len); struct sk_buff *skb; __le16 fc = hdr->frame_control; /* We received data from the HW, so stop the watchdog */ if (unlikely (len + IL39_RX_FRAME_SIZE > PAGE_SIZE << il->hw_params.rx_page_order)) { D_DROP("Corruption detected!\n"); return; } /* We only process data packets if the interface is open */ if (unlikely(!il->is_open)) { D_DROP("Dropping packet while interface is not open.\n"); return; } skb = dev_alloc_skb(128); if (!skb) { IL_ERR("dev_alloc_skb failed\n"); return; } if (!il3945_mod_params.sw_crypto) il_set_decrypted_flag(il, (struct ieee80211_hdr *)rxb_addr(rxb), le32_to_cpu(rx_end->status), stats); skb_add_rx_frag(skb, 0, rxb->page, (void *)rx_hdr->payload - (void *)pkt, len, len); il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); ieee80211_rx(il->hw, skb); il->alloc_rxb_page--; rxb->page = NULL; } #define IL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) static void il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) { struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; struct il_rx_pkt *pkt = rxb_addr(rxb); struct il3945_rx_frame_stats *rx_stats = IL_RX_STATS(pkt); struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt); u16 rx_stats_sig_avg __maybe_unused = le16_to_cpu(rx_stats->sig_avg); u16 rx_stats_noise_diff __maybe_unused = le16_to_cpu(rx_stats->noise_diff); u8 network_packet; rx_status.flag = 0; rx_status.mactime = le64_to_cpu(rx_end->timestamp); rx_status.band = (rx_hdr-> phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; rx_status.freq = ieee80211_channel_to_frequency(le16_to_cpu(rx_hdr->channel), rx_status.band); rx_status.rate_idx = il3945_hwrate_to_plcp_idx(rx_hdr->rate); if (rx_status.band == IEEE80211_BAND_5GHZ) rx_status.rate_idx -= IL_FIRST_OFDM_RATE; rx_status.antenna = (le16_to_cpu(rx_hdr->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; /* set the preamble flag if appropriate */ if (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) rx_status.flag |= RX_FLAG_SHORTPRE; if ((unlikely(rx_stats->phy_count > 20))) { D_DROP("dsp size out of range [0,20]: %d/n", rx_stats->phy_count); return; } if (!(rx_end->status & RX_RES_STATUS_NO_CRC32_ERROR) || !(rx_end->status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { D_RX("Bad CRC or FIFO: 0x%08X.\n", rx_end->status); return; } /* Convert 3945's rssi indicator to dBm */ rx_status.signal = rx_stats->rssi - IL39_RSSI_OFFSET; D_STATS("Rssi %d sig_avg %d noise_diff %d\n", rx_status.signal, rx_stats_sig_avg, rx_stats_noise_diff); header = (struct ieee80211_hdr *)IL_RX_DATA(pkt); network_packet = il3945_is_network_packet(il, header); D_STATS("[%c] %d RSSI:%d Signal:%u, Rate:%u\n", network_packet ? '*' : ' ', le16_to_cpu(rx_hdr->channel), rx_status.signal, rx_status.signal, rx_status.rate_idx); if (network_packet) { il->_3945.last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp); il->_3945.last_tsf = le64_to_cpu(rx_end->timestamp); il->_3945.last_rx_rssi = rx_status.signal; } il3945_pass_packet_to_mac80211(il, rxb, &rx_status); } int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset, u8 pad) { int count; struct il_queue *q; struct il3945_tfd *tfd, *tfd_tmp; q = &txq->q; tfd_tmp = (struct il3945_tfd *)txq->tfds; tfd = &tfd_tmp[q->write_ptr]; if (reset) memset(tfd, 0, sizeof(*tfd)); count = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags)); if (count >= NUM_TFD_CHUNKS || count < 0) { IL_ERR("Error can not send more than %d chunks\n", NUM_TFD_CHUNKS); return -EINVAL; } tfd->tbs[count].addr = cpu_to_le32(addr); tfd->tbs[count].len = cpu_to_le32(len); count++; tfd->control_flags = cpu_to_le32(TFD_CTL_COUNT_SET(count) | TFD_CTL_PAD_SET(pad)); return 0; } /** * il3945_hw_txq_free_tfd - Free one TFD, those at idx [txq->q.read_ptr] * * Does NOT advance any idxes */ void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq) { struct il3945_tfd *tfd_tmp = (struct il3945_tfd *)txq->tfds; int idx = txq->q.read_ptr; struct il3945_tfd *tfd = &tfd_tmp[idx]; struct pci_dev *dev = il->pci_dev; int i; int counter; /* sanity check */ counter = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags)); if (counter > NUM_TFD_CHUNKS) { IL_ERR("Too many chunks: %i\n", counter); /* @todo issue fatal error, it is quite serious situation */ return; } /* Unmap tx_cmd */ if (counter) pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping), dma_unmap_len(&txq->meta[idx], len), PCI_DMA_TODEVICE); /* unmap chunks if any */ for (i = 1; i < counter; i++) pci_unmap_single(dev, le32_to_cpu(tfd->tbs[i].addr), le32_to_cpu(tfd->tbs[i].len), PCI_DMA_TODEVICE); /* free SKB */ if (txq->skbs) { struct sk_buff *skb = txq->skbs[txq->q.read_ptr]; /* can be called from irqs-disabled context */ if (skb) { dev_kfree_skb_any(skb); txq->skbs[txq->q.read_ptr] = NULL; } } } /** * il3945_hw_build_tx_cmd_rate - Add rate portion to TX_CMD: * */ void il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, int sta_id) { u16 hw_value = ieee80211_get_tx_rate(il->hw, info)->hw_value; u16 rate_idx = min(hw_value & 0xffff, RATE_COUNT_3945 - 1); u16 rate_mask; int rate; const u8 rts_retry_limit = 7; u8 data_retry_limit; __le32 tx_flags; __le16 fc = hdr->frame_control; struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; rate = il3945_rates[rate_idx].plcp; tx_flags = tx_cmd->tx_flags; /* We need to figure out how to get the sta->supp_rates while * in this running context */ rate_mask = RATES_MASK_3945; /* Set retry limit on DATA packets and Probe Responses */ if (ieee80211_is_probe_resp(fc)) data_retry_limit = 3; else data_retry_limit = IL_DEFAULT_TX_RETRY; tx_cmd->data_retry_limit = data_retry_limit; /* Set retry limit on RTS packets */ tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit); tx_cmd->rate = rate; tx_cmd->tx_flags = tx_flags; /* OFDM */ tx_cmd->supp_rates[0] = ((rate_mask & IL_OFDM_RATES_MASK) >> IL_FIRST_OFDM_RATE) & 0xFF; /* CCK */ tx_cmd->supp_rates[1] = (rate_mask & 0xF); D_RATE("Tx sta id: %d, rate: %d (plcp), flags: 0x%4X " "cck/ofdm mask: 0x%x/0x%x\n", sta_id, tx_cmd->rate, le32_to_cpu(tx_cmd->tx_flags), tx_cmd->supp_rates[1], tx_cmd->supp_rates[0]); } static u8 il3945_sync_sta(struct il_priv *il, int sta_id, u16 tx_rate) { unsigned long flags_spin; struct il_station_entry *station; if (sta_id == IL_INVALID_STATION) return IL_INVALID_STATION; spin_lock_irqsave(&il->sta_lock, flags_spin); station = &il->stations[sta_id]; station->sta.sta.modify_mask = STA_MODIFY_TX_RATE_MSK; station->sta.rate_n_flags = cpu_to_le16(tx_rate); station->sta.mode = STA_CONTROL_MODIFY_MSK; il_send_add_sta(il, &station->sta, CMD_ASYNC); spin_unlock_irqrestore(&il->sta_lock, flags_spin); D_RATE("SCALE sync station %d to rate %d\n", sta_id, tx_rate); return sta_id; } static void il3945_set_pwr_vmain(struct il_priv *il) { /* * (for documentation purposes) * to set power to V_AUX, do if (pci_pme_capable(il->pci_dev, PCI_D3cold)) { il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VAUX, ~APMG_PS_CTRL_MSK_PWR_SRC); _il_poll_bit(il, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VAUX_PWR_SRC, CSR_GPIO_IN_BIT_AUX_POWER, 5000); } */ il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, ~APMG_PS_CTRL_MSK_PWR_SRC); _il_poll_bit(il, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC, CSR_GPIO_IN_BIT_AUX_POWER, 5000); } static int il3945_rx_init(struct il_priv *il, struct il_rx_queue *rxq) { il_wr(il, FH39_RCSR_RBD_BASE(0), rxq->bd_dma); il_wr(il, FH39_RCSR_RPTR_ADDR(0), rxq->rb_stts_dma); il_wr(il, FH39_RCSR_WPTR(0), 0); il_wr(il, FH39_RCSR_CONFIG(0), FH39_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE | FH39_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE | FH39_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN | FH39_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 | (RX_QUEUE_SIZE_LOG << FH39_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE) | FH39_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST | (1 << FH39_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH) | FH39_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH); /* fake read to flush all prev I/O */ il_rd(il, FH39_RSSR_CTRL); return 0; } static int il3945_tx_reset(struct il_priv *il) { /* bypass mode */ il_wr_prph(il, ALM_SCD_MODE_REG, 0x2); /* RA 0 is active */ il_wr_prph(il, ALM_SCD_ARASTAT_REG, 0x01); /* all 6 fifo are active */ il_wr_prph(il, ALM_SCD_TXFACT_REG, 0x3f); il_wr_prph(il, ALM_SCD_SBYP_MODE_1_REG, 0x010000); il_wr_prph(il, ALM_SCD_SBYP_MODE_2_REG, 0x030002); il_wr_prph(il, ALM_SCD_TXF4MF_REG, 0x000004); il_wr_prph(il, ALM_SCD_TXF5MF_REG, 0x000005); il_wr(il, FH39_TSSR_CBB_BASE, il->_3945.shared_phys); il_wr(il, FH39_TSSR_MSG_CONFIG, FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH); return 0; } /** * il3945_txq_ctx_reset - Reset TX queue context * * Destroys all DMA structures and initialize them again */ static int il3945_txq_ctx_reset(struct il_priv *il) { int rc, txq_id; il3945_hw_txq_ctx_free(il); /* allocate tx queue structure */ rc = il_alloc_txq_mem(il); if (rc) return rc; /* Tx CMD queue */ rc = il3945_tx_reset(il); if (rc) goto error; /* Tx queue(s) */ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { rc = il_tx_queue_init(il, txq_id); if (rc) { IL_ERR("Tx %d queue init failed\n", txq_id); goto error; } } return rc; error: il3945_hw_txq_ctx_free(il); return rc; } /* * Start up 3945's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via il_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor */ static int il3945_apm_init(struct il_priv *il) { int ret = il_apm_init(il); /* Clear APMG (NIC's internal power management) interrupts */ il_wr_prph(il, APMG_RTC_INT_MSK_REG, 0x0); il_wr_prph(il, APMG_RTC_INT_STT_REG, 0xFFFFFFFF); /* Reset radio chip */ il_set_bits_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); udelay(5); il_clear_bits_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); return ret; } static void il3945_nic_config(struct il_priv *il) { struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; unsigned long flags; u8 rev_id = il->pci_dev->revision; spin_lock_irqsave(&il->lock, flags); /* Determine HW type */ D_INFO("HW Revision ID = 0x%X\n", rev_id); if (rev_id & PCI_CFG_REV_ID_BIT_RTP) D_INFO("RTP type\n"); else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) { D_INFO("3945 RADIO-MB type\n"); il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_3945_MB); } else { D_INFO("3945 RADIO-MM type\n"); il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_3945_MM); } if (EEPROM_SKU_CAP_OP_MODE_MRC == eeprom->sku_cap) { D_INFO("SKU OP mode is mrc\n"); il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC); } else D_INFO("SKU OP mode is basic\n"); if ((eeprom->board_revision & 0xF0) == 0xD0) { D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision); il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); } else { D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision); il_clear_bit(il, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); } if (eeprom->almgor_m_version <= 1) { il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A); D_INFO("Card M type A version is 0x%X\n", eeprom->almgor_m_version); } else { D_INFO("Card M type B version is 0x%X\n", eeprom->almgor_m_version); il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B); } spin_unlock_irqrestore(&il->lock, flags); if (eeprom->sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE) D_RF_KILL("SW RF KILL supported in EEPROM.\n"); if (eeprom->sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE) D_RF_KILL("HW RF KILL supported in EEPROM.\n"); } int il3945_hw_nic_init(struct il_priv *il) { int rc; unsigned long flags; struct il_rx_queue *rxq = &il->rxq; spin_lock_irqsave(&il->lock, flags); il3945_apm_init(il); spin_unlock_irqrestore(&il->lock, flags); il3945_set_pwr_vmain(il); il3945_nic_config(il); /* Allocate the RX queue, or reset if it is already allocated */ if (!rxq->bd) { rc = il_rx_queue_alloc(il); if (rc) { IL_ERR("Unable to initialize Rx queue\n"); return -ENOMEM; } } else il3945_rx_queue_reset(il, rxq); il3945_rx_replenish(il); il3945_rx_init(il, rxq); /* Look at using this instead: rxq->need_update = 1; il_rx_queue_update_write_ptr(il, rxq); */ il_wr(il, FH39_RCSR_WPTR(0), rxq->write & ~7); rc = il3945_txq_ctx_reset(il); if (rc) return rc; set_bit(S_INIT, &il->status); return 0; } /** * il3945_hw_txq_ctx_free - Free TXQ Context * * Destroy all TX DMA queues and structures */ void il3945_hw_txq_ctx_free(struct il_priv *il) { int txq_id; /* Tx queues */ if (il->txq) for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) if (txq_id == IL39_CMD_QUEUE_NUM) il_cmd_queue_free(il); else il_tx_queue_free(il, txq_id); /* free tx queue structure */ il_free_txq_mem(il); } void il3945_hw_txq_ctx_stop(struct il_priv *il) { int txq_id; /* stop SCD */ _il_wr_prph(il, ALM_SCD_MODE_REG, 0); _il_wr_prph(il, ALM_SCD_TXFACT_REG, 0); /* reset TFD queues */ for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { _il_wr(il, FH39_TCSR_CONFIG(txq_id), 0x0); _il_poll_bit(il, FH39_TSSR_TX_STATUS, FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), 1000); } } /** * il3945_hw_reg_adjust_power_by_temp * return idx delta into power gain settings table */ static int il3945_hw_reg_adjust_power_by_temp(int new_reading, int old_reading) { return (new_reading - old_reading) * (-11) / 100; } /** * il3945_hw_reg_temp_out_of_range - Keep temperature in sane range */ static inline int il3945_hw_reg_temp_out_of_range(int temperature) { return (temperature < -260 || temperature > 25) ? 1 : 0; } int il3945_hw_get_temperature(struct il_priv *il) { return _il_rd(il, CSR_UCODE_DRV_GP2); } /** * il3945_hw_reg_txpower_get_temperature * get the current temperature by reading from NIC */ static int il3945_hw_reg_txpower_get_temperature(struct il_priv *il) { struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; int temperature; temperature = il3945_hw_get_temperature(il); /* driver's okay range is -260 to +25. * human readable okay range is 0 to +285 */ D_INFO("Temperature: %d\n", temperature + IL_TEMP_CONVERT); /* handle insane temp reading */ if (il3945_hw_reg_temp_out_of_range(temperature)) { IL_ERR("Error bad temperature value %d\n", temperature); /* if really really hot(?), * substitute the 3rd band/group's temp measured at factory */ if (il->last_temperature > 100) temperature = eeprom->groups[2].temperature; else /* else use most recent "sane" value from driver */ temperature = il->last_temperature; } return temperature; /* raw, not "human readable" */ } /* Adjust Txpower only if temperature variance is greater than threshold. * * Both are lower than older versions' 9 degrees */ #define IL_TEMPERATURE_LIMIT_TIMER 6 /** * il3945_is_temp_calib_needed - determines if new calibration is needed * * records new temperature in tx_mgr->temperature. * replaces tx_mgr->last_temperature *only* if calib needed * (assumes caller will actually do the calibration!). */ static int il3945_is_temp_calib_needed(struct il_priv *il) { int temp_diff; il->temperature = il3945_hw_reg_txpower_get_temperature(il); temp_diff = il->temperature - il->last_temperature; /* get absolute value */ if (temp_diff < 0) { D_POWER("Getting cooler, delta %d,\n", temp_diff); temp_diff = -temp_diff; } else if (temp_diff == 0) D_POWER("Same temp,\n"); else D_POWER("Getting warmer, delta %d,\n", temp_diff); /* if we don't need calibration, *don't* update last_temperature */ if (temp_diff < IL_TEMPERATURE_LIMIT_TIMER) { D_POWER("Timed thermal calib not needed\n"); return 0; } D_POWER("Timed thermal calib needed\n"); /* assume that caller will actually do calib ... * update the "last temperature" value */ il->last_temperature = il->temperature; return 1; } #define IL_MAX_GAIN_ENTRIES 78 #define IL_CCK_FROM_OFDM_POWER_DIFF -5 #define IL_CCK_FROM_OFDM_IDX_DIFF (10) /* radio and DSP power table, each step is 1/2 dB. * 1st number is for RF analog gain, 2nd number is for DSP pre-DAC gain. */ static struct il3945_tx_power power_gain_table[2][IL_MAX_GAIN_ENTRIES] = { { {251, 127}, /* 2.4 GHz, highest power */ {251, 127}, {251, 127}, {251, 127}, {251, 125}, {251, 110}, {251, 105}, {251, 98}, {187, 125}, {187, 115}, {187, 108}, {187, 99}, {243, 119}, {243, 111}, {243, 105}, {243, 97}, {243, 92}, {211, 106}, {211, 100}, {179, 120}, {179, 113}, {179, 107}, {147, 125}, {147, 119}, {147, 112}, {147, 106}, {147, 101}, {147, 97}, {147, 91}, {115, 107}, {235, 121}, {235, 115}, {235, 109}, {203, 127}, {203, 121}, {203, 115}, {203, 108}, {203, 102}, {203, 96}, {203, 92}, {171, 110}, {171, 104}, {171, 98}, {139, 116}, {227, 125}, {227, 119}, {227, 113}, {227, 107}, {227, 101}, {227, 96}, {195, 113}, {195, 106}, {195, 102}, {195, 95}, {163, 113}, {163, 106}, {163, 102}, {163, 95}, {131, 113}, {131, 106}, {131, 102}, {131, 95}, {99, 113}, {99, 106}, {99, 102}, {99, 95}, {67, 113}, {67, 106}, {67, 102}, {67, 95}, {35, 113}, {35, 106}, {35, 102}, {35, 95}, {3, 113}, {3, 106}, {3, 102}, {3, 95} /* 2.4 GHz, lowest power */ }, { {251, 127}, /* 5.x GHz, highest power */ {251, 120}, {251, 114}, {219, 119}, {219, 101}, {187, 113}, {187, 102}, {155, 114}, {155, 103}, {123, 117}, {123, 107}, {123, 99}, {123, 92}, {91, 108}, {59, 125}, {59, 118}, {59, 109}, {59, 102}, {59, 96}, {59, 90}, {27, 104}, {27, 98}, {27, 92}, {115, 118}, {115, 111}, {115, 104}, {83, 126}, {83, 121}, {83, 113}, {83, 105}, {83, 99}, {51, 118}, {51, 111}, {51, 104}, {51, 98}, {19, 116}, {19, 109}, {19, 102}, {19, 98}, {19, 93}, {171, 113}, {171, 107}, {171, 99}, {139, 120}, {139, 113}, {139, 107}, {139, 99}, {107, 120}, {107, 113}, {107, 107}, {107, 99}, {75, 120}, {75, 113}, {75, 107}, {75, 99}, {43, 120}, {43, 113}, {43, 107}, {43, 99}, {11, 120}, {11, 113}, {11, 107}, {11, 99}, {131, 107}, {131, 99}, {99, 120}, {99, 113}, {99, 107}, {99, 99}, {67, 120}, {67, 113}, {67, 107}, {67, 99}, {35, 120}, {35, 113}, {35, 107}, {35, 99}, {3, 120} /* 5.x GHz, lowest power */ } }; static inline u8 il3945_hw_reg_fix_power_idx(int idx) { if (idx < 0) return 0; if (idx >= IL_MAX_GAIN_ENTRIES) return IL_MAX_GAIN_ENTRIES - 1; return (u8) idx; } /* Kick off thermal recalibration check every 60 seconds */ #define REG_RECALIB_PERIOD (60) /** * il3945_hw_reg_set_scan_power - Set Tx power for scan probe requests * * Set (in our channel info database) the direct scan Tx power for 1 Mbit (CCK) * or 6 Mbit (OFDM) rates. */ static void il3945_hw_reg_set_scan_power(struct il_priv *il, u32 scan_tbl_idx, s32 rate_idx, const s8 *clip_pwrs, struct il_channel_info *ch_info, int band_idx) { struct il3945_scan_power_info *scan_power_info; s8 power; u8 power_idx; scan_power_info = &ch_info->scan_pwr_info[scan_tbl_idx]; /* use this channel group's 6Mbit clipping/saturation pwr, * but cap at regulatory scan power restriction (set during init * based on eeprom channel data) for this channel. */ power = min(ch_info->scan_power, clip_pwrs[RATE_6M_IDX_TBL]); power = min(power, il->tx_power_user_lmt); scan_power_info->requested_power = power; /* find difference between new scan *power* and current "normal" * Tx *power* for 6Mb. Use this difference (x2) to adjust the * current "normal" temperature-compensated Tx power *idx* for * this rate (1Mb or 6Mb) to yield new temp-compensated scan power * *idx*. */ power_idx = ch_info->power_info[rate_idx].power_table_idx - (power - ch_info-> power_info [RATE_6M_IDX_TBL]. requested_power) * 2; /* store reference idx that we use when adjusting *all* scan * powers. So we can accommodate user (all channel) or spectrum * management (single channel) power changes "between" temperature * feedback compensation procedures. * don't force fit this reference idx into gain table; it may be a * negative number. This will help avoid errors when we're at * the lower bounds (highest gains, for warmest temperatures) * of the table. */ /* don't exceed table bounds for "real" setting */ power_idx = il3945_hw_reg_fix_power_idx(power_idx); scan_power_info->power_table_idx = power_idx; scan_power_info->tpc.tx_gain = power_gain_table[band_idx][power_idx].tx_gain; scan_power_info->tpc.dsp_atten = power_gain_table[band_idx][power_idx].dsp_atten; } /** * il3945_send_tx_power - fill in Tx Power command with gain settings * * Configures power settings for all rates for the current channel, * using values from channel info struct, and send to NIC */ static int il3945_send_tx_power(struct il_priv *il) { int rate_idx, i; const struct il_channel_info *ch_info = NULL; struct il3945_txpowertable_cmd txpower = { .channel = il->active.channel, }; u16 chan; if (WARN_ONCE (test_bit(S_SCAN_HW, &il->status), "TX Power requested while scanning!\n")) return -EAGAIN; chan = le16_to_cpu(il->active.channel); txpower.band = (il->band == IEEE80211_BAND_5GHZ) ? 0 : 1; ch_info = il_get_channel_info(il, il->band, chan); if (!ch_info) { IL_ERR("Failed to get channel info for channel %d [%d]\n", chan, il->band); return -EINVAL; } if (!il_is_channel_valid(ch_info)) { D_POWER("Not calling TX_PWR_TBL_CMD on " "non-Tx channel.\n"); return 0; } /* fill cmd with power settings for all rates for current channel */ /* Fill OFDM rate */ for (rate_idx = IL_FIRST_OFDM_RATE, i = 0; rate_idx <= IL39_LAST_OFDM_RATE; rate_idx++, i++) { txpower.power[i].tpc = ch_info->power_info[i].tpc; txpower.power[i].rate = il3945_rates[rate_idx].plcp; D_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", le16_to_cpu(txpower.channel), txpower.band, txpower.power[i].tpc.tx_gain, txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); } /* Fill CCK rates */ for (rate_idx = IL_FIRST_CCK_RATE; rate_idx <= IL_LAST_CCK_RATE; rate_idx++, i++) { txpower.power[i].tpc = ch_info->power_info[i].tpc; txpower.power[i].rate = il3945_rates[rate_idx].plcp; D_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", le16_to_cpu(txpower.channel), txpower.band, txpower.power[i].tpc.tx_gain, txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); } return il_send_cmd_pdu(il, C_TX_PWR_TBL, sizeof(struct il3945_txpowertable_cmd), &txpower); } /** * il3945_hw_reg_set_new_power - Configures power tables at new levels * @ch_info: Channel to update. Uses power_info.requested_power. * * Replace requested_power and base_power_idx ch_info fields for * one channel. * * Called if user or spectrum management changes power preferences. * Takes into account h/w and modulation limitations (clip power). * * This does *not* send anything to NIC, just sets up ch_info for one channel. * * NOTE: reg_compensate_for_temperature_dif() *must* be run after this to * properly fill out the scan powers, and actual h/w gain settings, * and send changes to NIC */ static int il3945_hw_reg_set_new_power(struct il_priv *il, struct il_channel_info *ch_info) { struct il3945_channel_power_info *power_info; int power_changed = 0; int i; const s8 *clip_pwrs; int power; /* Get this chnlgrp's rate-to-max/clip-powers table */ clip_pwrs = il->_3945.clip_groups[ch_info->group_idx].clip_powers; /* Get this channel's rate-to-current-power settings table */ power_info = ch_info->power_info; /* update OFDM Txpower settings */ for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++, ++power_info) { int delta_idx; /* limit new power to be no more than h/w capability */ power = min(ch_info->curr_txpow, clip_pwrs[i]); if (power == power_info->requested_power) continue; /* find difference between old and new requested powers, * update base (non-temp-compensated) power idx */ delta_idx = (power - power_info->requested_power) * 2; power_info->base_power_idx -= delta_idx; /* save new requested power value */ power_info->requested_power = power; power_changed = 1; } /* update CCK Txpower settings, based on OFDM 12M setting ... * ... all CCK power settings for a given channel are the *same*. */ if (power_changed) { power = ch_info->power_info[RATE_12M_IDX_TBL].requested_power + IL_CCK_FROM_OFDM_POWER_DIFF; /* do all CCK rates' il3945_channel_power_info structures */ for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) { power_info->requested_power = power; power_info->base_power_idx = ch_info->power_info[RATE_12M_IDX_TBL]. base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF; ++power_info; } } return 0; } /** * il3945_hw_reg_get_ch_txpower_limit - returns new power limit for channel * * NOTE: Returned power limit may be less (but not more) than requested, * based strictly on regulatory (eeprom and spectrum mgt) limitations * (no consideration for h/w clipping limitations). */ static int il3945_hw_reg_get_ch_txpower_limit(struct il_channel_info *ch_info) { s8 max_power; #if 0 /* if we're using TGd limits, use lower of TGd or EEPROM */ if (ch_info->tgd_data.max_power != 0) max_power = min(ch_info->tgd_data.max_power, ch_info->eeprom.max_power_avg); /* else just use EEPROM limits */ else #endif max_power = ch_info->eeprom.max_power_avg; return min(max_power, ch_info->max_power_avg); } /** * il3945_hw_reg_comp_txpower_temp - Compensate for temperature * * Compensate txpower settings of *all* channels for temperature. * This only accounts for the difference between current temperature * and the factory calibration temperatures, and bases the new settings * on the channel's base_power_idx. * * If RxOn is "associated", this sends the new Txpower to NIC! */ static int il3945_hw_reg_comp_txpower_temp(struct il_priv *il) { struct il_channel_info *ch_info = NULL; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; int delta_idx; const s8 *clip_pwrs; /* array of h/w max power levels for each rate */ u8 a_band; u8 rate_idx; u8 scan_tbl_idx; u8 i; int ref_temp; int temperature = il->temperature; if (il->disable_tx_power_cal || test_bit(S_SCANNING, &il->status)) { /* do not perform tx power calibration */ return 0; } /* set up new Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0; i < il->channel_count; i++) { ch_info = &il->channel_info[i]; a_band = il_is_channel_a_band(ch_info); /* Get this chnlgrp's factory calibration temperature */ ref_temp = (s16) eeprom->groups[ch_info->group_idx].temperature; /* get power idx adjustment based on current and factory * temps */ delta_idx = il3945_hw_reg_adjust_power_by_temp(temperature, ref_temp); /* set tx power value for all rates, OFDM and CCK */ for (rate_idx = 0; rate_idx < RATE_COUNT_3945; rate_idx++) { int power_idx = ch_info->power_info[rate_idx].base_power_idx; /* temperature compensate */ power_idx += delta_idx; /* stay within table range */ power_idx = il3945_hw_reg_fix_power_idx(power_idx); ch_info->power_info[rate_idx].power_table_idx = (u8) power_idx; ch_info->power_info[rate_idx].tpc = power_gain_table[a_band][power_idx]; } /* Get this chnlgrp's rate-to-max/clip-powers table */ clip_pwrs = il->_3945.clip_groups[ch_info->group_idx].clip_powers; /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES; scan_tbl_idx++) { s32 actual_idx = (scan_tbl_idx == 0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL; il3945_hw_reg_set_scan_power(il, scan_tbl_idx, actual_idx, clip_pwrs, ch_info, a_band); } } /* send Txpower command for current channel to ucode */ return il->ops->send_tx_power(il); } int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power) { struct il_channel_info *ch_info; s8 max_power; u8 a_band; u8 i; if (il->tx_power_user_lmt == power) { D_POWER("Requested Tx power same as current " "limit: %ddBm.\n", power); return 0; } D_POWER("Setting upper limit clamp to %ddBm.\n", power); il->tx_power_user_lmt = power; /* set up new Tx powers for each and every channel, 2.4 and 5.x */ for (i = 0; i < il->channel_count; i++) { ch_info = &il->channel_info[i]; a_band = il_is_channel_a_band(ch_info); /* find minimum power of all user and regulatory constraints * (does not consider h/w clipping limitations) */ max_power = il3945_hw_reg_get_ch_txpower_limit(ch_info); max_power = min(power, max_power); if (max_power != ch_info->curr_txpow) { ch_info->curr_txpow = max_power; /* this considers the h/w clipping limitations */ il3945_hw_reg_set_new_power(il, ch_info); } } /* update txpower settings for all channels, * send to NIC if associated. */ il3945_is_temp_calib_needed(il); il3945_hw_reg_comp_txpower_temp(il); return 0; } static int il3945_send_rxon_assoc(struct il_priv *il) { int rc = 0; struct il_rx_pkt *pkt; struct il3945_rxon_assoc_cmd rxon_assoc; struct il_host_cmd cmd = { .id = C_RXON_ASSOC, .len = sizeof(rxon_assoc), .flags = CMD_WANT_SKB, .data = &rxon_assoc, }; const struct il_rxon_cmd *rxon1 = &il->staging; const struct il_rxon_cmd *rxon2 = &il->active; if (rxon1->flags == rxon2->flags && rxon1->filter_flags == rxon2->filter_flags && rxon1->cck_basic_rates == rxon2->cck_basic_rates && rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { D_INFO("Using current RXON_ASSOC. Not resending.\n"); return 0; } rxon_assoc.flags = il->staging.flags; rxon_assoc.filter_flags = il->staging.filter_flags; rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; rxon_assoc.reserved = 0; rc = il_send_cmd_sync(il, &cmd); if (rc) return rc; pkt = (struct il_rx_pkt *)cmd.reply_page; if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { IL_ERR("Bad return from C_RXON_ASSOC command\n"); rc = -EIO; } il_free_pages(il, cmd.reply_page); return rc; } /** * il3945_commit_rxon - commit staging_rxon to hardware * * The RXON command in staging_rxon is committed to the hardware and * the active_rxon structure is updated with the new data. This * function correctly transitions out of the RXON_ASSOC_MSK state if * a HW tune is required based on the RXON structure changes. */ int il3945_commit_rxon(struct il_priv *il) { /* cast away the const for active_rxon in this function */ struct il3945_rxon_cmd *active_rxon = (void *)&il->active; struct il3945_rxon_cmd *staging_rxon = (void *)&il->staging; int rc = 0; bool new_assoc = !!(staging_rxon->filter_flags & RXON_FILTER_ASSOC_MSK); if (test_bit(S_EXIT_PENDING, &il->status)) return -EINVAL; if (!il_is_alive(il)) return -1; /* always get timestamp with Rx frame */ staging_rxon->flags |= RXON_FLG_TSF2HOST_MSK; /* select antenna */ staging_rxon->flags &= ~(RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_SEL_MSK); staging_rxon->flags |= il3945_get_antenna_flags(il); rc = il_check_rxon_cmd(il); if (rc) { IL_ERR("Invalid RXON configuration. Not committing.\n"); return -EINVAL; } /* If we don't need to send a full RXON, we can use * il3945_rxon_assoc_cmd which is used to reconfigure filter * and other flags for the current radio configuration. */ if (!il_full_rxon_required(il)) { rc = il_send_rxon_assoc(il); if (rc) { IL_ERR("Error setting RXON_ASSOC " "configuration (%d).\n", rc); return rc; } memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); /* * We do not commit tx power settings while channel changing, * do it now if tx power changed. */ il_set_tx_power(il, il->tx_power_next, false); return 0; } /* If we are currently associated and the new config requires * an RXON_ASSOC and the new config wants the associated mask enabled, * we must clear the associated from the active configuration * before we apply the new config */ if (il_is_associated(il) && new_assoc) { D_INFO("Toggling associated bit on current RXON\n"); active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; /* * reserved4 and 5 could have been filled by the iwlcore code. * Let's clear them before pushing to the 3945. */ active_rxon->reserved4 = 0; active_rxon->reserved5 = 0; rc = il_send_cmd_pdu(il, C_RXON, sizeof(struct il3945_rxon_cmd), &il->active); /* If the mask clearing failed then we set * active_rxon back to what it was previously */ if (rc) { active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; IL_ERR("Error clearing ASSOC_MSK on current " "configuration (%d).\n", rc); return rc; } il_clear_ucode_stations(il); il_restore_stations(il); } D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), le16_to_cpu(staging_rxon->channel), staging_rxon->bssid_addr); /* * reserved4 and 5 could have been filled by the iwlcore code. * Let's clear them before pushing to the 3945. */ staging_rxon->reserved4 = 0; staging_rxon->reserved5 = 0; il_set_rxon_hwcrypto(il, !il3945_mod_params.sw_crypto); /* Apply the new configuration */ rc = il_send_cmd_pdu(il, C_RXON, sizeof(struct il3945_rxon_cmd), staging_rxon); if (rc) { IL_ERR("Error setting new configuration (%d).\n", rc); return rc; } memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); if (!new_assoc) { il_clear_ucode_stations(il); il_restore_stations(il); } /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ rc = il_set_tx_power(il, il->tx_power_next, true); if (rc) { IL_ERR("Error setting Tx power (%d).\n", rc); return rc; } /* Init the hardware's rate fallback order based on the band */ rc = il3945_init_hw_rate_table(il); if (rc) { IL_ERR("Error setting HW rate table: %02X\n", rc); return -EIO; } return 0; } /** * il3945_reg_txpower_periodic - called when time to check our temperature. * * -- reset periodic timer * -- see if temp has changed enough to warrant re-calibration ... if so: * -- correct coeffs for temp (can reset temp timer) * -- save this temp as "last", * -- send new set of gain settings to NIC * NOTE: This should continue working, even when we're not associated, * so we can keep our internal table of scan powers current. */ void il3945_reg_txpower_periodic(struct il_priv *il) { /* This will kick in the "brute force" * il3945_hw_reg_comp_txpower_temp() below */ if (!il3945_is_temp_calib_needed(il)) goto reschedule; /* Set up a new set of temp-adjusted TxPowers, send to NIC. * This is based *only* on current temperature, * ignoring any previous power measurements */ il3945_hw_reg_comp_txpower_temp(il); reschedule: queue_delayed_work(il->workqueue, &il->_3945.thermal_periodic, REG_RECALIB_PERIOD * HZ); } static void il3945_bg_reg_txpower_periodic(struct work_struct *work) { struct il_priv *il = container_of(work, struct il_priv, _3945.thermal_periodic.work); mutex_lock(&il->mutex); if (test_bit(S_EXIT_PENDING, &il->status) || il->txq == NULL) goto out; il3945_reg_txpower_periodic(il); out: mutex_unlock(&il->mutex); } /** * il3945_hw_reg_get_ch_grp_idx - find the channel-group idx (0-4) for channel. * * This function is used when initializing channel-info structs. * * NOTE: These channel groups do *NOT* match the bands above! * These channel groups are based on factory-tested channels; * on A-band, EEPROM's "group frequency" entries represent the top * channel in each group 1-4. Group 5 All B/G channels are in group 0. */ static u16 il3945_hw_reg_get_ch_grp_idx(struct il_priv *il, const struct il_channel_info *ch_info) { struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; struct il3945_eeprom_txpower_group *ch_grp = &eeprom->groups[0]; u8 group; u16 group_idx = 0; /* based on factory calib frequencies */ u8 grp_channel; /* Find the group idx for the channel ... don't use idx 1(?) */ if (il_is_channel_a_band(ch_info)) { for (group = 1; group < 5; group++) { grp_channel = ch_grp[group].group_channel; if (ch_info->channel <= grp_channel) { group_idx = group; break; } } /* group 4 has a few channels *above* its factory cal freq */ if (group == 5) group_idx = 4; } else group_idx = 0; /* 2.4 GHz, group 0 */ D_POWER("Chnl %d mapped to grp %d\n", ch_info->channel, group_idx); return group_idx; } /** * il3945_hw_reg_get_matched_power_idx - Interpolate to get nominal idx * * Interpolate to get nominal (i.e. at factory calibration temperature) idx * into radio/DSP gain settings table for requested power. */ static int il3945_hw_reg_get_matched_power_idx(struct il_priv *il, s8 requested_power, s32 setting_idx, s32 *new_idx) { const struct il3945_eeprom_txpower_group *chnl_grp = NULL; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; s32 idx0, idx1; s32 power = 2 * requested_power; s32 i; const struct il3945_eeprom_txpower_sample *samples; s32 gains0, gains1; s32 res; s32 denominator; chnl_grp = &eeprom->groups[setting_idx]; samples = chnl_grp->samples; for (i = 0; i < 5; i++) { if (power == samples[i].power) { *new_idx = samples[i].gain_idx; return 0; } } if (power > samples[1].power) { idx0 = 0; idx1 = 1; } else if (power > samples[2].power) { idx0 = 1; idx1 = 2; } else if (power > samples[3].power) { idx0 = 2; idx1 = 3; } else { idx0 = 3; idx1 = 4; } denominator = (s32) samples[idx1].power - (s32) samples[idx0].power; if (denominator == 0) return -EINVAL; gains0 = (s32) samples[idx0].gain_idx * (1 << 19); gains1 = (s32) samples[idx1].gain_idx * (1 << 19); res = gains0 + (gains1 - gains0) * ((s32) power - (s32) samples[idx0].power) / denominator + (1 << 18); *new_idx = res >> 19; return 0; } static void il3945_hw_reg_init_channel_groups(struct il_priv *il) { u32 i; s32 rate_idx; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; const struct il3945_eeprom_txpower_group *group; D_POWER("Initializing factory calib info from EEPROM\n"); for (i = 0; i < IL_NUM_TX_CALIB_GROUPS; i++) { s8 *clip_pwrs; /* table of power levels for each rate */ s8 satur_pwr; /* saturation power for each chnl group */ group = &eeprom->groups[i]; /* sanity check on factory saturation power value */ if (group->saturation_power < 40) { IL_WARN("Error: saturation power is %d, " "less than minimum expected 40\n", group->saturation_power); return; } /* * Derive requested power levels for each rate, based on * hardware capabilities (saturation power for band). * Basic value is 3dB down from saturation, with further * power reductions for highest 3 data rates. These * backoffs provide headroom for high rate modulation * power peaks, without too much distortion (clipping). */ /* we'll fill in this array with h/w max power levels */ clip_pwrs = (s8 *) il->_3945.clip_groups[i].clip_powers; /* divide factory saturation power by 2 to find -3dB level */ satur_pwr = (s8) (group->saturation_power >> 1); /* fill in channel group's nominal powers for each rate */ for (rate_idx = 0; rate_idx < RATE_COUNT_3945; rate_idx++, clip_pwrs++) { switch (rate_idx) { case RATE_36M_IDX_TBL: if (i == 0) /* B/G */ *clip_pwrs = satur_pwr; else /* A */ *clip_pwrs = satur_pwr - 5; break; case RATE_48M_IDX_TBL: if (i == 0) *clip_pwrs = satur_pwr - 7; else *clip_pwrs = satur_pwr - 10; break; case RATE_54M_IDX_TBL: if (i == 0) *clip_pwrs = satur_pwr - 9; else *clip_pwrs = satur_pwr - 12; break; default: *clip_pwrs = satur_pwr; break; } } } } /** * il3945_txpower_set_from_eeprom - Set channel power info based on EEPROM * * Second pass (during init) to set up il->channel_info * * Set up Tx-power settings in our channel info database for each VALID * (for this geo/SKU) channel, at all Tx data rates, based on eeprom values * and current temperature. * * Since this is based on current temperature (at init time), these values may * not be valid for very long, but it gives us a starting/default point, * and allows us to active (i.e. using Tx) scan. * * This does *not* write values to NIC, just sets up our internal table. */ int il3945_txpower_set_from_eeprom(struct il_priv *il) { struct il_channel_info *ch_info = NULL; struct il3945_channel_power_info *pwr_info; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; int delta_idx; u8 rate_idx; u8 scan_tbl_idx; const s8 *clip_pwrs; /* array of power levels for each rate */ u8 gain, dsp_atten; s8 power; u8 pwr_idx, base_pwr_idx, a_band; u8 i; int temperature; /* save temperature reference, * so we can determine next time to calibrate */ temperature = il3945_hw_reg_txpower_get_temperature(il); il->last_temperature = temperature; il3945_hw_reg_init_channel_groups(il); /* initialize Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0, ch_info = il->channel_info; i < il->channel_count; i++, ch_info++) { a_band = il_is_channel_a_band(ch_info); if (!il_is_channel_valid(ch_info)) continue; /* find this channel's channel group (*not* "band") idx */ ch_info->group_idx = il3945_hw_reg_get_ch_grp_idx(il, ch_info); /* Get this chnlgrp's rate->max/clip-powers table */ clip_pwrs = il->_3945.clip_groups[ch_info->group_idx].clip_powers; /* calculate power idx *adjustment* value according to * diff between current temperature and factory temperature */ delta_idx = il3945_hw_reg_adjust_power_by_temp(temperature, eeprom->groups[ch_info-> group_idx]. temperature); D_POWER("Delta idx for channel %d: %d [%d]\n", ch_info->channel, delta_idx, temperature + IL_TEMP_CONVERT); /* set tx power value for all OFDM rates */ for (rate_idx = 0; rate_idx < IL_OFDM_RATES; rate_idx++) { s32 uninitialized_var(power_idx); int rc; /* use channel group's clip-power table, * but don't exceed channel's max power */ s8 pwr = min(ch_info->max_power_avg, clip_pwrs[rate_idx]); pwr_info = &ch_info->power_info[rate_idx]; /* get base (i.e. at factory-measured temperature) * power table idx for this rate's power */ rc = il3945_hw_reg_get_matched_power_idx(il, pwr, ch_info-> group_idx, &power_idx); if (rc) { IL_ERR("Invalid power idx\n"); return rc; } pwr_info->base_power_idx = (u8) power_idx; /* temperature compensate */ power_idx += delta_idx; /* stay within range of gain table */ power_idx = il3945_hw_reg_fix_power_idx(power_idx); /* fill 1 OFDM rate's il3945_channel_power_info struct */ pwr_info->requested_power = pwr; pwr_info->power_table_idx = (u8) power_idx; pwr_info->tpc.tx_gain = power_gain_table[a_band][power_idx].tx_gain; pwr_info->tpc.dsp_atten = power_gain_table[a_band][power_idx].dsp_atten; } /* set tx power for CCK rates, based on OFDM 12 Mbit settings */ pwr_info = &ch_info->power_info[RATE_12M_IDX_TBL]; power = pwr_info->requested_power + IL_CCK_FROM_OFDM_POWER_DIFF; pwr_idx = pwr_info->power_table_idx + IL_CCK_FROM_OFDM_IDX_DIFF; base_pwr_idx = pwr_info->base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF; /* stay within table range */ pwr_idx = il3945_hw_reg_fix_power_idx(pwr_idx); gain = power_gain_table[a_band][pwr_idx].tx_gain; dsp_atten = power_gain_table[a_band][pwr_idx].dsp_atten; /* fill each CCK rate's il3945_channel_power_info structure * NOTE: All CCK-rate Txpwrs are the same for a given chnl! * NOTE: CCK rates start at end of OFDM rates! */ for (rate_idx = 0; rate_idx < IL_CCK_RATES; rate_idx++) { pwr_info = &ch_info->power_info[rate_idx + IL_OFDM_RATES]; pwr_info->requested_power = power; pwr_info->power_table_idx = pwr_idx; pwr_info->base_power_idx = base_pwr_idx; pwr_info->tpc.tx_gain = gain; pwr_info->tpc.dsp_atten = dsp_atten; } /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES; scan_tbl_idx++) { s32 actual_idx = (scan_tbl_idx == 0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL; il3945_hw_reg_set_scan_power(il, scan_tbl_idx, actual_idx, clip_pwrs, ch_info, a_band); } } return 0; } int il3945_hw_rxq_stop(struct il_priv *il) { int ret; _il_wr(il, FH39_RCSR_CONFIG(0), 0); ret = _il_poll_bit(il, FH39_RSSR_STATUS, FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); if (ret < 0) IL_ERR("Can't stop Rx DMA.\n"); return 0; } int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) { int txq_id = txq->q.id; struct il3945_shared *shared_data = il->_3945.shared_virt; shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32) txq->q.dma_addr); il_wr(il, FH39_CBCC_CTRL(txq_id), 0); il_wr(il, FH39_CBCC_BASE(txq_id), 0); il_wr(il, FH39_TCSR_CONFIG(txq_id), FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT | FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF | FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD | FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL | FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE); /* fake read to flush all prev. writes */ _il_rd(il, FH39_TSSR_CBB_BASE); return 0; } /* * HCMD utils */ static u16 il3945_get_hcmd_size(u8 cmd_id, u16 len) { switch (cmd_id) { case C_RXON: return sizeof(struct il3945_rxon_cmd); case C_POWER_TBL: return sizeof(struct il3945_powertable_cmd); default: return len; } } static u16 il3945_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) { struct il3945_addsta_cmd *addsta = (struct il3945_addsta_cmd *)data; addsta->mode = cmd->mode; memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); addsta->station_flags = cmd->station_flags; addsta->station_flags_msk = cmd->station_flags_msk; addsta->tid_disable_tx = cpu_to_le16(0); addsta->rate_n_flags = cmd->rate_n_flags; addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; return (u16) sizeof(struct il3945_addsta_cmd); } static int il3945_add_bssid_station(struct il_priv *il, const u8 * addr, u8 * sta_id_r) { int ret; u8 sta_id; unsigned long flags; if (sta_id_r) *sta_id_r = IL_INVALID_STATION; ret = il_add_station_common(il, addr, 0, NULL, &sta_id); if (ret) { IL_ERR("Unable to add station %pM\n", addr); return ret; } if (sta_id_r) *sta_id_r = sta_id; spin_lock_irqsave(&il->sta_lock, flags); il->stations[sta_id].used |= IL_STA_LOCAL; spin_unlock_irqrestore(&il->sta_lock, flags); return 0; } static int il3945_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, bool add) { struct il_vif_priv *vif_priv = (void *)vif->drv_priv; int ret; if (add) { ret = il3945_add_bssid_station(il, vif->bss_conf.bssid, &vif_priv->ibss_bssid_sta_id); if (ret) return ret; il3945_sync_sta(il, vif_priv->ibss_bssid_sta_id, (il->band == IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP); il3945_rate_scale_init(il->hw, vif_priv->ibss_bssid_sta_id); return 0; } return il_remove_station(il, vif_priv->ibss_bssid_sta_id, vif->bss_conf.bssid); } /** * il3945_init_hw_rate_table - Initialize the hardware rate fallback table */ int il3945_init_hw_rate_table(struct il_priv *il) { int rc, i, idx, prev_idx; struct il3945_rate_scaling_cmd rate_cmd = { .reserved = {0, 0, 0}, }; struct il3945_rate_scaling_info *table = rate_cmd.table; for (i = 0; i < ARRAY_SIZE(il3945_rates); i++) { idx = il3945_rates[i].table_rs_idx; table[idx].rate_n_flags = cpu_to_le16(il3945_rates[i].plcp); table[idx].try_cnt = il->retry_rate; prev_idx = il3945_get_prev_ieee_rate(i); table[idx].next_rate_idx = il3945_rates[prev_idx].table_rs_idx; } switch (il->band) { case IEEE80211_BAND_5GHZ: D_RATE("Select A mode rate scale\n"); /* If one of the following CCK rates is used, * have it fall back to the 6M OFDM rate */ for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) table[i].next_rate_idx = il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; /* Don't fall back to CCK rates */ table[RATE_12M_IDX_TBL].next_rate_idx = RATE_9M_IDX_TBL; /* Don't drop out of OFDM rates */ table[RATE_6M_IDX_TBL].next_rate_idx = il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; break; case IEEE80211_BAND_2GHZ: D_RATE("Select B/G mode rate scale\n"); /* If an OFDM rate is used, have it fall back to the * 1M CCK rates */ if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) && il_is_associated(il)) { idx = IL_FIRST_CCK_RATE; for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++) table[i].next_rate_idx = il3945_rates[idx].table_rs_idx; idx = RATE_11M_IDX_TBL; /* CCK shouldn't fall back to OFDM... */ table[idx].next_rate_idx = RATE_5M_IDX_TBL; } break; default: WARN_ON(1); break; } /* Update the rate scaling for control frame Tx */ rate_cmd.table_id = 0; rc = il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); if (rc) return rc; /* Update the rate scaling for data frame Tx */ rate_cmd.table_id = 1; return il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); } /* Called when initializing driver */ int il3945_hw_set_hw_params(struct il_priv *il) { memset((void *)&il->hw_params, 0, sizeof(struct il_hw_params)); il->_3945.shared_virt = dma_alloc_coherent(&il->pci_dev->dev, sizeof(struct il3945_shared), &il->_3945.shared_phys, GFP_KERNEL); if (!il->_3945.shared_virt) { IL_ERR("failed to allocate pci memory\n"); return -ENOMEM; } il->hw_params.bcast_id = IL3945_BROADCAST_ID; /* Assign number of Usable TX queues */ il->hw_params.max_txq_num = il->cfg->num_of_queues; il->hw_params.tfd_size = sizeof(struct il3945_tfd); il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_3K); il->hw_params.max_rxq_size = RX_QUEUE_SIZE; il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; il->hw_params.max_stations = IL3945_STATION_COUNT; il->sta_key_max_num = STA_KEY_MAX_NUM; il->hw_params.rx_wrt_ptr_reg = FH39_RSCSR_CHNL0_WPTR; il->hw_params.max_beacon_itrvl = IL39_MAX_UCODE_BEACON_INTERVAL; il->hw_params.beacon_time_tsf_bits = IL3945_EXT_BEACON_TIME_POS; return 0; } unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, struct il3945_frame *frame, u8 rate) { struct il3945_tx_beacon_cmd *tx_beacon_cmd; unsigned int frame_size; tx_beacon_cmd = (struct il3945_tx_beacon_cmd *)&frame->u; memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; frame_size = il3945_fill_beacon_frame(il, tx_beacon_cmd->frame, sizeof(frame->u) - sizeof(*tx_beacon_cmd)); BUG_ON(frame_size > MAX_MPDU_SIZE); tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); tx_beacon_cmd->tx.rate = rate; tx_beacon_cmd->tx.tx_flags = (TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK); /* supp_rates[0] == OFDM start at IL_FIRST_OFDM_RATE */ tx_beacon_cmd->tx.supp_rates[0] = (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; tx_beacon_cmd->tx.supp_rates[1] = (IL_CCK_BASIC_RATES_MASK & 0xF); return sizeof(struct il3945_tx_beacon_cmd) + frame_size; } void il3945_hw_handler_setup(struct il_priv *il) { il->handlers[C_TX] = il3945_hdl_tx; il->handlers[N_3945_RX] = il3945_hdl_rx; } void il3945_hw_setup_deferred_work(struct il_priv *il) { INIT_DELAYED_WORK(&il->_3945.thermal_periodic, il3945_bg_reg_txpower_periodic); } void il3945_hw_cancel_deferred_work(struct il_priv *il) { cancel_delayed_work(&il->_3945.thermal_periodic); } /* check contents of special bootstrap uCode SRAM */ static int il3945_verify_bsm(struct il_priv *il) { __le32 *image = il->ucode_boot.v_addr; u32 len = il->ucode_boot.len; u32 reg; u32 val; D_INFO("Begin verify bsm\n"); /* verify BSM SRAM contents */ val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; reg += sizeof(u32), image++) { val = il_rd_prph(il, reg); if (val != le32_to_cpu(*image)) { IL_ERR("BSM uCode verification failed at " "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, len, val, le32_to_cpu(*image)); return -EIO; } } D_INFO("BSM bootstrap uCode image OK\n"); return 0; } /****************************************************************************** * * EEPROM related functions * ******************************************************************************/ /* * Clear the OWNER_MSK, to establish driver (instead of uCode running on * embedded controller) as EEPROM reader; each read is a series of pulses * to/from the EEPROM chip, not a single event, so even reads could conflict * if they weren't arbitrated by some ownership mechanism. Here, the driver * simply claims ownership, which should be safe when this function is called * (i.e. before loading uCode!). */ static int il3945_eeprom_acquire_semaphore(struct il_priv *il) { _il_clear_bit(il, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK); return 0; } static void il3945_eeprom_release_semaphore(struct il_priv *il) { return; } /** * il3945_load_bsm - Load bootstrap instructions * * BSM operation: * * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program * in special SRAM that does not power down during RFKILL. When powering back * up after power-saving sleeps (or during initial uCode load), the BSM loads * the bootstrap program into the on-board processor, and starts it. * * The bootstrap program loads (via DMA) instructions and data for a new * program from host DRAM locations indicated by the host driver in the * BSM_DRAM_* registers. Once the new program is loaded, it starts * automatically. * * When initializing the NIC, the host driver points the BSM to the * "initialize" uCode image. This uCode sets up some internal data, then * notifies host via "initialize alive" that it is complete. * * The host then replaces the BSM_DRAM_* pointer values to point to the * normal runtime uCode instructions and a backup uCode data cache buffer * (filled initially with starting data values for the on-board processor), * then triggers the "initialize" uCode to load and launch the runtime uCode, * which begins normal operation. * * When doing a power-save shutdown, runtime uCode saves data SRAM into * the backup data cache in DRAM before SRAM is powered down. * * When powering back up, the BSM loads the bootstrap program. This reloads * the runtime uCode instructions and the backup data cache into SRAM, * and re-launches the runtime uCode from where it left off. */ static int il3945_load_bsm(struct il_priv *il) { __le32 *image = il->ucode_boot.v_addr; u32 len = il->ucode_boot.len; dma_addr_t pinst; dma_addr_t pdata; u32 inst_len; u32 data_len; int rc; int i; u32 done; u32 reg_offset; D_INFO("Begin load bsm\n"); /* make sure bootstrap program is no larger than BSM's SRAM size */ if (len > IL39_MAX_BSM_SIZE) return -EINVAL; /* Tell bootstrap uCode where to find the "Initialize" uCode * in host DRAM ... host DRAM physical address bits 31:0 for 3945. * NOTE: il3945_initialize_alive_start() will replace these values, * after the "initialize" uCode has run, to point to * runtime/protocol instructions and backup data cache. */ pinst = il->ucode_init.p_addr; pdata = il->ucode_init_data.p_addr; inst_len = il->ucode_init.len; data_len = il->ucode_init_data.len; il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); /* Fill BSM memory with bootstrap instructions */ for (reg_offset = BSM_SRAM_LOWER_BOUND; reg_offset < BSM_SRAM_LOWER_BOUND + len; reg_offset += sizeof(u32), image++) _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); rc = il3945_verify_bsm(il); if (rc) return rc; /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); il_wr_prph(il, BSM_WR_MEM_DST_REG, IL39_RTC_INST_LOWER_BOUND); il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); /* Load bootstrap code into instruction SRAM now, * to prepare to load "initialize" uCode */ il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); /* Wait for load of bootstrap uCode to finish */ for (i = 0; i < 100; i++) { done = il_rd_prph(il, BSM_WR_CTRL_REG); if (!(done & BSM_WR_CTRL_REG_BIT_START)) break; udelay(10); } if (i < 100) D_INFO("BSM write complete, poll %d iterations\n", i); else { IL_ERR("BSM write did not complete!\n"); return -EIO; } /* Enable future boot loads whenever power management unit triggers it * (e.g. when powering back up after power-save shutdown) */ il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); return 0; } const struct il_ops il3945_ops = { .txq_attach_buf_to_tfd = il3945_hw_txq_attach_buf_to_tfd, .txq_free_tfd = il3945_hw_txq_free_tfd, .txq_init = il3945_hw_tx_queue_init, .load_ucode = il3945_load_bsm, .dump_nic_error_log = il3945_dump_nic_error_log, .apm_init = il3945_apm_init, .send_tx_power = il3945_send_tx_power, .is_valid_rtc_data_addr = il3945_hw_valid_rtc_data_addr, .eeprom_acquire_semaphore = il3945_eeprom_acquire_semaphore, .eeprom_release_semaphore = il3945_eeprom_release_semaphore, .rxon_assoc = il3945_send_rxon_assoc, .commit_rxon = il3945_commit_rxon, .get_hcmd_size = il3945_get_hcmd_size, .build_addsta_hcmd = il3945_build_addsta_hcmd, .request_scan = il3945_request_scan, .post_scan = il3945_post_scan, .post_associate = il3945_post_associate, .config_ap = il3945_config_ap, .manage_ibss_station = il3945_manage_ibss_station, .send_led_cmd = il3945_send_led_cmd, }; static struct il_cfg il3945_bg_cfg = { .name = "3945BG", .fw_name_pre = IL3945_FW_PRE, .ucode_api_max = IL3945_UCODE_API_MAX, .ucode_api_min = IL3945_UCODE_API_MIN, .sku = IL_SKU_G, .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .mod_params = &il3945_mod_params, .led_mode = IL_LED_BLINK, .eeprom_size = IL3945_EEPROM_IMG_SIZE, .num_of_queues = IL39_NUM_QUEUES, .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, .set_l0s = false, .use_bsm = true, .led_compensation = 64, .wd_timeout = IL_DEF_WD_TIMEOUT, .regulatory_bands = { EEPROM_REGULATORY_BAND_1_CHANNELS, EEPROM_REGULATORY_BAND_2_CHANNELS, EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, EEPROM_REGULATORY_BAND_NO_HT40, EEPROM_REGULATORY_BAND_NO_HT40, }, }; static struct il_cfg il3945_abg_cfg = { .name = "3945ABG", .fw_name_pre = IL3945_FW_PRE, .ucode_api_max = IL3945_UCODE_API_MAX, .ucode_api_min = IL3945_UCODE_API_MIN, .sku = IL_SKU_A | IL_SKU_G, .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .mod_params = &il3945_mod_params, .led_mode = IL_LED_BLINK, .eeprom_size = IL3945_EEPROM_IMG_SIZE, .num_of_queues = IL39_NUM_QUEUES, .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, .set_l0s = false, .use_bsm = true, .led_compensation = 64, .wd_timeout = IL_DEF_WD_TIMEOUT, .regulatory_bands = { EEPROM_REGULATORY_BAND_1_CHANNELS, EEPROM_REGULATORY_BAND_2_CHANNELS, EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, EEPROM_REGULATORY_BAND_NO_HT40, EEPROM_REGULATORY_BAND_NO_HT40, }, }; DEFINE_PCI_DEVICE_TABLE(il3945_hw_card_ids) = { {IL_PCI_DEVICE(0x4222, 0x1005, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4222, 0x1034, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4222, 0x1044, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4227, 0x1014, il3945_bg_cfg)}, {IL_PCI_DEVICE(0x4222, PCI_ANY_ID, il3945_abg_cfg)}, {IL_PCI_DEVICE(0x4227, PCI_ANY_ID, il3945_abg_cfg)}, {0} }; MODULE_DEVICE_TABLE(pci, il3945_hw_card_ids); compat-drivers-2012-09-18/drivers/net/wireless/iwlegacy/Kconfig0000644000175000017500000000617212026211315023576 0ustar mcgrofmcgrofconfig IWLEGACY tristate select FW_LOADER select NEW_LEDS select LEDS_CLASS select LEDS_TRIGGERS select MAC80211_LEDS config IWL4965 tristate "Intel Wireless WiFi 4965AGN (iwl4965)" depends on PCI && MAC80211 select IWLEGACY ---help--- This option enables support for Select to build the driver supporting the: Intel Wireless WiFi Link 4965AGN This driver uses the kernel's mac80211 subsystem. In order to use this driver, you will need a microcode (uCode) image for it. You can obtain the microcode from: . The microcode is typically installed in /lib/firmware. You can look in the hotplug script /etc/hotplug/firmware.agent to determine which directory FIRMWARE_DIR is set to when the script runs. If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read . The module will be called iwl4965. config IWL3945 tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)" depends on PCI && MAC80211 select IWLEGACY ---help--- Select to build the driver supporting the: Intel PRO/Wireless 3945ABG/BG Network Connection This driver uses the kernel's mac80211 subsystem. In order to use this driver, you will need a microcode (uCode) image for it. You can obtain the microcode from: . The microcode is typically installed in /lib/firmware. You can look in the hotplug script /etc/hotplug/firmware.agent to determine which directory FIRMWARE_DIR is set to when the script runs. If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read . The module will be called iwl3945. menu "iwl3945 / iwl4965 Debugging Options" depends on IWLEGACY config IWLEGACY_DEBUG bool "Enable full debugging output in iwlegacy (iwl 3945/4965) drivers" depends on IWLEGACY ---help--- This option will enable debug tracing output for the iwlegacy drivers. This will result in the kernel module being ~100k larger. You can control which debug output is sent to the kernel log by setting the value in /sys/class/net/wlan0/device/debug_level This entry will only exist if this option is enabled. To set a value, simply echo an 8-byte hex value to the same file: % echo 0x43fff > /sys/class/net/wlan0/device/debug_level You can find the list of debug mask values in: drivers/net/wireless/iwlegacy/common.h If this is your first time using this driver, you should say Y here as the debug information can assist others in helping you resolve any problems you may encounter. config IWLEGACY_DEBUGFS bool "iwlegacy (iwl 3945/4965) debugfs support" depends on IWLEGACY && MAC80211_DEBUGFS ---help--- Enable creation of debugfs files for the iwlegacy drivers. This is a low-impact option that allows getting insight into the driver's state at runtime. endmenu compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/0000755000175000017500000000000012026211315022000 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/brcm80211/Makefile0000644000175000017500000000175112026211315023444 0ustar mcgrofmcgrof# # Makefile fragment for Broadcom 802.11n Networking Device Driver # # Copyright (c) 2010 Broadcom Corporation # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # common flags subdir-ccflags-$(CONFIG_BRCMDBG) += -DDEBUG obj-$(CONFIG_BRCMUTIL) += brcmutil/ obj-$(CONFIG_BRCMFMAC) += brcmfmac/ obj-$(CONFIG_BRCMSMAC) += brcmsmac/ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/Kconfig0000644000175000017500000000424012026211315023303 0ustar mcgrofmcgrofconfig BRCMUTIL tristate config BRCMSMAC tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver" depends on MAC80211 depends on BCMA select BRCMUTIL select FW_LOADER select CRC_CCITT select CRC8 select CORDIC ---help--- This module adds support for PCIe wireless adapters based on Broadcom IEEE802.11n SoftMAC chipsets. If you choose to build a module, it'll be called brcmsmac.ko. config BRCMFMAC tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver" depends on CFG80211 select BRCMUTIL ---help--- This module adds support for embedded wireless adapters based on Broadcom IEEE802.11n FullMAC chipsets. It has to work with at least one of the bus interface support. If you choose to build a module, it'll be called brcmfmac.ko. config BRCMFMAC_SDIO bool "SDIO bus interface support for FullMAC driver" depends on MMC depends on BRCMFMAC select FW_LOADER default y ---help--- This option enables the SDIO bus interface support for Broadcom IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to use the driver for a SDIO wireless card. config BRCMFMAC_SDIO_OOB bool "Out of band interrupt support for SDIO interface chipset" depends on BRCMFMAC_SDIO ---help--- This option enables out-of-band interrupt support for Broadcom SDIO Wifi chipset using fullmac in order to gain better performance and deep sleep wake up capability on certain platforms. Say N if you are unsure. config BRCMFMAC_USB bool "USB bus interface support for FullMAC driver" depends on USB depends on BRCMFMAC select FW_LOADER ---help--- This option enables the USB bus interface support for Broadcom IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to use the driver for an USB wireless card. config BRCMISCAN bool "Broadcom I-Scan (OBSOLETE)" depends on BRCMFMAC ---help--- This option enables the I-Scan method. By default fullmac uses the new E-Scan method which uses less memory in firmware and gives no limitation on the number of scan results. config BRCMDBG bool "Broadcom driver debug functions" depends on BRCMSMAC || BRCMFMAC ---help--- Selecting this enables additional code for debug purposes. compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/include/0000755000175000017500000000000012026211315023423 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/brcm80211/include/soc.h0000644000175000017500000000237312026211315024365 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_SOC_H #define _BRCM_SOC_H #define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ /* Common core control flags */ #define SICF_BIST_EN 0x8000 #define SICF_PME_EN 0x4000 #define SICF_CORE_BITS 0x3ffc #define SICF_FGC 0x0002 #define SICF_CLOCK_EN 0x0001 /* Common core status flags */ #define SISF_BIST_DONE 0x8000 #define SISF_BIST_ERROR 0x4000 #define SISF_GATED_CLK 0x2000 #define SISF_DMA64 0x1000 #define SISF_CORE_BITS 0x0fff #endif /* _BRCM_SOC_H */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/include/defs.h0000644000175000017500000000525512026211315024524 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_DEFS_H_ #define _BRCM_DEFS_H_ #include #define SI_BUS 0 #define PCI_BUS 1 #define PCMCIA_BUS 2 #define SDIO_BUS 3 #define JTAG_BUS 4 #define USB_BUS 5 #define SPI_BUS 6 #define OFF 0 #define ON 1 /* ON = 1 */ #define AUTO (-1) /* Auto = -1 */ /* * Priority definitions according 802.1D */ #define PRIO_8021D_NONE 2 #define PRIO_8021D_BK 1 #define PRIO_8021D_BE 0 #define PRIO_8021D_EE 3 #define PRIO_8021D_CL 4 #define PRIO_8021D_VI 5 #define PRIO_8021D_VO 6 #define PRIO_8021D_NC 7 #define MAXPRIO 7 #define NUMPRIO (MAXPRIO + 1) #define WL_NUMRATES 16 /* max # of rates in a rateset */ #define BRCM_CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */ #define BRCM_SET_CHANNEL 30 #define BRCM_SET_SRL 32 #define BRCM_SET_LRL 34 #define BRCM_SET_BCNPRD 76 #define BRCM_GET_CURR_RATESET 114 /* current rateset */ #define BRCM_GET_PHYLIST 180 /* Bit masks for radio disabled status - returned by WL_GET_RADIO */ #define WL_RADIO_SW_DISABLE (1<<0) #define WL_RADIO_HW_DISABLE (1<<1) /* some countries don't support any channel */ #define WL_RADIO_COUNTRY_DISABLE (1<<3) /* Override bit for SET_TXPWR. if set, ignore other level limits */ #define WL_TXPWR_OVERRIDE (1U<<31) /* band types */ #define BRCM_BAND_AUTO 0 /* auto-select */ #define BRCM_BAND_5G 1 /* 5 Ghz */ #define BRCM_BAND_2G 2 /* 2.4 Ghz */ #define BRCM_BAND_ALL 3 /* all bands */ /* Values for PM */ #define PM_OFF 0 #define PM_MAX 1 /* Message levels */ #define LOG_ERROR_VAL 0x00000001 #define LOG_TRACE_VAL 0x00000002 #define PM_OFF 0 #define PM_MAX 1 #define PM_FAST 2 /* * Sonics Configuration Space Registers. */ /* core sbconfig regs are top 256bytes of regs */ #define SBCONFIGOFF 0xf00 /* cpp contortions to concatenate w/arg prescan */ #ifndef PAD #define _PADLINE(line) pad ## line #define _XSTR(line) _PADLINE(line) #define PAD _XSTR(__LINE__) #endif #endif /* _BRCM_DEFS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/include/chipcommon.h0000644000175000017500000001722512026211315025737 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _SBCHIPC_H #define _SBCHIPC_H #include "defs.h" /* for PAD macro */ #define CHIPCREGOFFS(field) offsetof(struct chipcregs, field) struct chipcregs { u32 chipid; /* 0x0 */ u32 capabilities; u32 corecontrol; /* corerev >= 1 */ u32 bist; /* OTP */ u32 otpstatus; /* 0x10, corerev >= 10 */ u32 otpcontrol; u32 otpprog; u32 otplayout; /* corerev >= 23 */ /* Interrupt control */ u32 intstatus; /* 0x20 */ u32 intmask; /* Chip specific regs */ u32 chipcontrol; /* 0x28, rev >= 11 */ u32 chipstatus; /* 0x2c, rev >= 11 */ /* Jtag Master */ u32 jtagcmd; /* 0x30, rev >= 10 */ u32 jtagir; u32 jtagdr; u32 jtagctrl; /* serial flash interface registers */ u32 flashcontrol; /* 0x40 */ u32 flashaddress; u32 flashdata; u32 PAD[1]; /* Silicon backplane configuration broadcast control */ u32 broadcastaddress; /* 0x50 */ u32 broadcastdata; /* gpio - cleared only by power-on-reset */ u32 gpiopullup; /* 0x58, corerev >= 20 */ u32 gpiopulldown; /* 0x5c, corerev >= 20 */ u32 gpioin; /* 0x60 */ u32 gpioout; /* 0x64 */ u32 gpioouten; /* 0x68 */ u32 gpiocontrol; /* 0x6C */ u32 gpiointpolarity; /* 0x70 */ u32 gpiointmask; /* 0x74 */ /* GPIO events corerev >= 11 */ u32 gpioevent; u32 gpioeventintmask; /* Watchdog timer */ u32 watchdog; /* 0x80 */ /* GPIO events corerev >= 11 */ u32 gpioeventintpolarity; /* GPIO based LED powersave registers corerev >= 16 */ u32 gpiotimerval; /* 0x88 */ u32 gpiotimeroutmask; /* clock control */ u32 clockcontrol_n; /* 0x90 */ u32 clockcontrol_sb; /* aka m0 */ u32 clockcontrol_pci; /* aka m1 */ u32 clockcontrol_m2; /* mii/uart/mipsref */ u32 clockcontrol_m3; /* cpu */ u32 clkdiv; /* corerev >= 3 */ u32 gpiodebugsel; /* corerev >= 28 */ u32 capabilities_ext; /* 0xac */ /* pll delay registers (corerev >= 4) */ u32 pll_on_delay; /* 0xb0 */ u32 fref_sel_delay; u32 slow_clk_ctl; /* 5 < corerev < 10 */ u32 PAD; /* Instaclock registers (corerev >= 10) */ u32 system_clk_ctl; /* 0xc0 */ u32 clkstatestretch; u32 PAD[2]; /* Indirect backplane access (corerev >= 22) */ u32 bp_addrlow; /* 0xd0 */ u32 bp_addrhigh; u32 bp_data; u32 PAD; u32 bp_indaccess; u32 PAD[3]; /* More clock dividers (corerev >= 32) */ u32 clkdiv2; u32 PAD[2]; /* In AI chips, pointer to erom */ u32 eromptr; /* 0xfc */ /* ExtBus control registers (corerev >= 3) */ u32 pcmcia_config; /* 0x100 */ u32 pcmcia_memwait; u32 pcmcia_attrwait; u32 pcmcia_iowait; u32 ide_config; u32 ide_memwait; u32 ide_attrwait; u32 ide_iowait; u32 prog_config; u32 prog_waitcount; u32 flash_config; u32 flash_waitcount; u32 SECI_config; /* 0x130 SECI configuration */ u32 PAD[3]; /* Enhanced Coexistence Interface (ECI) registers (corerev >= 21) */ u32 eci_output; /* 0x140 */ u32 eci_control; u32 eci_inputlo; u32 eci_inputmi; u32 eci_inputhi; u32 eci_inputintpolaritylo; u32 eci_inputintpolaritymi; u32 eci_inputintpolarityhi; u32 eci_intmasklo; u32 eci_intmaskmi; u32 eci_intmaskhi; u32 eci_eventlo; u32 eci_eventmi; u32 eci_eventhi; u32 eci_eventmasklo; u32 eci_eventmaskmi; u32 eci_eventmaskhi; u32 PAD[3]; /* SROM interface (corerev >= 32) */ u32 sromcontrol; /* 0x190 */ u32 sromaddress; u32 sromdata; u32 PAD[17]; /* Clock control and hardware workarounds (corerev >= 20) */ u32 clk_ctl_st; /* 0x1e0 */ u32 hw_war; u32 PAD[70]; /* UARTs */ u8 uart0data; /* 0x300 */ u8 uart0imr; u8 uart0fcr; u8 uart0lcr; u8 uart0mcr; u8 uart0lsr; u8 uart0msr; u8 uart0scratch; u8 PAD[248]; /* corerev >= 1 */ u8 uart1data; /* 0x400 */ u8 uart1imr; u8 uart1fcr; u8 uart1lcr; u8 uart1mcr; u8 uart1lsr; u8 uart1msr; u8 uart1scratch; u32 PAD[126]; /* PMU registers (corerev >= 20) */ u32 pmucontrol; /* 0x600 */ u32 pmucapabilities; u32 pmustatus; u32 res_state; u32 res_pending; u32 pmutimer; u32 min_res_mask; u32 max_res_mask; u32 res_table_sel; u32 res_dep_mask; u32 res_updn_timer; u32 res_timer; u32 clkstretch; u32 pmuwatchdog; u32 gpiosel; /* 0x638, rev >= 1 */ u32 gpioenable; /* 0x63c, rev >= 1 */ u32 res_req_timer_sel; u32 res_req_timer; u32 res_req_mask; u32 PAD; u32 chipcontrol_addr; /* 0x650 */ u32 chipcontrol_data; /* 0x654 */ u32 regcontrol_addr; u32 regcontrol_data; u32 pllcontrol_addr; u32 pllcontrol_data; u32 pmustrapopt; /* 0x668, corerev >= 28 */ u32 pmu_xtalfreq; /* 0x66C, pmurev >= 10 */ u32 PAD[100]; u16 sromotp[768]; }; /* chipid */ #define CID_ID_MASK 0x0000ffff /* Chip Id mask */ #define CID_REV_MASK 0x000f0000 /* Chip Revision mask */ #define CID_REV_SHIFT 16 /* Chip Revision shift */ #define CID_PKG_MASK 0x00f00000 /* Package Option mask */ #define CID_PKG_SHIFT 20 /* Package Option shift */ #define CID_CC_MASK 0x0f000000 /* CoreCount (corerev >= 4) */ #define CID_CC_SHIFT 24 #define CID_TYPE_MASK 0xf0000000 /* Chip Type */ #define CID_TYPE_SHIFT 28 /* capabilities */ #define CC_CAP_UARTS_MASK 0x00000003 /* Number of UARTs */ #define CC_CAP_MIPSEB 0x00000004 /* MIPS is in big-endian mode */ #define CC_CAP_UCLKSEL 0x00000018 /* UARTs clock select */ /* UARTs are driven by internal divided clock */ #define CC_CAP_UINTCLK 0x00000008 #define CC_CAP_UARTGPIO 0x00000020 /* UARTs own GPIOs 15:12 */ #define CC_CAP_EXTBUS_MASK 0x000000c0 /* External bus mask */ #define CC_CAP_EXTBUS_NONE 0x00000000 /* No ExtBus present */ #define CC_CAP_EXTBUS_FULL 0x00000040 /* ExtBus: PCMCIA, IDE & Prog */ #define CC_CAP_EXTBUS_PROG 0x00000080 /* ExtBus: ProgIf only */ #define CC_CAP_FLASH_MASK 0x00000700 /* Type of flash */ #define CC_CAP_PLL_MASK 0x00038000 /* Type of PLL */ #define CC_CAP_PWR_CTL 0x00040000 /* Power control */ #define CC_CAP_OTPSIZE 0x00380000 /* OTP Size (0 = none) */ #define CC_CAP_OTPSIZE_SHIFT 19 /* OTP Size shift */ #define CC_CAP_OTPSIZE_BASE 5 /* OTP Size base */ #define CC_CAP_JTAGP 0x00400000 /* JTAG Master Present */ #define CC_CAP_ROM 0x00800000 /* Internal boot rom active */ #define CC_CAP_BKPLN64 0x08000000 /* 64-bit backplane */ #define CC_CAP_PMU 0x10000000 /* PMU Present, rev >= 20 */ #define CC_CAP_SROM 0x40000000 /* Srom Present, rev >= 32 */ /* Nand flash present, rev >= 35 */ #define CC_CAP_NFLASH 0x80000000 #define CC_CAP2_SECI 0x00000001 /* SECI Present, rev >= 36 */ /* GSIO (spi/i2c) present, rev >= 37 */ #define CC_CAP2_GSIO 0x00000002 /* pmucapabilities */ #define PCAP_REV_MASK 0x000000ff #define PCAP_RC_MASK 0x00001f00 #define PCAP_RC_SHIFT 8 #define PCAP_TC_MASK 0x0001e000 #define PCAP_TC_SHIFT 13 #define PCAP_PC_MASK 0x001e0000 #define PCAP_PC_SHIFT 17 #define PCAP_VC_MASK 0x01e00000 #define PCAP_VC_SHIFT 21 #define PCAP_CC_MASK 0x1e000000 #define PCAP_CC_SHIFT 25 #define PCAP5_PC_MASK 0x003e0000 /* PMU corerev >= 5 */ #define PCAP5_PC_SHIFT 17 #define PCAP5_VC_MASK 0x07c00000 #define PCAP5_VC_SHIFT 22 #define PCAP5_CC_MASK 0xf8000000 #define PCAP5_CC_SHIFT 27 /* * Maximum delay for the PMU state transition in us. * This is an upper bound intended for spinwaits etc. */ #define PMU_MAX_TRANSITION_DLY 15000 #endif /* _SBCHIPC_H */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/include/brcmu_wifi.h0000644000175000017500000001537212026211315025732 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCMU_WIFI_H_ #define _BRCMU_WIFI_H_ #include /* for ETH_ALEN */ #include /* for WLAN_PMKID_LEN */ /* * A chanspec (u16) holds the channel number, band, bandwidth and control * sideband */ /* channel defines */ #define CH_UPPER_SB 0x01 #define CH_LOWER_SB 0x02 #define CH_EWA_VALID 0x04 #define CH_20MHZ_APART 4 #define CH_10MHZ_APART 2 #define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */ #define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */ #define BRCM_MAX_2G_CHANNEL CH_MAX_2G_CHANNEL /* legacy define */ /* bandstate array indices */ #define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */ #define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */ /* * max # supported channels. The max channel no is 216, this is that + 1 * rounded up to a multiple of NBBY (8). DO NOT MAKE it > 255: channels are * u8's all over */ #define MAXCHANNEL 224 #define WL_CHANSPEC_CHAN_MASK 0x00ff #define WL_CHANSPEC_CHAN_SHIFT 0 #define WL_CHANSPEC_CTL_SB_MASK 0x0300 #define WL_CHANSPEC_CTL_SB_SHIFT 8 #define WL_CHANSPEC_CTL_SB_LOWER 0x0100 #define WL_CHANSPEC_CTL_SB_UPPER 0x0200 #define WL_CHANSPEC_CTL_SB_NONE 0x0300 #define WL_CHANSPEC_BW_MASK 0x0C00 #define WL_CHANSPEC_BW_SHIFT 10 #define WL_CHANSPEC_BW_10 0x0400 #define WL_CHANSPEC_BW_20 0x0800 #define WL_CHANSPEC_BW_40 0x0C00 #define WL_CHANSPEC_BAND_MASK 0xf000 #define WL_CHANSPEC_BAND_SHIFT 12 #define WL_CHANSPEC_BAND_5G 0x1000 #define WL_CHANSPEC_BAND_2G 0x2000 #define INVCHANSPEC 255 #define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK)) #define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) #define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK) #define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK) #define CHSPEC_IS10(chspec) \ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) #define CHSPEC_IS20(chspec) \ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) #ifndef CHSPEC_IS40 #define CHSPEC_IS40(chspec) \ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) #endif #define CHSPEC_IS5G(chspec) \ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) #define CHSPEC_IS2G(chspec) \ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) #define CHSPEC_SB_NONE(chspec) \ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE) #define CHSPEC_SB_UPPER(chspec) \ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) #define CHSPEC_SB_LOWER(chspec) \ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) #define CHSPEC_CTL_CHAN(chspec) \ ((CHSPEC_SB_LOWER(chspec)) ? \ (lower_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \ (upper_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK)))) #define CHSPEC2BAND(chspec) (CHSPEC_IS5G(chspec) ? BRCM_BAND_5G : BRCM_BAND_2G) #define CHANSPEC_STR_LEN 8 static inline int lower_20_sb(int channel) { return channel > CH_10MHZ_APART ? (channel - CH_10MHZ_APART) : 0; } static inline int upper_20_sb(int channel) { return (channel < (MAXCHANNEL - CH_10MHZ_APART)) ? channel + CH_10MHZ_APART : 0; } static inline int chspec_bandunit(u16 chspec) { return CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX; } static inline u16 ch20mhz_chspec(int channel) { u16 rc = channel <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G; return (u16)((u16)channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE | rc); } static inline int next_20mhz_chan(int channel) { return channel < (MAXCHANNEL - CH_20MHZ_APART) ? channel + CH_20MHZ_APART : 0; } /* defined rate in 500kbps */ #define BRCM_MAXRATE 108 /* in 500kbps units */ #define BRCM_RATE_1M 2 /* in 500kbps units */ #define BRCM_RATE_2M 4 /* in 500kbps units */ #define BRCM_RATE_5M5 11 /* in 500kbps units */ #define BRCM_RATE_11M 22 /* in 500kbps units */ #define BRCM_RATE_6M 12 /* in 500kbps units */ #define BRCM_RATE_9M 18 /* in 500kbps units */ #define BRCM_RATE_12M 24 /* in 500kbps units */ #define BRCM_RATE_18M 36 /* in 500kbps units */ #define BRCM_RATE_24M 48 /* in 500kbps units */ #define BRCM_RATE_36M 72 /* in 500kbps units */ #define BRCM_RATE_48M 96 /* in 500kbps units */ #define BRCM_RATE_54M 108 /* in 500kbps units */ #define BRCM_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */ #define MCSSET_LEN 16 static inline bool ac_bitmap_tst(u8 bitmap, int prec) { return (bitmap & (1 << (prec))) != 0; } /* Enumerate crypto algorithms */ #define CRYPTO_ALGO_OFF 0 #define CRYPTO_ALGO_WEP1 1 #define CRYPTO_ALGO_TKIP 2 #define CRYPTO_ALGO_WEP128 3 #define CRYPTO_ALGO_AES_CCM 4 #define CRYPTO_ALGO_AES_RESERVED1 5 #define CRYPTO_ALGO_AES_RESERVED2 6 #define CRYPTO_ALGO_NALG 7 /* wireless security bitvec */ #define WEP_ENABLED 0x0001 #define TKIP_ENABLED 0x0002 #define AES_ENABLED 0x0004 #define WSEC_SWFLAG 0x0008 /* to go into transition mode without setting wep */ #define SES_OW_ENABLED 0x0040 /* WPA authentication mode bitvec */ #define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ #define WPA_AUTH_NONE 0x0001 /* none (IBSS) */ #define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */ #define WPA_AUTH_PSK 0x0004 /* Pre-shared key */ #define WPA_AUTH_RESERVED1 0x0008 #define WPA_AUTH_RESERVED2 0x0010 #define WPA2_AUTH_RESERVED1 0x0020 #define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */ #define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */ #define WPA2_AUTH_RESERVED3 0x0200 #define WPA2_AUTH_RESERVED4 0x0400 #define WPA2_AUTH_RESERVED5 0x0800 /* pmkid */ #define MAXPMKID 16 #define DOT11_DEFAULT_RTS_LEN 2347 #define DOT11_DEFAULT_FRAG_LEN 2346 #define DOT11_ICV_AES_LEN 8 #define DOT11_QOS_LEN 2 #define DOT11_IV_MAX_LEN 8 #define DOT11_A4_HDR_LEN 30 #define HT_CAP_RX_STBC_NO 0x0 #define HT_CAP_RX_STBC_ONE_STREAM 0x1 struct pmkid { u8 BSSID[ETH_ALEN]; u8 PMKID[WLAN_PMKID_LEN]; }; struct pmkid_list { __le32 npmkid; struct pmkid pmkid[1]; }; struct pmkid_cand { u8 BSSID[ETH_ALEN]; u8 preauth; }; struct pmkid_cand_list { u32 npmkid_cand; struct pmkid_cand pmkid_cand[1]; }; #endif /* _BRCMU_WIFI_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/include/brcmu_utils.h0000644000175000017500000001315412026211315026130 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCMU_UTILS_H_ #define _BRCMU_UTILS_H_ #include /* * Spin at most 'us' microseconds while 'exp' is true. * Caller should explicitly test 'exp' when this completes * and take appropriate error action if 'exp' is still true. */ #define SPINWAIT(exp, us) { \ uint countdown = (us) + 9; \ while ((exp) && (countdown >= 10)) {\ udelay(10); \ countdown -= 10; \ } \ } /* osl multi-precedence packet queue */ #define PKTQ_LEN_DEFAULT 128 /* Max 128 packets */ #define PKTQ_MAX_PREC 16 /* Maximum precedence levels */ #define BCME_STRLEN 64 /* Max string length for BCM errors */ /* the largest reasonable packet buffer driver uses for ethernet MTU in bytes */ #define PKTBUFSZ 2048 #ifndef setbit #ifndef NBBY /* the BSD family defines NBBY */ #define NBBY 8 /* 8 bits per byte */ #endif /* #ifndef NBBY */ #define setbit(a, i) (((u8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a, i) (((u8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a, i) (((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a, i) ((((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) #endif /* setbit */ #define NBITS(type) (sizeof(type) * 8) #define NBITVAL(nbits) (1 << (nbits)) #define MAXBITVAL(nbits) ((1 << (nbits)) - 1) #define NBITMASK(nbits) MAXBITVAL(nbits) #define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8) /* crc defines */ #define CRC16_INIT_VALUE 0xffff /* Initial CRC16 checksum value */ #define CRC16_GOOD_VALUE 0xf0b8 /* Good final CRC16 checksum value */ /* 18-bytes of Ethernet address buffer length */ #define ETHER_ADDR_STR_LEN 18 struct pktq_prec { struct sk_buff_head skblist; u16 max; /* maximum number of queued packets */ }; /* multi-priority pkt queue */ struct pktq { u16 num_prec; /* number of precedences in use */ u16 hi_prec; /* rapid dequeue hint (>= highest non-empty prec) */ u16 max; /* total max packets */ u16 len; /* total number of packets */ /* * q array must be last since # of elements can be either * PKTQ_MAX_PREC or 1 */ struct pktq_prec q[PKTQ_MAX_PREC]; }; /* operations on a specific precedence in packet queue */ static inline int pktq_plen(struct pktq *pq, int prec) { return pq->q[prec].skblist.qlen; } static inline int pktq_pavail(struct pktq *pq, int prec) { return pq->q[prec].max - pq->q[prec].skblist.qlen; } static inline bool pktq_pfull(struct pktq *pq, int prec) { return pq->q[prec].skblist.qlen >= pq->q[prec].max; } static inline bool pktq_pempty(struct pktq *pq, int prec) { return skb_queue_empty(&pq->q[prec].skblist); } static inline struct sk_buff *pktq_ppeek(struct pktq *pq, int prec) { return skb_peek(&pq->q[prec].skblist); } static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec) { return skb_peek_tail(&pq->q[prec].skblist); } extern struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p); extern struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, struct sk_buff *p); extern struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec); extern struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec); /* packet primitives */ extern struct sk_buff *brcmu_pkt_buf_get_skb(uint len); extern void brcmu_pkt_buf_free_skb(struct sk_buff *skb); /* Empty the queue at particular precedence level */ /* callback function fn(pkt, arg) returns true if pkt belongs to if */ extern void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, bool (*fn)(struct sk_buff *, void *), void *arg); /* operations on a set of precedences in packet queue */ extern int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp); extern struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); /* operations on packet queue as a whole */ static inline int pktq_len(struct pktq *pq) { return (int)pq->len; } static inline int pktq_max(struct pktq *pq) { return (int)pq->max; } static inline int pktq_avail(struct pktq *pq) { return (int)(pq->max - pq->len); } static inline bool pktq_full(struct pktq *pq) { return pq->len >= pq->max; } static inline bool pktq_empty(struct pktq *pq) { return pq->len == 0; } extern void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len); /* prec_out may be NULL if caller is not interested in return value */ extern struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out); extern void brcmu_pktq_flush(struct pktq *pq, bool dir, bool (*fn)(struct sk_buff *, void *), void *arg); /* externs */ /* ip address */ struct ipv4_addr; /* externs */ /* format/print */ #ifdef DEBUG extern void brcmu_prpkt(const char *msg, struct sk_buff *p0); #else #define brcmu_prpkt(a, b) #endif /* DEBUG */ #ifdef DEBUG extern __printf(3, 4) void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...); #else __printf(3, 4) static inline void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) { } #endif #endif /* _BRCMU_UTILS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h0000644000175000017500000000317312026211315026060 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_HW_IDS_H_ #define _BRCM_HW_IDS_H_ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ #define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */ #define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */ #define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */ #define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */ #define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */ /* Chipcommon Core Chip IDs */ #define BCM4313_CHIP_ID 0x4313 #define BCM43224_CHIP_ID 43224 #define BCM43225_CHIP_ID 43225 #define BCM43235_CHIP_ID 43235 #define BCM43236_CHIP_ID 43236 #define BCM43238_CHIP_ID 43238 #define BCM43241_CHIP_ID 0x4324 #define BCM4329_CHIP_ID 0x4329 #define BCM4330_CHIP_ID 0x4330 #define BCM4331_CHIP_ID 0x4331 #define BCM4334_CHIP_ID 0x4334 #endif /* _BRCM_HW_IDS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmutil/0000755000175000017500000000000012026211315023621 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmutil/utils.c0000644000175000017500000001374412026211315025136 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities."); MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); struct sk_buff *brcmu_pkt_buf_get_skb(uint len) { struct sk_buff *skb; skb = dev_alloc_skb(len); if (skb) { skb_put(skb, len); skb->priority = 0; } return skb; } EXPORT_SYMBOL(brcmu_pkt_buf_get_skb); /* Free the driver packet. Free the tag if present */ void brcmu_pkt_buf_free_skb(struct sk_buff *skb) { if (!skb) return; WARN_ON(skb->next); if (skb->destructor) /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if * destructor exists */ dev_kfree_skb_any(skb); else /* can free immediately (even in_irq()) if destructor * does not exist */ dev_kfree_skb(skb); } EXPORT_SYMBOL(brcmu_pkt_buf_free_skb); /* * osl multiple-precedence packet queue * hi_prec is always >= the number of the highest non-empty precedence */ struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p) { struct sk_buff_head *q; if (pktq_full(pq) || pktq_pfull(pq, prec)) return NULL; q = &pq->q[prec].skblist; skb_queue_tail(q, p); pq->len++; if (pq->hi_prec < prec) pq->hi_prec = (u8) prec; return p; } EXPORT_SYMBOL(brcmu_pktq_penq); struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, struct sk_buff *p) { struct sk_buff_head *q; if (pktq_full(pq) || pktq_pfull(pq, prec)) return NULL; q = &pq->q[prec].skblist; skb_queue_head(q, p); pq->len++; if (pq->hi_prec < prec) pq->hi_prec = (u8) prec; return p; } EXPORT_SYMBOL(brcmu_pktq_penq_head); struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec) { struct sk_buff_head *q; struct sk_buff *p; q = &pq->q[prec].skblist; p = skb_dequeue(q); if (p == NULL) return NULL; pq->len--; return p; } EXPORT_SYMBOL(brcmu_pktq_pdeq); struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec) { struct sk_buff_head *q; struct sk_buff *p; q = &pq->q[prec].skblist; p = skb_dequeue_tail(q); if (p == NULL) return NULL; pq->len--; return p; } EXPORT_SYMBOL(brcmu_pktq_pdeq_tail); void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, bool (*fn)(struct sk_buff *, void *), void *arg) { struct sk_buff_head *q; struct sk_buff *p, *next; q = &pq->q[prec].skblist; skb_queue_walk_safe(q, p, next) { if (fn == NULL || (*fn) (p, arg)) { skb_unlink(p, q); brcmu_pkt_buf_free_skb(p); pq->len--; } } } EXPORT_SYMBOL(brcmu_pktq_pflush); void brcmu_pktq_flush(struct pktq *pq, bool dir, bool (*fn)(struct sk_buff *, void *), void *arg) { int prec; for (prec = 0; prec < pq->num_prec; prec++) brcmu_pktq_pflush(pq, prec, dir, fn, arg); } EXPORT_SYMBOL(brcmu_pktq_flush); void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len) { int prec; /* pq is variable size; only zero out what's requested */ memset(pq, 0, offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); pq->num_prec = (u16) num_prec; pq->max = (u16) max_len; for (prec = 0; prec < num_prec; prec++) { pq->q[prec].max = pq->max; skb_queue_head_init(&pq->q[prec].skblist); } } EXPORT_SYMBOL(brcmu_pktq_init); struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out) { int prec; if (pq->len == 0) return NULL; for (prec = 0; prec < pq->hi_prec; prec++) if (!skb_queue_empty(&pq->q[prec].skblist)) break; if (prec_out) *prec_out = prec; return skb_peek_tail(&pq->q[prec].skblist); } EXPORT_SYMBOL(brcmu_pktq_peek_tail); /* Return sum of lengths of a specific set of precedences */ int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp) { int prec, len; len = 0; for (prec = 0; prec <= pq->hi_prec; prec++) if (prec_bmp & (1 << prec)) len += pq->q[prec].skblist.qlen; return len; } EXPORT_SYMBOL(brcmu_pktq_mlen); /* Priority dequeue from a specific set of precedences */ struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out) { struct sk_buff_head *q; struct sk_buff *p; int prec; if (pq->len == 0) return NULL; while ((prec = pq->hi_prec) > 0 && skb_queue_empty(&pq->q[prec].skblist)) pq->hi_prec--; while ((prec_bmp & (1 << prec)) == 0 || skb_queue_empty(&pq->q[prec].skblist)) if (prec-- == 0) return NULL; q = &pq->q[prec].skblist; p = skb_dequeue(q); if (p == NULL) return NULL; pq->len--; if (prec_out) *prec_out = prec; return p; } EXPORT_SYMBOL(brcmu_pktq_mdeq); #if defined(DEBUG) /* pretty hex print a pkt buffer chain */ void brcmu_prpkt(const char *msg, struct sk_buff *p0) { struct sk_buff *p; if (msg && (msg[0] != '\0')) pr_debug("%s:\n", msg); for (p = p0; p; p = p->next) print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len); } EXPORT_SYMBOL(brcmu_prpkt); void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) { struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; pr_debug("%pV", &vaf); va_end(args); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size); } EXPORT_SYMBOL(brcmu_dbg_hex_dump); #endif /* defined(DEBUG) */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmutil/Makefile0000644000175000017500000000200712026211315025260 0ustar mcgrofmcgrof# # Makefile fragment for Broadcom 802.11n Networking Device Driver Utilities # # Copyright (c) 2011 Broadcom Corporation # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ccflags-y := \ -I$(obj) \ -I$(obj)/../include BRCMUTIL_OFILES := \ utils.o MODULEPFX := brcmutil obj-$(CONFIG_BRCMUTIL) += $(MODULEPFX).o $(MODULEPFX)-objs = $(BRCMUTIL_OFILES) compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/0000755000175000017500000000000012026211315023567 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c0000644000175000017500000004755012026211315025420 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * File contents: support functions for PCI/PCIe */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include "types.h" #include "pub.h" #include "pmu.h" #include "aiutils.h" /* slow_clk_ctl */ /* slow clock source mask */ #define SCC_SS_MASK 0x00000007 /* source of slow clock is LPO */ #define SCC_SS_LPO 0x00000000 /* source of slow clock is crystal */ #define SCC_SS_XTAL 0x00000001 /* source of slow clock is PCI */ #define SCC_SS_PCI 0x00000002 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ #define SCC_LF 0x00000200 /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ #define SCC_LP 0x00000400 /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ #define SCC_FS 0x00000800 /* IgnorePllOffReq, 1/0: * power logic ignores/honors PLL clock disable requests from core */ #define SCC_IP 0x00001000 /* XtalControlEn, 1/0: * power logic does/doesn't disable crystal when appropriate */ #define SCC_XC 0x00002000 /* XtalPU (RO), 1/0: crystal running/disabled */ #define SCC_XP 0x00004000 /* ClockDivider (SlowClk = 1/(4+divisor)) */ #define SCC_CD_MASK 0xffff0000 #define SCC_CD_SHIFT 16 /* system_clk_ctl */ /* ILPen: Enable Idle Low Power */ #define SYCC_IE 0x00000001 /* ALPen: Enable Active Low Power */ #define SYCC_AE 0x00000002 /* ForcePLLOn */ #define SYCC_FP 0x00000004 /* Force ALP (or HT if ALPen is not set */ #define SYCC_AR 0x00000008 /* Force HT */ #define SYCC_HR 0x00000010 /* ClkDiv (ILP = 1/(4 * (divisor + 1)) */ #define SYCC_CD_MASK 0xffff0000 #define SYCC_CD_SHIFT 16 #define CST4329_SPROM_OTP_SEL_MASK 0x00000003 /* OTP is powered up, use def. CIS, no SPROM */ #define CST4329_DEFCIS_SEL 0 /* OTP is powered up, SPROM is present */ #define CST4329_SPROM_SEL 1 /* OTP is powered up, no SPROM */ #define CST4329_OTP_SEL 2 /* OTP is powered down, SPROM is present */ #define CST4329_OTP_PWRDN 3 #define CST4329_SPI_SDIO_MODE_MASK 0x00000004 #define CST4329_SPI_SDIO_MODE_SHIFT 2 /* 43224 chip-specific ChipControl register bits */ #define CCTRL43224_GPIO_TOGGLE 0x8000 /* 12 mA drive strength */ #define CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength for later 43224s */ #define CCTRL_43224B0_12MA_LED_DRIVE 0xF0 /* 43236 Chip specific ChipStatus register bits */ #define CST43236_SFLASH_MASK 0x00000040 #define CST43236_OTP_MASK 0x00000080 #define CST43236_HSIC_MASK 0x00000100 /* USB/HSIC */ #define CST43236_BP_CLK 0x00000200 /* 120/96Mbps */ #define CST43236_BOOT_MASK 0x00001800 #define CST43236_BOOT_SHIFT 11 #define CST43236_BOOT_FROM_SRAM 0 /* boot from SRAM, ARM in reset */ #define CST43236_BOOT_FROM_ROM 1 /* boot from ROM */ #define CST43236_BOOT_FROM_FLASH 2 /* boot from FLASH */ #define CST43236_BOOT_FROM_INVALID 3 /* 4331 chip-specific ChipControl register bits */ /* 0 disable */ #define CCTRL4331_BT_COEXIST (1<<0) /* 0 SECI is disabled (JTAG functional) */ #define CCTRL4331_SECI (1<<1) /* 0 disable */ #define CCTRL4331_EXT_LNA (1<<2) /* sprom/gpio13-15 mux */ #define CCTRL4331_SPROM_GPIO13_15 (1<<3) /* 0 ext pa disable, 1 ext pa enabled */ #define CCTRL4331_EXTPA_EN (1<<4) /* set drive out GPIO_CLK on sprom_cs pin */ #define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) /* use sprom_cs pin as PCIE mdio interface */ #define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) /* aband extpa will be at gpio2/5 and sprom_dout */ #define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) /* override core control on pipe_AuxClkEnable */ #define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) /* override core control on pipe_AuxPowerDown */ #define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) /* pcie_auxclkenable */ #define CCTRL4331_PCIE_AUXCLKEN (1<<10) /* pcie_pipe_pllpowerdown */ #define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) /* enable bt_shd0 at gpio4 */ #define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) /* enable bt_shd1 at gpio5 */ #define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) /* 4331 Chip specific ChipStatus register bits */ /* crystal frequency 20/40Mhz */ #define CST4331_XTAL_FREQ 0x00000001 #define CST4331_SPROM_PRESENT 0x00000002 #define CST4331_OTP_PRESENT 0x00000004 #define CST4331_LDO_RF 0x00000008 #define CST4331_LDO_PAR 0x00000010 /* 4319 chip-specific ChipStatus register bits */ #define CST4319_SPI_CPULESSUSB 0x00000001 #define CST4319_SPI_CLK_POL 0x00000002 #define CST4319_SPI_CLK_PH 0x00000008 /* gpio [7:6], SDIO CIS selection */ #define CST4319_SPROM_OTP_SEL_MASK 0x000000c0 #define CST4319_SPROM_OTP_SEL_SHIFT 6 /* use default CIS, OTP is powered up */ #define CST4319_DEFCIS_SEL 0x00000000 /* use SPROM, OTP is powered up */ #define CST4319_SPROM_SEL 0x00000040 /* use OTP, OTP is powered up */ #define CST4319_OTP_SEL 0x00000080 /* use SPROM, OTP is powered down */ #define CST4319_OTP_PWRDN 0x000000c0 /* gpio [8], sdio/usb mode */ #define CST4319_SDIO_USB_MODE 0x00000100 #define CST4319_REMAP_SEL_MASK 0x00000600 #define CST4319_ILPDIV_EN 0x00000800 #define CST4319_XTAL_PD_POL 0x00001000 #define CST4319_LPO_SEL 0x00002000 #define CST4319_RES_INIT_MODE 0x0000c000 /* PALDO is configured with external PNP */ #define CST4319_PALDO_EXTPNP 0x00010000 #define CST4319_CBUCK_MODE_MASK 0x00060000 #define CST4319_CBUCK_MODE_BURST 0x00020000 #define CST4319_CBUCK_MODE_LPBURST 0x00060000 #define CST4319_RCAL_VALID 0x01000000 #define CST4319_RCAL_VALUE_MASK 0x3e000000 #define CST4319_RCAL_VALUE_SHIFT 25 /* 4336 chip-specific ChipStatus register bits */ #define CST4336_SPI_MODE_MASK 0x00000001 #define CST4336_SPROM_PRESENT 0x00000002 #define CST4336_OTP_PRESENT 0x00000004 #define CST4336_ARMREMAP_0 0x00000008 #define CST4336_ILPDIV_EN_MASK 0x00000010 #define CST4336_ILPDIV_EN_SHIFT 4 #define CST4336_XTAL_PD_POL_MASK 0x00000020 #define CST4336_XTAL_PD_POL_SHIFT 5 #define CST4336_LPO_SEL_MASK 0x00000040 #define CST4336_LPO_SEL_SHIFT 6 #define CST4336_RES_INIT_MODE_MASK 0x00000180 #define CST4336_RES_INIT_MODE_SHIFT 7 #define CST4336_CBUCK_MODE_MASK 0x00000600 #define CST4336_CBUCK_MODE_SHIFT 9 /* 4313 chip-specific ChipStatus register bits */ #define CST4313_SPROM_PRESENT 1 #define CST4313_OTP_PRESENT 2 #define CST4313_SPROM_OTP_SEL_MASK 0x00000002 #define CST4313_SPROM_OTP_SEL_SHIFT 0 /* 4313 Chip specific ChipControl register bits */ /* 12 mA drive strengh for later 4313 */ #define CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* Manufacturer Ids */ #define MFGID_ARM 0x43b #define MFGID_BRCM 0x4bf #define MFGID_MIPS 0x4a7 /* Enumeration ROM registers */ #define ER_EROMENTRY 0x000 #define ER_REMAPCONTROL 0xe00 #define ER_REMAPSELECT 0xe04 #define ER_MASTERSELECT 0xe10 #define ER_ITCR 0xf00 #define ER_ITIP 0xf04 /* Erom entries */ #define ER_TAG 0xe #define ER_TAG1 0x6 #define ER_VALID 1 #define ER_CI 0 #define ER_MP 2 #define ER_ADD 4 #define ER_END 0xe #define ER_BAD 0xffffffff /* EROM CompIdentA */ #define CIA_MFG_MASK 0xfff00000 #define CIA_MFG_SHIFT 20 #define CIA_CID_MASK 0x000fff00 #define CIA_CID_SHIFT 8 #define CIA_CCL_MASK 0x000000f0 #define CIA_CCL_SHIFT 4 /* EROM CompIdentB */ #define CIB_REV_MASK 0xff000000 #define CIB_REV_SHIFT 24 #define CIB_NSW_MASK 0x00f80000 #define CIB_NSW_SHIFT 19 #define CIB_NMW_MASK 0x0007c000 #define CIB_NMW_SHIFT 14 #define CIB_NSP_MASK 0x00003e00 #define CIB_NSP_SHIFT 9 #define CIB_NMP_MASK 0x000001f0 #define CIB_NMP_SHIFT 4 /* EROM AddrDesc */ #define AD_ADDR_MASK 0xfffff000 #define AD_SP_MASK 0x00000f00 #define AD_SP_SHIFT 8 #define AD_ST_MASK 0x000000c0 #define AD_ST_SHIFT 6 #define AD_ST_SLAVE 0x00000000 #define AD_ST_BRIDGE 0x00000040 #define AD_ST_SWRAP 0x00000080 #define AD_ST_MWRAP 0x000000c0 #define AD_SZ_MASK 0x00000030 #define AD_SZ_SHIFT 4 #define AD_SZ_4K 0x00000000 #define AD_SZ_8K 0x00000010 #define AD_SZ_16K 0x00000020 #define AD_SZ_SZD 0x00000030 #define AD_AG32 0x00000008 #define AD_ADDR_ALIGN 0x00000fff #define AD_SZ_BASE 0x00001000 /* 4KB */ /* EROM SizeDesc */ #define SD_SZ_MASK 0xfffff000 #define SD_SG32 0x00000008 #define SD_SZ_ALIGN 0x00000fff /* PCI config space bit 4 for 4306c0 slow clock source */ #define PCI_CFG_GPIO_SCS 0x10 /* PCI config space GPIO 14 for Xtal power-up */ #define PCI_CFG_GPIO_XTAL 0x40 /* PCI config space GPIO 15 for PLL power-down */ #define PCI_CFG_GPIO_PLL 0x80 /* power control defines */ #define PLL_DELAY 150 /* us pll on delay */ #define FREF_DELAY 200 /* us fref change delay */ #define XTAL_ON_DELAY 1000 /* us crystal power-on delay */ /* resetctrl */ #define AIRC_RESET 1 #define NOREV -1 /* Invalid rev */ /* GPIO Based LED powersave defines */ #define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */ #define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */ /* When Srom support present, fields in sromcontrol */ #define SRC_START 0x80000000 #define SRC_BUSY 0x80000000 #define SRC_OPCODE 0x60000000 #define SRC_OP_READ 0x00000000 #define SRC_OP_WRITE 0x20000000 #define SRC_OP_WRDIS 0x40000000 #define SRC_OP_WREN 0x60000000 #define SRC_OTPSEL 0x00000010 #define SRC_LOCK 0x00000008 #define SRC_SIZE_MASK 0x00000006 #define SRC_SIZE_1K 0x00000000 #define SRC_SIZE_4K 0x00000002 #define SRC_SIZE_16K 0x00000004 #define SRC_SIZE_SHIFT 1 #define SRC_PRESENT 0x00000001 /* External PA enable mask */ #define GPIO_CTRL_EPA_EN_MASK 0x40 #define DEFAULT_GPIOTIMERVAL \ ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME) #define BADIDX (SI_MAXCORES + 1) #define IS_SIM(chippkg) \ ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID)) #ifdef DEBUG #define SI_MSG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) #else #define SI_MSG(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* DEBUG */ #define GOODCOREADDR(x, b) \ (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \ IS_ALIGNED((x), SI_CORE_SIZE)) struct aidmp { u32 oobselina30; /* 0x000 */ u32 oobselina74; /* 0x004 */ u32 PAD[6]; u32 oobselinb30; /* 0x020 */ u32 oobselinb74; /* 0x024 */ u32 PAD[6]; u32 oobselinc30; /* 0x040 */ u32 oobselinc74; /* 0x044 */ u32 PAD[6]; u32 oobselind30; /* 0x060 */ u32 oobselind74; /* 0x064 */ u32 PAD[38]; u32 oobselouta30; /* 0x100 */ u32 oobselouta74; /* 0x104 */ u32 PAD[6]; u32 oobseloutb30; /* 0x120 */ u32 oobseloutb74; /* 0x124 */ u32 PAD[6]; u32 oobseloutc30; /* 0x140 */ u32 oobseloutc74; /* 0x144 */ u32 PAD[6]; u32 oobseloutd30; /* 0x160 */ u32 oobseloutd74; /* 0x164 */ u32 PAD[38]; u32 oobsynca; /* 0x200 */ u32 oobseloutaen; /* 0x204 */ u32 PAD[6]; u32 oobsyncb; /* 0x220 */ u32 oobseloutben; /* 0x224 */ u32 PAD[6]; u32 oobsyncc; /* 0x240 */ u32 oobseloutcen; /* 0x244 */ u32 PAD[6]; u32 oobsyncd; /* 0x260 */ u32 oobseloutden; /* 0x264 */ u32 PAD[38]; u32 oobaextwidth; /* 0x300 */ u32 oobainwidth; /* 0x304 */ u32 oobaoutwidth; /* 0x308 */ u32 PAD[5]; u32 oobbextwidth; /* 0x320 */ u32 oobbinwidth; /* 0x324 */ u32 oobboutwidth; /* 0x328 */ u32 PAD[5]; u32 oobcextwidth; /* 0x340 */ u32 oobcinwidth; /* 0x344 */ u32 oobcoutwidth; /* 0x348 */ u32 PAD[5]; u32 oobdextwidth; /* 0x360 */ u32 oobdinwidth; /* 0x364 */ u32 oobdoutwidth; /* 0x368 */ u32 PAD[37]; u32 ioctrlset; /* 0x400 */ u32 ioctrlclear; /* 0x404 */ u32 ioctrl; /* 0x408 */ u32 PAD[61]; u32 iostatus; /* 0x500 */ u32 PAD[127]; u32 ioctrlwidth; /* 0x700 */ u32 iostatuswidth; /* 0x704 */ u32 PAD[62]; u32 resetctrl; /* 0x800 */ u32 resetstatus; /* 0x804 */ u32 resetreadid; /* 0x808 */ u32 resetwriteid; /* 0x80c */ u32 PAD[60]; u32 errlogctrl; /* 0x900 */ u32 errlogdone; /* 0x904 */ u32 errlogstatus; /* 0x908 */ u32 errlogaddrlo; /* 0x90c */ u32 errlogaddrhi; /* 0x910 */ u32 errlogid; /* 0x914 */ u32 errloguser; /* 0x918 */ u32 errlogflags; /* 0x91c */ u32 PAD[56]; u32 intstatus; /* 0xa00 */ u32 PAD[127]; u32 config; /* 0xe00 */ u32 PAD[63]; u32 itcr; /* 0xf00 */ u32 PAD[3]; u32 itipooba; /* 0xf10 */ u32 itipoobb; /* 0xf14 */ u32 itipoobc; /* 0xf18 */ u32 itipoobd; /* 0xf1c */ u32 PAD[4]; u32 itipoobaout; /* 0xf30 */ u32 itipoobbout; /* 0xf34 */ u32 itipoobcout; /* 0xf38 */ u32 itipoobdout; /* 0xf3c */ u32 PAD[4]; u32 itopooba; /* 0xf50 */ u32 itopoobb; /* 0xf54 */ u32 itopoobc; /* 0xf58 */ u32 itopoobd; /* 0xf5c */ u32 PAD[4]; u32 itopoobain; /* 0xf70 */ u32 itopoobbin; /* 0xf74 */ u32 itopoobcin; /* 0xf78 */ u32 itopoobdin; /* 0xf7c */ u32 PAD[4]; u32 itopreset; /* 0xf90 */ u32 PAD[15]; u32 peripherialid4; /* 0xfd0 */ u32 peripherialid5; /* 0xfd4 */ u32 peripherialid6; /* 0xfd8 */ u32 peripherialid7; /* 0xfdc */ u32 peripherialid0; /* 0xfe0 */ u32 peripherialid1; /* 0xfe4 */ u32 peripherialid2; /* 0xfe8 */ u32 peripherialid3; /* 0xfec */ u32 componentid0; /* 0xff0 */ u32 componentid1; /* 0xff4 */ u32 componentid2; /* 0xff8 */ u32 componentid3; /* 0xffc */ }; static bool ai_buscore_setup(struct si_info *sii, struct bcma_device *cc) { /* no cores found, bail out */ if (cc->bus->nr_cores == 0) return false; /* get chipcommon rev */ sii->pub.ccrev = cc->id.rev; /* get chipcommon chipstatus */ sii->chipst = bcma_read32(cc, CHIPCREGOFFS(chipstatus)); /* get chipcommon capabilites */ sii->pub.cccaps = bcma_read32(cc, CHIPCREGOFFS(capabilities)); /* get pmu rev and caps */ if (ai_get_cccaps(&sii->pub) & CC_CAP_PMU) { sii->pub.pmucaps = bcma_read32(cc, CHIPCREGOFFS(pmucapabilities)); sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK; } return true; } static struct si_info *ai_doattach(struct si_info *sii, struct bcma_bus *pbus) { struct si_pub *sih = &sii->pub; struct bcma_device *cc; sii->icbus = pbus; sii->pcibus = pbus->host_pci; /* switch to Chipcommon core */ cc = pbus->drv_cc.core; sih->chip = pbus->chipinfo.id; sih->chiprev = pbus->chipinfo.rev; sih->chippkg = pbus->chipinfo.pkg; sih->boardvendor = pbus->boardinfo.vendor; sih->boardtype = pbus->boardinfo.type; if (!ai_buscore_setup(sii, cc)) goto exit; /* === NVRAM, clock is ready === */ bcma_write32(cc, CHIPCREGOFFS(gpiopullup), 0); bcma_write32(cc, CHIPCREGOFFS(gpiopulldown), 0); /* PMU specific initializations */ if (ai_get_cccaps(sih) & CC_CAP_PMU) { (void)si_pmu_measure_alpclk(sih); } return sii; exit: return NULL; } /* * Allocate a si handle and do the attach. */ struct si_pub * ai_attach(struct bcma_bus *pbus) { struct si_info *sii; /* alloc struct si_info */ sii = kzalloc(sizeof(struct si_info), GFP_ATOMIC); if (sii == NULL) return NULL; if (ai_doattach(sii, pbus) == NULL) { kfree(sii); return NULL; } return (struct si_pub *) sii; } /* may be called with core in reset */ void ai_detach(struct si_pub *sih) { struct si_info *sii; sii = container_of(sih, struct si_info, pub); if (sii == NULL) return; kfree(sii); } /* * read/modify chipcommon core register. */ uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val) { struct bcma_device *cc; u32 w; struct si_info *sii; sii = container_of(sih, struct si_info, pub); cc = sii->icbus->drv_cc.core; /* mask and set */ if (mask || val) bcma_maskset32(cc, regoff, ~mask, val); /* readback */ w = bcma_read32(cc, regoff); return w; } /* return the slow clock source - LPO, XTAL, or PCI */ static uint ai_slowclk_src(struct si_pub *sih, struct bcma_device *cc) { return SCC_SS_XTAL; } /* * return the ILP (slowclock) min or max frequency * precondition: we've established the chip has dynamic clk control */ static uint ai_slowclk_freq(struct si_pub *sih, bool max_freq, struct bcma_device *cc) { uint div; /* Chipc rev 10 is InstaClock */ div = bcma_read32(cc, CHIPCREGOFFS(system_clk_ctl)); div = 4 * ((div >> SYCC_CD_SHIFT) + 1); return max_freq ? XTALMAXFREQ : (XTALMINFREQ / div); } static void ai_clkctl_setdelay(struct si_pub *sih, struct bcma_device *cc) { uint slowmaxfreq, pll_delay, slowclk; uint pll_on_delay, fref_sel_delay; pll_delay = PLL_DELAY; /* * If the slow clock is not sourced by the xtal then * add the xtal_on_delay since the xtal will also be * powered down by dynamic clk control logic. */ slowclk = ai_slowclk_src(sih, cc); if (slowclk != SCC_SS_XTAL) pll_delay += XTAL_ON_DELAY; /* Starting with 4318 it is ILP that is used for the delays */ slowmaxfreq = ai_slowclk_freq(sih, false, cc); pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; bcma_write32(cc, CHIPCREGOFFS(pll_on_delay), pll_on_delay); bcma_write32(cc, CHIPCREGOFFS(fref_sel_delay), fref_sel_delay); } /* initialize power control delay registers */ void ai_clkctl_init(struct si_pub *sih) { struct si_info *sii = container_of(sih, struct si_info, pub); struct bcma_device *cc; if (!(ai_get_cccaps(sih) & CC_CAP_PWR_CTL)) return; cc = sii->icbus->drv_cc.core; if (cc == NULL) return; /* set all Instaclk chip ILP to 1 MHz */ bcma_maskset32(cc, CHIPCREGOFFS(system_clk_ctl), SYCC_CD_MASK, (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); ai_clkctl_setdelay(sih, cc); } /* * return the value suitable for writing to the * dot11 core FAST_PWRUP_DELAY register */ u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih) { struct si_info *sii; struct bcma_device *cc; uint slowminfreq; u16 fpdelay; sii = container_of(sih, struct si_info, pub); if (ai_get_cccaps(sih) & CC_CAP_PMU) { fpdelay = si_pmu_fast_pwrup_delay(sih); return fpdelay; } if (!(ai_get_cccaps(sih) & CC_CAP_PWR_CTL)) return 0; fpdelay = 0; cc = sii->icbus->drv_cc.core; if (cc) { slowminfreq = ai_slowclk_freq(sih, false, cc); fpdelay = (((bcma_read32(cc, CHIPCREGOFFS(pll_on_delay)) + 2) * 1000000) + (slowminfreq - 1)) / slowminfreq; } return fpdelay; } /* * clock control policy function throught chipcommon * * set dynamic clk control mode (forceslow, forcefast, dynamic) * returns true if we are forcing fast clock * this is a wrapper over the next internal function * to allow flexible policy settings for outside caller */ bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode) { struct si_info *sii; struct bcma_device *cc; sii = container_of(sih, struct si_info, pub); cc = sii->icbus->drv_cc.core; bcma_core_set_clockmode(cc, mode); return mode == BCMA_CLKMODE_FAST; } void ai_pci_up(struct si_pub *sih) { struct si_info *sii; sii = container_of(sih, struct si_info, pub); if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci, true); } /* Unconfigure and/or apply various WARs when going down */ void ai_pci_down(struct si_pub *sih) { struct si_info *sii; sii = container_of(sih, struct si_info, pub); if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci, false); } /* Enable BT-COEX & Ex-PA for 4313 */ void ai_epa_4313war(struct si_pub *sih) { struct si_info *sii = container_of(sih, struct si_info, pub); struct bcma_device *cc; cc = sii->icbus->drv_cc.core; /* EPA Fix */ bcma_set32(cc, CHIPCREGOFFS(gpiocontrol), GPIO_CTRL_EPA_EN_MASK); } /* check if the device is removed */ bool ai_deviceremoved(struct si_pub *sih) { u32 w; struct si_info *sii; sii = container_of(sih, struct si_info, pub); if (sii->icbus->hosttype != BCMA_HOSTTYPE_PCI) return false; pci_read_config_dword(sii->pcibus, PCI_VENDOR_ID, &w); if ((w & 0xFFFF) != PCI_VENDOR_ID_BROADCOM) return true; return false; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/main.c0000644000175000017500000070016612026211315024671 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include "rate.h" #include "scb.h" #include "phy/phy_hal.h" #include "channel.h" #include "antsel.h" #include "stf.h" #include "ampdu.h" #include "mac80211_if.h" #include "ucode_loader.h" #include "main.h" #include "soc.h" /* * Indication for txflowcontrol that all priority bits in * TXQ_STOP_FOR_PRIOFC_MASK are to be considered. */ #define ALLPRIO -1 /* watchdog timer, in unit of ms */ #define TIMER_INTERVAL_WATCHDOG 1000 /* radio monitor timer, in unit of ms */ #define TIMER_INTERVAL_RADIOCHK 800 /* beacon interval, in unit of 1024TU */ #define BEACON_INTERVAL_DEFAULT 100 /* n-mode support capability */ /* 2x2 includes both 1x1 & 2x2 devices * reserved #define 2 for future when we want to separate 1x1 & 2x2 and * control it independently */ #define WL_11N_2x2 1 #define WL_11N_3x3 3 #define WL_11N_4x4 4 #define EDCF_ACI_MASK 0x60 #define EDCF_ACI_SHIFT 5 #define EDCF_ECWMIN_MASK 0x0f #define EDCF_ECWMAX_SHIFT 4 #define EDCF_AIFSN_MASK 0x0f #define EDCF_AIFSN_MAX 15 #define EDCF_ECWMAX_MASK 0xf0 #define EDCF_AC_BE_TXOP_STA 0x0000 #define EDCF_AC_BK_TXOP_STA 0x0000 #define EDCF_AC_VO_ACI_STA 0x62 #define EDCF_AC_VO_ECW_STA 0x32 #define EDCF_AC_VI_ACI_STA 0x42 #define EDCF_AC_VI_ECW_STA 0x43 #define EDCF_AC_BK_ECW_STA 0xA4 #define EDCF_AC_VI_TXOP_STA 0x005e #define EDCF_AC_VO_TXOP_STA 0x002f #define EDCF_AC_BE_ACI_STA 0x03 #define EDCF_AC_BE_ECW_STA 0xA4 #define EDCF_AC_BK_ACI_STA 0x27 #define EDCF_AC_VO_TXOP_AP 0x002f #define EDCF_TXOP2USEC(txop) ((txop) << 5) #define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) #define APHY_SYMBOL_TIME 4 #define APHY_PREAMBLE_TIME 16 #define APHY_SIGNAL_TIME 4 #define APHY_SIFS_TIME 16 #define APHY_SERVICE_NBITS 16 #define APHY_TAIL_NBITS 6 #define BPHY_SIFS_TIME 10 #define BPHY_PLCP_SHORT_TIME 96 #define PREN_PREAMBLE 24 #define PREN_MM_EXT 12 #define PREN_PREAMBLE_EXT 4 #define DOT11_MAC_HDR_LEN 24 #define DOT11_ACK_LEN 10 #define DOT11_BA_LEN 4 #define DOT11_OFDM_SIGNAL_EXTENSION 6 #define DOT11_MIN_FRAG_LEN 256 #define DOT11_RTS_LEN 16 #define DOT11_CTS_LEN 10 #define DOT11_BA_BITMAP_LEN 128 #define DOT11_MIN_BEACON_PERIOD 1 #define DOT11_MAX_BEACON_PERIOD 0xFFFF #define DOT11_MAXNUMFRAGS 16 #define DOT11_MAX_FRAG_LEN 2346 #define BPHY_PLCP_TIME 192 #define RIFS_11N_TIME 2 /* length of the BCN template area */ #define BCN_TMPL_LEN 512 /* brcms_bss_info flag bit values */ #define BRCMS_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */ /* chip rx buffer offset */ #define BRCMS_HWRXOFF 38 /* rfdisable delay timer 500 ms, runs of ALP clock */ #define RFDISABLE_DEFAULT 10000000 #define BRCMS_TEMPSENSE_PERIOD 10 /* 10 second timeout */ /* precedences numbers for wlc queues. These are twice as may levels as * 802.1D priorities. * Odd numbers are used for HI priority traffic at same precedence levels * These constants are used ONLY by wlc_prio2prec_map. Do not use them * elsewhere. */ #define _BRCMS_PREC_NONE 0 /* None = - */ #define _BRCMS_PREC_BK 2 /* BK - Background */ #define _BRCMS_PREC_BE 4 /* BE - Best-effort */ #define _BRCMS_PREC_EE 6 /* EE - Excellent-effort */ #define _BRCMS_PREC_CL 8 /* CL - Controlled Load */ #define _BRCMS_PREC_VI 10 /* Vi - Video */ #define _BRCMS_PREC_VO 12 /* Vo - Voice */ #define _BRCMS_PREC_NC 14 /* NC - Network Control */ /* synthpu_dly times in us */ #define SYNTHPU_DLY_APHY_US 3700 #define SYNTHPU_DLY_BPHY_US 1050 #define SYNTHPU_DLY_NPHY_US 2048 #define SYNTHPU_DLY_LPPHY_US 300 #define ANTCNT 10 /* vanilla M_MAX_ANTCNT val */ /* Per-AC retry limit register definitions; uses defs.h bitfield macros */ #define EDCF_SHORT_S 0 #define EDCF_SFB_S 4 #define EDCF_LONG_S 8 #define EDCF_LFB_S 12 #define EDCF_SHORT_M BITFIELD_MASK(4) #define EDCF_SFB_M BITFIELD_MASK(4) #define EDCF_LONG_M BITFIELD_MASK(4) #define EDCF_LFB_M BITFIELD_MASK(4) #define RETRY_SHORT_DEF 7 /* Default Short retry Limit */ #define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */ #define RETRY_LONG_DEF 4 /* Default Long retry count */ #define RETRY_SHORT_FB 3 /* Short count for fb rate */ #define RETRY_LONG_FB 2 /* Long count for fb rate */ #define APHY_CWMIN 15 #define PHY_CWMAX 1023 #define EDCF_AIFSN_MIN 1 #define FRAGNUM_MASK 0xF #define APHY_SLOT_TIME 9 #define BPHY_SLOT_TIME 20 #define WL_SPURAVOID_OFF 0 #define WL_SPURAVOID_ON1 1 #define WL_SPURAVOID_ON2 2 /* invalid core flags, use the saved coreflags */ #define BRCMS_USE_COREFLAGS 0xffffffff /* values for PLCPHdr_override */ #define BRCMS_PLCP_AUTO -1 #define BRCMS_PLCP_SHORT 0 #define BRCMS_PLCP_LONG 1 /* values for g_protection_override and n_protection_override */ #define BRCMS_PROTECTION_AUTO -1 #define BRCMS_PROTECTION_OFF 0 #define BRCMS_PROTECTION_ON 1 #define BRCMS_PROTECTION_MMHDR_ONLY 2 #define BRCMS_PROTECTION_CTS_ONLY 3 /* values for g_protection_control and n_protection_control */ #define BRCMS_PROTECTION_CTL_OFF 0 #define BRCMS_PROTECTION_CTL_LOCAL 1 #define BRCMS_PROTECTION_CTL_OVERLAP 2 /* values for n_protection */ #define BRCMS_N_PROTECTION_OFF 0 #define BRCMS_N_PROTECTION_OPTIONAL 1 #define BRCMS_N_PROTECTION_20IN40 2 #define BRCMS_N_PROTECTION_MIXEDMODE 3 /* values for band specific 40MHz capabilities */ #define BRCMS_N_BW_20ALL 0 #define BRCMS_N_BW_40ALL 1 #define BRCMS_N_BW_20IN2G_40IN5G 2 /* bitflags for SGI support (sgi_rx iovar) */ #define BRCMS_N_SGI_20 0x01 #define BRCMS_N_SGI_40 0x02 /* defines used by the nrate iovar */ /* MSC in use,indicates b0-6 holds an mcs */ #define NRATE_MCS_INUSE 0x00000080 /* rate/mcs value */ #define NRATE_RATE_MASK 0x0000007f /* stf mode mask: siso, cdd, stbc, sdm */ #define NRATE_STF_MASK 0x0000ff00 /* stf mode shift */ #define NRATE_STF_SHIFT 8 /* bit indicate to override mcs only */ #define NRATE_OVERRIDE_MCS_ONLY 0x40000000 #define NRATE_SGI_MASK 0x00800000 /* sgi mode */ #define NRATE_SGI_SHIFT 23 /* sgi mode */ #define NRATE_LDPC_CODING 0x00400000 /* adv coding in use */ #define NRATE_LDPC_SHIFT 22 /* ldpc shift */ #define NRATE_STF_SISO 0 /* stf mode SISO */ #define NRATE_STF_CDD 1 /* stf mode CDD */ #define NRATE_STF_STBC 2 /* stf mode STBC */ #define NRATE_STF_SDM 3 /* stf mode SDM */ #define MAX_DMA_SEGS 4 /* Max # of entries in Tx FIFO based on 4kb page size */ #define NTXD 256 /* Max # of entries in Rx FIFO based on 4kb page size */ #define NRXD 256 /* try to keep this # rbufs posted to the chip */ #define NRXBUFPOST 32 /* data msg txq hiwat mark */ #define BRCMS_DATAHIWAT 50 /* max # frames to process in brcms_c_recv() */ #define RXBND 8 /* max # tx status to process in wlc_txstatus() */ #define TXSBND 8 /* brcmu_format_flags() bit description structure */ struct brcms_c_bit_desc { u32 bit; const char *name; }; /* * The following table lists the buffer memory allocated to xmt fifos in HW. * the size is in units of 256bytes(one block), total size is HW dependent * ucode has default fifo partition, sw can overwrite if necessary * * This is documented in twiki under the topic UcodeTxFifo. Please ensure * the twiki is updated before making changes. */ /* Starting corerev for the fifo size table */ #define XMTFIFOTBL_STARTREV 17 struct d11init { __le16 addr; __le16 size; __le32 value; }; struct edcf_acparam { u8 ACI; u8 ECW; u16 TXOP; } __packed; const u8 prio2fifo[NUMPRIO] = { TX_AC_BE_FIFO, /* 0 BE AC_BE Best Effort */ TX_AC_BK_FIFO, /* 1 BK AC_BK Background */ TX_AC_BK_FIFO, /* 2 -- AC_BK Background */ TX_AC_BE_FIFO, /* 3 EE AC_BE Best Effort */ TX_AC_VI_FIFO, /* 4 CL AC_VI Video */ TX_AC_VI_FIFO, /* 5 VI AC_VI Video */ TX_AC_VO_FIFO, /* 6 VO AC_VO Voice */ TX_AC_VO_FIFO /* 7 NC AC_VO Voice */ }; /* debug/trace */ uint brcm_msg_level = #if defined(DEBUG) LOG_ERROR_VAL; #else 0; #endif /* DEBUG */ /* TX FIFO number to WME/802.1E Access Category */ static const u8 wme_fifo2ac[] = { IEEE80211_AC_BK, IEEE80211_AC_BE, IEEE80211_AC_VI, IEEE80211_AC_VO, IEEE80211_AC_BE, IEEE80211_AC_BE }; /* ieee80211 Access Category to TX FIFO number */ static const u8 wme_ac2fifo[] = { TX_AC_VO_FIFO, TX_AC_VI_FIFO, TX_AC_BE_FIFO, TX_AC_BK_FIFO }; /* 802.1D Priority to precedence queue mapping */ const u8 wlc_prio2prec_map[] = { _BRCMS_PREC_BE, /* 0 BE - Best-effort */ _BRCMS_PREC_BK, /* 1 BK - Background */ _BRCMS_PREC_NONE, /* 2 None = - */ _BRCMS_PREC_EE, /* 3 EE - Excellent-effort */ _BRCMS_PREC_CL, /* 4 CL - Controlled Load */ _BRCMS_PREC_VI, /* 5 Vi - Video */ _BRCMS_PREC_VO, /* 6 Vo - Voice */ _BRCMS_PREC_NC, /* 7 NC - Network Control */ }; static const u16 xmtfifo_sz[][NFIFO] = { /* corerev 17: 5120, 49152, 49152, 5376, 4352, 1280 */ {20, 192, 192, 21, 17, 5}, /* corerev 18: */ {0, 0, 0, 0, 0, 0}, /* corerev 19: */ {0, 0, 0, 0, 0, 0}, /* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */ {20, 192, 192, 21, 17, 5}, /* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */ {9, 58, 22, 14, 14, 5}, /* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */ {20, 192, 192, 21, 17, 5}, /* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */ {20, 192, 192, 21, 17, 5}, /* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */ {9, 58, 22, 14, 14, 5}, /* corerev 25: */ {0, 0, 0, 0, 0, 0}, /* corerev 26: */ {0, 0, 0, 0, 0, 0}, /* corerev 27: */ {0, 0, 0, 0, 0, 0}, /* corerev 28: 2304, 14848, 5632, 3584, 3584, 1280 */ {9, 58, 22, 14, 14, 5}, }; #ifdef DEBUG static const char * const fifo_names[] = { "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" }; #else static const char fifo_names[6][0]; #endif #ifdef DEBUG /* pointer to most recently allocated wl/wlc */ static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); #endif /* Find basic rate for a given rate */ static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec) { if (is_mcs_rate(rspec)) return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK] .leg_ofdm]; return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK]; } static u16 frametype(u32 rspec, u8 mimoframe) { if (is_mcs_rate(rspec)) return mimoframe; return is_cck_rate(rspec) ? FT_CCK : FT_OFDM; } /* currently the best mechanism for determining SIFS is the band in use */ static u16 get_sifs(struct brcms_band *band) { return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME : BPHY_SIFS_TIME; } /* * Detect Card removed. * Even checking an sbconfig register read will not false trigger when the core * is in reset it breaks CF address mechanism. Accessing gphy phyversion will * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible * reg with fixed 0/1 pattern (some platforms return all 0). * If clocks are present, call the sb routine which will figure out if the * device is removed. */ static bool brcms_deviceremoved(struct brcms_c_info *wlc) { u32 macctrl; if (!wlc->hw->clk) return ai_deviceremoved(wlc->hw->sih); macctrl = bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); return (macctrl & (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN; } /* sum the individual fifo tx pending packet counts */ static s16 brcms_txpktpendtot(struct brcms_c_info *wlc) { return wlc->core->txpktpend[0] + wlc->core->txpktpend[1] + wlc->core->txpktpend[2] + wlc->core->txpktpend[3]; } static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc) { return wlc->pub->_nbands > 1 && !wlc->bandlocked; } static int brcms_chspec_bw(u16 chanspec) { if (CHSPEC_IS40(chanspec)) return BRCMS_40_MHZ; if (CHSPEC_IS20(chanspec)) return BRCMS_20_MHZ; return BRCMS_10_MHZ; } static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) { if (cfg == NULL) return; kfree(cfg->current_bss); kfree(cfg); } static void brcms_c_detach_mfree(struct brcms_c_info *wlc) { if (wlc == NULL) return; brcms_c_bsscfg_mfree(wlc->bsscfg); kfree(wlc->pub); kfree(wlc->modulecb); kfree(wlc->default_bss); kfree(wlc->protection); kfree(wlc->stf); kfree(wlc->bandstate[0]); kfree(wlc->corestate->macstat_snapshot); kfree(wlc->corestate); kfree(wlc->hw->bandstate[0]); kfree(wlc->hw); /* free the wlc */ kfree(wlc); wlc = NULL; } static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) { struct brcms_bss_cfg *cfg; cfg = kzalloc(sizeof(struct brcms_bss_cfg), GFP_ATOMIC); if (cfg == NULL) goto fail; cfg->current_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); if (cfg->current_bss == NULL) goto fail; return cfg; fail: brcms_c_bsscfg_mfree(cfg); return NULL; } static struct brcms_c_info * brcms_c_attach_malloc(uint unit, uint *err, uint devid) { struct brcms_c_info *wlc; wlc = kzalloc(sizeof(struct brcms_c_info), GFP_ATOMIC); if (wlc == NULL) { *err = 1002; goto fail; } /* allocate struct brcms_c_pub state structure */ wlc->pub = kzalloc(sizeof(struct brcms_pub), GFP_ATOMIC); if (wlc->pub == NULL) { *err = 1003; goto fail; } wlc->pub->wlc = wlc; /* allocate struct brcms_hardware state structure */ wlc->hw = kzalloc(sizeof(struct brcms_hardware), GFP_ATOMIC); if (wlc->hw == NULL) { *err = 1005; goto fail; } wlc->hw->wlc = wlc; wlc->hw->bandstate[0] = kzalloc(sizeof(struct brcms_hw_band) * MAXBANDS, GFP_ATOMIC); if (wlc->hw->bandstate[0] == NULL) { *err = 1006; goto fail; } else { int i; for (i = 1; i < MAXBANDS; i++) wlc->hw->bandstate[i] = (struct brcms_hw_band *) ((unsigned long)wlc->hw->bandstate[0] + (sizeof(struct brcms_hw_band) * i)); } wlc->modulecb = kzalloc(sizeof(struct modulecb) * BRCMS_MAXMODULES, GFP_ATOMIC); if (wlc->modulecb == NULL) { *err = 1009; goto fail; } wlc->default_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); if (wlc->default_bss == NULL) { *err = 1010; goto fail; } wlc->bsscfg = brcms_c_bsscfg_malloc(unit); if (wlc->bsscfg == NULL) { *err = 1011; goto fail; } wlc->protection = kzalloc(sizeof(struct brcms_protection), GFP_ATOMIC); if (wlc->protection == NULL) { *err = 1016; goto fail; } wlc->stf = kzalloc(sizeof(struct brcms_stf), GFP_ATOMIC); if (wlc->stf == NULL) { *err = 1017; goto fail; } wlc->bandstate[0] = kzalloc(sizeof(struct brcms_band)*MAXBANDS, GFP_ATOMIC); if (wlc->bandstate[0] == NULL) { *err = 1025; goto fail; } else { int i; for (i = 1; i < MAXBANDS; i++) wlc->bandstate[i] = (struct brcms_band *) ((unsigned long)wlc->bandstate[0] + (sizeof(struct brcms_band)*i)); } wlc->corestate = kzalloc(sizeof(struct brcms_core), GFP_ATOMIC); if (wlc->corestate == NULL) { *err = 1026; goto fail; } wlc->corestate->macstat_snapshot = kzalloc(sizeof(struct macstat), GFP_ATOMIC); if (wlc->corestate->macstat_snapshot == NULL) { *err = 1027; goto fail; } return wlc; fail: brcms_c_detach_mfree(wlc); return NULL; } /* * Update the slot timing for standard 11b/g (20us slots) * or shortslot 11g (9us slots) * The PSM needs to be suspended for this call. */ static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw, bool shortslot) { struct bcma_device *core = wlc_hw->d11core; if (shortslot) { /* 11g short slot: 11a timing */ bcma_write16(core, D11REGOFFS(ifs_slot), 0x0207); brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME); } else { /* 11g long slot: 11b timing */ bcma_write16(core, D11REGOFFS(ifs_slot), 0x0212); brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME); } } /* * calculate frame duration of a given rate and length, return * time in usec unit */ static uint brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, u8 preamble_type, uint mac_len) { uint nsyms, dur = 0, Ndps, kNdps; uint rate = rspec2rate(ratespec); if (rate == 0) { wiphy_err(wlc->wiphy, "wl%d: WAR: using rate of 1 mbps\n", wlc->pub->unit); rate = BRCM_RATE_1M; } BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, len%d\n", wlc->pub->unit, ratespec, preamble_type, mac_len); if (is_mcs_rate(ratespec)) { uint mcs = ratespec & RSPEC_RATE_MASK; int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); if (preamble_type == BRCMS_MM_PREAMBLE) dur += PREN_MM_EXT; /* 1000Ndbps = kbps * 4 */ kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), rspec_issgi(ratespec)) * 4; if (rspec_stc(ratespec) == 0) nsyms = CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS) * 1000, kNdps); else /* STBC needs to have even number of symbols */ nsyms = 2 * CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS) * 1000, 2 * kNdps); dur += APHY_SYMBOL_TIME * nsyms; if (wlc->band->bandtype == BRCM_BAND_2G) dur += DOT11_OFDM_SIGNAL_EXTENSION; } else if (is_ofdm_rate(rate)) { dur = APHY_PREAMBLE_TIME; dur += APHY_SIGNAL_TIME; /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ Ndps = rate * 2; /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */ nsyms = CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS), Ndps); dur += APHY_SYMBOL_TIME * nsyms; if (wlc->band->bandtype == BRCM_BAND_2G) dur += DOT11_OFDM_SIGNAL_EXTENSION; } else { /* * calc # bits * 2 so factor of 2 in rate (1/2 mbps) * will divide out */ mac_len = mac_len * 8 * 2; /* calc ceiling of bits/rate = microseconds of air time */ dur = (mac_len + rate - 1) / rate; if (preamble_type & BRCMS_SHORT_PREAMBLE) dur += BPHY_PLCP_SHORT_TIME; else dur += BPHY_PLCP_TIME; } return dur; } static void brcms_c_write_inits(struct brcms_hardware *wlc_hw, const struct d11init *inits) { struct bcma_device *core = wlc_hw->d11core; int i; uint offset; u16 size; u32 value; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); for (i = 0; inits[i].addr != cpu_to_le16(0xffff); i++) { size = le16_to_cpu(inits[i].size); offset = le16_to_cpu(inits[i].addr); value = le32_to_cpu(inits[i].value); if (size == 2) bcma_write16(core, offset, value); else if (size == 4) bcma_write32(core, offset, value); else break; } } static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs) { u8 idx; u16 addr[] = { M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, M_HOST_FLAGS5 }; for (idx = 0; idx < MHFMAX; idx++) brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]); } static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw) { struct wiphy *wiphy = wlc_hw->wlc->wiphy; struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; /* init microcode host flags */ brcms_c_write_mhf(wlc_hw, wlc_hw->band->mhfs); /* do band-specific ucode IHR, SHM, and SCR inits */ if (D11REV_IS(wlc_hw->corerev, 23)) { if (BRCMS_ISNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16); else wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev" " %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else { if (D11REV_IS(wlc_hw->corerev, 24)) { if (BRCMS_ISLCNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11lcn0bsinitvals24); else wiphy_err(wiphy, "%s: wl%d: unsupported phy in" " core rev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else { wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } } } static void brcms_b_core_ioctl(struct brcms_hardware *wlc_hw, u32 m, u32 v) { struct bcma_device *core = wlc_hw->d11core; u32 ioctl = bcma_aread32(core, BCMA_IOCTL) & ~m; bcma_awrite32(core, BCMA_IOCTL, ioctl | v); } static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d: clk %d\n", wlc_hw->unit, clk); wlc_hw->phyclk = clk; if (OFF == clk) { /* clear gmode bit, put phy into reset */ brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC | SICF_GMODE), (SICF_PRST | SICF_FGC)); udelay(1); brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_PRST); udelay(1); } else { /* take phy out of reset */ brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_FGC); udelay(1); brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); udelay(1); } } /* low-level band switch utility routine */ static void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, bandunit); wlc_hw->band = wlc_hw->bandstate[bandunit]; /* * BMAC_NOTE: * until we eliminate need for wlc->band refs in low level code */ wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit]; /* set gmode core flag */ if (wlc_hw->sbclk && !wlc_hw->noreset) { u32 gmode = 0; if (bandunit == 0) gmode = SICF_GMODE; brcms_b_core_ioctl(wlc_hw, SICF_GMODE, gmode); } } /* switch to new band but leave it inactive */ static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) { struct brcms_hardware *wlc_hw = wlc->hw; u32 macintmask; u32 macctrl; BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); macctrl = bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)); WARN_ON((macctrl & MCTL_EN_MAC) != 0); /* disable interrupts */ macintmask = brcms_intrsoff(wlc->wl); /* radio off */ wlc_phy_switch_radio(wlc_hw->band->pi, OFF); brcms_b_core_phy_clk(wlc_hw, OFF); brcms_c_setxband(wlc_hw, bandunit); return macintmask; } /* process an individual struct tx_status */ static bool brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) { struct sk_buff *p; uint queue; struct d11txh *txh; struct scb *scb = NULL; bool free_pdu; int tx_rts, tx_frame_count, tx_rts_count; uint totlen, supr_status; bool lastframe; struct ieee80211_hdr *h; u16 mcl; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *txrate; int i; /* discard intermediate indications for ucode with one legitimate case: * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, * but the subsequent tx of DATA failed. so it will start rts/cts * from the beginning (resetting the rts transmission count) */ if (!(txs->status & TX_STATUS_AMPDU) && (txs->status & TX_STATUS_INTERMEDIATE)) { BCMMSG(wlc->wiphy, "INTERMEDIATE but not AMPDU\n"); return false; } queue = txs->frameid & TXFID_QUEUE_MASK; if (queue >= NFIFO) { p = NULL; goto fatal; } p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); if (p == NULL) goto fatal; txh = (struct d11txh *) (p->data); mcl = le16_to_cpu(txh->MacTxControlLow); if (txs->phyerr) { if (brcm_msg_level & LOG_ERROR_VAL) { wiphy_err(wlc->wiphy, "phyerr 0x%x, rate 0x%x\n", txs->phyerr, txh->MainRates); brcms_c_print_txdesc(txh); } brcms_c_print_txstatus(txs); } if (txs->frameid != le16_to_cpu(txh->TxFrameID)) goto fatal; tx_info = IEEE80211_SKB_CB(p); h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); if (tx_info->rate_driver_data[0]) scb = &wlc->pri_scb; if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs); return false; } supr_status = txs->status & TX_STATUS_SUPR_MASK; if (supr_status == TX_STATUS_SUPR_BADCH) BCMMSG(wlc->wiphy, "%s: Pkt tx suppressed, possibly channel %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)); tx_rts = le16_to_cpu(txh->MacTxControlLow) & TXC_SENDRTS; tx_frame_count = (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT; tx_rts_count = (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT; lastframe = !ieee80211_has_morefrags(h->frame_control); if (!lastframe) { wiphy_err(wlc->wiphy, "Not last frame!\n"); } else { /* * Set information to be consumed by Minstrel ht. * * The "fallback limit" is the number of tx attempts a given * MPDU is sent at the "primary" rate. Tx attempts beyond that * limit are sent at the "secondary" rate. * A 'short frame' does not exceed RTS treshold. */ u16 sfbl, /* Short Frame Rate Fallback Limit */ lfbl, /* Long Frame Rate Fallback Limit */ fbl; if (queue < IEEE80211_NUM_ACS) { sfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], EDCF_SFB); lfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], EDCF_LFB); } else { sfbl = wlc->SFBL; lfbl = wlc->LFBL; } txrate = tx_info->status.rates; if (txrate[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) fbl = lfbl; else fbl = sfbl; ieee80211_tx_info_clear_status(tx_info); if ((tx_frame_count > fbl) && (txrate[1].idx >= 0)) { /* * rate selection requested a fallback rate * and we used it */ txrate[0].count = fbl; txrate[1].count = tx_frame_count - fbl; } else { /* * rate selection did not request fallback rate, or * we didn't need it */ txrate[0].count = tx_frame_count; /* * rc80211_minstrel.c:minstrel_tx_status() expects * unused rates to be marked with idx = -1 */ txrate[1].idx = -1; txrate[1].count = 0; } /* clear the rest of the rates */ for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { txrate[i].idx = -1; txrate[i].count = 0; } if (txs->status & TX_STATUS_ACK_RCV) tx_info->flags |= IEEE80211_TX_STAT_ACK; } totlen = p->len; free_pdu = true; brcms_c_txfifo_complete(wlc, queue, 1); if (lastframe) { /* remove PLCP & Broadcom tx descriptor header */ skb_pull(p, D11_PHY_HDR_LEN); skb_pull(p, D11_TXH_LEN); ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); } else { wiphy_err(wlc->wiphy, "%s: Not last frame => not calling " "tx_status\n", __func__); } return false; fatal: if (p) brcmu_pkt_buf_free_skb(p); return true; } /* process tx completion events in BMAC * Return true if more tx status need to be processed. false otherwise. */ static bool brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) { bool morepending = false; struct brcms_c_info *wlc = wlc_hw->wlc; struct bcma_device *core; struct tx_status txstatus, *txs; u32 s1, s2; uint n = 0; /* * Param 'max_tx_num' indicates max. # tx status to process before * break out. */ uint max_tx_num = bound ? TXSBND : -1; BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); txs = &txstatus; core = wlc_hw->d11core; *fatal = false; s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); while (!(*fatal) && (s1 & TXS_V)) { if (s1 == 0xffffffff) { wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); return morepending; } s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2)); txs->status = s1 & TXS_STATUS_MASK; txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT; txs->sequence = s2 & TXS_SEQ_MASK; txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT; txs->lasttxtime = 0; *fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs); /* !give others some time to run! */ if (++n >= max_tx_num) break; s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); } if (*fatal) return 0; if (n >= max_tx_num) morepending = true; if (!pktq_empty(&wlc->pkt_queue->q)) brcms_c_send_q(wlc); return morepending; } static void brcms_c_tbtt(struct brcms_c_info *wlc) { if (!wlc->bsscfg->BSS) /* * DirFrmQ is now valid...defer setting until end * of ATIM window */ wlc->qvalid |= MCMD_DIRFRMQVAL; } /* set initial host flags value */ static void brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init) { struct brcms_hardware *wlc_hw = wlc->hw; memset(mhfs, 0, MHFMAX * sizeof(u16)); mhfs[MHF2] |= mhf2_init; /* prohibit use of slowclock on multifunction boards */ if (wlc_hw->boardflags & BFL_NOPLLDOWN) mhfs[MHF1] |= MHF1_FORCEFASTCLK; if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) { mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR; mhfs[MHF1] |= MHF1_IQSWAP_WAR; } } static uint dmareg(uint direction, uint fifonum) { if (direction == DMA_TX) return offsetof(struct d11regs, fifo64regs[fifonum].dmaxmt); return offsetof(struct d11regs, fifo64regs[fifonum].dmarcv); } static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) { uint i; char name[8]; /* * ucode host flag 2 needed for pio mode, independent of band and fifo */ u16 pio_mhf2 = 0; struct brcms_hardware *wlc_hw = wlc->hw; uint unit = wlc_hw->unit; struct wiphy *wiphy = wlc->wiphy; /* name and offsets for dma_attach */ snprintf(name, sizeof(name), "wl%d", unit); if (wlc_hw->di[0] == NULL) { /* Init FIFOs */ int dma_attach_err = 0; /* * FIFO 0 * TX: TX_AC_BK_FIFO (TX AC Background data packets) * RX: RX_FIFO (RX data packets) */ wlc_hw->di[0] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, (wme ? dmareg(DMA_TX, 0) : 0), dmareg(DMA_RX, 0), (wme ? NTXD : 0), NRXD, RXBUFSZ, -1, NRXBUFPOST, BRCMS_HWRXOFF, &brcm_msg_level); dma_attach_err |= (NULL == wlc_hw->di[0]); /* * FIFO 1 * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets) * (legacy) TX_DATA_FIFO (TX data packets) * RX: UNUSED */ wlc_hw->di[1] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, dmareg(DMA_TX, 1), 0, NTXD, 0, 0, -1, 0, 0, &brcm_msg_level); dma_attach_err |= (NULL == wlc_hw->di[1]); /* * FIFO 2 * TX: TX_AC_VI_FIFO (TX AC Video data packets) * RX: UNUSED */ wlc_hw->di[2] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, dmareg(DMA_TX, 2), 0, NTXD, 0, 0, -1, 0, 0, &brcm_msg_level); dma_attach_err |= (NULL == wlc_hw->di[2]); /* * FIFO 3 * TX: TX_AC_VO_FIFO (TX AC Voice data packets) * (legacy) TX_CTL_FIFO (TX control & mgmt packets) */ wlc_hw->di[3] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, dmareg(DMA_TX, 3), 0, NTXD, 0, 0, -1, 0, 0, &brcm_msg_level); dma_attach_err |= (NULL == wlc_hw->di[3]); /* Cleaner to leave this as if with AP defined */ if (dma_attach_err) { wiphy_err(wiphy, "wl%d: wlc_attach: dma_attach failed" "\n", unit); return false; } /* get pointer to dma engine tx flow control variable */ for (i = 0; i < NFIFO; i++) if (wlc_hw->di[i]) wlc_hw->txavail[i] = (uint *) dma_getvar(wlc_hw->di[i], "&txavail"); } /* initial ucode host flags */ brcms_c_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2); return true; } static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw) { uint j; for (j = 0; j < NFIFO; j++) { if (wlc_hw->di[j]) { dma_detach(wlc_hw->di[j]); wlc_hw->di[j] = NULL; } } } /* * Initialize brcms_c_info default values ... * may get overrides later in this function * BMAC_NOTES, move low out and resolve the dangling ones */ static void brcms_b_info_init(struct brcms_hardware *wlc_hw) { struct brcms_c_info *wlc = wlc_hw->wlc; /* set default sw macintmask value */ wlc->defmacintmask = DEF_MACINTMASK; /* various 802.11g modes */ wlc_hw->shortslot = false; wlc_hw->SFBL = RETRY_SHORT_FB; wlc_hw->LFBL = RETRY_LONG_FB; /* default mac retry limits */ wlc_hw->SRL = RETRY_SHORT_DEF; wlc_hw->LRL = RETRY_LONG_DEF; wlc_hw->chanspec = ch20mhz_chspec(1); } static void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw) { /* delay before first read of ucode state */ udelay(40); /* wait until ucode is no longer asleep */ SPINWAIT((brcms_b_read_shm(wlc_hw, M_UCODE_DBGST) == DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly); } /* control chip clock to save power, enable dynamic clock or force fast clock */ static void brcms_b_clkctl_clk(struct brcms_hardware *wlc_hw, enum bcma_clkmode mode) { if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) { /* new chips with PMU, CCS_FORCEHT will distribute the HT clock * on backplane, but mac core will still run on ALP(not HT) when * it enters powersave mode, which means the FCA bit may not be * set. Should wakeup mac if driver wants it to run on HT. */ if (wlc_hw->clk) { if (mode == BCMA_CLKMODE_FAST) { bcma_set32(wlc_hw->d11core, D11REGOFFS(clk_ctl_st), CCS_FORCEHT); udelay(64); SPINWAIT( ((bcma_read32(wlc_hw->d11core, D11REGOFFS(clk_ctl_st)) & CCS_HTAVAIL) == 0), PMU_MAX_TRANSITION_DLY); WARN_ON(!(bcma_read32(wlc_hw->d11core, D11REGOFFS(clk_ctl_st)) & CCS_HTAVAIL)); } else { if ((ai_get_pmurev(wlc_hw->sih) == 0) && (bcma_read32(wlc_hw->d11core, D11REGOFFS(clk_ctl_st)) & (CCS_FORCEHT | CCS_HTAREQ))) SPINWAIT( ((bcma_read32(wlc_hw->d11core, offsetof(struct d11regs, clk_ctl_st)) & CCS_HTAVAIL) == 0), PMU_MAX_TRANSITION_DLY); bcma_mask32(wlc_hw->d11core, D11REGOFFS(clk_ctl_st), ~CCS_FORCEHT); } } wlc_hw->forcefastclk = (mode == BCMA_CLKMODE_FAST); } else { /* old chips w/o PMU, force HT through cc, * then use FCA to verify mac is running fast clock */ wlc_hw->forcefastclk = ai_clkctl_cc(wlc_hw->sih, mode); /* check fast clock is available (if core is not in reset) */ if (wlc_hw->forcefastclk && wlc_hw->clk) WARN_ON(!(bcma_aread32(wlc_hw->d11core, BCMA_IOST) & SISF_FCLKA)); /* * keep the ucode wake bit on if forcefastclk is on since we * do not want ucode to put us back to slow clock when it dozes * for PM mode. Code below matches the wake override bit with * current forcefastclk state. Only setting bit in wake_override * instead of waking ucode immediately since old code had this * behavior. Older code set wlc->forcefastclk but only had the * wake happen if the wakup_ucode work (protected by an up * check) was executed just below. */ if (wlc_hw->forcefastclk) mboolset(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_FORCEFAST); else mboolclr(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_FORCEFAST); } } /* set or clear ucode host flag bits * it has an optimization for no-change write * it only writes through shared memory when the core has clock; * pre-CLK changes should use wlc_write_mhf to get around the optimization * * * bands values are: BRCM_BAND_AUTO <--- Current band only * BRCM_BAND_5G <--- 5G band only * BRCM_BAND_2G <--- 2G band only * BRCM_BAND_ALL <--- All bands */ void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, int bands) { u16 save; u16 addr[MHFMAX] = { M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, M_HOST_FLAGS5 }; struct brcms_hw_band *band; if ((val & ~mask) || idx >= MHFMAX) return; /* error condition */ switch (bands) { /* Current band only or all bands, * then set the band to current band */ case BRCM_BAND_AUTO: case BRCM_BAND_ALL: band = wlc_hw->band; break; case BRCM_BAND_5G: band = wlc_hw->bandstate[BAND_5G_INDEX]; break; case BRCM_BAND_2G: band = wlc_hw->bandstate[BAND_2G_INDEX]; break; default: band = NULL; /* error condition */ } if (band) { save = band->mhfs[idx]; band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val; /* optimization: only write through if changed, and * changed band is the current band */ if (wlc_hw->clk && (band->mhfs[idx] != save) && (band == wlc_hw->band)) brcms_b_write_shm(wlc_hw, addr[idx], (u16) band->mhfs[idx]); } if (bands == BRCM_BAND_ALL) { wlc_hw->bandstate[0]->mhfs[idx] = (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val; wlc_hw->bandstate[1]->mhfs[idx] = (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val; } } /* set the maccontrol register to desired reset state and * initialize the sw cache of the register */ static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw) { /* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */ wlc_hw->maccontrol = 0; wlc_hw->suspended_fifos = 0; wlc_hw->wake_override = 0; wlc_hw->mute_override = 0; brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE); } /* * write the software state of maccontrol and * overrides to the maccontrol register */ static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw) { u32 maccontrol = wlc_hw->maccontrol; /* OR in the wake bit if overridden */ if (wlc_hw->wake_override) maccontrol |= MCTL_WAKE; /* set AP and INFRA bits for mute if needed */ if (wlc_hw->mute_override) { maccontrol &= ~(MCTL_AP); maccontrol |= MCTL_INFRA; } bcma_write32(wlc_hw->d11core, D11REGOFFS(maccontrol), maccontrol); } /* set or clear maccontrol bits */ void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val) { u32 maccontrol; u32 new_maccontrol; if (val & ~mask) return; /* error condition */ maccontrol = wlc_hw->maccontrol; new_maccontrol = (maccontrol & ~mask) | val; /* if the new maccontrol value is the same as the old, nothing to do */ if (new_maccontrol == maccontrol) return; /* something changed, cache the new value */ wlc_hw->maccontrol = new_maccontrol; /* write the new values with overrides applied */ brcms_c_mctrl_write(wlc_hw); } void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, u32 override_bit) { if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) { mboolset(wlc_hw->wake_override, override_bit); return; } mboolset(wlc_hw->wake_override, override_bit); brcms_c_mctrl_write(wlc_hw); brcms_b_wait_for_wake(wlc_hw); } void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, u32 override_bit) { mboolclr(wlc_hw->wake_override, override_bit); if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) return; brcms_c_mctrl_write(wlc_hw); } /* When driver needs ucode to stop beaconing, it has to make sure that * MCTL_AP is clear and MCTL_INFRA is set * Mode MCTL_AP MCTL_INFRA * AP 1 1 * STA 0 1 <--- This will ensure no beacons * IBSS 0 0 */ static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw) { wlc_hw->mute_override = 1; /* if maccontrol already has AP == 0 and INFRA == 1 without this * override, then there is no change to write */ if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) return; brcms_c_mctrl_write(wlc_hw); } /* Clear the override on AP and INFRA bits */ static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw) { if (wlc_hw->mute_override == 0) return; wlc_hw->mute_override = 0; /* if maccontrol already has AP == 0 and INFRA == 1 without this * override, then there is no change to write */ if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) return; brcms_c_mctrl_write(wlc_hw); } /* * Write a MAC address to the given match reg offset in the RXE match engine. */ static void brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset, const u8 *addr) { struct bcma_device *core = wlc_hw->d11core; u16 mac_l; u16 mac_m; u16 mac_h; BCMMSG(wlc_hw->wlc->wiphy, "wl%d: brcms_b_set_addrmatch\n", wlc_hw->unit); mac_l = addr[0] | (addr[1] << 8); mac_m = addr[2] | (addr[3] << 8); mac_h = addr[4] | (addr[5] << 8); /* enter the MAC addr into the RXE match registers */ bcma_write16(core, D11REGOFFS(rcm_ctl), RCM_INC_DATA | match_reg_offset); bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_l); bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_m); bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_h); } void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len, void *buf) { struct bcma_device *core = wlc_hw->d11core; u32 word; __le32 word_le; __be32 word_be; bool be_bit; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); bcma_write32(core, D11REGOFFS(tplatewrptr), offset); /* if MCTL_BIGEND bit set in mac control register, * the chip swaps data in fifo, as well as data in * template ram */ be_bit = (bcma_read32(core, D11REGOFFS(maccontrol)) & MCTL_BIGEND) != 0; while (len > 0) { memcpy(&word, buf, sizeof(u32)); if (be_bit) { word_be = cpu_to_be32(word); word = *(u32 *)&word_be; } else { word_le = cpu_to_le32(word); word = *(u32 *)&word_le; } bcma_write32(core, D11REGOFFS(tplatewrdata), word); buf = (u8 *) buf + sizeof(u32); len -= sizeof(u32); } } static void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin) { wlc_hw->band->CWmin = newmin; bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), OBJADDR_SCR_SEL | S_DOT11_CWMIN); (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmin); } static void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax) { wlc_hw->band->CWmax = newmax; bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), OBJADDR_SCR_SEL | S_DOT11_CWMAX); (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmax); } void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw) { bool fastclk; /* request FAST clock if not on */ fastclk = wlc_hw->forcefastclk; if (!fastclk) brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); wlc_phy_bw_state_set(wlc_hw->band->pi, bw); brcms_b_phy_reset(wlc_hw); wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi)); /* restore the clk */ if (!fastclk) brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); } static void brcms_b_upd_synthpu(struct brcms_hardware *wlc_hw) { u16 v; struct brcms_c_info *wlc = wlc_hw->wlc; /* update SYNTHPU_DLY */ if (BRCMS_ISLCNPHY(wlc->band)) v = SYNTHPU_DLY_LPPHY_US; else if (BRCMS_ISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3))) v = SYNTHPU_DLY_NPHY_US; else v = SYNTHPU_DLY_BPHY_US; brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v); } static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw) { u16 phyctl; u16 phytxant = wlc_hw->bmac_phytxant; u16 mask = PHY_TXC_ANT_MASK; /* set the Probe Response frame phy control word */ phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS); phyctl = (phyctl & ~mask) | phytxant; brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl); /* set the Response (ACK/CTS) frame phy control word */ phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD); phyctl = (phyctl & ~mask) | phytxant; brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl); } static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw, u8 rate) { uint i; u8 plcp_rate = 0; struct plcp_signal_rate_lookup { u8 rate; u8 signal_rate; }; /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */ const struct plcp_signal_rate_lookup rate_lookup[] = { {BRCM_RATE_6M, 0xB}, {BRCM_RATE_9M, 0xF}, {BRCM_RATE_12M, 0xA}, {BRCM_RATE_18M, 0xE}, {BRCM_RATE_24M, 0x9}, {BRCM_RATE_36M, 0xD}, {BRCM_RATE_48M, 0x8}, {BRCM_RATE_54M, 0xC} }; for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) { if (rate == rate_lookup[i].rate) { plcp_rate = rate_lookup[i].signal_rate; break; } } /* Find the SHM pointer to the rate table entry by looking in the * Direct-map Table */ return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2)); } static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw) { u8 rate; u8 rates[8] = { BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M, BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M }; u16 entry_ptr; u16 pctl1; uint i; if (!BRCMS_PHY_11N_CAP(wlc_hw->band)) return; /* walk the phy rate table and update the entries */ for (i = 0; i < ARRAY_SIZE(rates); i++) { rate = rates[i]; entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate); /* read the SHM Rate Table entry OFDM PCTL1 values */ pctl1 = brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS); /* modify the value */ pctl1 &= ~PHY_TXC1_MODE_MASK; pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT); /* Update the SHM Rate Table entry OFDM PCTL1 values */ brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS, pctl1); } } /* band-specific init */ static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec) { struct brcms_hardware *wlc_hw = wlc->hw; BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, wlc_hw->band->bandunit); brcms_c_ucode_bsinit(wlc_hw); wlc_phy_init(wlc_hw->band->pi, chanspec); brcms_c_ucode_txant_set(wlc_hw); /* * cwmin is band-specific, update hardware * with value for current band */ brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin); brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax); brcms_b_update_slot_timing(wlc_hw, wlc_hw->band->bandtype == BRCM_BAND_5G ? true : wlc_hw->shortslot); /* write phytype and phyvers */ brcms_b_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype); brcms_b_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev); /* * initialize the txphyctl1 rate table since * shmem is shared between bands */ brcms_upd_ofdm_pctl1_table(wlc_hw); brcms_b_upd_synthpu(wlc_hw); } /* Perform a soft reset of the PHY PLL */ void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_addr), ~0, 0); udelay(1); ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), 0x4, 0); udelay(1); ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), 0x4, 4); udelay(1); ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), 0x4, 0); udelay(1); } /* light way to turn on phy clock without reset for NPHY only * refer to brcms_b_core_phy_clk for full version */ void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk) { /* support(necessary for NPHY and HYPHY) only */ if (!BRCMS_ISNPHY(wlc_hw->band)) return; if (ON == clk) brcms_b_core_ioctl(wlc_hw, SICF_FGC, SICF_FGC); else brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); } void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk) { if (ON == clk) brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, SICF_MPCLKE); else brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, 0); } void brcms_b_phy_reset(struct brcms_hardware *wlc_hw) { struct brcms_phy_pub *pih = wlc_hw->band->pi; u32 phy_bw_clkbits; bool phy_in_reset = false; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); if (pih == NULL) return; phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi); /* Specific reset sequence required for NPHY rev 3 and 4 */ if (BRCMS_ISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) && NREV_LE(wlc_hw->band->phyrev, 4)) { /* Set the PHY bandwidth */ brcms_b_core_ioctl(wlc_hw, SICF_BWMASK, phy_bw_clkbits); udelay(1); /* Perform a soft reset of the PHY PLL */ brcms_b_core_phypll_reset(wlc_hw); /* reset the PHY */ brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_PCLKE), (SICF_PRST | SICF_PCLKE)); phy_in_reset = true; } else { brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_PCLKE | SICF_BWMASK), (SICF_PRST | SICF_PCLKE | phy_bw_clkbits)); } udelay(2); brcms_b_core_phy_clk(wlc_hw, ON); if (pih) wlc_phy_anacore(pih, ON); } /* switch to and initialize new band */ static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit, u16 chanspec) { struct brcms_c_info *wlc = wlc_hw->wlc; u32 macintmask; /* Enable the d11 core before accessing it */ if (!bcma_core_is_enabled(wlc_hw->d11core)) { bcma_core_enable(wlc_hw->d11core, 0); brcms_c_mctrl_reset(wlc_hw); } macintmask = brcms_c_setband_inact(wlc, bandunit); if (!wlc_hw->up) return; brcms_b_core_phy_clk(wlc_hw, ON); /* band-specific initializations */ brcms_b_bsinit(wlc, chanspec); /* * If there are any pending software interrupt bits, * then replace these with a harmless nonzero value * so brcms_c_dpc() will re-enable interrupts when done. */ if (wlc->macintstatus) wlc->macintstatus = MI_DMAINT; /* restore macintmask */ brcms_intrsrestore(wlc->wl, macintmask); /* ucode should still be suspended.. */ WARN_ON((bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC) != 0); } static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw) { /* reject unsupported corerev */ if (!CONF_HAS(D11CONF, wlc_hw->corerev)) { wiphy_err(wlc_hw->wlc->wiphy, "unsupported core rev %d\n", wlc_hw->corerev); return false; } return true; } /* Validate some board info parameters */ static bool brcms_c_validboardtype(struct brcms_hardware *wlc_hw) { uint boardrev = wlc_hw->boardrev; /* 4 bits each for board type, major, minor, and tiny version */ uint brt = (boardrev & 0xf000) >> 12; uint b0 = (boardrev & 0xf00) >> 8; uint b1 = (boardrev & 0xf0) >> 4; uint b2 = boardrev & 0xf; /* voards from other vendors are always considered valid */ if (ai_get_boardvendor(wlc_hw->sih) != PCI_VENDOR_ID_BROADCOM) return true; /* do some boardrev sanity checks when boardvendor is Broadcom */ if (boardrev == 0) return false; if (boardrev <= 0xff) return true; if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9) || (b2 > 9)) return false; return true; } static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ALEN]) { struct ssb_sprom *sprom = &wlc_hw->d11core->bus->sprom; /* If macaddr exists, use it (Sromrev4, CIS, ...). */ if (!is_zero_ether_addr(sprom->il0mac)) { memcpy(etheraddr, sprom->il0mac, 6); return; } if (wlc_hw->_nbands > 1) memcpy(etheraddr, sprom->et1mac, 6); else memcpy(etheraddr, sprom->il0mac, 6); } /* power both the pll and external oscillator on/off */ static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d: want %d\n", wlc_hw->unit, want); /* * dont power down if plldown is false or * we must poll hw radio disable */ if (!want && wlc_hw->pllreq) return; wlc_hw->sbclk = want; if (!wlc_hw->sbclk) { wlc_hw->clk = false; if (wlc_hw->band && wlc_hw->band->pi) wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); } } /* * Return true if radio is disabled, otherwise false. * hw radio disable signal is an external pin, users activate it asynchronously * this function could be called when driver is down and w/o clock * it operates on different registers depending on corerev and boardflag. */ static bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw) { bool v, clk, xtal; u32 flags = 0; xtal = wlc_hw->sbclk; if (!xtal) brcms_b_xtal(wlc_hw, ON); /* may need to take core out of reset first */ clk = wlc_hw->clk; if (!clk) { /* * mac no longer enables phyclk automatically when driver * accesses phyreg throughput mac. This can be skipped since * only mac reg is accessed below */ if (D11REV_GE(wlc_hw->corerev, 18)) flags |= SICF_PCLKE; /* * TODO: test suspend/resume * * AI chip doesn't restore bar0win2 on * hibernation/resume, need sw fixup */ bcma_core_enable(wlc_hw->d11core, flags); brcms_c_mctrl_reset(wlc_hw); } v = ((bcma_read32(wlc_hw->d11core, D11REGOFFS(phydebug)) & PDBG_RFD) != 0); /* put core back into reset */ if (!clk) bcma_core_disable(wlc_hw->d11core, 0); if (!xtal) brcms_b_xtal(wlc_hw, OFF); return v; } static bool wlc_dma_rxreset(struct brcms_hardware *wlc_hw, uint fifo) { struct dma_pub *di = wlc_hw->di[fifo]; return dma_rxreset(di); } /* d11 core reset * ensure fask clock during reset * reset dma * reset d11(out of reset) * reset phy(out of reset) * clear software macintstatus for fresh new start * one testing hack wlc_hw->noreset will bypass the d11/phy reset */ void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags) { uint i; bool fastclk; if (flags == BRCMS_USE_COREFLAGS) flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0); BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); /* request FAST clock if not on */ fastclk = wlc_hw->forcefastclk; if (!fastclk) brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); /* reset the dma engines except first time thru */ if (bcma_core_is_enabled(wlc_hw->d11core)) { for (i = 0; i < NFIFO; i++) if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i]))) wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: " "dma_txreset[%d]: cannot stop dma\n", wlc_hw->unit, __func__, i); if ((wlc_hw->di[RX_FIFO]) && (!wlc_dma_rxreset(wlc_hw, RX_FIFO))) wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: dma_rxreset" "[%d]: cannot stop dma\n", wlc_hw->unit, __func__, RX_FIFO); } /* if noreset, just stop the psm and return */ if (wlc_hw->noreset) { wlc_hw->wlc->macintstatus = 0; /* skip wl_dpc after down */ brcms_b_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0); return; } /* * mac no longer enables phyclk automatically when driver accesses * phyreg throughput mac, AND phy_reset is skipped at early stage when * band->pi is invalid. need to enable PHY CLK */ if (D11REV_GE(wlc_hw->corerev, 18)) flags |= SICF_PCLKE; /* * reset the core * In chips with PMU, the fastclk request goes through d11 core * reg 0x1e0, which is cleared by the core_reset. have to re-request it. * * This adds some delay and we can optimize it by also requesting * fastclk through chipcommon during this period if necessary. But * that has to work coordinate with other driver like mips/arm since * they may touch chipcommon as well. */ wlc_hw->clk = false; bcma_core_enable(wlc_hw->d11core, flags); wlc_hw->clk = true; if (wlc_hw->band && wlc_hw->band->pi) wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true); brcms_c_mctrl_reset(wlc_hw); if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); brcms_b_phy_reset(wlc_hw); /* turn on PHY_PLL */ brcms_b_core_phypll_ctl(wlc_hw, true); /* clear sw intstatus */ wlc_hw->wlc->macintstatus = 0; /* restore the clk setting */ if (!fastclk) brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); } /* txfifo sizes needs to be modified(increased) since the newer cores * have more memory. */ static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw) { struct bcma_device *core = wlc_hw->d11core; u16 fifo_nu; u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk; u16 txfifo_def, txfifo_def1; u16 txfifo_cmd; /* tx fifos start at TXFIFO_START_BLK from the Base address */ txfifo_startblk = TXFIFO_START_BLK; /* sequence of operations: reset fifo, set fifo size, reset fifo */ for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) { txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu]; txfifo_def = (txfifo_startblk & 0xff) | (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT); txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) | ((((txfifo_endblk - 1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT); txfifo_cmd = TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT); bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); bcma_write16(core, D11REGOFFS(xmtfifodef), txfifo_def); bcma_write16(core, D11REGOFFS(xmtfifodef1), txfifo_def1); bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu]; } /* * need to propagate to shm location to be in sync since ucode/hw won't * do this */ brcms_b_write_shm(wlc_hw, M_FIFOSIZE0, wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]); brcms_b_write_shm(wlc_hw, M_FIFOSIZE1, wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]); brcms_b_write_shm(wlc_hw, M_FIFOSIZE2, ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw-> xmtfifo_sz[TX_AC_BK_FIFO])); brcms_b_write_shm(wlc_hw, M_FIFOSIZE3, ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw-> xmtfifo_sz[TX_BCMC_FIFO])); } /* This function is used for changing the tsf frac register * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz * HTPHY Formula is 2^26/freq(MHz) e.g. * For spuron2 - 126MHz -> 2^26/126 = 532610.0 * - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082 * For spuron: 123MHz -> 2^26/123 = 545600.5 * - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341 * For spur off: 120MHz -> 2^26/120 = 559240.5 * - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889 */ void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode) { struct bcma_device *core = wlc_hw->d11core; if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43224) || (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) { if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */ bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x2082); bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */ bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x5341); bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); } else { /* 120Mhz */ bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x8889); bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); } } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */ bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x7CE0); bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); } else { /* 80Mhz */ bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0xCCCD); bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); } } } /* Initialize GPIOs that are controlled by D11 core */ static void brcms_c_gpio_init(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; u32 gc, gm; /* use GPIO select 0 to get all gpio signals from the gpio out reg */ brcms_b_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0); /* * Common GPIO setup: * G0 = LED 0 = WLAN Activity * G1 = LED 1 = WLAN 2.4 GHz Radio State * G2 = LED 2 = WLAN 5 GHz Radio State * G4 = radio disable input (HI enabled, LO disabled) */ gc = gm = 0; /* Allocate GPIOs for mimo antenna diversity feature */ if (wlc_hw->antsel_type == ANTSEL_2x3) { /* Enable antenna diversity, use 2x3 mode */ brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, MHF3_ANTSEL_EN, BRCM_BAND_ALL); brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, MHF3_ANTSEL_MODE, BRCM_BAND_ALL); /* init superswitch control */ wlc_phy_antsel_init(wlc_hw->band->pi, false); } else if (wlc_hw->antsel_type == ANTSEL_2x4) { gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13); /* * The board itself is powered by these GPIOs * (when not sending pattern) so set them high */ bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_oe), (BOARD_GPIO_12 | BOARD_GPIO_13)); bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_out), (BOARD_GPIO_12 | BOARD_GPIO_13)); /* Enable antenna diversity, use 2x4 mode */ brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, MHF3_ANTSEL_EN, BRCM_BAND_ALL); brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0, BRCM_BAND_ALL); /* Configure the desired clock to be 4Mhz */ brcms_b_write_shm(wlc_hw, M_ANTSEL_CLKDIV, ANTSEL_CLKDIV_4MHZ); } /* * gpio 9 controls the PA. ucode is responsible * for wiggling out and oe */ if (wlc_hw->boardflags & BFL_PACTRL) gm |= gc |= BOARD_GPIO_PACTRL; /* apply to gpiocontrol register */ bcma_chipco_gpio_control(&wlc_hw->d11core->bus->drv_cc, gm, gc); } static void brcms_ucode_write(struct brcms_hardware *wlc_hw, const __le32 ucode[], const size_t nbytes) { struct bcma_device *core = wlc_hw->d11core; uint i; uint count; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); count = (nbytes / sizeof(u32)); bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_AUTO_INC | OBJADDR_UCM_SEL); (void)bcma_read32(core, D11REGOFFS(objaddr)); for (i = 0; i < count; i++) bcma_write32(core, D11REGOFFS(objdata), le32_to_cpu(ucode[i])); } static void brcms_ucode_download(struct brcms_hardware *wlc_hw) { struct brcms_c_info *wlc; struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; wlc = wlc_hw->wlc; if (wlc_hw->ucode_loaded) return; if (D11REV_IS(wlc_hw->corerev, 23)) { if (BRCMS_ISNPHY(wlc_hw->band)) { brcms_ucode_write(wlc_hw, ucode->bcm43xx_16_mimo, ucode->bcm43xx_16_mimosz); wlc_hw->ucode_loaded = true; } else wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in " "corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else if (D11REV_IS(wlc_hw->corerev, 24)) { if (BRCMS_ISLCNPHY(wlc_hw->band)) { brcms_ucode_write(wlc_hw, ucode->bcm43xx_24_lcn, ucode->bcm43xx_24_lcnsz); wlc_hw->ucode_loaded = true; } else { wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in " "corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } } } void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant) { /* update sw state */ wlc_hw->bmac_phytxant = phytxant; /* push to ucode if up */ if (!wlc_hw->up) return; brcms_c_ucode_txant_set(wlc_hw); } u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw) { return (u16) wlc_hw->wlc->stf->txant; } void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type) { wlc_hw->antsel_type = antsel_type; /* Update the antsel type for phy module to use */ wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type); } static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) { bool fatal = false; uint unit; uint intstatus, idx; struct bcma_device *core = wlc_hw->d11core; struct wiphy *wiphy = wlc_hw->wlc->wiphy; unit = wlc_hw->unit; for (idx = 0; idx < NFIFO; idx++) { /* read intstatus register and ignore any non-error bits */ intstatus = bcma_read32(core, D11REGOFFS(intctrlregs[idx].intstatus)) & I_ERRORS; if (!intstatus) continue; BCMMSG(wlc_hw->wlc->wiphy, "wl%d: intstatus%d 0x%x\n", unit, idx, intstatus); if (intstatus & I_RO) { wiphy_err(wiphy, "wl%d: fifo %d: receive fifo " "overflow\n", unit, idx); fatal = true; } if (intstatus & I_PC) { wiphy_err(wiphy, "wl%d: fifo %d: descriptor error\n", unit, idx); fatal = true; } if (intstatus & I_PD) { wiphy_err(wiphy, "wl%d: fifo %d: data error\n", unit, idx); fatal = true; } if (intstatus & I_DE) { wiphy_err(wiphy, "wl%d: fifo %d: descriptor protocol " "error\n", unit, idx); fatal = true; } if (intstatus & I_RU) wiphy_err(wiphy, "wl%d: fifo %d: receive descriptor " "underflow\n", idx, unit); if (intstatus & I_XU) { wiphy_err(wiphy, "wl%d: fifo %d: transmit fifo " "underflow\n", idx, unit); fatal = true; } if (fatal) { brcms_fatal_error(wlc_hw->wlc->wl); /* big hammer */ break; } else bcma_write32(core, D11REGOFFS(intctrlregs[idx].intstatus), intstatus); } } void brcms_c_intrson(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; wlc->macintmask = wlc->defmacintmask; bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); } u32 brcms_c_intrsoff(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; u32 macintmask; if (!wlc_hw->clk) return 0; macintmask = wlc->macintmask; /* isr can still happen */ bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), 0); (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(macintmask)); udelay(1); /* ensure int line is no longer driven */ wlc->macintmask = 0; /* return previous macintmask; resolve race between us and our isr */ return wlc->macintstatus ? 0 : macintmask; } void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask) { struct brcms_hardware *wlc_hw = wlc->hw; if (!wlc_hw->clk) return; wlc->macintmask = macintmask; bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); } /* assumes that the d11 MAC is enabled */ static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw, uint tx_fifo) { u8 fifo = 1 << tx_fifo; /* Two clients of this code, 11h Quiet period and scanning. */ /* only suspend if not already suspended */ if ((wlc_hw->suspended_fifos & fifo) == fifo) return; /* force the core awake only if not already */ if (wlc_hw->suspended_fifos == 0) brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_TXFIFO); wlc_hw->suspended_fifos |= fifo; if (wlc_hw->di[tx_fifo]) { /* * Suspending AMPDU transmissions in the middle can cause * underflow which may result in mismatch between ucode and * driver so suspend the mac before suspending the FIFO */ if (BRCMS_PHY_11N_CAP(wlc_hw->band)) brcms_c_suspend_mac_and_wait(wlc_hw->wlc); dma_txsuspend(wlc_hw->di[tx_fifo]); if (BRCMS_PHY_11N_CAP(wlc_hw->band)) brcms_c_enable_mac(wlc_hw->wlc); } } static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw, uint tx_fifo) { /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case * but need to be done here for PIO otherwise the watchdog will catch * the inconsistency and fire */ /* Two clients of this code, 11h Quiet period and scanning. */ if (wlc_hw->di[tx_fifo]) dma_txresume(wlc_hw->di[tx_fifo]); /* allow core to sleep again */ if (wlc_hw->suspended_fifos == 0) return; else { wlc_hw->suspended_fifos &= ~(1 << tx_fifo); if (wlc_hw->suspended_fifos == 0) brcms_c_ucode_wake_override_clear(wlc_hw, BRCMS_WAKE_OVERRIDE_TXFIFO); } } /* precondition: requires the mac core to be enabled */ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) { static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; if (mute_tx) { /* suspend tx fifos */ brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO); brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO); brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO); brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO); /* zero the address match register so we do not send ACKs */ brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr); } else { /* resume tx fifos */ brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO); brcms_b_tx_fifo_resume(wlc_hw, TX_CTL_FIFO); brcms_b_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO); brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO); /* Restore address */ brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, wlc_hw->etheraddr); } wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0); if (mute_tx) brcms_c_ucode_mute_override_set(wlc_hw); else brcms_c_ucode_mute_override_clear(wlc_hw); } void brcms_c_mute(struct brcms_c_info *wlc, bool mute_tx) { brcms_b_mute(wlc->hw, mute_tx); } /* * Read and clear macintmask and macintstatus and intstatus registers. * This routine should be called with interrupts off * Return: * -1 if brcms_deviceremoved(wlc) evaluates to true; * 0 if the interrupt is not for us, or we are in some special cases; * device interrupt status bits otherwise. */ static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr) { struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; u32 macintstatus; /* macintstatus includes a DMA interrupt summary bit */ macintstatus = bcma_read32(core, D11REGOFFS(macintstatus)); BCMMSG(wlc->wiphy, "wl%d: macintstatus: 0x%x\n", wlc_hw->unit, macintstatus); /* detect cardbus removed, in power down(suspend) and in reset */ if (brcms_deviceremoved(wlc)) return -1; /* brcms_deviceremoved() succeeds even when the core is still resetting, * handle that case here. */ if (macintstatus == 0xffffffff) return 0; /* defer unsolicited interrupts */ macintstatus &= (in_isr ? wlc->macintmask : wlc->defmacintmask); /* if not for us */ if (macintstatus == 0) return 0; /* interrupts are already turned off for CFE build * Caution: For CFE Turning off the interrupts again has some undesired * consequences */ /* turn off the interrupts */ bcma_write32(core, D11REGOFFS(macintmask), 0); (void)bcma_read32(core, D11REGOFFS(macintmask)); wlc->macintmask = 0; /* clear device interrupts */ bcma_write32(core, D11REGOFFS(macintstatus), macintstatus); /* MI_DMAINT is indication of non-zero intstatus */ if (macintstatus & MI_DMAINT) /* * only fifo interrupt enabled is I_RI in * RX_FIFO. If MI_DMAINT is set, assume it * is set and clear the interrupt. */ bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intstatus), DEF_RXINTMASK); return macintstatus; } /* Update wlc->macintstatus and wlc->intstatus[]. */ /* Return true if they are updated successfully. false otherwise */ bool brcms_c_intrsupd(struct brcms_c_info *wlc) { u32 macintstatus; /* read and clear macintstatus and intstatus registers */ macintstatus = wlc_intstatus(wlc, false); /* device is removed */ if (macintstatus == 0xffffffff) return false; /* update interrupt status in software */ wlc->macintstatus |= macintstatus; return true; } /* * First-level interrupt processing. * Return true if this was our interrupt, false otherwise. * *wantdpc will be set to true if further brcms_c_dpc() processing is required, * false otherwise. */ bool brcms_c_isr(struct brcms_c_info *wlc, bool *wantdpc) { struct brcms_hardware *wlc_hw = wlc->hw; u32 macintstatus; *wantdpc = false; if (!wlc_hw->up || !wlc->macintmask) return false; /* read and clear macintstatus and intstatus registers */ macintstatus = wlc_intstatus(wlc, true); if (macintstatus == 0xffffffff) wiphy_err(wlc->wiphy, "DEVICEREMOVED detected in the ISR code" " path\n"); /* it is not for us */ if (macintstatus == 0) return false; *wantdpc = true; /* save interrupt status bits */ wlc->macintstatus = macintstatus; return true; } void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; u32 mc, mi; struct wiphy *wiphy = wlc->wiphy; BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, wlc_hw->band->bandunit); /* * Track overlapping suspend requests */ wlc_hw->mac_suspend_depth++; if (wlc_hw->mac_suspend_depth > 1) return; /* force the core awake */ brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND); mc = bcma_read32(core, D11REGOFFS(maccontrol)); if (mc == 0xffffffff) { wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return; } WARN_ON(mc & MCTL_PSM_JMP_0); WARN_ON(!(mc & MCTL_PSM_RUN)); WARN_ON(!(mc & MCTL_EN_MAC)); mi = bcma_read32(core, D11REGOFFS(macintstatus)); if (mi == 0xffffffff) { wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return; } WARN_ON(mi & MI_MACSSPNDD); brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, 0); SPINWAIT(!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD), BRCMS_MAX_MAC_SUSPEND); if (!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD)) { wiphy_err(wiphy, "wl%d: wlc_suspend_mac_and_wait: waited %d uS" " and MI_MACSSPNDD is still not on.\n", wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND); wiphy_err(wiphy, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, " "psm_brc 0x%04x\n", wlc_hw->unit, bcma_read32(core, D11REGOFFS(psmdebug)), bcma_read32(core, D11REGOFFS(phydebug)), bcma_read16(core, D11REGOFFS(psm_brc))); } mc = bcma_read32(core, D11REGOFFS(maccontrol)); if (mc == 0xffffffff) { wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return; } WARN_ON(mc & MCTL_PSM_JMP_0); WARN_ON(!(mc & MCTL_PSM_RUN)); WARN_ON(mc & MCTL_EN_MAC); } void brcms_c_enable_mac(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; u32 mc, mi; BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, wlc->band->bandunit); /* * Track overlapping suspend requests */ wlc_hw->mac_suspend_depth--; if (wlc_hw->mac_suspend_depth > 0) return; mc = bcma_read32(core, D11REGOFFS(maccontrol)); WARN_ON(mc & MCTL_PSM_JMP_0); WARN_ON(mc & MCTL_EN_MAC); WARN_ON(!(mc & MCTL_PSM_RUN)); brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC); bcma_write32(core, D11REGOFFS(macintstatus), MI_MACSSPNDD); mc = bcma_read32(core, D11REGOFFS(maccontrol)); WARN_ON(mc & MCTL_PSM_JMP_0); WARN_ON(!(mc & MCTL_EN_MAC)); WARN_ON(!(mc & MCTL_PSM_RUN)); mi = bcma_read32(core, D11REGOFFS(macintstatus)); WARN_ON(mi & MI_MACSSPNDD); brcms_c_ucode_wake_override_clear(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND); } void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode) { wlc_hw->hw_stf_ss_opmode = stf_mode; if (wlc_hw->clk) brcms_upd_ofdm_pctl1_table(wlc_hw); } static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw) { struct bcma_device *core = wlc_hw->d11core; u32 w, val; struct wiphy *wiphy = wlc_hw->wlc->wiphy; BCMMSG(wiphy, "wl%d\n", wlc_hw->unit); /* Validate dchip register access */ bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); (void)bcma_read32(core, D11REGOFFS(objaddr)); w = bcma_read32(core, D11REGOFFS(objdata)); /* Can we write and read back a 32bit register? */ bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); (void)bcma_read32(core, D11REGOFFS(objaddr)); bcma_write32(core, D11REGOFFS(objdata), (u32) 0xaa5555aa); bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); (void)bcma_read32(core, D11REGOFFS(objaddr)); val = bcma_read32(core, D11REGOFFS(objdata)); if (val != (u32) 0xaa5555aa) { wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " "expected 0xaa5555aa\n", wlc_hw->unit, val); return false; } bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); (void)bcma_read32(core, D11REGOFFS(objaddr)); bcma_write32(core, D11REGOFFS(objdata), (u32) 0x55aaaa55); bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); (void)bcma_read32(core, D11REGOFFS(objaddr)); val = bcma_read32(core, D11REGOFFS(objdata)); if (val != (u32) 0x55aaaa55) { wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " "expected 0x55aaaa55\n", wlc_hw->unit, val); return false; } bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); (void)bcma_read32(core, D11REGOFFS(objaddr)); bcma_write32(core, D11REGOFFS(objdata), w); /* clear CFPStart */ bcma_write32(core, D11REGOFFS(tsf_cfpstart), 0); w = bcma_read32(core, D11REGOFFS(maccontrol)); if ((w != (MCTL_IHR_EN | MCTL_WAKE)) && (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) { wiphy_err(wiphy, "wl%d: validate_chip_access: maccontrol = " "0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w, (MCTL_IHR_EN | MCTL_WAKE), (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE)); return false; } return true; } #define PHYPLL_WAIT_US 100000 void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) { struct bcma_device *core = wlc_hw->d11core; u32 tmp; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); tmp = 0; if (on) { if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { bcma_set32(core, D11REGOFFS(clk_ctl_st), CCS_ERSRC_REQ_HT | CCS_ERSRC_REQ_D11PLL | CCS_ERSRC_REQ_PHYPLL); SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT, PHYPLL_WAIT_US); tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); if ((tmp & CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT) wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on PHY" " PLL failed\n", __func__); } else { bcma_set32(core, D11REGOFFS(clk_ctl_st), tmp | CCS_ERSRC_REQ_D11PLL | CCS_ERSRC_REQ_PHYPLL); SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) != (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US); tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); if ((tmp & (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) != (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on " "PHY PLL failed\n", __func__); } } else { /* * Since the PLL may be shared, other cores can still * be requesting it; so we'll deassert the request but * not wait for status to comply. */ bcma_mask32(core, D11REGOFFS(clk_ctl_st), ~CCS_ERSRC_REQ_PHYPLL); (void)bcma_read32(core, D11REGOFFS(clk_ctl_st)); } } static void brcms_c_coredisable(struct brcms_hardware *wlc_hw) { bool dev_gone; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); dev_gone = brcms_deviceremoved(wlc_hw->wlc); if (dev_gone) return; if (wlc_hw->noreset) return; /* radio off */ wlc_phy_switch_radio(wlc_hw->band->pi, OFF); /* turn off analog core */ wlc_phy_anacore(wlc_hw->band->pi, OFF); /* turn off PHYPLL to save power */ brcms_b_core_phypll_ctl(wlc_hw, false); wlc_hw->clk = false; bcma_core_disable(wlc_hw->d11core, 0); wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); } static void brcms_c_flushqueues(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; uint i; /* free any posted tx packets */ for (i = 0; i < NFIFO; i++) if (wlc_hw->di[i]) { dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL); wlc->core->txpktpend[i] = 0; BCMMSG(wlc->wiphy, "pktpend fifo %d clrd\n", i); } /* free any posted rx packets */ dma_rxreclaim(wlc_hw->di[RX_FIFO]); } static u16 brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel) { struct bcma_device *core = wlc_hw->d11core; u16 objoff = D11REGOFFS(objdata); bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); (void)bcma_read32(core, D11REGOFFS(objaddr)); if (offset & 2) objoff += 2; return bcma_read16(core, objoff); } static void brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset, u16 v, u32 sel) { struct bcma_device *core = wlc_hw->d11core; u16 objoff = D11REGOFFS(objdata); bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); (void)bcma_read32(core, D11REGOFFS(objaddr)); if (offset & 2) objoff += 2; bcma_write16(core, objoff, v); } /* * Read a single u16 from shared memory. * SHM 'offset' needs to be an even address */ u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset) { return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL); } /* * Write a single u16 to shared memory. * SHM 'offset' needs to be an even address */ void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v) { brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL); } /* * Copy a buffer to shared memory of specified type . * SHM 'offset' needs to be an even address and * Buffer length 'len' must be an even number of bytes * 'sel' selects the type of memory */ void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, const void *buf, int len, u32 sel) { u16 v; const u8 *p = (const u8 *)buf; int i; if (len <= 0 || (offset & 1) || (len & 1)) return; for (i = 0; i < len; i += 2) { v = p[i] | (p[i + 1] << 8); brcms_b_write_objmem(wlc_hw, offset + i, v, sel); } } /* * Copy a piece of shared memory of specified type to a buffer . * SHM 'offset' needs to be an even address and * Buffer length 'len' must be an even number of bytes * 'sel' selects the type of memory */ void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf, int len, u32 sel) { u16 v; u8 *p = (u8 *) buf; int i; if (len <= 0 || (offset & 1) || (len & 1)) return; for (i = 0; i < len; i += 2) { v = brcms_b_read_objmem(wlc_hw, offset + i, sel); p[i] = v & 0xFF; p[i + 1] = (v >> 8) & 0xFF; } } /* Copy a buffer to shared memory. * SHM 'offset' needs to be an even address and * Buffer length 'len' must be an even number of bytes */ static void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset, const void *buf, int len) { brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL); } static void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, u16 SRL, u16 LRL) { wlc_hw->SRL = SRL; wlc_hw->LRL = LRL; /* write retry limit to SCR, shouldn't need to suspend */ if (wlc_hw->up) { bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->SRL); bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->LRL); } } static void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set, u32 req_bit) { if (set) { if (mboolisset(wlc_hw->pllreq, req_bit)) return; mboolset(wlc_hw->pllreq, req_bit); if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { if (!wlc_hw->sbclk) brcms_b_xtal(wlc_hw, ON); } } else { if (!mboolisset(wlc_hw->pllreq, req_bit)) return; mboolclr(wlc_hw->pllreq, req_bit); if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { if (wlc_hw->sbclk) brcms_b_xtal(wlc_hw, OFF); } } } static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail) { wlc_hw->antsel_avail = antsel_avail; } /* * conditions under which the PM bit should be set in outgoing frames * and STAY_AWAKE is meaningful */ static bool brcms_c_ps_allowed(struct brcms_c_info *wlc) { struct brcms_bss_cfg *cfg = wlc->bsscfg; /* disallow PS when one of the following global conditions meets */ if (!wlc->pub->associated) return false; /* disallow PS when one of these meets when not scanning */ if (wlc->filter_flags & FIF_PROMISC_IN_BSS) return false; if (cfg->associated) { /* * disallow PS when one of the following * bsscfg specific conditions meets */ if (!cfg->BSS) return false; return false; } return true; } static void brcms_c_statsupd(struct brcms_c_info *wlc) { int i; struct macstat macstats; #ifdef DEBUG u16 delta; u16 rxf0ovfl; u16 txfunfl[NFIFO]; #endif /* DEBUG */ /* if driver down, make no sense to update stats */ if (!wlc->pub->up) return; #ifdef DEBUG /* save last rx fifo 0 overflow count */ rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl; /* save last tx fifo underflow count */ for (i = 0; i < NFIFO; i++) txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i]; #endif /* DEBUG */ /* Read mac stats from contiguous shared memory */ brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, &macstats, sizeof(struct macstat), OBJADDR_SHM_SEL); #ifdef DEBUG /* check for rx fifo 0 overflow */ delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl); if (delta) wiphy_err(wlc->wiphy, "wl%d: %u rx fifo 0 overflows!\n", wlc->pub->unit, delta); /* check for tx fifo underflows */ for (i = 0; i < NFIFO; i++) { delta = (u16) (wlc->core->macstat_snapshot->txfunfl[i] - txfunfl[i]); if (delta) wiphy_err(wlc->wiphy, "wl%d: %u tx fifo %d underflows!" "\n", wlc->pub->unit, delta, i); } #endif /* DEBUG */ /* merge counters from dma module */ for (i = 0; i < NFIFO; i++) { if (wlc->hw->di[i]) dma_counterreset(wlc->hw->di[i]); } } static void brcms_b_reset(struct brcms_hardware *wlc_hw) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); /* reset the core */ if (!brcms_deviceremoved(wlc_hw->wlc)) brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); /* purge the dma rings */ brcms_c_flushqueues(wlc_hw->wlc); } void brcms_c_reset(struct brcms_c_info *wlc) { BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); /* slurp up hw mac counters before core reset */ brcms_c_statsupd(wlc); /* reset our snapshot of macstat counters */ memset((char *)wlc->core->macstat_snapshot, 0, sizeof(struct macstat)); brcms_b_reset(wlc->hw); } void brcms_c_init_scb(struct scb *scb) { int i; memset(scb, 0, sizeof(struct scb)); scb->flags = SCB_WMECAP | SCB_HTCAP; for (i = 0; i < NUMPRIO; i++) { scb->seqnum[i] = 0; scb->seqctl[i] = 0xFFFF; } scb->seqctl_nonqos = 0xFFFF; scb->magic = SCB_MAGIC; } /* d11 core init * reset PSM * download ucode/PCM * let ucode run to suspended * download ucode inits * config other core registers * init dma */ static void brcms_b_coreinit(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; u32 sflags; u32 bcnint_us; uint i = 0; bool fifosz_fixup = false; int err = 0; u16 buf[NFIFO]; struct wiphy *wiphy = wlc->wiphy; struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); /* reset PSM */ brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE)); brcms_ucode_download(wlc_hw); /* * FIFOSZ fixup. driver wants to controls the fifo allocation. */ fifosz_fixup = true; /* let the PSM run to the suspended state, set mode to BSS STA */ bcma_write32(core, D11REGOFFS(macintstatus), -1); brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_INFRA | MCTL_PSM_RUN | MCTL_WAKE)); /* wait for ucode to self-suspend after auto-init */ SPINWAIT(((bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD) == 0), 1000 * 1000); if ((bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD) == 0) wiphy_err(wiphy, "wl%d: wlc_coreinit: ucode did not self-" "suspend!\n", wlc_hw->unit); brcms_c_gpio_init(wlc); sflags = bcma_aread32(core, BCMA_IOST); if (D11REV_IS(wlc_hw->corerev, 23)) { if (BRCMS_ISNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11n0initvals16); else wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev" " %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else if (D11REV_IS(wlc_hw->corerev, 24)) { if (BRCMS_ISLCNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11lcn0initvals24); else wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev" " %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else { wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } /* For old ucode, txfifo sizes needs to be modified(increased) */ if (fifosz_fixup) brcms_b_corerev_fifofixup(wlc_hw); /* check txfifo allocations match between ucode and driver */ buf[TX_AC_BE_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE0); if (buf[TX_AC_BE_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]) { i = TX_AC_BE_FIFO; err = -1; } buf[TX_AC_VI_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE1); if (buf[TX_AC_VI_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]) { i = TX_AC_VI_FIFO; err = -1; } buf[TX_AC_BK_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE2); buf[TX_AC_VO_FIFO] = (buf[TX_AC_BK_FIFO] >> 8) & 0xff; buf[TX_AC_BK_FIFO] &= 0xff; if (buf[TX_AC_BK_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BK_FIFO]) { i = TX_AC_BK_FIFO; err = -1; } if (buf[TX_AC_VO_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO]) { i = TX_AC_VO_FIFO; err = -1; } buf[TX_BCMC_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE3); buf[TX_ATIM_FIFO] = (buf[TX_BCMC_FIFO] >> 8) & 0xff; buf[TX_BCMC_FIFO] &= 0xff; if (buf[TX_BCMC_FIFO] != wlc_hw->xmtfifo_sz[TX_BCMC_FIFO]) { i = TX_BCMC_FIFO; err = -1; } if (buf[TX_ATIM_FIFO] != wlc_hw->xmtfifo_sz[TX_ATIM_FIFO]) { i = TX_ATIM_FIFO; err = -1; } if (err != 0) wiphy_err(wiphy, "wlc_coreinit: txfifo mismatch: ucode size %d" " driver size %d index %d\n", buf[i], wlc_hw->xmtfifo_sz[i], i); /* make sure we can still talk to the mac */ WARN_ON(bcma_read32(core, D11REGOFFS(maccontrol)) == 0xffffffff); /* band-specific inits done by wlc_bsinit() */ /* Set up frame burst size and antenna swap threshold init values */ brcms_b_write_shm(wlc_hw, M_MBURST_SIZE, MAXTXFRAMEBURST); brcms_b_write_shm(wlc_hw, M_MAX_ANTCNT, ANTCNT); /* enable one rx interrupt per received frame */ bcma_write32(core, D11REGOFFS(intrcvlazy[0]), (1 << IRL_FC_SHIFT)); /* set the station mode (BSS STA) */ brcms_b_mctrl(wlc_hw, (MCTL_INFRA | MCTL_DISCARD_PMQ | MCTL_AP), (MCTL_INFRA | MCTL_DISCARD_PMQ)); /* set up Beacon interval */ bcnint_us = 0x8000 << 10; bcma_write32(core, D11REGOFFS(tsf_cfprep), (bcnint_us << CFPREP_CBI_SHIFT)); bcma_write32(core, D11REGOFFS(tsf_cfpstart), bcnint_us); bcma_write32(core, D11REGOFFS(macintstatus), MI_GP1); /* write interrupt mask */ bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intmask), DEF_RXINTMASK); /* allow the MAC to control the PHY clock (dynamic on/off) */ brcms_b_macphyclk_set(wlc_hw, ON); /* program dynamic clock control fast powerup delay register */ wlc->fastpwrup_dly = ai_clkctl_fast_pwrup_delay(wlc_hw->sih); bcma_write16(core, D11REGOFFS(scc_fastpwrup_dly), wlc->fastpwrup_dly); /* tell the ucode the corerev */ brcms_b_write_shm(wlc_hw, M_MACHW_VER, (u16) wlc_hw->corerev); /* tell the ucode MAC capabilities */ brcms_b_write_shm(wlc_hw, M_MACHW_CAP_L, (u16) (wlc_hw->machwcap & 0xffff)); brcms_b_write_shm(wlc_hw, M_MACHW_CAP_H, (u16) ((wlc_hw-> machwcap >> 16) & 0xffff)); /* write retry limits to SCR, this done after PSM init */ bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); (void)bcma_read32(core, D11REGOFFS(objaddr)); bcma_write32(core, D11REGOFFS(objdata), wlc_hw->SRL); bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); (void)bcma_read32(core, D11REGOFFS(objaddr)); bcma_write32(core, D11REGOFFS(objdata), wlc_hw->LRL); /* write rate fallback retry limits */ brcms_b_write_shm(wlc_hw, M_SFRMTXCNTFBRTHSD, wlc_hw->SFBL); brcms_b_write_shm(wlc_hw, M_LFRMTXCNTFBRTHSD, wlc_hw->LFBL); bcma_mask16(core, D11REGOFFS(ifs_ctl), 0x0FFF); bcma_write16(core, D11REGOFFS(ifs_aifsn), EDCF_AIFSN_MIN); /* init the tx dma engines */ for (i = 0; i < NFIFO; i++) { if (wlc_hw->di[i]) dma_txinit(wlc_hw->di[i]); } /* init the rx dma engine(s) and post receive buffers */ dma_rxinit(wlc_hw->di[RX_FIFO]); dma_rxfill(wlc_hw->di[RX_FIFO]); } void static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec) { u32 macintmask; bool fastclk; struct brcms_c_info *wlc = wlc_hw->wlc; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); /* request FAST clock if not on */ fastclk = wlc_hw->forcefastclk; if (!fastclk) brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); /* disable interrupts */ macintmask = brcms_intrsoff(wlc->wl); /* set up the specified band and chanspec */ brcms_c_setxband(wlc_hw, chspec_bandunit(chanspec)); wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); /* do one-time phy inits and calibration */ wlc_phy_cal_init(wlc_hw->band->pi); /* core-specific initialization */ brcms_b_coreinit(wlc); /* band-specific inits */ brcms_b_bsinit(wlc, chanspec); /* restore macintmask */ brcms_intrsrestore(wlc->wl, macintmask); /* seed wake_override with BRCMS_WAKE_OVERRIDE_MACSUSPEND since the mac * is suspended and brcms_c_enable_mac() will clear this override bit. */ mboolset(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_MACSUSPEND); /* * initialize mac_suspend_depth to 1 to match ucode * initial suspended state */ wlc_hw->mac_suspend_depth = 1; /* restore the clk */ if (!fastclk) brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); } static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc, u16 chanspec) { /* Save our copy of the chanspec */ wlc->chanspec = chanspec; /* Set the chanspec and power limits for this locale */ brcms_c_channel_set_chanspec(wlc->cmi, chanspec, BRCMS_TXPWR_MAX); if (wlc->stf->ss_algosel_auto) brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel, chanspec); brcms_c_stf_ss_update(wlc, wlc->band); } static void brcms_default_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs) { brcms_c_rateset_default(rs, NULL, wlc->band->phytype, wlc->band->bandtype, false, BRCMS_RATE_MASK_FULL, (bool) (wlc->pub->_n_enab & SUPPORT_11N), brcms_chspec_bw(wlc->default_bss->chanspec), wlc->stf->txstreams); } /* derive wlc->band->basic_rate[] table from 'rateset' */ static void brcms_c_rate_lookup_init(struct brcms_c_info *wlc, struct brcms_c_rateset *rateset) { u8 rate; u8 mandatory; u8 cck_basic = 0; u8 ofdm_basic = 0; u8 *br = wlc->band->basic_rate; uint i; /* incoming rates are in 500kbps units as in 802.11 Supported Rates */ memset(br, 0, BRCM_MAXRATE + 1); /* For each basic rate in the rates list, make an entry in the * best basic lookup. */ for (i = 0; i < rateset->count; i++) { /* only make an entry for a basic rate */ if (!(rateset->rates[i] & BRCMS_RATE_FLAG)) continue; /* mask off basic bit */ rate = (rateset->rates[i] & BRCMS_RATE_MASK); if (rate > BRCM_MAXRATE) { wiphy_err(wlc->wiphy, "brcms_c_rate_lookup_init: " "invalid rate 0x%X in rate set\n", rateset->rates[i]); continue; } br[rate] = rate; } /* The rate lookup table now has non-zero entries for each * basic rate, equal to the basic rate: br[basicN] = basicN * * To look up the best basic rate corresponding to any * particular rate, code can use the basic_rate table * like this * * basic_rate = wlc->band->basic_rate[tx_rate] * * Make sure there is a best basic rate entry for * every rate by walking up the table from low rates * to high, filling in holes in the lookup table */ for (i = 0; i < wlc->band->hw_rateset.count; i++) { rate = wlc->band->hw_rateset.rates[i]; if (br[rate] != 0) { /* This rate is a basic rate. * Keep track of the best basic rate so far by * modulation type. */ if (is_ofdm_rate(rate)) ofdm_basic = rate; else cck_basic = rate; continue; } /* This rate is not a basic rate so figure out the * best basic rate less than this rate and fill in * the hole in the table */ br[rate] = is_ofdm_rate(rate) ? ofdm_basic : cck_basic; if (br[rate] != 0) continue; if (is_ofdm_rate(rate)) { /* * In 11g and 11a, the OFDM mandatory rates * are 6, 12, and 24 Mbps */ if (rate >= BRCM_RATE_24M) mandatory = BRCM_RATE_24M; else if (rate >= BRCM_RATE_12M) mandatory = BRCM_RATE_12M; else mandatory = BRCM_RATE_6M; } else { /* In 11b, all CCK rates are mandatory 1 - 11 Mbps */ mandatory = rate; } br[rate] = mandatory; } } static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc, u16 chanspec) { struct brcms_c_rateset default_rateset; uint parkband; uint i, band_order[2]; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); /* * We might have been bandlocked during down and the chip * power-cycled (hibernate). Figure out the right band to park on */ if (wlc->bandlocked || wlc->pub->_nbands == 1) { /* updated in brcms_c_bandlock() */ parkband = wlc->band->bandunit; band_order[0] = band_order[1] = parkband; } else { /* park on the band of the specified chanspec */ parkband = chspec_bandunit(chanspec); /* order so that parkband initialize last */ band_order[0] = parkband ^ 1; band_order[1] = parkband; } /* make each band operational, software state init */ for (i = 0; i < wlc->pub->_nbands; i++) { uint j = band_order[i]; wlc->band = wlc->bandstate[j]; brcms_default_rateset(wlc, &default_rateset); /* fill in hw_rate */ brcms_c_rateset_filter(&default_rateset, &wlc->band->hw_rateset, false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, (bool) (wlc->pub->_n_enab & SUPPORT_11N)); /* init basic rate lookup */ brcms_c_rate_lookup_init(wlc, &default_rateset); } /* sync up phy/radio chanspec */ brcms_c_set_phy_chanspec(wlc, chanspec); } /* * Set or clear filtering related maccontrol bits based on * specified filter flags */ void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags) { u32 promisc_bits = 0; wlc->filter_flags = filter_flags; if (filter_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) promisc_bits |= MCTL_PROMISC; if (filter_flags & FIF_BCN_PRBRESP_PROMISC) promisc_bits |= MCTL_BCNS_PROMISC; if (filter_flags & FIF_FCSFAIL) promisc_bits |= MCTL_KEEPBADFCS; if (filter_flags & (FIF_CONTROL | FIF_PSPOLL)) promisc_bits |= MCTL_KEEPCONTROL; brcms_b_mctrl(wlc->hw, MCTL_PROMISC | MCTL_BCNS_PROMISC | MCTL_KEEPCONTROL | MCTL_KEEPBADFCS, promisc_bits); } /* * ucode, hwmac update * Channel dependent updates for ucode and hw */ static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc) { /* enable or disable any active IBSSs depending on whether or not * we are on the home channel */ if (wlc->home_chanspec == wlc_phy_chanspec_get(wlc->band->pi)) { if (wlc->pub->associated) { /* * BMAC_NOTE: This is something that should be fixed * in ucode inits. I think that the ucode inits set * up the bcn templates and shm values with a bogus * beacon. This should not be done in the inits. If * ucode needs to set up a beacon for testing, the * test routines should write it down, not expect the * inits to populate a bogus beacon. */ if (BRCMS_PHY_11N_CAP(wlc->band)) brcms_b_write_shm(wlc->hw, M_BCN_TXTSF_OFFSET, 0); } } else { /* disable an active IBSS if we are not on the home channel */ } } static void brcms_c_write_rate_shm(struct brcms_c_info *wlc, u8 rate, u8 basic_rate) { u8 phy_rate, index; u8 basic_phy_rate, basic_index; u16 dir_table, basic_table; u16 basic_ptr; /* Shared memory address for the table we are reading */ dir_table = is_ofdm_rate(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B; /* Shared memory address for the table we are writing */ basic_table = is_ofdm_rate(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B; /* * for a given rate, the LS-nibble of the PLCP SIGNAL field is * the index into the rate table. */ phy_rate = rate_info[rate] & BRCMS_RATE_MASK; basic_phy_rate = rate_info[basic_rate] & BRCMS_RATE_MASK; index = phy_rate & 0xf; basic_index = basic_phy_rate & 0xf; /* Find the SHM pointer to the ACK rate entry by looking in the * Direct-map Table */ basic_ptr = brcms_b_read_shm(wlc->hw, (dir_table + basic_index * 2)); /* Update the SHM BSS-basic-rate-set mapping table with the pointer * to the correct basic rate for the given incoming rate */ brcms_b_write_shm(wlc->hw, (basic_table + index * 2), basic_ptr); } static const struct brcms_c_rateset * brcms_c_rateset_get_hwrs(struct brcms_c_info *wlc) { const struct brcms_c_rateset *rs_dflt; if (BRCMS_PHY_11N_CAP(wlc->band)) { if (wlc->band->bandtype == BRCM_BAND_5G) rs_dflt = &ofdm_mimo_rates; else rs_dflt = &cck_ofdm_mimo_rates; } else if (wlc->band->gmode) rs_dflt = &cck_ofdm_rates; else rs_dflt = &cck_rates; return rs_dflt; } static void brcms_c_set_ratetable(struct brcms_c_info *wlc) { const struct brcms_c_rateset *rs_dflt; struct brcms_c_rateset rs; u8 rate, basic_rate; uint i; rs_dflt = brcms_c_rateset_get_hwrs(wlc); brcms_c_rateset_copy(rs_dflt, &rs); brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); /* walk the phy rate table and update SHM basic rate lookup table */ for (i = 0; i < rs.count; i++) { rate = rs.rates[i] & BRCMS_RATE_MASK; /* for a given rate brcms_basic_rate returns the rate at * which a response ACK/CTS should be sent. */ basic_rate = brcms_basic_rate(wlc, rate); if (basic_rate == 0) /* This should only happen if we are using a * restricted rateset. */ basic_rate = rs.rates[0] & BRCMS_RATE_MASK; brcms_c_write_rate_shm(wlc, rate, basic_rate); } } /* band-specific init */ static void brcms_c_bsinit(struct brcms_c_info *wlc) { BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc->pub->unit, wlc->band->bandunit); /* write ucode ACK/CTS rate table */ brcms_c_set_ratetable(wlc); /* update some band specific mac configuration */ brcms_c_ucode_mac_upd(wlc); /* init antenna selection */ brcms_c_antsel_init(wlc->asi); } /* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */ static int brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM, bool writeToShm) { int idle_busy_ratio_x_16 = 0; uint offset = isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM : M_TX_IDLE_BUSY_RATIO_X_16_CCK; if (duty_cycle > 100 || duty_cycle < 0) { wiphy_err(wlc->wiphy, "wl%d: duty cycle value off limit\n", wlc->pub->unit); return -EINVAL; } if (duty_cycle) idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle; /* Only write to shared memory when wl is up */ if (writeToShm) brcms_b_write_shm(wlc->hw, offset, (u16) idle_busy_ratio_x_16); if (isOFDM) wlc->tx_duty_cycle_ofdm = (u16) duty_cycle; else wlc->tx_duty_cycle_cck = (u16) duty_cycle; return 0; } /* * Initialize the base precedence map for dequeueing * from txq based on WME settings */ static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc) { wlc->tx_prec_map = BRCMS_PREC_BMP_ALL; memset(wlc->fifo2prec_map, 0, NFIFO * sizeof(u16)); wlc->fifo2prec_map[TX_AC_BK_FIFO] = BRCMS_PREC_BMP_AC_BK; wlc->fifo2prec_map[TX_AC_BE_FIFO] = BRCMS_PREC_BMP_AC_BE; wlc->fifo2prec_map[TX_AC_VI_FIFO] = BRCMS_PREC_BMP_AC_VI; wlc->fifo2prec_map[TX_AC_VO_FIFO] = BRCMS_PREC_BMP_AC_VO; } static void brcms_c_txflowcontrol_signal(struct brcms_c_info *wlc, struct brcms_txq_info *qi, bool on, int prio) { /* transmit flowcontrol is not yet implemented */ } static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc) { struct brcms_txq_info *qi; for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) { if (qi->stopped) { brcms_c_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO); qi->stopped = 0; } } } /* push sw hps and wake state through hardware */ static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) { u32 v1, v2; bool hps; bool awake_before; hps = brcms_c_ps_allowed(wlc); BCMMSG(wlc->wiphy, "wl%d: hps %d\n", wlc->pub->unit, hps); v1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); v2 = MCTL_WAKE; if (hps) v2 |= MCTL_HPS; brcms_b_mctrl(wlc->hw, MCTL_WAKE | MCTL_HPS, v2); awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0)); if (!awake_before) brcms_b_wait_for_wake(wlc->hw); } /* * Write this BSS config's MAC address to core. * Updates RXE match engine. */ static int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg) { int err = 0; struct brcms_c_info *wlc = bsscfg->wlc; /* enter the MAC addr into the RXE match registers */ brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, bsscfg->cur_etheraddr); brcms_c_ampdu_macaddr_upd(wlc); return err; } /* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl). * Updates RXE match engine. */ static void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg) { /* we need to update BSSID in RXE match registers */ brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID); } static void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot) { wlc_hw->shortslot = shortslot; if (wlc_hw->band->bandtype == BRCM_BAND_2G && wlc_hw->up) { brcms_c_suspend_mac_and_wait(wlc_hw->wlc); brcms_b_update_slot_timing(wlc_hw, shortslot); brcms_c_enable_mac(wlc_hw->wlc); } } /* * Suspend the the MAC and update the slot timing * for standard 11b/g (20us slots) or shortslot 11g (9us slots). */ static void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot) { /* use the override if it is set */ if (wlc->shortslot_override != BRCMS_SHORTSLOT_AUTO) shortslot = (wlc->shortslot_override == BRCMS_SHORTSLOT_ON); if (wlc->shortslot == shortslot) return; wlc->shortslot = shortslot; brcms_b_set_shortslot(wlc->hw, shortslot); } static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) { if (wlc->home_chanspec != chanspec) { wlc->home_chanspec = chanspec; if (wlc->bsscfg->associated) wlc->bsscfg->current_bss->chanspec = chanspec; } } void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, bool mute_tx, struct txpwr_limits *txpwr) { uint bandunit; BCMMSG(wlc_hw->wlc->wiphy, "wl%d: 0x%x\n", wlc_hw->unit, chanspec); wlc_hw->chanspec = chanspec; /* Switch bands if necessary */ if (wlc_hw->_nbands > 1) { bandunit = chspec_bandunit(chanspec); if (wlc_hw->band->bandunit != bandunit) { /* brcms_b_setband disables other bandunit, * use light band switch if not up yet */ if (wlc_hw->up) { wlc_phy_chanspec_radio_set(wlc_hw-> bandstate[bandunit]-> pi, chanspec); brcms_b_setband(wlc_hw, bandunit, chanspec); } else { brcms_c_setxband(wlc_hw, bandunit); } } } wlc_phy_initcal_enable(wlc_hw->band->pi, !mute_tx); if (!wlc_hw->up) { if (wlc_hw->clk) wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec); wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); } else { wlc_phy_chanspec_set(wlc_hw->band->pi, chanspec); wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec); /* Update muting of the channel */ brcms_b_mute(wlc_hw, mute_tx); } } /* switch to and initialize new band */ static void brcms_c_setband(struct brcms_c_info *wlc, uint bandunit) { wlc->band = wlc->bandstate[bandunit]; if (!wlc->pub->up) return; /* wait for at least one beacon before entering sleeping state */ brcms_c_set_ps_ctrl(wlc); /* band-specific initializations */ brcms_c_bsinit(wlc); } static void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) { uint bandunit; bool switchband = false; u16 old_chanspec = wlc->chanspec; if (!brcms_c_valid_chanspec_db(wlc->cmi, chanspec)) { wiphy_err(wlc->wiphy, "wl%d: %s: Bad channel %d\n", wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)); return; } /* Switch bands if necessary */ if (wlc->pub->_nbands > 1) { bandunit = chspec_bandunit(chanspec); if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) { switchband = true; if (wlc->bandlocked) { wiphy_err(wlc->wiphy, "wl%d: %s: chspec %d " "band is locked!\n", wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)); return; } /* * should the setband call come after the * brcms_b_chanspec() ? if the setband updates * (brcms_c_bsinit) use low level calls to inspect and * set state, the state inspected may be from the wrong * band, or the following brcms_b_set_chanspec() may * undo the work. */ brcms_c_setband(wlc, bandunit); } } /* sync up phy/radio chanspec */ brcms_c_set_phy_chanspec(wlc, chanspec); /* init antenna selection */ if (brcms_chspec_bw(old_chanspec) != brcms_chspec_bw(chanspec)) { brcms_c_antsel_init(wlc->asi); /* Fix the hardware rateset based on bw. * Mainly add MCS32 for 40Mhz, remove MCS 32 for 20Mhz */ brcms_c_rateset_bw_mcs_filter(&wlc->band->hw_rateset, wlc->band->mimo_cap_40 ? brcms_chspec_bw(chanspec) : 0); } /* update some mac configuration since chanspec changed */ brcms_c_ucode_mac_upd(wlc); } /* * This function changes the phytxctl for beacon based on current * beacon ratespec AND txant setting as per this table: * ratespec CCK ant = wlc->stf->txant * OFDM ant = 3 */ void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, u32 bcn_rspec) { u16 phyctl; u16 phytxant = wlc->stf->phytxant; u16 mask = PHY_TXC_ANT_MASK; /* for non-siso rates or default setting, use the available chains */ if (BRCMS_PHY_11N_CAP(wlc->band)) phytxant = brcms_c_stf_phytxchain_sel(wlc, bcn_rspec); phyctl = brcms_b_read_shm(wlc->hw, M_BCN_PCTLWD); phyctl = (phyctl & ~mask) | phytxant; brcms_b_write_shm(wlc->hw, M_BCN_PCTLWD, phyctl); } /* * centralized protection config change function to simplify debugging, no * consistency checking this should be called only on changes to avoid overhead * in periodic function */ void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val) { BCMMSG(wlc->wiphy, "idx %d, val %d\n", idx, val); switch (idx) { case BRCMS_PROT_G_SPEC: wlc->protection->_g = (bool) val; break; case BRCMS_PROT_G_OVR: wlc->protection->g_override = (s8) val; break; case BRCMS_PROT_G_USER: wlc->protection->gmode_user = (u8) val; break; case BRCMS_PROT_OVERLAP: wlc->protection->overlap = (s8) val; break; case BRCMS_PROT_N_USER: wlc->protection->nmode_user = (s8) val; break; case BRCMS_PROT_N_CFG: wlc->protection->n_cfg = (s8) val; break; case BRCMS_PROT_N_CFG_OVR: wlc->protection->n_cfg_override = (s8) val; break; case BRCMS_PROT_N_NONGF: wlc->protection->nongf = (bool) val; break; case BRCMS_PROT_N_NONGF_OVR: wlc->protection->nongf_override = (s8) val; break; case BRCMS_PROT_N_PAM_OVR: wlc->protection->n_pam_override = (s8) val; break; case BRCMS_PROT_N_OBSS: wlc->protection->n_obss = (bool) val; break; default: break; } } static void brcms_c_ht_update_sgi_rx(struct brcms_c_info *wlc, int val) { if (wlc->pub->up) { brcms_c_update_beacon(wlc); brcms_c_update_probe_resp(wlc, true); } } static void brcms_c_ht_update_ldpc(struct brcms_c_info *wlc, s8 val) { wlc->stf->ldpc = val; if (wlc->pub->up) { brcms_c_update_beacon(wlc); brcms_c_update_probe_resp(wlc, true); wlc_phy_ldpc_override_set(wlc->band->pi, (val ? true : false)); } } void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, const struct ieee80211_tx_queue_params *params, bool suspend) { int i; struct shm_acparams acp_shm; u16 *shm_entry; /* Only apply params if the core is out of reset and has clocks */ if (!wlc->clk) { wiphy_err(wlc->wiphy, "wl%d: %s : no-clock\n", wlc->pub->unit, __func__); return; } memset((char *)&acp_shm, 0, sizeof(struct shm_acparams)); /* fill in shm ac params struct */ acp_shm.txop = params->txop; /* convert from units of 32us to us for ucode */ wlc->edcf_txop[aci & 0x3] = acp_shm.txop = EDCF_TXOP2USEC(acp_shm.txop); acp_shm.aifs = (params->aifs & EDCF_AIFSN_MASK); if (aci == IEEE80211_AC_VI && acp_shm.txop == 0 && acp_shm.aifs < EDCF_AIFSN_MAX) acp_shm.aifs++; if (acp_shm.aifs < EDCF_AIFSN_MIN || acp_shm.aifs > EDCF_AIFSN_MAX) { wiphy_err(wlc->wiphy, "wl%d: edcf_setparams: bad " "aifs %d\n", wlc->pub->unit, acp_shm.aifs); } else { acp_shm.cwmin = params->cw_min; acp_shm.cwmax = params->cw_max; acp_shm.cwcur = acp_shm.cwmin; acp_shm.bslots = bcma_read16(wlc->hw->d11core, D11REGOFFS(tsf_random)) & acp_shm.cwcur; acp_shm.reggap = acp_shm.bslots + acp_shm.aifs; /* Indicate the new params to the ucode */ acp_shm.status = brcms_b_read_shm(wlc->hw, (M_EDCF_QINFO + wme_ac2fifo[aci] * M_EDCF_QLEN + M_EDCF_STATUS_OFF)); acp_shm.status |= WME_STATUS_NEWAC; /* Fill in shm acparam table */ shm_entry = (u16 *) &acp_shm; for (i = 0; i < (int)sizeof(struct shm_acparams); i += 2) brcms_b_write_shm(wlc->hw, M_EDCF_QINFO + wme_ac2fifo[aci] * M_EDCF_QLEN + i, *shm_entry++); } if (suspend) { brcms_c_suspend_mac_and_wait(wlc); brcms_c_enable_mac(wlc); } } static void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend) { u16 aci; int i_ac; struct ieee80211_tx_queue_params txq_pars; static const struct edcf_acparam default_edcf_acparams[] = { {EDCF_AC_BE_ACI_STA, EDCF_AC_BE_ECW_STA, EDCF_AC_BE_TXOP_STA}, {EDCF_AC_BK_ACI_STA, EDCF_AC_BK_ECW_STA, EDCF_AC_BK_TXOP_STA}, {EDCF_AC_VI_ACI_STA, EDCF_AC_VI_ECW_STA, EDCF_AC_VI_TXOP_STA}, {EDCF_AC_VO_ACI_STA, EDCF_AC_VO_ECW_STA, EDCF_AC_VO_TXOP_STA} }; /* ucode needs these parameters during its initialization */ const struct edcf_acparam *edcf_acp = &default_edcf_acparams[0]; for (i_ac = 0; i_ac < IEEE80211_NUM_ACS; i_ac++, edcf_acp++) { /* find out which ac this set of params applies to */ aci = (edcf_acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT; /* fill in shm ac params struct */ txq_pars.txop = edcf_acp->TXOP; txq_pars.aifs = edcf_acp->ACI; /* CWmin = 2^(ECWmin) - 1 */ txq_pars.cw_min = EDCF_ECW2CW(edcf_acp->ECW & EDCF_ECWMIN_MASK); /* CWmax = 2^(ECWmax) - 1 */ txq_pars.cw_max = EDCF_ECW2CW((edcf_acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT); brcms_c_wme_setparams(wlc, aci, &txq_pars, suspend); } if (suspend) { brcms_c_suspend_mac_and_wait(wlc); brcms_c_enable_mac(wlc); } } static void brcms_c_radio_monitor_start(struct brcms_c_info *wlc) { /* Don't start the timer if HWRADIO feature is disabled */ if (wlc->radio_monitor) return; wlc->radio_monitor = true; brcms_b_pllreq(wlc->hw, true, BRCMS_PLLREQ_RADIO_MON); brcms_add_timer(wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true); } static bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc) { if (!wlc->radio_monitor) return true; wlc->radio_monitor = false; brcms_b_pllreq(wlc->hw, false, BRCMS_PLLREQ_RADIO_MON); return brcms_del_timer(wlc->radio_timer); } /* read hwdisable state and propagate to wlc flag */ static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc) { if (wlc->pub->hw_off) return; if (brcms_b_radio_read_hwdisabled(wlc->hw)) mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); else mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); } /* update hwradio status and return it */ bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc) { brcms_c_radio_hwdisable_upd(wlc); return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ? true : false; } /* periodical query hw radio button while driver is "down" */ static void brcms_c_radio_timer(void *arg) { struct brcms_c_info *wlc = (struct brcms_c_info *) arg; if (brcms_deviceremoved(wlc)) { wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit, __func__); brcms_down(wlc->wl); return; } brcms_c_radio_hwdisable_upd(wlc); } /* common low-level watchdog code */ static void brcms_b_watchdog(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); if (!wlc_hw->up) return; /* increment second count */ wlc_hw->now++; /* Check for FIFO error interrupts */ brcms_b_fifoerrors(wlc_hw); /* make sure RX dma has buffers */ dma_rxfill(wlc->hw->di[RX_FIFO]); wlc_phy_watchdog(wlc_hw->band->pi); } /* common watchdog code */ static void brcms_c_watchdog(struct brcms_c_info *wlc) { BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); if (!wlc->pub->up) return; if (brcms_deviceremoved(wlc)) { wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit, __func__); brcms_down(wlc->wl); return; } /* increment second count */ wlc->pub->now++; brcms_c_radio_hwdisable_upd(wlc); /* if radio is disable, driver may be down, quit here */ if (wlc->pub->radio_disabled) return; brcms_b_watchdog(wlc); /* * occasionally sample mac stat counters to * detect 16-bit counter wrap */ if ((wlc->pub->now % SW_TIMER_MAC_STAT_UPD) == 0) brcms_c_statsupd(wlc); if (BRCMS_ISNPHY(wlc->band) && ((wlc->pub->now - wlc->tempsense_lasttime) >= BRCMS_TEMPSENSE_PERIOD)) { wlc->tempsense_lasttime = wlc->pub->now; brcms_c_tempsense_upd(wlc); } } static void brcms_c_watchdog_by_timer(void *arg) { struct brcms_c_info *wlc = (struct brcms_c_info *) arg; brcms_c_watchdog(wlc); } static bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit) { wlc->wdtimer = brcms_init_timer(wlc->wl, brcms_c_watchdog_by_timer, wlc, "watchdog"); if (!wlc->wdtimer) { wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for wdtimer " "failed\n", unit); goto fail; } wlc->radio_timer = brcms_init_timer(wlc->wl, brcms_c_radio_timer, wlc, "radio"); if (!wlc->radio_timer) { wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for radio_timer " "failed\n", unit); goto fail; } return true; fail: return false; } /* * Initialize brcms_c_info default values ... * may get overrides later in this function */ static void brcms_c_info_init(struct brcms_c_info *wlc, int unit) { int i; /* Save our copy of the chanspec */ wlc->chanspec = ch20mhz_chspec(1); /* various 802.11g modes */ wlc->shortslot = false; wlc->shortslot_override = BRCMS_SHORTSLOT_AUTO; brcms_c_protection_upd(wlc, BRCMS_PROT_G_OVR, BRCMS_PROTECTION_AUTO); brcms_c_protection_upd(wlc, BRCMS_PROT_G_SPEC, false); brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG_OVR, BRCMS_PROTECTION_AUTO); brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG, BRCMS_N_PROTECTION_OFF); brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF_OVR, BRCMS_PROTECTION_AUTO); brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF, false); brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, AUTO); brcms_c_protection_upd(wlc, BRCMS_PROT_OVERLAP, BRCMS_PROTECTION_CTL_OVERLAP); /* 802.11g draft 4.0 NonERP elt advertisement */ wlc->include_legacy_erp = true; wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF; wlc->stf->txant = ANT_TX_DEF; wlc->prb_resp_timeout = BRCMS_PRB_RESP_TIMEOUT; wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN; for (i = 0; i < NFIFO; i++) wlc->fragthresh[i] = DOT11_DEFAULT_FRAG_LEN; wlc->RTSThresh = DOT11_DEFAULT_RTS_LEN; /* default rate fallback retry limits */ wlc->SFBL = RETRY_SHORT_FB; wlc->LFBL = RETRY_LONG_FB; /* default mac retry limits */ wlc->SRL = RETRY_SHORT_DEF; wlc->LRL = RETRY_LONG_DEF; /* WME QoS mode is Auto by default */ wlc->pub->_ampdu = AMPDU_AGG_HOST; wlc->pub->bcmerror = 0; } static uint brcms_c_attach_module(struct brcms_c_info *wlc) { uint err = 0; uint unit; unit = wlc->pub->unit; wlc->asi = brcms_c_antsel_attach(wlc); if (wlc->asi == NULL) { wiphy_err(wlc->wiphy, "wl%d: attach: antsel_attach " "failed\n", unit); err = 44; goto fail; } wlc->ampdu = brcms_c_ampdu_attach(wlc); if (wlc->ampdu == NULL) { wiphy_err(wlc->wiphy, "wl%d: attach: ampdu_attach " "failed\n", unit); err = 50; goto fail; } if ((brcms_c_stf_attach(wlc) != 0)) { wiphy_err(wlc->wiphy, "wl%d: attach: stf_attach " "failed\n", unit); err = 68; goto fail; } fail: return err; } struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc) { return wlc->pub; } /* low level attach * run backplane attach, init nvram * run phy attach * initialize software state for each core and band * put the whole chip in reset(driver down state), no clock */ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, uint unit, bool piomode) { struct brcms_hardware *wlc_hw; uint err = 0; uint j; bool wme = false; struct shared_phy_params sha_params; struct wiphy *wiphy = wlc->wiphy; struct pci_dev *pcidev = core->bus->host_pci; struct ssb_sprom *sprom = &core->bus->sprom; if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) BCMMSG(wlc->wiphy, "wl%d: vendor 0x%x device 0x%x\n", unit, pcidev->vendor, pcidev->device); else BCMMSG(wlc->wiphy, "wl%d: vendor 0x%x device 0x%x\n", unit, core->bus->boardinfo.vendor, core->bus->boardinfo.type); wme = true; wlc_hw = wlc->hw; wlc_hw->wlc = wlc; wlc_hw->unit = unit; wlc_hw->band = wlc_hw->bandstate[0]; wlc_hw->_piomode = piomode; /* populate struct brcms_hardware with default values */ brcms_b_info_init(wlc_hw); /* * Do the hardware portion of the attach. Also initialize software * state that depends on the particular hardware we are running. */ wlc_hw->sih = ai_attach(core->bus); if (wlc_hw->sih == NULL) { wiphy_err(wiphy, "wl%d: brcms_b_attach: si_attach failed\n", unit); err = 11; goto fail; } /* verify again the device is supported */ if (!brcms_c_chipmatch(core)) { wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported device\n", unit); err = 12; goto fail; } if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) { wlc_hw->vendorid = pcidev->vendor; wlc_hw->deviceid = pcidev->device; } else { wlc_hw->vendorid = core->bus->boardinfo.vendor; wlc_hw->deviceid = core->bus->boardinfo.type; } wlc_hw->d11core = core; wlc_hw->corerev = core->id.rev; /* validate chip, chiprev and corerev */ if (!brcms_c_isgoodchip(wlc_hw)) { err = 13; goto fail; } /* initialize power control registers */ ai_clkctl_init(wlc_hw->sih); /* request fastclock and force fastclock for the rest of attach * bring the d11 core out of reset. * For PMU chips, the first wlc_clkctl_clk is no-op since core-clk * is still false; But it will be called again inside wlc_corereset, * after d11 is out of reset. */ brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); if (!brcms_b_validate_chip_access(wlc_hw)) { wiphy_err(wiphy, "wl%d: brcms_b_attach: validate_chip_access " "failed\n", unit); err = 14; goto fail; } /* get the board rev, used just below */ j = sprom->board_rev; /* promote srom boardrev of 0xFF to 1 */ if (j == BOARDREV_PROMOTABLE) j = BOARDREV_PROMOTED; wlc_hw->boardrev = (u16) j; if (!brcms_c_validboardtype(wlc_hw)) { wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported Broadcom " "board type (0x%x)" " or revision level (0x%x)\n", unit, ai_get_boardtype(wlc_hw->sih), wlc_hw->boardrev); err = 15; goto fail; } wlc_hw->sromrev = sprom->revision; wlc_hw->boardflags = sprom->boardflags_lo + (sprom->boardflags_hi << 16); wlc_hw->boardflags2 = sprom->boardflags2_lo + (sprom->boardflags2_hi << 16); if (wlc_hw->boardflags & BFL_NOPLLDOWN) brcms_b_pllreq(wlc_hw, true, BRCMS_PLLREQ_SHARED); /* check device id(srom, nvram etc.) to set bands */ if (wlc_hw->deviceid == BCM43224_D11N_ID || wlc_hw->deviceid == BCM43224_D11N_ID_VEN1) /* Dualband boards */ wlc_hw->_nbands = 2; else wlc_hw->_nbands = 1; if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) wlc_hw->_nbands = 1; /* BMAC_NOTE: remove init of pub values when brcms_c_attach() * unconditionally does the init of these values */ wlc->vendorid = wlc_hw->vendorid; wlc->deviceid = wlc_hw->deviceid; wlc->pub->sih = wlc_hw->sih; wlc->pub->corerev = wlc_hw->corerev; wlc->pub->sromrev = wlc_hw->sromrev; wlc->pub->boardrev = wlc_hw->boardrev; wlc->pub->boardflags = wlc_hw->boardflags; wlc->pub->boardflags2 = wlc_hw->boardflags2; wlc->pub->_nbands = wlc_hw->_nbands; wlc_hw->physhim = wlc_phy_shim_attach(wlc_hw, wlc->wl, wlc); if (wlc_hw->physhim == NULL) { wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_shim_attach " "failed\n", unit); err = 25; goto fail; } /* pass all the parameters to wlc_phy_shared_attach in one struct */ sha_params.sih = wlc_hw->sih; sha_params.physhim = wlc_hw->physhim; sha_params.unit = unit; sha_params.corerev = wlc_hw->corerev; sha_params.vid = wlc_hw->vendorid; sha_params.did = wlc_hw->deviceid; sha_params.chip = ai_get_chip_id(wlc_hw->sih); sha_params.chiprev = ai_get_chiprev(wlc_hw->sih); sha_params.chippkg = ai_get_chippkg(wlc_hw->sih); sha_params.sromrev = wlc_hw->sromrev; sha_params.boardtype = ai_get_boardtype(wlc_hw->sih); sha_params.boardrev = wlc_hw->boardrev; sha_params.boardflags = wlc_hw->boardflags; sha_params.boardflags2 = wlc_hw->boardflags2; /* alloc and save pointer to shared phy state area */ wlc_hw->phy_sh = wlc_phy_shared_attach(&sha_params); if (!wlc_hw->phy_sh) { err = 16; goto fail; } /* initialize software state for each core and band */ for (j = 0; j < wlc_hw->_nbands; j++) { /* * band0 is always 2.4Ghz * band1, if present, is 5Ghz */ brcms_c_setxband(wlc_hw, j); wlc_hw->band->bandunit = j; wlc_hw->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; wlc->band->bandunit = j; wlc->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; wlc->core->coreidx = core->core_index; wlc_hw->machwcap = bcma_read32(core, D11REGOFFS(machwcap)); wlc_hw->machwcap_backup = wlc_hw->machwcap; /* init tx fifo size */ WARN_ON((wlc_hw->corerev - XMTFIFOTBL_STARTREV) < 0 || (wlc_hw->corerev - XMTFIFOTBL_STARTREV) > ARRAY_SIZE(xmtfifo_sz)); wlc_hw->xmtfifo_sz = xmtfifo_sz[(wlc_hw->corerev - XMTFIFOTBL_STARTREV)]; WARN_ON(!wlc_hw->xmtfifo_sz[0]); /* Get a phy for this band */ wlc_hw->band->pi = wlc_phy_attach(wlc_hw->phy_sh, core, wlc_hw->band->bandtype, wlc->wiphy); if (wlc_hw->band->pi == NULL) { wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_" "attach failed\n", unit); err = 17; goto fail; } wlc_phy_machwcap_set(wlc_hw->band->pi, wlc_hw->machwcap); wlc_phy_get_phyversion(wlc_hw->band->pi, &wlc_hw->band->phytype, &wlc_hw->band->phyrev, &wlc_hw->band->radioid, &wlc_hw->band->radiorev); wlc_hw->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi); wlc->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi); wlc_hw->band->core_flags = wlc_phy_get_coreflags(wlc_hw->band->pi); /* verify good phy_type & supported phy revision */ if (BRCMS_ISNPHY(wlc_hw->band)) { if (NCONF_HAS(wlc_hw->band->phyrev)) goto good_phy; else goto bad_phy; } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { if (LCNCONF_HAS(wlc_hw->band->phyrev)) goto good_phy; else goto bad_phy; } else { bad_phy: wiphy_err(wiphy, "wl%d: brcms_b_attach: unsupported " "phy type/rev (%d/%d)\n", unit, wlc_hw->band->phytype, wlc_hw->band->phyrev); err = 18; goto fail; } good_phy: /* * BMAC_NOTE: wlc->band->pi should not be set below and should * be done in the high level attach. However we can not make * that change until all low level access is changed to * wlc_hw->band->pi. Instead do the wlc->band->pi init below, * keeping wlc_hw->band->pi as well for incremental update of * low level fns, and cut over low only init when all fns * updated. */ wlc->band->pi = wlc_hw->band->pi; wlc->band->phytype = wlc_hw->band->phytype; wlc->band->phyrev = wlc_hw->band->phyrev; wlc->band->radioid = wlc_hw->band->radioid; wlc->band->radiorev = wlc_hw->band->radiorev; /* default contention windows size limits */ wlc_hw->band->CWmin = APHY_CWMIN; wlc_hw->band->CWmax = PHY_CWMAX; if (!brcms_b_attach_dmapio(wlc, j, wme)) { err = 19; goto fail; } } /* disable core to match driver "down" state */ brcms_c_coredisable(wlc_hw); /* Match driver "down" state */ ai_pci_down(wlc_hw->sih); /* turn off pll and xtal to match driver "down" state */ brcms_b_xtal(wlc_hw, OFF); /* ******************************************************************* * The hardware is in the DOWN state at this point. D11 core * or cores are in reset with clocks off, and the board PLLs * are off if possible. * * Beyond this point, wlc->sbclk == false and chip registers * should not be touched. ********************************************************************* */ /* init etheraddr state variables */ brcms_c_get_macaddr(wlc_hw, wlc_hw->etheraddr); if (is_broadcast_ether_addr(wlc_hw->etheraddr) || is_zero_ether_addr(wlc_hw->etheraddr)) { wiphy_err(wiphy, "wl%d: brcms_b_attach: bad macaddr\n", unit); err = 22; goto fail; } BCMMSG(wlc->wiphy, "deviceid 0x%x nbands %d board 0x%x\n", wlc_hw->deviceid, wlc_hw->_nbands, ai_get_boardtype(wlc_hw->sih)); return err; fail: wiphy_err(wiphy, "wl%d: brcms_b_attach: failed with err %d\n", unit, err); return err; } static void brcms_c_attach_antgain_init(struct brcms_c_info *wlc) { uint unit; unit = wlc->pub->unit; if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) { /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */ wlc->band->antgain = 8; } else if (wlc->band->antgain == -1) { wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in" " srom, using 2dB\n", unit, __func__); wlc->band->antgain = 8; } else { s8 gain, fract; /* Older sroms specified gain in whole dbm only. In order * be able to specify qdbm granularity and remain backward * compatible the whole dbms are now encoded in only * low 6 bits and remaining qdbms are encoded in the hi 2 bits. * 6 bit signed number ranges from -32 - 31. * * Examples: * 0x1 = 1 db, * 0xc1 = 1.75 db (1 + 3 quarters), * 0x3f = -1 (-1 + 0 quarters), * 0x7f = -.75 (-1 + 1 quarters) = -3 qdbm. * 0xbf = -.50 (-1 + 2 quarters) = -2 qdbm. */ gain = wlc->band->antgain & 0x3f; gain <<= 2; /* Sign extend */ gain >>= 2; fract = (wlc->band->antgain & 0xc0) >> 6; wlc->band->antgain = 4 * gain + fract; } } static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc) { int aa; uint unit; int bandtype; struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; unit = wlc->pub->unit; bandtype = wlc->band->bandtype; /* get antennas available */ if (bandtype == BRCM_BAND_5G) aa = sprom->ant_available_a; else aa = sprom->ant_available_bg; if ((aa < 1) || (aa > 15)) { wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in" " srom (0x%x), using 3\n", unit, __func__, aa); aa = 3; } /* reset the defaults if we have a single antenna */ if (aa == 1) { wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0; wlc->stf->txant = ANT_TX_FORCE_0; } else if (aa == 2) { wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1; wlc->stf->txant = ANT_TX_FORCE_1; } else { } /* Compute Antenna Gain */ if (bandtype == BRCM_BAND_5G) wlc->band->antgain = sprom->antenna_gain.a1; else wlc->band->antgain = sprom->antenna_gain.a0; brcms_c_attach_antgain_init(wlc); return true; } static void brcms_c_bss_default_init(struct brcms_c_info *wlc) { u16 chanspec; struct brcms_band *band; struct brcms_bss_info *bi = wlc->default_bss; /* init default and target BSS with some sane initial values */ memset((char *)(bi), 0, sizeof(struct brcms_bss_info)); bi->beacon_period = BEACON_INTERVAL_DEFAULT; /* fill the default channel as the first valid channel * starting from the 2G channels */ chanspec = ch20mhz_chspec(1); wlc->home_chanspec = bi->chanspec = chanspec; /* find the band of our default channel */ band = wlc->band; if (wlc->pub->_nbands > 1 && band->bandunit != chspec_bandunit(chanspec)) band = wlc->bandstate[OTHERBANDUNIT(wlc)]; /* init bss rates to the band specific default rate set */ brcms_c_rateset_default(&bi->rateset, NULL, band->phytype, band->bandtype, false, BRCMS_RATE_MASK_FULL, (bool) (wlc->pub->_n_enab & SUPPORT_11N), brcms_chspec_bw(chanspec), wlc->stf->txstreams); if (wlc->pub->_n_enab & SUPPORT_11N) bi->flags |= BRCMS_BSS_HT; } static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc) { struct brcms_txq_info *qi, *p; qi = kzalloc(sizeof(struct brcms_txq_info), GFP_ATOMIC); if (qi != NULL) { /* * Have enough room for control packets along with HI watermark * Also, add room to txq for total psq packets if all the SCBs * leave PS mode. The watermark for flowcontrol to OS packets * will remain the same */ brcmu_pktq_init(&qi->q, BRCMS_PREC_COUNT, 2 * BRCMS_DATAHIWAT + PKTQ_LEN_DEFAULT); /* add this queue to the the global list */ p = wlc->tx_queues; if (p == NULL) { wlc->tx_queues = qi; } else { while (p->next != NULL) p = p->next; p->next = qi; } } return qi; } static void brcms_c_txq_free(struct brcms_c_info *wlc, struct brcms_txq_info *qi) { struct brcms_txq_info *p; if (qi == NULL) return; /* remove the queue from the linked list */ p = wlc->tx_queues; if (p == qi) wlc->tx_queues = p->next; else { while (p != NULL && p->next != qi) p = p->next; if (p != NULL) p->next = p->next->next; } kfree(qi); } static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap) { uint i; struct brcms_band *band; for (i = 0; i < wlc->pub->_nbands; i++) { band = wlc->bandstate[i]; if (band->bandtype == BRCM_BAND_5G) { if ((bwcap == BRCMS_N_BW_40ALL) || (bwcap == BRCMS_N_BW_20IN2G_40IN5G)) band->mimo_cap_40 = true; else band->mimo_cap_40 = false; } else { if (bwcap == BRCMS_N_BW_40ALL) band->mimo_cap_40 = true; else band->mimo_cap_40 = false; } } } static void brcms_c_timers_deinit(struct brcms_c_info *wlc) { /* free timer state */ if (wlc->wdtimer) { brcms_free_timer(wlc->wdtimer); wlc->wdtimer = NULL; } if (wlc->radio_timer) { brcms_free_timer(wlc->radio_timer); wlc->radio_timer = NULL; } } static void brcms_c_detach_module(struct brcms_c_info *wlc) { if (wlc->asi) { brcms_c_antsel_detach(wlc->asi); wlc->asi = NULL; } if (wlc->ampdu) { brcms_c_ampdu_detach(wlc->ampdu); wlc->ampdu = NULL; } brcms_c_stf_detach(wlc); } /* * low level detach */ static int brcms_b_detach(struct brcms_c_info *wlc) { uint i; struct brcms_hw_band *band; struct brcms_hardware *wlc_hw = wlc->hw; int callbacks; callbacks = 0; brcms_b_detach_dmapio(wlc_hw); band = wlc_hw->band; for (i = 0; i < wlc_hw->_nbands; i++) { if (band->pi) { /* Detach this band's phy */ wlc_phy_detach(band->pi); band->pi = NULL; } band = wlc_hw->bandstate[OTHERBANDUNIT(wlc)]; } /* Free shared phy state */ kfree(wlc_hw->phy_sh); wlc_phy_shim_detach(wlc_hw->physhim); if (wlc_hw->sih) { ai_detach(wlc_hw->sih); wlc_hw->sih = NULL; } return callbacks; } /* * Return a count of the number of driver callbacks still pending. * * General policy is that brcms_c_detach can only dealloc/free software states. * It can NOT touch hardware registers since the d11core may be in reset and * clock may not be available. * One exception is sb register access, which is possible if crystal is turned * on after "down" state, driver should avoid software timer with the exception * of radio_monitor. */ uint brcms_c_detach(struct brcms_c_info *wlc) { uint callbacks = 0; if (wlc == NULL) return 0; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); callbacks += brcms_b_detach(wlc); /* delete software timers */ if (!brcms_c_radio_monitor_stop(wlc)) callbacks++; brcms_c_channel_mgr_detach(wlc->cmi); brcms_c_timers_deinit(wlc); brcms_c_detach_module(wlc); while (wlc->tx_queues != NULL) brcms_c_txq_free(wlc, wlc->tx_queues); brcms_c_detach_mfree(wlc); return callbacks; } /* update state that depends on the current value of "ap" */ static void brcms_c_ap_upd(struct brcms_c_info *wlc) { /* STA-BSS; short capable */ wlc->PLCPHdr_override = BRCMS_PLCP_SHORT; } /* Initialize just the hardware when coming out of POR or S3/S5 system states */ static void brcms_b_hw_up(struct brcms_hardware *wlc_hw) { if (wlc_hw->wlc->pub->hw_up) return; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); /* * Enable pll and xtal, initialize the power control registers, * and force fastclock for the remainder of brcms_c_up(). */ brcms_b_xtal(wlc_hw, ON); ai_clkctl_init(wlc_hw->sih); brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); /* * TODO: test suspend/resume * * AI chip doesn't restore bar0win2 on * hibernation/resume, need sw fixup */ /* * Inform phy that a POR reset has occurred so * it does a complete phy init */ wlc_phy_por_inform(wlc_hw->band->pi); wlc_hw->ucode_loaded = false; wlc_hw->wlc->pub->hw_up = true; if ((wlc_hw->boardflags & BFL_FEM) && (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { if (! (wlc_hw->boardrev >= 0x1250 && (wlc_hw->boardflags & BFL_FEM_BT))) ai_epa_4313war(wlc_hw->sih); } } static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); /* * Enable pll and xtal, initialize the power control registers, * and force fastclock for the remainder of brcms_c_up(). */ brcms_b_xtal(wlc_hw, ON); ai_clkctl_init(wlc_hw->sih); brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); /* * Configure pci/pcmcia here instead of in brcms_c_attach() * to allow mfg hotswap: down, hotswap (chip power cycle), up. */ bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci, wlc_hw->d11core, true); /* * Need to read the hwradio status here to cover the case where the * system is loaded with the hw radio disabled. We do not want to * bring the driver up in this case. */ if (brcms_b_radio_read_hwdisabled(wlc_hw)) { /* put SB PCI in down state again */ ai_pci_down(wlc_hw->sih); brcms_b_xtal(wlc_hw, OFF); return -ENOMEDIUM; } ai_pci_up(wlc_hw->sih); /* reset the d11 core */ brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); return 0; } static int brcms_b_up_finish(struct brcms_hardware *wlc_hw) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); wlc_hw->up = true; wlc_phy_hw_state_upd(wlc_hw->band->pi, true); /* FULLY enable dynamic power control and d11 core interrupt */ brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); brcms_intrson(wlc_hw->wlc->wl); return 0; } /* * Write WME tunable parameters for retransmit/max rate * from wlc struct to ucode */ static void brcms_c_wme_retries_write(struct brcms_c_info *wlc) { int ac; /* Need clock to do this */ if (!wlc->clk) return; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) brcms_b_write_shm(wlc->hw, M_AC_TXLMT_ADDR(ac), wlc->wme_retries[ac]); } /* make interface operational */ int brcms_c_up(struct brcms_c_info *wlc) { struct ieee80211_channel *ch; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); /* HW is turned off so don't try to access it */ if (wlc->pub->hw_off || brcms_deviceremoved(wlc)) return -ENOMEDIUM; if (!wlc->pub->hw_up) { brcms_b_hw_up(wlc->hw); wlc->pub->hw_up = true; } if ((wlc->pub->boardflags & BFL_FEM) && (ai_get_chip_id(wlc->hw->sih) == BCMA_CHIP_ID_BCM4313)) { if (wlc->pub->boardrev >= 0x1250 && (wlc->pub->boardflags & BFL_FEM_BT)) brcms_b_mhf(wlc->hw, MHF5, MHF5_4313_GPIOCTRL, MHF5_4313_GPIOCTRL, BRCM_BAND_ALL); else brcms_b_mhf(wlc->hw, MHF4, MHF4_EXTPA_ENABLE, MHF4_EXTPA_ENABLE, BRCM_BAND_ALL); } /* * Need to read the hwradio status here to cover the case where the * system is loaded with the hw radio disabled. We do not want to bring * the driver up in this case. If radio is disabled, abort up, lower * power, start radio timer and return 0(for NDIS) don't call * radio_update to avoid looping brcms_c_up. * * brcms_b_up_prep() returns either 0 or -BCME_RADIOOFF only */ if (!wlc->pub->radio_disabled) { int status = brcms_b_up_prep(wlc->hw); if (status == -ENOMEDIUM) { if (!mboolisset (wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE)) { struct brcms_bss_cfg *bsscfg = wlc->bsscfg; mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); if (bsscfg->enable && bsscfg->BSS) wiphy_err(wlc->wiphy, "wl%d: up" ": rfdisable -> " "bsscfg_disable()\n", wlc->pub->unit); } } } if (wlc->pub->radio_disabled) { brcms_c_radio_monitor_start(wlc); return 0; } /* brcms_b_up_prep has done brcms_c_corereset(). so clk is on, set it */ wlc->clk = true; brcms_c_radio_monitor_stop(wlc); /* Set EDCF hostflags */ brcms_b_mhf(wlc->hw, MHF1, MHF1_EDCF, MHF1_EDCF, BRCM_BAND_ALL); brcms_init(wlc->wl); wlc->pub->up = true; if (wlc->bandinit_pending) { ch = wlc->pub->ieee_hw->conf.channel; brcms_c_suspend_mac_and_wait(wlc); brcms_c_set_chanspec(wlc, ch20mhz_chspec(ch->hw_value)); wlc->bandinit_pending = false; brcms_c_enable_mac(wlc); } brcms_b_up_finish(wlc->hw); /* Program the TX wme params with the current settings */ brcms_c_wme_retries_write(wlc); /* start one second watchdog timer */ brcms_add_timer(wlc->wdtimer, TIMER_INTERVAL_WATCHDOG, true); wlc->WDarmed = true; /* ensure antenna config is up to date */ brcms_c_stf_phy_txant_upd(wlc); /* ensure LDPC config is in sync */ brcms_c_ht_update_ldpc(wlc, wlc->stf->ldpc); return 0; } static uint brcms_c_down_del_timer(struct brcms_c_info *wlc) { uint callbacks = 0; return callbacks; } static int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw) { bool dev_gone; uint callbacks = 0; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); if (!wlc_hw->up) return callbacks; dev_gone = brcms_deviceremoved(wlc_hw->wlc); /* disable interrupts */ if (dev_gone) wlc_hw->wlc->macintmask = 0; else { /* now disable interrupts */ brcms_intrsoff(wlc_hw->wlc->wl); /* ensure we're running on the pll clock again */ brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); } /* down phy at the last of this stage */ callbacks += wlc_phy_down(wlc_hw->band->pi); return callbacks; } static int brcms_b_down_finish(struct brcms_hardware *wlc_hw) { uint callbacks = 0; bool dev_gone; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); if (!wlc_hw->up) return callbacks; wlc_hw->up = false; wlc_phy_hw_state_upd(wlc_hw->band->pi, false); dev_gone = brcms_deviceremoved(wlc_hw->wlc); if (dev_gone) { wlc_hw->sbclk = false; wlc_hw->clk = false; wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); /* reclaim any posted packets */ brcms_c_flushqueues(wlc_hw->wlc); } else { /* Reset and disable the core */ if (bcma_core_is_enabled(wlc_hw->d11core)) { if (bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC) brcms_c_suspend_mac_and_wait(wlc_hw->wlc); callbacks += brcms_reset(wlc_hw->wlc->wl); brcms_c_coredisable(wlc_hw); } /* turn off primary xtal and pll */ if (!wlc_hw->noreset) { ai_pci_down(wlc_hw->sih); brcms_b_xtal(wlc_hw, OFF); } } return callbacks; } /* * Mark the interface nonoperational, stop the software mechanisms, * disable the hardware, free any transient buffer state. * Return a count of the number of driver callbacks still pending. */ uint brcms_c_down(struct brcms_c_info *wlc) { uint callbacks = 0; int i; bool dev_gone = false; struct brcms_txq_info *qi; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); /* check if we are already in the going down path */ if (wlc->going_down) { wiphy_err(wlc->wiphy, "wl%d: %s: Driver going down so return" "\n", wlc->pub->unit, __func__); return 0; } if (!wlc->pub->up) return callbacks; wlc->going_down = true; callbacks += brcms_b_bmac_down_prep(wlc->hw); dev_gone = brcms_deviceremoved(wlc); /* Call any registered down handlers */ for (i = 0; i < BRCMS_MAXMODULES; i++) { if (wlc->modulecb[i].down_fn) callbacks += wlc->modulecb[i].down_fn(wlc->modulecb[i].hdl); } /* cancel the watchdog timer */ if (wlc->WDarmed) { if (!brcms_del_timer(wlc->wdtimer)) callbacks++; wlc->WDarmed = false; } /* cancel all other timers */ callbacks += brcms_c_down_del_timer(wlc); wlc->pub->up = false; wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL); /* clear txq flow control */ brcms_c_txflowcontrol_reset(wlc); /* flush tx queues */ for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) brcmu_pktq_flush(&qi->q, true, NULL, NULL); callbacks += brcms_b_down_finish(wlc->hw); /* brcms_b_down_finish has done brcms_c_coredisable(). so clk is off */ wlc->clk = false; wlc->going_down = false; return callbacks; } /* Set the current gmode configuration */ int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config) { int ret = 0; uint i; struct brcms_c_rateset rs; /* Default to 54g Auto */ /* Advertise and use shortslot (-1/0/1 Auto/Off/On) */ s8 shortslot = BRCMS_SHORTSLOT_AUTO; bool shortslot_restrict = false; /* Restrict association to stations * that support shortslot */ bool ofdm_basic = false; /* Make 6, 12, and 24 basic rates */ /* Advertise and use short preambles (-1/0/1 Auto/Off/On) */ int preamble = BRCMS_PLCP_LONG; bool preamble_restrict = false; /* Restrict association to stations * that support short preambles */ struct brcms_band *band; /* if N-support is enabled, allow Gmode set as long as requested * Gmode is not GMODE_LEGACY_B */ if ((wlc->pub->_n_enab & SUPPORT_11N) && gmode == GMODE_LEGACY_B) return -ENOTSUPP; /* verify that we are dealing with 2G band and grab the band pointer */ if (wlc->band->bandtype == BRCM_BAND_2G) band = wlc->band; else if ((wlc->pub->_nbands > 1) && (wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype == BRCM_BAND_2G)) band = wlc->bandstate[OTHERBANDUNIT(wlc)]; else return -EINVAL; /* update configuration value */ if (config) brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, gmode); /* Clear rateset override */ memset(&rs, 0, sizeof(struct brcms_c_rateset)); switch (gmode) { case GMODE_LEGACY_B: shortslot = BRCMS_SHORTSLOT_OFF; brcms_c_rateset_copy(&gphy_legacy_rates, &rs); break; case GMODE_LRS: break; case GMODE_AUTO: /* Accept defaults */ break; case GMODE_ONLY: ofdm_basic = true; preamble = BRCMS_PLCP_SHORT; preamble_restrict = true; break; case GMODE_PERFORMANCE: shortslot = BRCMS_SHORTSLOT_ON; shortslot_restrict = true; ofdm_basic = true; preamble = BRCMS_PLCP_SHORT; preamble_restrict = true; break; default: /* Error */ wiphy_err(wlc->wiphy, "wl%d: %s: invalid gmode %d\n", wlc->pub->unit, __func__, gmode); return -ENOTSUPP; } band->gmode = gmode; wlc->shortslot_override = shortslot; /* Use the default 11g rateset */ if (!rs.count) brcms_c_rateset_copy(&cck_ofdm_rates, &rs); if (ofdm_basic) { for (i = 0; i < rs.count; i++) { if (rs.rates[i] == BRCM_RATE_6M || rs.rates[i] == BRCM_RATE_12M || rs.rates[i] == BRCM_RATE_24M) rs.rates[i] |= BRCMS_RATE_FLAG; } } /* Set default bss rateset */ wlc->default_bss->rateset.count = rs.count; memcpy(wlc->default_bss->rateset.rates, rs.rates, sizeof(wlc->default_bss->rateset.rates)); return ret; } int brcms_c_set_nmode(struct brcms_c_info *wlc) { uint i; s32 nmode = AUTO; if (wlc->stf->txstreams == WL_11N_3x3) nmode = WL_11N_3x3; else nmode = WL_11N_2x2; /* force GMODE_AUTO if NMODE is ON */ brcms_c_set_gmode(wlc, GMODE_AUTO, true); if (nmode == WL_11N_3x3) wlc->pub->_n_enab = SUPPORT_HT; else wlc->pub->_n_enab = SUPPORT_11N; wlc->default_bss->flags |= BRCMS_BSS_HT; /* add the mcs rates to the default and hw ratesets */ brcms_c_rateset_mcs_build(&wlc->default_bss->rateset, wlc->stf->txstreams); for (i = 0; i < wlc->pub->_nbands; i++) memcpy(wlc->bandstate[i]->hw_rateset.mcs, wlc->default_bss->rateset.mcs, MCSSET_LEN); return 0; } static int brcms_c_set_internal_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs_arg) { struct brcms_c_rateset rs, new; uint bandunit; memcpy(&rs, rs_arg, sizeof(struct brcms_c_rateset)); /* check for bad count value */ if ((rs.count == 0) || (rs.count > BRCMS_NUMRATES)) return -EINVAL; /* try the current band */ bandunit = wlc->band->bandunit; memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); if (brcms_c_rate_hwrs_filter_sort_validate (&new, &wlc->bandstate[bandunit]->hw_rateset, true, wlc->stf->txstreams)) goto good; /* try the other band */ if (brcms_is_mband_unlocked(wlc)) { bandunit = OTHERBANDUNIT(wlc); memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); if (brcms_c_rate_hwrs_filter_sort_validate(&new, &wlc-> bandstate[bandunit]-> hw_rateset, true, wlc->stf->txstreams)) goto good; } return -EBADE; good: /* apply new rateset */ memcpy(&wlc->default_bss->rateset, &new, sizeof(struct brcms_c_rateset)); memcpy(&wlc->bandstate[bandunit]->defrateset, &new, sizeof(struct brcms_c_rateset)); return 0; } static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc) { u8 r; bool war = false; if (wlc->bsscfg->associated) r = wlc->bsscfg->current_bss->rateset.rates[0]; else r = wlc->default_bss->rateset.rates[0]; wlc_phy_ofdm_rateset_war(wlc->band->pi, war); } int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel) { u16 chspec = ch20mhz_chspec(channel); if (channel < 0 || channel > MAXCHANNEL) return -EINVAL; if (!brcms_c_valid_chanspec_db(wlc->cmi, chspec)) return -EINVAL; if (!wlc->pub->up && brcms_is_mband_unlocked(wlc)) { if (wlc->band->bandunit != chspec_bandunit(chspec)) wlc->bandinit_pending = true; else wlc->bandinit_pending = false; } wlc->default_bss->chanspec = chspec; /* brcms_c_BSSinit() will sanitize the rateset before * using it.. */ if (wlc->pub->up && (wlc_phy_chanspec_get(wlc->band->pi) != chspec)) { brcms_c_set_home_chanspec(wlc, chspec); brcms_c_suspend_mac_and_wait(wlc); brcms_c_set_chanspec(wlc, chspec); brcms_c_enable_mac(wlc); } return 0; } int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl) { int ac; if (srl < 1 || srl > RETRY_SHORT_MAX || lrl < 1 || lrl > RETRY_SHORT_MAX) return -EINVAL; wlc->SRL = srl; wlc->LRL = lrl; brcms_b_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL); for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], EDCF_SHORT, wlc->SRL); wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], EDCF_LONG, wlc->LRL); } brcms_c_wme_retries_write(wlc); return 0; } void brcms_c_get_current_rateset(struct brcms_c_info *wlc, struct brcm_rateset *currs) { struct brcms_c_rateset *rs; if (wlc->pub->associated) rs = &wlc->bsscfg->current_bss->rateset; else rs = &wlc->default_bss->rateset; /* Copy only legacy rateset section */ currs->count = rs->count; memcpy(&currs->rates, &rs->rates, rs->count); } int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs) { struct brcms_c_rateset internal_rs; int bcmerror; if (rs->count > BRCMS_NUMRATES) return -ENOBUFS; memset(&internal_rs, 0, sizeof(struct brcms_c_rateset)); /* Copy only legacy rateset section */ internal_rs.count = rs->count; memcpy(&internal_rs.rates, &rs->rates, internal_rs.count); /* merge rateset coming in with the current mcsset */ if (wlc->pub->_n_enab & SUPPORT_11N) { struct brcms_bss_info *mcsset_bss; if (wlc->bsscfg->associated) mcsset_bss = wlc->bsscfg->current_bss; else mcsset_bss = wlc->default_bss; memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0], MCSSET_LEN); } bcmerror = brcms_c_set_internal_rateset(wlc, &internal_rs); if (!bcmerror) brcms_c_ofdm_rateset_war(wlc); return bcmerror; } int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period) { if (period < DOT11_MIN_BEACON_PERIOD || period > DOT11_MAX_BEACON_PERIOD) return -EINVAL; wlc->default_bss->beacon_period = period; return 0; } u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx) { return wlc->band->phytype; } void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override) { wlc->shortslot_override = sslot_override; /* * shortslot is an 11g feature, so no more work if we are * currently on the 5G band */ if (wlc->band->bandtype == BRCM_BAND_5G) return; if (wlc->pub->up && wlc->pub->associated) { /* let watchdog or beacon processing update shortslot */ } else if (wlc->pub->up) { /* unassociated shortslot is off */ brcms_c_switch_shortslot(wlc, false); } else { /* driver is down, so just update the brcms_c_info * value */ if (wlc->shortslot_override == BRCMS_SHORTSLOT_AUTO) wlc->shortslot = false; else wlc->shortslot = (wlc->shortslot_override == BRCMS_SHORTSLOT_ON); } } /* * register watchdog and down handlers. */ int brcms_c_module_register(struct brcms_pub *pub, const char *name, struct brcms_info *hdl, int (*d_fn)(void *handle)) { struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; int i; /* find an empty entry and just add, no duplication check! */ for (i = 0; i < BRCMS_MAXMODULES; i++) { if (wlc->modulecb[i].name[0] == '\0') { strncpy(wlc->modulecb[i].name, name, sizeof(wlc->modulecb[i].name) - 1); wlc->modulecb[i].hdl = hdl; wlc->modulecb[i].down_fn = d_fn; return 0; } } return -ENOSR; } /* unregister module callbacks */ int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, struct brcms_info *hdl) { struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; int i; if (wlc == NULL) return -ENODATA; for (i = 0; i < BRCMS_MAXMODULES; i++) { if (!strcmp(wlc->modulecb[i].name, name) && (wlc->modulecb[i].hdl == hdl)) { memset(&wlc->modulecb[i], 0, sizeof(struct modulecb)); return 0; } } /* table not found! */ return -ENODATA; } void brcms_c_print_txstatus(struct tx_status *txs) { pr_debug("\ntxpkt (MPDU) Complete\n"); pr_debug("FrameID: %04x TxStatus: %04x\n", txs->frameid, txs->status); pr_debug("[15:12] %d frame attempts\n", (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT); pr_debug(" [11:8] %d rts attempts\n", (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT); pr_debug(" [7] %d PM mode indicated\n", txs->status & TX_STATUS_PMINDCTD ? 1 : 0); pr_debug(" [6] %d intermediate status\n", txs->status & TX_STATUS_INTERMEDIATE ? 1 : 0); pr_debug(" [5] %d AMPDU\n", txs->status & TX_STATUS_AMPDU ? 1 : 0); pr_debug(" [4:2] %d Frame Suppressed Reason (%s)\n", (txs->status & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT, (const char *[]) { "None", "PMQ Entry", "Flush request", "Previous frag failure", "Channel mismatch", "Lifetime Expiry", "Underflow" } [(txs->status & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT]); pr_debug(" [1] %d acked\n", txs->status & TX_STATUS_ACK_RCV ? 1 : 0); pr_debug("LastTxTime: %04x Seq: %04x PHYTxStatus: %04x RxAckRSSI: %04x RxAckSQ: %04x\n", txs->lasttxtime, txs->sequence, txs->phyerr, (txs->ackphyrxsh & PRXS1_JSSI_MASK) >> PRXS1_JSSI_SHIFT, (txs->ackphyrxsh & PRXS1_SQ_MASK) >> PRXS1_SQ_SHIFT); } static bool brcms_c_chipmatch_pci(struct bcma_device *core) { struct pci_dev *pcidev = core->bus->host_pci; u16 vendor = pcidev->vendor; u16 device = pcidev->device; if (vendor != PCI_VENDOR_ID_BROADCOM) { pr_err("unknown vendor id %04x\n", vendor); return false; } if (device == BCM43224_D11N_ID_VEN1) return true; if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID)) return true; if (device == BCM4313_D11N2G_ID) return true; if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID)) return true; pr_err("unknown device id %04x\n", device); return false; } static bool brcms_c_chipmatch_soc(struct bcma_device *core) { struct bcma_chipinfo *chipinfo = &core->bus->chipinfo; if (chipinfo->id == BCMA_CHIP_ID_BCM4716) return true; pr_err("unknown chip id %04x\n", chipinfo->id); return false; } bool brcms_c_chipmatch(struct bcma_device *core) { switch (core->bus->hosttype) { case BCMA_HOSTTYPE_PCI: return brcms_c_chipmatch_pci(core); case BCMA_HOSTTYPE_SOC: return brcms_c_chipmatch_soc(core); default: pr_err("unknown host type: %i\n", core->bus->hosttype); return false; } } #if defined(DEBUG) void brcms_c_print_txdesc(struct d11txh *txh) { u16 mtcl = le16_to_cpu(txh->MacTxControlLow); u16 mtch = le16_to_cpu(txh->MacTxControlHigh); u16 mfc = le16_to_cpu(txh->MacFrameControl); u16 tfest = le16_to_cpu(txh->TxFesTimeNormal); u16 ptcw = le16_to_cpu(txh->PhyTxControlWord); u16 ptcw_1 = le16_to_cpu(txh->PhyTxControlWord_1); u16 ptcw_1_Fbr = le16_to_cpu(txh->PhyTxControlWord_1_Fbr); u16 ptcw_1_Rts = le16_to_cpu(txh->PhyTxControlWord_1_Rts); u16 ptcw_1_FbrRts = le16_to_cpu(txh->PhyTxControlWord_1_FbrRts); u16 mainrates = le16_to_cpu(txh->MainRates); u16 xtraft = le16_to_cpu(txh->XtraFrameTypes); u8 *iv = txh->IV; u8 *ra = txh->TxFrameRA; u16 tfestfb = le16_to_cpu(txh->TxFesTimeFallback); u8 *rtspfb = txh->RTSPLCPFallback; u16 rtsdfb = le16_to_cpu(txh->RTSDurFallback); u8 *fragpfb = txh->FragPLCPFallback; u16 fragdfb = le16_to_cpu(txh->FragDurFallback); u16 mmodelen = le16_to_cpu(txh->MModeLen); u16 mmodefbrlen = le16_to_cpu(txh->MModeFbrLen); u16 tfid = le16_to_cpu(txh->TxFrameID); u16 txs = le16_to_cpu(txh->TxStatus); u16 mnmpdu = le16_to_cpu(txh->MaxNMpdus); u16 mabyte = le16_to_cpu(txh->MaxABytes_MRT); u16 mabyte_f = le16_to_cpu(txh->MaxABytes_FBR); u16 mmbyte = le16_to_cpu(txh->MinMBytes); u8 *rtsph = txh->RTSPhyHeader; struct ieee80211_rts rts = txh->rts_frame; /* add plcp header along with txh descriptor */ brcmu_dbg_hex_dump(txh, sizeof(struct d11txh) + 48, "Raw TxDesc + plcp header:\n"); pr_debug("TxCtlLow: %04x ", mtcl); pr_debug("TxCtlHigh: %04x ", mtch); pr_debug("FC: %04x ", mfc); pr_debug("FES Time: %04x\n", tfest); pr_debug("PhyCtl: %04x%s ", ptcw, (ptcw & PHY_TXC_SHORT_HDR) ? " short" : ""); pr_debug("PhyCtl_1: %04x ", ptcw_1); pr_debug("PhyCtl_1_Fbr: %04x\n", ptcw_1_Fbr); pr_debug("PhyCtl_1_Rts: %04x ", ptcw_1_Rts); pr_debug("PhyCtl_1_Fbr_Rts: %04x\n", ptcw_1_FbrRts); pr_debug("MainRates: %04x ", mainrates); pr_debug("XtraFrameTypes: %04x ", xtraft); pr_debug("\n"); print_hex_dump_bytes("SecIV:", DUMP_PREFIX_OFFSET, iv, sizeof(txh->IV)); print_hex_dump_bytes("RA:", DUMP_PREFIX_OFFSET, ra, sizeof(txh->TxFrameRA)); pr_debug("Fb FES Time: %04x ", tfestfb); print_hex_dump_bytes("Fb RTS PLCP:", DUMP_PREFIX_OFFSET, rtspfb, sizeof(txh->RTSPLCPFallback)); pr_debug("RTS DUR: %04x ", rtsdfb); print_hex_dump_bytes("PLCP:", DUMP_PREFIX_OFFSET, fragpfb, sizeof(txh->FragPLCPFallback)); pr_debug("DUR: %04x", fragdfb); pr_debug("\n"); pr_debug("MModeLen: %04x ", mmodelen); pr_debug("MModeFbrLen: %04x\n", mmodefbrlen); pr_debug("FrameID: %04x\n", tfid); pr_debug("TxStatus: %04x\n", txs); pr_debug("MaxNumMpdu: %04x\n", mnmpdu); pr_debug("MaxAggbyte: %04x\n", mabyte); pr_debug("MaxAggbyte_fb: %04x\n", mabyte_f); pr_debug("MinByte: %04x\n", mmbyte); print_hex_dump_bytes("RTS PLCP:", DUMP_PREFIX_OFFSET, rtsph, sizeof(txh->RTSPhyHeader)); print_hex_dump_bytes("RTS Frame:", DUMP_PREFIX_OFFSET, (u8 *)&rts, sizeof(txh->rts_frame)); pr_debug("\n"); } #endif /* defined(DEBUG) */ #if defined(DEBUG) static int brcms_c_format_flags(const struct brcms_c_bit_desc *bd, u32 flags, char *buf, int len) { int i; char *p = buf; char hexstr[16]; int slen = 0, nlen = 0; u32 bit; const char *name; if (len < 2 || !buf) return 0; buf[0] = '\0'; for (i = 0; flags != 0; i++) { bit = bd[i].bit; name = bd[i].name; if (bit == 0 && flags != 0) { /* print any unnamed bits */ snprintf(hexstr, 16, "0x%X", flags); name = hexstr; flags = 0; /* exit loop */ } else if ((flags & bit) == 0) continue; flags &= ~bit; nlen = strlen(name); slen += nlen; /* count btwn flag space */ if (flags != 0) slen += 1; /* need NULL char as well */ if (len <= slen) break; /* copy NULL char but don't count it */ strncpy(p, name, nlen + 1); p += nlen; /* copy btwn flag space and NULL char */ if (flags != 0) p += snprintf(p, 2, " "); len -= slen; } /* indicate the str was too short */ if (flags != 0) { if (len < 2) p -= 2 - len; /* overwrite last char */ p += snprintf(p, 2, ">"); } return (int)(p - buf); } #endif /* defined(DEBUG) */ #if defined(DEBUG) void brcms_c_print_rxh(struct d11rxhdr *rxh) { u16 len = rxh->RxFrameSize; u16 phystatus_0 = rxh->PhyRxStatus_0; u16 phystatus_1 = rxh->PhyRxStatus_1; u16 phystatus_2 = rxh->PhyRxStatus_2; u16 phystatus_3 = rxh->PhyRxStatus_3; u16 macstatus1 = rxh->RxStatus1; u16 macstatus2 = rxh->RxStatus2; char flagstr[64]; char lenbuf[20]; static const struct brcms_c_bit_desc macstat_flags[] = { {RXS_FCSERR, "FCSErr"}, {RXS_RESPFRAMETX, "Reply"}, {RXS_PBPRES, "PADDING"}, {RXS_DECATMPT, "DeCr"}, {RXS_DECERR, "DeCrErr"}, {RXS_BCNSENT, "Bcn"}, {0, NULL} }; brcmu_dbg_hex_dump(rxh, sizeof(struct d11rxhdr), "Raw RxDesc:\n"); brcms_c_format_flags(macstat_flags, macstatus1, flagstr, 64); snprintf(lenbuf, sizeof(lenbuf), "0x%x", len); pr_debug("RxFrameSize: %6s (%d)%s\n", lenbuf, len, (rxh->PhyRxStatus_0 & PRXS0_SHORTH) ? " short preamble" : ""); pr_debug("RxPHYStatus: %04x %04x %04x %04x\n", phystatus_0, phystatus_1, phystatus_2, phystatus_3); pr_debug("RxMACStatus: %x %s\n", macstatus1, flagstr); pr_debug("RXMACaggtype: %x\n", (macstatus2 & RXS_AGGTYPE_MASK)); pr_debug("RxTSFTime: %04x\n", rxh->RxTSFTime); } #endif /* defined(DEBUG) */ u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate) { u16 table_ptr; u8 phy_rate, index; /* get the phy specific rate encoding for the PLCP SIGNAL field */ if (is_ofdm_rate(rate)) table_ptr = M_RT_DIRMAP_A; else table_ptr = M_RT_DIRMAP_B; /* for a given rate, the LS-nibble of the PLCP SIGNAL field is * the index into the rate table. */ phy_rate = rate_info[rate] & BRCMS_RATE_MASK; index = phy_rate & 0xf; /* Find the SHM pointer to the rate table entry by looking in the * Direct-map Table */ return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2)); } static bool brcms_c_prec_enq_head(struct brcms_c_info *wlc, struct pktq *q, struct sk_buff *pkt, int prec, bool head) { struct sk_buff *p; int eprec = -1; /* precedence to evict from */ /* Determine precedence from which to evict packet, if any */ if (pktq_pfull(q, prec)) eprec = prec; else if (pktq_full(q)) { p = brcmu_pktq_peek_tail(q, &eprec); if (eprec > prec) { wiphy_err(wlc->wiphy, "%s: Failing: eprec %d > prec %d" "\n", __func__, eprec, prec); return false; } } /* Evict if needed */ if (eprec >= 0) { bool discard_oldest; discard_oldest = ac_bitmap_tst(0, eprec); /* Refuse newer packet unless configured to discard oldest */ if (eprec == prec && !discard_oldest) { wiphy_err(wlc->wiphy, "%s: No where to go, prec == %d" "\n", __func__, prec); return false; } /* Evict packet according to discard policy */ p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : brcmu_pktq_pdeq_tail(q, eprec); brcmu_pkt_buf_free_skb(p); } /* Enqueue */ if (head) p = brcmu_pktq_penq_head(q, prec, pkt); else p = brcmu_pktq_penq(q, prec, pkt); return true; } /* * Attempts to queue a packet onto a multiple-precedence queue, * if necessary evicting a lower precedence packet from the queue. * * 'prec' is the precedence number that has already been mapped * from the packet priority. * * Returns true if packet consumed (queued), false if not. */ static bool brcms_c_prec_enq(struct brcms_c_info *wlc, struct pktq *q, struct sk_buff *pkt, int prec) { return brcms_c_prec_enq_head(wlc, q, pkt, prec, false); } void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb, struct sk_buff *sdu, uint prec) { struct brcms_txq_info *qi = wlc->pkt_queue; /* Check me */ struct pktq *q = &qi->q; int prio; prio = sdu->priority; if (!brcms_c_prec_enq(wlc, q, sdu, prec)) { /* * we might hit this condtion in case * packet flooding from mac80211 stack */ brcmu_pkt_buf_free_skb(sdu); } } /* * bcmc_fid_generate: * Generate frame ID for a BCMC packet. The frag field is not used * for MC frames so is used as part of the sequence number. */ static inline u16 bcmc_fid_generate(struct brcms_c_info *wlc, struct brcms_bss_cfg *bsscfg, struct d11txh *txh) { u16 frameid; frameid = le16_to_cpu(txh->TxFrameID) & ~(TXFID_SEQ_MASK | TXFID_QUEUE_MASK); frameid |= (((wlc-> mc_fid_counter++) << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | TX_BCMC_FIFO; return frameid; } static uint brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec, u8 preamble_type) { uint dur = 0; BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d\n", wlc->pub->unit, rspec, preamble_type); /* * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that * is less than or equal to the rate of the immediately previous * frame in the FES */ rspec = brcms_basic_rate(wlc, rspec); /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */ dur = brcms_c_calc_frame_time(wlc, rspec, preamble_type, (DOT11_ACK_LEN + FCS_LEN)); return dur; } static uint brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec, u8 preamble_type) { BCMMSG(wlc->wiphy, "wl%d: ratespec 0x%x, preamble_type %d\n", wlc->pub->unit, rspec, preamble_type); return brcms_c_calc_ack_time(wlc, rspec, preamble_type); } static uint brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec, u8 preamble_type) { BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, " "preamble_type %d\n", wlc->pub->unit, rspec, preamble_type); /* * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that * is less than or equal to the rate of the immediately previous * frame in the FES */ rspec = brcms_basic_rate(wlc, rspec); /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */ return brcms_c_calc_frame_time(wlc, rspec, preamble_type, (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN + FCS_LEN)); } /* brcms_c_compute_frame_dur() * * Calculate the 802.11 MAC header DUR field for MPDU * DUR for a single frame = 1 SIFS + 1 ACK * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time * * rate MPDU rate in unit of 500kbps * next_frag_len next MPDU length in bytes * preamble_type use short/GF or long/MM PLCP header */ static u16 brcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate, u8 preamble_type, uint next_frag_len) { u16 dur, sifs; sifs = get_sifs(wlc->band); dur = sifs; dur += (u16) brcms_c_calc_ack_time(wlc, rate, preamble_type); if (next_frag_len) { /* Double the current DUR to get 2 SIFS + 2 ACKs */ dur *= 2; /* add another SIFS and the frag time */ dur += sifs; dur += (u16) brcms_c_calc_frame_time(wlc, rate, preamble_type, next_frag_len); } return dur; } /* The opposite of brcms_c_calc_frame_time */ static uint brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec, u8 preamble_type, uint dur) { uint nsyms, mac_len, Ndps, kNdps; uint rate = rspec2rate(ratespec); BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, dur %d\n", wlc->pub->unit, ratespec, preamble_type, dur); if (is_mcs_rate(ratespec)) { uint mcs = ratespec & RSPEC_RATE_MASK; int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); /* payload calculation matches that of regular ofdm */ if (wlc->band->bandtype == BRCM_BAND_2G) dur -= DOT11_OFDM_SIGNAL_EXTENSION; /* kNdbps = kbps * 4 */ kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), rspec_issgi(ratespec)) * 4; nsyms = dur / APHY_SYMBOL_TIME; mac_len = ((nsyms * kNdps) - ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000; } else if (is_ofdm_rate(ratespec)) { dur -= APHY_PREAMBLE_TIME; dur -= APHY_SIGNAL_TIME; /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ Ndps = rate * 2; nsyms = dur / APHY_SYMBOL_TIME; mac_len = ((nsyms * Ndps) - (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8; } else { if (preamble_type & BRCMS_SHORT_PREAMBLE) dur -= BPHY_PLCP_SHORT_TIME; else dur -= BPHY_PLCP_TIME; mac_len = dur * rate; /* divide out factor of 2 in rate (1/2 mbps) */ mac_len = mac_len / 8 / 2; } return mac_len; } /* * Return true if the specified rate is supported by the specified band. * BRCM_BAND_AUTO indicates the current band. */ static bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band, bool verbose) { struct brcms_c_rateset *hw_rateset; uint i; if ((band == BRCM_BAND_AUTO) || (band == wlc->band->bandtype)) hw_rateset = &wlc->band->hw_rateset; else if (wlc->pub->_nbands > 1) hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset; else /* other band specified and we are a single band device */ return false; /* check if this is a mimo rate */ if (is_mcs_rate(rspec)) { if ((rspec & RSPEC_RATE_MASK) >= MCS_TABLE_SIZE) goto error; return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK)); } for (i = 0; i < hw_rateset->count; i++) if (hw_rateset->rates[i] == rspec2rate(rspec)) return true; error: if (verbose) wiphy_err(wlc->wiphy, "wl%d: valid_rate: rate spec 0x%x " "not in hw_rateset\n", wlc->pub->unit, rspec); return false; } static u32 mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, u32 int_val) { u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT; u8 rate = int_val & NRATE_RATE_MASK; u32 rspec; bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE); bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT); bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY) == NRATE_OVERRIDE_MCS_ONLY); int bcmerror = 0; if (!ismcs) return (u32) rate; /* validate the combination of rate/mcs/stf is allowed */ if ((wlc->pub->_n_enab & SUPPORT_11N) && ismcs) { /* mcs only allowed when nmode */ if (stf > PHY_TXC1_MODE_SDM) { wiphy_err(wlc->wiphy, "wl%d: %s: Invalid stf\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } /* mcs 32 is a special case, DUP mode 40 only */ if (rate == 32) { if (!CHSPEC_IS40(wlc->home_chanspec) || ((stf != PHY_TXC1_MODE_SISO) && (stf != PHY_TXC1_MODE_CDD))) { wiphy_err(wlc->wiphy, "wl%d: %s: Invalid mcs " "32\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } /* mcs > 7 must use stf SDM */ } else if (rate > HIGHEST_SINGLE_STREAM_MCS) { /* mcs > 7 must use stf SDM */ if (stf != PHY_TXC1_MODE_SDM) { BCMMSG(wlc->wiphy, "wl%d: enabling " "SDM mode for mcs %d\n", wlc->pub->unit, rate); stf = PHY_TXC1_MODE_SDM; } } else { /* * MCS 0-7 may use SISO, CDD, and for * phy_rev >= 3 STBC */ if ((stf > PHY_TXC1_MODE_STBC) || (!BRCMS_STBC_CAP_PHY(wlc) && (stf == PHY_TXC1_MODE_STBC))) { wiphy_err(wlc->wiphy, "wl%d: %s: Invalid STBC" "\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } } } else if (is_ofdm_rate(rate)) { if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) { wiphy_err(wlc->wiphy, "wl%d: %s: Invalid OFDM\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } } else if (is_cck_rate(rate)) { if ((cur_band->bandtype != BRCM_BAND_2G) || (stf != PHY_TXC1_MODE_SISO)) { wiphy_err(wlc->wiphy, "wl%d: %s: Invalid CCK\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } } else { wiphy_err(wlc->wiphy, "wl%d: %s: Unknown rate type\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } /* make sure multiple antennae are available for non-siso rates */ if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) { wiphy_err(wlc->wiphy, "wl%d: %s: SISO antenna but !SISO " "request\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } rspec = rate; if (ismcs) { rspec |= RSPEC_MIMORATE; /* For STBC populate the STC field of the ratespec */ if (stf == PHY_TXC1_MODE_STBC) { u8 stc; stc = 1; /* Nss for single stream is always 1 */ rspec |= (stc << RSPEC_STC_SHIFT); } } rspec |= (stf << RSPEC_STF_SHIFT); if (override_mcs_only) rspec |= RSPEC_OVERRIDE_MCS_ONLY; if (issgi) rspec |= RSPEC_SHORT_GI; if ((rate != 0) && !brcms_c_valid_rate(wlc, rspec, cur_band->bandtype, true)) return rate; return rspec; done: return rate; } /* * Compute PLCP, but only requires actual rate and length of pkt. * Rate is given in the driver standard multiple of 500 kbps. * le is set for 11 Mbps rate if necessary. * Broken out for PRQ. */ static void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500, uint length, u8 *plcp) { u16 usec = 0; u8 le = 0; switch (rate_500) { case BRCM_RATE_1M: usec = length << 3; break; case BRCM_RATE_2M: usec = length << 2; break; case BRCM_RATE_5M5: usec = (length << 4) / 11; if ((length << 4) - (usec * 11) > 0) usec++; break; case BRCM_RATE_11M: usec = (length << 3) / 11; if ((length << 3) - (usec * 11) > 0) { usec++; if ((usec * 11) - (length << 3) >= 8) le = D11B_PLCP_SIGNAL_LE; } break; default: wiphy_err(wlc->wiphy, "brcms_c_cck_plcp_set: unsupported rate %d\n", rate_500); rate_500 = BRCM_RATE_1M; usec = length << 3; break; } /* PLCP signal byte */ plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */ /* PLCP service byte */ plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED); /* PLCP length u16, little endian */ plcp[2] = usec & 0xff; plcp[3] = (usec >> 8) & 0xff; /* PLCP CRC16 */ plcp[4] = 0; plcp[5] = 0; } /* Rate: 802.11 rate code, length: PSDU length in octets */ static void brcms_c_compute_mimo_plcp(u32 rspec, uint length, u8 *plcp) { u8 mcs = (u8) (rspec & RSPEC_RATE_MASK); plcp[0] = mcs; if (rspec_is40mhz(rspec) || (mcs == 32)) plcp[0] |= MIMO_PLCP_40MHZ; BRCMS_SET_MIMO_PLCP_LEN(plcp, length); plcp[3] = rspec_mimoplcp3(rspec); /* rspec already holds this byte */ plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */ plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */ plcp[5] = 0; } /* Rate: 802.11 rate code, length: PSDU length in octets */ static void brcms_c_compute_ofdm_plcp(u32 rspec, u32 length, u8 *plcp) { u8 rate_signal; u32 tmp = 0; int rate = rspec2rate(rspec); /* * encode rate per 802.11a-1999 sec 17.3.4.1, with lsb * transmitted first */ rate_signal = rate_info[rate] & BRCMS_RATE_MASK; memset(plcp, 0, D11_PHY_HDR_LEN); D11A_PHY_HDR_SRATE((struct ofdm_phy_hdr *) plcp, rate_signal); tmp = (length & 0xfff) << 5; plcp[2] |= (tmp >> 16) & 0xff; plcp[1] |= (tmp >> 8) & 0xff; plcp[0] |= tmp & 0xff; } /* Rate: 802.11 rate code, length: PSDU length in octets */ static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rspec, uint length, u8 *plcp) { int rate = rspec2rate(rspec); brcms_c_cck_plcp_set(wlc, rate, length, plcp); } static void brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec, uint length, u8 *plcp) { if (is_mcs_rate(rspec)) brcms_c_compute_mimo_plcp(rspec, length, plcp); else if (is_ofdm_rate(rspec)) brcms_c_compute_ofdm_plcp(rspec, length, plcp); else brcms_c_compute_cck_plcp(wlc, rspec, length, plcp); } /* brcms_c_compute_rtscts_dur() * * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK * * cts cts-to-self or rts/cts * rts_rate rts or cts rate in unit of 500kbps * rate next MPDU rate in unit of 500kbps * frame_len next MPDU frame length in bytes */ u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, u32 rts_rate, u32 frame_rate, u8 rts_preamble_type, u8 frame_preamble_type, uint frame_len, bool ba) { u16 dur, sifs; sifs = get_sifs(wlc->band); if (!cts_only) { /* RTS/CTS */ dur = 3 * sifs; dur += (u16) brcms_c_calc_cts_time(wlc, rts_rate, rts_preamble_type); } else { /* CTS-TO-SELF */ dur = 2 * sifs; } dur += (u16) brcms_c_calc_frame_time(wlc, frame_rate, frame_preamble_type, frame_len); if (ba) dur += (u16) brcms_c_calc_ba_time(wlc, frame_rate, BRCMS_SHORT_PREAMBLE); else dur += (u16) brcms_c_calc_ack_time(wlc, frame_rate, frame_preamble_type); return dur; } static u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec) { u16 phyctl1 = 0; u16 bw; if (BRCMS_ISLCNPHY(wlc->band)) { bw = PHY_TXC1_BW_20MHZ; } else { bw = rspec_get_bw(rspec); /* 10Mhz is not supported yet */ if (bw < PHY_TXC1_BW_20MHZ) { wiphy_err(wlc->wiphy, "phytxctl1_calc: bw %d is " "not supported yet, set to 20L\n", bw); bw = PHY_TXC1_BW_20MHZ; } } if (is_mcs_rate(rspec)) { uint mcs = rspec & RSPEC_RATE_MASK; /* bw, stf, coding-type is part of rspec_phytxbyte2 returns */ phyctl1 = rspec_phytxbyte2(rspec); /* set the upper byte of phyctl1 */ phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8); } else if (is_cck_rate(rspec) && !BRCMS_ISLCNPHY(wlc->band) && !BRCMS_ISSSLPNPHY(wlc->band)) { /* * In CCK mode LPPHY overloads OFDM Modulation bits with CCK * Data Rate. Eventually MIMOPHY would also be converted to * this format */ /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */ phyctl1 = (bw | (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); } else { /* legacy OFDM/CCK */ s16 phycfg; /* get the phyctl byte from rate phycfg table */ phycfg = brcms_c_rate_legacy_phyctl(rspec2rate(rspec)); if (phycfg == -1) { wiphy_err(wlc->wiphy, "phytxctl1_calc: wrong " "legacy OFDM/CCK rate\n"); phycfg = 0; } /* set the upper byte of phyctl1 */ phyctl1 = (bw | (phycfg << 8) | (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); } return phyctl1; } /* * Add struct d11txh, struct cck_phy_hdr. * * 'p' data must start with 802.11 MAC header * 'p' must allow enough bytes of local headers to be "pushed" onto the packet * * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes) * */ static u16 brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, struct sk_buff *p, struct scb *scb, uint frag, uint nfrags, uint queue, uint next_frag_len) { struct ieee80211_hdr *h; struct d11txh *txh; u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN]; int len, phylen, rts_phylen; u16 mch, phyctl, xfts, mainrates; u16 seq = 0, mcl = 0, status = 0, frameid = 0; u32 rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; u32 rts_rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; bool use_rts = false; bool use_cts = false; bool use_rifs = false; bool short_preamble[2] = { false, false }; u8 preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; u8 rts_preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN]; struct ieee80211_rts *rts = NULL; bool qos; uint ac; bool hwtkmic = false; u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; #define ANTCFG_NONE 0xFF u8 antcfg = ANTCFG_NONE; u8 fbantcfg = ANTCFG_NONE; uint phyctl1_stf = 0; u16 durid = 0; struct ieee80211_tx_rate *txrate[2]; int k; struct ieee80211_tx_info *tx_info; bool is_mcs; u16 mimo_txbw; u8 mimo_preamble_type; /* locate 802.11 MAC header */ h = (struct ieee80211_hdr *)(p->data); qos = ieee80211_is_data_qos(h->frame_control); /* compute length of frame in bytes for use in PLCP computations */ len = p->len; phylen = len + FCS_LEN; /* Get tx_info */ tx_info = IEEE80211_SKB_CB(p); /* add PLCP */ plcp = skb_push(p, D11_PHY_HDR_LEN); /* add Broadcom tx descriptor header */ txh = (struct d11txh *) skb_push(p, D11_TXH_LEN); memset(txh, 0, D11_TXH_LEN); /* setup frameid */ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { /* non-AP STA should never use BCMC queue */ if (queue == TX_BCMC_FIFO) { wiphy_err(wlc->wiphy, "wl%d: %s: ASSERT queue == " "TX_BCMC!\n", wlc->pub->unit, __func__); frameid = bcmc_fid_generate(wlc, NULL, txh); } else { /* Increment the counter for first fragment */ if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) scb->seqnum[p->priority]++; /* extract fragment number from frame first */ seq = le16_to_cpu(h->seq_ctrl) & FRAGNUM_MASK; seq |= (scb->seqnum[p->priority] << SEQNUM_SHIFT); h->seq_ctrl = cpu_to_le16(seq); frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | (queue & TXFID_QUEUE_MASK); } } frameid |= queue & TXFID_QUEUE_MASK; /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */ if (ieee80211_is_beacon(h->frame_control)) mcl |= TXC_IGNOREPMQ; txrate[0] = tx_info->control.rates; txrate[1] = txrate[0] + 1; /* * if rate control algorithm didn't give us a fallback * rate, use the primary rate */ if (txrate[1]->idx < 0) txrate[1] = txrate[0]; for (k = 0; k < hw->max_rates; k++) { is_mcs = txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false; if (!is_mcs) { if ((txrate[k]->idx >= 0) && (txrate[k]->idx < hw->wiphy->bands[tx_info->band]->n_bitrates)) { rspec[k] = hw->wiphy->bands[tx_info->band]-> bitrates[txrate[k]->idx].hw_value; short_preamble[k] = txrate[k]-> flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ? true : false; } else { rspec[k] = BRCM_RATE_1M; } } else { rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, NRATE_MCS_INUSE | txrate[k]->idx); } /* * Currently only support same setting for primay and * fallback rates. Unify flags for each rate into a * single value for the frame */ use_rts |= txrate[k]-> flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false; use_cts |= txrate[k]-> flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false; /* * (1) RATE: * determine and validate primary rate * and fallback rates */ if (!rspec_active(rspec[k])) { rspec[k] = BRCM_RATE_1M; } else { if (!is_multicast_ether_addr(h->addr1)) { /* set tx antenna config */ brcms_c_antsel_antcfg_get(wlc->asi, false, false, 0, 0, &antcfg, &fbantcfg); } } } phyctl1_stf = wlc->stf->ss_opmode; if (wlc->pub->_n_enab & SUPPORT_11N) { for (k = 0; k < hw->max_rates; k++) { /* * apply siso/cdd to single stream mcs's or ofdm * if rspec is auto selected */ if (((is_mcs_rate(rspec[k]) && is_single_stream(rspec[k] & RSPEC_RATE_MASK)) || is_ofdm_rate(rspec[k])) && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY) || !(rspec[k] & RSPEC_OVERRIDE))) { rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK); /* For SISO MCS use STBC if possible */ if (is_mcs_rate(rspec[k]) && BRCMS_STF_SS_STBC_TX(wlc, scb)) { u8 stc; /* Nss for single stream is always 1 */ stc = 1; rspec[k] |= (PHY_TXC1_MODE_STBC << RSPEC_STF_SHIFT) | (stc << RSPEC_STC_SHIFT); } else rspec[k] |= (phyctl1_stf << RSPEC_STF_SHIFT); } /* * Is the phy configured to use 40MHZ frames? If * so then pick the desired txbw */ if (brcms_chspec_bw(wlc->chanspec) == BRCMS_40_MHZ) { /* default txbw is 20in40 SB */ mimo_ctlchbw = mimo_txbw = CHSPEC_SB_UPPER(wlc_phy_chanspec_get( wlc->band->pi)) ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ; if (is_mcs_rate(rspec[k])) { /* mcs 32 must be 40b/w DUP */ if ((rspec[k] & RSPEC_RATE_MASK) == 32) { mimo_txbw = PHY_TXC1_BW_40MHZ_DUP; /* use override */ } else if (wlc->mimo_40txbw != AUTO) mimo_txbw = wlc->mimo_40txbw; /* else check if dst is using 40 Mhz */ else if (scb->flags & SCB_IS40) mimo_txbw = PHY_TXC1_BW_40MHZ; } else if (is_ofdm_rate(rspec[k])) { if (wlc->ofdm_40txbw != AUTO) mimo_txbw = wlc->ofdm_40txbw; } else if (wlc->cck_40txbw != AUTO) { mimo_txbw = wlc->cck_40txbw; } } else { /* * mcs32 is 40 b/w only. * This is possible for probe packets on * a STA during SCAN */ if ((rspec[k] & RSPEC_RATE_MASK) == 32) /* mcs 0 */ rspec[k] = RSPEC_MIMORATE; mimo_txbw = PHY_TXC1_BW_20MHZ; } /* Set channel width */ rspec[k] &= ~RSPEC_BW_MASK; if ((k == 0) || ((k > 0) && is_mcs_rate(rspec[k]))) rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT); else rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT); /* Disable short GI, not supported yet */ rspec[k] &= ~RSPEC_SHORT_GI; mimo_preamble_type = BRCMS_MM_PREAMBLE; if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD) mimo_preamble_type = BRCMS_GF_PREAMBLE; if ((txrate[k]->flags & IEEE80211_TX_RC_MCS) && (!is_mcs_rate(rspec[k]))) { wiphy_err(wlc->wiphy, "wl%d: %s: IEEE80211_TX_" "RC_MCS != is_mcs_rate(rspec)\n", wlc->pub->unit, __func__); } if (is_mcs_rate(rspec[k])) { preamble_type[k] = mimo_preamble_type; /* * if SGI is selected, then forced mm * for single stream */ if ((rspec[k] & RSPEC_SHORT_GI) && is_single_stream(rspec[k] & RSPEC_RATE_MASK)) preamble_type[k] = BRCMS_MM_PREAMBLE; } /* should be better conditionalized */ if (!is_mcs_rate(rspec[0]) && (tx_info->control.rates[0]. flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)) preamble_type[k] = BRCMS_SHORT_PREAMBLE; } } else { for (k = 0; k < hw->max_rates; k++) { /* Set ctrlchbw as 20Mhz */ rspec[k] &= ~RSPEC_BW_MASK; rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT); /* for nphy, stf of ofdm frames must follow policies */ if (BRCMS_ISNPHY(wlc->band) && is_ofdm_rate(rspec[k])) { rspec[k] &= ~RSPEC_STF_MASK; rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT; } } } /* Reset these for use with AMPDU's */ txrate[0]->count = 0; txrate[1]->count = 0; /* (2) PROTECTION, may change rspec */ if ((ieee80211_is_data(h->frame_control) || ieee80211_is_mgmt(h->frame_control)) && (phylen > wlc->RTSThresh) && !is_multicast_ether_addr(h->addr1)) use_rts = true; /* (3) PLCP: determine PLCP header and MAC duration, * fill struct d11txh */ brcms_c_compute_plcp(wlc, rspec[0], phylen, plcp); brcms_c_compute_plcp(wlc, rspec[1], phylen, plcp_fallback); memcpy(&txh->FragPLCPFallback, plcp_fallback, sizeof(txh->FragPLCPFallback)); /* Length field now put in CCK FBR CRC field */ if (is_cck_rate(rspec[1])) { txh->FragPLCPFallback[4] = phylen & 0xff; txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8; } /* MIMO-RATE: need validation ?? */ mainrates = is_ofdm_rate(rspec[0]) ? D11A_PHY_HDR_GRATE((struct ofdm_phy_hdr *) plcp) : plcp[0]; /* DUR field for main rate */ if (!ieee80211_is_pspoll(h->frame_control) && !is_multicast_ether_addr(h->addr1) && !use_rifs) { durid = brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0], next_frag_len); h->duration_id = cpu_to_le16(durid); } else if (use_rifs) { /* NAV protect to end of next max packet size */ durid = (u16) brcms_c_calc_frame_time(wlc, rspec[0], preamble_type[0], DOT11_MAX_FRAG_LEN); durid += RIFS_11N_TIME; h->duration_id = cpu_to_le16(durid); } /* DUR field for fallback rate */ if (ieee80211_is_pspoll(h->frame_control)) txh->FragDurFallback = h->duration_id; else if (is_multicast_ether_addr(h->addr1) || use_rifs) txh->FragDurFallback = 0; else { durid = brcms_c_compute_frame_dur(wlc, rspec[1], preamble_type[1], next_frag_len); txh->FragDurFallback = cpu_to_le16(durid); } /* (4) MAC-HDR: MacTxControlLow */ if (frag == 0) mcl |= TXC_STARTMSDU; if (!is_multicast_ether_addr(h->addr1)) mcl |= TXC_IMMEDACK; if (wlc->band->bandtype == BRCM_BAND_5G) mcl |= TXC_FREQBAND_5G; if (CHSPEC_IS40(wlc_phy_chanspec_get(wlc->band->pi))) mcl |= TXC_BW_40; /* set AMIC bit if using hardware TKIP MIC */ if (hwtkmic) mcl |= TXC_AMIC; txh->MacTxControlLow = cpu_to_le16(mcl); /* MacTxControlHigh */ mch = 0; /* Set fallback rate preamble type */ if ((preamble_type[1] == BRCMS_SHORT_PREAMBLE) || (preamble_type[1] == BRCMS_GF_PREAMBLE)) { if (rspec2rate(rspec[1]) != BRCM_RATE_1M) mch |= TXC_PREAMBLE_DATA_FB_SHORT; } /* MacFrameControl */ memcpy(&txh->MacFrameControl, &h->frame_control, sizeof(u16)); txh->TxFesTimeNormal = cpu_to_le16(0); txh->TxFesTimeFallback = cpu_to_le16(0); /* TxFrameRA */ memcpy(&txh->TxFrameRA, &h->addr1, ETH_ALEN); /* TxFrameID */ txh->TxFrameID = cpu_to_le16(frameid); /* * TxStatus, Note the case of recreating the first frag of a suppressed * frame then we may need to reset the retry cnt's via the status reg */ txh->TxStatus = cpu_to_le16(status); /* * extra fields for ucode AMPDU aggregation, the new fields are added to * the END of previous structure so that it's compatible in driver. */ txh->MaxNMpdus = cpu_to_le16(0); txh->MaxABytes_MRT = cpu_to_le16(0); txh->MaxABytes_FBR = cpu_to_le16(0); txh->MinMBytes = cpu_to_le16(0); /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration, * furnish struct d11txh */ /* RTS PLCP header and RTS frame */ if (use_rts || use_cts) { if (use_rts && use_cts) use_cts = false; for (k = 0; k < 2; k++) { rts_rspec[k] = brcms_c_rspec_to_rts_rspec(wlc, rspec[k], false, mimo_ctlchbw); } if (!is_ofdm_rate(rts_rspec[0]) && !((rspec2rate(rts_rspec[0]) == BRCM_RATE_1M) || (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { rts_preamble_type[0] = BRCMS_SHORT_PREAMBLE; mch |= TXC_PREAMBLE_RTS_MAIN_SHORT; } if (!is_ofdm_rate(rts_rspec[1]) && !((rspec2rate(rts_rspec[1]) == BRCM_RATE_1M) || (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { rts_preamble_type[1] = BRCMS_SHORT_PREAMBLE; mch |= TXC_PREAMBLE_RTS_FB_SHORT; } /* RTS/CTS additions to MacTxControlLow */ if (use_cts) { txh->MacTxControlLow |= cpu_to_le16(TXC_SENDCTS); } else { txh->MacTxControlLow |= cpu_to_le16(TXC_SENDRTS); txh->MacTxControlLow |= cpu_to_le16(TXC_LONGFRAME); } /* RTS PLCP header */ rts_plcp = txh->RTSPhyHeader; if (use_cts) rts_phylen = DOT11_CTS_LEN + FCS_LEN; else rts_phylen = DOT11_RTS_LEN + FCS_LEN; brcms_c_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp); /* fallback rate version of RTS PLCP header */ brcms_c_compute_plcp(wlc, rts_rspec[1], rts_phylen, rts_plcp_fallback); memcpy(&txh->RTSPLCPFallback, rts_plcp_fallback, sizeof(txh->RTSPLCPFallback)); /* RTS frame fields... */ rts = (struct ieee80211_rts *)&txh->rts_frame; durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[0], rspec[0], rts_preamble_type[0], preamble_type[0], phylen, false); rts->duration = cpu_to_le16(durid); /* fallback rate version of RTS DUR field */ durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[1], rspec[1], rts_preamble_type[1], preamble_type[1], phylen, false); txh->RTSDurFallback = cpu_to_le16(durid); if (use_cts) { rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); memcpy(&rts->ra, &h->addr2, ETH_ALEN); } else { rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); memcpy(&rts->ra, &h->addr1, 2 * ETH_ALEN); } /* mainrate * low 8 bits: main frag rate/mcs, * high 8 bits: rts/cts rate/mcs */ mainrates |= (is_ofdm_rate(rts_rspec[0]) ? D11A_PHY_HDR_GRATE( (struct ofdm_phy_hdr *) rts_plcp) : rts_plcp[0]) << 8; } else { memset((char *)txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN); memset((char *)&txh->rts_frame, 0, sizeof(struct ieee80211_rts)); memset((char *)txh->RTSPLCPFallback, 0, sizeof(txh->RTSPLCPFallback)); txh->RTSDurFallback = 0; } #ifdef SUPPORT_40MHZ /* add null delimiter count */ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && is_mcs_rate(rspec)) txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = brcm_c_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen); #endif /* * Now that RTS/RTS FB preamble types are updated, write * the final value */ txh->MacTxControlHigh = cpu_to_le16(mch); /* * MainRates (both the rts and frag plcp rates have * been calculated now) */ txh->MainRates = cpu_to_le16(mainrates); /* XtraFrameTypes */ xfts = frametype(rspec[1], wlc->mimoft); xfts |= (frametype(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT); xfts |= (frametype(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT); xfts |= CHSPEC_CHANNEL(wlc_phy_chanspec_get(wlc->band->pi)) << XFTS_CHANNEL_SHIFT; txh->XtraFrameTypes = cpu_to_le16(xfts); /* PhyTxControlWord */ phyctl = frametype(rspec[0], wlc->mimoft); if ((preamble_type[0] == BRCMS_SHORT_PREAMBLE) || (preamble_type[0] == BRCMS_GF_PREAMBLE)) { if (rspec2rate(rspec[0]) != BRCM_RATE_1M) phyctl |= PHY_TXC_SHORT_HDR; } /* phytxant is properly bit shifted */ phyctl |= brcms_c_stf_d11hdrs_phyctl_txant(wlc, rspec[0]); txh->PhyTxControlWord = cpu_to_le16(phyctl); /* PhyTxControlWord_1 */ if (BRCMS_PHY_11N_CAP(wlc->band)) { u16 phyctl1 = 0; phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[0]); txh->PhyTxControlWord_1 = cpu_to_le16(phyctl1); phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[1]); txh->PhyTxControlWord_1_Fbr = cpu_to_le16(phyctl1); if (use_rts || use_cts) { phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[0]); txh->PhyTxControlWord_1_Rts = cpu_to_le16(phyctl1); phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[1]); txh->PhyTxControlWord_1_FbrRts = cpu_to_le16(phyctl1); } /* * For mcs frames, if mixedmode(overloaded with long preamble) * is going to be set, fill in non-zero MModeLen and/or * MModeFbrLen it will be unnecessary if they are separated */ if (is_mcs_rate(rspec[0]) && (preamble_type[0] == BRCMS_MM_PREAMBLE)) { u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec[0], phylen); txh->MModeLen = cpu_to_le16(mmodelen); } if (is_mcs_rate(rspec[1]) && (preamble_type[1] == BRCMS_MM_PREAMBLE)) { u16 mmodefbrlen = brcms_c_calc_lsig_len(wlc, rspec[1], phylen); txh->MModeFbrLen = cpu_to_le16(mmodefbrlen); } } ac = skb_get_queue_mapping(p); if ((scb->flags & SCB_WMECAP) && qos && wlc->edcf_txop[ac]) { uint frag_dur, dur, dur_fallback; /* WME: Update TXOP threshold */ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) && frag == 0) { frag_dur = brcms_c_calc_frame_time(wlc, rspec[0], preamble_type[0], phylen); if (rts) { /* 1 RTS or CTS-to-self frame */ dur = brcms_c_calc_cts_time(wlc, rts_rspec[0], rts_preamble_type[0]); dur_fallback = brcms_c_calc_cts_time(wlc, rts_rspec[1], rts_preamble_type[1]); /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */ dur += le16_to_cpu(rts->duration); dur_fallback += le16_to_cpu(txh->RTSDurFallback); } else if (use_rifs) { dur = frag_dur; dur_fallback = 0; } else { /* frame + SIFS + ACK */ dur = frag_dur; dur += brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0], 0); dur_fallback = brcms_c_calc_frame_time(wlc, rspec[1], preamble_type[1], phylen); dur_fallback += brcms_c_compute_frame_dur(wlc, rspec[1], preamble_type[1], 0); } /* NEED to set TxFesTimeNormal (hard) */ txh->TxFesTimeNormal = cpu_to_le16((u16) dur); /* * NEED to set fallback rate version of * TxFesTimeNormal (hard) */ txh->TxFesTimeFallback = cpu_to_le16((u16) dur_fallback); /* * update txop byte threshold (txop minus intraframe * overhead) */ if (wlc->edcf_txop[ac] >= (dur - frag_dur)) { uint newfragthresh; newfragthresh = brcms_c_calc_frame_len(wlc, rspec[0], preamble_type[0], (wlc->edcf_txop[ac] - (dur - frag_dur))); /* range bound the fragthreshold */ if (newfragthresh < DOT11_MIN_FRAG_LEN) newfragthresh = DOT11_MIN_FRAG_LEN; else if (newfragthresh > wlc->usr_fragthresh) newfragthresh = wlc->usr_fragthresh; /* update the fragthresh and do txc update */ if (wlc->fragthresh[queue] != (u16) newfragthresh) wlc->fragthresh[queue] = (u16) newfragthresh; } else { wiphy_err(wlc->wiphy, "wl%d: %s txop invalid " "for rate %d\n", wlc->pub->unit, fifo_names[queue], rspec2rate(rspec[0])); } if (dur > wlc->edcf_txop[ac]) wiphy_err(wlc->wiphy, "wl%d: %s: %s txop " "exceeded phylen %d/%d dur %d/%d\n", wlc->pub->unit, __func__, fifo_names[queue], phylen, wlc->fragthresh[queue], dur, wlc->edcf_txop[ac]); } } return 0; } void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, struct ieee80211_hw *hw) { u8 prio; uint fifo; struct scb *scb = &wlc->pri_scb; struct ieee80211_hdr *d11_header = (struct ieee80211_hdr *)(sdu->data); /* * 802.11 standard requires management traffic * to go at highest priority */ prio = ieee80211_is_data(d11_header->frame_control) ? sdu->priority : MAXPRIO; fifo = prio2fifo[prio]; if (brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0)) return; brcms_c_txq_enq(wlc, scb, sdu, BRCMS_PRIO_TO_PREC(prio)); brcms_c_send_q(wlc); } void brcms_c_send_q(struct brcms_c_info *wlc) { struct sk_buff *pkt[DOT11_MAXNUMFRAGS]; int prec; u16 prec_map; int err = 0, i, count; uint fifo; struct brcms_txq_info *qi = wlc->pkt_queue; struct pktq *q = &qi->q; struct ieee80211_tx_info *tx_info; prec_map = wlc->tx_prec_map; /* Send all the enq'd pkts that we can. * Dequeue packets with precedence with empty HW fifo only */ while (prec_map && (pkt[0] = brcmu_pktq_mdeq(q, prec_map, &prec))) { tx_info = IEEE80211_SKB_CB(pkt[0]); if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { err = brcms_c_sendampdu(wlc->ampdu, qi, pkt, prec); } else { count = 1; err = brcms_c_prep_pdu(wlc, pkt[0], &fifo); if (!err) { for (i = 0; i < count; i++) brcms_c_txfifo(wlc, fifo, pkt[i], true, 1); } } if (err == -EBUSY) { brcmu_pktq_penq_head(q, prec, pkt[0]); /* * If send failed due to any other reason than a * change in HW FIFO condition, quit. Otherwise, * read the new prec_map! */ if (prec_map == wlc->tx_prec_map) break; prec_map = wlc->tx_prec_map; } } } void brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p, bool commit, s8 txpktpend) { u16 frameid = INVALIDFID; struct d11txh *txh; txh = (struct d11txh *) (p->data); /* When a BC/MC frame is being committed to the BCMC fifo * via DMA (NOT PIO), update ucode or BSS info as appropriate. */ if (fifo == TX_BCMC_FIFO) frameid = le16_to_cpu(txh->TxFrameID); /* * Bump up pending count for if not using rpc. If rpc is * used, this will be handled in brcms_b_txfifo() */ if (commit) { wlc->core->txpktpend[fifo] += txpktpend; BCMMSG(wlc->wiphy, "pktpend inc %d to %d\n", txpktpend, wlc->core->txpktpend[fifo]); } /* Commit BCMC sequence number in the SHM frame ID location */ if (frameid != INVALIDFID) { /* * To inform the ucode of the last mcast frame posted * so that it can clear moredata bit */ brcms_b_write_shm(wlc->hw, M_BCMC_FID, frameid); } if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0) wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n"); } u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, bool use_rspec, u16 mimo_ctlchbw) { u32 rts_rspec = 0; if (use_rspec) /* use frame rate as rts rate */ rts_rspec = rspec; else if (wlc->band->gmode && wlc->protection->_g && !is_cck_rate(rspec)) /* Use 11Mbps as the g protection RTS target rate and fallback. * Use the brcms_basic_rate() lookup to find the best basic rate * under the target in case 11 Mbps is not Basic. * 6 and 9 Mbps are not usually selected by rate selection, but * even if the OFDM rate we are protecting is 6 or 9 Mbps, 11 * is more robust. */ rts_rspec = brcms_basic_rate(wlc, BRCM_RATE_11M); else /* calculate RTS rate and fallback rate based on the frame rate * RTS must be sent at a basic rate since it is a * control frame, sec 9.6 of 802.11 spec */ rts_rspec = brcms_basic_rate(wlc, rspec); if (BRCMS_PHY_11N_CAP(wlc->band)) { /* set rts txbw to correct side band */ rts_rspec &= ~RSPEC_BW_MASK; /* * if rspec/rspec_fallback is 40MHz, then send RTS on both * 20MHz channel (DUP), otherwise send RTS on control channel */ if (rspec_is40mhz(rspec) && !is_cck_rate(rts_rspec)) rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT); else rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT); /* pick siso/cdd as default for ofdm */ if (is_ofdm_rate(rts_rspec)) { rts_rspec &= ~RSPEC_STF_MASK; rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT); } } return rts_rspec; } void brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo, s8 txpktpend) { wlc->core->txpktpend[fifo] -= txpktpend; BCMMSG(wlc->wiphy, "pktpend dec %d to %d\n", txpktpend, wlc->core->txpktpend[fifo]); /* There is more room; mark precedences related to this FIFO sendable */ wlc->tx_prec_map |= wlc->fifo2prec_map[fifo]; /* figure out which bsscfg is being worked on... */ } /* Update beacon listen interval in shared memory */ static void brcms_c_bcn_li_upd(struct brcms_c_info *wlc) { /* wake up every DTIM is the default */ if (wlc->bcn_li_dtim == 1) brcms_b_write_shm(wlc->hw, M_BCN_LI, 0); else brcms_b_write_shm(wlc->hw, M_BCN_LI, (wlc->bcn_li_dtim << 8) | wlc->bcn_li_bcn); } static void brcms_b_read_tsf(struct brcms_hardware *wlc_hw, u32 *tsf_l_ptr, u32 *tsf_h_ptr) { struct bcma_device *core = wlc_hw->d11core; /* read the tsf timer low, then high to get an atomic read */ *tsf_l_ptr = bcma_read32(core, D11REGOFFS(tsf_timerlow)); *tsf_h_ptr = bcma_read32(core, D11REGOFFS(tsf_timerhigh)); } /* * recover 64bit TSF value from the 16bit TSF value in the rx header * given the assumption that the TSF passed in header is within 65ms * of the current tsf. * * 6 5 4 4 3 2 1 * 3.......6.......8.......0.......2.......4.......6.......8......0 * |<---------- tsf_h ----------->||<--- tsf_l -->||<-RxTSFTime ->| * * The RxTSFTime are the lowest 16 bits and provided by the ucode. The * tsf_l is filled in by brcms_b_recv, which is done earlier in the * receive call sequence after rx interrupt. Only the higher 16 bits * are used. Finally, the tsf_h is read from the tsf register. */ static u64 brcms_c_recover_tsf64(struct brcms_c_info *wlc, struct d11rxhdr *rxh) { u32 tsf_h, tsf_l; u16 rx_tsf_0_15, rx_tsf_16_31; brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h); rx_tsf_16_31 = (u16)(tsf_l >> 16); rx_tsf_0_15 = rxh->RxTSFTime; /* * a greater tsf time indicates the low 16 bits of * tsf_l wrapped, so decrement the high 16 bits. */ if ((u16)tsf_l < rx_tsf_0_15) { rx_tsf_16_31 -= 1; if (rx_tsf_16_31 == 0xffff) tsf_h -= 1; } return ((u64)tsf_h << 32) | (((u32)rx_tsf_16_31 << 16) + rx_tsf_0_15); } static void prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, struct sk_buff *p, struct ieee80211_rx_status *rx_status) { int preamble; int channel; u32 rspec; unsigned char *plcp; /* fill in TSF and flag its presence */ rx_status->mactime = brcms_c_recover_tsf64(wlc, rxh); rx_status->flag |= RX_FLAG_MACTIME_MPDU; channel = BRCMS_CHAN_CHANNEL(rxh->RxChan); rx_status->band = channel > 14 ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; rx_status->freq = ieee80211_channel_to_frequency(channel, rx_status->band); rx_status->signal = wlc_phy_rssi_compute(wlc->hw->band->pi, rxh); /* noise */ /* qual */ rx_status->antenna = (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; plcp = p->data; rspec = brcms_c_compute_rspec(rxh, plcp); if (is_mcs_rate(rspec)) { rx_status->rate_idx = rspec & RSPEC_RATE_MASK; rx_status->flag |= RX_FLAG_HT; if (rspec_is40mhz(rspec)) rx_status->flag |= RX_FLAG_40MHZ; } else { switch (rspec2rate(rspec)) { case BRCM_RATE_1M: rx_status->rate_idx = 0; break; case BRCM_RATE_2M: rx_status->rate_idx = 1; break; case BRCM_RATE_5M5: rx_status->rate_idx = 2; break; case BRCM_RATE_11M: rx_status->rate_idx = 3; break; case BRCM_RATE_6M: rx_status->rate_idx = 4; break; case BRCM_RATE_9M: rx_status->rate_idx = 5; break; case BRCM_RATE_12M: rx_status->rate_idx = 6; break; case BRCM_RATE_18M: rx_status->rate_idx = 7; break; case BRCM_RATE_24M: rx_status->rate_idx = 8; break; case BRCM_RATE_36M: rx_status->rate_idx = 9; break; case BRCM_RATE_48M: rx_status->rate_idx = 10; break; case BRCM_RATE_54M: rx_status->rate_idx = 11; break; default: wiphy_err(wlc->wiphy, "%s: Unknown rate\n", __func__); } /* * For 5GHz, we should decrease the index as it is * a subset of the 2.4G rates. See bitrates field * of brcms_band_5GHz_nphy (in mac80211_if.c). */ if (rx_status->band == IEEE80211_BAND_5GHZ) rx_status->rate_idx -= BRCMS_LEGACY_5G_RATE_OFFSET; /* Determine short preamble and rate_idx */ preamble = 0; if (is_cck_rate(rspec)) { if (rxh->PhyRxStatus_0 & PRXS0_SHORTH) rx_status->flag |= RX_FLAG_SHORTPRE; } else if (is_ofdm_rate(rspec)) { rx_status->flag |= RX_FLAG_SHORTPRE; } else { wiphy_err(wlc->wiphy, "%s: Unknown modulation\n", __func__); } } if (plcp3_issgi(plcp[3])) rx_status->flag |= RX_FLAG_SHORT_GI; if (rxh->RxStatus1 & RXS_DECERR) { rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; wiphy_err(wlc->wiphy, "%s: RX_FLAG_FAILED_PLCP_CRC\n", __func__); } if (rxh->RxStatus1 & RXS_FCSERR) { rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; wiphy_err(wlc->wiphy, "%s: RX_FLAG_FAILED_FCS_CRC\n", __func__); } } static void brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, struct sk_buff *p) { int len_mpdu; struct ieee80211_rx_status rx_status; struct ieee80211_hdr *hdr; memset(&rx_status, 0, sizeof(rx_status)); prep_mac80211_status(wlc, rxh, p, &rx_status); /* mac header+body length, exclude CRC and plcp header */ len_mpdu = p->len - D11_PHY_HDR_LEN - FCS_LEN; skb_pull(p, D11_PHY_HDR_LEN); __skb_trim(p, len_mpdu); /* unmute transmit */ if (wlc->hw->suspended_fifos) { hdr = (struct ieee80211_hdr *)p->data; if (ieee80211_is_beacon(hdr->frame_control)) brcms_b_mute(wlc->hw, false); } memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p); } /* calculate frame duration for Mixed-mode L-SIG spoofing, return * number of bytes goes in the length field * * Formula given by HT PHY Spec v 1.13 * len = 3(nsyms + nstream + 3) - 3 */ u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len) { uint nsyms, len = 0, kNdps; BCMMSG(wlc->wiphy, "wl%d: rate %d, len%d\n", wlc->pub->unit, rspec2rate(ratespec), mac_len); if (is_mcs_rate(ratespec)) { uint mcs = ratespec & RSPEC_RATE_MASK; int tot_streams = (mcs_2_txstreams(mcs) + 1) + rspec_stc(ratespec); /* * the payload duration calculation matches that * of regular ofdm */ /* 1000Ndbps = kbps * 4 */ kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), rspec_issgi(ratespec)) * 4; if (rspec_stc(ratespec) == 0) nsyms = CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS) * 1000, kNdps); else /* STBC needs to have even number of symbols */ nsyms = 2 * CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS) * 1000, 2 * kNdps); /* (+3) account for HT-SIG(2) and HT-STF(1) */ nsyms += (tot_streams + 3); /* * 3 bytes/symbol @ legacy 6Mbps rate * (-3) excluding service bits and tail bits */ len = (3 * nsyms) - 3; } return (u16) len; } static void brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len) { const struct brcms_c_rateset *rs_dflt; struct brcms_c_rateset rs; u8 rate; u16 entry_ptr; u8 plcp[D11_PHY_HDR_LEN]; u16 dur, sifs; uint i; sifs = get_sifs(wlc->band); rs_dflt = brcms_c_rateset_get_hwrs(wlc); brcms_c_rateset_copy(rs_dflt, &rs); brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); /* * walk the phy rate table and update MAC core SHM * basic rate table entries */ for (i = 0; i < rs.count; i++) { rate = rs.rates[i] & BRCMS_RATE_MASK; entry_ptr = brcms_b_rate_shm_offset(wlc->hw, rate); /* Calculate the Probe Response PLCP for the given rate */ brcms_c_compute_plcp(wlc, rate, frame_len, plcp); /* * Calculate the duration of the Probe Response * frame plus SIFS for the MAC */ dur = (u16) brcms_c_calc_frame_time(wlc, rate, BRCMS_LONG_PREAMBLE, frame_len); dur += sifs; /* Update the SHM Rate Table entry Probe Response values */ brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS, (u16) (plcp[0] + (plcp[1] << 8))); brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS + 2, (u16) (plcp[2] + (plcp[3] << 8))); brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_DUR_POS, dur); } } /* Max buffering needed for beacon template/prb resp template is 142 bytes. * * PLCP header is 6 bytes. * 802.11 A3 header is 24 bytes. * Max beacon frame body template length is 112 bytes. * Max probe resp frame body template length is 110 bytes. * * *len on input contains the max length of the packet available. * * The *len value is set to the number of bytes in buf used, and starts * with the PLCP and included up to, but not including, the 4 byte FCS. */ static void brcms_c_bcn_prb_template(struct brcms_c_info *wlc, u16 type, u32 bcn_rspec, struct brcms_bss_cfg *cfg, u16 *buf, int *len) { static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255}; struct cck_phy_hdr *plcp; struct ieee80211_mgmt *h; int hdr_len, body_len; hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN; /* calc buffer size provided for frame body */ body_len = *len - hdr_len; /* return actual size */ *len = hdr_len + body_len; /* format PHY and MAC headers */ memset((char *)buf, 0, hdr_len); plcp = (struct cck_phy_hdr *) buf; /* * PLCP for Probe Response frames are filled in from * core's rate table */ if (type == IEEE80211_STYPE_BEACON) /* fill in PLCP */ brcms_c_compute_plcp(wlc, bcn_rspec, (DOT11_MAC_HDR_LEN + body_len + FCS_LEN), (u8 *) plcp); /* "Regular" and 16 MBSS but not for 4 MBSS */ /* Update the phytxctl for the beacon based on the rspec */ brcms_c_beacon_phytxctl_txant_upd(wlc, bcn_rspec); h = (struct ieee80211_mgmt *)&plcp[1]; /* fill in 802.11 header */ h->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | type); /* DUR is 0 for multicast bcn, or filled in by MAC for prb resp */ /* A1 filled in by MAC for prb resp, broadcast for bcn */ if (type == IEEE80211_STYPE_BEACON) memcpy(&h->da, ðer_bcast, ETH_ALEN); memcpy(&h->sa, &cfg->cur_etheraddr, ETH_ALEN); memcpy(&h->bssid, &cfg->BSSID, ETH_ALEN); /* SEQ filled in by MAC */ } int brcms_c_get_header_len(void) { return TXOFF; } /* * Update all beacons for the system. */ void brcms_c_update_beacon(struct brcms_c_info *wlc) { struct brcms_bss_cfg *bsscfg = wlc->bsscfg; if (bsscfg->up && !bsscfg->BSS) /* Clear the soft intmask */ wlc->defmacintmask &= ~MI_BCNTPL; } /* Write ssid into shared memory */ static void brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg) { u8 *ssidptr = cfg->SSID; u16 base = M_SSID; u8 ssidbuf[IEEE80211_MAX_SSID_LEN]; /* padding the ssid with zero and copy it into shm */ memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN); memcpy(ssidbuf, ssidptr, cfg->SSID_len); brcms_c_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN); brcms_b_write_shm(wlc->hw, M_SSIDLEN, (u16) cfg->SSID_len); } static void brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg, bool suspend) { u16 prb_resp[BCN_TMPL_LEN / 2]; int len = BCN_TMPL_LEN; /* * write the probe response to hardware, or save in * the config structure */ /* create the probe response template */ brcms_c_bcn_prb_template(wlc, IEEE80211_STYPE_PROBE_RESP, 0, cfg, prb_resp, &len); if (suspend) brcms_c_suspend_mac_and_wait(wlc); /* write the probe response into the template region */ brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE, (len + 3) & ~3, prb_resp); /* write the length of the probe response frame (+PLCP/-FCS) */ brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len); /* write the SSID and SSID length */ brcms_c_shm_ssid_upd(wlc, cfg); /* * Write PLCP headers and durations for probe response frames * at all rates. Use the actual frame length covered by the * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table() * by subtracting the PLCP len and adding the FCS. */ len += (-D11_PHY_HDR_LEN + FCS_LEN); brcms_c_mod_prb_rsp_rate_table(wlc, (u16) len); if (suspend) brcms_c_enable_mac(wlc); } void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend) { struct brcms_bss_cfg *bsscfg = wlc->bsscfg; /* update AP or IBSS probe responses */ if (bsscfg->up && !bsscfg->BSS) brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend); } /* prepares pdu for transmission. returns BCM error codes */ int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, uint *fifop) { uint fifo; struct d11txh *txh; struct ieee80211_hdr *h; struct scb *scb; txh = (struct d11txh *) (pdu->data); h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); /* get the pkt queue info. This was put at brcms_c_sendctl or * brcms_c_send for PDU */ fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; scb = NULL; *fifop = fifo; /* return if insufficient dma resources */ if (*wlc->core->txavail[fifo] < MAX_DMA_SEGS) { /* Mark precedences related to this FIFO, unsendable */ /* A fifo is full. Clear precedences related to that FIFO */ wlc->tx_prec_map &= ~(wlc->fifo2prec_map[fifo]); return -EBUSY; } return 0; } int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, uint *blocks) { if (fifo >= NFIFO) return -EINVAL; *blocks = wlc_hw->xmtfifo_sz[fifo]; return 0; } void brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, const u8 *addr) { brcms_b_set_addrmatch(wlc->hw, match_reg_offset, addr); if (match_reg_offset == RCM_BSSID_OFFSET) memcpy(wlc->bsscfg->BSSID, addr, ETH_ALEN); } /* * Flag 'scan in progress' to withhold dynamic phy calibration */ void brcms_c_scan_start(struct brcms_c_info *wlc) { wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, true); } void brcms_c_scan_stop(struct brcms_c_info *wlc) { wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, false); } void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state) { wlc->pub->associated = state; wlc->bsscfg->associated = state; } /* * When a remote STA/AP is removed by Mac80211, or when it can no longer accept * AMPDU traffic, packets pending in hardware have to be invalidated so that * when later on hardware releases them, they can be handled appropriately. */ void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, struct ieee80211_sta *sta, void (*dma_callback_fn)) { struct dma_pub *dmah; int i; for (i = 0; i < NFIFO; i++) { dmah = hw->di[i]; if (dmah != NULL) dma_walk_packets(dmah, dma_callback_fn, sta); } } int brcms_c_get_curband(struct brcms_c_info *wlc) { return wlc->band->bandunit; } void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop) { int timeout = 20; /* flush packet queue when requested */ if (drop) brcmu_pktq_flush(&wlc->pkt_queue->q, false, NULL, NULL); /* wait for queue and DMA fifos to run dry */ while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0) { brcms_msleep(wlc->wl, 1); if (--timeout == 0) break; } WARN_ON_ONCE(timeout == 0); } void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) { wlc->bcn_li_bcn = interval; if (wlc->pub->up) brcms_c_bcn_li_upd(wlc); } int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr) { uint qdbm; /* Remove override bit and clip to max qdbm value */ qdbm = min_t(uint, txpwr * BRCMS_TXPWR_DB_FACTOR, 0xff); return wlc_phy_txpower_set(wlc->band->pi, qdbm, false); } int brcms_c_get_tx_power(struct brcms_c_info *wlc) { uint qdbm; bool override; wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override); /* Return qdbm units */ return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR); } /* Process received frames */ /* * Return true if more frames need to be processed. false otherwise. * Param 'bound' indicates max. # frames to process before break out. */ static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) { struct d11rxhdr *rxh; struct ieee80211_hdr *h; uint len; bool is_amsdu; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); /* frame starts with rxhdr */ rxh = (struct d11rxhdr *) (p->data); /* strip off rxhdr */ skb_pull(p, BRCMS_HWRXOFF); /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */ if (rxh->RxStatus1 & RXS_PBPRES) { if (p->len < 2) { wiphy_err(wlc->wiphy, "wl%d: recv: rcvd runt of " "len %d\n", wlc->pub->unit, p->len); goto toss; } skb_pull(p, 2); } h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN); len = p->len; if (rxh->RxStatus1 & RXS_FCSERR) { if (!(wlc->filter_flags & FIF_FCSFAIL)) goto toss; } /* check received pkt has at least frame control field */ if (len < D11_PHY_HDR_LEN + sizeof(h->frame_control)) goto toss; /* not supporting A-MSDU */ is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK; if (is_amsdu) goto toss; brcms_c_recvctl(wlc, rxh, p); return; toss: brcmu_pkt_buf_free_skb(p); } /* Process received frames */ /* * Return true if more frames need to be processed. false otherwise. * Param 'bound' indicates max. # frames to process before break out. */ static bool brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) { struct sk_buff *p; struct sk_buff *next = NULL; struct sk_buff_head recv_frames; uint n = 0; uint bound_limit = bound ? RXBND : -1; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); skb_queue_head_init(&recv_frames); /* gather received frames */ while (dma_rx(wlc_hw->di[fifo], &recv_frames)) { /* !give others some time to run! */ if (++n >= bound_limit) break; } /* post more rbufs */ dma_rxfill(wlc_hw->di[fifo]); /* process each frame */ skb_queue_walk_safe(&recv_frames, p, next) { struct d11rxhdr_le *rxh_le; struct d11rxhdr *rxh; skb_unlink(p, &recv_frames); rxh_le = (struct d11rxhdr_le *)p->data; rxh = (struct d11rxhdr *)p->data; /* fixup rx header endianness */ rxh->RxFrameSize = le16_to_cpu(rxh_le->RxFrameSize); rxh->PhyRxStatus_0 = le16_to_cpu(rxh_le->PhyRxStatus_0); rxh->PhyRxStatus_1 = le16_to_cpu(rxh_le->PhyRxStatus_1); rxh->PhyRxStatus_2 = le16_to_cpu(rxh_le->PhyRxStatus_2); rxh->PhyRxStatus_3 = le16_to_cpu(rxh_le->PhyRxStatus_3); rxh->PhyRxStatus_4 = le16_to_cpu(rxh_le->PhyRxStatus_4); rxh->PhyRxStatus_5 = le16_to_cpu(rxh_le->PhyRxStatus_5); rxh->RxStatus1 = le16_to_cpu(rxh_le->RxStatus1); rxh->RxStatus2 = le16_to_cpu(rxh_le->RxStatus2); rxh->RxTSFTime = le16_to_cpu(rxh_le->RxTSFTime); rxh->RxChan = le16_to_cpu(rxh_le->RxChan); brcms_c_recv(wlc_hw->wlc, p); } return n >= bound_limit; } /* second-level interrupt processing * Return true if another dpc needs to be re-scheduled. false otherwise. * Param 'bounded' indicates if applicable loops should be bounded. */ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) { u32 macintstatus; struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; struct wiphy *wiphy = wlc->wiphy; if (brcms_deviceremoved(wlc)) { wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return false; } /* grab and clear the saved software intstatus bits */ macintstatus = wlc->macintstatus; wlc->macintstatus = 0; BCMMSG(wlc->wiphy, "wl%d: macintstatus 0x%x\n", wlc_hw->unit, macintstatus); WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */ /* tx status */ if (macintstatus & MI_TFS) { bool fatal; if (brcms_b_txstatus(wlc->hw, bounded, &fatal)) wlc->macintstatus |= MI_TFS; if (fatal) { wiphy_err(wiphy, "MI_TFS: fatal\n"); goto fatal; } } if (macintstatus & (MI_TBTT | MI_DTIM_TBTT)) brcms_c_tbtt(wlc); /* ATIM window end */ if (macintstatus & MI_ATIMWINEND) { BCMMSG(wlc->wiphy, "end of ATIM window\n"); bcma_set32(core, D11REGOFFS(maccommand), wlc->qvalid); wlc->qvalid = 0; } /* * received data or control frame, MI_DMAINT is * indication of RX_FIFO interrupt */ if (macintstatus & MI_DMAINT) if (brcms_b_recv(wlc_hw, RX_FIFO, bounded)) wlc->macintstatus |= MI_DMAINT; /* noise sample collected */ if (macintstatus & MI_BG_NOISE) wlc_phy_noise_sample_intr(wlc_hw->band->pi); if (macintstatus & MI_GP0) { wiphy_err(wiphy, "wl%d: PSM microcode watchdog fired at %d " "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now); printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", __func__, ai_get_chip_id(wlc_hw->sih), ai_get_chiprev(wlc_hw->sih)); brcms_fatal_error(wlc_hw->wlc->wl); } /* gptimer timeout */ if (macintstatus & MI_TO) bcma_write32(core, D11REGOFFS(gptimer), 0); if (macintstatus & MI_RFDISABLE) { BCMMSG(wlc->wiphy, "wl%d: BMAC Detected a change on the" " RF Disable Input\n", wlc_hw->unit); brcms_rfkill_set_hw_state(wlc->wl); } /* send any enq'd tx packets. Just makes sure to jump start tx */ if (!pktq_empty(&wlc->pkt_queue->q)) brcms_c_send_q(wlc); /* it isn't done and needs to be resched if macintstatus is non-zero */ return wlc->macintstatus != 0; fatal: brcms_fatal_error(wlc_hw->wlc->wl); return wlc->macintstatus != 0; } void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) { struct bcma_device *core = wlc->hw->d11core; struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; u16 chanspec; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); chanspec = ch20mhz_chspec(ch->hw_value); brcms_b_init(wlc->hw, chanspec); /* update beacon listen interval */ brcms_c_bcn_li_upd(wlc); /* write ethernet address to core */ brcms_c_set_mac(wlc->bsscfg); brcms_c_set_bssid(wlc->bsscfg); /* Update tsf_cfprep if associated and up */ if (wlc->pub->associated && wlc->bsscfg->up) { u32 bi; /* get beacon period and convert to uS */ bi = wlc->bsscfg->current_bss->beacon_period << 10; /* * update since init path would reset * to default value */ bcma_write32(core, D11REGOFFS(tsf_cfprep), bi << CFPREP_CBI_SHIFT); /* Update maccontrol PM related bits */ brcms_c_set_ps_ctrl(wlc); } brcms_c_bandinit_ordered(wlc, chanspec); /* init probe response timeout */ brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); /* init max burst txop (framebursting) */ brcms_b_write_shm(wlc->hw, M_MBURST_TXOP, (wlc-> _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP)); /* initialize maximum allowed duty cycle */ brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true); brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true); /* * Update some shared memory locations related to * max AMPDU size allowed to received */ brcms_c_ampdu_shm_upd(wlc->ampdu); /* band-specific inits */ brcms_c_bsinit(wlc); /* Enable EDCF mode (while the MAC is suspended) */ bcma_set16(core, D11REGOFFS(ifs_ctl), IFS_USEEDCF); brcms_c_edcf_setparams(wlc, false); /* Init precedence maps for empty FIFOs */ brcms_c_tx_prec_map_init(wlc); /* read the ucode version if we have not yet done so */ if (wlc->ucode_rev == 0) { wlc->ucode_rev = brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR) << NBITS(u16); wlc->ucode_rev |= brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR); } /* ..now really unleash hell (allow the MAC out of suspend) */ brcms_c_enable_mac(wlc); /* suspend the tx fifos and mute the phy for preism cac time */ if (mute_tx) brcms_b_mute(wlc->hw, true); /* clear tx flow control */ brcms_c_txflowcontrol_reset(wlc); /* enable the RF Disable Delay timer */ bcma_write32(core, D11REGOFFS(rfdisabledly), RFDISABLE_DEFAULT); /* * Initialize WME parameters; if they haven't been set by some other * mechanism (IOVar, etc) then read them from the hardware. */ if (GFIELD(wlc->wme_retries[0], EDCF_SHORT) == 0) { /* Uninitialized; read from HW */ int ac; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) wlc->wme_retries[ac] = brcms_b_read_shm(wlc->hw, M_AC_TXLMT_ADDR(ac)); } } /* * The common driver entry routine. Error codes should be unique */ struct brcms_c_info * brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, bool piomode, uint *perr) { struct brcms_c_info *wlc; uint err = 0; uint i, j; struct brcms_pub *pub; /* allocate struct brcms_c_info state and its substructures */ wlc = brcms_c_attach_malloc(unit, &err, 0); if (wlc == NULL) goto fail; wlc->wiphy = wl->wiphy; pub = wlc->pub; #if defined(DEBUG) wlc_info_dbg = wlc; #endif wlc->band = wlc->bandstate[0]; wlc->core = wlc->corestate; wlc->wl = wl; pub->unit = unit; pub->_piomode = piomode; wlc->bandinit_pending = false; /* populate struct brcms_c_info with default values */ brcms_c_info_init(wlc, unit); /* update sta/ap related parameters */ brcms_c_ap_upd(wlc); /* * low level attach steps(all hw accesses go * inside, no more in rest of the attach) */ err = brcms_b_attach(wlc, core, unit, piomode); if (err) goto fail; brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, OFF); pub->phy_11ncapable = BRCMS_PHY_11N_CAP(wlc->band); /* disable allowed duty cycle */ wlc->tx_duty_cycle_ofdm = 0; wlc->tx_duty_cycle_cck = 0; brcms_c_stf_phy_chain_calc(wlc); /* txchain 1: txant 0, txchain 2: txant 1 */ if (BRCMS_ISNPHY(wlc->band) && (wlc->stf->txstreams == 1)) wlc->stf->txant = wlc->stf->hw_txchain - 1; /* push to BMAC driver */ wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain, wlc->stf->hw_rxchain); /* pull up some info resulting from the low attach */ for (i = 0; i < NFIFO; i++) wlc->core->txavail[i] = wlc->hw->txavail[i]; memcpy(&wlc->perm_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); memcpy(&pub->cur_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); for (j = 0; j < wlc->pub->_nbands; j++) { wlc->band = wlc->bandstate[j]; if (!brcms_c_attach_stf_ant_init(wlc)) { err = 24; goto fail; } /* default contention windows size limits */ wlc->band->CWmin = APHY_CWMIN; wlc->band->CWmax = PHY_CWMAX; /* init gmode value */ if (wlc->band->bandtype == BRCM_BAND_2G) { wlc->band->gmode = GMODE_AUTO; brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, wlc->band->gmode); } /* init _n_enab supported mode */ if (BRCMS_PHY_11N_CAP(wlc->band)) { pub->_n_enab = SUPPORT_11N; brcms_c_protection_upd(wlc, BRCMS_PROT_N_USER, ((pub->_n_enab == SUPPORT_11N) ? WL_11N_2x2 : WL_11N_3x3)); } /* init per-band default rateset, depend on band->gmode */ brcms_default_rateset(wlc, &wlc->band->defrateset); /* fill in hw_rateset */ brcms_c_rateset_filter(&wlc->band->defrateset, &wlc->band->hw_rateset, false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, (bool) (wlc->pub->_n_enab & SUPPORT_11N)); } /* * update antenna config due to * wlc->stf->txant/txchain/ant_rx_ovr change */ brcms_c_stf_phy_txant_upd(wlc); /* attach each modules */ err = brcms_c_attach_module(wlc); if (err != 0) goto fail; if (!brcms_c_timers_init(wlc, unit)) { wiphy_err(wl->wiphy, "wl%d: %s: init_timer failed\n", unit, __func__); err = 32; goto fail; } /* depend on rateset, gmode */ wlc->cmi = brcms_c_channel_mgr_attach(wlc); if (!wlc->cmi) { wiphy_err(wl->wiphy, "wl%d: %s: channel_mgr_attach failed" "\n", unit, __func__); err = 33; goto fail; } /* init default when all parameters are ready, i.e. ->rateset */ brcms_c_bss_default_init(wlc); /* * Complete the wlc default state initializations.. */ /* allocate our initial queue */ wlc->pkt_queue = brcms_c_txq_alloc(wlc); if (wlc->pkt_queue == NULL) { wiphy_err(wl->wiphy, "wl%d: %s: failed to malloc tx queue\n", unit, __func__); err = 100; goto fail; } wlc->bsscfg->wlc = wlc; wlc->mimoft = FT_HT; wlc->mimo_40txbw = AUTO; wlc->ofdm_40txbw = AUTO; wlc->cck_40txbw = AUTO; brcms_c_update_mimo_band_bwcap(wlc, BRCMS_N_BW_20IN2G_40IN5G); /* Set default values of SGI */ if (BRCMS_SGI_CAP_PHY(wlc)) { brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | BRCMS_N_SGI_40)); } else if (BRCMS_ISSSLPNPHY(wlc->band)) { brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | BRCMS_N_SGI_40)); } else { brcms_c_ht_update_sgi_rx(wlc, 0); } brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail); if (perr) *perr = 0; return wlc; fail: wiphy_err(wl->wiphy, "wl%d: %s: failed with err %d\n", unit, __func__, err); if (wlc) brcms_c_detach(wlc); if (perr) *perr = err; return NULL; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/dma.c0000644000175000017500000011640412026211315024502 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include "types.h" #include "dma.h" #include "soc.h" /* * dma register field offset calculation */ #define DMA64REGOFFS(field) offsetof(struct dma64regs, field) #define DMA64TXREGOFFS(di, field) (di->d64txregbase + DMA64REGOFFS(field)) #define DMA64RXREGOFFS(di, field) (di->d64rxregbase + DMA64REGOFFS(field)) /* * DMA hardware requires each descriptor ring to be 8kB aligned, and fit within * a contiguous 8kB physical address. */ #define D64RINGALIGN_BITS 13 #define D64MAXRINGSZ (1 << D64RINGALIGN_BITS) #define D64RINGALIGN (1 << D64RINGALIGN_BITS) #define D64MAXDD (D64MAXRINGSZ / sizeof(struct dma64desc)) /* transmit channel control */ #define D64_XC_XE 0x00000001 /* transmit enable */ #define D64_XC_SE 0x00000002 /* transmit suspend request */ #define D64_XC_LE 0x00000004 /* loopback enable */ #define D64_XC_FL 0x00000010 /* flush request */ #define D64_XC_PD 0x00000800 /* parity check disable */ #define D64_XC_AE 0x00030000 /* address extension bits */ #define D64_XC_AE_SHIFT 16 /* transmit descriptor table pointer */ #define D64_XP_LD_MASK 0x00000fff /* last valid descriptor */ /* transmit channel status */ #define D64_XS0_CD_MASK 0x00001fff /* current descriptor pointer */ #define D64_XS0_XS_MASK 0xf0000000 /* transmit state */ #define D64_XS0_XS_SHIFT 28 #define D64_XS0_XS_DISABLED 0x00000000 /* disabled */ #define D64_XS0_XS_ACTIVE 0x10000000 /* active */ #define D64_XS0_XS_IDLE 0x20000000 /* idle wait */ #define D64_XS0_XS_STOPPED 0x30000000 /* stopped */ #define D64_XS0_XS_SUSP 0x40000000 /* suspend pending */ #define D64_XS1_AD_MASK 0x00001fff /* active descriptor */ #define D64_XS1_XE_MASK 0xf0000000 /* transmit errors */ #define D64_XS1_XE_SHIFT 28 #define D64_XS1_XE_NOERR 0x00000000 /* no error */ #define D64_XS1_XE_DPE 0x10000000 /* descriptor protocol error */ #define D64_XS1_XE_DFU 0x20000000 /* data fifo underrun */ #define D64_XS1_XE_DTE 0x30000000 /* data transfer error */ #define D64_XS1_XE_DESRE 0x40000000 /* descriptor read error */ #define D64_XS1_XE_COREE 0x50000000 /* core error */ /* receive channel control */ /* receive enable */ #define D64_RC_RE 0x00000001 /* receive frame offset */ #define D64_RC_RO_MASK 0x000000fe #define D64_RC_RO_SHIFT 1 /* direct fifo receive (pio) mode */ #define D64_RC_FM 0x00000100 /* separate rx header descriptor enable */ #define D64_RC_SH 0x00000200 /* overflow continue */ #define D64_RC_OC 0x00000400 /* parity check disable */ #define D64_RC_PD 0x00000800 /* address extension bits */ #define D64_RC_AE 0x00030000 #define D64_RC_AE_SHIFT 16 /* flags for dma controller */ /* partity enable */ #define DMA_CTRL_PEN (1 << 0) /* rx overflow continue */ #define DMA_CTRL_ROC (1 << 1) /* allow rx scatter to multiple descriptors */ #define DMA_CTRL_RXMULTI (1 << 2) /* Unframed Rx/Tx data */ #define DMA_CTRL_UNFRAMED (1 << 3) /* receive descriptor table pointer */ #define D64_RP_LD_MASK 0x00000fff /* last valid descriptor */ /* receive channel status */ #define D64_RS0_CD_MASK 0x00001fff /* current descriptor pointer */ #define D64_RS0_RS_MASK 0xf0000000 /* receive state */ #define D64_RS0_RS_SHIFT 28 #define D64_RS0_RS_DISABLED 0x00000000 /* disabled */ #define D64_RS0_RS_ACTIVE 0x10000000 /* active */ #define D64_RS0_RS_IDLE 0x20000000 /* idle wait */ #define D64_RS0_RS_STOPPED 0x30000000 /* stopped */ #define D64_RS0_RS_SUSP 0x40000000 /* suspend pending */ #define D64_RS1_AD_MASK 0x0001ffff /* active descriptor */ #define D64_RS1_RE_MASK 0xf0000000 /* receive errors */ #define D64_RS1_RE_SHIFT 28 #define D64_RS1_RE_NOERR 0x00000000 /* no error */ #define D64_RS1_RE_DPO 0x10000000 /* descriptor protocol error */ #define D64_RS1_RE_DFU 0x20000000 /* data fifo overflow */ #define D64_RS1_RE_DTE 0x30000000 /* data transfer error */ #define D64_RS1_RE_DESRE 0x40000000 /* descriptor read error */ #define D64_RS1_RE_COREE 0x50000000 /* core error */ /* fifoaddr */ #define D64_FA_OFF_MASK 0xffff /* offset */ #define D64_FA_SEL_MASK 0xf0000 /* select */ #define D64_FA_SEL_SHIFT 16 #define D64_FA_SEL_XDD 0x00000 /* transmit dma data */ #define D64_FA_SEL_XDP 0x10000 /* transmit dma pointers */ #define D64_FA_SEL_RDD 0x40000 /* receive dma data */ #define D64_FA_SEL_RDP 0x50000 /* receive dma pointers */ #define D64_FA_SEL_XFD 0x80000 /* transmit fifo data */ #define D64_FA_SEL_XFP 0x90000 /* transmit fifo pointers */ #define D64_FA_SEL_RFD 0xc0000 /* receive fifo data */ #define D64_FA_SEL_RFP 0xd0000 /* receive fifo pointers */ #define D64_FA_SEL_RSD 0xe0000 /* receive frame status data */ #define D64_FA_SEL_RSP 0xf0000 /* receive frame status pointers */ /* descriptor control flags 1 */ #define D64_CTRL_COREFLAGS 0x0ff00000 /* core specific flags */ #define D64_CTRL1_EOT ((u32)1 << 28) /* end of descriptor table */ #define D64_CTRL1_IOC ((u32)1 << 29) /* interrupt on completion */ #define D64_CTRL1_EOF ((u32)1 << 30) /* end of frame */ #define D64_CTRL1_SOF ((u32)1 << 31) /* start of frame */ /* descriptor control flags 2 */ /* buffer byte count. real data len must <= 16KB */ #define D64_CTRL2_BC_MASK 0x00007fff /* address extension bits */ #define D64_CTRL2_AE 0x00030000 #define D64_CTRL2_AE_SHIFT 16 /* parity bit */ #define D64_CTRL2_PARITY 0x00040000 /* control flags in the range [27:20] are core-specific and not defined here */ #define D64_CTRL_CORE_MASK 0x0ff00000 #define D64_RX_FRM_STS_LEN 0x0000ffff /* frame length mask */ #define D64_RX_FRM_STS_OVFL 0x00800000 /* RxOverFlow */ #define D64_RX_FRM_STS_DSCRCNT 0x0f000000 /* no. of descriptors used - 1 */ #define D64_RX_FRM_STS_DATATYPE 0xf0000000 /* core-dependent data type */ /* * packet headroom necessary to accommodate the largest header * in the system, (i.e TXOFF). By doing, we avoid the need to * allocate an extra buffer for the header when bridging to WL. * There is a compile time check in wlc.c which ensure that this * value is at least as big as TXOFF. This value is used in * dma_rxfill(). */ #define BCMEXTRAHDROOM 172 /* debug/trace */ #ifdef DEBUG #define DMA_ERROR(fmt, ...) \ do { \ if (*di->msg_level & 1) \ pr_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ } while (0) #define DMA_TRACE(fmt, ...) \ do { \ if (*di->msg_level & 2) \ pr_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ } while (0) #else #define DMA_ERROR(fmt, ...) \ no_printk(fmt, ##__VA_ARGS__) #define DMA_TRACE(fmt, ...) \ no_printk(fmt, ##__VA_ARGS__) #endif /* DEBUG */ #define DMA_NONE(fmt, ...) \ no_printk(fmt, ##__VA_ARGS__) #define MAXNAMEL 8 /* 8 char names */ /* macros to convert between byte offsets and indexes */ #define B2I(bytes, type) ((bytes) / sizeof(type)) #define I2B(index, type) ((index) * sizeof(type)) #define PCI32ADDR_HIGH 0xc0000000 /* address[31:30] */ #define PCI32ADDR_HIGH_SHIFT 30 /* address[31:30] */ #define PCI64ADDR_HIGH 0x80000000 /* address[63] */ #define PCI64ADDR_HIGH_SHIFT 31 /* address[63] */ /* * DMA Descriptor * Descriptors are only read by the hardware, never written back. */ struct dma64desc { __le32 ctrl1; /* misc control bits & bufcount */ __le32 ctrl2; /* buffer count and address extension */ __le32 addrlow; /* memory address of the date buffer, bits 31:0 */ __le32 addrhigh; /* memory address of the date buffer, bits 63:32 */ }; /* dma engine software state */ struct dma_info { struct dma_pub dma; /* exported structure */ uint *msg_level; /* message level pointer */ char name[MAXNAMEL]; /* callers name for diag msgs */ struct bcma_device *core; struct device *dmadev; bool dma64; /* this dma engine is operating in 64-bit mode */ bool addrext; /* this dma engine supports DmaExtendedAddrChanges */ /* 64-bit dma tx engine registers */ uint d64txregbase; /* 64-bit dma rx engine registers */ uint d64rxregbase; /* pointer to dma64 tx descriptor ring */ struct dma64desc *txd64; /* pointer to dma64 rx descriptor ring */ struct dma64desc *rxd64; u16 dmadesc_align; /* alignment requirement for dma descriptors */ u16 ntxd; /* # tx descriptors tunable */ u16 txin; /* index of next descriptor to reclaim */ u16 txout; /* index of next descriptor to post */ /* pointer to parallel array of pointers to packets */ struct sk_buff **txp; /* Aligned physical address of descriptor ring */ dma_addr_t txdpa; /* Original physical address of descriptor ring */ dma_addr_t txdpaorig; u16 txdalign; /* #bytes added to alloc'd mem to align txd */ u32 txdalloc; /* #bytes allocated for the ring */ u32 xmtptrbase; /* When using unaligned descriptors, the ptr register * is not just an index, it needs all 13 bits to be * an offset from the addr register. */ u16 nrxd; /* # rx descriptors tunable */ u16 rxin; /* index of next descriptor to reclaim */ u16 rxout; /* index of next descriptor to post */ /* pointer to parallel array of pointers to packets */ struct sk_buff **rxp; /* Aligned physical address of descriptor ring */ dma_addr_t rxdpa; /* Original physical address of descriptor ring */ dma_addr_t rxdpaorig; u16 rxdalign; /* #bytes added to alloc'd mem to align rxd */ u32 rxdalloc; /* #bytes allocated for the ring */ u32 rcvptrbase; /* Base for ptr reg when using unaligned descriptors */ /* tunables */ unsigned int rxbufsize; /* rx buffer size in bytes, not including * the extra headroom */ uint rxextrahdrroom; /* extra rx headroom, reverseved to assist upper * stack, e.g. some rx pkt buffers will be * bridged to tx side without byte copying. * The extra headroom needs to be large enough * to fit txheader needs. Some dongle driver may * not need it. */ uint nrxpost; /* # rx buffers to keep posted */ unsigned int rxoffset; /* rxcontrol offset */ /* add to get dma address of descriptor ring, low 32 bits */ uint ddoffsetlow; /* high 32 bits */ uint ddoffsethigh; /* add to get dma address of data buffer, low 32 bits */ uint dataoffsetlow; /* high 32 bits */ uint dataoffsethigh; /* descriptor base need to be aligned or not */ bool aligndesc_4k; }; /* * default dma message level (if input msg_level * pointer is null in dma_attach()) */ static uint dma_msg_level; /* Check for odd number of 1's */ static u32 parity32(__le32 data) { /* no swap needed for counting 1's */ u32 par_data = *(u32 *)&data; par_data ^= par_data >> 16; par_data ^= par_data >> 8; par_data ^= par_data >> 4; par_data ^= par_data >> 2; par_data ^= par_data >> 1; return par_data & 1; } static bool dma64_dd_parity(struct dma64desc *dd) { return parity32(dd->addrlow ^ dd->addrhigh ^ dd->ctrl1 ^ dd->ctrl2); } /* descriptor bumping functions */ static uint xxd(uint x, uint n) { return x & (n - 1); /* faster than %, but n must be power of 2 */ } static uint txd(struct dma_info *di, uint x) { return xxd(x, di->ntxd); } static uint rxd(struct dma_info *di, uint x) { return xxd(x, di->nrxd); } static uint nexttxd(struct dma_info *di, uint i) { return txd(di, i + 1); } static uint prevtxd(struct dma_info *di, uint i) { return txd(di, i - 1); } static uint nextrxd(struct dma_info *di, uint i) { return txd(di, i + 1); } static uint ntxdactive(struct dma_info *di, uint h, uint t) { return txd(di, t-h); } static uint nrxdactive(struct dma_info *di, uint h, uint t) { return rxd(di, t-h); } static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags) { uint dmactrlflags; if (di == NULL) { DMA_ERROR("NULL dma handle\n"); return 0; } dmactrlflags = di->dma.dmactrlflags; dmactrlflags &= ~mask; dmactrlflags |= flags; /* If trying to enable parity, check if parity is actually supported */ if (dmactrlflags & DMA_CTRL_PEN) { u32 control; control = bcma_read32(di->core, DMA64TXREGOFFS(di, control)); bcma_write32(di->core, DMA64TXREGOFFS(di, control), control | D64_XC_PD); if (bcma_read32(di->core, DMA64TXREGOFFS(di, control)) & D64_XC_PD) /* We *can* disable it so it is supported, * restore control register */ bcma_write32(di->core, DMA64TXREGOFFS(di, control), control); else /* Not supported, don't allow it to be enabled */ dmactrlflags &= ~DMA_CTRL_PEN; } di->dma.dmactrlflags = dmactrlflags; return dmactrlflags; } static bool _dma64_addrext(struct dma_info *di, uint ctrl_offset) { u32 w; bcma_set32(di->core, ctrl_offset, D64_XC_AE); w = bcma_read32(di->core, ctrl_offset); bcma_mask32(di->core, ctrl_offset, ~D64_XC_AE); return (w & D64_XC_AE) == D64_XC_AE; } /* * return true if this dma engine supports DmaExtendedAddrChanges, * otherwise false */ static bool _dma_isaddrext(struct dma_info *di) { /* DMA64 supports full 32- or 64-bit operation. AE is always valid */ /* not all tx or rx channel are available */ if (di->d64txregbase != 0) { if (!_dma64_addrext(di, DMA64TXREGOFFS(di, control))) DMA_ERROR("%s: DMA64 tx doesn't have AE set\n", di->name); return true; } else if (di->d64rxregbase != 0) { if (!_dma64_addrext(di, DMA64RXREGOFFS(di, control))) DMA_ERROR("%s: DMA64 rx doesn't have AE set\n", di->name); return true; } return false; } static bool _dma_descriptor_align(struct dma_info *di) { u32 addrl; /* Check to see if the descriptors need to be aligned on 4K/8K or not */ if (di->d64txregbase != 0) { bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), 0xff0); addrl = bcma_read32(di->core, DMA64TXREGOFFS(di, addrlow)); if (addrl != 0) return false; } else if (di->d64rxregbase != 0) { bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), 0xff0); addrl = bcma_read32(di->core, DMA64RXREGOFFS(di, addrlow)); if (addrl != 0) return false; } return true; } /* * Descriptor table must start at the DMA hardware dictated alignment, so * allocated memory must be large enough to support this requirement. */ static void *dma_alloc_consistent(struct dma_info *di, uint size, u16 align_bits, uint *alloced, dma_addr_t *pap) { if (align_bits) { u16 align = (1 << align_bits); if (!IS_ALIGNED(PAGE_SIZE, align)) size += align; *alloced = size; } return dma_alloc_coherent(di->dmadev, size, pap, GFP_ATOMIC); } static u8 dma_align_sizetobits(uint size) { u8 bitpos = 0; while (size >>= 1) bitpos++; return bitpos; } /* This function ensures that the DMA descriptor ring will not get allocated * across Page boundary. If the allocation is done across the page boundary * at the first time, then it is freed and the allocation is done at * descriptor ring size aligned location. This will ensure that the ring will * not cross page boundary */ static void *dma_ringalloc(struct dma_info *di, u32 boundary, uint size, u16 *alignbits, uint *alloced, dma_addr_t *descpa) { void *va; u32 desc_strtaddr; u32 alignbytes = 1 << *alignbits; va = dma_alloc_consistent(di, size, *alignbits, alloced, descpa); if (NULL == va) return NULL; desc_strtaddr = (u32) roundup((unsigned long)va, alignbytes); if (((desc_strtaddr + size - 1) & boundary) != (desc_strtaddr & boundary)) { *alignbits = dma_align_sizetobits(size); dma_free_coherent(di->dmadev, size, va, *descpa); va = dma_alloc_consistent(di, size, *alignbits, alloced, descpa); } return va; } static bool dma64_alloc(struct dma_info *di, uint direction) { u16 size; uint ddlen; void *va; uint alloced = 0; u16 align; u16 align_bits; ddlen = sizeof(struct dma64desc); size = (direction == DMA_TX) ? (di->ntxd * ddlen) : (di->nrxd * ddlen); align_bits = di->dmadesc_align; align = (1 << align_bits); if (direction == DMA_TX) { va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, &alloced, &di->txdpaorig); if (va == NULL) { DMA_ERROR("%s: DMA_ALLOC_CONSISTENT(ntxd) failed\n", di->name); return false; } align = (1 << align_bits); di->txd64 = (struct dma64desc *) roundup((unsigned long)va, align); di->txdalign = (uint) ((s8 *)di->txd64 - (s8 *) va); di->txdpa = di->txdpaorig + di->txdalign; di->txdalloc = alloced; } else { va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, &alloced, &di->rxdpaorig); if (va == NULL) { DMA_ERROR("%s: DMA_ALLOC_CONSISTENT(nrxd) failed\n", di->name); return false; } align = (1 << align_bits); di->rxd64 = (struct dma64desc *) roundup((unsigned long)va, align); di->rxdalign = (uint) ((s8 *)di->rxd64 - (s8 *) va); di->rxdpa = di->rxdpaorig + di->rxdalign; di->rxdalloc = alloced; } return true; } static bool _dma_alloc(struct dma_info *di, uint direction) { return dma64_alloc(di, direction); } struct dma_pub *dma_attach(char *name, struct si_pub *sih, struct bcma_device *core, uint txregbase, uint rxregbase, uint ntxd, uint nrxd, uint rxbufsize, int rxextheadroom, uint nrxpost, uint rxoffset, uint *msg_level) { struct dma_info *di; u8 rev = core->id.rev; uint size; struct si_info *sii = container_of(sih, struct si_info, pub); /* allocate private info structure */ di = kzalloc(sizeof(struct dma_info), GFP_ATOMIC); if (di == NULL) return NULL; di->msg_level = msg_level ? msg_level : &dma_msg_level; di->dma64 = ((bcma_aread32(core, BCMA_IOST) & SISF_DMA64) == SISF_DMA64); /* init dma reg info */ di->core = core; di->d64txregbase = txregbase; di->d64rxregbase = rxregbase; /* * Default flags (which can be changed by the driver calling * dma_ctrlflags before enable): For backwards compatibility * both Rx Overflow Continue and Parity are DISABLED. */ _dma_ctrlflags(di, DMA_CTRL_ROC | DMA_CTRL_PEN, 0); DMA_TRACE("%s: %s flags 0x%x ntxd %d nrxd %d " "rxbufsize %d rxextheadroom %d nrxpost %d rxoffset %d " "txregbase %u rxregbase %u\n", name, "DMA64", di->dma.dmactrlflags, ntxd, nrxd, rxbufsize, rxextheadroom, nrxpost, rxoffset, txregbase, rxregbase); /* make a private copy of our callers name */ strncpy(di->name, name, MAXNAMEL); di->name[MAXNAMEL - 1] = '\0'; di->dmadev = core->dma_dev; /* save tunables */ di->ntxd = (u16) ntxd; di->nrxd = (u16) nrxd; /* the actual dma size doesn't include the extra headroom */ di->rxextrahdrroom = (rxextheadroom == -1) ? BCMEXTRAHDROOM : rxextheadroom; if (rxbufsize > BCMEXTRAHDROOM) di->rxbufsize = (u16) (rxbufsize - di->rxextrahdrroom); else di->rxbufsize = (u16) rxbufsize; di->nrxpost = (u16) nrxpost; di->rxoffset = (u8) rxoffset; /* * figure out the DMA physical address offset for dd and data * PCI/PCIE: they map silicon backplace address to zero * based memory, need offset * Other bus: use zero SI_BUS BIGENDIAN kludge: use sdram * swapped region for data buffer, not descriptor */ di->ddoffsetlow = 0; di->dataoffsetlow = 0; /* for pci bus, add offset */ if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) { /* add offset for pcie with DMA64 bus */ di->ddoffsetlow = 0; di->ddoffsethigh = SI_PCIE_DMA_H32; } di->dataoffsetlow = di->ddoffsetlow; di->dataoffsethigh = di->ddoffsethigh; /* WAR64450 : DMACtl.Addr ext fields are not supported in SDIOD core. */ if ((core->id.id == BCMA_CORE_SDIO_DEV) && ((rev > 0) && (rev <= 2))) di->addrext = false; else if ((core->id.id == BCMA_CORE_I2S) && ((rev == 0) || (rev == 1))) di->addrext = false; else di->addrext = _dma_isaddrext(di); /* does the descriptor need to be aligned and if yes, on 4K/8K or not */ di->aligndesc_4k = _dma_descriptor_align(di); if (di->aligndesc_4k) { di->dmadesc_align = D64RINGALIGN_BITS; if ((ntxd < D64MAXDD / 2) && (nrxd < D64MAXDD / 2)) /* for smaller dd table, HW relax alignment reqmnt */ di->dmadesc_align = D64RINGALIGN_BITS - 1; } else { di->dmadesc_align = 4; /* 16 byte alignment */ } DMA_NONE("DMA descriptor align_needed %d, align %d\n", di->aligndesc_4k, di->dmadesc_align); /* allocate tx packet pointer vector */ if (ntxd) { size = ntxd * sizeof(void *); di->txp = kzalloc(size, GFP_ATOMIC); if (di->txp == NULL) goto fail; } /* allocate rx packet pointer vector */ if (nrxd) { size = nrxd * sizeof(void *); di->rxp = kzalloc(size, GFP_ATOMIC); if (di->rxp == NULL) goto fail; } /* * allocate transmit descriptor ring, only need ntxd descriptors * but it must be aligned */ if (ntxd) { if (!_dma_alloc(di, DMA_TX)) goto fail; } /* * allocate receive descriptor ring, only need nrxd descriptors * but it must be aligned */ if (nrxd) { if (!_dma_alloc(di, DMA_RX)) goto fail; } if ((di->ddoffsetlow != 0) && !di->addrext) { if (di->txdpa > SI_PCI_DMA_SZ) { DMA_ERROR("%s: txdpa 0x%x: addrext not supported\n", di->name, (u32)di->txdpa); goto fail; } if (di->rxdpa > SI_PCI_DMA_SZ) { DMA_ERROR("%s: rxdpa 0x%x: addrext not supported\n", di->name, (u32)di->rxdpa); goto fail; } } DMA_TRACE("ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh 0x%x addrext %d\n", di->ddoffsetlow, di->ddoffsethigh, di->dataoffsetlow, di->dataoffsethigh, di->addrext); return (struct dma_pub *) di; fail: dma_detach((struct dma_pub *)di); return NULL; } static inline void dma64_dd_upd(struct dma_info *di, struct dma64desc *ddring, dma_addr_t pa, uint outidx, u32 *flags, u32 bufcount) { u32 ctrl2 = bufcount & D64_CTRL2_BC_MASK; /* PCI bus with big(>1G) physical address, use address extension */ if ((di->dataoffsetlow == 0) || !(pa & PCI32ADDR_HIGH)) { ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow); ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh); ddring[outidx].ctrl1 = cpu_to_le32(*flags); ddring[outidx].ctrl2 = cpu_to_le32(ctrl2); } else { /* address extension for 32-bit PCI */ u32 ae; ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT; pa &= ~PCI32ADDR_HIGH; ctrl2 |= (ae << D64_CTRL2_AE_SHIFT) & D64_CTRL2_AE; ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow); ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh); ddring[outidx].ctrl1 = cpu_to_le32(*flags); ddring[outidx].ctrl2 = cpu_to_le32(ctrl2); } if (di->dma.dmactrlflags & DMA_CTRL_PEN) { if (dma64_dd_parity(&ddring[outidx])) ddring[outidx].ctrl2 = cpu_to_le32(ctrl2 | D64_CTRL2_PARITY); } } /* !! may be called with core in reset */ void dma_detach(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; DMA_TRACE("%s:\n", di->name); /* free dma descriptor rings */ if (di->txd64) dma_free_coherent(di->dmadev, di->txdalloc, ((s8 *)di->txd64 - di->txdalign), (di->txdpaorig)); if (di->rxd64) dma_free_coherent(di->dmadev, di->rxdalloc, ((s8 *)di->rxd64 - di->rxdalign), (di->rxdpaorig)); /* free packet pointer vectors */ kfree(di->txp); kfree(di->rxp); /* free our private info structure */ kfree(di); } /* initialize descriptor table base address */ static void _dma_ddtable_init(struct dma_info *di, uint direction, dma_addr_t pa) { if (!di->aligndesc_4k) { if (direction == DMA_TX) di->xmtptrbase = pa; else di->rcvptrbase = pa; } if ((di->ddoffsetlow == 0) || !(pa & PCI32ADDR_HIGH)) { if (direction == DMA_TX) { bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), pa + di->ddoffsetlow); bcma_write32(di->core, DMA64TXREGOFFS(di, addrhigh), di->ddoffsethigh); } else { bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), pa + di->ddoffsetlow); bcma_write32(di->core, DMA64RXREGOFFS(di, addrhigh), di->ddoffsethigh); } } else { /* DMA64 32bits address extension */ u32 ae; /* shift the high bit(s) from pa to ae */ ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT; pa &= ~PCI32ADDR_HIGH; if (direction == DMA_TX) { bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), pa + di->ddoffsetlow); bcma_write32(di->core, DMA64TXREGOFFS(di, addrhigh), di->ddoffsethigh); bcma_maskset32(di->core, DMA64TXREGOFFS(di, control), D64_XC_AE, (ae << D64_XC_AE_SHIFT)); } else { bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), pa + di->ddoffsetlow); bcma_write32(di->core, DMA64RXREGOFFS(di, addrhigh), di->ddoffsethigh); bcma_maskset32(di->core, DMA64RXREGOFFS(di, control), D64_RC_AE, (ae << D64_RC_AE_SHIFT)); } } } static void _dma_rxenable(struct dma_info *di) { uint dmactrlflags = di->dma.dmactrlflags; u32 control; DMA_TRACE("%s:\n", di->name); control = D64_RC_RE | (bcma_read32(di->core, DMA64RXREGOFFS(di, control)) & D64_RC_AE); if ((dmactrlflags & DMA_CTRL_PEN) == 0) control |= D64_RC_PD; if (dmactrlflags & DMA_CTRL_ROC) control |= D64_RC_OC; bcma_write32(di->core, DMA64RXREGOFFS(di, control), ((di->rxoffset << D64_RC_RO_SHIFT) | control)); } void dma_rxinit(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; DMA_TRACE("%s:\n", di->name); if (di->nrxd == 0) return; di->rxin = di->rxout = 0; /* clear rx descriptor ring */ memset(di->rxd64, '\0', di->nrxd * sizeof(struct dma64desc)); /* DMA engine with out alignment requirement requires table to be inited * before enabling the engine */ if (!di->aligndesc_4k) _dma_ddtable_init(di, DMA_RX, di->rxdpa); _dma_rxenable(di); if (di->aligndesc_4k) _dma_ddtable_init(di, DMA_RX, di->rxdpa); } static struct sk_buff *dma64_getnextrxp(struct dma_info *di, bool forceall) { uint i, curr; struct sk_buff *rxp; dma_addr_t pa; i = di->rxin; /* return if no packets posted */ if (i == di->rxout) return NULL; curr = B2I(((bcma_read32(di->core, DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) - di->rcvptrbase) & D64_RS0_CD_MASK, struct dma64desc); /* ignore curr if forceall */ if (!forceall && (i == curr)) return NULL; /* get the packet pointer that corresponds to the rx descriptor */ rxp = di->rxp[i]; di->rxp[i] = NULL; pa = le32_to_cpu(di->rxd64[i].addrlow) - di->dataoffsetlow; /* clear this packet from the descriptor ring */ dma_unmap_single(di->dmadev, pa, di->rxbufsize, DMA_FROM_DEVICE); di->rxd64[i].addrlow = cpu_to_le32(0xdeadbeef); di->rxd64[i].addrhigh = cpu_to_le32(0xdeadbeef); di->rxin = nextrxd(di, i); return rxp; } static struct sk_buff *_dma_getnextrxp(struct dma_info *di, bool forceall) { if (di->nrxd == 0) return NULL; return dma64_getnextrxp(di, forceall); } /* * !! rx entry routine * returns the number packages in the next frame, or 0 if there are no more * if DMA_CTRL_RXMULTI is defined, DMA scattering(multiple buffers) is * supported with pkts chain * otherwise, it's treated as giant pkt and will be tossed. * The DMA scattering starts with normal DMA header, followed by first * buffer data. After it reaches the max size of buffer, the data continues * in next DMA descriptor buffer WITHOUT DMA header */ int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list) { struct dma_info *di = (struct dma_info *)pub; struct sk_buff_head dma_frames; struct sk_buff *p, *next; uint len; uint pkt_len; int resid = 0; int pktcnt = 1; skb_queue_head_init(&dma_frames); next_frame: p = _dma_getnextrxp(di, false); if (p == NULL) return 0; len = le16_to_cpu(*(__le16 *) (p->data)); DMA_TRACE("%s: dma_rx len %d\n", di->name, len); dma_spin_for_len(len, p); /* set actual length */ pkt_len = min((di->rxoffset + len), di->rxbufsize); __skb_trim(p, pkt_len); skb_queue_tail(&dma_frames, p); resid = len - (di->rxbufsize - di->rxoffset); /* check for single or multi-buffer rx */ if (resid > 0) { while ((resid > 0) && (p = _dma_getnextrxp(di, false))) { pkt_len = min_t(uint, resid, di->rxbufsize); __skb_trim(p, pkt_len); skb_queue_tail(&dma_frames, p); resid -= di->rxbufsize; pktcnt++; } #ifdef DEBUG if (resid > 0) { uint cur; cur = B2I(((bcma_read32(di->core, DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) - di->rcvptrbase) & D64_RS0_CD_MASK, struct dma64desc); DMA_ERROR("rxin %d rxout %d, hw_curr %d\n", di->rxin, di->rxout, cur); } #endif /* DEBUG */ if ((di->dma.dmactrlflags & DMA_CTRL_RXMULTI) == 0) { DMA_ERROR("%s: bad frame length (%d)\n", di->name, len); skb_queue_walk_safe(&dma_frames, p, next) { skb_unlink(p, &dma_frames); brcmu_pkt_buf_free_skb(p); } di->dma.rxgiants++; pktcnt = 1; goto next_frame; } } skb_queue_splice_tail(&dma_frames, skb_list); return pktcnt; } static bool dma64_rxidle(struct dma_info *di) { DMA_TRACE("%s:\n", di->name); if (di->nrxd == 0) return true; return ((bcma_read32(di->core, DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) == (bcma_read32(di->core, DMA64RXREGOFFS(di, ptr)) & D64_RS0_CD_MASK)); } /* * post receive buffers * return false is refill failed completely and ring is empty this will stall * the rx dma and user might want to call rxfill again asap. This unlikely * happens on memory-rich NIC, but often on memory-constrained dongle */ bool dma_rxfill(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; struct sk_buff *p; u16 rxin, rxout; u32 flags = 0; uint n; uint i; dma_addr_t pa; uint extra_offset = 0; bool ring_empty; ring_empty = false; /* * Determine how many receive buffers we're lacking * from the full complement, allocate, initialize, * and post them, then update the chip rx lastdscr. */ rxin = di->rxin; rxout = di->rxout; n = di->nrxpost - nrxdactive(di, rxin, rxout); DMA_TRACE("%s: post %d\n", di->name, n); if (di->rxbufsize > BCMEXTRAHDROOM) extra_offset = di->rxextrahdrroom; for (i = 0; i < n; i++) { /* * the di->rxbufsize doesn't include the extra headroom, * we need to add it to the size to be allocated */ p = brcmu_pkt_buf_get_skb(di->rxbufsize + extra_offset); if (p == NULL) { DMA_ERROR("%s: out of rxbufs\n", di->name); if (i == 0 && dma64_rxidle(di)) { DMA_ERROR("%s: ring is empty !\n", di->name); ring_empty = true; } di->dma.rxnobuf++; break; } /* reserve an extra headroom, if applicable */ if (extra_offset) skb_pull(p, extra_offset); /* Do a cached write instead of uncached write since DMA_MAP * will flush the cache. */ *(u32 *) (p->data) = 0; pa = dma_map_single(di->dmadev, p->data, di->rxbufsize, DMA_FROM_DEVICE); /* save the free packet pointer */ di->rxp[rxout] = p; /* reset flags for each descriptor */ flags = 0; if (rxout == (di->nrxd - 1)) flags = D64_CTRL1_EOT; dma64_dd_upd(di, di->rxd64, pa, rxout, &flags, di->rxbufsize); rxout = nextrxd(di, rxout); } di->rxout = rxout; /* update the chip lastdscr pointer */ bcma_write32(di->core, DMA64RXREGOFFS(di, ptr), di->rcvptrbase + I2B(rxout, struct dma64desc)); return ring_empty; } void dma_rxreclaim(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; struct sk_buff *p; DMA_TRACE("%s:\n", di->name); while ((p = _dma_getnextrxp(di, true))) brcmu_pkt_buf_free_skb(p); } void dma_counterreset(struct dma_pub *pub) { /* reset all software counters */ pub->rxgiants = 0; pub->rxnobuf = 0; pub->txnobuf = 0; } /* get the address of the var in order to change later */ unsigned long dma_getvar(struct dma_pub *pub, const char *name) { struct dma_info *di = (struct dma_info *)pub; if (!strcmp(name, "&txavail")) return (unsigned long)&(di->dma.txavail); return 0; } /* 64-bit DMA functions */ void dma_txinit(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; u32 control = D64_XC_XE; DMA_TRACE("%s:\n", di->name); if (di->ntxd == 0) return; di->txin = di->txout = 0; di->dma.txavail = di->ntxd - 1; /* clear tx descriptor ring */ memset(di->txd64, '\0', (di->ntxd * sizeof(struct dma64desc))); /* DMA engine with out alignment requirement requires table to be inited * before enabling the engine */ if (!di->aligndesc_4k) _dma_ddtable_init(di, DMA_TX, di->txdpa); if ((di->dma.dmactrlflags & DMA_CTRL_PEN) == 0) control |= D64_XC_PD; bcma_set32(di->core, DMA64TXREGOFFS(di, control), control); /* DMA engine with alignment requirement requires table to be inited * before enabling the engine */ if (di->aligndesc_4k) _dma_ddtable_init(di, DMA_TX, di->txdpa); } void dma_txsuspend(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; DMA_TRACE("%s:\n", di->name); if (di->ntxd == 0) return; bcma_set32(di->core, DMA64TXREGOFFS(di, control), D64_XC_SE); } void dma_txresume(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; DMA_TRACE("%s:\n", di->name); if (di->ntxd == 0) return; bcma_mask32(di->core, DMA64TXREGOFFS(di, control), ~D64_XC_SE); } bool dma_txsuspended(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; return (di->ntxd == 0) || ((bcma_read32(di->core, DMA64TXREGOFFS(di, control)) & D64_XC_SE) == D64_XC_SE); } void dma_txreclaim(struct dma_pub *pub, enum txd_range range) { struct dma_info *di = (struct dma_info *)pub; struct sk_buff *p; DMA_TRACE("%s: %s\n", di->name, range == DMA_RANGE_ALL ? "all" : range == DMA_RANGE_TRANSMITTED ? "transmitted" : "transferred"); if (di->txin == di->txout) return; while ((p = dma_getnexttxp(pub, range))) { /* For unframed data, we don't have any packets to free */ if (!(di->dma.dmactrlflags & DMA_CTRL_UNFRAMED)) brcmu_pkt_buf_free_skb(p); } } bool dma_txreset(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; u32 status; if (di->ntxd == 0) return true; /* suspend tx DMA first */ bcma_write32(di->core, DMA64TXREGOFFS(di, control), D64_XC_SE); SPINWAIT(((status = (bcma_read32(di->core, DMA64TXREGOFFS(di, status0)) & D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED) && (status != D64_XS0_XS_IDLE) && (status != D64_XS0_XS_STOPPED), 10000); bcma_write32(di->core, DMA64TXREGOFFS(di, control), 0); SPINWAIT(((status = (bcma_read32(di->core, DMA64TXREGOFFS(di, status0)) & D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED), 10000); /* wait for the last transaction to complete */ udelay(300); return status == D64_XS0_XS_DISABLED; } bool dma_rxreset(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; u32 status; if (di->nrxd == 0) return true; bcma_write32(di->core, DMA64RXREGOFFS(di, control), 0); SPINWAIT(((status = (bcma_read32(di->core, DMA64RXREGOFFS(di, status0)) & D64_RS0_RS_MASK)) != D64_RS0_RS_DISABLED), 10000); return status == D64_RS0_RS_DISABLED; } /* * !! tx entry routine * WARNING: call must check the return value for error. * the error(toss frames) could be fatal and cause many subsequent hard * to debug problems */ int dma_txfast(struct dma_pub *pub, struct sk_buff *p, bool commit) { struct dma_info *di = (struct dma_info *)pub; unsigned char *data; uint len; u16 txout; u32 flags = 0; dma_addr_t pa; DMA_TRACE("%s:\n", di->name); txout = di->txout; /* * obtain and initialize transmit descriptor entry. */ data = p->data; len = p->len; /* no use to transmit a zero length packet */ if (len == 0) return 0; /* return nonzero if out of tx descriptors */ if (nexttxd(di, txout) == di->txin) goto outoftxd; /* get physical address of buffer start */ pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE); /* With a DMA segment list, Descriptor table is filled * using the segment list instead of looping over * buffers in multi-chain DMA. Therefore, EOF for SGLIST * is when end of segment list is reached. */ flags = D64_CTRL1_SOF | D64_CTRL1_IOC | D64_CTRL1_EOF; if (txout == (di->ntxd - 1)) flags |= D64_CTRL1_EOT; dma64_dd_upd(di, di->txd64, pa, txout, &flags, len); txout = nexttxd(di, txout); /* save the packet */ di->txp[prevtxd(di, txout)] = p; /* bump the tx descriptor index */ di->txout = txout; /* kick the chip */ if (commit) bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), di->xmtptrbase + I2B(txout, struct dma64desc)); /* tx flow control */ di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - 1; return 0; outoftxd: DMA_ERROR("%s: out of txds !!!\n", di->name); brcmu_pkt_buf_free_skb(p); di->dma.txavail = 0; di->dma.txnobuf++; return -1; } /* * Reclaim next completed txd (txds if using chained buffers) in the range * specified and return associated packet. * If range is DMA_RANGE_TRANSMITTED, reclaim descriptors that have be * transmitted as noted by the hardware "CurrDescr" pointer. * If range is DMA_RANGE_TRANSFERED, reclaim descriptors that have be * transferred by the DMA as noted by the hardware "ActiveDescr" pointer. * If range is DMA_RANGE_ALL, reclaim all txd(s) posted to the ring and * return associated packet regardless of the value of hardware pointers. */ struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range) { struct dma_info *di = (struct dma_info *)pub; u16 start, end, i; u16 active_desc; struct sk_buff *txp; DMA_TRACE("%s: %s\n", di->name, range == DMA_RANGE_ALL ? "all" : range == DMA_RANGE_TRANSMITTED ? "transmitted" : "transferred"); if (di->ntxd == 0) return NULL; txp = NULL; start = di->txin; if (range == DMA_RANGE_ALL) end = di->txout; else { end = (u16) (B2I(((bcma_read32(di->core, DMA64TXREGOFFS(di, status0)) & D64_XS0_CD_MASK) - di->xmtptrbase) & D64_XS0_CD_MASK, struct dma64desc)); if (range == DMA_RANGE_TRANSFERED) { active_desc = (u16)(bcma_read32(di->core, DMA64TXREGOFFS(di, status1)) & D64_XS1_AD_MASK); active_desc = (active_desc - di->xmtptrbase) & D64_XS0_CD_MASK; active_desc = B2I(active_desc, struct dma64desc); if (end != active_desc) end = prevtxd(di, active_desc); } } if ((start == 0) && (end > di->txout)) goto bogus; for (i = start; i != end && !txp; i = nexttxd(di, i)) { dma_addr_t pa; uint size; pa = le32_to_cpu(di->txd64[i].addrlow) - di->dataoffsetlow; size = (le32_to_cpu(di->txd64[i].ctrl2) & D64_CTRL2_BC_MASK); di->txd64[i].addrlow = cpu_to_le32(0xdeadbeef); di->txd64[i].addrhigh = cpu_to_le32(0xdeadbeef); txp = di->txp[i]; di->txp[i] = NULL; dma_unmap_single(di->dmadev, pa, size, DMA_TO_DEVICE); } di->txin = i; /* tx flow control */ di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - 1; return txp; bogus: DMA_NONE("bogus curr: start %d end %d txout %d\n", start, end, di->txout); return NULL; } /* * Mac80211 initiated actions sometimes require packets in the DMA queue to be * modified. The modified portion of the packet is not under control of the DMA * engine. This function calls a caller-supplied function for each packet in * the caller specified dma chain. */ void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc) (void *pkt, void *arg_a), void *arg_a) { struct dma_info *di = (struct dma_info *) dmah; uint i = di->txin; uint end = di->txout; struct sk_buff *skb; struct ieee80211_tx_info *tx_info; while (i != end) { skb = di->txp[i]; if (skb != NULL) { tx_info = (struct ieee80211_tx_info *)skb->cb; (callback_fnc)(tx_info, arg_a); } i = nexttxd(di, i); } } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c0000644000175000017500000012044712026211315025555 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define __UNDEF_NO_VERSION__ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include "phy/phy_int.h" #include "d11.h" #include "channel.h" #include "scb.h" #include "pub.h" #include "ucode_loader.h" #include "mac80211_if.h" #include "main.h" #define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */ /* Flags we support */ #define MAC_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ FIF_CONTROL | \ FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_PSPOLL) #define CHAN2GHZ(channel, freqency, chflags) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (freqency), \ .hw_value = (channel), \ .flags = chflags, \ .max_antenna_gain = 0, \ .max_power = 19, \ } #define CHAN5GHZ(channel, chflags) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = 5000 + 5*(channel), \ .hw_value = (channel), \ .flags = chflags, \ .max_antenna_gain = 0, \ .max_power = 21, \ } #define RATE(rate100m, _flags) { \ .bitrate = (rate100m), \ .flags = (_flags), \ .hw_value = (rate100m / 5), \ } struct firmware_hdr { __le32 offset; __le32 len; __le32 idx; }; static const char * const brcms_firmwares[MAX_FW_IMAGES] = { "brcm/bcm43xx", NULL }; static int n_adapters_found; MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); /* This needs to be adjusted when brcms_firmwares changes */ MODULE_FIRMWARE("brcm/bcm43xx-0.fw"); MODULE_FIRMWARE("brcm/bcm43xx_hdr-0.fw"); /* recognized BCMA Core IDs */ static struct bcma_device_id brcms_coreid_table[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS), BCMA_CORETABLE_END }; MODULE_DEVICE_TABLE(bcma, brcms_coreid_table); #ifdef DEBUG static int msglevel = 0xdeadbeef; module_param(msglevel, int, 0); #endif /* DEBUG */ static struct ieee80211_channel brcms_2ghz_chantable[] = { CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS), CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS), CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS), CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS), CHAN2GHZ(5, 2432, 0), CHAN2GHZ(6, 2437, 0), CHAN2GHZ(7, 2442, 0), CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(12, 2467, IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(13, 2472, IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(14, 2484, IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS | IEEE80211_CHAN_NO_OFDM) }; static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = { /* UNII-1 */ CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS), /* UNII-2 */ CHAN5GHZ(52, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(56, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(60, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(64, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), /* MID */ CHAN5GHZ(100, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(104, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(108, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(112, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(116, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(120, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(124, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(128, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(132, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(136, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(140, IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS), /* UNII-3 */ CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) }; /* * The rate table is used for both 2.4G and 5G rates. The * latter being a subset as it does not support CCK rates. */ static struct ieee80211_rate legacy_ratetable[] = { RATE(10, 0), RATE(20, IEEE80211_RATE_SHORT_PREAMBLE), RATE(55, IEEE80211_RATE_SHORT_PREAMBLE), RATE(110, IEEE80211_RATE_SHORT_PREAMBLE), RATE(60, 0), RATE(90, 0), RATE(120, 0), RATE(180, 0), RATE(240, 0), RATE(360, 0), RATE(480, 0), RATE(540, 0), }; static const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = { .band = IEEE80211_BAND_2GHZ, .channels = brcms_2ghz_chantable, .n_channels = ARRAY_SIZE(brcms_2ghz_chantable), .bitrates = legacy_ratetable, .n_bitrates = ARRAY_SIZE(legacy_ratetable), .ht_cap = { /* from include/linux/ieee80211.h */ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, .ampdu_density = AMPDU_DEF_MPDU_DENSITY, .mcs = { /* placeholders for now */ .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, .rx_highest = cpu_to_le16(500), .tx_params = IEEE80211_HT_MCS_TX_DEFINED} } }; static const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = { .band = IEEE80211_BAND_5GHZ, .channels = brcms_5ghz_nphy_chantable, .n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable), .bitrates = legacy_ratetable + BRCMS_LEGACY_5G_RATE_OFFSET, .n_bitrates = ARRAY_SIZE(legacy_ratetable) - BRCMS_LEGACY_5G_RATE_OFFSET, .ht_cap = { .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, .ampdu_density = AMPDU_DEF_MPDU_DENSITY, .mcs = { /* placeholders for now */ .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, .rx_highest = cpu_to_le16(500), .tx_params = IEEE80211_HT_MCS_TX_DEFINED} } }; /* flags the given rate in rateset as requested */ static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br) { u32 i; for (i = 0; i < rs->count; i++) { if (rate != (rs->rates[i] & 0x7f)) continue; if (is_br) rs->rates[i] |= BRCMS_RATE_FLAG; else rs->rates[i] &= BRCMS_RATE_MASK; return; } } static void brcms_ops_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct brcms_info *wl = hw->priv; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); spin_lock_bh(&wl->lock); if (!wl->pub->up) { wiphy_err(wl->wiphy, "ops->tx called while down\n"); kfree_skb(skb); goto done; } brcms_c_sendpkt_mac80211(wl->wlc, skb, hw); tx_info->rate_driver_data[0] = control->sta; done: spin_unlock_bh(&wl->lock); } static int brcms_ops_start(struct ieee80211_hw *hw) { struct brcms_info *wl = hw->priv; bool blocked; int err; ieee80211_wake_queues(hw); spin_lock_bh(&wl->lock); blocked = brcms_rfkill_set_hw_state(wl); spin_unlock_bh(&wl->lock); if (!blocked) wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); spin_lock_bh(&wl->lock); /* avoid acknowledging frames before a non-monitor device is added */ wl->mute_tx = true; if (!wl->pub->up) err = brcms_up(wl); else err = -ENODEV; spin_unlock_bh(&wl->lock); if (err != 0) wiphy_err(hw->wiphy, "%s: brcms_up() returned %d\n", __func__, err); return err; } static void brcms_ops_stop(struct ieee80211_hw *hw) { struct brcms_info *wl = hw->priv; int status; ieee80211_stop_queues(hw); if (wl->wlc == NULL) return; spin_lock_bh(&wl->lock); status = brcms_c_chipmatch(wl->wlc->hw->d11core); spin_unlock_bh(&wl->lock); if (!status) { wiphy_err(wl->wiphy, "wl: brcms_ops_stop: chipmatch failed\n"); return; } /* put driver in down state */ spin_lock_bh(&wl->lock); brcms_down(wl); spin_unlock_bh(&wl->lock); } static int brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct brcms_info *wl = hw->priv; /* Just STA for now */ if (vif->type != NL80211_IFTYPE_STATION) { wiphy_err(hw->wiphy, "%s: Attempt to add type %d, only" " STA for now\n", __func__, vif->type); return -EOPNOTSUPP; } wl->mute_tx = false; brcms_c_mute(wl->wlc, false); return 0; } static void brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { } static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed) { struct ieee80211_conf *conf = &hw->conf; struct brcms_info *wl = hw->priv; int err = 0; int new_int; struct wiphy *wiphy = hw->wiphy; spin_lock_bh(&wl->lock); if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { brcms_c_set_beacon_listen_interval(wl->wlc, conf->listen_interval); } if (changed & IEEE80211_CONF_CHANGE_MONITOR) wiphy_dbg(wiphy, "%s: change monitor mode: %s\n", __func__, conf->flags & IEEE80211_CONF_MONITOR ? "true" : "false"); if (changed & IEEE80211_CONF_CHANGE_PS) wiphy_err(wiphy, "%s: change power-save mode: %s (implement)\n", __func__, conf->flags & IEEE80211_CONF_PS ? "true" : "false"); if (changed & IEEE80211_CONF_CHANGE_POWER) { err = brcms_c_set_tx_power(wl->wlc, conf->power_level); if (err < 0) { wiphy_err(wiphy, "%s: Error setting power_level\n", __func__); goto config_out; } new_int = brcms_c_get_tx_power(wl->wlc); if (new_int != conf->power_level) wiphy_err(wiphy, "%s: Power level req != actual, %d %d" "\n", __func__, conf->power_level, new_int); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { if (conf->channel_type == NL80211_CHAN_HT20 || conf->channel_type == NL80211_CHAN_NO_HT) err = brcms_c_set_channel(wl->wlc, conf->channel->hw_value); else err = -ENOTSUPP; } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) err = brcms_c_set_rate_limit(wl->wlc, conf->short_frame_max_tx_count, conf->long_frame_max_tx_count); config_out: spin_unlock_bh(&wl->lock); return err; } static void brcms_ops_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct brcms_info *wl = hw->priv; struct wiphy *wiphy = hw->wiphy; if (changed & BSS_CHANGED_ASSOC) { /* association status changed (associated/disassociated) * also implies a change in the AID. */ wiphy_err(wiphy, "%s: %s: %sassociated\n", KBUILD_MODNAME, __func__, info->assoc ? "" : "dis"); spin_lock_bh(&wl->lock); brcms_c_associate_upd(wl->wlc, info->assoc); spin_unlock_bh(&wl->lock); } if (changed & BSS_CHANGED_ERP_SLOT) { s8 val; /* slot timing changed */ if (info->use_short_slot) val = 1; else val = 0; spin_lock_bh(&wl->lock); brcms_c_set_shortslot_override(wl->wlc, val); spin_unlock_bh(&wl->lock); } if (changed & BSS_CHANGED_HT) { /* 802.11n parameters changed */ u16 mode = info->ht_operation_mode; spin_lock_bh(&wl->lock); brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG, mode & IEEE80211_HT_OP_MODE_PROTECTION); brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF, mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS, mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT); spin_unlock_bh(&wl->lock); } if (changed & BSS_CHANGED_BASIC_RATES) { struct ieee80211_supported_band *bi; u32 br_mask, i; u16 rate; struct brcm_rateset rs; int error; /* retrieve the current rates */ spin_lock_bh(&wl->lock); brcms_c_get_current_rateset(wl->wlc, &rs); spin_unlock_bh(&wl->lock); br_mask = info->basic_rates; bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)]; for (i = 0; i < bi->n_bitrates; i++) { /* convert to internal rate value */ rate = (bi->bitrates[i].bitrate << 1) / 10; /* set/clear basic rate flag */ brcms_set_basic_rate(&rs, rate, br_mask & 1); br_mask >>= 1; } /* update the rate set */ spin_lock_bh(&wl->lock); error = brcms_c_set_rateset(wl->wlc, &rs); spin_unlock_bh(&wl->lock); if (error) wiphy_err(wiphy, "changing basic rates failed: %d\n", error); } if (changed & BSS_CHANGED_BEACON_INT) { /* Beacon interval changed */ spin_lock_bh(&wl->lock); brcms_c_set_beacon_period(wl->wlc, info->beacon_int); spin_unlock_bh(&wl->lock); } if (changed & BSS_CHANGED_BSSID) { /* BSSID changed, for whatever reason (IBSS and managed mode) */ spin_lock_bh(&wl->lock); brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid); spin_unlock_bh(&wl->lock); } if (changed & BSS_CHANGED_BEACON) /* Beacon data changed, retrieve new beacon (beaconing modes) */ wiphy_err(wiphy, "%s: beacon changed\n", __func__); if (changed & BSS_CHANGED_BEACON_ENABLED) { /* Beaconing should be enabled/disabled (beaconing modes) */ wiphy_err(wiphy, "%s: Beacon enabled: %s\n", __func__, info->enable_beacon ? "true" : "false"); } if (changed & BSS_CHANGED_CQM) { /* Connection quality monitor config changed */ wiphy_err(wiphy, "%s: cqm change: threshold %d, hys %d " " (implement)\n", __func__, info->cqm_rssi_thold, info->cqm_rssi_hyst); } if (changed & BSS_CHANGED_IBSS) { /* IBSS join status changed */ wiphy_err(wiphy, "%s: IBSS joined: %s (implement)\n", __func__, info->ibss_joined ? "true" : "false"); } if (changed & BSS_CHANGED_ARP_FILTER) { /* Hardware ARP filter address list or state changed */ wiphy_err(wiphy, "%s: arp filtering: enabled %s, count %d" " (implement)\n", __func__, info->arp_filter_enabled ? "true" : "false", info->arp_addr_cnt); } if (changed & BSS_CHANGED_QOS) { /* * QoS for this association was enabled/disabled. * Note that it is only ever disabled for station mode. */ wiphy_err(wiphy, "%s: qos enabled: %s (implement)\n", __func__, info->qos ? "true" : "false"); } return; } static void brcms_ops_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct brcms_info *wl = hw->priv; struct wiphy *wiphy = hw->wiphy; changed_flags &= MAC_FILTERS; *total_flags &= MAC_FILTERS; if (changed_flags & FIF_PROMISC_IN_BSS) wiphy_dbg(wiphy, "FIF_PROMISC_IN_BSS\n"); if (changed_flags & FIF_ALLMULTI) wiphy_dbg(wiphy, "FIF_ALLMULTI\n"); if (changed_flags & FIF_FCSFAIL) wiphy_dbg(wiphy, "FIF_FCSFAIL\n"); if (changed_flags & FIF_CONTROL) wiphy_dbg(wiphy, "FIF_CONTROL\n"); if (changed_flags & FIF_OTHER_BSS) wiphy_dbg(wiphy, "FIF_OTHER_BSS\n"); if (changed_flags & FIF_PSPOLL) wiphy_dbg(wiphy, "FIF_PSPOLL\n"); if (changed_flags & FIF_BCN_PRBRESP_PROMISC) wiphy_dbg(wiphy, "FIF_BCN_PRBRESP_PROMISC\n"); spin_lock_bh(&wl->lock); brcms_c_mac_promisc(wl->wlc, *total_flags); spin_unlock_bh(&wl->lock); return; } static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw) { struct brcms_info *wl = hw->priv; spin_lock_bh(&wl->lock); brcms_c_scan_start(wl->wlc); spin_unlock_bh(&wl->lock); return; } static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw) { struct brcms_info *wl = hw->priv; spin_lock_bh(&wl->lock); brcms_c_scan_stop(wl->wlc); spin_unlock_bh(&wl->lock); return; } static int brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct brcms_info *wl = hw->priv; spin_lock_bh(&wl->lock); brcms_c_wme_setparams(wl->wlc, queue, params, true); spin_unlock_bh(&wl->lock); return 0; } static int brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct brcms_info *wl = hw->priv; struct scb *scb = &wl->wlc->pri_scb; brcms_c_init_scb(scb); wl->pub->global_ampdu = &(scb->scb_ampdu); wl->pub->global_ampdu->scb = scb; wl->pub->global_ampdu->max_pdu = 16; /* * minstrel_ht initiates addBA on our behalf by calling * ieee80211_start_tx_ba_session() */ return 0; } static int brcms_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct brcms_info *wl = hw->priv; struct scb *scb = &wl->wlc->pri_scb; int status; if (WARN_ON(scb->magic != SCB_MAGIC)) return -EIDRM; switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: spin_lock_bh(&wl->lock); status = brcms_c_aggregatable(wl->wlc, tid); spin_unlock_bh(&wl->lock); if (!status) { wiphy_err(wl->wiphy, "START: tid %d is not agg\'able\n", tid); return -EINVAL; } ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: spin_lock_bh(&wl->lock); brcms_c_ampdu_flush(wl->wlc, sta, tid); spin_unlock_bh(&wl->lock); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: /* * BA window size from ADDBA response ('buf_size') defines how * many outstanding MPDUs are allowed for the BA stream by * recipient and traffic class. 'ampdu_factor' gives maximum * AMPDU size. */ spin_lock_bh(&wl->lock); brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size, (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor)) - 1); spin_unlock_bh(&wl->lock); /* Power save wakeup */ break; default: wiphy_err(wl->wiphy, "%s: Invalid command, ignoring\n", __func__); } return 0; } static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw) { struct brcms_info *wl = hw->priv; bool blocked; spin_lock_bh(&wl->lock); blocked = brcms_c_check_radio_disabled(wl->wlc); spin_unlock_bh(&wl->lock); wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); } static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop) { struct brcms_info *wl = hw->priv; no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false"); /* wait for packet queue and dma fifos to run empty */ spin_lock_bh(&wl->lock); brcms_c_wait_for_tx_completion(wl->wlc, drop); spin_unlock_bh(&wl->lock); } static const struct ieee80211_ops brcms_ops = { .tx = brcms_ops_tx, .start = brcms_ops_start, .stop = brcms_ops_stop, .add_interface = brcms_ops_add_interface, .remove_interface = brcms_ops_remove_interface, .config = brcms_ops_config, .bss_info_changed = brcms_ops_bss_info_changed, .configure_filter = brcms_ops_configure_filter, .sw_scan_start = brcms_ops_sw_scan_start, .sw_scan_complete = brcms_ops_sw_scan_complete, .conf_tx = brcms_ops_conf_tx, .sta_add = brcms_ops_sta_add, .ampdu_action = brcms_ops_ampdu_action, .rfkill_poll = brcms_ops_rfkill_poll, .flush = brcms_ops_flush, }; void brcms_dpc(unsigned long data) { struct brcms_info *wl; wl = (struct brcms_info *) data; spin_lock_bh(&wl->lock); /* call the common second level interrupt handler */ if (wl->pub->up) { if (wl->resched) { unsigned long flags; spin_lock_irqsave(&wl->isr_lock, flags); brcms_c_intrsupd(wl->wlc); spin_unlock_irqrestore(&wl->isr_lock, flags); } wl->resched = brcms_c_dpc(wl->wlc, true); } /* brcms_c_dpc() may bring the driver down */ if (!wl->pub->up) goto done; /* re-schedule dpc */ if (wl->resched) tasklet_schedule(&wl->tasklet); else /* re-enable interrupts */ brcms_intrson(wl); done: spin_unlock_bh(&wl->lock); } /* * Precondition: Since this function is called in brcms_pci_probe() context, * no locking is required. */ static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev) { int status; struct device *device = &pdev->dev; char fw_name[100]; int i; memset(&wl->fw, 0, sizeof(struct brcms_firmware)); for (i = 0; i < MAX_FW_IMAGES; i++) { if (brcms_firmwares[i] == NULL) break; sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i], UCODE_LOADER_API_VER); status = request_firmware(&wl->fw.fw_bin[i], fw_name, device); if (status) { wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", KBUILD_MODNAME, fw_name); return status; } sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i], UCODE_LOADER_API_VER); status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device); if (status) { wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", KBUILD_MODNAME, fw_name); return status; } wl->fw.hdr_num_entries[i] = wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr)); } wl->fw.fw_cnt = i; return brcms_ucode_data_init(wl, &wl->ucode); } /* * Precondition: Since this function is called in brcms_pci_probe() context, * no locking is required. */ static void brcms_release_fw(struct brcms_info *wl) { int i; for (i = 0; i < MAX_FW_IMAGES; i++) { release_firmware(wl->fw.fw_bin[i]); release_firmware(wl->fw.fw_hdr[i]); } } /** * This function frees the WL per-device resources. * * This function frees resources owned by the WL device pointed to * by the wl parameter. * * precondition: can both be called locked and unlocked * */ static void brcms_free(struct brcms_info *wl) { struct brcms_timer *t, *next; /* free ucode data */ if (wl->fw.fw_cnt) brcms_ucode_data_free(&wl->ucode); if (wl->irq) free_irq(wl->irq, wl); /* kill dpc */ tasklet_kill(&wl->tasklet); if (wl->pub) brcms_c_module_unregister(wl->pub, "linux", wl); /* free common resources */ if (wl->wlc) { brcms_c_detach(wl->wlc); wl->wlc = NULL; wl->pub = NULL; } /* virtual interface deletion is deferred so we cannot spinwait */ /* wait for all pending callbacks to complete */ while (atomic_read(&wl->callbacks) > 0) schedule(); /* free timers */ for (t = wl->timers; t; t = next) { next = t->next; #ifdef DEBUG kfree(t->name); #endif kfree(t); } } /* * called from both kernel as from this kernel module (error flow on attach) * precondition: perimeter lock is not acquired. */ static void brcms_remove(struct bcma_device *pdev) { struct ieee80211_hw *hw = bcma_get_drvdata(pdev); struct brcms_info *wl = hw->priv; if (wl->wlc) { wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false); wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); ieee80211_unregister_hw(hw); } brcms_free(wl); bcma_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); } static irqreturn_t brcms_isr(int irq, void *dev_id) { struct brcms_info *wl; bool ours, wantdpc; wl = (struct brcms_info *) dev_id; spin_lock(&wl->isr_lock); /* call common first level interrupt handler */ ours = brcms_c_isr(wl->wlc, &wantdpc); if (ours) { /* if more to do... */ if (wantdpc) { /* ...and call the second level interrupt handler */ /* schedule dpc */ tasklet_schedule(&wl->tasklet); } } spin_unlock(&wl->isr_lock); return IRQ_RETVAL(ours); } /* * is called in brcms_pci_probe() context, therefore no locking required. */ static int ieee_hw_rate_init(struct ieee80211_hw *hw) { struct brcms_info *wl = hw->priv; struct brcms_c_info *wlc = wl->wlc; struct ieee80211_supported_band *band; int has_5g = 0; u16 phy_type; hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; phy_type = brcms_c_get_phy_type(wl->wlc, 0); if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) { band = &wlc->bandstate[BAND_2G_INDEX]->band; *band = brcms_band_2GHz_nphy_template; if (phy_type == PHY_TYPE_LCN) { /* Single stream */ band->ht_cap.mcs.rx_mask[1] = 0; band->ht_cap.mcs.rx_highest = cpu_to_le16(72); } hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band; } else { return -EPERM; } /* Assume all bands use the same phy. True for 11n devices. */ if (wl->pub->_nbands > 1) { has_5g++; if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) { band = &wlc->bandstate[BAND_5G_INDEX]->band; *band = brcms_band_5GHz_nphy_template; hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band; } else { return -EPERM; } } return 0; } /* * is called in brcms_pci_probe() context, therefore no locking required. */ static int ieee_hw_init(struct ieee80211_hw *hw) { hw->flags = IEEE80211_HW_SIGNAL_DBM /* | IEEE80211_HW_CONNECTION_MONITOR What is this? */ | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_AMPDU_AGGREGATION; hw->extra_tx_headroom = brcms_c_get_header_len(); hw->queues = N_TX_QUEUES; hw->max_rates = 2; /* Primary rate and 1 fallback rate */ /* channel change time is dependent on chip and band */ hw->channel_change_time = 7 * 1000; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); hw->rate_control_algorithm = "minstrel_ht"; hw->sta_data_size = 0; return ieee_hw_rate_init(hw); } /** * attach to the WL device. * * Attach to the WL device identified by vendor and device parameters. * regs is a host accessible memory address pointing to WL device registers. * * brcms_attach is not defined as static because in the case where no bus * is defined, wl_attach will never be called, and thus, gcc will issue * a warning that this function is defined but not used if we declare * it as static. * * * is called in brcms_bcma_probe() context, therefore no locking required. */ static struct brcms_info *brcms_attach(struct bcma_device *pdev) { struct brcms_info *wl = NULL; int unit, err; struct ieee80211_hw *hw; u8 perm[ETH_ALEN]; unit = n_adapters_found; err = 0; if (unit < 0) return NULL; /* allocate private info */ hw = bcma_get_drvdata(pdev); if (hw != NULL) wl = hw->priv; if (WARN_ON(hw == NULL) || WARN_ON(wl == NULL)) return NULL; wl->wiphy = hw->wiphy; atomic_set(&wl->callbacks, 0); /* setup the bottom half handler */ tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl); spin_lock_init(&wl->lock); spin_lock_init(&wl->isr_lock); /* prepare ucode */ if (brcms_request_fw(wl, pdev) < 0) { wiphy_err(wl->wiphy, "%s: Failed to find firmware usually in " "%s\n", KBUILD_MODNAME, "/lib/firmware/brcm"); brcms_release_fw(wl); brcms_remove(pdev); return NULL; } /* common load-time initialization */ wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err); brcms_release_fw(wl); if (!wl->wlc) { wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n", KBUILD_MODNAME, err); goto fail; } wl->pub = brcms_c_pub(wl->wlc); wl->pub->ieee_hw = hw; /* register our interrupt handler */ if (request_irq(pdev->irq, brcms_isr, IRQF_SHARED, KBUILD_MODNAME, wl)) { wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit); goto fail; } wl->irq = pdev->irq; /* register module */ brcms_c_module_register(wl->pub, "linux", wl, NULL); if (ieee_hw_init(hw)) { wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit, __func__); goto fail; } brcms_c_regd_init(wl->wlc); memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN); if (WARN_ON(!is_valid_ether_addr(perm))) goto fail; SET_IEEE80211_PERM_ADDR(hw, perm); err = ieee80211_register_hw(hw); if (err) wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status" "%d\n", __func__, err); if (wl->pub->srom_ccode[0] && regulatory_hint(wl->wiphy, wl->pub->srom_ccode)) wiphy_err(wl->wiphy, "%s: regulatory hint failed\n", __func__); n_adapters_found++; return wl; fail: brcms_free(wl); return NULL; } /** * determines if a device is a WL device, and if so, attaches it. * * This function determines if a device pointed to by pdev is a WL device, * and if so, performs a brcms_attach() on it. * * Perimeter lock is initialized in the course of this function. */ static int __devinit brcms_bcma_probe(struct bcma_device *pdev) { struct brcms_info *wl; struct ieee80211_hw *hw; dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n", pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class, pdev->irq); if ((pdev->id.manuf != BCMA_MANUF_BCM) || (pdev->id.id != BCMA_CORE_80211)) return -ENODEV; hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops); if (!hw) { pr_err("%s: ieee80211_alloc_hw failed\n", __func__); return -ENOMEM; } SET_IEEE80211_DEV(hw, &pdev->dev); bcma_set_drvdata(pdev, hw); memset(hw->priv, 0, sizeof(*wl)); wl = brcms_attach(pdev); if (!wl) { pr_err("%s: brcms_attach failed!\n", __func__); return -ENODEV; } return 0; } static int brcms_suspend(struct bcma_device *pdev) { struct brcms_info *wl; struct ieee80211_hw *hw; hw = bcma_get_drvdata(pdev); wl = hw->priv; if (!wl) { pr_err("%s: %s: no driver private struct!\n", KBUILD_MODNAME, __func__); return -ENODEV; } /* only need to flag hw is down for proper resume */ spin_lock_bh(&wl->lock); wl->pub->hw_up = false; spin_unlock_bh(&wl->lock); pr_debug("brcms_suspend ok\n"); return 0; } static int brcms_resume(struct bcma_device *pdev) { pr_debug("brcms_resume ok\n"); return 0; } static struct bcma_driver brcms_bcma_driver = { .name = KBUILD_MODNAME, .probe = brcms_bcma_probe, .suspend = brcms_suspend, .resume = brcms_resume, .remove = __devexit_p(brcms_remove), .id_table = brcms_coreid_table, }; /** * This is the main entry point for the brcmsmac driver. * * This function is scheduled upon module initialization and * does the driver registration, which result in brcms_bcma_probe() * call resulting in the driver bringup. */ static void brcms_driver_init(struct work_struct *work) { int error; error = bcma_driver_register(&brcms_bcma_driver); if (error) pr_err("%s: register returned %d\n", __func__, error); } static DECLARE_WORK(brcms_driver_work, brcms_driver_init); static int __init brcms_module_init(void) { #ifdef DEBUG if (msglevel != 0xdeadbeef) brcm_msg_level = msglevel; #endif if (!schedule_work(&brcms_driver_work)) return -EBUSY; return 0; } /** * This function unloads the brcmsmac driver from the system. * * This function unconditionally unloads the brcmsmac driver module from the * system. * */ static void __exit brcms_module_exit(void) { cancel_work_sync(&brcms_driver_work); bcma_driver_unregister(&brcms_bcma_driver); } module_init(brcms_module_init); module_exit(brcms_module_exit); /* * precondition: perimeter lock has been acquired */ void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, bool state, int prio) { wiphy_err(wl->wiphy, "Shouldn't be here %s\n", __func__); } /* * precondition: perimeter lock has been acquired */ void brcms_init(struct brcms_info *wl) { BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit); brcms_reset(wl); brcms_c_init(wl->wlc, wl->mute_tx); } /* * precondition: perimeter lock has been acquired */ uint brcms_reset(struct brcms_info *wl) { BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit); brcms_c_reset(wl->wlc); /* dpc will not be rescheduled */ wl->resched = false; /* inform publicly that interface is down */ wl->pub->up = false; return 0; } void brcms_fatal_error(struct brcms_info *wl) { wiphy_err(wl->wlc->wiphy, "wl%d: fatal error, reinitializing\n", wl->wlc->pub->unit); brcms_reset(wl); ieee80211_restart_hw(wl->pub->ieee_hw); } /* * These are interrupt on/off entry points. Disable interrupts * during interrupt state transition. */ void brcms_intrson(struct brcms_info *wl) { unsigned long flags; spin_lock_irqsave(&wl->isr_lock, flags); brcms_c_intrson(wl->wlc); spin_unlock_irqrestore(&wl->isr_lock, flags); } u32 brcms_intrsoff(struct brcms_info *wl) { unsigned long flags; u32 status; spin_lock_irqsave(&wl->isr_lock, flags); status = brcms_c_intrsoff(wl->wlc); spin_unlock_irqrestore(&wl->isr_lock, flags); return status; } void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask) { unsigned long flags; spin_lock_irqsave(&wl->isr_lock, flags); brcms_c_intrsrestore(wl->wlc, macintmask); spin_unlock_irqrestore(&wl->isr_lock, flags); } /* * precondition: perimeter lock has been acquired */ int brcms_up(struct brcms_info *wl) { int error = 0; if (wl->pub->up) return 0; error = brcms_c_up(wl->wlc); return error; } /* * precondition: perimeter lock has been acquired */ void brcms_down(struct brcms_info *wl) { uint callbacks, ret_val = 0; /* call common down function */ ret_val = brcms_c_down(wl->wlc); callbacks = atomic_read(&wl->callbacks) - ret_val; /* wait for down callbacks to complete */ spin_unlock_bh(&wl->lock); /* For HIGH_only driver, it's important to actually schedule other work, * not just spin wait since everything runs at schedule level */ SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000); spin_lock_bh(&wl->lock); } /* * precondition: perimeter lock is not acquired */ static void _brcms_timer(struct work_struct *work) { struct brcms_timer *t = container_of(work, struct brcms_timer, dly_wrk.work); spin_lock_bh(&t->wl->lock); if (t->set) { if (t->periodic) { atomic_inc(&t->wl->callbacks); ieee80211_queue_delayed_work(t->wl->pub->ieee_hw, &t->dly_wrk, msecs_to_jiffies(t->ms)); } else { t->set = false; } t->fn(t->arg); } atomic_dec(&t->wl->callbacks); spin_unlock_bh(&t->wl->lock); } /* * Adds a timer to the list. Caller supplies a timer function. * Is called from wlc. * * precondition: perimeter lock has been acquired */ struct brcms_timer *brcms_init_timer(struct brcms_info *wl, void (*fn) (void *arg), void *arg, const char *name) { struct brcms_timer *t; t = kzalloc(sizeof(struct brcms_timer), GFP_ATOMIC); if (!t) return NULL; INIT_DELAYED_WORK(&t->dly_wrk, _brcms_timer); t->wl = wl; t->fn = fn; t->arg = arg; t->next = wl->timers; wl->timers = t; #ifdef DEBUG t->name = kmalloc(strlen(name) + 1, GFP_ATOMIC); if (t->name) strcpy(t->name, name); #endif return t; } /* * adds only the kernel timer since it's going to be more accurate * as well as it's easier to make it periodic * * precondition: perimeter lock has been acquired */ void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic) { struct ieee80211_hw *hw = t->wl->pub->ieee_hw; #ifdef DEBUG if (t->set) wiphy_err(hw->wiphy, "%s: Already set. Name: %s, per %d\n", __func__, t->name, periodic); #endif t->ms = ms; t->periodic = (bool) periodic; t->set = true; atomic_inc(&t->wl->callbacks); ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms)); } /* * return true if timer successfully deleted, false if still pending * * precondition: perimeter lock has been acquired */ bool brcms_del_timer(struct brcms_timer *t) { if (t->set) { t->set = false; if (!cancel_delayed_work(&t->dly_wrk)) return false; atomic_dec(&t->wl->callbacks); } return true; } /* * precondition: perimeter lock has been acquired */ void brcms_free_timer(struct brcms_timer *t) { struct brcms_info *wl = t->wl; struct brcms_timer *tmp; /* delete the timer in case it is active */ brcms_del_timer(t); if (wl->timers == t) { wl->timers = wl->timers->next; #ifdef DEBUG kfree(t->name); #endif kfree(t); return; } tmp = wl->timers; while (tmp) { if (tmp->next == t) { tmp->next = t->next; #ifdef DEBUG kfree(t->name); #endif kfree(t); return; } tmp = tmp->next; } } /* * precondition: perimeter lock has been acquired */ int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx) { int i, entry; const u8 *pdata; struct firmware_hdr *hdr; for (i = 0; i < wl->fw.fw_cnt; i++) { hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data; for (entry = 0; entry < wl->fw.hdr_num_entries[i]; entry++, hdr++) { u32 len = le32_to_cpu(hdr->len); if (le32_to_cpu(hdr->idx) == idx) { pdata = wl->fw.fw_bin[i]->data + le32_to_cpu(hdr->offset); *pbuf = kmemdup(pdata, len, GFP_ATOMIC); if (*pbuf == NULL) goto fail; return 0; } } } wiphy_err(wl->wiphy, "ERROR: ucode buf tag:%d can not be found!\n", idx); *pbuf = NULL; fail: return -ENODATA; } /* * Precondition: Since this function is called in brcms_bcma_probe() context, * no locking is required. */ int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx) { int i, entry; const u8 *pdata; struct firmware_hdr *hdr; for (i = 0; i < wl->fw.fw_cnt; i++) { hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data; for (entry = 0; entry < wl->fw.hdr_num_entries[i]; entry++, hdr++) { if (le32_to_cpu(hdr->idx) == idx) { pdata = wl->fw.fw_bin[i]->data + le32_to_cpu(hdr->offset); if (le32_to_cpu(hdr->len) != 4) { wiphy_err(wl->wiphy, "ERROR: fw hdr len\n"); return -ENOMSG; } *n_bytes = le32_to_cpu(*((__le32 *) pdata)); return 0; } } } wiphy_err(wl->wiphy, "ERROR: ucode tag:%d can not be found!\n", idx); return -ENOMSG; } /* * precondition: can both be called locked and unlocked */ void brcms_ucode_free_buf(void *p) { kfree(p); } /* * checks validity of all firmware images loaded from user space * * Precondition: Since this function is called in brcms_bcma_probe() context, * no locking is required. */ int brcms_check_firmwares(struct brcms_info *wl) { int i; int entry; int rc = 0; const struct firmware *fw; const struct firmware *fw_hdr; struct firmware_hdr *ucode_hdr; for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) { fw = wl->fw.fw_bin[i]; fw_hdr = wl->fw.fw_hdr[i]; if (fw == NULL && fw_hdr == NULL) { break; } else if (fw == NULL || fw_hdr == NULL) { wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n", __func__); rc = -EBADF; } else if (fw_hdr->size % sizeof(struct firmware_hdr)) { wiphy_err(wl->wiphy, "%s: non integral fw hdr file " "size %zu/%zu\n", __func__, fw_hdr->size, sizeof(struct firmware_hdr)); rc = -EBADF; } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) { wiphy_err(wl->wiphy, "%s: out of bounds fw file size " "%zu\n", __func__, fw->size); rc = -EBADF; } else { /* check if ucode section overruns firmware image */ ucode_hdr = (struct firmware_hdr *)fw_hdr->data; for (entry = 0; entry < wl->fw.hdr_num_entries[i] && !rc; entry++, ucode_hdr++) { if (le32_to_cpu(ucode_hdr->offset) + le32_to_cpu(ucode_hdr->len) > fw->size) { wiphy_err(wl->wiphy, "%s: conflicting bin/hdr\n", __func__); rc = -EBADF; } } } } if (rc == 0 && wl->fw.fw_cnt != i) { wiphy_err(wl->wiphy, "%s: invalid fw_cnt=%d\n", __func__, wl->fw.fw_cnt); rc = -EBADF; } return rc; } /* * precondition: perimeter lock has been acquired */ bool brcms_rfkill_set_hw_state(struct brcms_info *wl) { bool blocked = brcms_c_check_radio_disabled(wl->wlc); spin_unlock_bh(&wl->lock); wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); if (blocked) wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy); spin_lock_bh(&wl->lock); return blocked; } /* * precondition: perimeter lock has been acquired */ void brcms_msleep(struct brcms_info *wl, uint ms) { spin_unlock_bh(&wl->lock); msleep(ms); spin_lock_bh(&wl->lock); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/Makefile0000644000175000017500000000245412026211315025234 0ustar mcgrofmcgrof# # Makefile fragment for Broadcom 802.11n Networking Device Driver # # Copyright (c) 2010 Broadcom Corporation # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ccflags-y := \ -D__CHECK_ENDIAN__ \ -I$(obj) \ -I$(obj)/phy \ -I$(obj)/../include BRCMSMAC_OFILES := \ mac80211_if.o \ ucode_loader.o \ ampdu.o \ antsel.o \ channel.o \ main.o \ phy_shim.o \ pmu.o \ rate.o \ stf.o \ aiutils.o \ phy/phy_cmn.o \ phy/phy_lcn.o \ phy/phy_n.o \ phy/phytbl_lcn.o \ phy/phytbl_n.o \ phy/phy_qmath.o \ dma.o \ brcms_trace_events.o MODULEPFX := brcmsmac obj-$(CONFIG_BRCMSMAC) += $(MODULEPFX).o $(MODULEPFX)-objs = $(BRCMSMAC_OFILES) compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h0000644000175000017500000000372312026211315026372 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_UCODE_H_ #define _BRCM_UCODE_H_ #include "types.h" /* forward structure declarations */ #define MIN_FW_SIZE 40000 /* minimum firmware file size in bytes */ #define MAX_FW_SIZE 150000 #define UCODE_LOADER_API_VER 0 struct d11init; struct brcms_ucode { struct d11init *d11lcn0bsinitvals24; struct d11init *d11lcn0initvals24; struct d11init *d11lcn1bsinitvals24; struct d11init *d11lcn1initvals24; struct d11init *d11lcn2bsinitvals24; struct d11init *d11lcn2initvals24; struct d11init *d11n0absinitvals16; struct d11init *d11n0bsinitvals16; struct d11init *d11n0initvals16; __le32 *bcm43xx_16_mimo; size_t bcm43xx_16_mimosz; __le32 *bcm43xx_24_lcn; size_t bcm43xx_24_lcnsz; u32 *bcm43xx_bommajor; u32 *bcm43xx_bomminor; }; extern int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode); extern void brcms_ucode_data_free(struct brcms_ucode *ucode); extern int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, unsigned int idx); extern int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, unsigned int idx); extern void brcms_ucode_free_buf(void *); extern int brcms_check_firmwares(struct brcms_info *wl); #endif /* _BRCM_UCODE_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c0000644000175000017500000000757512026211315026376 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "types.h" #include enum { D11UCODE_NAMETAG_START = 0, D11LCN0BSINITVALS24, D11LCN0INITVALS24, D11LCN1BSINITVALS24, D11LCN1INITVALS24, D11LCN2BSINITVALS24, D11LCN2INITVALS24, D11N0ABSINITVALS16, D11N0BSINITVALS16, D11N0INITVALS16, D11UCODE_OVERSIGHT16_MIMO, D11UCODE_OVERSIGHT16_MIMOSZ, D11UCODE_OVERSIGHT24_LCN, D11UCODE_OVERSIGHT24_LCNSZ, D11UCODE_OVERSIGHT_BOMMAJOR, D11UCODE_OVERSIGHT_BOMMINOR }; int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode) { int rc; rc = brcms_check_firmwares(wl); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0bsinitvals24, D11LCN0BSINITVALS24); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0initvals24, D11LCN0INITVALS24); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1bsinitvals24, D11LCN1BSINITVALS24); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1initvals24, D11LCN1INITVALS24); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2bsinitvals24, D11LCN2BSINITVALS24); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2initvals24, D11LCN2INITVALS24); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0absinitvals16, D11N0ABSINITVALS16); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0bsinitvals16, D11N0BSINITVALS16); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0initvals16, D11N0INITVALS16); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_16_mimo, D11UCODE_OVERSIGHT16_MIMO); rc = rc < 0 ? rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_16_mimosz, D11UCODE_OVERSIGHT16_MIMOSZ); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_24_lcn, D11UCODE_OVERSIGHT24_LCN); rc = rc < 0 ? rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_24_lcnsz, D11UCODE_OVERSIGHT24_LCNSZ); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bommajor, D11UCODE_OVERSIGHT_BOMMAJOR); rc = rc < 0 ? rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bomminor, D11UCODE_OVERSIGHT_BOMMINOR); return rc; } void brcms_ucode_data_free(struct brcms_ucode *ucode) { brcms_ucode_free_buf((void *)ucode->d11lcn0bsinitvals24); brcms_ucode_free_buf((void *)ucode->d11lcn0initvals24); brcms_ucode_free_buf((void *)ucode->d11lcn1bsinitvals24); brcms_ucode_free_buf((void *)ucode->d11lcn1initvals24); brcms_ucode_free_buf((void *)ucode->d11lcn2bsinitvals24); brcms_ucode_free_buf((void *)ucode->d11lcn2initvals24); brcms_ucode_free_buf((void *)ucode->d11n0absinitvals16); brcms_ucode_free_buf((void *)ucode->d11n0bsinitvals16); brcms_ucode_free_buf((void *)ucode->d11n0initvals16); brcms_ucode_free_buf((void *)ucode->bcm43xx_16_mimo); brcms_ucode_free_buf((void *)ucode->bcm43xx_24_lcn); brcms_ucode_free_buf((void *)ucode->bcm43xx_bommajor); brcms_ucode_free_buf((void *)ucode->bcm43xx_bomminor); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/types.h0000644000175000017500000002405612026211315025113 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_TYPES_H_ #define _BRCM_TYPES_H_ #include #include #define WL_CHAN_FREQ_RANGE_2G 0 #define WL_CHAN_FREQ_RANGE_5GL 1 #define WL_CHAN_FREQ_RANGE_5GM 2 #define WL_CHAN_FREQ_RANGE_5GH 3 /* boardflags */ /* Board has gpio 9 controlling the PA */ #define BFL_PACTRL 0x00000002 /* Not ok to power down the chip pll and oscillator */ #define BFL_NOPLLDOWN 0x00000020 /* Board supports the Front End Module */ #define BFL_FEM 0x00000800 /* Board has an external LNA in 2.4GHz band */ #define BFL_EXTLNA 0x00001000 /* Board has no PA */ #define BFL_NOPA 0x00010000 /* Power topology uses BUCKBOOST */ #define BFL_BUCKBOOST 0x00200000 /* Board has FEM and switch to share antenna w/ BT */ #define BFL_FEM_BT 0x00400000 /* Power topology doesn't use CBUCK */ #define BFL_NOCBUCK 0x00800000 /* Power topology uses PALDO */ #define BFL_PALDO 0x02000000 /* Board has an external LNA in 5GHz band */ #define BFL_EXTLNA_5GHz 0x10000000 /* boardflags2 */ /* Board has an external rxbb regulator */ #define BFL2_RXBB_INT_REG_DIS 0x00000001 /* Flag to implement alternative A-band PLL settings */ #define BFL2_APLL_WAR 0x00000002 /* Board permits enabling TX Power Control */ #define BFL2_TXPWRCTRL_EN 0x00000004 /* Board supports the 2X4 diversity switch */ #define BFL2_2X4_DIV 0x00000008 /* Board supports 5G band power gain */ #define BFL2_5G_PWRGAIN 0x00000010 /* Board overrides ASPM and Clkreq settings */ #define BFL2_PCIEWAR_OVR 0x00000020 #define BFL2_LEGACY 0x00000080 /* 4321mcm93 board uses Skyworks FEM */ #define BFL2_SKWRKFEM_BRD 0x00000100 /* Board has a WAR for clock-harmonic spurs */ #define BFL2_SPUR_WAR 0x00000200 /* Flag to narrow G-band PLL loop b/w */ #define BFL2_GPLL_WAR 0x00000400 /* Tx CCK pkts on Ant 0 only */ #define BFL2_SINGLEANT_CCK 0x00001000 /* WAR to reduce and avoid clock-harmonic spurs in 2G */ #define BFL2_2G_SPUR_WAR 0x00002000 /* Flag to widen G-band PLL loop b/w */ #define BFL2_GPLL_WAR2 0x00010000 #define BFL2_IPALVLSHIFT_3P3 0x00020000 /* Use internal envelope detector for TX IQCAL */ #define BFL2_INTERNDET_TXIQCAL 0x00040000 /* Keep the buffered Xtal output from radio "ON". Most drivers will turn it * off without this flag to save power. */ #define BFL2_XTALBUFOUTEN 0x00080000 /* * board specific GPIO assignment, gpio 0-3 are also customer-configurable * led */ /* bit 9 controls the PA on new 4306 boards */ #define BOARD_GPIO_PACTRL 0x200 #define BOARD_GPIO_12 0x1000 #define BOARD_GPIO_13 0x2000 /* **** Core type/rev defaults **** */ #define D11CONF 0x0fffffb0 /* Supported D11 revs: 4, 5, 7-27 * also need to update wlc.h MAXCOREREV */ #define NCONF 0x000001ff /* Supported nphy revs: * 0 4321a0 * 1 4321a1 * 2 4321b0/b1/c0/c1 * 3 4322a0 * 4 4322a1 * 5 4716a0 * 6 43222a0, 43224a0 * 7 43226a0 * 8 5357a0, 43236a0 */ #define LCNCONF 0x00000007 /* Supported lcnphy revs: * 0 4313a0, 4336a0, 4330a0 * 1 * 2 4330a0 */ #define SSLPNCONF 0x0000000f /* Supported sslpnphy revs: * 0 4329a0/k0 * 1 4329b0/4329C0 * 2 4319a0 * 3 5356a0 */ /******************************************************************** * Phy/Core Configuration. Defines macros to to check core phy/rev * * compile-time configuration. Defines default core support. * * ****************************************************************** */ /* Basic macros to check a configuration bitmask */ #define CONF_HAS(config, val) ((config) & (1 << (val))) #define CONF_MSK(config, mask) ((config) & (mask)) #define MSK_RANGE(low, hi) ((1 << ((hi)+1)) - (1 << (low))) #define CONF_RANGE(config, low, hi) (CONF_MSK(config, MSK_RANGE(low, high))) #define CONF_IS(config, val) ((config) == (1 << (val))) #define CONF_GE(config, val) ((config) & (0-(1 << (val)))) #define CONF_GT(config, val) ((config) & (0-2*(1 << (val)))) #define CONF_LT(config, val) ((config) & ((1 << (val))-1)) #define CONF_LE(config, val) ((config) & (2*(1 << (val))-1)) /* Wrappers for some of the above, specific to config constants */ #define NCONF_HAS(val) CONF_HAS(NCONF, val) #define NCONF_MSK(mask) CONF_MSK(NCONF, mask) #define NCONF_IS(val) CONF_IS(NCONF, val) #define NCONF_GE(val) CONF_GE(NCONF, val) #define NCONF_GT(val) CONF_GT(NCONF, val) #define NCONF_LT(val) CONF_LT(NCONF, val) #define NCONF_LE(val) CONF_LE(NCONF, val) #define LCNCONF_HAS(val) CONF_HAS(LCNCONF, val) #define LCNCONF_MSK(mask) CONF_MSK(LCNCONF, mask) #define LCNCONF_IS(val) CONF_IS(LCNCONF, val) #define LCNCONF_GE(val) CONF_GE(LCNCONF, val) #define LCNCONF_GT(val) CONF_GT(LCNCONF, val) #define LCNCONF_LT(val) CONF_LT(LCNCONF, val) #define LCNCONF_LE(val) CONF_LE(LCNCONF, val) #define D11CONF_HAS(val) CONF_HAS(D11CONF, val) #define D11CONF_MSK(mask) CONF_MSK(D11CONF, mask) #define D11CONF_IS(val) CONF_IS(D11CONF, val) #define D11CONF_GE(val) CONF_GE(D11CONF, val) #define D11CONF_GT(val) CONF_GT(D11CONF, val) #define D11CONF_LT(val) CONF_LT(D11CONF, val) #define D11CONF_LE(val) CONF_LE(D11CONF, val) #define PHYCONF_HAS(val) CONF_HAS(PHYTYPE, val) #define PHYCONF_IS(val) CONF_IS(PHYTYPE, val) #define NREV_IS(var, val) \ (NCONF_HAS(val) && (NCONF_IS(val) || ((var) == (val)))) #define NREV_GE(var, val) \ (NCONF_GE(val) && (!NCONF_LT(val) || ((var) >= (val)))) #define NREV_GT(var, val) \ (NCONF_GT(val) && (!NCONF_LE(val) || ((var) > (val)))) #define NREV_LT(var, val) \ (NCONF_LT(val) && (!NCONF_GE(val) || ((var) < (val)))) #define NREV_LE(var, val) \ (NCONF_LE(val) && (!NCONF_GT(val) || ((var) <= (val)))) #define LCNREV_IS(var, val) \ (LCNCONF_HAS(val) && (LCNCONF_IS(val) || ((var) == (val)))) #define LCNREV_GE(var, val) \ (LCNCONF_GE(val) && (!LCNCONF_LT(val) || ((var) >= (val)))) #define LCNREV_GT(var, val) \ (LCNCONF_GT(val) && (!LCNCONF_LE(val) || ((var) > (val)))) #define LCNREV_LT(var, val) \ (LCNCONF_LT(val) && (!LCNCONF_GE(val) || ((var) < (val)))) #define LCNREV_LE(var, val) \ (LCNCONF_LE(val) && (!LCNCONF_GT(val) || ((var) <= (val)))) #define D11REV_IS(var, val) \ (D11CONF_HAS(val) && (D11CONF_IS(val) || ((var) == (val)))) #define D11REV_GE(var, val) \ (D11CONF_GE(val) && (!D11CONF_LT(val) || ((var) >= (val)))) #define D11REV_GT(var, val) \ (D11CONF_GT(val) && (!D11CONF_LE(val) || ((var) > (val)))) #define D11REV_LT(var, val) \ (D11CONF_LT(val) && (!D11CONF_GE(val) || ((var) < (val)))) #define D11REV_LE(var, val) \ (D11CONF_LE(val) && (!D11CONF_GT(val) || ((var) <= (val)))) #define PHYTYPE_IS(var, val)\ (PHYCONF_HAS(val) && (PHYCONF_IS(val) || ((var) == (val)))) /* Set up PHYTYPE automatically: (depends on PHY_TYPE_X, from d11.h) */ #define _PHYCONF_N (1 << PHY_TYPE_N) #define _PHYCONF_LCN (1 << PHY_TYPE_LCN) #define _PHYCONF_SSLPN (1 << PHY_TYPE_SSN) #define PHYTYPE (_PHYCONF_N | _PHYCONF_LCN | _PHYCONF_SSLPN) /* Utility macro to identify 802.11n (HT) capable PHYs */ #define PHYTYPE_11N_CAP(phytype) \ (PHYTYPE_IS(phytype, PHY_TYPE_N) || \ PHYTYPE_IS(phytype, PHY_TYPE_LCN) || \ PHYTYPE_IS(phytype, PHY_TYPE_SSN)) /* Last but not least: shorter wlc-specific var checks */ #define BRCMS_ISNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_N) #define BRCMS_ISLCNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_LCN) #define BRCMS_ISSSLPNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_SSN) #define BRCMS_PHY_11N_CAP(band) PHYTYPE_11N_CAP((band)->phytype) /********************************************************************** * ------------- End of Core phy/rev configuration. ----------------- * * ******************************************************************** */ #define BCMMSG(dev, fmt, args...) \ do { \ if (brcm_msg_level & LOG_TRACE_VAL) \ wiphy_err(dev, "%s: " fmt, __func__, ##args); \ } while (0) #ifdef CONFIG_BCM47XX /* * bcm4716 (which includes 4717 & 4718), plus 4706 on PCIe can reorder * transactions. As a fix, a read after write is performed on certain places * in the code. Older chips and the newer 5357 family don't require this fix. */ #define bcma_wflush16(c, o, v) \ ({ bcma_write16(c, o, v); (void)bcma_read16(c, o); }) #else #define bcma_wflush16(c, o, v) bcma_write16(c, o, v) #endif /* CONFIG_BCM47XX */ /* multi-bool data type: set of bools, mbool is true if any is set */ /* set one bool */ #define mboolset(mb, bit) ((mb) |= (bit)) /* clear one bool */ #define mboolclr(mb, bit) ((mb) &= ~(bit)) /* true if one bool is set */ #define mboolisset(mb, bit) (((mb) & (bit)) != 0) #define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val))) #define CEIL(x, y) (((x) + ((y)-1)) / (y)) /* forward declarations */ struct wiphy; struct ieee80211_sta; struct ieee80211_tx_queue_params; struct brcms_info; struct brcms_c_info; struct brcms_hardware; struct brcms_txq_info; struct brcms_band; struct dma_pub; struct si_pub; struct tx_status; struct d11rxhdr; struct txpwr_limits; /* iovar structure */ struct brcmu_iovar { const char *name; /* name for lookup and display */ u16 varid; /* id for switch */ u16 flags; /* driver-specific flag bits */ u16 type; /* base type of argument */ u16 minlen; /* min length for buffer vars */ }; /* brcm_msg_level is a bit vector with defs in defs.h */ extern u32 brcm_msg_level; #endif /* _BRCM_TYPES_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/stf.h0000644000175000017500000000341512026211315024537 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_STF_H_ #define _BRCM_STF_H_ #include "types.h" extern int brcms_c_stf_attach(struct brcms_c_info *wlc); extern void brcms_c_stf_detach(struct brcms_c_info *wlc); extern void brcms_c_tempsense_upd(struct brcms_c_info *wlc); extern void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel, u16 chanspec); extern int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band); extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); extern int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force); extern bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val); extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); extern void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc); extern u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec); extern u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec); #endif /* _BRCM_STF_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/stf.c0000644000175000017500000003071212026211315024532 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "types.h" #include "d11.h" #include "rate.h" #include "phy/phy_hal.h" #include "channel.h" #include "main.h" #include "stf.h" #define MIN_SPATIAL_EXPANSION 0 #define MAX_SPATIAL_EXPANSION 1 #define BRCMS_STF_SS_STBC_RX(wlc) (BRCMS_ISNPHY(wlc->band) && \ NREV_GT(wlc->band->phyrev, 3) && NREV_LE(wlc->band->phyrev, 6)) #define NSTS_1 1 #define NSTS_2 2 #define NSTS_3 3 #define NSTS_4 4 static const u8 txcore_default[5] = { (0), /* bitmap of the core enabled */ (0x01), /* For Nsts = 1, enable core 1 */ (0x03), /* For Nsts = 2, enable core 1 & 2 */ (0x07), /* For Nsts = 3, enable core 1, 2 & 3 */ (0x0f) /* For Nsts = 4, enable all cores */ }; static void brcms_c_stf_stbc_rx_ht_update(struct brcms_c_info *wlc, int val) { /* MIMOPHYs rev3-6 cannot receive STBC with only one rx core active */ if (BRCMS_STF_SS_STBC_RX(wlc)) { if ((wlc->stf->rxstreams == 1) && (val != HT_CAP_RX_STBC_NO)) return; } if (wlc->pub->up) { brcms_c_update_beacon(wlc); brcms_c_update_probe_resp(wlc, true); } } /* * every WLC_TEMPSENSE_PERIOD seconds temperature check to decide whether to * turn on/off txchain. */ void brcms_c_tempsense_upd(struct brcms_c_info *wlc) { struct brcms_phy_pub *pi = wlc->band->pi; uint active_chains, txchain; /* Check if the chip is too hot. Disable one Tx chain, if it is */ /* high 4 bits are for Rx chain, low 4 bits are for Tx chain */ active_chains = wlc_phy_stf_chain_active_get(pi); txchain = active_chains & 0xf; if (wlc->stf->txchain == wlc->stf->hw_txchain) { if (txchain && (txchain < wlc->stf->hw_txchain)) /* turn off 1 tx chain */ brcms_c_stf_txchain_set(wlc, txchain, true); } else if (wlc->stf->txchain < wlc->stf->hw_txchain) { if (txchain == wlc->stf->hw_txchain) /* turn back on txchain */ brcms_c_stf_txchain_set(wlc, txchain, true); } } void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel, u16 chanspec) { struct tx_power power; u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id; /* Clear previous settings */ *ss_algo_channel = 0; if (!wlc->pub->up) { *ss_algo_channel = (u16) -1; return; } wlc_phy_txpower_get_current(wlc->band->pi, &power, CHSPEC_CHANNEL(chanspec)); siso_mcs_id = (CHSPEC_IS40(chanspec)) ? WL_TX_POWER_MCS40_SISO_FIRST : WL_TX_POWER_MCS20_SISO_FIRST; cdd_mcs_id = (CHSPEC_IS40(chanspec)) ? WL_TX_POWER_MCS40_CDD_FIRST : WL_TX_POWER_MCS20_CDD_FIRST; stbc_mcs_id = (CHSPEC_IS40(chanspec)) ? WL_TX_POWER_MCS40_STBC_FIRST : WL_TX_POWER_MCS20_STBC_FIRST; /* criteria to choose stf mode */ /* * the "+3dbm (12 0.25db units)" is to account for the fact that with * CDD, tx occurs on both chains */ if (power.target[siso_mcs_id] > (power.target[cdd_mcs_id] + 12)) setbit(ss_algo_channel, PHY_TXC1_MODE_SISO); else setbit(ss_algo_channel, PHY_TXC1_MODE_CDD); /* * STBC is ORed into to algo channel as STBC requires per-packet SCB * capability check so cannot be default mode of operation. One of * SISO, CDD have to be set */ if (power.target[siso_mcs_id] <= (power.target[stbc_mcs_id] + 12)) setbit(ss_algo_channel, PHY_TXC1_MODE_STBC); } static bool brcms_c_stf_stbc_tx_set(struct brcms_c_info *wlc, s32 int_val) { if ((int_val != AUTO) && (int_val != OFF) && (int_val != ON)) return false; if ((int_val == ON) && (wlc->stf->txstreams == 1)) return false; wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = (s8) int_val; wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = (s8) int_val; return true; } bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val) { if ((int_val != HT_CAP_RX_STBC_NO) && (int_val != HT_CAP_RX_STBC_ONE_STREAM)) return false; if (BRCMS_STF_SS_STBC_RX(wlc)) { if ((int_val != HT_CAP_RX_STBC_NO) && (wlc->stf->rxstreams == 1)) return false; } brcms_c_stf_stbc_rx_ht_update(wlc, int_val); return true; } static int brcms_c_stf_txcore_set(struct brcms_c_info *wlc, u8 Nsts, u8 core_mask) { BCMMSG(wlc->wiphy, "wl%d: Nsts %d core_mask %x\n", wlc->pub->unit, Nsts, core_mask); if (hweight8(core_mask) > wlc->stf->txstreams) core_mask = 0; if ((hweight8(core_mask) == wlc->stf->txstreams) && ((core_mask & ~wlc->stf->txchain) || !(core_mask & wlc->stf->txchain))) core_mask = wlc->stf->txchain; wlc->stf->txcore[Nsts] = core_mask; /* Nsts = 1..4, txcore index = 1..4 */ if (Nsts == 1) { /* Needs to update beacon and ucode generated response * frames when 1 stream core map changed */ wlc->stf->phytxant = core_mask << PHY_TXC_ANT_SHIFT; brcms_b_txant_set(wlc->hw, wlc->stf->phytxant); if (wlc->clk) { brcms_c_suspend_mac_and_wait(wlc); brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec); brcms_c_enable_mac(wlc); } } return 0; } static int brcms_c_stf_spatial_policy_set(struct brcms_c_info *wlc, int val) { int i; u8 core_mask = 0; BCMMSG(wlc->wiphy, "wl%d: val %x\n", wlc->pub->unit, val); wlc->stf->spatial_policy = (s8) val; for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) { core_mask = (val == MAX_SPATIAL_EXPANSION) ? wlc->stf->txchain : txcore_default[i]; brcms_c_stf_txcore_set(wlc, (u8) i, core_mask); } return 0; } /* * Centralized txant update function. call it whenever wlc->stf->txant and/or * wlc->stf->txchain change. * * Antennas are controlled by ucode indirectly, which drives PHY or GPIO to * achieve various tx/rx antenna selection schemes * * legacy phy, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 * means auto(last rx). * for NREV<3, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 * means last rx and do tx-antenna selection for SISO transmissions * for NREV=3, bit 6 and bit _8_ means antenna 0 and 1 respectively, bit6+bit7 * means last rx and do tx-antenna selection for SISO transmissions * for NREV>=7, bit 6 and bit 7 mean antenna 0 and 1 respectively, nit6+bit7 * means both cores active */ static void _brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc) { s8 txant; txant = (s8) wlc->stf->txant; if (BRCMS_PHY_11N_CAP(wlc->band)) { if (txant == ANT_TX_FORCE_0) { wlc->stf->phytxant = PHY_TXC_ANT_0; } else if (txant == ANT_TX_FORCE_1) { wlc->stf->phytxant = PHY_TXC_ANT_1; if (BRCMS_ISNPHY(wlc->band) && NREV_GE(wlc->band->phyrev, 3) && NREV_LT(wlc->band->phyrev, 7)) wlc->stf->phytxant = PHY_TXC_ANT_2; } else { if (BRCMS_ISLCNPHY(wlc->band) || BRCMS_ISSSLPNPHY(wlc->band)) wlc->stf->phytxant = PHY_TXC_LCNPHY_ANT_LAST; else { /* catch out of sync wlc->stf->txcore */ WARN_ON(wlc->stf->txchain <= 0); wlc->stf->phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT; } } } else { if (txant == ANT_TX_FORCE_0) wlc->stf->phytxant = PHY_TXC_OLD_ANT_0; else if (txant == ANT_TX_FORCE_1) wlc->stf->phytxant = PHY_TXC_OLD_ANT_1; else wlc->stf->phytxant = PHY_TXC_OLD_ANT_LAST; } brcms_b_txant_set(wlc->hw, wlc->stf->phytxant); } int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force) { u8 txchain = (u8) int_val; u8 txstreams; uint i; if (wlc->stf->txchain == txchain) return 0; if ((txchain & ~wlc->stf->hw_txchain) || !(txchain & wlc->stf->hw_txchain)) return -EINVAL; /* * if nrate override is configured to be non-SISO STF mode, reject * reducing txchain to 1 */ txstreams = (u8) hweight8(txchain); if (txstreams > MAX_STREAMS_SUPPORTED) return -EINVAL; wlc->stf->txchain = txchain; wlc->stf->txstreams = txstreams; brcms_c_stf_stbc_tx_set(wlc, wlc->band->band_stf_stbc_tx); brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); wlc->stf->txant = (wlc->stf->txstreams == 1) ? ANT_TX_FORCE_0 : ANT_TX_DEF; _brcms_c_stf_phy_txant_upd(wlc); wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain, wlc->stf->rxchain); for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) brcms_c_stf_txcore_set(wlc, (u8) i, txcore_default[i]); return 0; } /* * update wlc->stf->ss_opmode which represents the operational stf_ss mode * we're using */ int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band) { int ret_code = 0; u8 prev_stf_ss; u8 upd_stf_ss; prev_stf_ss = wlc->stf->ss_opmode; /* * NOTE: opmode can only be SISO or CDD as STBC is decided on a * per-packet basis */ if (BRCMS_STBC_CAP_PHY(wlc) && wlc->stf->ss_algosel_auto && (wlc->stf->ss_algo_channel != (u16) -1)) { upd_stf_ss = (wlc->stf->txstreams == 1 || isset(&wlc->stf->ss_algo_channel, PHY_TXC1_MODE_SISO)) ? PHY_TXC1_MODE_SISO : PHY_TXC1_MODE_CDD; } else { if (wlc->band != band) return ret_code; upd_stf_ss = (wlc->stf->txstreams == 1) ? PHY_TXC1_MODE_SISO : band->band_stf_ss_mode; } if (prev_stf_ss != upd_stf_ss) { wlc->stf->ss_opmode = upd_stf_ss; brcms_b_band_stf_ss_set(wlc->hw, upd_stf_ss); } return ret_code; } int brcms_c_stf_attach(struct brcms_c_info *wlc) { wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_SISO; wlc->bandstate[BAND_5G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_CDD; if (BRCMS_ISNPHY(wlc->band) && (wlc_phy_txpower_hw_ctrl_get(wlc->band->pi) != PHY_TPC_HW_ON)) wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_CDD; brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); brcms_c_stf_stbc_rx_ht_update(wlc, HT_CAP_RX_STBC_NO); wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = OFF; wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = OFF; if (BRCMS_STBC_CAP_PHY(wlc)) { wlc->stf->ss_algosel_auto = true; /* Init the default value */ wlc->stf->ss_algo_channel = (u16) -1; } return 0; } void brcms_c_stf_detach(struct brcms_c_info *wlc) { } void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc) { _brcms_c_stf_phy_txant_upd(wlc); } void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc) { struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; /* get available rx/tx chains */ wlc->stf->hw_txchain = sprom->txchain; wlc->stf->hw_rxchain = sprom->rxchain; /* these parameter are intended to be used for all PHY types */ if (wlc->stf->hw_txchain == 0 || wlc->stf->hw_txchain == 0xf) { if (BRCMS_ISNPHY(wlc->band)) wlc->stf->hw_txchain = TXCHAIN_DEF_NPHY; else wlc->stf->hw_txchain = TXCHAIN_DEF; } wlc->stf->txchain = wlc->stf->hw_txchain; wlc->stf->txstreams = (u8) hweight8(wlc->stf->hw_txchain); if (wlc->stf->hw_rxchain == 0 || wlc->stf->hw_rxchain == 0xf) { if (BRCMS_ISNPHY(wlc->band)) wlc->stf->hw_rxchain = RXCHAIN_DEF_NPHY; else wlc->stf->hw_rxchain = RXCHAIN_DEF; } wlc->stf->rxchain = wlc->stf->hw_rxchain; wlc->stf->rxstreams = (u8) hweight8(wlc->stf->hw_rxchain); /* initialize the txcore table */ memcpy(wlc->stf->txcore, txcore_default, sizeof(wlc->stf->txcore)); /* default spatial_policy */ wlc->stf->spatial_policy = MIN_SPATIAL_EXPANSION; brcms_c_stf_spatial_policy_set(wlc, MIN_SPATIAL_EXPANSION); } static u16 _brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec) { u16 phytxant = wlc->stf->phytxant; if (rspec_stf(rspec) != PHY_TXC1_MODE_SISO) phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT; else if (wlc->stf->txant == ANT_TX_DEF) phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT; phytxant &= PHY_TXC_ANT_MASK; return phytxant; } u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec) { return _brcms_c_stf_phytxchain_sel(wlc, rspec); } u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec) { u16 phytxant = wlc->stf->phytxant; u16 mask = PHY_TXC_ANT_MASK; /* for non-siso rates or default setting, use the available chains */ if (BRCMS_ISNPHY(wlc->band)) { phytxant = _brcms_c_stf_phytxchain_sel(wlc, rspec); mask = PHY_TXC_HTANT_MASK; } phytxant |= phytxant & mask; return phytxant; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/scb.h0000644000175000017500000000565312026211315024520 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_SCB_H_ #define _BRCM_SCB_H_ #include #include #include #include "types.h" #define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */ #define AMPDU_MAX_SCB_TID NUMPRIO /* scb flags */ #define SCB_WMECAP 0x0040 #define SCB_HTCAP 0x10000 /* HT (MIMO) capable device */ #define SCB_IS40 0x80000 /* 40MHz capable */ #define SCB_STBCCAP 0x40000000 /* STBC Capable */ #define SCB_MAGIC 0xbeefcafe /* structure to store per-tid state for the ampdu initiator */ struct scb_ampdu_tid_ini { u8 tx_in_transit; /* number of pending mpdus in transit in driver */ u8 tid; /* initiator tid for easy lookup */ /* tx retry count; indexed by seq modulo */ u8 txretry[AMPDU_TX_BA_MAX_WSIZE]; struct scb *scb; /* backptr for easy lookup */ u8 ba_wsize; /* negotiated ba window size (in pdu) */ }; struct scb_ampdu { struct scb *scb; /* back pointer for easy reference */ u8 mpdu_density; /* mpdu density */ u8 max_pdu; /* max pdus allowed in ampdu */ u8 release; /* # of mpdus released at a time */ u16 min_len; /* min mpdu len to support the density */ u32 max_rx_ampdu_bytes; /* max ampdu rcv length; 8k, 16k, 32k, 64k */ /* * This could easily be a ini[] pointer and we keep this info in wl * itself instead of having mac80211 hold it for us. Also could be made * dynamic per tid instead of static. */ /* initiator info - per tid (NUMPRIO): */ struct scb_ampdu_tid_ini ini[AMPDU_MAX_SCB_TID]; }; /* station control block - one per remote MAC address */ struct scb { u32 magic; u32 flags; /* various bit flags as defined below */ u32 flags2; /* various bit flags2 as defined below */ u8 state; /* current state bitfield of auth/assoc process */ u8 ea[ETH_ALEN]; /* station address */ uint fragresid[NUMPRIO];/* #bytes unused in frag buffer per prio */ u16 seqctl[NUMPRIO]; /* seqctl of last received frame (for dups) */ /* seqctl of last received frame (for dups) for non-QoS data and * management */ u16 seqctl_nonqos; u16 seqnum[NUMPRIO];/* WME: driver maintained sw seqnum per priority */ struct scb_ampdu scb_ampdu; /* AMPDU state including per tid info */ }; #endif /* _BRCM_SCB_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/rate.h0000644000175000017500000001610612026211315024677 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_RATE_H_ #define _BRCM_RATE_H_ #include "types.h" #include "d11.h" #include "phy_hal.h" extern const u8 rate_info[]; extern const struct brcms_c_rateset cck_ofdm_mimo_rates; extern const struct brcms_c_rateset ofdm_mimo_rates; extern const struct brcms_c_rateset cck_ofdm_rates; extern const struct brcms_c_rateset ofdm_rates; extern const struct brcms_c_rateset cck_rates; extern const struct brcms_c_rateset gphy_legacy_rates; extern const struct brcms_c_rateset rate_limit_1_2; struct brcms_mcs_info { /* phy rate in kbps [20Mhz] */ u32 phy_rate_20; /* phy rate in kbps [40Mhz] */ u32 phy_rate_40; /* phy rate in kbps [20Mhz] with SGI */ u32 phy_rate_20_sgi; /* phy rate in kbps [40Mhz] with SGI */ u32 phy_rate_40_sgi; /* phy ctl byte 3, code rate, modulation type, # of streams */ u8 tx_phy_ctl3; /* matching legacy ofdm rate in 500bkps */ u8 leg_ofdm; }; #define BRCMS_MAXMCS 32 /* max valid mcs index */ #define MCS_TABLE_SIZE 33 /* Number of mcs entries in the table */ extern const struct brcms_mcs_info mcs_table[]; #define MCS_TXS_MASK 0xc0 /* num tx streams - 1 bit mask */ #define MCS_TXS_SHIFT 6 /* num tx streams - 1 bit shift */ /* returns num tx streams - 1 */ static inline u8 mcs_2_txstreams(u8 mcs) { return (mcs_table[mcs].tx_phy_ctl3 & MCS_TXS_MASK) >> MCS_TXS_SHIFT; } static inline uint mcs_2_rate(u8 mcs, bool is40, bool sgi) { if (sgi) { if (is40) return mcs_table[mcs].phy_rate_40_sgi; return mcs_table[mcs].phy_rate_20_sgi; } if (is40) return mcs_table[mcs].phy_rate_40; return mcs_table[mcs].phy_rate_20; } /* Macro to use the rate_info table */ #define BRCMS_RATE_MASK_FULL 0xff /* Rate value mask with basic rate flag */ /* * rate spec : holds rate and mode specific information required to generate a * tx frame. Legacy CCK and OFDM information is held in the same manner as was * done in the past (in the lower byte) the upper 3 bytes primarily hold MIMO * specific information */ /* rate spec bit fields */ /* Either 500Kbps units or MIMO MCS idx */ #define RSPEC_RATE_MASK 0x0000007F /* mimo MCS is stored in RSPEC_RATE_MASK */ #define RSPEC_MIMORATE 0x08000000 /* mimo bw mask */ #define RSPEC_BW_MASK 0x00000700 /* mimo bw shift */ #define RSPEC_BW_SHIFT 8 /* mimo Space/Time/Frequency mode mask */ #define RSPEC_STF_MASK 0x00003800 /* mimo Space/Time/Frequency mode shift */ #define RSPEC_STF_SHIFT 11 /* mimo coding type mask */ #define RSPEC_CT_MASK 0x0000C000 /* mimo coding type shift */ #define RSPEC_CT_SHIFT 14 /* mimo num STC streams per PLCP defn. */ #define RSPEC_STC_MASK 0x00300000 /* mimo num STC streams per PLCP defn. */ #define RSPEC_STC_SHIFT 20 /* mimo bit indicates adv coding in use */ #define RSPEC_LDPC_CODING 0x00400000 /* mimo bit indicates short GI in use */ #define RSPEC_SHORT_GI 0x00800000 /* bit indicates override both rate & mode */ #define RSPEC_OVERRIDE 0x80000000 /* bit indicates override rate only */ #define RSPEC_OVERRIDE_MCS_ONLY 0x40000000 static inline bool rspec_active(u32 rspec) { return rspec & (RSPEC_RATE_MASK | RSPEC_MIMORATE); } static inline u8 rspec_phytxbyte2(u32 rspec) { return (rspec & 0xff00) >> 8; } static inline u32 rspec_get_bw(u32 rspec) { return (rspec & RSPEC_BW_MASK) >> RSPEC_BW_SHIFT; } static inline bool rspec_issgi(u32 rspec) { return (rspec & RSPEC_SHORT_GI) == RSPEC_SHORT_GI; } static inline bool rspec_is40mhz(u32 rspec) { u32 bw = rspec_get_bw(rspec); return bw == PHY_TXC1_BW_40MHZ || bw == PHY_TXC1_BW_40MHZ_DUP; } static inline uint rspec2rate(u32 rspec) { if (rspec & RSPEC_MIMORATE) return mcs_2_rate(rspec & RSPEC_RATE_MASK, rspec_is40mhz(rspec), rspec_issgi(rspec)); return rspec & RSPEC_RATE_MASK; } static inline u8 rspec_mimoplcp3(u32 rspec) { return (rspec & 0xf00000) >> 16; } static inline bool plcp3_issgi(u8 plcp) { return (plcp & (RSPEC_SHORT_GI >> 16)) != 0; } static inline uint rspec_stc(u32 rspec) { return (rspec & RSPEC_STC_MASK) >> RSPEC_STC_SHIFT; } static inline uint rspec_stf(u32 rspec) { return (rspec & RSPEC_STF_MASK) >> RSPEC_STF_SHIFT; } static inline bool is_mcs_rate(u32 ratespec) { return (ratespec & RSPEC_MIMORATE) != 0; } static inline bool is_ofdm_rate(u32 ratespec) { return !is_mcs_rate(ratespec) && (rate_info[ratespec & RSPEC_RATE_MASK] & BRCMS_RATE_FLAG); } static inline bool is_cck_rate(u32 ratespec) { u32 rate = (ratespec & BRCMS_RATE_MASK); return !is_mcs_rate(ratespec) && ( rate == BRCM_RATE_1M || rate == BRCM_RATE_2M || rate == BRCM_RATE_5M5 || rate == BRCM_RATE_11M); } static inline bool is_single_stream(u8 mcs) { return mcs <= HIGHEST_SINGLE_STREAM_MCS || mcs == 32; } static inline u8 cck_rspec(u8 cck) { return cck & RSPEC_RATE_MASK; } /* Convert encoded rate value in plcp header to numerical rates in 500 KHz * increments */ static inline u8 ofdm_phy2mac_rate(u8 rlpt) { return wlc_phy_get_ofdm_rate_lookup()[rlpt & 0x7]; } static inline u8 cck_phy2mac_rate(u8 signal) { return signal/5; } /* Rates specified in brcms_c_rateset_filter() */ #define BRCMS_RATES_CCK_OFDM 0 #define BRCMS_RATES_CCK 1 #define BRCMS_RATES_OFDM 2 /* sanitize, and sort a rateset with the basic bit(s) preserved, validate * rateset */ extern bool brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, const struct brcms_c_rateset *hw_rs, bool check_brate, u8 txstreams); /* copy rateset src to dst as-is (no masking or sorting) */ extern void brcms_c_rateset_copy(const struct brcms_c_rateset *src, struct brcms_c_rateset *dst); /* would be nice to have these documented ... */ extern u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp); extern void brcms_c_rateset_filter(struct brcms_c_rateset *src, struct brcms_c_rateset *dst, bool basic_only, u8 rates, uint xmask, bool mcsallow); extern void brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, const struct brcms_c_rateset *rs_hw, uint phy_type, int bandtype, bool cck_only, uint rate_mask, bool mcsallow, u8 bw, u8 txstreams); extern s16 brcms_c_rate_legacy_phyctl(uint rate); extern void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams); extern void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset); extern void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams); extern void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw); #endif /* _BRCM_RATE_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/rate.c0000644000175000017500000004047112026211315024674 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "d11.h" #include "pub.h" #include "rate.h" /* * Rate info per rate: It tells whether a rate is ofdm or not and its phy_rate * value */ const u8 rate_info[BRCM_MAXRATE + 1] = { /* 0 1 2 3 4 5 6 7 8 9 */ /* 0 */ 0x00, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */ 0x00, 0x37, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x00, /* 20 */ 0x00, 0x00, 0x6e, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, /* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, /* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70 */ 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, /* 100 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c }; /* rates are in units of Kbps */ const struct brcms_mcs_info mcs_table[MCS_TABLE_SIZE] = { /* MCS 0: SS 1, MOD: BPSK, CR 1/2 */ {6500, 13500, CEIL(6500 * 10, 9), CEIL(13500 * 10, 9), 0x00, BRCM_RATE_6M}, /* MCS 1: SS 1, MOD: QPSK, CR 1/2 */ {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x08, BRCM_RATE_12M}, /* MCS 2: SS 1, MOD: QPSK, CR 3/4 */ {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x0A, BRCM_RATE_18M}, /* MCS 3: SS 1, MOD: 16QAM, CR 1/2 */ {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x10, BRCM_RATE_24M}, /* MCS 4: SS 1, MOD: 16QAM, CR 3/4 */ {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x12, BRCM_RATE_36M}, /* MCS 5: SS 1, MOD: 64QAM, CR 2/3 */ {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x19, BRCM_RATE_48M}, /* MCS 6: SS 1, MOD: 64QAM, CR 3/4 */ {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x1A, BRCM_RATE_54M}, /* MCS 7: SS 1, MOD: 64QAM, CR 5/6 */ {65000, 135000, CEIL(65000 * 10, 9), CEIL(135000 * 10, 9), 0x1C, BRCM_RATE_54M}, /* MCS 8: SS 2, MOD: BPSK, CR 1/2 */ {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x40, BRCM_RATE_6M}, /* MCS 9: SS 2, MOD: QPSK, CR 1/2 */ {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x48, BRCM_RATE_12M}, /* MCS 10: SS 2, MOD: QPSK, CR 3/4 */ {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x4A, BRCM_RATE_18M}, /* MCS 11: SS 2, MOD: 16QAM, CR 1/2 */ {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x50, BRCM_RATE_24M}, /* MCS 12: SS 2, MOD: 16QAM, CR 3/4 */ {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x52, BRCM_RATE_36M}, /* MCS 13: SS 2, MOD: 64QAM, CR 2/3 */ {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0x59, BRCM_RATE_48M}, /* MCS 14: SS 2, MOD: 64QAM, CR 3/4 */ {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x5A, BRCM_RATE_54M}, /* MCS 15: SS 2, MOD: 64QAM, CR 5/6 */ {130000, 270000, CEIL(130000 * 10, 9), CEIL(270000 * 10, 9), 0x5C, BRCM_RATE_54M}, /* MCS 16: SS 3, MOD: BPSK, CR 1/2 */ {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x80, BRCM_RATE_6M}, /* MCS 17: SS 3, MOD: QPSK, CR 1/2 */ {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x88, BRCM_RATE_12M}, /* MCS 18: SS 3, MOD: QPSK, CR 3/4 */ {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x8A, BRCM_RATE_18M}, /* MCS 19: SS 3, MOD: 16QAM, CR 1/2 */ {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x90, BRCM_RATE_24M}, /* MCS 20: SS 3, MOD: 16QAM, CR 3/4 */ {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x92, BRCM_RATE_36M}, /* MCS 21: SS 3, MOD: 64QAM, CR 2/3 */ {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0x99, BRCM_RATE_48M}, /* MCS 22: SS 3, MOD: 64QAM, CR 3/4 */ {175500, 364500, CEIL(175500 * 10, 9), CEIL(364500 * 10, 9), 0x9A, BRCM_RATE_54M}, /* MCS 23: SS 3, MOD: 64QAM, CR 5/6 */ {195000, 405000, CEIL(195000 * 10, 9), CEIL(405000 * 10, 9), 0x9B, BRCM_RATE_54M}, /* MCS 24: SS 4, MOD: BPSK, CR 1/2 */ {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0xC0, BRCM_RATE_6M}, /* MCS 25: SS 4, MOD: QPSK, CR 1/2 */ {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0xC8, BRCM_RATE_12M}, /* MCS 26: SS 4, MOD: QPSK, CR 3/4 */ {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0xCA, BRCM_RATE_18M}, /* MCS 27: SS 4, MOD: 16QAM, CR 1/2 */ {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0xD0, BRCM_RATE_24M}, /* MCS 28: SS 4, MOD: 16QAM, CR 3/4 */ {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0xD2, BRCM_RATE_36M}, /* MCS 29: SS 4, MOD: 64QAM, CR 2/3 */ {208000, 432000, CEIL(208000 * 10, 9), CEIL(432000 * 10, 9), 0xD9, BRCM_RATE_48M}, /* MCS 30: SS 4, MOD: 64QAM, CR 3/4 */ {234000, 486000, CEIL(234000 * 10, 9), CEIL(486000 * 10, 9), 0xDA, BRCM_RATE_54M}, /* MCS 31: SS 4, MOD: 64QAM, CR 5/6 */ {260000, 540000, CEIL(260000 * 10, 9), CEIL(540000 * 10, 9), 0xDB, BRCM_RATE_54M}, /* MCS 32: SS 1, MOD: BPSK, CR 1/2 */ {0, 6000, 0, CEIL(6000 * 10, 9), 0x00, BRCM_RATE_6M}, }; /* * phycfg for legacy OFDM frames: code rate, modulation scheme, spatial streams * Number of spatial streams: always 1 other fields: refer to table 78 of * section 17.3.2.2 of the original .11a standard */ struct legacy_phycfg { u32 rate_ofdm; /* ofdm mac rate */ /* phy ctl byte 3, code rate, modulation type, # of streams */ u8 tx_phy_ctl3; }; /* Number of legacy_rate_cfg entries in the table */ #define LEGACY_PHYCFG_TABLE_SIZE 12 /* * In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate * Eventually MIMOPHY would also be converted to this format * 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */ static const struct legacy_phycfg legacy_phycfg_table[LEGACY_PHYCFG_TABLE_SIZE] = { {BRCM_RATE_1M, 0x00}, /* CCK 1Mbps, data rate 0 */ {BRCM_RATE_2M, 0x08}, /* CCK 2Mbps, data rate 1 */ {BRCM_RATE_5M5, 0x10}, /* CCK 5.5Mbps, data rate 2 */ {BRCM_RATE_11M, 0x18}, /* CCK 11Mbps, data rate 3 */ /* OFDM 6Mbps, code rate 1/2, BPSK, 1 spatial stream */ {BRCM_RATE_6M, 0x00}, /* OFDM 9Mbps, code rate 3/4, BPSK, 1 spatial stream */ {BRCM_RATE_9M, 0x02}, /* OFDM 12Mbps, code rate 1/2, QPSK, 1 spatial stream */ {BRCM_RATE_12M, 0x08}, /* OFDM 18Mbps, code rate 3/4, QPSK, 1 spatial stream */ {BRCM_RATE_18M, 0x0A}, /* OFDM 24Mbps, code rate 1/2, 16-QAM, 1 spatial stream */ {BRCM_RATE_24M, 0x10}, /* OFDM 36Mbps, code rate 3/4, 16-QAM, 1 spatial stream */ {BRCM_RATE_36M, 0x12}, /* OFDM 48Mbps, code rate 2/3, 64-QAM, 1 spatial stream */ {BRCM_RATE_48M, 0x19}, /* OFDM 54Mbps, code rate 3/4, 64-QAM, 1 spatial stream */ {BRCM_RATE_54M, 0x1A}, }; /* Hardware rates (also encodes default basic rates) */ const struct brcms_c_rateset cck_ofdm_mimo_rates = { 12, /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, */ { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, /* 54 Mbps */ 0x6c}, 0x00, { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; const struct brcms_c_rateset ofdm_mimo_rates = { 8, /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, 0x00, { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; /* Default ratesets that include MCS32 for 40BW channels */ static const struct brcms_c_rateset cck_ofdm_40bw_mimo_rates = { 12, /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48 */ { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, /* 54 Mbps */ 0x6c}, 0x00, { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; static const struct brcms_c_rateset ofdm_40bw_mimo_rates = { 8, /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, 0x00, { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; const struct brcms_c_rateset cck_ofdm_rates = { 12, /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48,*/ { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, /*54 Mbps */ 0x6c}, 0x00, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; const struct brcms_c_rateset gphy_legacy_rates = { 4, /* 1b, 2b, 5.5b, 11b Mbps */ { 0x82, 0x84, 0x8b, 0x96}, 0x00, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; const struct brcms_c_rateset ofdm_rates = { 8, /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, 0x00, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; const struct brcms_c_rateset cck_rates = { 4, /* 1b, 2b, 5.5, 11 Mbps */ { 0x82, 0x84, 0x0b, 0x16}, 0x00, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; /* check if rateset is valid. * if check_brate is true, rateset without a basic rate is considered NOT valid. */ static bool brcms_c_rateset_valid(struct brcms_c_rateset *rs, bool check_brate) { uint idx; if (!rs->count) return false; if (!check_brate) return true; /* error if no basic rates */ for (idx = 0; idx < rs->count; idx++) { if (rs->rates[idx] & BRCMS_RATE_FLAG) return true; } return false; } void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams) { int i; for (i = txstreams; i < MAX_STREAMS_SUPPORTED; i++) rs->mcs[i] = 0; } /* * filter based on hardware rateset, and sort filtered rateset with basic * bit(s) preserved, and check if resulting rateset is valid. */ bool brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, const struct brcms_c_rateset *hw_rs, bool check_brate, u8 txstreams) { u8 rateset[BRCM_MAXRATE + 1]; u8 r; uint count; uint i; memset(rateset, 0, sizeof(rateset)); count = rs->count; for (i = 0; i < count; i++) { /* mask off "basic rate" bit, BRCMS_RATE_FLAG */ r = (int)rs->rates[i] & BRCMS_RATE_MASK; if ((r > BRCM_MAXRATE) || (rate_info[r] == 0)) continue; rateset[r] = rs->rates[i]; /* preserve basic bit! */ } /* fill out the rates in order, looking at only supported rates */ count = 0; for (i = 0; i < hw_rs->count; i++) { r = hw_rs->rates[i] & BRCMS_RATE_MASK; if (rateset[r]) rs->rates[count++] = rateset[r]; } rs->count = count; /* only set the mcs rate bit if the equivalent hw mcs bit is set */ for (i = 0; i < MCSSET_LEN; i++) rs->mcs[i] = (rs->mcs[i] & hw_rs->mcs[i]); if (brcms_c_rateset_valid(rs, check_brate)) return true; else return false; } /* calculate the rate of a rx'd frame and return it as a ratespec */ u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp) { int phy_type; u32 rspec = PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT; phy_type = ((rxh->RxChan & RXS_CHAN_PHYTYPE_MASK) >> RXS_CHAN_PHYTYPE_SHIFT); if ((phy_type == PHY_TYPE_N) || (phy_type == PHY_TYPE_SSN) || (phy_type == PHY_TYPE_LCN) || (phy_type == PHY_TYPE_HT)) { switch (rxh->PhyRxStatus_0 & PRXS0_FT_MASK) { case PRXS0_CCK: rspec = cck_phy2mac_rate( ((struct cck_phy_hdr *) plcp)->signal); break; case PRXS0_OFDM: rspec = ofdm_phy2mac_rate( ((struct ofdm_phy_hdr *) plcp)->rlpt[0]); break; case PRXS0_PREN: rspec = (plcp[0] & MIMO_PLCP_MCS_MASK) | RSPEC_MIMORATE; if (plcp[0] & MIMO_PLCP_40MHZ) { /* indicate rspec is for 40 MHz mode */ rspec &= ~RSPEC_BW_MASK; rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); } break; case PRXS0_STDN: /* fallthru */ default: /* not supported, error condition */ break; } if (plcp3_issgi(plcp[3])) rspec |= RSPEC_SHORT_GI; } else if ((phy_type == PHY_TYPE_A) || (rxh->PhyRxStatus_0 & PRXS0_OFDM)) rspec = ofdm_phy2mac_rate( ((struct ofdm_phy_hdr *) plcp)->rlpt[0]); else rspec = cck_phy2mac_rate( ((struct cck_phy_hdr *) plcp)->signal); return rspec; } /* copy rateset src to dst as-is (no masking or sorting) */ void brcms_c_rateset_copy(const struct brcms_c_rateset *src, struct brcms_c_rateset *dst) { memcpy(dst, src, sizeof(struct brcms_c_rateset)); } /* * Copy and selectively filter one rateset to another. * 'basic_only' means only copy basic rates. * 'rates' indicates cck (11b) and ofdm rates combinations. * - 0: cck and ofdm * - 1: cck only * - 2: ofdm only * 'xmask' is the copy mask (typically 0x7f or 0xff). */ void brcms_c_rateset_filter(struct brcms_c_rateset *src, struct brcms_c_rateset *dst, bool basic_only, u8 rates, uint xmask, bool mcsallow) { uint i; uint r; uint count; count = 0; for (i = 0; i < src->count; i++) { r = src->rates[i]; if (basic_only && !(r & BRCMS_RATE_FLAG)) continue; if (rates == BRCMS_RATES_CCK && is_ofdm_rate((r & BRCMS_RATE_MASK))) continue; if (rates == BRCMS_RATES_OFDM && is_cck_rate((r & BRCMS_RATE_MASK))) continue; dst->rates[count++] = r & xmask; } dst->count = count; dst->htphy_membership = src->htphy_membership; if (mcsallow && rates != BRCMS_RATES_CCK) memcpy(&dst->mcs[0], &src->mcs[0], MCSSET_LEN); else brcms_c_rateset_mcs_clear(dst); } /* select rateset for a given phy_type and bandtype and filter it, sort it * and fill rs_tgt with result */ void brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, const struct brcms_c_rateset *rs_hw, uint phy_type, int bandtype, bool cck_only, uint rate_mask, bool mcsallow, u8 bw, u8 txstreams) { const struct brcms_c_rateset *rs_dflt; struct brcms_c_rateset rs_sel; if ((PHYTYPE_IS(phy_type, PHY_TYPE_HT)) || (PHYTYPE_IS(phy_type, PHY_TYPE_N)) || (PHYTYPE_IS(phy_type, PHY_TYPE_LCN)) || (PHYTYPE_IS(phy_type, PHY_TYPE_SSN))) { if (bandtype == BRCM_BAND_5G) rs_dflt = (bw == BRCMS_20_MHZ ? &ofdm_mimo_rates : &ofdm_40bw_mimo_rates); else rs_dflt = (bw == BRCMS_20_MHZ ? &cck_ofdm_mimo_rates : &cck_ofdm_40bw_mimo_rates); } else if (PHYTYPE_IS(phy_type, PHY_TYPE_LP)) { rs_dflt = (bandtype == BRCM_BAND_5G) ? &ofdm_rates : &cck_ofdm_rates; } else if (PHYTYPE_IS(phy_type, PHY_TYPE_A)) { rs_dflt = &ofdm_rates; } else if (PHYTYPE_IS(phy_type, PHY_TYPE_G)) { rs_dflt = &cck_ofdm_rates; } else { /* should not happen, error condition */ rs_dflt = &cck_rates; /* force cck */ } /* if hw rateset is not supplied, assign selected rateset to it */ if (!rs_hw) rs_hw = rs_dflt; brcms_c_rateset_copy(rs_dflt, &rs_sel); brcms_c_rateset_mcs_upd(&rs_sel, txstreams); brcms_c_rateset_filter(&rs_sel, rs_tgt, false, cck_only ? BRCMS_RATES_CCK : BRCMS_RATES_CCK_OFDM, rate_mask, mcsallow); brcms_c_rate_hwrs_filter_sort_validate(rs_tgt, rs_hw, false, mcsallow ? txstreams : 1); } s16 brcms_c_rate_legacy_phyctl(uint rate) { uint i; for (i = 0; i < LEGACY_PHYCFG_TABLE_SIZE; i++) if (rate == legacy_phycfg_table[i].rate_ofdm) return legacy_phycfg_table[i].tx_phy_ctl3; return -1; } void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset) { uint i; for (i = 0; i < MCSSET_LEN; i++) rateset->mcs[i] = 0; } void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams) { memcpy(&rateset->mcs[0], &cck_ofdm_mimo_rates.mcs[0], MCSSET_LEN); brcms_c_rateset_mcs_upd(rateset, txstreams); } /* Based on bandwidth passed, allow/disallow MCS 32 in the rateset */ void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw) { if (bw == BRCMS_40_MHZ) setbit(rateset->mcs, 32); else clrbit(rateset->mcs, 32); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/pub.h0000644000175000017500000003320012026211315024524 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_PUB_H_ #define _BRCM_PUB_H_ #include #include #include "types.h" #include "defs.h" #define BRCMS_NUMRATES 16 /* max # of rates in a rateset */ /* phy types */ #define PHY_TYPE_A 0 /* Phy type A */ #define PHY_TYPE_G 2 /* Phy type G */ #define PHY_TYPE_N 4 /* Phy type N */ #define PHY_TYPE_LP 5 /* Phy type Low Power A/B/G */ #define PHY_TYPE_SSN 6 /* Phy type Single Stream N */ #define PHY_TYPE_LCN 8 /* Phy type Single Stream N */ #define PHY_TYPE_LCNXN 9 /* Phy type 2-stream N */ #define PHY_TYPE_HT 7 /* Phy type 3-Stream N */ /* bw */ #define BRCMS_10_MHZ 10 /* 10Mhz nphy channel bandwidth */ #define BRCMS_20_MHZ 20 /* 20Mhz nphy channel bandwidth */ #define BRCMS_40_MHZ 40 /* 40Mhz nphy channel bandwidth */ #define BRCMS_RSSI_MINVAL -200 /* Low value, e.g. for forcing roam */ #define BRCMS_RSSI_NO_SIGNAL -91 /* NDIS RSSI link quality cutoffs */ #define BRCMS_RSSI_VERY_LOW -80 /* Very low quality cutoffs */ #define BRCMS_RSSI_LOW -70 /* Low quality cutoffs */ #define BRCMS_RSSI_GOOD -68 /* Good quality cutoffs */ #define BRCMS_RSSI_VERY_GOOD -58 /* Very good quality cutoffs */ #define BRCMS_RSSI_EXCELLENT -57 /* Excellent quality cutoffs */ /* a large TX Power as an init value to factor out of min() calculations, * keep low enough to fit in an s8, units are .25 dBm */ #define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ /* rate related definitions */ #define BRCMS_RATE_FLAG 0x80 /* Flag to indicate it is a basic rate */ #define BRCMS_RATE_MASK 0x7f /* Rate value mask w/o basic rate flag */ /* legacy rx Antenna diversity for SISO rates */ #define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */ #define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */ #define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */ #define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */ #define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */ /* default antdiv setting */ #define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 /* legacy rx Antenna diversity for SISO rates */ /* Tx on antenna 0, "legacy term Main" */ #define ANT_TX_FORCE_0 0 /* Tx on antenna 1, "legacy term Aux" */ #define ANT_TX_FORCE_1 1 /* Tx on phy's last good Rx antenna */ #define ANT_TX_LAST_RX 3 /* driver's default tx antenna setting */ #define ANT_TX_DEF 3 /* Tx Chain values */ /* def bitmap of txchain */ #define TXCHAIN_DEF 0x1 /* default bitmap of tx chains for nphy */ #define TXCHAIN_DEF_NPHY 0x3 /* default bitmap of tx chains for nphy */ #define TXCHAIN_DEF_HTPHY 0x7 /* def bitmap of rxchain */ #define RXCHAIN_DEF 0x1 /* default bitmap of rx chains for nphy */ #define RXCHAIN_DEF_NPHY 0x3 /* default bitmap of rx chains for nphy */ #define RXCHAIN_DEF_HTPHY 0x7 /* no antenna switch */ #define ANTSWITCH_NONE 0 /* antenna switch on 4321CB2, 2of3 */ #define ANTSWITCH_TYPE_1 1 /* antenna switch on 4321MPCI, 2of3 */ #define ANTSWITCH_TYPE_2 2 /* antenna switch on 4322, 2of3 */ #define ANTSWITCH_TYPE_3 3 #define RXBUFSZ PKTBUFSZ #define MAX_STREAMS_SUPPORTED 4 /* max number of streams supported */ struct brcm_rateset { /* # rates in this set */ u32 count; /* rates in 500kbps units w/hi bit set if basic */ u8 rates[WL_NUMRATES]; }; struct brcms_c_rateset { uint count; /* number of rates in rates[] */ /* rates in 500kbps units w/hi bit set if basic */ u8 rates[BRCMS_NUMRATES]; u8 htphy_membership; /* HT PHY Membership */ u8 mcs[MCSSET_LEN]; /* supported mcs index bit map */ }; /* All the HT-specific default advertised capabilities (including AMPDU) * should be grouped here at one place */ #define AMPDU_DEF_MPDU_DENSITY 6 /* default mpdu density (110 ==> 4us) */ /* wlc internal bss_info */ struct brcms_bss_info { u8 BSSID[ETH_ALEN]; /* network BSSID */ u16 flags; /* flags for internal attributes */ u8 SSID_len; /* the length of SSID */ u8 SSID[32]; /* SSID string */ s16 RSSI; /* receive signal strength (in dBm) */ s16 SNR; /* receive signal SNR in dB */ u16 beacon_period; /* units are Kusec */ u16 chanspec; /* Channel num, bw, ctrl_sb and band */ struct brcms_c_rateset rateset; /* supported rates */ }; #define MAC80211_PROMISC_BCNS (1 << 0) #define MAC80211_SCAN (1 << 1) /* * Public portion of common driver state structure. * The wlc handle points at this. */ struct brcms_pub { struct brcms_c_info *wlc; struct ieee80211_hw *ieee_hw; struct scb_ampdu *global_ampdu; uint mac80211_state; uint unit; /* device instance number */ uint corerev; /* core revision */ struct si_pub *sih; /* SI handle (cookie for siutils calls) */ bool up; /* interface up and running */ bool hw_off; /* HW is off */ bool hw_up; /* one time hw up/down */ bool _piomode; /* true if pio mode */ uint _nbands; /* # bands supported */ uint now; /* # elapsed seconds */ bool delayed_down; /* down delayed */ bool associated; /* true:part of [I]BSS, false: not */ /* (union of stas_associated, aps_associated) */ bool _ampdu; /* ampdu enabled or not */ u8 _n_enab; /* bitmap of 11N + HT support */ u8 cur_etheraddr[ETH_ALEN]; /* our local ethernet address */ int bcmerror; /* last bcm error */ u32 radio_disabled; /* bit vector for radio disabled reasons */ u16 boardrev; /* version # of particular board */ u8 sromrev; /* version # of the srom */ char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */ u32 boardflags; /* Board specific flags from srom */ u32 boardflags2; /* More board flags if sromrev >= 4 */ bool phy_11ncapable; /* the PHY/HW is capable of 802.11N */ struct wl_cnt *_cnt; /* low-level counters in driver */ }; enum wlc_par_id { IOV_MPC = 1, IOV_RTSTHRESH, IOV_QTXPOWER, IOV_BCN_LI_BCN /* Beacon listen interval in # of beacons */ }; /*********************************************** * Feature-related macros to optimize out code * * ********************************************* */ #define ENAB_1x1 0x01 #define ENAB_2x2 0x02 #define ENAB_3x3 0x04 #define ENAB_4x4 0x08 #define SUPPORT_11N (ENAB_1x1|ENAB_2x2) #define SUPPORT_HT (ENAB_1x1|ENAB_2x2|ENAB_3x3) /* WL11N Support */ #define AMPDU_AGG_HOST 1 /* pri is priority encoded in the packet. This maps the Packet priority to * enqueue precedence as defined in wlc_prec_map */ extern const u8 wlc_prio2prec_map[]; #define BRCMS_PRIO_TO_PREC(pri) wlc_prio2prec_map[(pri) & 7] #define BRCMS_PREC_COUNT 16 /* Max precedence level implemented */ /* Mask to describe all precedence levels */ #define BRCMS_PREC_BMP_ALL MAXBITVAL(BRCMS_PREC_COUNT) /* * This maps priority to one precedence higher - Used by PS-Poll response * packets to simulate enqueue-at-head operation, but still maintain the * order on the queue */ #define BRCMS_PRIO_TO_HI_PREC(pri) min(BRCMS_PRIO_TO_PREC(pri) + 1,\ BRCMS_PREC_COUNT - 1) /* Define a bitmap of precedences comprised by each AC */ #define BRCMS_PREC_BMP_AC_BE (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_BE)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_BE)) | \ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_EE)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_EE))) #define BRCMS_PREC_BMP_AC_BK (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_BK)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_BK)) | \ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_NONE)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_NONE))) #define BRCMS_PREC_BMP_AC_VI (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_CL)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_CL)) | \ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_VI)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_VI))) #define BRCMS_PREC_BMP_AC_VO (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_VO)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_VO)) | \ NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_NC)) | \ NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_NC))) /* network protection config */ #define BRCMS_PROT_G_SPEC 1 /* SPEC g protection */ #define BRCMS_PROT_G_OVR 2 /* SPEC g prot override */ #define BRCMS_PROT_G_USER 3 /* gmode specified by user */ #define BRCMS_PROT_OVERLAP 4 /* overlap */ #define BRCMS_PROT_N_USER 10 /* nmode specified by user */ #define BRCMS_PROT_N_CFG 11 /* n protection */ #define BRCMS_PROT_N_CFG_OVR 12 /* n protection override */ #define BRCMS_PROT_N_NONGF 13 /* non-GF protection */ #define BRCMS_PROT_N_NONGF_OVR 14 /* non-GF protection override */ #define BRCMS_PROT_N_PAM_OVR 15 /* n preamble override */ #define BRCMS_PROT_N_OBSS 16 /* non-HT OBSS present */ /* * 54g modes (basic bits may still be overridden) * * GMODE_LEGACY_B * Rateset: 1b, 2b, 5.5, 11 * Preamble: Long * Shortslot: Off * GMODE_AUTO * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 * Extended Rateset: 6, 9, 12, 48 * Preamble: Long * Shortslot: Auto * GMODE_ONLY * Rateset: 1b, 2b, 5.5b, 11b, 18, 24b, 36, 54 * Extended Rateset: 6b, 9, 12b, 48 * Preamble: Short required * Shortslot: Auto * GMODE_B_DEFERRED * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 * Extended Rateset: 6, 9, 12, 48 * Preamble: Long * Shortslot: On * GMODE_PERFORMANCE * Rateset: 1b, 2b, 5.5b, 6b, 9, 11b, 12b, 18, 24b, 36, 48, 54 * Preamble: Short required * Shortslot: On and required * GMODE_LRS * Rateset: 1b, 2b, 5.5b, 11b * Extended Rateset: 6, 9, 12, 18, 24, 36, 48, 54 * Preamble: Long * Shortslot: Auto */ #define GMODE_LEGACY_B 0 #define GMODE_AUTO 1 #define GMODE_ONLY 2 #define GMODE_B_DEFERRED 3 #define GMODE_PERFORMANCE 4 #define GMODE_LRS 5 #define GMODE_MAX 6 /* MCS values greater than this enable multiple streams */ #define HIGHEST_SINGLE_STREAM_MCS 7 #define MAXBANDS 2 /* Maximum #of bands */ /* max number of antenna configurations */ #define ANT_SELCFG_MAX 4 struct brcms_antselcfg { u8 ant_config[ANT_SELCFG_MAX]; /* antenna configuration */ u8 num_antcfg; /* number of available antenna configurations */ }; /* common functions for every port */ extern struct brcms_c_info * brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, bool piomode, uint *perr); extern uint brcms_c_detach(struct brcms_c_info *wlc); extern int brcms_c_up(struct brcms_c_info *wlc); extern uint brcms_c_down(struct brcms_c_info *wlc); extern bool brcms_c_chipmatch(struct bcma_device *core); extern void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); extern void brcms_c_reset(struct brcms_c_info *wlc); extern void brcms_c_intrson(struct brcms_c_info *wlc); extern u32 brcms_c_intrsoff(struct brcms_c_info *wlc); extern void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask); extern bool brcms_c_intrsupd(struct brcms_c_info *wlc); extern bool brcms_c_isr(struct brcms_c_info *wlc, bool *wantdpc); extern bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded); extern void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, struct ieee80211_hw *hw); extern bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid); extern void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val); extern int brcms_c_get_header_len(void); extern void brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, const u8 *addr); extern void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, const struct ieee80211_tx_queue_params *arg, bool suspend); extern struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc); extern void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, u16 tid); extern void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, u8 ba_wsize, uint max_rx_ampdu_bytes); extern int brcms_c_module_register(struct brcms_pub *pub, const char *name, struct brcms_info *hdl, int (*down_fn)(void *handle)); extern int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, struct brcms_info *hdl); extern void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc); extern void brcms_c_enable_mac(struct brcms_c_info *wlc); extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); extern void brcms_c_scan_start(struct brcms_c_info *wlc); extern void brcms_c_scan_stop(struct brcms_c_info *wlc); extern int brcms_c_get_curband(struct brcms_c_info *wlc); extern void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop); extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc, struct brcm_rateset *currs); extern int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs); extern int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period); extern u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx); extern void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override); extern void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval); extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); extern int brcms_c_get_tx_power(struct brcms_c_info *wlc); extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); extern void brcms_c_mute(struct brcms_c_info *wlc, bool on); #endif /* _BRCM_PUB_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/pmu.h0000644000175000017500000000255012026211315024543 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_PMU_H_ #define _BRCM_PMU_H_ #include "types.h" extern u16 si_pmu_fast_pwrup_delay(struct si_pub *sih); extern void si_pmu_sprom_enable(struct si_pub *sih, bool enable); extern u32 si_pmu_chipcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val); extern u32 si_pmu_regcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val); extern u32 si_pmu_alp_clock(struct si_pub *sih); extern void si_pmu_pllupd(struct si_pub *sih); extern u32 si_pmu_pllcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val); extern u32 si_pmu_measure_alpclk(struct si_pub *sih); #endif /* _BRCM_PMU_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/pmu.c0000644000175000017500000001327712026211315024546 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "pub.h" #include "aiutils.h" #include "pmu.h" #include "soc.h" /* * external LPO crystal frequency */ #define EXT_ILP_HZ 32768 /* * Duration for ILP clock frequency measurment in milliseconds * * remark: 1000 must be an integer multiple of this duration */ #define ILP_CALC_DUR 10 /* Fields in pmucontrol */ #define PCTL_ILP_DIV_MASK 0xffff0000 #define PCTL_ILP_DIV_SHIFT 16 #define PCTL_PLL_PLLCTL_UPD 0x00000400 /* rev 2 */ #define PCTL_NOILP_ON_WAIT 0x00000200 /* rev 1 */ #define PCTL_HT_REQ_EN 0x00000100 #define PCTL_ALP_REQ_EN 0x00000080 #define PCTL_XTALFREQ_MASK 0x0000007c #define PCTL_XTALFREQ_SHIFT 2 #define PCTL_ILP_DIV_EN 0x00000002 #define PCTL_LPO_SEL 0x00000001 /* ILP clock */ #define ILP_CLOCK 32000 /* ALP clock on pre-PMU chips */ #define ALP_CLOCK 20000000 /* pmustatus */ #define PST_EXTLPOAVAIL 0x0100 #define PST_WDRESET 0x0080 #define PST_INTPEND 0x0040 #define PST_SBCLKST 0x0030 #define PST_SBCLKST_ILP 0x0010 #define PST_SBCLKST_ALP 0x0020 #define PST_SBCLKST_HT 0x0030 #define PST_ALPAVAIL 0x0008 #define PST_HTAVAIL 0x0004 #define PST_RESINIT 0x0003 /* PMU resource bit position */ #define PMURES_BIT(bit) (1 << (bit)) /* PMU corerev and chip specific PLL controls. * PMU_PLL_XX where is PMU corerev and is an arbitrary * number to differentiate different PLLs controlled by the same PMU rev. */ /* pmu XtalFreqRatio */ #define PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF #define PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 #define PMU_XTALFREQ_REG_MEASURE_SHIFT 31 /* 4313 resources */ #define RES4313_BB_PU_RSRC 0 #define RES4313_ILP_REQ_RSRC 1 #define RES4313_XTAL_PU_RSRC 2 #define RES4313_ALP_AVAIL_RSRC 3 #define RES4313_RADIO_PU_RSRC 4 #define RES4313_BG_PU_RSRC 5 #define RES4313_VREG1P4_PU_RSRC 6 #define RES4313_AFE_PWRSW_RSRC 7 #define RES4313_RX_PWRSW_RSRC 8 #define RES4313_TX_PWRSW_RSRC 9 #define RES4313_BB_PWRSW_RSRC 10 #define RES4313_SYNTH_PWRSW_RSRC 11 #define RES4313_MISC_PWRSW_RSRC 12 #define RES4313_BB_PLL_PWRSW_RSRC 13 #define RES4313_HT_AVAIL_RSRC 14 #define RES4313_MACPHY_CLK_AVAIL_RSRC 15 u16 si_pmu_fast_pwrup_delay(struct si_pub *sih) { uint delay = PMU_MAX_TRANSITION_DLY; switch (ai_get_chip_id(sih)) { case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43225: case BCMA_CHIP_ID_BCM4313: delay = 3700; break; default: break; } return (u16) delay; } /* Read/write a chipcontrol reg */ u32 si_pmu_chipcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val) { ai_cc_reg(sih, offsetof(struct chipcregs, chipcontrol_addr), ~0, reg); return ai_cc_reg(sih, offsetof(struct chipcregs, chipcontrol_data), mask, val); } /* Read/write a regcontrol reg */ u32 si_pmu_regcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val) { ai_cc_reg(sih, offsetof(struct chipcregs, regcontrol_addr), ~0, reg); return ai_cc_reg(sih, offsetof(struct chipcregs, regcontrol_data), mask, val); } /* Read/write a pllcontrol reg */ u32 si_pmu_pllcontrol(struct si_pub *sih, uint reg, u32 mask, u32 val) { ai_cc_reg(sih, offsetof(struct chipcregs, pllcontrol_addr), ~0, reg); return ai_cc_reg(sih, offsetof(struct chipcregs, pllcontrol_data), mask, val); } /* PMU PLL update */ void si_pmu_pllupd(struct si_pub *sih) { ai_cc_reg(sih, offsetof(struct chipcregs, pmucontrol), PCTL_PLL_PLLCTL_UPD, PCTL_PLL_PLLCTL_UPD); } /* query alp/xtal clock frequency */ u32 si_pmu_alp_clock(struct si_pub *sih) { u32 clock = ALP_CLOCK; /* bail out with default */ if (!(ai_get_cccaps(sih) & CC_CAP_PMU)) return clock; switch (ai_get_chip_id(sih)) { case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43225: case BCMA_CHIP_ID_BCM4313: /* always 20Mhz */ clock = 20000 * 1000; break; default: break; } return clock; } u32 si_pmu_measure_alpclk(struct si_pub *sih) { struct si_info *sii = container_of(sih, struct si_info, pub); struct bcma_device *core; u32 alp_khz; if (ai_get_pmurev(sih) < 10) return 0; /* Remember original core before switch to chipc */ core = sii->icbus->drv_cc.core; if (bcma_read32(core, CHIPCREGOFFS(pmustatus)) & PST_EXTLPOAVAIL) { u32 ilp_ctr, alp_hz; /* * Enable the reg to measure the freq, * in case it was disabled before */ bcma_write32(core, CHIPCREGOFFS(pmu_xtalfreq), 1U << PMU_XTALFREQ_REG_MEASURE_SHIFT); /* Delay for well over 4 ILP clocks */ udelay(1000); /* Read the latched number of ALP ticks per 4 ILP ticks */ ilp_ctr = bcma_read32(core, CHIPCREGOFFS(pmu_xtalfreq)) & PMU_XTALFREQ_REG_ILPCTR_MASK; /* * Turn off the PMU_XTALFREQ_REG_MEASURE_SHIFT * bit to save power */ bcma_write32(core, CHIPCREGOFFS(pmu_xtalfreq), 0); /* Calculate ALP frequency */ alp_hz = (ilp_ctr * EXT_ILP_HZ) / 4; /* * Round to nearest 100KHz, and at * the same time convert to KHz */ alp_khz = (alp_hz + 50000) / 100000 * 100; } else alp_khz = 0; return alp_khz; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h0000644000175000017500000001631212026211315025563 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * phy_shim.h: stuff defined in phy_shim.c and included only by the phy */ #ifndef _BRCM_PHY_SHIM_H_ #define _BRCM_PHY_SHIM_H_ #include "types.h" #define RADAR_TYPE_NONE 0 /* Radar type None */ #define RADAR_TYPE_ETSI_1 1 /* ETSI 1 Radar type */ #define RADAR_TYPE_ETSI_2 2 /* ETSI 2 Radar type */ #define RADAR_TYPE_ETSI_3 3 /* ETSI 3 Radar type */ #define RADAR_TYPE_ITU_E 4 /* ITU E Radar type */ #define RADAR_TYPE_ITU_K 5 /* ITU K Radar type */ #define RADAR_TYPE_UNCLASSIFIED 6 /* Unclassified Radar type */ #define RADAR_TYPE_BIN5 7 /* long pulse radar type */ #define RADAR_TYPE_STG2 8 /* staggered-2 radar */ #define RADAR_TYPE_STG3 9 /* staggered-3 radar */ #define RADAR_TYPE_FRA 10 /* French radar */ /* French radar pulse widths */ #define FRA_T1_20MHZ 52770 #define FRA_T2_20MHZ 61538 #define FRA_T3_20MHZ 66002 #define FRA_T1_40MHZ 105541 #define FRA_T2_40MHZ 123077 #define FRA_T3_40MHZ 132004 #define FRA_ERR_20MHZ 60 #define FRA_ERR_40MHZ 120 #define ANTSEL_NA 0 /* No boardlevel selection available */ #define ANTSEL_2x4 1 /* 2x4 boardlevel selection available */ #define ANTSEL_2x3 2 /* 2x3 CB2 boardlevel selection available */ /* Rx Antenna diversity control values */ #define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */ #define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */ #define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */ #define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */ #define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */ #define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 /* default antdiv setting */ #define WL_ANT_RX_MAX 2 /* max 2 receive antennas */ #define WL_ANT_HT_RX_MAX 3 /* max 3 receive antennas/cores */ #define WL_ANT_IDX_1 0 /* antenna index 1 */ #define WL_ANT_IDX_2 1 /* antenna index 2 */ /* values for n_preamble_type */ #define BRCMS_N_PREAMBLE_MIXEDMODE 0 #define BRCMS_N_PREAMBLE_GF 1 #define BRCMS_N_PREAMBLE_GF_BRCM 2 #define WL_TX_POWER_RATES_LEGACY 45 #define WL_TX_POWER_MCS20_FIRST 12 #define WL_TX_POWER_MCS20_NUM 16 #define WL_TX_POWER_MCS40_FIRST 28 #define WL_TX_POWER_MCS40_NUM 17 #define WL_TX_POWER_RATES 101 #define WL_TX_POWER_CCK_FIRST 0 #define WL_TX_POWER_CCK_NUM 4 /* Index for first 20MHz OFDM SISO rate */ #define WL_TX_POWER_OFDM_FIRST 4 /* Index for first 20MHz OFDM CDD rate */ #define WL_TX_POWER_OFDM20_CDD_FIRST 12 /* Index for first 40MHz OFDM SISO rate */ #define WL_TX_POWER_OFDM40_SISO_FIRST 52 /* Index for first 40MHz OFDM CDD rate */ #define WL_TX_POWER_OFDM40_CDD_FIRST 60 #define WL_TX_POWER_OFDM_NUM 8 /* Index for first 20MHz MCS SISO rate */ #define WL_TX_POWER_MCS20_SISO_FIRST 20 /* Index for first 20MHz MCS CDD rate */ #define WL_TX_POWER_MCS20_CDD_FIRST 28 /* Index for first 20MHz MCS STBC rate */ #define WL_TX_POWER_MCS20_STBC_FIRST 36 /* Index for first 20MHz MCS SDM rate */ #define WL_TX_POWER_MCS20_SDM_FIRST 44 /* Index for first 40MHz MCS SISO rate */ #define WL_TX_POWER_MCS40_SISO_FIRST 68 /* Index for first 40MHz MCS CDD rate */ #define WL_TX_POWER_MCS40_CDD_FIRST 76 /* Index for first 40MHz MCS STBC rate */ #define WL_TX_POWER_MCS40_STBC_FIRST 84 /* Index for first 40MHz MCS SDM rate */ #define WL_TX_POWER_MCS40_SDM_FIRST 92 #define WL_TX_POWER_MCS_1_STREAM_NUM 8 #define WL_TX_POWER_MCS_2_STREAM_NUM 8 /* Index for 40MHz rate MCS 32 */ #define WL_TX_POWER_MCS_32 100 #define WL_TX_POWER_MCS_32_NUM 1 /* sslpnphy specifics */ /* Index for first 20MHz MCS SISO rate */ #define WL_TX_POWER_MCS20_SISO_FIRST_SSN 12 /* struct tx_power::flags bits */ #define WL_TX_POWER_F_ENABLED 1 #define WL_TX_POWER_F_HW 2 #define WL_TX_POWER_F_MIMO 4 #define WL_TX_POWER_F_SISO 8 /* values to force tx/rx chain */ #define BRCMS_N_TXRX_CHAIN0 0 #define BRCMS_N_TXRX_CHAIN1 1 struct brcms_phy; extern struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, struct brcms_info *wl, struct brcms_c_info *wlc); extern void wlc_phy_shim_detach(struct phy_shim_info *physhim); /* PHY to WL utility functions */ extern struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, void (*fn) (struct brcms_phy *pi), void *arg, const char *name); extern void wlapi_free_timer(struct wlapi_timer *t); extern void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); extern bool wlapi_del_timer(struct wlapi_timer *t); extern void wlapi_intrson(struct phy_shim_info *physhim); extern u32 wlapi_intrsoff(struct phy_shim_info *physhim); extern void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask); extern void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v); extern u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset); extern void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, u16 val, int bands); extern void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags); extern void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim); extern void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode); extern void wlapi_enable_mac(struct phy_shim_info *physhim); extern void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val); extern void wlapi_bmac_phy_reset(struct phy_shim_info *physhim); extern void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw); extern void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk); extern void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk); extern void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on); extern void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim); extern void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info * physhim); extern void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info * physhim); extern void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o, int len, void *buf); extern u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate); extern void wlapi_ucode_sample_init(struct phy_shim_info *physhim); extern void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, void *buf, int, u32 sel); extern void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, const void *buf, int, u32); extern void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, u32 phy_mode); extern u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim); #endif /* _BRCM_PHY_SHIM_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c0000644000175000017500000001301712026211315025555 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This is "two-way" interface, acting as the SHIM layer between driver * and PHY layer. The driver can optionally call this translation layer * to do some preprocessing, then reach PHY. On the PHY->driver direction, * all calls go through this layer since PHY doesn't have access to the * driver's brcms_hardware pointer. */ #include #include #include "main.h" #include "mac80211_if.h" #include "phy_shim.h" /* PHY SHIM module specific state */ struct phy_shim_info { struct brcms_hardware *wlc_hw; /* pointer to main wlc_hw structure */ struct brcms_c_info *wlc; /* pointer to main wlc structure */ struct brcms_info *wl; /* pointer to os-specific private state */ }; struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, struct brcms_info *wl, struct brcms_c_info *wlc) { struct phy_shim_info *physhim = NULL; physhim = kzalloc(sizeof(struct phy_shim_info), GFP_ATOMIC); if (!physhim) return NULL; physhim->wlc_hw = wlc_hw; physhim->wlc = wlc; physhim->wl = wl; return physhim; } void wlc_phy_shim_detach(struct phy_shim_info *physhim) { kfree(physhim); } struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, void (*fn)(struct brcms_phy *pi), void *arg, const char *name) { return (struct wlapi_timer *) brcms_init_timer(physhim->wl, (void (*)(void *))fn, arg, name); } void wlapi_free_timer(struct wlapi_timer *t) { brcms_free_timer((struct brcms_timer *)t); } void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic) { brcms_add_timer((struct brcms_timer *)t, ms, periodic); } bool wlapi_del_timer(struct wlapi_timer *t) { return brcms_del_timer((struct brcms_timer *)t); } void wlapi_intrson(struct phy_shim_info *physhim) { brcms_intrson(physhim->wl); } u32 wlapi_intrsoff(struct phy_shim_info *physhim) { return brcms_intrsoff(physhim->wl); } void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask) { brcms_intrsrestore(physhim->wl, macintmask); } void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v) { brcms_b_write_shm(physhim->wlc_hw, offset, v); } u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset) { return brcms_b_read_shm(physhim->wlc_hw, offset); } void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, u16 val, int bands) { brcms_b_mhf(physhim->wlc_hw, idx, mask, val, bands); } void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags) { brcms_b_corereset(physhim->wlc_hw, flags); } void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim) { brcms_c_suspend_mac_and_wait(physhim->wlc); } void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode) { brcms_b_switch_macfreq(physhim->wlc_hw, spurmode); } void wlapi_enable_mac(struct phy_shim_info *physhim) { brcms_c_enable_mac(physhim->wlc); } void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val) { brcms_b_mctrl(physhim->wlc_hw, mask, val); } void wlapi_bmac_phy_reset(struct phy_shim_info *physhim) { brcms_b_phy_reset(physhim->wlc_hw); } void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw) { brcms_b_bw_set(physhim->wlc_hw, bw); } u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim) { return brcms_b_get_txant(physhim->wlc_hw); } void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk) { brcms_b_phyclk_fgc(physhim->wlc_hw, clk); } void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk) { brcms_b_macphyclk_set(physhim->wlc_hw, clk); } void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on) { brcms_b_core_phypll_ctl(physhim->wlc_hw, on); } void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim) { brcms_b_core_phypll_reset(physhim->wlc_hw); } void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim) { brcms_c_ucode_wake_override_set(physhim->wlc_hw, BRCMS_WAKE_OVERRIDE_PHYREG); } void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim) { brcms_c_ucode_wake_override_clear(physhim->wlc_hw, BRCMS_WAKE_OVERRIDE_PHYREG); } void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int offset, int len, void *buf) { brcms_b_write_template_ram(physhim->wlc_hw, offset, len, buf); } u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate) { return brcms_b_rate_shm_offset(physhim->wlc_hw, rate); } void wlapi_ucode_sample_init(struct phy_shim_info *physhim) { } void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint offset, void *buf, int len, u32 sel) { brcms_b_copyfrom_objmem(physhim->wlc_hw, offset, buf, len, sel); } void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint offset, const void *buf, int l, u32 sel) { brcms_b_copyto_objmem(physhim->wlc_hw, offset, buf, l, sel); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/main.h0000644000175000017500000005632612026211315024700 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_MAIN_H_ #define _BRCM_MAIN_H_ #include #include #include "types.h" #include "d11.h" #include "scb.h" #define INVCHANNEL 255 /* invalid channel */ /* max # brcms_c_module_register() calls */ #define BRCMS_MAXMODULES 22 #define SEQNUM_SHIFT 4 #define SEQNUM_MAX 0x1000 #define NTXRATE 64 /* # tx MPDUs rate is reported for */ /* Maximum wait time for a MAC suspend */ /* uS: 83mS is max packet time (64KB ampdu @ 6Mbps) */ #define BRCMS_MAX_MAC_SUSPEND 83000 /* responses for probe requests older that this are tossed, zero to disable */ #define BRCMS_PRB_RESP_TIMEOUT 0 /* Disable probe response timeout */ /* transmit buffer max headroom for protocol headers */ #define TXOFF (D11_TXH_LEN + D11_PHY_HDR_LEN) /* Macros for doing definition and get/set of bitfields * Usage example, e.g. a three-bit field (bits 4-6): * #define _M BITFIELD_MASK(3) * #define _S 4 * ... * regval = R_REG(osh, ®s->regfoo); * field = GFIELD(regval, ); * regval = SFIELD(regval, , 1); * W_REG(osh, ®s->regfoo, regval); */ #define BITFIELD_MASK(width) \ (((unsigned)1 << (width)) - 1) #define GFIELD(val, field) \ (((val) >> field ## _S) & field ## _M) #define SFIELD(val, field, bits) \ (((val) & (~(field ## _M << field ## _S))) | \ ((unsigned)(bits) << field ## _S)) #define SW_TIMER_MAC_STAT_UPD 30 /* periodic MAC stats update */ /* max # supported core revisions (0 .. MAXCOREREV - 1) */ #define MAXCOREREV 28 /* Double check that unsupported cores are not enabled */ #if CONF_MSK(D11CONF, 0x4f) || CONF_GE(D11CONF, MAXCOREREV) #error "Configuration for D11CONF includes unsupported versions." #endif /* Bad versions */ /* values for shortslot_override */ #define BRCMS_SHORTSLOT_AUTO -1 /* Driver will manage Shortslot setting */ #define BRCMS_SHORTSLOT_OFF 0 /* Turn off short slot */ #define BRCMS_SHORTSLOT_ON 1 /* Turn on short slot */ /* value for short/long and mixmode/greenfield preamble */ #define BRCMS_LONG_PREAMBLE (0) #define BRCMS_SHORT_PREAMBLE (1 << 0) #define BRCMS_GF_PREAMBLE (1 << 1) #define BRCMS_MM_PREAMBLE (1 << 2) #define BRCMS_IS_MIMO_PREAMBLE(_pre) (((_pre) == BRCMS_GF_PREAMBLE) || \ ((_pre) == BRCMS_MM_PREAMBLE)) /* TxFrameID */ /* seq and frag bits: SEQNUM_SHIFT, FRAGNUM_MASK (802.11.h) */ /* rate epoch bits: TXFID_RATE_SHIFT, TXFID_RATE_MASK ((wlc_rate.c) */ #define TXFID_QUEUE_MASK 0x0007 /* Bits 0-2 */ #define TXFID_SEQ_MASK 0x7FE0 /* Bits 5-15 */ #define TXFID_SEQ_SHIFT 5 /* Number of bit shifts */ #define TXFID_RATE_PROBE_MASK 0x8000 /* Bit 15 for rate probe */ #define TXFID_RATE_MASK 0x0018 /* Mask for bits 3 and 4 */ #define TXFID_RATE_SHIFT 3 /* Shift 3 bits for rate mask */ /* promote boardrev */ #define BOARDREV_PROMOTABLE 0xFF /* from */ #define BOARDREV_PROMOTED 1 /* to */ #define DATA_BLOCK_TX_SUPR (1 << 4) /* 802.1D Priority to TX FIFO number for wme */ extern const u8 prio2fifo[]; /* Ucode MCTL_WAKE override bits */ #define BRCMS_WAKE_OVERRIDE_CLKCTL 0x01 #define BRCMS_WAKE_OVERRIDE_PHYREG 0x02 #define BRCMS_WAKE_OVERRIDE_MACSUSPEND 0x04 #define BRCMS_WAKE_OVERRIDE_TXFIFO 0x08 #define BRCMS_WAKE_OVERRIDE_FORCEFAST 0x10 /* stuff pulled in from wlc.c */ /* Interrupt bit error summary. Don't include I_RU: we refill DMA at other * times; and if we run out, constant I_RU interrupts may cause lockup. We * will still get error counts from rx0ovfl. */ #define I_ERRORS (I_PC | I_PD | I_DE | I_RO | I_XU) /* default software intmasks */ #define DEF_RXINTMASK (I_RI) /* enable rx int on rxfifo only */ #define DEF_MACINTMASK (MI_TXSTOP | MI_TBTT | MI_ATIMWINEND | MI_PMQ | \ MI_PHYTXERR | MI_DMAINT | MI_TFS | MI_BG_NOISE | \ MI_CCA | MI_TO | MI_GP0 | MI_RFDISABLE | MI_PWRUP) #define MAXTXPKTS 6 /* max # pkts pending */ /* frameburst */ #define MAXTXFRAMEBURST 8 /* vanilla xpress mode: max frames/burst */ #define MAXFRAMEBURST_TXOP 10000 /* Frameburst TXOP in usec */ #define NFIFO 6 /* # tx/rx fifopairs */ /* PLL requests */ /* pll is shared on old chips */ #define BRCMS_PLLREQ_SHARED 0x1 /* hold pll for radio monitor register checking */ #define BRCMS_PLLREQ_RADIO_MON 0x2 /* hold/release pll for some short operation */ #define BRCMS_PLLREQ_FLIP 0x4 #define CHANNEL_BANDUNIT(wlc, ch) \ (((ch) <= CH_MAX_2G_CHANNEL) ? BAND_2G_INDEX : BAND_5G_INDEX) #define OTHERBANDUNIT(wlc) \ ((uint)((wlc)->band->bandunit ? BAND_2G_INDEX : BAND_5G_INDEX)) /* * 802.11 protection information * * _g: use g spec protection, driver internal. * g_override: override for use of g spec protection. * gmode_user: user config gmode, operating band->gmode is different. * overlap: Overlap BSS/IBSS protection for both 11g and 11n. * nmode_user: user config nmode, operating pub->nmode is different. * n_cfg: use OFDM protection on MIMO frames. * n_cfg_override: override for use of N protection. * nongf: non-GF present protection. * nongf_override: override for use of GF protection. * n_pam_override: override for preamble: MM or GF. * n_obss: indicated OBSS Non-HT STA present. */ struct brcms_protection { bool _g; s8 g_override; u8 gmode_user; s8 overlap; s8 nmode_user; s8 n_cfg; s8 n_cfg_override; bool nongf; s8 nongf_override; s8 n_pam_override; bool n_obss; }; /* * anything affecting the single/dual streams/antenna operation * * hw_txchain: HW txchain bitmap cfg. * txchain: txchain bitmap being used. * txstreams: number of txchains being used. * hw_rxchain: HW rxchain bitmap cfg. * rxchain: rxchain bitmap being used. * rxstreams: number of rxchains being used. * ant_rx_ovr: rx antenna override. * txant: userTx antenna setting. * phytxant: phyTx antenna setting in txheader. * ss_opmode: singlestream Operational mode, 0:siso; 1:cdd. * ss_algosel_auto: if true, use wlc->stf->ss_algo_channel; * else use wlc->band->stf->ss_mode_band. * ss_algo_channel: ss based on per-channel algo: 0: SISO, 1: CDD 2: STBC. * rxchain_restore_delay: delay time to restore default rxchain. * ldpc: AUTO/ON/OFF ldpc cap supported. * txcore[MAX_STREAMS_SUPPORTED + 1]: bitmap of selected core for each Nsts. * spatial_policy: */ struct brcms_stf { u8 hw_txchain; u8 txchain; u8 txstreams; u8 hw_rxchain; u8 rxchain; u8 rxstreams; u8 ant_rx_ovr; s8 txant; u16 phytxant; u8 ss_opmode; bool ss_algosel_auto; u16 ss_algo_channel; u8 rxchain_restore_delay; s8 ldpc; u8 txcore[MAX_STREAMS_SUPPORTED + 1]; s8 spatial_policy; }; #define BRCMS_STF_SS_STBC_TX(wlc, scb) \ (((wlc)->stf->txstreams > 1) && (((wlc)->band->band_stf_stbc_tx == ON) \ || (((scb)->flags & SCB_STBCCAP) && \ (wlc)->band->band_stf_stbc_tx == AUTO && \ isset(&((wlc)->stf->ss_algo_channel), PHY_TXC1_MODE_STBC)))) #define BRCMS_STBC_CAP_PHY(wlc) (BRCMS_ISNPHY(wlc->band) && \ NREV_GE(wlc->band->phyrev, 3)) #define BRCMS_SGI_CAP_PHY(wlc) ((BRCMS_ISNPHY(wlc->band) && \ NREV_GE(wlc->band->phyrev, 3)) || \ BRCMS_ISLCNPHY(wlc->band)) #define BRCMS_CHAN_PHYTYPE(x) (((x) & RXS_CHAN_PHYTYPE_MASK) \ >> RXS_CHAN_PHYTYPE_SHIFT) #define BRCMS_CHAN_CHANNEL(x) (((x) & RXS_CHAN_ID_MASK) \ >> RXS_CHAN_ID_SHIFT) /* * core state (mac) */ struct brcms_core { uint coreidx; /* # sb enumerated core */ /* fifo */ uint *txavail[NFIFO]; /* # tx descriptors available */ s16 txpktpend[NFIFO]; /* tx admission control */ struct macstat *macstat_snapshot; /* mac hw prev read values */ }; /* * band state (phy+ana+radio) */ struct brcms_band { int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */ uint bandunit; /* bandstate[] index */ u16 phytype; /* phytype */ u16 phyrev; u16 radioid; u16 radiorev; struct brcms_phy_pub *pi; /* pointer to phy specific information */ bool abgphy_encore; u8 gmode; /* currently active gmode */ struct scb *hwrs_scb; /* permanent scb for hw rateset */ /* band-specific copy of default_bss.rateset */ struct brcms_c_rateset defrateset; u8 band_stf_ss_mode; /* Configured STF type, 0:siso; 1:cdd */ s8 band_stf_stbc_tx; /* STBC TX 0:off; 1:force on; -1:auto */ /* rates supported by chip (phy-specific) */ struct brcms_c_rateset hw_rateset; u8 basic_rate[BRCM_MAXRATE + 1]; /* basic rates indexed by rate */ bool mimo_cap_40; /* 40 MHz cap enabled on this band */ s8 antgain; /* antenna gain from srom */ u16 CWmin; /* minimum size of contention window, in unit of aSlotTime */ u16 CWmax; /* maximum size of contention window, in unit of aSlotTime */ struct ieee80211_supported_band band; }; /* module control blocks */ struct modulecb { /* module name : NULL indicates empty array member */ char name[32]; /* handle passed when handler 'doiovar' is called */ struct brcms_info *hdl; int (*down_fn)(void *handle); /* down handler. Note: the int returned * by the down function is a count of the * number of timers that could not be * freed. */ }; struct brcms_hw_band { int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */ uint bandunit; /* bandstate[] index */ u16 mhfs[MHFMAX]; /* MHF array shadow */ u8 bandhw_stf_ss_mode; /* HW configured STF type, 0:siso; 1:cdd */ u16 CWmin; u16 CWmax; u32 core_flags; u16 phytype; /* phytype */ u16 phyrev; u16 radioid; u16 radiorev; struct brcms_phy_pub *pi; /* pointer to phy specific information */ bool abgphy_encore; }; struct brcms_hardware { bool _piomode; /* true if pio mode */ struct brcms_c_info *wlc; /* fifo */ struct dma_pub *di[NFIFO]; /* dma handles, per fifo */ uint unit; /* device instance number */ /* version info */ u16 vendorid; /* PCI vendor id */ u16 deviceid; /* PCI device id */ uint corerev; /* core revision */ u8 sromrev; /* version # of the srom */ u16 boardrev; /* version # of particular board */ u32 boardflags; /* Board specific flags from srom */ u32 boardflags2; /* More board flags if sromrev >= 4 */ u32 machwcap; /* MAC capabilities */ u32 machwcap_backup; /* backup of machwcap */ struct si_pub *sih; /* SI handle (cookie for siutils calls) */ struct bcma_device *d11core; /* pointer to 802.11 core */ struct phy_shim_info *physhim; /* phy shim layer handler */ struct shared_phy *phy_sh; /* pointer to shared phy state */ struct brcms_hw_band *band;/* pointer to active per-band state */ /* band state per phy/radio */ struct brcms_hw_band *bandstate[MAXBANDS]; u16 bmac_phytxant; /* cache of high phytxant state */ bool shortslot; /* currently using 11g ShortSlot timing */ u16 SRL; /* 802.11 dot11ShortRetryLimit */ u16 LRL; /* 802.11 dot11LongRetryLimit */ u16 SFBL; /* Short Frame Rate Fallback Limit */ u16 LFBL; /* Long Frame Rate Fallback Limit */ bool up; /* d11 hardware up and running */ uint now; /* # elapsed seconds */ uint _nbands; /* # bands supported */ u16 chanspec; /* bmac chanspec shadow */ uint *txavail[NFIFO]; /* # tx descriptors available */ const u16 *xmtfifo_sz; /* fifo size in 256B for each xmt fifo */ u32 pllreq; /* pll requests to keep PLL on */ u8 suspended_fifos; /* Which TX fifo to remain awake for */ u32 maccontrol; /* Cached value of maccontrol */ uint mac_suspend_depth; /* current depth of mac_suspend levels */ u32 wake_override; /* bit flags to force MAC to WAKE mode */ u32 mute_override; /* Prevent ucode from sending beacons */ u8 etheraddr[ETH_ALEN]; /* currently configured ethernet address */ bool noreset; /* true= do not reset hw, used by WLC_OUT */ bool forcefastclk; /* true if h/w is forcing to use fast clk */ bool clk; /* core is out of reset and has clock */ bool sbclk; /* sb has clock */ bool phyclk; /* phy is out of reset and has clock */ bool ucode_loaded; /* true after ucode downloaded */ u8 hw_stf_ss_opmode; /* STF single stream operation mode */ u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board */ u32 antsel_avail; /* * put struct antsel_info here if more info is * needed */ }; /* TX Queue information * * Each flow of traffic out of the device has a TX Queue with independent * flow control. Several interfaces may be associated with a single TX Queue * if they belong to the same flow of traffic from the device. For multi-channel * operation there are independent TX Queues for each channel. */ struct brcms_txq_info { struct brcms_txq_info *next; struct pktq q; uint stopped; /* tx flow control bits */ }; /* * Principal common driver data structure. * * pub: pointer to driver public state. * wl: pointer to specific private state. * hw: HW related state. * clkreq_override: setting for clkreq for PCIE : Auto, 0, 1. * fastpwrup_dly: time in us needed to bring up d11 fast clock. * macintstatus: bit channel between isr and dpc. * macintmask: sw runtime master macintmask value. * defmacintmask: default "on" macintmask value. * clk: core is out of reset and has clock. * core: pointer to active io core. * band: pointer to active per-band state. * corestate: per-core state (one per hw core). * bandstate: per-band state (one per phy/radio). * qvalid: DirFrmQValid and BcMcFrmQValid. * ampdu: ampdu module handler. * asi: antsel module handler. * cmi: channel manager module handler. * vendorid: PCI vendor id. * deviceid: PCI device id. * ucode_rev: microcode revision. * machwcap: MAC capabilities, BMAC shadow. * perm_etheraddr: original sprom local ethernet address. * bandlocked: disable auto multi-band switching. * bandinit_pending: track band init in auto band. * radio_monitor: radio timer is running. * going_down: down path intermediate variable. * wdtimer: timer for watchdog routine. * radio_timer: timer for hw radio button monitor routine. * monitor: monitor (MPDU sniffing) mode. * bcnmisc_monitor: bcns promisc mode override for monitor. * _rifs: enable per-packet rifs. * bcn_li_bcn: beacon listen interval in # beacons. * bcn_li_dtim: beacon listen interval in # dtims. * WDarmed: watchdog timer is armed. * WDlast: last time wlc_watchdog() was called. * edcf_txop[IEEE80211_NUM_ACS]: current txop for each ac. * wme_retries: per-AC retry limits. * tx_prec_map: Precedence map based on HW FIFO space. * fifo2prec_map[NFIFO]: pointer to fifo2_prec map based on WME. * bsscfg: set of BSS configurations, idx 0 is default and always valid. * cfg: the primary bsscfg (can be AP or STA). * tx_queues: common TX Queue list. * modulecb: * mimoft: SIGN or 11N. * cck_40txbw: 11N, cck tx b/w override when in 40MHZ mode. * ofdm_40txbw: 11N, ofdm tx b/w override when in 40MHZ mode. * mimo_40txbw: 11N, mimo tx b/w override when in 40MHZ mode. * default_bss: configured BSS parameters. * mc_fid_counter: BC/MC FIFO frame ID counter. * country_default: saved country for leaving 802.11d auto-country mode. * autocountry_default: initial country for 802.11d auto-country mode. * prb_resp_timeout: do not send prb resp if request older * than this, 0 = disable. * home_chanspec: shared home chanspec. * chanspec: target operational channel. * usr_fragthresh: user configured fragmentation threshold. * fragthresh[NFIFO]: per-fifo fragmentation thresholds. * RTSThresh: 802.11 dot11RTSThreshold. * SRL: 802.11 dot11ShortRetryLimit. * LRL: 802.11 dot11LongRetryLimit. * SFBL: Short Frame Rate Fallback Limit. * LFBL: Long Frame Rate Fallback Limit. * shortslot: currently using 11g ShortSlot timing. * shortslot_override: 11g ShortSlot override. * include_legacy_erp: include Legacy ERP info elt ID 47 as well as g ID 42. * PLCPHdr_override: 802.11b Preamble Type override. * stf: * bcn_rspec: save bcn ratespec purpose. * tempsense_lasttime; * tx_duty_cycle_ofdm: maximum allowed duty cycle for OFDM. * tx_duty_cycle_cck: maximum allowed duty cycle for CCK. * pkt_queue: txq for transmit packets. * wiphy: * pri_scb: primary Station Control Block */ struct brcms_c_info { struct brcms_pub *pub; struct brcms_info *wl; struct brcms_hardware *hw; /* clock */ u16 fastpwrup_dly; /* interrupt */ u32 macintstatus; u32 macintmask; u32 defmacintmask; bool clk; /* multiband */ struct brcms_core *core; struct brcms_band *band; struct brcms_core *corestate; struct brcms_band *bandstate[MAXBANDS]; /* packet queue */ uint qvalid; struct ampdu_info *ampdu; struct antsel_info *asi; struct brcms_cm_info *cmi; u16 vendorid; u16 deviceid; uint ucode_rev; u8 perm_etheraddr[ETH_ALEN]; bool bandlocked; bool bandinit_pending; bool radio_monitor; bool going_down; struct brcms_timer *wdtimer; struct brcms_timer *radio_timer; /* promiscuous */ uint filter_flags; /* driver feature */ bool _rifs; /* AP-STA synchronization, power save */ u8 bcn_li_bcn; u8 bcn_li_dtim; bool WDarmed; u32 WDlast; /* WME */ u16 edcf_txop[IEEE80211_NUM_ACS]; u16 wme_retries[IEEE80211_NUM_ACS]; u16 tx_prec_map; u16 fifo2prec_map[NFIFO]; struct brcms_bss_cfg *bsscfg; /* tx queue */ struct brcms_txq_info *tx_queues; struct modulecb *modulecb; u8 mimoft; s8 cck_40txbw; s8 ofdm_40txbw; s8 mimo_40txbw; struct brcms_bss_info *default_bss; u16 mc_fid_counter; char country_default[BRCM_CNTRY_BUF_SZ]; char autocountry_default[BRCM_CNTRY_BUF_SZ]; u16 prb_resp_timeout; u16 home_chanspec; /* PHY parameters */ u16 chanspec; u16 usr_fragthresh; u16 fragthresh[NFIFO]; u16 RTSThresh; u16 SRL; u16 LRL; u16 SFBL; u16 LFBL; /* network config */ bool shortslot; s8 shortslot_override; bool include_legacy_erp; struct brcms_protection *protection; s8 PLCPHdr_override; struct brcms_stf *stf; u32 bcn_rspec; uint tempsense_lasttime; u16 tx_duty_cycle_ofdm; u16 tx_duty_cycle_cck; struct brcms_txq_info *pkt_queue; struct wiphy *wiphy; struct scb pri_scb; }; /* antsel module specific state */ struct antsel_info { struct brcms_c_info *wlc; /* pointer to main wlc structure */ struct brcms_pub *pub; /* pointer to public fn */ u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board */ u8 antsel_antswitch; /* board level antenna switch type */ bool antsel_avail; /* Ant selection availability (SROM based) */ struct brcms_antselcfg antcfg_11n; /* antenna configuration */ struct brcms_antselcfg antcfg_cur; /* current antenna config (auto) */ }; /* * BSS configuration state * * wlc: wlc to which this bsscfg belongs to. * up: is this configuration up operational * enable: is this configuration enabled * associated: is BSS in ASSOCIATED state * BSS: infraustructure or adhoc * SSID_len: the length of SSID * SSID: SSID string * * * BSSID: BSSID (associated) * cur_etheraddr: h/w address * flags: BSSCFG flags; see below * * current_bss: BSS parms in ASSOCIATED state * * * ID: 'unique' ID of this bsscfg, assigned at bsscfg allocation */ struct brcms_bss_cfg { struct brcms_c_info *wlc; bool up; bool enable; bool associated; bool BSS; u8 SSID_len; u8 SSID[IEEE80211_MAX_SSID_LEN]; u8 BSSID[ETH_ALEN]; u8 cur_etheraddr[ETH_ALEN]; struct brcms_bss_info *current_bss; }; extern void brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p, bool commit, s8 txpktpend); extern void brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo, s8 txpktpend); extern void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb, struct sk_buff *sdu, uint prec); extern void brcms_c_print_txstatus(struct tx_status *txs); extern int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, uint *blocks); #if defined(DEBUG) extern void brcms_c_print_txdesc(struct d11txh *txh); #else static inline void brcms_c_print_txdesc(struct d11txh *txh) { } #endif extern int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); extern void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); extern void brcms_c_send_q(struct brcms_c_info *wlc); extern int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, uint *fifo); extern u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len); extern u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, bool use_rspec, u16 mimo_ctlchbw); extern u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, u32 rts_rate, u32 frame_rate, u8 rts_preamble_type, u8 frame_preamble_type, uint frame_len, bool ba); extern void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, struct ieee80211_sta *sta, void (*dma_callback_fn)); extern void brcms_c_update_beacon(struct brcms_c_info *wlc); extern void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend); extern int brcms_c_set_nmode(struct brcms_c_info *wlc); extern void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, u32 bcn_rate); extern void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type); extern void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, bool mute, struct txpwr_limits *txpwr); extern void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v); extern u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset); extern void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, int bands); extern void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags); extern void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val); extern void brcms_b_phy_reset(struct brcms_hardware *wlc_hw); extern void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw); extern void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw); extern void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, u32 override_bit); extern void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, u32 override_bit); extern void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len, void *buf); extern u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate); extern void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, const void *buf, int len, u32 sel); extern void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf, int len, u32 sel); extern void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode); extern u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw); extern void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk); extern void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk); extern void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on); extern void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant); extern void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode); extern void brcms_c_init_scb(struct scb *scb); #endif /* _BRCM_MAIN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h0000644000175000017500000000677212026211315025566 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_MAC80211_IF_H_ #define _BRCM_MAC80211_IF_H_ #include #include #include #include "ucode_loader.h" /* * Starting index for 5G rates in the * legacy rate table. */ #define BRCMS_LEGACY_5G_RATE_OFFSET 4 /* softmac ioctl definitions */ #define BRCMS_SET_SHORTSLOT_OVERRIDE 146 struct brcms_timer { struct delayed_work dly_wrk; struct brcms_info *wl; void (*fn) (void *); /* function called upon expiration */ void *arg; /* fixed argument provided to called function */ uint ms; bool periodic; bool set; /* indicates if timer is active */ struct brcms_timer *next; /* for freeing on unload */ #ifdef DEBUG char *name; /* Description of the timer */ #endif }; struct brcms_if { uint subunit; /* WDS/BSS unit */ struct pci_dev *pci_dev; }; #define MAX_FW_IMAGES 4 struct brcms_firmware { u32 fw_cnt; const struct firmware *fw_bin[MAX_FW_IMAGES]; const struct firmware *fw_hdr[MAX_FW_IMAGES]; u32 hdr_num_entries[MAX_FW_IMAGES]; }; struct brcms_info { struct brcms_pub *pub; /* pointer to public wlc state */ struct brcms_c_info *wlc; /* pointer to private common data */ u32 magic; int irq; spinlock_t lock; /* per-device perimeter lock */ spinlock_t isr_lock; /* per-device ISR synchronization lock */ /* timer related fields */ atomic_t callbacks; /* # outstanding callback functions */ struct brcms_timer *timers; /* timer cleanup queue */ struct tasklet_struct tasklet; /* dpc tasklet */ bool resched; /* dpc needs to be and is rescheduled */ struct brcms_firmware fw; struct wiphy *wiphy; struct brcms_ucode ucode; bool mute_tx; }; /* misc callbacks */ extern void brcms_init(struct brcms_info *wl); extern uint brcms_reset(struct brcms_info *wl); extern void brcms_intrson(struct brcms_info *wl); extern u32 brcms_intrsoff(struct brcms_info *wl); extern void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask); extern int brcms_up(struct brcms_info *wl); extern void brcms_down(struct brcms_info *wl); extern void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, bool state, int prio); extern bool brcms_rfkill_set_hw_state(struct brcms_info *wl); /* timer functions */ extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl, void (*fn) (void *arg), void *arg, const char *name); extern void brcms_free_timer(struct brcms_timer *timer); extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); extern bool brcms_del_timer(struct brcms_timer *timer); extern void brcms_msleep(struct brcms_info *wl, uint ms); extern void brcms_dpc(unsigned long data); extern void brcms_timer(struct brcms_timer *t); extern void brcms_fatal_error(struct brcms_info *wl); #endif /* _BRCM_MAC80211_IF_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/dma.h0000644000175000017500000001003312026211315024476 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_DMA_H_ #define _BRCM_DMA_H_ #include #include #include "types.h" /* forward structure declarations */ /* map/unmap direction */ #define DMA_TX 1 /* TX direction for DMA */ #define DMA_RX 2 /* RX direction for DMA */ /* DMA structure: * support two DMA engines: 32 bits address or 64 bit addressing * basic DMA register set is per channel(transmit or receive) * a pair of channels is defined for convenience */ /* 32 bits addressing */ struct dma32diag { /* diag access */ u32 fifoaddr; /* diag address */ u32 fifodatalow; /* low 32bits of data */ u32 fifodatahigh; /* high 32bits of data */ u32 pad; /* reserved */ }; /* 64 bits addressing */ /* dma registers per channel(xmt or rcv) */ struct dma64regs { u32 control; /* enable, et al */ u32 ptr; /* last descriptor posted to chip */ u32 addrlow; /* desc ring base address low 32-bits (8K aligned) */ u32 addrhigh; /* desc ring base address bits 63:32 (8K aligned) */ u32 status0; /* current descriptor, xmt state */ u32 status1; /* active descriptor, xmt error */ }; /* range param for dma_getnexttxp() and dma_txreclaim */ enum txd_range { DMA_RANGE_ALL = 1, DMA_RANGE_TRANSMITTED, DMA_RANGE_TRANSFERED }; /* * Exported data structure (read-only) */ /* export structure */ struct dma_pub { uint txavail; /* # free tx descriptors */ uint dmactrlflags; /* dma control flags */ /* rx error counters */ uint rxgiants; /* rx giant frames */ uint rxnobuf; /* rx out of dma descriptors */ /* tx error counters */ uint txnobuf; /* tx out of dma descriptors */ }; extern struct dma_pub *dma_attach(char *name, struct si_pub *sih, struct bcma_device *d11core, uint txregbase, uint rxregbase, uint ntxd, uint nrxd, uint rxbufsize, int rxextheadroom, uint nrxpost, uint rxoffset, uint *msg_level); void dma_rxinit(struct dma_pub *pub); int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list); bool dma_rxfill(struct dma_pub *pub); bool dma_rxreset(struct dma_pub *pub); bool dma_txreset(struct dma_pub *pub); void dma_txinit(struct dma_pub *pub); int dma_txfast(struct dma_pub *pub, struct sk_buff *p0, bool commit); void dma_txsuspend(struct dma_pub *pub); bool dma_txsuspended(struct dma_pub *pub); void dma_txresume(struct dma_pub *pub); void dma_txreclaim(struct dma_pub *pub, enum txd_range range); void dma_rxreclaim(struct dma_pub *pub); void dma_detach(struct dma_pub *pub); unsigned long dma_getvar(struct dma_pub *pub, const char *name); struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range); void dma_counterreset(struct dma_pub *pub); void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc) (void *pkt, void *arg_a), void *arg_a); /* * DMA(Bug) on bcm47xx chips seems to declare that the packet is ready, but * the packet length is not updated yet (by DMA) on the expected time. * Workaround is to hold processor till DMA updates the length, and stay off * the bus to allow DMA update the length in buffer */ static inline void dma_spin_for_len(uint len, struct sk_buff *head) { #if defined(CONFIG_BCM47XX) if (!len) { while (!(len = *(u16 *) KSEG1ADDR(head->data))) udelay(1); *(u16 *) (head->data) = cpu_to_le16((u16) len); } #endif /* defined(CONFIG_BCM47XX) */ } #endif /* _BRCM_DMA_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/d11.h0000644000175000017500000015712312026211315024336 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_D11_H_ #define _BRCM_D11_H_ #include #include #include "pub.h" #include "dma.h" /* RX FIFO numbers */ #define RX_FIFO 0 /* data and ctl frames */ #define RX_TXSTATUS_FIFO 3 /* RX fifo for tx status packages */ /* TX FIFO numbers using WME Access Category */ #define TX_AC_BK_FIFO 0 /* Background TX FIFO */ #define TX_AC_BE_FIFO 1 /* Best-Effort TX FIFO */ #define TX_AC_VI_FIFO 2 /* Video TX FIFO */ #define TX_AC_VO_FIFO 3 /* Voice TX FIFO */ #define TX_BCMC_FIFO 4 /* Broadcast/Multicast TX FIFO */ #define TX_ATIM_FIFO 5 /* TX fifo for ATIM window info */ /* Addr is byte address used by SW; offset is word offset used by uCode */ /* Per AC TX limit settings */ #define M_AC_TXLMT_BASE_ADDR (0x180 * 2) #define M_AC_TXLMT_ADDR(_ac) (M_AC_TXLMT_BASE_ADDR + (2 * (_ac))) /* Legacy TX FIFO numbers */ #define TX_DATA_FIFO TX_AC_BE_FIFO #define TX_CTL_FIFO TX_AC_VO_FIFO #define WL_RSSI_ANT_MAX 4 /* max possible rx antennas */ struct intctrlregs { u32 intstatus; u32 intmask; }; /* PIO structure, * support two PIO format: 2 bytes access and 4 bytes access * basic FIFO register set is per channel(transmit or receive) * a pair of channels is defined for convenience */ /* 2byte-wide pio register set per channel(xmt or rcv) */ struct pio2regs { u16 fifocontrol; u16 fifodata; u16 fifofree; /* only valid in xmt channel, not in rcv channel */ u16 PAD; }; /* a pair of pio channels(tx and rx) */ struct pio2regp { struct pio2regs tx; struct pio2regs rx; }; /* 4byte-wide pio register set per channel(xmt or rcv) */ struct pio4regs { u32 fifocontrol; u32 fifodata; }; /* a pair of pio channels(tx and rx) */ struct pio4regp { struct pio4regs tx; struct pio4regs rx; }; /* read: 32-bit register that can be read as 32-bit or as 2 16-bit * write: only low 16b-it half can be written */ union pmqreg { u32 pmqhostdata; /* read only! */ struct { u16 pmqctrlstatus; /* read/write */ u16 PAD; } w; }; struct fifo64 { struct dma64regs dmaxmt; /* dma tx */ struct pio4regs piotx; /* pio tx */ struct dma64regs dmarcv; /* dma rx */ struct pio4regs piorx; /* pio rx */ }; /* * Host Interface Registers */ struct d11regs { /* Device Control ("semi-standard host registers") */ u32 PAD[3]; /* 0x0 - 0x8 */ u32 biststatus; /* 0xC */ u32 biststatus2; /* 0x10 */ u32 PAD; /* 0x14 */ u32 gptimer; /* 0x18 */ u32 usectimer; /* 0x1c *//* for corerev >= 26 */ /* Interrupt Control *//* 0x20 */ struct intctrlregs intctrlregs[8]; u32 PAD[40]; /* 0x60 - 0xFC */ u32 intrcvlazy[4]; /* 0x100 - 0x10C */ u32 PAD[4]; /* 0x110 - 0x11c */ u32 maccontrol; /* 0x120 */ u32 maccommand; /* 0x124 */ u32 macintstatus; /* 0x128 */ u32 macintmask; /* 0x12C */ /* Transmit Template Access */ u32 tplatewrptr; /* 0x130 */ u32 tplatewrdata; /* 0x134 */ u32 PAD[2]; /* 0x138 - 0x13C */ /* PMQ registers */ union pmqreg pmqreg; /* 0x140 */ u32 pmqpatl; /* 0x144 */ u32 pmqpath; /* 0x148 */ u32 PAD; /* 0x14C */ u32 chnstatus; /* 0x150 */ u32 psmdebug; /* 0x154 */ u32 phydebug; /* 0x158 */ u32 machwcap; /* 0x15C */ /* Extended Internal Objects */ u32 objaddr; /* 0x160 */ u32 objdata; /* 0x164 */ u32 PAD[2]; /* 0x168 - 0x16c */ u32 frmtxstatus; /* 0x170 */ u32 frmtxstatus2; /* 0x174 */ u32 PAD[2]; /* 0x178 - 0x17c */ /* TSF host access */ u32 tsf_timerlow; /* 0x180 */ u32 tsf_timerhigh; /* 0x184 */ u32 tsf_cfprep; /* 0x188 */ u32 tsf_cfpstart; /* 0x18c */ u32 tsf_cfpmaxdur32; /* 0x190 */ u32 PAD[3]; /* 0x194 - 0x19c */ u32 maccontrol1; /* 0x1a0 */ u32 machwcap1; /* 0x1a4 */ u32 PAD[14]; /* 0x1a8 - 0x1dc */ /* Clock control and hardware workarounds*/ u32 clk_ctl_st; /* 0x1e0 */ u32 hw_war; u32 d11_phypllctl; /* the phypll request/avail bits are * moved to clk_ctl_st */ u32 PAD[5]; /* 0x1ec - 0x1fc */ /* 0x200-0x37F dma/pio registers */ struct fifo64 fifo64regs[6]; /* FIFO diagnostic port access */ struct dma32diag dmafifo; /* 0x380 - 0x38C */ u32 aggfifocnt; /* 0x390 */ u32 aggfifodata; /* 0x394 */ u32 PAD[16]; /* 0x398 - 0x3d4 */ u16 radioregaddr; /* 0x3d8 */ u16 radioregdata; /* 0x3da */ /* * time delay between the change on rf disable input and * radio shutdown */ u32 rfdisabledly; /* 0x3DC */ /* PHY register access */ u16 phyversion; /* 0x3e0 - 0x0 */ u16 phybbconfig; /* 0x3e2 - 0x1 */ u16 phyadcbias; /* 0x3e4 - 0x2 Bphy only */ u16 phyanacore; /* 0x3e6 - 0x3 pwwrdwn on aphy */ u16 phyrxstatus0; /* 0x3e8 - 0x4 */ u16 phyrxstatus1; /* 0x3ea - 0x5 */ u16 phycrsth; /* 0x3ec - 0x6 */ u16 phytxerror; /* 0x3ee - 0x7 */ u16 phychannel; /* 0x3f0 - 0x8 */ u16 PAD[1]; /* 0x3f2 - 0x9 */ u16 phytest; /* 0x3f4 - 0xa */ u16 phy4waddr; /* 0x3f6 - 0xb */ u16 phy4wdatahi; /* 0x3f8 - 0xc */ u16 phy4wdatalo; /* 0x3fa - 0xd */ u16 phyregaddr; /* 0x3fc - 0xe */ u16 phyregdata; /* 0x3fe - 0xf */ /* IHR *//* 0x400 - 0x7FE */ /* RXE Block */ u16 PAD[3]; /* 0x400 - 0x406 */ u16 rcv_fifo_ctl; /* 0x406 */ u16 PAD; /* 0x408 - 0x40a */ u16 rcv_frm_cnt; /* 0x40a */ u16 PAD[4]; /* 0x40a - 0x414 */ u16 rssi; /* 0x414 */ u16 PAD[5]; /* 0x414 - 0x420 */ u16 rcm_ctl; /* 0x420 */ u16 rcm_mat_data; /* 0x422 */ u16 rcm_mat_mask; /* 0x424 */ u16 rcm_mat_dly; /* 0x426 */ u16 rcm_cond_mask_l; /* 0x428 */ u16 rcm_cond_mask_h; /* 0x42A */ u16 rcm_cond_dly; /* 0x42C */ u16 PAD[1]; /* 0x42E */ u16 ext_ihr_addr; /* 0x430 */ u16 ext_ihr_data; /* 0x432 */ u16 rxe_phyrs_2; /* 0x434 */ u16 rxe_phyrs_3; /* 0x436 */ u16 phy_mode; /* 0x438 */ u16 rcmta_ctl; /* 0x43a */ u16 rcmta_size; /* 0x43c */ u16 rcmta_addr0; /* 0x43e */ u16 rcmta_addr1; /* 0x440 */ u16 rcmta_addr2; /* 0x442 */ u16 PAD[30]; /* 0x444 - 0x480 */ /* PSM Block *//* 0x480 - 0x500 */ u16 PAD; /* 0x480 */ u16 psm_maccontrol_h; /* 0x482 */ u16 psm_macintstatus_l; /* 0x484 */ u16 psm_macintstatus_h; /* 0x486 */ u16 psm_macintmask_l; /* 0x488 */ u16 psm_macintmask_h; /* 0x48A */ u16 PAD; /* 0x48C */ u16 psm_maccommand; /* 0x48E */ u16 psm_brc; /* 0x490 */ u16 psm_phy_hdr_param; /* 0x492 */ u16 psm_postcard; /* 0x494 */ u16 psm_pcard_loc_l; /* 0x496 */ u16 psm_pcard_loc_h; /* 0x498 */ u16 psm_gpio_in; /* 0x49A */ u16 psm_gpio_out; /* 0x49C */ u16 psm_gpio_oe; /* 0x49E */ u16 psm_bred_0; /* 0x4A0 */ u16 psm_bred_1; /* 0x4A2 */ u16 psm_bred_2; /* 0x4A4 */ u16 psm_bred_3; /* 0x4A6 */ u16 psm_brcl_0; /* 0x4A8 */ u16 psm_brcl_1; /* 0x4AA */ u16 psm_brcl_2; /* 0x4AC */ u16 psm_brcl_3; /* 0x4AE */ u16 psm_brpo_0; /* 0x4B0 */ u16 psm_brpo_1; /* 0x4B2 */ u16 psm_brpo_2; /* 0x4B4 */ u16 psm_brpo_3; /* 0x4B6 */ u16 psm_brwk_0; /* 0x4B8 */ u16 psm_brwk_1; /* 0x4BA */ u16 psm_brwk_2; /* 0x4BC */ u16 psm_brwk_3; /* 0x4BE */ u16 psm_base_0; /* 0x4C0 */ u16 psm_base_1; /* 0x4C2 */ u16 psm_base_2; /* 0x4C4 */ u16 psm_base_3; /* 0x4C6 */ u16 psm_base_4; /* 0x4C8 */ u16 psm_base_5; /* 0x4CA */ u16 psm_base_6; /* 0x4CC */ u16 psm_pc_reg_0; /* 0x4CE */ u16 psm_pc_reg_1; /* 0x4D0 */ u16 psm_pc_reg_2; /* 0x4D2 */ u16 psm_pc_reg_3; /* 0x4D4 */ u16 PAD[0xD]; /* 0x4D6 - 0x4DE */ u16 psm_corectlsts; /* 0x4f0 *//* Corerev >= 13 */ u16 PAD[0x7]; /* 0x4f2 - 0x4fE */ /* TXE0 Block *//* 0x500 - 0x580 */ u16 txe_ctl; /* 0x500 */ u16 txe_aux; /* 0x502 */ u16 txe_ts_loc; /* 0x504 */ u16 txe_time_out; /* 0x506 */ u16 txe_wm_0; /* 0x508 */ u16 txe_wm_1; /* 0x50A */ u16 txe_phyctl; /* 0x50C */ u16 txe_status; /* 0x50E */ u16 txe_mmplcp0; /* 0x510 */ u16 txe_mmplcp1; /* 0x512 */ u16 txe_phyctl1; /* 0x514 */ u16 PAD[0x05]; /* 0x510 - 0x51E */ /* Transmit control */ u16 xmtfifodef; /* 0x520 */ u16 xmtfifo_frame_cnt; /* 0x522 *//* Corerev >= 16 */ u16 xmtfifo_byte_cnt; /* 0x524 *//* Corerev >= 16 */ u16 xmtfifo_head; /* 0x526 *//* Corerev >= 16 */ u16 xmtfifo_rd_ptr; /* 0x528 *//* Corerev >= 16 */ u16 xmtfifo_wr_ptr; /* 0x52A *//* Corerev >= 16 */ u16 xmtfifodef1; /* 0x52C *//* Corerev >= 16 */ u16 PAD[0x09]; /* 0x52E - 0x53E */ u16 xmtfifocmd; /* 0x540 */ u16 xmtfifoflush; /* 0x542 */ u16 xmtfifothresh; /* 0x544 */ u16 xmtfifordy; /* 0x546 */ u16 xmtfifoprirdy; /* 0x548 */ u16 xmtfiforqpri; /* 0x54A */ u16 xmttplatetxptr; /* 0x54C */ u16 PAD; /* 0x54E */ u16 xmttplateptr; /* 0x550 */ u16 smpl_clct_strptr; /* 0x552 *//* Corerev >= 22 */ u16 smpl_clct_stpptr; /* 0x554 *//* Corerev >= 22 */ u16 smpl_clct_curptr; /* 0x556 *//* Corerev >= 22 */ u16 PAD[0x04]; /* 0x558 - 0x55E */ u16 xmttplatedatalo; /* 0x560 */ u16 xmttplatedatahi; /* 0x562 */ u16 PAD[2]; /* 0x564 - 0x566 */ u16 xmtsel; /* 0x568 */ u16 xmttxcnt; /* 0x56A */ u16 xmttxshmaddr; /* 0x56C */ u16 PAD[0x09]; /* 0x56E - 0x57E */ /* TXE1 Block */ u16 PAD[0x40]; /* 0x580 - 0x5FE */ /* TSF Block */ u16 PAD[0X02]; /* 0x600 - 0x602 */ u16 tsf_cfpstrt_l; /* 0x604 */ u16 tsf_cfpstrt_h; /* 0x606 */ u16 PAD[0X05]; /* 0x608 - 0x610 */ u16 tsf_cfppretbtt; /* 0x612 */ u16 PAD[0XD]; /* 0x614 - 0x62C */ u16 tsf_clk_frac_l; /* 0x62E */ u16 tsf_clk_frac_h; /* 0x630 */ u16 PAD[0X14]; /* 0x632 - 0x658 */ u16 tsf_random; /* 0x65A */ u16 PAD[0x05]; /* 0x65C - 0x664 */ /* GPTimer 2 registers */ u16 tsf_gpt2_stat; /* 0x666 */ u16 tsf_gpt2_ctr_l; /* 0x668 */ u16 tsf_gpt2_ctr_h; /* 0x66A */ u16 tsf_gpt2_val_l; /* 0x66C */ u16 tsf_gpt2_val_h; /* 0x66E */ u16 tsf_gptall_stat; /* 0x670 */ u16 PAD[0x07]; /* 0x672 - 0x67E */ /* IFS Block */ u16 ifs_sifs_rx_tx_tx; /* 0x680 */ u16 ifs_sifs_nav_tx; /* 0x682 */ u16 ifs_slot; /* 0x684 */ u16 PAD; /* 0x686 */ u16 ifs_ctl; /* 0x688 */ u16 PAD[0x3]; /* 0x68a - 0x68F */ u16 ifsstat; /* 0x690 */ u16 ifsmedbusyctl; /* 0x692 */ u16 iftxdur; /* 0x694 */ u16 PAD[0x3]; /* 0x696 - 0x69b */ /* EDCF support in dot11macs */ u16 ifs_aifsn; /* 0x69c */ u16 ifs_ctl1; /* 0x69e */ /* slow clock registers */ u16 scc_ctl; /* 0x6a0 */ u16 scc_timer_l; /* 0x6a2 */ u16 scc_timer_h; /* 0x6a4 */ u16 scc_frac; /* 0x6a6 */ u16 scc_fastpwrup_dly; /* 0x6a8 */ u16 scc_per; /* 0x6aa */ u16 scc_per_frac; /* 0x6ac */ u16 scc_cal_timer_l; /* 0x6ae */ u16 scc_cal_timer_h; /* 0x6b0 */ u16 PAD; /* 0x6b2 */ u16 PAD[0x26]; /* NAV Block */ u16 nav_ctl; /* 0x700 */ u16 navstat; /* 0x702 */ u16 PAD[0x3e]; /* 0x702 - 0x77E */ /* WEP/PMQ Block *//* 0x780 - 0x7FE */ u16 PAD[0x20]; /* 0x780 - 0x7BE */ u16 wepctl; /* 0x7C0 */ u16 wepivloc; /* 0x7C2 */ u16 wepivkey; /* 0x7C4 */ u16 wepwkey; /* 0x7C6 */ u16 PAD[4]; /* 0x7C8 - 0x7CE */ u16 pcmctl; /* 0X7D0 */ u16 pcmstat; /* 0X7D2 */ u16 PAD[6]; /* 0x7D4 - 0x7DE */ u16 pmqctl; /* 0x7E0 */ u16 pmqstatus; /* 0x7E2 */ u16 pmqpat0; /* 0x7E4 */ u16 pmqpat1; /* 0x7E6 */ u16 pmqpat2; /* 0x7E8 */ u16 pmqdat; /* 0x7EA */ u16 pmqdator; /* 0x7EC */ u16 pmqhst; /* 0x7EE */ u16 pmqpath0; /* 0x7F0 */ u16 pmqpath1; /* 0x7F2 */ u16 pmqpath2; /* 0x7F4 */ u16 pmqdath; /* 0x7F6 */ u16 PAD[0x04]; /* 0x7F8 - 0x7FE */ /* SHM *//* 0x800 - 0xEFE */ u16 PAD[0x380]; /* 0x800 - 0xEFE */ }; /* d11 register field offset */ #define D11REGOFFS(field) offsetof(struct d11regs, field) #define PIHR_BASE 0x0400 /* byte address of packed IHR region */ /* biststatus */ #define BT_DONE (1U << 31) /* bist done */ #define BT_B2S (1 << 30) /* bist2 ram summary bit */ /* intstatus and intmask */ #define I_PC (1 << 10) /* pci descriptor error */ #define I_PD (1 << 11) /* pci data error */ #define I_DE (1 << 12) /* descriptor protocol error */ #define I_RU (1 << 13) /* receive descriptor underflow */ #define I_RO (1 << 14) /* receive fifo overflow */ #define I_XU (1 << 15) /* transmit fifo underflow */ #define I_RI (1 << 16) /* receive interrupt */ #define I_XI (1 << 24) /* transmit interrupt */ /* interrupt receive lazy */ #define IRL_TO_MASK 0x00ffffff /* timeout */ #define IRL_FC_MASK 0xff000000 /* frame count */ #define IRL_FC_SHIFT 24 /* frame count */ /*== maccontrol register ==*/ #define MCTL_GMODE (1U << 31) #define MCTL_DISCARD_PMQ (1 << 30) #define MCTL_WAKE (1 << 26) #define MCTL_HPS (1 << 25) #define MCTL_PROMISC (1 << 24) #define MCTL_KEEPBADFCS (1 << 23) #define MCTL_KEEPCONTROL (1 << 22) #define MCTL_PHYLOCK (1 << 21) #define MCTL_BCNS_PROMISC (1 << 20) #define MCTL_LOCK_RADIO (1 << 19) #define MCTL_AP (1 << 18) #define MCTL_INFRA (1 << 17) #define MCTL_BIGEND (1 << 16) #define MCTL_GPOUT_SEL_MASK (3 << 14) #define MCTL_GPOUT_SEL_SHIFT 14 #define MCTL_EN_PSMDBG (1 << 13) #define MCTL_IHR_EN (1 << 10) #define MCTL_SHM_UPPER (1 << 9) #define MCTL_SHM_EN (1 << 8) #define MCTL_PSM_JMP_0 (1 << 2) #define MCTL_PSM_RUN (1 << 1) #define MCTL_EN_MAC (1 << 0) /*== maccommand register ==*/ #define MCMD_BCN0VLD (1 << 0) #define MCMD_BCN1VLD (1 << 1) #define MCMD_DIRFRMQVAL (1 << 2) #define MCMD_CCA (1 << 3) #define MCMD_BG_NOISE (1 << 4) #define MCMD_SKIP_SHMINIT (1 << 5) /* only used for simulation */ #define MCMD_SAMPLECOLL MCMD_SKIP_SHMINIT /* reuse for sample collect */ /*== macintstatus/macintmask ==*/ /* gracefully suspended */ #define MI_MACSSPNDD (1 << 0) /* beacon template available */ #define MI_BCNTPL (1 << 1) /* TBTT indication */ #define MI_TBTT (1 << 2) /* beacon successfully tx'd */ #define MI_BCNSUCCESS (1 << 3) /* beacon canceled (IBSS) */ #define MI_BCNCANCLD (1 << 4) /* end of ATIM-window (IBSS) */ #define MI_ATIMWINEND (1 << 5) /* PMQ entries available */ #define MI_PMQ (1 << 6) /* non-specific gen-stat bits that are set by PSM */ #define MI_NSPECGEN_0 (1 << 7) /* non-specific gen-stat bits that are set by PSM */ #define MI_NSPECGEN_1 (1 << 8) /* MAC level Tx error */ #define MI_MACTXERR (1 << 9) /* non-specific gen-stat bits that are set by PSM */ #define MI_NSPECGEN_3 (1 << 10) /* PHY Tx error */ #define MI_PHYTXERR (1 << 11) /* Power Management Event */ #define MI_PME (1 << 12) /* General-purpose timer0 */ #define MI_GP0 (1 << 13) /* General-purpose timer1 */ #define MI_GP1 (1 << 14) /* (ORed) DMA-interrupts */ #define MI_DMAINT (1 << 15) /* MAC has completed a TX FIFO Suspend/Flush */ #define MI_TXSTOP (1 << 16) /* MAC has completed a CCA measurement */ #define MI_CCA (1 << 17) /* MAC has collected background noise samples */ #define MI_BG_NOISE (1 << 18) /* MBSS DTIM TBTT indication */ #define MI_DTIM_TBTT (1 << 19) /* Probe response queue needs attention */ #define MI_PRQ (1 << 20) /* Radio/PHY has been powered back up. */ #define MI_PWRUP (1 << 21) #define MI_RESERVED3 (1 << 22) #define MI_RESERVED2 (1 << 23) #define MI_RESERVED1 (1 << 25) /* MAC detected change on RF Disable input*/ #define MI_RFDISABLE (1 << 28) /* MAC has completed a TX */ #define MI_TFS (1 << 29) /* A phy status change wrt G mode */ #define MI_PHYCHANGED (1 << 30) /* general purpose timeout */ #define MI_TO (1U << 31) /* Mac capabilities registers */ /*== machwcap ==*/ #define MCAP_TKIPMIC 0x80000000 /* TKIP MIC hardware present */ /*== pmqhost data ==*/ /* data entry of head pmq entry */ #define PMQH_DATA_MASK 0xffff0000 /* PM entry for BSS config */ #define PMQH_BSSCFG 0x00100000 /* PM Mode OFF: power save off */ #define PMQH_PMOFF 0x00010000 /* PM Mode ON: power save on */ #define PMQH_PMON 0x00020000 /* Dis-associated or De-authenticated */ #define PMQH_DASAT 0x00040000 /* ATIM not acknowledged */ #define PMQH_ATIMFAIL 0x00080000 /* delete head entry */ #define PMQH_DEL_ENTRY 0x00000001 /* delete head entry to cur read pointer -1 */ #define PMQH_DEL_MULT 0x00000002 /* pmq overflow indication */ #define PMQH_OFLO 0x00000004 /* entries are present in pmq */ #define PMQH_NOT_EMPTY 0x00000008 /*== phydebug ==*/ /* phy is asserting carrier sense */ #define PDBG_CRS (1 << 0) /* phy is taking xmit byte from mac this cycle */ #define PDBG_TXA (1 << 1) /* mac is instructing the phy to transmit a frame */ #define PDBG_TXF (1 << 2) /* phy is signalling a transmit Error to the mac */ #define PDBG_TXE (1 << 3) /* phy detected the end of a valid frame preamble */ #define PDBG_RXF (1 << 4) /* phy detected the end of a valid PLCP header */ #define PDBG_RXS (1 << 5) /* rx start not asserted */ #define PDBG_RXFRG (1 << 6) /* mac is taking receive byte from phy this cycle */ #define PDBG_RXV (1 << 7) /* RF portion of the radio is disabled */ #define PDBG_RFD (1 << 16) /*== objaddr register ==*/ #define OBJADDR_SEL_MASK 0x000F0000 #define OBJADDR_UCM_SEL 0x00000000 #define OBJADDR_SHM_SEL 0x00010000 #define OBJADDR_SCR_SEL 0x00020000 #define OBJADDR_IHR_SEL 0x00030000 #define OBJADDR_RCMTA_SEL 0x00040000 #define OBJADDR_SRCHM_SEL 0x00060000 #define OBJADDR_WINC 0x01000000 #define OBJADDR_RINC 0x02000000 #define OBJADDR_AUTO_INC 0x03000000 #define WEP_PCMADDR 0x07d4 #define WEP_PCMDATA 0x07d6 /*== frmtxstatus ==*/ #define TXS_V (1 << 0) /* valid bit */ #define TXS_STATUS_MASK 0xffff #define TXS_FID_MASK 0xffff0000 #define TXS_FID_SHIFT 16 /*== frmtxstatus2 ==*/ #define TXS_SEQ_MASK 0xffff #define TXS_PTX_MASK 0xff0000 #define TXS_PTX_SHIFT 16 #define TXS_MU_MASK 0x01000000 #define TXS_MU_SHIFT 24 /*== clk_ctl_st ==*/ #define CCS_ERSRC_REQ_D11PLL 0x00000100 /* d11 core pll request */ #define CCS_ERSRC_REQ_PHYPLL 0x00000200 /* PHY pll request */ #define CCS_ERSRC_AVAIL_D11PLL 0x01000000 /* d11 core pll available */ #define CCS_ERSRC_AVAIL_PHYPLL 0x02000000 /* PHY pll available */ /* HT Cloclk Ctrl and Clock Avail for 4313 */ #define CCS_ERSRC_REQ_HT 0x00000010 /* HT avail request */ #define CCS_ERSRC_AVAIL_HT 0x00020000 /* HT clock available */ /* tsf_cfprep register */ #define CFPREP_CBI_MASK 0xffffffc0 #define CFPREP_CBI_SHIFT 6 #define CFPREP_CFPP 0x00000001 /* tx fifo sizes values are in terms of 256 byte blocks */ #define TXFIFOCMD_RESET_MASK (1 << 15) /* reset */ #define TXFIFOCMD_FIFOSEL_SHIFT 8 /* fifo */ #define TXFIFO_FIFOTOP_SHIFT 8 /* fifo start */ #define TXFIFO_START_BLK16 65 /* Base address + 32 * 512 B/P */ #define TXFIFO_START_BLK 6 /* Base address + 6 * 256 B */ #define TXFIFO_SIZE_UNIT 256 /* one unit corresponds to 256 bytes */ #define MBSS16_TEMPLMEM_MINBLKS 65 /* one unit corresponds to 256 bytes */ /*== phy versions (PhyVersion:Revision field) ==*/ /* analog block version */ #define PV_AV_MASK 0xf000 /* analog block version bitfield offset */ #define PV_AV_SHIFT 12 /* phy type */ #define PV_PT_MASK 0x0f00 /* phy type bitfield offset */ #define PV_PT_SHIFT 8 /* phy version */ #define PV_PV_MASK 0x000f #define PHY_TYPE(v) ((v & PV_PT_MASK) >> PV_PT_SHIFT) /*== phy types (PhyVersion:PhyType field) ==*/ #define PHY_TYPE_N 4 /* N-Phy value */ #define PHY_TYPE_SSN 6 /* SSLPN-Phy value */ #define PHY_TYPE_LCN 8 /* LCN-Phy value */ #define PHY_TYPE_LCNXN 9 /* LCNXN-Phy value */ #define PHY_TYPE_NULL 0xf /* Invalid Phy value */ /*== analog types (PhyVersion:AnalogType field) ==*/ #define ANA_11N_013 5 /* 802.11a PLCP header def */ struct ofdm_phy_hdr { u8 rlpt[3]; /* rate, length, parity, tail */ u16 service; u8 pad; } __packed; #define D11A_PHY_HDR_GRATE(phdr) ((phdr)->rlpt[0] & 0x0f) #define D11A_PHY_HDR_GRES(phdr) (((phdr)->rlpt[0] >> 4) & 0x01) #define D11A_PHY_HDR_GLENGTH(phdr) (((u32 *)((phdr)->rlpt) >> 5) & 0x0fff) #define D11A_PHY_HDR_GPARITY(phdr) (((phdr)->rlpt[3] >> 1) & 0x01) #define D11A_PHY_HDR_GTAIL(phdr) (((phdr)->rlpt[3] >> 2) & 0x3f) /* rate encoded per 802.11a-1999 sec 17.3.4.1 */ #define D11A_PHY_HDR_SRATE(phdr, rate) \ ((phdr)->rlpt[0] = ((phdr)->rlpt[0] & 0xf0) | ((rate) & 0xf)) /* set reserved field to zero */ #define D11A_PHY_HDR_SRES(phdr) ((phdr)->rlpt[0] &= 0xef) /* length is number of octets in PSDU */ #define D11A_PHY_HDR_SLENGTH(phdr, length) \ (*(u32 *)((phdr)->rlpt) = *(u32 *)((phdr)->rlpt) | \ (((length) & 0x0fff) << 5)) /* set the tail to all zeros */ #define D11A_PHY_HDR_STAIL(phdr) ((phdr)->rlpt[3] &= 0x03) #define D11A_PHY_HDR_LEN_L 3 /* low-rate part of PLCP header */ #define D11A_PHY_HDR_LEN_R 2 /* high-rate part of PLCP header */ #define D11A_PHY_TX_DELAY (2) /* 2.1 usec */ #define D11A_PHY_HDR_TIME (4) /* low-rate part of PLCP header */ #define D11A_PHY_PRE_TIME (16) #define D11A_PHY_PREHDR_TIME (D11A_PHY_PRE_TIME + D11A_PHY_HDR_TIME) /* 802.11b PLCP header def */ struct cck_phy_hdr { u8 signal; u8 service; u16 length; u16 crc; } __packed; #define D11B_PHY_HDR_LEN 6 #define D11B_PHY_TX_DELAY (3) /* 3.4 usec */ #define D11B_PHY_LHDR_TIME (D11B_PHY_HDR_LEN << 3) #define D11B_PHY_LPRE_TIME (144) #define D11B_PHY_LPREHDR_TIME (D11B_PHY_LPRE_TIME + D11B_PHY_LHDR_TIME) #define D11B_PHY_SHDR_TIME (D11B_PHY_LHDR_TIME >> 1) #define D11B_PHY_SPRE_TIME (D11B_PHY_LPRE_TIME >> 1) #define D11B_PHY_SPREHDR_TIME (D11B_PHY_SPRE_TIME + D11B_PHY_SHDR_TIME) #define D11B_PLCP_SIGNAL_LOCKED (1 << 2) #define D11B_PLCP_SIGNAL_LE (1 << 7) #define MIMO_PLCP_MCS_MASK 0x7f /* mcs index */ #define MIMO_PLCP_40MHZ 0x80 /* 40 Hz frame */ #define MIMO_PLCP_AMPDU 0x08 /* ampdu */ #define BRCMS_GET_CCK_PLCP_LEN(plcp) (plcp[4] + (plcp[5] << 8)) #define BRCMS_GET_MIMO_PLCP_LEN(plcp) (plcp[1] + (plcp[2] << 8)) #define BRCMS_SET_MIMO_PLCP_LEN(plcp, len) \ do { \ plcp[1] = len & 0xff; \ plcp[2] = ((len >> 8) & 0xff); \ } while (0) #define BRCMS_SET_MIMO_PLCP_AMPDU(plcp) (plcp[3] |= MIMO_PLCP_AMPDU) #define BRCMS_CLR_MIMO_PLCP_AMPDU(plcp) (plcp[3] &= ~MIMO_PLCP_AMPDU) #define BRCMS_IS_MIMO_PLCP_AMPDU(plcp) (plcp[3] & MIMO_PLCP_AMPDU) /* * The dot11a PLCP header is 5 bytes. To simplify the software (so that we * don't need e.g. different tx DMA headers for 11a and 11b), the PLCP header * has padding added in the ucode. */ #define D11_PHY_HDR_LEN 6 /* TX DMA buffer header */ struct d11txh { __le16 MacTxControlLow; /* 0x0 */ __le16 MacTxControlHigh; /* 0x1 */ __le16 MacFrameControl; /* 0x2 */ __le16 TxFesTimeNormal; /* 0x3 */ __le16 PhyTxControlWord; /* 0x4 */ __le16 PhyTxControlWord_1; /* 0x5 */ __le16 PhyTxControlWord_1_Fbr; /* 0x6 */ __le16 PhyTxControlWord_1_Rts; /* 0x7 */ __le16 PhyTxControlWord_1_FbrRts; /* 0x8 */ __le16 MainRates; /* 0x9 */ __le16 XtraFrameTypes; /* 0xa */ u8 IV[16]; /* 0x0b - 0x12 */ u8 TxFrameRA[6]; /* 0x13 - 0x15 */ __le16 TxFesTimeFallback; /* 0x16 */ u8 RTSPLCPFallback[6]; /* 0x17 - 0x19 */ __le16 RTSDurFallback; /* 0x1a */ u8 FragPLCPFallback[6]; /* 0x1b - 1d */ __le16 FragDurFallback; /* 0x1e */ __le16 MModeLen; /* 0x1f */ __le16 MModeFbrLen; /* 0x20 */ __le16 TstampLow; /* 0x21 */ __le16 TstampHigh; /* 0x22 */ __le16 ABI_MimoAntSel; /* 0x23 */ __le16 PreloadSize; /* 0x24 */ __le16 AmpduSeqCtl; /* 0x25 */ __le16 TxFrameID; /* 0x26 */ __le16 TxStatus; /* 0x27 */ __le16 MaxNMpdus; /* 0x28 */ __le16 MaxABytes_MRT; /* 0x29 */ __le16 MaxABytes_FBR; /* 0x2a */ __le16 MinMBytes; /* 0x2b */ u8 RTSPhyHeader[D11_PHY_HDR_LEN]; /* 0x2c - 0x2e */ struct ieee80211_rts rts_frame; /* 0x2f - 0x36 */ u16 PAD; /* 0x37 */ } __packed; #define D11_TXH_LEN 112 /* bytes */ /* Frame Types */ #define FT_CCK 0 #define FT_OFDM 1 #define FT_HT 2 #define FT_N 3 /* * Position of MPDU inside A-MPDU; indicated with bits 10:9 * of MacTxControlLow */ #define TXC_AMPDU_SHIFT 9 /* shift for ampdu settings */ #define TXC_AMPDU_NONE 0 /* Regular MPDU, not an A-MPDU */ #define TXC_AMPDU_FIRST 1 /* first MPDU of an A-MPDU */ #define TXC_AMPDU_MIDDLE 2 /* intermediate MPDU of an A-MPDU */ #define TXC_AMPDU_LAST 3 /* last (or single) MPDU of an A-MPDU */ /*== MacTxControlLow ==*/ #define TXC_AMIC 0x8000 #define TXC_SENDCTS 0x0800 #define TXC_AMPDU_MASK 0x0600 #define TXC_BW_40 0x0100 #define TXC_FREQBAND_5G 0x0080 #define TXC_DFCS 0x0040 #define TXC_IGNOREPMQ 0x0020 #define TXC_HWSEQ 0x0010 #define TXC_STARTMSDU 0x0008 #define TXC_SENDRTS 0x0004 #define TXC_LONGFRAME 0x0002 #define TXC_IMMEDACK 0x0001 /*== MacTxControlHigh ==*/ /* RTS fallback preamble type 1 = SHORT 0 = LONG */ #define TXC_PREAMBLE_RTS_FB_SHORT 0x8000 /* RTS main rate preamble type 1 = SHORT 0 = LONG */ #define TXC_PREAMBLE_RTS_MAIN_SHORT 0x4000 /* * Main fallback rate preamble type * 1 = SHORT for OFDM/GF for MIMO * 0 = LONG for CCK/MM for MIMO */ #define TXC_PREAMBLE_DATA_FB_SHORT 0x2000 /* TXC_PREAMBLE_DATA_MAIN is in PhyTxControl bit 5 */ /* use fallback rate for this AMPDU */ #define TXC_AMPDU_FBR 0x1000 #define TXC_SECKEY_MASK 0x0FF0 #define TXC_SECKEY_SHIFT 4 /* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */ #define TXC_ALT_TXPWR 0x0008 #define TXC_SECTYPE_MASK 0x0007 #define TXC_SECTYPE_SHIFT 0 /* Null delimiter for Fallback rate */ #define AMPDU_FBR_NULL_DELIM 5 /* Location of Null delimiter count for AMPDU */ /* PhyTxControl for Mimophy */ #define PHY_TXC_PWR_MASK 0xFC00 #define PHY_TXC_PWR_SHIFT 10 #define PHY_TXC_ANT_MASK 0x03C0 /* bit 6, 7, 8, 9 */ #define PHY_TXC_ANT_SHIFT 6 #define PHY_TXC_ANT_0_1 0x00C0 /* auto, last rx */ #define PHY_TXC_LCNPHY_ANT_LAST 0x0000 #define PHY_TXC_ANT_3 0x0200 /* virtual antenna 3 */ #define PHY_TXC_ANT_2 0x0100 /* virtual antenna 2 */ #define PHY_TXC_ANT_1 0x0080 /* virtual antenna 1 */ #define PHY_TXC_ANT_0 0x0040 /* virtual antenna 0 */ #define PHY_TXC_SHORT_HDR 0x0010 #define PHY_TXC_OLD_ANT_0 0x0000 #define PHY_TXC_OLD_ANT_1 0x0100 #define PHY_TXC_OLD_ANT_LAST 0x0300 /* PhyTxControl_1 for Mimophy */ #define PHY_TXC1_BW_MASK 0x0007 #define PHY_TXC1_BW_10MHZ 0 #define PHY_TXC1_BW_10MHZ_UP 1 #define PHY_TXC1_BW_20MHZ 2 #define PHY_TXC1_BW_20MHZ_UP 3 #define PHY_TXC1_BW_40MHZ 4 #define PHY_TXC1_BW_40MHZ_DUP 5 #define PHY_TXC1_MODE_SHIFT 3 #define PHY_TXC1_MODE_MASK 0x0038 #define PHY_TXC1_MODE_SISO 0 #define PHY_TXC1_MODE_CDD 1 #define PHY_TXC1_MODE_STBC 2 #define PHY_TXC1_MODE_SDM 3 /* PhyTxControl for HTphy that are different from Mimophy */ #define PHY_TXC_HTANT_MASK 0x3fC0 /* bits 6-13 */ /* XtraFrameTypes */ #define XFTS_RTS_FT_SHIFT 2 #define XFTS_FBRRTS_FT_SHIFT 4 #define XFTS_CHANNEL_SHIFT 8 /* Antenna diversity bit in ant_wr_settle */ #define PHY_AWS_ANTDIV 0x2000 /* IFS ctl */ #define IFS_USEEDCF (1 << 2) /* IFS ctl1 */ #define IFS_CTL1_EDCRS (1 << 3) #define IFS_CTL1_EDCRS_20L (1 << 4) #define IFS_CTL1_EDCRS_40 (1 << 5) /* ABI_MimoAntSel */ #define ABI_MAS_ADDR_BMP_IDX_MASK 0x0f00 #define ABI_MAS_ADDR_BMP_IDX_SHIFT 8 #define ABI_MAS_FBR_ANT_PTN_MASK 0x00f0 #define ABI_MAS_FBR_ANT_PTN_SHIFT 4 #define ABI_MAS_MRT_ANT_PTN_MASK 0x000f /* tx status packet */ struct tx_status { u16 framelen; u16 PAD; u16 frameid; u16 status; u16 lasttxtime; u16 sequence; u16 phyerr; u16 ackphyrxsh; } __packed; #define TXSTATUS_LEN 16 /* status field bit definitions */ #define TX_STATUS_FRM_RTX_MASK 0xF000 #define TX_STATUS_FRM_RTX_SHIFT 12 #define TX_STATUS_RTS_RTX_MASK 0x0F00 #define TX_STATUS_RTS_RTX_SHIFT 8 #define TX_STATUS_MASK 0x00FE #define TX_STATUS_PMINDCTD (1 << 7) /* PM mode indicated to AP */ #define TX_STATUS_INTERMEDIATE (1 << 6) /* intermediate or 1st ampdu pkg */ #define TX_STATUS_AMPDU (1 << 5) /* AMPDU status */ #define TX_STATUS_SUPR_MASK 0x1C /* suppress status bits (4:2) */ #define TX_STATUS_SUPR_SHIFT 2 #define TX_STATUS_ACK_RCV (1 << 1) /* ACK received */ #define TX_STATUS_VALID (1 << 0) /* Tx status valid */ #define TX_STATUS_NO_ACK 0 /* suppress status reason codes */ #define TX_STATUS_SUPR_PMQ (1 << 2) /* PMQ entry */ #define TX_STATUS_SUPR_FLUSH (2 << 2) /* flush request */ #define TX_STATUS_SUPR_FRAG (3 << 2) /* previous frag failure */ #define TX_STATUS_SUPR_TBTT (3 << 2) /* SHARED: Probe resp supr for TBTT */ #define TX_STATUS_SUPR_BADCH (4 << 2) /* channel mismatch */ #define TX_STATUS_SUPR_EXPTIME (5 << 2) /* lifetime expiry */ #define TX_STATUS_SUPR_UF (6 << 2) /* underflow */ /* Unexpected tx status for rate update */ #define TX_STATUS_UNEXP(status) \ ((((status) & TX_STATUS_INTERMEDIATE) != 0) && \ TX_STATUS_UNEXP_AMPDU(status)) /* Unexpected tx status for A-MPDU rate update */ #define TX_STATUS_UNEXP_AMPDU(status) \ ((((status) & TX_STATUS_SUPR_MASK) != 0) && \ (((status) & TX_STATUS_SUPR_MASK) != TX_STATUS_SUPR_EXPTIME)) #define TX_STATUS_BA_BMAP03_MASK 0xF000 /* ba bitmap 0:3 in 1st pkg */ #define TX_STATUS_BA_BMAP03_SHIFT 12 /* ba bitmap 0:3 in 1st pkg */ #define TX_STATUS_BA_BMAP47_MASK 0x001E /* ba bitmap 4:7 in 2nd pkg */ #define TX_STATUS_BA_BMAP47_SHIFT 3 /* ba bitmap 4:7 in 2nd pkg */ /* RXE (Receive Engine) */ /* RCM_CTL */ #define RCM_INC_MASK_H 0x0080 #define RCM_INC_MASK_L 0x0040 #define RCM_INC_DATA 0x0020 #define RCM_INDEX_MASK 0x001F #define RCM_SIZE 15 #define RCM_MAC_OFFSET 0 /* current MAC address */ #define RCM_BSSID_OFFSET 3 /* current BSSID address */ #define RCM_F_BSSID_0_OFFSET 6 /* foreign BSS CFP tracking */ #define RCM_F_BSSID_1_OFFSET 9 /* foreign BSS CFP tracking */ #define RCM_F_BSSID_2_OFFSET 12 /* foreign BSS CFP tracking */ #define RCM_WEP_TA0_OFFSET 16 #define RCM_WEP_TA1_OFFSET 19 #define RCM_WEP_TA2_OFFSET 22 #define RCM_WEP_TA3_OFFSET 25 /* PSM Block */ /* psm_phy_hdr_param bits */ #define MAC_PHY_RESET 1 #define MAC_PHY_CLOCK_EN 2 #define MAC_PHY_FORCE_CLK 4 /* WEP Block */ /* WEP_WKEY */ #define WKEY_START (1 << 8) #define WKEY_SEL_MASK 0x1F /* WEP data formats */ /* the number of RCMTA entries */ #define RCMTA_SIZE 50 #define M_ADDR_BMP_BLK (0x37e * 2) #define M_ADDR_BMP_BLK_SZ 12 #define ADDR_BMP_RA (1 << 0) /* Receiver Address (RA) */ #define ADDR_BMP_TA (1 << 1) /* Transmitter Address (TA) */ #define ADDR_BMP_BSSID (1 << 2) /* BSSID */ #define ADDR_BMP_AP (1 << 3) /* Infra-BSS Access Point */ #define ADDR_BMP_STA (1 << 4) /* Infra-BSS Station */ #define ADDR_BMP_RESERVED1 (1 << 5) #define ADDR_BMP_RESERVED2 (1 << 6) #define ADDR_BMP_RESERVED3 (1 << 7) #define ADDR_BMP_BSS_IDX_MASK (3 << 8) /* BSS control block index */ #define ADDR_BMP_BSS_IDX_SHIFT 8 #define WSEC_MAX_RCMTA_KEYS 54 /* max keys in M_TKMICKEYS_BLK */ #define WSEC_MAX_TKMIC_ENGINE_KEYS 12 /* 8 + 4 default */ /* max RXE match registers */ #define WSEC_MAX_RXE_KEYS 4 /* SECKINDXALGO (Security Key Index & Algorithm Block) word format */ /* SKL (Security Key Lookup) */ #define SKL_ALGO_MASK 0x0007 #define SKL_ALGO_SHIFT 0 #define SKL_KEYID_MASK 0x0008 #define SKL_KEYID_SHIFT 3 #define SKL_INDEX_MASK 0x03F0 #define SKL_INDEX_SHIFT 4 #define SKL_GRP_ALGO_MASK 0x1c00 #define SKL_GRP_ALGO_SHIFT 10 /* additional bits defined for IBSS group key support */ #define SKL_IBSS_INDEX_MASK 0x01F0 #define SKL_IBSS_INDEX_SHIFT 4 #define SKL_IBSS_KEYID1_MASK 0x0600 #define SKL_IBSS_KEYID1_SHIFT 9 #define SKL_IBSS_KEYID2_MASK 0x1800 #define SKL_IBSS_KEYID2_SHIFT 11 #define SKL_IBSS_KEYALGO_MASK 0xE000 #define SKL_IBSS_KEYALGO_SHIFT 13 #define WSEC_MODE_OFF 0 #define WSEC_MODE_HW 1 #define WSEC_MODE_SW 2 #define WSEC_ALGO_OFF 0 #define WSEC_ALGO_WEP1 1 #define WSEC_ALGO_TKIP 2 #define WSEC_ALGO_AES 3 #define WSEC_ALGO_WEP128 4 #define WSEC_ALGO_AES_LEGACY 5 #define WSEC_ALGO_NALG 6 #define AES_MODE_NONE 0 #define AES_MODE_CCM 1 /* WEP_CTL (Rev 0) */ #define WECR0_KEYREG_SHIFT 0 #define WECR0_KEYREG_MASK 0x7 #define WECR0_DECRYPT (1 << 3) #define WECR0_IVINLINE (1 << 4) #define WECR0_WEPALG_SHIFT 5 #define WECR0_WEPALG_MASK (0x7 << 5) #define WECR0_WKEYSEL_SHIFT 8 #define WECR0_WKEYSEL_MASK (0x7 << 8) #define WECR0_WKEYSTART (1 << 11) #define WECR0_WEPINIT (1 << 14) #define WECR0_ICVERR (1 << 15) /* Frame template map byte offsets */ #define T_ACTS_TPL_BASE (0) #define T_NULL_TPL_BASE (0xc * 2) #define T_QNULL_TPL_BASE (0x1c * 2) #define T_RR_TPL_BASE (0x2c * 2) #define T_BCN0_TPL_BASE (0x34 * 2) #define T_PRS_TPL_BASE (0x134 * 2) #define T_BCN1_TPL_BASE (0x234 * 2) #define T_TX_FIFO_TXRAM_BASE (T_ACTS_TPL_BASE + \ (TXFIFO_START_BLK * TXFIFO_SIZE_UNIT)) #define T_BA_TPL_BASE T_QNULL_TPL_BASE /* template area for BA */ #define T_RAM_ACCESS_SZ 4 /* template ram is 4 byte access only */ /* Shared Mem byte offsets */ /* Location where the ucode expects the corerev */ #define M_MACHW_VER (0x00b * 2) /* Location where the ucode expects the MAC capabilities */ #define M_MACHW_CAP_L (0x060 * 2) #define M_MACHW_CAP_H (0x061 * 2) /* WME shared memory */ #define M_EDCF_STATUS_OFF (0x007 * 2) #define M_TXF_CUR_INDEX (0x018 * 2) #define M_EDCF_QINFO (0x120 * 2) /* PS-mode related parameters */ #define M_DOT11_SLOT (0x008 * 2) #define M_DOT11_DTIMPERIOD (0x009 * 2) #define M_NOSLPZNATDTIM (0x026 * 2) /* Beacon-related parameters */ #define M_BCN0_FRM_BYTESZ (0x00c * 2) /* Bcn 0 template length */ #define M_BCN1_FRM_BYTESZ (0x00d * 2) /* Bcn 1 template length */ #define M_BCN_TXTSF_OFFSET (0x00e * 2) #define M_TIMBPOS_INBEACON (0x00f * 2) #define M_SFRMTXCNTFBRTHSD (0x022 * 2) #define M_LFRMTXCNTFBRTHSD (0x023 * 2) #define M_BCN_PCTLWD (0x02a * 2) #define M_BCN_LI (0x05b * 2) /* beacon listen interval */ /* MAX Rx Frame len */ #define M_MAXRXFRM_LEN (0x010 * 2) /* ACK/CTS related params */ #define M_RSP_PCTLWD (0x011 * 2) /* Hardware Power Control */ #define M_TXPWR_N (0x012 * 2) #define M_TXPWR_TARGET (0x013 * 2) #define M_TXPWR_MAX (0x014 * 2) #define M_TXPWR_CUR (0x019 * 2) /* Rx-related parameters */ #define M_RX_PAD_DATA_OFFSET (0x01a * 2) /* WEP Shared mem data */ #define M_SEC_DEFIVLOC (0x01e * 2) #define M_SEC_VALNUMSOFTMCHTA (0x01f * 2) #define M_PHYVER (0x028 * 2) #define M_PHYTYPE (0x029 * 2) #define M_SECRXKEYS_PTR (0x02b * 2) #define M_TKMICKEYS_PTR (0x059 * 2) #define M_SECKINDXALGO_BLK (0x2ea * 2) #define M_SECKINDXALGO_BLK_SZ 54 #define M_SECPSMRXTAMCH_BLK (0x2fa * 2) #define M_TKIP_TSC_TTAK (0x18c * 2) #define D11_MAX_KEY_SIZE 16 #define M_MAX_ANTCNT (0x02e * 2) /* antenna swap threshold */ /* Probe response related parameters */ #define M_SSIDLEN (0x024 * 2) #define M_PRB_RESP_FRM_LEN (0x025 * 2) #define M_PRS_MAXTIME (0x03a * 2) #define M_SSID (0xb0 * 2) #define M_CTXPRS_BLK (0xc0 * 2) #define C_CTX_PCTLWD_POS (0x4 * 2) /* Delta between OFDM and CCK power in CCK power boost mode */ #define M_OFDM_OFFSET (0x027 * 2) /* TSSI for last 4 11b/g CCK packets transmitted */ #define M_B_TSSI_0 (0x02c * 2) #define M_B_TSSI_1 (0x02d * 2) /* Host flags to turn on ucode options */ #define M_HOST_FLAGS1 (0x02f * 2) #define M_HOST_FLAGS2 (0x030 * 2) #define M_HOST_FLAGS3 (0x031 * 2) #define M_HOST_FLAGS4 (0x03c * 2) #define M_HOST_FLAGS5 (0x06a * 2) #define M_HOST_FLAGS_SZ 16 #define M_RADAR_REG (0x033 * 2) /* TSSI for last 4 11a OFDM packets transmitted */ #define M_A_TSSI_0 (0x034 * 2) #define M_A_TSSI_1 (0x035 * 2) /* noise interference measurement */ #define M_NOISE_IF_COUNT (0x034 * 2) #define M_NOISE_IF_TIMEOUT (0x035 * 2) #define M_RF_RX_SP_REG1 (0x036 * 2) /* TSSI for last 4 11g OFDM packets transmitted */ #define M_G_TSSI_0 (0x038 * 2) #define M_G_TSSI_1 (0x039 * 2) /* Background noise measure */ #define M_JSSI_0 (0x44 * 2) #define M_JSSI_1 (0x45 * 2) #define M_JSSI_AUX (0x46 * 2) #define M_CUR_2050_RADIOCODE (0x47 * 2) /* TX fifo sizes */ #define M_FIFOSIZE0 (0x4c * 2) #define M_FIFOSIZE1 (0x4d * 2) #define M_FIFOSIZE2 (0x4e * 2) #define M_FIFOSIZE3 (0x4f * 2) #define D11_MAX_TX_FRMS 32 /* max frames allowed in tx fifo */ /* Current channel number plus upper bits */ #define M_CURCHANNEL (0x50 * 2) #define D11_CURCHANNEL_5G 0x0100; #define D11_CURCHANNEL_40 0x0200; #define D11_CURCHANNEL_MAX 0x00FF; /* last posted frameid on the bcmc fifo */ #define M_BCMC_FID (0x54 * 2) #define INVALIDFID 0xffff /* extended beacon phyctl bytes for 11N */ #define M_BCN_PCTL1WD (0x058 * 2) /* idle busy ratio to duty_cycle requirement */ #define M_TX_IDLE_BUSY_RATIO_X_16_CCK (0x52 * 2) #define M_TX_IDLE_BUSY_RATIO_X_16_OFDM (0x5A * 2) /* CW RSSI for LCNPHY */ #define M_LCN_RSSI_0 0x1332 #define M_LCN_RSSI_1 0x1338 #define M_LCN_RSSI_2 0x133e #define M_LCN_RSSI_3 0x1344 /* SNR for LCNPHY */ #define M_LCN_SNR_A_0 0x1334 #define M_LCN_SNR_B_0 0x1336 #define M_LCN_SNR_A_1 0x133a #define M_LCN_SNR_B_1 0x133c #define M_LCN_SNR_A_2 0x1340 #define M_LCN_SNR_B_2 0x1342 #define M_LCN_SNR_A_3 0x1346 #define M_LCN_SNR_B_3 0x1348 #define M_LCN_LAST_RESET (81*2) #define M_LCN_LAST_LOC (63*2) #define M_LCNPHY_RESET_STATUS (4902) #define M_LCNPHY_DSC_TIME (0x98d*2) #define M_LCNPHY_RESET_CNT_DSC (0x98b*2) #define M_LCNPHY_RESET_CNT (0x98c*2) /* Rate table offsets */ #define M_RT_DIRMAP_A (0xe0 * 2) #define M_RT_BBRSMAP_A (0xf0 * 2) #define M_RT_DIRMAP_B (0x100 * 2) #define M_RT_BBRSMAP_B (0x110 * 2) /* Rate table entry offsets */ #define M_RT_PRS_PLCP_POS 10 #define M_RT_PRS_DUR_POS 16 #define M_RT_OFDM_PCTL1_POS 18 #define M_20IN40_IQ (0x380 * 2) /* SHM locations where ucode stores the current power index */ #define M_CURR_IDX1 (0x384 * 2) #define M_CURR_IDX2 (0x387 * 2) #define M_BSCALE_ANT0 (0x5e * 2) #define M_BSCALE_ANT1 (0x5f * 2) /* Antenna Diversity Testing */ #define M_MIMO_ANTSEL_RXDFLT (0x63 * 2) #define M_ANTSEL_CLKDIV (0x61 * 2) #define M_MIMO_ANTSEL_TXDFLT (0x64 * 2) #define M_MIMO_MAXSYM (0x5d * 2) #define MIMO_MAXSYM_DEF 0x8000 /* 32k */ #define MIMO_MAXSYM_MAX 0xffff /* 64k */ #define M_WATCHDOG_8TU (0x1e * 2) #define WATCHDOG_8TU_DEF 5 #define WATCHDOG_8TU_MAX 10 /* Manufacturing Test Variables */ /* PER test mode */ #define M_PKTENG_CTRL (0x6c * 2) /* IFS for TX mode */ #define M_PKTENG_IFS (0x6d * 2) /* Lower word of tx frmcnt/rx lostcnt */ #define M_PKTENG_FRMCNT_LO (0x6e * 2) /* Upper word of tx frmcnt/rx lostcnt */ #define M_PKTENG_FRMCNT_HI (0x6f * 2) /* Index variation in vbat ripple */ #define M_LCN_PWR_IDX_MAX (0x67 * 2) /* highest index read by ucode */ #define M_LCN_PWR_IDX_MIN (0x66 * 2) /* lowest index read by ucode */ /* M_PKTENG_CTRL bit definitions */ #define M_PKTENG_MODE_TX 0x0001 #define M_PKTENG_MODE_TX_RIFS 0x0004 #define M_PKTENG_MODE_TX_CTS 0x0008 #define M_PKTENG_MODE_RX 0x0002 #define M_PKTENG_MODE_RX_WITH_ACK 0x0402 #define M_PKTENG_MODE_MASK 0x0003 /* TX frames indicated in the frmcnt reg */ #define M_PKTENG_FRMCNT_VLD 0x0100 /* Sample Collect parameters (bitmap and type) */ /* Trigger bitmap for sample collect */ #define M_SMPL_COL_BMP (0x37d * 2) /* Sample collect type */ #define M_SMPL_COL_CTL (0x3b2 * 2) #define ANTSEL_CLKDIV_4MHZ 6 #define MIMO_ANTSEL_BUSY 0x4000 /* bit 14 (busy) */ #define MIMO_ANTSEL_SEL 0x8000 /* bit 15 write the value */ #define MIMO_ANTSEL_WAIT 50 /* 50us wait */ #define MIMO_ANTSEL_OVERRIDE 0x8000 /* flag */ struct shm_acparams { u16 txop; u16 cwmin; u16 cwmax; u16 cwcur; u16 aifs; u16 bslots; u16 reggap; u16 status; u16 rsvd[8]; } __packed; #define M_EDCF_QLEN (16 * 2) #define WME_STATUS_NEWAC (1 << 8) /* M_HOST_FLAGS */ #define MHFMAX 5 /* Number of valid hostflag half-word (u16) */ #define MHF1 0 /* Hostflag 1 index */ #define MHF2 1 /* Hostflag 2 index */ #define MHF3 2 /* Hostflag 3 index */ #define MHF4 3 /* Hostflag 4 index */ #define MHF5 4 /* Hostflag 5 index */ /* Flags in M_HOST_FLAGS */ /* Enable ucode antenna diversity help */ #define MHF1_ANTDIV 0x0001 /* Enable EDCF access control */ #define MHF1_EDCF 0x0100 #define MHF1_IQSWAP_WAR 0x0200 /* Disable Slow clock request, for corerev < 11 */ #define MHF1_FORCEFASTCLK 0x0400 /* Flags in M_HOST_FLAGS2 */ /* Flush BCMC FIFO immediately */ #define MHF2_TXBCMC_NOW 0x0040 /* Enable ucode/hw power control */ #define MHF2_HWPWRCTL 0x0080 #define MHF2_NPHY40MHZ_WAR 0x0800 /* Flags in M_HOST_FLAGS3 */ /* enabled mimo antenna selection */ #define MHF3_ANTSEL_EN 0x0001 /* antenna selection mode: 0: 2x3, 1: 2x4 */ #define MHF3_ANTSEL_MODE 0x0002 #define MHF3_RESERVED1 0x0004 #define MHF3_RESERVED2 0x0008 #define MHF3_NPHY_MLADV_WAR 0x0010 /* Flags in M_HOST_FLAGS4 */ /* force bphy Tx on core 0 (board level WAR) */ #define MHF4_BPHY_TXCORE0 0x0080 /* for 4313A0 FEM boards */ #define MHF4_EXTPA_ENABLE 0x4000 /* Flags in M_HOST_FLAGS5 */ #define MHF5_4313_GPIOCTRL 0x0001 #define MHF5_RESERVED1 0x0002 #define MHF5_RESERVED2 0x0004 /* Radio power setting for ucode */ #define M_RADIO_PWR (0x32 * 2) /* phy noise recorded by ucode right after tx */ #define M_PHY_NOISE (0x037 * 2) #define PHY_NOISE_MASK 0x00ff /* * Receive Frame Data Header for 802.11b DCF-only frames * * RxFrameSize: Actual byte length of the frame data received * PAD: padding (not used) * PhyRxStatus_0: PhyRxStatus 15:0 * PhyRxStatus_1: PhyRxStatus 31:16 * PhyRxStatus_2: PhyRxStatus 47:32 * PhyRxStatus_3: PhyRxStatus 63:48 * PhyRxStatus_4: PhyRxStatus 79:64 * PhyRxStatus_5: PhyRxStatus 95:80 * RxStatus1: MAC Rx Status * RxStatus2: extended MAC Rx status * RxTSFTime: RxTSFTime time of first MAC symbol + M_PHY_PLCPRX_DLY * RxChan: gain code, channel radio code, and phy type */ struct d11rxhdr_le { __le16 RxFrameSize; u16 PAD; __le16 PhyRxStatus_0; __le16 PhyRxStatus_1; __le16 PhyRxStatus_2; __le16 PhyRxStatus_3; __le16 PhyRxStatus_4; __le16 PhyRxStatus_5; __le16 RxStatus1; __le16 RxStatus2; __le16 RxTSFTime; __le16 RxChan; } __packed; struct d11rxhdr { u16 RxFrameSize; u16 PAD; u16 PhyRxStatus_0; u16 PhyRxStatus_1; u16 PhyRxStatus_2; u16 PhyRxStatus_3; u16 PhyRxStatus_4; u16 PhyRxStatus_5; u16 RxStatus1; u16 RxStatus2; u16 RxTSFTime; u16 RxChan; } __packed; /* PhyRxStatus_0: */ /* NPHY only: CCK, OFDM, preN, N */ #define PRXS0_FT_MASK 0x0003 /* NPHY only: clip count adjustment steps by AGC */ #define PRXS0_CLIP_MASK 0x000C #define PRXS0_CLIP_SHIFT 2 /* PHY received a frame with unsupported rate */ #define PRXS0_UNSRATE 0x0010 /* GPHY: rx ant, NPHY: upper sideband */ #define PRXS0_RXANT_UPSUBBAND 0x0020 /* CCK frame only: lost crs during cck frame reception */ #define PRXS0_LCRS 0x0040 /* Short Preamble */ #define PRXS0_SHORTH 0x0080 /* PLCP violation */ #define PRXS0_PLCPFV 0x0100 /* PLCP header integrity check failed */ #define PRXS0_PLCPHCF 0x0200 /* legacy PHY gain control */ #define PRXS0_GAIN_CTL 0x4000 /* NPHY: Antennas used for received frame, bitmask */ #define PRXS0_ANTSEL_MASK 0xF000 #define PRXS0_ANTSEL_SHIFT 0x12 /* subfield PRXS0_FT_MASK */ #define PRXS0_CCK 0x0000 /* valid only for G phy, use rxh->RxChan for A phy */ #define PRXS0_OFDM 0x0001 #define PRXS0_PREN 0x0002 #define PRXS0_STDN 0x0003 /* subfield PRXS0_ANTSEL_MASK */ #define PRXS0_ANTSEL_0 0x0 /* antenna 0 is used */ #define PRXS0_ANTSEL_1 0x2 /* antenna 1 is used */ #define PRXS0_ANTSEL_2 0x4 /* antenna 2 is used */ #define PRXS0_ANTSEL_3 0x8 /* antenna 3 is used */ /* PhyRxStatus_1: */ #define PRXS1_JSSI_MASK 0x00FF #define PRXS1_JSSI_SHIFT 0 #define PRXS1_SQ_MASK 0xFF00 #define PRXS1_SQ_SHIFT 8 /* nphy PhyRxStatus_1: */ #define PRXS1_nphy_PWR0_MASK 0x00FF #define PRXS1_nphy_PWR1_MASK 0xFF00 /* HTPHY Rx Status defines */ /* htphy PhyRxStatus_0: those bit are overlapped with PhyRxStatus_0 */ #define PRXS0_BAND 0x0400 /* 0 = 2.4G, 1 = 5G */ #define PRXS0_RSVD 0x0800 /* reserved; set to 0 */ #define PRXS0_UNUSED 0xF000 /* unused and not defined; set to 0 */ /* htphy PhyRxStatus_1: */ /* core enables for {3..0}, 0=disabled, 1=enabled */ #define PRXS1_HTPHY_CORE_MASK 0x000F /* antenna configation */ #define PRXS1_HTPHY_ANTCFG_MASK 0x00F0 /* Mixmode PLCP Length low byte mask */ #define PRXS1_HTPHY_MMPLCPLenL_MASK 0xFF00 /* htphy PhyRxStatus_2: */ /* Mixmode PLCP Length high byte maskw */ #define PRXS2_HTPHY_MMPLCPLenH_MASK 0x000F /* Mixmode PLCP rate mask */ #define PRXS2_HTPHY_MMPLCH_RATE_MASK 0x00F0 /* Rx power on core 0 */ #define PRXS2_HTPHY_RXPWR_ANT0 0xFF00 /* htphy PhyRxStatus_3: */ /* Rx power on core 1 */ #define PRXS3_HTPHY_RXPWR_ANT1 0x00FF /* Rx power on core 2 */ #define PRXS3_HTPHY_RXPWR_ANT2 0xFF00 /* htphy PhyRxStatus_4: */ /* Rx power on core 3 */ #define PRXS4_HTPHY_RXPWR_ANT3 0x00FF /* Coarse frequency offset */ #define PRXS4_HTPHY_CFO 0xFF00 /* htphy PhyRxStatus_5: */ /* Fine frequency offset */ #define PRXS5_HTPHY_FFO 0x00FF /* Advance Retard */ #define PRXS5_HTPHY_AR 0xFF00 #define HTPHY_MMPLCPLen(rxs) \ ((((rxs)->PhyRxStatus_1 & PRXS1_HTPHY_MMPLCPLenL_MASK) >> 8) | \ (((rxs)->PhyRxStatus_2 & PRXS2_HTPHY_MMPLCPLenH_MASK) << 8)) /* Get Rx power on core 0 */ #define HTPHY_RXPWR_ANT0(rxs) \ ((((rxs)->PhyRxStatus_2) & PRXS2_HTPHY_RXPWR_ANT0) >> 8) /* Get Rx power on core 1 */ #define HTPHY_RXPWR_ANT1(rxs) \ (((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT1) /* Get Rx power on core 2 */ #define HTPHY_RXPWR_ANT2(rxs) \ ((((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT2) >> 8) /* ucode RxStatus1: */ #define RXS_BCNSENT 0x8000 #define RXS_SECKINDX_MASK 0x07e0 #define RXS_SECKINDX_SHIFT 5 #define RXS_DECERR (1 << 4) #define RXS_DECATMPT (1 << 3) /* PAD bytes to make IP data 4 bytes aligned */ #define RXS_PBPRES (1 << 2) #define RXS_RESPFRAMETX (1 << 1) #define RXS_FCSERR (1 << 0) /* ucode RxStatus2: */ #define RXS_AMSDU_MASK 1 #define RXS_AGGTYPE_MASK 0x6 #define RXS_AGGTYPE_SHIFT 1 #define RXS_PHYRXST_VALID (1 << 8) #define RXS_RXANT_MASK 0x3 #define RXS_RXANT_SHIFT 12 /* RxChan */ #define RXS_CHAN_40 0x1000 #define RXS_CHAN_5G 0x0800 #define RXS_CHAN_ID_MASK 0x07f8 #define RXS_CHAN_ID_SHIFT 3 #define RXS_CHAN_PHYTYPE_MASK 0x0007 #define RXS_CHAN_PHYTYPE_SHIFT 0 /* Index of attenuations used during ucode power control. */ #define M_PWRIND_BLKS (0x184 * 2) #define M_PWRIND_MAP0 (M_PWRIND_BLKS + 0x0) #define M_PWRIND_MAP1 (M_PWRIND_BLKS + 0x2) #define M_PWRIND_MAP2 (M_PWRIND_BLKS + 0x4) #define M_PWRIND_MAP3 (M_PWRIND_BLKS + 0x6) /* M_PWRIND_MAP(core) macro */ #define M_PWRIND_MAP(core) (M_PWRIND_BLKS + ((core)<<1)) /* PSM SHM variable offsets */ #define M_PSM_SOFT_REGS 0x0 #define M_BOM_REV_MAJOR (M_PSM_SOFT_REGS + 0x0) #define M_BOM_REV_MINOR (M_PSM_SOFT_REGS + 0x2) #define M_UCODE_DBGST (M_PSM_SOFT_REGS + 0x40) /* ucode debug status code */ #define M_UCODE_MACSTAT (M_PSM_SOFT_REGS + 0xE0) /* macstat counters */ #define M_AGING_THRSH (0x3e * 2) /* max time waiting for medium before tx */ #define M_MBURST_SIZE (0x40 * 2) /* max frames in a frameburst */ #define M_MBURST_TXOP (0x41 * 2) /* max frameburst TXOP in unit of us */ #define M_SYNTHPU_DLY (0x4a * 2) /* pre-wakeup for synthpu, default: 500 */ #define M_PRETBTT (0x4b * 2) /* offset to the target txpwr */ #define M_ALT_TXPWR_IDX (M_PSM_SOFT_REGS + (0x3b * 2)) #define M_PHY_TX_FLT_PTR (M_PSM_SOFT_REGS + (0x3d * 2)) #define M_CTS_DURATION (M_PSM_SOFT_REGS + (0x5c * 2)) #define M_LP_RCCAL_OVR (M_PSM_SOFT_REGS + (0x6b * 2)) /* PKTENG Rx Stats Block */ #define M_RXSTATS_BLK_PTR (M_PSM_SOFT_REGS + (0x65 * 2)) /* ucode debug status codes */ /* not valid really */ #define DBGST_INACTIVE 0 /* after zeroing SHM, before suspending at init */ #define DBGST_INIT 1 /* "normal" state */ #define DBGST_ACTIVE 2 /* suspended */ #define DBGST_SUSPENDED 3 /* asleep (PS mode) */ #define DBGST_ASLEEP 4 /* Scratch Reg defs */ enum _ePsmScratchPadRegDefinitions { S_RSV0 = 0, S_RSV1, S_RSV2, /* offset 0x03: scratch registers for Dot11-contants */ S_DOT11_CWMIN, /* CW-minimum */ S_DOT11_CWMAX, /* CW-maximum */ S_DOT11_CWCUR, /* CW-current */ S_DOT11_SRC_LMT, /* short retry count limit */ S_DOT11_LRC_LMT, /* long retry count limit */ S_DOT11_DTIMCOUNT, /* DTIM-count */ /* offset 0x09: Tx-side scratch registers */ S_SEQ_NUM, /* hardware sequence number reg */ S_SEQ_NUM_FRAG, /* seq num for frags (at the start of MSDU) */ S_FRMRETX_CNT, /* frame retx count */ S_SSRC, /* Station short retry count */ S_SLRC, /* Station long retry count */ S_EXP_RSP, /* Expected response frame */ S_OLD_BREM, /* Remaining backoff ctr */ S_OLD_CWWIN, /* saved-off CW-cur */ S_TXECTL, /* TXE-Ctl word constructed in scr-pad */ S_CTXTST, /* frm type-subtype as read from Tx-descr */ /* offset 0x13: Rx-side scratch registers */ S_RXTST, /* Type and subtype in Rxframe */ /* Global state register */ S_STREG, /* state storage actual bit maps below */ S_TXPWR_SUM, /* Tx power control: accumulator */ S_TXPWR_ITER, /* Tx power control: iteration */ S_RX_FRMTYPE, /* Rate and PHY type for frames */ S_THIS_AGG, /* Size of this AGG (A-MSDU) */ S_KEYINDX, S_RXFRMLEN, /* Receive MPDU length in bytes */ /* offset 0x1B: Receive TSF time stored in SCR */ S_RXTSFTMRVAL_WD3, /* TSF value at the start of rx */ S_RXTSFTMRVAL_WD2, /* TSF value at the start of rx */ S_RXTSFTMRVAL_WD1, /* TSF value at the start of rx */ S_RXTSFTMRVAL_WD0, /* TSF value at the start of rx */ S_RXSSN, /* Received start seq number for A-MPDU BA */ S_RXQOSFLD, /* Rx-QoS field (if present) */ /* offset 0x21: Scratch pad regs used in microcode as temp storage */ S_TMP0, /* stmp0 */ S_TMP1, /* stmp1 */ S_TMP2, /* stmp2 */ S_TMP3, /* stmp3 */ S_TMP4, /* stmp4 */ S_TMP5, /* stmp5 */ S_PRQPENALTY_CTR, /* Probe response queue penalty counter */ S_ANTCNT, /* unsuccessful attempts on current ant. */ S_SYMBOL, /* flag for possible symbol ctl frames */ S_RXTP, /* rx frame type */ S_STREG2, /* extra state storage */ S_STREG3, /* even more extra state storage */ S_STREG4, /* ... */ S_STREG5, /* remember to initialize it to zero */ S_ADJPWR_IDX, S_CUR_PTR, /* Temp pointer for A-MPDU re-Tx SHM table */ S_REVID4, /* 0x33 */ S_INDX, /* 0x34 */ S_ADDR0, /* 0x35 */ S_ADDR1, /* 0x36 */ S_ADDR2, /* 0x37 */ S_ADDR3, /* 0x38 */ S_ADDR4, /* 0x39 */ S_ADDR5, /* 0x3A */ S_TMP6, /* 0x3B */ S_KEYINDX_BU, /* Backup for Key index */ S_MFGTEST_TMP0, /* Temp regs used for RX test calculations */ S_RXESN, /* Received end sequence number for A-MPDU BA */ S_STREG6, /* 0x3F */ }; #define S_BEACON_INDX S_OLD_BREM #define S_PRS_INDX S_OLD_CWWIN #define S_PHYTYPE S_SSRC #define S_PHYVER S_SLRC /* IHR SLOW_CTRL values */ #define SLOW_CTRL_PDE (1 << 0) #define SLOW_CTRL_FD (1 << 8) /* ucode mac statistic counters in shared memory */ struct macstat { u16 txallfrm; /* 0x80 */ u16 txrtsfrm; /* 0x82 */ u16 txctsfrm; /* 0x84 */ u16 txackfrm; /* 0x86 */ u16 txdnlfrm; /* 0x88 */ u16 txbcnfrm; /* 0x8a */ u16 txfunfl[8]; /* 0x8c - 0x9b */ u16 txtplunfl; /* 0x9c */ u16 txphyerr; /* 0x9e */ u16 pktengrxducast; /* 0xa0 */ u16 pktengrxdmcast; /* 0xa2 */ u16 rxfrmtoolong; /* 0xa4 */ u16 rxfrmtooshrt; /* 0xa6 */ u16 rxinvmachdr; /* 0xa8 */ u16 rxbadfcs; /* 0xaa */ u16 rxbadplcp; /* 0xac */ u16 rxcrsglitch; /* 0xae */ u16 rxstrt; /* 0xb0 */ u16 rxdfrmucastmbss; /* 0xb2 */ u16 rxmfrmucastmbss; /* 0xb4 */ u16 rxcfrmucast; /* 0xb6 */ u16 rxrtsucast; /* 0xb8 */ u16 rxctsucast; /* 0xba */ u16 rxackucast; /* 0xbc */ u16 rxdfrmocast; /* 0xbe */ u16 rxmfrmocast; /* 0xc0 */ u16 rxcfrmocast; /* 0xc2 */ u16 rxrtsocast; /* 0xc4 */ u16 rxctsocast; /* 0xc6 */ u16 rxdfrmmcast; /* 0xc8 */ u16 rxmfrmmcast; /* 0xca */ u16 rxcfrmmcast; /* 0xcc */ u16 rxbeaconmbss; /* 0xce */ u16 rxdfrmucastobss; /* 0xd0 */ u16 rxbeaconobss; /* 0xd2 */ u16 rxrsptmout; /* 0xd4 */ u16 bcntxcancl; /* 0xd6 */ u16 PAD; u16 rxf0ovfl; /* 0xda */ u16 rxf1ovfl; /* 0xdc */ u16 rxf2ovfl; /* 0xde */ u16 txsfovfl; /* 0xe0 */ u16 pmqovfl; /* 0xe2 */ u16 rxcgprqfrm; /* 0xe4 */ u16 rxcgprsqovfl; /* 0xe6 */ u16 txcgprsfail; /* 0xe8 */ u16 txcgprssuc; /* 0xea */ u16 prs_timeout; /* 0xec */ u16 rxnack; u16 frmscons; u16 txnack; u16 txglitch_nack; u16 txburst; /* 0xf6 # tx bursts */ u16 bphy_rxcrsglitch; /* bphy rx crs glitch */ u16 phywatchdog; /* 0xfa # of phy watchdog events */ u16 PAD; u16 bphy_badplcp; /* bphy bad plcp */ }; /* dot11 core-specific control flags */ #define SICF_PCLKE 0x0004 /* PHY clock enable */ #define SICF_PRST 0x0008 /* PHY reset */ #define SICF_MPCLKE 0x0010 /* MAC PHY clockcontrol enable */ #define SICF_FREF 0x0020 /* PLL FreqRefSelect */ /* NOTE: the following bw bits only apply when the core is attached * to a NPHY */ #define SICF_BWMASK 0x00c0 /* phy clock mask (b6 & b7) */ #define SICF_BW40 0x0080 /* 40MHz BW (160MHz phyclk) */ #define SICF_BW20 0x0040 /* 20MHz BW (80MHz phyclk) */ #define SICF_BW10 0x0000 /* 10MHz BW (40MHz phyclk) */ #define SICF_GMODE 0x2000 /* gmode enable */ /* dot11 core-specific status flags */ #define SISF_2G_PHY 0x0001 /* 2.4G capable phy */ #define SISF_5G_PHY 0x0002 /* 5G capable phy */ #define SISF_FCLKA 0x0004 /* FastClkAvailable */ #define SISF_DB_PHY 0x0008 /* Dualband phy */ /* === End of MAC reg, Beginning of PHY(b/a/g/n) reg === */ /* radio and LPPHY regs are separated */ #define BPHY_REG_OFT_BASE 0x0 /* offsets for indirect access to bphy registers */ #define BPHY_BB_CONFIG 0x01 #define BPHY_ADCBIAS 0x02 #define BPHY_ANACORE 0x03 #define BPHY_PHYCRSTH 0x06 #define BPHY_TEST 0x0a #define BPHY_PA_TX_TO 0x10 #define BPHY_SYNTH_DC_TO 0x11 #define BPHY_PA_TX_TIME_UP 0x12 #define BPHY_RX_FLTR_TIME_UP 0x13 #define BPHY_TX_POWER_OVERRIDE 0x14 #define BPHY_RF_OVERRIDE 0x15 #define BPHY_RF_TR_LOOKUP1 0x16 #define BPHY_RF_TR_LOOKUP2 0x17 #define BPHY_COEFFS 0x18 #define BPHY_PLL_OUT 0x19 #define BPHY_REFRESH_MAIN 0x1a #define BPHY_REFRESH_TO0 0x1b #define BPHY_REFRESH_TO1 0x1c #define BPHY_RSSI_TRESH 0x20 #define BPHY_IQ_TRESH_HH 0x21 #define BPHY_IQ_TRESH_H 0x22 #define BPHY_IQ_TRESH_L 0x23 #define BPHY_IQ_TRESH_LL 0x24 #define BPHY_GAIN 0x25 #define BPHY_LNA_GAIN_RANGE 0x26 #define BPHY_JSSI 0x27 #define BPHY_TSSI_CTL 0x28 #define BPHY_TSSI 0x29 #define BPHY_TR_LOSS_CTL 0x2a #define BPHY_LO_LEAKAGE 0x2b #define BPHY_LO_RSSI_ACC 0x2c #define BPHY_LO_IQMAG_ACC 0x2d #define BPHY_TX_DC_OFF1 0x2e #define BPHY_TX_DC_OFF2 0x2f #define BPHY_PEAK_CNT_THRESH 0x30 #define BPHY_FREQ_OFFSET 0x31 #define BPHY_DIVERSITY_CTL 0x32 #define BPHY_PEAK_ENERGY_LO 0x33 #define BPHY_PEAK_ENERGY_HI 0x34 #define BPHY_SYNC_CTL 0x35 #define BPHY_TX_PWR_CTRL 0x36 #define BPHY_TX_EST_PWR 0x37 #define BPHY_STEP 0x38 #define BPHY_WARMUP 0x39 #define BPHY_LMS_CFF_READ 0x3a #define BPHY_LMS_COEFF_I 0x3b #define BPHY_LMS_COEFF_Q 0x3c #define BPHY_SIG_POW 0x3d #define BPHY_RFDC_CANCEL_CTL 0x3e #define BPHY_HDR_TYPE 0x40 #define BPHY_SFD_TO 0x41 #define BPHY_SFD_CTL 0x42 #define BPHY_DEBUG 0x43 #define BPHY_RX_DELAY_COMP 0x44 #define BPHY_CRS_DROP_TO 0x45 #define BPHY_SHORT_SFD_NZEROS 0x46 #define BPHY_DSSS_COEFF1 0x48 #define BPHY_DSSS_COEFF2 0x49 #define BPHY_CCK_COEFF1 0x4a #define BPHY_CCK_COEFF2 0x4b #define BPHY_TR_CORR 0x4c #define BPHY_ANGLE_SCALE 0x4d #define BPHY_TX_PWR_BASE_IDX 0x4e #define BPHY_OPTIONAL_MODES2 0x4f #define BPHY_CCK_LMS_STEP 0x50 #define BPHY_BYPASS 0x51 #define BPHY_CCK_DELAY_LONG 0x52 #define BPHY_CCK_DELAY_SHORT 0x53 #define BPHY_PPROC_CHAN_DELAY 0x54 #define BPHY_DDFS_ENABLE 0x58 #define BPHY_PHASE_SCALE 0x59 #define BPHY_FREQ_CONTROL 0x5a #define BPHY_LNA_GAIN_RANGE_10 0x5b #define BPHY_LNA_GAIN_RANGE_32 0x5c #define BPHY_OPTIONAL_MODES 0x5d #define BPHY_RX_STATUS2 0x5e #define BPHY_RX_STATUS3 0x5f #define BPHY_DAC_CONTROL 0x60 #define BPHY_ANA11G_FILT_CTRL 0x62 #define BPHY_REFRESH_CTRL 0x64 #define BPHY_RF_OVERRIDE2 0x65 #define BPHY_SPUR_CANCEL_CTRL 0x66 #define BPHY_FINE_DIGIGAIN_CTRL 0x67 #define BPHY_RSSI_LUT 0x88 #define BPHY_RSSI_LUT_END 0xa7 #define BPHY_TSSI_LUT 0xa8 #define BPHY_TSSI_LUT_END 0xc7 #define BPHY_TSSI2PWR_LUT 0x380 #define BPHY_TSSI2PWR_LUT_END 0x39f #define BPHY_LOCOMP_LUT 0x3a0 #define BPHY_LOCOMP_LUT_END 0x3bf #define BPHY_TXGAIN_LUT 0x3c0 #define BPHY_TXGAIN_LUT_END 0x3ff /* Bits in BB_CONFIG: */ #define PHY_BBC_ANT_MASK 0x0180 #define PHY_BBC_ANT_SHIFT 7 #define BB_DARWIN 0x1000 #define BBCFG_RESETCCA 0x4000 #define BBCFG_RESETRX 0x8000 /* Bits in phytest(0x0a): */ #define TST_DDFS 0x2000 #define TST_TXFILT1 0x0800 #define TST_UNSCRAM 0x0400 #define TST_CARR_SUPP 0x0200 #define TST_DC_COMP_LOOP 0x0100 #define TST_LOOPBACK 0x0080 #define TST_TXFILT0 0x0040 #define TST_TXTEST_ENABLE 0x0020 #define TST_TXTEST_RATE 0x0018 #define TST_TXTEST_PHASE 0x0007 /* phytest txTestRate values */ #define TST_TXTEST_RATE_1MBPS 0 #define TST_TXTEST_RATE_2MBPS 1 #define TST_TXTEST_RATE_5_5MBPS 2 #define TST_TXTEST_RATE_11MBPS 3 #define TST_TXTEST_RATE_SHIFT 3 #define SHM_BYT_CNT 0x2 /* IHR location */ #define MAX_BYT_CNT 0x600 /* Maximum frame len */ struct d11cnt { u32 txfrag; u32 txmulti; u32 txfail; u32 txretry; u32 txretrie; u32 rxdup; u32 txrts; u32 txnocts; u32 txnoack; u32 rxfrag; u32 rxmulti; u32 rxcrc; u32 txfrmsnt; u32 rxundec; }; #endif /* _BRCM_D11_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/channel.h0000644000175000017500000000406012026211315025350 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_CHANNEL_H_ #define _BRCM_CHANNEL_H_ /* conversion for phy txpwr calculations that use .25 dB units */ #define BRCMS_TXPWR_DB_FACTOR 4 /* bits for locale_info flags */ #define BRCMS_PEAK_CONDUCTED 0x00 /* Peak for locals */ #define BRCMS_EIRP 0x01 /* Flag for EIRP */ #define BRCMS_DFS_TPC 0x02 /* Flag for DFS TPC */ #define BRCMS_NO_OFDM 0x04 /* Flag for No OFDM */ #define BRCMS_NO_40MHZ 0x08 /* Flag for No MIMO 40MHz */ #define BRCMS_NO_MIMO 0x10 /* Flag for No MIMO, 20 or 40 MHz */ #define BRCMS_RADAR_TYPE_EU 0x20 /* Flag for EU */ #define BRCMS_DFS_FCC BRCMS_DFS_TPC /* Flag for DFS FCC */ #define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */ extern struct brcms_cm_info * brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); extern void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); extern bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec); extern void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, struct txpwr_limits *txpwr); extern void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, u8 local_constraint_qdbm); extern void brcms_c_regd_init(struct brcms_c_info *wlc); #endif /* _WLC_CHANNEL_H */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/channel.c0000644000175000017500000005166112026211315025354 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "pub.h" #include "phy/phy_hal.h" #include "main.h" #include "stf.h" #include "channel.h" #include "mac80211_if.h" /* QDB() macro takes a dB value and converts to a quarter dB value */ #define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) #define LOCALE_MIMO_IDX_bn 0 #define LOCALE_MIMO_IDX_11n 0 /* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */ #define BRCMS_MAXPWR_MIMO_TBL_SIZE 14 /* maxpwr mapping to 5GHz band channels: * maxpwr[0] - channels [34-48] * maxpwr[1] - channels [52-60] * maxpwr[2] - channels [62-64] * maxpwr[3] - channels [100-140] * maxpwr[4] - channels [149-165] */ #define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */ #define LC(id) LOCALE_MIMO_IDX_ ## id #define LOCALES(mimo2, mimo5) \ {LC(mimo2), LC(mimo5)} /* macro to get 5 GHz channel group index for tx power */ #define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \ (((c) < 62) ? 1 : \ (((c) < 100) ? 2 : \ (((c) < 149) ? 3 : 4)))) #define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0) #define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_NO_IBSS) #define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_NO_IBSS) #define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_DFS | \ NL80211_RRF_NO_IBSS) #define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_DFS | \ NL80211_RRF_NO_IBSS) #define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \ NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_NO_IBSS) static const struct ieee80211_regdomain brcms_regdom_x2 = { .n_reg_rules = 6, .alpha2 = "X2", .reg_rules = { BRCM_2GHZ_2412_2462, BRCM_2GHZ_2467_2472, BRCM_5GHZ_5180_5240, BRCM_5GHZ_5260_5320, BRCM_5GHZ_5500_5700, BRCM_5GHZ_5745_5825, } }; /* locale per-channel tx power limits for MIMO frames * maxpwr arrays are index by channel for 2.4 GHz limits, and * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel) */ struct locale_mimo_info { /* tx 20 MHz power limits, qdBm units */ s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE]; /* tx 40 MHz power limits, qdBm units */ s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE]; }; /* Country names and abbreviations with locale defined from ISO 3166 */ struct country_info { const u8 locale_mimo_2G; /* 2.4G mimo info */ const u8 locale_mimo_5G; /* 5G mimo info */ }; struct brcms_regd { struct country_info country; const struct ieee80211_regdomain *regdomain; }; struct brcms_cm_info { struct brcms_pub *pub; struct brcms_c_info *wlc; const struct brcms_regd *world_regd; }; /* * MIMO Locale Definitions - 2.4 GHz */ static const struct locale_mimo_info locale_bn = { {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13)}, {0, 0, QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), 0, 0}, }; static const struct locale_mimo_info *g_mimo_2g_table[] = { &locale_bn }; /* * MIMO Locale Definitions - 5 GHz */ static const struct locale_mimo_info locale_11n = { { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)}, {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)}, }; static const struct locale_mimo_info *g_mimo_5g_table[] = { &locale_11n }; static const struct brcms_regd cntry_locales[] = { /* Worldwide RoW 2, must always be at index 0 */ { .country = LOCALES(bn, 11n), .regdomain = &brcms_regdom_x2, }, }; static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx) { if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) return NULL; return g_mimo_2g_table[locale_idx]; } static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx) { if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) return NULL; return g_mimo_5g_table[locale_idx]; } /* * Indicates whether the country provided is valid to pass * to cfg80211 or not. * * returns true if valid; false if not. */ static bool brcms_c_country_valid(const char *ccode) { /* * only allow ascii alpha uppercase for the first 2 * chars. */ if (!((0x80 & ccode[0]) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A && (0x80 & ccode[1]) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A && ccode[2] == '\0')) return false; /* * do not match ISO 3166-1 user assigned country codes * that may be in the driver table */ if (!strcmp("AA", ccode) || /* AA */ !strcmp("ZZ", ccode) || /* ZZ */ ccode[0] == 'X' || /* XA - XZ */ (ccode[0] == 'Q' && /* QM - QZ */ (ccode[1] >= 'M' && ccode[1] <= 'Z'))) return false; if (!strcmp("NA", ccode)) return false; return true; } static const struct brcms_regd *brcms_world_regd(const char *regdom, int len) { const struct brcms_regd *regd = NULL; int i; for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) { if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) { regd = &cntry_locales[i]; break; } } return regd; } static const struct brcms_regd *brcms_default_world_regd(void) { return &cntry_locales[0]; } /* JP, J1 - J10 are Japan ccodes */ static bool brcms_c_japan_ccode(const char *ccode) { return (ccode[0] == 'J' && (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9'))); } static void brcms_c_channel_min_txpower_limits_with_local_constraint( struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr, u8 local_constraint_qdbm) { int j; /* CCK Rates */ for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm); /* 20 MHz Legacy OFDM SISO */ for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm); /* 20 MHz Legacy OFDM CDD */ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) txpwr->ofdm_cdd[j] = min(txpwr->ofdm_cdd[j], local_constraint_qdbm); /* 40 MHz Legacy OFDM SISO */ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) txpwr->ofdm_40_siso[j] = min(txpwr->ofdm_40_siso[j], local_constraint_qdbm); /* 40 MHz Legacy OFDM CDD */ for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) txpwr->ofdm_40_cdd[j] = min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm); /* 20MHz MCS 0-7 SISO */ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) txpwr->mcs_20_siso[j] = min(txpwr->mcs_20_siso[j], local_constraint_qdbm); /* 20MHz MCS 0-7 CDD */ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) txpwr->mcs_20_cdd[j] = min(txpwr->mcs_20_cdd[j], local_constraint_qdbm); /* 20MHz MCS 0-7 STBC */ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) txpwr->mcs_20_stbc[j] = min(txpwr->mcs_20_stbc[j], local_constraint_qdbm); /* 20MHz MCS 8-15 MIMO */ for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) txpwr->mcs_20_mimo[j] = min(txpwr->mcs_20_mimo[j], local_constraint_qdbm); /* 40MHz MCS 0-7 SISO */ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) txpwr->mcs_40_siso[j] = min(txpwr->mcs_40_siso[j], local_constraint_qdbm); /* 40MHz MCS 0-7 CDD */ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) txpwr->mcs_40_cdd[j] = min(txpwr->mcs_40_cdd[j], local_constraint_qdbm); /* 40MHz MCS 0-7 STBC */ for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) txpwr->mcs_40_stbc[j] = min(txpwr->mcs_40_stbc[j], local_constraint_qdbm); /* 40MHz MCS 8-15 MIMO */ for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) txpwr->mcs_40_mimo[j] = min(txpwr->mcs_40_mimo[j], local_constraint_qdbm); /* 40MHz MCS 32 */ txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm); } /* * set the driver's current country and regulatory information * using a country code as the source. Look up built in country * information found with the country code. */ static void brcms_c_set_country(struct brcms_cm_info *wlc_cm, const struct brcms_regd *regd) { struct brcms_c_info *wlc = wlc_cm->wlc; if ((wlc->pub->_n_enab & SUPPORT_11N) != wlc->protection->nmode_user) brcms_c_set_nmode(wlc); brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); return; } struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) { struct brcms_cm_info *wlc_cm; struct brcms_pub *pub = wlc->pub; struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; const char *ccode = sprom->alpha2; int ccode_len = sizeof(sprom->alpha2); BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC); if (wlc_cm == NULL) return NULL; wlc_cm->pub = pub; wlc_cm->wlc = wlc; wlc->cmi = wlc_cm; /* store the country code for passing up as a regulatory hint */ wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len); if (brcms_c_country_valid(ccode)) strncpy(wlc->pub->srom_ccode, ccode, ccode_len); /* * If no custom world domain is found in the SROM, use the * default "X2" domain. */ if (!wlc_cm->world_regd) { wlc_cm->world_regd = brcms_default_world_regd(); ccode = wlc_cm->world_regd->regdomain->alpha2; ccode_len = BRCM_CNTRY_BUF_SZ - 1; } /* save default country for exiting 11d regulatory mode */ strncpy(wlc->country_default, ccode, ccode_len); /* initialize autocountry_default to driver default */ strncpy(wlc->autocountry_default, ccode, ccode_len); brcms_c_set_country(wlc_cm, wlc_cm->world_regd); return wlc_cm; } void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm) { kfree(wlc_cm); } void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, u8 local_constraint_qdbm) { struct brcms_c_info *wlc = wlc_cm->wlc; struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; struct txpwr_limits txpwr; brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr); brcms_c_channel_min_txpower_limits_with_local_constraint( wlc_cm, &txpwr, local_constraint_qdbm ); /* set or restore gmode as required by regulatory */ if (ch->flags & IEEE80211_CHAN_NO_OFDM) brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false); else brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); brcms_b_set_chanspec(wlc->hw, chanspec, !!(ch->flags & IEEE80211_CHAN_PASSIVE_SCAN), &txpwr); } void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, struct txpwr_limits *txpwr) { struct brcms_c_info *wlc = wlc_cm->wlc; struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; uint i; uint chan; int maxpwr; int delta; const struct country_info *country; struct brcms_band *band; int conducted_max = BRCMS_TXPWR_MAX; const struct locale_mimo_info *li_mimo; int maxpwr20, maxpwr40; int maxpwr_idx; uint j; memset(txpwr, 0, sizeof(struct txpwr_limits)); if (WARN_ON(!ch)) return; country = &wlc_cm->world_regd->country; chan = CHSPEC_CHANNEL(chanspec); band = wlc->bandstate[chspec_bandunit(chanspec)]; li_mimo = (band->bandtype == BRCM_BAND_5G) ? brcms_c_get_mimo_5g(country->locale_mimo_5G) : brcms_c_get_mimo_2g(country->locale_mimo_2G); delta = band->antgain; if (band->bandtype == BRCM_BAND_2G) conducted_max = QDB(22); maxpwr = QDB(ch->max_power) - delta; maxpwr = max(maxpwr, 0); maxpwr = min(maxpwr, conducted_max); /* CCK txpwr limits for 2.4G band */ if (band->bandtype == BRCM_BAND_2G) { for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) txpwr->cck[i] = (u8) maxpwr; } for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) { txpwr->ofdm[i] = (u8) maxpwr; /* * OFDM 40 MHz SISO has the same power as the corresponding * MCS0-7 rate unless overriden by the locale specific code. * We set this value to 0 as a flag (presumably 0 dBm isn't * a possibility) and then copy the MCS0-7 value to the 40 MHz * value if it wasn't explicitly set. */ txpwr->ofdm_40_siso[i] = 0; txpwr->ofdm_cdd[i] = (u8) maxpwr; txpwr->ofdm_40_cdd[i] = 0; } delta = 0; if (band->antgain > QDB(6)) delta = band->antgain - QDB(6); /* Excess over 6 dB */ if (band->bandtype == BRCM_BAND_2G) maxpwr_idx = (chan - 1); else maxpwr_idx = CHANNEL_POWER_IDX_5G(chan); maxpwr20 = li_mimo->maxpwr20[maxpwr_idx]; maxpwr40 = li_mimo->maxpwr40[maxpwr_idx]; maxpwr20 = maxpwr20 - delta; maxpwr20 = max(maxpwr20, 0); maxpwr40 = maxpwr40 - delta; maxpwr40 = max(maxpwr40, 0); /* Fill in the MCS 0-7 (SISO) rates */ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { /* * 20 MHz has the same power as the corresponding OFDM rate * unless overriden by the locale specific code. */ txpwr->mcs_20_siso[i] = txpwr->ofdm[i]; txpwr->mcs_40_siso[i] = 0; } /* Fill in the MCS 0-7 CDD rates */ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { txpwr->mcs_20_cdd[i] = (u8) maxpwr20; txpwr->mcs_40_cdd[i] = (u8) maxpwr40; } /* * These locales have SISO expressed in the * table and override CDD later */ if (li_mimo == &locale_bn) { if (li_mimo == &locale_bn) { maxpwr20 = QDB(16); maxpwr40 = 0; if (chan >= 3 && chan <= 11) maxpwr40 = QDB(16); } for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { txpwr->mcs_20_siso[i] = (u8) maxpwr20; txpwr->mcs_40_siso[i] = (u8) maxpwr40; } } /* Fill in the MCS 0-7 STBC rates */ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { txpwr->mcs_20_stbc[i] = 0; txpwr->mcs_40_stbc[i] = 0; } /* Fill in the MCS 8-15 SDM rates */ for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) { txpwr->mcs_20_mimo[i] = (u8) maxpwr20; txpwr->mcs_40_mimo[i] = (u8) maxpwr40; } /* Fill in MCS32 */ txpwr->mcs32 = (u8) maxpwr40; for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { if (txpwr->ofdm_40_cdd[i] == 0) txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; if (i == 0) { i = i + 1; if (txpwr->ofdm_40_cdd[i] == 0) txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; } } /* * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO * value if it wasn't provided explicitly. */ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { if (txpwr->mcs_40_siso[i] == 0) txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i]; } for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { if (txpwr->ofdm_40_siso[i] == 0) txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; if (i == 0) { i = i + 1; if (txpwr->ofdm_40_siso[i] == 0) txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; } } /* * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding * STBC values if they weren't provided explicitly. */ for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { if (txpwr->mcs_20_stbc[i] == 0) txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i]; if (txpwr->mcs_40_stbc[i] == 0) txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i]; } return; } /* * Verify the chanspec is using a legal set of parameters, i.e. that the * chanspec specified a band, bw, ctl_sb and channel and that the * combination could be legal given any set of circumstances. * RETURNS: true is the chanspec is malformed, false if it looks good. */ static bool brcms_c_chspec_malformed(u16 chanspec) { /* must be 2G or 5G band */ if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) return true; /* must be 20 or 40 bandwidth */ if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) return true; /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ if (CHSPEC_IS20(chanspec)) { if (!CHSPEC_SB_NONE(chanspec)) return true; } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) { return true; } return false; } /* * Validate the chanspec for this locale, for 40MHZ we need to also * check that the sidebands are valid 20MZH channels in this locale * and they are also a legal HT combination */ static bool brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec) { struct brcms_c_info *wlc = wlc_cm->wlc; u8 channel = CHSPEC_CHANNEL(chspec); /* check the chanspec */ if (brcms_c_chspec_malformed(chspec)) { wiphy_err(wlc->wiphy, "wl%d: malformed chanspec 0x%x\n", wlc->pub->unit, chspec); return false; } if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) != chspec_bandunit(chspec)) return false; return true; } bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec) { return brcms_c_valid_chanspec_ext(wlc_cm, chspec); } static bool brcms_is_radar_freq(u16 center_freq) { return center_freq >= 5260 && center_freq <= 5700; } static void brcms_reg_apply_radar_flags(struct wiphy *wiphy) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; int i; sband = wiphy->bands[IEEE80211_BAND_5GHZ]; if (!sband) return; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (!brcms_is_radar_freq(ch->center_freq)) continue; /* * All channels in this range should be passive and have * DFS enabled. */ if (!(ch->flags & IEEE80211_CHAN_DISABLED)) ch->flags |= IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN; } } static void brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *rule; int band, i, ret; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (ch->flags & (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { ret = freq_reg_info(wiphy, ch->center_freq, 0, &rule); if (ret) continue; if (!(rule->flags & NL80211_RRF_NO_IBSS)) ch->flags &= ~IEEE80211_CHAN_NO_IBSS; if (!(rule->flags & NL80211_RRF_PASSIVE_SCAN)) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } else if (ch->beacon_found) { ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN); } } } } static int brcms_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct brcms_info *wl = hw->priv; struct brcms_c_info *wlc = wl->wlc; struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; int band, i; bool ch_found = false; brcms_reg_apply_radar_flags(wiphy); if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) brcms_reg_apply_beaconing_flags(wiphy, request->initiator); /* Disable radio if all channels disallowed by regulatory */ for (band = 0; !ch_found && band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; if (!sband) continue; for (i = 0; !ch_found && i < sband->n_channels; i++) { ch = &sband->channels[i]; if (!(ch->flags & IEEE80211_CHAN_DISABLED)) ch_found = true; } } if (ch_found) { mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); } else { mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\"\n", wlc->pub->unit, __func__, request->alpha2); } if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, brcms_c_japan_ccode(request->alpha2)); return 0; } void brcms_c_regd_init(struct brcms_c_info *wlc) { struct wiphy *wiphy = wlc->wiphy; const struct brcms_regd *regd = wlc->cmi->world_regd; struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; struct brcms_chanvec sup_chan; struct brcms_band *band; int band_idx, i; /* Disable any channels not supported by the phy */ for (band_idx = 0; band_idx < wlc->pub->_nbands; band_idx++) { band = wlc->bandstate[band_idx]; wlc_phy_chanspec_band_validch(band->pi, band->bandtype, &sup_chan); if (band_idx == BAND_2G_INDEX) sband = wiphy->bands[IEEE80211_BAND_2GHZ]; else sband = wiphy->bands[IEEE80211_BAND_5GHZ]; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (!isset(sup_chan.vec, ch->hw_value)) ch->flags |= IEEE80211_CHAN_DISABLED; } } wlc->wiphy->reg_notifier = brcms_reg_notifier; wlc->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_STRICT_REGULATORY; wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain); brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h0000644000175000017500000000453512026211315027617 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef TRACE_SYSTEM #define TRACE_SYSTEM brcmsmac #if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ) #define __TRACE_BRCMSMAC_H #include #include "mac80211_if.h" #ifndef CONFIG_BRCMDBG #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ static inline void trace_ ## name(proto) {} #endif /* * We define a tracepoint, its arguments, its printk format and its * 'fast binary record' layout. */ TRACE_EVENT(brcms_timer, /* TPPROTO is the prototype of the function called by this tracepoint */ TP_PROTO(struct brcms_timer *t), /* * TPARGS(firstarg, p) are the parameters names, same as found in the * prototype. */ TP_ARGS(t), /* * Fast binary tracing: define the trace record via TP_STRUCT__entry(). * You can think about it like a regular C structure local variable * definition. */ TP_STRUCT__entry( __field(uint, ms) __field(uint, set) __field(uint, periodic) ), TP_fast_assign( __entry->ms = t->ms; __entry->set = t->set; __entry->periodic = t->periodic; ), TP_printk( "ms=%u set=%u periodic=%u", __entry->ms, __entry->set, __entry->periodic ) ); TRACE_EVENT(brcms_dpc, TP_PROTO(unsigned long data), TP_ARGS(data), TP_STRUCT__entry( __field(unsigned long, data) ), TP_fast_assign( __entry->data = data; ), TP_printk( "data=%p", (void *)__entry->data ) ); #endif /* __TRACE_BRCMSMAC_H */ #ifdef CONFIG_BRCMDBG #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH . #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE brcms_trace_events #include #endif /* CONFIG_BRCMDBG */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c0000644000175000017500000000171412026211315027606 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* bug in tracepoint.h, it should include this */ #ifndef __CHECKER__ #include "mac80211_if.h" #define CREATE_TRACE_POINTS #include "brcms_trace_events.h" #endif compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/antsel.h0000644000175000017500000000237412026211315025234 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_ANTSEL_H_ #define _BRCM_ANTSEL_H_ extern struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc); extern void brcms_c_antsel_detach(struct antsel_info *asi); extern void brcms_c_antsel_init(struct antsel_info *asi); extern void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, u8 id, u8 fbid, u8 *antcfg, u8 *fbantcfg); extern u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel); #endif /* _BRCM_ANTSEL_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/antsel.c0000644000175000017500000002264412026211315025231 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "types.h" #include "main.h" #include "phy_shim.h" #include "antsel.h" #define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */ #define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */ #define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */ #define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */ #define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */ #define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */ /* useful macros */ #define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf) #define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf) #define BRCMS_ANTIDX_11N(ant) (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\ (BRCMS_ANTSEL_11N_1(ant))) #define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) #define BRCMS_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK) /* antenna switch */ /* defines for no boardlevel antenna diversity */ #define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */ /* 2x3 antdiv defines and tables for GPIO communication */ #define ANT_SELCFG_NUM_2x3 3 #define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */ /* 2x4 antdiv rev4 defines and tables for GPIO communication */ #define ANT_SELCFG_NUM_2x4 4 #define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */ static const u16 mimo_2x4_div_antselpat_tbl[] = { 0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */ 0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */ 0, 0, 0, 0, /* n.a. */ 0, 0, 0, 0 /* n.a. */ }; static const u8 mimo_2x4_div_antselid_tbl[16] = { 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */ }; static const u16 mimo_2x3_div_antselpat_tbl[] = { 16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */ 16, 16, 16, 16, /* n.a. */ 16, 2, 16, 16, /* ant0: 2 ant1: 1 */ 16, 16, 16, 16 /* n.a. */ }; static const u8 mimo_2x3_div_antselid_tbl[16] = { 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */ }; /* boardlevel antenna selection: init antenna selection structure */ static void brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel, bool auto_sel) { if (asi->antsel_type == ANTSEL_2x3) { u8 antcfg_def = ANT_SELCFG_DEF_2x3 | ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0); antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def; antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def; antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def; antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def; antsel->num_antcfg = ANT_SELCFG_NUM_2x3; } else if (asi->antsel_type == ANTSEL_2x4) { antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4; antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4; antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4; antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4; antsel->num_antcfg = ANT_SELCFG_NUM_2x4; } else { /* no antenna selection available */ antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2; antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2; antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2; antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2; antsel->num_antcfg = 0; } } struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc) { struct antsel_info *asi; struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC); if (!asi) return NULL; asi->wlc = wlc; asi->pub = wlc->pub; asi->antsel_type = ANTSEL_NA; asi->antsel_avail = false; asi->antsel_antswitch = sprom->antswitch; if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) { switch (asi->antsel_antswitch) { case ANTSWITCH_TYPE_1: case ANTSWITCH_TYPE_2: case ANTSWITCH_TYPE_3: /* 4321/2 board with 2x3 switch logic */ asi->antsel_type = ANTSEL_2x3; /* Antenna selection availability */ if ((sprom->ant_available_bg == 7) || (sprom->ant_available_a == 7)) { asi->antsel_avail = true; } else if ( sprom->ant_available_bg == 3 || sprom->ant_available_a == 3) { asi->antsel_avail = false; } else { asi->antsel_avail = false; wiphy_err(wlc->wiphy, "antsel_attach: 2o3 " "board cfg invalid\n"); } break; default: break; } } else if ((asi->pub->sromrev == 4) && (sprom->ant_available_bg == 7) && (sprom->ant_available_a == 0)) { /* hack to match old 4321CB2 cards with 2of3 antenna switch */ asi->antsel_type = ANTSEL_2x3; asi->antsel_avail = true; } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) { asi->antsel_type = ANTSEL_2x4; asi->antsel_avail = true; } /* Set the antenna selection type for the low driver */ brcms_b_antsel_type_set(wlc->hw, asi->antsel_type); /* Init (auto/manual) antenna selection */ brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true); brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true); return asi; } void brcms_c_antsel_detach(struct antsel_info *asi) { kfree(asi); } /* * boardlevel antenna selection: * convert ant_cfg to mimo_antsel (ucode interface) */ static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg) { u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg)); u16 mimo_antsel = 0; if (asi->antsel_type == ANTSEL_2x4) { /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf); return mimo_antsel; } else if (asi->antsel_type == ANTSEL_2x3) { /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf); return mimo_antsel; } return mimo_antsel; } /* boardlevel antenna selection: ucode interface control */ static int brcms_c_antsel_cfgupd(struct antsel_info *asi, struct brcms_antselcfg *antsel) { struct brcms_c_info *wlc = asi->wlc; u8 ant_cfg; u16 mimo_antsel; /* 1) Update TX antconfig for all frames that are not unicast data * (aka default TX) */ ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF]; mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg); brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel); /* * Update driver stats for currently selected * default tx/rx antenna config */ asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg; /* 2) Update RX antconfig for all frames that are not unicast data * (aka default RX) */ ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF]; mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg); brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel); /* * Update driver stats for currently selected * default tx/rx antenna config */ asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg; return 0; } void brcms_c_antsel_init(struct antsel_info *asi) { if ((asi->antsel_type == ANTSEL_2x3) || (asi->antsel_type == ANTSEL_2x4)) brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n); } /* boardlevel antenna selection: convert id to ant_cfg */ static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id) { u8 antcfg = ANT_SELCFG_DEF_2x2; if (asi->antsel_type == ANTSEL_2x4) { /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2)); return antcfg; } else if (asi->antsel_type == ANTSEL_2x3) { /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1)); return antcfg; } return antcfg; } void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, u8 antselid, u8 fbantselid, u8 *antcfg, u8 *fbantcfg) { u8 ant; /* if use default, assign it and return */ if (usedef) { *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF]; *fbantcfg = *antcfg; return; } if (!sel) { *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; *fbantcfg = *antcfg; } else { ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) { *antcfg = brcms_c_antsel_id2antcfg(asi, antselid); *fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid); } else { *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; *fbantcfg = *antcfg; } } return; } /* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */ u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel) { u8 antselid = 0; if (asi->antsel_type == ANTSEL_2x4) { /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)]; return antselid; } else if (asi->antsel_type == ANTSEL_2x3) { /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)]; return antselid; } return antselid; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h0000644000175000017500000000254612026211315025055 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_AMPDU_H_ #define _BRCM_AMPDU_H_ extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu); extern int brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, struct sk_buff **aggp, int prec); extern void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p, struct tx_status *txs); extern void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); extern void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu); #endif /* _BRCM_AMPDU_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c0000644000175000017500000010540712026211315025050 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "rate.h" #include "scb.h" #include "phy/phy_hal.h" #include "antsel.h" #include "main.h" #include "ampdu.h" /* max number of mpdus in an ampdu */ #define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu to a legacy */ #define AMPDU_NUM_MPDU_LEGACY 16 /* max Tx ba window size (in pdu) */ #define AMPDU_TX_BA_MAX_WSIZE 64 /* default Tx ba window size (in pdu) */ #define AMPDU_TX_BA_DEF_WSIZE 64 /* default Rx ba window size (in pdu) */ #define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */ #define AMPDU_RX_BA_MAX_WSIZE 64 /* max dur of tx ampdu (in msec) */ #define AMPDU_MAX_DUR 5 /* default tx retry limit */ #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit at reg rate */ #define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default weight of ampdu in txfifo */ #define AMPDU_DEF_TXPKT_WEIGHT 2 /* default ffpld reserved bytes */ #define AMPDU_DEF_FFPLD_RSVD 2048 /* # of inis to be freed on detach */ #define AMPDU_INI_FREE 10 /* max # of mpdus released at a time */ #define AMPDU_SCB_MAX_RELEASE 20 #define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */ #define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu * without underflows */ #define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */ #define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */ #define FFPLD_PLD_INCR 1000 /* increments in bytes */ #define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we * accumulate between resets. */ #define AMPDU_DELIMITER_LEN 4 /* max allowed number of mpdus in an ampdu (2 streams) */ #define AMPDU_NUM_MPDU 16 #define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE) /* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */ #define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\ AMPDU_DELIMITER_LEN + 3\ + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN) /* modulo add/sub, bound = 2^k */ #define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) #define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) /* structure to hold tx fifo information and pre-loading state * counters specific to tx underflows of ampdus * some counters might be redundant with the ones in wlc or ampdu structures. * This allows to maintain a specific state independently of * how often and/or when the wlc counters are updated. * * ampdu_pld_size: number of bytes to be pre-loaded * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu * prev_txfunfl: num of underflows last read from the HW macstats counter * accum_txfunfl: num of underflows since we modified pld params * accum_txampdu: num of tx ampdu since we modified pld params * prev_txampdu: previous reading of tx ampdu * dmaxferrate: estimated dma avg xfer rate in kbits/sec */ struct brcms_fifo_info { u16 ampdu_pld_size; u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; u16 prev_txfunfl; u32 accum_txfunfl; u32 accum_txampdu; u32 prev_txampdu; u32 dmaxferrate; }; /* AMPDU module specific state * * wlc: pointer to main wlc structure * scb_handle: scb cubby handle to retrieve data from scb * ini_enable: per-tid initiator enable/disable of ampdu * ba_tx_wsize: Tx ba window size (in pdu) * ba_rx_wsize: Rx ba window size (in pdu) * retry_limit: mpdu transmit retry limit * rr_retry_limit: mpdu transmit retry limit at regular rate * retry_limit_tid: per-tid mpdu transmit retry limit * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec * max_pdu: max pdus allowed in ampdu * dur: max duration of an ampdu (in msec) * txpkt_weight: weight of ampdu in txfifo; reduces rate lag * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes * ffpld_rsvd: number of bytes to reserve for preload * max_txlen: max size of ampdu per mcs, bw and sgi * mfbr: enable multiple fallback rate * tx_max_funl: underflows should be kept such that * (tx_max_funfl*underflows) < tx frames * fifo_tb: table of fifo infos */ struct ampdu_info { struct brcms_c_info *wlc; int scb_handle; u8 ini_enable[AMPDU_MAX_SCB_TID]; u8 ba_tx_wsize; u8 ba_rx_wsize; u8 retry_limit; u8 rr_retry_limit; u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID]; u8 mpdu_density; s8 max_pdu; u8 dur; u8 txpkt_weight; u8 rx_factor; u32 ffpld_rsvd; u32 max_txlen[MCS_TABLE_SIZE][2][2]; bool mfbr; u32 tx_max_funl; struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO]; }; /* used for flushing ampdu packets */ struct cb_del_ampdu_pars { struct ieee80211_sta *sta; u16 tid; }; static void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur) { u32 rate, mcs; for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) { /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */ /* 20MHz, No SGI */ rate = mcs_2_rate(mcs, false, false); ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3; /* 40 MHz, No SGI */ rate = mcs_2_rate(mcs, true, false); ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3; /* 20MHz, SGI */ rate = mcs_2_rate(mcs, false, true); ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3; /* 40 MHz, SGI */ rate = mcs_2_rate(mcs, true, true); ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3; } } static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu) { if (BRCMS_PHY_11N_CAP(ampdu->wlc->band)) return true; else return false; } static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on) { struct brcms_c_info *wlc = ampdu->wlc; wlc->pub->_ampdu = false; if (on) { if (!(wlc->pub->_n_enab & SUPPORT_11N)) { wiphy_err(ampdu->wlc->wiphy, "wl%d: driver not " "nmode enabled\n", wlc->pub->unit); return -ENOTSUPP; } if (!brcms_c_ampdu_cap(ampdu)) { wiphy_err(ampdu->wlc->wiphy, "wl%d: device not " "ampdu capable\n", wlc->pub->unit); return -ENOTSUPP; } wlc->pub->_ampdu = on; } return 0; } static void brcms_c_ffpld_init(struct ampdu_info *ampdu) { int i, j; struct brcms_fifo_info *fifo; for (j = 0; j < NUM_FFPLD_FIFO; j++) { fifo = (ampdu->fifo_tb + j); fifo->ampdu_pld_size = 0; for (i = 0; i <= FFPLD_MAX_MCS; i++) fifo->mcs2ampdu_table[i] = 255; fifo->dmaxferrate = 0; fifo->accum_txampdu = 0; fifo->prev_txfunfl = 0; fifo->accum_txfunfl = 0; } } struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc) { struct ampdu_info *ampdu; int i; ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC); if (!ampdu) return NULL; ampdu->wlc = wlc; for (i = 0; i < AMPDU_MAX_SCB_TID; i++) ampdu->ini_enable[i] = true; /* Disable ampdu for VO by default */ ampdu->ini_enable[PRIO_8021D_VO] = false; ampdu->ini_enable[PRIO_8021D_NC] = false; /* Disable ampdu for BK by default since not enough fifo space */ ampdu->ini_enable[PRIO_8021D_NONE] = false; ampdu->ini_enable[PRIO_8021D_BK] = false; ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE; ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE; ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY; ampdu->max_pdu = AUTO; ampdu->dur = AMPDU_MAX_DUR; ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT; ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD; /* * bump max ampdu rcv size to 64k for all 11n * devices except 4321A0 and 4321A1 */ if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2)) ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K; else ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K; ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT; ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT; for (i = 0; i < AMPDU_MAX_SCB_TID; i++) { ampdu->retry_limit_tid[i] = ampdu->retry_limit; ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit; } brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur); ampdu->mfbr = false; /* try to set ampdu to the default value */ brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu); ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL; brcms_c_ffpld_init(ampdu); return ampdu; } void brcms_c_ampdu_detach(struct ampdu_info *ampdu) { kfree(ampdu); } static void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb) { struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; int i; scb_ampdu->max_pdu = AMPDU_NUM_MPDU; /* go back to legacy size if some preloading is occurring */ for (i = 0; i < NUM_FFPLD_FIFO; i++) { if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR) scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY; } /* apply user override */ if (ampdu->max_pdu != AUTO) scb_ampdu->max_pdu = (u8) ampdu->max_pdu; scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE); if (scb_ampdu->max_rx_ampdu_bytes) scb_ampdu->release = min_t(u8, scb_ampdu->release, scb_ampdu->max_rx_ampdu_bytes / 1600); scb_ampdu->release = min(scb_ampdu->release, ampdu->fifo_tb[TX_AC_BE_FIFO]. mcs2ampdu_table[FFPLD_MAX_MCS]); } static void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu) { brcms_c_scb_ampdu_update_config(ampdu, &du->wlc->pri_scb); } static void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f) { int i; u32 phy_rate, dma_rate, tmp; u8 max_mpdu; struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f); /* recompute the dma rate */ /* note : we divide/multiply by 100 to avoid integer overflows */ max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY); phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); dma_rate = (((phy_rate / 100) * (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; fifo->dmaxferrate = dma_rate; /* fill up the mcs2ampdu table; do not recalc the last mcs */ dma_rate = dma_rate >> 7; for (i = 0; i < FFPLD_MAX_MCS; i++) { /* shifting to keep it within integer range */ phy_rate = mcs_2_rate(i, true, false) >> 7; if (phy_rate > dma_rate) { tmp = ((fifo->ampdu_pld_size * phy_rate) / ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1; tmp = min_t(u32, tmp, 255); fifo->mcs2ampdu_table[i] = (u8) tmp; } } } /* evaluate the dma transfer rate using the tx underflows as feedback. * If necessary, increase tx fifo preloading. If not enough, * decrease maximum ampdu size for each mcs till underflows stop * Return 1 if pre-loading not active, -1 if not an underflow event, * 0 if pre-loading module took care of the event. */ static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid) { struct ampdu_info *ampdu = wlc->ampdu; u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); u32 txunfl_ratio; u8 max_mpdu; u32 current_ampdu_cnt = 0; u16 max_pld_size; u32 new_txunfl; struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid); uint xmtfifo_sz; u16 cur_txunfl; /* return if we got here for a different reason than underflows */ cur_txunfl = brcms_b_read_shm(wlc->hw, M_UCODE_MACSTAT + offsetof(struct macstat, txfunfl[fid])); new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl); if (new_txunfl == 0) { BCMMSG(wlc->wiphy, "TX status FRAG set but no tx underflows\n"); return -1; } fifo->prev_txfunfl = cur_txunfl; if (!ampdu->tx_max_funl) return 1; /* check if fifo is big enough */ if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz)) return -1; if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd) return 1; max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd; fifo->accum_txfunfl += new_txunfl; /* we need to wait for at least 10 underflows */ if (fifo->accum_txfunfl < 10) return 0; BCMMSG(wlc->wiphy, "ampdu_count %d tx_underflows %d\n", current_ampdu_cnt, fifo->accum_txfunfl); /* compute the current ratio of tx unfl per ampdu. When the current ampdu count becomes too big while the ratio remains small, we reset the current count in order to not introduce too big of a latency in detecting a large amount of tx underflows later. */ txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl; if (txunfl_ratio > ampdu->tx_max_funl) { if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) fifo->accum_txfunfl = 0; return 0; } max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY); /* In case max value max_pdu is already lower than the fifo depth, there is nothing more we can do. */ if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) { fifo->accum_txfunfl = 0; return 0; } if (fifo->ampdu_pld_size < max_pld_size) { /* increment by TX_FIFO_PLD_INC bytes */ fifo->ampdu_pld_size += FFPLD_PLD_INCR; if (fifo->ampdu_pld_size > max_pld_size) fifo->ampdu_pld_size = max_pld_size; /* update scb release size */ brcms_c_scb_ampdu_update_config_all(ampdu); /* * compute a new dma xfer rate for max_mpdu @ max mcs. * This is the minimum dma rate that can achieve no * underflow condition for the current mpdu size. * * note : we divide/multiply by 100 to avoid integer overflows */ fifo->dmaxferrate = (((phy_rate / 100) * (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; BCMMSG(wlc->wiphy, "DMA estimated transfer rate %d; " "pre-load size %d\n", fifo->dmaxferrate, fifo->ampdu_pld_size); } else { /* decrease ampdu size */ if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) { if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255) fifo->mcs2ampdu_table[FFPLD_MAX_MCS] = AMPDU_NUM_MPDU_LEGACY - 1; else fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1; /* recompute the table */ brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid); /* update scb release size */ brcms_c_scb_ampdu_update_config_all(ampdu); } } fifo->accum_txfunfl = 0; return 0; } void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, u8 ba_wsize, /* negotiated ba window size (in pdu) */ uint max_rx_ampdu_bytes) /* from ht_cap in beacon */ { struct scb_ampdu *scb_ampdu; struct scb_ampdu_tid_ini *ini; struct ampdu_info *ampdu = wlc->ampdu; struct scb *scb = &wlc->pri_scb; scb_ampdu = &scb->scb_ampdu; if (!ampdu->ini_enable[tid]) { wiphy_err(ampdu->wlc->wiphy, "%s: Rejecting tid %d\n", __func__, tid); return; } ini = &scb_ampdu->ini[tid]; ini->tid = tid; ini->scb = scb_ampdu->scb; ini->ba_wsize = ba_wsize; scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes; } int brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, struct sk_buff **pdu, int prec) { struct brcms_c_info *wlc; struct sk_buff *p, *pkt[AMPDU_MAX_MPDU]; u8 tid, ndelim; int err = 0; u8 preamble_type = BRCMS_GF_PREAMBLE; u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; bool rr = true, fbr = false; uint i, count = 0, fifo, seg_cnt = 0; u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0; u32 ampdu_len, max_ampdu_bytes = 0; struct d11txh *txh = NULL; u8 *plcp; struct ieee80211_hdr *h; struct scb *scb; struct scb_ampdu *scb_ampdu; struct scb_ampdu_tid_ini *ini; u8 mcs = 0; bool use_rts = false, use_cts = false; u32 rspec = 0, rspec_fallback = 0; u32 rts_rspec = 0, rts_rspec_fallback = 0; u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; struct ieee80211_rts *rts; u8 rr_retry_limit; struct brcms_fifo_info *f; bool fbr_iscck; struct ieee80211_tx_info *tx_info; u16 qlen; struct wiphy *wiphy; wlc = ampdu->wlc; wiphy = wlc->wiphy; p = *pdu; tid = (u8) (p->priority); f = ampdu->fifo_tb + prio2fifo[tid]; scb = &wlc->pri_scb; scb_ampdu = &scb->scb_ampdu; ini = &scb_ampdu->ini[tid]; /* Let pressure continue to build ... */ qlen = pktq_plen(&qi->q, prec); if (ini->tx_in_transit > 0 && qlen < min(scb_ampdu->max_pdu, ini->ba_wsize)) /* Collect multiple MPDU's to be sent in the next AMPDU */ return -EBUSY; /* at this point we intend to transmit an AMPDU */ rr_retry_limit = ampdu->rr_retry_limit_tid[tid]; ampdu_len = 0; dma_len = 0; while (p) { struct ieee80211_tx_rate *txrate; tx_info = IEEE80211_SKB_CB(p); txrate = tx_info->status.rates; if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { err = brcms_c_prep_pdu(wlc, p, &fifo); } else { wiphy_err(wiphy, "%s: AMPDU flag is off!\n", __func__); *pdu = NULL; err = 0; break; } if (err) { if (err == -EBUSY) { wiphy_err(wiphy, "wl%d: sendampdu: " "prep_xdu retry; seq 0x%x\n", wlc->pub->unit, seq); *pdu = p; break; } /* error in the packet; reject it */ wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu " "rejected; seq 0x%x\n", wlc->pub->unit, seq); *pdu = NULL; break; } /* pkt is good to be aggregated */ txh = (struct d11txh *) p->data; plcp = (u8 *) (txh + 1); h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; index = TX_SEQ_TO_INDEX(seq); /* check mcl fields and test whether it can be agg'd */ mcl = le16_to_cpu(txh->MacTxControlLow); mcl &= ~TXC_AMPDU_MASK; fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x3); txh->PreloadSize = 0; /* always default to 0 */ /* Handle retry limits */ if (txrate[0].count <= rr_retry_limit) { txrate[0].count++; rr = true; fbr = false; } else { fbr = true; rr = false; txrate[1].count++; } /* extract the length info */ len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); /* retrieve null delimiter count */ ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; seg_cnt += 1; BCMMSG(wlc->wiphy, "wl%d: mpdu %d plcp_len %d\n", wlc->pub->unit, count, len); /* * aggregateable mpdu. For ucode/hw agg, * test whether need to break or change the epoch */ if (count == 0) { mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); /* refill the bits since might be a retx mpdu */ mcl |= TXC_STARTMSDU; rts = (struct ieee80211_rts *)&txh->rts_frame; if (ieee80211_is_rts(rts->frame_control)) { mcl |= TXC_SENDRTS; use_rts = true; } if (ieee80211_is_cts(rts->frame_control)) { mcl |= TXC_SENDCTS; use_cts = true; } } else { mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); } len = roundup(len, 4); ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN); dma_len += (u16) p->len; BCMMSG(wlc->wiphy, "wl%d: ampdu_len %d" " seg_cnt %d null delim %d\n", wlc->pub->unit, ampdu_len, seg_cnt, ndelim); txh->MacTxControlLow = cpu_to_le16(mcl); /* this packet is added */ pkt[count++] = p; /* patch the first MPDU */ if (count == 1) { u8 plcp0, plcp3, is40, sgi; if (rr) { plcp0 = plcp[0]; plcp3 = plcp[3]; } else { plcp0 = txh->FragPLCPFallback[0]; plcp3 = txh->FragPLCPFallback[3]; } is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; sgi = plcp3_issgi(plcp3) ? 1 : 0; mcs = plcp0 & ~MIMO_PLCP_40MHZ; max_ampdu_bytes = min(scb_ampdu->max_rx_ampdu_bytes, ampdu->max_txlen[mcs][is40][sgi]); if (is40) mimo_ctlchbw = CHSPEC_SB_UPPER(wlc_phy_chanspec_get( wlc->band->pi)) ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ; /* rebuild the rspec and rspec_fallback */ rspec = RSPEC_MIMORATE; rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; if (plcp[0] & MIMO_PLCP_40MHZ) rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); if (fbr_iscck) /* CCK */ rspec_fallback = cck_rspec(cck_phy2mac_rate (txh->FragPLCPFallback[0])); else { /* MIMO */ rspec_fallback = RSPEC_MIMORATE; rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) rspec_fallback |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); } if (use_rts || use_cts) { rts_rspec = brcms_c_rspec_to_rts_rspec(wlc, rspec, false, mimo_ctlchbw); rts_rspec_fallback = brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback, false, mimo_ctlchbw); } } /* if (first mpdu for host agg) */ /* test whether to add more */ if ((mcs_2_rate(mcs, true, false) >= f->dmaxferrate) && (count == f->mcs2ampdu_table[mcs])) { BCMMSG(wlc->wiphy, "wl%d: PR 37644: stopping" " ampdu at %d for mcs %d\n", wlc->pub->unit, count, mcs); break; } if (count == scb_ampdu->max_pdu) break; /* * check to see if the next pkt is * a candidate for aggregation */ p = pktq_ppeek(&qi->q, prec); if (p) { tx_info = IEEE80211_SKB_CB(p); if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && ((u8) (p->priority) == tid)) { plen = p->len + AMPDU_MAX_MPDU_OVERHEAD; plen = max(scb_ampdu->min_len, plen); if ((plen + ampdu_len) > max_ampdu_bytes) { p = NULL; continue; } /* * check if there are enough * descriptors available */ if (*wlc->core->txavail[fifo] <= seg_cnt + 1) { wiphy_err(wiphy, "%s: No fifo space " "!!\n", __func__); p = NULL; continue; } /* next packet fit for aggregation so dequeue */ p = brcmu_pktq_pdeq(&qi->q, prec); } else { p = NULL; } } } /* end while(p) */ ini->tx_in_transit += count; if (count) { /* patch up the last txh */ txh = (struct d11txh *) pkt[count - 1]->data; mcl = le16_to_cpu(txh->MacTxControlLow); mcl &= ~TXC_AMPDU_MASK; mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); txh->MacTxControlLow = cpu_to_le16(mcl); /* remove the null delimiter after last mpdu */ ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; /* remove the pad len from last mpdu */ fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); ampdu_len -= roundup(len, 4) - len; /* patch up the first txh & plcp */ txh = (struct d11txh *) pkt[0]->data; plcp = (u8 *) (txh + 1); BRCMS_SET_MIMO_PLCP_LEN(plcp, ampdu_len); /* mark plcp to indicate ampdu */ BRCMS_SET_MIMO_PLCP_AMPDU(plcp); /* reset the mixed mode header durations */ if (txh->MModeLen) { u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec, ampdu_len); txh->MModeLen = cpu_to_le16(mmodelen); preamble_type = BRCMS_MM_PREAMBLE; } if (txh->MModeFbrLen) { u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback, ampdu_len); txh->MModeFbrLen = cpu_to_le16(mmfbrlen); fbr_preamble_type = BRCMS_MM_PREAMBLE; } /* set the preload length */ if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { dma_len = min(dma_len, f->ampdu_pld_size); txh->PreloadSize = cpu_to_le16(dma_len); } else txh->PreloadSize = 0; mch = le16_to_cpu(txh->MacTxControlHigh); /* update RTS dur fields */ if (use_rts || use_cts) { u16 durid; rts = (struct ieee80211_rts *)&txh->rts_frame; if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == TXC_PREAMBLE_RTS_MAIN_SHORT) rts_preamble_type = BRCMS_SHORT_PREAMBLE; if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == TXC_PREAMBLE_RTS_FB_SHORT) rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, rspec, rts_preamble_type, preamble_type, ampdu_len, true); rts->duration = cpu_to_le16(durid); durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec_fallback, rspec_fallback, rts_fbr_preamble_type, fbr_preamble_type, ampdu_len, true); txh->RTSDurFallback = cpu_to_le16(durid); /* set TxFesTimeNormal */ txh->TxFesTimeNormal = rts->duration; /* set fallback rate version of TxFesTimeNormal */ txh->TxFesTimeFallback = txh->RTSDurFallback; } /* set flag and plcp for fallback rate */ if (fbr) { mch |= TXC_AMPDU_FBR; txh->MacTxControlHigh = cpu_to_le16(mch); BRCMS_SET_MIMO_PLCP_AMPDU(plcp); BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); } BCMMSG(wlc->wiphy, "wl%d: count %d ampdu_len %d\n", wlc->pub->unit, count, ampdu_len); /* inform rate_sel if it this is a rate probe pkt */ frameid = le16_to_cpu(txh->TxFrameID); if (frameid & TXFID_RATE_PROBE_MASK) wiphy_err(wiphy, "%s: XXX what to do with " "TXFID_RATE_PROBE_MASK!?\n", __func__); for (i = 0; i < count; i++) brcms_c_txfifo(wlc, fifo, pkt[i], i == (count - 1), ampdu->txpkt_weight); } /* endif (count) */ return err; } static void brcms_c_ampdu_rate_status(struct brcms_c_info *wlc, struct ieee80211_tx_info *tx_info, struct tx_status *txs, u8 mcs) { struct ieee80211_tx_rate *txrate = tx_info->status.rates; int i; /* clear the rest of the rates */ for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { txrate[i].idx = -1; txrate[i].count = 0; } } static void brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p, struct tx_status *txs, u32 s1, u32 s2) { struct scb_ampdu *scb_ampdu; struct brcms_c_info *wlc = ampdu->wlc; struct scb_ampdu_tid_ini *ini; u8 bitmap[8], queue, tid; struct d11txh *txh; u8 *plcp; struct ieee80211_hdr *h; u16 seq, start_seq = 0, bindex, index, mcl; u8 mcs = 0; bool ba_recd = false, ack_recd = false; u8 suc_mpdu = 0, tot_mpdu = 0; uint supr_status; bool update_rate = true, retry = true, tx_error = false; u16 mimoantsel = 0; u8 antselid = 0; u8 retry_limit, rr_retry_limit; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); struct wiphy *wiphy = wlc->wiphy; #ifdef DEBUG u8 hole[AMPDU_MAX_MPDU]; memset(hole, 0, sizeof(hole)); #endif scb_ampdu = &scb->scb_ampdu; tid = (u8) (p->priority); ini = &scb_ampdu->ini[tid]; retry_limit = ampdu->retry_limit_tid[tid]; rr_retry_limit = ampdu->rr_retry_limit_tid[tid]; memset(bitmap, 0, sizeof(bitmap)); queue = txs->frameid & TXFID_QUEUE_MASK; supr_status = txs->status & TX_STATUS_SUPR_MASK; if (txs->status & TX_STATUS_ACK_RCV) { if (TX_STATUS_SUPR_UF == supr_status) update_rate = false; WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE)); start_seq = txs->sequence >> SEQNUM_SHIFT; bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >> TX_STATUS_BA_BMAP03_SHIFT; WARN_ON(s1 & TX_STATUS_INTERMEDIATE); WARN_ON(!(s1 & TX_STATUS_AMPDU)); bitmap[0] |= (s1 & TX_STATUS_BA_BMAP47_MASK) << TX_STATUS_BA_BMAP47_SHIFT; bitmap[1] = (s1 >> 8) & 0xff; bitmap[2] = (s1 >> 16) & 0xff; bitmap[3] = (s1 >> 24) & 0xff; bitmap[4] = s2 & 0xff; bitmap[5] = (s2 >> 8) & 0xff; bitmap[6] = (s2 >> 16) & 0xff; bitmap[7] = (s2 >> 24) & 0xff; ba_recd = true; } else { if (supr_status) { update_rate = false; if (supr_status == TX_STATUS_SUPR_BADCH) { wiphy_err(wiphy, "%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL( wlc->default_bss->chanspec)); } else { if (supr_status != TX_STATUS_SUPR_FRAG) wiphy_err(wiphy, "%s: supr_status 0x%x\n", __func__, supr_status); } /* no need to retry for badch; will fail again */ if (supr_status == TX_STATUS_SUPR_BADCH || supr_status == TX_STATUS_SUPR_EXPTIME) { retry = false; } else if (supr_status == TX_STATUS_SUPR_EXPTIME) { /* TX underflow: * try tuning pre-loading or ampdu size */ } else if (supr_status == TX_STATUS_SUPR_FRAG) { /* * if there were underflows, but pre-loading * is not active, notify rate adaptation. */ if (brcms_c_ffpld_check_txfunfl(wlc, prio2fifo[tid]) > 0) tx_error = true; } } else if (txs->phyerr) { update_rate = false; wiphy_err(wiphy, "%s: ampdu tx phy error (0x%x)\n", __func__, txs->phyerr); if (brcm_msg_level & LOG_ERROR_VAL) { brcmu_prpkt("txpkt (AMPDU)", p); brcms_c_print_txdesc((struct d11txh *) p->data); } brcms_c_print_txstatus(txs); } } /* loop through all pkts and retry if not acked */ while (p) { tx_info = IEEE80211_SKB_CB(p); txh = (struct d11txh *) p->data; mcl = le16_to_cpu(txh->MacTxControlLow); plcp = (u8 *) (txh + 1); h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; if (tot_mpdu == 0) { mcs = plcp[0] & MIMO_PLCP_MCS_MASK; mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel); } index = TX_SEQ_TO_INDEX(seq); ack_recd = false; if (ba_recd) { bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX); BCMMSG(wiphy, "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n", tid, seq, start_seq, bindex, isset(bitmap, bindex), index); /* if acked then clear bit and free packet */ if ((bindex < AMPDU_TX_BA_MAX_WSIZE) && isset(bitmap, bindex)) { ini->tx_in_transit--; ini->txretry[index] = 0; /* * ampdu_ack_len: * number of acked aggregated frames */ /* ampdu_len: number of aggregated frames */ brcms_c_ampdu_rate_status(wlc, tx_info, txs, mcs); tx_info->flags |= IEEE80211_TX_STAT_ACK; tx_info->flags |= IEEE80211_TX_STAT_AMPDU; tx_info->status.ampdu_ack_len = tx_info->status.ampdu_len = 1; skb_pull(p, D11_PHY_HDR_LEN); skb_pull(p, D11_TXH_LEN); ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); ack_recd = true; suc_mpdu++; } } /* either retransmit or send bar if ack not recd */ if (!ack_recd) { if (retry && (ini->txretry[index] < (int)retry_limit)) { ini->txretry[index]++; ini->tx_in_transit--; /* * Use high prededence for retransmit to * give some punch */ brcms_c_txq_enq(wlc, scb, p, BRCMS_PRIO_TO_HI_PREC(tid)); } else { /* Retry timeout */ ini->tx_in_transit--; ieee80211_tx_info_clear_status(tx_info); tx_info->status.ampdu_ack_len = 0; tx_info->status.ampdu_len = 1; tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; skb_pull(p, D11_PHY_HDR_LEN); skb_pull(p, D11_TXH_LEN); BCMMSG(wiphy, "BA Timeout, seq %d, in_transit %d\n", seq, ini->tx_in_transit); ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); } } tot_mpdu++; /* break out if last packet of ampdu */ if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == TXC_AMPDU_LAST) break; p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); } brcms_c_send_q(wlc); /* update rate state */ antselid = brcms_c_antsel_antsel2id(wlc->asi, mimoantsel); brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight); } void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p, struct tx_status *txs) { struct scb_ampdu *scb_ampdu; struct brcms_c_info *wlc = ampdu->wlc; struct scb_ampdu_tid_ini *ini; u32 s1 = 0, s2 = 0; struct ieee80211_tx_info *tx_info; tx_info = IEEE80211_SKB_CB(p); /* BMAC_NOTE: For the split driver, second level txstatus comes later * So if the ACK was received then wait for the second level else just * call the first one */ if (txs->status & TX_STATUS_ACK_RCV) { u8 status_delay = 0; /* wait till the next 8 bytes of txstatus is available */ s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus)); while ((s1 & TXS_V) == 0) { udelay(1); status_delay++; if (status_delay > 10) return; /* error condition */ s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus)); } s2 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus2)); } if (scb) { scb_ampdu = &scb->scb_ampdu; ini = &scb_ampdu->ini[p->priority]; brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2); } else { /* loop through all pkts and free */ u8 queue = txs->frameid & TXFID_QUEUE_MASK; struct d11txh *txh; u16 mcl; while (p) { tx_info = IEEE80211_SKB_CB(p); txh = (struct d11txh *) p->data; mcl = le16_to_cpu(txh->MacTxControlLow); brcmu_pkt_buf_free_skb(p); /* break out if last packet of ampdu */ if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == TXC_AMPDU_LAST) break; p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); } brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight); } } void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc) { char template[T_RAM_ACCESS_SZ * 2]; /* driver needs to write the ta in the template; ta is at offset 16 */ memset(template, 0, sizeof(template)); memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN); brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2), template); } bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid) { return wlc->ampdu->ini_enable[tid]; } void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu) { struct brcms_c_info *wlc = ampdu->wlc; /* * Extend ucode internal watchdog timer to * match larger received frames */ if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) == IEEE80211_HT_MAX_AMPDU_64K) { brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX); brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX); } else { brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF); brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF); } } /* * callback function that helps flushing ampdu packets from a priority queue */ static bool cb_del_ampdu_pkt(struct sk_buff *mpdu, void *arg_a) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(mpdu); struct cb_del_ampdu_pars *ampdu_pars = (struct cb_del_ampdu_pars *)arg_a; bool rc; rc = tx_info->flags & IEEE80211_TX_CTL_AMPDU ? true : false; rc = rc && (tx_info->rate_driver_data[0] == NULL || ampdu_pars->sta == NULL || tx_info->rate_driver_data[0] == ampdu_pars->sta); rc = rc && ((u8)(mpdu->priority) == ampdu_pars->tid); return rc; } /* * callback function that helps invalidating ampdu packets in a DMA queue */ static void dma_cb_fn_ampdu(void *txi, void *arg_a) { struct ieee80211_sta *sta = arg_a; struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi; if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && (tx_info->rate_driver_data[0] == sta || sta == NULL)) tx_info->rate_driver_data[0] = NULL; } /* * When a remote party is no longer available for ampdu communication, any * pending tx ampdu packets in the driver have to be flushed. */ void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, u16 tid) { struct brcms_txq_info *qi = wlc->pkt_queue; struct pktq *pq = &qi->q; int prec; struct cb_del_ampdu_pars ampdu_pars; ampdu_pars.sta = sta; ampdu_pars.tid = tid; for (prec = 0; prec < pq->num_prec; prec++) brcmu_pktq_pflush(pq, prec, true, cb_del_ampdu_pkt, (void *)&du_pars); brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h0000644000175000017500000001544712026211315025425 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_AIUTILS_H_ #define _BRCM_AIUTILS_H_ #include #include "types.h" /* * SOC Interconnect Address Map. * All regions may not exist on all chips. */ /* each core gets 4Kbytes for registers */ #define SI_CORE_SIZE 0x1000 /* * Max cores (this is arbitrary, for software * convenience and could be changed if we * make any larger chips */ #define SI_MAXCORES 16 /* Client Mode sb2pcitranslation2 size in bytes */ #define SI_PCI_DMA_SZ 0x40000000 /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */ #define SI_PCIE_DMA_H32 0x80000000 /* chipcommon being the first core: */ #define SI_CC_IDX 0 /* SOC Interconnect types (aka chip types) */ #define SOCI_AI 1 /* A register that is common to all cores to * communicate w/PMU regarding clock control. */ #define SI_CLK_CTL_ST 0x1e0 /* clock control and status */ /* clk_ctl_st register */ #define CCS_FORCEALP 0x00000001 /* force ALP request */ #define CCS_FORCEHT 0x00000002 /* force HT request */ #define CCS_FORCEILP 0x00000004 /* force ILP request */ #define CCS_ALPAREQ 0x00000008 /* ALP Avail Request */ #define CCS_HTAREQ 0x00000010 /* HT Avail Request */ #define CCS_FORCEHWREQOFF 0x00000020 /* Force HW Clock Request Off */ #define CCS_ERSRC_REQ_MASK 0x00000700 /* external resource requests */ #define CCS_ERSRC_REQ_SHIFT 8 #define CCS_ALPAVAIL 0x00010000 /* ALP is available */ #define CCS_HTAVAIL 0x00020000 /* HT is available */ #define CCS_BP_ON_APL 0x00040000 /* RO: running on ALP clock */ #define CCS_BP_ON_HT 0x00080000 /* RO: running on HT clock */ #define CCS_ERSRC_STS_MASK 0x07000000 /* external resource status */ #define CCS_ERSRC_STS_SHIFT 24 /* HT avail in chipc and pcmcia on 4328a0 */ #define CCS0_HTAVAIL 0x00010000 /* ALP avail in chipc and pcmcia on 4328a0 */ #define CCS0_ALPAVAIL 0x00020000 /* Not really related to SOC Interconnect, but a couple of software * conventions for the use the flash space: */ /* Minumum amount of flash we support */ #define FLASH_MIN 0x00020000 /* Minimum flash size */ #define CC_SROM_OTP 0x800 /* SROM/OTP address space */ /* gpiotimerval */ #define GPIO_ONTIME_SHIFT 16 /* Fields in clkdiv */ #define CLKD_OTP 0x000f0000 #define CLKD_OTP_SHIFT 16 /* dynamic clock control defines */ #define LPOMINFREQ 25000 /* low power oscillator min */ #define LPOMAXFREQ 43000 /* low power oscillator max */ #define XTALMINFREQ 19800000 /* 20 MHz - 1% */ #define XTALMAXFREQ 20200000 /* 20 MHz + 1% */ #define PCIMINFREQ 25000000 /* 25 MHz */ #define PCIMAXFREQ 34000000 /* 33 MHz + fudge */ #define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ #define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ /* clkctl xtal what flags */ #define XTAL 0x1 /* primary crystal oscillator (2050) */ #define PLL 0x2 /* main chip pll */ /* GPIO usage priorities */ #define GPIO_DRV_PRIORITY 0 /* Driver */ #define GPIO_APP_PRIORITY 1 /* Application */ #define GPIO_HI_PRIORITY 2 /* Highest priority. Ignore GPIO * reservation */ /* GPIO pull up/down */ #define GPIO_PULLUP 0 #define GPIO_PULLDN 1 /* GPIO event regtype */ #define GPIO_REGEVT 0 /* GPIO register event */ #define GPIO_REGEVT_INTMSK 1 /* GPIO register event int mask */ #define GPIO_REGEVT_INTPOL 2 /* GPIO register event int polarity */ /* device path */ #define SI_DEVPATH_BUFSZ 16 /* min buffer size in bytes */ /* SI routine enumeration: to be used by update function with multiple hooks */ #define SI_DOATTACH 1 #define SI_PCIDOWN 2 #define SI_PCIUP 3 /* * Data structure to export all chip specific common variables * public (read-only) portion of aiutils handle returned by si_attach() */ struct si_pub { int ccrev; /* chip common core rev */ u32 cccaps; /* chip common capabilities */ int pmurev; /* pmu core rev */ u32 pmucaps; /* pmu capabilities */ uint boardtype; /* board type */ uint boardvendor; /* board vendor */ uint chip; /* chip number */ uint chiprev; /* chip revision */ uint chippkg; /* chip package option */ }; struct pci_dev; struct gpioh_item { void *arg; bool level; void (*handler) (u32 stat, void *arg); u32 event; struct gpioh_item *next; }; /* misc si info needed by some of the routines */ struct si_info { struct si_pub pub; /* back plane public state (must be first) */ struct bcma_bus *icbus; /* handle to soc interconnect bus */ struct pci_dev *pcibus; /* handle to pci bus */ u32 chipst; /* chip status */ }; /* * Many of the routines below take an 'sih' handle as their first arg. * Allocate this by calling si_attach(). Free it by calling si_detach(). * At any one time, the sih is logically focused on one particular si core * (the "current core"). * Use si_setcore() or si_setcoreidx() to change the association to another core */ /* AMBA Interconnect exported externs */ extern u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val); /* === exported functions === */ extern struct si_pub *ai_attach(struct bcma_bus *pbus); extern void ai_detach(struct si_pub *sih); extern uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); extern void ai_clkctl_init(struct si_pub *sih); extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); extern bool ai_deviceremoved(struct si_pub *sih); extern void ai_pci_down(struct si_pub *sih); extern void ai_pci_up(struct si_pub *sih); /* Enable Ex-PA for 4313 */ extern void ai_epa_4313war(struct si_pub *sih); static inline u32 ai_get_cccaps(struct si_pub *sih) { return sih->cccaps; } static inline int ai_get_pmurev(struct si_pub *sih) { return sih->pmurev; } static inline u32 ai_get_pmucaps(struct si_pub *sih) { return sih->pmucaps; } static inline uint ai_get_boardtype(struct si_pub *sih) { return sih->boardtype; } static inline uint ai_get_boardvendor(struct si_pub *sih) { return sih->boardvendor; } static inline uint ai_get_chip_id(struct si_pub *sih) { return sih->chip; } static inline uint ai_get_chiprev(struct si_pub *sih) { return sih->chiprev; } static inline uint ai_get_chippkg(struct si_pub *sih) { return sih->chippkg; } #endif /* _BRCM_AIUTILS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/0000755000175000017500000000000012026211315024367 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c0000644000175000017500000345034012026211315025661 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include "phy_int.h" #include "phy_hal.h" #include "phy_radio.h" #include "phyreg_n.h" #include "phytbl_n.h" #include "soc.h" #define READ_RADIO_REG2(pi, radio_type, jspace, core, reg_name) \ read_radio_reg(pi, radio_type##_##jspace##_##reg_name | \ ((core == PHY_CORE_0) ? \ radio_type##_##jspace##0 : \ radio_type##_##jspace##1)) #define WRITE_RADIO_REG2(pi, radio_type, jspace, core, reg_name, value) \ write_radio_reg(pi, radio_type##_##jspace##_##reg_name | \ ((core == PHY_CORE_0) ? \ radio_type##_##jspace##0 : \ radio_type##_##jspace##1), value) #define WRITE_RADIO_SYN(pi, radio_type, reg_name, value) \ write_radio_reg(pi, radio_type##_##SYN##_##reg_name, value) #define READ_RADIO_REG3(pi, radio_type, jspace, core, reg_name) \ read_radio_reg(pi, ((core == PHY_CORE_0) ? \ radio_type##_##jspace##0##_##reg_name : \ radio_type##_##jspace##1##_##reg_name)) #define WRITE_RADIO_REG3(pi, radio_type, jspace, core, reg_name, value) \ write_radio_reg(pi, ((core == PHY_CORE_0) ? \ radio_type##_##jspace##0##_##reg_name : \ radio_type##_##jspace##1##_##reg_name), \ value) #define READ_RADIO_REG4(pi, radio_type, jspace, core, reg_name) \ read_radio_reg(pi, ((core == PHY_CORE_0) ? \ radio_type##_##reg_name##_##jspace##0 : \ radio_type##_##reg_name##_##jspace##1)) #define WRITE_RADIO_REG4(pi, radio_type, jspace, core, reg_name, value) \ write_radio_reg(pi, ((core == PHY_CORE_0) ? \ radio_type##_##reg_name##_##jspace##0 : \ radio_type##_##reg_name##_##jspace##1), \ value) #define NPHY_ACI_MAX_UNDETECT_WINDOW_SZ 40 #define NPHY_ACI_CHANNEL_DELTA 5 #define NPHY_ACI_CHANNEL_SKIP 4 #define NPHY_ACI_40MHZ_CHANNEL_DELTA 6 #define NPHY_ACI_40MHZ_CHANNEL_SKIP 5 #define NPHY_ACI_40MHZ_CHANNEL_DELTA_GE_REV3 6 #define NPHY_ACI_40MHZ_CHANNEL_SKIP_GE_REV3 5 #define NPHY_ACI_CHANNEL_DELTA_GE_REV3 4 #define NPHY_ACI_CHANNEL_SKIP_GE_REV3 3 #define NPHY_NOISE_NOASSOC_GLITCH_TH_UP 2 #define NPHY_NOISE_NOASSOC_GLITCH_TH_DN 8 #define NPHY_NOISE_ASSOC_GLITCH_TH_UP 2 #define NPHY_NOISE_ASSOC_GLITCH_TH_DN 8 #define NPHY_NOISE_ASSOC_ACI_GLITCH_TH_UP 2 #define NPHY_NOISE_ASSOC_ACI_GLITCH_TH_DN 8 #define NPHY_NOISE_NOASSOC_ENTER_TH 400 #define NPHY_NOISE_ASSOC_ENTER_TH 400 #define NPHY_NOISE_ASSOC_RX_GLITCH_BADPLCP_ENTER_TH 400 #define NPHY_NOISE_CRSMINPWR_ARRAY_MAX_INDEX 44 #define NPHY_NOISE_CRSMINPWR_ARRAY_MAX_INDEX_REV_7 56 #define NPHY_NOISE_NOASSOC_CRSIDX_INCR 16 #define NPHY_NOISE_ASSOC_CRSIDX_INCR 8 #define NPHY_IS_SROM_REINTERPRET NREV_GE(pi->pubpi.phy_rev, 5) #define NPHY_RSSICAL_MAXREAD 31 #define NPHY_RSSICAL_NPOLL 8 #define NPHY_RSSICAL_MAXD (1<<20) #define NPHY_MIN_RXIQ_PWR 2 #define NPHY_RSSICAL_W1_TARGET 25 #define NPHY_RSSICAL_W2_TARGET NPHY_RSSICAL_W1_TARGET #define NPHY_RSSICAL_NB_TARGET 0 #define NPHY_RSSICAL_W1_TARGET_REV3 29 #define NPHY_RSSICAL_W2_TARGET_REV3 NPHY_RSSICAL_W1_TARGET_REV3 #define NPHY_CALSANITY_RSSI_NB_MAX_POS 9 #define NPHY_CALSANITY_RSSI_NB_MAX_NEG -9 #define NPHY_CALSANITY_RSSI_W1_MAX_POS 12 #define NPHY_CALSANITY_RSSI_W1_MAX_NEG (NPHY_RSSICAL_W1_TARGET - \ NPHY_RSSICAL_MAXREAD) #define NPHY_CALSANITY_RSSI_W2_MAX_POS NPHY_CALSANITY_RSSI_W1_MAX_POS #define NPHY_CALSANITY_RSSI_W2_MAX_NEG (NPHY_RSSICAL_W2_TARGET - \ NPHY_RSSICAL_MAXREAD) #define NPHY_RSSI_SXT(x) ((s8) (-((x) & 0x20) + ((x) & 0x1f))) #define NPHY_RSSI_NB_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_NB_MAX_POS) || \ ((x) < NPHY_CALSANITY_RSSI_NB_MAX_NEG)) #define NPHY_RSSI_W1_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_W1_MAX_POS) || \ ((x) < NPHY_CALSANITY_RSSI_W1_MAX_NEG)) #define NPHY_RSSI_W2_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_W2_MAX_POS) || \ ((x) < NPHY_CALSANITY_RSSI_W2_MAX_NEG)) #define NPHY_IQCAL_NUMGAINS 9 #define NPHY_N_GCTL 0x66 #define NPHY_PAPD_EPS_TBL_SIZE 64 #define NPHY_PAPD_SCL_TBL_SIZE 64 #define NPHY_NUM_DIG_FILT_COEFFS 15 #define NPHY_PAPD_COMP_OFF 0 #define NPHY_PAPD_COMP_ON 1 #define NPHY_SROM_TEMPSHIFT 32 #define NPHY_SROM_MAXTEMPOFFSET 16 #define NPHY_SROM_MINTEMPOFFSET -16 #define NPHY_CAL_MAXTEMPDELTA 64 #define NPHY_NOISEVAR_TBLLEN40 256 #define NPHY_NOISEVAR_TBLLEN20 128 #define NPHY_ANARXLPFBW_REDUCTIONFACT 7 #define NPHY_ADJUSTED_MINCRSPOWER 0x1e /* 5357 Chip specific ChipControl register bits */ #define CCTRL5357_EXTPA (1<<14) /* extPA in ChipControl 1, bit 14 */ #define CCTRL5357_ANT_MUX_2o3 (1<<15) /* 2o3 in ChipControl 1, bit 15 */ #define NPHY_CAL_TSSISAMPS 64 #define NPHY_TEST_TONE_FREQ_40MHz 4000 #define NPHY_TEST_TONE_FREQ_20MHz 2500 #define MAX_205x_RCAL_WAITLOOPS 10000 #define NPHY_RXCAL_TONEAMP 181 #define NPHY_RXCAL_TONEFREQ_40MHz 4000 #define NPHY_RXCAL_TONEFREQ_20MHz 2000 #define TXFILT_SHAPING_OFDM20 0 #define TXFILT_SHAPING_OFDM40 1 #define TXFILT_SHAPING_CCK 2 #define TXFILT_DEFAULT_OFDM20 3 #define TXFILT_DEFAULT_OFDM40 4 struct nphy_iqcal_params { u16 txlpf; u16 txgm; u16 pga; u16 pad; u16 ipa; u16 cal_gain; u16 ncorr[5]; }; struct nphy_txiqcal_ladder { u8 percent; u8 g_env; }; struct nphy_ipa_txcalgains { struct nphy_txgains gains; bool useindex; u8 index; }; struct nphy_papd_restore_state { u16 fbmix[2]; u16 vga_master[2]; u16 intpa_master[2]; u16 afectrl[2]; u16 afeoverride[2]; u16 pwrup[2]; u16 atten[2]; u16 mm; }; struct nphy_ipa_txrxgain { u16 hpvga; u16 lpf_biq1; u16 lpf_biq0; u16 lna2; u16 lna1; s8 txpwrindex; }; #define NPHY_IPA_RXCAL_MAXGAININDEX (6 - 1) static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz[] = { {0, 0, 0, 0, 0, 100}, {0, 0, 0, 0, 0, 50}, {0, 0, 0, 0, 0, -1}, {0, 0, 0, 3, 0, -1}, {0, 0, 3, 3, 0, -1}, {0, 2, 3, 3, 0, -1} }; static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz[] = { {0, 0, 0, 0, 0, 128}, {0, 0, 0, 0, 0, 70}, {0, 0, 0, 0, 0, 20}, {0, 0, 0, 3, 0, 20}, {0, 0, 3, 3, 0, 20}, {0, 2, 3, 3, 0, 20} }; static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz_rev7[] = { {0, 0, 0, 0, 0, 100}, {0, 0, 0, 0, 0, 50}, {0, 0, 0, 0, 0, -1}, {0, 0, 0, 3, 0, -1}, {0, 0, 3, 3, 0, -1}, {0, 0, 5, 3, 0, -1} }; static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz_rev7[] = { {0, 0, 0, 0, 0, 10}, {0, 0, 0, 1, 0, 10}, {0, 0, 1, 2, 0, 10}, {0, 0, 1, 3, 0, 10}, {0, 0, 4, 3, 0, 10}, {0, 0, 6, 3, 0, 10} }; enum { NPHY_RXCAL_GAIN_INIT = 0, NPHY_RXCAL_GAIN_UP, NPHY_RXCAL_GAIN_DOWN }; #define wlc_phy_get_papd_nphy(pi) \ (read_phy_reg((pi), 0x1e7) & \ ((0x1 << 15) | \ (0x1 << 14) | \ (0x1 << 13))) static const u16 NPHY_IPA_REV4_txdigi_filtcoeffs[][NPHY_NUM_DIG_FILT_COEFFS] = { {-377, 137, -407, 208, -1527, 956, 93, 186, 93, 230, -44, 230, 201, -191, 201}, {-77, 20, -98, 49, -93, 60, 56, 111, 56, 26, -5, 26, 34, -32, 34}, {-360, 164, -376, 164, -1533, 576, 308, -314, 308, 121, -73, 121, 91, 124, 91}, {-295, 200, -363, 142, -1391, 826, 151, 301, 151, 151, 301, 151, 602, -752, 602}, {-92, 58, -96, 49, -104, 44, 17, 35, 17, 12, 25, 12, 13, 27, 13}, {-375, 136, -399, 209, -1479, 949, 130, 260, 130, 230, -44, 230, 201, -191, 201}, {0xed9, 0xc8, 0xe95, 0x8e, 0xa91, 0x33a, 0x97, 0x12d, 0x97, 0x97, 0x12d, 0x97, 0x25a, 0xd10, 0x25a} }; struct chan_info_nphy_2055 { u16 chan; u16 freq; uint unknown; u8 RF_pll_ref; u8 RF_rf_pll_mod1; u8 RF_rf_pll_mod0; u8 RF_vco_cap_tail; u8 RF_vco_cal1; u8 RF_vco_cal2; u8 RF_pll_lf_c1; u8 RF_pll_lf_r1; u8 RF_pll_lf_c2; u8 RF_lgbuf_cen_buf; u8 RF_lgen_tune1; u8 RF_lgen_tune2; u8 RF_core1_lgbuf_a_tune; u8 RF_core1_lgbuf_g_tune; u8 RF_core1_rxrf_reg1; u8 RF_core1_tx_pga_pad_tn; u8 RF_core1_tx_mx_bgtrim; u8 RF_core2_lgbuf_a_tune; u8 RF_core2_lgbuf_g_tune; u8 RF_core2_rxrf_reg1; u8 RF_core2_tx_pga_pad_tn; u8 RF_core2_tx_mx_bgtrim; u16 PHY_BW1a; u16 PHY_BW2; u16 PHY_BW3; u16 PHY_BW4; u16 PHY_BW5; u16 PHY_BW6; }; struct chan_info_nphy_radio205x { u16 chan; u16 freq; u8 RF_SYN_pll_vcocal1; u8 RF_SYN_pll_vcocal2; u8 RF_SYN_pll_refdiv; u8 RF_SYN_pll_mmd2; u8 RF_SYN_pll_mmd1; u8 RF_SYN_pll_loopfilter1; u8 RF_SYN_pll_loopfilter2; u8 RF_SYN_pll_loopfilter3; u8 RF_SYN_pll_loopfilter4; u8 RF_SYN_pll_loopfilter5; u8 RF_SYN_reserved_addr27; u8 RF_SYN_reserved_addr28; u8 RF_SYN_reserved_addr29; u8 RF_SYN_logen_VCOBUF1; u8 RF_SYN_logen_MIXER2; u8 RF_SYN_logen_BUF3; u8 RF_SYN_logen_BUF4; u8 RF_RX0_lnaa_tune; u8 RF_RX0_lnag_tune; u8 RF_TX0_intpaa_boost_tune; u8 RF_TX0_intpag_boost_tune; u8 RF_TX0_pada_boost_tune; u8 RF_TX0_padg_boost_tune; u8 RF_TX0_pgaa_boost_tune; u8 RF_TX0_pgag_boost_tune; u8 RF_TX0_mixa_boost_tune; u8 RF_TX0_mixg_boost_tune; u8 RF_RX1_lnaa_tune; u8 RF_RX1_lnag_tune; u8 RF_TX1_intpaa_boost_tune; u8 RF_TX1_intpag_boost_tune; u8 RF_TX1_pada_boost_tune; u8 RF_TX1_padg_boost_tune; u8 RF_TX1_pgaa_boost_tune; u8 RF_TX1_pgag_boost_tune; u8 RF_TX1_mixa_boost_tune; u8 RF_TX1_mixg_boost_tune; u16 PHY_BW1a; u16 PHY_BW2; u16 PHY_BW3; u16 PHY_BW4; u16 PHY_BW5; u16 PHY_BW6; }; struct chan_info_nphy_radio2057 { u16 chan; u16 freq; u8 RF_vcocal_countval0; u8 RF_vcocal_countval1; u8 RF_rfpll_refmaster_sparextalsize; u8 RF_rfpll_loopfilter_r1; u8 RF_rfpll_loopfilter_c2; u8 RF_rfpll_loopfilter_c1; u8 RF_cp_kpd_idac; u8 RF_rfpll_mmd0; u8 RF_rfpll_mmd1; u8 RF_vcobuf_tune; u8 RF_logen_mx2g_tune; u8 RF_logen_mx5g_tune; u8 RF_logen_indbuf2g_tune; u8 RF_logen_indbuf5g_tune; u8 RF_txmix2g_tune_boost_pu_core0; u8 RF_pad2g_tune_pus_core0; u8 RF_pga_boost_tune_core0; u8 RF_txmix5g_boost_tune_core0; u8 RF_pad5g_tune_misc_pus_core0; u8 RF_lna2g_tune_core0; u8 RF_lna5g_tune_core0; u8 RF_txmix2g_tune_boost_pu_core1; u8 RF_pad2g_tune_pus_core1; u8 RF_pga_boost_tune_core1; u8 RF_txmix5g_boost_tune_core1; u8 RF_pad5g_tune_misc_pus_core1; u8 RF_lna2g_tune_core1; u8 RF_lna5g_tune_core1; u16 PHY_BW1a; u16 PHY_BW2; u16 PHY_BW3; u16 PHY_BW4; u16 PHY_BW5; u16 PHY_BW6; }; struct chan_info_nphy_radio2057_rev5 { u16 chan; u16 freq; u8 RF_vcocal_countval0; u8 RF_vcocal_countval1; u8 RF_rfpll_refmaster_sparextalsize; u8 RF_rfpll_loopfilter_r1; u8 RF_rfpll_loopfilter_c2; u8 RF_rfpll_loopfilter_c1; u8 RF_cp_kpd_idac; u8 RF_rfpll_mmd0; u8 RF_rfpll_mmd1; u8 RF_vcobuf_tune; u8 RF_logen_mx2g_tune; u8 RF_logen_indbuf2g_tune; u8 RF_txmix2g_tune_boost_pu_core0; u8 RF_pad2g_tune_pus_core0; u8 RF_lna2g_tune_core0; u8 RF_txmix2g_tune_boost_pu_core1; u8 RF_pad2g_tune_pus_core1; u8 RF_lna2g_tune_core1; u16 PHY_BW1a; u16 PHY_BW2; u16 PHY_BW3; u16 PHY_BW4; u16 PHY_BW5; u16 PHY_BW6; }; struct nphy_sfo_cfg { u16 PHY_BW1a; u16 PHY_BW2; u16 PHY_BW3; u16 PHY_BW4; u16 PHY_BW5; u16 PHY_BW6; }; static const struct chan_info_nphy_2055 chan_info_nphy_2055[] = { { 184, 4920, 3280, 0x71, 0x01, 0xEC, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7B4, 0x7B0, 0x7AC, 0x214, 0x215, 0x216}, { 186, 4930, 3287, 0x71, 0x01, 0xED, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7B8, 0x7B4, 0x7B0, 0x213, 0x214, 0x215}, { 188, 4940, 3293, 0x71, 0x01, 0xEE, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7BC, 0x7B8, 0x7B4, 0x212, 0x213, 0x214}, { 190, 4950, 3300, 0x71, 0x01, 0xEF, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7C0, 0x7BC, 0x7B8, 0x211, 0x212, 0x213}, { 192, 4960, 3307, 0x71, 0x01, 0xF0, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7C4, 0x7C0, 0x7BC, 0x20F, 0x211, 0x212}, { 194, 4970, 3313, 0x71, 0x01, 0xF1, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7C8, 0x7C4, 0x7C0, 0x20E, 0x20F, 0x211}, { 196, 4980, 3320, 0x71, 0x01, 0xF2, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7CC, 0x7C8, 0x7C4, 0x20D, 0x20E, 0x20F}, { 198, 4990, 3327, 0x71, 0x01, 0xF3, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7D0, 0x7CC, 0x7C8, 0x20C, 0x20D, 0x20E}, { 200, 5000, 3333, 0x71, 0x01, 0xF4, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7D4, 0x7D0, 0x7CC, 0x20B, 0x20C, 0x20D}, { 202, 5010, 3340, 0x71, 0x01, 0xF5, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7D8, 0x7D4, 0x7D0, 0x20A, 0x20B, 0x20C}, { 204, 5020, 3347, 0x71, 0x01, 0xF6, 0x0E, 0xF7, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7DC, 0x7D8, 0x7D4, 0x209, 0x20A, 0x20B}, { 206, 5030, 3353, 0x71, 0x01, 0xF7, 0x0E, 0xF7, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7E0, 0x7DC, 0x7D8, 0x208, 0x209, 0x20A}, { 208, 5040, 3360, 0x71, 0x01, 0xF8, 0x0D, 0xEF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7E4, 0x7E0, 0x7DC, 0x207, 0x208, 0x209}, { 210, 5050, 3367, 0x71, 0x01, 0xF9, 0x0D, 0xEF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0x7E8, 0x7E4, 0x7E0, 0x206, 0x207, 0x208}, { 212, 5060, 3373, 0x71, 0x01, 0xFA, 0x0D, 0xE6, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0x7EC, 0x7E8, 0x7E4, 0x205, 0x206, 0x207}, { 214, 5070, 3380, 0x71, 0x01, 0xFB, 0x0D, 0xE6, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0x7F0, 0x7EC, 0x7E8, 0x204, 0x205, 0x206}, { 216, 5080, 3387, 0x71, 0x01, 0xFC, 0x0D, 0xDE, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0x7F4, 0x7F0, 0x7EC, 0x203, 0x204, 0x205}, { 218, 5090, 3393, 0x71, 0x01, 0xFD, 0x0D, 0xDE, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0x7F8, 0x7F4, 0x7F0, 0x202, 0x203, 0x204}, { 220, 5100, 3400, 0x71, 0x01, 0xFE, 0x0C, 0xD6, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0x7FC, 0x7F8, 0x7F4, 0x201, 0x202, 0x203}, { 222, 5110, 3407, 0x71, 0x01, 0xFF, 0x0C, 0xD6, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0x800, 0x7FC, 0x7F8, 0x200, 0x201, 0x202}, { 224, 5120, 3413, 0x71, 0x02, 0x00, 0x0C, 0xCE, 0x01, 0x04, 0x0A, 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0x804, 0x800, 0x7FC, 0x1FF, 0x200, 0x201}, { 226, 5130, 3420, 0x71, 0x02, 0x01, 0x0C, 0xCE, 0x01, 0x04, 0x0A, 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0x808, 0x804, 0x800, 0x1FE, 0x1FF, 0x200}, { 228, 5140, 3427, 0x71, 0x02, 0x02, 0x0C, 0xC6, 0x01, 0x04, 0x0A, 0x00, 0x8D, 0x99, 0x99, 0xDD, 0x00, 0x0C, 0x0E, 0x8B, 0xDD, 0x00, 0x0C, 0x0E, 0x8B, 0x80C, 0x808, 0x804, 0x1FD, 0x1FE, 0x1FF}, { 32, 5160, 3440, 0x71, 0x02, 0x04, 0x0B, 0xBE, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0x814, 0x810, 0x80C, 0x1FB, 0x1FC, 0x1FD}, { 34, 5170, 3447, 0x71, 0x02, 0x05, 0x0B, 0xBE, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0x818, 0x814, 0x810, 0x1FA, 0x1FB, 0x1FC}, { 36, 5180, 3453, 0x71, 0x02, 0x06, 0x0B, 0xB6, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0x81C, 0x818, 0x814, 0x1F9, 0x1FA, 0x1FB}, { 38, 5190, 3460, 0x71, 0x02, 0x07, 0x0B, 0xB6, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0x820, 0x81C, 0x818, 0x1F8, 0x1F9, 0x1FA}, { 40, 5200, 3467, 0x71, 0x02, 0x08, 0x0B, 0xAF, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0x824, 0x820, 0x81C, 0x1F7, 0x1F8, 0x1F9}, { 42, 5210, 3473, 0x71, 0x02, 0x09, 0x0B, 0xAF, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0x828, 0x824, 0x820, 0x1F6, 0x1F7, 0x1F8}, { 44, 5220, 3480, 0x71, 0x02, 0x0A, 0x0A, 0xA7, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0x82C, 0x828, 0x824, 0x1F5, 0x1F6, 0x1F7}, { 46, 5230, 3487, 0x71, 0x02, 0x0B, 0x0A, 0xA7, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0x830, 0x82C, 0x828, 0x1F4, 0x1F5, 0x1F6}, { 48, 5240, 3493, 0x71, 0x02, 0x0C, 0x0A, 0xA0, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0x834, 0x830, 0x82C, 0x1F3, 0x1F4, 0x1F5}, { 50, 5250, 3500, 0x71, 0x02, 0x0D, 0x0A, 0xA0, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0x838, 0x834, 0x830, 0x1F2, 0x1F3, 0x1F4}, { 52, 5260, 3507, 0x71, 0x02, 0x0E, 0x0A, 0x98, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87, 0x83C, 0x838, 0x834, 0x1F1, 0x1F2, 0x1F3}, { 54, 5270, 3513, 0x71, 0x02, 0x0F, 0x0A, 0x98, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87, 0x840, 0x83C, 0x838, 0x1F0, 0x1F1, 0x1F2}, { 56, 5280, 3520, 0x71, 0x02, 0x10, 0x09, 0x91, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, 0x08, 0x86, 0x844, 0x840, 0x83C, 0x1F0, 0x1F0, 0x1F1}, { 58, 5290, 3527, 0x71, 0x02, 0x11, 0x09, 0x91, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, 0x08, 0x86, 0x848, 0x844, 0x840, 0x1EF, 0x1F0, 0x1F0}, { 60, 5300, 3533, 0x71, 0x02, 0x12, 0x09, 0x8A, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, 0x07, 0x85, 0x84C, 0x848, 0x844, 0x1EE, 0x1EF, 0x1F0}, { 62, 5310, 3540, 0x71, 0x02, 0x13, 0x09, 0x8A, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, 0x07, 0x85, 0x850, 0x84C, 0x848, 0x1ED, 0x1EE, 0x1EF}, { 64, 5320, 3547, 0x71, 0x02, 0x14, 0x09, 0x83, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, 0x07, 0x84, 0x854, 0x850, 0x84C, 0x1EC, 0x1ED, 0x1EE}, { 66, 5330, 3553, 0x71, 0x02, 0x15, 0x09, 0x83, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, 0x07, 0x84, 0x858, 0x854, 0x850, 0x1EB, 0x1EC, 0x1ED}, { 68, 5340, 3560, 0x71, 0x02, 0x16, 0x08, 0x7C, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, 0x06, 0x84, 0x85C, 0x858, 0x854, 0x1EA, 0x1EB, 0x1EC}, { 70, 5350, 3567, 0x71, 0x02, 0x17, 0x08, 0x7C, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, 0x06, 0x84, 0x860, 0x85C, 0x858, 0x1E9, 0x1EA, 0x1EB}, { 72, 5360, 3573, 0x71, 0x02, 0x18, 0x08, 0x75, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, 0x05, 0x83, 0x864, 0x860, 0x85C, 0x1E8, 0x1E9, 0x1EA}, { 74, 5370, 3580, 0x71, 0x02, 0x19, 0x08, 0x75, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, 0x05, 0x83, 0x868, 0x864, 0x860, 0x1E7, 0x1E8, 0x1E9}, { 76, 5380, 3587, 0x71, 0x02, 0x1A, 0x08, 0x6E, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, 0x04, 0x82, 0x86C, 0x868, 0x864, 0x1E6, 0x1E7, 0x1E8}, { 78, 5390, 3593, 0x71, 0x02, 0x1B, 0x08, 0x6E, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, 0x04, 0x82, 0x870, 0x86C, 0x868, 0x1E5, 0x1E6, 0x1E7}, { 80, 5400, 3600, 0x71, 0x02, 0x1C, 0x07, 0x67, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, 0x04, 0x81, 0x874, 0x870, 0x86C, 0x1E5, 0x1E5, 0x1E6}, { 82, 5410, 3607, 0x71, 0x02, 0x1D, 0x07, 0x67, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, 0x04, 0x81, 0x878, 0x874, 0x870, 0x1E4, 0x1E5, 0x1E5}, { 84, 5420, 3613, 0x71, 0x02, 0x1E, 0x07, 0x61, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, 0x03, 0x80, 0x87C, 0x878, 0x874, 0x1E3, 0x1E4, 0x1E5}, { 86, 5430, 3620, 0x71, 0x02, 0x1F, 0x07, 0x61, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, 0x03, 0x80, 0x880, 0x87C, 0x878, 0x1E2, 0x1E3, 0x1E4}, { 88, 5440, 3627, 0x71, 0x02, 0x20, 0x07, 0x5A, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, 0x02, 0x80, 0x884, 0x880, 0x87C, 0x1E1, 0x1E2, 0x1E3}, { 90, 5450, 3633, 0x71, 0x02, 0x21, 0x07, 0x5A, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, 0x02, 0x80, 0x888, 0x884, 0x880, 0x1E0, 0x1E1, 0x1E2}, { 92, 5460, 3640, 0x71, 0x02, 0x22, 0x06, 0x53, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, 0x01, 0x80, 0x88C, 0x888, 0x884, 0x1DF, 0x1E0, 0x1E1}, { 94, 5470, 3647, 0x71, 0x02, 0x23, 0x06, 0x53, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, 0x01, 0x80, 0x890, 0x88C, 0x888, 0x1DE, 0x1DF, 0x1E0}, { 96, 5480, 3653, 0x71, 0x02, 0x24, 0x06, 0x4D, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80, 0x894, 0x890, 0x88C, 0x1DD, 0x1DE, 0x1DF}, { 98, 5490, 3660, 0x71, 0x02, 0x25, 0x06, 0x4D, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80, 0x898, 0x894, 0x890, 0x1DD, 0x1DD, 0x1DE}, { 100, 5500, 3667, 0x71, 0x02, 0x26, 0x06, 0x47, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80, 0x89C, 0x898, 0x894, 0x1DC, 0x1DD, 0x1DD}, { 102, 5510, 3673, 0x71, 0x02, 0x27, 0x06, 0x47, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80, 0x8A0, 0x89C, 0x898, 0x1DB, 0x1DC, 0x1DD}, { 104, 5520, 3680, 0x71, 0x02, 0x28, 0x05, 0x40, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80, 0x8A4, 0x8A0, 0x89C, 0x1DA, 0x1DB, 0x1DC}, { 106, 5530, 3687, 0x71, 0x02, 0x29, 0x05, 0x40, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80, 0x8A8, 0x8A4, 0x8A0, 0x1D9, 0x1DA, 0x1DB}, { 108, 5540, 3693, 0x71, 0x02, 0x2A, 0x05, 0x3A, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80, 0x8AC, 0x8A8, 0x8A4, 0x1D8, 0x1D9, 0x1DA}, { 110, 5550, 3700, 0x71, 0x02, 0x2B, 0x05, 0x3A, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80, 0x8B0, 0x8AC, 0x8A8, 0x1D7, 0x1D8, 0x1D9}, { 112, 5560, 3707, 0x71, 0x02, 0x2C, 0x05, 0x34, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80, 0x8B4, 0x8B0, 0x8AC, 0x1D7, 0x1D7, 0x1D8}, { 114, 5570, 3713, 0x71, 0x02, 0x2D, 0x05, 0x34, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80, 0x8B8, 0x8B4, 0x8B0, 0x1D6, 0x1D7, 0x1D7}, { 116, 5580, 3720, 0x71, 0x02, 0x2E, 0x04, 0x2E, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80, 0x8BC, 0x8B8, 0x8B4, 0x1D5, 0x1D6, 0x1D7}, { 118, 5590, 3727, 0x71, 0x02, 0x2F, 0x04, 0x2E, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80, 0x8C0, 0x8BC, 0x8B8, 0x1D4, 0x1D5, 0x1D6}, { 120, 5600, 3733, 0x71, 0x02, 0x30, 0x04, 0x28, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, 0x00, 0x80, 0x8C4, 0x8C0, 0x8BC, 0x1D3, 0x1D4, 0x1D5}, { 122, 5610, 3740, 0x71, 0x02, 0x31, 0x04, 0x28, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, 0x00, 0x80, 0x8C8, 0x8C4, 0x8C0, 0x1D2, 0x1D3, 0x1D4}, { 124, 5620, 3747, 0x71, 0x02, 0x32, 0x04, 0x21, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x80, 0x8CC, 0x8C8, 0x8C4, 0x1D2, 0x1D2, 0x1D3}, { 126, 5630, 3753, 0x71, 0x02, 0x33, 0x04, 0x21, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x80, 0x8D0, 0x8CC, 0x8C8, 0x1D1, 0x1D2, 0x1D2}, { 128, 5640, 3760, 0x71, 0x02, 0x34, 0x03, 0x1C, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8D4, 0x8D0, 0x8CC, 0x1D0, 0x1D1, 0x1D2}, { 130, 5650, 3767, 0x71, 0x02, 0x35, 0x03, 0x1C, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8D8, 0x8D4, 0x8D0, 0x1CF, 0x1D0, 0x1D1}, { 132, 5660, 3773, 0x71, 0x02, 0x36, 0x03, 0x16, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8DC, 0x8D8, 0x8D4, 0x1CE, 0x1CF, 0x1D0}, { 134, 5670, 3780, 0x71, 0x02, 0x37, 0x03, 0x16, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8E0, 0x8DC, 0x8D8, 0x1CE, 0x1CE, 0x1CF}, { 136, 5680, 3787, 0x71, 0x02, 0x38, 0x03, 0x10, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8E4, 0x8E0, 0x8DC, 0x1CD, 0x1CE, 0x1CE}, { 138, 5690, 3793, 0x71, 0x02, 0x39, 0x03, 0x10, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8E8, 0x8E4, 0x8E0, 0x1CC, 0x1CD, 0x1CE}, { 140, 5700, 3800, 0x71, 0x02, 0x3A, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8EC, 0x8E8, 0x8E4, 0x1CB, 0x1CC, 0x1CD}, { 142, 5710, 3807, 0x71, 0x02, 0x3B, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8F0, 0x8EC, 0x8E8, 0x1CA, 0x1CB, 0x1CC}, { 144, 5720, 3813, 0x71, 0x02, 0x3C, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8F4, 0x8F0, 0x8EC, 0x1C9, 0x1CA, 0x1CB}, { 145, 5725, 3817, 0x72, 0x04, 0x79, 0x02, 0x03, 0x01, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8F6, 0x8F2, 0x8EE, 0x1C9, 0x1CA, 0x1CB}, { 146, 5730, 3820, 0x71, 0x02, 0x3D, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8F8, 0x8F4, 0x8F0, 0x1C9, 0x1C9, 0x1CA}, { 147, 5735, 3823, 0x72, 0x04, 0x7B, 0x02, 0x03, 0x01, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8FA, 0x8F6, 0x8F2, 0x1C8, 0x1C9, 0x1CA}, { 148, 5740, 3827, 0x71, 0x02, 0x3E, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8FC, 0x8F8, 0x8F4, 0x1C8, 0x1C9, 0x1C9}, { 149, 5745, 3830, 0x72, 0x04, 0x7D, 0x02, 0xFE, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8FE, 0x8FA, 0x8F6, 0x1C8, 0x1C8, 0x1C9}, { 150, 5750, 3833, 0x71, 0x02, 0x3F, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x900, 0x8FC, 0x8F8, 0x1C7, 0x1C8, 0x1C9}, { 151, 5755, 3837, 0x72, 0x04, 0x7F, 0x02, 0xFE, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x902, 0x8FE, 0x8FA, 0x1C7, 0x1C8, 0x1C8}, { 152, 5760, 3840, 0x71, 0x02, 0x40, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x904, 0x900, 0x8FC, 0x1C6, 0x1C7, 0x1C8}, { 153, 5765, 3843, 0x72, 0x04, 0x81, 0x02, 0xF8, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x906, 0x902, 0x8FE, 0x1C6, 0x1C7, 0x1C8}, { 154, 5770, 3847, 0x71, 0x02, 0x41, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x908, 0x904, 0x900, 0x1C6, 0x1C6, 0x1C7}, { 155, 5775, 3850, 0x72, 0x04, 0x83, 0x02, 0xF8, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x90A, 0x906, 0x902, 0x1C5, 0x1C6, 0x1C7}, { 156, 5780, 3853, 0x71, 0x02, 0x42, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x90C, 0x908, 0x904, 0x1C5, 0x1C6, 0x1C6}, { 157, 5785, 3857, 0x72, 0x04, 0x85, 0x02, 0xF2, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x90E, 0x90A, 0x906, 0x1C4, 0x1C5, 0x1C6}, { 158, 5790, 3860, 0x71, 0x02, 0x43, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x910, 0x90C, 0x908, 0x1C4, 0x1C5, 0x1C6}, { 159, 5795, 3863, 0x72, 0x04, 0x87, 0x02, 0xF2, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x912, 0x90E, 0x90A, 0x1C4, 0x1C4, 0x1C5}, { 160, 5800, 3867, 0x71, 0x02, 0x44, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x914, 0x910, 0x90C, 0x1C3, 0x1C4, 0x1C5}, { 161, 5805, 3870, 0x72, 0x04, 0x89, 0x01, 0xED, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x916, 0x912, 0x90E, 0x1C3, 0x1C4, 0x1C4}, { 162, 5810, 3873, 0x71, 0x02, 0x45, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x918, 0x914, 0x910, 0x1C2, 0x1C3, 0x1C4}, { 163, 5815, 3877, 0x72, 0x04, 0x8B, 0x01, 0xED, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x91A, 0x916, 0x912, 0x1C2, 0x1C3, 0x1C4}, { 164, 5820, 3880, 0x71, 0x02, 0x46, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x91C, 0x918, 0x914, 0x1C2, 0x1C2, 0x1C3}, { 165, 5825, 3883, 0x72, 0x04, 0x8D, 0x01, 0xED, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x91E, 0x91A, 0x916, 0x1C1, 0x1C2, 0x1C3}, { 166, 5830, 3887, 0x71, 0x02, 0x47, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x920, 0x91C, 0x918, 0x1C1, 0x1C2, 0x1C2}, { 168, 5840, 3893, 0x71, 0x02, 0x48, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x924, 0x920, 0x91C, 0x1C0, 0x1C1, 0x1C2}, { 170, 5850, 3900, 0x71, 0x02, 0x49, 0x01, 0xE0, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x928, 0x924, 0x920, 0x1BF, 0x1C0, 0x1C1}, { 172, 5860, 3907, 0x71, 0x02, 0x4A, 0x01, 0xDE, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x92C, 0x928, 0x924, 0x1BF, 0x1BF, 0x1C0}, { 174, 5870, 3913, 0x71, 0x02, 0x4B, 0x00, 0xDB, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x930, 0x92C, 0x928, 0x1BE, 0x1BF, 0x1BF}, { 176, 5880, 3920, 0x71, 0x02, 0x4C, 0x00, 0xD8, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x934, 0x930, 0x92C, 0x1BD, 0x1BE, 0x1BF}, { 178, 5890, 3927, 0x71, 0x02, 0x4D, 0x00, 0xD6, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x938, 0x934, 0x930, 0x1BC, 0x1BD, 0x1BE}, { 180, 5900, 3933, 0x71, 0x02, 0x4E, 0x00, 0xD3, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x93C, 0x938, 0x934, 0x1BC, 0x1BC, 0x1BD}, { 182, 5910, 3940, 0x71, 0x02, 0x4F, 0x00, 0xD6, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x940, 0x93C, 0x938, 0x1BB, 0x1BC, 0x1BC}, { 1, 2412, 3216, 0x73, 0x09, 0x6C, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0D, 0x0C, 0x80, 0xFF, 0x88, 0x0D, 0x0C, 0x80, 0x3C9, 0x3C5, 0x3C1, 0x43A, 0x43F, 0x443}, { 2, 2417, 3223, 0x73, 0x09, 0x71, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0B, 0x80, 0xFF, 0x88, 0x0C, 0x0B, 0x80, 0x3CB, 0x3C7, 0x3C3, 0x438, 0x43D, 0x441}, { 3, 2422, 3229, 0x73, 0x09, 0x76, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0x3CD, 0x3C9, 0x3C5, 0x436, 0x43A, 0x43F}, { 4, 2427, 3236, 0x73, 0x09, 0x7B, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0x3CF, 0x3CB, 0x3C7, 0x434, 0x438, 0x43D}, { 5, 2432, 3243, 0x73, 0x09, 0x80, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x09, 0x80, 0xFF, 0x88, 0x0C, 0x09, 0x80, 0x3D1, 0x3CD, 0x3C9, 0x431, 0x436, 0x43A}, { 6, 2437, 3249, 0x73, 0x09, 0x85, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0B, 0x08, 0x80, 0xFF, 0x88, 0x0B, 0x08, 0x80, 0x3D3, 0x3CF, 0x3CB, 0x42F, 0x434, 0x438}, { 7, 2442, 3256, 0x73, 0x09, 0x8A, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x07, 0x80, 0xFF, 0x88, 0x0A, 0x07, 0x80, 0x3D5, 0x3D1, 0x3CD, 0x42D, 0x431, 0x436}, { 8, 2447, 3263, 0x73, 0x09, 0x8F, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x06, 0x80, 0xFF, 0x88, 0x0A, 0x06, 0x80, 0x3D7, 0x3D3, 0x3CF, 0x42B, 0x42F, 0x434}, { 9, 2452, 3269, 0x73, 0x09, 0x94, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x09, 0x06, 0x80, 0xFF, 0x88, 0x09, 0x06, 0x80, 0x3D9, 0x3D5, 0x3D1, 0x429, 0x42D, 0x431}, { 10, 2457, 3276, 0x73, 0x09, 0x99, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x05, 0x80, 0xFF, 0x88, 0x08, 0x05, 0x80, 0x3DB, 0x3D7, 0x3D3, 0x427, 0x42B, 0x42F}, { 11, 2462, 3283, 0x73, 0x09, 0x9E, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x04, 0x80, 0xFF, 0x88, 0x08, 0x04, 0x80, 0x3DD, 0x3D9, 0x3D5, 0x424, 0x429, 0x42D}, { 12, 2467, 3289, 0x73, 0x09, 0xA3, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x03, 0x80, 0xFF, 0x88, 0x08, 0x03, 0x80, 0x3DF, 0x3DB, 0x3D7, 0x422, 0x427, 0x42B}, { 13, 2472, 3296, 0x73, 0x09, 0xA8, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x03, 0x80, 0xFF, 0x88, 0x07, 0x03, 0x80, 0x3E1, 0x3DD, 0x3D9, 0x420, 0x424, 0x429}, { 14, 2484, 3312, 0x73, 0x09, 0xB4, 0x0F, 0xFF, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x01, 0x80, 0xFF, 0x88, 0x07, 0x01, 0x80, 0x3E6, 0x3E2, 0x3DE, 0x41B, 0x41F, 0x424} }; static const struct chan_info_nphy_radio205x chan_info_nphyrev3_2056[] = { { 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfc, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf6, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, { 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio205x chan_info_nphyrev4_2056_A1[] = { { 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, { 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio205x chan_info_nphyrev5_2056v5[] = { { 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6e, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5b, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5a, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5a, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5a, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x5a, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x5a, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x59, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x59, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x59, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x69, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x69, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x78, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x75, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x75, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x75, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x84, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, { 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v6[] = { { 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, { 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio205x chan_info_nphyrev5n6_2056v7[] = { { 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6e, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7b, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7a, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7a, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7a, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x7a, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x7a, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x79, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x79, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x79, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x79, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x79, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x78, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x85, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x85, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x85, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x84, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x84, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, { 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v8[] = { { 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, { 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v11[] = { { 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, { 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio2057 chan_info_nphyrev7_2057_rev4[] = { { 184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216, }, { 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215, }, { 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214, }, { 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213, }, { 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212, }, { 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211, }, { 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f, }, { 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e, }, { 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d, }, { 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c, }, { 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b, }, { 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a, }, { 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209, }, { 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208, }, { 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xe3, 0x00, 0xef, 0x00, 0x00, 0x0f, 0x0f, 0xe3, 0x00, 0xef, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207, }, { 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206, }, { 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205, }, { 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204, }, { 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203, }, { 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202, }, { 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201, }, { 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200, }, { 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0e, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0e, 0x0e, 0xe3, 0x00, 0xd6, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff, }, { 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd, }, { 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc, }, { 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb, }, { 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa, }, { 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9, }, { 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8, }, { 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7, }, { 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6, }, { 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5, }, { 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4, }, { 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3, }, { 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2, }, { 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1, }, { 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0, }, { 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0, }, { 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef, }, { 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee, }, { 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed, }, { 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0c, 0xc3, 0x00, 0xa1, 0x00, 0x00, 0x0a, 0x0c, 0xc3, 0x00, 0xa1, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec, }, { 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb, }, { 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea, }, { 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9, }, { 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8, }, { 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0a, 0xa3, 0x00, 0xa1, 0x00, 0x00, 0x0a, 0x0a, 0xa3, 0x00, 0xa1, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7, }, { 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6, }, { 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5, }, { 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0xa3, 0x00, 0x90, 0x00, 0x00, 0x09, 0x09, 0xa3, 0x00, 0x90, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5, }, { 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4, }, { 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3, }, { 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2, }, { 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x90, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x90, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1, }, { 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x60, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0, }, { 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df, }, { 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de, }, { 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd, }, { 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd, }, { 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc, }, { 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db, }, { 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da, }, { 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9, }, { 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8, }, { 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7, }, { 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x05, 0x83, 0x00, 0x60, 0x00, 0x00, 0x07, 0x05, 0x83, 0x00, 0x60, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7, }, { 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x04, 0x83, 0x00, 0x60, 0x00, 0x00, 0x07, 0x04, 0x83, 0x00, 0x60, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6, }, { 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x04, 0x73, 0x00, 0x30, 0x00, 0x00, 0x07, 0x04, 0x73, 0x00, 0x30, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5, }, { 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4, }, { 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3, }, { 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2, }, { 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2, }, { 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1, }, { 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0, }, { 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x03, 0x63, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x63, 0x00, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf, }, { 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce, }, { 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce, }, { 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd, }, { 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc, }, { 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb, }, { 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x05, 0x01, 0x53, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x53, 0x00, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb, }, { 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca, }, { 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca, }, { 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9, }, { 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9, }, { 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9, }, { 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8, }, { 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8, }, { 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8, }, { 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7, }, { 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7, }, { 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x03, 0x01, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x43, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6, }, { 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6, }, { 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6, }, { 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5, }, { 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5, }, { 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4, }, { 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4, }, { 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4, }, { 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3, }, { 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3, }, { 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2, }, { 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2, }, { 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1, }, { 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0, }, { 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf, }, { 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf, }, { 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be, }, { 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd, }, { 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443, }, { 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441, }, { 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f, }, { 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d, }, { 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a, }, { 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438, }, { 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x51, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436, }, { 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434, }, { 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431, }, { 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f, }, { 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d, }, { 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b, }, { 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429, }, { 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, 0x04, 0x00, 0x04, 0x00, 0x11, 0x43, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x11, 0x43, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio2057_rev5 chan_info_nphyrev8_2057_rev5[] = { { 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, 0x03, 0xef, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0c, 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, 0x03, 0xdf, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, 0x03, 0xcf, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, 0x03, 0xbf, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, 0x03, 0xaf, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, 0x03, 0x9f, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, 0x03, 0x8f, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, 0x03, 0x7f, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, 0x03, 0x6f, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0b, 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, 0x03, 0x5f, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, 0x03, 0x4f, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, 0x03, 0x3f, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio2057_rev5 chan_info_nphyrev9_2057_rev5v1[] = { { 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, 0x03, 0xef, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0c, 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, 0x03, 0xdf, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, 0x03, 0xcf, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, 0x03, 0xbf, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, 0x03, 0xaf, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, 0x03, 0x9f, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, 0x03, 0x8f, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, 0x03, 0x7f, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, 0x03, 0x6f, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0b, 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, 0x03, 0x5f, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, 0x03, 0x4f, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, 0x03, 0x3f, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev7[] = { { 184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, { 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, 0x04, 0x00, 0x04, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev8[] = { { 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, { 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, { 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, { 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, { 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, { 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, { 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, { 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, { 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, { 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, { 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, { 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, { 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, { 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, { 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, { 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, { 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, { 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, { 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, { 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, { 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, { 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, { 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, { 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, { 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, { 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, { 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, { 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, { 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, { 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, { 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, { 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, { 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, { 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, { 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, { 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, { 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, { 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, { 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, { 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, { 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, { 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, { 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, { 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, { 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, { 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, { 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, { 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, { 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, { 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, { 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, { 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, { 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, { 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, { 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, { 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, { 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, { 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, { 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, { 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, { 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, { 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, { 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, { 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, { 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, { 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, { 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, { 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, { 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, { 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, { 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, { 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, { 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, { 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, { 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, { 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, { 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, { 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, { 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, { 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, { 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, { 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, { 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, { 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, { 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, { 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, { 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, { 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, { 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, { 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, { 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, { 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, { 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, { 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, { 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, { 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, { 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, { 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, { 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, { 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, { 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, { 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, { 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, { 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, { 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, { 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, { 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, { 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, { 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, { 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, { 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, { 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, { 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, { 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, { 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, { 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, { 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, { 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, { 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, { 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, { 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, { 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, 0x04, 0x00, 0x04, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} }; static struct radio_regs regs_2055[] = { {0x02, 0x80, 0x80, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0x27, 0x27, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0x27, 0x27, 0, 0}, {0x07, 0x7f, 0x7f, 1, 1}, {0x08, 0x7, 0x7, 1, 1}, {0x09, 0x7f, 0x7f, 1, 1}, {0x0A, 0x7, 0x7, 1, 1}, {0x0B, 0x15, 0x15, 0, 0}, {0x0C, 0x15, 0x15, 0, 0}, {0x0D, 0x4f, 0x4f, 1, 1}, {0x0E, 0x5, 0x5, 1, 1}, {0x0F, 0x4f, 0x4f, 1, 1}, {0x10, 0x5, 0x5, 1, 1}, {0x11, 0xd0, 0xd0, 0, 0}, {0x12, 0x2, 0x2, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0x40, 0x40, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0xc0, 0xc0, 0, 0}, {0x1E, 0xff, 0xff, 0, 0}, {0x1F, 0xc0, 0xc0, 0, 0}, {0x20, 0xff, 0xff, 0, 0}, {0x21, 0xc0, 0xc0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x2c, 0x2c, 0, 0}, {0x24, 0, 0, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0xa4, 0xa4, 0, 0}, {0x2E, 0x38, 0x38, 0, 0}, {0x2F, 0, 0, 0, 0}, {0x30, 0x4, 0x4, 1, 1}, {0x31, 0, 0, 0, 0}, {0x32, 0xa, 0xa, 0, 0}, {0x33, 0x87, 0x87, 0, 0}, {0x34, 0x9, 0x9, 0, 0}, {0x35, 0x70, 0x70, 0, 0}, {0x36, 0x11, 0x11, 0, 0}, {0x37, 0x18, 0x18, 1, 1}, {0x38, 0x6, 0x6, 0, 0}, {0x39, 0x4, 0x4, 1, 1}, {0x3A, 0x6, 0x6, 0, 0}, {0x3B, 0x9e, 0x9e, 0, 0}, {0x3C, 0x9, 0x9, 0, 0}, {0x3D, 0xc8, 0xc8, 1, 1}, {0x3E, 0x88, 0x88, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0, 0, 0, 0}, {0x42, 0x1, 0x1, 0, 0}, {0x43, 0x2, 0x2, 0, 0}, {0x44, 0x96, 0x96, 0, 0}, {0x45, 0x3e, 0x3e, 0, 0}, {0x46, 0x3e, 0x3e, 0, 0}, {0x47, 0x13, 0x13, 0, 0}, {0x48, 0x2, 0x2, 0, 0}, {0x49, 0x15, 0x15, 0, 0}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0, 0, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0, 0, 0, 0}, {0x50, 0x8, 0x8, 0, 0}, {0x51, 0x8, 0x8, 0, 0}, {0x52, 0x6, 0x6, 0, 0}, {0x53, 0x84, 0x84, 1, 1}, {0x54, 0xc3, 0xc3, 0, 0}, {0x55, 0x8f, 0x8f, 0, 0}, {0x56, 0xff, 0xff, 0, 0}, {0x57, 0xff, 0xff, 0, 0}, {0x58, 0x88, 0x88, 0, 0}, {0x59, 0x88, 0x88, 0, 0}, {0x5A, 0, 0, 0, 0}, {0x5B, 0xcc, 0xcc, 0, 0}, {0x5C, 0x6, 0x6, 0, 0}, {0x5D, 0x80, 0x80, 0, 0}, {0x5E, 0x80, 0x80, 0, 0}, {0x5F, 0xf8, 0xf8, 0, 0}, {0x60, 0x88, 0x88, 0, 0}, {0x61, 0x88, 0x88, 0, 0}, {0x62, 0x88, 0x8, 1, 1}, {0x63, 0x88, 0x88, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0x1, 0x1, 1, 1}, {0x66, 0x8a, 0x8a, 0, 0}, {0x67, 0x8, 0x8, 0, 0}, {0x68, 0x83, 0x83, 0, 0}, {0x69, 0x6, 0x6, 0, 0}, {0x6A, 0xa0, 0xa0, 0, 0}, {0x6B, 0xa, 0xa, 0, 0}, {0x6C, 0x87, 0x87, 1, 1}, {0x6D, 0x2a, 0x2a, 0, 0}, {0x6E, 0x2a, 0x2a, 0, 0}, {0x6F, 0x2a, 0x2a, 0, 0}, {0x70, 0x2a, 0x2a, 0, 0}, {0x71, 0x18, 0x18, 0, 0}, {0x72, 0x6a, 0x6a, 1, 1}, {0x73, 0xab, 0xab, 1, 1}, {0x74, 0x13, 0x13, 1, 1}, {0x75, 0xc1, 0xc1, 1, 1}, {0x76, 0xaa, 0xaa, 1, 1}, {0x77, 0x87, 0x87, 1, 1}, {0x78, 0, 0, 0, 0}, {0x79, 0x6, 0x6, 0, 0}, {0x7A, 0x7, 0x7, 0, 0}, {0x7B, 0x7, 0x7, 0, 0}, {0x7C, 0x15, 0x15, 0, 0}, {0x7D, 0x55, 0x55, 0, 0}, {0x7E, 0x97, 0x97, 1, 1}, {0x7F, 0x8, 0x8, 0, 0}, {0x80, 0x14, 0x14, 1, 1}, {0x81, 0x33, 0x33, 0, 0}, {0x82, 0x88, 0x88, 0, 0}, {0x83, 0x6, 0x6, 0, 0}, {0x84, 0x3, 0x3, 1, 1}, {0x85, 0xa, 0xa, 0, 0}, {0x86, 0x3, 0x3, 1, 1}, {0x87, 0x2a, 0x2a, 0, 0}, {0x88, 0xa4, 0xa4, 0, 0}, {0x89, 0x18, 0x18, 0, 0}, {0x8A, 0x28, 0x28, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0x4a, 0x4a, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0xf8, 0xf8, 0, 0}, {0x8F, 0x88, 0x88, 0, 0}, {0x90, 0x88, 0x88, 0, 0}, {0x91, 0x88, 0x8, 1, 1}, {0x92, 0x88, 0x88, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0x1, 0x1, 1, 1}, {0x95, 0x8a, 0x8a, 0, 0}, {0x96, 0x8, 0x8, 0, 0}, {0x97, 0x83, 0x83, 0, 0}, {0x98, 0x6, 0x6, 0, 0}, {0x99, 0xa0, 0xa0, 0, 0}, {0x9A, 0xa, 0xa, 0, 0}, {0x9B, 0x87, 0x87, 1, 1}, {0x9C, 0x2a, 0x2a, 0, 0}, {0x9D, 0x2a, 0x2a, 0, 0}, {0x9E, 0x2a, 0x2a, 0, 0}, {0x9F, 0x2a, 0x2a, 0, 0}, {0xA0, 0x18, 0x18, 0, 0}, {0xA1, 0x6a, 0x6a, 1, 1}, {0xA2, 0xab, 0xab, 1, 1}, {0xA3, 0x13, 0x13, 1, 1}, {0xA4, 0xc1, 0xc1, 1, 1}, {0xA5, 0xaa, 0xaa, 1, 1}, {0xA6, 0x87, 0x87, 1, 1}, {0xA7, 0, 0, 0, 0}, {0xA8, 0x6, 0x6, 0, 0}, {0xA9, 0x7, 0x7, 0, 0}, {0xAA, 0x7, 0x7, 0, 0}, {0xAB, 0x15, 0x15, 0, 0}, {0xAC, 0x55, 0x55, 0, 0}, {0xAD, 0x97, 0x97, 1, 1}, {0xAE, 0x8, 0x8, 0, 0}, {0xAF, 0x14, 0x14, 1, 1}, {0xB0, 0x33, 0x33, 0, 0}, {0xB1, 0x88, 0x88, 0, 0}, {0xB2, 0x6, 0x6, 0, 0}, {0xB3, 0x3, 0x3, 1, 1}, {0xB4, 0xa, 0xa, 0, 0}, {0xB5, 0x3, 0x3, 1, 1}, {0xB6, 0x2a, 0x2a, 0, 0}, {0xB7, 0xa4, 0xa4, 0, 0}, {0xB8, 0x18, 0x18, 0, 0}, {0xB9, 0x28, 0x28, 0, 0}, {0xBA, 0, 0, 0, 0}, {0xBB, 0x4a, 0x4a, 0, 0}, {0xBC, 0, 0, 0, 0}, {0xBD, 0x71, 0x71, 0, 0}, {0xBE, 0x72, 0x72, 0, 0}, {0xBF, 0x73, 0x73, 0, 0}, {0xC0, 0x74, 0x74, 0, 0}, {0xC1, 0x75, 0x75, 0, 0}, {0xC2, 0x76, 0x76, 0, 0}, {0xC3, 0x77, 0x77, 0, 0}, {0xC4, 0x78, 0x78, 0, 0}, {0xC5, 0x79, 0x79, 0, 0}, {0xC6, 0x7a, 0x7a, 0, 0}, {0xC7, 0, 0, 0, 0}, {0xC8, 0, 0, 0, 0}, {0xC9, 0, 0, 0, 0}, {0xCA, 0, 0, 0, 0}, {0xCB, 0, 0, 0, 0}, {0xCC, 0, 0, 0, 0}, {0xCD, 0, 0, 0, 0}, {0xCE, 0x6, 0x6, 0, 0}, {0xCF, 0, 0, 0, 0}, {0xD0, 0, 0, 0, 0}, {0xD1, 0x18, 0x18, 0, 0}, {0xD2, 0x88, 0x88, 0, 0}, {0xD3, 0, 0, 0, 0}, {0xD4, 0, 0, 0, 0}, {0xD5, 0, 0, 0, 0}, {0xD6, 0, 0, 0, 0}, {0xD7, 0, 0, 0, 0}, {0xD8, 0, 0, 0, 0}, {0xD9, 0, 0, 0, 0}, {0xDA, 0x6, 0x6, 0, 0}, {0xDB, 0, 0, 0, 0}, {0xDC, 0, 0, 0, 0}, {0xDD, 0x18, 0x18, 0, 0}, {0xDE, 0x88, 0x88, 0, 0}, {0xDF, 0, 0, 0, 0}, {0xE0, 0, 0, 0, 0}, {0xE1, 0, 0, 0, 0}, {0xE2, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static struct radio_regs regs_SYN_2056[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0x1, 0x1, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0x60, 0x60, 0, 0}, {0x23, 0x6, 0x6, 0, 0}, {0x24, 0xc, 0xc, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0x1, 0x1, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0xd, 0xd, 0, 0}, {0x2F, 0x1f, 0x1f, 0, 0}, {0x30, 0x15, 0x15, 0, 0}, {0x31, 0xf, 0xf, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0, 0, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0, 0, 0, 0}, {0x38, 0, 0, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0, 0, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x13, 0x13, 0, 0}, {0x3D, 0xf, 0xf, 0, 0}, {0x3E, 0x18, 0x18, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x20, 0x20, 0, 0}, {0x42, 0x20, 0x20, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x77, 0x77, 0, 0}, {0x45, 0x7, 0x7, 0, 0}, {0x46, 0x1, 0x1, 0, 0}, {0x47, 0x4, 0x4, 0, 0}, {0x48, 0xf, 0xf, 0, 0}, {0x49, 0x30, 0x30, 0, 0}, {0x4A, 0x32, 0x32, 0, 0}, {0x4B, 0xd, 0xd, 0, 0}, {0x4C, 0xd, 0xd, 0, 0}, {0x4D, 0x4, 0x4, 0, 0}, {0x4E, 0x6, 0x6, 0, 0}, {0x4F, 0x1, 0x1, 0, 0}, {0x50, 0x1c, 0x1c, 0, 0}, {0x51, 0x2, 0x2, 0, 0}, {0x52, 0x2, 0x2, 0, 0}, {0x53, 0xf7, 0xf7, 1, 1}, {0x54, 0xb4, 0xb4, 0, 0}, {0x55, 0xd2, 0xd2, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0, 0, 0, 0}, {0x58, 0x4, 0x4, 0, 0}, {0x59, 0x96, 0x96, 0, 0}, {0x5A, 0x3e, 0x3e, 0, 0}, {0x5B, 0x3e, 0x3e, 0, 0}, {0x5C, 0x13, 0x13, 0, 0}, {0x5D, 0x2, 0x2, 0, 0}, {0x5E, 0, 0, 0, 0}, {0x5F, 0x7, 0x7, 0, 0}, {0x60, 0x7, 0x7, 1, 1}, {0x61, 0x8, 0x8, 0, 0}, {0x62, 0x3, 0x3, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0x40, 0x40, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0x1, 0x1, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0x60, 0x60, 0, 0}, {0x71, 0x66, 0x66, 0, 0}, {0x72, 0xc, 0xc, 0, 0}, {0x73, 0x66, 0x66, 0, 0}, {0x74, 0x8f, 0x8f, 1, 1}, {0x75, 0, 0, 0, 0}, {0x76, 0xcc, 0xcc, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0x66, 0x66, 0, 0}, {0x79, 0x66, 0x66, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0xff, 0xff, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0, 0, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0, 0, 0, 0}, {0x9A, 0, 0, 0, 0}, {0x9B, 0, 0, 0, 0}, {0x9C, 0, 0, 0, 0}, {0x9D, 0, 0, 0, 0}, {0x9E, 0, 0, 0, 0}, {0x9F, 0x6, 0x6, 0, 0}, {0xA0, 0x66, 0x66, 0, 0}, {0xA1, 0x66, 0x66, 0, 0}, {0xA2, 0x66, 0x66, 0, 0}, {0xA3, 0x66, 0x66, 0, 0}, {0xA4, 0x66, 0x66, 0, 0}, {0xA5, 0x66, 0x66, 0, 0}, {0xA6, 0x66, 0x66, 0, 0}, {0xA7, 0x66, 0x66, 0, 0}, {0xA8, 0x66, 0x66, 0, 0}, {0xA9, 0x66, 0x66, 0, 0}, {0xAA, 0x66, 0x66, 0, 0}, {0xAB, 0x66, 0x66, 0, 0}, {0xAC, 0x66, 0x66, 0, 0}, {0xAD, 0x66, 0x66, 0, 0}, {0xAE, 0x66, 0x66, 0, 0}, {0xAF, 0x66, 0x66, 0, 0}, {0xB0, 0x66, 0x66, 0, 0}, {0xB1, 0x66, 0x66, 0, 0}, {0xB2, 0x66, 0x66, 0, 0}, {0xB3, 0xa, 0xa, 0, 0}, {0xB4, 0, 0, 0, 0}, {0xB5, 0, 0, 0, 0}, {0xB6, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_TX_2056[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0x88, 0x88, 0, 0}, {0x22, 0x88, 0x88, 0, 0}, {0x23, 0x88, 0x88, 0, 0}, {0x24, 0x88, 0x88, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0x3, 0x3, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x3, 0x3, 0, 0}, {0x2A, 0x37, 0x37, 0, 0}, {0x2B, 0x3, 0x3, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0x1, 0x1, 0, 0}, {0x2F, 0x1, 0x1, 0, 0}, {0x30, 0, 0, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0x11, 0x11, 0, 0}, {0x34, 0x11, 0x11, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x3, 0x3, 0, 0}, {0x38, 0xf, 0xf, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0x2d, 0x2d, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x6e, 0x6e, 0, 0}, {0x3D, 0xf0, 0xf0, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x3, 0x3, 0, 0}, {0x42, 0x3, 0x3, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x1e, 0x1e, 0, 0}, {0x45, 0, 0, 0, 0}, {0x46, 0x6e, 0x6e, 0, 0}, {0x47, 0xf0, 0xf0, 1, 1}, {0x48, 0, 0, 0, 0}, {0x49, 0x2, 0x2, 0, 0}, {0x4A, 0xff, 0xff, 1, 1}, {0x4B, 0xc, 0xc, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0x38, 0x38, 0, 0}, {0x4E, 0x70, 0x70, 1, 1}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0x88, 0x88, 0, 0}, {0x51, 0xc, 0xc, 0, 0}, {0x52, 0, 0, 0, 0}, {0x53, 0x8, 0x8, 0, 0}, {0x54, 0x70, 0x70, 1, 1}, {0x55, 0x2, 0x2, 0, 0}, {0x56, 0xff, 0xff, 1, 1}, {0x57, 0, 0, 0, 0}, {0x58, 0x83, 0x83, 0, 0}, {0x59, 0x77, 0x77, 1, 1}, {0x5A, 0, 0, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x88, 0x88, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x8, 0x8, 0, 0}, {0x5F, 0x77, 0x77, 1, 1}, {0x60, 0x1, 0x1, 0, 0}, {0x61, 0, 0, 0, 0}, {0x62, 0x7, 0x7, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0x7, 0x7, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0x74, 0x74, 1, 1}, {0x68, 0, 0, 0, 0}, {0x69, 0xa, 0xa, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0x2, 0x2, 0, 0}, {0x72, 0, 0, 0, 0}, {0x73, 0, 0, 0, 0}, {0x74, 0xe, 0xe, 0, 0}, {0x75, 0xe, 0xe, 0, 0}, {0x76, 0xe, 0xe, 0, 0}, {0x77, 0x13, 0x13, 0, 0}, {0x78, 0x13, 0x13, 0, 0}, {0x79, 0x1b, 0x1b, 0, 0}, {0x7A, 0x1b, 0x1b, 0, 0}, {0x7B, 0x55, 0x55, 0, 0}, {0x7C, 0x5b, 0x5b, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_RX_2056[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0x3, 0x3, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x90, 0x90, 0, 0}, {0x24, 0x55, 0x55, 0, 0}, {0x25, 0x15, 0x15, 0, 0}, {0x26, 0x5, 0x5, 0, 0}, {0x27, 0x15, 0x15, 0, 0}, {0x28, 0x5, 0x5, 0, 0}, {0x29, 0x20, 0x20, 0, 0}, {0x2A, 0x11, 0x11, 0, 0}, {0x2B, 0x90, 0x90, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0x88, 0x88, 0, 0}, {0x2E, 0x32, 0x32, 0, 0}, {0x2F, 0x77, 0x77, 0, 0}, {0x30, 0x17, 0x17, 1, 1}, {0x31, 0xff, 0xff, 1, 1}, {0x32, 0x20, 0x20, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0x88, 0x88, 0, 0}, {0x35, 0x32, 0x32, 0, 0}, {0x36, 0x77, 0x77, 0, 0}, {0x37, 0x17, 0x17, 1, 1}, {0x38, 0xf0, 0xf0, 1, 1}, {0x39, 0x20, 0x20, 0, 0}, {0x3A, 0x8, 0x8, 0, 0}, {0x3B, 0x99, 0x99, 0, 0}, {0x3C, 0, 0, 0, 0}, {0x3D, 0x44, 0x44, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0x44, 0x44, 0, 0}, {0x40, 0xf, 0xf, 1, 1}, {0x41, 0x6, 0x6, 0, 0}, {0x42, 0x4, 0x4, 0, 0}, {0x43, 0x50, 0x50, 1, 1}, {0x44, 0x8, 0x8, 0, 0}, {0x45, 0x99, 0x99, 0, 0}, {0x46, 0, 0, 0, 0}, {0x47, 0x11, 0x11, 0, 0}, {0x48, 0, 0, 0, 0}, {0x49, 0x44, 0x44, 0, 0}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0x6, 0x6, 0, 0}, {0x4C, 0x4, 0x4, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x66, 0x66, 0, 0}, {0x50, 0x66, 0x66, 0, 0}, {0x51, 0x57, 0x57, 0, 0}, {0x52, 0x57, 0x57, 0, 0}, {0x53, 0x44, 0x44, 0, 0}, {0x54, 0, 0, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0x8, 0x8, 0, 0}, {0x57, 0x8, 0x8, 0, 0}, {0x58, 0x7, 0x7, 0, 0}, {0x59, 0x22, 0x22, 0, 0}, {0x5A, 0x22, 0x22, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x23, 0x23, 0, 0}, {0x5D, 0x7, 0x7, 0, 0}, {0x5E, 0x55, 0x55, 0, 0}, {0x5F, 0x23, 0x23, 0, 0}, {0x60, 0x41, 0x41, 0, 0}, {0x61, 0x1, 0x1, 0, 0}, {0x62, 0xa, 0xa, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0, 0, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0xc, 0xc, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0, 0, 0, 0}, {0x72, 0x22, 0x22, 0, 0}, {0x73, 0x22, 0x22, 0, 0}, {0x74, 0x2, 0x2, 0, 0}, {0x75, 0xa, 0xa, 0, 0}, {0x76, 0x1, 0x1, 0, 0}, {0x77, 0x22, 0x22, 0, 0}, {0x78, 0x30, 0x30, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_SYN_2056_A1[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0x1, 0x1, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0x60, 0x60, 0, 0}, {0x23, 0x6, 0x6, 0, 0}, {0x24, 0xc, 0xc, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0x1, 0x1, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0xd, 0xd, 0, 0}, {0x2F, 0x1f, 0x1f, 0, 0}, {0x30, 0x15, 0x15, 0, 0}, {0x31, 0xf, 0xf, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0, 0, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0, 0, 0, 0}, {0x38, 0, 0, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0, 0, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x13, 0x13, 0, 0}, {0x3D, 0xf, 0xf, 0, 0}, {0x3E, 0x18, 0x18, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x20, 0x20, 0, 0}, {0x42, 0x20, 0x20, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x77, 0x77, 0, 0}, {0x45, 0x7, 0x7, 0, 0}, {0x46, 0x1, 0x1, 0, 0}, {0x47, 0x4, 0x4, 0, 0}, {0x48, 0xf, 0xf, 0, 0}, {0x49, 0x30, 0x30, 0, 0}, {0x4A, 0x32, 0x32, 0, 0}, {0x4B, 0xd, 0xd, 0, 0}, {0x4C, 0xd, 0xd, 0, 0}, {0x4D, 0x4, 0x4, 0, 0}, {0x4E, 0x6, 0x6, 0, 0}, {0x4F, 0x1, 0x1, 0, 0}, {0x50, 0x1c, 0x1c, 0, 0}, {0x51, 0x2, 0x2, 0, 0}, {0x52, 0x2, 0x2, 0, 0}, {0x53, 0xf7, 0xf7, 1, 1}, {0x54, 0xb4, 0xb4, 0, 0}, {0x55, 0xd2, 0xd2, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0, 0, 0, 0}, {0x58, 0x4, 0x4, 0, 0}, {0x59, 0x96, 0x96, 0, 0}, {0x5A, 0x3e, 0x3e, 0, 0}, {0x5B, 0x3e, 0x3e, 0, 0}, {0x5C, 0x13, 0x13, 0, 0}, {0x5D, 0x2, 0x2, 0, 0}, {0x5E, 0, 0, 0, 0}, {0x5F, 0x7, 0x7, 0, 0}, {0x60, 0x7, 0x7, 1, 1}, {0x61, 0x8, 0x8, 0, 0}, {0x62, 0x3, 0x3, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0x40, 0x40, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0x1, 0x1, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0x60, 0x60, 0, 0}, {0x71, 0x66, 0x66, 0, 0}, {0x72, 0xc, 0xc, 0, 0}, {0x73, 0x66, 0x66, 0, 0}, {0x74, 0x8f, 0x8f, 1, 1}, {0x75, 0, 0, 0, 0}, {0x76, 0xcc, 0xcc, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0x66, 0x66, 0, 0}, {0x79, 0x66, 0x66, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0xff, 0xff, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0, 0, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0, 0, 0, 0}, {0x9A, 0, 0, 0, 0}, {0x9B, 0, 0, 0, 0}, {0x9C, 0, 0, 0, 0}, {0x9D, 0, 0, 0, 0}, {0x9E, 0, 0, 0, 0}, {0x9F, 0x6, 0x6, 0, 0}, {0xA0, 0x66, 0x66, 0, 0}, {0xA1, 0x66, 0x66, 0, 0}, {0xA2, 0x66, 0x66, 0, 0}, {0xA3, 0x66, 0x66, 0, 0}, {0xA4, 0x66, 0x66, 0, 0}, {0xA5, 0x66, 0x66, 0, 0}, {0xA6, 0x66, 0x66, 0, 0}, {0xA7, 0x66, 0x66, 0, 0}, {0xA8, 0x66, 0x66, 0, 0}, {0xA9, 0x66, 0x66, 0, 0}, {0xAA, 0x66, 0x66, 0, 0}, {0xAB, 0x66, 0x66, 0, 0}, {0xAC, 0x66, 0x66, 0, 0}, {0xAD, 0x66, 0x66, 0, 0}, {0xAE, 0x66, 0x66, 0, 0}, {0xAF, 0x66, 0x66, 0, 0}, {0xB0, 0x66, 0x66, 0, 0}, {0xB1, 0x66, 0x66, 0, 0}, {0xB2, 0x66, 0x66, 0, 0}, {0xB3, 0xa, 0xa, 0, 0}, {0xB4, 0, 0, 0, 0}, {0xB5, 0, 0, 0, 0}, {0xB6, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_TX_2056_A1[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0x88, 0x88, 0, 0}, {0x22, 0x88, 0x88, 0, 0}, {0x23, 0x88, 0x88, 0, 0}, {0x24, 0x88, 0x88, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0x3, 0x3, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x3, 0x3, 0, 0}, {0x2A, 0x37, 0x37, 0, 0}, {0x2B, 0x3, 0x3, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0x1, 0x1, 0, 0}, {0x2F, 0x1, 0x1, 0, 0}, {0x30, 0, 0, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0x11, 0x11, 0, 0}, {0x34, 0x11, 0x11, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x3, 0x3, 0, 0}, {0x38, 0xf, 0xf, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0x2d, 0x2d, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x6e, 0x6e, 0, 0}, {0x3D, 0xf0, 0xf0, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x3, 0x3, 0, 0}, {0x42, 0x3, 0x3, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x1e, 0x1e, 0, 0}, {0x45, 0, 0, 0, 0}, {0x46, 0x6e, 0x6e, 0, 0}, {0x47, 0xf0, 0xf0, 1, 1}, {0x48, 0, 0, 0, 0}, {0x49, 0x2, 0x2, 0, 0}, {0x4A, 0xff, 0xff, 1, 1}, {0x4B, 0xc, 0xc, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0x38, 0x38, 0, 0}, {0x4E, 0x70, 0x70, 1, 1}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0x88, 0x88, 0, 0}, {0x51, 0xc, 0xc, 0, 0}, {0x52, 0, 0, 0, 0}, {0x53, 0x8, 0x8, 0, 0}, {0x54, 0x70, 0x70, 1, 1}, {0x55, 0x2, 0x2, 0, 0}, {0x56, 0xff, 0xff, 1, 1}, {0x57, 0, 0, 0, 0}, {0x58, 0x83, 0x83, 0, 0}, {0x59, 0x77, 0x77, 1, 1}, {0x5A, 0, 0, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x88, 0x88, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x8, 0x8, 0, 0}, {0x5F, 0x77, 0x77, 1, 1}, {0x60, 0x1, 0x1, 0, 0}, {0x61, 0, 0, 0, 0}, {0x62, 0x7, 0x7, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0x7, 0x7, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0x72, 0x72, 1, 1}, {0x68, 0, 0, 0, 0}, {0x69, 0xa, 0xa, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0x2, 0x2, 0, 0}, {0x72, 0, 0, 0, 0}, {0x73, 0, 0, 0, 0}, {0x74, 0xe, 0xe, 0, 0}, {0x75, 0xe, 0xe, 0, 0}, {0x76, 0xe, 0xe, 0, 0}, {0x77, 0x13, 0x13, 0, 0}, {0x78, 0x13, 0x13, 0, 0}, {0x79, 0x1b, 0x1b, 0, 0}, {0x7A, 0x1b, 0x1b, 0, 0}, {0x7B, 0x55, 0x55, 0, 0}, {0x7C, 0x5b, 0x5b, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_RX_2056_A1[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0x3, 0x3, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x90, 0x90, 0, 0}, {0x24, 0x55, 0x55, 0, 0}, {0x25, 0x15, 0x15, 0, 0}, {0x26, 0x5, 0x5, 0, 0}, {0x27, 0x15, 0x15, 0, 0}, {0x28, 0x5, 0x5, 0, 0}, {0x29, 0x20, 0x20, 0, 0}, {0x2A, 0x11, 0x11, 0, 0}, {0x2B, 0x90, 0x90, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0x88, 0x88, 0, 0}, {0x2E, 0x32, 0x32, 0, 0}, {0x2F, 0x77, 0x77, 0, 0}, {0x30, 0x17, 0x17, 1, 1}, {0x31, 0xff, 0xff, 1, 1}, {0x32, 0x20, 0x20, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0x88, 0x88, 0, 0}, {0x35, 0x32, 0x32, 0, 0}, {0x36, 0x77, 0x77, 0, 0}, {0x37, 0x17, 0x17, 1, 1}, {0x38, 0xf0, 0xf0, 1, 1}, {0x39, 0x20, 0x20, 0, 0}, {0x3A, 0x8, 0x8, 0, 0}, {0x3B, 0x55, 0x55, 1, 1}, {0x3C, 0, 0, 0, 0}, {0x3D, 0x44, 0x44, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0x44, 0x44, 0, 0}, {0x40, 0xf, 0xf, 1, 1}, {0x41, 0x6, 0x6, 0, 0}, {0x42, 0x4, 0x4, 0, 0}, {0x43, 0x50, 0x50, 1, 1}, {0x44, 0x8, 0x8, 0, 0}, {0x45, 0x55, 0x55, 1, 1}, {0x46, 0, 0, 0, 0}, {0x47, 0x11, 0x11, 0, 0}, {0x48, 0, 0, 0, 0}, {0x49, 0x44, 0x44, 0, 0}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0x6, 0x6, 0, 0}, {0x4C, 0x4, 0x4, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x26, 0x26, 1, 1}, {0x50, 0x26, 0x26, 1, 1}, {0x51, 0xf, 0xf, 1, 1}, {0x52, 0xf, 0xf, 1, 1}, {0x53, 0x44, 0x44, 0, 0}, {0x54, 0, 0, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0x8, 0x8, 0, 0}, {0x57, 0x8, 0x8, 0, 0}, {0x58, 0x7, 0x7, 0, 0}, {0x59, 0x22, 0x22, 0, 0}, {0x5A, 0x22, 0x22, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x2f, 0x2f, 1, 1}, {0x5D, 0x7, 0x7, 0, 0}, {0x5E, 0x55, 0x55, 0, 0}, {0x5F, 0x23, 0x23, 0, 0}, {0x60, 0x41, 0x41, 0, 0}, {0x61, 0x1, 0x1, 0, 0}, {0x62, 0xa, 0xa, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0, 0, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0xc, 0xc, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0, 0, 0, 0}, {0x72, 0x22, 0x22, 0, 0}, {0x73, 0x22, 0x22, 0, 0}, {0x74, 0, 0, 1, 1}, {0x75, 0xa, 0xa, 0, 0}, {0x76, 0x1, 0x1, 0, 0}, {0x77, 0x22, 0x22, 0, 0}, {0x78, 0x30, 0x30, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_SYN_2056_rev5[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0x1, 0x1, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0x60, 0x60, 0, 0}, {0x23, 0x6, 0x6, 0, 0}, {0x24, 0xc, 0xc, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0x1, 0x1, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0, 0, 0, 0}, {0x2F, 0x1f, 0x1f, 0, 0}, {0x30, 0x15, 0x15, 0, 0}, {0x31, 0xf, 0xf, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0, 0, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0, 0, 0, 0}, {0x38, 0, 0, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0, 0, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x13, 0x13, 0, 0}, {0x3D, 0xf, 0xf, 0, 0}, {0x3E, 0x18, 0x18, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x20, 0x20, 0, 0}, {0x42, 0x20, 0x20, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x77, 0x77, 0, 0}, {0x45, 0x7, 0x7, 0, 0}, {0x46, 0x1, 0x1, 0, 0}, {0x47, 0x4, 0x4, 0, 0}, {0x48, 0xf, 0xf, 0, 0}, {0x49, 0x30, 0x30, 0, 0}, {0x4A, 0x32, 0x32, 0, 0}, {0x4B, 0xd, 0xd, 0, 0}, {0x4C, 0xd, 0xd, 0, 0}, {0x4D, 0x4, 0x4, 0, 0}, {0x4E, 0x6, 0x6, 0, 0}, {0x4F, 0x1, 0x1, 0, 0}, {0x50, 0x1c, 0x1c, 0, 0}, {0x51, 0x2, 0x2, 0, 0}, {0x52, 0x2, 0x2, 0, 0}, {0x53, 0xf7, 0xf7, 1, 1}, {0x54, 0xb4, 0xb4, 0, 0}, {0x55, 0xd2, 0xd2, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0, 0, 0, 0}, {0x58, 0x4, 0x4, 0, 0}, {0x59, 0x96, 0x96, 0, 0}, {0x5A, 0x3e, 0x3e, 0, 0}, {0x5B, 0x3e, 0x3e, 0, 0}, {0x5C, 0x13, 0x13, 0, 0}, {0x5D, 0x2, 0x2, 0, 0}, {0x5E, 0, 0, 0, 0}, {0x5F, 0x7, 0x7, 0, 0}, {0x60, 0x7, 0x7, 1, 1}, {0x61, 0x8, 0x8, 0, 0}, {0x62, 0x3, 0x3, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0x40, 0x40, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0x1, 0x1, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0x60, 0x60, 0, 0}, {0x71, 0x66, 0x66, 0, 0}, {0x72, 0xc, 0xc, 0, 0}, {0x73, 0x66, 0x66, 0, 0}, {0x74, 0x8f, 0x8f, 1, 1}, {0x75, 0, 0, 0, 0}, {0x76, 0xcc, 0xcc, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0x66, 0x66, 0, 0}, {0x79, 0x66, 0x66, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0xff, 0xff, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0, 0, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0, 0, 0, 0}, {0x9A, 0, 0, 0, 0}, {0x9B, 0, 0, 0, 0}, {0x9C, 0, 0, 0, 0}, {0x9D, 0, 0, 0, 0}, {0x9E, 0, 0, 0, 0}, {0x9F, 0x6, 0x6, 0, 0}, {0xA0, 0x66, 0x66, 0, 0}, {0xA1, 0x66, 0x66, 0, 0}, {0xA2, 0x66, 0x66, 0, 0}, {0xA3, 0x66, 0x66, 0, 0}, {0xA4, 0x66, 0x66, 0, 0}, {0xA5, 0x66, 0x66, 0, 0}, {0xA6, 0x66, 0x66, 0, 0}, {0xA7, 0x66, 0x66, 0, 0}, {0xA8, 0x66, 0x66, 0, 0}, {0xA9, 0x66, 0x66, 0, 0}, {0xAA, 0x66, 0x66, 0, 0}, {0xAB, 0x66, 0x66, 0, 0}, {0xAC, 0x66, 0x66, 0, 0}, {0xAD, 0x66, 0x66, 0, 0}, {0xAE, 0x66, 0x66, 0, 0}, {0xAF, 0x66, 0x66, 0, 0}, {0xB0, 0x66, 0x66, 0, 0}, {0xB1, 0x66, 0x66, 0, 0}, {0xB2, 0x66, 0x66, 0, 0}, {0xB3, 0xa, 0xa, 0, 0}, {0xB4, 0, 0, 0, 0}, {0xB5, 0, 0, 0, 0}, {0xB6, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_TX_2056_rev5[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0x88, 0x88, 0, 0}, {0x22, 0x88, 0x88, 0, 0}, {0x23, 0x88, 0x88, 0, 0}, {0x24, 0x88, 0x88, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0x3, 0x3, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x3, 0x3, 0, 0}, {0x2A, 0x37, 0x37, 0, 0}, {0x2B, 0x3, 0x3, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0x1, 0x1, 0, 0}, {0x2F, 0x1, 0x1, 0, 0}, {0x30, 0, 0, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0x11, 0x11, 0, 0}, {0x34, 0x11, 0x11, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x3, 0x3, 0, 0}, {0x38, 0xf, 0xf, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0x2d, 0x2d, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x6e, 0x6e, 0, 0}, {0x3D, 0xf0, 0xf0, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x3, 0x3, 0, 0}, {0x42, 0x3, 0x3, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x1e, 0x1e, 0, 0}, {0x45, 0, 0, 0, 0}, {0x46, 0x6e, 0x6e, 0, 0}, {0x47, 0xf0, 0xf0, 1, 1}, {0x48, 0, 0, 0, 0}, {0x49, 0x2, 0x2, 0, 0}, {0x4A, 0xff, 0xff, 1, 1}, {0x4B, 0xc, 0xc, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0x38, 0x38, 0, 0}, {0x4E, 0x70, 0x70, 1, 1}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0x88, 0x88, 0, 0}, {0x51, 0xc, 0xc, 0, 0}, {0x52, 0, 0, 0, 0}, {0x53, 0x8, 0x8, 0, 0}, {0x54, 0x70, 0x70, 1, 1}, {0x55, 0x2, 0x2, 0, 0}, {0x56, 0xff, 0xff, 1, 1}, {0x57, 0, 0, 0, 0}, {0x58, 0x83, 0x83, 0, 0}, {0x59, 0x77, 0x77, 1, 1}, {0x5A, 0, 0, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x88, 0x88, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x8, 0x8, 0, 0}, {0x5F, 0x77, 0x77, 1, 1}, {0x60, 0x1, 0x1, 0, 0}, {0x61, 0, 0, 0, 0}, {0x62, 0x7, 0x7, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0x7, 0x7, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 1, 1}, {0x68, 0, 0, 0, 0}, {0x69, 0xa, 0xa, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0x2, 0x2, 0, 0}, {0x72, 0, 0, 0, 0}, {0x73, 0, 0, 0, 0}, {0x74, 0xe, 0xe, 0, 0}, {0x75, 0xe, 0xe, 0, 0}, {0x76, 0xe, 0xe, 0, 0}, {0x77, 0x13, 0x13, 0, 0}, {0x78, 0x13, 0x13, 0, 0}, {0x79, 0x1b, 0x1b, 0, 0}, {0x7A, 0x1b, 0x1b, 0, 0}, {0x7B, 0x55, 0x55, 0, 0}, {0x7C, 0x5b, 0x5b, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0x70, 0x70, 0, 0}, {0x94, 0x70, 0x70, 0, 0}, {0x95, 0x71, 0x71, 1, 1}, {0x96, 0x71, 0x71, 1, 1}, {0x97, 0x72, 0x72, 1, 1}, {0x98, 0x73, 0x73, 1, 1}, {0x99, 0x74, 0x74, 1, 1}, {0x9A, 0x75, 0x75, 1, 1}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_RX_2056_rev5[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0x3, 0x3, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x90, 0x90, 0, 0}, {0x24, 0x55, 0x55, 0, 0}, {0x25, 0x15, 0x15, 0, 0}, {0x26, 0x5, 0x5, 0, 0}, {0x27, 0x15, 0x15, 0, 0}, {0x28, 0x5, 0x5, 0, 0}, {0x29, 0x20, 0x20, 0, 0}, {0x2A, 0x11, 0x11, 0, 0}, {0x2B, 0x90, 0x90, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0x88, 0x88, 0, 0}, {0x2E, 0x32, 0x32, 0, 0}, {0x2F, 0x77, 0x77, 0, 0}, {0x30, 0x17, 0x17, 1, 1}, {0x31, 0xff, 0xff, 1, 1}, {0x32, 0x20, 0x20, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0x88, 0x88, 0, 0}, {0x35, 0x32, 0x32, 0, 0}, {0x36, 0x77, 0x77, 0, 0}, {0x37, 0x17, 0x17, 1, 1}, {0x38, 0xf0, 0xf0, 1, 1}, {0x39, 0x20, 0x20, 0, 0}, {0x3A, 0x8, 0x8, 0, 0}, {0x3B, 0x55, 0x55, 1, 1}, {0x3C, 0, 0, 0, 0}, {0x3D, 0x88, 0x88, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 1, 1}, {0x40, 0x7, 0x7, 1, 1}, {0x41, 0x6, 0x6, 0, 0}, {0x42, 0x4, 0x4, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x8, 0x8, 0, 0}, {0x45, 0x55, 0x55, 1, 1}, {0x46, 0, 0, 0, 0}, {0x47, 0x11, 0x11, 0, 0}, {0x48, 0, 0, 0, 0}, {0x49, 0, 0, 1, 1}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0x6, 0x6, 0, 0}, {0x4C, 0x4, 0x4, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x26, 0x26, 1, 1}, {0x50, 0x26, 0x26, 1, 1}, {0x51, 0xf, 0xf, 1, 1}, {0x52, 0xf, 0xf, 1, 1}, {0x53, 0x44, 0x44, 0, 0}, {0x54, 0, 0, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0x8, 0x8, 0, 0}, {0x57, 0x8, 0x8, 0, 0}, {0x58, 0x7, 0x7, 0, 0}, {0x59, 0x22, 0x22, 0, 0}, {0x5A, 0x22, 0x22, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x4, 0x4, 1, 1}, {0x5D, 0x7, 0x7, 0, 0}, {0x5E, 0x55, 0x55, 0, 0}, {0x5F, 0x23, 0x23, 0, 0}, {0x60, 0x41, 0x41, 0, 0}, {0x61, 0x1, 0x1, 0, 0}, {0x62, 0xa, 0xa, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0, 0, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0xc, 0xc, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0, 0, 0, 0}, {0x72, 0x22, 0x22, 0, 0}, {0x73, 0x22, 0x22, 0, 0}, {0x74, 0, 0, 1, 1}, {0x75, 0xa, 0xa, 0, 0}, {0x76, 0x1, 0x1, 0, 0}, {0x77, 0x22, 0x22, 0, 0}, {0x78, 0x30, 0x30, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_SYN_2056_rev6[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0x1, 0x1, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0x60, 0x60, 0, 0}, {0x23, 0x6, 0x6, 0, 0}, {0x24, 0xc, 0xc, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0x1, 0x1, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0, 0, 0, 0}, {0x2F, 0x1f, 0x1f, 0, 0}, {0x30, 0x15, 0x15, 0, 0}, {0x31, 0xf, 0xf, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0, 0, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0, 0, 0, 0}, {0x38, 0, 0, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0, 0, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x13, 0x13, 0, 0}, {0x3D, 0xf, 0xf, 0, 0}, {0x3E, 0x18, 0x18, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x20, 0x20, 0, 0}, {0x42, 0x20, 0x20, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x77, 0x77, 0, 0}, {0x45, 0x7, 0x7, 0, 0}, {0x46, 0x1, 0x1, 0, 0}, {0x47, 0x4, 0x4, 0, 0}, {0x48, 0xf, 0xf, 0, 0}, {0x49, 0x30, 0x30, 0, 0}, {0x4A, 0x32, 0x32, 0, 0}, {0x4B, 0xd, 0xd, 0, 0}, {0x4C, 0xd, 0xd, 0, 0}, {0x4D, 0x4, 0x4, 0, 0}, {0x4E, 0x6, 0x6, 0, 0}, {0x4F, 0x1, 0x1, 0, 0}, {0x50, 0x1c, 0x1c, 0, 0}, {0x51, 0x2, 0x2, 0, 0}, {0x52, 0x2, 0x2, 0, 0}, {0x53, 0xf7, 0xf7, 1, 1}, {0x54, 0xb4, 0xb4, 0, 0}, {0x55, 0xd2, 0xd2, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0, 0, 0, 0}, {0x58, 0x4, 0x4, 0, 0}, {0x59, 0x96, 0x96, 0, 0}, {0x5A, 0x3e, 0x3e, 0, 0}, {0x5B, 0x3e, 0x3e, 0, 0}, {0x5C, 0x13, 0x13, 0, 0}, {0x5D, 0x2, 0x2, 0, 0}, {0x5E, 0, 0, 0, 0}, {0x5F, 0x7, 0x7, 0, 0}, {0x60, 0x7, 0x7, 1, 1}, {0x61, 0x8, 0x8, 0, 0}, {0x62, 0x3, 0x3, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0x40, 0x40, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0x1, 0x1, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0x60, 0x60, 0, 0}, {0x71, 0x66, 0x66, 0, 0}, {0x72, 0xc, 0xc, 0, 0}, {0x73, 0x66, 0x66, 0, 0}, {0x74, 0x8f, 0x8f, 1, 1}, {0x75, 0, 0, 0, 0}, {0x76, 0xcc, 0xcc, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0x66, 0x66, 0, 0}, {0x79, 0x66, 0x66, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0xff, 0xff, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0, 0, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0, 0, 0, 0}, {0x9A, 0, 0, 0, 0}, {0x9B, 0, 0, 0, 0}, {0x9C, 0, 0, 0, 0}, {0x9D, 0, 0, 0, 0}, {0x9E, 0, 0, 0, 0}, {0x9F, 0x6, 0x6, 0, 0}, {0xA0, 0x66, 0x66, 0, 0}, {0xA1, 0x66, 0x66, 0, 0}, {0xA2, 0x66, 0x66, 0, 0}, {0xA3, 0x66, 0x66, 0, 0}, {0xA4, 0x66, 0x66, 0, 0}, {0xA5, 0x66, 0x66, 0, 0}, {0xA6, 0x66, 0x66, 0, 0}, {0xA7, 0x66, 0x66, 0, 0}, {0xA8, 0x66, 0x66, 0, 0}, {0xA9, 0x66, 0x66, 0, 0}, {0xAA, 0x66, 0x66, 0, 0}, {0xAB, 0x66, 0x66, 0, 0}, {0xAC, 0x66, 0x66, 0, 0}, {0xAD, 0x66, 0x66, 0, 0}, {0xAE, 0x66, 0x66, 0, 0}, {0xAF, 0x66, 0x66, 0, 0}, {0xB0, 0x66, 0x66, 0, 0}, {0xB1, 0x66, 0x66, 0, 0}, {0xB2, 0x66, 0x66, 0, 0}, {0xB3, 0xa, 0xa, 0, 0}, {0xB4, 0, 0, 0, 0}, {0xB5, 0, 0, 0, 0}, {0xB6, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_TX_2056_rev6[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0x88, 0x88, 0, 0}, {0x22, 0x88, 0x88, 0, 0}, {0x23, 0x88, 0x88, 0, 0}, {0x24, 0x88, 0x88, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0x3, 0x3, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x3, 0x3, 0, 0}, {0x2A, 0x37, 0x37, 0, 0}, {0x2B, 0x3, 0x3, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0x1, 0x1, 0, 0}, {0x2F, 0x1, 0x1, 0, 0}, {0x30, 0, 0, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0x11, 0x11, 0, 0}, {0x34, 0xee, 0xee, 1, 1}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x3, 0x3, 0, 0}, {0x38, 0x50, 0x50, 1, 1}, {0x39, 0, 0, 0, 0}, {0x3A, 0x50, 0x50, 1, 1}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x6e, 0x6e, 0, 0}, {0x3D, 0xf0, 0xf0, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x3, 0x3, 0, 0}, {0x42, 0x3, 0x3, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x1e, 0x1e, 0, 0}, {0x45, 0, 0, 0, 0}, {0x46, 0x6e, 0x6e, 0, 0}, {0x47, 0xf0, 0xf0, 1, 1}, {0x48, 0, 0, 0, 0}, {0x49, 0x2, 0x2, 0, 0}, {0x4A, 0xff, 0xff, 1, 1}, {0x4B, 0xc, 0xc, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0x38, 0x38, 0, 0}, {0x4E, 0x70, 0x70, 1, 1}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0x88, 0x88, 0, 0}, {0x51, 0xc, 0xc, 0, 0}, {0x52, 0, 0, 0, 0}, {0x53, 0x8, 0x8, 0, 0}, {0x54, 0x70, 0x70, 1, 1}, {0x55, 0x2, 0x2, 0, 0}, {0x56, 0xff, 0xff, 1, 1}, {0x57, 0, 0, 0, 0}, {0x58, 0x83, 0x83, 0, 0}, {0x59, 0x77, 0x77, 1, 1}, {0x5A, 0, 0, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x88, 0x88, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x8, 0x8, 0, 0}, {0x5F, 0x77, 0x77, 1, 1}, {0x60, 0x1, 0x1, 0, 0}, {0x61, 0, 0, 0, 0}, {0x62, 0x7, 0x7, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0x7, 0x7, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 1, 1}, {0x68, 0, 0, 0, 0}, {0x69, 0xa, 0xa, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0x2, 0x2, 0, 0}, {0x72, 0, 0, 0, 0}, {0x73, 0, 0, 0, 0}, {0x74, 0xe, 0xe, 0, 0}, {0x75, 0xe, 0xe, 0, 0}, {0x76, 0xe, 0xe, 0, 0}, {0x77, 0x13, 0x13, 0, 0}, {0x78, 0x13, 0x13, 0, 0}, {0x79, 0x1b, 0x1b, 0, 0}, {0x7A, 0x1b, 0x1b, 0, 0}, {0x7B, 0x55, 0x55, 0, 0}, {0x7C, 0x5b, 0x5b, 0, 0}, {0x7D, 0x30, 0x30, 1, 1}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0x70, 0x70, 0, 0}, {0x94, 0x70, 0x70, 0, 0}, {0x95, 0x70, 0x70, 0, 0}, {0x96, 0x70, 0x70, 0, 0}, {0x97, 0x70, 0x70, 0, 0}, {0x98, 0x70, 0x70, 0, 0}, {0x99, 0x70, 0x70, 0, 0}, {0x9A, 0x70, 0x70, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_RX_2056_rev6[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0x3, 0x3, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x90, 0x90, 0, 0}, {0x24, 0x55, 0x55, 0, 0}, {0x25, 0x15, 0x15, 0, 0}, {0x26, 0x5, 0x5, 0, 0}, {0x27, 0x15, 0x15, 0, 0}, {0x28, 0x5, 0x5, 0, 0}, {0x29, 0x20, 0x20, 0, 0}, {0x2A, 0x11, 0x11, 0, 0}, {0x2B, 0x90, 0x90, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0x88, 0x88, 0, 0}, {0x2E, 0x32, 0x32, 0, 0}, {0x2F, 0x77, 0x77, 0, 0}, {0x30, 0x17, 0x17, 1, 1}, {0x31, 0xff, 0xff, 1, 1}, {0x32, 0x20, 0x20, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0x88, 0x88, 0, 0}, {0x35, 0x32, 0x32, 0, 0}, {0x36, 0x77, 0x77, 0, 0}, {0x37, 0x17, 0x17, 1, 1}, {0x38, 0xf0, 0xf0, 1, 1}, {0x39, 0x20, 0x20, 0, 0}, {0x3A, 0x8, 0x8, 0, 0}, {0x3B, 0x55, 0x55, 1, 1}, {0x3C, 0, 0, 0, 0}, {0x3D, 0x88, 0x88, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0x44, 0x44, 0, 0}, {0x40, 0x7, 0x7, 1, 1}, {0x41, 0x6, 0x6, 0, 0}, {0x42, 0x4, 0x4, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x8, 0x8, 0, 0}, {0x45, 0x55, 0x55, 1, 1}, {0x46, 0, 0, 0, 0}, {0x47, 0x11, 0x11, 0, 0}, {0x48, 0, 0, 0, 0}, {0x49, 0x44, 0x44, 0, 0}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0x6, 0x6, 0, 0}, {0x4C, 0x4, 0x4, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x26, 0x26, 1, 1}, {0x50, 0x26, 0x26, 1, 1}, {0x51, 0xf, 0xf, 1, 1}, {0x52, 0xf, 0xf, 1, 1}, {0x53, 0x44, 0x44, 0, 0}, {0x54, 0, 0, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0x8, 0x8, 0, 0}, {0x57, 0x8, 0x8, 0, 0}, {0x58, 0x7, 0x7, 0, 0}, {0x59, 0x22, 0x22, 0, 0}, {0x5A, 0x22, 0x22, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x4, 0x4, 1, 1}, {0x5D, 0x7, 0x7, 0, 0}, {0x5E, 0x55, 0x55, 0, 0}, {0x5F, 0x23, 0x23, 0, 0}, {0x60, 0x41, 0x41, 0, 0}, {0x61, 0x1, 0x1, 0, 0}, {0x62, 0xa, 0xa, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0, 0, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0xc, 0xc, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0, 0, 0, 0}, {0x72, 0x22, 0x22, 0, 0}, {0x73, 0x22, 0x22, 0, 0}, {0x74, 0, 0, 1, 1}, {0x75, 0xa, 0xa, 0, 0}, {0x76, 0x1, 0x1, 0, 0}, {0x77, 0x22, 0x22, 0, 0}, {0x78, 0x30, 0x30, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0x5, 0x5, 1, 1}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; static struct radio_regs regs_SYN_2056_rev7[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0x1, 0x1, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0x60, 0x60, 0, 0}, {0x23, 0x6, 0x6, 0, 0}, {0x24, 0xc, 0xc, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0x1, 0x1, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0, 0, 0, 0}, {0x2F, 0x1f, 0x1f, 0, 0}, {0x30, 0x15, 0x15, 0, 0}, {0x31, 0xf, 0xf, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0, 0, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0, 0, 0, 0}, {0x38, 0, 0, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0, 0, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x13, 0x13, 0, 0}, {0x3D, 0xf, 0xf, 0, 0}, {0x3E, 0x18, 0x18, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x20, 0x20, 0, 0}, {0x42, 0x20, 0x20, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x77, 0x77, 0, 0}, {0x45, 0x7, 0x7, 0, 0}, {0x46, 0x1, 0x1, 0, 0}, {0x47, 0x4, 0x4, 0, 0}, {0x48, 0xf, 0xf, 0, 0}, {0x49, 0x30, 0x30, 0, 0}, {0x4A, 0x32, 0x32, 0, 0}, {0x4B, 0xd, 0xd, 0, 0}, {0x4C, 0xd, 0xd, 0, 0}, {0x4D, 0x4, 0x4, 0, 0}, {0x4E, 0x6, 0x6, 0, 0}, {0x4F, 0x1, 0x1, 0, 0}, {0x50, 0x1c, 0x1c, 0, 0}, {0x51, 0x2, 0x2, 0, 0}, {0x52, 0x2, 0x2, 0, 0}, {0x53, 0xf7, 0xf7, 1, 1}, {0x54, 0xb4, 0xb4, 0, 0}, {0x55, 0xd2, 0xd2, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0, 0, 0, 0}, {0x58, 0x4, 0x4, 0, 0}, {0x59, 0x96, 0x96, 0, 0}, {0x5A, 0x3e, 0x3e, 0, 0}, {0x5B, 0x3e, 0x3e, 0, 0}, {0x5C, 0x13, 0x13, 0, 0}, {0x5D, 0x2, 0x2, 0, 0}, {0x5E, 0, 0, 0, 0}, {0x5F, 0x7, 0x7, 0, 0}, {0x60, 0x7, 0x7, 1, 1}, {0x61, 0x8, 0x8, 0, 0}, {0x62, 0x3, 0x3, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0x40, 0x40, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0x1, 0x1, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0x60, 0x60, 0, 0}, {0x71, 0x66, 0x66, 0, 0}, {0x72, 0xc, 0xc, 0, 0}, {0x73, 0x66, 0x66, 0, 0}, {0x74, 0x8f, 0x8f, 1, 1}, {0x75, 0, 0, 0, 0}, {0x76, 0xcc, 0xcc, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0x66, 0x66, 0, 0}, {0x79, 0x66, 0x66, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0xff, 0xff, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0, 0, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0, 0, 0, 0}, {0x9A, 0, 0, 0, 0}, {0x9B, 0, 0, 0, 0}, {0x9C, 0, 0, 0, 0}, {0x9D, 0, 0, 0, 0}, {0x9E, 0, 0, 0, 0}, {0x9F, 0x6, 0x6, 0, 0}, {0xA0, 0x66, 0x66, 0, 0}, {0xA1, 0x66, 0x66, 0, 0}, {0xA2, 0x66, 0x66, 0, 0}, {0xA3, 0x66, 0x66, 0, 0}, {0xA4, 0x66, 0x66, 0, 0}, {0xA5, 0x66, 0x66, 0, 0}, {0xA6, 0x66, 0x66, 0, 0}, {0xA7, 0x66, 0x66, 0, 0}, {0xA8, 0x66, 0x66, 0, 0}, {0xA9, 0x66, 0x66, 0, 0}, {0xAA, 0x66, 0x66, 0, 0}, {0xAB, 0x66, 0x66, 0, 0}, {0xAC, 0x66, 0x66, 0, 0}, {0xAD, 0x66, 0x66, 0, 0}, {0xAE, 0x66, 0x66, 0, 0}, {0xAF, 0x66, 0x66, 0, 0}, {0xB0, 0x66, 0x66, 0, 0}, {0xB1, 0x66, 0x66, 0, 0}, {0xB2, 0x66, 0x66, 0, 0}, {0xB3, 0xa, 0xa, 0, 0}, {0xB4, 0, 0, 0, 0}, {0xB5, 0, 0, 0, 0}, {0xB6, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static struct radio_regs regs_TX_2056_rev7[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0x88, 0x88, 0, 0}, {0x22, 0x88, 0x88, 0, 0}, {0x23, 0x88, 0x88, 0, 0}, {0x24, 0x88, 0x88, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0x3, 0x3, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x3, 0x3, 0, 0}, {0x2A, 0x37, 0x37, 0, 0}, {0x2B, 0x3, 0x3, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0x1, 0x1, 0, 0}, {0x2F, 0x1, 0x1, 0, 0}, {0x30, 0, 0, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0x11, 0x11, 0, 0}, {0x34, 0xee, 0xee, 1, 1}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x3, 0x3, 0, 0}, {0x38, 0x50, 0x50, 1, 1}, {0x39, 0, 0, 0, 0}, {0x3A, 0x50, 0x50, 1, 1}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x6e, 0x6e, 0, 0}, {0x3D, 0xf0, 0xf0, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x3, 0x3, 0, 0}, {0x42, 0x3, 0x3, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x1e, 0x1e, 0, 0}, {0x45, 0, 0, 0, 0}, {0x46, 0x6e, 0x6e, 0, 0}, {0x47, 0xf0, 0xf0, 1, 1}, {0x48, 0, 0, 0, 0}, {0x49, 0x2, 0x2, 0, 0}, {0x4A, 0xff, 0xff, 1, 1}, {0x4B, 0xc, 0xc, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0x38, 0x38, 0, 0}, {0x4E, 0x70, 0x70, 1, 1}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0x88, 0x88, 0, 0}, {0x51, 0xc, 0xc, 0, 0}, {0x52, 0, 0, 0, 0}, {0x53, 0x8, 0x8, 0, 0}, {0x54, 0x70, 0x70, 1, 1}, {0x55, 0x2, 0x2, 0, 0}, {0x56, 0xff, 0xff, 1, 1}, {0x57, 0, 0, 0, 0}, {0x58, 0x83, 0x83, 0, 0}, {0x59, 0x77, 0x77, 1, 1}, {0x5A, 0, 0, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x88, 0x88, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x8, 0x8, 0, 0}, {0x5F, 0x77, 0x77, 1, 1}, {0x60, 0x1, 0x1, 0, 0}, {0x61, 0, 0, 0, 0}, {0x62, 0x7, 0x7, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0x7, 0x7, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 1, 1}, {0x68, 0, 0, 0, 0}, {0x69, 0xa, 0xa, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0x2, 0x2, 0, 0}, {0x72, 0, 0, 0, 0}, {0x73, 0, 0, 0, 0}, {0x74, 0xe, 0xe, 0, 0}, {0x75, 0xe, 0xe, 0, 0}, {0x76, 0xe, 0xe, 0, 0}, {0x77, 0x13, 0x13, 0, 0}, {0x78, 0x13, 0x13, 0, 0}, {0x79, 0x1b, 0x1b, 0, 0}, {0x7A, 0x1b, 0x1b, 0, 0}, {0x7B, 0x55, 0x55, 0, 0}, {0x7C, 0x5b, 0x5b, 0, 0}, {0x7D, 0x30, 0x30, 1, 1}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0x70, 0x70, 0, 0}, {0x94, 0x70, 0x70, 0, 0}, {0x95, 0x71, 0x71, 1, 1}, {0x96, 0x71, 0x71, 1, 1}, {0x97, 0x72, 0x72, 1, 1}, {0x98, 0x73, 0x73, 1, 1}, {0x99, 0x74, 0x74, 1, 1}, {0x9A, 0x75, 0x75, 1, 1}, {0xFFFF, 0, 0, 0, 0}, }; static struct radio_regs regs_RX_2056_rev7[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0x3, 0x3, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x90, 0x90, 0, 0}, {0x24, 0x55, 0x55, 0, 0}, {0x25, 0x15, 0x15, 0, 0}, {0x26, 0x5, 0x5, 0, 0}, {0x27, 0x15, 0x15, 0, 0}, {0x28, 0x5, 0x5, 0, 0}, {0x29, 0x20, 0x20, 0, 0}, {0x2A, 0x11, 0x11, 0, 0}, {0x2B, 0x90, 0x90, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0x88, 0x88, 0, 0}, {0x2E, 0x32, 0x32, 0, 0}, {0x2F, 0x77, 0x77, 0, 0}, {0x30, 0x17, 0x17, 1, 1}, {0x31, 0xff, 0xff, 1, 1}, {0x32, 0x20, 0x20, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0x88, 0x88, 0, 0}, {0x35, 0x32, 0x32, 0, 0}, {0x36, 0x77, 0x77, 0, 0}, {0x37, 0x17, 0x17, 1, 1}, {0x38, 0xf0, 0xf0, 1, 1}, {0x39, 0x20, 0x20, 0, 0}, {0x3A, 0x8, 0x8, 0, 0}, {0x3B, 0x55, 0x55, 1, 1}, {0x3C, 0, 0, 0, 0}, {0x3D, 0x88, 0x88, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 1, 1}, {0x40, 0x7, 0x7, 1, 1}, {0x41, 0x6, 0x6, 0, 0}, {0x42, 0x4, 0x4, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x8, 0x8, 0, 0}, {0x45, 0x55, 0x55, 1, 1}, {0x46, 0, 0, 0, 0}, {0x47, 0x11, 0x11, 0, 0}, {0x48, 0, 0, 0, 0}, {0x49, 0, 0, 1, 1}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0x6, 0x6, 0, 0}, {0x4C, 0x4, 0x4, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x26, 0x26, 1, 1}, {0x50, 0x26, 0x26, 1, 1}, {0x51, 0xf, 0xf, 1, 1}, {0x52, 0xf, 0xf, 1, 1}, {0x53, 0x44, 0x44, 0, 0}, {0x54, 0, 0, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0x8, 0x8, 0, 0}, {0x57, 0x8, 0x8, 0, 0}, {0x58, 0x7, 0x7, 0, 0}, {0x59, 0x22, 0x22, 0, 0}, {0x5A, 0x22, 0x22, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x4, 0x4, 1, 1}, {0x5D, 0x7, 0x7, 0, 0}, {0x5E, 0x55, 0x55, 0, 0}, {0x5F, 0x23, 0x23, 0, 0}, {0x60, 0x41, 0x41, 0, 0}, {0x61, 0x1, 0x1, 0, 0}, {0x62, 0xa, 0xa, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0, 0, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0xc, 0xc, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0, 0, 0, 0}, {0x72, 0x22, 0x22, 0, 0}, {0x73, 0x22, 0x22, 0, 0}, {0x74, 0, 0, 1, 1}, {0x75, 0xa, 0xa, 0, 0}, {0x76, 0x1, 0x1, 0, 0}, {0x77, 0x22, 0x22, 0, 0}, {0x78, 0x30, 0x30, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static struct radio_regs regs_SYN_2056_rev8[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0x1, 0x1, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0x60, 0x60, 0, 0}, {0x23, 0x6, 0x6, 0, 0}, {0x24, 0xc, 0xc, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0x1, 0x1, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0, 0, 0, 0}, {0x2F, 0x1f, 0x1f, 0, 0}, {0x30, 0x15, 0x15, 0, 0}, {0x31, 0xf, 0xf, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0, 0, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0, 0, 0, 0}, {0x38, 0, 0, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0, 0, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x13, 0x13, 0, 0}, {0x3D, 0xf, 0xf, 0, 0}, {0x3E, 0x18, 0x18, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x20, 0x20, 0, 0}, {0x42, 0x20, 0x20, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x77, 0x77, 0, 0}, {0x45, 0x7, 0x7, 0, 0}, {0x46, 0x1, 0x1, 0, 0}, {0x47, 0x4, 0x4, 0, 0}, {0x48, 0xf, 0xf, 0, 0}, {0x49, 0x30, 0x30, 0, 0}, {0x4A, 0x32, 0x32, 0, 0}, {0x4B, 0xd, 0xd, 0, 0}, {0x4C, 0xd, 0xd, 0, 0}, {0x4D, 0x4, 0x4, 0, 0}, {0x4E, 0x6, 0x6, 0, 0}, {0x4F, 0x1, 0x1, 0, 0}, {0x50, 0x1c, 0x1c, 0, 0}, {0x51, 0x2, 0x2, 0, 0}, {0x52, 0x2, 0x2, 0, 0}, {0x53, 0xf7, 0xf7, 1, 1}, {0x54, 0xb4, 0xb4, 0, 0}, {0x55, 0xd2, 0xd2, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0, 0, 0, 0}, {0x58, 0x4, 0x4, 0, 0}, {0x59, 0x96, 0x96, 0, 0}, {0x5A, 0x3e, 0x3e, 0, 0}, {0x5B, 0x3e, 0x3e, 0, 0}, {0x5C, 0x13, 0x13, 0, 0}, {0x5D, 0x2, 0x2, 0, 0}, {0x5E, 0, 0, 0, 0}, {0x5F, 0x7, 0x7, 0, 0}, {0x60, 0x7, 0x7, 1, 1}, {0x61, 0x8, 0x8, 0, 0}, {0x62, 0x3, 0x3, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0x40, 0x40, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0x1, 0x1, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0x60, 0x60, 0, 0}, {0x71, 0x66, 0x66, 0, 0}, {0x72, 0xc, 0xc, 0, 0}, {0x73, 0x66, 0x66, 0, 0}, {0x74, 0x8f, 0x8f, 1, 1}, {0x75, 0, 0, 0, 0}, {0x76, 0xcc, 0xcc, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0x66, 0x66, 0, 0}, {0x79, 0x66, 0x66, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0xff, 0xff, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0, 0, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0, 0, 0, 0}, {0x9A, 0, 0, 0, 0}, {0x9B, 0, 0, 0, 0}, {0x9C, 0, 0, 0, 0}, {0x9D, 0, 0, 0, 0}, {0x9E, 0, 0, 0, 0}, {0x9F, 0x6, 0x6, 0, 0}, {0xA0, 0x66, 0x66, 0, 0}, {0xA1, 0x66, 0x66, 0, 0}, {0xA2, 0x66, 0x66, 0, 0}, {0xA3, 0x66, 0x66, 0, 0}, {0xA4, 0x66, 0x66, 0, 0}, {0xA5, 0x66, 0x66, 0, 0}, {0xA6, 0x66, 0x66, 0, 0}, {0xA7, 0x66, 0x66, 0, 0}, {0xA8, 0x66, 0x66, 0, 0}, {0xA9, 0x66, 0x66, 0, 0}, {0xAA, 0x66, 0x66, 0, 0}, {0xAB, 0x66, 0x66, 0, 0}, {0xAC, 0x66, 0x66, 0, 0}, {0xAD, 0x66, 0x66, 0, 0}, {0xAE, 0x66, 0x66, 0, 0}, {0xAF, 0x66, 0x66, 0, 0}, {0xB0, 0x66, 0x66, 0, 0}, {0xB1, 0x66, 0x66, 0, 0}, {0xB2, 0x66, 0x66, 0, 0}, {0xB3, 0xa, 0xa, 0, 0}, {0xB4, 0, 0, 0, 0}, {0xB5, 0, 0, 0, 0}, {0xB6, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static struct radio_regs regs_TX_2056_rev8[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0x88, 0x88, 0, 0}, {0x22, 0x88, 0x88, 0, 0}, {0x23, 0x88, 0x88, 0, 0}, {0x24, 0x88, 0x88, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0x3, 0x3, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x3, 0x3, 0, 0}, {0x2A, 0x37, 0x37, 0, 0}, {0x2B, 0x3, 0x3, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0x1, 0x1, 0, 0}, {0x2F, 0x1, 0x1, 0, 0}, {0x30, 0, 0, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0x11, 0x11, 0, 0}, {0x34, 0xee, 0xee, 1, 1}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x3, 0x3, 0, 0}, {0x38, 0x50, 0x50, 1, 1}, {0x39, 0, 0, 0, 0}, {0x3A, 0x50, 0x50, 1, 1}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x6e, 0x6e, 0, 0}, {0x3D, 0xf0, 0xf0, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x3, 0x3, 0, 0}, {0x42, 0x3, 0x3, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x1e, 0x1e, 0, 0}, {0x45, 0, 0, 0, 0}, {0x46, 0x6e, 0x6e, 0, 0}, {0x47, 0xf0, 0xf0, 1, 1}, {0x48, 0, 0, 0, 0}, {0x49, 0x2, 0x2, 0, 0}, {0x4A, 0xff, 0xff, 1, 1}, {0x4B, 0xc, 0xc, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0x38, 0x38, 0, 0}, {0x4E, 0x70, 0x70, 1, 1}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0x88, 0x88, 0, 0}, {0x51, 0xc, 0xc, 0, 0}, {0x52, 0, 0, 0, 0}, {0x53, 0x8, 0x8, 0, 0}, {0x54, 0x70, 0x70, 1, 1}, {0x55, 0x2, 0x2, 0, 0}, {0x56, 0xff, 0xff, 1, 1}, {0x57, 0, 0, 0, 0}, {0x58, 0x83, 0x83, 0, 0}, {0x59, 0x77, 0x77, 1, 1}, {0x5A, 0, 0, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x88, 0x88, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x8, 0x8, 0, 0}, {0x5F, 0x77, 0x77, 1, 1}, {0x60, 0x1, 0x1, 0, 0}, {0x61, 0, 0, 0, 0}, {0x62, 0x7, 0x7, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0x7, 0x7, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 1, 1}, {0x68, 0, 0, 0, 0}, {0x69, 0xa, 0xa, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0x2, 0x2, 0, 0}, {0x72, 0, 0, 0, 0}, {0x73, 0, 0, 0, 0}, {0x74, 0xe, 0xe, 0, 0}, {0x75, 0xe, 0xe, 0, 0}, {0x76, 0xe, 0xe, 0, 0}, {0x77, 0x13, 0x13, 0, 0}, {0x78, 0x13, 0x13, 0, 0}, {0x79, 0x1b, 0x1b, 0, 0}, {0x7A, 0x1b, 0x1b, 0, 0}, {0x7B, 0x55, 0x55, 0, 0}, {0x7C, 0x5b, 0x5b, 0, 0}, {0x7D, 0x30, 0x30, 1, 1}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0x70, 0x70, 0, 0}, {0x94, 0x70, 0x70, 0, 0}, {0x95, 0x70, 0x70, 0, 0}, {0x96, 0x70, 0x70, 0, 0}, {0x97, 0x70, 0x70, 0, 0}, {0x98, 0x70, 0x70, 0, 0}, {0x99, 0x70, 0x70, 0, 0}, {0x9A, 0x70, 0x70, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static struct radio_regs regs_RX_2056_rev8[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0x3, 0x3, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x90, 0x90, 0, 0}, {0x24, 0x55, 0x55, 0, 0}, {0x25, 0x15, 0x15, 0, 0}, {0x26, 0x5, 0x5, 0, 0}, {0x27, 0x15, 0x15, 0, 0}, {0x28, 0x5, 0x5, 0, 0}, {0x29, 0x20, 0x20, 0, 0}, {0x2A, 0x11, 0x11, 0, 0}, {0x2B, 0x90, 0x90, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0x88, 0x88, 0, 0}, {0x2E, 0x32, 0x32, 0, 0}, {0x2F, 0x77, 0x77, 0, 0}, {0x30, 0x17, 0x17, 1, 1}, {0x31, 0xff, 0xff, 1, 1}, {0x32, 0x20, 0x20, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0x88, 0x88, 0, 0}, {0x35, 0x32, 0x32, 0, 0}, {0x36, 0x77, 0x77, 0, 0}, {0x37, 0x17, 0x17, 1, 1}, {0x38, 0xf0, 0xf0, 1, 1}, {0x39, 0x20, 0x20, 0, 0}, {0x3A, 0x8, 0x8, 0, 0}, {0x3B, 0x55, 0x55, 1, 1}, {0x3C, 0, 0, 0, 0}, {0x3D, 0x88, 0x88, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0x44, 0x44, 0, 0}, {0x40, 0x7, 0x7, 1, 1}, {0x41, 0x6, 0x6, 0, 0}, {0x42, 0x4, 0x4, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x8, 0x8, 0, 0}, {0x45, 0x55, 0x55, 1, 1}, {0x46, 0, 0, 0, 0}, {0x47, 0x11, 0x11, 0, 0}, {0x48, 0, 0, 0, 0}, {0x49, 0x44, 0x44, 0, 0}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0x6, 0x6, 0, 0}, {0x4C, 0x4, 0x4, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x26, 0x26, 1, 1}, {0x50, 0x26, 0x26, 1, 1}, {0x51, 0xf, 0xf, 1, 1}, {0x52, 0xf, 0xf, 1, 1}, {0x53, 0x44, 0x44, 0, 0}, {0x54, 0, 0, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0x8, 0x8, 0, 0}, {0x57, 0x8, 0x8, 0, 0}, {0x58, 0x7, 0x7, 0, 0}, {0x59, 0x22, 0x22, 0, 0}, {0x5A, 0x22, 0x22, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x4, 0x4, 1, 1}, {0x5D, 0x7, 0x7, 0, 0}, {0x5E, 0x55, 0x55, 0, 0}, {0x5F, 0x23, 0x23, 0, 0}, {0x60, 0x41, 0x41, 0, 0}, {0x61, 0x1, 0x1, 0, 0}, {0x62, 0xa, 0xa, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0, 0, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0xc, 0xc, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0, 0, 0, 0}, {0x72, 0x22, 0x22, 0, 0}, {0x73, 0x22, 0x22, 0, 0}, {0x74, 0, 0, 1, 1}, {0x75, 0xa, 0xa, 0, 0}, {0x76, 0x1, 0x1, 0, 0}, {0x77, 0x22, 0x22, 0, 0}, {0x78, 0x30, 0x30, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0x5, 0x5, 1, 1}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static const struct radio_regs regs_SYN_2056_rev11[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0x1, 0x1, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0x60, 0x60, 0, 0}, {0x23, 0x6, 0x6, 0, 0}, {0x24, 0xc, 0xc, 0, 0}, {0x25, 0, 0, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0, 0, 0, 0}, {0x28, 0x1, 0x1, 0, 0}, {0x29, 0, 0, 0, 0}, {0x2A, 0, 0, 0, 0}, {0x2B, 0, 0, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0, 0, 0, 0}, {0x2F, 0x1f, 0x1f, 0, 0}, {0x30, 0x15, 0x15, 0, 0}, {0x31, 0xf, 0xf, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0, 0, 0, 0}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0, 0, 0, 0}, {0x38, 0, 0, 0, 0}, {0x39, 0, 0, 0, 0}, {0x3A, 0, 0, 0, 0}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x13, 0x13, 0, 0}, {0x3D, 0xf, 0xf, 0, 0}, {0x3E, 0x18, 0x18, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x20, 0x20, 0, 0}, {0x42, 0x20, 0x20, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x77, 0x77, 0, 0}, {0x45, 0x7, 0x7, 0, 0}, {0x46, 0x1, 0x1, 0, 0}, {0x47, 0x6, 0x6, 1, 1}, {0x48, 0xf, 0xf, 0, 0}, {0x49, 0x3f, 0x3f, 1, 1}, {0x4A, 0x32, 0x32, 0, 0}, {0x4B, 0x6, 0x6, 1, 1}, {0x4C, 0x6, 0x6, 1, 1}, {0x4D, 0x4, 0x4, 0, 0}, {0x4E, 0x2b, 0x2b, 1, 1}, {0x4F, 0x1, 0x1, 0, 0}, {0x50, 0x1c, 0x1c, 0, 0}, {0x51, 0x2, 0x2, 0, 0}, {0x52, 0x2, 0x2, 0, 0}, {0x53, 0xf7, 0xf7, 1, 1}, {0x54, 0xb4, 0xb4, 0, 0}, {0x55, 0xd2, 0xd2, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0, 0, 0, 0}, {0x58, 0x4, 0x4, 0, 0}, {0x59, 0x96, 0x96, 0, 0}, {0x5A, 0x3e, 0x3e, 0, 0}, {0x5B, 0x3e, 0x3e, 0, 0}, {0x5C, 0x13, 0x13, 0, 0}, {0x5D, 0x2, 0x2, 0, 0}, {0x5E, 0, 0, 0, 0}, {0x5F, 0x7, 0x7, 0, 0}, {0x60, 0x7, 0x7, 1, 1}, {0x61, 0x8, 0x8, 0, 0}, {0x62, 0x3, 0x3, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0x40, 0x40, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0x1, 0x1, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0x60, 0x60, 0, 0}, {0x71, 0x66, 0x66, 0, 0}, {0x72, 0xc, 0xc, 0, 0}, {0x73, 0x66, 0x66, 0, 0}, {0x74, 0x8f, 0x8f, 1, 1}, {0x75, 0, 0, 0, 0}, {0x76, 0xcc, 0xcc, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0x66, 0x66, 0, 0}, {0x79, 0x66, 0x66, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0xff, 0xff, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0, 0, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0, 0, 0, 0}, {0x9A, 0, 0, 0, 0}, {0x9B, 0, 0, 0, 0}, {0x9C, 0, 0, 0, 0}, {0x9D, 0, 0, 0, 0}, {0x9E, 0, 0, 0, 0}, {0x9F, 0x6, 0x6, 0, 0}, {0xA0, 0x66, 0x66, 0, 0}, {0xA1, 0x66, 0x66, 0, 0}, {0xA2, 0x66, 0x66, 0, 0}, {0xA3, 0x66, 0x66, 0, 0}, {0xA4, 0x66, 0x66, 0, 0}, {0xA5, 0x66, 0x66, 0, 0}, {0xA6, 0x66, 0x66, 0, 0}, {0xA7, 0x66, 0x66, 0, 0}, {0xA8, 0x66, 0x66, 0, 0}, {0xA9, 0x66, 0x66, 0, 0}, {0xAA, 0x66, 0x66, 0, 0}, {0xAB, 0x66, 0x66, 0, 0}, {0xAC, 0x66, 0x66, 0, 0}, {0xAD, 0x66, 0x66, 0, 0}, {0xAE, 0x66, 0x66, 0, 0}, {0xAF, 0x66, 0x66, 0, 0}, {0xB0, 0x66, 0x66, 0, 0}, {0xB1, 0x66, 0x66, 0, 0}, {0xB2, 0x66, 0x66, 0, 0}, {0xB3, 0xa, 0xa, 0, 0}, {0xB4, 0, 0, 0, 0}, {0xB5, 0, 0, 0, 0}, {0xB6, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static const struct radio_regs regs_TX_2056_rev11[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0, 0, 0, 0}, {0x21, 0x88, 0x88, 0, 0}, {0x22, 0x88, 0x88, 0, 0}, {0x23, 0x88, 0x88, 0, 0}, {0x24, 0x88, 0x88, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0, 0, 0, 0}, {0x27, 0x3, 0x3, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x3, 0x3, 0, 0}, {0x2A, 0x37, 0x37, 0, 0}, {0x2B, 0x3, 0x3, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0, 0, 0, 0}, {0x2E, 0x1, 0x1, 0, 0}, {0x2F, 0x1, 0x1, 0, 0}, {0x30, 0, 0, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0, 0, 0, 0}, {0x33, 0x11, 0x11, 0, 0}, {0x34, 0xee, 0xee, 1, 1}, {0x35, 0, 0, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x3, 0x3, 0, 0}, {0x38, 0x50, 0x50, 1, 1}, {0x39, 0, 0, 0, 0}, {0x3A, 0x50, 0x50, 1, 1}, {0x3B, 0, 0, 0, 0}, {0x3C, 0x6e, 0x6e, 0, 0}, {0x3D, 0xf0, 0xf0, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0, 0, 0, 0}, {0x41, 0x3, 0x3, 0, 0}, {0x42, 0x3, 0x3, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x1e, 0x1e, 0, 0}, {0x45, 0, 0, 0, 0}, {0x46, 0x6e, 0x6e, 0, 0}, {0x47, 0xf0, 0xf0, 1, 1}, {0x48, 0, 0, 0, 0}, {0x49, 0x2, 0x2, 0, 0}, {0x4A, 0xff, 0xff, 1, 1}, {0x4B, 0xc, 0xc, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0x38, 0x38, 0, 0}, {0x4E, 0x70, 0x70, 1, 1}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0x88, 0x88, 0, 0}, {0x51, 0xc, 0xc, 0, 0}, {0x52, 0, 0, 0, 0}, {0x53, 0x8, 0x8, 0, 0}, {0x54, 0x70, 0x70, 1, 1}, {0x55, 0x2, 0x2, 0, 0}, {0x56, 0xff, 0xff, 1, 1}, {0x57, 0, 0, 0, 0}, {0x58, 0x83, 0x83, 0, 0}, {0x59, 0x77, 0x77, 1, 1}, {0x5A, 0, 0, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x88, 0x88, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x8, 0x8, 0, 0}, {0x5F, 0x77, 0x77, 1, 1}, {0x60, 0x1, 0x1, 0, 0}, {0x61, 0, 0, 0, 0}, {0x62, 0x7, 0x7, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0x7, 0x7, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 1, 1}, {0x68, 0, 0, 0, 0}, {0x69, 0xa, 0xa, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0, 0, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0x2, 0x2, 0, 0}, {0x72, 0, 0, 0, 0}, {0x73, 0, 0, 0, 0}, {0x74, 0xe, 0xe, 0, 0}, {0x75, 0xe, 0xe, 0, 0}, {0x76, 0xe, 0xe, 0, 0}, {0x77, 0x13, 0x13, 0, 0}, {0x78, 0x13, 0x13, 0, 0}, {0x79, 0x1b, 0x1b, 0, 0}, {0x7A, 0x1b, 0x1b, 0, 0}, {0x7B, 0x55, 0x55, 0, 0}, {0x7C, 0x5b, 0x5b, 0, 0}, {0x7D, 0x30, 0x30, 1, 1}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0x70, 0x70, 0, 0}, {0x94, 0x70, 0x70, 0, 0}, {0x95, 0x70, 0x70, 0, 0}, {0x96, 0x70, 0x70, 0, 0}, {0x97, 0x70, 0x70, 0, 0}, {0x98, 0x70, 0x70, 0, 0}, {0x99, 0x70, 0x70, 0, 0}, {0x9A, 0x70, 0x70, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static const struct radio_regs regs_RX_2056_rev11[] = { {0x02, 0, 0, 0, 0}, {0x03, 0, 0, 0, 0}, {0x04, 0, 0, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0, 0, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0, 0, 0, 0}, {0x0B, 0, 0, 0, 0}, {0x0C, 0, 0, 0, 0}, {0x0D, 0, 0, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0, 0, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0, 0, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0, 0, 0, 0}, {0x14, 0, 0, 0, 0}, {0x15, 0, 0, 0, 0}, {0x16, 0, 0, 0, 0}, {0x17, 0, 0, 0, 0}, {0x18, 0, 0, 0, 0}, {0x19, 0, 0, 0, 0}, {0x1A, 0, 0, 0, 0}, {0x1B, 0, 0, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0, 0, 0, 0}, {0x1E, 0, 0, 0, 0}, {0x1F, 0, 0, 0, 0}, {0x20, 0x3, 0x3, 0, 0}, {0x21, 0, 0, 0, 0}, {0x22, 0, 0, 0, 0}, {0x23, 0x90, 0x90, 0, 0}, {0x24, 0x55, 0x55, 0, 0}, {0x25, 0x15, 0x15, 0, 0}, {0x26, 0x5, 0x5, 0, 0}, {0x27, 0x15, 0x15, 0, 0}, {0x28, 0x5, 0x5, 0, 0}, {0x29, 0x20, 0x20, 0, 0}, {0x2A, 0x11, 0x11, 0, 0}, {0x2B, 0x90, 0x90, 0, 0}, {0x2C, 0, 0, 0, 0}, {0x2D, 0x88, 0x88, 0, 0}, {0x2E, 0x32, 0x32, 0, 0}, {0x2F, 0x77, 0x77, 0, 0}, {0x30, 0x17, 0x17, 1, 1}, {0x31, 0xff, 0xff, 1, 1}, {0x32, 0x20, 0x20, 0, 0}, {0x33, 0, 0, 0, 0}, {0x34, 0x88, 0x88, 0, 0}, {0x35, 0x32, 0x32, 0, 0}, {0x36, 0x77, 0x77, 0, 0}, {0x37, 0x17, 0x17, 1, 1}, {0x38, 0xf0, 0xf0, 1, 1}, {0x39, 0x20, 0x20, 0, 0}, {0x3A, 0x8, 0x8, 0, 0}, {0x3B, 0x55, 0x55, 1, 1}, {0x3C, 0, 0, 0, 0}, {0x3D, 0x88, 0x88, 1, 1}, {0x3E, 0, 0, 0, 0}, {0x3F, 0x44, 0x44, 0, 0}, {0x40, 0x7, 0x7, 1, 1}, {0x41, 0x6, 0x6, 0, 0}, {0x42, 0x4, 0x4, 0, 0}, {0x43, 0, 0, 0, 0}, {0x44, 0x8, 0x8, 0, 0}, {0x45, 0x55, 0x55, 1, 1}, {0x46, 0, 0, 0, 0}, {0x47, 0x11, 0x11, 0, 0}, {0x48, 0, 0, 0, 0}, {0x49, 0x44, 0x44, 0, 0}, {0x4A, 0x7, 0x7, 0, 0}, {0x4B, 0x6, 0x6, 0, 0}, {0x4C, 0x4, 0x4, 0, 0}, {0x4D, 0, 0, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x26, 0x26, 1, 1}, {0x50, 0x26, 0x26, 1, 1}, {0x51, 0xf, 0xf, 1, 1}, {0x52, 0xf, 0xf, 1, 1}, {0x53, 0x44, 0x44, 0, 0}, {0x54, 0, 0, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0x8, 0x8, 0, 0}, {0x57, 0x8, 0x8, 0, 0}, {0x58, 0x7, 0x7, 0, 0}, {0x59, 0x22, 0x22, 0, 0}, {0x5A, 0x22, 0x22, 0, 0}, {0x5B, 0x2, 0x2, 0, 0}, {0x5C, 0x4, 0x4, 1, 1}, {0x5D, 0x7, 0x7, 0, 0}, {0x5E, 0x55, 0x55, 0, 0}, {0x5F, 0x23, 0x23, 0, 0}, {0x60, 0x41, 0x41, 0, 0}, {0x61, 0x1, 0x1, 0, 0}, {0x62, 0xa, 0xa, 0, 0}, {0x63, 0, 0, 0, 0}, {0x64, 0, 0, 0, 0}, {0x65, 0, 0, 0, 0}, {0x66, 0, 0, 0, 0}, {0x67, 0, 0, 0, 0}, {0x68, 0, 0, 0, 0}, {0x69, 0, 0, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0xc, 0xc, 0, 0}, {0x6C, 0, 0, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0, 0, 0, 0}, {0x6F, 0, 0, 0, 0}, {0x70, 0, 0, 0, 0}, {0x71, 0, 0, 0, 0}, {0x72, 0x22, 0x22, 0, 0}, {0x73, 0x22, 0x22, 0, 0}, {0x74, 0, 0, 1, 1}, {0x75, 0xa, 0xa, 0, 0}, {0x76, 0x1, 0x1, 0, 0}, {0x77, 0x22, 0x22, 0, 0}, {0x78, 0x30, 0x30, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0x5, 0x5, 1, 1}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0, 0, 0, 0}, {0x84, 0, 0, 0, 0}, {0x85, 0, 0, 0, 0}, {0x86, 0, 0, 0, 0}, {0x87, 0, 0, 0, 0}, {0x88, 0, 0, 0, 0}, {0x89, 0, 0, 0, 0}, {0x8A, 0, 0, 0, 0}, {0x8B, 0, 0, 0, 0}, {0x8C, 0, 0, 0, 0}, {0x8D, 0, 0, 0, 0}, {0x8E, 0, 0, 0, 0}, {0x8F, 0, 0, 0, 0}, {0x90, 0, 0, 0, 0}, {0x91, 0, 0, 0, 0}, {0x92, 0, 0, 0, 0}, {0x93, 0, 0, 0, 0}, {0x94, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0}, }; static struct radio_20xx_regs regs_2057_rev4[] = { {0x00, 0x84, 0}, {0x01, 0, 0}, {0x02, 0x60, 0}, {0x03, 0x1f, 0}, {0x04, 0x4, 0}, {0x05, 0x2, 0}, {0x06, 0x1, 0}, {0x07, 0x1, 0}, {0x08, 0x1, 0}, {0x09, 0x69, 0}, {0x0A, 0x66, 0}, {0x0B, 0x6, 0}, {0x0C, 0x18, 0}, {0x0D, 0x3, 0}, {0x0E, 0x20, 1}, {0x0F, 0x20, 0}, {0x10, 0, 0}, {0x11, 0x7c, 0}, {0x12, 0x42, 0}, {0x13, 0xbd, 0}, {0x14, 0x7, 0}, {0x15, 0xf7, 0}, {0x16, 0x8, 0}, {0x17, 0x17, 0}, {0x18, 0x7, 0}, {0x19, 0, 0}, {0x1A, 0x2, 0}, {0x1B, 0x13, 0}, {0x1C, 0x3e, 0}, {0x1D, 0x3e, 0}, {0x1E, 0x96, 0}, {0x1F, 0x4, 0}, {0x20, 0, 0}, {0x21, 0, 0}, {0x22, 0x17, 0}, {0x23, 0x4, 0}, {0x24, 0x1, 0}, {0x25, 0x6, 0}, {0x26, 0x4, 0}, {0x27, 0xd, 0}, {0x28, 0xd, 0}, {0x29, 0x30, 0}, {0x2A, 0x32, 0}, {0x2B, 0x8, 0}, {0x2C, 0x1c, 0}, {0x2D, 0x2, 0}, {0x2E, 0x4, 0}, {0x2F, 0x7f, 0}, {0x30, 0x27, 0}, {0x31, 0, 1}, {0x32, 0, 1}, {0x33, 0, 1}, {0x34, 0, 0}, {0x35, 0x26, 1}, {0x36, 0x18, 0}, {0x37, 0x7, 0}, {0x38, 0x66, 0}, {0x39, 0x66, 0}, {0x3A, 0x66, 0}, {0x3B, 0x66, 0}, {0x3C, 0xff, 1}, {0x3D, 0xff, 1}, {0x3E, 0xff, 1}, {0x3F, 0xff, 1}, {0x40, 0x16, 0}, {0x41, 0x7, 0}, {0x42, 0x19, 0}, {0x43, 0x7, 0}, {0x44, 0x6, 0}, {0x45, 0x3, 0}, {0x46, 0x1, 0}, {0x47, 0x7, 0}, {0x48, 0x33, 0}, {0x49, 0x5, 0}, {0x4A, 0x77, 0}, {0x4B, 0x66, 0}, {0x4C, 0x66, 0}, {0x4D, 0, 0}, {0x4E, 0x4, 0}, {0x4F, 0xc, 0}, {0x50, 0, 0}, {0x51, 0x75, 0}, {0x56, 0x7, 0}, {0x57, 0, 0}, {0x58, 0, 0}, {0x59, 0xa8, 0}, {0x5A, 0, 0}, {0x5B, 0x1f, 0}, {0x5C, 0x30, 0}, {0x5D, 0x1, 0}, {0x5E, 0x30, 0}, {0x5F, 0x70, 0}, {0x60, 0, 0}, {0x61, 0, 0}, {0x62, 0x33, 1}, {0x63, 0x19, 0}, {0x64, 0x62, 0}, {0x65, 0, 0}, {0x66, 0x11, 0}, {0x69, 0, 0}, {0x6A, 0x7e, 0}, {0x6B, 0x3f, 0}, {0x6C, 0x7f, 0}, {0x6D, 0x78, 0}, {0x6E, 0xc8, 0}, {0x6F, 0x88, 0}, {0x70, 0x8, 0}, {0x71, 0xf, 0}, {0x72, 0xbc, 0}, {0x73, 0x8, 0}, {0x74, 0x60, 0}, {0x75, 0x1e, 0}, {0x76, 0x70, 0}, {0x77, 0, 0}, {0x78, 0, 0}, {0x79, 0, 0}, {0x7A, 0x33, 0}, {0x7B, 0x1e, 0}, {0x7C, 0x62, 0}, {0x7D, 0x11, 0}, {0x80, 0x3c, 0}, {0x81, 0x9c, 0}, {0x82, 0xa, 0}, {0x83, 0x9d, 0}, {0x84, 0xa, 0}, {0x85, 0, 0}, {0x86, 0x40, 0}, {0x87, 0x40, 0}, {0x88, 0x88, 0}, {0x89, 0x10, 0}, {0x8A, 0xf0, 1}, {0x8B, 0x10, 1}, {0x8C, 0xf0, 1}, {0x8D, 0, 0}, {0x8E, 0, 0}, {0x8F, 0x10, 0}, {0x90, 0x55, 0}, {0x91, 0x3f, 1}, {0x92, 0x36, 1}, {0x93, 0, 0}, {0x94, 0, 0}, {0x95, 0, 0}, {0x96, 0x87, 0}, {0x97, 0x11, 0}, {0x98, 0, 0}, {0x99, 0x33, 0}, {0x9A, 0x88, 0}, {0x9B, 0, 0}, {0x9C, 0x87, 0}, {0x9D, 0x11, 0}, {0x9E, 0, 0}, {0x9F, 0x33, 0}, {0xA0, 0x88, 0}, {0xA1, 0xe1, 0}, {0xA2, 0x3f, 0}, {0xA3, 0x44, 0}, {0xA4, 0x8c, 1}, {0xA5, 0x6d, 0}, {0xA6, 0x22, 0}, {0xA7, 0xbe, 0}, {0xA8, 0x55, 1}, {0xA9, 0xc, 0}, {0xAA, 0xc, 0}, {0xAB, 0xaa, 0}, {0xAC, 0x2, 0}, {0xAD, 0, 0}, {0xAE, 0x10, 0}, {0xAF, 0x1, 1}, {0xB0, 0, 0}, {0xB1, 0, 0}, {0xB2, 0x80, 0}, {0xB3, 0x60, 0}, {0xB4, 0x44, 0}, {0xB5, 0x55, 0}, {0xB6, 0x1, 0}, {0xB7, 0x55, 0}, {0xB8, 0x1, 0}, {0xB9, 0x5, 0}, {0xBA, 0x55, 0}, {0xBB, 0x55, 0}, {0xC1, 0, 0}, {0xC2, 0, 0}, {0xC3, 0, 0}, {0xC4, 0, 0}, {0xC5, 0, 0}, {0xC6, 0, 0}, {0xC7, 0, 0}, {0xC8, 0, 0}, {0xC9, 0, 0}, {0xCA, 0, 0}, {0xCB, 0, 0}, {0xCC, 0, 0}, {0xCD, 0, 0}, {0xCE, 0x5e, 0}, {0xCF, 0xc, 0}, {0xD0, 0xc, 0}, {0xD1, 0xc, 0}, {0xD2, 0, 0}, {0xD3, 0x2b, 0}, {0xD4, 0xc, 0}, {0xD5, 0, 0}, {0xD6, 0x75, 0}, {0xDB, 0x7, 0}, {0xDC, 0, 0}, {0xDD, 0, 0}, {0xDE, 0xa8, 0}, {0xDF, 0, 0}, {0xE0, 0x1f, 0}, {0xE1, 0x30, 0}, {0xE2, 0x1, 0}, {0xE3, 0x30, 0}, {0xE4, 0x70, 0}, {0xE5, 0, 0}, {0xE6, 0, 0}, {0xE7, 0x33, 0}, {0xE8, 0x19, 0}, {0xE9, 0x62, 0}, {0xEA, 0, 0}, {0xEB, 0x11, 0}, {0xEE, 0, 0}, {0xEF, 0x7e, 0}, {0xF0, 0x3f, 0}, {0xF1, 0x7f, 0}, {0xF2, 0x78, 0}, {0xF3, 0xc8, 0}, {0xF4, 0x88, 0}, {0xF5, 0x8, 0}, {0xF6, 0xf, 0}, {0xF7, 0xbc, 0}, {0xF8, 0x8, 0}, {0xF9, 0x60, 0}, {0xFA, 0x1e, 0}, {0xFB, 0x70, 0}, {0xFC, 0, 0}, {0xFD, 0, 0}, {0xFE, 0, 0}, {0xFF, 0x33, 0}, {0x100, 0x1e, 0}, {0x101, 0x62, 0}, {0x102, 0x11, 0}, {0x105, 0x3c, 0}, {0x106, 0x9c, 0}, {0x107, 0xa, 0}, {0x108, 0x9d, 0}, {0x109, 0xa, 0}, {0x10A, 0, 0}, {0x10B, 0x40, 0}, {0x10C, 0x40, 0}, {0x10D, 0x88, 0}, {0x10E, 0x10, 0}, {0x10F, 0xf0, 1}, {0x110, 0x10, 1}, {0x111, 0xf0, 1}, {0x112, 0, 0}, {0x113, 0, 0}, {0x114, 0x10, 0}, {0x115, 0x55, 0}, {0x116, 0x3f, 1}, {0x117, 0x36, 1}, {0x118, 0, 0}, {0x119, 0, 0}, {0x11A, 0, 0}, {0x11B, 0x87, 0}, {0x11C, 0x11, 0}, {0x11D, 0, 0}, {0x11E, 0x33, 0}, {0x11F, 0x88, 0}, {0x120, 0, 0}, {0x121, 0x87, 0}, {0x122, 0x11, 0}, {0x123, 0, 0}, {0x124, 0x33, 0}, {0x125, 0x88, 0}, {0x126, 0xe1, 0}, {0x127, 0x3f, 0}, {0x128, 0x44, 0}, {0x129, 0x8c, 1}, {0x12A, 0x6d, 0}, {0x12B, 0x22, 0}, {0x12C, 0xbe, 0}, {0x12D, 0x55, 1}, {0x12E, 0xc, 0}, {0x12F, 0xc, 0}, {0x130, 0xaa, 0}, {0x131, 0x2, 0}, {0x132, 0, 0}, {0x133, 0x10, 0}, {0x134, 0x1, 1}, {0x135, 0, 0}, {0x136, 0, 0}, {0x137, 0x80, 0}, {0x138, 0x60, 0}, {0x139, 0x44, 0}, {0x13A, 0x55, 0}, {0x13B, 0x1, 0}, {0x13C, 0x55, 0}, {0x13D, 0x1, 0}, {0x13E, 0x5, 0}, {0x13F, 0x55, 0}, {0x140, 0x55, 0}, {0x146, 0, 0}, {0x147, 0, 0}, {0x148, 0, 0}, {0x149, 0, 0}, {0x14A, 0, 0}, {0x14B, 0, 0}, {0x14C, 0, 0}, {0x14D, 0, 0}, {0x14E, 0, 0}, {0x14F, 0, 0}, {0x150, 0, 0}, {0x151, 0, 0}, {0x152, 0, 0}, {0x153, 0, 0}, {0x154, 0xc, 0}, {0x155, 0xc, 0}, {0x156, 0xc, 0}, {0x157, 0, 0}, {0x158, 0x2b, 0}, {0x159, 0x84, 0}, {0x15A, 0x15, 0}, {0x15B, 0xf, 0}, {0x15C, 0, 0}, {0x15D, 0, 0}, {0x15E, 0, 1}, {0x15F, 0, 1}, {0x160, 0, 1}, {0x161, 0, 1}, {0x162, 0, 1}, {0x163, 0, 1}, {0x164, 0, 0}, {0x165, 0, 0}, {0x166, 0, 0}, {0x167, 0, 0}, {0x168, 0, 0}, {0x169, 0x2, 1}, {0x16A, 0, 1}, {0x16B, 0, 1}, {0x16C, 0, 1}, {0x16D, 0, 0}, {0x170, 0, 0}, {0x171, 0x77, 0}, {0x172, 0x77, 0}, {0x173, 0x77, 0}, {0x174, 0x77, 0}, {0x175, 0, 0}, {0x176, 0x3, 0}, {0x177, 0x37, 0}, {0x178, 0x3, 0}, {0x179, 0, 0}, {0x17A, 0x21, 0}, {0x17B, 0x21, 0}, {0x17C, 0, 0}, {0x17D, 0xaa, 0}, {0x17E, 0, 0}, {0x17F, 0xaa, 0}, {0x180, 0, 0}, {0x190, 0, 0}, {0x191, 0x77, 0}, {0x192, 0x77, 0}, {0x193, 0x77, 0}, {0x194, 0x77, 0}, {0x195, 0, 0}, {0x196, 0x3, 0}, {0x197, 0x37, 0}, {0x198, 0x3, 0}, {0x199, 0, 0}, {0x19A, 0x21, 0}, {0x19B, 0x21, 0}, {0x19C, 0, 0}, {0x19D, 0xaa, 0}, {0x19E, 0, 0}, {0x19F, 0xaa, 0}, {0x1A0, 0, 0}, {0x1A1, 0x2, 0}, {0x1A2, 0xf, 0}, {0x1A3, 0xf, 0}, {0x1A4, 0, 1}, {0x1A5, 0, 1}, {0x1A6, 0, 1}, {0x1A7, 0x2, 0}, {0x1A8, 0xf, 0}, {0x1A9, 0xf, 0}, {0x1AA, 0, 1}, {0x1AB, 0, 1}, {0x1AC, 0, 1}, {0xFFFF, 0, 0}, }; static struct radio_20xx_regs regs_2057_rev5[] = { {0x00, 0, 1}, {0x01, 0x57, 1}, {0x02, 0x20, 1}, {0x03, 0x1f, 0}, {0x04, 0x4, 0}, {0x05, 0x2, 0}, {0x06, 0x1, 0}, {0x07, 0x1, 0}, {0x08, 0x1, 0}, {0x09, 0x69, 0}, {0x0A, 0x66, 0}, {0x0B, 0x6, 0}, {0x0C, 0x18, 0}, {0x0D, 0x3, 0}, {0x0E, 0x20, 0}, {0x0F, 0x20, 0}, {0x10, 0, 0}, {0x11, 0x7c, 0}, {0x12, 0x42, 0}, {0x13, 0xbd, 0}, {0x14, 0x7, 0}, {0x15, 0x87, 0}, {0x16, 0x8, 0}, {0x17, 0x17, 0}, {0x18, 0x7, 0}, {0x19, 0, 0}, {0x1A, 0x2, 0}, {0x1B, 0x13, 0}, {0x1C, 0x3e, 0}, {0x1D, 0x3e, 0}, {0x1E, 0x96, 0}, {0x1F, 0x4, 0}, {0x20, 0, 0}, {0x21, 0, 0}, {0x22, 0x17, 0}, {0x23, 0x6, 1}, {0x24, 0x1, 0}, {0x25, 0x6, 0}, {0x26, 0x4, 0}, {0x27, 0xd, 0}, {0x28, 0xd, 0}, {0x29, 0x30, 0}, {0x2A, 0x32, 0}, {0x2B, 0x8, 0}, {0x2C, 0x1c, 0}, {0x2D, 0x2, 0}, {0x2E, 0x4, 0}, {0x2F, 0x7f, 0}, {0x30, 0x27, 0}, {0x31, 0, 1}, {0x32, 0, 1}, {0x33, 0, 1}, {0x34, 0, 0}, {0x35, 0x20, 0}, {0x36, 0x18, 0}, {0x37, 0x7, 0}, {0x38, 0x66, 0}, {0x39, 0x66, 0}, {0x3C, 0xff, 0}, {0x3D, 0xff, 0}, {0x40, 0x16, 0}, {0x41, 0x7, 0}, {0x45, 0x3, 0}, {0x46, 0x1, 0}, {0x47, 0x7, 0}, {0x4B, 0x66, 0}, {0x4C, 0x66, 0}, {0x4D, 0, 0}, {0x4E, 0x4, 0}, {0x4F, 0xc, 0}, {0x50, 0, 0}, {0x51, 0x70, 1}, {0x56, 0x7, 0}, {0x57, 0, 0}, {0x58, 0, 0}, {0x59, 0x88, 1}, {0x5A, 0, 0}, {0x5B, 0x1f, 0}, {0x5C, 0x20, 1}, {0x5D, 0x1, 0}, {0x5E, 0x30, 0}, {0x5F, 0x70, 0}, {0x60, 0, 0}, {0x61, 0, 0}, {0x62, 0x33, 1}, {0x63, 0xf, 1}, {0x64, 0xf, 1}, {0x65, 0, 0}, {0x66, 0x11, 0}, {0x80, 0x3c, 0}, {0x81, 0x1, 1}, {0x82, 0xa, 0}, {0x85, 0, 0}, {0x86, 0x40, 0}, {0x87, 0x40, 0}, {0x88, 0x88, 0}, {0x89, 0x10, 0}, {0x8A, 0xf0, 0}, {0x8B, 0x10, 0}, {0x8C, 0xf0, 0}, {0x8F, 0x10, 0}, {0x90, 0x55, 0}, {0x91, 0x3f, 1}, {0x92, 0x36, 1}, {0x93, 0, 0}, {0x94, 0, 0}, {0x95, 0, 0}, {0x96, 0x87, 0}, {0x97, 0x11, 0}, {0x98, 0, 0}, {0x99, 0x33, 0}, {0x9A, 0x88, 0}, {0xA1, 0x20, 1}, {0xA2, 0x3f, 0}, {0xA3, 0x44, 0}, {0xA4, 0x8c, 0}, {0xA5, 0x6c, 0}, {0xA6, 0x22, 0}, {0xA7, 0xbe, 0}, {0xA8, 0x55, 0}, {0xAA, 0xc, 0}, {0xAB, 0xaa, 0}, {0xAC, 0x2, 0}, {0xAD, 0, 0}, {0xAE, 0x10, 0}, {0xAF, 0x1, 0}, {0xB0, 0, 0}, {0xB1, 0, 0}, {0xB2, 0x80, 0}, {0xB3, 0x60, 0}, {0xB4, 0x44, 0}, {0xB5, 0x55, 0}, {0xB6, 0x1, 0}, {0xB7, 0x55, 0}, {0xB8, 0x1, 0}, {0xB9, 0x5, 0}, {0xBA, 0x55, 0}, {0xBB, 0x55, 0}, {0xC3, 0, 0}, {0xC4, 0, 0}, {0xC5, 0, 0}, {0xC6, 0, 0}, {0xC7, 0, 0}, {0xC8, 0, 0}, {0xC9, 0, 0}, {0xCA, 0, 0}, {0xCB, 0, 0}, {0xCD, 0, 0}, {0xCE, 0x5e, 0}, {0xCF, 0xc, 0}, {0xD0, 0xc, 0}, {0xD1, 0xc, 0}, {0xD2, 0, 0}, {0xD3, 0x2b, 0}, {0xD4, 0xc, 0}, {0xD5, 0, 0}, {0xD6, 0x70, 1}, {0xDB, 0x7, 0}, {0xDC, 0, 0}, {0xDD, 0, 0}, {0xDE, 0x88, 1}, {0xDF, 0, 0}, {0xE0, 0x1f, 0}, {0xE1, 0x20, 1}, {0xE2, 0x1, 0}, {0xE3, 0x30, 0}, {0xE4, 0x70, 0}, {0xE5, 0, 0}, {0xE6, 0, 0}, {0xE7, 0x33, 0}, {0xE8, 0xf, 1}, {0xE9, 0xf, 1}, {0xEA, 0, 0}, {0xEB, 0x11, 0}, {0x105, 0x3c, 0}, {0x106, 0x1, 1}, {0x107, 0xa, 0}, {0x10A, 0, 0}, {0x10B, 0x40, 0}, {0x10C, 0x40, 0}, {0x10D, 0x88, 0}, {0x10E, 0x10, 0}, {0x10F, 0xf0, 0}, {0x110, 0x10, 0}, {0x111, 0xf0, 0}, {0x114, 0x10, 0}, {0x115, 0x55, 0}, {0x116, 0x3f, 1}, {0x117, 0x36, 1}, {0x118, 0, 0}, {0x119, 0, 0}, {0x11A, 0, 0}, {0x11B, 0x87, 0}, {0x11C, 0x11, 0}, {0x11D, 0, 0}, {0x11E, 0x33, 0}, {0x11F, 0x88, 0}, {0x126, 0x20, 1}, {0x127, 0x3f, 0}, {0x128, 0x44, 0}, {0x129, 0x8c, 0}, {0x12A, 0x6c, 0}, {0x12B, 0x22, 0}, {0x12C, 0xbe, 0}, {0x12D, 0x55, 0}, {0x12F, 0xc, 0}, {0x130, 0xaa, 0}, {0x131, 0x2, 0}, {0x132, 0, 0}, {0x133, 0x10, 0}, {0x134, 0x1, 0}, {0x135, 0, 0}, {0x136, 0, 0}, {0x137, 0x80, 0}, {0x138, 0x60, 0}, {0x139, 0x44, 0}, {0x13A, 0x55, 0}, {0x13B, 0x1, 0}, {0x13C, 0x55, 0}, {0x13D, 0x1, 0}, {0x13E, 0x5, 0}, {0x13F, 0x55, 0}, {0x140, 0x55, 0}, {0x148, 0, 0}, {0x149, 0, 0}, {0x14A, 0, 0}, {0x14B, 0, 0}, {0x14C, 0, 0}, {0x14D, 0, 0}, {0x14E, 0, 0}, {0x14F, 0, 0}, {0x150, 0, 0}, {0x154, 0xc, 0}, {0x155, 0xc, 0}, {0x156, 0xc, 0}, {0x157, 0, 0}, {0x158, 0x2b, 0}, {0x159, 0x84, 0}, {0x15A, 0x15, 0}, {0x15B, 0xf, 0}, {0x15C, 0, 0}, {0x15D, 0, 0}, {0x15E, 0, 1}, {0x15F, 0, 1}, {0x160, 0, 1}, {0x161, 0, 1}, {0x162, 0, 1}, {0x163, 0, 1}, {0x164, 0, 0}, {0x165, 0, 0}, {0x166, 0, 0}, {0x167, 0, 0}, {0x168, 0, 0}, {0x169, 0, 0}, {0x16A, 0, 1}, {0x16B, 0, 1}, {0x16C, 0, 1}, {0x16D, 0, 0}, {0x170, 0, 0}, {0x171, 0x77, 0}, {0x172, 0x77, 0}, {0x173, 0x77, 0}, {0x174, 0x77, 0}, {0x175, 0, 0}, {0x176, 0x3, 0}, {0x177, 0x37, 0}, {0x178, 0x3, 0}, {0x179, 0, 0}, {0x17B, 0x21, 0}, {0x17C, 0, 0}, {0x17D, 0xaa, 0}, {0x17E, 0, 0}, {0x190, 0, 0}, {0x191, 0x77, 0}, {0x192, 0x77, 0}, {0x193, 0x77, 0}, {0x194, 0x77, 0}, {0x195, 0, 0}, {0x196, 0x3, 0}, {0x197, 0x37, 0}, {0x198, 0x3, 0}, {0x199, 0, 0}, {0x19B, 0x21, 0}, {0x19C, 0, 0}, {0x19D, 0xaa, 0}, {0x19E, 0, 0}, {0x1A1, 0x2, 0}, {0x1A2, 0xf, 0}, {0x1A3, 0xf, 0}, {0x1A4, 0, 1}, {0x1A5, 0, 1}, {0x1A6, 0, 1}, {0x1A7, 0x2, 0}, {0x1A8, 0xf, 0}, {0x1A9, 0xf, 0}, {0x1AA, 0, 1}, {0x1AB, 0, 1}, {0x1AC, 0, 1}, {0x1AD, 0x84, 0}, {0x1AE, 0x60, 0}, {0x1AF, 0x47, 0}, {0x1B0, 0x47, 0}, {0x1B1, 0, 0}, {0x1B2, 0, 0}, {0x1B3, 0, 0}, {0x1B4, 0, 0}, {0x1B5, 0, 0}, {0x1B6, 0, 0}, {0x1B7, 0xc, 1}, {0x1B8, 0, 0}, {0x1B9, 0, 0}, {0x1BA, 0, 0}, {0x1BB, 0, 0}, {0x1BC, 0, 0}, {0x1BD, 0, 0}, {0x1BE, 0, 0}, {0x1BF, 0, 0}, {0x1C0, 0, 0}, {0x1C1, 0x1, 1}, {0x1C2, 0x80, 1}, {0x1C3, 0, 0}, {0x1C4, 0, 0}, {0x1C5, 0, 0}, {0x1C6, 0, 0}, {0x1C7, 0, 0}, {0x1C8, 0, 0}, {0x1C9, 0, 0}, {0x1CA, 0, 0}, {0xFFFF, 0, 0} }; static struct radio_20xx_regs regs_2057_rev5v1[] = { {0x00, 0x15, 1}, {0x01, 0x57, 1}, {0x02, 0x20, 1}, {0x03, 0x1f, 0}, {0x04, 0x4, 0}, {0x05, 0x2, 0}, {0x06, 0x1, 0}, {0x07, 0x1, 0}, {0x08, 0x1, 0}, {0x09, 0x69, 0}, {0x0A, 0x66, 0}, {0x0B, 0x6, 0}, {0x0C, 0x18, 0}, {0x0D, 0x3, 0}, {0x0E, 0x20, 0}, {0x0F, 0x20, 0}, {0x10, 0, 0}, {0x11, 0x7c, 0}, {0x12, 0x42, 0}, {0x13, 0xbd, 0}, {0x14, 0x7, 0}, {0x15, 0x87, 0}, {0x16, 0x8, 0}, {0x17, 0x17, 0}, {0x18, 0x7, 0}, {0x19, 0, 0}, {0x1A, 0x2, 0}, {0x1B, 0x13, 0}, {0x1C, 0x3e, 0}, {0x1D, 0x3e, 0}, {0x1E, 0x96, 0}, {0x1F, 0x4, 0}, {0x20, 0, 0}, {0x21, 0, 0}, {0x22, 0x17, 0}, {0x23, 0x6, 1}, {0x24, 0x1, 0}, {0x25, 0x6, 0}, {0x26, 0x4, 0}, {0x27, 0xd, 0}, {0x28, 0xd, 0}, {0x29, 0x30, 0}, {0x2A, 0x32, 0}, {0x2B, 0x8, 0}, {0x2C, 0x1c, 0}, {0x2D, 0x2, 0}, {0x2E, 0x4, 0}, {0x2F, 0x7f, 0}, {0x30, 0x27, 0}, {0x31, 0, 1}, {0x32, 0, 1}, {0x33, 0, 1}, {0x34, 0, 0}, {0x35, 0x20, 0}, {0x36, 0x18, 0}, {0x37, 0x7, 0}, {0x38, 0x66, 0}, {0x39, 0x66, 0}, {0x3C, 0xff, 0}, {0x3D, 0xff, 0}, {0x40, 0x16, 0}, {0x41, 0x7, 0}, {0x45, 0x3, 0}, {0x46, 0x1, 0}, {0x47, 0x7, 0}, {0x4B, 0x66, 0}, {0x4C, 0x66, 0}, {0x4D, 0, 0}, {0x4E, 0x4, 0}, {0x4F, 0xc, 0}, {0x50, 0, 0}, {0x51, 0x70, 1}, {0x56, 0x7, 0}, {0x57, 0, 0}, {0x58, 0, 0}, {0x59, 0x88, 1}, {0x5A, 0, 0}, {0x5B, 0x1f, 0}, {0x5C, 0x20, 1}, {0x5D, 0x1, 0}, {0x5E, 0x30, 0}, {0x5F, 0x70, 0}, {0x60, 0, 0}, {0x61, 0, 0}, {0x62, 0x33, 1}, {0x63, 0xf, 1}, {0x64, 0xf, 1}, {0x65, 0, 0}, {0x66, 0x11, 0}, {0x80, 0x3c, 0}, {0x81, 0x1, 1}, {0x82, 0xa, 0}, {0x85, 0, 0}, {0x86, 0x40, 0}, {0x87, 0x40, 0}, {0x88, 0x88, 0}, {0x89, 0x10, 0}, {0x8A, 0xf0, 0}, {0x8B, 0x10, 0}, {0x8C, 0xf0, 0}, {0x8F, 0x10, 0}, {0x90, 0x55, 0}, {0x91, 0x3f, 1}, {0x92, 0x36, 1}, {0x93, 0, 0}, {0x94, 0, 0}, {0x95, 0, 0}, {0x96, 0x87, 0}, {0x97, 0x11, 0}, {0x98, 0, 0}, {0x99, 0x33, 0}, {0x9A, 0x88, 0}, {0xA1, 0x20, 1}, {0xA2, 0x3f, 0}, {0xA3, 0x44, 0}, {0xA4, 0x8c, 0}, {0xA5, 0x6c, 0}, {0xA6, 0x22, 0}, {0xA7, 0xbe, 0}, {0xA8, 0x55, 0}, {0xAA, 0xc, 0}, {0xAB, 0xaa, 0}, {0xAC, 0x2, 0}, {0xAD, 0, 0}, {0xAE, 0x10, 0}, {0xAF, 0x1, 0}, {0xB0, 0, 0}, {0xB1, 0, 0}, {0xB2, 0x80, 0}, {0xB3, 0x60, 0}, {0xB4, 0x44, 0}, {0xB5, 0x55, 0}, {0xB6, 0x1, 0}, {0xB7, 0x55, 0}, {0xB8, 0x1, 0}, {0xB9, 0x5, 0}, {0xBA, 0x55, 0}, {0xBB, 0x55, 0}, {0xC3, 0, 0}, {0xC4, 0, 0}, {0xC5, 0, 0}, {0xC6, 0, 0}, {0xC7, 0, 0}, {0xC8, 0, 0}, {0xC9, 0x1, 1}, {0xCA, 0, 0}, {0xCB, 0, 0}, {0xCD, 0, 0}, {0xCE, 0x5e, 0}, {0xCF, 0xc, 0}, {0xD0, 0xc, 0}, {0xD1, 0xc, 0}, {0xD2, 0, 0}, {0xD3, 0x2b, 0}, {0xD4, 0xc, 0}, {0xD5, 0, 0}, {0xD6, 0x70, 1}, {0xDB, 0x7, 0}, {0xDC, 0, 0}, {0xDD, 0, 0}, {0xDE, 0x88, 1}, {0xDF, 0, 0}, {0xE0, 0x1f, 0}, {0xE1, 0x20, 1}, {0xE2, 0x1, 0}, {0xE3, 0x30, 0}, {0xE4, 0x70, 0}, {0xE5, 0, 0}, {0xE6, 0, 0}, {0xE7, 0x33, 0}, {0xE8, 0xf, 1}, {0xE9, 0xf, 1}, {0xEA, 0, 0}, {0xEB, 0x11, 0}, {0x105, 0x3c, 0}, {0x106, 0x1, 1}, {0x107, 0xa, 0}, {0x10A, 0, 0}, {0x10B, 0x40, 0}, {0x10C, 0x40, 0}, {0x10D, 0x88, 0}, {0x10E, 0x10, 0}, {0x10F, 0xf0, 0}, {0x110, 0x10, 0}, {0x111, 0xf0, 0}, {0x114, 0x10, 0}, {0x115, 0x55, 0}, {0x116, 0x3f, 1}, {0x117, 0x36, 1}, {0x118, 0, 0}, {0x119, 0, 0}, {0x11A, 0, 0}, {0x11B, 0x87, 0}, {0x11C, 0x11, 0}, {0x11D, 0, 0}, {0x11E, 0x33, 0}, {0x11F, 0x88, 0}, {0x126, 0x20, 1}, {0x127, 0x3f, 0}, {0x128, 0x44, 0}, {0x129, 0x8c, 0}, {0x12A, 0x6c, 0}, {0x12B, 0x22, 0}, {0x12C, 0xbe, 0}, {0x12D, 0x55, 0}, {0x12F, 0xc, 0}, {0x130, 0xaa, 0}, {0x131, 0x2, 0}, {0x132, 0, 0}, {0x133, 0x10, 0}, {0x134, 0x1, 0}, {0x135, 0, 0}, {0x136, 0, 0}, {0x137, 0x80, 0}, {0x138, 0x60, 0}, {0x139, 0x44, 0}, {0x13A, 0x55, 0}, {0x13B, 0x1, 0}, {0x13C, 0x55, 0}, {0x13D, 0x1, 0}, {0x13E, 0x5, 0}, {0x13F, 0x55, 0}, {0x140, 0x55, 0}, {0x148, 0, 0}, {0x149, 0, 0}, {0x14A, 0, 0}, {0x14B, 0, 0}, {0x14C, 0, 0}, {0x14D, 0, 0}, {0x14E, 0x1, 1}, {0x14F, 0, 0}, {0x150, 0, 0}, {0x154, 0xc, 0}, {0x155, 0xc, 0}, {0x156, 0xc, 0}, {0x157, 0, 0}, {0x158, 0x2b, 0}, {0x159, 0x84, 0}, {0x15A, 0x15, 0}, {0x15B, 0xf, 0}, {0x15C, 0, 0}, {0x15D, 0, 0}, {0x15E, 0, 1}, {0x15F, 0, 1}, {0x160, 0, 1}, {0x161, 0, 1}, {0x162, 0, 1}, {0x163, 0, 1}, {0x164, 0, 0}, {0x165, 0, 0}, {0x166, 0, 0}, {0x167, 0, 0}, {0x168, 0, 0}, {0x169, 0, 0}, {0x16A, 0, 1}, {0x16B, 0, 1}, {0x16C, 0, 1}, {0x16D, 0, 0}, {0x170, 0, 0}, {0x171, 0x77, 0}, {0x172, 0x77, 0}, {0x173, 0x77, 0}, {0x174, 0x77, 0}, {0x175, 0, 0}, {0x176, 0x3, 0}, {0x177, 0x37, 0}, {0x178, 0x3, 0}, {0x179, 0, 0}, {0x17B, 0x21, 0}, {0x17C, 0, 0}, {0x17D, 0xaa, 0}, {0x17E, 0, 0}, {0x190, 0, 0}, {0x191, 0x77, 0}, {0x192, 0x77, 0}, {0x193, 0x77, 0}, {0x194, 0x77, 0}, {0x195, 0, 0}, {0x196, 0x3, 0}, {0x197, 0x37, 0}, {0x198, 0x3, 0}, {0x199, 0, 0}, {0x19B, 0x21, 0}, {0x19C, 0, 0}, {0x19D, 0xaa, 0}, {0x19E, 0, 0}, {0x1A1, 0x2, 0}, {0x1A2, 0xf, 0}, {0x1A3, 0xf, 0}, {0x1A4, 0, 1}, {0x1A5, 0, 1}, {0x1A6, 0, 1}, {0x1A7, 0x2, 0}, {0x1A8, 0xf, 0}, {0x1A9, 0xf, 0}, {0x1AA, 0, 1}, {0x1AB, 0, 1}, {0x1AC, 0, 1}, {0x1AD, 0x84, 0}, {0x1AE, 0x60, 0}, {0x1AF, 0x47, 0}, {0x1B0, 0x47, 0}, {0x1B1, 0, 0}, {0x1B2, 0, 0}, {0x1B3, 0, 0}, {0x1B4, 0, 0}, {0x1B5, 0, 0}, {0x1B6, 0, 0}, {0x1B7, 0xc, 1}, {0x1B8, 0, 0}, {0x1B9, 0, 0}, {0x1BA, 0, 0}, {0x1BB, 0, 0}, {0x1BC, 0, 0}, {0x1BD, 0, 0}, {0x1BE, 0, 0}, {0x1BF, 0, 0}, {0x1C0, 0, 0}, {0x1C1, 0x1, 1}, {0x1C2, 0x80, 1}, {0x1C3, 0, 0}, {0x1C4, 0, 0}, {0x1C5, 0, 0}, {0x1C6, 0, 0}, {0x1C7, 0, 0}, {0x1C8, 0, 0}, {0x1C9, 0, 0}, {0x1CA, 0, 0}, {0xFFFF, 0, 0} }; static struct radio_20xx_regs regs_2057_rev7[] = { {0x00, 0, 1}, {0x01, 0x57, 1}, {0x02, 0x20, 1}, {0x03, 0x1f, 0}, {0x04, 0x4, 0}, {0x05, 0x2, 0}, {0x06, 0x1, 0}, {0x07, 0x1, 0}, {0x08, 0x1, 0}, {0x09, 0x69, 0}, {0x0A, 0x66, 0}, {0x0B, 0x6, 0}, {0x0C, 0x18, 0}, {0x0D, 0x3, 0}, {0x0E, 0x20, 0}, {0x0F, 0x20, 0}, {0x10, 0, 0}, {0x11, 0x7c, 0}, {0x12, 0x42, 0}, {0x13, 0xbd, 0}, {0x14, 0x7, 0}, {0x15, 0x87, 0}, {0x16, 0x8, 0}, {0x17, 0x17, 0}, {0x18, 0x7, 0}, {0x19, 0, 0}, {0x1A, 0x2, 0}, {0x1B, 0x13, 0}, {0x1C, 0x3e, 0}, {0x1D, 0x3e, 0}, {0x1E, 0x96, 0}, {0x1F, 0x4, 0}, {0x20, 0, 0}, {0x21, 0, 0}, {0x22, 0x17, 0}, {0x23, 0x6, 0}, {0x24, 0x1, 0}, {0x25, 0x6, 0}, {0x26, 0x4, 0}, {0x27, 0xd, 0}, {0x28, 0xd, 0}, {0x29, 0x30, 0}, {0x2A, 0x32, 0}, {0x2B, 0x8, 0}, {0x2C, 0x1c, 0}, {0x2D, 0x2, 0}, {0x2E, 0x4, 0}, {0x2F, 0x7f, 0}, {0x30, 0x27, 0}, {0x31, 0, 1}, {0x32, 0, 1}, {0x33, 0, 1}, {0x34, 0, 0}, {0x35, 0x20, 0}, {0x36, 0x18, 0}, {0x37, 0x7, 0}, {0x38, 0x66, 0}, {0x39, 0x66, 0}, {0x3A, 0x66, 0}, {0x3B, 0x66, 0}, {0x3C, 0xff, 0}, {0x3D, 0xff, 0}, {0x3E, 0xff, 0}, {0x3F, 0xff, 0}, {0x40, 0x16, 0}, {0x41, 0x7, 0}, {0x42, 0x19, 0}, {0x43, 0x7, 0}, {0x44, 0x6, 0}, {0x45, 0x3, 0}, {0x46, 0x1, 0}, {0x47, 0x7, 0}, {0x48, 0x33, 0}, {0x49, 0x5, 0}, {0x4A, 0x77, 0}, {0x4B, 0x66, 0}, {0x4C, 0x66, 0}, {0x4D, 0, 0}, {0x4E, 0x4, 0}, {0x4F, 0xc, 0}, {0x50, 0, 0}, {0x51, 0x70, 1}, {0x56, 0x7, 0}, {0x57, 0, 0}, {0x58, 0, 0}, {0x59, 0x88, 1}, {0x5A, 0, 0}, {0x5B, 0x1f, 0}, {0x5C, 0x20, 1}, {0x5D, 0x1, 0}, {0x5E, 0x30, 0}, {0x5F, 0x70, 0}, {0x60, 0, 0}, {0x61, 0, 0}, {0x62, 0x33, 1}, {0x63, 0xf, 1}, {0x64, 0x13, 1}, {0x65, 0, 0}, {0x66, 0xee, 1}, {0x69, 0, 0}, {0x6A, 0x7e, 0}, {0x6B, 0x3f, 0}, {0x6C, 0x7f, 0}, {0x6D, 0x78, 0}, {0x6E, 0x58, 1}, {0x6F, 0x88, 0}, {0x70, 0x8, 0}, {0x71, 0xf, 0}, {0x72, 0xbc, 0}, {0x73, 0x8, 0}, {0x74, 0x60, 0}, {0x75, 0x13, 1}, {0x76, 0x70, 0}, {0x77, 0, 0}, {0x78, 0, 0}, {0x79, 0, 0}, {0x7A, 0x33, 0}, {0x7B, 0x13, 1}, {0x7C, 0x14, 1}, {0x7D, 0xee, 1}, {0x80, 0x3c, 0}, {0x81, 0x1, 1}, {0x82, 0xa, 0}, {0x83, 0x9d, 0}, {0x84, 0xa, 0}, {0x85, 0, 0}, {0x86, 0x40, 0}, {0x87, 0x40, 0}, {0x88, 0x88, 0}, {0x89, 0x10, 0}, {0x8A, 0xf0, 0}, {0x8B, 0x10, 0}, {0x8C, 0xf0, 0}, {0x8D, 0, 0}, {0x8E, 0, 0}, {0x8F, 0x10, 0}, {0x90, 0x55, 0}, {0x91, 0x3f, 1}, {0x92, 0x36, 1}, {0x93, 0, 0}, {0x94, 0, 0}, {0x95, 0, 0}, {0x96, 0x87, 0}, {0x97, 0x11, 0}, {0x98, 0, 0}, {0x99, 0x33, 0}, {0x9A, 0x88, 0}, {0x9B, 0, 0}, {0x9C, 0x87, 0}, {0x9D, 0x11, 0}, {0x9E, 0, 0}, {0x9F, 0x33, 0}, {0xA0, 0x88, 0}, {0xA1, 0x20, 1}, {0xA2, 0x3f, 0}, {0xA3, 0x44, 0}, {0xA4, 0x8c, 0}, {0xA5, 0x6c, 0}, {0xA6, 0x22, 0}, {0xA7, 0xbe, 0}, {0xA8, 0x55, 0}, {0xAA, 0xc, 0}, {0xAB, 0xaa, 0}, {0xAC, 0x2, 0}, {0xAD, 0, 0}, {0xAE, 0x10, 0}, {0xAF, 0x1, 0}, {0xB0, 0, 0}, {0xB1, 0, 0}, {0xB2, 0x80, 0}, {0xB3, 0x60, 0}, {0xB4, 0x44, 0}, {0xB5, 0x55, 0}, {0xB6, 0x1, 0}, {0xB7, 0x55, 0}, {0xB8, 0x1, 0}, {0xB9, 0x5, 0}, {0xBA, 0x55, 0}, {0xBB, 0x55, 0}, {0xC1, 0, 0}, {0xC2, 0, 0}, {0xC3, 0, 0}, {0xC4, 0, 0}, {0xC5, 0, 0}, {0xC6, 0, 0}, {0xC7, 0, 0}, {0xC8, 0, 0}, {0xC9, 0, 0}, {0xCA, 0, 0}, {0xCB, 0, 0}, {0xCC, 0, 0}, {0xCD, 0, 0}, {0xCE, 0x5e, 0}, {0xCF, 0xc, 0}, {0xD0, 0xc, 0}, {0xD1, 0xc, 0}, {0xD2, 0, 0}, {0xD3, 0x2b, 0}, {0xD4, 0xc, 0}, {0xD5, 0, 0}, {0xD6, 0x70, 1}, {0xDB, 0x7, 0}, {0xDC, 0, 0}, {0xDD, 0, 0}, {0xDE, 0x88, 1}, {0xDF, 0, 0}, {0xE0, 0x1f, 0}, {0xE1, 0x20, 1}, {0xE2, 0x1, 0}, {0xE3, 0x30, 0}, {0xE4, 0x70, 0}, {0xE5, 0, 0}, {0xE6, 0, 0}, {0xE7, 0x33, 0}, {0xE8, 0xf, 1}, {0xE9, 0x13, 1}, {0xEA, 0, 0}, {0xEB, 0xee, 1}, {0xEE, 0, 0}, {0xEF, 0x7e, 0}, {0xF0, 0x3f, 0}, {0xF1, 0x7f, 0}, {0xF2, 0x78, 0}, {0xF3, 0x58, 1}, {0xF4, 0x88, 0}, {0xF5, 0x8, 0}, {0xF6, 0xf, 0}, {0xF7, 0xbc, 0}, {0xF8, 0x8, 0}, {0xF9, 0x60, 0}, {0xFA, 0x13, 1}, {0xFB, 0x70, 0}, {0xFC, 0, 0}, {0xFD, 0, 0}, {0xFE, 0, 0}, {0xFF, 0x33, 0}, {0x100, 0x13, 1}, {0x101, 0x14, 1}, {0x102, 0xee, 1}, {0x105, 0x3c, 0}, {0x106, 0x1, 1}, {0x107, 0xa, 0}, {0x108, 0x9d, 0}, {0x109, 0xa, 0}, {0x10A, 0, 0}, {0x10B, 0x40, 0}, {0x10C, 0x40, 0}, {0x10D, 0x88, 0}, {0x10E, 0x10, 0}, {0x10F, 0xf0, 0}, {0x110, 0x10, 0}, {0x111, 0xf0, 0}, {0x112, 0, 0}, {0x113, 0, 0}, {0x114, 0x10, 0}, {0x115, 0x55, 0}, {0x116, 0x3f, 1}, {0x117, 0x36, 1}, {0x118, 0, 0}, {0x119, 0, 0}, {0x11A, 0, 0}, {0x11B, 0x87, 0}, {0x11C, 0x11, 0}, {0x11D, 0, 0}, {0x11E, 0x33, 0}, {0x11F, 0x88, 0}, {0x120, 0, 0}, {0x121, 0x87, 0}, {0x122, 0x11, 0}, {0x123, 0, 0}, {0x124, 0x33, 0}, {0x125, 0x88, 0}, {0x126, 0x20, 1}, {0x127, 0x3f, 0}, {0x128, 0x44, 0}, {0x129, 0x8c, 0}, {0x12A, 0x6c, 0}, {0x12B, 0x22, 0}, {0x12C, 0xbe, 0}, {0x12D, 0x55, 0}, {0x12F, 0xc, 0}, {0x130, 0xaa, 0}, {0x131, 0x2, 0}, {0x132, 0, 0}, {0x133, 0x10, 0}, {0x134, 0x1, 0}, {0x135, 0, 0}, {0x136, 0, 0}, {0x137, 0x80, 0}, {0x138, 0x60, 0}, {0x139, 0x44, 0}, {0x13A, 0x55, 0}, {0x13B, 0x1, 0}, {0x13C, 0x55, 0}, {0x13D, 0x1, 0}, {0x13E, 0x5, 0}, {0x13F, 0x55, 0}, {0x140, 0x55, 0}, {0x146, 0, 0}, {0x147, 0, 0}, {0x148, 0, 0}, {0x149, 0, 0}, {0x14A, 0, 0}, {0x14B, 0, 0}, {0x14C, 0, 0}, {0x14D, 0, 0}, {0x14E, 0, 0}, {0x14F, 0, 0}, {0x150, 0, 0}, {0x151, 0, 0}, {0x154, 0xc, 0}, {0x155, 0xc, 0}, {0x156, 0xc, 0}, {0x157, 0, 0}, {0x158, 0x2b, 0}, {0x159, 0x84, 0}, {0x15A, 0x15, 0}, {0x15B, 0xf, 0}, {0x15C, 0, 0}, {0x15D, 0, 0}, {0x15E, 0, 1}, {0x15F, 0, 1}, {0x160, 0, 1}, {0x161, 0, 1}, {0x162, 0, 1}, {0x163, 0, 1}, {0x164, 0, 0}, {0x165, 0, 0}, {0x166, 0, 0}, {0x167, 0, 0}, {0x168, 0, 0}, {0x169, 0, 0}, {0x16A, 0, 1}, {0x16B, 0, 1}, {0x16C, 0, 1}, {0x16D, 0, 0}, {0x170, 0, 0}, {0x171, 0x77, 0}, {0x172, 0x77, 0}, {0x173, 0x77, 0}, {0x174, 0x77, 0}, {0x175, 0, 0}, {0x176, 0x3, 0}, {0x177, 0x37, 0}, {0x178, 0x3, 0}, {0x179, 0, 0}, {0x17A, 0x21, 0}, {0x17B, 0x21, 0}, {0x17C, 0, 0}, {0x17D, 0xaa, 0}, {0x17E, 0, 0}, {0x17F, 0xaa, 0}, {0x180, 0, 0}, {0x190, 0, 0}, {0x191, 0x77, 0}, {0x192, 0x77, 0}, {0x193, 0x77, 0}, {0x194, 0x77, 0}, {0x195, 0, 0}, {0x196, 0x3, 0}, {0x197, 0x37, 0}, {0x198, 0x3, 0}, {0x199, 0, 0}, {0x19A, 0x21, 0}, {0x19B, 0x21, 0}, {0x19C, 0, 0}, {0x19D, 0xaa, 0}, {0x19E, 0, 0}, {0x19F, 0xaa, 0}, {0x1A0, 0, 0}, {0x1A1, 0x2, 0}, {0x1A2, 0xf, 0}, {0x1A3, 0xf, 0}, {0x1A4, 0, 1}, {0x1A5, 0, 1}, {0x1A6, 0, 1}, {0x1A7, 0x2, 0}, {0x1A8, 0xf, 0}, {0x1A9, 0xf, 0}, {0x1AA, 0, 1}, {0x1AB, 0, 1}, {0x1AC, 0, 1}, {0x1AD, 0x84, 0}, {0x1AE, 0x60, 0}, {0x1AF, 0x47, 0}, {0x1B0, 0x47, 0}, {0x1B1, 0, 0}, {0x1B2, 0, 0}, {0x1B3, 0, 0}, {0x1B4, 0, 0}, {0x1B5, 0, 0}, {0x1B6, 0, 0}, {0x1B7, 0x5, 1}, {0x1B8, 0, 0}, {0x1B9, 0, 0}, {0x1BA, 0, 0}, {0x1BB, 0, 0}, {0x1BC, 0, 0}, {0x1BD, 0, 0}, {0x1BE, 0, 0}, {0x1BF, 0, 0}, {0x1C0, 0, 0}, {0x1C1, 0, 0}, {0x1C2, 0xa0, 1}, {0x1C3, 0, 0}, {0x1C4, 0, 0}, {0x1C5, 0, 0}, {0x1C6, 0, 0}, {0x1C7, 0, 0}, {0x1C8, 0, 0}, {0x1C9, 0, 0}, {0x1CA, 0, 0}, {0xFFFF, 0, 0} }; static struct radio_20xx_regs regs_2057_rev8[] = { {0x00, 0x8, 1}, {0x01, 0x57, 1}, {0x02, 0x20, 1}, {0x03, 0x1f, 0}, {0x04, 0x4, 0}, {0x05, 0x2, 0}, {0x06, 0x1, 0}, {0x07, 0x1, 0}, {0x08, 0x1, 0}, {0x09, 0x69, 0}, {0x0A, 0x66, 0}, {0x0B, 0x6, 0}, {0x0C, 0x18, 0}, {0x0D, 0x3, 0}, {0x0E, 0x20, 0}, {0x0F, 0x20, 0}, {0x10, 0, 0}, {0x11, 0x7c, 0}, {0x12, 0x42, 0}, {0x13, 0xbd, 0}, {0x14, 0x7, 0}, {0x15, 0x87, 0}, {0x16, 0x8, 0}, {0x17, 0x17, 0}, {0x18, 0x7, 0}, {0x19, 0, 0}, {0x1A, 0x2, 0}, {0x1B, 0x13, 0}, {0x1C, 0x3e, 0}, {0x1D, 0x3e, 0}, {0x1E, 0x96, 0}, {0x1F, 0x4, 0}, {0x20, 0, 0}, {0x21, 0, 0}, {0x22, 0x17, 0}, {0x23, 0x6, 0}, {0x24, 0x1, 0}, {0x25, 0x6, 0}, {0x26, 0x4, 0}, {0x27, 0xd, 0}, {0x28, 0xd, 0}, {0x29, 0x30, 0}, {0x2A, 0x32, 0}, {0x2B, 0x8, 0}, {0x2C, 0x1c, 0}, {0x2D, 0x2, 0}, {0x2E, 0x4, 0}, {0x2F, 0x7f, 0}, {0x30, 0x27, 0}, {0x31, 0, 1}, {0x32, 0, 1}, {0x33, 0, 1}, {0x34, 0, 0}, {0x35, 0x20, 0}, {0x36, 0x18, 0}, {0x37, 0x7, 0}, {0x38, 0x66, 0}, {0x39, 0x66, 0}, {0x3A, 0x66, 0}, {0x3B, 0x66, 0}, {0x3C, 0xff, 0}, {0x3D, 0xff, 0}, {0x3E, 0xff, 0}, {0x3F, 0xff, 0}, {0x40, 0x16, 0}, {0x41, 0x7, 0}, {0x42, 0x19, 0}, {0x43, 0x7, 0}, {0x44, 0x6, 0}, {0x45, 0x3, 0}, {0x46, 0x1, 0}, {0x47, 0x7, 0}, {0x48, 0x33, 0}, {0x49, 0x5, 0}, {0x4A, 0x77, 0}, {0x4B, 0x66, 0}, {0x4C, 0x66, 0}, {0x4D, 0, 0}, {0x4E, 0x4, 0}, {0x4F, 0xc, 0}, {0x50, 0, 0}, {0x51, 0x70, 1}, {0x56, 0x7, 0}, {0x57, 0, 0}, {0x58, 0, 0}, {0x59, 0x88, 1}, {0x5A, 0, 0}, {0x5B, 0x1f, 0}, {0x5C, 0x20, 1}, {0x5D, 0x1, 0}, {0x5E, 0x30, 0}, {0x5F, 0x70, 0}, {0x60, 0, 0}, {0x61, 0, 0}, {0x62, 0x33, 1}, {0x63, 0xf, 1}, {0x64, 0xf, 1}, {0x65, 0, 0}, {0x66, 0x11, 0}, {0x69, 0, 0}, {0x6A, 0x7e, 0}, {0x6B, 0x3f, 0}, {0x6C, 0x7f, 0}, {0x6D, 0x78, 0}, {0x6E, 0x58, 1}, {0x6F, 0x88, 0}, {0x70, 0x8, 0}, {0x71, 0xf, 0}, {0x72, 0xbc, 0}, {0x73, 0x8, 0}, {0x74, 0x60, 0}, {0x75, 0x13, 1}, {0x76, 0x70, 0}, {0x77, 0, 0}, {0x78, 0, 0}, {0x79, 0, 0}, {0x7A, 0x33, 0}, {0x7B, 0x13, 1}, {0x7C, 0xf, 1}, {0x7D, 0xee, 1}, {0x80, 0x3c, 0}, {0x81, 0x1, 1}, {0x82, 0xa, 0}, {0x83, 0x9d, 0}, {0x84, 0xa, 0}, {0x85, 0, 0}, {0x86, 0x40, 0}, {0x87, 0x40, 0}, {0x88, 0x88, 0}, {0x89, 0x10, 0}, {0x8A, 0xf0, 0}, {0x8B, 0x10, 0}, {0x8C, 0xf0, 0}, {0x8D, 0, 0}, {0x8E, 0, 0}, {0x8F, 0x10, 0}, {0x90, 0x55, 0}, {0x91, 0x3f, 1}, {0x92, 0x36, 1}, {0x93, 0, 0}, {0x94, 0, 0}, {0x95, 0, 0}, {0x96, 0x87, 0}, {0x97, 0x11, 0}, {0x98, 0, 0}, {0x99, 0x33, 0}, {0x9A, 0x88, 0}, {0x9B, 0, 0}, {0x9C, 0x87, 0}, {0x9D, 0x11, 0}, {0x9E, 0, 0}, {0x9F, 0x33, 0}, {0xA0, 0x88, 0}, {0xA1, 0x20, 1}, {0xA2, 0x3f, 0}, {0xA3, 0x44, 0}, {0xA4, 0x8c, 0}, {0xA5, 0x6c, 0}, {0xA6, 0x22, 0}, {0xA7, 0xbe, 0}, {0xA8, 0x55, 0}, {0xAA, 0xc, 0}, {0xAB, 0xaa, 0}, {0xAC, 0x2, 0}, {0xAD, 0, 0}, {0xAE, 0x10, 0}, {0xAF, 0x1, 0}, {0xB0, 0, 0}, {0xB1, 0, 0}, {0xB2, 0x80, 0}, {0xB3, 0x60, 0}, {0xB4, 0x44, 0}, {0xB5, 0x55, 0}, {0xB6, 0x1, 0}, {0xB7, 0x55, 0}, {0xB8, 0x1, 0}, {0xB9, 0x5, 0}, {0xBA, 0x55, 0}, {0xBB, 0x55, 0}, {0xC1, 0, 0}, {0xC2, 0, 0}, {0xC3, 0, 0}, {0xC4, 0, 0}, {0xC5, 0, 0}, {0xC6, 0, 0}, {0xC7, 0, 0}, {0xC8, 0, 0}, {0xC9, 0x1, 1}, {0xCA, 0, 0}, {0xCB, 0, 0}, {0xCC, 0, 0}, {0xCD, 0, 0}, {0xCE, 0x5e, 0}, {0xCF, 0xc, 0}, {0xD0, 0xc, 0}, {0xD1, 0xc, 0}, {0xD2, 0, 0}, {0xD3, 0x2b, 0}, {0xD4, 0xc, 0}, {0xD5, 0, 0}, {0xD6, 0x70, 1}, {0xDB, 0x7, 0}, {0xDC, 0, 0}, {0xDD, 0, 0}, {0xDE, 0x88, 1}, {0xDF, 0, 0}, {0xE0, 0x1f, 0}, {0xE1, 0x20, 1}, {0xE2, 0x1, 0}, {0xE3, 0x30, 0}, {0xE4, 0x70, 0}, {0xE5, 0, 0}, {0xE6, 0, 0}, {0xE7, 0x33, 0}, {0xE8, 0xf, 1}, {0xE9, 0xf, 1}, {0xEA, 0, 0}, {0xEB, 0x11, 0}, {0xEE, 0, 0}, {0xEF, 0x7e, 0}, {0xF0, 0x3f, 0}, {0xF1, 0x7f, 0}, {0xF2, 0x78, 0}, {0xF3, 0x58, 1}, {0xF4, 0x88, 0}, {0xF5, 0x8, 0}, {0xF6, 0xf, 0}, {0xF7, 0xbc, 0}, {0xF8, 0x8, 0}, {0xF9, 0x60, 0}, {0xFA, 0x13, 1}, {0xFB, 0x70, 0}, {0xFC, 0, 0}, {0xFD, 0, 0}, {0xFE, 0, 0}, {0xFF, 0x33, 0}, {0x100, 0x13, 1}, {0x101, 0xf, 1}, {0x102, 0xee, 1}, {0x105, 0x3c, 0}, {0x106, 0x1, 1}, {0x107, 0xa, 0}, {0x108, 0x9d, 0}, {0x109, 0xa, 0}, {0x10A, 0, 0}, {0x10B, 0x40, 0}, {0x10C, 0x40, 0}, {0x10D, 0x88, 0}, {0x10E, 0x10, 0}, {0x10F, 0xf0, 0}, {0x110, 0x10, 0}, {0x111, 0xf0, 0}, {0x112, 0, 0}, {0x113, 0, 0}, {0x114, 0x10, 0}, {0x115, 0x55, 0}, {0x116, 0x3f, 1}, {0x117, 0x36, 1}, {0x118, 0, 0}, {0x119, 0, 0}, {0x11A, 0, 0}, {0x11B, 0x87, 0}, {0x11C, 0x11, 0}, {0x11D, 0, 0}, {0x11E, 0x33, 0}, {0x11F, 0x88, 0}, {0x120, 0, 0}, {0x121, 0x87, 0}, {0x122, 0x11, 0}, {0x123, 0, 0}, {0x124, 0x33, 0}, {0x125, 0x88, 0}, {0x126, 0x20, 1}, {0x127, 0x3f, 0}, {0x128, 0x44, 0}, {0x129, 0x8c, 0}, {0x12A, 0x6c, 0}, {0x12B, 0x22, 0}, {0x12C, 0xbe, 0}, {0x12D, 0x55, 0}, {0x12F, 0xc, 0}, {0x130, 0xaa, 0}, {0x131, 0x2, 0}, {0x132, 0, 0}, {0x133, 0x10, 0}, {0x134, 0x1, 0}, {0x135, 0, 0}, {0x136, 0, 0}, {0x137, 0x80, 0}, {0x138, 0x60, 0}, {0x139, 0x44, 0}, {0x13A, 0x55, 0}, {0x13B, 0x1, 0}, {0x13C, 0x55, 0}, {0x13D, 0x1, 0}, {0x13E, 0x5, 0}, {0x13F, 0x55, 0}, {0x140, 0x55, 0}, {0x146, 0, 0}, {0x147, 0, 0}, {0x148, 0, 0}, {0x149, 0, 0}, {0x14A, 0, 0}, {0x14B, 0, 0}, {0x14C, 0, 0}, {0x14D, 0, 0}, {0x14E, 0x1, 1}, {0x14F, 0, 0}, {0x150, 0, 0}, {0x151, 0, 0}, {0x154, 0xc, 0}, {0x155, 0xc, 0}, {0x156, 0xc, 0}, {0x157, 0, 0}, {0x158, 0x2b, 0}, {0x159, 0x84, 0}, {0x15A, 0x15, 0}, {0x15B, 0xf, 0}, {0x15C, 0, 0}, {0x15D, 0, 0}, {0x15E, 0, 1}, {0x15F, 0, 1}, {0x160, 0, 1}, {0x161, 0, 1}, {0x162, 0, 1}, {0x163, 0, 1}, {0x164, 0, 0}, {0x165, 0, 0}, {0x166, 0, 0}, {0x167, 0, 0}, {0x168, 0, 0}, {0x169, 0, 0}, {0x16A, 0, 1}, {0x16B, 0, 1}, {0x16C, 0, 1}, {0x16D, 0, 0}, {0x170, 0, 0}, {0x171, 0x77, 0}, {0x172, 0x77, 0}, {0x173, 0x77, 0}, {0x174, 0x77, 0}, {0x175, 0, 0}, {0x176, 0x3, 0}, {0x177, 0x37, 0}, {0x178, 0x3, 0}, {0x179, 0, 0}, {0x17A, 0x21, 0}, {0x17B, 0x21, 0}, {0x17C, 0, 0}, {0x17D, 0xaa, 0}, {0x17E, 0, 0}, {0x17F, 0xaa, 0}, {0x180, 0, 0}, {0x190, 0, 0}, {0x191, 0x77, 0}, {0x192, 0x77, 0}, {0x193, 0x77, 0}, {0x194, 0x77, 0}, {0x195, 0, 0}, {0x196, 0x3, 0}, {0x197, 0x37, 0}, {0x198, 0x3, 0}, {0x199, 0, 0}, {0x19A, 0x21, 0}, {0x19B, 0x21, 0}, {0x19C, 0, 0}, {0x19D, 0xaa, 0}, {0x19E, 0, 0}, {0x19F, 0xaa, 0}, {0x1A0, 0, 0}, {0x1A1, 0x2, 0}, {0x1A2, 0xf, 0}, {0x1A3, 0xf, 0}, {0x1A4, 0, 1}, {0x1A5, 0, 1}, {0x1A6, 0, 1}, {0x1A7, 0x2, 0}, {0x1A8, 0xf, 0}, {0x1A9, 0xf, 0}, {0x1AA, 0, 1}, {0x1AB, 0, 1}, {0x1AC, 0, 1}, {0x1AD, 0x84, 0}, {0x1AE, 0x60, 0}, {0x1AF, 0x47, 0}, {0x1B0, 0x47, 0}, {0x1B1, 0, 0}, {0x1B2, 0, 0}, {0x1B3, 0, 0}, {0x1B4, 0, 0}, {0x1B5, 0, 0}, {0x1B6, 0, 0}, {0x1B7, 0x5, 1}, {0x1B8, 0, 0}, {0x1B9, 0, 0}, {0x1BA, 0, 0}, {0x1BB, 0, 0}, {0x1BC, 0, 0}, {0x1BD, 0, 0}, {0x1BE, 0, 0}, {0x1BF, 0, 0}, {0x1C0, 0, 0}, {0x1C1, 0, 0}, {0x1C2, 0xa0, 1}, {0x1C3, 0, 0}, {0x1C4, 0, 0}, {0x1C5, 0, 0}, {0x1C6, 0, 0}, {0x1C7, 0, 0}, {0x1C8, 0, 0}, {0x1C9, 0, 0}, {0x1CA, 0, 0}, {0xFFFF, 0, 0} }; static s16 nphy_def_lnagains[] = { -2, 10, 19, 25 }; static s32 nphy_lnagain_est0[] = { -315, 40370 }; static s32 nphy_lnagain_est1[] = { -224, 23242 }; static const u16 tbl_iqcal_gainparams_nphy[2][NPHY_IQCAL_NUMGAINS][8] = { { {0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69}, {0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69}, {0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68}, {0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67}, {0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66}, {0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65}, {0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65}, {0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65}, {0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65} }, { {0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79}, {0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79}, {0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79}, {0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78}, {0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78}, {0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78}, {0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78}, {0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78}, {0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78} } }; static const u32 nphy_tpc_txgain[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, 0x03902942, 0x03902844, 0x03902842, 0x03902744, 0x03902742, 0x03902644, 0x03902642, 0x03902544, 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, 0x03802a42, 0x03802944, 0x03802942, 0x03802844, 0x03802842, 0x03802744, 0x03802742, 0x03802644, 0x03802642, 0x03802544, 0x03802542, 0x03802444, 0x03802442, 0x03802344, 0x03802342, 0x03802244, 0x03802242, 0x03802144, 0x03802142, 0x03802044, 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, 0x03801a42, 0x03801944, 0x03801942, 0x03801844, 0x03801842, 0x03801744, 0x03801742, 0x03801644, 0x03801642, 0x03801544, 0x03801542, 0x03801444, 0x03801442, 0x03801344, 0x03801342, 0x00002b00 }; static const u16 nphy_tpc_loscale[] = { 256, 256, 271, 271, 287, 256, 256, 271, 271, 287, 287, 304, 304, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 383, 383, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 383, 383, 406, 406, 430, 430, 455, 455, 482, 482, 511, 511, 541, 541, 573, 573, 607, 607, 643, 643, 681, 681, 722, 722, 764, 764, 810, 810, 858, 858, 908, 908, 962, 962, 1019, 1019, 256 }; static u32 nphy_tpc_txgain_ipa[] = { 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025 }; static u32 nphy_tpc_txgain_ipa_rev5[] = { 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025 }; static u32 nphy_tpc_txgain_ipa_rev6[] = { 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025 }; static u32 nphy_tpc_txgain_ipa_2g_2057rev3[] = { 0x70ff0040, 0x70f7003e, 0x70ef003b, 0x70e70039, 0x70df0037, 0x70d70036, 0x70cf0033, 0x70c70032, 0x70bf0031, 0x70b7002f, 0x70af002e, 0x70a7002d, 0x709f002d, 0x7097002c, 0x708f002c, 0x7087002c, 0x707f002b, 0x7077002c, 0x706f002c, 0x7067002d, 0x705f002e, 0x705f002b, 0x705f0029, 0x7057002a, 0x70570028, 0x704f002a, 0x7047002c, 0x7047002a, 0x70470028, 0x70470026, 0x70470024, 0x70470022, 0x7047001f, 0x70370027, 0x70370024, 0x70370022, 0x70370020, 0x7037001f, 0x7037001d, 0x7037001b, 0x7037001a, 0x70370018, 0x70370017, 0x7027001e, 0x7027001d, 0x7027001a, 0x701f0024, 0x701f0022, 0x701f0020, 0x701f001f, 0x701f001d, 0x701f001b, 0x701f001a, 0x701f0018, 0x701f0017, 0x701f0015, 0x701f0014, 0x701f0013, 0x701f0012, 0x701f0011, 0x70170019, 0x70170018, 0x70170016, 0x70170015, 0x70170014, 0x70170013, 0x70170012, 0x70170010, 0x70170010, 0x7017000f, 0x700f001d, 0x700f001b, 0x700f001a, 0x700f0018, 0x700f0017, 0x700f0015, 0x700f0015, 0x700f0013, 0x700f0013, 0x700f0011, 0x700f0010, 0x700f0010, 0x700f000f, 0x700f000e, 0x700f000d, 0x700f000c, 0x700f000b, 0x700f000b, 0x700f000b, 0x700f000a, 0x700f0009, 0x700f0009, 0x700f0009, 0x700f0008, 0x700f0007, 0x700f0007, 0x700f0006, 0x700f0006, 0x700f0006, 0x700f0006, 0x700f0005, 0x700f0005, 0x700f0005, 0x700f0004, 0x700f0004, 0x700f0004, 0x700f0004, 0x700f0004, 0x700f0004, 0x700f0003, 0x700f0003, 0x700f0003, 0x700f0003, 0x700f0002, 0x700f0002, 0x700f0002, 0x700f0002, 0x700f0002, 0x700f0002, 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001 }; static u32 nphy_tpc_txgain_ipa_2g_2057rev4n6[] = { 0xf0ff0040, 0xf0f7003e, 0xf0ef003b, 0xf0e70039, 0xf0df0037, 0xf0d70036, 0xf0cf0033, 0xf0c70032, 0xf0bf0031, 0xf0b7002f, 0xf0af002e, 0xf0a7002d, 0xf09f002d, 0xf097002c, 0xf08f002c, 0xf087002c, 0xf07f002b, 0xf077002c, 0xf06f002c, 0xf067002d, 0xf05f002e, 0xf05f002b, 0xf05f0029, 0xf057002a, 0xf0570028, 0xf04f002a, 0xf047002c, 0xf047002a, 0xf0470028, 0xf0470026, 0xf0470024, 0xf0470022, 0xf047001f, 0xf0370027, 0xf0370024, 0xf0370022, 0xf0370020, 0xf037001f, 0xf037001d, 0xf037001b, 0xf037001a, 0xf0370018, 0xf0370017, 0xf027001e, 0xf027001d, 0xf027001a, 0xf01f0024, 0xf01f0022, 0xf01f0020, 0xf01f001f, 0xf01f001d, 0xf01f001b, 0xf01f001a, 0xf01f0018, 0xf01f0017, 0xf01f0015, 0xf01f0014, 0xf01f0013, 0xf01f0012, 0xf01f0011, 0xf0170019, 0xf0170018, 0xf0170016, 0xf0170015, 0xf0170014, 0xf0170013, 0xf0170012, 0xf0170010, 0xf0170010, 0xf017000f, 0xf00f001d, 0xf00f001b, 0xf00f001a, 0xf00f0018, 0xf00f0017, 0xf00f0015, 0xf00f0015, 0xf00f0013, 0xf00f0013, 0xf00f0011, 0xf00f0010, 0xf00f0010, 0xf00f000f, 0xf00f000e, 0xf00f000d, 0xf00f000c, 0xf00f000b, 0xf00f000b, 0xf00f000b, 0xf00f000a, 0xf00f0009, 0xf00f0009, 0xf00f0009, 0xf00f0008, 0xf00f0007, 0xf00f0007, 0xf00f0006, 0xf00f0006, 0xf00f0006, 0xf00f0006, 0xf00f0005, 0xf00f0005, 0xf00f0005, 0xf00f0004, 0xf00f0004, 0xf00f0004, 0xf00f0004, 0xf00f0004, 0xf00f0004, 0xf00f0003, 0xf00f0003, 0xf00f0003, 0xf00f0003, 0xf00f0002, 0xf00f0002, 0xf00f0002, 0xf00f0002, 0xf00f0002, 0xf00f0002, 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001 }; static u32 nphy_tpc_txgain_ipa_2g_2057rev5[] = { 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, 0x30270027, 0x30270025, 0x30270023, 0x301f002c, 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, 0x30170028, 0x30170026, 0x30170024, 0x30170022, 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, 0x3017001a, 0x30170018, 0x30170017, 0x30170015, 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715 }; static u32 nphy_tpc_txgain_ipa_2g_2057rev7[] = { 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, 0x30270027, 0x30270025, 0x30270023, 0x301f002c, 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, 0x30170028, 0x30170026, 0x30170024, 0x30170022, 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, 0x3017001a, 0x30170018, 0x30170017, 0x30170015, 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715 }; static u32 nphy_tpc_txgain_ipa_5g[] = { 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f }; static u32 nphy_tpc_txgain_ipa_5g_2057[] = { 0x7f7f0044, 0x7f7f0040, 0x7f7f003c, 0x7f7f0039, 0x7f7f0036, 0x7e7f003c, 0x7e7f0038, 0x7e7f0035, 0x7d7f003c, 0x7d7f0039, 0x7d7f0036, 0x7d7f0033, 0x7c7f003b, 0x7c7f0037, 0x7c7f0034, 0x7b7f003a, 0x7b7f0036, 0x7b7f0033, 0x7a7f003c, 0x7a7f0039, 0x7a7f0036, 0x7a7f0033, 0x797f003b, 0x797f0038, 0x797f0035, 0x797f0032, 0x787f003b, 0x787f0038, 0x787f0035, 0x787f0032, 0x777f003a, 0x777f0037, 0x777f0034, 0x777f0031, 0x767f003a, 0x767f0036, 0x767f0033, 0x767f0031, 0x757f003a, 0x757f0037, 0x757f0034, 0x747f003c, 0x747f0039, 0x747f0036, 0x747f0033, 0x737f003b, 0x737f0038, 0x737f0035, 0x737f0032, 0x727f0039, 0x727f0036, 0x727f0033, 0x727f0030, 0x717f003a, 0x717f0037, 0x717f0034, 0x707f003b, 0x707f0038, 0x707f0035, 0x707f0032, 0x707f002f, 0x707f002d, 0x707f002a, 0x707f0028, 0x707f0025, 0x707f0023, 0x707f0021, 0x707f0020, 0x707f001e, 0x707f001c, 0x707f001b, 0x707f0019, 0x707f0018, 0x707f0016, 0x707f0015, 0x707f0014, 0x707f0013, 0x707f0012, 0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e, 0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b, 0x707f000b, 0x707f000a, 0x707f0009, 0x707f0009, 0x707f0008, 0x707f0008, 0x707f0007, 0x707f0007, 0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006, 0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004, 0x707f0004, 0x707f0004, 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001 }; static u32 nphy_tpc_txgain_ipa_5g_2057rev7[] = { 0x6f7f0031, 0x6f7f002e, 0x6f7f002c, 0x6f7f002a, 0x6f7f0027, 0x6e7f002e, 0x6e7f002c, 0x6e7f002a, 0x6d7f0030, 0x6d7f002d, 0x6d7f002a, 0x6d7f0028, 0x6c7f0030, 0x6c7f002d, 0x6c7f002b, 0x6b7f002e, 0x6b7f002c, 0x6b7f002a, 0x6b7f0027, 0x6a7f002e, 0x6a7f002c, 0x6a7f002a, 0x697f0030, 0x697f002e, 0x697f002b, 0x697f0029, 0x687f002f, 0x687f002d, 0x687f002a, 0x687f0027, 0x677f002f, 0x677f002d, 0x677f002a, 0x667f0031, 0x667f002e, 0x667f002c, 0x667f002a, 0x657f0030, 0x657f002e, 0x657f002b, 0x657f0029, 0x647f0030, 0x647f002d, 0x647f002b, 0x647f0029, 0x637f002f, 0x637f002d, 0x637f002a, 0x627f0030, 0x627f002d, 0x627f002b, 0x627f0029, 0x617f0030, 0x617f002e, 0x617f002b, 0x617f0029, 0x607f002f, 0x607f002d, 0x607f002a, 0x607f0027, 0x607f0026, 0x607f0023, 0x607f0021, 0x607f0020, 0x607f001e, 0x607f001c, 0x607f001a, 0x607f0019, 0x607f0018, 0x607f0016, 0x607f0015, 0x607f0014, 0x607f0012, 0x607f0012, 0x607f0011, 0x607f000f, 0x607f000f, 0x607f000e, 0x607f000d, 0x607f000c, 0x607f000c, 0x607f000b, 0x607f000b, 0x607f000a, 0x607f0009, 0x607f0009, 0x607f0008, 0x607f0008, 0x607f0008, 0x607f0007, 0x607f0007, 0x607f0006, 0x607f0006, 0x607f0005, 0x607f0005, 0x607f0005, 0x607f0005, 0x607f0005, 0x607f0004, 0x607f0004, 0x607f0004, 0x607f0004, 0x607f0003, 0x607f0003, 0x607f0003, 0x607f0003, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0001, 0x607f0001, 0x607f0001, 0x607f0001, 0x607f0001, 0x607f0001, 0x607f0001 }; static s8 nphy_papd_pga_gain_delta_ipa_2g[] = { -114, -108, -98, -91, -84, -78, -70, -62, -54, -46, -39, -31, -23, -15, -8, 0 }; static s8 nphy_papd_pga_gain_delta_ipa_5g[] = { -100, -95, -89, -83, -77, -70, -63, -56, -48, -41, -33, -25, -19, -12, -6, 0 }; static s16 nphy_papd_padgain_dlt_2g_2057rev3n4[] = { -159, -113, -86, -72, -62, -54, -48, -43, -39, -35, -31, -28, -25, -23, -20, -18, -17, -15, -13, -11, -10, -8, -7, -6, -5, -4, -3, -3, -2, -1, -1, 0 }; static s16 nphy_papd_padgain_dlt_2g_2057rev5[] = { -109, -109, -82, -68, -58, -50, -44, -39, -35, -31, -28, -26, -23, -21, -19, -17, -16, -14, -13, -11, -10, -9, -8, -7, -5, -5, -4, -3, -2, -1, -1, 0 }; static s16 nphy_papd_padgain_dlt_2g_2057rev7[] = { -122, -122, -95, -80, -69, -61, -54, -49, -43, -39, -35, -32, -28, -26, -23, -21, -18, -16, -15, -13, -11, -10, -8, -7, -6, -5, -4, -3, -2, -1, -1, 0 }; static s8 nphy_papd_pgagain_dlt_5g_2057[] = { -107, -101, -92, -85, -78, -71, -62, -55, -47, -39, -32, -24, -19, -12, -6, 0 }; static s8 nphy_papd_pgagain_dlt_5g_2057rev7[] = { -110, -104, -95, -88, -81, -74, -66, -58, -50, -44, -36, -28, -23, -15, -8, 0 }; static u8 pad_gain_codes_used_2057rev5[] = { 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; static u8 pad_gain_codes_used_2057rev7[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; static u8 pad_all_gain_codes_2057[] = { 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; static u8 pga_all_gain_codes_2057[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; static u32 nphy_papd_scaltbl[] = { 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, 0x0887003c, 0x081f003f, 0x07a20043, 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d0011a, 0x01b7012a, 0x019e013c, 0x0187014f, 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, 0x011501d9, 0x010501f5, 0x00f70212, 0x00e90232, 0x00dc0253, 0x00d00276, 0x00c4029c, 0x00b902c3, 0x00af02ed, 0x00a5031a, 0x009c0349, 0x0093037a, 0x008b03af, 0x008303e7, 0x007c0422, 0x00750461, 0x006e04a3, 0x006804ea, 0x00620534, 0x005d0583, 0x005805d7, 0x0053062f, 0x004e068d, 0x004a06f1 }; static u32 nphy_tpc_txgain_rev3[] = { 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, 0x19410044, 0x19410042, 0x19410040, 0x1941003e, 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, 0x18410044, 0x18410042, 0x18410040, 0x1841003e, 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, 0x17410044, 0x17410042, 0x17410040, 0x1741003e, 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, 0x16410044, 0x16410042, 0x16410040, 0x1641003e, 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, 0x15410044, 0x15410042, 0x15410040, 0x1541003e, 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, 0x14410044, 0x14410042, 0x14410040, 0x1441003e, 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, 0x13410044, 0x13410042, 0x13410040, 0x1341003e, 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, 0x12410044, 0x12410042, 0x12410040, 0x1241003e, 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, 0x11410044, 0x11410042, 0x11410040, 0x1141003e, 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, 0x10410044, 0x10410042, 0x10410040, 0x1041003e, 0x1041003c, 0x1041003b, 0x10410039, 0x10410037 }; static u32 nphy_tpc_txgain_HiPwrEPA[] = { 0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e, 0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037, 0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e, 0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037, 0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e, 0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037, 0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e, 0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037, 0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e, 0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037, 0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e, 0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037, 0x09410044, 0x09410042, 0x09410040, 0x0941003e, 0x0941003c, 0x0941003b, 0x09410039, 0x09410037, 0x08410044, 0x08410042, 0x08410040, 0x0841003e, 0x0841003c, 0x0841003b, 0x08410039, 0x08410037, 0x07410044, 0x07410042, 0x07410040, 0x0741003e, 0x0741003c, 0x0741003b, 0x07410039, 0x07410037, 0x06410044, 0x06410042, 0x06410040, 0x0641003e, 0x0641003c, 0x0641003b, 0x06410039, 0x06410037, 0x05410044, 0x05410042, 0x05410040, 0x0541003e, 0x0541003c, 0x0541003b, 0x05410039, 0x05410037, 0x04410044, 0x04410042, 0x04410040, 0x0441003e, 0x0441003c, 0x0441003b, 0x04410039, 0x04410037, 0x03410044, 0x03410042, 0x03410040, 0x0341003e, 0x0341003c, 0x0341003b, 0x03410039, 0x03410037, 0x02410044, 0x02410042, 0x02410040, 0x0241003e, 0x0241003c, 0x0241003b, 0x02410039, 0x02410037, 0x01410044, 0x01410042, 0x01410040, 0x0141003e, 0x0141003c, 0x0141003b, 0x01410039, 0x01410037, 0x00410044, 0x00410042, 0x00410040, 0x0041003e, 0x0041003c, 0x0041003b, 0x00410039, 0x00410037 }; static u32 nphy_tpc_txgain_epa_2057rev3[] = { 0x80f90040, 0x80e10040, 0x80e1003c, 0x80c9003d, 0x80b9003c, 0x80a9003d, 0x80a1003c, 0x8099003b, 0x8091003b, 0x8089003a, 0x8081003a, 0x80790039, 0x80710039, 0x8069003a, 0x8061003b, 0x8059003d, 0x8051003f, 0x80490042, 0x8049003e, 0x8049003b, 0x8041003e, 0x8041003b, 0x8039003e, 0x8039003b, 0x80390038, 0x80390035, 0x8031003a, 0x80310036, 0x80310033, 0x8029003a, 0x80290037, 0x80290034, 0x80290031, 0x80210039, 0x80210036, 0x80210033, 0x80210030, 0x8019003c, 0x80190039, 0x80190036, 0x80190033, 0x80190030, 0x8019002d, 0x8019002b, 0x80190028, 0x8011003a, 0x80110036, 0x80110033, 0x80110030, 0x8011002e, 0x8011002b, 0x80110029, 0x80110027, 0x80110024, 0x80110022, 0x80110020, 0x8011001f, 0x8011001d, 0x8009003a, 0x80090037, 0x80090034, 0x80090031, 0x8009002e, 0x8009002c, 0x80090029, 0x80090027, 0x80090025, 0x80090023, 0x80090021, 0x8009001f, 0x8009001d, 0x8009011d, 0x8009021d, 0x8009031d, 0x8009041d, 0x8009051d, 0x8009061d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d }; static u32 nphy_tpc_txgain_epa_2057rev5[] = { 0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d, 0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b, 0x1091003b, 0x1089003a, 0x1081003a, 0x10790039, 0x10710039, 0x1069003a, 0x1061003b, 0x1059003d, 0x1051003f, 0x10490042, 0x1049003e, 0x1049003b, 0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b, 0x10390038, 0x10390035, 0x1031003a, 0x10310036, 0x10310033, 0x1029003a, 0x10290037, 0x10290034, 0x10290031, 0x10210039, 0x10210036, 0x10210033, 0x10210030, 0x1019003c, 0x10190039, 0x10190036, 0x10190033, 0x10190030, 0x1019002d, 0x1019002b, 0x10190028, 0x1011003a, 0x10110036, 0x10110033, 0x10110030, 0x1011002e, 0x1011002b, 0x10110029, 0x10110027, 0x10110024, 0x10110022, 0x10110020, 0x1011001f, 0x1011001d, 0x1009003a, 0x10090037, 0x10090034, 0x10090031, 0x1009002e, 0x1009002c, 0x10090029, 0x10090027, 0x10090025, 0x10090023, 0x10090021, 0x1009001f, 0x1009001d, 0x1009001b, 0x1009001a, 0x10090018, 0x10090017, 0x10090016, 0x10090015, 0x10090013, 0x10090012, 0x10090011, 0x10090010, 0x1009000f, 0x1009000f, 0x1009000e, 0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b, 0x1009000a, 0x1009000a, 0x10090009, 0x10090009, 0x10090008, 0x10090008, 0x10090007, 0x10090007, 0x10090007, 0x10090006, 0x10090006, 0x10090005, 0x10090005, 0x10090005, 0x10090005, 0x10090004, 0x10090004, 0x10090004, 0x10090004, 0x10090003, 0x10090003, 0x10090003, 0x10090003, 0x10090003, 0x10090003, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090001, 0x10090001, 0x10090001, 0x10090001, 0x10090001, 0x10090001 }; static u32 nphy_tpc_5GHz_txgain_rev3[] = { 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037 }; static u32 nphy_tpc_5GHz_txgain_rev4[] = { 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034 }; static u32 nphy_tpc_5GHz_txgain_rev5[] = { 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, 0x09620039, 0x09620037, 0x09620035, 0x09620033, 0x08620044, 0x08620042, 0x08620040, 0x0862003e, 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, 0x07620043, 0x07620042, 0x07620040, 0x0762003f, 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, 0x06620039, 0x06620037, 0x06620035, 0x06620033, 0x05620046, 0x05620044, 0x05620042, 0x05620040, 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, 0x04620044, 0x04620042, 0x04620040, 0x0462003e, 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, 0x03620038, 0x03620037, 0x03620035, 0x03620033, 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, 0x02620046, 0x02620044, 0x02620043, 0x02620042, 0x0162004a, 0x01620048, 0x01620046, 0x01620044, 0x01620043, 0x01620042, 0x01620041, 0x01620040, 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, 0x0062003b, 0x00620039, 0x00620037, 0x00620035 }; static u32 nphy_tpc_5GHz_txgain_HiPwrEPA[] = { 0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e, 0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037, 0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e, 0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037, 0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e, 0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037, 0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e, 0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037, 0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e, 0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037, 0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e, 0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037, 0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e, 0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037, 0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e, 0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037, 0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e, 0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037, 0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e, 0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037, 0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e, 0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037, 0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e, 0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038, 0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e, 0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037, 0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e, 0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037, 0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e, 0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037, 0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c, 0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034 }; static u8 ant_sw_ctrl_tbl_rev8_2o3[] = { 0x14, 0x18 }; static u8 ant_sw_ctrl_tbl_rev8[] = { 0x4, 0x8, 0x4, 0x8, 0x11, 0x12 }; static u8 ant_sw_ctrl_tbl_rev8_2057v7_core0[] = { 0x09, 0x0a, 0x15, 0x16, 0x09, 0x0a }; static u8 ant_sw_ctrl_tbl_rev8_2057v7_core1[] = { 0x09, 0x0a, 0x09, 0x0a, 0x15, 0x16 }; bool wlc_phy_bist_check_phy(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; u32 phybist0, phybist1, phybist2, phybist3, phybist4; if (NREV_GE(pi->pubpi.phy_rev, 16)) return true; phybist0 = read_phy_reg(pi, 0x0e); phybist1 = read_phy_reg(pi, 0x0f); phybist2 = read_phy_reg(pi, 0xea); phybist3 = read_phy_reg(pi, 0xeb); phybist4 = read_phy_reg(pi, 0x156); if ((phybist0 == 0) && (phybist1 == 0x4000) && (phybist2 == 0x1fe0) && (phybist3 == 0) && (phybist4 == 0)) return true; return false; } static void wlc_phy_bphy_init_nphy(struct brcms_phy *pi) { u16 addr, val; val = 0x1e1f; for (addr = (NPHY_TO_BPHY_OFF + BPHY_RSSI_LUT); addr <= (NPHY_TO_BPHY_OFF + BPHY_RSSI_LUT_END); addr++) { write_phy_reg(pi, addr, val); if (addr == (NPHY_TO_BPHY_OFF + 0x97)) val = 0x3e3f; else val -= 0x0202; } write_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_STEP, 0x668); } void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset, u32 width, const void *data) { struct phytbl_info tbl; tbl.tbl_id = id; tbl.tbl_len = len; tbl.tbl_offset = offset; tbl.tbl_width = width; tbl.tbl_ptr = data; wlc_phy_write_table_nphy(pi, &tbl); } void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset, u32 width, void *data) { struct phytbl_info tbl; tbl.tbl_id = id; tbl.tbl_len = len; tbl.tbl_offset = offset; tbl.tbl_width = width; tbl.tbl_ptr = data; wlc_phy_read_table_nphy(pi, &tbl); } static void wlc_phy_static_table_download_nphy(struct brcms_phy *pi) { uint idx; if (NREV_GE(pi->pubpi.phy_rev, 16)) { for (idx = 0; idx < mimophytbl_info_sz_rev16; idx++) wlc_phy_write_table_nphy(pi, &mimophytbl_info_rev16[idx]); } else if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (idx = 0; idx < mimophytbl_info_sz_rev7; idx++) wlc_phy_write_table_nphy(pi, &mimophytbl_info_rev7[idx]); } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { for (idx = 0; idx < mimophytbl_info_sz_rev3; idx++) wlc_phy_write_table_nphy(pi, &mimophytbl_info_rev3[idx]); } else { for (idx = 0; idx < mimophytbl_info_sz_rev0; idx++) wlc_phy_write_table_nphy(pi, &mimophytbl_info_rev0[idx]); } } static void wlc_phy_tbl_init_nphy(struct brcms_phy *pi) { uint idx = 0; u8 antswctrllut; if (pi->phy_init_por) wlc_phy_static_table_download_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 7)) { antswctrllut = CHSPEC_IS2G(pi->radio_chanspec) ? pi->srom_fem2g.antswctrllut : pi->srom_fem5g. antswctrllut; switch (antswctrllut) { case 0: break; case 1: if (pi->aa2g == 7) wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x21, 8, &ant_sw_ctrl_tbl_rev8_2o3[0]); else wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x21, 8, &ant_sw_ctrl_tbl_rev8 [0]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x25, 8, &ant_sw_ctrl_tbl_rev8[2]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x29, 8, &ant_sw_ctrl_tbl_rev8[4]); break; case 2: wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x1, 8, &ant_sw_ctrl_tbl_rev8_2057v7_core0[0]); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x5, 8, &ant_sw_ctrl_tbl_rev8_2057v7_core0[2]); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x9, 8, &ant_sw_ctrl_tbl_rev8_2057v7_core0[4]); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x21, 8, &ant_sw_ctrl_tbl_rev8_2057v7_core1[0]); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x25, 8, &ant_sw_ctrl_tbl_rev8_2057v7_core1[2]); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_ANTSWCTRLLUT, 2, 0x29, 8, &ant_sw_ctrl_tbl_rev8_2057v7_core1[4]); break; default: break; } } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { for (idx = 0; idx < mimophytbl_info_sz_rev3_volatile; idx++) { if (idx == ANT_SWCTRL_TBL_REV3_IDX) { antswctrllut = CHSPEC_IS2G(pi->radio_chanspec) ? pi->srom_fem2g.antswctrllut : pi->srom_fem5g.antswctrllut; switch (antswctrllut) { case 0: wlc_phy_write_table_nphy( pi, &mimophytbl_info_rev3_volatile [idx]); break; case 1: wlc_phy_write_table_nphy( pi, &mimophytbl_info_rev3_volatile1 [idx]); break; case 2: wlc_phy_write_table_nphy( pi, &mimophytbl_info_rev3_volatile2 [idx]); break; case 3: wlc_phy_write_table_nphy( pi, &mimophytbl_info_rev3_volatile3 [idx]); break; default: break; } } else { wlc_phy_write_table_nphy( pi, &mimophytbl_info_rev3_volatile[idx]); } } } else { for (idx = 0; idx < mimophytbl_info_sz_rev0_volatile; idx++) wlc_phy_write_table_nphy(pi, &mimophytbl_info_rev0_volatile [idx]); } } static void wlc_phy_write_txmacreg_nphy(struct brcms_phy *pi, u16 holdoff, u16 delay) { write_phy_reg(pi, 0x77, holdoff); write_phy_reg(pi, 0xb4, delay); } void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs) { u16 holdoff, delay; if (rifs) { holdoff = 0x10; delay = 0x258; } else { holdoff = 0x15; delay = 0x320; } wlc_phy_write_txmacreg_nphy(pi, holdoff, delay); if (pi->sh && (pi->sh->_rifs_phy != rifs)) pi->sh->_rifs_phy = rifs; } static void wlc_phy_txpwrctrl_config_nphy(struct brcms_phy *pi) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { pi->nphy_txpwrctrl = PHY_TPC_HW_ON; pi->phy_5g_pwrgain = true; return; } pi->nphy_txpwrctrl = PHY_TPC_HW_OFF; pi->phy_5g_pwrgain = false; if ((pi->sh->boardflags2 & BFL2_TXPWRCTRL_EN) && NREV_GE(pi->pubpi.phy_rev, 2) && (pi->sh->sromrev >= 4)) pi->nphy_txpwrctrl = PHY_TPC_HW_ON; else if ((pi->sh->sromrev >= 4) && (pi->sh->boardflags2 & BFL2_5G_PWRGAIN)) pi->phy_5g_pwrgain = true; } static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi) { u16 bw40po, cddpo, stbcpo, bwduppo; uint band_num; struct ssb_sprom *sprom = &pi->d11core->bus->sprom; if (pi->sh->sromrev >= 9) return; bw40po = sprom->bw40po; pi->bw402gpo = bw40po & 0xf; pi->bw405gpo = (bw40po & 0xf0) >> 4; pi->bw405glpo = (bw40po & 0xf00) >> 8; pi->bw405ghpo = (bw40po & 0xf000) >> 12; cddpo = sprom->cddpo; pi->cdd2gpo = cddpo & 0xf; pi->cdd5gpo = (cddpo & 0xf0) >> 4; pi->cdd5glpo = (cddpo & 0xf00) >> 8; pi->cdd5ghpo = (cddpo & 0xf000) >> 12; stbcpo = sprom->stbcpo; pi->stbc2gpo = stbcpo & 0xf; pi->stbc5gpo = (stbcpo & 0xf0) >> 4; pi->stbc5glpo = (stbcpo & 0xf00) >> 8; pi->stbc5ghpo = (stbcpo & 0xf000) >> 12; bwduppo = sprom->bwduppo; pi->bwdup2gpo = bwduppo & 0xf; pi->bwdup5gpo = (bwduppo & 0xf0) >> 4; pi->bwdup5glpo = (bwduppo & 0xf00) >> 8; pi->bwdup5ghpo = (bwduppo & 0xf000) >> 12; for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); band_num++) { switch (band_num) { case 0: pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_2g = sprom->core_pwr_info[0].maxpwr_2g; pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_2g = sprom->core_pwr_info[1].maxpwr_2g; pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_a1 = sprom->core_pwr_info[0].pa_2g[0]; pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_a1 = sprom->core_pwr_info[1].pa_2g[0]; pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b0 = sprom->core_pwr_info[0].pa_2g[1]; pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b0 = sprom->core_pwr_info[1].pa_2g[1]; pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b1 = sprom->core_pwr_info[0].pa_2g[2]; pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b1 = sprom->core_pwr_info[1].pa_2g[2]; pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_2g = sprom->core_pwr_info[0].itssi_2g; pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_2g = sprom->core_pwr_info[1].itssi_2g; pi->cck2gpo = sprom->cck2gpo; pi->ofdm2gpo = sprom->ofdm2gpo; pi->mcs2gpo[0] = sprom->mcs2gpo[0]; pi->mcs2gpo[1] = sprom->mcs2gpo[1]; pi->mcs2gpo[2] = sprom->mcs2gpo[2]; pi->mcs2gpo[3] = sprom->mcs2gpo[3]; pi->mcs2gpo[4] = sprom->mcs2gpo[4]; pi->mcs2gpo[5] = sprom->mcs2gpo[5]; pi->mcs2gpo[6] = sprom->mcs2gpo[6]; pi->mcs2gpo[7] = sprom->mcs2gpo[7]; break; case 1: pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_5gm = sprom->core_pwr_info[0].maxpwr_5g; pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_5gm = sprom->core_pwr_info[1].maxpwr_5g; pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_a1 = sprom->core_pwr_info[0].pa_5g[0]; pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_a1 = sprom->core_pwr_info[1].pa_5g[0]; pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b0 = sprom->core_pwr_info[0].pa_5g[1]; pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b0 = sprom->core_pwr_info[1].pa_5g[1]; pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b1 = sprom->core_pwr_info[0].pa_5g[2]; pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b1 = sprom->core_pwr_info[1].pa_5g[2]; pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_5gm = sprom->core_pwr_info[0].itssi_5g; pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_5gm = sprom->core_pwr_info[1].itssi_5g; pi->ofdm5gpo = sprom->ofdm5gpo; pi->mcs5gpo[0] = sprom->mcs5gpo[0]; pi->mcs5gpo[1] = sprom->mcs5gpo[1]; pi->mcs5gpo[2] = sprom->mcs5gpo[2]; pi->mcs5gpo[3] = sprom->mcs5gpo[3]; pi->mcs5gpo[4] = sprom->mcs5gpo[4]; pi->mcs5gpo[5] = sprom->mcs5gpo[5]; pi->mcs5gpo[6] = sprom->mcs5gpo[6]; pi->mcs5gpo[7] = sprom->mcs5gpo[7]; break; case 2: pi->nphy_pwrctrl_info[0].max_pwr_5gl = sprom->core_pwr_info[0].maxpwr_5gl; pi->nphy_pwrctrl_info[1].max_pwr_5gl = sprom->core_pwr_info[1].maxpwr_5gl; pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1 = sprom->core_pwr_info[0].pa_5gl[0]; pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1 = sprom->core_pwr_info[1].pa_5gl[0]; pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0 = sprom->core_pwr_info[0].pa_5gl[1]; pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0 = sprom->core_pwr_info[1].pa_5gl[1]; pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1 = sprom->core_pwr_info[0].pa_5gl[2]; pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1 = sprom->core_pwr_info[1].pa_5gl[2]; pi->nphy_pwrctrl_info[0].idle_targ_5gl = 0; pi->nphy_pwrctrl_info[1].idle_targ_5gl = 0; pi->ofdm5glpo = sprom->ofdm5glpo; pi->mcs5glpo[0] = sprom->mcs5glpo[0]; pi->mcs5glpo[1] = sprom->mcs5glpo[1]; pi->mcs5glpo[2] = sprom->mcs5glpo[2]; pi->mcs5glpo[3] = sprom->mcs5glpo[3]; pi->mcs5glpo[4] = sprom->mcs5glpo[4]; pi->mcs5glpo[5] = sprom->mcs5glpo[5]; pi->mcs5glpo[6] = sprom->mcs5glpo[6]; pi->mcs5glpo[7] = sprom->mcs5glpo[7]; break; case 3: pi->nphy_pwrctrl_info[0].max_pwr_5gh = sprom->core_pwr_info[0].maxpwr_5gh; pi->nphy_pwrctrl_info[1].max_pwr_5gh = sprom->core_pwr_info[1].maxpwr_5gh; pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1 = sprom->core_pwr_info[0].pa_5gh[0]; pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1 = sprom->core_pwr_info[1].pa_5gh[0]; pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0 = sprom->core_pwr_info[0].pa_5gh[1]; pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0 = sprom->core_pwr_info[1].pa_5gh[1]; pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1 = sprom->core_pwr_info[0].pa_5gh[2]; pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1 = sprom->core_pwr_info[1].pa_5gh[2]; pi->nphy_pwrctrl_info[0].idle_targ_5gh = 0; pi->nphy_pwrctrl_info[1].idle_targ_5gh = 0; pi->ofdm5ghpo = sprom->ofdm5ghpo; pi->mcs5ghpo[0] = sprom->mcs5ghpo[0]; pi->mcs5ghpo[1] = sprom->mcs5ghpo[1]; pi->mcs5ghpo[2] = sprom->mcs5ghpo[2]; pi->mcs5ghpo[3] = sprom->mcs5ghpo[3]; pi->mcs5ghpo[4] = sprom->mcs5ghpo[4]; pi->mcs5ghpo[5] = sprom->mcs5ghpo[5]; pi->mcs5ghpo[6] = sprom->mcs5ghpo[6]; pi->mcs5ghpo[7] = sprom->mcs5ghpo[7]; break; } } wlc_phy_txpwr_apply_nphy(pi); } static bool wlc_phy_txpwr_srom_read_nphy(struct brcms_phy *pi) { struct ssb_sprom *sprom = &pi->d11core->bus->sprom; pi->antswitch = sprom->antswitch; pi->aa2g = sprom->ant_available_bg; pi->aa5g = sprom->ant_available_a; pi->srom_fem2g.tssipos = sprom->fem.ghz2.tssipos; pi->srom_fem2g.extpagain = sprom->fem.ghz2.extpa_gain; pi->srom_fem2g.pdetrange = sprom->fem.ghz2.pdet_range; pi->srom_fem2g.triso = sprom->fem.ghz2.tr_iso; pi->srom_fem2g.antswctrllut = sprom->fem.ghz2.antswlut; pi->srom_fem5g.tssipos = sprom->fem.ghz5.tssipos; pi->srom_fem5g.extpagain = sprom->fem.ghz5.extpa_gain; pi->srom_fem5g.pdetrange = sprom->fem.ghz5.pdet_range; pi->srom_fem5g.triso = sprom->fem.ghz5.tr_iso; if (sprom->fem.ghz5.antswlut) pi->srom_fem5g.antswctrllut = sprom->fem.ghz5.antswlut; else pi->srom_fem5g.antswctrllut = sprom->fem.ghz2.antswlut; wlc_phy_txpower_ipa_upd(pi); pi->phy_txcore_disable_temp = sprom->tempthresh; if (pi->phy_txcore_disable_temp == 0) pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP; pi->phy_tempsense_offset = sprom->tempoffset; if (pi->phy_tempsense_offset != 0) { if (pi->phy_tempsense_offset > (NPHY_SROM_TEMPSHIFT + NPHY_SROM_MAXTEMPOFFSET)) pi->phy_tempsense_offset = NPHY_SROM_MAXTEMPOFFSET; else if (pi->phy_tempsense_offset < (NPHY_SROM_TEMPSHIFT + NPHY_SROM_MINTEMPOFFSET)) pi->phy_tempsense_offset = NPHY_SROM_MINTEMPOFFSET; else pi->phy_tempsense_offset -= NPHY_SROM_TEMPSHIFT; } pi->phy_txcore_enable_temp = pi->phy_txcore_disable_temp - PHY_HYSTERESIS_DELTATEMP; pi->phycal_tempdelta = sprom->phycal_tempdelta; if (pi->phycal_tempdelta > NPHY_CAL_MAXTEMPDELTA) pi->phycal_tempdelta = 0; wlc_phy_txpwr_srom_read_ppr_nphy(pi); return true; } bool wlc_phy_attach_nphy(struct brcms_phy *pi) { uint i; if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 6)) pi->phyhang_avoid = true; if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { pi->nphy_gband_spurwar_en = true; if (pi->sh->boardflags2 & BFL2_SPUR_WAR) pi->nphy_aband_spurwar_en = true; } if (NREV_GE(pi->pubpi.phy_rev, 6) && NREV_LT(pi->pubpi.phy_rev, 7)) { if (pi->sh->boardflags2 & BFL2_2G_SPUR_WAR) pi->nphy_gband_spurwar2_en = true; } pi->n_preamble_override = AUTO; if (NREV_IS(pi->pubpi.phy_rev, 3) || NREV_IS(pi->pubpi.phy_rev, 4)) pi->n_preamble_override = BRCMS_N_PREAMBLE_MIXEDMODE; pi->nphy_txrx_chain = AUTO; pi->phy_scraminit = AUTO; pi->nphy_rxcalparams = 0x010100B5; pi->nphy_perical = PHY_PERICAL_MPHASE; pi->mphase_cal_phase_id = MPHASE_CAL_STATE_IDLE; pi->mphase_txcal_numcmds = MPHASE_TXCAL_NUMCMDS; pi->nphy_gain_boost = true; pi->nphy_elna_gain_config = false; pi->radio_is_on = false; for (i = 0; i < pi->pubpi.phy_corenum; i++) pi->nphy_txpwrindex[i].index = AUTO; wlc_phy_txpwrctrl_config_nphy(pi); if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON) pi->hwpwrctrl_capable = true; pi->pi_fptr.init = wlc_phy_init_nphy; pi->pi_fptr.calinit = wlc_phy_cal_init_nphy; pi->pi_fptr.chanset = wlc_phy_chanspec_set_nphy; pi->pi_fptr.txpwrrecalc = wlc_phy_txpower_recalc_target_nphy; if (!wlc_phy_txpwr_srom_read_nphy(pi)) return false; return true; } static s32 get_rf_pwr_offset(struct brcms_phy *pi, s16 pga_gn, s16 pad_gn) { s32 rfpwr_offset = 0; if (CHSPEC_IS2G(pi->radio_chanspec)) { if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) rfpwr_offset = (s16) nphy_papd_padgain_dlt_2g_2057rev3n4 [pad_gn]; else if (pi->pubpi.radiorev == 5) rfpwr_offset = (s16) nphy_papd_padgain_dlt_2g_2057rev5 [pad_gn]; else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) rfpwr_offset = (s16) nphy_papd_padgain_dlt_2g_2057rev7 [pad_gn]; } else { if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) rfpwr_offset = (s16) nphy_papd_pgagain_dlt_5g_2057 [pga_gn]; else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) rfpwr_offset = (s16) nphy_papd_pgagain_dlt_5g_2057rev7 [pga_gn]; } return rfpwr_offset; } static void wlc_phy_update_mimoconfig_nphy(struct brcms_phy *pi, s32 preamble) { bool gf_preamble = false; u16 val; if (preamble == BRCMS_N_PREAMBLE_GF) gf_preamble = true; val = read_phy_reg(pi, 0xed); val |= RX_GF_MM_AUTO; val &= ~RX_GF_OR_MM; if (gf_preamble) val |= RX_GF_OR_MM; write_phy_reg(pi, 0xed, val); } static void wlc_phy_ipa_set_tx_digi_filts_nphy(struct brcms_phy *pi) { int j, type; u16 addr_offset[] = { 0x186, 0x195, 0x2c5}; for (type = 0; type < 3; type++) { for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, addr_offset[type] + j, NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]); } if (pi->bw == WL_CHANSPEC_BW_40) { for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, 0x186 + j, NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, 0x186 + j, NPHY_IPA_REV4_txdigi_filtcoeffs[5][j]); } if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, 0x2c5 + j, NPHY_IPA_REV4_txdigi_filtcoeffs[6][j]); } } } static void wlc_phy_ipa_restore_tx_digi_filts_nphy(struct brcms_phy *pi) { int j; if (pi->bw == WL_CHANSPEC_BW_40) { for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, 0x195 + j, NPHY_IPA_REV4_txdigi_filtcoeffs[4][j]); } else { for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, 0x186 + j, NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]); } } static void wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, u8 *events, u8 *dlys, u8 len) { u32 t1_offset, t2_offset; u8 ctr; u8 end_event = NREV_GE(pi->pubpi.phy_rev, 3) ? NPHY_REV3_RFSEQ_CMD_END : NPHY_RFSEQ_CMD_END; u8 end_dly = 1; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); t1_offset = cmd << 4; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t1_offset, 8, events); t2_offset = t1_offset + 0x080; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t2_offset, 8, dlys); for (ctr = len; ctr < 16; ctr++) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, t1_offset + ctr, 8, &end_event); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, t2_offset + ctr, 8, &end_dly); } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static u16 wlc_phy_read_lpf_bw_ctl_nphy(struct brcms_phy *pi, u16 offset) { u16 lpf_bw_ctl_val = 0; u16 rx2tx_lpf_rc_lut_offset = 0; if (offset == 0) { if (CHSPEC_IS40(pi->radio_chanspec)) rx2tx_lpf_rc_lut_offset = 0x159; else rx2tx_lpf_rc_lut_offset = 0x154; } else { rx2tx_lpf_rc_lut_offset = offset; } wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, (u32) rx2tx_lpf_rc_lut_offset, 16, &lpf_bw_ctl_val); lpf_bw_ctl_val = lpf_bw_ctl_val & 0x7; return lpf_bw_ctl_val; } static void wlc_phy_rfctrl_override_nphy_rev7(struct brcms_phy *pi, u16 field, u16 value, u8 core_mask, u8 off, u8 override_id) { u8 core_num; u16 addr = 0, en_addr = 0, val_addr = 0, en_mask = 0, val_mask = 0; u8 val_shift = 0; if (NREV_GE(pi->pubpi.phy_rev, 7)) { en_mask = field; for (core_num = 0; core_num < 2; core_num++) { if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID0) { switch (field) { case (0x1 << 2): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 1); val_shift = 1; break; case (0x1 << 3): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 2); val_shift = 2; break; case (0x1 << 4): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 4); val_shift = 4; break; case (0x1 << 5): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 5); val_shift = 5; break; case (0x1 << 6): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 6); val_shift = 6; break; case (0x1 << 7): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 7); val_shift = 7; break; case (0x1 << 10): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0xf8 : 0xfa; val_mask = (0x7 << 4); val_shift = 4; break; case (0x1 << 11): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7b : 0x7e; val_mask = (0xffff << 0); val_shift = 0; break; case (0x1 << 12): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7c : 0x7f; val_mask = (0xffff << 0); val_shift = 0; break; case (0x3 << 13): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x348 : 0x349; val_mask = (0xff << 0); val_shift = 0; break; case (0x1 << 13): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x348 : 0x349; val_mask = (0xf << 0); val_shift = 0; break; default: addr = 0xffff; break; } } else if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID1) { switch (field) { case (0x1 << 1): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 1); val_shift = 1; break; case (0x1 << 3): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 3); val_shift = 3; break; case (0x1 << 5): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 5); val_shift = 5; break; case (0x1 << 4): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 4); val_shift = 4; break; case (0x1 << 2): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 2); val_shift = 2; break; case (0x1 << 7): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x7 << 8); val_shift = 8; break; case (0x1 << 11): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 14); val_shift = 14; break; case (0x1 << 10): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 13); val_shift = 13; break; case (0x1 << 9): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 12); val_shift = 12; break; case (0x1 << 8): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 11); val_shift = 11; break; case (0x1 << 6): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 6); val_shift = 6; break; case (0x1 << 0): en_addr = (core_num == 0) ? 0x342 : 0x343; val_addr = (core_num == 0) ? 0x340 : 0x341; val_mask = (0x1 << 0); val_shift = 0; break; default: addr = 0xffff; break; } } else if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID2) { switch (field) { case (0x1 << 3): en_addr = (core_num == 0) ? 0x346 : 0x347; val_addr = (core_num == 0) ? 0x344 : 0x345; val_mask = (0x1 << 3); val_shift = 3; break; case (0x1 << 1): en_addr = (core_num == 0) ? 0x346 : 0x347; val_addr = (core_num == 0) ? 0x344 : 0x345; val_mask = (0x1 << 1); val_shift = 1; break; case (0x1 << 0): en_addr = (core_num == 0) ? 0x346 : 0x347; val_addr = (core_num == 0) ? 0x344 : 0x345; val_mask = (0x1 << 0); val_shift = 0; break; case (0x1 << 2): en_addr = (core_num == 0) ? 0x346 : 0x347; val_addr = (core_num == 0) ? 0x344 : 0x345; val_mask = (0x1 << 2); val_shift = 2; break; case (0x1 << 4): en_addr = (core_num == 0) ? 0x346 : 0x347; val_addr = (core_num == 0) ? 0x344 : 0x345; val_mask = (0x1 << 4); val_shift = 4; break; default: addr = 0xffff; break; } } if (off) { and_phy_reg(pi, en_addr, ~en_mask); and_phy_reg(pi, val_addr, ~val_mask); } else { if ((core_mask == 0) || (core_mask & (1 << core_num))) { or_phy_reg(pi, en_addr, en_mask); if (addr != 0xffff) mod_phy_reg(pi, val_addr, val_mask, (value << val_shift)); } } } } } static void wlc_phy_adjust_lnagaintbl_nphy(struct brcms_phy *pi) { uint core; int ctr; s16 gain_delta[2]; u8 curr_channel; u16 minmax_gain[2]; u16 regval[4]; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); if (pi->nphy_gain_boost) { if ((CHSPEC_IS2G(pi->radio_chanspec))) { gain_delta[0] = 6; gain_delta[1] = 6; } else { curr_channel = CHSPEC_CHANNEL(pi->radio_chanspec); gain_delta[0] = (s16) PHY_HW_ROUND(((nphy_lnagain_est0[0] * curr_channel) + nphy_lnagain_est0[1]), 13); gain_delta[1] = (s16) PHY_HW_ROUND(((nphy_lnagain_est1[0] * curr_channel) + nphy_lnagain_est1[1]), 13); } } else { gain_delta[0] = 0; gain_delta[1] = 0; } for (core = 0; core < pi->pubpi.phy_corenum; core++) { if (pi->nphy_elna_gain_config) { regval[0] = nphy_def_lnagains[2] + gain_delta[core]; regval[1] = nphy_def_lnagains[3] + gain_delta[core]; regval[2] = nphy_def_lnagains[3] + gain_delta[core]; regval[3] = nphy_def_lnagains[3] + gain_delta[core]; } else { for (ctr = 0; ctr < 4; ctr++) regval[ctr] = nphy_def_lnagains[ctr] + gain_delta[core]; } wlc_phy_table_write_nphy(pi, core, 4, 8, 16, regval); minmax_gain[core] = (u16) (nphy_def_lnagains[2] + gain_delta[core] + 4); } mod_phy_reg(pi, 0x1e, (0xff << 0), (minmax_gain[0] << 0)); mod_phy_reg(pi, 0x34, (0xff << 0), (minmax_gain[1] << 0)); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static void wlc_phy_war_force_trsw_to_R_cliplo_nphy(struct brcms_phy *pi, u8 core) { if (core == PHY_CORE_0) { write_phy_reg(pi, 0x38, 0x4); if (CHSPEC_IS2G(pi->radio_chanspec)) write_phy_reg(pi, 0x37, 0x0060); else write_phy_reg(pi, 0x37, 0x1080); } else if (core == PHY_CORE_1) { write_phy_reg(pi, 0x2ae, 0x4); if (CHSPEC_IS2G(pi->radio_chanspec)) write_phy_reg(pi, 0x2ad, 0x0060); else write_phy_reg(pi, 0x2ad, 0x1080); } } static void wlc_phy_war_txchain_upd_nphy(struct brcms_phy *pi, u8 txchain) { u8 txchain0, txchain1; txchain0 = txchain & 0x1; txchain1 = (txchain & 0x2) >> 1; if (!txchain0) wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0); if (!txchain1) wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1); } static void wlc_phy_workarounds_nphy_gainctrl_2057_rev5(struct brcms_phy *pi) { s8 lna1_gain_db[] = { 8, 13, 17, 22 }; s8 lna2_gain_db[] = { -2, 7, 11, 15 }; s8 tia_gain_db[] = { -4, -1, 2, 5, 5, 5, 5, 5, 5, 5 }; s8 tia_gainbits[] = { 0x0, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }; mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0)); mod_phy_reg(pi, 0x283, (0xff << 0), (0x3c << 0)); mod_phy_reg(pi, 0x280, (0xff << 0), (0x3c << 0)); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x8, 8, lna1_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x8, 8, lna1_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, 8, lna2_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, 8, lna2_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8, tia_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8, tia_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8, tia_gainbits); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8, tia_gainbits); write_phy_reg(pi, 0x37, 0x74); write_phy_reg(pi, 0x2ad, 0x74); write_phy_reg(pi, 0x38, 0x18); write_phy_reg(pi, 0x2ae, 0x18); write_phy_reg(pi, 0x2b, 0xe8); write_phy_reg(pi, 0x41, 0xe8); if (CHSPEC_IS20(pi->radio_chanspec)) { mod_phy_reg(pi, 0x300, (0x3f << 0), (0x12 << 0)); mod_phy_reg(pi, 0x301, (0x3f << 0), (0x12 << 0)); } else { mod_phy_reg(pi, 0x300, (0x3f << 0), (0x10 << 0)); mod_phy_reg(pi, 0x301, (0x3f << 0), (0x10 << 0)); } } static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi) { u16 currband; s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 }; s8 *lna1_gain_db = NULL; s8 *lna1_gain_db_2 = NULL; s8 *lna2_gain_db = NULL; s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 }; s8 *tia_gain_db; s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 }; s8 *tia_gainbits; u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f }; u16 *rfseq_init_gain; u16 init_gaincode; u16 clip1hi_gaincode; u16 clip1md_gaincode = 0; u16 clip1md_gaincode_B; u16 clip1lo_gaincode; u16 clip1lo_gaincode_B; u8 crsminl_th = 0; u8 crsminu_th; u16 nbclip_th = 0; u8 w1clip_th; u16 freq; s8 nvar_baseline_offset0 = 0, nvar_baseline_offset1 = 0; u8 chg_nbclip_th = 0; mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); currband = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; if (currband == 0) { lna1_gain_db = lna1G_gain_db_rev7; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8, lna1_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8, lna1_gain_db); mod_phy_reg(pi, 0x283, (0xff << 0), (0x40 << 0)); if (CHSPEC_IS40(pi->radio_chanspec)) { mod_phy_reg(pi, 0x280, (0xff << 0), (0x3e << 0)); mod_phy_reg(pi, 0x283, (0xff << 0), (0x3e << 0)); } mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0)); if (CHSPEC_IS20(pi->radio_chanspec)) { mod_phy_reg(pi, 0x300, (0x3f << 0), (13 << 0)); mod_phy_reg(pi, 0x301, (0x3f << 0), (13 << 0)); } } else { init_gaincode = 0x9e; clip1hi_gaincode = 0x9e; clip1md_gaincode_B = 0x24; clip1lo_gaincode = 0x8a; clip1lo_gaincode_B = 8; rfseq_init_gain = rfseqA_init_gain_rev7; tia_gain_db = tiaA_gain_db_rev7; tia_gainbits = tiaA_gainbits_rev7; freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec)); if (CHSPEC_IS20(pi->radio_chanspec)) { w1clip_th = 25; clip1md_gaincode = 0x82; if ((freq <= 5080) || (freq == 5825)) { s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 }; s8 lna1A_gain_db_2_rev7[] = { 11, 17, 22, 25}; s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; crsminu_th = 0x3e; lna1_gain_db = lna1A_gain_db_rev7; lna1_gain_db_2 = lna1A_gain_db_2_rev7; lna2_gain_db = lna2A_gain_db_rev7; } else if ((freq >= 5500) && (freq <= 5700)) { s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 }; s8 lna1A_gain_db_2_rev7[] = { 12, 18, 22, 26}; s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 }; crsminu_th = 0x45; clip1md_gaincode_B = 0x14; nbclip_th = 0xff; chg_nbclip_th = 1; lna1_gain_db = lna1A_gain_db_rev7; lna1_gain_db_2 = lna1A_gain_db_2_rev7; lna2_gain_db = lna2A_gain_db_rev7; } else { s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 }; s8 lna1A_gain_db_2_rev7[] = { 12, 18, 22, 26}; s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; crsminu_th = 0x41; lna1_gain_db = lna1A_gain_db_rev7; lna1_gain_db_2 = lna1A_gain_db_2_rev7; lna2_gain_db = lna2A_gain_db_rev7; } if (freq <= 4920) { nvar_baseline_offset0 = 5; nvar_baseline_offset1 = 5; } else if ((freq > 4920) && (freq <= 5320)) { nvar_baseline_offset0 = 3; nvar_baseline_offset1 = 5; } else if ((freq > 5320) && (freq <= 5700)) { nvar_baseline_offset0 = 3; nvar_baseline_offset1 = 2; } else { nvar_baseline_offset0 = 4; nvar_baseline_offset1 = 0; } } else { crsminu_th = 0x3a; crsminl_th = 0x3a; w1clip_th = 20; if ((freq >= 4920) && (freq <= 5320)) { nvar_baseline_offset0 = 4; nvar_baseline_offset1 = 5; } else if ((freq > 5320) && (freq <= 5550)) { nvar_baseline_offset0 = 4; nvar_baseline_offset1 = 2; } else { nvar_baseline_offset0 = 5; nvar_baseline_offset1 = 3; } } write_phy_reg(pi, 0x20, init_gaincode); write_phy_reg(pi, 0x2a7, init_gaincode); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, pi->pubpi.phy_corenum, 0x106, 16, rfseq_init_gain); write_phy_reg(pi, 0x22, clip1hi_gaincode); write_phy_reg(pi, 0x2a9, clip1hi_gaincode); write_phy_reg(pi, 0x36, clip1md_gaincode_B); write_phy_reg(pi, 0x2ac, clip1md_gaincode_B); write_phy_reg(pi, 0x37, clip1lo_gaincode); write_phy_reg(pi, 0x2ad, clip1lo_gaincode); write_phy_reg(pi, 0x38, clip1lo_gaincode_B); write_phy_reg(pi, 0x2ae, clip1lo_gaincode_B); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8, tia_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8, tia_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8, tia_gainbits); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8, tia_gainbits); mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0)); if (chg_nbclip_th == 1) { write_phy_reg(pi, 0x2b, nbclip_th); write_phy_reg(pi, 0x41, nbclip_th); } mod_phy_reg(pi, 0x300, (0x3f << 0), (w1clip_th << 0)); mod_phy_reg(pi, 0x301, (0x3f << 0), (w1clip_th << 0)); mod_phy_reg(pi, 0x2e4, (0x3f << 0), (nvar_baseline_offset0 << 0)); mod_phy_reg(pi, 0x2e4, (0x3f << 6), (nvar_baseline_offset1 << 6)); if (CHSPEC_IS20(pi->radio_chanspec)) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8, lna1_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8, lna1_gain_db_2); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, 8, lna2_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, 8, lna2_gain_db); write_phy_reg(pi, 0x24, clip1md_gaincode); write_phy_reg(pi, 0x2ab, clip1md_gaincode); } else { mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0)); } } } static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi) { u16 w1th, hpf_code, currband; int ctr; u8 rfseq_updategainu_events[] = { NPHY_RFSEQ_CMD_RX_GAIN, NPHY_RFSEQ_CMD_CLR_HIQ_DIS, NPHY_RFSEQ_CMD_SET_HPF_BW }; u8 rfseq_updategainu_dlys[] = { 10, 30, 1 }; s8 lna1G_gain_db[] = { 7, 11, 16, 23 }; s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 }; s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 }; s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 }; s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 }; s8 lna1A_gain_db[] = { 7, 11, 17, 23 }; s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 }; s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 }; s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 }; s8 *lna1_gain_db = NULL; s8 lna2G_gain_db[] = { -5, 6, 10, 14 }; s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 }; s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 }; s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 }; s8 lna2A_gain_db[] = { -6, 2, 6, 10 }; s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 }; s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 }; s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 }; s8 *lna2_gain_db = NULL; s8 tiaG_gain_db[] = { 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }; s8 tiaA_gain_db[] = { 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 }; s8 tiaA_gain_db_rev4[] = { 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; s8 tiaA_gain_db_rev5[] = { 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; s8 tiaA_gain_db_rev6[] = { 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; s8 *tia_gain_db; s8 tiaG_gainbits[] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }; s8 tiaA_gainbits[] = { 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 }; s8 tiaA_gainbits_rev4[] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; s8 tiaA_gainbits_rev5[] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; s8 tiaA_gainbits_rev6[] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; s8 *tia_gainbits; s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 }; s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 }; u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f }; u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f }; u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f }; u16 rfseqG_init_gain_rev5_elna[] = { 0x013f, 0x013f, 0x013f, 0x013f }; u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f }; u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f }; u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f }; u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f }; u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f }; u16 rfseqA_init_gain_rev4_elna[] = { 0x314f, 0x314f, 0x314f, 0x314f }; u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f }; u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f }; u16 *rfseq_init_gain; u16 initG_gaincode = 0x627e; u16 initG_gaincode_rev4 = 0x527e; u16 initG_gaincode_rev5 = 0x427e; u16 initG_gaincode_rev5_elna = 0x027e; u16 initG_gaincode_rev6 = 0x527e; u16 initG_gaincode_rev6_224B0 = 0x427e; u16 initG_gaincode_rev6_elna = 0x127e; u16 initA_gaincode = 0x52de; u16 initA_gaincode_rev4 = 0x629e; u16 initA_gaincode_rev4_elna = 0x329e; u16 initA_gaincode_rev5 = 0x729e; u16 initA_gaincode_rev6 = 0x729e; u16 init_gaincode; u16 clip1hiG_gaincode = 0x107e; u16 clip1hiG_gaincode_rev4 = 0x007e; u16 clip1hiG_gaincode_rev5 = 0x1076; u16 clip1hiG_gaincode_rev6 = 0x007e; u16 clip1hiA_gaincode = 0x00de; u16 clip1hiA_gaincode_rev4 = 0x029e; u16 clip1hiA_gaincode_rev5 = 0x029e; u16 clip1hiA_gaincode_rev6 = 0x029e; u16 clip1hi_gaincode; u16 clip1mdG_gaincode = 0x0066; u16 clip1mdA_gaincode = 0x00ca; u16 clip1mdA_gaincode_rev4 = 0x1084; u16 clip1mdA_gaincode_rev5 = 0x2084; u16 clip1mdA_gaincode_rev6 = 0x2084; u16 clip1md_gaincode = 0; u16 clip1loG_gaincode = 0x0074; u16 clip1loG_gaincode_rev5[] = { 0x0062, 0x0064, 0x006a, 0x106a, 0x106c, 0x1074, 0x107c, 0x207c }; u16 clip1loG_gaincode_rev6[] = { 0x106a, 0x106c, 0x1074, 0x107c, 0x007e, 0x107e, 0x207e, 0x307e }; u16 clip1loG_gaincode_rev6_224B0 = 0x1074; u16 clip1loA_gaincode = 0x00cc; u16 clip1loA_gaincode_rev4 = 0x0086; u16 clip1loA_gaincode_rev5 = 0x2086; u16 clip1loA_gaincode_rev6 = 0x2086; u16 clip1lo_gaincode; u8 crsminG_th = 0x18; u8 crsminG_th_rev5 = 0x18; u8 crsminG_th_rev6 = 0x18; u8 crsminA_th = 0x1e; u8 crsminA_th_rev4 = 0x24; u8 crsminA_th_rev5 = 0x24; u8 crsminA_th_rev6 = 0x24; u8 crsmin_th; u8 crsminlG_th = 0x18; u8 crsminlG_th_rev5 = 0x18; u8 crsminlG_th_rev6 = 0x18; u8 crsminlA_th = 0x1e; u8 crsminlA_th_rev4 = 0x24; u8 crsminlA_th_rev5 = 0x24; u8 crsminlA_th_rev6 = 0x24; u8 crsminl_th = 0; u8 crsminuG_th = 0x18; u8 crsminuG_th_rev5 = 0x18; u8 crsminuG_th_rev6 = 0x18; u8 crsminuA_th = 0x1e; u8 crsminuA_th_rev4 = 0x24; u8 crsminuA_th_rev5 = 0x24; u8 crsminuA_th_rev6 = 0x24; u8 crsminuA_th_rev6_224B0 = 0x2d; u8 crsminu_th; u16 nbclipG_th = 0x20d; u16 nbclipG_th_rev4 = 0x1a1; u16 nbclipG_th_rev5 = 0x1d0; u16 nbclipG_th_rev6 = 0x1d0; u16 nbclipA_th = 0x1a1; u16 nbclipA_th_rev4 = 0x107; u16 nbclipA_th_rev5 = 0x0a9; u16 nbclipA_th_rev6 = 0x0f0; u16 nbclip_th = 0; u8 w1clipG_th = 5; u8 w1clipG_th_rev5 = 9; u8 w1clipG_th_rev6 = 5; u8 w1clipA_th = 25, w1clip_th; u8 rssi_gain_default = 0x50; u8 rssiG_gain_rev6_224B0 = 0x50; u8 rssiA_gain_rev5 = 0x90; u8 rssiA_gain_rev6 = 0x90; u8 rssi_gain; u16 regval[21]; u8 triso; triso = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.triso : pi->srom_fem2g.triso; if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (pi->pubpi.radiorev == 5) { wlc_phy_workarounds_nphy_gainctrl_2057_rev5(pi); } else if (pi->pubpi.radiorev == 7) { wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); mod_phy_reg(pi, 0x283, (0xff << 0), (0x44 << 0)); mod_phy_reg(pi, 0x280, (0xff << 0), (0x44 << 0)); } else if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 8)) { wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); if (pi->pubpi.radiorev == 8) { mod_phy_reg(pi, 0x283, (0xff << 0), (0x44 << 0)); mod_phy_reg(pi, 0x280, (0xff << 0), (0x44 << 0)); } } else { wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); } } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { mod_phy_reg(pi, 0xa0, (0x1 << 6), (1 << 6)); mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); currband = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; if (currband == 0) { if (NREV_GE(pi->pubpi.phy_rev, 6)) { if (pi->pubpi.radiorev == 11) { lna1_gain_db = lna1G_gain_db_rev6_224B0; lna2_gain_db = lna2G_gain_db_rev6_224B0; rfseq_init_gain = rfseqG_init_gain_rev6_224B0; init_gaincode = initG_gaincode_rev6_224B0; clip1hi_gaincode = clip1hiG_gaincode_rev6; clip1lo_gaincode = clip1loG_gaincode_rev6_224B0; nbclip_th = nbclipG_th_rev6; w1clip_th = w1clipG_th_rev6; crsmin_th = crsminG_th_rev6; crsminl_th = crsminlG_th_rev6; crsminu_th = crsminuG_th_rev6; rssi_gain = rssiG_gain_rev6_224B0; } else { lna1_gain_db = lna1G_gain_db_rev6; lna2_gain_db = lna2G_gain_db_rev6; if (pi->sh->boardflags & BFL_EXTLNA) { rfseq_init_gain = rfseqG_init_gain_rev6_elna; init_gaincode = initG_gaincode_rev6_elna; } else { rfseq_init_gain = rfseqG_init_gain_rev6; init_gaincode = initG_gaincode_rev6; } clip1hi_gaincode = clip1hiG_gaincode_rev6; switch (triso) { case 0: clip1lo_gaincode = clip1loG_gaincode_rev6 [0]; break; case 1: clip1lo_gaincode = clip1loG_gaincode_rev6 [1]; break; case 2: clip1lo_gaincode = clip1loG_gaincode_rev6 [2]; break; case 3: default: clip1lo_gaincode = clip1loG_gaincode_rev6 [3]; break; case 4: clip1lo_gaincode = clip1loG_gaincode_rev6 [4]; break; case 5: clip1lo_gaincode = clip1loG_gaincode_rev6 [5]; break; case 6: clip1lo_gaincode = clip1loG_gaincode_rev6 [6]; break; case 7: clip1lo_gaincode = clip1loG_gaincode_rev6 [7]; break; } nbclip_th = nbclipG_th_rev6; w1clip_th = w1clipG_th_rev6; crsmin_th = crsminG_th_rev6; crsminl_th = crsminlG_th_rev6; crsminu_th = crsminuG_th_rev6; rssi_gain = rssi_gain_default; } } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { lna1_gain_db = lna1G_gain_db_rev5; lna2_gain_db = lna2G_gain_db_rev5; if (pi->sh->boardflags & BFL_EXTLNA) { rfseq_init_gain = rfseqG_init_gain_rev5_elna; init_gaincode = initG_gaincode_rev5_elna; } else { rfseq_init_gain = rfseqG_init_gain_rev5; init_gaincode = initG_gaincode_rev5; } clip1hi_gaincode = clip1hiG_gaincode_rev5; switch (triso) { case 0: clip1lo_gaincode = clip1loG_gaincode_rev5[0]; break; case 1: clip1lo_gaincode = clip1loG_gaincode_rev5[1]; break; case 2: clip1lo_gaincode = clip1loG_gaincode_rev5[2]; break; case 3: clip1lo_gaincode = clip1loG_gaincode_rev5[3]; break; case 4: clip1lo_gaincode = clip1loG_gaincode_rev5[4]; break; case 5: clip1lo_gaincode = clip1loG_gaincode_rev5[5]; break; case 6: clip1lo_gaincode = clip1loG_gaincode_rev5[6]; break; case 7: clip1lo_gaincode = clip1loG_gaincode_rev5[7]; break; default: clip1lo_gaincode = clip1loG_gaincode_rev5[3]; break; } nbclip_th = nbclipG_th_rev5; w1clip_th = w1clipG_th_rev5; crsmin_th = crsminG_th_rev5; crsminl_th = crsminlG_th_rev5; crsminu_th = crsminuG_th_rev5; rssi_gain = rssi_gain_default; } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { lna1_gain_db = lna1G_gain_db_rev4; lna2_gain_db = lna2G_gain_db; rfseq_init_gain = rfseqG_init_gain_rev4; init_gaincode = initG_gaincode_rev4; clip1hi_gaincode = clip1hiG_gaincode_rev4; clip1lo_gaincode = clip1loG_gaincode; nbclip_th = nbclipG_th_rev4; w1clip_th = w1clipG_th; crsmin_th = crsminG_th; crsminl_th = crsminlG_th; crsminu_th = crsminuG_th; rssi_gain = rssi_gain_default; } else { lna1_gain_db = lna1G_gain_db; lna2_gain_db = lna2G_gain_db; rfseq_init_gain = rfseqG_init_gain; init_gaincode = initG_gaincode; clip1hi_gaincode = clip1hiG_gaincode; clip1lo_gaincode = clip1loG_gaincode; nbclip_th = nbclipG_th; w1clip_th = w1clipG_th; crsmin_th = crsminG_th; crsminl_th = crsminlG_th; crsminu_th = crsminuG_th; rssi_gain = rssi_gain_default; } tia_gain_db = tiaG_gain_db; tia_gainbits = tiaG_gainbits; clip1md_gaincode = clip1mdG_gaincode; } else { if (NREV_GE(pi->pubpi.phy_rev, 6)) { lna1_gain_db = lna1A_gain_db_rev6; lna2_gain_db = lna2A_gain_db_rev6; tia_gain_db = tiaA_gain_db_rev6; tia_gainbits = tiaA_gainbits_rev6; rfseq_init_gain = rfseqA_init_gain_rev6; init_gaincode = initA_gaincode_rev6; clip1hi_gaincode = clip1hiA_gaincode_rev6; clip1md_gaincode = clip1mdA_gaincode_rev6; clip1lo_gaincode = clip1loA_gaincode_rev6; crsmin_th = crsminA_th_rev6; crsminl_th = crsminlA_th_rev6; if ((pi->pubpi.radiorev == 11) && (CHSPEC_IS40(pi->radio_chanspec) == 0)) crsminu_th = crsminuA_th_rev6_224B0; else crsminu_th = crsminuA_th_rev6; nbclip_th = nbclipA_th_rev6; rssi_gain = rssiA_gain_rev6; } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { lna1_gain_db = lna1A_gain_db_rev5; lna2_gain_db = lna2A_gain_db_rev5; tia_gain_db = tiaA_gain_db_rev5; tia_gainbits = tiaA_gainbits_rev5; rfseq_init_gain = rfseqA_init_gain_rev5; init_gaincode = initA_gaincode_rev5; clip1hi_gaincode = clip1hiA_gaincode_rev5; clip1md_gaincode = clip1mdA_gaincode_rev5; clip1lo_gaincode = clip1loA_gaincode_rev5; crsmin_th = crsminA_th_rev5; crsminl_th = crsminlA_th_rev5; crsminu_th = crsminuA_th_rev5; nbclip_th = nbclipA_th_rev5; rssi_gain = rssiA_gain_rev5; } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { lna1_gain_db = lna1A_gain_db_rev4; lna2_gain_db = lna2A_gain_db_rev4; tia_gain_db = tiaA_gain_db_rev4; tia_gainbits = tiaA_gainbits_rev4; if (pi->sh->boardflags & BFL_EXTLNA_5GHz) { rfseq_init_gain = rfseqA_init_gain_rev4_elna; init_gaincode = initA_gaincode_rev4_elna; } else { rfseq_init_gain = rfseqA_init_gain_rev4; init_gaincode = initA_gaincode_rev4; } clip1hi_gaincode = clip1hiA_gaincode_rev4; clip1md_gaincode = clip1mdA_gaincode_rev4; clip1lo_gaincode = clip1loA_gaincode_rev4; crsmin_th = crsminA_th_rev4; crsminl_th = crsminlA_th_rev4; crsminu_th = crsminuA_th_rev4; nbclip_th = nbclipA_th_rev4; rssi_gain = rssi_gain_default; } else { lna1_gain_db = lna1A_gain_db; lna2_gain_db = lna2A_gain_db; tia_gain_db = tiaA_gain_db; tia_gainbits = tiaA_gainbits; rfseq_init_gain = rfseqA_init_gain; init_gaincode = initA_gaincode; clip1hi_gaincode = clip1hiA_gaincode; clip1md_gaincode = clip1mdA_gaincode; clip1lo_gaincode = clip1loA_gaincode; crsmin_th = crsminA_th; crsminl_th = crsminlA_th; crsminu_th = crsminuA_th; nbclip_th = nbclipA_th; rssi_gain = rssi_gain_default; } w1clip_th = w1clipA_th; } write_radio_reg(pi, (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC | RADIO_2056_RX0), 0x17); write_radio_reg(pi, (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC | RADIO_2056_RX1), 0x17); write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX0), 0xf0); write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX1), 0xf0); write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX0), 0x0); write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX1), 0x0); write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX0), rssi_gain); write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX1), rssi_gain); write_radio_reg(pi, (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC | RADIO_2056_RX0), 0x17); write_radio_reg(pi, (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC | RADIO_2056_RX1), 0x17); write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX0), 0xFF); write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX1), 0xFF); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8, lna1_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8, lna1_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, 8, lna2_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, 8, lna2_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8, tia_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8, tia_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8, tia_gainbits); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8, tia_gainbits); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 6, 0x40, 8, &lpf_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 6, 0x40, 8, &lpf_gain_db); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 6, 0x40, 8, &lpf_gainbits); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 6, 0x40, 8, &lpf_gainbits); write_phy_reg(pi, 0x20, init_gaincode); write_phy_reg(pi, 0x2a7, init_gaincode); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, pi->pubpi.phy_corenum, 0x106, 16, rfseq_init_gain); write_phy_reg(pi, 0x22, clip1hi_gaincode); write_phy_reg(pi, 0x2a9, clip1hi_gaincode); write_phy_reg(pi, 0x24, clip1md_gaincode); write_phy_reg(pi, 0x2ab, clip1md_gaincode); write_phy_reg(pi, 0x37, clip1lo_gaincode); write_phy_reg(pi, 0x2ad, clip1lo_gaincode); mod_phy_reg(pi, 0x27d, (0xff << 0), (crsmin_th << 0)); mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0)); mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0)); write_phy_reg(pi, 0x2b, nbclip_th); write_phy_reg(pi, 0x41, nbclip_th); mod_phy_reg(pi, 0x27, (0x3f << 0), (w1clip_th << 0)); mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1clip_th << 0)); write_phy_reg(pi, 0x150, 0x809c); } else { mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); write_phy_reg(pi, 0x2b, 0x84); write_phy_reg(pi, 0x41, 0x84); if (CHSPEC_IS20(pi->radio_chanspec)) { write_phy_reg(pi, 0x6b, 0x2b); write_phy_reg(pi, 0x6c, 0x2b); write_phy_reg(pi, 0x6d, 0x9); write_phy_reg(pi, 0x6e, 0x9); } w1th = NPHY_RSSICAL_W1_TARGET - 4; mod_phy_reg(pi, 0x27, (0x3f << 0), (w1th << 0)); mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1th << 0)); if (CHSPEC_IS20(pi->radio_chanspec)) { mod_phy_reg(pi, 0x1c, (0x1f << 0), (0x1 << 0)); mod_phy_reg(pi, 0x32, (0x1f << 0), (0x1 << 0)); mod_phy_reg(pi, 0x1d, (0x1f << 0), (0x1 << 0)); mod_phy_reg(pi, 0x33, (0x1f << 0), (0x1 << 0)); } write_phy_reg(pi, 0x150, 0x809c); if (pi->nphy_gain_boost) if ((CHSPEC_IS2G(pi->radio_chanspec)) && (CHSPEC_IS40(pi->radio_chanspec))) hpf_code = 4; else hpf_code = 5; else if (CHSPEC_IS40(pi->radio_chanspec)) hpf_code = 6; else hpf_code = 7; mod_phy_reg(pi, 0x20, (0x1f << 7), (hpf_code << 7)); mod_phy_reg(pi, 0x36, (0x1f << 7), (hpf_code << 7)); for (ctr = 0; ctr < 4; ctr++) regval[ctr] = (hpf_code << 8) | 0x7c; wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval); wlc_phy_adjust_lnagaintbl_nphy(pi); if (pi->nphy_elna_gain_config) { regval[0] = 0; regval[1] = 1; regval[2] = 1; regval[3] = 1; wlc_phy_table_write_nphy(pi, 2, 4, 8, 16, regval); wlc_phy_table_write_nphy(pi, 3, 4, 8, 16, regval); for (ctr = 0; ctr < 4; ctr++) regval[ctr] = (hpf_code << 8) | 0x74; wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval); } if (NREV_IS(pi->pubpi.phy_rev, 2)) { for (ctr = 0; ctr < 21; ctr++) regval[ctr] = 3 * ctr; wlc_phy_table_write_nphy(pi, 0, 21, 32, 16, regval); wlc_phy_table_write_nphy(pi, 1, 21, 32, 16, regval); for (ctr = 0; ctr < 21; ctr++) regval[ctr] = (u16) ctr; wlc_phy_table_write_nphy(pi, 2, 21, 32, 16, regval); wlc_phy_table_write_nphy(pi, 3, 21, 32, 16, regval); } wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_UPDATEGAINU, rfseq_updategainu_events, rfseq_updategainu_dlys, sizeof(rfseq_updategainu_events) / sizeof(rfseq_updategainu_events[0])); mod_phy_reg(pi, 0x153, (0xff << 8), (90 << 8)); if (CHSPEC_IS2G(pi->radio_chanspec)) mod_phy_reg(pi, (NPHY_TO_BPHY_OFF + BPHY_OPTIONAL_MODES), 0x7f, 0x4); } } static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) { u8 rfseq_rx2tx_events[] = { NPHY_RFSEQ_CMD_NOP, NPHY_RFSEQ_CMD_RXG_FBW, NPHY_RFSEQ_CMD_TR_SWITCH, NPHY_RFSEQ_CMD_CLR_HIQ_DIS, NPHY_RFSEQ_CMD_RXPD_TXPD, NPHY_RFSEQ_CMD_TX_GAIN, NPHY_RFSEQ_CMD_EXT_PA }; u8 rfseq_rx2tx_dlys[] = { 8, 6, 6, 2, 4, 60, 1 }; u8 rfseq_tx2rx_events[] = { NPHY_RFSEQ_CMD_NOP, NPHY_RFSEQ_CMD_EXT_PA, NPHY_RFSEQ_CMD_TX_GAIN, NPHY_RFSEQ_CMD_RXPD_TXPD, NPHY_RFSEQ_CMD_TR_SWITCH, NPHY_RFSEQ_CMD_RXG_FBW, NPHY_RFSEQ_CMD_CLR_HIQ_DIS }; u8 rfseq_tx2rx_dlys[] = { 8, 6, 2, 4, 4, 6, 1 }; u8 rfseq_tx2rx_events_rev3[] = { NPHY_REV3_RFSEQ_CMD_EXT_PA, NPHY_REV3_RFSEQ_CMD_INT_PA_PU, NPHY_REV3_RFSEQ_CMD_TX_GAIN, NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, NPHY_REV3_RFSEQ_CMD_TR_SWITCH, NPHY_REV3_RFSEQ_CMD_RXG_FBW, NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, NPHY_REV3_RFSEQ_CMD_END }; u8 rfseq_tx2rx_dlys_rev3[] = { 8, 4, 2, 2, 4, 4, 6, 1 }; u8 rfseq_rx2tx_events_rev3[] = { NPHY_REV3_RFSEQ_CMD_NOP, NPHY_REV3_RFSEQ_CMD_RXG_FBW, NPHY_REV3_RFSEQ_CMD_TR_SWITCH, NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, NPHY_REV3_RFSEQ_CMD_TX_GAIN, NPHY_REV3_RFSEQ_CMD_INT_PA_PU, NPHY_REV3_RFSEQ_CMD_EXT_PA, NPHY_REV3_RFSEQ_CMD_END }; u8 rfseq_rx2tx_dlys_rev3[] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; u8 rfseq_rx2tx_events_rev3_ipa[] = { NPHY_REV3_RFSEQ_CMD_NOP, NPHY_REV3_RFSEQ_CMD_RXG_FBW, NPHY_REV3_RFSEQ_CMD_TR_SWITCH, NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, NPHY_REV3_RFSEQ_CMD_TX_GAIN, NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS, NPHY_REV3_RFSEQ_CMD_INT_PA_PU, NPHY_REV3_RFSEQ_CMD_END }; u8 rfseq_rx2tx_dlys_rev3_ipa[] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; u16 rfseq_rx2tx_dacbufpu_rev7[] = { 0x10f, 0x10f }; s16 alpha0, alpha1, alpha2; s16 beta0, beta1, beta2; u32 leg_data_weights, ht_data_weights, nss1_data_weights, stbc_data_weights; u8 chan_freq_range = 0; u16 dac_control = 0x0002; u16 aux_adc_vmid_rev7_core0[] = { 0x8e, 0x96, 0x96, 0x96 }; u16 aux_adc_vmid_rev7_core1[] = { 0x8f, 0x9f, 0x9f, 0x96 }; u16 aux_adc_vmid_rev4[] = { 0xa2, 0xb4, 0xb4, 0x89 }; u16 aux_adc_vmid_rev3[] = { 0xa2, 0xb4, 0xb4, 0x89 }; u16 *aux_adc_vmid; u16 aux_adc_gain_rev7[] = { 0x02, 0x02, 0x02, 0x02 }; u16 aux_adc_gain_rev4[] = { 0x02, 0x02, 0x02, 0x00 }; u16 aux_adc_gain_rev3[] = { 0x02, 0x02, 0x02, 0x00 }; u16 *aux_adc_gain; u16 sk_adc_vmid[] = { 0xb4, 0xb4, 0xb4, 0x24 }; u16 sk_adc_gain[] = { 0x02, 0x02, 0x02, 0x02 }; s32 min_nvar_val = 0x18d; s32 min_nvar_offset_6mbps = 20; u8 pdetrange; u8 triso; u16 regval; u16 afectrl_adc_ctrl1_rev7 = 0x20; u16 afectrl_adc_ctrl2_rev7 = 0x0; u16 rfseq_rx2tx_lpf_h_hpc_rev7 = 0x77; u16 rfseq_tx2rx_lpf_h_hpc_rev7 = 0x77; u16 rfseq_pktgn_lpf_h_hpc_rev7 = 0x77; u16 rfseq_htpktgn_lpf_hpc_rev7[] = { 0x77, 0x11, 0x11 }; u16 rfseq_pktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; u16 rfseq_cckpktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; u16 ipalvlshift_3p3_war_en = 0; u16 rccal_bcap_val, rccal_scap_val; u16 rccal_tx20_11b_bcap = 0; u16 rccal_tx20_11b_scap = 0; u16 rccal_tx20_11n_bcap = 0; u16 rccal_tx20_11n_scap = 0; u16 rccal_tx40_11n_bcap = 0; u16 rccal_tx40_11n_scap = 0; u16 rx2tx_lpf_rc_lut_tx20_11b = 0; u16 rx2tx_lpf_rc_lut_tx20_11n = 0; u16 rx2tx_lpf_rc_lut_tx40_11n = 0; u16 tx_lpf_bw_ofdm_20mhz = 0; u16 tx_lpf_bw_ofdm_40mhz = 0; u16 tx_lpf_bw_11b = 0; u16 ipa2g_mainbias, ipa2g_casconv, ipa2g_biasfilt; u16 txgm_idac_bleed = 0; bool rccal_ovrd = false; u16 freq; int coreNum; if (CHSPEC_IS5G(pi->radio_chanspec)) wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 0); else wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 1); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); or_phy_reg(pi, 0xb1, NPHY_IQFlip_ADC1 | NPHY_IQFlip_ADC2); if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (NREV_IS(pi->pubpi.phy_rev, 7)) { mod_phy_reg(pi, 0x221, (0x1 << 4), (1 << 4)); mod_phy_reg(pi, 0x160, (0x7f << 0), (32 << 0)); mod_phy_reg(pi, 0x160, (0x7f << 8), (39 << 8)); mod_phy_reg(pi, 0x161, (0x7f << 0), (46 << 0)); mod_phy_reg(pi, 0x161, (0x7f << 8), (51 << 8)); mod_phy_reg(pi, 0x162, (0x7f << 0), (55 << 0)); mod_phy_reg(pi, 0x162, (0x7f << 8), (58 << 8)); mod_phy_reg(pi, 0x163, (0x7f << 0), (60 << 0)); mod_phy_reg(pi, 0x163, (0x7f << 8), (62 << 8)); mod_phy_reg(pi, 0x164, (0x7f << 0), (62 << 0)); mod_phy_reg(pi, 0x164, (0x7f << 8), (63 << 8)); mod_phy_reg(pi, 0x165, (0x7f << 0), (63 << 0)); mod_phy_reg(pi, 0x165, (0x7f << 8), (64 << 8)); mod_phy_reg(pi, 0x166, (0x7f << 0), (64 << 0)); mod_phy_reg(pi, 0x166, (0x7f << 8), (64 << 8)); mod_phy_reg(pi, 0x167, (0x7f << 0), (64 << 0)); mod_phy_reg(pi, 0x167, (0x7f << 8), (64 << 8)); } if (NREV_LE(pi->pubpi.phy_rev, 8)) { write_phy_reg(pi, 0x23f, 0x1b0); write_phy_reg(pi, 0x240, 0x1b0); } if (NREV_GE(pi->pubpi.phy_rev, 8)) mod_phy_reg(pi, 0xbd, (0xff << 0), (114 << 0)); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x00, 16, &dac_control); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x10, 16, &dac_control); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, 1, 0, 32, &leg_data_weights); leg_data_weights = leg_data_weights & 0xffffff; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, 1, 0, 32, &leg_data_weights); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x15e, 16, rfseq_rx2tx_dacbufpu_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x16e, 16, rfseq_rx2tx_dacbufpu_rev7); if (PHY_IPA(pi)) wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, rfseq_rx2tx_events_rev3_ipa, rfseq_rx2tx_dlys_rev3_ipa, ARRAY_SIZE(rfseq_rx2tx_events_rev3_ipa)); mod_phy_reg(pi, 0x299, (0x3 << 14), (0x1 << 14)); mod_phy_reg(pi, 0x29d, (0x3 << 14), (0x1 << 14)); tx_lpf_bw_ofdm_20mhz = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x154); tx_lpf_bw_ofdm_40mhz = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x159); tx_lpf_bw_11b = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x152); if (PHY_IPA(pi)) { if (((pi->pubpi.radiorev == 5) && (CHSPEC_IS40(pi->radio_chanspec) == 1)) || (pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { rccal_bcap_val = read_radio_reg( pi, RADIO_2057_RCCAL_BCAP_VAL); rccal_scap_val = read_radio_reg( pi, RADIO_2057_RCCAL_SCAP_VAL); rccal_tx20_11b_bcap = rccal_bcap_val; rccal_tx20_11b_scap = rccal_scap_val; if ((pi->pubpi.radiorev == 5) && (CHSPEC_IS40(pi->radio_chanspec) == 1)) { rccal_tx20_11n_bcap = rccal_bcap_val; rccal_tx20_11n_scap = rccal_scap_val; rccal_tx40_11n_bcap = 0xc; rccal_tx40_11n_scap = 0xc; rccal_ovrd = true; } else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { tx_lpf_bw_ofdm_20mhz = 4; tx_lpf_bw_11b = 1; if (CHSPEC_IS2G(pi->radio_chanspec)) { rccal_tx20_11n_bcap = 0xc; rccal_tx20_11n_scap = 0xc; rccal_tx40_11n_bcap = 0xa; rccal_tx40_11n_scap = 0xa; } else { rccal_tx20_11n_bcap = 0x14; rccal_tx20_11n_scap = 0x14; rccal_tx40_11n_bcap = 0xf; rccal_tx40_11n_scap = 0xf; } rccal_ovrd = true; } } } else { if (pi->pubpi.radiorev == 5) { tx_lpf_bw_ofdm_20mhz = 1; tx_lpf_bw_ofdm_40mhz = 3; rccal_bcap_val = read_radio_reg( pi, RADIO_2057_RCCAL_BCAP_VAL); rccal_scap_val = read_radio_reg( pi, RADIO_2057_RCCAL_SCAP_VAL); rccal_tx20_11b_bcap = rccal_bcap_val; rccal_tx20_11b_scap = rccal_scap_val; rccal_tx20_11n_bcap = 0x13; rccal_tx20_11n_scap = 0x11; rccal_tx40_11n_bcap = 0x13; rccal_tx40_11n_scap = 0x11; rccal_ovrd = true; } } if (rccal_ovrd) { rx2tx_lpf_rc_lut_tx20_11b = (rccal_tx20_11b_bcap << 8) | (rccal_tx20_11b_scap << 3) | tx_lpf_bw_11b; rx2tx_lpf_rc_lut_tx20_11n = (rccal_tx20_11n_bcap << 8) | (rccal_tx20_11n_scap << 3) | tx_lpf_bw_ofdm_20mhz; rx2tx_lpf_rc_lut_tx40_11n = (rccal_tx40_11n_bcap << 8) | (rccal_tx40_11n_scap << 3) | tx_lpf_bw_ofdm_40mhz; for (coreNum = 0; coreNum <= 1; coreNum++) { wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x152 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx20_11b); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x153 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx20_11n); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x154 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx20_11n); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x155 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx40_11n); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x156 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx40_11n); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x157 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx40_11n); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x158 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx40_11n); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, 0x159 + coreNum * 0x10, 16, &rx2tx_lpf_rc_lut_tx40_11n); } wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 4), 1, 0x3, 0, NPHY_REV7_RFCTRLOVERRIDE_ID2); } write_phy_reg(pi, 0x32f, 0x3); if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 2), 1, 0x3, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) { if ((pi->sh->sromrev >= 8) && (pi->sh->boardflags2 & BFL2_IPALVLSHIFT_3P3)) ipalvlshift_3p3_war_en = 1; if (ipalvlshift_3p3_war_en) { write_radio_reg(pi, RADIO_2057_GPAIO_CONFIG, 0x5); write_radio_reg(pi, RADIO_2057_GPAIO_SEL1, 0x30); write_radio_reg(pi, RADIO_2057_GPAIO_SEL0, 0x0); or_radio_reg(pi, RADIO_2057_RXTXBIAS_CONFIG_CORE0, 0x1); or_radio_reg(pi, RADIO_2057_RXTXBIAS_CONFIG_CORE1, 0x1); ipa2g_mainbias = 0x1f; ipa2g_casconv = 0x6f; ipa2g_biasfilt = 0xaa; } else { ipa2g_mainbias = 0x2b; ipa2g_casconv = 0x7f; ipa2g_biasfilt = 0xee; } if (CHSPEC_IS2G(pi->radio_chanspec)) { for (coreNum = 0; coreNum <= 1; coreNum++) { WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, IPA2G_IMAIN, ipa2g_mainbias); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, IPA2G_CASCONV, ipa2g_casconv); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, IPA2G_BIAS_FILTER, ipa2g_biasfilt); } } } if (PHY_IPA(pi)) { if (CHSPEC_IS2G(pi->radio_chanspec)) { if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) txgm_idac_bleed = 0x7f; for (coreNum = 0; coreNum <= 1; coreNum++) { if (txgm_idac_bleed != 0) WRITE_RADIO_REG4( pi, RADIO_2057, CORE, coreNum, TXGM_IDAC_BLEED, txgm_idac_bleed); } if (pi->pubpi.radiorev == 5) { for (coreNum = 0; coreNum <= 1; coreNum++) { WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, IPA2G_CASCONV, 0x13); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, IPA2G_IMAIN, 0x1f); WRITE_RADIO_REG4( pi, RADIO_2057, CORE, coreNum, IPA2G_BIAS_FILTER, 0xee); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, PAD2G_IDACS, 0x8a); WRITE_RADIO_REG4( pi, RADIO_2057, CORE, coreNum, PAD_BIAS_FILTER_BWS, 0x3e); } } else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { if (CHSPEC_IS40(pi->radio_chanspec) == 0) { WRITE_RADIO_REG4(pi, RADIO_2057, CORE, 0, IPA2G_IMAIN, 0x14); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, 1, IPA2G_IMAIN, 0x12); } else { WRITE_RADIO_REG4(pi, RADIO_2057, CORE, 0, IPA2G_IMAIN, 0x16); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, 1, IPA2G_IMAIN, 0x16); } } } else { freq = CHAN5G_FREQ(CHSPEC_CHANNEL( pi->radio_chanspec)); if (((freq >= 5180) && (freq <= 5230)) || ((freq >= 5745) && (freq <= 5805))) { WRITE_RADIO_REG4(pi, RADIO_2057, CORE, 0, IPA5G_BIAS_FILTER, 0xff); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, 1, IPA5G_BIAS_FILTER, 0xff); } } } else { if (pi->pubpi.radiorev != 5) { for (coreNum = 0; coreNum <= 1; coreNum++) { WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, TXMIX2G_TUNE_BOOST_PU, 0x61); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, TXGM_IDAC_BLEED, 0x70); } } } if (pi->pubpi.radiorev == 4) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x05, 16, &afectrl_adc_ctrl1_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x15, 16, &afectrl_adc_ctrl1_rev7); for (coreNum = 0; coreNum <= 1; coreNum++) { WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, AFE_VCM_CAL_MASTER, 0x0); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, AFE_SET_VCM_I, 0x3f); WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, AFE_SET_VCM_Q, 0x3f); } } else { mod_phy_reg(pi, 0xa6, (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, 0x8f, (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, 0xa7, (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, 0xa5, (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, 0xa6, (0x1 << 0), 0); mod_phy_reg(pi, 0x8f, (0x1 << 0), (0x1 << 0)); mod_phy_reg(pi, 0xa7, (0x1 << 0), 0); mod_phy_reg(pi, 0xa5, (0x1 << 0), (0x1 << 0)); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x05, 16, &afectrl_adc_ctrl2_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x15, 16, &afectrl_adc_ctrl2_rev7); mod_phy_reg(pi, 0xa6, (0x1 << 2), 0); mod_phy_reg(pi, 0x8f, (0x1 << 2), 0); mod_phy_reg(pi, 0xa7, (0x1 << 2), 0); mod_phy_reg(pi, 0xa5, (0x1 << 2), 0); } write_phy_reg(pi, 0x6a, 0x2); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 256, 32, &min_nvar_offset_6mbps); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x138, 16, &rfseq_pktgn_lpf_hpc_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x141, 16, &rfseq_pktgn_lpf_h_hpc_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 3, 0x133, 16, &rfseq_htpktgn_lpf_hpc_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x146, 16, &rfseq_cckpktgn_lpf_hpc_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x123, 16, &rfseq_tx2rx_lpf_h_hpc_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x12A, 16, &rfseq_rx2tx_lpf_h_hpc_rev7); if (CHSPEC_IS40(pi->radio_chanspec) == 0) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, 32, &min_nvar_val); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 127, 32, &min_nvar_val); } else { min_nvar_val = noise_var_tbl_rev7[3]; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, 32, &min_nvar_val); min_nvar_val = noise_var_tbl_rev7[127]; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 127, 32, &min_nvar_val); } wlc_phy_workarounds_nphy_gainctrl(pi); pdetrange = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. pdetrange : pi->srom_fem2g.pdetrange; if (pdetrange == 0) { chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { aux_adc_vmid_rev7_core0[3] = 0x70; aux_adc_vmid_rev7_core1[3] = 0x70; aux_adc_gain_rev7[3] = 2; } else { aux_adc_vmid_rev7_core0[3] = 0x80; aux_adc_vmid_rev7_core1[3] = 0x80; aux_adc_gain_rev7[3] = 3; } } else if (pdetrange == 1) { if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { aux_adc_vmid_rev7_core0[3] = 0x7c; aux_adc_vmid_rev7_core1[3] = 0x7c; aux_adc_gain_rev7[3] = 2; } else { aux_adc_vmid_rev7_core0[3] = 0x8c; aux_adc_vmid_rev7_core1[3] = 0x8c; aux_adc_gain_rev7[3] = 1; } } else if (pdetrange == 2) { if (pi->pubpi.radioid == BCM2057_ID) { if ((pi->pubpi.radiorev == 5) || (pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { if (chan_freq_range == WL_CHAN_FREQ_RANGE_2G) { aux_adc_vmid_rev7_core0[3] = 0x8c; aux_adc_vmid_rev7_core1[3] = 0x8c; aux_adc_gain_rev7[3] = 0; } else { aux_adc_vmid_rev7_core0[3] = 0x96; aux_adc_vmid_rev7_core1[3] = 0x96; aux_adc_gain_rev7[3] = 0; } } } } else if (pdetrange == 3) { if (chan_freq_range == WL_CHAN_FREQ_RANGE_2G) { aux_adc_vmid_rev7_core0[3] = 0x89; aux_adc_vmid_rev7_core1[3] = 0x89; aux_adc_gain_rev7[3] = 0; } } else if (pdetrange == 5) { if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { aux_adc_vmid_rev7_core0[3] = 0x80; aux_adc_vmid_rev7_core1[3] = 0x80; aux_adc_gain_rev7[3] = 3; } else { aux_adc_vmid_rev7_core0[3] = 0x70; aux_adc_vmid_rev7_core1[3] = 0x70; aux_adc_gain_rev7[3] = 2; } } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, &aux_adc_vmid_rev7_core0); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, &aux_adc_vmid_rev7_core1); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, &aux_adc_gain_rev7); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, &aux_adc_gain_rev7); } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_phy_reg(pi, 0x23f, 0x1f8); write_phy_reg(pi, 0x240, 0x1f8); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, 1, 0, 32, &leg_data_weights); leg_data_weights = leg_data_weights & 0xffffff; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, 1, 0, 32, &leg_data_weights); alpha0 = 293; alpha1 = 435; alpha2 = 261; beta0 = 366; beta1 = 205; beta2 = 32; write_phy_reg(pi, 0x145, alpha0); write_phy_reg(pi, 0x146, alpha1); write_phy_reg(pi, 0x147, alpha2); write_phy_reg(pi, 0x148, beta0); write_phy_reg(pi, 0x149, beta1); write_phy_reg(pi, 0x14a, beta2); write_phy_reg(pi, 0x38, 0xC); write_phy_reg(pi, 0x2ae, 0xC); wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_TX2RX, rfseq_tx2rx_events_rev3, rfseq_tx2rx_dlys_rev3, ARRAY_SIZE(rfseq_tx2rx_events_rev3)); if (PHY_IPA(pi)) wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, rfseq_rx2tx_events_rev3_ipa, rfseq_rx2tx_dlys_rev3_ipa, ARRAY_SIZE(rfseq_rx2tx_events_rev3_ipa)); if ((pi->sh->hw_phyrxchain != 0x3) && (pi->sh->hw_phyrxchain != pi->sh->hw_phytxchain)) { if (PHY_IPA(pi)) { rfseq_rx2tx_dlys_rev3[5] = 59; rfseq_rx2tx_dlys_rev3[6] = 1; rfseq_rx2tx_events_rev3[7] = NPHY_REV3_RFSEQ_CMD_END; } wlc_phy_set_rfseq_nphy( pi, NPHY_RFSEQ_RX2TX, rfseq_rx2tx_events_rev3, rfseq_rx2tx_dlys_rev3, ARRAY_SIZE(rfseq_rx2tx_events_rev3)); } if (CHSPEC_IS2G(pi->radio_chanspec)) write_phy_reg(pi, 0x6a, 0x2); else write_phy_reg(pi, 0x6a, 0x9c40); mod_phy_reg(pi, 0x294, (0xf << 8), (7 << 8)); if (CHSPEC_IS40(pi->radio_chanspec) == 0) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, 32, &min_nvar_val); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 127, 32, &min_nvar_val); } else { min_nvar_val = noise_var_tbl_rev3[3]; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, 32, &min_nvar_val); min_nvar_val = noise_var_tbl_rev3[127]; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 127, 32, &min_nvar_val); } wlc_phy_workarounds_nphy_gainctrl(pi); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x00, 16, &dac_control); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x10, 16, &dac_control); pdetrange = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. pdetrange : pi->srom_fem2g.pdetrange; if (pdetrange == 0) { if (NREV_GE(pi->pubpi.phy_rev, 4)) { aux_adc_vmid = aux_adc_vmid_rev4; aux_adc_gain = aux_adc_gain_rev4; } else { aux_adc_vmid = aux_adc_vmid_rev3; aux_adc_gain = aux_adc_gain_rev3; } chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { switch (chan_freq_range) { case WL_CHAN_FREQ_RANGE_5GL: aux_adc_vmid[3] = 0x89; aux_adc_gain[3] = 0; break; case WL_CHAN_FREQ_RANGE_5GM: aux_adc_vmid[3] = 0x89; aux_adc_gain[3] = 0; break; case WL_CHAN_FREQ_RANGE_5GH: aux_adc_vmid[3] = 0x89; aux_adc_gain[3] = 0; break; default: break; } } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, aux_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, aux_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, aux_adc_gain); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, aux_adc_gain); } else if (pdetrange == 1) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, sk_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, sk_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, sk_adc_gain); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, sk_adc_gain); } else if (pdetrange == 2) { u16 bcm_adc_vmid[] = { 0xa2, 0xb4, 0xb4, 0x74 }; u16 bcm_adc_gain[] = { 0x02, 0x02, 0x02, 0x04 }; if (NREV_GE(pi->pubpi.phy_rev, 6)) { chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { bcm_adc_vmid[3] = 0x8e; bcm_adc_gain[3] = 0x03; } else { bcm_adc_vmid[3] = 0x94; bcm_adc_gain[3] = 0x03; } } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { bcm_adc_vmid[3] = 0x84; bcm_adc_gain[3] = 0x02; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, bcm_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, bcm_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, bcm_adc_gain); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, bcm_adc_gain); } else if (pdetrange == 3) { chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); if ((NREV_GE(pi->pubpi.phy_rev, 4)) && (chan_freq_range == WL_CHAN_FREQ_RANGE_2G)) { u16 auxadc_vmid[] = { 0xa2, 0xb4, 0xb4, 0x270 }; u16 auxadc_gain[] = { 0x02, 0x02, 0x02, 0x00 }; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, auxadc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, auxadc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, auxadc_gain); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, auxadc_gain); } } else if ((pdetrange == 4) || (pdetrange == 5)) { u16 bcm_adc_vmid[] = { 0xa2, 0xb4, 0xb4, 0x0 }; u16 bcm_adc_gain[] = { 0x02, 0x02, 0x02, 0x0 }; u16 Vmid[2], Av[2]; chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { Vmid[0] = (pdetrange == 4) ? 0x8e : 0x89; Vmid[1] = (pdetrange == 4) ? 0x96 : 0x89; Av[0] = (pdetrange == 4) ? 2 : 0; Av[1] = (pdetrange == 4) ? 2 : 0; } else { Vmid[0] = (pdetrange == 4) ? 0x89 : 0x74; Vmid[1] = (pdetrange == 4) ? 0x8b : 0x70; Av[0] = (pdetrange == 4) ? 2 : 0; Av[1] = (pdetrange == 4) ? 2 : 0; } bcm_adc_vmid[3] = Vmid[0]; bcm_adc_gain[3] = Av[0]; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, bcm_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, bcm_adc_gain); bcm_adc_vmid[3] = Vmid[1]; bcm_adc_gain[3] = Av[1]; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, bcm_adc_vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, bcm_adc_gain); } write_radio_reg(pi, (RADIO_2056_RX_MIXA_MAST_BIAS | RADIO_2056_RX0), 0x0); write_radio_reg(pi, (RADIO_2056_RX_MIXA_MAST_BIAS | RADIO_2056_RX1), 0x0); write_radio_reg(pi, (RADIO_2056_RX_MIXA_BIAS_MAIN | RADIO_2056_RX0), 0x6); write_radio_reg(pi, (RADIO_2056_RX_MIXA_BIAS_MAIN | RADIO_2056_RX1), 0x6); write_radio_reg(pi, (RADIO_2056_RX_MIXA_BIAS_AUX | RADIO_2056_RX0), 0x7); write_radio_reg(pi, (RADIO_2056_RX_MIXA_BIAS_AUX | RADIO_2056_RX1), 0x7); write_radio_reg(pi, (RADIO_2056_RX_MIXA_LOB_BIAS | RADIO_2056_RX0), 0x88); write_radio_reg(pi, (RADIO_2056_RX_MIXA_LOB_BIAS | RADIO_2056_RX1), 0x88); write_radio_reg(pi, (RADIO_2056_RX_MIXA_CMFB_IDAC | RADIO_2056_RX0), 0x0); write_radio_reg(pi, (RADIO_2056_RX_MIXA_CMFB_IDAC | RADIO_2056_RX1), 0x0); write_radio_reg(pi, (RADIO_2056_RX_MIXG_CMFB_IDAC | RADIO_2056_RX0), 0x0); write_radio_reg(pi, (RADIO_2056_RX_MIXG_CMFB_IDAC | RADIO_2056_RX1), 0x0); triso = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. triso : pi->srom_fem2g.triso; if (triso == 7) { wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0); wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1); } wlc_phy_war_txchain_upd_nphy(pi, pi->sh->hw_phytxchain); if (((pi->sh->boardflags2 & BFL2_APLL_WAR) && (CHSPEC_IS5G(pi->radio_chanspec))) || (((pi->sh->boardflags2 & BFL2_GPLL_WAR) || (pi->sh->boardflags2 & BFL2_GPLL_WAR2)) && (CHSPEC_IS2G(pi->radio_chanspec)))) { nss1_data_weights = 0x00088888; ht_data_weights = 0x00088888; stbc_data_weights = 0x00088888; } else { nss1_data_weights = 0x88888888; ht_data_weights = 0x88888888; stbc_data_weights = 0x88888888; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, 1, 1, 32, &nss1_data_weights); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, 1, 2, 32, &ht_data_weights); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, 1, 3, 32, &stbc_data_weights); if (NREV_IS(pi->pubpi.phy_rev, 4)) { if (CHSPEC_IS5G(pi->radio_chanspec)) { write_radio_reg(pi, RADIO_2056_TX_GMBB_IDAC | RADIO_2056_TX0, 0x70); write_radio_reg(pi, RADIO_2056_TX_GMBB_IDAC | RADIO_2056_TX1, 0x70); } } if (!pi->edcrs_threshold_lock) { write_phy_reg(pi, 0x224, 0x3eb); write_phy_reg(pi, 0x225, 0x3eb); write_phy_reg(pi, 0x226, 0x341); write_phy_reg(pi, 0x227, 0x341); write_phy_reg(pi, 0x228, 0x42b); write_phy_reg(pi, 0x229, 0x42b); write_phy_reg(pi, 0x22a, 0x381); write_phy_reg(pi, 0x22b, 0x381); write_phy_reg(pi, 0x22c, 0x42b); write_phy_reg(pi, 0x22d, 0x42b); write_phy_reg(pi, 0x22e, 0x381); write_phy_reg(pi, 0x22f, 0x381); } if (NREV_GE(pi->pubpi.phy_rev, 6)) { if (pi->sh->boardflags2 & BFL2_SINGLEANT_CCK) wlapi_bmac_mhf(pi->sh->physhim, MHF4, MHF4_BPHY_TXCORE0, MHF4_BPHY_TXCORE0, BRCM_BAND_ALL); } } else { if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD || (pi->sh->boardtype == 0x8b)) { uint i; u8 war_dlys[] = { 1, 6, 6, 2, 4, 20, 1 }; for (i = 0; i < ARRAY_SIZE(rfseq_rx2tx_dlys); i++) rfseq_rx2tx_dlys[i] = war_dlys[i]; } if (CHSPEC_IS5G(pi->radio_chanspec) && pi->phy_5g_pwrgain) { and_radio_reg(pi, RADIO_2055_CORE1_TX_RF_SPARE, 0xf7); and_radio_reg(pi, RADIO_2055_CORE2_TX_RF_SPARE, 0xf7); } else { or_radio_reg(pi, RADIO_2055_CORE1_TX_RF_SPARE, 0x8); or_radio_reg(pi, RADIO_2055_CORE2_TX_RF_SPARE, 0x8); } regval = 0x000a; wlc_phy_table_write_nphy(pi, 8, 1, 0, 16, ®val); wlc_phy_table_write_nphy(pi, 8, 1, 0x10, 16, ®val); if (NREV_LT(pi->pubpi.phy_rev, 3)) { regval = 0xcdaa; wlc_phy_table_write_nphy(pi, 8, 1, 0x02, 16, ®val); wlc_phy_table_write_nphy(pi, 8, 1, 0x12, 16, ®val); } if (NREV_LT(pi->pubpi.phy_rev, 2)) { regval = 0x0000; wlc_phy_table_write_nphy(pi, 8, 1, 0x08, 16, ®val); wlc_phy_table_write_nphy(pi, 8, 1, 0x18, 16, ®val); regval = 0x7aab; wlc_phy_table_write_nphy(pi, 8, 1, 0x07, 16, ®val); wlc_phy_table_write_nphy(pi, 8, 1, 0x17, 16, ®val); regval = 0x0800; wlc_phy_table_write_nphy(pi, 8, 1, 0x06, 16, ®val); wlc_phy_table_write_nphy(pi, 8, 1, 0x16, 16, ®val); } write_phy_reg(pi, 0xf8, 0x02d8); write_phy_reg(pi, 0xf9, 0x0301); write_phy_reg(pi, 0xfa, 0x02d8); write_phy_reg(pi, 0xfb, 0x0301); wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, rfseq_rx2tx_events, rfseq_rx2tx_dlys, ARRAY_SIZE(rfseq_rx2tx_events)); wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_TX2RX, rfseq_tx2rx_events, rfseq_tx2rx_dlys, ARRAY_SIZE(rfseq_tx2rx_events)); wlc_phy_workarounds_nphy_gainctrl(pi); if (NREV_LT(pi->pubpi.phy_rev, 2)) { if (read_phy_reg(pi, 0xa0) & NPHY_MLenable) wlapi_bmac_mhf(pi->sh->physhim, MHF3, MHF3_NPHY_MLADV_WAR, MHF3_NPHY_MLADV_WAR, BRCM_BAND_ALL); } else if (NREV_IS(pi->pubpi.phy_rev, 2)) { write_phy_reg(pi, 0x1e3, 0x0); write_phy_reg(pi, 0x1e4, 0x0); } if (NREV_LT(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0x90, (0x1 << 7), 0); alpha0 = 293; alpha1 = 435; alpha2 = 261; beta0 = 366; beta1 = 205; beta2 = 32; write_phy_reg(pi, 0x145, alpha0); write_phy_reg(pi, 0x146, alpha1); write_phy_reg(pi, 0x147, alpha2); write_phy_reg(pi, 0x148, beta0); write_phy_reg(pi, 0x149, beta1); write_phy_reg(pi, 0x14a, beta2); if (NREV_LT(pi->pubpi.phy_rev, 3)) { mod_phy_reg(pi, 0x142, (0xf << 12), 0); write_phy_reg(pi, 0x192, 0xb5); write_phy_reg(pi, 0x193, 0xa4); write_phy_reg(pi, 0x194, 0x0); } if (NREV_IS(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0x221, NPHY_FORCESIG_DECODEGATEDCLKS, NPHY_FORCESIG_DECODEGATEDCLKS); } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static void wlc_phy_extpa_set_tx_digi_filts_nphy(struct brcms_phy *pi) { int j, type = 2; u16 addr_offset = 0x2c5; for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, addr_offset + j, NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]); } static void wlc_phy_clip_det_nphy(struct brcms_phy *pi, u8 write, u16 *vals) { if (write == 0) { vals[0] = read_phy_reg(pi, 0x2c); vals[1] = read_phy_reg(pi, 0x42); } else { write_phy_reg(pi, 0x2c, vals[0]); write_phy_reg(pi, 0x42, vals[1]); } } static void wlc_phy_ipa_internal_tssi_setup_nphy(struct brcms_phy *pi) { u8 core; if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (core = 0; core < pi->pubpi.phy_corenum; core++) { if (CHSPEC_IS2G(pi->radio_chanspec)) { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MASTER, 0x5); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, 0xe); if (pi->pubpi.radiorev != 5) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA, 0); if (!NREV_IS(pi->pubpi.phy_rev, 7)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, 0x1); else WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, 0x31); } else { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MASTER, 0x9); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, 0xc); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, 0); if (pi->pubpi.radiorev != 5) { if (!NREV_IS(pi->pubpi.phy_rev, 7)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA, 0x1); else WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA, 0x31); } } WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, 0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, 0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, 0x3); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, 0x0); } } else { WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR31, (CHSPEC_IS2G(pi->radio_chanspec)) ? 0x128 : 0x80); WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR30, 0x0); WRITE_RADIO_SYN(pi, RADIO_2056, GPIO_MASTER1, 0x29); for (core = 0; core < pi->pubpi.phy_corenum; core++) { WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_VCM_HG, 0x0); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_IDAC, 0x0); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_VCM, 0x3); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_AMP_DET, 0x0); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC1, 0x8); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC2, 0x0); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC3, 0x0); if (CHSPEC_IS2G(pi->radio_chanspec)) { WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_SSI_MASTER, 0x5); if (pi->pubpi.radiorev != 5) WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSIA, 0x0); if (NREV_GE(pi->pubpi.phy_rev, 5)) WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSIG, 0x31); else WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSIG, 0x11); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_SSI_MUX, 0xe); } else { WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_SSI_MASTER, 0x9); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSIA, 0x31); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSIG, 0x0); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_SSI_MUX, 0xc); } } } } static void wlc_phy_rfctrl_override_nphy(struct brcms_phy *pi, u16 field, u16 value, u8 core_mask, u8 off) { u8 core_num; u16 addr = 0, mask = 0, en_addr = 0, val_addr = 0, en_mask = 0, val_mask = 0; u8 shift = 0, val_shift = 0; if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { en_mask = field; for (core_num = 0; core_num < 2; core_num++) { switch (field) { case (0x1 << 1): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 0); val_shift = 0; break; case (0x1 << 2): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 1); val_shift = 1; break; case (0x1 << 3): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 2); val_shift = 2; break; case (0x1 << 4): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 4); val_shift = 4; break; case (0x1 << 5): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 5); val_shift = 5; break; case (0x1 << 6): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 6); val_shift = 6; break; case (0x1 << 7): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x1 << 7); val_shift = 7; break; case (0x1 << 8): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x7 << 8); val_shift = 8; break; case (0x1 << 11): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7a : 0x7d; val_mask = (0x7 << 13); val_shift = 13; break; case (0x1 << 9): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0xf8 : 0xfa; val_mask = (0x7 << 0); val_shift = 0; break; case (0x1 << 10): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0xf8 : 0xfa; val_mask = (0x7 << 4); val_shift = 4; break; case (0x1 << 12): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7b : 0x7e; val_mask = (0xffff << 0); val_shift = 0; break; case (0x1 << 13): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0x7c : 0x7f; val_mask = (0xffff << 0); val_shift = 0; break; case (0x1 << 14): en_addr = (core_num == 0) ? 0xe7 : 0xec; val_addr = (core_num == 0) ? 0xf9 : 0xfb; val_mask = (0x3 << 6); val_shift = 6; break; case (0x1 << 0): en_addr = (core_num == 0) ? 0xe5 : 0xe6; val_addr = (core_num == 0) ? 0xf9 : 0xfb; val_mask = (0x1 << 15); val_shift = 15; break; default: addr = 0xffff; break; } if (off) { and_phy_reg(pi, en_addr, ~en_mask); and_phy_reg(pi, val_addr, ~val_mask); } else { if ((core_mask == 0) || (core_mask & (1 << core_num))) { or_phy_reg(pi, en_addr, en_mask); if (addr != 0xffff) mod_phy_reg(pi, val_addr, val_mask, (value << val_shift)); } } } } else { if (off) { and_phy_reg(pi, 0xec, ~field); value = 0x0; } else { or_phy_reg(pi, 0xec, field); } for (core_num = 0; core_num < 2; core_num++) { switch (field) { case (0x1 << 1): case (0x1 << 9): case (0x1 << 12): case (0x1 << 13): case (0x1 << 14): addr = 0x78; core_mask = 0x1; break; case (0x1 << 2): case (0x1 << 3): case (0x1 << 4): case (0x1 << 5): case (0x1 << 6): case (0x1 << 7): case (0x1 << 8): addr = (core_num == 0) ? 0x7a : 0x7d; break; case (0x1 << 10): addr = (core_num == 0) ? 0x7b : 0x7e; break; case (0x1 << 11): addr = (core_num == 0) ? 0x7c : 0x7f; break; default: addr = 0xffff; } switch (field) { case (0x1 << 1): mask = (0x7 << 3); shift = 3; break; case (0x1 << 9): mask = (0x1 << 2); shift = 2; break; case (0x1 << 12): mask = (0x1 << 8); shift = 8; break; case (0x1 << 13): mask = (0x1 << 9); shift = 9; break; case (0x1 << 14): mask = (0xf << 12); shift = 12; break; case (0x1 << 2): mask = (0x1 << 0); shift = 0; break; case (0x1 << 3): mask = (0x1 << 1); shift = 1; break; case (0x1 << 4): mask = (0x1 << 2); shift = 2; break; case (0x1 << 5): mask = (0x3 << 4); shift = 4; break; case (0x1 << 6): mask = (0x3 << 6); shift = 6; break; case (0x1 << 7): mask = (0x1 << 8); shift = 8; break; case (0x1 << 8): mask = (0x1 << 9); shift = 9; break; case (0x1 << 10): mask = 0x1fff; shift = 0x0; break; case (0x1 << 11): mask = 0x1fff; shift = 0x0; break; default: mask = 0x0; shift = 0x0; break; } if ((addr != 0xffff) && (core_mask & (1 << core_num))) mod_phy_reg(pi, addr, mask, (value << shift)); } or_phy_reg(pi, 0xec, (0x1 << 0)); or_phy_reg(pi, 0x78, (0x1 << 0)); udelay(1); and_phy_reg(pi, 0xec, ~(0x1 << 0)); } } static void wlc_phy_txpwrctrl_idle_tssi_nphy(struct brcms_phy *pi) { s32 rssi_buf[4]; s32 int_val; if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) || PHY_MUTED(pi)) return; if (PHY_IPA(pi)) wlc_phy_ipa_internal_tssi_setup_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 7)) wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), 0, 0x3, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); else if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 0); wlc_phy_stopplayback_nphy(pi); wlc_phy_tx_tone_nphy(pi, 4000, 0, 0, 0, false); udelay(20); int_val = wlc_phy_poll_rssi_nphy(pi, (u8) NPHY_RSSI_SEL_TSSI_2G, rssi_buf, 1); wlc_phy_stopplayback_nphy(pi); wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, 0); if (NREV_GE(pi->pubpi.phy_rev, 7)) wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID0); else if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 1); if (NREV_GE(pi->pubpi.phy_rev, 3)) { pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g = (u8) ((int_val >> 24) & 0xff); pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g = (u8) ((int_val >> 24) & 0xff); pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g = (u8) ((int_val >> 8) & 0xff); pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g = (u8) ((int_val >> 8) & 0xff); } else { pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g = (u8) ((int_val >> 24) & 0xff); pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g = (u8) ((int_val >> 8) & 0xff); pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g = (u8) ((int_val >> 16) & 0xff); pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g = (u8) ((int_val) & 0xff); } } static void wlc_phy_txpwr_limit_to_tbl_nphy(struct brcms_phy *pi) { u8 idx, idx2, i, delta_ind; for (idx = TXP_FIRST_CCK; idx <= TXP_LAST_CCK; idx++) pi->adj_pwr_tbl_nphy[idx] = pi->tx_power_offset[idx]; for (i = 0; i < 4; i++) { idx2 = 0; delta_ind = 0; switch (i) { case 0: if (CHSPEC_IS40(pi->radio_chanspec) && NPHY_IS_SROM_REINTERPRET) { idx = TXP_FIRST_MCS_40_SISO; } else { idx = (CHSPEC_IS40(pi->radio_chanspec)) ? TXP_FIRST_OFDM_40_SISO : TXP_FIRST_OFDM; delta_ind = 1; } break; case 1: idx = (CHSPEC_IS40(pi->radio_chanspec)) ? TXP_FIRST_MCS_40_CDD : TXP_FIRST_MCS_20_CDD; break; case 2: idx = (CHSPEC_IS40(pi->radio_chanspec)) ? TXP_FIRST_MCS_40_STBC : TXP_FIRST_MCS_20_STBC; break; case 3: idx = (CHSPEC_IS40(pi->radio_chanspec)) ? TXP_FIRST_MCS_40_SDM : TXP_FIRST_MCS_20_SDM; break; } pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; idx = idx + delta_ind; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx++]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx++]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx++]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx++]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx++]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx++]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; idx = idx + 1 - delta_ind; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = pi->tx_power_offset[idx]; } } static void wlc_phy_txpwrctrl_pwr_setup_nphy(struct brcms_phy *pi) { u32 idx; s16 a1[2], b0[2], b1[2]; s8 target_pwr_qtrdbm[2]; s32 num, den, pwr_est; u8 chan_freq_range; u8 idle_tssi[2]; u32 tbl_id, tbl_len, tbl_offset; u32 regval[64]; u8 core; if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); udelay(1); } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); or_phy_reg(pi, 0x122, (0x1 << 0)); if (NREV_GE(pi->pubpi.phy_rev, 3)) and_phy_reg(pi, 0x1e7, (u16) (~(0x1 << 15))); else or_phy_reg(pi, 0x1e7, (0x1 << 15)); if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); if (pi->sh->sromrev < 4) { idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; a1[0] = -424; a1[1] = -424; b0[0] = 5612; b0[1] = 5612; b1[1] = -1393; b1[0] = -1393; } else { chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); switch (chan_freq_range) { case WL_CHAN_FREQ_RANGE_2G: idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_a1; a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_a1; b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b0; b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b0; b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b1; b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b1; break; case WL_CHAN_FREQ_RANGE_5GL: idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1; a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1; b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0; b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0; b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1; b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1; break; case WL_CHAN_FREQ_RANGE_5GM: idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_a1; a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_a1; b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b0; b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b0; b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b1; b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b1; break; case WL_CHAN_FREQ_RANGE_5GH: idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1; a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1; b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0; b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0; b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1; b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1; break; default: idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; a1[0] = -424; a1[1] = -424; b0[0] = 5612; b0[1] = 5612; b1[1] = -1393; b1[0] = -1393; break; } } /* use the provided transmit power */ target_pwr_qtrdbm[0] = (s8) pi->tx_power_max; target_pwr_qtrdbm[1] = (s8) pi->tx_power_max; if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (pi->srom_fem2g.tssipos) or_phy_reg(pi, 0x1e9, (0x1 << 14)); if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (core = 0; core <= 1; core++) { if (PHY_IPA(pi)) { if (CHSPEC_IS2G(pi->radio_chanspec)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, 0xe); else WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, 0xc); } } } else { if (PHY_IPA(pi)) { write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | RADIO_2056_TX0, (CHSPEC_IS5G (pi->radio_chanspec)) ? 0xc : 0xe); write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | RADIO_2056_TX1, (CHSPEC_IS5G (pi->radio_chanspec)) ? 0xc : 0xe); } else { write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | RADIO_2056_TX0, 0x11); write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | RADIO_2056_TX1, 0x11); } } } if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); udelay(1); } if (NREV_GE(pi->pubpi.phy_rev, 7)) mod_phy_reg(pi, 0x1e7, (0x7f << 0), (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0)); else mod_phy_reg(pi, 0x1e7, (0x7f << 0), (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0)); if (NREV_GE(pi->pubpi.phy_rev, 7)) mod_phy_reg(pi, 0x222, (0xff << 0), (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0)); else if (NREV_GT(pi->pubpi.phy_rev, 1)) mod_phy_reg(pi, 0x222, (0xff << 0), (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0)); if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); write_phy_reg(pi, 0x1e8, (0x3 << 8) | (240 << 0)); write_phy_reg(pi, 0x1e9, (1 << 15) | (idle_tssi[0] << 0) | (idle_tssi[1] << 8)); write_phy_reg(pi, 0x1ea, (target_pwr_qtrdbm[0] << 0) | (target_pwr_qtrdbm[1] << 8)); tbl_len = 64; tbl_offset = 0; for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { for (idx = 0; idx < tbl_len; idx++) { num = 8 * (16 * b0[tbl_id - 26] + b1[tbl_id - 26] * idx); den = 32768 + a1[tbl_id - 26] * idx; pwr_est = max(((4 * num + den / 2) / den), -8); if (NREV_LT(pi->pubpi.phy_rev, 3)) { if (idx <= (uint) (31 - idle_tssi[tbl_id - 26] + 1)) pwr_est = max(pwr_est, target_pwr_qtrdbm [tbl_id - 26] + 1); } regval[idx] = (u32) pwr_est; } wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, regval); } wlc_phy_txpwr_limit_to_tbl_nphy(pi); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, 8, pi->adj_pwr_tbl_nphy); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, 8, pi->adj_pwr_tbl_nphy); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static u32 *wlc_phy_get_ipa_gaintbl_nphy(struct brcms_phy *pi) { u32 *tx_pwrctrl_tbl = NULL; if (CHSPEC_IS2G(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_2g_2057rev4n6; else if (pi->pubpi.radiorev == 3) tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_2g_2057rev3; else if (pi->pubpi.radiorev == 5) tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_2g_2057rev5; else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_2g_2057rev7; } else if (NREV_IS(pi->pubpi.phy_rev, 6)) { tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev6; if (pi->sh->chip == BCMA_CHIP_ID_BCM47162) tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5; } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5; } else { tx_pwrctrl_tbl = nphy_tpc_txgain_ipa; } } else { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g_2057; else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g_2057rev7; } else { tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g; } } return tx_pwrctrl_tbl; } static void wlc_phy_restore_rssical_nphy(struct brcms_phy *pi) { if (CHSPEC_IS2G(pi->radio_chanspec)) { if (pi->nphy_rssical_chanspec_2G == 0) return; if (NREV_GE(pi->pubpi.phy_rev, 7)) { mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0, RADIO_2057_VCM_MASK, pi->rssical_cache. rssical_radio_regs_2G[0]); mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1, RADIO_2057_VCM_MASK, pi->rssical_cache. rssical_radio_regs_2G[1]); } else { mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0, RADIO_2056_VCM_MASK, pi->rssical_cache. rssical_radio_regs_2G[0]); mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1, RADIO_2056_VCM_MASK, pi->rssical_cache. rssical_radio_regs_2G[1]); } write_phy_reg(pi, 0x1a6, pi->rssical_cache.rssical_phyregs_2G[0]); write_phy_reg(pi, 0x1ac, pi->rssical_cache.rssical_phyregs_2G[1]); write_phy_reg(pi, 0x1b2, pi->rssical_cache.rssical_phyregs_2G[2]); write_phy_reg(pi, 0x1b8, pi->rssical_cache.rssical_phyregs_2G[3]); write_phy_reg(pi, 0x1a4, pi->rssical_cache.rssical_phyregs_2G[4]); write_phy_reg(pi, 0x1aa, pi->rssical_cache.rssical_phyregs_2G[5]); write_phy_reg(pi, 0x1b0, pi->rssical_cache.rssical_phyregs_2G[6]); write_phy_reg(pi, 0x1b6, pi->rssical_cache.rssical_phyregs_2G[7]); write_phy_reg(pi, 0x1a5, pi->rssical_cache.rssical_phyregs_2G[8]); write_phy_reg(pi, 0x1ab, pi->rssical_cache.rssical_phyregs_2G[9]); write_phy_reg(pi, 0x1b1, pi->rssical_cache.rssical_phyregs_2G[10]); write_phy_reg(pi, 0x1b7, pi->rssical_cache.rssical_phyregs_2G[11]); } else { if (pi->nphy_rssical_chanspec_5G == 0) return; if (NREV_GE(pi->pubpi.phy_rev, 7)) { mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0, RADIO_2057_VCM_MASK, pi->rssical_cache. rssical_radio_regs_5G[0]); mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1, RADIO_2057_VCM_MASK, pi->rssical_cache. rssical_radio_regs_5G[1]); } else { mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0, RADIO_2056_VCM_MASK, pi->rssical_cache. rssical_radio_regs_5G[0]); mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1, RADIO_2056_VCM_MASK, pi->rssical_cache. rssical_radio_regs_5G[1]); } write_phy_reg(pi, 0x1a6, pi->rssical_cache.rssical_phyregs_5G[0]); write_phy_reg(pi, 0x1ac, pi->rssical_cache.rssical_phyregs_5G[1]); write_phy_reg(pi, 0x1b2, pi->rssical_cache.rssical_phyregs_5G[2]); write_phy_reg(pi, 0x1b8, pi->rssical_cache.rssical_phyregs_5G[3]); write_phy_reg(pi, 0x1a4, pi->rssical_cache.rssical_phyregs_5G[4]); write_phy_reg(pi, 0x1aa, pi->rssical_cache.rssical_phyregs_5G[5]); write_phy_reg(pi, 0x1b0, pi->rssical_cache.rssical_phyregs_5G[6]); write_phy_reg(pi, 0x1b6, pi->rssical_cache.rssical_phyregs_5G[7]); write_phy_reg(pi, 0x1a5, pi->rssical_cache.rssical_phyregs_5G[8]); write_phy_reg(pi, 0x1ab, pi->rssical_cache.rssical_phyregs_5G[9]); write_phy_reg(pi, 0x1b1, pi->rssical_cache.rssical_phyregs_5G[10]); write_phy_reg(pi, 0x1b7, pi->rssical_cache.rssical_phyregs_5G[11]); } } static void wlc_phy_internal_cal_txgain_nphy(struct brcms_phy *pi) { u16 txcal_gain[2]; pi->nphy_txcal_pwr_idx[0] = pi->nphy_cal_orig_pwr_idx[0]; pi->nphy_txcal_pwr_idx[1] = pi->nphy_cal_orig_pwr_idx[0]; wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true); wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, txcal_gain); if (CHSPEC_IS2G(pi->radio_chanspec)) { txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F40; txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F40; } else { txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F60; txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F60; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, txcal_gain); } static void wlc_phy_precal_txgain_nphy(struct brcms_phy *pi) { bool save_bbmult = false; u8 txcal_index_2057_rev5n7 = 0; u8 txcal_index_2057_rev3n4n6 = 10; if (pi->use_int_tx_iqlo_cal_nphy) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) { pi->nphy_txcal_pwr_idx[0] = txcal_index_2057_rev3n4n6; pi->nphy_txcal_pwr_idx[1] = txcal_index_2057_rev3n4n6; wlc_phy_txpwr_index_nphy( pi, 3, txcal_index_2057_rev3n4n6, false); } else { pi->nphy_txcal_pwr_idx[0] = txcal_index_2057_rev5n7; pi->nphy_txcal_pwr_idx[1] = txcal_index_2057_rev5n7; wlc_phy_txpwr_index_nphy( pi, 3, txcal_index_2057_rev5n7, false); } save_bbmult = true; } else if (NREV_LT(pi->pubpi.phy_rev, 5)) { wlc_phy_cal_txgainctrl_nphy(pi, 11, false); if (pi->sh->hw_phytxchain != 3) { pi->nphy_txcal_pwr_idx[1] = pi->nphy_txcal_pwr_idx[0]; wlc_phy_txpwr_index_nphy(pi, 3, pi-> nphy_txcal_pwr_idx[0], true); save_bbmult = true; } } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { if (PHY_IPA(pi)) { if (CHSPEC_IS2G(pi->radio_chanspec)) { wlc_phy_cal_txgainctrl_nphy(pi, 12, false); } else { pi->nphy_txcal_pwr_idx[0] = 80; pi->nphy_txcal_pwr_idx[1] = 80; wlc_phy_txpwr_index_nphy(pi, 3, 80, false); save_bbmult = true; } } else { wlc_phy_internal_cal_txgain_nphy(pi); save_bbmult = true; } } else if (NREV_IS(pi->pubpi.phy_rev, 6)) { if (PHY_IPA(pi)) { if (CHSPEC_IS2G(pi->radio_chanspec)) wlc_phy_cal_txgainctrl_nphy(pi, 12, false); else wlc_phy_cal_txgainctrl_nphy(pi, 14, false); } else { wlc_phy_internal_cal_txgain_nphy(pi); save_bbmult = true; } } } else { wlc_phy_cal_txgainctrl_nphy(pi, 10, false); } if (save_bbmult) wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &pi->nphy_txcal_bbmult); } static void wlc_phy_rfctrlintc_override_nphy(struct brcms_phy *pi, u8 field, u16 value, u8 core_code) { u16 mask; u16 val; u8 core; if (NREV_GE(pi->pubpi.phy_rev, 3)) { for (core = 0; core < pi->pubpi.phy_corenum; core++) { if (core_code == RADIO_MIMO_CORESEL_CORE1 && core == PHY_CORE_1) continue; else if (core_code == RADIO_MIMO_CORESEL_CORE2 && core == PHY_CORE_0) continue; if (NREV_LT(pi->pubpi.phy_rev, 7)) { mask = (0x1 << 10); val = 1 << 10; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); } if (field == NPHY_RfctrlIntc_override_OFF) { write_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, 0); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); } else if (field == NPHY_RfctrlIntc_override_TRSW) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { mask = (0x1 << 6) | (0x1 << 7); val = value << 6; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); or_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, (0x1 << 10)); and_phy_reg(pi, 0x2ff, (u16) ~(0x3 << 14)); or_phy_reg(pi, 0x2ff, (0x1 << 13)); or_phy_reg(pi, 0x2ff, (0x1 << 0)); } else { mask = (0x1 << 6) | (0x1 << 7) | (0x1 << 8) | (0x1 << 9); val = value << 6; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); mask = (0x1 << 0); val = 1 << 0; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xe7 : 0xec, mask, val); mask = (core == PHY_CORE_0) ? (0x1 << 0) : (0x1 << 1); val = 1 << ((core == PHY_CORE_0) ? 0 : 1); mod_phy_reg(pi, 0x78, mask, val); SPINWAIT(((read_phy_reg(pi, 0x78) & val) != 0), 10000); if (WARN(read_phy_reg(pi, 0x78) & val, "HW error: override failed")) return; mask = (0x1 << 0); val = 0 << 0; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xe7 : 0xec, mask, val); } } else if (field == NPHY_RfctrlIntc_override_PA) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { mask = (0x1 << 4) | (0x1 << 5); if (CHSPEC_IS5G(pi->radio_chanspec)) val = value << 5; else val = value << 4; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); or_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, (0x1 << 12)); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { mask = (0x1 << 5); val = value << 5; } else { mask = (0x1 << 4); val = value << 4; } mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); } } else if (field == NPHY_RfctrlIntc_override_EXT_LNA_PU) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (CHSPEC_IS5G(pi->radio_chanspec)) { mask = (0x1 << 0); val = value << 0; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); mask = (0x1 << 2); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, 0); } else { mask = (0x1 << 2); val = value << 2; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); mask = (0x1 << 0); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, 0); } mask = (0x1 << 11); val = 1 << 11; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { mask = (0x1 << 0); val = value << 0; } else { mask = (0x1 << 2); val = value << 2; } mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); } } else if (field == NPHY_RfctrlIntc_override_EXT_LNA_GAIN) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (CHSPEC_IS5G(pi->radio_chanspec)) { mask = (0x1 << 1); val = value << 1; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); mask = (0x1 << 3); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, 0); } else { mask = (0x1 << 3); val = value << 3; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); mask = (0x1 << 1); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, 0); } mask = (0x1 << 11); val = 1 << 11; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { mask = (0x1 << 1); val = value << 1; } else { mask = (0x1 << 3); val = value << 3; } mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : 0x92, mask, val); } } } } } void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, bool debug) { int gainctrl_loopidx; uint core; u16 m0m1, curr_m0m1; s32 delta_power; s32 txpwrindex; s32 qdBm_power[2]; u16 orig_BBConfig; u16 phy_saveregs[4]; u32 freq_test; u16 ampl_test = 250; uint stepsize; bool phyhang_avoid_state = false; if (NREV_GE(pi->pubpi.phy_rev, 7)) stepsize = 2; else stepsize = 1; if (CHSPEC_IS40(pi->radio_chanspec)) freq_test = 5000; else freq_test = 2500; wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true); wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); phyhang_avoid_state = pi->phyhang_avoid; pi->phyhang_avoid = false; phy_saveregs[0] = read_phy_reg(pi, 0x91); phy_saveregs[1] = read_phy_reg(pi, 0x92); phy_saveregs[2] = read_phy_reg(pi, 0xe7); phy_saveregs[3] = read_phy_reg(pi, 0xec); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 1, RADIO_MIMO_CORESEL_CORE1 | RADIO_MIMO_CORESEL_CORE2); if (!debug) { wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 0x2, RADIO_MIMO_CORESEL_CORE1); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 0x8, RADIO_MIMO_CORESEL_CORE2); } else { wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 0x1, RADIO_MIMO_CORESEL_CORE1); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 0x7, RADIO_MIMO_CORESEL_CORE2); } orig_BBConfig = read_phy_reg(pi, 0x01); mod_phy_reg(pi, 0x01, (0x1 << 15), 0); wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1); for (core = 0; core < pi->pubpi.phy_corenum; core++) { txpwrindex = (s32) pi->nphy_cal_orig_pwr_idx[core]; for (gainctrl_loopidx = 0; gainctrl_loopidx < 2; gainctrl_loopidx++) { wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0, false); if (core == PHY_CORE_0) curr_m0m1 = m0m1 & 0xff00; else curr_m0m1 = m0m1 & 0x00ff; wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &curr_m0m1); wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &curr_m0m1); udelay(50); wlc_phy_est_tonepwr_nphy(pi, qdBm_power, NPHY_CAL_TSSISAMPS); pi->nphy_bb_mult_save = 0; wlc_phy_stopplayback_nphy(pi); delta_power = (dBm_targetpower * 4) - qdBm_power[core]; txpwrindex -= stepsize * delta_power; if (txpwrindex < 0) txpwrindex = 0; else if (txpwrindex > 127) txpwrindex = 127; if (CHSPEC_IS5G(pi->radio_chanspec)) { if (NREV_IS(pi->pubpi.phy_rev, 4) && (pi->srom_fem5g.extpagain == 3)) { if (txpwrindex < 30) txpwrindex = 30; } } else { if (NREV_GE(pi->pubpi.phy_rev, 5) && (pi->srom_fem2g.extpagain == 3)) { if (txpwrindex < 50) txpwrindex = 50; } } wlc_phy_txpwr_index_nphy(pi, (1 << core), (u8) txpwrindex, true); } pi->nphy_txcal_pwr_idx[core] = (u8) txpwrindex; if (debug) { u16 radio_gain; u16 dbg_m0m1; wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &dbg_m0m1); wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0, false); wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &dbg_m0m1); wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &dbg_m0m1); udelay(100); wlc_phy_est_tonepwr_nphy(pi, qdBm_power, NPHY_CAL_TSSISAMPS); wlc_phy_table_read_nphy(pi, 7, 1, (0x110 + core), 16, &radio_gain); mdelay(4000); pi->nphy_bb_mult_save = 0; wlc_phy_stopplayback_nphy(pi); } } wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_txcal_pwr_idx[0], true); wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_txcal_pwr_idx[1], true); wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &pi->nphy_txcal_bbmult); write_phy_reg(pi, 0x01, orig_BBConfig); write_phy_reg(pi, 0x91, phy_saveregs[0]); write_phy_reg(pi, 0x92, phy_saveregs[1]); write_phy_reg(pi, 0xe7, phy_saveregs[2]); write_phy_reg(pi, 0xec, phy_saveregs[3]); pi->phyhang_avoid = phyhang_avoid_state; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static void wlc_phy_savecal_nphy(struct brcms_phy *pi) { void *tbl_ptr; int coreNum; u16 *txcal_radio_regs = NULL; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); if (CHSPEC_IS2G(pi->radio_chanspec)) { wlc_phy_rx_iq_coeffs_nphy(pi, 0, &pi->calibration_cache. rxcal_coeffs_2G); if (NREV_GE(pi->pubpi.phy_rev, 7)) { txcal_radio_regs = pi->calibration_cache.txcal_radio_regs_2G; } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { pi->calibration_cache.txcal_radio_regs_2G[0] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_2G[1] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_2G[2] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX1); pi->calibration_cache.txcal_radio_regs_2G[3] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX1); pi->calibration_cache.txcal_radio_regs_2G[4] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_2G[5] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_2G[6] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX1); pi->calibration_cache.txcal_radio_regs_2G[7] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX1); } else { pi->calibration_cache.txcal_radio_regs_2G[0] = read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL); pi->calibration_cache.txcal_radio_regs_2G[1] = read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL); pi->calibration_cache.txcal_radio_regs_2G[2] = read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM); pi->calibration_cache.txcal_radio_regs_2G[3] = read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM); } pi->nphy_iqcal_chanspec_2G = pi->radio_chanspec; tbl_ptr = pi->calibration_cache.txcal_coeffs_2G; } else { wlc_phy_rx_iq_coeffs_nphy(pi, 0, &pi->calibration_cache. rxcal_coeffs_5G); if (NREV_GE(pi->pubpi.phy_rev, 7)) { txcal_radio_regs = pi->calibration_cache.txcal_radio_regs_5G; } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { pi->calibration_cache.txcal_radio_regs_5G[0] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_5G[1] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_5G[2] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX1); pi->calibration_cache.txcal_radio_regs_5G[3] = read_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX1); pi->calibration_cache.txcal_radio_regs_5G[4] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_5G[5] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX0); pi->calibration_cache.txcal_radio_regs_5G[6] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX1); pi->calibration_cache.txcal_radio_regs_5G[7] = read_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX1); } else { pi->calibration_cache.txcal_radio_regs_5G[0] = read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL); pi->calibration_cache.txcal_radio_regs_5G[1] = read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL); pi->calibration_cache.txcal_radio_regs_5G[2] = read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM); pi->calibration_cache.txcal_radio_regs_5G[3] = read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM); } pi->nphy_iqcal_chanspec_5G = pi->radio_chanspec; tbl_ptr = pi->calibration_cache.txcal_coeffs_5G; } if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (coreNum = 0; coreNum <= 1; coreNum++) { txcal_radio_regs[2 * coreNum] = READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_FINE_I); txcal_radio_regs[2 * coreNum + 1] = READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_FINE_Q); txcal_radio_regs[2 * coreNum + 4] = READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_COARSE_I); txcal_radio_regs[2 * coreNum + 5] = READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_COARSE_Q); } } wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 8, 80, 16, tbl_ptr); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static void wlc_phy_tx_iq_war_nphy(struct brcms_phy *pi) { struct nphy_iq_comp tx_comp; wlc_phy_table_read_nphy(pi, 15, 4, 0x50, 16, &tx_comp); wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ, tx_comp.a0); wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 2, tx_comp.b0); wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 4, tx_comp.a1); wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 6, tx_comp.b1); } static void wlc_phy_restorecal_nphy(struct brcms_phy *pi) { u16 *loft_comp; u16 txcal_coeffs_bphy[4]; u16 *tbl_ptr; int coreNum; u16 *txcal_radio_regs = NULL; if (CHSPEC_IS2G(pi->radio_chanspec)) { if (pi->nphy_iqcal_chanspec_2G == 0) return; tbl_ptr = pi->calibration_cache.txcal_coeffs_2G; loft_comp = &pi->calibration_cache.txcal_coeffs_2G[5]; } else { if (pi->nphy_iqcal_chanspec_5G == 0) return; tbl_ptr = pi->calibration_cache.txcal_coeffs_5G; loft_comp = &pi->calibration_cache.txcal_coeffs_5G[5]; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, 16, tbl_ptr); if (NREV_GE(pi->pubpi.phy_rev, 3)) { txcal_coeffs_bphy[0] = tbl_ptr[0]; txcal_coeffs_bphy[1] = tbl_ptr[1]; txcal_coeffs_bphy[2] = tbl_ptr[2]; txcal_coeffs_bphy[3] = tbl_ptr[3]; } else { txcal_coeffs_bphy[0] = 0; txcal_coeffs_bphy[1] = 0; txcal_coeffs_bphy[2] = 0; txcal_coeffs_bphy[3] = 0; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, 16, txcal_coeffs_bphy); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, 16, loft_comp); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, 16, loft_comp); if (NREV_LT(pi->pubpi.phy_rev, 2)) wlc_phy_tx_iq_war_nphy(pi); if (CHSPEC_IS2G(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { txcal_radio_regs = pi->calibration_cache.txcal_radio_regs_2G; } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_2G[0]); write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_2G[1]); write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_2G[2]); write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_2G[3]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_2G[4]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_2G[5]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_2G[6]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_2G[7]); } else { write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL, pi->calibration_cache. txcal_radio_regs_2G[0]); write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL, pi->calibration_cache. txcal_radio_regs_2G[1]); write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, pi->calibration_cache. txcal_radio_regs_2G[2]); write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, pi->calibration_cache. txcal_radio_regs_2G[3]); } wlc_phy_rx_iq_coeffs_nphy(pi, 1, &pi->calibration_cache. rxcal_coeffs_2G); } else { if (NREV_GE(pi->pubpi.phy_rev, 7)) { txcal_radio_regs = pi->calibration_cache.txcal_radio_regs_5G; } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_5G[0]); write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_5G[1]); write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_I | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_5G[2]); write_radio_reg(pi, RADIO_2056_TX_LOFT_FINE_Q | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_5G[3]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_5G[4]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX0, pi->calibration_cache. txcal_radio_regs_5G[5]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_I | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_5G[6]); write_radio_reg(pi, RADIO_2056_TX_LOFT_COARSE_Q | RADIO_2056_TX1, pi->calibration_cache. txcal_radio_regs_5G[7]); } else { write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL, pi->calibration_cache. txcal_radio_regs_5G[0]); write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL, pi->calibration_cache. txcal_radio_regs_5G[1]); write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, pi->calibration_cache. txcal_radio_regs_5G[2]); write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, pi->calibration_cache. txcal_radio_regs_5G[3]); } wlc_phy_rx_iq_coeffs_nphy(pi, 1, &pi->calibration_cache. rxcal_coeffs_5G); } if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (coreNum = 0; coreNum <= 1; coreNum++) { WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_FINE_I, txcal_radio_regs[2 * coreNum]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_FINE_Q, txcal_radio_regs[2 * coreNum + 1]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_COARSE_I, txcal_radio_regs[2 * coreNum + 4]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, LOFT_COARSE_Q, txcal_radio_regs[2 * coreNum + 5]); } } } static void wlc_phy_txpwrctrl_coeff_setup_nphy(struct brcms_phy *pi) { u32 idx; u16 iqloCalbuf[7]; u32 iqcomp, locomp, curr_locomp; s8 locomp_i, locomp_q; s8 curr_locomp_i, curr_locomp_q; u32 tbl_id, tbl_len, tbl_offset; u32 regval[128]; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); wlc_phy_table_read_nphy(pi, 15, 7, 80, 16, iqloCalbuf); tbl_len = 128; tbl_offset = 320; for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { iqcomp = (tbl_id == 26) ? (((u32) (iqloCalbuf[0] & 0x3ff)) << 10) | (iqloCalbuf[1] & 0x3ff) : (((u32) (iqloCalbuf[2] & 0x3ff)) << 10) | (iqloCalbuf[3] & 0x3ff); for (idx = 0; idx < tbl_len; idx++) regval[idx] = iqcomp; wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, regval); } tbl_offset = 448; for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { locomp = (u32) ((tbl_id == 26) ? iqloCalbuf[5] : iqloCalbuf[6]); locomp_i = (s8) ((locomp >> 8) & 0xff); locomp_q = (s8) ((locomp) & 0xff); for (idx = 0; idx < tbl_len; idx++) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { curr_locomp_i = locomp_i; curr_locomp_q = locomp_q; } else { curr_locomp_i = (s8) ((locomp_i * nphy_tpc_loscale[idx] + 128) >> 8); curr_locomp_q = (s8) ((locomp_q * nphy_tpc_loscale[idx] + 128) >> 8); } curr_locomp = (u32) ((curr_locomp_i & 0xff) << 8); curr_locomp |= (u32) (curr_locomp_q & 0xff); regval[idx] = curr_locomp; } wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, regval); } if (NREV_LT(pi->pubpi.phy_rev, 2)) { wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX1, 0xFFFF); wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX2, 0xFFFF); } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static void wlc_phy_txlpfbw_nphy(struct brcms_phy *pi) { u8 tx_lpf_bw = 0; if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { if (CHSPEC_IS40(pi->radio_chanspec)) tx_lpf_bw = 3; else tx_lpf_bw = 1; if (PHY_IPA(pi)) { if (CHSPEC_IS40(pi->radio_chanspec)) tx_lpf_bw = 5; else tx_lpf_bw = 4; } write_phy_reg(pi, 0xe8, (tx_lpf_bw << 0) | (tx_lpf_bw << 3) | (tx_lpf_bw << 6) | (tx_lpf_bw << 9)); if (PHY_IPA(pi)) { if (CHSPEC_IS40(pi->radio_chanspec)) tx_lpf_bw = 4; else tx_lpf_bw = 1; write_phy_reg(pi, 0xe9, (tx_lpf_bw << 0) | (tx_lpf_bw << 3) | (tx_lpf_bw << 6) | (tx_lpf_bw << 9)); } } } static void wlc_phy_adjust_rx_analpfbw_nphy(struct brcms_phy *pi, u16 reduction_factr) { if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) && CHSPEC_IS40(pi->radio_chanspec)) { if (!pi->nphy_anarxlpf_adjusted) { write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_LPC | RADIO_2056_RX0), ((pi->nphy_rccal_value + reduction_factr) | 0x80)); pi->nphy_anarxlpf_adjusted = true; } } else { if (pi->nphy_anarxlpf_adjusted) { write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_LPC | RADIO_2056_RX0), (pi->nphy_rccal_value | 0x80)); pi->nphy_anarxlpf_adjusted = false; } } } } static void wlc_phy_adjust_min_noisevar_nphy(struct brcms_phy *pi, int ntones, int *tone_id_buf, u32 *noise_var_buf) { int i; u32 offset; int tone_id; int tbllen = CHSPEC_IS40(pi->radio_chanspec) ? NPHY_NOISEVAR_TBLLEN40 : NPHY_NOISEVAR_TBLLEN20; if (pi->nphy_noisevars_adjusted) { for (i = 0; i < pi->nphy_saved_noisevars.bufcount; i++) { tone_id = pi->nphy_saved_noisevars.tone_id[i]; offset = (tone_id >= 0) ? ((tone_id * 2) + 1) : (tbllen + (tone_id * 2) + 1); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_NOISEVAR, 1, offset, 32, &pi->nphy_saved_noisevars.min_noise_vars[i]); } pi->nphy_saved_noisevars.bufcount = 0; pi->nphy_noisevars_adjusted = false; } if ((noise_var_buf != NULL) && (tone_id_buf != NULL)) { pi->nphy_saved_noisevars.bufcount = 0; for (i = 0; i < ntones; i++) { tone_id = tone_id_buf[i]; offset = (tone_id >= 0) ? ((tone_id * 2) + 1) : (tbllen + (tone_id * 2) + 1); pi->nphy_saved_noisevars.tone_id[i] = tone_id; wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, offset, 32, &pi->nphy_saved_noisevars. min_noise_vars[i]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, offset, 32, &noise_var_buf[i]); pi->nphy_saved_noisevars.bufcount++; } pi->nphy_noisevars_adjusted = true; } } static void wlc_phy_adjust_crsminpwr_nphy(struct brcms_phy *pi, u8 minpwr) { u16 regval; if (NREV_GE(pi->pubpi.phy_rev, 3)) { if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) && CHSPEC_IS40(pi->radio_chanspec)) { if (!pi->nphy_crsminpwr_adjusted) { regval = read_phy_reg(pi, 0x27d); pi->nphy_crsminpwr[0] = regval & 0xff; regval &= 0xff00; regval |= (u16) minpwr; write_phy_reg(pi, 0x27d, regval); regval = read_phy_reg(pi, 0x280); pi->nphy_crsminpwr[1] = regval & 0xff; regval &= 0xff00; regval |= (u16) minpwr; write_phy_reg(pi, 0x280, regval); regval = read_phy_reg(pi, 0x283); pi->nphy_crsminpwr[2] = regval & 0xff; regval &= 0xff00; regval |= (u16) minpwr; write_phy_reg(pi, 0x283, regval); pi->nphy_crsminpwr_adjusted = true; } } else { if (pi->nphy_crsminpwr_adjusted) { regval = read_phy_reg(pi, 0x27d); regval &= 0xff00; regval |= pi->nphy_crsminpwr[0]; write_phy_reg(pi, 0x27d, regval); regval = read_phy_reg(pi, 0x280); regval &= 0xff00; regval |= pi->nphy_crsminpwr[1]; write_phy_reg(pi, 0x280, regval); regval = read_phy_reg(pi, 0x283); regval &= 0xff00; regval |= pi->nphy_crsminpwr[2]; write_phy_reg(pi, 0x283, regval); pi->nphy_crsminpwr_adjusted = false; } } } } static void wlc_phy_spurwar_nphy(struct brcms_phy *pi) { u16 cur_channel = 0; int nphy_adj_tone_id_buf[] = { 57, 58 }; u32 nphy_adj_noise_var_buf[] = { 0x3ff, 0x3ff }; bool isAdjustNoiseVar = false; uint numTonesAdjust = 0; u32 tempval = 0; if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); cur_channel = CHSPEC_CHANNEL(pi->radio_chanspec); if (pi->nphy_gband_spurwar_en) { wlc_phy_adjust_rx_analpfbw_nphy( pi, NPHY_ANARXLPFBW_REDUCTIONFACT); if (CHSPEC_IS2G(pi->radio_chanspec)) { if ((cur_channel == 11) && CHSPEC_IS40(pi->radio_chanspec)) wlc_phy_adjust_min_noisevar_nphy( pi, 2, nphy_adj_tone_id_buf, nphy_adj_noise_var_buf); else wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL, NULL); } wlc_phy_adjust_crsminpwr_nphy(pi, NPHY_ADJUSTED_MINCRSPOWER); } if ((pi->nphy_gband_spurwar2_en) && CHSPEC_IS2G(pi->radio_chanspec)) { if (CHSPEC_IS40(pi->radio_chanspec)) { switch (cur_channel) { case 3: nphy_adj_tone_id_buf[0] = 57; nphy_adj_tone_id_buf[1] = 58; nphy_adj_noise_var_buf[0] = 0x22f; nphy_adj_noise_var_buf[1] = 0x25f; isAdjustNoiseVar = true; break; case 4: nphy_adj_tone_id_buf[0] = 41; nphy_adj_tone_id_buf[1] = 42; nphy_adj_noise_var_buf[0] = 0x22f; nphy_adj_noise_var_buf[1] = 0x25f; isAdjustNoiseVar = true; break; case 5: nphy_adj_tone_id_buf[0] = 25; nphy_adj_tone_id_buf[1] = 26; nphy_adj_noise_var_buf[0] = 0x24f; nphy_adj_noise_var_buf[1] = 0x25f; isAdjustNoiseVar = true; break; case 6: nphy_adj_tone_id_buf[0] = 9; nphy_adj_tone_id_buf[1] = 10; nphy_adj_noise_var_buf[0] = 0x22f; nphy_adj_noise_var_buf[1] = 0x24f; isAdjustNoiseVar = true; break; case 7: nphy_adj_tone_id_buf[0] = 121; nphy_adj_tone_id_buf[1] = 122; nphy_adj_noise_var_buf[0] = 0x18f; nphy_adj_noise_var_buf[1] = 0x24f; isAdjustNoiseVar = true; break; case 8: nphy_adj_tone_id_buf[0] = 105; nphy_adj_tone_id_buf[1] = 106; nphy_adj_noise_var_buf[0] = 0x22f; nphy_adj_noise_var_buf[1] = 0x25f; isAdjustNoiseVar = true; break; case 9: nphy_adj_tone_id_buf[0] = 89; nphy_adj_tone_id_buf[1] = 90; nphy_adj_noise_var_buf[0] = 0x22f; nphy_adj_noise_var_buf[1] = 0x24f; isAdjustNoiseVar = true; break; case 10: nphy_adj_tone_id_buf[0] = 73; nphy_adj_tone_id_buf[1] = 74; nphy_adj_noise_var_buf[0] = 0x22f; nphy_adj_noise_var_buf[1] = 0x24f; isAdjustNoiseVar = true; break; default: isAdjustNoiseVar = false; break; } } if (isAdjustNoiseVar) { numTonesAdjust = ARRAY_SIZE(nphy_adj_tone_id_buf); wlc_phy_adjust_min_noisevar_nphy( pi, numTonesAdjust, nphy_adj_tone_id_buf, nphy_adj_noise_var_buf); tempval = 0; } else { wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL, NULL); } } if ((pi->nphy_aband_spurwar_en) && (CHSPEC_IS5G(pi->radio_chanspec))) { switch (cur_channel) { case 54: nphy_adj_tone_id_buf[0] = 32; nphy_adj_noise_var_buf[0] = 0x25f; break; case 38: case 102: case 118: if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) && (pi->sh->chippkg == BCMA_PKG_ID_BCM4717)) { nphy_adj_tone_id_buf[0] = 32; nphy_adj_noise_var_buf[0] = 0x21f; } else { nphy_adj_tone_id_buf[0] = 0; nphy_adj_noise_var_buf[0] = 0x0; } break; case 134: nphy_adj_tone_id_buf[0] = 32; nphy_adj_noise_var_buf[0] = 0x21f; break; case 151: nphy_adj_tone_id_buf[0] = 16; nphy_adj_noise_var_buf[0] = 0x23f; break; case 153: case 161: nphy_adj_tone_id_buf[0] = 48; nphy_adj_noise_var_buf[0] = 0x23f; break; default: nphy_adj_tone_id_buf[0] = 0; nphy_adj_noise_var_buf[0] = 0x0; break; } if (nphy_adj_tone_id_buf[0] && nphy_adj_noise_var_buf[0]) wlc_phy_adjust_min_noisevar_nphy( pi, 1, nphy_adj_tone_id_buf, nphy_adj_noise_var_buf); else wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL, NULL); } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } } void wlc_phy_init_nphy(struct brcms_phy *pi) { u16 val; u16 clip1_ths[2]; struct nphy_txgains target_gain; u8 tx_pwr_ctrl_state; bool do_nphy_cal = false; uint core; u32 d11_clk_ctl_st; bool do_rssi_cal = false; core = 0; if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC; if ((ISNPHY(pi)) && (NREV_GE(pi->pubpi.phy_rev, 5)) && ((pi->sh->chippkg == BCMA_PKG_ID_BCM4717) || (pi->sh->chippkg == BCMA_PKG_ID_BCM4718))) { if ((pi->sh->boardflags & BFL_EXTLNA) && (CHSPEC_IS2G(pi->radio_chanspec))) ai_cc_reg(pi->sh->sih, offsetof(struct chipcregs, chipcontrol), 0x40, 0x40); } if ((!PHY_IPA(pi)) && (pi->sh->chip == BCMA_CHIP_ID_BCM5357)) si_pmu_chipcontrol(pi->sh->sih, 1, CCTRL5357_EXTPA, CCTRL5357_EXTPA); if ((pi->nphy_gband_spurwar2_en) && CHSPEC_IS2G(pi->radio_chanspec) && CHSPEC_IS40(pi->radio_chanspec)) { d11_clk_ctl_st = bcma_read32(pi->d11core, D11REGOFFS(clk_ctl_st)); bcma_mask32(pi->d11core, D11REGOFFS(clk_ctl_st), ~(CCS_FORCEHT | CCS_HTAREQ)); bcma_write32(pi->d11core, D11REGOFFS(clk_ctl_st), d11_clk_ctl_st); } pi->use_int_tx_iqlo_cal_nphy = (PHY_IPA(pi) || (NREV_GE(pi->pubpi.phy_rev, 7) || (NREV_GE(pi->pubpi.phy_rev, 5) && pi->sh->boardflags2 & BFL2_INTERNDET_TXIQCAL))); pi->internal_tx_iqlo_cal_tapoff_intpa_nphy = false; pi->nphy_deaf_count = 0; wlc_phy_tbl_init_nphy(pi); pi->nphy_crsminpwr_adjusted = false; pi->nphy_noisevars_adjusted = false; if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_phy_reg(pi, 0xe7, 0); write_phy_reg(pi, 0xec, 0); if (NREV_GE(pi->pubpi.phy_rev, 7)) { write_phy_reg(pi, 0x342, 0); write_phy_reg(pi, 0x343, 0); write_phy_reg(pi, 0x346, 0); write_phy_reg(pi, 0x347, 0); } write_phy_reg(pi, 0xe5, 0); write_phy_reg(pi, 0xe6, 0); } else { write_phy_reg(pi, 0xec, 0); } write_phy_reg(pi, 0x91, 0); write_phy_reg(pi, 0x92, 0); if (NREV_LT(pi->pubpi.phy_rev, 6)) { write_phy_reg(pi, 0x93, 0); write_phy_reg(pi, 0x94, 0); } and_phy_reg(pi, 0xa1, ~3); if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_phy_reg(pi, 0x8f, 0); write_phy_reg(pi, 0xa5, 0); } else { write_phy_reg(pi, 0xa5, 0); } if (NREV_IS(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b); else if (NREV_LT(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0xdc, 0x00ff, 0x40); write_phy_reg(pi, 0x203, 32); write_phy_reg(pi, 0x201, 32); if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD) write_phy_reg(pi, 0x20d, 160); else write_phy_reg(pi, 0x20d, 184); write_phy_reg(pi, 0x13a, 200); write_phy_reg(pi, 0x70, 80); write_phy_reg(pi, 0x1ff, 48); if (NREV_LT(pi->pubpi.phy_rev, 8)) wlc_phy_update_mimoconfig_nphy(pi, pi->n_preamble_override); wlc_phy_stf_chain_upd_nphy(pi); if (NREV_LT(pi->pubpi.phy_rev, 2)) { write_phy_reg(pi, 0x180, 0xaa8); write_phy_reg(pi, 0x181, 0x9a4); } if (PHY_IPA(pi)) { for (core = 0; core < pi->pubpi.phy_corenum; core++) { mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (1) << 0); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x298 : 0x29c, (0x1ff << 7), (pi->nphy_papd_epsilon_offset[core]) << 7); } wlc_phy_ipa_set_tx_digi_filts_nphy(pi); } else if (NREV_GE(pi->pubpi.phy_rev, 5)) { wlc_phy_extpa_set_tx_digi_filts_nphy(pi); } wlc_phy_workarounds_nphy(pi); wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); val = read_phy_reg(pi, 0x01); write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA); write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA)); wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); wlapi_bmac_macphyclk_set(pi->sh->physhim, ON); wlc_phy_pa_override_nphy(pi, OFF); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); wlc_phy_pa_override_nphy(pi, ON); wlc_phy_classifier_nphy(pi, 0, 0); wlc_phy_clip_det_nphy(pi, 0, clip1_ths); if (CHSPEC_IS2G(pi->radio_chanspec)) wlc_phy_bphy_init_nphy(pi); tx_pwr_ctrl_state = pi->nphy_txpwrctrl; wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); wlc_phy_txpwr_fixpower_nphy(pi); wlc_phy_txpwrctrl_idle_tssi_nphy(pi); wlc_phy_txpwrctrl_pwr_setup_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 3)) { u32 *tx_pwrctrl_tbl = NULL; u16 idx; s16 pga_gn = 0; s16 pad_gn = 0; s32 rfpwr_offset; if (PHY_IPA(pi)) { tx_pwrctrl_tbl = wlc_phy_get_ipa_gaintbl_nphy(pi); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { if (NREV_IS(pi->pubpi.phy_rev, 3)) tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev3; else if (NREV_IS(pi->pubpi.phy_rev, 4)) tx_pwrctrl_tbl = (pi->srom_fem5g.extpagain == 3) ? nphy_tpc_5GHz_txgain_HiPwrEPA : nphy_tpc_5GHz_txgain_rev4; else tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev5; } else { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (pi->pubpi.radiorev == 5) tx_pwrctrl_tbl = nphy_tpc_txgain_epa_2057rev5; else if (pi->pubpi.radiorev == 3) tx_pwrctrl_tbl = nphy_tpc_txgain_epa_2057rev3; } else { if (NREV_GE(pi->pubpi.phy_rev, 5) && (pi->srom_fem2g.extpagain == 3)) tx_pwrctrl_tbl = nphy_tpc_txgain_HiPwrEPA; else tx_pwrctrl_tbl = nphy_tpc_txgain_rev3; } } } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128, 192, 32, tx_pwrctrl_tbl); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128, 192, 32, tx_pwrctrl_tbl); pi->nphy_gmval = (u16) ((*tx_pwrctrl_tbl >> 16) & 0x7000); if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (idx = 0; idx < 128; idx++) { pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf; pad_gn = (tx_pwrctrl_tbl[idx] >> 19) & 0x1f; rfpwr_offset = get_rf_pwr_offset(pi, pga_gn, pad_gn); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_CORE1TXPWRCTL, 1, 576 + idx, 32, &rfpwr_offset); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_CORE2TXPWRCTL, 1, 576 + idx, 32, &rfpwr_offset); } } else { for (idx = 0; idx < 128; idx++) { pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf; if (CHSPEC_IS2G(pi->radio_chanspec)) rfpwr_offset = (s16) nphy_papd_pga_gain_delta_ipa_2g [pga_gn]; else rfpwr_offset = (s16) nphy_papd_pga_gain_delta_ipa_5g [pga_gn]; wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_CORE1TXPWRCTL, 1, 576 + idx, 32, &rfpwr_offset); wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_CORE2TXPWRCTL, 1, 576 + idx, 32, &rfpwr_offset); } } } else { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128, 192, 32, nphy_tpc_txgain); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128, 192, 32, nphy_tpc_txgain); } if (pi->sh->phyrxchain != 0x3) wlc_phy_rxcore_setstate_nphy((struct brcms_phy_pub *) pi, pi->sh->phyrxchain); if (PHY_PERICAL_MPHASE_PENDING(pi)) wlc_phy_cal_perical_mphase_restart(pi); if (NREV_GE(pi->pubpi.phy_rev, 3)) { do_rssi_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ? (pi->nphy_rssical_chanspec_2G == 0) : (pi->nphy_rssical_chanspec_5G == 0); if (do_rssi_cal) wlc_phy_rssi_cal_nphy(pi); else wlc_phy_restore_rssical_nphy(pi); } else { wlc_phy_rssi_cal_nphy(pi); } if (!SCAN_RM_IN_PROGRESS(pi)) do_nphy_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ? (pi->nphy_iqcal_chanspec_2G == 0) : (pi->nphy_iqcal_chanspec_5G == 0); if (!pi->do_initcal) do_nphy_cal = false; if (do_nphy_cal) { target_gain = wlc_phy_get_tx_gain_nphy(pi); if (pi->antsel_type == ANTSEL_2x3) wlc_phy_antsel_init((struct brcms_phy_pub *) pi, true); if (pi->nphy_perical != PHY_PERICAL_MPHASE) { wlc_phy_rssi_cal_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 3)) { pi->nphy_cal_orig_pwr_idx[0] = pi->nphy_txpwrindex[PHY_CORE_0] . index_internal; pi->nphy_cal_orig_pwr_idx[1] = pi->nphy_txpwrindex[PHY_CORE_1] . index_internal; wlc_phy_precal_txgain_nphy(pi); target_gain = wlc_phy_get_tx_gain_nphy(pi); } if (wlc_phy_cal_txiqlo_nphy (pi, target_gain, true, false) == 0) { if (wlc_phy_cal_rxiq_nphy (pi, target_gain, 2, false) == 0) wlc_phy_savecal_nphy(pi); } } else if (pi->mphase_cal_phase_id == MPHASE_CAL_STATE_IDLE) { wlc_phy_cal_perical((struct brcms_phy_pub *) pi, PHY_PERICAL_PHYINIT); } } else { wlc_phy_restorecal_nphy(pi); } wlc_phy_txpwrctrl_coeff_setup_nphy(pi); wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); wlc_phy_nphy_tkip_rifs_war(pi, pi->sh->_rifs_phy); if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LE(pi->pubpi.phy_rev, 6)) write_phy_reg(pi, 0x70, 50); wlc_phy_txlpfbw_nphy(pi); wlc_phy_spurwar_nphy(pi); } static void wlc_phy_resetcca_nphy(struct brcms_phy *pi) { u16 val; wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); val = read_phy_reg(pi, 0x01); write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA); udelay(1); write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA)); wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); } void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en) { u16 rfctrlintc_override_val; if (!en) { pi->rfctrlIntc1_save = read_phy_reg(pi, 0x91); pi->rfctrlIntc2_save = read_phy_reg(pi, 0x92); if (NREV_GE(pi->pubpi.phy_rev, 7)) rfctrlintc_override_val = 0x1480; else if (NREV_GE(pi->pubpi.phy_rev, 3)) rfctrlintc_override_val = CHSPEC_IS5G(pi->radio_chanspec) ? 0x600 : 0x480; else rfctrlintc_override_val = CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120; write_phy_reg(pi, 0x91, rfctrlintc_override_val); write_phy_reg(pi, 0x92, rfctrlintc_override_val); } else { write_phy_reg(pi, 0x91, pi->rfctrlIntc1_save); write_phy_reg(pi, 0x92, pi->rfctrlIntc2_save); } } void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi) { u16 txrx_chain = (NPHY_RfseqCoreActv_TxRxChain0 | NPHY_RfseqCoreActv_TxRxChain1); bool CoreActv_override = false; if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN0) { txrx_chain = NPHY_RfseqCoreActv_TxRxChain0; CoreActv_override = true; if (NREV_LE(pi->pubpi.phy_rev, 2)) and_phy_reg(pi, 0xa0, ~0x20); } else if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN1) { txrx_chain = NPHY_RfseqCoreActv_TxRxChain1; CoreActv_override = true; if (NREV_LE(pi->pubpi.phy_rev, 2)) or_phy_reg(pi, 0xa0, 0x20); } mod_phy_reg(pi, 0xa2, ((0xf << 0) | (0xf << 4)), txrx_chain); if (CoreActv_override) { pi->nphy_perical = PHY_PERICAL_DISABLE; or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override); } else { pi->nphy_perical = PHY_PERICAL_MPHASE; and_phy_reg(pi, 0xa1, ~NPHY_RfseqMode_CoreActv_override); } } void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask) { u16 regval; u16 tbl_buf[16]; uint i; struct brcms_phy *pi = (struct brcms_phy *) pih; u16 tbl_opcode; bool suspend; pi->sh->phyrxchain = rxcore_bitmask; if (!pi->sh->clk) return; suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); regval = read_phy_reg(pi, 0xa2); regval &= ~(0xf << 4); regval |= ((u16) (rxcore_bitmask & 0x3)) << 4; write_phy_reg(pi, 0xa2, regval); if ((rxcore_bitmask & 0x3) != 0x3) { write_phy_reg(pi, 0x20e, 1); if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (pi->rx2tx_biasentry == -1) { wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, ARRAY_SIZE(tbl_buf), 80, 16, tbl_buf); for (i = 0; i < ARRAY_SIZE(tbl_buf); i++) { if (tbl_buf[i] == NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS) { pi->rx2tx_biasentry = (u8) i; tbl_opcode = NPHY_REV3_RFSEQ_CMD_NOP; wlc_phy_table_write_nphy( pi, NPHY_TBL_ID_RFSEQ, 1, i, 16, &tbl_opcode); break; } else if (tbl_buf[i] == NPHY_REV3_RFSEQ_CMD_END) break; } } } } else { write_phy_reg(pi, 0x20e, 30); if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (pi->rx2tx_biasentry != -1) { tbl_opcode = NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, pi->rx2tx_biasentry, 16, &tbl_opcode); pi->rx2tx_biasentry = -1; } } } wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih) { u16 regval, rxen_bits; struct brcms_phy *pi = (struct brcms_phy *) pih; regval = read_phy_reg(pi, 0xa2); rxen_bits = (regval >> 4) & 0xf; return (u8) rxen_bits; } bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pi) { return PHY_IPA(pi); } void wlc_phy_cal_init_nphy(struct brcms_phy *pi) { } static void wlc_phy_radio_preinit_205x(struct brcms_phy *pi) { and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); and_phy_reg(pi, 0x78, RFCC_OE_POR_FORCE); or_phy_reg(pi, 0x78, ~RFCC_OE_POR_FORCE); or_phy_reg(pi, 0x78, RFCC_CHIP0_PU); } static void wlc_phy_radio_init_2057(struct brcms_phy *pi) { struct radio_20xx_regs *regs_2057_ptr = NULL; if (NREV_IS(pi->pubpi.phy_rev, 7)) { regs_2057_ptr = regs_2057_rev4; } else if (NREV_IS(pi->pubpi.phy_rev, 8) || NREV_IS(pi->pubpi.phy_rev, 9)) { switch (pi->pubpi.radiorev) { case 5: if (NREV_IS(pi->pubpi.phy_rev, 8)) regs_2057_ptr = regs_2057_rev5; else if (NREV_IS(pi->pubpi.phy_rev, 9)) regs_2057_ptr = regs_2057_rev5v1; break; case 7: regs_2057_ptr = regs_2057_rev7; break; case 8: regs_2057_ptr = regs_2057_rev8; break; default: break; } } wlc_phy_init_radio_regs_allbands(pi, regs_2057_ptr); } static u16 wlc_phy_radio205x_rcal(struct brcms_phy *pi) { u16 rcal_reg = 0; int i; if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (pi->pubpi.radiorev == 5) { and_phy_reg(pi, 0x342, ~(0x1 << 1)); udelay(10); mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x1); mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2, 0x1); } mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x1); udelay(10); mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x3, 0x3); for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS); if (rcal_reg & 0x1) break; udelay(100); } if (WARN(i == MAX_205x_RCAL_WAITLOOPS, "HW error: radio calib2")) return 0; mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x2, 0x0); rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS) & 0x3e; mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x0); if (pi->pubpi.radiorev == 5) { mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x0); mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2, 0x0); } if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) { mod_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x3c, rcal_reg); mod_radio_reg(pi, RADIO_2057_BANDGAP_RCAL_TRIM, 0xf0, rcal_reg << 2); } } else if (NREV_IS(pi->pubpi.phy_rev, 3)) { u16 savereg; savereg = read_radio_reg( pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN); write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN, savereg | 0x7); udelay(10); write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, 0x1); udelay(10); write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, 0x9); for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { rcal_reg = read_radio_reg( pi, RADIO_2056_SYN_RCAL_CODE_OUT | RADIO_2056_SYN); if (rcal_reg & 0x80) break; udelay(100); } if (WARN(i == MAX_205x_RCAL_WAITLOOPS, "HW error: radio calib3")) return 0; write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, 0x1); rcal_reg = read_radio_reg(pi, RADIO_2056_SYN_RCAL_CODE_OUT | RADIO_2056_SYN); write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, 0x0); write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN, savereg); return rcal_reg & 0x1f; } return rcal_reg & 0x3e; } static u16 wlc_phy_radio2057_rccal(struct brcms_phy *pi) { u16 rccal_valid; int i; bool chip43226_6362A0; chip43226_6362A0 = ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)); rccal_valid = 0; if (chip43226_6362A0) { write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x61); write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xc0); } else { write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x61); write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xe9); } write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); if (rccal_valid & 0x2) break; udelay(500); } write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); rccal_valid = 0; if (chip43226_6362A0) { write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x69); write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0); } else { write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x69); write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xd5); } write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); if (rccal_valid & 0x2) break; udelay(500); } write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); rccal_valid = 0; if (chip43226_6362A0) { write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x73); write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x28); write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0); } else { write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x73); write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0x99); } write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); if (rccal_valid & 0x2) break; udelay(500); } if (WARN(!(rccal_valid & 0x2), "HW error: radio calib4")) return 0; write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); return rccal_valid; } static void wlc_phy_radio_postinit_2057(struct brcms_phy *pi) { mod_radio_reg(pi, RADIO_2057_XTALPUOVR_PINCTRL, 0x1, 0x1); mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x78); mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x80); mdelay(2); mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x0); mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x0); if (pi->phy_init_por) { wlc_phy_radio205x_rcal(pi); wlc_phy_radio2057_rccal(pi); } mod_radio_reg(pi, RADIO_2057_RFPLL_MASTER, 0x8, 0x0); } static void wlc_phy_radio_init_2056(struct brcms_phy *pi) { const struct radio_regs *regs_SYN_2056_ptr = NULL; const struct radio_regs *regs_TX_2056_ptr = NULL; const struct radio_regs *regs_RX_2056_ptr = NULL; if (NREV_IS(pi->pubpi.phy_rev, 3)) { regs_SYN_2056_ptr = regs_SYN_2056; regs_TX_2056_ptr = regs_TX_2056; regs_RX_2056_ptr = regs_RX_2056; } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { regs_SYN_2056_ptr = regs_SYN_2056_A1; regs_TX_2056_ptr = regs_TX_2056_A1; regs_RX_2056_ptr = regs_RX_2056_A1; } else { switch (pi->pubpi.radiorev) { case 5: regs_SYN_2056_ptr = regs_SYN_2056_rev5; regs_TX_2056_ptr = regs_TX_2056_rev5; regs_RX_2056_ptr = regs_RX_2056_rev5; break; case 6: regs_SYN_2056_ptr = regs_SYN_2056_rev6; regs_TX_2056_ptr = regs_TX_2056_rev6; regs_RX_2056_ptr = regs_RX_2056_rev6; break; case 7: case 9: regs_SYN_2056_ptr = regs_SYN_2056_rev7; regs_TX_2056_ptr = regs_TX_2056_rev7; regs_RX_2056_ptr = regs_RX_2056_rev7; break; case 8: regs_SYN_2056_ptr = regs_SYN_2056_rev8; regs_TX_2056_ptr = regs_TX_2056_rev8; regs_RX_2056_ptr = regs_RX_2056_rev8; break; case 11: regs_SYN_2056_ptr = regs_SYN_2056_rev11; regs_TX_2056_ptr = regs_TX_2056_rev11; regs_RX_2056_ptr = regs_RX_2056_rev11; break; default: break; } } wlc_phy_init_radio_regs(pi, regs_SYN_2056_ptr, (u16) RADIO_2056_SYN); wlc_phy_init_radio_regs(pi, regs_TX_2056_ptr, (u16) RADIO_2056_TX0); wlc_phy_init_radio_regs(pi, regs_TX_2056_ptr, (u16) RADIO_2056_TX1); wlc_phy_init_radio_regs(pi, regs_RX_2056_ptr, (u16) RADIO_2056_RX0); wlc_phy_init_radio_regs(pi, regs_RX_2056_ptr, (u16) RADIO_2056_RX1); } static void wlc_phy_radio_postinit_2056(struct brcms_phy *pi) { mod_radio_reg(pi, RADIO_2056_SYN_COM_CTRL, 0xb, 0xb); mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x2); mod_radio_reg(pi, RADIO_2056_SYN_COM_RESET, 0x2, 0x2); udelay(1000); mod_radio_reg(pi, RADIO_2056_SYN_COM_RESET, 0x2, 0x0); if ((pi->sh->boardflags2 & BFL2_LEGACY) || (pi->sh->boardflags2 & BFL2_XTALBUFOUTEN)) mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xf4, 0x0); else mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xfc, 0x0); mod_radio_reg(pi, RADIO_2056_SYN_RCCAL_CTRL0, 0x1, 0x0); if (pi->phy_init_por) wlc_phy_radio205x_rcal(pi); } static void wlc_phy_radio_preinit_2055(struct brcms_phy *pi) { and_phy_reg(pi, 0x78, ~RFCC_POR_FORCE); or_phy_reg(pi, 0x78, RFCC_CHIP0_PU | RFCC_OE_POR_FORCE); or_phy_reg(pi, 0x78, RFCC_POR_FORCE); } static void wlc_phy_radio_init_2055(struct brcms_phy *pi) { wlc_phy_init_radio_regs(pi, regs_2055, RADIO_DEFAULT_CORE); } static void wlc_phy_radio_postinit_2055(struct brcms_phy *pi) { and_radio_reg(pi, RADIO_2055_MASTER_CNTRL1, ~(RADIO_2055_JTAGCTRL_MASK | RADIO_2055_JTAGSYNC_MASK)); if (((pi->sh->sromrev >= 4) && !(pi->sh->boardflags2 & BFL2_RXBB_INT_REG_DIS)) || ((pi->sh->sromrev < 4))) { and_radio_reg(pi, RADIO_2055_CORE1_RXBB_REGULATOR, 0x7F); and_radio_reg(pi, RADIO_2055_CORE2_RXBB_REGULATOR, 0x7F); } mod_radio_reg(pi, RADIO_2055_RRCCAL_N_OPT_SEL, 0x3F, 0x2C); write_radio_reg(pi, RADIO_2055_CAL_MISC, 0x3C); and_radio_reg(pi, RADIO_2055_CAL_MISC, ~(RADIO_2055_RRCAL_START | RADIO_2055_RRCAL_RST_N)); or_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, RADIO_2055_CAL_LPO_ENABLE); or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_RST_N); udelay(1000); or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_START); SPINWAIT(((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) & RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE), 2000); if (WARN((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) & RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE, "HW error: radio calibration1\n")) return; and_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, ~(RADIO_2055_CAL_LPO_ENABLE)); wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); write_radio_reg(pi, RADIO_2055_CORE1_RXBB_LPF, 9); write_radio_reg(pi, RADIO_2055_CORE2_RXBB_LPF, 9); write_radio_reg(pi, RADIO_2055_CORE1_RXBB_MIDAC_HIPAS, 0x83); write_radio_reg(pi, RADIO_2055_CORE2_RXBB_MIDAC_HIPAS, 0x83); mod_radio_reg(pi, RADIO_2055_CORE1_LNA_GAINBST, RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE); mod_radio_reg(pi, RADIO_2055_CORE2_LNA_GAINBST, RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE); if (pi->nphy_gain_boost) { and_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1, ~(RADIO_2055_GAINBST_DISABLE)); and_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1, ~(RADIO_2055_GAINBST_DISABLE)); } else { or_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1, RADIO_2055_GAINBST_DISABLE); or_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1, RADIO_2055_GAINBST_DISABLE); } udelay(2); } void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on) { if (on) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (!pi->radio_is_on) { wlc_phy_radio_preinit_205x(pi); wlc_phy_radio_init_2057(pi); wlc_phy_radio_postinit_2057(pi); } wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { wlc_phy_radio_preinit_205x(pi); wlc_phy_radio_init_2056(pi); wlc_phy_radio_postinit_2056(pi); wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); } else { wlc_phy_radio_preinit_2055(pi); wlc_phy_radio_init_2055(pi); wlc_phy_radio_postinit_2055(pi); } pi->radio_is_on = true; } else { if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x0); write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX0, 0); write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX0, 0); write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX0, 0); write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX0, 0); mod_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX0, 0xf0, 0); write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX0, 0); write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX1, 0); write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX1, 0); write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX1, 0); write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX1, 0); mod_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX1, 0xf0, 0); write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX1, 0); pi->radio_is_on = false; } if (NREV_GE(pi->pubpi.phy_rev, 8)) { and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); pi->radio_is_on = false; } } } static bool wlc_phy_chan2freq_nphy(struct brcms_phy *pi, uint channel, int *f, const struct chan_info_nphy_radio2057 **t0, const struct chan_info_nphy_radio205x **t1, const struct chan_info_nphy_radio2057_rev5 **t2, const struct chan_info_nphy_2055 **t3) { uint i; const struct chan_info_nphy_radio2057 *chan_info_tbl_p_0 = NULL; const struct chan_info_nphy_radio205x *chan_info_tbl_p_1 = NULL; const struct chan_info_nphy_radio2057_rev5 *chan_info_tbl_p_2 = NULL; u32 tbl_len = 0; int freq = 0; if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (NREV_IS(pi->pubpi.phy_rev, 7)) { chan_info_tbl_p_0 = chan_info_nphyrev7_2057_rev4; tbl_len = ARRAY_SIZE(chan_info_nphyrev7_2057_rev4); } else if (NREV_IS(pi->pubpi.phy_rev, 8) || NREV_IS(pi->pubpi.phy_rev, 9)) { switch (pi->pubpi.radiorev) { case 5: if (pi->pubpi.radiover == 0x0) { chan_info_tbl_p_2 = chan_info_nphyrev8_2057_rev5; tbl_len = ARRAY_SIZE( chan_info_nphyrev8_2057_rev5); } else if (pi->pubpi.radiover == 0x1) { chan_info_tbl_p_2 = chan_info_nphyrev9_2057_rev5v1; tbl_len = ARRAY_SIZE( chan_info_nphyrev9_2057_rev5v1); } break; case 7: chan_info_tbl_p_0 = chan_info_nphyrev8_2057_rev7; tbl_len = ARRAY_SIZE( chan_info_nphyrev8_2057_rev7); break; case 8: chan_info_tbl_p_0 = chan_info_nphyrev8_2057_rev8; tbl_len = ARRAY_SIZE( chan_info_nphyrev8_2057_rev8); break; default: break; } } else if (NREV_IS(pi->pubpi.phy_rev, 16)) { chan_info_tbl_p_0 = chan_info_nphyrev8_2057_rev8; tbl_len = ARRAY_SIZE(chan_info_nphyrev8_2057_rev8); } else { goto fail; } for (i = 0; i < tbl_len; i++) { if (pi->pubpi.radiorev == 5) { if (chan_info_tbl_p_2[i].chan == channel) break; } else { if (chan_info_tbl_p_0[i].chan == channel) break; } } if (i >= tbl_len) goto fail; if (pi->pubpi.radiorev == 5) { *t2 = &chan_info_tbl_p_2[i]; freq = chan_info_tbl_p_2[i].freq; } else { *t0 = &chan_info_tbl_p_0[i]; freq = chan_info_tbl_p_0[i].freq; } } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (NREV_IS(pi->pubpi.phy_rev, 3)) { chan_info_tbl_p_1 = chan_info_nphyrev3_2056; tbl_len = ARRAY_SIZE(chan_info_nphyrev3_2056); } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { chan_info_tbl_p_1 = chan_info_nphyrev4_2056_A1; tbl_len = ARRAY_SIZE(chan_info_nphyrev4_2056_A1); } else if (NREV_IS(pi->pubpi.phy_rev, 5) || NREV_IS(pi->pubpi.phy_rev, 6)) { switch (pi->pubpi.radiorev) { case 5: chan_info_tbl_p_1 = chan_info_nphyrev5_2056v5; tbl_len = ARRAY_SIZE(chan_info_nphyrev5_2056v5); break; case 6: chan_info_tbl_p_1 = chan_info_nphyrev6_2056v6; tbl_len = ARRAY_SIZE(chan_info_nphyrev6_2056v6); break; case 7: case 9: chan_info_tbl_p_1 = chan_info_nphyrev5n6_2056v7; tbl_len = ARRAY_SIZE(chan_info_nphyrev5n6_2056v7); break; case 8: chan_info_tbl_p_1 = chan_info_nphyrev6_2056v8; tbl_len = ARRAY_SIZE(chan_info_nphyrev6_2056v8); break; case 11: chan_info_tbl_p_1 = chan_info_nphyrev6_2056v11; tbl_len = ARRAY_SIZE( chan_info_nphyrev6_2056v11); break; default: break; } } for (i = 0; i < tbl_len; i++) { if (chan_info_tbl_p_1[i].chan == channel) break; } if (i >= tbl_len) goto fail; *t1 = &chan_info_tbl_p_1[i]; freq = chan_info_tbl_p_1[i].freq; } else { for (i = 0; i < ARRAY_SIZE(chan_info_nphy_2055); i++) if (chan_info_nphy_2055[i].chan == channel) break; if (i >= ARRAY_SIZE(chan_info_nphy_2055)) goto fail; *t3 = &chan_info_nphy_2055[i]; freq = chan_info_nphy_2055[i].freq; } *f = freq; return true; fail: *f = WL_CHAN_FREQ_RANGE_2G; return false; } u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint channel) { int freq; const struct chan_info_nphy_radio2057 *t0 = NULL; const struct chan_info_nphy_radio205x *t1 = NULL; const struct chan_info_nphy_radio2057_rev5 *t2 = NULL; const struct chan_info_nphy_2055 *t3 = NULL; if (channel == 0) channel = CHSPEC_CHANNEL(pi->radio_chanspec); wlc_phy_chan2freq_nphy(pi, channel, &freq, &t0, &t1, &t2, &t3); if (CHSPEC_IS2G(pi->radio_chanspec)) return WL_CHAN_FREQ_RANGE_2G; if ((freq >= BASE_LOW_5G_CHAN) && (freq < BASE_MID_5G_CHAN)) return WL_CHAN_FREQ_RANGE_5GL; else if ((freq >= BASE_MID_5G_CHAN) && (freq < BASE_HIGH_5G_CHAN)) return WL_CHAN_FREQ_RANGE_5GM; else return WL_CHAN_FREQ_RANGE_5GH; } static void wlc_phy_chanspec_radio2055_setup(struct brcms_phy *pi, const struct chan_info_nphy_2055 *ci) { write_radio_reg(pi, RADIO_2055_PLL_REF, ci->RF_pll_ref); write_radio_reg(pi, RADIO_2055_RF_PLL_MOD0, ci->RF_rf_pll_mod0); write_radio_reg(pi, RADIO_2055_RF_PLL_MOD1, ci->RF_rf_pll_mod1); write_radio_reg(pi, RADIO_2055_VCO_CAP_TAIL, ci->RF_vco_cap_tail); BRCMS_PHY_WAR_PR51571(pi); write_radio_reg(pi, RADIO_2055_VCO_CAL1, ci->RF_vco_cal1); write_radio_reg(pi, RADIO_2055_VCO_CAL2, ci->RF_vco_cal2); write_radio_reg(pi, RADIO_2055_PLL_LF_C1, ci->RF_pll_lf_c1); write_radio_reg(pi, RADIO_2055_PLL_LF_R1, ci->RF_pll_lf_r1); BRCMS_PHY_WAR_PR51571(pi); write_radio_reg(pi, RADIO_2055_PLL_LF_C2, ci->RF_pll_lf_c2); write_radio_reg(pi, RADIO_2055_LGBUF_CEN_BUF, ci->RF_lgbuf_cen_buf); write_radio_reg(pi, RADIO_2055_LGEN_TUNE1, ci->RF_lgen_tune1); write_radio_reg(pi, RADIO_2055_LGEN_TUNE2, ci->RF_lgen_tune2); BRCMS_PHY_WAR_PR51571(pi); write_radio_reg(pi, RADIO_2055_CORE1_LGBUF_A_TUNE, ci->RF_core1_lgbuf_a_tune); write_radio_reg(pi, RADIO_2055_CORE1_LGBUF_G_TUNE, ci->RF_core1_lgbuf_g_tune); write_radio_reg(pi, RADIO_2055_CORE1_RXRF_REG1, ci->RF_core1_rxrf_reg1); write_radio_reg(pi, RADIO_2055_CORE1_TX_PGA_PAD_TN, ci->RF_core1_tx_pga_pad_tn); BRCMS_PHY_WAR_PR51571(pi); write_radio_reg(pi, RADIO_2055_CORE1_TX_MX_BGTRIM, ci->RF_core1_tx_mx_bgtrim); write_radio_reg(pi, RADIO_2055_CORE2_LGBUF_A_TUNE, ci->RF_core2_lgbuf_a_tune); write_radio_reg(pi, RADIO_2055_CORE2_LGBUF_G_TUNE, ci->RF_core2_lgbuf_g_tune); write_radio_reg(pi, RADIO_2055_CORE2_RXRF_REG1, ci->RF_core2_rxrf_reg1); BRCMS_PHY_WAR_PR51571(pi); write_radio_reg(pi, RADIO_2055_CORE2_TX_PGA_PAD_TN, ci->RF_core2_tx_pga_pad_tn); write_radio_reg(pi, RADIO_2055_CORE2_TX_MX_BGTRIM, ci->RF_core2_tx_mx_bgtrim); udelay(50); write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x05); write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x45); BRCMS_PHY_WAR_PR51571(pi); write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x65); udelay(300); } static void wlc_phy_chanspec_radio2056_setup(struct brcms_phy *pi, const struct chan_info_nphy_radio205x *ci) { const struct radio_regs *regs_SYN_2056_ptr = NULL; write_radio_reg(pi, RADIO_2056_SYN_PLL_VCOCAL1 | RADIO_2056_SYN, ci->RF_SYN_pll_vcocal1); write_radio_reg(pi, RADIO_2056_SYN_PLL_VCOCAL2 | RADIO_2056_SYN, ci->RF_SYN_pll_vcocal2); write_radio_reg(pi, RADIO_2056_SYN_PLL_REFDIV | RADIO_2056_SYN, ci->RF_SYN_pll_refdiv); write_radio_reg(pi, RADIO_2056_SYN_PLL_MMD2 | RADIO_2056_SYN, ci->RF_SYN_pll_mmd2); write_radio_reg(pi, RADIO_2056_SYN_PLL_MMD1 | RADIO_2056_SYN, ci->RF_SYN_pll_mmd1); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, ci->RF_SYN_pll_loopfilter1); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, ci->RF_SYN_pll_loopfilter2); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER3 | RADIO_2056_SYN, ci->RF_SYN_pll_loopfilter3); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, ci->RF_SYN_pll_loopfilter4); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER5 | RADIO_2056_SYN, ci->RF_SYN_pll_loopfilter5); write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR27 | RADIO_2056_SYN, ci->RF_SYN_reserved_addr27); write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR28 | RADIO_2056_SYN, ci->RF_SYN_reserved_addr28); write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR29 | RADIO_2056_SYN, ci->RF_SYN_reserved_addr29); write_radio_reg(pi, RADIO_2056_SYN_LOGEN_VCOBUF1 | RADIO_2056_SYN, ci->RF_SYN_logen_VCOBUF1); write_radio_reg(pi, RADIO_2056_SYN_LOGEN_MIXER2 | RADIO_2056_SYN, ci->RF_SYN_logen_MIXER2); write_radio_reg(pi, RADIO_2056_SYN_LOGEN_BUF3 | RADIO_2056_SYN, ci->RF_SYN_logen_BUF3); write_radio_reg(pi, RADIO_2056_SYN_LOGEN_BUF4 | RADIO_2056_SYN, ci->RF_SYN_logen_BUF4); write_radio_reg(pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX0, ci->RF_RX0_lnaa_tune); write_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX0, ci->RF_RX0_lnag_tune); write_radio_reg(pi, RADIO_2056_TX_INTPAA_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_intpaa_boost_tune); write_radio_reg(pi, RADIO_2056_TX_INTPAG_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_intpag_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_pada_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_padg_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_pgaa_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_pgag_boost_tune); write_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_mixa_boost_tune); write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX0, ci->RF_TX0_mixg_boost_tune); write_radio_reg(pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX1, ci->RF_RX1_lnaa_tune); write_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX1, ci->RF_RX1_lnag_tune); write_radio_reg(pi, RADIO_2056_TX_INTPAA_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_intpaa_boost_tune); write_radio_reg(pi, RADIO_2056_TX_INTPAG_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_intpag_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_pada_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_padg_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_pgaa_boost_tune); write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_pgag_boost_tune); write_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_mixa_boost_tune); write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX1, ci->RF_TX1_mixg_boost_tune); if (NREV_IS(pi->pubpi.phy_rev, 3)) regs_SYN_2056_ptr = regs_SYN_2056; else if (NREV_IS(pi->pubpi.phy_rev, 4)) regs_SYN_2056_ptr = regs_SYN_2056_A1; else { switch (pi->pubpi.radiorev) { case 5: regs_SYN_2056_ptr = regs_SYN_2056_rev5; break; case 6: regs_SYN_2056_ptr = regs_SYN_2056_rev6; break; case 7: case 9: regs_SYN_2056_ptr = regs_SYN_2056_rev7; break; case 8: regs_SYN_2056_ptr = regs_SYN_2056_rev8; break; case 11: regs_SYN_2056_ptr = regs_SYN_2056_rev11; break; } } if (CHSPEC_IS2G(pi->radio_chanspec)) write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, (u16) regs_SYN_2056_ptr[0x49 - 2].init_g); else write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, (u16) regs_SYN_2056_ptr[0x49 - 2].init_a); if (pi->sh->boardflags2 & BFL2_GPLL_WAR) { if (CHSPEC_IS2G(pi->radio_chanspec)) { write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, 0x1f); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, 0x1f); if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || (pi->sh->chip == BCMA_CHIP_ID_BCM47162)) { write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, 0x14); write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, 0x00); } else { write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, 0xb); write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, 0x14); } } } if ((pi->sh->boardflags2 & BFL2_GPLL_WAR2) && (CHSPEC_IS2G(pi->radio_chanspec))) { write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, 0x1f); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, 0x1f); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, 0xb); write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, 0x20); } if (pi->sh->boardflags2 & BFL2_APLL_WAR) { if (CHSPEC_IS5G(pi->radio_chanspec)) { write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, 0x1f); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, 0x1f); write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, 0x5); write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, 0xc); } } if (PHY_IPA(pi) && CHSPEC_IS2G(pi->radio_chanspec)) { u16 pag_boost_tune; u16 padg_boost_tune; u16 pgag_boost_tune; u16 mixg_boost_tune; u16 bias, cascbias; uint core; for (core = 0; core < pi->pubpi.phy_corenum; core++) { if (NREV_GE(pi->pubpi.phy_rev, 5)) { WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PADG_IDAC, 0xcc); if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || (pi->sh->chip == BCMA_CHIP_ID_BCM47162)) { bias = 0x40; cascbias = 0x45; pag_boost_tune = 0x5; pgag_boost_tune = 0x33; padg_boost_tune = 0x77; mixg_boost_tune = 0x55; } else { bias = 0x25; cascbias = 0x20; if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224 || pi->sh->chip == BCMA_CHIP_ID_BCM43225) && pi->sh->chippkg == BCMA_PKG_ID_BCM43224_FAB_SMIC) { bias = 0x2a; cascbias = 0x38; } pag_boost_tune = 0x4; pgag_boost_tune = 0x03; padg_boost_tune = 0x77; mixg_boost_tune = 0x65; } WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_IMAIN_STAT, bias); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_IAUX_STAT, bias); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_CASCBIAS, cascbias); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_BOOST_TUNE, pag_boost_tune); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PGAG_BOOST_TUNE, pgag_boost_tune); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PADG_BOOST_TUNE, padg_boost_tune); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, MIXG_BOOST_TUNE, mixg_boost_tune); } else { bias = (pi->bw == WL_CHANSPEC_BW_40) ? 0x40 : 0x20; WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_IMAIN_STAT, bias); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_IAUX_STAT, bias); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_CASCBIAS, 0x30); } WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PA_SPARE1, 0xee); } } if (PHY_IPA(pi) && NREV_IS(pi->pubpi.phy_rev, 6) && CHSPEC_IS5G(pi->radio_chanspec)) { u16 paa_boost_tune; u16 pada_boost_tune; u16 pgaa_boost_tune; u16 mixa_boost_tune; u16 freq, pabias, cascbias; uint core; freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec)); if (freq < 5150) { paa_boost_tune = 0xa; pada_boost_tune = 0x77; pgaa_boost_tune = 0xf; mixa_boost_tune = 0xf; } else if (freq < 5340) { paa_boost_tune = 0x8; pada_boost_tune = 0x77; pgaa_boost_tune = 0xfb; mixa_boost_tune = 0xf; } else if (freq < 5650) { paa_boost_tune = 0x0; pada_boost_tune = 0x77; pgaa_boost_tune = 0xb; mixa_boost_tune = 0xf; } else { paa_boost_tune = 0x0; pada_boost_tune = 0x77; if (freq != 5825) pgaa_boost_tune = -(int)(freq - 18) / 36 + 168; else pgaa_boost_tune = 6; mixa_boost_tune = 0xf; } for (core = 0; core < pi->pubpi.phy_corenum; core++) { WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAA_BOOST_TUNE, paa_boost_tune); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PADA_BOOST_TUNE, pada_boost_tune); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PGAA_BOOST_TUNE, pgaa_boost_tune); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, MIXA_BOOST_TUNE, mixa_boost_tune); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TXSPARE1, 0x30); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PA_SPARE2, 0xee); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PADA_CASCBIAS, 0x3); cascbias = 0x30; if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224 || pi->sh->chip == BCMA_CHIP_ID_BCM43225) && pi->sh->chippkg == BCMA_PKG_ID_BCM43224_FAB_SMIC) cascbias = 0x35; pabias = (pi->phy_pabias == 0) ? 0x30 : pi->phy_pabias; WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAA_IAUX_STAT, pabias); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAA_IMAIN_STAT, pabias); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAA_CASCBIAS, cascbias); } } udelay(50); wlc_phy_radio205x_vcocal_nphy(pi); } void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_EN, 0x01, 0x0); mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x04, 0x0); mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x04, (1 << 2)); mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_EN, 0x01, 0x01); } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_radio_reg(pi, RADIO_2056_SYN_PLL_VCOCAL12, 0x0); write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x38); write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x18); write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x38); write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x39); } udelay(300); } static void wlc_phy_chanspec_radio2057_setup( struct brcms_phy *pi, const struct chan_info_nphy_radio2057 *ci, const struct chan_info_nphy_radio2057_rev5 * ci2) { int coreNum; u16 txmix2g_tune_boost_pu = 0; u16 pad2g_tune_pus = 0; if (pi->pubpi.radiorev == 5) { write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL0, ci2->RF_vcocal_countval0); write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL1, ci2->RF_vcocal_countval1); write_radio_reg(pi, RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE, ci2->RF_rfpll_refmaster_sparextalsize); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, ci2->RF_rfpll_loopfilter_r1); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, ci2->RF_rfpll_loopfilter_c2); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, ci2->RF_rfpll_loopfilter_c1); write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, ci2->RF_cp_kpd_idac); write_radio_reg(pi, RADIO_2057_RFPLL_MMD0, ci2->RF_rfpll_mmd0); write_radio_reg(pi, RADIO_2057_RFPLL_MMD1, ci2->RF_rfpll_mmd1); write_radio_reg(pi, RADIO_2057_VCOBUF_TUNE, ci2->RF_vcobuf_tune); write_radio_reg(pi, RADIO_2057_LOGEN_MX2G_TUNE, ci2->RF_logen_mx2g_tune); write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF2G_TUNE, ci2->RF_logen_indbuf2g_tune); write_radio_reg(pi, RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0, ci2->RF_txmix2g_tune_boost_pu_core0); write_radio_reg(pi, RADIO_2057_PAD2G_TUNE_PUS_CORE0, ci2->RF_pad2g_tune_pus_core0); write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE0, ci2->RF_lna2g_tune_core0); write_radio_reg(pi, RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1, ci2->RF_txmix2g_tune_boost_pu_core1); write_radio_reg(pi, RADIO_2057_PAD2G_TUNE_PUS_CORE1, ci2->RF_pad2g_tune_pus_core1); write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE1, ci2->RF_lna2g_tune_core1); } else { write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL0, ci->RF_vcocal_countval0); write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL1, ci->RF_vcocal_countval1); write_radio_reg(pi, RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE, ci->RF_rfpll_refmaster_sparextalsize); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, ci->RF_rfpll_loopfilter_r1); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, ci->RF_rfpll_loopfilter_c2); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, ci->RF_rfpll_loopfilter_c1); write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, ci->RF_cp_kpd_idac); write_radio_reg(pi, RADIO_2057_RFPLL_MMD0, ci->RF_rfpll_mmd0); write_radio_reg(pi, RADIO_2057_RFPLL_MMD1, ci->RF_rfpll_mmd1); write_radio_reg(pi, RADIO_2057_VCOBUF_TUNE, ci->RF_vcobuf_tune); write_radio_reg(pi, RADIO_2057_LOGEN_MX2G_TUNE, ci->RF_logen_mx2g_tune); write_radio_reg(pi, RADIO_2057_LOGEN_MX5G_TUNE, ci->RF_logen_mx5g_tune); write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF2G_TUNE, ci->RF_logen_indbuf2g_tune); write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF5G_TUNE, ci->RF_logen_indbuf5g_tune); write_radio_reg(pi, RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0, ci->RF_txmix2g_tune_boost_pu_core0); write_radio_reg(pi, RADIO_2057_PAD2G_TUNE_PUS_CORE0, ci->RF_pad2g_tune_pus_core0); write_radio_reg(pi, RADIO_2057_PGA_BOOST_TUNE_CORE0, ci->RF_pga_boost_tune_core0); write_radio_reg(pi, RADIO_2057_TXMIX5G_BOOST_TUNE_CORE0, ci->RF_txmix5g_boost_tune_core0); write_radio_reg(pi, RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE0, ci->RF_pad5g_tune_misc_pus_core0); write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE0, ci->RF_lna2g_tune_core0); write_radio_reg(pi, RADIO_2057_LNA5G_TUNE_CORE0, ci->RF_lna5g_tune_core0); write_radio_reg(pi, RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1, ci->RF_txmix2g_tune_boost_pu_core1); write_radio_reg(pi, RADIO_2057_PAD2G_TUNE_PUS_CORE1, ci->RF_pad2g_tune_pus_core1); write_radio_reg(pi, RADIO_2057_PGA_BOOST_TUNE_CORE1, ci->RF_pga_boost_tune_core1); write_radio_reg(pi, RADIO_2057_TXMIX5G_BOOST_TUNE_CORE1, ci->RF_txmix5g_boost_tune_core1); write_radio_reg(pi, RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE1, ci->RF_pad5g_tune_misc_pus_core1); write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE1, ci->RF_lna2g_tune_core1); write_radio_reg(pi, RADIO_2057_LNA5G_TUNE_CORE1, ci->RF_lna5g_tune_core1); } if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) { if (CHSPEC_IS2G(pi->radio_chanspec)) { write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, 0x3f); write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, 0x8); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, 0x8); } else { write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, 0x1f); write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, 0x8); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, 0x8); } } else if ((pi->pubpi.radiorev == 5) || (pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { if (CHSPEC_IS2G(pi->radio_chanspec)) { write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, 0x1b); write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x30); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, 0xa); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, 0xa); } else { write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, 0x1f); write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, 0x8); write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, 0x8); } } if (CHSPEC_IS2G(pi->radio_chanspec)) { if (PHY_IPA(pi)) { if (pi->pubpi.radiorev == 3) txmix2g_tune_boost_pu = 0x6b; if (pi->pubpi.radiorev == 5) pad2g_tune_pus = 0x73; } else { if (pi->pubpi.radiorev != 5) { pad2g_tune_pus = 0x3; txmix2g_tune_boost_pu = 0x61; } } for (coreNum = 0; coreNum <= 1; coreNum++) { if (txmix2g_tune_boost_pu != 0) WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, TXMIX2G_TUNE_BOOST_PU, txmix2g_tune_boost_pu); if (pad2g_tune_pus != 0) WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, PAD2G_TUNE_PUS, pad2g_tune_pus); } } udelay(50); wlc_phy_radio205x_vcocal_nphy(pi); } static void wlc_phy_chanspec_nphy_setup(struct brcms_phy *pi, u16 chanspec, const struct nphy_sfo_cfg *ci) { u16 val; struct si_info *sii = container_of(pi->sh->sih, struct si_info, pub); val = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; if (CHSPEC_IS5G(chanspec) && !val) { val = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), (val | MAC_PHY_FORCE_CLK)); or_phy_reg(pi, (NPHY_TO_BPHY_OFF + BPHY_BB_CONFIG), (BBCFG_RESETCCA | BBCFG_RESETRX)); bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), val); or_phy_reg(pi, 0x09, NPHY_BandControl_currentBand); } else if (!CHSPEC_IS5G(chanspec) && val) { and_phy_reg(pi, 0x09, ~NPHY_BandControl_currentBand); val = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), (val | MAC_PHY_FORCE_CLK)); and_phy_reg(pi, (NPHY_TO_BPHY_OFF + BPHY_BB_CONFIG), (u16) (~(BBCFG_RESETCCA | BBCFG_RESETRX))); bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), val); } write_phy_reg(pi, 0x1ce, ci->PHY_BW1a); write_phy_reg(pi, 0x1cf, ci->PHY_BW2); write_phy_reg(pi, 0x1d0, ci->PHY_BW3); write_phy_reg(pi, 0x1d1, ci->PHY_BW4); write_phy_reg(pi, 0x1d2, ci->PHY_BW5); write_phy_reg(pi, 0x1d3, ci->PHY_BW6); if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_ofdm_en, 0); or_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_TEST, 0x800); } else { wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_ofdm_en, NPHY_ClassifierCtrl_ofdm_en); if (CHSPEC_IS2G(chanspec)) and_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_TEST, ~0x840); } if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) wlc_phy_txpwr_fixpower_nphy(pi); if (NREV_LT(pi->pubpi.phy_rev, 3)) wlc_phy_adjust_lnagaintbl_nphy(pi); wlc_phy_txlpfbw_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 3) && (pi->phy_spuravoid != SPURAVOID_DISABLE)) { u8 spuravoid = 0; val = CHSPEC_CHANNEL(chanspec); if (!CHSPEC_IS40(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if ((val == 13) || (val == 14) || (val == 153)) spuravoid = 1; } else if (((val >= 5) && (val <= 8)) || (val == 13) || (val == 14)) { spuravoid = 1; } } else if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (val == 54) spuravoid = 1; } else if (pi->nphy_aband_spurwar_en && ((val == 38) || (val == 102) || (val == 118))) { if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) && (pi->sh->chippkg == BCMA_PKG_ID_BCM4717)) { spuravoid = 0; } else { spuravoid = 1; } } if (pi->phy_spuravoid == SPURAVOID_FORCEON) spuravoid = 1; if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || (pi->sh->chip == BCMA_CHIP_ID_BCM43225)) { bcma_pmu_spuravoid_pllupdate(&sii->icbus->drv_cc, spuravoid); } else { wlapi_bmac_core_phypll_ctl(pi->sh->physhim, false); bcma_pmu_spuravoid_pllupdate(&sii->icbus->drv_cc, spuravoid); wlapi_bmac_core_phypll_ctl(pi->sh->physhim, true); } if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) || (pi->sh->chip == BCMA_CHIP_ID_BCM43225)) { if (spuravoid == 1) { bcma_write16(pi->d11core, D11REGOFFS(tsf_clk_frac_l), 0x5341); bcma_write16(pi->d11core, D11REGOFFS(tsf_clk_frac_h), 0x8); } else { bcma_write16(pi->d11core, D11REGOFFS(tsf_clk_frac_l), 0x8889); bcma_write16(pi->d11core, D11REGOFFS(tsf_clk_frac_h), 0x8); } } if (!((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || (pi->sh->chip == BCMA_CHIP_ID_BCM47162))) wlapi_bmac_core_phypll_reset(pi->sh->physhim); mod_phy_reg(pi, 0x01, (0x1 << 15), ((spuravoid > 0) ? (0x1 << 15) : 0)); wlc_phy_resetcca_nphy(pi); pi->phy_isspuravoid = (spuravoid > 0); } if (NREV_LT(pi->pubpi.phy_rev, 7)) write_phy_reg(pi, 0x17e, 0x3830); wlc_phy_spurwar_nphy(pi); } void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec) { int freq; const struct chan_info_nphy_radio2057 *t0 = NULL; const struct chan_info_nphy_radio205x *t1 = NULL; const struct chan_info_nphy_radio2057_rev5 *t2 = NULL; const struct chan_info_nphy_2055 *t3 = NULL; if (!wlc_phy_chan2freq_nphy (pi, CHSPEC_CHANNEL(chanspec), &freq, &t0, &t1, &t2, &t3)) return; wlc_phy_chanspec_radio_set((struct brcms_phy_pub *) pi, chanspec); if (CHSPEC_BW(chanspec) != pi->bw) wlapi_bmac_bw_set(pi->sh->physhim, CHSPEC_BW(chanspec)); if (CHSPEC_IS40(chanspec)) { if (CHSPEC_SB_UPPER(chanspec)) { or_phy_reg(pi, 0xa0, BPHY_BAND_SEL_UP20); if (NREV_GE(pi->pubpi.phy_rev, 7)) or_phy_reg(pi, 0x310, PRIM_SEL_UP20); } else { and_phy_reg(pi, 0xa0, ~BPHY_BAND_SEL_UP20); if (NREV_GE(pi->pubpi.phy_rev, 7)) and_phy_reg(pi, 0x310, (~PRIM_SEL_UP20 & 0xffff)); } } if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) { mod_radio_reg(pi, RADIO_2057_TIA_CONFIG_CORE0, 0x2, (CHSPEC_IS5G(chanspec) ? (1 << 1) : 0)); mod_radio_reg(pi, RADIO_2057_TIA_CONFIG_CORE1, 0x2, (CHSPEC_IS5G(chanspec) ? (1 << 1) : 0)); } wlc_phy_chanspec_radio2057_setup(pi, t0, t2); wlc_phy_chanspec_nphy_setup(pi, chanspec, (pi->pubpi.radiorev == 5) ? (const struct nphy_sfo_cfg *)&(t2->PHY_BW1a) : (const struct nphy_sfo_cfg *)&(t0->PHY_BW1a)); } else { mod_radio_reg(pi, RADIO_2056_SYN_COM_CTRL | RADIO_2056_SYN, 0x4, (CHSPEC_IS5G(chanspec) ? (0x1 << 2) : 0)); wlc_phy_chanspec_radio2056_setup(pi, t1); wlc_phy_chanspec_nphy_setup(pi, chanspec, (const struct nphy_sfo_cfg *) &(t1->PHY_BW1a)); } } else { mod_radio_reg(pi, RADIO_2055_MASTER_CNTRL1, 0x70, (CHSPEC_IS5G(chanspec) ? (0x02 << 4) : (0x05 << 4))); wlc_phy_chanspec_radio2055_setup(pi, t3); wlc_phy_chanspec_nphy_setup(pi, chanspec, (const struct nphy_sfo_cfg *) &(t3->PHY_BW1a)); } } void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init) { struct brcms_phy *pi = (struct brcms_phy *) ppi; u16 mask = 0xfc00; u32 mc = 0; if (NREV_GE(pi->pubpi.phy_rev, 7)) return; if (NREV_GE(pi->pubpi.phy_rev, 3)) { u16 v0 = 0x211, v1 = 0x222, v2 = 0x144, v3 = 0x188; if (!lut_init) return; if (pi->srom_fem2g.antswctrllut == 0) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x02, 16, &v0); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x03, 16, &v1); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x08, 16, &v2); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x0C, 16, &v3); } if (pi->srom_fem5g.antswctrllut == 0) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x12, 16, &v0); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x13, 16, &v1); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x18, 16, &v2); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, 1, 0x1C, 16, &v3); } } else { write_phy_reg(pi, 0xc8, 0x0); write_phy_reg(pi, 0xc9, 0x0); bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc, mask, mask); mc = bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); mc &= ~MCTL_GPOUT_SEL_MASK; bcma_write32(pi->d11core, D11REGOFFS(maccontrol), mc); bcma_set16(pi->d11core, D11REGOFFS(psm_gpio_oe), mask); bcma_mask16(pi->d11core, D11REGOFFS(psm_gpio_out), ~mask); if (lut_init) { write_phy_reg(pi, 0xf8, 0x02d8); write_phy_reg(pi, 0xf9, 0x0301); write_phy_reg(pi, 0xfa, 0x02d8); write_phy_reg(pi, 0xfb, 0x0301); } } } u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val) { u16 curr_ctl, new_ctl; bool suspended = false; if (D11REV_IS(pi->sh->corerev, 16)) { suspended = (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC) ? false : true; if (!suspended) wlapi_suspend_mac_and_wait(pi->sh->physhim); } curr_ctl = read_phy_reg(pi, 0xb0) & (0x7 << 0); new_ctl = (curr_ctl & (~mask)) | (val & mask); mod_phy_reg(pi, 0xb0, (0x7 << 0), new_ctl); if (D11REV_IS(pi->sh->corerev, 16) && !suspended) wlapi_enable_mac(pi->sh->physhim); return new_ctl; } void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd) { u16 trigger_mask, status_mask; u16 orig_RfseqCoreActv; switch (cmd) { case NPHY_RFSEQ_RX2TX: trigger_mask = NPHY_RfseqTrigger_rx2tx; status_mask = NPHY_RfseqStatus_rx2tx; break; case NPHY_RFSEQ_TX2RX: trigger_mask = NPHY_RfseqTrigger_tx2rx; status_mask = NPHY_RfseqStatus_tx2rx; break; case NPHY_RFSEQ_RESET2RX: trigger_mask = NPHY_RfseqTrigger_reset2rx; status_mask = NPHY_RfseqStatus_reset2rx; break; case NPHY_RFSEQ_UPDATEGAINH: trigger_mask = NPHY_RfseqTrigger_updategainh; status_mask = NPHY_RfseqStatus_updategainh; break; case NPHY_RFSEQ_UPDATEGAINL: trigger_mask = NPHY_RfseqTrigger_updategainl; status_mask = NPHY_RfseqStatus_updategainl; break; case NPHY_RFSEQ_UPDATEGAINU: trigger_mask = NPHY_RfseqTrigger_updategainu; status_mask = NPHY_RfseqStatus_updategainu; break; default: return; } orig_RfseqCoreActv = read_phy_reg(pi, 0xa1); or_phy_reg(pi, 0xa1, (NPHY_RfseqMode_CoreActv_override | NPHY_RfseqMode_Trigger_override)); or_phy_reg(pi, 0xa3, trigger_mask); SPINWAIT((read_phy_reg(pi, 0xa4) & status_mask), 200000); write_phy_reg(pi, 0xa1, orig_RfseqCoreActv); WARN(read_phy_reg(pi, 0xa4) & status_mask, "HW error in rf"); } static void wlc_phy_rfctrl_override_1tomany_nphy(struct brcms_phy *pi, u16 cmd, u16 value, u8 core_mask, u8 off) { u16 rfmxgain = 0, lpfgain = 0; u16 tgain = 0; if (NREV_GE(pi->pubpi.phy_rev, 7)) { switch (cmd) { case NPHY_REV7_RfctrlOverride_cmd_rxrf_pu: wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 5), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 4), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 3), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); break; case NPHY_REV7_RfctrlOverride_cmd_rx_pu: wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 2), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 1), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 0), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 1), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 11), 0, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); break; case NPHY_REV7_RfctrlOverride_cmd_tx_pu: wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 2), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 1), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 0), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 2), value, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 11), 1, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID1); break; case NPHY_REV7_RfctrlOverride_cmd_rxgain: rfmxgain = value & 0x000ff; lpfgain = value & 0x0ff00; lpfgain = lpfgain >> 8; wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 11), rfmxgain, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x3 << 13), lpfgain, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID0); break; case NPHY_REV7_RfctrlOverride_cmd_txgain: tgain = value & 0x7fff; lpfgain = value & 0x8000; lpfgain = lpfgain >> 14; wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 12), tgain, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 13), lpfgain, core_mask, off, NPHY_REV7_RFCTRLOVERRIDE_ID0); break; } } } static void wlc_phy_scale_offset_rssi_nphy(struct brcms_phy *pi, u16 scale, s8 offset, u8 coresel, u8 rail, u8 rssi_type) { u16 valuetostuff; offset = (offset > NPHY_RSSICAL_MAXREAD) ? NPHY_RSSICAL_MAXREAD : offset; offset = (offset < (-NPHY_RSSICAL_MAXREAD - 1)) ? -NPHY_RSSICAL_MAXREAD - 1 : offset; valuetostuff = ((scale & 0x3f) << 8) | (offset & 0x3f); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) write_phy_reg(pi, 0x1a6, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) write_phy_reg(pi, 0x1ac, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) write_phy_reg(pi, 0x1b2, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) write_phy_reg(pi, 0x1b8, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) write_phy_reg(pi, 0x1a4, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) write_phy_reg(pi, 0x1aa, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) write_phy_reg(pi, 0x1b0, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) write_phy_reg(pi, 0x1b6, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) write_phy_reg(pi, 0x1a5, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) write_phy_reg(pi, 0x1ab, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) write_phy_reg(pi, 0x1b1, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) write_phy_reg(pi, 0x1b7, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) write_phy_reg(pi, 0x1a7, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) write_phy_reg(pi, 0x1ad, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) write_phy_reg(pi, 0x1b3, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) write_phy_reg(pi, 0x1b9, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) write_phy_reg(pi, 0x1a8, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) write_phy_reg(pi, 0x1ae, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) write_phy_reg(pi, 0x1b4, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) write_phy_reg(pi, 0x1ba, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) write_phy_reg(pi, 0x1a9, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) write_phy_reg(pi, 0x1b5, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE1) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) write_phy_reg(pi, 0x1af, valuetostuff); if (((coresel == RADIO_MIMO_CORESEL_CORE2) || (coresel == RADIO_MIMO_CORESEL_ALLRX)) && (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) write_phy_reg(pi, 0x1bb, valuetostuff); } static void brcms_phy_wr_tx_mux(struct brcms_phy *pi, u8 core) { if (PHY_IPA(pi)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) write_radio_reg(pi, ((core == PHY_CORE_0) ? RADIO_2057_TX0_TX_SSI_MUX : RADIO_2057_TX1_TX_SSI_MUX), (CHSPEC_IS5G(pi->radio_chanspec) ? 0xc : 0xe)); else write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | ((core == PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1), (CHSPEC_IS5G(pi->radio_chanspec) ? 0xc : 0xe)); } else { if (NREV_GE(pi->pubpi.phy_rev, 7)) { write_radio_reg(pi, ((core == PHY_CORE_0) ? RADIO_2057_TX0_TX_SSI_MUX : RADIO_2057_TX1_TX_SSI_MUX), 0x11); if (pi->pubpi.radioid == BCM2057_ID) write_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1); } else { write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | ((core == PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1), 0x11); } } } void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core_code, u8 rssi_type) { u16 mask, val; u16 afectrlovr_rssi_val, rfctrlcmd_rxen_val, rfctrlcmd_coresel_val, startseq; u16 rfctrlovr_rssi_val, rfctrlovr_rxen_val, rfctrlovr_coresel_val, rfctrlovr_trigger_val; u16 afectrlovr_rssi_mask, rfctrlcmd_mask, rfctrlovr_mask; u16 rfctrlcmd_val, rfctrlovr_val; u8 core; if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (core_code == RADIO_MIMO_CORESEL_OFF) { mod_phy_reg(pi, 0x8f, (0x1 << 9), 0); mod_phy_reg(pi, 0xa5, (0x1 << 9), 0); mod_phy_reg(pi, 0xa6, (0x3 << 8), 0); mod_phy_reg(pi, 0xa7, (0x3 << 8), 0); mod_phy_reg(pi, 0xe5, (0x1 << 5), 0); mod_phy_reg(pi, 0xe6, (0x1 << 5), 0); mask = (0x1 << 2) | (0x1 << 3) | (0x1 << 4) | (0x1 << 5); mod_phy_reg(pi, 0xf9, mask, 0); mod_phy_reg(pi, 0xfb, mask, 0); } else { for (core = 0; core < pi->pubpi.phy_corenum; core++) { if (core_code == RADIO_MIMO_CORESEL_CORE1 && core == PHY_CORE_1) continue; else if (core_code == RADIO_MIMO_CORESEL_CORE2 && core == PHY_CORE_0) continue; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5, (0x1 << 9), 1 << 9); if (rssi_type == NPHY_RSSI_SEL_W1 || rssi_type == NPHY_RSSI_SEL_W2 || rssi_type == NPHY_RSSI_SEL_NB) { mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, (0x3 << 8), 0); mask = (0x1 << 2) | (0x1 << 3) | (0x1 << 4) | (0x1 << 5); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xf9 : 0xfb, mask, 0); if (rssi_type == NPHY_RSSI_SEL_W1) { if (CHSPEC_IS5G( pi->radio_chanspec)) { mask = (0x1 << 2); val = 1 << 2; } else { mask = (0x1 << 3); val = 1 << 3; } } else if (rssi_type == NPHY_RSSI_SEL_W2) { mask = (0x1 << 4); val = 1 << 4; } else { mask = (0x1 << 5); val = 1 << 5; } mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xf9 : 0xfb, mask, val); mask = (0x1 << 5); val = 1 << 5; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xe5 : 0xe6, mask, val); } else { if (rssi_type == NPHY_RSSI_SEL_TBD) { mask = (0x3 << 8); val = 1 << 8; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, mask, val); mask = (0x3 << 10); val = 1 << 10; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, mask, val); } else if (rssi_type == NPHY_RSSI_SEL_IQ) { mask = (0x3 << 8); val = 2 << 8; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, mask, val); mask = (0x3 << 10); val = 2 << 10; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, mask, val); } else { mask = (0x3 << 8); val = 3 << 8; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, mask, val); mask = (0x3 << 10); val = 3 << 10; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, mask, val); brcms_phy_wr_tx_mux(pi, core); afectrlovr_rssi_val = 1 << 9; mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5, (0x1 << 9), afectrlovr_rssi_val); } } } } } else { if ((rssi_type == NPHY_RSSI_SEL_W1) || (rssi_type == NPHY_RSSI_SEL_W2) || (rssi_type == NPHY_RSSI_SEL_NB)) val = 0x0; else if (rssi_type == NPHY_RSSI_SEL_TBD) val = 0x1; else if (rssi_type == NPHY_RSSI_SEL_IQ) val = 0x2; else val = 0x3; mask = ((0x3 << 12) | (0x3 << 14)); val = (val << 12) | (val << 14); mod_phy_reg(pi, 0xa6, mask, val); mod_phy_reg(pi, 0xa7, mask, val); if ((rssi_type == NPHY_RSSI_SEL_W1) || (rssi_type == NPHY_RSSI_SEL_W2) || (rssi_type == NPHY_RSSI_SEL_NB)) { if (rssi_type == NPHY_RSSI_SEL_W1) val = 0x1; if (rssi_type == NPHY_RSSI_SEL_W2) val = 0x2; if (rssi_type == NPHY_RSSI_SEL_NB) val = 0x3; mask = (0x3 << 4); val = (val << 4); mod_phy_reg(pi, 0x7a, mask, val); mod_phy_reg(pi, 0x7d, mask, val); } if (core_code == RADIO_MIMO_CORESEL_OFF) { afectrlovr_rssi_val = 0; rfctrlcmd_rxen_val = 0; rfctrlcmd_coresel_val = 0; rfctrlovr_rssi_val = 0; rfctrlovr_rxen_val = 0; rfctrlovr_coresel_val = 0; rfctrlovr_trigger_val = 0; startseq = 0; } else { afectrlovr_rssi_val = 1; rfctrlcmd_rxen_val = 1; rfctrlcmd_coresel_val = core_code; rfctrlovr_rssi_val = 1; rfctrlovr_rxen_val = 1; rfctrlovr_coresel_val = 1; rfctrlovr_trigger_val = 1; startseq = 1; } afectrlovr_rssi_mask = ((0x1 << 12) | (0x1 << 13)); afectrlovr_rssi_val = (afectrlovr_rssi_val << 12) | (afectrlovr_rssi_val << 13); mod_phy_reg(pi, 0xa5, afectrlovr_rssi_mask, afectrlovr_rssi_val); if ((rssi_type == NPHY_RSSI_SEL_W1) || (rssi_type == NPHY_RSSI_SEL_W2) || (rssi_type == NPHY_RSSI_SEL_NB)) { rfctrlcmd_mask = ((0x1 << 8) | (0x7 << 3)); rfctrlcmd_val = (rfctrlcmd_rxen_val << 8) | (rfctrlcmd_coresel_val << 3); rfctrlovr_mask = ((0x1 << 5) | (0x1 << 12) | (0x1 << 1) | (0x1 << 0)); rfctrlovr_val = (rfctrlovr_rssi_val << 5) | (rfctrlovr_rxen_val << 12) | (rfctrlovr_coresel_val << 1) | (rfctrlovr_trigger_val << 0); mod_phy_reg(pi, 0x78, rfctrlcmd_mask, rfctrlcmd_val); mod_phy_reg(pi, 0xec, rfctrlovr_mask, rfctrlovr_val); mod_phy_reg(pi, 0x78, (0x1 << 0), (startseq << 0)); udelay(20); mod_phy_reg(pi, 0xec, (0x1 << 0), 0); } } } int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, s32 *rssi_buf, u8 nsamps) { s16 rssi0, rssi1; u16 afectrlCore1_save = 0; u16 afectrlCore2_save = 0; u16 afectrlOverride1_save = 0; u16 afectrlOverride2_save = 0; u16 rfctrlOverrideAux0_save = 0; u16 rfctrlOverrideAux1_save = 0; u16 rfctrlMiscReg1_save = 0; u16 rfctrlMiscReg2_save = 0; u16 rfctrlcmd_save = 0; u16 rfctrloverride_save = 0; u16 rfctrlrssiothers1_save = 0; u16 rfctrlrssiothers2_save = 0; s8 tmp_buf[4]; u8 ctr = 0, samp = 0; s32 rssi_out_val; u16 gpiosel_orig; afectrlCore1_save = read_phy_reg(pi, 0xa6); afectrlCore2_save = read_phy_reg(pi, 0xa7); if (NREV_GE(pi->pubpi.phy_rev, 3)) { rfctrlMiscReg1_save = read_phy_reg(pi, 0xf9); rfctrlMiscReg2_save = read_phy_reg(pi, 0xfb); afectrlOverride1_save = read_phy_reg(pi, 0x8f); afectrlOverride2_save = read_phy_reg(pi, 0xa5); rfctrlOverrideAux0_save = read_phy_reg(pi, 0xe5); rfctrlOverrideAux1_save = read_phy_reg(pi, 0xe6); } else { afectrlOverride1_save = read_phy_reg(pi, 0xa5); rfctrlcmd_save = read_phy_reg(pi, 0x78); rfctrloverride_save = read_phy_reg(pi, 0xec); rfctrlrssiothers1_save = read_phy_reg(pi, 0x7a); rfctrlrssiothers2_save = read_phy_reg(pi, 0x7d); } wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type); gpiosel_orig = read_phy_reg(pi, 0xca); if (NREV_LT(pi->pubpi.phy_rev, 2)) write_phy_reg(pi, 0xca, 5); for (ctr = 0; ctr < 4; ctr++) rssi_buf[ctr] = 0; for (samp = 0; samp < nsamps; samp++) { if (NREV_LT(pi->pubpi.phy_rev, 2)) { rssi0 = read_phy_reg(pi, 0x1c9); rssi1 = read_phy_reg(pi, 0x1ca); } else { rssi0 = read_phy_reg(pi, 0x219); rssi1 = read_phy_reg(pi, 0x21a); } ctr = 0; tmp_buf[ctr++] = ((s8) ((rssi0 & 0x3f) << 2)) >> 2; tmp_buf[ctr++] = ((s8) (((rssi0 >> 8) & 0x3f) << 2)) >> 2; tmp_buf[ctr++] = ((s8) ((rssi1 & 0x3f) << 2)) >> 2; tmp_buf[ctr++] = ((s8) (((rssi1 >> 8) & 0x3f) << 2)) >> 2; for (ctr = 0; ctr < 4; ctr++) rssi_buf[ctr] += tmp_buf[ctr]; } rssi_out_val = rssi_buf[3] & 0xff; rssi_out_val |= (rssi_buf[2] & 0xff) << 8; rssi_out_val |= (rssi_buf[1] & 0xff) << 16; rssi_out_val |= (rssi_buf[0] & 0xff) << 24; if (NREV_LT(pi->pubpi.phy_rev, 2)) write_phy_reg(pi, 0xca, gpiosel_orig); write_phy_reg(pi, 0xa6, afectrlCore1_save); write_phy_reg(pi, 0xa7, afectrlCore2_save); if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_phy_reg(pi, 0xf9, rfctrlMiscReg1_save); write_phy_reg(pi, 0xfb, rfctrlMiscReg2_save); write_phy_reg(pi, 0x8f, afectrlOverride1_save); write_phy_reg(pi, 0xa5, afectrlOverride2_save); write_phy_reg(pi, 0xe5, rfctrlOverrideAux0_save); write_phy_reg(pi, 0xe6, rfctrlOverrideAux1_save); } else { write_phy_reg(pi, 0xa5, afectrlOverride1_save); write_phy_reg(pi, 0x78, rfctrlcmd_save); write_phy_reg(pi, 0xec, rfctrloverride_save); write_phy_reg(pi, 0x7a, rfctrlrssiothers1_save); write_phy_reg(pi, 0x7d, rfctrlrssiothers2_save); } return rssi_out_val; } s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi) { u16 core1_txrf_iqcal1_save, core1_txrf_iqcal2_save; u16 core2_txrf_iqcal1_save, core2_txrf_iqcal2_save; u16 pwrdet_rxtx_core1_save; u16 pwrdet_rxtx_core2_save; u16 afectrlCore1_save; u16 afectrlCore2_save; u16 afectrlOverride_save; u16 afectrlOverride2_save; u16 pd_pll_ts_save; u16 gpioSel_save; s32 radio_temp[4]; s32 radio_temp2[4]; u16 syn_tempprocsense_save; s16 offset = 0; if (NREV_GE(pi->pubpi.phy_rev, 7)) { u16 auxADC_Vmid, auxADC_Av, auxADC_Vmid_save, auxADC_Av_save; u16 auxADC_rssi_ctrlL_save, auxADC_rssi_ctrlH_save; u16 auxADC_rssi_ctrlL, auxADC_rssi_ctrlH; s32 auxADC_Vl; u16 RfctrlOverride5_save, RfctrlOverride6_save; u16 RfctrlMiscReg5_save, RfctrlMiscReg6_save; u16 RSSIMultCoef0QPowerDet_save; u16 tempsense_Rcal; syn_tempprocsense_save = read_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG); afectrlCore1_save = read_phy_reg(pi, 0xa6); afectrlCore2_save = read_phy_reg(pi, 0xa7); afectrlOverride_save = read_phy_reg(pi, 0x8f); afectrlOverride2_save = read_phy_reg(pi, 0xa5); RSSIMultCoef0QPowerDet_save = read_phy_reg(pi, 0x1ae); RfctrlOverride5_save = read_phy_reg(pi, 0x346); RfctrlOverride6_save = read_phy_reg(pi, 0x347); RfctrlMiscReg5_save = read_phy_reg(pi, 0x344); RfctrlMiscReg6_save = read_phy_reg(pi, 0x345); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, &auxADC_Vmid_save); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, &auxADC_Av_save); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, &auxADC_rssi_ctrlL_save); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, &auxADC_rssi_ctrlH_save); write_phy_reg(pi, 0x1ae, 0x0); auxADC_rssi_ctrlL = 0x0; auxADC_rssi_ctrlH = 0x20; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, &auxADC_rssi_ctrlL); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, &auxADC_rssi_ctrlH); tempsense_Rcal = syn_tempprocsense_save & 0x1c; write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, tempsense_Rcal | 0x01); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID2); mod_phy_reg(pi, 0xa6, (0x1 << 7), 0); mod_phy_reg(pi, 0xa7, (0x1 << 7), 0); mod_phy_reg(pi, 0x8f, (0x1 << 7), (0x1 << 7)); mod_phy_reg(pi, 0xa5, (0x1 << 7), (0x1 << 7)); mod_phy_reg(pi, 0xa6, (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, 0xa7, (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, 0x8f, (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, 0xa5, (0x1 << 2), (0x1 << 2)); udelay(5); mod_phy_reg(pi, 0xa6, (0x1 << 2), 0); mod_phy_reg(pi, 0xa7, (0x1 << 2), 0); mod_phy_reg(pi, 0xa6, (0x1 << 3), 0); mod_phy_reg(pi, 0xa7, (0x1 << 3), 0); mod_phy_reg(pi, 0x8f, (0x1 << 3), (0x1 << 3)); mod_phy_reg(pi, 0xa5, (0x1 << 3), (0x1 << 3)); mod_phy_reg(pi, 0xa6, (0x1 << 6), 0); mod_phy_reg(pi, 0xa7, (0x1 << 6), 0); mod_phy_reg(pi, 0x8f, (0x1 << 6), (0x1 << 6)); mod_phy_reg(pi, 0xa5, (0x1 << 6), (0x1 << 6)); auxADC_Vmid = 0xA3; auxADC_Av = 0x0; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, &auxADC_Vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, &auxADC_Av); udelay(3); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, tempsense_Rcal | 0x03); udelay(5); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); auxADC_Av = 0x7; if (radio_temp[1] + radio_temp2[1] < -30) { auxADC_Vmid = 0x45; auxADC_Vl = 263; } else if (radio_temp[1] + radio_temp2[1] < -9) { auxADC_Vmid = 0x200; auxADC_Vl = 467; } else if (radio_temp[1] + radio_temp2[1] < 11) { auxADC_Vmid = 0x266; auxADC_Vl = 634; } else { auxADC_Vmid = 0x2D5; auxADC_Vl = 816; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, &auxADC_Vmid); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, &auxADC_Av); udelay(3); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, tempsense_Rcal | 0x01); udelay(5); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, syn_tempprocsense_save); write_phy_reg(pi, 0xa6, afectrlCore1_save); write_phy_reg(pi, 0xa7, afectrlCore2_save); write_phy_reg(pi, 0x8f, afectrlOverride_save); write_phy_reg(pi, 0xa5, afectrlOverride2_save); write_phy_reg(pi, 0x1ae, RSSIMultCoef0QPowerDet_save); write_phy_reg(pi, 0x346, RfctrlOverride5_save); write_phy_reg(pi, 0x347, RfctrlOverride6_save); write_phy_reg(pi, 0x344, RfctrlMiscReg5_save); write_phy_reg(pi, 0x345, RfctrlMiscReg5_save); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, &auxADC_Vmid_save); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, &auxADC_Av_save); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, &auxADC_rssi_ctrlL_save); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, &auxADC_rssi_ctrlH_save); if (pi->sh->chip == BCMA_CHIP_ID_BCM5357) { radio_temp[0] = (193 * (radio_temp[1] + radio_temp2[1]) + 88 * (auxADC_Vl) - 27111 + 128) / 256; } else { radio_temp[0] = (179 * (radio_temp[1] + radio_temp2[1]) + 82 * (auxADC_Vl) - 28861 + 128) / 256; } offset = (s16) pi->phy_tempsense_offset; } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { syn_tempprocsense_save = read_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE); afectrlCore1_save = read_phy_reg(pi, 0xa6); afectrlCore2_save = read_phy_reg(pi, 0xa7); afectrlOverride_save = read_phy_reg(pi, 0x8f); afectrlOverride2_save = read_phy_reg(pi, 0xa5); gpioSel_save = read_phy_reg(pi, 0xca); write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); if (NREV_LT(pi->pubpi.phy_rev, 7)) write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x05); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); if (NREV_GE(pi->pubpi.phy_rev, 7)) write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x01); else write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01); radio_temp[0] = (126 * (radio_temp[1] + radio_temp2[1]) + 3987) / 64; write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, syn_tempprocsense_save); write_phy_reg(pi, 0xca, gpioSel_save); write_phy_reg(pi, 0xa6, afectrlCore1_save); write_phy_reg(pi, 0xa7, afectrlCore2_save); write_phy_reg(pi, 0x8f, afectrlOverride_save); write_phy_reg(pi, 0xa5, afectrlOverride2_save); offset = (s16) pi->phy_tempsense_offset; } else { pwrdet_rxtx_core1_save = read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1); pwrdet_rxtx_core2_save = read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2); core1_txrf_iqcal1_save = read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1); core1_txrf_iqcal2_save = read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2); core2_txrf_iqcal1_save = read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1); core2_txrf_iqcal2_save = read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2); pd_pll_ts_save = read_radio_reg(pi, RADIO_2055_PD_PLL_TS); afectrlCore1_save = read_phy_reg(pi, 0xa6); afectrlCore2_save = read_phy_reg(pi, 0xa7); afectrlOverride_save = read_phy_reg(pi, 0xa5); gpioSel_save = read_phy_reg(pi, 0xca); write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, 0x01); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, 0x01); write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, 0x08); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, 0x08); write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x04); write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x04); write_radio_reg(pi, RADIO_2055_PD_PLL_TS, 0x00); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); radio_temp[0] = (radio_temp[0] + radio_temp2[0]); radio_temp[1] = (radio_temp[1] + radio_temp2[1]); radio_temp[2] = (radio_temp[2] + radio_temp2[2]); radio_temp[3] = (radio_temp[3] + radio_temp2[3]); radio_temp[0] = (radio_temp[0] + radio_temp[1] + radio_temp[2] + radio_temp[3]); radio_temp[0] = (radio_temp[0] + (8 * 32)) * (950 - 350) / 63 + (350 * 8); radio_temp[0] = (radio_temp[0] - (8 * 420)) / 38; write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, pwrdet_rxtx_core1_save); write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, pwrdet_rxtx_core2_save); write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, core1_txrf_iqcal1_save); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, core2_txrf_iqcal1_save); write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, core1_txrf_iqcal2_save); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, core2_txrf_iqcal2_save); write_radio_reg(pi, RADIO_2055_PD_PLL_TS, pd_pll_ts_save); write_phy_reg(pi, 0xca, gpioSel_save); write_phy_reg(pi, 0xa6, afectrlCore1_save); write_phy_reg(pi, 0xa7, afectrlCore2_save); write_phy_reg(pi, 0xa5, afectrlOverride_save); } return (s16) radio_temp[0] + offset; } static void wlc_phy_set_rssi_2055_vcm(struct brcms_phy *pi, u8 rssi_type, u8 *vcm_buf) { u8 core; for (core = 0; core < pi->pubpi.phy_corenum; core++) { if (rssi_type == NPHY_RSSI_SEL_NB) { if (core == PHY_CORE_0) { mod_radio_reg(pi, RADIO_2055_CORE1_B0_NBRSSI_VCM, RADIO_2055_NBRSSI_VCM_I_MASK, vcm_buf[2 * core] << RADIO_2055_NBRSSI_VCM_I_SHIFT); mod_radio_reg(pi, RADIO_2055_CORE1_RXBB_RSSI_CTRL5, RADIO_2055_NBRSSI_VCM_Q_MASK, vcm_buf[2 * core + 1] << RADIO_2055_NBRSSI_VCM_Q_SHIFT); } else { mod_radio_reg(pi, RADIO_2055_CORE2_B0_NBRSSI_VCM, RADIO_2055_NBRSSI_VCM_I_MASK, vcm_buf[2 * core] << RADIO_2055_NBRSSI_VCM_I_SHIFT); mod_radio_reg(pi, RADIO_2055_CORE2_RXBB_RSSI_CTRL5, RADIO_2055_NBRSSI_VCM_Q_MASK, vcm_buf[2 * core + 1] << RADIO_2055_NBRSSI_VCM_Q_SHIFT); } } else { if (core == PHY_CORE_0) mod_radio_reg(pi, RADIO_2055_CORE1_RXBB_RSSI_CTRL5, RADIO_2055_WBRSSI_VCM_IQ_MASK, vcm_buf[2 * core] << RADIO_2055_WBRSSI_VCM_IQ_SHIFT); else mod_radio_reg(pi, RADIO_2055_CORE2_RXBB_RSSI_CTRL5, RADIO_2055_WBRSSI_VCM_IQ_MASK, vcm_buf[2 * core] << RADIO_2055_WBRSSI_VCM_IQ_SHIFT); } } } static void wlc_phy_rssi_cal_nphy_rev3(struct brcms_phy *pi) { u16 classif_state; u16 clip_state[2]; u16 clip_off[] = { 0xffff, 0xffff }; s32 target_code; u8 vcm, min_vcm; u8 vcm_final = 0; u8 result_idx; s32 poll_results[8][4] = { {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0} }; s32 poll_result_core[4] = { 0, 0, 0, 0 }; s32 min_d = NPHY_RSSICAL_MAXD, curr_d; s32 fine_digital_offset[4]; s32 poll_results_min[4] = { 0, 0, 0, 0 }; s32 min_poll; u8 vcm_level_max; u8 core; u8 wb_cnt; u8 rssi_type; u16 NPHY_Rfctrlintc1_save, NPHY_Rfctrlintc2_save; u16 NPHY_AfectrlOverride1_save, NPHY_AfectrlOverride2_save; u16 NPHY_AfectrlCore1_save, NPHY_AfectrlCore2_save; u16 NPHY_RfctrlOverride0_save, NPHY_RfctrlOverride1_save; u16 NPHY_RfctrlOverrideAux0_save, NPHY_RfctrlOverrideAux1_save; u16 NPHY_RfctrlCmd_save; u16 NPHY_RfctrlMiscReg1_save, NPHY_RfctrlMiscReg2_save; u16 NPHY_RfctrlRSSIOTHERS1_save, NPHY_RfctrlRSSIOTHERS2_save; u8 rxcore_state; u16 NPHY_REV7_RfctrlOverride3_save, NPHY_REV7_RfctrlOverride4_save; u16 NPHY_REV7_RfctrlOverride5_save, NPHY_REV7_RfctrlOverride6_save; u16 NPHY_REV7_RfctrlMiscReg3_save, NPHY_REV7_RfctrlMiscReg4_save; u16 NPHY_REV7_RfctrlMiscReg5_save, NPHY_REV7_RfctrlMiscReg6_save; NPHY_REV7_RfctrlOverride3_save = NPHY_REV7_RfctrlOverride4_save = NPHY_REV7_RfctrlOverride5_save = NPHY_REV7_RfctrlOverride6_save = NPHY_REV7_RfctrlMiscReg3_save = NPHY_REV7_RfctrlMiscReg4_save = NPHY_REV7_RfctrlMiscReg5_save = NPHY_REV7_RfctrlMiscReg6_save = 0; classif_state = wlc_phy_classifier_nphy(pi, 0, 0); wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); wlc_phy_clip_det_nphy(pi, 0, clip_state); wlc_phy_clip_det_nphy(pi, 1, clip_off); NPHY_Rfctrlintc1_save = read_phy_reg(pi, 0x91); NPHY_Rfctrlintc2_save = read_phy_reg(pi, 0x92); NPHY_AfectrlOverride1_save = read_phy_reg(pi, 0x8f); NPHY_AfectrlOverride2_save = read_phy_reg(pi, 0xa5); NPHY_AfectrlCore1_save = read_phy_reg(pi, 0xa6); NPHY_AfectrlCore2_save = read_phy_reg(pi, 0xa7); NPHY_RfctrlOverride0_save = read_phy_reg(pi, 0xe7); NPHY_RfctrlOverride1_save = read_phy_reg(pi, 0xec); if (NREV_GE(pi->pubpi.phy_rev, 7)) { NPHY_REV7_RfctrlOverride3_save = read_phy_reg(pi, 0x342); NPHY_REV7_RfctrlOverride4_save = read_phy_reg(pi, 0x343); NPHY_REV7_RfctrlOverride5_save = read_phy_reg(pi, 0x346); NPHY_REV7_RfctrlOverride6_save = read_phy_reg(pi, 0x347); } NPHY_RfctrlOverrideAux0_save = read_phy_reg(pi, 0xe5); NPHY_RfctrlOverrideAux1_save = read_phy_reg(pi, 0xe6); NPHY_RfctrlCmd_save = read_phy_reg(pi, 0x78); NPHY_RfctrlMiscReg1_save = read_phy_reg(pi, 0xf9); NPHY_RfctrlMiscReg2_save = read_phy_reg(pi, 0xfb); if (NREV_GE(pi->pubpi.phy_rev, 7)) { NPHY_REV7_RfctrlMiscReg3_save = read_phy_reg(pi, 0x340); NPHY_REV7_RfctrlMiscReg4_save = read_phy_reg(pi, 0x341); NPHY_REV7_RfctrlMiscReg5_save = read_phy_reg(pi, 0x344); NPHY_REV7_RfctrlMiscReg6_save = read_phy_reg(pi, 0x345); } NPHY_RfctrlRSSIOTHERS1_save = read_phy_reg(pi, 0x7a); NPHY_RfctrlRSSIOTHERS2_save = read_phy_reg(pi, 0x7d); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_OFF, 0, RADIO_MIMO_CORESEL_ALLRXTX); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 1, RADIO_MIMO_CORESEL_ALLRXTX); if (NREV_GE(pi->pubpi.phy_rev, 7)) wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_rxrf_pu, 0, 0, 0); else wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0, 0); if (NREV_GE(pi->pubpi.phy_rev, 7)) wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_rx_pu, 1, 0, 0); else wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0, 0); if (NREV_GE(pi->pubpi.phy_rev, 7)) { wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 6), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); } else { wlc_phy_rfctrl_override_nphy(pi, (0x1 << 7), 1, 0, 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 6), 1, 0, 0); } if (CHSPEC_IS5G(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 5), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 4), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); } else { wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 0, 0, 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 1, 0, 0); } } else { if (NREV_GE(pi->pubpi.phy_rev, 7)) { wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 4), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 5), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); } else { wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 0, 0, 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 1, 0, 0); } } rxcore_state = wlc_phy_rxcore_getstate_nphy( (struct brcms_phy_pub *) pi); vcm_level_max = 8; for (core = 0; core < pi->pubpi.phy_corenum; core++) { if ((rxcore_state & (1 << core)) == 0) continue; wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, core == PHY_CORE_0 ? RADIO_MIMO_CORESEL_CORE1 : RADIO_MIMO_CORESEL_CORE2, NPHY_RAIL_I, NPHY_RSSI_SEL_NB); wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, core == PHY_CORE_0 ? RADIO_MIMO_CORESEL_CORE1 : RADIO_MIMO_CORESEL_CORE2, NPHY_RAIL_Q, NPHY_RSSI_SEL_NB); for (vcm = 0; vcm < vcm_level_max; vcm++) { if (NREV_GE(pi->pubpi.phy_rev, 7)) mod_radio_reg(pi, (core == PHY_CORE_0) ? RADIO_2057_NB_MASTER_CORE0 : RADIO_2057_NB_MASTER_CORE1, RADIO_2057_VCM_MASK, vcm); else mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | ((core == PHY_CORE_0) ? RADIO_2056_RX0 : RADIO_2056_RX1), RADIO_2056_VCM_MASK, vcm << RADIO_2056_RSSI_VCM_SHIFT); wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_NB, &poll_results[vcm][0], NPHY_RSSICAL_NPOLL); } for (result_idx = 0; result_idx < 4; result_idx++) { if ((core == result_idx / 2) && (result_idx % 2 == 0)) { min_d = NPHY_RSSICAL_MAXD; min_vcm = 0; min_poll = NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL + 1; for (vcm = 0; vcm < vcm_level_max; vcm++) { curr_d = poll_results[vcm][result_idx] * poll_results[vcm][result_idx] + poll_results[vcm][result_idx + 1] * poll_results[vcm][result_idx + 1]; if (curr_d < min_d) { min_d = curr_d; min_vcm = vcm; } if (poll_results[vcm][result_idx] < min_poll) min_poll = poll_results[vcm] [result_idx]; } vcm_final = min_vcm; poll_results_min[result_idx] = min_poll; } } if (NREV_GE(pi->pubpi.phy_rev, 7)) mod_radio_reg(pi, (core == PHY_CORE_0) ? RADIO_2057_NB_MASTER_CORE0 : RADIO_2057_NB_MASTER_CORE1, RADIO_2057_VCM_MASK, vcm_final); else mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | ((core == PHY_CORE_0) ? RADIO_2056_RX0 : RADIO_2056_RX1), RADIO_2056_VCM_MASK, vcm_final << RADIO_2056_RSSI_VCM_SHIFT); for (result_idx = 0; result_idx < 4; result_idx++) { if (core == result_idx / 2) { fine_digital_offset[result_idx] = (NPHY_RSSICAL_NB_TARGET * NPHY_RSSICAL_NPOLL) - poll_results[vcm_final][result_idx]; if (fine_digital_offset[result_idx] < 0) { fine_digital_offset[result_idx] = abs(fine_digital_offset [result_idx]); fine_digital_offset[result_idx] += (NPHY_RSSICAL_NPOLL / 2); fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; fine_digital_offset[result_idx] = -fine_digital_offset[ result_idx]; } else { fine_digital_offset[result_idx] += (NPHY_RSSICAL_NPOLL / 2); fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; } if (poll_results_min[result_idx] == NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) fine_digital_offset[result_idx] = (NPHY_RSSICAL_NB_TARGET - NPHY_RSSICAL_MAXREAD - 1); wlc_phy_scale_offset_rssi_nphy( pi, 0x0, (s8) fine_digital_offset [result_idx], (result_idx / 2 == 0) ? RADIO_MIMO_CORESEL_CORE1 : RADIO_MIMO_CORESEL_CORE2, (result_idx % 2 == 0) ? NPHY_RAIL_I : NPHY_RAIL_Q, NPHY_RSSI_SEL_NB); } } } for (core = 0; core < pi->pubpi.phy_corenum; core++) { if ((rxcore_state & (1 << core)) == 0) continue; for (wb_cnt = 0; wb_cnt < 2; wb_cnt++) { if (wb_cnt == 0) { rssi_type = NPHY_RSSI_SEL_W1; target_code = NPHY_RSSICAL_W1_TARGET_REV3; } else { rssi_type = NPHY_RSSI_SEL_W2; target_code = NPHY_RSSICAL_W2_TARGET_REV3; } wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, core == PHY_CORE_0 ? RADIO_MIMO_CORESEL_CORE1 : RADIO_MIMO_CORESEL_CORE2, NPHY_RAIL_I, rssi_type); wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, core == PHY_CORE_0 ? RADIO_MIMO_CORESEL_CORE1 : RADIO_MIMO_CORESEL_CORE2, NPHY_RAIL_Q, rssi_type); wlc_phy_poll_rssi_nphy(pi, rssi_type, poll_result_core, NPHY_RSSICAL_NPOLL); for (result_idx = 0; result_idx < 4; result_idx++) { if (core == result_idx / 2) { fine_digital_offset[result_idx] = (target_code * NPHY_RSSICAL_NPOLL) - poll_result_core[result_idx]; if (fine_digital_offset[result_idx] < 0) { fine_digital_offset[result_idx] = abs( fine_digital_offset [result_idx]); fine_digital_offset[result_idx] += (NPHY_RSSICAL_NPOLL / 2); fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; fine_digital_offset[result_idx] = -fine_digital_offset [result_idx]; } else { fine_digital_offset[result_idx] += (NPHY_RSSICAL_NPOLL / 2); fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; } wlc_phy_scale_offset_rssi_nphy( pi, 0x0, (s8) fine_digital_offset [core * 2], (core == PHY_CORE_0) ? RADIO_MIMO_CORESEL_CORE1 : RADIO_MIMO_CORESEL_CORE2, (result_idx % 2 == 0) ? NPHY_RAIL_I : NPHY_RAIL_Q, rssi_type); } } } } write_phy_reg(pi, 0x91, NPHY_Rfctrlintc1_save); write_phy_reg(pi, 0x92, NPHY_Rfctrlintc2_save); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); mod_phy_reg(pi, 0xe7, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x78, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0xe7, (0x1 << 0), 0); mod_phy_reg(pi, 0xec, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x78, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0xec, (0x1 << 0), 0); write_phy_reg(pi, 0x8f, NPHY_AfectrlOverride1_save); write_phy_reg(pi, 0xa5, NPHY_AfectrlOverride2_save); write_phy_reg(pi, 0xa6, NPHY_AfectrlCore1_save); write_phy_reg(pi, 0xa7, NPHY_AfectrlCore2_save); write_phy_reg(pi, 0xe7, NPHY_RfctrlOverride0_save); write_phy_reg(pi, 0xec, NPHY_RfctrlOverride1_save); if (NREV_GE(pi->pubpi.phy_rev, 7)) { write_phy_reg(pi, 0x342, NPHY_REV7_RfctrlOverride3_save); write_phy_reg(pi, 0x343, NPHY_REV7_RfctrlOverride4_save); write_phy_reg(pi, 0x346, NPHY_REV7_RfctrlOverride5_save); write_phy_reg(pi, 0x347, NPHY_REV7_RfctrlOverride6_save); } write_phy_reg(pi, 0xe5, NPHY_RfctrlOverrideAux0_save); write_phy_reg(pi, 0xe6, NPHY_RfctrlOverrideAux1_save); write_phy_reg(pi, 0x78, NPHY_RfctrlCmd_save); write_phy_reg(pi, 0xf9, NPHY_RfctrlMiscReg1_save); write_phy_reg(pi, 0xfb, NPHY_RfctrlMiscReg2_save); if (NREV_GE(pi->pubpi.phy_rev, 7)) { write_phy_reg(pi, 0x340, NPHY_REV7_RfctrlMiscReg3_save); write_phy_reg(pi, 0x341, NPHY_REV7_RfctrlMiscReg4_save); write_phy_reg(pi, 0x344, NPHY_REV7_RfctrlMiscReg5_save); write_phy_reg(pi, 0x345, NPHY_REV7_RfctrlMiscReg6_save); } write_phy_reg(pi, 0x7a, NPHY_RfctrlRSSIOTHERS1_save); write_phy_reg(pi, 0x7d, NPHY_RfctrlRSSIOTHERS2_save); if (CHSPEC_IS2G(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { pi->rssical_cache.rssical_radio_regs_2G[0] = read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0); pi->rssical_cache.rssical_radio_regs_2G[1] = read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1); } else { pi->rssical_cache.rssical_radio_regs_2G[0] = read_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0); pi->rssical_cache.rssical_radio_regs_2G[1] = read_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1); } pi->rssical_cache.rssical_phyregs_2G[0] = read_phy_reg(pi, 0x1a6); pi->rssical_cache.rssical_phyregs_2G[1] = read_phy_reg(pi, 0x1ac); pi->rssical_cache.rssical_phyregs_2G[2] = read_phy_reg(pi, 0x1b2); pi->rssical_cache.rssical_phyregs_2G[3] = read_phy_reg(pi, 0x1b8); pi->rssical_cache.rssical_phyregs_2G[4] = read_phy_reg(pi, 0x1a4); pi->rssical_cache.rssical_phyregs_2G[5] = read_phy_reg(pi, 0x1aa); pi->rssical_cache.rssical_phyregs_2G[6] = read_phy_reg(pi, 0x1b0); pi->rssical_cache.rssical_phyregs_2G[7] = read_phy_reg(pi, 0x1b6); pi->rssical_cache.rssical_phyregs_2G[8] = read_phy_reg(pi, 0x1a5); pi->rssical_cache.rssical_phyregs_2G[9] = read_phy_reg(pi, 0x1ab); pi->rssical_cache.rssical_phyregs_2G[10] = read_phy_reg(pi, 0x1b1); pi->rssical_cache.rssical_phyregs_2G[11] = read_phy_reg(pi, 0x1b7); pi->nphy_rssical_chanspec_2G = pi->radio_chanspec; } else { if (NREV_GE(pi->pubpi.phy_rev, 7)) { pi->rssical_cache.rssical_radio_regs_5G[0] = read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0); pi->rssical_cache.rssical_radio_regs_5G[1] = read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1); } else { pi->rssical_cache.rssical_radio_regs_5G[0] = read_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0); pi->rssical_cache.rssical_radio_regs_5G[1] = read_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1); } pi->rssical_cache.rssical_phyregs_5G[0] = read_phy_reg(pi, 0x1a6); pi->rssical_cache.rssical_phyregs_5G[1] = read_phy_reg(pi, 0x1ac); pi->rssical_cache.rssical_phyregs_5G[2] = read_phy_reg(pi, 0x1b2); pi->rssical_cache.rssical_phyregs_5G[3] = read_phy_reg(pi, 0x1b8); pi->rssical_cache.rssical_phyregs_5G[4] = read_phy_reg(pi, 0x1a4); pi->rssical_cache.rssical_phyregs_5G[5] = read_phy_reg(pi, 0x1aa); pi->rssical_cache.rssical_phyregs_5G[6] = read_phy_reg(pi, 0x1b0); pi->rssical_cache.rssical_phyregs_5G[7] = read_phy_reg(pi, 0x1b6); pi->rssical_cache.rssical_phyregs_5G[8] = read_phy_reg(pi, 0x1a5); pi->rssical_cache.rssical_phyregs_5G[9] = read_phy_reg(pi, 0x1ab); pi->rssical_cache.rssical_phyregs_5G[10] = read_phy_reg(pi, 0x1b1); pi->rssical_cache.rssical_phyregs_5G[11] = read_phy_reg(pi, 0x1b7); pi->nphy_rssical_chanspec_5G = pi->radio_chanspec; } wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); wlc_phy_clip_det_nphy(pi, 1, clip_state); } static void wlc_phy_rssi_cal_nphy_rev2(struct brcms_phy *pi, u8 rssi_type) { s32 target_code; u16 classif_state; u16 clip_state[2]; u16 rssi_ctrl_state[2], pd_state[2]; u16 rfctrlintc_state[2], rfpdcorerxtx_state[2]; u16 rfctrlintc_override_val; u16 clip_off[] = { 0xffff, 0xffff }; u16 rf_pd_val, pd_mask, rssi_ctrl_mask; u8 vcm, min_vcm, vcm_tmp[4]; u8 vcm_final[4] = { 0, 0, 0, 0 }; u8 result_idx, ctr; s32 poll_results[4][4] = { {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0} }; s32 poll_miniq[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; s32 min_d, curr_d; s32 fine_digital_offset[4]; s32 poll_results_min[4] = { 0, 0, 0, 0 }; s32 min_poll; switch (rssi_type) { case NPHY_RSSI_SEL_NB: target_code = NPHY_RSSICAL_NB_TARGET; break; case NPHY_RSSI_SEL_W1: target_code = NPHY_RSSICAL_W1_TARGET; break; case NPHY_RSSI_SEL_W2: target_code = NPHY_RSSICAL_W2_TARGET; break; default: return; break; } classif_state = wlc_phy_classifier_nphy(pi, 0, 0); wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); wlc_phy_clip_det_nphy(pi, 0, clip_state); wlc_phy_clip_det_nphy(pi, 1, clip_off); rf_pd_val = (rssi_type == NPHY_RSSI_SEL_NB) ? 0x6 : 0x4; rfctrlintc_override_val = CHSPEC_IS5G(pi->radio_chanspec) ? 0x140 : 0x110; rfctrlintc_state[0] = read_phy_reg(pi, 0x91); rfpdcorerxtx_state[0] = read_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX); write_phy_reg(pi, 0x91, rfctrlintc_override_val); write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rf_pd_val); rfctrlintc_state[1] = read_phy_reg(pi, 0x92); rfpdcorerxtx_state[1] = read_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX); write_phy_reg(pi, 0x92, rfctrlintc_override_val); write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rf_pd_val); pd_mask = RADIO_2055_NBRSSI_PD | RADIO_2055_WBRSSI_G1_PD | RADIO_2055_WBRSSI_G2_PD; pd_state[0] = read_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC) & pd_mask; pd_state[1] = read_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC) & pd_mask; mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, 0); mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, 0); rssi_ctrl_mask = RADIO_2055_NBRSSI_SEL | RADIO_2055_WBRSSI_G1_SEL | RADIO_2055_WBRSSI_G2_SEL; rssi_ctrl_state[0] = read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE1) & rssi_ctrl_mask; rssi_ctrl_state[1] = read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE2) & rssi_ctrl_mask; wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type); wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX, NPHY_RAIL_I, rssi_type); wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX, NPHY_RAIL_Q, rssi_type); for (vcm = 0; vcm < 4; vcm++) { vcm_tmp[0] = vcm_tmp[1] = vcm_tmp[2] = vcm_tmp[3] = vcm; if (rssi_type != NPHY_RSSI_SEL_W2) wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_tmp); wlc_phy_poll_rssi_nphy(pi, rssi_type, &poll_results[vcm][0], NPHY_RSSICAL_NPOLL); if ((rssi_type == NPHY_RSSI_SEL_W1) || (rssi_type == NPHY_RSSI_SEL_W2)) { for (ctr = 0; ctr < 2; ctr++) poll_miniq[vcm][ctr] = min(poll_results[vcm][ctr * 2 + 0], poll_results[vcm][ctr * 2 + 1]); } } for (result_idx = 0; result_idx < 4; result_idx++) { min_d = NPHY_RSSICAL_MAXD; min_vcm = 0; min_poll = NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL + 1; for (vcm = 0; vcm < 4; vcm++) { curr_d = abs(((rssi_type == NPHY_RSSI_SEL_NB) ? poll_results[vcm][result_idx] : poll_miniq[vcm][result_idx / 2]) - (target_code * NPHY_RSSICAL_NPOLL)); if (curr_d < min_d) { min_d = curr_d; min_vcm = vcm; } if (poll_results[vcm][result_idx] < min_poll) min_poll = poll_results[vcm][result_idx]; } vcm_final[result_idx] = min_vcm; poll_results_min[result_idx] = min_poll; } if (rssi_type != NPHY_RSSI_SEL_W2) wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_final); for (result_idx = 0; result_idx < 4; result_idx++) { fine_digital_offset[result_idx] = (target_code * NPHY_RSSICAL_NPOLL) - poll_results[vcm_final[result_idx]][result_idx]; if (fine_digital_offset[result_idx] < 0) { fine_digital_offset[result_idx] = abs(fine_digital_offset[result_idx]); fine_digital_offset[result_idx] += (NPHY_RSSICAL_NPOLL / 2); fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; fine_digital_offset[result_idx] = -fine_digital_offset[result_idx]; } else { fine_digital_offset[result_idx] += (NPHY_RSSICAL_NPOLL / 2); fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; } if (poll_results_min[result_idx] == NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) fine_digital_offset[result_idx] = (target_code - NPHY_RSSICAL_MAXREAD - 1); wlc_phy_scale_offset_rssi_nphy(pi, 0x0, (s8) fine_digital_offset[result_idx], (result_idx / 2 == 0) ? RADIO_MIMO_CORESEL_CORE1 : RADIO_MIMO_CORESEL_CORE2, (result_idx % 2 == 0) ? NPHY_RAIL_I : NPHY_RAIL_Q, rssi_type); } mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, pd_state[0]); mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, pd_state[1]); if (rssi_ctrl_state[0] == RADIO_2055_NBRSSI_SEL) wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, NPHY_RSSI_SEL_NB); else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G1_SEL) wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, NPHY_RSSI_SEL_W1); else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G2_SEL) wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, NPHY_RSSI_SEL_W2); else wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, NPHY_RSSI_SEL_W2); if (rssi_ctrl_state[1] == RADIO_2055_NBRSSI_SEL) wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, NPHY_RSSI_SEL_NB); else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G1_SEL) wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, NPHY_RSSI_SEL_W1); else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G2_SEL) wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, NPHY_RSSI_SEL_W2); else wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, NPHY_RSSI_SEL_W2); wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, rssi_type); write_phy_reg(pi, 0x91, rfctrlintc_state[0]); write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rfpdcorerxtx_state[0]); write_phy_reg(pi, 0x92, rfctrlintc_state[1]); write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rfpdcorerxtx_state[1]); wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); wlc_phy_clip_det_nphy(pi, 1, clip_state); wlc_phy_resetcca_nphy(pi); } void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { wlc_phy_rssi_cal_nphy_rev3(pi); } else { wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_NB); wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W1); wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W2); } } int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh) { s16 rxpwr, rxpwr0, rxpwr1; s16 phyRx0_l, phyRx2_l; rxpwr = 0; rxpwr0 = rxh->PhyRxStatus_1 & PRXS1_nphy_PWR0_MASK; rxpwr1 = (rxh->PhyRxStatus_1 & PRXS1_nphy_PWR1_MASK) >> 8; if (rxpwr0 > 127) rxpwr0 -= 256; if (rxpwr1 > 127) rxpwr1 -= 256; phyRx0_l = rxh->PhyRxStatus_0 & 0x00ff; phyRx2_l = rxh->PhyRxStatus_2 & 0x00ff; if (phyRx2_l > 127) phyRx2_l -= 256; if (((rxpwr0 == 16) || (rxpwr0 == 32))) { rxpwr0 = rxpwr1; rxpwr1 = phyRx2_l; } if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MAX) rxpwr = (rxpwr0 > rxpwr1) ? rxpwr0 : rxpwr1; else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MIN) rxpwr = (rxpwr0 < rxpwr1) ? rxpwr0 : rxpwr1; else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_AVG) rxpwr = (rxpwr0 + rxpwr1) >> 1; return rxpwr; } static void wlc_phy_loadsampletable_nphy(struct brcms_phy *pi, struct cordic_iq *tone_buf, u16 num_samps) { u16 t; u32 *data_buf = NULL; data_buf = kmalloc(sizeof(u32) * num_samps, GFP_ATOMIC); if (data_buf == NULL) return; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); for (t = 0; t < num_samps; t++) data_buf[t] = ((((unsigned int)tone_buf[t].i) & 0x3ff) << 10) | (((unsigned int)tone_buf[t].q) & 0x3ff); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SAMPLEPLAY, num_samps, 0, 32, data_buf); kfree(data_buf); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static u16 wlc_phy_gen_load_samples_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 dac_test_mode) { u8 phy_bw, is_phybw40; u16 num_samps, t, spur; s32 theta = 0, rot = 0; u32 tbl_len; struct cordic_iq *tone_buf = NULL; is_phybw40 = CHSPEC_IS40(pi->radio_chanspec); phy_bw = (is_phybw40 == 1) ? 40 : 20; tbl_len = (phy_bw << 3); if (dac_test_mode == 1) { spur = read_phy_reg(pi, 0x01); spur = (spur >> 15) & 1; phy_bw = (spur == 1) ? 82 : 80; phy_bw = (is_phybw40 == 1) ? (phy_bw << 1) : phy_bw; tbl_len = (phy_bw << 1); } tone_buf = kmalloc(sizeof(struct cordic_iq) * tbl_len, GFP_ATOMIC); if (tone_buf == NULL) return 0; num_samps = (u16) tbl_len; rot = ((f_kHz * 36) / phy_bw) / 100; theta = 0; for (t = 0; t < num_samps; t++) { tone_buf[t] = cordic_calc_iq(theta); theta += rot; tone_buf[t].q = (s32) FLOAT(tone_buf[t].q * max_val); tone_buf[t].i = (s32) FLOAT(tone_buf[t].i * max_val); } wlc_phy_loadsampletable_nphy(pi, tone_buf, num_samps); kfree(tone_buf); return num_samps; } static void wlc_phy_runsamples_nphy(struct brcms_phy *pi, u16 num_samps, u16 loops, u16 wait, u8 iqmode, u8 dac_test_mode, bool modify_bbmult) { u16 bb_mult; u8 phy_bw, sample_cmd; u16 orig_RfseqCoreActv; u16 lpf_bw_ctl_override3, lpf_bw_ctl_override4, lpf_bw_ctl_miscreg3, lpf_bw_ctl_miscreg4; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); phy_bw = 20; if (CHSPEC_IS40(pi->radio_chanspec)) phy_bw = 40; if (NREV_GE(pi->pubpi.phy_rev, 7)) { lpf_bw_ctl_override3 = read_phy_reg(pi, 0x342) & (0x1 << 7); lpf_bw_ctl_override4 = read_phy_reg(pi, 0x343) & (0x1 << 7); if (lpf_bw_ctl_override3 | lpf_bw_ctl_override4) { lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) & (0x7 << 8); lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) & (0x7 << 8); } else { wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), wlc_phy_read_lpf_bw_ctl_nphy (pi, 0), 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); pi->nphy_sample_play_lpf_bw_ctl_ovr = true; lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) & (0x7 << 8); lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) & (0x7 << 8); } } if ((pi->nphy_bb_mult_save & BB_MULT_VALID_MASK) == 0) { wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, &bb_mult); pi->nphy_bb_mult_save = BB_MULT_VALID_MASK | (bb_mult & BB_MULT_MASK); } if (modify_bbmult) { bb_mult = (phy_bw == 20) ? 100 : 71; bb_mult = (bb_mult << 8) + bb_mult; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, &bb_mult); } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); write_phy_reg(pi, 0xc6, num_samps - 1); if (loops != 0xffff) write_phy_reg(pi, 0xc4, loops - 1); else write_phy_reg(pi, 0xc4, loops); write_phy_reg(pi, 0xc5, wait); orig_RfseqCoreActv = read_phy_reg(pi, 0xa1); or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override); if (iqmode) { and_phy_reg(pi, 0xc2, 0x7FFF); or_phy_reg(pi, 0xc2, 0x8000); } else { sample_cmd = (dac_test_mode == 1) ? 0x5 : 0x1; write_phy_reg(pi, 0xc3, sample_cmd); } SPINWAIT(((read_phy_reg(pi, 0xa4) & 0x1) == 1), 1000); write_phy_reg(pi, 0xa1, orig_RfseqCoreActv); } int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 iqmode, u8 dac_test_mode, bool modify_bbmult) { u16 num_samps; u16 loops = 0xffff; u16 wait = 0; num_samps = wlc_phy_gen_load_samples_nphy(pi, f_kHz, max_val, dac_test_mode); if (num_samps == 0) return -EBADE; wlc_phy_runsamples_nphy(pi, num_samps, loops, wait, iqmode, dac_test_mode, modify_bbmult); return 0; } void wlc_phy_stopplayback_nphy(struct brcms_phy *pi) { u16 playback_status; u16 bb_mult; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); playback_status = read_phy_reg(pi, 0xc7); if (playback_status & 0x1) or_phy_reg(pi, 0xc3, NPHY_sampleCmd_STOP); else if (playback_status & 0x2) and_phy_reg(pi, 0xc2, (u16) ~NPHY_iqloCalCmdGctl_IQLO_CAL_EN); and_phy_reg(pi, 0xc3, (u16) ~(0x1 << 2)); if ((pi->nphy_bb_mult_save & BB_MULT_VALID_MASK) != 0) { bb_mult = pi->nphy_bb_mult_save & BB_MULT_MASK; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, &bb_mult); pi->nphy_bb_mult_save = 0; } if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) { if (pi->nphy_sample_play_lpf_bw_ctl_ovr) { wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), 0, 0, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); pi->nphy_sample_play_lpf_bw_ctl_ovr = false; } } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static u32 *brcms_phy_get_tx_pwrctrl_tbl(struct brcms_phy *pi) { u32 *tx_pwrctrl_tbl = NULL; uint phyrev = pi->pubpi.phy_rev; if (PHY_IPA(pi)) { tx_pwrctrl_tbl = wlc_phy_get_ipa_gaintbl_nphy(pi); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { if (NREV_IS(phyrev, 3)) tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev3; else if (NREV_IS(phyrev, 4)) tx_pwrctrl_tbl = (pi->srom_fem5g.extpagain == 3) ? nphy_tpc_5GHz_txgain_HiPwrEPA : nphy_tpc_5GHz_txgain_rev4; else tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev5; } else { if (NREV_GE(phyrev, 7)) { if (pi->pubpi.radiorev == 3) tx_pwrctrl_tbl = nphy_tpc_txgain_epa_2057rev3; else if (pi->pubpi.radiorev == 5) tx_pwrctrl_tbl = nphy_tpc_txgain_epa_2057rev5; } else { if (NREV_GE(phyrev, 5) && (pi->srom_fem2g.extpagain == 3)) tx_pwrctrl_tbl = nphy_tpc_txgain_HiPwrEPA; else tx_pwrctrl_tbl = nphy_tpc_txgain_rev3; } } } return tx_pwrctrl_tbl; } struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi) { u16 base_idx[2], curr_gain[2]; u8 core_no; struct nphy_txgains target_gain; u32 *tx_pwrctrl_tbl = NULL; if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) { if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, curr_gain); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); for (core_no = 0; core_no < 2; core_no++) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { target_gain.ipa[core_no] = curr_gain[core_no] & 0x0007; target_gain.pad[core_no] = ((curr_gain[core_no] & 0x00F8) >> 3); target_gain.pga[core_no] = ((curr_gain[core_no] & 0x0F00) >> 8); target_gain.txgm[core_no] = ((curr_gain[core_no] & 0x7000) >> 12); target_gain.txlpf[core_no] = ((curr_gain[core_no] & 0x8000) >> 15); } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { target_gain.ipa[core_no] = curr_gain[core_no] & 0x000F; target_gain.pad[core_no] = ((curr_gain[core_no] & 0x00F0) >> 4); target_gain.pga[core_no] = ((curr_gain[core_no] & 0x0F00) >> 8); target_gain.txgm[core_no] = ((curr_gain[core_no] & 0x7000) >> 12); } else { target_gain.ipa[core_no] = curr_gain[core_no] & 0x0003; target_gain.pad[core_no] = ((curr_gain[core_no] & 0x000C) >> 2); target_gain.pga[core_no] = ((curr_gain[core_no] & 0x0070) >> 4); target_gain.txgm[core_no] = ((curr_gain[core_no] & 0x0380) >> 7); } } } else { uint phyrev = pi->pubpi.phy_rev; base_idx[0] = (read_phy_reg(pi, 0x1ed) >> 8) & 0x7f; base_idx[1] = (read_phy_reg(pi, 0x1ee) >> 8) & 0x7f; for (core_no = 0; core_no < 2; core_no++) { if (NREV_GE(phyrev, 3)) { tx_pwrctrl_tbl = brcms_phy_get_tx_pwrctrl_tbl(pi); if (NREV_GE(phyrev, 7)) { target_gain.ipa[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 16) & 0x7; target_gain.pad[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 19) & 0x1f; target_gain.pga[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 24) & 0xf; target_gain.txgm[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 28) & 0x7; target_gain.txlpf[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 31) & 0x1; } else { target_gain.ipa[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 16) & 0xf; target_gain.pad[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 20) & 0xf; target_gain.pga[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 24) & 0xf; target_gain.txgm[core_no] = (tx_pwrctrl_tbl [base_idx[core_no]] >> 28) & 0x7; } } else { target_gain.ipa[core_no] = (nphy_tpc_txgain[base_idx[core_no]] >> 16) & 0x3; target_gain.pad[core_no] = (nphy_tpc_txgain[base_idx[core_no]] >> 18) & 0x3; target_gain.pga[core_no] = (nphy_tpc_txgain[base_idx[core_no]] >> 20) & 0x7; target_gain.txgm[core_no] = (nphy_tpc_txgain[base_idx[core_no]] >> 23) & 0x7; } } } return target_gain; } static void wlc_phy_iqcal_gainparams_nphy(struct brcms_phy *pi, u16 core_no, struct nphy_txgains target_gain, struct nphy_iqcal_params *params) { u8 k; int idx; u16 gain_index; u8 band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0); if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) params->txlpf = target_gain.txlpf[core_no]; params->txgm = target_gain.txgm[core_no]; params->pga = target_gain.pga[core_no]; params->pad = target_gain.pad[core_no]; params->ipa = target_gain.ipa[core_no]; if (NREV_GE(pi->pubpi.phy_rev, 7)) params->cal_gain = ((params->txlpf << 15) | (params->txgm << 12) | (params->pga << 8) | (params->pad << 3) | (params->ipa)); else params->cal_gain = ((params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa)); params->ncorr[0] = 0x79; params->ncorr[1] = 0x79; params->ncorr[2] = 0x79; params->ncorr[3] = 0x79; params->ncorr[4] = 0x79; } else { gain_index = ((target_gain.pad[core_no] << 0) | (target_gain.pga[core_no] << 4) | (target_gain.txgm[core_no] << 8)); idx = -1; for (k = 0; k < NPHY_IQCAL_NUMGAINS; k++) { if (tbl_iqcal_gainparams_nphy[band_idx][k][0] == gain_index) { idx = k; break; } } params->txgm = tbl_iqcal_gainparams_nphy[band_idx][k][1]; params->pga = tbl_iqcal_gainparams_nphy[band_idx][k][2]; params->pad = tbl_iqcal_gainparams_nphy[band_idx][k][3]; params->cal_gain = ((params->txgm << 7) | (params->pga << 4) | (params->pad << 2)); params->ncorr[0] = tbl_iqcal_gainparams_nphy[band_idx][k][4]; params->ncorr[1] = tbl_iqcal_gainparams_nphy[band_idx][k][5]; params->ncorr[2] = tbl_iqcal_gainparams_nphy[band_idx][k][6]; params->ncorr[3] = tbl_iqcal_gainparams_nphy[band_idx][k][7]; } } static void wlc_phy_txcal_radio_setup_nphy(struct brcms_phy *pi) { u16 jtag_core, core; if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (core = 0; core <= 1; core++) { pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MASTER); pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG); pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC); pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM); pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] = 0; pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX); if (pi->pubpi.radiorev != 5) pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA); pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG); pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1); if (CHSPEC_IS5G(pi->radio_chanspec)) { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MASTER, 0x0a); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, 0x43); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, 0x55); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, 0x00); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, 0x00); if (pi->use_int_tx_iqlo_cal_nphy) { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, 0x4); if (!(pi-> internal_tx_iqlo_cal_tapoff_intpa_nphy)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA, 0x31); else WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA, 0x21); } WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, 0x00); } else { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MASTER, 0x06); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, 0x43); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, 0x55); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, 0x00); if (pi->pubpi.radiorev != 5) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA, 0x00); if (pi->use_int_tx_iqlo_cal_nphy) { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, 0x06); if (!(pi-> internal_tx_iqlo_cal_tapoff_intpa_nphy)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, 0x31); else WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, 0x21); } WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, 0x00); } } } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { for (core = 0; core <= 1; core++) { jtag_core = (core == PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1; pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] = read_radio_reg(pi, RADIO_2056_TX_TX_SSI_MASTER | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] = read_radio_reg(pi, RADIO_2056_TX_IQCAL_VCM_HG | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] = read_radio_reg(pi, RADIO_2056_TX_IQCAL_IDAC | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] = read_radio_reg( pi, RADIO_2056_TX_TSSI_VCM | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] = read_radio_reg(pi, RADIO_2056_TX_TX_AMP_DET | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] = read_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] = read_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] = read_radio_reg(pi, RADIO_2056_TX_TSSIG | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] = read_radio_reg(pi, RADIO_2056_TX_TSSI_MISC1 | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 9] = read_radio_reg(pi, RADIO_2056_TX_TSSI_MISC2 | jtag_core); pi->tx_rx_cal_radio_saveregs[(core * 11) + 10] = read_radio_reg(pi, RADIO_2056_TX_TSSI_MISC3 | jtag_core); if (CHSPEC_IS5G(pi->radio_chanspec)) { write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MASTER | jtag_core, 0x0a); write_radio_reg(pi, RADIO_2056_TX_IQCAL_VCM_HG | jtag_core, 0x40); write_radio_reg(pi, RADIO_2056_TX_IQCAL_IDAC | jtag_core, 0x55); write_radio_reg(pi, RADIO_2056_TX_TSSI_VCM | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TX_AMP_DET | jtag_core, 0x00); if (PHY_IPA(pi)) { write_radio_reg( pi, RADIO_2056_TX_TX_SSI_MUX | jtag_core, 0x4); write_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core, 0x1); } else { write_radio_reg( pi, RADIO_2056_TX_TX_SSI_MUX | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core, 0x2f); } write_radio_reg(pi, RADIO_2056_TX_TSSIG | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC1 | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC2 | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC3 | jtag_core, 0x00); } else { write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MASTER | jtag_core, 0x06); write_radio_reg(pi, RADIO_2056_TX_IQCAL_VCM_HG | jtag_core, 0x40); write_radio_reg(pi, RADIO_2056_TX_IQCAL_IDAC | jtag_core, 0x55); write_radio_reg(pi, RADIO_2056_TX_TSSI_VCM | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TX_AMP_DET | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core, 0x00); if (PHY_IPA(pi)) { write_radio_reg( pi, RADIO_2056_TX_TX_SSI_MUX | jtag_core, 0x06); if (NREV_LT(pi->pubpi.phy_rev, 5)) write_radio_reg( pi, RADIO_2056_TX_TSSIG | jtag_core, 0x11); else write_radio_reg( pi, RADIO_2056_TX_TSSIG | jtag_core, 0x1); } else { write_radio_reg( pi, RADIO_2056_TX_TX_SSI_MUX | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSIG | jtag_core, 0x20); } write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC1 | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC2 | jtag_core, 0x00); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC3 | jtag_core, 0x00); } } } else { pi->tx_rx_cal_radio_saveregs[0] = read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1); write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, 0x29); pi->tx_rx_cal_radio_saveregs[1] = read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2); write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, 0x54); pi->tx_rx_cal_radio_saveregs[2] = read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, 0x29); pi->tx_rx_cal_radio_saveregs[3] = read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, 0x54); pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1); pi->tx_rx_cal_radio_saveregs[5] = read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2); if ((read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand) == 0) { write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x04); write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x04); } else { write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x20); write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x20); } if (NREV_LT(pi->pubpi.phy_rev, 2)) { or_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, 0x20); or_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, 0x20); } else { and_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, 0xdf); and_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, 0xdf); } } } static void wlc_phy_txcal_radio_cleanup_nphy(struct brcms_phy *pi) { u16 jtag_core, core; if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (core = 0; core <= 1; core++) { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MASTER, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 0]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 1]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 2]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 3]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 5]); if (pi->pubpi.radiorev != 5) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIA, pi->tx_rx_cal_radio_saveregs [(core * 11) + 6]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 7]); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 8]); } } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { for (core = 0; core <= 1; core++) { jtag_core = (core == PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1; write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MASTER | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 0]); write_radio_reg(pi, RADIO_2056_TX_IQCAL_VCM_HG | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 1]); write_radio_reg(pi, RADIO_2056_TX_IQCAL_IDAC | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 2]); write_radio_reg(pi, RADIO_2056_TX_TSSI_VCM | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 3]); write_radio_reg(pi, RADIO_2056_TX_TX_AMP_DET | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 4]); write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 5]); write_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 6]); write_radio_reg(pi, RADIO_2056_TX_TSSIG | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 7]); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC1 | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 8]); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC2 | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 9]); write_radio_reg(pi, RADIO_2056_TX_TSSI_MISC3 | jtag_core, pi-> tx_rx_cal_radio_saveregs[(core * 11) + 10]); } } else { write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, pi->tx_rx_cal_radio_saveregs[0]); write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, pi->tx_rx_cal_radio_saveregs[1]); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, pi->tx_rx_cal_radio_saveregs[2]); write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, pi->tx_rx_cal_radio_saveregs[3]); write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, pi->tx_rx_cal_radio_saveregs[4]); write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, pi->tx_rx_cal_radio_saveregs[5]); } } static void wlc_phy_txcal_physetup_nphy(struct brcms_phy *pi) { u16 val, mask; if (NREV_GE(pi->pubpi.phy_rev, 3)) { pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa6); pi->tx_rx_cal_phy_saveregs[1] = read_phy_reg(pi, 0xa7); mask = ((0x3 << 8) | (0x3 << 10)); val = (0x2 << 8); val |= (0x2 << 10); mod_phy_reg(pi, 0xa6, mask, val); mod_phy_reg(pi, 0xa7, mask, val); val = read_phy_reg(pi, 0x8f); pi->tx_rx_cal_phy_saveregs[2] = val; val |= ((0x1 << 9) | (0x1 << 10)); write_phy_reg(pi, 0x8f, val); val = read_phy_reg(pi, 0xa5); pi->tx_rx_cal_phy_saveregs[3] = val; val |= ((0x1 << 9) | (0x1 << 10)); write_phy_reg(pi, 0xa5, val); pi->tx_rx_cal_phy_saveregs[4] = read_phy_reg(pi, 0x01); mod_phy_reg(pi, 0x01, (0x1 << 15), 0); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, &val); pi->tx_rx_cal_phy_saveregs[5] = val; val = 0; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, &val); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, &val); pi->tx_rx_cal_phy_saveregs[6] = val; val = 0; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, &val); pi->tx_rx_cal_phy_saveregs[7] = read_phy_reg(pi, 0x91); pi->tx_rx_cal_phy_saveregs[8] = read_phy_reg(pi, 0x92); if (!(pi->use_int_tx_iqlo_cal_nphy)) wlc_phy_rfctrlintc_override_nphy( pi, NPHY_RfctrlIntc_override_PA, 1, RADIO_MIMO_CORESEL_CORE1 | RADIO_MIMO_CORESEL_CORE2); else wlc_phy_rfctrlintc_override_nphy( pi, NPHY_RfctrlIntc_override_PA, 0, RADIO_MIMO_CORESEL_CORE1 | RADIO_MIMO_CORESEL_CORE2); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 0x2, RADIO_MIMO_CORESEL_CORE1); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 0x8, RADIO_MIMO_CORESEL_CORE2); pi->tx_rx_cal_phy_saveregs[9] = read_phy_reg(pi, 0x297); pi->tx_rx_cal_phy_saveregs[10] = read_phy_reg(pi, 0x29b); mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (0) << 0); mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (0) << 0); if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), wlc_phy_read_lpf_bw_ctl_nphy (pi, 0), 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); if (pi->use_int_tx_iqlo_cal_nphy && !(pi->internal_tx_iqlo_cal_tapoff_intpa_nphy)) { if (NREV_IS(pi->pubpi.phy_rev, 7)) { mod_radio_reg(pi, RADIO_2057_OVR_REG0, 1 << 4, 1 << 4); if (CHSPEC_IS2G(pi->radio_chanspec)) { mod_radio_reg( pi, RADIO_2057_PAD2G_TUNE_PUS_CORE0, 1, 0); mod_radio_reg( pi, RADIO_2057_PAD2G_TUNE_PUS_CORE1, 1, 0); } else { mod_radio_reg( pi, RADIO_2057_IPA5G_CASCOFFV_PU_CORE0, 1, 0); mod_radio_reg( pi, RADIO_2057_IPA5G_CASCOFFV_PU_CORE1, 1, 0); } } else if (NREV_GE(pi->pubpi.phy_rev, 8)) { wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 3), 0, 0x3, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); } } } else { pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa6); pi->tx_rx_cal_phy_saveregs[1] = read_phy_reg(pi, 0xa7); mask = ((0x3 << 12) | (0x3 << 14)); val = (0x2 << 12); val |= (0x2 << 14); mod_phy_reg(pi, 0xa6, mask, val); mod_phy_reg(pi, 0xa7, mask, val); val = read_phy_reg(pi, 0xa5); pi->tx_rx_cal_phy_saveregs[2] = val; val |= ((0x1 << 12) | (0x1 << 13)); write_phy_reg(pi, 0xa5, val); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, &val); pi->tx_rx_cal_phy_saveregs[3] = val; val |= 0x2000; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, &val); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, &val); pi->tx_rx_cal_phy_saveregs[4] = val; val |= 0x2000; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, &val); pi->tx_rx_cal_phy_saveregs[5] = read_phy_reg(pi, 0x91); pi->tx_rx_cal_phy_saveregs[6] = read_phy_reg(pi, 0x92); val = CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120; write_phy_reg(pi, 0x91, val); write_phy_reg(pi, 0x92, val); } } static void wlc_phy_txcal_phycleanup_nphy(struct brcms_phy *pi) { u16 mask; if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_phy_reg(pi, 0xa6, pi->tx_rx_cal_phy_saveregs[0]); write_phy_reg(pi, 0xa7, pi->tx_rx_cal_phy_saveregs[1]); write_phy_reg(pi, 0x8f, pi->tx_rx_cal_phy_saveregs[2]); write_phy_reg(pi, 0xa5, pi->tx_rx_cal_phy_saveregs[3]); write_phy_reg(pi, 0x01, pi->tx_rx_cal_phy_saveregs[4]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, &pi->tx_rx_cal_phy_saveregs[5]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, &pi->tx_rx_cal_phy_saveregs[6]); write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[7]); write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[8]); write_phy_reg(pi, 0x297, pi->tx_rx_cal_phy_saveregs[9]); write_phy_reg(pi, 0x29b, pi->tx_rx_cal_phy_saveregs[10]); if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), 0, 0, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_resetcca_nphy(pi); if (pi->use_int_tx_iqlo_cal_nphy && !(pi->internal_tx_iqlo_cal_tapoff_intpa_nphy)) { if (NREV_IS(pi->pubpi.phy_rev, 7)) { if (CHSPEC_IS2G(pi->radio_chanspec)) { mod_radio_reg( pi, RADIO_2057_PAD2G_TUNE_PUS_CORE0, 1, 1); mod_radio_reg( pi, RADIO_2057_PAD2G_TUNE_PUS_CORE1, 1, 1); } else { mod_radio_reg( pi, RADIO_2057_IPA5G_CASCOFFV_PU_CORE0, 1, 1); mod_radio_reg( pi, RADIO_2057_IPA5G_CASCOFFV_PU_CORE1, 1, 1); } mod_radio_reg(pi, RADIO_2057_OVR_REG0, 1 << 4, 0); } else if (NREV_GE(pi->pubpi.phy_rev, 8)) { wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 3), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID0); } } } else { mask = ((0x3 << 12) | (0x3 << 14)); mod_phy_reg(pi, 0xa6, mask, pi->tx_rx_cal_phy_saveregs[0]); mod_phy_reg(pi, 0xa7, mask, pi->tx_rx_cal_phy_saveregs[1]); write_phy_reg(pi, 0xa5, pi->tx_rx_cal_phy_saveregs[2]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, &pi->tx_rx_cal_phy_saveregs[3]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, &pi->tx_rx_cal_phy_saveregs[4]); write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[5]); write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[6]); } } void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, u8 num_samps) { u16 tssi_reg; s32 temp, pwrindex[2]; s32 idle_tssi[2]; s32 rssi_buf[4]; s32 tssival[2]; u8 tssi_type; tssi_reg = read_phy_reg(pi, 0x1e9); temp = (s32) (tssi_reg & 0x3f); idle_tssi[0] = (temp <= 31) ? temp : (temp - 64); temp = (s32) ((tssi_reg >> 8) & 0x3f); idle_tssi[1] = (temp <= 31) ? temp : (temp - 64); tssi_type = CHSPEC_IS5G(pi->radio_chanspec) ? (u8)NPHY_RSSI_SEL_TSSI_5G : (u8)NPHY_RSSI_SEL_TSSI_2G; wlc_phy_poll_rssi_nphy(pi, tssi_type, rssi_buf, num_samps); tssival[0] = rssi_buf[0] / ((s32) num_samps); tssival[1] = rssi_buf[2] / ((s32) num_samps); pwrindex[0] = idle_tssi[0] - tssival[0] + 64; pwrindex[1] = idle_tssi[1] - tssival[1] + 64; if (pwrindex[0] < 0) pwrindex[0] = 0; else if (pwrindex[0] > 63) pwrindex[0] = 63; if (pwrindex[1] < 0) pwrindex[1] = 0; else if (pwrindex[1] > 63) pwrindex[1] = 63; wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 1, (u32) pwrindex[0], 32, &qdBm_pwrbuf[0]); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 1, (u32) pwrindex[1], 32, &qdBm_pwrbuf[1]); } static void wlc_phy_update_txcal_ladder_nphy(struct brcms_phy *pi, u16 core) { int index; u32 bbmult_scale; u16 bbmult; u16 tblentry; struct nphy_txiqcal_ladder ladder_lo[] = { {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, {25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5}, {25, 6}, {25, 7}, {35, 7}, {50, 7}, {71, 7}, {100, 7} }; struct nphy_txiqcal_ladder ladder_iq[] = { {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, {25, 0}, {35, 0}, {50, 0}, {71, 0}, {100, 0}, {100, 1}, {100, 2}, {100, 3}, {100, 4}, {100, 5}, {100, 6}, {100, 7} }; bbmult = (core == PHY_CORE_0) ? ((pi->nphy_txcal_bbmult >> 8) & 0xff) : (pi->nphy_txcal_bbmult & 0xff); for (index = 0; index < 18; index++) { bbmult_scale = ladder_lo[index].percent * bbmult; bbmult_scale /= 100; tblentry = ((bbmult_scale & 0xff) << 8) | ladder_lo[index].g_env; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index, 16, &tblentry); bbmult_scale = ladder_iq[index].percent * bbmult; bbmult_scale /= 100; tblentry = ((bbmult_scale & 0xff) << 8) | ladder_iq[index].g_env; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index + 32, 16, &tblentry); } } static u8 wlc_phy_txpwr_idx_cur_get_nphy(struct brcms_phy *pi, u8 core) { u16 tmp; tmp = read_phy_reg(pi, ((core == PHY_CORE_0) ? 0x1ed : 0x1ee)); tmp = (tmp & (0x7f << 8)) >> 8; return (u8) tmp; } static void wlc_phy_txpwr_idx_cur_set_nphy(struct brcms_phy *pi, u8 idx0, u8 idx1) { mod_phy_reg(pi, 0x1e7, (0x7f << 0), idx0); if (NREV_GT(pi->pubpi.phy_rev, 1)) mod_phy_reg(pi, 0x222, (0xff << 0), idx1); } static u16 wlc_phy_ipa_get_bbmult_nphy(struct brcms_phy *pi) { u16 m0m1; wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1); return m0m1; } static void wlc_phy_ipa_set_bbmult_nphy(struct brcms_phy *pi, u8 m0, u8 m1) { u16 m0m1 = (u16) ((m0 << 8) | m1); wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m0m1); wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &m0m1); } static void wlc_phy_papd_cal_setup_nphy(struct brcms_phy *pi, struct nphy_papd_restore_state *state, u8 core) { s32 tone_freq; u8 off_core; u16 mixgain = 0; off_core = core ^ 0x1; if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), wlc_phy_read_lpf_bw_ctl_nphy (pi, 0), 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); if (CHSPEC_IS2G(pi->radio_chanspec)) { if (pi->pubpi.radiorev == 5) mixgain = (core == 0) ? 0x20 : 0x00; else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) mixgain = 0x00; else if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) mixgain = 0x00; } else { if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) mixgain = 0x50; else if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) mixgain = 0x0; } wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), mixgain, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_tx_pu, 1, (1 << core), 0); wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_tx_pu, 0, (1 << off_core), 0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0x3, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, (1 << core), 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7); state->afeoverride[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5); state->afectrl[off_core] = read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa7 : 0xa6); state->afeoverride[off_core] = read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa5 : 0x8f); mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7), (0x1 << 2), 0); mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : 0xa5), (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa7 : 0xa6), (0x1 << 2), (0x1 << 2)); mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa5 : 0x8f), (0x1 << 2), (0x1 << 2)); if (CHSPEC_IS2G(pi->radio_chanspec)) { state->pwrup[core] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_PWRUP); state->atten[core] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_ATTEN); state->pwrup[off_core] = READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_2G_PWRUP); state->atten[off_core] = READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_2G_ATTEN); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_PWRUP, 0xc); if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_ATTEN, 0xf0); else if (pi->pubpi.radiorev == 5) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_ATTEN, (core == 0) ? 0xf7 : 0xf2); else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_ATTEN, 0xf0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_2G_PWRUP, 0x0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_2G_ATTEN, 0xff); } else { state->pwrup[core] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_5G_PWRUP); state->atten[core] = READ_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_5G_ATTEN); state->pwrup[off_core] = READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_5G_PWRUP); state->atten[off_core] = READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_5G_ATTEN); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_5G_PWRUP, 0xc); if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_5G_ATTEN, 0xf4); else WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_5G_ATTEN, 0xf0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_5G_PWRUP, 0x0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, TXRXCOUPLE_5G_ATTEN, 0xff); } tone_freq = 4000; wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (1) << 13); mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_OFF) << 0); mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (0) << 13); } else { wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0, 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 1, 0x3, 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0x3, 0); state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7); state->afeoverride[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5); mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7), (0x1 << 0) | (0x1 << 1) | (0x1 << 2), 0); mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : 0xa5), (0x1 << 0) | (0x1 << 1) | (0x1 << 2), (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); state->vga_master[core] = READ_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER); WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, 0x2b); if (CHSPEC_IS2G(pi->radio_chanspec)) { state->fbmix[core] = READ_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_G); state->intpa_master[core] = READ_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_MASTER); WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_G, 0x03); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_MASTER, 0x04); } else { state->fbmix[core] = READ_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_A); state->intpa_master[core] = READ_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAA_MASTER); WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_A, 0x03); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAA_MASTER, 0x04); } tone_freq = 4000; wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (1) << 0); mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (0) << 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0); } } static void wlc_phy_papd_cal_cleanup_nphy(struct brcms_phy *pi, struct nphy_papd_restore_state *state) { u8 core; wlc_phy_stopplayback_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 7)) { for (core = 0; core < pi->pubpi.phy_corenum; core++) { if (CHSPEC_IS2G(pi->radio_chanspec)) { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_PWRUP, 0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_2G_ATTEN, state->atten[core]); } else { WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_5G_PWRUP, 0); WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TXRXCOUPLE_5G_ATTEN, state->atten[core]); } } if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 2), 1, 0x3, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); else wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 2), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 1, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, 0x3, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); for (core = 0; core < pi->pubpi.phy_corenum; core++) { write_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, state->afectrl[core]); write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5, state->afeoverride[core]); } wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff, (state->mm & 0xff)); if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), 0, 0, 1, NPHY_REV7_RFCTRLOVERRIDE_ID1); } else { wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 0x3, 1); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 1); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 0, 0x3, 1); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 0, 0x3, 1); for (core = 0; core < pi->pubpi.phy_corenum; core++) { WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, state->vga_master[core]); if (CHSPEC_IS2G(pi->radio_chanspec)) { WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_G, state->fbmix[core]); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAG_MASTER, state->intpa_master[core]); } else { WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_A, state->fbmix[core]); WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, INTPAA_MASTER, state->intpa_master[core]); } write_phy_reg(pi, (core == PHY_CORE_0) ? 0xa6 : 0xa7, state->afectrl[core]); write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5, state->afeoverride[core]); } wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff, (state->mm & 0xff)); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 1); } } static void wlc_phy_a1_nphy(struct brcms_phy *pi, u8 core, u32 winsz, u32 start, u32 end) { u32 *buf, *src, *dst, sz; sz = end - start + 1; buf = kmalloc(2 * sizeof(u32) * NPHY_PAPD_EPS_TBL_SIZE, GFP_ATOMIC); if (NULL == buf) return; src = buf; dst = buf + NPHY_PAPD_EPS_TBL_SIZE; wlc_phy_table_read_nphy(pi, (core == PHY_CORE_0 ? NPHY_TBL_ID_EPSILONTBL0 : NPHY_TBL_ID_EPSILONTBL1), NPHY_PAPD_EPS_TBL_SIZE, 0, 32, src); do { u32 phy_a1, phy_a2; s32 phy_a3, phy_a4, phy_a5, phy_a6, phy_a7; phy_a1 = end - min(end, (winsz >> 1)); phy_a2 = min_t(u32, NPHY_PAPD_EPS_TBL_SIZE - 1, end + (winsz >> 1)); phy_a3 = phy_a2 - phy_a1 + 1; phy_a6 = 0; phy_a7 = 0; do { wlc_phy_papd_decode_epsilon(src[phy_a2], &phy_a4, &phy_a5); phy_a6 += phy_a4; phy_a7 += phy_a5; } while (phy_a2-- != phy_a1); phy_a6 /= phy_a3; phy_a7 /= phy_a3; dst[end] = ((u32) phy_a7 << 13) | ((u32) phy_a6 & 0x1fff); } while (end-- != start); wlc_phy_table_write_nphy(pi, (core == PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 : NPHY_TBL_ID_EPSILONTBL1, sz, start, 32, dst); kfree(buf); } static void wlc_phy_a2_nphy(struct brcms_phy *pi, struct nphy_ipa_txcalgains *txgains, enum phy_cal_mode cal_mode, u8 core) { u16 phy_a1, phy_a2, phy_a3; u16 phy_a4, phy_a5; bool phy_a6; u8 phy_a7, m[2]; u32 phy_a8 = 0; struct nphy_txgains phy_a9; if (NREV_LT(pi->pubpi.phy_rev, 3)) return; phy_a7 = (core == PHY_CORE_0) ? 1 : 0; phy_a6 = ((cal_mode == CAL_GCTRL) || (cal_mode == CAL_SOFT)) ? true : false; if (NREV_GE(pi->pubpi.phy_rev, 7)) { phy_a9 = wlc_phy_get_tx_gain_nphy(pi); if (CHSPEC_IS2G(pi->radio_chanspec)) phy_a5 = ((phy_a9.txlpf[core] << 15) | (phy_a9.txgm[core] << 12) | (phy_a9.pga[core] << 8) | (txgains->gains.pad[core] << 3) | (phy_a9.ipa[core])); else phy_a5 = ((phy_a9.txlpf[core] << 15) | (phy_a9.txgm[core] << 12) | (txgains->gains.pga[core] << 8) | (phy_a9.pad[core] << 3) | (phy_a9.ipa[core])); wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_txgain, phy_a5, (1 << core), 0); if (CHSPEC_IS2G(pi->radio_chanspec)) { if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 60 : 79; else m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 45 : 64; } else { m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107; } m[phy_a7] = 0; wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]); phy_a2 = 63; if (CHSPEC_IS2G(pi->radio_chanspec)) { if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) { phy_a1 = 30; phy_a3 = 30; } else { phy_a1 = 25; phy_a3 = 25; } } else { if ((pi->pubpi.radiorev == 5) || (pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { phy_a1 = 25; phy_a3 = 25; } else { phy_a1 = 35; phy_a3 = 35; } } if (cal_mode == CAL_GCTRL) { if ((pi->pubpi.radiorev == 5) && (CHSPEC_IS2G(pi->radio_chanspec))) phy_a1 = 55; else if (((pi->pubpi.radiorev == 7) && (CHSPEC_IS2G(pi->radio_chanspec))) || ((pi->pubpi.radiorev == 8) && (CHSPEC_IS2G(pi->radio_chanspec)))) phy_a1 = 60; else phy_a1 = 63; } else if ((cal_mode != CAL_FULL) && (cal_mode != CAL_SOFT)) { phy_a1 = 35; phy_a3 = 35; } mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (1) << 0); mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (0) << 0); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (1) << 13); mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (0) << 13); write_phy_reg(pi, 0x2a1, 0x80); write_phy_reg(pi, 0x2a2, 0x100); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x7 << 4), (11) << 4); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x7 << 8), (11) << 8); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x7 << 0), (0x3) << 0); write_phy_reg(pi, 0x2e5, 0x20); mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0); mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0); mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, ((core == 0) ? 1 : 2), 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, ((core == 0) ? 2 : 1), 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); write_phy_reg(pi, 0x2be, 1); SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0x3, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_table_write_nphy(pi, (core == PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3, 32, &phy_a8); if (cal_mode != CAL_GCTRL) { if (CHSPEC_IS5G(pi->radio_chanspec)) wlc_phy_a1_nphy(pi, core, 5, 0, 35); } wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_txgain, phy_a5, (1 << core), 1); } else { if (txgains) { if (txgains->useindex) { phy_a4 = 15 - ((txgains->index) >> 3); if (CHSPEC_IS2G(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 6) && pi->sh->chip == BCMA_CHIP_ID_BCM47162) { phy_a5 = 0x10f7 | (phy_a4 << 8); } else if (NREV_GE(pi->pubpi.phy_rev, 6)) { phy_a5 = 0x00f7 | (phy_a4 << 8); } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { phy_a5 = 0x10f7 | (phy_a4 << 8); } else { phy_a5 = 0x50f7 | (phy_a4 << 8); } } else { phy_a5 = 0x70f7 | (phy_a4 << 8); } wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), phy_a5, (1 << core), 0); } else { wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0x5bf7, (1 << core), 0); } } if (CHSPEC_IS2G(pi->radio_chanspec)) m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 45 : 64; else m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107; m[phy_a7] = 0; wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]); phy_a2 = 63; if (cal_mode == CAL_FULL) { phy_a1 = 25; phy_a3 = 25; } else if (cal_mode == CAL_SOFT) { phy_a1 = 25; phy_a3 = 25; } else if (cal_mode == CAL_GCTRL) { phy_a1 = 63; phy_a3 = 25; } else { phy_a1 = 25; phy_a3 = 25; } mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (1) << 0); mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (0) << 0); if (NREV_GE(pi->pubpi.phy_rev, 6)) { mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (1) << 13); mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (0) << 13); write_phy_reg(pi, 0x2a1, 0x20); write_phy_reg(pi, 0x2a2, 0x60); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0xf << 4), (9) << 4); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0xf << 8), (9) << 8); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0xf << 0), (0x2) << 0); write_phy_reg(pi, 0x2e5, 0x20); } else { mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 11), (1) << 11); mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 11), (0) << 11); write_phy_reg(pi, 0x2a1, 0x80); write_phy_reg(pi, 0x2a2, 0x600); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x7 << 4), (0) << 4); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x7 << 8), (0) << 8); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x7 << 0), (0x3) << 0); mod_phy_reg(pi, 0x2a0, (0x3f << 8), (0x20) << 8); } mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0); mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0); mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0x3, 0); write_phy_reg(pi, 0x2be, 1); SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0); wlc_phy_table_write_nphy(pi, (core == PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3, 32, &phy_a8); if (cal_mode != CAL_GCTRL) wlc_phy_a1_nphy(pi, core, 5, 0, 40); } } static u8 wlc_phy_a3_nphy(struct brcms_phy *pi, u8 start_gain, u8 core) { int phy_a1; int phy_a2; bool phy_a3; struct nphy_ipa_txcalgains phy_a4; bool phy_a5 = false; bool phy_a6 = true; s32 phy_a7, phy_a8; u32 phy_a9; int phy_a10; bool phy_a11 = false; int phy_a12; u8 phy_a13 = 0; u8 phy_a14; u8 *phy_a15 = NULL; phy_a4.useindex = true; phy_a12 = start_gain; if (NREV_GE(pi->pubpi.phy_rev, 7)) { phy_a2 = 20; phy_a1 = 1; if (CHSPEC_IS2G(pi->radio_chanspec)) { if (pi->pubpi.radiorev == 5) { phy_a15 = pad_gain_codes_used_2057rev5; phy_a13 = ARRAY_SIZE(pad_gain_codes_used_2057rev5) - 1; } else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { phy_a15 = pad_gain_codes_used_2057rev7; phy_a13 = ARRAY_SIZE(pad_gain_codes_used_2057rev7) - 1; } else { phy_a15 = pad_all_gain_codes_2057; phy_a13 = ARRAY_SIZE(pad_all_gain_codes_2057) - 1; } } else { phy_a15 = pga_all_gain_codes_2057; phy_a13 = ARRAY_SIZE(pga_all_gain_codes_2057) - 1; } phy_a14 = 0; for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) { if (CHSPEC_IS2G(pi->radio_chanspec)) phy_a4.gains.pad[core] = (u16) phy_a15[phy_a12]; else phy_a4.gains.pga[core] = (u16) phy_a15[phy_a12]; wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core); wlc_phy_table_read_nphy(pi, (core == PHY_CORE_0 ? NPHY_TBL_ID_EPSILONTBL0 : NPHY_TBL_ID_EPSILONTBL1), 1, 63, 32, &phy_a9); wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8); phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) || (phy_a8 == 4095) || (phy_a8 == -4096)); if (!phy_a6 && (phy_a3 != phy_a5)) { if (!phy_a3) phy_a12 -= (u8) phy_a1; phy_a11 = true; break; } if (phy_a3) phy_a12 += (u8) phy_a1; else phy_a12 -= (u8) phy_a1; if ((phy_a12 < phy_a14) || (phy_a12 > phy_a13)) { if (phy_a12 < phy_a14) phy_a12 = phy_a14; else phy_a12 = phy_a13; phy_a11 = true; break; } phy_a6 = false; phy_a5 = phy_a3; } } else { phy_a2 = 10; phy_a1 = 8; for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) { phy_a4.index = (u8) phy_a12; wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core); wlc_phy_table_read_nphy(pi, (core == PHY_CORE_0 ? NPHY_TBL_ID_EPSILONTBL0 : NPHY_TBL_ID_EPSILONTBL1), 1, 63, 32, &phy_a9); wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8); phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) || (phy_a8 == 4095) || (phy_a8 == -4096)); if (!phy_a6 && (phy_a3 != phy_a5)) { if (!phy_a3) phy_a12 -= (u8) phy_a1; phy_a11 = true; break; } if (phy_a3) phy_a12 += (u8) phy_a1; else phy_a12 -= (u8) phy_a1; if ((phy_a12 < 0) || (phy_a12 > 127)) { if (phy_a12 < 0) phy_a12 = 0; else phy_a12 = 127; phy_a11 = true; break; } phy_a6 = false; phy_a5 = phy_a3; } } if (NREV_GE(pi->pubpi.phy_rev, 7)) return (u8) phy_a15[phy_a12]; else return (u8) phy_a12; } static void wlc_phy_a4(struct brcms_phy *pi, bool full_cal) { struct nphy_ipa_txcalgains phy_b1[2]; struct nphy_papd_restore_state phy_b2; bool phy_b3; u8 phy_b4; u8 phy_b5; s16 phy_b6, phy_b7, phy_b8; u16 phy_b9; s16 phy_b10, phy_b11, phy_b12; phy_b11 = 0; phy_b12 = 0; phy_b7 = 0; phy_b8 = 0; phy_b6 = 0; if (pi->nphy_papd_skip == 1) return; phy_b3 = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!phy_b3) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phy_stay_in_carriersearch_nphy(pi, true); pi->nphy_force_papd_cal = false; for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) pi->nphy_papd_tx_gain_at_last_cal[phy_b5] = wlc_phy_txpwr_idx_cur_get_nphy(pi, phy_b5); pi->nphy_papd_last_cal = pi->sh->now; pi->nphy_papd_recal_counter++; phy_b4 = pi->nphy_txpwrctrl; wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL0, 64, 0, 32, nphy_papd_scaltbl); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL1, 64, 0, 32, nphy_papd_scaltbl); phy_b9 = read_phy_reg(pi, 0x01); mod_phy_reg(pi, 0x01, (0x1 << 15), 0); for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { s32 i, val = 0; for (i = 0; i < 64; i++) wlc_phy_table_write_nphy(pi, ((phy_b5 == PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 : NPHY_TBL_ID_EPSILONTBL1), 1, i, 32, &val); } wlc_phy_ipa_restore_tx_digi_filts_nphy(pi); phy_b2.mm = wlc_phy_ipa_get_bbmult_nphy(pi); for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { wlc_phy_papd_cal_setup_nphy(pi, &phy_b2, phy_b5); if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (CHSPEC_IS2G(pi->radio_chanspec)) { if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) { pi->nphy_papd_cal_gain_index[phy_b5] = 23; } else if (pi->pubpi.radiorev == 5) { pi->nphy_papd_cal_gain_index[phy_b5] = 0; pi->nphy_papd_cal_gain_index[phy_b5] = wlc_phy_a3_nphy( pi, pi-> nphy_papd_cal_gain_index [phy_b5], phy_b5); } else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { pi->nphy_papd_cal_gain_index[phy_b5] = 0; pi->nphy_papd_cal_gain_index[phy_b5] = wlc_phy_a3_nphy( pi, pi-> nphy_papd_cal_gain_index [phy_b5], phy_b5); } phy_b1[phy_b5].gains.pad[phy_b5] = pi->nphy_papd_cal_gain_index[phy_b5]; } else { pi->nphy_papd_cal_gain_index[phy_b5] = 0; pi->nphy_papd_cal_gain_index[phy_b5] = wlc_phy_a3_nphy( pi, pi-> nphy_papd_cal_gain_index [phy_b5], phy_b5); phy_b1[phy_b5].gains.pga[phy_b5] = pi->nphy_papd_cal_gain_index[phy_b5]; } } else { phy_b1[phy_b5].useindex = true; phy_b1[phy_b5].index = 16; phy_b1[phy_b5].index = wlc_phy_a3_nphy(pi, phy_b1[phy_b5].index, phy_b5); pi->nphy_papd_cal_gain_index[phy_b5] = 15 - ((phy_b1[phy_b5].index) >> 3); } switch (pi->nphy_papd_cal_type) { case 0: wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_FULL, phy_b5); break; case 1: wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_SOFT, phy_b5); break; } if (NREV_GE(pi->pubpi.phy_rev, 7)) wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2); } if (NREV_LT(pi->pubpi.phy_rev, 7)) wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2); for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { int eps_offset = 0; if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (CHSPEC_IS2G(pi->radio_chanspec)) { if (pi->pubpi.radiorev == 3) eps_offset = -2; else if (pi->pubpi.radiorev == 5) eps_offset = 3; else eps_offset = -1; } else { eps_offset = 2; } if (CHSPEC_IS2G(pi->radio_chanspec)) { phy_b8 = phy_b1[phy_b5].gains.pad[phy_b5]; phy_b10 = 0; if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) { phy_b12 = -( nphy_papd_padgain_dlt_2g_2057rev3n4 [phy_b8] + 1) / 2; phy_b10 = -1; } else if (pi->pubpi.radiorev == 5) { phy_b12 = -( nphy_papd_padgain_dlt_2g_2057rev5 [phy_b8] + 1) / 2; } else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) { phy_b12 = -( nphy_papd_padgain_dlt_2g_2057rev7 [phy_b8] + 1) / 2; } } else { phy_b7 = phy_b1[phy_b5].gains.pga[phy_b5]; if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) phy_b11 = -(nphy_papd_pgagain_dlt_5g_2057 [phy_b7] + 1) / 2; else if ((pi->pubpi.radiorev == 7) || (pi->pubpi.radiorev == 8)) phy_b11 = -( nphy_papd_pgagain_dlt_5g_2057rev7 [phy_b7] + 1) / 2; phy_b10 = -9; } if (CHSPEC_IS2G(pi->radio_chanspec)) phy_b6 = -60 + 27 + eps_offset + phy_b12 + phy_b10; else phy_b6 = -60 + 27 + eps_offset + phy_b11 + phy_b10; mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 : 0x29c, (0x1ff << 7), (phy_b6) << 7); pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6; } else { if (NREV_LT(pi->pubpi.phy_rev, 5)) eps_offset = 4; else eps_offset = 2; phy_b7 = 15 - ((phy_b1[phy_b5].index) >> 3); if (CHSPEC_IS2G(pi->radio_chanspec)) { phy_b11 = -(nphy_papd_pga_gain_delta_ipa_2g[ phy_b7] + 1) / 2; phy_b10 = 0; } else { phy_b11 = -(nphy_papd_pga_gain_delta_ipa_5g[ phy_b7] + 1) / 2; phy_b10 = -9; } phy_b6 = -60 + 27 + eps_offset + phy_b11 + phy_b10; mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 : 0x29c, (0x1ff << 7), (phy_b6) << 7); pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6; } } mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); if (NREV_GE(pi->pubpi.phy_rev, 6)) { mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (0) << 13); mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 13), (0) << 13); } else { mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 11), (0) << 11); mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 : 0x2a4, (0x1 << 11), (0) << 11); } pi->nphy_papdcomp = NPHY_PAPD_COMP_ON; write_phy_reg(pi, 0x01, phy_b9); wlc_phy_ipa_set_tx_digi_filts_nphy(pi); wlc_phy_txpwrctrl_enable_nphy(pi, phy_b4); if (phy_b4 == PHY_TPC_HW_OFF) { wlc_phy_txpwr_index_nphy(pi, (1 << 0), (s8) (pi->nphy_txpwrindex[0]. index_internal), false); wlc_phy_txpwr_index_nphy(pi, (1 << 1), (s8) (pi->nphy_txpwrindex[1]. index_internal), false); } wlc_phy_stay_in_carriersearch_nphy(pi, false); if (!phy_b3) wlapi_enable_mac(pi->sh->physhim); } void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype) { struct nphy_txgains target_gain; u8 tx_pwr_ctrl_state; bool fullcal = true; bool restore_tx_gain = false; bool mphase; if (PHY_MUTED(pi)) return; if (caltype == PHY_PERICAL_AUTO) fullcal = (pi->radio_chanspec != pi->nphy_txiqlocal_chanspec); else if (caltype == PHY_PERICAL_PARTIAL) fullcal = false; if (pi->cal_type_override != PHY_PERICAL_AUTO) fullcal = (pi->cal_type_override == PHY_PERICAL_FULL) ? true : false; if ((pi->mphase_cal_phase_id > MPHASE_CAL_STATE_INIT)) { if (pi->nphy_txiqlocal_chanspec != pi->radio_chanspec) wlc_phy_cal_perical_mphase_restart(pi); } if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_RXCAL)) wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phyreg_enter((struct brcms_phy_pub *) pi); if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_IDLE) || (pi->mphase_cal_phase_id == MPHASE_CAL_STATE_INIT)) { pi->nphy_cal_orig_pwr_idx[0] = (u8) ((read_phy_reg(pi, 0x1ed) >> 8) & 0x7f); pi->nphy_cal_orig_pwr_idx[1] = (u8) ((read_phy_reg(pi, 0x1ee) >> 8) & 0x7f); if (pi->nphy_txpwrctrl != PHY_TPC_HW_OFF) { wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, pi->nphy_cal_orig_tx_gain); } else { pi->nphy_cal_orig_tx_gain[0] = 0; pi->nphy_cal_orig_tx_gain[1] = 0; } } target_gain = wlc_phy_get_tx_gain_nphy(pi); tx_pwr_ctrl_state = pi->nphy_txpwrctrl; wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); if (pi->antsel_type == ANTSEL_2x3) wlc_phy_antsel_init((struct brcms_phy_pub *) pi, true); mphase = (pi->mphase_cal_phase_id != MPHASE_CAL_STATE_IDLE); if (!mphase) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { wlc_phy_precal_txgain_nphy(pi); pi->nphy_cal_target_gain = wlc_phy_get_tx_gain_nphy(pi); restore_tx_gain = true; target_gain = pi->nphy_cal_target_gain; } if (0 == wlc_phy_cal_txiqlo_nphy(pi, target_gain, fullcal, mphase)) { if (PHY_IPA(pi)) wlc_phy_a4(pi, true); wlc_phyreg_exit((struct brcms_phy_pub *) pi); wlapi_enable_mac(pi->sh->physhim); wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phyreg_enter((struct brcms_phy_pub *) pi); if (0 == wlc_phy_cal_rxiq_nphy(pi, target_gain, (pi->first_cal_after_assoc || (pi->cal_type_override == PHY_PERICAL_FULL)) ? 2 : 0, false)) { wlc_phy_savecal_nphy(pi); wlc_phy_txpwrctrl_coeff_setup_nphy(pi); pi->nphy_perical_last = pi->sh->now; } } if (caltype != PHY_PERICAL_AUTO) wlc_phy_rssi_cal_nphy(pi); if (pi->first_cal_after_assoc || (pi->cal_type_override == PHY_PERICAL_FULL)) { pi->first_cal_after_assoc = false; wlc_phy_txpwrctrl_idle_tssi_nphy(pi); wlc_phy_txpwrctrl_pwr_setup_nphy(pi); } if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_radio205x_vcocal_nphy(pi); } else { switch (pi->mphase_cal_phase_id) { case MPHASE_CAL_STATE_INIT: pi->nphy_perical_last = pi->sh->now; pi->nphy_txiqlocal_chanspec = pi->radio_chanspec; if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_precal_txgain_nphy(pi); pi->nphy_cal_target_gain = wlc_phy_get_tx_gain_nphy(pi); pi->mphase_cal_phase_id++; break; case MPHASE_CAL_STATE_TXPHASE0: case MPHASE_CAL_STATE_TXPHASE1: case MPHASE_CAL_STATE_TXPHASE2: case MPHASE_CAL_STATE_TXPHASE3: case MPHASE_CAL_STATE_TXPHASE4: case MPHASE_CAL_STATE_TXPHASE5: if ((pi->radar_percal_mask & 0x10) != 0) pi->nphy_rxcal_active = true; if (wlc_phy_cal_txiqlo_nphy (pi, pi->nphy_cal_target_gain, fullcal, true) != 0) { wlc_phy_cal_perical_mphase_reset(pi); break; } if (NREV_LE(pi->pubpi.phy_rev, 2) && (pi->mphase_cal_phase_id == MPHASE_CAL_STATE_TXPHASE4)) pi->mphase_cal_phase_id += 2; else pi->mphase_cal_phase_id++; break; case MPHASE_CAL_STATE_PAPDCAL: if ((pi->radar_percal_mask & 0x2) != 0) pi->nphy_rxcal_active = true; if (PHY_IPA(pi)) wlc_phy_a4(pi, true); pi->mphase_cal_phase_id++; break; case MPHASE_CAL_STATE_RXCAL: if ((pi->radar_percal_mask & 0x1) != 0) pi->nphy_rxcal_active = true; if (wlc_phy_cal_rxiq_nphy(pi, target_gain, (pi->first_cal_after_assoc || (pi->cal_type_override == PHY_PERICAL_FULL)) ? 2 : 0, false) == 0) wlc_phy_savecal_nphy(pi); pi->mphase_cal_phase_id++; break; case MPHASE_CAL_STATE_RSSICAL: if ((pi->radar_percal_mask & 0x4) != 0) pi->nphy_rxcal_active = true; wlc_phy_txpwrctrl_coeff_setup_nphy(pi); wlc_phy_rssi_cal_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_radio205x_vcocal_nphy(pi); restore_tx_gain = true; if (pi->first_cal_after_assoc) pi->mphase_cal_phase_id++; else wlc_phy_cal_perical_mphase_reset(pi); break; case MPHASE_CAL_STATE_IDLETSSI: if ((pi->radar_percal_mask & 0x8) != 0) pi->nphy_rxcal_active = true; if (pi->first_cal_after_assoc) { pi->first_cal_after_assoc = false; wlc_phy_txpwrctrl_idle_tssi_nphy(pi); wlc_phy_txpwrctrl_pwr_setup_nphy(pi); } wlc_phy_cal_perical_mphase_reset(pi); break; default: wlc_phy_cal_perical_mphase_reset(pi); break; } } if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (restore_tx_gain) { if (tx_pwr_ctrl_state != PHY_TPC_HW_OFF) { wlc_phy_txpwr_index_nphy(pi, 1, pi-> nphy_cal_orig_pwr_idx [0], false); wlc_phy_txpwr_index_nphy(pi, 2, pi-> nphy_cal_orig_pwr_idx [1], false); pi->nphy_txpwrindex[0].index = -1; pi->nphy_txpwrindex[1].index = -1; } else { wlc_phy_txpwr_index_nphy(pi, (1 << 0), (s8) (pi-> nphy_txpwrindex [0]. index_internal), false); wlc_phy_txpwr_index_nphy(pi, (1 << 1), (s8) (pi-> nphy_txpwrindex [1]. index_internal), false); } } } wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); wlc_phyreg_exit((struct brcms_phy_pub *) pi); wlapi_enable_mac(pi->sh->physhim); } int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, bool fullcal, bool mphase) { u16 val; u16 tbl_buf[11]; u8 cal_cnt; u16 cal_cmd; u8 num_cals, max_cal_cmds; u16 core_no, cal_type; u16 diq_start = 0; u8 phy_bw; u16 max_val; u16 tone_freq; u16 gain_save[2]; u16 cal_gain[2]; struct nphy_iqcal_params cal_params[2]; u32 tbl_len; void *tbl_ptr; bool ladder_updated[2]; u8 mphase_cal_lastphase = 0; int bcmerror = 0; bool phyhang_avoid_state = false; u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { 0x0300, 0x0500, 0x0700, 0x0900, 0x0d00, 0x1100, 0x1900, 0x1901, 0x1902, 0x1903, 0x1904, 0x1905, 0x1906, 0x1907, 0x2407, 0x3207, 0x4607, 0x6407 }; u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { 0x0200, 0x0300, 0x0600, 0x0900, 0x0d00, 0x1100, 0x1900, 0x2400, 0x3200, 0x4600, 0x6400, 0x6401, 0x6402, 0x6403, 0x6404, 0x6405, 0x6406, 0x6407 }; u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { 0x0200, 0x0300, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1201, 0x1202, 0x1203, 0x1204, 0x1205, 0x1206, 0x1207, 0x1907, 0x2307, 0x3207, 0x4707 }; u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { 0x0100, 0x0200, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1900, 0x2300, 0x3200, 0x4700, 0x4701, 0x4702, 0x4703, 0x4704, 0x4705, 0x4706, 0x4707 }; u16 tbl_tx_iqlo_cal_startcoefs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { 0x8123, 0x8264, 0x8086, 0x8245, 0x8056, 0x9123, 0x9264, 0x9086, 0x9245, 0x9056 }; u16 tbl_tx_iqlo_cal_cmds_recal[] = { 0x8101, 0x8253, 0x8053, 0x8234, 0x8034, 0x9101, 0x9253, 0x9053, 0x9234, 0x9034 }; u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234, 0x9434, 0x9334, 0x9084, 0x9267, 0x9056, 0x9234 }; u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { 0x8423, 0x8323, 0x8073, 0x8256, 0x8045, 0x8223, 0x9423, 0x9323, 0x9073, 0x9256, 0x9045, 0x9223 }; wlc_phy_stay_in_carriersearch_nphy(pi, true); if (NREV_GE(pi->pubpi.phy_rev, 4)) { phyhang_avoid_state = pi->phyhang_avoid; pi->phyhang_avoid = false; } if (CHSPEC_IS40(pi->radio_chanspec)) phy_bw = 40; else phy_bw = 20; wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); for (core_no = 0; core_no <= 1; core_no++) { wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, &cal_params[core_no]); cal_gain[core_no] = cal_params[core_no].cal_gain; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); wlc_phy_txcal_radio_setup_nphy(pi); wlc_phy_txcal_physetup_nphy(pi); ladder_updated[0] = ladder_updated[1] = false; if (!(NREV_GE(pi->pubpi.phy_rev, 6) || (NREV_IS(pi->pubpi.phy_rev, 5) && PHY_IPA(pi) && (CHSPEC_IS2G(pi->radio_chanspec))))) { if (phy_bw == 40) { tbl_ptr = tbl_tx_iqlo_cal_loft_ladder_40; tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_loft_ladder_40); } else { tbl_ptr = tbl_tx_iqlo_cal_loft_ladder_20; tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_loft_ladder_20); } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 0, 16, tbl_ptr); if (phy_bw == 40) { tbl_ptr = tbl_tx_iqlo_cal_iqimb_ladder_40; tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_iqimb_ladder_40); } else { tbl_ptr = tbl_tx_iqlo_cal_iqimb_ladder_20; tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_iqimb_ladder_20); } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 32, 16, tbl_ptr); } if (NREV_GE(pi->pubpi.phy_rev, 7)) write_phy_reg(pi, 0xc2, 0x8ad9); else write_phy_reg(pi, 0xc2, 0x8aa9); max_val = 250; tone_freq = (phy_bw == 20) ? 2500 : 5000; if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_TXPHASE0) { wlc_phy_runsamples_nphy(pi, phy_bw * 8, 0xffff, 0, 1, 0, false); bcmerror = 0; } else { bcmerror = wlc_phy_tx_tone_nphy(pi, tone_freq, max_val, 1, 0, false); } if (bcmerror == 0) { if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_TXPHASE0) { tbl_ptr = pi->mphase_txcal_bestcoeffs; tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs); if (NREV_LT(pi->pubpi.phy_rev, 3)) tbl_len -= 2; } else { if ((!fullcal) && (pi->nphy_txiqlocal_coeffsvalid)) { tbl_ptr = pi->nphy_txiqlocal_bestc; tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc); if (NREV_LT(pi->pubpi.phy_rev, 3)) tbl_len -= 2; } else { fullcal = true; if (NREV_GE(pi->pubpi.phy_rev, 3)) { tbl_ptr = tbl_tx_iqlo_cal_startcoefs_nphyrev3; tbl_len = ARRAY_SIZE( tbl_tx_iqlo_cal_startcoefs_nphyrev3); } else { tbl_ptr = tbl_tx_iqlo_cal_startcoefs; tbl_len = ARRAY_SIZE( tbl_tx_iqlo_cal_startcoefs); } } } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 64, 16, tbl_ptr); if (fullcal) { max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ? ARRAY_SIZE( tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3) : ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_fullcal); } else { max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ? ARRAY_SIZE( tbl_tx_iqlo_cal_cmds_recal_nphyrev3) : ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_recal); } if (mphase) { cal_cnt = pi->mphase_txcal_cmdidx; if ((cal_cnt + pi->mphase_txcal_numcmds) < max_cal_cmds) num_cals = cal_cnt + pi->mphase_txcal_numcmds; else num_cals = max_cal_cmds; } else { cal_cnt = 0; num_cals = max_cal_cmds; } for (; cal_cnt < num_cals; cal_cnt++) { if (fullcal) { cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ? tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3 [cal_cnt] : tbl_tx_iqlo_cal_cmds_fullcal[cal_cnt]; } else { cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ? tbl_tx_iqlo_cal_cmds_recal_nphyrev3[ cal_cnt] : tbl_tx_iqlo_cal_cmds_recal[cal_cnt]; } core_no = ((cal_cmd & 0x3000) >> 12); cal_type = ((cal_cmd & 0x0F00) >> 8); if (NREV_GE(pi->pubpi.phy_rev, 6) || (NREV_IS(pi->pubpi.phy_rev, 5) && PHY_IPA(pi) && (CHSPEC_IS2G(pi->radio_chanspec)))) { if (!ladder_updated[core_no]) { wlc_phy_update_txcal_ladder_nphy( pi, core_no); ladder_updated[core_no] = true; } } val = (cal_params[core_no]. ncorr[cal_type] << 8) | NPHY_N_GCTL; write_phy_reg(pi, 0xc1, val); if ((cal_type == 1) || (cal_type == 3) || (cal_type == 4)) { wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 69 + core_no, 16, tbl_buf); diq_start = tbl_buf[0]; tbl_buf[0] = 0; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 69 + core_no, 16, tbl_buf); } write_phy_reg(pi, 0xc0, cal_cmd); SPINWAIT(((read_phy_reg(pi, 0xc0) & 0xc000) != 0), 20000); if (WARN(read_phy_reg(pi, 0xc0) & 0xc000, "HW error: txiq calib")) return -EIO; wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 96, 16, tbl_buf); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 64, 16, tbl_buf); if ((cal_type == 1) || (cal_type == 3) || (cal_type == 4)) { tbl_buf[0] = diq_start; } } if (mphase) { pi->mphase_txcal_cmdidx = num_cals; if (pi->mphase_txcal_cmdidx >= max_cal_cmds) pi->mphase_txcal_cmdidx = 0; } mphase_cal_lastphase = (NREV_LE(pi->pubpi.phy_rev, 2)) ? MPHASE_CAL_STATE_TXPHASE4 : MPHASE_CAL_STATE_TXPHASE5; if (!mphase || (pi->mphase_cal_phase_id == mphase_cal_lastphase)) { wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 96, 16, tbl_buf); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, 16, tbl_buf); if (NREV_LT(pi->pubpi.phy_rev, 2)) { tbl_buf[0] = 0; tbl_buf[1] = 0; tbl_buf[2] = 0; tbl_buf[3] = 0; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, 16, tbl_buf); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 101, 16, tbl_buf); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, 16, tbl_buf); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, 16, tbl_buf); tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc); if (NREV_LT(pi->pubpi.phy_rev, 3)) tbl_len -= 2; wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 96, 16, pi->nphy_txiqlocal_bestc); pi->nphy_txiqlocal_coeffsvalid = true; pi->nphy_txiqlocal_chanspec = pi->radio_chanspec; } else { tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs); if (NREV_LT(pi->pubpi.phy_rev, 3)) tbl_len -= 2; wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 96, 16, pi->mphase_txcal_bestcoeffs); } wlc_phy_stopplayback_nphy(pi); write_phy_reg(pi, 0xc2, 0x0000); } wlc_phy_txcal_phycleanup_nphy(pi); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); wlc_phy_txcal_radio_cleanup_nphy(pi); if (NREV_LT(pi->pubpi.phy_rev, 2)) { if (!mphase || (pi->mphase_cal_phase_id == mphase_cal_lastphase)) wlc_phy_tx_iq_war_nphy(pi); } if (NREV_GE(pi->pubpi.phy_rev, 4)) pi->phyhang_avoid = phyhang_avoid_state; wlc_phy_stay_in_carriersearch_nphy(pi, false); return bcmerror; } static void wlc_phy_reapply_txcal_coeffs_nphy(struct brcms_phy *pi) { u16 tbl_buf[7]; if ((pi->nphy_txiqlocal_chanspec == pi->radio_chanspec) && (pi->nphy_txiqlocal_coeffsvalid)) { wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, ARRAY_SIZE(tbl_buf), 80, 16, tbl_buf); if ((pi->nphy_txiqlocal_bestc[0] != tbl_buf[0]) || (pi->nphy_txiqlocal_bestc[1] != tbl_buf[1]) || (pi->nphy_txiqlocal_bestc[2] != tbl_buf[2]) || (pi->nphy_txiqlocal_bestc[3] != tbl_buf[3])) { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, 16, pi->nphy_txiqlocal_bestc); tbl_buf[0] = 0; tbl_buf[1] = 0; tbl_buf[2] = 0; tbl_buf[3] = 0; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, 16, tbl_buf); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, 16, &pi->nphy_txiqlocal_bestc[5]); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, 16, &pi->nphy_txiqlocal_bestc[5]); } } } void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, struct nphy_iq_comp *pcomp) { if (write) { write_phy_reg(pi, 0x9a, pcomp->a0); write_phy_reg(pi, 0x9b, pcomp->b0); write_phy_reg(pi, 0x9c, pcomp->a1); write_phy_reg(pi, 0x9d, pcomp->b1); } else { pcomp->a0 = read_phy_reg(pi, 0x9a); pcomp->b0 = read_phy_reg(pi, 0x9b); pcomp->a1 = read_phy_reg(pi, 0x9c); pcomp->b1 = read_phy_reg(pi, 0x9d); } } void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, u16 num_samps, u8 wait_time, u8 wait_for_crs) { u8 core; write_phy_reg(pi, 0x12b, num_samps); mod_phy_reg(pi, 0x12a, (0xff << 0), (wait_time << 0)); mod_phy_reg(pi, 0x129, NPHY_IqestCmd_iqMode, (wait_for_crs) ? NPHY_IqestCmd_iqMode : 0); mod_phy_reg(pi, 0x129, NPHY_IqestCmd_iqstart, NPHY_IqestCmd_iqstart); SPINWAIT(((read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart) != 0), 10000); if (WARN(read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart, "HW error: rxiq est")) return; if ((read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart) == 0) { for (core = 0; core < pi->pubpi.phy_corenum; core++) { est[core].i_pwr = (read_phy_reg(pi, NPHY_IqestipwrAccHi(core)) << 16) | read_phy_reg(pi, NPHY_IqestipwrAccLo(core)); est[core].q_pwr = (read_phy_reg(pi, NPHY_IqestqpwrAccHi(core)) << 16) | read_phy_reg(pi, NPHY_IqestqpwrAccLo(core)); est[core].iq_prod = (read_phy_reg(pi, NPHY_IqestIqAccHi(core)) << 16) | read_phy_reg(pi, NPHY_IqestIqAccLo(core)); } } } #define CAL_RETRY_CNT 2 static void wlc_phy_calc_rx_iq_comp_nphy(struct brcms_phy *pi, u8 core_mask) { u8 curr_core; struct phy_iq_est est[PHY_CORE_MAX]; struct nphy_iq_comp old_comp, new_comp; s32 iq = 0; u32 ii = 0, qq = 0; s16 iq_nbits, qq_nbits, brsh, arsh; s32 a, b, temp; int bcmerror = 0; uint cal_retry = 0; if (core_mask == 0x0) return; wlc_phy_rx_iq_coeffs_nphy(pi, 0, &old_comp); new_comp.a0 = new_comp.b0 = new_comp.a1 = new_comp.b1 = 0x0; wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp); cal_try: wlc_phy_rx_iq_est_nphy(pi, est, 0x4000, 32, 0); new_comp = old_comp; for (curr_core = 0; curr_core < pi->pubpi.phy_corenum; curr_core++) { if ((curr_core == PHY_CORE_0) && (core_mask & 0x1)) { iq = est[curr_core].iq_prod; ii = est[curr_core].i_pwr; qq = est[curr_core].q_pwr; } else if ((curr_core == PHY_CORE_1) && (core_mask & 0x2)) { iq = est[curr_core].iq_prod; ii = est[curr_core].i_pwr; qq = est[curr_core].q_pwr; } else { continue; } if ((ii + qq) < NPHY_MIN_RXIQ_PWR) { bcmerror = -EBADE; break; } iq_nbits = wlc_phy_nbits(iq); qq_nbits = wlc_phy_nbits(qq); arsh = 10 - (30 - iq_nbits); if (arsh >= 0) { a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); temp = (s32) (ii >> arsh); if (temp == 0) { bcmerror = -EBADE; break; } } else { a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); temp = (s32) (ii << -arsh); if (temp == 0) { bcmerror = -EBADE; break; } } a /= temp; brsh = qq_nbits - 31 + 20; if (brsh >= 0) { b = (qq << (31 - qq_nbits)); temp = (s32) (ii >> brsh); if (temp == 0) { bcmerror = -EBADE; break; } } else { b = (qq << (31 - qq_nbits)); temp = (s32) (ii << -brsh); if (temp == 0) { bcmerror = -EBADE; break; } } b /= temp; b -= a * a; b = (s32) int_sqrt((unsigned long) b); b -= (1 << 10); if ((curr_core == PHY_CORE_0) && (core_mask & 0x1)) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { new_comp.a0 = (s16) a & 0x3ff; new_comp.b0 = (s16) b & 0x3ff; } else { new_comp.a0 = (s16) b & 0x3ff; new_comp.b0 = (s16) a & 0x3ff; } } if ((curr_core == PHY_CORE_1) && (core_mask & 0x2)) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { new_comp.a1 = (s16) a & 0x3ff; new_comp.b1 = (s16) b & 0x3ff; } else { new_comp.a1 = (s16) b & 0x3ff; new_comp.b1 = (s16) a & 0x3ff; } } } if (bcmerror != 0) { pr_debug("%s: Failed, cnt = %d\n", __func__, cal_retry); if (cal_retry < CAL_RETRY_CNT) { cal_retry++; goto cal_try; } new_comp = old_comp; } wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp); } static void wlc_phy_rxcal_radio_setup_nphy(struct brcms_phy *pi, u8 rx_core) { u16 offtune_val; u16 bias_g = 0; u16 bias_a = 0; if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (rx_core == PHY_CORE_0) { if (CHSPEC_IS5G(pi->radio_chanspec)) { pi->tx_rx_cal_radio_saveregs[0] = read_radio_reg(pi, RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP); pi->tx_rx_cal_radio_saveregs[1] = read_radio_reg(pi, RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN); write_radio_reg(pi, RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP, 0x3); write_radio_reg(pi, RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN, 0xaf); } else { pi->tx_rx_cal_radio_saveregs[0] = read_radio_reg(pi, RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP); pi->tx_rx_cal_radio_saveregs[1] = read_radio_reg(pi, RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN); write_radio_reg( pi, RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP, 0x3); write_radio_reg( pi, RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN, 0x7f); } } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { pi->tx_rx_cal_radio_saveregs[0] = read_radio_reg(pi, RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP); pi->tx_rx_cal_radio_saveregs[1] = read_radio_reg(pi, RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN); write_radio_reg( pi, RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP, 0x3); write_radio_reg( pi, RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN, 0xaf); } else { pi->tx_rx_cal_radio_saveregs[0] = read_radio_reg(pi, RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP); pi->tx_rx_cal_radio_saveregs[1] = read_radio_reg(pi, RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN); write_radio_reg(pi, RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP, 0x3); write_radio_reg(pi, RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN, 0x7f); } } } else { if (rx_core == PHY_CORE_0) { pi->tx_rx_cal_radio_saveregs[0] = read_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX1); pi->tx_rx_cal_radio_saveregs[1] = read_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX0); if (pi->pubpi.radiorev >= 5) { pi->tx_rx_cal_radio_saveregs[2] = read_radio_reg(pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX0); pi->tx_rx_cal_radio_saveregs[3] = read_radio_reg(pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX1); } if (CHSPEC_IS5G(pi->radio_chanspec)) { if (pi->pubpi.radiorev >= 5) { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg(pi, RADIO_2056_RX_LNAA_MASTER | RADIO_2056_RX0); write_radio_reg( pi, RADIO_2056_RX_LNAA_MASTER | RADIO_2056_RX0, 0x40); write_radio_reg(pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX1, bias_a); write_radio_reg(pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX0, bias_a); } else { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg(pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX0); offtune_val = (pi->tx_rx_cal_radio_saveregs [2] & 0xF0) >> 8; offtune_val = (offtune_val <= 0x7) ? 0xF : 0; mod_radio_reg(pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX0, 0xF0, (offtune_val << 8)); } write_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX1, 0x9); write_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX0, 0x9); } else { if (pi->pubpi.radiorev >= 5) { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg( pi, RADIO_2056_RX_LNAG_MASTER | RADIO_2056_RX0); write_radio_reg( pi, RADIO_2056_RX_LNAG_MASTER | RADIO_2056_RX0, 0x40); write_radio_reg( pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX1, bias_g); write_radio_reg( pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX0, bias_g); } else { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg( pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX0); offtune_val = (pi-> tx_rx_cal_radio_saveregs[2] & 0xF0) >> 8; offtune_val = (offtune_val <= 0x7) ? 0xF : 0; mod_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX0, 0xF0, (offtune_val << 8)); } write_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX1, 0x6); write_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX0, 0x6); } } else { pi->tx_rx_cal_radio_saveregs[0] = read_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX0); pi->tx_rx_cal_radio_saveregs[1] = read_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX1); if (pi->pubpi.radiorev >= 5) { pi->tx_rx_cal_radio_saveregs[2] = read_radio_reg(pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX1); pi->tx_rx_cal_radio_saveregs[3] = read_radio_reg(pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX0); } if (CHSPEC_IS5G(pi->radio_chanspec)) { if (pi->pubpi.radiorev >= 5) { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg( pi, RADIO_2056_RX_LNAA_MASTER | RADIO_2056_RX1); write_radio_reg( pi, RADIO_2056_RX_LNAA_MASTER | RADIO_2056_RX1, 0x40); write_radio_reg( pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX0, bias_a); write_radio_reg( pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX1, bias_a); } else { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg( pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX1); offtune_val = (pi-> tx_rx_cal_radio_saveregs[2] & 0xF0) >> 8; offtune_val = (offtune_val <= 0x7) ? 0xF : 0; mod_radio_reg(pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX1, 0xF0, (offtune_val << 8)); } write_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX0, 0x9); write_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX1, 0x9); } else { if (pi->pubpi.radiorev >= 5) { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg( pi, RADIO_2056_RX_LNAG_MASTER | RADIO_2056_RX1); write_radio_reg( pi, RADIO_2056_RX_LNAG_MASTER | RADIO_2056_RX1, 0x40); write_radio_reg( pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX0, bias_g); write_radio_reg( pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX1, bias_g); } else { pi->tx_rx_cal_radio_saveregs[4] = read_radio_reg( pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX1); offtune_val = (pi-> tx_rx_cal_radio_saveregs[2] & 0xF0) >> 8; offtune_val = (offtune_val <= 0x7) ? 0xF : 0; mod_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX1, 0xF0, (offtune_val << 8)); } write_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX0, 0x6); write_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX1, 0x6); } } } } static void wlc_phy_rxcal_radio_cleanup_nphy(struct brcms_phy *pi, u8 rx_core) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { if (rx_core == PHY_CORE_0) { if (CHSPEC_IS5G(pi->radio_chanspec)) { write_radio_reg( pi, RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP, pi-> tx_rx_cal_radio_saveregs[0]); write_radio_reg( pi, RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN, pi-> tx_rx_cal_radio_saveregs[1]); } else { write_radio_reg( pi, RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP, pi-> tx_rx_cal_radio_saveregs[0]); write_radio_reg( pi, RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN, pi-> tx_rx_cal_radio_saveregs[1]); } } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { write_radio_reg( pi, RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP, pi-> tx_rx_cal_radio_saveregs[0]); write_radio_reg( pi, RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN, pi-> tx_rx_cal_radio_saveregs[1]); } else { write_radio_reg( pi, RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP, pi-> tx_rx_cal_radio_saveregs[0]); write_radio_reg( pi, RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN, pi-> tx_rx_cal_radio_saveregs[1]); } } } else { if (rx_core == PHY_CORE_0) { write_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX1, pi->tx_rx_cal_radio_saveregs[0]); write_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX0, pi->tx_rx_cal_radio_saveregs[1]); if (pi->pubpi.radiorev >= 5) { write_radio_reg(pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX0, pi-> tx_rx_cal_radio_saveregs[2]); write_radio_reg(pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX1, pi-> tx_rx_cal_radio_saveregs[3]); } if (CHSPEC_IS5G(pi->radio_chanspec)) { if (pi->pubpi.radiorev >= 5) write_radio_reg( pi, RADIO_2056_RX_LNAA_MASTER | RADIO_2056_RX0, pi-> tx_rx_cal_radio_saveregs [4]); else write_radio_reg( pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX0, pi-> tx_rx_cal_radio_saveregs [4]); } else { if (pi->pubpi.radiorev >= 5) write_radio_reg( pi, RADIO_2056_RX_LNAG_MASTER | RADIO_2056_RX0, pi-> tx_rx_cal_radio_saveregs [4]); else write_radio_reg( pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX0, pi-> tx_rx_cal_radio_saveregs [4]); } } else { write_radio_reg(pi, RADIO_2056_TX_RXIQCAL_TXMUX | RADIO_2056_TX0, pi->tx_rx_cal_radio_saveregs[0]); write_radio_reg(pi, RADIO_2056_RX_RXIQCAL_RXMUX | RADIO_2056_RX1, pi->tx_rx_cal_radio_saveregs[1]); if (pi->pubpi.radiorev >= 5) { write_radio_reg(pi, RADIO_2056_RX_RXSPARE2 | RADIO_2056_RX1, pi-> tx_rx_cal_radio_saveregs[2]); write_radio_reg(pi, RADIO_2056_TX_TXSPARE2 | RADIO_2056_TX0, pi-> tx_rx_cal_radio_saveregs[3]); } if (CHSPEC_IS5G(pi->radio_chanspec)) { if (pi->pubpi.radiorev >= 5) write_radio_reg( pi, RADIO_2056_RX_LNAA_MASTER | RADIO_2056_RX1, pi-> tx_rx_cal_radio_saveregs [4]); else write_radio_reg( pi, RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX1, pi-> tx_rx_cal_radio_saveregs [4]); } else { if (pi->pubpi.radiorev >= 5) write_radio_reg( pi, RADIO_2056_RX_LNAG_MASTER | RADIO_2056_RX1, pi-> tx_rx_cal_radio_saveregs [4]); else write_radio_reg( pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX1, pi-> tx_rx_cal_radio_saveregs [4]); } } } } static void wlc_phy_rxcal_physetup_nphy(struct brcms_phy *pi, u8 rx_core) { u8 tx_core; u16 rx_antval, tx_antval; if (NREV_GE(pi->pubpi.phy_rev, 7)) tx_core = rx_core; else tx_core = (rx_core == PHY_CORE_0) ? 1 : 0; pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa2); pi->tx_rx_cal_phy_saveregs[1] = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7); pi->tx_rx_cal_phy_saveregs[2] = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5); pi->tx_rx_cal_phy_saveregs[3] = read_phy_reg(pi, 0x91); pi->tx_rx_cal_phy_saveregs[4] = read_phy_reg(pi, 0x92); pi->tx_rx_cal_phy_saveregs[5] = read_phy_reg(pi, 0x7a); pi->tx_rx_cal_phy_saveregs[6] = read_phy_reg(pi, 0x7d); pi->tx_rx_cal_phy_saveregs[7] = read_phy_reg(pi, 0xe7); pi->tx_rx_cal_phy_saveregs[8] = read_phy_reg(pi, 0xec); if (NREV_GE(pi->pubpi.phy_rev, 7)) { pi->tx_rx_cal_phy_saveregs[11] = read_phy_reg(pi, 0x342); pi->tx_rx_cal_phy_saveregs[12] = read_phy_reg(pi, 0x343); pi->tx_rx_cal_phy_saveregs[13] = read_phy_reg(pi, 0x346); pi->tx_rx_cal_phy_saveregs[14] = read_phy_reg(pi, 0x347); } pi->tx_rx_cal_phy_saveregs[9] = read_phy_reg(pi, 0x297); pi->tx_rx_cal_phy_saveregs[10] = read_phy_reg(pi, 0x29b); mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (0) << 0); mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 0), (0) << 0); if (NREV_GE(pi->pubpi.phy_rev, 7)) { mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << (1 - rx_core)) << 12); } else { mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << tx_core) << 12); mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); mod_phy_reg(pi, 0xa2, (0xf << 4), (1 << rx_core) << 4); mod_phy_reg(pi, 0xa2, (0xf << 8), (1 << rx_core) << 8); } mod_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), (0x1 << 2), 0); mod_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5, (0x1 << 2), (0x1 << 2)); if (NREV_LT(pi->pubpi.phy_rev, 7)) { mod_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), (0x1 << 0) | (0x1 << 1), 0); mod_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5, (0x1 << 0) | (0x1 << 1), (0x1 << 0) | (0x1 << 1)); } wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 0, RADIO_MIMO_CORESEL_CORE1 | RADIO_MIMO_CORESEL_CORE2); if (NREV_GE(pi->pubpi.phy_rev, 7)) { wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID0); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID2); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); if (CHSPEC_IS40(pi->radio_chanspec)) wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), 2, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); else wlc_phy_rfctrl_override_nphy_rev7( pi, (0x1 << 7), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0, 0, NPHY_REV7_RFCTRLOVERRIDE_ID1); } else { wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 3, 0); } wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX); if (NREV_GE(pi->pubpi.phy_rev, 7)) { wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 0x1, rx_core + 1); } else { if (rx_core == PHY_CORE_0) { rx_antval = 0x1; tx_antval = 0x8; } else { rx_antval = 0x4; tx_antval = 0x2; } wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, rx_antval, rx_core + 1); wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, tx_antval, tx_core + 1); } } static void wlc_phy_rxcal_phycleanup_nphy(struct brcms_phy *pi, u8 rx_core) { write_phy_reg(pi, 0xa2, pi->tx_rx_cal_phy_saveregs[0]); write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7, pi->tx_rx_cal_phy_saveregs[1]); write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5, pi->tx_rx_cal_phy_saveregs[2]); write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[3]); write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[4]); write_phy_reg(pi, 0x7a, pi->tx_rx_cal_phy_saveregs[5]); write_phy_reg(pi, 0x7d, pi->tx_rx_cal_phy_saveregs[6]); write_phy_reg(pi, 0xe7, pi->tx_rx_cal_phy_saveregs[7]); write_phy_reg(pi, 0xec, pi->tx_rx_cal_phy_saveregs[8]); if (NREV_GE(pi->pubpi.phy_rev, 7)) { write_phy_reg(pi, 0x342, pi->tx_rx_cal_phy_saveregs[11]); write_phy_reg(pi, 0x343, pi->tx_rx_cal_phy_saveregs[12]); write_phy_reg(pi, 0x346, pi->tx_rx_cal_phy_saveregs[13]); write_phy_reg(pi, 0x347, pi->tx_rx_cal_phy_saveregs[14]); } write_phy_reg(pi, 0x297, pi->tx_rx_cal_phy_saveregs[9]); write_phy_reg(pi, 0x29b, pi->tx_rx_cal_phy_saveregs[10]); } static void wlc_phy_rxcal_gainctrl_nphy_rev5(struct brcms_phy *pi, u8 rx_core, u16 *rxgain, u8 cal_type) { u16 num_samps; struct phy_iq_est est[PHY_CORE_MAX]; u8 tx_core; struct nphy_iq_comp save_comp, zero_comp; u32 i_pwr, q_pwr, curr_pwr, optim_pwr = 0, prev_pwr = 0, thresh_pwr = 10000; s16 desired_log2_pwr, actual_log2_pwr, delta_pwr; bool gainctrl_done = false; u8 mix_tia_gain = 3; s8 optim_gaintbl_index = 0, prev_gaintbl_index = 0; s8 curr_gaintbl_index = 3; u8 gainctrl_dirn = NPHY_RXCAL_GAIN_INIT; const struct nphy_ipa_txrxgain *nphy_rxcal_gaintbl; u16 hpvga, lpf_biq1, lpf_biq0, lna2, lna1; int fine_gain_idx; s8 txpwrindex; u16 nphy_rxcal_txgain[2]; if (NREV_GE(pi->pubpi.phy_rev, 7)) tx_core = rx_core; else tx_core = 1 - rx_core; num_samps = 1024; desired_log2_pwr = (cal_type == 0) ? 13 : 13; wlc_phy_rx_iq_coeffs_nphy(pi, 0, &save_comp); zero_comp.a0 = zero_comp.b0 = zero_comp.a1 = zero_comp.b1 = 0x0; wlc_phy_rx_iq_coeffs_nphy(pi, 1, &zero_comp); if (CHSPEC_IS5G(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) mix_tia_gain = 3; else if (NREV_GE(pi->pubpi.phy_rev, 4)) mix_tia_gain = 4; else mix_tia_gain = 6; if (NREV_GE(pi->pubpi.phy_rev, 7)) nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz_rev7; else nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz; } else { if (NREV_GE(pi->pubpi.phy_rev, 7)) nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz_rev7; else nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz; } do { hpvga = (NREV_GE(pi->pubpi.phy_rev, 7)) ? 0 : nphy_rxcal_gaintbl[curr_gaintbl_index].hpvga; lpf_biq1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq1; lpf_biq0 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq0; lna2 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna2; lna1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna1; txpwrindex = nphy_rxcal_gaintbl[curr_gaintbl_index].txpwrindex; if (NREV_GE(pi->pubpi.phy_rev, 7)) wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_rxgain, ((lpf_biq1 << 12) | (lpf_biq0 << 8) | (mix_tia_gain << 4) | (lna2 << 2) | lna1), 0x3, 0); else wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), ((hpvga << 12) | (lpf_biq1 << 10) | (lpf_biq0 << 8) | (mix_tia_gain << 4) | (lna2 << 2) | lna1), 0x3, 0); pi->nphy_rxcal_pwr_idx[tx_core] = txpwrindex; if (txpwrindex == -1) { nphy_rxcal_txgain[0] = 0x8ff0 | pi->nphy_gmval; nphy_rxcal_txgain[1] = 0x8ff0 | pi->nphy_gmval; wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, nphy_rxcal_txgain); } else { wlc_phy_txpwr_index_nphy(pi, tx_core + 1, txpwrindex, false); } wlc_phy_tx_tone_nphy(pi, (CHSPEC_IS40(pi->radio_chanspec)) ? NPHY_RXCAL_TONEFREQ_40MHz : NPHY_RXCAL_TONEFREQ_20MHz, NPHY_RXCAL_TONEAMP, 0, cal_type, false); wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); i_pwr = (est[rx_core].i_pwr + num_samps / 2) / num_samps; q_pwr = (est[rx_core].q_pwr + num_samps / 2) / num_samps; curr_pwr = i_pwr + q_pwr; switch (gainctrl_dirn) { case NPHY_RXCAL_GAIN_INIT: if (curr_pwr > thresh_pwr) { gainctrl_dirn = NPHY_RXCAL_GAIN_DOWN; prev_gaintbl_index = curr_gaintbl_index; curr_gaintbl_index--; } else { gainctrl_dirn = NPHY_RXCAL_GAIN_UP; prev_gaintbl_index = curr_gaintbl_index; curr_gaintbl_index++; } break; case NPHY_RXCAL_GAIN_UP: if (curr_pwr > thresh_pwr) { gainctrl_done = true; optim_pwr = prev_pwr; optim_gaintbl_index = prev_gaintbl_index; } else { prev_gaintbl_index = curr_gaintbl_index; curr_gaintbl_index++; } break; case NPHY_RXCAL_GAIN_DOWN: if (curr_pwr > thresh_pwr) { prev_gaintbl_index = curr_gaintbl_index; curr_gaintbl_index--; } else { gainctrl_done = true; optim_pwr = curr_pwr; optim_gaintbl_index = curr_gaintbl_index; } break; default: break; } if ((curr_gaintbl_index < 0) || (curr_gaintbl_index > NPHY_IPA_RXCAL_MAXGAININDEX)) { gainctrl_done = true; optim_pwr = curr_pwr; optim_gaintbl_index = prev_gaintbl_index; } else { prev_pwr = curr_pwr; } wlc_phy_stopplayback_nphy(pi); } while (!gainctrl_done); hpvga = nphy_rxcal_gaintbl[optim_gaintbl_index].hpvga; lpf_biq1 = nphy_rxcal_gaintbl[optim_gaintbl_index].lpf_biq1; lpf_biq0 = nphy_rxcal_gaintbl[optim_gaintbl_index].lpf_biq0; lna2 = nphy_rxcal_gaintbl[optim_gaintbl_index].lna2; lna1 = nphy_rxcal_gaintbl[optim_gaintbl_index].lna1; txpwrindex = nphy_rxcal_gaintbl[optim_gaintbl_index].txpwrindex; actual_log2_pwr = wlc_phy_nbits(optim_pwr); delta_pwr = desired_log2_pwr - actual_log2_pwr; if (NREV_GE(pi->pubpi.phy_rev, 7)) { fine_gain_idx = (int)lpf_biq1 + delta_pwr; if (fine_gain_idx + (int)lpf_biq0 > 10) lpf_biq1 = 10 - lpf_biq0; else lpf_biq1 = (u16) max(fine_gain_idx, 0); wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_rxgain, ((lpf_biq1 << 12) | (lpf_biq0 << 8) | (mix_tia_gain << 4) | (lna2 << 2) | lna1), 0x3, 0); } else { hpvga = (u16) max(min(((int)hpvga) + delta_pwr, 10), 0); wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), ((hpvga << 12) | (lpf_biq1 << 10) | (lpf_biq0 << 8) | (mix_tia_gain << 4) | (lna2 << 2) | lna1), 0x3, 0); } if (rxgain != NULL) { *rxgain++ = lna1; *rxgain++ = lna2; *rxgain++ = mix_tia_gain; *rxgain++ = lpf_biq0; *rxgain++ = lpf_biq1; *rxgain = hpvga; } wlc_phy_rx_iq_coeffs_nphy(pi, 1, &save_comp); } static void wlc_phy_rxcal_gainctrl_nphy(struct brcms_phy *pi, u8 rx_core, u16 *rxgain, u8 cal_type) { wlc_phy_rxcal_gainctrl_nphy_rev5(pi, rx_core, rxgain, cal_type); } static u8 wlc_phy_rc_sweep_nphy(struct brcms_phy *pi, u8 core_idx, u8 loopback_type) { u32 target_bws[2] = { 9500, 21000 }; u32 ref_tones[2] = { 3000, 6000 }; u32 target_bw, ref_tone; u32 target_pwr_ratios[2] = { 28606, 18468 }; u32 target_pwr_ratio, pwr_ratio, last_pwr_ratio = 0; u16 start_rccal_ovr_val = 128; u16 txlpf_rccal_lpc_ovr_val = 128; u16 rxlpf_rccal_hpc_ovr_val = 159; u16 orig_txlpf_rccal_lpc_ovr_val; u16 orig_rxlpf_rccal_hpc_ovr_val; u16 radio_addr_offset_rx; u16 radio_addr_offset_tx; u16 orig_dcBypass; u16 orig_RxStrnFilt40Num[6]; u16 orig_RxStrnFilt40Den[4]; u16 orig_rfctrloverride[2]; u16 orig_rfctrlauxreg[2]; u16 orig_rfctrlrssiothers; u16 tx_lpf_bw = 4; u16 rx_lpf_bw, rx_lpf_bws[2] = { 2, 4 }; u16 lpf_hpc = 7, hpvga_hpc = 7; s8 rccal_stepsize; u16 rccal_val, last_rccal_val = 0, best_rccal_val = 0; u32 ref_iq_vals = 0, target_iq_vals = 0; u16 num_samps, log_num_samps = 10; struct phy_iq_est est[PHY_CORE_MAX]; if (NREV_GE(pi->pubpi.phy_rev, 7)) return 0; num_samps = (1 << log_num_samps); if (CHSPEC_IS40(pi->radio_chanspec)) { target_bw = target_bws[1]; target_pwr_ratio = target_pwr_ratios[1]; ref_tone = ref_tones[1]; rx_lpf_bw = rx_lpf_bws[1]; } else { target_bw = target_bws[0]; target_pwr_ratio = target_pwr_ratios[0]; ref_tone = ref_tones[0]; rx_lpf_bw = rx_lpf_bws[0]; } if (core_idx == 0) { radio_addr_offset_rx = RADIO_2056_RX0; radio_addr_offset_tx = (loopback_type == 0) ? RADIO_2056_TX0 : RADIO_2056_TX1; } else { radio_addr_offset_rx = RADIO_2056_RX1; radio_addr_offset_tx = (loopback_type == 0) ? RADIO_2056_TX1 : RADIO_2056_TX0; } orig_txlpf_rccal_lpc_ovr_val = read_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx)); orig_rxlpf_rccal_hpc_ovr_val = read_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_HPC | radio_addr_offset_rx)); orig_dcBypass = ((read_phy_reg(pi, 0x48) >> 8) & 1); orig_RxStrnFilt40Num[0] = read_phy_reg(pi, 0x267); orig_RxStrnFilt40Num[1] = read_phy_reg(pi, 0x268); orig_RxStrnFilt40Num[2] = read_phy_reg(pi, 0x269); orig_RxStrnFilt40Den[0] = read_phy_reg(pi, 0x26a); orig_RxStrnFilt40Den[1] = read_phy_reg(pi, 0x26b); orig_RxStrnFilt40Num[3] = read_phy_reg(pi, 0x26c); orig_RxStrnFilt40Num[4] = read_phy_reg(pi, 0x26d); orig_RxStrnFilt40Num[5] = read_phy_reg(pi, 0x26e); orig_RxStrnFilt40Den[2] = read_phy_reg(pi, 0x26f); orig_RxStrnFilt40Den[3] = read_phy_reg(pi, 0x270); orig_rfctrloverride[0] = read_phy_reg(pi, 0xe7); orig_rfctrloverride[1] = read_phy_reg(pi, 0xec); orig_rfctrlauxreg[0] = read_phy_reg(pi, 0xf8); orig_rfctrlauxreg[1] = read_phy_reg(pi, 0xfa); orig_rfctrlrssiothers = read_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d); write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx), txlpf_rccal_lpc_ovr_val); write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_HPC | radio_addr_offset_rx), rxlpf_rccal_hpc_ovr_val); mod_phy_reg(pi, 0x48, (0x1 << 8), (0x1 << 8)); write_phy_reg(pi, 0x267, 0x02d4); write_phy_reg(pi, 0x268, 0x0000); write_phy_reg(pi, 0x269, 0x0000); write_phy_reg(pi, 0x26a, 0x0000); write_phy_reg(pi, 0x26b, 0x0000); write_phy_reg(pi, 0x26c, 0x02d4); write_phy_reg(pi, 0x26d, 0x0000); write_phy_reg(pi, 0x26e, 0x0000); write_phy_reg(pi, 0x26f, 0x0000); write_phy_reg(pi, 0x270, 0x0000); or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 8)); or_phy_reg(pi, (core_idx == 0) ? 0xec : 0xe7, (0x1 << 15)); or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 9)); or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 10)); mod_phy_reg(pi, (core_idx == 0) ? 0xfa : 0xf8, (0x7 << 10), (tx_lpf_bw << 10)); mod_phy_reg(pi, (core_idx == 0) ? 0xf8 : 0xfa, (0x7 << 0), (hpvga_hpc << 0)); mod_phy_reg(pi, (core_idx == 0) ? 0xf8 : 0xfa, (0x7 << 4), (lpf_hpc << 4)); mod_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d, (0x7 << 8), (rx_lpf_bw << 8)); rccal_stepsize = 16; rccal_val = start_rccal_ovr_val + rccal_stepsize; while (rccal_stepsize >= 0) { write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_LPC | radio_addr_offset_rx), rccal_val); if (rccal_stepsize == 16) { wlc_phy_tx_tone_nphy(pi, ref_tone, NPHY_RXCAL_TONEAMP, 0, 1, false); udelay(2); wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); if (core_idx == 0) ref_iq_vals = max_t(u32, (est[0].i_pwr + est[0].q_pwr) >> (log_num_samps + 1), 1); else ref_iq_vals = max_t(u32, (est[1].i_pwr + est[1].q_pwr) >> (log_num_samps + 1), 1); wlc_phy_tx_tone_nphy(pi, target_bw, NPHY_RXCAL_TONEAMP, 0, 1, false); udelay(2); } wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); if (core_idx == 0) target_iq_vals = (est[0].i_pwr + est[0].q_pwr) >> (log_num_samps + 1); else target_iq_vals = (est[1].i_pwr + est[1].q_pwr) >> (log_num_samps + 1); pwr_ratio = (uint) ((target_iq_vals << 16) / ref_iq_vals); if (rccal_stepsize == 0) rccal_stepsize--; else if (rccal_stepsize == 1) { last_rccal_val = rccal_val; rccal_val += (pwr_ratio > target_pwr_ratio) ? 1 : -1; last_pwr_ratio = pwr_ratio; rccal_stepsize--; } else { rccal_stepsize = (rccal_stepsize >> 1); rccal_val += ((pwr_ratio > target_pwr_ratio) ? rccal_stepsize : (-rccal_stepsize)); } if (rccal_stepsize == -1) { best_rccal_val = (abs((int)last_pwr_ratio - (int)target_pwr_ratio) < abs((int)pwr_ratio - (int)target_pwr_ratio)) ? last_rccal_val : rccal_val; if (CHSPEC_IS40(pi->radio_chanspec)) { if ((best_rccal_val > 140) || (best_rccal_val < 135)) best_rccal_val = 138; } else { if ((best_rccal_val > 142) || (best_rccal_val < 137)) best_rccal_val = 140; } write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_LPC | radio_addr_offset_rx), best_rccal_val); } } wlc_phy_stopplayback_nphy(pi); write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx), orig_txlpf_rccal_lpc_ovr_val); write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_HPC | radio_addr_offset_rx), orig_rxlpf_rccal_hpc_ovr_val); mod_phy_reg(pi, 0x48, (0x1 << 8), (orig_dcBypass << 8)); write_phy_reg(pi, 0x267, orig_RxStrnFilt40Num[0]); write_phy_reg(pi, 0x268, orig_RxStrnFilt40Num[1]); write_phy_reg(pi, 0x269, orig_RxStrnFilt40Num[2]); write_phy_reg(pi, 0x26a, orig_RxStrnFilt40Den[0]); write_phy_reg(pi, 0x26b, orig_RxStrnFilt40Den[1]); write_phy_reg(pi, 0x26c, orig_RxStrnFilt40Num[3]); write_phy_reg(pi, 0x26d, orig_RxStrnFilt40Num[4]); write_phy_reg(pi, 0x26e, orig_RxStrnFilt40Num[5]); write_phy_reg(pi, 0x26f, orig_RxStrnFilt40Den[2]); write_phy_reg(pi, 0x270, orig_RxStrnFilt40Den[3]); write_phy_reg(pi, 0xe7, orig_rfctrloverride[0]); write_phy_reg(pi, 0xec, orig_rfctrloverride[1]); write_phy_reg(pi, 0xf8, orig_rfctrlauxreg[0]); write_phy_reg(pi, 0xfa, orig_rfctrlauxreg[1]); write_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d, orig_rfctrlrssiothers); pi->nphy_anarxlpf_adjusted = false; return best_rccal_val - 0x80; } #define WAIT_FOR_SCOPE 4000 static int wlc_phy_cal_rxiq_nphy_rev3(struct brcms_phy *pi, struct nphy_txgains target_gain, u8 cal_type, bool debug) { u16 orig_BBConfig; u8 core_no, rx_core; u8 best_rccal[2]; u16 gain_save[2]; u16 cal_gain[2]; struct nphy_iqcal_params cal_params[2]; u8 rxcore_state; s8 rxlpf_rccal_hpc, txlpf_rccal_lpc; s8 txlpf_idac; bool phyhang_avoid_state = false; bool skip_rxiqcal = false; orig_BBConfig = read_phy_reg(pi, 0x01); mod_phy_reg(pi, 0x01, (0x1 << 15), 0); wlc_phy_stay_in_carriersearch_nphy(pi, true); if (NREV_GE(pi->pubpi.phy_rev, 4)) { phyhang_avoid_state = pi->phyhang_avoid; pi->phyhang_avoid = false; } wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); for (core_no = 0; core_no <= 1; core_no++) { wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, &cal_params[core_no]); cal_gain[core_no] = cal_params[core_no].cal_gain; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); rxcore_state = wlc_phy_rxcore_getstate_nphy( (struct brcms_phy_pub *) pi); for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) { skip_rxiqcal = ((rxcore_state & (1 << rx_core)) == 0) ? true : false; wlc_phy_rxcal_physetup_nphy(pi, rx_core); wlc_phy_rxcal_radio_setup_nphy(pi, rx_core); if ((!skip_rxiqcal) && ((cal_type == 0) || (cal_type == 2))) { wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL, 0); wlc_phy_tx_tone_nphy(pi, (CHSPEC_IS40( pi->radio_chanspec)) ? NPHY_RXCAL_TONEFREQ_40MHz : NPHY_RXCAL_TONEFREQ_20MHz, NPHY_RXCAL_TONEAMP, 0, cal_type, false); if (debug) mdelay(WAIT_FOR_SCOPE); wlc_phy_calc_rx_iq_comp_nphy(pi, rx_core + 1); wlc_phy_stopplayback_nphy(pi); } if (((cal_type == 1) || (cal_type == 2)) && NREV_LT(pi->pubpi.phy_rev, 7)) { if (rx_core == PHY_CORE_1) { if (rxcore_state == 1) wlc_phy_rxcore_setstate_nphy( (struct brcms_phy_pub *) pi, 3); wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL, 1); best_rccal[rx_core] = wlc_phy_rc_sweep_nphy(pi, rx_core, 1); pi->nphy_rccal_value = best_rccal[rx_core]; if (rxcore_state == 1) wlc_phy_rxcore_setstate_nphy( (struct brcms_phy_pub *) pi, rxcore_state); } } wlc_phy_rxcal_radio_cleanup_nphy(pi, rx_core); wlc_phy_rxcal_phycleanup_nphy(pi, rx_core); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); } if ((cal_type == 1) || (cal_type == 2)) { best_rccal[0] = best_rccal[1]; write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_LPC | RADIO_2056_RX0), (best_rccal[0] | 0x80)); for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) { rxlpf_rccal_hpc = (((int)best_rccal[rx_core] - 12) >> 1) + 10; txlpf_rccal_lpc = ((int)best_rccal[rx_core] - 12) + 10; if (PHY_IPA(pi)) { txlpf_rccal_lpc += (pi->bw == WL_CHANSPEC_BW_40) ? 24 : 12; txlpf_idac = (pi->bw == WL_CHANSPEC_BW_40) ? 0x0e : 0x13; WRITE_RADIO_REG2(pi, RADIO_2056, TX, rx_core, TXLPF_IDAC_4, txlpf_idac); } rxlpf_rccal_hpc = max(min_t(u8, rxlpf_rccal_hpc, 31), 0); txlpf_rccal_lpc = max(min_t(u8, txlpf_rccal_lpc, 31), 0); write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_HPC | ((rx_core == PHY_CORE_0) ? RADIO_2056_RX0 : RADIO_2056_RX1)), (rxlpf_rccal_hpc | 0x80)); write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | ((rx_core == PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1)), (txlpf_rccal_lpc | 0x80)); } } write_phy_reg(pi, 0x01, orig_BBConfig); wlc_phy_resetcca_nphy(pi); if (NREV_GE(pi->pubpi.phy_rev, 7)) wlc_phy_rfctrl_override_1tomany_nphy( pi, NPHY_REV7_RfctrlOverride_cmd_rxgain, 0, 0x3, 1); else wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); if (NREV_GE(pi->pubpi.phy_rev, 4)) pi->phyhang_avoid = phyhang_avoid_state; wlc_phy_stay_in_carriersearch_nphy(pi, false); return 0; } static int wlc_phy_cal_rxiq_nphy_rev2(struct brcms_phy *pi, struct nphy_txgains target_gain, bool debug) { struct phy_iq_est est[PHY_CORE_MAX]; u8 core_num, rx_core, tx_core; u16 lna_vals[] = { 0x3, 0x3, 0x1 }; u16 hpf1_vals[] = { 0x7, 0x2, 0x0 }; u16 hpf2_vals[] = { 0x2, 0x0, 0x0 }; s16 curr_hpf1, curr_hpf2, curr_hpf, curr_lna; s16 desired_log2_pwr, actual_log2_pwr, hpf_change; u16 orig_RfseqCoreActv, orig_AfectrlCore, orig_AfectrlOverride; u16 orig_RfctrlIntcRx, orig_RfctrlIntcTx; u16 num_samps; u32 i_pwr, q_pwr, tot_pwr[3]; u8 gain_pass, use_hpf_num; u16 mask, val1, val2; u16 core_no; u16 gain_save[2]; u16 cal_gain[2]; struct nphy_iqcal_params cal_params[2]; u8 phy_bw; int bcmerror = 0; bool first_playtone = true; wlc_phy_stay_in_carriersearch_nphy(pi, true); if (NREV_LT(pi->pubpi.phy_rev, 2)) wlc_phy_reapply_txcal_coeffs_nphy(pi); wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); for (core_no = 0; core_no <= 1; core_no++) { wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, &cal_params[core_no]); cal_gain[core_no] = cal_params[core_no].cal_gain; } wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); num_samps = 1024; desired_log2_pwr = 13; for (core_num = 0; core_num < 2; core_num++) { rx_core = core_num; tx_core = 1 - core_num; orig_RfseqCoreActv = read_phy_reg(pi, 0xa2); orig_AfectrlCore = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7); orig_AfectrlOverride = read_phy_reg(pi, 0xa5); orig_RfctrlIntcRx = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x91 : 0x92); orig_RfctrlIntcTx = read_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : 0x92); mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << tx_core) << 12); mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); or_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), ((0x1 << 1) | (0x1 << 2))); or_phy_reg(pi, 0xa5, ((0x1 << 1) | (0x1 << 2))); if (((pi->nphy_rxcalparams) & 0xff000000)) write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x91 : 0x92, (CHSPEC_IS5G(pi->radio_chanspec) ? 0x140 : 0x110)); else write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x91 : 0x92, (CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120)); write_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : 0x92, (CHSPEC_IS5G(pi->radio_chanspec) ? 0x148 : 0x114)); mask = RADIO_2055_COUPLE_RX_MASK | RADIO_2055_COUPLE_TX_MASK; if (rx_core == PHY_CORE_0) { val1 = RADIO_2055_COUPLE_RX_MASK; val2 = RADIO_2055_COUPLE_TX_MASK; } else { val1 = RADIO_2055_COUPLE_TX_MASK; val2 = RADIO_2055_COUPLE_RX_MASK; } if ((pi->nphy_rxcalparams & 0x10000)) { mod_radio_reg(pi, RADIO_2055_CORE1_GEN_SPARE2, mask, val1); mod_radio_reg(pi, RADIO_2055_CORE2_GEN_SPARE2, mask, val2); } for (gain_pass = 0; gain_pass < 4; gain_pass++) { if (debug) mdelay(WAIT_FOR_SCOPE); if (gain_pass < 3) { curr_lna = lna_vals[gain_pass]; curr_hpf1 = hpf1_vals[gain_pass]; curr_hpf2 = hpf2_vals[gain_pass]; } else { if (tot_pwr[1] > 10000) { curr_lna = lna_vals[2]; curr_hpf1 = hpf1_vals[2]; curr_hpf2 = hpf2_vals[2]; use_hpf_num = 1; curr_hpf = curr_hpf1; actual_log2_pwr = wlc_phy_nbits(tot_pwr[2]); } else { if (tot_pwr[0] > 10000) { curr_lna = lna_vals[1]; curr_hpf1 = hpf1_vals[1]; curr_hpf2 = hpf2_vals[1]; use_hpf_num = 1; curr_hpf = curr_hpf1; actual_log2_pwr = wlc_phy_nbits( tot_pwr[1]); } else { curr_lna = lna_vals[0]; curr_hpf1 = hpf1_vals[0]; curr_hpf2 = hpf2_vals[0]; use_hpf_num = 2; curr_hpf = curr_hpf2; actual_log2_pwr = wlc_phy_nbits( tot_pwr[0]); } } hpf_change = desired_log2_pwr - actual_log2_pwr; curr_hpf += hpf_change; curr_hpf = max(min_t(u16, curr_hpf, 10), 0); if (use_hpf_num == 1) curr_hpf1 = curr_hpf; else curr_hpf2 = curr_hpf; } wlc_phy_rfctrl_override_nphy(pi, (0x1 << 10), ((curr_hpf2 << 8) | (curr_hpf1 << 4) | (curr_lna << 2)), 0x3, 0); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); wlc_phy_stopplayback_nphy(pi); if (first_playtone) { bcmerror = wlc_phy_tx_tone_nphy(pi, 4000, (u16) (pi->nphy_rxcalparams & 0xffff), 0, 0, true); first_playtone = false; } else { phy_bw = (CHSPEC_IS40(pi->radio_chanspec)) ? 40 : 20; wlc_phy_runsamples_nphy(pi, phy_bw * 8, 0xffff, 0, 0, 0, true); } if (bcmerror == 0) { if (gain_pass < 3) { wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); i_pwr = (est[rx_core].i_pwr + num_samps / 2) / num_samps; q_pwr = (est[rx_core].q_pwr + num_samps / 2) / num_samps; tot_pwr[gain_pass] = i_pwr + q_pwr; } else { wlc_phy_calc_rx_iq_comp_nphy(pi, (1 << rx_core)); } wlc_phy_stopplayback_nphy(pi); } if (bcmerror != 0) break; } and_radio_reg(pi, RADIO_2055_CORE1_GEN_SPARE2, ~mask); and_radio_reg(pi, RADIO_2055_CORE2_GEN_SPARE2, ~mask); write_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : 0x92, orig_RfctrlIntcTx); write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x91 : 0x92, orig_RfctrlIntcRx); write_phy_reg(pi, 0xa5, orig_AfectrlOverride); write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7, orig_AfectrlCore); write_phy_reg(pi, 0xa2, orig_RfseqCoreActv); if (bcmerror != 0) break; } wlc_phy_rfctrl_override_nphy(pi, (0x1 << 10), 0, 0x3, 1); wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); wlc_phy_stay_in_carriersearch_nphy(pi, false); return bcmerror; } int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, u8 cal_type, bool debug) { if (NREV_GE(pi->pubpi.phy_rev, 7)) cal_type = 0; if (NREV_GE(pi->pubpi.phy_rev, 3)) return wlc_phy_cal_rxiq_nphy_rev3(pi, target_gain, cal_type, debug); else return wlc_phy_cal_rxiq_nphy_rev2(pi, target_gain, debug); } void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi) { uint core; u32 txgain; u16 rad_gain, dac_gain, bbmult, m1m2; u8 txpi[2], chan_freq_range; s32 rfpwr_offset; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); if (pi->sh->sromrev < 4) { txpi[0] = txpi[1] = 72; } else { chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); switch (chan_freq_range) { case WL_CHAN_FREQ_RANGE_2G: case WL_CHAN_FREQ_RANGE_5GL: case WL_CHAN_FREQ_RANGE_5GM: case WL_CHAN_FREQ_RANGE_5GH: txpi[0] = 0; txpi[1] = 0; break; default: txpi[0] = txpi[1] = 91; break; } } if (NREV_GE(pi->pubpi.phy_rev, 7)) txpi[0] = txpi[1] = 30; else if (NREV_GE(pi->pubpi.phy_rev, 3)) txpi[0] = txpi[1] = 40; if (NREV_LT(pi->pubpi.phy_rev, 7)) { if ((txpi[0] < 40) || (txpi[0] > 100) || (txpi[1] < 40) || (txpi[1] > 100)) txpi[0] = txpi[1] = 91; } pi->nphy_txpwrindex[PHY_CORE_0].index_internal = txpi[0]; pi->nphy_txpwrindex[PHY_CORE_1].index_internal = txpi[1]; pi->nphy_txpwrindex[PHY_CORE_0].index_internal_save = txpi[0]; pi->nphy_txpwrindex[PHY_CORE_1].index_internal_save = txpi[1]; for (core = 0; core < pi->pubpi.phy_corenum; core++) { uint phyrev = pi->pubpi.phy_rev; if (NREV_GE(phyrev, 3)) { if (PHY_IPA(pi)) { u32 *tx_gaintbl = wlc_phy_get_ipa_gaintbl_nphy(pi); txgain = tx_gaintbl[txpi[core]]; } else { if (CHSPEC_IS5G(pi->radio_chanspec)) { if (NREV_IS(phyrev, 3)) { txgain = nphy_tpc_5GHz_txgain_rev3 [txpi[core]]; } else if (NREV_IS(phyrev, 4)) { txgain = ( pi->srom_fem5g.extpagain == 3) ? nphy_tpc_5GHz_txgain_HiPwrEPA [txpi[core]] : nphy_tpc_5GHz_txgain_rev4 [txpi[core]]; } else { txgain = nphy_tpc_5GHz_txgain_rev5 [txpi[core]]; } } else { if (NREV_GE(phyrev, 5) && (pi->srom_fem2g.extpagain == 3)) { txgain = nphy_tpc_txgain_HiPwrEPA [txpi[core]]; } else { txgain = nphy_tpc_txgain_rev3 [txpi[core]]; } } } } else { txgain = nphy_tpc_txgain[txpi[core]]; } if (NREV_GE(phyrev, 3)) rad_gain = (txgain >> 16) & ((1 << (32 - 16 + 1)) - 1); else rad_gain = (txgain >> 16) & ((1 << (28 - 16 + 1)) - 1); if (NREV_GE(phyrev, 7)) dac_gain = (txgain >> 8) & ((1 << (10 - 8 + 1)) - 1); else dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1); bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1); if (NREV_GE(phyrev, 3)) mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : 0xa5), (0x1 << 8), (0x1 << 8)); else mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14)); write_phy_reg(pi, (core == PHY_CORE_0) ? 0xaa : 0xab, dac_gain); wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, &rad_gain); wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); m1m2 |= ((core == PHY_CORE_0) ? (bbmult << 8) : (bbmult << 0)); wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); if (PHY_IPA(pi)) { wlc_phy_table_read_nphy(pi, (core == PHY_CORE_0 ? NPHY_TBL_ID_CORE1TXPWRCTL : NPHY_TBL_ID_CORE2TXPWRCTL), 1, 576 + txpi[core], 32, &rfpwr_offset); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1ff << 4), ((s16) rfpwr_offset) << 4); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 2), (1) << 2); } } and_phy_reg(pi, 0xbf, (u16) (~(0x1f << 0))); if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } static void wlc_phy_txpwr_nphy_srom_convert(u8 *srom_max, u16 *pwr_offset, u8 tmp_max_pwr, u8 rate_start, u8 rate_end) { u8 rate; u8 word_num, nibble_num; u8 tmp_nibble; for (rate = rate_start; rate <= rate_end; rate++) { word_num = (rate - rate_start) >> 2; nibble_num = (rate - rate_start) & 0x3; tmp_nibble = (pwr_offset[word_num] >> 4 * nibble_num) & 0xf; srom_max[rate] = tmp_max_pwr - 2 * tmp_nibble; } } static void wlc_phy_txpwr_nphy_po_apply(u8 *srom_max, u8 pwr_offset, u8 rate_start, u8 rate_end) { u8 rate; for (rate = rate_start; rate <= rate_end; rate++) srom_max[rate] -= 2 * pwr_offset; } void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, u8 rate_mcs_end, u8 rate_ofdm_start) { u8 rate1, rate2; rate2 = rate_ofdm_start; for (rate1 = rate_mcs_start; rate1 <= rate_mcs_end - 1; rate1++) { power[rate1] = power[rate2]; rate2 += (rate1 == rate_mcs_start) ? 2 : 1; } power[rate_mcs_end] = power[rate_mcs_end - 1]; } void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, u8 rate_ofdm_end, u8 rate_mcs_start) { u8 rate1, rate2; for (rate1 = rate_ofdm_start, rate2 = rate_mcs_start; rate1 <= rate_ofdm_end; rate1++, rate2++) { power[rate1] = power[rate2]; if (rate1 == rate_ofdm_start) power[++rate1] = power[rate2]; } } void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi) { uint rate1, rate2, band_num; u8 tmp_bw40po = 0, tmp_cddpo = 0, tmp_stbcpo = 0; u8 tmp_max_pwr = 0; u16 pwr_offsets1[2], *pwr_offsets2 = NULL; u8 *tx_srom_max_rate = NULL; for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); band_num++) { switch (band_num) { case 0: tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_2g, pi->nphy_pwrctrl_info[1].max_pwr_2g); pwr_offsets1[0] = pi->cck2gpo; wlc_phy_txpwr_nphy_srom_convert(pi->tx_srom_max_rate_2g, pwr_offsets1, tmp_max_pwr, TXP_FIRST_CCK, TXP_LAST_CCK); pwr_offsets1[0] = (u16) (pi->ofdm2gpo & 0xffff); pwr_offsets1[1] = (u16) (pi->ofdm2gpo >> 16) & 0xffff; pwr_offsets2 = pi->mcs2gpo; tmp_cddpo = pi->cdd2gpo; tmp_stbcpo = pi->stbc2gpo; tmp_bw40po = pi->bw402gpo; tx_srom_max_rate = pi->tx_srom_max_rate_2g; break; case 1: tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gm, pi->nphy_pwrctrl_info[1].max_pwr_5gm); pwr_offsets1[0] = (u16) (pi->ofdm5gpo & 0xffff); pwr_offsets1[1] = (u16) (pi->ofdm5gpo >> 16) & 0xffff; pwr_offsets2 = pi->mcs5gpo; tmp_cddpo = pi->cdd5gpo; tmp_stbcpo = pi->stbc5gpo; tmp_bw40po = pi->bw405gpo; tx_srom_max_rate = pi->tx_srom_max_rate_5g_mid; break; case 2: tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gl, pi->nphy_pwrctrl_info[1].max_pwr_5gl); pwr_offsets1[0] = (u16) (pi->ofdm5glpo & 0xffff); pwr_offsets1[1] = (u16) (pi->ofdm5glpo >> 16) & 0xffff; pwr_offsets2 = pi->mcs5glpo; tmp_cddpo = pi->cdd5glpo; tmp_stbcpo = pi->stbc5glpo; tmp_bw40po = pi->bw405glpo; tx_srom_max_rate = pi->tx_srom_max_rate_5g_low; break; case 3: tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gh, pi->nphy_pwrctrl_info[1].max_pwr_5gh); pwr_offsets1[0] = (u16) (pi->ofdm5ghpo & 0xffff); pwr_offsets1[1] = (u16) (pi->ofdm5ghpo >> 16) & 0xffff; pwr_offsets2 = pi->mcs5ghpo; tmp_cddpo = pi->cdd5ghpo; tmp_stbcpo = pi->stbc5ghpo; tmp_bw40po = pi->bw405ghpo; tx_srom_max_rate = pi->tx_srom_max_rate_5g_hi; break; } wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets1, tmp_max_pwr, TXP_FIRST_OFDM, TXP_LAST_OFDM); wlc_phy_ofdm_to_mcs_powers_nphy(tx_srom_max_rate, TXP_FIRST_MCS_20_SISO, TXP_LAST_MCS_20_SISO, TXP_FIRST_OFDM); wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets2, tmp_max_pwr, TXP_FIRST_MCS_20_CDD, TXP_LAST_MCS_20_CDD); if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_cddpo, TXP_FIRST_MCS_20_CDD, TXP_LAST_MCS_20_CDD); wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, TXP_FIRST_OFDM_20_CDD, TXP_LAST_OFDM_20_CDD, TXP_FIRST_MCS_20_CDD); wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets2, tmp_max_pwr, TXP_FIRST_MCS_20_STBC, TXP_LAST_MCS_20_STBC); if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_stbcpo, TXP_FIRST_MCS_20_STBC, TXP_LAST_MCS_20_STBC); wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, &pwr_offsets2[2], tmp_max_pwr, TXP_FIRST_MCS_20_SDM, TXP_LAST_MCS_20_SDM); if (NPHY_IS_SROM_REINTERPRET) { wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, &pwr_offsets2[4], tmp_max_pwr, TXP_FIRST_MCS_40_SISO, TXP_LAST_MCS_40_SISO); wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, TXP_FIRST_OFDM_40_SISO, TXP_LAST_OFDM_40_SISO, TXP_FIRST_MCS_40_SISO); wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, &pwr_offsets2[4], tmp_max_pwr, TXP_FIRST_MCS_40_CDD, TXP_LAST_MCS_40_CDD); wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_cddpo, TXP_FIRST_MCS_40_CDD, TXP_LAST_MCS_40_CDD); wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, TXP_FIRST_OFDM_40_CDD, TXP_LAST_OFDM_40_CDD, TXP_FIRST_MCS_40_CDD); wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, &pwr_offsets2[4], tmp_max_pwr, TXP_FIRST_MCS_40_STBC, TXP_LAST_MCS_40_STBC); wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_stbcpo, TXP_FIRST_MCS_40_STBC, TXP_LAST_MCS_40_STBC); wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, &pwr_offsets2[6], tmp_max_pwr, TXP_FIRST_MCS_40_SDM, TXP_LAST_MCS_40_SDM); } else { for (rate1 = TXP_FIRST_OFDM_40_SISO, rate2 = TXP_FIRST_OFDM; rate1 <= TXP_LAST_MCS_40_SDM; rate1++, rate2++) tx_srom_max_rate[rate1] = tx_srom_max_rate[rate2]; } if (NREV_GE(pi->pubpi.phy_rev, 3)) wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_bw40po, TXP_FIRST_OFDM_40_SISO, TXP_LAST_MCS_40_SDM); tx_srom_max_rate[TXP_MCS_32] = tx_srom_max_rate[TXP_FIRST_MCS_40_CDD]; } return; } void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi) { u8 tx_pwr_ctrl_state; wlc_phy_txpwr_limit_to_tbl_nphy(pi); wlc_phy_txpwrctrl_pwr_setup_nphy(pi); tx_pwr_ctrl_state = pi->nphy_txpwrctrl; if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); udelay(1); } wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); } static bool wlc_phy_txpwr_ison_nphy(struct brcms_phy *pi) { return read_phy_reg((pi), 0x1e7) & ((0x1 << 15) | (0x1 << 14) | (0x1 << 13)); } u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi) { u16 tmp; u16 pwr_idx[2]; if (wlc_phy_txpwr_ison_nphy(pi)) { pwr_idx[0] = wlc_phy_txpwr_idx_cur_get_nphy(pi, PHY_CORE_0); pwr_idx[1] = wlc_phy_txpwr_idx_cur_get_nphy(pi, PHY_CORE_1); tmp = (pwr_idx[0] << 8) | pwr_idx[1]; } else { tmp = ((pi->nphy_txpwrindex[PHY_CORE_0].index_internal & 0xff) << 8) | (pi->nphy_txpwrindex[PHY_CORE_1].index_internal & 0xff); } return tmp; } void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi) { if (PHY_IPA(pi) && (pi->nphy_force_papd_cal || (wlc_phy_txpwr_ison_nphy(pi) && (((u32) abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 0) - pi->nphy_papd_tx_gain_at_last_cal[0]) >= 4) || ((u32) abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 1) - pi->nphy_papd_tx_gain_at_last_cal[1]) >= 4))))) wlc_phy_a4(pi, true); } void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type) { u16 mask = 0, val = 0, ishw = 0; u8 ctr; uint core; u32 tbl_offset; u32 tbl_len; u16 regval[84]; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); switch (ctrl_type) { case PHY_TPC_HW_OFF: case PHY_TPC_HW_ON: pi->nphy_txpwrctrl = ctrl_type; break; default: break; } if (ctrl_type == PHY_TPC_HW_OFF) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { if (wlc_phy_txpwr_ison_nphy(pi)) { for (core = 0; core < pi->pubpi.phy_corenum; core++) pi->nphy_txpwr_idx[core] = wlc_phy_txpwr_idx_cur_get_nphy( pi, (u8) core); } } tbl_len = 84; tbl_offset = 64; for (ctr = 0; ctr < tbl_len; ctr++) regval[ctr] = 0; wlc_phy_table_write_nphy(pi, 26, tbl_len, tbl_offset, 16, regval); wlc_phy_table_write_nphy(pi, 27, tbl_len, tbl_offset, 16, regval); if (NREV_GE(pi->pubpi.phy_rev, 3)) and_phy_reg(pi, 0x1e7, (u16) (~((0x1 << 15) | (0x1 << 14) | (0x1 << 13)))); else and_phy_reg(pi, 0x1e7, (u16) (~((0x1 << 14) | (0x1 << 13)))); if (NREV_GE(pi->pubpi.phy_rev, 3)) { or_phy_reg(pi, 0x8f, (0x1 << 8)); or_phy_reg(pi, 0xa5, (0x1 << 8)); } else { or_phy_reg(pi, 0xa5, (0x1 << 14)); } if (NREV_IS(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0xdc, 0x00ff, 0x53); else if (NREV_LT(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0xdc, 0x00ff, 0x5a); if (NREV_LT(pi->pubpi.phy_rev, 2) && pi->bw == WL_CHANSPEC_BW_40) wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR, MHF1_IQSWAP_WAR, BRCM_BAND_ALL); } else { wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, 8, pi->adj_pwr_tbl_nphy); wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, 8, pi->adj_pwr_tbl_nphy); ishw = (ctrl_type == PHY_TPC_HW_ON) ? 0x1 : 0x0; mask = (0x1 << 14) | (0x1 << 13); val = (ishw << 14) | (ishw << 13); if (NREV_GE(pi->pubpi.phy_rev, 3)) { mask |= (0x1 << 15); val |= (ishw << 15); } mod_phy_reg(pi, 0x1e7, mask, val); if (CHSPEC_IS5G(pi->radio_chanspec)) { if (NREV_GE(pi->pubpi.phy_rev, 7)) { mod_phy_reg(pi, 0x1e7, (0x7f << 0), 0x32); mod_phy_reg(pi, 0x222, (0xff << 0), 0x32); } else { mod_phy_reg(pi, 0x1e7, (0x7f << 0), 0x64); if (NREV_GT(pi->pubpi.phy_rev, 1)) mod_phy_reg(pi, 0x222, (0xff << 0), 0x64); } } if (NREV_GE(pi->pubpi.phy_rev, 3)) { if ((pi->nphy_txpwr_idx[0] != 128) && (pi->nphy_txpwr_idx[1] != 128)) wlc_phy_txpwr_idx_cur_set_nphy(pi, pi-> nphy_txpwr_idx [0], pi-> nphy_txpwr_idx [1]); } if (NREV_GE(pi->pubpi.phy_rev, 3)) { and_phy_reg(pi, 0x8f, ~(0x1 << 8)); and_phy_reg(pi, 0xa5, ~(0x1 << 8)); } else { and_phy_reg(pi, 0xa5, ~(0x1 << 14)); } if (NREV_IS(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b); else if (NREV_LT(pi->pubpi.phy_rev, 2)) mod_phy_reg(pi, 0xdc, 0x00ff, 0x40); if (NREV_LT(pi->pubpi.phy_rev, 2) && pi->bw == WL_CHANSPEC_BW_40) wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR, 0x0, BRCM_BAND_ALL); if (PHY_IPA(pi)) { mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 2), (0) << 2); mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 2), (0) << 2); } } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, s8 txpwrindex, bool restore_cals) { u8 core, txpwrctl_tbl; u16 tx_ind0, iq_ind0, lo_ind0; u16 m1m2; u32 txgain; u16 rad_gain, dac_gain; u8 bbmult; u32 iqcomp; u16 iqcomp_a, iqcomp_b; u32 locomp; u16 tmpval; u8 tx_pwr_ctrl_state; s32 rfpwr_offset; u16 regval[2]; if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, true); tx_ind0 = 192; iq_ind0 = 320; lo_ind0 = 448; for (core = 0; core < pi->pubpi.phy_corenum; core++) { if ((core_mask & (1 << core)) == 0) continue; txpwrctl_tbl = (core == PHY_CORE_0) ? 26 : 27; if (txpwrindex < 0) { if (pi->nphy_txpwrindex[core].index < 0) continue; if (NREV_GE(pi->pubpi.phy_rev, 3)) { mod_phy_reg(pi, 0x8f, (0x1 << 8), pi->nphy_txpwrindex[core]. AfectrlOverride); mod_phy_reg(pi, 0xa5, (0x1 << 8), pi->nphy_txpwrindex[core]. AfectrlOverride); } else { mod_phy_reg(pi, 0xa5, (0x1 << 14), pi->nphy_txpwrindex[core]. AfectrlOverride); } write_phy_reg(pi, (core == PHY_CORE_0) ? 0xaa : 0xab, pi->nphy_txpwrindex[core].AfeCtrlDacGain); wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, &pi->nphy_txpwrindex[core]. rad_gain); wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); m1m2 |= ((core == PHY_CORE_0) ? (pi->nphy_txpwrindex[core].bbmult << 8) : (pi->nphy_txpwrindex[core].bbmult << 0)); wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); if (restore_cals) { wlc_phy_table_write_nphy( pi, 15, 2, (80 + 2 * core), 16, &pi->nphy_txpwrindex[core].iqcomp_a); wlc_phy_table_write_nphy( pi, 15, 1, (85 + core), 16, &pi->nphy_txpwrindex[core].locomp); wlc_phy_table_write_nphy( pi, 15, 1, (93 + core), 16, &pi->nphy_txpwrindex[core].locomp); } wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl); pi->nphy_txpwrindex[core].index_internal = pi->nphy_txpwrindex[core].index_internal_save; } else { if (pi->nphy_txpwrindex[core].index < 0) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { mod_phy_reg(pi, 0x8f, (0x1 << 8), pi->nphy_txpwrindex[core]. AfectrlOverride); mod_phy_reg(pi, 0xa5, (0x1 << 8), pi->nphy_txpwrindex[core]. AfectrlOverride); } else { pi->nphy_txpwrindex[core]. AfectrlOverride = read_phy_reg(pi, 0xa5); } pi->nphy_txpwrindex[core].AfeCtrlDacGain = read_phy_reg(pi, (core == PHY_CORE_0) ? 0xaa : 0xab); wlc_phy_table_read_nphy(pi, 7, 1, (0x110 + core), 16, &pi-> nphy_txpwrindex[core]. rad_gain); wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &tmpval); tmpval >>= ((core == PHY_CORE_0) ? 8 : 0); tmpval &= 0xff; pi->nphy_txpwrindex[core].bbmult = (u8) tmpval; wlc_phy_table_read_nphy(pi, 15, 2, (80 + 2 * core), 16, &pi-> nphy_txpwrindex[core]. iqcomp_a); wlc_phy_table_read_nphy(pi, 15, 1, (85 + core), 16, &pi-> nphy_txpwrindex[core]. locomp); pi->nphy_txpwrindex[core].index_internal_save = pi->nphy_txpwrindex[core]. index_internal; } tx_pwr_ctrl_state = pi->nphy_txpwrctrl; wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); if (NREV_IS(pi->pubpi.phy_rev, 1)) wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, (tx_ind0 + txpwrindex), 32, &txgain); if (NREV_GE(pi->pubpi.phy_rev, 3)) rad_gain = (txgain >> 16) & ((1 << (32 - 16 + 1)) - 1); else rad_gain = (txgain >> 16) & ((1 << (28 - 16 + 1)) - 1); dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1); bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1); if (NREV_GE(pi->pubpi.phy_rev, 3)) mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : 0xa5), (0x1 << 8), (0x1 << 8)); else mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14)); write_phy_reg(pi, (core == PHY_CORE_0) ? 0xaa : 0xab, dac_gain); wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, &rad_gain); wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); m1m2 |= ((core == PHY_CORE_0) ? (bbmult << 8) : (bbmult << 0)); wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, (iq_ind0 + txpwrindex), 32, &iqcomp); iqcomp_a = (iqcomp >> 10) & ((1 << (19 - 10 + 1)) - 1); iqcomp_b = (iqcomp >> 0) & ((1 << (9 - 0 + 1)) - 1); if (restore_cals) { regval[0] = (u16) iqcomp_a; regval[1] = (u16) iqcomp_b; wlc_phy_table_write_nphy(pi, 15, 2, (80 + 2 * core), 16, regval); } wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, (lo_ind0 + txpwrindex), 32, &locomp); if (restore_cals) wlc_phy_table_write_nphy(pi, 15, 1, (85 + core), 16, &locomp); if (NREV_IS(pi->pubpi.phy_rev, 1)) wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); if (PHY_IPA(pi)) { wlc_phy_table_read_nphy(pi, (core == PHY_CORE_0 ? NPHY_TBL_ID_CORE1TXPWRCTL : NPHY_TBL_ID_CORE2TXPWRCTL), 1, 576 + txpwrindex, 32, &rfpwr_offset); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1ff << 4), ((s16) rfpwr_offset) << 4); mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : 0x29b, (0x1 << 2), (1) << 2); } wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); } pi->nphy_txpwrindex[core].index = txpwrindex; } if (pi->phyhang_avoid) wlc_phy_stay_in_carriersearch_nphy(pi, false); } void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, u8 *max_pwr, u8 txp_rate_idx) { u8 chan_freq_range; chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, chan); switch (chan_freq_range) { case WL_CHAN_FREQ_RANGE_2G: *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; break; case WL_CHAN_FREQ_RANGE_5GM: *max_pwr = pi->tx_srom_max_rate_5g_mid[txp_rate_idx]; break; case WL_CHAN_FREQ_RANGE_5GL: *max_pwr = pi->tx_srom_max_rate_5g_low[txp_rate_idx]; break; case WL_CHAN_FREQ_RANGE_5GH: *max_pwr = pi->tx_srom_max_rate_5g_hi[txp_rate_idx]; break; default: *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; break; } return; } void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable) { u16 clip_off[] = { 0xffff, 0xffff }; if (enable) { if (pi->nphy_deaf_count == 0) { pi->classifier_state = wlc_phy_classifier_nphy(pi, 0, 0); wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); wlc_phy_clip_det_nphy(pi, 0, pi->clip_state); wlc_phy_clip_det_nphy(pi, 1, clip_off); } pi->nphy_deaf_count++; wlc_phy_resetcca_nphy(pi); } else { pi->nphy_deaf_count--; if (pi->nphy_deaf_count == 0) { wlc_phy_classifier_nphy(pi, (0x7 << 0), pi->classifier_state); wlc_phy_clip_det_nphy(pi, 1, pi->clip_state); } } } void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode) { wlapi_suspend_mac_and_wait(pi->sh->physhim); if (mode) { if (pi->nphy_deaf_count == 0) wlc_phy_stay_in_carriersearch_nphy(pi, true); } else if (pi->nphy_deaf_count > 0) { wlc_phy_stay_in_carriersearch_nphy(pi, false); } wlapi_enable_mac(pi->sh->physhim); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h0000644000175000017500000000326612026211315026366 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define ANT_SWCTRL_TBL_REV3_IDX (0) #include #include "phy_int.h" extern const struct phytbl_info mimophytbl_info_rev0[], mimophytbl_info_rev0_volatile[]; extern const u32 mimophytbl_info_sz_rev0, mimophytbl_info_sz_rev0_volatile; extern const struct phytbl_info mimophytbl_info_rev3[], mimophytbl_info_rev3_volatile[], mimophytbl_info_rev3_volatile1[], mimophytbl_info_rev3_volatile2[], mimophytbl_info_rev3_volatile3[]; extern const u32 mimophytbl_info_sz_rev3, mimophytbl_info_sz_rev3_volatile, mimophytbl_info_sz_rev3_volatile1, mimophytbl_info_sz_rev3_volatile2, mimophytbl_info_sz_rev3_volatile3; extern const u32 noise_var_tbl_rev3[]; extern const struct phytbl_info mimophytbl_info_rev7[]; extern const u32 mimophytbl_info_sz_rev7; extern const u32 noise_var_tbl_rev7[]; extern const struct phytbl_info mimophytbl_info_rev16[]; extern const u32 mimophytbl_info_sz_rev16; compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c0000644000175000017500000040767512026211315026375 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "phytbl_n.h" static const u32 frame_struct_rev0[] = { 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x09804506, 0x00100030, 0x09804507, 0x00100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a0c, 0x00100004, 0x01000a0d, 0x00100024, 0x0980450e, 0x00100034, 0x0980450f, 0x00100034, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, 0x1980c506, 0x00100030, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x01800504, 0x00100030, 0x11808505, 0x00100030, 0x29814507, 0x01100030, 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x1980c50e, 0x00100038, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x0180050c, 0x00100038, 0x1180850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x1980c506, 0x00100030, 0x1980c506, 0x00100030, 0x11808504, 0x00100030, 0x3981ca05, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x10008a04, 0x00100000, 0x3981ca05, 0x00100030, 0x1980c506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a0c, 0x00100008, 0x01000a0d, 0x00100028, 0x1980c50e, 0x00100038, 0x1980c50e, 0x00100038, 0x1180850c, 0x00100038, 0x3981ca0d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x10008a0c, 0x00100008, 0x3981ca0d, 0x00100038, 0x1980c50e, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x02001405, 0x00100040, 0x0b004a06, 0x01900060, 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, 0x43020a04, 0x00100060, 0x1b00ca05, 0x00100060, 0x23010a07, 0x01500060, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x0200140d, 0x00100050, 0x0b004a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x23010a0f, 0x01500070, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x0b004a06, 0x01900060, 0x0b004a06, 0x01900060, 0x5b02ca04, 0x00100060, 0x3b01d405, 0x00100060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x5802d404, 0x00100000, 0x3b01d405, 0x00100060, 0x0b004a06, 0x01900060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5002940c, 0x00100010, 0x3201940d, 0x00100050, 0x0b004a0e, 0x01900070, 0x0b004a0e, 0x01900070, 0x5b02ca0c, 0x00100070, 0x3b01d40d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x5802d40c, 0x00100010, 0x3b01d40d, 0x00100070, 0x0b004a0e, 0x01900070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x000f4800, 0x62031405, 0x00100040, 0x53028a06, 0x01900060, 0x53028a07, 0x01900060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x000f4808, 0x6203140d, 0x00100048, 0x53028a0e, 0x01900068, 0x53028a0f, 0x01900068, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100004, 0x11008a0d, 0x00100024, 0x1980c50e, 0x00100034, 0x2181050e, 0x00100034, 0x2181050e, 0x00100034, 0x0180050c, 0x00100038, 0x1180850d, 0x00100038, 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x0180c506, 0x00100030, 0x0180c506, 0x00100030, 0x2180c50c, 0x00100030, 0x49820a0d, 0x0016a130, 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2000ca0c, 0x00100000, 0x49820a0d, 0x0016a130, 0x1980c50e, 0x00100030, 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100008, 0x0200140d, 0x00100048, 0x0b004a0e, 0x01900068, 0x13008a0e, 0x01900068, 0x13008a0e, 0x01900068, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x03004a06, 0x01900060, 0x03004a06, 0x01900060, 0x6b030a0c, 0x00100060, 0x4b02140d, 0x0016a160, 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b03140c, 0x00100060, 0x4b02140d, 0x0016a160, 0x0b004a0e, 0x01900060, 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x53028a06, 0x01900060, 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, 0x43020a04, 0x00100060, 0x1b00ca05, 0x00100060, 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x53028a0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u8 frame_lut_rev0[] = { 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, 0x0a, 0x0c, 0x1c, 0x1c, 0x0b, 0x0d, 0x1e, 0x1e, 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1a, 0x1a, 0x0e, 0x10, 0x20, 0x28, 0x0f, 0x11, 0x22, 0x2a, }; static const u32 tmap_tbl_rev0[] = { 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, 0x22222222, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x11111111, 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 tdtrn_tbl_rev0[] = { 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, 0xfa58fa58, 0xf895043b, 0xff4c09c0, 0xfbc6ffa8, 0xfb84f384, 0x0798f6f9, 0x05760122, 0x058409f6, 0x0b500000, 0x05b7f542, 0x08860432, 0x06ddfee7, 0xfb84f384, 0xf9d90664, 0xf7e8025c, 0x00fff7bd, 0x05a805a8, 0xf7bd00ff, 0x025cf7e8, 0x0664f9d9, 0xf384fb84, 0xfee706dd, 0x04320886, 0xf54205b7, 0x00000b50, 0x09f60584, 0x01220576, 0xf6f90798, 0xf384fb84, 0xffa8fbc6, 0x09c0ff4c, 0x043bf895, 0x02d402d4, 0x07de0270, 0xfc96079c, 0xf90afe94, 0xfe00ff2c, 0x02d4065d, 0x092a0096, 0x0014fbb8, 0xfd2cfd2c, 0x076afb3c, 0x0096f752, 0xf991fd87, 0xfb2c0200, 0xfeb8f960, 0x08e0fc96, 0x049802a8, 0xfd2cfd2c, 0x02a80498, 0xfc9608e0, 0xf960feb8, 0x0200fb2c, 0xfd87f991, 0xf7520096, 0xfb3c076a, 0xfd2cfd2c, 0xfbb80014, 0x0096092a, 0x065d02d4, 0xff2cfe00, 0xfe94f90a, 0x079cfc96, 0x027007de, 0x02d402d4, 0x027007de, 0x079cfc96, 0xfe94f90a, 0xff2cfe00, 0x065d02d4, 0x0096092a, 0xfbb80014, 0xfd2cfd2c, 0xfb3c076a, 0xf7520096, 0xfd87f991, 0x0200fb2c, 0xf960feb8, 0xfc9608e0, 0x02a80498, 0xfd2cfd2c, 0x049802a8, 0x08e0fc96, 0xfeb8f960, 0xfb2c0200, 0xf991fd87, 0x0096f752, 0x076afb3c, 0xfd2cfd2c, 0x0014fbb8, 0x092a0096, 0x02d4065d, 0xfe00ff2c, 0xf90afe94, 0xfc96079c, 0x07de0270, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, 0x061c061c, 0xff30009d, 0xffb21141, 0xfd87fb54, 0xf65dfe59, 0x02eef99e, 0x0166f03c, 0xfff809b6, 0x000008a4, 0x000af42b, 0x00eff577, 0xfa840bf2, 0xfc02ff51, 0x08260f67, 0xfff0036f, 0x0842f9c3, 0x00000000, 0x063df7be, 0xfc910010, 0xf099f7da, 0x00af03fe, 0xf40e057c, 0x0a89ff11, 0x0bd5fff6, 0xf75c0000, 0xf64a0008, 0x0fc4fe9a, 0x0662fd12, 0x01a709a3, 0x04ac0279, 0xeebf004e, 0xff6300d0, 0xf9e4f9e4, 0x00d0ff63, 0x004eeebf, 0x027904ac, 0x09a301a7, 0xfd120662, 0xfe9a0fc4, 0x0008f64a, 0x0000f75c, 0xfff60bd5, 0xff110a89, 0x057cf40e, 0x03fe00af, 0xf7daf099, 0x0010fc91, 0xf7be063d, 0x00000000, 0xf9c30842, 0x036ffff0, 0x0f670826, 0xff51fc02, 0x0bf2fa84, 0xf57700ef, 0xf42b000a, 0x08a40000, 0x09b6fff8, 0xf03c0166, 0xf99e02ee, 0xfe59f65d, 0xfb54fd87, 0x1141ffb2, 0x009dff30, 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, 0xfa58fa58, 0xf8f0fe00, 0x0448073d, 0xfdc9fe46, 0xf9910258, 0x089d0407, 0xfd5cf71a, 0x02affde0, 0x083e0496, 0xff5a0740, 0xff7afd97, 0x00fe01f1, 0x0009082e, 0xfa94ff75, 0xfecdf8ea, 0xffb0f693, 0xfd2cfa58, 0x0433ff16, 0xfba405dd, 0xfa610341, 0x06a606cb, 0x0039fd2d, 0x0677fa97, 0x01fa05e0, 0xf896003e, 0x075a068b, 0x012cfc3e, 0xfa23f98d, 0xfc7cfd43, 0xff90fc0d, 0x01c10982, 0x00c601d6, 0xfd2cfd2c, 0x01d600c6, 0x098201c1, 0xfc0dff90, 0xfd43fc7c, 0xf98dfa23, 0xfc3e012c, 0x068b075a, 0x003ef896, 0x05e001fa, 0xfa970677, 0xfd2d0039, 0x06cb06a6, 0x0341fa61, 0x05ddfba4, 0xff160433, 0xfa58fd2c, 0xf693ffb0, 0xf8eafecd, 0xff75fa94, 0x082e0009, 0x01f100fe, 0xfd97ff7a, 0x0740ff5a, 0x0496083e, 0xfde002af, 0xf71afd5c, 0x0407089d, 0x0258f991, 0xfe46fdc9, 0x073d0448, 0xfe00f8f0, 0xfd2cfd2c, 0xfce00500, 0xfc09fddc, 0xfe680157, 0x04c70571, 0xfc3aff21, 0xfcd70228, 0x056d0277, 0x0200fe00, 0x0022f927, 0xfe3c032b, 0xfc44ff3c, 0x03e9fbdb, 0x04570313, 0x04c9ff5c, 0x000d03b8, 0xfa580000, 0xfbe900d2, 0xf9d0fe0b, 0x0125fdf9, 0x042501bf, 0x0328fa2b, 0xffa902f0, 0xfa250157, 0x0200fe00, 0x03740438, 0xff0405fd, 0x030cfe52, 0x0037fb39, 0xff6904c5, 0x04f8fd23, 0xfd31fc1b, 0xfd2cfd2c, 0xfc1bfd31, 0xfd2304f8, 0x04c5ff69, 0xfb390037, 0xfe52030c, 0x05fdff04, 0x04380374, 0xfe000200, 0x0157fa25, 0x02f0ffa9, 0xfa2b0328, 0x01bf0425, 0xfdf90125, 0xfe0bf9d0, 0x00d2fbe9, 0x0000fa58, 0x03b8000d, 0xff5c04c9, 0x03130457, 0xfbdb03e9, 0xff3cfc44, 0x032bfe3c, 0xf9270022, 0xfe000200, 0x0277056d, 0x0228fcd7, 0xff21fc3a, 0x057104c7, 0x0157fe68, 0xfddcfc09, 0x0500fce0, 0xfd2cfd2c, 0x0500fce0, 0xfddcfc09, 0x0157fe68, 0x057104c7, 0xff21fc3a, 0x0228fcd7, 0x0277056d, 0xfe000200, 0xf9270022, 0x032bfe3c, 0xff3cfc44, 0xfbdb03e9, 0x03130457, 0xff5c04c9, 0x03b8000d, 0x0000fa58, 0x00d2fbe9, 0xfe0bf9d0, 0xfdf90125, 0x01bf0425, 0xfa2b0328, 0x02f0ffa9, 0x0157fa25, 0xfe000200, 0x04380374, 0x05fdff04, 0xfe52030c, 0xfb390037, 0x04c5ff69, 0xfd2304f8, 0xfc1bfd31, 0xfd2cfd2c, 0xfd31fc1b, 0x04f8fd23, 0xff6904c5, 0x0037fb39, 0x030cfe52, 0xff0405fd, 0x03740438, 0x0200fe00, 0xfa250157, 0xffa902f0, 0x0328fa2b, 0x042501bf, 0x0125fdf9, 0xf9d0fe0b, 0xfbe900d2, 0xfa580000, 0x000d03b8, 0x04c9ff5c, 0x04570313, 0x03e9fbdb, 0xfc44ff3c, 0xfe3c032b, 0x0022f927, 0x0200fe00, 0x056d0277, 0xfcd70228, 0xfc3aff21, 0x04c70571, 0xfe680157, 0xfc09fddc, 0xfce00500, 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, }; static const u32 intlv_tbl_rev0[] = { 0x00802070, 0x0671188d, 0x0a60192c, 0x0a300e46, 0x00c1188d, 0x080024d2, 0x00000070, }; static const u16 pilot_tbl_rev0[] = { 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0xff0a, 0xff82, 0xffa0, 0xff28, 0xffff, 0xffff, 0xffff, 0xffff, 0xff82, 0xffa0, 0xff28, 0xff0a, 0xffff, 0xffff, 0xffff, 0xffff, 0xf83f, 0xfa1f, 0xfa97, 0xfab5, 0xf2bd, 0xf0bf, 0xffff, 0xffff, 0xf017, 0xf815, 0xf215, 0xf095, 0xf035, 0xf01d, 0xffff, 0xffff, 0xff08, 0xff02, 0xff80, 0xff20, 0xff08, 0xff02, 0xff80, 0xff20, 0xf01f, 0xf817, 0xfa15, 0xf295, 0xf0b5, 0xf03d, 0xffff, 0xffff, 0xf82a, 0xfa0a, 0xfa82, 0xfaa0, 0xf2a8, 0xf0aa, 0xffff, 0xffff, 0xf002, 0xf800, 0xf200, 0xf080, 0xf020, 0xf008, 0xffff, 0xffff, 0xf00a, 0xf802, 0xfa00, 0xf280, 0xf0a0, 0xf028, 0xffff, 0xffff, }; static const u32 pltlut_tbl_rev0[] = { 0x76540123, 0x62407351, 0x76543201, 0x76540213, 0x76540123, 0x76430521, }; static const u32 tdi_tbl20_ant0_rev0[] = { 0x00091226, 0x000a1429, 0x000b56ad, 0x000c58b0, 0x000d5ab3, 0x000e9cb6, 0x000f9eba, 0x0000c13d, 0x00020301, 0x00030504, 0x00040708, 0x0005090b, 0x00064b8e, 0x00095291, 0x000a5494, 0x000b9718, 0x000c9927, 0x000d9b2a, 0x000edd2e, 0x000fdf31, 0x000101b4, 0x000243b7, 0x000345bb, 0x000447be, 0x00058982, 0x00068c05, 0x00099309, 0x000a950c, 0x000bd78f, 0x000cd992, 0x000ddb96, 0x000f1d99, 0x00005fa8, 0x0001422c, 0x0002842f, 0x00038632, 0x00048835, 0x0005ca38, 0x0006ccbc, 0x0009d3bf, 0x000b1603, 0x000c1806, 0x000d1a0a, 0x000e1c0d, 0x000f5e10, 0x00008093, 0x00018297, 0x0002c49a, 0x0003c680, 0x0004c880, 0x00060b00, 0x00070d00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 tdi_tbl20_ant1_rev0[] = { 0x00014b26, 0x00028d29, 0x000393ad, 0x00049630, 0x0005d833, 0x0006da36, 0x00099c3a, 0x000a9e3d, 0x000bc081, 0x000cc284, 0x000dc488, 0x000f068b, 0x0000488e, 0x00018b91, 0x0002d214, 0x0003d418, 0x0004d6a7, 0x000618aa, 0x00071aae, 0x0009dcb1, 0x000b1eb4, 0x000c0137, 0x000d033b, 0x000e053e, 0x000f4702, 0x00008905, 0x00020c09, 0x0003128c, 0x0004148f, 0x00051712, 0x00065916, 0x00091b19, 0x000a1d28, 0x000b5f2c, 0x000c41af, 0x000d43b2, 0x000e85b5, 0x000f87b8, 0x0000c9bc, 0x00024cbf, 0x00035303, 0x00045506, 0x0005978a, 0x0006998d, 0x00095b90, 0x000a5d93, 0x000b9f97, 0x000c821a, 0x000d8400, 0x000ec600, 0x000fc800, 0x00010a00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 tdi_tbl40_ant0_rev0[] = { 0x0011a346, 0x00136ccf, 0x0014f5d9, 0x001641e2, 0x0017cb6b, 0x00195475, 0x001b2383, 0x001cad0c, 0x001e7616, 0x0000821f, 0x00020ba8, 0x0003d4b2, 0x00056447, 0x00072dd0, 0x0008b6da, 0x000a02e3, 0x000b8c6c, 0x000d15f6, 0x0011e484, 0x0013ae0d, 0x00153717, 0x00168320, 0x00180ca9, 0x00199633, 0x001b6548, 0x001ceed1, 0x001eb7db, 0x0000c3e4, 0x00024d6d, 0x000416f7, 0x0005a585, 0x00076f0f, 0x0008f818, 0x000a4421, 0x000bcdab, 0x000d9734, 0x00122649, 0x0013efd2, 0x001578dc, 0x0016c4e5, 0x00184e6e, 0x001a17f8, 0x001ba686, 0x001d3010, 0x001ef999, 0x00010522, 0x00028eac, 0x00045835, 0x0005e74a, 0x0007b0d3, 0x00093a5d, 0x000a85e6, 0x000c0f6f, 0x000dd8f9, 0x00126787, 0x00143111, 0x0015ba9a, 0x00170623, 0x00188fad, 0x001a5936, 0x001be84b, 0x001db1d4, 0x001f3b5e, 0x000146e7, 0x00031070, 0x000499fa, 0x00062888, 0x0007f212, 0x00097b9b, 0x000ac7a4, 0x000c50ae, 0x000e1a37, 0x0012a94c, 0x001472d5, 0x0015fc5f, 0x00174868, 0x0018d171, 0x001a9afb, 0x001c2989, 0x001df313, 0x001f7c9c, 0x000188a5, 0x000351af, 0x0004db38, 0x0006aa4d, 0x000833d7, 0x0009bd60, 0x000b0969, 0x000c9273, 0x000e5bfc, 0x00132a8a, 0x0014b414, 0x00163d9d, 0x001789a6, 0x001912b0, 0x001adc39, 0x001c6bce, 0x001e34d8, 0x001fbe61, 0x0001ca6a, 0x00039374, 0x00051cfd, 0x0006ec0b, 0x00087515, 0x0009fe9e, 0x000b4aa7, 0x000cd3b1, 0x000e9d3a, 0x00000000, 0x00000000, }; static const u32 tdi_tbl40_ant1_rev0[] = { 0x001edb36, 0x000129ca, 0x0002b353, 0x00047cdd, 0x0005c8e6, 0x000791ef, 0x00091bf9, 0x000aaa07, 0x000c3391, 0x000dfd1a, 0x00120923, 0x0013d22d, 0x00155c37, 0x0016eacb, 0x00187454, 0x001a3dde, 0x001b89e7, 0x001d12f0, 0x001f1cfa, 0x00016b88, 0x00033492, 0x0004be1b, 0x00060a24, 0x0007d32e, 0x00095d38, 0x000aec4c, 0x000c7555, 0x000e3edf, 0x00124ae8, 0x001413f1, 0x0015a37b, 0x00172c89, 0x0018b593, 0x001a419c, 0x001bcb25, 0x001d942f, 0x001f63b9, 0x0001ad4d, 0x00037657, 0x0004c260, 0x00068be9, 0x000814f3, 0x0009a47c, 0x000b2d8a, 0x000cb694, 0x000e429d, 0x00128c26, 0x001455b0, 0x0015e4ba, 0x00176e4e, 0x0018f758, 0x001a8361, 0x001c0cea, 0x001dd674, 0x001fa57d, 0x0001ee8b, 0x0003b795, 0x0005039e, 0x0006cd27, 0x000856b1, 0x0009e5c6, 0x000b6f4f, 0x000cf859, 0x000e8462, 0x00130deb, 0x00149775, 0x00162603, 0x0017af8c, 0x00193896, 0x001ac49f, 0x001c4e28, 0x001e17b2, 0x0000a6c7, 0x00023050, 0x0003f9da, 0x00054563, 0x00070eec, 0x00089876, 0x000a2704, 0x000bb08d, 0x000d3a17, 0x001185a0, 0x00134f29, 0x0014d8b3, 0x001667c8, 0x0017f151, 0x00197adb, 0x001b0664, 0x001c8fed, 0x001e5977, 0x0000e805, 0x0002718f, 0x00043b18, 0x000586a1, 0x0007502b, 0x0008d9b4, 0x000a68c9, 0x000bf252, 0x000dbbdc, 0x0011c7e5, 0x001390ee, 0x00151a78, 0x0016a906, 0x00183290, 0x0019bc19, 0x001b4822, 0x001cd12c, 0x001e9ab5, 0x00000000, 0x00000000, }; static const u16 bdi_tbl_rev0[] = { 0x0070, 0x0126, 0x012c, 0x0246, 0x048d, 0x04d2, }; static const u32 chanest_tbl_rev0[] = { 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, }; static const u8 mcs_tbl_rev0[] = { 0x00, 0x08, 0x0a, 0x10, 0x12, 0x19, 0x1a, 0x1c, 0x40, 0x48, 0x4a, 0x50, 0x52, 0x59, 0x5a, 0x5c, 0x80, 0x88, 0x8a, 0x90, 0x92, 0x99, 0x9a, 0x9c, 0xc0, 0xc8, 0xca, 0xd0, 0xd2, 0xd9, 0xda, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x09, 0x0a, 0x0c, 0x10, 0x11, 0x12, 0x14, 0x18, 0x19, 0x1a, 0x1c, 0x20, 0x21, 0x22, 0x24, 0x40, 0x41, 0x42, 0x44, 0x48, 0x49, 0x4a, 0x4c, 0x50, 0x51, 0x52, 0x54, 0x58, 0x59, 0x5a, 0x5c, 0x60, 0x61, 0x62, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u32 noise_var_tbl0_rev0[] = { 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, }; static const u32 noise_var_tbl1_rev0[] = { 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, }; static const u8 est_pwr_lut_core0_rev0[] = { 0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; static const u8 est_pwr_lut_core1_rev0[] = { 0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; static const u8 adj_pwr_lut_core0_rev0[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u8 adj_pwr_lut_core1_rev0[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u32 gainctrl_lut_core0_rev0[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2b40, 0x03cc2b3e, 0x03cc2b3d, 0x03cc2b3b, 0x03c82b44, 0x03c82b42, 0x03c82b40, 0x03c82b3e, 0x03c82b3d, 0x03c82b3b, 0x03c82b39, 0x03c82b38, 0x03c82b36, 0x03c82b34, 0x03c42b44, 0x03c42b42, 0x03c42b40, 0x03c42b3e, 0x03c42b3d, 0x03c42b3b, 0x03c42b39, 0x03c42b38, 0x03c42b36, 0x03c42b34, 0x03c42b33, 0x03c42b32, 0x03c42b30, 0x03c42b2f, 0x03c42b2d, 0x03c02b44, 0x03c02b42, 0x03c02b40, 0x03c02b3e, 0x03c02b3d, 0x03c02b3b, 0x03c02b39, 0x03c02b38, 0x03c02b36, 0x03c02b34, 0x03b02b44, 0x03b02b42, 0x03b02b40, 0x03b02b3e, 0x03b02b3d, 0x03b02b3b, 0x03b02b39, 0x03b02b38, 0x03b02b36, 0x03b02b34, 0x03b02b33, 0x03b02b32, 0x03b02b30, 0x03b02b2f, 0x03b02b2d, 0x03a02b44, 0x03a02b42, 0x03a02b40, 0x03a02b3e, 0x03a02b3d, 0x03a02b3b, 0x03a02b39, 0x03a02b38, 0x03a02b36, 0x03a02b34, 0x03902b44, 0x03902b42, 0x03902b40, 0x03902b3e, 0x03902b3d, 0x03902b3b, 0x03902b39, 0x03902b38, 0x03902b36, 0x03902b34, 0x03902b33, 0x03902b32, 0x03902b30, 0x03802b44, 0x03802b42, 0x03802b40, 0x03802b3e, 0x03802b3d, 0x03802b3b, 0x03802b39, 0x03802b38, 0x03802b36, 0x03802b34, 0x03802b33, 0x03802b32, 0x03802b30, 0x03802b2f, 0x03802b2d, 0x03802b2c, 0x03802b2b, 0x03802b2a, 0x03802b29, 0x03802b27, 0x03802b26, 0x03802b25, 0x03802b24, 0x03802b23, 0x03802b22, 0x03802b21, 0x03802b20, 0x03802b1f, 0x03802b1e, 0x03802b1e, 0x03802b1d, 0x03802b1c, 0x03802b1b, 0x03802b1a, 0x03802b1a, 0x03802b19, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x00002b00, }; static const u32 gainctrl_lut_core1_rev0[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2b40, 0x03cc2b3e, 0x03cc2b3d, 0x03cc2b3b, 0x03c82b44, 0x03c82b42, 0x03c82b40, 0x03c82b3e, 0x03c82b3d, 0x03c82b3b, 0x03c82b39, 0x03c82b38, 0x03c82b36, 0x03c82b34, 0x03c42b44, 0x03c42b42, 0x03c42b40, 0x03c42b3e, 0x03c42b3d, 0x03c42b3b, 0x03c42b39, 0x03c42b38, 0x03c42b36, 0x03c42b34, 0x03c42b33, 0x03c42b32, 0x03c42b30, 0x03c42b2f, 0x03c42b2d, 0x03c02b44, 0x03c02b42, 0x03c02b40, 0x03c02b3e, 0x03c02b3d, 0x03c02b3b, 0x03c02b39, 0x03c02b38, 0x03c02b36, 0x03c02b34, 0x03b02b44, 0x03b02b42, 0x03b02b40, 0x03b02b3e, 0x03b02b3d, 0x03b02b3b, 0x03b02b39, 0x03b02b38, 0x03b02b36, 0x03b02b34, 0x03b02b33, 0x03b02b32, 0x03b02b30, 0x03b02b2f, 0x03b02b2d, 0x03a02b44, 0x03a02b42, 0x03a02b40, 0x03a02b3e, 0x03a02b3d, 0x03a02b3b, 0x03a02b39, 0x03a02b38, 0x03a02b36, 0x03a02b34, 0x03902b44, 0x03902b42, 0x03902b40, 0x03902b3e, 0x03902b3d, 0x03902b3b, 0x03902b39, 0x03902b38, 0x03902b36, 0x03902b34, 0x03902b33, 0x03902b32, 0x03902b30, 0x03802b44, 0x03802b42, 0x03802b40, 0x03802b3e, 0x03802b3d, 0x03802b3b, 0x03802b39, 0x03802b38, 0x03802b36, 0x03802b34, 0x03802b33, 0x03802b32, 0x03802b30, 0x03802b2f, 0x03802b2d, 0x03802b2c, 0x03802b2b, 0x03802b2a, 0x03802b29, 0x03802b27, 0x03802b26, 0x03802b25, 0x03802b24, 0x03802b23, 0x03802b22, 0x03802b21, 0x03802b20, 0x03802b1f, 0x03802b1e, 0x03802b1e, 0x03802b1d, 0x03802b1c, 0x03802b1b, 0x03802b1a, 0x03802b1a, 0x03802b19, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x03802b18, 0x00002b00, }; static const u32 iq_lut_core0_rev0[] = { 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, }; static const u32 iq_lut_core1_rev0[] = { 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, }; static const u16 loft_lut_core0_rev0[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, }; static const u16 loft_lut_core1_rev0[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, }; const struct phytbl_info mimophytbl_info_rev0_volatile[] = { {&bdi_tbl_rev0, sizeof(bdi_tbl_rev0) / sizeof(bdi_tbl_rev0[0]), 21, 0, 16} , {&pltlut_tbl_rev0, sizeof(pltlut_tbl_rev0) / sizeof(pltlut_tbl_rev0[0]), 20, 0, 32} , {&gainctrl_lut_core0_rev0, sizeof(gainctrl_lut_core0_rev0) / sizeof(gainctrl_lut_core0_rev0[0]), 26, 192, 32} , {&gainctrl_lut_core1_rev0, sizeof(gainctrl_lut_core1_rev0) / sizeof(gainctrl_lut_core1_rev0[0]), 27, 192, 32} , {&est_pwr_lut_core0_rev0, sizeof(est_pwr_lut_core0_rev0) / sizeof(est_pwr_lut_core0_rev0[0]), 26, 0, 8} , {&est_pwr_lut_core1_rev0, sizeof(est_pwr_lut_core1_rev0) / sizeof(est_pwr_lut_core1_rev0[0]), 27, 0, 8} , {&adj_pwr_lut_core0_rev0, sizeof(adj_pwr_lut_core0_rev0) / sizeof(adj_pwr_lut_core0_rev0[0]), 26, 64, 8} , {&adj_pwr_lut_core1_rev0, sizeof(adj_pwr_lut_core1_rev0) / sizeof(adj_pwr_lut_core1_rev0[0]), 27, 64, 8} , {&iq_lut_core0_rev0, sizeof(iq_lut_core0_rev0) / sizeof(iq_lut_core0_rev0[0]), 26, 320, 32} , {&iq_lut_core1_rev0, sizeof(iq_lut_core1_rev0) / sizeof(iq_lut_core1_rev0[0]), 27, 320, 32} , {&loft_lut_core0_rev0, sizeof(loft_lut_core0_rev0) / sizeof(loft_lut_core0_rev0[0]), 26, 448, 16} , {&loft_lut_core1_rev0, sizeof(loft_lut_core1_rev0) / sizeof(loft_lut_core1_rev0[0]), 27, 448, 16} , }; const struct phytbl_info mimophytbl_info_rev0[] = { {&frame_struct_rev0, sizeof(frame_struct_rev0) / sizeof(frame_struct_rev0[0]), 10, 0, 32} , {&frame_lut_rev0, sizeof(frame_lut_rev0) / sizeof(frame_lut_rev0[0]), 24, 0, 8} , {&tmap_tbl_rev0, sizeof(tmap_tbl_rev0) / sizeof(tmap_tbl_rev0[0]), 12, 0, 32} , {&tdtrn_tbl_rev0, sizeof(tdtrn_tbl_rev0) / sizeof(tdtrn_tbl_rev0[0]), 14, 0, 32} , {&intlv_tbl_rev0, sizeof(intlv_tbl_rev0) / sizeof(intlv_tbl_rev0[0]), 13, 0, 32} , {&pilot_tbl_rev0, sizeof(pilot_tbl_rev0) / sizeof(pilot_tbl_rev0[0]), 11, 0, 16} , {&tdi_tbl20_ant0_rev0, sizeof(tdi_tbl20_ant0_rev0) / sizeof(tdi_tbl20_ant0_rev0[0]), 19, 128, 32} , {&tdi_tbl20_ant1_rev0, sizeof(tdi_tbl20_ant1_rev0) / sizeof(tdi_tbl20_ant1_rev0[0]), 19, 256, 32} , {&tdi_tbl40_ant0_rev0, sizeof(tdi_tbl40_ant0_rev0) / sizeof(tdi_tbl40_ant0_rev0[0]), 19, 640, 32} , {&tdi_tbl40_ant1_rev0, sizeof(tdi_tbl40_ant1_rev0) / sizeof(tdi_tbl40_ant1_rev0[0]), 19, 768, 32} , {&chanest_tbl_rev0, sizeof(chanest_tbl_rev0) / sizeof(chanest_tbl_rev0[0]), 22, 0, 32} , {&mcs_tbl_rev0, sizeof(mcs_tbl_rev0) / sizeof(mcs_tbl_rev0[0]), 18, 0, 8} , {&noise_var_tbl0_rev0, sizeof(noise_var_tbl0_rev0) / sizeof(noise_var_tbl0_rev0[0]), 16, 0, 32} , {&noise_var_tbl1_rev0, sizeof(noise_var_tbl1_rev0) / sizeof(noise_var_tbl1_rev0[0]), 16, 128, 32} , }; const u32 mimophytbl_info_sz_rev0 = sizeof(mimophytbl_info_rev0) / sizeof(mimophytbl_info_rev0[0]); const u32 mimophytbl_info_sz_rev0_volatile = sizeof(mimophytbl_info_rev0_volatile) / sizeof(mimophytbl_info_rev0_volatile[0]); static const u16 ant_swctrl_tbl_rev3[] = { 0x0082, 0x0082, 0x0211, 0x0222, 0x0328, 0x0000, 0x0000, 0x0000, 0x0144, 0x0000, 0x0000, 0x0000, 0x0188, 0x0000, 0x0000, 0x0000, 0x0082, 0x0082, 0x0211, 0x0222, 0x0328, 0x0000, 0x0000, 0x0000, 0x0144, 0x0000, 0x0000, 0x0000, 0x0188, 0x0000, 0x0000, 0x0000, }; static const u16 ant_swctrl_tbl_rev3_1[] = { 0x0022, 0x0022, 0x0011, 0x0022, 0x0022, 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, 0x0000, 0x0022, 0x0022, 0x0011, 0x0022, 0x0022, 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, 0x0000, }; static const u16 ant_swctrl_tbl_rev3_2[] = { 0x0088, 0x0088, 0x0044, 0x0088, 0x0088, 0x0000, 0x0000, 0x0000, 0x0044, 0x0000, 0x0000, 0x0000, 0x0088, 0x0000, 0x0000, 0x0000, 0x0088, 0x0088, 0x0044, 0x0088, 0x0088, 0x0000, 0x0000, 0x0000, 0x0044, 0x0000, 0x0000, 0x0000, 0x0088, 0x0000, 0x0000, 0x0000, }; static const u16 ant_swctrl_tbl_rev3_3[] = { 0x022, 0x022, 0x011, 0x022, 0x000, 0x000, 0x000, 0x000, 0x011, 0x000, 0x000, 0x000, 0x022, 0x000, 0x000, 0x3cc, 0x022, 0x022, 0x011, 0x022, 0x000, 0x000, 0x000, 0x000, 0x011, 0x000, 0x000, 0x000, 0x022, 0x000, 0x000, 0x3cc }; static const u32 frame_struct_rev3[] = { 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x09804506, 0x00100030, 0x09804507, 0x00100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a0c, 0x00100004, 0x01000a0d, 0x00100024, 0x0980450e, 0x00100034, 0x0980450f, 0x00100034, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, 0x1980c506, 0x00100030, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x01800504, 0x00100030, 0x11808505, 0x00100030, 0x29814507, 0x01100030, 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x1980c50e, 0x00100038, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x0180050c, 0x00100038, 0x1180850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x1980c506, 0x00100030, 0x1980c506, 0x00100030, 0x11808504, 0x00100030, 0x3981ca05, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x10008a04, 0x00100000, 0x3981ca05, 0x00100030, 0x1980c506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a0c, 0x00100008, 0x01000a0d, 0x00100028, 0x1980c50e, 0x00100038, 0x1980c50e, 0x00100038, 0x1180850c, 0x00100038, 0x3981ca0d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x10008a0c, 0x00100008, 0x3981ca0d, 0x00100038, 0x1980c50e, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x02001405, 0x00100040, 0x0b004a06, 0x01900060, 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, 0x43020a04, 0x00100060, 0x1b00ca05, 0x00100060, 0x23010a07, 0x01500060, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x0200140d, 0x00100050, 0x0b004a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x23010a0f, 0x01500070, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x0b004a06, 0x01900060, 0x0b004a06, 0x01900060, 0x5b02ca04, 0x00100060, 0x3b01d405, 0x00100060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x5802d404, 0x00100000, 0x3b01d405, 0x00100060, 0x0b004a06, 0x01900060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5002940c, 0x00100010, 0x3201940d, 0x00100050, 0x0b004a0e, 0x01900070, 0x0b004a0e, 0x01900070, 0x5b02ca0c, 0x00100070, 0x3b01d40d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x5802d40c, 0x00100010, 0x3b01d40d, 0x00100070, 0x0b004a0e, 0x01900070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x000f4800, 0x62031405, 0x00100040, 0x53028a06, 0x01900060, 0x53028a07, 0x01900060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x000f4808, 0x6203140d, 0x00100048, 0x53028a0e, 0x01900068, 0x53028a0f, 0x01900068, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100004, 0x11008a0d, 0x00100024, 0x1980c50e, 0x00100034, 0x2181050e, 0x00100034, 0x2181050e, 0x00100034, 0x0180050c, 0x00100038, 0x1180850d, 0x00100038, 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x0180c506, 0x00100030, 0x0180c506, 0x00100030, 0x2180c50c, 0x00100030, 0x49820a0d, 0x0016a130, 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2000ca0c, 0x00100000, 0x49820a0d, 0x0016a130, 0x1980c50e, 0x00100030, 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100008, 0x0200140d, 0x00100048, 0x0b004a0e, 0x01900068, 0x13008a0e, 0x01900068, 0x13008a0e, 0x01900068, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x03004a06, 0x01900060, 0x03004a06, 0x01900060, 0x6b030a0c, 0x00100060, 0x4b02140d, 0x0016a160, 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b03140c, 0x00100060, 0x4b02140d, 0x0016a160, 0x0b004a0e, 0x01900060, 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x53028a06, 0x01900060, 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, 0x43020a04, 0x00100060, 0x1b00ca05, 0x00100060, 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x53028a0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u16 pilot_tbl_rev3[] = { 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0xff0a, 0xff82, 0xffa0, 0xff28, 0xffff, 0xffff, 0xffff, 0xffff, 0xff82, 0xffa0, 0xff28, 0xff0a, 0xffff, 0xffff, 0xffff, 0xffff, 0xf83f, 0xfa1f, 0xfa97, 0xfab5, 0xf2bd, 0xf0bf, 0xffff, 0xffff, 0xf017, 0xf815, 0xf215, 0xf095, 0xf035, 0xf01d, 0xffff, 0xffff, 0xff08, 0xff02, 0xff80, 0xff20, 0xff08, 0xff02, 0xff80, 0xff20, 0xf01f, 0xf817, 0xfa15, 0xf295, 0xf0b5, 0xf03d, 0xffff, 0xffff, 0xf82a, 0xfa0a, 0xfa82, 0xfaa0, 0xf2a8, 0xf0aa, 0xffff, 0xffff, 0xf002, 0xf800, 0xf200, 0xf080, 0xf020, 0xf008, 0xffff, 0xffff, 0xf00a, 0xf802, 0xfa00, 0xf280, 0xf0a0, 0xf028, 0xffff, 0xffff, }; static const u32 tmap_tbl_rev3[] = { 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, 0x22222222, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x11111111, 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 intlv_tbl_rev3[] = { 0x00802070, 0x0671188d, 0x0a60192c, 0x0a300e46, 0x00c1188d, 0x080024d2, 0x00000070, }; static const u32 tdtrn_tbl_rev3[] = { 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, 0xfa58fa58, 0xf895043b, 0xff4c09c0, 0xfbc6ffa8, 0xfb84f384, 0x0798f6f9, 0x05760122, 0x058409f6, 0x0b500000, 0x05b7f542, 0x08860432, 0x06ddfee7, 0xfb84f384, 0xf9d90664, 0xf7e8025c, 0x00fff7bd, 0x05a805a8, 0xf7bd00ff, 0x025cf7e8, 0x0664f9d9, 0xf384fb84, 0xfee706dd, 0x04320886, 0xf54205b7, 0x00000b50, 0x09f60584, 0x01220576, 0xf6f90798, 0xf384fb84, 0xffa8fbc6, 0x09c0ff4c, 0x043bf895, 0x02d402d4, 0x07de0270, 0xfc96079c, 0xf90afe94, 0xfe00ff2c, 0x02d4065d, 0x092a0096, 0x0014fbb8, 0xfd2cfd2c, 0x076afb3c, 0x0096f752, 0xf991fd87, 0xfb2c0200, 0xfeb8f960, 0x08e0fc96, 0x049802a8, 0xfd2cfd2c, 0x02a80498, 0xfc9608e0, 0xf960feb8, 0x0200fb2c, 0xfd87f991, 0xf7520096, 0xfb3c076a, 0xfd2cfd2c, 0xfbb80014, 0x0096092a, 0x065d02d4, 0xff2cfe00, 0xfe94f90a, 0x079cfc96, 0x027007de, 0x02d402d4, 0x027007de, 0x079cfc96, 0xfe94f90a, 0xff2cfe00, 0x065d02d4, 0x0096092a, 0xfbb80014, 0xfd2cfd2c, 0xfb3c076a, 0xf7520096, 0xfd87f991, 0x0200fb2c, 0xf960feb8, 0xfc9608e0, 0x02a80498, 0xfd2cfd2c, 0x049802a8, 0x08e0fc96, 0xfeb8f960, 0xfb2c0200, 0xf991fd87, 0x0096f752, 0x076afb3c, 0xfd2cfd2c, 0x0014fbb8, 0x092a0096, 0x02d4065d, 0xfe00ff2c, 0xf90afe94, 0xfc96079c, 0x07de0270, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, 0x061c061c, 0xff30009d, 0xffb21141, 0xfd87fb54, 0xf65dfe59, 0x02eef99e, 0x0166f03c, 0xfff809b6, 0x000008a4, 0x000af42b, 0x00eff577, 0xfa840bf2, 0xfc02ff51, 0x08260f67, 0xfff0036f, 0x0842f9c3, 0x00000000, 0x063df7be, 0xfc910010, 0xf099f7da, 0x00af03fe, 0xf40e057c, 0x0a89ff11, 0x0bd5fff6, 0xf75c0000, 0xf64a0008, 0x0fc4fe9a, 0x0662fd12, 0x01a709a3, 0x04ac0279, 0xeebf004e, 0xff6300d0, 0xf9e4f9e4, 0x00d0ff63, 0x004eeebf, 0x027904ac, 0x09a301a7, 0xfd120662, 0xfe9a0fc4, 0x0008f64a, 0x0000f75c, 0xfff60bd5, 0xff110a89, 0x057cf40e, 0x03fe00af, 0xf7daf099, 0x0010fc91, 0xf7be063d, 0x00000000, 0xf9c30842, 0x036ffff0, 0x0f670826, 0xff51fc02, 0x0bf2fa84, 0xf57700ef, 0xf42b000a, 0x08a40000, 0x09b6fff8, 0xf03c0166, 0xf99e02ee, 0xfe59f65d, 0xfb54fd87, 0x1141ffb2, 0x009dff30, 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, 0xfa58fa58, 0xf8f0fe00, 0x0448073d, 0xfdc9fe46, 0xf9910258, 0x089d0407, 0xfd5cf71a, 0x02affde0, 0x083e0496, 0xff5a0740, 0xff7afd97, 0x00fe01f1, 0x0009082e, 0xfa94ff75, 0xfecdf8ea, 0xffb0f693, 0xfd2cfa58, 0x0433ff16, 0xfba405dd, 0xfa610341, 0x06a606cb, 0x0039fd2d, 0x0677fa97, 0x01fa05e0, 0xf896003e, 0x075a068b, 0x012cfc3e, 0xfa23f98d, 0xfc7cfd43, 0xff90fc0d, 0x01c10982, 0x00c601d6, 0xfd2cfd2c, 0x01d600c6, 0x098201c1, 0xfc0dff90, 0xfd43fc7c, 0xf98dfa23, 0xfc3e012c, 0x068b075a, 0x003ef896, 0x05e001fa, 0xfa970677, 0xfd2d0039, 0x06cb06a6, 0x0341fa61, 0x05ddfba4, 0xff160433, 0xfa58fd2c, 0xf693ffb0, 0xf8eafecd, 0xff75fa94, 0x082e0009, 0x01f100fe, 0xfd97ff7a, 0x0740ff5a, 0x0496083e, 0xfde002af, 0xf71afd5c, 0x0407089d, 0x0258f991, 0xfe46fdc9, 0x073d0448, 0xfe00f8f0, 0xfd2cfd2c, 0xfce00500, 0xfc09fddc, 0xfe680157, 0x04c70571, 0xfc3aff21, 0xfcd70228, 0x056d0277, 0x0200fe00, 0x0022f927, 0xfe3c032b, 0xfc44ff3c, 0x03e9fbdb, 0x04570313, 0x04c9ff5c, 0x000d03b8, 0xfa580000, 0xfbe900d2, 0xf9d0fe0b, 0x0125fdf9, 0x042501bf, 0x0328fa2b, 0xffa902f0, 0xfa250157, 0x0200fe00, 0x03740438, 0xff0405fd, 0x030cfe52, 0x0037fb39, 0xff6904c5, 0x04f8fd23, 0xfd31fc1b, 0xfd2cfd2c, 0xfc1bfd31, 0xfd2304f8, 0x04c5ff69, 0xfb390037, 0xfe52030c, 0x05fdff04, 0x04380374, 0xfe000200, 0x0157fa25, 0x02f0ffa9, 0xfa2b0328, 0x01bf0425, 0xfdf90125, 0xfe0bf9d0, 0x00d2fbe9, 0x0000fa58, 0x03b8000d, 0xff5c04c9, 0x03130457, 0xfbdb03e9, 0xff3cfc44, 0x032bfe3c, 0xf9270022, 0xfe000200, 0x0277056d, 0x0228fcd7, 0xff21fc3a, 0x057104c7, 0x0157fe68, 0xfddcfc09, 0x0500fce0, 0xfd2cfd2c, 0x0500fce0, 0xfddcfc09, 0x0157fe68, 0x057104c7, 0xff21fc3a, 0x0228fcd7, 0x0277056d, 0xfe000200, 0xf9270022, 0x032bfe3c, 0xff3cfc44, 0xfbdb03e9, 0x03130457, 0xff5c04c9, 0x03b8000d, 0x0000fa58, 0x00d2fbe9, 0xfe0bf9d0, 0xfdf90125, 0x01bf0425, 0xfa2b0328, 0x02f0ffa9, 0x0157fa25, 0xfe000200, 0x04380374, 0x05fdff04, 0xfe52030c, 0xfb390037, 0x04c5ff69, 0xfd2304f8, 0xfc1bfd31, 0xfd2cfd2c, 0xfd31fc1b, 0x04f8fd23, 0xff6904c5, 0x0037fb39, 0x030cfe52, 0xff0405fd, 0x03740438, 0x0200fe00, 0xfa250157, 0xffa902f0, 0x0328fa2b, 0x042501bf, 0x0125fdf9, 0xf9d0fe0b, 0xfbe900d2, 0xfa580000, 0x000d03b8, 0x04c9ff5c, 0x04570313, 0x03e9fbdb, 0xfc44ff3c, 0xfe3c032b, 0x0022f927, 0x0200fe00, 0x056d0277, 0xfcd70228, 0xfc3aff21, 0x04c70571, 0xfe680157, 0xfc09fddc, 0xfce00500, 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, }; const u32 noise_var_tbl_rev3[] = { 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, }; static const u16 mcs_tbl_rev3[] = { 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, }; static const u32 tdi_tbl20_ant0_rev3[] = { 0x00091226, 0x000a1429, 0x000b56ad, 0x000c58b0, 0x000d5ab3, 0x000e9cb6, 0x000f9eba, 0x0000c13d, 0x00020301, 0x00030504, 0x00040708, 0x0005090b, 0x00064b8e, 0x00095291, 0x000a5494, 0x000b9718, 0x000c9927, 0x000d9b2a, 0x000edd2e, 0x000fdf31, 0x000101b4, 0x000243b7, 0x000345bb, 0x000447be, 0x00058982, 0x00068c05, 0x00099309, 0x000a950c, 0x000bd78f, 0x000cd992, 0x000ddb96, 0x000f1d99, 0x00005fa8, 0x0001422c, 0x0002842f, 0x00038632, 0x00048835, 0x0005ca38, 0x0006ccbc, 0x0009d3bf, 0x000b1603, 0x000c1806, 0x000d1a0a, 0x000e1c0d, 0x000f5e10, 0x00008093, 0x00018297, 0x0002c49a, 0x0003c680, 0x0004c880, 0x00060b00, 0x00070d00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 tdi_tbl20_ant1_rev3[] = { 0x00014b26, 0x00028d29, 0x000393ad, 0x00049630, 0x0005d833, 0x0006da36, 0x00099c3a, 0x000a9e3d, 0x000bc081, 0x000cc284, 0x000dc488, 0x000f068b, 0x0000488e, 0x00018b91, 0x0002d214, 0x0003d418, 0x0004d6a7, 0x000618aa, 0x00071aae, 0x0009dcb1, 0x000b1eb4, 0x000c0137, 0x000d033b, 0x000e053e, 0x000f4702, 0x00008905, 0x00020c09, 0x0003128c, 0x0004148f, 0x00051712, 0x00065916, 0x00091b19, 0x000a1d28, 0x000b5f2c, 0x000c41af, 0x000d43b2, 0x000e85b5, 0x000f87b8, 0x0000c9bc, 0x00024cbf, 0x00035303, 0x00045506, 0x0005978a, 0x0006998d, 0x00095b90, 0x000a5d93, 0x000b9f97, 0x000c821a, 0x000d8400, 0x000ec600, 0x000fc800, 0x00010a00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 tdi_tbl40_ant0_rev3[] = { 0x0011a346, 0x00136ccf, 0x0014f5d9, 0x001641e2, 0x0017cb6b, 0x00195475, 0x001b2383, 0x001cad0c, 0x001e7616, 0x0000821f, 0x00020ba8, 0x0003d4b2, 0x00056447, 0x00072dd0, 0x0008b6da, 0x000a02e3, 0x000b8c6c, 0x000d15f6, 0x0011e484, 0x0013ae0d, 0x00153717, 0x00168320, 0x00180ca9, 0x00199633, 0x001b6548, 0x001ceed1, 0x001eb7db, 0x0000c3e4, 0x00024d6d, 0x000416f7, 0x0005a585, 0x00076f0f, 0x0008f818, 0x000a4421, 0x000bcdab, 0x000d9734, 0x00122649, 0x0013efd2, 0x001578dc, 0x0016c4e5, 0x00184e6e, 0x001a17f8, 0x001ba686, 0x001d3010, 0x001ef999, 0x00010522, 0x00028eac, 0x00045835, 0x0005e74a, 0x0007b0d3, 0x00093a5d, 0x000a85e6, 0x000c0f6f, 0x000dd8f9, 0x00126787, 0x00143111, 0x0015ba9a, 0x00170623, 0x00188fad, 0x001a5936, 0x001be84b, 0x001db1d4, 0x001f3b5e, 0x000146e7, 0x00031070, 0x000499fa, 0x00062888, 0x0007f212, 0x00097b9b, 0x000ac7a4, 0x000c50ae, 0x000e1a37, 0x0012a94c, 0x001472d5, 0x0015fc5f, 0x00174868, 0x0018d171, 0x001a9afb, 0x001c2989, 0x001df313, 0x001f7c9c, 0x000188a5, 0x000351af, 0x0004db38, 0x0006aa4d, 0x000833d7, 0x0009bd60, 0x000b0969, 0x000c9273, 0x000e5bfc, 0x00132a8a, 0x0014b414, 0x00163d9d, 0x001789a6, 0x001912b0, 0x001adc39, 0x001c6bce, 0x001e34d8, 0x001fbe61, 0x0001ca6a, 0x00039374, 0x00051cfd, 0x0006ec0b, 0x00087515, 0x0009fe9e, 0x000b4aa7, 0x000cd3b1, 0x000e9d3a, 0x00000000, 0x00000000, }; static const u32 tdi_tbl40_ant1_rev3[] = { 0x001edb36, 0x000129ca, 0x0002b353, 0x00047cdd, 0x0005c8e6, 0x000791ef, 0x00091bf9, 0x000aaa07, 0x000c3391, 0x000dfd1a, 0x00120923, 0x0013d22d, 0x00155c37, 0x0016eacb, 0x00187454, 0x001a3dde, 0x001b89e7, 0x001d12f0, 0x001f1cfa, 0x00016b88, 0x00033492, 0x0004be1b, 0x00060a24, 0x0007d32e, 0x00095d38, 0x000aec4c, 0x000c7555, 0x000e3edf, 0x00124ae8, 0x001413f1, 0x0015a37b, 0x00172c89, 0x0018b593, 0x001a419c, 0x001bcb25, 0x001d942f, 0x001f63b9, 0x0001ad4d, 0x00037657, 0x0004c260, 0x00068be9, 0x000814f3, 0x0009a47c, 0x000b2d8a, 0x000cb694, 0x000e429d, 0x00128c26, 0x001455b0, 0x0015e4ba, 0x00176e4e, 0x0018f758, 0x001a8361, 0x001c0cea, 0x001dd674, 0x001fa57d, 0x0001ee8b, 0x0003b795, 0x0005039e, 0x0006cd27, 0x000856b1, 0x0009e5c6, 0x000b6f4f, 0x000cf859, 0x000e8462, 0x00130deb, 0x00149775, 0x00162603, 0x0017af8c, 0x00193896, 0x001ac49f, 0x001c4e28, 0x001e17b2, 0x0000a6c7, 0x00023050, 0x0003f9da, 0x00054563, 0x00070eec, 0x00089876, 0x000a2704, 0x000bb08d, 0x000d3a17, 0x001185a0, 0x00134f29, 0x0014d8b3, 0x001667c8, 0x0017f151, 0x00197adb, 0x001b0664, 0x001c8fed, 0x001e5977, 0x0000e805, 0x0002718f, 0x00043b18, 0x000586a1, 0x0007502b, 0x0008d9b4, 0x000a68c9, 0x000bf252, 0x000dbbdc, 0x0011c7e5, 0x001390ee, 0x00151a78, 0x0016a906, 0x00183290, 0x0019bc19, 0x001b4822, 0x001cd12c, 0x001e9ab5, 0x00000000, 0x00000000, }; static const u32 pltlut_tbl_rev3[] = { 0x76540213, 0x62407351, 0x76543210, 0x76540213, 0x76540213, 0x76430521, }; static const u32 chanest_tbl_rev3[] = { 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, }; static const u8 frame_lut_rev3[] = { 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, 0x0a, 0x0c, 0x1c, 0x1c, 0x0b, 0x0d, 0x1e, 0x1e, 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1a, 0x1a, 0x0e, 0x10, 0x20, 0x28, 0x0f, 0x11, 0x22, 0x2a, }; static const u8 est_pwr_lut_core0_rev3[] = { 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, }; static const u8 est_pwr_lut_core1_rev3[] = { 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, }; static const u8 adj_pwr_lut_core0_rev3[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u8 adj_pwr_lut_core1_rev3[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u32 gainctrl_lut_core0_rev3[] = { 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, }; static const u32 gainctrl_lut_core1_rev3[] = { 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, }; static const u32 iq_lut_core0_rev3[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 iq_lut_core1_rev3[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u16 loft_lut_core0_rev3[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 loft_lut_core1_rev3[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 papd_comp_rfpwr_tbl_core0_rev3[] = { 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, }; static const u16 papd_comp_rfpwr_tbl_core1_rev3[] = { 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, }; static const u32 papd_comp_epsilon_tbl_core0_rev3[] = { 0x00000000, 0x00001fa0, 0x00019f78, 0x0001df7e, 0x03fa9f86, 0x03fd1f90, 0x03fe5f8a, 0x03fb1f94, 0x03fd9fa0, 0x00009f98, 0x03fd1fac, 0x03ff9fa2, 0x03fe9fae, 0x00001fae, 0x03fddfb4, 0x03ff1fb8, 0x03ff9fbc, 0x03ffdfbe, 0x03fe9fc2, 0x03fedfc6, 0x03fedfc6, 0x03ff9fc8, 0x03ff5fc6, 0x03fedfc2, 0x03ff9fc0, 0x03ff5fac, 0x03ff5fac, 0x03ff9fa2, 0x03ff9fa6, 0x03ff9faa, 0x03ff5fb0, 0x03ff5fb4, 0x03ff1fca, 0x03ff5fce, 0x03fcdfdc, 0x03fb4006, 0x00000030, 0x03ff808a, 0x03ff80da, 0x0000016c, 0x03ff8318, 0x03ff063a, 0x03fd8bd6, 0x00014ffe, 0x00034ffe, 0x00034ffe, 0x0003cffe, 0x00040ffe, 0x00040ffe, 0x0003cffe, 0x0003cffe, 0x00020ffe, 0x03fe0ffe, 0x03fdcffe, 0x03f94ffe, 0x03f54ffe, 0x03f44ffe, 0x03ef8ffe, 0x03ee0ffe, 0x03ebcffe, 0x03e8cffe, 0x03e74ffe, 0x03e4cffe, 0x03e38ffe, }; static const u32 papd_cal_scalars_tbl_core0_rev3[] = { 0x05af005a, 0x0571005e, 0x05040066, 0x04bd006c, 0x047d0072, 0x04430078, 0x03f70081, 0x03cb0087, 0x03870091, 0x035e0098, 0x032e00a1, 0x030300aa, 0x02d800b4, 0x02ae00bf, 0x028900ca, 0x026400d6, 0x024100e3, 0x022200f0, 0x020200ff, 0x01e5010e, 0x01ca011e, 0x01b0012f, 0x01990140, 0x01830153, 0x016c0168, 0x0158017d, 0x01450193, 0x013301ab, 0x012101c5, 0x011101e0, 0x010201fc, 0x00f4021a, 0x00e6011d, 0x00d9012e, 0x00cd0140, 0x00c20153, 0x00b70167, 0x00ac017c, 0x00a30193, 0x009a01ab, 0x009101c4, 0x008901df, 0x008101fb, 0x007a0219, 0x00730239, 0x006d025b, 0x0067027e, 0x006102a4, 0x005c02cc, 0x005602f6, 0x00520323, 0x004d0353, 0x00490385, 0x004503bb, 0x004103f3, 0x003d042f, 0x003a046f, 0x003704b2, 0x003404f9, 0x00310545, 0x002e0596, 0x002b05f5, 0x00290640, 0x002606a4, }; static const u32 papd_comp_epsilon_tbl_core1_rev3[] = { 0x00000000, 0x00001fa0, 0x00019f78, 0x0001df7e, 0x03fa9f86, 0x03fd1f90, 0x03fe5f8a, 0x03fb1f94, 0x03fd9fa0, 0x00009f98, 0x03fd1fac, 0x03ff9fa2, 0x03fe9fae, 0x00001fae, 0x03fddfb4, 0x03ff1fb8, 0x03ff9fbc, 0x03ffdfbe, 0x03fe9fc2, 0x03fedfc6, 0x03fedfc6, 0x03ff9fc8, 0x03ff5fc6, 0x03fedfc2, 0x03ff9fc0, 0x03ff5fac, 0x03ff5fac, 0x03ff9fa2, 0x03ff9fa6, 0x03ff9faa, 0x03ff5fb0, 0x03ff5fb4, 0x03ff1fca, 0x03ff5fce, 0x03fcdfdc, 0x03fb4006, 0x00000030, 0x03ff808a, 0x03ff80da, 0x0000016c, 0x03ff8318, 0x03ff063a, 0x03fd8bd6, 0x00014ffe, 0x00034ffe, 0x00034ffe, 0x0003cffe, 0x00040ffe, 0x00040ffe, 0x0003cffe, 0x0003cffe, 0x00020ffe, 0x03fe0ffe, 0x03fdcffe, 0x03f94ffe, 0x03f54ffe, 0x03f44ffe, 0x03ef8ffe, 0x03ee0ffe, 0x03ebcffe, 0x03e8cffe, 0x03e74ffe, 0x03e4cffe, 0x03e38ffe, }; static const u32 papd_cal_scalars_tbl_core1_rev3[] = { 0x05af005a, 0x0571005e, 0x05040066, 0x04bd006c, 0x047d0072, 0x04430078, 0x03f70081, 0x03cb0087, 0x03870091, 0x035e0098, 0x032e00a1, 0x030300aa, 0x02d800b4, 0x02ae00bf, 0x028900ca, 0x026400d6, 0x024100e3, 0x022200f0, 0x020200ff, 0x01e5010e, 0x01ca011e, 0x01b0012f, 0x01990140, 0x01830153, 0x016c0168, 0x0158017d, 0x01450193, 0x013301ab, 0x012101c5, 0x011101e0, 0x010201fc, 0x00f4021a, 0x00e6011d, 0x00d9012e, 0x00cd0140, 0x00c20153, 0x00b70167, 0x00ac017c, 0x00a30193, 0x009a01ab, 0x009101c4, 0x008901df, 0x008101fb, 0x007a0219, 0x00730239, 0x006d025b, 0x0067027e, 0x006102a4, 0x005c02cc, 0x005602f6, 0x00520323, 0x004d0353, 0x00490385, 0x004503bb, 0x004103f3, 0x003d042f, 0x003a046f, 0x003704b2, 0x003404f9, 0x00310545, 0x002e0596, 0x002b05f5, 0x00290640, 0x002606a4, }; const struct phytbl_info mimophytbl_info_rev3_volatile[] = { {&ant_swctrl_tbl_rev3, sizeof(ant_swctrl_tbl_rev3) / sizeof(ant_swctrl_tbl_rev3[0]), 9, 0, 16} , }; const struct phytbl_info mimophytbl_info_rev3_volatile1[] = { {&ant_swctrl_tbl_rev3_1, sizeof(ant_swctrl_tbl_rev3_1) / sizeof(ant_swctrl_tbl_rev3_1[0]), 9, 0, 16} , }; const struct phytbl_info mimophytbl_info_rev3_volatile2[] = { {&ant_swctrl_tbl_rev3_2, sizeof(ant_swctrl_tbl_rev3_2) / sizeof(ant_swctrl_tbl_rev3_2[0]), 9, 0, 16} , }; const struct phytbl_info mimophytbl_info_rev3_volatile3[] = { {&ant_swctrl_tbl_rev3_3, sizeof(ant_swctrl_tbl_rev3_3) / sizeof(ant_swctrl_tbl_rev3_3[0]), 9, 0, 16} , }; const struct phytbl_info mimophytbl_info_rev3[] = { {&frame_struct_rev3, sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32} , {&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]), 11, 0, 16} , {&tmap_tbl_rev3, sizeof(tmap_tbl_rev3) / sizeof(tmap_tbl_rev3[0]), 12, 0, 32} , {&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]), 13, 0, 32} , {&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]), 14, 0, 32} , {&noise_var_tbl_rev3, sizeof(noise_var_tbl_rev3) / sizeof(noise_var_tbl_rev3[0]), 16, 0, 32} , {&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0, 16} , {&tdi_tbl20_ant0_rev3, sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128, 32} , {&tdi_tbl20_ant1_rev3, sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256, 32} , {&tdi_tbl40_ant0_rev3, sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640, 32} , {&tdi_tbl40_ant1_rev3, sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768, 32} , {&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]), 20, 0, 32} , {&chanest_tbl_rev3, sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32} , {&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]), 24, 0, 8} , {&est_pwr_lut_core0_rev3, sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, 0, 8} , {&est_pwr_lut_core1_rev3, sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, 0, 8} , {&adj_pwr_lut_core0_rev3, sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, 64, 8} , {&adj_pwr_lut_core1_rev3, sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, 64, 8} , {&gainctrl_lut_core0_rev3, sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), 26, 192, 32} , {&gainctrl_lut_core1_rev3, sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), 27, 192, 32} , {&iq_lut_core0_rev3, sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} , {&iq_lut_core1_rev3, sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} , {&loft_lut_core0_rev3, sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, 16} , {&loft_lut_core1_rev3, sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, 16} }; const u32 mimophytbl_info_sz_rev3 = sizeof(mimophytbl_info_rev3) / sizeof(mimophytbl_info_rev3[0]); const u32 mimophytbl_info_sz_rev3_volatile = sizeof(mimophytbl_info_rev3_volatile) / sizeof(mimophytbl_info_rev3_volatile[0]); const u32 mimophytbl_info_sz_rev3_volatile1 = sizeof(mimophytbl_info_rev3_volatile1) / sizeof(mimophytbl_info_rev3_volatile1[0]); const u32 mimophytbl_info_sz_rev3_volatile2 = sizeof(mimophytbl_info_rev3_volatile2) / sizeof(mimophytbl_info_rev3_volatile2[0]); const u32 mimophytbl_info_sz_rev3_volatile3 = sizeof(mimophytbl_info_rev3_volatile3) / sizeof(mimophytbl_info_rev3_volatile3[0]); static const u32 tmap_tbl_rev7[] = { 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, 0x22222222, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x11111111, 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; const u32 noise_var_tbl_rev7[] = { 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, }; static const u32 papd_comp_epsilon_tbl_core0_rev7[] = { 0x00000000, 0x00000000, 0x00016023, 0x00006028, 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, }; static const u32 papd_cal_scalars_tbl_core0_rev7[] = { 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, }; static const u32 papd_comp_epsilon_tbl_core1_rev7[] = { 0x00000000, 0x00000000, 0x00016023, 0x00006028, 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, }; static const u32 papd_cal_scalars_tbl_core1_rev7[] = { 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, }; const struct phytbl_info mimophytbl_info_rev7[] = { {&frame_struct_rev3, sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32} , {&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]), 11, 0, 16} , {&tmap_tbl_rev7, sizeof(tmap_tbl_rev7) / sizeof(tmap_tbl_rev7[0]), 12, 0, 32} , {&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]), 13, 0, 32} , {&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]), 14, 0, 32} , {&noise_var_tbl_rev7, sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32} , {&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0, 16} , {&tdi_tbl20_ant0_rev3, sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128, 32} , {&tdi_tbl20_ant1_rev3, sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256, 32} , {&tdi_tbl40_ant0_rev3, sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640, 32} , {&tdi_tbl40_ant1_rev3, sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768, 32} , {&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]), 20, 0, 32} , {&chanest_tbl_rev3, sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32} , {&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]), 24, 0, 8} , {&est_pwr_lut_core0_rev3, sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, 0, 8} , {&est_pwr_lut_core1_rev3, sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, 0, 8} , {&adj_pwr_lut_core0_rev3, sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, 64, 8} , {&adj_pwr_lut_core1_rev3, sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, 64, 8} , {&gainctrl_lut_core0_rev3, sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), 26, 192, 32} , {&gainctrl_lut_core1_rev3, sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), 27, 192, 32} , {&iq_lut_core0_rev3, sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} , {&iq_lut_core1_rev3, sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} , {&loft_lut_core0_rev3, sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, 16} , {&loft_lut_core1_rev3, sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, 16} , {&papd_comp_rfpwr_tbl_core0_rev3, sizeof(papd_comp_rfpwr_tbl_core0_rev3) / sizeof(papd_comp_rfpwr_tbl_core0_rev3[0]), 26, 576, 16} , {&papd_comp_rfpwr_tbl_core1_rev3, sizeof(papd_comp_rfpwr_tbl_core1_rev3) / sizeof(papd_comp_rfpwr_tbl_core1_rev3[0]), 27, 576, 16} , {&papd_comp_epsilon_tbl_core0_rev7, sizeof(papd_comp_epsilon_tbl_core0_rev7) / sizeof(papd_comp_epsilon_tbl_core0_rev7[0]), 31, 0, 32} , {&papd_cal_scalars_tbl_core0_rev7, sizeof(papd_cal_scalars_tbl_core0_rev7) / sizeof(papd_cal_scalars_tbl_core0_rev7[0]), 32, 0, 32} , {&papd_comp_epsilon_tbl_core1_rev7, sizeof(papd_comp_epsilon_tbl_core1_rev7) / sizeof(papd_comp_epsilon_tbl_core1_rev7[0]), 33, 0, 32} , {&papd_cal_scalars_tbl_core1_rev7, sizeof(papd_cal_scalars_tbl_core1_rev7) / sizeof(papd_cal_scalars_tbl_core1_rev7[0]), 34, 0, 32} , }; const u32 mimophytbl_info_sz_rev7 = sizeof(mimophytbl_info_rev7) / sizeof(mimophytbl_info_rev7[0]); const struct phytbl_info mimophytbl_info_rev16[] = { {&noise_var_tbl_rev7, sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32} , {&est_pwr_lut_core0_rev3, sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, 0, 8} , {&est_pwr_lut_core1_rev3, sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, 0, 8} , {&adj_pwr_lut_core0_rev3, sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, 64, 8} , {&adj_pwr_lut_core1_rev3, sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, 64, 8} , {&gainctrl_lut_core0_rev3, sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), 26, 192, 32} , {&gainctrl_lut_core1_rev3, sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), 27, 192, 32} , {&iq_lut_core0_rev3, sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} , {&iq_lut_core1_rev3, sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} , {&loft_lut_core0_rev3, sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, 16} , {&loft_lut_core1_rev3, sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, 16} , }; const u32 mimophytbl_info_sz_rev16 = sizeof(mimophytbl_info_rev16) / sizeof(mimophytbl_info_rev16[0]); compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h0000644000175000017500000000424412026211315026702 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "phy_int.h" extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[]; extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250; extern const struct phytbl_info dot11lcnphytbl_info_rev0[]; extern const u32 dot11lcnphytbl_info_sz_rev0; extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[]; extern const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz; extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[]; extern const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz; extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[]; extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[]; struct lcnphy_tx_gain_tbl_entry { unsigned char gm; unsigned char pga; unsigned char pad; unsigned char dac; unsigned char bb_mult; }; extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[]; extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[]; extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[]; compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c0000644000175000017500000012717112026211315026702 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "phytbl_lcn.h" static const u32 dot11lcn_gain_tbl_rev0[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000d, 0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf, 0x000000d3, 0x00000113, 0x00000513, 0x00000913, 0x00000953, 0x00000d53, 0x00001153, 0x00001193, 0x00005193, 0x00009193, 0x0000d193, 0x00011193, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000d, 0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf, 0x000000d3, 0x00000113, 0x00000513, 0x00000913, 0x00000953, 0x00000d53, 0x00001153, 0x00005153, 0x00009153, 0x0000d153, 0x00011153, 0x00015153, 0x00019153, 0x0001d153, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 dot11lcn_gain_tbl_rev1[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000D, 0x00000011, 0x00000051, 0x00000091, 0x00000011, 0x00000051, 0x00000091, 0x000000d1, 0x00000053, 0x00000093, 0x000000d3, 0x000000d7, 0x00000117, 0x00000517, 0x00000917, 0x00000957, 0x00000d57, 0x00001157, 0x00001197, 0x00005197, 0x00009197, 0x0000d197, 0x00011197, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000D, 0x00000011, 0x00000051, 0x00000091, 0x00000011, 0x00000051, 0x00000091, 0x000000d1, 0x00000053, 0x00000093, 0x000000d3, 0x000000d7, 0x00000117, 0x00000517, 0x00000917, 0x00000957, 0x00000d57, 0x00001157, 0x00005157, 0x00009157, 0x0000d157, 0x00011157, 0x00015157, 0x00019157, 0x0001d157, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u16 dot11lcn_aux_gain_idx_tbl_rev0[] = { 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x058b, 0x058c, 0x058d, 0x058e, 0x058f, 0x0090, 0x0091, 0x0092, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, 0x019f, 0x01a0, 0x01a1, 0x01a2, 0x01a3, 0x01a4, 0x01a5, 0x0000, }; static const u32 dot11lcn_gain_idx_tbl_rev0[] = { 0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x20000000, 0x00000000, 0x30000000, 0x00000000, 0x40000000, 0x00000000, 0x50000000, 0x00000000, 0x60000000, 0x00000000, 0x70000000, 0x00000000, 0x80000000, 0x00000000, 0x90000000, 0x00000008, 0xa0000000, 0x00000008, 0xb0000000, 0x00000008, 0xc0000000, 0x00000008, 0xd0000000, 0x00000008, 0xe0000000, 0x00000008, 0xf0000000, 0x00000008, 0x00000000, 0x00000009, 0x10000000, 0x00000009, 0x20000000, 0x00000019, 0x30000000, 0x00000019, 0x40000000, 0x00000019, 0x50000000, 0x00000019, 0x60000000, 0x00000019, 0x70000000, 0x00000019, 0x80000000, 0x00000019, 0x90000000, 0x00000019, 0xa0000000, 0x00000019, 0xb0000000, 0x00000019, 0xc0000000, 0x00000019, 0xd0000000, 0x00000019, 0xe0000000, 0x00000019, 0xf0000000, 0x00000019, 0x00000000, 0x0000001a, 0x10000000, 0x0000001a, 0x20000000, 0x0000001a, 0x30000000, 0x0000001a, 0x40000000, 0x0000001a, 0x50000000, 0x00000002, 0x60000000, 0x00000002, 0x70000000, 0x00000002, 0x80000000, 0x00000002, 0x90000000, 0x00000002, 0xa0000000, 0x00000002, 0xb0000000, 0x00000002, 0xc0000000, 0x0000000a, 0xd0000000, 0x0000000a, 0xe0000000, 0x0000000a, 0xf0000000, 0x0000000a, 0x00000000, 0x0000000b, 0x10000000, 0x0000000b, 0x20000000, 0x0000000b, 0x30000000, 0x0000000b, 0x40000000, 0x0000000b, 0x50000000, 0x0000001b, 0x60000000, 0x0000001b, 0x70000000, 0x0000001b, 0x80000000, 0x0000001b, 0x90000000, 0x0000001b, 0xa0000000, 0x0000001b, 0xb0000000, 0x0000001b, 0xc0000000, 0x0000001b, 0xd0000000, 0x0000001b, 0xe0000000, 0x0000001b, 0xf0000000, 0x0000001b, 0x00000000, 0x0000001c, 0x10000000, 0x0000001c, 0x20000000, 0x0000001c, 0x30000000, 0x0000001c, 0x40000000, 0x0000001c, 0x50000000, 0x0000001c, 0x60000000, 0x0000001c, 0x70000000, 0x0000001c, 0x80000000, 0x0000001c, 0x90000000, 0x0000001c, }; static const u16 dot11lcn_aux_gain_idx_tbl_2G[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0080, 0x0081, 0x0100, 0x0101, 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, 0x0188, 0x0285, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f, 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 0x0298, 0x0299, 0x029a, 0x0000 }; static const u8 dot11lcn_gain_val_tbl_2G[] = { 0xfc, 0x02, 0x08, 0x0e, 0x13, 0x1b, 0xfc, 0x02, 0x08, 0x0e, 0x13, 0x1b, 0xfc, 0x00, 0x0c, 0x03, 0xeb, 0xfe, 0x07, 0x0b, 0x0f, 0xfb, 0xfe, 0x01, 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, 0x18, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const u32 dot11lcn_gain_idx_tbl_2G[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x00000000, 0x00000008, 0x10000000, 0x00000008, 0x00000000, 0x00000010, 0x10000000, 0x00000010, 0x00000000, 0x00000018, 0x10000000, 0x00000018, 0x20000000, 0x00000018, 0x30000000, 0x00000018, 0x40000000, 0x00000018, 0x50000000, 0x00000018, 0x60000000, 0x00000018, 0x70000000, 0x00000018, 0x80000000, 0x00000018, 0x50000000, 0x00000028, 0x90000000, 0x00000028, 0xa0000000, 0x00000028, 0xb0000000, 0x00000028, 0xc0000000, 0x00000028, 0xd0000000, 0x00000028, 0xe0000000, 0x00000028, 0xf0000000, 0x00000028, 0x00000000, 0x00000029, 0x10000000, 0x00000029, 0x20000000, 0x00000029, 0x30000000, 0x00000029, 0x40000000, 0x00000029, 0x50000000, 0x00000029, 0x60000000, 0x00000029, 0x70000000, 0x00000029, 0x80000000, 0x00000029, 0x90000000, 0x00000029, 0xa0000000, 0x00000029, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x00000000, 0x00000008, 0x10000000, 0x00000008, 0x00000000, 0x00000010, 0x10000000, 0x00000010, 0x00000000, 0x00000018, 0x10000000, 0x00000018, 0x20000000, 0x00000018, 0x30000000, 0x00000018, 0x40000000, 0x00000018, 0x50000000, 0x00000018, 0x60000000, 0x00000018, 0x70000000, 0x00000018, 0x80000000, 0x00000018, 0x50000000, 0x00000028, 0x90000000, 0x00000028, 0xa0000000, 0x00000028, 0xb0000000, 0x00000028, 0xc0000000, 0x00000028, 0xd0000000, 0x00000028, 0xe0000000, 0x00000028, 0xf0000000, 0x00000028, 0x00000000, 0x00000029, 0x10000000, 0x00000029, 0x20000000, 0x00000029, 0x30000000, 0x00000029, 0x40000000, 0x00000029, 0x50000000, 0x00000029, 0x60000000, 0x00000029, 0x70000000, 0x00000029, 0x80000000, 0x00000029, 0x90000000, 0x00000029, 0xa0000000, 0x00000029, 0xb0000000, 0x00000029, 0xc0000000, 0x00000029, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; static const u32 dot11lcn_gain_tbl_2G[] = { 0x00000000, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000d, 0x0000004d, 0x0000008d, 0x00000049, 0x00000089, 0x000000c9, 0x0000004b, 0x0000008b, 0x000000cb, 0x000000cf, 0x0000010f, 0x0000050f, 0x0000090f, 0x0000094f, 0x00000d4f, 0x0000114f, 0x0000118f, 0x0000518f, 0x0000918f, 0x0000d18f, 0x0001118f, 0x0001518f, 0x0001918f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; static const u32 dot11lcn_gain_tbl_extlna_2G[] = { 0x00000000, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000d, 0x00000003, 0x00000007, 0x0000000b, 0x0000000f, 0x0000004f, 0x0000008f, 0x000000cf, 0x0000010f, 0x0000014f, 0x0000018f, 0x0000058f, 0x0000098f, 0x00000d8f, 0x00008000, 0x00008004, 0x00008008, 0x00008001, 0x00008005, 0x00008009, 0x0000800d, 0x00008003, 0x00008007, 0x0000800b, 0x0000800f, 0x0000804f, 0x0000808f, 0x000080cf, 0x0000810f, 0x0000814f, 0x0000818f, 0x0000858f, 0x0000898f, 0x00008d8f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; static const u16 dot11lcn_aux_gain_idx_tbl_extlna_2G[] = { 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0483, 0x0484, 0x0485, 0x0486, 0x0583, 0x0584, 0x0585, 0x0587, 0x0588, 0x0589, 0x058a, 0x0687, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x068d, 0x068e, 0x068f, 0x0690, 0x0691, 0x0692, 0x0693, 0x0000 }; static const u8 dot11lcn_gain_val_tbl_extlna_2G[] = { 0xfc, 0x02, 0x08, 0x0e, 0x13, 0x1b, 0xfc, 0x02, 0x08, 0x0e, 0x13, 0x1b, 0xfc, 0x00, 0x0f, 0x03, 0xeb, 0xfe, 0x07, 0x0b, 0x0f, 0xfb, 0xfe, 0x01, 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, 0x18, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const u32 dot11lcn_gain_idx_tbl_extlna_2G[] = { 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x00000000, 0x00000040, 0x10000000, 0x00000040, 0x20000000, 0x00000040, 0x30000000, 0x00000040, 0x40000000, 0x00000040, 0x30000000, 0x00000048, 0x40000000, 0x00000048, 0x50000000, 0x00000048, 0x60000000, 0x00000048, 0x30000000, 0x00000058, 0x40000000, 0x00000058, 0x50000000, 0x00000058, 0x70000000, 0x00000058, 0x80000000, 0x00000058, 0x90000000, 0x00000058, 0xa0000000, 0x00000058, 0x70000000, 0x00000068, 0x80000000, 0x00000068, 0x90000000, 0x00000068, 0xa0000000, 0x00000068, 0xb0000000, 0x00000068, 0xc0000000, 0x00000068, 0xd0000000, 0x00000068, 0xe0000000, 0x00000068, 0xf0000000, 0x00000068, 0x00000000, 0x00000069, 0x10000000, 0x00000069, 0x20000000, 0x00000069, 0x30000000, 0x00000069, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x40000000, 0x00000041, 0x50000000, 0x00000041, 0x60000000, 0x00000041, 0x70000000, 0x00000041, 0x80000000, 0x00000041, 0x70000000, 0x00000049, 0x80000000, 0x00000049, 0x90000000, 0x00000049, 0xa0000000, 0x00000049, 0x70000000, 0x00000059, 0x80000000, 0x00000059, 0x90000000, 0x00000059, 0xb0000000, 0x00000059, 0xc0000000, 0x00000059, 0xd0000000, 0x00000059, 0xe0000000, 0x00000059, 0xb0000000, 0x00000069, 0xc0000000, 0x00000069, 0xd0000000, 0x00000069, 0xe0000000, 0x00000069, 0xf0000000, 0x00000069, 0x00000000, 0x0000006a, 0x10000000, 0x0000006a, 0x20000000, 0x0000006a, 0x30000000, 0x0000006a, 0x40000000, 0x0000006a, 0x50000000, 0x0000006a, 0x60000000, 0x0000006a, 0x70000000, 0x0000006a, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; static const u32 dot11lcn_aux_gain_idx_tbl_5G[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0186, 0x0187, 0x0188, 0x0189, 0x018a, 0x018b, 0x018c, 0x018d, 0x018e, 0x018f, 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x0000 }; static const u32 dot11lcn_gain_val_tbl_5G[] = { 0xf7, 0xfd, 0x00, 0x04, 0x04, 0x04, 0xf7, 0xfd, 0x00, 0x04, 0x04, 0x04, 0xf6, 0x00, 0x0c, 0x03, 0xeb, 0xfe, 0x06, 0x0a, 0x10, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, 0x18, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, 0x18, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const u32 dot11lcn_gain_idx_tbl_5G[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x20000000, 0x00000000, 0x30000000, 0x00000000, 0x40000000, 0x00000000, 0x30000000, 0x00000008, 0x40000000, 0x00000008, 0x50000000, 0x00000008, 0x60000000, 0x00000008, 0x70000000, 0x00000008, 0x60000000, 0x00000018, 0x70000000, 0x00000018, 0x80000000, 0x00000018, 0x90000000, 0x00000018, 0xa0000000, 0x00000018, 0xb0000000, 0x00000018, 0xc0000000, 0x00000018, 0xd0000000, 0x00000018, 0xe0000000, 0x00000018, 0xf0000000, 0x00000018, 0x00000000, 0x00000019, 0x10000000, 0x00000019, 0x20000000, 0x00000019, 0x30000000, 0x00000019, 0x40000000, 0x00000019, 0x50000000, 0x00000019, 0x60000000, 0x00000019, 0x70000000, 0x00000019, 0x80000000, 0x00000019, 0x90000000, 0x00000019, 0xa0000000, 0x00000019, 0xb0000000, 0x00000019, 0xc0000000, 0x00000019, 0xd0000000, 0x00000019, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; static const u32 dot11lcn_gain_tbl_5G[] = { 0x00000000, 0x00000040, 0x00000080, 0x00000001, 0x00000005, 0x00000009, 0x0000000d, 0x00000011, 0x00000015, 0x00000055, 0x00000095, 0x00000017, 0x0000001b, 0x0000005b, 0x0000009b, 0x000000db, 0x0000011b, 0x0000015b, 0x0000019b, 0x0000059b, 0x0000099b, 0x00000d9b, 0x0000119b, 0x0000519b, 0x0000919b, 0x0000d19b, 0x0001119b, 0x0001519b, 0x0001919b, 0x0001d19b, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[] = { {&dot11lcn_gain_tbl_rev0, sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_rev0, sizeof(dot11lcn_aux_gain_idx_tbl_rev0) / sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_rev0, sizeof(dot11lcn_gain_idx_tbl_rev0) / sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32} , }; static const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev1[] = { {&dot11lcn_gain_tbl_rev1, sizeof(dot11lcn_gain_tbl_rev1) / sizeof(dot11lcn_gain_tbl_rev1[0]), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_rev0, sizeof(dot11lcn_aux_gain_idx_tbl_rev0) / sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_rev0, sizeof(dot11lcn_gain_idx_tbl_rev0) / sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32} , }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[] = { {&dot11lcn_gain_tbl_2G, sizeof(dot11lcn_gain_tbl_2G) / sizeof(dot11lcn_gain_tbl_2G[0]), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_2G, sizeof(dot11lcn_aux_gain_idx_tbl_2G) / sizeof(dot11lcn_aux_gain_idx_tbl_2G[0]), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_2G, sizeof(dot11lcn_gain_idx_tbl_2G) / sizeof(dot11lcn_gain_idx_tbl_2G[0]), 13, 0, 32} , {&dot11lcn_gain_val_tbl_2G, sizeof(dot11lcn_gain_val_tbl_2G) / sizeof(dot11lcn_gain_val_tbl_2G[0]), 17, 0, 8} }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[] = { {&dot11lcn_gain_tbl_5G, sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_5G, sizeof(dot11lcn_aux_gain_idx_tbl_5G) / sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_5G, sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]), 13, 0, 32} , {&dot11lcn_gain_val_tbl_5G, sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]), 17, 0, 8} }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = { {&dot11lcn_gain_tbl_extlna_2G, sizeof(dot11lcn_gain_tbl_extlna_2G) / sizeof(dot11lcn_gain_tbl_extlna_2G[0]), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_extlna_2G, sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G) / sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G[0]), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_extlna_2G, sizeof(dot11lcn_gain_idx_tbl_extlna_2G) / sizeof(dot11lcn_gain_idx_tbl_extlna_2G[0]), 13, 0, 32} , {&dot11lcn_gain_val_tbl_extlna_2G, sizeof(dot11lcn_gain_val_tbl_extlna_2G) / sizeof(dot11lcn_gain_val_tbl_extlna_2G[0]), 17, 0, 8} }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = { {&dot11lcn_gain_tbl_5G, sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_5G, sizeof(dot11lcn_aux_gain_idx_tbl_5G) / sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_5G, sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]), 13, 0, 32} , {&dot11lcn_gain_val_tbl_5G, sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]), 17, 0, 8} }; const u32 dot11lcnphytbl_rx_gain_info_sz_rev0 = sizeof(dot11lcnphytbl_rx_gain_info_rev0) / sizeof(dot11lcnphytbl_rx_gain_info_rev0[0]); const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz = sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2) / sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2[0]); const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz = sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2) / sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2[0]); static const u16 dot11lcn_min_sig_sq_tbl_rev0[] = { 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, }; static const u16 dot11lcn_noise_scale_tbl_rev0[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u32 dot11lcn_fltr_ctrl_tbl_rev0[] = { 0x000141f8, 0x000021f8, 0x000021fb, 0x000041fb, 0x0001fe4b, 0x0000217b, 0x00002133, 0x000040eb, 0x0001fea3, 0x0000024b, }; static const u32 dot11lcn_ps_ctrl_tbl_rev0[] = { 0x00100001, 0x00200010, 0x00300001, 0x00400010, 0x00500022, 0x00600122, 0x00700222, 0x00800322, 0x00900422, 0x00a00522, 0x00b00622, 0x00c00722, 0x00d00822, 0x00f00922, 0x00100a22, 0x00200b22, 0x00300c22, 0x00400d22, 0x00500e22, 0x00600f22, }; static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[] = { 0x0007, 0x0005, 0x0006, 0x0004, 0x0007, 0x0005, 0x0006, 0x0004, 0x0007, 0x0005, 0x0006, 0x0004, 0x0007, 0x0005, 0x0006, 0x0004, 0x000b, 0x000b, 0x000a, 0x000a, 0x000b, 0x000b, 0x000a, 0x000a, 0x000b, 0x000b, 0x000a, 0x000a, 0x000b, 0x000b, 0x000a, 0x000a, 0x0007, 0x0005, 0x0006, 0x0004, 0x0007, 0x0005, 0x0006, 0x0004, 0x0007, 0x0005, 0x0006, 0x0004, 0x0007, 0x0005, 0x0006, 0x0004, 0x000b, 0x000b, 0x000a, 0x000a, 0x000b, 0x000b, 0x000a, 0x000a, 0x000b, 0x000b, 0x000a, 0x000a, 0x000b, 0x000b, 0x000a, 0x000a, }; static const u16 dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[] = { 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0007, 0x0002, 0x0002, 0x0007, 0x0007, 0x0002, 0x0002, 0x0007, 0x0007, 0x0002, 0x0002, 0x0007, 0x0007, 0x0002, 0x0002, 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0005, 0x0002, 0x0000, 0x0007, 0x0007, 0x0002, 0x0002, 0x0007, 0x0007, 0x0002, 0x0002, 0x0007, 0x0007, 0x0002, 0x0002, 0x0007, 0x0007, 0x0002, 0x0002, }; static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0[] = { 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, }; static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = { 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, }; static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = { 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, 0x0004, 0x0004, 0x0002, 0x0002, }; static const u8 dot11lcn_nf_table_rev0[] = { 0x5f, 0x36, 0x29, 0x1f, 0x5f, 0x36, 0x29, 0x1f, 0x5f, 0x36, 0x29, 0x1f, 0x5f, 0x36, 0x29, 0x1f, }; static const u8 dot11lcn_gain_val_tbl_rev0[] = { 0x09, 0x0f, 0x14, 0x18, 0xfe, 0x07, 0x0b, 0x0f, 0xfb, 0xfe, 0x01, 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x15, 0x18, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xeb, 0x00, 0x00, }; static const u8 dot11lcn_spur_tbl_rev0[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, }; static const u16 dot11lcn_unsup_mcs_tbl_rev0[] = { 0x001a, 0x0034, 0x004e, 0x0068, 0x009c, 0x00d0, 0x00ea, 0x0104, 0x0034, 0x0068, 0x009c, 0x00d0, 0x0138, 0x01a0, 0x01d4, 0x0208, 0x004e, 0x009c, 0x00ea, 0x0138, 0x01d4, 0x0270, 0x02be, 0x030c, 0x0068, 0x00d0, 0x0138, 0x01a0, 0x0270, 0x0340, 0x03a8, 0x0410, 0x0018, 0x009c, 0x00d0, 0x0104, 0x00ea, 0x0138, 0x0186, 0x00d0, 0x0104, 0x0104, 0x0138, 0x016c, 0x016c, 0x01a0, 0x0138, 0x0186, 0x0186, 0x01d4, 0x0222, 0x0222, 0x0270, 0x0104, 0x0138, 0x016c, 0x0138, 0x016c, 0x01a0, 0x01d4, 0x01a0, 0x01d4, 0x0208, 0x0208, 0x023c, 0x0186, 0x01d4, 0x0222, 0x01d4, 0x0222, 0x0270, 0x02be, 0x0270, 0x02be, 0x030c, 0x030c, 0x035a, 0x0036, 0x006c, 0x00a2, 0x00d8, 0x0144, 0x01b0, 0x01e6, 0x021c, 0x006c, 0x00d8, 0x0144, 0x01b0, 0x0288, 0x0360, 0x03cc, 0x0438, 0x00a2, 0x0144, 0x01e6, 0x0288, 0x03cc, 0x0510, 0x05b2, 0x0654, 0x00d8, 0x01b0, 0x0288, 0x0360, 0x0510, 0x06c0, 0x0798, 0x0870, 0x0018, 0x0144, 0x01b0, 0x021c, 0x01e6, 0x0288, 0x032a, 0x01b0, 0x021c, 0x021c, 0x0288, 0x02f4, 0x02f4, 0x0360, 0x0288, 0x032a, 0x032a, 0x03cc, 0x046e, 0x046e, 0x0510, 0x021c, 0x0288, 0x02f4, 0x0288, 0x02f4, 0x0360, 0x03cc, 0x0360, 0x03cc, 0x0438, 0x0438, 0x04a4, 0x032a, 0x03cc, 0x046e, 0x03cc, 0x046e, 0x0510, 0x05b2, 0x0510, 0x05b2, 0x0654, 0x0654, 0x06f6, }; static const u16 dot11lcn_iq_local_tbl_rev0[] = { 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u32 dot11lcn_papd_compdelta_tbl_rev0[] = { 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, }; const struct phytbl_info dot11lcnphytbl_info_rev0[] = { {&dot11lcn_min_sig_sq_tbl_rev0, sizeof(dot11lcn_min_sig_sq_tbl_rev0) / sizeof(dot11lcn_min_sig_sq_tbl_rev0[0]), 2, 0, 16} , {&dot11lcn_noise_scale_tbl_rev0, sizeof(dot11lcn_noise_scale_tbl_rev0) / sizeof(dot11lcn_noise_scale_tbl_rev0[0]), 1, 0, 16} , {&dot11lcn_fltr_ctrl_tbl_rev0, sizeof(dot11lcn_fltr_ctrl_tbl_rev0) / sizeof(dot11lcn_fltr_ctrl_tbl_rev0[0]), 11, 0, 32} , {&dot11lcn_ps_ctrl_tbl_rev0, sizeof(dot11lcn_ps_ctrl_tbl_rev0) / sizeof(dot11lcn_ps_ctrl_tbl_rev0[0]), 12, 0, 32} , {&dot11lcn_gain_idx_tbl_rev0, sizeof(dot11lcn_gain_idx_tbl_rev0) / sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_rev0, sizeof(dot11lcn_aux_gain_idx_tbl_rev0) / sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16} , {&dot11lcn_sw_ctrl_tbl_rev0, sizeof(dot11lcn_sw_ctrl_tbl_rev0) / sizeof(dot11lcn_sw_ctrl_tbl_rev0[0]), 15, 0, 16} , {&dot11lcn_nf_table_rev0, sizeof(dot11lcn_nf_table_rev0) / sizeof(dot11lcn_nf_table_rev0[0]), 16, 0, 8} , {&dot11lcn_gain_val_tbl_rev0, sizeof(dot11lcn_gain_val_tbl_rev0) / sizeof(dot11lcn_gain_val_tbl_rev0[0]), 17, 0, 8} , {&dot11lcn_gain_tbl_rev0, sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18, 0, 32} , {&dot11lcn_spur_tbl_rev0, sizeof(dot11lcn_spur_tbl_rev0) / sizeof(dot11lcn_spur_tbl_rev0[0]), 20, 0, 8} , {&dot11lcn_unsup_mcs_tbl_rev0, sizeof(dot11lcn_unsup_mcs_tbl_rev0) / sizeof(dot11lcn_unsup_mcs_tbl_rev0[0]), 23, 0, 16} , {&dot11lcn_iq_local_tbl_rev0, sizeof(dot11lcn_iq_local_tbl_rev0) / sizeof(dot11lcn_iq_local_tbl_rev0[0]), 0, 0, 16} , {&dot11lcn_papd_compdelta_tbl_rev0, sizeof(dot11lcn_papd_compdelta_tbl_rev0) / sizeof(dot11lcn_papd_compdelta_tbl_rev0[0]), 24, 0, 32} , }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = { &dot11lcn_sw_ctrl_tbl_4313_rev0, sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0) / sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16 }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = { &dot11lcn_sw_ctrl_tbl_4313_epa_rev0, sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0) / sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0[0]), 15, 0, 16 }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = { &dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo, sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo) / sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[0]), 15, 0, 16 }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = { &dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0, sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0) / sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[0]), 15, 0, 16 }; const u32 dot11lcnphytbl_info_sz_rev0 = sizeof(dot11lcnphytbl_info_rev0) / sizeof(dot11lcnphytbl_info_rev0[0]); const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = { {3, 0, 31, 0, 72}, {3, 0, 31, 0, 70}, {3, 0, 31, 0, 68}, {3, 0, 30, 0, 67}, {3, 0, 29, 0, 68}, {3, 0, 28, 0, 68}, {3, 0, 27, 0, 69}, {3, 0, 26, 0, 70}, {3, 0, 25, 0, 70}, {3, 0, 24, 0, 71}, {3, 0, 23, 0, 72}, {3, 0, 23, 0, 70}, {3, 0, 22, 0, 71}, {3, 0, 21, 0, 72}, {3, 0, 21, 0, 70}, {3, 0, 21, 0, 68}, {3, 0, 21, 0, 66}, {3, 0, 21, 0, 64}, {3, 0, 21, 0, 63}, {3, 0, 20, 0, 64}, {3, 0, 19, 0, 65}, {3, 0, 19, 0, 64}, {3, 0, 18, 0, 65}, {3, 0, 18, 0, 64}, {3, 0, 17, 0, 65}, {3, 0, 17, 0, 64}, {3, 0, 16, 0, 65}, {3, 0, 16, 0, 64}, {3, 0, 16, 0, 62}, {3, 0, 16, 0, 60}, {3, 0, 16, 0, 58}, {3, 0, 15, 0, 61}, {3, 0, 15, 0, 59}, {3, 0, 14, 0, 61}, {3, 0, 14, 0, 60}, {3, 0, 14, 0, 58}, {3, 0, 13, 0, 60}, {3, 0, 13, 0, 59}, {3, 0, 12, 0, 62}, {3, 0, 12, 0, 60}, {3, 0, 12, 0, 58}, {3, 0, 11, 0, 62}, {3, 0, 11, 0, 60}, {3, 0, 11, 0, 59}, {3, 0, 11, 0, 57}, {3, 0, 10, 0, 61}, {3, 0, 10, 0, 59}, {3, 0, 10, 0, 57}, {3, 0, 9, 0, 62}, {3, 0, 9, 0, 60}, {3, 0, 9, 0, 58}, {3, 0, 9, 0, 57}, {3, 0, 8, 0, 62}, {3, 0, 8, 0, 60}, {3, 0, 8, 0, 58}, {3, 0, 8, 0, 57}, {3, 0, 8, 0, 55}, {3, 0, 7, 0, 61}, {3, 0, 7, 0, 60}, {3, 0, 7, 0, 58}, {3, 0, 7, 0, 56}, {3, 0, 7, 0, 55}, {3, 0, 6, 0, 62}, {3, 0, 6, 0, 60}, {3, 0, 6, 0, 58}, {3, 0, 6, 0, 57}, {3, 0, 6, 0, 55}, {3, 0, 6, 0, 54}, {3, 0, 6, 0, 52}, {3, 0, 5, 0, 61}, {3, 0, 5, 0, 59}, {3, 0, 5, 0, 57}, {3, 0, 5, 0, 56}, {3, 0, 5, 0, 54}, {3, 0, 5, 0, 53}, {3, 0, 5, 0, 51}, {3, 0, 4, 0, 62}, {3, 0, 4, 0, 60}, {3, 0, 4, 0, 58}, {3, 0, 4, 0, 57}, {3, 0, 4, 0, 55}, {3, 0, 4, 0, 54}, {3, 0, 4, 0, 52}, {3, 0, 4, 0, 51}, {3, 0, 4, 0, 49}, {3, 0, 4, 0, 48}, {3, 0, 4, 0, 46}, {3, 0, 3, 0, 60}, {3, 0, 3, 0, 58}, {3, 0, 3, 0, 57}, {3, 0, 3, 0, 55}, {3, 0, 3, 0, 54}, {3, 0, 3, 0, 52}, {3, 0, 3, 0, 51}, {3, 0, 3, 0, 49}, {3, 0, 3, 0, 48}, {3, 0, 3, 0, 46}, {3, 0, 3, 0, 45}, {3, 0, 3, 0, 44}, {3, 0, 3, 0, 43}, {3, 0, 3, 0, 41}, {3, 0, 2, 0, 61}, {3, 0, 2, 0, 59}, {3, 0, 2, 0, 57}, {3, 0, 2, 0, 56}, {3, 0, 2, 0, 54}, {3, 0, 2, 0, 53}, {3, 0, 2, 0, 51}, {3, 0, 2, 0, 50}, {3, 0, 2, 0, 48}, {3, 0, 2, 0, 47}, {3, 0, 2, 0, 46}, {3, 0, 2, 0, 44}, {3, 0, 2, 0, 43}, {3, 0, 2, 0, 42}, {3, 0, 2, 0, 41}, {3, 0, 2, 0, 39}, {3, 0, 2, 0, 38}, {3, 0, 2, 0, 37}, {3, 0, 2, 0, 36}, {3, 0, 2, 0, 35}, {3, 0, 2, 0, 34}, {3, 0, 2, 0, 33}, {3, 0, 2, 0, 32}, {3, 0, 1, 0, 63}, {3, 0, 1, 0, 61}, {3, 0, 1, 0, 59}, {3, 0, 1, 0, 57}, }; const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = { {7, 0, 31, 0, 72}, {7, 0, 31, 0, 70}, {7, 0, 31, 0, 68}, {7, 0, 30, 0, 67}, {7, 0, 29, 0, 68}, {7, 0, 28, 0, 68}, {7, 0, 27, 0, 69}, {7, 0, 26, 0, 70}, {7, 0, 25, 0, 70}, {7, 0, 24, 0, 71}, {7, 0, 23, 0, 72}, {7, 0, 23, 0, 70}, {7, 0, 22, 0, 71}, {7, 0, 21, 0, 72}, {7, 0, 21, 0, 70}, {7, 0, 21, 0, 68}, {7, 0, 21, 0, 66}, {7, 0, 21, 0, 64}, {7, 0, 21, 0, 63}, {7, 0, 20, 0, 64}, {7, 0, 19, 0, 65}, {7, 0, 19, 0, 64}, {7, 0, 18, 0, 65}, {7, 0, 18, 0, 64}, {7, 0, 17, 0, 65}, {7, 0, 17, 0, 64}, {7, 0, 16, 0, 65}, {7, 0, 16, 0, 64}, {7, 0, 16, 0, 62}, {7, 0, 16, 0, 60}, {7, 0, 16, 0, 58}, {7, 0, 15, 0, 61}, {7, 0, 15, 0, 59}, {7, 0, 14, 0, 61}, {7, 0, 14, 0, 60}, {7, 0, 14, 0, 58}, {7, 0, 13, 0, 60}, {7, 0, 13, 0, 59}, {7, 0, 12, 0, 62}, {7, 0, 12, 0, 60}, {7, 0, 12, 0, 58}, {7, 0, 11, 0, 62}, {7, 0, 11, 0, 60}, {7, 0, 11, 0, 59}, {7, 0, 11, 0, 57}, {7, 0, 10, 0, 61}, {7, 0, 10, 0, 59}, {7, 0, 10, 0, 57}, {7, 0, 9, 0, 62}, {7, 0, 9, 0, 60}, {7, 0, 9, 0, 58}, {7, 0, 9, 0, 57}, {7, 0, 8, 0, 62}, {7, 0, 8, 0, 60}, {7, 0, 8, 0, 58}, {7, 0, 8, 0, 57}, {7, 0, 8, 0, 55}, {7, 0, 7, 0, 61}, {7, 0, 7, 0, 60}, {7, 0, 7, 0, 58}, {7, 0, 7, 0, 56}, {7, 0, 7, 0, 55}, {7, 0, 6, 0, 62}, {7, 0, 6, 0, 60}, {7, 0, 6, 0, 58}, {7, 0, 6, 0, 57}, {7, 0, 6, 0, 55}, {7, 0, 6, 0, 54}, {7, 0, 6, 0, 52}, {7, 0, 5, 0, 61}, {7, 0, 5, 0, 59}, {7, 0, 5, 0, 57}, {7, 0, 5, 0, 56}, {7, 0, 5, 0, 54}, {7, 0, 5, 0, 53}, {7, 0, 5, 0, 51}, {7, 0, 4, 0, 62}, {7, 0, 4, 0, 60}, {7, 0, 4, 0, 58}, {7, 0, 4, 0, 57}, {7, 0, 4, 0, 55}, {7, 0, 4, 0, 54}, {7, 0, 4, 0, 52}, {7, 0, 4, 0, 51}, {7, 0, 4, 0, 49}, {7, 0, 4, 0, 48}, {7, 0, 4, 0, 46}, {7, 0, 3, 0, 60}, {7, 0, 3, 0, 58}, {7, 0, 3, 0, 57}, {7, 0, 3, 0, 55}, {7, 0, 3, 0, 54}, {7, 0, 3, 0, 52}, {7, 0, 3, 0, 51}, {7, 0, 3, 0, 49}, {7, 0, 3, 0, 48}, {7, 0, 3, 0, 46}, {7, 0, 3, 0, 45}, {7, 0, 3, 0, 44}, {7, 0, 3, 0, 43}, {7, 0, 3, 0, 41}, {7, 0, 2, 0, 61}, {7, 0, 2, 0, 59}, {7, 0, 2, 0, 57}, {7, 0, 2, 0, 56}, {7, 0, 2, 0, 54}, {7, 0, 2, 0, 53}, {7, 0, 2, 0, 51}, {7, 0, 2, 0, 50}, {7, 0, 2, 0, 48}, {7, 0, 2, 0, 47}, {7, 0, 2, 0, 46}, {7, 0, 2, 0, 44}, {7, 0, 2, 0, 43}, {7, 0, 2, 0, 42}, {7, 0, 2, 0, 41}, {7, 0, 2, 0, 39}, {7, 0, 2, 0, 38}, {7, 0, 2, 0, 37}, {7, 0, 2, 0, 36}, {7, 0, 2, 0, 35}, {7, 0, 2, 0, 34}, {7, 0, 2, 0, 33}, {7, 0, 2, 0, 32}, {7, 0, 1, 0, 63}, {7, 0, 1, 0, 61}, {7, 0, 1, 0, 59}, {7, 0, 1, 0, 57}, }; const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = { {255, 255, 0xf0, 0, 152}, {255, 255, 0xf0, 0, 147}, {255, 255, 0xf0, 0, 143}, {255, 255, 0xf0, 0, 139}, {255, 255, 0xf0, 0, 135}, {255, 255, 0xf0, 0, 131}, {255, 255, 0xf0, 0, 128}, {255, 255, 0xf0, 0, 124}, {255, 255, 0xf0, 0, 121}, {255, 255, 0xf0, 0, 117}, {255, 255, 0xf0, 0, 114}, {255, 255, 0xf0, 0, 111}, {255, 255, 0xf0, 0, 107}, {255, 255, 0xf0, 0, 104}, {255, 255, 0xf0, 0, 101}, {255, 255, 0xf0, 0, 99}, {255, 255, 0xf0, 0, 96}, {255, 255, 0xf0, 0, 93}, {255, 255, 0xf0, 0, 90}, {255, 255, 0xf0, 0, 88}, {255, 255, 0xf0, 0, 85}, {255, 255, 0xf0, 0, 83}, {255, 255, 0xf0, 0, 81}, {255, 255, 0xf0, 0, 78}, {255, 255, 0xf0, 0, 76}, {255, 255, 0xf0, 0, 74}, {255, 255, 0xf0, 0, 72}, {255, 255, 0xf0, 0, 70}, {255, 255, 0xf0, 0, 68}, {255, 255, 0xf0, 0, 66}, {255, 255, 0xf0, 0, 64}, {255, 248, 0xf0, 0, 64}, {255, 241, 0xf0, 0, 64}, {255, 251, 0xe0, 0, 64}, {255, 244, 0xe0, 0, 64}, {255, 254, 0xd0, 0, 64}, {255, 246, 0xd0, 0, 64}, {255, 239, 0xd0, 0, 64}, {255, 249, 0xc0, 0, 64}, {255, 242, 0xc0, 0, 64}, {255, 255, 0xb0, 0, 64}, {255, 248, 0xb0, 0, 64}, {255, 241, 0xb0, 0, 64}, {255, 254, 0xa0, 0, 64}, {255, 246, 0xa0, 0, 64}, {255, 239, 0xa0, 0, 64}, {255, 255, 0x90, 0, 64}, {255, 248, 0x90, 0, 64}, {255, 241, 0x90, 0, 64}, {255, 234, 0x90, 0, 64}, {255, 255, 0x80, 0, 64}, {255, 248, 0x80, 0, 64}, {255, 241, 0x80, 0, 64}, {255, 234, 0x80, 0, 64}, {255, 255, 0x70, 0, 64}, {255, 248, 0x70, 0, 64}, {255, 241, 0x70, 0, 64}, {255, 234, 0x70, 0, 64}, {255, 227, 0x70, 0, 64}, {255, 221, 0x70, 0, 64}, {255, 215, 0x70, 0, 64}, {255, 208, 0x70, 0, 64}, {255, 203, 0x70, 0, 64}, {255, 197, 0x70, 0, 64}, {255, 255, 0x60, 0, 64}, {255, 248, 0x60, 0, 64}, {255, 241, 0x60, 0, 64}, {255, 234, 0x60, 0, 64}, {255, 227, 0x60, 0, 64}, {255, 221, 0x60, 0, 64}, {255, 255, 0x50, 0, 64}, {255, 248, 0x50, 0, 64}, {255, 241, 0x50, 0, 64}, {255, 234, 0x50, 0, 64}, {255, 227, 0x50, 0, 64}, {255, 221, 0x50, 0, 64}, {255, 215, 0x50, 0, 64}, {255, 208, 0x50, 0, 64}, {255, 255, 0x40, 0, 64}, {255, 248, 0x40, 0, 64}, {255, 241, 0x40, 0, 64}, {255, 234, 0x40, 0, 64}, {255, 227, 0x40, 0, 64}, {255, 221, 0x40, 0, 64}, {255, 215, 0x40, 0, 64}, {255, 208, 0x40, 0, 64}, {255, 203, 0x40, 0, 64}, {255, 197, 0x40, 0, 64}, {255, 255, 0x30, 0, 64}, {255, 248, 0x30, 0, 64}, {255, 241, 0x30, 0, 64}, {255, 234, 0x30, 0, 64}, {255, 227, 0x30, 0, 64}, {255, 221, 0x30, 0, 64}, {255, 215, 0x30, 0, 64}, {255, 208, 0x30, 0, 64}, {255, 203, 0x30, 0, 64}, {255, 197, 0x30, 0, 64}, {255, 191, 0x30, 0, 64}, {255, 186, 0x30, 0, 64}, {255, 181, 0x30, 0, 64}, {255, 175, 0x30, 0, 64}, {255, 255, 0x20, 0, 64}, {255, 248, 0x20, 0, 64}, {255, 241, 0x20, 0, 64}, {255, 234, 0x20, 0, 64}, {255, 227, 0x20, 0, 64}, {255, 221, 0x20, 0, 64}, {255, 215, 0x20, 0, 64}, {255, 208, 0x20, 0, 64}, {255, 203, 0x20, 0, 64}, {255, 197, 0x20, 0, 64}, {255, 191, 0x20, 0, 64}, {255, 186, 0x20, 0, 64}, {255, 181, 0x20, 0, 64}, {255, 175, 0x20, 0, 64}, {255, 170, 0x20, 0, 64}, {255, 166, 0x20, 0, 64}, {255, 161, 0x20, 0, 64}, {255, 156, 0x20, 0, 64}, {255, 152, 0x20, 0, 64}, {255, 148, 0x20, 0, 64}, {255, 143, 0x20, 0, 64}, {255, 139, 0x20, 0, 64}, {255, 135, 0x20, 0, 64}, {255, 132, 0x20, 0, 64}, {255, 255, 0x10, 0, 64}, {255, 248, 0x10, 0, 64}, }; compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h0000644000175000017500000001327612026211315026364 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define NPHY_TBL_ID_GAIN1 0 #define NPHY_TBL_ID_GAIN2 1 #define NPHY_TBL_ID_GAINBITS1 2 #define NPHY_TBL_ID_GAINBITS2 3 #define NPHY_TBL_ID_GAINLIMIT 4 #define NPHY_TBL_ID_WRSSIGainLimit 5 #define NPHY_TBL_ID_RFSEQ 7 #define NPHY_TBL_ID_AFECTRL 8 #define NPHY_TBL_ID_ANTSWCTRLLUT 9 #define NPHY_TBL_ID_IQLOCAL 15 #define NPHY_TBL_ID_NOISEVAR 16 #define NPHY_TBL_ID_SAMPLEPLAY 17 #define NPHY_TBL_ID_CORE1TXPWRCTL 26 #define NPHY_TBL_ID_CORE2TXPWRCTL 27 #define NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL 30 #define NPHY_TBL_ID_EPSILONTBL0 31 #define NPHY_TBL_ID_SCALARTBL0 32 #define NPHY_TBL_ID_EPSILONTBL1 33 #define NPHY_TBL_ID_SCALARTBL1 34 #define NPHY_TO_BPHY_OFF 0xc00 #define NPHY_BandControl_currentBand 0x0001 #define RFCC_CHIP0_PU 0x0400 #define RFCC_POR_FORCE 0x0040 #define RFCC_OE_POR_FORCE 0x0080 #define NPHY_RfctrlIntc_override_OFF 0 #define NPHY_RfctrlIntc_override_TRSW 1 #define NPHY_RfctrlIntc_override_PA 2 #define NPHY_RfctrlIntc_override_EXT_LNA_PU 3 #define NPHY_RfctrlIntc_override_EXT_LNA_GAIN 4 #define RIFS_ENABLE 0x80 #define BPHY_BAND_SEL_UP20 0x10 #define NPHY_MLenable 0x02 #define NPHY_RfseqMode_CoreActv_override 0x0001 #define NPHY_RfseqMode_Trigger_override 0x0002 #define NPHY_RfseqCoreActv_TxRxChain0 (0x11) #define NPHY_RfseqCoreActv_TxRxChain1 (0x22) #define NPHY_RfseqTrigger_rx2tx 0x0001 #define NPHY_RfseqTrigger_tx2rx 0x0002 #define NPHY_RfseqTrigger_updategainh 0x0004 #define NPHY_RfseqTrigger_updategainl 0x0008 #define NPHY_RfseqTrigger_updategainu 0x0010 #define NPHY_RfseqTrigger_reset2rx 0x0020 #define NPHY_RfseqStatus_rx2tx 0x0001 #define NPHY_RfseqStatus_tx2rx 0x0002 #define NPHY_RfseqStatus_updategainh 0x0004 #define NPHY_RfseqStatus_updategainl 0x0008 #define NPHY_RfseqStatus_updategainu 0x0010 #define NPHY_RfseqStatus_reset2rx 0x0020 #define NPHY_ClassifierCtrl_cck_en 0x1 #define NPHY_ClassifierCtrl_ofdm_en 0x2 #define NPHY_ClassifierCtrl_waited_en 0x4 #define NPHY_IQFlip_ADC1 0x0001 #define NPHY_IQFlip_ADC2 0x0010 #define NPHY_sampleCmd_STOP 0x0002 #define RX_GF_OR_MM 0x0004 #define RX_GF_MM_AUTO 0x0100 #define NPHY_iqloCalCmdGctl_IQLO_CAL_EN 0x8000 #define NPHY_IqestCmd_iqstart 0x1 #define NPHY_IqestCmd_iqMode 0x2 #define NPHY_TxPwrCtrlCmd_pwrIndex_init 0x40 #define NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 0x19 #define PRIM_SEL_UP20 0x8000 #define NPHY_RFSEQ_RX2TX 0x0 #define NPHY_RFSEQ_TX2RX 0x1 #define NPHY_RFSEQ_RESET2RX 0x2 #define NPHY_RFSEQ_UPDATEGAINH 0x3 #define NPHY_RFSEQ_UPDATEGAINL 0x4 #define NPHY_RFSEQ_UPDATEGAINU 0x5 #define NPHY_RFSEQ_CMD_NOP 0x0 #define NPHY_RFSEQ_CMD_RXG_FBW 0x1 #define NPHY_RFSEQ_CMD_TR_SWITCH 0x2 #define NPHY_RFSEQ_CMD_EXT_PA 0x3 #define NPHY_RFSEQ_CMD_RXPD_TXPD 0x4 #define NPHY_RFSEQ_CMD_TX_GAIN 0x5 #define NPHY_RFSEQ_CMD_RX_GAIN 0x6 #define NPHY_RFSEQ_CMD_SET_HPF_BW 0x7 #define NPHY_RFSEQ_CMD_CLR_HIQ_DIS 0x8 #define NPHY_RFSEQ_CMD_END 0xf #define NPHY_REV3_RFSEQ_CMD_NOP 0x0 #define NPHY_REV3_RFSEQ_CMD_RXG_FBW 0x1 #define NPHY_REV3_RFSEQ_CMD_TR_SWITCH 0x2 #define NPHY_REV3_RFSEQ_CMD_INT_PA_PU 0x3 #define NPHY_REV3_RFSEQ_CMD_EXT_PA 0x4 #define NPHY_REV3_RFSEQ_CMD_RXPD_TXPD 0x5 #define NPHY_REV3_RFSEQ_CMD_TX_GAIN 0x6 #define NPHY_REV3_RFSEQ_CMD_RX_GAIN 0x7 #define NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS 0x8 #define NPHY_REV3_RFSEQ_CMD_SET_HPF_H_HPC 0x9 #define NPHY_REV3_RFSEQ_CMD_SET_LPF_H_HPC 0xa #define NPHY_REV3_RFSEQ_CMD_SET_HPF_M_HPC 0xb #define NPHY_REV3_RFSEQ_CMD_SET_LPF_M_HPC 0xc #define NPHY_REV3_RFSEQ_CMD_SET_HPF_L_HPC 0xd #define NPHY_REV3_RFSEQ_CMD_SET_LPF_L_HPC 0xe #define NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS 0xf #define NPHY_REV3_RFSEQ_CMD_END 0x1f #define NPHY_RSSI_SEL_W1 0x0 #define NPHY_RSSI_SEL_W2 0x1 #define NPHY_RSSI_SEL_NB 0x2 #define NPHY_RSSI_SEL_IQ 0x3 #define NPHY_RSSI_SEL_TSSI_2G 0x4 #define NPHY_RSSI_SEL_TSSI_5G 0x5 #define NPHY_RSSI_SEL_TBD 0x6 #define NPHY_RAIL_I 0x0 #define NPHY_RAIL_Q 0x1 #define NPHY_FORCESIG_DECODEGATEDCLKS 0x8 #define NPHY_REV7_RfctrlOverride_cmd_rxrf_pu 0x0 #define NPHY_REV7_RfctrlOverride_cmd_rx_pu 0x1 #define NPHY_REV7_RfctrlOverride_cmd_tx_pu 0x2 #define NPHY_REV7_RfctrlOverride_cmd_rxgain 0x3 #define NPHY_REV7_RfctrlOverride_cmd_txgain 0x4 #define NPHY_REV7_RXGAINCODE_RFMXGAIN_MASK 0x000ff #define NPHY_REV7_RXGAINCODE_LPFGAIN_MASK 0x0ff00 #define NPHY_REV7_RXGAINCODE_DVGAGAIN_MASK 0xf0000 #define NPHY_REV7_TXGAINCODE_TGAIN_MASK 0x7fff #define NPHY_REV7_TXGAINCODE_LPFGAIN_MASK 0x8000 #define NPHY_REV7_TXGAINCODE_BIQ0GAIN_SHIFT 14 #define NPHY_REV7_RFCTRLOVERRIDE_ID0 0x0 #define NPHY_REV7_RFCTRLOVERRIDE_ID1 0x1 #define NPHY_REV7_RFCTRLOVERRIDE_ID2 0x2 #define NPHY_IqestIqAccLo(core) ((core == 0) ? 0x12c : 0x134) #define NPHY_IqestIqAccHi(core) ((core == 0) ? 0x12d : 0x135) #define NPHY_IqestipwrAccLo(core) ((core == 0) ? 0x12e : 0x136) #define NPHY_IqestipwrAccHi(core) ((core == 0) ? 0x12f : 0x137) #define NPHY_IqestqpwrAccLo(core) ((core == 0) ? 0x130 : 0x138) #define NPHY_IqestqpwrAccHi(core) ((core == 0) ? 0x131 : 0x139) compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h0000644000175000017500000022221012026211315026515 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_PHY_RADIO_H_ #define _BRCM_PHY_RADIO_H_ #define RADIO_IDCODE 0x01 #define RADIO_DEFAULT_CORE 0 #define RXC0_RSSI_RST 0x80 #define RXC0_MODE_RSSI 0x40 #define RXC0_MODE_OFF 0x20 #define RXC0_MODE_CM 0x10 #define RXC0_LAN_LOAD 0x08 #define RXC0_OFF_ADJ_MASK 0x07 #define TXC0_MODE_TXLPF 0x04 #define TXC0_PA_TSSI_EN 0x02 #define TXC0_TSSI_EN 0x01 #define TXC1_PA_GAIN_MASK 0x60 #define TXC1_PA_GAIN_3DB 0x40 #define TXC1_PA_GAIN_2DB 0x20 #define TXC1_TX_MIX_GAIN 0x10 #define TXC1_OFF_I_MASK 0x0c #define TXC1_OFF_Q_MASK 0x03 #define RADIO_2055_READ_OFF 0x100 #define RADIO_2057_READ_OFF 0x200 #define RADIO_2055_GEN_SPARE 0x00 #define RADIO_2055_SP_PIN_PD 0x02 #define RADIO_2055_SP_RSSI_CORE1 0x03 #define RADIO_2055_SP_PD_MISC_CORE1 0x04 #define RADIO_2055_SP_RSSI_CORE2 0x05 #define RADIO_2055_SP_PD_MISC_CORE2 0x06 #define RADIO_2055_SP_RX_GC1_CORE1 0x07 #define RADIO_2055_SP_RX_GC2_CORE1 0x08 #define RADIO_2055_SP_RX_GC1_CORE2 0x09 #define RADIO_2055_SP_RX_GC2_CORE2 0x0a #define RADIO_2055_SP_LPF_BW_SELECT_CORE1 0x0b #define RADIO_2055_SP_LPF_BW_SELECT_CORE2 0x0c #define RADIO_2055_SP_TX_GC1_CORE1 0x0d #define RADIO_2055_SP_TX_GC2_CORE1 0x0e #define RADIO_2055_SP_TX_GC1_CORE2 0x0f #define RADIO_2055_SP_TX_GC2_CORE2 0x10 #define RADIO_2055_MASTER_CNTRL1 0x11 #define RADIO_2055_MASTER_CNTRL2 0x12 #define RADIO_2055_PD_LGEN 0x13 #define RADIO_2055_PD_PLL_TS 0x14 #define RADIO_2055_PD_CORE1_LGBUF 0x15 #define RADIO_2055_PD_CORE1_TX 0x16 #define RADIO_2055_PD_CORE1_RXTX 0x17 #define RADIO_2055_PD_CORE1_RSSI_MISC 0x18 #define RADIO_2055_PD_CORE2_LGBUF 0x19 #define RADIO_2055_PD_CORE2_TX 0x1a #define RADIO_2055_PD_CORE2_RXTX 0x1b #define RADIO_2055_PD_CORE2_RSSI_MISC 0x1c #define RADIO_2055_PWRDET_LGEN 0x1d #define RADIO_2055_PWRDET_LGBUF_CORE1 0x1e #define RADIO_2055_PWRDET_RXTX_CORE1 0x1f #define RADIO_2055_PWRDET_LGBUF_CORE2 0x20 #define RADIO_2055_PWRDET_RXTX_CORE2 0x21 #define RADIO_2055_RRCCAL_CNTRL_SPARE 0x22 #define RADIO_2055_RRCCAL_N_OPT_SEL 0x23 #define RADIO_2055_CAL_MISC 0x24 #define RADIO_2055_CAL_COUNTER_OUT 0x25 #define RADIO_2055_CAL_COUNTER_OUT2 0x26 #define RADIO_2055_CAL_CVAR_CNTRL 0x27 #define RADIO_2055_CAL_RVAR_CNTRL 0x28 #define RADIO_2055_CAL_LPO_CNTRL 0x29 #define RADIO_2055_CAL_TS 0x2a #define RADIO_2055_CAL_RCCAL_READ_TS 0x2b #define RADIO_2055_CAL_RCAL_READ_TS 0x2c #define RADIO_2055_PAD_DRIVER 0x2d #define RADIO_2055_XO_CNTRL1 0x2e #define RADIO_2055_XO_CNTRL2 0x2f #define RADIO_2055_XO_REGULATOR 0x30 #define RADIO_2055_XO_MISC 0x31 #define RADIO_2055_PLL_LF_C1 0x32 #define RADIO_2055_PLL_CAL_VTH 0x33 #define RADIO_2055_PLL_LF_C2 0x34 #define RADIO_2055_PLL_REF 0x35 #define RADIO_2055_PLL_LF_R1 0x36 #define RADIO_2055_PLL_PFD_CP 0x37 #define RADIO_2055_PLL_IDAC_CPOPAMP 0x38 #define RADIO_2055_PLL_CP_REGULATOR 0x39 #define RADIO_2055_PLL_RCAL 0x3a #define RADIO_2055_RF_PLL_MOD0 0x3b #define RADIO_2055_RF_PLL_MOD1 0x3c #define RADIO_2055_RF_MMD_IDAC1 0x3d #define RADIO_2055_RF_MMD_IDAC0 0x3e #define RADIO_2055_RF_MMD_SPARE 0x3f #define RADIO_2055_VCO_CAL1 0x40 #define RADIO_2055_VCO_CAL2 0x41 #define RADIO_2055_VCO_CAL3 0x42 #define RADIO_2055_VCO_CAL4 0x43 #define RADIO_2055_VCO_CAL5 0x44 #define RADIO_2055_VCO_CAL6 0x45 #define RADIO_2055_VCO_CAL7 0x46 #define RADIO_2055_VCO_CAL8 0x47 #define RADIO_2055_VCO_CAL9 0x48 #define RADIO_2055_VCO_CAL10 0x49 #define RADIO_2055_VCO_CAL11 0x4a #define RADIO_2055_VCO_CAL12 0x4b #define RADIO_2055_VCO_CAL13 0x4c #define RADIO_2055_VCO_CAL14 0x4d #define RADIO_2055_VCO_CAL15 0x4e #define RADIO_2055_VCO_CAL16 0x4f #define RADIO_2055_VCO_KVCO 0x50 #define RADIO_2055_VCO_CAP_TAIL 0x51 #define RADIO_2055_VCO_IDAC_VCO 0x52 #define RADIO_2055_VCO_REGULATOR 0x53 #define RADIO_2055_PLL_RF_VTH 0x54 #define RADIO_2055_LGBUF_CEN_BUF 0x55 #define RADIO_2055_LGEN_TUNE1 0x56 #define RADIO_2055_LGEN_TUNE2 0x57 #define RADIO_2055_LGEN_IDAC1 0x58 #define RADIO_2055_LGEN_IDAC2 0x59 #define RADIO_2055_LGEN_BIAS_CNT 0x5a #define RADIO_2055_LGEN_BIAS_IDAC 0x5b #define RADIO_2055_LGEN_RCAL 0x5c #define RADIO_2055_LGEN_DIV 0x5d #define RADIO_2055_LGEN_SPARE2 0x5e #define RADIO_2055_CORE1_LGBUF_A_TUNE 0x5f #define RADIO_2055_CORE1_LGBUF_G_TUNE 0x60 #define RADIO_2055_CORE1_LGBUF_DIV 0x61 #define RADIO_2055_CORE1_LGBUF_A_IDAC 0x62 #define RADIO_2055_CORE1_LGBUF_G_IDAC 0x63 #define RADIO_2055_CORE1_LGBUF_IDACFIL_OVR 0x64 #define RADIO_2055_CORE1_LGBUF_SPARE 0x65 #define RADIO_2055_CORE1_RXRF_SPC1 0x66 #define RADIO_2055_CORE1_RXRF_REG1 0x67 #define RADIO_2055_CORE1_RXRF_REG2 0x68 #define RADIO_2055_CORE1_RXRF_RCAL 0x69 #define RADIO_2055_CORE1_RXBB_BUFI_LPFCMP 0x6a #define RADIO_2055_CORE1_RXBB_LPF 0x6b #define RADIO_2055_CORE1_RXBB_MIDAC_HIPAS 0x6c #define RADIO_2055_CORE1_RXBB_VGA1_IDAC 0x6d #define RADIO_2055_CORE1_RXBB_VGA2_IDAC 0x6e #define RADIO_2055_CORE1_RXBB_VGA3_IDAC 0x6f #define RADIO_2055_CORE1_RXBB_BUFO_CTRL 0x70 #define RADIO_2055_CORE1_RXBB_RCCAL_CTRL 0x71 #define RADIO_2055_CORE1_RXBB_RSSI_CTRL1 0x72 #define RADIO_2055_CORE1_RXBB_RSSI_CTRL2 0x73 #define RADIO_2055_CORE1_RXBB_RSSI_CTRL3 0x74 #define RADIO_2055_CORE1_RXBB_RSSI_CTRL4 0x75 #define RADIO_2055_CORE1_RXBB_RSSI_CTRL5 0x76 #define RADIO_2055_CORE1_RXBB_REGULATOR 0x77 #define RADIO_2055_CORE1_RXBB_SPARE1 0x78 #define RADIO_2055_CORE1_RXTXBB_RCAL 0x79 #define RADIO_2055_CORE1_TXRF_SGM_PGA 0x7a #define RADIO_2055_CORE1_TXRF_SGM_PAD 0x7b #define RADIO_2055_CORE1_TXRF_CNTR_PGA1 0x7c #define RADIO_2055_CORE1_TXRF_CNTR_PAD1 0x7d #define RADIO_2055_CORE1_TX_RFPGA_IDAC 0x7e #define RADIO_2055_CORE1_TX_PGA_PAD_TN 0x7f #define RADIO_2055_CORE1_TX_PAD_IDAC1 0x80 #define RADIO_2055_CORE1_TX_PAD_IDAC2 0x81 #define RADIO_2055_CORE1_TX_MX_BGTRIM 0x82 #define RADIO_2055_CORE1_TXRF_RCAL 0x83 #define RADIO_2055_CORE1_TXRF_PAD_TSSI1 0x84 #define RADIO_2055_CORE1_TXRF_PAD_TSSI2 0x85 #define RADIO_2055_CORE1_TX_RF_SPARE 0x86 #define RADIO_2055_CORE1_TXRF_IQCAL1 0x87 #define RADIO_2055_CORE1_TXRF_IQCAL2 0x88 #define RADIO_2055_CORE1_TXBB_RCCAL_CTRL 0x89 #define RADIO_2055_CORE1_TXBB_LPF1 0x8a #define RADIO_2055_CORE1_TX_VOS_CNCL 0x8b #define RADIO_2055_CORE1_TX_LPF_MXGM_IDAC 0x8c #define RADIO_2055_CORE1_TX_BB_MXGM 0x8d #define RADIO_2055_CORE2_LGBUF_A_TUNE 0x8e #define RADIO_2055_CORE2_LGBUF_G_TUNE 0x8f #define RADIO_2055_CORE2_LGBUF_DIV 0x90 #define RADIO_2055_CORE2_LGBUF_A_IDAC 0x91 #define RADIO_2055_CORE2_LGBUF_G_IDAC 0x92 #define RADIO_2055_CORE2_LGBUF_IDACFIL_OVR 0x93 #define RADIO_2055_CORE2_LGBUF_SPARE 0x94 #define RADIO_2055_CORE2_RXRF_SPC1 0x95 #define RADIO_2055_CORE2_RXRF_REG1 0x96 #define RADIO_2055_CORE2_RXRF_REG2 0x97 #define RADIO_2055_CORE2_RXRF_RCAL 0x98 #define RADIO_2055_CORE2_RXBB_BUFI_LPFCMP 0x99 #define RADIO_2055_CORE2_RXBB_LPF 0x9a #define RADIO_2055_CORE2_RXBB_MIDAC_HIPAS 0x9b #define RADIO_2055_CORE2_RXBB_VGA1_IDAC 0x9c #define RADIO_2055_CORE2_RXBB_VGA2_IDAC 0x9d #define RADIO_2055_CORE2_RXBB_VGA3_IDAC 0x9e #define RADIO_2055_CORE2_RXBB_BUFO_CTRL 0x9f #define RADIO_2055_CORE2_RXBB_RCCAL_CTRL 0xa0 #define RADIO_2055_CORE2_RXBB_RSSI_CTRL1 0xa1 #define RADIO_2055_CORE2_RXBB_RSSI_CTRL2 0xa2 #define RADIO_2055_CORE2_RXBB_RSSI_CTRL3 0xa3 #define RADIO_2055_CORE2_RXBB_RSSI_CTRL4 0xa4 #define RADIO_2055_CORE2_RXBB_RSSI_CTRL5 0xa5 #define RADIO_2055_CORE2_RXBB_REGULATOR 0xa6 #define RADIO_2055_CORE2_RXBB_SPARE1 0xa7 #define RADIO_2055_CORE2_RXTXBB_RCAL 0xa8 #define RADIO_2055_CORE2_TXRF_SGM_PGA 0xa9 #define RADIO_2055_CORE2_TXRF_SGM_PAD 0xaa #define RADIO_2055_CORE2_TXRF_CNTR_PGA1 0xab #define RADIO_2055_CORE2_TXRF_CNTR_PAD1 0xac #define RADIO_2055_CORE2_TX_RFPGA_IDAC 0xad #define RADIO_2055_CORE2_TX_PGA_PAD_TN 0xae #define RADIO_2055_CORE2_TX_PAD_IDAC1 0xaf #define RADIO_2055_CORE2_TX_PAD_IDAC2 0xb0 #define RADIO_2055_CORE2_TX_MX_BGTRIM 0xb1 #define RADIO_2055_CORE2_TXRF_RCAL 0xb2 #define RADIO_2055_CORE2_TXRF_PAD_TSSI1 0xb3 #define RADIO_2055_CORE2_TXRF_PAD_TSSI2 0xb4 #define RADIO_2055_CORE2_TX_RF_SPARE 0xb5 #define RADIO_2055_CORE2_TXRF_IQCAL1 0xb6 #define RADIO_2055_CORE2_TXRF_IQCAL2 0xb7 #define RADIO_2055_CORE2_TXBB_RCCAL_CTRL 0xb8 #define RADIO_2055_CORE2_TXBB_LPF1 0xb9 #define RADIO_2055_CORE2_TX_VOS_CNCL 0xba #define RADIO_2055_CORE2_TX_LPF_MXGM_IDAC 0xbb #define RADIO_2055_CORE2_TX_BB_MXGM 0xbc #define RADIO_2055_PRG_GC_HPVGA23_21 0xbd #define RADIO_2055_PRG_GC_HPVGA23_22 0xbe #define RADIO_2055_PRG_GC_HPVGA23_23 0xbf #define RADIO_2055_PRG_GC_HPVGA23_24 0xc0 #define RADIO_2055_PRG_GC_HPVGA23_25 0xc1 #define RADIO_2055_PRG_GC_HPVGA23_26 0xc2 #define RADIO_2055_PRG_GC_HPVGA23_27 0xc3 #define RADIO_2055_PRG_GC_HPVGA23_28 0xc4 #define RADIO_2055_PRG_GC_HPVGA23_29 0xc5 #define RADIO_2055_PRG_GC_HPVGA23_30 0xc6 #define RADIO_2055_CORE1_LNA_GAINBST 0xcd #define RADIO_2055_CORE1_B0_NBRSSI_VCM 0xd2 #define RADIO_2055_CORE1_GEN_SPARE2 0xd6 #define RADIO_2055_CORE2_LNA_GAINBST 0xd9 #define RADIO_2055_CORE2_B0_NBRSSI_VCM 0xde #define RADIO_2055_CORE2_GEN_SPARE2 0xe2 #define RADIO_2055_GAINBST_GAIN_DB 6 #define RADIO_2055_GAINBST_CODE 0x6 #define RADIO_2055_JTAGCTRL_MASK 0x04 #define RADIO_2055_JTAGSYNC_MASK 0x08 #define RADIO_2055_RRCAL_START 0x40 #define RADIO_2055_RRCAL_RST_N 0x01 #define RADIO_2055_CAL_LPO_ENABLE 0x80 #define RADIO_2055_RCAL_DONE 0x80 #define RADIO_2055_NBRSSI_VCM_I_MASK 0x03 #define RADIO_2055_NBRSSI_VCM_I_SHIFT 0x00 #define RADIO_2055_NBRSSI_VCM_Q_MASK 0x03 #define RADIO_2055_NBRSSI_VCM_Q_SHIFT 0x00 #define RADIO_2055_WBRSSI_VCM_IQ_MASK 0x0c #define RADIO_2055_WBRSSI_VCM_IQ_SHIFT 0x02 #define RADIO_2055_NBRSSI_PD 0x01 #define RADIO_2055_WBRSSI_G1_PD 0x04 #define RADIO_2055_WBRSSI_G2_PD 0x02 #define RADIO_2055_NBRSSI_SEL 0x01 #define RADIO_2055_WBRSSI_G1_SEL 0x04 #define RADIO_2055_WBRSSI_G2_SEL 0x02 #define RADIO_2055_COUPLE_RX_MASK 0x01 #define RADIO_2055_COUPLE_TX_MASK 0x02 #define RADIO_2055_GAINBST_DISABLE 0x02 #define RADIO_2055_GAINBST_VAL_MASK 0x07 #define RADIO_2055_RXMX_GC_MASK 0x0c #define RADIO_MIMO_CORESEL_OFF 0x0 #define RADIO_MIMO_CORESEL_CORE1 0x1 #define RADIO_MIMO_CORESEL_CORE2 0x2 #define RADIO_MIMO_CORESEL_CORE3 0x3 #define RADIO_MIMO_CORESEL_CORE4 0x4 #define RADIO_MIMO_CORESEL_ALLRX 0x5 #define RADIO_MIMO_CORESEL_ALLTX 0x6 #define RADIO_MIMO_CORESEL_ALLRXTX 0x7 #define RADIO_2064_READ_OFF 0x200 #define RADIO_2064_REG000 0x0 #define RADIO_2064_REG001 0x1 #define RADIO_2064_REG002 0x2 #define RADIO_2064_REG003 0x3 #define RADIO_2064_REG004 0x4 #define RADIO_2064_REG005 0x5 #define RADIO_2064_REG006 0x6 #define RADIO_2064_REG007 0x7 #define RADIO_2064_REG008 0x8 #define RADIO_2064_REG009 0x9 #define RADIO_2064_REG00A 0xa #define RADIO_2064_REG00B 0xb #define RADIO_2064_REG00C 0xc #define RADIO_2064_REG00D 0xd #define RADIO_2064_REG00E 0xe #define RADIO_2064_REG00F 0xf #define RADIO_2064_REG010 0x10 #define RADIO_2064_REG011 0x11 #define RADIO_2064_REG012 0x12 #define RADIO_2064_REG013 0x13 #define RADIO_2064_REG014 0x14 #define RADIO_2064_REG015 0x15 #define RADIO_2064_REG016 0x16 #define RADIO_2064_REG017 0x17 #define RADIO_2064_REG018 0x18 #define RADIO_2064_REG019 0x19 #define RADIO_2064_REG01A 0x1a #define RADIO_2064_REG01B 0x1b #define RADIO_2064_REG01C 0x1c #define RADIO_2064_REG01D 0x1d #define RADIO_2064_REG01E 0x1e #define RADIO_2064_REG01F 0x1f #define RADIO_2064_REG020 0x20 #define RADIO_2064_REG021 0x21 #define RADIO_2064_REG022 0x22 #define RADIO_2064_REG023 0x23 #define RADIO_2064_REG024 0x24 #define RADIO_2064_REG025 0x25 #define RADIO_2064_REG026 0x26 #define RADIO_2064_REG027 0x27 #define RADIO_2064_REG028 0x28 #define RADIO_2064_REG029 0x29 #define RADIO_2064_REG02A 0x2a #define RADIO_2064_REG02B 0x2b #define RADIO_2064_REG02C 0x2c #define RADIO_2064_REG02D 0x2d #define RADIO_2064_REG02E 0x2e #define RADIO_2064_REG02F 0x2f #define RADIO_2064_REG030 0x30 #define RADIO_2064_REG031 0x31 #define RADIO_2064_REG032 0x32 #define RADIO_2064_REG033 0x33 #define RADIO_2064_REG034 0x34 #define RADIO_2064_REG035 0x35 #define RADIO_2064_REG036 0x36 #define RADIO_2064_REG037 0x37 #define RADIO_2064_REG038 0x38 #define RADIO_2064_REG039 0x39 #define RADIO_2064_REG03A 0x3a #define RADIO_2064_REG03B 0x3b #define RADIO_2064_REG03C 0x3c #define RADIO_2064_REG03D 0x3d #define RADIO_2064_REG03E 0x3e #define RADIO_2064_REG03F 0x3f #define RADIO_2064_REG040 0x40 #define RADIO_2064_REG041 0x41 #define RADIO_2064_REG042 0x42 #define RADIO_2064_REG043 0x43 #define RADIO_2064_REG044 0x44 #define RADIO_2064_REG045 0x45 #define RADIO_2064_REG046 0x46 #define RADIO_2064_REG047 0x47 #define RADIO_2064_REG048 0x48 #define RADIO_2064_REG049 0x49 #define RADIO_2064_REG04A 0x4a #define RADIO_2064_REG04B 0x4b #define RADIO_2064_REG04C 0x4c #define RADIO_2064_REG04D 0x4d #define RADIO_2064_REG04E 0x4e #define RADIO_2064_REG04F 0x4f #define RADIO_2064_REG050 0x50 #define RADIO_2064_REG051 0x51 #define RADIO_2064_REG052 0x52 #define RADIO_2064_REG053 0x53 #define RADIO_2064_REG054 0x54 #define RADIO_2064_REG055 0x55 #define RADIO_2064_REG056 0x56 #define RADIO_2064_REG057 0x57 #define RADIO_2064_REG058 0x58 #define RADIO_2064_REG059 0x59 #define RADIO_2064_REG05A 0x5a #define RADIO_2064_REG05B 0x5b #define RADIO_2064_REG05C 0x5c #define RADIO_2064_REG05D 0x5d #define RADIO_2064_REG05E 0x5e #define RADIO_2064_REG05F 0x5f #define RADIO_2064_REG060 0x60 #define RADIO_2064_REG061 0x61 #define RADIO_2064_REG062 0x62 #define RADIO_2064_REG063 0x63 #define RADIO_2064_REG064 0x64 #define RADIO_2064_REG065 0x65 #define RADIO_2064_REG066 0x66 #define RADIO_2064_REG067 0x67 #define RADIO_2064_REG068 0x68 #define RADIO_2064_REG069 0x69 #define RADIO_2064_REG06A 0x6a #define RADIO_2064_REG06B 0x6b #define RADIO_2064_REG06C 0x6c #define RADIO_2064_REG06D 0x6d #define RADIO_2064_REG06E 0x6e #define RADIO_2064_REG06F 0x6f #define RADIO_2064_REG070 0x70 #define RADIO_2064_REG071 0x71 #define RADIO_2064_REG072 0x72 #define RADIO_2064_REG073 0x73 #define RADIO_2064_REG074 0x74 #define RADIO_2064_REG075 0x75 #define RADIO_2064_REG076 0x76 #define RADIO_2064_REG077 0x77 #define RADIO_2064_REG078 0x78 #define RADIO_2064_REG079 0x79 #define RADIO_2064_REG07A 0x7a #define RADIO_2064_REG07B 0x7b #define RADIO_2064_REG07C 0x7c #define RADIO_2064_REG07D 0x7d #define RADIO_2064_REG07E 0x7e #define RADIO_2064_REG07F 0x7f #define RADIO_2064_REG080 0x80 #define RADIO_2064_REG081 0x81 #define RADIO_2064_REG082 0x82 #define RADIO_2064_REG083 0x83 #define RADIO_2064_REG084 0x84 #define RADIO_2064_REG085 0x85 #define RADIO_2064_REG086 0x86 #define RADIO_2064_REG087 0x87 #define RADIO_2064_REG088 0x88 #define RADIO_2064_REG089 0x89 #define RADIO_2064_REG08A 0x8a #define RADIO_2064_REG08B 0x8b #define RADIO_2064_REG08C 0x8c #define RADIO_2064_REG08D 0x8d #define RADIO_2064_REG08E 0x8e #define RADIO_2064_REG08F 0x8f #define RADIO_2064_REG090 0x90 #define RADIO_2064_REG091 0x91 #define RADIO_2064_REG092 0x92 #define RADIO_2064_REG093 0x93 #define RADIO_2064_REG094 0x94 #define RADIO_2064_REG095 0x95 #define RADIO_2064_REG096 0x96 #define RADIO_2064_REG097 0x97 #define RADIO_2064_REG098 0x98 #define RADIO_2064_REG099 0x99 #define RADIO_2064_REG09A 0x9a #define RADIO_2064_REG09B 0x9b #define RADIO_2064_REG09C 0x9c #define RADIO_2064_REG09D 0x9d #define RADIO_2064_REG09E 0x9e #define RADIO_2064_REG09F 0x9f #define RADIO_2064_REG0A0 0xa0 #define RADIO_2064_REG0A1 0xa1 #define RADIO_2064_REG0A2 0xa2 #define RADIO_2064_REG0A3 0xa3 #define RADIO_2064_REG0A4 0xa4 #define RADIO_2064_REG0A5 0xa5 #define RADIO_2064_REG0A6 0xa6 #define RADIO_2064_REG0A7 0xa7 #define RADIO_2064_REG0A8 0xa8 #define RADIO_2064_REG0A9 0xa9 #define RADIO_2064_REG0AA 0xaa #define RADIO_2064_REG0AB 0xab #define RADIO_2064_REG0AC 0xac #define RADIO_2064_REG0AD 0xad #define RADIO_2064_REG0AE 0xae #define RADIO_2064_REG0AF 0xaf #define RADIO_2064_REG0B0 0xb0 #define RADIO_2064_REG0B1 0xb1 #define RADIO_2064_REG0B2 0xb2 #define RADIO_2064_REG0B3 0xb3 #define RADIO_2064_REG0B4 0xb4 #define RADIO_2064_REG0B5 0xb5 #define RADIO_2064_REG0B6 0xb6 #define RADIO_2064_REG0B7 0xb7 #define RADIO_2064_REG0B8 0xb8 #define RADIO_2064_REG0B9 0xb9 #define RADIO_2064_REG0BA 0xba #define RADIO_2064_REG0BB 0xbb #define RADIO_2064_REG0BC 0xbc #define RADIO_2064_REG0BD 0xbd #define RADIO_2064_REG0BE 0xbe #define RADIO_2064_REG0BF 0xbf #define RADIO_2064_REG0C0 0xc0 #define RADIO_2064_REG0C1 0xc1 #define RADIO_2064_REG0C2 0xc2 #define RADIO_2064_REG0C3 0xc3 #define RADIO_2064_REG0C4 0xc4 #define RADIO_2064_REG0C5 0xc5 #define RADIO_2064_REG0C6 0xc6 #define RADIO_2064_REG0C7 0xc7 #define RADIO_2064_REG0C8 0xc8 #define RADIO_2064_REG0C9 0xc9 #define RADIO_2064_REG0CA 0xca #define RADIO_2064_REG0CB 0xcb #define RADIO_2064_REG0CC 0xcc #define RADIO_2064_REG0CD 0xcd #define RADIO_2064_REG0CE 0xce #define RADIO_2064_REG0CF 0xcf #define RADIO_2064_REG0D0 0xd0 #define RADIO_2064_REG0D1 0xd1 #define RADIO_2064_REG0D2 0xd2 #define RADIO_2064_REG0D3 0xd3 #define RADIO_2064_REG0D4 0xd4 #define RADIO_2064_REG0D5 0xd5 #define RADIO_2064_REG0D6 0xd6 #define RADIO_2064_REG0D7 0xd7 #define RADIO_2064_REG0D8 0xd8 #define RADIO_2064_REG0D9 0xd9 #define RADIO_2064_REG0DA 0xda #define RADIO_2064_REG0DB 0xdb #define RADIO_2064_REG0DC 0xdc #define RADIO_2064_REG0DD 0xdd #define RADIO_2064_REG0DE 0xde #define RADIO_2064_REG0DF 0xdf #define RADIO_2064_REG0E0 0xe0 #define RADIO_2064_REG0E1 0xe1 #define RADIO_2064_REG0E2 0xe2 #define RADIO_2064_REG0E3 0xe3 #define RADIO_2064_REG0E4 0xe4 #define RADIO_2064_REG0E5 0xe5 #define RADIO_2064_REG0E6 0xe6 #define RADIO_2064_REG0E7 0xe7 #define RADIO_2064_REG0E8 0xe8 #define RADIO_2064_REG0E9 0xe9 #define RADIO_2064_REG0EA 0xea #define RADIO_2064_REG0EB 0xeb #define RADIO_2064_REG0EC 0xec #define RADIO_2064_REG0ED 0xed #define RADIO_2064_REG0EE 0xee #define RADIO_2064_REG0EF 0xef #define RADIO_2064_REG0F0 0xf0 #define RADIO_2064_REG0F1 0xf1 #define RADIO_2064_REG0F2 0xf2 #define RADIO_2064_REG0F3 0xf3 #define RADIO_2064_REG0F4 0xf4 #define RADIO_2064_REG0F5 0xf5 #define RADIO_2064_REG0F6 0xf6 #define RADIO_2064_REG0F7 0xf7 #define RADIO_2064_REG0F8 0xf8 #define RADIO_2064_REG0F9 0xf9 #define RADIO_2064_REG0FA 0xfa #define RADIO_2064_REG0FB 0xfb #define RADIO_2064_REG0FC 0xfc #define RADIO_2064_REG0FD 0xfd #define RADIO_2064_REG0FE 0xfe #define RADIO_2064_REG0FF 0xff #define RADIO_2064_REG100 0x100 #define RADIO_2064_REG101 0x101 #define RADIO_2064_REG102 0x102 #define RADIO_2064_REG103 0x103 #define RADIO_2064_REG104 0x104 #define RADIO_2064_REG105 0x105 #define RADIO_2064_REG106 0x106 #define RADIO_2064_REG107 0x107 #define RADIO_2064_REG108 0x108 #define RADIO_2064_REG109 0x109 #define RADIO_2064_REG10A 0x10a #define RADIO_2064_REG10B 0x10b #define RADIO_2064_REG10C 0x10c #define RADIO_2064_REG10D 0x10d #define RADIO_2064_REG10E 0x10e #define RADIO_2064_REG10F 0x10f #define RADIO_2064_REG110 0x110 #define RADIO_2064_REG111 0x111 #define RADIO_2064_REG112 0x112 #define RADIO_2064_REG113 0x113 #define RADIO_2064_REG114 0x114 #define RADIO_2064_REG115 0x115 #define RADIO_2064_REG116 0x116 #define RADIO_2064_REG117 0x117 #define RADIO_2064_REG118 0x118 #define RADIO_2064_REG119 0x119 #define RADIO_2064_REG11A 0x11a #define RADIO_2064_REG11B 0x11b #define RADIO_2064_REG11C 0x11c #define RADIO_2064_REG11D 0x11d #define RADIO_2064_REG11E 0x11e #define RADIO_2064_REG11F 0x11f #define RADIO_2064_REG120 0x120 #define RADIO_2064_REG121 0x121 #define RADIO_2064_REG122 0x122 #define RADIO_2064_REG123 0x123 #define RADIO_2064_REG124 0x124 #define RADIO_2064_REG125 0x125 #define RADIO_2064_REG126 0x126 #define RADIO_2064_REG127 0x127 #define RADIO_2064_REG128 0x128 #define RADIO_2064_REG129 0x129 #define RADIO_2064_REG12A 0x12a #define RADIO_2064_REG12B 0x12b #define RADIO_2064_REG12C 0x12c #define RADIO_2064_REG12D 0x12d #define RADIO_2064_REG12E 0x12e #define RADIO_2064_REG12F 0x12f #define RADIO_2064_REG130 0x130 #define RADIO_2056_SYN (0x0 << 12) #define RADIO_2056_TX0 (0x2 << 12) #define RADIO_2056_TX1 (0x3 << 12) #define RADIO_2056_RX0 (0x6 << 12) #define RADIO_2056_RX1 (0x7 << 12) #define RADIO_2056_ALLTX (0xe << 12) #define RADIO_2056_ALLRX (0xf << 12) #define RADIO_2056_SYN_RESERVED_ADDR0 0x0 #define RADIO_2056_SYN_IDCODE 0x1 #define RADIO_2056_SYN_RESERVED_ADDR2 0x2 #define RADIO_2056_SYN_RESERVED_ADDR3 0x3 #define RADIO_2056_SYN_RESERVED_ADDR4 0x4 #define RADIO_2056_SYN_RESERVED_ADDR5 0x5 #define RADIO_2056_SYN_RESERVED_ADDR6 0x6 #define RADIO_2056_SYN_RESERVED_ADDR7 0x7 #define RADIO_2056_SYN_COM_CTRL 0x8 #define RADIO_2056_SYN_COM_PU 0x9 #define RADIO_2056_SYN_COM_OVR 0xa #define RADIO_2056_SYN_COM_RESET 0xb #define RADIO_2056_SYN_COM_RCAL 0xc #define RADIO_2056_SYN_COM_RC_RXLPF 0xd #define RADIO_2056_SYN_COM_RC_TXLPF 0xe #define RADIO_2056_SYN_COM_RC_RXHPF 0xf #define RADIO_2056_SYN_RESERVED_ADDR16 0x10 #define RADIO_2056_SYN_RESERVED_ADDR17 0x11 #define RADIO_2056_SYN_RESERVED_ADDR18 0x12 #define RADIO_2056_SYN_RESERVED_ADDR19 0x13 #define RADIO_2056_SYN_RESERVED_ADDR20 0x14 #define RADIO_2056_SYN_RESERVED_ADDR21 0x15 #define RADIO_2056_SYN_RESERVED_ADDR22 0x16 #define RADIO_2056_SYN_RESERVED_ADDR23 0x17 #define RADIO_2056_SYN_RESERVED_ADDR24 0x18 #define RADIO_2056_SYN_RESERVED_ADDR25 0x19 #define RADIO_2056_SYN_RESERVED_ADDR26 0x1a #define RADIO_2056_SYN_RESERVED_ADDR27 0x1b #define RADIO_2056_SYN_RESERVED_ADDR28 0x1c #define RADIO_2056_SYN_RESERVED_ADDR29 0x1d #define RADIO_2056_SYN_RESERVED_ADDR30 0x1e #define RADIO_2056_SYN_RESERVED_ADDR31 0x1f #define RADIO_2056_SYN_GPIO_MASTER1 0x20 #define RADIO_2056_SYN_GPIO_MASTER2 0x21 #define RADIO_2056_SYN_TOPBIAS_MASTER 0x22 #define RADIO_2056_SYN_TOPBIAS_RCAL 0x23 #define RADIO_2056_SYN_AFEREG 0x24 #define RADIO_2056_SYN_TEMPPROCSENSE 0x25 #define RADIO_2056_SYN_TEMPPROCSENSEIDAC 0x26 #define RADIO_2056_SYN_TEMPPROCSENSERCAL 0x27 #define RADIO_2056_SYN_LPO 0x28 #define RADIO_2056_SYN_VDDCAL_MASTER 0x29 #define RADIO_2056_SYN_VDDCAL_IDAC 0x2a #define RADIO_2056_SYN_VDDCAL_STATUS 0x2b #define RADIO_2056_SYN_RCAL_MASTER 0x2c #define RADIO_2056_SYN_RCAL_CODE_OUT 0x2d #define RADIO_2056_SYN_RCCAL_CTRL0 0x2e #define RADIO_2056_SYN_RCCAL_CTRL1 0x2f #define RADIO_2056_SYN_RCCAL_CTRL2 0x30 #define RADIO_2056_SYN_RCCAL_CTRL3 0x31 #define RADIO_2056_SYN_RCCAL_CTRL4 0x32 #define RADIO_2056_SYN_RCCAL_CTRL5 0x33 #define RADIO_2056_SYN_RCCAL_CTRL6 0x34 #define RADIO_2056_SYN_RCCAL_CTRL7 0x35 #define RADIO_2056_SYN_RCCAL_CTRL8 0x36 #define RADIO_2056_SYN_RCCAL_CTRL9 0x37 #define RADIO_2056_SYN_RCCAL_CTRL10 0x38 #define RADIO_2056_SYN_RCCAL_CTRL11 0x39 #define RADIO_2056_SYN_ZCAL_SPARE1 0x3a #define RADIO_2056_SYN_ZCAL_SPARE2 0x3b #define RADIO_2056_SYN_PLL_MAST1 0x3c #define RADIO_2056_SYN_PLL_MAST2 0x3d #define RADIO_2056_SYN_PLL_MAST3 0x3e #define RADIO_2056_SYN_PLL_BIAS_RESET 0x3f #define RADIO_2056_SYN_PLL_XTAL0 0x40 #define RADIO_2056_SYN_PLL_XTAL1 0x41 #define RADIO_2056_SYN_PLL_XTAL3 0x42 #define RADIO_2056_SYN_PLL_XTAL4 0x43 #define RADIO_2056_SYN_PLL_XTAL5 0x44 #define RADIO_2056_SYN_PLL_XTAL6 0x45 #define RADIO_2056_SYN_PLL_REFDIV 0x46 #define RADIO_2056_SYN_PLL_PFD 0x47 #define RADIO_2056_SYN_PLL_CP1 0x48 #define RADIO_2056_SYN_PLL_CP2 0x49 #define RADIO_2056_SYN_PLL_CP3 0x4a #define RADIO_2056_SYN_PLL_LOOPFILTER1 0x4b #define RADIO_2056_SYN_PLL_LOOPFILTER2 0x4c #define RADIO_2056_SYN_PLL_LOOPFILTER3 0x4d #define RADIO_2056_SYN_PLL_LOOPFILTER4 0x4e #define RADIO_2056_SYN_PLL_LOOPFILTER5 0x4f #define RADIO_2056_SYN_PLL_MMD1 0x50 #define RADIO_2056_SYN_PLL_MMD2 0x51 #define RADIO_2056_SYN_PLL_VCO1 0x52 #define RADIO_2056_SYN_PLL_VCO2 0x53 #define RADIO_2056_SYN_PLL_MONITOR1 0x54 #define RADIO_2056_SYN_PLL_MONITOR2 0x55 #define RADIO_2056_SYN_PLL_VCOCAL1 0x56 #define RADIO_2056_SYN_PLL_VCOCAL2 0x57 #define RADIO_2056_SYN_PLL_VCOCAL4 0x58 #define RADIO_2056_SYN_PLL_VCOCAL5 0x59 #define RADIO_2056_SYN_PLL_VCOCAL6 0x5a #define RADIO_2056_SYN_PLL_VCOCAL7 0x5b #define RADIO_2056_SYN_PLL_VCOCAL8 0x5c #define RADIO_2056_SYN_PLL_VCOCAL9 0x5d #define RADIO_2056_SYN_PLL_VCOCAL10 0x5e #define RADIO_2056_SYN_PLL_VCOCAL11 0x5f #define RADIO_2056_SYN_PLL_VCOCAL12 0x60 #define RADIO_2056_SYN_PLL_VCOCAL13 0x61 #define RADIO_2056_SYN_PLL_VREG 0x62 #define RADIO_2056_SYN_PLL_STATUS1 0x63 #define RADIO_2056_SYN_PLL_STATUS2 0x64 #define RADIO_2056_SYN_PLL_STATUS3 0x65 #define RADIO_2056_SYN_LOGEN_PU0 0x66 #define RADIO_2056_SYN_LOGEN_PU1 0x67 #define RADIO_2056_SYN_LOGEN_PU2 0x68 #define RADIO_2056_SYN_LOGEN_PU3 0x69 #define RADIO_2056_SYN_LOGEN_PU5 0x6a #define RADIO_2056_SYN_LOGEN_PU6 0x6b #define RADIO_2056_SYN_LOGEN_PU7 0x6c #define RADIO_2056_SYN_LOGEN_PU8 0x6d #define RADIO_2056_SYN_LOGEN_BIAS_RESET 0x6e #define RADIO_2056_SYN_LOGEN_RCCR1 0x6f #define RADIO_2056_SYN_LOGEN_VCOBUF1 0x70 #define RADIO_2056_SYN_LOGEN_MIXER1 0x71 #define RADIO_2056_SYN_LOGEN_MIXER2 0x72 #define RADIO_2056_SYN_LOGEN_BUF1 0x73 #define RADIO_2056_SYN_LOGENBUF2 0x74 #define RADIO_2056_SYN_LOGEN_BUF3 0x75 #define RADIO_2056_SYN_LOGEN_BUF4 0x76 #define RADIO_2056_SYN_LOGEN_DIV1 0x77 #define RADIO_2056_SYN_LOGEN_DIV2 0x78 #define RADIO_2056_SYN_LOGEN_DIV3 0x79 #define RADIO_2056_SYN_LOGEN_ACL1 0x7a #define RADIO_2056_SYN_LOGEN_ACL2 0x7b #define RADIO_2056_SYN_LOGEN_ACL3 0x7c #define RADIO_2056_SYN_LOGEN_ACL4 0x7d #define RADIO_2056_SYN_LOGEN_ACL5 0x7e #define RADIO_2056_SYN_LOGEN_ACL6 0x7f #define RADIO_2056_SYN_LOGEN_ACLOUT 0x80 #define RADIO_2056_SYN_LOGEN_ACLCAL1 0x81 #define RADIO_2056_SYN_LOGEN_ACLCAL2 0x82 #define RADIO_2056_SYN_LOGEN_ACLCAL3 0x83 #define RADIO_2056_SYN_CALEN 0x84 #define RADIO_2056_SYN_LOGEN_PEAKDET1 0x85 #define RADIO_2056_SYN_LOGEN_CORE_ACL_OVR 0x86 #define RADIO_2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 #define RADIO_2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 #define RADIO_2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 #define RADIO_2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8a #define RADIO_2056_SYN_LOGEN_VCOBUF2 0x8b #define RADIO_2056_SYN_LOGEN_MIXER3 0x8c #define RADIO_2056_SYN_LOGEN_BUF5 0x8d #define RADIO_2056_SYN_LOGEN_BUF6 0x8e #define RADIO_2056_SYN_LOGEN_CBUFRX1 0x8f #define RADIO_2056_SYN_LOGEN_CBUFRX2 0x90 #define RADIO_2056_SYN_LOGEN_CBUFRX3 0x91 #define RADIO_2056_SYN_LOGEN_CBUFRX4 0x92 #define RADIO_2056_SYN_LOGEN_CBUFTX1 0x93 #define RADIO_2056_SYN_LOGEN_CBUFTX2 0x94 #define RADIO_2056_SYN_LOGEN_CBUFTX3 0x95 #define RADIO_2056_SYN_LOGEN_CBUFTX4 0x96 #define RADIO_2056_SYN_LOGEN_CMOSRX1 0x97 #define RADIO_2056_SYN_LOGEN_CMOSRX2 0x98 #define RADIO_2056_SYN_LOGEN_CMOSRX3 0x99 #define RADIO_2056_SYN_LOGEN_CMOSRX4 0x9a #define RADIO_2056_SYN_LOGEN_CMOSTX1 0x9b #define RADIO_2056_SYN_LOGEN_CMOSTX2 0x9c #define RADIO_2056_SYN_LOGEN_CMOSTX3 0x9d #define RADIO_2056_SYN_LOGEN_CMOSTX4 0x9e #define RADIO_2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9f #define RADIO_2056_SYN_LOGEN_MIXER3_OVRVAL 0xa0 #define RADIO_2056_SYN_LOGEN_BUF5_OVRVAL 0xa1 #define RADIO_2056_SYN_LOGEN_BUF6_OVRVAL 0xa2 #define RADIO_2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xa3 #define RADIO_2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xa4 #define RADIO_2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xa5 #define RADIO_2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xa6 #define RADIO_2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xa7 #define RADIO_2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xa8 #define RADIO_2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xa9 #define RADIO_2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xaa #define RADIO_2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xab #define RADIO_2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xac #define RADIO_2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xad #define RADIO_2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xae #define RADIO_2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xaf #define RADIO_2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xb0 #define RADIO_2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xb1 #define RADIO_2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xb2 #define RADIO_2056_SYN_LOGEN_ACL_WAITCNT 0xb3 #define RADIO_2056_SYN_LOGEN_CORE_CALVALID 0xb4 #define RADIO_2056_SYN_LOGEN_RX_CMOS_CALVALID 0xb5 #define RADIO_2056_SYN_LOGEN_TX_CMOS_VALID 0xb6 #define RADIO_2056_TX_RESERVED_ADDR0 0x0 #define RADIO_2056_TX_IDCODE 0x1 #define RADIO_2056_TX_RESERVED_ADDR2 0x2 #define RADIO_2056_TX_RESERVED_ADDR3 0x3 #define RADIO_2056_TX_RESERVED_ADDR4 0x4 #define RADIO_2056_TX_RESERVED_ADDR5 0x5 #define RADIO_2056_TX_RESERVED_ADDR6 0x6 #define RADIO_2056_TX_RESERVED_ADDR7 0x7 #define RADIO_2056_TX_COM_CTRL 0x8 #define RADIO_2056_TX_COM_PU 0x9 #define RADIO_2056_TX_COM_OVR 0xa #define RADIO_2056_TX_COM_RESET 0xb #define RADIO_2056_TX_COM_RCAL 0xc #define RADIO_2056_TX_COM_RC_RXLPF 0xd #define RADIO_2056_TX_COM_RC_TXLPF 0xe #define RADIO_2056_TX_COM_RC_RXHPF 0xf #define RADIO_2056_TX_RESERVED_ADDR16 0x10 #define RADIO_2056_TX_RESERVED_ADDR17 0x11 #define RADIO_2056_TX_RESERVED_ADDR18 0x12 #define RADIO_2056_TX_RESERVED_ADDR19 0x13 #define RADIO_2056_TX_RESERVED_ADDR20 0x14 #define RADIO_2056_TX_RESERVED_ADDR21 0x15 #define RADIO_2056_TX_RESERVED_ADDR22 0x16 #define RADIO_2056_TX_RESERVED_ADDR23 0x17 #define RADIO_2056_TX_RESERVED_ADDR24 0x18 #define RADIO_2056_TX_RESERVED_ADDR25 0x19 #define RADIO_2056_TX_RESERVED_ADDR26 0x1a #define RADIO_2056_TX_RESERVED_ADDR27 0x1b #define RADIO_2056_TX_RESERVED_ADDR28 0x1c #define RADIO_2056_TX_RESERVED_ADDR29 0x1d #define RADIO_2056_TX_RESERVED_ADDR30 0x1e #define RADIO_2056_TX_RESERVED_ADDR31 0x1f #define RADIO_2056_TX_IQCAL_GAIN_BW 0x20 #define RADIO_2056_TX_LOFT_FINE_I 0x21 #define RADIO_2056_TX_LOFT_FINE_Q 0x22 #define RADIO_2056_TX_LOFT_COARSE_I 0x23 #define RADIO_2056_TX_LOFT_COARSE_Q 0x24 #define RADIO_2056_TX_TX_COM_MASTER1 0x25 #define RADIO_2056_TX_TX_COM_MASTER2 0x26 #define RADIO_2056_TX_RXIQCAL_TXMUX 0x27 #define RADIO_2056_TX_TX_SSI_MASTER 0x28 #define RADIO_2056_TX_IQCAL_VCM_HG 0x29 #define RADIO_2056_TX_IQCAL_IDAC 0x2a #define RADIO_2056_TX_TSSI_VCM 0x2b #define RADIO_2056_TX_TX_AMP_DET 0x2c #define RADIO_2056_TX_TX_SSI_MUX 0x2d #define RADIO_2056_TX_TSSIA 0x2e #define RADIO_2056_TX_TSSIG 0x2f #define RADIO_2056_TX_TSSI_MISC1 0x30 #define RADIO_2056_TX_TSSI_MISC2 0x31 #define RADIO_2056_TX_TSSI_MISC3 0x32 #define RADIO_2056_TX_PA_SPARE1 0x33 #define RADIO_2056_TX_PA_SPARE2 0x34 #define RADIO_2056_TX_INTPAA_MASTER 0x35 #define RADIO_2056_TX_INTPAA_GAIN 0x36 #define RADIO_2056_TX_INTPAA_BOOST_TUNE 0x37 #define RADIO_2056_TX_INTPAA_IAUX_STAT 0x38 #define RADIO_2056_TX_INTPAA_IAUX_DYN 0x39 #define RADIO_2056_TX_INTPAA_IMAIN_STAT 0x3a #define RADIO_2056_TX_INTPAA_IMAIN_DYN 0x3b #define RADIO_2056_TX_INTPAA_CASCBIAS 0x3c #define RADIO_2056_TX_INTPAA_PASLOPE 0x3d #define RADIO_2056_TX_INTPAA_PA_MISC 0x3e #define RADIO_2056_TX_INTPAG_MASTER 0x3f #define RADIO_2056_TX_INTPAG_GAIN 0x40 #define RADIO_2056_TX_INTPAG_BOOST_TUNE 0x41 #define RADIO_2056_TX_INTPAG_IAUX_STAT 0x42 #define RADIO_2056_TX_INTPAG_IAUX_DYN 0x43 #define RADIO_2056_TX_INTPAG_IMAIN_STAT 0x44 #define RADIO_2056_TX_INTPAG_IMAIN_DYN 0x45 #define RADIO_2056_TX_INTPAG_CASCBIAS 0x46 #define RADIO_2056_TX_INTPAG_PASLOPE 0x47 #define RADIO_2056_TX_INTPAG_PA_MISC 0x48 #define RADIO_2056_TX_PADA_MASTER 0x49 #define RADIO_2056_TX_PADA_IDAC 0x4a #define RADIO_2056_TX_PADA_CASCBIAS 0x4b #define RADIO_2056_TX_PADA_GAIN 0x4c #define RADIO_2056_TX_PADA_BOOST_TUNE 0x4d #define RADIO_2056_TX_PADA_SLOPE 0x4e #define RADIO_2056_TX_PADG_MASTER 0x4f #define RADIO_2056_TX_PADG_IDAC 0x50 #define RADIO_2056_TX_PADG_CASCBIAS 0x51 #define RADIO_2056_TX_PADG_GAIN 0x52 #define RADIO_2056_TX_PADG_BOOST_TUNE 0x53 #define RADIO_2056_TX_PADG_SLOPE 0x54 #define RADIO_2056_TX_PGAA_MASTER 0x55 #define RADIO_2056_TX_PGAA_IDAC 0x56 #define RADIO_2056_TX_PGAA_GAIN 0x57 #define RADIO_2056_TX_PGAA_BOOST_TUNE 0x58 #define RADIO_2056_TX_PGAA_SLOPE 0x59 #define RADIO_2056_TX_PGAA_MISC 0x5a #define RADIO_2056_TX_PGAG_MASTER 0x5b #define RADIO_2056_TX_PGAG_IDAC 0x5c #define RADIO_2056_TX_PGAG_GAIN 0x5d #define RADIO_2056_TX_PGAG_BOOST_TUNE 0x5e #define RADIO_2056_TX_PGAG_SLOPE 0x5f #define RADIO_2056_TX_PGAG_MISC 0x60 #define RADIO_2056_TX_MIXA_MASTER 0x61 #define RADIO_2056_TX_MIXA_BOOST_TUNE 0x62 #define RADIO_2056_TX_MIXG 0x63 #define RADIO_2056_TX_MIXG_BOOST_TUNE 0x64 #define RADIO_2056_TX_BB_GM_MASTER 0x65 #define RADIO_2056_TX_GMBB_GM 0x66 #define RADIO_2056_TX_GMBB_IDAC 0x67 #define RADIO_2056_TX_TXLPF_MASTER 0x68 #define RADIO_2056_TX_TXLPF_RCCAL 0x69 #define RADIO_2056_TX_TXLPF_RCCAL_OFF0 0x6a #define RADIO_2056_TX_TXLPF_RCCAL_OFF1 0x6b #define RADIO_2056_TX_TXLPF_RCCAL_OFF2 0x6c #define RADIO_2056_TX_TXLPF_RCCAL_OFF3 0x6d #define RADIO_2056_TX_TXLPF_RCCAL_OFF4 0x6e #define RADIO_2056_TX_TXLPF_RCCAL_OFF5 0x6f #define RADIO_2056_TX_TXLPF_RCCAL_OFF6 0x70 #define RADIO_2056_TX_TXLPF_BW 0x71 #define RADIO_2056_TX_TXLPF_GAIN 0x72 #define RADIO_2056_TX_TXLPF_IDAC 0x73 #define RADIO_2056_TX_TXLPF_IDAC_0 0x74 #define RADIO_2056_TX_TXLPF_IDAC_1 0x75 #define RADIO_2056_TX_TXLPF_IDAC_2 0x76 #define RADIO_2056_TX_TXLPF_IDAC_3 0x77 #define RADIO_2056_TX_TXLPF_IDAC_4 0x78 #define RADIO_2056_TX_TXLPF_IDAC_5 0x79 #define RADIO_2056_TX_TXLPF_IDAC_6 0x7a #define RADIO_2056_TX_TXLPF_OPAMP_IDAC 0x7b #define RADIO_2056_TX_TXLPF_MISC 0x7c #define RADIO_2056_TX_TXSPARE1 0x7d #define RADIO_2056_TX_TXSPARE2 0x7e #define RADIO_2056_TX_TXSPARE3 0x7f #define RADIO_2056_TX_TXSPARE4 0x80 #define RADIO_2056_TX_TXSPARE5 0x81 #define RADIO_2056_TX_TXSPARE6 0x82 #define RADIO_2056_TX_TXSPARE7 0x83 #define RADIO_2056_TX_TXSPARE8 0x84 #define RADIO_2056_TX_TXSPARE9 0x85 #define RADIO_2056_TX_TXSPARE10 0x86 #define RADIO_2056_TX_TXSPARE11 0x87 #define RADIO_2056_TX_TXSPARE12 0x88 #define RADIO_2056_TX_TXSPARE13 0x89 #define RADIO_2056_TX_TXSPARE14 0x8a #define RADIO_2056_TX_TXSPARE15 0x8b #define RADIO_2056_TX_TXSPARE16 0x8c #define RADIO_2056_TX_STATUS_INTPA_GAIN 0x8d #define RADIO_2056_TX_STATUS_PAD_GAIN 0x8e #define RADIO_2056_TX_STATUS_PGA_GAIN 0x8f #define RADIO_2056_TX_STATUS_GM_TXLPF_GAIN 0x90 #define RADIO_2056_TX_STATUS_TXLPF_BW 0x91 #define RADIO_2056_TX_STATUS_TXLPF_RC 0x92 #define RADIO_2056_TX_GMBB_IDAC0 0x93 #define RADIO_2056_TX_GMBB_IDAC1 0x94 #define RADIO_2056_TX_GMBB_IDAC2 0x95 #define RADIO_2056_TX_GMBB_IDAC3 0x96 #define RADIO_2056_TX_GMBB_IDAC4 0x97 #define RADIO_2056_TX_GMBB_IDAC5 0x98 #define RADIO_2056_TX_GMBB_IDAC6 0x99 #define RADIO_2056_TX_GMBB_IDAC7 0x9a #define RADIO_2056_RX_RESERVED_ADDR0 0x0 #define RADIO_2056_RX_IDCODE 0x1 #define RADIO_2056_RX_RESERVED_ADDR2 0x2 #define RADIO_2056_RX_RESERVED_ADDR3 0x3 #define RADIO_2056_RX_RESERVED_ADDR4 0x4 #define RADIO_2056_RX_RESERVED_ADDR5 0x5 #define RADIO_2056_RX_RESERVED_ADDR6 0x6 #define RADIO_2056_RX_RESERVED_ADDR7 0x7 #define RADIO_2056_RX_COM_CTRL 0x8 #define RADIO_2056_RX_COM_PU 0x9 #define RADIO_2056_RX_COM_OVR 0xa #define RADIO_2056_RX_COM_RESET 0xb #define RADIO_2056_RX_COM_RCAL 0xc #define RADIO_2056_RX_COM_RC_RXLPF 0xd #define RADIO_2056_RX_COM_RC_TXLPF 0xe #define RADIO_2056_RX_COM_RC_RXHPF 0xf #define RADIO_2056_RX_RESERVED_ADDR16 0x10 #define RADIO_2056_RX_RESERVED_ADDR17 0x11 #define RADIO_2056_RX_RESERVED_ADDR18 0x12 #define RADIO_2056_RX_RESERVED_ADDR19 0x13 #define RADIO_2056_RX_RESERVED_ADDR20 0x14 #define RADIO_2056_RX_RESERVED_ADDR21 0x15 #define RADIO_2056_RX_RESERVED_ADDR22 0x16 #define RADIO_2056_RX_RESERVED_ADDR23 0x17 #define RADIO_2056_RX_RESERVED_ADDR24 0x18 #define RADIO_2056_RX_RESERVED_ADDR25 0x19 #define RADIO_2056_RX_RESERVED_ADDR26 0x1a #define RADIO_2056_RX_RESERVED_ADDR27 0x1b #define RADIO_2056_RX_RESERVED_ADDR28 0x1c #define RADIO_2056_RX_RESERVED_ADDR29 0x1d #define RADIO_2056_RX_RESERVED_ADDR30 0x1e #define RADIO_2056_RX_RESERVED_ADDR31 0x1f #define RADIO_2056_RX_RXIQCAL_RXMUX 0x20 #define RADIO_2056_RX_RSSI_PU 0x21 #define RADIO_2056_RX_RSSI_SEL 0x22 #define RADIO_2056_RX_RSSI_GAIN 0x23 #define RADIO_2056_RX_RSSI_NB_IDAC 0x24 #define RADIO_2056_RX_RSSI_WB2I_IDAC_1 0x25 #define RADIO_2056_RX_RSSI_WB2I_IDAC_2 0x26 #define RADIO_2056_RX_RSSI_WB2Q_IDAC_1 0x27 #define RADIO_2056_RX_RSSI_WB2Q_IDAC_2 0x28 #define RADIO_2056_RX_RSSI_POLE 0x29 #define RADIO_2056_RX_RSSI_WB1_IDAC 0x2a #define RADIO_2056_RX_RSSI_MISC 0x2b #define RADIO_2056_RX_LNAA_MASTER 0x2c #define RADIO_2056_RX_LNAA_TUNE 0x2d #define RADIO_2056_RX_LNAA_GAIN 0x2e #define RADIO_2056_RX_LNA_A_SLOPE 0x2f #define RADIO_2056_RX_BIASPOLE_LNAA1_IDAC 0x30 #define RADIO_2056_RX_LNAA2_IDAC 0x31 #define RADIO_2056_RX_LNA1A_MISC 0x32 #define RADIO_2056_RX_LNAG_MASTER 0x33 #define RADIO_2056_RX_LNAG_TUNE 0x34 #define RADIO_2056_RX_LNAG_GAIN 0x35 #define RADIO_2056_RX_LNA_G_SLOPE 0x36 #define RADIO_2056_RX_BIASPOLE_LNAG1_IDAC 0x37 #define RADIO_2056_RX_LNAG2_IDAC 0x38 #define RADIO_2056_RX_LNA1G_MISC 0x39 #define RADIO_2056_RX_MIXA_MASTER 0x3a #define RADIO_2056_RX_MIXA_VCM 0x3b #define RADIO_2056_RX_MIXA_CTRLPTAT 0x3c #define RADIO_2056_RX_MIXA_LOB_BIAS 0x3d #define RADIO_2056_RX_MIXA_CORE_IDAC 0x3e #define RADIO_2056_RX_MIXA_CMFB_IDAC 0x3f #define RADIO_2056_RX_MIXA_BIAS_AUX 0x40 #define RADIO_2056_RX_MIXA_BIAS_MAIN 0x41 #define RADIO_2056_RX_MIXA_BIAS_MISC 0x42 #define RADIO_2056_RX_MIXA_MAST_BIAS 0x43 #define RADIO_2056_RX_MIXG_MASTER 0x44 #define RADIO_2056_RX_MIXG_VCM 0x45 #define RADIO_2056_RX_MIXG_CTRLPTAT 0x46 #define RADIO_2056_RX_MIXG_LOB_BIAS 0x47 #define RADIO_2056_RX_MIXG_CORE_IDAC 0x48 #define RADIO_2056_RX_MIXG_CMFB_IDAC 0x49 #define RADIO_2056_RX_MIXG_BIAS_AUX 0x4a #define RADIO_2056_RX_MIXG_BIAS_MAIN 0x4b #define RADIO_2056_RX_MIXG_BIAS_MISC 0x4c #define RADIO_2056_RX_MIXG_MAST_BIAS 0x4d #define RADIO_2056_RX_TIA_MASTER 0x4e #define RADIO_2056_RX_TIA_IOPAMP 0x4f #define RADIO_2056_RX_TIA_QOPAMP 0x50 #define RADIO_2056_RX_TIA_IMISC 0x51 #define RADIO_2056_RX_TIA_QMISC 0x52 #define RADIO_2056_RX_TIA_GAIN 0x53 #define RADIO_2056_RX_TIA_SPARE1 0x54 #define RADIO_2056_RX_TIA_SPARE2 0x55 #define RADIO_2056_RX_BB_LPF_MASTER 0x56 #define RADIO_2056_RX_AACI_MASTER 0x57 #define RADIO_2056_RX_RXLPF_IDAC 0x58 #define RADIO_2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 #define RADIO_2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5a #define RADIO_2056_RX_RXLPF_BIAS_DCCANCEL 0x5b #define RADIO_2056_RX_RXLPF_OUTVCM 0x5c #define RADIO_2056_RX_RXLPF_INVCM_BODY 0x5d #define RADIO_2056_RX_RXLPF_CC_OP 0x5e #define RADIO_2056_RX_RXLPF_GAIN 0x5f #define RADIO_2056_RX_RXLPF_Q_BW 0x60 #define RADIO_2056_RX_RXLPF_HP_CORNER_BW 0x61 #define RADIO_2056_RX_RXLPF_RCCAL_HPC 0x62 #define RADIO_2056_RX_RXHPF_OFF0 0x63 #define RADIO_2056_RX_RXHPF_OFF1 0x64 #define RADIO_2056_RX_RXHPF_OFF2 0x65 #define RADIO_2056_RX_RXHPF_OFF3 0x66 #define RADIO_2056_RX_RXHPF_OFF4 0x67 #define RADIO_2056_RX_RXHPF_OFF5 0x68 #define RADIO_2056_RX_RXHPF_OFF6 0x69 #define RADIO_2056_RX_RXHPF_OFF7 0x6a #define RADIO_2056_RX_RXLPF_RCCAL_LPC 0x6b #define RADIO_2056_RX_RXLPF_OFF_0 0x6c #define RADIO_2056_RX_RXLPF_OFF_1 0x6d #define RADIO_2056_RX_RXLPF_OFF_2 0x6e #define RADIO_2056_RX_RXLPF_OFF_3 0x6f #define RADIO_2056_RX_RXLPF_OFF_4 0x70 #define RADIO_2056_RX_UNUSED 0x71 #define RADIO_2056_RX_VGA_MASTER 0x72 #define RADIO_2056_RX_VGA_BIAS 0x73 #define RADIO_2056_RX_VGA_BIAS_DCCANCEL 0x74 #define RADIO_2056_RX_VGA_GAIN 0x75 #define RADIO_2056_RX_VGA_HP_CORNER_BW 0x76 #define RADIO_2056_RX_VGABUF_BIAS 0x77 #define RADIO_2056_RX_VGABUF_GAIN_BW 0x78 #define RADIO_2056_RX_TXFBMIX_A 0x79 #define RADIO_2056_RX_TXFBMIX_G 0x7a #define RADIO_2056_RX_RXSPARE1 0x7b #define RADIO_2056_RX_RXSPARE2 0x7c #define RADIO_2056_RX_RXSPARE3 0x7d #define RADIO_2056_RX_RXSPARE4 0x7e #define RADIO_2056_RX_RXSPARE5 0x7f #define RADIO_2056_RX_RXSPARE6 0x80 #define RADIO_2056_RX_RXSPARE7 0x81 #define RADIO_2056_RX_RXSPARE8 0x82 #define RADIO_2056_RX_RXSPARE9 0x83 #define RADIO_2056_RX_RXSPARE10 0x84 #define RADIO_2056_RX_RXSPARE11 0x85 #define RADIO_2056_RX_RXSPARE12 0x86 #define RADIO_2056_RX_RXSPARE13 0x87 #define RADIO_2056_RX_RXSPARE14 0x88 #define RADIO_2056_RX_RXSPARE15 0x89 #define RADIO_2056_RX_RXSPARE16 0x8a #define RADIO_2056_RX_STATUS_LNAA_GAIN 0x8b #define RADIO_2056_RX_STATUS_LNAG_GAIN 0x8c #define RADIO_2056_RX_STATUS_MIXTIA_GAIN 0x8d #define RADIO_2056_RX_STATUS_RXLPF_GAIN 0x8e #define RADIO_2056_RX_STATUS_VGA_BUF_GAIN 0x8f #define RADIO_2056_RX_STATUS_RXLPF_Q 0x90 #define RADIO_2056_RX_STATUS_RXLPF_BUF_BW 0x91 #define RADIO_2056_RX_STATUS_RXLPF_VGA_HPC 0x92 #define RADIO_2056_RX_STATUS_RXLPF_RC 0x93 #define RADIO_2056_RX_STATUS_HPC_RC 0x94 #define RADIO_2056_LNA1_A_PU 0x01 #define RADIO_2056_LNA2_A_PU 0x02 #define RADIO_2056_LNA1_G_PU 0x01 #define RADIO_2056_LNA2_G_PU 0x02 #define RADIO_2056_MIXA_PU_I 0x01 #define RADIO_2056_MIXA_PU_Q 0x02 #define RADIO_2056_MIXA_PU_GM 0x10 #define RADIO_2056_MIXG_PU_I 0x01 #define RADIO_2056_MIXG_PU_Q 0x02 #define RADIO_2056_MIXG_PU_GM 0x10 #define RADIO_2056_TIA_PU 0x01 #define RADIO_2056_BB_LPF_PU 0x20 #define RADIO_2056_W1_PU 0x02 #define RADIO_2056_W2_PU 0x04 #define RADIO_2056_NB_PU 0x08 #define RADIO_2056_RSSI_W1_SEL 0x02 #define RADIO_2056_RSSI_W2_SEL 0x04 #define RADIO_2056_RSSI_NB_SEL 0x08 #define RADIO_2056_VCM_MASK 0x1c #define RADIO_2056_RSSI_VCM_SHIFT 0x02 #define RADIO_2057_DACBUF_VINCM_CORE0 0x0 #define RADIO_2057_IDCODE 0x1 #define RADIO_2057_RCCAL_MASTER 0x2 #define RADIO_2057_RCCAL_CAP_SIZE 0x3 #define RADIO_2057_RCAL_CONFIG 0x4 #define RADIO_2057_GPAIO_CONFIG 0x5 #define RADIO_2057_GPAIO_SEL1 0x6 #define RADIO_2057_GPAIO_SEL0 0x7 #define RADIO_2057_CLPO_CONFIG 0x8 #define RADIO_2057_BANDGAP_CONFIG 0x9 #define RADIO_2057_BANDGAP_RCAL_TRIM 0xa #define RADIO_2057_AFEREG_CONFIG 0xb #define RADIO_2057_TEMPSENSE_CONFIG 0xc #define RADIO_2057_XTAL_CONFIG1 0xd #define RADIO_2057_XTAL_ICORE_SIZE 0xe #define RADIO_2057_XTAL_BUF_SIZE 0xf #define RADIO_2057_XTAL_PULLCAP_SIZE 0x10 #define RADIO_2057_RFPLL_MASTER 0x11 #define RADIO_2057_VCOMONITOR_VTH_L 0x12 #define RADIO_2057_VCOMONITOR_VTH_H 0x13 #define RADIO_2057_VCOCAL_BIASRESET_RFPLLREG_VOUT 0x14 #define RADIO_2057_VCO_VARCSIZE_IDAC 0x15 #define RADIO_2057_VCOCAL_COUNTVAL0 0x16 #define RADIO_2057_VCOCAL_COUNTVAL1 0x17 #define RADIO_2057_VCOCAL_INTCLK_COUNT 0x18 #define RADIO_2057_VCOCAL_MASTER 0x19 #define RADIO_2057_VCOCAL_NUMCAPCHANGE 0x1a #define RADIO_2057_VCOCAL_WINSIZE 0x1b #define RADIO_2057_VCOCAL_DELAY_AFTER_REFRESH 0x1c #define RADIO_2057_VCOCAL_DELAY_AFTER_CLOSELOOP 0x1d #define RADIO_2057_VCOCAL_DELAY_AFTER_OPENLOOP 0x1e #define RADIO_2057_VCOCAL_DELAY_BEFORE_OPENLOOP 0x1f #define RADIO_2057_VCO_FORCECAPEN_FORCECAP1 0x20 #define RADIO_2057_VCO_FORCECAP0 0x21 #define RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE 0x22 #define RADIO_2057_RFPLL_PFD_RESET_PW 0x23 #define RADIO_2057_RFPLL_LOOPFILTER_R2 0x24 #define RADIO_2057_RFPLL_LOOPFILTER_R1 0x25 #define RADIO_2057_RFPLL_LOOPFILTER_C3 0x26 #define RADIO_2057_RFPLL_LOOPFILTER_C2 0x27 #define RADIO_2057_RFPLL_LOOPFILTER_C1 0x28 #define RADIO_2057_CP_KPD_IDAC 0x29 #define RADIO_2057_RFPLL_IDACS 0x2a #define RADIO_2057_RFPLL_MISC_EN 0x2b #define RADIO_2057_RFPLL_MMD0 0x2c #define RADIO_2057_RFPLL_MMD1 0x2d #define RADIO_2057_RFPLL_MISC_CAL_RESETN 0x2e #define RADIO_2057_JTAGXTAL_SIZE_CPBIAS_FILTRES 0x2f #define RADIO_2057_VCO_ALCREF_BBPLLXTAL_SIZE 0x30 #define RADIO_2057_VCOCAL_READCAP0 0x31 #define RADIO_2057_VCOCAL_READCAP1 0x32 #define RADIO_2057_VCOCAL_STATUS 0x33 #define RADIO_2057_LOGEN_PUS 0x34 #define RADIO_2057_LOGEN_PTAT_RESETS 0x35 #define RADIO_2057_VCOBUF_IDACS 0x36 #define RADIO_2057_VCOBUF_TUNE 0x37 #define RADIO_2057_CMOSBUF_TX2GQ_IDACS 0x38 #define RADIO_2057_CMOSBUF_TX2GI_IDACS 0x39 #define RADIO_2057_CMOSBUF_TX5GQ_IDACS 0x3a #define RADIO_2057_CMOSBUF_TX5GI_IDACS 0x3b #define RADIO_2057_CMOSBUF_RX2GQ_IDACS 0x3c #define RADIO_2057_CMOSBUF_RX2GI_IDACS 0x3d #define RADIO_2057_CMOSBUF_RX5GQ_IDACS 0x3e #define RADIO_2057_CMOSBUF_RX5GI_IDACS 0x3f #define RADIO_2057_LOGEN_MX2G_IDACS 0x40 #define RADIO_2057_LOGEN_MX2G_TUNE 0x41 #define RADIO_2057_LOGEN_MX5G_IDACS 0x42 #define RADIO_2057_LOGEN_MX5G_TUNE 0x43 #define RADIO_2057_LOGEN_MX5G_RCCR 0x44 #define RADIO_2057_LOGEN_INDBUF2G_IDAC 0x45 #define RADIO_2057_LOGEN_INDBUF2G_IBOOST 0x46 #define RADIO_2057_LOGEN_INDBUF2G_TUNE 0x47 #define RADIO_2057_LOGEN_INDBUF5G_IDAC 0x48 #define RADIO_2057_LOGEN_INDBUF5G_IBOOST 0x49 #define RADIO_2057_LOGEN_INDBUF5G_TUNE 0x4a #define RADIO_2057_CMOSBUF_TX_RCCR 0x4b #define RADIO_2057_CMOSBUF_RX_RCCR 0x4c #define RADIO_2057_LOGEN_SEL_PKDET 0x4d #define RADIO_2057_CMOSBUF_SHAREIQ_PTAT 0x4e #define RADIO_2057_RXTXBIAS_CONFIG_CORE0 0x4f #define RADIO_2057_TXGM_TXRF_PUS_CORE0 0x50 #define RADIO_2057_TXGM_IDAC_BLEED_CORE0 0x51 #define RADIO_2057_TXGM_GAIN_CORE0 0x56 #define RADIO_2057_TXGM2G_PKDET_PUS_CORE0 0x57 #define RADIO_2057_PAD2G_PTATS_CORE0 0x58 #define RADIO_2057_PAD2G_IDACS_CORE0 0x59 #define RADIO_2057_PAD2G_BOOST_PU_CORE0 0x5a #define RADIO_2057_PAD2G_CASCV_GAIN_CORE0 0x5b #define RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0 0x5c #define RADIO_2057_TXMIX2G_LODC_CORE0 0x5d #define RADIO_2057_PAD2G_TUNE_PUS_CORE0 0x5e #define RADIO_2057_IPA2G_GAIN_CORE0 0x5f #define RADIO_2057_TSSI2G_SPARE1_CORE0 0x60 #define RADIO_2057_TSSI2G_SPARE2_CORE0 0x61 #define RADIO_2057_IPA2G_TUNEV_CASCV_PTAT_CORE0 0x62 #define RADIO_2057_IPA2G_IMAIN_CORE0 0x63 #define RADIO_2057_IPA2G_CASCONV_CORE0 0x64 #define RADIO_2057_IPA2G_CASCOFFV_CORE0 0x65 #define RADIO_2057_IPA2G_BIAS_FILTER_CORE0 0x66 #define RADIO_2057_TX5G_PKDET_CORE0 0x69 #define RADIO_2057_PGA_PTAT_TXGM5G_PU_CORE0 0x6a #define RADIO_2057_PAD5G_PTATS1_CORE0 0x6b #define RADIO_2057_PAD5G_CLASS_PTATS2_CORE0 0x6c #define RADIO_2057_PGA_BOOSTPTAT_IMAIN_CORE0 0x6d #define RADIO_2057_PAD5G_CASCV_IMAIN_CORE0 0x6e #define RADIO_2057_TXMIX5G_IBOOST_PAD_IAUX_CORE0 0x6f #define RADIO_2057_PGA_BOOST_TUNE_CORE0 0x70 #define RADIO_2057_PGA_GAIN_CORE0 0x71 #define RADIO_2057_PAD5G_CASCOFFV_GAIN_PUS_CORE0 0x72 #define RADIO_2057_TXMIX5G_BOOST_TUNE_CORE0 0x73 #define RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE0 0x74 #define RADIO_2057_IPA5G_IAUX_CORE0 0x75 #define RADIO_2057_IPA5G_GAIN_CORE0 0x76 #define RADIO_2057_TSSI5G_SPARE1_CORE0 0x77 #define RADIO_2057_TSSI5G_SPARE2_CORE0 0x78 #define RADIO_2057_IPA5G_CASCOFFV_PU_CORE0 0x79 #define RADIO_2057_IPA5G_PTAT_CORE0 0x7a #define RADIO_2057_IPA5G_IMAIN_CORE0 0x7b #define RADIO_2057_IPA5G_CASCONV_CORE0 0x7c #define RADIO_2057_IPA5G_BIAS_FILTER_CORE0 0x7d #define RADIO_2057_PAD_BIAS_FILTER_BWS_CORE0 0x80 #define RADIO_2057_TR2G_CONFIG1_CORE0_NU 0x81 #define RADIO_2057_TR2G_CONFIG2_CORE0_NU 0x82 #define RADIO_2057_LNA5G_RFEN_CORE0 0x83 #define RADIO_2057_TR5G_CONFIG2_CORE0_NU 0x84 #define RADIO_2057_RXRFBIAS_IBOOST_PU_CORE0 0x85 #define RADIO_2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE0 0x86 #define RADIO_2057_RXGM_CMFBITAIL_AUXPTAT_CORE0 0x87 #define RADIO_2057_RXMIX_ICORE_RXGM_IAUX_CORE0 0x88 #define RADIO_2057_RXMIX_CMFBITAIL_PU_CORE0 0x89 #define RADIO_2057_LNA2_IMAIN_PTAT_PU_CORE0 0x8a #define RADIO_2057_LNA2_IAUX_PTAT_CORE0 0x8b #define RADIO_2057_LNA1_IMAIN_PTAT_PU_CORE0 0x8c #define RADIO_2057_LNA15G_INPUT_MATCH_TUNE_CORE0 0x8d #define RADIO_2057_RXRFBIAS_BANDSEL_CORE0 0x8e #define RADIO_2057_TIA_CONFIG_CORE0 0x8f #define RADIO_2057_TIA_IQGAIN_CORE0 0x90 #define RADIO_2057_TIA_IBIAS2_CORE0 0x91 #define RADIO_2057_TIA_IBIAS1_CORE0 0x92 #define RADIO_2057_TIA_SPARE_Q_CORE0 0x93 #define RADIO_2057_TIA_SPARE_I_CORE0 0x94 #define RADIO_2057_RXMIX2G_PUS_CORE0 0x95 #define RADIO_2057_RXMIX2G_VCMREFS_CORE0 0x96 #define RADIO_2057_RXMIX2G_LODC_QI_CORE0 0x97 #define RADIO_2057_W12G_BW_LNA2G_PUS_CORE0 0x98 #define RADIO_2057_LNA2G_GAIN_CORE0 0x99 #define RADIO_2057_LNA2G_TUNE_CORE0 0x9a #define RADIO_2057_RXMIX5G_PUS_CORE0 0x9b #define RADIO_2057_RXMIX5G_VCMREFS_CORE0 0x9c #define RADIO_2057_RXMIX5G_LODC_QI_CORE0 0x9d #define RADIO_2057_W15G_BW_LNA5G_PUS_CORE0 0x9e #define RADIO_2057_LNA5G_GAIN_CORE0 0x9f #define RADIO_2057_LNA5G_TUNE_CORE0 0xa0 #define RADIO_2057_LPFSEL_TXRX_RXBB_PUS_CORE0 0xa1 #define RADIO_2057_RXBB_BIAS_MASTER_CORE0 0xa2 #define RADIO_2057_RXBB_VGABUF_IDACS_CORE0 0xa3 #define RADIO_2057_LPF_VCMREF_TXBUF_VCMREF_CORE0 0xa4 #define RADIO_2057_TXBUF_VINCM_CORE0 0xa5 #define RADIO_2057_TXBUF_IDACS_CORE0 0xa6 #define RADIO_2057_LPF_RESP_RXBUF_BW_CORE0 0xa7 #define RADIO_2057_RXBB_CC_CORE0 0xa8 #define RADIO_2057_RXBB_SPARE3_CORE0 0xa9 #define RADIO_2057_RXBB_RCCAL_HPC_CORE0 0xaa #define RADIO_2057_LPF_IDACS_CORE0 0xab #define RADIO_2057_LPFBYP_DCLOOP_BYP_IDAC_CORE0 0xac #define RADIO_2057_TXBUF_GAIN_CORE0 0xad #define RADIO_2057_AFELOOPBACK_AACI_RESP_CORE0 0xae #define RADIO_2057_RXBUF_DEGEN_CORE0 0xaf #define RADIO_2057_RXBB_SPARE2_CORE0 0xb0 #define RADIO_2057_RXBB_SPARE1_CORE0 0xb1 #define RADIO_2057_RSSI_MASTER_CORE0 0xb2 #define RADIO_2057_W2_MASTER_CORE0 0xb3 #define RADIO_2057_NB_MASTER_CORE0 0xb4 #define RADIO_2057_W2_IDACS0_Q_CORE0 0xb5 #define RADIO_2057_W2_IDACS1_Q_CORE0 0xb6 #define RADIO_2057_W2_IDACS0_I_CORE0 0xb7 #define RADIO_2057_W2_IDACS1_I_CORE0 0xb8 #define RADIO_2057_RSSI_GPAIOSEL_W1_IDACS_CORE0 0xb9 #define RADIO_2057_NB_IDACS_Q_CORE0 0xba #define RADIO_2057_NB_IDACS_I_CORE0 0xbb #define RADIO_2057_BACKUP4_CORE0 0xc1 #define RADIO_2057_BACKUP3_CORE0 0xc2 #define RADIO_2057_BACKUP2_CORE0 0xc3 #define RADIO_2057_BACKUP1_CORE0 0xc4 #define RADIO_2057_SPARE16_CORE0 0xc5 #define RADIO_2057_SPARE15_CORE0 0xc6 #define RADIO_2057_SPARE14_CORE0 0xc7 #define RADIO_2057_SPARE13_CORE0 0xc8 #define RADIO_2057_SPARE12_CORE0 0xc9 #define RADIO_2057_SPARE11_CORE0 0xca #define RADIO_2057_TX2G_BIAS_RESETS_CORE0 0xcb #define RADIO_2057_TX5G_BIAS_RESETS_CORE0 0xcc #define RADIO_2057_IQTEST_SEL_PU 0xcd #define RADIO_2057_XTAL_CONFIG2 0xce #define RADIO_2057_BUFS_MISC_LPFBW_CORE0 0xcf #define RADIO_2057_TXLPF_RCCAL_CORE0 0xd0 #define RADIO_2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0 0xd1 #define RADIO_2057_LPF_GAIN_CORE0 0xd2 #define RADIO_2057_DACBUF_IDACS_BW_CORE0 0xd3 #define RADIO_2057_RXTXBIAS_CONFIG_CORE1 0xd4 #define RADIO_2057_TXGM_TXRF_PUS_CORE1 0xd5 #define RADIO_2057_TXGM_IDAC_BLEED_CORE1 0xd6 #define RADIO_2057_TXGM_GAIN_CORE1 0xdb #define RADIO_2057_TXGM2G_PKDET_PUS_CORE1 0xdc #define RADIO_2057_PAD2G_PTATS_CORE1 0xdd #define RADIO_2057_PAD2G_IDACS_CORE1 0xde #define RADIO_2057_PAD2G_BOOST_PU_CORE1 0xdf #define RADIO_2057_PAD2G_CASCV_GAIN_CORE1 0xe0 #define RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1 0xe1 #define RADIO_2057_TXMIX2G_LODC_CORE1 0xe2 #define RADIO_2057_PAD2G_TUNE_PUS_CORE1 0xe3 #define RADIO_2057_IPA2G_GAIN_CORE1 0xe4 #define RADIO_2057_TSSI2G_SPARE1_CORE1 0xe5 #define RADIO_2057_TSSI2G_SPARE2_CORE1 0xe6 #define RADIO_2057_IPA2G_TUNEV_CASCV_PTAT_CORE1 0xe7 #define RADIO_2057_IPA2G_IMAIN_CORE1 0xe8 #define RADIO_2057_IPA2G_CASCONV_CORE1 0xe9 #define RADIO_2057_IPA2G_CASCOFFV_CORE1 0xea #define RADIO_2057_IPA2G_BIAS_FILTER_CORE1 0xeb #define RADIO_2057_TX5G_PKDET_CORE1 0xee #define RADIO_2057_PGA_PTAT_TXGM5G_PU_CORE1 0xef #define RADIO_2057_PAD5G_PTATS1_CORE1 0xf0 #define RADIO_2057_PAD5G_CLASS_PTATS2_CORE1 0xf1 #define RADIO_2057_PGA_BOOSTPTAT_IMAIN_CORE1 0xf2 #define RADIO_2057_PAD5G_CASCV_IMAIN_CORE1 0xf3 #define RADIO_2057_TXMIX5G_IBOOST_PAD_IAUX_CORE1 0xf4 #define RADIO_2057_PGA_BOOST_TUNE_CORE1 0xf5 #define RADIO_2057_PGA_GAIN_CORE1 0xf6 #define RADIO_2057_PAD5G_CASCOFFV_GAIN_PUS_CORE1 0xf7 #define RADIO_2057_TXMIX5G_BOOST_TUNE_CORE1 0xf8 #define RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE1 0xf9 #define RADIO_2057_IPA5G_IAUX_CORE1 0xfa #define RADIO_2057_IPA5G_GAIN_CORE1 0xfb #define RADIO_2057_TSSI5G_SPARE1_CORE1 0xfc #define RADIO_2057_TSSI5G_SPARE2_CORE1 0xfd #define RADIO_2057_IPA5G_CASCOFFV_PU_CORE1 0xfe #define RADIO_2057_IPA5G_PTAT_CORE1 0xff #define RADIO_2057_IPA5G_IMAIN_CORE1 0x100 #define RADIO_2057_IPA5G_CASCONV_CORE1 0x101 #define RADIO_2057_IPA5G_BIAS_FILTER_CORE1 0x102 #define RADIO_2057_PAD_BIAS_FILTER_BWS_CORE1 0x105 #define RADIO_2057_TR2G_CONFIG1_CORE1_NU 0x106 #define RADIO_2057_TR2G_CONFIG2_CORE1_NU 0x107 #define RADIO_2057_LNA5G_RFEN_CORE1 0x108 #define RADIO_2057_TR5G_CONFIG2_CORE1_NU 0x109 #define RADIO_2057_RXRFBIAS_IBOOST_PU_CORE1 0x10a #define RADIO_2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE1 0x10b #define RADIO_2057_RXGM_CMFBITAIL_AUXPTAT_CORE1 0x10c #define RADIO_2057_RXMIX_ICORE_RXGM_IAUX_CORE1 0x10d #define RADIO_2057_RXMIX_CMFBITAIL_PU_CORE1 0x10e #define RADIO_2057_LNA2_IMAIN_PTAT_PU_CORE1 0x10f #define RADIO_2057_LNA2_IAUX_PTAT_CORE1 0x110 #define RADIO_2057_LNA1_IMAIN_PTAT_PU_CORE1 0x111 #define RADIO_2057_LNA15G_INPUT_MATCH_TUNE_CORE1 0x112 #define RADIO_2057_RXRFBIAS_BANDSEL_CORE1 0x113 #define RADIO_2057_TIA_CONFIG_CORE1 0x114 #define RADIO_2057_TIA_IQGAIN_CORE1 0x115 #define RADIO_2057_TIA_IBIAS2_CORE1 0x116 #define RADIO_2057_TIA_IBIAS1_CORE1 0x117 #define RADIO_2057_TIA_SPARE_Q_CORE1 0x118 #define RADIO_2057_TIA_SPARE_I_CORE1 0x119 #define RADIO_2057_RXMIX2G_PUS_CORE1 0x11a #define RADIO_2057_RXMIX2G_VCMREFS_CORE1 0x11b #define RADIO_2057_RXMIX2G_LODC_QI_CORE1 0x11c #define RADIO_2057_W12G_BW_LNA2G_PUS_CORE1 0x11d #define RADIO_2057_LNA2G_GAIN_CORE1 0x11e #define RADIO_2057_LNA2G_TUNE_CORE1 0x11f #define RADIO_2057_RXMIX5G_PUS_CORE1 0x120 #define RADIO_2057_RXMIX5G_VCMREFS_CORE1 0x121 #define RADIO_2057_RXMIX5G_LODC_QI_CORE1 0x122 #define RADIO_2057_W15G_BW_LNA5G_PUS_CORE1 0x123 #define RADIO_2057_LNA5G_GAIN_CORE1 0x124 #define RADIO_2057_LNA5G_TUNE_CORE1 0x125 #define RADIO_2057_LPFSEL_TXRX_RXBB_PUS_CORE1 0x126 #define RADIO_2057_RXBB_BIAS_MASTER_CORE1 0x127 #define RADIO_2057_RXBB_VGABUF_IDACS_CORE1 0x128 #define RADIO_2057_LPF_VCMREF_TXBUF_VCMREF_CORE1 0x129 #define RADIO_2057_TXBUF_VINCM_CORE1 0x12a #define RADIO_2057_TXBUF_IDACS_CORE1 0x12b #define RADIO_2057_LPF_RESP_RXBUF_BW_CORE1 0x12c #define RADIO_2057_RXBB_CC_CORE1 0x12d #define RADIO_2057_RXBB_SPARE3_CORE1 0x12e #define RADIO_2057_RXBB_RCCAL_HPC_CORE1 0x12f #define RADIO_2057_LPF_IDACS_CORE1 0x130 #define RADIO_2057_LPFBYP_DCLOOP_BYP_IDAC_CORE1 0x131 #define RADIO_2057_TXBUF_GAIN_CORE1 0x132 #define RADIO_2057_AFELOOPBACK_AACI_RESP_CORE1 0x133 #define RADIO_2057_RXBUF_DEGEN_CORE1 0x134 #define RADIO_2057_RXBB_SPARE2_CORE1 0x135 #define RADIO_2057_RXBB_SPARE1_CORE1 0x136 #define RADIO_2057_RSSI_MASTER_CORE1 0x137 #define RADIO_2057_W2_MASTER_CORE1 0x138 #define RADIO_2057_NB_MASTER_CORE1 0x139 #define RADIO_2057_W2_IDACS0_Q_CORE1 0x13a #define RADIO_2057_W2_IDACS1_Q_CORE1 0x13b #define RADIO_2057_W2_IDACS0_I_CORE1 0x13c #define RADIO_2057_W2_IDACS1_I_CORE1 0x13d #define RADIO_2057_RSSI_GPAIOSEL_W1_IDACS_CORE1 0x13e #define RADIO_2057_NB_IDACS_Q_CORE1 0x13f #define RADIO_2057_NB_IDACS_I_CORE1 0x140 #define RADIO_2057_BACKUP4_CORE1 0x146 #define RADIO_2057_BACKUP3_CORE1 0x147 #define RADIO_2057_BACKUP2_CORE1 0x148 #define RADIO_2057_BACKUP1_CORE1 0x149 #define RADIO_2057_SPARE16_CORE1 0x14a #define RADIO_2057_SPARE15_CORE1 0x14b #define RADIO_2057_SPARE14_CORE1 0x14c #define RADIO_2057_SPARE13_CORE1 0x14d #define RADIO_2057_SPARE12_CORE1 0x14e #define RADIO_2057_SPARE11_CORE1 0x14f #define RADIO_2057_TX2G_BIAS_RESETS_CORE1 0x150 #define RADIO_2057_TX5G_BIAS_RESETS_CORE1 0x151 #define RADIO_2057_SPARE8_CORE1 0x152 #define RADIO_2057_SPARE7_CORE1 0x153 #define RADIO_2057_BUFS_MISC_LPFBW_CORE1 0x154 #define RADIO_2057_TXLPF_RCCAL_CORE1 0x155 #define RADIO_2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1 0x156 #define RADIO_2057_LPF_GAIN_CORE1 0x157 #define RADIO_2057_DACBUF_IDACS_BW_CORE1 0x158 #define RADIO_2057_DACBUF_VINCM_CORE1 0x159 #define RADIO_2057_RCCAL_START_R1_Q1_P1 0x15a #define RADIO_2057_RCCAL_X1 0x15b #define RADIO_2057_RCCAL_TRC0 0x15c #define RADIO_2057_RCCAL_TRC1 0x15d #define RADIO_2057_RCCAL_DONE_OSCCAP 0x15e #define RADIO_2057_RCCAL_N0_0 0x15f #define RADIO_2057_RCCAL_N0_1 0x160 #define RADIO_2057_RCCAL_N1_0 0x161 #define RADIO_2057_RCCAL_N1_1 0x162 #define RADIO_2057_RCAL_STATUS 0x163 #define RADIO_2057_XTALPUOVR_PINCTRL 0x164 #define RADIO_2057_OVR_REG0 0x165 #define RADIO_2057_OVR_REG1 0x166 #define RADIO_2057_OVR_REG2 0x167 #define RADIO_2057_OVR_REG3 0x168 #define RADIO_2057_OVR_REG4 0x169 #define RADIO_2057_RCCAL_SCAP_VAL 0x16a #define RADIO_2057_RCCAL_BCAP_VAL 0x16b #define RADIO_2057_RCCAL_HPC_VAL 0x16c #define RADIO_2057_RCCAL_OVERRIDES 0x16d #define RADIO_2057_TX0_IQCAL_GAIN_BW 0x170 #define RADIO_2057_TX0_LOFT_FINE_I 0x171 #define RADIO_2057_TX0_LOFT_FINE_Q 0x172 #define RADIO_2057_TX0_LOFT_COARSE_I 0x173 #define RADIO_2057_TX0_LOFT_COARSE_Q 0x174 #define RADIO_2057_TX0_TX_SSI_MASTER 0x175 #define RADIO_2057_TX0_IQCAL_VCM_HG 0x176 #define RADIO_2057_TX0_IQCAL_IDAC 0x177 #define RADIO_2057_TX0_TSSI_VCM 0x178 #define RADIO_2057_TX0_TX_SSI_MUX 0x179 #define RADIO_2057_TX0_TSSIA 0x17a #define RADIO_2057_TX0_TSSIG 0x17b #define RADIO_2057_TX0_TSSI_MISC1 0x17c #define RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN 0x17d #define RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP 0x17e #define RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN 0x17f #define RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP 0x180 #define RADIO_2057_TX1_IQCAL_GAIN_BW 0x190 #define RADIO_2057_TX1_LOFT_FINE_I 0x191 #define RADIO_2057_TX1_LOFT_FINE_Q 0x192 #define RADIO_2057_TX1_LOFT_COARSE_I 0x193 #define RADIO_2057_TX1_LOFT_COARSE_Q 0x194 #define RADIO_2057_TX1_TX_SSI_MASTER 0x195 #define RADIO_2057_TX1_IQCAL_VCM_HG 0x196 #define RADIO_2057_TX1_IQCAL_IDAC 0x197 #define RADIO_2057_TX1_TSSI_VCM 0x198 #define RADIO_2057_TX1_TX_SSI_MUX 0x199 #define RADIO_2057_TX1_TSSIA 0x19a #define RADIO_2057_TX1_TSSIG 0x19b #define RADIO_2057_TX1_TSSI_MISC1 0x19c #define RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN 0x19d #define RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP 0x19e #define RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN 0x19f #define RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP 0x1a0 #define RADIO_2057_AFE_VCM_CAL_MASTER_CORE0 0x1a1 #define RADIO_2057_AFE_SET_VCM_I_CORE0 0x1a2 #define RADIO_2057_AFE_SET_VCM_Q_CORE0 0x1a3 #define RADIO_2057_AFE_STATUS_VCM_IQADC_CORE0 0x1a4 #define RADIO_2057_AFE_STATUS_VCM_I_CORE0 0x1a5 #define RADIO_2057_AFE_STATUS_VCM_Q_CORE0 0x1a6 #define RADIO_2057_AFE_VCM_CAL_MASTER_CORE1 0x1a7 #define RADIO_2057_AFE_SET_VCM_I_CORE1 0x1a8 #define RADIO_2057_AFE_SET_VCM_Q_CORE1 0x1a9 #define RADIO_2057_AFE_STATUS_VCM_IQADC_CORE1 0x1aa #define RADIO_2057_AFE_STATUS_VCM_I_CORE1 0x1ab #define RADIO_2057_AFE_STATUS_VCM_Q_CORE1 0x1ac #define RADIO_2057v7_DACBUF_VINCM_CORE0 0x1ad #define RADIO_2057v7_RCCAL_MASTER 0x1ae #define RADIO_2057v7_TR2G_CONFIG3_CORE0_NU 0x1af #define RADIO_2057v7_TR2G_CONFIG3_CORE1_NU 0x1b0 #define RADIO_2057v7_LOGEN_PUS1 0x1b1 #define RADIO_2057v7_OVR_REG5 0x1b2 #define RADIO_2057v7_OVR_REG6 0x1b3 #define RADIO_2057v7_OVR_REG7 0x1b4 #define RADIO_2057v7_OVR_REG8 0x1b5 #define RADIO_2057v7_OVR_REG9 0x1b6 #define RADIO_2057v7_OVR_REG10 0x1b7 #define RADIO_2057v7_OVR_REG11 0x1b8 #define RADIO_2057v7_OVR_REG12 0x1b9 #define RADIO_2057v7_OVR_REG13 0x1ba #define RADIO_2057v7_OVR_REG14 0x1bb #define RADIO_2057v7_OVR_REG15 0x1bc #define RADIO_2057v7_OVR_REG16 0x1bd #define RADIO_2057v7_OVR_REG1 0x1be #define RADIO_2057v7_OVR_REG18 0x1bf #define RADIO_2057v7_OVR_REG19 0x1c0 #define RADIO_2057v7_OVR_REG20 0x1c1 #define RADIO_2057v7_OVR_REG21 0x1c2 #define RADIO_2057v7_OVR_REG2 0x1c3 #define RADIO_2057v7_OVR_REG23 0x1c4 #define RADIO_2057v7_OVR_REG24 0x1c5 #define RADIO_2057v7_OVR_REG25 0x1c6 #define RADIO_2057v7_OVR_REG26 0x1c7 #define RADIO_2057v7_OVR_REG27 0x1c8 #define RADIO_2057v7_OVR_REG28 0x1c9 #define RADIO_2057v7_IQTEST_SEL_PU2 0x1ca #define RADIO_2057_VCM_MASK 0x7 #endif /* _BRCM_PHY_RADIO_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h0000644000175000017500000000233012026211315026530 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_QMATH_H_ #define _BRCM_QMATH_H_ #include u16 qm_mulu16(u16 op1, u16 op2); s16 qm_muls16(s16 op1, s16 op2); s32 qm_add32(s32 op1, s32 op2); s16 qm_add16(s16 op1, s16 op2); s16 qm_sub16(s16 op1, s16 op2); s32 qm_shl32(s32 op, int shift); s16 qm_shl16(s16 op, int shift); s16 qm_shr16(s16 op, int shift); s16 qm_norm32(s32 op); void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N); #endif /* #ifndef _BRCM_QMATH_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c0000644000175000017500000001765312026211315026541 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "phy_qmath.h" /* * Description: This function make 16 bit unsigned multiplication. * To fit the output into 16 bits the 32 bit multiplication result is right * shifted by 16 bits. */ u16 qm_mulu16(u16 op1, u16 op2) { return (u16) (((u32) op1 * (u32) op2) >> 16); } /* * Description: This function make 16 bit multiplication and return the result * in 16 bits. To fit the multiplication result into 16 bits the multiplication * result is right shifted by 15 bits. Right shifting 15 bits instead of 16 bits * is done to remove the extra sign bit formed due to the multiplication. * When both the 16bit inputs are 0x8000 then the output is saturated to * 0x7fffffff. */ s16 qm_muls16(s16 op1, s16 op2) { s32 result; if (op1 == (s16) 0x8000 && op2 == (s16) 0x8000) result = 0x7fffffff; else result = ((s32) (op1) * (s32) (op2)); return (s16) (result >> 15); } /* * Description: This function add two 32 bit numbers and return the 32bit * result. If the result overflow 32 bits, the output will be saturated to * 32bits. */ s32 qm_add32(s32 op1, s32 op2) { s32 result; result = op1 + op2; if (op1 < 0 && op2 < 0 && result > 0) result = 0x80000000; else if (op1 > 0 && op2 > 0 && result < 0) result = 0x7fffffff; return result; } /* * Description: This function add two 16 bit numbers and return the 16bit * result. If the result overflow 16 bits, the output will be saturated to * 16bits. */ s16 qm_add16(s16 op1, s16 op2) { s16 result; s32 temp = (s32) op1 + (s32) op2; if (temp > (s32) 0x7fff) result = (s16) 0x7fff; else if (temp < (s32) 0xffff8000) result = (s16) 0xffff8000; else result = (s16) temp; return result; } /* * Description: This function make 16 bit subtraction and return the 16bit * result. If the result overflow 16 bits, the output will be saturated to * 16bits. */ s16 qm_sub16(s16 op1, s16 op2) { s16 result; s32 temp = (s32) op1 - (s32) op2; if (temp > (s32) 0x7fff) result = (s16) 0x7fff; else if (temp < (s32) 0xffff8000) result = (s16) 0xffff8000; else result = (s16) temp; return result; } /* * Description: This function make a 32 bit saturated left shift when the * specified shift is +ve. This function will make a 32 bit right shift when * the specified shift is -ve. This function return the result after shifting * operation. */ s32 qm_shl32(s32 op, int shift) { int i; s32 result; result = op; if (shift > 31) shift = 31; else if (shift < -31) shift = -31; if (shift >= 0) { for (i = 0; i < shift; i++) result = qm_add32(result, result); } else { result = result >> (-shift); } return result; } /* * Description: This function make a 16 bit saturated left shift when the * specified shift is +ve. This function will make a 16 bit right shift when * the specified shift is -ve. This function return the result after shifting * operation. */ s16 qm_shl16(s16 op, int shift) { int i; s16 result; result = op; if (shift > 15) shift = 15; else if (shift < -15) shift = -15; if (shift > 0) { for (i = 0; i < shift; i++) result = qm_add16(result, result); } else { result = result >> (-shift); } return result; } /* * Description: This function make a 16 bit right shift when shift is +ve. * This function make a 16 bit saturated left shift when shift is -ve. This * function return the result of the shift operation. */ s16 qm_shr16(s16 op, int shift) { return qm_shl16(op, -shift); } /* * Description: This function return the number of redundant sign bits in a * 32 bit number. Example: qm_norm32(0x00000080) = 23 */ s16 qm_norm32(s32 op) { u16 u16extraSignBits; if (op == 0) { return 31; } else { u16extraSignBits = 0; while ((op >> 31) == (op >> 30)) { u16extraSignBits++; op = op << 1; } } return u16extraSignBits; } /* This table is log2(1+(i/32)) where i=[0:1:31], in q.15 format */ static const s16 log_table[] = { 0, 1455, 2866, 4236, 5568, 6863, 8124, 9352, 10549, 11716, 12855, 13968, 15055, 16117, 17156, 18173, 19168, 20143, 21098, 22034, 22952, 23852, 24736, 25604, 26455, 27292, 28114, 28922, 29717, 30498, 31267, 32024 }; #define LOG_TABLE_SIZE 32 /* log_table size */ #define LOG2_LOG_TABLE_SIZE 5 /* log2(log_table size) */ #define Q_LOG_TABLE 15 /* qformat of log_table */ #define LOG10_2 19728 /* log10(2) in q.16 */ /* * Description: * This routine takes the input number N and its q format qN and compute * the log10(N). This routine first normalizes the input no N. Then N is in * mag*(2^x) format. mag is any number in the range 2^30-(2^31 - 1). * Then log2(mag * 2^x) = log2(mag) + x is computed. From that * log10(mag * 2^x) = log2(mag * 2^x) * log10(2) is computed. * This routine looks the log2 value in the table considering * LOG2_LOG_TABLE_SIZE+1 MSBs. As the MSB is always 1, only next * LOG2_OF_LOG_TABLE_SIZE MSBs are used for table lookup. Next 16 MSBs are used * for interpolation. * Inputs: * N - number to which log10 has to be found. * qN - q format of N * log10N - address where log10(N) will be written. * qLog10N - address where log10N qformat will be written. * Note/Problem: * For accurate results input should be in normalized or near normalized form. */ void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N) { s16 s16norm, s16tableIndex, s16errorApproximation; u16 u16offset; s32 s32log; /* normalize the N. */ s16norm = qm_norm32(N); N = N << s16norm; /* The qformat of N after normalization. * -30 is added to treat the no as between 1.0 to 2.0 * i.e. after adding the -30 to the qformat the decimal point will be * just rigtht of the MSB. (i.e. after sign bit and 1st MSB). i.e. * at the right side of 30th bit. */ qN = qN + s16norm - 30; /* take the table index as the LOG2_OF_LOG_TABLE_SIZE bits right of the * MSB */ s16tableIndex = (s16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE))); /* remove the MSB. the MSB is always 1 after normalization. */ s16tableIndex = s16tableIndex & (s16) ((1 << LOG2_LOG_TABLE_SIZE) - 1); /* remove the (1+LOG2_OF_LOG_TABLE_SIZE) MSBs in the N. */ N = N & ((1 << (32 - (2 + LOG2_LOG_TABLE_SIZE))) - 1); /* take the offset as the 16 MSBS after table index. */ u16offset = (u16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE + 16))); /* look the log value in the table. */ s32log = log_table[s16tableIndex]; /* q.15 format */ /* interpolate using the offset. q.15 format. */ s16errorApproximation = (s16) qm_mulu16(u16offset, (u16) (log_table[s16tableIndex + 1] - log_table[s16tableIndex])); /* q.15 format */ s32log = qm_add16((s16) s32log, s16errorApproximation); /* adjust for the qformat of the N as * log2(mag * 2^x) = log2(mag) + x */ s32log = qm_add32(s32log, ((s32) -qN) << 15); /* q.15 format */ /* normalize the result. */ s16norm = qm_norm32(s32log); /* bring all the important bits into lower 16 bits */ /* q.15+s16norm-16 format */ s32log = qm_shl32(s32log, s16norm - 16); /* compute the log10(N) by multiplying log2(N) with log10(2). * as log10(mag * 2^x) = log2(mag * 2^x) * log10(2) * log10N in q.15+s16norm-16+1 (LOG10_2 is in q.16) */ *log10N = qm_muls16((s16) s32log, (s16) LOG10_2); /* write the q format of the result. */ *qLog10N = 15 + s16norm - 16 + 1; return; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h0000644000175000017500000000626412026211315026204 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_PHY_LCN_H_ #define _BRCM_PHY_LCN_H_ #include struct brcms_phy_lcnphy { int lcnphy_txrf_sp_9_override; u8 lcnphy_full_cal_channel; u8 lcnphy_cal_counter; u16 lcnphy_cal_temper; bool lcnphy_recal; u8 lcnphy_rc_cap; u32 lcnphy_mcs20_po; u8 lcnphy_tr_isolation_mid; u8 lcnphy_tr_isolation_low; u8 lcnphy_tr_isolation_hi; u8 lcnphy_bx_arch; u8 lcnphy_rx_power_offset; u8 lcnphy_rssi_vf; u8 lcnphy_rssi_vc; u8 lcnphy_rssi_gs; u8 lcnphy_tssi_val; u8 lcnphy_rssi_vf_lowtemp; u8 lcnphy_rssi_vc_lowtemp; u8 lcnphy_rssi_gs_lowtemp; u8 lcnphy_rssi_vf_hightemp; u8 lcnphy_rssi_vc_hightemp; u8 lcnphy_rssi_gs_hightemp; s16 lcnphy_pa0b0; s16 lcnphy_pa0b1; s16 lcnphy_pa0b2; u16 lcnphy_rawtempsense; u8 lcnphy_measPower; u8 lcnphy_tempsense_slope; u8 lcnphy_freqoffset_corr; u8 lcnphy_tempsense_option; u8 lcnphy_tempcorrx; bool lcnphy_iqcal_swp_dis; bool lcnphy_hw_iqcal_en; uint lcnphy_bandedge_corr; bool lcnphy_spurmod; u16 lcnphy_tssi_tx_cnt; u16 lcnphy_tssi_idx; u16 lcnphy_tssi_npt; u16 lcnphy_target_tx_freq; s8 lcnphy_tx_power_idx_override; u16 lcnphy_noise_samples; u32 lcnphy_papdRxGnIdx; u32 lcnphy_papd_rxGnCtrl_init; u32 lcnphy_gain_idx_14_lowword; u32 lcnphy_gain_idx_14_hiword; u32 lcnphy_gain_idx_27_lowword; u32 lcnphy_gain_idx_27_hiword; s16 lcnphy_ofdmgainidxtableoffset; s16 lcnphy_dsssgainidxtableoffset; u32 lcnphy_tr_R_gain_val; u32 lcnphy_tr_T_gain_val; s8 lcnphy_input_pwr_offset_db; u16 lcnphy_Med_Low_Gain_db; u16 lcnphy_Very_Low_Gain_db; s8 lcnphy_lastsensed_temperature; s8 lcnphy_pkteng_rssi_slope; u8 lcnphy_saved_tx_user_target[TXP_NUM_RATES]; u8 lcnphy_volt_winner; u8 lcnphy_volt_low; u8 lcnphy_54_48_36_24mbps_backoff; u8 lcnphy_11n_backoff; u8 lcnphy_lowerofdm; u8 lcnphy_cck; u8 lcnphy_psat_2pt3_detected; s32 lcnphy_lowest_Re_div_Im; s8 lcnphy_final_papd_cal_idx; u16 lcnphy_extstxctrl4; u16 lcnphy_extstxctrl0; u16 lcnphy_extstxctrl1; s16 lcnphy_cck_dig_filt_type; s16 lcnphy_ofdm_dig_filt_type; struct lcnphy_cal_results lcnphy_cal_results; u8 lcnphy_psat_pwr; u8 lcnphy_psat_indx; s32 lcnphy_min_phase; u8 lcnphy_final_idx; u8 lcnphy_start_idx; u8 lcnphy_current_index; u16 lcnphy_logen_buf_1; u16 lcnphy_local_ovr_2; u16 lcnphy_local_oval_6; u16 lcnphy_local_oval_5; u16 lcnphy_logen_mixer_1; u8 lcnphy_aci_stat; uint lcnphy_aci_start_time; s8 lcnphy_tx_power_offset[TXP_NUM_RATES]; }; #endif /* _BRCM_PHY_LCN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c0000644000175000017500000037423112026211315026201 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "phy_qmath.h" #include "phy_hal.h" #include "phy_radio.h" #include "phytbl_lcn.h" #include "phy_lcn.h" #define PLL_2064_NDIV 90 #define PLL_2064_LOW_END_VCO 3000 #define PLL_2064_LOW_END_KVCO 27 #define PLL_2064_HIGH_END_VCO 4200 #define PLL_2064_HIGH_END_KVCO 68 #define PLL_2064_LOOP_BW_DOUBLER 200 #define PLL_2064_D30_DOUBLER 10500 #define PLL_2064_LOOP_BW 260 #define PLL_2064_D30 8000 #define PLL_2064_CAL_REF_TO 8 #define PLL_2064_MHZ 1000000 #define PLL_2064_OPEN_LOOP_DELAY 5 #define TEMPSENSE 1 #define VBATSENSE 2 #define NOISE_IF_UPD_CHK_INTERVAL 1 #define NOISE_IF_UPD_RST_INTERVAL 60 #define NOISE_IF_UPD_THRESHOLD_CNT 1 #define NOISE_IF_UPD_TRHRESHOLD 50 #define NOISE_IF_UPD_TIMEOUT 1000 #define NOISE_IF_OFF 0 #define NOISE_IF_CHK 1 #define NOISE_IF_ON 2 #define PAPD_BLANKING_PROFILE 3 #define PAPD2LUT 0 #define PAPD_CORR_NORM 0 #define PAPD_BLANKING_THRESHOLD 0 #define PAPD_STOP_AFTER_LAST_UPDATE 0 #define LCN_TARGET_PWR 60 #define LCN_VBAT_OFFSET_433X 34649679 #define LCN_VBAT_SLOPE_433X 8258032 #define LCN_VBAT_SCALE_NOM 53 #define LCN_VBAT_SCALE_DEN 432 #define LCN_TEMPSENSE_OFFSET 80812 #define LCN_TEMPSENSE_DEN 2647 #define LCN_BW_LMT 200 #define LCN_CUR_LMT 1250 #define LCN_MULT 1 #define LCN_VCO_DIV 30 #define LCN_OFFSET 680 #define LCN_FACT 490 #define LCN_CUR_DIV 2640 #define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT \ (0 + 8) #define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK \ (0x7f << LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT) #define LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT \ (0 + 8) #define LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_MASK \ (0x7f << LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT) #define wlc_lcnphy_enable_tx_gain_override(pi) \ wlc_lcnphy_set_tx_gain_override(pi, true) #define wlc_lcnphy_disable_tx_gain_override(pi) \ wlc_lcnphy_set_tx_gain_override(pi, false) #define wlc_lcnphy_iqcal_active(pi) \ (read_phy_reg((pi), 0x451) & \ ((0x1 << 15) | (0x1 << 14))) #define txpwrctrl_off(pi) (0x7 != ((read_phy_reg(pi, 0x4a4) & 0xE000) >> 13)) #define wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) \ (pi->temppwrctrl_capable) #define wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) \ (pi->hwpwrctrl_capable) #define SWCTRL_BT_TX 0x18 #define SWCTRL_OVR_DISABLE 0x40 #define AFE_CLK_INIT_MODE_TXRX2X 1 #define AFE_CLK_INIT_MODE_PAPD 0 #define LCNPHY_TBL_ID_IQLOCAL 0x00 #define LCNPHY_TBL_ID_RFSEQ 0x08 #define LCNPHY_TBL_ID_GAIN_IDX 0x0d #define LCNPHY_TBL_ID_SW_CTRL 0x0f #define LCNPHY_TBL_ID_GAIN_TBL 0x12 #define LCNPHY_TBL_ID_SPUR 0x14 #define LCNPHY_TBL_ID_SAMPLEPLAY 0x15 #define LCNPHY_TBL_ID_SAMPLEPLAY1 0x16 #define LCNPHY_TX_PWR_CTRL_RATE_OFFSET 832 #define LCNPHY_TX_PWR_CTRL_MAC_OFFSET 128 #define LCNPHY_TX_PWR_CTRL_GAIN_OFFSET 192 #define LCNPHY_TX_PWR_CTRL_IQ_OFFSET 320 #define LCNPHY_TX_PWR_CTRL_LO_OFFSET 448 #define LCNPHY_TX_PWR_CTRL_PWR_OFFSET 576 #define LCNPHY_TX_PWR_CTRL_START_INDEX_2G_4313 140 #define LCNPHY_TX_PWR_CTRL_START_NPT 1 #define LCNPHY_TX_PWR_CTRL_MAX_NPT 7 #define LCNPHY_NOISE_SAMPLES_DEFAULT 5000 #define LCNPHY_ACI_DETECT_START 1 #define LCNPHY_ACI_DETECT_PROGRESS 2 #define LCNPHY_ACI_DETECT_STOP 3 #define LCNPHY_ACI_CRSHIFRMLO_TRSH 100 #define LCNPHY_ACI_GLITCH_TRSH 2000 #define LCNPHY_ACI_TMOUT 250 #define LCNPHY_ACI_DETECT_TIMEOUT 2 #define LCNPHY_ACI_START_DELAY 0 #define wlc_lcnphy_tx_gain_override_enabled(pi) \ (0 != (read_phy_reg((pi), 0x43b) & (0x1 << 6))) #define wlc_lcnphy_total_tx_frames(pi) \ wlapi_bmac_read_shm((pi)->sh->physhim, M_UCODE_MACSTAT + \ offsetof(struct macstat, txallfrm)) struct lcnphy_txgains { u16 gm_gain; u16 pga_gain; u16 pad_gain; u16 dac_gain; }; enum lcnphy_cal_mode { LCNPHY_CAL_FULL, LCNPHY_CAL_RECAL, LCNPHY_CAL_CURRECAL, LCNPHY_CAL_DIGCAL, LCNPHY_CAL_GCTRL }; struct lcnphy_rx_iqcomp { u8 chan; s16 a; s16 b; }; struct lcnphy_spb_tone { s16 re; s16 im; }; struct lcnphy_unsign16_struct { u16 re; u16 im; }; struct lcnphy_iq_est { u32 iq_prod; u32 i_pwr; u32 q_pwr; }; struct lcnphy_sfo_cfg { u16 ptcentreTs20; u16 ptcentreFactor; }; enum lcnphy_papd_cal_type { LCNPHY_PAPD_CAL_CW, LCNPHY_PAPD_CAL_OFDM }; typedef u16 iqcal_gain_params_lcnphy[9]; static const iqcal_gain_params_lcnphy tbl_iqcal_gainparams_lcnphy_2G[] = { {0, 0, 0, 0, 0, 0, 0, 0, 0}, }; static const iqcal_gain_params_lcnphy *tbl_iqcal_gainparams_lcnphy[1] = { tbl_iqcal_gainparams_lcnphy_2G, }; static const u16 iqcal_gainparams_numgains_lcnphy[1] = { ARRAY_SIZE(tbl_iqcal_gainparams_lcnphy_2G), }; static const struct lcnphy_sfo_cfg lcnphy_sfo_cfg[] = { {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078}, {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067}, {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055} }; static const u16 lcnphy_iqcal_loft_gainladder[] = { ((2 << 8) | 0), ((3 << 8) | 0), ((4 << 8) | 0), ((6 << 8) | 0), ((8 << 8) | 0), ((11 << 8) | 0), ((16 << 8) | 0), ((16 << 8) | 1), ((16 << 8) | 2), ((16 << 8) | 3), ((16 << 8) | 4), ((16 << 8) | 5), ((16 << 8) | 6), ((16 << 8) | 7), ((23 << 8) | 7), ((32 << 8) | 7), ((45 << 8) | 7), ((64 << 8) | 7), ((91 << 8) | 7), ((128 << 8) | 7) }; static const u16 lcnphy_iqcal_ir_gainladder[] = { ((1 << 8) | 0), ((2 << 8) | 0), ((4 << 8) | 0), ((6 << 8) | 0), ((8 << 8) | 0), ((11 << 8) | 0), ((16 << 8) | 0), ((23 << 8) | 0), ((32 << 8) | 0), ((45 << 8) | 0), ((64 << 8) | 0), ((64 << 8) | 1), ((64 << 8) | 2), ((64 << 8) | 3), ((64 << 8) | 4), ((64 << 8) | 5), ((64 << 8) | 6), ((64 << 8) | 7), ((91 << 8) | 7), ((128 << 8) | 7) }; static const struct lcnphy_spb_tone lcnphy_spb_tone_3750[] = { {88, 0}, {73, 49}, {34, 81}, {-17, 86}, {-62, 62}, {-86, 17}, {-81, -34}, {-49, -73}, {0, -88}, {49, -73}, {81, -34}, {86, 17}, {62, 62}, {17, 86}, {-34, 81}, {-73, 49}, {-88, 0}, {-73, -49}, {-34, -81}, {17, -86}, {62, -62}, {86, -17}, {81, 34}, {49, 73}, {0, 88}, {-49, 73}, {-81, 34}, {-86, -17}, {-62, -62}, {-17, -86}, {34, -81}, {73, -49}, }; static const u16 iqlo_loopback_rf_regs[20] = { RADIO_2064_REG036, RADIO_2064_REG11A, RADIO_2064_REG03A, RADIO_2064_REG025, RADIO_2064_REG028, RADIO_2064_REG005, RADIO_2064_REG112, RADIO_2064_REG0FF, RADIO_2064_REG11F, RADIO_2064_REG00B, RADIO_2064_REG113, RADIO_2064_REG007, RADIO_2064_REG0FC, RADIO_2064_REG0FD, RADIO_2064_REG012, RADIO_2064_REG057, RADIO_2064_REG059, RADIO_2064_REG05C, RADIO_2064_REG078, RADIO_2064_REG092, }; static const u16 tempsense_phy_regs[14] = { 0x503, 0x4a4, 0x4d0, 0x4d9, 0x4da, 0x4a6, 0x938, 0x939, 0x4d8, 0x4d0, 0x4d7, 0x4a5, 0x40d, 0x4a2, }; static const u16 rxiq_cal_rf_reg[11] = { RADIO_2064_REG098, RADIO_2064_REG116, RADIO_2064_REG12C, RADIO_2064_REG06A, RADIO_2064_REG00B, RADIO_2064_REG01B, RADIO_2064_REG113, RADIO_2064_REG01D, RADIO_2064_REG114, RADIO_2064_REG02E, RADIO_2064_REG12A, }; static const struct lcnphy_rx_iqcomp lcnphy_rx_iqcomp_table_rev0[] = { {1, 0, 0}, {2, 0, 0}, {3, 0, 0}, {4, 0, 0}, {5, 0, 0}, {6, 0, 0}, {7, 0, 0}, {8, 0, 0}, {9, 0, 0}, {10, 0, 0}, {11, 0, 0}, {12, 0, 0}, {13, 0, 0}, {14, 0, 0}, {34, 0, 0}, {38, 0, 0}, {42, 0, 0}, {46, 0, 0}, {36, 0, 0}, {40, 0, 0}, {44, 0, 0}, {48, 0, 0}, {52, 0, 0}, {56, 0, 0}, {60, 0, 0}, {64, 0, 0}, {100, 0, 0}, {104, 0, 0}, {108, 0, 0}, {112, 0, 0}, {116, 0, 0}, {120, 0, 0}, {124, 0, 0}, {128, 0, 0}, {132, 0, 0}, {136, 0, 0}, {140, 0, 0}, {149, 0, 0}, {153, 0, 0}, {157, 0, 0}, {161, 0, 0}, {165, 0, 0}, {184, 0, 0}, {188, 0, 0}, {192, 0, 0}, {196, 0, 0}, {200, 0, 0}, {204, 0, 0}, {208, 0, 0}, {212, 0, 0}, {216, 0, 0}, }; static const u32 lcnphy_23bitgaincode_table[] = { 0x200100, 0x200200, 0x200004, 0x200014, 0x200024, 0x200034, 0x200134, 0x200234, 0x200334, 0x200434, 0x200037, 0x200137, 0x200237, 0x200337, 0x200437, 0x000035, 0x000135, 0x000235, 0x000037, 0x000137, 0x000237, 0x000337, 0x00013f, 0x00023f, 0x00033f, 0x00034f, 0x00044f, 0x00144f, 0x00244f, 0x00254f, 0x00354f, 0x00454f, 0x00464f, 0x01464f, 0x02464f, 0x03464f, 0x04464f, }; static const s8 lcnphy_gain_table[] = { -16, -13, 10, 7, 4, 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92, }; static const s8 lcnphy_gain_index_offset_for_rssi[] = { 7, 7, 7, 7, 7, 7, 7, 8, 7, 7, 6, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 2, 2, 2, 2, 2, 2, -1, -2, -2, -2 }; struct chan_info_2064_lcnphy { uint chan; uint freq; u8 logen_buftune; u8 logen_rccr_tx; u8 txrf_mix_tune_ctrl; u8 pa_input_tune_g; u8 logen_rccr_rx; u8 pa_rxrf_lna1_freq_tune; u8 pa_rxrf_lna2_freq_tune; u8 rxrf_rxrf_spare1; }; static const struct chan_info_2064_lcnphy chan_info_2064_lcnphy[] = { {1, 2412, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {2, 2417, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {3, 2422, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {4, 2427, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {5, 2432, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {6, 2437, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {7, 2442, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {8, 2447, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {9, 2452, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {10, 2457, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {11, 2462, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {12, 2467, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {13, 2472, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, {14, 2484, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, }; static const struct lcnphy_radio_regs lcnphy_radio_regs_2064[] = { {0x00, 0, 0, 0, 0}, {0x01, 0x64, 0x64, 0, 0}, {0x02, 0x20, 0x20, 0, 0}, {0x03, 0x66, 0x66, 0, 0}, {0x04, 0xf8, 0xf8, 0, 0}, {0x05, 0, 0, 0, 0}, {0x06, 0x10, 0x10, 0, 0}, {0x07, 0, 0, 0, 0}, {0x08, 0, 0, 0, 0}, {0x09, 0, 0, 0, 0}, {0x0A, 0x37, 0x37, 0, 0}, {0x0B, 0x6, 0x6, 0, 0}, {0x0C, 0x55, 0x55, 0, 0}, {0x0D, 0x8b, 0x8b, 0, 0}, {0x0E, 0, 0, 0, 0}, {0x0F, 0x5, 0x5, 0, 0}, {0x10, 0, 0, 0, 0}, {0x11, 0xe, 0xe, 0, 0}, {0x12, 0, 0, 0, 0}, {0x13, 0xb, 0xb, 0, 0}, {0x14, 0x2, 0x2, 0, 0}, {0x15, 0x12, 0x12, 0, 0}, {0x16, 0x12, 0x12, 0, 0}, {0x17, 0xc, 0xc, 0, 0}, {0x18, 0xc, 0xc, 0, 0}, {0x19, 0xc, 0xc, 0, 0}, {0x1A, 0x8, 0x8, 0, 0}, {0x1B, 0x2, 0x2, 0, 0}, {0x1C, 0, 0, 0, 0}, {0x1D, 0x1, 0x1, 0, 0}, {0x1E, 0x12, 0x12, 0, 0}, {0x1F, 0x6e, 0x6e, 0, 0}, {0x20, 0x2, 0x2, 0, 0}, {0x21, 0x23, 0x23, 0, 0}, {0x22, 0x8, 0x8, 0, 0}, {0x23, 0, 0, 0, 0}, {0x24, 0, 0, 0, 0}, {0x25, 0xc, 0xc, 0, 0}, {0x26, 0x33, 0x33, 0, 0}, {0x27, 0x55, 0x55, 0, 0}, {0x28, 0, 0, 0, 0}, {0x29, 0x30, 0x30, 0, 0}, {0x2A, 0xb, 0xb, 0, 0}, {0x2B, 0x1b, 0x1b, 0, 0}, {0x2C, 0x3, 0x3, 0, 0}, {0x2D, 0x1b, 0x1b, 0, 0}, {0x2E, 0, 0, 0, 0}, {0x2F, 0x20, 0x20, 0, 0}, {0x30, 0xa, 0xa, 0, 0}, {0x31, 0, 0, 0, 0}, {0x32, 0x62, 0x62, 0, 0}, {0x33, 0x19, 0x19, 0, 0}, {0x34, 0x33, 0x33, 0, 0}, {0x35, 0x77, 0x77, 0, 0}, {0x36, 0, 0, 0, 0}, {0x37, 0x70, 0x70, 0, 0}, {0x38, 0x3, 0x3, 0, 0}, {0x39, 0xf, 0xf, 0, 0}, {0x3A, 0x6, 0x6, 0, 0}, {0x3B, 0xcf, 0xcf, 0, 0}, {0x3C, 0x1a, 0x1a, 0, 0}, {0x3D, 0x6, 0x6, 0, 0}, {0x3E, 0x42, 0x42, 0, 0}, {0x3F, 0, 0, 0, 0}, {0x40, 0xfb, 0xfb, 0, 0}, {0x41, 0x9a, 0x9a, 0, 0}, {0x42, 0x7a, 0x7a, 0, 0}, {0x43, 0x29, 0x29, 0, 0}, {0x44, 0, 0, 0, 0}, {0x45, 0x8, 0x8, 0, 0}, {0x46, 0xce, 0xce, 0, 0}, {0x47, 0x27, 0x27, 0, 0}, {0x48, 0x62, 0x62, 0, 0}, {0x49, 0x6, 0x6, 0, 0}, {0x4A, 0x58, 0x58, 0, 0}, {0x4B, 0xf7, 0xf7, 0, 0}, {0x4C, 0, 0, 0, 0}, {0x4D, 0xb3, 0xb3, 0, 0}, {0x4E, 0, 0, 0, 0}, {0x4F, 0x2, 0x2, 0, 0}, {0x50, 0, 0, 0, 0}, {0x51, 0x9, 0x9, 0, 0}, {0x52, 0x5, 0x5, 0, 0}, {0x53, 0x17, 0x17, 0, 0}, {0x54, 0x38, 0x38, 0, 0}, {0x55, 0, 0, 0, 0}, {0x56, 0, 0, 0, 0}, {0x57, 0xb, 0xb, 0, 0}, {0x58, 0, 0, 0, 0}, {0x59, 0, 0, 0, 0}, {0x5A, 0, 0, 0, 0}, {0x5B, 0, 0, 0, 0}, {0x5C, 0, 0, 0, 0}, {0x5D, 0, 0, 0, 0}, {0x5E, 0x88, 0x88, 0, 0}, {0x5F, 0xcc, 0xcc, 0, 0}, {0x60, 0x74, 0x74, 0, 0}, {0x61, 0x74, 0x74, 0, 0}, {0x62, 0x74, 0x74, 0, 0}, {0x63, 0x44, 0x44, 0, 0}, {0x64, 0x77, 0x77, 0, 0}, {0x65, 0x44, 0x44, 0, 0}, {0x66, 0x77, 0x77, 0, 0}, {0x67, 0x55, 0x55, 0, 0}, {0x68, 0x77, 0x77, 0, 0}, {0x69, 0x77, 0x77, 0, 0}, {0x6A, 0, 0, 0, 0}, {0x6B, 0x7f, 0x7f, 0, 0}, {0x6C, 0x8, 0x8, 0, 0}, {0x6D, 0, 0, 0, 0}, {0x6E, 0x88, 0x88, 0, 0}, {0x6F, 0x66, 0x66, 0, 0}, {0x70, 0x66, 0x66, 0, 0}, {0x71, 0x28, 0x28, 0, 0}, {0x72, 0x55, 0x55, 0, 0}, {0x73, 0x4, 0x4, 0, 0}, {0x74, 0, 0, 0, 0}, {0x75, 0, 0, 0, 0}, {0x76, 0, 0, 0, 0}, {0x77, 0x1, 0x1, 0, 0}, {0x78, 0xd6, 0xd6, 0, 0}, {0x79, 0, 0, 0, 0}, {0x7A, 0, 0, 0, 0}, {0x7B, 0, 0, 0, 0}, {0x7C, 0, 0, 0, 0}, {0x7D, 0, 0, 0, 0}, {0x7E, 0, 0, 0, 0}, {0x7F, 0, 0, 0, 0}, {0x80, 0, 0, 0, 0}, {0x81, 0, 0, 0, 0}, {0x82, 0, 0, 0, 0}, {0x83, 0xb4, 0xb4, 0, 0}, {0x84, 0x1, 0x1, 0, 0}, {0x85, 0x20, 0x20, 0, 0}, {0x86, 0x5, 0x5, 0, 0}, {0x87, 0xff, 0xff, 0, 0}, {0x88, 0x7, 0x7, 0, 0}, {0x89, 0x77, 0x77, 0, 0}, {0x8A, 0x77, 0x77, 0, 0}, {0x8B, 0x77, 0x77, 0, 0}, {0x8C, 0x77, 0x77, 0, 0}, {0x8D, 0x8, 0x8, 0, 0}, {0x8E, 0xa, 0xa, 0, 0}, {0x8F, 0x8, 0x8, 0, 0}, {0x90, 0x18, 0x18, 0, 0}, {0x91, 0x5, 0x5, 0, 0}, {0x92, 0x1f, 0x1f, 0, 0}, {0x93, 0x10, 0x10, 0, 0}, {0x94, 0x3, 0x3, 0, 0}, {0x95, 0, 0, 0, 0}, {0x96, 0, 0, 0, 0}, {0x97, 0xaa, 0xaa, 0, 0}, {0x98, 0, 0, 0, 0}, {0x99, 0x23, 0x23, 0, 0}, {0x9A, 0x7, 0x7, 0, 0}, {0x9B, 0xf, 0xf, 0, 0}, {0x9C, 0x10, 0x10, 0, 0}, {0x9D, 0x3, 0x3, 0, 0}, {0x9E, 0x4, 0x4, 0, 0}, {0x9F, 0x20, 0x20, 0, 0}, {0xA0, 0, 0, 0, 0}, {0xA1, 0, 0, 0, 0}, {0xA2, 0, 0, 0, 0}, {0xA3, 0, 0, 0, 0}, {0xA4, 0x1, 0x1, 0, 0}, {0xA5, 0x77, 0x77, 0, 0}, {0xA6, 0x77, 0x77, 0, 0}, {0xA7, 0x77, 0x77, 0, 0}, {0xA8, 0x77, 0x77, 0, 0}, {0xA9, 0x8c, 0x8c, 0, 0}, {0xAA, 0x88, 0x88, 0, 0}, {0xAB, 0x78, 0x78, 0, 0}, {0xAC, 0x57, 0x57, 0, 0}, {0xAD, 0x88, 0x88, 0, 0}, {0xAE, 0, 0, 0, 0}, {0xAF, 0x8, 0x8, 0, 0}, {0xB0, 0x88, 0x88, 0, 0}, {0xB1, 0, 0, 0, 0}, {0xB2, 0x1b, 0x1b, 0, 0}, {0xB3, 0x3, 0x3, 0, 0}, {0xB4, 0x24, 0x24, 0, 0}, {0xB5, 0x3, 0x3, 0, 0}, {0xB6, 0x1b, 0x1b, 0, 0}, {0xB7, 0x24, 0x24, 0, 0}, {0xB8, 0x3, 0x3, 0, 0}, {0xB9, 0, 0, 0, 0}, {0xBA, 0xaa, 0xaa, 0, 0}, {0xBB, 0, 0, 0, 0}, {0xBC, 0x4, 0x4, 0, 0}, {0xBD, 0, 0, 0, 0}, {0xBE, 0x8, 0x8, 0, 0}, {0xBF, 0x11, 0x11, 0, 0}, {0xC0, 0, 0, 0, 0}, {0xC1, 0, 0, 0, 0}, {0xC2, 0x62, 0x62, 0, 0}, {0xC3, 0x1e, 0x1e, 0, 0}, {0xC4, 0x33, 0x33, 0, 0}, {0xC5, 0x37, 0x37, 0, 0}, {0xC6, 0, 0, 0, 0}, {0xC7, 0x70, 0x70, 0, 0}, {0xC8, 0x1e, 0x1e, 0, 0}, {0xC9, 0x6, 0x6, 0, 0}, {0xCA, 0x4, 0x4, 0, 0}, {0xCB, 0x2f, 0x2f, 0, 0}, {0xCC, 0xf, 0xf, 0, 0}, {0xCD, 0, 0, 0, 0}, {0xCE, 0xff, 0xff, 0, 0}, {0xCF, 0x8, 0x8, 0, 0}, {0xD0, 0x3f, 0x3f, 0, 0}, {0xD1, 0x3f, 0x3f, 0, 0}, {0xD2, 0x3f, 0x3f, 0, 0}, {0xD3, 0, 0, 0, 0}, {0xD4, 0, 0, 0, 0}, {0xD5, 0, 0, 0, 0}, {0xD6, 0xcc, 0xcc, 0, 0}, {0xD7, 0, 0, 0, 0}, {0xD8, 0x8, 0x8, 0, 0}, {0xD9, 0x8, 0x8, 0, 0}, {0xDA, 0x8, 0x8, 0, 0}, {0xDB, 0x11, 0x11, 0, 0}, {0xDC, 0, 0, 0, 0}, {0xDD, 0x87, 0x87, 0, 0}, {0xDE, 0x88, 0x88, 0, 0}, {0xDF, 0x8, 0x8, 0, 0}, {0xE0, 0x8, 0x8, 0, 0}, {0xE1, 0x8, 0x8, 0, 0}, {0xE2, 0, 0, 0, 0}, {0xE3, 0, 0, 0, 0}, {0xE4, 0, 0, 0, 0}, {0xE5, 0xf5, 0xf5, 0, 0}, {0xE6, 0x30, 0x30, 0, 0}, {0xE7, 0x1, 0x1, 0, 0}, {0xE8, 0, 0, 0, 0}, {0xE9, 0xff, 0xff, 0, 0}, {0xEA, 0, 0, 0, 0}, {0xEB, 0, 0, 0, 0}, {0xEC, 0x22, 0x22, 0, 0}, {0xED, 0, 0, 0, 0}, {0xEE, 0, 0, 0, 0}, {0xEF, 0, 0, 0, 0}, {0xF0, 0x3, 0x3, 0, 0}, {0xF1, 0x1, 0x1, 0, 0}, {0xF2, 0, 0, 0, 0}, {0xF3, 0, 0, 0, 0}, {0xF4, 0, 0, 0, 0}, {0xF5, 0, 0, 0, 0}, {0xF6, 0, 0, 0, 0}, {0xF7, 0x6, 0x6, 0, 0}, {0xF8, 0, 0, 0, 0}, {0xF9, 0, 0, 0, 0}, {0xFA, 0x40, 0x40, 0, 0}, {0xFB, 0, 0, 0, 0}, {0xFC, 0x1, 0x1, 0, 0}, {0xFD, 0x80, 0x80, 0, 0}, {0xFE, 0x2, 0x2, 0, 0}, {0xFF, 0x10, 0x10, 0, 0}, {0x100, 0x2, 0x2, 0, 0}, {0x101, 0x1e, 0x1e, 0, 0}, {0x102, 0x1e, 0x1e, 0, 0}, {0x103, 0, 0, 0, 0}, {0x104, 0x1f, 0x1f, 0, 0}, {0x105, 0, 0x8, 0, 1}, {0x106, 0x2a, 0x2a, 0, 0}, {0x107, 0xf, 0xf, 0, 0}, {0x108, 0, 0, 0, 0}, {0x109, 0, 0, 0, 0}, {0x10A, 0, 0, 0, 0}, {0x10B, 0, 0, 0, 0}, {0x10C, 0, 0, 0, 0}, {0x10D, 0, 0, 0, 0}, {0x10E, 0, 0, 0, 0}, {0x10F, 0, 0, 0, 0}, {0x110, 0, 0, 0, 0}, {0x111, 0, 0, 0, 0}, {0x112, 0, 0, 0, 0}, {0x113, 0, 0, 0, 0}, {0x114, 0, 0, 0, 0}, {0x115, 0, 0, 0, 0}, {0x116, 0, 0, 0, 0}, {0x117, 0, 0, 0, 0}, {0x118, 0, 0, 0, 0}, {0x119, 0, 0, 0, 0}, {0x11A, 0, 0, 0, 0}, {0x11B, 0, 0, 0, 0}, {0x11C, 0x1, 0x1, 0, 0}, {0x11D, 0, 0, 0, 0}, {0x11E, 0, 0, 0, 0}, {0x11F, 0, 0, 0, 0}, {0x120, 0, 0, 0, 0}, {0x121, 0, 0, 0, 0}, {0x122, 0x80, 0x80, 0, 0}, {0x123, 0, 0, 0, 0}, {0x124, 0xf8, 0xf8, 0, 0}, {0x125, 0, 0, 0, 0}, {0x126, 0, 0, 0, 0}, {0x127, 0, 0, 0, 0}, {0x128, 0, 0, 0, 0}, {0x129, 0, 0, 0, 0}, {0x12A, 0, 0, 0, 0}, {0x12B, 0, 0, 0, 0}, {0x12C, 0, 0, 0, 0}, {0x12D, 0, 0, 0, 0}, {0x12E, 0, 0, 0, 0}, {0x12F, 0, 0, 0, 0}, {0x130, 0, 0, 0, 0}, {0xFFFF, 0, 0, 0, 0} }; #define LCNPHY_NUM_DIG_FILT_COEFFS 16 #define LCNPHY_NUM_TX_DIG_FILTERS_CCK 13 static const u16 LCNPHY_txdigfiltcoeffs_cck[LCNPHY_NUM_TX_DIG_FILTERS_CCK] [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = { {0, 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, 1582, 64, 128, 64,}, {1, 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, 1863, 93, 167, 93,}, {2, 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192, 778, 1582, 64, 128, 64,}, {3, 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205, 754, 1760, 170, 340, 170,}, {20, 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205, 767, 1760, 256, 185, 256,}, {21, 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205, 767, 1760, 256, 273, 256,}, {22, 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205, 767, 1760, 256, 352, 256,}, {23, 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205, 767, 1760, 128, 233, 128,}, {24, 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766, 1760, 256, 1881, 256,}, {25, 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765, 1760, 256, 1881, 256,}, {26, 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614, 1864, 128, 384, 288,}, {27, 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576, 613, 1864, 128, 384, 288,}, {30, 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205, 754, 1760, 170, 340, 170,}, }; #define LCNPHY_NUM_TX_DIG_FILTERS_OFDM 3 static const u16 LCNPHY_txdigfiltcoeffs_ofdm[LCNPHY_NUM_TX_DIG_FILTERS_OFDM] [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = { {0, 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80,}, {1, 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750, 0xFE2B, 212, 0xFFCE, 212,}, {2, 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748, 0xFEF2, 128, 0xFFE2, 128} }; #define wlc_lcnphy_set_start_tx_pwr_idx(pi, idx) \ mod_phy_reg(pi, 0x4a4, \ (0x1ff << 0), \ (u16)(idx) << 0) #define wlc_lcnphy_set_tx_pwr_npt(pi, npt) \ mod_phy_reg(pi, 0x4a5, \ (0x7 << 8), \ (u16)(npt) << 8) #define wlc_lcnphy_get_tx_pwr_ctrl(pi) \ (read_phy_reg((pi), 0x4a4) & \ ((0x1 << 15) | \ (0x1 << 14) | \ (0x1 << 13))) #define wlc_lcnphy_get_tx_pwr_npt(pi) \ ((read_phy_reg(pi, 0x4a5) & \ (0x7 << 8)) >> \ 8) #define wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on(pi) \ (read_phy_reg(pi, 0x473) & 0x1ff) #define wlc_lcnphy_get_target_tx_pwr(pi) \ ((read_phy_reg(pi, 0x4a7) & \ (0xff << 0)) >> \ 0) #define wlc_lcnphy_set_target_tx_pwr(pi, target) \ mod_phy_reg(pi, 0x4a7, \ (0xff << 0), \ (u16)(target) << 0) #define wlc_radio_2064_rcal_done(pi) \ (0 != (read_radio_reg(pi, RADIO_2064_REG05C) & 0x20)) #define tempsense_done(pi) \ (0x8000 == (read_phy_reg(pi, 0x476) & 0x8000)) #define LCNPHY_IQLOCC_READ(val) \ ((u8)(-(s8)(((val) & 0xf0) >> 4) + (s8)((val) & 0x0f))) #define FIXED_TXPWR 78 #define LCNPHY_TEMPSENSE(val) ((s16)((val > 255) ? (val - 512) : val)) void wlc_lcnphy_write_table(struct brcms_phy *pi, const struct phytbl_info *pti) { wlc_phy_write_table(pi, pti, 0x455, 0x457, 0x456); } void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti) { wlc_phy_read_table(pi, pti, 0x455, 0x457, 0x456); } static void wlc_lcnphy_common_read_table(struct brcms_phy *pi, u32 tbl_id, const u16 *tbl_ptr, u32 tbl_len, u32 tbl_width, u32 tbl_offset) { struct phytbl_info tab; tab.tbl_id = tbl_id; tab.tbl_ptr = tbl_ptr; tab.tbl_len = tbl_len; tab.tbl_width = tbl_width; tab.tbl_offset = tbl_offset; wlc_lcnphy_read_table(pi, &tab); } static void wlc_lcnphy_common_write_table(struct brcms_phy *pi, u32 tbl_id, const u16 *tbl_ptr, u32 tbl_len, u32 tbl_width, u32 tbl_offset) { struct phytbl_info tab; tab.tbl_id = tbl_id; tab.tbl_ptr = tbl_ptr; tab.tbl_len = tbl_len; tab.tbl_width = tbl_width; tab.tbl_offset = tbl_offset; wlc_lcnphy_write_table(pi, &tab); } static u32 wlc_lcnphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision) { u32 quotient, remainder, roundup, rbit; quotient = dividend / divisor; remainder = dividend % divisor; rbit = divisor & 1; roundup = (divisor >> 1) + rbit; while (precision--) { quotient <<= 1; if (remainder >= roundup) { quotient++; remainder = ((remainder - roundup) << 1) + rbit; } else { remainder <<= 1; } } if (remainder >= roundup) quotient++; return quotient; } static int wlc_lcnphy_calc_floor(s16 coeff_x, int type) { int k; k = 0; if (type == 0) { if (coeff_x < 0) k = (coeff_x - 1) / 2; else k = coeff_x / 2; } if (type == 1) { if ((coeff_x + 1) < 0) k = (coeff_x) / 2; else k = (coeff_x + 1) / 2; } return k; } static void wlc_lcnphy_get_tx_gain(struct brcms_phy *pi, struct lcnphy_txgains *gains) { u16 dac_gain, rfgain0, rfgain1; dac_gain = read_phy_reg(pi, 0x439) >> 0; gains->dac_gain = (dac_gain & 0x380) >> 7; rfgain0 = (read_phy_reg(pi, 0x4b5) & (0xffff << 0)) >> 0; rfgain1 = (read_phy_reg(pi, 0x4fb) & (0x7fff << 0)) >> 0; gains->gm_gain = rfgain0 & 0xff; gains->pga_gain = (rfgain0 >> 8) & 0xff; gains->pad_gain = rfgain1 & 0xff; } static void wlc_lcnphy_set_dac_gain(struct brcms_phy *pi, u16 dac_gain) { u16 dac_ctrl; dac_ctrl = (read_phy_reg(pi, 0x439) >> 0); dac_ctrl = dac_ctrl & 0xc7f; dac_ctrl = dac_ctrl | (dac_gain << 7); mod_phy_reg(pi, 0x439, (0xfff << 0), (dac_ctrl) << 0); } static void wlc_lcnphy_set_tx_gain_override(struct brcms_phy *pi, bool bEnable) { u16 bit = bEnable ? 1 : 0; mod_phy_reg(pi, 0x4b0, (0x1 << 7), bit << 7); mod_phy_reg(pi, 0x4b0, (0x1 << 14), bit << 14); mod_phy_reg(pi, 0x43b, (0x1 << 6), bit << 6); } static void wlc_lcnphy_rx_gain_override_enable(struct brcms_phy *pi, bool enable) { u16 ebit = enable ? 1 : 0; mod_phy_reg(pi, 0x4b0, (0x1 << 8), ebit << 8); mod_phy_reg(pi, 0x44c, (0x1 << 0), ebit << 0); if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { mod_phy_reg(pi, 0x44c, (0x1 << 4), ebit << 4); mod_phy_reg(pi, 0x44c, (0x1 << 6), ebit << 6); mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5); mod_phy_reg(pi, 0x4b0, (0x1 << 6), ebit << 6); } else { mod_phy_reg(pi, 0x4b0, (0x1 << 12), ebit << 12); mod_phy_reg(pi, 0x4b0, (0x1 << 13), ebit << 13); mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5); } if (CHSPEC_IS2G(pi->radio_chanspec)) { mod_phy_reg(pi, 0x4b0, (0x1 << 10), ebit << 10); mod_phy_reg(pi, 0x4e5, (0x1 << 3), ebit << 3); } } static void wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi, u16 trsw, u16 ext_lna, u16 biq2, u16 biq1, u16 tia, u16 lna2, u16 lna1) { u16 gain0_15, gain16_19; gain16_19 = biq2 & 0xf; gain0_15 = ((biq1 & 0xf) << 12) | ((tia & 0xf) << 8) | ((lna2 & 0x3) << 6) | ((lna2 & 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0); mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0); mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0); mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11); if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10); } else { mod_phy_reg(pi, 0x4b1, (0x1 << 10), 0 << 10); mod_phy_reg(pi, 0x4b1, (0x1 << 15), 0 << 15); mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); } mod_phy_reg(pi, 0x44d, (0x1 << 0), (!trsw) << 0); } static void wlc_lcnphy_set_trsw_override(struct brcms_phy *pi, bool tx, bool rx) { mod_phy_reg(pi, 0x44d, (0x1 << 1) | (0x1 << 0), (tx ? (0x1 << 1) : 0) | (rx ? (0x1 << 0) : 0)); or_phy_reg(pi, 0x44c, (0x1 << 1) | (0x1 << 0)); } static void wlc_lcnphy_clear_trsw_override(struct brcms_phy *pi) { and_phy_reg(pi, 0x44c, (u16) ~((0x1 << 1) | (0x1 << 0))); } static void wlc_lcnphy_set_rx_iq_comp(struct brcms_phy *pi, u16 a, u16 b) { mod_phy_reg(pi, 0x645, (0x3ff << 0), (a) << 0); mod_phy_reg(pi, 0x646, (0x3ff << 0), (b) << 0); mod_phy_reg(pi, 0x647, (0x3ff << 0), (a) << 0); mod_phy_reg(pi, 0x648, (0x3ff << 0), (b) << 0); mod_phy_reg(pi, 0x649, (0x3ff << 0), (a) << 0); mod_phy_reg(pi, 0x64a, (0x3ff << 0), (b) << 0); } static bool wlc_lcnphy_rx_iq_est(struct brcms_phy *pi, u16 num_samps, u8 wait_time, struct lcnphy_iq_est *iq_est) { int wait_count = 0; bool result = true; u8 phybw40; phybw40 = CHSPEC_IS40(pi->radio_chanspec); mod_phy_reg(pi, 0x6da, (0x1 << 5), (1) << 5); mod_phy_reg(pi, 0x410, (0x1 << 3), (0) << 3); mod_phy_reg(pi, 0x482, (0xffff << 0), (num_samps) << 0); mod_phy_reg(pi, 0x481, (0xff << 0), ((u16) wait_time) << 0); mod_phy_reg(pi, 0x481, (0x1 << 8), (0) << 8); mod_phy_reg(pi, 0x481, (0x1 << 9), (1) << 9); while (read_phy_reg(pi, 0x481) & (0x1 << 9)) { if (wait_count > (10 * 500)) { result = false; goto cleanup; } udelay(100); wait_count++; } iq_est->iq_prod = ((u32) read_phy_reg(pi, 0x483) << 16) | (u32) read_phy_reg(pi, 0x484); iq_est->i_pwr = ((u32) read_phy_reg(pi, 0x485) << 16) | (u32) read_phy_reg(pi, 0x486); iq_est->q_pwr = ((u32) read_phy_reg(pi, 0x487) << 16) | (u32) read_phy_reg(pi, 0x488); cleanup: mod_phy_reg(pi, 0x410, (0x1 << 3), (1) << 3); mod_phy_reg(pi, 0x6da, (0x1 << 5), (0) << 5); return result; } static bool wlc_lcnphy_calc_rx_iq_comp(struct brcms_phy *pi, u16 num_samps) { #define LCNPHY_MIN_RXIQ_PWR 2 bool result; u16 a0_new, b0_new; struct lcnphy_iq_est iq_est = { 0, 0, 0 }; s32 a, b, temp; s16 iq_nbits, qq_nbits, arsh, brsh; s32 iq; u32 ii, qq; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; a0_new = ((read_phy_reg(pi, 0x645) & (0x3ff << 0)) >> 0); b0_new = ((read_phy_reg(pi, 0x646) & (0x3ff << 0)) >> 0); mod_phy_reg(pi, 0x6d1, (0x1 << 2), (0) << 2); mod_phy_reg(pi, 0x64b, (0x1 << 6), (1) << 6); wlc_lcnphy_set_rx_iq_comp(pi, 0, 0); result = wlc_lcnphy_rx_iq_est(pi, num_samps, 32, &iq_est); if (!result) goto cleanup; iq = (s32) iq_est.iq_prod; ii = iq_est.i_pwr; qq = iq_est.q_pwr; if ((ii + qq) < LCNPHY_MIN_RXIQ_PWR) { result = false; goto cleanup; } iq_nbits = wlc_phy_nbits(iq); qq_nbits = wlc_phy_nbits(qq); arsh = 10 - (30 - iq_nbits); if (arsh >= 0) { a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); temp = (s32) (ii >> arsh); if (temp == 0) return false; } else { a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); temp = (s32) (ii << -arsh); if (temp == 0) return false; } a /= temp; brsh = qq_nbits - 31 + 20; if (brsh >= 0) { b = (qq << (31 - qq_nbits)); temp = (s32) (ii >> brsh); if (temp == 0) return false; } else { b = (qq << (31 - qq_nbits)); temp = (s32) (ii << -brsh); if (temp == 0) return false; } b /= temp; b -= a * a; b = (s32) int_sqrt((unsigned long) b); b -= (1 << 10); a0_new = (u16) (a & 0x3ff); b0_new = (u16) (b & 0x3ff); cleanup: wlc_lcnphy_set_rx_iq_comp(pi, a0_new, b0_new); mod_phy_reg(pi, 0x64b, (0x1 << 0), (1) << 0); mod_phy_reg(pi, 0x64b, (0x1 << 3), (1) << 3); pi_lcn->lcnphy_cal_results.rxiqcal_coeff_a0 = a0_new; pi_lcn->lcnphy_cal_results.rxiqcal_coeff_b0 = b0_new; return result; } static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples) { struct lcnphy_iq_est iq_est = { 0, 0, 0 }; if (!wlc_lcnphy_rx_iq_est(pi, nsamples, 32, &iq_est)) return 0; return (iq_est.i_pwr + iq_est.q_pwr) / nsamples; } static bool wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi, const struct lcnphy_rx_iqcomp *iqcomp, int iqcomp_sz, bool tx_switch, bool rx_switch, int module, int tx_gain_idx) { struct lcnphy_txgains old_gains; u16 tx_pwr_ctrl; u8 tx_gain_index_old = 0; bool result = false, tx_gain_override_old = false; u16 i, Core1TxControl_old, RFOverride0_old, RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old, rfoverride3_old, rfoverride3val_old, rfoverride4_old, rfoverride4val_old, afectrlovr_old, afectrlovrval_old; int tia_gain; u32 received_power, rx_pwr_threshold; u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl; u16 values_to_save[11]; s16 *ptr; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC); if (NULL == ptr) return false; if (module == 2) { while (iqcomp_sz--) { if (iqcomp[iqcomp_sz].chan == CHSPEC_CHANNEL(pi->radio_chanspec)) { wlc_lcnphy_set_rx_iq_comp(pi, (u16) iqcomp[iqcomp_sz].a, (u16) iqcomp[iqcomp_sz].b); result = true; break; } } goto cal_done; } if (module == 1) { tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); for (i = 0; i < 11; i++) values_to_save[i] = read_radio_reg(pi, rxiq_cal_rf_reg[i]); Core1TxControl_old = read_phy_reg(pi, 0x631); or_phy_reg(pi, 0x631, 0x0015); RFOverride0_old = read_phy_reg(pi, 0x44c); RFOverrideVal0_old = read_phy_reg(pi, 0x44d); rfoverride2_old = read_phy_reg(pi, 0x4b0); rfoverride2val_old = read_phy_reg(pi, 0x4b1); rfoverride3_old = read_phy_reg(pi, 0x4f9); rfoverride3val_old = read_phy_reg(pi, 0x4fa); rfoverride4_old = read_phy_reg(pi, 0x938); rfoverride4val_old = read_phy_reg(pi, 0x939); afectrlovr_old = read_phy_reg(pi, 0x43b); afectrlovrval_old = read_phy_reg(pi, 0x43c); old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); if (tx_gain_override_old) { wlc_lcnphy_get_tx_gain(pi, &old_gains); tx_gain_index_old = pi_lcn->lcnphy_current_index; } wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx); mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); write_radio_reg(pi, RADIO_2064_REG116, 0x06); write_radio_reg(pi, RADIO_2064_REG12C, 0x07); write_radio_reg(pi, RADIO_2064_REG06A, 0xd3); write_radio_reg(pi, RADIO_2064_REG098, 0x03); write_radio_reg(pi, RADIO_2064_REG00B, 0x7); mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4); write_radio_reg(pi, RADIO_2064_REG01D, 0x01); write_radio_reg(pi, RADIO_2064_REG114, 0x01); write_radio_reg(pi, RADIO_2064_REG02E, 0x10); write_radio_reg(pi, RADIO_2064_REG12A, 0x08); mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0); mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2); mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2); mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3); mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3); mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5); mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5); mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0); write_phy_reg(pi, 0x6da, 0xffff); or_phy_reg(pi, 0x6db, 0x3); wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch); wlc_lcnphy_rx_gain_override_enable(pi, true); tia_gain = 8; rx_pwr_threshold = 950; while (tia_gain > 0) { tia_gain -= 1; wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 2, 2, (u16) tia_gain, 1, 0); udelay(500); received_power = wlc_lcnphy_measure_digital_power(pi, 2000); if (received_power < rx_pwr_threshold) break; } result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff); wlc_lcnphy_stop_tx_tone(pi); write_phy_reg(pi, 0x631, Core1TxControl_old); write_phy_reg(pi, 0x44c, RFOverrideVal0_old); write_phy_reg(pi, 0x44d, RFOverrideVal0_old); write_phy_reg(pi, 0x4b0, rfoverride2_old); write_phy_reg(pi, 0x4b1, rfoverride2val_old); write_phy_reg(pi, 0x4f9, rfoverride3_old); write_phy_reg(pi, 0x4fa, rfoverride3val_old); write_phy_reg(pi, 0x938, rfoverride4_old); write_phy_reg(pi, 0x939, rfoverride4val_old); write_phy_reg(pi, 0x43b, afectrlovr_old); write_phy_reg(pi, 0x43c, afectrlovrval_old); write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl); wlc_lcnphy_clear_trsw_override(pi); mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2); for (i = 0; i < 11; i++) write_radio_reg(pi, rxiq_cal_rf_reg[i], values_to_save[i]); if (tx_gain_override_old) wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old); else wlc_lcnphy_disable_tx_gain_override(pi); wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl); wlc_lcnphy_rx_gain_override_enable(pi, false); } cal_done: kfree(ptr); return result; } s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi) { s8 index; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; if (txpwrctrl_off(pi)) index = pi_lcn->lcnphy_current_index; else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) index = (s8) (wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on( pi) / 2); else index = pi_lcn->lcnphy_current_index; return index; } void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel) { u16 afectrlovr, afectrlovrval; afectrlovr = read_phy_reg(pi, 0x43b); afectrlovrval = read_phy_reg(pi, 0x43c); if (channel != 0) { mod_phy_reg(pi, 0x43b, (0x1 << 1), (1) << 1); mod_phy_reg(pi, 0x43c, (0x1 << 1), (0) << 1); mod_phy_reg(pi, 0x43b, (0x1 << 4), (1) << 4); mod_phy_reg(pi, 0x43c, (0x1 << 6), (0) << 6); write_phy_reg(pi, 0x44b, 0xffff); wlc_lcnphy_tx_pu(pi, 1); mod_phy_reg(pi, 0x634, (0xff << 8), (0) << 8); or_phy_reg(pi, 0x6da, 0x0080); or_phy_reg(pi, 0x00a, 0x228); } else { and_phy_reg(pi, 0x00a, ~(0x228)); and_phy_reg(pi, 0x6da, 0xFF7F); write_phy_reg(pi, 0x43b, afectrlovr); write_phy_reg(pi, 0x43c, afectrlovrval); } } static void wlc_lcnphy_toggle_afe_pwdn(struct brcms_phy *pi) { u16 save_AfeCtrlOvrVal, save_AfeCtrlOvr; save_AfeCtrlOvrVal = read_phy_reg(pi, 0x43c); save_AfeCtrlOvr = read_phy_reg(pi, 0x43b); write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal | 0x1); write_phy_reg(pi, 0x43b, save_AfeCtrlOvr | 0x1); write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal & 0xfffe); write_phy_reg(pi, 0x43b, save_AfeCtrlOvr & 0xfffe); write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal); write_phy_reg(pi, 0x43b, save_AfeCtrlOvr); } static void wlc_lcnphy_txrx_spur_avoidance_mode(struct brcms_phy *pi, bool enable) { if (enable) { write_phy_reg(pi, 0x942, 0x7); write_phy_reg(pi, 0x93b, ((1 << 13) + 23)); write_phy_reg(pi, 0x93c, ((1 << 13) + 1989)); write_phy_reg(pi, 0x44a, 0x084); write_phy_reg(pi, 0x44a, 0x080); write_phy_reg(pi, 0x6d3, 0x2222); write_phy_reg(pi, 0x6d3, 0x2220); } else { write_phy_reg(pi, 0x942, 0x0); write_phy_reg(pi, 0x93b, ((0 << 13) + 23)); write_phy_reg(pi, 0x93c, ((0 << 13) + 1989)); } wlapi_switch_macfreq(pi->sh->physhim, enable); } static void wlc_lcnphy_set_chanspec_tweaks(struct brcms_phy *pi, u16 chanspec) { u8 channel = CHSPEC_CHANNEL(chanspec); struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; if (channel == 14) mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8); else mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8); pi_lcn->lcnphy_bandedge_corr = 2; if (channel == 1) pi_lcn->lcnphy_bandedge_corr = 4; if (channel == 1 || channel == 2 || channel == 3 || channel == 4 || channel == 9 || channel == 10 || channel == 11 || channel == 12) { si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03000c04); si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x0); si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x200005c0); si_pmu_pllupd(pi->sh->sih); write_phy_reg(pi, 0x942, 0); wlc_lcnphy_txrx_spur_avoidance_mode(pi, false); pi_lcn->lcnphy_spurmod = false; mod_phy_reg(pi, 0x424, (0xff << 8), (0x1b) << 8); write_phy_reg(pi, 0x425, 0x5907); } else { si_pmu_pllcontrol(pi->sh->sih, 0x2, 0xffffffff, 0x03140c04); si_pmu_pllcontrol(pi->sh->sih, 0x3, 0xffffff, 0x333333); si_pmu_pllcontrol(pi->sh->sih, 0x4, 0xffffffff, 0x202c2820); si_pmu_pllupd(pi->sh->sih); write_phy_reg(pi, 0x942, 0); wlc_lcnphy_txrx_spur_avoidance_mode(pi, true); pi_lcn->lcnphy_spurmod = false; mod_phy_reg(pi, 0x424, (0xff << 8), (0x1f) << 8); write_phy_reg(pi, 0x425, 0x590a); } or_phy_reg(pi, 0x44a, 0x44); write_phy_reg(pi, 0x44a, 0x80); } static void wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel) { uint i; const struct chan_info_2064_lcnphy *ci; u8 rfpll_doubler = 0; u8 pll_pwrup, pll_pwrup_ovr; s32 qFxtal, qFref, qFvco, qFcal; u8 d15, d16, f16, e44, e45; u32 div_int, div_frac, fvco3, fpfd, fref3, fcal_div; u16 loop_bw, d30, setCount; u8 h29, h28_ten, e30, h30_ten, cp_current; u16 g30, d28; ci = &chan_info_2064_lcnphy[0]; rfpll_doubler = 1; mod_radio_reg(pi, RADIO_2064_REG09D, 0x4, 0x1 << 2); write_radio_reg(pi, RADIO_2064_REG09E, 0xf); if (!rfpll_doubler) { loop_bw = PLL_2064_LOOP_BW; d30 = PLL_2064_D30; } else { loop_bw = PLL_2064_LOOP_BW_DOUBLER; d30 = PLL_2064_D30_DOUBLER; } if (CHSPEC_IS2G(pi->radio_chanspec)) { for (i = 0; i < ARRAY_SIZE(chan_info_2064_lcnphy); i++) if (chan_info_2064_lcnphy[i].chan == channel) break; if (i >= ARRAY_SIZE(chan_info_2064_lcnphy)) return; ci = &chan_info_2064_lcnphy[i]; } write_radio_reg(pi, RADIO_2064_REG02A, ci->logen_buftune); mod_radio_reg(pi, RADIO_2064_REG030, 0x3, ci->logen_rccr_tx); mod_radio_reg(pi, RADIO_2064_REG091, 0x3, ci->txrf_mix_tune_ctrl); mod_radio_reg(pi, RADIO_2064_REG038, 0xf, ci->pa_input_tune_g); mod_radio_reg(pi, RADIO_2064_REG030, 0x3 << 2, (ci->logen_rccr_rx) << 2); mod_radio_reg(pi, RADIO_2064_REG05E, 0xf, ci->pa_rxrf_lna1_freq_tune); mod_radio_reg(pi, RADIO_2064_REG05E, (0xf) << 4, (ci->pa_rxrf_lna2_freq_tune) << 4); write_radio_reg(pi, RADIO_2064_REG06C, ci->rxrf_rxrf_spare1); pll_pwrup = (u8) read_radio_reg(pi, RADIO_2064_REG044); pll_pwrup_ovr = (u8) read_radio_reg(pi, RADIO_2064_REG12B); or_radio_reg(pi, RADIO_2064_REG044, 0x07); or_radio_reg(pi, RADIO_2064_REG12B, (0x07) << 1); e44 = 0; e45 = 0; fpfd = rfpll_doubler ? (pi->xtalfreq << 1) : (pi->xtalfreq); if (pi->xtalfreq > 26000000) e44 = 1; if (pi->xtalfreq > 52000000) e45 = 1; if (e44 == 0) fcal_div = 1; else if (e45 == 0) fcal_div = 2; else fcal_div = 4; fvco3 = (ci->freq * 3); fref3 = 2 * fpfd; qFxtal = wlc_lcnphy_qdiv_roundup(pi->xtalfreq, PLL_2064_MHZ, 16); qFref = wlc_lcnphy_qdiv_roundup(fpfd, PLL_2064_MHZ, 16); qFcal = pi->xtalfreq * fcal_div / PLL_2064_MHZ; qFvco = wlc_lcnphy_qdiv_roundup(fvco3, 2, 16); write_radio_reg(pi, RADIO_2064_REG04F, 0x02); d15 = (pi->xtalfreq * fcal_div * 4 / 5) / PLL_2064_MHZ - 1; write_radio_reg(pi, RADIO_2064_REG052, (0x07 & (d15 >> 2))); write_radio_reg(pi, RADIO_2064_REG053, (d15 & 0x3) << 5); d16 = (qFcal * 8 / (d15 + 1)) - 1; write_radio_reg(pi, RADIO_2064_REG051, d16); f16 = ((d16 + 1) * (d15 + 1)) / qFcal; setCount = f16 * 3 * (ci->freq) / 32 - 1; mod_radio_reg(pi, RADIO_2064_REG053, (0x0f << 0), (u8) (setCount >> 8)); or_radio_reg(pi, RADIO_2064_REG053, 0x10); write_radio_reg(pi, RADIO_2064_REG054, (u8) (setCount & 0xff)); div_int = ((fvco3 * (PLL_2064_MHZ >> 4)) / fref3) << 4; div_frac = ((fvco3 * (PLL_2064_MHZ >> 4)) % fref3) << 4; while (div_frac >= fref3) { div_int++; div_frac -= fref3; } div_frac = wlc_lcnphy_qdiv_roundup(div_frac, fref3, 20); mod_radio_reg(pi, RADIO_2064_REG045, (0x1f << 0), (u8) (div_int >> 4)); mod_radio_reg(pi, RADIO_2064_REG046, (0x1f << 4), (u8) (div_int << 4)); mod_radio_reg(pi, RADIO_2064_REG046, (0x0f << 0), (u8) (div_frac >> 16)); write_radio_reg(pi, RADIO_2064_REG047, (u8) (div_frac >> 8) & 0xff); write_radio_reg(pi, RADIO_2064_REG048, (u8) div_frac & 0xff); write_radio_reg(pi, RADIO_2064_REG040, 0xfb); write_radio_reg(pi, RADIO_2064_REG041, 0x9A); write_radio_reg(pi, RADIO_2064_REG042, 0xA3); write_radio_reg(pi, RADIO_2064_REG043, 0x0C); h29 = LCN_BW_LMT / loop_bw; d28 = (((PLL_2064_HIGH_END_KVCO - PLL_2064_LOW_END_KVCO) * (fvco3 / 2 - PLL_2064_LOW_END_VCO)) / (PLL_2064_HIGH_END_VCO - PLL_2064_LOW_END_VCO)) + PLL_2064_LOW_END_KVCO; h28_ten = (d28 * 10) / LCN_VCO_DIV; e30 = (d30 - LCN_OFFSET) / LCN_FACT; g30 = LCN_OFFSET + (e30 * LCN_FACT); h30_ten = (g30 * 10) / LCN_CUR_DIV; cp_current = ((LCN_CUR_LMT * h29 * LCN_MULT * 100) / h28_ten) / h30_ten; mod_radio_reg(pi, RADIO_2064_REG03C, 0x3f, cp_current); if (channel >= 1 && channel <= 5) write_radio_reg(pi, RADIO_2064_REG03C, 0x8); else write_radio_reg(pi, RADIO_2064_REG03C, 0x7); write_radio_reg(pi, RADIO_2064_REG03D, 0x3); mod_radio_reg(pi, RADIO_2064_REG044, 0x0c, 0x0c); udelay(1); wlc_2064_vco_cal(pi); write_radio_reg(pi, RADIO_2064_REG044, pll_pwrup); write_radio_reg(pi, RADIO_2064_REG12B, pll_pwrup_ovr); if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { write_radio_reg(pi, RADIO_2064_REG038, 3); write_radio_reg(pi, RADIO_2064_REG091, 7); } } static int wlc_lcnphy_load_tx_iir_filter(struct brcms_phy *pi, bool is_ofdm, s16 filt_type) { s16 filt_index = -1; int j; u16 addr[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920, 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930, 0x931, 0x932 }; u16 addr_ofdm[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902, 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c, 0x90d, 0x90e }; if (!is_ofdm) { for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_CCK; j++) { if (filt_type == LCNPHY_txdigfiltcoeffs_cck[j][0]) { filt_index = (s16) j; break; } } if (filt_index != -1) { for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, addr[j], LCNPHY_txdigfiltcoeffs_cck [filt_index][j + 1]); } } else { for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_OFDM; j++) { if (filt_type == LCNPHY_txdigfiltcoeffs_ofdm[j][0]) { filt_index = (s16) j; break; } } if (filt_index != -1) { for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) write_phy_reg(pi, addr_ofdm[j], LCNPHY_txdigfiltcoeffs_ofdm [filt_index][j + 1]); } } return (filt_index != -1) ? 0 : -1; } void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec) { u8 channel = CHSPEC_CHANNEL(chanspec); wlc_phy_chanspec_radio_set((struct brcms_phy_pub *) pi, chanspec); wlc_lcnphy_set_chanspec_tweaks(pi, pi->radio_chanspec); or_phy_reg(pi, 0x44a, 0x44); write_phy_reg(pi, 0x44a, 0x80); wlc_lcnphy_radio_2064_channel_tune_4313(pi, channel); udelay(1000); wlc_lcnphy_toggle_afe_pwdn(pi); write_phy_reg(pi, 0x657, lcnphy_sfo_cfg[channel - 1].ptcentreTs20); write_phy_reg(pi, 0x658, lcnphy_sfo_cfg[channel - 1].ptcentreFactor); if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8); wlc_lcnphy_load_tx_iir_filter(pi, false, 3); } else { mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8); wlc_lcnphy_load_tx_iir_filter(pi, false, 2); } wlc_lcnphy_load_tx_iir_filter(pi, true, 0); mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3); } static u16 wlc_lcnphy_get_pa_gain(struct brcms_phy *pi) { u16 pa_gain; pa_gain = (read_phy_reg(pi, 0x4fb) & LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK) >> LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT; return pa_gain; } static void wlc_lcnphy_set_tx_gain(struct brcms_phy *pi, struct lcnphy_txgains *target_gains) { u16 pa_gain = wlc_lcnphy_get_pa_gain(pi); mod_phy_reg( pi, 0x4b5, (0xffff << 0), ((target_gains->gm_gain) | (target_gains->pga_gain << 8)) << 0); mod_phy_reg(pi, 0x4fb, (0x7fff << 0), ((target_gains->pad_gain) | (pa_gain << 8)) << 0); mod_phy_reg( pi, 0x4fc, (0xffff << 0), ((target_gains->gm_gain) | (target_gains->pga_gain << 8)) << 0); mod_phy_reg(pi, 0x4fd, (0x7fff << 0), ((target_gains->pad_gain) | (pa_gain << 8)) << 0); wlc_lcnphy_set_dac_gain(pi, target_gains->dac_gain); wlc_lcnphy_enable_tx_gain_override(pi); } static void wlc_lcnphy_set_bbmult(struct brcms_phy *pi, u8 m0) { u16 m0m1 = (u16) m0 << 8; struct phytbl_info tab; tab.tbl_ptr = &m0m1; tab.tbl_len = 1; tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; tab.tbl_offset = 87; tab.tbl_width = 16; wlc_lcnphy_write_table(pi, &tab); } static void wlc_lcnphy_clear_tx_power_offsets(struct brcms_phy *pi) { u32 data_buf[64]; struct phytbl_info tab; memset(data_buf, 0, sizeof(data_buf)); tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_ptr = data_buf; if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { tab.tbl_len = 30; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; wlc_lcnphy_write_table(pi, &tab); } tab.tbl_len = 64; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_MAC_OFFSET; wlc_lcnphy_write_table(pi, &tab); } enum lcnphy_tssi_mode { LCNPHY_TSSI_PRE_PA, LCNPHY_TSSI_POST_PA, LCNPHY_TSSI_EXT }; static void wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos) { mod_phy_reg(pi, 0x4d7, (0x1 << 0), (0x1) << 0); mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1) << 6); if (LCNPHY_TSSI_POST_PA == pos) { mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0) << 2); mod_phy_reg(pi, 0x4d9, (0x1 << 3), (1) << 3); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); } else { mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1); mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); } } else { mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2); mod_phy_reg(pi, 0x4d9, (0x1 << 3), (0) << 3); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); } else { mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0); mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); } } mod_phy_reg(pi, 0x637, (0x3 << 14), (0) << 14); if (LCNPHY_TSSI_EXT == pos) { write_radio_reg(pi, RADIO_2064_REG07F, 1); mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 0x2); mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 0x1 << 7); mod_radio_reg(pi, RADIO_2064_REG028, 0x1f, 0x3); } } static u16 wlc_lcnphy_rfseq_tbl_adc_pwrup(struct brcms_phy *pi) { u16 N1, N2, N3, N4, N5, N6, N; N1 = ((read_phy_reg(pi, 0x4a5) & (0xff << 0)) >> 0); N2 = 1 << ((read_phy_reg(pi, 0x4a5) & (0x7 << 12)) >> 12); N3 = ((read_phy_reg(pi, 0x40d) & (0xff << 0)) >> 0); N4 = 1 << ((read_phy_reg(pi, 0x40d) & (0x7 << 8)) >> 8); N5 = ((read_phy_reg(pi, 0x4a2) & (0xff << 0)) >> 0); N6 = 1 << ((read_phy_reg(pi, 0x4a2) & (0x7 << 8)) >> 8); N = 2 * (N1 + N2 + N3 + N4 + 2 * (N5 + N6)) + 80; if (N < 1600) N = 1600; return N; } static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi) { u16 auxpga_vmid, auxpga_vmid_temp, auxpga_gain_temp; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; auxpga_vmid = (2 << 8) | (pi_lcn->lcnphy_rssi_vc << 4) | pi_lcn->lcnphy_rssi_vf; auxpga_vmid_temp = (2 << 8) | (8 << 4) | 4; auxpga_gain_temp = 2; mod_phy_reg(pi, 0x4d8, (0x1 << 0), (0) << 0); mod_phy_reg(pi, 0x4d8, (0x1 << 1), (0) << 1); mod_phy_reg(pi, 0x4d7, (0x1 << 3), (0) << 3); mod_phy_reg(pi, 0x4db, (0x3ff << 0) | (0x7 << 12), (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); mod_phy_reg(pi, 0x4dc, (0x3ff << 0) | (0x7 << 12), (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); mod_phy_reg(pi, 0x40a, (0x3ff << 0) | (0x7 << 12), (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); mod_phy_reg(pi, 0x40b, (0x3ff << 0) | (0x7 << 12), (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12)); mod_phy_reg(pi, 0x40c, (0x3ff << 0) | (0x7 << 12), (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12)); mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5)); } static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi) { struct phytbl_info tab; u32 rfseq, ind; tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_ptr = &ind; tab.tbl_len = 1; tab.tbl_offset = 0; for (ind = 0; ind < 128; ind++) { wlc_lcnphy_write_table(pi, &tab); tab.tbl_offset++; } tab.tbl_offset = 704; for (ind = 0; ind < 128; ind++) { wlc_lcnphy_write_table(pi, &tab); tab.tbl_offset++; } mod_phy_reg(pi, 0x503, (0x1 << 0), (0) << 0); mod_phy_reg(pi, 0x503, (0x1 << 2), (0) << 2); mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4); wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT); mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14); mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15); mod_phy_reg(pi, 0x4d0, (0x1 << 5), (0) << 5); mod_phy_reg(pi, 0x4a4, (0x1ff << 0), (0) << 0); mod_phy_reg(pi, 0x4a5, (0xff << 0), (255) << 0); mod_phy_reg(pi, 0x4a5, (0x7 << 12), (5) << 12); mod_phy_reg(pi, 0x4a5, (0x7 << 8), (0) << 8); mod_phy_reg(pi, 0x40d, (0xff << 0), (64) << 0); mod_phy_reg(pi, 0x40d, (0x7 << 8), (4) << 8); mod_phy_reg(pi, 0x4a2, (0xff << 0), (64) << 0); mod_phy_reg(pi, 0x4a2, (0x7 << 8), (4) << 8); mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (0) << 6); mod_phy_reg(pi, 0x4a8, (0xff << 0), (0x1) << 0); wlc_lcnphy_clear_tx_power_offsets(pi); mod_phy_reg(pi, 0x4a6, (0x1 << 15), (1) << 15); mod_phy_reg(pi, 0x4a6, (0x1ff << 0), (0xff) << 0); mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { mod_radio_reg(pi, RADIO_2064_REG028, 0xf, 0xe); mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); } else { mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1); mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3); } write_radio_reg(pi, RADIO_2064_REG025, 0xc); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1); } else { if (CHSPEC_IS2G(pi->radio_chanspec)) mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 1 << 1); else mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 0 << 1); } if (LCNREV_IS(pi->pubpi.phy_rev, 2)) mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 1 << 1); else mod_radio_reg(pi, RADIO_2064_REG03A, 0x4, 1 << 2); mod_radio_reg(pi, RADIO_2064_REG11A, 0x1, 1 << 0); mod_radio_reg(pi, RADIO_2064_REG005, 0x8, 1 << 3); if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) mod_phy_reg(pi, 0x4d7, (0x1 << 3) | (0x7 << 12), 0 << 3 | 2 << 12); rfseq = wlc_lcnphy_rfseq_tbl_adc_pwrup(pi); tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; tab.tbl_width = 16; tab.tbl_ptr = &rfseq; tab.tbl_len = 1; tab.tbl_offset = 6; wlc_lcnphy_write_table(pi, &tab); mod_phy_reg(pi, 0x938, (0x1 << 2), (1) << 2); mod_phy_reg(pi, 0x939, (0x1 << 2), (1) << 2); mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); mod_phy_reg(pi, 0x4d7, (0x1 << 2), (1) << 2); mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8); wlc_lcnphy_pwrctrl_rssiparams(pi); } void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi) { u16 tx_cnt, tx_total, npt; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; tx_total = wlc_lcnphy_total_tx_frames(pi); tx_cnt = tx_total - pi_lcn->lcnphy_tssi_tx_cnt; npt = wlc_lcnphy_get_tx_pwr_npt(pi); if (tx_cnt > (1 << npt)) { pi_lcn->lcnphy_tssi_tx_cnt = tx_total; pi_lcn->lcnphy_tssi_idx = wlc_lcnphy_get_current_tx_pwr_idx(pi); pi_lcn->lcnphy_tssi_npt = npt; } } s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1) { s32 a, b, p; a = 32768 + (a1 * tssi); b = (1024 * b0) + (64 * b1 * tssi); p = ((2 * b) + a) / (2 * a); return p; } static void wlc_lcnphy_txpower_reset_npt(struct brcms_phy *pi) { struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) return; pi_lcn->lcnphy_tssi_idx = LCNPHY_TX_PWR_CTRL_START_INDEX_2G_4313; pi_lcn->lcnphy_tssi_npt = LCNPHY_TX_PWR_CTRL_START_NPT; } void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi) { struct phytbl_info tab; u32 rate_table[BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM + BRCMS_NUM_RATES_MCS_1_STREAM]; uint i, j; if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) return; for (i = 0, j = 0; i < ARRAY_SIZE(rate_table); i++, j++) { if (i == BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM) j = TXP_FIRST_MCS_20_SISO; rate_table[i] = (u32) ((s32) (-pi->tx_power_offset[j])); } tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_len = ARRAY_SIZE(rate_table); tab.tbl_ptr = rate_table; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; wlc_lcnphy_write_table(pi, &tab); if (wlc_lcnphy_get_target_tx_pwr(pi) != pi->tx_power_min) { wlc_lcnphy_set_target_tx_pwr(pi, pi->tx_power_min); wlc_lcnphy_txpower_reset_npt(pi); } } static void wlc_lcnphy_set_tx_pwr_soft_ctrl(struct brcms_phy *pi, s8 index) { u32 cck_offset[4] = { 22, 22, 22, 22 }; u32 ofdm_offset, reg_offset_cck; int i; u16 index2; struct phytbl_info tab; if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) return; mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x1) << 14); mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x0) << 14); or_phy_reg(pi, 0x6da, 0x0040); reg_offset_cck = 0; for (i = 0; i < 4; i++) cck_offset[i] -= reg_offset_cck; tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_len = 4; tab.tbl_ptr = cck_offset; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; wlc_lcnphy_write_table(pi, &tab); ofdm_offset = 0; tab.tbl_len = 1; tab.tbl_ptr = &ofdm_offset; for (i = 836; i < 862; i++) { tab.tbl_offset = i; wlc_lcnphy_write_table(pi, &tab); } mod_phy_reg(pi, 0x4a4, (0x1 << 15), (0x1) << 15); mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x1) << 14); mod_phy_reg(pi, 0x4a4, (0x1 << 13), (0x1) << 13); mod_phy_reg(pi, 0x4b0, (0x1 << 7), (0) << 7); mod_phy_reg(pi, 0x43b, (0x1 << 6), (0) << 6); mod_phy_reg(pi, 0x4a9, (0x1 << 15), (1) << 15); index2 = (u16) (index * 2); mod_phy_reg(pi, 0x4a9, (0x1ff << 0), (index2) << 0); mod_phy_reg(pi, 0x6a3, (0x1 << 4), (0) << 4); } static s8 wlc_lcnphy_tempcompensated_txpwrctrl(struct brcms_phy *pi) { s8 index, delta_brd, delta_temp, new_index, tempcorrx; s16 manp, meas_temp, temp_diff; bool neg = false; u16 temp; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) return pi_lcn->lcnphy_current_index; index = FIXED_TXPWR; if (pi_lcn->lcnphy_tempsense_slope == 0) return index; temp = (u16) wlc_lcnphy_tempsense(pi, 0); meas_temp = LCNPHY_TEMPSENSE(temp); if (pi->tx_power_min != 0) delta_brd = (pi_lcn->lcnphy_measPower - pi->tx_power_min); else delta_brd = 0; manp = LCNPHY_TEMPSENSE(pi_lcn->lcnphy_rawtempsense); temp_diff = manp - meas_temp; if (temp_diff < 0) { neg = true; temp_diff = -temp_diff; } delta_temp = (s8) wlc_lcnphy_qdiv_roundup((u32) (temp_diff * 192), (u32) (pi_lcn-> lcnphy_tempsense_slope * 10), 0); if (neg) delta_temp = -delta_temp; if (pi_lcn->lcnphy_tempsense_option == 3 && LCNREV_IS(pi->pubpi.phy_rev, 0)) delta_temp = 0; if (pi_lcn->lcnphy_tempcorrx > 31) tempcorrx = (s8) (pi_lcn->lcnphy_tempcorrx - 64); else tempcorrx = (s8) pi_lcn->lcnphy_tempcorrx; if (LCNREV_IS(pi->pubpi.phy_rev, 1)) tempcorrx = 4; new_index = index + delta_brd + delta_temp - pi_lcn->lcnphy_bandedge_corr; new_index += tempcorrx; if (LCNREV_IS(pi->pubpi.phy_rev, 1)) index = 127; if (new_index < 0 || new_index > 126) return index; return new_index; } static u16 wlc_lcnphy_set_tx_pwr_ctrl_mode(struct brcms_phy *pi, u16 mode) { u16 current_mode = mode; if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) && mode == LCNPHY_TX_PWR_CTRL_HW) current_mode = LCNPHY_TX_PWR_CTRL_TEMPBASED; if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) && mode == LCNPHY_TX_PWR_CTRL_TEMPBASED) current_mode = LCNPHY_TX_PWR_CTRL_HW; return current_mode; } void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode) { u16 old_mode = wlc_lcnphy_get_tx_pwr_ctrl(pi); s8 index; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, mode); old_mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, old_mode); mod_phy_reg(pi, 0x6da, (0x1 << 6), ((LCNPHY_TX_PWR_CTRL_HW == mode) ? 1 : 0) << 6); mod_phy_reg(pi, 0x6a3, (0x1 << 4), ((LCNPHY_TX_PWR_CTRL_HW == mode) ? 0 : 1) << 4); if (old_mode != mode) { if (LCNPHY_TX_PWR_CTRL_HW == old_mode) { wlc_lcnphy_tx_pwr_update_npt(pi); wlc_lcnphy_clear_tx_power_offsets(pi); } if (LCNPHY_TX_PWR_CTRL_HW == mode) { wlc_lcnphy_txpower_recalc_target(pi); wlc_lcnphy_set_start_tx_pwr_idx(pi, pi_lcn-> lcnphy_tssi_idx); wlc_lcnphy_set_tx_pwr_npt(pi, pi_lcn->lcnphy_tssi_npt); mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 0); pi_lcn->lcnphy_tssi_tx_cnt = wlc_lcnphy_total_tx_frames(pi); wlc_lcnphy_disable_tx_gain_override(pi); pi_lcn->lcnphy_tx_power_idx_override = -1; } else wlc_lcnphy_enable_tx_gain_override(pi); mod_phy_reg(pi, 0x4a4, ((0x1 << 15) | (0x1 << 14) | (0x1 << 13)), mode); if (mode == LCNPHY_TX_PWR_CTRL_TEMPBASED) { index = wlc_lcnphy_tempcompensated_txpwrctrl(pi); wlc_lcnphy_set_tx_pwr_soft_ctrl(pi, index); pi_lcn->lcnphy_current_index = (s8) ((read_phy_reg(pi, 0x4a9) & 0xFF) / 2); } } } static void wlc_lcnphy_tx_iqlo_loopback(struct brcms_phy *pi, u16 *values_to_save) { u16 vmid; int i; for (i = 0; i < 20; i++) values_to_save[i] = read_radio_reg(pi, iqlo_loopback_rf_regs[i]); mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12); mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14); mod_phy_reg(pi, 0x44c, (0x1 << 11), 1 << 11); mod_phy_reg(pi, 0x44d, (0x1 << 13), 0 << 13); mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) and_radio_reg(pi, RADIO_2064_REG03A, 0xFD); else and_radio_reg(pi, RADIO_2064_REG03A, 0xF9); or_radio_reg(pi, RADIO_2064_REG11A, 0x1); or_radio_reg(pi, RADIO_2064_REG036, 0x01); or_radio_reg(pi, RADIO_2064_REG11A, 0x18); udelay(20); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { if (CHSPEC_IS5G(pi->radio_chanspec)) mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0); else or_radio_reg(pi, RADIO_2064_REG03A, 1); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) mod_radio_reg(pi, RADIO_2064_REG03A, 3, 1); else or_radio_reg(pi, RADIO_2064_REG03A, 0x3); } udelay(20); write_radio_reg(pi, RADIO_2064_REG025, 0xF); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { if (CHSPEC_IS5G(pi->radio_chanspec)) mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x4); else mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x6); } else { if (CHSPEC_IS5G(pi->radio_chanspec)) mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x4 << 1); else mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x6 << 1); } udelay(20); write_radio_reg(pi, RADIO_2064_REG005, 0x8); or_radio_reg(pi, RADIO_2064_REG112, 0x80); udelay(20); or_radio_reg(pi, RADIO_2064_REG0FF, 0x10); or_radio_reg(pi, RADIO_2064_REG11F, 0x44); udelay(20); or_radio_reg(pi, RADIO_2064_REG00B, 0x7); or_radio_reg(pi, RADIO_2064_REG113, 0x10); udelay(20); write_radio_reg(pi, RADIO_2064_REG007, 0x1); udelay(20); vmid = 0x2A6; mod_radio_reg(pi, RADIO_2064_REG0FC, 0x3 << 0, (vmid >> 8) & 0x3); write_radio_reg(pi, RADIO_2064_REG0FD, (vmid & 0xff)); or_radio_reg(pi, RADIO_2064_REG11F, 0x44); udelay(20); or_radio_reg(pi, RADIO_2064_REG0FF, 0x10); udelay(20); write_radio_reg(pi, RADIO_2064_REG012, 0x02); or_radio_reg(pi, RADIO_2064_REG112, 0x06); write_radio_reg(pi, RADIO_2064_REG036, 0x11); write_radio_reg(pi, RADIO_2064_REG059, 0xcc); write_radio_reg(pi, RADIO_2064_REG05C, 0x2e); write_radio_reg(pi, RADIO_2064_REG078, 0xd7); write_radio_reg(pi, RADIO_2064_REG092, 0x15); } static bool wlc_lcnphy_iqcal_wait(struct brcms_phy *pi) { uint delay_count = 0; while (wlc_lcnphy_iqcal_active(pi)) { udelay(100); delay_count++; if (delay_count > (10 * 500)) break; } return (0 == wlc_lcnphy_iqcal_active(pi)); } static void wlc_lcnphy_tx_iqlo_loopback_cleanup(struct brcms_phy *pi, u16 *values_to_save) { int i; and_phy_reg(pi, 0x44c, 0x0 >> 11); and_phy_reg(pi, 0x43b, 0xC); for (i = 0; i < 20; i++) write_radio_reg(pi, iqlo_loopback_rf_regs[i], values_to_save[i]); } static void wlc_lcnphy_tx_iqlo_cal(struct brcms_phy *pi, struct lcnphy_txgains *target_gains, enum lcnphy_cal_mode cal_mode, bool keep_tone) { struct lcnphy_txgains cal_gains, temp_gains; u16 hash; u8 band_idx; int j; u16 ncorr_override[5]; u16 syst_coeffs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; u16 commands_fullcal[] = { 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 }; u16 commands_recal[] = { 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 }; u16 command_nums_fullcal[] = { 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 }; u16 command_nums_recal[] = { 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 }; u16 *command_nums = command_nums_fullcal; u16 *start_coeffs = NULL, *cal_cmds = NULL, cal_type, diq_start; u16 tx_pwr_ctrl_old, save_txpwrctrlrfctrl2; u16 save_sslpnCalibClkEnCtrl, save_sslpnRxFeClkEnCtrl; bool tx_gain_override_old; struct lcnphy_txgains old_gains; uint i, n_cal_cmds = 0, n_cal_start = 0; u16 *values_to_save; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; values_to_save = kmalloc(sizeof(u16) * 20, GFP_ATOMIC); if (NULL == values_to_save) return; save_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); save_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); or_phy_reg(pi, 0x6da, 0x40); or_phy_reg(pi, 0x6db, 0x3); switch (cal_mode) { case LCNPHY_CAL_FULL: start_coeffs = syst_coeffs; cal_cmds = commands_fullcal; n_cal_cmds = ARRAY_SIZE(commands_fullcal); break; case LCNPHY_CAL_RECAL: start_coeffs = syst_coeffs; cal_cmds = commands_recal; n_cal_cmds = ARRAY_SIZE(commands_recal); command_nums = command_nums_recal; break; default: break; } wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, start_coeffs, 11, 16, 64); write_phy_reg(pi, 0x6da, 0xffff); mod_phy_reg(pi, 0x503, (0x1 << 3), (1) << 3); tx_pwr_ctrl_old = wlc_lcnphy_get_tx_pwr_ctrl(pi); mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); save_txpwrctrlrfctrl2 = read_phy_reg(pi, 0x4db); mod_phy_reg(pi, 0x4db, (0x3ff << 0), (0x2a6) << 0); mod_phy_reg(pi, 0x4db, (0x7 << 12), (2) << 12); wlc_lcnphy_tx_iqlo_loopback(pi, values_to_save); tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); if (tx_gain_override_old) wlc_lcnphy_get_tx_gain(pi, &old_gains); if (!target_gains) { if (!tx_gain_override_old) wlc_lcnphy_set_tx_pwr_by_index(pi, pi_lcn->lcnphy_tssi_idx); wlc_lcnphy_get_tx_gain(pi, &temp_gains); target_gains = &temp_gains; } hash = (target_gains->gm_gain << 8) | (target_gains->pga_gain << 4) | (target_gains->pad_gain); band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0); cal_gains = *target_gains; memset(ncorr_override, 0, sizeof(ncorr_override)); for (j = 0; j < iqcal_gainparams_numgains_lcnphy[band_idx]; j++) { if (hash == tbl_iqcal_gainparams_lcnphy[band_idx][j][0]) { cal_gains.gm_gain = tbl_iqcal_gainparams_lcnphy[band_idx][j][1]; cal_gains.pga_gain = tbl_iqcal_gainparams_lcnphy[band_idx][j][2]; cal_gains.pad_gain = tbl_iqcal_gainparams_lcnphy[band_idx][j][3]; memcpy(ncorr_override, &tbl_iqcal_gainparams_lcnphy[band_idx][j][3], sizeof(ncorr_override)); break; } } wlc_lcnphy_set_tx_gain(pi, &cal_gains); write_phy_reg(pi, 0x453, 0xaa9); write_phy_reg(pi, 0x93d, 0xc0); wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, lcnphy_iqcal_loft_gainladder, ARRAY_SIZE(lcnphy_iqcal_loft_gainladder), 16, 0); wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, lcnphy_iqcal_ir_gainladder, ARRAY_SIZE( lcnphy_iqcal_ir_gainladder), 16, 32); if (pi->phy_tx_tone_freq) { wlc_lcnphy_stop_tx_tone(pi); udelay(5); wlc_lcnphy_start_tx_tone(pi, 3750, 88, 1); } else { wlc_lcnphy_start_tx_tone(pi, 3750, 88, 1); } write_phy_reg(pi, 0x6da, 0xffff); for (i = n_cal_start; i < n_cal_cmds; i++) { u16 zero_diq = 0; u16 best_coeffs[11]; u16 command_num; cal_type = (cal_cmds[i] & 0x0f00) >> 8; command_num = command_nums[i]; if (ncorr_override[cal_type]) command_num = ncorr_override[cal_type] << 8 | (command_num & 0xff); write_phy_reg(pi, 0x452, command_num); if ((cal_type == 3) || (cal_type == 4)) { wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, &diq_start, 1, 16, 69); wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, &zero_diq, 1, 16, 69); } write_phy_reg(pi, 0x451, cal_cmds[i]); if (!wlc_lcnphy_iqcal_wait(pi)) goto cleanup; wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, best_coeffs, ARRAY_SIZE(best_coeffs), 16, 96); wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, best_coeffs, ARRAY_SIZE(best_coeffs), 16, 64); if ((cal_type == 3) || (cal_type == 4)) wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, &diq_start, 1, 16, 69); wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, pi_lcn->lcnphy_cal_results. txiqlocal_bestcoeffs, ARRAY_SIZE(pi_lcn-> lcnphy_cal_results. txiqlocal_bestcoeffs), 16, 96); } wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, pi_lcn->lcnphy_cal_results. txiqlocal_bestcoeffs, ARRAY_SIZE(pi_lcn->lcnphy_cal_results. txiqlocal_bestcoeffs), 16, 96); pi_lcn->lcnphy_cal_results.txiqlocal_bestcoeffs_valid = true; wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, &pi_lcn->lcnphy_cal_results. txiqlocal_bestcoeffs[0], 4, 16, 80); wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, &pi_lcn->lcnphy_cal_results. txiqlocal_bestcoeffs[5], 2, 16, 85); cleanup: wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, values_to_save); kfree(values_to_save); if (!keep_tone) wlc_lcnphy_stop_tx_tone(pi); write_phy_reg(pi, 0x4db, save_txpwrctrlrfctrl2); write_phy_reg(pi, 0x453, 0); if (tx_gain_override_old) wlc_lcnphy_set_tx_gain(pi, &old_gains); wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl_old); write_phy_reg(pi, 0x6da, save_sslpnCalibClkEnCtrl); write_phy_reg(pi, 0x6db, save_sslpnRxFeClkEnCtrl); } static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi) { bool suspend, tx_gain_override_old; struct lcnphy_txgains old_gains; struct brcms_phy *pi = (struct brcms_phy *) ppi; u16 idleTssi, idleTssi0_2C, idleTssi0_OB, idleTssi0_regvalue_OB, idleTssi0_regvalue_2C; u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); u16 SAVE_lpfgain = read_radio_reg(pi, RADIO_2064_REG112); u16 SAVE_jtag_bb_afe_switch = read_radio_reg(pi, RADIO_2064_REG007) & 1; u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10; u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4; idleTssi = read_phy_reg(pi, 0x4ab); suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); wlc_lcnphy_get_tx_gain(pi, &old_gains); wlc_lcnphy_enable_tx_gain_override(pi); wlc_lcnphy_set_tx_pwr_by_index(pi, 127); write_radio_reg(pi, RADIO_2064_REG112, 0x6); mod_radio_reg(pi, RADIO_2064_REG007, 0x1, 1); mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4); mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2); wlc_lcnphy_tssi_setup(pi); wlc_phy_do_dummy_tx(pi, true, OFF); idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0)) >> 0); idleTssi0_2C = ((read_phy_reg(pi, 0x63e) & (0x1ff << 0)) >> 0); if (idleTssi0_2C >= 256) idleTssi0_OB = idleTssi0_2C - 256; else idleTssi0_OB = idleTssi0_2C + 256; idleTssi0_regvalue_OB = idleTssi0_OB; if (idleTssi0_regvalue_OB >= 256) idleTssi0_regvalue_2C = idleTssi0_regvalue_OB - 256; else idleTssi0_regvalue_2C = idleTssi0_regvalue_OB + 256; mod_phy_reg(pi, 0x4a6, (0x1ff << 0), (idleTssi0_regvalue_2C) << 0); mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12); wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old); wlc_lcnphy_set_tx_gain(pi, &old_gains); wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); write_radio_reg(pi, RADIO_2064_REG112, SAVE_lpfgain); mod_radio_reg(pi, RADIO_2064_REG007, 0x1, SAVE_jtag_bb_afe_switch); mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, SAVE_jtag_auxpga); mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, SAVE_iqadc_aux_en); mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1 << 7); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } static void wlc_lcnphy_vbat_temp_sense_setup(struct brcms_phy *pi, u8 mode) { bool suspend; u16 save_txpwrCtrlEn; u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain; u16 auxpga_vmid; struct phytbl_info tab; u32 val; u8 save_reg007, save_reg0FF, save_reg11F, save_reg005, save_reg025, save_reg112; u16 values_to_save[14]; s8 index; int i; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; udelay(999); save_reg007 = (u8) read_radio_reg(pi, RADIO_2064_REG007); save_reg0FF = (u8) read_radio_reg(pi, RADIO_2064_REG0FF); save_reg11F = (u8) read_radio_reg(pi, RADIO_2064_REG11F); save_reg005 = (u8) read_radio_reg(pi, RADIO_2064_REG005); save_reg025 = (u8) read_radio_reg(pi, RADIO_2064_REG025); save_reg112 = (u8) read_radio_reg(pi, RADIO_2064_REG112); for (i = 0; i < 14; i++) values_to_save[i] = read_phy_reg(pi, tempsense_phy_regs[i]); suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); save_txpwrCtrlEn = read_radio_reg(pi, 0x4a4); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); index = pi_lcn->lcnphy_current_index; wlc_lcnphy_set_tx_pwr_by_index(pi, 127); mod_radio_reg(pi, RADIO_2064_REG007, 0x1, 0x1); mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 0x1 << 4); mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 0x1 << 2); mod_phy_reg(pi, 0x503, (0x1 << 0), (0) << 0); mod_phy_reg(pi, 0x503, (0x1 << 2), (0) << 2); mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14); mod_phy_reg(pi, 0x4a4, (0x1 << 15), (0) << 15); mod_phy_reg(pi, 0x4d0, (0x1 << 5), (0) << 5); mod_phy_reg(pi, 0x4a5, (0xff << 0), (255) << 0); mod_phy_reg(pi, 0x4a5, (0x7 << 12), (5) << 12); mod_phy_reg(pi, 0x4a5, (0x7 << 8), (0) << 8); mod_phy_reg(pi, 0x40d, (0xff << 0), (64) << 0); mod_phy_reg(pi, 0x40d, (0x7 << 8), (6) << 8); mod_phy_reg(pi, 0x4a2, (0xff << 0), (64) << 0); mod_phy_reg(pi, 0x4a2, (0x7 << 8), (6) << 8); mod_phy_reg(pi, 0x4d9, (0x7 << 4), (2) << 4); mod_phy_reg(pi, 0x4d9, (0x7 << 8), (3) << 8); mod_phy_reg(pi, 0x4d9, (0x7 << 12), (1) << 12); mod_phy_reg(pi, 0x4da, (0x1 << 12), (0) << 12); mod_phy_reg(pi, 0x4da, (0x1 << 13), (1) << 13); mod_phy_reg(pi, 0x4a6, (0x1 << 15), (1) << 15); write_radio_reg(pi, RADIO_2064_REG025, 0xC); mod_radio_reg(pi, RADIO_2064_REG005, 0x8, 0x1 << 3); mod_phy_reg(pi, 0x938, (0x1 << 2), (1) << 2); mod_phy_reg(pi, 0x939, (0x1 << 2), (1) << 2); mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); val = wlc_lcnphy_rfseq_tbl_adc_pwrup(pi); tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; tab.tbl_width = 16; tab.tbl_len = 1; tab.tbl_ptr = &val; tab.tbl_offset = 6; wlc_lcnphy_write_table(pi, &tab); if (mode == TEMPSENSE) { mod_phy_reg(pi, 0x4d7, (0x1 << 3), (1) << 3); mod_phy_reg(pi, 0x4d7, (0x7 << 12), (1) << 12); auxpga_vmidcourse = 8; auxpga_vmidfine = 0x4; auxpga_gain = 2; mod_radio_reg(pi, RADIO_2064_REG082, 0x20, 1 << 5); } else { mod_phy_reg(pi, 0x4d7, (0x1 << 3), (1) << 3); mod_phy_reg(pi, 0x4d7, (0x7 << 12), (3) << 12); auxpga_vmidcourse = 7; auxpga_vmidfine = 0xa; auxpga_gain = 2; } auxpga_vmid = (u16) ((2 << 8) | (auxpga_vmidcourse << 4) | auxpga_vmidfine); mod_phy_reg(pi, 0x4d8, (0x1 << 0), (1) << 0); mod_phy_reg(pi, 0x4d8, (0x3ff << 2), (auxpga_vmid) << 2); mod_phy_reg(pi, 0x4d8, (0x1 << 1), (1) << 1); mod_phy_reg(pi, 0x4d8, (0x7 << 12), (auxpga_gain) << 12); mod_phy_reg(pi, 0x4d0, (0x1 << 5), (1) << 5); write_radio_reg(pi, RADIO_2064_REG112, 0x6); wlc_phy_do_dummy_tx(pi, true, OFF); if (!tempsense_done(pi)) udelay(10); write_radio_reg(pi, RADIO_2064_REG007, (u16) save_reg007); write_radio_reg(pi, RADIO_2064_REG0FF, (u16) save_reg0FF); write_radio_reg(pi, RADIO_2064_REG11F, (u16) save_reg11F); write_radio_reg(pi, RADIO_2064_REG005, (u16) save_reg005); write_radio_reg(pi, RADIO_2064_REG025, (u16) save_reg025); write_radio_reg(pi, RADIO_2064_REG112, (u16) save_reg112); for (i = 0; i < 14; i++) write_phy_reg(pi, tempsense_phy_regs[i], values_to_save[i]); wlc_lcnphy_set_tx_pwr_by_index(pi, (int)index); write_radio_reg(pi, 0x4a4, save_txpwrCtrlEn); if (!suspend) wlapi_enable_mac(pi->sh->physhim); udelay(999); } static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi) { struct lcnphy_txgains tx_gains; u8 bbmult; struct phytbl_info tab; s32 a1, b0, b1; s32 tssi, pwr, maxtargetpwr, mintargetpwr; bool suspend; struct brcms_phy *pi = (struct brcms_phy *) ppi; suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); if (!pi->hwpwrctrl_capable) { if (CHSPEC_IS2G(pi->radio_chanspec)) { tx_gains.gm_gain = 4; tx_gains.pga_gain = 12; tx_gains.pad_gain = 12; tx_gains.dac_gain = 0; bbmult = 150; } else { tx_gains.gm_gain = 7; tx_gains.pga_gain = 15; tx_gains.pad_gain = 14; tx_gains.dac_gain = 0; bbmult = 150; } wlc_lcnphy_set_tx_gain(pi, &tx_gains); wlc_lcnphy_set_bbmult(pi, bbmult); wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); } else { wlc_lcnphy_idle_tssi_est(ppi); wlc_lcnphy_clear_tx_power_offsets(pi); b0 = pi->txpa_2g[0]; b1 = pi->txpa_2g[1]; a1 = pi->txpa_2g[2]; maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1); mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1); tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_ptr = &pwr; tab.tbl_len = 1; tab.tbl_offset = 0; for (tssi = 0; tssi < 128; tssi++) { pwr = wlc_lcnphy_tssi2dbm(tssi, a1, b0, b1); pwr = (pwr < mintargetpwr) ? mintargetpwr : pwr; wlc_lcnphy_write_table(pi, &tab); tab.tbl_offset++; } mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7); write_phy_reg(pi, 0x4a8, 10); wlc_lcnphy_set_target_tx_pwr(pi, LCN_TARGET_PWR); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW); } if (!suspend) wlapi_enable_mac(pi->sh->physhim); } static u8 wlc_lcnphy_get_bbmult(struct brcms_phy *pi) { u16 m0m1; struct phytbl_info tab; tab.tbl_ptr = &m0m1; tab.tbl_len = 1; tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; tab.tbl_offset = 87; tab.tbl_width = 16; wlc_lcnphy_read_table(pi, &tab); return (u8) ((m0m1 & 0xff00) >> 8); } static void wlc_lcnphy_set_pa_gain(struct brcms_phy *pi, u16 gain) { mod_phy_reg(pi, 0x4fb, LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK, gain << LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT); mod_phy_reg(pi, 0x4fd, LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_MASK, gain << LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT); } void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, u8 *eq0, u8 *fi0, u8 *fq0) { *ei0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG089)); *eq0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08A)); *fi0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08B)); *fq0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08C)); } void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b) { struct phytbl_info tab; u16 iqcc[2]; iqcc[0] = a; iqcc[1] = b; tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; tab.tbl_width = 16; tab.tbl_ptr = iqcc; tab.tbl_len = 2; tab.tbl_offset = 80; wlc_lcnphy_write_table(pi, &tab); } void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq) { struct phytbl_info tab; tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; tab.tbl_width = 16; tab.tbl_ptr = &didq; tab.tbl_len = 1; tab.tbl_offset = 85; wlc_lcnphy_write_table(pi, &tab); } void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index) { struct phytbl_info tab; u16 a, b; u8 bb_mult; u32 bbmultiqcomp, txgain, locoeffs, rfpower; struct lcnphy_txgains gains; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; pi_lcn->lcnphy_tx_power_idx_override = (s8) index; pi_lcn->lcnphy_current_index = (u8) index; tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_len = 1; wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index; tab.tbl_ptr = &bbmultiqcomp; wlc_lcnphy_read_table(pi, &tab); tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index; tab.tbl_width = 32; tab.tbl_ptr = &txgain; wlc_lcnphy_read_table(pi, &tab); gains.gm_gain = (u16) (txgain & 0xff); gains.pga_gain = (u16) (txgain >> 8) & 0xff; gains.pad_gain = (u16) (txgain >> 16) & 0xff; gains.dac_gain = (u16) (bbmultiqcomp >> 28) & 0x07; wlc_lcnphy_set_tx_gain(pi, &gains); wlc_lcnphy_set_pa_gain(pi, (u16) (txgain >> 24) & 0x7f); bb_mult = (u8) ((bbmultiqcomp >> 20) & 0xff); wlc_lcnphy_set_bbmult(pi, bb_mult); wlc_lcnphy_enable_tx_gain_override(pi); if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { a = (u16) ((bbmultiqcomp >> 10) & 0x3ff); b = (u16) (bbmultiqcomp & 0x3ff); wlc_lcnphy_set_tx_iqcc(pi, a, b); tab.tbl_offset = LCNPHY_TX_PWR_CTRL_LO_OFFSET + index; tab.tbl_ptr = &locoeffs; wlc_lcnphy_read_table(pi, &tab); wlc_lcnphy_set_tx_locc(pi, (u16) locoeffs); tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index; tab.tbl_ptr = &rfpower; wlc_lcnphy_read_table(pi, &tab); mod_phy_reg(pi, 0x6a6, (0x1fff << 0), (rfpower * 8) << 0); } } static void wlc_lcnphy_clear_papd_comptable(struct brcms_phy *pi) { u32 j; struct phytbl_info tab; u32 temp_offset[128]; tab.tbl_ptr = temp_offset; tab.tbl_len = 128; tab.tbl_id = LCNPHY_TBL_ID_PAPDCOMPDELTATBL; tab.tbl_width = 32; tab.tbl_offset = 0; memset(temp_offset, 0, sizeof(temp_offset)); for (j = 1; j < 128; j += 2) temp_offset[j] = 0x80000; wlc_lcnphy_write_table(pi, &tab); return; } void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable) { if (!bEnable) { and_phy_reg(pi, 0x43b, ~(u16) ((0x1 << 1) | (0x1 << 4))); mod_phy_reg(pi, 0x43c, (0x1 << 1), 1 << 1); and_phy_reg(pi, 0x44c, ~(u16) ((0x1 << 3) | (0x1 << 5) | (0x1 << 12) | (0x1 << 0) | (0x1 << 1) | (0x1 << 2))); and_phy_reg(pi, 0x44d, ~(u16) ((0x1 << 3) | (0x1 << 5) | (0x1 << 14))); mod_phy_reg(pi, 0x44d, (0x1 << 2), 1 << 2); mod_phy_reg(pi, 0x44d, (0x1 << 1) | (0x1 << 0), (0x1 << 0)); and_phy_reg(pi, 0x4f9, ~(u16) ((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); and_phy_reg(pi, 0x4fa, ~(u16) ((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); } else { mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); mod_phy_reg(pi, 0x43b, (0x1 << 4), 1 << 4); mod_phy_reg(pi, 0x43c, (0x1 << 6), 0 << 6); mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12); mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14); wlc_lcnphy_set_trsw_override(pi, true, false); mod_phy_reg(pi, 0x44d, (0x1 << 2), 0 << 2); mod_phy_reg(pi, 0x44c, (0x1 << 2), 1 << 2); if (CHSPEC_IS2G(pi->radio_chanspec)) { mod_phy_reg(pi, 0x44c, (0x1 << 3), 1 << 3); mod_phy_reg(pi, 0x44d, (0x1 << 3), 1 << 3); mod_phy_reg(pi, 0x44c, (0x1 << 5), 1 << 5); mod_phy_reg(pi, 0x44d, (0x1 << 5), 0 << 5); mod_phy_reg(pi, 0x4f9, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x4fa, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x4f9, (0x1 << 2), 1 << 2); mod_phy_reg(pi, 0x4fa, (0x1 << 2), 1 << 2); mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x4fa, (0x1 << 0), 1 << 0); } else { mod_phy_reg(pi, 0x44c, (0x1 << 3), 1 << 3); mod_phy_reg(pi, 0x44d, (0x1 << 3), 0 << 3); mod_phy_reg(pi, 0x44c, (0x1 << 5), 1 << 5); mod_phy_reg(pi, 0x44d, (0x1 << 5), 1 << 5); mod_phy_reg(pi, 0x4f9, (0x1 << 1), 1 << 1); mod_phy_reg(pi, 0x4fa, (0x1 << 1), 0 << 1); mod_phy_reg(pi, 0x4f9, (0x1 << 2), 1 << 2); mod_phy_reg(pi, 0x4fa, (0x1 << 2), 0 << 2); mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); } } } static void wlc_lcnphy_run_samples(struct brcms_phy *pi, u16 num_samps, u16 num_loops, u16 wait, bool iqcalmode) { or_phy_reg(pi, 0x6da, 0x8080); mod_phy_reg(pi, 0x642, (0x7f << 0), (num_samps - 1) << 0); if (num_loops != 0xffff) num_loops--; mod_phy_reg(pi, 0x640, (0xffff << 0), num_loops << 0); mod_phy_reg(pi, 0x641, (0xffff << 0), wait << 0); if (iqcalmode) { and_phy_reg(pi, 0x453, (u16) ~(0x1 << 15)); or_phy_reg(pi, 0x453, (0x1 << 15)); } else { write_phy_reg(pi, 0x63f, 1); wlc_lcnphy_tx_pu(pi, 1); } or_radio_reg(pi, RADIO_2064_REG112, 0x6); } void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode) { u8 phybw40; phybw40 = CHSPEC_IS40(pi->radio_chanspec); if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5); mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9); } else { mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5); mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9); } if (phybw40 == 0) { mod_phy_reg((pi), 0x410, (0x1 << 6) | (0x1 << 5), ((CHSPEC_IS2G( pi->radio_chanspec)) ? (!mode) : 0) << 6 | (!mode) << 5); mod_phy_reg(pi, 0x410, (0x1 << 7), (mode) << 7); } } void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, bool iqcalmode) { u8 phy_bw; u16 num_samps, t, k; u32 bw; s32 theta = 0, rot = 0; struct cordic_iq tone_samp; u32 data_buf[64]; u16 i_samp, q_samp; struct phytbl_info tab; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; pi->phy_tx_tone_freq = f_kHz; wlc_lcnphy_deaf_mode(pi, true); phy_bw = 40; if (pi_lcn->lcnphy_spurmod) { write_phy_reg(pi, 0x942, 0x2); write_phy_reg(pi, 0x93b, 0x0); write_phy_reg(pi, 0x93c, 0x0); wlc_lcnphy_txrx_spur_avoidance_mode(pi, false); } if (f_kHz) { k = 1; do { bw = phy_bw * 1000 * k; num_samps = bw / abs(f_kHz); k++; } while ((num_samps * (u32) (abs(f_kHz))) != bw); } else num_samps = 2; rot = ((f_kHz * 36) / phy_bw) / 100; theta = 0; for (t = 0; t < num_samps; t++) { tone_samp = cordic_calc_iq(theta); theta += rot; i_samp = (u16) (FLOAT(tone_samp.i * max_val) & 0x3ff); q_samp = (u16) (FLOAT(tone_samp.q * max_val) & 0x3ff); data_buf[t] = (i_samp << 10) | q_samp; } mod_phy_reg(pi, 0x6d6, (0x3 << 0), 0 << 0); mod_phy_reg(pi, 0x6da, (0x1 << 3), 1 << 3); tab.tbl_ptr = data_buf; tab.tbl_len = num_samps; tab.tbl_id = LCNPHY_TBL_ID_SAMPLEPLAY; tab.tbl_offset = 0; tab.tbl_width = 32; wlc_lcnphy_write_table(pi, &tab); wlc_lcnphy_run_samples(pi, num_samps, 0xffff, 0, iqcalmode); } void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi) { s16 playback_status; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; pi->phy_tx_tone_freq = 0; if (pi_lcn->lcnphy_spurmod) { write_phy_reg(pi, 0x942, 0x7); write_phy_reg(pi, 0x93b, 0x2017); write_phy_reg(pi, 0x93c, 0x27c5); wlc_lcnphy_txrx_spur_avoidance_mode(pi, true); } playback_status = read_phy_reg(pi, 0x644); if (playback_status & (0x1 << 0)) { wlc_lcnphy_tx_pu(pi, 0); mod_phy_reg(pi, 0x63f, (0x1 << 1), 1 << 1); } else if (playback_status & (0x1 << 1)) mod_phy_reg(pi, 0x453, (0x1 << 15), 0 << 15); mod_phy_reg(pi, 0x6d6, (0x3 << 0), 1 << 0); mod_phy_reg(pi, 0x6da, (0x1 << 3), 0 << 3); mod_phy_reg(pi, 0x6da, (0x1 << 7), 0 << 7); and_radio_reg(pi, RADIO_2064_REG112, 0xFFF9); wlc_lcnphy_deaf_mode(pi, false); } static void wlc_lcnphy_set_cc(struct brcms_phy *pi, int cal_type, s16 coeff_x, s16 coeff_y) { u16 di0dq0; u16 x, y, data_rf; int k; switch (cal_type) { case 0: wlc_lcnphy_set_tx_iqcc(pi, coeff_x, coeff_y); break; case 2: di0dq0 = (coeff_x & 0xff) << 8 | (coeff_y & 0xff); wlc_lcnphy_set_tx_locc(pi, di0dq0); break; case 3: k = wlc_lcnphy_calc_floor(coeff_x, 0); y = 8 + k; k = wlc_lcnphy_calc_floor(coeff_x, 1); x = 8 - k; data_rf = (x * 16 + y); write_radio_reg(pi, RADIO_2064_REG089, data_rf); k = wlc_lcnphy_calc_floor(coeff_y, 0); y = 8 + k; k = wlc_lcnphy_calc_floor(coeff_y, 1); x = 8 - k; data_rf = (x * 16 + y); write_radio_reg(pi, RADIO_2064_REG08A, data_rf); break; case 4: k = wlc_lcnphy_calc_floor(coeff_x, 0); y = 8 + k; k = wlc_lcnphy_calc_floor(coeff_x, 1); x = 8 - k; data_rf = (x * 16 + y); write_radio_reg(pi, RADIO_2064_REG08B, data_rf); k = wlc_lcnphy_calc_floor(coeff_y, 0); y = 8 + k; k = wlc_lcnphy_calc_floor(coeff_y, 1); x = 8 - k; data_rf = (x * 16 + y); write_radio_reg(pi, RADIO_2064_REG08C, data_rf); break; } } static struct lcnphy_unsign16_struct wlc_lcnphy_get_cc(struct brcms_phy *pi, int cal_type) { u16 a, b, didq; u8 di0, dq0, ei, eq, fi, fq; struct lcnphy_unsign16_struct cc; cc.re = 0; cc.im = 0; switch (cal_type) { case 0: wlc_lcnphy_get_tx_iqcc(pi, &a, &b); cc.re = a; cc.im = b; break; case 2: didq = wlc_lcnphy_get_tx_locc(pi); di0 = (((didq & 0xff00) << 16) >> 24); dq0 = (((didq & 0x00ff) << 24) >> 24); cc.re = (u16) di0; cc.im = (u16) dq0; break; case 3: wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq); cc.re = (u16) ei; cc.im = (u16) eq; break; case 4: wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq); cc.re = (u16) fi; cc.im = (u16) fq; break; } return cc; } static void wlc_lcnphy_samp_cap(struct brcms_phy *pi, int clip_detect_algo, u16 thresh, s16 *ptr, int mode) { u32 curval1, curval2, stpptr, curptr, strptr, val; u16 sslpnCalibClkEnCtrl, timer; u16 old_sslpnCalibClkEnCtrl; s16 imag, real; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; timer = 0; old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); curval1 = bcma_read16(pi->d11core, D11REGOFFS(psm_corectlsts)); ptr[130] = 0; bcma_write16(pi->d11core, D11REGOFFS(psm_corectlsts), ((1 << 6) | curval1)); bcma_write16(pi->d11core, D11REGOFFS(smpl_clct_strptr), 0x7E00); bcma_write16(pi->d11core, D11REGOFFS(smpl_clct_stpptr), 0x8000); udelay(20); curval2 = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), curval2 | 0x30); write_phy_reg(pi, 0x555, 0x0); write_phy_reg(pi, 0x5a6, 0x5); write_phy_reg(pi, 0x5a2, (u16) (mode | mode << 6)); write_phy_reg(pi, 0x5cf, 3); write_phy_reg(pi, 0x5a5, 0x3); write_phy_reg(pi, 0x583, 0x0); write_phy_reg(pi, 0x584, 0x0); write_phy_reg(pi, 0x585, 0x0fff); write_phy_reg(pi, 0x586, 0x0000); write_phy_reg(pi, 0x580, 0x4501); sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); write_phy_reg(pi, 0x6da, (u32) (sslpnCalibClkEnCtrl | 0x2008)); stpptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_stpptr)); curptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_curptr)); do { udelay(10); curptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_curptr)); timer++; } while ((curptr != stpptr) && (timer < 500)); bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), 0x2); strptr = 0x7E00; bcma_write32(pi->d11core, D11REGOFFS(tplatewrptr), strptr); while (strptr < 0x8000) { val = bcma_read32(pi->d11core, D11REGOFFS(tplatewrdata)); imag = ((val >> 16) & 0x3ff); real = ((val) & 0x3ff); if (imag > 511) imag -= 1024; if (real > 511) real -= 1024; if (pi_lcn->lcnphy_iqcal_swp_dis) ptr[(strptr - 0x7E00) / 4] = real; else ptr[(strptr - 0x7E00) / 4] = imag; if (clip_detect_algo) { if (imag > thresh || imag < -thresh) { strptr = 0x8000; ptr[130] = 1; } } strptr += 4; } write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), curval2); bcma_write16(pi->d11core, D11REGOFFS(psm_corectlsts), curval1); } static void wlc_lcnphy_a1(struct brcms_phy *pi, int cal_type, int num_levels, int step_size_lg2) { const struct lcnphy_spb_tone *phy_c1; struct lcnphy_spb_tone phy_c2; struct lcnphy_unsign16_struct phy_c3; int phy_c4, phy_c5, k, l, j, phy_c6; u16 phy_c7, phy_c8, phy_c9; s16 phy_c10, phy_c11, phy_c12, phy_c13, phy_c14, phy_c15, phy_c16; s16 *ptr, phy_c17; s32 phy_c18, phy_c19; u32 phy_c20, phy_c21; bool phy_c22, phy_c23, phy_c24, phy_c25; u16 phy_c26, phy_c27; u16 phy_c28, phy_c29, phy_c30; u16 phy_c31; u16 *phy_c32; phy_c21 = 0; phy_c10 = phy_c13 = phy_c14 = phy_c8 = 0; ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC); if (NULL == ptr) return; phy_c32 = kmalloc(sizeof(u16) * 20, GFP_ATOMIC); if (NULL == phy_c32) { kfree(ptr); return; } phy_c26 = read_phy_reg(pi, 0x6da); phy_c27 = read_phy_reg(pi, 0x6db); phy_c31 = read_radio_reg(pi, RADIO_2064_REG026); write_phy_reg(pi, 0x93d, 0xC0); wlc_lcnphy_start_tx_tone(pi, 3750, 88, 0); write_phy_reg(pi, 0x6da, 0xffff); or_phy_reg(pi, 0x6db, 0x3); wlc_lcnphy_tx_iqlo_loopback(pi, phy_c32); udelay(500); phy_c28 = read_phy_reg(pi, 0x938); phy_c29 = read_phy_reg(pi, 0x4d7); phy_c30 = read_phy_reg(pi, 0x4d8); or_phy_reg(pi, 0x938, 0x1 << 2); or_phy_reg(pi, 0x4d7, 0x1 << 2); or_phy_reg(pi, 0x4d7, 0x1 << 3); mod_phy_reg(pi, 0x4d7, (0x7 << 12), 0x2 << 12); or_phy_reg(pi, 0x4d8, 1 << 0); or_phy_reg(pi, 0x4d8, 1 << 1); mod_phy_reg(pi, 0x4d8, (0x3ff << 2), 0x23A << 2); mod_phy_reg(pi, 0x4d8, (0x7 << 12), 0x7 << 12); phy_c1 = &lcnphy_spb_tone_3750[0]; phy_c4 = 32; if (num_levels == 0) { if (cal_type != 0) num_levels = 4; else num_levels = 9; } if (step_size_lg2 == 0) { if (cal_type != 0) step_size_lg2 = 3; else step_size_lg2 = 8; } phy_c7 = (1 << step_size_lg2); phy_c3 = wlc_lcnphy_get_cc(pi, cal_type); phy_c15 = (s16) phy_c3.re; phy_c16 = (s16) phy_c3.im; if (cal_type == 2) { if (phy_c3.re > 127) phy_c15 = phy_c3.re - 256; if (phy_c3.im > 127) phy_c16 = phy_c3.im - 256; } wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16); udelay(20); for (phy_c8 = 0; phy_c7 != 0 && phy_c8 < num_levels; phy_c8++) { phy_c23 = true; phy_c22 = false; switch (cal_type) { case 0: phy_c10 = 511; break; case 2: phy_c10 = 127; break; case 3: phy_c10 = 15; break; case 4: phy_c10 = 15; break; } phy_c9 = read_phy_reg(pi, 0x93d); phy_c9 = 2 * phy_c9; phy_c24 = false; phy_c5 = 7; phy_c25 = true; while (1) { write_radio_reg(pi, RADIO_2064_REG026, (phy_c5 & 0x7) | ((phy_c5 & 0x7) << 4)); udelay(50); phy_c22 = false; ptr[130] = 0; wlc_lcnphy_samp_cap(pi, 1, phy_c9, &ptr[0], 2); if (ptr[130] == 1) phy_c22 = true; if (phy_c22) phy_c5 -= 1; if ((phy_c22 != phy_c24) && (!phy_c25)) break; if (!phy_c22) phy_c5 += 1; if (phy_c5 <= 0 || phy_c5 >= 7) break; phy_c24 = phy_c22; phy_c25 = false; } if (phy_c5 < 0) phy_c5 = 0; else if (phy_c5 > 7) phy_c5 = 7; for (k = -phy_c7; k <= phy_c7; k += phy_c7) { for (l = -phy_c7; l <= phy_c7; l += phy_c7) { phy_c11 = phy_c15 + k; phy_c12 = phy_c16 + l; if (phy_c11 < -phy_c10) phy_c11 = -phy_c10; else if (phy_c11 > phy_c10) phy_c11 = phy_c10; if (phy_c12 < -phy_c10) phy_c12 = -phy_c10; else if (phy_c12 > phy_c10) phy_c12 = phy_c10; wlc_lcnphy_set_cc(pi, cal_type, phy_c11, phy_c12); udelay(20); wlc_lcnphy_samp_cap(pi, 0, 0, ptr, 2); phy_c18 = 0; phy_c19 = 0; for (j = 0; j < 128; j++) { if (cal_type != 0) phy_c6 = j % phy_c4; else phy_c6 = (2 * j) % phy_c4; phy_c2.re = phy_c1[phy_c6].re; phy_c2.im = phy_c1[phy_c6].im; phy_c17 = ptr[j]; phy_c18 = phy_c18 + phy_c17 * phy_c2.re; phy_c19 = phy_c19 + phy_c17 * phy_c2.im; } phy_c18 = phy_c18 >> 10; phy_c19 = phy_c19 >> 10; phy_c20 = ((phy_c18 * phy_c18) + (phy_c19 * phy_c19)); if (phy_c23 || phy_c20 < phy_c21) { phy_c21 = phy_c20; phy_c13 = phy_c11; phy_c14 = phy_c12; } phy_c23 = false; } } phy_c23 = true; phy_c15 = phy_c13; phy_c16 = phy_c14; phy_c7 = phy_c7 >> 1; wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16); udelay(20); } goto cleanup; cleanup: wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, phy_c32); wlc_lcnphy_stop_tx_tone(pi); write_phy_reg(pi, 0x6da, phy_c26); write_phy_reg(pi, 0x6db, phy_c27); write_phy_reg(pi, 0x938, phy_c28); write_phy_reg(pi, 0x4d7, phy_c29); write_phy_reg(pi, 0x4d8, phy_c30); write_radio_reg(pi, RADIO_2064_REG026, phy_c31); kfree(phy_c32); kfree(ptr); } void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b) { u16 iqcc[2]; struct phytbl_info tab; tab.tbl_ptr = iqcc; tab.tbl_len = 2; tab.tbl_id = 0; tab.tbl_offset = 80; tab.tbl_width = 16; wlc_lcnphy_read_table(pi, &tab); *a = iqcc[0]; *b = iqcc[1]; } static void wlc_lcnphy_tx_iqlo_soft_cal_full(struct brcms_phy *pi) { struct lcnphy_unsign16_struct iqcc0, locc2, locc3, locc4; wlc_lcnphy_set_cc(pi, 0, 0, 0); wlc_lcnphy_set_cc(pi, 2, 0, 0); wlc_lcnphy_set_cc(pi, 3, 0, 0); wlc_lcnphy_set_cc(pi, 4, 0, 0); wlc_lcnphy_a1(pi, 4, 0, 0); wlc_lcnphy_a1(pi, 3, 0, 0); wlc_lcnphy_a1(pi, 2, 3, 2); wlc_lcnphy_a1(pi, 0, 5, 8); wlc_lcnphy_a1(pi, 2, 2, 1); wlc_lcnphy_a1(pi, 0, 4, 3); iqcc0 = wlc_lcnphy_get_cc(pi, 0); locc2 = wlc_lcnphy_get_cc(pi, 2); locc3 = wlc_lcnphy_get_cc(pi, 3); locc4 = wlc_lcnphy_get_cc(pi, 4); } u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi) { struct phytbl_info tab; u16 didq; tab.tbl_id = 0; tab.tbl_width = 16; tab.tbl_ptr = &didq; tab.tbl_len = 1; tab.tbl_offset = 85; wlc_lcnphy_read_table(pi, &tab); return didq; } static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi) { struct lcnphy_txgains target_gains, old_gains; u8 save_bb_mult; u16 a, b, didq, save_pa_gain = 0; uint idx, SAVE_txpwrindex = 0xFF; u32 val; u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); struct phytbl_info tab; u8 ei0, eq0, fi0, fq0; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; wlc_lcnphy_get_tx_gain(pi, &old_gains); save_pa_gain = wlc_lcnphy_get_pa_gain(pi); save_bb_mult = wlc_lcnphy_get_bbmult(pi); if (SAVE_txpwrctrl == LCNPHY_TX_PWR_CTRL_OFF) SAVE_txpwrindex = wlc_lcnphy_get_current_tx_pwr_idx(pi); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); target_gains.gm_gain = 7; target_gains.pga_gain = 0; target_gains.pad_gain = 21; target_gains.dac_gain = 0; wlc_lcnphy_set_tx_gain(pi, &target_gains); wlc_lcnphy_set_tx_pwr_by_index(pi, 16); if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) { wlc_lcnphy_set_tx_pwr_by_index(pi, 30); wlc_lcnphy_tx_iqlo_cal(pi, &target_gains, (pi_lcn-> lcnphy_recal ? LCNPHY_CAL_RECAL : LCNPHY_CAL_FULL), false); } else { wlc_lcnphy_tx_iqlo_soft_cal_full(pi); } wlc_lcnphy_get_radio_loft(pi, &ei0, &eq0, &fi0, &fq0); if ((abs((s8) fi0) == 15) && (abs((s8) fq0) == 15)) { if (CHSPEC_IS5G(pi->radio_chanspec)) { target_gains.gm_gain = 255; target_gains.pga_gain = 255; target_gains.pad_gain = 0xf0; target_gains.dac_gain = 0; } else { target_gains.gm_gain = 7; target_gains.pga_gain = 45; target_gains.pad_gain = 186; target_gains.dac_gain = 0; } if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) { target_gains.pga_gain = 0; target_gains.pad_gain = 30; wlc_lcnphy_set_tx_pwr_by_index(pi, 16); wlc_lcnphy_tx_iqlo_cal(pi, &target_gains, LCNPHY_CAL_FULL, false); } else { wlc_lcnphy_tx_iqlo_soft_cal_full(pi); } } wlc_lcnphy_get_tx_iqcc(pi, &a, &b); didq = wlc_lcnphy_get_tx_locc(pi); tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_ptr = &val; tab.tbl_len = 1; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; for (idx = 0; idx < 128; idx++) { tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + idx; wlc_lcnphy_read_table(pi, &tab); val = (val & 0xfff00000) | ((u32) (a & 0x3FF) << 10) | (b & 0x3ff); wlc_lcnphy_write_table(pi, &tab); val = didq; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_LO_OFFSET + idx; wlc_lcnphy_write_table(pi, &tab); } pi_lcn->lcnphy_cal_results.txiqlocal_a = a; pi_lcn->lcnphy_cal_results.txiqlocal_b = b; pi_lcn->lcnphy_cal_results.txiqlocal_didq = didq; pi_lcn->lcnphy_cal_results.txiqlocal_ei0 = ei0; pi_lcn->lcnphy_cal_results.txiqlocal_eq0 = eq0; pi_lcn->lcnphy_cal_results.txiqlocal_fi0 = fi0; pi_lcn->lcnphy_cal_results.txiqlocal_fq0 = fq0; wlc_lcnphy_set_bbmult(pi, save_bb_mult); wlc_lcnphy_set_pa_gain(pi, save_pa_gain); wlc_lcnphy_set_tx_gain(pi, &old_gains); if (SAVE_txpwrctrl != LCNPHY_TX_PWR_CTRL_OFF) wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); else wlc_lcnphy_set_tx_pwr_by_index(pi, SAVE_txpwrindex); } s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode) { u16 tempsenseval1, tempsenseval2; s16 avg = 0; bool suspend = false; if (mode == 1) { suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); } tempsenseval1 = read_phy_reg(pi, 0x476) & 0x1FF; tempsenseval2 = read_phy_reg(pi, 0x477) & 0x1FF; if (tempsenseval1 > 255) avg = (s16) (tempsenseval1 - 512); else avg = (s16) tempsenseval1; if (tempsenseval2 > 255) avg += (s16) (tempsenseval2 - 512); else avg += (s16) tempsenseval2; avg /= 2; if (mode == 1) { mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); udelay(100); mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } return avg; } u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode) { u16 tempsenseval1, tempsenseval2; s32 avg = 0; bool suspend = false; u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; if (mode == 1) { suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); } tempsenseval1 = read_phy_reg(pi, 0x476) & 0x1FF; tempsenseval2 = read_phy_reg(pi, 0x477) & 0x1FF; if (tempsenseval1 > 255) avg = (int)(tempsenseval1 - 512); else avg = (int)tempsenseval1; if (pi_lcn->lcnphy_tempsense_option == 1 || pi->hwpwrctrl_capable) { if (tempsenseval2 > 255) avg = (int)(avg - tempsenseval2 + 512); else avg = (int)(avg - tempsenseval2); } else { if (tempsenseval2 > 255) avg = (int)(avg + tempsenseval2 - 512); else avg = (int)(avg + tempsenseval2); avg = avg / 2; } if (avg < 0) avg = avg + 512; if (pi_lcn->lcnphy_tempsense_option == 2) avg = tempsenseval1; if (mode) wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); if (mode == 1) { mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); udelay(100); mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } return (u16) avg; } s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode) { s32 degree = wlc_lcnphy_tempsense_new(pi, mode); degree = ((degree << 10) + LCN_TEMPSENSE_OFFSET + (LCN_TEMPSENSE_DEN >> 1)) / LCN_TEMPSENSE_DEN; return (s8) degree; } s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode) { u16 vbatsenseval; s32 avg = 0; bool suspend = false; if (mode == 1) { suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_lcnphy_vbat_temp_sense_setup(pi, VBATSENSE); } vbatsenseval = read_phy_reg(pi, 0x475) & 0x1FF; if (vbatsenseval > 255) avg = (s32) (vbatsenseval - 512); else avg = (s32) vbatsenseval; avg = (avg * LCN_VBAT_SCALE_NOM + (LCN_VBAT_SCALE_DEN >> 1)) / LCN_VBAT_SCALE_DEN; if (mode == 1) { if (!suspend) wlapi_enable_mac(pi->sh->physhim); } return (s8) avg; } static void wlc_lcnphy_afe_clk_init(struct brcms_phy *pi, u8 mode) { u8 phybw40; phybw40 = CHSPEC_IS40(pi->radio_chanspec); mod_phy_reg(pi, 0x6d1, (0x1 << 7), (1) << 7); if (((mode == AFE_CLK_INIT_MODE_PAPD) && (phybw40 == 0)) || (mode == AFE_CLK_INIT_MODE_TXRX2X)) write_phy_reg(pi, 0x6d0, 0x7); wlc_lcnphy_toggle_afe_pwdn(pi); } static void wlc_lcnphy_temp_adj(struct brcms_phy *pi) { } static void wlc_lcnphy_glacial_timer_based_cal(struct brcms_phy *pi) { bool suspend; s8 index; u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_lcnphy_deaf_mode(pi, true); pi->phy_lastcal = pi->sh->now; pi->phy_forcecal = false; index = pi_lcn->lcnphy_current_index; wlc_lcnphy_txpwrtbl_iqlo_cal(pi); wlc_lcnphy_set_tx_pwr_by_index(pi, index); wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_pwrctrl); wlc_lcnphy_deaf_mode(pi, false); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } static void wlc_lcnphy_periodic_cal(struct brcms_phy *pi) { bool suspend, full_cal; const struct lcnphy_rx_iqcomp *rx_iqcomp; int rx_iqcomp_sz; u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); s8 index; struct phytbl_info tab; s32 a1, b0, b1; s32 tssi, pwr, maxtargetpwr, mintargetpwr; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; pi->phy_lastcal = pi->sh->now; pi->phy_forcecal = false; full_cal = (pi_lcn->lcnphy_full_cal_channel != CHSPEC_CHANNEL(pi->radio_chanspec)); pi_lcn->lcnphy_full_cal_channel = CHSPEC_CHANNEL(pi->radio_chanspec); index = pi_lcn->lcnphy_current_index; suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) { wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); wlapi_suspend_mac_and_wait(pi->sh->physhim); } wlc_lcnphy_deaf_mode(pi, true); wlc_lcnphy_txpwrtbl_iqlo_cal(pi); rx_iqcomp = lcnphy_rx_iqcomp_table_rev0; rx_iqcomp_sz = ARRAY_SIZE(lcnphy_rx_iqcomp_table_rev0); if (LCNREV_IS(pi->pubpi.phy_rev, 1)) wlc_lcnphy_rx_iq_cal(pi, NULL, 0, true, false, 1, 40); else wlc_lcnphy_rx_iq_cal(pi, NULL, 0, true, false, 1, 127); if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) { wlc_lcnphy_idle_tssi_est((struct brcms_phy_pub *) pi); b0 = pi->txpa_2g[0]; b1 = pi->txpa_2g[1]; a1 = pi->txpa_2g[2]; maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1); mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1); tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_ptr = &pwr; tab.tbl_len = 1; tab.tbl_offset = 0; for (tssi = 0; tssi < 128; tssi++) { pwr = wlc_lcnphy_tssi2dbm(tssi, a1, b0, b1); pwr = (pwr < mintargetpwr) ? mintargetpwr : pwr; wlc_lcnphy_write_table(pi, &tab); tab.tbl_offset++; } } wlc_lcnphy_set_tx_pwr_by_index(pi, index); wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_pwrctrl); wlc_lcnphy_deaf_mode(pi, false); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode) { u16 temp_new; int temp1, temp2, temp_diff; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; switch (mode) { case PHY_PERICAL_CHAN: break; case PHY_FULLCAL: wlc_lcnphy_periodic_cal(pi); break; case PHY_PERICAL_PHYINIT: wlc_lcnphy_periodic_cal(pi); break; case PHY_PERICAL_WATCHDOG: if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { temp_new = wlc_lcnphy_tempsense(pi, 0); temp1 = LCNPHY_TEMPSENSE(temp_new); temp2 = LCNPHY_TEMPSENSE(pi_lcn->lcnphy_cal_temper); temp_diff = temp1 - temp2; if ((pi_lcn->lcnphy_cal_counter > 90) || (temp_diff > 60) || (temp_diff < -60)) { wlc_lcnphy_glacial_timer_based_cal(pi); wlc_2064_vco_cal(pi); pi_lcn->lcnphy_cal_temper = temp_new; pi_lcn->lcnphy_cal_counter = 0; } else pi_lcn->lcnphy_cal_counter++; } break; case LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL: if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) wlc_lcnphy_tx_power_adjustment( (struct brcms_phy_pub *) pi); break; } } void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr) { s8 cck_offset; u16 status; status = (read_phy_reg(pi, 0x4ab)); if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) && (status & (0x1 << 15))) { *ofdm_pwr = (s8) (((read_phy_reg(pi, 0x4ab) & (0x1ff << 0)) >> 0) >> 1); if (wlc_phy_tpc_isenabled_lcnphy(pi)) cck_offset = pi->tx_power_offset[TXP_FIRST_CCK]; else cck_offset = 0; *cck_pwr = *ofdm_pwr + cck_offset; } else { *cck_pwr = 0; *ofdm_pwr = 0; } } void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi) { return; } void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi) { s8 index; u16 index2; struct brcms_phy *pi = (struct brcms_phy *) ppi; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) && SAVE_txpwrctrl) { index = wlc_lcnphy_tempcompensated_txpwrctrl(pi); index2 = (u16) (index * 2); mod_phy_reg(pi, 0x4a9, (0x1ff << 0), (index2) << 0); pi_lcn->lcnphy_current_index = (s8)((read_phy_reg(pi, 0x4a9) & 0xFF) / 2); } } static void wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi, const struct lcnphy_tx_gain_tbl_entry *gain_table) { u32 j; struct phytbl_info tab; u32 val; u16 pa_gain; u16 gm_gain; if (CHSPEC_IS5G(pi->radio_chanspec)) pa_gain = 0x70; else pa_gain = 0x70; if (pi->sh->boardflags & BFL_FEM) pa_gain = 0x10; tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_len = 1; tab.tbl_ptr = &val; for (j = 0; j < 128; j++) { gm_gain = gain_table[j].gm; val = (((u32) pa_gain << 24) | (gain_table[j].pad << 16) | (gain_table[j].pga << 8) | gm_gain); tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + j; wlc_lcnphy_write_table(pi, &tab); val = (gain_table[j].dac << 28) | (gain_table[j].bb_mult << 20); tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + j; wlc_lcnphy_write_table(pi, &tab); } } static void wlc_lcnphy_load_rfpower(struct brcms_phy *pi) { struct phytbl_info tab; u32 val, bbmult, rfgain; u8 index; u8 scale_factor = 1; s16 temp, temp1, temp2, qQ, qQ1, qQ2, shift; tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_len = 1; for (index = 0; index < 128; index++) { tab.tbl_ptr = &bbmult; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index; wlc_lcnphy_read_table(pi, &tab); bbmult = bbmult >> 20; tab.tbl_ptr = &rfgain; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index; wlc_lcnphy_read_table(pi, &tab); qm_log10((s32) (bbmult), 0, &temp1, &qQ1); qm_log10((s32) (1 << 6), 0, &temp2, &qQ2); if (qQ1 < qQ2) { temp2 = qm_shr16(temp2, qQ2 - qQ1); qQ = qQ1; } else { temp1 = qm_shr16(temp1, qQ1 - qQ2); qQ = qQ2; } temp = qm_sub16(temp1, temp2); if (qQ >= 4) shift = qQ - 4; else shift = 4 - qQ; val = (((index << shift) + (5 * temp) + (1 << (scale_factor + shift - 3))) >> (scale_factor + shift - 2)); tab.tbl_ptr = &val; tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index; wlc_lcnphy_write_table(pi, &tab); } } static void wlc_lcnphy_bu_tweaks(struct brcms_phy *pi) { or_phy_reg(pi, 0x805, 0x1); mod_phy_reg(pi, 0x42f, (0x7 << 0), (0x3) << 0); mod_phy_reg(pi, 0x030, (0x7 << 0), (0x3) << 0); write_phy_reg(pi, 0x414, 0x1e10); write_phy_reg(pi, 0x415, 0x0640); mod_phy_reg(pi, 0x4df, (0xff << 8), -9 << 8); or_phy_reg(pi, 0x44a, 0x44); write_phy_reg(pi, 0x44a, 0x80); mod_phy_reg(pi, 0x434, (0xff << 0), (0xFD) << 0); mod_phy_reg(pi, 0x420, (0xff << 0), (16) << 0); if (!(pi->sh->boardrev < 0x1204)) mod_radio_reg(pi, RADIO_2064_REG09B, 0xF0, 0xF0); write_phy_reg(pi, 0x7d6, 0x0902); mod_phy_reg(pi, 0x429, (0xf << 0), (0x9) << 0); mod_phy_reg(pi, 0x429, (0x3f << 4), (0xe) << 4); if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { mod_phy_reg(pi, 0x423, (0xff << 0), (0x46) << 0); mod_phy_reg(pi, 0x411, (0xff << 0), (1) << 0); mod_phy_reg(pi, 0x434, (0xff << 0), (0xFF) << 0); mod_phy_reg(pi, 0x656, (0xf << 0), (2) << 0); mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2); mod_radio_reg(pi, RADIO_2064_REG0F7, 0x4, 0x4); mod_radio_reg(pi, RADIO_2064_REG0F1, 0x3, 0); mod_radio_reg(pi, RADIO_2064_REG0F2, 0xF8, 0x90); mod_radio_reg(pi, RADIO_2064_REG0F3, 0x3, 0x2); mod_radio_reg(pi, RADIO_2064_REG0F3, 0xf0, 0xa0); mod_radio_reg(pi, RADIO_2064_REG11F, 0x2, 0x2); wlc_lcnphy_clear_tx_power_offsets(pi); mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (10) << 6); } } static void wlc_lcnphy_rcal(struct brcms_phy *pi) { u8 rcal_value; and_radio_reg(pi, RADIO_2064_REG05B, 0xfD); or_radio_reg(pi, RADIO_2064_REG004, 0x40); or_radio_reg(pi, RADIO_2064_REG120, 0x10); or_radio_reg(pi, RADIO_2064_REG078, 0x80); or_radio_reg(pi, RADIO_2064_REG129, 0x02); or_radio_reg(pi, RADIO_2064_REG057, 0x01); or_radio_reg(pi, RADIO_2064_REG05B, 0x02); mdelay(5); SPINWAIT(!wlc_radio_2064_rcal_done(pi), 10 * 1000 * 1000); if (wlc_radio_2064_rcal_done(pi)) { rcal_value = (u8) read_radio_reg(pi, RADIO_2064_REG05C); rcal_value = rcal_value & 0x1f; } and_radio_reg(pi, RADIO_2064_REG05B, 0xfD); and_radio_reg(pi, RADIO_2064_REG057, 0xFE); } static void wlc_lcnphy_rc_cal(struct brcms_phy *pi) { u8 dflt_rc_cal_val; u16 flt_val; dflt_rc_cal_val = 7; if (LCNREV_IS(pi->pubpi.phy_rev, 1)) dflt_rc_cal_val = 11; flt_val = (dflt_rc_cal_val << 10) | (dflt_rc_cal_val << 5) | (dflt_rc_cal_val); write_phy_reg(pi, 0x933, flt_val); write_phy_reg(pi, 0x934, flt_val); write_phy_reg(pi, 0x935, flt_val); write_phy_reg(pi, 0x936, flt_val); write_phy_reg(pi, 0x937, (flt_val & 0x1FF)); return; } static void wlc_radio_2064_init(struct brcms_phy *pi) { u32 i; const struct lcnphy_radio_regs *lcnphyregs = NULL; lcnphyregs = lcnphy_radio_regs_2064; for (i = 0; lcnphyregs[i].address != 0xffff; i++) if (CHSPEC_IS5G(pi->radio_chanspec) && lcnphyregs[i].do_init_a) write_radio_reg(pi, ((lcnphyregs[i].address & 0x3fff) | RADIO_DEFAULT_CORE), (u16) lcnphyregs[i].init_a); else if (lcnphyregs[i].do_init_g) write_radio_reg(pi, ((lcnphyregs[i].address & 0x3fff) | RADIO_DEFAULT_CORE), (u16) lcnphyregs[i].init_g); write_radio_reg(pi, RADIO_2064_REG032, 0x62); write_radio_reg(pi, RADIO_2064_REG033, 0x19); write_radio_reg(pi, RADIO_2064_REG090, 0x10); write_radio_reg(pi, RADIO_2064_REG010, 0x00); if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { write_radio_reg(pi, RADIO_2064_REG060, 0x7f); write_radio_reg(pi, RADIO_2064_REG061, 0x72); write_radio_reg(pi, RADIO_2064_REG062, 0x7f); } write_radio_reg(pi, RADIO_2064_REG01D, 0x02); write_radio_reg(pi, RADIO_2064_REG01E, 0x06); mod_phy_reg(pi, 0x4ea, (0x7 << 0), 0 << 0); mod_phy_reg(pi, 0x4ea, (0x7 << 3), 1 << 3); mod_phy_reg(pi, 0x4ea, (0x7 << 6), 2 << 6); mod_phy_reg(pi, 0x4ea, (0x7 << 9), 3 << 9); mod_phy_reg(pi, 0x4ea, (0x7 << 12), 4 << 12); write_phy_reg(pi, 0x4ea, 0x4688); mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0); mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6); mod_phy_reg(pi, 0x46a, (0xffff << 0), 25 << 0); wlc_lcnphy_set_tx_locc(pi, 0); wlc_lcnphy_rcal(pi); wlc_lcnphy_rc_cal(pi); } static void wlc_lcnphy_radio_init(struct brcms_phy *pi) { wlc_radio_2064_init(pi); } static void wlc_lcnphy_tbl_init(struct brcms_phy *pi) { uint idx; u8 phybw40; struct phytbl_info tab; u32 val; phybw40 = CHSPEC_IS40(pi->radio_chanspec); for (idx = 0; idx < dot11lcnphytbl_info_sz_rev0; idx++) wlc_lcnphy_write_table(pi, &dot11lcnphytbl_info_rev0[idx]); if (pi->sh->boardflags & BFL_FEM_BT) { tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; tab.tbl_width = 16; tab.tbl_ptr = &val; tab.tbl_len = 1; val = 100; tab.tbl_offset = 4; wlc_lcnphy_write_table(pi, &tab); } tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; tab.tbl_width = 16; tab.tbl_ptr = &val; tab.tbl_len = 1; val = 114; tab.tbl_offset = 0; wlc_lcnphy_write_table(pi, &tab); val = 130; tab.tbl_offset = 1; wlc_lcnphy_write_table(pi, &tab); val = 6; tab.tbl_offset = 8; wlc_lcnphy_write_table(pi, &tab); if (CHSPEC_IS2G(pi->radio_chanspec)) { if (pi->sh->boardflags & BFL_FEM) wlc_lcnphy_load_tx_gain_table( pi, dot11lcnphy_2GHz_extPA_gaintable_rev0); else wlc_lcnphy_load_tx_gain_table( pi, dot11lcnphy_2GHz_gaintable_rev0); } if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { const struct phytbl_info *tb; int l; if (CHSPEC_IS2G(pi->radio_chanspec)) { l = dot11lcnphytbl_rx_gain_info_2G_rev2_sz; if (pi->sh->boardflags & BFL_EXTLNA) tb = dot11lcnphytbl_rx_gain_info_extlna_2G_rev2; else tb = dot11lcnphytbl_rx_gain_info_2G_rev2; } else { l = dot11lcnphytbl_rx_gain_info_5G_rev2_sz; if (pi->sh->boardflags & BFL_EXTLNA_5GHz) tb = dot11lcnphytbl_rx_gain_info_extlna_5G_rev2; else tb = dot11lcnphytbl_rx_gain_info_5G_rev2; } for (idx = 0; idx < l; idx++) wlc_lcnphy_write_table(pi, &tb[idx]); } if ((pi->sh->boardflags & BFL_FEM) && !(pi->sh->boardflags & BFL_FEM_BT)) wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa); else if (pi->sh->boardflags & BFL_FEM_BT) { if (pi->sh->boardrev < 0x1250) wlc_lcnphy_write_table( pi, &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa); else wlc_lcnphy_write_table( pi, &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250); } else wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313); wlc_lcnphy_load_rfpower(pi); wlc_lcnphy_clear_papd_comptable(pi); } static void wlc_lcnphy_rev0_baseband_init(struct brcms_phy *pi) { u16 afectrl1; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; write_radio_reg(pi, RADIO_2064_REG11C, 0x0); write_phy_reg(pi, 0x43b, 0x0); write_phy_reg(pi, 0x43c, 0x0); write_phy_reg(pi, 0x44c, 0x0); write_phy_reg(pi, 0x4e6, 0x0); write_phy_reg(pi, 0x4f9, 0x0); write_phy_reg(pi, 0x4b0, 0x0); write_phy_reg(pi, 0x938, 0x0); write_phy_reg(pi, 0x4b0, 0x0); write_phy_reg(pi, 0x44e, 0); or_phy_reg(pi, 0x567, 0x03); or_phy_reg(pi, 0x44a, 0x44); write_phy_reg(pi, 0x44a, 0x80); if (!(pi->sh->boardflags & BFL_FEM)) wlc_lcnphy_set_tx_pwr_by_index(pi, 52); if (0) { afectrl1 = 0; afectrl1 = (u16) ((pi_lcn->lcnphy_rssi_vf) | (pi_lcn->lcnphy_rssi_vc << 4) | (pi_lcn->lcnphy_rssi_gs << 10)); write_phy_reg(pi, 0x43e, afectrl1); } mod_phy_reg(pi, 0x634, (0xff << 0), 0xC << 0); if (pi->sh->boardflags & BFL_FEM) { mod_phy_reg(pi, 0x634, (0xff << 0), 0xA << 0); write_phy_reg(pi, 0x910, 0x1); } mod_phy_reg(pi, 0x448, (0x3 << 8), 1 << 8); mod_phy_reg(pi, 0x608, (0xff << 0), 0x17 << 0); mod_phy_reg(pi, 0x604, (0x7ff << 0), 0x3EA << 0); } static void wlc_lcnphy_rev2_baseband_init(struct brcms_phy *pi) { if (CHSPEC_IS5G(pi->radio_chanspec)) { mod_phy_reg(pi, 0x416, (0xff << 0), 80 << 0); mod_phy_reg(pi, 0x416, (0xff << 8), 80 << 8); } } static void wlc_lcnphy_agc_temp_init(struct brcms_phy *pi) { s16 temp; struct phytbl_info tab; u32 tableBuffer[2]; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; temp = (s16) read_phy_reg(pi, 0x4df); pi_lcn->lcnphy_ofdmgainidxtableoffset = (temp & (0xff << 0)) >> 0; if (pi_lcn->lcnphy_ofdmgainidxtableoffset > 127) pi_lcn->lcnphy_ofdmgainidxtableoffset -= 256; pi_lcn->lcnphy_dsssgainidxtableoffset = (temp & (0xff << 8)) >> 8; if (pi_lcn->lcnphy_dsssgainidxtableoffset > 127) pi_lcn->lcnphy_dsssgainidxtableoffset -= 256; tab.tbl_ptr = tableBuffer; tab.tbl_len = 2; tab.tbl_id = 17; tab.tbl_offset = 59; tab.tbl_width = 32; wlc_lcnphy_read_table(pi, &tab); if (tableBuffer[0] > 63) tableBuffer[0] -= 128; pi_lcn->lcnphy_tr_R_gain_val = tableBuffer[0]; if (tableBuffer[1] > 63) tableBuffer[1] -= 128; pi_lcn->lcnphy_tr_T_gain_val = tableBuffer[1]; temp = (s16) (read_phy_reg(pi, 0x434) & (0xff << 0)); if (temp > 127) temp -= 256; pi_lcn->lcnphy_input_pwr_offset_db = (s8) temp; pi_lcn->lcnphy_Med_Low_Gain_db = (read_phy_reg(pi, 0x424) & (0xff << 8)) >> 8; pi_lcn->lcnphy_Very_Low_Gain_db = (read_phy_reg(pi, 0x425) & (0xff << 0)) >> 0; tab.tbl_ptr = tableBuffer; tab.tbl_len = 2; tab.tbl_id = LCNPHY_TBL_ID_GAIN_IDX; tab.tbl_offset = 28; tab.tbl_width = 32; wlc_lcnphy_read_table(pi, &tab); pi_lcn->lcnphy_gain_idx_14_lowword = tableBuffer[0]; pi_lcn->lcnphy_gain_idx_14_hiword = tableBuffer[1]; } static void wlc_lcnphy_baseband_init(struct brcms_phy *pi) { wlc_lcnphy_tbl_init(pi); wlc_lcnphy_rev0_baseband_init(pi); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) wlc_lcnphy_rev2_baseband_init(pi); wlc_lcnphy_bu_tweaks(pi); } void wlc_phy_init_lcnphy(struct brcms_phy *pi) { u8 phybw40; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; phybw40 = CHSPEC_IS40(pi->radio_chanspec); pi_lcn->lcnphy_cal_counter = 0; pi_lcn->lcnphy_cal_temper = pi_lcn->lcnphy_rawtempsense; or_phy_reg(pi, 0x44a, 0x80); and_phy_reg(pi, 0x44a, 0x7f); wlc_lcnphy_afe_clk_init(pi, AFE_CLK_INIT_MODE_TXRX2X); write_phy_reg(pi, 0x60a, 160); write_phy_reg(pi, 0x46a, 25); wlc_lcnphy_baseband_init(pi); wlc_lcnphy_radio_init(pi); if (CHSPEC_IS2G(pi->radio_chanspec)) wlc_lcnphy_tx_pwr_ctrl_init((struct brcms_phy_pub *) pi); wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); si_pmu_regcontrol(pi->sh->sih, 0, 0xf, 0x9); si_pmu_chipcontrol(pi->sh->sih, 0, 0xffffffff, 0x03CDDDDD); if ((pi->sh->boardflags & BFL_FEM) && wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) wlc_lcnphy_set_tx_pwr_by_index(pi, FIXED_TXPWR); wlc_lcnphy_agc_temp_init(pi); wlc_lcnphy_temp_adj(pi); mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); udelay(100); mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW); pi_lcn->lcnphy_noise_samples = LCNPHY_NOISE_SAMPLES_DEFAULT; wlc_lcnphy_calib_modes(pi, PHY_PERICAL_PHYINIT); } static bool wlc_phy_txpwr_srom_read_lcnphy(struct brcms_phy *pi) { s8 txpwr = 0; int i; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; struct ssb_sprom *sprom = &pi->d11core->bus->sprom; if (CHSPEC_IS2G(pi->radio_chanspec)) { u16 cckpo = 0; u32 offset_ofdm, offset_mcs; pi_lcn->lcnphy_tr_isolation_mid = sprom->fem.ghz2.tr_iso; pi_lcn->lcnphy_rx_power_offset = sprom->rxpo2g; pi->txpa_2g[0] = sprom->pa0b0; pi->txpa_2g[1] = sprom->pa0b1; pi->txpa_2g[2] = sprom->pa0b2; pi_lcn->lcnphy_rssi_vf = sprom->rssismf2g; pi_lcn->lcnphy_rssi_vc = sprom->rssismc2g; pi_lcn->lcnphy_rssi_gs = sprom->rssisav2g; pi_lcn->lcnphy_rssi_vf_lowtemp = pi_lcn->lcnphy_rssi_vf; pi_lcn->lcnphy_rssi_vc_lowtemp = pi_lcn->lcnphy_rssi_vc; pi_lcn->lcnphy_rssi_gs_lowtemp = pi_lcn->lcnphy_rssi_gs; pi_lcn->lcnphy_rssi_vf_hightemp = pi_lcn->lcnphy_rssi_vf; pi_lcn->lcnphy_rssi_vc_hightemp = pi_lcn->lcnphy_rssi_vc; pi_lcn->lcnphy_rssi_gs_hightemp = pi_lcn->lcnphy_rssi_gs; txpwr = sprom->core_pwr_info[0].maxpwr_2g; pi->tx_srom_max_2g = txpwr; for (i = 0; i < PWRTBL_NUM_COEFF; i++) { pi->txpa_2g_low_temp[i] = pi->txpa_2g[i]; pi->txpa_2g_high_temp[i] = pi->txpa_2g[i]; } cckpo = sprom->cck2gpo; offset_ofdm = sprom->ofdm2gpo; if (cckpo) { uint max_pwr_chan = txpwr; for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) { pi->tx_srom_max_rate_2g[i] = max_pwr_chan - ((cckpo & 0xf) * 2); cckpo >>= 4; } for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) { pi->tx_srom_max_rate_2g[i] = max_pwr_chan - ((offset_ofdm & 0xf) * 2); offset_ofdm >>= 4; } } else { u8 opo = 0; opo = sprom->opo; for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) pi->tx_srom_max_rate_2g[i] = txpwr; for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) { pi->tx_srom_max_rate_2g[i] = txpwr - ((offset_ofdm & 0xf) * 2); offset_ofdm >>= 4; } offset_mcs = sprom->mcs2gpo[1] << 16; offset_mcs |= sprom->mcs2gpo[0]; pi_lcn->lcnphy_mcs20_po = offset_mcs; for (i = TXP_FIRST_SISO_MCS_20; i <= TXP_LAST_SISO_MCS_20; i++) { pi->tx_srom_max_rate_2g[i] = txpwr - ((offset_mcs & 0xf) * 2); offset_mcs >>= 4; } } pi_lcn->lcnphy_rawtempsense = sprom->rawtempsense; pi_lcn->lcnphy_measPower = sprom->measpower; pi_lcn->lcnphy_tempsense_slope = sprom->tempsense_slope; pi_lcn->lcnphy_hw_iqcal_en = sprom->hw_iqcal_en; pi_lcn->lcnphy_iqcal_swp_dis = sprom->iqcal_swp_dis; pi_lcn->lcnphy_tempcorrx = sprom->tempcorrx; pi_lcn->lcnphy_tempsense_option = sprom->tempsense_option; pi_lcn->lcnphy_freqoffset_corr = sprom->freqoffset_corr; if (sprom->ant_available_bg > 1) wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi, sprom->ant_available_bg); } pi_lcn->lcnphy_cck_dig_filt_type = -1; return true; } void wlc_2064_vco_cal(struct brcms_phy *pi) { u8 calnrst; mod_radio_reg(pi, RADIO_2064_REG057, 1 << 3, 1 << 3); calnrst = (u8) read_radio_reg(pi, RADIO_2064_REG056) & 0xf8; write_radio_reg(pi, RADIO_2064_REG056, calnrst); udelay(1); write_radio_reg(pi, RADIO_2064_REG056, calnrst | 0x03); udelay(1); write_radio_reg(pi, RADIO_2064_REG056, calnrst | 0x07); udelay(300); mod_radio_reg(pi, RADIO_2064_REG057, 1 << 3, 0); } bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi) { if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) return 0; else return (LCNPHY_TX_PWR_CTRL_HW == wlc_lcnphy_get_tx_pwr_ctrl((pi))); } void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi) { u16 pwr_ctrl; if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { wlc_lcnphy_calib_modes(pi, LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL); } else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) { pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); wlc_lcnphy_txpower_recalc_target(pi); wlc_lcnphy_set_tx_pwr_ctrl(pi, pwr_ctrl); } } void wlc_phy_detach_lcnphy(struct brcms_phy *pi) { kfree(pi->u.pi_lcnphy); } bool wlc_phy_attach_lcnphy(struct brcms_phy *pi) { struct brcms_phy_lcnphy *pi_lcn; pi->u.pi_lcnphy = kzalloc(sizeof(struct brcms_phy_lcnphy), GFP_ATOMIC); if (pi->u.pi_lcnphy == NULL) return false; pi_lcn = pi->u.pi_lcnphy; if (0 == (pi->sh->boardflags & BFL_NOPA)) { pi->hwpwrctrl = true; pi->hwpwrctrl_capable = true; } pi->xtalfreq = si_pmu_alp_clock(pi->sh->sih); pi_lcn->lcnphy_papd_rxGnCtrl_init = 0; pi->pi_fptr.init = wlc_phy_init_lcnphy; pi->pi_fptr.calinit = wlc_phy_cal_init_lcnphy; pi->pi_fptr.chanset = wlc_phy_chanspec_set_lcnphy; pi->pi_fptr.txpwrrecalc = wlc_phy_txpower_recalc_target_lcnphy; pi->pi_fptr.txiqccget = wlc_lcnphy_get_tx_iqcc; pi->pi_fptr.txiqccset = wlc_lcnphy_set_tx_iqcc; pi->pi_fptr.txloccget = wlc_lcnphy_get_tx_locc; pi->pi_fptr.radioloftget = wlc_lcnphy_get_radio_loft; pi->pi_fptr.detach = wlc_phy_detach_lcnphy; if (!wlc_phy_txpwr_srom_read_lcnphy(pi)) return false; if ((pi->sh->boardflags & BFL_FEM) && (LCNREV_IS(pi->pubpi.phy_rev, 1))) { if (pi_lcn->lcnphy_tempsense_option == 3) { pi->hwpwrctrl = true; pi->hwpwrctrl_capable = true; pi->temppwrctrl_capable = false; } else { pi->hwpwrctrl = false; pi->hwpwrctrl_capable = false; pi->temppwrctrl_capable = true; } } return true; } static void wlc_lcnphy_set_rx_gain(struct brcms_phy *pi, u32 gain) { u16 trsw, ext_lna, lna1, lna2, tia, biq0, biq1, gain0_15, gain16_19; trsw = (gain & ((u32) 1 << 28)) ? 0 : 1; ext_lna = (u16) (gain >> 29) & 0x01; lna1 = (u16) (gain >> 0) & 0x0f; lna2 = (u16) (gain >> 4) & 0x0f; tia = (u16) (gain >> 8) & 0xf; biq0 = (u16) (gain >> 12) & 0xf; biq1 = (u16) (gain >> 16) & 0xf; gain0_15 = (u16) ((lna1 & 0x3) | ((lna1 & 0x3) << 2) | ((lna2 & 0x3) << 4) | ((lna2 & 0x3) << 6) | ((tia & 0xf) << 8) | ((biq0 & 0xf) << 12)); gain16_19 = biq1; mod_phy_reg(pi, 0x44d, (0x1 << 0), trsw << 0); mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10); mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0); mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0); if (CHSPEC_IS2G(pi->radio_chanspec)) { mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11); mod_phy_reg(pi, 0x4e6, (0x3 << 3), lna1 << 3); } wlc_lcnphy_rx_gain_override_enable(pi, true); } static u32 wlc_lcnphy_get_receive_power(struct brcms_phy *pi, s32 *gain_index) { u32 received_power = 0; s32 max_index = 0; u32 gain_code = 0; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; max_index = 36; if (*gain_index >= 0) gain_code = lcnphy_23bitgaincode_table[*gain_index]; if (-1 == *gain_index) { *gain_index = 0; while ((*gain_index <= (s32) max_index) && (received_power < 700)) { wlc_lcnphy_set_rx_gain(pi, lcnphy_23bitgaincode_table [*gain_index]); received_power = wlc_lcnphy_measure_digital_power( pi, pi_lcn-> lcnphy_noise_samples); (*gain_index)++; } (*gain_index)--; } else { wlc_lcnphy_set_rx_gain(pi, gain_code); received_power = wlc_lcnphy_measure_digital_power(pi, pi_lcn-> lcnphy_noise_samples); } return received_power; } s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index) { s32 gain = 0; s32 nominal_power_db; s32 log_val, gain_mismatch, desired_gain, input_power_offset_db, input_power_db; s32 received_power, temperature; u32 power; u32 msb1, msb2, val1, val2, diff1, diff2; uint freq; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; received_power = wlc_lcnphy_get_receive_power(pi, &gain_index); gain = lcnphy_gain_table[gain_index]; nominal_power_db = read_phy_reg(pi, 0x425) >> 8; power = (received_power * 16); msb1 = ffs(power) - 1; msb2 = msb1 + 1; val1 = 1 << msb1; val2 = 1 << msb2; diff1 = (power - val1); diff2 = (val2 - power); if (diff1 < diff2) log_val = msb1; else log_val = msb2; log_val = log_val * 3; gain_mismatch = (nominal_power_db / 2) - (log_val); desired_gain = gain + gain_mismatch; input_power_offset_db = read_phy_reg(pi, 0x434) & 0xFF; if (input_power_offset_db > 127) input_power_offset_db -= 256; input_power_db = input_power_offset_db - desired_gain; input_power_db = input_power_db + lcnphy_gain_index_offset_for_rssi[gain_index]; freq = wlc_phy_channel2freq(CHSPEC_CHANNEL(pi->radio_chanspec)); if ((freq > 2427) && (freq <= 2467)) input_power_db = input_power_db - 1; temperature = pi_lcn->lcnphy_lastsensed_temperature; if ((temperature - 15) < -30) input_power_db = input_power_db + (((temperature - 10 - 25) * 286) >> 12) - 7; else if ((temperature - 15) < 4) input_power_db = input_power_db + (((temperature - 10 - 25) * 286) >> 12) - 3; else input_power_db = input_power_db + (((temperature - 10 - 25) * 286) >> 12); wlc_lcnphy_rx_gain_override_enable(pi, 0); return input_power_db; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h0000644000175000017500000007362212026211315026224 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_PHY_INT_H_ #define _BRCM_PHY_INT_H_ #include #include #include #define PHY_VERSION { 1, 82, 8, 0 } #define LCNXN_BASEREV 16 struct phy_shim_info; struct brcms_phy_srom_fem { /* TSSI positive slope, 1: positive, 0: negative */ u8 tssipos; /* Ext PA gain-type: full-gain: 0, pa-lite: 1, no_pa: 2 */ u8 extpagain; /* support 32 combinations of different Pdet dynamic ranges */ u8 pdetrange; /* TR switch isolation */ u8 triso; /* antswctrl lookup table configuration: 32 possible choices */ u8 antswctrllut; }; #define ISNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_N) #define ISLCNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_LCN) #define PHY_GET_RFATTN(rfgain) ((rfgain) & 0x0f) #define PHY_GET_PADMIX(rfgain) (((rfgain) & 0x10) >> 4) #define PHY_GET_RFGAINID(rfattn, padmix, width) ((rfattn) + ((padmix)*(width))) #define PHY_SAT(x, n) ((x) > ((1<<((n)-1))-1) ? ((1<<((n)-1))-1) : \ ((x) < -(1<<((n)-1)) ? -(1<<((n)-1)) : (x))) #define PHY_SHIFT_ROUND(x, n) ((x) >= 0 ? ((x)+(1<<((n)-1)))>>(n) : (x)>>(n)) #define PHY_HW_ROUND(x, s) ((x >> s) + ((x >> (s-1)) & (s != 0))) #define CH_5G_GROUP 3 #define A_LOW_CHANS 0 #define A_MID_CHANS 1 #define A_HIGH_CHANS 2 #define CH_2G_GROUP 1 #define G_ALL_CHANS 0 #define FIRST_REF5_CHANNUM 149 #define LAST_REF5_CHANNUM 165 #define FIRST_5G_CHAN 14 #define LAST_5G_CHAN 50 #define FIRST_MID_5G_CHAN 14 #define LAST_MID_5G_CHAN 35 #define FIRST_HIGH_5G_CHAN 36 #define LAST_HIGH_5G_CHAN 41 #define FIRST_LOW_5G_CHAN 42 #define LAST_LOW_5G_CHAN 50 #define BASE_LOW_5G_CHAN 4900 #define BASE_MID_5G_CHAN 5100 #define BASE_HIGH_5G_CHAN 5500 #define CHAN5G_FREQ(chan) (5000 + chan*5) #define CHAN2G_FREQ(chan) (2407 + chan*5) #define TXP_FIRST_CCK 0 #define TXP_LAST_CCK 3 #define TXP_FIRST_OFDM 4 #define TXP_LAST_OFDM 11 #define TXP_FIRST_OFDM_20_CDD 12 #define TXP_LAST_OFDM_20_CDD 19 #define TXP_FIRST_MCS_20_SISO 20 #define TXP_LAST_MCS_20_SISO 27 #define TXP_FIRST_MCS_20_CDD 28 #define TXP_LAST_MCS_20_CDD 35 #define TXP_FIRST_MCS_20_STBC 36 #define TXP_LAST_MCS_20_STBC 43 #define TXP_FIRST_MCS_20_SDM 44 #define TXP_LAST_MCS_20_SDM 51 #define TXP_FIRST_OFDM_40_SISO 52 #define TXP_LAST_OFDM_40_SISO 59 #define TXP_FIRST_OFDM_40_CDD 60 #define TXP_LAST_OFDM_40_CDD 67 #define TXP_FIRST_MCS_40_SISO 68 #define TXP_LAST_MCS_40_SISO 75 #define TXP_FIRST_MCS_40_CDD 76 #define TXP_LAST_MCS_40_CDD 83 #define TXP_FIRST_MCS_40_STBC 84 #define TXP_LAST_MCS_40_STBC 91 #define TXP_FIRST_MCS_40_SDM 92 #define TXP_LAST_MCS_40_SDM 99 #define TXP_MCS_32 100 #define TXP_NUM_RATES 101 #define ADJ_PWR_TBL_LEN 84 #define TXP_FIRST_SISO_MCS_20 20 #define TXP_LAST_SISO_MCS_20 27 #define PHY_CORE_NUM_1 1 #define PHY_CORE_NUM_2 2 #define PHY_CORE_NUM_3 3 #define PHY_CORE_NUM_4 4 #define PHY_CORE_MAX PHY_CORE_NUM_4 #define PHY_CORE_0 0 #define PHY_CORE_1 1 #define PHY_CORE_2 2 #define PHY_CORE_3 3 #define MA_WINDOW_SZ 8 #define PHY_NOISE_SAMPLE_MON 1 #define PHY_NOISE_SAMPLE_EXTERNAL 2 #define PHY_NOISE_WINDOW_SZ 16 #define PHY_NOISE_GLITCH_INIT_MA 10 #define PHY_NOISE_GLITCH_INIT_MA_BADPlCP 10 #define PHY_NOISE_STATE_MON 0x1 #define PHY_NOISE_STATE_EXTERNAL 0x2 #define PHY_NOISE_SAMPLE_LOG_NUM_NPHY 10 #define PHY_NOISE_SAMPLE_LOG_NUM_UCODE 9 #define PHY_NOISE_OFFSETFACT_4322 (-103) #define PHY_NOISE_MA_WINDOW_SZ 2 #define PHY_RSSI_TABLE_SIZE 64 #define RSSI_ANT_MERGE_MAX 0 #define RSSI_ANT_MERGE_MIN 1 #define RSSI_ANT_MERGE_AVG 2 #define PHY_TSSI_TABLE_SIZE 64 #define APHY_TSSI_TABLE_SIZE 256 #define TX_GAIN_TABLE_LENGTH 64 #define DEFAULT_11A_TXP_IDX 24 #define NUM_TSSI_FRAMES 4 #define NULL_TSSI 0x7f #define NULL_TSSI_W 0x7f7f #define PHY_PAPD_EPS_TBL_SIZE_LCNPHY 64 #define LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL 9 #define PHY_TXPWR_MIN 10 #define PHY_TXPWR_MIN_NPHY 8 #define RADIOPWR_OVERRIDE_DEF (-1) #define PWRTBL_NUM_COEFF 3 #define SPURAVOID_DISABLE 0 #define SPURAVOID_AUTO 1 #define SPURAVOID_FORCEON 2 #define SPURAVOID_FORCEON2 3 #define PHY_SW_TIMER_FAST 15 #define PHY_SW_TIMER_SLOW 60 #define PHY_SW_TIMER_GLACIAL 120 #define PHY_PERICAL_AUTO 0 #define PHY_PERICAL_FULL 1 #define PHY_PERICAL_PARTIAL 2 #define PHY_PERICAL_NODELAY 0 #define PHY_PERICAL_INIT_DELAY 5 #define PHY_PERICAL_ASSOC_DELAY 5 #define PHY_PERICAL_WDOG_DELAY 5 #define MPHASE_TXCAL_NUMCMDS 2 #define PHY_PERICAL_MPHASE_PENDING(pi) \ (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_IDLE) enum { MPHASE_CAL_STATE_IDLE = 0, MPHASE_CAL_STATE_INIT = 1, MPHASE_CAL_STATE_TXPHASE0, MPHASE_CAL_STATE_TXPHASE1, MPHASE_CAL_STATE_TXPHASE2, MPHASE_CAL_STATE_TXPHASE3, MPHASE_CAL_STATE_TXPHASE4, MPHASE_CAL_STATE_TXPHASE5, MPHASE_CAL_STATE_PAPDCAL, MPHASE_CAL_STATE_RXCAL, MPHASE_CAL_STATE_RSSICAL, MPHASE_CAL_STATE_IDLETSSI }; enum phy_cal_mode { CAL_FULL, CAL_RECAL, CAL_CURRECAL, CAL_DIGCAL, CAL_GCTRL, CAL_SOFT, CAL_DIGLO }; #define RDR_NTIERS 1 #define RDR_TIER_SIZE 64 #define RDR_LIST_SIZE (512/3) #define RDR_EPOCH_SIZE 40 #define RDR_NANTENNAS 2 #define RDR_NTIER_SIZE RDR_LIST_SIZE #define RDR_LP_BUFFER_SIZE 64 #define LP_LEN_HIS_SIZE 10 #define STATIC_NUM_RF 32 #define STATIC_NUM_BB 9 #define BB_MULT_MASK 0x0000ffff #define BB_MULT_VALID_MASK 0x80000000 #define CORDIC_AG 39797 #define CORDIC_NI 18 #define FIXED(X) ((s32)((X) << 16)) #define FLOAT(X) \ (((X) >= 0) ? ((((X) >> 15) + 1) >> 1) : -((((-(X)) >> 15) + 1) >> 1)) #define PHY_CHAIN_TX_DISABLE_TEMP 115 #define PHY_HYSTERESIS_DELTATEMP 5 #define SCAN_INPROG_PHY(pi) \ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN)) #define PLT_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_PLT)) #define ASSOC_INPROG_PHY(pi) \ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_ASSOC)) #define SCAN_RM_IN_PROGRESS(pi) \ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN | PHY_HOLD_FOR_RM)) #define PHY_MUTED(pi) \ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_MUTE)) #define PUB_NOT_ASSOC(pi) \ (mboolisset(pi->measure_hold, PHY_HOLD_FOR_NOT_ASSOC)) struct phy_table_info { uint table; int q; uint max; }; struct phytbl_info { const void *tbl_ptr; u32 tbl_len; u32 tbl_id; u32 tbl_offset; u32 tbl_width; }; struct interference_info { u8 curr_home_channel; u16 crsminpwrthld_40_stored; u16 crsminpwrthld_20L_stored; u16 crsminpwrthld_20U_stored; u16 init_gain_code_core1_stored; u16 init_gain_code_core2_stored; u16 init_gain_codeb_core1_stored; u16 init_gain_codeb_core2_stored; u16 init_gain_table_stored[4]; u16 clip1_hi_gain_code_core1_stored; u16 clip1_hi_gain_code_core2_stored; u16 clip1_hi_gain_codeb_core1_stored; u16 clip1_hi_gain_codeb_core2_stored; u16 nb_clip_thresh_core1_stored; u16 nb_clip_thresh_core2_stored; u16 init_ofdmlna2gainchange_stored[4]; u16 init_ccklna2gainchange_stored[4]; u16 clip1_lo_gain_code_core1_stored; u16 clip1_lo_gain_code_core2_stored; u16 clip1_lo_gain_codeb_core1_stored; u16 clip1_lo_gain_codeb_core2_stored; u16 w1_clip_thresh_core1_stored; u16 w1_clip_thresh_core2_stored; u16 radio_2056_core1_rssi_gain_stored; u16 radio_2056_core2_rssi_gain_stored; u16 energy_drop_timeout_len_stored; u16 ed_crs40_assertthld0_stored; u16 ed_crs40_assertthld1_stored; u16 ed_crs40_deassertthld0_stored; u16 ed_crs40_deassertthld1_stored; u16 ed_crs20L_assertthld0_stored; u16 ed_crs20L_assertthld1_stored; u16 ed_crs20L_deassertthld0_stored; u16 ed_crs20L_deassertthld1_stored; u16 ed_crs20U_assertthld0_stored; u16 ed_crs20U_assertthld1_stored; u16 ed_crs20U_deassertthld0_stored; u16 ed_crs20U_deassertthld1_stored; u16 badplcp_ma; u16 badplcp_ma_previous; u16 badplcp_ma_total; u16 badplcp_ma_list[MA_WINDOW_SZ]; int badplcp_ma_index; s16 pre_badplcp_cnt; s16 bphy_pre_badplcp_cnt; u16 init_gain_core1; u16 init_gain_core2; u16 init_gainb_core1; u16 init_gainb_core2; u16 init_gain_rfseq[4]; u16 crsminpwr0; u16 crsminpwrl0; u16 crsminpwru0; s16 crsminpwr_index; u16 radio_2057_core1_rssi_wb1a_gc_stored; u16 radio_2057_core2_rssi_wb1a_gc_stored; u16 radio_2057_core1_rssi_wb1g_gc_stored; u16 radio_2057_core2_rssi_wb1g_gc_stored; u16 radio_2057_core1_rssi_wb2_gc_stored; u16 radio_2057_core2_rssi_wb2_gc_stored; u16 radio_2057_core1_rssi_nb_gc_stored; u16 radio_2057_core2_rssi_nb_gc_stored; }; struct aci_save_gphy { u16 rc_cal_ovr; u16 phycrsth1; u16 phycrsth2; u16 init_n1p1_gain; u16 p1_p2_gain; u16 n1_n2_gain; u16 n1_p1_gain; u16 div_search_gain; u16 div_p1_p2_gain; u16 div_search_gn_change; u16 table_7_2; u16 table_7_3; u16 cckshbits_gnref; u16 clip_thresh; u16 clip2_thresh; u16 clip3_thresh; u16 clip_p2_thresh; u16 clip_pwdn_thresh; u16 clip_n1p1_thresh; u16 clip_n1_pwdn_thresh; u16 bbconfig; u16 cthr_sthr_shdin; u16 energy; u16 clip_p1_p2_thresh; u16 threshold; u16 reg15; u16 reg16; u16 reg17; u16 div_srch_idx; u16 div_srch_p1_p2; u16 div_srch_gn_back; u16 ant_dwell; u16 ant_wr_settle; }; struct lo_complex_abgphy_info { s8 i; s8 q; }; struct nphy_iq_comp { s16 a0; s16 b0; s16 a1; s16 b1; }; struct nphy_txpwrindex { s8 index; s8 index_internal; s8 index_internal_save; u16 AfectrlOverride; u16 AfeCtrlDacGain; u16 rad_gain; u8 bbmult; u16 iqcomp_a; u16 iqcomp_b; u16 locomp; }; struct txiqcal_cache { u16 txcal_coeffs_2G[8]; u16 txcal_radio_regs_2G[8]; struct nphy_iq_comp rxcal_coeffs_2G; u16 txcal_coeffs_5G[8]; u16 txcal_radio_regs_5G[8]; struct nphy_iq_comp rxcal_coeffs_5G; }; struct nphy_pwrctrl { s8 max_pwr_2g; s8 idle_targ_2g; s16 pwrdet_2g_a1; s16 pwrdet_2g_b0; s16 pwrdet_2g_b1; s8 max_pwr_5gm; s8 idle_targ_5gm; s8 max_pwr_5gh; s8 max_pwr_5gl; s16 pwrdet_5gm_a1; s16 pwrdet_5gm_b0; s16 pwrdet_5gm_b1; s16 pwrdet_5gl_a1; s16 pwrdet_5gl_b0; s16 pwrdet_5gl_b1; s16 pwrdet_5gh_a1; s16 pwrdet_5gh_b0; s16 pwrdet_5gh_b1; s8 idle_targ_5gl; s8 idle_targ_5gh; s8 idle_tssi_2g; s8 idle_tssi_5g; s8 idle_tssi; s16 a1; s16 b0; s16 b1; }; struct nphy_txgains { u16 txlpf[2]; u16 txgm[2]; u16 pga[2]; u16 pad[2]; u16 ipa[2]; }; #define PHY_NOISEVAR_BUFSIZE 10 struct nphy_noisevar_buf { int bufcount; int tone_id[PHY_NOISEVAR_BUFSIZE]; u32 noise_vars[PHY_NOISEVAR_BUFSIZE]; u32 min_noise_vars[PHY_NOISEVAR_BUFSIZE]; }; struct rssical_cache { u16 rssical_radio_regs_2G[2]; u16 rssical_phyregs_2G[12]; u16 rssical_radio_regs_5G[2]; u16 rssical_phyregs_5G[12]; }; struct lcnphy_cal_results { u16 txiqlocal_a; u16 txiqlocal_b; u16 txiqlocal_didq; u8 txiqlocal_ei0; u8 txiqlocal_eq0; u8 txiqlocal_fi0; u8 txiqlocal_fq0; u16 txiqlocal_bestcoeffs[11]; u16 txiqlocal_bestcoeffs_valid; u32 papd_eps_tbl[PHY_PAPD_EPS_TBL_SIZE_LCNPHY]; u16 analog_gain_ref; u16 lut_begin; u16 lut_end; u16 lut_step; u16 rxcompdbm; u16 papdctrl; u16 sslpnCalibClkEnCtrl; u16 rxiqcal_coeff_a0; u16 rxiqcal_coeff_b0; }; struct shared_phy { struct brcms_phy *phy_head; uint unit; struct si_pub *sih; struct phy_shim_info *physhim; uint corerev; u32 machwcap; bool up; bool clk; uint now; u16 vid; u16 did; uint chip; uint chiprev; uint chippkg; uint sromrev; uint boardtype; uint boardrev; u32 boardflags; u32 boardflags2; uint fast_timer; uint slow_timer; uint glacial_timer; u8 rx_antdiv; s8 phy_noise_window[MA_WINDOW_SZ]; uint phy_noise_index; u8 hw_phytxchain; u8 hw_phyrxchain; u8 phytxchain; u8 phyrxchain; u8 rssi_mode; bool _rifs_phy; }; struct brcms_phy_pub { uint phy_type; uint phy_rev; u8 phy_corenum; u16 radioid; u8 radiorev; u8 radiover; uint coreflags; uint ana_rev; bool abgphy_encore; }; struct phy_func_ptr { void (*init)(struct brcms_phy *); void (*calinit)(struct brcms_phy *); void (*chanset)(struct brcms_phy *, u16 chanspec); void (*txpwrrecalc)(struct brcms_phy *); int (*longtrn)(struct brcms_phy *, int); void (*txiqccget)(struct brcms_phy *, u16 *, u16 *); void (*txiqccset)(struct brcms_phy *, u16, u16); u16 (*txloccget)(struct brcms_phy *); void (*radioloftget)(struct brcms_phy *, u8 *, u8 *, u8 *, u8 *); void (*carrsuppr)(struct brcms_phy *); s32 (*rxsigpwr)(struct brcms_phy *, s32); void (*detach)(struct brcms_phy *); }; struct brcms_phy { struct brcms_phy_pub pubpi_ro; struct shared_phy *sh; struct phy_func_ptr pi_fptr; union { struct brcms_phy_lcnphy *pi_lcnphy; } u; bool user_txpwr_at_rfport; struct bcma_device *d11core; struct brcms_phy *next; struct brcms_phy_pub pubpi; bool do_initcal; bool phytest_on; bool ofdm_rateset_war; bool bf_preempt_4306; u16 radio_chanspec; u8 antsel_type; u16 bw; u8 txpwr_percent; bool phy_init_por; bool init_in_progress; bool initialized; bool sbtml_gm; uint refcnt; bool watchdog_override; u8 phynoise_state; uint phynoise_now; int phynoise_chan_watchdog; bool phynoise_polling; bool disable_percal; u32 measure_hold; s16 txpa_2g[PWRTBL_NUM_COEFF]; s16 txpa_2g_low_temp[PWRTBL_NUM_COEFF]; s16 txpa_2g_high_temp[PWRTBL_NUM_COEFF]; s16 txpa_5g_low[PWRTBL_NUM_COEFF]; s16 txpa_5g_mid[PWRTBL_NUM_COEFF]; s16 txpa_5g_hi[PWRTBL_NUM_COEFF]; u8 tx_srom_max_2g; u8 tx_srom_max_5g_low; u8 tx_srom_max_5g_mid; u8 tx_srom_max_5g_hi; u8 tx_srom_max_rate_2g[TXP_NUM_RATES]; u8 tx_srom_max_rate_5g_low[TXP_NUM_RATES]; u8 tx_srom_max_rate_5g_mid[TXP_NUM_RATES]; u8 tx_srom_max_rate_5g_hi[TXP_NUM_RATES]; u8 tx_user_target[TXP_NUM_RATES]; s8 tx_power_offset[TXP_NUM_RATES]; u8 tx_power_target[TXP_NUM_RATES]; struct brcms_phy_srom_fem srom_fem2g; struct brcms_phy_srom_fem srom_fem5g; u8 tx_power_max; u8 tx_power_max_rate_ind; bool hwpwrctrl; u8 nphy_txpwrctrl; s8 nphy_txrx_chain; bool phy_5g_pwrgain; u16 phy_wreg; u16 phy_wreg_limit; s8 n_preamble_override; u8 antswitch; u8 aa2g, aa5g; s8 idle_tssi[CH_5G_GROUP]; s8 target_idle_tssi; s8 txpwr_est_Pout; u8 tx_power_min; u8 txpwr_limit[TXP_NUM_RATES]; u8 txpwr_env_limit[TXP_NUM_RATES]; u8 adj_pwr_tbl_nphy[ADJ_PWR_TBL_LEN]; bool channel_14_wide_filter; bool txpwroverride; bool txpwridx_override_aphy; s16 radiopwr_override; u16 hwpwr_txcur; u8 saved_txpwr_idx; bool edcrs_threshold_lock; u32 tr_R_gain_val; u32 tr_T_gain_val; s16 ofdm_analog_filt_bw_override; s16 cck_analog_filt_bw_override; s16 ofdm_rccal_override; s16 cck_rccal_override; u16 extlna_type; uint interference_mode_crs_time; u16 crsglitch_prev; bool interference_mode_crs; u32 phy_tx_tone_freq; uint phy_lastcal; bool phy_forcecal; bool phy_fixed_noise; u32 xtalfreq; u8 pdiv; s8 carrier_suppr_disable; bool phy_bphy_evm; bool phy_bphy_rfcs; s8 phy_scraminit; u8 phy_gpiosel; s16 phy_txcore_disable_temp; s16 phy_txcore_enable_temp; s8 phy_tempsense_offset; bool phy_txcore_heatedup; u16 radiopwr; u16 bb_atten; u16 txctl1; u16 mintxbias; u16 mintxmag; struct lo_complex_abgphy_info gphy_locomp_iq [STATIC_NUM_RF][STATIC_NUM_BB]; s8 stats_11b_txpower[STATIC_NUM_RF][STATIC_NUM_BB]; u16 gain_table[TX_GAIN_TABLE_LENGTH]; bool loopback_gain; s16 max_lpback_gain_hdB; s16 trsw_rx_gain_hdB; u8 power_vec[8]; u16 rc_cal; int nrssi_table_delta; int nrssi_slope_scale; int nrssi_slope_offset; int min_rssi; int max_rssi; s8 txpwridx; u8 min_txpower; u8 a_band_high_disable; u16 tx_vos; u16 global_tx_bb_dc_bias_loft; int rf_max; int bb_max; int rf_list_size; int bb_list_size; u16 *rf_attn_list; u16 *bb_attn_list; u16 padmix_mask; u16 padmix_reg; u16 *txmag_list; uint txmag_len; bool txmag_enable; s8 *a_tssi_to_dbm; s8 *m_tssi_to_dbm; s8 *l_tssi_to_dbm; s8 *h_tssi_to_dbm; u8 *hwtxpwr; u16 freqtrack_saved_regs[2]; int cur_interference_mode; bool hwpwrctrl_capable; bool temppwrctrl_capable; uint phycal_nslope; uint phycal_noffset; uint phycal_mlo; uint phycal_txpower; u8 phy_aa2g; bool nphy_tableloaded; s8 nphy_rssisel; u32 nphy_bb_mult_save; u16 nphy_txiqlocal_bestc[11]; bool nphy_txiqlocal_coeffsvalid; struct nphy_txpwrindex nphy_txpwrindex[PHY_CORE_NUM_2]; struct nphy_pwrctrl nphy_pwrctrl_info[PHY_CORE_NUM_2]; u16 cck2gpo; u32 ofdm2gpo; u32 ofdm5gpo; u32 ofdm5glpo; u32 ofdm5ghpo; u8 bw402gpo; u8 bw405gpo; u8 bw405glpo; u8 bw405ghpo; u8 cdd2gpo; u8 cdd5gpo; u8 cdd5glpo; u8 cdd5ghpo; u8 stbc2gpo; u8 stbc5gpo; u8 stbc5glpo; u8 stbc5ghpo; u8 bwdup2gpo; u8 bwdup5gpo; u8 bwdup5glpo; u8 bwdup5ghpo; u16 mcs2gpo[8]; u16 mcs5gpo[8]; u16 mcs5glpo[8]; u16 mcs5ghpo[8]; u32 nphy_rxcalparams; u8 phy_spuravoid; bool phy_isspuravoid; u8 phy_pabias; u8 nphy_papd_skip; u8 nphy_tssi_slope; s16 nphy_noise_win[PHY_CORE_MAX][PHY_NOISE_WINDOW_SZ]; u8 nphy_noise_index; bool nphy_gain_boost; bool nphy_elna_gain_config; u16 old_bphy_test; u16 old_bphy_testcontrol; bool phyhang_avoid; bool rssical_nphy; u8 nphy_perical; uint nphy_perical_last; u8 cal_type_override; u8 mphase_cal_phase_id; u8 mphase_txcal_cmdidx; u8 mphase_txcal_numcmds; u16 mphase_txcal_bestcoeffs[11]; u16 nphy_txiqlocal_chanspec; u16 nphy_iqcal_chanspec_2G; u16 nphy_iqcal_chanspec_5G; u16 nphy_rssical_chanspec_2G; u16 nphy_rssical_chanspec_5G; struct wlapi_timer *phycal_timer; bool use_int_tx_iqlo_cal_nphy; bool internal_tx_iqlo_cal_tapoff_intpa_nphy; s16 nphy_lastcal_temp; struct txiqcal_cache calibration_cache; struct rssical_cache rssical_cache; u8 nphy_txpwr_idx[2]; u8 nphy_papd_cal_type; uint nphy_papd_last_cal; u16 nphy_papd_tx_gain_at_last_cal[2]; u8 nphy_papd_cal_gain_index[2]; s16 nphy_papd_epsilon_offset[2]; bool nphy_papd_recal_enable; u32 nphy_papd_recal_counter; bool nphy_force_papd_cal; bool nphy_papdcomp; bool ipa2g_on; bool ipa5g_on; u16 classifier_state; u16 clip_state[2]; uint nphy_deaf_count; u8 rxiq_samps; u8 rxiq_antsel; u16 rfctrlIntc1_save; u16 rfctrlIntc2_save; bool first_cal_after_assoc; u16 tx_rx_cal_radio_saveregs[22]; u16 tx_rx_cal_phy_saveregs[15]; u8 nphy_cal_orig_pwr_idx[2]; u8 nphy_txcal_pwr_idx[2]; u8 nphy_rxcal_pwr_idx[2]; u16 nphy_cal_orig_tx_gain[2]; struct nphy_txgains nphy_cal_target_gain; u16 nphy_txcal_bbmult; u16 nphy_gmval; u16 nphy_saved_bbconf; bool nphy_gband_spurwar_en; bool nphy_gband_spurwar2_en; bool nphy_aband_spurwar_en; u16 nphy_rccal_value; u16 nphy_crsminpwr[3]; struct nphy_noisevar_buf nphy_saved_noisevars; bool nphy_anarxlpf_adjusted; bool nphy_crsminpwr_adjusted; bool nphy_noisevars_adjusted; bool nphy_rxcal_active; u16 radar_percal_mask; bool dfs_lp_buffer_nphy; u16 nphy_fineclockgatecontrol; s8 rx2tx_biasentry; u16 crsminpwr0; u16 crsminpwrl0; u16 crsminpwru0; s16 noise_crsminpwr_index; u16 init_gain_core1; u16 init_gain_core2; u16 init_gainb_core1; u16 init_gainb_core2; u8 aci_noise_curr_channel; u16 init_gain_rfseq[4]; bool radio_is_on; bool nphy_sample_play_lpf_bw_ctl_ovr; u16 tbl_data_hi; u16 tbl_data_lo; u16 tbl_addr; uint tbl_save_id; uint tbl_save_offset; u8 txpwrctrl; s8 txpwrindex[PHY_CORE_MAX]; u8 phycal_tempdelta; u32 mcs20_po; u32 mcs40_po; struct wiphy *wiphy; }; struct cs32 { s32 q; s32 i; }; struct radio_regs { u16 address; u32 init_a; u32 init_g; u8 do_init_a; u8 do_init_g; }; struct radio_20xx_regs { u16 address; u8 init; u8 do_init; }; struct lcnphy_radio_regs { u16 address; u8 init_a; u8 init_g; u8 do_init_a; u8 do_init_g; }; extern u16 read_phy_reg(struct brcms_phy *pi, u16 addr); extern void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); extern void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); extern void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); extern void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); extern u16 read_radio_reg(struct brcms_phy *pi, u16 addr); extern void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); extern void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); extern void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); extern void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask); extern void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); extern void wlc_phyreg_enter(struct brcms_phy_pub *pih); extern void wlc_phyreg_exit(struct brcms_phy_pub *pih); extern void wlc_radioreg_enter(struct brcms_phy_pub *pih); extern void wlc_radioreg_exit(struct brcms_phy_pub *pih); extern void wlc_phy_read_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, u16 tblAddr, u16 tblDataHi, u16 tblDatalo); extern void wlc_phy_write_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, u16 tblAddr, u16 tblDataHi, u16 tblDatalo); extern void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, u16 tblAddr, u16 tblDataHi, u16 tblDataLo); extern void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val); extern void write_phy_channel_reg(struct brcms_phy *pi, uint val); extern void wlc_phy_txpower_update_shm(struct brcms_phy *pi); extern u8 wlc_phy_nbits(s32 value); extern void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core); extern uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, struct radio_20xx_regs *radioregs); extern uint wlc_phy_init_radio_regs(struct brcms_phy *pi, const struct radio_regs *radioregs, u16 core_offset); extern void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi); extern void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on); extern void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag); extern void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi); extern void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi); extern bool wlc_phy_attach_nphy(struct brcms_phy *pi); extern bool wlc_phy_attach_lcnphy(struct brcms_phy *pi); extern void wlc_phy_detach_lcnphy(struct brcms_phy *pi); extern void wlc_phy_init_nphy(struct brcms_phy *pi); extern void wlc_phy_init_lcnphy(struct brcms_phy *pi); extern void wlc_phy_cal_init_nphy(struct brcms_phy *pi); extern void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi); extern void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec); extern void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec); extern void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, u16 chanspec); extern int wlc_phy_channel2freq(uint channel); extern int wlc_phy_chanspec_freq2bandrange_lpssn(uint); extern int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec); extern void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode); extern s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi); extern void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi); extern void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi); extern void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi); extern void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index); extern void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable); extern void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi); extern void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, bool iqcalmode); extern void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, u8 *max_pwr, u8 rate_id); extern void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, u8 rate_mcs_end, u8 rate_ofdm_start); extern void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, u8 rate_ofdm_end, u8 rate_mcs_start); extern u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode); extern s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode); extern s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode); extern s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode); extern void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi); extern void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel); extern void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode); extern void wlc_2064_vco_cal(struct brcms_phy *pi); extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); #define LCNPHY_TBL_ID_PAPDCOMPDELTATBL 0x18 #define LCNPHY_TX_POWER_TABLE_SIZE 128 #define LCNPHY_MAX_TX_POWER_INDEX (LCNPHY_TX_POWER_TABLE_SIZE - 1) #define LCNPHY_TBL_ID_TXPWRCTL 0x07 #define LCNPHY_TX_PWR_CTRL_OFF 0 #define LCNPHY_TX_PWR_CTRL_SW (0x1 << 15) #define LCNPHY_TX_PWR_CTRL_HW ((0x1 << 15) | \ (0x1 << 14) | \ (0x1 << 13)) #define LCNPHY_TX_PWR_CTRL_TEMPBASED 0xE001 extern void wlc_lcnphy_write_table(struct brcms_phy *pi, const struct phytbl_info *pti); extern void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti); extern void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b); extern void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq); extern void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b); extern u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi); extern void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, u8 *eq0, u8 *fi0, u8 *fq0); extern void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode); extern void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode); extern bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi); extern void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi); extern s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1); extern void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr); extern void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi); extern s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index); #define NPHY_MAX_HPVGA1_INDEX 10 #define NPHY_DEF_HPVGA1_INDEXLIMIT 7 struct phy_iq_est { s32 iq_prod; u32 i_pwr; u32 q_pwr; }; extern void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable); extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); #define wlc_phy_write_table_nphy(pi, pti) \ wlc_phy_write_table(pi, pti, 0x72, 0x74, 0x73) #define wlc_phy_read_table_nphy(pi, pti) \ wlc_phy_read_table(pi, pti, 0x72, 0x74, 0x73) #define wlc_nphy_table_addr(pi, id, off) \ wlc_phy_table_addr((pi), (id), (off), 0x72, 0x74, 0x73) #define wlc_nphy_table_data_write(pi, w, v) \ wlc_phy_table_data_write((pi), (w), (v)) extern void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, u32 w, void *d); extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, u32, const void *); #define PHY_IPA(pi) \ ((pi->ipa2g_on && CHSPEC_IS2G(pi->radio_chanspec)) || \ (pi->ipa5g_on && CHSPEC_IS5G(pi->radio_chanspec))) #define BRCMS_PHY_WAR_PR51571(pi) \ if (NREV_LT((pi)->pubpi.phy_rev, 3)) \ (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) extern void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype); extern void wlc_phy_aci_reset_nphy(struct brcms_phy *pi); extern void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en); extern u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan); extern void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on); extern void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi); extern void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd); extern s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi); extern u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val); extern void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, u16 num_samps, u8 wait_time, u8 wait_for_crs); extern void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, struct nphy_iq_comp *comp); extern void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi); extern void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask); extern u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih); extern void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type); extern void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi); extern void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi); extern void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi); extern u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi); extern struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi); extern int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, bool full, bool m); extern int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, u8 type, bool d); extern void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, s8 txpwrindex, bool res); extern void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type); extern int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, s32 *rssi_buf, u8 nsamps); extern void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi); extern int wlc_phy_aci_scan_nphy(struct brcms_phy *pi); extern void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, bool debug); extern int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 mode, u8, bool); extern void wlc_phy_stopplayback_nphy(struct brcms_phy *pi); extern void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, u8 num_samps); extern void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi); extern int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh); #define NPHY_TESTPATTERN_BPHY_EVM 0 #define NPHY_TESTPATTERN_BPHY_RFCS 1 extern void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs); void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, s8 *ofdmoffset); extern s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec); extern bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih); #endif /* _BRCM_PHY_INT_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h0000644000175000017500000002511312026211315026166 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * phy_hal.h: functionality exported from the phy to higher layers */ #ifndef _BRCM_PHY_HAL_H_ #define _BRCM_PHY_HAL_H_ #include #include #include #define IDCODE_VER_MASK 0x0000000f #define IDCODE_VER_SHIFT 0 #define IDCODE_MFG_MASK 0x00000fff #define IDCODE_MFG_SHIFT 0 #define IDCODE_ID_MASK 0x0ffff000 #define IDCODE_ID_SHIFT 12 #define IDCODE_REV_MASK 0xf0000000 #define IDCODE_REV_SHIFT 28 #define NORADIO_ID 0xe4f5 #define NORADIO_IDCODE 0x4e4f5246 #define BCM2055_ID 0x2055 #define BCM2055_IDCODE 0x02055000 #define BCM2055A0_IDCODE 0x1205517f #define BCM2056_ID 0x2056 #define BCM2056_IDCODE 0x02056000 #define BCM2056A0_IDCODE 0x1205617f #define BCM2057_ID 0x2057 #define BCM2057_IDCODE 0x02057000 #define BCM2057A0_IDCODE 0x1205717f #define BCM2064_ID 0x2064 #define BCM2064_IDCODE 0x02064000 #define BCM2064A0_IDCODE 0x0206417f #define PHY_TPC_HW_OFF false #define PHY_TPC_HW_ON true #define PHY_PERICAL_DRIVERUP 1 #define PHY_PERICAL_WATCHDOG 2 #define PHY_PERICAL_PHYINIT 3 #define PHY_PERICAL_JOIN_BSS 4 #define PHY_PERICAL_START_IBSS 5 #define PHY_PERICAL_UP_BSS 6 #define PHY_PERICAL_CHAN 7 #define PHY_FULLCAL 8 #define PHY_PERICAL_DISABLE 0 #define PHY_PERICAL_SPHASE 1 #define PHY_PERICAL_MPHASE 2 #define PHY_PERICAL_MANUAL 3 #define PHY_HOLD_FOR_ASSOC 1 #define PHY_HOLD_FOR_SCAN 2 #define PHY_HOLD_FOR_RM 4 #define PHY_HOLD_FOR_PLT 8 #define PHY_HOLD_FOR_MUTE 16 #define PHY_HOLD_FOR_NOT_ASSOC 0x20 #define PHY_MUTE_FOR_PREISM 1 #define PHY_MUTE_ALL 0xffffffff #define PHY_NOISE_FIXED_VAL (-95) #define PHY_NOISE_FIXED_VAL_NPHY (-92) #define PHY_NOISE_FIXED_VAL_LCNPHY (-92) #define PHY_MODE_CAL 0x0002 #define PHY_MODE_NOISEM 0x0004 #define BRCMS_TXPWR_DB_FACTOR 4 /* a large TX Power as an init value to factor out of min() calculations, * keep low enough to fit in an s8, units are .25 dBm */ #define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ #define BRCMS_NUM_RATES_CCK 4 #define BRCMS_NUM_RATES_OFDM 8 #define BRCMS_NUM_RATES_MCS_1_STREAM 8 #define BRCMS_NUM_RATES_MCS_2_STREAM 8 #define BRCMS_NUM_RATES_MCS_3_STREAM 8 #define BRCMS_NUM_RATES_MCS_4_STREAM 8 #define BRCMS_RSSI_INVALID 0 /* invalid RSSI value */ struct d11regs; struct phy_shim_info; struct txpwr_limits { u8 cck[BRCMS_NUM_RATES_CCK]; u8 ofdm[BRCMS_NUM_RATES_OFDM]; u8 ofdm_cdd[BRCMS_NUM_RATES_OFDM]; u8 ofdm_40_siso[BRCMS_NUM_RATES_OFDM]; u8 ofdm_40_cdd[BRCMS_NUM_RATES_OFDM]; u8 mcs_20_siso[BRCMS_NUM_RATES_MCS_1_STREAM]; u8 mcs_20_cdd[BRCMS_NUM_RATES_MCS_1_STREAM]; u8 mcs_20_stbc[BRCMS_NUM_RATES_MCS_1_STREAM]; u8 mcs_20_mimo[BRCMS_NUM_RATES_MCS_2_STREAM]; u8 mcs_40_siso[BRCMS_NUM_RATES_MCS_1_STREAM]; u8 mcs_40_cdd[BRCMS_NUM_RATES_MCS_1_STREAM]; u8 mcs_40_stbc[BRCMS_NUM_RATES_MCS_1_STREAM]; u8 mcs_40_mimo[BRCMS_NUM_RATES_MCS_2_STREAM]; u8 mcs32; }; struct tx_power { u32 flags; u16 chanspec; /* txpwr report for this channel */ u16 local_chanspec; /* channel on which we are associated */ u8 local_max; /* local max according to the AP */ u8 local_constraint; /* local constraint according to the AP */ s8 antgain[2]; /* Ant gain for each band - from SROM */ u8 rf_cores; /* count of RF Cores being reported */ u8 est_Pout[4]; /* Latest tx power out estimate per RF chain */ u8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain * without adjustment */ u8 est_Pout_cck; /* Latest CCK tx power out estimate */ u8 tx_power_max[4]; /* Maximum target power among all rates */ /* Index of the rate with the max target power */ u8 tx_power_max_rate_ind[4]; /* User limit */ u8 user_limit[WL_TX_POWER_RATES]; /* Regulatory power limit */ u8 reg_limit[WL_TX_POWER_RATES]; /* Max power board can support (SROM) */ u8 board_limit[WL_TX_POWER_RATES]; /* Latest target power */ u8 target[WL_TX_POWER_RATES]; }; struct tx_inst_power { u8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */ u8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */ }; struct brcms_chanvec { u8 vec[MAXCHANNEL / NBBY]; }; struct shared_phy_params { struct si_pub *sih; struct phy_shim_info *physhim; uint unit; uint corerev; u16 vid; u16 did; uint chip; uint chiprev; uint chippkg; uint sromrev; uint boardtype; uint boardrev; u32 boardflags; u32 boardflags2; }; extern struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp); extern struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh, struct bcma_device *d11core, int bandtype, struct wiphy *wiphy); extern void wlc_phy_detach(struct brcms_phy_pub *ppi); extern bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, u16 *phyrev, u16 *radioid, u16 *radiover); extern bool wlc_phy_get_encore(struct brcms_phy_pub *pih); extern u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih); extern void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate); extern void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate); extern void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec); extern void wlc_phy_watchdog(struct brcms_phy_pub *ppi); extern int wlc_phy_down(struct brcms_phy_pub *ppi); extern u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih); extern void wlc_phy_cal_init(struct brcms_phy_pub *ppi); extern void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init); extern void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec); extern u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi); extern void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch); extern u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi); extern void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw); extern int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, struct d11rxhdr *rxh); extern void wlc_phy_por_inform(struct brcms_phy_pub *ppi); extern void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi); extern bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi); extern void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag); extern void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on); extern void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on); extern void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi); extern void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, bool wide_filter); extern void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, struct brcms_chanvec *channels); extern u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band); extern void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, u8 *_min_, u8 *_max_, int rate); extern void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, u8 *_max_, u8 *_min_); extern void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint band, s32 *, s32 *, u32 *); extern void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *, u16 chanspec); extern int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override); extern int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override); extern void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, struct txpwr_limits *); extern bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi); extern void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl); extern u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi); extern u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi); extern bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih); extern void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); extern void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); extern void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain); extern u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih); extern s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec); extern void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val); extern void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason); extern void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi); extern void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock); extern void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi); extern void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val); extern void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi); extern void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val); extern void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags); extern void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type); extern void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, struct tx_power *power, uint channel); extern void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal); extern bool wlc_phy_test_ison(struct brcms_phy_pub *ppi); extern void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent); extern void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war); extern void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt); extern void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap); extern void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end); extern void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi); extern void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi); extern const u8 *wlc_phy_get_ofdm_rate_lookup(void); extern s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi, u8 mcs_offset); extern s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset); #endif /* _BRCM_PHY_HAL_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c0000644000175000017500000021437712026211315026206 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "phy_hal.h" #include "phy_int.h" #include "phy_radio.h" #include "phy_lcn.h" #include "phyreg_n.h" #define VALID_N_RADIO(radioid) ((radioid == BCM2055_ID) || \ (radioid == BCM2056_ID) || \ (radioid == BCM2057_ID)) #define VALID_LCN_RADIO(radioid) (radioid == BCM2064_ID) #define VALID_RADIO(pi, radioid) ( \ (ISNPHY(pi) ? VALID_N_RADIO(radioid) : false) || \ (ISLCNPHY(pi) ? VALID_LCN_RADIO(radioid) : false)) /* basic mux operation - can be optimized on several architectures */ #define MUX(pred, true, false) ((pred) ? (true) : (false)) /* modulo inc/dec - assumes x E [0, bound - 1] */ #define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1) /* modulo inc/dec, bound = 2^k */ #define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1)) #define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1)) struct chan_info_basic { u16 chan; u16 freq; }; static const struct chan_info_basic chan_info_all[] = { {1, 2412}, {2, 2417}, {3, 2422}, {4, 2427}, {5, 2432}, {6, 2437}, {7, 2442}, {8, 2447}, {9, 2452}, {10, 2457}, {11, 2462}, {12, 2467}, {13, 2472}, {14, 2484}, {34, 5170}, {38, 5190}, {42, 5210}, {46, 5230}, {36, 5180}, {40, 5200}, {44, 5220}, {48, 5240}, {52, 5260}, {56, 5280}, {60, 5300}, {64, 5320}, {100, 5500}, {104, 5520}, {108, 5540}, {112, 5560}, {116, 5580}, {120, 5600}, {124, 5620}, {128, 5640}, {132, 5660}, {136, 5680}, {140, 5700}, {149, 5745}, {153, 5765}, {157, 5785}, {161, 5805}, {165, 5825}, {184, 4920}, {188, 4940}, {192, 4960}, {196, 4980}, {200, 5000}, {204, 5020}, {208, 5040}, {212, 5060}, {216, 5080} }; static const u8 ofdm_rate_lookup[] = { BRCM_RATE_48M, BRCM_RATE_24M, BRCM_RATE_12M, BRCM_RATE_6M, BRCM_RATE_54M, BRCM_RATE_36M, BRCM_RATE_18M, BRCM_RATE_9M }; #define PHY_WREG_LIMIT 24 void wlc_phyreg_enter(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; wlapi_bmac_ucode_wake_override_phyreg_set(pi->sh->physhim); } void wlc_phyreg_exit(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; wlapi_bmac_ucode_wake_override_phyreg_clear(pi->sh->physhim); } void wlc_radioreg_enter(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, MCTL_LOCK_RADIO); udelay(10); } void wlc_radioreg_exit(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; (void)bcma_read16(pi->d11core, D11REGOFFS(phyversion)); pi->phy_wreg = 0; wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, 0); } u16 read_radio_reg(struct brcms_phy *pi, u16 addr) { u16 data; if ((addr == RADIO_IDCODE)) return 0xffff; switch (pi->pubpi.phy_type) { case PHY_TYPE_N: if (!CONF_HAS(PHYTYPE, PHY_TYPE_N)) break; if (NREV_GE(pi->pubpi.phy_rev, 7)) addr |= RADIO_2057_READ_OFF; else addr |= RADIO_2055_READ_OFF; break; case PHY_TYPE_LCN: if (!CONF_HAS(PHYTYPE, PHY_TYPE_LCN)) break; addr |= RADIO_2064_READ_OFF; break; default: break; } if ((D11REV_GE(pi->sh->corerev, 24)) || (D11REV_IS(pi->sh->corerev, 22) && (pi->pubpi.phy_type != PHY_TYPE_SSN))) { bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), addr); data = bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); } else { bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), addr); data = bcma_read16(pi->d11core, D11REGOFFS(phy4wdatalo)); } pi->phy_wreg = 0; return data; } void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) { struct si_info *sii = container_of(pi->sh->sih, struct si_info, pub); if ((D11REV_GE(pi->sh->corerev, 24)) || (D11REV_IS(pi->sh->corerev, 22) && (pi->pubpi.phy_type != PHY_TYPE_SSN))) { bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), addr); bcma_write16(pi->d11core, D11REGOFFS(radioregdata), val); } else { bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), addr); bcma_write16(pi->d11core, D11REGOFFS(phy4wdatalo), val); } if ((sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) && (++pi->phy_wreg >= pi->phy_wreg_limit)) { (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); pi->phy_wreg = 0; } } static u32 read_radio_id(struct brcms_phy *pi) { u32 id; if (D11REV_GE(pi->sh->corerev, 24)) { u32 b0, b1, b2; bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 0); b0 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 1); b1 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 2); b2 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); id = ((b0 & 0xf) << 28) | (((b2 << 8) | b1) << 12) | ((b0 >> 4) & 0xf); } else { bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), RADIO_IDCODE); id = (u32) bcma_read16(pi->d11core, D11REGOFFS(phy4wdatalo)); id |= (u32) bcma_read16(pi->d11core, D11REGOFFS(phy4wdatahi)) << 16; } pi->phy_wreg = 0; return id; } void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) { u16 rval; rval = read_radio_reg(pi, addr); write_radio_reg(pi, addr, (rval & val)); } void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) { u16 rval; rval = read_radio_reg(pi, addr); write_radio_reg(pi, addr, (rval | val)); } void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask) { u16 rval; rval = read_radio_reg(pi, addr); write_radio_reg(pi, addr, (rval ^ mask)); } void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val) { u16 rval; rval = read_radio_reg(pi, addr); write_radio_reg(pi, addr, (rval & ~mask) | (val & mask)); } void write_phy_channel_reg(struct brcms_phy *pi, uint val) { bcma_write16(pi->d11core, D11REGOFFS(phychannel), val); } u16 read_phy_reg(struct brcms_phy *pi, u16 addr) { bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); pi->phy_wreg = 0; return bcma_read16(pi->d11core, D11REGOFFS(phyregdata)); } void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) { #ifdef CONFIG_BCM47XX bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); bcma_write16(pi->d11core, D11REGOFFS(phyregdata), val); if (addr == 0x72) (void)bcma_read16(pi->d11core, D11REGOFFS(phyregdata)); #else struct si_info *sii = container_of(pi->sh->sih, struct si_info, pub); bcma_write32(pi->d11core, D11REGOFFS(phyregaddr), addr | (val << 16)); if ((sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) && (++pi->phy_wreg >= pi->phy_wreg_limit)) { pi->phy_wreg = 0; (void)bcma_read16(pi->d11core, D11REGOFFS(phyversion)); } #endif } void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) { bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); bcma_mask16(pi->d11core, D11REGOFFS(phyregdata), val); pi->phy_wreg = 0; } void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) { bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); bcma_set16(pi->d11core, D11REGOFFS(phyregdata), val); pi->phy_wreg = 0; } void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val) { val &= mask; bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); bcma_maskset16(pi->d11core, D11REGOFFS(phyregdata), ~mask, val); pi->phy_wreg = 0; } static void wlc_set_phy_uninitted(struct brcms_phy *pi) { int i, j; pi->initialized = false; pi->tx_vos = 0xffff; pi->nrssi_table_delta = 0x7fffffff; pi->rc_cal = 0xffff; pi->mintxbias = 0xffff; pi->txpwridx = -1; if (ISNPHY(pi)) { pi->phy_spuravoid = SPURAVOID_DISABLE; if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) pi->phy_spuravoid = SPURAVOID_AUTO; pi->nphy_papd_skip = 0; pi->nphy_papd_epsilon_offset[0] = 0xf588; pi->nphy_papd_epsilon_offset[1] = 0xf588; pi->nphy_txpwr_idx[0] = 128; pi->nphy_txpwr_idx[1] = 128; pi->nphy_txpwrindex[0].index_internal = 40; pi->nphy_txpwrindex[1].index_internal = 40; pi->phy_pabias = 0; } else { pi->phy_spuravoid = SPURAVOID_AUTO; } pi->radiopwr = 0xffff; for (i = 0; i < STATIC_NUM_RF; i++) { for (j = 0; j < STATIC_NUM_BB; j++) pi->stats_11b_txpower[i][j] = -1; } } struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp) { struct shared_phy *sh; sh = kzalloc(sizeof(struct shared_phy), GFP_ATOMIC); if (sh == NULL) return NULL; sh->sih = shp->sih; sh->physhim = shp->physhim; sh->unit = shp->unit; sh->corerev = shp->corerev; sh->vid = shp->vid; sh->did = shp->did; sh->chip = shp->chip; sh->chiprev = shp->chiprev; sh->chippkg = shp->chippkg; sh->sromrev = shp->sromrev; sh->boardtype = shp->boardtype; sh->boardrev = shp->boardrev; sh->boardflags = shp->boardflags; sh->boardflags2 = shp->boardflags2; sh->fast_timer = PHY_SW_TIMER_FAST; sh->slow_timer = PHY_SW_TIMER_SLOW; sh->glacial_timer = PHY_SW_TIMER_GLACIAL; sh->rssi_mode = RSSI_ANT_MERGE_MAX; return sh; } static void wlc_phy_timercb_phycal(struct brcms_phy *pi) { uint delay = 5; if (PHY_PERICAL_MPHASE_PENDING(pi)) { if (!pi->sh->up) { wlc_phy_cal_perical_mphase_reset(pi); return; } if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)) { delay = 1000; wlc_phy_cal_perical_mphase_restart(pi); } else wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_AUTO); wlapi_add_timer(pi->phycal_timer, delay, 0); return; } } static u32 wlc_phy_get_radio_ver(struct brcms_phy *pi) { u32 ver; ver = read_radio_id(pi); return ver; } struct brcms_phy_pub * wlc_phy_attach(struct shared_phy *sh, struct bcma_device *d11core, int bandtype, struct wiphy *wiphy) { struct brcms_phy *pi; u32 sflags = 0; uint phyversion; u32 idcode; int i; if (D11REV_IS(sh->corerev, 4)) sflags = SISF_2G_PHY | SISF_5G_PHY; else sflags = bcma_aread32(d11core, BCMA_IOST); if (bandtype == BRCM_BAND_5G) { if ((sflags & (SISF_5G_PHY | SISF_DB_PHY)) == 0) return NULL; } pi = sh->phy_head; if ((sflags & SISF_DB_PHY) && pi) { wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags); pi->refcnt++; return &pi->pubpi_ro; } pi = kzalloc(sizeof(struct brcms_phy), GFP_ATOMIC); if (pi == NULL) return NULL; pi->wiphy = wiphy; pi->d11core = d11core; pi->sh = sh; pi->phy_init_por = true; pi->phy_wreg_limit = PHY_WREG_LIMIT; pi->txpwr_percent = 100; pi->do_initcal = true; pi->phycal_tempdelta = 0; if (bandtype == BRCM_BAND_2G && (sflags & SISF_2G_PHY)) pi->pubpi.coreflags = SICF_GMODE; wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags); phyversion = bcma_read16(pi->d11core, D11REGOFFS(phyversion)); pi->pubpi.phy_type = PHY_TYPE(phyversion); pi->pubpi.phy_rev = phyversion & PV_PV_MASK; if (pi->pubpi.phy_type == PHY_TYPE_LCNXN) { pi->pubpi.phy_type = PHY_TYPE_N; pi->pubpi.phy_rev += LCNXN_BASEREV; } pi->pubpi.phy_corenum = PHY_CORE_NUM_2; pi->pubpi.ana_rev = (phyversion & PV_AV_MASK) >> PV_AV_SHIFT; if (pi->pubpi.phy_type != PHY_TYPE_N && pi->pubpi.phy_type != PHY_TYPE_LCN) goto err; if (bandtype == BRCM_BAND_5G) { if (!ISNPHY(pi)) goto err; } else if (!ISNPHY(pi) && !ISLCNPHY(pi)) { goto err; } wlc_phy_anacore((struct brcms_phy_pub *) pi, ON); idcode = wlc_phy_get_radio_ver(pi); pi->pubpi.radioid = (idcode & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT; pi->pubpi.radiorev = (idcode & IDCODE_REV_MASK) >> IDCODE_REV_SHIFT; pi->pubpi.radiover = (idcode & IDCODE_VER_MASK) >> IDCODE_VER_SHIFT; if (!VALID_RADIO(pi, pi->pubpi.radioid)) goto err; wlc_phy_switch_radio((struct brcms_phy_pub *) pi, OFF); wlc_set_phy_uninitted(pi); pi->bw = WL_CHANSPEC_BW_20; pi->radio_chanspec = (bandtype == BRCM_BAND_2G) ? ch20mhz_chspec(1) : ch20mhz_chspec(36); pi->rxiq_samps = PHY_NOISE_SAMPLE_LOG_NUM_NPHY; pi->rxiq_antsel = ANT_RX_DIV_DEF; pi->watchdog_override = true; pi->cal_type_override = PHY_PERICAL_AUTO; pi->nphy_saved_noisevars.bufcount = 0; if (ISNPHY(pi)) pi->min_txpower = PHY_TXPWR_MIN_NPHY; else pi->min_txpower = PHY_TXPWR_MIN; pi->sh->phyrxchain = 0x3; pi->rx2tx_biasentry = -1; pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP; pi->phy_txcore_enable_temp = PHY_CHAIN_TX_DISABLE_TEMP - PHY_HYSTERESIS_DELTATEMP; pi->phy_tempsense_offset = 0; pi->phy_txcore_heatedup = false; pi->nphy_lastcal_temp = -50; pi->phynoise_polling = true; if (ISNPHY(pi) || ISLCNPHY(pi)) pi->phynoise_polling = false; for (i = 0; i < TXP_NUM_RATES; i++) { pi->txpwr_limit[i] = BRCMS_TXPWR_MAX; pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX; pi->tx_user_target[i] = BRCMS_TXPWR_MAX; } pi->radiopwr_override = RADIOPWR_OVERRIDE_DEF; pi->user_txpwr_at_rfport = false; if (ISNPHY(pi)) { pi->phycal_timer = wlapi_init_timer(pi->sh->physhim, wlc_phy_timercb_phycal, pi, "phycal"); if (!pi->phycal_timer) goto err; if (!wlc_phy_attach_nphy(pi)) goto err; } else if (ISLCNPHY(pi)) { if (!wlc_phy_attach_lcnphy(pi)) goto err; } pi->refcnt++; pi->next = pi->sh->phy_head; sh->phy_head = pi; memcpy(&pi->pubpi_ro, &pi->pubpi, sizeof(struct brcms_phy_pub)); return &pi->pubpi_ro; err: kfree(pi); return NULL; } void wlc_phy_detach(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; if (pih) { if (--pi->refcnt) return; if (pi->phycal_timer) { wlapi_free_timer(pi->phycal_timer); pi->phycal_timer = NULL; } if (pi->sh->phy_head == pi) pi->sh->phy_head = pi->next; else if (pi->sh->phy_head->next == pi) pi->sh->phy_head->next = NULL; if (pi->pi_fptr.detach) (pi->pi_fptr.detach)(pi); kfree(pi); } } bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, u16 *phyrev, u16 *radioid, u16 *radiover) { struct brcms_phy *pi = (struct brcms_phy *) pih; *phytype = (u16) pi->pubpi.phy_type; *phyrev = (u16) pi->pubpi.phy_rev; *radioid = pi->pubpi.radioid; *radiover = pi->pubpi.radiorev; return true; } bool wlc_phy_get_encore(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; return pi->pubpi.abgphy_encore; } u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; return pi->pubpi.coreflags; } void wlc_phy_anacore(struct brcms_phy_pub *pih, bool on) { struct brcms_phy *pi = (struct brcms_phy *) pih; if (ISNPHY(pi)) { if (on) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_phy_reg(pi, 0xa6, 0x0d); write_phy_reg(pi, 0x8f, 0x0); write_phy_reg(pi, 0xa7, 0x0d); write_phy_reg(pi, 0xa5, 0x0); } else { write_phy_reg(pi, 0xa5, 0x0); } } else { if (NREV_GE(pi->pubpi.phy_rev, 3)) { write_phy_reg(pi, 0x8f, 0x07ff); write_phy_reg(pi, 0xa6, 0x0fd); write_phy_reg(pi, 0xa5, 0x07ff); write_phy_reg(pi, 0xa7, 0x0fd); } else { write_phy_reg(pi, 0xa5, 0x7fff); } } } else if (ISLCNPHY(pi)) { if (on) { and_phy_reg(pi, 0x43b, ~((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); } else { or_phy_reg(pi, 0x43c, (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); or_phy_reg(pi, 0x43b, (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); } } } u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; u32 phy_bw_clkbits = 0; if (pi && (ISNPHY(pi) || ISLCNPHY(pi))) { switch (pi->bw) { case WL_CHANSPEC_BW_10: phy_bw_clkbits = SICF_BW10; break; case WL_CHANSPEC_BW_20: phy_bw_clkbits = SICF_BW20; break; case WL_CHANSPEC_BW_40: phy_bw_clkbits = SICF_BW40; break; default: break; } } return phy_bw_clkbits; } void wlc_phy_por_inform(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; pi->phy_init_por = true; } void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock) { struct brcms_phy *pi = (struct brcms_phy *) pih; pi->edcrs_threshold_lock = lock; write_phy_reg(pi, 0x22c, 0x46b); write_phy_reg(pi, 0x22d, 0x46b); write_phy_reg(pi, 0x22e, 0x3c0); write_phy_reg(pi, 0x22f, 0x3c0); } void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal) { struct brcms_phy *pi = (struct brcms_phy *) pih; pi->do_initcal = initcal; } void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *pih, bool newstate) { struct brcms_phy *pi = (struct brcms_phy *) pih; if (!pi || !pi->sh) return; pi->sh->clk = newstate; } void wlc_phy_hw_state_upd(struct brcms_phy_pub *pih, bool newstate) { struct brcms_phy *pi = (struct brcms_phy *) pih; if (!pi || !pi->sh) return; pi->sh->up = newstate; } void wlc_phy_init(struct brcms_phy_pub *pih, u16 chanspec) { u32 mc; void (*phy_init)(struct brcms_phy *) = NULL; struct brcms_phy *pi = (struct brcms_phy *) pih; if (pi->init_in_progress) return; pi->init_in_progress = true; pi->radio_chanspec = chanspec; mc = bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); if (WARN(mc & MCTL_EN_MAC, "HW error MAC running on init")) return; if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC; if (WARN(!(bcma_aread32(pi->d11core, BCMA_IOST) & SISF_FCLKA), "HW error SISF_FCLKA\n")) return; phy_init = pi->pi_fptr.init; if (phy_init == NULL) return; wlc_phy_anacore(pih, ON); if (CHSPEC_BW(pi->radio_chanspec) != pi->bw) wlapi_bmac_bw_set(pi->sh->physhim, CHSPEC_BW(pi->radio_chanspec)); pi->nphy_gain_boost = true; wlc_phy_switch_radio((struct brcms_phy_pub *) pi, ON); (*phy_init)(pi); pi->phy_init_por = false; if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) wlc_phy_do_dummy_tx(pi, true, OFF); if (!(ISNPHY(pi))) wlc_phy_txpower_update_shm(pi); wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi, pi->sh->rx_antdiv); pi->init_in_progress = false; } void wlc_phy_cal_init(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; void (*cal_init)(struct brcms_phy *) = NULL; if (WARN((bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC) != 0, "HW error: MAC enabled during phy cal\n")) return; if (!pi->initialized) { cal_init = pi->pi_fptr.calinit; if (cal_init) (*cal_init)(pi); pi->initialized = true; } } int wlc_phy_down(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; int callbacks = 0; if (pi->phycal_timer && !wlapi_del_timer(pi->phycal_timer)) callbacks++; pi->nphy_iqcal_chanspec_2G = 0; pi->nphy_iqcal_chanspec_5G = 0; return callbacks; } void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, u16 tblAddr, u16 tblDataHi, u16 tblDataLo) { write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); pi->tbl_data_hi = tblDataHi; pi->tbl_data_lo = tblDataLo; if (pi->sh->chip == BCMA_CHIP_ID_BCM43224 && pi->sh->chiprev == 1) { pi->tbl_addr = tblAddr; pi->tbl_save_id = tbl_id; pi->tbl_save_offset = tbl_offset; } } void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val) { if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && (pi->sh->chiprev == 1) && (pi->tbl_save_id == NPHY_TBL_ID_ANTSWCTRLLUT)) { read_phy_reg(pi, pi->tbl_data_lo); write_phy_reg(pi, pi->tbl_addr, (pi->tbl_save_id << 10) | pi->tbl_save_offset); pi->tbl_save_offset++; } if (width == 32) { write_phy_reg(pi, pi->tbl_data_hi, (u16) (val >> 16)); write_phy_reg(pi, pi->tbl_data_lo, (u16) val); } else { write_phy_reg(pi, pi->tbl_data_lo, (u16) val); } } void wlc_phy_write_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, u16 tblAddr, u16 tblDataHi, u16 tblDataLo) { uint idx; uint tbl_id = ptbl_info->tbl_id; uint tbl_offset = ptbl_info->tbl_offset; uint tbl_width = ptbl_info->tbl_width; const u8 *ptbl_8b = (const u8 *)ptbl_info->tbl_ptr; const u16 *ptbl_16b = (const u16 *)ptbl_info->tbl_ptr; const u32 *ptbl_32b = (const u32 *)ptbl_info->tbl_ptr; write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); for (idx = 0; idx < ptbl_info->tbl_len; idx++) { if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && (pi->sh->chiprev == 1) && (tbl_id == NPHY_TBL_ID_ANTSWCTRLLUT)) { read_phy_reg(pi, tblDataLo); write_phy_reg(pi, tblAddr, (tbl_id << 10) | (tbl_offset + idx)); } if (tbl_width == 32) { write_phy_reg(pi, tblDataHi, (u16) (ptbl_32b[idx] >> 16)); write_phy_reg(pi, tblDataLo, (u16) ptbl_32b[idx]); } else if (tbl_width == 16) { write_phy_reg(pi, tblDataLo, ptbl_16b[idx]); } else { write_phy_reg(pi, tblDataLo, ptbl_8b[idx]); } } } void wlc_phy_read_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, u16 tblAddr, u16 tblDataHi, u16 tblDataLo) { uint idx; uint tbl_id = ptbl_info->tbl_id; uint tbl_offset = ptbl_info->tbl_offset; uint tbl_width = ptbl_info->tbl_width; u8 *ptbl_8b = (u8 *)ptbl_info->tbl_ptr; u16 *ptbl_16b = (u16 *)ptbl_info->tbl_ptr; u32 *ptbl_32b = (u32 *)ptbl_info->tbl_ptr; write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); for (idx = 0; idx < ptbl_info->tbl_len; idx++) { if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && (pi->sh->chiprev == 1)) { (void)read_phy_reg(pi, tblDataLo); write_phy_reg(pi, tblAddr, (tbl_id << 10) | (tbl_offset + idx)); } if (tbl_width == 32) { ptbl_32b[idx] = read_phy_reg(pi, tblDataLo); ptbl_32b[idx] |= (read_phy_reg(pi, tblDataHi) << 16); } else if (tbl_width == 16) { ptbl_16b[idx] = read_phy_reg(pi, tblDataLo); } else { ptbl_8b[idx] = (u8) read_phy_reg(pi, tblDataLo); } } } uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, struct radio_20xx_regs *radioregs) { uint i = 0; do { if (radioregs[i].do_init) write_radio_reg(pi, radioregs[i].address, (u16) radioregs[i].init); i++; } while (radioregs[i].address != 0xffff); return i; } uint wlc_phy_init_radio_regs(struct brcms_phy *pi, const struct radio_regs *radioregs, u16 core_offset) { uint i = 0; uint count = 0; do { if (CHSPEC_IS5G(pi->radio_chanspec)) { if (radioregs[i].do_init_a) { write_radio_reg(pi, radioregs[i]. address | core_offset, (u16) radioregs[i].init_a); if (ISNPHY(pi) && (++count % 4 == 0)) BRCMS_PHY_WAR_PR51571(pi); } } else { if (radioregs[i].do_init_g) { write_radio_reg(pi, radioregs[i]. address | core_offset, (u16) radioregs[i].init_g); if (ISNPHY(pi) && (++count % 4 == 0)) BRCMS_PHY_WAR_PR51571(pi); } } i++; } while (radioregs[i].address != 0xffff); return i; } void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on) { #define DUMMY_PKT_LEN 20 struct bcma_device *core = pi->d11core; int i, count; u8 ofdmpkt[DUMMY_PKT_LEN] = { 0xcc, 0x01, 0x02, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }; u8 cckpkt[DUMMY_PKT_LEN] = { 0x6e, 0x84, 0x0b, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }; u32 *dummypkt; dummypkt = (u32 *) (ofdm ? ofdmpkt : cckpkt); wlapi_bmac_write_template_ram(pi->sh->physhim, 0, DUMMY_PKT_LEN, dummypkt); bcma_write16(core, D11REGOFFS(xmtsel), 0); if (D11REV_GE(pi->sh->corerev, 11)) bcma_write16(core, D11REGOFFS(wepctl), 0x100); else bcma_write16(core, D11REGOFFS(wepctl), 0); bcma_write16(core, D11REGOFFS(txe_phyctl), (ofdm ? 1 : 0) | PHY_TXC_ANT_0); if (ISNPHY(pi) || ISLCNPHY(pi)) bcma_write16(core, D11REGOFFS(txe_phyctl1), 0x1A02); bcma_write16(core, D11REGOFFS(txe_wm_0), 0); bcma_write16(core, D11REGOFFS(txe_wm_1), 0); bcma_write16(core, D11REGOFFS(xmttplatetxptr), 0); bcma_write16(core, D11REGOFFS(xmttxcnt), DUMMY_PKT_LEN); bcma_write16(core, D11REGOFFS(xmtsel), ((8 << 8) | (1 << 5) | (1 << 2) | 2)); bcma_write16(core, D11REGOFFS(txe_ctl), 0); if (!pa_on) { if (ISNPHY(pi)) wlc_phy_pa_override_nphy(pi, OFF); } if (ISNPHY(pi) || ISLCNPHY(pi)) bcma_write16(core, D11REGOFFS(txe_aux), 0xD0); else bcma_write16(core, D11REGOFFS(txe_aux), ((1 << 5) | (1 << 4))); (void)bcma_read16(core, D11REGOFFS(txe_aux)); i = 0; count = ofdm ? 30 : 250; while ((i++ < count) && (bcma_read16(core, D11REGOFFS(txe_status)) & (1 << 7))) udelay(10); i = 0; while ((i++ < 10) && ((bcma_read16(core, D11REGOFFS(txe_status)) & (1 << 10)) == 0)) udelay(10); i = 0; while ((i++ < 10) && ((bcma_read16(core, D11REGOFFS(ifsstat)) & (1 << 8)))) udelay(10); if (!pa_on) { if (ISNPHY(pi)) wlc_phy_pa_override_nphy(pi, ON); } } void wlc_phy_hold_upd(struct brcms_phy_pub *pih, u32 id, bool set) { struct brcms_phy *pi = (struct brcms_phy *) pih; if (set) mboolset(pi->measure_hold, id); else mboolclr(pi->measure_hold, id); return; } void wlc_phy_mute_upd(struct brcms_phy_pub *pih, bool mute, u32 flags) { struct brcms_phy *pi = (struct brcms_phy *) pih; if (mute) mboolset(pi->measure_hold, PHY_HOLD_FOR_MUTE); else mboolclr(pi->measure_hold, PHY_HOLD_FOR_MUTE); if (!mute && (flags & PHY_MUTE_FOR_PREISM)) pi->nphy_perical_last = pi->sh->now - pi->sh->glacial_timer; return; } void wlc_phy_clear_tssi(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; if (ISNPHY(pi)) { return; } else { wlapi_bmac_write_shm(pi->sh->physhim, M_B_TSSI_0, NULL_TSSI_W); wlapi_bmac_write_shm(pi->sh->physhim, M_B_TSSI_1, NULL_TSSI_W); wlapi_bmac_write_shm(pi->sh->physhim, M_G_TSSI_0, NULL_TSSI_W); wlapi_bmac_write_shm(pi->sh->physhim, M_G_TSSI_1, NULL_TSSI_W); } } static bool wlc_phy_cal_txpower_recalc_sw(struct brcms_phy *pi) { return false; } void wlc_phy_switch_radio(struct brcms_phy_pub *pih, bool on) { struct brcms_phy *pi = (struct brcms_phy *) pih; (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); if (ISNPHY(pi)) { wlc_phy_switch_radio_nphy(pi, on); } else if (ISLCNPHY(pi)) { if (on) { and_phy_reg(pi, 0x44c, ~((0x1 << 8) | (0x1 << 9) | (0x1 << 10) | (0x1 << 11) | (0x1 << 12))); and_phy_reg(pi, 0x4b0, ~((0x1 << 3) | (0x1 << 11))); and_phy_reg(pi, 0x4f9, ~(0x1 << 3)); } else { and_phy_reg(pi, 0x44d, ~((0x1 << 10) | (0x1 << 11) | (0x1 << 12) | (0x1 << 13) | (0x1 << 14))); or_phy_reg(pi, 0x44c, (0x1 << 8) | (0x1 << 9) | (0x1 << 10) | (0x1 << 11) | (0x1 << 12)); and_phy_reg(pi, 0x4b7, ~((0x7f << 8))); and_phy_reg(pi, 0x4b1, ~((0x1 << 13))); or_phy_reg(pi, 0x4b0, (0x1 << 3) | (0x1 << 11)); and_phy_reg(pi, 0x4fa, ~((0x1 << 3))); or_phy_reg(pi, 0x4f9, (0x1 << 3)); } } } u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; return pi->bw; } void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw) { struct brcms_phy *pi = (struct brcms_phy *) ppi; pi->bw = bw; } void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch) { struct brcms_phy *pi = (struct brcms_phy *) ppi; pi->radio_chanspec = newch; } u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; return pi->radio_chanspec; } void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec) { struct brcms_phy *pi = (struct brcms_phy *) ppi; u16 m_cur_channel; void (*chanspec_set)(struct brcms_phy *, u16) = NULL; m_cur_channel = CHSPEC_CHANNEL(chanspec); if (CHSPEC_IS5G(chanspec)) m_cur_channel |= D11_CURCHANNEL_5G; if (CHSPEC_IS40(chanspec)) m_cur_channel |= D11_CURCHANNEL_40; wlapi_bmac_write_shm(pi->sh->physhim, M_CURCHANNEL, m_cur_channel); chanspec_set = pi->pi_fptr.chanset; if (chanspec_set) (*chanspec_set)(pi, chanspec); } int wlc_phy_chanspec_freq2bandrange_lpssn(uint freq) { int range = -1; if (freq < 2500) range = WL_CHAN_FREQ_RANGE_2G; else if (freq <= 5320) range = WL_CHAN_FREQ_RANGE_5GL; else if (freq <= 5700) range = WL_CHAN_FREQ_RANGE_5GM; else range = WL_CHAN_FREQ_RANGE_5GH; return range; } int wlc_phy_chanspec_bandrange_get(struct brcms_phy *pi, u16 chanspec) { int range = -1; uint channel = CHSPEC_CHANNEL(chanspec); uint freq = wlc_phy_channel2freq(channel); if (ISNPHY(pi)) range = wlc_phy_get_chan_freq_range_nphy(pi, channel); else if (ISLCNPHY(pi)) range = wlc_phy_chanspec_freq2bandrange_lpssn(freq); return range; } void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, bool wide_filter) { struct brcms_phy *pi = (struct brcms_phy *) ppi; pi->channel_14_wide_filter = wide_filter; } int wlc_phy_channel2freq(uint channel) { uint i; for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) if (chan_info_all[i].chan == channel) return chan_info_all[i].freq; return 0; } void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, struct brcms_chanvec *channels) { struct brcms_phy *pi = (struct brcms_phy *) ppi; uint i; uint channel; memset(channels, 0, sizeof(struct brcms_chanvec)); for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { channel = chan_info_all[i].chan; if ((pi->a_band_high_disable) && (channel >= FIRST_REF5_CHANNUM) && (channel <= LAST_REF5_CHANNUM)) continue; if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) || (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL)) setbit(channels->vec, channel); } } u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band) { struct brcms_phy *pi = (struct brcms_phy *) ppi; uint i; uint channel; u16 chspec; for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { channel = chan_info_all[i].chan; if (ISNPHY(pi) && pi->bw == WL_CHANSPEC_BW_40) { uint j; for (j = 0; j < ARRAY_SIZE(chan_info_all); j++) { if (chan_info_all[j].chan == channel + CH_10MHZ_APART) break; } if (j == ARRAY_SIZE(chan_info_all)) continue; channel = upper_20_sb(channel); chspec = channel | WL_CHANSPEC_BW_40 | WL_CHANSPEC_CTL_SB_LOWER; if (band == BRCM_BAND_2G) chspec |= WL_CHANSPEC_BAND_2G; else chspec |= WL_CHANSPEC_BAND_5G; } else chspec = ch20mhz_chspec(channel); if ((pi->a_band_high_disable) && (channel >= FIRST_REF5_CHANNUM) && (channel <= LAST_REF5_CHANNUM)) continue; if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) || (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL)) return chspec; } return (u16) INVCHANSPEC; } int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override) { struct brcms_phy *pi = (struct brcms_phy *) ppi; *qdbm = pi->tx_user_target[0]; if (override != NULL) *override = pi->txpwroverride; return 0; } void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, struct txpwr_limits *txpwr) { bool mac_enabled = false; struct brcms_phy *pi = (struct brcms_phy *) ppi; memcpy(&pi->tx_user_target[TXP_FIRST_CCK], &txpwr->cck[0], BRCMS_NUM_RATES_CCK); memcpy(&pi->tx_user_target[TXP_FIRST_OFDM], &txpwr->ofdm[0], BRCMS_NUM_RATES_OFDM); memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_20_CDD], &txpwr->ofdm_cdd[0], BRCMS_NUM_RATES_OFDM); memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_SISO], &txpwr->ofdm_40_siso[0], BRCMS_NUM_RATES_OFDM); memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_CDD], &txpwr->ofdm_40_cdd[0], BRCMS_NUM_RATES_OFDM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SISO], &txpwr->mcs_20_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_CDD], &txpwr->mcs_20_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_STBC], &txpwr->mcs_20_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SDM], &txpwr->mcs_20_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SISO], &txpwr->mcs_40_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_CDD], &txpwr->mcs_40_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_STBC], &txpwr->mcs_40_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM); memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SDM], &txpwr->mcs_40_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM); if (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC) mac_enabled = true; if (mac_enabled) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phy_txpower_recalc_target(pi); wlc_phy_cal_txpower_recalc_sw(pi); if (mac_enabled) wlapi_enable_mac(pi->sh->physhim); } int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override) { struct brcms_phy *pi = (struct brcms_phy *) ppi; int i; if (qdbm > 127) return -EINVAL; for (i = 0; i < TXP_NUM_RATES; i++) pi->tx_user_target[i] = (u8) qdbm; pi->txpwroverride = false; if (pi->sh->up) { if (!SCAN_INPROG_PHY(pi)) { bool suspend; suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phy_txpower_recalc_target(pi); wlc_phy_cal_txpower_recalc_sw(pi); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } } return 0; } void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint channel, u8 *min_pwr, u8 *max_pwr, int txp_rate_idx) { struct brcms_phy *pi = (struct brcms_phy *) ppi; uint i; *min_pwr = pi->min_txpower * BRCMS_TXPWR_DB_FACTOR; if (ISNPHY(pi)) { if (txp_rate_idx < 0) txp_rate_idx = TXP_FIRST_CCK; wlc_phy_txpower_sromlimit_get_nphy(pi, channel, max_pwr, (u8) txp_rate_idx); } else if ((channel <= CH_MAX_2G_CHANNEL)) { if (txp_rate_idx < 0) txp_rate_idx = TXP_FIRST_CCK; *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; } else { *max_pwr = BRCMS_TXPWR_MAX; if (txp_rate_idx < 0) txp_rate_idx = TXP_FIRST_OFDM; for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { if (channel == chan_info_all[i].chan) break; } if (pi->hwtxpwr) { *max_pwr = pi->hwtxpwr[i]; } else { if ((i >= FIRST_MID_5G_CHAN) && (i <= LAST_MID_5G_CHAN)) *max_pwr = pi->tx_srom_max_rate_5g_mid[txp_rate_idx]; if ((i >= FIRST_HIGH_5G_CHAN) && (i <= LAST_HIGH_5G_CHAN)) *max_pwr = pi->tx_srom_max_rate_5g_hi[txp_rate_idx]; if ((i >= FIRST_LOW_5G_CHAN) && (i <= LAST_LOW_5G_CHAN)) *max_pwr = pi->tx_srom_max_rate_5g_low[txp_rate_idx]; } } } void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, u8 *max_txpwr, u8 *min_txpwr) { struct brcms_phy *pi = (struct brcms_phy *) ppi; u8 tx_pwr_max = 0; u8 tx_pwr_min = 255; u8 max_num_rate; u8 maxtxpwr, mintxpwr, rate, pactrl; pactrl = 0; max_num_rate = ISNPHY(pi) ? TXP_NUM_RATES : ISLCNPHY(pi) ? (TXP_LAST_SISO_MCS_20 + 1) : (TXP_LAST_OFDM + 1); for (rate = 0; rate < max_num_rate; rate++) { wlc_phy_txpower_sromlimit(ppi, chan, &mintxpwr, &maxtxpwr, rate); maxtxpwr = (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0; maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0; tx_pwr_max = max(tx_pwr_max, maxtxpwr); tx_pwr_min = min(tx_pwr_min, maxtxpwr); } *max_txpwr = tx_pwr_max; *min_txpwr = tx_pwr_min; } void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint bandunit, s32 *max_pwr, s32 *min_pwr, u32 *step_pwr) { return; } u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; return pi->tx_power_min; } u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; return pi->tx_power_max; } static s8 wlc_phy_env_measure_vbat(struct brcms_phy *pi) { if (ISLCNPHY(pi)) return wlc_lcnphy_vbatsense(pi, 0); else return 0; } static s8 wlc_phy_env_measure_temperature(struct brcms_phy *pi) { if (ISLCNPHY(pi)) return wlc_lcnphy_tempsense_degree(pi, 0); else return 0; } static void wlc_phy_upd_env_txpwr_rate_limits(struct brcms_phy *pi, u32 band) { u8 i; s8 temp, vbat; for (i = 0; i < TXP_NUM_RATES; i++) pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX; vbat = wlc_phy_env_measure_vbat(pi); temp = wlc_phy_env_measure_temperature(pi); } static s8 wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band, u8 rate) { s8 offset = 0; if (!pi->user_txpwr_at_rfport) return offset; return offset; } void wlc_phy_txpower_recalc_target(struct brcms_phy *pi) { u8 maxtxpwr, mintxpwr, rate, pactrl; uint target_chan; u8 tx_pwr_target[TXP_NUM_RATES]; u8 tx_pwr_max = 0; u8 tx_pwr_min = 255; u8 tx_pwr_max_rate_ind = 0; u8 max_num_rate; u8 start_rate = 0; u16 chspec; u32 band = CHSPEC2BAND(pi->radio_chanspec); void (*txpwr_recalc_fn)(struct brcms_phy *) = NULL; chspec = pi->radio_chanspec; if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) target_chan = CHSPEC_CHANNEL(chspec); else if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) target_chan = upper_20_sb(CHSPEC_CHANNEL(chspec)); else target_chan = lower_20_sb(CHSPEC_CHANNEL(chspec)); pactrl = 0; if (ISLCNPHY(pi)) { u32 offset_mcs, i; if (CHSPEC_IS40(pi->radio_chanspec)) { offset_mcs = pi->mcs40_po; for (i = TXP_FIRST_SISO_MCS_20; i <= TXP_LAST_SISO_MCS_20; i++) { pi->tx_srom_max_rate_2g[i - 8] = pi->tx_srom_max_2g - ((offset_mcs & 0xf) * 2); offset_mcs >>= 4; } } else { offset_mcs = pi->mcs20_po; for (i = TXP_FIRST_SISO_MCS_20; i <= TXP_LAST_SISO_MCS_20; i++) { pi->tx_srom_max_rate_2g[i - 8] = pi->tx_srom_max_2g - ((offset_mcs & 0xf) * 2); offset_mcs >>= 4; } } } max_num_rate = ((ISNPHY(pi)) ? (TXP_NUM_RATES) : ((ISLCNPHY(pi)) ? (TXP_LAST_SISO_MCS_20 + 1) : (TXP_LAST_OFDM + 1))); wlc_phy_upd_env_txpwr_rate_limits(pi, band); for (rate = start_rate; rate < max_num_rate; rate++) { tx_pwr_target[rate] = pi->tx_user_target[rate]; if (pi->user_txpwr_at_rfport) tx_pwr_target[rate] += wlc_user_txpwr_antport_to_rfport(pi, target_chan, band, rate); wlc_phy_txpower_sromlimit((struct brcms_phy_pub *) pi, target_chan, &mintxpwr, &maxtxpwr, rate); maxtxpwr = min(maxtxpwr, pi->txpwr_limit[rate]); maxtxpwr = (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0; maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0; maxtxpwr = min(maxtxpwr, tx_pwr_target[rate]); if (pi->txpwr_percent <= 100) maxtxpwr = (maxtxpwr * pi->txpwr_percent) / 100; tx_pwr_target[rate] = max(maxtxpwr, mintxpwr); tx_pwr_target[rate] = min(tx_pwr_target[rate], pi->txpwr_env_limit[rate]); if (tx_pwr_target[rate] > tx_pwr_max) tx_pwr_max_rate_ind = rate; tx_pwr_max = max(tx_pwr_max, tx_pwr_target[rate]); tx_pwr_min = min(tx_pwr_min, tx_pwr_target[rate]); } memset(pi->tx_power_offset, 0, sizeof(pi->tx_power_offset)); pi->tx_power_max = tx_pwr_max; pi->tx_power_min = tx_pwr_min; pi->tx_power_max_rate_ind = tx_pwr_max_rate_ind; for (rate = 0; rate < max_num_rate; rate++) { pi->tx_power_target[rate] = tx_pwr_target[rate]; if (!pi->hwpwrctrl || ISNPHY(pi)) pi->tx_power_offset[rate] = pi->tx_power_max - pi->tx_power_target[rate]; else pi->tx_power_offset[rate] = pi->tx_power_target[rate] - pi->tx_power_min; } txpwr_recalc_fn = pi->pi_fptr.txpwrrecalc; if (txpwr_recalc_fn) (*txpwr_recalc_fn)(pi); } static void wlc_phy_txpower_reg_limit_calc(struct brcms_phy *pi, struct txpwr_limits *txpwr, u16 chanspec) { u8 tmp_txpwr_limit[2 * BRCMS_NUM_RATES_OFDM]; u8 *txpwr_ptr1 = NULL, *txpwr_ptr2 = NULL; int rate_start_index = 0, rate1, rate2, k; for (rate1 = WL_TX_POWER_CCK_FIRST, rate2 = 0; rate2 < WL_TX_POWER_CCK_NUM; rate1++, rate2++) pi->txpwr_limit[rate1] = txpwr->cck[rate2]; for (rate1 = WL_TX_POWER_OFDM_FIRST, rate2 = 0; rate2 < WL_TX_POWER_OFDM_NUM; rate1++, rate2++) pi->txpwr_limit[rate1] = txpwr->ofdm[rate2]; if (ISNPHY(pi)) { for (k = 0; k < 4; k++) { switch (k) { case 0: txpwr_ptr1 = txpwr->mcs_20_siso; txpwr_ptr2 = txpwr->ofdm; rate_start_index = WL_TX_POWER_OFDM_FIRST; break; case 1: txpwr_ptr1 = txpwr->mcs_20_cdd; txpwr_ptr2 = txpwr->ofdm_cdd; rate_start_index = WL_TX_POWER_OFDM20_CDD_FIRST; break; case 2: txpwr_ptr1 = txpwr->mcs_40_siso; txpwr_ptr2 = txpwr->ofdm_40_siso; rate_start_index = WL_TX_POWER_OFDM40_SISO_FIRST; break; case 3: txpwr_ptr1 = txpwr->mcs_40_cdd; txpwr_ptr2 = txpwr->ofdm_40_cdd; rate_start_index = WL_TX_POWER_OFDM40_CDD_FIRST; break; } for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM; rate2++) { tmp_txpwr_limit[rate2] = 0; tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] = txpwr_ptr1[rate2]; } wlc_phy_mcs_to_ofdm_powers_nphy( tmp_txpwr_limit, 0, BRCMS_NUM_RATES_OFDM - 1, BRCMS_NUM_RATES_OFDM); for (rate1 = rate_start_index, rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM; rate1++, rate2++) pi->txpwr_limit[rate1] = min(txpwr_ptr2[rate2], tmp_txpwr_limit[rate2]); } for (k = 0; k < 4; k++) { switch (k) { case 0: txpwr_ptr1 = txpwr->ofdm; txpwr_ptr2 = txpwr->mcs_20_siso; rate_start_index = WL_TX_POWER_MCS20_SISO_FIRST; break; case 1: txpwr_ptr1 = txpwr->ofdm_cdd; txpwr_ptr2 = txpwr->mcs_20_cdd; rate_start_index = WL_TX_POWER_MCS20_CDD_FIRST; break; case 2: txpwr_ptr1 = txpwr->ofdm_40_siso; txpwr_ptr2 = txpwr->mcs_40_siso; rate_start_index = WL_TX_POWER_MCS40_SISO_FIRST; break; case 3: txpwr_ptr1 = txpwr->ofdm_40_cdd; txpwr_ptr2 = txpwr->mcs_40_cdd; rate_start_index = WL_TX_POWER_MCS40_CDD_FIRST; break; } for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM; rate2++) { tmp_txpwr_limit[rate2] = 0; tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] = txpwr_ptr1[rate2]; } wlc_phy_ofdm_to_mcs_powers_nphy( tmp_txpwr_limit, 0, BRCMS_NUM_RATES_OFDM - 1, BRCMS_NUM_RATES_OFDM); for (rate1 = rate_start_index, rate2 = 0; rate2 < BRCMS_NUM_RATES_MCS_1_STREAM; rate1++, rate2++) pi->txpwr_limit[rate1] = min(txpwr_ptr2[rate2], tmp_txpwr_limit[rate2]); } for (k = 0; k < 2; k++) { switch (k) { case 0: rate_start_index = WL_TX_POWER_MCS20_STBC_FIRST; txpwr_ptr1 = txpwr->mcs_20_stbc; break; case 1: rate_start_index = WL_TX_POWER_MCS40_STBC_FIRST; txpwr_ptr1 = txpwr->mcs_40_stbc; break; } for (rate1 = rate_start_index, rate2 = 0; rate2 < BRCMS_NUM_RATES_MCS_1_STREAM; rate1++, rate2++) pi->txpwr_limit[rate1] = txpwr_ptr1[rate2]; } for (k = 0; k < 2; k++) { switch (k) { case 0: rate_start_index = WL_TX_POWER_MCS20_SDM_FIRST; txpwr_ptr1 = txpwr->mcs_20_mimo; break; case 1: rate_start_index = WL_TX_POWER_MCS40_SDM_FIRST; txpwr_ptr1 = txpwr->mcs_40_mimo; break; } for (rate1 = rate_start_index, rate2 = 0; rate2 < BRCMS_NUM_RATES_MCS_2_STREAM; rate1++, rate2++) pi->txpwr_limit[rate1] = txpwr_ptr1[rate2]; } pi->txpwr_limit[WL_TX_POWER_MCS_32] = txpwr->mcs32; pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST] = min(pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST], pi->txpwr_limit[WL_TX_POWER_MCS_32]); pi->txpwr_limit[WL_TX_POWER_MCS_32] = pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST]; } } void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent) { struct brcms_phy *pi = (struct brcms_phy *) ppi; pi->txpwr_percent = txpwr_percent; } void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap) { struct brcms_phy *pi = (struct brcms_phy *) ppi; pi->sh->machwcap = machwcap; } void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end) { struct brcms_phy *pi = (struct brcms_phy *) ppi; u16 rxc; rxc = 0; if (start_end == ON) { if (!ISNPHY(pi)) return; if (NREV_IS(pi->pubpi.phy_rev, 3) || NREV_IS(pi->pubpi.phy_rev, 4)) { bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), 0xa0); bcma_set16(pi->d11core, D11REGOFFS(phyregdata), 0x1 << 15); } } else { if (NREV_IS(pi->pubpi.phy_rev, 3) || NREV_IS(pi->pubpi.phy_rev, 4)) { bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), 0xa0); bcma_write16(pi->d11core, D11REGOFFS(phyregdata), rxc); } wlc_phy_por_inform(ppi); } } void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *txpwr, u16 chanspec) { struct brcms_phy *pi = (struct brcms_phy *) ppi; wlc_phy_txpower_reg_limit_calc(pi, txpwr, chanspec); if (ISLCNPHY(pi)) { int i, j; for (i = TXP_FIRST_OFDM_20_CDD, j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; i++, j++) { if (txpwr->mcs_20_siso[j]) pi->txpwr_limit[i] = txpwr->mcs_20_siso[j]; else pi->txpwr_limit[i] = txpwr->ofdm[j]; } } wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phy_txpower_recalc_target(pi); wlc_phy_cal_txpower_recalc_sw(pi); wlapi_enable_mac(pi->sh->physhim); } void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war) { struct brcms_phy *pi = (struct brcms_phy *) pih; pi->ofdm_rateset_war = war; } void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt) { struct brcms_phy *pi = (struct brcms_phy *) pih; pi->bf_preempt_4306 = bf_preempt; } void wlc_phy_txpower_update_shm(struct brcms_phy *pi) { int j; if (ISNPHY(pi)) return; if (!pi->sh->clk) return; if (pi->hwpwrctrl) { u16 offset; wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_MAX, 63); wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_N, 1 << NUM_TSSI_FRAMES); wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_TARGET, pi->tx_power_min << NUM_TSSI_FRAMES); wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_CUR, pi->hwpwr_txcur); for (j = TXP_FIRST_OFDM; j <= TXP_LAST_OFDM; j++) { const u8 ucode_ofdm_rates[] = { 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c }; offset = wlapi_bmac_rate_shm_offset( pi->sh->physhim, ucode_ofdm_rates[j - TXP_FIRST_OFDM]); wlapi_bmac_write_shm(pi->sh->physhim, offset + 6, pi->tx_power_offset[j]); wlapi_bmac_write_shm(pi->sh->physhim, offset + 14, -(pi->tx_power_offset[j] / 2)); } wlapi_bmac_mhf(pi->sh->physhim, MHF2, MHF2_HWPWRCTL, MHF2_HWPWRCTL, BRCM_BAND_ALL); } else { int i; for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) pi->tx_power_offset[i] = (u8) roundup(pi->tx_power_offset[i], 8); wlapi_bmac_write_shm(pi->sh->physhim, M_OFDM_OFFSET, (u16) ((pi->tx_power_offset[TXP_FIRST_OFDM] + 7) >> 3)); } } bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; if (ISNPHY(pi)) return pi->nphy_txpwrctrl; else return pi->hwpwrctrl; } void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl) { struct brcms_phy *pi = (struct brcms_phy *) ppi; bool suspend; if (!pi->hwpwrctrl_capable) return; pi->hwpwrctrl = hwpwrctrl; pi->nphy_txpwrctrl = hwpwrctrl; pi->txpwrctrl = hwpwrctrl; if (ISNPHY(pi)) { suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl); if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) wlc_phy_txpwr_fixpower_nphy(pi); else mod_phy_reg(pi, 0x1e7, (0x7f << 0), pi->saved_txpwr_idx); if (!suspend) wlapi_enable_mac(pi->sh->physhim); } } void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi) { if (NREV_GE(pi->pubpi.phy_rev, 3)) { pi->ipa2g_on = (pi->srom_fem2g.extpagain == 2); pi->ipa5g_on = (pi->srom_fem5g.extpagain == 2); } else { pi->ipa2g_on = false; pi->ipa5g_on = false; } } static u32 wlc_phy_txpower_est_power_nphy(struct brcms_phy *pi) { s16 tx0_status, tx1_status; u16 estPower1, estPower2; u8 pwr0, pwr1, adj_pwr0, adj_pwr1; u32 est_pwr; estPower1 = read_phy_reg(pi, 0x118); estPower2 = read_phy_reg(pi, 0x119); if ((estPower1 & (0x1 << 8)) == (0x1 << 8)) pwr0 = (u8) (estPower1 & (0xff << 0)) >> 0; else pwr0 = 0x80; if ((estPower2 & (0x1 << 8)) == (0x1 << 8)) pwr1 = (u8) (estPower2 & (0xff << 0)) >> 0; else pwr1 = 0x80; tx0_status = read_phy_reg(pi, 0x1ed); tx1_status = read_phy_reg(pi, 0x1ee); if ((tx0_status & (0x1 << 15)) == (0x1 << 15)) adj_pwr0 = (u8) (tx0_status & (0xff << 0)) >> 0; else adj_pwr0 = 0x80; if ((tx1_status & (0x1 << 15)) == (0x1 << 15)) adj_pwr1 = (u8) (tx1_status & (0xff << 0)) >> 0; else adj_pwr1 = 0x80; est_pwr = (u32) ((pwr0 << 24) | (pwr1 << 16) | (adj_pwr0 << 8) | adj_pwr1); return est_pwr; } void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, struct tx_power *power, uint channel) { struct brcms_phy *pi = (struct brcms_phy *) ppi; uint rate, num_rates; u8 min_pwr, max_pwr; #if WL_TX_POWER_RATES != TXP_NUM_RATES #error "struct tx_power out of sync with this fn" #endif if (ISNPHY(pi)) { power->rf_cores = 2; power->flags |= (WL_TX_POWER_F_MIMO); if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON) power->flags |= (WL_TX_POWER_F_ENABLED | WL_TX_POWER_F_HW); } else if (ISLCNPHY(pi)) { power->rf_cores = 1; power->flags |= (WL_TX_POWER_F_SISO); if (pi->radiopwr_override == RADIOPWR_OVERRIDE_DEF) power->flags |= WL_TX_POWER_F_ENABLED; if (pi->hwpwrctrl) power->flags |= WL_TX_POWER_F_HW; } num_rates = ((ISNPHY(pi)) ? (TXP_NUM_RATES) : ((ISLCNPHY(pi)) ? (TXP_LAST_OFDM_20_CDD + 1) : (TXP_LAST_OFDM + 1))); for (rate = 0; rate < num_rates; rate++) { power->user_limit[rate] = pi->tx_user_target[rate]; wlc_phy_txpower_sromlimit(ppi, channel, &min_pwr, &max_pwr, rate); power->board_limit[rate] = (u8) max_pwr; power->target[rate] = pi->tx_power_target[rate]; } if (ISNPHY(pi)) { u32 est_pout; wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_phyreg_enter((struct brcms_phy_pub *) pi); est_pout = wlc_phy_txpower_est_power_nphy(pi); wlc_phyreg_exit((struct brcms_phy_pub *) pi); wlapi_enable_mac(pi->sh->physhim); power->est_Pout[0] = (est_pout >> 8) & 0xff; power->est_Pout[1] = est_pout & 0xff; power->est_Pout_act[0] = est_pout >> 24; power->est_Pout_act[1] = (est_pout >> 16) & 0xff; if (power->est_Pout[0] == 0x80) power->est_Pout[0] = 0; if (power->est_Pout[1] == 0x80) power->est_Pout[1] = 0; if (power->est_Pout_act[0] == 0x80) power->est_Pout_act[0] = 0; if (power->est_Pout_act[1] == 0x80) power->est_Pout_act[1] = 0; power->est_Pout_cck = 0; power->tx_power_max[0] = pi->tx_power_max; power->tx_power_max[1] = pi->tx_power_max; power->tx_power_max_rate_ind[0] = pi->tx_power_max_rate_ind; power->tx_power_max_rate_ind[1] = pi->tx_power_max_rate_ind; } else if (pi->hwpwrctrl && pi->sh->up) { wlc_phyreg_enter(ppi); if (ISLCNPHY(pi)) { power->tx_power_max[0] = pi->tx_power_max; power->tx_power_max[1] = pi->tx_power_max; power->tx_power_max_rate_ind[0] = pi->tx_power_max_rate_ind; power->tx_power_max_rate_ind[1] = pi->tx_power_max_rate_ind; if (wlc_phy_tpc_isenabled_lcnphy(pi)) power->flags |= (WL_TX_POWER_F_HW | WL_TX_POWER_F_ENABLED); else power->flags &= ~(WL_TX_POWER_F_HW | WL_TX_POWER_F_ENABLED); wlc_lcnphy_get_tssi(pi, (s8 *) &power->est_Pout[0], (s8 *) &power->est_Pout_cck); } wlc_phyreg_exit(ppi); } } void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type) { struct brcms_phy *pi = (struct brcms_phy *) ppi; pi->antsel_type = antsel_type; } bool wlc_phy_test_ison(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; return pi->phytest_on; } void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val) { struct brcms_phy *pi = (struct brcms_phy *) ppi; bool suspend; pi->sh->rx_antdiv = val; if (!(ISNPHY(pi) && D11REV_IS(pi->sh->corerev, 16))) { if (val > ANT_RX_DIV_FORCE_1) wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV, MHF1_ANTDIV, BRCM_BAND_ALL); else wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV, 0, BRCM_BAND_ALL); } if (ISNPHY(pi)) return; if (!pi->sh->clk) return; suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); if (!suspend) wlapi_suspend_mac_and_wait(pi->sh->physhim); if (ISLCNPHY(pi)) { if (val > ANT_RX_DIV_FORCE_1) { mod_phy_reg(pi, 0x410, (0x1 << 1), 0x01 << 1); mod_phy_reg(pi, 0x410, (0x1 << 0), ((ANT_RX_DIV_START_1 == val) ? 1 : 0) << 0); } else { mod_phy_reg(pi, 0x410, (0x1 << 1), 0x00 << 1); mod_phy_reg(pi, 0x410, (0x1 << 0), (u16) val << 0); } } if (!suspend) wlapi_enable_mac(pi->sh->physhim); return; } static bool wlc_phy_noise_calc_phy(struct brcms_phy *pi, u32 *cmplx_pwr, s8 *pwr_ant) { s8 cmplx_pwr_dbm[PHY_CORE_MAX]; u8 i; memset((u8 *) cmplx_pwr_dbm, 0, sizeof(cmplx_pwr_dbm)); wlc_phy_compute_dB(cmplx_pwr, cmplx_pwr_dbm, pi->pubpi.phy_corenum); for (i = 0; i < pi->pubpi.phy_corenum; i++) { if (NREV_GE(pi->pubpi.phy_rev, 3)) cmplx_pwr_dbm[i] += (s8) PHY_NOISE_OFFSETFACT_4322; else cmplx_pwr_dbm[i] += (s8) (16 - (15) * 3 - 70); } for (i = 0; i < pi->pubpi.phy_corenum; i++) { pi->nphy_noise_win[i][pi->nphy_noise_index] = cmplx_pwr_dbm[i]; pwr_ant[i] = cmplx_pwr_dbm[i]; } pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); return true; } static void wlc_phy_noise_cb(struct brcms_phy *pi, u8 channel, s8 noise_dbm) { if (!pi->phynoise_state) return; if (pi->phynoise_state & PHY_NOISE_STATE_MON) { if (pi->phynoise_chan_watchdog == channel) { pi->sh->phy_noise_window[pi->sh->phy_noise_index] = noise_dbm; pi->sh->phy_noise_index = MODINC(pi->sh->phy_noise_index, MA_WINDOW_SZ); } pi->phynoise_state &= ~PHY_NOISE_STATE_MON; } if (pi->phynoise_state & PHY_NOISE_STATE_EXTERNAL) pi->phynoise_state &= ~PHY_NOISE_STATE_EXTERNAL; } static s8 wlc_phy_noise_read_shmem(struct brcms_phy *pi) { u32 cmplx_pwr[PHY_CORE_MAX]; s8 noise_dbm_ant[PHY_CORE_MAX]; u16 lo, hi; u32 cmplx_pwr_tot = 0; s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; u8 idx, core; memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr)); memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant)); for (idx = 0, core = 0; core < pi->pubpi.phy_corenum; idx += 2, core++) { lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP(idx)); hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP(idx + 1)); cmplx_pwr[core] = (hi << 16) + lo; cmplx_pwr_tot += cmplx_pwr[core]; if (cmplx_pwr[core] == 0) noise_dbm_ant[core] = PHY_NOISE_FIXED_VAL_NPHY; else cmplx_pwr[core] >>= PHY_NOISE_SAMPLE_LOG_NUM_UCODE; } if (cmplx_pwr_tot != 0) wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant); for (core = 0; core < pi->pubpi.phy_corenum; core++) { pi->nphy_noise_win[core][pi->nphy_noise_index] = noise_dbm_ant[core]; if (noise_dbm_ant[core] > noise_dbm) noise_dbm = noise_dbm_ant[core]; } pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); return noise_dbm; } void wlc_phy_noise_sample_intr(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; u16 jssi_aux; u8 channel = 0; s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; if (ISLCNPHY(pi)) { u32 cmplx_pwr, cmplx_pwr0, cmplx_pwr1; u16 lo, hi; s32 pwr_offset_dB, gain_dB; u16 status_0, status_1; jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX); channel = jssi_aux & D11_CURCHANNEL_MAX; lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP0); hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP1); cmplx_pwr0 = (hi << 16) + lo; lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP2); hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP3); cmplx_pwr1 = (hi << 16) + lo; cmplx_pwr = (cmplx_pwr0 + cmplx_pwr1) >> 6; status_0 = 0x44; status_1 = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_0); if ((cmplx_pwr > 0 && cmplx_pwr < 500) && ((status_1 & 0xc000) == 0x4000)) { wlc_phy_compute_dB(&cmplx_pwr, &noise_dbm, pi->pubpi.phy_corenum); pwr_offset_dB = (read_phy_reg(pi, 0x434) & 0xFF); if (pwr_offset_dB > 127) pwr_offset_dB -= 256; noise_dbm += (s8) (pwr_offset_dB - 30); gain_dB = (status_0 & 0x1ff); noise_dbm -= (s8) (gain_dB); } else { noise_dbm = PHY_NOISE_FIXED_VAL_LCNPHY; } } else if (ISNPHY(pi)) { jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX); channel = jssi_aux & D11_CURCHANNEL_MAX; noise_dbm = wlc_phy_noise_read_shmem(pi); } wlc_phy_noise_cb(pi, channel, noise_dbm); } static void wlc_phy_noise_sample_request(struct brcms_phy_pub *pih, u8 reason, u8 ch) { struct brcms_phy *pi = (struct brcms_phy *) pih; s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; bool sampling_in_progress = (pi->phynoise_state != 0); bool wait_for_intr = true; switch (reason) { case PHY_NOISE_SAMPLE_MON: pi->phynoise_chan_watchdog = ch; pi->phynoise_state |= PHY_NOISE_STATE_MON; break; case PHY_NOISE_SAMPLE_EXTERNAL: pi->phynoise_state |= PHY_NOISE_STATE_EXTERNAL; break; default: break; } if (sampling_in_progress) return; pi->phynoise_now = pi->sh->now; if (pi->phy_fixed_noise) { if (ISNPHY(pi)) { pi->nphy_noise_win[WL_ANT_IDX_1][pi->nphy_noise_index] = PHY_NOISE_FIXED_VAL_NPHY; pi->nphy_noise_win[WL_ANT_IDX_2][pi->nphy_noise_index] = PHY_NOISE_FIXED_VAL_NPHY; pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; } else { noise_dbm = PHY_NOISE_FIXED_VAL; } wait_for_intr = false; goto done; } if (ISLCNPHY(pi)) { if (!pi->phynoise_polling || (reason == PHY_NOISE_SAMPLE_EXTERNAL)) { wlapi_bmac_write_shm(pi->sh->physhim, M_JSSI_0, 0); wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP0, 0); wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP1, 0); wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP2, 0); wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP3, 0); bcma_set32(pi->d11core, D11REGOFFS(maccommand), MCMD_BG_NOISE); } else { wlapi_suspend_mac_and_wait(pi->sh->physhim); wlc_lcnphy_deaf_mode(pi, (bool) 0); noise_dbm = (s8) wlc_lcnphy_rx_signal_power(pi, 20); wlc_lcnphy_deaf_mode(pi, (bool) 1); wlapi_enable_mac(pi->sh->physhim); wait_for_intr = false; } } else if (ISNPHY(pi)) { if (!pi->phynoise_polling || (reason == PHY_NOISE_SAMPLE_EXTERNAL)) { wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP0, 0); wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP1, 0); wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP2, 0); wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP3, 0); bcma_set32(pi->d11core, D11REGOFFS(maccommand), MCMD_BG_NOISE); } else { struct phy_iq_est est[PHY_CORE_MAX]; u32 cmplx_pwr[PHY_CORE_MAX]; s8 noise_dbm_ant[PHY_CORE_MAX]; u16 log_num_samps, num_samps, classif_state = 0; u8 wait_time = 32; u8 wait_crs = 0; u8 i; memset((u8 *) est, 0, sizeof(est)); memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr)); memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant)); log_num_samps = PHY_NOISE_SAMPLE_LOG_NUM_NPHY; num_samps = 1 << log_num_samps; wlapi_suspend_mac_and_wait(pi->sh->physhim); classif_state = wlc_phy_classifier_nphy(pi, 0, 0); wlc_phy_classifier_nphy(pi, 3, 0); wlc_phy_rx_iq_est_nphy(pi, est, num_samps, wait_time, wait_crs); wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); wlapi_enable_mac(pi->sh->physhim); for (i = 0; i < pi->pubpi.phy_corenum; i++) cmplx_pwr[i] = (est[i].i_pwr + est[i].q_pwr) >> log_num_samps; wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant); for (i = 0; i < pi->pubpi.phy_corenum; i++) { pi->nphy_noise_win[i][pi->nphy_noise_index] = noise_dbm_ant[i]; if (noise_dbm_ant[i] > noise_dbm) noise_dbm = noise_dbm_ant[i]; } pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); wait_for_intr = false; } } done: if (!wait_for_intr) wlc_phy_noise_cb(pi, ch, noise_dbm); } void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *pih) { u8 channel; channel = CHSPEC_CHANNEL(wlc_phy_chanspec_get(pih)); wlc_phy_noise_sample_request(pih, PHY_NOISE_SAMPLE_EXTERNAL, channel); } static const s8 lcnphy_gain_index_offset_for_pkt_rssi[] = { 8, 8, 8, 8, 8, 8, 8, 9, 10, 8, 8, 7, 7, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0, 0, 0, 0 }; void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_cmplx_pwr_dB, u8 core) { u8 msb, secondmsb, i; u32 tmp; for (i = 0; i < core; i++) { secondmsb = 0; tmp = cmplx_pwr[i]; msb = fls(tmp); if (msb) secondmsb = (u8) ((tmp >> (--msb - 1)) & 1); p_cmplx_pwr_dB[i] = (s8) (3 * msb + 2 * secondmsb); } } int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, struct d11rxhdr *rxh) { int rssi = rxh->PhyRxStatus_1 & PRXS1_JSSI_MASK; uint radioid = pih->radioid; struct brcms_phy *pi = (struct brcms_phy *) pih; if ((pi->sh->corerev >= 11) && !(rxh->RxStatus2 & RXS_PHYRXST_VALID)) { rssi = BRCMS_RSSI_INVALID; goto end; } if (ISLCNPHY(pi)) { u8 gidx = (rxh->PhyRxStatus_2 & 0xFC00) >> 10; struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; if (rssi > 127) rssi -= 256; rssi = rssi + lcnphy_gain_index_offset_for_pkt_rssi[gidx]; if ((rssi > -46) && (gidx > 18)) rssi = rssi + 7; rssi = rssi + pi_lcn->lcnphy_pkteng_rssi_slope; rssi = rssi + 2; } if (ISLCNPHY(pi)) { if (rssi > 127) rssi -= 256; } else if (radioid == BCM2055_ID || radioid == BCM2056_ID || radioid == BCM2057_ID) { rssi = wlc_phy_rssi_compute_nphy(pi, rxh); } end: return rssi; } void wlc_phy_freqtrack_start(struct brcms_phy_pub *pih) { return; } void wlc_phy_freqtrack_end(struct brcms_phy_pub *pih) { return; } void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag) { struct brcms_phy *pi; pi = (struct brcms_phy *) ppi; if (ISLCNPHY(pi)) wlc_lcnphy_deaf_mode(pi, true); else if (ISNPHY(pi)) wlc_nphy_deaf_mode(pi, true); } void wlc_phy_watchdog(struct brcms_phy_pub *pih) { struct brcms_phy *pi = (struct brcms_phy *) pih; bool delay_phy_cal = false; pi->sh->now++; if (!pi->watchdog_override) return; if (!(SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi))) wlc_phy_noise_sample_request((struct brcms_phy_pub *) pi, PHY_NOISE_SAMPLE_MON, CHSPEC_CHANNEL(pi-> radio_chanspec)); if (pi->phynoise_state && (pi->sh->now - pi->phynoise_now) > 5) pi->phynoise_state = 0; if ((!pi->phycal_txpower) || ((pi->sh->now - pi->phycal_txpower) >= pi->sh->fast_timer)) { if (!SCAN_INPROG_PHY(pi) && wlc_phy_cal_txpower_recalc_sw(pi)) pi->phycal_txpower = pi->sh->now; } if ((SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) || ASSOC_INPROG_PHY(pi))) return; if (ISNPHY(pi) && !pi->disable_percal && !delay_phy_cal) { if ((pi->nphy_perical != PHY_PERICAL_DISABLE) && (pi->nphy_perical != PHY_PERICAL_MANUAL) && ((pi->sh->now - pi->nphy_perical_last) >= pi->sh->glacial_timer)) wlc_phy_cal_perical((struct brcms_phy_pub *) pi, PHY_PERICAL_WATCHDOG); wlc_phy_txpwr_papd_cal_nphy(pi); } if (ISLCNPHY(pi)) { if (pi->phy_forcecal || ((pi->sh->now - pi->phy_lastcal) >= pi->sh->glacial_timer)) { if (!(SCAN_RM_IN_PROGRESS(pi) || ASSOC_INPROG_PHY(pi))) wlc_lcnphy_calib_modes( pi, LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL); if (! (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) || ASSOC_INPROG_PHY(pi) || pi->carrier_suppr_disable || pi->disable_percal)) wlc_lcnphy_calib_modes(pi, PHY_PERICAL_WATCHDOG); } } } void wlc_phy_BSSinit(struct brcms_phy_pub *pih, bool bonlyap, int rssi) { struct brcms_phy *pi = (struct brcms_phy *) pih; uint i; uint k; for (i = 0; i < MA_WINDOW_SZ; i++) pi->sh->phy_noise_window[i] = (s8) (rssi & 0xff); if (ISLCNPHY(pi)) { for (i = 0; i < MA_WINDOW_SZ; i++) pi->sh->phy_noise_window[i] = PHY_NOISE_FIXED_VAL_LCNPHY; } pi->sh->phy_noise_index = 0; for (i = 0; i < PHY_NOISE_WINDOW_SZ; i++) { for (k = WL_ANT_IDX_1; k < WL_ANT_RX_MAX; k++) pi->nphy_noise_win[k][i] = PHY_NOISE_FIXED_VAL_NPHY; } pi->nphy_noise_index = 0; } void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag) { *eps_imag = (epsilon >> 13); if (*eps_imag > 0xfff) *eps_imag -= 0x2000; *eps_real = (epsilon & 0x1fff); if (*eps_real > 0xfff) *eps_real -= 0x2000; } void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi) { wlapi_del_timer(pi->phycal_timer); pi->cal_type_override = PHY_PERICAL_AUTO; pi->mphase_cal_phase_id = MPHASE_CAL_STATE_IDLE; pi->mphase_txcal_cmdidx = 0; } static void wlc_phy_cal_perical_mphase_schedule(struct brcms_phy *pi, uint delay) { if ((pi->nphy_perical != PHY_PERICAL_MPHASE) && (pi->nphy_perical != PHY_PERICAL_MANUAL)) return; wlapi_del_timer(pi->phycal_timer); pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT; wlapi_add_timer(pi->phycal_timer, delay, 0); } void wlc_phy_cal_perical(struct brcms_phy_pub *pih, u8 reason) { s16 nphy_currtemp = 0; s16 delta_temp = 0; bool do_periodic_cal = true; struct brcms_phy *pi = (struct brcms_phy *) pih; if (!ISNPHY(pi)) return; if ((pi->nphy_perical == PHY_PERICAL_DISABLE) || (pi->nphy_perical == PHY_PERICAL_MANUAL)) return; switch (reason) { case PHY_PERICAL_DRIVERUP: break; case PHY_PERICAL_PHYINIT: if (pi->nphy_perical == PHY_PERICAL_MPHASE) { if (PHY_PERICAL_MPHASE_PENDING(pi)) wlc_phy_cal_perical_mphase_reset(pi); wlc_phy_cal_perical_mphase_schedule( pi, PHY_PERICAL_INIT_DELAY); } break; case PHY_PERICAL_JOIN_BSS: case PHY_PERICAL_START_IBSS: case PHY_PERICAL_UP_BSS: if ((pi->nphy_perical == PHY_PERICAL_MPHASE) && PHY_PERICAL_MPHASE_PENDING(pi)) wlc_phy_cal_perical_mphase_reset(pi); pi->first_cal_after_assoc = true; pi->cal_type_override = PHY_PERICAL_FULL; if (pi->phycal_tempdelta) pi->nphy_lastcal_temp = wlc_phy_tempsense_nphy(pi); wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_FULL); break; case PHY_PERICAL_WATCHDOG: if (pi->phycal_tempdelta) { nphy_currtemp = wlc_phy_tempsense_nphy(pi); delta_temp = (nphy_currtemp > pi->nphy_lastcal_temp) ? nphy_currtemp - pi->nphy_lastcal_temp : pi->nphy_lastcal_temp - nphy_currtemp; if ((delta_temp < (s16) pi->phycal_tempdelta) && (pi->nphy_txiqlocal_chanspec == pi->radio_chanspec)) do_periodic_cal = false; else pi->nphy_lastcal_temp = nphy_currtemp; } if (do_periodic_cal) { if (pi->nphy_perical == PHY_PERICAL_MPHASE) { if (!PHY_PERICAL_MPHASE_PENDING(pi)) wlc_phy_cal_perical_mphase_schedule( pi, PHY_PERICAL_WDOG_DELAY); } else if (pi->nphy_perical == PHY_PERICAL_SPHASE) wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_AUTO); } break; default: break; } } void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi) { pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT; pi->mphase_txcal_cmdidx = 0; } u8 wlc_phy_nbits(s32 value) { s32 abs_val; u8 nbits = 0; abs_val = abs(value); while ((abs_val >> nbits) > 0) nbits++; return nbits; } void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain) { struct brcms_phy *pi = (struct brcms_phy *) pih; pi->sh->hw_phytxchain = txchain; pi->sh->hw_phyrxchain = rxchain; pi->sh->phytxchain = txchain; pi->sh->phyrxchain = rxchain; pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain); } void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain) { struct brcms_phy *pi = (struct brcms_phy *) pih; pi->sh->phytxchain = txchain; if (ISNPHY(pi)) wlc_phy_rxcore_setstate_nphy(pih, rxchain); pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain); } void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain) { struct brcms_phy *pi = (struct brcms_phy *) pih; *txchain = pi->sh->phytxchain; *rxchain = pi->sh->phyrxchain; } u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih) { s16 nphy_currtemp; u8 active_bitmap; struct brcms_phy *pi = (struct brcms_phy *) pih; active_bitmap = (pi->phy_txcore_heatedup) ? 0x31 : 0x33; if (!pi->watchdog_override) return active_bitmap; if (NREV_GE(pi->pubpi.phy_rev, 6)) { wlapi_suspend_mac_and_wait(pi->sh->physhim); nphy_currtemp = wlc_phy_tempsense_nphy(pi); wlapi_enable_mac(pi->sh->physhim); if (!pi->phy_txcore_heatedup) { if (nphy_currtemp >= pi->phy_txcore_disable_temp) { active_bitmap &= 0xFD; pi->phy_txcore_heatedup = true; } } else { if (nphy_currtemp <= pi->phy_txcore_enable_temp) { active_bitmap |= 0x2; pi->phy_txcore_heatedup = false; } } } return active_bitmap; } s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec) { struct brcms_phy *pi = (struct brcms_phy *) pih; u8 siso_mcs_id, cdd_mcs_id; siso_mcs_id = (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_SISO : TXP_FIRST_MCS_20_SISO; cdd_mcs_id = (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_CDD : TXP_FIRST_MCS_20_CDD; if (pi->tx_power_target[siso_mcs_id] > (pi->tx_power_target[cdd_mcs_id] + 12)) return PHY_TXC1_MODE_SISO; else return PHY_TXC1_MODE_CDD; } const u8 *wlc_phy_get_ofdm_rate_lookup(void) { return ofdm_rate_lookup; } void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode) { if ((pi->sh->chip == BCMA_CHIP_ID_BCM4313) && (pi->sh->boardflags & BFL_FEM)) { if (mode) { u16 txant = 0; txant = wlapi_bmac_get_txant(pi->sh->physhim); if (txant == 1) { mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2); mod_phy_reg(pi, 0x44c, (0x1 << 2), (1) << 2); } ai_cc_reg(pi->sh->sih, offsetof(struct chipcregs, gpiocontrol), ~0x0, 0x0); ai_cc_reg(pi->sh->sih, offsetof(struct chipcregs, gpioout), 0x40, 0x40); ai_cc_reg(pi->sh->sih, offsetof(struct chipcregs, gpioouten), 0x40, 0x40); } else { mod_phy_reg(pi, 0x44c, (0x1 << 2), (0) << 2); mod_phy_reg(pi, 0x44d, (0x1 << 2), (0) << 2); ai_cc_reg(pi->sh->sih, offsetof(struct chipcregs, gpioout), 0x40, 0x00); ai_cc_reg(pi->sh->sih, offsetof(struct chipcregs, gpioouten), 0x40, 0x0); ai_cc_reg(pi->sh->sih, offsetof(struct chipcregs, gpiocontrol), ~0x0, 0x40); } } } void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool ldpc) { return; } void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, s8 *ofdmoffset) { *cckoffset = 0; *ofdmoffset = 0; } s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec) { return rssi; } bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *ppi) { struct brcms_phy *pi = (struct brcms_phy *) ppi; if (ISNPHY(pi)) return wlc_phy_n_txpower_ipa_ison(pi); else return 0; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/0000755000175000017500000000000012026211315023552 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/usb.c0000644000175000017500000011667012026211315024522 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usb_rdl.h" #include "usb.h" #define IOCTL_RESP_TIMEOUT 2000 #define BRCMF_USB_SYNC_TIMEOUT 300 /* ms */ #define BRCMF_USB_DLIMAGE_SPINWAIT 100 /* in unit of ms */ #define BRCMF_USB_DLIMAGE_LIMIT 500 /* spinwait limit (ms) */ #define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */ #define BRCMF_USB_RESETCFG_SPINWAIT 1 /* wait after resetcfg (ms) */ #define BRCMF_USB_NRXQ 50 #define BRCMF_USB_NTXQ 50 #define CONFIGDESC(usb) (&((usb)->actconfig)->desc) #define IFPTR(usb, idx) ((usb)->actconfig->interface[(idx)]) #define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0]) #define IFDESC(usb, idx) IFALTS((usb), (idx)).desc #define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[(ep)]).desc #define CONTROL_IF 0 #define BULK_IF 0 #define BRCMF_USB_CBCTL_WRITE 0 #define BRCMF_USB_CBCTL_READ 1 #define BRCMF_USB_MAX_PKT_SIZE 1600 #define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" #define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" #define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" enum usbdev_suspend_state { USBOS_SUSPEND_STATE_DEVICE_ACTIVE = 0, /* Device is busy, won't allow suspend */ USBOS_SUSPEND_STATE_SUSPEND_PENDING, /* Device is idle, can be * suspended. Wating PM to * suspend the device */ USBOS_SUSPEND_STATE_SUSPENDED /* Device suspended */ }; struct brcmf_usb_image { void *data; u32 len; }; static struct brcmf_usb_image g_image = { NULL, 0 }; struct intr_transfer_buf { u32 notification; u32 reserved; }; struct brcmf_usbdev_info { struct brcmf_usbdev bus_pub; /* MUST BE FIRST */ spinlock_t qlock; struct list_head rx_freeq; struct list_head rx_postq; struct list_head tx_freeq; struct list_head tx_postq; enum usbdev_suspend_state suspend_state; uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; bool activity; int rx_low_watermark; int tx_low_watermark; int tx_high_watermark; int tx_freecount; bool tx_flowblock; struct brcmf_usbreq *tx_reqs; struct brcmf_usbreq *rx_reqs; u8 *image; /* buffer for combine fw and nvram */ int image_len; wait_queue_head_t wait; bool waitdone; int sync_urb_status; struct usb_device *usbdev; struct device *dev; int ctl_in_pipe, ctl_out_pipe; struct urb *ctl_urb; /* URB for control endpoint */ struct usb_ctrlrequest ctl_write; struct usb_ctrlrequest ctl_read; u32 ctl_urb_actual_length; int ctl_urb_status; int ctl_completed; wait_queue_head_t ioctl_resp_wait; wait_queue_head_t ctrl_wait; ulong ctl_op; bool rxctl_deferrespok; struct urb *bulk_urb; /* used for FW download */ struct urb *intr_urb; /* URB for interrupt endpoint */ int intr_size; /* Size of interrupt message */ int interval; /* Interrupt polling interval */ struct intr_transfer_buf intr; /* Data buffer for interrupt endpoint */ }; static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, struct brcmf_usbreq *req); MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac usb driver."); MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac usb cards"); MODULE_LICENSE("Dual BSD/GPL"); static struct brcmf_usbdev *brcmf_usb_get_buspub(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); return bus_if->bus_priv.usb; } static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev) { return brcmf_usb_get_buspub(dev)->devinfo; } static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo, uint *condition, bool *pending) { DECLARE_WAITQUEUE(wait, current); int timeout = IOCTL_RESP_TIMEOUT; /* Convert timeout in millsecond to jiffies */ timeout = msecs_to_jiffies(timeout); /* Wait until control frame is available */ add_wait_queue(&devinfo->ioctl_resp_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); smp_mb(); while (!(*condition) && (!signal_pending(current) && timeout)) { timeout = schedule_timeout(timeout); /* Wait until control frame is available */ smp_mb(); } if (signal_pending(current)) *pending = true; set_current_state(TASK_RUNNING); remove_wait_queue(&devinfo->ioctl_resp_wait, &wait); return timeout; } static int brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo) { if (waitqueue_active(&devinfo->ioctl_resp_wait)) wake_up_interruptible(&devinfo->ioctl_resp_wait); return 0; } static void brcmf_usb_ctl_complete(struct brcmf_usbdev_info *devinfo, int type, int status) { if (unlikely(devinfo == NULL)) return; if (type == BRCMF_USB_CBCTL_READ) { if (status == 0) devinfo->bus_pub.stats.rx_ctlpkts++; else devinfo->bus_pub.stats.rx_ctlerrs++; } else if (type == BRCMF_USB_CBCTL_WRITE) { if (status == 0) devinfo->bus_pub.stats.tx_ctlpkts++; else devinfo->bus_pub.stats.tx_ctlerrs++; } devinfo->ctl_urb_status = status; devinfo->ctl_completed = true; brcmf_usb_ioctl_resp_wake(devinfo); } static void brcmf_usb_ctlread_complete(struct urb *urb) { struct brcmf_usbdev_info *devinfo = (struct brcmf_usbdev_info *)urb->context; devinfo->ctl_urb_actual_length = urb->actual_length; brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_READ, urb->status); } static void brcmf_usb_ctlwrite_complete(struct urb *urb) { struct brcmf_usbdev_info *devinfo = (struct brcmf_usbdev_info *)urb->context; brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_WRITE, urb->status); } static int brcmf_usb_pnp(struct brcmf_usbdev_info *devinfo, uint state) { return 0; } static int brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) { int ret; u16 size; if (devinfo == NULL || buf == NULL || len == 0 || devinfo->ctl_urb == NULL) return -EINVAL; /* If the USB/HSIC bus in sleep state, wake it up */ if (devinfo->suspend_state == USBOS_SUSPEND_STATE_SUSPENDED) if (brcmf_usb_pnp(devinfo, BCMFMAC_USB_PNP_RESUME) != 0) { brcmf_dbg(ERROR, "Could not Resume the bus!\n"); return -EIO; } devinfo->activity = true; size = len; devinfo->ctl_write.wLength = cpu_to_le16p(&size); devinfo->ctl_urb->transfer_buffer_length = size; devinfo->ctl_urb_status = 0; devinfo->ctl_urb_actual_length = 0; usb_fill_control_urb(devinfo->ctl_urb, devinfo->usbdev, devinfo->ctl_out_pipe, (unsigned char *) &devinfo->ctl_write, buf, size, (usb_complete_t)brcmf_usb_ctlwrite_complete, devinfo); ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); if (ret < 0) brcmf_dbg(ERROR, "usb_submit_urb failed %d\n", ret); return ret; } static int brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) { int ret; u16 size; if ((devinfo == NULL) || (buf == NULL) || (len == 0) || (devinfo->ctl_urb == NULL)) return -EINVAL; size = len; devinfo->ctl_read.wLength = cpu_to_le16p(&size); devinfo->ctl_urb->transfer_buffer_length = size; if (devinfo->rxctl_deferrespok) { /* BMAC model */ devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE; devinfo->ctl_read.bRequest = DL_DEFER_RESP_OK; } else { /* full dongle model */ devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; devinfo->ctl_read.bRequest = 1; } usb_fill_control_urb(devinfo->ctl_urb, devinfo->usbdev, devinfo->ctl_in_pipe, (unsigned char *) &devinfo->ctl_read, buf, size, (usb_complete_t)brcmf_usb_ctlread_complete, devinfo); ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); if (ret < 0) brcmf_dbg(ERROR, "usb_submit_urb failed %d\n", ret); return ret; } static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len) { int err = 0; int timeout = 0; bool pending; struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { /* TODO: handle suspend/resume */ return -EIO; } if (test_and_set_bit(0, &devinfo->ctl_op)) return -EIO; devinfo->ctl_completed = false; err = brcmf_usb_send_ctl(devinfo, buf, len); if (err) { brcmf_dbg(ERROR, "fail %d bytes: %d\n", err, len); return err; } timeout = brcmf_usb_ioctl_resp_wait(devinfo, &devinfo->ctl_completed, &pending); clear_bit(0, &devinfo->ctl_op); if (!timeout) { brcmf_dbg(ERROR, "Txctl wait timed out\n"); err = -EIO; } return err; } static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) { int err = 0; int timeout = 0; bool pending; struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { /* TODO: handle suspend/resume */ return -EIO; } if (test_and_set_bit(0, &devinfo->ctl_op)) return -EIO; err = brcmf_usb_recv_ctl(devinfo, buf, len); if (err) { brcmf_dbg(ERROR, "fail %d bytes: %d\n", err, len); return err; } devinfo->ctl_completed = false; timeout = brcmf_usb_ioctl_resp_wait(devinfo, &devinfo->ctl_completed, &pending); err = devinfo->ctl_urb_status; clear_bit(0, &devinfo->ctl_op); if (!timeout) { brcmf_dbg(ERROR, "rxctl wait timed out\n"); err = -EIO; } if (!err) return devinfo->ctl_urb_actual_length; else return err; } static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo, struct list_head *q, int *counter) { unsigned long flags; struct brcmf_usbreq *req; spin_lock_irqsave(&devinfo->qlock, flags); if (list_empty(q)) { spin_unlock_irqrestore(&devinfo->qlock, flags); return NULL; } req = list_entry(q->next, struct brcmf_usbreq, list); list_del_init(q->next); if (counter) (*counter)--; spin_unlock_irqrestore(&devinfo->qlock, flags); return req; } static void brcmf_usb_enq(struct brcmf_usbdev_info *devinfo, struct list_head *q, struct brcmf_usbreq *req, int *counter) { unsigned long flags; spin_lock_irqsave(&devinfo->qlock, flags); list_add_tail(&req->list, q); if (counter) (*counter)++; spin_unlock_irqrestore(&devinfo->qlock, flags); } static struct brcmf_usbreq * brcmf_usbdev_qinit(struct list_head *q, int qsize) { int i; struct brcmf_usbreq *req, *reqs; reqs = kzalloc(sizeof(struct brcmf_usbreq) * qsize, GFP_ATOMIC); if (reqs == NULL) { brcmf_dbg(ERROR, "fail to allocate memory!\n"); return NULL; } req = reqs; for (i = 0; i < qsize; i++) { req->urb = usb_alloc_urb(0, GFP_ATOMIC); if (!req->urb) goto fail; INIT_LIST_HEAD(&req->list); list_add_tail(&req->list, q); req++; } return reqs; fail: brcmf_dbg(ERROR, "fail!\n"); while (!list_empty(q)) { req = list_entry(q->next, struct brcmf_usbreq, list); if (req && req->urb) usb_free_urb(req->urb); list_del(q->next); } return NULL; } static void brcmf_usb_free_q(struct list_head *q, bool pending) { struct brcmf_usbreq *req, *next; int i = 0; list_for_each_entry_safe(req, next, q, list) { if (!req->urb) { brcmf_dbg(ERROR, "bad req\n"); break; } i++; if (pending) { usb_kill_urb(req->urb); } else { usb_free_urb(req->urb); list_del_init(&req->list); } } } static void brcmf_usb_del_fromq(struct brcmf_usbdev_info *devinfo, struct brcmf_usbreq *req) { unsigned long flags; spin_lock_irqsave(&devinfo->qlock, flags); list_del_init(&req->list); spin_unlock_irqrestore(&devinfo->qlock, flags); } static void brcmf_usb_tx_complete(struct urb *urb) { struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; brcmf_usb_del_fromq(devinfo, req); if (urb->status == 0) devinfo->bus_pub.bus->dstats.tx_packets++; else devinfo->bus_pub.bus->dstats.tx_errors++; brcmu_pkt_buf_free_skb(req->skb); req->skb = NULL; brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount); if (devinfo->tx_freecount > devinfo->tx_high_watermark && devinfo->tx_flowblock) { brcmf_txflowblock(devinfo->dev, false); devinfo->tx_flowblock = false; } } static void brcmf_usb_rx_complete(struct urb *urb) { struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; struct sk_buff *skb; int ifidx = 0; brcmf_usb_del_fromq(devinfo, req); skb = req->skb; req->skb = NULL; if (urb->status == 0) { devinfo->bus_pub.bus->dstats.rx_packets++; } else { devinfo->bus_pub.bus->dstats.rx_errors++; brcmu_pkt_buf_free_skb(skb); brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); return; } if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) { skb_put(skb, urb->actual_length); if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) { brcmf_dbg(ERROR, "rx protocol error\n"); brcmu_pkt_buf_free_skb(skb); devinfo->bus_pub.bus->dstats.rx_errors++; } else brcmf_rx_packet(devinfo->dev, ifidx, skb); brcmf_usb_rx_refill(devinfo, req); } else { brcmu_pkt_buf_free_skb(skb); brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); } return; } static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, struct brcmf_usbreq *req) { struct sk_buff *skb; int ret; if (!req || !devinfo) return; skb = dev_alloc_skb(devinfo->bus_pub.bus_mtu); if (!skb) { brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); return; } req->skb = skb; usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe, skb->data, skb_tailroom(skb), brcmf_usb_rx_complete, req); req->devinfo = devinfo; brcmf_usb_enq(devinfo, &devinfo->rx_postq, req, NULL); ret = usb_submit_urb(req->urb, GFP_ATOMIC); if (ret) { brcmf_usb_del_fromq(devinfo, req); brcmu_pkt_buf_free_skb(req->skb); req->skb = NULL; brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); } return; } static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo) { struct brcmf_usbreq *req; if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { brcmf_dbg(ERROR, "bus is not up\n"); return; } while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL) brcmf_usb_rx_refill(devinfo, req); } static void brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) { struct brcmf_bus *bcmf_bus = devinfo->bus_pub.bus; int old_state; if (devinfo->bus_pub.state == state) return; old_state = devinfo->bus_pub.state; brcmf_dbg(TRACE, "dbus state change from %d to to %d\n", old_state, state); /* Don't update state if it's PnP firmware re-download */ if (state != BCMFMAC_USB_STATE_PNP_FWDL) /* TODO */ devinfo->bus_pub.state = state; if ((old_state == BCMFMAC_USB_STATE_SLEEP) && (state == BCMFMAC_USB_STATE_UP)) { brcmf_usb_rx_fill_all(devinfo); } /* update state of upper layer */ if (state == BCMFMAC_USB_STATE_DOWN) { brcmf_dbg(INFO, "DBUS is down\n"); bcmf_bus->state = BRCMF_BUS_DOWN; } else { brcmf_dbg(INFO, "DBUS current state=%d\n", state); } } static void brcmf_usb_intr_complete(struct urb *urb) { struct brcmf_usbdev_info *devinfo = (struct brcmf_usbdev_info *)urb->context; bool killed; if (devinfo == NULL) return; if (unlikely(urb->status)) { if (devinfo->suspend_state == USBOS_SUSPEND_STATE_SUSPEND_PENDING) killed = true; if ((urb->status == -ENOENT && (!killed)) || urb->status == -ESHUTDOWN || urb->status == -ENODEV) { brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN); } } if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_DOWN) { brcmf_dbg(ERROR, "intr cb when DBUS down, ignoring\n"); return; } if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); } static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); struct brcmf_usbreq *req; int ret; if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { /* TODO: handle suspend/resume */ return -EIO; } req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq, &devinfo->tx_freecount); if (!req) { brcmu_pkt_buf_free_skb(skb); brcmf_dbg(ERROR, "no req to send\n"); return -ENOMEM; } req->skb = skb; req->devinfo = devinfo; usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe, skb->data, skb->len, brcmf_usb_tx_complete, req); req->urb->transfer_flags |= URB_ZERO_PACKET; brcmf_usb_enq(devinfo, &devinfo->tx_postq, req, NULL); ret = usb_submit_urb(req->urb, GFP_ATOMIC); if (ret) { brcmf_dbg(ERROR, "brcmf_usb_tx usb_submit_urb FAILED\n"); brcmf_usb_del_fromq(devinfo, req); brcmu_pkt_buf_free_skb(req->skb); req->skb = NULL; brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount); } else { if (devinfo->tx_freecount < devinfo->tx_low_watermark && !devinfo->tx_flowblock) { brcmf_txflowblock(dev, true); devinfo->tx_flowblock = true; } } return ret; } static int brcmf_usb_up(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); u16 ifnum; if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) return 0; /* If the USB/HSIC bus in sleep state, wake it up */ if (devinfo->suspend_state == USBOS_SUSPEND_STATE_SUSPENDED) { if (brcmf_usb_pnp(devinfo, BCMFMAC_USB_PNP_RESUME) != 0) { brcmf_dbg(ERROR, "Could not Resume the bus!\n"); return -EIO; } } devinfo->activity = true; /* Success, indicate devinfo is fully up */ brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_UP); if (devinfo->intr_urb) { int ret; usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev, devinfo->intr_pipe, &devinfo->intr, devinfo->intr_size, (usb_complete_t)brcmf_usb_intr_complete, devinfo, devinfo->interval); ret = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); if (ret) { brcmf_dbg(ERROR, "USB_SUBMIT_URB failed with status %d\n", ret); return -EINVAL; } } if (devinfo->ctl_urb) { devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); ifnum = IFDESC(devinfo->usbdev, CONTROL_IF).bInterfaceNumber; /* CTL Write */ devinfo->ctl_write.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; devinfo->ctl_write.bRequest = 0; devinfo->ctl_write.wValue = cpu_to_le16(0); devinfo->ctl_write.wIndex = cpu_to_le16p(&ifnum); /* CTL Read */ devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; devinfo->ctl_read.bRequest = 1; devinfo->ctl_read.wValue = cpu_to_le16(0); devinfo->ctl_read.wIndex = cpu_to_le16p(&ifnum); } brcmf_usb_rx_fill_all(devinfo); return 0; } static void brcmf_usb_down(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); if (devinfo == NULL) return; brcmf_dbg(TRACE, "enter\n"); if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_DOWN) return; brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN); if (devinfo->intr_urb) usb_kill_urb(devinfo->intr_urb); if (devinfo->ctl_urb) usb_kill_urb(devinfo->ctl_urb); if (devinfo->bulk_urb) usb_kill_urb(devinfo->bulk_urb); brcmf_usb_free_q(&devinfo->tx_postq, true); brcmf_usb_free_q(&devinfo->rx_postq, true); } static int brcmf_usb_sync_wait(struct brcmf_usbdev_info *devinfo, u16 time) { int ret; int err = 0; int ms = time; ret = wait_event_interruptible_timeout(devinfo->wait, devinfo->waitdone == true, (ms * HZ / 1000)); if ((devinfo->waitdone == false) || (devinfo->sync_urb_status)) { brcmf_dbg(ERROR, "timeout(%d) or urb err=%d\n", ret, devinfo->sync_urb_status); err = -EINVAL; } devinfo->waitdone = false; return err; } static void brcmf_usb_sync_complete(struct urb *urb) { struct brcmf_usbdev_info *devinfo = (struct brcmf_usbdev_info *)urb->context; devinfo->waitdone = true; wake_up_interruptible(&devinfo->wait); devinfo->sync_urb_status = urb->status; } static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, void *buffer, int buflen) { int ret = 0; char *tmpbuf; u16 size; if ((!devinfo) || (devinfo->ctl_urb == NULL)) return false; tmpbuf = kmalloc(buflen, GFP_ATOMIC); if (!tmpbuf) return false; size = buflen; devinfo->ctl_urb->transfer_buffer_length = size; devinfo->ctl_read.wLength = cpu_to_le16p(&size); devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE; devinfo->ctl_read.bRequest = cmd; usb_fill_control_urb(devinfo->ctl_urb, devinfo->usbdev, usb_rcvctrlpipe(devinfo->usbdev, 0), (unsigned char *) &devinfo->ctl_read, (void *) tmpbuf, size, (usb_complete_t)brcmf_usb_sync_complete, devinfo); ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); if (ret < 0) { brcmf_dbg(ERROR, "usb_submit_urb failed %d\n", ret); kfree(tmpbuf); return false; } ret = brcmf_usb_sync_wait(devinfo, BRCMF_USB_SYNC_TIMEOUT); memcpy(buffer, tmpbuf, buflen); kfree(tmpbuf); return (ret == 0); } static bool brcmf_usb_dlneeded(struct brcmf_usbdev_info *devinfo) { struct bootrom_id_le id; u32 chipid, chiprev; brcmf_dbg(TRACE, "enter\n"); if (devinfo == NULL) return false; /* Check if firmware downloaded already by querying runtime ID */ id.chip = cpu_to_le32(0xDEAD); brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(struct bootrom_id_le)); chipid = le32_to_cpu(id.chip); chiprev = le32_to_cpu(id.chiprev); if ((chipid & 0x4300) == 0x4300) brcmf_dbg(INFO, "chip %x rev 0x%x\n", chipid, chiprev); else brcmf_dbg(INFO, "chip %d rev 0x%x\n", chipid, chiprev); if (chipid == BRCMF_POSTBOOT_ID) { brcmf_dbg(INFO, "firmware already downloaded\n"); brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(struct bootrom_id_le)); return false; } else { devinfo->bus_pub.devid = chipid; devinfo->bus_pub.chiprev = chiprev; } return true; } static int brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) { struct bootrom_id_le id; u16 wait = 0, wait_time; brcmf_dbg(TRACE, "enter\n"); if (devinfo == NULL) return -EINVAL; /* Give dongle chance to boot */ wait_time = BRCMF_USB_DLIMAGE_SPINWAIT; while (wait < BRCMF_USB_DLIMAGE_LIMIT) { mdelay(wait_time); wait += wait_time; id.chip = cpu_to_le32(0xDEAD); /* Get the ID */ brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(struct bootrom_id_le)); if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) break; } if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) { brcmf_dbg(INFO, "download done %d ms postboot chip 0x%x/rev 0x%x\n", wait, le32_to_cpu(id.chip), le32_to_cpu(id.chiprev)); brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(struct bootrom_id_le)); /* XXX this wait may not be necessary */ mdelay(BRCMF_USB_RESETCFG_SPINWAIT); return 0; } else { brcmf_dbg(ERROR, "Cannot talk to Dongle. Firmware is not UP, %d ms\n", wait); return -EINVAL; } } static int brcmf_usb_dl_send_bulk(struct brcmf_usbdev_info *devinfo, void *buffer, int len) { int ret; if ((devinfo == NULL) || (devinfo->bulk_urb == NULL)) return -EINVAL; /* Prepare the URB */ usb_fill_bulk_urb(devinfo->bulk_urb, devinfo->usbdev, devinfo->tx_pipe, buffer, len, (usb_complete_t)brcmf_usb_sync_complete, devinfo); devinfo->bulk_urb->transfer_flags |= URB_ZERO_PACKET; ret = usb_submit_urb(devinfo->bulk_urb, GFP_ATOMIC); if (ret) { brcmf_dbg(ERROR, "usb_submit_urb failed %d\n", ret); return ret; } ret = brcmf_usb_sync_wait(devinfo, BRCMF_USB_SYNC_TIMEOUT); return ret; } static int brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) { unsigned int sendlen, sent, dllen; char *bulkchunk = NULL, *dlpos; struct rdl_state_le state; u32 rdlstate, rdlbytes; int err = 0; brcmf_dbg(TRACE, "fw %p, len %d\n", fw, fwlen); bulkchunk = kmalloc(RDL_CHUNK, GFP_ATOMIC); if (bulkchunk == NULL) { err = -ENOMEM; goto fail; } /* 1) Prepare USB boot loader for runtime image */ brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(struct rdl_state_le)); rdlstate = le32_to_cpu(state.state); rdlbytes = le32_to_cpu(state.bytes); /* 2) Check we are in the Waiting state */ if (rdlstate != DL_WAITING) { brcmf_dbg(ERROR, "Failed to DL_START\n"); err = -EINVAL; goto fail; } sent = 0; dlpos = fw; dllen = fwlen; /* Get chip id and rev */ while (rdlbytes != dllen) { /* Wait until the usb device reports it received all * the bytes we sent */ if ((rdlbytes == sent) && (rdlbytes != dllen)) { if ((dllen-sent) < RDL_CHUNK) sendlen = dllen-sent; else sendlen = RDL_CHUNK; /* simply avoid having to send a ZLP by ensuring we * never have an even * multiple of 64 */ if (!(sendlen % 64)) sendlen -= 4; /* send data */ memcpy(bulkchunk, dlpos, sendlen); if (brcmf_usb_dl_send_bulk(devinfo, bulkchunk, sendlen)) { brcmf_dbg(ERROR, "send_bulk failed\n"); err = -EINVAL; goto fail; } dlpos += sendlen; sent += sendlen; } if (!brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(struct rdl_state_le))) { brcmf_dbg(ERROR, "DL_GETSTATE Failed xxxx\n"); err = -EINVAL; goto fail; } rdlstate = le32_to_cpu(state.state); rdlbytes = le32_to_cpu(state.bytes); /* restart if an error is reported */ if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) { brcmf_dbg(ERROR, "Bad Hdr or Bad CRC state %d\n", rdlstate); err = -EINVAL; goto fail; } } fail: kfree(bulkchunk); brcmf_dbg(TRACE, "err=%d\n", err); return err; } static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len) { int err; brcmf_dbg(TRACE, "enter\n"); if (devinfo == NULL) return -EINVAL; if (devinfo->bus_pub.devid == 0xDEAD) return -EINVAL; err = brcmf_usb_dl_writeimage(devinfo, fw, len); if (err == 0) devinfo->bus_pub.state = BCMFMAC_USB_STATE_DL_DONE; else devinfo->bus_pub.state = BCMFMAC_USB_STATE_DL_PENDING; brcmf_dbg(TRACE, "exit: err=%d\n", err); return err; } static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) { struct rdl_state_le state; brcmf_dbg(TRACE, "enter\n"); if (!devinfo) return -EINVAL; if (devinfo->bus_pub.devid == 0xDEAD) return -EINVAL; /* Check we are runnable */ brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(struct rdl_state_le)); /* Start the image */ if (state.state == cpu_to_le32(DL_RUNNABLE)) { if (!brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(struct rdl_state_le))) return -ENODEV; if (brcmf_usb_resetcfg(devinfo)) return -ENODEV; /* The Dongle may go for re-enumeration. */ } else { brcmf_dbg(ERROR, "Dongle not runnable\n"); return -EINVAL; } brcmf_dbg(TRACE, "exit\n"); return 0; } static bool brcmf_usb_chip_support(int chipid, int chiprev) { switch(chipid) { case 43143: return true; case 43235: case 43236: case 43238: return (chiprev == 3); case 43242: return true; default: break; } return false; } static int brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) { int devid, chiprev; int err; brcmf_dbg(TRACE, "enter\n"); if (devinfo == NULL) return -ENODEV; devid = devinfo->bus_pub.devid; chiprev = devinfo->bus_pub.chiprev; if (!brcmf_usb_chip_support(devid, chiprev)) { brcmf_dbg(ERROR, "unsupported chip %d rev %d\n", devid, chiprev); return -EINVAL; } if (!devinfo->image) { brcmf_dbg(ERROR, "No firmware!\n"); return -ENOENT; } err = brcmf_usb_dlstart(devinfo, devinfo->image, devinfo->image_len); if (err == 0) err = brcmf_usb_dlrun(devinfo); return err; } static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) { brcmf_dbg(TRACE, "devinfo %p\n", devinfo); /* store the image globally */ g_image.data = devinfo->image; g_image.len = devinfo->image_len; /* free the URBS */ brcmf_usb_free_q(&devinfo->rx_freeq, false); brcmf_usb_free_q(&devinfo->tx_freeq, false); usb_free_urb(devinfo->intr_urb); usb_free_urb(devinfo->ctl_urb); usb_free_urb(devinfo->bulk_urb); kfree(devinfo->tx_reqs); kfree(devinfo->rx_reqs); } #define TRX_MAGIC 0x30524448 /* "HDR0" */ #define TRX_VERSION 1 /* Version 1 */ #define TRX_MAX_LEN 0x3B0000 /* Max length */ #define TRX_NO_HEADER 1 /* Do not write TRX header */ #define TRX_MAX_OFFSET 3 /* Max number of individual files */ #define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed image */ struct trx_header_le { __le32 magic; /* "HDR0" */ __le32 len; /* Length of file including header */ __le32 crc32; /* CRC from flag_version to end of file */ __le32 flag_version; /* 0:15 flags, 16:31 version */ __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of * header */ }; static int check_file(const u8 *headers) { struct trx_header_le *trx; int actual_len = -1; /* Extract trx header */ trx = (struct trx_header_le *) headers; if (trx->magic != cpu_to_le32(TRX_MAGIC)) return -1; headers += sizeof(struct trx_header_le); if (le32_to_cpu(trx->flag_version) & TRX_UNCOMP_IMAGE) { actual_len = le32_to_cpu(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]); return actual_len + sizeof(struct trx_header_le); } return -1; } static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) { s8 *fwname; const struct firmware *fw; int err; devinfo->image = g_image.data; devinfo->image_len = g_image.len; /* * if we have an image we can leave here. */ if (devinfo->image) return 0; switch (devinfo->bus_pub.devid) { case 43143: fwname = BRCMF_USB_43143_FW_NAME; break; case 43235: case 43236: case 43238: fwname = BRCMF_USB_43236_FW_NAME; break; case 43242: fwname = BRCMF_USB_43242_FW_NAME; break; default: return -EINVAL; break; } err = request_firmware(&fw, fwname, devinfo->dev); if (!fw) { brcmf_dbg(ERROR, "fail to request firmware %s\n", fwname); return err; } if (check_file(fw->data) < 0) { brcmf_dbg(ERROR, "invalid firmware %s\n", fwname); return -EINVAL; } devinfo->image = vmalloc(fw->size); /* plus nvram */ if (!devinfo->image) return -ENOMEM; memcpy(devinfo->image, fw->data, fw->size); devinfo->image_len = fw->size; release_firmware(fw); return 0; } static struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, int nrxq, int ntxq) { devinfo->bus_pub.nrxq = nrxq; devinfo->rx_low_watermark = nrxq / 2; devinfo->bus_pub.devinfo = devinfo; devinfo->bus_pub.ntxq = ntxq; /* flow control when too many tx urbs posted */ devinfo->tx_low_watermark = ntxq / 4; devinfo->tx_high_watermark = devinfo->tx_low_watermark * 3; devinfo->bus_pub.bus_mtu = BRCMF_USB_MAX_PKT_SIZE; /* Initialize other structure content */ init_waitqueue_head(&devinfo->ioctl_resp_wait); /* Initialize the spinlocks */ spin_lock_init(&devinfo->qlock); INIT_LIST_HEAD(&devinfo->rx_freeq); INIT_LIST_HEAD(&devinfo->rx_postq); INIT_LIST_HEAD(&devinfo->tx_freeq); INIT_LIST_HEAD(&devinfo->tx_postq); devinfo->tx_flowblock = false; devinfo->rx_reqs = brcmf_usbdev_qinit(&devinfo->rx_freeq, nrxq); if (!devinfo->rx_reqs) goto error; devinfo->tx_reqs = brcmf_usbdev_qinit(&devinfo->tx_freeq, ntxq); if (!devinfo->tx_reqs) goto error; devinfo->tx_freecount = ntxq; devinfo->intr_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!devinfo->intr_urb) { brcmf_dbg(ERROR, "usb_alloc_urb (intr) failed\n"); goto error; } devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!devinfo->ctl_urb) { brcmf_dbg(ERROR, "usb_alloc_urb (ctl) failed\n"); goto error; } devinfo->rxctl_deferrespok = 0; devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!devinfo->bulk_urb) { brcmf_dbg(ERROR, "usb_alloc_urb (bulk) failed\n"); goto error; } init_waitqueue_head(&devinfo->wait); if (!brcmf_usb_dlneeded(devinfo)) return &devinfo->bus_pub; brcmf_dbg(TRACE, "start fw downloading\n"); if (brcmf_usb_get_fw(devinfo)) goto error; if (brcmf_usb_fw_download(devinfo)) goto error; return &devinfo->bus_pub; error: brcmf_dbg(ERROR, "failed!\n"); brcmf_usb_detach(devinfo); return NULL; } static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo, const char *desc, u32 bustype, u32 hdrlen) { struct brcmf_bus *bus = NULL; struct brcmf_usbdev *bus_pub = NULL; int ret; struct device *dev = devinfo->dev; bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); if (!bus_pub) { ret = -ENODEV; goto fail; } bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC); if (!bus) { ret = -ENOMEM; goto fail; } bus_pub->bus = bus; bus->brcmf_bus_txdata = brcmf_usb_tx; bus->brcmf_bus_init = brcmf_usb_up; bus->brcmf_bus_stop = brcmf_usb_down; bus->brcmf_bus_txctl = brcmf_usb_tx_ctlpkt; bus->brcmf_bus_rxctl = brcmf_usb_rx_ctlpkt; bus->type = bustype; bus->bus_priv.usb = bus_pub; dev_set_drvdata(dev, bus); /* Attach to the common driver interface */ ret = brcmf_attach(hdrlen, dev); if (ret) { brcmf_dbg(ERROR, "dhd_attach failed\n"); goto fail; } ret = brcmf_bus_start(dev); if (ret == -ENOLINK) { brcmf_dbg(ERROR, "dongle is not responding\n"); brcmf_detach(dev); goto fail; } return 0; fail: /* Release resources in reverse order */ kfree(bus); brcmf_usb_detach(devinfo); return ret; } static void brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) { if (!devinfo) return; brcmf_dbg(TRACE, "enter: bus_pub %p\n", devinfo); brcmf_detach(devinfo->dev); kfree(devinfo->bus_pub.bus); brcmf_usb_detach(devinfo); } static int brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { int ep; struct usb_endpoint_descriptor *endpoint; int ret = 0; struct usb_device *usb = interface_to_usbdev(intf); int num_of_eps; u8 endpoint_num; struct brcmf_usbdev_info *devinfo; brcmf_dbg(TRACE, "enter\n"); devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC); if (devinfo == NULL) return -ENOMEM; devinfo->usbdev = usb; devinfo->dev = &usb->dev; usb_set_intfdata(intf, devinfo); /* Check that the device supports only one configuration */ if (usb->descriptor.bNumConfigurations != 1) { ret = -1; goto fail; } if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { ret = -1; goto fail; } /* * Only the BDC interface configuration is supported: * Device class: USB_CLASS_VENDOR_SPEC * if0 class: USB_CLASS_VENDOR_SPEC * if0/ep0: control * if0/ep1: bulk in * if0/ep2: bulk out (ok if swapped with bulk in) */ if (CONFIGDESC(usb)->bNumInterfaces != 1) { ret = -1; goto fail; } /* Check interface */ if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC || IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 || IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) { brcmf_dbg(ERROR, "invalid control interface: class %d, subclass %d, proto %d\n", IFDESC(usb, CONTROL_IF).bInterfaceClass, IFDESC(usb, CONTROL_IF).bInterfaceSubClass, IFDESC(usb, CONTROL_IF).bInterfaceProtocol); ret = -1; goto fail; } /* Check control endpoint */ endpoint = &IFEPDESC(usb, CONTROL_IF, 0); if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) { brcmf_dbg(ERROR, "invalid control endpoint %d\n", endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); ret = -1; goto fail; } endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; devinfo->intr_pipe = usb_rcvintpipe(usb, endpoint_num); devinfo->rx_pipe = 0; devinfo->rx_pipe2 = 0; devinfo->tx_pipe = 0; num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1; /* Check data endpoints and get pipes */ for (ep = 1; ep <= num_of_eps; ep++) { endpoint = &IFEPDESC(usb, BULK_IF, ep); if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { brcmf_dbg(ERROR, "invalid data endpoint %d\n", ep); ret = -1; goto fail; } endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { if (!devinfo->rx_pipe) { devinfo->rx_pipe = usb_rcvbulkpipe(usb, endpoint_num); } else { devinfo->rx_pipe2 = usb_rcvbulkpipe(usb, endpoint_num); } } else { devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num); } } /* Allocate interrupt URB and data buffer */ /* RNDIS says 8-byte intr, our old drivers used 4-byte */ if (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == cpu_to_le16(16)) devinfo->intr_size = 8; else devinfo->intr_size = 4; devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; if (usb->speed == USB_SPEED_HIGH) brcmf_dbg(INFO, "Broadcom high speed USB wireless device detected\n"); else brcmf_dbg(INFO, "Broadcom full speed USB wireless device detected\n"); ret = brcmf_usb_probe_cb(devinfo, "", USB_BUS, 0); if (ret) goto fail; /* Success */ return 0; fail: brcmf_dbg(ERROR, "failed with errno %d\n", ret); kfree(devinfo); usb_set_intfdata(intf, NULL); return ret; } static void brcmf_usb_disconnect(struct usb_interface *intf) { struct brcmf_usbdev_info *devinfo; brcmf_dbg(TRACE, "enter\n"); devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf); brcmf_usb_disconnect_cb(devinfo); kfree(devinfo); } /* * only need to signal the bus being down and update the suspend state. */ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); brcmf_dbg(TRACE, "enter\n"); devinfo->bus_pub.state = BCMFMAC_USB_STATE_DOWN; devinfo->suspend_state = USBOS_SUSPEND_STATE_SUSPENDED; return 0; } /* * mark suspend state active and crank up the bus. */ static int brcmf_usb_resume(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); brcmf_dbg(TRACE, "enter\n"); devinfo->suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE; brcmf_bus_start(&usb->dev); return 0; } #define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c #define BRCMF_USB_DEVICE_ID_43143 0xbd1e #define BRCMF_USB_DEVICE_ID_43236 0xbd17 #define BRCMF_USB_DEVICE_ID_43242 0xbd1f #define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc static struct usb_device_id brcmf_usb_devid_table[] = { { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) }, { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) }, { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) }, /* special entry for device with firmware loaded and running */ { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) }, { } }; MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); /* TODO: suspend and resume entries */ static struct usb_driver brcmf_usbdrvr = { .name = KBUILD_MODNAME, .probe = brcmf_usb_probe, .disconnect = brcmf_usb_disconnect, .id_table = brcmf_usb_devid_table, .suspend = brcmf_usb_suspend, .resume = brcmf_usb_resume, .supports_autosuspend = 1, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; void brcmf_usb_exit(void) { usb_deregister(&brcmf_usbdrvr); vfree(g_image.data); g_image.data = NULL; g_image.len = 0; } void brcmf_usb_init(void) { usb_register(&brcmf_usbdrvr); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c0000644000175000017500000034110712026211315025501 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sdio_host.h" #include "sdio_chip.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ #ifdef DEBUG #define BRCMF_TRAP_INFO_SIZE 80 #define CBUF_LEN (128) /* Device console log buffer state */ #define CONSOLE_BUFFER_MAX 2024 struct rte_log_le { __le32 buf; /* Can't be pointer on (64-bit) hosts */ __le32 buf_size; __le32 idx; char *_buf_compat; /* Redundant pointer for backward compat. */ }; struct rte_console { /* Virtual UART * When there is no UART (e.g. Quickturn), * the host should write a complete * input line directly into cbuf and then write * the length into vcons_in. * This may also be used when there is a real UART * (at risk of conflicting with * the real UART). vcons_out is currently unused. */ uint vcons_in; uint vcons_out; /* Output (logging) buffer * Console output is written to a ring buffer log_buf at index log_idx. * The host may read the output when it sees log_idx advance. * Output will be lost if the output wraps around faster than the host * polls. */ struct rte_log_le log_le; /* Console input line buffer * Characters are read one at a time into cbuf * until is received, then * the buffer is processed as a command line. * Also used for virtual UART. */ uint cbuf_idx; char cbuf[CBUF_LEN]; }; #endif /* DEBUG */ #include #include "dhd_bus.h" #include "dhd_dbg.h" #define TXQLEN 2048 /* bulk tx queue length */ #define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */ #define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */ #define PRIOMASK 7 #define TXRETRIES 2 /* # of retries for tx frames */ #define BRCMF_RXBOUND 50 /* Default for max rx frames in one scheduling */ #define BRCMF_TXBOUND 20 /* Default for max tx frames in one scheduling */ #define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ #define MEMBLOCK 2048 /* Block size used for downloading of dongle image */ #define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */ #define BRCMF_FIRSTREAD (1 << 6) /* SBSDIO_DEVICE_CTL */ /* 1: device will assert busy signal when receiving CMD53 */ #define SBSDIO_DEVCTL_SETBUSY 0x01 /* 1: assertion of sdio interrupt is synchronous to the sdio clock */ #define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 /* 1: mask all interrupts to host except the chipActive (rev 8) */ #define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 /* 1: isolate internal sdio signals, put external pads in tri-state; requires * sdio bus power cycle to clear (rev 9) */ #define SBSDIO_DEVCTL_PADS_ISO 0x08 /* Force SD->SB reset mapping (rev 11) */ #define SBSDIO_DEVCTL_SB_RST_CTL 0x30 /* Determined by CoreControl bit */ #define SBSDIO_DEVCTL_RST_CORECTL 0x00 /* Force backplane reset */ #define SBSDIO_DEVCTL_RST_BPRESET 0x10 /* Force no backplane reset */ #define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 /* direct(mapped) cis space */ /* MAPPED common CIS address */ #define SBSDIO_CIS_BASE_COMMON 0x1000 /* maximum bytes in one CIS */ #define SBSDIO_CIS_SIZE_LIMIT 0x200 /* cis offset addr is < 17 bits */ #define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF /* manfid tuple length, include tuple, link bytes */ #define SBSDIO_CIS_MANFID_TUPLE_LEN 6 /* intstatus */ #define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ #define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ #define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ #define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ #define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ #define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ #define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ #define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ #define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ #define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ #define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ #define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ #define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ #define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ #define I_PC (1 << 10) /* descriptor error */ #define I_PD (1 << 11) /* data error */ #define I_DE (1 << 12) /* Descriptor protocol Error */ #define I_RU (1 << 13) /* Receive descriptor Underflow */ #define I_RO (1 << 14) /* Receive fifo Overflow */ #define I_XU (1 << 15) /* Transmit fifo Underflow */ #define I_RI (1 << 16) /* Receive Interrupt */ #define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ #define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ #define I_XI (1 << 24) /* Transmit Interrupt */ #define I_RF_TERM (1 << 25) /* Read Frame Terminate */ #define I_WF_TERM (1 << 26) /* Write Frame Terminate */ #define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ #define I_SBINT (1 << 28) /* sbintstatus Interrupt */ #define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */ #define I_SRESET (1 << 30) /* CCCR RES interrupt */ #define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ #define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) #define I_DMA (I_RI | I_XI | I_ERRORS) /* corecontrol */ #define CC_CISRDY (1 << 0) /* CIS Ready */ #define CC_BPRESEN (1 << 1) /* CCCR RES signal */ #define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ #define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */ #define CC_XMTDATAAVAIL_MODE (1 << 4) #define CC_XMTDATAAVAIL_CTRL (1 << 5) /* SDA_FRAMECTRL */ #define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ #define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ #define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */ #define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */ /* HW frame tag */ #define SDPCM_FRAMETAG_LEN 4 /* 2 bytes len, 2 bytes check val */ /* Total length of frame header for dongle protocol */ #define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) #define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN) /* * Software allocation of To SB Mailbox resources */ /* tosbmailbox bits corresponding to intstatus bits */ #define SMB_NAK (1 << 0) /* Frame NAK */ #define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */ #define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */ #define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */ /* tosbmailboxdata */ #define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */ /* * Software allocation of To Host Mailbox resources */ /* intstatus bits */ #define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */ #define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */ #define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */ #define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */ /* tohostmailboxdata */ #define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */ #define HMB_DATA_DEVREADY 2 /* talk to host after enable */ #define HMB_DATA_FC 4 /* per prio flowcontrol update flag */ #define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */ #define HMB_DATA_FCDATA_MASK 0xff000000 #define HMB_DATA_FCDATA_SHIFT 24 #define HMB_DATA_VERSION_MASK 0x00ff0000 #define HMB_DATA_VERSION_SHIFT 16 /* * Software-defined protocol header */ /* Current protocol version */ #define SDPCM_PROT_VERSION 4 /* SW frame header */ #define SDPCM_PACKET_SEQUENCE(p) (((u8 *)p)[0] & 0xff) #define SDPCM_CHANNEL_MASK 0x00000f00 #define SDPCM_CHANNEL_SHIFT 8 #define SDPCM_PACKET_CHANNEL(p) (((u8 *)p)[1] & 0x0f) #define SDPCM_NEXTLEN_OFFSET 2 /* Data Offset from SOF (HW Tag, SW Tag, Pad) */ #define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */ #define SDPCM_DOFFSET_VALUE(p) (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff) #define SDPCM_DOFFSET_MASK 0xff000000 #define SDPCM_DOFFSET_SHIFT 24 #define SDPCM_FCMASK_OFFSET 4 /* Flow control */ #define SDPCM_FCMASK_VALUE(p) (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff) #define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */ #define SDPCM_WINDOW_VALUE(p) (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff) #define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */ /* logical channel numbers */ #define SDPCM_CONTROL_CHANNEL 0 /* Control channel Id */ #define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */ #define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */ #define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets */ #define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */ #define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for 8bit frame seq */ #define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80) /* * Shared structure between dongle and the host. * The structure contains pointers to trap or assert information. */ #define SDPCM_SHARED_VERSION 0x0003 #define SDPCM_SHARED_VERSION_MASK 0x00FF #define SDPCM_SHARED_ASSERT_BUILT 0x0100 #define SDPCM_SHARED_ASSERT 0x0200 #define SDPCM_SHARED_TRAP 0x0400 /* Space for header read, limit for data packets */ #define MAX_HDR_READ (1 << 6) #define MAX_RX_DATASZ 2048 /* Maximum milliseconds to wait for F2 to come up */ #define BRCMF_WAIT_F2RDY 3000 /* Bump up limit on waiting for HT to account for first startup; * if the image is doing a CRC calculation before programming the PMU * for HT availability, it could take a couple hundred ms more, so * max out at a 1 second (1000000us). */ #undef PMU_MAX_TRANSITION_DLY #define PMU_MAX_TRANSITION_DLY 1000000 /* Value for ChipClockCSR during initial setup */ #define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \ SBSDIO_ALP_AVAIL_REQ) /* Flags for SDH calls */ #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) #define BRCMF_SDIO_FW_NAME "brcm/brcmfmac-sdio.bin" #define BRCMF_SDIO_NV_NAME "brcm/brcmfmac-sdio.txt" MODULE_FIRMWARE(BRCMF_SDIO_FW_NAME); MODULE_FIRMWARE(BRCMF_SDIO_NV_NAME); #define BRCMF_IDLE_IMMEDIATE (-1) /* Enter idle immediately */ #define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change * when idle */ #define BRCMF_IDLE_INTERVAL 1 /* * Conversion of 802.1D priority to precedence level */ static uint prio2prec(u32 prio) { return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ? (prio^2) : prio; } /* core registers */ struct sdpcmd_regs { u32 corecontrol; /* 0x00, rev8 */ u32 corestatus; /* rev8 */ u32 PAD[1]; u32 biststatus; /* rev8 */ /* PCMCIA access */ u16 pcmciamesportaladdr; /* 0x010, rev8 */ u16 PAD[1]; u16 pcmciamesportalmask; /* rev8 */ u16 PAD[1]; u16 pcmciawrframebc; /* rev8 */ u16 PAD[1]; u16 pcmciaunderflowtimer; /* rev8 */ u16 PAD[1]; /* interrupt */ u32 intstatus; /* 0x020, rev8 */ u32 hostintmask; /* rev8 */ u32 intmask; /* rev8 */ u32 sbintstatus; /* rev8 */ u32 sbintmask; /* rev8 */ u32 funcintmask; /* rev4 */ u32 PAD[2]; u32 tosbmailbox; /* 0x040, rev8 */ u32 tohostmailbox; /* rev8 */ u32 tosbmailboxdata; /* rev8 */ u32 tohostmailboxdata; /* rev8 */ /* synchronized access to registers in SDIO clock domain */ u32 sdioaccess; /* 0x050, rev8 */ u32 PAD[3]; /* PCMCIA frame control */ u8 pcmciaframectrl; /* 0x060, rev8 */ u8 PAD[3]; u8 pcmciawatermark; /* rev8 */ u8 PAD[155]; /* interrupt batching control */ u32 intrcvlazy; /* 0x100, rev8 */ u32 PAD[3]; /* counters */ u32 cmd52rd; /* 0x110, rev8 */ u32 cmd52wr; /* rev8 */ u32 cmd53rd; /* rev8 */ u32 cmd53wr; /* rev8 */ u32 abort; /* rev8 */ u32 datacrcerror; /* rev8 */ u32 rdoutofsync; /* rev8 */ u32 wroutofsync; /* rev8 */ u32 writebusy; /* rev8 */ u32 readwait; /* rev8 */ u32 readterm; /* rev8 */ u32 writeterm; /* rev8 */ u32 PAD[40]; u32 clockctlstatus; /* rev8 */ u32 PAD[7]; u32 PAD[128]; /* DMA engines */ /* SDIO/PCMCIA CIS region */ char cis[512]; /* 0x400-0x5ff, rev6 */ /* PCMCIA function control registers */ char pcmciafcr[256]; /* 0x600-6ff, rev6 */ u16 PAD[55]; /* PCMCIA backplane access */ u16 backplanecsr; /* 0x76E, rev6 */ u16 backplaneaddr0; /* rev6 */ u16 backplaneaddr1; /* rev6 */ u16 backplaneaddr2; /* rev6 */ u16 backplaneaddr3; /* rev6 */ u16 backplanedata0; /* rev6 */ u16 backplanedata1; /* rev6 */ u16 backplanedata2; /* rev6 */ u16 backplanedata3; /* rev6 */ u16 PAD[31]; /* sprom "size" & "blank" info */ u16 spromstatus; /* 0x7BE, rev2 */ u32 PAD[464]; u16 PAD[0x80]; }; #ifdef DEBUG /* Device console log buffer state */ struct brcmf_console { uint count; /* Poll interval msec counter */ uint log_addr; /* Log struct address (fixed) */ struct rte_log_le log_le; /* Log struct (host copy) */ uint bufsize; /* Size of log buffer */ u8 *buf; /* Log buffer (host copy) */ uint last; /* Last buffer read index */ }; struct brcmf_trap_info { __le32 type; __le32 epc; __le32 cpsr; __le32 spsr; __le32 r0; /* a1 */ __le32 r1; /* a2 */ __le32 r2; /* a3 */ __le32 r3; /* a4 */ __le32 r4; /* v1 */ __le32 r5; /* v2 */ __le32 r6; /* v3 */ __le32 r7; /* v4 */ __le32 r8; /* v5 */ __le32 r9; /* sb/v6 */ __le32 r10; /* sl/v7 */ __le32 r11; /* fp/v8 */ __le32 r12; /* ip */ __le32 r13; /* sp */ __le32 r14; /* lr */ __le32 pc; /* r15 */ }; #endif /* DEBUG */ struct sdpcm_shared { u32 flags; u32 trap_addr; u32 assert_exp_addr; u32 assert_file_addr; u32 assert_line; u32 console_addr; /* Address of struct rte_console */ u32 msgtrace_addr; u8 tag[32]; u32 brpt_addr; }; struct sdpcm_shared_le { __le32 flags; __le32 trap_addr; __le32 assert_exp_addr; __le32 assert_file_addr; __le32 assert_line; __le32 console_addr; /* Address of struct rte_console */ __le32 msgtrace_addr; u8 tag[32]; __le32 brpt_addr; }; /* misc chip info needed by some of the routines */ /* Private data for SDIO bus interaction */ struct brcmf_sdio { struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ struct chip_info *ci; /* Chip info struct */ char *vars; /* Variables (from CIS and/or other) */ uint varsz; /* Size of variables buffer */ u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ u32 hostintmask; /* Copy of Host Interrupt Mask */ u32 intstatus; /* Intstatus bits (events) pending */ bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */ bool fcstate; /* State of dongle flow-control */ uint blocksize; /* Block size of SDIO transfers */ uint roundup; /* Max roundup limit */ struct pktq txq; /* Queue length used for flow-control */ u8 flowcontrol; /* per prio flow control bitmask */ u8 tx_seq; /* Transmit sequence number (next) */ u8 tx_max; /* Maximum transmit sequence allowed */ u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN]; u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ u16 nextlen; /* Next Read Len from last header */ u8 rx_seq; /* Receive sequence number (expected) */ bool rxskip; /* Skip receive (awaiting NAK ACK) */ uint rxbound; /* Rx frames to read before resched */ uint txbound; /* Tx frames to send before resched */ uint txminmax; struct sk_buff *glomd; /* Packet containing glomming descriptor */ struct sk_buff_head glom; /* Packet list for glommed superframe */ uint glomerr; /* Glom packet read errors */ u8 *rxbuf; /* Buffer for receiving control packets */ uint rxblen; /* Allocated length of rxbuf */ u8 *rxctl; /* Aligned pointer into rxbuf */ u8 *databuf; /* Buffer for receiving big glom packet */ u8 *dataptr; /* Aligned pointer into databuf */ uint rxlen; /* Length of valid data in buffer */ u8 sdpcm_ver; /* Bus protocol reported by dongle */ bool intr; /* Use interrupts */ bool poll; /* Use polling */ bool ipend; /* Device interrupt is pending */ uint spurious; /* Count of spurious interrupts */ uint pollrate; /* Ticks between device polls */ uint polltick; /* Tick counter */ #ifdef DEBUG uint console_interval; struct brcmf_console console; /* Console output polling support */ uint console_addr; /* Console address from shared struct */ #endif /* DEBUG */ uint clkstate; /* State of sd and backplane clock(s) */ bool activity; /* Activity flag for clock down */ s32 idletime; /* Control for activity timeout */ s32 idlecount; /* Activity timeout counter */ s32 idleclock; /* How to set bus driver when idle */ s32 sd_rxchain; bool use_rxchain; /* If brcmf should use PKT chains */ bool sleeping; /* Is SDIO bus sleeping? */ bool rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ bool alp_only; /* Don't use HT clock (ALP only) */ /* Field to decide if rx of control frames happen in rxbuf or lb-pool */ bool usebufpool; u8 *ctrl_frame_buf; u32 ctrl_frame_len; bool ctrl_frame_stat; spinlock_t txqlock; wait_queue_head_t ctrl_wait; wait_queue_head_t dcmd_resp_wait; struct timer_list timer; struct completion watchdog_wait; struct task_struct *watchdog_tsk; bool wd_timer_valid; uint save_ms; struct task_struct *dpc_tsk; struct completion dpc_wait; struct list_head dpc_tsklst; spinlock_t dpc_tl_lock; struct semaphore sdsem; const struct firmware *firmware; u32 fw_ptr; bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; }; /* clkstate */ #define CLK_NONE 0 #define CLK_SDONLY 1 #define CLK_PENDING 2 /* Not used yet */ #define CLK_AVAIL 3 #ifdef DEBUG static int qcount[NUMPRIO]; static int tx_packets[NUMPRIO]; #endif /* DEBUG */ #define SDIO_DRIVE_STRENGTH 6 /* in milliamps */ #define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL) /* Retry count for register access failures */ static const uint retry_limit = 2; /* Limit on rounding up frames */ static const uint max_roundup = 512; #define ALIGNMENT 4 static void pkt_align(struct sk_buff *p, int len, int align) { uint datalign; datalign = (unsigned long)(p->data); datalign = roundup(datalign, (align)) - datalign; if (datalign) skb_pull(p, datalign); __skb_trim(p, len); } /* To check if there's window offered */ static bool data_ok(struct brcmf_sdio *bus) { return (u8)(bus->tx_max - bus->tx_seq) != 0 && ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0; } /* * Reads a register in the SDIO hardware block. This block occupies a series of * adresses on the 32 bit backplane bus. */ static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) { u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); int ret; *regvar = brcmf_sdio_regrl(bus->sdiodev, bus->ci->c_inf[idx].base + offset, &ret); return ret; } static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) { u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); int ret; brcmf_sdio_regwl(bus->sdiodev, bus->ci->c_inf[idx].base + reg_offset, regval, &ret); return ret; } #define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND) #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) /* Packet free applicable unconditionally for sdio and sdspi. * Conditional if bufpool was present for gspi bus. */ static void brcmf_sdbrcm_pktfree2(struct brcmf_sdio *bus, struct sk_buff *pkt) { if (bus->usebufpool) brcmu_pkt_buf_free_skb(pkt); } /* Turn backplane clock on or off */ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) { int err; u8 clkctl, clkreq, devctl; unsigned long timeout; brcmf_dbg(TRACE, "Enter\n"); clkctl = 0; if (on) { /* Request HT Avail */ clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); if (err) { brcmf_dbg(ERROR, "HT Avail request error: %d\n", err); return -EBADE; } /* Check current status */ clkctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err) { brcmf_dbg(ERROR, "HT Avail read error: %d\n", err); return -EBADE; } /* Go to pending and await interrupt if appropriate */ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { /* Allow only clock-available interrupt */ devctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_DEVICE_CTL, &err); if (err) { brcmf_dbg(ERROR, "Devctl error setting CA: %d\n", err); return -EBADE; } devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, devctl, &err); brcmf_dbg(INFO, "CLKCTL: set PENDING\n"); bus->clkstate = CLK_PENDING; return 0; } else if (bus->clkstate == CLK_PENDING) { /* Cancel CA-only interrupt filter */ devctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_DEVICE_CTL, &err); devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, devctl, &err); } /* Otherwise, wait here (polling) for HT Avail */ timeout = jiffies + msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { clkctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (time_after(jiffies, timeout)) break; else usleep_range(5000, 10000); } if (err) { brcmf_dbg(ERROR, "HT Avail request error: %d\n", err); return -EBADE; } if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { brcmf_dbg(ERROR, "HT Avail timeout (%d): clkctl 0x%02x\n", PMU_MAX_TRANSITION_DLY, clkctl); return -EBADE; } /* Mark clock available */ bus->clkstate = CLK_AVAIL; brcmf_dbg(INFO, "CLKCTL: turned ON\n"); #if defined(DEBUG) if (!bus->alp_only) { if (SBSDIO_ALPONLY(clkctl)) brcmf_dbg(ERROR, "HT Clock should be on\n"); } #endif /* defined (DEBUG) */ bus->activity = true; } else { clkreq = 0; if (bus->clkstate == CLK_PENDING) { /* Cancel CA-only interrupt filter */ devctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_DEVICE_CTL, &err); devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, devctl, &err); } bus->clkstate = CLK_SDONLY; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); brcmf_dbg(INFO, "CLKCTL: turned OFF\n"); if (err) { brcmf_dbg(ERROR, "Failed access turning clock off: %d\n", err); return -EBADE; } } return 0; } /* Change idle/active SD state */ static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) { brcmf_dbg(TRACE, "Enter\n"); if (on) bus->clkstate = CLK_SDONLY; else bus->clkstate = CLK_NONE; return 0; } /* Transition SD and backplane clock readiness */ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) { #ifdef DEBUG uint oldstate = bus->clkstate; #endif /* DEBUG */ brcmf_dbg(TRACE, "Enter\n"); /* Early exit if we're already there */ if (bus->clkstate == target) { if (target == CLK_AVAIL) { brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); bus->activity = true; } return 0; } switch (target) { case CLK_AVAIL: /* Make sure SD clock is available */ if (bus->clkstate == CLK_NONE) brcmf_sdbrcm_sdclk(bus, true); /* Now request HT Avail on the backplane */ brcmf_sdbrcm_htclk(bus, true, pendok); brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); bus->activity = true; break; case CLK_SDONLY: /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE) brcmf_sdbrcm_sdclk(bus, true); else if (bus->clkstate == CLK_AVAIL) brcmf_sdbrcm_htclk(bus, false, false); else brcmf_dbg(ERROR, "request for %d -> %d\n", bus->clkstate, target); brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); break; case CLK_NONE: /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL) brcmf_sdbrcm_htclk(bus, false, false); /* Now remove the SD clock */ brcmf_sdbrcm_sdclk(bus, false); brcmf_sdbrcm_wd_timer(bus, 0); break; } #ifdef DEBUG brcmf_dbg(INFO, "%d -> %d\n", oldstate, bus->clkstate); #endif /* DEBUG */ return 0; } static int brcmf_sdbrcm_bussleep(struct brcmf_sdio *bus, bool sleep) { int ret; brcmf_dbg(INFO, "request %s (currently %s)\n", sleep ? "SLEEP" : "WAKE", bus->sleeping ? "SLEEP" : "WAKE"); /* Done if we're already in the requested state */ if (sleep == bus->sleeping) return 0; /* Going to sleep: set the alarm and turn off the lights... */ if (sleep) { /* Don't sleep if something is pending */ if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq)) return -EBUSY; /* Make sure the controller has the bus up */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); /* Tell device to start using OOB wakeup */ ret = w_sdreg32(bus, SMB_USE_OOB, offsetof(struct sdpcmd_regs, tosbmailbox)); if (ret != 0) brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"); /* Turn off our contribution to the HT clock request */ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); /* Isolate the bus */ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, SBSDIO_DEVCTL_PADS_ISO, NULL); /* Change state */ bus->sleeping = true; } else { /* Waking up: bus power up is ok, set local state */ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); /* Make sure the controller has the bus up */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); /* Send misc interrupt to indicate OOB not needed */ ret = w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, tosbmailboxdata)); if (ret == 0) ret = w_sdreg32(bus, SMB_DEV_INT, offsetof(struct sdpcmd_regs, tosbmailbox)); if (ret != 0) brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"); /* Make sure we have SD bus access */ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); /* Change state */ bus->sleeping = false; } return 0; } static void bus_wake(struct brcmf_sdio *bus) { if (bus->sleeping) brcmf_sdbrcm_bussleep(bus, false); } static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) { u32 intstatus = 0; u32 hmb_data; u8 fcbits; int ret; brcmf_dbg(TRACE, "Enter\n"); /* Read mailbox data and ack that we did so */ ret = r_sdreg32(bus, &hmb_data, offsetof(struct sdpcmd_regs, tohostmailboxdata)); if (ret == 0) w_sdreg32(bus, SMB_INT_ACK, offsetof(struct sdpcmd_regs, tosbmailbox)); bus->sdcnt.f1regdata += 2; /* Dongle recomposed rx frames, accept them again */ if (hmb_data & HMB_DATA_NAKHANDLED) { brcmf_dbg(INFO, "Dongle reports NAK handled, expect rtx of %d\n", bus->rx_seq); if (!bus->rxskip) brcmf_dbg(ERROR, "unexpected NAKHANDLED!\n"); bus->rxskip = false; intstatus |= I_HMB_FRAME_IND; } /* * DEVREADY does not occur with gSPI. */ if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { bus->sdpcm_ver = (hmb_data & HMB_DATA_VERSION_MASK) >> HMB_DATA_VERSION_SHIFT; if (bus->sdpcm_ver != SDPCM_PROT_VERSION) brcmf_dbg(ERROR, "Version mismatch, dongle reports %d, " "expecting %d\n", bus->sdpcm_ver, SDPCM_PROT_VERSION); else brcmf_dbg(INFO, "Dongle ready, protocol version %d\n", bus->sdpcm_ver); } /* * Flow Control has been moved into the RX headers and this out of band * method isn't used any more. * remaining backward compatible with older dongles. */ if (hmb_data & HMB_DATA_FC) { fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> HMB_DATA_FCDATA_SHIFT; if (fcbits & ~bus->flowcontrol) bus->sdcnt.fc_xoff++; if (bus->flowcontrol & ~fcbits) bus->sdcnt.fc_xon++; bus->sdcnt.fc_rcvd++; bus->flowcontrol = fcbits; } /* Shouldn't be any others */ if (hmb_data & ~(HMB_DATA_DEVREADY | HMB_DATA_NAKHANDLED | HMB_DATA_FC | HMB_DATA_FWREADY | HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) brcmf_dbg(ERROR, "Unknown mailbox data content: 0x%02x\n", hmb_data); return intstatus; } static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) { uint retries = 0; u16 lastrbc; u8 hi, lo; int err; brcmf_dbg(ERROR, "%sterminate frame%s\n", abort ? "abort command, " : "", rtx ? ", send NAK" : ""); if (abort) brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM, &err); bus->sdcnt.f1regdata++; /* Wait until the packet has been flushed (device/FIFO stable) */ for (lastrbc = retries = 0xffff; retries > 0; retries--) { hi = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_RFRAMEBCHI, &err); lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_RFRAMEBCLO, &err); bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { brcmf_dbg(ERROR, "count growing: last 0x%04x now 0x%04x\n", lastrbc, (hi << 8) + lo); } lastrbc = (hi << 8) + lo; } if (!retries) brcmf_dbg(ERROR, "count never zeroed: last 0x%04x\n", lastrbc); else brcmf_dbg(INFO, "flush took %d iterations\n", 0xffff - retries); if (rtx) { bus->sdcnt.rxrtx++; err = w_sdreg32(bus, SMB_NAK, offsetof(struct sdpcmd_regs, tosbmailbox)); bus->sdcnt.f1regdata++; if (err == 0) bus->rxskip = true; } /* Clear partial in any case */ bus->nextlen = 0; /* If we can't reach the device, signal failure */ if (err) bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } /* copy a buffer into a pkt buffer chain */ static uint brcmf_sdbrcm_glom_from_buf(struct brcmf_sdio *bus, uint len) { uint n, ret = 0; struct sk_buff *p; u8 *buf; buf = bus->dataptr; /* copy the data */ skb_queue_walk(&bus->glom, p) { n = min_t(uint, p->len, len); memcpy(p->data, buf, n); buf += n; len -= n; ret += n; if (!len) break; } return ret; } /* return total length of buffer chain */ static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) { struct sk_buff *p; uint total; total = 0; skb_queue_walk(&bus->glom, p) total += p->len; return total; } static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) { struct sk_buff *cur, *next; skb_queue_walk_safe(&bus->glom, cur, next) { skb_unlink(cur, &bus->glom); brcmu_pkt_buf_free_skb(cur); } } static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) { u16 dlen, totlen; u8 *dptr, num = 0; u16 sublen, check; struct sk_buff *pfirst, *pnext; int errcode; u8 chan, seq, doff, sfdoff; u8 txmax; int ifidx = 0; bool usechain = bus->use_rxchain; /* If packets, issue read(s) and send up packet chain */ /* Return sequence numbers consumed? */ brcmf_dbg(TRACE, "start: glomd %p glom %p\n", bus->glomd, skb_peek(&bus->glom)); /* If there's a descriptor, generate the packet chain */ if (bus->glomd) { pfirst = pnext = NULL; dlen = (u16) (bus->glomd->len); dptr = bus->glomd->data; if (!dlen || (dlen & 1)) { brcmf_dbg(ERROR, "bad glomd len(%d), ignore descriptor\n", dlen); dlen = 0; } for (totlen = num = 0; dlen; num++) { /* Get (and move past) next length */ sublen = get_unaligned_le16(dptr); dlen -= sizeof(u16); dptr += sizeof(u16); if ((sublen < SDPCM_HDRLEN) || ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { brcmf_dbg(ERROR, "descriptor len %d bad: %d\n", num, sublen); pnext = NULL; break; } if (sublen % BRCMF_SDALIGN) { brcmf_dbg(ERROR, "sublen %d not multiple of %d\n", sublen, BRCMF_SDALIGN); usechain = false; } totlen += sublen; /* For last frame, adjust read len so total is a block multiple */ if (!dlen) { sublen += (roundup(totlen, bus->blocksize) - totlen); totlen = roundup(totlen, bus->blocksize); } /* Allocate/chain packet for next subframe */ pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN); if (pnext == NULL) { brcmf_dbg(ERROR, "bcm_pkt_buf_get_skb failed, num %d len %d\n", num, sublen); break; } skb_queue_tail(&bus->glom, pnext); /* Adhere to start alignment requirements */ pkt_align(pnext, sublen, BRCMF_SDALIGN); } /* If all allocations succeeded, save packet chain in bus structure */ if (pnext) { brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n", totlen, num); if (BRCMF_GLOM_ON() && bus->nextlen && totlen != bus->nextlen) { brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", bus->nextlen, totlen, rxseq); } pfirst = pnext = NULL; } else { brcmf_sdbrcm_free_glom(bus); num = 0; } /* Done with descriptor packet */ brcmu_pkt_buf_free_skb(bus->glomd); bus->glomd = NULL; bus->nextlen = 0; } /* Ok -- either we just generated a packet chain, or had one from before */ if (!skb_queue_empty(&bus->glom)) { if (BRCMF_GLOM_ON()) { brcmf_dbg(GLOM, "try superframe read, packet chain:\n"); skb_queue_walk(&bus->glom, pnext) { brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n", pnext, (u8 *) (pnext->data), pnext->len, pnext->len); } } pfirst = skb_peek(&bus->glom); dlen = (u16) brcmf_sdbrcm_glom_len(bus); /* Do an SDIO read for the superframe. Configurable iovar to * read directly into the chained packet, or allocate a large * packet and and copy into the chain. */ if (usechain) { errcode = brcmf_sdcard_recv_chain(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, &bus->glom); } else if (bus->dataptr) { errcode = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, bus->dataptr, dlen); sublen = (u16) brcmf_sdbrcm_glom_from_buf(bus, dlen); if (sublen != dlen) { brcmf_dbg(ERROR, "FAILED TO COPY, dlen %d sublen %d\n", dlen, sublen); errcode = -1; } pnext = NULL; } else { brcmf_dbg(ERROR, "COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n", dlen); errcode = -1; } bus->sdcnt.f2rxdata++; /* On failure, kill the superframe, allow a couple retries */ if (errcode < 0) { brcmf_dbg(ERROR, "glom read of %d bytes failed: %d\n", dlen, errcode); bus->sdiodev->bus_if->dstats.rx_errors++; if (bus->glomerr++ < 3) { brcmf_sdbrcm_rxfail(bus, true, true); } else { bus->glomerr = 0; brcmf_sdbrcm_rxfail(bus, true, false); bus->sdcnt.rxglomfail++; brcmf_sdbrcm_free_glom(bus); } return 0; } brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), pfirst->data, min_t(int, pfirst->len, 48), "SUPERFRAME:\n"); /* Validate the superframe header */ dptr = (u8 *) (pfirst->data); sublen = get_unaligned_le16(dptr); check = get_unaligned_le16(dptr + sizeof(u16)); chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; if ((bus->nextlen << 4) > MAX_RX_DATASZ) { brcmf_dbg(INFO, "nextlen too large (%d) seq %d\n", bus->nextlen, seq); bus->nextlen = 0; } doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); errcode = 0; if ((u16)~(sublen ^ check)) { brcmf_dbg(ERROR, "(superframe): HW hdr error: len/check 0x%04x/0x%04x\n", sublen, check); errcode = -1; } else if (roundup(sublen, bus->blocksize) != dlen) { brcmf_dbg(ERROR, "(superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n", sublen, roundup(sublen, bus->blocksize), dlen); errcode = -1; } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) != SDPCM_GLOM_CHANNEL) { brcmf_dbg(ERROR, "(superframe): bad channel %d\n", SDPCM_PACKET_CHANNEL( &dptr[SDPCM_FRAMETAG_LEN])); errcode = -1; } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) { brcmf_dbg(ERROR, "(superframe): got 2nd descriptor?\n"); errcode = -1; } else if ((doff < SDPCM_HDRLEN) || (doff > (pfirst->len - SDPCM_HDRLEN))) { brcmf_dbg(ERROR, "(superframe): Bad data offset %d: HW %d pkt %d min %d\n", doff, sublen, pfirst->len, SDPCM_HDRLEN); errcode = -1; } /* Check sequence number of superframe SW header */ if (rxseq != seq) { brcmf_dbg(INFO, "(superframe) rx_seq %d, expected %d\n", seq, rxseq); bus->sdcnt.rx_badseq++; rxseq = seq; } /* Check window for sanity */ if ((u8) (txmax - bus->tx_seq) > 0x40) { brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n", txmax, bus->tx_seq); txmax = bus->tx_seq + 2; } bus->tx_max = txmax; /* Remove superframe header, remember offset */ skb_pull(pfirst, doff); sfdoff = doff; num = 0; /* Validate all the subframe headers */ skb_queue_walk(&bus->glom, pnext) { /* leave when invalid subframe is found */ if (errcode) break; dptr = (u8 *) (pnext->data); dlen = (u16) (pnext->len); sublen = get_unaligned_le16(dptr); check = get_unaligned_le16(dptr + sizeof(u16)); chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), dptr, 32, "subframe:\n"); if ((u16)~(sublen ^ check)) { brcmf_dbg(ERROR, "(subframe %d): HW hdr error: len/check 0x%04x/0x%04x\n", num, sublen, check); errcode = -1; } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) { brcmf_dbg(ERROR, "(subframe %d): length mismatch: len 0x%04x, expect 0x%04x\n", num, sublen, dlen); errcode = -1; } else if ((chan != SDPCM_DATA_CHANNEL) && (chan != SDPCM_EVENT_CHANNEL)) { brcmf_dbg(ERROR, "(subframe %d): bad channel %d\n", num, chan); errcode = -1; } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) { brcmf_dbg(ERROR, "(subframe %d): Bad data offset %d: HW %d min %d\n", num, doff, sublen, SDPCM_HDRLEN); errcode = -1; } /* increase the subframe count */ num++; } if (errcode) { /* Terminate frame on error, request a couple retries */ if (bus->glomerr++ < 3) { /* Restore superframe header space */ skb_push(pfirst, sfdoff); brcmf_sdbrcm_rxfail(bus, true, true); } else { bus->glomerr = 0; brcmf_sdbrcm_rxfail(bus, true, false); bus->sdcnt.rxglomfail++; brcmf_sdbrcm_free_glom(bus); } bus->nextlen = 0; return 0; } /* Basic SD framing looks ok - process each packet (header) */ skb_queue_walk_safe(&bus->glom, pfirst, pnext) { dptr = (u8 *) (pfirst->data); sublen = get_unaligned_le16(dptr); chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); brcmf_dbg(GLOM, "Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n", num, pfirst, pfirst->data, pfirst->len, sublen, chan, seq); /* precondition: chan == SDPCM_DATA_CHANNEL || chan == SDPCM_EVENT_CHANNEL */ if (rxseq != seq) { brcmf_dbg(GLOM, "rx_seq %d, expected %d\n", seq, rxseq); bus->sdcnt.rx_badseq++; rxseq = seq; } rxseq++; brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), dptr, dlen, "Rx Subframe Data:\n"); __skb_trim(pfirst, sublen); skb_pull(pfirst, doff); if (pfirst->len == 0) { skb_unlink(pfirst, &bus->glom); brcmu_pkt_buf_free_skb(pfirst); continue; } else if (brcmf_proto_hdrpull(bus->sdiodev->dev, &ifidx, pfirst) != 0) { brcmf_dbg(ERROR, "rx protocol error\n"); bus->sdiodev->bus_if->dstats.rx_errors++; skb_unlink(pfirst, &bus->glom); brcmu_pkt_buf_free_skb(pfirst); continue; } brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), pfirst->data, min_t(int, pfirst->len, 32), "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n", bus->glom.qlen, pfirst, pfirst->data, pfirst->len, pfirst->next, pfirst->prev); } /* sent any remaining packets up */ if (bus->glom.qlen) { up(&bus->sdsem); brcmf_rx_frame(bus->sdiodev->dev, ifidx, &bus->glom); down(&bus->sdsem); } bus->sdcnt.rxglomframes++; bus->sdcnt.rxglompkts += bus->glom.qlen; } return num; } static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, bool *pending) { DECLARE_WAITQUEUE(wait, current); int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); /* Wait until control frame is available */ add_wait_queue(&bus->dcmd_resp_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); while (!(*condition) && (!signal_pending(current) && timeout)) timeout = schedule_timeout(timeout); if (signal_pending(current)) *pending = true; set_current_state(TASK_RUNNING); remove_wait_queue(&bus->dcmd_resp_wait, &wait); return timeout; } static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus) { if (waitqueue_active(&bus->dcmd_resp_wait)) wake_up_interruptible(&bus->dcmd_resp_wait); return 0; } static void brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) { uint rdlen, pad; int sdret; brcmf_dbg(TRACE, "Enter\n"); /* Set rxctl for frame (w/optional alignment) */ bus->rxctl = bus->rxbuf; bus->rxctl += BRCMF_FIRSTREAD; pad = ((unsigned long)bus->rxctl % BRCMF_SDALIGN); if (pad) bus->rxctl += (BRCMF_SDALIGN - pad); bus->rxctl -= BRCMF_FIRSTREAD; /* Copy the already-read portion over */ memcpy(bus->rxctl, hdr, BRCMF_FIRSTREAD); if (len <= BRCMF_FIRSTREAD) goto gotpkt; /* Raise rdlen to next SDIO block to avoid tail command */ rdlen = len - BRCMF_FIRSTREAD; if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { pad = bus->blocksize - (rdlen % bus->blocksize); if ((pad <= bus->roundup) && (pad < bus->blocksize) && ((len + pad) < bus->sdiodev->bus_if->maxctl)) rdlen += pad; } else if (rdlen % BRCMF_SDALIGN) { rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN); } /* Satisfy length-alignment requirements */ if (rdlen & (ALIGNMENT - 1)) rdlen = roundup(rdlen, ALIGNMENT); /* Drop if the read is too big or it exceeds our maximum */ if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { brcmf_dbg(ERROR, "%d-byte control read exceeds %d-byte buffer\n", rdlen, bus->sdiodev->bus_if->maxctl); bus->sdiodev->bus_if->dstats.rx_errors++; brcmf_sdbrcm_rxfail(bus, false, false); goto done; } if ((len - doff) > bus->sdiodev->bus_if->maxctl) { brcmf_dbg(ERROR, "%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", len, len - doff, bus->sdiodev->bus_if->maxctl); bus->sdiodev->bus_if->dstats.rx_errors++; bus->sdcnt.rx_toolong++; brcmf_sdbrcm_rxfail(bus, false, false); goto done; } /* Read remainder of frame body into the rxctl buffer */ sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, (bus->rxctl + BRCMF_FIRSTREAD), rdlen); bus->sdcnt.f2rxdata++; /* Control frame failures need retransmission */ if (sdret < 0) { brcmf_dbg(ERROR, "read %d control bytes failed: %d\n", rdlen, sdret); bus->sdcnt.rxc_errors++; brcmf_sdbrcm_rxfail(bus, true, true); goto done; } gotpkt: brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), bus->rxctl, len, "RxCtrl:\n"); /* Point to valid data and indicate its length */ bus->rxctl += doff; bus->rxlen = len - doff; done: /* Awake any waiters */ brcmf_sdbrcm_dcmd_resp_wake(bus); } /* Pad read to blocksize for efficiency */ static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) { if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { *pad = bus->blocksize - (*rdlen % bus->blocksize); if (*pad <= bus->roundup && *pad < bus->blocksize && *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ) *rdlen += *pad; } else if (*rdlen % BRCMF_SDALIGN) { *rdlen += BRCMF_SDALIGN - (*rdlen % BRCMF_SDALIGN); } } static void brcmf_alloc_pkt_and_read(struct brcmf_sdio *bus, u16 rdlen, struct sk_buff **pkt, u8 **rxbuf) { int sdret; /* Return code from calls */ *pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_SDALIGN); if (*pkt == NULL) return; pkt_align(*pkt, rdlen, BRCMF_SDALIGN); *rxbuf = (u8 *) ((*pkt)->data); /* Read the entire frame */ sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, *pkt); bus->sdcnt.f2rxdata++; if (sdret < 0) { brcmf_dbg(ERROR, "(nextlen): read %d bytes failed: %d\n", rdlen, sdret); brcmu_pkt_buf_free_skb(*pkt); bus->sdiodev->bus_if->dstats.rx_errors++; /* Force retry w/normal header read. * Don't attempt NAK for * gSPI */ brcmf_sdbrcm_rxfail(bus, true, true); *pkt = NULL; } } /* Checks the header */ static int brcmf_check_rxbuf(struct brcmf_sdio *bus, struct sk_buff *pkt, u8 *rxbuf, u8 rxseq, u16 nextlen, u16 *len) { u16 check; bool len_consistent; /* Result of comparing readahead len and len from hw-hdr */ memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN); /* Extract hardware header fields */ *len = get_unaligned_le16(bus->rxhdr); check = get_unaligned_le16(bus->rxhdr + sizeof(u16)); /* All zeros means readahead info was bad */ if (!(*len | check)) { brcmf_dbg(INFO, "(nextlen): read zeros in HW header???\n"); goto fail; } /* Validate check bytes */ if ((u16)~(*len ^ check)) { brcmf_dbg(ERROR, "(nextlen): HW hdr error: nextlen/len/check 0x%04x/0x%04x/0x%04x\n", nextlen, *len, check); bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); goto fail; } /* Validate frame length */ if (*len < SDPCM_HDRLEN) { brcmf_dbg(ERROR, "(nextlen): HW hdr length invalid: %d\n", *len); goto fail; } /* Check for consistency with readahead info */ len_consistent = (nextlen != (roundup(*len, 16) >> 4)); if (len_consistent) { /* Mismatch, force retry w/normal header (may be >4K) */ brcmf_dbg(ERROR, "(nextlen): mismatch, nextlen %d len %d rnd %d; expected rxseq %d\n", nextlen, *len, roundup(*len, 16), rxseq); brcmf_sdbrcm_rxfail(bus, true, true); goto fail; } return 0; fail: brcmf_sdbrcm_pktfree2(bus, pkt); return -EINVAL; } /* Return true if there may be more frames to read */ static uint brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) { u16 len, check; /* Extracted hardware header fields */ u8 chan, seq, doff; /* Extracted software header fields */ u8 fcbits; /* Extracted fcbits from software header */ struct sk_buff *pkt; /* Packet for event or data frames */ u16 pad; /* Number of pad bytes to read */ u16 rdlen; /* Total number of bytes to read */ u8 rxseq; /* Next sequence number to expect */ uint rxleft = 0; /* Remaining number of frames allowed */ int sdret; /* Return code from calls */ u8 txmax; /* Maximum tx sequence offered */ u8 *rxbuf; int ifidx = 0; uint rxcount = 0; /* Total frames read */ brcmf_dbg(TRACE, "Enter\n"); /* Not finished unless we encounter no more frames indication */ *finished = false; for (rxseq = bus->rx_seq, rxleft = maxframes; !bus->rxskip && rxleft && bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN; rxseq++, rxleft--) { /* Handle glomming separately */ if (bus->glomd || !skb_queue_empty(&bus->glom)) { u8 cnt; brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", bus->glomd, skb_peek(&bus->glom)); cnt = brcmf_sdbrcm_rxglom(bus, rxseq); brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); rxseq += cnt - 1; rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; continue; } /* Try doing single read if we can */ if (bus->nextlen) { u16 nextlen = bus->nextlen; bus->nextlen = 0; rdlen = len = nextlen << 4; brcmf_pad(bus, &pad, &rdlen); /* * After the frame is received we have to * distinguish whether it is data * or non-data frame. */ brcmf_alloc_pkt_and_read(bus, rdlen, &pkt, &rxbuf); if (pkt == NULL) { /* Give up on data, request rtx of events */ brcmf_dbg(ERROR, "(nextlen): brcmf_alloc_pkt_and_read failed: len %d rdlen %d expected rxseq %d\n", len, rdlen, rxseq); continue; } if (brcmf_check_rxbuf(bus, pkt, rxbuf, rxseq, nextlen, &len) < 0) continue; /* Extract software header fields */ chan = SDPCM_PACKET_CHANNEL( &bus->rxhdr[SDPCM_FRAMETAG_LEN]); seq = SDPCM_PACKET_SEQUENCE( &bus->rxhdr[SDPCM_FRAMETAG_LEN]); doff = SDPCM_DOFFSET_VALUE( &bus->rxhdr[SDPCM_FRAMETAG_LEN]); txmax = SDPCM_WINDOW_VALUE( &bus->rxhdr[SDPCM_FRAMETAG_LEN]); bus->nextlen = bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; if ((bus->nextlen << 4) > MAX_RX_DATASZ) { brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n", bus->nextlen, seq); bus->nextlen = 0; } bus->sdcnt.rx_readahead_cnt++; /* Handle Flow Control */ fcbits = SDPCM_FCMASK_VALUE( &bus->rxhdr[SDPCM_FRAMETAG_LEN]); if (bus->flowcontrol != fcbits) { if (~bus->flowcontrol & fcbits) bus->sdcnt.fc_xoff++; if (bus->flowcontrol & ~fcbits) bus->sdcnt.fc_xon++; bus->sdcnt.fc_rcvd++; bus->flowcontrol = fcbits; } /* Check and update sequence number */ if (rxseq != seq) { brcmf_dbg(INFO, "(nextlen): rx_seq %d, expected %d\n", seq, rxseq); bus->sdcnt.rx_badseq++; rxseq = seq; } /* Check window for sanity */ if ((u8) (txmax - bus->tx_seq) > 0x40) { brcmf_dbg(ERROR, "got unlikely tx max %d with tx_seq %d\n", txmax, bus->tx_seq); txmax = bus->tx_seq + 2; } bus->tx_max = txmax; brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), rxbuf, len, "Rx Data:\n"); brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_DATA_ON()) && BRCMF_HDRS_ON(), bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n"); if (chan == SDPCM_CONTROL_CHANNEL) { brcmf_dbg(ERROR, "(nextlen): readahead on control packet %d?\n", seq); /* Force retry w/normal header read */ bus->nextlen = 0; brcmf_sdbrcm_rxfail(bus, false, true); brcmf_sdbrcm_pktfree2(bus, pkt); continue; } /* Validate data offset */ if ((doff < SDPCM_HDRLEN) || (doff > len)) { brcmf_dbg(ERROR, "(nextlen): bad data offset %d: HW len %d min %d\n", doff, len, SDPCM_HDRLEN); brcmf_sdbrcm_rxfail(bus, false, false); brcmf_sdbrcm_pktfree2(bus, pkt); continue; } /* All done with this one -- now deliver the packet */ goto deliver; } /* Read frame header (hardware and software) */ sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, bus->rxhdr, BRCMF_FIRSTREAD); bus->sdcnt.f2rxhdrs++; if (sdret < 0) { brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", sdret); bus->sdcnt.rx_hdrfail++; brcmf_sdbrcm_rxfail(bus, true, true); continue; } brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n"); /* Extract hardware header fields */ len = get_unaligned_le16(bus->rxhdr); check = get_unaligned_le16(bus->rxhdr + sizeof(u16)); /* All zeros means no more frames */ if (!(len | check)) { *finished = true; break; } /* Validate check bytes */ if ((u16) ~(len ^ check)) { brcmf_dbg(ERROR, "HW hdr err: len/check 0x%04x/0x%04x\n", len, check); bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); continue; } /* Validate frame length */ if (len < SDPCM_HDRLEN) { brcmf_dbg(ERROR, "HW hdr length invalid: %d\n", len); continue; } /* Extract software header fields */ chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); /* Validate data offset */ if ((doff < SDPCM_HDRLEN) || (doff > len)) { brcmf_dbg(ERROR, "Bad data offset %d: HW len %d, min %d seq %d\n", doff, len, SDPCM_HDRLEN, seq); bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); continue; } /* Save the readahead length if there is one */ bus->nextlen = bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; if ((bus->nextlen << 4) > MAX_RX_DATASZ) { brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n", bus->nextlen, seq); bus->nextlen = 0; } /* Handle Flow Control */ fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); if (bus->flowcontrol != fcbits) { if (~bus->flowcontrol & fcbits) bus->sdcnt.fc_xoff++; if (bus->flowcontrol & ~fcbits) bus->sdcnt.fc_xon++; bus->sdcnt.fc_rcvd++; bus->flowcontrol = fcbits; } /* Check and update sequence number */ if (rxseq != seq) { brcmf_dbg(INFO, "rx_seq %d, expected %d\n", seq, rxseq); bus->sdcnt.rx_badseq++; rxseq = seq; } /* Check window for sanity */ if ((u8) (txmax - bus->tx_seq) > 0x40) { brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n", txmax, bus->tx_seq); txmax = bus->tx_seq + 2; } bus->tx_max = txmax; /* Call a separate function for control frames */ if (chan == SDPCM_CONTROL_CHANNEL) { brcmf_sdbrcm_read_control(bus, bus->rxhdr, len, doff); continue; } /* precondition: chan is either SDPCM_DATA_CHANNEL, SDPCM_EVENT_CHANNEL, SDPCM_TEST_CHANNEL or SDPCM_GLOM_CHANNEL */ /* Length to read */ rdlen = (len > BRCMF_FIRSTREAD) ? (len - BRCMF_FIRSTREAD) : 0; /* May pad read to blocksize for efficiency */ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { pad = bus->blocksize - (rdlen % bus->blocksize); if ((pad <= bus->roundup) && (pad < bus->blocksize) && ((rdlen + pad + BRCMF_FIRSTREAD) < MAX_RX_DATASZ)) rdlen += pad; } else if (rdlen % BRCMF_SDALIGN) { rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN); } /* Satisfy length-alignment requirements */ if (rdlen & (ALIGNMENT - 1)) rdlen = roundup(rdlen, ALIGNMENT); if ((rdlen + BRCMF_FIRSTREAD) > MAX_RX_DATASZ) { /* Too long -- skip this frame */ brcmf_dbg(ERROR, "too long: len %d rdlen %d\n", len, rdlen); bus->sdiodev->bus_if->dstats.rx_errors++; bus->sdcnt.rx_toolong++; brcmf_sdbrcm_rxfail(bus, false, false); continue; } pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_FIRSTREAD + BRCMF_SDALIGN); if (!pkt) { /* Give up on data, request rtx of events */ brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: rdlen %d chan %d\n", rdlen, chan); bus->sdiodev->bus_if->dstats.rx_dropped++; brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(chan)); continue; } /* Leave room for what we already read, and align remainder */ skb_pull(pkt, BRCMF_FIRSTREAD); pkt_align(pkt, rdlen, BRCMF_SDALIGN); /* Read the remaining frame data */ sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, pkt); bus->sdcnt.f2rxdata++; if (sdret < 0) { brcmf_dbg(ERROR, "read %d %s bytes failed: %d\n", rdlen, ((chan == SDPCM_EVENT_CHANNEL) ? "event" : ((chan == SDPCM_DATA_CHANNEL) ? "data" : "test")), sdret); brcmu_pkt_buf_free_skb(pkt); bus->sdiodev->bus_if->dstats.rx_errors++; brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(chan)); continue; } /* Copy the already-read portion */ skb_push(pkt, BRCMF_FIRSTREAD); memcpy(pkt->data, bus->rxhdr, BRCMF_FIRSTREAD); brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), pkt->data, len, "Rx Data:\n"); deliver: /* Save superframe descriptor and allocate packet frame */ if (chan == SDPCM_GLOM_CHANNEL) { if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) { brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", len); brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), pkt->data, len, "Glom Data:\n"); __skb_trim(pkt, len); skb_pull(pkt, SDPCM_HDRLEN); bus->glomd = pkt; } else { brcmf_dbg(ERROR, "%s: glom superframe w/o " "descriptor!\n", __func__); brcmf_sdbrcm_rxfail(bus, false, false); } continue; } /* Fill in packet len and prio, deliver upward */ __skb_trim(pkt, len); skb_pull(pkt, doff); if (pkt->len == 0) { brcmu_pkt_buf_free_skb(pkt); continue; } else if (brcmf_proto_hdrpull(bus->sdiodev->dev, &ifidx, pkt) != 0) { brcmf_dbg(ERROR, "rx protocol error\n"); brcmu_pkt_buf_free_skb(pkt); bus->sdiodev->bus_if->dstats.rx_errors++; continue; } /* Unlock during rx call */ up(&bus->sdsem); brcmf_rx_packet(bus->sdiodev->dev, ifidx, pkt); down(&bus->sdsem); } rxcount = maxframes - rxleft; /* Message if we hit the limit */ if (!rxleft) brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); else brcmf_dbg(DATA, "processed %d frames\n", rxcount); /* Back off rxseq if awaiting rtx, update rx_seq */ if (bus->rxskip) rxseq--; bus->rx_seq = rxseq; return rxcount; } static void brcmf_sdbrcm_wait_for_event(struct brcmf_sdio *bus, bool *lockvar) { up(&bus->sdsem); wait_event_interruptible_timeout(bus->ctrl_wait, !*lockvar, HZ * 2); down(&bus->sdsem); return; } static void brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) { if (waitqueue_active(&bus->ctrl_wait)) wake_up_interruptible(&bus->ctrl_wait); return; } /* Writes a HW/SW header into the packet and sends it. */ /* Assumes: (a) header space already there, (b) caller holds lock */ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, uint chan, bool free_pkt) { int ret; u8 *frame; u16 len, pad = 0; u32 swheader; struct sk_buff *new; int i; brcmf_dbg(TRACE, "Enter\n"); frame = (u8 *) (pkt->data); /* Add alignment padding, allocate new packet if needed */ pad = ((unsigned long)frame % BRCMF_SDALIGN); if (pad) { if (skb_headroom(pkt) < pad) { brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n", skb_headroom(pkt), pad); bus->sdiodev->bus_if->tx_realloc++; new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN); if (!new) { brcmf_dbg(ERROR, "couldn't allocate new %d-byte packet\n", pkt->len + BRCMF_SDALIGN); ret = -ENOMEM; goto done; } pkt_align(new, pkt->len, BRCMF_SDALIGN); memcpy(new->data, pkt->data, pkt->len); if (free_pkt) brcmu_pkt_buf_free_skb(pkt); /* free the pkt if canned one is not used */ free_pkt = true; pkt = new; frame = (u8 *) (pkt->data); /* precondition: (frame % BRCMF_SDALIGN) == 0) */ pad = 0; } else { skb_push(pkt, pad); frame = (u8 *) (pkt->data); /* precondition: pad + SDPCM_HDRLEN <= pkt->len */ memset(frame, 0, pad + SDPCM_HDRLEN); } } /* precondition: pad < BRCMF_SDALIGN */ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ len = (u16) (pkt->len); *(__le16 *) frame = cpu_to_le16(len); *(((__le16 *) frame) + 1) = cpu_to_le16(~len); /* Software tag: channel, sequence number, data offset */ swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | (((pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN); put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); #ifdef DEBUG tx_packets[pkt->priority]++; #endif brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)), frame, len, "Tx Frame:\n"); brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) && BRCMF_HDRS_ON(), frame, min_t(u16, len, 16), "TxHdr:\n"); /* Raise len to next SDIO block to eliminate tail command */ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { u16 pad = bus->blocksize - (len % bus->blocksize); if ((pad <= bus->roundup) && (pad < bus->blocksize)) len += pad; } else if (len % BRCMF_SDALIGN) { len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN); } /* Some controllers have trouble with odd bytes -- round to even */ if (len & (ALIGNMENT - 1)) len = roundup(len, ALIGNMENT); ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, pkt); bus->sdcnt.f2txdata++; if (ret < 0) { /* On failure, abort the command and terminate the frame */ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", ret); bus->sdcnt.tx_sderrs++; brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; hi = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; } } if (ret == 0) bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; done: /* restore pkt buffer pointer before calling tx complete routine */ skb_pull(pkt, SDPCM_HDRLEN + pad); up(&bus->sdsem); brcmf_txcomplete(bus->sdiodev->dev, pkt, ret != 0); down(&bus->sdsem); if (free_pkt) brcmu_pkt_buf_free_skb(pkt); return ret; } static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) { struct sk_buff *pkt; u32 intstatus = 0; int ret = 0, prec_out; uint cnt = 0; uint datalen; u8 tx_prec_map; brcmf_dbg(TRACE, "Enter\n"); tx_prec_map = ~bus->flowcontrol; /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus); cnt++) { spin_lock_bh(&bus->txqlock); pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); if (pkt == NULL) { spin_unlock_bh(&bus->txqlock); break; } spin_unlock_bh(&bus->txqlock); datalen = pkt->len - SDPCM_HDRLEN; ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true); if (ret) bus->sdiodev->bus_if->dstats.tx_errors++; else bus->sdiodev->bus_if->dstats.tx_bytes += datalen; /* In poll mode, need to check for other events */ if (!bus->intr && cnt) { /* Check device status, signal pending interrupt */ ret = r_sdreg32(bus, &intstatus, offsetof(struct sdpcmd_regs, intstatus)); bus->sdcnt.f2txdata++; if (ret != 0) break; if (intstatus & bus->hostintmask) bus->ipend = true; } } /* Deflow-control stack if needed */ if (bus->sdiodev->bus_if->drvr_up && (bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) && bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { bus->txoff = false; brcmf_txflowblock(bus->sdiodev->dev, false); } return cnt; } static void brcmf_sdbrcm_bus_stop(struct device *dev) { u32 local_hostintmask; u8 saveclk; int err; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; brcmf_dbg(TRACE, "Enter\n"); if (bus->watchdog_tsk) { send_sig(SIGTERM, bus->watchdog_tsk, 1); kthread_stop(bus->watchdog_tsk); bus->watchdog_tsk = NULL; } if (bus->dpc_tsk && bus->dpc_tsk != current) { send_sig(SIGTERM, bus->dpc_tsk, 1); kthread_stop(bus->dpc_tsk); bus->dpc_tsk = NULL; } down(&bus->sdsem); bus_wake(bus); /* Enable clock for device interrupts */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); /* Disable and clear interrupts at the chip level also */ w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); local_hostintmask = bus->hostintmask; bus->hostintmask = 0; /* Change our idea of bus state */ bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; /* Force clocks on backplane to be sure F2 interrupt propagates */ saveclk = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, (saveclk | SBSDIO_FORCE_HT), &err); } if (err) brcmf_dbg(ERROR, "Failed to force clock for F2: err %d\n", err); /* Turn off the bus (F2), free any pending packets */ brcmf_dbg(INTR, "disable SDIO interrupts\n"); brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1, NULL); /* Clear any pending interrupts now that F2 is disabled */ w_sdreg32(bus, local_hostintmask, offsetof(struct sdpcmd_regs, intstatus)); /* Turn off the backplane clock (only) */ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); /* Clear the data packet queues */ brcmu_pktq_flush(&bus->txq, true, NULL, NULL); /* Clear any held glomming stuff */ if (bus->glomd) brcmu_pkt_buf_free_skb(bus->glomd); brcmf_sdbrcm_free_glom(bus); /* Clear rx control and wake any waiters */ bus->rxlen = 0; brcmf_sdbrcm_dcmd_resp_wake(bus); /* Reset some F2 state stuff */ bus->rxskip = false; bus->tx_seq = bus->rx_seq = 0; up(&bus->sdsem); } #ifdef CONFIG_BRCMFMAC_SDIO_OOB static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) { unsigned long flags; spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); if (!bus->sdiodev->irq_en && !bus->ipend) { enable_irq(bus->sdiodev->irq); bus->sdiodev->irq_en = true; } spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); } #else static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) { } #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) { u32 intstatus, newstatus = 0; uint rxlimit = bus->rxbound; /* Rx frames to read before resched */ uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt = 0; /* Temporary counter of tx/rx frames */ bool rxdone = true; /* Flag for no more read data */ bool resched = false; /* Flag indicating resched wanted */ int err; brcmf_dbg(TRACE, "Enter\n"); /* Start with leftover status bits */ intstatus = bus->intstatus; down(&bus->sdsem); /* If waiting for HTAVAIL, check status */ if (bus->clkstate == CLK_PENDING) { u8 clkctl, devctl = 0; #ifdef DEBUG /* Check for inconsistent device control */ devctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_DEVICE_CTL, &err); if (err) { brcmf_dbg(ERROR, "error reading DEVCTL: %d\n", err); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } #endif /* DEBUG */ /* Read CSR, if clock on switch to AVAIL, else ignore */ clkctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err) { brcmf_dbg(ERROR, "error reading CSR: %d\n", err); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } brcmf_dbg(INFO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl); if (SBSDIO_HTAV(clkctl)) { devctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_DEVICE_CTL, &err); if (err) { brcmf_dbg(ERROR, "error reading DEVCTL: %d\n", err); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, devctl, &err); if (err) { brcmf_dbg(ERROR, "error writing DEVCTL: %d\n", err); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } bus->clkstate = CLK_AVAIL; } else { goto clkwait; } } bus_wake(bus); /* Make sure backplane clock is on */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true); if (bus->clkstate == CLK_PENDING) goto clkwait; /* Pending interrupt indicates new device status */ if (bus->ipend) { bus->ipend = false; err = r_sdreg32(bus, &newstatus, offsetof(struct sdpcmd_regs, intstatus)); bus->sdcnt.f1regdata++; if (err != 0) newstatus = 0; newstatus &= bus->hostintmask; bus->fcstate = !!(newstatus & I_HMB_FC_STATE); if (newstatus) { err = w_sdreg32(bus, newstatus, offsetof(struct sdpcmd_regs, intstatus)); bus->sdcnt.f1regdata++; } } /* Merge new bits with previous */ intstatus |= newstatus; bus->intstatus = 0; /* Handle flow-control change: read new state in case our ack * crossed another change interrupt. If change still set, assume * FC ON for safety, let next loop through do the debounce. */ if (intstatus & I_HMB_FC_CHANGE) { intstatus &= ~I_HMB_FC_CHANGE; err = w_sdreg32(bus, I_HMB_FC_CHANGE, offsetof(struct sdpcmd_regs, intstatus)); err = r_sdreg32(bus, &newstatus, offsetof(struct sdpcmd_regs, intstatus)); bus->sdcnt.f1regdata += 2; bus->fcstate = !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)); intstatus |= (newstatus & bus->hostintmask); } /* Handle host mailbox indication */ if (intstatus & I_HMB_HOST_INT) { intstatus &= ~I_HMB_HOST_INT; intstatus |= brcmf_sdbrcm_hostmail(bus); } /* Generally don't ask for these, can get CRC errors... */ if (intstatus & I_WR_OOSYNC) { brcmf_dbg(ERROR, "Dongle reports WR_OOSYNC\n"); intstatus &= ~I_WR_OOSYNC; } if (intstatus & I_RD_OOSYNC) { brcmf_dbg(ERROR, "Dongle reports RD_OOSYNC\n"); intstatus &= ~I_RD_OOSYNC; } if (intstatus & I_SBINT) { brcmf_dbg(ERROR, "Dongle reports SBINT\n"); intstatus &= ~I_SBINT; } /* Would be active due to wake-wlan in gSPI */ if (intstatus & I_CHIPACTIVE) { brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n"); intstatus &= ~I_CHIPACTIVE; } /* Ignore frame indications if rxskip is set */ if (bus->rxskip) intstatus &= ~I_HMB_FRAME_IND; /* On frame indication, read available frames */ if (PKT_AVAILABLE()) { framecnt = brcmf_sdbrcm_readframes(bus, rxlimit, &rxdone); if (rxdone || bus->rxskip) intstatus &= ~I_HMB_FRAME_IND; rxlimit -= min(framecnt, rxlimit); } /* Keep still-pending events for next scheduling */ bus->intstatus = intstatus; clkwait: brcmf_sdbrcm_clrintr(bus); if (data_ok(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { int ret, i; ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, (u32) bus->ctrl_frame_len); if (ret < 0) { /* On failure, abort the command and terminate the frame */ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", ret); bus->sdcnt.tx_sderrs++; brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, &err); bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; hi = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, &err); lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, &err); bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; } } if (ret == 0) bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; brcmf_dbg(INFO, "Return_dpc value is : %d\n", ret); bus->ctrl_frame_stat = false; brcmf_sdbrcm_wait_event_wakeup(bus); } /* Send queued frames (limit 1 if rx may still be pending) */ else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && data_ok(bus)) { framecnt = rxdone ? txlimit : min(txlimit, bus->txminmax); framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt); txlimit -= framecnt; } /* Resched if events or tx frames are pending, else await next interrupt */ /* On failed register access, all bets are off: no resched or interrupts */ if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) { brcmf_dbg(ERROR, "failed backplane access over SDIO, halting operation\n"); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; bus->intstatus = 0; } else if (bus->clkstate == CLK_PENDING) { brcmf_dbg(INFO, "rescheduled due to CLK_PENDING awaiting I_CHIPACTIVE interrupt\n"); resched = true; } else if (bus->intstatus || bus->ipend || (!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && data_ok(bus)) || PKT_AVAILABLE()) { resched = true; } bus->dpc_sched = resched; /* If we're done for now, turn off clock request. */ if ((bus->clkstate != CLK_PENDING) && bus->idletime == BRCMF_IDLE_IMMEDIATE) { bus->activity = false; brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); } up(&bus->sdsem); return resched; } static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus) { struct list_head *new_hd; unsigned long flags; if (in_interrupt()) new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC); else new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL); if (new_hd == NULL) return; spin_lock_irqsave(&bus->dpc_tl_lock, flags); list_add_tail(new_hd, &bus->dpc_tsklst); spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } static int brcmf_sdbrcm_dpc_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *) data; struct list_head *cur_hd, *tmp_hd; unsigned long flags; allow_signal(SIGTERM); /* Run until signal received */ while (1) { if (kthread_should_stop()) break; if (list_empty(&bus->dpc_tsklst)) if (wait_for_completion_interruptible(&bus->dpc_wait)) break; spin_lock_irqsave(&bus->dpc_tl_lock, flags); list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) { spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { /* after stopping the bus, exit thread */ brcmf_sdbrcm_bus_stop(bus->sdiodev->dev); bus->dpc_tsk = NULL; spin_lock_irqsave(&bus->dpc_tl_lock, flags); break; } if (brcmf_sdbrcm_dpc(bus)) brcmf_sdbrcm_adddpctsk(bus); spin_lock_irqsave(&bus->dpc_tl_lock, flags); list_del(cur_hd); kfree(cur_hd); } spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } return 0; } static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; uint datalen, prec; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; brcmf_dbg(TRACE, "Enter\n"); datalen = pkt->len; /* Add space for the header */ skb_push(pkt, SDPCM_HDRLEN); /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */ prec = prio2prec((pkt->priority & PRIOMASK)); /* Check for existing queue, current flow-control, pending event, or pending clock */ brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); bus->sdcnt.fcqueued++; /* Priority based enq */ spin_lock_bh(&bus->txqlock); if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { skb_pull(pkt, SDPCM_HDRLEN); brcmf_txcomplete(bus->sdiodev->dev, pkt, false); brcmu_pkt_buf_free_skb(pkt); brcmf_dbg(ERROR, "out of bus->txq !!!\n"); ret = -ENOSR; } else { ret = 0; } spin_unlock_bh(&bus->txqlock); if (pktq_len(&bus->txq) >= TXHI) { bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) qcount[prec] = pktq_plen(&bus->txq, prec); #endif /* Schedule DPC if needed to send queued packet(s) */ if (!bus->dpc_sched) { bus->dpc_sched = true; if (bus->dpc_tsk) { brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); } } return ret; } static int brcmf_sdbrcm_membytes(struct brcmf_sdio *bus, bool write, u32 address, u8 *data, uint size) { int bcmerror = 0; u32 sdaddr; uint dsize; /* Determine initial transfer parameters */ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK) dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr); else dsize = size; /* Set the backplane window to include the start address */ bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev, address); if (bcmerror) { brcmf_dbg(ERROR, "window change failed\n"); goto xfer_done; } /* Do the transfer(s) */ while (size) { brcmf_dbg(INFO, "%s %d bytes at offset 0x%08x in window 0x%08x\n", write ? "write" : "read", dsize, sdaddr, address & SBSDIO_SBWINDOW_MASK); bcmerror = brcmf_sdcard_rwdata(bus->sdiodev, write, sdaddr, data, dsize); if (bcmerror) { brcmf_dbg(ERROR, "membytes transfer failed\n"); break; } /* Adjust for next transfer (if any) */ size -= dsize; if (size) { data += dsize; address += dsize; bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev, address); if (bcmerror) { brcmf_dbg(ERROR, "window change failed\n"); break; } sdaddr = 0; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); } } xfer_done: /* Return the window to backplane enumeration space for core access */ if (brcmf_sdcard_set_sbaddr_window(bus->sdiodev, bus->sdiodev->sbwad)) brcmf_dbg(ERROR, "FAILED to set window back to 0x%x\n", bus->sdiodev->sbwad); return bcmerror; } #ifdef DEBUG #define CONSOLE_LINE_MAX 192 static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) { struct brcmf_console *c = &bus->console; u8 line[CONSOLE_LINE_MAX], ch; u32 n, idx, addr; int rv; /* Don't do anything until FWREADY updates console address */ if (bus->console_addr == 0) return 0; /* Read console log struct */ addr = bus->console_addr + offsetof(struct rte_console, log_le); rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&c->log_le, sizeof(c->log_le)); if (rv < 0) return rv; /* Allocate console buffer (one time only) */ if (c->buf == NULL) { c->bufsize = le32_to_cpu(c->log_le.buf_size); c->buf = kmalloc(c->bufsize, GFP_ATOMIC); if (c->buf == NULL) return -ENOMEM; } idx = le32_to_cpu(c->log_le.idx); /* Protect against corrupt value */ if (idx > c->bufsize) return -EBADE; /* Skip reading the console buffer if the index pointer has not moved */ if (idx == c->last) return 0; /* Read the console buffer */ addr = le32_to_cpu(c->log_le.buf); rv = brcmf_sdbrcm_membytes(bus, false, addr, c->buf, c->bufsize); if (rv < 0) return rv; while (c->last != idx) { for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { if (c->last == idx) { /* This would output a partial line. * Instead, back up * the buffer pointer and output this * line next time around. */ if (c->last >= n) c->last -= n; else c->last = c->bufsize - n; goto break2; } ch = c->buf[c->last]; c->last = (c->last + 1) % c->bufsize; if (ch == '\n') break; line[n] = ch; } if (n > 0) { if (line[n - 1] == '\r') n--; line[n] = 0; pr_debug("CONSOLE: %s\n", line); } } break2: return 0; } #endif /* DEBUG */ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) { int i; int ret; bus->ctrl_frame_stat = false; ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, frame, len); if (ret < 0) { /* On failure, abort the command and terminate the frame */ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", ret); bus->sdcnt.tx_sderrs++; brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; hi = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); bus->sdcnt.f1regdata += 2; if (hi == 0 && lo == 0) break; } return ret; } bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; return ret; } static int brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { u8 *frame; u16 len; u32 swheader; uint retries = 0; u8 doff = 0; int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; brcmf_dbg(TRACE, "Enter\n"); /* Back the pointer to make a room for bus header */ frame = msg - SDPCM_HDRLEN; len = (msglen += SDPCM_HDRLEN); /* Add alignment padding (optional for ctl frames) */ doff = ((unsigned long)frame % BRCMF_SDALIGN); if (doff) { frame -= doff; len += doff; msglen += doff; memset(frame, 0, doff + SDPCM_HDRLEN); } /* precondition: doff < BRCMF_SDALIGN */ doff += SDPCM_HDRLEN; /* Round send length to next SDIO block */ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { u16 pad = bus->blocksize - (len % bus->blocksize); if ((pad <= bus->roundup) && (pad < bus->blocksize)) len += pad; } else if (len % BRCMF_SDALIGN) { len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN); } /* Satisfy length-alignment requirements */ if (len & (ALIGNMENT - 1)) len = roundup(len, ALIGNMENT); /* precondition: IS_ALIGNED((unsigned long)frame, 2) */ /* Need to lock here to protect txseq and SDIO tx calls */ down(&bus->sdsem); bus_wake(bus); /* Make sure backplane clock is on */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ *(__le16 *) frame = cpu_to_le16((u16) msglen); *(((__le16 *) frame) + 1) = cpu_to_le16(~msglen); /* Software tag: channel, sequence number, data offset */ swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN); put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); if (!data_ok(bus)) { brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", bus->tx_max, bus->tx_seq); bus->ctrl_frame_stat = true; /* Send from dpc */ bus->ctrl_frame_buf = frame; bus->ctrl_frame_len = len; brcmf_sdbrcm_wait_for_event(bus, &bus->ctrl_frame_stat); if (!bus->ctrl_frame_stat) { brcmf_dbg(INFO, "ctrl_frame_stat == false\n"); ret = 0; } else { brcmf_dbg(INFO, "ctrl_frame_stat == true\n"); ret = -1; } } if (ret == -1) { brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), frame, len, "Tx Frame:\n"); brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && BRCMF_HDRS_ON(), frame, min_t(u16, len, 16), "TxHdr:\n"); do { ret = brcmf_tx_frame(bus, frame, len); } while (ret < 0 && retries++ < TXRETRIES); } if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) { bus->activity = false; brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); } up(&bus->sdsem); if (ret) bus->sdcnt.tx_ctlerrs++; else bus->sdcnt.tx_ctlpkts++; return ret ? -EIO : 0; } #ifdef DEBUG static inline bool brcmf_sdio_valid_shared_address(u32 addr) { return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); } static int brcmf_sdio_readshared(struct brcmf_sdio *bus, struct sdpcm_shared *sh) { u32 addr; int rv; u32 shaddr = 0; struct sdpcm_shared_le sh_le; __le32 addr_le; shaddr = bus->ramsize - 4; /* * Read last word in socram to determine * address of sdpcm_shared structure */ rv = brcmf_sdbrcm_membytes(bus, false, shaddr, (u8 *)&addr_le, 4); if (rv < 0) return rv; addr = le32_to_cpu(addr_le); brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); /* * Check if addr is valid. * NVRAM length at the end of memory should have been overwritten. */ if (!brcmf_sdio_valid_shared_address(addr)) { brcmf_dbg(ERROR, "invalid sdpcm_shared address 0x%08X\n", addr); return -EINVAL; } /* Read hndrte_shared structure */ rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_le, sizeof(struct sdpcm_shared_le)); if (rv < 0) return rv; /* Endianness */ sh->flags = le32_to_cpu(sh_le.flags); sh->trap_addr = le32_to_cpu(sh_le.trap_addr); sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); sh->assert_line = le32_to_cpu(sh_le.assert_line); sh->console_addr = le32_to_cpu(sh_le.console_addr); sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { brcmf_dbg(ERROR, "sdpcm_shared version mismatch: dhd %d dongle %d\n", SDPCM_SHARED_VERSION, sh->flags & SDPCM_SHARED_VERSION_MASK); return -EPROTO; } return 0; } static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, struct sdpcm_shared *sh, char __user *data, size_t count) { u32 addr, console_ptr, console_size, console_index; char *conbuf = NULL; __le32 sh_val; int rv; loff_t pos = 0; int nbytes = 0; /* obtain console information from device memory */ addr = sh->console_addr + offsetof(struct rte_console, log_le); rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_ptr = le32_to_cpu(sh_val); addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_size = le32_to_cpu(sh_val); addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_index = le32_to_cpu(sh_val); /* allocate buffer for console data */ if (console_size <= CONSOLE_BUFFER_MAX) conbuf = vzalloc(console_size+1); if (!conbuf) return -ENOMEM; /* obtain the console data from device */ conbuf[console_size] = '\0'; rv = brcmf_sdbrcm_membytes(bus, false, console_ptr, (u8 *)conbuf, console_size); if (rv < 0) goto done; rv = simple_read_from_buffer(data, count, &pos, conbuf + console_index, console_size - console_index); if (rv < 0) goto done; nbytes = rv; if (console_index > 0) { pos = 0; rv = simple_read_from_buffer(data+nbytes, count, &pos, conbuf, console_index - 1); if (rv < 0) goto done; rv += nbytes; } done: vfree(conbuf); return rv; } static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, char __user *data, size_t count) { int error, res; char buf[350]; struct brcmf_trap_info tr; int nbytes; loff_t pos = 0; if ((sh->flags & SDPCM_SHARED_TRAP) == 0) return 0; error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr, sizeof(struct brcmf_trap_info)); if (error < 0) return error; nbytes = brcmf_sdio_dump_console(bus, sh, data, count); if (nbytes < 0) return nbytes; res = scnprintf(buf, sizeof(buf), "dongle trap info: type 0x%x @ epc 0x%08x\n" " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" " lr 0x%08x pc 0x%08x offset 0x%x\n" " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", le32_to_cpu(tr.type), le32_to_cpu(tr.epc), le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), le32_to_cpu(tr.pc), sh->trap_addr, le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); error = simple_read_from_buffer(data+nbytes, count, &pos, buf, res); if (error < 0) return error; nbytes += error; return nbytes; } static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, char __user *data, size_t count) { int error = 0; char buf[200]; char file[80] = "?"; char expr[80] = ""; int res; loff_t pos = 0; if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { brcmf_dbg(INFO, "firmware not built with -assert\n"); return 0; } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) { brcmf_dbg(INFO, "no assert in dongle\n"); return 0; } if (sh->assert_file_addr != 0) { error = brcmf_sdbrcm_membytes(bus, false, sh->assert_file_addr, (u8 *)file, 80); if (error < 0) return error; } if (sh->assert_exp_addr != 0) { error = brcmf_sdbrcm_membytes(bus, false, sh->assert_exp_addr, (u8 *)expr, 80); if (error < 0) return error; } res = scnprintf(buf, sizeof(buf), "dongle assert: %s:%d: assert(%s)\n", file, sh->assert_line, expr); return simple_read_from_buffer(data, count, &pos, buf, res); } static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) { int error; struct sdpcm_shared sh; down(&bus->sdsem); error = brcmf_sdio_readshared(bus, &sh); up(&bus->sdsem); if (error < 0) return error; if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) brcmf_dbg(INFO, "firmware not built with -assert\n"); else if (sh.flags & SDPCM_SHARED_ASSERT) brcmf_dbg(ERROR, "assertion in dongle\n"); if (sh.flags & SDPCM_SHARED_TRAP) brcmf_dbg(ERROR, "firmware trap in dongle\n"); return 0; } static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, size_t count, loff_t *ppos) { int error = 0; struct sdpcm_shared sh; int nbytes = 0; loff_t pos = *ppos; if (pos != 0) return 0; down(&bus->sdsem); error = brcmf_sdio_readshared(bus, &sh); if (error < 0) goto done; error = brcmf_sdio_assert_info(bus, &sh, data, count); if (error < 0) goto done; nbytes = error; error = brcmf_sdio_trap_info(bus, &sh, data, count); if (error < 0) goto done; error += nbytes; *ppos += error; done: up(&bus->sdsem); return error; } static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data, size_t count, loff_t *ppos) { struct brcmf_sdio *bus = f->private_data; int res; res = brcmf_sdbrcm_died_dump(bus, data, count, ppos); if (res > 0) *ppos += res; return (ssize_t)res; } static const struct file_operations brcmf_sdio_forensic_ops = { .owner = THIS_MODULE, .open = simple_open, .read = brcmf_sdio_forensic_read }; static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) { struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); if (IS_ERR_OR_NULL(dentry)) return; debugfs_create_file("forensics", S_IRUGO, dentry, bus, &brcmf_sdio_forensic_ops); brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); } #else static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) { return 0; } static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) { } #endif /* DEBUG */ static int brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) { int timeleft; uint rxlen = 0; bool pending; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; brcmf_dbg(TRACE, "Enter\n"); /* Wait until control frame is available */ timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending); down(&bus->sdsem); rxlen = bus->rxlen; memcpy(msg, bus->rxctl, min(msglen, rxlen)); bus->rxlen = 0; up(&bus->sdsem); if (rxlen) { brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n", rxlen, msglen); } else if (timeleft == 0) { brcmf_dbg(ERROR, "resumed on timeout\n"); brcmf_sdbrcm_checkdied(bus); } else if (pending) { brcmf_dbg(CTL, "cancelled\n"); return -ERESTARTSYS; } else { brcmf_dbg(CTL, "resumed for unknown reason?\n"); brcmf_sdbrcm_checkdied(bus); } if (rxlen) bus->sdcnt.rx_ctlpkts++; else bus->sdcnt.rx_ctlerrs++; return rxlen ? (int)rxlen : -ETIMEDOUT; } static int brcmf_sdbrcm_write_vars(struct brcmf_sdio *bus) { int bcmerror = 0; u32 varaddr; u32 varsizew; __le32 varsizew_le; #ifdef DEBUG char *nvram_ularray; #endif /* DEBUG */ /* Even if there are no vars are to be written, we still need to set the ramsize. */ varaddr = (bus->ramsize - 4) - bus->varsz; if (bus->vars) { /* Write the vars list */ bcmerror = brcmf_sdbrcm_membytes(bus, true, varaddr, bus->vars, bus->varsz); #ifdef DEBUG /* Verify NVRAM bytes */ brcmf_dbg(INFO, "Compare NVRAM dl & ul; varsize=%d\n", bus->varsz); nvram_ularray = kmalloc(bus->varsz, GFP_ATOMIC); if (!nvram_ularray) return -ENOMEM; /* Upload image to verify downloaded contents. */ memset(nvram_ularray, 0xaa, bus->varsz); /* Read the vars list to temp buffer for comparison */ bcmerror = brcmf_sdbrcm_membytes(bus, false, varaddr, nvram_ularray, bus->varsz); if (bcmerror) { brcmf_dbg(ERROR, "error %d on reading %d nvram bytes at 0x%08x\n", bcmerror, bus->varsz, varaddr); } /* Compare the org NVRAM with the one read from RAM */ if (memcmp(bus->vars, nvram_ularray, bus->varsz)) brcmf_dbg(ERROR, "Downloaded NVRAM image is corrupted\n"); else brcmf_dbg(ERROR, "Download/Upload/Compare of NVRAM ok\n"); kfree(nvram_ularray); #endif /* DEBUG */ } /* adjust to the user specified RAM */ brcmf_dbg(INFO, "Physical memory size: %d\n", bus->ramsize); brcmf_dbg(INFO, "Vars are at %d, orig varsize is %d\n", varaddr, bus->varsz); /* * Determine the length token: * Varsize, converted to words, in lower 16-bits, checksum * in upper 16-bits. */ if (bcmerror) { varsizew = 0; varsizew_le = cpu_to_le32(0); } else { varsizew = bus->varsz / 4; varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); varsizew_le = cpu_to_le32(varsizew); } brcmf_dbg(INFO, "New varsize is %d, length token=0x%08x\n", bus->varsz, varsizew); /* Write the length token to the last word */ bcmerror = brcmf_sdbrcm_membytes(bus, true, (bus->ramsize - 4), (u8 *)&varsizew_le, 4); return bcmerror; } static int brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter) { int bcmerror = 0; struct chip_info *ci = bus->ci; /* To enter download state, disable ARM and reset SOCRAM. * To exit download state, simply reset ARM (default is RAM boot). */ if (enter) { bus->alp_only = true; ci->coredisable(bus->sdiodev, ci, BCMA_CORE_ARM_CM3); ci->resetcore(bus->sdiodev, ci, BCMA_CORE_INTERNAL_MEM); /* Clear the top bit of memory */ if (bus->ramsize) { u32 zeros = 0; brcmf_sdbrcm_membytes(bus, true, bus->ramsize - 4, (u8 *)&zeros, 4); } } else { if (!ci->iscoreup(bus->sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) { brcmf_dbg(ERROR, "SOCRAM core is down after reset?\n"); bcmerror = -EBADE; goto fail; } bcmerror = brcmf_sdbrcm_write_vars(bus); if (bcmerror) { brcmf_dbg(ERROR, "no vars written to RAM\n"); bcmerror = 0; } w_sdreg32(bus, 0xFFFFFFFF, offsetof(struct sdpcmd_regs, intstatus)); ci->resetcore(bus->sdiodev, ci, BCMA_CORE_ARM_CM3); /* Allow HT Clock now that the ARM is running. */ bus->alp_only = false; bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD; } fail: return bcmerror; } static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus) { if (bus->firmware->size < bus->fw_ptr + len) len = bus->firmware->size - bus->fw_ptr; memcpy(buf, &bus->firmware->data[bus->fw_ptr], len); bus->fw_ptr += len; return len; } static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) { int offset = 0; uint len; u8 *memblock = NULL, *memptr; int ret; brcmf_dbg(INFO, "Enter\n"); ret = request_firmware(&bus->firmware, BRCMF_SDIO_FW_NAME, &bus->sdiodev->func[2]->dev); if (ret) { brcmf_dbg(ERROR, "Fail to request firmware %d\n", ret); return ret; } bus->fw_ptr = 0; memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC); if (memblock == NULL) { ret = -ENOMEM; goto err; } if ((u32)(unsigned long)memblock % BRCMF_SDALIGN) memptr += (BRCMF_SDALIGN - ((u32)(unsigned long)memblock % BRCMF_SDALIGN)); /* Download image */ while ((len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus))) { ret = brcmf_sdbrcm_membytes(bus, true, offset, memptr, len); if (ret) { brcmf_dbg(ERROR, "error %d on writing %d membytes at 0x%08x\n", ret, MEMBLOCK, offset); goto err; } offset += MEMBLOCK; } err: kfree(memblock); release_firmware(bus->firmware); bus->fw_ptr = 0; return ret; } /* * ProcessVars:Takes a buffer of "=\n" lines read from a file * and ending in a NUL. * Removes carriage returns, empty lines, comment lines, and converts * newlines to NULs. * Shortens buffer as needed and pads with NULs. End of buffer is marked * by two NULs. */ static int brcmf_process_nvram_vars(struct brcmf_sdio *bus) { char *varbuf; char *dp; bool findNewline; int column; int ret = 0; uint buf_len, n, len; len = bus->firmware->size; varbuf = vmalloc(len); if (!varbuf) return -ENOMEM; memcpy(varbuf, bus->firmware->data, len); dp = varbuf; findNewline = false; column = 0; for (n = 0; n < len; n++) { if (varbuf[n] == 0) break; if (varbuf[n] == '\r') continue; if (findNewline && varbuf[n] != '\n') continue; findNewline = false; if (varbuf[n] == '#') { findNewline = true; continue; } if (varbuf[n] == '\n') { if (column == 0) continue; *dp++ = 0; column = 0; continue; } *dp++ = varbuf[n]; column++; } buf_len = dp - varbuf; while (dp < varbuf + n) *dp++ = 0; kfree(bus->vars); /* roundup needed for download to device */ bus->varsz = roundup(buf_len + 1, 4); bus->vars = kmalloc(bus->varsz, GFP_KERNEL); if (bus->vars == NULL) { bus->varsz = 0; ret = -ENOMEM; goto err; } /* copy the processed variables and add null termination */ memcpy(bus->vars, varbuf, buf_len); bus->vars[buf_len] = 0; err: vfree(varbuf); return ret; } static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) { int ret; if (bus->sdiodev->bus_if->drvr_up) return -EISCONN; ret = request_firmware(&bus->firmware, BRCMF_SDIO_NV_NAME, &bus->sdiodev->func[2]->dev); if (ret) { brcmf_dbg(ERROR, "Fail to request nvram %d\n", ret); return ret; } ret = brcmf_process_nvram_vars(bus); release_firmware(bus->firmware); return ret; } static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) { int bcmerror = -1; /* Keep arm in reset */ if (brcmf_sdbrcm_download_state(bus, true)) { brcmf_dbg(ERROR, "error placing ARM core in reset\n"); goto err; } /* External image takes precedence if specified */ if (brcmf_sdbrcm_download_code_file(bus)) { brcmf_dbg(ERROR, "dongle image file download failed\n"); goto err; } /* External nvram takes precedence if specified */ if (brcmf_sdbrcm_download_nvram(bus)) brcmf_dbg(ERROR, "dongle nvram file download failed\n"); /* Take arm out of reset */ if (brcmf_sdbrcm_download_state(bus, false)) { brcmf_dbg(ERROR, "error getting out of ARM core reset\n"); goto err; } bcmerror = 0; err: return bcmerror; } static bool brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) { bool ret; /* Download the firmware */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); ret = _brcmf_sdbrcm_download_firmware(bus) == 0; brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); return ret; } static int brcmf_sdbrcm_bus_init(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; unsigned long timeout; u8 ready, enable; int err, ret = 0; u8 saveclk; brcmf_dbg(TRACE, "Enter\n"); /* try to download image and nvram to the dongle */ if (bus_if->state == BRCMF_BUS_DOWN) { if (!(brcmf_sdbrcm_download_firmware(bus))) return -1; } if (!bus->sdiodev->bus_if->drvr) return 0; /* Start the watchdog timer */ bus->sdcnt.tickcnt = 0; brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); down(&bus->sdsem); /* Make sure backplane clock is on, needed to generate F2 interrupt */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); if (bus->clkstate != CLK_AVAIL) goto exit; /* Force clocks on backplane to be sure F2 interrupt propagates */ saveclk = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, (saveclk | SBSDIO_FORCE_HT), &err); } if (err) { brcmf_dbg(ERROR, "Failed to force clock for F2: err %d\n", err); goto exit; } /* Enable function 2 (frame transfers) */ w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, offsetof(struct sdpcmd_regs, tosbmailboxdata)); enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2); brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); timeout = jiffies + msecs_to_jiffies(BRCMF_WAIT_F2RDY); ready = 0; while (enable != ready) { ready = brcmf_sdio_regrb(bus->sdiodev, SDIO_CCCR_IORx, NULL); if (time_after(jiffies, timeout)) break; else if (time_after(jiffies, timeout - BRCMF_WAIT_F2RDY + 50)) /* prevent busy waiting if it takes too long */ msleep_interruptible(20); } brcmf_dbg(INFO, "enable 0x%02x, ready 0x%02x\n", enable, ready); /* If F2 successfully enabled, set core and enable interrupts */ if (ready == enable) { /* Set up the interrupt mask and enable interrupts */ bus->hostintmask = HOSTINTMASK; w_sdreg32(bus, bus->hostintmask, offsetof(struct sdpcmd_regs, hostintmask)); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); } else { /* Disable F2 again */ enable = SDIO_FUNC_ENABLE_1; brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); ret = -ENODEV; } /* Restore previous clock setting */ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); if (ret == 0) { ret = brcmf_sdio_intr_register(bus->sdiodev); if (ret != 0) brcmf_dbg(ERROR, "intr register failed:%d\n", ret); } /* If we didn't come up, turn off backplane clock */ if (bus_if->state != BRCMF_BUS_DATA) brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); exit: up(&bus->sdsem); return ret; } void brcmf_sdbrcm_isr(void *arg) { struct brcmf_sdio *bus = (struct brcmf_sdio *) arg; brcmf_dbg(TRACE, "Enter\n"); if (!bus) { brcmf_dbg(ERROR, "bus is null pointer, exiting\n"); return; } if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { brcmf_dbg(ERROR, "bus is down. we have nothing to do\n"); return; } /* Count the interrupt call */ bus->sdcnt.intrcount++; bus->ipend = true; /* Shouldn't get this interrupt if we're sleeping? */ if (bus->sleeping) { brcmf_dbg(ERROR, "INTERRUPT WHILE SLEEPING??\n"); return; } /* Disable additional interrupts (is this needed now)? */ if (!bus->intr) brcmf_dbg(ERROR, "isr w/o interrupt configured!\n"); bus->dpc_sched = true; if (bus->dpc_tsk) { brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); } } static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) { #ifdef DEBUG struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); #endif /* DEBUG */ brcmf_dbg(TIMER, "Enter\n"); /* Ignore the timer if simulating bus down */ if (bus->sleeping) return false; down(&bus->sdsem); /* Poll period: check device if appropriate. */ if (bus->poll && (++bus->polltick >= bus->pollrate)) { u32 intstatus = 0; /* Reset poll tick */ bus->polltick = 0; /* Check device if no interrupts */ if (!bus->intr || (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { if (!bus->dpc_sched) { u8 devpend; devpend = brcmf_sdio_regrb(bus->sdiodev, SDIO_CCCR_INTx, NULL); intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2); } /* If there is something, make like the ISR and schedule the DPC */ if (intstatus) { bus->sdcnt.pollcnt++; bus->ipend = true; bus->dpc_sched = true; if (bus->dpc_tsk) { brcmf_sdbrcm_adddpctsk(bus); complete(&bus->dpc_wait); } } } /* Update interrupt tracking */ bus->sdcnt.lastintrs = bus->sdcnt.intrcount; } #ifdef DEBUG /* Poll for console output periodically */ if (bus_if->state == BRCMF_BUS_DATA && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { bus->console.count -= bus->console_interval; /* Make sure backplane clock is on */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); if (brcmf_sdbrcm_readconsole(bus) < 0) /* stop on error */ bus->console_interval = 0; } } #endif /* DEBUG */ /* On idle timeout clear activity flag and/or turn off clock */ if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { if (++bus->idlecount >= bus->idletime) { bus->idlecount = 0; if (bus->activity) { bus->activity = false; brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); } else { brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); } } } up(&bus->sdsem); return bus->ipend; } static bool brcmf_sdbrcm_chipmatch(u16 chipid) { if (chipid == BCM43241_CHIP_ID) return true; if (chipid == BCM4329_CHIP_ID) return true; if (chipid == BCM4330_CHIP_ID) return true; if (chipid == BCM4334_CHIP_ID) return true; return false; } static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); kfree(bus->rxbuf); bus->rxctl = bus->rxbuf = NULL; bus->rxlen = 0; kfree(bus->databuf); bus->databuf = NULL; } static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); if (bus->sdiodev->bus_if->maxctl) { bus->rxblen = roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), ALIGNMENT) + BRCMF_SDALIGN; bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); if (!(bus->rxbuf)) goto fail; } /* Allocate buffer to receive glomed packet */ bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC); if (!(bus->databuf)) { /* release rxbuf which was already located as above */ if (!bus->rxblen) kfree(bus->rxbuf); goto fail; } /* Align the buffer */ if ((unsigned long)bus->databuf % BRCMF_SDALIGN) bus->dataptr = bus->databuf + (BRCMF_SDALIGN - ((unsigned long)bus->databuf % BRCMF_SDALIGN)); else bus->dataptr = bus->databuf; return true; fail: return false; } static bool brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) { u8 clkctl = 0; int err = 0; int reg_addr; u32 reg_val; u8 idx; bus->alp_only = true; pr_debug("F1 signature read @0x18000000=0x%4x\n", brcmf_sdio_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); /* * Force PLL off until brcmf_sdio_chip_attach() * programs PLL control regs */ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, BRCMF_INIT_CLKCTL1, &err); if (!err) clkctl = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { brcmf_dbg(ERROR, "ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", err, BRCMF_INIT_CLKCTL1, clkctl); goto fail; } if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) { brcmf_dbg(ERROR, "brcmf_sdio_chip_attach failed!\n"); goto fail; } if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) { brcmf_dbg(ERROR, "unsupported chip: 0x%04x\n", bus->ci->chip); goto fail; } brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, SDIO_DRIVE_STRENGTH); /* Get info on the SOCRAM cores... */ bus->ramsize = bus->ci->ramsize; if (!(bus->ramsize)) { brcmf_dbg(ERROR, "failed to find SOCRAM memory!\n"); goto fail; } /* Set core control so an SDIO reset does a backplane reset */ idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); reg_addr = bus->ci->c_inf[idx].base + offsetof(struct sdpcmd_regs, corecontrol); reg_val = brcmf_sdio_regrl(bus->sdiodev, reg_addr, NULL); brcmf_sdio_regwl(bus->sdiodev, reg_addr, reg_val | CC_BPRESEN, NULL); brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); /* Locate an appropriately-aligned portion of hdrbuf */ bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], BRCMF_SDALIGN); /* Set the poll and/or interrupt flags */ bus->intr = true; bus->poll = false; if (bus->poll) bus->pollrate = 1; return true; fail: return false; } static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); /* Disable F2 to clear any intermediate frame state on the dongle */ brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1, NULL); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; bus->sleeping = false; bus->rxflow = false; /* Done with backplane-dependent accesses, can drop clock... */ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); /* ...and initialize clock/power states */ bus->clkstate = CLK_SDONLY; bus->idletime = BRCMF_IDLE_INTERVAL; bus->idleclock = BRCMF_IDLE_ACTIVE; /* Query the F2 block size, set roundup accordingly */ bus->blocksize = bus->sdiodev->func[2]->cur_blksize; bus->roundup = min(max_roundup, bus->blocksize); /* bus module does not support packet chaining */ bus->use_rxchain = false; bus->sd_rxchain = false; return true; } static int brcmf_sdbrcm_watchdog_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; allow_signal(SIGTERM); /* Run until signal received */ while (1) { if (kthread_should_stop()) break; if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { brcmf_sdbrcm_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; } else break; } return 0; } static void brcmf_sdbrcm_watchdog(unsigned long data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; if (bus->watchdog_tsk) { complete(&bus->watchdog_wait); /* Reschedule the watchdog */ if (bus->wd_timer_valid) mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL_MS * HZ / 1000); } } static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); if (bus->ci) { brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); brcmf_sdio_chip_detach(&bus->ci); if (bus->vars && bus->varsz) kfree(bus->vars); bus->vars = NULL; } brcmf_dbg(TRACE, "Disconnected\n"); } /* Detach and free everything */ static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); if (bus) { /* De-register interrupt handler */ brcmf_sdio_intr_unregister(bus->sdiodev); if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); brcmf_sdbrcm_release_dongle(bus); } brcmf_sdbrcm_release_malloc(bus); kfree(bus); } brcmf_dbg(TRACE, "Disconnected\n"); } void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) { int ret; struct brcmf_sdio *bus; struct brcmf_bus_dcmd *dlst; u32 dngl_txglom; u32 dngl_txglomalign; u8 idx; brcmf_dbg(TRACE, "Enter\n"); /* We make an assumption about address window mappings: * regsva == SI_ENUM_BASE*/ /* Allocate private bus interface state */ bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); if (!bus) goto fail; bus->sdiodev = sdiodev; sdiodev->bus = bus; skb_queue_head_init(&bus->glom); bus->txbound = BRCMF_TXBOUND; bus->rxbound = BRCMF_RXBOUND; bus->txminmax = BRCMF_TXMINMAX; bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; bus->usebufpool = false; /* Use bufpool if allocated, else use locally malloced rxbuf */ /* attempt to attach to the dongle */ if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_attach failed\n"); goto fail; } spin_lock_init(&bus->txqlock); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); /* Set up the watchdog timer */ init_timer(&bus->timer); bus->timer.data = (unsigned long)bus; bus->timer.function = brcmf_sdbrcm_watchdog; /* Initialize thread based operation and lock */ sema_init(&bus->sdsem, 1); /* Initialize watchdog thread */ init_completion(&bus->watchdog_wait); bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread, bus, "brcmf_watchdog"); if (IS_ERR(bus->watchdog_tsk)) { pr_warn("brcmf_watchdog thread failed to start\n"); bus->watchdog_tsk = NULL; } /* Initialize DPC thread */ init_completion(&bus->dpc_wait); INIT_LIST_HEAD(&bus->dpc_tsklst); spin_lock_init(&bus->dpc_tl_lock); bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread, bus, "brcmf_dpc"); if (IS_ERR(bus->dpc_tsk)) { pr_warn("brcmf_dpc thread failed to start\n"); bus->dpc_tsk = NULL; } /* Assign bus interface call back */ bus->sdiodev->bus_if->brcmf_bus_stop = brcmf_sdbrcm_bus_stop; bus->sdiodev->bus_if->brcmf_bus_init = brcmf_sdbrcm_bus_init; bus->sdiodev->bus_if->brcmf_bus_txdata = brcmf_sdbrcm_bus_txdata; bus->sdiodev->bus_if->brcmf_bus_txctl = brcmf_sdbrcm_bus_txctl; bus->sdiodev->bus_if->brcmf_bus_rxctl = brcmf_sdbrcm_bus_rxctl; /* Attach to the brcmf/OS/network interface */ ret = brcmf_attach(SDPCM_RESERVE, bus->sdiodev->dev); if (ret != 0) { brcmf_dbg(ERROR, "brcmf_attach failed\n"); goto fail; } /* Allocate buffers */ if (!(brcmf_sdbrcm_probe_malloc(bus))) { brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_malloc failed\n"); goto fail; } if (!(brcmf_sdbrcm_probe_init(bus))) { brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_init failed\n"); goto fail; } brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); /* sdio bus core specific dcmd */ idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); dlst = kzalloc(sizeof(struct brcmf_bus_dcmd), GFP_KERNEL); if (dlst) { if (bus->ci->c_inf[idx].rev < 12) { /* for sdio core rev < 12, disable txgloming */ dngl_txglom = 0; dlst->name = "bus:txglom"; dlst->param = (char *)&dngl_txglom; dlst->param_len = sizeof(u32); } else { /* otherwise, set txglomalign */ dngl_txglomalign = bus->sdiodev->bus_if->align; dlst->name = "bus:txglomalign"; dlst->param = (char *)&dngl_txglomalign; dlst->param_len = sizeof(u32); } list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list); } /* if firmware path present try to download and bring up bus */ ret = brcmf_bus_start(bus->sdiodev->dev); if (ret != 0) { if (ret == -ENOLINK) { brcmf_dbg(ERROR, "dongle is not responding\n"); goto fail; } } return bus; fail: brcmf_sdbrcm_release(bus); return NULL; } void brcmf_sdbrcm_disconnect(void *ptr) { struct brcmf_sdio *bus = (struct brcmf_sdio *)ptr; brcmf_dbg(TRACE, "Enter\n"); if (bus) brcmf_sdbrcm_release(bus); brcmf_dbg(TRACE, "Disconnected\n"); } void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick) { /* Totally stop the timer */ if (!wdtick && bus->wd_timer_valid) { del_timer_sync(&bus->timer); bus->wd_timer_valid = false; bus->save_ms = wdtick; return; } /* don't start the wd until fw is loaded */ if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) return; if (wdtick) { if (bus->save_ms != BRCMF_WD_POLL_MS) { if (bus->wd_timer_valid) /* Stop timer and restart at new value */ del_timer_sync(&bus->timer); /* Create timer again when watchdog period is dynamically changed or in the first instance */ bus->timer.expires = jiffies + BRCMF_WD_POLL_MS * HZ / 1000; add_timer(&bus->timer); } else { /* Re arm the timer, at last watchdog period */ mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL_MS * HZ / 1000); } bus->wd_timer_valid = true; bus->save_ms = wdtick; } } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c0000644000175000017500000034245712026211315025572 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhd.h" #include "wl_cfg80211.h" #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255}; static u32 brcmf_dbg_level = WL_DBG_ERR; static void brcmf_set_drvdata(struct brcmf_cfg80211_dev *dev, void *data) { dev->driver_data = data; } static void *brcmf_get_drvdata(struct brcmf_cfg80211_dev *dev) { void *data = NULL; if (dev) data = dev->driver_data; return data; } static struct brcmf_cfg80211_priv *brcmf_priv_get(struct brcmf_cfg80211_dev *cfg_dev) { struct brcmf_cfg80211_iface *ci = brcmf_get_drvdata(cfg_dev); return ci->cfg_priv; } static bool check_sys_up(struct wiphy *wiphy) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); if (!test_bit(WL_STATUS_READY, &cfg_priv->status)) { WL_INFO("device is not ready : status (%d)\n", (int)cfg_priv->status); return false; } return true; } #define CHAN2G(_channel, _freq, _flags) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } #define CHAN5G(_channel, _flags) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } #define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) #define RATETAB_ENT(_rateid, _flags) \ { \ .bitrate = RATE_TO_BASE100KBPS(_rateid), \ .hw_value = (_rateid), \ .flags = (_flags), \ } static struct ieee80211_rate __wl_rates[] = { RATETAB_ENT(BRCM_RATE_1M, 0), RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(BRCM_RATE_6M, 0), RATETAB_ENT(BRCM_RATE_9M, 0), RATETAB_ENT(BRCM_RATE_12M, 0), RATETAB_ENT(BRCM_RATE_18M, 0), RATETAB_ENT(BRCM_RATE_24M, 0), RATETAB_ENT(BRCM_RATE_36M, 0), RATETAB_ENT(BRCM_RATE_48M, 0), RATETAB_ENT(BRCM_RATE_54M, 0), }; #define wl_a_rates (__wl_rates + 4) #define wl_a_rates_size 8 #define wl_g_rates (__wl_rates + 0) #define wl_g_rates_size 12 static struct ieee80211_channel __wl_2ghz_channels[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), CHAN2G(3, 2422, 0), CHAN2G(4, 2427, 0), CHAN2G(5, 2432, 0), CHAN2G(6, 2437, 0), CHAN2G(7, 2442, 0), CHAN2G(8, 2447, 0), CHAN2G(9, 2452, 0), CHAN2G(10, 2457, 0), CHAN2G(11, 2462, 0), CHAN2G(12, 2467, 0), CHAN2G(13, 2472, 0), CHAN2G(14, 2484, 0), }; static struct ieee80211_channel __wl_5ghz_a_channels[] = { CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0), CHAN5G(108, 0), CHAN5G(112, 0), CHAN5G(116, 0), CHAN5G(120, 0), CHAN5G(124, 0), CHAN5G(128, 0), CHAN5G(132, 0), CHAN5G(136, 0), CHAN5G(140, 0), CHAN5G(149, 0), CHAN5G(153, 0), CHAN5G(157, 0), CHAN5G(161, 0), CHAN5G(165, 0), CHAN5G(184, 0), CHAN5G(188, 0), CHAN5G(192, 0), CHAN5G(196, 0), CHAN5G(200, 0), CHAN5G(204, 0), CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; static struct ieee80211_channel __wl_5ghz_n_channels[] = { CHAN5G(32, 0), CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(50, 0), CHAN5G(52, 0), CHAN5G(54, 0), CHAN5G(56, 0), CHAN5G(58, 0), CHAN5G(60, 0), CHAN5G(62, 0), CHAN5G(64, 0), CHAN5G(66, 0), CHAN5G(68, 0), CHAN5G(70, 0), CHAN5G(72, 0), CHAN5G(74, 0), CHAN5G(76, 0), CHAN5G(78, 0), CHAN5G(80, 0), CHAN5G(82, 0), CHAN5G(84, 0), CHAN5G(86, 0), CHAN5G(88, 0), CHAN5G(90, 0), CHAN5G(92, 0), CHAN5G(94, 0), CHAN5G(96, 0), CHAN5G(98, 0), CHAN5G(100, 0), CHAN5G(102, 0), CHAN5G(104, 0), CHAN5G(106, 0), CHAN5G(108, 0), CHAN5G(110, 0), CHAN5G(112, 0), CHAN5G(114, 0), CHAN5G(116, 0), CHAN5G(118, 0), CHAN5G(120, 0), CHAN5G(122, 0), CHAN5G(124, 0), CHAN5G(126, 0), CHAN5G(128, 0), CHAN5G(130, 0), CHAN5G(132, 0), CHAN5G(134, 0), CHAN5G(136, 0), CHAN5G(138, 0), CHAN5G(140, 0), CHAN5G(142, 0), CHAN5G(144, 0), CHAN5G(145, 0), CHAN5G(146, 0), CHAN5G(147, 0), CHAN5G(148, 0), CHAN5G(149, 0), CHAN5G(150, 0), CHAN5G(151, 0), CHAN5G(152, 0), CHAN5G(153, 0), CHAN5G(154, 0), CHAN5G(155, 0), CHAN5G(156, 0), CHAN5G(157, 0), CHAN5G(158, 0), CHAN5G(159, 0), CHAN5G(160, 0), CHAN5G(161, 0), CHAN5G(162, 0), CHAN5G(163, 0), CHAN5G(164, 0), CHAN5G(165, 0), CHAN5G(166, 0), CHAN5G(168, 0), CHAN5G(170, 0), CHAN5G(172, 0), CHAN5G(174, 0), CHAN5G(176, 0), CHAN5G(178, 0), CHAN5G(180, 0), CHAN5G(182, 0), CHAN5G(184, 0), CHAN5G(186, 0), CHAN5G(188, 0), CHAN5G(190, 0), CHAN5G(192, 0), CHAN5G(194, 0), CHAN5G(196, 0), CHAN5G(198, 0), CHAN5G(200, 0), CHAN5G(202, 0), CHAN5G(204, 0), CHAN5G(206, 0), CHAN5G(208, 0), CHAN5G(210, 0), CHAN5G(212, 0), CHAN5G(214, 0), CHAN5G(216, 0), CHAN5G(218, 0), CHAN5G(220, 0), CHAN5G(222, 0), CHAN5G(224, 0), CHAN5G(226, 0), CHAN5G(228, 0), }; static struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, .channels = __wl_2ghz_channels, .n_channels = ARRAY_SIZE(__wl_2ghz_channels), .bitrates = wl_g_rates, .n_bitrates = wl_g_rates_size, }; static struct ieee80211_supported_band __wl_band_5ghz_a = { .band = IEEE80211_BAND_5GHZ, .channels = __wl_5ghz_a_channels, .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size, }; static struct ieee80211_supported_band __wl_band_5ghz_n = { .band = IEEE80211_BAND_5GHZ, .channels = __wl_5ghz_n_channels, .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels), .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size, }; static const u32 __wl_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WLAN_CIPHER_SUITE_AES_CMAC, }; /* tag_ID/length/value_buffer tuple */ struct brcmf_tlv { u8 id; u8 len; u8 data[1]; }; /* Quarter dBm units to mW * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 * Table is offset so the last entry is largest mW value that fits in * a u16. */ #define QDBM_OFFSET 153 /* Offset for first entry */ #define QDBM_TABLE_LEN 40 /* Table size */ /* Smallest mW value that will round up to the first table entry, QDBM_OFFSET. * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2 */ #define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */ /* Largest mW value that will round down to the last table entry, * QDBM_OFFSET + QDBM_TABLE_LEN-1. * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2. */ #define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */ static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = { /* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */ /* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, /* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849, /* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, /* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, /* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096 }; static u16 brcmf_qdbm_to_mw(u8 qdbm) { uint factor = 1; int idx = qdbm - QDBM_OFFSET; if (idx >= QDBM_TABLE_LEN) /* clamp to max u16 mW value */ return 0xFFFF; /* scale the qdBm index up to the range of the table 0-40 * where an offset of 40 qdBm equals a factor of 10 mW. */ while (idx < 0) { idx += 40; factor *= 10; } /* return the mW value scaled down to the correct factor of 10, * adding in factor/2 to get proper rounding. */ return (nqdBm_to_mW_map[idx] + factor / 2) / factor; } static u8 brcmf_mw_to_qdbm(u16 mw) { u8 qdbm; int offset; uint mw_uint = mw; uint boundary; /* handle boundary case */ if (mw_uint <= 1) return 0; offset = QDBM_OFFSET; /* move mw into the range of the table */ while (mw_uint < QDBM_TABLE_LOW_BOUND) { mw_uint *= 10; offset -= 40; } for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) { boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] - nqdBm_to_mW_map[qdbm]) / 2; if (mw_uint < boundary) break; } qdbm += (u8) offset; return qdbm; } /* function for reading/writing a single u32 from/to the dongle */ static int brcmf_exec_dcmd_u32(struct net_device *ndev, u32 cmd, u32 *par) { int err; __le32 par_le = cpu_to_le32(*par); err = brcmf_exec_dcmd(ndev, cmd, &par_le, sizeof(__le32)); *par = le32_to_cpu(par_le); return err; } static void convert_key_from_CPU(struct brcmf_wsec_key *key, struct brcmf_wsec_key_le *key_le) { key_le->index = cpu_to_le32(key->index); key_le->len = cpu_to_le32(key->len); key_le->algo = cpu_to_le32(key->algo); key_le->flags = cpu_to_le32(key->flags); key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi); key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo); key_le->iv_initialized = cpu_to_le32(key->iv_initialized); memcpy(key_le->data, key->data, sizeof(key->data)); memcpy(key_le->ea, key->ea, sizeof(key->ea)); } static int send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key) { int err; struct brcmf_wsec_key_le key_le; convert_key_from_CPU(key, &key_le); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_KEY, &key_le, sizeof(key_le)); if (err) WL_ERR("WLC_SET_KEY error (%d)\n", err); return err; } static s32 brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct wireless_dev *wdev; s32 infra = 0; s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: WL_ERR("type (%d) : currently we do not support this type\n", type); return -EOPNOTSUPP; case NL80211_IFTYPE_ADHOC: cfg_priv->conf->mode = WL_MODE_IBSS; infra = 0; break; case NL80211_IFTYPE_STATION: cfg_priv->conf->mode = WL_MODE_BSS; infra = 1; break; default: err = -EINVAL; goto done; } err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra); if (err) { WL_ERR("WLC_SET_INFRA error (%d)\n", err); err = -EAGAIN; } else { wdev = ndev->ieee80211_ptr; wdev->iftype = type; } WL_INFO("IF Type = %s\n", (cfg_priv->conf->mode == WL_MODE_IBSS) ? "Adhoc" : "Infra"); done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_dev_intvar_set(struct net_device *ndev, s8 *name, s32 val) { s8 buf[BRCMF_DCMD_SMLEN]; u32 len; s32 err = 0; __le32 val_le; val_le = cpu_to_le32(val); len = brcmf_c_mkiovar(name, (char *)(&val_le), sizeof(val_le), buf, sizeof(buf)); BUG_ON(!len); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, buf, len); if (err) WL_ERR("error (%d)\n", err); return err; } static s32 brcmf_dev_intvar_get(struct net_device *ndev, s8 *name, s32 *retval) { union { s8 buf[BRCMF_DCMD_SMLEN]; __le32 val; } var; u32 len; u32 data_null; s32 err = 0; len = brcmf_c_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf)); BUG_ON(!len); err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, &var, len); if (err) WL_ERR("error (%d)\n", err); *retval = le32_to_cpu(var.val); return err; } static void brcmf_set_mpc(struct net_device *ndev, int mpc) { s32 err = 0; struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); if (test_bit(WL_STATUS_READY, &cfg_priv->status)) { err = brcmf_dev_intvar_set(ndev, "mpc", mpc); if (err) { WL_ERR("fail to set mpc\n"); return; } WL_INFO("MPC : %d\n", mpc); } } static void brcmf_iscan_prep(struct brcmf_scan_params_le *params_le, struct brcmf_ssid *ssid) { memcpy(params_le->bssid, ether_bcast, ETH_ALEN); params_le->bss_type = DOT11_BSSTYPE_ANY; params_le->scan_type = 0; params_le->channel_num = 0; params_le->nprobes = cpu_to_le32(-1); params_le->active_time = cpu_to_le32(-1); params_le->passive_time = cpu_to_le32(-1); params_le->home_time = cpu_to_le32(-1); if (ssid && ssid->SSID_len) { params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len); memcpy(¶ms_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len); } } static s32 brcmf_dev_iovar_setbuf(struct net_device *ndev, s8 * iovar, void *param, s32 paramlen, void *bufptr, s32 buflen) { s32 iolen; iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen); BUG_ON(!iolen); return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, bufptr, iolen); } static s32 brcmf_dev_iovar_getbuf(struct net_device *ndev, s8 * iovar, void *param, s32 paramlen, void *bufptr, s32 buflen) { s32 iolen; iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen); BUG_ON(!iolen); return brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, bufptr, buflen); } static s32 brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan, struct brcmf_ssid *ssid, u16 action) { s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + offsetof(struct brcmf_iscan_params_le, params_le); struct brcmf_iscan_params_le *params; s32 err = 0; if (ssid && ssid->SSID_len) params_size += sizeof(struct brcmf_ssid); params = kzalloc(params_size, GFP_KERNEL); if (!params) return -ENOMEM; BUG_ON(params_size >= BRCMF_DCMD_SMLEN); brcmf_iscan_prep(¶ms->params_le, ssid); params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION); params->action = cpu_to_le16(action); params->scan_duration = cpu_to_le16(0); err = brcmf_dev_iovar_setbuf(iscan->ndev, "iscan", params, params_size, iscan->dcmd_buf, BRCMF_DCMD_SMLEN); if (err) { if (err == -EBUSY) WL_INFO("system busy : iscan canceled\n"); else WL_ERR("error (%d)\n", err); } kfree(params); return err; } static s32 brcmf_do_iscan(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv); struct net_device *ndev = cfg_to_ndev(cfg_priv); struct brcmf_ssid ssid; __le32 passive_scan; s32 err = 0; /* Broadcast scan by default */ memset(&ssid, 0, sizeof(ssid)); iscan->state = WL_ISCAN_STATE_SCANING; passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1); err = brcmf_exec_dcmd(cfg_to_ndev(cfg_priv), BRCMF_C_SET_PASSIVE_SCAN, &passive_scan, sizeof(passive_scan)); if (err) { WL_ERR("error (%d)\n", err); return err; } brcmf_set_mpc(ndev, 0); cfg_priv->iscan_kickstart = true; err = brcmf_run_iscan(iscan, &ssid, BRCMF_SCAN_ACTION_START); if (err) { brcmf_set_mpc(ndev, 1); cfg_priv->iscan_kickstart = false; return err; } mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); iscan->timer_on = 1; return err; } static s32 brcmf_cfg80211_iscan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request, struct cfg80211_ssid *this_ssid) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); struct cfg80211_ssid *ssids; struct brcmf_cfg80211_scan_req *sr = cfg_priv->scan_req_int; __le32 passive_scan; bool iscan_req; bool spec_scan; s32 err = 0; u32 SSID_len; if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status); return -EAGAIN; } if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status)) { WL_ERR("Scanning being aborted : status (%lu)\n", cfg_priv->status); return -EAGAIN; } if (test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) { WL_ERR("Connecting : status (%lu)\n", cfg_priv->status); return -EAGAIN; } iscan_req = false; spec_scan = false; if (request) { /* scan bss */ ssids = request->ssids; if (cfg_priv->iscan_on && (!ssids || !ssids->ssid_len)) iscan_req = true; } else { /* scan in ibss */ /* we don't do iscan in ibss */ ssids = this_ssid; } cfg_priv->scan_request = request; set_bit(WL_STATUS_SCANNING, &cfg_priv->status); if (iscan_req) { err = brcmf_do_iscan(cfg_priv); if (!err) return err; else goto scan_out; } else { WL_SCAN("ssid \"%s\", ssid_len (%d)\n", ssids->ssid, ssids->ssid_len); memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); sr->ssid_le.SSID_len = cpu_to_le32(0); if (SSID_len) { memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); spec_scan = true; } else { WL_SCAN("Broadcast scan\n"); } passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN, &passive_scan, sizeof(passive_scan)); if (err) { WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err); goto scan_out; } brcmf_set_mpc(ndev, 0); err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le, sizeof(sr->ssid_le)); if (err) { if (err == -EBUSY) WL_INFO("system busy : scan for \"%s\" " "canceled\n", sr->ssid_le.SSID); else WL_ERR("WLC_SCAN error (%d)\n", err); brcmf_set_mpc(ndev, 1); goto scan_out; } } return 0; scan_out: clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); cfg_priv->scan_request = NULL; return err; } static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, struct cfg80211_scan_request *request) { u32 n_ssids; u32 n_channels; s32 i; s32 offset; __le16 chanspec; u16 channel; struct ieee80211_channel *req_channel; char *ptr; struct brcmf_ssid ssid; memcpy(params_le->bssid, ether_bcast, ETH_ALEN); params_le->bss_type = DOT11_BSSTYPE_ANY; params_le->scan_type = 0; params_le->channel_num = 0; params_le->nprobes = cpu_to_le32(-1); params_le->active_time = cpu_to_le32(-1); params_le->passive_time = cpu_to_le32(-1); params_le->home_time = cpu_to_le32(-1); memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); /* if request is null exit so it will be all channel broadcast scan */ if (!request) return; n_ssids = request->n_ssids; n_channels = request->n_channels; /* Copy channel array if applicable */ WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels); if (n_channels > 0) { for (i = 0; i < n_channels; i++) { chanspec = 0; req_channel = request->channels[i]; channel = ieee80211_frequency_to_channel( req_channel->center_freq); if (req_channel->band == IEEE80211_BAND_2GHZ) chanspec |= WL_CHANSPEC_BAND_2G; else chanspec |= WL_CHANSPEC_BAND_5G; if (req_channel->flags & IEEE80211_CHAN_NO_HT40) { chanspec |= WL_CHANSPEC_BW_20; chanspec |= WL_CHANSPEC_CTL_SB_NONE; } else { chanspec |= WL_CHANSPEC_BW_40; if (req_channel->flags & IEEE80211_CHAN_NO_HT40PLUS) chanspec |= WL_CHANSPEC_CTL_SB_LOWER; else chanspec |= WL_CHANSPEC_CTL_SB_UPPER; } params_le->channel_list[i] = (channel & WL_CHANSPEC_CHAN_MASK) | chanspec; WL_SCAN("Chan : %d, Channel spec: %x\n", channel, params_le->channel_list[i]); params_le->channel_list[i] = cpu_to_le16(params_le->channel_list[i]); } } else { WL_SCAN("Scanning all channels\n"); } /* Copy ssid array if applicable */ WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids); if (n_ssids > 0) { offset = offsetof(struct brcmf_scan_params_le, channel_list) + n_channels * sizeof(u16); offset = roundup(offset, sizeof(u32)); ptr = (char *)params_le + offset; for (i = 0; i < n_ssids; i++) { memset(&ssid, 0, sizeof(ssid)); ssid.SSID_len = cpu_to_le32(request->ssids[i].ssid_len); memcpy(ssid.SSID, request->ssids[i].ssid, request->ssids[i].ssid_len); if (!ssid.SSID_len) WL_SCAN("%d: Broadcast scan\n", i); else WL_SCAN("%d: scan for %s size =%d\n", i, ssid.SSID, ssid.SSID_len); memcpy(ptr, &ssid, sizeof(ssid)); ptr += sizeof(ssid); } } else { WL_SCAN("Broadcast scan %p\n", request->ssids); if ((request->ssids) && request->ssids->ssid_len) { WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID, request->ssids->ssid_len); params_le->ssid_le.SSID_len = cpu_to_le32(request->ssids->ssid_len); memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid, request->ssids->ssid_len); } } /* Adding mask to channel numbers */ params_le->channel_num = cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); } static s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, bool aborted, bool fw_abort) { struct brcmf_scan_params_le params_le; struct cfg80211_scan_request *scan_request; s32 err = 0; WL_SCAN("Enter\n"); /* clear scan request, because the FW abort can cause a second call */ /* to this functon and might cause a double cfg80211_scan_done */ scan_request = cfg_priv->scan_request; cfg_priv->scan_request = NULL; if (timer_pending(&cfg_priv->escan_timeout)) del_timer_sync(&cfg_priv->escan_timeout); if (fw_abort) { /* Do a scan abort to stop the driver's scan engine */ WL_SCAN("ABORT scan in firmware\n"); memset(¶ms_le, 0, sizeof(params_le)); memcpy(params_le.bssid, ether_bcast, ETH_ALEN); params_le.bss_type = DOT11_BSSTYPE_ANY; params_le.scan_type = 0; params_le.channel_num = cpu_to_le32(1); params_le.nprobes = cpu_to_le32(1); params_le.active_time = cpu_to_le32(-1); params_le.passive_time = cpu_to_le32(-1); params_le.home_time = cpu_to_le32(-1); /* Scan is aborted by setting channel_list[0] to -1 */ params_le.channel_list[0] = cpu_to_le16(-1); /* E-Scan (or anyother type) can be aborted by SCAN */ err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, ¶ms_le, sizeof(params_le)); if (err) WL_ERR("Scan abort failed\n"); } if (scan_request) { WL_SCAN("ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(scan_request, aborted); brcmf_set_mpc(ndev, 1); } if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { WL_ERR("Scan complete while device not scanning\n"); return -EPERM; } return err; } static s32 brcmf_run_escan(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, struct cfg80211_scan_request *request, u16 action) { s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + offsetof(struct brcmf_escan_params_le, params_le); struct brcmf_escan_params_le *params; s32 err = 0; WL_SCAN("E-SCAN START\n"); if (request != NULL) { /* Allocate space for populating ssids in struct */ params_size += sizeof(u32) * ((request->n_channels + 1) / 2); /* Allocate space for populating ssids in struct */ params_size += sizeof(struct brcmf_ssid) * request->n_ssids; } params = kzalloc(params_size, GFP_KERNEL); if (!params) { err = -ENOMEM; goto exit; } BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); brcmf_escan_prep(¶ms->params_le, request); params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); params->action = cpu_to_le16(action); params->sync_id = cpu_to_le16(0x1234); err = brcmf_dev_iovar_setbuf(ndev, "escan", params, params_size, cfg_priv->escan_ioctl_buf, BRCMF_DCMD_MEDLEN); if (err) { if (err == -EBUSY) WL_INFO("system busy : escan canceled\n"); else WL_ERR("error (%d)\n", err); } kfree(params); exit: return err; } static s32 brcmf_do_escan(struct brcmf_cfg80211_priv *cfg_priv, struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request) { s32 err; __le32 passive_scan; struct brcmf_scan_results *results; WL_SCAN("Enter\n"); cfg_priv->escan_info.ndev = ndev; cfg_priv->escan_info.wiphy = wiphy; cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_SCANNING; passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN, &passive_scan, sizeof(passive_scan)); if (err) { WL_ERR("error (%d)\n", err); return err; } brcmf_set_mpc(ndev, 0); results = (struct brcmf_scan_results *)cfg_priv->escan_info.escan_buf; results->version = 0; results->count = 0; results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; err = brcmf_run_escan(cfg_priv, ndev, request, WL_ESCAN_ACTION_START); if (err) brcmf_set_mpc(ndev, 1); return err; } static s32 brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request, struct cfg80211_ssid *this_ssid) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); struct cfg80211_ssid *ssids; struct brcmf_cfg80211_scan_req *sr = cfg_priv->scan_req_int; __le32 passive_scan; bool escan_req; bool spec_scan; s32 err; u32 SSID_len; WL_SCAN("START ESCAN\n"); if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status); return -EAGAIN; } if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status)) { WL_ERR("Scanning being aborted : status (%lu)\n", cfg_priv->status); return -EAGAIN; } if (test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) { WL_ERR("Connecting : status (%lu)\n", cfg_priv->status); return -EAGAIN; } /* Arm scan timeout timer */ mod_timer(&cfg_priv->escan_timeout, jiffies + WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); escan_req = false; if (request) { /* scan bss */ ssids = request->ssids; escan_req = true; } else { /* scan in ibss */ /* we don't do escan in ibss */ ssids = this_ssid; } cfg_priv->scan_request = request; set_bit(WL_STATUS_SCANNING, &cfg_priv->status); if (escan_req) { err = brcmf_do_escan(cfg_priv, wiphy, ndev, request); if (!err) return err; else goto scan_out; } else { WL_SCAN("ssid \"%s\", ssid_len (%d)\n", ssids->ssid, ssids->ssid_len); memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); sr->ssid_le.SSID_len = cpu_to_le32(0); spec_scan = false; if (SSID_len) { memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); spec_scan = true; } else WL_SCAN("Broadcast scan\n"); passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN, &passive_scan, sizeof(passive_scan)); if (err) { WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err); goto scan_out; } brcmf_set_mpc(ndev, 0); err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le, sizeof(sr->ssid_le)); if (err) { if (err == -EBUSY) WL_INFO("BUSY: scan for \"%s\" canceled\n", sr->ssid_le.SSID); else WL_ERR("WLC_SCAN error (%d)\n", err); brcmf_set_mpc(ndev, 1); goto scan_out; } } return 0; scan_out: clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); if (timer_pending(&cfg_priv->escan_timeout)) del_timer_sync(&cfg_priv->escan_timeout); cfg_priv->scan_request = NULL; return err; } static s32 brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct net_device *ndev = request->wdev->netdev; struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; if (cfg_priv->iscan_on) err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL); else if (cfg_priv->escan_on) err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL); if (err) WL_ERR("scan error (%d)\n", err); WL_TRACE("Exit\n"); return err; } static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold) { s32 err = 0; err = brcmf_dev_intvar_set(ndev, "rtsthresh", rts_threshold); if (err) WL_ERR("Error (%d)\n", err); return err; } static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold) { s32 err = 0; err = brcmf_dev_intvar_set(ndev, "fragthresh", frag_threshold); if (err) WL_ERR("Error (%d)\n", err); return err; } static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l) { s32 err = 0; u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL); err = brcmf_exec_dcmd_u32(ndev, cmd, &retry); if (err) { WL_ERR("cmd (%d) , error (%d)\n", cmd, err); return err; } return err; } static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg_priv); s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; if (changed & WIPHY_PARAM_RTS_THRESHOLD && (cfg_priv->conf->rts_threshold != wiphy->rts_threshold)) { cfg_priv->conf->rts_threshold = wiphy->rts_threshold; err = brcmf_set_rts(ndev, cfg_priv->conf->rts_threshold); if (!err) goto done; } if (changed & WIPHY_PARAM_FRAG_THRESHOLD && (cfg_priv->conf->frag_threshold != wiphy->frag_threshold)) { cfg_priv->conf->frag_threshold = wiphy->frag_threshold; err = brcmf_set_frag(ndev, cfg_priv->conf->frag_threshold); if (!err) goto done; } if (changed & WIPHY_PARAM_RETRY_LONG && (cfg_priv->conf->retry_long != wiphy->retry_long)) { cfg_priv->conf->retry_long = wiphy->retry_long; err = brcmf_set_retry(ndev, cfg_priv->conf->retry_long, true); if (!err) goto done; } if (changed & WIPHY_PARAM_RETRY_SHORT && (cfg_priv->conf->retry_short != wiphy->retry_short)) { cfg_priv->conf->retry_short = wiphy->retry_short; err = brcmf_set_retry(ndev, cfg_priv->conf->retry_short, false); if (!err) goto done; } done: WL_TRACE("Exit\n"); return err; } static void *brcmf_read_prof(struct brcmf_cfg80211_priv *cfg_priv, s32 item) { switch (item) { case WL_PROF_SEC: return &cfg_priv->profile->sec; case WL_PROF_BSSID: return &cfg_priv->profile->bssid; case WL_PROF_SSID: return &cfg_priv->profile->ssid; } WL_ERR("invalid item (%d)\n", item); return NULL; } static s32 brcmf_update_prof(struct brcmf_cfg80211_priv *cfg_priv, const struct brcmf_event_msg *e, void *data, s32 item) { s32 err = 0; struct brcmf_ssid *ssid; switch (item) { case WL_PROF_SSID: ssid = (struct brcmf_ssid *) data; memset(cfg_priv->profile->ssid.SSID, 0, sizeof(cfg_priv->profile->ssid.SSID)); memcpy(cfg_priv->profile->ssid.SSID, ssid->SSID, ssid->SSID_len); cfg_priv->profile->ssid.SSID_len = ssid->SSID_len; break; case WL_PROF_BSSID: if (data) memcpy(cfg_priv->profile->bssid, data, ETH_ALEN); else memset(cfg_priv->profile->bssid, 0, ETH_ALEN); break; case WL_PROF_SEC: memcpy(&cfg_priv->profile->sec, data, sizeof(cfg_priv->profile->sec)); break; case WL_PROF_BEACONINT: cfg_priv->profile->beacon_interval = *(u16 *)data; break; case WL_PROF_DTIMPERIOD: cfg_priv->profile->dtim_period = *(u8 *)data; break; default: WL_ERR("unsupported item (%d)\n", item); err = -EOPNOTSUPP; break; } return err; } static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) { memset(prof, 0, sizeof(*prof)); } static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params, size_t *join_params_size) { u16 chanspec = 0; if (ch != 0) { if (ch <= CH_MAX_2G_CHANNEL) chanspec |= WL_CHANSPEC_BAND_2G; else chanspec |= WL_CHANSPEC_BAND_5G; chanspec |= WL_CHANSPEC_BW_20; chanspec |= WL_CHANSPEC_CTL_SB_NONE; *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE + sizeof(u16); chanspec |= (ch & WL_CHANSPEC_CHAN_MASK); join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec); join_params->params_le.chanspec_num = cpu_to_le32(1); WL_CONN("join_params->params.chanspec_list[0]= %#X," "channel %d, chanspec %#X\n", chanspec, ch, chanspec); } } static void brcmf_link_down(struct brcmf_cfg80211_priv *cfg_priv) { struct net_device *ndev = NULL; s32 err = 0; WL_TRACE("Enter\n"); if (cfg_priv->link_up) { ndev = cfg_to_ndev(cfg_priv); WL_INFO("Call WLC_DISASSOC to stop excess roaming\n "); err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, NULL, 0); if (err) WL_ERR("WLC_DISASSOC failed (%d)\n", err); cfg_priv->link_up = false; } WL_TRACE("Exit\n"); } static s32 brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_ibss_params *params) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct brcmf_join_params join_params; size_t join_params_size = 0; s32 err = 0; s32 wsec = 0; s32 bcnprd; struct brcmf_ssid ssid; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; if (params->ssid) WL_CONN("SSID: %s\n", params->ssid); else { WL_CONN("SSID: NULL, Not supported\n"); return -EOPNOTSUPP; } set_bit(WL_STATUS_CONNECTING, &cfg_priv->status); if (params->bssid) WL_CONN("BSSID: %pM\n", params->bssid); else WL_CONN("No BSSID specified\n"); if (params->channel) WL_CONN("channel: %d\n", params->channel->center_freq); else WL_CONN("no channel specified\n"); if (params->channel_fixed) WL_CONN("fixed channel required\n"); else WL_CONN("no fixed channel required\n"); if (params->ie && params->ie_len) WL_CONN("ie len: %d\n", params->ie_len); else WL_CONN("no ie specified\n"); if (params->beacon_interval) WL_CONN("beacon interval: %d\n", params->beacon_interval); else WL_CONN("no beacon interval specified\n"); if (params->basic_rates) WL_CONN("basic rates: %08X\n", params->basic_rates); else WL_CONN("no basic rates specified\n"); if (params->privacy) WL_CONN("privacy required\n"); else WL_CONN("no privacy required\n"); /* Configure Privacy for starter */ if (params->privacy) wsec |= WEP_ENABLED; err = brcmf_dev_intvar_set(ndev, "wsec", wsec); if (err) { WL_ERR("wsec failed (%d)\n", err); goto done; } /* Configure Beacon Interval for starter */ if (params->beacon_interval) bcnprd = params->beacon_interval; else bcnprd = 100; err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_BCNPRD, &bcnprd); if (err) { WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err); goto done; } /* Configure required join parameter */ memset(&join_params, 0, sizeof(struct brcmf_join_params)); /* SSID */ ssid.SSID_len = min_t(u32, params->ssid_len, 32); memcpy(ssid.SSID, params->ssid, ssid.SSID_len); memcpy(join_params.ssid_le.SSID, params->ssid, ssid.SSID_len); join_params.ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len); join_params_size = sizeof(join_params.ssid_le); brcmf_update_prof(cfg_priv, NULL, &ssid, WL_PROF_SSID); /* BSSID */ if (params->bssid) { memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN); join_params_size = sizeof(join_params.ssid_le) + BRCMF_ASSOC_PARAMS_FIXED_SIZE; } else { memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN); } brcmf_update_prof(cfg_priv, NULL, &join_params.params_le.bssid, WL_PROF_BSSID); /* Channel */ if (params->channel) { u32 target_channel; cfg_priv->channel = ieee80211_frequency_to_channel( params->channel->center_freq); if (params->channel_fixed) { /* adding chanspec */ brcmf_ch_to_chanspec(cfg_priv->channel, &join_params, &join_params_size); } /* set channel for starter */ target_channel = cfg_priv->channel; err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_CHANNEL, &target_channel); if (err) { WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err); goto done; } } else cfg_priv->channel = 0; cfg_priv->ibss_starter = false; err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID, &join_params, join_params_size); if (err) { WL_ERR("WLC_SET_SSID failed (%d)\n", err); goto done; } done: if (err) clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status); WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; brcmf_link_down(cfg_priv); WL_TRACE("Exit\n"); return err; } static s32 brcmf_set_wpa_version(struct net_device *ndev, struct cfg80211_connect_params *sme) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); struct brcmf_cfg80211_security *sec; s32 val = 0; s32 err = 0; if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; else val = WPA_AUTH_DISABLED; WL_CONN("setting wpa_auth to 0x%0x\n", val); err = brcmf_dev_intvar_set(ndev, "wpa_auth", val); if (err) { WL_ERR("set wpa_auth failed (%d)\n", err); return err; } sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC); sec->wpa_versions = sme->crypto.wpa_versions; return err; } static s32 brcmf_set_auth_type(struct net_device *ndev, struct cfg80211_connect_params *sme) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); struct brcmf_cfg80211_security *sec; s32 val = 0; s32 err = 0; switch (sme->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: val = 0; WL_CONN("open system\n"); break; case NL80211_AUTHTYPE_SHARED_KEY: val = 1; WL_CONN("shared key\n"); break; case NL80211_AUTHTYPE_AUTOMATIC: val = 2; WL_CONN("automatic\n"); break; case NL80211_AUTHTYPE_NETWORK_EAP: WL_CONN("network eap\n"); default: val = 2; WL_ERR("invalid auth type (%d)\n", sme->auth_type); break; } err = brcmf_dev_intvar_set(ndev, "auth", val); if (err) { WL_ERR("set auth failed (%d)\n", err); return err; } sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC); sec->auth_type = sme->auth_type; return err; } static s32 brcmf_set_set_cipher(struct net_device *ndev, struct cfg80211_connect_params *sme) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); struct brcmf_cfg80211_security *sec; s32 pval = 0; s32 gval = 0; s32 err = 0; if (sme->crypto.n_ciphers_pairwise) { switch (sme->crypto.ciphers_pairwise[0]) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: pval = WEP_ENABLED; break; case WLAN_CIPHER_SUITE_TKIP: pval = TKIP_ENABLED; break; case WLAN_CIPHER_SUITE_CCMP: pval = AES_ENABLED; break; case WLAN_CIPHER_SUITE_AES_CMAC: pval = AES_ENABLED; break; default: WL_ERR("invalid cipher pairwise (%d)\n", sme->crypto.ciphers_pairwise[0]); return -EINVAL; } } if (sme->crypto.cipher_group) { switch (sme->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: gval = WEP_ENABLED; break; case WLAN_CIPHER_SUITE_TKIP: gval = TKIP_ENABLED; break; case WLAN_CIPHER_SUITE_CCMP: gval = AES_ENABLED; break; case WLAN_CIPHER_SUITE_AES_CMAC: gval = AES_ENABLED; break; default: WL_ERR("invalid cipher group (%d)\n", sme->crypto.cipher_group); return -EINVAL; } } WL_CONN("pval (%d) gval (%d)\n", pval, gval); err = brcmf_dev_intvar_set(ndev, "wsec", pval | gval); if (err) { WL_ERR("error (%d)\n", err); return err; } sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC); sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; sec->cipher_group = sme->crypto.cipher_group; return err; } static s32 brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); struct brcmf_cfg80211_security *sec; s32 val = 0; s32 err = 0; if (sme->crypto.n_akm_suites) { err = brcmf_dev_intvar_get(ndev, "wpa_auth", &val); if (err) { WL_ERR("could not get wpa_auth (%d)\n", err); return err; } if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA_AUTH_UNSPECIFIED; break; case WLAN_AKM_SUITE_PSK: val = WPA_AUTH_PSK; break; default: WL_ERR("invalid cipher group (%d)\n", sme->crypto.cipher_group); return -EINVAL; } } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA2_AUTH_UNSPECIFIED; break; case WLAN_AKM_SUITE_PSK: val = WPA2_AUTH_PSK; break; default: WL_ERR("invalid cipher group (%d)\n", sme->crypto.cipher_group); return -EINVAL; } } WL_CONN("setting wpa_auth to %d\n", val); err = brcmf_dev_intvar_set(ndev, "wpa_auth", val); if (err) { WL_ERR("could not set wpa_auth (%d)\n", err); return err; } } sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC); sec->wpa_auth = sme->crypto.akm_suites[0]; return err; } static s32 brcmf_set_wep_sharedkey(struct net_device *ndev, struct cfg80211_connect_params *sme) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); struct brcmf_cfg80211_security *sec; struct brcmf_wsec_key key; s32 val; s32 err = 0; WL_CONN("key len (%d)\n", sme->key_len); if (sme->key_len == 0) return 0; sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC); WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n", sec->wpa_versions, sec->cipher_pairwise); if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) return 0; if (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)) { memset(&key, 0, sizeof(key)); key.len = (u32) sme->key_len; key.index = (u32) sme->key_idx; if (key.len > sizeof(key.data)) { WL_ERR("Too long key length (%u)\n", key.len); return -EINVAL; } memcpy(key.data, sme->key, key.len); key.flags = BRCMF_PRIMARY_KEY; switch (sec->cipher_pairwise) { case WLAN_CIPHER_SUITE_WEP40: key.algo = CRYPTO_ALGO_WEP1; break; case WLAN_CIPHER_SUITE_WEP104: key.algo = CRYPTO_ALGO_WEP128; break; default: WL_ERR("Invalid algorithm (%d)\n", sme->crypto.ciphers_pairwise[0]); return -EINVAL; } /* Set the new key/index */ WL_CONN("key length (%d) key index (%d) algo (%d)\n", key.len, key.index, key.algo); WL_CONN("key \"%s\"\n", key.data); err = send_key_to_dongle(ndev, &key); if (err) return err; if (sec->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) { WL_CONN("set auth_type to shared key\n"); val = 1; /* shared key */ err = brcmf_dev_intvar_set(ndev, "auth", val); if (err) { WL_ERR("set auth failed (%d)\n", err); return err; } } } return err; } static s32 brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_connect_params *sme) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct ieee80211_channel *chan = sme->channel; struct brcmf_join_params join_params; size_t join_params_size; struct brcmf_ssid ssid; s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; if (!sme->ssid) { WL_ERR("Invalid ssid\n"); return -EOPNOTSUPP; } set_bit(WL_STATUS_CONNECTING, &cfg_priv->status); if (chan) { cfg_priv->channel = ieee80211_frequency_to_channel(chan->center_freq); WL_CONN("channel (%d), center_req (%d)\n", cfg_priv->channel, chan->center_freq); } else cfg_priv->channel = 0; WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len); err = brcmf_set_wpa_version(ndev, sme); if (err) { WL_ERR("wl_set_wpa_version failed (%d)\n", err); goto done; } err = brcmf_set_auth_type(ndev, sme); if (err) { WL_ERR("wl_set_auth_type failed (%d)\n", err); goto done; } err = brcmf_set_set_cipher(ndev, sme); if (err) { WL_ERR("wl_set_set_cipher failed (%d)\n", err); goto done; } err = brcmf_set_key_mgmt(ndev, sme); if (err) { WL_ERR("wl_set_key_mgmt failed (%d)\n", err); goto done; } err = brcmf_set_wep_sharedkey(ndev, sme); if (err) { WL_ERR("brcmf_set_wep_sharedkey failed (%d)\n", err); goto done; } memset(&join_params, 0, sizeof(join_params)); join_params_size = sizeof(join_params.ssid_le); ssid.SSID_len = min_t(u32, sizeof(ssid.SSID), (u32)sme->ssid_len); memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid.SSID_len); memcpy(&ssid.SSID, sme->ssid, ssid.SSID_len); join_params.ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len); brcmf_update_prof(cfg_priv, NULL, &ssid, WL_PROF_SSID); memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN); if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN) WL_CONN("ssid \"%s\", len (%d)\n", ssid.SSID, ssid.SSID_len); brcmf_ch_to_chanspec(cfg_priv->channel, &join_params, &join_params_size); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID, &join_params, join_params_size); if (err) WL_ERR("WLC_SET_SSID failed (%d)\n", err); done: if (err) clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status); WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, u16 reason_code) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct brcmf_scb_val_le scbval; s32 err = 0; WL_TRACE("Enter. Reason code = %d\n", reason_code); if (!check_sys_up(wiphy)) return -EIO; clear_bit(WL_STATUS_CONNECTED, &cfg_priv->status); memcpy(&scbval.ea, brcmf_read_prof(cfg_priv, WL_PROF_BSSID), ETH_ALEN); scbval.val = cpu_to_le32(reason_code); err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, &scbval, sizeof(struct brcmf_scb_val_le)); if (err) WL_ERR("error (%d)\n", err); cfg_priv->link_up = false; WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, s32 mbm) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg_priv); u16 txpwrmw; s32 err = 0; s32 disable = 0; s32 dbm = MBM_TO_DBM(mbm); WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; switch (type) { case NL80211_TX_POWER_AUTOMATIC: break; case NL80211_TX_POWER_LIMITED: case NL80211_TX_POWER_FIXED: if (dbm < 0) { WL_ERR("TX_POWER_FIXED - dbm is negative\n"); err = -EINVAL; goto done; } break; } /* Make sure radio is off or on as far as software is concerned */ disable = WL_RADIO_SW_DISABLE << 16; err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_RADIO, &disable); if (err) WL_ERR("WLC_SET_RADIO error (%d)\n", err); if (dbm > 0xffff) txpwrmw = 0xffff; else txpwrmw = (u16) dbm; err = brcmf_dev_intvar_set(ndev, "qtxpower", (s32) (brcmf_mw_to_qdbm(txpwrmw))); if (err) WL_ERR("qtxpower error (%d)\n", err); cfg_priv->conf->tx_power = dbm; done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg_priv); s32 txpwrdbm; u8 result; s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; err = brcmf_dev_intvar_get(ndev, "qtxpower", &txpwrdbm); if (err) { WL_ERR("error (%d)\n", err); goto done; } result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); *dbm = (s32) brcmf_qdbm_to_mw(result); done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx, bool unicast, bool multicast) { u32 index; u32 wsec; s32 err = 0; WL_TRACE("Enter\n"); WL_CONN("key index (%d)\n", key_idx); if (!check_sys_up(wiphy)) return -EIO; err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_WSEC, &wsec); if (err) { WL_ERR("WLC_GET_WSEC error (%d)\n", err); goto done; } if (wsec & WEP_ENABLED) { /* Just select a new current key */ index = key_idx; err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_KEY_PRIMARY, &index); if (err) WL_ERR("error (%d)\n", err); } done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx, const u8 *mac_addr, struct key_params *params) { struct brcmf_wsec_key key; struct brcmf_wsec_key_le key_le; s32 err = 0; memset(&key, 0, sizeof(key)); key.index = (u32) key_idx; /* Instead of bcast for ea address for default wep keys, driver needs it to be Null */ if (!is_multicast_ether_addr(mac_addr)) memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN); key.len = (u32) params->key_len; /* check for key index change */ if (key.len == 0) { /* key delete */ err = send_key_to_dongle(ndev, &key); if (err) return err; } else { if (key.len > sizeof(key.data)) { WL_ERR("Invalid key length (%d)\n", key.len); return -EINVAL; } WL_CONN("Setting the key index %d\n", key.index); memcpy(key.data, params->key, key.len); if (params->cipher == WLAN_CIPHER_SUITE_TKIP) { u8 keybuf[8]; memcpy(keybuf, &key.data[24], sizeof(keybuf)); memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); memcpy(&key.data[16], keybuf, sizeof(keybuf)); } /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ if (params->seq && params->seq_len == 6) { /* rx iv */ u8 *ivptr; ivptr = (u8 *) params->seq; key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | (ivptr[3] << 8) | ivptr[2]; key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; key.iv_initialized = true; } switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: key.algo = CRYPTO_ALGO_WEP1; WL_CONN("WLAN_CIPHER_SUITE_WEP40\n"); break; case WLAN_CIPHER_SUITE_WEP104: key.algo = CRYPTO_ALGO_WEP128; WL_CONN("WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: key.algo = CRYPTO_ALGO_TKIP; WL_CONN("WLAN_CIPHER_SUITE_TKIP\n"); break; case WLAN_CIPHER_SUITE_AES_CMAC: key.algo = CRYPTO_ALGO_AES_CCM; WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n"); break; case WLAN_CIPHER_SUITE_CCMP: key.algo = CRYPTO_ALGO_AES_CCM; WL_CONN("WLAN_CIPHER_SUITE_CCMP\n"); break; default: WL_ERR("Invalid cipher (0x%x)\n", params->cipher); return -EINVAL; } convert_key_from_CPU(&key, &key_le); brcmf_netdev_wait_pend8021x(ndev); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_KEY, &key_le, sizeof(key_le)); if (err) { WL_ERR("WLC_SET_KEY error (%d)\n", err); return err; } } return err; } static s32 brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct brcmf_wsec_key key; s32 val; s32 wsec; s32 err = 0; u8 keybuf[8]; WL_TRACE("Enter\n"); WL_CONN("key index (%d)\n", key_idx); if (!check_sys_up(wiphy)) return -EIO; if (mac_addr) { WL_TRACE("Exit"); return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); } memset(&key, 0, sizeof(key)); key.len = (u32) params->key_len; key.index = (u32) key_idx; if (key.len > sizeof(key.data)) { WL_ERR("Too long key length (%u)\n", key.len); err = -EINVAL; goto done; } memcpy(key.data, params->key, key.len); key.flags = BRCMF_PRIMARY_KEY; switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: key.algo = CRYPTO_ALGO_WEP1; WL_CONN("WLAN_CIPHER_SUITE_WEP40\n"); break; case WLAN_CIPHER_SUITE_WEP104: key.algo = CRYPTO_ALGO_WEP128; WL_CONN("WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: memcpy(keybuf, &key.data[24], sizeof(keybuf)); memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); memcpy(&key.data[16], keybuf, sizeof(keybuf)); key.algo = CRYPTO_ALGO_TKIP; WL_CONN("WLAN_CIPHER_SUITE_TKIP\n"); break; case WLAN_CIPHER_SUITE_AES_CMAC: key.algo = CRYPTO_ALGO_AES_CCM; WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n"); break; case WLAN_CIPHER_SUITE_CCMP: key.algo = CRYPTO_ALGO_AES_CCM; WL_CONN("WLAN_CIPHER_SUITE_CCMP\n"); break; default: WL_ERR("Invalid cipher (0x%x)\n", params->cipher); err = -EINVAL; goto done; } err = send_key_to_dongle(ndev, &key); /* Set the new key/index */ if (err) goto done; val = WEP_ENABLED; err = brcmf_dev_intvar_get(ndev, "wsec", &wsec); if (err) { WL_ERR("get wsec error (%d)\n", err); goto done; } wsec &= ~(WEP_ENABLED); wsec |= val; err = brcmf_dev_intvar_set(ndev, "wsec", wsec); if (err) { WL_ERR("set wsec error (%d)\n", err); goto done; } val = 1; /* assume shared key. otherwise 0 */ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AUTH, &val); if (err) WL_ERR("WLC_SET_AUTH error (%d)\n", err); done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx, bool pairwise, const u8 *mac_addr) { struct brcmf_wsec_key key; s32 err = 0; s32 val; s32 wsec; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; memset(&key, 0, sizeof(key)); key.index = (u32) key_idx; key.flags = BRCMF_PRIMARY_KEY; key.algo = CRYPTO_ALGO_OFF; WL_CONN("key index (%d)\n", key_idx); /* Set the new key/index */ err = send_key_to_dongle(ndev, &key); if (err) { if (err == -EINVAL) { if (key.index >= DOT11_MAX_DEFAULT_KEYS) /* we ignore this key index in this case */ WL_ERR("invalid key index (%d)\n", key_idx); } /* Ignore this error, may happen during DISASSOC */ err = -EAGAIN; goto done; } val = 0; err = brcmf_dev_intvar_get(ndev, "wsec", &wsec); if (err) { WL_ERR("get wsec error (%d)\n", err); /* Ignore this error, may happen during DISASSOC */ err = -EAGAIN; goto done; } wsec &= ~(WEP_ENABLED); wsec |= val; err = brcmf_dev_intvar_set(ndev, "wsec", wsec); if (err) { WL_ERR("set wsec error (%d)\n", err); /* Ignore this error, may happen during DISASSOC */ err = -EAGAIN; goto done; } val = 0; /* assume open key. otherwise 1 */ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AUTH, &val); if (err) { WL_ERR("WLC_SET_AUTH error (%d)\n", err); /* Ignore this error, may happen during DISASSOC */ err = -EAGAIN; } done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback) (void *cookie, struct key_params * params)) { struct key_params params; struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct brcmf_cfg80211_security *sec; s32 wsec; s32 err = 0; WL_TRACE("Enter\n"); WL_CONN("key index (%d)\n", key_idx); if (!check_sys_up(wiphy)) return -EIO; memset(¶ms, 0, sizeof(params)); err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_WSEC, &wsec); if (err) { WL_ERR("WLC_GET_WSEC error (%d)\n", err); /* Ignore this error, may happen during DISASSOC */ err = -EAGAIN; goto done; } switch (wsec) { case WEP_ENABLED: sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC); if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { params.cipher = WLAN_CIPHER_SUITE_WEP40; WL_CONN("WLAN_CIPHER_SUITE_WEP40\n"); } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { params.cipher = WLAN_CIPHER_SUITE_WEP104; WL_CONN("WLAN_CIPHER_SUITE_WEP104\n"); } break; case TKIP_ENABLED: params.cipher = WLAN_CIPHER_SUITE_TKIP; WL_CONN("WLAN_CIPHER_SUITE_TKIP\n"); break; case AES_ENABLED: params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n"); break; default: WL_ERR("Invalid algo (0x%x)\n", wsec); err = -EINVAL; goto done; } callback(cookie, ¶ms); done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx) { WL_INFO("Not supported\n"); return -EOPNOTSUPP; } static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, u8 *mac, struct station_info *sinfo) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct brcmf_scb_val_le scb_val; int rssi; s32 rate; s32 err = 0; u8 *bssid = brcmf_read_prof(cfg_priv, WL_PROF_BSSID); WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; if (memcmp(mac, bssid, ETH_ALEN)) { WL_ERR("Wrong Mac address cfg_mac-%X:%X:%X:%X:%X:%X" "wl_bssid-%X:%X:%X:%X:%X:%X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); err = -ENOENT; goto done; } /* Report the current tx rate */ err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_RATE, &rate); if (err) { WL_ERR("Could not get rate (%d)\n", err); } else { sinfo->filled |= STATION_INFO_TX_BITRATE; sinfo->txrate.legacy = rate * 5; WL_CONN("Rate %d Mbps\n", rate / 2); } if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) { memset(&scb_val, 0, sizeof(scb_val)); err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val, sizeof(struct brcmf_scb_val_le)); if (err) { WL_ERR("Could not get rssi (%d)\n", err); } else { rssi = le32_to_cpu(scb_val.val); sinfo->filled |= STATION_INFO_SIGNAL; sinfo->signal = rssi; WL_CONN("RSSI %d dBm\n", rssi); } } done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, bool enabled, s32 timeout) { s32 pm; s32 err = 0; struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); WL_TRACE("Enter\n"); /* * Powersave enable/disable request is coming from the * cfg80211 even before the interface is up. In that * scenario, driver will be storing the power save * preference in cfg_priv struct to apply this to * FW later while initializing the dongle */ cfg_priv->pwr_save = enabled; if (!test_bit(WL_STATUS_READY, &cfg_priv->status)) { WL_INFO("Device is not ready," "storing the value in cfg_priv struct\n"); goto done; } pm = enabled ? PM_FAST : PM_OFF; WL_INFO("power save %s\n", (pm ? "enabled" : "disabled")); err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &pm); if (err) { if (err == -ENODEV) WL_ERR("net_device is not ready yet\n"); else WL_ERR("error (%d)\n", err); } done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev, const u8 *addr, const struct cfg80211_bitrate_mask *mask) { struct brcm_rateset_le rateset_le; s32 rate; s32 val; s32 err_bg; s32 err_a; u32 legacy; s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; /* addr param is always NULL. ignore it */ /* Get current rateset */ err = brcmf_exec_dcmd(ndev, BRCM_GET_CURR_RATESET, &rateset_le, sizeof(rateset_le)); if (err) { WL_ERR("could not get current rateset (%d)\n", err); goto done; } legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF); if (!legacy) legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy & 0xFFFF); val = wl_g_rates[legacy - 1].bitrate * 100000; if (val < le32_to_cpu(rateset_le.count)) /* Select rate by rateset index */ rate = rateset_le.rates[val] & 0x7f; else /* Specified rate in bps */ rate = val / 500000; WL_CONN("rate %d mbps\n", rate / 2); /* * * Set rate override, * Since the is a/b/g-blind, both a/bg_rate are enforced. */ err_bg = brcmf_dev_intvar_set(ndev, "bg_rate", rate); err_a = brcmf_dev_intvar_set(ndev, "a_rate", rate); if (err_bg && err_a) { WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a); err = err_bg | err_a; } done: WL_TRACE("Exit\n"); return err; } static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_priv *cfg_priv, struct brcmf_bss_info_le *bi) { struct wiphy *wiphy = cfg_to_wiphy(cfg_priv); struct ieee80211_channel *notify_channel; struct cfg80211_bss *bss; struct ieee80211_supported_band *band; s32 err = 0; u16 channel; u32 freq; u16 notify_capability; u16 notify_interval; u8 *notify_ie; size_t notify_ielen; s32 notify_signal; if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { WL_ERR("Bss info is larger than buffer. Discarding\n"); return 0; } channel = bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec)); if (channel <= CH_MAX_2G_CHANNEL) band = wiphy->bands[IEEE80211_BAND_2GHZ]; else band = wiphy->bands[IEEE80211_BAND_5GHZ]; freq = ieee80211_channel_to_frequency(channel, band->band); notify_channel = ieee80211_get_channel(wiphy, freq); notify_capability = le16_to_cpu(bi->capability); notify_interval = le16_to_cpu(bi->beacon_period); notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); notify_ielen = le32_to_cpu(bi->ie_length); notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", bi->BSSID[0], bi->BSSID[1], bi->BSSID[2], bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]); WL_CONN("Channel: %d(%d)\n", channel, freq); WL_CONN("Capability: %X\n", notify_capability); WL_CONN("Beacon interval: %d\n", notify_interval); WL_CONN("Signal: %d\n", notify_signal); bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID, 0, notify_capability, notify_interval, notify_ie, notify_ielen, notify_signal, GFP_KERNEL); if (!bss) return -ENOMEM; cfg80211_put_bss(bss); return err; } static struct brcmf_bss_info_le * next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss) { if (bss == NULL) return list->bss_info_le; return (struct brcmf_bss_info_le *)((unsigned long)bss + le32_to_cpu(bss->length)); } static s32 brcmf_inform_bss(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_scan_results *bss_list; struct brcmf_bss_info_le *bi = NULL; /* must be initialized */ s32 err = 0; int i; bss_list = cfg_priv->bss_list; if (bss_list->version != BRCMF_BSS_INFO_VERSION) { WL_ERR("Version %d != WL_BSS_INFO_VERSION\n", bss_list->version); return -EOPNOTSUPP; } WL_SCAN("scanned AP count (%d)\n", bss_list->count); for (i = 0; i < bss_list->count && i < WL_AP_MAX; i++) { bi = next_bss_le(bss_list, bi); err = brcmf_inform_single_bss(cfg_priv, bi); if (err) break; } return err; } static s32 wl_inform_ibss(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const u8 *bssid) { struct wiphy *wiphy = cfg_to_wiphy(cfg_priv); struct ieee80211_channel *notify_channel; struct brcmf_bss_info_le *bi = NULL; struct ieee80211_supported_band *band; struct cfg80211_bss *bss; u8 *buf = NULL; s32 err = 0; u16 channel; u32 freq; u16 notify_capability; u16 notify_interval; u8 *notify_ie; size_t notify_ielen; s32 notify_signal; WL_TRACE("Enter\n"); buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); if (buf == NULL) { err = -ENOMEM; goto CleanUp; } *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX); if (err) { WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err); goto CleanUp; } bi = (struct brcmf_bss_info_le *)(buf + 4); channel = bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec)); if (channel <= CH_MAX_2G_CHANNEL) band = wiphy->bands[IEEE80211_BAND_2GHZ]; else band = wiphy->bands[IEEE80211_BAND_5GHZ]; freq = ieee80211_channel_to_frequency(channel, band->band); notify_channel = ieee80211_get_channel(wiphy, freq); notify_capability = le16_to_cpu(bi->capability); notify_interval = le16_to_cpu(bi->beacon_period); notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); notify_ielen = le32_to_cpu(bi->ie_length); notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; WL_CONN("channel: %d(%d)\n", channel, freq); WL_CONN("capability: %X\n", notify_capability); WL_CONN("beacon interval: %d\n", notify_interval); WL_CONN("signal: %d\n", notify_signal); bss = cfg80211_inform_bss(wiphy, notify_channel, bssid, 0, notify_capability, notify_interval, notify_ie, notify_ielen, notify_signal, GFP_KERNEL); if (!bss) { err = -ENOMEM; goto CleanUp; } cfg80211_put_bss(bss); CleanUp: kfree(buf); WL_TRACE("Exit\n"); return err; } static bool brcmf_is_ibssmode(struct brcmf_cfg80211_priv *cfg_priv) { return cfg_priv->conf->mode == WL_MODE_IBSS; } /* * Traverse a string of 1-byte tag/1-byte length/variable-length value * triples, returning a pointer to the substring whose first element * matches tag */ static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) { struct brcmf_tlv *elt; int totlen; elt = (struct brcmf_tlv *) buf; totlen = buflen; /* find tagged parameter */ while (totlen >= 2) { int len = elt->len; /* validate remaining totlen */ if ((elt->id == key) && (totlen >= (len + 2))) return elt; elt = (struct brcmf_tlv *) ((u8 *) elt + (len + 2)); totlen -= (len + 2); } return NULL; } static s32 brcmf_update_bss_info(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_bss_info_le *bi; struct brcmf_ssid *ssid; struct brcmf_tlv *tim; u16 beacon_interval; u8 dtim_period; size_t ie_len; u8 *ie; s32 err = 0; WL_TRACE("Enter\n"); if (brcmf_is_ibssmode(cfg_priv)) return err; ssid = (struct brcmf_ssid *)brcmf_read_prof(cfg_priv, WL_PROF_SSID); *(__le32 *)cfg_priv->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); err = brcmf_exec_dcmd(cfg_to_ndev(cfg_priv), BRCMF_C_GET_BSS_INFO, cfg_priv->extra_buf, WL_EXTRA_BUF_MAX); if (err) { WL_ERR("Could not get bss info %d\n", err); goto update_bss_info_out; } bi = (struct brcmf_bss_info_le *)(cfg_priv->extra_buf + 4); err = brcmf_inform_single_bss(cfg_priv, bi); if (err) goto update_bss_info_out; ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); ie_len = le32_to_cpu(bi->ie_length); beacon_interval = le16_to_cpu(bi->beacon_period); tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); if (tim) dtim_period = tim->data[1]; else { /* * active scan was done so we could not get dtim * information out of probe response. * so we speficially query dtim information to dongle. */ u32 var; err = brcmf_dev_intvar_get(cfg_to_ndev(cfg_priv), "dtim_assoc", &var); if (err) { WL_ERR("wl dtim_assoc failed (%d)\n", err); goto update_bss_info_out; } dtim_period = (u8)var; } brcmf_update_prof(cfg_priv, NULL, &beacon_interval, WL_PROF_BEACONINT); brcmf_update_prof(cfg_priv, NULL, &dtim_period, WL_PROF_DTIMPERIOD); update_bss_info_out: WL_TRACE("Exit"); return err; } static void brcmf_term_iscan(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv); struct brcmf_ssid ssid; if (cfg_priv->iscan_on) { iscan->state = WL_ISCAN_STATE_IDLE; if (iscan->timer_on) { del_timer_sync(&iscan->timer); iscan->timer_on = 0; } cancel_work_sync(&iscan->work); /* Abort iscan running in FW */ memset(&ssid, 0, sizeof(ssid)); brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT); } } static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan, bool aborted) { struct brcmf_cfg80211_priv *cfg_priv = iscan_to_cfg(iscan); struct net_device *ndev = cfg_to_ndev(cfg_priv); if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { WL_ERR("Scan complete while device not scanning\n"); return; } if (cfg_priv->scan_request) { WL_SCAN("ISCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(cfg_priv->scan_request, aborted); brcmf_set_mpc(ndev, 1); cfg_priv->scan_request = NULL; } cfg_priv->iscan_kickstart = false; } static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan) { if (iscan->state != WL_ISCAN_STATE_IDLE) { WL_SCAN("wake up iscan\n"); schedule_work(&iscan->work); return 0; } return -EIO; } static s32 brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status, struct brcmf_scan_results **bss_list) { struct brcmf_iscan_results list; struct brcmf_scan_results *results; struct brcmf_scan_results_le *results_le; struct brcmf_iscan_results *list_buf; s32 err = 0; memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX); list_buf = (struct brcmf_iscan_results *)iscan->scan_buf; results = &list_buf->results; results_le = &list_buf->results_le; results->buflen = BRCMF_ISCAN_RESULTS_FIXED_SIZE; results->version = 0; results->count = 0; memset(&list, 0, sizeof(list)); list.results_le.buflen = cpu_to_le32(WL_ISCAN_BUF_MAX); err = brcmf_dev_iovar_getbuf(iscan->ndev, "iscanresults", &list, BRCMF_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf, WL_ISCAN_BUF_MAX); if (err) { WL_ERR("error (%d)\n", err); return err; } results->buflen = le32_to_cpu(results_le->buflen); results->version = le32_to_cpu(results_le->version); results->count = le32_to_cpu(results_le->count); WL_SCAN("results->count = %d\n", results_le->count); WL_SCAN("results->buflen = %d\n", results_le->buflen); *status = le32_to_cpu(list_buf->status_le); WL_SCAN("status = %d\n", *status); *bss_list = results; return err; } static s32 brcmf_iscan_done(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan; s32 err = 0; iscan->state = WL_ISCAN_STATE_IDLE; brcmf_inform_bss(cfg_priv); brcmf_notify_iscan_complete(iscan, false); return err; } static s32 brcmf_iscan_pending(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan; s32 err = 0; /* Reschedule the timer */ mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); iscan->timer_on = 1; return err; } static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan; s32 err = 0; brcmf_inform_bss(cfg_priv); brcmf_run_iscan(iscan, NULL, BRCMF_SCAN_ACTION_CONTINUE); /* Reschedule the timer */ mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); iscan->timer_on = 1; return err; } static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan; s32 err = 0; iscan->state = WL_ISCAN_STATE_IDLE; brcmf_notify_iscan_complete(iscan, true); return err; } static void brcmf_cfg80211_iscan_handler(struct work_struct *work) { struct brcmf_cfg80211_iscan_ctrl *iscan = container_of(work, struct brcmf_cfg80211_iscan_ctrl, work); struct brcmf_cfg80211_priv *cfg_priv = iscan_to_cfg(iscan); struct brcmf_cfg80211_iscan_eloop *el = &iscan->el; u32 status = BRCMF_SCAN_RESULTS_PARTIAL; if (iscan->timer_on) { del_timer_sync(&iscan->timer); iscan->timer_on = 0; } if (brcmf_get_iscan_results(iscan, &status, &cfg_priv->bss_list)) { status = BRCMF_SCAN_RESULTS_ABORTED; WL_ERR("Abort iscan\n"); } el->handler[status](cfg_priv); } static void brcmf_iscan_timer(unsigned long data) { struct brcmf_cfg80211_iscan_ctrl *iscan = (struct brcmf_cfg80211_iscan_ctrl *)data; if (iscan) { iscan->timer_on = 0; WL_SCAN("timer expired\n"); brcmf_wakeup_iscan(iscan); } } static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv); if (cfg_priv->iscan_on) { iscan->state = WL_ISCAN_STATE_IDLE; INIT_WORK(&iscan->work, brcmf_cfg80211_iscan_handler); } return 0; } static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el) { memset(el, 0, sizeof(*el)); el->handler[BRCMF_SCAN_RESULTS_SUCCESS] = brcmf_iscan_done; el->handler[BRCMF_SCAN_RESULTS_PARTIAL] = brcmf_iscan_inprogress; el->handler[BRCMF_SCAN_RESULTS_PENDING] = brcmf_iscan_pending; el->handler[BRCMF_SCAN_RESULTS_ABORTED] = brcmf_iscan_aborted; el->handler[BRCMF_SCAN_RESULTS_NO_MEM] = brcmf_iscan_aborted; } static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv); int err = 0; if (cfg_priv->iscan_on) { iscan->ndev = cfg_to_ndev(cfg_priv); brcmf_init_iscan_eloop(&iscan->el); iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS; init_timer(&iscan->timer); iscan->timer.data = (unsigned long) iscan; iscan->timer.function = brcmf_iscan_timer; err = brcmf_invoke_iscan(cfg_priv); if (!err) iscan->data = cfg_priv; } return err; } static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) { struct brcmf_cfg80211_priv *cfg_priv = container_of(work, struct brcmf_cfg80211_priv, escan_timeout_work); brcmf_notify_escan_complete(cfg_priv, cfg_priv->escan_info.ndev, true, true); } static void brcmf_escan_timeout(unsigned long data) { struct brcmf_cfg80211_priv *cfg_priv = (struct brcmf_cfg80211_priv *)data; if (cfg_priv->scan_request) { WL_ERR("timer expired\n"); if (cfg_priv->escan_on) schedule_work(&cfg_priv->escan_timeout_work); } } static s32 brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss, struct brcmf_bss_info_le *bss_info_le) { if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) && (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) == CHSPEC_BAND(le16_to_cpu(bss->chanspec))) && bss_info_le->SSID_len == bss->SSID_len && !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) { /* preserve max RSSI if the measurements are * both on-channel or both off-channel */ if (bss_info_le->RSSI > bss->RSSI) bss->RSSI = bss_info_le->RSSI; } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { /* preserve the on-channel rssi measurement * if the new measurement is off channel */ bss->RSSI = bss_info_le->RSSI; bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; } return 1; } return 0; } static s32 brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { s32 status; s32 err = 0; struct brcmf_escan_result_le *escan_result_le; struct brcmf_bss_info_le *bss_info_le; struct brcmf_bss_info_le *bss = NULL; u32 bi_length; struct brcmf_scan_results *list; u32 i; status = be32_to_cpu(e->status); if (!ndev || !cfg_priv->escan_on || !test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n", ndev, cfg_priv->escan_on, !test_bit(WL_STATUS_SCANNING, &cfg_priv->status)); return -EPERM; } if (status == BRCMF_E_STATUS_PARTIAL) { WL_SCAN("ESCAN Partial result\n"); escan_result_le = (struct brcmf_escan_result_le *) data; if (!escan_result_le) { WL_ERR("Invalid escan result (NULL pointer)\n"); goto exit; } if (!cfg_priv->scan_request) { WL_SCAN("result without cfg80211 request\n"); goto exit; } if (le16_to_cpu(escan_result_le->bss_count) != 1) { WL_ERR("Invalid bss_count %d: ignoring\n", escan_result_le->bss_count); goto exit; } bss_info_le = &escan_result_le->bss_info_le; bi_length = le32_to_cpu(bss_info_le->length); if (bi_length != (le32_to_cpu(escan_result_le->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) { WL_ERR("Invalid bss_info length %d: ignoring\n", bi_length); goto exit; } if (!(cfg_to_wiphy(cfg_priv)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) { if (le16_to_cpu(bss_info_le->capability) & WLAN_CAPABILITY_IBSS) { WL_ERR("Ignoring IBSS result\n"); goto exit; } } list = (struct brcmf_scan_results *) cfg_priv->escan_info.escan_buf; if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) { WL_ERR("Buffer is too small: ignoring\n"); goto exit; } for (i = 0; i < list->count; i++) { bss = bss ? (struct brcmf_bss_info_le *) ((unsigned char *)bss + le32_to_cpu(bss->length)) : list->bss_info_le; if (brcmf_compare_update_same_bss(bss, bss_info_le)) goto exit; } memcpy(&(cfg_priv->escan_info.escan_buf[list->buflen]), bss_info_le, bi_length); list->version = le32_to_cpu(bss_info_le->version); list->buflen += bi_length; list->count++; } else { cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (cfg_priv->scan_request) { cfg_priv->bss_list = (struct brcmf_scan_results *) cfg_priv->escan_info.escan_buf; brcmf_inform_bss(cfg_priv); if (status == BRCMF_E_STATUS_SUCCESS) { WL_SCAN("ESCAN Completed\n"); brcmf_notify_escan_complete(cfg_priv, ndev, false, false); } else { WL_ERR("ESCAN Aborted, Event 0x%x\n", status); brcmf_notify_escan_complete(cfg_priv, ndev, true, false); } brcmf_set_mpc(ndev, 1); } else WL_ERR("Unexpected scan result 0x%x\n", status); } exit: return err; } static void brcmf_init_escan(struct brcmf_cfg80211_priv *cfg_priv) { if (cfg_priv->escan_on) { cfg_priv->el.handler[BRCMF_E_ESCAN_RESULT] = brcmf_cfg80211_escan_handler; cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE; /* Init scan_timeout timer */ init_timer(&cfg_priv->escan_timeout); cfg_priv->escan_timeout.data = (unsigned long) cfg_priv; cfg_priv->escan_timeout.function = brcmf_escan_timeout; INIT_WORK(&cfg_priv->escan_timeout_work, brcmf_cfg80211_escan_timeout_worker); } } static __always_inline void brcmf_delay(u32 ms) { if (ms < 1000 / HZ) { cond_resched(); mdelay(ms); } else { msleep(ms); } } static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); /* * Check for WL_STATUS_READY before any function call which * could result is bus access. Don't block the resume for * any driver error conditions */ WL_TRACE("Enter\n"); if (test_bit(WL_STATUS_READY, &cfg_priv->status)) brcmf_invoke_iscan(wiphy_to_cfg(wiphy)); WL_TRACE("Exit\n"); return 0; } static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg_priv); WL_TRACE("Enter\n"); /* * Check for WL_STATUS_READY before any function call which * could result is bus access. Don't block the suspend for * any driver error conditions */ /* * While going to suspend if associated with AP disassociate * from AP to save power while system is in suspended state */ if ((test_bit(WL_STATUS_CONNECTED, &cfg_priv->status) || test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) && test_bit(WL_STATUS_READY, &cfg_priv->status)) { WL_INFO("Disassociating from AP" " while entering suspend state\n"); brcmf_link_down(cfg_priv); /* * Make sure WPA_Supplicant receives all the event * generated due to DISASSOC call to the fw to keep * the state fw and WPA_Supplicant state consistent */ brcmf_delay(500); } set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); if (test_bit(WL_STATUS_READY, &cfg_priv->status)) brcmf_term_iscan(cfg_priv); if (cfg_priv->scan_request) { /* Indidate scan abort to cfg80211 layer */ WL_INFO("Terminating scan in progress\n"); cfg80211_scan_done(cfg_priv->scan_request, true); cfg_priv->scan_request = NULL; } clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); /* Turn off watchdog timer */ if (test_bit(WL_STATUS_READY, &cfg_priv->status)) brcmf_set_mpc(ndev, 1); WL_TRACE("Exit\n"); return 0; } static __used s32 brcmf_dev_bufvar_set(struct net_device *ndev, s8 *name, s8 *buf, s32 len) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); u32 buflen; buflen = brcmf_c_mkiovar(name, buf, len, cfg_priv->dcmd_buf, WL_DCMD_LEN_MAX); BUG_ON(!buflen); return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, cfg_priv->dcmd_buf, buflen); } static s32 brcmf_dev_bufvar_get(struct net_device *ndev, s8 *name, s8 *buf, s32 buf_len) { struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); u32 len; s32 err = 0; len = brcmf_c_mkiovar(name, NULL, 0, cfg_priv->dcmd_buf, WL_DCMD_LEN_MAX); BUG_ON(!len); err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, cfg_priv->dcmd_buf, WL_DCMD_LEN_MAX); if (err) { WL_ERR("error (%d)\n", err); return err; } memcpy(buf, cfg_priv->dcmd_buf, buf_len); return err; } static __used s32 brcmf_update_pmklist(struct net_device *ndev, struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) { int i, j; int pmkid_len; pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); WL_CONN("No of elements %d\n", pmkid_len); for (i = 0; i < pmkid_len; i++) { WL_CONN("PMKID[%d]: %pM =\n", i, &pmk_list->pmkids.pmkid[i].BSSID); for (j = 0; j < WLAN_PMKID_LEN; j++) WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]); } if (!err) brcmf_dev_bufvar_set(ndev, "pmkid_info", (char *)pmk_list, sizeof(*pmk_list)); return err; } static s32 brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_pmksa *pmksa) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct pmkid_list *pmkids = &cfg_priv->pmk_list->pmkids; s32 err = 0; int i; int pmkid_len; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; pmkid_len = le32_to_cpu(pmkids->npmkid); for (i = 0; i < pmkid_len; i++) if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN)) break; if (i < WL_NUM_PMKIDS_MAX) { memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN); memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); if (i == pmkid_len) { pmkid_len++; pmkids->npmkid = cpu_to_le32(pmkid_len); } } else err = -EINVAL; WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", pmkids->pmkid[pmkid_len].BSSID); for (i = 0; i < WLAN_PMKID_LEN; i++) WL_CONN("%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]); err = brcmf_update_pmklist(ndev, cfg_priv->pmk_list, err); WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_pmksa *pmksa) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct pmkid_list pmkid; s32 err = 0; int i, pmkid_len; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN); memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", &pmkid.pmkid[0].BSSID); for (i = 0; i < WLAN_PMKID_LEN; i++) WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]); pmkid_len = le32_to_cpu(cfg_priv->pmk_list->pmkids.npmkid); for (i = 0; i < pmkid_len; i++) if (!memcmp (pmksa->bssid, &cfg_priv->pmk_list->pmkids.pmkid[i].BSSID, ETH_ALEN)) break; if ((pmkid_len > 0) && (i < pmkid_len)) { memset(&cfg_priv->pmk_list->pmkids.pmkid[i], 0, sizeof(struct pmkid)); for (; i < (pmkid_len - 1); i++) { memcpy(&cfg_priv->pmk_list->pmkids.pmkid[i].BSSID, &cfg_priv->pmk_list->pmkids.pmkid[i + 1].BSSID, ETH_ALEN); memcpy(&cfg_priv->pmk_list->pmkids.pmkid[i].PMKID, &cfg_priv->pmk_list->pmkids.pmkid[i + 1].PMKID, WLAN_PMKID_LEN); } cfg_priv->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1); } else err = -EINVAL; err = brcmf_update_pmklist(ndev, cfg_priv->pmk_list, err); WL_TRACE("Exit\n"); return err; } static s32 brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); s32 err = 0; WL_TRACE("Enter\n"); if (!check_sys_up(wiphy)) return -EIO; memset(cfg_priv->pmk_list, 0, sizeof(*cfg_priv->pmk_list)); err = brcmf_update_pmklist(ndev, cfg_priv->pmk_list, err); WL_TRACE("Exit\n"); return err; } #ifdef CONFIG_NL80211_TESTMODE static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len) { struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_priv->wdev->netdev; struct brcmf_dcmd *dcmd = data; struct sk_buff *reply; int ret; ret = brcmf_netlink_dcmd(ndev, dcmd); if (ret == 0) { reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd)); nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd); ret = cfg80211_testmode_reply(reply); } return ret; } #endif static struct cfg80211_ops wl_cfg80211_ops = { .change_virtual_intf = brcmf_cfg80211_change_iface, .scan = brcmf_cfg80211_scan, .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, .join_ibss = brcmf_cfg80211_join_ibss, .leave_ibss = brcmf_cfg80211_leave_ibss, .get_station = brcmf_cfg80211_get_station, .set_tx_power = brcmf_cfg80211_set_tx_power, .get_tx_power = brcmf_cfg80211_get_tx_power, .add_key = brcmf_cfg80211_add_key, .del_key = brcmf_cfg80211_del_key, .get_key = brcmf_cfg80211_get_key, .set_default_key = brcmf_cfg80211_config_default_key, .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask, .connect = brcmf_cfg80211_connect, .disconnect = brcmf_cfg80211_disconnect, .suspend = brcmf_cfg80211_suspend, .resume = brcmf_cfg80211_resume, .set_pmksa = brcmf_cfg80211_set_pmksa, .del_pmksa = brcmf_cfg80211_del_pmksa, .flush_pmksa = brcmf_cfg80211_flush_pmksa, #ifdef CONFIG_NL80211_TESTMODE .testmode_cmd = brcmf_cfg80211_testmode #endif }; static s32 brcmf_mode_to_nl80211_iftype(s32 mode) { s32 err = 0; switch (mode) { case WL_MODE_BSS: return NL80211_IFTYPE_STATION; case WL_MODE_IBSS: return NL80211_IFTYPE_ADHOC; default: return NL80211_IFTYPE_UNSPECIFIED; } return err; } static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface, struct device *ndev) { struct wireless_dev *wdev; s32 err = 0; wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); if (!wdev) return ERR_PTR(-ENOMEM); wdev->wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_priv) + sizeof_iface); if (!wdev->wiphy) { WL_ERR("Could not allocate wiphy device\n"); err = -ENOMEM; goto wiphy_new_out; } set_wiphy_dev(wdev->wiphy, ndev); wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set * it as 11a by default. * This will be updated with * 11n phy tables in * "ifconfig up" * if phy has 11n capability */ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wdev->wiphy->cipher_suites = __wl_cipher_suites; wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power * save mode * by default */ err = wiphy_register(wdev->wiphy); if (err < 0) { WL_ERR("Could not register wiphy device (%d)\n", err); goto wiphy_register_out; } return wdev; wiphy_register_out: wiphy_free(wdev->wiphy); wiphy_new_out: kfree(wdev); return ERR_PTR(err); } static void brcmf_free_wdev(struct brcmf_cfg80211_priv *cfg_priv) { struct wireless_dev *wdev = cfg_priv->wdev; if (!wdev) { WL_ERR("wdev is invalid\n"); return; } wiphy_unregister(wdev->wiphy); wiphy_free(wdev->wiphy); kfree(wdev); cfg_priv->wdev = NULL; } static bool brcmf_is_linkup(struct brcmf_cfg80211_priv *cfg_priv, const struct brcmf_event_msg *e) { u32 event = be32_to_cpu(e->event_type); u32 status = be32_to_cpu(e->status); if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { WL_CONN("Processing set ssid\n"); cfg_priv->link_up = true; return true; } return false; } static bool brcmf_is_linkdown(struct brcmf_cfg80211_priv *cfg_priv, const struct brcmf_event_msg *e) { u32 event = be32_to_cpu(e->event_type); u16 flags = be16_to_cpu(e->flags); if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) { WL_CONN("Processing link down\n"); return true; } return false; } static bool brcmf_is_nonetwork(struct brcmf_cfg80211_priv *cfg_priv, const struct brcmf_event_msg *e) { u32 event = be32_to_cpu(e->event_type); u32 status = be32_to_cpu(e->status); if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) { WL_CONN("Processing Link %s & no network found\n", be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ? "up" : "down"); return true; } if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) { WL_CONN("Processing connecting & no network found\n"); return true; } return false; } static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv); kfree(conn_info->req_ie); conn_info->req_ie = NULL; conn_info->req_ie_len = 0; kfree(conn_info->resp_ie); conn_info->resp_ie = NULL; conn_info->resp_ie_len = 0; } static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv) { struct net_device *ndev = cfg_to_ndev(cfg_priv); struct brcmf_cfg80211_assoc_ielen_le *assoc_info; struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv); u32 req_len; u32 resp_len; s32 err = 0; brcmf_clear_assoc_ies(cfg_priv); err = brcmf_dev_bufvar_get(ndev, "assoc_info", cfg_priv->extra_buf, WL_ASSOC_INFO_MAX); if (err) { WL_ERR("could not get assoc info (%d)\n", err); return err; } assoc_info = (struct brcmf_cfg80211_assoc_ielen_le *)cfg_priv->extra_buf; req_len = le32_to_cpu(assoc_info->req_len); resp_len = le32_to_cpu(assoc_info->resp_len); if (req_len) { err = brcmf_dev_bufvar_get(ndev, "assoc_req_ies", cfg_priv->extra_buf, WL_ASSOC_INFO_MAX); if (err) { WL_ERR("could not get assoc req (%d)\n", err); return err; } conn_info->req_ie_len = req_len; conn_info->req_ie = kmemdup(cfg_priv->extra_buf, conn_info->req_ie_len, GFP_KERNEL); } else { conn_info->req_ie_len = 0; conn_info->req_ie = NULL; } if (resp_len) { err = brcmf_dev_bufvar_get(ndev, "assoc_resp_ies", cfg_priv->extra_buf, WL_ASSOC_INFO_MAX); if (err) { WL_ERR("could not get assoc resp (%d)\n", err); return err; } conn_info->resp_ie_len = resp_len; conn_info->resp_ie = kmemdup(cfg_priv->extra_buf, conn_info->resp_ie_len, GFP_KERNEL); } else { conn_info->resp_ie_len = 0; conn_info->resp_ie = NULL; } WL_CONN("req len (%d) resp len (%d)\n", conn_info->req_ie_len, conn_info->resp_ie_len); return err; } static s32 brcmf_bss_roaming_done(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e) { struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv); struct wiphy *wiphy = cfg_to_wiphy(cfg_priv); struct brcmf_channel_info_le channel_le; struct ieee80211_channel *notify_channel; struct ieee80211_supported_band *band; u32 freq; s32 err = 0; u32 target_channel; WL_TRACE("Enter\n"); brcmf_get_assoc_ies(cfg_priv); brcmf_update_prof(cfg_priv, NULL, &e->addr, WL_PROF_BSSID); brcmf_update_bss_info(cfg_priv); brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_le, sizeof(channel_le)); target_channel = le32_to_cpu(channel_le.target_channel); WL_CONN("Roamed to channel %d\n", target_channel); if (target_channel <= CH_MAX_2G_CHANNEL) band = wiphy->bands[IEEE80211_BAND_2GHZ]; else band = wiphy->bands[IEEE80211_BAND_5GHZ]; freq = ieee80211_channel_to_frequency(target_channel, band->band); notify_channel = ieee80211_get_channel(wiphy, freq); cfg80211_roamed(ndev, notify_channel, (u8 *)brcmf_read_prof(cfg_priv, WL_PROF_BSSID), conn_info->req_ie, conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); WL_CONN("Report roaming result\n"); set_bit(WL_STATUS_CONNECTED, &cfg_priv->status); WL_TRACE("Exit\n"); return err; } static s32 brcmf_bss_connect_done(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e, bool completed) { struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv); s32 err = 0; WL_TRACE("Enter\n"); if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) { if (completed) { brcmf_get_assoc_ies(cfg_priv); brcmf_update_prof(cfg_priv, NULL, &e->addr, WL_PROF_BSSID); brcmf_update_bss_info(cfg_priv); } cfg80211_connect_result(ndev, (u8 *)brcmf_read_prof(cfg_priv, WL_PROF_BSSID), conn_info->req_ie, conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len, completed ? WLAN_STATUS_SUCCESS : WLAN_STATUS_AUTH_TIMEOUT, GFP_KERNEL); if (completed) set_bit(WL_STATUS_CONNECTED, &cfg_priv->status); WL_CONN("Report connect result - connection %s\n", completed ? "succeeded" : "failed"); } WL_TRACE("Exit\n"); return err; } static s32 brcmf_notify_connect_status(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { s32 err = 0; if (brcmf_is_linkup(cfg_priv, e)) { WL_CONN("Linkup\n"); if (brcmf_is_ibssmode(cfg_priv)) { brcmf_update_prof(cfg_priv, NULL, (void *)e->addr, WL_PROF_BSSID); wl_inform_ibss(cfg_priv, ndev, e->addr); cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL); clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status); set_bit(WL_STATUS_CONNECTED, &cfg_priv->status); } else brcmf_bss_connect_done(cfg_priv, ndev, e, true); } else if (brcmf_is_linkdown(cfg_priv, e)) { WL_CONN("Linkdown\n"); if (brcmf_is_ibssmode(cfg_priv)) { clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status); if (test_and_clear_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) brcmf_link_down(cfg_priv); } else { brcmf_bss_connect_done(cfg_priv, ndev, e, false); if (test_and_clear_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) { cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); brcmf_link_down(cfg_priv); } } brcmf_init_prof(cfg_priv->profile); } else if (brcmf_is_nonetwork(cfg_priv, e)) { if (brcmf_is_ibssmode(cfg_priv)) clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status); else brcmf_bss_connect_done(cfg_priv, ndev, e, false); } return err; } static s32 brcmf_notify_roaming_status(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { s32 err = 0; u32 event = be32_to_cpu(e->event_type); u32 status = be32_to_cpu(e->status); if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) brcmf_bss_roaming_done(cfg_priv, ndev, e); else brcmf_bss_connect_done(cfg_priv, ndev, e, true); } return err; } static s32 brcmf_notify_mic_status(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { u16 flags = be16_to_cpu(e->flags); enum nl80211_key_type key_type; if (flags & BRCMF_EVENT_MSG_GROUP) key_type = NL80211_KEYTYPE_GROUP; else key_type = NL80211_KEYTYPE_PAIRWISE; cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1, NULL, GFP_KERNEL); return 0; } static s32 brcmf_notify_scan_status(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { struct brcmf_channel_info_le channel_inform_le; struct brcmf_scan_results_le *bss_list_le; u32 len = WL_SCAN_BUF_MAX; s32 err = 0; bool scan_abort = false; u32 scan_channel; WL_TRACE("Enter\n"); if (cfg_priv->iscan_on && cfg_priv->iscan_kickstart) { WL_TRACE("Exit\n"); return brcmf_wakeup_iscan(cfg_to_iscan(cfg_priv)); } if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { WL_ERR("Scan complete while device not scanning\n"); scan_abort = true; err = -EINVAL; goto scan_done_out; } err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_inform_le, sizeof(channel_inform_le)); if (err) { WL_ERR("scan busy (%d)\n", err); scan_abort = true; goto scan_done_out; } scan_channel = le32_to_cpu(channel_inform_le.scan_channel); if (scan_channel) WL_CONN("channel_inform.scan_channel (%d)\n", scan_channel); cfg_priv->bss_list = cfg_priv->scan_results; bss_list_le = (struct brcmf_scan_results_le *) cfg_priv->bss_list; memset(cfg_priv->scan_results, 0, len); bss_list_le->buflen = cpu_to_le32(len); err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN_RESULTS, cfg_priv->scan_results, len); if (err) { WL_ERR("%s Scan_results error (%d)\n", ndev->name, err); err = -EINVAL; scan_abort = true; goto scan_done_out; } cfg_priv->scan_results->buflen = le32_to_cpu(bss_list_le->buflen); cfg_priv->scan_results->version = le32_to_cpu(bss_list_le->version); cfg_priv->scan_results->count = le32_to_cpu(bss_list_le->count); err = brcmf_inform_bss(cfg_priv); if (err) scan_abort = true; scan_done_out: if (cfg_priv->scan_request) { WL_SCAN("calling cfg80211_scan_done\n"); cfg80211_scan_done(cfg_priv->scan_request, scan_abort); brcmf_set_mpc(ndev, 1); cfg_priv->scan_request = NULL; } WL_TRACE("Exit\n"); return err; } static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) { conf->mode = (u32)-1; conf->frag_threshold = (u32)-1; conf->rts_threshold = (u32)-1; conf->retry_short = (u32)-1; conf->retry_long = (u32)-1; conf->tx_power = -1; } static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el) { memset(el, 0, sizeof(*el)); el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status; el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status; el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status; el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status; el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status; } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv) { kfree(cfg_priv->scan_results); cfg_priv->scan_results = NULL; kfree(cfg_priv->bss_info); cfg_priv->bss_info = NULL; kfree(cfg_priv->conf); cfg_priv->conf = NULL; kfree(cfg_priv->profile); cfg_priv->profile = NULL; kfree(cfg_priv->scan_req_int); cfg_priv->scan_req_int = NULL; kfree(cfg_priv->escan_ioctl_buf); cfg_priv->escan_ioctl_buf = NULL; kfree(cfg_priv->dcmd_buf); cfg_priv->dcmd_buf = NULL; kfree(cfg_priv->extra_buf); cfg_priv->extra_buf = NULL; kfree(cfg_priv->iscan); cfg_priv->iscan = NULL; kfree(cfg_priv->pmk_list); cfg_priv->pmk_list = NULL; } static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_priv *cfg_priv) { cfg_priv->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL); if (!cfg_priv->scan_results) goto init_priv_mem_out; cfg_priv->conf = kzalloc(sizeof(*cfg_priv->conf), GFP_KERNEL); if (!cfg_priv->conf) goto init_priv_mem_out; cfg_priv->profile = kzalloc(sizeof(*cfg_priv->profile), GFP_KERNEL); if (!cfg_priv->profile) goto init_priv_mem_out; cfg_priv->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); if (!cfg_priv->bss_info) goto init_priv_mem_out; cfg_priv->scan_req_int = kzalloc(sizeof(*cfg_priv->scan_req_int), GFP_KERNEL); if (!cfg_priv->scan_req_int) goto init_priv_mem_out; cfg_priv->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); if (!cfg_priv->escan_ioctl_buf) goto init_priv_mem_out; cfg_priv->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL); if (!cfg_priv->dcmd_buf) goto init_priv_mem_out; cfg_priv->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!cfg_priv->extra_buf) goto init_priv_mem_out; cfg_priv->iscan = kzalloc(sizeof(*cfg_priv->iscan), GFP_KERNEL); if (!cfg_priv->iscan) goto init_priv_mem_out; cfg_priv->pmk_list = kzalloc(sizeof(*cfg_priv->pmk_list), GFP_KERNEL); if (!cfg_priv->pmk_list) goto init_priv_mem_out; return 0; init_priv_mem_out: brcmf_deinit_priv_mem(cfg_priv); return -ENOMEM; } /* * retrieve first queued event from head */ static struct brcmf_cfg80211_event_q *brcmf_deq_event( struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_event_q *e = NULL; spin_lock_irq(&cfg_priv->evt_q_lock); if (!list_empty(&cfg_priv->evt_q_list)) { e = list_first_entry(&cfg_priv->evt_q_list, struct brcmf_cfg80211_event_q, evt_q_list); list_del(&e->evt_q_list); } spin_unlock_irq(&cfg_priv->evt_q_lock); return e; } /* * push event to tail of the queue * * remark: this function may not sleep as it is called in atomic context. */ static s32 brcmf_enq_event(struct brcmf_cfg80211_priv *cfg_priv, u32 event, const struct brcmf_event_msg *msg, void *data) { struct brcmf_cfg80211_event_q *e; s32 err = 0; ulong flags; u32 data_len; u32 total_len; total_len = sizeof(struct brcmf_cfg80211_event_q); if (data) data_len = be32_to_cpu(msg->datalen); else data_len = 0; total_len += data_len; e = kzalloc(total_len, GFP_ATOMIC); if (!e) return -ENOMEM; e->etype = event; memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg)); if (data) memcpy(&e->edata, data, data_len); spin_lock_irqsave(&cfg_priv->evt_q_lock, flags); list_add_tail(&e->evt_q_list, &cfg_priv->evt_q_list); spin_unlock_irqrestore(&cfg_priv->evt_q_lock, flags); return err; } static void brcmf_put_event(struct brcmf_cfg80211_event_q *e) { kfree(e); } static void brcmf_cfg80211_event_handler(struct work_struct *work) { struct brcmf_cfg80211_priv *cfg_priv = container_of(work, struct brcmf_cfg80211_priv, event_work); struct brcmf_cfg80211_event_q *e; e = brcmf_deq_event(cfg_priv); if (unlikely(!e)) { WL_ERR("event queue empty...\n"); return; } do { WL_INFO("event type (%d)\n", e->etype); if (cfg_priv->el.handler[e->etype]) cfg_priv->el.handler[e->etype](cfg_priv, cfg_to_ndev(cfg_priv), &e->emsg, e->edata); else WL_INFO("Unknown Event (%d): ignoring\n", e->etype); brcmf_put_event(e); } while ((e = brcmf_deq_event(cfg_priv))); } static void brcmf_init_eq(struct brcmf_cfg80211_priv *cfg_priv) { spin_lock_init(&cfg_priv->evt_q_lock); INIT_LIST_HEAD(&cfg_priv->evt_q_list); } static void brcmf_flush_eq(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_event_q *e; spin_lock_irq(&cfg_priv->evt_q_lock); while (!list_empty(&cfg_priv->evt_q_list)) { e = list_first_entry(&cfg_priv->evt_q_list, struct brcmf_cfg80211_event_q, evt_q_list); list_del(&e->evt_q_list); kfree(e); } spin_unlock_irq(&cfg_priv->evt_q_lock); } static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv) { s32 err = 0; cfg_priv->scan_request = NULL; cfg_priv->pwr_save = true; #ifdef CONFIG_BRCMISCAN cfg_priv->iscan_on = true; /* iscan on & off switch. we enable iscan per default */ cfg_priv->escan_on = false; /* escan on & off switch. we disable escan per default */ #else cfg_priv->iscan_on = false; /* iscan on & off switch. we disable iscan per default */ cfg_priv->escan_on = true; /* escan on & off switch. we enable escan per default */ #endif cfg_priv->roam_on = true; /* roam on & off switch. we enable roam per default */ cfg_priv->iscan_kickstart = false; cfg_priv->active_scan = true; /* we do active scan for specific scan per default */ cfg_priv->dongle_up = false; /* dongle is not up yet */ brcmf_init_eq(cfg_priv); err = brcmf_init_priv_mem(cfg_priv); if (err) return err; INIT_WORK(&cfg_priv->event_work, brcmf_cfg80211_event_handler); brcmf_init_eloop_handler(&cfg_priv->el); mutex_init(&cfg_priv->usr_sync); err = brcmf_init_iscan(cfg_priv); if (err) return err; brcmf_init_escan(cfg_priv); brcmf_init_conf(cfg_priv->conf); brcmf_init_prof(cfg_priv->profile); brcmf_link_down(cfg_priv); return err; } static void wl_deinit_priv(struct brcmf_cfg80211_priv *cfg_priv) { cancel_work_sync(&cfg_priv->event_work); cfg_priv->dongle_up = false; /* dongle down */ brcmf_flush_eq(cfg_priv); brcmf_link_down(cfg_priv); brcmf_term_iscan(cfg_priv); brcmf_deinit_priv_mem(cfg_priv); } struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev, struct device *busdev, void *data) { struct wireless_dev *wdev; struct brcmf_cfg80211_priv *cfg_priv; struct brcmf_cfg80211_iface *ci; struct brcmf_cfg80211_dev *cfg_dev; s32 err = 0; if (!ndev) { WL_ERR("ndev is invalid\n"); return NULL; } cfg_dev = kzalloc(sizeof(struct brcmf_cfg80211_dev), GFP_KERNEL); if (!cfg_dev) return NULL; wdev = brcmf_alloc_wdev(sizeof(struct brcmf_cfg80211_iface), busdev); if (IS_ERR(wdev)) { kfree(cfg_dev); return NULL; } wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS); cfg_priv = wdev_to_cfg(wdev); cfg_priv->wdev = wdev; cfg_priv->pub = data; ci = (struct brcmf_cfg80211_iface *)&cfg_priv->ci; ci->cfg_priv = cfg_priv; ndev->ieee80211_ptr = wdev; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; err = wl_init_priv(cfg_priv); if (err) { WL_ERR("Failed to init iwm_priv (%d)\n", err); goto cfg80211_attach_out; } brcmf_set_drvdata(cfg_dev, ci); return cfg_dev; cfg80211_attach_out: brcmf_free_wdev(cfg_priv); kfree(cfg_dev); return NULL; } void brcmf_cfg80211_detach(struct brcmf_cfg80211_dev *cfg_dev) { struct brcmf_cfg80211_priv *cfg_priv; cfg_priv = brcmf_priv_get(cfg_dev); wl_deinit_priv(cfg_priv); brcmf_free_wdev(cfg_priv); brcmf_set_drvdata(cfg_dev, NULL); kfree(cfg_dev); } void brcmf_cfg80211_event(struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { u32 event_type = be32_to_cpu(e->event_type); struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); if (!brcmf_enq_event(cfg_priv, event_type, e, data)) schedule_work(&cfg_priv->event_work); } static s32 brcmf_dongle_mode(struct net_device *ndev, s32 iftype) { s32 infra = 0; s32 err = 0; switch (iftype) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: WL_ERR("type (%d) : currently we do not support this mode\n", iftype); err = -EINVAL; return err; case NL80211_IFTYPE_ADHOC: infra = 0; break; case NL80211_IFTYPE_STATION: infra = 1; break; default: err = -EINVAL; WL_ERR("invalid type (%d)\n", iftype); return err; } err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra); if (err) { WL_ERR("WLC_SET_INFRA error (%d)\n", err); return err; } return 0; } static s32 brcmf_dongle_eventmsg(struct net_device *ndev) { /* Room for "event_msgs" + '\0' + bitvec */ s8 iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; s8 eventmask[BRCMF_EVENTING_MASK_LEN]; s32 err = 0; WL_TRACE("Enter\n"); /* Setup event_msgs */ brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, iovbuf, sizeof(iovbuf)); if (err) { WL_ERR("Get event_msgs error (%d)\n", err); goto dongle_eventmsg_out; } memcpy(eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN); setbit(eventmask, BRCMF_E_SET_SSID); setbit(eventmask, BRCMF_E_ROAM); setbit(eventmask, BRCMF_E_PRUNE); setbit(eventmask, BRCMF_E_AUTH); setbit(eventmask, BRCMF_E_REASSOC); setbit(eventmask, BRCMF_E_REASSOC_IND); setbit(eventmask, BRCMF_E_DEAUTH_IND); setbit(eventmask, BRCMF_E_DISASSOC_IND); setbit(eventmask, BRCMF_E_DISASSOC); setbit(eventmask, BRCMF_E_JOIN); setbit(eventmask, BRCMF_E_ASSOC_IND); setbit(eventmask, BRCMF_E_PSK_SUP); setbit(eventmask, BRCMF_E_LINK); setbit(eventmask, BRCMF_E_NDIS_LINK); setbit(eventmask, BRCMF_E_MIC_ERROR); setbit(eventmask, BRCMF_E_PMKID_CACHE); setbit(eventmask, BRCMF_E_TXFAIL); setbit(eventmask, BRCMF_E_JOIN_START); setbit(eventmask, BRCMF_E_SCAN_COMPLETE); setbit(eventmask, BRCMF_E_ESCAN_RESULT); brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); if (err) { WL_ERR("Set event_msgs error (%d)\n", err); goto dongle_eventmsg_out; } dongle_eventmsg_out: WL_TRACE("Exit\n"); return err; } static s32 brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) { s8 iovbuf[32]; s32 err = 0; __le32 roamtrigger[2]; __le32 roam_delta[2]; __le32 bcn_to_le; __le32 roamvar_le; /* * Setup timeout if Beacons are lost and roam is * off to report link down */ if (roamvar) { bcn_to_le = cpu_to_le32(bcn_timeout); brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_to_le, sizeof(bcn_to_le), iovbuf, sizeof(iovbuf)); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); if (err) { WL_ERR("bcn_timeout error (%d)\n", err); goto dongle_rom_out; } } /* * Enable/Disable built-in roaming to allow supplicant * to take care of roaming */ WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On"); roamvar_le = cpu_to_le32(roamvar); brcmf_c_mkiovar("roam_off", (char *)&roamvar_le, sizeof(roamvar_le), iovbuf, sizeof(iovbuf)); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); if (err) { WL_ERR("roam_off error (%d)\n", err); goto dongle_rom_out; } roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL); roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_TRIGGER, (void *)roamtrigger, sizeof(roamtrigger)); if (err) { WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err); goto dongle_rom_out; } roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_DELTA, (void *)roam_delta, sizeof(roam_delta)); if (err) { WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err); goto dongle_rom_out; } dongle_rom_out: return err; } static s32 brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, s32 scan_unassoc_time, s32 scan_passive_time) { s32 err = 0; __le32 scan_assoc_tm_le = cpu_to_le32(scan_assoc_time); __le32 scan_unassoc_tm_le = cpu_to_le32(scan_unassoc_time); __le32 scan_passive_tm_le = cpu_to_le32(scan_passive_time); err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME, &scan_assoc_tm_le, sizeof(scan_assoc_tm_le)); if (err) { if (err == -EOPNOTSUPP) WL_INFO("Scan assoc time is not supported\n"); else WL_ERR("Scan assoc time error (%d)\n", err); goto dongle_scantime_out; } err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME, &scan_unassoc_tm_le, sizeof(scan_unassoc_tm_le)); if (err) { if (err == -EOPNOTSUPP) WL_INFO("Scan unassoc time is not supported\n"); else WL_ERR("Scan unassoc time error (%d)\n", err); goto dongle_scantime_out; } err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME, &scan_passive_tm_le, sizeof(scan_passive_tm_le)); if (err) { if (err == -EOPNOTSUPP) WL_INFO("Scan passive time is not supported\n"); else WL_ERR("Scan passive time error (%d)\n", err); goto dongle_scantime_out; } dongle_scantime_out: return err; } static s32 wl_update_wiphybands(struct brcmf_cfg80211_priv *cfg_priv) { struct wiphy *wiphy; s32 phy_list; s8 phy; s32 err = 0; err = brcmf_exec_dcmd(cfg_to_ndev(cfg_priv), BRCM_GET_PHYLIST, &phy_list, sizeof(phy_list)); if (err) { WL_ERR("error (%d)\n", err); return err; } phy = ((char *)&phy_list)[1]; WL_INFO("%c phy\n", phy); if (phy == 'n' || phy == 'a') { wiphy = cfg_to_wiphy(cfg_priv); wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n; } return err; } static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_priv *cfg_priv) { return wl_update_wiphybands(cfg_priv); } static s32 brcmf_config_dongle(struct brcmf_cfg80211_priv *cfg_priv) { struct net_device *ndev; struct wireless_dev *wdev; s32 power_mode; s32 err = 0; if (cfg_priv->dongle_up) return err; ndev = cfg_to_ndev(cfg_priv); wdev = ndev->ieee80211_ptr; brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME, WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); err = brcmf_dongle_eventmsg(ndev); if (err) goto default_conf_out; power_mode = cfg_priv->pwr_save ? PM_FAST : PM_OFF; err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &power_mode); if (err) goto default_conf_out; WL_INFO("power save set to %s\n", (power_mode ? "enabled" : "disabled")); err = brcmf_dongle_roam(ndev, (cfg_priv->roam_on ? 0 : 1), WL_BEACON_TIMEOUT); if (err) goto default_conf_out; err = brcmf_dongle_mode(ndev, wdev->iftype); if (err && err != -EINPROGRESS) goto default_conf_out; err = brcmf_dongle_probecap(cfg_priv); if (err) goto default_conf_out; /* -EINPROGRESS: Call commit handler */ default_conf_out: cfg_priv->dongle_up = true; return err; } static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_priv *cfg_priv) { char buf[10+IFNAMSIZ]; struct dentry *fd; s32 err = 0; sprintf(buf, "netdev:%s", cfg_to_ndev(cfg_priv)->name); cfg_priv->debugfsdir = debugfs_create_dir(buf, cfg_to_wiphy(cfg_priv)->debugfsdir); fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg_priv->debugfsdir, (u16 *)&cfg_priv->profile->beacon_interval); if (!fd) { err = -ENOMEM; goto err_out; } fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg_priv->debugfsdir, (u8 *)&cfg_priv->profile->dtim_period); if (!fd) { err = -ENOMEM; goto err_out; } err_out: return err; } static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_priv *cfg_priv) { debugfs_remove_recursive(cfg_priv->debugfsdir); cfg_priv->debugfsdir = NULL; } static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_priv *cfg_priv) { s32 err = 0; set_bit(WL_STATUS_READY, &cfg_priv->status); brcmf_debugfs_add_netdev_params(cfg_priv); err = brcmf_config_dongle(cfg_priv); if (err) return err; brcmf_invoke_iscan(cfg_priv); return err; } static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_priv *cfg_priv) { /* * While going down, if associated with AP disassociate * from AP to save power */ if ((test_bit(WL_STATUS_CONNECTED, &cfg_priv->status) || test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) && test_bit(WL_STATUS_READY, &cfg_priv->status)) { WL_INFO("Disassociating from AP"); brcmf_link_down(cfg_priv); /* Make sure WPA_Supplicant receives all the event generated due to DISASSOC call to the fw to keep the state fw and WPA_Supplicant state consistent */ brcmf_delay(500); } set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); brcmf_term_iscan(cfg_priv); if (cfg_priv->scan_request) { cfg80211_scan_done(cfg_priv->scan_request, true); /* May need to perform this to cover rmmod */ /* wl_set_mpc(cfg_to_ndev(wl), 1); */ cfg_priv->scan_request = NULL; } clear_bit(WL_STATUS_READY, &cfg_priv->status); clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); brcmf_debugfs_remove_netdev(cfg_priv); return 0; } s32 brcmf_cfg80211_up(struct brcmf_cfg80211_dev *cfg_dev) { struct brcmf_cfg80211_priv *cfg_priv; s32 err = 0; cfg_priv = brcmf_priv_get(cfg_dev); mutex_lock(&cfg_priv->usr_sync); err = __brcmf_cfg80211_up(cfg_priv); mutex_unlock(&cfg_priv->usr_sync); return err; } s32 brcmf_cfg80211_down(struct brcmf_cfg80211_dev *cfg_dev) { struct brcmf_cfg80211_priv *cfg_priv; s32 err = 0; cfg_priv = brcmf_priv_get(cfg_dev); mutex_lock(&cfg_priv->usr_sync); err = __brcmf_cfg80211_down(cfg_priv); mutex_unlock(&cfg_priv->usr_sync); return err; } static __used s32 brcmf_add_ie(struct brcmf_cfg80211_priv *cfg_priv, u8 t, u8 l, u8 *v) { struct brcmf_cfg80211_ie *ie = &cfg_priv->ie; s32 err = 0; if (ie->offset + l + 2 > WL_TLV_INFO_MAX) { WL_ERR("ei crosses buffer boundary\n"); return -ENOSPC; } ie->buf[ie->offset] = t; ie->buf[ie->offset + 1] = l; memcpy(&ie->buf[ie->offset + 2], v, l); ie->offset += l + 2; return err; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c0000644000175000017500000004300012026211315025654 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ***** SDIO interface chip backplane handle functions ***** */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include "dhd_dbg.h" #include "sdio_host.h" #include "sdio_chip.h" /* chip core base & ramsize */ /* bcm4329 */ /* SDIO device core, ID 0x829 */ #define BCM4329_CORE_BUS_BASE 0x18011000 /* internal memory core, ID 0x80e */ #define BCM4329_CORE_SOCRAM_BASE 0x18003000 /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 #define BCM4329_RAMSIZE 0x48000 #define SBCOREREV(sbidh) \ ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ ((sbidh) & SSB_IDHIGH_RCLO)) /* SOC Interconnect types (aka chip types) */ #define SOCI_SB 0 #define SOCI_AI 1 /* EROM CompIdentB */ #define CIB_REV_MASK 0xff000000 #define CIB_REV_SHIFT 24 #define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) /* SDIO Pad drive strength to select value mappings */ struct sdiod_drive_str { u8 strength; /* Pad Drive Strength in mA */ u8 sel; /* Chip-specific select value */ }; /* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { {32, 0x6}, {26, 0x7}, {22, 0x4}, {16, 0x5}, {12, 0x2}, {8, 0x3}, {4, 0x0}, {0, 0x1} }; u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid) { u8 idx; for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) if (coreid == ci->c_inf[idx].id) return idx; return BRCMF_MAX_CORENUM; } static u32 brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u32 regdata; u8 idx; idx = brcmf_sdio_chip_getinfidx(ci, coreid); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbidhigh), NULL); return SBCOREREV(regdata); } static u32 brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u8 idx; idx = brcmf_sdio_chip_getinfidx(ci, coreid); return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT; } static bool brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u32 regdata; u8 idx; idx = brcmf_sdio_chip_getinfidx(ci, coreid); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), NULL); regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); return (SSB_TMSLOW_CLOCK == regdata); } static bool brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u32 regdata; u8 idx; bool ret; idx = brcmf_sdio_chip_getinfidx(ci, coreid); regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, NULL); ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, NULL); ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); return ret; } static void brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u32 regdata, base; u8 idx; idx = brcmf_sdio_chip_getinfidx(ci, coreid); base = ci->c_inf[idx].base; regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); if (regdata & SSB_TMSLOW_RESET) return; regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); if ((regdata & SSB_TMSLOW_CLOCK) != 0) { /* * set target reject and spin until busy is clear * (preserve core-specific bits) */ regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), regdata | SSB_TMSLOW_REJECT, NULL); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); udelay(1); SPINWAIT((brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatehigh), NULL) & SSB_TMSHIGH_BUSY), 100000); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatehigh), NULL); if (regdata & SSB_TMSHIGH_BUSY) brcmf_dbg(ERROR, "core state still busy\n"); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), NULL); if (regdata & SSB_IDLOW_INITIATOR) { regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbimstate), NULL); regdata |= SSB_IMSTATE_REJECT; brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), regdata, NULL); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbimstate), NULL); udelay(1); SPINWAIT((brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbimstate), NULL) & SSB_IMSTATE_BUSY), 100000); } /* set reset and reject while enabling the clocks */ regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), regdata, NULL); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); udelay(10); /* clear the initiator reject bit */ regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), NULL); if (regdata & SSB_IDLOW_INITIATOR) { regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbimstate), NULL); regdata &= ~SSB_IMSTATE_REJECT; brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), regdata, NULL); } } /* leave reset and reject asserted */ brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); udelay(1); } static void brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u8 idx; u32 regdata; idx = brcmf_sdio_chip_getinfidx(ci, coreid); /* if core is already in reset, just return */ regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, NULL); if ((regdata & BCMA_RESET_CTL_RESET) != 0) return; brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, 0, NULL); regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, NULL); udelay(10); brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, BCMA_RESET_CTL_RESET, NULL); udelay(1); } static void brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u32 regdata; u8 idx; idx = brcmf_sdio_chip_getinfidx(ci, coreid); /* * Must do the disable sequence first to work for * arbitrary current core state. */ brcmf_sdio_sb_coredisable(sdiodev, ci, coreid); /* * Now do the initialization sequence. * set reset while enabling the clock and * forcing them on throughout the core */ brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, NULL); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), NULL); udelay(1); /* clear any serror */ regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), NULL); if (regdata & SSB_TMSHIGH_SERR) brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), 0, NULL); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbimstate), NULL); if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbimstate), regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), NULL); /* clear reset and allow it to propagate throughout the core */ brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), NULL); udelay(1); /* leave clock enabled */ brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), SSB_TMSLOW_CLOCK, NULL); regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), NULL); udelay(1); } static void brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid) { u8 idx; u32 regdata; idx = brcmf_sdio_chip_getinfidx(ci, coreid); /* must disable first to work for arbitrary current core state */ brcmf_sdio_ai_coredisable(sdiodev, ci, coreid); /* now do initialization sequence */ brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, NULL); brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, 0, NULL); udelay(1); brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, BCMA_IOCTL_CLK, NULL); regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, NULL); udelay(1); } static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u32 regs) { u32 regdata; /* * Get CC core rev * Chipid is assume to be at offset 0 from regs arg * For different chiptypes or old sdio hosts w/o chipcommon, * other ways of recognition should be added here. */ ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; ci->c_inf[0].base = regs; regdata = brcmf_sdio_regrl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, chipid), NULL); ci->chip = regdata & CID_ID_MASK; ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); /* Address of cores for new chips should be added here */ switch (ci->chip) { case BCM43241_CHIP_ID: ci->c_inf[0].wrapbase = 0x18100000; ci->c_inf[0].cib = 0x2a084411; ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; ci->c_inf[1].base = 0x18002000; ci->c_inf[1].wrapbase = 0x18102000; ci->c_inf[1].cib = 0x0e004211; ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; ci->c_inf[2].base = 0x18004000; ci->c_inf[2].wrapbase = 0x18104000; ci->c_inf[2].cib = 0x14080401; ci->c_inf[3].id = BCMA_CORE_ARM_CM3; ci->c_inf[3].base = 0x18003000; ci->c_inf[3].wrapbase = 0x18103000; ci->c_inf[3].cib = 0x07004211; ci->ramsize = 0x90000; break; case BCM4329_CHIP_ID: ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; ci->c_inf[3].id = BCMA_CORE_ARM_CM3; ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; ci->ramsize = BCM4329_RAMSIZE; break; case BCM4330_CHIP_ID: ci->c_inf[0].wrapbase = 0x18100000; ci->c_inf[0].cib = 0x27004211; ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; ci->c_inf[1].base = 0x18002000; ci->c_inf[1].wrapbase = 0x18102000; ci->c_inf[1].cib = 0x07004211; ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; ci->c_inf[2].base = 0x18004000; ci->c_inf[2].wrapbase = 0x18104000; ci->c_inf[2].cib = 0x0d080401; ci->c_inf[3].id = BCMA_CORE_ARM_CM3; ci->c_inf[3].base = 0x18003000; ci->c_inf[3].wrapbase = 0x18103000; ci->c_inf[3].cib = 0x03004211; ci->ramsize = 0x48000; break; case BCM4334_CHIP_ID: ci->c_inf[0].wrapbase = 0x18100000; ci->c_inf[0].cib = 0x29004211; ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; ci->c_inf[1].base = 0x18002000; ci->c_inf[1].wrapbase = 0x18102000; ci->c_inf[1].cib = 0x0d004211; ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; ci->c_inf[2].base = 0x18004000; ci->c_inf[2].wrapbase = 0x18104000; ci->c_inf[2].cib = 0x13080401; ci->c_inf[3].id = BCMA_CORE_ARM_CM3; ci->c_inf[3].base = 0x18003000; ci->c_inf[3].wrapbase = 0x18103000; ci->c_inf[3].cib = 0x07004211; ci->ramsize = 0x80000; break; default: brcmf_dbg(ERROR, "chipid 0x%x is not supported\n", ci->chip); return -ENODEV; } switch (ci->socitype) { case SOCI_SB: ci->iscoreup = brcmf_sdio_sb_iscoreup; ci->corerev = brcmf_sdio_sb_corerev; ci->coredisable = brcmf_sdio_sb_coredisable; ci->resetcore = brcmf_sdio_sb_resetcore; break; case SOCI_AI: ci->iscoreup = brcmf_sdio_ai_iscoreup; ci->corerev = brcmf_sdio_ai_corerev; ci->coredisable = brcmf_sdio_ai_coredisable; ci->resetcore = brcmf_sdio_ai_resetcore; break; default: brcmf_dbg(ERROR, "socitype %u not supported\n", ci->socitype); return -ENODEV; } return 0; } static int brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) { int err = 0; u8 clkval, clkset; /* Try forcing SDIO core to do ALPAvail request only */ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); if (err) { brcmf_dbg(ERROR, "error writing for HT off\n"); return err; } /* If register supported, wait for ALPAvail and then force ALP */ /* This may take up to 15 milliseconds */ clkval = brcmf_sdio_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, NULL); if ((clkval & ~SBSDIO_AVBITS) != clkset) { brcmf_dbg(ERROR, "ChipClkCSR access: wrote 0x%02x read 0x%02x\n", clkset, clkval); return -EACCES; } SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)), PMU_MAX_TRANSITION_DLY); if (!SBSDIO_ALPAV(clkval)) { brcmf_dbg(ERROR, "timeout on ALPAV wait, clkval 0x%02x\n", clkval); return -EBUSY; } clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); udelay(65); /* Also, disable the extra SDIO pull-ups */ brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); return 0; } static void brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { u32 base = ci->c_inf[0].base; /* get chipcommon rev */ ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); /* get chipcommon capabilites */ ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev, CORE_CC_REG(base, capabilities), NULL); /* get pmu caps & rev */ if (ci->c_inf[0].caps & CC_CAP_PMU) { ci->pmucaps = brcmf_sdio_regrl(sdiodev, CORE_CC_REG(base, pmucapabilities), NULL); ci->pmurev = ci->pmucaps & PCAP_REV_MASK; } ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id); brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", ci->c_inf[0].rev, ci->pmurev, ci->c_inf[1].rev, ci->c_inf[1].id); /* * Make sure any on-chip ARM is off (in case strapping is wrong), * or downloaded code was already running. */ ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3); } int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, struct chip_info **ci_ptr, u32 regs) { int ret; struct chip_info *ci; brcmf_dbg(TRACE, "Enter\n"); /* alloc chip_info_t */ ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); if (!ci) return -ENOMEM; ret = brcmf_sdio_chip_buscoreprep(sdiodev); if (ret != 0) goto err; ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs); if (ret != 0) goto err; brcmf_sdio_chip_buscoresetup(sdiodev, ci); brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), 0, NULL); brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), 0, NULL); *ci_ptr = ci; return 0; err: kfree(ci); return ret; } void brcmf_sdio_chip_detach(struct chip_info **ci_ptr) { brcmf_dbg(TRACE, "Enter\n"); kfree(*ci_ptr); *ci_ptr = NULL; } static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) { const char *fmt; fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; snprintf(buf, len, fmt, chipid); return buf; } void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u32 drivestrength) { struct sdiod_drive_str *str_tab = NULL; u32 str_mask = 0; u32 str_shift = 0; char chn[8]; u32 base = ci->c_inf[0].base; if (!(ci->c_inf[0].caps & CC_CAP_PMU)) return; switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): str_tab = (struct sdiod_drive_str *)&sdiod_drvstr_tab1_1v8; str_mask = 0x00003800; str_shift = 11; break; default: brcmf_dbg(ERROR, "No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", brcmf_sdio_chip_name(ci->chip, chn, 8), ci->chiprev, ci->pmurev); break; } if (str_tab != NULL) { u32 drivestrength_sel = 0; u32 cc_data_temp; int i; for (i = 0; str_tab[i].strength != 0; i++) { if (drivestrength >= str_tab[i].strength) { drivestrength_sel = str_tab[i].sel; break; } } brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr), 1, NULL); cc_data_temp = brcmf_sdio_regrl(sdiodev, CORE_CC_REG(base, chipcontrol_addr), NULL); cc_data_temp &= ~str_mask; drivestrength_sel <<= str_shift; cc_data_temp |= drivestrength_sel; brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr), cc_data_temp, NULL); brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n", drivestrength, cc_data_temp); } } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c0000644000175000017500000006142112026211315026031 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include "dhd.h" #include "dhd_bus.h" #include "dhd_proto.h" #include "dhd_dbg.h" #define BRCM_OUI "\x00\x10\x18" #define DOT11_OUI_LEN 3 #define BCMILCP_BCM_SUBTYPE_EVENT 1 #define PKTFILTER_BUF_SIZE 2048 #define BRCMF_ARPOL_MODE 0xb /* agent|snoop|peer_autoreply */ #define MSGTRACE_VERSION 1 #define BRCMF_PKT_FILTER_FIXED_LEN offsetof(struct brcmf_pkt_filter_le, u) #define BRCMF_PKT_FILTER_PATTERN_FIXED_LEN \ offsetof(struct brcmf_pkt_filter_pattern_le, mask_and_pattern) #ifdef DEBUG static const char brcmf_version[] = "Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on " __DATE__ " at " __TIME__; #else static const char brcmf_version[] = "Dongle Host Driver, version " BRCMF_VERSION_STR; #endif /* Message trace header */ struct msgtrace_hdr { u8 version; u8 spare; __be16 len; /* Len of the trace */ __be32 seqnum; /* Sequence number of message. Useful * if the messsage has been lost * because of DMA error or a bus reset * (ex: SDIO Func2) */ __be32 discarded_bytes; /* Number of discarded bytes because of trace overflow */ __be32 discarded_printf; /* Number of discarded printf because of trace overflow */ } __packed; uint brcmf_c_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen) { uint len; len = strlen(name) + 1; if ((len + datalen) > buflen) return 0; strncpy(buf, name, buflen); /* append data onto the end of the name string */ memcpy(&buf[len], data, datalen); len += datalen; return len; } bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec) { struct sk_buff *p; int eprec = -1; /* precedence to evict from */ bool discard_oldest; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; /* Fast case, precedence queue is not full and we are also not * exceeding total queue length */ if (!pktq_pfull(q, prec) && !pktq_full(q)) { brcmu_pktq_penq(q, prec, pkt); return true; } /* Determine precedence from which to evict packet, if any */ if (pktq_pfull(q, prec)) eprec = prec; else if (pktq_full(q)) { p = brcmu_pktq_peek_tail(q, &eprec); if (eprec > prec) return false; } /* Evict if needed */ if (eprec >= 0) { /* Detect queueing to unconfigured precedence */ discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec); if (eprec == prec && !discard_oldest) return false; /* refuse newer (incoming) packet */ /* Evict packet according to discard policy */ p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : brcmu_pktq_pdeq_tail(q, eprec); if (p == NULL) brcmf_dbg(ERROR, "brcmu_pktq_penq() failed, oldest %d\n", discard_oldest); brcmu_pkt_buf_free_skb(p); } /* Enqueue */ p = brcmu_pktq_penq(q, prec, pkt); if (p == NULL) brcmf_dbg(ERROR, "brcmu_pktq_penq() failed\n"); return p != NULL; } #ifdef DEBUG static void brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data) { uint i, status, reason; bool group = false, flush_txq = false, link = false; char *auth_str, *event_name; unsigned char *buf; char err_msg[256], eabuf[ETHER_ADDR_STR_LEN]; static struct { uint event; char *event_name; } event_names[] = { { BRCMF_E_SET_SSID, "SET_SSID"}, { BRCMF_E_JOIN, "JOIN"}, { BRCMF_E_START, "START"}, { BRCMF_E_AUTH, "AUTH"}, { BRCMF_E_AUTH_IND, "AUTH_IND"}, { BRCMF_E_DEAUTH, "DEAUTH"}, { BRCMF_E_DEAUTH_IND, "DEAUTH_IND"}, { BRCMF_E_ASSOC, "ASSOC"}, { BRCMF_E_ASSOC_IND, "ASSOC_IND"}, { BRCMF_E_REASSOC, "REASSOC"}, { BRCMF_E_REASSOC_IND, "REASSOC_IND"}, { BRCMF_E_DISASSOC, "DISASSOC"}, { BRCMF_E_DISASSOC_IND, "DISASSOC_IND"}, { BRCMF_E_QUIET_START, "START_QUIET"}, { BRCMF_E_QUIET_END, "END_QUIET"}, { BRCMF_E_BEACON_RX, "BEACON_RX"}, { BRCMF_E_LINK, "LINK"}, { BRCMF_E_MIC_ERROR, "MIC_ERROR"}, { BRCMF_E_NDIS_LINK, "NDIS_LINK"}, { BRCMF_E_ROAM, "ROAM"}, { BRCMF_E_TXFAIL, "TXFAIL"}, { BRCMF_E_PMKID_CACHE, "PMKID_CACHE"}, { BRCMF_E_RETROGRADE_TSF, "RETROGRADE_TSF"}, { BRCMF_E_PRUNE, "PRUNE"}, { BRCMF_E_AUTOAUTH, "AUTOAUTH"}, { BRCMF_E_EAPOL_MSG, "EAPOL_MSG"}, { BRCMF_E_SCAN_COMPLETE, "SCAN_COMPLETE"}, { BRCMF_E_ADDTS_IND, "ADDTS_IND"}, { BRCMF_E_DELTS_IND, "DELTS_IND"}, { BRCMF_E_BCNSENT_IND, "BCNSENT_IND"}, { BRCMF_E_BCNRX_MSG, "BCNRX_MSG"}, { BRCMF_E_BCNLOST_MSG, "BCNLOST_MSG"}, { BRCMF_E_ROAM_PREP, "ROAM_PREP"}, { BRCMF_E_PFN_NET_FOUND, "PNO_NET_FOUND"}, { BRCMF_E_PFN_NET_LOST, "PNO_NET_LOST"}, { BRCMF_E_RESET_COMPLETE, "RESET_COMPLETE"}, { BRCMF_E_JOIN_START, "JOIN_START"}, { BRCMF_E_ROAM_START, "ROAM_START"}, { BRCMF_E_ASSOC_START, "ASSOC_START"}, { BRCMF_E_IBSS_ASSOC, "IBSS_ASSOC"}, { BRCMF_E_RADIO, "RADIO"}, { BRCMF_E_PSM_WATCHDOG, "PSM_WATCHDOG"}, { BRCMF_E_PROBREQ_MSG, "PROBREQ_MSG"}, { BRCMF_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"}, { BRCMF_E_PSK_SUP, "PSK_SUP"}, { BRCMF_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"}, { BRCMF_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"}, { BRCMF_E_ICV_ERROR, "ICV_ERROR"}, { BRCMF_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"}, { BRCMF_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"}, { BRCMF_E_TRACE, "TRACE"}, { BRCMF_E_ACTION_FRAME, "ACTION FRAME"}, { BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, { BRCMF_E_IF, "IF"}, { BRCMF_E_RSSI, "RSSI"}, { BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}, { BRCMF_E_ESCAN_RESULT, "ESCAN_RESULT"} }; uint event_type, flags, auth_type, datalen; static u32 seqnum_prev; struct msgtrace_hdr hdr; u32 nblost; char *s, *p; event_type = be32_to_cpu(event->event_type); flags = be16_to_cpu(event->flags); status = be32_to_cpu(event->status); reason = be32_to_cpu(event->reason); auth_type = be32_to_cpu(event->auth_type); datalen = be32_to_cpu(event->datalen); /* debug dump of event messages */ sprintf(eabuf, "%pM", event->addr); event_name = "UNKNOWN"; for (i = 0; i < ARRAY_SIZE(event_names); i++) { if (event_names[i].event == event_type) event_name = event_names[i].event_name; } brcmf_dbg(EVENT, "EVENT: %s, event ID = %d\n", event_name, event_type); brcmf_dbg(EVENT, "flags 0x%04x, status %d, reason %d, auth_type %d MAC %s\n", flags, status, reason, auth_type, eabuf); if (flags & BRCMF_EVENT_MSG_LINK) link = true; if (flags & BRCMF_EVENT_MSG_GROUP) group = true; if (flags & BRCMF_EVENT_MSG_FLUSHTXQ) flush_txq = true; switch (event_type) { case BRCMF_E_START: case BRCMF_E_DEAUTH: case BRCMF_E_DISASSOC: brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf); break; case BRCMF_E_ASSOC_IND: case BRCMF_E_REASSOC_IND: brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf); break; case BRCMF_E_ASSOC: case BRCMF_E_REASSOC: if (status == BRCMF_E_STATUS_SUCCESS) brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf); else if (status == BRCMF_E_STATUS_TIMEOUT) brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf); else if (status == BRCMF_E_STATUS_FAIL) brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, FAILURE, reason %d\n", event_name, eabuf, (int)reason); else brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, unexpected status %d\n", event_name, eabuf, (int)status); break; case BRCMF_E_DEAUTH_IND: case BRCMF_E_DISASSOC_IND: brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason); break; case BRCMF_E_AUTH: case BRCMF_E_AUTH_IND: if (auth_type == WLAN_AUTH_OPEN) auth_str = "Open System"; else if (auth_type == WLAN_AUTH_SHARED_KEY) auth_str = "Shared Key"; else { sprintf(err_msg, "AUTH unknown: %d", (int)auth_type); auth_str = err_msg; } if (event_type == BRCMF_E_AUTH_IND) brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str); else if (status == BRCMF_E_STATUS_SUCCESS) brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, SUCCESS\n", event_name, eabuf, auth_str); else if (status == BRCMF_E_STATUS_TIMEOUT) brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, TIMEOUT\n", event_name, eabuf, auth_str); else if (status == BRCMF_E_STATUS_FAIL) { brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n", event_name, eabuf, auth_str, (int)reason); } break; case BRCMF_E_JOIN: case BRCMF_E_ROAM: case BRCMF_E_SET_SSID: if (status == BRCMF_E_STATUS_SUCCESS) brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf); else if (status == BRCMF_E_STATUS_FAIL) brcmf_dbg(EVENT, "MACEVENT: %s, failed\n", event_name); else if (status == BRCMF_E_STATUS_NO_NETWORKS) brcmf_dbg(EVENT, "MACEVENT: %s, no networks found\n", event_name); else brcmf_dbg(EVENT, "MACEVENT: %s, unexpected status %d\n", event_name, (int)status); break; case BRCMF_E_BEACON_RX: if (status == BRCMF_E_STATUS_SUCCESS) brcmf_dbg(EVENT, "MACEVENT: %s, SUCCESS\n", event_name); else if (status == BRCMF_E_STATUS_FAIL) brcmf_dbg(EVENT, "MACEVENT: %s, FAIL\n", event_name); else brcmf_dbg(EVENT, "MACEVENT: %s, status %d\n", event_name, status); break; case BRCMF_E_LINK: brcmf_dbg(EVENT, "MACEVENT: %s %s\n", event_name, link ? "UP" : "DOWN"); break; case BRCMF_E_MIC_ERROR: brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, Group %d, Flush %d\n", event_name, eabuf, group, flush_txq); break; case BRCMF_E_ICV_ERROR: case BRCMF_E_UNICAST_DECODE_ERROR: case BRCMF_E_MULTICAST_DECODE_ERROR: brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf); break; case BRCMF_E_TXFAIL: brcmf_dbg(EVENT, "MACEVENT: %s, RA %s\n", event_name, eabuf); break; case BRCMF_E_SCAN_COMPLETE: case BRCMF_E_PMKID_CACHE: brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name); break; case BRCMF_E_ESCAN_RESULT: brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name); datalen = 0; break; case BRCMF_E_PFN_NET_FOUND: case BRCMF_E_PFN_NET_LOST: case BRCMF_E_PFN_SCAN_COMPLETE: brcmf_dbg(EVENT, "PNOEVENT: %s\n", event_name); break; case BRCMF_E_PSK_SUP: case BRCMF_E_PRUNE: brcmf_dbg(EVENT, "MACEVENT: %s, status %d, reason %d\n", event_name, (int)status, (int)reason); break; case BRCMF_E_TRACE: buf = (unsigned char *) event_data; memcpy(&hdr, buf, sizeof(struct msgtrace_hdr)); if (hdr.version != MSGTRACE_VERSION) { brcmf_dbg(ERROR, "MACEVENT: %s [unsupported version --> brcmf" " version:%d dongle version:%d]\n", event_name, MSGTRACE_VERSION, hdr.version); /* Reset datalen to avoid display below */ datalen = 0; break; } /* There are 2 bytes available at the end of data */ *(buf + sizeof(struct msgtrace_hdr) + be16_to_cpu(hdr.len)) = '\0'; if (be32_to_cpu(hdr.discarded_bytes) || be32_to_cpu(hdr.discarded_printf)) brcmf_dbg(ERROR, "WLC_E_TRACE: [Discarded traces in dongle -->" " discarded_bytes %d discarded_printf %d]\n", be32_to_cpu(hdr.discarded_bytes), be32_to_cpu(hdr.discarded_printf)); nblost = be32_to_cpu(hdr.seqnum) - seqnum_prev - 1; if (nblost > 0) brcmf_dbg(ERROR, "WLC_E_TRACE: [Event lost --> seqnum " " %d nblost %d\n", be32_to_cpu(hdr.seqnum), nblost); seqnum_prev = be32_to_cpu(hdr.seqnum); /* Display the trace buffer. Advance from \n to \n to * avoid display big * printf (issue with Linux printk ) */ p = (char *)&buf[sizeof(struct msgtrace_hdr)]; while ((s = strstr(p, "\n")) != NULL) { *s = '\0'; pr_debug("%s\n", p); p = s + 1; } pr_debug("%s\n", p); /* Reset datalen to avoid display below */ datalen = 0; break; case BRCMF_E_RSSI: brcmf_dbg(EVENT, "MACEVENT: %s %d\n", event_name, be32_to_cpu(*((__be32 *)event_data))); break; default: brcmf_dbg(EVENT, "MACEVENT: %s %d, MAC %s, status %d, reason %d, " "auth %d\n", event_name, event_type, eabuf, (int)status, (int)reason, (int)auth_type); break; } /* show any appended data */ if (datalen) { buf = (unsigned char *) event_data; brcmf_dbg(EVENT, " data (%d) : ", datalen); for (i = 0; i < datalen; i++) brcmf_dbg(EVENT, " 0x%02x ", *buf++); brcmf_dbg(EVENT, "\n"); } } #endif /* DEBUG */ int brcmf_c_host_event(struct brcmf_pub *drvr, int *ifidx, void *pktdata, struct brcmf_event_msg *event, void **data_ptr) { /* check whether packet is a BRCM event pkt */ struct brcmf_event *pvt_data = (struct brcmf_event *) pktdata; struct brcmf_if_event *ifevent; char *event_data; u32 type, status; u16 flags; int evlen; if (memcmp(BRCM_OUI, &pvt_data->hdr.oui[0], DOT11_OUI_LEN)) { brcmf_dbg(ERROR, "mismatched OUI, bailing\n"); return -EBADE; } /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */ if (get_unaligned_be16(&pvt_data->hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) { brcmf_dbg(ERROR, "mismatched subtype, bailing\n"); return -EBADE; } *data_ptr = &pvt_data[1]; event_data = *data_ptr; /* memcpy since BRCM event pkt may be unaligned. */ memcpy(event, &pvt_data->msg, sizeof(struct brcmf_event_msg)); type = get_unaligned_be32(&event->event_type); flags = get_unaligned_be16(&event->flags); status = get_unaligned_be32(&event->status); evlen = get_unaligned_be32(&event->datalen) + sizeof(struct brcmf_event); switch (type) { case BRCMF_E_IF: ifevent = (struct brcmf_if_event *) event_data; brcmf_dbg(TRACE, "if event\n"); if (ifevent->ifidx > 0 && ifevent->ifidx < BRCMF_MAX_IFS) { if (ifevent->action == BRCMF_E_IF_ADD) brcmf_add_if(drvr->dev, ifevent->ifidx, event->ifname, pvt_data->eth.h_dest); else brcmf_del_if(drvr, ifevent->ifidx); } else { brcmf_dbg(ERROR, "Invalid ifidx %d for %s\n", ifevent->ifidx, event->ifname); } /* send up the if event: btamp user needs it */ *ifidx = brcmf_ifname2idx(drvr, event->ifname); break; /* These are what external supplicant/authenticator wants */ case BRCMF_E_LINK: case BRCMF_E_ASSOC_IND: case BRCMF_E_REASSOC_IND: case BRCMF_E_DISASSOC_IND: case BRCMF_E_MIC_ERROR: default: /* Fall through: this should get _everything_ */ *ifidx = brcmf_ifname2idx(drvr, event->ifname); brcmf_dbg(TRACE, "MAC event %d, flags %x, status %x\n", type, flags, status); /* put it back to BRCMF_E_NDIS_LINK */ if (type == BRCMF_E_NDIS_LINK) { u32 temp1; __be32 temp2; temp1 = get_unaligned_be32(&event->event_type); brcmf_dbg(TRACE, "Converted to WLC_E_LINK type %d\n", temp1); temp2 = cpu_to_be32(BRCMF_E_NDIS_LINK); memcpy((void *)(&pvt_data->msg.event_type), &temp2, sizeof(pvt_data->msg.event_type)); } break; } #ifdef DEBUG brcmf_c_show_host_event(event, event_data); #endif /* DEBUG */ return 0; } /* Convert user's input in hex pattern to byte-size mask */ static int brcmf_c_pattern_atoh(char *src, char *dst) { int i; if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) { brcmf_dbg(ERROR, "Mask invalid format. Needs to start with 0x\n"); return -EINVAL; } src = src + 2; /* Skip past 0x */ if (strlen(src) % 2 != 0) { brcmf_dbg(ERROR, "Mask invalid format. Length must be even.\n"); return -EINVAL; } for (i = 0; *src != '\0'; i++) { unsigned long res; char num[3]; strncpy(num, src, 2); num[2] = '\0'; if (kstrtoul(num, 16, &res)) return -EINVAL; dst[i] = (u8)res; src += 2; } return i; } void brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg, int enable, int master_mode) { unsigned long res; char *argv[8]; int i = 0; const char *str; int buf_len; int str_len; char *arg_save = NULL, *arg_org = NULL; int rc; char buf[128]; struct brcmf_pkt_filter_enable_le enable_parm; struct brcmf_pkt_filter_enable_le *pkt_filterp; __le32 mmode_le; arg_save = kmalloc(strlen(arg) + 1, GFP_ATOMIC); if (!arg_save) goto fail; arg_org = arg_save; memcpy(arg_save, arg, strlen(arg) + 1); argv[i] = strsep(&arg_save, " "); i = 0; if (NULL == argv[i]) { brcmf_dbg(ERROR, "No args provided\n"); goto fail; } str = "pkt_filter_enable"; str_len = strlen(str); strncpy(buf, str, str_len); buf[str_len] = '\0'; buf_len = str_len + 1; pkt_filterp = (struct brcmf_pkt_filter_enable_le *) (buf + str_len + 1); /* Parse packet filter id. */ enable_parm.id = 0; if (!kstrtoul(argv[i], 0, &res)) enable_parm.id = cpu_to_le32((u32)res); /* Parse enable/disable value. */ enable_parm.enable = cpu_to_le32(enable); buf_len += sizeof(enable_parm); memcpy((char *)pkt_filterp, &enable_parm, sizeof(enable_parm)); /* Enable/disable the specified filter. */ rc = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, buf, buf_len); rc = rc >= 0 ? 0 : rc; if (rc) brcmf_dbg(TRACE, "failed to add pktfilter %s, retcode = %d\n", arg, rc); else brcmf_dbg(TRACE, "successfully added pktfilter %s\n", arg); /* Contorl the master mode */ mmode_le = cpu_to_le32(master_mode); brcmf_c_mkiovar("pkt_filter_mode", (char *)&mmode_le, 4, buf, sizeof(buf)); rc = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, buf, sizeof(buf)); rc = rc >= 0 ? 0 : rc; if (rc) brcmf_dbg(TRACE, "failed to add pktfilter %s, retcode = %d\n", arg, rc); fail: kfree(arg_org); } void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg) { const char *str; struct brcmf_pkt_filter_le pkt_filter; struct brcmf_pkt_filter_le *pkt_filterp; unsigned long res; int buf_len; int str_len; int rc; u32 mask_size; u32 pattern_size; char *argv[8], *buf = NULL; int i = 0; char *arg_save = NULL, *arg_org = NULL; arg_save = kstrdup(arg, GFP_ATOMIC); if (!arg_save) goto fail; arg_org = arg_save; buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC); if (!buf) goto fail; argv[i] = strsep(&arg_save, " "); while (argv[i++]) argv[i] = strsep(&arg_save, " "); i = 0; if (NULL == argv[i]) { brcmf_dbg(ERROR, "No args provided\n"); goto fail; } str = "pkt_filter_add"; strcpy(buf, str); str_len = strlen(str); buf_len = str_len + 1; pkt_filterp = (struct brcmf_pkt_filter_le *) (buf + str_len + 1); /* Parse packet filter id. */ pkt_filter.id = 0; if (!kstrtoul(argv[i], 0, &res)) pkt_filter.id = cpu_to_le32((u32)res); if (NULL == argv[++i]) { brcmf_dbg(ERROR, "Polarity not provided\n"); goto fail; } /* Parse filter polarity. */ pkt_filter.negate_match = 0; if (!kstrtoul(argv[i], 0, &res)) pkt_filter.negate_match = cpu_to_le32((u32)res); if (NULL == argv[++i]) { brcmf_dbg(ERROR, "Filter type not provided\n"); goto fail; } /* Parse filter type. */ pkt_filter.type = 0; if (!kstrtoul(argv[i], 0, &res)) pkt_filter.type = cpu_to_le32((u32)res); if (NULL == argv[++i]) { brcmf_dbg(ERROR, "Offset not provided\n"); goto fail; } /* Parse pattern filter offset. */ pkt_filter.u.pattern.offset = 0; if (!kstrtoul(argv[i], 0, &res)) pkt_filter.u.pattern.offset = cpu_to_le32((u32)res); if (NULL == argv[++i]) { brcmf_dbg(ERROR, "Bitmask not provided\n"); goto fail; } /* Parse pattern filter mask. */ mask_size = brcmf_c_pattern_atoh (argv[i], (char *)pkt_filterp->u.pattern.mask_and_pattern); if (NULL == argv[++i]) { brcmf_dbg(ERROR, "Pattern not provided\n"); goto fail; } /* Parse pattern filter pattern. */ pattern_size = brcmf_c_pattern_atoh(argv[i], (char *)&pkt_filterp->u.pattern. mask_and_pattern[mask_size]); if (mask_size != pattern_size) { brcmf_dbg(ERROR, "Mask and pattern not the same size\n"); goto fail; } pkt_filter.u.pattern.size_bytes = cpu_to_le32(mask_size); buf_len += BRCMF_PKT_FILTER_FIXED_LEN; buf_len += (BRCMF_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); /* Keep-alive attributes are set in local * variable (keep_alive_pkt), and ** then memcpy'ed into buffer (keep_alive_pktp) since there is no ** guarantee that the buffer is properly aligned. */ memcpy((char *)pkt_filterp, &pkt_filter, BRCMF_PKT_FILTER_FIXED_LEN + BRCMF_PKT_FILTER_PATTERN_FIXED_LEN); rc = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, buf, buf_len); rc = rc >= 0 ? 0 : rc; if (rc) brcmf_dbg(TRACE, "failed to add pktfilter %s, retcode = %d\n", arg, rc); else brcmf_dbg(TRACE, "successfully added pktfilter %s\n", arg); fail: kfree(arg_org); kfree(buf); } static void brcmf_c_arp_offload_set(struct brcmf_pub *drvr, int arp_mode) { char iovbuf[32]; int retcode; __le32 arp_mode_le; arp_mode_le = cpu_to_le32(arp_mode); brcmf_c_mkiovar("arp_ol", (char *)&arp_mode_le, 4, iovbuf, sizeof(iovbuf)); retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); retcode = retcode >= 0 ? 0 : retcode; if (retcode) brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, retcode = %d\n", arp_mode, retcode); else brcmf_dbg(TRACE, "successfully set ARP offload mode to 0x%x\n", arp_mode); } static void brcmf_c_arp_offload_enable(struct brcmf_pub *drvr, int arp_enable) { char iovbuf[32]; int retcode; __le32 arp_enable_le; arp_enable_le = cpu_to_le32(arp_enable); brcmf_c_mkiovar("arpoe", (char *)&arp_enable_le, 4, iovbuf, sizeof(iovbuf)); retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); retcode = retcode >= 0 ? 0 : retcode; if (retcode) brcmf_dbg(TRACE, "failed to enable ARP offload to %d, retcode = %d\n", arp_enable, retcode); else brcmf_dbg(TRACE, "successfully enabled ARP offload to %d\n", arp_enable); } int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr) { char iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ char buf[128], *ptr; __le32 roaming_le = cpu_to_le32(1); __le32 bcn_timeout_le = cpu_to_le32(3); __le32 scan_assoc_time_le = cpu_to_le32(40); __le32 scan_unassoc_time_le = cpu_to_le32(40); int i; struct brcmf_bus_dcmd *cmdlst; struct list_head *cur, *q; mutex_lock(&drvr->proto_block); /* Set Country code */ if (drvr->country_code[0] != 0) { if (brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_COUNTRY, drvr->country_code, sizeof(drvr->country_code)) < 0) brcmf_dbg(ERROR, "country code setting failed\n"); } /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); ptr = buf; brcmf_c_mkiovar("ver", NULL, 0, buf, sizeof(buf)); brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR, buf, sizeof(buf)); strsep(&ptr, "\n"); /* Print fw version info */ brcmf_dbg(ERROR, "Firmware version = %s\n", buf); /* Setup timeout if Beacons are lost and roam is off to report link down */ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout_le, 4, iovbuf, sizeof(iovbuf)); brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); /* Enable/Disable build-in roaming to allowed ext supplicant to take of romaing */ brcmf_c_mkiovar("roam_off", (char *)&roaming_le, 4, iovbuf, sizeof(iovbuf)); brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); /* Setup event_msgs */ brcmf_c_mkiovar("event_msgs", drvr->eventmask, BRCMF_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time_le, sizeof(scan_assoc_time_le)); brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time_le, sizeof(scan_unassoc_time_le)); /* Set and enable ARP offload feature */ brcmf_c_arp_offload_set(drvr, BRCMF_ARPOL_MODE); brcmf_c_arp_offload_enable(drvr, true); /* Set up pkt filter */ for (i = 0; i < drvr->pktfilter_count; i++) { brcmf_c_pktfilter_offload_set(drvr, drvr->pktfilter[i]); brcmf_c_pktfilter_offload_enable(drvr, drvr->pktfilter[i], 0, true); } /* set bus specific command if there is any */ list_for_each_safe(cur, q, &drvr->bus_if->dcmd_list) { cmdlst = list_entry(cur, struct brcmf_bus_dcmd, list); if (cmdlst->name && cmdlst->param && cmdlst->param_len) { brcmf_c_mkiovar(cmdlst->name, cmdlst->param, cmdlst->param_len, iovbuf, sizeof(iovbuf)); brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); } list_del(cur); kfree(cmdlst); } mutex_unlock(&drvr->proto_block); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c0000644000175000017500000007345712026211315025714 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhd.h" #include "dhd_bus.h" #include "dhd_proto.h" #include "dhd_dbg.h" #include "wl_cfg80211.h" MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac driver."); MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac cards"); MODULE_LICENSE("Dual BSD/GPL"); /* Interface control information */ struct brcmf_if { struct brcmf_pub *drvr; /* back pointer to brcmf_pub */ /* OS/stack specifics */ struct net_device *ndev; struct net_device_stats stats; int idx; /* iface idx in dongle */ u8 mac_addr[ETH_ALEN]; /* assigned MAC address */ }; /* Error bits */ int brcmf_msg_level = BRCMF_ERROR_VAL; module_param(brcmf_msg_level, int, 0); int brcmf_ifname2idx(struct brcmf_pub *drvr, char *name) { int i = BRCMF_MAX_IFS; struct brcmf_if *ifp; if (name == NULL || *name == '\0') return 0; while (--i > 0) { ifp = drvr->iflist[i]; if (ifp && !strncmp(ifp->ndev->name, name, IFNAMSIZ)) break; } brcmf_dbg(TRACE, "return idx %d for \"%s\"\n", i, name); return i; /* default - the primary interface */ } char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) { if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { brcmf_dbg(ERROR, "ifidx %d out of range\n", ifidx); return ""; } if (drvr->iflist[ifidx] == NULL) { brcmf_dbg(ERROR, "null i/f %d\n", ifidx); return ""; } if (drvr->iflist[ifidx]->ndev) return drvr->iflist[ifidx]->ndev->name; return ""; } static void _brcmf_set_multicast_list(struct work_struct *work) { struct net_device *ndev; struct netdev_hw_addr *ha; u32 dcmd_value, cnt; __le32 cnt_le; __le32 dcmd_le_value; struct brcmf_dcmd dcmd; char *buf, *bufp; uint buflen; int ret; struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, multicast_work); ndev = drvr->iflist[0]->ndev; cnt = netdev_mc_count(ndev); /* Determine initial value of allmulti flag */ dcmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; /* Send down the multicast list first. */ buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETH_ALEN); bufp = buf = kmalloc(buflen, GFP_ATOMIC); if (!bufp) return; strcpy(bufp, "mcast_list"); bufp += strlen("mcast_list") + 1; cnt_le = cpu_to_le32(cnt); memcpy(bufp, &cnt_le, sizeof(cnt)); bufp += sizeof(cnt_le); netdev_for_each_mc_addr(ha, ndev) { if (!cnt) break; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(bufp, ha->addr, ETH_ALEN); #else memcpy(bufp, ha->dmi_addr, ETH_ALEN); #endif bufp += ETH_ALEN; cnt--; } memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = BRCMF_C_SET_VAR; dcmd.buf = buf; dcmd.len = buflen; dcmd.set = true; ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); if (ret < 0) { brcmf_dbg(ERROR, "%s: set mcast_list failed, cnt %d\n", brcmf_ifname(drvr, 0), cnt); dcmd_value = cnt ? true : dcmd_value; } kfree(buf); /* Now send the allmulti setting. This is based on the setting in the * net_device flags, but might be modified above to be turned on if we * were trying to set some addresses and dongle rejected it... */ buflen = sizeof("allmulti") + sizeof(dcmd_value); buf = kmalloc(buflen, GFP_ATOMIC); if (!buf) return; dcmd_le_value = cpu_to_le32(dcmd_value); if (!brcmf_c_mkiovar ("allmulti", (void *)&dcmd_le_value, sizeof(dcmd_le_value), buf, buflen)) { brcmf_dbg(ERROR, "%s: mkiovar failed for allmulti, datalen %d buflen %u\n", brcmf_ifname(drvr, 0), (int)sizeof(dcmd_value), buflen); kfree(buf); return; } memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = BRCMF_C_SET_VAR; dcmd.buf = buf; dcmd.len = buflen; dcmd.set = true; ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); if (ret < 0) { brcmf_dbg(ERROR, "%s: set allmulti %d failed\n", brcmf_ifname(drvr, 0), le32_to_cpu(dcmd_le_value)); } kfree(buf); /* Finally, pick up the PROMISC flag as well, like the NIC driver does */ dcmd_value = (ndev->flags & IFF_PROMISC) ? true : false; dcmd_le_value = cpu_to_le32(dcmd_value); memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = BRCMF_C_SET_PROMISC; dcmd.buf = &dcmd_le_value; dcmd.len = sizeof(dcmd_le_value); dcmd.set = true; ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); if (ret < 0) { brcmf_dbg(ERROR, "%s: set promisc %d failed\n", brcmf_ifname(drvr, 0), le32_to_cpu(dcmd_le_value)); } } static void _brcmf_set_mac_address(struct work_struct *work) { char buf[32]; struct brcmf_dcmd dcmd; int ret; struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, setmacaddr_work); brcmf_dbg(TRACE, "enter\n"); if (!brcmf_c_mkiovar("cur_etheraddr", (char *)drvr->macvalue, ETH_ALEN, buf, 32)) { brcmf_dbg(ERROR, "%s: mkiovar failed for cur_etheraddr\n", brcmf_ifname(drvr, 0)); return; } memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = BRCMF_C_SET_VAR; dcmd.buf = buf; dcmd.len = 32; dcmd.set = true; ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); if (ret < 0) brcmf_dbg(ERROR, "%s: set cur_etheraddr failed\n", brcmf_ifname(drvr, 0)); else memcpy(drvr->iflist[0]->ndev->dev_addr, drvr->macvalue, ETH_ALEN); return; } static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; struct sockaddr *sa = (struct sockaddr *)addr; memcpy(&drvr->macvalue, sa->sa_data, ETH_ALEN); schedule_work(&drvr->setmacaddr_work); return 0; } static void brcmf_netdev_set_multicast_list(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; schedule_work(&drvr->multicast_work); } int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx, struct sk_buff *pktbuf) { /* Reject if down */ if (!drvr->bus_if->drvr_up || (drvr->bus_if->state == BRCMF_BUS_DOWN)) return -ENODEV; /* Update multicast statistic */ if (pktbuf->len >= ETH_ALEN) { u8 *pktdata = (u8 *) (pktbuf->data); struct ethhdr *eh = (struct ethhdr *)pktdata; if (is_multicast_ether_addr(eh->h_dest)) drvr->tx_multicast++; if (ntohs(eh->h_proto) == ETH_P_PAE) atomic_inc(&drvr->pend_8021x_cnt); } /* If the protocol uses a data header, apply it */ brcmf_proto_hdrpush(drvr, ifidx, pktbuf); /* Use bus module to send data frame */ return drvr->bus_if->brcmf_bus_txdata(drvr->dev, pktbuf); } static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) { int ret; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; brcmf_dbg(TRACE, "Enter\n"); /* Reject if down */ if (!drvr->bus_if->drvr_up || (drvr->bus_if->state == BRCMF_BUS_DOWN)) { brcmf_dbg(ERROR, "xmit rejected drvup=%d state=%d\n", drvr->bus_if->drvr_up, drvr->bus_if->state); netif_stop_queue(ndev); return -ENODEV; } if (!drvr->iflist[ifp->idx]) { brcmf_dbg(ERROR, "bad ifidx %d\n", ifp->idx); netif_stop_queue(ndev); return -ENODEV; } /* Make sure there's enough room for any header */ if (skb_headroom(skb) < drvr->hdrlen) { struct sk_buff *skb2; brcmf_dbg(INFO, "%s: insufficient headroom\n", brcmf_ifname(drvr, ifp->idx)); drvr->bus_if->tx_realloc++; skb2 = skb_realloc_headroom(skb, drvr->hdrlen); dev_kfree_skb(skb); skb = skb2; if (skb == NULL) { brcmf_dbg(ERROR, "%s: skb_realloc_headroom failed\n", brcmf_ifname(drvr, ifp->idx)); ret = -ENOMEM; goto done; } } ret = brcmf_sendpkt(drvr, ifp->idx, skb); done: if (ret) drvr->bus_if->dstats.tx_dropped++; else drvr->bus_if->dstats.tx_packets++; /* Return ok: we always eat the packet */ return 0; } void brcmf_txflowblock(struct device *dev, bool state) { struct net_device *ndev; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; int i; brcmf_dbg(TRACE, "Enter\n"); for (i = 0; i < BRCMF_MAX_IFS; i++) if (drvr->iflist[i]) { ndev = drvr->iflist[i]->ndev; if (state) netif_stop_queue(ndev); else netif_wake_queue(ndev); } } static int brcmf_host_event(struct brcmf_pub *drvr, int *ifidx, void *pktdata, struct brcmf_event_msg *event, void **data) { int bcmerror = 0; bcmerror = brcmf_c_host_event(drvr, ifidx, pktdata, event, data); if (bcmerror != 0) return bcmerror; if (drvr->iflist[*ifidx]->ndev) brcmf_cfg80211_event(drvr->iflist[*ifidx]->ndev, event, *data); return bcmerror; } void brcmf_rx_frame(struct device *dev, int ifidx, struct sk_buff_head *skb_list) { unsigned char *eth; uint len; void *data; struct sk_buff *skb, *pnext; struct brcmf_if *ifp; struct brcmf_event_msg event; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; brcmf_dbg(TRACE, "Enter\n"); skb_queue_walk_safe(skb_list, skb, pnext) { skb_unlink(skb, skb_list); /* Get the protocol, maintain skb around eth_type_trans() * The main reason for this hack is for the limitation of * Linux 2.4 where 'eth_type_trans' uses the * 'net->hard_header_len' * to perform skb_pull inside vs ETH_HLEN. Since to avoid * coping of the packet coming from the network stack to add * BDC, Hardware header etc, during network interface * registration * we set the 'net->hard_header_len' to ETH_HLEN + extra space * required * for BDC, Hardware header etc. and not just the ETH_HLEN */ eth = skb->data; len = skb->len; ifp = drvr->iflist[ifidx]; if (ifp == NULL) ifp = drvr->iflist[0]; if (!ifp || !ifp->ndev || ifp->ndev->reg_state != NETREG_REGISTERED) { brcmu_pkt_buf_free_skb(skb); continue; } skb->dev = ifp->ndev; skb->protocol = eth_type_trans(skb, skb->dev); if (skb->pkt_type == PACKET_MULTICAST) bus_if->dstats.multicast++; skb->data = eth; skb->len = len; /* Strip header, count, deliver upward */ skb_pull(skb, ETH_HLEN); /* Process special event packets and then discard them */ if (ntohs(skb->protocol) == ETH_P_LINK_CTL) brcmf_host_event(drvr, &ifidx, skb_mac_header(skb), &event, &data); if (drvr->iflist[ifidx]) { ifp = drvr->iflist[ifidx]; ifp->ndev->last_rx = jiffies; } bus_if->dstats.rx_bytes += skb->len; bus_if->dstats.rx_packets++; /* Local count */ if (in_interrupt()) netif_rx(skb); else /* If the receive is not processed inside an ISR, * the softirqd must be woken explicitly to service * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled * by netif_rx_ni(), but in earlier kernels, we need * to do it manually. */ netif_rx_ni(skb); } } void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { uint ifidx; struct ethhdr *eh; u16 type; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; brcmf_proto_hdrpull(dev, &ifidx, txp); eh = (struct ethhdr *)(txp->data); type = ntohs(eh->h_proto); if (type == ETH_P_PAE) atomic_dec(&drvr->pend_8021x_cnt); } static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_bus *bus_if = ifp->drvr->bus_if; brcmf_dbg(TRACE, "Enter\n"); /* Copy dongle stats to net device stats */ ifp->stats.rx_packets = bus_if->dstats.rx_packets; ifp->stats.tx_packets = bus_if->dstats.tx_packets; ifp->stats.rx_bytes = bus_if->dstats.rx_bytes; ifp->stats.tx_bytes = bus_if->dstats.tx_bytes; ifp->stats.rx_errors = bus_if->dstats.rx_errors; ifp->stats.tx_errors = bus_if->dstats.tx_errors; ifp->stats.rx_dropped = bus_if->dstats.rx_dropped; ifp->stats.tx_dropped = bus_if->dstats.tx_dropped; ifp->stats.multicast = bus_if->dstats.multicast; return &ifp->stats; } /* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */ static int brcmf_toe_get(struct brcmf_pub *drvr, int ifidx, u32 *toe_ol) { struct brcmf_dcmd dcmd; __le32 toe_le; char buf[32]; int ret; memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = BRCMF_C_GET_VAR; dcmd.buf = buf; dcmd.len = (uint) sizeof(buf); dcmd.set = false; strcpy(buf, "toe_ol"); ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len); if (ret < 0) { /* Check for older dongle image that doesn't support toe_ol */ if (ret == -EIO) { brcmf_dbg(ERROR, "%s: toe not supported by device\n", brcmf_ifname(drvr, ifidx)); return -EOPNOTSUPP; } brcmf_dbg(INFO, "%s: could not get toe_ol: ret=%d\n", brcmf_ifname(drvr, ifidx), ret); return ret; } memcpy(&toe_le, buf, sizeof(u32)); *toe_ol = le32_to_cpu(toe_le); return 0; } /* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */ static int brcmf_toe_set(struct brcmf_pub *drvr, int ifidx, u32 toe_ol) { struct brcmf_dcmd dcmd; char buf[32]; int ret; __le32 toe_le = cpu_to_le32(toe_ol); memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = BRCMF_C_SET_VAR; dcmd.buf = buf; dcmd.len = (uint) sizeof(buf); dcmd.set = true; /* Set toe_ol as requested */ strcpy(buf, "toe_ol"); memcpy(&buf[sizeof("toe_ol")], &toe_le, sizeof(u32)); ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len); if (ret < 0) { brcmf_dbg(ERROR, "%s: could not set toe_ol: ret=%d\n", brcmf_ifname(drvr, ifidx), ret); return ret; } /* Enable toe globally only if any components are enabled. */ toe_le = cpu_to_le32(toe_ol != 0); strcpy(buf, "toe"); memcpy(&buf[sizeof("toe")], &toe_le, sizeof(u32)); ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len); if (ret < 0) { brcmf_dbg(ERROR, "%s: could not set toe: ret=%d\n", brcmf_ifname(drvr, ifidx), ret); return ret; } return 0; } static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; sprintf(info->driver, KBUILD_MODNAME); sprintf(info->version, "%lu", drvr->drv_version); sprintf(info->bus_info, "%s", dev_name(drvr->dev)); } static const struct ethtool_ops brcmf_ethtool_ops = { .get_drvinfo = brcmf_ethtool_get_drvinfo, }; static int brcmf_ethtool(struct brcmf_pub *drvr, void __user *uaddr) { struct ethtool_drvinfo info; char drvname[sizeof(info.driver)]; u32 cmd; struct ethtool_value edata; u32 toe_cmpnt, csum_dir; int ret; brcmf_dbg(TRACE, "Enter\n"); /* all ethtool calls start with a cmd word */ if (copy_from_user(&cmd, uaddr, sizeof(u32))) return -EFAULT; switch (cmd) { case ETHTOOL_GDRVINFO: /* Copy out any request driver name */ if (copy_from_user(&info, uaddr, sizeof(info))) return -EFAULT; strncpy(drvname, info.driver, sizeof(info.driver)); drvname[sizeof(info.driver) - 1] = '\0'; /* clear struct for return */ memset(&info, 0, sizeof(info)); info.cmd = cmd; /* if requested, identify ourselves */ if (strcmp(drvname, "?dhd") == 0) { sprintf(info.driver, "dhd"); strcpy(info.version, BRCMF_VERSION_STR); } /* otherwise, require dongle to be up */ else if (!drvr->bus_if->drvr_up) { brcmf_dbg(ERROR, "dongle is not up\n"); return -ENODEV; } /* finally, report dongle driver type */ else if (drvr->iswl) sprintf(info.driver, "wl"); else sprintf(info.driver, "xx"); sprintf(info.version, "%lu", drvr->drv_version); if (copy_to_user(uaddr, &info, sizeof(info))) return -EFAULT; brcmf_dbg(CTL, "given %*s, returning %s\n", (int)sizeof(drvname), drvname, info.driver); break; /* Get toe offload components from dongle */ case ETHTOOL_GRXCSUM: case ETHTOOL_GTXCSUM: ret = brcmf_toe_get(drvr, 0, &toe_cmpnt); if (ret < 0) return ret; csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; edata.cmd = cmd; edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; if (copy_to_user(uaddr, &edata, sizeof(edata))) return -EFAULT; break; /* Set toe offload components in dongle */ case ETHTOOL_SRXCSUM: case ETHTOOL_STXCSUM: if (copy_from_user(&edata, uaddr, sizeof(edata))) return -EFAULT; /* Read the current settings, update and write back */ ret = brcmf_toe_get(drvr, 0, &toe_cmpnt); if (ret < 0) return ret; csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; if (edata.data != 0) toe_cmpnt |= csum_dir; else toe_cmpnt &= ~csum_dir; ret = brcmf_toe_set(drvr, 0, toe_cmpnt); if (ret < 0) return ret; /* If setting TX checksum mode, tell Linux the new mode */ if (cmd == ETHTOOL_STXCSUM) { if (edata.data) drvr->iflist[0]->ndev->features |= NETIF_F_IP_CSUM; else drvr->iflist[0]->ndev->features &= ~NETIF_F_IP_CSUM; } break; default: return -EOPNOTSUPP; } return 0; } static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr, int cmd) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; brcmf_dbg(TRACE, "ifidx %d, cmd 0x%04x\n", ifp->idx, cmd); if (!drvr->iflist[ifp->idx]) return -1; if (cmd == SIOCETHTOOL) return brcmf_ethtool(drvr, ifr->ifr_data); return -EOPNOTSUPP; } /* called only from within this driver. Sends a command to the dongle. */ s32 brcmf_exec_dcmd(struct net_device *ndev, u32 cmd, void *arg, u32 len) { struct brcmf_dcmd dcmd; s32 err = 0; int buflen = 0; bool is_set_key_cmd; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = cmd; dcmd.buf = arg; dcmd.len = len; if (dcmd.buf != NULL) buflen = min_t(uint, dcmd.len, BRCMF_DCMD_MAXLEN); /* send to dongle (must be up, and wl) */ if ((drvr->bus_if->state != BRCMF_BUS_DATA)) { brcmf_dbg(ERROR, "DONGLE_DOWN\n"); err = -EIO; goto done; } if (!drvr->iswl) { err = -EIO; goto done; } /* * Intercept BRCMF_C_SET_KEY CMD - serialize M4 send and * set key CMD to prevent M4 encryption. */ is_set_key_cmd = ((dcmd.cmd == BRCMF_C_SET_KEY) || ((dcmd.cmd == BRCMF_C_SET_VAR) && !(strncmp("wsec_key", dcmd.buf, 9))) || ((dcmd.cmd == BRCMF_C_SET_VAR) && !(strncmp("bsscfg:wsec_key", dcmd.buf, 15)))); if (is_set_key_cmd) brcmf_netdev_wait_pend8021x(ndev); err = brcmf_proto_dcmd(drvr, ifp->idx, &dcmd, buflen); done: if (err > 0) err = 0; return err; } int brcmf_netlink_dcmd(struct net_device *ndev, struct brcmf_dcmd *dcmd) { brcmf_dbg(TRACE, "enter: cmd %x buf %p len %d\n", dcmd->cmd, dcmd->buf, dcmd->len); return brcmf_exec_dcmd(ndev, dcmd->cmd, dcmd->buf, dcmd->len); } static int brcmf_netdev_stop(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; brcmf_dbg(TRACE, "Enter\n"); brcmf_cfg80211_down(drvr->config); if (drvr->bus_if->drvr_up == 0) return 0; /* Set state and stop OS transmissions */ drvr->bus_if->drvr_up = false; netif_stop_queue(ndev); return 0; } static int brcmf_netdev_open(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; struct brcmf_bus *bus_if = drvr->bus_if; u32 toe_ol; s32 ret = 0; uint up = 0; brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx); if (ifp->idx == 0) { /* do it only for primary eth0 */ /* If bus is not ready, can't continue */ if (bus_if->state != BRCMF_BUS_DATA) { brcmf_dbg(ERROR, "failed bus is not ready\n"); return -EAGAIN; } atomic_set(&drvr->pend_8021x_cnt, 0); memcpy(ndev->dev_addr, drvr->mac, ETH_ALEN); /* Get current TOE mode from dongle */ if (brcmf_toe_get(drvr, ifp->idx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) drvr->iflist[ifp->idx]->ndev->features |= NETIF_F_IP_CSUM; else drvr->iflist[ifp->idx]->ndev->features &= ~NETIF_F_IP_CSUM; } /* make sure RF is ready for work */ brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_UP, (char *)&up, sizeof(up)); /* Allow transmit calls */ netif_start_queue(ndev); drvr->bus_if->drvr_up = true; if (brcmf_cfg80211_up(drvr->config)) { brcmf_dbg(ERROR, "failed to bring up cfg80211\n"); return -1; } return ret; } static const struct net_device_ops brcmf_netdev_ops_pri = { .ndo_open = brcmf_netdev_open, .ndo_stop = brcmf_netdev_stop, .ndo_get_stats = brcmf_netdev_get_stats, .ndo_do_ioctl = brcmf_netdev_ioctl_entry, .ndo_start_xmit = brcmf_netdev_start_xmit, .ndo_set_mac_address = brcmf_netdev_set_mac_address, .ndo_set_rx_mode = brcmf_netdev_set_multicast_list }; static int brcmf_net_attach(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; struct net_device *ndev; u8 temp_addr[ETH_ALEN]; brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx); ndev = drvr->iflist[ifp->idx]->ndev; ndev->netdev_ops = &brcmf_netdev_ops_pri; /* * determine mac address to use */ if (is_valid_ether_addr(ifp->mac_addr)) memcpy(temp_addr, ifp->mac_addr, ETH_ALEN); else memcpy(temp_addr, drvr->mac, ETH_ALEN); if (ifp->idx == 1) { brcmf_dbg(TRACE, "ACCESS POINT MAC:\n"); /* ACCESSPOINT INTERFACE CASE */ temp_addr[0] |= 0X02; /* set bit 2 , - Locally Administered address */ } ndev->hard_header_len = ETH_HLEN + drvr->hdrlen; ndev->ethtool_ops = &brcmf_ethtool_ops; drvr->rxsz = ndev->mtu + ndev->hard_header_len + drvr->hdrlen; memcpy(ndev->dev_addr, temp_addr, ETH_ALEN); /* attach to cfg80211 for primary interface */ if (!ifp->idx) { drvr->config = brcmf_cfg80211_attach(ndev, drvr->dev, drvr); if (drvr->config == NULL) { brcmf_dbg(ERROR, "wl_cfg80211_attach failed\n"); goto fail; } } if (register_netdev(ndev) != 0) { brcmf_dbg(ERROR, "couldn't register the net device\n"); goto fail; } brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); return 0; fail: ndev->netdev_ops = NULL; return -EBADE; } int brcmf_add_if(struct device *dev, int ifidx, char *name, u8 *mac_addr) { struct brcmf_if *ifp; struct net_device *ndev; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; brcmf_dbg(TRACE, "idx %d\n", ifidx); ifp = drvr->iflist[ifidx]; /* * Delete the existing interface before overwriting it * in case we missed the BRCMF_E_IF_DEL event. */ if (ifp) { brcmf_dbg(ERROR, "ERROR: netdev:%s already exists, try free & unregister\n", ifp->ndev->name); netif_stop_queue(ifp->ndev); unregister_netdev(ifp->ndev); free_netdev(ifp->ndev); drvr->iflist[ifidx] = NULL; } /* Allocate netdev, including space for private structure */ ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup); if (!ndev) { brcmf_dbg(ERROR, "OOM - alloc_netdev\n"); return -ENOMEM; } ifp = netdev_priv(ndev); ifp->ndev = ndev; ifp->drvr = drvr; drvr->iflist[ifidx] = ifp; ifp->idx = ifidx; if (mac_addr != NULL) memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN); if (brcmf_net_attach(ifp)) { brcmf_dbg(ERROR, "brcmf_net_attach failed"); free_netdev(ifp->ndev); drvr->iflist[ifidx] = NULL; return -EOPNOTSUPP; } brcmf_dbg(TRACE, " ==== pid:%x, net_device for if:%s created ===\n", current->pid, ifp->ndev->name); return 0; } void brcmf_del_if(struct brcmf_pub *drvr, int ifidx) { struct brcmf_if *ifp; brcmf_dbg(TRACE, "idx %d\n", ifidx); ifp = drvr->iflist[ifidx]; if (!ifp) { brcmf_dbg(ERROR, "Null interface\n"); return; } if (ifp->ndev) { if (ifidx == 0) { if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { rtnl_lock(); brcmf_netdev_stop(ifp->ndev); rtnl_unlock(); } } else { netif_stop_queue(ifp->ndev); } unregister_netdev(ifp->ndev); drvr->iflist[ifidx] = NULL; if (ifidx == 0) brcmf_cfg80211_detach(drvr->config); free_netdev(ifp->ndev); } } int brcmf_attach(uint bus_hdrlen, struct device *dev) { struct brcmf_pub *drvr = NULL; int ret = 0; brcmf_dbg(TRACE, "Enter\n"); /* Allocate primary brcmf_info */ drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC); if (!drvr) return -ENOMEM; mutex_init(&drvr->proto_block); /* Link to bus module */ drvr->hdrlen = bus_hdrlen; drvr->bus_if = dev_get_drvdata(dev); drvr->bus_if->drvr = drvr; drvr->dev = dev; /* create device debugfs folder */ brcmf_debugfs_attach(drvr); /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); if (ret != 0) { brcmf_dbg(ERROR, "brcmf_prot_attach failed\n"); goto fail; } INIT_WORK(&drvr->setmacaddr_work, _brcmf_set_mac_address); INIT_WORK(&drvr->multicast_work, _brcmf_set_multicast_list); INIT_LIST_HEAD(&drvr->bus_if->dcmd_list); return ret; fail: brcmf_detach(dev); return ret; } int brcmf_bus_start(struct device *dev) { int ret = -1; /* Room for "event_msgs" + '\0' + bitvec */ char iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; brcmf_dbg(TRACE, "\n"); /* Bring up the bus */ ret = bus_if->brcmf_bus_init(dev); if (ret != 0) { brcmf_dbg(ERROR, "brcmf_sdbrcm_bus_init failed %d\n", ret); return ret; } brcmf_c_mkiovar("event_msgs", drvr->eventmask, BRCMF_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR, iovbuf, sizeof(iovbuf)); memcpy(drvr->eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN); setbit(drvr->eventmask, BRCMF_E_SET_SSID); setbit(drvr->eventmask, BRCMF_E_PRUNE); setbit(drvr->eventmask, BRCMF_E_AUTH); setbit(drvr->eventmask, BRCMF_E_REASSOC); setbit(drvr->eventmask, BRCMF_E_REASSOC_IND); setbit(drvr->eventmask, BRCMF_E_DEAUTH_IND); setbit(drvr->eventmask, BRCMF_E_DISASSOC_IND); setbit(drvr->eventmask, BRCMF_E_DISASSOC); setbit(drvr->eventmask, BRCMF_E_JOIN); setbit(drvr->eventmask, BRCMF_E_ASSOC_IND); setbit(drvr->eventmask, BRCMF_E_PSK_SUP); setbit(drvr->eventmask, BRCMF_E_LINK); setbit(drvr->eventmask, BRCMF_E_NDIS_LINK); setbit(drvr->eventmask, BRCMF_E_MIC_ERROR); setbit(drvr->eventmask, BRCMF_E_PMKID_CACHE); setbit(drvr->eventmask, BRCMF_E_TXFAIL); setbit(drvr->eventmask, BRCMF_E_JOIN_START); setbit(drvr->eventmask, BRCMF_E_SCAN_COMPLETE); /* enable dongle roaming event */ drvr->pktfilter_count = 1; /* Setup filter to allow only unicast */ drvr->pktfilter[0] = "100 0 0 0 0x01 0x00"; /* Bus is ready, do any protocol initialization */ ret = brcmf_proto_init(drvr); if (ret < 0) return ret; /* add primary networking interface */ ret = brcmf_add_if(dev, 0, "wlan%d", drvr->mac); if (ret < 0) return ret; /* signal bus ready */ bus_if->state = BRCMF_BUS_DATA; return 0; } static void brcmf_bus_detach(struct brcmf_pub *drvr) { brcmf_dbg(TRACE, "Enter\n"); if (drvr) { /* Stop the protocol module */ brcmf_proto_stop(drvr); /* Stop the bus module */ drvr->bus_if->brcmf_bus_stop(drvr->dev); } } void brcmf_detach(struct device *dev) { int i; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; brcmf_dbg(TRACE, "Enter\n"); /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) if (drvr->iflist[i]) brcmf_del_if(drvr, i); brcmf_bus_detach(drvr); if (drvr->prot) { cancel_work_sync(&drvr->setmacaddr_work); cancel_work_sync(&drvr->multicast_work); brcmf_proto_detach(drvr); } brcmf_debugfs_detach(drvr); bus_if->drvr = NULL; kfree(drvr); } static int brcmf_get_pend_8021x_cnt(struct brcmf_pub *drvr) { return atomic_read(&drvr->pend_8021x_cnt); } #define MAX_WAIT_FOR_8021X_TX 10 int brcmf_netdev_wait_pend8021x(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; int timeout = 10 * HZ / 1000; int ntimes = MAX_WAIT_FOR_8021X_TX; int pend = brcmf_get_pend_8021x_cnt(drvr); while (ntimes && pend) { if (pend) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(timeout); set_current_state(TASK_RUNNING); ntimes--; } pend = brcmf_get_pend_8021x_cnt(drvr); } return pend; } #ifdef DEBUG int brcmf_write_to_file(struct brcmf_pub *drvr, const u8 *buf, int size) { int ret = 0; struct file *fp; mm_segment_t old_fs; loff_t pos = 0; /* change to KERNEL_DS address limit */ old_fs = get_fs(); set_fs(KERNEL_DS); /* open file to write */ fp = filp_open("/tmp/mem_dump", O_WRONLY | O_CREAT, 0640); if (!fp) { brcmf_dbg(ERROR, "open file error\n"); ret = -1; goto exit; } /* Write buf to file */ fp->f_op->write(fp, (char __user *)buf, size, &pos); exit: /* free buf before return */ kfree(buf); /* close file before return */ if (fp) filp_close(fp, NULL); /* restore previous address limit */ set_fs(old_fs); return ret; } #endif /* DEBUG */ static void brcmf_driver_init(struct work_struct *work) { brcmf_debugfs_init(); #ifdef CONFIG_BRCMFMAC_SDIO brcmf_sdio_init(); #endif #ifdef CONFIG_BRCMFMAC_USB brcmf_usb_init(); #endif } static DECLARE_WORK(brcmf_driver_work, brcmf_driver_init); static int __init brcmfmac_module_init(void) { if (!schedule_work(&brcmf_driver_work)) return -EBUSY; return 0; } static void __exit brcmfmac_module_exit(void) { cancel_work_sync(&brcmf_driver_work); #ifdef CONFIG_BRCMFMAC_SDIO brcmf_sdio_exit(); #endif #ifdef CONFIG_BRCMFMAC_USB brcmf_usb_exit(); #endif brcmf_debugfs_exit(); } module_init(brcmfmac_module_init); module_exit(brcmfmac_module_exit); compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c0000644000175000017500000003132312026211315025160 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ****************** SDIO CARD Interface Functions **************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhd_bus.h" #include "dhd_dbg.h" #include "sdio_host.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 #ifdef CONFIG_BRCMFMAC_SDIO_OOB static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id) { struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(dev_id); brcmf_dbg(INTR, "oob intr triggered\n"); /* * out-of-band interrupt is level-triggered which won't * be cleared until dpc */ if (sdiodev->irq_en) { disable_irq_nosync(irq); sdiodev->irq_en = false; } brcmf_sdbrcm_isr(sdiodev->bus); return IRQ_HANDLED; } int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; u8 data; unsigned long flags; brcmf_dbg(TRACE, "Entering\n"); brcmf_dbg(ERROR, "requesting irq %d\n", sdiodev->irq); ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler, sdiodev->irq_flags, "brcmf_oob_intr", &sdiodev->func[1]->card->dev); if (ret != 0) return ret; spin_lock_init(&sdiodev->irq_en_lock); spin_lock_irqsave(&sdiodev->irq_en_lock, flags); sdiodev->irq_en = true; spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); ret = enable_irq_wake(sdiodev->irq); if (ret != 0) return ret; sdiodev->irq_wake = true; /* must configure SDIO_CCCR_IENx to enable irq */ data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret); data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); /* redirect, configure and enable io for interrupt signal */ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; if (sdiodev->irq_flags & IRQF_TRIGGER_HIGH) data |= SDIO_SEPINT_ACT_HI; brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); return 0; } int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(TRACE, "Entering\n"); brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); if (sdiodev->irq_wake) { disable_irq_wake(sdiodev->irq); sdiodev->irq_wake = false; } free_irq(sdiodev->irq, &sdiodev->func[1]->card->dev); sdiodev->irq_en = false; return 0; } #else /* CONFIG_BRCMFMAC_SDIO_OOB */ static void brcmf_sdio_irqhandler(struct sdio_func *func) { struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); brcmf_dbg(INTR, "ib intr triggered\n"); brcmf_sdbrcm_isr(sdiodev->bus); } /* dummy handler for SDIO function 2 interrupt */ static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func) { } int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(TRACE, "Entering\n"); sdio_claim_host(sdiodev->func[1]); sdio_claim_irq(sdiodev->func[1], brcmf_sdio_irqhandler); sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler); sdio_release_host(sdiodev->func[1]); return 0; } int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(TRACE, "Entering\n"); sdio_claim_host(sdiodev->func[1]); sdio_release_irq(sdiodev->func[2]); sdio_release_irq(sdiodev->func[1]); sdio_release_host(sdiodev->func[1]); return 0; } #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ int brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) { int err = 0, i; u8 addr[3]; s32 retry; addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK; addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK; for (i = 0; i < 3; i++) { retry = 0; do { if (retry) usleep_range(1000, 2000); err = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW + i, &addr[i]); } while (err != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (err) { brcmf_dbg(ERROR, "failed at addr:0x%0x\n", SBSDIO_FUNC1_SBADDRLOW + i); break; } } return err; } static int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, void *data, bool write) { u8 func_num, reg_size; u32 bar; s32 retry = 0; int ret; /* * figure out how to read the register based on address range * 0x00 ~ 0x7FF: function 0 CCCR and FBR * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers * The rest: function 1 silicon backplane core registers */ if ((addr & ~REG_F0_REG_MASK) == 0) { func_num = SDIO_FUNC_0; reg_size = 1; } else if ((addr & ~REG_F1_MISC_MASK) == 0) { func_num = SDIO_FUNC_1; reg_size = 1; } else { func_num = SDIO_FUNC_1; reg_size = 4; /* Set the window for SB core register */ bar = addr & ~SBSDIO_SB_OFT_ADDR_MASK; if (bar != sdiodev->sbwad) { ret = brcmf_sdcard_set_sbaddr_window(sdiodev, bar); if (ret != 0) { memset(data, 0xFF, reg_size); return ret; } sdiodev->sbwad = bar; } addr &= SBSDIO_SB_OFT_ADDR_MASK; addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; } do { if (!write) memset(data, 0, reg_size); if (retry) /* wait for 1 ms till bus get settled down */ usleep_range(1000, 2000); if (reg_size == 1) ret = brcmf_sdioh_request_byte(sdiodev, write, func_num, addr, data); else ret = brcmf_sdioh_request_word(sdiodev, write, func_num, addr, data, 4); } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (ret != 0) brcmf_dbg(ERROR, "failed with %d\n", ret); return ret; } u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) { u8 data; int retval; brcmf_dbg(INFO, "addr:0x%08x\n", addr); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); brcmf_dbg(INFO, "data:0x%02x\n", data); if (ret) *ret = retval; return data; } u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) { u32 data; int retval; brcmf_dbg(INFO, "addr:0x%08x\n", addr); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); brcmf_dbg(INFO, "data:0x%08x\n", data); if (ret) *ret = retval; return data; } void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, int *ret) { int retval; brcmf_dbg(INFO, "addr:0x%08x, data:0x%02x\n", addr, data); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); if (ret) *ret = retval; } void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, int *ret) { int retval; brcmf_dbg(INFO, "addr:0x%08x, data:0x%08x\n", addr, data); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); if (ret) *ret = retval; } static int brcmf_sdcard_recv_prepare(struct brcmf_sdio_dev *sdiodev, uint fn, uint flags, uint width, u32 *addr) { uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; int err = 0; /* Async not implemented yet */ if (flags & SDIO_REQ_ASYNC) return -ENOTSUPP; if (bar0 != sdiodev->sbwad) { err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); if (err) return err; sdiodev->sbwad = bar0; } *addr &= SBSDIO_SB_OFT_ADDR_MASK; if (width == 4) *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; return 0; } int brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes) { struct sk_buff *mypkt; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); if (!mypkt) { brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n", nbytes); return -EIO; } err = brcmf_sdcard_recv_pkt(sdiodev, addr, fn, flags, mypkt); if (!err) memcpy(buf, mypkt->data, nbytes); brcmu_pkt_buf_free_skb(mypkt); return err; } int brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff *pkt) { uint incr_fix; uint width; int err = 0; brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); if (err) return err; incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ, fn, addr, pkt); return err; } int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff_head *pktq) { uint incr_fix; uint width; int err = 0; brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); if (err) return err; incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr, pktq); return err; } int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes) { struct sk_buff *mypkt; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); if (!mypkt) { brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n", nbytes); return -EIO; } memcpy(mypkt->data, buf, nbytes); err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt); brcmu_pkt_buf_free_skb(mypkt); return err; } int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff *pkt) { uint incr_fix; uint width; uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; int err = 0; brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); /* Async not implemented yet */ if (flags & SDIO_REQ_ASYNC) return -ENOTSUPP; if (bar0 != sdiodev->sbwad) { err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); if (err) return err; sdiodev->sbwad = bar0; } addr &= SBSDIO_SB_OFT_ADDR_MASK; incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; if (width == 4) addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn, addr, pkt); } int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, u8 *buf, uint nbytes) { struct sk_buff *mypkt; bool write = rw ? SDIOH_WRITE : SDIOH_READ; int err; addr &= SBSDIO_SB_OFT_ADDR_MASK; addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; mypkt = brcmu_pkt_buf_get_skb(nbytes); if (!mypkt) { brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: len %d\n", nbytes); return -EIO; } /* For a write, copy the buffer data into the packet. */ if (write) memcpy(mypkt->data, buf, nbytes); err = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, write, SDIO_FUNC_1, addr, mypkt); /* For a read, copy the packet data back to the buffer. */ if (!err && !write) memcpy(buf, mypkt->data, nbytes); brcmu_pkt_buf_free_skb(mypkt); return err; } int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) { char t_func = (char)fn; brcmf_dbg(TRACE, "Enter\n"); /* issue abort cmd52 command through F0 */ brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, SDIO_CCCR_ABORT, &t_func); brcmf_dbg(TRACE, "Exit\n"); return 0; } int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { u32 regs = 0; int ret = 0; ret = brcmf_sdioh_attach(sdiodev); if (ret) goto out; regs = SI_ENUM_BASE; /* Report the BAR, to fix if needed */ sdiodev->sbwad = SI_ENUM_BASE; /* try to attach to the target device */ sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev); if (!sdiodev->bus) { brcmf_dbg(ERROR, "device attach failed\n"); ret = -ENODEV; goto out; } out: if (ret) brcmf_sdio_remove(sdiodev); return ret; } EXPORT_SYMBOL(brcmf_sdio_probe); int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev) { if (sdiodev->bus) { brcmf_sdbrcm_disconnect(sdiodev->bus); sdiodev->bus = NULL; } brcmf_sdioh_detach(sdiodev); sdiodev->sbwad = 0; return 0; } EXPORT_SYMBOL(brcmf_sdio_remove); void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable) { if (enable) brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); else brcmf_sdbrcm_wd_timer(sdiodev->bus, 0); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c0000644000175000017500000003101712026211315025270 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /******************************************************************************* * Communicates with the dongle by using dcmd codes. * For certain dcmd codes, the dongle interprets string data from the host. ******************************************************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include "dhd.h" #include "dhd_proto.h" #include "dhd_bus.h" #include "dhd_dbg.h" struct brcmf_proto_cdc_dcmd { __le32 cmd; /* dongle command value */ __le32 len; /* lower 16: output buflen; * upper 16: input buflen (excludes header) */ __le32 flags; /* flag defns given below */ __le32 status; /* status code returned from the device */ }; /* Max valid buffer size that can be sent to the dongle */ #define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) /* CDC flag definitions */ #define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */ #define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ #define CDC_DCMD_IF_MASK 0xF000 /* I/F index */ #define CDC_DCMD_IF_SHIFT 12 #define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ #define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ #define CDC_DCMD_ID(flags) \ (((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT) /* * BDC header - Broadcom specific extension of CDC. * Used on data packets to convey priority across USB. */ #define BDC_HEADER_LEN 4 #define BDC_PROTO_VER 2 /* Protocol version */ #define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ #define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ #define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ #define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ #define BDC_PRIORITY_MASK 0x7 #define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ #define BDC_FLAG2_IF_SHIFT 0 #define BDC_GET_IF_IDX(hdr) \ ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) #define BDC_SET_IF_IDX(hdr, idx) \ ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \ ((idx) << BDC_FLAG2_IF_SHIFT))) struct brcmf_proto_bdc_header { u8 flags; u8 priority; /* 802.1d Priority, 4:7 flow control info for usb */ u8 flags2; u8 data_offset; }; #define RETRIES 2 /* # of retries to retrieve matching dcmd response */ #define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE * (amount of header tha might be added) * plus any space that might be needed * for bus alignment padding. */ #define ROUND_UP_MARGIN 2048 /* Biggest bus block size possible for * round off at the end of buffer * Currently is SDIO */ struct brcmf_proto { u16 reqid; u8 pending; u32 lastcmd; u8 bus_header[BUS_HEADER_LEN]; struct brcmf_proto_cdc_dcmd msg; unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN]; }; static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) { struct brcmf_proto *prot = drvr->prot; int len = le32_to_cpu(prot->msg.len) + sizeof(struct brcmf_proto_cdc_dcmd); brcmf_dbg(TRACE, "Enter\n"); /* NOTE : cdc->msg.len holds the desired length of the buffer to be * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area * is actually sent to the dongle */ if (len > CDC_MAX_MSG_SIZE) len = CDC_MAX_MSG_SIZE; /* Send request */ return drvr->bus_if->brcmf_bus_txctl(drvr->dev, (unsigned char *)&prot->msg, len); } static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) { int ret; struct brcmf_proto *prot = drvr->prot; brcmf_dbg(TRACE, "Enter\n"); do { ret = drvr->bus_if->brcmf_bus_rxctl(drvr->dev, (unsigned char *)&prot->msg, len + sizeof(struct brcmf_proto_cdc_dcmd)); if (ret < 0) break; } while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id); return ret; } int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) { struct brcmf_proto *prot = drvr->prot; struct brcmf_proto_cdc_dcmd *msg = &prot->msg; void *info; int ret = 0, retries = 0; u32 id, flags; brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len); /* Respond "bcmerror" and "bcmerrorstr" with local cache */ if (cmd == BRCMF_C_GET_VAR && buf) { if (!strcmp((char *)buf, "bcmerrorstr")) { strncpy((char *)buf, "bcm_error", BCME_STRLEN); goto done; } else if (!strcmp((char *)buf, "bcmerror")) { *(int *)buf = drvr->dongle_error; goto done; } } memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); msg->cmd = cpu_to_le32(cmd); msg->len = cpu_to_le32(len); flags = (++prot->reqid << CDC_DCMD_ID_SHIFT); flags = (flags & ~CDC_DCMD_IF_MASK) | (ifidx << CDC_DCMD_IF_SHIFT); msg->flags = cpu_to_le32(flags); if (buf) memcpy(prot->buf, buf, len); ret = brcmf_proto_cdc_msg(drvr); if (ret < 0) { brcmf_dbg(ERROR, "brcmf_proto_cdc_msg failed w/status %d\n", ret); goto done; } retry: /* wait for interrupt and get first fragment */ ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); if (ret < 0) goto done; flags = le32_to_cpu(msg->flags); id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; if ((id < prot->reqid) && (++retries < RETRIES)) goto retry; if (id != prot->reqid) { brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n", brcmf_ifname(drvr, ifidx), id, prot->reqid); ret = -EINVAL; goto done; } /* Check info buffer */ info = (void *)&msg[1]; /* Copy info buffer */ if (buf) { if (ret < (int)len) len = ret; memcpy(buf, info, len); } /* Check the ERROR flag */ if (flags & CDC_DCMD_ERROR) { ret = le32_to_cpu(msg->status); /* Cache error from dongle */ drvr->dongle_error = ret; } done: return ret; } int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) { struct brcmf_proto *prot = drvr->prot; struct brcmf_proto_cdc_dcmd *msg = &prot->msg; int ret = 0; u32 flags, id; brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len); memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); msg->cmd = cpu_to_le32(cmd); msg->len = cpu_to_le32(len); flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET; flags = (flags & ~CDC_DCMD_IF_MASK) | (ifidx << CDC_DCMD_IF_SHIFT); msg->flags = cpu_to_le32(flags); if (buf) memcpy(prot->buf, buf, len); ret = brcmf_proto_cdc_msg(drvr); if (ret < 0) goto done; ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); if (ret < 0) goto done; flags = le32_to_cpu(msg->flags); id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; if (id != prot->reqid) { brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n", brcmf_ifname(drvr, ifidx), id, prot->reqid); ret = -EINVAL; goto done; } /* Check the ERROR flag */ if (flags & CDC_DCMD_ERROR) { ret = le32_to_cpu(msg->status); /* Cache error from dongle */ drvr->dongle_error = ret; } done: return ret; } int brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx, struct brcmf_dcmd *dcmd, int len) { struct brcmf_proto *prot = drvr->prot; int ret = -1; if (drvr->bus_if->state == BRCMF_BUS_DOWN) { brcmf_dbg(ERROR, "bus is down. we have nothing to do.\n"); return ret; } mutex_lock(&drvr->proto_block); brcmf_dbg(TRACE, "Enter\n"); if (len > BRCMF_DCMD_MAXLEN) goto done; if (prot->pending == true) { brcmf_dbg(TRACE, "CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", dcmd->cmd, (unsigned long)dcmd->cmd, prot->lastcmd, (unsigned long)prot->lastcmd); if (dcmd->cmd == BRCMF_C_SET_VAR || dcmd->cmd == BRCMF_C_GET_VAR) brcmf_dbg(TRACE, "iovar cmd=%s\n", (char *)dcmd->buf); goto done; } prot->pending = true; prot->lastcmd = dcmd->cmd; if (dcmd->set) ret = brcmf_proto_cdc_set_dcmd(drvr, ifidx, dcmd->cmd, dcmd->buf, len); else { ret = brcmf_proto_cdc_query_dcmd(drvr, ifidx, dcmd->cmd, dcmd->buf, len); if (ret > 0) dcmd->used = ret - sizeof(struct brcmf_proto_cdc_dcmd); } if (ret >= 0) ret = 0; else { struct brcmf_proto_cdc_dcmd *msg = &prot->msg; /* len == needed when set/query fails from dongle */ dcmd->needed = le32_to_cpu(msg->len); } /* Intercept the wme_dp dongle cmd here */ if (!ret && dcmd->cmd == BRCMF_C_SET_VAR && !strcmp(dcmd->buf, "wme_dp")) { int slen; __le32 val = 0; slen = strlen("wme_dp") + 1; if (len >= (int)(slen + sizeof(int))) memcpy(&val, (char *)dcmd->buf + slen, sizeof(int)); drvr->wme_dp = (u8) le32_to_cpu(val); } prot->pending = false; done: mutex_unlock(&drvr->proto_block); return ret; } static bool pkt_sum_needed(struct sk_buff *skb) { return skb->ip_summed == CHECKSUM_PARTIAL; } static void pkt_set_sum_good(struct sk_buff *skb, bool x) { skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE); } void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, struct sk_buff *pktbuf) { struct brcmf_proto_bdc_header *h; brcmf_dbg(TRACE, "Enter\n"); /* Push BDC header used to convey priority for buses that don't */ skb_push(pktbuf, BDC_HEADER_LEN); h = (struct brcmf_proto_bdc_header *)(pktbuf->data); h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); if (pkt_sum_needed(pktbuf)) h->flags |= BDC_FLAG_SUM_NEEDED; h->priority = (pktbuf->priority & BDC_PRIORITY_MASK); h->flags2 = 0; h->data_offset = 0; BDC_SET_IF_IDX(h, ifidx); } int brcmf_proto_hdrpull(struct device *dev, int *ifidx, struct sk_buff *pktbuf) { struct brcmf_proto_bdc_header *h; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; brcmf_dbg(TRACE, "Enter\n"); /* Pop BDC header used to convey priority for buses that don't */ if (pktbuf->len < BDC_HEADER_LEN) { brcmf_dbg(ERROR, "rx data too short (%d < %d)\n", pktbuf->len, BDC_HEADER_LEN); return -EBADE; } h = (struct brcmf_proto_bdc_header *)(pktbuf->data); *ifidx = BDC_GET_IF_IDX(h); if (*ifidx >= BRCMF_MAX_IFS) { brcmf_dbg(ERROR, "rx data ifnum out of range (%d)\n", *ifidx); return -EBADE; } if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) { brcmf_dbg(ERROR, "%s: non-BDC packet received, flags 0x%x\n", brcmf_ifname(drvr, *ifidx), h->flags); return -EBADE; } if (h->flags & BDC_FLAG_SUM_GOOD) { brcmf_dbg(INFO, "%s: BDC packet received with good rx-csum, flags 0x%x\n", brcmf_ifname(drvr, *ifidx), h->flags); pkt_set_sum_good(pktbuf, true); } pktbuf->priority = h->priority & BDC_PRIORITY_MASK; skb_pull(pktbuf, BDC_HEADER_LEN); skb_pull(pktbuf, h->data_offset << 2); return 0; } int brcmf_proto_attach(struct brcmf_pub *drvr) { struct brcmf_proto *cdc; cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC); if (!cdc) goto fail; /* ensure that the msg buf directly follows the cdc msg struct */ if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) { brcmf_dbg(ERROR, "struct brcmf_proto is not correctly defined\n"); goto fail; } drvr->prot = cdc; drvr->hdrlen += BDC_HEADER_LEN; drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN; return 0; fail: kfree(cdc); return -ENOMEM; } /* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ void brcmf_proto_detach(struct brcmf_pub *drvr) { kfree(drvr->prot); drvr->prot = NULL; } int brcmf_proto_init(struct brcmf_pub *drvr) { int ret = 0; char buf[128]; brcmf_dbg(TRACE, "Enter\n"); mutex_lock(&drvr->proto_block); /* Get the device MAC address */ strcpy(buf, "cur_etheraddr"); ret = brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR, buf, sizeof(buf)); if (ret < 0) { mutex_unlock(&drvr->proto_block); return ret; } memcpy(drvr->mac, buf, ETH_ALEN); mutex_unlock(&drvr->proto_block); ret = brcmf_c_preinit_dcmds(drvr); /* Always assumes wl for now */ drvr->iswl = true; return ret; } void brcmf_proto_stop(struct brcmf_pub *drvr) { /* Nothing to do for CDC */ } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c0000644000175000017500000004344112026211315026347 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include /* request_irq() */ #include #include #include #include #include #include #include #include "sdio_host.h" #include "dhd_dbg.h" #include "dhd_bus.h" #define SDIO_VENDOR_ID_BROADCOM 0x02d0 #define DMA_ALIGN_MASK 0x03 #define SDIO_DEVICE_ID_BROADCOM_43241 0x4324 #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 #define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 #define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 #define SDIO_FUNC1_BLOCKSIZE 64 #define SDIO_FUNC2_BLOCKSIZE 512 /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); #ifdef CONFIG_BRCMFMAC_SDIO_OOB static struct list_head oobirq_lh; struct brcmf_sdio_oobirq { unsigned int irq; unsigned long flags; struct list_head list; }; #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ static bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) { bool is_err = false; #if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) is_err = atomic_read(&sdiodev->suspend); #endif return is_err; } static void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq) { #if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) int retry = 0; while (atomic_read(&sdiodev->suspend) && retry++ != 30) wait_event_timeout(*wq, false, HZ/100); #endif } static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, uint regaddr, u8 *byte) { struct sdio_func *sdfunc = sdiodev->func[0]; int err_ret; /* * Can only directly write to some F0 registers. * Handle F2 enable/disable and Abort command * as a special case. */ if (regaddr == SDIO_CCCR_IOEx) { sdfunc = sdiodev->func[2]; if (sdfunc) { sdio_claim_host(sdfunc); if (*byte & SDIO_FUNC_ENABLE_2) { /* Enable Function 2 */ err_ret = sdio_enable_func(sdfunc); if (err_ret) brcmf_dbg(ERROR, "enable F2 failed:%d\n", err_ret); } else { /* Disable Function 2 */ err_ret = sdio_disable_func(sdfunc); if (err_ret) brcmf_dbg(ERROR, "Disable F2 failed:%d\n", err_ret); } sdio_release_host(sdfunc); } } else if ((regaddr == SDIO_CCCR_ABORT) || (regaddr == SDIO_CCCR_IENx)) { sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func), GFP_KERNEL); if (!sdfunc) return -ENOMEM; sdfunc->num = 0; sdio_claim_host(sdfunc); sdio_writeb(sdfunc, *byte, regaddr, &err_ret); sdio_release_host(sdfunc); kfree(sdfunc); } else if (regaddr < 0xF0) { brcmf_dbg(ERROR, "F0 Wr:0x%02x: write disallowed\n", regaddr); err_ret = -EPERM; } else { sdio_claim_host(sdfunc); sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret); sdio_release_host(sdfunc); } return err_ret; } int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, uint regaddr, u8 *byte) { int err_ret; brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr); brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait); if (brcmf_pm_resume_error(sdiodev)) return -EIO; if (rw && func == 0) { /* handle F0 separately */ err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte); } else { sdio_claim_host(sdiodev->func[func]); if (rw) /* CMD52 Write */ sdio_writeb(sdiodev->func[func], *byte, regaddr, &err_ret); else if (func == 0) { *byte = sdio_f0_readb(sdiodev->func[func], regaddr, &err_ret); } else { *byte = sdio_readb(sdiodev->func[func], regaddr, &err_ret); } sdio_release_host(sdiodev->func[func]); } if (err_ret) brcmf_dbg(ERROR, "Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", rw ? "write" : "read", func, regaddr, *byte, err_ret); return err_ret; } int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, uint addr, u32 *word, uint nbytes) { int err_ret = -EIO; if (func == 0) { brcmf_dbg(ERROR, "Only CMD52 allowed to F0\n"); return -EINVAL; } brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", rw, func, addr, nbytes); brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); if (brcmf_pm_resume_error(sdiodev)) return -EIO; /* Claim host controller */ sdio_claim_host(sdiodev->func[func]); if (rw) { /* CMD52 Write */ if (nbytes == 4) sdio_writel(sdiodev->func[func], *word, addr, &err_ret); else if (nbytes == 2) sdio_writew(sdiodev->func[func], (*word & 0xFFFF), addr, &err_ret); else brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes); } else { /* CMD52 Read */ if (nbytes == 4) *word = sdio_readl(sdiodev->func[func], addr, &err_ret); else if (nbytes == 2) *word = sdio_readw(sdiodev->func[func], addr, &err_ret) & 0xFFFF; else brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes); } /* Release host controller */ sdio_release_host(sdiodev->func[func]); if (err_ret) brcmf_dbg(ERROR, "Failed to %s word, Err: 0x%08x\n", rw ? "write" : "read", err_ret); return err_ret; } /* precondition: host controller is claimed */ static int brcmf_sdioh_request_data(struct brcmf_sdio_dev *sdiodev, uint write, bool fifo, uint func, uint addr, struct sk_buff *pkt, uint pktlen) { int err_ret = 0; if ((write) && (!fifo)) { err_ret = sdio_memcpy_toio(sdiodev->func[func], addr, ((u8 *) (pkt->data)), pktlen); } else if (write) { err_ret = sdio_memcpy_toio(sdiodev->func[func], addr, ((u8 *) (pkt->data)), pktlen); } else if (fifo) { err_ret = sdio_readsb(sdiodev->func[func], ((u8 *) (pkt->data)), addr, pktlen); } else { err_ret = sdio_memcpy_fromio(sdiodev->func[func], ((u8 *) (pkt->data)), addr, pktlen); } return err_ret; } /* * This function takes a queue of packets. The packets on the queue * are assumed to be properly aligned by the caller. */ int brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, uint write, uint func, uint addr, struct sk_buff_head *pktq) { bool fifo = (fix_inc == SDIOH_DATA_FIX); u32 SGCount = 0; int err_ret = 0; struct sk_buff *pkt; brcmf_dbg(TRACE, "Enter\n"); brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait); if (brcmf_pm_resume_error(sdiodev)) return -EIO; /* Claim host controller */ sdio_claim_host(sdiodev->func[func]); skb_queue_walk(pktq, pkt) { uint pkt_len = pkt->len; pkt_len += 3; pkt_len &= 0xFFFFFFFC; err_ret = brcmf_sdioh_request_data(sdiodev, write, fifo, func, addr, pkt, pkt_len); if (err_ret) { brcmf_dbg(ERROR, "%s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n", write ? "TX" : "RX", pkt, SGCount, addr, pkt_len, err_ret); } else { brcmf_dbg(TRACE, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n", write ? "TX" : "RX", pkt, SGCount, addr, pkt_len); } if (!fifo) addr += pkt_len; SGCount++; } /* Release host controller */ sdio_release_host(sdiodev->func[func]); brcmf_dbg(TRACE, "Exit\n"); return err_ret; } /* * This function takes a single DMA-able packet. */ int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, uint fix_inc, uint write, uint func, uint addr, struct sk_buff *pkt) { int status; uint pkt_len; bool fifo = (fix_inc == SDIOH_DATA_FIX); brcmf_dbg(TRACE, "Enter\n"); if (pkt == NULL) return -EINVAL; pkt_len = pkt->len; brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); if (brcmf_pm_resume_error(sdiodev)) return -EIO; /* Claim host controller */ sdio_claim_host(sdiodev->func[func]); pkt_len += 3; pkt_len &= (uint)~3; status = brcmf_sdioh_request_data(sdiodev, write, fifo, func, addr, pkt, pkt_len); if (status) { brcmf_dbg(ERROR, "%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n", write ? "TX" : "RX", pkt, addr, pkt_len, status); } else { brcmf_dbg(TRACE, "%s xfr'd %p, addr=0x%05x, len=%d\n", write ? "TX" : "RX", pkt, addr, pkt_len); } /* Release host controller */ sdio_release_host(sdiodev->func[func]); return status; } static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr) { /* read 24 bits and return valid 17 bit addr */ int i, ret; u32 scratch, regdata; __le32 scratch_le; u8 *ptr = (u8 *)&scratch_le; for (i = 0; i < 3; i++) { regdata = brcmf_sdio_regrl(sdiodev, regaddr, &ret); if (ret != 0) brcmf_dbg(ERROR, "Can't read!\n"); *ptr++ = (u8) regdata; regaddr++; } /* Only the lower 17-bits are valid */ scratch = le32_to_cpu(scratch_le); scratch &= 0x0001FFFF; return scratch; } static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev) { int err_ret; u32 fbraddr; u8 func; brcmf_dbg(TRACE, "\n"); /* Get the Card's common CIS address */ sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev, SDIO_CCCR_CIS); brcmf_dbg(INFO, "Card's Common CIS Ptr = 0x%x\n", sdiodev->func_cis_ptr[0]); /* Get the Card's function CIS (for each function) */ for (fbraddr = SDIO_FBR_BASE(1), func = 1; func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { sdiodev->func_cis_ptr[func] = brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr); brcmf_dbg(INFO, "Function %d CIS Ptr = 0x%x\n", func, sdiodev->func_cis_ptr[func]); } /* Enable Function 1 */ sdio_claim_host(sdiodev->func[1]); err_ret = sdio_enable_func(sdiodev->func[1]); sdio_release_host(sdiodev->func[1]); if (err_ret) brcmf_dbg(ERROR, "Failed to enable F1 Err: 0x%08x\n", err_ret); return false; } /* * Public entry points & extern's */ int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) { int err_ret = 0; brcmf_dbg(TRACE, "\n"); sdiodev->num_funcs = 2; sdio_claim_host(sdiodev->func[1]); err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); sdio_release_host(sdiodev->func[1]); if (err_ret) { brcmf_dbg(ERROR, "Failed to set F1 blocksize\n"); goto out; } sdio_claim_host(sdiodev->func[2]); err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); sdio_release_host(sdiodev->func[2]); if (err_ret) { brcmf_dbg(ERROR, "Failed to set F2 blocksize\n"); goto out; } brcmf_sdioh_enablefuncs(sdiodev); out: brcmf_dbg(TRACE, "Done\n"); return err_ret; } void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(TRACE, "\n"); /* Disable Function 2 */ sdio_claim_host(sdiodev->func[2]); sdio_disable_func(sdiodev->func[2]); sdio_release_host(sdiodev->func[2]); /* Disable Function 1 */ sdio_claim_host(sdiodev->func[1]); sdio_disable_func(sdiodev->func[1]); sdio_release_host(sdiodev->func[1]); } #ifdef CONFIG_BRCMFMAC_SDIO_OOB static int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev) { struct brcmf_sdio_oobirq *oobirq_entry; if (list_empty(&oobirq_lh)) { brcmf_dbg(ERROR, "no valid oob irq resource\n"); return -ENXIO; } oobirq_entry = list_first_entry(&oobirq_lh, struct brcmf_sdio_oobirq, list); sdiodev->irq = oobirq_entry->irq; sdiodev->irq_flags = oobirq_entry->flags; list_del(&oobirq_entry->list); kfree(oobirq_entry); return 0; } #else static inline int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev) { return 0; } #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ static int brcmf_ops_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int ret = 0; struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(TRACE, "func->class=%x\n", func->class); brcmf_dbg(TRACE, "sdio_vendor: 0x%04x\n", func->vendor); brcmf_dbg(TRACE, "sdio_device: 0x%04x\n", func->device); brcmf_dbg(TRACE, "Function#: 0x%04x\n", func->num); if (func->num == 1) { if (dev_get_drvdata(&func->card->dev)) { brcmf_dbg(ERROR, "card private drvdata occupied\n"); return -ENXIO; } bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); if (!bus_if) return -ENOMEM; sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); if (!sdiodev) { kfree(bus_if); return -ENOMEM; } sdiodev->func[0] = func; sdiodev->func[1] = func; sdiodev->bus_if = bus_if; bus_if->bus_priv.sdio = sdiodev; bus_if->type = SDIO_BUS; bus_if->align = BRCMF_SDALIGN; dev_set_drvdata(&func->card->dev, sdiodev); atomic_set(&sdiodev->suspend, false); init_waitqueue_head(&sdiodev->request_byte_wait); init_waitqueue_head(&sdiodev->request_word_wait); init_waitqueue_head(&sdiodev->request_chain_wait); init_waitqueue_head(&sdiodev->request_buffer_wait); } if (func->num == 2) { sdiodev = dev_get_drvdata(&func->card->dev); if ((!sdiodev) || (sdiodev->func[1]->card != func->card)) return -ENODEV; ret = brcmf_sdio_getintrcfg(sdiodev); if (ret) return ret; sdiodev->func[2] = func; bus_if = sdiodev->bus_if; sdiodev->dev = &func->dev; dev_set_drvdata(&func->dev, bus_if); brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n"); ret = brcmf_sdio_probe(sdiodev); } return ret; } static void brcmf_ops_sdio_remove(struct sdio_func *func) { struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(INFO, "func->class=%x\n", func->class); brcmf_dbg(INFO, "sdio_vendor: 0x%04x\n", func->vendor); brcmf_dbg(INFO, "sdio_device: 0x%04x\n", func->device); brcmf_dbg(INFO, "Function#: 0x%04x\n", func->num); if (func->num == 2) { bus_if = dev_get_drvdata(&func->dev); sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_remove...\n"); brcmf_sdio_remove(sdiodev); dev_set_drvdata(&func->card->dev, NULL); dev_set_drvdata(&func->dev, NULL); kfree(bus_if); kfree(sdiodev); } } #if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int brcmf_sdio_suspend(struct device *dev) { mmc_pm_flag_t sdio_flags; struct sdio_func *func = dev_to_sdio_func(dev); struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); int ret = 0; brcmf_dbg(TRACE, "\n"); atomic_set(&sdiodev->suspend, true); sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { brcmf_dbg(ERROR, "Host can't keep power while suspended\n"); return -EINVAL; } ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); if (ret) { brcmf_dbg(ERROR, "Failed to set pm_flags\n"); return ret; } brcmf_sdio_wdtmr_enable(sdiodev, false); return ret; } static int brcmf_sdio_resume(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); brcmf_sdio_wdtmr_enable(sdiodev, true); atomic_set(&sdiodev->suspend, false); return 0; } static const struct dev_pm_ops brcmf_sdio_pm_ops = { .suspend = brcmf_sdio_suspend, .resume = brcmf_sdio_resume, }; #endif /* CONFIG_PM_SLEEP */ static struct sdio_driver brcmf_sdmmc_driver = { .probe = brcmf_ops_sdio_probe, .remove = brcmf_ops_sdio_remove, .name = "brcmfmac", .id_table = brcmf_sdmmc_ids, #if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .drv = { .pm = &brcmf_sdio_pm_ops, }, #endif /* CONFIG_PM_SLEEP */ }; #ifdef CONFIG_BRCMFMAC_SDIO_OOB static int brcmf_sdio_pd_probe(struct platform_device *pdev) { struct resource *res; struct brcmf_sdio_oobirq *oobirq_entry; int i, ret; INIT_LIST_HEAD(&oobirq_lh); for (i = 0; ; i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); if (!res) break; oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq), GFP_KERNEL); if (!oobirq_entry) return -ENOMEM; oobirq_entry->irq = res->start; oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK; list_add_tail(&oobirq_entry->list, &oobirq_lh); } if (i == 0) return -ENXIO; ret = sdio_register_driver(&brcmf_sdmmc_driver); if (ret) brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret); return ret; } static struct platform_driver brcmf_sdio_pd = { .probe = brcmf_sdio_pd_probe, .driver = { .name = "brcmf_sdio_pd" } }; void brcmf_sdio_exit(void) { brcmf_dbg(TRACE, "Enter\n"); sdio_unregister_driver(&brcmf_sdmmc_driver); platform_driver_unregister(&brcmf_sdio_pd); } void brcmf_sdio_init(void) { int ret; brcmf_dbg(TRACE, "Enter\n"); ret = platform_driver_register(&brcmf_sdio_pd); if (ret) brcmf_dbg(ERROR, "platform_driver_register failed: %d\n", ret); } #else void brcmf_sdio_exit(void) { brcmf_dbg(TRACE, "Enter\n"); sdio_unregister_driver(&brcmf_sdmmc_driver); } void brcmf_sdio_init(void) { int ret; brcmf_dbg(TRACE, "Enter\n"); ret = sdio_register_driver(&brcmf_sdmmc_driver); if (ret) brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret); } #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/Makefile0000644000175000017500000000230612026211315025213 0ustar mcgrofmcgrof# # Makefile fragment for Broadcom 802.11n Networking Device Driver # # Copyright (c) 2010 Broadcom Corporation # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ccflags-y += \ -I$(obj) \ -I$(obj)/../include ccflags-y += -D__CHECK_ENDIAN__ obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ wl_cfg80211.o \ dhd_cdc.o \ dhd_common.o \ dhd_linux.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ bcmsdh.o \ bcmsdh_sdmmc.o \ sdio_chip.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o brcmfmac-$(CONFIG_BRCMDBG) += \ dhd_dbg.ocompat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h0000644000175000017500000002453612026211315025572 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _wl_cfg80211_h_ #define _wl_cfg80211_h_ struct brcmf_cfg80211_conf; struct brcmf_cfg80211_iface; struct brcmf_cfg80211_priv; struct brcmf_cfg80211_security; struct brcmf_cfg80211_ibss; #define WL_DBG_NONE 0 #define WL_DBG_CONN (1 << 5) #define WL_DBG_SCAN (1 << 4) #define WL_DBG_TRACE (1 << 3) #define WL_DBG_INFO (1 << 1) #define WL_DBG_ERR (1 << 0) #define WL_DBG_MASK ((WL_DBG_INFO | WL_DBG_ERR | WL_DBG_TRACE) | \ (WL_DBG_SCAN) | (WL_DBG_CONN)) #define WL_ERR(fmt, ...) \ do { \ if (brcmf_dbg_level & WL_DBG_ERR) { \ if (net_ratelimit()) { \ pr_err("ERROR @%s : " fmt, \ __func__, ##__VA_ARGS__); \ } \ } \ } while (0) #if (defined DEBUG) #define WL_INFO(fmt, ...) \ do { \ if (brcmf_dbg_level & WL_DBG_INFO) { \ if (net_ratelimit()) { \ pr_err("INFO @%s : " fmt, \ __func__, ##__VA_ARGS__); \ } \ } \ } while (0) #define WL_TRACE(fmt, ...) \ do { \ if (brcmf_dbg_level & WL_DBG_TRACE) { \ if (net_ratelimit()) { \ pr_err("TRACE @%s : " fmt, \ __func__, ##__VA_ARGS__); \ } \ } \ } while (0) #define WL_SCAN(fmt, ...) \ do { \ if (brcmf_dbg_level & WL_DBG_SCAN) { \ if (net_ratelimit()) { \ pr_err("SCAN @%s : " fmt, \ __func__, ##__VA_ARGS__); \ } \ } \ } while (0) #define WL_CONN(fmt, ...) \ do { \ if (brcmf_dbg_level & WL_DBG_CONN) { \ if (net_ratelimit()) { \ pr_err("CONN @%s : " fmt, \ __func__, ##__VA_ARGS__); \ } \ } \ } while (0) #else /* (defined DEBUG) */ #define WL_INFO(fmt, args...) #define WL_TRACE(fmt, args...) #define WL_SCAN(fmt, args...) #define WL_CONN(fmt, args...) #endif /* (defined DEBUG) */ #define WL_NUM_SCAN_MAX 1 #define WL_NUM_PMKIDS_MAX MAXPMKID /* will be used * for 2.6.33 kernel * or later */ #define WL_SCAN_BUF_MAX (1024 * 8) #define WL_TLV_INFO_MAX 1024 #define WL_BSS_INFO_MAX 2048 #define WL_ASSOC_INFO_MAX 512 /* * needs to grab assoc info from dongle to * report it to cfg80211 through "connect" * event */ #define WL_DCMD_LEN_MAX 1024 #define WL_EXTRA_BUF_MAX 2048 #define WL_ISCAN_BUF_MAX 2048 /* * the buf length can be BRCMF_DCMD_MAXLEN * to reduce iteration */ #define WL_ISCAN_TIMER_INTERVAL_MS 3000 #define WL_SCAN_ERSULTS_LAST (BRCMF_SCAN_RESULTS_NO_MEM+1) #define WL_AP_MAX 256 /* virtually unlimitted as long * as kernel memory allows */ #define WL_ROAM_TRIGGER_LEVEL -75 #define WL_ROAM_DELTA 20 #define WL_BEACON_TIMEOUT 3 #define WL_SCAN_CHANNEL_TIME 40 #define WL_SCAN_UNASSOC_TIME 40 #define WL_SCAN_PASSIVE_TIME 120 #define WL_ESCAN_BUF_SIZE (1024 * 64) #define WL_ESCAN_TIMER_INTERVAL_MS 8000 /* E-Scan timeout */ #define WL_ESCAN_ACTION_START 1 #define WL_ESCAN_ACTION_CONTINUE 2 #define WL_ESCAN_ACTION_ABORT 3 /* dongle status */ enum wl_status { WL_STATUS_READY, WL_STATUS_SCANNING, WL_STATUS_SCAN_ABORTING, WL_STATUS_CONNECTING, WL_STATUS_CONNECTED }; /* wi-fi mode */ enum wl_mode { WL_MODE_BSS, WL_MODE_IBSS, WL_MODE_AP }; /* dongle profile list */ enum wl_prof_list { WL_PROF_MODE, WL_PROF_SSID, WL_PROF_SEC, WL_PROF_IBSS, WL_PROF_BAND, WL_PROF_BSSID, WL_PROF_ACT, WL_PROF_BEACONINT, WL_PROF_DTIMPERIOD }; /* dongle iscan state */ enum wl_iscan_state { WL_ISCAN_STATE_IDLE, WL_ISCAN_STATE_SCANING }; /* dongle configuration */ struct brcmf_cfg80211_conf { u32 mode; /* adhoc , infrastructure or ap */ u32 frag_threshold; u32 rts_threshold; u32 retry_short; u32 retry_long; s32 tx_power; struct ieee80211_channel channel; }; /* cfg80211 main event loop */ struct brcmf_cfg80211_event_loop { s32(*handler[BRCMF_E_LAST]) (struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, const struct brcmf_event_msg *e, void *data); }; /* representing interface of cfg80211 plane */ struct brcmf_cfg80211_iface { struct brcmf_cfg80211_priv *cfg_priv; }; struct brcmf_cfg80211_dev { void *driver_data; /* to store cfg80211 object information */ }; /* basic structure of scan request */ struct brcmf_cfg80211_scan_req { struct brcmf_ssid_le ssid_le; }; /* basic structure of information element */ struct brcmf_cfg80211_ie { u16 offset; u8 buf[WL_TLV_INFO_MAX]; }; /* event queue for cfg80211 main event */ struct brcmf_cfg80211_event_q { struct list_head evt_q_list; u32 etype; struct brcmf_event_msg emsg; s8 edata[1]; }; /* security information with currently associated ap */ struct brcmf_cfg80211_security { u32 wpa_versions; u32 auth_type; u32 cipher_pairwise; u32 cipher_group; u32 wpa_auth; }; /* ibss information for currently joined ibss network */ struct brcmf_cfg80211_ibss { u8 beacon_interval; /* in millisecond */ u8 atim; /* in millisecond */ s8 join_only; u8 band; u8 channel; }; /* dongle profile */ struct brcmf_cfg80211_profile { u32 mode; struct brcmf_ssid ssid; u8 bssid[ETH_ALEN]; u16 beacon_interval; u8 dtim_period; struct brcmf_cfg80211_security sec; struct brcmf_cfg80211_ibss ibss; s32 band; }; /* dongle iscan event loop */ struct brcmf_cfg80211_iscan_eloop { s32 (*handler[WL_SCAN_ERSULTS_LAST]) (struct brcmf_cfg80211_priv *cfg_priv); }; /* dongle iscan controller */ struct brcmf_cfg80211_iscan_ctrl { struct net_device *ndev; struct timer_list timer; u32 timer_ms; u32 timer_on; s32 state; struct work_struct work; struct brcmf_cfg80211_iscan_eloop el; void *data; s8 dcmd_buf[BRCMF_DCMD_SMLEN]; s8 scan_buf[WL_ISCAN_BUF_MAX]; }; /* association inform */ struct brcmf_cfg80211_connect_info { u8 *req_ie; s32 req_ie_len; u8 *resp_ie; s32 resp_ie_len; }; /* assoc ie length */ struct brcmf_cfg80211_assoc_ielen_le { __le32 req_len; __le32 resp_len; }; /* wpa2 pmk list */ struct brcmf_cfg80211_pmk_list { struct pmkid_list pmkids; struct pmkid foo[MAXPMKID - 1]; }; /* dongle escan state */ enum wl_escan_state { WL_ESCAN_STATE_IDLE, WL_ESCAN_STATE_SCANNING }; struct escan_info { u32 escan_state; u8 escan_buf[WL_ESCAN_BUF_SIZE]; struct wiphy *wiphy; struct net_device *ndev; }; /* dongle private data of cfg80211 interface */ struct brcmf_cfg80211_priv { struct wireless_dev *wdev; /* representing wl cfg80211 device */ struct brcmf_cfg80211_conf *conf; /* dongle configuration */ struct cfg80211_scan_request *scan_request; /* scan request object */ struct brcmf_cfg80211_event_loop el; /* main event loop */ struct list_head evt_q_list; /* used for event queue */ spinlock_t evt_q_lock; /* for event queue synchronization */ struct mutex usr_sync; /* maily for dongle up/down synchronization */ struct brcmf_scan_results *bss_list; /* bss_list holding scanned ap information */ struct brcmf_scan_results *scan_results; struct brcmf_cfg80211_scan_req *scan_req_int; /* scan request object for internal purpose */ struct wl_cfg80211_bss_info *bss_info; /* bss information for cfg80211 layer */ struct brcmf_cfg80211_ie ie; /* information element object for internal purpose */ struct brcmf_cfg80211_profile *profile; /* holding dongle profile */ struct brcmf_cfg80211_iscan_ctrl *iscan; /* iscan controller */ struct brcmf_cfg80211_connect_info conn_info; /* association info */ struct brcmf_cfg80211_pmk_list *pmk_list; /* wpa2 pmk list */ struct work_struct event_work; /* event handler work struct */ unsigned long status; /* current dongle status */ void *pub; u32 channel; /* current channel */ bool iscan_on; /* iscan on/off switch */ bool iscan_kickstart; /* indicate iscan already started */ bool active_scan; /* current scan mode */ bool ibss_starter; /* indicates this sta is ibss starter */ bool link_up; /* link/connection up flag */ bool pwr_save; /* indicate whether dongle to support power save mode */ bool dongle_up; /* indicate whether dongle up or not */ bool roam_on; /* on/off switch for dongle self-roaming */ bool scan_tried; /* indicates if first scan attempted */ u8 *dcmd_buf; /* dcmd buffer */ u8 *extra_buf; /* maily to grab assoc information */ struct dentry *debugfsdir; bool escan_on; /* escan on/off switch */ struct escan_info escan_info; /* escan information */ struct timer_list escan_timeout; /* Timer for catch scan timeout */ struct work_struct escan_timeout_work; /* scan timeout worker */ u8 *escan_ioctl_buf; u8 ci[0] __aligned(NETDEV_ALIGN); }; static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_priv *w) { return w->wdev->wiphy; } static inline struct brcmf_cfg80211_priv *wiphy_to_cfg(struct wiphy *w) { return (struct brcmf_cfg80211_priv *)(wiphy_priv(w)); } static inline struct brcmf_cfg80211_priv *wdev_to_cfg(struct wireless_dev *wd) { return (struct brcmf_cfg80211_priv *)(wdev_priv(wd)); } static inline struct net_device *cfg_to_ndev(struct brcmf_cfg80211_priv *cfg) { return cfg->wdev->netdev; } static inline struct brcmf_cfg80211_priv *ndev_to_cfg(struct net_device *ndev) { return wdev_to_cfg(ndev->ieee80211_ptr); } #define iscan_to_cfg(i) ((struct brcmf_cfg80211_priv *)(i->data)) #define cfg_to_iscan(w) (w->iscan) static inline struct brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_priv *cfg) { return &cfg->conn_info; } extern struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev, struct device *busdev, void *data); extern void brcmf_cfg80211_detach(struct brcmf_cfg80211_dev *cfg); /* event handler from dongle */ extern void brcmf_cfg80211_event(struct net_device *ndev, const struct brcmf_event_msg *e, void *data); extern s32 brcmf_cfg80211_up(struct brcmf_cfg80211_dev *cfg_dev); extern s32 brcmf_cfg80211_down(struct brcmf_cfg80211_dev *cfg_dev); #endif /* _wl_cfg80211_h_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h0000644000175000017500000000524312026211315025361 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USB_RDL_H #define _USB_RDL_H /* Control messages: bRequest values */ #define DL_GETSTATE 0 /* returns the rdl_state_t struct */ #define DL_CHECK_CRC 1 /* currently unused */ #define DL_GO 2 /* execute downloaded image */ #define DL_START 3 /* initialize dl state */ #define DL_REBOOT 4 /* reboot the device in 2 seconds */ #define DL_GETVER 5 /* returns the bootrom_id_t struct */ #define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset * event to occur in 2 seconds. It is the * responsibility of the downloaded code to * clear this event */ #define DL_EXEC 7 /* jump to a supplied address */ #define DL_RESETCFG 8 /* To support single enum on dongle * - Not used by bootloader */ #define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup * if resp unavailable */ /* states */ #define DL_WAITING 0 /* waiting to rx first pkt */ #define DL_READY 1 /* hdr was good, waiting for more of the * compressed image */ #define DL_BAD_HDR 2 /* hdr was corrupted */ #define DL_BAD_CRC 3 /* compressed image was corrupted */ #define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */ #define DL_START_FAIL 5 /* failed to initialize correctly */ #define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM * value */ #define DL_IMAGE_TOOBIG 7 /* download image too big (exceeds DATA_START * for rdl) */ struct rdl_state_le { __le32 state; __le32 bytes; }; struct bootrom_id_le { __le32 chip; /* Chip id */ __le32 chiprev; /* Chip rev */ __le32 ramsize; /* Size of RAM */ __le32 remapbase; /* Current remap base address */ __le32 boardtype; /* Type of board */ __le32 boardrev; /* Board revision */ }; #define RDL_CHUNK 1500 /* size of each dl transfer */ #define TRX_OFFSETS_DLFWLEN_IDX 0 #define TRX_OFFSETS_JUMPTO_IDX 1 #define TRX_OFFSETS_NVM_LEN_IDX 2 #define TRX_OFFSETS_DLBASE_IDX 0 #endif /* _USB_RDL_H */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/usb.h0000644000175000017500000000320312026211315024512 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef BRCMFMAC_USB_H #define BRCMFMAC_USB_H enum brcmf_usb_state { BCMFMAC_USB_STATE_DL_PENDING, BCMFMAC_USB_STATE_DL_DONE, BCMFMAC_USB_STATE_UP, BCMFMAC_USB_STATE_DOWN, BCMFMAC_USB_STATE_PNP_FWDL, BCMFMAC_USB_STATE_DISCONNECT, BCMFMAC_USB_STATE_SLEEP }; enum brcmf_usb_pnp_state { BCMFMAC_USB_PNP_DISCONNECT, BCMFMAC_USB_PNP_SLEEP, BCMFMAC_USB_PNP_RESUME, }; struct brcmf_stats { u32 tx_ctlpkts; u32 tx_ctlerrs; u32 rx_ctlpkts; u32 rx_ctlerrs; }; struct brcmf_usbdev { struct brcmf_bus *bus; struct brcmf_usbdev_info *devinfo; enum brcmf_usb_state state; struct brcmf_stats stats; int ntxq, nrxq, rxsize; u32 bus_mtu; int devid; int chiprev; /* chip revsion number */ }; /* IO Request Block (IRB) */ struct brcmf_usbreq { struct list_head list; struct brcmf_usbdev_info *devinfo; struct urb *urb; struct sk_buff *skb; }; #endif /* BRCMFMAC_USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h0000644000175000017500000002226412026211315025724 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCM_SDH_H_ #define _BRCM_SDH_H_ #include #define SDIO_FUNC_0 0 #define SDIO_FUNC_1 1 #define SDIO_FUNC_2 2 #define SDIOD_FBR_SIZE 0x100 /* io_en */ #define SDIO_FUNC_ENABLE_1 0x02 #define SDIO_FUNC_ENABLE_2 0x04 /* io_rdys */ #define SDIO_FUNC_READY_1 0x02 #define SDIO_FUNC_READY_2 0x04 /* intr_status */ #define INTR_STATUS_FUNC1 0x2 #define INTR_STATUS_FUNC2 0x4 /* Maximum number of I/O funcs */ #define SDIOD_MAX_IOFUNCS 7 /* mask of register map */ #define REG_F0_REG_MASK 0x7FF #define REG_F1_MISC_MASK 0x1FFFF /* as of sdiod rev 0, supports 3 functions */ #define SBSDIO_NUM_FUNCTION 3 /* function 0 vendor specific CCCR registers */ #define SDIO_CCCR_BRCM_SEPINT 0xf2 #define SDIO_SEPINT_MASK 0x01 #define SDIO_SEPINT_OE 0x02 #define SDIO_SEPINT_ACT_HI 0x04 /* function 1 miscellaneous registers */ /* sprom command and status */ #define SBSDIO_SPROM_CS 0x10000 /* sprom info register */ #define SBSDIO_SPROM_INFO 0x10001 /* sprom indirect access data byte 0 */ #define SBSDIO_SPROM_DATA_LOW 0x10002 /* sprom indirect access data byte 1 */ #define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access addr byte 0 */ #define SBSDIO_SPROM_ADDR_LOW 0x10004 /* sprom indirect access addr byte 0 */ #define SBSDIO_SPROM_ADDR_HIGH 0x10005 /* xtal_pu (gpio) output */ #define SBSDIO_CHIP_CTRL_DATA 0x10006 /* xtal_pu (gpio) enable */ #define SBSDIO_CHIP_CTRL_EN 0x10007 /* rev < 7, watermark for sdio device */ #define SBSDIO_WATERMARK 0x10008 /* control busy signal generation */ #define SBSDIO_DEVICE_CTL 0x10009 /* SB Address Window Low (b15) */ #define SBSDIO_FUNC1_SBADDRLOW 0x1000A /* SB Address Window Mid (b23:b16) */ #define SBSDIO_FUNC1_SBADDRMID 0x1000B /* SB Address Window High (b31:b24) */ #define SBSDIO_FUNC1_SBADDRHIGH 0x1000C /* Frame Control (frame term/abort) */ #define SBSDIO_FUNC1_FRAMECTRL 0x1000D /* ChipClockCSR (ALP/HT ctl/status) */ #define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E /* SdioPullUp (on cmd, d0-d2) */ #define SBSDIO_FUNC1_SDIOPULLUP 0x1000F /* Write Frame Byte Count Low */ #define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 /* Write Frame Byte Count High */ #define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A /* Read Frame Byte Count Low */ #define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B /* Read Frame Byte Count High */ #define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C #define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ #define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001C /* f1 misc register end */ /* function 1 OCP space */ /* sb offset addr is <= 15 bits, 32k */ #define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF #define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 /* with b15, maps to 32-bit SB access */ #define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 /* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ #define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ #define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ #define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ /* Address bits from SBADDR regs */ #define SBSDIO_SBWINDOW_MASK 0xffff8000 #define SDIOH_READ 0 /* Read request */ #define SDIOH_WRITE 1 /* Write request */ #define SDIOH_DATA_FIX 0 /* Fixed addressing */ #define SDIOH_DATA_INC 1 /* Incremental addressing */ /* internal return code */ #define SUCCESS 0 #define ERROR 1 /* Packet alignment for most efficient SDIO (can change based on platform) */ #define BRCMF_SDALIGN (1 << 6) /* watchdog polling interval in ms */ #define BRCMF_WD_POLL_MS 10 struct brcmf_sdreg { int func; int offset; int value; }; struct brcmf_sdio; struct brcmf_sdio_dev { struct sdio_func *func[SDIO_MAX_FUNCS]; u8 num_funcs; /* Supported funcs on client */ u32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; u32 sbwad; /* Save backplane window address */ void *bus; atomic_t suspend; /* suspend flag */ wait_queue_head_t request_byte_wait; wait_queue_head_t request_word_wait; wait_queue_head_t request_chain_wait; wait_queue_head_t request_buffer_wait; struct device *dev; struct brcmf_bus *bus_if; #ifdef CONFIG_BRCMFMAC_SDIO_OOB unsigned int irq; /* oob interrupt number */ unsigned long irq_flags; /* board specific oob flags */ bool irq_en; /* irq enable flags */ spinlock_t irq_en_lock; bool irq_wake; /* irq wake enable flags */ #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ }; /* Register/deregister interrupt handler. */ extern int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev); extern int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev); /* sdio device register access interface */ extern u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); extern u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); extern void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, int *ret); extern void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, int *ret); /* Buffer transfer to/from device (client) core via cmd53. * fn: function number * addr: backplane address (i.e. >= regsva from attach) * flags: backplane width, address increment, sync/async * buf: pointer to memory data buffer * nbytes: number of bytes to transfer to/from buf * pkt: pointer to packet associated with buf (if any) * complete: callback function for command completion (async only) * handle: handle for completion callback (first arg in callback) * Returns 0 or error code. * NOTE: Async operation is not currently supported. */ extern int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff *pkt); extern int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes); extern int brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff *pkt); extern int brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes); extern int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff_head *pktq); /* Flags bits */ /* Four-byte target (backplane) width (vs. two-byte) */ #define SDIO_REQ_4BYTE 0x1 /* Fixed address (FIFO) (vs. incrementing address) */ #define SDIO_REQ_FIXED 0x2 /* Async request (vs. sync request) */ #define SDIO_REQ_ASYNC 0x4 /* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). * rw: read or write (0/1) * addr: direct SDIO address * buf: pointer to memory data buffer * nbytes: number of bytes to transfer to/from buf * Returns 0 or error code. */ extern int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, u8 *buf, uint nbytes); /* Issue an abort to the specified function */ extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn); /* platform specific/high level functions */ extern int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); extern int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev); extern int brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address); /* attach, return handler on success, NULL if failed. * The handler shall be provided by all subsequent calls. No local cache * cfghdl points to the starting address of pci device mapped memory */ extern int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev); extern void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev); /* read or write one byte using cmd52 */ extern int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc, uint addr, u8 *byte); /* read or write 2/4 bytes using cmd53 */ extern int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc, uint addr, u32 *word, uint nbyte); /* read or write any buffer using cmd53 */ extern int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, uint fix_inc, uint rw, uint fnc_num, u32 addr, struct sk_buff *pkt); extern int brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, uint write, uint func, uint addr, struct sk_buff_head *pktq); /* Watchdog timer interface for pm ops */ extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable); extern void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev); extern void brcmf_sdbrcm_disconnect(void *ptr); extern void brcmf_sdbrcm_isr(void *arg); extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick); #endif /* _BRCM_SDH_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h0000644000175000017500000001053412026211315025667 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCMFMAC_SDIO_CHIP_H_ #define _BRCMFMAC_SDIO_CHIP_H_ /* * Core reg address translation. * Both macro's returns a 32 bits byte address on the backplane bus. */ #define CORE_CC_REG(base, field) \ (base + offsetof(struct chipcregs, field)) #define CORE_BUS_REG(base, field) \ (base + offsetof(struct sdpcmd_regs, field)) #define CORE_SB(base, field) \ (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) /* SDIO function 1 register CHIPCLKCSR */ /* Force ALP request to backplane */ #define SBSDIO_FORCE_ALP 0x01 /* Force HT request to backplane */ #define SBSDIO_FORCE_HT 0x02 /* Force ILP request to backplane */ #define SBSDIO_FORCE_ILP 0x04 /* Make ALP ready (power up xtal) */ #define SBSDIO_ALP_AVAIL_REQ 0x08 /* Make HT ready (power up PLL) */ #define SBSDIO_HT_AVAIL_REQ 0x10 /* Squelch clock requests from HW */ #define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 /* Status: ALP is ready */ #define SBSDIO_ALP_AVAIL 0x40 /* Status: HT is ready */ #define SBSDIO_HT_AVAIL 0x80 #define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) #define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) #define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) #define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) #define SBSDIO_CLKAV(regval, alponly) \ (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) #define BRCMF_MAX_CORENUM 6 struct chip_core_info { u16 id; u16 rev; u32 base; u32 wrapbase; u32 caps; u32 cib; }; struct chip_info { u32 chip; u32 chiprev; u32 socitype; /* core info */ /* always put chipcommon core at 0, bus core at 1 */ struct chip_core_info c_inf[BRCMF_MAX_CORENUM]; u32 pmurev; u32 pmucaps; u32 ramsize; bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid); u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid); void (*coredisable)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid); void (*resetcore)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid); }; struct sbconfig { u32 PAD[2]; u32 sbipsflag; /* initiator port ocp slave flag */ u32 PAD[3]; u32 sbtpsflag; /* target port ocp slave flag */ u32 PAD[11]; u32 sbtmerrloga; /* (sonics >= 2.3) */ u32 PAD; u32 sbtmerrlog; /* (sonics >= 2.3) */ u32 PAD[3]; u32 sbadmatch3; /* address match3 */ u32 PAD; u32 sbadmatch2; /* address match2 */ u32 PAD; u32 sbadmatch1; /* address match1 */ u32 PAD[7]; u32 sbimstate; /* initiator agent state */ u32 sbintvec; /* interrupt mask */ u32 sbtmstatelow; /* target state */ u32 sbtmstatehigh; /* target state */ u32 sbbwa0; /* bandwidth allocation table0 */ u32 PAD; u32 sbimconfiglow; /* initiator configuration */ u32 sbimconfighigh; /* initiator configuration */ u32 sbadmatch0; /* address match0 */ u32 PAD; u32 sbtmconfiglow; /* target configuration */ u32 sbtmconfighigh; /* target configuration */ u32 sbbconfig; /* broadcast configuration */ u32 PAD; u32 sbbstate; /* broadcast state */ u32 PAD[3]; u32 sbactcnfg; /* activate configuration */ u32 PAD[3]; u32 sbflagst; /* current sbflags */ u32 PAD[3]; u32 sbidlow; /* identification */ u32 sbidhigh; /* identification */ }; extern int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, struct chip_info **ci_ptr, u32 regs); extern void brcmf_sdio_chip_detach(struct chip_info **ci_ptr); extern void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u32 drivestrength); extern u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); #endif /* _BRCMFMAC_SDIO_CHIP_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h0000644000175000017500000000361512026211315025712 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCMF_PROTO_H_ #define _BRCMF_PROTO_H_ /* * Exported from the brcmf protocol module (brcmf_cdc) */ /* Linkage, sets prot link and updates hdrlen in pub */ extern int brcmf_proto_attach(struct brcmf_pub *drvr); /* Unlink, frees allocated protocol memory (including brcmf_proto) */ extern void brcmf_proto_detach(struct brcmf_pub *drvr); /* Initialize protocol: sync w/dongle state. * Sets dongle media info (iswl, drv_version, mac address). */ extern int brcmf_proto_init(struct brcmf_pub *drvr); /* Stop protocol: sync w/dongle state. */ extern void brcmf_proto_stop(struct brcmf_pub *drvr); /* Add any protocol-specific data header. * Caller must reserve prot_hdrlen prepend space. */ extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, struct sk_buff *txp); /* Use protocol to issue command to dongle */ extern int brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx, struct brcmf_dcmd *dcmd, int len); extern int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr); extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); #endif /* _BRCMF_PROTO_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd.h0000644000175000017500000005021012026211315024460 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /**************** * Common types * */ #ifndef _BRCMF_H_ #define _BRCMF_H_ #define BRCMF_VERSION_STR "4.218.248.5" /******************************************************************************* * IO codes that are interpreted by dongle firmware ******************************************************************************/ #define BRCMF_C_UP 2 #define BRCMF_C_SET_PROMISC 10 #define BRCMF_C_GET_RATE 12 #define BRCMF_C_GET_INFRA 19 #define BRCMF_C_SET_INFRA 20 #define BRCMF_C_GET_AUTH 21 #define BRCMF_C_SET_AUTH 22 #define BRCMF_C_GET_BSSID 23 #define BRCMF_C_GET_SSID 25 #define BRCMF_C_SET_SSID 26 #define BRCMF_C_GET_CHANNEL 29 #define BRCMF_C_GET_SRL 31 #define BRCMF_C_GET_LRL 33 #define BRCMF_C_GET_RADIO 37 #define BRCMF_C_SET_RADIO 38 #define BRCMF_C_GET_PHYTYPE 39 #define BRCMF_C_SET_KEY 45 #define BRCMF_C_SET_PASSIVE_SCAN 49 #define BRCMF_C_SCAN 50 #define BRCMF_C_SCAN_RESULTS 51 #define BRCMF_C_DISASSOC 52 #define BRCMF_C_REASSOC 53 #define BRCMF_C_SET_ROAM_TRIGGER 55 #define BRCMF_C_SET_ROAM_DELTA 57 #define BRCMF_C_GET_DTIMPRD 77 #define BRCMF_C_SET_COUNTRY 84 #define BRCMF_C_GET_PM 85 #define BRCMF_C_SET_PM 86 #define BRCMF_C_GET_AP 117 #define BRCMF_C_SET_AP 118 #define BRCMF_C_GET_RSSI 127 #define BRCMF_C_GET_WSEC 133 #define BRCMF_C_SET_WSEC 134 #define BRCMF_C_GET_PHY_NOISE 135 #define BRCMF_C_GET_BSS_INFO 136 #define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 #define BRCMF_C_SET_SCAN_UNASSOC_TIME 187 #define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 #define BRCMF_C_GET_VALID_CHANNELS 217 #define BRCMF_C_GET_KEY_PRIMARY 235 #define BRCMF_C_SET_KEY_PRIMARY 236 #define BRCMF_C_SET_SCAN_PASSIVE_TIME 258 #define BRCMF_C_GET_VAR 262 #define BRCMF_C_SET_VAR 263 /* phy types (returned by WLC_GET_PHYTPE) */ #define WLC_PHY_TYPE_A 0 #define WLC_PHY_TYPE_B 1 #define WLC_PHY_TYPE_G 2 #define WLC_PHY_TYPE_N 4 #define WLC_PHY_TYPE_LP 5 #define WLC_PHY_TYPE_SSN 6 #define WLC_PHY_TYPE_HT 7 #define WLC_PHY_TYPE_LCN 8 #define WLC_PHY_TYPE_NULL 0xf #define BRCMF_EVENTING_MASK_LEN 16 #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 #define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */ /* size of brcmf_scan_params not including variable length array */ #define BRCMF_SCAN_PARAMS_FIXED_SIZE 64 /* masks for channel and ssid count */ #define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff #define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16 #define BRCMF_SCAN_ACTION_START 1 #define BRCMF_SCAN_ACTION_CONTINUE 2 #define WL_SCAN_ACTION_ABORT 3 #define BRCMF_ISCAN_REQ_VERSION 1 /* brcmf_iscan_results status values */ #define BRCMF_SCAN_RESULTS_SUCCESS 0 #define BRCMF_SCAN_RESULTS_PARTIAL 1 #define BRCMF_SCAN_RESULTS_PENDING 2 #define BRCMF_SCAN_RESULTS_ABORTED 3 #define BRCMF_SCAN_RESULTS_NO_MEM 4 /* Indicates this key is using soft encrypt */ #define WL_SOFT_KEY (1 << 0) /* primary (ie tx) key */ #define BRCMF_PRIMARY_KEY (1 << 1) /* Reserved for backward compat */ #define WL_KF_RES_4 (1 << 4) /* Reserved for backward compat */ #define WL_KF_RES_5 (1 << 5) /* Indicates a group key for a IBSS PEER */ #define WL_IBSS_PEER_GROUP_KEY (1 << 6) /* For supporting multiple interfaces */ #define BRCMF_MAX_IFS 16 #define DOT11_BSSTYPE_ANY 2 #define DOT11_MAX_DEFAULT_KEYS 4 #define BRCMF_EVENT_MSG_LINK 0x01 #define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 #define BRCMF_EVENT_MSG_GROUP 0x04 #define BRCMF_ESCAN_REQ_VERSION 1 #define WLC_BSS_RSSI_ON_CHANNEL 0x0002 struct brcmf_event_msg { __be16 version; __be16 flags; __be32 event_type; __be32 status; __be32 reason; __be32 auth_type; __be32 datalen; u8 addr[ETH_ALEN]; char ifname[IFNAMSIZ]; u8 ifidx; u8 bsscfgidx; } __packed; struct brcm_ethhdr { u16 subtype; u16 length; u8 version; u8 oui[3]; u16 usr_subtype; } __packed; struct brcmf_event { struct ethhdr eth; struct brcm_ethhdr hdr; struct brcmf_event_msg msg; } __packed; /* event codes sent by the dongle to this driver */ #define BRCMF_E_SET_SSID 0 #define BRCMF_E_JOIN 1 #define BRCMF_E_START 2 #define BRCMF_E_AUTH 3 #define BRCMF_E_AUTH_IND 4 #define BRCMF_E_DEAUTH 5 #define BRCMF_E_DEAUTH_IND 6 #define BRCMF_E_ASSOC 7 #define BRCMF_E_ASSOC_IND 8 #define BRCMF_E_REASSOC 9 #define BRCMF_E_REASSOC_IND 10 #define BRCMF_E_DISASSOC 11 #define BRCMF_E_DISASSOC_IND 12 #define BRCMF_E_QUIET_START 13 #define BRCMF_E_QUIET_END 14 #define BRCMF_E_BEACON_RX 15 #define BRCMF_E_LINK 16 #define BRCMF_E_MIC_ERROR 17 #define BRCMF_E_NDIS_LINK 18 #define BRCMF_E_ROAM 19 #define BRCMF_E_TXFAIL 20 #define BRCMF_E_PMKID_CACHE 21 #define BRCMF_E_RETROGRADE_TSF 22 #define BRCMF_E_PRUNE 23 #define BRCMF_E_AUTOAUTH 24 #define BRCMF_E_EAPOL_MSG 25 #define BRCMF_E_SCAN_COMPLETE 26 #define BRCMF_E_ADDTS_IND 27 #define BRCMF_E_DELTS_IND 28 #define BRCMF_E_BCNSENT_IND 29 #define BRCMF_E_BCNRX_MSG 30 #define BRCMF_E_BCNLOST_MSG 31 #define BRCMF_E_ROAM_PREP 32 #define BRCMF_E_PFN_NET_FOUND 33 #define BRCMF_E_PFN_NET_LOST 34 #define BRCMF_E_RESET_COMPLETE 35 #define BRCMF_E_JOIN_START 36 #define BRCMF_E_ROAM_START 37 #define BRCMF_E_ASSOC_START 38 #define BRCMF_E_IBSS_ASSOC 39 #define BRCMF_E_RADIO 40 #define BRCMF_E_PSM_WATCHDOG 41 #define BRCMF_E_PROBREQ_MSG 44 #define BRCMF_E_SCAN_CONFIRM_IND 45 #define BRCMF_E_PSK_SUP 46 #define BRCMF_E_COUNTRY_CODE_CHANGED 47 #define BRCMF_E_EXCEEDED_MEDIUM_TIME 48 #define BRCMF_E_ICV_ERROR 49 #define BRCMF_E_UNICAST_DECODE_ERROR 50 #define BRCMF_E_MULTICAST_DECODE_ERROR 51 #define BRCMF_E_TRACE 52 #define BRCMF_E_IF 54 #define BRCMF_E_RSSI 56 #define BRCMF_E_PFN_SCAN_COMPLETE 57 #define BRCMF_E_EXTLOG_MSG 58 #define BRCMF_E_ACTION_FRAME 59 #define BRCMF_E_ACTION_FRAME_COMPLETE 60 #define BRCMF_E_PRE_ASSOC_IND 61 #define BRCMF_E_PRE_REASSOC_IND 62 #define BRCMF_E_CHANNEL_ADOPTED 63 #define BRCMF_E_AP_STARTED 64 #define BRCMF_E_DFS_AP_STOP 65 #define BRCMF_E_DFS_AP_RESUME 66 #define BRCMF_E_RESERVED1 67 #define BRCMF_E_RESERVED2 68 #define BRCMF_E_ESCAN_RESULT 69 #define BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE 70 #define BRCMF_E_DCS_REQUEST 73 #define BRCMF_E_FIFO_CREDIT_MAP 74 #define BRCMF_E_LAST 75 #define BRCMF_E_STATUS_SUCCESS 0 #define BRCMF_E_STATUS_FAIL 1 #define BRCMF_E_STATUS_TIMEOUT 2 #define BRCMF_E_STATUS_NO_NETWORKS 3 #define BRCMF_E_STATUS_ABORT 4 #define BRCMF_E_STATUS_NO_ACK 5 #define BRCMF_E_STATUS_UNSOLICITED 6 #define BRCMF_E_STATUS_ATTEMPT 7 #define BRCMF_E_STATUS_PARTIAL 8 #define BRCMF_E_STATUS_NEWSCAN 9 #define BRCMF_E_STATUS_NEWASSOC 10 #define BRCMF_E_STATUS_11HQUIET 11 #define BRCMF_E_STATUS_SUPPRESS 12 #define BRCMF_E_STATUS_NOCHANS 13 #define BRCMF_E_STATUS_CS_ABORT 15 #define BRCMF_E_STATUS_ERROR 16 #define BRCMF_E_REASON_INITIAL_ASSOC 0 #define BRCMF_E_REASON_LOW_RSSI 1 #define BRCMF_E_REASON_DEAUTH 2 #define BRCMF_E_REASON_DISASSOC 3 #define BRCMF_E_REASON_BCNS_LOST 4 #define BRCMF_E_REASON_MINTXRATE 9 #define BRCMF_E_REASON_TXFAIL 10 #define BRCMF_E_REASON_FAST_ROAM_FAILED 5 #define BRCMF_E_REASON_DIRECTED_ROAM 6 #define BRCMF_E_REASON_TSPEC_REJECTED 7 #define BRCMF_E_REASON_BETTER_AP 8 #define BRCMF_E_PRUNE_ENCR_MISMATCH 1 #define BRCMF_E_PRUNE_BCAST_BSSID 2 #define BRCMF_E_PRUNE_MAC_DENY 3 #define BRCMF_E_PRUNE_MAC_NA 4 #define BRCMF_E_PRUNE_REG_PASSV 5 #define BRCMF_E_PRUNE_SPCT_MGMT 6 #define BRCMF_E_PRUNE_RADAR 7 #define BRCMF_E_RSN_MISMATCH 8 #define BRCMF_E_PRUNE_NO_COMMON_RATES 9 #define BRCMF_E_PRUNE_BASIC_RATES 10 #define BRCMF_E_PRUNE_CIPHER_NA 12 #define BRCMF_E_PRUNE_KNOWN_STA 13 #define BRCMF_E_PRUNE_WDS_PEER 15 #define BRCMF_E_PRUNE_QBSS_LOAD 16 #define BRCMF_E_PRUNE_HOME_AP 17 #define BRCMF_E_SUP_OTHER 0 #define BRCMF_E_SUP_DECRYPT_KEY_DATA 1 #define BRCMF_E_SUP_BAD_UCAST_WEP128 2 #define BRCMF_E_SUP_BAD_UCAST_WEP40 3 #define BRCMF_E_SUP_UNSUP_KEY_LEN 4 #define BRCMF_E_SUP_PW_KEY_CIPHER 5 #define BRCMF_E_SUP_MSG3_TOO_MANY_IE 6 #define BRCMF_E_SUP_MSG3_IE_MISMATCH 7 #define BRCMF_E_SUP_NO_INSTALL_FLAG 8 #define BRCMF_E_SUP_MSG3_NO_GTK 9 #define BRCMF_E_SUP_GRP_KEY_CIPHER 10 #define BRCMF_E_SUP_GRP_MSG1_NO_GTK 11 #define BRCMF_E_SUP_GTK_DECRYPT_FAIL 12 #define BRCMF_E_SUP_SEND_FAIL 13 #define BRCMF_E_SUP_DEAUTH 14 #define BRCMF_E_IF_ADD 1 #define BRCMF_E_IF_DEL 2 #define BRCMF_E_IF_CHANGE 3 #define BRCMF_E_IF_ROLE_STA 0 #define BRCMF_E_IF_ROLE_AP 1 #define BRCMF_E_IF_ROLE_WDS 2 #define BRCMF_E_LINK_BCN_LOSS 1 #define BRCMF_E_LINK_DISASSOC 2 #define BRCMF_E_LINK_ASSOC_REC 3 #define BRCMF_E_LINK_BSSCFG_DIS 4 /* Pattern matching filter. Specifies an offset within received packets to * start matching, the pattern to match, the size of the pattern, and a bitmask * that indicates which bits within the pattern should be matched. */ struct brcmf_pkt_filter_pattern_le { /* * Offset within received packet to start pattern matching. * Offset '0' is the first byte of the ethernet header. */ __le32 offset; /* Size of the pattern. Bitmask must be the same size.*/ __le32 size_bytes; /* * Variable length mask and pattern data. mask starts at offset 0. * Pattern immediately follows mask. */ u8 mask_and_pattern[1]; }; /* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */ struct brcmf_pkt_filter_le { __le32 id; /* Unique filter id, specified by app. */ __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */ __le32 negate_match; /* Negate the result of filter matches */ union { /* Filter definitions */ struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */ } u; }; /* IOVAR "pkt_filter_enable" parameter. */ struct brcmf_pkt_filter_enable_le { __le32 id; /* Unique filter id */ __le32 enable; /* Enable/disable bool */ }; /* BSS info structure * Applications MUST CHECK ie_offset field and length field to access IEs and * next bss_info structure in a vector (in struct brcmf_scan_results) */ struct brcmf_bss_info_le { __le32 version; /* version field */ __le32 length; /* byte length of data in this record, * starting at version and including IEs */ u8 BSSID[ETH_ALEN]; __le16 beacon_period; /* units are Kusec */ __le16 capability; /* Capability information */ u8 SSID_len; u8 SSID[32]; struct { __le32 count; /* # rates in this set */ u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ } rateset; /* supported rates */ __le16 chanspec; /* chanspec for bss */ __le16 atim_window; /* units are Kusec */ u8 dtim_period; /* DTIM period */ __le16 RSSI; /* receive signal strength (in dBm) */ s8 phy_noise; /* noise (in dBm) */ u8 n_cap; /* BSS is 802.11N Capable */ /* 802.11N BSS Capabilities (based on HT_CAP_*): */ __le32 nbss_cap; u8 ctl_ch; /* 802.11N BSS control channel number */ __le32 reserved32[1]; /* Reserved for expansion of BSS properties */ u8 flags; /* flags */ u8 reserved[3]; /* Reserved for expansion of BSS properties */ u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ __le16 ie_offset; /* offset at which IEs start, from beginning */ __le32 ie_length; /* byte length of Information Elements */ __le16 SNR; /* average SNR of during frame reception */ /* Add new fields here */ /* variable length Information Elements */ }; struct brcm_rateset_le { /* # rates in this set */ __le32 count; /* rates in 500kbps units w/hi bit set if basic */ u8 rates[WL_NUMRATES]; }; struct brcmf_ssid { u32 SSID_len; unsigned char SSID[32]; }; struct brcmf_ssid_le { __le32 SSID_len; unsigned char SSID[32]; }; struct brcmf_scan_params_le { struct brcmf_ssid_le ssid_le; /* default: {0, ""} */ u8 bssid[ETH_ALEN]; /* default: bcast */ s8 bss_type; /* default: any, * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT */ u8 scan_type; /* flags, 0 use default */ __le32 nprobes; /* -1 use default, number of probes per channel */ __le32 active_time; /* -1 use default, dwell time per channel for * active scanning */ __le32 passive_time; /* -1 use default, dwell time per channel * for passive scanning */ __le32 home_time; /* -1 use default, dwell time for the * home channel between channel scans */ __le32 channel_num; /* count of channels and ssids that follow * * low half is count of channels in * channel_list, 0 means default (use all * available channels) * * high half is entries in struct brcmf_ssid * array that follows channel_list, aligned for * s32 (4 bytes) meaning an odd channel count * implies a 2-byte pad between end of * channel_list and first ssid * * if ssid count is zero, single ssid in the * fixed parameter portion is assumed, otherwise * ssid in the fixed portion is ignored */ __le16 channel_list[1]; /* list of chanspecs */ }; /* incremental scan struct */ struct brcmf_iscan_params_le { __le32 version; __le16 action; __le16 scan_duration; struct brcmf_scan_params_le params_le; }; struct brcmf_scan_results { u32 buflen; u32 version; u32 count; struct brcmf_bss_info_le bss_info_le[]; }; struct brcmf_scan_results_le { __le32 buflen; __le32 version; __le32 count; }; struct brcmf_escan_params_le { __le32 version; __le16 action; __le16 sync_id; struct brcmf_scan_params_le params_le; }; struct brcmf_escan_result_le { __le32 buflen; __le32 version; __le16 sync_id; __le16 bss_count; struct brcmf_bss_info_le bss_info_le; }; #define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \ sizeof(struct brcmf_bss_info_le)) /* used for association with a specific BSSID and chanspec list */ struct brcmf_assoc_params_le { /* 00:00:00:00:00:00: broadcast scan */ u8 bssid[ETH_ALEN]; /* 0: all available channels, otherwise count of chanspecs in * chanspec_list */ __le32 chanspec_num; /* list of chanspecs */ __le16 chanspec_list[1]; }; /* used for join with or without a specific bssid and channel list */ struct brcmf_join_params { struct brcmf_ssid_le ssid_le; struct brcmf_assoc_params_le params_le; }; /* incremental scan results struct */ struct brcmf_iscan_results { union { u32 status; __le32 status_le; }; union { struct brcmf_scan_results results; struct brcmf_scan_results_le results_le; }; }; /* size of brcmf_iscan_results not including variable length array */ #define BRCMF_ISCAN_RESULTS_FIXED_SIZE \ (sizeof(struct brcmf_scan_results) + \ offsetof(struct brcmf_iscan_results, results)) struct brcmf_wsec_key { u32 index; /* key index */ u32 len; /* key length */ u8 data[WLAN_MAX_KEY_LEN]; /* key data */ u32 pad_1[18]; u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ u32 flags; /* misc flags */ u32 pad_2[3]; u32 iv_initialized; /* has IV been initialized already? */ u32 pad_3; /* Rx IV */ struct { u32 hi; /* upper 32 bits of IV */ u16 lo; /* lower 16 bits of IV */ } rxiv; u32 pad_4[2]; u8 ea[ETH_ALEN]; /* per station */ }; /* * dongle requires same struct as above but with fields in little endian order */ struct brcmf_wsec_key_le { __le32 index; /* key index */ __le32 len; /* key length */ u8 data[WLAN_MAX_KEY_LEN]; /* key data */ __le32 pad_1[18]; __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ __le32 flags; /* misc flags */ __le32 pad_2[3]; __le32 iv_initialized; /* has IV been initialized already? */ __le32 pad_3; /* Rx IV */ struct { __le32 hi; /* upper 32 bits of IV */ __le16 lo; /* lower 16 bits of IV */ } rxiv; __le32 pad_4[2]; u8 ea[ETH_ALEN]; /* per station */ }; /* Used to get specific STA parameters */ struct brcmf_scb_val_le { __le32 val; u8 ea[ETH_ALEN]; }; /* channel encoding */ struct brcmf_channel_info_le { __le32 hw_channel; __le32 target_channel; __le32 scan_channel; }; /* Bus independent dongle command */ struct brcmf_dcmd { uint cmd; /* common dongle cmd definition */ void *buf; /* pointer to user buffer */ uint len; /* length of user buffer */ u8 set; /* get or set request (optional) */ uint used; /* bytes read or written (optional) */ uint needed; /* bytes needed (optional) */ }; /* Forward decls for struct brcmf_pub (see below) */ struct brcmf_proto; /* device communication protocol info */ struct brcmf_cfg80211_dev; /* cfg80211 device info */ /* Common structure for module and instance linkage */ struct brcmf_pub { /* Linkage ponters */ struct brcmf_bus *bus_if; struct brcmf_proto *prot; struct brcmf_cfg80211_dev *config; struct device *dev; /* fullmac dongle device pointer */ /* Internal brcmf items */ uint hdrlen; /* Total BRCMF header length (proto + bus) */ uint rxsz; /* Rx buffer size bus module should use */ u8 wme_dp; /* wme discard priority */ /* Dongle media info */ bool iswl; /* Dongle-resident driver is wl */ unsigned long drv_version; /* Version of dongle-resident driver */ u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ /* Additional stats for the bus level */ /* Multicast data packets sent to dongle */ unsigned long tx_multicast; /* Packets flushed due to unscheduled sendup thread */ unsigned long rx_flushed; /* Number of times dpc scheduled by watchdog timer */ unsigned long wd_dpc_sched; /* Number of flow control pkts recvd */ unsigned long fc_packets; /* Last error return */ int bcmerror; /* Last error from dongle */ int dongle_error; /* Suspend disable flag flag */ int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */ int in_suspend; /* flag set to 1 when early suspend called */ int dtim_skip; /* dtim skip , default 0 means wake each dtim */ /* Pkt filter defination */ char *pktfilter[100]; int pktfilter_count; u8 country_code[BRCM_CNTRY_BUF_SZ]; char eventmask[BRCMF_EVENTING_MASK_LEN]; struct brcmf_if *iflist[BRCMF_MAX_IFS]; struct mutex proto_block; struct work_struct setmacaddr_work; struct work_struct multicast_work; u8 macvalue[ETH_ALEN]; atomic_t pend_8021x_cnt; #ifdef DEBUG struct dentry *dbgfs_dir; #endif }; struct brcmf_if_event { u8 ifidx; u8 action; u8 flags; u8 bssidx; }; struct bcmevent_name { uint event; const char *name; }; extern const struct bcmevent_name bcmevent_names[]; extern uint brcmf_c_mkiovar(char *name, char *data, uint datalen, char *buf, uint len); extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev); extern s32 brcmf_exec_dcmd(struct net_device *dev, u32 cmd, void *arg, u32 len); extern int brcmf_netlink_dcmd(struct net_device *ndev, struct brcmf_dcmd *dcmd); /* Return pointer to interface name */ extern char *brcmf_ifname(struct brcmf_pub *drvr, int idx); /* Query dongle */ extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); #ifdef DEBUG extern int brcmf_write_to_file(struct brcmf_pub *drvr, const u8 *buf, int size); #endif /* DEBUG */ extern int brcmf_ifname2idx(struct brcmf_pub *drvr, char *name); extern int brcmf_c_host_event(struct brcmf_pub *drvr, int *idx, void *pktdata, struct brcmf_event_msg *, void **data_ptr); extern void brcmf_del_if(struct brcmf_pub *drvr, int ifidx); /* Send packet to dongle via data channel */ extern int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx,\ struct sk_buff *pkt); extern void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg); extern void brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg, int enable, int master_mode); #define BRCMF_DCMD_SMLEN 256 /* "small" cmd buffer required */ #define BRCMF_DCMD_MEDLEN 1536 /* "med" cmd buffer required */ #define BRCMF_DCMD_MAXLEN 8192 /* max length cmd buffer required */ #endif /* _BRCMF_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h0000644000175000017500000001122512026211315025277 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCMF_DBG_H_ #define _BRCMF_DBG_H_ /* message levels */ #define BRCMF_ERROR_VAL 0x0001 #define BRCMF_TRACE_VAL 0x0002 #define BRCMF_INFO_VAL 0x0004 #define BRCMF_DATA_VAL 0x0008 #define BRCMF_CTL_VAL 0x0010 #define BRCMF_TIMER_VAL 0x0020 #define BRCMF_HDRS_VAL 0x0040 #define BRCMF_BYTES_VAL 0x0080 #define BRCMF_INTR_VAL 0x0100 #define BRCMF_GLOM_VAL 0x0400 #define BRCMF_EVENT_VAL 0x0800 #define BRCMF_BTA_VAL 0x1000 #define BRCMF_ISCAN_VAL 0x2000 #if defined(DEBUG) #define brcmf_dbg(level, fmt, ...) \ do { \ if (BRCMF_ERROR_VAL == BRCMF_##level##_VAL) { \ if (brcmf_msg_level & BRCMF_##level##_VAL) { \ if (net_ratelimit()) \ pr_debug("%s: " fmt, \ __func__, ##__VA_ARGS__); \ } \ } else { \ if (brcmf_msg_level & BRCMF_##level##_VAL) { \ pr_debug("%s: " fmt, \ __func__, ##__VA_ARGS__); \ } \ } \ } while (0) #define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL) #define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL) #define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL) #define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL) #define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL) #else /* (defined DEBUG) || (defined DEBUG) */ #define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__) #define BRCMF_DATA_ON() 0 #define BRCMF_CTL_ON() 0 #define BRCMF_HDRS_ON() 0 #define BRCMF_BYTES_ON() 0 #define BRCMF_GLOM_ON() 0 #endif /* defined(DEBUG) */ #define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ do { \ if (test) \ brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \ } while (0) extern int brcmf_msg_level; /* * hold counter variables used in brcmfmac sdio driver. */ struct brcmf_sdio_count { uint intrcount; /* Count of device interrupt callbacks */ uint lastintrs; /* Count as of last watchdog timer */ uint pollcnt; /* Count of active polls */ uint regfails; /* Count of R_REG failures */ uint tx_sderrs; /* Count of tx attempts with sd errors */ uint fcqueued; /* Tx packets that got queued */ uint rxrtx; /* Count of rtx requests (NAK to dongle) */ uint rx_toolong; /* Receive frames too long to receive */ uint rxc_errors; /* SDIO errors when reading control frames */ uint rx_hdrfail; /* SDIO errors on header reads */ uint rx_badhdr; /* Bad received headers (roosync?) */ uint rx_badseq; /* Mismatched rx sequence number */ uint fc_rcvd; /* Number of flow-control events received */ uint fc_xoff; /* Number which turned on flow-control */ uint fc_xon; /* Number which turned off flow-control */ uint rxglomfail; /* Failed deglom attempts */ uint rxglomframes; /* Number of glom frames (superframes) */ uint rxglompkts; /* Number of packets from glom frames */ uint f2rxhdrs; /* Number of header reads */ uint f2rxdata; /* Number of frame data reads */ uint f2txdata; /* Number of f2 frame writes */ uint f1regdata; /* Number of f1 register accesses */ uint tickcnt; /* Number of watchdog been schedule */ ulong tx_ctlerrs; /* Err of sending ctrl frames */ ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ ulong rx_readahead_cnt; /* packets where header read-ahead was used */ }; struct brcmf_pub; #ifdef DEBUG void brcmf_debugfs_init(void); void brcmf_debugfs_exit(void); int brcmf_debugfs_attach(struct brcmf_pub *drvr); void brcmf_debugfs_detach(struct brcmf_pub *drvr); struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, struct brcmf_sdio_count *sdcnt); #else static inline void brcmf_debugfs_init(void) { } static inline void brcmf_debugfs_exit(void) { } static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) { return 0; } static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) { } #endif #endif /* _BRCMF_DBG_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c0000644000175000017500000000720312026211315025273 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "dhd.h" #include "dhd_bus.h" #include "dhd_dbg.h" static struct dentry *root_folder; void brcmf_debugfs_init(void) { root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); if (IS_ERR(root_folder)) root_folder = NULL; } void brcmf_debugfs_exit(void) { if (!root_folder) return; debugfs_remove_recursive(root_folder); root_folder = NULL; } int brcmf_debugfs_attach(struct brcmf_pub *drvr) { if (!root_folder) return -ENODEV; drvr->dbgfs_dir = debugfs_create_dir(dev_name(drvr->dev), root_folder); return PTR_RET(drvr->dbgfs_dir); } void brcmf_debugfs_detach(struct brcmf_pub *drvr) { if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) debugfs_remove_recursive(drvr->dbgfs_dir); } struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { return drvr->dbgfs_dir; } static ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data, size_t count, loff_t *ppos) { struct brcmf_sdio_count *sdcnt = f->private_data; char buf[750]; int res; /* only allow read from start */ if (*ppos > 0) return 0; res = scnprintf(buf, sizeof(buf), "intrcount: %u\nlastintrs: %u\n" "pollcnt: %u\nregfails: %u\n" "tx_sderrs: %u\nfcqueued: %u\n" "rxrtx: %u\nrx_toolong: %u\n" "rxc_errors: %u\nrx_hdrfail: %u\n" "rx_badhdr: %u\nrx_badseq: %u\n" "fc_rcvd: %u\nfc_xoff: %u\n" "fc_xon: %u\nrxglomfail: %u\n" "rxglomframes: %u\nrxglompkts: %u\n" "f2rxhdrs: %u\nf2rxdata: %u\n" "f2txdata: %u\nf1regdata: %u\n" "tickcnt: %u\ntx_ctlerrs: %lu\n" "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" "rx_ctlpkts: %lu\nrx_readahead: %lu\n", sdcnt->intrcount, sdcnt->lastintrs, sdcnt->pollcnt, sdcnt->regfails, sdcnt->tx_sderrs, sdcnt->fcqueued, sdcnt->rxrtx, sdcnt->rx_toolong, sdcnt->rxc_errors, sdcnt->rx_hdrfail, sdcnt->rx_badhdr, sdcnt->rx_badseq, sdcnt->fc_rcvd, sdcnt->fc_xoff, sdcnt->fc_xon, sdcnt->rxglomfail, sdcnt->rxglomframes, sdcnt->rxglompkts, sdcnt->f2rxhdrs, sdcnt->f2rxdata, sdcnt->f2txdata, sdcnt->f1regdata, sdcnt->tickcnt, sdcnt->tx_ctlerrs, sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); return simple_read_from_buffer(data, count, ppos, buf, res); } static const struct file_operations brcmf_debugfs_sdio_counter_ops = { .owner = THIS_MODULE, .open = simple_open, .read = brcmf_debugfs_sdio_counter_read }; void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, struct brcmf_sdio_count *sdcnt) { struct dentry *dentry = drvr->dbgfs_dir; if (!IS_ERR_OR_NULL(dentry)) debugfs_create_file("counters", S_IRUGO, dentry, sdcnt, &brcmf_debugfs_sdio_counter_ops); } compat-drivers-2012-09-18/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h0000644000175000017500000001055512026211315025341 0ustar mcgrofmcgrof/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BRCMF_BUS_H_ #define _BRCMF_BUS_H_ /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_DOWN, /* Not ready for frame transfers */ BRCMF_BUS_LOAD, /* Download access only (CPU reset) */ BRCMF_BUS_DATA /* Ready for frame transfers */ }; struct dngl_stats { unsigned long rx_packets; /* total packets received */ unsigned long tx_packets; /* total packets transmitted */ unsigned long rx_bytes; /* total bytes received */ unsigned long tx_bytes; /* total bytes transmitted */ unsigned long rx_errors; /* bad packets received */ unsigned long tx_errors; /* packet transmit problems */ unsigned long rx_dropped; /* packets dropped by dongle */ unsigned long tx_dropped; /* packets dropped by dongle */ unsigned long multicast; /* multicast packets received */ }; struct brcmf_bus_dcmd { char *name; char *param; int param_len; struct list_head list; }; /* interface structure between common and bus layer */ struct brcmf_bus { u8 type; /* bus type */ union { struct brcmf_sdio_dev *sdio; struct brcmf_usbdev *usb; } bus_priv; struct brcmf_pub *drvr; /* pointer to driver pub structure brcmf_pub */ enum brcmf_bus_state state; uint maxctl; /* Max size rxctl request from proto to bus */ bool drvr_up; /* Status flag of driver up/down */ unsigned long tx_realloc; /* Tx packets realloced for headroom */ struct dngl_stats dstats; /* Stats for dongle-based data */ u8 align; /* bus alignment requirement */ struct list_head dcmd_list; /* interface functions pointers */ /* Stop bus module: clear pending frames, disable data flow */ void (*brcmf_bus_stop)(struct device *); /* Initialize bus module: prepare for communication w/dongle */ int (*brcmf_bus_init)(struct device *); /* Send a data frame to the dongle. Callee disposes of txp. */ int (*brcmf_bus_txdata)(struct device *, struct sk_buff *); /* Send/receive a control message to/from the dongle. * Expects caller to enforce a single outstanding transaction. */ int (*brcmf_bus_txctl)(struct device *, unsigned char *, uint); int (*brcmf_bus_rxctl)(struct device *, unsigned char *, uint); }; /* * interface functions from common layer */ /* Remove any protocol-specific data header. */ extern int brcmf_proto_hdrpull(struct device *dev, int *ifidx, struct sk_buff *rxp); extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec); /* Receive frame for delivery to OS. Callee disposes of rxp. */ extern void brcmf_rx_frame(struct device *dev, int ifidx, struct sk_buff_head *rxlist); static inline void brcmf_rx_packet(struct device *dev, int ifidx, struct sk_buff *pkt) { struct sk_buff_head q; skb_queue_head_init(&q); skb_queue_tail(&q, pkt); brcmf_rx_frame(dev, ifidx, &q); } /* Indication from bus module regarding presence/insertion of dongle. */ extern int brcmf_attach(uint bus_hdrlen, struct device *dev); /* Indication from bus module regarding removal/absence of dongle */ extern void brcmf_detach(struct device *dev); /* Indication from bus module to change flow-control state */ extern void brcmf_txflowblock(struct device *dev, bool state); /* Notify tx completion */ extern void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); extern int brcmf_bus_start(struct device *dev); extern int brcmf_add_if(struct device *dev, int ifidx, char *name, u8 *mac_addr); #ifdef CONFIG_BRCMFMAC_SDIO extern void brcmf_sdio_exit(void); extern void brcmf_sdio_init(void); #endif #ifdef CONFIG_BRCMFMAC_USB extern void brcmf_usb_exit(void); extern void brcmf_usb_init(void); #endif #endif /* _BRCMF_BUS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/0000755000175000017500000000000012026211315022236 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/b43legacy/main.c0000644000175000017500000033131012026211315023327 0ustar mcgrofmcgrof/* * * Broadcom B43legacy wireless driver * * Copyright (c) 2005 Martin Langer * Copyright (c) 2005-2008 Stefano Brivio * Copyright (c) 2005, 2006 Michael Buesch * Copyright (c) 2005 Danny van Dyk * Copyright (c) 2005 Andreas Jaggi * Copyright (c) 2007 Larry Finger * * Some parts of the code in this file are derived from the ipw2200 * driver Copyright(c) 2003 - 2004 Intel Corporation. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "b43legacy.h" #include "main.h" #include "debugfs.h" #include "phy.h" #include "dma.h" #include "pio.h" #include "sysfs.h" #include "xmit.h" #include "radio.h" MODULE_DESCRIPTION("Broadcom B43legacy wireless driver"); MODULE_AUTHOR("Martin Langer"); MODULE_AUTHOR("Stefano Brivio"); MODULE_AUTHOR("Michael Buesch"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("b43legacy/ucode2.fw"); MODULE_FIRMWARE("b43legacy/ucode4.fw"); #if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) static int modparam_pio; module_param_named(pio, modparam_pio, int, 0444); MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode"); #elif defined(CONFIG_B43LEGACY_DMA) # define modparam_pio 0 #elif defined(CONFIG_B43LEGACY_PIO) # define modparam_pio 1 #endif static int modparam_bad_frames_preempt; module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames" " Preemption"); static char modparam_fwpostfix[16]; module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444); MODULE_PARM_DESC(fwpostfix, "Postfix for the firmware files to load."); /* The following table supports BCM4301, BCM4303 and BCM4306/2 devices. */ static const struct ssb_device_id b43legacy_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 2), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 4), SSB_DEVTABLE_END }; MODULE_DEVICE_TABLE(ssb, b43legacy_ssb_tbl); /* Channel and ratetables are shared for all devices. * They can't be const, because ieee80211 puts some precalculated * data in there. This data is the same for all devices, so we don't * get concurrency issues */ #define RATETAB_ENT(_rateid, _flags) \ { \ .bitrate = B43legacy_RATE_TO_100KBPS(_rateid), \ .hw_value = (_rateid), \ .flags = (_flags), \ } /* * NOTE: When changing this, sync with xmit.c's * b43legacy_plcp_get_bitrate_idx_* functions! */ static struct ieee80211_rate __b43legacy_ratetable[] = { RATETAB_ENT(B43legacy_CCK_RATE_1MB, 0), RATETAB_ENT(B43legacy_CCK_RATE_2MB, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(B43legacy_CCK_RATE_5MB, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(B43legacy_CCK_RATE_11MB, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(B43legacy_OFDM_RATE_6MB, 0), RATETAB_ENT(B43legacy_OFDM_RATE_9MB, 0), RATETAB_ENT(B43legacy_OFDM_RATE_12MB, 0), RATETAB_ENT(B43legacy_OFDM_RATE_18MB, 0), RATETAB_ENT(B43legacy_OFDM_RATE_24MB, 0), RATETAB_ENT(B43legacy_OFDM_RATE_36MB, 0), RATETAB_ENT(B43legacy_OFDM_RATE_48MB, 0), RATETAB_ENT(B43legacy_OFDM_RATE_54MB, 0), }; #define b43legacy_b_ratetable (__b43legacy_ratetable + 0) #define b43legacy_b_ratetable_size 4 #define b43legacy_g_ratetable (__b43legacy_ratetable + 0) #define b43legacy_g_ratetable_size 12 #define CHANTAB_ENT(_chanid, _freq) \ { \ .center_freq = (_freq), \ .hw_value = (_chanid), \ } static struct ieee80211_channel b43legacy_bg_chantable[] = { CHANTAB_ENT(1, 2412), CHANTAB_ENT(2, 2417), CHANTAB_ENT(3, 2422), CHANTAB_ENT(4, 2427), CHANTAB_ENT(5, 2432), CHANTAB_ENT(6, 2437), CHANTAB_ENT(7, 2442), CHANTAB_ENT(8, 2447), CHANTAB_ENT(9, 2452), CHANTAB_ENT(10, 2457), CHANTAB_ENT(11, 2462), CHANTAB_ENT(12, 2467), CHANTAB_ENT(13, 2472), CHANTAB_ENT(14, 2484), }; static struct ieee80211_supported_band b43legacy_band_2GHz_BPHY = { .channels = b43legacy_bg_chantable, .n_channels = ARRAY_SIZE(b43legacy_bg_chantable), .bitrates = b43legacy_b_ratetable, .n_bitrates = b43legacy_b_ratetable_size, }; static struct ieee80211_supported_band b43legacy_band_2GHz_GPHY = { .channels = b43legacy_bg_chantable, .n_channels = ARRAY_SIZE(b43legacy_bg_chantable), .bitrates = b43legacy_g_ratetable, .n_bitrates = b43legacy_g_ratetable_size, }; static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev); static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev); static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev); static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev); static int b43legacy_ratelimit(struct b43legacy_wl *wl) { if (!wl || !wl->current_dev) return 1; if (b43legacy_status(wl->current_dev) < B43legacy_STAT_STARTED) return 1; /* We are up and running. * Ratelimit the messages to avoid DoS over the net. */ return net_ratelimit(); } void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; if (!b43legacy_ratelimit(wl)) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_INFO "b43legacy-%s: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; if (!b43legacy_ratelimit(wl)) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_ERR "b43legacy-%s ERROR: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; if (!b43legacy_ratelimit(wl)) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_WARNING "b43legacy-%s warning: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } #if B43legacy_DEBUG void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_DEBUG "b43legacy-%s debug: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } #endif /* DEBUG */ static void b43legacy_ram_write(struct b43legacy_wldev *dev, u16 offset, u32 val) { u32 status; B43legacy_WARN_ON(offset % 4 != 0); status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); if (status & B43legacy_MACCTL_BE) val = swab32(val); b43legacy_write32(dev, B43legacy_MMIO_RAM_CONTROL, offset); mmiowb(); b43legacy_write32(dev, B43legacy_MMIO_RAM_DATA, val); } static inline void b43legacy_shm_control_word(struct b43legacy_wldev *dev, u16 routing, u16 offset) { u32 control; /* "offset" is the WORD offset. */ control = routing; control <<= 16; control |= offset; b43legacy_write32(dev, B43legacy_MMIO_SHM_CONTROL, control); } u32 b43legacy_shm_read32(struct b43legacy_wldev *dev, u16 routing, u16 offset) { u32 ret; if (routing == B43legacy_SHM_SHARED) { B43legacy_WARN_ON((offset & 0x0001) != 0); if (offset & 0x0003) { /* Unaligned access */ b43legacy_shm_control_word(dev, routing, offset >> 2); ret = b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA_UNALIGNED); ret <<= 16; b43legacy_shm_control_word(dev, routing, (offset >> 2) + 1); ret |= b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA); return ret; } offset >>= 2; } b43legacy_shm_control_word(dev, routing, offset); ret = b43legacy_read32(dev, B43legacy_MMIO_SHM_DATA); return ret; } u16 b43legacy_shm_read16(struct b43legacy_wldev *dev, u16 routing, u16 offset) { u16 ret; if (routing == B43legacy_SHM_SHARED) { B43legacy_WARN_ON((offset & 0x0001) != 0); if (offset & 0x0003) { /* Unaligned access */ b43legacy_shm_control_word(dev, routing, offset >> 2); ret = b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA_UNALIGNED); return ret; } offset >>= 2; } b43legacy_shm_control_word(dev, routing, offset); ret = b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA); return ret; } void b43legacy_shm_write32(struct b43legacy_wldev *dev, u16 routing, u16 offset, u32 value) { if (routing == B43legacy_SHM_SHARED) { B43legacy_WARN_ON((offset & 0x0001) != 0); if (offset & 0x0003) { /* Unaligned access */ b43legacy_shm_control_word(dev, routing, offset >> 2); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA_UNALIGNED, (value >> 16) & 0xffff); mmiowb(); b43legacy_shm_control_word(dev, routing, (offset >> 2) + 1); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA, value & 0xffff); return; } offset >>= 2; } b43legacy_shm_control_word(dev, routing, offset); mmiowb(); b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, value); } void b43legacy_shm_write16(struct b43legacy_wldev *dev, u16 routing, u16 offset, u16 value) { if (routing == B43legacy_SHM_SHARED) { B43legacy_WARN_ON((offset & 0x0001) != 0); if (offset & 0x0003) { /* Unaligned access */ b43legacy_shm_control_word(dev, routing, offset >> 2); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA_UNALIGNED, value); return; } offset >>= 2; } b43legacy_shm_control_word(dev, routing, offset); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA, value); } /* Read HostFlags */ u32 b43legacy_hf_read(struct b43legacy_wldev *dev) { u32 ret; ret = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_HOSTFHI); ret <<= 16; ret |= b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_HOSTFLO); return ret; } /* Write HostFlags */ void b43legacy_hf_write(struct b43legacy_wldev *dev, u32 value) { b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_HOSTFLO, (value & 0x0000FFFF)); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_HOSTFHI, ((value & 0xFFFF0000) >> 16)); } void b43legacy_tsf_read(struct b43legacy_wldev *dev, u64 *tsf) { /* We need to be careful. As we read the TSF from multiple * registers, we should take care of register overflows. * In theory, the whole tsf read process should be atomic. * We try to be atomic here, by restaring the read process, * if any of the high registers changed (overflew). */ if (dev->dev->id.revision >= 3) { u32 low; u32 high; u32 high2; do { high = b43legacy_read32(dev, B43legacy_MMIO_REV3PLUS_TSF_HIGH); low = b43legacy_read32(dev, B43legacy_MMIO_REV3PLUS_TSF_LOW); high2 = b43legacy_read32(dev, B43legacy_MMIO_REV3PLUS_TSF_HIGH); } while (unlikely(high != high2)); *tsf = high; *tsf <<= 32; *tsf |= low; } else { u64 tmp; u16 v0; u16 v1; u16 v2; u16 v3; u16 test1; u16 test2; u16 test3; do { v3 = b43legacy_read16(dev, B43legacy_MMIO_TSF_3); v2 = b43legacy_read16(dev, B43legacy_MMIO_TSF_2); v1 = b43legacy_read16(dev, B43legacy_MMIO_TSF_1); v0 = b43legacy_read16(dev, B43legacy_MMIO_TSF_0); test3 = b43legacy_read16(dev, B43legacy_MMIO_TSF_3); test2 = b43legacy_read16(dev, B43legacy_MMIO_TSF_2); test1 = b43legacy_read16(dev, B43legacy_MMIO_TSF_1); } while (v3 != test3 || v2 != test2 || v1 != test1); *tsf = v3; *tsf <<= 48; tmp = v2; tmp <<= 32; *tsf |= tmp; tmp = v1; tmp <<= 16; *tsf |= tmp; *tsf |= v0; } } static void b43legacy_time_lock(struct b43legacy_wldev *dev) { u32 status; status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); status |= B43legacy_MACCTL_TBTTHOLD; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); mmiowb(); } static void b43legacy_time_unlock(struct b43legacy_wldev *dev) { u32 status; status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); status &= ~B43legacy_MACCTL_TBTTHOLD; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); } static void b43legacy_tsf_write_locked(struct b43legacy_wldev *dev, u64 tsf) { /* Be careful with the in-progress timer. * First zero out the low register, so we have a full * register-overflow duration to complete the operation. */ if (dev->dev->id.revision >= 3) { u32 lo = (tsf & 0x00000000FFFFFFFFULL); u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32; b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_LOW, 0); mmiowb(); b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_HIGH, hi); mmiowb(); b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_LOW, lo); } else { u16 v0 = (tsf & 0x000000000000FFFFULL); u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16; u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32; u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48; b43legacy_write16(dev, B43legacy_MMIO_TSF_0, 0); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_TSF_3, v3); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_TSF_2, v2); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_TSF_1, v1); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_TSF_0, v0); } } void b43legacy_tsf_write(struct b43legacy_wldev *dev, u64 tsf) { b43legacy_time_lock(dev); b43legacy_tsf_write_locked(dev, tsf); b43legacy_time_unlock(dev); } static void b43legacy_macfilter_set(struct b43legacy_wldev *dev, u16 offset, const u8 *mac) { static const u8 zero_addr[ETH_ALEN] = { 0 }; u16 data; if (!mac) mac = zero_addr; offset |= 0x0020; b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_CONTROL, offset); data = mac[0]; data |= mac[1] << 8; b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); data = mac[2]; data |= mac[3] << 8; b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); data = mac[4]; data |= mac[5] << 8; b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); } static void b43legacy_write_mac_bssid_templates(struct b43legacy_wldev *dev) { static const u8 zero_addr[ETH_ALEN] = { 0 }; const u8 *mac = dev->wl->mac_addr; const u8 *bssid = dev->wl->bssid; u8 mac_bssid[ETH_ALEN * 2]; int i; u32 tmp; if (!bssid) bssid = zero_addr; if (!mac) mac = zero_addr; b43legacy_macfilter_set(dev, B43legacy_MACFILTER_BSSID, bssid); memcpy(mac_bssid, mac, ETH_ALEN); memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); /* Write our MAC address and BSSID to template ram */ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) { tmp = (u32)(mac_bssid[i + 0]); tmp |= (u32)(mac_bssid[i + 1]) << 8; tmp |= (u32)(mac_bssid[i + 2]) << 16; tmp |= (u32)(mac_bssid[i + 3]) << 24; b43legacy_ram_write(dev, 0x20 + i, tmp); b43legacy_ram_write(dev, 0x78 + i, tmp); b43legacy_ram_write(dev, 0x478 + i, tmp); } } static void b43legacy_upload_card_macaddress(struct b43legacy_wldev *dev) { b43legacy_write_mac_bssid_templates(dev); b43legacy_macfilter_set(dev, B43legacy_MACFILTER_SELF, dev->wl->mac_addr); } static void b43legacy_set_slot_time(struct b43legacy_wldev *dev, u16 slot_time) { /* slot_time is in usec. */ if (dev->phy.type != B43legacy_PHYTYPE_G) return; b43legacy_write16(dev, 0x684, 510 + slot_time); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0010, slot_time); } static void b43legacy_short_slot_timing_enable(struct b43legacy_wldev *dev) { b43legacy_set_slot_time(dev, 9); } static void b43legacy_short_slot_timing_disable(struct b43legacy_wldev *dev) { b43legacy_set_slot_time(dev, 20); } /* Synchronize IRQ top- and bottom-half. * IRQs must be masked before calling this. * This must not be called with the irq_lock held. */ static void b43legacy_synchronize_irq(struct b43legacy_wldev *dev) { synchronize_irq(dev->dev->irq); tasklet_kill(&dev->isr_tasklet); } /* DummyTransmission function, as documented on * http://bcm-specs.sipsolutions.net/DummyTransmission */ void b43legacy_dummy_transmission(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; unsigned int i; unsigned int max_loop; u16 value; u32 buffer[5] = { 0x00000000, 0x00D40000, 0x00000000, 0x01000000, 0x00000000, }; switch (phy->type) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: max_loop = 0xFA; buffer[0] = 0x000B846E; break; default: B43legacy_BUG_ON(1); return; } for (i = 0; i < 5; i++) b43legacy_ram_write(dev, i * 4, buffer[i]); /* dummy read follows */ b43legacy_read32(dev, B43legacy_MMIO_MACCTL); b43legacy_write16(dev, 0x0568, 0x0000); b43legacy_write16(dev, 0x07C0, 0x0000); b43legacy_write16(dev, 0x050C, 0x0000); b43legacy_write16(dev, 0x0508, 0x0000); b43legacy_write16(dev, 0x050A, 0x0000); b43legacy_write16(dev, 0x054C, 0x0000); b43legacy_write16(dev, 0x056A, 0x0014); b43legacy_write16(dev, 0x0568, 0x0826); b43legacy_write16(dev, 0x0500, 0x0000); b43legacy_write16(dev, 0x0502, 0x0030); if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) b43legacy_radio_write16(dev, 0x0051, 0x0017); for (i = 0x00; i < max_loop; i++) { value = b43legacy_read16(dev, 0x050E); if (value & 0x0080) break; udelay(10); } for (i = 0x00; i < 0x0A; i++) { value = b43legacy_read16(dev, 0x050E); if (value & 0x0400) break; udelay(10); } for (i = 0x00; i < 0x0A; i++) { value = b43legacy_read16(dev, 0x0690); if (!(value & 0x0100)) break; udelay(10); } if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) b43legacy_radio_write16(dev, 0x0051, 0x0037); } /* Turn the Analog ON/OFF */ static void b43legacy_switch_analog(struct b43legacy_wldev *dev, int on) { b43legacy_write16(dev, B43legacy_MMIO_PHY0, on ? 0 : 0xF4); } void b43legacy_wireless_core_reset(struct b43legacy_wldev *dev, u32 flags) { u32 tmslow; u32 macctl; flags |= B43legacy_TMSLOW_PHYCLKEN; flags |= B43legacy_TMSLOW_PHYRESET; ssb_device_enable(dev->dev, flags); msleep(2); /* Wait for the PLL to turn on. */ /* Now take the PHY out of Reset again */ tmslow = ssb_read32(dev->dev, SSB_TMSLOW); tmslow |= SSB_TMSLOW_FGC; tmslow &= ~B43legacy_TMSLOW_PHYRESET; ssb_write32(dev->dev, SSB_TMSLOW, tmslow); ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ msleep(1); tmslow &= ~SSB_TMSLOW_FGC; ssb_write32(dev->dev, SSB_TMSLOW, tmslow); ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ msleep(1); /* Turn Analog ON */ b43legacy_switch_analog(dev, 1); macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); macctl &= ~B43legacy_MACCTL_GMODE; if (flags & B43legacy_TMSLOW_GMODE) { macctl |= B43legacy_MACCTL_GMODE; dev->phy.gmode = true; } else dev->phy.gmode = false; macctl |= B43legacy_MACCTL_IHR_ENABLED; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); } static void handle_irq_transmit_status(struct b43legacy_wldev *dev) { u32 v0; u32 v1; u16 tmp; struct b43legacy_txstatus stat; while (1) { v0 = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_0); if (!(v0 & 0x00000001)) break; v1 = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_1); stat.cookie = (v0 >> 16); stat.seq = (v1 & 0x0000FFFF); stat.phy_stat = ((v1 & 0x00FF0000) >> 16); tmp = (v0 & 0x0000FFFF); stat.frame_count = ((tmp & 0xF000) >> 12); stat.rts_count = ((tmp & 0x0F00) >> 8); stat.supp_reason = ((tmp & 0x001C) >> 2); stat.pm_indicated = !!(tmp & 0x0080); stat.intermediate = !!(tmp & 0x0040); stat.for_ampdu = !!(tmp & 0x0020); stat.acked = !!(tmp & 0x0002); b43legacy_handle_txstatus(dev, &stat); } } static void drain_txstatus_queue(struct b43legacy_wldev *dev) { u32 dummy; if (dev->dev->id.revision < 5) return; /* Read all entries from the microcode TXstatus FIFO * and throw them away. */ while (1) { dummy = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_0); if (!(dummy & 0x00000001)) break; dummy = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_1); } } static u32 b43legacy_jssi_read(struct b43legacy_wldev *dev) { u32 val = 0; val = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x40A); val <<= 16; val |= b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x408); return val; } static void b43legacy_jssi_write(struct b43legacy_wldev *dev, u32 jssi) { b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x408, (jssi & 0x0000FFFF)); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x40A, (jssi & 0xFFFF0000) >> 16); } static void b43legacy_generate_noise_sample(struct b43legacy_wldev *dev) { b43legacy_jssi_write(dev, 0x7F7F7F7F); b43legacy_write32(dev, B43legacy_MMIO_MACCMD, b43legacy_read32(dev, B43legacy_MMIO_MACCMD) | B43legacy_MACCMD_BGNOISE); B43legacy_WARN_ON(dev->noisecalc.channel_at_start != dev->phy.channel); } static void b43legacy_calculate_link_quality(struct b43legacy_wldev *dev) { /* Top half of Link Quality calculation. */ if (dev->noisecalc.calculation_running) return; dev->noisecalc.channel_at_start = dev->phy.channel; dev->noisecalc.calculation_running = true; dev->noisecalc.nr_samples = 0; b43legacy_generate_noise_sample(dev); } static void handle_irq_noise(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 tmp; u8 noise[4]; u8 i; u8 j; s32 average; /* Bottom half of Link Quality calculation. */ B43legacy_WARN_ON(!dev->noisecalc.calculation_running); if (dev->noisecalc.channel_at_start != phy->channel) goto drop_calculation; *((__le32 *)noise) = cpu_to_le32(b43legacy_jssi_read(dev)); if (noise[0] == 0x7F || noise[1] == 0x7F || noise[2] == 0x7F || noise[3] == 0x7F) goto generate_new; /* Get the noise samples. */ B43legacy_WARN_ON(dev->noisecalc.nr_samples >= 8); i = dev->noisecalc.nr_samples; noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]]; dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]]; dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]]; dev->noisecalc.samples[i][3] = phy->nrssi_lt[noise[3]]; dev->noisecalc.nr_samples++; if (dev->noisecalc.nr_samples == 8) { /* Calculate the Link Quality by the noise samples. */ average = 0; for (i = 0; i < 8; i++) { for (j = 0; j < 4; j++) average += dev->noisecalc.samples[i][j]; } average /= (8 * 4); average *= 125; average += 64; average /= 128; tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x40C); tmp = (tmp / 128) & 0x1F; if (tmp >= 8) average += 2; else average -= 25; if (tmp == 8) average -= 72; else average -= 48; dev->stats.link_noise = average; drop_calculation: dev->noisecalc.calculation_running = false; return; } generate_new: b43legacy_generate_noise_sample(dev); } static void handle_irq_tbtt_indication(struct b43legacy_wldev *dev) { if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) { /* TODO: PS TBTT */ } else { if (1/*FIXME: the last PSpoll frame was sent successfully */) b43legacy_power_saving_ctl_bits(dev, -1, -1); } if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) dev->dfq_valid = true; } static void handle_irq_atim_end(struct b43legacy_wldev *dev) { if (dev->dfq_valid) { b43legacy_write32(dev, B43legacy_MMIO_MACCMD, b43legacy_read32(dev, B43legacy_MMIO_MACCMD) | B43legacy_MACCMD_DFQ_VALID); dev->dfq_valid = false; } } static void handle_irq_pmq(struct b43legacy_wldev *dev) { u32 tmp; /* TODO: AP mode. */ while (1) { tmp = b43legacy_read32(dev, B43legacy_MMIO_PS_STATUS); if (!(tmp & 0x00000008)) break; } /* 16bit write is odd, but correct. */ b43legacy_write16(dev, B43legacy_MMIO_PS_STATUS, 0x0002); } static void b43legacy_write_template_common(struct b43legacy_wldev *dev, const u8 *data, u16 size, u16 ram_offset, u16 shm_size_offset, u8 rate) { u32 i; u32 tmp; struct b43legacy_plcp_hdr4 plcp; plcp.data = 0; b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); b43legacy_ram_write(dev, ram_offset, le32_to_cpu(plcp.data)); ram_offset += sizeof(u32); /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet. * So leave the first two bytes of the next write blank. */ tmp = (u32)(data[0]) << 16; tmp |= (u32)(data[1]) << 24; b43legacy_ram_write(dev, ram_offset, tmp); ram_offset += sizeof(u32); for (i = 2; i < size; i += sizeof(u32)) { tmp = (u32)(data[i + 0]); if (i + 1 < size) tmp |= (u32)(data[i + 1]) << 8; if (i + 2 < size) tmp |= (u32)(data[i + 2]) << 16; if (i + 3 < size) tmp |= (u32)(data[i + 3]) << 24; b43legacy_ram_write(dev, ram_offset + i - 2, tmp); } b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_size_offset, size + sizeof(struct b43legacy_plcp_hdr6)); } /* Convert a b43legacy antenna number value to the PHY TX control value. */ static u16 b43legacy_antenna_to_phyctl(int antenna) { switch (antenna) { case B43legacy_ANTENNA0: return B43legacy_TX4_PHY_ANT0; case B43legacy_ANTENNA1: return B43legacy_TX4_PHY_ANT1; } return B43legacy_TX4_PHY_ANTLAST; } static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev, u16 ram_offset, u16 shm_size_offset) { unsigned int i, len, variable_len; const struct ieee80211_mgmt *bcn; const u8 *ie; bool tim_found = false; unsigned int rate; u16 ctl; int antenna; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); len = min((size_t)dev->wl->current_beacon->len, 0x200 - sizeof(struct b43legacy_plcp_hdr6)); rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; b43legacy_write_template_common(dev, (const u8 *)bcn, len, ram_offset, shm_size_offset, rate); /* Write the PHY TX control parameters. */ antenna = B43legacy_ANTENNA_DEFAULT; antenna = b43legacy_antenna_to_phyctl(antenna); ctl = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_BEACPHYCTL); /* We can't send beacons with short preamble. Would get PHY errors. */ ctl &= ~B43legacy_TX4_PHY_SHORTPRMBL; ctl &= ~B43legacy_TX4_PHY_ANT; ctl &= ~B43legacy_TX4_PHY_ENC; ctl |= antenna; ctl |= B43legacy_TX4_PHY_ENC_CCK; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_BEACPHYCTL, ctl); /* Find the position of the TIM and the DTIM_period value * and write them to SHM. */ ie = bcn->u.beacon.variable; variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable); for (i = 0; i < variable_len - 2; ) { uint8_t ie_id, ie_len; ie_id = ie[i]; ie_len = ie[i + 1]; if (ie_id == 5) { u16 tim_position; u16 dtim_period; /* This is the TIM Information Element */ /* Check whether the ie_len is in the beacon data range. */ if (variable_len < ie_len + 2 + i) break; /* A valid TIM is at least 4 bytes long. */ if (ie_len < 4) break; tim_found = true; tim_position = sizeof(struct b43legacy_plcp_hdr6); tim_position += offsetof(struct ieee80211_mgmt, u.beacon.variable); tim_position += i; dtim_period = ie[i + 3]; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_TIMPOS, tim_position); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_DTIMP, dtim_period); break; } i += ie_len + 2; } if (!tim_found) { b43legacywarn(dev->wl, "Did not find a valid TIM IE in the " "beacon template packet. AP or IBSS operation " "may be broken.\n"); } else b43legacydbg(dev->wl, "Updated beacon template\n"); } static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev, u16 shm_offset, u16 size, struct ieee80211_rate *rate) { struct b43legacy_plcp_hdr4 plcp; u32 tmp; __le16 dur; plcp.data = 0; b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate->hw_value); dur = ieee80211_generic_frame_duration(dev->wl->hw, dev->wl->vif, IEEE80211_BAND_2GHZ, size, rate); /* Write PLCP in two parts and timing for packet transfer */ tmp = le32_to_cpu(plcp.data); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset, tmp & 0xFFFF); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset + 2, tmp >> 16); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset + 6, le16_to_cpu(dur)); } /* Instead of using custom probe response template, this function * just patches custom beacon template by: * 1) Changing packet type * 2) Patching duration field * 3) Stripping TIM */ static const u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev, u16 *dest_size, struct ieee80211_rate *rate) { const u8 *src_data; u8 *dest_data; u16 src_size, elem_size, src_pos, dest_pos; __le16 dur; struct ieee80211_hdr *hdr; size_t ie_start; src_size = dev->wl->current_beacon->len; src_data = (const u8 *)dev->wl->current_beacon->data; /* Get the start offset of the variable IEs in the packet. */ ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); B43legacy_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt, u.beacon.variable)); if (B43legacy_WARN_ON(src_size < ie_start)) return NULL; dest_data = kmalloc(src_size, GFP_ATOMIC); if (unlikely(!dest_data)) return NULL; /* Copy the static data and all Information Elements, except the TIM. */ memcpy(dest_data, src_data, ie_start); src_pos = ie_start; dest_pos = ie_start; for ( ; src_pos < src_size - 2; src_pos += elem_size) { elem_size = src_data[src_pos + 1] + 2; if (src_data[src_pos] == 5) { /* This is the TIM. */ continue; } memcpy(dest_data + dest_pos, src_data + src_pos, elem_size); dest_pos += elem_size; } *dest_size = dest_pos; hdr = (struct ieee80211_hdr *)dest_data; /* Set the frame control. */ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); dur = ieee80211_generic_frame_duration(dev->wl->hw, dev->wl->vif, IEEE80211_BAND_2GHZ, *dest_size, rate); hdr->duration_id = dur; return dest_data; } static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev, u16 ram_offset, u16 shm_size_offset, struct ieee80211_rate *rate) { const u8 *probe_resp_data; u16 size; size = dev->wl->current_beacon->len; probe_resp_data = b43legacy_generate_probe_resp(dev, &size, rate); if (unlikely(!probe_resp_data)) return; /* Looks like PLCP headers plus packet timings are stored for * all possible basic rates */ b43legacy_write_probe_resp_plcp(dev, 0x31A, size, &b43legacy_b_ratetable[0]); b43legacy_write_probe_resp_plcp(dev, 0x32C, size, &b43legacy_b_ratetable[1]); b43legacy_write_probe_resp_plcp(dev, 0x33E, size, &b43legacy_b_ratetable[2]); b43legacy_write_probe_resp_plcp(dev, 0x350, size, &b43legacy_b_ratetable[3]); size = min((size_t)size, 0x200 - sizeof(struct b43legacy_plcp_hdr6)); b43legacy_write_template_common(dev, probe_resp_data, size, ram_offset, shm_size_offset, rate->hw_value); kfree(probe_resp_data); } static void b43legacy_upload_beacon0(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; if (wl->beacon0_uploaded) return; b43legacy_write_beacon_template(dev, 0x68, 0x18); /* FIXME: Probe resp upload doesn't really belong here, * but we don't use that feature anyway. */ b43legacy_write_probe_resp_template(dev, 0x268, 0x4A, &__b43legacy_ratetable[3]); wl->beacon0_uploaded = true; } static void b43legacy_upload_beacon1(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; if (wl->beacon1_uploaded) return; b43legacy_write_beacon_template(dev, 0x468, 0x1A); wl->beacon1_uploaded = true; } static void handle_irq_beacon(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; u32 cmd, beacon0_valid, beacon1_valid; if (!b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) return; /* This is the bottom half of the asynchronous beacon update. */ /* Ignore interrupt in the future. */ dev->irq_mask &= ~B43legacy_IRQ_BEACON; cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); beacon0_valid = (cmd & B43legacy_MACCMD_BEACON0_VALID); beacon1_valid = (cmd & B43legacy_MACCMD_BEACON1_VALID); /* Schedule interrupt manually, if busy. */ if (beacon0_valid && beacon1_valid) { b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_BEACON); dev->irq_mask |= B43legacy_IRQ_BEACON; return; } if (unlikely(wl->beacon_templates_virgin)) { /* We never uploaded a beacon before. * Upload both templates now, but only mark one valid. */ wl->beacon_templates_virgin = false; b43legacy_upload_beacon0(dev); b43legacy_upload_beacon1(dev); cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); cmd |= B43legacy_MACCMD_BEACON0_VALID; b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); } else { if (!beacon0_valid) { b43legacy_upload_beacon0(dev); cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); cmd |= B43legacy_MACCMD_BEACON0_VALID; b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); } else if (!beacon1_valid) { b43legacy_upload_beacon1(dev); cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); cmd |= B43legacy_MACCMD_BEACON1_VALID; b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); } } } static void b43legacy_beacon_update_trigger_work(struct work_struct *work) { struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, beacon_update_trigger); struct b43legacy_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (likely(dev && (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED))) { spin_lock_irq(&wl->irq_lock); /* Update beacon right away or defer to IRQ. */ handle_irq_beacon(dev); /* The handler might have updated the IRQ mask. */ b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); mmiowb(); spin_unlock_irq(&wl->irq_lock); } mutex_unlock(&wl->mutex); } /* Asynchronously update the packet templates in template RAM. * Locking: Requires wl->irq_lock to be locked. */ static void b43legacy_update_templates(struct b43legacy_wl *wl) { struct sk_buff *beacon; /* This is the top half of the ansynchronous beacon update. The bottom * half is the beacon IRQ. Beacon update must be asynchronous to avoid * sending an invalid beacon. This can happen for example, if the * firmware transmits a beacon while we are updating it. */ /* We could modify the existing beacon and set the aid bit in the TIM * field, but that would probably require resizing and moving of data * within the beacon template. Simply request a new beacon and let * mac80211 do the hard work. */ beacon = ieee80211_beacon_get(wl->hw, wl->vif); if (unlikely(!beacon)) return; if (wl->current_beacon) dev_kfree_skb_any(wl->current_beacon); wl->current_beacon = beacon; wl->beacon0_uploaded = false; wl->beacon1_uploaded = false; ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); } static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev, u16 beacon_int) { b43legacy_time_lock(dev); if (dev->dev->id.revision >= 3) { b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_REP, (beacon_int << 16)); b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_START, (beacon_int << 10)); } else { b43legacy_write16(dev, 0x606, (beacon_int >> 6)); b43legacy_write16(dev, 0x610, beacon_int); } b43legacy_time_unlock(dev); b43legacydbg(dev->wl, "Set beacon interval to %u\n", beacon_int); } static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) { } /* Interrupt handler bottom-half */ static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev) { u32 reason; u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; u32 merged_dma_reason = 0; int i; unsigned long flags; spin_lock_irqsave(&dev->wl->irq_lock, flags); B43legacy_WARN_ON(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED); reason = dev->irq_reason; for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { dma_reason[i] = dev->dma_reason[i]; merged_dma_reason |= dma_reason[i]; } if (unlikely(reason & B43legacy_IRQ_MAC_TXERR)) b43legacyerr(dev->wl, "MAC transmission error\n"); if (unlikely(reason & B43legacy_IRQ_PHY_TXERR)) { b43legacyerr(dev->wl, "PHY transmission error\n"); rmb(); if (unlikely(atomic_dec_and_test(&dev->phy.txerr_cnt))) { b43legacyerr(dev->wl, "Too many PHY TX errors, " "restarting the controller\n"); b43legacy_controller_restart(dev, "PHY TX errors"); } } if (unlikely(merged_dma_reason & (B43legacy_DMAIRQ_FATALMASK | B43legacy_DMAIRQ_NONFATALMASK))) { if (merged_dma_reason & B43legacy_DMAIRQ_FATALMASK) { b43legacyerr(dev->wl, "Fatal DMA error: " "0x%08X, 0x%08X, 0x%08X, " "0x%08X, 0x%08X, 0x%08X\n", dma_reason[0], dma_reason[1], dma_reason[2], dma_reason[3], dma_reason[4], dma_reason[5]); b43legacy_controller_restart(dev, "DMA error"); mmiowb(); spin_unlock_irqrestore(&dev->wl->irq_lock, flags); return; } if (merged_dma_reason & B43legacy_DMAIRQ_NONFATALMASK) b43legacyerr(dev->wl, "DMA error: " "0x%08X, 0x%08X, 0x%08X, " "0x%08X, 0x%08X, 0x%08X\n", dma_reason[0], dma_reason[1], dma_reason[2], dma_reason[3], dma_reason[4], dma_reason[5]); } if (unlikely(reason & B43legacy_IRQ_UCODE_DEBUG)) handle_irq_ucode_debug(dev); if (reason & B43legacy_IRQ_TBTT_INDI) handle_irq_tbtt_indication(dev); if (reason & B43legacy_IRQ_ATIM_END) handle_irq_atim_end(dev); if (reason & B43legacy_IRQ_BEACON) handle_irq_beacon(dev); if (reason & B43legacy_IRQ_PMQ) handle_irq_pmq(dev); if (reason & B43legacy_IRQ_TXFIFO_FLUSH_OK) ;/*TODO*/ if (reason & B43legacy_IRQ_NOISESAMPLE_OK) handle_irq_noise(dev); /* Check the DMA reason registers for received data. */ if (dma_reason[0] & B43legacy_DMAIRQ_RX_DONE) { if (b43legacy_using_pio(dev)) b43legacy_pio_rx(dev->pio.queue0); else b43legacy_dma_rx(dev->dma.rx_ring0); } B43legacy_WARN_ON(dma_reason[1] & B43legacy_DMAIRQ_RX_DONE); B43legacy_WARN_ON(dma_reason[2] & B43legacy_DMAIRQ_RX_DONE); if (dma_reason[3] & B43legacy_DMAIRQ_RX_DONE) { if (b43legacy_using_pio(dev)) b43legacy_pio_rx(dev->pio.queue3); else b43legacy_dma_rx(dev->dma.rx_ring3); } B43legacy_WARN_ON(dma_reason[4] & B43legacy_DMAIRQ_RX_DONE); B43legacy_WARN_ON(dma_reason[5] & B43legacy_DMAIRQ_RX_DONE); if (reason & B43legacy_IRQ_TX_OK) handle_irq_transmit_status(dev); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); mmiowb(); spin_unlock_irqrestore(&dev->wl->irq_lock, flags); } static void pio_irq_workaround(struct b43legacy_wldev *dev, u16 base, int queueidx) { u16 rxctl; rxctl = b43legacy_read16(dev, base + B43legacy_PIO_RXCTL); if (rxctl & B43legacy_PIO_RXCTL_DATAAVAILABLE) dev->dma_reason[queueidx] |= B43legacy_DMAIRQ_RX_DONE; else dev->dma_reason[queueidx] &= ~B43legacy_DMAIRQ_RX_DONE; } static void b43legacy_interrupt_ack(struct b43legacy_wldev *dev, u32 reason) { if (b43legacy_using_pio(dev) && (dev->dev->id.revision < 3) && (!(reason & B43legacy_IRQ_PIO_WORKAROUND))) { /* Apply a PIO specific workaround to the dma_reasons */ pio_irq_workaround(dev, B43legacy_MMIO_PIO1_BASE, 0); pio_irq_workaround(dev, B43legacy_MMIO_PIO2_BASE, 1); pio_irq_workaround(dev, B43legacy_MMIO_PIO3_BASE, 2); pio_irq_workaround(dev, B43legacy_MMIO_PIO4_BASE, 3); } b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, reason); b43legacy_write32(dev, B43legacy_MMIO_DMA0_REASON, dev->dma_reason[0]); b43legacy_write32(dev, B43legacy_MMIO_DMA1_REASON, dev->dma_reason[1]); b43legacy_write32(dev, B43legacy_MMIO_DMA2_REASON, dev->dma_reason[2]); b43legacy_write32(dev, B43legacy_MMIO_DMA3_REASON, dev->dma_reason[3]); b43legacy_write32(dev, B43legacy_MMIO_DMA4_REASON, dev->dma_reason[4]); b43legacy_write32(dev, B43legacy_MMIO_DMA5_REASON, dev->dma_reason[5]); } /* Interrupt handler top-half */ static irqreturn_t b43legacy_interrupt_handler(int irq, void *dev_id) { irqreturn_t ret = IRQ_NONE; struct b43legacy_wldev *dev = dev_id; u32 reason; B43legacy_WARN_ON(!dev); spin_lock(&dev->wl->irq_lock); if (unlikely(b43legacy_status(dev) < B43legacy_STAT_STARTED)) /* This can only happen on shared IRQ lines. */ goto out; reason = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); if (reason == 0xffffffff) /* shared IRQ */ goto out; ret = IRQ_HANDLED; reason &= dev->irq_mask; if (!reason) goto out; dev->dma_reason[0] = b43legacy_read32(dev, B43legacy_MMIO_DMA0_REASON) & 0x0001DC00; dev->dma_reason[1] = b43legacy_read32(dev, B43legacy_MMIO_DMA1_REASON) & 0x0000DC00; dev->dma_reason[2] = b43legacy_read32(dev, B43legacy_MMIO_DMA2_REASON) & 0x0000DC00; dev->dma_reason[3] = b43legacy_read32(dev, B43legacy_MMIO_DMA3_REASON) & 0x0001DC00; dev->dma_reason[4] = b43legacy_read32(dev, B43legacy_MMIO_DMA4_REASON) & 0x0000DC00; dev->dma_reason[5] = b43legacy_read32(dev, B43legacy_MMIO_DMA5_REASON) & 0x0000DC00; b43legacy_interrupt_ack(dev, reason); /* Disable all IRQs. They are enabled again in the bottom half. */ b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); /* Save the reason code and call our bottom half. */ dev->irq_reason = reason; tasklet_schedule(&dev->isr_tasklet); out: mmiowb(); spin_unlock(&dev->wl->irq_lock); return ret; } static void b43legacy_release_firmware(struct b43legacy_wldev *dev) { release_firmware(dev->fw.ucode); dev->fw.ucode = NULL; release_firmware(dev->fw.pcm); dev->fw.pcm = NULL; release_firmware(dev->fw.initvals); dev->fw.initvals = NULL; release_firmware(dev->fw.initvals_band); dev->fw.initvals_band = NULL; } static void b43legacy_print_fw_helptext(struct b43legacy_wl *wl) { b43legacyerr(wl, "You must go to http://wireless.kernel.org/en/users/" "Drivers/b43#devicefirmware " "and download the correct firmware (version 3).\n"); } static int do_request_fw(struct b43legacy_wldev *dev, const char *name, const struct firmware **fw) { char path[sizeof(modparam_fwpostfix) + 32]; struct b43legacy_fw_header *hdr; u32 size; int err; if (!name) return 0; snprintf(path, ARRAY_SIZE(path), "b43legacy%s/%s.fw", modparam_fwpostfix, name); err = request_firmware(fw, path, dev->dev->dev); if (err) { b43legacyerr(dev->wl, "Firmware file \"%s\" not found " "or load failed.\n", path); return err; } if ((*fw)->size < sizeof(struct b43legacy_fw_header)) goto err_format; hdr = (struct b43legacy_fw_header *)((*fw)->data); switch (hdr->type) { case B43legacy_FW_TYPE_UCODE: case B43legacy_FW_TYPE_PCM: size = be32_to_cpu(hdr->size); if (size != (*fw)->size - sizeof(struct b43legacy_fw_header)) goto err_format; /* fallthrough */ case B43legacy_FW_TYPE_IV: if (hdr->ver != 1) goto err_format; break; default: goto err_format; } return err; err_format: b43legacyerr(dev->wl, "Firmware file \"%s\" format error.\n", path); return -EPROTO; } static int b43legacy_one_core_attach(struct ssb_device *dev, struct b43legacy_wl *wl); static void b43legacy_one_core_detach(struct ssb_device *dev); static void b43legacy_request_firmware(struct work_struct *work) { struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, firmware_load); struct b43legacy_wldev *dev = wl->current_dev; struct b43legacy_firmware *fw = &dev->fw; const u8 rev = dev->dev->id.revision; const char *filename; int err; if (!fw->ucode) { if (rev == 2) filename = "ucode2"; else if (rev == 4) filename = "ucode4"; else filename = "ucode5"; err = do_request_fw(dev, filename, &fw->ucode); if (err) goto err_load; } if (!fw->pcm) { if (rev < 5) filename = "pcm4"; else filename = "pcm5"; err = do_request_fw(dev, filename, &fw->pcm); if (err) goto err_load; } if (!fw->initvals) { switch (dev->phy.type) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: if ((rev >= 5) && (rev <= 10)) filename = "b0g0initvals5"; else if (rev == 2 || rev == 4) filename = "b0g0initvals2"; else goto err_no_initvals; break; default: goto err_no_initvals; } err = do_request_fw(dev, filename, &fw->initvals); if (err) goto err_load; } if (!fw->initvals_band) { switch (dev->phy.type) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: if ((rev >= 5) && (rev <= 10)) filename = "b0g0bsinitvals5"; else if (rev >= 11) filename = NULL; else if (rev == 2 || rev == 4) filename = NULL; else goto err_no_initvals; break; default: goto err_no_initvals; } err = do_request_fw(dev, filename, &fw->initvals_band); if (err) goto err_load; } err = ieee80211_register_hw(wl->hw); if (err) goto err_one_core_detach; return; err_one_core_detach: b43legacy_one_core_detach(dev->dev); goto error; err_load: b43legacy_print_fw_helptext(dev->wl); goto error; err_no_initvals: err = -ENODEV; b43legacyerr(dev->wl, "No Initial Values firmware file for PHY %u, " "core rev %u\n", dev->phy.type, rev); goto error; error: b43legacy_release_firmware(dev); return; } static int b43legacy_upload_microcode(struct b43legacy_wldev *dev) { struct wiphy *wiphy = dev->wl->hw->wiphy; const size_t hdr_len = sizeof(struct b43legacy_fw_header); const __be32 *data; unsigned int i; unsigned int len; u16 fwrev; u16 fwpatch; u16 fwdate; u16 fwtime; u32 tmp, macctl; int err = 0; /* Jump the microcode PSM to offset 0 */ macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); B43legacy_WARN_ON(macctl & B43legacy_MACCTL_PSM_RUN); macctl |= B43legacy_MACCTL_PSM_JMP0; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); /* Zero out all microcode PSM registers and shared memory. */ for (i = 0; i < 64; i++) b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, i, 0); for (i = 0; i < 4096; i += 2) b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, i, 0); /* Upload Microcode. */ data = (__be32 *) (dev->fw.ucode->data + hdr_len); len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32); b43legacy_shm_control_word(dev, B43legacy_SHM_UCODE | B43legacy_SHM_AUTOINC_W, 0x0000); for (i = 0; i < len; i++) { b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, be32_to_cpu(data[i])); udelay(10); } if (dev->fw.pcm) { /* Upload PCM data. */ data = (__be32 *) (dev->fw.pcm->data + hdr_len); len = (dev->fw.pcm->size - hdr_len) / sizeof(__be32); b43legacy_shm_control_word(dev, B43legacy_SHM_HW, 0x01EA); b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, 0x00004000); /* No need for autoinc bit in SHM_HW */ b43legacy_shm_control_word(dev, B43legacy_SHM_HW, 0x01EB); for (i = 0; i < len; i++) { b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, be32_to_cpu(data[i])); udelay(10); } } b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_ALL); /* Start the microcode PSM */ macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); macctl &= ~B43legacy_MACCTL_PSM_JMP0; macctl |= B43legacy_MACCTL_PSM_RUN; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); /* Wait for the microcode to load and respond */ i = 0; while (1) { tmp = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); if (tmp == B43legacy_IRQ_MAC_SUSPENDED) break; i++; if (i >= B43legacy_IRQWAIT_MAX_RETRIES) { b43legacyerr(dev->wl, "Microcode not responding\n"); b43legacy_print_fw_helptext(dev->wl); err = -ENODEV; goto error; } msleep_interruptible(50); if (signal_pending(current)) { err = -EINTR; goto error; } } /* dummy read follows */ b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); /* Get and check the revisions. */ fwrev = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_UCODEREV); fwpatch = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_UCODEPATCH); fwdate = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_UCODEDATE); fwtime = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_UCODETIME); if (fwrev > 0x128) { b43legacyerr(dev->wl, "YOU ARE TRYING TO LOAD V4 FIRMWARE." " Only firmware from binary drivers version 3.x" " is supported. You must change your firmware" " files.\n"); b43legacy_print_fw_helptext(dev->wl); err = -EOPNOTSUPP; goto error; } b43legacyinfo(dev->wl, "Loading firmware version 0x%X, patch level %u " "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", fwrev, fwpatch, (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF, (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F); dev->fw.rev = fwrev; dev->fw.patch = fwpatch; snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u", dev->fw.rev, dev->fw.patch); wiphy->hw_version = dev->dev->id.coreid; return 0; error: macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); macctl &= ~B43legacy_MACCTL_PSM_RUN; macctl |= B43legacy_MACCTL_PSM_JMP0; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); return err; } static int b43legacy_write_initvals(struct b43legacy_wldev *dev, const struct b43legacy_iv *ivals, size_t count, size_t array_size) { const struct b43legacy_iv *iv; u16 offset; size_t i; bool bit32; BUILD_BUG_ON(sizeof(struct b43legacy_iv) != 6); iv = ivals; for (i = 0; i < count; i++) { if (array_size < sizeof(iv->offset_size)) goto err_format; array_size -= sizeof(iv->offset_size); offset = be16_to_cpu(iv->offset_size); bit32 = !!(offset & B43legacy_IV_32BIT); offset &= B43legacy_IV_OFFSET_MASK; if (offset >= 0x1000) goto err_format; if (bit32) { u32 value; if (array_size < sizeof(iv->data.d32)) goto err_format; array_size -= sizeof(iv->data.d32); value = get_unaligned_be32(&iv->data.d32); b43legacy_write32(dev, offset, value); iv = (const struct b43legacy_iv *)((const uint8_t *)iv + sizeof(__be16) + sizeof(__be32)); } else { u16 value; if (array_size < sizeof(iv->data.d16)) goto err_format; array_size -= sizeof(iv->data.d16); value = be16_to_cpu(iv->data.d16); b43legacy_write16(dev, offset, value); iv = (const struct b43legacy_iv *)((const uint8_t *)iv + sizeof(__be16) + sizeof(__be16)); } } if (array_size) goto err_format; return 0; err_format: b43legacyerr(dev->wl, "Initial Values Firmware file-format error.\n"); b43legacy_print_fw_helptext(dev->wl); return -EPROTO; } static int b43legacy_upload_initvals(struct b43legacy_wldev *dev) { const size_t hdr_len = sizeof(struct b43legacy_fw_header); const struct b43legacy_fw_header *hdr; struct b43legacy_firmware *fw = &dev->fw; const struct b43legacy_iv *ivals; size_t count; int err; hdr = (const struct b43legacy_fw_header *)(fw->initvals->data); ivals = (const struct b43legacy_iv *)(fw->initvals->data + hdr_len); count = be32_to_cpu(hdr->size); err = b43legacy_write_initvals(dev, ivals, count, fw->initvals->size - hdr_len); if (err) goto out; if (fw->initvals_band) { hdr = (const struct b43legacy_fw_header *) (fw->initvals_band->data); ivals = (const struct b43legacy_iv *)(fw->initvals_band->data + hdr_len); count = be32_to_cpu(hdr->size); err = b43legacy_write_initvals(dev, ivals, count, fw->initvals_band->size - hdr_len); if (err) goto out; } out: return err; } /* Initialize the GPIOs * http://bcm-specs.sipsolutions.net/GPIO */ static int b43legacy_gpio_init(struct b43legacy_wldev *dev) { struct ssb_bus *bus = dev->dev->bus; struct ssb_device *gpiodev, *pcidev = NULL; u32 mask; u32 set; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, b43legacy_read32(dev, B43legacy_MMIO_MACCTL) & 0xFFFF3FFF); b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK, b43legacy_read16(dev, B43legacy_MMIO_GPIO_MASK) | 0x000F); mask = 0x0000001F; set = 0x0000000F; if (dev->dev->bus->chip_id == 0x4301) { mask |= 0x0060; set |= 0x0060; } if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) { b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK, b43legacy_read16(dev, B43legacy_MMIO_GPIO_MASK) | 0x0200); mask |= 0x0200; set |= 0x0200; } if (dev->dev->id.revision >= 2) mask |= 0x0010; /* FIXME: This is redundant. */ #ifdef CONFIG_SSB_DRIVER_PCICORE pcidev = bus->pcicore.dev; #endif gpiodev = bus->chipco.dev ? : pcidev; if (!gpiodev) return 0; ssb_write32(gpiodev, B43legacy_GPIO_CONTROL, (ssb_read32(gpiodev, B43legacy_GPIO_CONTROL) & ~mask) | set); return 0; } /* Turn off all GPIO stuff. Call this on module unload, for example. */ static void b43legacy_gpio_cleanup(struct b43legacy_wldev *dev) { struct ssb_bus *bus = dev->dev->bus; struct ssb_device *gpiodev, *pcidev = NULL; #ifdef CONFIG_SSB_DRIVER_PCICORE pcidev = bus->pcicore.dev; #endif gpiodev = bus->chipco.dev ? : pcidev; if (!gpiodev) return; ssb_write32(gpiodev, B43legacy_GPIO_CONTROL, 0); } /* http://bcm-specs.sipsolutions.net/EnableMac */ void b43legacy_mac_enable(struct b43legacy_wldev *dev) { dev->mac_suspended--; B43legacy_WARN_ON(dev->mac_suspended < 0); B43legacy_WARN_ON(irqs_disabled()); if (dev->mac_suspended == 0) { b43legacy_write32(dev, B43legacy_MMIO_MACCTL, b43legacy_read32(dev, B43legacy_MMIO_MACCTL) | B43legacy_MACCTL_ENABLED); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_MAC_SUSPENDED); /* the next two are dummy reads */ b43legacy_read32(dev, B43legacy_MMIO_MACCTL); b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); b43legacy_power_saving_ctl_bits(dev, -1, -1); /* Re-enable IRQs. */ spin_lock_irq(&dev->wl->irq_lock); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); spin_unlock_irq(&dev->wl->irq_lock); } } /* http://bcm-specs.sipsolutions.net/SuspendMAC */ void b43legacy_mac_suspend(struct b43legacy_wldev *dev) { int i; u32 tmp; might_sleep(); B43legacy_WARN_ON(irqs_disabled()); B43legacy_WARN_ON(dev->mac_suspended < 0); if (dev->mac_suspended == 0) { /* Mask IRQs before suspending MAC. Otherwise * the MAC stays busy and won't suspend. */ spin_lock_irq(&dev->wl->irq_lock); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); spin_unlock_irq(&dev->wl->irq_lock); b43legacy_synchronize_irq(dev); b43legacy_power_saving_ctl_bits(dev, -1, 1); b43legacy_write32(dev, B43legacy_MMIO_MACCTL, b43legacy_read32(dev, B43legacy_MMIO_MACCTL) & ~B43legacy_MACCTL_ENABLED); b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); for (i = 40; i; i--) { tmp = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); if (tmp & B43legacy_IRQ_MAC_SUSPENDED) goto out; msleep(1); } b43legacyerr(dev->wl, "MAC suspend failed\n"); } out: dev->mac_suspended++; } static void b43legacy_adjust_opmode(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; u32 ctl; u16 cfp_pretbtt; ctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); /* Reset status to STA infrastructure mode. */ ctl &= ~B43legacy_MACCTL_AP; ctl &= ~B43legacy_MACCTL_KEEP_CTL; ctl &= ~B43legacy_MACCTL_KEEP_BADPLCP; ctl &= ~B43legacy_MACCTL_KEEP_BAD; ctl &= ~B43legacy_MACCTL_PROMISC; ctl &= ~B43legacy_MACCTL_BEACPROMISC; ctl |= B43legacy_MACCTL_INFRA; if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) ctl |= B43legacy_MACCTL_AP; else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) ctl &= ~B43legacy_MACCTL_INFRA; if (wl->filter_flags & FIF_CONTROL) ctl |= B43legacy_MACCTL_KEEP_CTL; if (wl->filter_flags & FIF_FCSFAIL) ctl |= B43legacy_MACCTL_KEEP_BAD; if (wl->filter_flags & FIF_PLCPFAIL) ctl |= B43legacy_MACCTL_KEEP_BADPLCP; if (wl->filter_flags & FIF_PROMISC_IN_BSS) ctl |= B43legacy_MACCTL_PROMISC; if (wl->filter_flags & FIF_BCN_PRBRESP_PROMISC) ctl |= B43legacy_MACCTL_BEACPROMISC; /* Workaround: On old hardware the HW-MAC-address-filter * doesn't work properly, so always run promisc in filter * it in software. */ if (dev->dev->id.revision <= 4) ctl |= B43legacy_MACCTL_PROMISC; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, ctl); cfp_pretbtt = 2; if ((ctl & B43legacy_MACCTL_INFRA) && !(ctl & B43legacy_MACCTL_AP)) { if (dev->dev->bus->chip_id == 0x4306 && dev->dev->bus->chip_rev == 3) cfp_pretbtt = 100; else cfp_pretbtt = 50; } b43legacy_write16(dev, 0x612, cfp_pretbtt); } static void b43legacy_rate_memory_write(struct b43legacy_wldev *dev, u16 rate, int is_ofdm) { u16 offset; if (is_ofdm) { offset = 0x480; offset += (b43legacy_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; } else { offset = 0x4C0; offset += (b43legacy_plcp_get_ratecode_cck(rate) & 0x000F) * 2; } b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, offset + 0x20, b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, offset)); } static void b43legacy_rate_memory_init(struct b43legacy_wldev *dev) { switch (dev->phy.type) { case B43legacy_PHYTYPE_G: b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_6MB, 1); b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_12MB, 1); b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_18MB, 1); b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_24MB, 1); b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_36MB, 1); b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_48MB, 1); b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_54MB, 1); /* fallthrough */ case B43legacy_PHYTYPE_B: b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_1MB, 0); b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_2MB, 0); b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_5MB, 0); b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_11MB, 0); break; default: B43legacy_BUG_ON(1); } } /* Set the TX-Antenna for management frames sent by firmware. */ static void b43legacy_mgmtframe_txantenna(struct b43legacy_wldev *dev, int antenna) { u16 ant = 0; u16 tmp; switch (antenna) { case B43legacy_ANTENNA0: ant |= B43legacy_TX4_PHY_ANT0; break; case B43legacy_ANTENNA1: ant |= B43legacy_TX4_PHY_ANT1; break; case B43legacy_ANTENNA_AUTO: ant |= B43legacy_TX4_PHY_ANTLAST; break; default: B43legacy_BUG_ON(1); } /* FIXME We also need to set the other flags of the PHY control * field somewhere. */ /* For Beacons */ tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_BEACPHYCTL); tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_BEACPHYCTL, tmp); /* For ACK/CTS */ tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_ACKCTSPHYCTL); tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_ACKCTSPHYCTL, tmp); /* For Probe Resposes */ tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_PRPHYCTL); tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_PRPHYCTL, tmp); } /* This is the opposite of b43legacy_chip_init() */ static void b43legacy_chip_exit(struct b43legacy_wldev *dev) { b43legacy_radio_turn_off(dev, 1); b43legacy_gpio_cleanup(dev); /* firmware is released later */ } /* Initialize the chip * http://bcm-specs.sipsolutions.net/ChipInit */ static int b43legacy_chip_init(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; int err; int tmp; u32 value32, macctl; u16 value16; /* Initialize the MAC control */ macctl = B43legacy_MACCTL_IHR_ENABLED | B43legacy_MACCTL_SHM_ENABLED; if (dev->phy.gmode) macctl |= B43legacy_MACCTL_GMODE; macctl |= B43legacy_MACCTL_INFRA; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); err = b43legacy_upload_microcode(dev); if (err) goto out; /* firmware is released later */ err = b43legacy_gpio_init(dev); if (err) goto out; /* firmware is released later */ err = b43legacy_upload_initvals(dev); if (err) goto err_gpio_clean; b43legacy_radio_turn_on(dev); b43legacy_write16(dev, 0x03E6, 0x0000); err = b43legacy_phy_init(dev); if (err) goto err_radio_off; /* Select initial Interference Mitigation. */ tmp = phy->interfmode; phy->interfmode = B43legacy_INTERFMODE_NONE; b43legacy_radio_set_interference_mitigation(dev, tmp); b43legacy_phy_set_antenna_diversity(dev); b43legacy_mgmtframe_txantenna(dev, B43legacy_ANTENNA_DEFAULT); if (phy->type == B43legacy_PHYTYPE_B) { value16 = b43legacy_read16(dev, 0x005E); value16 |= 0x0004; b43legacy_write16(dev, 0x005E, value16); } b43legacy_write32(dev, 0x0100, 0x01000000); if (dev->dev->id.revision < 5) b43legacy_write32(dev, 0x010C, 0x01000000); value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); value32 &= ~B43legacy_MACCTL_INFRA; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32); value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); value32 |= B43legacy_MACCTL_INFRA; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32); if (b43legacy_using_pio(dev)) { b43legacy_write32(dev, 0x0210, 0x00000100); b43legacy_write32(dev, 0x0230, 0x00000100); b43legacy_write32(dev, 0x0250, 0x00000100); b43legacy_write32(dev, 0x0270, 0x00000100); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0034, 0x0000); } /* Probe Response Timeout value */ /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0074, 0x0000); /* Initially set the wireless operation mode. */ b43legacy_adjust_opmode(dev); if (dev->dev->id.revision < 3) { b43legacy_write16(dev, 0x060E, 0x0000); b43legacy_write16(dev, 0x0610, 0x8000); b43legacy_write16(dev, 0x0604, 0x0000); b43legacy_write16(dev, 0x0606, 0x0200); } else { b43legacy_write32(dev, 0x0188, 0x80000000); b43legacy_write32(dev, 0x018C, 0x02000000); } b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, 0x00004000); b43legacy_write32(dev, B43legacy_MMIO_DMA0_IRQ_MASK, 0x0001DC00); b43legacy_write32(dev, B43legacy_MMIO_DMA1_IRQ_MASK, 0x0000DC00); b43legacy_write32(dev, B43legacy_MMIO_DMA2_IRQ_MASK, 0x0000DC00); b43legacy_write32(dev, B43legacy_MMIO_DMA3_IRQ_MASK, 0x0001DC00); b43legacy_write32(dev, B43legacy_MMIO_DMA4_IRQ_MASK, 0x0000DC00); b43legacy_write32(dev, B43legacy_MMIO_DMA5_IRQ_MASK, 0x0000DC00); value32 = ssb_read32(dev->dev, SSB_TMSLOW); value32 |= B43legacy_TMSLOW_MACPHYCLKEN; ssb_write32(dev->dev, SSB_TMSLOW, value32); b43legacy_write16(dev, B43legacy_MMIO_POWERUP_DELAY, dev->dev->bus->chipco.fast_pwrup_delay); /* PHY TX errors counter. */ atomic_set(&phy->txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT); B43legacy_WARN_ON(err != 0); b43legacydbg(dev->wl, "Chip initialized\n"); out: return err; err_radio_off: b43legacy_radio_turn_off(dev, 1); err_gpio_clean: b43legacy_gpio_cleanup(dev); goto out; } static void b43legacy_periodic_every120sec(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; if (phy->type != B43legacy_PHYTYPE_G || phy->rev < 2) return; b43legacy_mac_suspend(dev); b43legacy_phy_lo_g_measure(dev); b43legacy_mac_enable(dev); } static void b43legacy_periodic_every60sec(struct b43legacy_wldev *dev) { b43legacy_phy_lo_mark_all_unused(dev); if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { b43legacy_mac_suspend(dev); b43legacy_calc_nrssi_slope(dev); b43legacy_mac_enable(dev); } } static void b43legacy_periodic_every30sec(struct b43legacy_wldev *dev) { /* Update device statistics. */ b43legacy_calculate_link_quality(dev); } static void b43legacy_periodic_every15sec(struct b43legacy_wldev *dev) { b43legacy_phy_xmitpower(dev); /* FIXME: unless scanning? */ atomic_set(&dev->phy.txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT); wmb(); } static void do_periodic_work(struct b43legacy_wldev *dev) { unsigned int state; state = dev->periodic_state; if (state % 8 == 0) b43legacy_periodic_every120sec(dev); if (state % 4 == 0) b43legacy_periodic_every60sec(dev); if (state % 2 == 0) b43legacy_periodic_every30sec(dev); b43legacy_periodic_every15sec(dev); } /* Periodic work locking policy: * The whole periodic work handler is protected by * wl->mutex. If another lock is needed somewhere in the * pwork callchain, it's acquired in-place, where it's needed. */ static void b43legacy_periodic_work_handler(struct work_struct *work) { struct b43legacy_wldev *dev = container_of(work, struct b43legacy_wldev, periodic_work.work); struct b43legacy_wl *wl = dev->wl; unsigned long delay; mutex_lock(&wl->mutex); if (unlikely(b43legacy_status(dev) != B43legacy_STAT_STARTED)) goto out; if (b43legacy_debug(dev, B43legacy_DBG_PWORK_STOP)) goto out_requeue; do_periodic_work(dev); dev->periodic_state++; out_requeue: if (b43legacy_debug(dev, B43legacy_DBG_PWORK_FAST)) delay = msecs_to_jiffies(50); else delay = round_jiffies_relative(HZ * 15); ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); out: mutex_unlock(&wl->mutex); } static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev) { struct delayed_work *work = &dev->periodic_work; dev->periodic_state = 0; INIT_DELAYED_WORK(work, b43legacy_periodic_work_handler); ieee80211_queue_delayed_work(dev->wl->hw, work, 0); } /* Validate access to the chip (SHM) */ static int b43legacy_validate_chipaccess(struct b43legacy_wldev *dev) { u32 value; u32 shm_backup; shm_backup = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0); b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, 0xAA5555AA); if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0) != 0xAA5555AA) goto error; b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, 0x55AAAA55); if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0) != 0x55AAAA55) goto error; b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, shm_backup); value = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); if ((value | B43legacy_MACCTL_GMODE) != (B43legacy_MACCTL_GMODE | B43legacy_MACCTL_IHR_ENABLED)) goto error; value = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); if (value) goto error; return 0; error: b43legacyerr(dev->wl, "Failed to validate the chipaccess\n"); return -ENODEV; } static void b43legacy_security_init(struct b43legacy_wldev *dev) { dev->max_nr_keys = (dev->dev->id.revision >= 5) ? 58 : 20; B43legacy_WARN_ON(dev->max_nr_keys > ARRAY_SIZE(dev->key)); dev->ktp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0056); /* KTP is a word address, but we address SHM bytewise. * So multiply by two. */ dev->ktp *= 2; if (dev->dev->id.revision >= 5) /* Number of RCMTA address slots */ b43legacy_write16(dev, B43legacy_MMIO_RCMTA_COUNT, dev->max_nr_keys - 8); } #ifdef CONFIG_B43LEGACY_HWRNG static int b43legacy_rng_read(struct hwrng *rng, u32 *data) { struct b43legacy_wl *wl = (struct b43legacy_wl *)rng->priv; unsigned long flags; /* Don't take wl->mutex here, as it could deadlock with * hwrng internal locking. It's not needed to take * wl->mutex here, anyway. */ spin_lock_irqsave(&wl->irq_lock, flags); *data = b43legacy_read16(wl->current_dev, B43legacy_MMIO_RNG); spin_unlock_irqrestore(&wl->irq_lock, flags); return (sizeof(u16)); } #endif static void b43legacy_rng_exit(struct b43legacy_wl *wl) { #ifdef CONFIG_B43LEGACY_HWRNG if (wl->rng_initialized) hwrng_unregister(&wl->rng); #endif } static int b43legacy_rng_init(struct b43legacy_wl *wl) { int err = 0; #ifdef CONFIG_B43LEGACY_HWRNG snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); wl->rng.name = wl->rng_name; wl->rng.data_read = b43legacy_rng_read; wl->rng.priv = (unsigned long)wl; wl->rng_initialized = 1; err = hwrng_register(&wl->rng); if (err) { wl->rng_initialized = 0; b43legacyerr(wl, "Failed to register the random " "number generator (%d)\n", err); } #endif return err; } static void b43legacy_tx_work(struct work_struct *work) { struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, tx_work); struct b43legacy_wldev *dev; struct sk_buff *skb; int queue_num; int err = 0; mutex_lock(&wl->mutex); dev = wl->current_dev; if (unlikely(!dev || b43legacy_status(dev) < B43legacy_STAT_STARTED)) { mutex_unlock(&wl->mutex); return; } for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { while (skb_queue_len(&wl->tx_queue[queue_num])) { skb = skb_dequeue(&wl->tx_queue[queue_num]); if (b43legacy_using_pio(dev)) err = b43legacy_pio_tx(dev, skb); else err = b43legacy_dma_tx(dev, skb); if (err == -ENOSPC) { wl->tx_queue_stopped[queue_num] = 1; ieee80211_stop_queue(wl->hw, queue_num); skb_queue_head(&wl->tx_queue[queue_num], skb); break; } if (unlikely(err)) dev_kfree_skb(skb); /* Drop it */ err = 0; } if (!err) wl->tx_queue_stopped[queue_num] = 0; } mutex_unlock(&wl->mutex); } static void b43legacy_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); if (unlikely(skb->len < 2 + 2 + 6)) { /* Too short, this can't be a valid frame. */ dev_kfree_skb_any(skb); return; } B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags); skb_queue_tail(&wl->tx_queue[skb_get_queue_mapping(skb)], skb); if (!wl->tx_queue_stopped[skb_get_queue_mapping(skb)]) ieee80211_queue_work(wl->hw, &wl->tx_work); else ieee80211_stop_queue(wl->hw, skb_get_queue_mapping(skb)); } static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { return 0; } static int b43legacy_op_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); unsigned long flags; spin_lock_irqsave(&wl->irq_lock, flags); memcpy(stats, &wl->ieee_stats, sizeof(*stats)); spin_unlock_irqrestore(&wl->irq_lock, flags); return 0; } static const char *phymode_to_string(unsigned int phymode) { switch (phymode) { case B43legacy_PHYMODE_B: return "B"; case B43legacy_PHYMODE_G: return "G"; default: B43legacy_BUG_ON(1); } return ""; } static int find_wldev_for_phymode(struct b43legacy_wl *wl, unsigned int phymode, struct b43legacy_wldev **dev, bool *gmode) { struct b43legacy_wldev *d; list_for_each_entry(d, &wl->devlist, list) { if (d->phy.possible_phymodes & phymode) { /* Ok, this device supports the PHY-mode. * Set the gmode bit. */ *gmode = true; *dev = d; return 0; } } return -ESRCH; } static void b43legacy_put_phy_into_reset(struct b43legacy_wldev *dev) { struct ssb_device *sdev = dev->dev; u32 tmslow; tmslow = ssb_read32(sdev, SSB_TMSLOW); tmslow &= ~B43legacy_TMSLOW_GMODE; tmslow |= B43legacy_TMSLOW_PHYRESET; tmslow |= SSB_TMSLOW_FGC; ssb_write32(sdev, SSB_TMSLOW, tmslow); msleep(1); tmslow = ssb_read32(sdev, SSB_TMSLOW); tmslow &= ~SSB_TMSLOW_FGC; tmslow |= B43legacy_TMSLOW_PHYRESET; ssb_write32(sdev, SSB_TMSLOW, tmslow); msleep(1); } /* Expects wl->mutex locked */ static int b43legacy_switch_phymode(struct b43legacy_wl *wl, unsigned int new_mode) { struct b43legacy_wldev *uninitialized_var(up_dev); struct b43legacy_wldev *down_dev; int err; bool gmode = false; int prev_status; err = find_wldev_for_phymode(wl, new_mode, &up_dev, &gmode); if (err) { b43legacyerr(wl, "Could not find a device for %s-PHY mode\n", phymode_to_string(new_mode)); return err; } if ((up_dev == wl->current_dev) && (!!wl->current_dev->phy.gmode == !!gmode)) /* This device is already running. */ return 0; b43legacydbg(wl, "Reconfiguring PHYmode to %s-PHY\n", phymode_to_string(new_mode)); down_dev = wl->current_dev; prev_status = b43legacy_status(down_dev); /* Shutdown the currently running core. */ if (prev_status >= B43legacy_STAT_STARTED) b43legacy_wireless_core_stop(down_dev); if (prev_status >= B43legacy_STAT_INITIALIZED) b43legacy_wireless_core_exit(down_dev); if (down_dev != up_dev) /* We switch to a different core, so we put PHY into * RESET on the old core. */ b43legacy_put_phy_into_reset(down_dev); /* Now start the new core. */ up_dev->phy.gmode = gmode; if (prev_status >= B43legacy_STAT_INITIALIZED) { err = b43legacy_wireless_core_init(up_dev); if (err) { b43legacyerr(wl, "Fatal: Could not initialize device" " for newly selected %s-PHY mode\n", phymode_to_string(new_mode)); goto init_failure; } } if (prev_status >= B43legacy_STAT_STARTED) { err = b43legacy_wireless_core_start(up_dev); if (err) { b43legacyerr(wl, "Fatal: Could not start device for " "newly selected %s-PHY mode\n", phymode_to_string(new_mode)); b43legacy_wireless_core_exit(up_dev); goto init_failure; } } B43legacy_WARN_ON(b43legacy_status(up_dev) != prev_status); b43legacy_shm_write32(up_dev, B43legacy_SHM_SHARED, 0x003E, 0); wl->current_dev = up_dev; return 0; init_failure: /* Whoops, failed to init the new core. No core is operating now. */ wl->current_dev = NULL; return err; } /* Write the short and long frame retry limit values. */ static void b43legacy_set_retry_limits(struct b43legacy_wldev *dev, unsigned int short_retry, unsigned int long_retry) { /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing * the chip-internal counter. */ short_retry = min(short_retry, (unsigned int)0xF); long_retry = min(long_retry, (unsigned int)0xF); b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0006, short_retry); b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0007, long_retry); } static int b43legacy_op_dev_config(struct ieee80211_hw *hw, u32 changed) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev; struct b43legacy_phy *phy; struct ieee80211_conf *conf = &hw->conf; unsigned long flags; unsigned int new_phymode = 0xFFFF; int antenna_tx; int err = 0; antenna_tx = B43legacy_ANTENNA_DEFAULT; mutex_lock(&wl->mutex); dev = wl->current_dev; phy = &dev->phy; if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) b43legacy_set_retry_limits(dev, conf->short_frame_max_tx_count, conf->long_frame_max_tx_count); changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS; if (!changed) goto out_unlock_mutex; /* Switch the PHY mode (if necessary). */ switch (conf->channel->band) { case IEEE80211_BAND_2GHZ: if (phy->type == B43legacy_PHYTYPE_B) new_phymode = B43legacy_PHYMODE_B; else new_phymode = B43legacy_PHYMODE_G; break; default: B43legacy_WARN_ON(1); } err = b43legacy_switch_phymode(wl, new_phymode); if (err) goto out_unlock_mutex; /* Disable IRQs while reconfiguring the device. * This makes it possible to drop the spinlock throughout * the reconfiguration process. */ spin_lock_irqsave(&wl->irq_lock, flags); if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { spin_unlock_irqrestore(&wl->irq_lock, flags); goto out_unlock_mutex; } b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); spin_unlock_irqrestore(&wl->irq_lock, flags); b43legacy_synchronize_irq(dev); /* Switch to the requested channel. * The firmware takes care of races with the TX handler. */ if (conf->channel->hw_value != phy->channel) b43legacy_radio_selectchannel(dev, conf->channel->hw_value, 0); dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); /* Adjust the desired TX power level. */ if (conf->power_level != 0) { if (conf->power_level != phy->power_level) { phy->power_level = conf->power_level; b43legacy_phy_xmitpower(dev); } } /* Antennas for RX and management frame TX. */ b43legacy_mgmtframe_txantenna(dev, antenna_tx); if (wl->radio_enabled != phy->radio_on) { if (wl->radio_enabled) { b43legacy_radio_turn_on(dev); b43legacyinfo(dev->wl, "Radio turned on by software\n"); if (!dev->radio_hw_enable) b43legacyinfo(dev->wl, "The hardware RF-kill" " button still turns the radio" " physically off. Press the" " button to turn it on.\n"); } else { b43legacy_radio_turn_off(dev, 0); b43legacyinfo(dev->wl, "Radio turned off by" " software\n"); } } spin_lock_irqsave(&wl->irq_lock, flags); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); mmiowb(); spin_unlock_irqrestore(&wl->irq_lock, flags); out_unlock_mutex: mutex_unlock(&wl->mutex); return err; } static void b43legacy_update_basic_rates(struct b43legacy_wldev *dev, u32 brates) { struct ieee80211_supported_band *sband = dev->wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; struct ieee80211_rate *rate; int i; u16 basic, direct, offset, basic_offset, rateptr; for (i = 0; i < sband->n_bitrates; i++) { rate = &sband->bitrates[i]; if (b43legacy_is_cck_rate(rate->hw_value)) { direct = B43legacy_SHM_SH_CCKDIRECT; basic = B43legacy_SHM_SH_CCKBASIC; offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value); offset &= 0xF; } else { direct = B43legacy_SHM_SH_OFDMDIRECT; basic = B43legacy_SHM_SH_OFDMBASIC; offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value); offset &= 0xF; } rate = ieee80211_get_response_rate(sband, brates, rate->bitrate); if (b43legacy_is_cck_rate(rate->hw_value)) { basic_offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value); basic_offset &= 0xF; } else { basic_offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value); basic_offset &= 0xF; } /* * Get the pointer that we need to point to * from the direct map */ rateptr = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, direct + 2 * basic_offset); /* and write it to the basic map */ b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, basic + 2 * offset, rateptr); } } static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf, u32 changed) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev; unsigned long flags; mutex_lock(&wl->mutex); B43legacy_WARN_ON(wl->vif != vif); dev = wl->current_dev; /* Disable IRQs while reconfiguring the device. * This makes it possible to drop the spinlock throughout * the reconfiguration process. */ spin_lock_irqsave(&wl->irq_lock, flags); if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { spin_unlock_irqrestore(&wl->irq_lock, flags); goto out_unlock_mutex; } b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); if (changed & BSS_CHANGED_BSSID) { b43legacy_synchronize_irq(dev); if (conf->bssid) memcpy(wl->bssid, conf->bssid, ETH_ALEN); else memset(wl->bssid, 0, ETH_ALEN); } if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) { if (changed & BSS_CHANGED_BEACON && (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) b43legacy_update_templates(wl); if (changed & BSS_CHANGED_BSSID) b43legacy_write_mac_bssid_templates(dev); } spin_unlock_irqrestore(&wl->irq_lock, flags); b43legacy_mac_suspend(dev); if (changed & BSS_CHANGED_BEACON_INT && (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) b43legacy_set_beacon_int(dev, conf->beacon_int); if (changed & BSS_CHANGED_BASIC_RATES) b43legacy_update_basic_rates(dev, conf->basic_rates); if (changed & BSS_CHANGED_ERP_SLOT) { if (conf->use_short_slot) b43legacy_short_slot_timing_enable(dev); else b43legacy_short_slot_timing_disable(dev); } b43legacy_mac_enable(dev); spin_lock_irqsave(&wl->irq_lock, flags); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); /* XXX: why? */ mmiowb(); spin_unlock_irqrestore(&wl->irq_lock, flags); out_unlock_mutex: mutex_unlock(&wl->mutex); } static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *fflags,u64 multicast) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; unsigned long flags; if (!dev) { *fflags = 0; return; } spin_lock_irqsave(&wl->irq_lock, flags); *fflags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; changed &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; wl->filter_flags = *fflags; if (changed && b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) b43legacy_adjust_opmode(dev); spin_unlock_irqrestore(&wl->irq_lock, flags); } /* Locking: wl->mutex */ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; unsigned long flags; int queue_num; if (b43legacy_status(dev) < B43legacy_STAT_STARTED) return; /* Disable and sync interrupts. We must do this before than * setting the status to INITIALIZED, as the interrupt handler * won't care about IRQs then. */ spin_lock_irqsave(&wl->irq_lock, flags); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); /* flush */ spin_unlock_irqrestore(&wl->irq_lock, flags); b43legacy_synchronize_irq(dev); b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED); mutex_unlock(&wl->mutex); /* Must unlock as it would otherwise deadlock. No races here. * Cancel the possibly running self-rearming periodic work. */ cancel_delayed_work_sync(&dev->periodic_work); cancel_work_sync(&wl->tx_work); mutex_lock(&wl->mutex); /* Drain all TX queues. */ for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { while (skb_queue_len(&wl->tx_queue[queue_num])) dev_kfree_skb(skb_dequeue(&wl->tx_queue[queue_num])); } b43legacy_mac_suspend(dev); free_irq(dev->dev->irq, dev); b43legacydbg(wl, "Wireless interface stopped\n"); } /* Locking: wl->mutex */ static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev) { int err; B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_INITIALIZED); drain_txstatus_queue(dev); err = request_irq(dev->dev->irq, b43legacy_interrupt_handler, IRQF_SHARED, KBUILD_MODNAME, dev); if (err) { b43legacyerr(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); goto out; } /* We are ready to run. */ ieee80211_wake_queues(dev->wl->hw); b43legacy_set_status(dev, B43legacy_STAT_STARTED); /* Start data flow (TX/RX) */ b43legacy_mac_enable(dev); b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); /* Start maintenance work */ b43legacy_periodic_tasks_setup(dev); b43legacydbg(dev->wl, "Wireless interface started\n"); out: return err; } /* Get PHY and RADIO versioning numbers */ static int b43legacy_phy_versioning(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u32 tmp; u8 analog_type; u8 phy_type; u8 phy_rev; u16 radio_manuf; u16 radio_ver; u16 radio_rev; int unsupported = 0; /* Get PHY versioning */ tmp = b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); analog_type = (tmp & B43legacy_PHYVER_ANALOG) >> B43legacy_PHYVER_ANALOG_SHIFT; phy_type = (tmp & B43legacy_PHYVER_TYPE) >> B43legacy_PHYVER_TYPE_SHIFT; phy_rev = (tmp & B43legacy_PHYVER_VERSION); switch (phy_type) { case B43legacy_PHYTYPE_B: if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 && phy_rev != 7) unsupported = 1; break; case B43legacy_PHYTYPE_G: if (phy_rev > 8) unsupported = 1; break; default: unsupported = 1; } if (unsupported) { b43legacyerr(dev->wl, "FOUND UNSUPPORTED PHY " "(Analog %u, Type %u, Revision %u)\n", analog_type, phy_type, phy_rev); return -EOPNOTSUPP; } b43legacydbg(dev->wl, "Found PHY: Analog %u, Type %u, Revision %u\n", analog_type, phy_type, phy_rev); /* Get RADIO versioning */ if (dev->dev->bus->chip_id == 0x4317) { if (dev->dev->bus->chip_rev == 0) tmp = 0x3205017F; else if (dev->dev->bus->chip_rev == 1) tmp = 0x4205017F; else tmp = 0x5205017F; } else { b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, B43legacy_RADIOCTL_ID); tmp = b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_HIGH); tmp <<= 16; b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, B43legacy_RADIOCTL_ID); tmp |= b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW); } radio_manuf = (tmp & 0x00000FFF); radio_ver = (tmp & 0x0FFFF000) >> 12; radio_rev = (tmp & 0xF0000000) >> 28; switch (phy_type) { case B43legacy_PHYTYPE_B: if ((radio_ver & 0xFFF0) != 0x2050) unsupported = 1; break; case B43legacy_PHYTYPE_G: if (radio_ver != 0x2050) unsupported = 1; break; default: B43legacy_BUG_ON(1); } if (unsupported) { b43legacyerr(dev->wl, "FOUND UNSUPPORTED RADIO " "(Manuf 0x%X, Version 0x%X, Revision %u)\n", radio_manuf, radio_ver, radio_rev); return -EOPNOTSUPP; } b43legacydbg(dev->wl, "Found Radio: Manuf 0x%X, Version 0x%X," " Revision %u\n", radio_manuf, radio_ver, radio_rev); phy->radio_manuf = radio_manuf; phy->radio_ver = radio_ver; phy->radio_rev = radio_rev; phy->analog = analog_type; phy->type = phy_type; phy->rev = phy_rev; return 0; } static void setup_struct_phy_for_init(struct b43legacy_wldev *dev, struct b43legacy_phy *phy) { struct b43legacy_lopair *lo; int i; memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); /* Assume the radio is enabled. If it's not enabled, the state will * immediately get fixed on the first periodic work run. */ dev->radio_hw_enable = true; phy->savedpctlreg = 0xFFFF; phy->aci_enable = false; phy->aci_wlan_automatic = false; phy->aci_hw_rssi = false; lo = phy->_lo_pairs; if (lo) memset(lo, 0, sizeof(struct b43legacy_lopair) * B43legacy_LO_COUNT); phy->max_lb_gain = 0; phy->trsw_rx_gain = 0; /* Set default attenuation values. */ phy->bbatt = b43legacy_default_baseband_attenuation(dev); phy->rfatt = b43legacy_default_radio_attenuation(dev); phy->txctl1 = b43legacy_default_txctl1(dev); phy->txpwr_offset = 0; /* NRSSI */ phy->nrssislope = 0; for (i = 0; i < ARRAY_SIZE(phy->nrssi); i++) phy->nrssi[i] = -1000; for (i = 0; i < ARRAY_SIZE(phy->nrssi_lt); i++) phy->nrssi_lt[i] = i; phy->lofcal = 0xFFFF; phy->initval = 0xFFFF; phy->interfmode = B43legacy_INTERFMODE_NONE; phy->channel = 0xFF; } static void setup_struct_wldev_for_init(struct b43legacy_wldev *dev) { /* Flags */ dev->dfq_valid = false; /* Stats */ memset(&dev->stats, 0, sizeof(dev->stats)); setup_struct_phy_for_init(dev, &dev->phy); /* IRQ related flags */ dev->irq_reason = 0; memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); dev->irq_mask = B43legacy_IRQ_MASKTEMPLATE; dev->mac_suspended = 1; /* Noise calculation context */ memset(&dev->noisecalc, 0, sizeof(dev->noisecalc)); } static void b43legacy_set_synth_pu_delay(struct b43legacy_wldev *dev, bool idle) { u16 pu_delay = 1050; if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC) || idle) pu_delay = 500; if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8)) pu_delay = max(pu_delay, (u16)2400); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_SPUWKUP, pu_delay); } /* Set the TSF CFP pre-TargetBeaconTransmissionTime. */ static void b43legacy_set_pretbtt(struct b43legacy_wldev *dev) { u16 pretbtt; /* The time value is in microseconds. */ if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) pretbtt = 2; else pretbtt = 250; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_PRETBTT, pretbtt); b43legacy_write16(dev, B43legacy_MMIO_TSF_CFP_PRETBTT, pretbtt); } /* Shutdown a wireless core */ /* Locking: wl->mutex */ static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u32 macctl; B43legacy_WARN_ON(b43legacy_status(dev) > B43legacy_STAT_INITIALIZED); if (b43legacy_status(dev) != B43legacy_STAT_INITIALIZED) return; b43legacy_set_status(dev, B43legacy_STAT_UNINIT); /* Stop the microcode PSM. */ macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); macctl &= ~B43legacy_MACCTL_PSM_RUN; macctl |= B43legacy_MACCTL_PSM_JMP0; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); b43legacy_leds_exit(dev); b43legacy_rng_exit(dev->wl); b43legacy_pio_free(dev); b43legacy_dma_free(dev); b43legacy_chip_exit(dev); b43legacy_radio_turn_off(dev, 1); b43legacy_switch_analog(dev, 0); if (phy->dyn_tssi_tbl) kfree(phy->tssi2dbm); kfree(phy->lo_control); phy->lo_control = NULL; if (dev->wl->current_beacon) { dev_kfree_skb_any(dev->wl->current_beacon); dev->wl->current_beacon = NULL; } ssb_device_disable(dev->dev, 0); ssb_bus_may_powerdown(dev->dev->bus); } static void prepare_phy_data_for_init(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; int i; /* Set default attenuation values. */ phy->bbatt = b43legacy_default_baseband_attenuation(dev); phy->rfatt = b43legacy_default_radio_attenuation(dev); phy->txctl1 = b43legacy_default_txctl1(dev); phy->txctl2 = 0xFFFF; phy->txpwr_offset = 0; /* NRSSI */ phy->nrssislope = 0; for (i = 0; i < ARRAY_SIZE(phy->nrssi); i++) phy->nrssi[i] = -1000; for (i = 0; i < ARRAY_SIZE(phy->nrssi_lt); i++) phy->nrssi_lt[i] = i; phy->lofcal = 0xFFFF; phy->initval = 0xFFFF; phy->aci_enable = false; phy->aci_wlan_automatic = false; phy->aci_hw_rssi = false; phy->antenna_diversity = 0xFFFF; memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); /* Flags */ phy->calibrated = 0; if (phy->_lo_pairs) memset(phy->_lo_pairs, 0, sizeof(struct b43legacy_lopair) * B43legacy_LO_COUNT); memset(phy->loopback_gain, 0, sizeof(phy->loopback_gain)); } /* Initialize a wireless core */ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; struct ssb_bus *bus = dev->dev->bus; struct b43legacy_phy *phy = &dev->phy; struct ssb_sprom *sprom = &dev->dev->bus->sprom; int err; u32 hf; u32 tmp; B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_UNINIT); err = ssb_bus_powerup(bus, 0); if (err) goto out; if (!ssb_device_is_enabled(dev->dev)) { tmp = phy->gmode ? B43legacy_TMSLOW_GMODE : 0; b43legacy_wireless_core_reset(dev, tmp); } if ((phy->type == B43legacy_PHYTYPE_B) || (phy->type == B43legacy_PHYTYPE_G)) { phy->_lo_pairs = kzalloc(sizeof(struct b43legacy_lopair) * B43legacy_LO_COUNT, GFP_KERNEL); if (!phy->_lo_pairs) return -ENOMEM; } setup_struct_wldev_for_init(dev); err = b43legacy_phy_init_tssi2dbm_table(dev); if (err) goto err_kfree_lo_control; /* Enable IRQ routing to this device. */ ssb_pcicore_dev_irqvecs_enable(&bus->pcicore, dev->dev); prepare_phy_data_for_init(dev); b43legacy_phy_calibrate(dev); err = b43legacy_chip_init(dev); if (err) goto err_kfree_tssitbl; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_WLCOREREV, dev->dev->id.revision); hf = b43legacy_hf_read(dev); if (phy->type == B43legacy_PHYTYPE_G) { hf |= B43legacy_HF_SYMW; if (phy->rev == 1) hf |= B43legacy_HF_GDCW; if (sprom->boardflags_lo & B43legacy_BFL_PACTRL) hf |= B43legacy_HF_OFDMPABOOST; } else if (phy->type == B43legacy_PHYTYPE_B) { hf |= B43legacy_HF_SYMW; if (phy->rev >= 2 && phy->radio_ver == 0x2050) hf &= ~B43legacy_HF_GDCW; } b43legacy_hf_write(dev, hf); b43legacy_set_retry_limits(dev, B43legacy_DEFAULT_SHORT_RETRY_LIMIT, B43legacy_DEFAULT_LONG_RETRY_LIMIT); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0044, 3); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0046, 2); /* Disable sending probe responses from firmware. * Setting the MaxTime to one usec will always trigger * a timeout, so we never send any probe resp. * A timeout of zero is infinite. */ b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, B43legacy_SHM_SH_PRMAXTIME, 1); b43legacy_rate_memory_init(dev); /* Minimum Contention Window */ if (phy->type == B43legacy_PHYTYPE_B) b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0003, 31); else b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0003, 15); /* Maximum Contention Window */ b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0004, 1023); do { if (b43legacy_using_pio(dev)) err = b43legacy_pio_init(dev); else { err = b43legacy_dma_init(dev); if (!err) b43legacy_qos_init(dev); } } while (err == -EAGAIN); if (err) goto err_chip_exit; b43legacy_set_synth_pu_delay(dev, 1); ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */ b43legacy_upload_card_macaddress(dev); b43legacy_security_init(dev); b43legacy_rng_init(wl); ieee80211_wake_queues(dev->wl->hw); b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED); b43legacy_leds_init(dev); out: return err; err_chip_exit: b43legacy_chip_exit(dev); err_kfree_tssitbl: if (phy->dyn_tssi_tbl) kfree(phy->tssi2dbm); err_kfree_lo_control: kfree(phy->lo_control); phy->lo_control = NULL; ssb_bus_may_powerdown(bus); B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_UNINIT); return err; } static int b43legacy_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev; unsigned long flags; int err = -EOPNOTSUPP; /* TODO: allow WDS/AP devices to coexist */ if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_WDS && vif->type != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; mutex_lock(&wl->mutex); if (wl->operating) goto out_mutex_unlock; b43legacydbg(wl, "Adding Interface type %d\n", vif->type); dev = wl->current_dev; wl->operating = true; wl->vif = vif; wl->if_type = vif->type; memcpy(wl->mac_addr, vif->addr, ETH_ALEN); spin_lock_irqsave(&wl->irq_lock, flags); b43legacy_adjust_opmode(dev); b43legacy_set_pretbtt(dev); b43legacy_set_synth_pu_delay(dev, 0); b43legacy_upload_card_macaddress(dev); spin_unlock_irqrestore(&wl->irq_lock, flags); err = 0; out_mutex_unlock: mutex_unlock(&wl->mutex); return err; } static void b43legacy_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; unsigned long flags; b43legacydbg(wl, "Removing Interface type %d\n", vif->type); mutex_lock(&wl->mutex); B43legacy_WARN_ON(!wl->operating); B43legacy_WARN_ON(wl->vif != vif); wl->vif = NULL; wl->operating = false; spin_lock_irqsave(&wl->irq_lock, flags); b43legacy_adjust_opmode(dev); memset(wl->mac_addr, 0, ETH_ALEN); b43legacy_upload_card_macaddress(dev); spin_unlock_irqrestore(&wl->irq_lock, flags); mutex_unlock(&wl->mutex); } static int b43legacy_op_start(struct ieee80211_hw *hw) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; int did_init = 0; int err = 0; /* Kill all old instance specific information to make sure * the card won't use it in the short timeframe between start * and mac80211 reconfiguring it. */ memset(wl->bssid, 0, ETH_ALEN); memset(wl->mac_addr, 0, ETH_ALEN); wl->filter_flags = 0; wl->beacon0_uploaded = false; wl->beacon1_uploaded = false; wl->beacon_templates_virgin = true; wl->radio_enabled = true; mutex_lock(&wl->mutex); if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { err = b43legacy_wireless_core_init(dev); if (err) goto out_mutex_unlock; did_init = 1; } if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { err = b43legacy_wireless_core_start(dev); if (err) { if (did_init) b43legacy_wireless_core_exit(dev); goto out_mutex_unlock; } } wiphy_rfkill_start_polling(hw->wiphy); out_mutex_unlock: mutex_unlock(&wl->mutex); return err; } static void b43legacy_op_stop(struct ieee80211_hw *hw) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; cancel_work_sync(&(wl->beacon_update_trigger)); mutex_lock(&wl->mutex); if (b43legacy_status(dev) >= B43legacy_STAT_STARTED) b43legacy_wireless_core_stop(dev); b43legacy_wireless_core_exit(dev); wl->radio_enabled = false; mutex_unlock(&wl->mutex); } static int b43legacy_op_beacon_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); unsigned long flags; spin_lock_irqsave(&wl->irq_lock, flags); b43legacy_update_templates(wl); spin_unlock_irqrestore(&wl->irq_lock, flags); return 0; } static int b43legacy_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; survey->filled = SURVEY_INFO_NOISE_DBM; survey->noise = dev->stats.link_noise; return 0; } static const struct ieee80211_ops b43legacy_hw_ops = { .tx = b43legacy_op_tx, .conf_tx = b43legacy_op_conf_tx, .add_interface = b43legacy_op_add_interface, .remove_interface = b43legacy_op_remove_interface, .config = b43legacy_op_dev_config, .bss_info_changed = b43legacy_op_bss_info_changed, .configure_filter = b43legacy_op_configure_filter, .get_stats = b43legacy_op_get_stats, .start = b43legacy_op_start, .stop = b43legacy_op_stop, .set_tim = b43legacy_op_beacon_set_tim, .get_survey = b43legacy_op_get_survey, .rfkill_poll = b43legacy_rfkill_poll, }; /* Hard-reset the chip. Do not call this directly. * Use b43legacy_controller_restart() */ static void b43legacy_chip_reset(struct work_struct *work) { struct b43legacy_wldev *dev = container_of(work, struct b43legacy_wldev, restart_work); struct b43legacy_wl *wl = dev->wl; int err = 0; int prev_status; mutex_lock(&wl->mutex); prev_status = b43legacy_status(dev); /* Bring the device down... */ if (prev_status >= B43legacy_STAT_STARTED) b43legacy_wireless_core_stop(dev); if (prev_status >= B43legacy_STAT_INITIALIZED) b43legacy_wireless_core_exit(dev); /* ...and up again. */ if (prev_status >= B43legacy_STAT_INITIALIZED) { err = b43legacy_wireless_core_init(dev); if (err) goto out; } if (prev_status >= B43legacy_STAT_STARTED) { err = b43legacy_wireless_core_start(dev); if (err) { b43legacy_wireless_core_exit(dev); goto out; } } out: if (err) wl->current_dev = NULL; /* Failed to init the dev. */ mutex_unlock(&wl->mutex); if (err) b43legacyerr(wl, "Controller restart FAILED\n"); else b43legacyinfo(wl, "Controller restarted\n"); } static int b43legacy_setup_modes(struct b43legacy_wldev *dev, int have_bphy, int have_gphy) { struct ieee80211_hw *hw = dev->wl->hw; struct b43legacy_phy *phy = &dev->phy; phy->possible_phymodes = 0; if (have_bphy) { hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43legacy_band_2GHz_BPHY; phy->possible_phymodes |= B43legacy_PHYMODE_B; } if (have_gphy) { hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43legacy_band_2GHz_GPHY; phy->possible_phymodes |= B43legacy_PHYMODE_G; } return 0; } static void b43legacy_wireless_core_detach(struct b43legacy_wldev *dev) { /* We release firmware that late to not be required to re-request * is all the time when we reinit the core. */ b43legacy_release_firmware(dev); } static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; struct ssb_bus *bus = dev->dev->bus; struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL; int err; int have_bphy = 0; int have_gphy = 0; u32 tmp; /* Do NOT do any device initialization here. * Do it in wireless_core_init() instead. * This function is for gathering basic information about the HW, only. * Also some structs may be set up here. But most likely you want to * have that in core_init(), too. */ err = ssb_bus_powerup(bus, 0); if (err) { b43legacyerr(wl, "Bus powerup failed\n"); goto out; } /* Get the PHY type. */ if (dev->dev->id.revision >= 5) { u32 tmshigh; tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH); have_gphy = !!(tmshigh & B43legacy_TMSHIGH_GPHY); if (!have_gphy) have_bphy = 1; } else if (dev->dev->id.revision == 4) have_gphy = 1; else have_bphy = 1; dev->phy.gmode = (have_gphy || have_bphy); dev->phy.radio_on = true; tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0; b43legacy_wireless_core_reset(dev, tmp); err = b43legacy_phy_versioning(dev); if (err) goto err_powerdown; /* Check if this device supports multiband. */ if (!pdev || (pdev->device != 0x4312 && pdev->device != 0x4319 && pdev->device != 0x4324)) { /* No multiband support. */ have_bphy = 0; have_gphy = 0; switch (dev->phy.type) { case B43legacy_PHYTYPE_B: have_bphy = 1; break; case B43legacy_PHYTYPE_G: have_gphy = 1; break; default: B43legacy_BUG_ON(1); } } dev->phy.gmode = (have_gphy || have_bphy); tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0; b43legacy_wireless_core_reset(dev, tmp); err = b43legacy_validate_chipaccess(dev); if (err) goto err_powerdown; err = b43legacy_setup_modes(dev, have_bphy, have_gphy); if (err) goto err_powerdown; /* Now set some default "current_dev" */ if (!wl->current_dev) wl->current_dev = dev; INIT_WORK(&dev->restart_work, b43legacy_chip_reset); b43legacy_radio_turn_off(dev, 1); b43legacy_switch_analog(dev, 0); ssb_device_disable(dev->dev, 0); ssb_bus_may_powerdown(bus); out: return err; err_powerdown: ssb_bus_may_powerdown(bus); return err; } static void b43legacy_one_core_detach(struct ssb_device *dev) { struct b43legacy_wldev *wldev; struct b43legacy_wl *wl; /* Do not cancel ieee80211-workqueue based work here. * See comment in b43legacy_remove(). */ wldev = ssb_get_drvdata(dev); wl = wldev->wl; b43legacy_debugfs_remove_device(wldev); b43legacy_wireless_core_detach(wldev); list_del(&wldev->list); wl->nr_devs--; ssb_set_drvdata(dev, NULL); kfree(wldev); } static int b43legacy_one_core_attach(struct ssb_device *dev, struct b43legacy_wl *wl) { struct b43legacy_wldev *wldev; int err = -ENOMEM; wldev = kzalloc(sizeof(*wldev), GFP_KERNEL); if (!wldev) goto out; wldev->dev = dev; wldev->wl = wl; b43legacy_set_status(wldev, B43legacy_STAT_UNINIT); wldev->bad_frames_preempt = modparam_bad_frames_preempt; tasklet_init(&wldev->isr_tasklet, (void (*)(unsigned long))b43legacy_interrupt_tasklet, (unsigned long)wldev); if (modparam_pio) wldev->__using_pio = true; INIT_LIST_HEAD(&wldev->list); err = b43legacy_wireless_core_attach(wldev); if (err) goto err_kfree_wldev; list_add(&wldev->list, &wl->devlist); wl->nr_devs++; ssb_set_drvdata(dev, wldev); b43legacy_debugfs_add_device(wldev); out: return err; err_kfree_wldev: kfree(wldev); return err; } static void b43legacy_sprom_fixup(struct ssb_bus *bus) { /* boardflags workarounds */ if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && bus->boardinfo.type == 0x4E && bus->sprom.board_rev > 0x40) bus->sprom.boardflags_lo |= B43legacy_BFL_PACTRL; } static void b43legacy_wireless_exit(struct ssb_device *dev, struct b43legacy_wl *wl) { struct ieee80211_hw *hw = wl->hw; ssb_set_devtypedata(dev, NULL); ieee80211_free_hw(hw); } static int b43legacy_wireless_init(struct ssb_device *dev) { struct ssb_sprom *sprom = &dev->bus->sprom; struct ieee80211_hw *hw; struct b43legacy_wl *wl; int err = -ENOMEM; int queue_num; b43legacy_sprom_fixup(dev->bus); hw = ieee80211_alloc_hw(sizeof(*wl), &b43legacy_hw_ops); if (!hw) { b43legacyerr(NULL, "Could not allocate ieee80211 device\n"); goto out; } /* fill hw info */ hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_WDS) | BIT(NL80211_IFTYPE_ADHOC); hw->queues = 1; /* FIXME: hardware has more queues */ hw->max_rates = 2; SET_IEEE80211_DEV(hw, dev->dev); if (is_valid_ether_addr(sprom->et1mac)) SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); else SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); /* Get and initialize struct b43legacy_wl */ wl = hw_to_b43legacy_wl(hw); memset(wl, 0, sizeof(*wl)); wl->hw = hw; spin_lock_init(&wl->irq_lock); spin_lock_init(&wl->leds_lock); mutex_init(&wl->mutex); INIT_LIST_HEAD(&wl->devlist); INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work); INIT_WORK(&wl->tx_work, b43legacy_tx_work); /* Initialize queues and flags. */ for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { skb_queue_head_init(&wl->tx_queue[queue_num]); wl->tx_queue_stopped[queue_num] = 0; } ssb_set_devtypedata(dev, wl); b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n", dev->bus->chip_id, dev->id.revision); err = 0; out: return err; } static int b43legacy_probe(struct ssb_device *dev, const struct ssb_device_id *id) { struct b43legacy_wl *wl; int err; int first = 0; wl = ssb_get_devtypedata(dev); if (!wl) { /* Probing the first core - setup common struct b43legacy_wl */ first = 1; err = b43legacy_wireless_init(dev); if (err) goto out; wl = ssb_get_devtypedata(dev); B43legacy_WARN_ON(!wl); } err = b43legacy_one_core_attach(dev, wl); if (err) goto err_wireless_exit; /* setup and start work to load firmware */ INIT_WORK(&wl->firmware_load, b43legacy_request_firmware); schedule_work(&wl->firmware_load); out: return err; err_wireless_exit: if (first) b43legacy_wireless_exit(dev, wl); return err; } static void b43legacy_remove(struct ssb_device *dev) { struct b43legacy_wl *wl = ssb_get_devtypedata(dev); struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); /* We must cancel any work here before unregistering from ieee80211, * as the ieee80211 unreg will destroy the workqueue. */ cancel_work_sync(&wldev->restart_work); cancel_work_sync(&wl->firmware_load); B43legacy_WARN_ON(!wl); if (wl->current_dev == wldev) ieee80211_unregister_hw(wl->hw); b43legacy_one_core_detach(dev); if (list_empty(&wl->devlist)) /* Last core on the chip unregistered. * We can destroy common struct b43legacy_wl. */ b43legacy_wireless_exit(dev, wl); } /* Perform a hardware reset. This can be called from any context. */ void b43legacy_controller_restart(struct b43legacy_wldev *dev, const char *reason) { /* Must avoid requeueing, if we are in shutdown. */ if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) return; b43legacyinfo(dev->wl, "Controller RESET (%s) ...\n", reason); ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } #ifdef CONFIG_PM static int b43legacy_suspend(struct ssb_device *dev, pm_message_t state) { struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); struct b43legacy_wl *wl = wldev->wl; b43legacydbg(wl, "Suspending...\n"); mutex_lock(&wl->mutex); wldev->suspend_init_status = b43legacy_status(wldev); if (wldev->suspend_init_status >= B43legacy_STAT_STARTED) b43legacy_wireless_core_stop(wldev); if (wldev->suspend_init_status >= B43legacy_STAT_INITIALIZED) b43legacy_wireless_core_exit(wldev); mutex_unlock(&wl->mutex); b43legacydbg(wl, "Device suspended.\n"); return 0; } static int b43legacy_resume(struct ssb_device *dev) { struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); struct b43legacy_wl *wl = wldev->wl; int err = 0; b43legacydbg(wl, "Resuming...\n"); mutex_lock(&wl->mutex); if (wldev->suspend_init_status >= B43legacy_STAT_INITIALIZED) { err = b43legacy_wireless_core_init(wldev); if (err) { b43legacyerr(wl, "Resume failed at core init\n"); goto out; } } if (wldev->suspend_init_status >= B43legacy_STAT_STARTED) { err = b43legacy_wireless_core_start(wldev); if (err) { b43legacy_wireless_core_exit(wldev); b43legacyerr(wl, "Resume failed at core start\n"); goto out; } } b43legacydbg(wl, "Device resumed.\n"); out: mutex_unlock(&wl->mutex); return err; } #else /* CONFIG_PM */ # define b43legacy_suspend NULL # define b43legacy_resume NULL #endif /* CONFIG_PM */ static struct ssb_driver b43legacy_ssb_driver = { .name = KBUILD_MODNAME, .id_table = b43legacy_ssb_tbl, .probe = b43legacy_probe, .remove = b43legacy_remove, .suspend = b43legacy_suspend, .resume = b43legacy_resume, }; static void b43legacy_print_driverinfo(void) { const char *feat_pci = "", *feat_leds = "", *feat_pio = "", *feat_dma = ""; #ifdef CONFIG_B43LEGACY_PCI_AUTOSELECT feat_pci = "P"; #endif #ifdef CONFIG_B43LEGACY_LEDS feat_leds = "L"; #endif #ifdef CONFIG_B43LEGACY_PIO feat_pio = "I"; #endif #ifdef CONFIG_B43LEGACY_DMA feat_dma = "D"; #endif printk(KERN_INFO "Broadcom 43xx-legacy driver loaded " "[ Features: %s%s%s%s ]\n", feat_pci, feat_leds, feat_pio, feat_dma); } static int __init b43legacy_init(void) { int err; b43legacy_debugfs_init(); err = ssb_driver_register(&b43legacy_ssb_driver); if (err) goto err_dfs_exit; b43legacy_print_driverinfo(); return err; err_dfs_exit: b43legacy_debugfs_exit(); return err; } static void __exit b43legacy_exit(void) { ssb_driver_unregister(&b43legacy_ssb_driver); b43legacy_debugfs_exit(); } module_init(b43legacy_init) module_exit(b43legacy_exit) compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/xmit.h0000644000175000017500000002105112026211315023367 0ustar mcgrofmcgrof#ifndef B43legacy_XMIT_H_ #define B43legacy_XMIT_H_ #include "main.h" #define _b43legacy_declare_plcp_hdr(size) \ struct b43legacy_plcp_hdr##size { \ union { \ __le32 data; \ __u8 raw[size]; \ } __packed; \ } __packed /* struct b43legacy_plcp_hdr4 */ _b43legacy_declare_plcp_hdr(4); /* struct b43legacy_plcp_hdr6 */ _b43legacy_declare_plcp_hdr(6); #undef _b43legacy_declare_plcp_hdr /* TX header for v3 firmware */ struct b43legacy_txhdr_fw3 { __le32 mac_ctl; /* MAC TX control */ __le16 mac_frame_ctl; /* Copy of the FrameControl */ __le16 tx_fes_time_norm; /* TX FES Time Normal */ __le16 phy_ctl; /* PHY TX control */ __u8 iv[16]; /* Encryption IV */ __u8 tx_receiver[6]; /* TX Frame Receiver address */ __le16 tx_fes_time_fb; /* TX FES Time Fallback */ struct b43legacy_plcp_hdr4 rts_plcp_fb; /* RTS fallback PLCP */ __le16 rts_dur_fb; /* RTS fallback duration */ struct b43legacy_plcp_hdr4 plcp_fb; /* Fallback PLCP */ __le16 dur_fb; /* Fallback duration */ PAD_BYTES(2); __le16 cookie; __le16 unknown_scb_stuff; struct b43legacy_plcp_hdr6 rts_plcp; /* RTS PLCP */ __u8 rts_frame[18]; /* The RTS frame (if used) */ struct b43legacy_plcp_hdr6 plcp; } __packed; /* MAC TX control */ #define B43legacy_TX4_MAC_KEYIDX 0x0FF00000 /* Security key index */ #define B43legacy_TX4_MAC_KEYIDX_SHIFT 20 #define B43legacy_TX4_MAC_KEYALG 0x00070000 /* Security key algorithm */ #define B43legacy_TX4_MAC_KEYALG_SHIFT 16 #define B43legacy_TX4_MAC_LIFETIME 0x00001000 #define B43legacy_TX4_MAC_FRAMEBURST 0x00000800 #define B43legacy_TX4_MAC_SENDCTS 0x00000400 #define B43legacy_TX4_MAC_AMPDU 0x00000300 #define B43legacy_TX4_MAC_AMPDU_SHIFT 8 #define B43legacy_TX4_MAC_CTSFALLBACKOFDM 0x00000200 #define B43legacy_TX4_MAC_FALLBACKOFDM 0x00000100 #define B43legacy_TX4_MAC_5GHZ 0x00000080 #define B43legacy_TX4_MAC_IGNPMQ 0x00000020 #define B43legacy_TX4_MAC_HWSEQ 0x00000010 /* Use Hardware Seq No */ #define B43legacy_TX4_MAC_STMSDU 0x00000008 /* Start MSDU */ #define B43legacy_TX4_MAC_SENDRTS 0x00000004 #define B43legacy_TX4_MAC_LONGFRAME 0x00000002 #define B43legacy_TX4_MAC_ACK 0x00000001 /* Extra Frame Types */ #define B43legacy_TX4_EFT_FBOFDM 0x0001 /* Data frame fb rate type */ #define B43legacy_TX4_EFT_RTSOFDM 0x0004 /* RTS/CTS rate type */ #define B43legacy_TX4_EFT_RTSFBOFDM 0x0010 /* RTS/CTS fallback rate type */ /* PHY TX control word */ #define B43legacy_TX4_PHY_ENC 0x0003 /* Data frame encoding */ #define B43legacy_TX4_PHY_ENC_CCK 0x0000 /* CCK */ #define B43legacy_TX4_PHY_ENC_OFDM 0x0001 /* Data frame rate type */ #define B43legacy_TX4_PHY_SHORTPRMBL 0x0010 /* Use short preamble */ #define B43legacy_TX4_PHY_ANT 0x03C0 /* Antenna selection */ #define B43legacy_TX4_PHY_ANT0 0x0000 /* Use antenna 0 */ #define B43legacy_TX4_PHY_ANT1 0x0100 /* Use antenna 1 */ #define B43legacy_TX4_PHY_ANTLAST 0x0300 /* Use last used antenna */ int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, u8 *txhdr, const unsigned char *fragment_data, unsigned int fragment_len, struct ieee80211_tx_info *info, u16 cookie); /* Transmit Status */ struct b43legacy_txstatus { u16 cookie; /* The cookie from the txhdr */ u16 seq; /* Sequence number */ u8 phy_stat; /* PHY TX status */ u8 frame_count; /* Frame transmit count */ u8 rts_count; /* RTS transmit count */ u8 supp_reason; /* Suppression reason */ /* flags */ u8 pm_indicated;/* PM mode indicated to AP */ u8 intermediate;/* Intermediate status notification */ u8 for_ampdu; /* Status is for an AMPDU (afterburner) */ u8 acked; /* Wireless ACK received */ }; /* txstatus supp_reason values */ enum { B43legacy_TXST_SUPP_NONE, /* Not suppressed */ B43legacy_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */ B43legacy_TXST_SUPP_FLUSH, /* Suppressed due to flush request */ B43legacy_TXST_SUPP_PREV, /* Previous fragment failed */ B43legacy_TXST_SUPP_CHAN, /* Channel mismatch */ B43legacy_TXST_SUPP_LIFE, /* Lifetime expired */ B43legacy_TXST_SUPP_UNDER, /* Buffer underflow */ B43legacy_TXST_SUPP_ABNACK, /* Afterburner NACK */ }; /* Transmit Status as received through DMA/PIO on old chips */ struct b43legacy_hwtxstatus { PAD_BYTES(4); __le16 cookie; u8 flags; u8 count; PAD_BYTES(2); __le16 seq; u8 phy_stat; PAD_BYTES(1); } __packed; /* Receive header for v3 firmware. */ struct b43legacy_rxhdr_fw3 { __le16 frame_len; /* Frame length */ PAD_BYTES(2); __le16 phy_status0; /* PHY RX Status 0 */ __u8 jssi; /* PHY RX Status 1: JSSI */ __u8 sig_qual; /* PHY RX Status 1: Signal Quality */ PAD_BYTES(2); /* PHY RX Status 2 */ __le16 phy_status3; /* PHY RX Status 3 */ __le16 mac_status; /* MAC RX status */ __le16 mac_time; __le16 channel; } __packed; /* PHY RX Status 0 */ #define B43legacy_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */ #define B43legacy_RX_PHYST0_PLCPHCF 0x0200 #define B43legacy_RX_PHYST0_PLCPFV 0x0100 #define B43legacy_RX_PHYST0_SHORTPRMBL 0x0080 /* Recvd with Short Preamble */ #define B43legacy_RX_PHYST0_LCRS 0x0040 #define B43legacy_RX_PHYST0_ANT 0x0020 /* Antenna */ #define B43legacy_RX_PHYST0_UNSRATE 0x0010 #define B43legacy_RX_PHYST0_CLIP 0x000C #define B43legacy_RX_PHYST0_CLIP_SHIFT 2 #define B43legacy_RX_PHYST0_FTYPE 0x0003 /* Frame type */ #define B43legacy_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */ #define B43legacy_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */ #define B43legacy_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */ #define B43legacy_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */ /* PHY RX Status 2 */ #define B43legacy_RX_PHYST2_LNAG 0xC000 /* LNA Gain */ #define B43legacy_RX_PHYST2_LNAG_SHIFT 14 #define B43legacy_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */ #define B43legacy_RX_PHYST2_PNAG_SHIFT 10 #define B43legacy_RX_PHYST2_FOFF 0x03FF /* F offset */ /* PHY RX Status 3 */ #define B43legacy_RX_PHYST3_DIGG 0x1800 /* DIG Gain */ #define B43legacy_RX_PHYST3_DIGG_SHIFT 11 #define B43legacy_RX_PHYST3_TRSTATE 0x0400 /* TR state */ /* MAC RX Status */ #define B43legacy_RX_MAC_BEACONSENT 0x00008000 /* Beacon send flag */ #define B43legacy_RX_MAC_KEYIDX 0x000007E0 /* Key index */ #define B43legacy_RX_MAC_KEYIDX_SHIFT 5 #define B43legacy_RX_MAC_DECERR 0x00000010 /* Decrypt error */ #define B43legacy_RX_MAC_DEC 0x00000008 /* Decryption attempted */ #define B43legacy_RX_MAC_PADDING 0x00000004 /* Pad bytes present */ #define B43legacy_RX_MAC_RESP 0x00000002 /* Response frame xmitted */ #define B43legacy_RX_MAC_FCSERR 0x00000001 /* FCS error */ /* RX channel */ #define B43legacy_RX_CHAN_GAIN 0xFC00 /* Gain */ #define B43legacy_RX_CHAN_GAIN_SHIFT 10 #define B43legacy_RX_CHAN_ID 0x03FC /* Channel ID */ #define B43legacy_RX_CHAN_ID_SHIFT 2 #define B43legacy_RX_CHAN_PHYTYPE 0x0003 /* PHY type */ u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate); u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate); void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp, const u16 octets, const u8 bitrate); void b43legacy_rx(struct b43legacy_wldev *dev, struct sk_buff *skb, const void *_rxhdr); void b43legacy_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status); void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev, const struct b43legacy_hwtxstatus *hw); void b43legacy_tx_suspend(struct b43legacy_wldev *dev); void b43legacy_tx_resume(struct b43legacy_wldev *dev); #define B43legacy_NR_QOSPARMS 22 enum { B43legacy_QOSPARM_TXOP = 0, B43legacy_QOSPARM_CWMIN, B43legacy_QOSPARM_CWMAX, B43legacy_QOSPARM_CWCUR, B43legacy_QOSPARM_AIFS, B43legacy_QOSPARM_BSLOTS, B43legacy_QOSPARM_REGGAP, B43legacy_QOSPARM_STATUS, }; void b43legacy_qos_init(struct b43legacy_wldev *dev); /* Helper functions for converting the key-table index from "firmware-format" * to "raw-format" and back. The firmware API changed for this at some revision. * We need to account for that here. */ static inline int b43legacy_new_kidx_api(struct b43legacy_wldev *dev) { /* FIXME: Not sure the change was at rev 351 */ return (dev->fw.rev >= 351); } static inline u8 b43legacy_kidx_to_fw(struct b43legacy_wldev *dev, u8 raw_kidx) { u8 firmware_kidx; if (b43legacy_new_kidx_api(dev)) firmware_kidx = raw_kidx; else { if (raw_kidx >= 4) /* Is per STA key? */ firmware_kidx = raw_kidx - 4; else firmware_kidx = raw_kidx; /* TX default key */ } return firmware_kidx; } static inline u8 b43legacy_kidx_to_raw(struct b43legacy_wldev *dev, u8 firmware_kidx) { u8 raw_kidx; if (b43legacy_new_kidx_api(dev)) raw_kidx = firmware_kidx; else /* RX default keys or per STA keys */ raw_kidx = firmware_kidx + 4; return raw_kidx; } #endif /* B43legacy_XMIT_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/xmit.c0000644000175000017500000004335012026211315023370 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver Transmission (TX/RX) related functions. Copyright (C) 2005 Martin Langer Copyright (C) 2005 Stefano Brivio Copyright (C) 2005, 2006 Michael Buesch Copyright (C) 2005 Danny van Dyk Copyright (C) 2005 Andreas Jaggi Copyright (C) 2007 Larry Finger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "xmit.h" #include "phy.h" #include "dma.h" #include "pio.h" /* Extract the bitrate out of a CCK PLCP header. */ static u8 b43legacy_plcp_get_bitrate_idx_cck(struct b43legacy_plcp_hdr6 *plcp) { switch (plcp->raw[0]) { case 0x0A: return 0; case 0x14: return 1; case 0x37: return 2; case 0x6E: return 3; } B43legacy_BUG_ON(1); return -1; } /* Extract the bitrate out of an OFDM PLCP header. */ static u8 b43legacy_plcp_get_bitrate_idx_ofdm(struct b43legacy_plcp_hdr6 *plcp, bool aphy) { int base = aphy ? 0 : 4; switch (plcp->raw[0] & 0xF) { case 0xB: return base + 0; case 0xF: return base + 1; case 0xA: return base + 2; case 0xE: return base + 3; case 0x9: return base + 4; case 0xD: return base + 5; case 0x8: return base + 6; case 0xC: return base + 7; } B43legacy_BUG_ON(1); return -1; } u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate) { switch (bitrate) { case B43legacy_CCK_RATE_1MB: return 0x0A; case B43legacy_CCK_RATE_2MB: return 0x14; case B43legacy_CCK_RATE_5MB: return 0x37; case B43legacy_CCK_RATE_11MB: return 0x6E; } B43legacy_BUG_ON(1); return 0; } u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate) { switch (bitrate) { case B43legacy_OFDM_RATE_6MB: return 0xB; case B43legacy_OFDM_RATE_9MB: return 0xF; case B43legacy_OFDM_RATE_12MB: return 0xA; case B43legacy_OFDM_RATE_18MB: return 0xE; case B43legacy_OFDM_RATE_24MB: return 0x9; case B43legacy_OFDM_RATE_36MB: return 0xD; case B43legacy_OFDM_RATE_48MB: return 0x8; case B43legacy_OFDM_RATE_54MB: return 0xC; } B43legacy_BUG_ON(1); return 0; } void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp, const u16 octets, const u8 bitrate) { __le32 *data = &(plcp->data); __u8 *raw = plcp->raw; if (b43legacy_is_ofdm_rate(bitrate)) { u16 d; d = b43legacy_plcp_get_ratecode_ofdm(bitrate); B43legacy_WARN_ON(octets & 0xF000); d |= (octets << 5); *data = cpu_to_le32(d); } else { u32 plen; plen = octets * 16 / bitrate; if ((octets * 16 % bitrate) > 0) { plen++; if ((bitrate == B43legacy_CCK_RATE_11MB) && ((octets * 8 % 11) < 4)) raw[1] = 0x84; else raw[1] = 0x04; } else raw[1] = 0x04; *data |= cpu_to_le32(plen << 16); raw[0] = b43legacy_plcp_get_ratecode_cck(bitrate); } } static u8 b43legacy_calc_fallback_rate(u8 bitrate) { switch (bitrate) { case B43legacy_CCK_RATE_1MB: return B43legacy_CCK_RATE_1MB; case B43legacy_CCK_RATE_2MB: return B43legacy_CCK_RATE_1MB; case B43legacy_CCK_RATE_5MB: return B43legacy_CCK_RATE_2MB; case B43legacy_CCK_RATE_11MB: return B43legacy_CCK_RATE_5MB; case B43legacy_OFDM_RATE_6MB: return B43legacy_CCK_RATE_5MB; case B43legacy_OFDM_RATE_9MB: return B43legacy_OFDM_RATE_6MB; case B43legacy_OFDM_RATE_12MB: return B43legacy_OFDM_RATE_9MB; case B43legacy_OFDM_RATE_18MB: return B43legacy_OFDM_RATE_12MB; case B43legacy_OFDM_RATE_24MB: return B43legacy_OFDM_RATE_18MB; case B43legacy_OFDM_RATE_36MB: return B43legacy_OFDM_RATE_24MB; case B43legacy_OFDM_RATE_48MB: return B43legacy_OFDM_RATE_36MB; case B43legacy_OFDM_RATE_54MB: return B43legacy_OFDM_RATE_48MB; } B43legacy_BUG_ON(1); return 0; } static int generate_txhdr_fw3(struct b43legacy_wldev *dev, struct b43legacy_txhdr_fw3 *txhdr, const unsigned char *fragment_data, unsigned int fragment_len, struct ieee80211_tx_info *info, u16 cookie) { const struct ieee80211_hdr *wlhdr; int use_encryption = !!info->control.hw_key; u8 rate; struct ieee80211_rate *rate_fb; int rate_ofdm; int rate_fb_ofdm; unsigned int plcp_fragment_len; u32 mac_ctl = 0; u16 phy_ctl = 0; struct ieee80211_rate *tx_rate; struct ieee80211_tx_rate *rates; wlhdr = (const struct ieee80211_hdr *)fragment_data; memset(txhdr, 0, sizeof(*txhdr)); tx_rate = ieee80211_get_tx_rate(dev->wl->hw, info); rate = tx_rate->hw_value; rate_ofdm = b43legacy_is_ofdm_rate(rate); rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : tx_rate; rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value); txhdr->mac_frame_ctl = wlhdr->frame_control; memcpy(txhdr->tx_receiver, wlhdr->addr1, 6); /* Calculate duration for fallback rate */ if ((rate_fb->hw_value == rate) || (wlhdr->duration_id & cpu_to_le16(0x8000)) || (wlhdr->duration_id == cpu_to_le16(0))) { /* If the fallback rate equals the normal rate or the * dur_id field contains an AID, CFP magic or 0, * use the original dur_id field. */ txhdr->dur_fb = wlhdr->duration_id; } else { txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw, info->control.vif, info->band, fragment_len, rate_fb); } plcp_fragment_len = fragment_len + FCS_LEN; if (use_encryption) { u8 key_idx = info->control.hw_key->hw_key_idx; struct b43legacy_key *key; int wlhdr_len; size_t iv_len; B43legacy_WARN_ON(key_idx >= dev->max_nr_keys); key = &(dev->key[key_idx]); if (key->enabled) { /* Hardware appends ICV. */ plcp_fragment_len += info->control.hw_key->icv_len; key_idx = b43legacy_kidx_to_fw(dev, key_idx); mac_ctl |= (key_idx << B43legacy_TX4_MAC_KEYIDX_SHIFT) & B43legacy_TX4_MAC_KEYIDX; mac_ctl |= (key->algorithm << B43legacy_TX4_MAC_KEYALG_SHIFT) & B43legacy_TX4_MAC_KEYALG; wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control); iv_len = min((size_t)info->control.hw_key->iv_len, ARRAY_SIZE(txhdr->iv)); memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); } else { /* This key is invalid. This might only happen * in a short timeframe after machine resume before * we were able to reconfigure keys. * Drop this packet completely. Do not transmit it * unencrypted to avoid leaking information. */ return -ENOKEY; } } b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) (&txhdr->plcp), plcp_fragment_len, rate); b43legacy_generate_plcp_hdr(&txhdr->plcp_fb, plcp_fragment_len, rate_fb->hw_value); /* PHY TX Control word */ if (rate_ofdm) phy_ctl |= B43legacy_TX4_PHY_ENC_OFDM; if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL; phy_ctl |= B43legacy_TX4_PHY_ANTLAST; /* MAC control */ rates = info->control.rates; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) mac_ctl |= B43legacy_TX4_MAC_ACK; if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) mac_ctl |= B43legacy_TX4_MAC_HWSEQ; if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) mac_ctl |= B43legacy_TX4_MAC_STMSDU; if (rate_fb_ofdm) mac_ctl |= B43legacy_TX4_MAC_FALLBACKOFDM; /* Overwrite rates[0].count to make the retry calculation * in the tx status easier. need the actual retry limit to * detect whether the fallback rate was used. */ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) { rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count; mac_ctl |= B43legacy_TX4_MAC_LONGFRAME; } else { rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count; } /* Generate the RTS or CTS-to-self frame */ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) { unsigned int len; struct ieee80211_hdr *hdr; int rts_rate; int rts_rate_fb; int rts_rate_fb_ofdm; rts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info)->hw_value; rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate); rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb); if (rts_rate_fb_ofdm) mac_ctl |= B43legacy_TX4_MAC_CTSFALLBACKOFDM; if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { ieee80211_ctstoself_get(dev->wl->hw, info->control.vif, fragment_data, fragment_len, info, (struct ieee80211_cts *) (txhdr->rts_frame)); mac_ctl |= B43legacy_TX4_MAC_SENDCTS; len = sizeof(struct ieee80211_cts); } else { ieee80211_rts_get(dev->wl->hw, info->control.vif, fragment_data, fragment_len, info, (struct ieee80211_rts *) (txhdr->rts_frame)); mac_ctl |= B43legacy_TX4_MAC_SENDRTS; len = sizeof(struct ieee80211_rts); } len += FCS_LEN; b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) (&txhdr->rts_plcp), len, rts_rate); b43legacy_generate_plcp_hdr(&txhdr->rts_plcp_fb, len, rts_rate_fb); hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame); txhdr->rts_dur_fb = hdr->duration_id; } /* Magic cookie */ txhdr->cookie = cpu_to_le16(cookie); /* Apply the bitfields */ txhdr->mac_ctl = cpu_to_le32(mac_ctl); txhdr->phy_ctl = cpu_to_le16(phy_ctl); return 0; } int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, u8 *txhdr, const unsigned char *fragment_data, unsigned int fragment_len, struct ieee80211_tx_info *info, u16 cookie) { return generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr, fragment_data, fragment_len, info, cookie); } static s8 b43legacy_rssi_postprocess(struct b43legacy_wldev *dev, u8 in_rssi, int ofdm, int adjust_2053, int adjust_2050) { struct b43legacy_phy *phy = &dev->phy; s32 tmp; switch (phy->radio_ver) { case 0x2050: if (ofdm) { tmp = in_rssi; if (tmp > 127) tmp -= 256; tmp *= 73; tmp /= 64; if (adjust_2050) tmp += 25; else tmp -= 3; } else { if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { if (in_rssi > 63) in_rssi = 63; tmp = phy->nrssi_lt[in_rssi]; tmp = 31 - tmp; tmp *= -131; tmp /= 128; tmp -= 57; } else { tmp = in_rssi; tmp = 31 - tmp; tmp *= -149; tmp /= 128; tmp -= 68; } if (phy->type == B43legacy_PHYTYPE_G && adjust_2050) tmp += 25; } break; case 0x2060: if (in_rssi > 127) tmp = in_rssi - 256; else tmp = in_rssi; break; default: tmp = in_rssi; tmp -= 11; tmp *= 103; tmp /= 64; if (adjust_2053) tmp -= 109; else tmp -= 83; } return (s8)tmp; } void b43legacy_rx(struct b43legacy_wldev *dev, struct sk_buff *skb, const void *_rxhdr) { struct ieee80211_rx_status status; struct b43legacy_plcp_hdr6 *plcp; struct ieee80211_hdr *wlhdr; const struct b43legacy_rxhdr_fw3 *rxhdr = _rxhdr; __le16 fctl; u16 phystat0; u16 phystat3; u16 chanstat; u16 mactime; u32 macstat; u16 chanid; u8 jssi; int padding; memset(&status, 0, sizeof(status)); /* Get metadata about the frame from the header. */ phystat0 = le16_to_cpu(rxhdr->phy_status0); phystat3 = le16_to_cpu(rxhdr->phy_status3); jssi = rxhdr->jssi; macstat = le16_to_cpu(rxhdr->mac_status); mactime = le16_to_cpu(rxhdr->mac_time); chanstat = le16_to_cpu(rxhdr->channel); if (macstat & B43legacy_RX_MAC_FCSERR) dev->wl->ieee_stats.dot11FCSErrorCount++; /* Skip PLCP and padding */ padding = (macstat & B43legacy_RX_MAC_PADDING) ? 2 : 0; if (unlikely(skb->len < (sizeof(struct b43legacy_plcp_hdr6) + padding))) { b43legacydbg(dev->wl, "RX: Packet size underrun (1)\n"); goto drop; } plcp = (struct b43legacy_plcp_hdr6 *)(skb->data + padding); skb_pull(skb, sizeof(struct b43legacy_plcp_hdr6) + padding); /* The skb contains the Wireless Header + payload data now */ if (unlikely(skb->len < (2+2+6/*minimum hdr*/ + FCS_LEN))) { b43legacydbg(dev->wl, "RX: Packet size underrun (2)\n"); goto drop; } wlhdr = (struct ieee80211_hdr *)(skb->data); fctl = wlhdr->frame_control; if ((macstat & B43legacy_RX_MAC_DEC) && !(macstat & B43legacy_RX_MAC_DECERR)) { unsigned int keyidx; int wlhdr_len; int iv_len; int icv_len; keyidx = ((macstat & B43legacy_RX_MAC_KEYIDX) >> B43legacy_RX_MAC_KEYIDX_SHIFT); /* We must adjust the key index here. We want the "physical" * key index, but the ucode passed it slightly different. */ keyidx = b43legacy_kidx_to_raw(dev, keyidx); B43legacy_WARN_ON(keyidx >= dev->max_nr_keys); if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) { /* Remove PROTECTED flag to mark it as decrypted. */ B43legacy_WARN_ON(!ieee80211_has_protected(fctl)); fctl &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED); wlhdr->frame_control = fctl; wlhdr_len = ieee80211_hdrlen(fctl); if (unlikely(skb->len < (wlhdr_len + 3))) { b43legacydbg(dev->wl, "RX: Packet size" " underrun3\n"); goto drop; } if (skb->data[wlhdr_len + 3] & (1 << 5)) { /* The Ext-IV Bit is set in the "KeyID" * octet of the IV. */ iv_len = 8; icv_len = 8; } else { iv_len = 4; icv_len = 4; } if (unlikely(skb->len < (wlhdr_len + iv_len + icv_len))) { b43legacydbg(dev->wl, "RX: Packet size" " underrun4\n"); goto drop; } /* Remove the IV */ memmove(skb->data + iv_len, skb->data, wlhdr_len); skb_pull(skb, iv_len); /* Remove the ICV */ skb_trim(skb, skb->len - icv_len); status.flag |= RX_FLAG_DECRYPTED; } } status.signal = b43legacy_rssi_postprocess(dev, jssi, (phystat0 & B43legacy_RX_PHYST0_OFDM), (phystat0 & B43legacy_RX_PHYST0_GAINCTL), (phystat3 & B43legacy_RX_PHYST3_TRSTATE)); /* change to support A PHY */ if (phystat0 & B43legacy_RX_PHYST0_OFDM) status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false); else status.rate_idx = b43legacy_plcp_get_bitrate_idx_cck(plcp); status.antenna = !!(phystat0 & B43legacy_RX_PHYST0_ANT); /* * All frames on monitor interfaces and beacons always need a full * 64-bit timestamp. Monitor interfaces need it for diagnostic * purposes and beacons for IBSS merging. * This code assumes we get to process the packet within 16 bits * of timestamp, i.e. about 65 milliseconds after the PHY received * the first symbol. */ if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) { u16 low_mactime_now; b43legacy_tsf_read(dev, &status.mactime); low_mactime_now = status.mactime; status.mactime = status.mactime & ~0xFFFFULL; status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; status.flag |= RX_FLAG_MACTIME_MPDU; } chanid = (chanstat & B43legacy_RX_CHAN_ID) >> B43legacy_RX_CHAN_ID_SHIFT; switch (chanstat & B43legacy_RX_CHAN_PHYTYPE) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: status.band = IEEE80211_BAND_2GHZ; status.freq = chanid + 2400; break; default: b43legacywarn(dev->wl, "Unexpected value for chanstat (0x%X)\n", chanstat); } memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); ieee80211_rx_irqsafe(dev->wl->hw, skb); return; drop: b43legacydbg(dev->wl, "RX: Packet dropped\n"); dev_kfree_skb_any(skb); } void b43legacy_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status) { b43legacy_debugfs_log_txstat(dev, status); if (status->intermediate) return; if (status->for_ampdu) return; if (!status->acked) dev->wl->ieee_stats.dot11ACKFailureCount++; if (status->rts_count) { if (status->rts_count == 0xF) /* FIXME */ dev->wl->ieee_stats.dot11RTSFailureCount++; else dev->wl->ieee_stats.dot11RTSSuccessCount++; } if (b43legacy_using_pio(dev)) b43legacy_pio_handle_txstatus(dev, status); else b43legacy_dma_handle_txstatus(dev, status); } /* Handle TX status report as received through DMA/PIO queues */ void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev, const struct b43legacy_hwtxstatus *hw) { struct b43legacy_txstatus status; u8 tmp; status.cookie = le16_to_cpu(hw->cookie); status.seq = le16_to_cpu(hw->seq); status.phy_stat = hw->phy_stat; tmp = hw->count; status.frame_count = (tmp >> 4); status.rts_count = (tmp & 0x0F); tmp = hw->flags << 1; status.supp_reason = ((tmp & 0x1C) >> 2); status.pm_indicated = !!(tmp & 0x80); status.intermediate = !!(tmp & 0x40); status.for_ampdu = !!(tmp & 0x20); status.acked = !!(tmp & 0x02); b43legacy_handle_txstatus(dev, &status); } /* Stop any TX operation on the device (suspend the hardware queues) */ void b43legacy_tx_suspend(struct b43legacy_wldev *dev) { if (b43legacy_using_pio(dev)) b43legacy_pio_freeze_txqueues(dev); else b43legacy_dma_tx_suspend(dev); } /* Resume any TX operation on the device (resume the hardware queues) */ void b43legacy_tx_resume(struct b43legacy_wldev *dev) { if (b43legacy_using_pio(dev)) b43legacy_pio_thaw_txqueues(dev); else b43legacy_dma_tx_resume(dev); } /* Initialize the QoS parameters */ void b43legacy_qos_init(struct b43legacy_wldev *dev) { /* FIXME: This function must probably be called from the mac80211 * config callback. */ return; b43legacy_hf_write(dev, b43legacy_hf_read(dev) | B43legacy_HF_EDCF); /* FIXME kill magic */ b43legacy_write16(dev, 0x688, b43legacy_read16(dev, 0x688) | 0x4); /*TODO: We might need some stack support here to get the values. */ } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/sysfs.h0000644000175000017500000000035212026211315023556 0ustar mcgrofmcgrof#ifndef B43legacy_SYSFS_H_ #define B43legacy_SYSFS_H_ struct b43legacy_wldev; int b43legacy_sysfs_register(struct b43legacy_wldev *dev); void b43legacy_sysfs_unregister(struct b43legacy_wldev *dev); #endif /* B43legacy_SYSFS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/sysfs.c0000644000175000017500000001276012026211315023557 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver SYSFS support routines Copyright (c) 2006 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysfs.h" #include "b43legacy.h" #include "main.h" #include "phy.h" #include "radio.h" #include #define GENERIC_FILESIZE 64 static int get_integer(const char *buf, size_t count) { char tmp[10 + 1] = { 0 }; int ret = -EINVAL; if (count == 0) goto out; count = min(count, (size_t)10); memcpy(tmp, buf, count); ret = simple_strtol(tmp, NULL, 10); out: return ret; } static int get_boolean(const char *buf, size_t count) { if (count != 0) { if (buf[0] == '1') return 1; if (buf[0] == '0') return 0; if (count >= 4 && memcmp(buf, "true", 4) == 0) return 1; if (count >= 5 && memcmp(buf, "false", 5) == 0) return 0; if (count >= 3 && memcmp(buf, "yes", 3) == 0) return 1; if (count >= 2 && memcmp(buf, "no", 2) == 0) return 0; if (count >= 2 && memcmp(buf, "on", 2) == 0) return 1; if (count >= 3 && memcmp(buf, "off", 3) == 0) return 0; } return -EINVAL; } static ssize_t b43legacy_attr_interfmode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); ssize_t count = 0; if (!capable(CAP_NET_ADMIN)) return -EPERM; mutex_lock(&wldev->wl->mutex); switch (wldev->phy.interfmode) { case B43legacy_INTERFMODE_NONE: count = snprintf(buf, PAGE_SIZE, "0 (No Interference" " Mitigation)\n"); break; case B43legacy_INTERFMODE_NONWLAN: count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference" " Mitigation)\n"); break; case B43legacy_INTERFMODE_MANUALWLAN: count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference" " Mitigation)\n"); break; default: B43legacy_WARN_ON(1); } mutex_unlock(&wldev->wl->mutex); return count; } static ssize_t b43legacy_attr_interfmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); unsigned long flags; int err; int mode; if (!capable(CAP_NET_ADMIN)) return -EPERM; mode = get_integer(buf, count); switch (mode) { case 0: mode = B43legacy_INTERFMODE_NONE; break; case 1: mode = B43legacy_INTERFMODE_NONWLAN; break; case 2: mode = B43legacy_INTERFMODE_MANUALWLAN; break; case 3: mode = B43legacy_INTERFMODE_AUTOWLAN; break; default: return -EINVAL; } mutex_lock(&wldev->wl->mutex); spin_lock_irqsave(&wldev->wl->irq_lock, flags); err = b43legacy_radio_set_interference_mitigation(wldev, mode); if (err) b43legacyerr(wldev->wl, "Interference Mitigation not " "supported by device\n"); mmiowb(); spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); mutex_unlock(&wldev->wl->mutex); return err ? err : count; } static DEVICE_ATTR(interference, 0644, b43legacy_attr_interfmode_show, b43legacy_attr_interfmode_store); static ssize_t b43legacy_attr_preamble_show(struct device *dev, struct device_attribute *attr, char *buf) { struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); ssize_t count; if (!capable(CAP_NET_ADMIN)) return -EPERM; mutex_lock(&wldev->wl->mutex); if (wldev->short_preamble) count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble" " enabled)\n"); else count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble" " disabled)\n"); mutex_unlock(&wldev->wl->mutex); return count; } static ssize_t b43legacy_attr_preamble_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); unsigned long flags; int value; if (!capable(CAP_NET_ADMIN)) return -EPERM; value = get_boolean(buf, count); if (value < 0) return value; mutex_lock(&wldev->wl->mutex); spin_lock_irqsave(&wldev->wl->irq_lock, flags); wldev->short_preamble = !!value; spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); mutex_unlock(&wldev->wl->mutex); return count; } static DEVICE_ATTR(shortpreamble, 0644, b43legacy_attr_preamble_show, b43legacy_attr_preamble_store); int b43legacy_sysfs_register(struct b43legacy_wldev *wldev) { struct device *dev = wldev->dev->dev; int err; B43legacy_WARN_ON(b43legacy_status(wldev) != B43legacy_STAT_INITIALIZED); err = device_create_file(dev, &dev_attr_interference); if (err) goto out; err = device_create_file(dev, &dev_attr_shortpreamble); if (err) goto err_remove_interfmode; out: return err; err_remove_interfmode: device_remove_file(dev, &dev_attr_interference); goto out; } void b43legacy_sysfs_unregister(struct b43legacy_wldev *wldev) { struct device *dev = wldev->dev->dev; device_remove_file(dev, &dev_attr_shortpreamble); device_remove_file(dev, &dev_attr_interference); } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/rfkill.h0000644000175000017500000000040012026211315023664 0ustar mcgrofmcgrof#ifndef B43legacy_RFKILL_H_ #define B43legacy_RFKILL_H_ struct ieee80211_hw; struct b43legacy_wldev; void b43legacy_rfkill_poll(struct ieee80211_hw *hw); bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev); #endif /* B43legacy_RFKILL_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/rfkill.c0000644000175000017500000000514512026211315023672 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver RFKILL support Copyright (c) 2007 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "radio.h" #include "b43legacy.h" /* Returns TRUE, if the radio is enabled in hardware. */ bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev) { if (dev->dev->id.revision >= 3) { if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI) & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK)) return 1; } else { /* To prevent CPU fault on PPC, do not read a register * unless the interface is started; however, on resume * for hibernation, this routine is entered early. When * that happens, unconditionally return TRUE. */ if (b43legacy_status(dev) < B43legacy_STAT_STARTED) return 1; if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO) & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK) return 1; } return 0; } /* The poll callback for the hardware button. */ void b43legacy_rfkill_poll(struct ieee80211_hw *hw) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; struct ssb_bus *bus = dev->dev->bus; bool enabled; bool brought_up = false; mutex_lock(&wl->mutex); if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) { if (ssb_bus_powerup(bus, 0)) { mutex_unlock(&wl->mutex); return; } ssb_device_enable(dev->dev, 0); brought_up = true; } enabled = b43legacy_is_hw_radio_enabled(dev); if (unlikely(enabled != dev->radio_hw_enable)) { dev->radio_hw_enable = enabled; b43legacyinfo(wl, "Radio hardware status changed to %s\n", enabled ? "ENABLED" : "DISABLED"); wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); if (enabled != dev->phy.radio_on) { if (enabled) b43legacy_radio_turn_on(dev); else b43legacy_radio_turn_off(dev, 0); } } if (brought_up) { ssb_device_disable(dev->dev, 0); ssb_bus_may_powerdown(bus); } mutex_unlock(&wl->mutex); } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/radio.h0000644000175000017500000000720112026211315023505 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver Copyright (c) 2005 Martin Langer , Stefano Brivio Michael Buesch Danny van Dyk Andreas Jaggi Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef B43legacy_RADIO_H_ #define B43legacy_RADIO_H_ #include "b43legacy.h" #define B43legacy_RADIO_DEFAULT_CHANNEL_BG 6 /* Force antenna 0. */ #define B43legacy_RADIO_TXANTENNA_0 0 /* Force antenna 1. */ #define B43legacy_RADIO_TXANTENNA_1 1 /* Use the RX antenna, that was selected for the most recently * received good PLCP header. */ #define B43legacy_RADIO_TXANTENNA_LASTPLCP 3 #define B43legacy_RADIO_TXANTENNA_DEFAULT B43legacy_RADIO_TXANTENNA_LASTPLCP #define B43legacy_RADIO_INTERFMODE_NONE 0 #define B43legacy_RADIO_INTERFMODE_NONWLAN 1 #define B43legacy_RADIO_INTERFMODE_MANUALWLAN 2 #define B43legacy_RADIO_INTERFMODE_AUTOWLAN 3 void b43legacy_radio_lock(struct b43legacy_wldev *dev); void b43legacy_radio_unlock(struct b43legacy_wldev *dev); u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset); void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val); u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev); void b43legacy_radio_turn_on(struct b43legacy_wldev *dev); void b43legacy_radio_turn_off(struct b43legacy_wldev *dev, bool force); int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, u8 channel, int synthetic_pu_workaround); void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower); void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev, u16 baseband_attenuation, u16 attenuation, u16 txpower); u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev); u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev); u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev); void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val); void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev); u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel); u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev); int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev, int mode); void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev); void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev); s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset); void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val); void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val); void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev); void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev); u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev); #endif /* B43legacy_RADIO_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/radio.c0000644000175000017500000016560012026211315023510 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver Copyright (c) 2005 Martin Langer , Stefano Brivio Michael Buesch Danny van Dyk Andreas Jaggi Copyright (c) 2007 Larry Finger Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "b43legacy.h" #include "main.h" #include "phy.h" #include "radio.h" #include "ilt.h" /* Table for b43legacy_radio_calibrationvalue() */ static const u16 rcc_table[16] = { 0x0002, 0x0003, 0x0001, 0x000F, 0x0006, 0x0007, 0x0005, 0x000F, 0x000A, 0x000B, 0x0009, 0x000F, 0x000E, 0x000F, 0x000D, 0x000F, }; /* Reverse the bits of a 4bit value. * Example: 1101 is flipped 1011 */ static u16 flip_4bit(u16 value) { u16 flipped = 0x0000; B43legacy_BUG_ON(!((value & ~0x000F) == 0x0000)); flipped |= (value & 0x0001) << 3; flipped |= (value & 0x0002) << 1; flipped |= (value & 0x0004) >> 1; flipped |= (value & 0x0008) >> 3; return flipped; } /* Get the freq, as it has to be written to the device. */ static inline u16 channel2freq_bg(u8 channel) { /* Frequencies are given as frequencies_bg[index] + 2.4GHz * Starting with channel 1 */ static const u16 frequencies_bg[14] = { 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 84, }; if (unlikely(channel < 1 || channel > 14)) { printk(KERN_INFO "b43legacy: Channel %d is out of range\n", channel); dump_stack(); return 2412; } return frequencies_bg[channel - 1]; } void b43legacy_radio_lock(struct b43legacy_wldev *dev) { u32 status; status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); B43legacy_WARN_ON(status & B43legacy_MACCTL_RADIOLOCK); status |= B43legacy_MACCTL_RADIOLOCK; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); mmiowb(); udelay(10); } void b43legacy_radio_unlock(struct b43legacy_wldev *dev) { u32 status; b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); /* dummy read */ status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); B43legacy_WARN_ON(!(status & B43legacy_MACCTL_RADIOLOCK)); status &= ~B43legacy_MACCTL_RADIOLOCK; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); mmiowb(); } u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset) { struct b43legacy_phy *phy = &dev->phy; switch (phy->type) { case B43legacy_PHYTYPE_B: if (phy->radio_ver == 0x2053) { if (offset < 0x70) offset += 0x80; else if (offset < 0x80) offset += 0x70; } else if (phy->radio_ver == 0x2050) offset |= 0x80; else B43legacy_WARN_ON(1); break; case B43legacy_PHYTYPE_G: offset |= 0x80; break; default: B43legacy_BUG_ON(1); } b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); return b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW); } void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val) { b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_RADIO_DATA_LOW, val); } static void b43legacy_set_all_gains(struct b43legacy_wldev *dev, s16 first, s16 second, s16 third) { struct b43legacy_phy *phy = &dev->phy; u16 i; u16 start = 0x08; u16 end = 0x18; u16 offset = 0x0400; u16 tmp; if (phy->rev <= 1) { offset = 0x5000; start = 0x10; end = 0x20; } for (i = 0; i < 4; i++) b43legacy_ilt_write(dev, offset + i, first); for (i = start; i < end; i++) b43legacy_ilt_write(dev, offset + i, second); if (third != -1) { tmp = ((u16)third << 14) | ((u16)third << 6); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) | tmp); b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) | tmp); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) | tmp); } b43legacy_dummy_transmission(dev); } static void b43legacy_set_original_gains(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 i; u16 tmp; u16 offset = 0x0400; u16 start = 0x0008; u16 end = 0x0018; if (phy->rev <= 1) { offset = 0x5000; start = 0x0010; end = 0x0020; } for (i = 0; i < 4; i++) { tmp = (i & 0xFFFC); tmp |= (i & 0x0001) << 1; tmp |= (i & 0x0002) >> 1; b43legacy_ilt_write(dev, offset + i, tmp); } for (i = start; i < end; i++) b43legacy_ilt_write(dev, offset + i, i - start); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) | 0x4040); b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) | 0x4040); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) | 0x4000); b43legacy_dummy_transmission(dev); } /* Synthetic PU workaround */ static void b43legacy_synth_pu_workaround(struct b43legacy_wldev *dev, u8 channel) { struct b43legacy_phy *phy = &dev->phy; might_sleep(); if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) /* We do not need the workaround. */ return; if (channel <= 10) b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, channel2freq_bg(channel + 4)); else b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, channel2freq_bg(channel)); msleep(1); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, channel2freq_bg(channel)); } u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel) { struct b43legacy_phy *phy = &dev->phy; u8 ret = 0; u16 saved; u16 rssi; u16 temp; int i; int j = 0; saved = b43legacy_phy_read(dev, 0x0403); b43legacy_radio_selectchannel(dev, channel, 0); b43legacy_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); if (phy->aci_hw_rssi) rssi = b43legacy_phy_read(dev, 0x048A) & 0x3F; else rssi = saved & 0x3F; /* clamp temp to signed 5bit */ if (rssi > 32) rssi -= 64; for (i = 0; i < 100; i++) { temp = (b43legacy_phy_read(dev, 0x047F) >> 8) & 0x3F; if (temp > 32) temp -= 64; if (temp < rssi) j++; if (j >= 20) ret = 1; } b43legacy_phy_write(dev, 0x0403, saved); return ret; } u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u8 ret[13]; unsigned int channel = phy->channel; unsigned int i; unsigned int j; unsigned int start; unsigned int end; if (!((phy->type == B43legacy_PHYTYPE_G) && (phy->rev > 0))) return 0; b43legacy_phy_lock(dev); b43legacy_radio_lock(dev); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) & 0xFFFC); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) & 0x7FFF); b43legacy_set_all_gains(dev, 3, 8, 1); start = (channel - 5 > 0) ? channel - 5 : 1; end = (channel + 5 < 14) ? channel + 5 : 13; for (i = start; i <= end; i++) { if (abs(channel - i) > 2) ret[i-1] = b43legacy_radio_aci_detect(dev, i); } b43legacy_radio_selectchannel(dev, channel, 0); b43legacy_phy_write(dev, 0x0802, (b43legacy_phy_read(dev, 0x0802) & 0xFFFC) | 0x0003); b43legacy_phy_write(dev, 0x0403, b43legacy_phy_read(dev, 0x0403) & 0xFFF8); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) | 0x8000); b43legacy_set_original_gains(dev); for (i = 0; i < 13; i++) { if (!ret[i]) continue; end = (i + 5 < 13) ? i + 5 : 13; for (j = i; j < end; j++) ret[j] = 1; } b43legacy_radio_unlock(dev); b43legacy_phy_unlock(dev); return ret[channel - 1]; } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val) { b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); mmiowb(); b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_DATA, (u16)val); } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset) { u16 val; b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); val = b43legacy_phy_read(dev, B43legacy_PHY_NRSSILT_DATA); return (s16)val; } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val) { u16 i; s16 tmp; for (i = 0; i < 64; i++) { tmp = b43legacy_nrssi_hw_read(dev, i); tmp -= val; tmp = clamp_val(tmp, -32, 31); b43legacy_nrssi_hw_write(dev, i, tmp); } } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; s16 i; s16 delta; s32 tmp; delta = 0x1F - phy->nrssi[0]; for (i = 0; i < 64; i++) { tmp = (i - delta) * phy->nrssislope; tmp /= 0x10000; tmp += 0x3A; tmp = clamp_val(tmp, 0, 0x3F); phy->nrssi_lt[i] = tmp; } } static void b43legacy_calc_nrssi_offset(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 backup[20] = { 0 }; s16 v47F; u16 i; u16 saved = 0xFFFF; backup[0] = b43legacy_phy_read(dev, 0x0001); backup[1] = b43legacy_phy_read(dev, 0x0811); backup[2] = b43legacy_phy_read(dev, 0x0812); backup[3] = b43legacy_phy_read(dev, 0x0814); backup[4] = b43legacy_phy_read(dev, 0x0815); backup[5] = b43legacy_phy_read(dev, 0x005A); backup[6] = b43legacy_phy_read(dev, 0x0059); backup[7] = b43legacy_phy_read(dev, 0x0058); backup[8] = b43legacy_phy_read(dev, 0x000A); backup[9] = b43legacy_phy_read(dev, 0x0003); backup[10] = b43legacy_radio_read16(dev, 0x007A); backup[11] = b43legacy_radio_read16(dev, 0x0043); b43legacy_phy_write(dev, 0x0429, b43legacy_phy_read(dev, 0x0429) & 0x7FFF); b43legacy_phy_write(dev, 0x0001, (b43legacy_phy_read(dev, 0x0001) & 0x3FFF) | 0x4000); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x000C); b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) & 0xFFF3) | 0x0004); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) & ~(0x1 | 0x2)); if (phy->rev >= 6) { backup[12] = b43legacy_phy_read(dev, 0x002E); backup[13] = b43legacy_phy_read(dev, 0x002F); backup[14] = b43legacy_phy_read(dev, 0x080F); backup[15] = b43legacy_phy_read(dev, 0x0810); backup[16] = b43legacy_phy_read(dev, 0x0801); backup[17] = b43legacy_phy_read(dev, 0x0060); backup[18] = b43legacy_phy_read(dev, 0x0014); backup[19] = b43legacy_phy_read(dev, 0x0478); b43legacy_phy_write(dev, 0x002E, 0); b43legacy_phy_write(dev, 0x002F, 0); b43legacy_phy_write(dev, 0x080F, 0); b43legacy_phy_write(dev, 0x0810, 0); b43legacy_phy_write(dev, 0x0478, b43legacy_phy_read(dev, 0x0478) | 0x0100); b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) | 0x0040); b43legacy_phy_write(dev, 0x0060, b43legacy_phy_read(dev, 0x0060) | 0x0040); b43legacy_phy_write(dev, 0x0014, b43legacy_phy_read(dev, 0x0014) | 0x0200); } b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0070); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0080); udelay(30); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == 31) { for (i = 7; i >= 4; i--) { b43legacy_radio_write16(dev, 0x007B, i); udelay(20); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F < 31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 4; } else { b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) & 0x007F); b43legacy_phy_write(dev, 0x0814, b43legacy_phy_read(dev, 0x0814) | 0x0001); b43legacy_phy_write(dev, 0x0815, b43legacy_phy_read(dev, 0x0815) & 0xFFFE); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x000C); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | 0x000C); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x0030); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | 0x0030); b43legacy_phy_write(dev, 0x005A, 0x0480); b43legacy_phy_write(dev, 0x0059, 0x0810); b43legacy_phy_write(dev, 0x0058, 0x000D); if (phy->analog == 0) b43legacy_phy_write(dev, 0x0003, 0x0122); else b43legacy_phy_write(dev, 0x000A, b43legacy_phy_read(dev, 0x000A) | 0x2000); b43legacy_phy_write(dev, 0x0814, b43legacy_phy_read(dev, 0x0814) | 0x0004); b43legacy_phy_write(dev, 0x0815, b43legacy_phy_read(dev, 0x0815) & 0xFFFB); b43legacy_phy_write(dev, 0x0003, (b43legacy_phy_read(dev, 0x0003) & 0xFF9F) | 0x0040); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x000F); b43legacy_set_all_gains(dev, 3, 0, 1); b43legacy_radio_write16(dev, 0x0043, (b43legacy_radio_read16(dev, 0x0043) & 0x00F0) | 0x000F); udelay(30); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == -32) { for (i = 0; i < 4; i++) { b43legacy_radio_write16(dev, 0x007B, i); udelay(20); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F > -31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 3; } else saved = 0; } b43legacy_radio_write16(dev, 0x007B, saved); if (phy->rev >= 6) { b43legacy_phy_write(dev, 0x002E, backup[12]); b43legacy_phy_write(dev, 0x002F, backup[13]); b43legacy_phy_write(dev, 0x080F, backup[14]); b43legacy_phy_write(dev, 0x0810, backup[15]); } b43legacy_phy_write(dev, 0x0814, backup[3]); b43legacy_phy_write(dev, 0x0815, backup[4]); b43legacy_phy_write(dev, 0x005A, backup[5]); b43legacy_phy_write(dev, 0x0059, backup[6]); b43legacy_phy_write(dev, 0x0058, backup[7]); b43legacy_phy_write(dev, 0x000A, backup[8]); b43legacy_phy_write(dev, 0x0003, backup[9]); b43legacy_radio_write16(dev, 0x0043, backup[11]); b43legacy_radio_write16(dev, 0x007A, backup[10]); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) | 0x1 | 0x2); b43legacy_phy_write(dev, 0x0429, b43legacy_phy_read(dev, 0x0429) | 0x8000); b43legacy_set_original_gains(dev); if (phy->rev >= 6) { b43legacy_phy_write(dev, 0x0801, backup[16]); b43legacy_phy_write(dev, 0x0060, backup[17]); b43legacy_phy_write(dev, 0x0014, backup[18]); b43legacy_phy_write(dev, 0x0478, backup[19]); } b43legacy_phy_write(dev, 0x0001, backup[0]); b43legacy_phy_write(dev, 0x0812, backup[2]); b43legacy_phy_write(dev, 0x0811, backup[1]); } void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 backup[18] = { 0 }; u16 tmp; s16 nrssi0; s16 nrssi1; switch (phy->type) { case B43legacy_PHYTYPE_B: backup[0] = b43legacy_radio_read16(dev, 0x007A); backup[1] = b43legacy_radio_read16(dev, 0x0052); backup[2] = b43legacy_radio_read16(dev, 0x0043); backup[3] = b43legacy_phy_read(dev, 0x0030); backup[4] = b43legacy_phy_read(dev, 0x0026); backup[5] = b43legacy_phy_read(dev, 0x0015); backup[6] = b43legacy_phy_read(dev, 0x002A); backup[7] = b43legacy_phy_read(dev, 0x0020); backup[8] = b43legacy_phy_read(dev, 0x005A); backup[9] = b43legacy_phy_read(dev, 0x0059); backup[10] = b43legacy_phy_read(dev, 0x0058); backup[11] = b43legacy_read16(dev, 0x03E2); backup[12] = b43legacy_read16(dev, 0x03E6); backup[13] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); tmp = b43legacy_radio_read16(dev, 0x007A); tmp &= (phy->rev >= 5) ? 0x007F : 0x000F; b43legacy_radio_write16(dev, 0x007A, tmp); b43legacy_phy_write(dev, 0x0030, 0x00FF); b43legacy_write16(dev, 0x03EC, 0x7F7F); b43legacy_phy_write(dev, 0x0026, 0x0000); b43legacy_phy_write(dev, 0x0015, b43legacy_phy_read(dev, 0x0015) | 0x0020); b43legacy_phy_write(dev, 0x002A, 0x08A3); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0080); nrssi0 = (s16)b43legacy_phy_read(dev, 0x0027); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) & 0x007F); if (phy->analog >= 2) b43legacy_write16(dev, 0x03E6, 0x0040); else if (phy->analog == 0) b43legacy_write16(dev, 0x03E6, 0x0122); else b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT) & 0x2000); b43legacy_phy_write(dev, 0x0020, 0x3F3F); b43legacy_phy_write(dev, 0x0015, 0xF330); b43legacy_radio_write16(dev, 0x005A, 0x0060); b43legacy_radio_write16(dev, 0x0043, b43legacy_radio_read16(dev, 0x0043) & 0x00F0); b43legacy_phy_write(dev, 0x005A, 0x0480); b43legacy_phy_write(dev, 0x0059, 0x0810); b43legacy_phy_write(dev, 0x0058, 0x000D); udelay(20); nrssi1 = (s16)b43legacy_phy_read(dev, 0x0027); b43legacy_phy_write(dev, 0x0030, backup[3]); b43legacy_radio_write16(dev, 0x007A, backup[0]); b43legacy_write16(dev, 0x03E2, backup[11]); b43legacy_phy_write(dev, 0x0026, backup[4]); b43legacy_phy_write(dev, 0x0015, backup[5]); b43legacy_phy_write(dev, 0x002A, backup[6]); b43legacy_synth_pu_workaround(dev, phy->channel); if (phy->analog != 0) b43legacy_write16(dev, 0x03F4, backup[13]); b43legacy_phy_write(dev, 0x0020, backup[7]); b43legacy_phy_write(dev, 0x005A, backup[8]); b43legacy_phy_write(dev, 0x0059, backup[9]); b43legacy_phy_write(dev, 0x0058, backup[10]); b43legacy_radio_write16(dev, 0x0052, backup[1]); b43legacy_radio_write16(dev, 0x0043, backup[2]); if (nrssi0 == nrssi1) phy->nrssislope = 0x00010000; else phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); if (nrssi0 <= -4) { phy->nrssi[0] = nrssi0; phy->nrssi[1] = nrssi1; } break; case B43legacy_PHYTYPE_G: if (phy->radio_rev >= 9) return; if (phy->radio_rev == 8) b43legacy_calc_nrssi_offset(dev); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) & 0x7FFF); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) & 0xFFFC); backup[7] = b43legacy_read16(dev, 0x03E2); b43legacy_write16(dev, 0x03E2, b43legacy_read16(dev, 0x03E2) | 0x8000); backup[0] = b43legacy_radio_read16(dev, 0x007A); backup[1] = b43legacy_radio_read16(dev, 0x0052); backup[2] = b43legacy_radio_read16(dev, 0x0043); backup[3] = b43legacy_phy_read(dev, 0x0015); backup[4] = b43legacy_phy_read(dev, 0x005A); backup[5] = b43legacy_phy_read(dev, 0x0059); backup[6] = b43legacy_phy_read(dev, 0x0058); backup[8] = b43legacy_read16(dev, 0x03E6); backup[9] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); if (phy->rev >= 3) { backup[10] = b43legacy_phy_read(dev, 0x002E); backup[11] = b43legacy_phy_read(dev, 0x002F); backup[12] = b43legacy_phy_read(dev, 0x080F); backup[13] = b43legacy_phy_read(dev, B43legacy_PHY_G_LO_CONTROL); backup[14] = b43legacy_phy_read(dev, 0x0801); backup[15] = b43legacy_phy_read(dev, 0x0060); backup[16] = b43legacy_phy_read(dev, 0x0014); backup[17] = b43legacy_phy_read(dev, 0x0478); b43legacy_phy_write(dev, 0x002E, 0); b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, 0); switch (phy->rev) { case 4: case 6: case 7: b43legacy_phy_write(dev, 0x0478, b43legacy_phy_read(dev, 0x0478) | 0x0100); b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) | 0x0040); break; case 3: case 5: b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) & 0xFFBF); break; } b43legacy_phy_write(dev, 0x0060, b43legacy_phy_read(dev, 0x0060) | 0x0040); b43legacy_phy_write(dev, 0x0014, b43legacy_phy_read(dev, 0x0014) | 0x0200); } b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0070); b43legacy_set_all_gains(dev, 0, 8, 0); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) & 0x00F7); if (phy->rev >= 2) { b43legacy_phy_write(dev, 0x0811, (b43legacy_phy_read(dev, 0x0811) & 0xFFCF) | 0x0030); b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) & 0xFFCF) | 0x0010); } b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0080); udelay(20); nrssi0 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (nrssi0 >= 0x0020) nrssi0 -= 0x0040; b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) & 0x007F); if (phy->analog >= 2) b43legacy_phy_write(dev, 0x0003, (b43legacy_phy_read(dev, 0x0003) & 0xFF9F) | 0x0040); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT) | 0x2000); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x000F); b43legacy_phy_write(dev, 0x0015, 0xF330); if (phy->rev >= 2) { b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) & 0xFFCF) | 0x0020); b43legacy_phy_write(dev, 0x0811, (b43legacy_phy_read(dev, 0x0811) & 0xFFCF) | 0x0020); } b43legacy_set_all_gains(dev, 3, 0, 1); if (phy->radio_rev == 8) b43legacy_radio_write16(dev, 0x0043, 0x001F); else { tmp = b43legacy_radio_read16(dev, 0x0052) & 0xFF0F; b43legacy_radio_write16(dev, 0x0052, tmp | 0x0060); tmp = b43legacy_radio_read16(dev, 0x0043) & 0xFFF0; b43legacy_radio_write16(dev, 0x0043, tmp | 0x0009); } b43legacy_phy_write(dev, 0x005A, 0x0480); b43legacy_phy_write(dev, 0x0059, 0x0810); b43legacy_phy_write(dev, 0x0058, 0x000D); udelay(20); nrssi1 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (nrssi1 >= 0x0020) nrssi1 -= 0x0040; if (nrssi0 == nrssi1) phy->nrssislope = 0x00010000; else phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); if (nrssi0 >= -4) { phy->nrssi[0] = nrssi1; phy->nrssi[1] = nrssi0; } if (phy->rev >= 3) { b43legacy_phy_write(dev, 0x002E, backup[10]); b43legacy_phy_write(dev, 0x002F, backup[11]); b43legacy_phy_write(dev, 0x080F, backup[12]); b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, backup[13]); } if (phy->rev >= 2) { b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) & 0xFFCF); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) & 0xFFCF); } b43legacy_radio_write16(dev, 0x007A, backup[0]); b43legacy_radio_write16(dev, 0x0052, backup[1]); b43legacy_radio_write16(dev, 0x0043, backup[2]); b43legacy_write16(dev, 0x03E2, backup[7]); b43legacy_write16(dev, 0x03E6, backup[8]); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[9]); b43legacy_phy_write(dev, 0x0015, backup[3]); b43legacy_phy_write(dev, 0x005A, backup[4]); b43legacy_phy_write(dev, 0x0059, backup[5]); b43legacy_phy_write(dev, 0x0058, backup[6]); b43legacy_synth_pu_workaround(dev, phy->channel); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) | 0x0003); b43legacy_set_original_gains(dev); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) | 0x8000); if (phy->rev >= 3) { b43legacy_phy_write(dev, 0x0801, backup[14]); b43legacy_phy_write(dev, 0x0060, backup[15]); b43legacy_phy_write(dev, 0x0014, backup[16]); b43legacy_phy_write(dev, 0x0478, backup[17]); } b43legacy_nrssi_mem_update(dev); b43legacy_calc_nrssi_threshold(dev); break; default: B43legacy_BUG_ON(1); } } void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; s32 threshold; s32 a; s32 b; s16 tmp16; u16 tmp_u16; switch (phy->type) { case B43legacy_PHYTYPE_B: { if (phy->radio_ver != 0x2050) return; if (!(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI)) return; if (phy->radio_rev >= 6) { threshold = (phy->nrssi[1] - phy->nrssi[0]) * 32; threshold += 20 * (phy->nrssi[0] + 1); threshold /= 40; } else threshold = phy->nrssi[1] - 5; threshold = clamp_val(threshold, 0, 0x3E); b43legacy_phy_read(dev, 0x0020); /* dummy read */ b43legacy_phy_write(dev, 0x0020, (((u16)threshold) << 8) | 0x001C); if (phy->radio_rev >= 6) { b43legacy_phy_write(dev, 0x0087, 0x0E0D); b43legacy_phy_write(dev, 0x0086, 0x0C0B); b43legacy_phy_write(dev, 0x0085, 0x0A09); b43legacy_phy_write(dev, 0x0084, 0x0808); b43legacy_phy_write(dev, 0x0083, 0x0808); b43legacy_phy_write(dev, 0x0082, 0x0604); b43legacy_phy_write(dev, 0x0081, 0x0302); b43legacy_phy_write(dev, 0x0080, 0x0100); } break; } case B43legacy_PHYTYPE_G: if (!phy->gmode || !(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI)) { tmp16 = b43legacy_nrssi_hw_read(dev, 0x20); if (tmp16 >= 0x20) tmp16 -= 0x40; if (tmp16 < 3) b43legacy_phy_write(dev, 0x048A, (b43legacy_phy_read(dev, 0x048A) & 0xF000) | 0x09EB); else b43legacy_phy_write(dev, 0x048A, (b43legacy_phy_read(dev, 0x048A) & 0xF000) | 0x0AED); } else { if (phy->interfmode == B43legacy_RADIO_INTERFMODE_NONWLAN) { a = 0xE; b = 0xA; } else if (!phy->aci_wlan_automatic && phy->aci_enable) { a = 0x13; b = 0x12; } else { a = 0xE; b = 0x11; } a = a * (phy->nrssi[1] - phy->nrssi[0]); a += (phy->nrssi[0] << 6); if (a < 32) a += 31; else a += 32; a = a >> 6; a = clamp_val(a, -31, 31); b = b * (phy->nrssi[1] - phy->nrssi[0]); b += (phy->nrssi[0] << 6); if (b < 32) b += 31; else b += 32; b = b >> 6; b = clamp_val(b, -31, 31); tmp_u16 = b43legacy_phy_read(dev, 0x048A) & 0xF000; tmp_u16 |= ((u32)b & 0x0000003F); tmp_u16 |= (((u32)a & 0x0000003F) << 6); b43legacy_phy_write(dev, 0x048A, tmp_u16); } break; default: B43legacy_BUG_ON(1); } } /* Stack implementation to save/restore values from the * interference mitigation code. * It is save to restore values in random order. */ static void _stack_save(u32 *_stackptr, size_t *stackidx, u8 id, u16 offset, u16 value) { u32 *stackptr = &(_stackptr[*stackidx]); B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000)); B43legacy_WARN_ON(!((id & 0xF8) == 0x00)); *stackptr = offset; *stackptr |= ((u32)id) << 13; *stackptr |= ((u32)value) << 16; (*stackidx)++; B43legacy_WARN_ON(!(*stackidx < B43legacy_INTERFSTACK_SIZE)); } static u16 _stack_restore(u32 *stackptr, u8 id, u16 offset) { size_t i; B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000)); B43legacy_WARN_ON(!((id & 0xF8) == 0x00)); for (i = 0; i < B43legacy_INTERFSTACK_SIZE; i++, stackptr++) { if ((*stackptr & 0x00001FFF) != offset) continue; if (((*stackptr & 0x00007000) >> 13) != id) continue; return ((*stackptr & 0xFFFF0000) >> 16); } B43legacy_BUG_ON(1); return 0; } #define phy_stacksave(offset) \ do { \ _stack_save(stack, &stackidx, 0x1, (offset), \ b43legacy_phy_read(dev, (offset))); \ } while (0) #define phy_stackrestore(offset) \ do { \ b43legacy_phy_write(dev, (offset), \ _stack_restore(stack, 0x1, \ (offset))); \ } while (0) #define radio_stacksave(offset) \ do { \ _stack_save(stack, &stackidx, 0x2, (offset), \ b43legacy_radio_read16(dev, (offset))); \ } while (0) #define radio_stackrestore(offset) \ do { \ b43legacy_radio_write16(dev, (offset), \ _stack_restore(stack, 0x2, \ (offset))); \ } while (0) #define ilt_stacksave(offset) \ do { \ _stack_save(stack, &stackidx, 0x3, (offset), \ b43legacy_ilt_read(dev, (offset))); \ } while (0) #define ilt_stackrestore(offset) \ do { \ b43legacy_ilt_write(dev, (offset), \ _stack_restore(stack, 0x3, \ (offset))); \ } while (0) static void b43legacy_radio_interference_mitigation_enable(struct b43legacy_wldev *dev, int mode) { struct b43legacy_phy *phy = &dev->phy; u16 tmp; u16 flipped; u32 tmp32; size_t stackidx = 0; u32 *stack = phy->interfstack; switch (mode) { case B43legacy_RADIO_INTERFMODE_NONWLAN: if (phy->rev != 1) { b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) | 0x0800); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) & ~0x4000); break; } radio_stacksave(0x0078); tmp = (b43legacy_radio_read16(dev, 0x0078) & 0x001E); flipped = flip_4bit(tmp); if (flipped < 10 && flipped >= 8) flipped = 7; else if (flipped >= 10) flipped -= 3; flipped = flip_4bit(flipped); flipped = (flipped << 1) | 0x0020; b43legacy_radio_write16(dev, 0x0078, flipped); b43legacy_calc_nrssi_threshold(dev); phy_stacksave(0x0406); b43legacy_phy_write(dev, 0x0406, 0x7E28); b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) | 0x0800); b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, b43legacy_phy_read(dev, B43legacy_PHY_RADIO_BITFIELD) | 0x1000); phy_stacksave(0x04A0); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xC0C0) | 0x0008); phy_stacksave(0x04A1); b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xC0C0) | 0x0605); phy_stacksave(0x04A2); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xC0C0) | 0x0204); phy_stacksave(0x04A8); b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) & 0xC0C0) | 0x0803); phy_stacksave(0x04AB); b43legacy_phy_write(dev, 0x04AB, (b43legacy_phy_read(dev, 0x04AB) & 0xC0C0) | 0x0605); phy_stacksave(0x04A7); b43legacy_phy_write(dev, 0x04A7, 0x0002); phy_stacksave(0x04A3); b43legacy_phy_write(dev, 0x04A3, 0x287A); phy_stacksave(0x04A9); b43legacy_phy_write(dev, 0x04A9, 0x2027); phy_stacksave(0x0493); b43legacy_phy_write(dev, 0x0493, 0x32F5); phy_stacksave(0x04AA); b43legacy_phy_write(dev, 0x04AA, 0x2027); phy_stacksave(0x04AC); b43legacy_phy_write(dev, 0x04AC, 0x32F5); break; case B43legacy_RADIO_INTERFMODE_MANUALWLAN: if (b43legacy_phy_read(dev, 0x0033) & 0x0800) break; phy->aci_enable = true; phy_stacksave(B43legacy_PHY_RADIO_BITFIELD); phy_stacksave(B43legacy_PHY_G_CRS); if (phy->rev < 2) phy_stacksave(0x0406); else { phy_stacksave(0x04C0); phy_stacksave(0x04C1); } phy_stacksave(0x0033); phy_stacksave(0x04A7); phy_stacksave(0x04A3); phy_stacksave(0x04A9); phy_stacksave(0x04AA); phy_stacksave(0x04AC); phy_stacksave(0x0493); phy_stacksave(0x04A1); phy_stacksave(0x04A0); phy_stacksave(0x04A2); phy_stacksave(0x048A); phy_stacksave(0x04A8); phy_stacksave(0x04AB); if (phy->rev == 2) { phy_stacksave(0x04AD); phy_stacksave(0x04AE); } else if (phy->rev >= 3) { phy_stacksave(0x04AD); phy_stacksave(0x0415); phy_stacksave(0x0416); phy_stacksave(0x0417); ilt_stacksave(0x1A00 + 0x2); ilt_stacksave(0x1A00 + 0x3); } phy_stacksave(0x042B); phy_stacksave(0x048C); b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, b43legacy_phy_read(dev, B43legacy_PHY_RADIO_BITFIELD) & ~0x1000); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, (b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) & 0xFFFC) | 0x0002); b43legacy_phy_write(dev, 0x0033, 0x0800); b43legacy_phy_write(dev, 0x04A3, 0x2027); b43legacy_phy_write(dev, 0x04A9, 0x1CA8); b43legacy_phy_write(dev, 0x0493, 0x287A); b43legacy_phy_write(dev, 0x04AA, 0x1CA8); b43legacy_phy_write(dev, 0x04AC, 0x287A); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xFFC0) | 0x001A); b43legacy_phy_write(dev, 0x04A7, 0x000D); if (phy->rev < 2) b43legacy_phy_write(dev, 0x0406, 0xFF0D); else if (phy->rev == 2) { b43legacy_phy_write(dev, 0x04C0, 0xFFFF); b43legacy_phy_write(dev, 0x04C1, 0x00A9); } else { b43legacy_phy_write(dev, 0x04C0, 0x00C1); b43legacy_phy_write(dev, 0x04C1, 0x0059); } b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xC0FF) | 0x1800); b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xFFC0) | 0x0015); b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) & 0xCFFF) | 0x1000); b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) & 0xF0FF) | 0x0A00); b43legacy_phy_write(dev, 0x04AB, (b43legacy_phy_read(dev, 0x04AB) & 0xCFFF) | 0x1000); b43legacy_phy_write(dev, 0x04AB, (b43legacy_phy_read(dev, 0x04AB) & 0xF0FF) | 0x0800); b43legacy_phy_write(dev, 0x04AB, (b43legacy_phy_read(dev, 0x04AB) & 0xFFCF) | 0x0010); b43legacy_phy_write(dev, 0x04AB, (b43legacy_phy_read(dev, 0x04AB) & 0xFFF0) | 0x0005); b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) & 0xFFCF) | 0x0010); b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) & 0xFFF0) | 0x0006); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xF0FF) | 0x0800); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xF0FF) | 0x0500); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xFFF0) | 0x000B); if (phy->rev >= 3) { b43legacy_phy_write(dev, 0x048A, b43legacy_phy_read(dev, 0x048A) & ~0x8000); b43legacy_phy_write(dev, 0x0415, (b43legacy_phy_read(dev, 0x0415) & 0x8000) | 0x36D8); b43legacy_phy_write(dev, 0x0416, (b43legacy_phy_read(dev, 0x0416) & 0x8000) | 0x36D8); b43legacy_phy_write(dev, 0x0417, (b43legacy_phy_read(dev, 0x0417) & 0xFE00) | 0x016D); } else { b43legacy_phy_write(dev, 0x048A, b43legacy_phy_read(dev, 0x048A) | 0x1000); b43legacy_phy_write(dev, 0x048A, (b43legacy_phy_read(dev, 0x048A) & 0x9FFF) | 0x2000); tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET); if (!(tmp32 & 0x800)) { tmp32 |= 0x800; b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET, tmp32); } } if (phy->rev >= 2) b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) | 0x0800); b43legacy_phy_write(dev, 0x048C, (b43legacy_phy_read(dev, 0x048C) & 0xF0FF) | 0x0200); if (phy->rev == 2) { b43legacy_phy_write(dev, 0x04AE, (b43legacy_phy_read(dev, 0x04AE) & 0xFF00) | 0x007F); b43legacy_phy_write(dev, 0x04AD, (b43legacy_phy_read(dev, 0x04AD) & 0x00FF) | 0x1300); } else if (phy->rev >= 6) { b43legacy_ilt_write(dev, 0x1A00 + 0x3, 0x007F); b43legacy_ilt_write(dev, 0x1A00 + 0x2, 0x007F); b43legacy_phy_write(dev, 0x04AD, b43legacy_phy_read(dev, 0x04AD) & 0x00FF); } b43legacy_calc_nrssi_slope(dev); break; default: B43legacy_BUG_ON(1); } } static void b43legacy_radio_interference_mitigation_disable(struct b43legacy_wldev *dev, int mode) { struct b43legacy_phy *phy = &dev->phy; u32 tmp32; u32 *stack = phy->interfstack; switch (mode) { case B43legacy_RADIO_INTERFMODE_NONWLAN: if (phy->rev != 1) { b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) & ~0x0800); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) | 0x4000); break; } phy_stackrestore(0x0078); b43legacy_calc_nrssi_threshold(dev); phy_stackrestore(0x0406); b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) & ~0x0800); if (!dev->bad_frames_preempt) b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, b43legacy_phy_read(dev, B43legacy_PHY_RADIO_BITFIELD) & ~(1 << 11)); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) | 0x4000); phy_stackrestore(0x04A0); phy_stackrestore(0x04A1); phy_stackrestore(0x04A2); phy_stackrestore(0x04A8); phy_stackrestore(0x04AB); phy_stackrestore(0x04A7); phy_stackrestore(0x04A3); phy_stackrestore(0x04A9); phy_stackrestore(0x0493); phy_stackrestore(0x04AA); phy_stackrestore(0x04AC); break; case B43legacy_RADIO_INTERFMODE_MANUALWLAN: if (!(b43legacy_phy_read(dev, 0x0033) & 0x0800)) break; phy->aci_enable = false; phy_stackrestore(B43legacy_PHY_RADIO_BITFIELD); phy_stackrestore(B43legacy_PHY_G_CRS); phy_stackrestore(0x0033); phy_stackrestore(0x04A3); phy_stackrestore(0x04A9); phy_stackrestore(0x0493); phy_stackrestore(0x04AA); phy_stackrestore(0x04AC); phy_stackrestore(0x04A0); phy_stackrestore(0x04A7); if (phy->rev >= 2) { phy_stackrestore(0x04C0); phy_stackrestore(0x04C1); } else phy_stackrestore(0x0406); phy_stackrestore(0x04A1); phy_stackrestore(0x04AB); phy_stackrestore(0x04A8); if (phy->rev == 2) { phy_stackrestore(0x04AD); phy_stackrestore(0x04AE); } else if (phy->rev >= 3) { phy_stackrestore(0x04AD); phy_stackrestore(0x0415); phy_stackrestore(0x0416); phy_stackrestore(0x0417); ilt_stackrestore(0x1A00 + 0x2); ilt_stackrestore(0x1A00 + 0x3); } phy_stackrestore(0x04A2); phy_stackrestore(0x04A8); phy_stackrestore(0x042B); phy_stackrestore(0x048C); tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET); if (tmp32 & 0x800) { tmp32 &= ~0x800; b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET, tmp32); } b43legacy_calc_nrssi_slope(dev); break; default: B43legacy_BUG_ON(1); } } #undef phy_stacksave #undef phy_stackrestore #undef radio_stacksave #undef radio_stackrestore #undef ilt_stacksave #undef ilt_stackrestore int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev, int mode) { struct b43legacy_phy *phy = &dev->phy; int currentmode; if ((phy->type != B43legacy_PHYTYPE_G) || (phy->rev == 0) || (!phy->gmode)) return -ENODEV; phy->aci_wlan_automatic = false; switch (mode) { case B43legacy_RADIO_INTERFMODE_AUTOWLAN: phy->aci_wlan_automatic = true; if (phy->aci_enable) mode = B43legacy_RADIO_INTERFMODE_MANUALWLAN; else mode = B43legacy_RADIO_INTERFMODE_NONE; break; case B43legacy_RADIO_INTERFMODE_NONE: case B43legacy_RADIO_INTERFMODE_NONWLAN: case B43legacy_RADIO_INTERFMODE_MANUALWLAN: break; default: return -EINVAL; } currentmode = phy->interfmode; if (currentmode == mode) return 0; if (currentmode != B43legacy_RADIO_INTERFMODE_NONE) b43legacy_radio_interference_mitigation_disable(dev, currentmode); if (mode == B43legacy_RADIO_INTERFMODE_NONE) { phy->aci_enable = false; phy->aci_hw_rssi = false; } else b43legacy_radio_interference_mitigation_enable(dev, mode); phy->interfmode = mode; return 0; } u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev) { u16 reg; u16 index; u16 ret; reg = b43legacy_radio_read16(dev, 0x0060); index = (reg & 0x001E) >> 1; ret = rcc_table[index] << 1; ret |= (reg & 0x0001); ret |= 0x0020; return ret; } #define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) static u16 b43legacy_get_812_value(struct b43legacy_wldev *dev, u8 lpd) { struct b43legacy_phy *phy = &dev->phy; u16 loop_or = 0; u16 adj_loopback_gain = phy->loopback_gain[0]; u8 loop; u16 extern_lna_control; if (!phy->gmode) return 0; if (!has_loopback_gain(phy)) { if (phy->rev < 7 || !(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_EXTLNA)) { switch (lpd) { case LPD(0, 1, 1): return 0x0FB2; case LPD(0, 0, 1): return 0x00B2; case LPD(1, 0, 1): return 0x30B2; case LPD(1, 0, 0): return 0x30B3; default: B43legacy_BUG_ON(1); } } else { switch (lpd) { case LPD(0, 1, 1): return 0x8FB2; case LPD(0, 0, 1): return 0x80B2; case LPD(1, 0, 1): return 0x20B2; case LPD(1, 0, 0): return 0x20B3; default: B43legacy_BUG_ON(1); } } } else { if (phy->radio_rev == 8) adj_loopback_gain += 0x003E; else adj_loopback_gain += 0x0026; if (adj_loopback_gain >= 0x46) { adj_loopback_gain -= 0x46; extern_lna_control = 0x3000; } else if (adj_loopback_gain >= 0x3A) { adj_loopback_gain -= 0x3A; extern_lna_control = 0x2000; } else if (adj_loopback_gain >= 0x2E) { adj_loopback_gain -= 0x2E; extern_lna_control = 0x1000; } else { adj_loopback_gain -= 0x10; extern_lna_control = 0x0000; } for (loop = 0; loop < 16; loop++) { u16 tmp = adj_loopback_gain - 6 * loop; if (tmp < 6) break; } loop_or = (loop << 8) | extern_lna_control; if (phy->rev >= 7 && dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_EXTLNA) { if (extern_lna_control) loop_or |= 0x8000; switch (lpd) { case LPD(0, 1, 1): return 0x8F92; case LPD(0, 0, 1): return (0x8092 | loop_or); case LPD(1, 0, 1): return (0x2092 | loop_or); case LPD(1, 0, 0): return (0x2093 | loop_or); default: B43legacy_BUG_ON(1); } } else { switch (lpd) { case LPD(0, 1, 1): return 0x0F92; case LPD(0, 0, 1): case LPD(1, 0, 1): return (0x0092 | loop_or); case LPD(1, 0, 0): return (0x0093 | loop_or); default: B43legacy_BUG_ON(1); } } } return 0; } u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 backup[21] = { 0 }; u16 ret; u16 i; u16 j; u32 tmp1 = 0; u32 tmp2 = 0; backup[0] = b43legacy_radio_read16(dev, 0x0043); backup[14] = b43legacy_radio_read16(dev, 0x0051); backup[15] = b43legacy_radio_read16(dev, 0x0052); backup[1] = b43legacy_phy_read(dev, 0x0015); backup[16] = b43legacy_phy_read(dev, 0x005A); backup[17] = b43legacy_phy_read(dev, 0x0059); backup[18] = b43legacy_phy_read(dev, 0x0058); if (phy->type == B43legacy_PHYTYPE_B) { backup[2] = b43legacy_phy_read(dev, 0x0030); backup[3] = b43legacy_read16(dev, 0x03EC); b43legacy_phy_write(dev, 0x0030, 0x00FF); b43legacy_write16(dev, 0x03EC, 0x3F3F); } else { if (phy->gmode) { backup[4] = b43legacy_phy_read(dev, 0x0811); backup[5] = b43legacy_phy_read(dev, 0x0812); backup[6] = b43legacy_phy_read(dev, 0x0814); backup[7] = b43legacy_phy_read(dev, 0x0815); backup[8] = b43legacy_phy_read(dev, B43legacy_PHY_G_CRS); backup[9] = b43legacy_phy_read(dev, 0x0802); b43legacy_phy_write(dev, 0x0814, (b43legacy_phy_read(dev, 0x0814) | 0x0003)); b43legacy_phy_write(dev, 0x0815, (b43legacy_phy_read(dev, 0x0815) & 0xFFFC)); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, (b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) & 0x7FFF)); b43legacy_phy_write(dev, 0x0802, (b43legacy_phy_read(dev, 0x0802) & 0xFFFC)); if (phy->rev > 1) { /* loopback gain enabled */ backup[19] = b43legacy_phy_read(dev, 0x080F); backup[20] = b43legacy_phy_read(dev, 0x0810); if (phy->rev >= 3) b43legacy_phy_write(dev, 0x080F, 0xC020); else b43legacy_phy_write(dev, 0x080F, 0x8020); b43legacy_phy_write(dev, 0x0810, 0x0000); } b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(0, 1, 1))); if (phy->rev < 7 || !(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_EXTLNA)) b43legacy_phy_write(dev, 0x0811, 0x01B3); else b43legacy_phy_write(dev, 0x0811, 0x09B3); } } b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, (b43legacy_read16(dev, B43legacy_MMIO_PHY_RADIO) | 0x8000)); backup[10] = b43legacy_phy_read(dev, 0x0035); b43legacy_phy_write(dev, 0x0035, (b43legacy_phy_read(dev, 0x0035) & 0xFF7F)); backup[11] = b43legacy_read16(dev, 0x03E6); backup[12] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); /* Initialization */ if (phy->analog == 0) b43legacy_write16(dev, 0x03E6, 0x0122); else { if (phy->analog >= 2) b43legacy_phy_write(dev, 0x0003, (b43legacy_phy_read(dev, 0x0003) & 0xFFBF) | 0x0040); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, (b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT) | 0x2000)); } ret = b43legacy_radio_calibrationvalue(dev); if (phy->type == B43legacy_PHYTYPE_B) b43legacy_radio_write16(dev, 0x0078, 0x0026); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(0, 1, 1))); b43legacy_phy_write(dev, 0x0015, 0xBFAF); b43legacy_phy_write(dev, 0x002B, 0x1403); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(0, 0, 1))); b43legacy_phy_write(dev, 0x0015, 0xBFA0); b43legacy_radio_write16(dev, 0x0051, (b43legacy_radio_read16(dev, 0x0051) | 0x0004)); if (phy->radio_rev == 8) b43legacy_radio_write16(dev, 0x0043, 0x001F); else { b43legacy_radio_write16(dev, 0x0052, 0x0000); b43legacy_radio_write16(dev, 0x0043, (b43legacy_radio_read16(dev, 0x0043) & 0xFFF0) | 0x0009); } b43legacy_phy_write(dev, 0x0058, 0x0000); for (i = 0; i < 16; i++) { b43legacy_phy_write(dev, 0x005A, 0x0480); b43legacy_phy_write(dev, 0x0059, 0xC810); b43legacy_phy_write(dev, 0x0058, 0x000D); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 1))); b43legacy_phy_write(dev, 0x0015, 0xAFB0); udelay(10); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 1))); b43legacy_phy_write(dev, 0x0015, 0xEFB0); udelay(10); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 0))); b43legacy_phy_write(dev, 0x0015, 0xFFF0); udelay(20); tmp1 += b43legacy_phy_read(dev, 0x002D); b43legacy_phy_write(dev, 0x0058, 0x0000); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 1))); b43legacy_phy_write(dev, 0x0015, 0xAFB0); } tmp1++; tmp1 >>= 9; udelay(10); b43legacy_phy_write(dev, 0x0058, 0x0000); for (i = 0; i < 16; i++) { b43legacy_radio_write16(dev, 0x0078, (flip_4bit(i) << 1) | 0x0020); backup[13] = b43legacy_radio_read16(dev, 0x0078); udelay(10); for (j = 0; j < 16; j++) { b43legacy_phy_write(dev, 0x005A, 0x0D80); b43legacy_phy_write(dev, 0x0059, 0xC810); b43legacy_phy_write(dev, 0x0058, 0x000D); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 1))); b43legacy_phy_write(dev, 0x0015, 0xAFB0); udelay(10); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 1))); b43legacy_phy_write(dev, 0x0015, 0xEFB0); udelay(10); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 0))); b43legacy_phy_write(dev, 0x0015, 0xFFF0); udelay(10); tmp2 += b43legacy_phy_read(dev, 0x002D); b43legacy_phy_write(dev, 0x0058, 0x0000); if (phy->gmode) b43legacy_phy_write(dev, 0x0812, b43legacy_get_812_value(dev, LPD(1, 0, 1))); b43legacy_phy_write(dev, 0x0015, 0xAFB0); } tmp2++; tmp2 >>= 8; if (tmp1 < tmp2) break; } /* Restore the registers */ b43legacy_phy_write(dev, 0x0015, backup[1]); b43legacy_radio_write16(dev, 0x0051, backup[14]); b43legacy_radio_write16(dev, 0x0052, backup[15]); b43legacy_radio_write16(dev, 0x0043, backup[0]); b43legacy_phy_write(dev, 0x005A, backup[16]); b43legacy_phy_write(dev, 0x0059, backup[17]); b43legacy_phy_write(dev, 0x0058, backup[18]); b43legacy_write16(dev, 0x03E6, backup[11]); if (phy->analog != 0) b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[12]); b43legacy_phy_write(dev, 0x0035, backup[10]); b43legacy_radio_selectchannel(dev, phy->channel, 1); if (phy->type == B43legacy_PHYTYPE_B) { b43legacy_phy_write(dev, 0x0030, backup[2]); b43legacy_write16(dev, 0x03EC, backup[3]); } else { if (phy->gmode) { b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, (b43legacy_read16(dev, B43legacy_MMIO_PHY_RADIO) & 0x7FFF)); b43legacy_phy_write(dev, 0x0811, backup[4]); b43legacy_phy_write(dev, 0x0812, backup[5]); b43legacy_phy_write(dev, 0x0814, backup[6]); b43legacy_phy_write(dev, 0x0815, backup[7]); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, backup[8]); b43legacy_phy_write(dev, 0x0802, backup[9]); if (phy->rev > 1) { b43legacy_phy_write(dev, 0x080F, backup[19]); b43legacy_phy_write(dev, 0x0810, backup[20]); } } } if (i >= 15) ret = backup[13]; return ret; } static inline u16 freq_r3A_value(u16 frequency) { u16 value; if (frequency < 5091) value = 0x0040; else if (frequency < 5321) value = 0x0000; else if (frequency < 5806) value = 0x0080; else value = 0x0040; return value; } void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev) { static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; u16 tmp = b43legacy_radio_read16(dev, 0x001E); int i; int j; for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) { if (tmp == (data_high[i] | data_low[j])) { b43legacy_phy_write(dev, 0x0069, (i - j) << 8 | 0x00C0); return; } } } } int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, u8 channel, int synthetic_pu_workaround) { struct b43legacy_phy *phy = &dev->phy; if (channel == 0xFF) { switch (phy->type) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: channel = B43legacy_RADIO_DEFAULT_CHANNEL_BG; break; default: B43legacy_WARN_ON(1); } } /* TODO: Check if channel is valid - return -EINVAL if not */ if (synthetic_pu_workaround) b43legacy_synth_pu_workaround(dev, channel); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, channel2freq_bg(channel)); if (channel == 14) { if (dev->dev->bus->sprom.country_code == 5) /* JAPAN) */ b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET, b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET) & ~(1 << 7)); else b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET, b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET) | (1 << 7)); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT) | (1 << 11)); } else b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT) & 0xF7BF); phy->channel = channel; /*XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states * that 2000 usecs might suffice. */ msleep(8); return 0; } void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val) { u16 tmp; val <<= 8; tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0022) & 0xFCFF; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0022, tmp | val); tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x03A8) & 0xFCFF; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x03A8, tmp | val); tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0054) & 0xFCFF; b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0054, tmp | val); } /* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */ static u16 b43legacy_get_txgain_base_band(u16 txpower) { u16 ret; B43legacy_WARN_ON(txpower > 63); if (txpower >= 54) ret = 2; else if (txpower >= 49) ret = 4; else if (txpower >= 44) ret = 5; else ret = 6; return ret; } /* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */ static u16 b43legacy_get_txgain_freq_power_amp(u16 txpower) { u16 ret; B43legacy_WARN_ON(txpower > 63); if (txpower >= 32) ret = 0; else if (txpower >= 25) ret = 1; else if (txpower >= 20) ret = 2; else if (txpower >= 12) ret = 3; else ret = 4; return ret; } /* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */ static u16 b43legacy_get_txgain_dac(u16 txpower) { u16 ret; B43legacy_WARN_ON(txpower > 63); if (txpower >= 54) ret = txpower - 53; else if (txpower >= 49) ret = txpower - 42; else if (txpower >= 44) ret = txpower - 37; else if (txpower >= 32) ret = txpower - 32; else if (txpower >= 25) ret = txpower - 20; else if (txpower >= 20) ret = txpower - 13; else if (txpower >= 12) ret = txpower - 8; else ret = txpower; return ret; } void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower) { struct b43legacy_phy *phy = &dev->phy; u16 pamp; u16 base; u16 dac; u16 ilt; txpower = clamp_val(txpower, 0, 63); pamp = b43legacy_get_txgain_freq_power_amp(txpower); pamp <<= 5; pamp &= 0x00E0; b43legacy_phy_write(dev, 0x0019, pamp); base = b43legacy_get_txgain_base_band(txpower); base &= 0x000F; b43legacy_phy_write(dev, 0x0017, base | 0x0020); ilt = b43legacy_ilt_read(dev, 0x3001); ilt &= 0x0007; dac = b43legacy_get_txgain_dac(txpower); dac <<= 3; dac |= ilt; b43legacy_ilt_write(dev, 0x3001, dac); phy->txpwr_offset = txpower; /* TODO: FuncPlaceholder (Adjust BB loft cancel) */ } void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev, u16 baseband_attenuation, u16 radio_attenuation, u16 txpower) { struct b43legacy_phy *phy = &dev->phy; if (baseband_attenuation == 0xFFFF) baseband_attenuation = phy->bbatt; if (radio_attenuation == 0xFFFF) radio_attenuation = phy->rfatt; if (txpower == 0xFFFF) txpower = phy->txctl1; phy->bbatt = baseband_attenuation; phy->rfatt = radio_attenuation; phy->txctl1 = txpower; B43legacy_WARN_ON(baseband_attenuation > 11); if (phy->radio_rev < 6) B43legacy_WARN_ON(radio_attenuation > 9); else B43legacy_WARN_ON(radio_attenuation > 31); B43legacy_WARN_ON(txpower > 7); b43legacy_phy_set_baseband_attenuation(dev, baseband_attenuation); b43legacy_radio_write16(dev, 0x0043, radio_attenuation); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0064, radio_attenuation); if (phy->radio_ver == 0x2050) b43legacy_radio_write16(dev, 0x0052, (b43legacy_radio_read16(dev, 0x0052) & ~0x0070) | ((txpower << 4) & 0x0070)); /* FIXME: The spec is very weird and unclear here. */ if (phy->type == B43legacy_PHYTYPE_G) b43legacy_phy_lo_adjust(dev, 0); } u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; if (phy->radio_ver == 0x2050 && phy->radio_rev < 6) return 0; return 2; } u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 att = 0xFFFF; switch (phy->radio_ver) { case 0x2053: switch (phy->radio_rev) { case 1: att = 6; break; } break; case 0x2050: switch (phy->radio_rev) { case 0: att = 5; break; case 1: if (phy->type == B43legacy_PHYTYPE_G) { if (is_bcm_board_vendor(dev) && dev->dev->bus->boardinfo.type == 0x421 && dev->dev->bus->sprom.board_rev >= 30) att = 3; else if (is_bcm_board_vendor(dev) && dev->dev->bus->boardinfo.type == 0x416) att = 3; else att = 1; } else { if (is_bcm_board_vendor(dev) && dev->dev->bus->boardinfo.type == 0x421 && dev->dev->bus->sprom.board_rev >= 30) att = 7; else att = 6; } break; case 2: if (phy->type == B43legacy_PHYTYPE_G) { if (is_bcm_board_vendor(dev) && dev->dev->bus->boardinfo.type == 0x421 && dev->dev->bus->sprom.board_rev >= 30) att = 3; else if (is_bcm_board_vendor(dev) && dev->dev->bus->boardinfo.type == 0x416) att = 5; else if (dev->dev->bus->chip_id == 0x4320) att = 4; else att = 3; } else att = 6; break; case 3: att = 5; break; case 4: case 5: att = 1; break; case 6: case 7: att = 5; break; case 8: att = 0x1A; break; case 9: default: att = 5; } } if (is_bcm_board_vendor(dev) && dev->dev->bus->boardinfo.type == 0x421) { if (dev->dev->bus->sprom.board_rev < 0x43) att = 2; else if (dev->dev->bus->sprom.board_rev < 0x51) att = 3; } if (att == 0xFFFF) att = 5; return att; } u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; if (phy->radio_ver != 0x2050) return 0; if (phy->radio_rev == 1) return 3; if (phy->radio_rev < 6) return 2; if (phy->radio_rev == 8) return 1; return 0; } void b43legacy_radio_turn_on(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; int err; u8 channel; might_sleep(); if (phy->radio_on) return; switch (phy->type) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: b43legacy_phy_write(dev, 0x0015, 0x8000); b43legacy_phy_write(dev, 0x0015, 0xCC00); b43legacy_phy_write(dev, 0x0015, (phy->gmode ? 0x00C0 : 0x0000)); if (phy->radio_off_context.valid) { /* Restore the RFover values. */ b43legacy_phy_write(dev, B43legacy_PHY_RFOVER, phy->radio_off_context.rfover); b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL, phy->radio_off_context.rfoverval); phy->radio_off_context.valid = false; } channel = phy->channel; err = b43legacy_radio_selectchannel(dev, B43legacy_RADIO_DEFAULT_CHANNEL_BG, 1); err |= b43legacy_radio_selectchannel(dev, channel, 0); B43legacy_WARN_ON(err); break; default: B43legacy_BUG_ON(1); } phy->radio_on = true; } void b43legacy_radio_turn_off(struct b43legacy_wldev *dev, bool force) { struct b43legacy_phy *phy = &dev->phy; if (!phy->radio_on && !force) return; if (phy->type == B43legacy_PHYTYPE_G && dev->dev->id.revision >= 5) { u16 rfover, rfoverval; rfover = b43legacy_phy_read(dev, B43legacy_PHY_RFOVER); rfoverval = b43legacy_phy_read(dev, B43legacy_PHY_RFOVERVAL); if (!force) { phy->radio_off_context.rfover = rfover; phy->radio_off_context.rfoverval = rfoverval; phy->radio_off_context.valid = true; } b43legacy_phy_write(dev, B43legacy_PHY_RFOVER, rfover | 0x008C); b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL, rfoverval & 0xFF73); } else b43legacy_phy_write(dev, 0x0015, 0xAA00); phy->radio_on = false; b43legacydbg(dev->wl, "Radio initialized\n"); } void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; switch (phy->type) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0058, 0x7F7F); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x005a, 0x7F7F); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0070, 0x7F7F); b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0072, 0x7F7F); break; } } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/pio.h0000644000175000017500000000760712026211315023210 0ustar mcgrofmcgrof#ifndef B43legacy_PIO_H_ #define B43legacy_PIO_H_ #include "b43legacy.h" #include #include #include #define B43legacy_PIO_TXCTL 0x00 #define B43legacy_PIO_TXDATA 0x02 #define B43legacy_PIO_TXQBUFSIZE 0x04 #define B43legacy_PIO_RXCTL 0x08 #define B43legacy_PIO_RXDATA 0x0A #define B43legacy_PIO_TXCTL_WRITELO (1 << 0) #define B43legacy_PIO_TXCTL_WRITEHI (1 << 1) #define B43legacy_PIO_TXCTL_COMPLETE (1 << 2) #define B43legacy_PIO_TXCTL_INIT (1 << 3) #define B43legacy_PIO_TXCTL_SUSPEND (1 << 7) #define B43legacy_PIO_RXCTL_DATAAVAILABLE (1 << 0) #define B43legacy_PIO_RXCTL_READY (1 << 1) /* PIO constants */ #define B43legacy_PIO_MAXTXDEVQPACKETS 31 #define B43legacy_PIO_TXQADJUST 80 /* PIO tuning knobs */ #define B43legacy_PIO_MAXTXPACKETS 256 #ifdef CONFIG_B43LEGACY_PIO struct b43legacy_pioqueue; struct b43legacy_xmitstatus; struct b43legacy_pio_txpacket { struct b43legacy_pioqueue *queue; struct sk_buff *skb; struct list_head list; }; #define pio_txpacket_getindex(packet) ((int)((packet) - \ (packet)->queue->tx_packets_cache)) struct b43legacy_pioqueue { struct b43legacy_wldev *dev; u16 mmio_base; bool tx_suspended; bool tx_frozen; bool need_workarounds; /* Workarounds needed for core.rev < 3 */ /* Adjusted size of the device internal TX buffer. */ u16 tx_devq_size; /* Used octets of the device internal TX buffer. */ u16 tx_devq_used; /* Used packet slots in the device internal TX buffer. */ u8 tx_devq_packets; /* Packets from the txfree list can * be taken on incoming TX requests. */ struct list_head txfree; unsigned int nr_txfree; /* Packets on the txqueue are queued, * but not completely written to the chip, yet. */ struct list_head txqueue; /* Packets on the txrunning queue are completely * posted to the device. We are waiting for the txstatus. */ struct list_head txrunning; struct tasklet_struct txtask; struct b43legacy_pio_txpacket tx_packets_cache[B43legacy_PIO_MAXTXPACKETS]; }; static inline u16 b43legacy_pio_read(struct b43legacy_pioqueue *queue, u16 offset) { return b43legacy_read16(queue->dev, queue->mmio_base + offset); } static inline void b43legacy_pio_write(struct b43legacy_pioqueue *queue, u16 offset, u16 value) { b43legacy_write16(queue->dev, queue->mmio_base + offset, value); mmiowb(); } int b43legacy_pio_init(struct b43legacy_wldev *dev); void b43legacy_pio_free(struct b43legacy_wldev *dev); int b43legacy_pio_tx(struct b43legacy_wldev *dev, struct sk_buff *skb); void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status); void b43legacy_pio_rx(struct b43legacy_pioqueue *queue); /* Suspend TX queue in hardware. */ void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue); void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue); /* Suspend (freeze) the TX tasklet (software level). */ void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev); void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev); #else /* CONFIG_B43LEGACY_PIO */ static inline int b43legacy_pio_init(struct b43legacy_wldev *dev) { return 0; } static inline void b43legacy_pio_free(struct b43legacy_wldev *dev) { } static inline int b43legacy_pio_tx(struct b43legacy_wldev *dev, struct sk_buff *skb) { return 0; } static inline void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status) { } static inline void b43legacy_pio_rx(struct b43legacy_pioqueue *queue) { } static inline void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue) { } static inline void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue) { } static inline void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev) { } static inline void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev) { } #endif /* CONFIG_B43LEGACY_PIO */ #endif /* B43legacy_PIO_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/pio.c0000644000175000017500000004306412026211315023200 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver PIO Transmission Copyright (c) 2005 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43legacy.h" #include "pio.h" #include "main.h" #include "xmit.h" #include #include static void tx_start(struct b43legacy_pioqueue *queue) { b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, B43legacy_PIO_TXCTL_INIT); } static void tx_octet(struct b43legacy_pioqueue *queue, u8 octet) { if (queue->need_workarounds) { b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet); b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, B43legacy_PIO_TXCTL_WRITELO); } else { b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, B43legacy_PIO_TXCTL_WRITELO); b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet); } } static u16 tx_get_next_word(const u8 *txhdr, const u8 *packet, size_t txhdr_size, unsigned int *pos) { const u8 *source; unsigned int i = *pos; u16 ret; if (i < txhdr_size) source = txhdr; else { source = packet; i -= txhdr_size; } ret = le16_to_cpu(*((__le16 *)(source + i))); *pos += 2; return ret; } static void tx_data(struct b43legacy_pioqueue *queue, u8 *txhdr, const u8 *packet, unsigned int octets) { u16 data; unsigned int i = 0; if (queue->need_workarounds) { data = tx_get_next_word(txhdr, packet, sizeof(struct b43legacy_txhdr_fw3), &i); b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data); } b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, B43legacy_PIO_TXCTL_WRITELO | B43legacy_PIO_TXCTL_WRITEHI); while (i < octets - 1) { data = tx_get_next_word(txhdr, packet, sizeof(struct b43legacy_txhdr_fw3), &i); b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data); } if (octets % 2) tx_octet(queue, packet[octets - sizeof(struct b43legacy_txhdr_fw3) - 1]); } static void tx_complete(struct b43legacy_pioqueue *queue, struct sk_buff *skb) { if (queue->need_workarounds) { b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, skb->data[skb->len - 1]); b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, B43legacy_PIO_TXCTL_WRITELO | B43legacy_PIO_TXCTL_COMPLETE); } else b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, B43legacy_PIO_TXCTL_COMPLETE); } static u16 generate_cookie(struct b43legacy_pioqueue *queue, struct b43legacy_pio_txpacket *packet) { u16 cookie = 0x0000; int packetindex; /* We use the upper 4 bits for the PIO * controller ID and the lower 12 bits * for the packet index (in the cache). */ switch (queue->mmio_base) { case B43legacy_MMIO_PIO1_BASE: break; case B43legacy_MMIO_PIO2_BASE: cookie = 0x1000; break; case B43legacy_MMIO_PIO3_BASE: cookie = 0x2000; break; case B43legacy_MMIO_PIO4_BASE: cookie = 0x3000; break; default: B43legacy_WARN_ON(1); } packetindex = pio_txpacket_getindex(packet); B43legacy_WARN_ON(!(((u16)packetindex & 0xF000) == 0x0000)); cookie |= (u16)packetindex; return cookie; } static struct b43legacy_pioqueue *parse_cookie(struct b43legacy_wldev *dev, u16 cookie, struct b43legacy_pio_txpacket **packet) { struct b43legacy_pio *pio = &dev->pio; struct b43legacy_pioqueue *queue = NULL; int packetindex; switch (cookie & 0xF000) { case 0x0000: queue = pio->queue0; break; case 0x1000: queue = pio->queue1; break; case 0x2000: queue = pio->queue2; break; case 0x3000: queue = pio->queue3; break; default: B43legacy_WARN_ON(1); } packetindex = (cookie & 0x0FFF); B43legacy_WARN_ON(!(packetindex >= 0 && packetindex < B43legacy_PIO_MAXTXPACKETS)); *packet = &(queue->tx_packets_cache[packetindex]); return queue; } union txhdr_union { struct b43legacy_txhdr_fw3 txhdr_fw3; }; static int pio_tx_write_fragment(struct b43legacy_pioqueue *queue, struct sk_buff *skb, struct b43legacy_pio_txpacket *packet, size_t txhdr_size) { union txhdr_union txhdr_data; u8 *txhdr = NULL; unsigned int octets; int err; txhdr = (u8 *)(&txhdr_data.txhdr_fw3); B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); err = b43legacy_generate_txhdr(queue->dev, txhdr, skb->data, skb->len, IEEE80211_SKB_CB(skb), generate_cookie(queue, packet)); if (err) return err; tx_start(queue); octets = skb->len + txhdr_size; if (queue->need_workarounds) octets--; tx_data(queue, txhdr, (u8 *)skb->data, octets); tx_complete(queue, skb); return 0; } static void free_txpacket(struct b43legacy_pio_txpacket *packet, int irq_context) { struct b43legacy_pioqueue *queue = packet->queue; if (packet->skb) { if (irq_context) dev_kfree_skb_irq(packet->skb); else dev_kfree_skb(packet->skb); } list_move(&packet->list, &queue->txfree); queue->nr_txfree++; } static int pio_tx_packet(struct b43legacy_pio_txpacket *packet) { struct b43legacy_pioqueue *queue = packet->queue; struct sk_buff *skb = packet->skb; u16 octets; int err; octets = (u16)skb->len + sizeof(struct b43legacy_txhdr_fw3); if (queue->tx_devq_size < octets) { b43legacywarn(queue->dev->wl, "PIO queue too small. " "Dropping packet.\n"); /* Drop it silently (return success) */ free_txpacket(packet, 1); return 0; } B43legacy_WARN_ON(queue->tx_devq_packets > B43legacy_PIO_MAXTXDEVQPACKETS); B43legacy_WARN_ON(queue->tx_devq_used > queue->tx_devq_size); /* Check if there is sufficient free space on the device * TX queue. If not, return and let the TX tasklet * retry later. */ if (queue->tx_devq_packets == B43legacy_PIO_MAXTXDEVQPACKETS) return -EBUSY; if (queue->tx_devq_used + octets > queue->tx_devq_size) return -EBUSY; /* Now poke the device. */ err = pio_tx_write_fragment(queue, skb, packet, sizeof(struct b43legacy_txhdr_fw3)); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ free_txpacket(packet, 1); return 0; } /* Account for the packet size. * (We must not overflow the device TX queue) */ queue->tx_devq_packets++; queue->tx_devq_used += octets; /* Transmission started, everything ok, move the * packet to the txrunning list. */ list_move_tail(&packet->list, &queue->txrunning); return 0; } static void tx_tasklet(unsigned long d) { struct b43legacy_pioqueue *queue = (struct b43legacy_pioqueue *)d; struct b43legacy_wldev *dev = queue->dev; unsigned long flags; struct b43legacy_pio_txpacket *packet, *tmp_packet; int err; u16 txctl; spin_lock_irqsave(&dev->wl->irq_lock, flags); if (queue->tx_frozen) goto out_unlock; txctl = b43legacy_pio_read(queue, B43legacy_PIO_TXCTL); if (txctl & B43legacy_PIO_TXCTL_SUSPEND) goto out_unlock; list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) { /* Try to transmit the packet. This can fail, if * the device queue is full. In case of failure, the * packet is left in the txqueue. * If transmission succeed, the packet is moved to txrunning. * If it is impossible to transmit the packet, it * is dropped. */ err = pio_tx_packet(packet); if (err) break; } out_unlock: spin_unlock_irqrestore(&dev->wl->irq_lock, flags); } static void setup_txqueues(struct b43legacy_pioqueue *queue) { struct b43legacy_pio_txpacket *packet; int i; queue->nr_txfree = B43legacy_PIO_MAXTXPACKETS; for (i = 0; i < B43legacy_PIO_MAXTXPACKETS; i++) { packet = &(queue->tx_packets_cache[i]); packet->queue = queue; INIT_LIST_HEAD(&packet->list); list_add(&packet->list, &queue->txfree); } } static struct b43legacy_pioqueue *b43legacy_setup_pioqueue(struct b43legacy_wldev *dev, u16 pio_mmio_base) { struct b43legacy_pioqueue *queue; u32 value; u16 qsize; queue = kzalloc(sizeof(*queue), GFP_KERNEL); if (!queue) goto out; queue->dev = dev; queue->mmio_base = pio_mmio_base; queue->need_workarounds = (dev->dev->id.revision < 3); INIT_LIST_HEAD(&queue->txfree); INIT_LIST_HEAD(&queue->txqueue); INIT_LIST_HEAD(&queue->txrunning); tasklet_init(&queue->txtask, tx_tasklet, (unsigned long)queue); value = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); value &= ~B43legacy_MACCTL_BE; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value); qsize = b43legacy_read16(dev, queue->mmio_base + B43legacy_PIO_TXQBUFSIZE); if (qsize == 0) { b43legacyerr(dev->wl, "This card does not support PIO " "operation mode. Please use DMA mode " "(module parameter pio=0).\n"); goto err_freequeue; } if (qsize <= B43legacy_PIO_TXQADJUST) { b43legacyerr(dev->wl, "PIO tx device-queue too small (%u)\n", qsize); goto err_freequeue; } qsize -= B43legacy_PIO_TXQADJUST; queue->tx_devq_size = qsize; setup_txqueues(queue); out: return queue; err_freequeue: kfree(queue); queue = NULL; goto out; } static void cancel_transfers(struct b43legacy_pioqueue *queue) { struct b43legacy_pio_txpacket *packet, *tmp_packet; tasklet_disable(&queue->txtask); list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list) free_txpacket(packet, 0); list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) free_txpacket(packet, 0); } static void b43legacy_destroy_pioqueue(struct b43legacy_pioqueue *queue) { if (!queue) return; cancel_transfers(queue); kfree(queue); } void b43legacy_pio_free(struct b43legacy_wldev *dev) { struct b43legacy_pio *pio; if (!b43legacy_using_pio(dev)) return; pio = &dev->pio; b43legacy_destroy_pioqueue(pio->queue3); pio->queue3 = NULL; b43legacy_destroy_pioqueue(pio->queue2); pio->queue2 = NULL; b43legacy_destroy_pioqueue(pio->queue1); pio->queue1 = NULL; b43legacy_destroy_pioqueue(pio->queue0); pio->queue0 = NULL; } int b43legacy_pio_init(struct b43legacy_wldev *dev) { struct b43legacy_pio *pio = &dev->pio; struct b43legacy_pioqueue *queue; int err = -ENOMEM; queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO1_BASE); if (!queue) goto out; pio->queue0 = queue; queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO2_BASE); if (!queue) goto err_destroy0; pio->queue1 = queue; queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO3_BASE); if (!queue) goto err_destroy1; pio->queue2 = queue; queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO4_BASE); if (!queue) goto err_destroy2; pio->queue3 = queue; if (dev->dev->id.revision < 3) dev->irq_mask |= B43legacy_IRQ_PIO_WORKAROUND; b43legacydbg(dev->wl, "PIO initialized\n"); err = 0; out: return err; err_destroy2: b43legacy_destroy_pioqueue(pio->queue2); pio->queue2 = NULL; err_destroy1: b43legacy_destroy_pioqueue(pio->queue1); pio->queue1 = NULL; err_destroy0: b43legacy_destroy_pioqueue(pio->queue0); pio->queue0 = NULL; goto out; } int b43legacy_pio_tx(struct b43legacy_wldev *dev, struct sk_buff *skb) { struct b43legacy_pioqueue *queue = dev->pio.queue1; struct b43legacy_pio_txpacket *packet; B43legacy_WARN_ON(queue->tx_suspended); B43legacy_WARN_ON(list_empty(&queue->txfree)); packet = list_entry(queue->txfree.next, struct b43legacy_pio_txpacket, list); packet->skb = skb; list_move_tail(&packet->list, &queue->txqueue); queue->nr_txfree--; B43legacy_WARN_ON(queue->nr_txfree >= B43legacy_PIO_MAXTXPACKETS); tasklet_schedule(&queue->txtask); return 0; } void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status) { struct b43legacy_pioqueue *queue; struct b43legacy_pio_txpacket *packet; struct ieee80211_tx_info *info; int retry_limit; queue = parse_cookie(dev, status->cookie, &packet); B43legacy_WARN_ON(!queue); if (!packet->skb) return; queue->tx_devq_packets--; queue->tx_devq_used -= (packet->skb->len + sizeof(struct b43legacy_txhdr_fw3)); info = IEEE80211_SKB_CB(packet->skb); /* preserve the confiured retry limit before clearing the status * The xmit function has overwritten the rc's value with the actual * retry limit done by the hardware */ retry_limit = info->status.rates[0].count; ieee80211_tx_info_clear_status(info); if (status->acked) info->flags |= IEEE80211_TX_STAT_ACK; if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { /* * If the short retries (RTS, not data frame) have exceeded * the limit, the hw will not have tried the selected rate, * but will have used the fallback rate instead. * Don't let the rate control count attempts for the selected * rate in this case, otherwise the statistics will be off. */ info->status.rates[0].count = 0; info->status.rates[1].count = status->frame_count; } else { if (status->frame_count > retry_limit) { info->status.rates[0].count = retry_limit; info->status.rates[1].count = status->frame_count - retry_limit; } else { info->status.rates[0].count = status->frame_count; info->status.rates[1].idx = -1; } } ieee80211_tx_status_irqsafe(dev->wl->hw, packet->skb); packet->skb = NULL; free_txpacket(packet, 1); /* If there are packets on the txqueue, poke the tasklet * to transmit them. */ if (!list_empty(&queue->txqueue)) tasklet_schedule(&queue->txtask); } static void pio_rx_error(struct b43legacy_pioqueue *queue, int clear_buffers, const char *error) { int i; b43legacyerr(queue->dev->wl, "PIO RX error: %s\n", error); b43legacy_pio_write(queue, B43legacy_PIO_RXCTL, B43legacy_PIO_RXCTL_READY); if (clear_buffers) { B43legacy_WARN_ON(queue->mmio_base != B43legacy_MMIO_PIO1_BASE); for (i = 0; i < 15; i++) { /* Dummy read. */ b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); } } } void b43legacy_pio_rx(struct b43legacy_pioqueue *queue) { __le16 preamble[21] = { 0 }; struct b43legacy_rxhdr_fw3 *rxhdr; u16 tmp; u16 len; u16 macstat; int i; int preamble_readwords; struct sk_buff *skb; tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL); if (!(tmp & B43legacy_PIO_RXCTL_DATAAVAILABLE)) return; b43legacy_pio_write(queue, B43legacy_PIO_RXCTL, B43legacy_PIO_RXCTL_DATAAVAILABLE); for (i = 0; i < 10; i++) { tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL); if (tmp & B43legacy_PIO_RXCTL_READY) goto data_ready; udelay(10); } b43legacydbg(queue->dev->wl, "PIO RX timed out\n"); return; data_ready: len = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); if (unlikely(len > 0x700)) { pio_rx_error(queue, 0, "len > 0x700"); return; } if (unlikely(len == 0 && queue->mmio_base != B43legacy_MMIO_PIO4_BASE)) { pio_rx_error(queue, 0, "len == 0"); return; } preamble[0] = cpu_to_le16(len); if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE) preamble_readwords = 14 / sizeof(u16); else preamble_readwords = 18 / sizeof(u16); for (i = 0; i < preamble_readwords; i++) { tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); preamble[i + 1] = cpu_to_le16(tmp); } rxhdr = (struct b43legacy_rxhdr_fw3 *)preamble; macstat = le16_to_cpu(rxhdr->mac_status); if (macstat & B43legacy_RX_MAC_FCSERR) { pio_rx_error(queue, (queue->mmio_base == B43legacy_MMIO_PIO1_BASE), "Frame FCS error"); return; } if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE) { /* We received an xmit status. */ struct b43legacy_hwtxstatus *hw; hw = (struct b43legacy_hwtxstatus *)(preamble + 1); b43legacy_handle_hwtxstatus(queue->dev, hw); return; } skb = dev_alloc_skb(len); if (unlikely(!skb)) { pio_rx_error(queue, 1, "OOM"); return; } skb_put(skb, len); for (i = 0; i < len - 1; i += 2) { tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); *((__le16 *)(skb->data + i)) = cpu_to_le16(tmp); } if (len % 2) { tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); skb->data[len - 1] = (tmp & 0x00FF); } b43legacy_rx(queue->dev, skb, rxhdr); } void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue) { b43legacy_power_saving_ctl_bits(queue->dev, -1, 1); b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, b43legacy_pio_read(queue, B43legacy_PIO_TXCTL) | B43legacy_PIO_TXCTL_SUSPEND); } void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue) { b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, b43legacy_pio_read(queue, B43legacy_PIO_TXCTL) & ~B43legacy_PIO_TXCTL_SUSPEND); b43legacy_power_saving_ctl_bits(queue->dev, -1, -1); tasklet_schedule(&queue->txtask); } void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev) { struct b43legacy_pio *pio; B43legacy_WARN_ON(!b43legacy_using_pio(dev)); pio = &dev->pio; pio->queue0->tx_frozen = 1; pio->queue1->tx_frozen = 1; pio->queue2->tx_frozen = 1; pio->queue3->tx_frozen = 1; } void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev) { struct b43legacy_pio *pio; B43legacy_WARN_ON(!b43legacy_using_pio(dev)); pio = &dev->pio; pio->queue0->tx_frozen = 0; pio->queue1->tx_frozen = 0; pio->queue2->tx_frozen = 0; pio->queue3->tx_frozen = 0; if (!list_empty(&pio->queue0->txqueue)) tasklet_schedule(&pio->queue0->txtask); if (!list_empty(&pio->queue1->txqueue)) tasklet_schedule(&pio->queue1->txtask); if (!list_empty(&pio->queue2->txqueue)) tasklet_schedule(&pio->queue2->txtask); if (!list_empty(&pio->queue3->txqueue)) tasklet_schedule(&pio->queue3->txtask); } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/phy.h0000644000175000017500000002360212026211315023212 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver Copyright (c) 2005 Martin Langer , Stefano Brivio Michael Buesch Danny van Dyk Andreas Jaggi Copyright (c) 2007 Larry Finger Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef B43legacy_PHY_H_ #define B43legacy_PHY_H_ #include enum { B43legacy_ANTENNA0, /* Antenna 0 */ B43legacy_ANTENNA1, /* Antenna 0 */ B43legacy_ANTENNA_AUTO1, /* Automatic, starting with antenna 1 */ B43legacy_ANTENNA_AUTO0, /* Automatic, starting with antenna 0 */ B43legacy_ANTENNA_AUTO = B43legacy_ANTENNA_AUTO0, B43legacy_ANTENNA_DEFAULT = B43legacy_ANTENNA_AUTO, }; enum { B43legacy_INTERFMODE_NONE, B43legacy_INTERFMODE_NONWLAN, B43legacy_INTERFMODE_MANUALWLAN, B43legacy_INTERFMODE_AUTOWLAN, }; /*** PHY Registers ***/ /* Routing */ #define B43legacy_PHYROUTE_OFDM_GPHY 0x400 #define B43legacy_PHYROUTE_EXT_GPHY 0x800 /* Base registers. */ #define B43legacy_PHY_BASE(reg) (reg) /* OFDM (A) registers of a G-PHY */ #define B43legacy_PHY_OFDM(reg) ((reg) | B43legacy_PHYROUTE_OFDM_GPHY) /* Extended G-PHY registers */ #define B43legacy_PHY_EXTG(reg) ((reg) | B43legacy_PHYROUTE_EXT_GPHY) /* Extended G-PHY Registers */ #define B43legacy_PHY_CLASSCTL B43legacy_PHY_EXTG(0x02) /* Classify control */ #define B43legacy_PHY_GTABCTL B43legacy_PHY_EXTG(0x03) /* G-PHY table control (see below) */ #define B43legacy_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */ #define B43legacy_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */ #define B43legacy_PHY_GTABNR_SHIFT 10 #define B43legacy_PHY_GTABDATA B43legacy_PHY_EXTG(0x04) /* G-PHY table data */ #define B43legacy_PHY_LO_MASK B43legacy_PHY_EXTG(0x0F) /* Local Oscillator control mask */ #define B43legacy_PHY_LO_CTL B43legacy_PHY_EXTG(0x10) /* Local Oscillator control */ #define B43legacy_PHY_RFOVER B43legacy_PHY_EXTG(0x11) /* RF override */ #define B43legacy_PHY_RFOVERVAL B43legacy_PHY_EXTG(0x12) /* RF override value */ /*** OFDM table numbers ***/ #define B43legacy_OFDMTAB(number, offset) \ (((number) << B43legacy_PHY_OTABLENR_SHIFT) \ | (offset)) #define B43legacy_OFDMTAB_AGC1 B43legacy_OFDMTAB(0x00, 0) #define B43legacy_OFDMTAB_GAIN0 B43legacy_OFDMTAB(0x00, 0) #define B43legacy_OFDMTAB_GAINX B43legacy_OFDMTAB(0x01, 0) #define B43legacy_OFDMTAB_GAIN1 B43legacy_OFDMTAB(0x01, 4) #define B43legacy_OFDMTAB_AGC3 B43legacy_OFDMTAB(0x02, 0) #define B43legacy_OFDMTAB_GAIN2 B43legacy_OFDMTAB(0x02, 3) #define B43legacy_OFDMTAB_LNAHPFGAIN1 B43legacy_OFDMTAB(0x03, 0) #define B43legacy_OFDMTAB_WRSSI B43legacy_OFDMTAB(0x04, 0) #define B43legacy_OFDMTAB_LNAHPFGAIN2 B43legacy_OFDMTAB(0x04, 0) #define B43legacy_OFDMTAB_NOISESCALE B43legacy_OFDMTAB(0x05, 0) #define B43legacy_OFDMTAB_AGC2 B43legacy_OFDMTAB(0x06, 0) #define B43legacy_OFDMTAB_ROTOR B43legacy_OFDMTAB(0x08, 0) #define B43legacy_OFDMTAB_ADVRETARD B43legacy_OFDMTAB(0x09, 0) #define B43legacy_OFDMTAB_DAC B43legacy_OFDMTAB(0x0C, 0) #define B43legacy_OFDMTAB_DC B43legacy_OFDMTAB(0x0E, 7) #define B43legacy_OFDMTAB_PWRDYN2 B43legacy_OFDMTAB(0x0E, 12) #define B43legacy_OFDMTAB_LNAGAIN B43legacy_OFDMTAB(0x0E, 13) #define B43legacy_OFDMTAB_LPFGAIN B43legacy_OFDMTAB(0x0F, 12) #define B43legacy_OFDMTAB_RSSI B43legacy_OFDMTAB(0x10, 0) #define B43legacy_OFDMTAB_AGC1_R1 B43legacy_OFDMTAB(0x13, 0) #define B43legacy_OFDMTAB_GAINX_R1 B43legacy_OFDMTAB(0x14, 0) #define B43legacy_OFDMTAB_MINSIGSQ B43legacy_OFDMTAB(0x14, 1) #define B43legacy_OFDMTAB_AGC3_R1 B43legacy_OFDMTAB(0x15, 0) #define B43legacy_OFDMTAB_WRSSI_R1 B43legacy_OFDMTAB(0x15, 4) #define B43legacy_OFDMTAB_TSSI B43legacy_OFDMTAB(0x15, 0) #define B43legacy_OFDMTAB_DACRFPABB B43legacy_OFDMTAB(0x16, 0) #define B43legacy_OFDMTAB_DACOFF B43legacy_OFDMTAB(0x17, 0) #define B43legacy_OFDMTAB_DCBIAS B43legacy_OFDMTAB(0x18, 0) void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt); /* OFDM (A) PHY Registers */ #define B43legacy_PHY_VERSION_OFDM B43legacy_PHY_OFDM(0x00) /* Versioning register for A-PHY */ #define B43legacy_PHY_BBANDCFG B43legacy_PHY_OFDM(0x01) /* Baseband config */ #define B43legacy_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */ #define B43legacy_PHY_BBANDCFG_RXANT_SHIFT 7 #define B43legacy_PHY_PWRDOWN B43legacy_PHY_OFDM(0x03) /* Powerdown */ #define B43legacy_PHY_CRSTHRES1 B43legacy_PHY_OFDM(0x06) /* CRS Threshold 1 */ #define B43legacy_PHY_LNAHPFCTL B43legacy_PHY_OFDM(0x1C) /* LNA/HPF control */ #define B43legacy_PHY_ADIVRELATED B43legacy_PHY_OFDM(0x27) /* FIXME rename */ #define B43legacy_PHY_CRS0 B43legacy_PHY_OFDM(0x29) #define B43legacy_PHY_ANTDWELL B43legacy_PHY_OFDM(0x2B) /* Antenna dwell */ #define B43legacy_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */ #define B43legacy_PHY_ENCORE B43legacy_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */ #define B43legacy_PHY_ENCORE_EN 0x0200 /* Encore enable */ #define B43legacy_PHY_LMS B43legacy_PHY_OFDM(0x55) #define B43legacy_PHY_OFDM61 B43legacy_PHY_OFDM(0x61) /* FIXME rename */ #define B43legacy_PHY_OFDM61_10 0x0010 /* FIXME rename */ #define B43legacy_PHY_IQBAL B43legacy_PHY_OFDM(0x69) /* I/Q balance */ #define B43legacy_PHY_OTABLECTL B43legacy_PHY_OFDM(0x72) /* OFDM table control (see below) */ #define B43legacy_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */ #define B43legacy_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */ #define B43legacy_PHY_OTABLENR_SHIFT 10 #define B43legacy_PHY_OTABLEI B43legacy_PHY_OFDM(0x73) /* OFDM table data I */ #define B43legacy_PHY_OTABLEQ B43legacy_PHY_OFDM(0x74) /* OFDM table data Q */ #define B43legacy_PHY_HPWR_TSSICTL B43legacy_PHY_OFDM(0x78) /* Hardware power TSSI control */ #define B43legacy_PHY_NRSSITHRES B43legacy_PHY_OFDM(0x8A) /* NRSSI threshold */ #define B43legacy_PHY_ANTWRSETT B43legacy_PHY_OFDM(0x8C) /* Antenna WR settle */ #define B43legacy_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */ #define B43legacy_PHY_CLIPPWRDOWNT B43legacy_PHY_OFDM(0x93) /* Clip powerdown threshold */ #define B43legacy_PHY_OFDM9B B43legacy_PHY_OFDM(0x9B) /* FIXME rename */ #define B43legacy_PHY_N1P1GAIN B43legacy_PHY_OFDM(0xA0) #define B43legacy_PHY_P1P2GAIN B43legacy_PHY_OFDM(0xA1) #define B43legacy_PHY_N1N2GAIN B43legacy_PHY_OFDM(0xA2) #define B43legacy_PHY_CLIPTHRES B43legacy_PHY_OFDM(0xA3) #define B43legacy_PHY_CLIPN1P2THRES B43legacy_PHY_OFDM(0xA4) #define B43legacy_PHY_DIVSRCHIDX B43legacy_PHY_OFDM(0xA8) /* Divider search gain/index */ #define B43legacy_PHY_CLIPP2THRES B43legacy_PHY_OFDM(0xA9) #define B43legacy_PHY_CLIPP3THRES B43legacy_PHY_OFDM(0xAA) #define B43legacy_PHY_DIVP1P2GAIN B43legacy_PHY_OFDM(0xAB) #define B43legacy_PHY_DIVSRCHGAINBACK B43legacy_PHY_OFDM(0xAD) /* Divider search gain back */ #define B43legacy_PHY_DIVSRCHGAINCHNG B43legacy_PHY_OFDM(0xAE) /* Divider search gain change */ #define B43legacy_PHY_CRSTHRES1_R1 B43legacy_PHY_OFDM(0xC0) /* CRS Threshold 1 (rev 1 only) */ #define B43legacy_PHY_CRSTHRES2_R1 B43legacy_PHY_OFDM(0xC1) /* CRS Threshold 2 (rev 1 only) */ #define B43legacy_PHY_TSSIP_LTBASE B43legacy_PHY_OFDM(0x380) /* TSSI power lookup table base */ #define B43legacy_PHY_DC_LTBASE B43legacy_PHY_OFDM(0x3A0) /* DC lookup table base */ #define B43legacy_PHY_GAIN_LTBASE B43legacy_PHY_OFDM(0x3C0) /* Gain lookup table base */ void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt); /* Masks for the different PHY versioning registers. */ #define B43legacy_PHYVER_ANALOG 0xF000 #define B43legacy_PHYVER_ANALOG_SHIFT 12 #define B43legacy_PHYVER_TYPE 0x0F00 #define B43legacy_PHYVER_TYPE_SHIFT 8 #define B43legacy_PHYVER_VERSION 0x00FF struct b43legacy_wldev; void b43legacy_phy_lock(struct b43legacy_wldev *dev); void b43legacy_phy_unlock(struct b43legacy_wldev *dev); /* Card uses the loopback gain stuff */ #define has_loopback_gain(phy) \ (((phy)->rev > 1) || ((phy)->gmode)) u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset); void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val); int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev); int b43legacy_phy_init(struct b43legacy_wldev *dev); void b43legacy_set_rx_antenna(struct b43legacy_wldev *dev, int antenna); void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev); void b43legacy_phy_calibrate(struct b43legacy_wldev *dev); int b43legacy_phy_connect(struct b43legacy_wldev *dev, int connect); void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev); void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev); void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev); /* Adjust the LocalOscillator to the saved values. * "fixed" is only set to 1 once in initialization. Set to 0 otherwise. */ void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed); void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev); void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev, u16 baseband_attenuation); void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev, int bit25, int bit26); #endif /* B43legacy_PHY_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/phy.c0000644000175000017500000020121012026211315023176 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver Copyright (c) 2005 Martin Langer , Stefano Brivio Michael Buesch Danny van Dyk Andreas Jaggi Copyright (c) 2007 Larry Finger Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "b43legacy.h" #include "phy.h" #include "main.h" #include "radio.h" #include "ilt.h" static const s8 b43legacy_tssi2dbm_b_table[] = { 0x4D, 0x4C, 0x4B, 0x4A, 0x4A, 0x49, 0x48, 0x47, 0x47, 0x46, 0x45, 0x45, 0x44, 0x43, 0x42, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x32, 0x31, 0x30, 0x2F, 0x2D, 0x2C, 0x2B, 0x29, 0x28, 0x26, 0x25, 0x23, 0x21, 0x1F, 0x1D, 0x1A, 0x17, 0x14, 0x10, 0x0C, 0x06, 0x00, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, }; static const s8 b43legacy_tssi2dbm_g_table[] = { 77, 77, 77, 76, 76, 76, 75, 75, 74, 74, 73, 73, 73, 72, 72, 71, 71, 70, 70, 69, 68, 68, 67, 67, 66, 65, 65, 64, 63, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 50, 49, 47, 45, 43, 40, 37, 33, 28, 22, 14, 5, -7, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, }; static void b43legacy_phy_initg(struct b43legacy_wldev *dev); static inline void b43legacy_voluntary_preempt(void) { B43legacy_BUG_ON(!(!in_atomic() && !in_irq() && !in_interrupt() && !irqs_disabled())); #ifndef CONFIG_PREEMPT cond_resched(); #endif /* CONFIG_PREEMPT */ } /* Lock the PHY registers against concurrent access from the microcode. * This lock is nonrecursive. */ void b43legacy_phy_lock(struct b43legacy_wldev *dev) { #if B43legacy_DEBUG B43legacy_WARN_ON(dev->phy.phy_locked); dev->phy.phy_locked = 1; #endif if (dev->dev->id.revision < 3) { b43legacy_mac_suspend(dev); } else { if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) b43legacy_power_saving_ctl_bits(dev, -1, 1); } } void b43legacy_phy_unlock(struct b43legacy_wldev *dev) { #if B43legacy_DEBUG B43legacy_WARN_ON(!dev->phy.phy_locked); dev->phy.phy_locked = 0; #endif if (dev->dev->id.revision < 3) { b43legacy_mac_enable(dev); } else { if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) b43legacy_power_saving_ctl_bits(dev, -1, -1); } } u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset) { b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset); return b43legacy_read16(dev, B43legacy_MMIO_PHY_DATA); } void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val) { b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_PHY_DATA, val); } void b43legacy_phy_calibrate(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; b43legacy_read32(dev, B43legacy_MMIO_MACCTL); /* Dummy read. */ if (phy->calibrated) return; if (phy->type == B43legacy_PHYTYPE_G && phy->rev == 1) { b43legacy_wireless_core_reset(dev, 0); b43legacy_phy_initg(dev); b43legacy_wireless_core_reset(dev, B43legacy_TMSLOW_GMODE); } phy->calibrated = 1; } /* initialize B PHY power control * as described in http://bcm-specs.sipsolutions.net/InitPowerControl */ static void b43legacy_phy_init_pctl(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 saved_batt = 0; u16 saved_ratt = 0; u16 saved_txctl1 = 0; int must_reset_txpower = 0; B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B || phy->type == B43legacy_PHYTYPE_G)); if (is_bcm_board_vendor(dev) && (dev->dev->bus->boardinfo.type == 0x0416)) return; b43legacy_phy_write(dev, 0x0028, 0x8018); b43legacy_write16(dev, 0x03E6, b43legacy_read16(dev, 0x03E6) & 0xFFDF); if (phy->type == B43legacy_PHYTYPE_G) { if (!phy->gmode) return; b43legacy_phy_write(dev, 0x047A, 0xC111); } if (phy->savedpctlreg != 0xFFFF) return; #ifdef CONFIG_B43LEGACY_DEBUG if (phy->manual_txpower_control) return; #endif if (phy->type == B43legacy_PHYTYPE_B && phy->rev >= 2 && phy->radio_ver == 0x2050) b43legacy_radio_write16(dev, 0x0076, b43legacy_radio_read16(dev, 0x0076) | 0x0084); else { saved_batt = phy->bbatt; saved_ratt = phy->rfatt; saved_txctl1 = phy->txctl1; if ((phy->radio_rev >= 6) && (phy->radio_rev <= 8) && /*FIXME: incomplete specs for 5 < revision < 9 */ 0) b43legacy_radio_set_txpower_bg(dev, 0xB, 0x1F, 0); else b43legacy_radio_set_txpower_bg(dev, 0xB, 9, 0); must_reset_txpower = 1; } b43legacy_dummy_transmission(dev); phy->savedpctlreg = b43legacy_phy_read(dev, B43legacy_PHY_G_PCTL); if (must_reset_txpower) b43legacy_radio_set_txpower_bg(dev, saved_batt, saved_ratt, saved_txctl1); else b43legacy_radio_write16(dev, 0x0076, b43legacy_radio_read16(dev, 0x0076) & 0xFF7B); b43legacy_radio_clear_tssi(dev); } static void b43legacy_phy_agcsetup(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 offset = 0x0000; if (phy->rev == 1) offset = 0x4C00; b43legacy_ilt_write(dev, offset, 0x00FE); b43legacy_ilt_write(dev, offset + 1, 0x000D); b43legacy_ilt_write(dev, offset + 2, 0x0013); b43legacy_ilt_write(dev, offset + 3, 0x0019); if (phy->rev == 1) { b43legacy_ilt_write(dev, 0x1800, 0x2710); b43legacy_ilt_write(dev, 0x1801, 0x9B83); b43legacy_ilt_write(dev, 0x1802, 0x9B83); b43legacy_ilt_write(dev, 0x1803, 0x0F8D); b43legacy_phy_write(dev, 0x0455, 0x0004); } b43legacy_phy_write(dev, 0x04A5, (b43legacy_phy_read(dev, 0x04A5) & 0x00FF) | 0x5700); b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A) & 0xFF80) | 0x000F); b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A) & 0xC07F) | 0x2B80); b43legacy_phy_write(dev, 0x048C, (b43legacy_phy_read(dev, 0x048C) & 0xF0FF) | 0x0300); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0008); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xFFF0) | 0x0008); b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xF0FF) | 0x0600); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xF0FF) | 0x0700); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xF0FF) | 0x0100); if (phy->rev == 1) b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xFFF0) | 0x0007); b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488) & 0xFF00) | 0x001C); b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488) & 0xC0FF) | 0x0200); b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496) & 0xFF00) | 0x001C); b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489) & 0xFF00) | 0x0020); b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489) & 0xC0FF) | 0x0200); b43legacy_phy_write(dev, 0x0482, (b43legacy_phy_read(dev, 0x0482) & 0xFF00) | 0x002E); b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496) & 0x00FF) | 0x1A00); b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481) & 0xFF00) | 0x0028); b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481) & 0x00FF) | 0x2C00); if (phy->rev == 1) { b43legacy_phy_write(dev, 0x0430, 0x092B); b43legacy_phy_write(dev, 0x041B, (b43legacy_phy_read(dev, 0x041B) & 0xFFE1) | 0x0002); } else { b43legacy_phy_write(dev, 0x041B, b43legacy_phy_read(dev, 0x041B) & 0xFFE1); b43legacy_phy_write(dev, 0x041F, 0x287A); b43legacy_phy_write(dev, 0x0420, (b43legacy_phy_read(dev, 0x0420) & 0xFFF0) | 0x0004); } if (phy->rev > 2) { b43legacy_phy_write(dev, 0x0422, 0x287A); b43legacy_phy_write(dev, 0x0420, (b43legacy_phy_read(dev, 0x0420) & 0x0FFF) | 0x3000); } b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) & 0x8080) | 0x7874); b43legacy_phy_write(dev, 0x048E, 0x1C00); if (phy->rev == 1) { b43legacy_phy_write(dev, 0x04AB, (b43legacy_phy_read(dev, 0x04AB) & 0xF0FF) | 0x0600); b43legacy_phy_write(dev, 0x048B, 0x005E); b43legacy_phy_write(dev, 0x048C, (b43legacy_phy_read(dev, 0x048C) & 0xFF00) | 0x001E); b43legacy_phy_write(dev, 0x048D, 0x0002); } b43legacy_ilt_write(dev, offset + 0x0800, 0); b43legacy_ilt_write(dev, offset + 0x0801, 7); b43legacy_ilt_write(dev, offset + 0x0802, 16); b43legacy_ilt_write(dev, offset + 0x0803, 28); if (phy->rev >= 6) { b43legacy_phy_write(dev, 0x0426, (b43legacy_phy_read(dev, 0x0426) & 0xFFFC)); b43legacy_phy_write(dev, 0x0426, (b43legacy_phy_read(dev, 0x0426) & 0xEFFF)); } } static void b43legacy_phy_setupg(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 i; B43legacy_BUG_ON(phy->type != B43legacy_PHYTYPE_G); if (phy->rev == 1) { b43legacy_phy_write(dev, 0x0406, 0x4F19); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, (b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) & 0xFC3F) | 0x0340); b43legacy_phy_write(dev, 0x042C, 0x005A); b43legacy_phy_write(dev, 0x0427, 0x001A); for (i = 0; i < B43legacy_ILT_FINEFREQG_SIZE; i++) b43legacy_ilt_write(dev, 0x5800 + i, b43legacy_ilt_finefreqg[i]); for (i = 0; i < B43legacy_ILT_NOISEG1_SIZE; i++) b43legacy_ilt_write(dev, 0x1800 + i, b43legacy_ilt_noiseg1[i]); for (i = 0; i < B43legacy_ILT_ROTOR_SIZE; i++) b43legacy_ilt_write32(dev, 0x2000 + i, b43legacy_ilt_rotor[i]); } else { /* nrssi values are signed 6-bit values. Why 0x7654 here? */ b43legacy_nrssi_hw_write(dev, 0xBA98, (s16)0x7654); if (phy->rev == 2) { b43legacy_phy_write(dev, 0x04C0, 0x1861); b43legacy_phy_write(dev, 0x04C1, 0x0271); } else if (phy->rev > 2) { b43legacy_phy_write(dev, 0x04C0, 0x0098); b43legacy_phy_write(dev, 0x04C1, 0x0070); b43legacy_phy_write(dev, 0x04C9, 0x0080); } b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) | 0x800); for (i = 0; i < 64; i++) b43legacy_ilt_write(dev, 0x4000 + i, i); for (i = 0; i < B43legacy_ILT_NOISEG2_SIZE; i++) b43legacy_ilt_write(dev, 0x1800 + i, b43legacy_ilt_noiseg2[i]); } if (phy->rev <= 2) for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) b43legacy_ilt_write(dev, 0x1400 + i, b43legacy_ilt_noisescaleg1[i]); else if ((phy->rev >= 7) && (b43legacy_phy_read(dev, 0x0449) & 0x0200)) for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) b43legacy_ilt_write(dev, 0x1400 + i, b43legacy_ilt_noisescaleg3[i]); else for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) b43legacy_ilt_write(dev, 0x1400 + i, b43legacy_ilt_noisescaleg2[i]); if (phy->rev == 2) for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++) b43legacy_ilt_write(dev, 0x5000 + i, b43legacy_ilt_sigmasqr1[i]); else if ((phy->rev > 2) && (phy->rev <= 8)) for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++) b43legacy_ilt_write(dev, 0x5000 + i, b43legacy_ilt_sigmasqr2[i]); if (phy->rev == 1) { for (i = 0; i < B43legacy_ILT_RETARD_SIZE; i++) b43legacy_ilt_write32(dev, 0x2400 + i, b43legacy_ilt_retard[i]); for (i = 4; i < 20; i++) b43legacy_ilt_write(dev, 0x5400 + i, 0x0020); b43legacy_phy_agcsetup(dev); if (is_bcm_board_vendor(dev) && (dev->dev->bus->boardinfo.type == 0x0416) && (dev->dev->bus->sprom.board_rev == 0x0017)) return; b43legacy_ilt_write(dev, 0x5001, 0x0002); b43legacy_ilt_write(dev, 0x5002, 0x0001); } else { for (i = 0; i <= 0x20; i++) b43legacy_ilt_write(dev, 0x1000 + i, 0x0820); b43legacy_phy_agcsetup(dev); b43legacy_phy_read(dev, 0x0400); /* dummy read */ b43legacy_phy_write(dev, 0x0403, 0x1000); b43legacy_ilt_write(dev, 0x3C02, 0x000F); b43legacy_ilt_write(dev, 0x3C03, 0x0014); if (is_bcm_board_vendor(dev) && (dev->dev->bus->boardinfo.type == 0x0416) && (dev->dev->bus->sprom.board_rev == 0x0017)) return; b43legacy_ilt_write(dev, 0x0401, 0x0002); b43legacy_ilt_write(dev, 0x0402, 0x0001); } } /* Initialize the APHY portion of a GPHY. */ static void b43legacy_phy_inita(struct b43legacy_wldev *dev) { might_sleep(); b43legacy_phy_setupg(dev); if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) b43legacy_phy_write(dev, 0x046E, 0x03CF); } static void b43legacy_phy_initb2(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 offset; int val; b43legacy_write16(dev, 0x03EC, 0x3F22); b43legacy_phy_write(dev, 0x0020, 0x301C); b43legacy_phy_write(dev, 0x0026, 0x0000); b43legacy_phy_write(dev, 0x0030, 0x00C6); b43legacy_phy_write(dev, 0x0088, 0x3E00); val = 0x3C3D; for (offset = 0x0089; offset < 0x00A7; offset++) { b43legacy_phy_write(dev, offset, val); val -= 0x0202; } b43legacy_phy_write(dev, 0x03E4, 0x3000); b43legacy_radio_selectchannel(dev, phy->channel, 0); if (phy->radio_ver != 0x2050) { b43legacy_radio_write16(dev, 0x0075, 0x0080); b43legacy_radio_write16(dev, 0x0079, 0x0081); } b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x0050, 0x0023); if (phy->radio_ver == 0x2050) { b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x005A, 0x0070); b43legacy_radio_write16(dev, 0x005B, 0x007B); b43legacy_radio_write16(dev, 0x005C, 0x00B0); b43legacy_radio_write16(dev, 0x007A, 0x000F); b43legacy_phy_write(dev, 0x0038, 0x0677); b43legacy_radio_init2050(dev); } b43legacy_phy_write(dev, 0x0014, 0x0080); b43legacy_phy_write(dev, 0x0032, 0x00CA); b43legacy_phy_write(dev, 0x0032, 0x00CC); b43legacy_phy_write(dev, 0x0035, 0x07C2); b43legacy_phy_lo_b_measure(dev); b43legacy_phy_write(dev, 0x0026, 0xCC00); if (phy->radio_ver != 0x2050) b43legacy_phy_write(dev, 0x0026, 0xCE00); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1000); b43legacy_phy_write(dev, 0x002A, 0x88A3); if (phy->radio_ver != 0x2050) b43legacy_phy_write(dev, 0x002A, 0x88C2); b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); b43legacy_phy_init_pctl(dev); } static void b43legacy_phy_initb4(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 offset; u16 val; b43legacy_write16(dev, 0x03EC, 0x3F22); b43legacy_phy_write(dev, 0x0020, 0x301C); b43legacy_phy_write(dev, 0x0026, 0x0000); b43legacy_phy_write(dev, 0x0030, 0x00C6); b43legacy_phy_write(dev, 0x0088, 0x3E00); val = 0x3C3D; for (offset = 0x0089; offset < 0x00A7; offset++) { b43legacy_phy_write(dev, offset, val); val -= 0x0202; } b43legacy_phy_write(dev, 0x03E4, 0x3000); b43legacy_radio_selectchannel(dev, phy->channel, 0); if (phy->radio_ver != 0x2050) { b43legacy_radio_write16(dev, 0x0075, 0x0080); b43legacy_radio_write16(dev, 0x0079, 0x0081); } b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x0050, 0x0023); if (phy->radio_ver == 0x2050) { b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x005A, 0x0070); b43legacy_radio_write16(dev, 0x005B, 0x007B); b43legacy_radio_write16(dev, 0x005C, 0x00B0); b43legacy_radio_write16(dev, 0x007A, 0x000F); b43legacy_phy_write(dev, 0x0038, 0x0677); b43legacy_radio_init2050(dev); } b43legacy_phy_write(dev, 0x0014, 0x0080); b43legacy_phy_write(dev, 0x0032, 0x00CA); if (phy->radio_ver == 0x2050) b43legacy_phy_write(dev, 0x0032, 0x00E0); b43legacy_phy_write(dev, 0x0035, 0x07C2); b43legacy_phy_lo_b_measure(dev); b43legacy_phy_write(dev, 0x0026, 0xCC00); if (phy->radio_ver == 0x2050) b43legacy_phy_write(dev, 0x0026, 0xCE00); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1100); b43legacy_phy_write(dev, 0x002A, 0x88A3); if (phy->radio_ver == 0x2050) b43legacy_phy_write(dev, 0x002A, 0x88C2); b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { b43legacy_calc_nrssi_slope(dev); b43legacy_calc_nrssi_threshold(dev); } b43legacy_phy_init_pctl(dev); } static void b43legacy_phy_initb5(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 offset; u16 value; u8 old_channel; if (phy->analog == 1) b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0050); if (!is_bcm_board_vendor(dev) && (dev->dev->bus->boardinfo.type != 0x0416)) { value = 0x2120; for (offset = 0x00A8 ; offset < 0x00C7; offset++) { b43legacy_phy_write(dev, offset, value); value += 0x0202; } } b43legacy_phy_write(dev, 0x0035, (b43legacy_phy_read(dev, 0x0035) & 0xF0FF) | 0x0700); if (phy->radio_ver == 0x2050) b43legacy_phy_write(dev, 0x0038, 0x0667); if (phy->gmode) { if (phy->radio_ver == 0x2050) { b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0020); b43legacy_radio_write16(dev, 0x0051, b43legacy_radio_read16(dev, 0x0051) | 0x0004); } b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, 0x0000); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) | 0x0100); b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) | 0x2000); b43legacy_phy_write(dev, 0x001C, 0x186A); b43legacy_phy_write(dev, 0x0013, (b43legacy_phy_read(dev, 0x0013) & 0x00FF) | 0x1900); b43legacy_phy_write(dev, 0x0035, (b43legacy_phy_read(dev, 0x0035) & 0xFFC0) | 0x0064); b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, 0x005D) & 0xFF80) | 0x000A); b43legacy_phy_write(dev, 0x5B, 0x0000); b43legacy_phy_write(dev, 0x5C, 0x0000); } if (dev->bad_frames_preempt) b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, b43legacy_phy_read(dev, B43legacy_PHY_RADIO_BITFIELD) | (1 << 12)); if (phy->analog == 1) { b43legacy_phy_write(dev, 0x0026, 0xCE00); b43legacy_phy_write(dev, 0x0021, 0x3763); b43legacy_phy_write(dev, 0x0022, 0x1BC3); b43legacy_phy_write(dev, 0x0023, 0x06F9); b43legacy_phy_write(dev, 0x0024, 0x037E); } else b43legacy_phy_write(dev, 0x0026, 0xCC00); b43legacy_phy_write(dev, 0x0030, 0x00C6); b43legacy_write16(dev, 0x03EC, 0x3F22); if (phy->analog == 1) b43legacy_phy_write(dev, 0x0020, 0x3E1C); else b43legacy_phy_write(dev, 0x0020, 0x301C); if (phy->analog == 0) b43legacy_write16(dev, 0x03E4, 0x3000); old_channel = (phy->channel == 0xFF) ? 1 : phy->channel; /* Force to channel 7, even if not supported. */ b43legacy_radio_selectchannel(dev, 7, 0); if (phy->radio_ver != 0x2050) { b43legacy_radio_write16(dev, 0x0075, 0x0080); b43legacy_radio_write16(dev, 0x0079, 0x0081); } b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x0050, 0x0023); if (phy->radio_ver == 0x2050) { b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x005A, 0x0070); } b43legacy_radio_write16(dev, 0x005B, 0x007B); b43legacy_radio_write16(dev, 0x005C, 0x00B0); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0007); b43legacy_radio_selectchannel(dev, old_channel, 0); b43legacy_phy_write(dev, 0x0014, 0x0080); b43legacy_phy_write(dev, 0x0032, 0x00CA); b43legacy_phy_write(dev, 0x002A, 0x88A3); b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); if (phy->radio_ver == 0x2050) b43legacy_radio_write16(dev, 0x005D, 0x000D); b43legacy_write16(dev, 0x03E4, (b43legacy_read16(dev, 0x03E4) & 0xFFC0) | 0x0004); } static void b43legacy_phy_initb6(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 offset; u16 val; u8 old_channel; b43legacy_phy_write(dev, 0x003E, 0x817A); b43legacy_radio_write16(dev, 0x007A, (b43legacy_radio_read16(dev, 0x007A) | 0x0058)); if (phy->radio_rev == 4 || phy->radio_rev == 5) { b43legacy_radio_write16(dev, 0x0051, 0x0037); b43legacy_radio_write16(dev, 0x0052, 0x0070); b43legacy_radio_write16(dev, 0x0053, 0x00B3); b43legacy_radio_write16(dev, 0x0054, 0x009B); b43legacy_radio_write16(dev, 0x005A, 0x0088); b43legacy_radio_write16(dev, 0x005B, 0x0088); b43legacy_radio_write16(dev, 0x005D, 0x0088); b43legacy_radio_write16(dev, 0x005E, 0x0088); b43legacy_radio_write16(dev, 0x007D, 0x0088); b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET, (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET) | 0x00000200)); } if (phy->radio_rev == 8) { b43legacy_radio_write16(dev, 0x0051, 0x0000); b43legacy_radio_write16(dev, 0x0052, 0x0040); b43legacy_radio_write16(dev, 0x0053, 0x00B7); b43legacy_radio_write16(dev, 0x0054, 0x0098); b43legacy_radio_write16(dev, 0x005A, 0x0088); b43legacy_radio_write16(dev, 0x005B, 0x006B); b43legacy_radio_write16(dev, 0x005C, 0x000F); if (dev->dev->bus->sprom.boardflags_lo & 0x8000) { b43legacy_radio_write16(dev, 0x005D, 0x00FA); b43legacy_radio_write16(dev, 0x005E, 0x00D8); } else { b43legacy_radio_write16(dev, 0x005D, 0x00F5); b43legacy_radio_write16(dev, 0x005E, 0x00B8); } b43legacy_radio_write16(dev, 0x0073, 0x0003); b43legacy_radio_write16(dev, 0x007D, 0x00A8); b43legacy_radio_write16(dev, 0x007C, 0x0001); b43legacy_radio_write16(dev, 0x007E, 0x0008); } val = 0x1E1F; for (offset = 0x0088; offset < 0x0098; offset++) { b43legacy_phy_write(dev, offset, val); val -= 0x0202; } val = 0x3E3F; for (offset = 0x0098; offset < 0x00A8; offset++) { b43legacy_phy_write(dev, offset, val); val -= 0x0202; } val = 0x2120; for (offset = 0x00A8; offset < 0x00C8; offset++) { b43legacy_phy_write(dev, offset, (val & 0x3F3F)); val += 0x0202; } if (phy->type == B43legacy_PHYTYPE_G) { b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0020); b43legacy_radio_write16(dev, 0x0051, b43legacy_radio_read16(dev, 0x0051) | 0x0004); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) | 0x0100); b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) | 0x2000); b43legacy_phy_write(dev, 0x5B, 0x0000); b43legacy_phy_write(dev, 0x5C, 0x0000); } old_channel = phy->channel; if (old_channel >= 8) b43legacy_radio_selectchannel(dev, 1, 0); else b43legacy_radio_selectchannel(dev, 13, 0); b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x0050, 0x0023); udelay(40); if (phy->radio_rev < 6 || phy->radio_rev == 8) { b43legacy_radio_write16(dev, 0x007C, (b43legacy_radio_read16(dev, 0x007C) | 0x0002)); b43legacy_radio_write16(dev, 0x0050, 0x0020); } if (phy->radio_rev <= 2) { b43legacy_radio_write16(dev, 0x0050, 0x0020); b43legacy_radio_write16(dev, 0x005A, 0x0070); b43legacy_radio_write16(dev, 0x005B, 0x007B); b43legacy_radio_write16(dev, 0x005C, 0x00B0); } b43legacy_radio_write16(dev, 0x007A, (b43legacy_radio_read16(dev, 0x007A) & 0x00F8) | 0x0007); b43legacy_radio_selectchannel(dev, old_channel, 0); b43legacy_phy_write(dev, 0x0014, 0x0200); if (phy->radio_rev >= 6) b43legacy_phy_write(dev, 0x002A, 0x88C2); else b43legacy_phy_write(dev, 0x002A, 0x8AC0); b43legacy_phy_write(dev, 0x0038, 0x0668); b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); if (phy->radio_rev == 4 || phy->radio_rev == 5) b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, 0x005D) & 0xFF80) | 0x0003); if (phy->radio_rev <= 2) b43legacy_radio_write16(dev, 0x005D, 0x000D); if (phy->analog == 4) { b43legacy_write16(dev, 0x03E4, 0x0009); b43legacy_phy_write(dev, 0x61, b43legacy_phy_read(dev, 0x61) & 0xFFF); } else b43legacy_phy_write(dev, 0x0002, (b43legacy_phy_read(dev, 0x0002) & 0xFFC0) | 0x0004); if (phy->type == B43legacy_PHYTYPE_G) b43legacy_write16(dev, 0x03E6, 0x0); if (phy->type == B43legacy_PHYTYPE_B) { b43legacy_write16(dev, 0x03E6, 0x8140); b43legacy_phy_write(dev, 0x0016, 0x0410); b43legacy_phy_write(dev, 0x0017, 0x0820); b43legacy_phy_write(dev, 0x0062, 0x0007); b43legacy_radio_init2050(dev); b43legacy_phy_lo_g_measure(dev); if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { b43legacy_calc_nrssi_slope(dev); b43legacy_calc_nrssi_threshold(dev); } b43legacy_phy_init_pctl(dev); } } static void b43legacy_calc_loopback_gain(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 backup_phy[15] = {0}; u16 backup_radio[3]; u16 backup_bband; u16 i; u16 loop1_cnt; u16 loop1_done; u16 loop1_omitted; u16 loop2_done; backup_phy[0] = b43legacy_phy_read(dev, 0x0429); backup_phy[1] = b43legacy_phy_read(dev, 0x0001); backup_phy[2] = b43legacy_phy_read(dev, 0x0811); backup_phy[3] = b43legacy_phy_read(dev, 0x0812); if (phy->rev != 1) { backup_phy[4] = b43legacy_phy_read(dev, 0x0814); backup_phy[5] = b43legacy_phy_read(dev, 0x0815); } backup_phy[6] = b43legacy_phy_read(dev, 0x005A); backup_phy[7] = b43legacy_phy_read(dev, 0x0059); backup_phy[8] = b43legacy_phy_read(dev, 0x0058); backup_phy[9] = b43legacy_phy_read(dev, 0x000A); backup_phy[10] = b43legacy_phy_read(dev, 0x0003); backup_phy[11] = b43legacy_phy_read(dev, 0x080F); backup_phy[12] = b43legacy_phy_read(dev, 0x0810); backup_phy[13] = b43legacy_phy_read(dev, 0x002B); backup_phy[14] = b43legacy_phy_read(dev, 0x0015); b43legacy_phy_read(dev, 0x002D); /* dummy read */ backup_bband = phy->bbatt; backup_radio[0] = b43legacy_radio_read16(dev, 0x0052); backup_radio[1] = b43legacy_radio_read16(dev, 0x0043); backup_radio[2] = b43legacy_radio_read16(dev, 0x007A); b43legacy_phy_write(dev, 0x0429, b43legacy_phy_read(dev, 0x0429) & 0x3FFF); b43legacy_phy_write(dev, 0x0001, b43legacy_phy_read(dev, 0x0001) & 0x8000); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x0002); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) & 0xFFFD); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x0001); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) & 0xFFFE); if (phy->rev != 1) { b43legacy_phy_write(dev, 0x0814, b43legacy_phy_read(dev, 0x0814) | 0x0001); b43legacy_phy_write(dev, 0x0815, b43legacy_phy_read(dev, 0x0815) & 0xFFFE); b43legacy_phy_write(dev, 0x0814, b43legacy_phy_read(dev, 0x0814) | 0x0002); b43legacy_phy_write(dev, 0x0815, b43legacy_phy_read(dev, 0x0815) & 0xFFFD); } b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x000C); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | 0x000C); b43legacy_phy_write(dev, 0x0811, (b43legacy_phy_read(dev, 0x0811) & 0xFFCF) | 0x0030); b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) & 0xFFCF) | 0x0010); b43legacy_phy_write(dev, 0x005A, 0x0780); b43legacy_phy_write(dev, 0x0059, 0xC810); b43legacy_phy_write(dev, 0x0058, 0x000D); if (phy->analog == 0) b43legacy_phy_write(dev, 0x0003, 0x0122); else b43legacy_phy_write(dev, 0x000A, b43legacy_phy_read(dev, 0x000A) | 0x2000); if (phy->rev != 1) { b43legacy_phy_write(dev, 0x0814, b43legacy_phy_read(dev, 0x0814) | 0x0004); b43legacy_phy_write(dev, 0x0815, b43legacy_phy_read(dev, 0x0815) & 0xFFFB); } b43legacy_phy_write(dev, 0x0003, (b43legacy_phy_read(dev, 0x0003) & 0xFF9F) | 0x0040); if (phy->radio_ver == 0x2050 && phy->radio_rev == 2) { b43legacy_radio_write16(dev, 0x0052, 0x0000); b43legacy_radio_write16(dev, 0x0043, (b43legacy_radio_read16(dev, 0x0043) & 0xFFF0) | 0x0009); loop1_cnt = 9; } else if (phy->radio_rev == 8) { b43legacy_radio_write16(dev, 0x0043, 0x000F); loop1_cnt = 15; } else loop1_cnt = 0; b43legacy_phy_set_baseband_attenuation(dev, 11); if (phy->rev >= 3) b43legacy_phy_write(dev, 0x080F, 0xC020); else b43legacy_phy_write(dev, 0x080F, 0x8020); b43legacy_phy_write(dev, 0x0810, 0x0000); b43legacy_phy_write(dev, 0x002B, (b43legacy_phy_read(dev, 0x002B) & 0xFFC0) | 0x0001); b43legacy_phy_write(dev, 0x002B, (b43legacy_phy_read(dev, 0x002B) & 0xC0FF) | 0x0800); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x0100); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) & 0xCFFF); if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_EXTLNA) { if (phy->rev >= 7) { b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x0800); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | 0x8000); } } b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) & 0x00F7); for (i = 0; i < loop1_cnt; i++) { b43legacy_radio_write16(dev, 0x0043, loop1_cnt); b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) & 0xF0FF) | (i << 8)); b43legacy_phy_write(dev, 0x0015, (b43legacy_phy_read(dev, 0x0015) & 0x0FFF) | 0xA000); b43legacy_phy_write(dev, 0x0015, (b43legacy_phy_read(dev, 0x0015) & 0x0FFF) | 0xF000); udelay(20); if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC) break; } loop1_done = i; loop1_omitted = loop1_cnt - loop1_done; loop2_done = 0; if (loop1_done >= 8) { b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | 0x0030); for (i = loop1_done - 8; i < 16; i++) { b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) & 0xF0FF) | (i << 8)); b43legacy_phy_write(dev, 0x0015, (b43legacy_phy_read(dev, 0x0015) & 0x0FFF) | 0xA000); b43legacy_phy_write(dev, 0x0015, (b43legacy_phy_read(dev, 0x0015) & 0x0FFF) | 0xF000); udelay(20); if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC) break; } } if (phy->rev != 1) { b43legacy_phy_write(dev, 0x0814, backup_phy[4]); b43legacy_phy_write(dev, 0x0815, backup_phy[5]); } b43legacy_phy_write(dev, 0x005A, backup_phy[6]); b43legacy_phy_write(dev, 0x0059, backup_phy[7]); b43legacy_phy_write(dev, 0x0058, backup_phy[8]); b43legacy_phy_write(dev, 0x000A, backup_phy[9]); b43legacy_phy_write(dev, 0x0003, backup_phy[10]); b43legacy_phy_write(dev, 0x080F, backup_phy[11]); b43legacy_phy_write(dev, 0x0810, backup_phy[12]); b43legacy_phy_write(dev, 0x002B, backup_phy[13]); b43legacy_phy_write(dev, 0x0015, backup_phy[14]); b43legacy_phy_set_baseband_attenuation(dev, backup_bband); b43legacy_radio_write16(dev, 0x0052, backup_radio[0]); b43legacy_radio_write16(dev, 0x0043, backup_radio[1]); b43legacy_radio_write16(dev, 0x007A, backup_radio[2]); b43legacy_phy_write(dev, 0x0811, backup_phy[2] | 0x0003); udelay(10); b43legacy_phy_write(dev, 0x0811, backup_phy[2]); b43legacy_phy_write(dev, 0x0812, backup_phy[3]); b43legacy_phy_write(dev, 0x0429, backup_phy[0]); b43legacy_phy_write(dev, 0x0001, backup_phy[1]); phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11; phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2; } static void b43legacy_phy_initg(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 tmp; if (phy->rev == 1) b43legacy_phy_initb5(dev); else b43legacy_phy_initb6(dev); if (phy->rev >= 2 && phy->gmode) b43legacy_phy_inita(dev); if (phy->rev >= 2) { b43legacy_phy_write(dev, 0x0814, 0x0000); b43legacy_phy_write(dev, 0x0815, 0x0000); } if (phy->rev == 2) { b43legacy_phy_write(dev, 0x0811, 0x0000); b43legacy_phy_write(dev, 0x0015, 0x00C0); } if (phy->rev > 5) { b43legacy_phy_write(dev, 0x0811, 0x0400); b43legacy_phy_write(dev, 0x0015, 0x00C0); } if (phy->gmode) { tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF; if (tmp == 3) { b43legacy_phy_write(dev, 0x04C2, 0x1816); b43legacy_phy_write(dev, 0x04C3, 0x8606); } if (tmp == 4 || tmp == 5) { b43legacy_phy_write(dev, 0x04C2, 0x1816); b43legacy_phy_write(dev, 0x04C3, 0x8006); b43legacy_phy_write(dev, 0x04CC, (b43legacy_phy_read(dev, 0x04CC) & 0x00FF) | 0x1F00); } if (phy->rev >= 2) b43legacy_phy_write(dev, 0x047E, 0x0078); } if (phy->radio_rev == 8) { b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) | 0x0080); b43legacy_phy_write(dev, 0x043E, b43legacy_phy_read(dev, 0x043E) | 0x0004); } if (phy->rev >= 2 && phy->gmode) b43legacy_calc_loopback_gain(dev); if (phy->radio_rev != 8) { if (phy->initval == 0xFFFF) phy->initval = b43legacy_radio_init2050(dev); else b43legacy_radio_write16(dev, 0x0078, phy->initval); } if (phy->txctl2 == 0xFFFF) b43legacy_phy_lo_g_measure(dev); else { if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) b43legacy_radio_write16(dev, 0x0052, (phy->txctl1 << 4) | phy->txctl2); else b43legacy_radio_write16(dev, 0x0052, (b43legacy_radio_read16(dev, 0x0052) & 0xFFF0) | phy->txctl1); if (phy->rev >= 6) b43legacy_phy_write(dev, 0x0036, (b43legacy_phy_read(dev, 0x0036) & 0x0FFF) | (phy->txctl2 << 12)); if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) b43legacy_phy_write(dev, 0x002E, 0x8075); else b43legacy_phy_write(dev, 0x002E, 0x807F); if (phy->rev < 2) b43legacy_phy_write(dev, 0x002F, 0x0101); else b43legacy_phy_write(dev, 0x002F, 0x0202); } if (phy->gmode) { b43legacy_phy_lo_adjust(dev, 0); b43legacy_phy_write(dev, 0x080F, 0x8078); } if (!(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI)) { /* The specs state to update the NRSSI LT with * the value 0x7FFFFFFF here. I think that is some weird * compiler optimization in the original driver. * Essentially, what we do here is resetting all NRSSI LT * entries to -32 (see the clamp_val() in nrssi_hw_update()) */ b43legacy_nrssi_hw_update(dev, 0xFFFF); b43legacy_calc_nrssi_threshold(dev); } else if (phy->gmode || phy->rev >= 2) { if (phy->nrssi[0] == -1000) { B43legacy_WARN_ON(phy->nrssi[1] != -1000); b43legacy_calc_nrssi_slope(dev); } else { B43legacy_WARN_ON(phy->nrssi[1] == -1000); b43legacy_calc_nrssi_threshold(dev); } } if (phy->radio_rev == 8) b43legacy_phy_write(dev, 0x0805, 0x3230); b43legacy_phy_init_pctl(dev); if (dev->dev->bus->chip_id == 0x4306 && dev->dev->bus->chip_package == 2) { b43legacy_phy_write(dev, 0x0429, b43legacy_phy_read(dev, 0x0429) & 0xBFFF); b43legacy_phy_write(dev, 0x04C3, b43legacy_phy_read(dev, 0x04C3) & 0x7FFF); } } static u16 b43legacy_phy_lo_b_r15_loop(struct b43legacy_wldev *dev) { int i; u16 ret = 0; unsigned long flags; local_irq_save(flags); for (i = 0; i < 10; i++) { b43legacy_phy_write(dev, 0x0015, 0xAFA0); udelay(1); b43legacy_phy_write(dev, 0x0015, 0xEFA0); udelay(10); b43legacy_phy_write(dev, 0x0015, 0xFFA0); udelay(40); ret += b43legacy_phy_read(dev, 0x002C); } local_irq_restore(flags); b43legacy_voluntary_preempt(); return ret; } void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 regstack[12] = { 0 }; u16 mls; u16 fval; int i; int j; regstack[0] = b43legacy_phy_read(dev, 0x0015); regstack[1] = b43legacy_radio_read16(dev, 0x0052) & 0xFFF0; if (phy->radio_ver == 0x2053) { regstack[2] = b43legacy_phy_read(dev, 0x000A); regstack[3] = b43legacy_phy_read(dev, 0x002A); regstack[4] = b43legacy_phy_read(dev, 0x0035); regstack[5] = b43legacy_phy_read(dev, 0x0003); regstack[6] = b43legacy_phy_read(dev, 0x0001); regstack[7] = b43legacy_phy_read(dev, 0x0030); regstack[8] = b43legacy_radio_read16(dev, 0x0043); regstack[9] = b43legacy_radio_read16(dev, 0x007A); regstack[10] = b43legacy_read16(dev, 0x03EC); regstack[11] = b43legacy_radio_read16(dev, 0x0052) & 0x00F0; b43legacy_phy_write(dev, 0x0030, 0x00FF); b43legacy_write16(dev, 0x03EC, 0x3F3F); b43legacy_phy_write(dev, 0x0035, regstack[4] & 0xFF7F); b43legacy_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0); } b43legacy_phy_write(dev, 0x0015, 0xB000); b43legacy_phy_write(dev, 0x002B, 0x0004); if (phy->radio_ver == 0x2053) { b43legacy_phy_write(dev, 0x002B, 0x0203); b43legacy_phy_write(dev, 0x002A, 0x08A3); } phy->minlowsig[0] = 0xFFFF; for (i = 0; i < 4; i++) { b43legacy_radio_write16(dev, 0x0052, regstack[1] | i); b43legacy_phy_lo_b_r15_loop(dev); } for (i = 0; i < 10; i++) { b43legacy_radio_write16(dev, 0x0052, regstack[1] | i); mls = b43legacy_phy_lo_b_r15_loop(dev) / 10; if (mls < phy->minlowsig[0]) { phy->minlowsig[0] = mls; phy->minlowsigpos[0] = i; } } b43legacy_radio_write16(dev, 0x0052, regstack[1] | phy->minlowsigpos[0]); phy->minlowsig[1] = 0xFFFF; for (i = -4; i < 5; i += 2) { for (j = -4; j < 5; j += 2) { if (j < 0) fval = (0x0100 * i) + j + 0x0100; else fval = (0x0100 * i) + j; b43legacy_phy_write(dev, 0x002F, fval); mls = b43legacy_phy_lo_b_r15_loop(dev) / 10; if (mls < phy->minlowsig[1]) { phy->minlowsig[1] = mls; phy->minlowsigpos[1] = fval; } } } phy->minlowsigpos[1] += 0x0101; b43legacy_phy_write(dev, 0x002F, phy->minlowsigpos[1]); if (phy->radio_ver == 0x2053) { b43legacy_phy_write(dev, 0x000A, regstack[2]); b43legacy_phy_write(dev, 0x002A, regstack[3]); b43legacy_phy_write(dev, 0x0035, regstack[4]); b43legacy_phy_write(dev, 0x0003, regstack[5]); b43legacy_phy_write(dev, 0x0001, regstack[6]); b43legacy_phy_write(dev, 0x0030, regstack[7]); b43legacy_radio_write16(dev, 0x0043, regstack[8]); b43legacy_radio_write16(dev, 0x007A, regstack[9]); b43legacy_radio_write16(dev, 0x0052, (b43legacy_radio_read16(dev, 0x0052) & 0x000F) | regstack[11]); b43legacy_write16(dev, 0x03EC, regstack[10]); } b43legacy_phy_write(dev, 0x0015, regstack[0]); } static inline u16 b43legacy_phy_lo_g_deviation_subval(struct b43legacy_wldev *dev, u16 control) { struct b43legacy_phy *phy = &dev->phy; u16 ret; unsigned long flags; local_irq_save(flags); if (phy->gmode) { b43legacy_phy_write(dev, 0x15, 0xE300); control <<= 8; b43legacy_phy_write(dev, 0x0812, control | 0x00B0); udelay(5); b43legacy_phy_write(dev, 0x0812, control | 0x00B2); udelay(2); b43legacy_phy_write(dev, 0x0812, control | 0x00B3); udelay(4); b43legacy_phy_write(dev, 0x0015, 0xF300); udelay(8); } else { b43legacy_phy_write(dev, 0x0015, control | 0xEFA0); udelay(2); b43legacy_phy_write(dev, 0x0015, control | 0xEFE0); udelay(4); b43legacy_phy_write(dev, 0x0015, control | 0xFFE0); udelay(8); } ret = b43legacy_phy_read(dev, 0x002D); local_irq_restore(flags); b43legacy_voluntary_preempt(); return ret; } static u32 b43legacy_phy_lo_g_singledeviation(struct b43legacy_wldev *dev, u16 control) { int i; u32 ret = 0; for (i = 0; i < 8; i++) ret += b43legacy_phy_lo_g_deviation_subval(dev, control); return ret; } /* Write the LocalOscillator CONTROL */ static inline void b43legacy_lo_write(struct b43legacy_wldev *dev, struct b43legacy_lopair *pair) { u16 value; value = (u8)(pair->low); value |= ((u8)(pair->high)) << 8; #ifdef CONFIG_B43LEGACY_DEBUG /* Sanity check. */ if (pair->low < -8 || pair->low > 8 || pair->high < -8 || pair->high > 8) { b43legacydbg(dev->wl, "WARNING: Writing invalid LOpair " "(low: %d, high: %d)\n", pair->low, pair->high); dump_stack(); } #endif b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, value); } static inline struct b43legacy_lopair *b43legacy_find_lopair(struct b43legacy_wldev *dev, u16 bbatt, u16 rfatt, u16 tx) { static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 }; struct b43legacy_phy *phy = &dev->phy; if (bbatt > 6) bbatt = 6; B43legacy_WARN_ON(rfatt >= 10); if (tx == 3) return b43legacy_get_lopair(phy, rfatt, bbatt); return b43legacy_get_lopair(phy, dict[rfatt], bbatt); } static inline struct b43legacy_lopair *b43legacy_current_lopair(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; return b43legacy_find_lopair(dev, phy->bbatt, phy->rfatt, phy->txctl1); } /* Adjust B/G LO */ void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed) { struct b43legacy_lopair *pair; if (fixed) { /* Use fixed values. Only for initialization. */ pair = b43legacy_find_lopair(dev, 2, 3, 0); } else pair = b43legacy_current_lopair(dev); b43legacy_lo_write(dev, pair); } static void b43legacy_phy_lo_g_measure_txctl2(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 txctl2 = 0; u16 i; u32 smallest; u32 tmp; b43legacy_radio_write16(dev, 0x0052, 0x0000); udelay(10); smallest = b43legacy_phy_lo_g_singledeviation(dev, 0); for (i = 0; i < 16; i++) { b43legacy_radio_write16(dev, 0x0052, i); udelay(10); tmp = b43legacy_phy_lo_g_singledeviation(dev, 0); if (tmp < smallest) { smallest = tmp; txctl2 = i; } } phy->txctl2 = txctl2; } static void b43legacy_phy_lo_g_state(struct b43legacy_wldev *dev, const struct b43legacy_lopair *in_pair, struct b43legacy_lopair *out_pair, u16 r27) { static const struct b43legacy_lopair transitions[8] = { { .high = 1, .low = 1, }, { .high = 1, .low = 0, }, { .high = 1, .low = -1, }, { .high = 0, .low = -1, }, { .high = -1, .low = -1, }, { .high = -1, .low = 0, }, { .high = -1, .low = 1, }, { .high = 0, .low = 1, }, }; struct b43legacy_lopair lowest_transition = { .high = in_pair->high, .low = in_pair->low, }; struct b43legacy_lopair tmp_pair; struct b43legacy_lopair transition; int i = 12; int state = 0; int found_lower; int j; int begin; int end; u32 lowest_deviation; u32 tmp; /* Note that in_pair and out_pair can point to the same pair. * Be careful. */ b43legacy_lo_write(dev, &lowest_transition); lowest_deviation = b43legacy_phy_lo_g_singledeviation(dev, r27); do { found_lower = 0; B43legacy_WARN_ON(!(state >= 0 && state <= 8)); if (state == 0) { begin = 1; end = 8; } else if (state % 2 == 0) { begin = state - 1; end = state + 1; } else { begin = state - 2; end = state + 2; } if (begin < 1) begin += 8; if (end > 8) end -= 8; j = begin; tmp_pair.high = lowest_transition.high; tmp_pair.low = lowest_transition.low; while (1) { B43legacy_WARN_ON(!(j >= 1 && j <= 8)); transition.high = tmp_pair.high + transitions[j - 1].high; transition.low = tmp_pair.low + transitions[j - 1].low; if ((abs(transition.low) < 9) && (abs(transition.high) < 9)) { b43legacy_lo_write(dev, &transition); tmp = b43legacy_phy_lo_g_singledeviation(dev, r27); if (tmp < lowest_deviation) { lowest_deviation = tmp; state = j; found_lower = 1; lowest_transition.high = transition.high; lowest_transition.low = transition.low; } } if (j == end) break; if (j == 8) j = 1; else j++; } } while (i-- && found_lower); out_pair->high = lowest_transition.high; out_pair->low = lowest_transition.low; } /* Set the baseband attenuation value on chip. */ void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev, u16 bbatt) { struct b43legacy_phy *phy = &dev->phy; u16 value; if (phy->analog == 0) { value = (b43legacy_read16(dev, 0x03E6) & 0xFFF0); value |= (bbatt & 0x000F); b43legacy_write16(dev, 0x03E6, value); return; } if (phy->analog > 1) { value = b43legacy_phy_read(dev, 0x0060) & 0xFFC3; value |= (bbatt << 2) & 0x003C; } else { value = b43legacy_phy_read(dev, 0x0060) & 0xFF87; value |= (bbatt << 3) & 0x0078; } b43legacy_phy_write(dev, 0x0060, value); } /* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */ void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev) { static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 }; const int is_initializing = (b43legacy_status(dev) < B43legacy_STAT_STARTED); struct b43legacy_phy *phy = &dev->phy; u16 h; u16 i; u16 oldi = 0; u16 j; struct b43legacy_lopair control; struct b43legacy_lopair *tmp_control; u16 tmp; u16 regstack[16] = { 0 }; u8 oldchannel; /* XXX: What are these? */ u8 r27 = 0; u16 r31; oldchannel = phy->channel; /* Setup */ if (phy->gmode) { regstack[0] = b43legacy_phy_read(dev, B43legacy_PHY_G_CRS); regstack[1] = b43legacy_phy_read(dev, 0x0802); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0] & 0x7FFF); b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC); } regstack[3] = b43legacy_read16(dev, 0x03E2); b43legacy_write16(dev, 0x03E2, regstack[3] | 0x8000); regstack[4] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); regstack[5] = b43legacy_phy_read(dev, 0x15); regstack[6] = b43legacy_phy_read(dev, 0x2A); regstack[7] = b43legacy_phy_read(dev, 0x35); regstack[8] = b43legacy_phy_read(dev, 0x60); regstack[9] = b43legacy_radio_read16(dev, 0x43); regstack[10] = b43legacy_radio_read16(dev, 0x7A); regstack[11] = b43legacy_radio_read16(dev, 0x52); if (phy->gmode) { regstack[12] = b43legacy_phy_read(dev, 0x0811); regstack[13] = b43legacy_phy_read(dev, 0x0812); regstack[14] = b43legacy_phy_read(dev, 0x0814); regstack[15] = b43legacy_phy_read(dev, 0x0815); } b43legacy_radio_selectchannel(dev, 6, 0); if (phy->gmode) { b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0] & 0x7FFF); b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC); b43legacy_dummy_transmission(dev); } b43legacy_radio_write16(dev, 0x0043, 0x0006); b43legacy_phy_set_baseband_attenuation(dev, 2); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x0000); b43legacy_phy_write(dev, 0x002E, 0x007F); b43legacy_phy_write(dev, 0x080F, 0x0078); b43legacy_phy_write(dev, 0x0035, regstack[7] & ~(1 << 7)); b43legacy_radio_write16(dev, 0x007A, regstack[10] & 0xFFF0); b43legacy_phy_write(dev, 0x002B, 0x0203); b43legacy_phy_write(dev, 0x002A, 0x08A3); if (phy->gmode) { b43legacy_phy_write(dev, 0x0814, regstack[14] | 0x0003); b43legacy_phy_write(dev, 0x0815, regstack[15] & 0xFFFC); b43legacy_phy_write(dev, 0x0811, 0x01B3); b43legacy_phy_write(dev, 0x0812, 0x00B2); } if (is_initializing) b43legacy_phy_lo_g_measure_txctl2(dev); b43legacy_phy_write(dev, 0x080F, 0x8078); /* Measure */ control.low = 0; control.high = 0; for (h = 0; h < 10; h++) { /* Loop over each possible RadioAttenuation (0-9) */ i = pairorder[h]; if (is_initializing) { if (i == 3) { control.low = 0; control.high = 0; } else if (((i % 2 == 1) && (oldi % 2 == 1)) || ((i % 2 == 0) && (oldi % 2 == 0))) { tmp_control = b43legacy_get_lopair(phy, oldi, 0); memcpy(&control, tmp_control, sizeof(control)); } else { tmp_control = b43legacy_get_lopair(phy, 3, 0); memcpy(&control, tmp_control, sizeof(control)); } } /* Loop over each possible BasebandAttenuation/2 */ for (j = 0; j < 4; j++) { if (is_initializing) { tmp = i * 2 + j; r27 = 0; r31 = 0; if (tmp > 14) { r31 = 1; if (tmp > 17) r27 = 1; if (tmp > 19) r27 = 2; } } else { tmp_control = b43legacy_get_lopair(phy, i, j * 2); if (!tmp_control->used) continue; memcpy(&control, tmp_control, sizeof(control)); r27 = 3; r31 = 0; } b43legacy_radio_write16(dev, 0x43, i); b43legacy_radio_write16(dev, 0x52, phy->txctl2); udelay(10); b43legacy_voluntary_preempt(); b43legacy_phy_set_baseband_attenuation(dev, j * 2); tmp = (regstack[10] & 0xFFF0); if (r31) tmp |= 0x0008; b43legacy_radio_write16(dev, 0x007A, tmp); tmp_control = b43legacy_get_lopair(phy, i, j * 2); b43legacy_phy_lo_g_state(dev, &control, tmp_control, r27); } oldi = i; } /* Loop over each possible RadioAttenuation (10-13) */ for (i = 10; i < 14; i++) { /* Loop over each possible BasebandAttenuation/2 */ for (j = 0; j < 4; j++) { if (is_initializing) { tmp_control = b43legacy_get_lopair(phy, i - 9, j * 2); memcpy(&control, tmp_control, sizeof(control)); /* FIXME: The next line is wrong, as the * following if statement can never trigger. */ tmp = (i - 9) * 2 + j - 5; r27 = 0; r31 = 0; if (tmp > 14) { r31 = 1; if (tmp > 17) r27 = 1; if (tmp > 19) r27 = 2; } } else { tmp_control = b43legacy_get_lopair(phy, i - 9, j * 2); if (!tmp_control->used) continue; memcpy(&control, tmp_control, sizeof(control)); r27 = 3; r31 = 0; } b43legacy_radio_write16(dev, 0x43, i - 9); /* FIXME: shouldn't txctl1 be zero in the next line * and 3 in the loop above? */ b43legacy_radio_write16(dev, 0x52, phy->txctl2 | (3/*txctl1*/ << 4)); udelay(10); b43legacy_voluntary_preempt(); b43legacy_phy_set_baseband_attenuation(dev, j * 2); tmp = (regstack[10] & 0xFFF0); if (r31) tmp |= 0x0008; b43legacy_radio_write16(dev, 0x7A, tmp); tmp_control = b43legacy_get_lopair(phy, i, j * 2); b43legacy_phy_lo_g_state(dev, &control, tmp_control, r27); } } /* Restoration */ if (phy->gmode) { b43legacy_phy_write(dev, 0x0015, 0xE300); b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA0); udelay(5); b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA2); udelay(2); b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA3); b43legacy_voluntary_preempt(); } else b43legacy_phy_write(dev, 0x0015, r27 | 0xEFA0); b43legacy_phy_lo_adjust(dev, is_initializing); b43legacy_phy_write(dev, 0x002E, 0x807F); if (phy->gmode) b43legacy_phy_write(dev, 0x002F, 0x0202); else b43legacy_phy_write(dev, 0x002F, 0x0101); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, regstack[4]); b43legacy_phy_write(dev, 0x0015, regstack[5]); b43legacy_phy_write(dev, 0x002A, regstack[6]); b43legacy_phy_write(dev, 0x0035, regstack[7]); b43legacy_phy_write(dev, 0x0060, regstack[8]); b43legacy_radio_write16(dev, 0x0043, regstack[9]); b43legacy_radio_write16(dev, 0x007A, regstack[10]); regstack[11] &= 0x00F0; regstack[11] |= (b43legacy_radio_read16(dev, 0x52) & 0x000F); b43legacy_radio_write16(dev, 0x52, regstack[11]); b43legacy_write16(dev, 0x03E2, regstack[3]); if (phy->gmode) { b43legacy_phy_write(dev, 0x0811, regstack[12]); b43legacy_phy_write(dev, 0x0812, regstack[13]); b43legacy_phy_write(dev, 0x0814, regstack[14]); b43legacy_phy_write(dev, 0x0815, regstack[15]); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]); b43legacy_phy_write(dev, 0x0802, regstack[1]); } b43legacy_radio_selectchannel(dev, oldchannel, 1); #ifdef CONFIG_B43LEGACY_DEBUG { /* Sanity check for all lopairs. */ for (i = 0; i < B43legacy_LO_COUNT; i++) { tmp_control = phy->_lo_pairs + i; if (tmp_control->low < -8 || tmp_control->low > 8 || tmp_control->high < -8 || tmp_control->high > 8) b43legacywarn(dev->wl, "WARNING: Invalid LOpair (low: %d, high:" " %d, index: %d)\n", tmp_control->low, tmp_control->high, i); } } #endif /* CONFIG_B43LEGACY_DEBUG */ } static void b43legacy_phy_lo_mark_current_used(struct b43legacy_wldev *dev) { struct b43legacy_lopair *pair; pair = b43legacy_current_lopair(dev); pair->used = 1; } void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; struct b43legacy_lopair *pair; int i; for (i = 0; i < B43legacy_LO_COUNT; i++) { pair = phy->_lo_pairs + i; pair->used = 0; } } /* http://bcm-specs.sipsolutions.net/EstimatePowerOut * This function converts a TSSI value to dBm in Q5.2 */ static s8 b43legacy_phy_estimate_power_out(struct b43legacy_wldev *dev, s8 tssi) { struct b43legacy_phy *phy = &dev->phy; s8 dbm = 0; s32 tmp; tmp = phy->idle_tssi; tmp += tssi; tmp -= phy->savedpctlreg; switch (phy->type) { case B43legacy_PHYTYPE_B: case B43legacy_PHYTYPE_G: tmp = clamp_val(tmp, 0x00, 0x3F); dbm = phy->tssi2dbm[tmp]; break; default: B43legacy_BUG_ON(1); } return dbm; } /* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */ void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 tmp; u16 txpower; s8 v0; s8 v1; s8 v2; s8 v3; s8 average; int max_pwr; s16 desired_pwr; s16 estimated_pwr; s16 pwr_adjust; s16 radio_att_delta; s16 baseband_att_delta; s16 radio_attenuation; s16 baseband_attenuation; if (phy->savedpctlreg == 0xFFFF) return; if ((dev->dev->bus->boardinfo.type == 0x0416) && is_bcm_board_vendor(dev)) return; #ifdef CONFIG_B43LEGACY_DEBUG if (phy->manual_txpower_control) return; #endif B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B || phy->type == B43legacy_PHYTYPE_G)); tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0058); v0 = (s8)(tmp & 0x00FF); v1 = (s8)((tmp & 0xFF00) >> 8); tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005A); v2 = (s8)(tmp & 0x00FF); v3 = (s8)((tmp & 0xFF00) >> 8); tmp = 0; if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) { tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0070); v0 = (s8)(tmp & 0x00FF); v1 = (s8)((tmp & 0xFF00) >> 8); tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0072); v2 = (s8)(tmp & 0x00FF); v3 = (s8)((tmp & 0xFF00) >> 8); if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) return; v0 = (v0 + 0x20) & 0x3F; v1 = (v1 + 0x20) & 0x3F; v2 = (v2 + 0x20) & 0x3F; v3 = (v3 + 0x20) & 0x3F; tmp = 1; } b43legacy_radio_clear_tssi(dev); average = (v0 + v1 + v2 + v3 + 2) / 4; if (tmp && (b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005E) & 0x8)) average -= 13; estimated_pwr = b43legacy_phy_estimate_power_out(dev, average); max_pwr = dev->dev->bus->sprom.maxpwr_bg; if ((dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) && (phy->type == B43legacy_PHYTYPE_G)) max_pwr -= 0x3; if (unlikely(max_pwr <= 0)) { b43legacywarn(dev->wl, "Invalid max-TX-power value in SPROM." "\n"); max_pwr = 74; /* fake it */ dev->dev->bus->sprom.maxpwr_bg = max_pwr; } /* Use regulatory information to get the maximum power. * In the absence of such data from mac80211, we will use 20 dBm, which * is the value for the EU, US, Canada, and most of the world. * The regulatory maximum is reduced by the antenna gain (from sprom) * and 1.5 dBm (a safety factor??). The result is in Q5.2 format * which accounts for the factor of 4 */ #define REG_MAX_PWR 20 max_pwr = min(REG_MAX_PWR * 4 - dev->dev->bus->sprom.antenna_gain.a0 - 0x6, max_pwr); /* find the desired power in Q5.2 - power_level is in dBm * and limit it - max_pwr is already in Q5.2 */ desired_pwr = clamp_val(phy->power_level << 2, 0, max_pwr); if (b43legacy_debug(dev, B43legacy_DBG_XMITPOWER)) b43legacydbg(dev->wl, "Current TX power output: " Q52_FMT " dBm, Desired TX power output: " Q52_FMT " dBm\n", Q52_ARG(estimated_pwr), Q52_ARG(desired_pwr)); /* Check if we need to adjust the current power. The factor of 2 is * for damping */ pwr_adjust = (desired_pwr - estimated_pwr) / 2; /* RF attenuation delta * The minus sign is because lower attenuation => more power */ radio_att_delta = -(pwr_adjust + 7) >> 3; /* Baseband attenuation delta */ baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta); /* Do we need to adjust anything? */ if ((radio_att_delta == 0) && (baseband_att_delta == 0)) { b43legacy_phy_lo_mark_current_used(dev); return; } /* Calculate the new attenuation values. */ baseband_attenuation = phy->bbatt; baseband_attenuation += baseband_att_delta; radio_attenuation = phy->rfatt; radio_attenuation += radio_att_delta; /* Get baseband and radio attenuation values into permitted ranges. * baseband 0-11, radio 0-9. * Radio attenuation affects power level 4 times as much as baseband. */ if (radio_attenuation < 0) { baseband_attenuation -= (4 * -radio_attenuation); radio_attenuation = 0; } else if (radio_attenuation > 9) { baseband_attenuation += (4 * (radio_attenuation - 9)); radio_attenuation = 9; } else { while (baseband_attenuation < 0 && radio_attenuation > 0) { baseband_attenuation += 4; radio_attenuation--; } while (baseband_attenuation > 11 && radio_attenuation < 9) { baseband_attenuation -= 4; radio_attenuation++; } } baseband_attenuation = clamp_val(baseband_attenuation, 0, 11); txpower = phy->txctl1; if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) { if (radio_attenuation <= 1) { if (txpower == 0) { txpower = 3; radio_attenuation += 2; baseband_attenuation += 2; } else if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) { baseband_attenuation += 4 * (radio_attenuation - 2); radio_attenuation = 2; } } else if (radio_attenuation > 4 && txpower != 0) { txpower = 0; if (baseband_attenuation < 3) { radio_attenuation -= 3; baseband_attenuation += 2; } else { radio_attenuation -= 2; baseband_attenuation -= 2; } } } /* Save the control values */ phy->txctl1 = txpower; baseband_attenuation = clamp_val(baseband_attenuation, 0, 11); radio_attenuation = clamp_val(radio_attenuation, 0, 9); phy->rfatt = radio_attenuation; phy->bbatt = baseband_attenuation; /* Adjust the hardware */ b43legacy_phy_lock(dev); b43legacy_radio_lock(dev); b43legacy_radio_set_txpower_bg(dev, baseband_attenuation, radio_attenuation, txpower); b43legacy_phy_lo_mark_current_used(dev); b43legacy_radio_unlock(dev); b43legacy_phy_unlock(dev); } static inline s32 b43legacy_tssi2dbm_ad(s32 num, s32 den) { if (num < 0) return num/den; else return (num+den/2)/den; } static inline s8 b43legacy_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2) { s32 m1; s32 m2; s32 f = 256; s32 q; s32 delta; s8 i = 0; m1 = b43legacy_tssi2dbm_ad(16 * pab0 + index * pab1, 32); m2 = max(b43legacy_tssi2dbm_ad(32768 + index * pab2, 256), 1); do { if (i > 15) return -EINVAL; q = b43legacy_tssi2dbm_ad(f * 4096 - b43legacy_tssi2dbm_ad(m2 * f, 16) * f, 2048); delta = abs(q - f); f = q; i++; } while (delta >= 2); entry[index] = clamp_val(b43legacy_tssi2dbm_ad(m1 * f, 8192), -127, 128); return 0; } /* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */ int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; s16 pab0; s16 pab1; s16 pab2; u8 idx; s8 *dyn_tssi2dbm; B43legacy_WARN_ON(!(phy->type == B43legacy_PHYTYPE_B || phy->type == B43legacy_PHYTYPE_G)); pab0 = (s16)(dev->dev->bus->sprom.pa0b0); pab1 = (s16)(dev->dev->bus->sprom.pa0b1); pab2 = (s16)(dev->dev->bus->sprom.pa0b2); if ((dev->dev->bus->chip_id == 0x4301) && (phy->radio_ver != 0x2050)) { phy->idle_tssi = 0x34; phy->tssi2dbm = b43legacy_tssi2dbm_b_table; return 0; } if (pab0 != 0 && pab1 != 0 && pab2 != 0 && pab0 != -1 && pab1 != -1 && pab2 != -1) { /* The pabX values are set in SPROM. Use them. */ if ((s8)dev->dev->bus->sprom.itssi_bg != 0 && (s8)dev->dev->bus->sprom.itssi_bg != -1) phy->idle_tssi = (s8)(dev->dev->bus->sprom. itssi_bg); else phy->idle_tssi = 62; dyn_tssi2dbm = kmalloc(64, GFP_KERNEL); if (dyn_tssi2dbm == NULL) { b43legacyerr(dev->wl, "Could not allocate memory " "for tssi2dbm table\n"); return -ENOMEM; } for (idx = 0; idx < 64; idx++) if (b43legacy_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) { phy->tssi2dbm = NULL; b43legacyerr(dev->wl, "Could not generate " "tssi2dBm table\n"); kfree(dyn_tssi2dbm); return -ENODEV; } phy->tssi2dbm = dyn_tssi2dbm; phy->dyn_tssi_tbl = 1; } else { /* pabX values not set in SPROM. */ switch (phy->type) { case B43legacy_PHYTYPE_B: phy->idle_tssi = 0x34; phy->tssi2dbm = b43legacy_tssi2dbm_b_table; break; case B43legacy_PHYTYPE_G: phy->idle_tssi = 0x34; phy->tssi2dbm = b43legacy_tssi2dbm_g_table; break; } } return 0; } int b43legacy_phy_init(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; int err = -ENODEV; switch (phy->type) { case B43legacy_PHYTYPE_B: switch (phy->rev) { case 2: b43legacy_phy_initb2(dev); err = 0; break; case 4: b43legacy_phy_initb4(dev); err = 0; break; case 5: b43legacy_phy_initb5(dev); err = 0; break; case 6: b43legacy_phy_initb6(dev); err = 0; break; } break; case B43legacy_PHYTYPE_G: b43legacy_phy_initg(dev); err = 0; break; } if (err) b43legacyerr(dev->wl, "Unknown PHYTYPE found\n"); return err; } void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev) { struct b43legacy_phy *phy = &dev->phy; u16 antennadiv; u16 offset; u16 value; u32 ucodeflags; antennadiv = phy->antenna_diversity; if (antennadiv == 0xFFFF) antennadiv = 3; B43legacy_WARN_ON(antennadiv > 3); ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET); b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET, ucodeflags & ~B43legacy_UCODEFLAG_AUTODIV); switch (phy->type) { case B43legacy_PHYTYPE_G: offset = 0x0400; if (antennadiv == 2) value = (3/*automatic*/ << 7); else value = (antennadiv << 7); b43legacy_phy_write(dev, offset + 1, (b43legacy_phy_read(dev, offset + 1) & 0x7E7F) | value); if (antennadiv >= 2) { if (antennadiv == 2) value = (antennadiv << 7); else value = (0/*force0*/ << 7); b43legacy_phy_write(dev, offset + 0x2B, (b43legacy_phy_read(dev, offset + 0x2B) & 0xFEFF) | value); } if (phy->type == B43legacy_PHYTYPE_G) { if (antennadiv >= 2) b43legacy_phy_write(dev, 0x048C, b43legacy_phy_read(dev, 0x048C) | 0x2000); else b43legacy_phy_write(dev, 0x048C, b43legacy_phy_read(dev, 0x048C) & ~0x2000); if (phy->rev >= 2) { b43legacy_phy_write(dev, 0x0461, b43legacy_phy_read(dev, 0x0461) | 0x0010); b43legacy_phy_write(dev, 0x04AD, (b43legacy_phy_read(dev, 0x04AD) & 0x00FF) | 0x0015); if (phy->rev == 2) b43legacy_phy_write(dev, 0x0427, 0x0008); else b43legacy_phy_write(dev, 0x0427, (b43legacy_phy_read(dev, 0x0427) & 0x00FF) | 0x0008); } else if (phy->rev >= 6) b43legacy_phy_write(dev, 0x049B, 0x00DC); } else { if (phy->rev < 3) b43legacy_phy_write(dev, 0x002B, (b43legacy_phy_read(dev, 0x002B) & 0x00FF) | 0x0024); else { b43legacy_phy_write(dev, 0x0061, b43legacy_phy_read(dev, 0x0061) | 0x0010); if (phy->rev == 3) { b43legacy_phy_write(dev, 0x0093, 0x001D); b43legacy_phy_write(dev, 0x0027, 0x0008); } else { b43legacy_phy_write(dev, 0x0093, 0x003A); b43legacy_phy_write(dev, 0x0027, (b43legacy_phy_read(dev, 0x0027) & 0x00FF) | 0x0008); } } } break; case B43legacy_PHYTYPE_B: if (dev->dev->id.revision == 2) value = (3/*automatic*/ << 7); else value = (antennadiv << 7); b43legacy_phy_write(dev, 0x03E2, (b43legacy_phy_read(dev, 0x03E2) & 0xFE7F) | value); break; default: B43legacy_WARN_ON(1); } if (antennadiv >= 2) { ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET); b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, B43legacy_UCODEFLAGS_OFFSET, ucodeflags | B43legacy_UCODEFLAG_AUTODIV); } phy->antenna_diversity = antennadiv; } /* Set the PowerSavingControlBits. * Bitvalues: * 0 => unset the bit * 1 => set the bit * -1 => calculate the bit */ void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev, int bit25, int bit26) { int i; u32 status; /* FIXME: Force 25 to off and 26 to on for now: */ bit25 = 0; bit26 = 1; if (bit25 == -1) { /* TODO: If powersave is not off and FIXME is not set and we * are not in adhoc and thus is not an AP and we arei * associated, set bit 25 */ } if (bit26 == -1) { /* TODO: If the device is awake or this is an AP, or we are * scanning, or FIXME, or we are associated, or FIXME, * or the latest PS-Poll packet sent was successful, * set bit26 */ } status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); if (bit25) status |= B43legacy_MACCTL_HWPS; else status &= ~B43legacy_MACCTL_HWPS; if (bit26) status |= B43legacy_MACCTL_AWAKE; else status &= ~B43legacy_MACCTL_AWAKE; b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); if (bit26 && dev->dev->id.revision >= 5) { for (i = 0; i < 100; i++) { if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0x0040) != 4) break; udelay(10); } } } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/main.h0000644000175000017500000000712712026211315023342 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver Copyright (c) 2005 Martin Langer , Copyright (c) 2005 Stefano Brivio Copyright (c) 2005, 2006 Michael Buesch Copyright (c) 2005 Danny van Dyk Copyright (c) 2005 Andreas Jaggi Copyright (c) 2007 Larry Finger Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef B43legacy_MAIN_H_ #define B43legacy_MAIN_H_ #include "b43legacy.h" #define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] #define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) /* Magic helper macro to pad structures. Ignore those above. It's magic. */ #define PAD_BYTES(nr_bytes) P4D_BYTES(__LINE__ , (nr_bytes)) /* Lightweight function to convert a frequency (in Mhz) to a channel number. */ static inline u8 b43legacy_freq_to_channel_bg(int freq) { u8 channel; if (freq == 2484) channel = 14; else channel = (freq - 2407) / 5; return channel; } static inline u8 b43legacy_freq_to_channel(struct b43legacy_wldev *dev, int freq) { return b43legacy_freq_to_channel_bg(freq); } /* Lightweight function to convert a channel number to a frequency (in Mhz). */ static inline int b43legacy_channel_to_freq_bg(u8 channel) { int freq; if (channel == 14) freq = 2484; else freq = 2407 + (5 * channel); return freq; } static inline int b43legacy_channel_to_freq(struct b43legacy_wldev *dev, u8 channel) { return b43legacy_channel_to_freq_bg(channel); } static inline int b43legacy_is_cck_rate(int rate) { return (rate == B43legacy_CCK_RATE_1MB || rate == B43legacy_CCK_RATE_2MB || rate == B43legacy_CCK_RATE_5MB || rate == B43legacy_CCK_RATE_11MB); } static inline int b43legacy_is_ofdm_rate(int rate) { return !b43legacy_is_cck_rate(rate); } void b43legacy_tsf_read(struct b43legacy_wldev *dev, u64 *tsf); void b43legacy_tsf_write(struct b43legacy_wldev *dev, u64 tsf); u32 b43legacy_shm_read32(struct b43legacy_wldev *dev, u16 routing, u16 offset); u16 b43legacy_shm_read16(struct b43legacy_wldev *dev, u16 routing, u16 offset); void b43legacy_shm_write32(struct b43legacy_wldev *dev, u16 routing, u16 offset, u32 value); void b43legacy_shm_write16(struct b43legacy_wldev *dev, u16 routing, u16 offset, u16 value); u32 b43legacy_hf_read(struct b43legacy_wldev *dev); void b43legacy_hf_write(struct b43legacy_wldev *dev, u32 value); void b43legacy_dummy_transmission(struct b43legacy_wldev *dev); void b43legacy_wireless_core_reset(struct b43legacy_wldev *dev, u32 flags); void b43legacy_mac_suspend(struct b43legacy_wldev *dev); void b43legacy_mac_enable(struct b43legacy_wldev *dev); void b43legacy_controller_restart(struct b43legacy_wldev *dev, const char *reason); #endif /* B43legacy_MAIN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/leds.h0000644000175000017500000000256012026211315023341 0ustar mcgrofmcgrof#ifndef B43legacy_LEDS_H_ #define B43legacy_LEDS_H_ struct b43legacy_wldev; #ifdef CONFIG_B43LEGACY_LEDS #include #include #define B43legacy_LED_MAX_NAME_LEN 31 struct b43legacy_led { struct b43legacy_wldev *dev; /* The LED class device */ struct led_classdev led_dev; /* The index number of the LED. */ u8 index; /* If activelow is true, the LED is ON if the * bit is switched off. */ bool activelow; /* The unique name string for this LED device. */ char name[B43legacy_LED_MAX_NAME_LEN + 1]; }; #define B43legacy_LED_BEHAVIOUR 0x7F #define B43legacy_LED_ACTIVELOW 0x80 /* LED behaviour values */ enum b43legacy_led_behaviour { B43legacy_LED_OFF, B43legacy_LED_ON, B43legacy_LED_ACTIVITY, B43legacy_LED_RADIO_ALL, B43legacy_LED_RADIO_A, B43legacy_LED_RADIO_B, B43legacy_LED_MODE_BG, B43legacy_LED_TRANSFER, B43legacy_LED_APTRANSFER, B43legacy_LED_WEIRD, B43legacy_LED_ASSOC, B43legacy_LED_INACTIVE, }; void b43legacy_leds_init(struct b43legacy_wldev *dev); void b43legacy_leds_exit(struct b43legacy_wldev *dev); #else /* CONFIG_B43LEGACY_LEDS */ /* LED support disabled */ struct b43legacy_led { /* empty */ }; static inline void b43legacy_leds_init(struct b43legacy_wldev *dev) { } static inline void b43legacy_leds_exit(struct b43legacy_wldev *dev) { } #endif /* CONFIG_B43LEGACY_LEDS */ #endif /* B43legacy_LEDS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/leds.c0000644000175000017500000001564212026211315023341 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver LED control Copyright (c) 2005 Martin Langer , Copyright (c) 2005 Stefano Brivio Copyright (c) 2005-2007 Michael Buesch Copyright (c) 2005 Danny van Dyk Copyright (c) 2005 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43legacy.h" #include "leds.h" #include "rfkill.h" static void b43legacy_led_turn_on(struct b43legacy_wldev *dev, u8 led_index, bool activelow) { struct b43legacy_wl *wl = dev->wl; unsigned long flags; u16 ctl; spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL); if (activelow) ctl &= ~(1 << led_index); else ctl |= (1 << led_index); b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl); spin_unlock_irqrestore(&wl->leds_lock, flags); } static void b43legacy_led_turn_off(struct b43legacy_wldev *dev, u8 led_index, bool activelow) { struct b43legacy_wl *wl = dev->wl; unsigned long flags; u16 ctl; spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL); if (activelow) ctl |= (1 << led_index); else ctl &= ~(1 << led_index); b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl); spin_unlock_irqrestore(&wl->leds_lock, flags); } /* Callback from the LED subsystem. */ static void b43legacy_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct b43legacy_led *led = container_of(led_dev, struct b43legacy_led, led_dev); struct b43legacy_wldev *dev = led->dev; bool radio_enabled; /* Checking the radio-enabled status here is slightly racy, * but we want to avoid the locking overhead and we don't care * whether the LED has the wrong state for a second. */ radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); if (brightness == LED_OFF || !radio_enabled) b43legacy_led_turn_off(dev, led->index, led->activelow); else b43legacy_led_turn_on(dev, led->index, led->activelow); } static int b43legacy_register_led(struct b43legacy_wldev *dev, struct b43legacy_led *led, const char *name, const char *default_trigger, u8 led_index, bool activelow) { int err; b43legacy_led_turn_off(dev, led_index, activelow); if (led->dev) return -EEXIST; if (!default_trigger) return -EINVAL; led->dev = dev; led->index = led_index; led->activelow = activelow; strncpy(led->name, name, sizeof(led->name)); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; led->led_dev.brightness_set = b43legacy_led_brightness_set; err = led_classdev_register(dev->dev->dev, &led->led_dev); if (err) { b43legacywarn(dev->wl, "LEDs: Failed to register %s\n", name); led->dev = NULL; return err; } return 0; } static void b43legacy_unregister_led(struct b43legacy_led *led) { if (!led->dev) return; led_classdev_unregister(&led->led_dev); b43legacy_led_turn_off(led->dev, led->index, led->activelow); led->dev = NULL; } static void b43legacy_map_led(struct b43legacy_wldev *dev, u8 led_index, enum b43legacy_led_behaviour behaviour, bool activelow) { struct ieee80211_hw *hw = dev->wl->hw; char name[B43legacy_LED_MAX_NAME_LEN + 1]; /* Map the b43 specific LED behaviour value to the * generic LED triggers. */ switch (behaviour) { case B43legacy_LED_INACTIVE: break; case B43legacy_LED_OFF: b43legacy_led_turn_off(dev, led_index, activelow); break; case B43legacy_LED_ON: b43legacy_led_turn_on(dev, led_index, activelow); break; case B43legacy_LED_ACTIVITY: case B43legacy_LED_TRANSFER: case B43legacy_LED_APTRANSFER: snprintf(name, sizeof(name), "b43legacy-%s::tx", wiphy_name(hw->wiphy)); b43legacy_register_led(dev, &dev->led_tx, name, ieee80211_get_tx_led_name(hw), led_index, activelow); snprintf(name, sizeof(name), "b43legacy-%s::rx", wiphy_name(hw->wiphy)); b43legacy_register_led(dev, &dev->led_rx, name, ieee80211_get_rx_led_name(hw), led_index, activelow); break; case B43legacy_LED_RADIO_ALL: case B43legacy_LED_RADIO_A: case B43legacy_LED_RADIO_B: case B43legacy_LED_MODE_BG: snprintf(name, sizeof(name), "b43legacy-%s::radio", wiphy_name(hw->wiphy)); b43legacy_register_led(dev, &dev->led_radio, name, ieee80211_get_radio_led_name(hw), led_index, activelow); /* Sync the RF-kill LED state with radio and switch states. */ if (dev->phy.radio_on && b43legacy_is_hw_radio_enabled(dev)) b43legacy_led_turn_on(dev, led_index, activelow); break; case B43legacy_LED_WEIRD: case B43legacy_LED_ASSOC: snprintf(name, sizeof(name), "b43legacy-%s::assoc", wiphy_name(hw->wiphy)); b43legacy_register_led(dev, &dev->led_assoc, name, ieee80211_get_assoc_led_name(hw), led_index, activelow); break; default: b43legacywarn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n", behaviour); break; } } void b43legacy_leds_init(struct b43legacy_wldev *dev) { struct ssb_bus *bus = dev->dev->bus; u8 sprom[4]; int i; enum b43legacy_led_behaviour behaviour; bool activelow; sprom[0] = bus->sprom.gpio0; sprom[1] = bus->sprom.gpio1; sprom[2] = bus->sprom.gpio2; sprom[3] = bus->sprom.gpio3; for (i = 0; i < 4; i++) { if (sprom[i] == 0xFF) { /* There is no LED information in the SPROM * for this LED. Hardcode it here. */ activelow = false; switch (i) { case 0: behaviour = B43legacy_LED_ACTIVITY; activelow = true; if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) behaviour = B43legacy_LED_RADIO_ALL; break; case 1: behaviour = B43legacy_LED_RADIO_B; if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) behaviour = B43legacy_LED_ASSOC; break; case 2: behaviour = B43legacy_LED_RADIO_A; break; case 3: behaviour = B43legacy_LED_OFF; break; default: B43legacy_WARN_ON(1); return; } } else { behaviour = sprom[i] & B43legacy_LED_BEHAVIOUR; activelow = !!(sprom[i] & B43legacy_LED_ACTIVELOW); } b43legacy_map_led(dev, i, behaviour, activelow); } } void b43legacy_leds_exit(struct b43legacy_wldev *dev) { b43legacy_unregister_led(&dev->led_tx); b43legacy_unregister_led(&dev->led_rx); b43legacy_unregister_led(&dev->led_assoc); b43legacy_unregister_led(&dev->led_radio); } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/ilt.h0000644000175000017500000000311412026211315023176 0ustar mcgrofmcgrof#ifndef B43legacy_ILT_H_ #define B43legacy_ILT_H_ #define B43legacy_ILT_ROTOR_SIZE 53 extern const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE]; #define B43legacy_ILT_RETARD_SIZE 53 extern const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE]; #define B43legacy_ILT_FINEFREQA_SIZE 256 extern const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE]; #define B43legacy_ILT_FINEFREQG_SIZE 256 extern const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE]; #define B43legacy_ILT_NOISEA2_SIZE 8 extern const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE]; #define B43legacy_ILT_NOISEA3_SIZE 8 extern const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE]; #define B43legacy_ILT_NOISEG1_SIZE 8 extern const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE]; #define B43legacy_ILT_NOISEG2_SIZE 8 extern const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE]; #define B43legacy_ILT_NOISESCALEG_SIZE 27 extern const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE]; extern const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE]; extern const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE]; #define B43legacy_ILT_SIGMASQR_SIZE 53 extern const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE]; extern const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE]; void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val); void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset, u32 val); u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset); #endif /* B43legacy_ILT_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/ilt.c0000644000175000017500000002475512026211315023207 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver Copyright (c) 2005 Martin Langer , Stefano Brivio Michael Buesch Danny van Dyk Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43legacy.h" #include "ilt.h" #include "phy.h" /**** Initial Internal Lookup Tables ****/ const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE] = { 0xFEB93FFD, 0xFEC63FFD, /* 0 */ 0xFED23FFD, 0xFEDF3FFD, 0xFEEC3FFE, 0xFEF83FFE, 0xFF053FFE, 0xFF113FFE, 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ 0xFF373FFF, 0xFF443FFF, 0xFF503FFF, 0xFF5D3FFF, 0xFF693FFF, 0xFF763FFF, 0xFF824000, 0xFF8F4000, /* 16 */ 0xFF9B4000, 0xFFA84000, 0xFFB54000, 0xFFC14000, 0xFFCE4000, 0xFFDA4000, 0xFFE74000, 0xFFF34000, /* 24 */ 0x00004000, 0x000D4000, 0x00194000, 0x00264000, 0x00324000, 0x003F4000, 0x004B4000, 0x00584000, /* 32 */ 0x00654000, 0x00714000, 0x007E4000, 0x008A3FFF, 0x00973FFF, 0x00A33FFF, 0x00B03FFF, 0x00BC3FFF, /* 40 */ 0x00C93FFF, 0x00D63FFF, 0x00E23FFE, 0x00EF3FFE, 0x00FB3FFE, 0x01083FFE, 0x01143FFE, 0x01213FFD, /* 48 */ 0x012E3FFD, 0x013A3FFD, 0x01473FFD, }; const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE] = { 0xDB93CB87, 0xD666CF64, /* 0 */ 0xD1FDD358, 0xCDA6D826, 0xCA38DD9F, 0xC729E2B4, 0xC469E88E, 0xC26AEE2B, 0xC0DEF46C, 0xC073FA62, /* 8 */ 0xC01D00D5, 0xC0760743, 0xC1560D1E, 0xC2E51369, 0xC4ED18FF, 0xC7AC1ED7, 0xCB2823B2, 0xCEFA28D9, /* 16 */ 0xD2F62D3F, 0xD7BB3197, 0xDCE53568, 0xE1FE3875, 0xE7D13B35, 0xED663D35, 0xF39B3EC4, 0xF98E3FA7, /* 24 */ 0x00004000, 0x06723FA7, 0x0C653EC4, 0x129A3D35, 0x182F3B35, 0x1E023875, 0x231B3568, 0x28453197, /* 32 */ 0x2D0A2D3F, 0x310628D9, 0x34D823B2, 0x38541ED7, 0x3B1318FF, 0x3D1B1369, 0x3EAA0D1E, 0x3F8A0743, /* 40 */ 0x3FE300D5, 0x3F8DFA62, 0x3F22F46C, 0x3D96EE2B, 0x3B97E88E, 0x38D7E2B4, 0x35C8DD9F, 0x325AD826, /* 48 */ 0x2E03D358, 0x299ACF64, 0x246DCB87, }; const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE] = { 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ 0x0202, 0x0282, 0x0302, 0x0382, 0x0402, 0x0482, 0x0502, 0x0582, 0x05E2, 0x0662, 0x06E2, 0x0762, 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ 0x09C2, 0x0A22, 0x0AA2, 0x0B02, 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, 0x0D42, 0x0DA2, 0x0E02, 0x0E62, 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ 0x1062, 0x10C2, 0x1122, 0x1182, 0x11E2, 0x1242, 0x12A2, 0x12E2, 0x1342, 0x13A2, 0x1402, 0x1442, 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ 0x15E2, 0x1622, 0x1662, 0x16C1, 0x1701, 0x1741, 0x1781, 0x17E1, 0x1821, 0x1861, 0x18A1, 0x18E1, 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, 0x1B01, 0x1B41, 0x1B81, 0x1BA1, 0x1BE1, 0x1C21, 0x1C41, 0x1C81, 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, 0x1E21, 0x1E61, 0x1E81, 0x1EA1, 0x1EE1, 0x1F01, 0x1F21, 0x1F41, 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ 0x2001, 0x2041, 0x2061, 0x2081, 0x20A1, 0x20C1, 0x20E1, 0x2101, 0x2121, 0x2141, 0x2161, 0x2181, 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ 0x2221, 0x2241, 0x2261, 0x2281, 0x22A1, 0x22C1, 0x22C1, 0x22E1, 0x2301, 0x2321, 0x2341, 0x2361, 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ 0x23E1, 0x23E1, 0x2401, 0x2421, 0x2441, 0x2441, 0x2461, 0x2481, 0x2481, 0x24A1, 0x24C1, 0x24C1, 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ 0x2541, 0x2541, 0x2561, 0x2561, 0x2581, 0x25A1, 0x25A1, 0x25C1, 0x25C1, 0x25E1, 0x2601, 0x2601, 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ 0x2661, 0x2661, 0x2681, 0x2681, 0x26A1, 0x26A1, 0x26C1, 0x26C1, 0x26E1, 0x26E1, 0x2701, 0x2701, 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ 0x2760, 0x2760, 0x2780, 0x2780, 0x2780, 0x27A0, 0x27A0, 0x27C0, 0x27C0, 0x27E0, 0x27E0, 0x27E0, 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ 0x2820, 0x2840, 0x2840, 0x2840, 0x2860, 0x2860, 0x2880, 0x2880, 0x2880, 0x28A0, 0x28A0, 0x28A0, 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ 0x28E0, 0x28E0, 0x2900, 0x2900, 0x2900, 0x2920, 0x2920, 0x2920, 0x2940, 0x2940, 0x2940, 0x2960, 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ 0x2980, 0x2980, 0x29A0, 0x29A0, 0x29A0, 0x29A0, 0x29C0, 0x29C0, 0x29C0, 0x29E0, 0x29E0, 0x29E0, 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ 0x2A00, 0x2A20, 0x2A20, 0x2A20, 0x2A20, 0x2A40, 0x2A40, 0x2A40, 0x2A40, 0x2A60, 0x2A60, 0x2A60, }; const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE] = { 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ 0x05A9, 0x0669, 0x0709, 0x0789, 0x0829, 0x08A9, 0x0929, 0x0989, 0x0A09, 0x0A69, 0x0AC9, 0x0B29, 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ 0x0D09, 0x0D69, 0x0DA9, 0x0E09, 0x0E69, 0x0EA9, 0x0F09, 0x0F49, 0x0FA9, 0x0FE9, 0x1029, 0x1089, 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ 0x11E9, 0x1229, 0x1289, 0x12C9, 0x1309, 0x1349, 0x1389, 0x13C9, 0x1409, 0x1449, 0x14A9, 0x14E9, 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ 0x1629, 0x1669, 0x16A9, 0x16E8, 0x1728, 0x1768, 0x17A8, 0x17E8, 0x1828, 0x1868, 0x18A8, 0x18E8, 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ 0x1E48, 0x1E88, 0x1EC8, 0x1F08, 0x1F48, 0x1F88, 0x1FE8, 0x2028, 0x2068, 0x20A8, 0x2108, 0x2148, 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ 0x22C8, 0x2308, 0x2348, 0x23A8, 0x23E8, 0x2448, 0x24A8, 0x24E8, 0x2548, 0x25A8, 0x2608, 0x2668, 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ 0x2847, 0x28C7, 0x2947, 0x29A7, 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, 0x2CA7, 0x2D67, 0x2E47, 0x2F67, 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ 0x3806, 0x38A6, 0x3946, 0x39E6, 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, 0x3C45, 0x3CA5, 0x3D05, 0x3D85, 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ 0x3F45, 0x3FA5, 0x4005, 0x4045, 0x40A5, 0x40E5, 0x4145, 0x4185, 0x41E5, 0x4225, 0x4265, 0x42C5, 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ 0x4424, 0x4464, 0x44C4, 0x4504, 0x4544, 0x4584, 0x45C4, 0x4604, 0x4644, 0x46A4, 0x46E4, 0x4724, 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ 0x4864, 0x48A4, 0x48E4, 0x4924, 0x4964, 0x49A4, 0x49E4, 0x4A24, 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ 0x5083, 0x50C3, 0x5103, 0x5143, 0x5183, 0x51E2, 0x5222, 0x5262, 0x52A2, 0x52E2, 0x5342, 0x5382, 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ 0x5502, 0x5542, 0x55A2, 0x55E2, 0x5642, 0x5682, 0x56E2, 0x5722, 0x5782, 0x57E1, 0x5841, 0x58A1, 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, 0x5C61, 0x5D01, 0x5D80, 0x5E20, 0x5EE0, 0x5FA0, 0x6080, 0x61C0, }; const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE] = { 0x0001, 0x0001, 0x0001, 0xFFFE, 0xFFFE, 0x3FFF, 0x1000, 0x0393, }; const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE] = { 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, }; const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE] = { 0x013C, 0x01F5, 0x031A, 0x0631, 0x0001, 0x0001, 0x0001, 0x0001, }; const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE] = { 0x5484, 0x3C40, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE] = { 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ 0x2F2D, 0x2A2A, 0x2527, 0x1F21, 0x1A1D, 0x1719, 0x1616, 0x1414, 0x1414, 0x1400, 0x1414, 0x1614, 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ 0x2A27, 0x2F2A, 0x332D, 0x3B35, 0x5140, 0x6C62, 0x0077, }; const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE] = { 0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */ 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, 0x969B, 0x9195, 0x8F8F, 0x8A8A, 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, 0xCBC0, 0xD8D4, 0x00DD, }; const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE] = { 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0x00A4, }; const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE] = { 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ 0x0067, 0x0063, 0x005E, 0x0059, 0x0054, 0x0050, 0x004B, 0x0046, 0x0042, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x0000, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ 0x003D, 0x003D, 0x003D, 0x003D, 0x0042, 0x0046, 0x004B, 0x0050, 0x0054, 0x0059, 0x005E, 0x0063, 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ 0x007A, }; const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE] = { 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ 0x00D6, 0x00D4, 0x00D2, 0x00CF, 0x00CD, 0x00CA, 0x00C7, 0x00C4, 0x00C1, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x0000, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00C1, 0x00C4, 0x00C7, 0x00CA, 0x00CD, 0x00CF, 0x00D2, 0x00D4, 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ 0x00DE, }; /**** Helper functions to access the device Internal Lookup Tables ****/ void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val) { b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); mmiowb(); b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1, val); } void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset, u32 val) { b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); mmiowb(); b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA2, (val & 0xFFFF0000) >> 16); b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1, val & 0x0000FFFF); } u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset) { b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); return b43legacy_phy_read(dev, B43legacy_PHY_ILT_G_DATA1); } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/dma.h0000644000175000017500000001526112026211315023155 0ustar mcgrofmcgrof#ifndef B43legacy_DMA_H_ #define B43legacy_DMA_H_ #include #include #include #include #include #include "b43legacy.h" /* DMA-Interrupt reasons. */ #define B43legacy_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ | (1 << 14) | (1 << 15)) #define B43legacy_DMAIRQ_NONFATALMASK (1 << 13) #define B43legacy_DMAIRQ_RX_DONE (1 << 16) /*** 32-bit DMA Engine. ***/ /* 32-bit DMA controller registers. */ #define B43legacy_DMA32_TXCTL 0x00 #define B43legacy_DMA32_TXENABLE 0x00000001 #define B43legacy_DMA32_TXSUSPEND 0x00000002 #define B43legacy_DMA32_TXLOOPBACK 0x00000004 #define B43legacy_DMA32_TXFLUSH 0x00000010 #define B43legacy_DMA32_TXADDREXT_MASK 0x00030000 #define B43legacy_DMA32_TXADDREXT_SHIFT 16 #define B43legacy_DMA32_TXRING 0x04 #define B43legacy_DMA32_TXINDEX 0x08 #define B43legacy_DMA32_TXSTATUS 0x0C #define B43legacy_DMA32_TXDPTR 0x00000FFF #define B43legacy_DMA32_TXSTATE 0x0000F000 #define B43legacy_DMA32_TXSTAT_DISABLED 0x00000000 #define B43legacy_DMA32_TXSTAT_ACTIVE 0x00001000 #define B43legacy_DMA32_TXSTAT_IDLEWAIT 0x00002000 #define B43legacy_DMA32_TXSTAT_STOPPED 0x00003000 #define B43legacy_DMA32_TXSTAT_SUSP 0x00004000 #define B43legacy_DMA32_TXERROR 0x000F0000 #define B43legacy_DMA32_TXERR_NOERR 0x00000000 #define B43legacy_DMA32_TXERR_PROT 0x00010000 #define B43legacy_DMA32_TXERR_UNDERRUN 0x00020000 #define B43legacy_DMA32_TXERR_BUFREAD 0x00030000 #define B43legacy_DMA32_TXERR_DESCREAD 0x00040000 #define B43legacy_DMA32_TXACTIVE 0xFFF00000 #define B43legacy_DMA32_RXCTL 0x10 #define B43legacy_DMA32_RXENABLE 0x00000001 #define B43legacy_DMA32_RXFROFF_MASK 0x000000FE #define B43legacy_DMA32_RXFROFF_SHIFT 1 #define B43legacy_DMA32_RXDIRECTFIFO 0x00000100 #define B43legacy_DMA32_RXADDREXT_MASK 0x00030000 #define B43legacy_DMA32_RXADDREXT_SHIFT 16 #define B43legacy_DMA32_RXRING 0x14 #define B43legacy_DMA32_RXINDEX 0x18 #define B43legacy_DMA32_RXSTATUS 0x1C #define B43legacy_DMA32_RXDPTR 0x00000FFF #define B43legacy_DMA32_RXSTATE 0x0000F000 #define B43legacy_DMA32_RXSTAT_DISABLED 0x00000000 #define B43legacy_DMA32_RXSTAT_ACTIVE 0x00001000 #define B43legacy_DMA32_RXSTAT_IDLEWAIT 0x00002000 #define B43legacy_DMA32_RXSTAT_STOPPED 0x00003000 #define B43legacy_DMA32_RXERROR 0x000F0000 #define B43legacy_DMA32_RXERR_NOERR 0x00000000 #define B43legacy_DMA32_RXERR_PROT 0x00010000 #define B43legacy_DMA32_RXERR_OVERFLOW 0x00020000 #define B43legacy_DMA32_RXERR_BUFWRITE 0x00030000 #define B43legacy_DMA32_RXERR_DESCREAD 0x00040000 #define B43legacy_DMA32_RXACTIVE 0xFFF00000 /* 32-bit DMA descriptor. */ struct b43legacy_dmadesc32 { __le32 control; __le32 address; } __packed; #define B43legacy_DMA32_DCTL_BYTECNT 0x00001FFF #define B43legacy_DMA32_DCTL_ADDREXT_MASK 0x00030000 #define B43legacy_DMA32_DCTL_ADDREXT_SHIFT 16 #define B43legacy_DMA32_DCTL_DTABLEEND 0x10000000 #define B43legacy_DMA32_DCTL_IRQ 0x20000000 #define B43legacy_DMA32_DCTL_FRAMEEND 0x40000000 #define B43legacy_DMA32_DCTL_FRAMESTART 0x80000000 /* Misc DMA constants */ #define B43legacy_DMA_RINGMEMSIZE PAGE_SIZE #define B43legacy_DMA0_RX_FRAMEOFFSET 30 #define B43legacy_DMA3_RX_FRAMEOFFSET 0 /* DMA engine tuning knobs */ #define B43legacy_TXRING_SLOTS 128 #define B43legacy_RXRING_SLOTS 64 #define B43legacy_DMA0_RX_BUFFERSIZE (2304 + 100) #define B43legacy_DMA3_RX_BUFFERSIZE 16 #ifdef CONFIG_B43LEGACY_DMA struct sk_buff; struct b43legacy_private; struct b43legacy_txstatus; struct b43legacy_dmadesc_meta { /* The kernel DMA-able buffer. */ struct sk_buff *skb; /* DMA base bus-address of the descriptor buffer. */ dma_addr_t dmaaddr; /* ieee80211 TX status. Only used once per 802.11 frag. */ bool is_last_fragment; }; enum b43legacy_dmatype { B43legacy_DMA_30BIT = 30, B43legacy_DMA_32BIT = 32, }; struct b43legacy_dmaring { /* Kernel virtual base address of the ring memory. */ void *descbase; /* Meta data about all descriptors. */ struct b43legacy_dmadesc_meta *meta; /* Cache of TX headers for each slot. * This is to avoid an allocation on each TX. * This is NULL for an RX ring. */ u8 *txhdr_cache; /* (Unadjusted) DMA base bus-address of the ring memory. */ dma_addr_t dmabase; /* Number of descriptor slots in the ring. */ int nr_slots; /* Number of used descriptor slots. */ int used_slots; /* Currently used slot in the ring. */ int current_slot; /* Frameoffset in octets. */ u32 frameoffset; /* Descriptor buffer size. */ u16 rx_buffersize; /* The MMIO base register of the DMA controller. */ u16 mmio_base; /* DMA controller index number (0-5). */ int index; /* Boolean. Is this a TX ring? */ bool tx; /* The type of DMA engine used. */ enum b43legacy_dmatype type; /* Boolean. Is this ring stopped at ieee80211 level? */ bool stopped; /* The QOS priority assigned to this ring. Only used for TX rings. * This is the mac80211 "queue" value. */ u8 queue_prio; struct b43legacy_wldev *dev; #ifdef CONFIG_B43LEGACY_DEBUG /* Maximum number of used slots. */ int max_used_slots; /* Last time we injected a ring overflow. */ unsigned long last_injected_overflow; #endif /* CONFIG_B43LEGACY_DEBUG*/ }; static inline u32 b43legacy_dma_read(struct b43legacy_dmaring *ring, u16 offset) { return b43legacy_read32(ring->dev, ring->mmio_base + offset); } static inline void b43legacy_dma_write(struct b43legacy_dmaring *ring, u16 offset, u32 value) { b43legacy_write32(ring->dev, ring->mmio_base + offset, value); } int b43legacy_dma_init(struct b43legacy_wldev *dev); void b43legacy_dma_free(struct b43legacy_wldev *dev); void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev); void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev); int b43legacy_dma_tx(struct b43legacy_wldev *dev, struct sk_buff *skb); void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status); void b43legacy_dma_rx(struct b43legacy_dmaring *ring); #else /* CONFIG_B43LEGACY_DMA */ static inline int b43legacy_dma_init(struct b43legacy_wldev *dev) { return 0; } static inline void b43legacy_dma_free(struct b43legacy_wldev *dev) { } static inline int b43legacy_dma_tx(struct b43legacy_wldev *dev, struct sk_buff *skb) { return 0; } static inline void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status) { } static inline void b43legacy_dma_rx(struct b43legacy_dmaring *ring) { } static inline void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) { } static inline void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) { } #endif /* CONFIG_B43LEGACY_DMA */ #endif /* B43legacy_DMA_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/dma.c0000644000175000017500000011154212026211315023147 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver DMA ringbuffer and descriptor allocation/management Copyright (c) 2005, 2006 Michael Buesch Some code in this file is derived from the b44.c driver Copyright (C) 2002 David S. Miller Copyright (C) Pekka Pietikainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43legacy.h" #include "dma.h" #include "main.h" #include "debugfs.h" #include "xmit.h" #include #include #include #include #include #include /* 32bit DMA ops. */ static struct b43legacy_dmadesc32 *op32_idx2desc(struct b43legacy_dmaring *ring, int slot, struct b43legacy_dmadesc_meta **meta) { struct b43legacy_dmadesc32 *desc; *meta = &(ring->meta[slot]); desc = ring->descbase; desc = &(desc[slot]); return desc; } static void op32_fill_descriptor(struct b43legacy_dmaring *ring, struct b43legacy_dmadesc32 *desc, dma_addr_t dmaaddr, u16 bufsize, int start, int end, int irq) { struct b43legacy_dmadesc32 *descbase = ring->descbase; int slot; u32 ctl; u32 addr; u32 addrext; slot = (int)(desc - descbase); B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK); addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK) >> SSB_DMA_TRANSLATION_SHIFT; addr |= ring->dev->dma.translation; ctl = (bufsize - ring->frameoffset) & B43legacy_DMA32_DCTL_BYTECNT; if (slot == ring->nr_slots - 1) ctl |= B43legacy_DMA32_DCTL_DTABLEEND; if (start) ctl |= B43legacy_DMA32_DCTL_FRAMESTART; if (end) ctl |= B43legacy_DMA32_DCTL_FRAMEEND; if (irq) ctl |= B43legacy_DMA32_DCTL_IRQ; ctl |= (addrext << B43legacy_DMA32_DCTL_ADDREXT_SHIFT) & B43legacy_DMA32_DCTL_ADDREXT_MASK; desc->control = cpu_to_le32(ctl); desc->address = cpu_to_le32(addr); } static void op32_poke_tx(struct b43legacy_dmaring *ring, int slot) { b43legacy_dma_write(ring, B43legacy_DMA32_TXINDEX, (u32)(slot * sizeof(struct b43legacy_dmadesc32))); } static void op32_tx_suspend(struct b43legacy_dmaring *ring) { b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) | B43legacy_DMA32_TXSUSPEND); } static void op32_tx_resume(struct b43legacy_dmaring *ring) { b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) & ~B43legacy_DMA32_TXSUSPEND); } static int op32_get_current_rxslot(struct b43legacy_dmaring *ring) { u32 val; val = b43legacy_dma_read(ring, B43legacy_DMA32_RXSTATUS); val &= B43legacy_DMA32_RXDPTR; return (val / sizeof(struct b43legacy_dmadesc32)); } static void op32_set_current_rxslot(struct b43legacy_dmaring *ring, int slot) { b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, (u32)(slot * sizeof(struct b43legacy_dmadesc32))); } static inline int free_slots(struct b43legacy_dmaring *ring) { return (ring->nr_slots - ring->used_slots); } static inline int next_slot(struct b43legacy_dmaring *ring, int slot) { B43legacy_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); if (slot == ring->nr_slots - 1) return 0; return slot + 1; } static inline int prev_slot(struct b43legacy_dmaring *ring, int slot) { B43legacy_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); if (slot == 0) return ring->nr_slots - 1; return slot - 1; } #ifdef CONFIG_B43LEGACY_DEBUG static void update_max_used_slots(struct b43legacy_dmaring *ring, int current_used_slots) { if (current_used_slots <= ring->max_used_slots) return; ring->max_used_slots = current_used_slots; if (b43legacy_debug(ring->dev, B43legacy_DBG_DMAVERBOSE)) b43legacydbg(ring->dev->wl, "max_used_slots increased to %d on %s ring %d\n", ring->max_used_slots, ring->tx ? "TX" : "RX", ring->index); } #else static inline void update_max_used_slots(struct b43legacy_dmaring *ring, int current_used_slots) { } #endif /* DEBUG */ /* Request a slot for usage. */ static inline int request_slot(struct b43legacy_dmaring *ring) { int slot; B43legacy_WARN_ON(!ring->tx); B43legacy_WARN_ON(ring->stopped); B43legacy_WARN_ON(free_slots(ring) == 0); slot = next_slot(ring, ring->current_slot); ring->current_slot = slot; ring->used_slots++; update_max_used_slots(ring, ring->used_slots); return slot; } /* Mac80211-queue to b43legacy-ring mapping */ static struct b43legacy_dmaring *priority_to_txring( struct b43legacy_wldev *dev, int queue_priority) { struct b43legacy_dmaring *ring; /*FIXME: For now we always run on TX-ring-1 */ return dev->dma.tx_ring1; /* 0 = highest priority */ switch (queue_priority) { default: B43legacy_WARN_ON(1); /* fallthrough */ case 0: ring = dev->dma.tx_ring3; break; case 1: ring = dev->dma.tx_ring2; break; case 2: ring = dev->dma.tx_ring1; break; case 3: ring = dev->dma.tx_ring0; break; case 4: ring = dev->dma.tx_ring4; break; case 5: ring = dev->dma.tx_ring5; break; } return ring; } /* Bcm4301-ring to mac80211-queue mapping */ static inline int txring_to_priority(struct b43legacy_dmaring *ring) { static const u8 idx_to_prio[] = { 3, 2, 1, 0, 4, 5, }; /*FIXME: have only one queue, for now */ return 0; return idx_to_prio[ring->index]; } static u16 b43legacy_dmacontroller_base(enum b43legacy_dmatype type, int controller_idx) { static const u16 map32[] = { B43legacy_MMIO_DMA32_BASE0, B43legacy_MMIO_DMA32_BASE1, B43legacy_MMIO_DMA32_BASE2, B43legacy_MMIO_DMA32_BASE3, B43legacy_MMIO_DMA32_BASE4, B43legacy_MMIO_DMA32_BASE5, }; B43legacy_WARN_ON(!(controller_idx >= 0 && controller_idx < ARRAY_SIZE(map32))); return map32[controller_idx]; } static inline dma_addr_t map_descbuffer(struct b43legacy_dmaring *ring, unsigned char *buf, size_t len, int tx) { dma_addr_t dmaaddr; if (tx) dmaaddr = dma_map_single(ring->dev->dev->dma_dev, buf, len, DMA_TO_DEVICE); else dmaaddr = dma_map_single(ring->dev->dev->dma_dev, buf, len, DMA_FROM_DEVICE); return dmaaddr; } static inline void unmap_descbuffer(struct b43legacy_dmaring *ring, dma_addr_t addr, size_t len, int tx) { if (tx) dma_unmap_single(ring->dev->dev->dma_dev, addr, len, DMA_TO_DEVICE); else dma_unmap_single(ring->dev->dev->dma_dev, addr, len, DMA_FROM_DEVICE); } static inline void sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring, dma_addr_t addr, size_t len) { B43legacy_WARN_ON(ring->tx); dma_sync_single_for_cpu(ring->dev->dev->dma_dev, addr, len, DMA_FROM_DEVICE); } static inline void sync_descbuffer_for_device(struct b43legacy_dmaring *ring, dma_addr_t addr, size_t len) { B43legacy_WARN_ON(ring->tx); dma_sync_single_for_device(ring->dev->dev->dma_dev, addr, len, DMA_FROM_DEVICE); } static inline void free_descriptor_buffer(struct b43legacy_dmaring *ring, struct b43legacy_dmadesc_meta *meta, int irq_context) { if (meta->skb) { if (irq_context) dev_kfree_skb_irq(meta->skb); else dev_kfree_skb(meta->skb); meta->skb = NULL; } } static int alloc_ringmemory(struct b43legacy_dmaring *ring) { /* GFP flags must match the flags in free_ringmemory()! */ ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, B43legacy_DMA_RINGMEMSIZE, &(ring->dmabase), GFP_KERNEL); if (!ring->descbase) { b43legacyerr(ring->dev->wl, "DMA ringmemory allocation" " failed\n"); return -ENOMEM; } memset(ring->descbase, 0, B43legacy_DMA_RINGMEMSIZE); return 0; } static void free_ringmemory(struct b43legacy_dmaring *ring) { dma_free_coherent(ring->dev->dev->dma_dev, B43legacy_DMA_RINGMEMSIZE, ring->descbase, ring->dmabase); } /* Reset the RX DMA channel */ static int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev, u16 mmio_base, enum b43legacy_dmatype type) { int i; u32 value; u16 offset; might_sleep(); offset = B43legacy_DMA32_RXCTL; b43legacy_write32(dev, mmio_base + offset, 0); for (i = 0; i < 10; i++) { offset = B43legacy_DMA32_RXSTATUS; value = b43legacy_read32(dev, mmio_base + offset); value &= B43legacy_DMA32_RXSTATE; if (value == B43legacy_DMA32_RXSTAT_DISABLED) { i = -1; break; } msleep(1); } if (i != -1) { b43legacyerr(dev->wl, "DMA RX reset timed out\n"); return -ENODEV; } return 0; } /* Reset the RX DMA channel */ static int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev, u16 mmio_base, enum b43legacy_dmatype type) { int i; u32 value; u16 offset; might_sleep(); for (i = 0; i < 10; i++) { offset = B43legacy_DMA32_TXSTATUS; value = b43legacy_read32(dev, mmio_base + offset); value &= B43legacy_DMA32_TXSTATE; if (value == B43legacy_DMA32_TXSTAT_DISABLED || value == B43legacy_DMA32_TXSTAT_IDLEWAIT || value == B43legacy_DMA32_TXSTAT_STOPPED) break; msleep(1); } offset = B43legacy_DMA32_TXCTL; b43legacy_write32(dev, mmio_base + offset, 0); for (i = 0; i < 10; i++) { offset = B43legacy_DMA32_TXSTATUS; value = b43legacy_read32(dev, mmio_base + offset); value &= B43legacy_DMA32_TXSTATE; if (value == B43legacy_DMA32_TXSTAT_DISABLED) { i = -1; break; } msleep(1); } if (i != -1) { b43legacyerr(dev->wl, "DMA TX reset timed out\n"); return -ENODEV; } /* ensure the reset is completed. */ msleep(1); return 0; } /* Check if a DMA mapping address is invalid. */ static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring, dma_addr_t addr, size_t buffersize, bool dma_to_device) { if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) return 1; switch (ring->type) { case B43legacy_DMA_30BIT: if ((u64)addr + buffersize > (1ULL << 30)) goto address_error; break; case B43legacy_DMA_32BIT: if ((u64)addr + buffersize > (1ULL << 32)) goto address_error; break; } /* The address is OK. */ return 0; address_error: /* We can't support this address. Unmap it again. */ unmap_descbuffer(ring, addr, buffersize, dma_to_device); return 1; } static int setup_rx_descbuffer(struct b43legacy_dmaring *ring, struct b43legacy_dmadesc32 *desc, struct b43legacy_dmadesc_meta *meta, gfp_t gfp_flags) { struct b43legacy_rxhdr_fw3 *rxhdr; struct b43legacy_hwtxstatus *txstat; dma_addr_t dmaaddr; struct sk_buff *skb; B43legacy_WARN_ON(ring->tx); skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); if (unlikely(!skb)) return -ENOMEM; dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { /* ugh. try to realloc in zone_dma */ gfp_flags |= GFP_DMA; dev_kfree_skb_any(skb); skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); if (unlikely(!skb)) return -ENOMEM; dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); } if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { dev_kfree_skb_any(skb); return -EIO; } meta->skb = skb; meta->dmaaddr = dmaaddr; op32_fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); rxhdr = (struct b43legacy_rxhdr_fw3 *)(skb->data); rxhdr->frame_len = 0; txstat = (struct b43legacy_hwtxstatus *)(skb->data); txstat->cookie = 0; return 0; } /* Allocate the initial descbuffers. * This is used for an RX ring only. */ static int alloc_initial_descbuffers(struct b43legacy_dmaring *ring) { int i; int err = -ENOMEM; struct b43legacy_dmadesc32 *desc; struct b43legacy_dmadesc_meta *meta; for (i = 0; i < ring->nr_slots; i++) { desc = op32_idx2desc(ring, i, &meta); err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); if (err) { b43legacyerr(ring->dev->wl, "Failed to allocate initial descbuffers\n"); goto err_unwind; } } mb(); /* all descbuffer setup before next line */ ring->used_slots = ring->nr_slots; err = 0; out: return err; err_unwind: for (i--; i >= 0; i--) { desc = op32_idx2desc(ring, i, &meta); unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); dev_kfree_skb(meta->skb); } goto out; } /* Do initial setup of the DMA controller. * Reset the controller, write the ring busaddress * and switch the "enable" bit on. */ static int dmacontroller_setup(struct b43legacy_dmaring *ring) { int err = 0; u32 value; u32 addrext; u32 trans = ring->dev->dma.translation; u32 ringbase = (u32)(ring->dmabase); if (ring->tx) { addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) >> SSB_DMA_TRANSLATION_SHIFT; value = B43legacy_DMA32_TXENABLE; value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT) & B43legacy_DMA32_TXADDREXT_MASK; b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, value); b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, (ringbase & ~SSB_DMA_TRANSLATION_MASK) | trans); } else { err = alloc_initial_descbuffers(ring); if (err) goto out; addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) >> SSB_DMA_TRANSLATION_SHIFT; value = (ring->frameoffset << B43legacy_DMA32_RXFROFF_SHIFT); value |= B43legacy_DMA32_RXENABLE; value |= (addrext << B43legacy_DMA32_RXADDREXT_SHIFT) & B43legacy_DMA32_RXADDREXT_MASK; b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL, value); b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, (ringbase & ~SSB_DMA_TRANSLATION_MASK) | trans); b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 200); } out: return err; } /* Shutdown the DMA controller. */ static void dmacontroller_cleanup(struct b43legacy_dmaring *ring) { if (ring->tx) { b43legacy_dmacontroller_tx_reset(ring->dev, ring->mmio_base, ring->type); b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0); } else { b43legacy_dmacontroller_rx_reset(ring->dev, ring->mmio_base, ring->type); b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0); } } static void free_all_descbuffers(struct b43legacy_dmaring *ring) { struct b43legacy_dmadesc_meta *meta; int i; if (!ring->used_slots) return; for (i = 0; i < ring->nr_slots; i++) { op32_idx2desc(ring, i, &meta); if (!meta->skb) { B43legacy_WARN_ON(!ring->tx); continue; } if (ring->tx) unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); else unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); free_descriptor_buffer(ring, meta, 0); } } static u64 supported_dma_mask(struct b43legacy_wldev *dev) { u32 tmp; u16 mmio_base; mmio_base = b43legacy_dmacontroller_base(0, 0); b43legacy_write32(dev, mmio_base + B43legacy_DMA32_TXCTL, B43legacy_DMA32_TXADDREXT_MASK); tmp = b43legacy_read32(dev, mmio_base + B43legacy_DMA32_TXCTL); if (tmp & B43legacy_DMA32_TXADDREXT_MASK) return DMA_BIT_MASK(32); return DMA_BIT_MASK(30); } static enum b43legacy_dmatype dma_mask_to_engine_type(u64 dmamask) { if (dmamask == DMA_BIT_MASK(30)) return B43legacy_DMA_30BIT; if (dmamask == DMA_BIT_MASK(32)) return B43legacy_DMA_32BIT; B43legacy_WARN_ON(1); return B43legacy_DMA_30BIT; } /* Main initialization function. */ static struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, int controller_index, int for_tx, enum b43legacy_dmatype type) { struct b43legacy_dmaring *ring; int err; int nr_slots; dma_addr_t dma_test; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out; ring->type = type; ring->dev = dev; nr_slots = B43legacy_RXRING_SLOTS; if (for_tx) nr_slots = B43legacy_TXRING_SLOTS; ring->meta = kcalloc(nr_slots, sizeof(struct b43legacy_dmadesc_meta), GFP_KERNEL); if (!ring->meta) goto err_kfree_ring; if (for_tx) { ring->txhdr_cache = kcalloc(nr_slots, sizeof(struct b43legacy_txhdr_fw3), GFP_KERNEL); if (!ring->txhdr_cache) goto err_kfree_meta; /* test for ability to dma to txhdr_cache */ dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, sizeof(struct b43legacy_txhdr_fw3), DMA_TO_DEVICE); if (b43legacy_dma_mapping_error(ring, dma_test, sizeof(struct b43legacy_txhdr_fw3), 1)) { /* ugh realloc */ kfree(ring->txhdr_cache); ring->txhdr_cache = kcalloc(nr_slots, sizeof(struct b43legacy_txhdr_fw3), GFP_KERNEL | GFP_DMA); if (!ring->txhdr_cache) goto err_kfree_meta; dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, sizeof(struct b43legacy_txhdr_fw3), DMA_TO_DEVICE); if (b43legacy_dma_mapping_error(ring, dma_test, sizeof(struct b43legacy_txhdr_fw3), 1)) goto err_kfree_txhdr_cache; } dma_unmap_single(dev->dev->dma_dev, dma_test, sizeof(struct b43legacy_txhdr_fw3), DMA_TO_DEVICE); } ring->nr_slots = nr_slots; ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); ring->index = controller_index; if (for_tx) { ring->tx = true; ring->current_slot = -1; } else { if (ring->index == 0) { ring->rx_buffersize = B43legacy_DMA0_RX_BUFFERSIZE; ring->frameoffset = B43legacy_DMA0_RX_FRAMEOFFSET; } else if (ring->index == 3) { ring->rx_buffersize = B43legacy_DMA3_RX_BUFFERSIZE; ring->frameoffset = B43legacy_DMA3_RX_FRAMEOFFSET; } else B43legacy_WARN_ON(1); } #ifdef CONFIG_B43LEGACY_DEBUG ring->last_injected_overflow = jiffies; #endif err = alloc_ringmemory(ring); if (err) goto err_kfree_txhdr_cache; err = dmacontroller_setup(ring); if (err) goto err_free_ringmemory; out: return ring; err_free_ringmemory: free_ringmemory(ring); err_kfree_txhdr_cache: kfree(ring->txhdr_cache); err_kfree_meta: kfree(ring->meta); err_kfree_ring: kfree(ring); ring = NULL; goto out; } /* Main cleanup function. */ static void b43legacy_destroy_dmaring(struct b43legacy_dmaring *ring) { if (!ring) return; b43legacydbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots:" " %d/%d\n", (unsigned int)(ring->type), ring->mmio_base, (ring->tx) ? "TX" : "RX", ring->max_used_slots, ring->nr_slots); /* Device IRQs are disabled prior entering this function, * so no need to take care of concurrency with rx handler stuff. */ dmacontroller_cleanup(ring); free_all_descbuffers(ring); free_ringmemory(ring); kfree(ring->txhdr_cache); kfree(ring->meta); kfree(ring); } void b43legacy_dma_free(struct b43legacy_wldev *dev) { struct b43legacy_dma *dma; if (b43legacy_using_pio(dev)) return; dma = &dev->dma; b43legacy_destroy_dmaring(dma->rx_ring3); dma->rx_ring3 = NULL; b43legacy_destroy_dmaring(dma->rx_ring0); dma->rx_ring0 = NULL; b43legacy_destroy_dmaring(dma->tx_ring5); dma->tx_ring5 = NULL; b43legacy_destroy_dmaring(dma->tx_ring4); dma->tx_ring4 = NULL; b43legacy_destroy_dmaring(dma->tx_ring3); dma->tx_ring3 = NULL; b43legacy_destroy_dmaring(dma->tx_ring2); dma->tx_ring2 = NULL; b43legacy_destroy_dmaring(dma->tx_ring1); dma->tx_ring1 = NULL; b43legacy_destroy_dmaring(dma->tx_ring0); dma->tx_ring0 = NULL; } static int b43legacy_dma_set_mask(struct b43legacy_wldev *dev, u64 mask) { u64 orig_mask = mask; bool fallback = false; int err; /* Try to set the DMA mask. If it fails, try falling back to a * lower mask, as we can always also support a lower one. */ while (1) { err = dma_set_mask(dev->dev->dma_dev, mask); if (!err) { err = dma_set_coherent_mask(dev->dev->dma_dev, mask); if (!err) break; } if (mask == DMA_BIT_MASK(64)) { mask = DMA_BIT_MASK(32); fallback = true; continue; } if (mask == DMA_BIT_MASK(32)) { mask = DMA_BIT_MASK(30); fallback = true; continue; } b43legacyerr(dev->wl, "The machine/kernel does not support " "the required %u-bit DMA mask\n", (unsigned int)dma_mask_to_engine_type(orig_mask)); return -EOPNOTSUPP; } if (fallback) { b43legacyinfo(dev->wl, "DMA mask fallback from %u-bit to %u-" "bit\n", (unsigned int)dma_mask_to_engine_type(orig_mask), (unsigned int)dma_mask_to_engine_type(mask)); } return 0; } int b43legacy_dma_init(struct b43legacy_wldev *dev) { struct b43legacy_dma *dma = &dev->dma; struct b43legacy_dmaring *ring; int err; u64 dmamask; enum b43legacy_dmatype type; dmamask = supported_dma_mask(dev); type = dma_mask_to_engine_type(dmamask); err = b43legacy_dma_set_mask(dev, dmamask); if (err) { #ifdef CONFIG_B43LEGACY_PIO b43legacywarn(dev->wl, "DMA for this device not supported. " "Falling back to PIO\n"); dev->__using_pio = true; return -EAGAIN; #else b43legacyerr(dev->wl, "DMA for this device not supported and " "no PIO support compiled in\n"); return -EOPNOTSUPP; #endif } dma->translation = ssb_dma_translation(dev->dev); err = -ENOMEM; /* setup TX DMA channels. */ ring = b43legacy_setup_dmaring(dev, 0, 1, type); if (!ring) goto out; dma->tx_ring0 = ring; ring = b43legacy_setup_dmaring(dev, 1, 1, type); if (!ring) goto err_destroy_tx0; dma->tx_ring1 = ring; ring = b43legacy_setup_dmaring(dev, 2, 1, type); if (!ring) goto err_destroy_tx1; dma->tx_ring2 = ring; ring = b43legacy_setup_dmaring(dev, 3, 1, type); if (!ring) goto err_destroy_tx2; dma->tx_ring3 = ring; ring = b43legacy_setup_dmaring(dev, 4, 1, type); if (!ring) goto err_destroy_tx3; dma->tx_ring4 = ring; ring = b43legacy_setup_dmaring(dev, 5, 1, type); if (!ring) goto err_destroy_tx4; dma->tx_ring5 = ring; /* setup RX DMA channels. */ ring = b43legacy_setup_dmaring(dev, 0, 0, type); if (!ring) goto err_destroy_tx5; dma->rx_ring0 = ring; if (dev->dev->id.revision < 5) { ring = b43legacy_setup_dmaring(dev, 3, 0, type); if (!ring) goto err_destroy_rx0; dma->rx_ring3 = ring; } b43legacydbg(dev->wl, "%u-bit DMA initialized\n", (unsigned int)type); err = 0; out: return err; err_destroy_rx0: b43legacy_destroy_dmaring(dma->rx_ring0); dma->rx_ring0 = NULL; err_destroy_tx5: b43legacy_destroy_dmaring(dma->tx_ring5); dma->tx_ring5 = NULL; err_destroy_tx4: b43legacy_destroy_dmaring(dma->tx_ring4); dma->tx_ring4 = NULL; err_destroy_tx3: b43legacy_destroy_dmaring(dma->tx_ring3); dma->tx_ring3 = NULL; err_destroy_tx2: b43legacy_destroy_dmaring(dma->tx_ring2); dma->tx_ring2 = NULL; err_destroy_tx1: b43legacy_destroy_dmaring(dma->tx_ring1); dma->tx_ring1 = NULL; err_destroy_tx0: b43legacy_destroy_dmaring(dma->tx_ring0); dma->tx_ring0 = NULL; goto out; } /* Generate a cookie for the TX header. */ static u16 generate_cookie(struct b43legacy_dmaring *ring, int slot) { u16 cookie = 0x1000; /* Use the upper 4 bits of the cookie as * DMA controller ID and store the slot number * in the lower 12 bits. * Note that the cookie must never be 0, as this * is a special value used in RX path. */ switch (ring->index) { case 0: cookie = 0xA000; break; case 1: cookie = 0xB000; break; case 2: cookie = 0xC000; break; case 3: cookie = 0xD000; break; case 4: cookie = 0xE000; break; case 5: cookie = 0xF000; break; } B43legacy_WARN_ON(!(((u16)slot & 0xF000) == 0x0000)); cookie |= (u16)slot; return cookie; } /* Inspect a cookie and find out to which controller/slot it belongs. */ static struct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev, u16 cookie, int *slot) { struct b43legacy_dma *dma = &dev->dma; struct b43legacy_dmaring *ring = NULL; switch (cookie & 0xF000) { case 0xA000: ring = dma->tx_ring0; break; case 0xB000: ring = dma->tx_ring1; break; case 0xC000: ring = dma->tx_ring2; break; case 0xD000: ring = dma->tx_ring3; break; case 0xE000: ring = dma->tx_ring4; break; case 0xF000: ring = dma->tx_ring5; break; default: B43legacy_WARN_ON(1); } *slot = (cookie & 0x0FFF); B43legacy_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); return ring; } static int dma_tx_fragment(struct b43legacy_dmaring *ring, struct sk_buff **in_skb) { struct sk_buff *skb = *in_skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 *header; int slot, old_top_slot, old_used_slots; int err; struct b43legacy_dmadesc32 *desc; struct b43legacy_dmadesc_meta *meta; struct b43legacy_dmadesc_meta *meta_hdr; struct sk_buff *bounce_skb; #define SLOTS_PER_PACKET 2 B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); old_top_slot = ring->current_slot; old_used_slots = ring->used_slots; /* Get a slot for the header. */ slot = request_slot(ring); desc = op32_idx2desc(ring, slot, &meta_hdr); memset(meta_hdr, 0, sizeof(*meta_hdr)); header = &(ring->txhdr_cache[slot * sizeof( struct b43legacy_txhdr_fw3)]); err = b43legacy_generate_txhdr(ring->dev, header, skb->data, skb->len, info, generate_cookie(ring, slot)); if (unlikely(err)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; return err; } meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, sizeof(struct b43legacy_txhdr_fw3), 1); if (b43legacy_dma_mapping_error(ring, meta_hdr->dmaaddr, sizeof(struct b43legacy_txhdr_fw3), 1)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; return -EIO; } op32_fill_descriptor(ring, desc, meta_hdr->dmaaddr, sizeof(struct b43legacy_txhdr_fw3), 1, 0, 0); /* Get a slot for the payload. */ slot = request_slot(ring); desc = op32_idx2desc(ring, slot, &meta); memset(meta, 0, sizeof(*meta)); meta->skb = skb; meta->is_last_fragment = true; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); /* create a bounce buffer in zone_dma on mapping failure. */ if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { bounce_skb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!bounce_skb) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; err = -ENOMEM; goto out_unmap_hdr; } memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); bounce_skb->dev = skb->dev; skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); info = IEEE80211_SKB_CB(bounce_skb); dev_kfree_skb_any(skb); skb = bounce_skb; *in_skb = bounce_skb; meta->skb = skb; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; err = -EIO; goto out_free_bounce; } } op32_fill_descriptor(ring, desc, meta->dmaaddr, skb->len, 0, 1, 1); wmb(); /* previous stuff MUST be done */ /* Now transfer the whole frame. */ op32_poke_tx(ring, next_slot(ring, slot)); return 0; out_free_bounce: dev_kfree_skb_any(skb); out_unmap_hdr: unmap_descbuffer(ring, meta_hdr->dmaaddr, sizeof(struct b43legacy_txhdr_fw3), 1); return err; } static inline int should_inject_overflow(struct b43legacy_dmaring *ring) { #ifdef CONFIG_B43LEGACY_DEBUG if (unlikely(b43legacy_debug(ring->dev, B43legacy_DBG_DMAOVERFLOW))) { /* Check if we should inject another ringbuffer overflow * to test handling of this situation in the stack. */ unsigned long next_overflow; next_overflow = ring->last_injected_overflow + HZ; if (time_after(jiffies, next_overflow)) { ring->last_injected_overflow = jiffies; b43legacydbg(ring->dev->wl, "Injecting TX ring overflow on " "DMA controller %d\n", ring->index); return 1; } } #endif /* CONFIG_B43LEGACY_DEBUG */ return 0; } int b43legacy_dma_tx(struct b43legacy_wldev *dev, struct sk_buff *skb) { struct b43legacy_dmaring *ring; int err = 0; ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); B43legacy_WARN_ON(!ring->tx); if (unlikely(ring->stopped)) { /* We get here only because of a bug in mac80211. * Because of a race, one packet may be queued after * the queue is stopped, thus we got called when we shouldn't. * For now, just refuse the transmit. */ if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) b43legacyerr(dev->wl, "Packet after queue stopped\n"); return -ENOSPC; } if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { /* If we get here, we have a real error with the queue * full, but queues not stopped. */ b43legacyerr(dev->wl, "DMA queue overflow\n"); return -ENOSPC; } /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing * into the skb data or cb now. */ err = dma_tx_fragment(ring, &skb); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ dev_kfree_skb_any(skb); return 0; } if (unlikely(err)) { b43legacyerr(dev->wl, "DMA tx mapping failure\n"); return err; } if ((free_slots(ring) < SLOTS_PER_PACKET) || should_inject_overflow(ring)) { /* This TX ring is full. */ unsigned int skb_mapping = skb_get_queue_mapping(skb); ieee80211_stop_queue(dev->wl->hw, skb_mapping); dev->wl->tx_queue_stopped[skb_mapping] = 1; ring->stopped = true; if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) b43legacydbg(dev->wl, "Stopped TX ring %d\n", ring->index); } return err; } void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status) { struct b43legacy_dmaring *ring; struct b43legacy_dmadesc_meta *meta; int retry_limit; int slot; int firstused; ring = parse_cookie(dev, status->cookie, &slot); if (unlikely(!ring)) return; B43legacy_WARN_ON(!ring->tx); /* Sanity check: TX packets are processed in-order on one ring. * Check if the slot deduced from the cookie really is the first * used slot. */ firstused = ring->current_slot - ring->used_slots + 1; if (firstused < 0) firstused = ring->nr_slots + firstused; if (unlikely(slot != firstused)) { /* This possibly is a firmware bug and will result in * malfunction, memory leaks and/or stall of DMA functionality. */ b43legacydbg(dev->wl, "Out of order TX status report on DMA " "ring %d. Expected %d, but got %d\n", ring->index, firstused, slot); return; } while (1) { B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); op32_idx2desc(ring, slot, &meta); if (meta->skb) unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); else unmap_descbuffer(ring, meta->dmaaddr, sizeof(struct b43legacy_txhdr_fw3), 1); if (meta->is_last_fragment) { struct ieee80211_tx_info *info; BUG_ON(!meta->skb); info = IEEE80211_SKB_CB(meta->skb); /* preserve the confiured retry limit before clearing the status * The xmit function has overwritten the rc's value with the actual * retry limit done by the hardware */ retry_limit = info->status.rates[0].count; ieee80211_tx_info_clear_status(info); if (status->acked) info->flags |= IEEE80211_TX_STAT_ACK; if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { /* * If the short retries (RTS, not data frame) have exceeded * the limit, the hw will not have tried the selected rate, * but will have used the fallback rate instead. * Don't let the rate control count attempts for the selected * rate in this case, otherwise the statistics will be off. */ info->status.rates[0].count = 0; info->status.rates[1].count = status->frame_count; } else { if (status->frame_count > retry_limit) { info->status.rates[0].count = retry_limit; info->status.rates[1].count = status->frame_count - retry_limit; } else { info->status.rates[0].count = status->frame_count; info->status.rates[1].idx = -1; } } /* Call back to inform the ieee80211 subsystem about the * status of the transmission. * Some fields of txstat are already filled in dma_tx(). */ ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); /* skb is freed by ieee80211_tx_status_irqsafe() */ meta->skb = NULL; } else { /* No need to call free_descriptor_buffer here, as * this is only the txhdr, which is not allocated. */ B43legacy_WARN_ON(meta->skb != NULL); } /* Everything unmapped and free'd. So it's not used anymore. */ ring->used_slots--; if (meta->is_last_fragment) break; slot = next_slot(ring, slot); } dev->stats.last_tx = jiffies; if (ring->stopped) { B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); ring->stopped = false; } if (dev->wl->tx_queue_stopped[ring->queue_prio]) { dev->wl->tx_queue_stopped[ring->queue_prio] = 0; } else { /* If the driver queue is running wake the corresponding * mac80211 queue. */ ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) b43legacydbg(dev->wl, "Woke up TX ring %d\n", ring->index); } /* Add work to the queue. */ ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); } static void dma_rx(struct b43legacy_dmaring *ring, int *slot) { struct b43legacy_dmadesc32 *desc; struct b43legacy_dmadesc_meta *meta; struct b43legacy_rxhdr_fw3 *rxhdr; struct sk_buff *skb; u16 len; int err; dma_addr_t dmaaddr; desc = op32_idx2desc(ring, *slot, &meta); sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); skb = meta->skb; if (ring->index == 3) { /* We received an xmit status. */ struct b43legacy_hwtxstatus *hw = (struct b43legacy_hwtxstatus *)skb->data; int i = 0; while (hw->cookie == 0) { if (i > 100) break; i++; udelay(2); barrier(); } b43legacy_handle_hwtxstatus(ring->dev, hw); /* recycle the descriptor buffer. */ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); return; } rxhdr = (struct b43legacy_rxhdr_fw3 *)skb->data; len = le16_to_cpu(rxhdr->frame_len); if (len == 0) { int i = 0; do { udelay(2); barrier(); len = le16_to_cpu(rxhdr->frame_len); } while (len == 0 && i++ < 5); if (unlikely(len == 0)) { /* recycle the descriptor buffer. */ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); goto drop; } } if (unlikely(len > ring->rx_buffersize)) { /* The data did not fit into one descriptor buffer * and is split over multiple buffers. * This should never happen, as we try to allocate buffers * big enough. So simply ignore this packet. */ int cnt = 0; s32 tmp = len; while (1) { desc = op32_idx2desc(ring, *slot, &meta); /* recycle the descriptor buffer. */ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); *slot = next_slot(ring, *slot); cnt++; tmp -= ring->rx_buffersize; if (tmp <= 0) break; } b43legacyerr(ring->dev->wl, "DMA RX buffer too small " "(len: %u, buffer: %u, nr-dropped: %d)\n", len, ring->rx_buffersize, cnt); goto drop; } dmaaddr = meta->dmaaddr; err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); if (unlikely(err)) { b43legacydbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer()" " failed\n"); sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize); goto drop; } unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); skb_put(skb, len + ring->frameoffset); skb_pull(skb, ring->frameoffset); b43legacy_rx(ring->dev, skb, rxhdr); drop: return; } void b43legacy_dma_rx(struct b43legacy_dmaring *ring) { int slot; int current_slot; int used_slots = 0; B43legacy_WARN_ON(ring->tx); current_slot = op32_get_current_rxslot(ring); B43legacy_WARN_ON(!(current_slot >= 0 && current_slot < ring->nr_slots)); slot = ring->current_slot; for (; slot != current_slot; slot = next_slot(ring, slot)) { dma_rx(ring, &slot); update_max_used_slots(ring, ++used_slots); } op32_set_current_rxslot(ring, slot); ring->current_slot = slot; } static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) { B43legacy_WARN_ON(!ring->tx); op32_tx_suspend(ring); } static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) { B43legacy_WARN_ON(!ring->tx); op32_tx_resume(ring); } void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) { b43legacy_power_saving_ctl_bits(dev, -1, 1); b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring0); b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring1); b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring2); b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring3); b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring4); b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring5); } void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) { b43legacy_dma_tx_resume_ring(dev->dma.tx_ring5); b43legacy_dma_tx_resume_ring(dev->dma.tx_ring4); b43legacy_dma_tx_resume_ring(dev->dma.tx_ring3); b43legacy_dma_tx_resume_ring(dev->dma.tx_ring2); b43legacy_dma_tx_resume_ring(dev->dma.tx_ring1); b43legacy_dma_tx_resume_ring(dev->dma.tx_ring0); b43legacy_power_saving_ctl_bits(dev, -1, -1); } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/debugfs.h0000644000175000017500000000435512026211315024035 0ustar mcgrofmcgrof#ifndef B43legacy_DEBUGFS_H_ #define B43legacy_DEBUGFS_H_ struct b43legacy_wldev; struct b43legacy_txstatus; enum b43legacy_dyndbg { /* Dynamic debugging features */ B43legacy_DBG_XMITPOWER, B43legacy_DBG_DMAOVERFLOW, B43legacy_DBG_DMAVERBOSE, B43legacy_DBG_PWORK_FAST, B43legacy_DBG_PWORK_STOP, __B43legacy_NR_DYNDBG, }; #ifdef CONFIG_B43LEGACY_DEBUG struct dentry; #define B43legacy_NR_LOGGED_TXSTATUS 100 struct b43legacy_txstatus_log { struct b43legacy_txstatus *log; int end; spinlock_t lock; /* lock for debugging */ }; struct b43legacy_dfs_file { struct dentry *dentry; char *buffer; size_t data_len; }; struct b43legacy_dfsentry { struct b43legacy_wldev *dev; struct dentry *subdir; struct b43legacy_dfs_file file_tsf; struct b43legacy_dfs_file file_ucode_regs; struct b43legacy_dfs_file file_shm; struct b43legacy_dfs_file file_txstat; struct b43legacy_dfs_file file_txpower_g; struct b43legacy_dfs_file file_restart; struct b43legacy_dfs_file file_loctls; struct b43legacy_txstatus_log txstatlog; /* Enabled/Disabled list for the dynamic debugging features. */ u32 dyn_debug[__B43legacy_NR_DYNDBG]; /* Dentries for the dynamic debugging entries. */ struct dentry *dyn_debug_dentries[__B43legacy_NR_DYNDBG]; }; int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature); void b43legacy_debugfs_init(void); void b43legacy_debugfs_exit(void); void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev); void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev); void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status); #else /* CONFIG_B43LEGACY_DEBUG*/ static inline int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature) { return 0; } static inline void b43legacy_debugfs_init(void) { } static inline void b43legacy_debugfs_exit(void) { } static inline void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev) { } static inline void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev) { } static inline void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status) { } #endif /* CONFIG_B43LEGACY_DEBUG*/ #endif /* B43legacy_DEBUGFS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/debugfs.c0000644000175000017500000002744212026211315024032 0ustar mcgrofmcgrof/* Broadcom B43legacy wireless driver debugfs driver debugging code Copyright (c) 2005-2007 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "b43legacy.h" #include "main.h" #include "debugfs.h" #include "dma.h" #include "pio.h" #include "xmit.h" /* The root directory. */ static struct dentry *rootdir; struct b43legacy_debugfs_fops { ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize); int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count); struct file_operations fops; /* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */ size_t file_struct_offset; /* Take wl->irq_lock before calling read/write? */ bool take_irqlock; }; static inline struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev, const struct b43legacy_debugfs_fops *dfops) { void *p; p = dev->dfsentry; p += dfops->file_struct_offset; return p; } #define fappend(fmt, x...) \ do { \ if (bufsize - count) \ count += snprintf(buf + count, \ bufsize - count, \ fmt , ##x); \ else \ printk(KERN_ERR "b43legacy: fappend overflow\n"); \ } while (0) /* wl->irq_lock is locked */ static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; u64 tsf; b43legacy_tsf_read(dev, &tsf); fappend("0x%08x%08x\n", (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32), (unsigned int)(tsf & 0xFFFFFFFFULL)); return count; } /* wl->irq_lock is locked */ static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count) { u64 tsf; if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1) return -EINVAL; b43legacy_tsf_write(dev, tsf); return 0; } /* wl->irq_lock is locked */ static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; int i; for (i = 0; i < 64; i++) { fappend("r%d = 0x%04x\n", i, b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i)); } return count; } /* wl->irq_lock is locked */ static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; int i; u16 tmp; __le16 *le16buf = (__le16 *)buf; for (i = 0; i < 0x1000; i++) { if (bufsize < sizeof(tmp)) break; tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i); le16buf[i] = cpu_to_le16(tmp); count += sizeof(tmp); bufsize -= sizeof(tmp); } return count; } static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) { struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog; ssize_t count = 0; unsigned long flags; int i, idx; struct b43legacy_txstatus *stat; spin_lock_irqsave(&log->lock, flags); if (log->end < 0) { fappend("Nothing transmitted, yet\n"); goto out_unlock; } fappend("b43legacy TX status reports:\n\n" "index | cookie | seq | phy_stat | frame_count | " "rts_count | supp_reason | pm_indicated | " "intermediate | for_ampdu | acked\n" "---\n"); i = log->end + 1; idx = 0; while (1) { if (i == B43legacy_NR_LOGGED_TXSTATUS) i = 0; stat = &(log->log[i]); if (stat->cookie) { fappend("%03d | " "0x%04X | 0x%04X | 0x%02X | " "0x%X | 0x%X | " "%u | %u | " "%u | %u | %u\n", idx, stat->cookie, stat->seq, stat->phy_stat, stat->frame_count, stat->rts_count, stat->supp_reason, stat->pm_indicated, stat->intermediate, stat->for_ampdu, stat->acked); idx++; } if (i == log->end) break; i++; } out_unlock: spin_unlock_irqrestore(&log->lock, flags); return count; } /* wl->irq_lock is locked */ static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count) { int err = 0; if (count > 0 && buf[0] == '1') { b43legacy_controller_restart(dev, "manually restarted"); } else err = -EINVAL; return err; } #undef fappend static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct b43legacy_wldev *dev; struct b43legacy_debugfs_fops *dfops; struct b43legacy_dfs_file *dfile; ssize_t uninitialized_var(ret); char *buf; const size_t bufsize = 1024 * 16; /* 16 KiB buffer */ const size_t buforder = get_order(bufsize); int err = 0; if (!count) return 0; dev = file->private_data; if (!dev) return -ENODEV; mutex_lock(&dev->wl->mutex); if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { err = -ENODEV; goto out_unlock; } dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops); if (!dfops->read) { err = -ENOSYS; goto out_unlock; } dfile = fops_to_dfs_file(dev, dfops); if (!dfile->buffer) { buf = (char *)__get_free_pages(GFP_KERNEL, buforder); if (!buf) { err = -ENOMEM; goto out_unlock; } memset(buf, 0, bufsize); if (dfops->take_irqlock) { spin_lock_irq(&dev->wl->irq_lock); ret = dfops->read(dev, buf, bufsize); spin_unlock_irq(&dev->wl->irq_lock); } else ret = dfops->read(dev, buf, bufsize); if (ret <= 0) { free_pages((unsigned long)buf, buforder); err = ret; goto out_unlock; } dfile->data_len = ret; dfile->buffer = buf; } ret = simple_read_from_buffer(userbuf, count, ppos, dfile->buffer, dfile->data_len); if (*ppos >= dfile->data_len) { free_pages((unsigned long)dfile->buffer, buforder); dfile->buffer = NULL; dfile->data_len = 0; } out_unlock: mutex_unlock(&dev->wl->mutex); return err ? err : ret; } static ssize_t b43legacy_debugfs_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct b43legacy_wldev *dev; struct b43legacy_debugfs_fops *dfops; char *buf; int err = 0; if (!count) return 0; if (count > PAGE_SIZE) return -E2BIG; dev = file->private_data; if (!dev) return -ENODEV; mutex_lock(&dev->wl->mutex); if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { err = -ENODEV; goto out_unlock; } dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops); if (!dfops->write) { err = -ENOSYS; goto out_unlock; } buf = (char *)get_zeroed_page(GFP_KERNEL); if (!buf) { err = -ENOMEM; goto out_unlock; } if (copy_from_user(buf, userbuf, count)) { err = -EFAULT; goto out_freepage; } if (dfops->take_irqlock) { spin_lock_irq(&dev->wl->irq_lock); err = dfops->write(dev, buf, count); spin_unlock_irq(&dev->wl->irq_lock); } else err = dfops->write(dev, buf, count); if (err) goto out_freepage; out_freepage: free_page((unsigned long)buf); out_unlock: mutex_unlock(&dev->wl->mutex); return err ? err : count; } #define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \ static struct b43legacy_debugfs_fops fops_##name = { \ .read = _read, \ .write = _write, \ .fops = { \ .open = simple_open, \ .read = b43legacy_debugfs_read, \ .write = b43legacy_debugfs_write, \ .llseek = generic_file_llseek, \ }, \ .file_struct_offset = offsetof(struct b43legacy_dfsentry, \ file_##name), \ .take_irqlock = _take_irqlock, \ } B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1); B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1); B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1); B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0); B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1); int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature) { return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]); } static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev) { struct b43legacy_dfsentry *e = dev->dfsentry; int i; for (i = 0; i < __B43legacy_NR_DYNDBG; i++) debugfs_remove(e->dyn_debug_dentries[i]); } static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev) { struct b43legacy_dfsentry *e = dev->dfsentry; struct dentry *d; #define add_dyn_dbg(name, id, initstate) do { \ e->dyn_debug[id] = (initstate); \ d = debugfs_create_bool(name, 0600, e->subdir, \ &(e->dyn_debug[id])); \ if (!IS_ERR(d)) \ e->dyn_debug_dentries[id] = d; \ } while (0) add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, 0); add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, 0); add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, 0); add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, 0); add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, 0); #undef add_dyn_dbg } void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev) { struct b43legacy_dfsentry *e; struct b43legacy_txstatus_log *log; char devdir[16]; B43legacy_WARN_ON(!dev); e = kzalloc(sizeof(*e), GFP_KERNEL); if (!e) { b43legacyerr(dev->wl, "debugfs: add device OOM\n"); return; } e->dev = dev; log = &e->txstatlog; log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS, sizeof(struct b43legacy_txstatus), GFP_KERNEL); if (!log->log) { b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n"); kfree(e); return; } log->end = -1; spin_lock_init(&log->lock); dev->dfsentry = e; snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); e->subdir = debugfs_create_dir(devdir, rootdir); if (!e->subdir || IS_ERR(e->subdir)) { if (e->subdir == ERR_PTR(-ENODEV)) { b43legacydbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not " "enabled in kernel config\n"); } else { b43legacyerr(dev->wl, "debugfs: cannot create %s directory\n", devdir); } dev->dfsentry = NULL; kfree(log->log); kfree(e); return; } #define ADD_FILE(name, mode) \ do { \ struct dentry *d; \ d = debugfs_create_file(__stringify(name), \ mode, e->subdir, dev, \ &fops_##name.fops); \ e->file_##name.dentry = NULL; \ if (!IS_ERR(d)) \ e->file_##name.dentry = d; \ } while (0) ADD_FILE(tsf, 0600); ADD_FILE(ucode_regs, 0400); ADD_FILE(shm, 0400); ADD_FILE(txstat, 0400); ADD_FILE(restart, 0200); #undef ADD_FILE b43legacy_add_dynamic_debug(dev); } void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev) { struct b43legacy_dfsentry *e; if (!dev) return; e = dev->dfsentry; if (!e) return; b43legacy_remove_dynamic_debug(dev); debugfs_remove(e->file_tsf.dentry); debugfs_remove(e->file_ucode_regs.dentry); debugfs_remove(e->file_shm.dentry); debugfs_remove(e->file_txstat.dentry); debugfs_remove(e->file_restart.dentry); debugfs_remove(e->subdir); kfree(e->txstatlog.log); kfree(e); } void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, const struct b43legacy_txstatus *status) { struct b43legacy_dfsentry *e = dev->dfsentry; struct b43legacy_txstatus_log *log; struct b43legacy_txstatus *cur; int i; if (!e) return; log = &e->txstatlog; B43legacy_WARN_ON(!irqs_disabled()); spin_lock(&log->lock); i = log->end + 1; if (i == B43legacy_NR_LOGGED_TXSTATUS) i = 0; log->end = i; cur = &(log->log[i]); memcpy(cur, status, sizeof(*cur)); spin_unlock(&log->lock); } void b43legacy_debugfs_init(void) { rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); if (IS_ERR(rootdir)) rootdir = NULL; } void b43legacy_debugfs_exit(void) { debugfs_remove(rootdir); } compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/b43legacy.h0000644000175000017500000006467012026211315024201 0ustar mcgrofmcgrof#ifndef B43legacy_H_ #define B43legacy_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include "debugfs.h" #include "leds.h" #include "rfkill.h" #include "phy.h" #define B43legacy_IRQWAIT_MAX_RETRIES 20 /* MMIO offsets */ #define B43legacy_MMIO_DMA0_REASON 0x20 #define B43legacy_MMIO_DMA0_IRQ_MASK 0x24 #define B43legacy_MMIO_DMA1_REASON 0x28 #define B43legacy_MMIO_DMA1_IRQ_MASK 0x2C #define B43legacy_MMIO_DMA2_REASON 0x30 #define B43legacy_MMIO_DMA2_IRQ_MASK 0x34 #define B43legacy_MMIO_DMA3_REASON 0x38 #define B43legacy_MMIO_DMA3_IRQ_MASK 0x3C #define B43legacy_MMIO_DMA4_REASON 0x40 #define B43legacy_MMIO_DMA4_IRQ_MASK 0x44 #define B43legacy_MMIO_DMA5_REASON 0x48 #define B43legacy_MMIO_DMA5_IRQ_MASK 0x4C #define B43legacy_MMIO_MACCTL 0x120 /* MAC control */ #define B43legacy_MMIO_MACCMD 0x124 /* MAC command */ #define B43legacy_MMIO_GEN_IRQ_REASON 0x128 #define B43legacy_MMIO_GEN_IRQ_MASK 0x12C #define B43legacy_MMIO_RAM_CONTROL 0x130 #define B43legacy_MMIO_RAM_DATA 0x134 #define B43legacy_MMIO_PS_STATUS 0x140 #define B43legacy_MMIO_RADIO_HWENABLED_HI 0x158 #define B43legacy_MMIO_SHM_CONTROL 0x160 #define B43legacy_MMIO_SHM_DATA 0x164 #define B43legacy_MMIO_SHM_DATA_UNALIGNED 0x166 #define B43legacy_MMIO_XMITSTAT_0 0x170 #define B43legacy_MMIO_XMITSTAT_1 0x174 #define B43legacy_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ #define B43legacy_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ #define B43legacy_MMIO_TSF_CFP_REP 0x188 #define B43legacy_MMIO_TSF_CFP_START 0x18C /* 32-bit DMA */ #define B43legacy_MMIO_DMA32_BASE0 0x200 #define B43legacy_MMIO_DMA32_BASE1 0x220 #define B43legacy_MMIO_DMA32_BASE2 0x240 #define B43legacy_MMIO_DMA32_BASE3 0x260 #define B43legacy_MMIO_DMA32_BASE4 0x280 #define B43legacy_MMIO_DMA32_BASE5 0x2A0 /* 64-bit DMA */ #define B43legacy_MMIO_DMA64_BASE0 0x200 #define B43legacy_MMIO_DMA64_BASE1 0x240 #define B43legacy_MMIO_DMA64_BASE2 0x280 #define B43legacy_MMIO_DMA64_BASE3 0x2C0 #define B43legacy_MMIO_DMA64_BASE4 0x300 #define B43legacy_MMIO_DMA64_BASE5 0x340 /* PIO */ #define B43legacy_MMIO_PIO1_BASE 0x300 #define B43legacy_MMIO_PIO2_BASE 0x310 #define B43legacy_MMIO_PIO3_BASE 0x320 #define B43legacy_MMIO_PIO4_BASE 0x330 #define B43legacy_MMIO_PHY_VER 0x3E0 #define B43legacy_MMIO_PHY_RADIO 0x3E2 #define B43legacy_MMIO_PHY0 0x3E6 #define B43legacy_MMIO_ANTENNA 0x3E8 #define B43legacy_MMIO_CHANNEL 0x3F0 #define B43legacy_MMIO_CHANNEL_EXT 0x3F4 #define B43legacy_MMIO_RADIO_CONTROL 0x3F6 #define B43legacy_MMIO_RADIO_DATA_HIGH 0x3F8 #define B43legacy_MMIO_RADIO_DATA_LOW 0x3FA #define B43legacy_MMIO_PHY_CONTROL 0x3FC #define B43legacy_MMIO_PHY_DATA 0x3FE #define B43legacy_MMIO_MACFILTER_CONTROL 0x420 #define B43legacy_MMIO_MACFILTER_DATA 0x422 #define B43legacy_MMIO_RCMTA_COUNT 0x43C /* Receive Match Transmitter Addr */ #define B43legacy_MMIO_RADIO_HWENABLED_LO 0x49A #define B43legacy_MMIO_GPIO_CONTROL 0x49C #define B43legacy_MMIO_GPIO_MASK 0x49E #define B43legacy_MMIO_TSF_CFP_PRETBTT 0x612 #define B43legacy_MMIO_TSF_0 0x632 /* core rev < 3 only */ #define B43legacy_MMIO_TSF_1 0x634 /* core rev < 3 only */ #define B43legacy_MMIO_TSF_2 0x636 /* core rev < 3 only */ #define B43legacy_MMIO_TSF_3 0x638 /* core rev < 3 only */ #define B43legacy_MMIO_RNG 0x65A #define B43legacy_MMIO_POWERUP_DELAY 0x6A8 /* SPROM boardflags_lo values */ #define B43legacy_BFL_PACTRL 0x0002 #define B43legacy_BFL_RSSI 0x0008 #define B43legacy_BFL_EXTLNA 0x1000 /* GPIO register offset, in both ChipCommon and PCI core. */ #define B43legacy_GPIO_CONTROL 0x6c /* SHM Routing */ #define B43legacy_SHM_SHARED 0x0001 #define B43legacy_SHM_WIRELESS 0x0002 #define B43legacy_SHM_HW 0x0004 #define B43legacy_SHM_UCODE 0x0300 /* SHM Routing modifiers */ #define B43legacy_SHM_AUTOINC_R 0x0200 /* Read Auto-increment */ #define B43legacy_SHM_AUTOINC_W 0x0100 /* Write Auto-increment */ #define B43legacy_SHM_AUTOINC_RW (B43legacy_SHM_AUTOINC_R | \ B43legacy_SHM_AUTOINC_W) /* Misc SHM_SHARED offsets */ #define B43legacy_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */ #define B43legacy_SHM_SH_HOSTFLO 0x005E /* Hostflags ucode opts (low) */ #define B43legacy_SHM_SH_HOSTFHI 0x0060 /* Hostflags ucode opts (high) */ /* SHM_SHARED crypto engine */ #define B43legacy_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block */ /* SHM_SHARED beacon/AP variables */ #define B43legacy_SHM_SH_DTIMP 0x0012 /* DTIM period */ #define B43legacy_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */ #define B43legacy_SHM_SH_BTL1 0x001A /* Beacon template length 1 */ #define B43legacy_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */ #define B43legacy_SHM_SH_TIMPOS 0x001E /* TIM position in beacon */ #define B43legacy_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word */ /* SHM_SHARED ACK/CTS control */ #define B43legacy_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word */ /* SHM_SHARED probe response variables */ #define B43legacy_SHM_SH_PRTLEN 0x004A /* Probe Response template length */ #define B43legacy_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ #define B43legacy_SHM_SH_PRPHYCTL 0x0188 /* Probe Resp PHY TX control */ /* SHM_SHARED rate tables */ #define B43legacy_SHM_SH_OFDMDIRECT 0x0480 /* Pointer to OFDM direct map */ #define B43legacy_SHM_SH_OFDMBASIC 0x04A0 /* Pointer to OFDM basic rate map */ #define B43legacy_SHM_SH_CCKDIRECT 0x04C0 /* Pointer to CCK direct map */ #define B43legacy_SHM_SH_CCKBASIC 0x04E0 /* Pointer to CCK basic rate map */ /* SHM_SHARED microcode soft registers */ #define B43legacy_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ #define B43legacy_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */ #define B43legacy_SHM_SH_UCODEDATE 0x0004 /* Microcode date */ #define B43legacy_SHM_SH_UCODETIME 0x0006 /* Microcode time */ #define B43legacy_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ #define B43legacy_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ #define B43legacy_UCODEFLAGS_OFFSET 0x005E /* Hardware Radio Enable masks */ #define B43legacy_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) #define B43legacy_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) /* HostFlags. See b43legacy_hf_read/write() */ #define B43legacy_HF_SYMW 0x00000002 /* G-PHY SYM workaround */ #define B43legacy_HF_GDCW 0x00000020 /* G-PHY DV cancel filter */ #define B43legacy_HF_OFDMPABOOST 0x00000040 /* Enable PA boost OFDM */ #define B43legacy_HF_EDCF 0x00000100 /* on if WME/MAC suspended */ /* MacFilter offsets. */ #define B43legacy_MACFILTER_SELF 0x0000 #define B43legacy_MACFILTER_BSSID 0x0003 #define B43legacy_MACFILTER_MAC 0x0010 /* PHYVersioning */ #define B43legacy_PHYTYPE_B 0x01 #define B43legacy_PHYTYPE_G 0x02 /* PHYRegisters */ #define B43legacy_PHY_G_LO_CONTROL 0x0810 #define B43legacy_PHY_ILT_G_CTRL 0x0472 #define B43legacy_PHY_ILT_G_DATA1 0x0473 #define B43legacy_PHY_ILT_G_DATA2 0x0474 #define B43legacy_PHY_G_PCTL 0x0029 #define B43legacy_PHY_RADIO_BITFIELD 0x0401 #define B43legacy_PHY_G_CRS 0x0429 #define B43legacy_PHY_NRSSILT_CTRL 0x0803 #define B43legacy_PHY_NRSSILT_DATA 0x0804 /* RadioRegisters */ #define B43legacy_RADIOCTL_ID 0x01 /* MAC Control bitfield */ #define B43legacy_MACCTL_ENABLED 0x00000001 /* MAC Enabled */ #define B43legacy_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */ #define B43legacy_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */ #define B43legacy_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */ #define B43legacy_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */ #define B43legacy_MACCTL_BE 0x00010000 /* Big Endian mode */ #define B43legacy_MACCTL_INFRA 0x00020000 /* Infrastructure mode */ #define B43legacy_MACCTL_AP 0x00040000 /* AccessPoint mode */ #define B43legacy_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ #define B43legacy_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ #define B43legacy_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep bad PLCP frames */ #define B43legacy_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ #define B43legacy_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ #define B43legacy_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ #define B43legacy_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */ #define B43legacy_MACCTL_AWAKE 0x04000000 /* Device is awake */ #define B43legacy_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */ #define B43legacy_MACCTL_GMODE 0x80000000 /* G Mode */ /* MAC Command bitfield */ #define B43legacy_MACCMD_BEACON0_VALID 0x00000001 /* Beacon 0 in template RAM is busy/valid */ #define B43legacy_MACCMD_BEACON1_VALID 0x00000002 /* Beacon 1 in template RAM is busy/valid */ #define B43legacy_MACCMD_DFQ_VALID 0x00000004 /* Directed frame queue valid (IBSS PS mode, ATIM) */ #define B43legacy_MACCMD_CCA 0x00000008 /* Clear channel assessment */ #define B43legacy_MACCMD_BGNOISE 0x00000010 /* Background noise */ /* 802.11 core specific TM State Low flags */ #define B43legacy_TMSLOW_GMODE 0x20000000 /* G Mode Enable */ #define B43legacy_TMSLOW_PLLREFSEL 0x00200000 /* PLL Freq Ref Select */ #define B43legacy_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Ctrl Enbl */ #define B43legacy_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */ #define B43legacy_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */ /* 802.11 core specific TM State High flags */ #define B43legacy_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available */ #define B43legacy_TMSHIGH_GPHY 0x00010000 /* G-PHY avail (rev >= 5) */ #define B43legacy_UCODEFLAG_AUTODIV 0x0001 /* Generic-Interrupt reasons. */ #define B43legacy_IRQ_MAC_SUSPENDED 0x00000001 #define B43legacy_IRQ_BEACON 0x00000002 #define B43legacy_IRQ_TBTT_INDI 0x00000004 /* Target Beacon Transmit Time */ #define B43legacy_IRQ_BEACON_TX_OK 0x00000008 #define B43legacy_IRQ_BEACON_CANCEL 0x00000010 #define B43legacy_IRQ_ATIM_END 0x00000020 #define B43legacy_IRQ_PMQ 0x00000040 #define B43legacy_IRQ_PIO_WORKAROUND 0x00000100 #define B43legacy_IRQ_MAC_TXERR 0x00000200 #define B43legacy_IRQ_PHY_TXERR 0x00000800 #define B43legacy_IRQ_PMEVENT 0x00001000 #define B43legacy_IRQ_TIMER0 0x00002000 #define B43legacy_IRQ_TIMER1 0x00004000 #define B43legacy_IRQ_DMA 0x00008000 #define B43legacy_IRQ_TXFIFO_FLUSH_OK 0x00010000 #define B43legacy_IRQ_CCA_MEASURE_OK 0x00020000 #define B43legacy_IRQ_NOISESAMPLE_OK 0x00040000 #define B43legacy_IRQ_UCODE_DEBUG 0x08000000 #define B43legacy_IRQ_RFKILL 0x10000000 #define B43legacy_IRQ_TX_OK 0x20000000 #define B43legacy_IRQ_PHY_G_CHANGED 0x40000000 #define B43legacy_IRQ_TIMEOUT 0x80000000 #define B43legacy_IRQ_ALL 0xFFFFFFFF #define B43legacy_IRQ_MASKTEMPLATE (B43legacy_IRQ_MAC_SUSPENDED | \ B43legacy_IRQ_TBTT_INDI | \ B43legacy_IRQ_ATIM_END | \ B43legacy_IRQ_PMQ | \ B43legacy_IRQ_MAC_TXERR | \ B43legacy_IRQ_PHY_TXERR | \ B43legacy_IRQ_DMA | \ B43legacy_IRQ_TXFIFO_FLUSH_OK | \ B43legacy_IRQ_NOISESAMPLE_OK | \ B43legacy_IRQ_UCODE_DEBUG | \ B43legacy_IRQ_RFKILL | \ B43legacy_IRQ_TX_OK) /* Device specific rate values. * The actual values defined here are (rate_in_mbps * 2). * Some code depends on this. Don't change it. */ #define B43legacy_CCK_RATE_1MB 2 #define B43legacy_CCK_RATE_2MB 4 #define B43legacy_CCK_RATE_5MB 11 #define B43legacy_CCK_RATE_11MB 22 #define B43legacy_OFDM_RATE_6MB 12 #define B43legacy_OFDM_RATE_9MB 18 #define B43legacy_OFDM_RATE_12MB 24 #define B43legacy_OFDM_RATE_18MB 36 #define B43legacy_OFDM_RATE_24MB 48 #define B43legacy_OFDM_RATE_36MB 72 #define B43legacy_OFDM_RATE_48MB 96 #define B43legacy_OFDM_RATE_54MB 108 /* Convert a b43legacy rate value to a rate in 100kbps */ #define B43legacy_RATE_TO_100KBPS(rate) (((rate) * 10) / 2) #define B43legacy_DEFAULT_SHORT_RETRY_LIMIT 7 #define B43legacy_DEFAULT_LONG_RETRY_LIMIT 4 #define B43legacy_PHY_TX_BADNESS_LIMIT 1000 /* Max size of a security key */ #define B43legacy_SEC_KEYSIZE 16 /* Security algorithms. */ enum { B43legacy_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ B43legacy_SEC_ALGO_WEP40, B43legacy_SEC_ALGO_TKIP, B43legacy_SEC_ALGO_AES, B43legacy_SEC_ALGO_WEP104, B43legacy_SEC_ALGO_AES_LEGACY, }; /* Core Information Registers */ #define B43legacy_CIR_BASE 0xf00 #define B43legacy_CIR_SBTPSFLAG (B43legacy_CIR_BASE + 0x18) #define B43legacy_CIR_SBIMSTATE (B43legacy_CIR_BASE + 0x90) #define B43legacy_CIR_SBINTVEC (B43legacy_CIR_BASE + 0x94) #define B43legacy_CIR_SBTMSTATELOW (B43legacy_CIR_BASE + 0x98) #define B43legacy_CIR_SBTMSTATEHIGH (B43legacy_CIR_BASE + 0x9c) #define B43legacy_CIR_SBIMCONFIGLOW (B43legacy_CIR_BASE + 0xa8) #define B43legacy_CIR_SB_ID_HI (B43legacy_CIR_BASE + 0xfc) /* sbtmstatehigh state flags */ #define B43legacy_SBTMSTATEHIGH_SERROR 0x00000001 #define B43legacy_SBTMSTATEHIGH_BUSY 0x00000004 #define B43legacy_SBTMSTATEHIGH_TIMEOUT 0x00000020 #define B43legacy_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000 #define B43legacy_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000 #define B43legacy_SBTMSTATEHIGH_DMA64BIT 0x10000000 #define B43legacy_SBTMSTATEHIGH_GATEDCLK 0x20000000 #define B43legacy_SBTMSTATEHIGH_BISTFAILED 0x40000000 #define B43legacy_SBTMSTATEHIGH_BISTCOMPLETE 0x80000000 /* sbimstate flags */ #define B43legacy_SBIMSTATE_IB_ERROR 0x20000 #define B43legacy_SBIMSTATE_TIMEOUT 0x40000 #define PFX KBUILD_MODNAME ": " #ifdef assert # undef assert #endif #ifdef CONFIG_B43LEGACY_DEBUG # define B43legacy_WARN_ON(x) WARN_ON(x) # define B43legacy_BUG_ON(expr) \ do { \ if (unlikely((expr))) { \ printk(KERN_INFO PFX "Test (%s) failed\n", \ #expr); \ BUG_ON(expr); \ } \ } while (0) # define B43legacy_DEBUG 1 #else /* This will evaluate the argument even if debugging is disabled. */ static inline bool __b43legacy_warn_on_dummy(bool x) { return x; } # define B43legacy_WARN_ON(x) __b43legacy_warn_on_dummy(unlikely(!!(x))) # define B43legacy_BUG_ON(x) do { /* nothing */ } while (0) # define B43legacy_DEBUG 0 #endif struct net_device; struct pci_dev; struct b43legacy_dmaring; struct b43legacy_pioqueue; /* The firmware file header */ #define B43legacy_FW_TYPE_UCODE 'u' #define B43legacy_FW_TYPE_PCM 'p' #define B43legacy_FW_TYPE_IV 'i' struct b43legacy_fw_header { /* File type */ u8 type; /* File format version */ u8 ver; u8 __padding[2]; /* Size of the data. For ucode and PCM this is in bytes. * For IV this is number-of-ivs. */ __be32 size; } __packed; /* Initial Value file format */ #define B43legacy_IV_OFFSET_MASK 0x7FFF #define B43legacy_IV_32BIT 0x8000 struct b43legacy_iv { __be16 offset_size; union { __be16 d16; __be32 d32; } data __packed; } __packed; #define B43legacy_PHYMODE(phytype) (1 << (phytype)) #define B43legacy_PHYMODE_B B43legacy_PHYMODE \ ((B43legacy_PHYTYPE_B)) #define B43legacy_PHYMODE_G B43legacy_PHYMODE \ ((B43legacy_PHYTYPE_G)) /* Value pair to measure the LocalOscillator. */ struct b43legacy_lopair { s8 low; s8 high; u8 used:1; }; #define B43legacy_LO_COUNT (14*4) struct b43legacy_phy { /* Possible PHYMODEs on this PHY */ u8 possible_phymodes; /* GMODE bit enabled in MACCTL? */ bool gmode; /* Analog Type */ u8 analog; /* B43legacy_PHYTYPE_ */ u8 type; /* PHY revision number. */ u8 rev; u16 antenna_diversity; u16 savedpctlreg; /* Radio versioning */ u16 radio_manuf; /* Radio manufacturer */ u16 radio_ver; /* Radio version */ u8 calibrated:1; u8 radio_rev; /* Radio revision */ bool dyn_tssi_tbl; /* tssi2dbm is kmalloc()ed. */ /* ACI (adjacent channel interference) flags. */ bool aci_enable; bool aci_wlan_automatic; bool aci_hw_rssi; /* Radio switched on/off */ bool radio_on; struct { /* Values saved when turning the radio off. * They are needed when turning it on again. */ bool valid; u16 rfover; u16 rfoverval; } radio_off_context; u16 minlowsig[2]; u16 minlowsigpos[2]; /* LO Measurement Data. * Use b43legacy_get_lopair() to get a value. */ struct b43legacy_lopair *_lo_pairs; /* TSSI to dBm table in use */ const s8 *tssi2dbm; /* idle TSSI value */ s8 idle_tssi; /* Target idle TSSI */ int tgt_idle_tssi; /* Current idle TSSI */ int cur_idle_tssi; /* LocalOscillator control values. */ struct b43legacy_txpower_lo_control *lo_control; /* Values from b43legacy_calc_loopback_gain() */ s16 max_lb_gain; /* Maximum Loopback gain in hdB */ s16 trsw_rx_gain; /* TRSW RX gain in hdB */ s16 lna_lod_gain; /* LNA lod */ s16 lna_gain; /* LNA */ s16 pga_gain; /* PGA */ /* Desired TX power level (in dBm). This is set by the user and * adjusted in b43legacy_phy_xmitpower(). */ u8 power_level; /* Values from b43legacy_calc_loopback_gain() */ u16 loopback_gain[2]; /* TX Power control values. */ /* B/G PHY */ struct { /* Current Radio Attenuation for TXpower recalculation. */ u16 rfatt; /* Current Baseband Attenuation for TXpower recalculation. */ u16 bbatt; /* Current TXpower control value for TXpower recalculation. */ u16 txctl1; u16 txctl2; }; /* A PHY */ struct { u16 txpwr_offset; }; /* Current Interference Mitigation mode */ int interfmode; /* Stack of saved values from the Interference Mitigation code. * Each value in the stack is laid out as follows: * bit 0-11: offset * bit 12-15: register ID * bit 16-32: value * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT */ #define B43legacy_INTERFSTACK_SIZE 26 u32 interfstack[B43legacy_INTERFSTACK_SIZE]; /* Saved values from the NRSSI Slope calculation */ s16 nrssi[2]; s32 nrssislope; /* In memory nrssi lookup table. */ s8 nrssi_lt[64]; /* current channel */ u8 channel; u16 lofcal; u16 initval; /* PHY TX errors counter. */ atomic_t txerr_cnt; #if B43legacy_DEBUG /* Manual TX-power control enabled? */ bool manual_txpower_control; /* PHY registers locked by b43legacy_phy_lock()? */ bool phy_locked; #endif /* B43legacy_DEBUG */ }; /* Data structures for DMA transmission, per 80211 core. */ struct b43legacy_dma { struct b43legacy_dmaring *tx_ring0; struct b43legacy_dmaring *tx_ring1; struct b43legacy_dmaring *tx_ring2; struct b43legacy_dmaring *tx_ring3; struct b43legacy_dmaring *tx_ring4; struct b43legacy_dmaring *tx_ring5; struct b43legacy_dmaring *rx_ring0; struct b43legacy_dmaring *rx_ring3; /* only on core.rev < 5 */ u32 translation; /* Routing bits */ }; /* Data structures for PIO transmission, per 80211 core. */ struct b43legacy_pio { struct b43legacy_pioqueue *queue0; struct b43legacy_pioqueue *queue1; struct b43legacy_pioqueue *queue2; struct b43legacy_pioqueue *queue3; }; /* Context information for a noise calculation (Link Quality). */ struct b43legacy_noise_calculation { u8 channel_at_start; bool calculation_running; u8 nr_samples; s8 samples[8][4]; }; struct b43legacy_stats { u8 link_noise; /* Store the last TX/RX times here for updating the leds. */ unsigned long last_tx; unsigned long last_rx; }; struct b43legacy_key { void *keyconf; bool enabled; u8 algorithm; }; #define B43legacy_QOS_QUEUE_NUM 4 struct b43legacy_wldev; /* QOS parameters for a queue. */ struct b43legacy_qos_params { /* The QOS parameters */ struct ieee80211_tx_queue_params p; }; /* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */ struct b43legacy_wl { /* Pointer to the active wireless device on this chip */ struct b43legacy_wldev *current_dev; /* Pointer to the ieee80211 hardware data structure */ struct ieee80211_hw *hw; spinlock_t irq_lock; /* locks IRQ */ struct mutex mutex; /* locks wireless core state */ spinlock_t leds_lock; /* lock for leds */ /* firmware loading work */ struct work_struct firmware_load; /* We can only have one operating interface (802.11 core) * at a time. General information about this interface follows. */ struct ieee80211_vif *vif; /* MAC address (can be NULL). */ u8 mac_addr[ETH_ALEN]; /* Current BSSID (can be NULL). */ u8 bssid[ETH_ALEN]; /* Interface type. (IEEE80211_IF_TYPE_XXX) */ int if_type; /* Is the card operating in AP, STA or IBSS mode? */ bool operating; /* filter flags */ unsigned int filter_flags; /* Stats about the wireless interface */ struct ieee80211_low_level_stats ieee_stats; #ifdef CONFIG_B43LEGACY_HWRNG struct hwrng rng; u8 rng_initialized; char rng_name[30 + 1]; #endif /* List of all wireless devices on this chip */ struct list_head devlist; u8 nr_devs; bool radiotap_enabled; bool radio_enabled; /* The beacon we are currently using (AP or IBSS mode). * This beacon stuff is protected by the irq_lock. */ struct sk_buff *current_beacon; bool beacon0_uploaded; bool beacon1_uploaded; bool beacon_templates_virgin; /* Never wrote the templates? */ struct work_struct beacon_update_trigger; /* The current QOS parameters for the 4 queues. */ struct b43legacy_qos_params qos_params[B43legacy_QOS_QUEUE_NUM]; /* Packet transmit work */ struct work_struct tx_work; /* Queue of packets to be transmitted. */ struct sk_buff_head tx_queue[B43legacy_QOS_QUEUE_NUM]; /* Flag that implement the queues stopping. */ bool tx_queue_stopped[B43legacy_QOS_QUEUE_NUM]; }; /* Pointers to the firmware data and meta information about it. */ struct b43legacy_firmware { /* Microcode */ const struct firmware *ucode; /* PCM code */ const struct firmware *pcm; /* Initial MMIO values for the firmware */ const struct firmware *initvals; /* Initial MMIO values for the firmware, band-specific */ const struct firmware *initvals_band; /* Firmware revision */ u16 rev; /* Firmware patchlevel */ u16 patch; }; /* Device (802.11 core) initialization status. */ enum { B43legacy_STAT_UNINIT = 0, /* Uninitialized. */ B43legacy_STAT_INITIALIZED = 1, /* Initialized, not yet started. */ B43legacy_STAT_STARTED = 2, /* Up and running. */ }; #define b43legacy_status(wldev) atomic_read(&(wldev)->__init_status) #define b43legacy_set_status(wldev, stat) do { \ atomic_set(&(wldev)->__init_status, (stat)); \ smp_wmb(); \ } while (0) /* *** --- HOW LOCKING WORKS IN B43legacy --- *** * * You should always acquire both, wl->mutex and wl->irq_lock unless: * - You don't need to acquire wl->irq_lock, if the interface is stopped. * - You don't need to acquire wl->mutex in the IRQ handler, IRQ tasklet * and packet TX path (and _ONLY_ there.) */ /* Data structure for one wireless device (802.11 core) */ struct b43legacy_wldev { struct ssb_device *dev; struct b43legacy_wl *wl; /* The device initialization status. * Use b43legacy_status() to query. */ atomic_t __init_status; /* Saved init status for handling suspend. */ int suspend_init_status; bool __using_pio; /* Using pio rather than dma. */ bool bad_frames_preempt;/* Use "Bad Frames Preemption". */ bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM). */ bool short_preamble; /* TRUE if using short preamble. */ bool radio_hw_enable; /* State of radio hardware enable bit. */ /* PHY/Radio device. */ struct b43legacy_phy phy; union { /* DMA engines. */ struct b43legacy_dma dma; /* PIO engines. */ struct b43legacy_pio pio; }; /* Various statistics about the physical device. */ struct b43legacy_stats stats; /* The device LEDs. */ struct b43legacy_led led_tx; struct b43legacy_led led_rx; struct b43legacy_led led_assoc; struct b43legacy_led led_radio; /* Reason code of the last interrupt. */ u32 irq_reason; u32 dma_reason[6]; /* The currently active generic-interrupt mask. */ u32 irq_mask; /* Link Quality calculation context. */ struct b43legacy_noise_calculation noisecalc; /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ int mac_suspended; /* Interrupt Service Routine tasklet (bottom-half) */ struct tasklet_struct isr_tasklet; /* Periodic tasks */ struct delayed_work periodic_work; unsigned int periodic_state; struct work_struct restart_work; /* encryption/decryption */ u16 ktp; /* Key table pointer */ u8 max_nr_keys; struct b43legacy_key key[58]; /* Firmware data */ struct b43legacy_firmware fw; /* Devicelist in struct b43legacy_wl (all 802.11 cores) */ struct list_head list; /* Debugging stuff follows. */ #ifdef CONFIG_B43LEGACY_DEBUG struct b43legacy_dfsentry *dfsentry; #endif }; static inline struct b43legacy_wl *hw_to_b43legacy_wl(struct ieee80211_hw *hw) { return hw->priv; } /* Helper function, which returns a boolean. * TRUE, if PIO is used; FALSE, if DMA is used. */ #if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) static inline int b43legacy_using_pio(struct b43legacy_wldev *dev) { return dev->__using_pio; } #elif defined(CONFIG_B43LEGACY_DMA) static inline int b43legacy_using_pio(struct b43legacy_wldev *dev) { return 0; } #elif defined(CONFIG_B43LEGACY_PIO) static inline int b43legacy_using_pio(struct b43legacy_wldev *dev) { return 1; } #else # error "Using neither DMA nor PIO? Confused..." #endif static inline struct b43legacy_wldev *dev_to_b43legacy_wldev(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); return ssb_get_drvdata(ssb_dev); } /* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */ static inline int b43legacy_is_mode(struct b43legacy_wl *wl, int type) { return (wl->operating && wl->if_type == type); } static inline bool is_bcm_board_vendor(struct b43legacy_wldev *dev) { return (dev->dev->bus->boardinfo.vendor == PCI_VENDOR_ID_BROADCOM); } static inline u16 b43legacy_read16(struct b43legacy_wldev *dev, u16 offset) { return ssb_read16(dev->dev, offset); } static inline void b43legacy_write16(struct b43legacy_wldev *dev, u16 offset, u16 value) { ssb_write16(dev->dev, offset, value); } static inline u32 b43legacy_read32(struct b43legacy_wldev *dev, u16 offset) { return ssb_read32(dev->dev, offset); } static inline void b43legacy_write32(struct b43legacy_wldev *dev, u16 offset, u32 value) { ssb_write32(dev->dev, offset, value); } static inline struct b43legacy_lopair *b43legacy_get_lopair(struct b43legacy_phy *phy, u16 radio_attenuation, u16 baseband_attenuation) { return phy->_lo_pairs + (radio_attenuation + 14 * (baseband_attenuation / 2)); } /* Message printing */ __printf(2, 3) void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...); __printf(2, 3) void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...); __printf(2, 3) void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...); #if B43legacy_DEBUG __printf(2, 3) void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...); #else /* DEBUG */ # define b43legacydbg(wl, fmt...) do { /* nothing */ } while (0) #endif /* DEBUG */ /* Macros for printing a value in Q5.2 format */ #define Q52_FMT "%u.%u" #define Q52_ARG(q52) ((q52) / 4), (((q52) & 3) * 100 / 4) #endif /* B43legacy_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/Makefile0000644000175000017500000000100312026211315023670 0ustar mcgrofmcgrof# b43legacy core b43legacy-y += main.o b43legacy-y += ilt.o b43legacy-y += phy.o b43legacy-y += radio.o b43legacy-y += sysfs.o b43legacy-y += xmit.o # b43 RFKILL button support b43legacy-y += rfkill.o # b43legacy LED support b43legacy-$(CONFIG_B43LEGACY_LEDS) += leds.o # b43legacy debugging b43legacy-$(CONFIG_B43LEGACY_DEBUG) += debugfs.o # b43legacy DMA and PIO b43legacy-$(CONFIG_B43LEGACY_DMA) += dma.o b43legacy-$(CONFIG_B43LEGACY_PIO) += pio.o obj-$(CONFIG_B43LEGACY) += b43legacy.o compat-drivers-2012-09-18/drivers/net/wireless/b43legacy/Kconfig0000644000175000017500000000571112026211315023545 0ustar mcgrofmcgrofconfig B43LEGACY tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)" depends on SSB_POSSIBLE && MAC80211 && HAS_DMA select SSB select FW_LOADER ---help--- b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and BCM4303) and early model 802.11g chips (BCM4306 Ver. 2) used in the Linksys WPC54G V1 PCMCIA devices. Newer 802.11g and 802.11a devices need b43. It is safe to include both b43 and b43legacy as the underlying glue layer will automatically load the correct version for your device. This driver uses V3 firmware, which must be installed separately using b43-fwcutter. This driver can be built as a module (recommended) that will be called "b43legacy". If unsure, say M. # Auto-select SSB PCI-HOST support, if possible config B43LEGACY_PCI_AUTOSELECT bool depends on B43LEGACY && SSB_PCIHOST_POSSIBLE select SSB_PCIHOST select SSB_B43_PCI_BRIDGE default y # Auto-select SSB PCICORE driver, if possible config B43LEGACY_PCICORE_AUTOSELECT bool depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE select SSB_DRIVER_PCICORE default y # LED support # This config option automatically enables b43legacy LEDS support, # if it's possible. config B43LEGACY_LEDS bool depends on B43LEGACY && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = B43LEGACY) default y # This config option automatically enables b43 HW-RNG support, # if the HW-RNG core is enabled. config B43LEGACY_HWRNG bool depends on B43LEGACY && (HW_RANDOM = y || HW_RANDOM = B43LEGACY) default y config B43LEGACY_DEBUG bool "Broadcom 43xx-legacy debugging" depends on B43LEGACY default y ---help--- Say Y, because this information will help you get the driver running. This option generates a minimum of log output. config B43LEGACY_DMA bool depends on B43LEGACY config B43LEGACY_PIO bool depends on B43LEGACY choice prompt "Broadcom 43xx-legacy data transfer mode" depends on B43LEGACY default B43LEGACY_DMA_AND_PIO_MODE config B43LEGACY_DMA_AND_PIO_MODE bool "DMA + PIO" select B43LEGACY_DMA select B43LEGACY_PIO ---help--- Include both, Direct Memory Access (DMA) and Programmed I/O (PIO) data transfer modes. The mode actually used is selectable through the module parameter "pio". With pio=0 as a module parameter, the default DMA is used, otherwise PIO is used. If unsure, choose this option. config B43LEGACY_DMA_MODE bool "DMA (Direct Memory Access) only" select B43LEGACY_DMA ---help--- Only include Direct Memory Access (DMA). This reduces the size of the driver module, by omitting the PIO code. config B43LEGACY_PIO_MODE bool "PIO (Programmed I/O) only" select B43LEGACY_PIO ---help--- Only include Programmed I/O (PIO). This reduces the size of the driver module, by omitting the DMA code. Please note that PIO transfers are slow (compared to DMA). Also note that not all devices of the b43legacy series support PIO. You should use PIO only if DMA does not work for you. endchoice compat-drivers-2012-09-18/drivers/net/wireless/b43/0000755000175000017500000000000012026211315021051 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/b43/main.c0000644000175000017500000044212612026211315022152 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver Copyright (c) 2005 Martin Langer Copyright (c) 2005 Stefano Brivio Copyright (c) 2005-2009 Michael Buesch Copyright (c) 2005 Danny van Dyk Copyright (c) 2005 Andreas Jaggi Copyright (c) 2010-2011 RafaÅ‚ MiÅ‚ecki SDIO support Copyright (c) 2009 Albert Herranz Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "b43.h" #include "main.h" #include "debugfs.h" #include "phy_common.h" #include "phy_g.h" #include "phy_n.h" #include "dma.h" #include "pio.h" #include "sysfs.h" #include "xmit.h" #include "lo.h" #include "pcmcia.h" #include "sdio.h" #include MODULE_DESCRIPTION("Broadcom B43 wireless driver"); MODULE_AUTHOR("Martin Langer"); MODULE_AUTHOR("Stefano Brivio"); MODULE_AUTHOR("Michael Buesch"); MODULE_AUTHOR("Gábor Stefanik"); MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("b43/ucode11.fw"); MODULE_FIRMWARE("b43/ucode13.fw"); MODULE_FIRMWARE("b43/ucode14.fw"); MODULE_FIRMWARE("b43/ucode15.fw"); MODULE_FIRMWARE("b43/ucode16_mimo.fw"); MODULE_FIRMWARE("b43/ucode5.fw"); MODULE_FIRMWARE("b43/ucode9.fw"); static int modparam_bad_frames_preempt; module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames Preemption"); static char modparam_fwpostfix[16]; module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444); MODULE_PARM_DESC(fwpostfix, "Postfix for the .fw files to load."); static int modparam_hwpctl; module_param_named(hwpctl, modparam_hwpctl, int, 0444); MODULE_PARM_DESC(hwpctl, "Enable hardware-side power control (default off)"); static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); static int modparam_hwtkip; module_param_named(hwtkip, modparam_hwtkip, int, 0444); MODULE_PARM_DESC(hwtkip, "Enable hardware tkip."); static int modparam_qos = 1; module_param_named(qos, modparam_qos, int, 0444); MODULE_PARM_DESC(qos, "Enable QOS support (default on)"); static int modparam_btcoex = 1; module_param_named(btcoex, modparam_btcoex, int, 0444); MODULE_PARM_DESC(btcoex, "Enable Bluetooth coexistence (default on)"); int b43_modparam_verbose = B43_VERBOSITY_DEFAULT; module_param_named(verbose, b43_modparam_verbose, int, 0644); MODULE_PARM_DESC(verbose, "Log message verbosity: 0=error, 1=warn, 2=info(default), 3=debug"); static int b43_modparam_pio = 0; module_param_named(pio, b43_modparam_pio, int, 0644); MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO"); #ifdef CONFIG_B43_BCMA static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), #ifdef CONFIG_B43_BCMA_EXTRA BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), #endif BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS), BCMA_CORETABLE_END }; MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl); #endif #ifdef CONFIG_B43_SSB static const struct ssb_device_id b43_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 7), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 9), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 10), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 11), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 12), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16), SSB_DEVTABLE_END }; MODULE_DEVICE_TABLE(ssb, b43_ssb_tbl); #endif /* Channel and ratetables are shared for all devices. * They can't be const, because ieee80211 puts some precalculated * data in there. This data is the same for all devices, so we don't * get concurrency issues */ #define RATETAB_ENT(_rateid, _flags) \ { \ .bitrate = B43_RATE_TO_BASE100KBPS(_rateid), \ .hw_value = (_rateid), \ .flags = (_flags), \ } /* * NOTE: When changing this, sync with xmit.c's * b43_plcp_get_bitrate_idx_* functions! */ static struct ieee80211_rate __b43_ratetable[] = { RATETAB_ENT(B43_CCK_RATE_1MB, 0), RATETAB_ENT(B43_CCK_RATE_2MB, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(B43_CCK_RATE_5MB, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(B43_CCK_RATE_11MB, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(B43_OFDM_RATE_6MB, 0), RATETAB_ENT(B43_OFDM_RATE_9MB, 0), RATETAB_ENT(B43_OFDM_RATE_12MB, 0), RATETAB_ENT(B43_OFDM_RATE_18MB, 0), RATETAB_ENT(B43_OFDM_RATE_24MB, 0), RATETAB_ENT(B43_OFDM_RATE_36MB, 0), RATETAB_ENT(B43_OFDM_RATE_48MB, 0), RATETAB_ENT(B43_OFDM_RATE_54MB, 0), }; #define b43_a_ratetable (__b43_ratetable + 4) #define b43_a_ratetable_size 8 #define b43_b_ratetable (__b43_ratetable + 0) #define b43_b_ratetable_size 4 #define b43_g_ratetable (__b43_ratetable + 0) #define b43_g_ratetable_size 12 #define CHAN4G(_channel, _freq, _flags) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel b43_2ghz_chantable[] = { CHAN4G(1, 2412, 0), CHAN4G(2, 2417, 0), CHAN4G(3, 2422, 0), CHAN4G(4, 2427, 0), CHAN4G(5, 2432, 0), CHAN4G(6, 2437, 0), CHAN4G(7, 2442, 0), CHAN4G(8, 2447, 0), CHAN4G(9, 2452, 0), CHAN4G(10, 2457, 0), CHAN4G(11, 2462, 0), CHAN4G(12, 2467, 0), CHAN4G(13, 2472, 0), CHAN4G(14, 2484, 0), }; #undef CHAN4G #define CHAN5G(_channel, _flags) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel b43_5ghz_nphy_chantable[] = { CHAN5G(32, 0), CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(50, 0), CHAN5G(52, 0), CHAN5G(54, 0), CHAN5G(56, 0), CHAN5G(58, 0), CHAN5G(60, 0), CHAN5G(62, 0), CHAN5G(64, 0), CHAN5G(66, 0), CHAN5G(68, 0), CHAN5G(70, 0), CHAN5G(72, 0), CHAN5G(74, 0), CHAN5G(76, 0), CHAN5G(78, 0), CHAN5G(80, 0), CHAN5G(82, 0), CHAN5G(84, 0), CHAN5G(86, 0), CHAN5G(88, 0), CHAN5G(90, 0), CHAN5G(92, 0), CHAN5G(94, 0), CHAN5G(96, 0), CHAN5G(98, 0), CHAN5G(100, 0), CHAN5G(102, 0), CHAN5G(104, 0), CHAN5G(106, 0), CHAN5G(108, 0), CHAN5G(110, 0), CHAN5G(112, 0), CHAN5G(114, 0), CHAN5G(116, 0), CHAN5G(118, 0), CHAN5G(120, 0), CHAN5G(122, 0), CHAN5G(124, 0), CHAN5G(126, 0), CHAN5G(128, 0), CHAN5G(130, 0), CHAN5G(132, 0), CHAN5G(134, 0), CHAN5G(136, 0), CHAN5G(138, 0), CHAN5G(140, 0), CHAN5G(142, 0), CHAN5G(144, 0), CHAN5G(145, 0), CHAN5G(146, 0), CHAN5G(147, 0), CHAN5G(148, 0), CHAN5G(149, 0), CHAN5G(150, 0), CHAN5G(151, 0), CHAN5G(152, 0), CHAN5G(153, 0), CHAN5G(154, 0), CHAN5G(155, 0), CHAN5G(156, 0), CHAN5G(157, 0), CHAN5G(158, 0), CHAN5G(159, 0), CHAN5G(160, 0), CHAN5G(161, 0), CHAN5G(162, 0), CHAN5G(163, 0), CHAN5G(164, 0), CHAN5G(165, 0), CHAN5G(166, 0), CHAN5G(168, 0), CHAN5G(170, 0), CHAN5G(172, 0), CHAN5G(174, 0), CHAN5G(176, 0), CHAN5G(178, 0), CHAN5G(180, 0), CHAN5G(182, 0), CHAN5G(184, 0), CHAN5G(186, 0), CHAN5G(188, 0), CHAN5G(190, 0), CHAN5G(192, 0), CHAN5G(194, 0), CHAN5G(196, 0), CHAN5G(198, 0), CHAN5G(200, 0), CHAN5G(202, 0), CHAN5G(204, 0), CHAN5G(206, 0), CHAN5G(208, 0), CHAN5G(210, 0), CHAN5G(212, 0), CHAN5G(214, 0), CHAN5G(216, 0), CHAN5G(218, 0), CHAN5G(220, 0), CHAN5G(222, 0), CHAN5G(224, 0), CHAN5G(226, 0), CHAN5G(228, 0), }; static struct ieee80211_channel b43_5ghz_aphy_chantable[] = { CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0), CHAN5G(108, 0), CHAN5G(112, 0), CHAN5G(116, 0), CHAN5G(120, 0), CHAN5G(124, 0), CHAN5G(128, 0), CHAN5G(132, 0), CHAN5G(136, 0), CHAN5G(140, 0), CHAN5G(149, 0), CHAN5G(153, 0), CHAN5G(157, 0), CHAN5G(161, 0), CHAN5G(165, 0), CHAN5G(184, 0), CHAN5G(188, 0), CHAN5G(192, 0), CHAN5G(196, 0), CHAN5G(200, 0), CHAN5G(204, 0), CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; #undef CHAN5G static struct ieee80211_supported_band b43_band_5GHz_nphy = { .band = IEEE80211_BAND_5GHZ, .channels = b43_5ghz_nphy_chantable, .n_channels = ARRAY_SIZE(b43_5ghz_nphy_chantable), .bitrates = b43_a_ratetable, .n_bitrates = b43_a_ratetable_size, }; static struct ieee80211_supported_band b43_band_5GHz_aphy = { .band = IEEE80211_BAND_5GHZ, .channels = b43_5ghz_aphy_chantable, .n_channels = ARRAY_SIZE(b43_5ghz_aphy_chantable), .bitrates = b43_a_ratetable, .n_bitrates = b43_a_ratetable_size, }; static struct ieee80211_supported_band b43_band_2GHz = { .band = IEEE80211_BAND_2GHZ, .channels = b43_2ghz_chantable, .n_channels = ARRAY_SIZE(b43_2ghz_chantable), .bitrates = b43_g_ratetable, .n_bitrates = b43_g_ratetable_size, }; static void b43_wireless_core_exit(struct b43_wldev *dev); static int b43_wireless_core_init(struct b43_wldev *dev); static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev); static int b43_wireless_core_start(struct b43_wldev *dev); static void b43_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf, u32 changed); static int b43_ratelimit(struct b43_wl *wl) { if (!wl || !wl->current_dev) return 1; if (b43_status(wl->current_dev) < B43_STAT_STARTED) return 1; /* We are up and running. * Ratelimit the messages to avoid DoS over the net. */ return net_ratelimit(); } void b43info(struct b43_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; if (b43_modparam_verbose < B43_VERBOSITY_INFO) return; if (!b43_ratelimit(wl)) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_INFO "b43-%s: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } void b43err(struct b43_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; if (b43_modparam_verbose < B43_VERBOSITY_ERROR) return; if (!b43_ratelimit(wl)) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_ERR "b43-%s ERROR: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } void b43warn(struct b43_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; if (b43_modparam_verbose < B43_VERBOSITY_WARN) return; if (!b43_ratelimit(wl)) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_WARNING "b43-%s warning: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } void b43dbg(struct b43_wl *wl, const char *fmt, ...) { struct va_format vaf; va_list args; if (b43_modparam_verbose < B43_VERBOSITY_DEBUG) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; printk(KERN_DEBUG "b43-%s debug: %pV", (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); va_end(args); } static void b43_ram_write(struct b43_wldev *dev, u16 offset, u32 val) { u32 macctl; B43_WARN_ON(offset % 4 != 0); macctl = b43_read32(dev, B43_MMIO_MACCTL); if (macctl & B43_MACCTL_BE) val = swab32(val); b43_write32(dev, B43_MMIO_RAM_CONTROL, offset); mmiowb(); b43_write32(dev, B43_MMIO_RAM_DATA, val); } static inline void b43_shm_control_word(struct b43_wldev *dev, u16 routing, u16 offset) { u32 control; /* "offset" is the WORD offset. */ control = routing; control <<= 16; control |= offset; b43_write32(dev, B43_MMIO_SHM_CONTROL, control); } u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset) { u32 ret; if (routing == B43_SHM_SHARED) { B43_WARN_ON(offset & 0x0001); if (offset & 0x0003) { /* Unaligned access */ b43_shm_control_word(dev, routing, offset >> 2); ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED); b43_shm_control_word(dev, routing, (offset >> 2) + 1); ret |= ((u32)b43_read16(dev, B43_MMIO_SHM_DATA)) << 16; goto out; } offset >>= 2; } b43_shm_control_word(dev, routing, offset); ret = b43_read32(dev, B43_MMIO_SHM_DATA); out: return ret; } u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset) { u16 ret; if (routing == B43_SHM_SHARED) { B43_WARN_ON(offset & 0x0001); if (offset & 0x0003) { /* Unaligned access */ b43_shm_control_word(dev, routing, offset >> 2); ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED); goto out; } offset >>= 2; } b43_shm_control_word(dev, routing, offset); ret = b43_read16(dev, B43_MMIO_SHM_DATA); out: return ret; } void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value) { if (routing == B43_SHM_SHARED) { B43_WARN_ON(offset & 0x0001); if (offset & 0x0003) { /* Unaligned access */ b43_shm_control_word(dev, routing, offset >> 2); b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, value & 0xFFFF); b43_shm_control_word(dev, routing, (offset >> 2) + 1); b43_write16(dev, B43_MMIO_SHM_DATA, (value >> 16) & 0xFFFF); return; } offset >>= 2; } b43_shm_control_word(dev, routing, offset); b43_write32(dev, B43_MMIO_SHM_DATA, value); } void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value) { if (routing == B43_SHM_SHARED) { B43_WARN_ON(offset & 0x0001); if (offset & 0x0003) { /* Unaligned access */ b43_shm_control_word(dev, routing, offset >> 2); b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, value); return; } offset >>= 2; } b43_shm_control_word(dev, routing, offset); b43_write16(dev, B43_MMIO_SHM_DATA, value); } /* Read HostFlags */ u64 b43_hf_read(struct b43_wldev *dev) { u64 ret; ret = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF3); ret <<= 16; ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF2); ret <<= 16; ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1); return ret; } /* Write HostFlags */ void b43_hf_write(struct b43_wldev *dev, u64 value) { u16 lo, mi, hi; lo = (value & 0x00000000FFFFULL); mi = (value & 0x0000FFFF0000ULL) >> 16; hi = (value & 0xFFFF00000000ULL) >> 32; b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1, lo); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF2, mi); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF3, hi); } /* Read the firmware capabilities bitmask (Opensource firmware only) */ static u16 b43_fwcapa_read(struct b43_wldev *dev) { B43_WARN_ON(!dev->fw.opensource); return b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_FWCAPA); } void b43_tsf_read(struct b43_wldev *dev, u64 *tsf) { u32 low, high; B43_WARN_ON(dev->dev->core_rev < 3); /* The hardware guarantees us an atomic read, if we * read the low register first. */ low = b43_read32(dev, B43_MMIO_REV3PLUS_TSF_LOW); high = b43_read32(dev, B43_MMIO_REV3PLUS_TSF_HIGH); *tsf = high; *tsf <<= 32; *tsf |= low; } static void b43_time_lock(struct b43_wldev *dev) { b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_TBTTHOLD); /* Commit the write */ b43_read32(dev, B43_MMIO_MACCTL); } static void b43_time_unlock(struct b43_wldev *dev) { b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_TBTTHOLD, 0); /* Commit the write */ b43_read32(dev, B43_MMIO_MACCTL); } static void b43_tsf_write_locked(struct b43_wldev *dev, u64 tsf) { u32 low, high; B43_WARN_ON(dev->dev->core_rev < 3); low = tsf; high = (tsf >> 32); /* The hardware guarantees us an atomic write, if we * write the low register first. */ b43_write32(dev, B43_MMIO_REV3PLUS_TSF_LOW, low); mmiowb(); b43_write32(dev, B43_MMIO_REV3PLUS_TSF_HIGH, high); mmiowb(); } void b43_tsf_write(struct b43_wldev *dev, u64 tsf) { b43_time_lock(dev); b43_tsf_write_locked(dev, tsf); b43_time_unlock(dev); } static void b43_macfilter_set(struct b43_wldev *dev, u16 offset, const u8 *mac) { static const u8 zero_addr[ETH_ALEN] = { 0 }; u16 data; if (!mac) mac = zero_addr; offset |= 0x0020; b43_write16(dev, B43_MMIO_MACFILTER_CONTROL, offset); data = mac[0]; data |= mac[1] << 8; b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); data = mac[2]; data |= mac[3] << 8; b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); data = mac[4]; data |= mac[5] << 8; b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); } static void b43_write_mac_bssid_templates(struct b43_wldev *dev) { const u8 *mac; const u8 *bssid; u8 mac_bssid[ETH_ALEN * 2]; int i; u32 tmp; bssid = dev->wl->bssid; mac = dev->wl->mac_addr; b43_macfilter_set(dev, B43_MACFILTER_BSSID, bssid); memcpy(mac_bssid, mac, ETH_ALEN); memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); /* Write our MAC address and BSSID to template ram */ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) { tmp = (u32) (mac_bssid[i + 0]); tmp |= (u32) (mac_bssid[i + 1]) << 8; tmp |= (u32) (mac_bssid[i + 2]) << 16; tmp |= (u32) (mac_bssid[i + 3]) << 24; b43_ram_write(dev, 0x20 + i, tmp); } } static void b43_upload_card_macaddress(struct b43_wldev *dev) { b43_write_mac_bssid_templates(dev); b43_macfilter_set(dev, B43_MACFILTER_SELF, dev->wl->mac_addr); } static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time) { /* slot_time is in usec. */ /* This test used to exit for all but a G PHY. */ if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) return; b43_write16(dev, B43_MMIO_IFSSLOT, 510 + slot_time); /* Shared memory location 0x0010 is the slot time and should be * set to slot_time; however, this register is initially 0 and changing * the value adversely affects the transmit rate for BCM4311 * devices. Until this behavior is unterstood, delete this step * * b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time); */ } static void b43_short_slot_timing_enable(struct b43_wldev *dev) { b43_set_slot_time(dev, 9); } static void b43_short_slot_timing_disable(struct b43_wldev *dev) { b43_set_slot_time(dev, 20); } /* DummyTransmission function, as documented on * http://bcm-v4.sipsolutions.net/802.11/DummyTransmission */ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on) { struct b43_phy *phy = &dev->phy; unsigned int i, max_loop; u16 value; u32 buffer[5] = { 0x00000000, 0x00D40000, 0x00000000, 0x01000000, 0x00000000, }; if (ofdm) { max_loop = 0x1E; buffer[0] = 0x000201CC; } else { max_loop = 0xFA; buffer[0] = 0x000B846E; } for (i = 0; i < 5; i++) b43_ram_write(dev, i * 4, buffer[i]); b43_write16(dev, B43_MMIO_XMTSEL, 0x0000); if (dev->dev->core_rev < 11) b43_write16(dev, B43_MMIO_WEPCTL, 0x0000); else b43_write16(dev, B43_MMIO_WEPCTL, 0x0100); value = (ofdm ? 0x41 : 0x40); b43_write16(dev, B43_MMIO_TXE0_PHYCTL, value); if (phy->type == B43_PHYTYPE_N || phy->type == B43_PHYTYPE_LP || phy->type == B43_PHYTYPE_LCN) b43_write16(dev, B43_MMIO_TXE0_PHYCTL1, 0x1A02); b43_write16(dev, B43_MMIO_TXE0_WM_0, 0x0000); b43_write16(dev, B43_MMIO_TXE0_WM_1, 0x0000); b43_write16(dev, B43_MMIO_XMTTPLATETXPTR, 0x0000); b43_write16(dev, B43_MMIO_XMTTXCNT, 0x0014); b43_write16(dev, B43_MMIO_XMTSEL, 0x0826); b43_write16(dev, B43_MMIO_TXE0_CTL, 0x0000); if (!pa_on && phy->type == B43_PHYTYPE_N) ; /*b43_nphy_pa_override(dev, false) */ switch (phy->type) { case B43_PHYTYPE_N: case B43_PHYTYPE_LCN: b43_write16(dev, B43_MMIO_TXE0_AUX, 0x00D0); break; case B43_PHYTYPE_LP: b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0050); break; default: b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0030); } b43_read16(dev, B43_MMIO_TXE0_AUX); if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) b43_radio_write16(dev, 0x0051, 0x0017); for (i = 0x00; i < max_loop; i++) { value = b43_read16(dev, B43_MMIO_TXE0_STATUS); if (value & 0x0080) break; udelay(10); } for (i = 0x00; i < 0x0A; i++) { value = b43_read16(dev, B43_MMIO_TXE0_STATUS); if (value & 0x0400) break; udelay(10); } for (i = 0x00; i < 0x19; i++) { value = b43_read16(dev, B43_MMIO_IFSSTAT); if (!(value & 0x0100)) break; udelay(10); } if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) b43_radio_write16(dev, 0x0051, 0x0037); } static void key_write(struct b43_wldev *dev, u8 index, u8 algorithm, const u8 *key) { unsigned int i; u32 offset; u16 value; u16 kidx; /* Key index/algo block */ kidx = b43_kidx_to_fw(dev, index); value = ((kidx << 4) | algorithm); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_KEYIDXBLOCK + (kidx * 2), value); /* Write the key to the Key Table Pointer offset */ offset = dev->ktp + (index * B43_SEC_KEYSIZE); for (i = 0; i < B43_SEC_KEYSIZE; i += 2) { value = key[i]; value |= (u16) (key[i + 1]) << 8; b43_shm_write16(dev, B43_SHM_SHARED, offset + i, value); } } static void keymac_write(struct b43_wldev *dev, u8 index, const u8 *addr) { u32 addrtmp[2] = { 0, 0, }; u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; if (b43_new_kidx_api(dev)) pairwise_keys_start = B43_NR_GROUP_KEYS; B43_WARN_ON(index < pairwise_keys_start); /* We have four default TX keys and possibly four default RX keys. * Physical mac 0 is mapped to physical key 4 or 8, depending * on the firmware version. * So we must adjust the index here. */ index -= pairwise_keys_start; B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); if (addr) { addrtmp[0] = addr[0]; addrtmp[0] |= ((u32) (addr[1]) << 8); addrtmp[0] |= ((u32) (addr[2]) << 16); addrtmp[0] |= ((u32) (addr[3]) << 24); addrtmp[1] = addr[4]; addrtmp[1] |= ((u32) (addr[5]) << 8); } /* Receive match transmitter address (RCMTA) mechanism */ b43_shm_write32(dev, B43_SHM_RCMTA, (index * 2) + 0, addrtmp[0]); b43_shm_write16(dev, B43_SHM_RCMTA, (index * 2) + 1, addrtmp[1]); } /* The ucode will use phase1 key with TEK key to decrypt rx packets. * When a packet is received, the iv32 is checked. * - if it doesn't the packet is returned without modification (and software * decryption can be done). That's what happen when iv16 wrap. * - if it does, the rc4 key is computed, and decryption is tried. * Either it will success and B43_RX_MAC_DEC is returned, * either it fails and B43_RX_MAC_DEC|B43_RX_MAC_DECERR is returned * and the packet is not usable (it got modified by the ucode). * So in order to never have B43_RX_MAC_DECERR, we should provide * a iv32 and phase1key that match. Because we drop packets in case of * B43_RX_MAC_DECERR, if we have a correct iv32 but a wrong phase1key, all * packets will be lost without higher layer knowing (ie no resync possible * until next wrap). * * NOTE : this should support 50 key like RCMTA because * (B43_SHM_SH_KEYIDXBLOCK - B43_SHM_SH_TKIPTSCTTAK)/14 = 50 */ static void rx_tkip_phase1_write(struct b43_wldev *dev, u8 index, u32 iv32, u16 *phase1key) { unsigned int i; u32 offset; u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; if (!modparam_hwtkip) return; if (b43_new_kidx_api(dev)) pairwise_keys_start = B43_NR_GROUP_KEYS; B43_WARN_ON(index < pairwise_keys_start); /* We have four default TX keys and possibly four default RX keys. * Physical mac 0 is mapped to physical key 4 or 8, depending * on the firmware version. * So we must adjust the index here. */ index -= pairwise_keys_start; B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); if (b43_debug(dev, B43_DBG_KEYS)) { b43dbg(dev->wl, "rx_tkip_phase1_write : idx 0x%x, iv32 0x%x\n", index, iv32); } /* Write the key to the RX tkip shared mem */ offset = B43_SHM_SH_TKIPTSCTTAK + index * (10 + 4); for (i = 0; i < 10; i += 2) { b43_shm_write16(dev, B43_SHM_SHARED, offset + i, phase1key ? phase1key[i / 2] : 0); } b43_shm_write16(dev, B43_SHM_SHARED, offset + i, iv32); b43_shm_write16(dev, B43_SHM_SHARED, offset + i + 2, iv32 >> 16); } static void b43_op_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; int index = keyconf->hw_key_idx; if (B43_WARN_ON(!modparam_hwtkip)) return; /* This is only called from the RX path through mac80211, where * our mutex is already locked. */ B43_WARN_ON(!mutex_is_locked(&wl->mutex)); dev = wl->current_dev; B43_WARN_ON(!dev || b43_status(dev) < B43_STAT_INITIALIZED); keymac_write(dev, index, NULL); /* First zero out mac to avoid race */ rx_tkip_phase1_write(dev, index, iv32, phase1key); /* only pairwise TKIP keys are supported right now */ if (WARN_ON(!sta)) return; keymac_write(dev, index, sta->addr); } static void do_key_write(struct b43_wldev *dev, u8 index, u8 algorithm, const u8 *key, size_t key_len, const u8 *mac_addr) { u8 buf[B43_SEC_KEYSIZE] = { 0, }; u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; if (b43_new_kidx_api(dev)) pairwise_keys_start = B43_NR_GROUP_KEYS; B43_WARN_ON(index >= ARRAY_SIZE(dev->key)); B43_WARN_ON(key_len > B43_SEC_KEYSIZE); if (index >= pairwise_keys_start) keymac_write(dev, index, NULL); /* First zero out mac. */ if (algorithm == B43_SEC_ALGO_TKIP) { /* * We should provide an initial iv32, phase1key pair. * We could start with iv32=0 and compute the corresponding * phase1key, but this means calling ieee80211_get_tkip_key * with a fake skb (or export other tkip function). * Because we are lazy we hope iv32 won't start with * 0xffffffff and let's b43_op_update_tkip_key provide a * correct pair. */ rx_tkip_phase1_write(dev, index, 0xffffffff, (u16*)buf); } else if (index >= pairwise_keys_start) /* clear it */ rx_tkip_phase1_write(dev, index, 0, NULL); if (key) memcpy(buf, key, key_len); key_write(dev, index, algorithm, buf); if (index >= pairwise_keys_start) keymac_write(dev, index, mac_addr); dev->key[index].algorithm = algorithm; } static int b43_key_write(struct b43_wldev *dev, int index, u8 algorithm, const u8 *key, size_t key_len, const u8 *mac_addr, struct ieee80211_key_conf *keyconf) { int i; int pairwise_keys_start; /* For ALG_TKIP the key is encoded as a 256-bit (32 byte) data block: * - Temporal Encryption Key (128 bits) * - Temporal Authenticator Tx MIC Key (64 bits) * - Temporal Authenticator Rx MIC Key (64 bits) * * Hardware only store TEK */ if (algorithm == B43_SEC_ALGO_TKIP && key_len == 32) key_len = 16; if (key_len > B43_SEC_KEYSIZE) return -EINVAL; for (i = 0; i < ARRAY_SIZE(dev->key); i++) { /* Check that we don't already have this key. */ B43_WARN_ON(dev->key[i].keyconf == keyconf); } if (index < 0) { /* Pairwise key. Get an empty slot for the key. */ if (b43_new_kidx_api(dev)) pairwise_keys_start = B43_NR_GROUP_KEYS; else pairwise_keys_start = B43_NR_GROUP_KEYS * 2; for (i = pairwise_keys_start; i < pairwise_keys_start + B43_NR_PAIRWISE_KEYS; i++) { B43_WARN_ON(i >= ARRAY_SIZE(dev->key)); if (!dev->key[i].keyconf) { /* found empty */ index = i; break; } } if (index < 0) { b43warn(dev->wl, "Out of hardware key memory\n"); return -ENOSPC; } } else B43_WARN_ON(index > 3); do_key_write(dev, index, algorithm, key, key_len, mac_addr); if ((index <= 3) && !b43_new_kidx_api(dev)) { /* Default RX key */ B43_WARN_ON(mac_addr); do_key_write(dev, index + 4, algorithm, key, key_len, NULL); } keyconf->hw_key_idx = index; dev->key[index].keyconf = keyconf; return 0; } static int b43_key_clear(struct b43_wldev *dev, int index) { if (B43_WARN_ON((index < 0) || (index >= ARRAY_SIZE(dev->key)))) return -EINVAL; do_key_write(dev, index, B43_SEC_ALGO_NONE, NULL, B43_SEC_KEYSIZE, NULL); if ((index <= 3) && !b43_new_kidx_api(dev)) { do_key_write(dev, index + 4, B43_SEC_ALGO_NONE, NULL, B43_SEC_KEYSIZE, NULL); } dev->key[index].keyconf = NULL; return 0; } static void b43_clear_keys(struct b43_wldev *dev) { int i, count; if (b43_new_kidx_api(dev)) count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; else count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; for (i = 0; i < count; i++) b43_key_clear(dev, i); } static void b43_dump_keymemory(struct b43_wldev *dev) { unsigned int i, index, count, offset, pairwise_keys_start; u8 mac[ETH_ALEN]; u16 algo; u32 rcmta0; u16 rcmta1; u64 hf; struct b43_key *key; if (!b43_debug(dev, B43_DBG_KEYS)) return; hf = b43_hf_read(dev); b43dbg(dev->wl, "Hardware key memory dump: USEDEFKEYS=%u\n", !!(hf & B43_HF_USEDEFKEYS)); if (b43_new_kidx_api(dev)) { pairwise_keys_start = B43_NR_GROUP_KEYS; count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; } else { pairwise_keys_start = B43_NR_GROUP_KEYS * 2; count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; } for (index = 0; index < count; index++) { key = &(dev->key[index]); printk(KERN_DEBUG "Key slot %02u: %s", index, (key->keyconf == NULL) ? " " : "*"); offset = dev->ktp + (index * B43_SEC_KEYSIZE); for (i = 0; i < B43_SEC_KEYSIZE; i += 2) { u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i); printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF)); } algo = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_KEYIDXBLOCK + (index * 2)); printk(" Algo: %04X/%02X", algo, key->algorithm); if (index >= pairwise_keys_start) { if (key->algorithm == B43_SEC_ALGO_TKIP) { printk(" TKIP: "); offset = B43_SHM_SH_TKIPTSCTTAK + (index - 4) * (10 + 4); for (i = 0; i < 14; i += 2) { u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i); printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF)); } } rcmta0 = b43_shm_read32(dev, B43_SHM_RCMTA, ((index - pairwise_keys_start) * 2) + 0); rcmta1 = b43_shm_read16(dev, B43_SHM_RCMTA, ((index - pairwise_keys_start) * 2) + 1); *((__le32 *)(&mac[0])) = cpu_to_le32(rcmta0); *((__le16 *)(&mac[4])) = cpu_to_le16(rcmta1); printk(" MAC: %pM", mac); } else printk(" DEFAULT KEY"); printk("\n"); } } void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags) { u32 macctl; u16 ucstat; bool hwps; bool awake; int i; B43_WARN_ON((ps_flags & B43_PS_ENABLED) && (ps_flags & B43_PS_DISABLED)); B43_WARN_ON((ps_flags & B43_PS_AWAKE) && (ps_flags & B43_PS_ASLEEP)); if (ps_flags & B43_PS_ENABLED) { hwps = true; } else if (ps_flags & B43_PS_DISABLED) { hwps = false; } else { //TODO: If powersave is not off and FIXME is not set and we are not in adhoc // and thus is not an AP and we are associated, set bit 25 } if (ps_flags & B43_PS_AWAKE) { awake = true; } else if (ps_flags & B43_PS_ASLEEP) { awake = false; } else { //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME, // or we are associated, or FIXME, or the latest PS-Poll packet sent was // successful, set bit26 } /* FIXME: For now we force awake-on and hwps-off */ hwps = false; awake = true; macctl = b43_read32(dev, B43_MMIO_MACCTL); if (hwps) macctl |= B43_MACCTL_HWPS; else macctl &= ~B43_MACCTL_HWPS; if (awake) macctl |= B43_MACCTL_AWAKE; else macctl &= ~B43_MACCTL_AWAKE; b43_write32(dev, B43_MMIO_MACCTL, macctl); /* Commit write */ b43_read32(dev, B43_MMIO_MACCTL); if (awake && dev->dev->core_rev >= 5) { /* Wait for the microcode to wake up. */ for (i = 0; i < 100; i++) { ucstat = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODESTAT); if (ucstat != B43_SHM_SH_UCODESTAT_SLEEP) break; udelay(10); } } } #ifdef CONFIG_B43_BCMA static void b43_bcma_phy_reset(struct b43_wldev *dev) { u32 flags; /* Put PHY into reset */ flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); flags |= B43_BCMA_IOCTL_PHY_RESET; flags |= B43_BCMA_IOCTL_PHY_BW_20MHZ; /* Make 20 MHz def */ bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); udelay(2); /* Take PHY out of reset */ flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); flags &= ~B43_BCMA_IOCTL_PHY_RESET; flags |= BCMA_IOCTL_FGC; bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); udelay(1); /* Do not force clock anymore */ flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); flags &= ~BCMA_IOCTL_FGC; bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); udelay(1); } static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) { b43_device_enable(dev, B43_BCMA_IOCTL_PHY_CLKEN); bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST); b43_bcma_phy_reset(dev); bcma_core_pll_ctl(dev->dev->bdev, 0x300, 0x3000000, true); } #endif static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode) { struct ssb_device *sdev = dev->dev->sdev; u32 tmslow; u32 flags = 0; if (gmode) flags |= B43_TMSLOW_GMODE; flags |= B43_TMSLOW_PHYCLKEN; flags |= B43_TMSLOW_PHYRESET; if (dev->phy.type == B43_PHYTYPE_N) flags |= B43_TMSLOW_PHY_BANDWIDTH_20MHZ; /* Make 20 MHz def */ b43_device_enable(dev, flags); msleep(2); /* Wait for the PLL to turn on. */ /* Now take the PHY out of Reset again */ tmslow = ssb_read32(sdev, SSB_TMSLOW); tmslow |= SSB_TMSLOW_FGC; tmslow &= ~B43_TMSLOW_PHYRESET; ssb_write32(sdev, SSB_TMSLOW, tmslow); ssb_read32(sdev, SSB_TMSLOW); /* flush */ msleep(1); tmslow &= ~SSB_TMSLOW_FGC; ssb_write32(sdev, SSB_TMSLOW, tmslow); ssb_read32(sdev, SSB_TMSLOW); /* flush */ msleep(1); } void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode) { u32 macctl; switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: b43_bcma_wireless_core_reset(dev, gmode); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: b43_ssb_wireless_core_reset(dev, gmode); break; #endif } /* Turn Analog ON, but only if we already know the PHY-type. * This protects against very early setup where we don't know the * PHY-type, yet. wireless_core_reset will be called once again later, * when we know the PHY-type. */ if (dev->phy.ops) dev->phy.ops->switch_analog(dev, 1); macctl = b43_read32(dev, B43_MMIO_MACCTL); macctl &= ~B43_MACCTL_GMODE; if (gmode) macctl |= B43_MACCTL_GMODE; macctl |= B43_MACCTL_IHR_ENABLED; b43_write32(dev, B43_MMIO_MACCTL, macctl); } static void handle_irq_transmit_status(struct b43_wldev *dev) { u32 v0, v1; u16 tmp; struct b43_txstatus stat; while (1) { v0 = b43_read32(dev, B43_MMIO_XMITSTAT_0); if (!(v0 & 0x00000001)) break; v1 = b43_read32(dev, B43_MMIO_XMITSTAT_1); stat.cookie = (v0 >> 16); stat.seq = (v1 & 0x0000FFFF); stat.phy_stat = ((v1 & 0x00FF0000) >> 16); tmp = (v0 & 0x0000FFFF); stat.frame_count = ((tmp & 0xF000) >> 12); stat.rts_count = ((tmp & 0x0F00) >> 8); stat.supp_reason = ((tmp & 0x001C) >> 2); stat.pm_indicated = !!(tmp & 0x0080); stat.intermediate = !!(tmp & 0x0040); stat.for_ampdu = !!(tmp & 0x0020); stat.acked = !!(tmp & 0x0002); b43_handle_txstatus(dev, &stat); } } static void drain_txstatus_queue(struct b43_wldev *dev) { u32 dummy; if (dev->dev->core_rev < 5) return; /* Read all entries from the microcode TXstatus FIFO * and throw them away. */ while (1) { dummy = b43_read32(dev, B43_MMIO_XMITSTAT_0); if (!(dummy & 0x00000001)) break; dummy = b43_read32(dev, B43_MMIO_XMITSTAT_1); } } static u32 b43_jssi_read(struct b43_wldev *dev) { u32 val = 0; val = b43_shm_read16(dev, B43_SHM_SHARED, 0x08A); val <<= 16; val |= b43_shm_read16(dev, B43_SHM_SHARED, 0x088); return val; } static void b43_jssi_write(struct b43_wldev *dev, u32 jssi) { b43_shm_write16(dev, B43_SHM_SHARED, 0x088, (jssi & 0x0000FFFF)); b43_shm_write16(dev, B43_SHM_SHARED, 0x08A, (jssi & 0xFFFF0000) >> 16); } static void b43_generate_noise_sample(struct b43_wldev *dev) { b43_jssi_write(dev, 0x7F7F7F7F); b43_write32(dev, B43_MMIO_MACCMD, b43_read32(dev, B43_MMIO_MACCMD) | B43_MACCMD_BGNOISE); } static void b43_calculate_link_quality(struct b43_wldev *dev) { /* Top half of Link Quality calculation. */ if (dev->phy.type != B43_PHYTYPE_G) return; if (dev->noisecalc.calculation_running) return; dev->noisecalc.calculation_running = true; dev->noisecalc.nr_samples = 0; b43_generate_noise_sample(dev); } static void handle_irq_noise(struct b43_wldev *dev) { struct b43_phy_g *phy = dev->phy.g; u16 tmp; u8 noise[4]; u8 i, j; s32 average; /* Bottom half of Link Quality calculation. */ if (dev->phy.type != B43_PHYTYPE_G) return; /* Possible race condition: It might be possible that the user * changed to a different channel in the meantime since we * started the calculation. We ignore that fact, since it's * not really that much of a problem. The background noise is * an estimation only anyway. Slightly wrong results will get damped * by the averaging of the 8 sample rounds. Additionally the * value is shortlived. So it will be replaced by the next noise * calculation round soon. */ B43_WARN_ON(!dev->noisecalc.calculation_running); *((__le32 *)noise) = cpu_to_le32(b43_jssi_read(dev)); if (noise[0] == 0x7F || noise[1] == 0x7F || noise[2] == 0x7F || noise[3] == 0x7F) goto generate_new; /* Get the noise samples. */ B43_WARN_ON(dev->noisecalc.nr_samples >= 8); i = dev->noisecalc.nr_samples; noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]]; dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]]; dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]]; dev->noisecalc.samples[i][3] = phy->nrssi_lt[noise[3]]; dev->noisecalc.nr_samples++; if (dev->noisecalc.nr_samples == 8) { /* Calculate the Link Quality by the noise samples. */ average = 0; for (i = 0; i < 8; i++) { for (j = 0; j < 4; j++) average += dev->noisecalc.samples[i][j]; } average /= (8 * 4); average *= 125; average += 64; average /= 128; tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x40C); tmp = (tmp / 128) & 0x1F; if (tmp >= 8) average += 2; else average -= 25; if (tmp == 8) average -= 72; else average -= 48; dev->stats.link_noise = average; dev->noisecalc.calculation_running = false; return; } generate_new: b43_generate_noise_sample(dev); } static void handle_irq_tbtt_indication(struct b43_wldev *dev) { if (b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) { ///TODO: PS TBTT } else { if (1 /*FIXME: the last PSpoll frame was sent successfully */ ) b43_power_saving_ctl_bits(dev, 0); } if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) dev->dfq_valid = true; } static void handle_irq_atim_end(struct b43_wldev *dev) { if (dev->dfq_valid) { b43_write32(dev, B43_MMIO_MACCMD, b43_read32(dev, B43_MMIO_MACCMD) | B43_MACCMD_DFQ_VALID); dev->dfq_valid = false; } } static void handle_irq_pmq(struct b43_wldev *dev) { u32 tmp; //TODO: AP mode. while (1) { tmp = b43_read32(dev, B43_MMIO_PS_STATUS); if (!(tmp & 0x00000008)) break; } /* 16bit write is odd, but correct. */ b43_write16(dev, B43_MMIO_PS_STATUS, 0x0002); } static void b43_write_template_common(struct b43_wldev *dev, const u8 *data, u16 size, u16 ram_offset, u16 shm_size_offset, u8 rate) { u32 i, tmp; struct b43_plcp_hdr4 plcp; plcp.data = 0; b43_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); b43_ram_write(dev, ram_offset, le32_to_cpu(plcp.data)); ram_offset += sizeof(u32); /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet. * So leave the first two bytes of the next write blank. */ tmp = (u32) (data[0]) << 16; tmp |= (u32) (data[1]) << 24; b43_ram_write(dev, ram_offset, tmp); ram_offset += sizeof(u32); for (i = 2; i < size; i += sizeof(u32)) { tmp = (u32) (data[i + 0]); if (i + 1 < size) tmp |= (u32) (data[i + 1]) << 8; if (i + 2 < size) tmp |= (u32) (data[i + 2]) << 16; if (i + 3 < size) tmp |= (u32) (data[i + 3]) << 24; b43_ram_write(dev, ram_offset + i - 2, tmp); } b43_shm_write16(dev, B43_SHM_SHARED, shm_size_offset, size + sizeof(struct b43_plcp_hdr6)); } /* Check if the use of the antenna that ieee80211 told us to * use is possible. This will fall back to DEFAULT. * "antenna_nr" is the antenna identifier we got from ieee80211. */ u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev, u8 antenna_nr) { u8 antenna_mask; if (antenna_nr == 0) { /* Zero means "use default antenna". That's always OK. */ return 0; } /* Get the mask of available antennas. */ if (dev->phy.gmode) antenna_mask = dev->dev->bus_sprom->ant_available_bg; else antenna_mask = dev->dev->bus_sprom->ant_available_a; if (!(antenna_mask & (1 << (antenna_nr - 1)))) { /* This antenna is not available. Fall back to default. */ return 0; } return antenna_nr; } /* Convert a b43 antenna number value to the PHY TX control value. */ static u16 b43_antenna_to_phyctl(int antenna) { switch (antenna) { case B43_ANTENNA0: return B43_TXH_PHY_ANT0; case B43_ANTENNA1: return B43_TXH_PHY_ANT1; case B43_ANTENNA2: return B43_TXH_PHY_ANT2; case B43_ANTENNA3: return B43_TXH_PHY_ANT3; case B43_ANTENNA_AUTO0: case B43_ANTENNA_AUTO1: return B43_TXH_PHY_ANT01AUTO; } B43_WARN_ON(1); return 0; } static void b43_write_beacon_template(struct b43_wldev *dev, u16 ram_offset, u16 shm_size_offset) { unsigned int i, len, variable_len; const struct ieee80211_mgmt *bcn; const u8 *ie; bool tim_found = false; unsigned int rate; u16 ctl; int antenna; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); len = min((size_t) dev->wl->current_beacon->len, 0x200 - sizeof(struct b43_plcp_hdr6)); rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; b43_write_template_common(dev, (const u8 *)bcn, len, ram_offset, shm_size_offset, rate); /* Write the PHY TX control parameters. */ antenna = B43_ANTENNA_DEFAULT; antenna = b43_antenna_to_phyctl(antenna); ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL); /* We can't send beacons with short preamble. Would get PHY errors. */ ctl &= ~B43_TXH_PHY_SHORTPRMBL; ctl &= ~B43_TXH_PHY_ANT; ctl &= ~B43_TXH_PHY_ENC; ctl |= antenna; if (b43_is_cck_rate(rate)) ctl |= B43_TXH_PHY_ENC_CCK; else ctl |= B43_TXH_PHY_ENC_OFDM; b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl); /* Find the position of the TIM and the DTIM_period value * and write them to SHM. */ ie = bcn->u.beacon.variable; variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable); for (i = 0; i < variable_len - 2; ) { uint8_t ie_id, ie_len; ie_id = ie[i]; ie_len = ie[i + 1]; if (ie_id == 5) { u16 tim_position; u16 dtim_period; /* This is the TIM Information Element */ /* Check whether the ie_len is in the beacon data range. */ if (variable_len < ie_len + 2 + i) break; /* A valid TIM is at least 4 bytes long. */ if (ie_len < 4) break; tim_found = true; tim_position = sizeof(struct b43_plcp_hdr6); tim_position += offsetof(struct ieee80211_mgmt, u.beacon.variable); tim_position += i; dtim_period = ie[i + 3]; b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_TIMBPOS, tim_position); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_DTIMPER, dtim_period); break; } i += ie_len + 2; } if (!tim_found) { /* * If ucode wants to modify TIM do it behind the beacon, this * will happen, for example, when doing mesh networking. */ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_TIMBPOS, len + sizeof(struct b43_plcp_hdr6)); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_DTIMPER, 0); } b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset); } static void b43_upload_beacon0(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; if (wl->beacon0_uploaded) return; b43_write_beacon_template(dev, 0x68, 0x18); wl->beacon0_uploaded = true; } static void b43_upload_beacon1(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; if (wl->beacon1_uploaded) return; b43_write_beacon_template(dev, 0x468, 0x1A); wl->beacon1_uploaded = true; } static void handle_irq_beacon(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; u32 cmd, beacon0_valid, beacon1_valid; if (!b43_is_mode(wl, NL80211_IFTYPE_AP) && !b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) && !b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) return; /* This is the bottom half of the asynchronous beacon update. */ /* Ignore interrupt in the future. */ dev->irq_mask &= ~B43_IRQ_BEACON; cmd = b43_read32(dev, B43_MMIO_MACCMD); beacon0_valid = (cmd & B43_MACCMD_BEACON0_VALID); beacon1_valid = (cmd & B43_MACCMD_BEACON1_VALID); /* Schedule interrupt manually, if busy. */ if (beacon0_valid && beacon1_valid) { b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_BEACON); dev->irq_mask |= B43_IRQ_BEACON; return; } if (unlikely(wl->beacon_templates_virgin)) { /* We never uploaded a beacon before. * Upload both templates now, but only mark one valid. */ wl->beacon_templates_virgin = false; b43_upload_beacon0(dev); b43_upload_beacon1(dev); cmd = b43_read32(dev, B43_MMIO_MACCMD); cmd |= B43_MACCMD_BEACON0_VALID; b43_write32(dev, B43_MMIO_MACCMD, cmd); } else { if (!beacon0_valid) { b43_upload_beacon0(dev); cmd = b43_read32(dev, B43_MMIO_MACCMD); cmd |= B43_MACCMD_BEACON0_VALID; b43_write32(dev, B43_MMIO_MACCMD, cmd); } else if (!beacon1_valid) { b43_upload_beacon1(dev); cmd = b43_read32(dev, B43_MMIO_MACCMD); cmd |= B43_MACCMD_BEACON1_VALID; b43_write32(dev, B43_MMIO_MACCMD, cmd); } } } static void b43_do_beacon_update_trigger_work(struct b43_wldev *dev) { u32 old_irq_mask = dev->irq_mask; /* update beacon right away or defer to irq */ handle_irq_beacon(dev); if (old_irq_mask != dev->irq_mask) { /* The handler updated the IRQ mask. */ B43_WARN_ON(!dev->irq_mask); if (b43_read32(dev, B43_MMIO_GEN_IRQ_MASK)) { b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); } else { /* Device interrupts are currently disabled. That means * we just ran the hardirq handler and scheduled the * IRQ thread. The thread will write the IRQ mask when * it finished, so there's nothing to do here. Writing * the mask _here_ would incorrectly re-enable IRQs. */ } } } static void b43_beacon_update_trigger_work(struct work_struct *work) { struct b43_wl *wl = container_of(work, struct b43_wl, beacon_update_trigger); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { if (b43_bus_host_is_sdio(dev->dev)) { /* wl->mutex is enough. */ b43_do_beacon_update_trigger_work(dev); mmiowb(); } else { spin_lock_irq(&wl->hardirq_lock); b43_do_beacon_update_trigger_work(dev); mmiowb(); spin_unlock_irq(&wl->hardirq_lock); } } mutex_unlock(&wl->mutex); } /* Asynchronously update the packet templates in template RAM. * Locking: Requires wl->mutex to be locked. */ static void b43_update_templates(struct b43_wl *wl) { struct sk_buff *beacon; /* This is the top half of the ansynchronous beacon update. * The bottom half is the beacon IRQ. * Beacon update must be asynchronous to avoid sending an * invalid beacon. This can happen for example, if the firmware * transmits a beacon while we are updating it. */ /* We could modify the existing beacon and set the aid bit in * the TIM field, but that would probably require resizing and * moving of data within the beacon template. * Simply request a new beacon and let mac80211 do the hard work. */ beacon = ieee80211_beacon_get(wl->hw, wl->vif); if (unlikely(!beacon)) return; if (wl->current_beacon) dev_kfree_skb_any(wl->current_beacon); wl->current_beacon = beacon; wl->beacon0_uploaded = false; wl->beacon1_uploaded = false; ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); } static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int) { b43_time_lock(dev); if (dev->dev->core_rev >= 3) { b43_write32(dev, B43_MMIO_TSF_CFP_REP, (beacon_int << 16)); b43_write32(dev, B43_MMIO_TSF_CFP_START, (beacon_int << 10)); } else { b43_write16(dev, 0x606, (beacon_int >> 6)); b43_write16(dev, 0x610, beacon_int); } b43_time_unlock(dev); b43dbg(dev->wl, "Set beacon interval to %u\n", beacon_int); } static void b43_handle_firmware_panic(struct b43_wldev *dev) { u16 reason; /* Read the register that contains the reason code for the panic. */ reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_FWPANIC_REASON_REG); b43err(dev->wl, "Whoopsy, firmware panic! Reason: %u\n", reason); switch (reason) { default: b43dbg(dev->wl, "The panic reason is unknown.\n"); /* fallthrough */ case B43_FWPANIC_DIE: /* Do not restart the controller or firmware. * The device is nonfunctional from now on. * Restarting would result in this panic to trigger again, * so we avoid that recursion. */ break; case B43_FWPANIC_RESTART: b43_controller_restart(dev, "Microcode panic"); break; } } static void handle_irq_ucode_debug(struct b43_wldev *dev) { unsigned int i, cnt; u16 reason, marker_id, marker_line; __le16 *buf; /* The proprietary firmware doesn't have this IRQ. */ if (!dev->fw.opensource) return; /* Read the register that contains the reason code for this IRQ. */ reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_DEBUGIRQ_REASON_REG); switch (reason) { case B43_DEBUGIRQ_PANIC: b43_handle_firmware_panic(dev); break; case B43_DEBUGIRQ_DUMP_SHM: if (!B43_DEBUG) break; /* Only with driver debugging enabled. */ buf = kmalloc(4096, GFP_ATOMIC); if (!buf) { b43dbg(dev->wl, "SHM-dump: Failed to allocate memory\n"); goto out; } for (i = 0; i < 4096; i += 2) { u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, i); buf[i / 2] = cpu_to_le16(tmp); } b43info(dev->wl, "Shared memory dump:\n"); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 2, buf, 4096, 1); kfree(buf); break; case B43_DEBUGIRQ_DUMP_REGS: if (!B43_DEBUG) break; /* Only with driver debugging enabled. */ b43info(dev->wl, "Microcode register dump:\n"); for (i = 0, cnt = 0; i < 64; i++) { u16 tmp = b43_shm_read16(dev, B43_SHM_SCRATCH, i); if (cnt == 0) printk(KERN_INFO); printk("r%02u: 0x%04X ", i, tmp); cnt++; if (cnt == 6) { printk("\n"); cnt = 0; } } printk("\n"); break; case B43_DEBUGIRQ_MARKER: if (!B43_DEBUG) break; /* Only with driver debugging enabled. */ marker_id = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_MARKER_ID_REG); marker_line = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_MARKER_LINE_REG); b43info(dev->wl, "The firmware just executed the MARKER(%u) " "at line number %u\n", marker_id, marker_line); break; default: b43dbg(dev->wl, "Debug-IRQ triggered for unknown reason: %u\n", reason); } out: /* Acknowledge the debug-IRQ, so the firmware can continue. */ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK); } static void b43_do_interrupt_thread(struct b43_wldev *dev) { u32 reason; u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; u32 merged_dma_reason = 0; int i; if (unlikely(b43_status(dev) != B43_STAT_STARTED)) return; reason = dev->irq_reason; for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { dma_reason[i] = dev->dma_reason[i]; merged_dma_reason |= dma_reason[i]; } if (unlikely(reason & B43_IRQ_MAC_TXERR)) b43err(dev->wl, "MAC transmission error\n"); if (unlikely(reason & B43_IRQ_PHY_TXERR)) { b43err(dev->wl, "PHY transmission error\n"); rmb(); if (unlikely(atomic_dec_and_test(&dev->phy.txerr_cnt))) { atomic_set(&dev->phy.txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); b43err(dev->wl, "Too many PHY TX errors, " "restarting the controller\n"); b43_controller_restart(dev, "PHY TX errors"); } } if (unlikely(merged_dma_reason & (B43_DMAIRQ_FATALMASK | B43_DMAIRQ_NONFATALMASK))) { if (merged_dma_reason & B43_DMAIRQ_FATALMASK) { b43err(dev->wl, "Fatal DMA error: " "0x%08X, 0x%08X, 0x%08X, " "0x%08X, 0x%08X, 0x%08X\n", dma_reason[0], dma_reason[1], dma_reason[2], dma_reason[3], dma_reason[4], dma_reason[5]); b43err(dev->wl, "This device does not support DMA " "on your system. It will now be switched to PIO.\n"); /* Fall back to PIO transfers if we get fatal DMA errors! */ dev->use_pio = true; b43_controller_restart(dev, "DMA error"); return; } if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) { b43err(dev->wl, "DMA error: " "0x%08X, 0x%08X, 0x%08X, " "0x%08X, 0x%08X, 0x%08X\n", dma_reason[0], dma_reason[1], dma_reason[2], dma_reason[3], dma_reason[4], dma_reason[5]); } } if (unlikely(reason & B43_IRQ_UCODE_DEBUG)) handle_irq_ucode_debug(dev); if (reason & B43_IRQ_TBTT_INDI) handle_irq_tbtt_indication(dev); if (reason & B43_IRQ_ATIM_END) handle_irq_atim_end(dev); if (reason & B43_IRQ_BEACON) handle_irq_beacon(dev); if (reason & B43_IRQ_PMQ) handle_irq_pmq(dev); if (reason & B43_IRQ_TXFIFO_FLUSH_OK) ;/* TODO */ if (reason & B43_IRQ_NOISESAMPLE_OK) handle_irq_noise(dev); /* Check the DMA reason registers for received data. */ if (dma_reason[0] & B43_DMAIRQ_RX_DONE) { if (b43_using_pio_transfers(dev)) b43_pio_rx(dev->pio.rx_queue); else b43_dma_rx(dev->dma.rx_ring); } B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE); B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE); B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE); B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE); B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE); if (reason & B43_IRQ_TX_OK) handle_irq_transmit_status(dev); /* Re-enable interrupts on the device by restoring the current interrupt mask. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); #if B43_DEBUG if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { dev->irq_count++; for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { if (reason & (1 << i)) dev->irq_bit_count[i]++; } } #endif } /* Interrupt thread handler. Handles device interrupts in thread context. */ static irqreturn_t b43_interrupt_thread_handler(int irq, void *dev_id) { struct b43_wldev *dev = dev_id; mutex_lock(&dev->wl->mutex); b43_do_interrupt_thread(dev); mmiowb(); mutex_unlock(&dev->wl->mutex); return IRQ_HANDLED; } static irqreturn_t b43_do_interrupt(struct b43_wldev *dev) { u32 reason; /* This code runs under wl->hardirq_lock, but _only_ on non-SDIO busses. * On SDIO, this runs under wl->mutex. */ reason = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); if (reason == 0xffffffff) /* shared IRQ */ return IRQ_NONE; reason &= dev->irq_mask; if (!reason) return IRQ_NONE; dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON) & 0x0001DC00; dev->dma_reason[1] = b43_read32(dev, B43_MMIO_DMA1_REASON) & 0x0000DC00; dev->dma_reason[2] = b43_read32(dev, B43_MMIO_DMA2_REASON) & 0x0000DC00; dev->dma_reason[3] = b43_read32(dev, B43_MMIO_DMA3_REASON) & 0x0001DC00; dev->dma_reason[4] = b43_read32(dev, B43_MMIO_DMA4_REASON) & 0x0000DC00; /* Unused ring dev->dma_reason[5] = b43_read32(dev, B43_MMIO_DMA5_REASON) & 0x0000DC00; */ /* ACK the interrupt. */ b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, reason); b43_write32(dev, B43_MMIO_DMA0_REASON, dev->dma_reason[0]); b43_write32(dev, B43_MMIO_DMA1_REASON, dev->dma_reason[1]); b43_write32(dev, B43_MMIO_DMA2_REASON, dev->dma_reason[2]); b43_write32(dev, B43_MMIO_DMA3_REASON, dev->dma_reason[3]); b43_write32(dev, B43_MMIO_DMA4_REASON, dev->dma_reason[4]); /* Unused ring b43_write32(dev, B43_MMIO_DMA5_REASON, dev->dma_reason[5]); */ /* Disable IRQs on the device. The IRQ thread handler will re-enable them. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); /* Save the reason bitmasks for the IRQ thread handler. */ dev->irq_reason = reason; return IRQ_WAKE_THREAD; } /* Interrupt handler top-half. This runs with interrupts disabled. */ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) { struct b43_wldev *dev = dev_id; irqreturn_t ret; if (unlikely(b43_status(dev) < B43_STAT_STARTED)) return IRQ_NONE; spin_lock(&dev->wl->hardirq_lock); ret = b43_do_interrupt(dev); mmiowb(); spin_unlock(&dev->wl->hardirq_lock); return ret; } /* SDIO interrupt handler. This runs in process context. */ static void b43_sdio_interrupt_handler(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; irqreturn_t ret; mutex_lock(&wl->mutex); ret = b43_do_interrupt(dev); if (ret == IRQ_WAKE_THREAD) b43_do_interrupt_thread(dev); mutex_unlock(&wl->mutex); } void b43_do_release_fw(struct b43_firmware_file *fw) { release_firmware(fw->data); fw->data = NULL; fw->filename = NULL; } static void b43_release_firmware(struct b43_wldev *dev) { b43_do_release_fw(&dev->fw.ucode); b43_do_release_fw(&dev->fw.pcm); b43_do_release_fw(&dev->fw.initvals); b43_do_release_fw(&dev->fw.initvals_band); } static void b43_print_fw_helptext(struct b43_wl *wl, bool error) { const char text[] = "You must go to " \ "http://wireless.kernel.org/en/users/Drivers/b43#devicefirmware " \ "and download the correct firmware for this driver version. " \ "Please carefully read all instructions on this website.\n"; if (error) b43err(wl, text); else b43warn(wl, text); } int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, struct b43_firmware_file *fw) { const struct firmware *blob; struct b43_fw_header *hdr; u32 size; int err; if (!name) { /* Don't fetch anything. Free possibly cached firmware. */ /* FIXME: We should probably keep it anyway, to save some headache * on suspend/resume with multiband devices. */ b43_do_release_fw(fw); return 0; } if (fw->filename) { if ((fw->type == ctx->req_type) && (strcmp(fw->filename, name) == 0)) return 0; /* Already have this fw. */ /* Free the cached firmware first. */ /* FIXME: We should probably do this later after we successfully * got the new fw. This could reduce headache with multiband devices. * We could also redesign this to cache the firmware for all possible * bands all the time. */ b43_do_release_fw(fw); } switch (ctx->req_type) { case B43_FWTYPE_PROPRIETARY: snprintf(ctx->fwname, sizeof(ctx->fwname), "b43%s/%s.fw", modparam_fwpostfix, name); break; case B43_FWTYPE_OPENSOURCE: snprintf(ctx->fwname, sizeof(ctx->fwname), "b43-open%s/%s.fw", modparam_fwpostfix, name); break; default: B43_WARN_ON(1); return -ENOSYS; } err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev); if (err == -ENOENT) { snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), "Firmware file \"%s\" not found\n", ctx->fwname); return err; } else if (err) { snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), "Firmware file \"%s\" request failed (err=%d)\n", ctx->fwname, err); return err; } if (blob->size < sizeof(struct b43_fw_header)) goto err_format; hdr = (struct b43_fw_header *)(blob->data); switch (hdr->type) { case B43_FW_TYPE_UCODE: case B43_FW_TYPE_PCM: size = be32_to_cpu(hdr->size); if (size != blob->size - sizeof(struct b43_fw_header)) goto err_format; /* fallthrough */ case B43_FW_TYPE_IV: if (hdr->ver != 1) goto err_format; break; default: goto err_format; } fw->data = blob; fw->filename = name; fw->type = ctx->req_type; return 0; err_format: snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), "Firmware file \"%s\" format error.\n", ctx->fwname); release_firmware(blob); return -EPROTO; } static int b43_try_request_fw(struct b43_request_fw_context *ctx) { struct b43_wldev *dev = ctx->dev; struct b43_firmware *fw = &ctx->dev->fw; const u8 rev = ctx->dev->dev->core_rev; const char *filename; u32 tmshigh; int err; /* Files for HT and LCN were found by trying one by one */ /* Get microcode */ if ((rev >= 5) && (rev <= 10)) { filename = "ucode5"; } else if ((rev >= 11) && (rev <= 12)) { filename = "ucode11"; } else if (rev == 13) { filename = "ucode13"; } else if (rev == 14) { filename = "ucode14"; } else if (rev == 15) { filename = "ucode15"; } else { switch (dev->phy.type) { case B43_PHYTYPE_N: if (rev >= 16) filename = "ucode16_mimo"; else goto err_no_ucode; break; case B43_PHYTYPE_HT: if (rev == 29) filename = "ucode29_mimo"; else goto err_no_ucode; break; case B43_PHYTYPE_LCN: if (rev == 24) filename = "ucode24_mimo"; else goto err_no_ucode; break; default: goto err_no_ucode; } } err = b43_do_request_fw(ctx, filename, &fw->ucode); if (err) goto err_load; /* Get PCM code */ if ((rev >= 5) && (rev <= 10)) filename = "pcm5"; else if (rev >= 11) filename = NULL; else goto err_no_pcm; fw->pcm_request_failed = false; err = b43_do_request_fw(ctx, filename, &fw->pcm); if (err == -ENOENT) { /* We did not find a PCM file? Not fatal, but * core rev <= 10 must do without hwcrypto then. */ fw->pcm_request_failed = true; } else if (err) goto err_load; /* Get initvals */ switch (dev->phy.type) { case B43_PHYTYPE_A: if ((rev >= 5) && (rev <= 10)) { tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY) filename = "a0g1initvals5"; else filename = "a0g0initvals5"; } else goto err_no_initvals; break; case B43_PHYTYPE_G: if ((rev >= 5) && (rev <= 10)) filename = "b0g0initvals5"; else if (rev >= 13) filename = "b0g0initvals13"; else goto err_no_initvals; break; case B43_PHYTYPE_N: if (rev >= 16) filename = "n0initvals16"; else if ((rev >= 11) && (rev <= 12)) filename = "n0initvals11"; else goto err_no_initvals; break; case B43_PHYTYPE_LP: if (rev == 13) filename = "lp0initvals13"; else if (rev == 14) filename = "lp0initvals14"; else if (rev >= 15) filename = "lp0initvals15"; else goto err_no_initvals; break; case B43_PHYTYPE_HT: if (rev == 29) filename = "ht0initvals29"; else goto err_no_initvals; break; case B43_PHYTYPE_LCN: if (rev == 24) filename = "lcn0initvals24"; else goto err_no_initvals; break; default: goto err_no_initvals; } err = b43_do_request_fw(ctx, filename, &fw->initvals); if (err) goto err_load; /* Get bandswitch initvals */ switch (dev->phy.type) { case B43_PHYTYPE_A: if ((rev >= 5) && (rev <= 10)) { tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY) filename = "a0g1bsinitvals5"; else filename = "a0g0bsinitvals5"; } else if (rev >= 11) filename = NULL; else goto err_no_initvals; break; case B43_PHYTYPE_G: if ((rev >= 5) && (rev <= 10)) filename = "b0g0bsinitvals5"; else if (rev >= 11) filename = NULL; else goto err_no_initvals; break; case B43_PHYTYPE_N: if (rev >= 16) filename = "n0bsinitvals16"; else if ((rev >= 11) && (rev <= 12)) filename = "n0bsinitvals11"; else goto err_no_initvals; break; case B43_PHYTYPE_LP: if (rev == 13) filename = "lp0bsinitvals13"; else if (rev == 14) filename = "lp0bsinitvals14"; else if (rev >= 15) filename = "lp0bsinitvals15"; else goto err_no_initvals; break; case B43_PHYTYPE_HT: if (rev == 29) filename = "ht0bsinitvals29"; else goto err_no_initvals; break; case B43_PHYTYPE_LCN: if (rev == 24) filename = "lcn0bsinitvals24"; else goto err_no_initvals; break; default: goto err_no_initvals; } err = b43_do_request_fw(ctx, filename, &fw->initvals_band); if (err) goto err_load; fw->opensource = (ctx->req_type == B43_FWTYPE_OPENSOURCE); return 0; err_no_ucode: err = ctx->fatal_failure = -EOPNOTSUPP; b43err(dev->wl, "The driver does not know which firmware (ucode) " "is required for your device (wl-core rev %u)\n", rev); goto error; err_no_pcm: err = ctx->fatal_failure = -EOPNOTSUPP; b43err(dev->wl, "The driver does not know which firmware (PCM) " "is required for your device (wl-core rev %u)\n", rev); goto error; err_no_initvals: err = ctx->fatal_failure = -EOPNOTSUPP; b43err(dev->wl, "The driver does not know which firmware (initvals) " "is required for your device (wl-core rev %u)\n", rev); goto error; err_load: /* We failed to load this firmware image. The error message * already is in ctx->errors. Return and let our caller decide * what to do. */ goto error; error: b43_release_firmware(dev); return err; } static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl); static void b43_one_core_detach(struct b43_bus_dev *dev); static void b43_request_firmware(struct work_struct *work) { struct b43_wl *wl = container_of(work, struct b43_wl, firmware_load); struct b43_wldev *dev = wl->current_dev; struct b43_request_fw_context *ctx; unsigned int i; int err; const char *errmsg; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return; ctx->dev = dev; ctx->req_type = B43_FWTYPE_PROPRIETARY; err = b43_try_request_fw(ctx); if (!err) goto start_ieee80211; /* Successfully loaded it. */ /* Was fw version known? */ if (ctx->fatal_failure) goto out; /* proprietary fw not found, try open source */ ctx->req_type = B43_FWTYPE_OPENSOURCE; err = b43_try_request_fw(ctx); if (!err) goto start_ieee80211; /* Successfully loaded it. */ if(ctx->fatal_failure) goto out; /* Could not find a usable firmware. Print the errors. */ for (i = 0; i < B43_NR_FWTYPES; i++) { errmsg = ctx->errors[i]; if (strlen(errmsg)) b43err(dev->wl, errmsg); } b43_print_fw_helptext(dev->wl, 1); goto out; start_ieee80211: wl->hw->queues = B43_QOS_QUEUE_NUM; if (!modparam_qos || dev->fw.opensource) wl->hw->queues = 1; err = ieee80211_register_hw(wl->hw); if (err) goto err_one_core_detach; wl->hw_registred = true; b43_leds_register(wl->current_dev); goto out; err_one_core_detach: b43_one_core_detach(dev->dev); out: kfree(ctx); } static int b43_upload_microcode(struct b43_wldev *dev) { struct wiphy *wiphy = dev->wl->hw->wiphy; const size_t hdr_len = sizeof(struct b43_fw_header); const __be32 *data; unsigned int i, len; u16 fwrev, fwpatch, fwdate, fwtime; u32 tmp, macctl; int err = 0; /* Jump the microcode PSM to offset 0 */ macctl = b43_read32(dev, B43_MMIO_MACCTL); B43_WARN_ON(macctl & B43_MACCTL_PSM_RUN); macctl |= B43_MACCTL_PSM_JMP0; b43_write32(dev, B43_MMIO_MACCTL, macctl); /* Zero out all microcode PSM registers and shared memory. */ for (i = 0; i < 64; i++) b43_shm_write16(dev, B43_SHM_SCRATCH, i, 0); for (i = 0; i < 4096; i += 2) b43_shm_write16(dev, B43_SHM_SHARED, i, 0); /* Upload Microcode. */ data = (__be32 *) (dev->fw.ucode.data->data + hdr_len); len = (dev->fw.ucode.data->size - hdr_len) / sizeof(__be32); b43_shm_control_word(dev, B43_SHM_UCODE | B43_SHM_AUTOINC_W, 0x0000); for (i = 0; i < len; i++) { b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i])); udelay(10); } if (dev->fw.pcm.data) { /* Upload PCM data. */ data = (__be32 *) (dev->fw.pcm.data->data + hdr_len); len = (dev->fw.pcm.data->size - hdr_len) / sizeof(__be32); b43_shm_control_word(dev, B43_SHM_HW, 0x01EA); b43_write32(dev, B43_MMIO_SHM_DATA, 0x00004000); /* No need for autoinc bit in SHM_HW */ b43_shm_control_word(dev, B43_SHM_HW, 0x01EB); for (i = 0; i < len; i++) { b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i])); udelay(10); } } b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_ALL); /* Start the microcode PSM */ b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_JMP0, B43_MACCTL_PSM_RUN); /* Wait for the microcode to load and respond */ i = 0; while (1) { tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); if (tmp == B43_IRQ_MAC_SUSPENDED) break; i++; if (i >= 20) { b43err(dev->wl, "Microcode not responding\n"); b43_print_fw_helptext(dev->wl, 1); err = -ENODEV; goto error; } msleep(50); } b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); /* dummy read */ /* Get and check the revisions. */ fwrev = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEREV); fwpatch = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEPATCH); fwdate = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEDATE); fwtime = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODETIME); if (fwrev <= 0x128) { b43err(dev->wl, "YOUR FIRMWARE IS TOO OLD. Firmware from " "binary drivers older than version 4.x is unsupported. " "You must upgrade your firmware files.\n"); b43_print_fw_helptext(dev->wl, 1); err = -EOPNOTSUPP; goto error; } dev->fw.rev = fwrev; dev->fw.patch = fwpatch; if (dev->fw.rev >= 598) dev->fw.hdr_format = B43_FW_HDR_598; else if (dev->fw.rev >= 410) dev->fw.hdr_format = B43_FW_HDR_410; else dev->fw.hdr_format = B43_FW_HDR_351; WARN_ON(dev->fw.opensource != (fwdate == 0xFFFF)); dev->qos_enabled = dev->wl->hw->queues > 1; /* Default to firmware/hardware crypto acceleration. */ dev->hwcrypto_enabled = true; if (dev->fw.opensource) { u16 fwcapa; /* Patchlevel info is encoded in the "time" field. */ dev->fw.patch = fwtime; b43info(dev->wl, "Loading OpenSource firmware version %u.%u\n", dev->fw.rev, dev->fw.patch); fwcapa = b43_fwcapa_read(dev); if (!(fwcapa & B43_FWCAPA_HWCRYPTO) || dev->fw.pcm_request_failed) { b43info(dev->wl, "Hardware crypto acceleration not supported by firmware\n"); /* Disable hardware crypto and fall back to software crypto. */ dev->hwcrypto_enabled = false; } /* adding QoS support should use an offline discovery mechanism */ WARN(fwcapa & B43_FWCAPA_QOS, "QoS in OpenFW not supported\n"); } else { b43info(dev->wl, "Loading firmware version %u.%u " "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", fwrev, fwpatch, (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF, (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F); if (dev->fw.pcm_request_failed) { b43warn(dev->wl, "No \"pcm5.fw\" firmware file found. " "Hardware accelerated cryptography is disabled.\n"); b43_print_fw_helptext(dev->wl, 0); } } snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u", dev->fw.rev, dev->fw.patch); wiphy->hw_version = dev->dev->core_id; if (dev->fw.hdr_format == B43_FW_HDR_351) { /* We're over the deadline, but we keep support for old fw * until it turns out to be in major conflict with something new. */ b43warn(dev->wl, "You are using an old firmware image. " "Support for old firmware will be removed soon " "(official deadline was July 2008).\n"); b43_print_fw_helptext(dev->wl, 0); } return 0; error: /* Stop the microcode PSM. */ b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, B43_MACCTL_PSM_JMP0); return err; } static int b43_write_initvals(struct b43_wldev *dev, const struct b43_iv *ivals, size_t count, size_t array_size) { const struct b43_iv *iv; u16 offset; size_t i; bool bit32; BUILD_BUG_ON(sizeof(struct b43_iv) != 6); iv = ivals; for (i = 0; i < count; i++) { if (array_size < sizeof(iv->offset_size)) goto err_format; array_size -= sizeof(iv->offset_size); offset = be16_to_cpu(iv->offset_size); bit32 = !!(offset & B43_IV_32BIT); offset &= B43_IV_OFFSET_MASK; if (offset >= 0x1000) goto err_format; if (bit32) { u32 value; if (array_size < sizeof(iv->data.d32)) goto err_format; array_size -= sizeof(iv->data.d32); value = get_unaligned_be32(&iv->data.d32); b43_write32(dev, offset, value); iv = (const struct b43_iv *)((const uint8_t *)iv + sizeof(__be16) + sizeof(__be32)); } else { u16 value; if (array_size < sizeof(iv->data.d16)) goto err_format; array_size -= sizeof(iv->data.d16); value = be16_to_cpu(iv->data.d16); b43_write16(dev, offset, value); iv = (const struct b43_iv *)((const uint8_t *)iv + sizeof(__be16) + sizeof(__be16)); } } if (array_size) goto err_format; return 0; err_format: b43err(dev->wl, "Initial Values Firmware file-format error.\n"); b43_print_fw_helptext(dev->wl, 1); return -EPROTO; } static int b43_upload_initvals(struct b43_wldev *dev) { const size_t hdr_len = sizeof(struct b43_fw_header); const struct b43_fw_header *hdr; struct b43_firmware *fw = &dev->fw; const struct b43_iv *ivals; size_t count; int err; hdr = (const struct b43_fw_header *)(fw->initvals.data->data); ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len); count = be32_to_cpu(hdr->size); err = b43_write_initvals(dev, ivals, count, fw->initvals.data->size - hdr_len); if (err) goto out; if (fw->initvals_band.data) { hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data); ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len); count = be32_to_cpu(hdr->size); err = b43_write_initvals(dev, ivals, count, fw->initvals_band.data->size - hdr_len); if (err) goto out; } out: return err; } /* Initialize the GPIOs * http://bcm-specs.sipsolutions.net/GPIO */ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) { struct ssb_bus *bus = dev->dev->sdev->bus; #ifdef CONFIG_SSB_DRIVER_PCICORE return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev); #else return bus->chipco.dev; #endif } static int b43_gpio_init(struct b43_wldev *dev) { struct ssb_device *gpiodev; u32 mask, set; b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xF); mask = 0x0000001F; set = 0x0000000F; if (dev->dev->chip_id == 0x4301) { mask |= 0x0060; set |= 0x0060; } else if (dev->dev->chip_id == 0x5354) { /* Don't allow overtaking buttons GPIOs */ set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */ } if (0 /* FIXME: conditional unknown */ ) { b43_write16(dev, B43_MMIO_GPIO_MASK, b43_read16(dev, B43_MMIO_GPIO_MASK) | 0x0100); /* BT Coexistance Input */ mask |= 0x0080; set |= 0x0080; /* BT Coexistance Out */ mask |= 0x0100; set |= 0x0100; } if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) { /* PA is controlled by gpio 9, let ucode handle it */ b43_write16(dev, B43_MMIO_GPIO_MASK, b43_read16(dev, B43_MMIO_GPIO_MASK) | 0x0200); mask |= 0x0200; set |= 0x0200; } switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL, (bcma_cc_read32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL) & ~mask) | set); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: gpiodev = b43_ssb_gpio_dev(dev); if (gpiodev) ssb_write32(gpiodev, B43_GPIO_CONTROL, (ssb_read32(gpiodev, B43_GPIO_CONTROL) & ~mask) | set); break; #endif } return 0; } /* Turn off all GPIO stuff. Call this on module unload, for example. */ static void b43_gpio_cleanup(struct b43_wldev *dev) { struct ssb_device *gpiodev; switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL, 0); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: gpiodev = b43_ssb_gpio_dev(dev); if (gpiodev) ssb_write32(gpiodev, B43_GPIO_CONTROL, 0); break; #endif } } /* http://bcm-specs.sipsolutions.net/EnableMac */ void b43_mac_enable(struct b43_wldev *dev) { if (b43_debug(dev, B43_DBG_FIRMWARE)) { u16 fwstate; fwstate = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODESTAT); if ((fwstate != B43_SHM_SH_UCODESTAT_SUSP) && (fwstate != B43_SHM_SH_UCODESTAT_SLEEP)) { b43err(dev->wl, "b43_mac_enable(): The firmware " "should be suspended, but current state is %u\n", fwstate); } } dev->mac_suspended--; B43_WARN_ON(dev->mac_suspended < 0); if (dev->mac_suspended == 0) { b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_ENABLED); b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_MAC_SUSPENDED); /* Commit writes */ b43_read32(dev, B43_MMIO_MACCTL); b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); b43_power_saving_ctl_bits(dev, 0); } } /* http://bcm-specs.sipsolutions.net/SuspendMAC */ void b43_mac_suspend(struct b43_wldev *dev) { int i; u32 tmp; might_sleep(); B43_WARN_ON(dev->mac_suspended < 0); if (dev->mac_suspended == 0) { b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_ENABLED, 0); /* force pci to flush the write */ b43_read32(dev, B43_MMIO_MACCTL); for (i = 35; i; i--) { tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); if (tmp & B43_IRQ_MAC_SUSPENDED) goto out; udelay(10); } /* Hm, it seems this will take some time. Use msleep(). */ for (i = 40; i; i--) { tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); if (tmp & B43_IRQ_MAC_SUSPENDED) goto out; msleep(1); } b43err(dev->wl, "MAC suspend failed\n"); } out: dev->mac_suspended++; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MacPhyClkSet */ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on) { u32 tmp; switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); if (on) tmp |= B43_BCMA_IOCTL_MACPHYCLKEN; else tmp &= ~B43_BCMA_IOCTL_MACPHYCLKEN; bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); if (on) tmp |= B43_TMSLOW_MACPHYCLKEN; else tmp &= ~B43_TMSLOW_MACPHYCLKEN; ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); break; #endif } } static void b43_adjust_opmode(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; u32 ctl; u16 cfp_pretbtt; ctl = b43_read32(dev, B43_MMIO_MACCTL); /* Reset status to STA infrastructure mode. */ ctl &= ~B43_MACCTL_AP; ctl &= ~B43_MACCTL_KEEP_CTL; ctl &= ~B43_MACCTL_KEEP_BADPLCP; ctl &= ~B43_MACCTL_KEEP_BAD; ctl &= ~B43_MACCTL_PROMISC; ctl &= ~B43_MACCTL_BEACPROMISC; ctl |= B43_MACCTL_INFRA; if (b43_is_mode(wl, NL80211_IFTYPE_AP) || b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) ctl |= B43_MACCTL_AP; else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) ctl &= ~B43_MACCTL_INFRA; if (wl->filter_flags & FIF_CONTROL) ctl |= B43_MACCTL_KEEP_CTL; if (wl->filter_flags & FIF_FCSFAIL) ctl |= B43_MACCTL_KEEP_BAD; if (wl->filter_flags & FIF_PLCPFAIL) ctl |= B43_MACCTL_KEEP_BADPLCP; if (wl->filter_flags & FIF_PROMISC_IN_BSS) ctl |= B43_MACCTL_PROMISC; if (wl->filter_flags & FIF_BCN_PRBRESP_PROMISC) ctl |= B43_MACCTL_BEACPROMISC; /* Workaround: On old hardware the HW-MAC-address-filter * doesn't work properly, so always run promisc in filter * it in software. */ if (dev->dev->core_rev <= 4) ctl |= B43_MACCTL_PROMISC; b43_write32(dev, B43_MMIO_MACCTL, ctl); cfp_pretbtt = 2; if ((ctl & B43_MACCTL_INFRA) && !(ctl & B43_MACCTL_AP)) { if (dev->dev->chip_id == 0x4306 && dev->dev->chip_rev == 3) cfp_pretbtt = 100; else cfp_pretbtt = 50; } b43_write16(dev, 0x612, cfp_pretbtt); /* FIXME: We don't currently implement the PMQ mechanism, * so always disable it. If we want to implement PMQ, * we need to enable it here (clear DISCPMQ) in AP mode. */ if (0 /* ctl & B43_MACCTL_AP */) b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_DISCPMQ, 0); else b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_DISCPMQ); } static void b43_rate_memory_write(struct b43_wldev *dev, u16 rate, int is_ofdm) { u16 offset; if (is_ofdm) { offset = 0x480; offset += (b43_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; } else { offset = 0x4C0; offset += (b43_plcp_get_ratecode_cck(rate) & 0x000F) * 2; } b43_shm_write16(dev, B43_SHM_SHARED, offset + 0x20, b43_shm_read16(dev, B43_SHM_SHARED, offset)); } static void b43_rate_memory_init(struct b43_wldev *dev) { switch (dev->phy.type) { case B43_PHYTYPE_A: case B43_PHYTYPE_G: case B43_PHYTYPE_N: case B43_PHYTYPE_LP: case B43_PHYTYPE_HT: case B43_PHYTYPE_LCN: b43_rate_memory_write(dev, B43_OFDM_RATE_6MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_12MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_18MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_24MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_36MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_48MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_54MB, 1); if (dev->phy.type == B43_PHYTYPE_A) break; /* fallthrough */ case B43_PHYTYPE_B: b43_rate_memory_write(dev, B43_CCK_RATE_1MB, 0); b43_rate_memory_write(dev, B43_CCK_RATE_2MB, 0); b43_rate_memory_write(dev, B43_CCK_RATE_5MB, 0); b43_rate_memory_write(dev, B43_CCK_RATE_11MB, 0); break; default: B43_WARN_ON(1); } } /* Set the default values for the PHY TX Control Words. */ static void b43_set_phytxctl_defaults(struct b43_wldev *dev) { u16 ctl = 0; ctl |= B43_TXH_PHY_ENC_CCK; ctl |= B43_TXH_PHY_ANT01AUTO; ctl |= B43_TXH_PHY_TXPWR; b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, ctl); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, ctl); } /* Set the TX-Antenna for management frames sent by firmware. */ static void b43_mgmtframe_txantenna(struct b43_wldev *dev, int antenna) { u16 ant; u16 tmp; ant = b43_antenna_to_phyctl(antenna); /* For ACK/CTS */ tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL); tmp = (tmp & ~B43_TXH_PHY_ANT) | ant; b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, tmp); /* For Probe Resposes */ tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL); tmp = (tmp & ~B43_TXH_PHY_ANT) | ant; b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, tmp); } /* This is the opposite of b43_chip_init() */ static void b43_chip_exit(struct b43_wldev *dev) { b43_phy_exit(dev); b43_gpio_cleanup(dev); /* firmware is released later */ } /* Initialize the chip * http://bcm-specs.sipsolutions.net/ChipInit */ static int b43_chip_init(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; int err; u32 macctl; u16 value16; /* Initialize the MAC control */ macctl = B43_MACCTL_IHR_ENABLED | B43_MACCTL_SHM_ENABLED; if (dev->phy.gmode) macctl |= B43_MACCTL_GMODE; macctl |= B43_MACCTL_INFRA; b43_write32(dev, B43_MMIO_MACCTL, macctl); err = b43_upload_microcode(dev); if (err) goto out; /* firmware is released later */ err = b43_gpio_init(dev); if (err) goto out; /* firmware is released later */ err = b43_upload_initvals(dev); if (err) goto err_gpio_clean; /* Turn the Analog on and initialize the PHY. */ phy->ops->switch_analog(dev, 1); err = b43_phy_init(dev); if (err) goto err_gpio_clean; /* Disable Interference Mitigation. */ if (phy->ops->interf_mitigation) phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE); /* Select the antennae */ if (phy->ops->set_rx_antenna) phy->ops->set_rx_antenna(dev, B43_ANTENNA_DEFAULT); b43_mgmtframe_txantenna(dev, B43_ANTENNA_DEFAULT); if (phy->type == B43_PHYTYPE_B) { value16 = b43_read16(dev, 0x005E); value16 |= 0x0004; b43_write16(dev, 0x005E, value16); } b43_write32(dev, 0x0100, 0x01000000); if (dev->dev->core_rev < 5) b43_write32(dev, 0x010C, 0x01000000); b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_INFRA, 0); b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_INFRA); /* Probe Response Timeout value */ /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ b43_shm_write16(dev, B43_SHM_SHARED, 0x0074, 0x0000); /* Initially set the wireless operation mode. */ b43_adjust_opmode(dev); if (dev->dev->core_rev < 3) { b43_write16(dev, 0x060E, 0x0000); b43_write16(dev, 0x0610, 0x8000); b43_write16(dev, 0x0604, 0x0000); b43_write16(dev, 0x0606, 0x0200); } else { b43_write32(dev, 0x0188, 0x80000000); b43_write32(dev, 0x018C, 0x02000000); } b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, 0x00004000); b43_write32(dev, B43_MMIO_DMA0_IRQ_MASK, 0x0001DC00); b43_write32(dev, B43_MMIO_DMA1_IRQ_MASK, 0x0000DC00); b43_write32(dev, B43_MMIO_DMA2_IRQ_MASK, 0x0000DC00); b43_write32(dev, B43_MMIO_DMA3_IRQ_MASK, 0x0001DC00); b43_write32(dev, B43_MMIO_DMA4_IRQ_MASK, 0x0000DC00); b43_write32(dev, B43_MMIO_DMA5_IRQ_MASK, 0x0000DC00); b43_mac_phy_clock_set(dev, true); switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: /* FIXME: 0xE74 is quite common, but should be read from CC */ b43_write16(dev, B43_MMIO_POWERUP_DELAY, 0xE74); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: b43_write16(dev, B43_MMIO_POWERUP_DELAY, dev->dev->sdev->bus->chipco.fast_pwrup_delay); break; #endif } err = 0; b43dbg(dev->wl, "Chip initialized\n"); out: return err; err_gpio_clean: b43_gpio_cleanup(dev); return err; } static void b43_periodic_every60sec(struct b43_wldev *dev) { const struct b43_phy_operations *ops = dev->phy.ops; if (ops->pwork_60sec) ops->pwork_60sec(dev); /* Force check the TX power emission now. */ b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME); } static void b43_periodic_every30sec(struct b43_wldev *dev) { /* Update device statistics. */ b43_calculate_link_quality(dev); } static void b43_periodic_every15sec(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u16 wdr; if (dev->fw.opensource) { /* Check if the firmware is still alive. * It will reset the watchdog counter to 0 in its idle loop. */ wdr = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_WATCHDOG_REG); if (unlikely(wdr)) { b43err(dev->wl, "Firmware watchdog: The firmware died!\n"); b43_controller_restart(dev, "Firmware watchdog"); return; } else { b43_shm_write16(dev, B43_SHM_SCRATCH, B43_WATCHDOG_REG, 1); } } if (phy->ops->pwork_15sec) phy->ops->pwork_15sec(dev); atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); wmb(); #if B43_DEBUG if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { unsigned int i; b43dbg(dev->wl, "Stats: %7u IRQs/sec, %7u TX/sec, %7u RX/sec\n", dev->irq_count / 15, dev->tx_count / 15, dev->rx_count / 15); dev->irq_count = 0; dev->tx_count = 0; dev->rx_count = 0; for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { if (dev->irq_bit_count[i]) { b43dbg(dev->wl, "Stats: %7u IRQ-%02u/sec (0x%08X)\n", dev->irq_bit_count[i] / 15, i, (1 << i)); dev->irq_bit_count[i] = 0; } } } #endif } static void do_periodic_work(struct b43_wldev *dev) { unsigned int state; state = dev->periodic_state; if (state % 4 == 0) b43_periodic_every60sec(dev); if (state % 2 == 0) b43_periodic_every30sec(dev); b43_periodic_every15sec(dev); } /* Periodic work locking policy: * The whole periodic work handler is protected by * wl->mutex. If another lock is needed somewhere in the * pwork callchain, it's acquired in-place, where it's needed. */ static void b43_periodic_work_handler(struct work_struct *work) { struct b43_wldev *dev = container_of(work, struct b43_wldev, periodic_work.work); struct b43_wl *wl = dev->wl; unsigned long delay; mutex_lock(&wl->mutex); if (unlikely(b43_status(dev) != B43_STAT_STARTED)) goto out; if (b43_debug(dev, B43_DBG_PWORK_STOP)) goto out_requeue; do_periodic_work(dev); dev->periodic_state++; out_requeue: if (b43_debug(dev, B43_DBG_PWORK_FAST)) delay = msecs_to_jiffies(50); else delay = round_jiffies_relative(HZ * 15); ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); out: mutex_unlock(&wl->mutex); } static void b43_periodic_tasks_setup(struct b43_wldev *dev) { struct delayed_work *work = &dev->periodic_work; dev->periodic_state = 0; INIT_DELAYED_WORK(work, b43_periodic_work_handler); ieee80211_queue_delayed_work(dev->wl->hw, work, 0); } /* Check if communication with the device works correctly. */ static int b43_validate_chipaccess(struct b43_wldev *dev) { u32 v, backup0, backup4; backup0 = b43_shm_read32(dev, B43_SHM_SHARED, 0); backup4 = b43_shm_read32(dev, B43_SHM_SHARED, 4); /* Check for read/write and endianness problems. */ b43_shm_write32(dev, B43_SHM_SHARED, 0, 0x55AAAA55); if (b43_shm_read32(dev, B43_SHM_SHARED, 0) != 0x55AAAA55) goto error; b43_shm_write32(dev, B43_SHM_SHARED, 0, 0xAA5555AA); if (b43_shm_read32(dev, B43_SHM_SHARED, 0) != 0xAA5555AA) goto error; /* Check if unaligned 32bit SHM_SHARED access works properly. * However, don't bail out on failure, because it's noncritical. */ b43_shm_write16(dev, B43_SHM_SHARED, 0, 0x1122); b43_shm_write16(dev, B43_SHM_SHARED, 2, 0x3344); b43_shm_write16(dev, B43_SHM_SHARED, 4, 0x5566); b43_shm_write16(dev, B43_SHM_SHARED, 6, 0x7788); if (b43_shm_read32(dev, B43_SHM_SHARED, 2) != 0x55663344) b43warn(dev->wl, "Unaligned 32bit SHM read access is broken\n"); b43_shm_write32(dev, B43_SHM_SHARED, 2, 0xAABBCCDD); if (b43_shm_read16(dev, B43_SHM_SHARED, 0) != 0x1122 || b43_shm_read16(dev, B43_SHM_SHARED, 2) != 0xCCDD || b43_shm_read16(dev, B43_SHM_SHARED, 4) != 0xAABB || b43_shm_read16(dev, B43_SHM_SHARED, 6) != 0x7788) b43warn(dev->wl, "Unaligned 32bit SHM write access is broken\n"); b43_shm_write32(dev, B43_SHM_SHARED, 0, backup0); b43_shm_write32(dev, B43_SHM_SHARED, 4, backup4); if ((dev->dev->core_rev >= 3) && (dev->dev->core_rev <= 10)) { /* The 32bit register shadows the two 16bit registers * with update sideeffects. Validate this. */ b43_write16(dev, B43_MMIO_TSF_CFP_START, 0xAAAA); b43_write32(dev, B43_MMIO_TSF_CFP_START, 0xCCCCBBBB); if (b43_read16(dev, B43_MMIO_TSF_CFP_START_LOW) != 0xBBBB) goto error; if (b43_read16(dev, B43_MMIO_TSF_CFP_START_HIGH) != 0xCCCC) goto error; } b43_write32(dev, B43_MMIO_TSF_CFP_START, 0); v = b43_read32(dev, B43_MMIO_MACCTL); v |= B43_MACCTL_GMODE; if (v != (B43_MACCTL_GMODE | B43_MACCTL_IHR_ENABLED)) goto error; return 0; error: b43err(dev->wl, "Failed to validate the chipaccess\n"); return -ENODEV; } static void b43_security_init(struct b43_wldev *dev) { dev->ktp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_KTP); /* KTP is a word address, but we address SHM bytewise. * So multiply by two. */ dev->ktp *= 2; /* Number of RCMTA address slots */ b43_write16(dev, B43_MMIO_RCMTA_COUNT, B43_NR_PAIRWISE_KEYS); /* Clear the key memory. */ b43_clear_keys(dev); } #ifdef CONFIG_B43_HWRNG static int b43_rng_read(struct hwrng *rng, u32 *data) { struct b43_wl *wl = (struct b43_wl *)rng->priv; struct b43_wldev *dev; int count = -ENODEV; mutex_lock(&wl->mutex); dev = wl->current_dev; if (likely(dev && b43_status(dev) >= B43_STAT_INITIALIZED)) { *data = b43_read16(dev, B43_MMIO_RNG); count = sizeof(u16); } mutex_unlock(&wl->mutex); return count; } #endif /* CONFIG_B43_HWRNG */ static void b43_rng_exit(struct b43_wl *wl) { #ifdef CONFIG_B43_HWRNG if (wl->rng_initialized) hwrng_unregister(&wl->rng); #endif /* CONFIG_B43_HWRNG */ } static int b43_rng_init(struct b43_wl *wl) { int err = 0; #ifdef CONFIG_B43_HWRNG snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); wl->rng.name = wl->rng_name; wl->rng.data_read = b43_rng_read; wl->rng.priv = (unsigned long)wl; wl->rng_initialized = true; err = hwrng_register(&wl->rng); if (err) { wl->rng_initialized = false; b43err(wl, "Failed to register the random " "number generator (%d)\n", err); } #endif /* CONFIG_B43_HWRNG */ return err; } static void b43_tx_work(struct work_struct *work) { struct b43_wl *wl = container_of(work, struct b43_wl, tx_work); struct b43_wldev *dev; struct sk_buff *skb; int queue_num; int err = 0; mutex_lock(&wl->mutex); dev = wl->current_dev; if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) { mutex_unlock(&wl->mutex); return; } for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { while (skb_queue_len(&wl->tx_queue[queue_num])) { skb = skb_dequeue(&wl->tx_queue[queue_num]); if (b43_using_pio_transfers(dev)) err = b43_pio_tx(dev, skb); else err = b43_dma_tx(dev, skb); if (err == -ENOSPC) { wl->tx_queue_stopped[queue_num] = 1; ieee80211_stop_queue(wl->hw, queue_num); skb_queue_head(&wl->tx_queue[queue_num], skb); break; } if (unlikely(err)) dev_kfree_skb(skb); /* Drop it */ err = 0; } if (!err) wl->tx_queue_stopped[queue_num] = 0; } #if B43_DEBUG dev->tx_count++; #endif mutex_unlock(&wl->mutex); } static void b43_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct b43_wl *wl = hw_to_b43_wl(hw); if (unlikely(skb->len < 2 + 2 + 6)) { /* Too short, this can't be a valid frame. */ dev_kfree_skb_any(skb); return; } B43_WARN_ON(skb_shinfo(skb)->nr_frags); skb_queue_tail(&wl->tx_queue[skb_get_queue_mapping(skb)], skb); if (!wl->tx_queue_stopped[skb_get_queue_mapping(skb)]) { ieee80211_queue_work(wl->hw, &wl->tx_work); } else { ieee80211_stop_queue(wl->hw, skb_get_queue_mapping(skb)); } } static void b43_qos_params_upload(struct b43_wldev *dev, const struct ieee80211_tx_queue_params *p, u16 shm_offset) { u16 params[B43_NR_QOSPARAMS]; int bslots, tmp; unsigned int i; if (!dev->qos_enabled) return; bslots = b43_read16(dev, B43_MMIO_RNG) & p->cw_min; memset(¶ms, 0, sizeof(params)); params[B43_QOSPARAM_TXOP] = p->txop * 32; params[B43_QOSPARAM_CWMIN] = p->cw_min; params[B43_QOSPARAM_CWMAX] = p->cw_max; params[B43_QOSPARAM_CWCUR] = p->cw_min; params[B43_QOSPARAM_AIFS] = p->aifs; params[B43_QOSPARAM_BSLOTS] = bslots; params[B43_QOSPARAM_REGGAP] = bslots + p->aifs; for (i = 0; i < ARRAY_SIZE(params); i++) { if (i == B43_QOSPARAM_STATUS) { tmp = b43_shm_read16(dev, B43_SHM_SHARED, shm_offset + (i * 2)); /* Mark the parameters as updated. */ tmp |= 0x100; b43_shm_write16(dev, B43_SHM_SHARED, shm_offset + (i * 2), tmp); } else { b43_shm_write16(dev, B43_SHM_SHARED, shm_offset + (i * 2), params[i]); } } } /* Mapping of mac80211 queue numbers to b43 QoS SHM offsets. */ static const u16 b43_qos_shm_offsets[] = { /* [mac80211-queue-nr] = SHM_OFFSET, */ [0] = B43_QOS_VOICE, [1] = B43_QOS_VIDEO, [2] = B43_QOS_BESTEFFORT, [3] = B43_QOS_BACKGROUND, }; /* Update all QOS parameters in hardware. */ static void b43_qos_upload_all(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; struct b43_qos_params *params; unsigned int i; if (!dev->qos_enabled) return; BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != ARRAY_SIZE(wl->qos_params)); b43_mac_suspend(dev); for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { params = &(wl->qos_params[i]); b43_qos_params_upload(dev, &(params->p), b43_qos_shm_offsets[i]); } b43_mac_enable(dev); } static void b43_qos_clear(struct b43_wl *wl) { struct b43_qos_params *params; unsigned int i; /* Initialize QoS parameters to sane defaults. */ BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != ARRAY_SIZE(wl->qos_params)); for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { params = &(wl->qos_params[i]); switch (b43_qos_shm_offsets[i]) { case B43_QOS_VOICE: params->p.txop = 0; params->p.aifs = 2; params->p.cw_min = 0x0001; params->p.cw_max = 0x0001; break; case B43_QOS_VIDEO: params->p.txop = 0; params->p.aifs = 2; params->p.cw_min = 0x0001; params->p.cw_max = 0x0001; break; case B43_QOS_BESTEFFORT: params->p.txop = 0; params->p.aifs = 3; params->p.cw_min = 0x0001; params->p.cw_max = 0x03FF; break; case B43_QOS_BACKGROUND: params->p.txop = 0; params->p.aifs = 7; params->p.cw_min = 0x0001; params->p.cw_max = 0x03FF; break; default: B43_WARN_ON(1); } } } /* Initialize the core's QOS capabilities */ static void b43_qos_init(struct b43_wldev *dev) { if (!dev->qos_enabled) { /* Disable QOS support. */ b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_EDCF); b43_write16(dev, B43_MMIO_IFSCTL, b43_read16(dev, B43_MMIO_IFSCTL) & ~B43_MMIO_IFSCTL_USE_EDCF); b43dbg(dev->wl, "QoS disabled\n"); return; } /* Upload the current QOS parameters. */ b43_qos_upload_all(dev); /* Enable QOS support. */ b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF); b43_write16(dev, B43_MMIO_IFSCTL, b43_read16(dev, B43_MMIO_IFSCTL) | B43_MMIO_IFSCTL_USE_EDCF); b43dbg(dev->wl, "QoS enabled\n"); } static int b43_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 _queue, const struct ieee80211_tx_queue_params *params) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; unsigned int queue = (unsigned int)_queue; int err = -ENODEV; if (queue >= ARRAY_SIZE(wl->qos_params)) { /* Queue not available or don't support setting * params on this queue. Return success to not * confuse mac80211. */ return 0; } BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != ARRAY_SIZE(wl->qos_params)); mutex_lock(&wl->mutex); dev = wl->current_dev; if (unlikely(!dev || (b43_status(dev) < B43_STAT_INITIALIZED))) goto out_unlock; memcpy(&(wl->qos_params[queue].p), params, sizeof(*params)); b43_mac_suspend(dev); b43_qos_params_upload(dev, &(wl->qos_params[queue].p), b43_qos_shm_offsets[queue]); b43_mac_enable(dev); err = 0; out_unlock: mutex_unlock(&wl->mutex); return err; } static int b43_op_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct b43_wl *wl = hw_to_b43_wl(hw); mutex_lock(&wl->mutex); memcpy(stats, &wl->ieee_stats, sizeof(*stats)); mutex_unlock(&wl->mutex); return 0; } static u64 b43_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; u64 tsf; mutex_lock(&wl->mutex); dev = wl->current_dev; if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) b43_tsf_read(dev, &tsf); else tsf = 0; mutex_unlock(&wl->mutex); return tsf; } static void b43_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) b43_tsf_write(dev, tsf); mutex_unlock(&wl->mutex); } static void b43_put_phy_into_reset(struct b43_wldev *dev) { u32 tmp; switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: b43err(dev->wl, "Putting PHY into reset not supported on BCMA\n"); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); tmp &= ~B43_TMSLOW_GMODE; tmp |= B43_TMSLOW_PHYRESET; tmp |= SSB_TMSLOW_FGC; ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); msleep(1); tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); tmp &= ~SSB_TMSLOW_FGC; tmp |= B43_TMSLOW_PHYRESET; ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); msleep(1); break; #endif } } static const char *band_to_string(enum ieee80211_band band) { switch (band) { case IEEE80211_BAND_5GHZ: return "5"; case IEEE80211_BAND_2GHZ: return "2.4"; default: break; } B43_WARN_ON(1); return ""; } /* Expects wl->mutex locked */ static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan) { struct b43_wldev *up_dev = NULL; struct b43_wldev *down_dev; struct b43_wldev *d; int err; bool uninitialized_var(gmode); int prev_status; /* Find a device and PHY which supports the band. */ list_for_each_entry(d, &wl->devlist, list) { switch (chan->band) { case IEEE80211_BAND_5GHZ: if (d->phy.supports_5ghz) { up_dev = d; gmode = false; } break; case IEEE80211_BAND_2GHZ: if (d->phy.supports_2ghz) { up_dev = d; gmode = true; } break; default: B43_WARN_ON(1); return -EINVAL; } if (up_dev) break; } if (!up_dev) { b43err(wl, "Could not find a device for %s-GHz band operation\n", band_to_string(chan->band)); return -ENODEV; } if ((up_dev == wl->current_dev) && (!!wl->current_dev->phy.gmode == !!gmode)) { /* This device is already running. */ return 0; } b43dbg(wl, "Switching to %s-GHz band\n", band_to_string(chan->band)); down_dev = wl->current_dev; prev_status = b43_status(down_dev); /* Shutdown the currently running core. */ if (prev_status >= B43_STAT_STARTED) down_dev = b43_wireless_core_stop(down_dev); if (prev_status >= B43_STAT_INITIALIZED) b43_wireless_core_exit(down_dev); if (down_dev != up_dev) { /* We switch to a different core, so we put PHY into * RESET on the old core. */ b43_put_phy_into_reset(down_dev); } /* Now start the new core. */ up_dev->phy.gmode = gmode; if (prev_status >= B43_STAT_INITIALIZED) { err = b43_wireless_core_init(up_dev); if (err) { b43err(wl, "Fatal: Could not initialize device for " "selected %s-GHz band\n", band_to_string(chan->band)); goto init_failure; } } if (prev_status >= B43_STAT_STARTED) { err = b43_wireless_core_start(up_dev); if (err) { b43err(wl, "Fatal: Could not start device for " "selected %s-GHz band\n", band_to_string(chan->band)); b43_wireless_core_exit(up_dev); goto init_failure; } } B43_WARN_ON(b43_status(up_dev) != prev_status); wl->current_dev = up_dev; return 0; init_failure: /* Whoops, failed to init the new core. No core is operating now. */ wl->current_dev = NULL; return err; } /* Write the short and long frame retry limit values. */ static void b43_set_retry_limits(struct b43_wldev *dev, unsigned int short_retry, unsigned int long_retry) { /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing * the chip-internal counter. */ short_retry = min(short_retry, (unsigned int)0xF); long_retry = min(long_retry, (unsigned int)0xF); b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_SRLIMIT, short_retry); b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_LRLIMIT, long_retry); } static int b43_op_config(struct ieee80211_hw *hw, u32 changed) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; struct b43_phy *phy; struct ieee80211_conf *conf = &hw->conf; int antenna; int err = 0; bool reload_bss = false; mutex_lock(&wl->mutex); dev = wl->current_dev; /* Switch the band (if necessary). This might change the active core. */ err = b43_switch_band(wl, conf->channel); if (err) goto out_unlock_mutex; /* Need to reload all settings if the core changed */ if (dev != wl->current_dev) { dev = wl->current_dev; changed = ~0; reload_bss = true; } phy = &dev->phy; if (conf_is_ht(conf)) phy->is_40mhz = (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf)); else phy->is_40mhz = false; b43_mac_suspend(dev); if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) b43_set_retry_limits(dev, conf->short_frame_max_tx_count, conf->long_frame_max_tx_count); changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS; if (!changed) goto out_mac_enable; /* Switch to the requested channel. * The firmware takes care of races with the TX handler. */ if (conf->channel->hw_value != phy->channel) b43_switch_channel(dev, conf->channel->hw_value); dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); /* Adjust the desired TX power level. */ if (conf->power_level != 0) { if (conf->power_level != phy->desired_txpower) { phy->desired_txpower = conf->power_level; b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME | B43_TXPWR_IGNORE_TSSI); } } /* Antennas for RX and management frame TX. */ antenna = B43_ANTENNA_DEFAULT; b43_mgmtframe_txantenna(dev, antenna); antenna = B43_ANTENNA_DEFAULT; if (phy->ops->set_rx_antenna) phy->ops->set_rx_antenna(dev, antenna); if (wl->radio_enabled != phy->radio_on) { if (wl->radio_enabled) { b43_software_rfkill(dev, false); b43info(dev->wl, "Radio turned on by software\n"); if (!dev->radio_hw_enable) { b43info(dev->wl, "The hardware RF-kill button " "still turns the radio physically off. " "Press the button to turn it on.\n"); } } else { b43_software_rfkill(dev, true); b43info(dev->wl, "Radio turned off by software\n"); } } out_mac_enable: b43_mac_enable(dev); out_unlock_mutex: mutex_unlock(&wl->mutex); if (wl->vif && reload_bss) b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0); return err; } static void b43_update_basic_rates(struct b43_wldev *dev, u32 brates) { struct ieee80211_supported_band *sband = dev->wl->hw->wiphy->bands[b43_current_band(dev->wl)]; struct ieee80211_rate *rate; int i; u16 basic, direct, offset, basic_offset, rateptr; for (i = 0; i < sband->n_bitrates; i++) { rate = &sband->bitrates[i]; if (b43_is_cck_rate(rate->hw_value)) { direct = B43_SHM_SH_CCKDIRECT; basic = B43_SHM_SH_CCKBASIC; offset = b43_plcp_get_ratecode_cck(rate->hw_value); offset &= 0xF; } else { direct = B43_SHM_SH_OFDMDIRECT; basic = B43_SHM_SH_OFDMBASIC; offset = b43_plcp_get_ratecode_ofdm(rate->hw_value); offset &= 0xF; } rate = ieee80211_get_response_rate(sband, brates, rate->bitrate); if (b43_is_cck_rate(rate->hw_value)) { basic_offset = b43_plcp_get_ratecode_cck(rate->hw_value); basic_offset &= 0xF; } else { basic_offset = b43_plcp_get_ratecode_ofdm(rate->hw_value); basic_offset &= 0xF; } /* * Get the pointer that we need to point to * from the direct map */ rateptr = b43_shm_read16(dev, B43_SHM_SHARED, direct + 2 * basic_offset); /* and write it to the basic map */ b43_shm_write16(dev, B43_SHM_SHARED, basic + 2 * offset, rateptr); } } static void b43_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf, u32 changed) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev || b43_status(dev) < B43_STAT_STARTED) goto out_unlock_mutex; B43_WARN_ON(wl->vif != vif); if (changed & BSS_CHANGED_BSSID) { if (conf->bssid) memcpy(wl->bssid, conf->bssid, ETH_ALEN); else memset(wl->bssid, 0, ETH_ALEN); } if (b43_status(dev) >= B43_STAT_INITIALIZED) { if (changed & BSS_CHANGED_BEACON && (b43_is_mode(wl, NL80211_IFTYPE_AP) || b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || b43_is_mode(wl, NL80211_IFTYPE_ADHOC))) b43_update_templates(wl); if (changed & BSS_CHANGED_BSSID) b43_write_mac_bssid_templates(dev); } b43_mac_suspend(dev); /* Update templates for AP/mesh mode. */ if (changed & BSS_CHANGED_BEACON_INT && (b43_is_mode(wl, NL80211_IFTYPE_AP) || b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) && conf->beacon_int) b43_set_beacon_int(dev, conf->beacon_int); if (changed & BSS_CHANGED_BASIC_RATES) b43_update_basic_rates(dev, conf->basic_rates); if (changed & BSS_CHANGED_ERP_SLOT) { if (conf->use_short_slot) b43_short_slot_timing_enable(dev); else b43_short_slot_timing_disable(dev); } b43_mac_enable(dev); out_unlock_mutex: mutex_unlock(&wl->mutex); } static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; u8 algorithm; u8 index; int err; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (modparam_nohwcrypt) return -ENOSPC; /* User disabled HW-crypto */ if ((vif->type == NL80211_IFTYPE_ADHOC || vif->type == NL80211_IFTYPE_MESH_POINT) && (key->cipher == WLAN_CIPHER_SUITE_TKIP || key->cipher == WLAN_CIPHER_SUITE_CCMP) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { /* * For now, disable hw crypto for the RSN IBSS group keys. This * could be optimized in the future, but until that gets * implemented, use of software crypto for group addressed * frames is a acceptable to allow RSN IBSS to be used. */ return -EOPNOTSUPP; } mutex_lock(&wl->mutex); dev = wl->current_dev; err = -ENODEV; if (!dev || b43_status(dev) < B43_STAT_INITIALIZED) goto out_unlock; if (dev->fw.pcm_request_failed || !dev->hwcrypto_enabled) { /* We don't have firmware for the crypto engine. * Must use software-crypto. */ err = -EOPNOTSUPP; goto out_unlock; } err = -EINVAL; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: algorithm = B43_SEC_ALGO_WEP40; break; case WLAN_CIPHER_SUITE_WEP104: algorithm = B43_SEC_ALGO_WEP104; break; case WLAN_CIPHER_SUITE_TKIP: algorithm = B43_SEC_ALGO_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: algorithm = B43_SEC_ALGO_AES; break; default: B43_WARN_ON(1); goto out_unlock; } index = (u8) (key->keyidx); if (index > 3) goto out_unlock; switch (cmd) { case SET_KEY: if (algorithm == B43_SEC_ALGO_TKIP && (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) || !modparam_hwtkip)) { /* We support only pairwise key */ err = -EOPNOTSUPP; goto out_unlock; } if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { if (WARN_ON(!sta)) { err = -EOPNOTSUPP; goto out_unlock; } /* Pairwise key with an assigned MAC address. */ err = b43_key_write(dev, -1, algorithm, key->key, key->keylen, sta->addr, key); } else { /* Group key */ err = b43_key_write(dev, index, algorithm, key->key, key->keylen, NULL, key); } if (err) goto out_unlock; if (algorithm == B43_SEC_ALGO_WEP40 || algorithm == B43_SEC_ALGO_WEP104) { b43_hf_write(dev, b43_hf_read(dev) | B43_HF_USEDEFKEYS); } else { b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_USEDEFKEYS); } key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; if (algorithm == B43_SEC_ALGO_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; break; case DISABLE_KEY: { err = b43_key_clear(dev, key->hw_key_idx); if (err) goto out_unlock; break; } default: B43_WARN_ON(1); } out_unlock: if (!err) { b43dbg(wl, "%s hardware based encryption for keyidx: %d, " "mac: %pM\n", cmd == SET_KEY ? "Using" : "Disabling", key->keyidx, sta ? sta->addr : bcast_addr); b43_dump_keymemory(dev); } mutex_unlock(&wl->mutex); return err; } static void b43_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *fflags, u64 multicast) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev) { *fflags = 0; goto out_unlock; } *fflags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; changed &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; wl->filter_flags = *fflags; if (changed && b43_status(dev) >= B43_STAT_INITIALIZED) b43_adjust_opmode(dev); out_unlock: mutex_unlock(&wl->mutex); } /* Locking: wl->mutex * Returns the current dev. This might be different from the passed in dev, * because the core might be gone away while we unlocked the mutex. */ static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev) { struct b43_wl *wl; struct b43_wldev *orig_dev; u32 mask; int queue_num; if (!dev) return NULL; wl = dev->wl; redo: if (!dev || b43_status(dev) < B43_STAT_STARTED) return dev; /* Cancel work. Unlock to avoid deadlocks. */ mutex_unlock(&wl->mutex); cancel_delayed_work_sync(&dev->periodic_work); cancel_work_sync(&wl->tx_work); cancel_work_sync(&wl->firmware_load); mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev || b43_status(dev) < B43_STAT_STARTED) { /* Whoops, aliens ate up the device while we were unlocked. */ return dev; } /* Disable interrupts on the device. */ b43_set_status(dev, B43_STAT_INITIALIZED); if (b43_bus_host_is_sdio(dev->dev)) { /* wl->mutex is locked. That is enough. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ } else { spin_lock_irq(&wl->hardirq_lock); b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ spin_unlock_irq(&wl->hardirq_lock); } /* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */ orig_dev = dev; mutex_unlock(&wl->mutex); if (b43_bus_host_is_sdio(dev->dev)) { b43_sdio_free_irq(dev); } else { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) compat_synchronize_threaded_irq(&dev->irq_compat); compat_free_threaded_irq(&dev->irq_compat); #else synchronize_irq(dev->dev->irq); free_irq(dev->dev->irq, dev); #endif } mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev) return dev; if (dev != orig_dev) { if (b43_status(dev) >= B43_STAT_STARTED) goto redo; return dev; } mask = b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); B43_WARN_ON(mask != 0xFFFFFFFF && mask); /* Drain all TX queues. */ for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { while (skb_queue_len(&wl->tx_queue[queue_num])) dev_kfree_skb(skb_dequeue(&wl->tx_queue[queue_num])); } b43_mac_suspend(dev); b43_leds_exit(dev); b43dbg(wl, "Wireless interface stopped\n"); return dev; } /* Locking: wl->mutex */ static int b43_wireless_core_start(struct b43_wldev *dev) { int err; B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); drain_txstatus_queue(dev); if (b43_bus_host_is_sdio(dev->dev)) { err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler); if (err) { b43err(dev->wl, "Cannot request SDIO IRQ\n"); goto out; } } else { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) err = compat_request_threaded_irq(&dev->irq_compat, dev->dev->irq, b43_interrupt_handler, b43_interrupt_thread_handler, IRQF_SHARED, KBUILD_MODNAME, dev); #else err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, b43_interrupt_thread_handler, IRQF_SHARED, KBUILD_MODNAME, dev); #endif if (err) { b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); goto out; } } /* We are ready to run. */ ieee80211_wake_queues(dev->wl->hw); b43_set_status(dev, B43_STAT_STARTED); /* Start data flow (TX/RX). */ b43_mac_enable(dev); b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); /* Start maintenance work */ b43_periodic_tasks_setup(dev); b43_leds_init(dev); b43dbg(dev->wl, "Wireless interface started\n"); out: return err; } static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type) { switch (phy_type) { case B43_PHYTYPE_A: return "A"; case B43_PHYTYPE_B: return "B"; case B43_PHYTYPE_G: return "G"; case B43_PHYTYPE_N: return "N"; case B43_PHYTYPE_LP: return "LP"; case B43_PHYTYPE_SSLPN: return "SSLPN"; case B43_PHYTYPE_HT: return "HT"; case B43_PHYTYPE_LCN: return "LCN"; case B43_PHYTYPE_LCNXN: return "LCNXN"; case B43_PHYTYPE_LCN40: return "LCN40"; case B43_PHYTYPE_AC: return "AC"; } return "UNKNOWN"; } /* Get PHY and RADIO versioning numbers */ static int b43_phy_versioning(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u32 tmp; u8 analog_type; u8 phy_type; u8 phy_rev; u16 radio_manuf; u16 radio_ver; u16 radio_rev; int unsupported = 0; /* Get PHY versioning */ tmp = b43_read16(dev, B43_MMIO_PHY_VER); analog_type = (tmp & B43_PHYVER_ANALOG) >> B43_PHYVER_ANALOG_SHIFT; phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT; phy_rev = (tmp & B43_PHYVER_VERSION); switch (phy_type) { case B43_PHYTYPE_A: if (phy_rev >= 4) unsupported = 1; break; case B43_PHYTYPE_B: if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 && phy_rev != 7) unsupported = 1; break; case B43_PHYTYPE_G: if (phy_rev > 9) unsupported = 1; break; #ifdef CONFIG_B43_PHY_N case B43_PHYTYPE_N: if (phy_rev > 9) unsupported = 1; break; #endif #ifdef CONFIG_B43_PHY_LP case B43_PHYTYPE_LP: if (phy_rev > 2) unsupported = 1; break; #endif #ifdef CONFIG_B43_PHY_HT case B43_PHYTYPE_HT: if (phy_rev > 1) unsupported = 1; break; #endif #ifdef CONFIG_B43_PHY_LCN case B43_PHYTYPE_LCN: if (phy_rev > 1) unsupported = 1; break; #endif default: unsupported = 1; } if (unsupported) { b43err(dev->wl, "FOUND UNSUPPORTED PHY (Analog %u, Type %d (%s), Revision %u)\n", analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev); return -EOPNOTSUPP; } b43info(dev->wl, "Found PHY: Analog %u, Type %d (%s), Revision %u\n", analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev); /* Get RADIO versioning */ if (dev->dev->core_rev >= 24) { u16 radio24[3]; for (tmp = 0; tmp < 3; tmp++) { b43_write16(dev, B43_MMIO_RADIO24_CONTROL, tmp); radio24[tmp] = b43_read16(dev, B43_MMIO_RADIO24_DATA); } /* Broadcom uses "id" for our "ver" and has separated "ver" */ /* radio_ver = (radio24[0] & 0xF0) >> 4; */ radio_manuf = 0x17F; radio_ver = (radio24[2] << 8) | radio24[1]; radio_rev = (radio24[0] & 0xF); } else { if (dev->dev->chip_id == 0x4317) { if (dev->dev->chip_rev == 0) tmp = 0x3205017F; else if (dev->dev->chip_rev == 1) tmp = 0x4205017F; else tmp = 0x5205017F; } else { b43_write16(dev, B43_MMIO_RADIO_CONTROL, B43_RADIOCTL_ID); tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); b43_write16(dev, B43_MMIO_RADIO_CONTROL, B43_RADIOCTL_ID); tmp |= (u32)b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH) << 16; } radio_manuf = (tmp & 0x00000FFF); radio_ver = (tmp & 0x0FFFF000) >> 12; radio_rev = (tmp & 0xF0000000) >> 28; } if (radio_manuf != 0x17F /* Broadcom */) unsupported = 1; switch (phy_type) { case B43_PHYTYPE_A: if (radio_ver != 0x2060) unsupported = 1; if (radio_rev != 1) unsupported = 1; if (radio_manuf != 0x17F) unsupported = 1; break; case B43_PHYTYPE_B: if ((radio_ver & 0xFFF0) != 0x2050) unsupported = 1; break; case B43_PHYTYPE_G: if (radio_ver != 0x2050) unsupported = 1; break; case B43_PHYTYPE_N: if (radio_ver != 0x2055 && radio_ver != 0x2056) unsupported = 1; break; case B43_PHYTYPE_LP: if (radio_ver != 0x2062 && radio_ver != 0x2063) unsupported = 1; break; case B43_PHYTYPE_HT: if (radio_ver != 0x2059) unsupported = 1; break; case B43_PHYTYPE_LCN: if (radio_ver != 0x2064) unsupported = 1; break; default: B43_WARN_ON(1); } if (unsupported) { b43err(dev->wl, "FOUND UNSUPPORTED RADIO " "(Manuf 0x%X, Version 0x%X, Revision %u)\n", radio_manuf, radio_ver, radio_rev); return -EOPNOTSUPP; } b43dbg(dev->wl, "Found Radio: Manuf 0x%X, Version 0x%X, Revision %u\n", radio_manuf, radio_ver, radio_rev); phy->radio_manuf = radio_manuf; phy->radio_ver = radio_ver; phy->radio_rev = radio_rev; phy->analog = analog_type; phy->type = phy_type; phy->rev = phy_rev; return 0; } static void setup_struct_phy_for_init(struct b43_wldev *dev, struct b43_phy *phy) { phy->hardware_power_control = !!modparam_hwpctl; phy->next_txpwr_check_time = jiffies; /* PHY TX errors counter. */ atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); #if B43_DEBUG phy->phy_locked = false; phy->radio_locked = false; #endif } static void setup_struct_wldev_for_init(struct b43_wldev *dev) { dev->dfq_valid = false; /* Assume the radio is enabled. If it's not enabled, the state will * immediately get fixed on the first periodic work run. */ dev->radio_hw_enable = true; /* Stats */ memset(&dev->stats, 0, sizeof(dev->stats)); setup_struct_phy_for_init(dev, &dev->phy); /* IRQ related flags */ dev->irq_reason = 0; memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); dev->irq_mask = B43_IRQ_MASKTEMPLATE; if (b43_modparam_verbose < B43_VERBOSITY_DEBUG) dev->irq_mask &= ~B43_IRQ_PHY_TXERR; dev->mac_suspended = 1; /* Noise calculation context */ memset(&dev->noisecalc, 0, sizeof(dev->noisecalc)); } static void b43_bluetooth_coext_enable(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; u64 hf; if (!modparam_btcoex) return; if (!(sprom->boardflags_lo & B43_BFL_BTCOEXIST)) return; if (dev->phy.type != B43_PHYTYPE_B && !dev->phy.gmode) return; hf = b43_hf_read(dev); if (sprom->boardflags_lo & B43_BFL_BTCMOD) hf |= B43_HF_BTCOEXALT; else hf |= B43_HF_BTCOEX; b43_hf_write(dev, hf); } static void b43_bluetooth_coext_disable(struct b43_wldev *dev) { if (!modparam_btcoex) return; //TODO } static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev) { struct ssb_bus *bus; u32 tmp; if (dev->dev->bus_type != B43_BUS_SSB) return; bus = dev->dev->sdev->bus; if ((bus->chip_id == 0x4311 && bus->chip_rev == 2) || (bus->chip_id == 0x4312)) { tmp = ssb_read32(dev->dev->sdev, SSB_IMCFGLO); tmp &= ~SSB_IMCFGLO_REQTO; tmp &= ~SSB_IMCFGLO_SERTO; tmp |= 0x3; ssb_write32(dev->dev->sdev, SSB_IMCFGLO, tmp); ssb_commit_settings(bus); } } static void b43_set_synth_pu_delay(struct b43_wldev *dev, bool idle) { u16 pu_delay; /* The time value is in microseconds. */ if (dev->phy.type == B43_PHYTYPE_A) pu_delay = 3700; else pu_delay = 1050; if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC) || idle) pu_delay = 500; if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8)) pu_delay = max(pu_delay, (u16)2400); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SPUWKUP, pu_delay); } /* Set the TSF CFP pre-TargetBeaconTransmissionTime. */ static void b43_set_pretbtt(struct b43_wldev *dev) { u16 pretbtt; /* The time value is in microseconds. */ if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) { pretbtt = 2; } else { if (dev->phy.type == B43_PHYTYPE_A) pretbtt = 120; else pretbtt = 250; } b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRETBTT, pretbtt); b43_write16(dev, B43_MMIO_TSF_CFP_PRETBTT, pretbtt); } /* Shutdown a wireless core */ /* Locking: wl->mutex */ static void b43_wireless_core_exit(struct b43_wldev *dev) { B43_WARN_ON(dev && b43_status(dev) > B43_STAT_INITIALIZED); if (!dev || b43_status(dev) != B43_STAT_INITIALIZED) return; /* Unregister HW RNG driver */ b43_rng_exit(dev->wl); b43_set_status(dev, B43_STAT_UNINIT); /* Stop the microcode PSM. */ b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, B43_MACCTL_PSM_JMP0); b43_dma_free(dev); b43_pio_free(dev); b43_chip_exit(dev); dev->phy.ops->switch_analog(dev, 0); if (dev->wl->current_beacon) { dev_kfree_skb_any(dev->wl->current_beacon); dev->wl->current_beacon = NULL; } b43_device_disable(dev, 0); b43_bus_may_powerdown(dev); } /* Initialize a wireless core */ static int b43_wireless_core_init(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy *phy = &dev->phy; int err; u64 hf; B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT); err = b43_bus_powerup(dev, 0); if (err) goto out; if (!b43_device_is_enabled(dev)) b43_wireless_core_reset(dev, phy->gmode); /* Reset all data structures. */ setup_struct_wldev_for_init(dev); phy->ops->prepare_structs(dev); /* Enable IRQ routing to this device. */ switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci, dev->dev->bdev, true); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: ssb_pcicore_dev_irqvecs_enable(&dev->dev->sdev->bus->pcicore, dev->dev->sdev); break; #endif } b43_imcfglo_timeouts_workaround(dev); b43_bluetooth_coext_disable(dev); if (phy->ops->prepare_hardware) { err = phy->ops->prepare_hardware(dev); if (err) goto err_busdown; } err = b43_chip_init(dev); if (err) goto err_busdown; b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_WLCOREREV, dev->dev->core_rev); hf = b43_hf_read(dev); if (phy->type == B43_PHYTYPE_G) { hf |= B43_HF_SYMW; if (phy->rev == 1) hf |= B43_HF_GDCW; if (sprom->boardflags_lo & B43_BFL_PACTRL) hf |= B43_HF_OFDMPABOOST; } if (phy->radio_ver == 0x2050) { if (phy->radio_rev == 6) hf |= B43_HF_4318TSSI; if (phy->radio_rev < 6) hf |= B43_HF_VCORECALC; } if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ #ifdef CONFIG_SSB_DRIVER_PCICORE if (dev->dev->bus_type == B43_BUS_SSB && dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && dev->dev->sdev->bus->pcicore.dev->id.revision <= 10) hf |= B43_HF_PCISCW; /* PCI slow clock workaround. */ #endif hf &= ~B43_HF_SKCFPUP; b43_hf_write(dev, hf); b43_set_retry_limits(dev, B43_DEFAULT_SHORT_RETRY_LIMIT, B43_DEFAULT_LONG_RETRY_LIMIT); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SFFBLIM, 3); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_LFFBLIM, 2); /* Disable sending probe responses from firmware. * Setting the MaxTime to one usec will always trigger * a timeout, so we never send any probe resp. * A timeout of zero is infinite. */ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 1); b43_rate_memory_init(dev); b43_set_phytxctl_defaults(dev); /* Minimum Contention Window */ if (phy->type == B43_PHYTYPE_B) b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F); else b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF); /* Maximum Contention Window */ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); if (b43_bus_host_is_pcmcia(dev->dev) || b43_bus_host_is_sdio(dev->dev)) { dev->__using_pio_transfers = true; err = b43_pio_init(dev); } else if (dev->use_pio) { b43warn(dev->wl, "Forced PIO by use_pio module parameter. " "This should not be needed and will result in lower " "performance.\n"); dev->__using_pio_transfers = true; err = b43_pio_init(dev); } else { dev->__using_pio_transfers = false; err = b43_dma_init(dev); } if (err) goto err_chip_exit; b43_qos_init(dev); b43_set_synth_pu_delay(dev, 1); b43_bluetooth_coext_enable(dev); b43_bus_powerup(dev, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)); b43_upload_card_macaddress(dev); b43_security_init(dev); ieee80211_wake_queues(dev->wl->hw); b43_set_status(dev, B43_STAT_INITIALIZED); /* Register HW RNG driver */ b43_rng_init(dev->wl); out: return err; err_chip_exit: b43_chip_exit(dev); err_busdown: b43_bus_may_powerdown(dev); B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT); return err; } static int b43_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; int err = -EOPNOTSUPP; /* TODO: allow WDS/AP devices to coexist */ if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_MESH_POINT && vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_WDS && vif->type != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; mutex_lock(&wl->mutex); if (wl->operating) goto out_mutex_unlock; b43dbg(wl, "Adding Interface type %d\n", vif->type); dev = wl->current_dev; wl->operating = true; wl->vif = vif; wl->if_type = vif->type; memcpy(wl->mac_addr, vif->addr, ETH_ALEN); b43_adjust_opmode(dev); b43_set_pretbtt(dev); b43_set_synth_pu_delay(dev, 0); b43_upload_card_macaddress(dev); err = 0; out_mutex_unlock: mutex_unlock(&wl->mutex); if (err == 0) b43_op_bss_info_changed(hw, vif, &vif->bss_conf, ~0); return err; } static void b43_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; b43dbg(wl, "Removing Interface type %d\n", vif->type); mutex_lock(&wl->mutex); B43_WARN_ON(!wl->operating); B43_WARN_ON(wl->vif != vif); wl->vif = NULL; wl->operating = false; b43_adjust_opmode(dev); memset(wl->mac_addr, 0, ETH_ALEN); b43_upload_card_macaddress(dev); mutex_unlock(&wl->mutex); } static int b43_op_start(struct ieee80211_hw *hw) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; int did_init = 0; int err = 0; /* Kill all old instance specific information to make sure * the card won't use it in the short timeframe between start * and mac80211 reconfiguring it. */ memset(wl->bssid, 0, ETH_ALEN); memset(wl->mac_addr, 0, ETH_ALEN); wl->filter_flags = 0; wl->radiotap_enabled = false; b43_qos_clear(wl); wl->beacon0_uploaded = false; wl->beacon1_uploaded = false; wl->beacon_templates_virgin = true; wl->radio_enabled = true; mutex_lock(&wl->mutex); if (b43_status(dev) < B43_STAT_INITIALIZED) { err = b43_wireless_core_init(dev); if (err) goto out_mutex_unlock; did_init = 1; } if (b43_status(dev) < B43_STAT_STARTED) { err = b43_wireless_core_start(dev); if (err) { if (did_init) b43_wireless_core_exit(dev); goto out_mutex_unlock; } } /* XXX: only do if device doesn't support rfkill irq */ wiphy_rfkill_start_polling(hw->wiphy); out_mutex_unlock: mutex_unlock(&wl->mutex); /* * Configuration may have been overwritten during initialization. * Reload the configuration, but only if initialization was * successful. Reloading the configuration after a failed init * may hang the system. */ if (!err) b43_op_config(hw, ~0); return err; } static void b43_op_stop(struct ieee80211_hw *hw) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; cancel_work_sync(&(wl->beacon_update_trigger)); if (!dev) goto out; mutex_lock(&wl->mutex); if (b43_status(dev) >= B43_STAT_STARTED) { dev = b43_wireless_core_stop(dev); if (!dev) goto out_unlock; } b43_wireless_core_exit(dev); wl->radio_enabled = false; out_unlock: mutex_unlock(&wl->mutex); out: cancel_work_sync(&(wl->txpower_adjust_work)); } static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { struct b43_wl *wl = hw_to_b43_wl(hw); /* FIXME: add locking */ b43_update_templates(wl); return 0; } static void b43_op_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) { struct b43_wl *wl = hw_to_b43_wl(hw); B43_WARN_ON(!vif || wl->vif != vif); } static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) { /* Disable CFP update during scan on other channels. */ b43_hf_write(dev, b43_hf_read(dev) | B43_HF_SKCFPUP); } mutex_unlock(&wl->mutex); } static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) { /* Re-enable CFP update. */ b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_SKCFPUP); } mutex_unlock(&wl->mutex); } static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; survey->filled = SURVEY_INFO_NOISE_DBM; survey->noise = dev->stats.link_noise; return 0; } static const struct ieee80211_ops b43_hw_ops = { .tx = b43_op_tx, .conf_tx = b43_op_conf_tx, .add_interface = b43_op_add_interface, .remove_interface = b43_op_remove_interface, .config = b43_op_config, .bss_info_changed = b43_op_bss_info_changed, .configure_filter = b43_op_configure_filter, .set_key = b43_op_set_key, .update_tkip_key = b43_op_update_tkip_key, .get_stats = b43_op_get_stats, .get_tsf = b43_op_get_tsf, .set_tsf = b43_op_set_tsf, .start = b43_op_start, .stop = b43_op_stop, .set_tim = b43_op_beacon_set_tim, .sta_notify = b43_op_sta_notify, .sw_scan_start = b43_op_sw_scan_start_notifier, .sw_scan_complete = b43_op_sw_scan_complete_notifier, .get_survey = b43_op_get_survey, .rfkill_poll = b43_rfkill_poll, }; /* Hard-reset the chip. Do not call this directly. * Use b43_controller_restart() */ static void b43_chip_reset(struct work_struct *work) { struct b43_wldev *dev = container_of(work, struct b43_wldev, restart_work); struct b43_wl *wl = dev->wl; int err = 0; int prev_status; mutex_lock(&wl->mutex); prev_status = b43_status(dev); /* Bring the device down... */ if (prev_status >= B43_STAT_STARTED) { dev = b43_wireless_core_stop(dev); if (!dev) { err = -ENODEV; goto out; } } if (prev_status >= B43_STAT_INITIALIZED) b43_wireless_core_exit(dev); /* ...and up again. */ if (prev_status >= B43_STAT_INITIALIZED) { err = b43_wireless_core_init(dev); if (err) goto out; } if (prev_status >= B43_STAT_STARTED) { err = b43_wireless_core_start(dev); if (err) { b43_wireless_core_exit(dev); goto out; } } out: if (err) wl->current_dev = NULL; /* Failed to init the dev. */ mutex_unlock(&wl->mutex); if (err) { b43err(wl, "Controller restart FAILED\n"); return; } /* reload configuration */ b43_op_config(wl->hw, ~0); if (wl->vif) b43_op_bss_info_changed(wl->hw, wl->vif, &wl->vif->bss_conf, ~0); b43info(wl, "Controller restarted\n"); } static int b43_setup_bands(struct b43_wldev *dev, bool have_2ghz_phy, bool have_5ghz_phy) { struct ieee80211_hw *hw = dev->wl->hw; if (have_2ghz_phy) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz; if (dev->phy.type == B43_PHYTYPE_N) { if (have_5ghz_phy) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_nphy; } else { if (have_5ghz_phy) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_aphy; } dev->phy.supports_2ghz = have_2ghz_phy; dev->phy.supports_5ghz = have_5ghz_phy; return 0; } static void b43_wireless_core_detach(struct b43_wldev *dev) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) if (dev->dev->sdev->bus->bustype != SSB_BUSTYPE_SDIO) compat_destroy_threaded_irq(&dev->irq_compat); #endif /* We release firmware that late to not be required to re-request * is all the time when we reinit the core. */ b43_release_firmware(dev); b43_phy_free(dev); } static int b43_wireless_core_attach(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; struct pci_dev *pdev = NULL; int err; u32 tmp; bool have_2ghz_phy = false, have_5ghz_phy = false; /* Do NOT do any device initialization here. * Do it in wireless_core_init() instead. * This function is for gathering basic information about the HW, only. * Also some structs may be set up here. But most likely you want to have * that in core_init(), too. */ #ifdef CONFIG_B43_SSB if (dev->dev->bus_type == B43_BUS_SSB && dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) pdev = dev->dev->sdev->bus->host_pci; #endif err = b43_bus_powerup(dev, 0); if (err) { b43err(wl, "Bus powerup failed\n"); goto out; } /* Get the PHY type. */ switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); have_2ghz_phy = !!(tmp & B43_BCMA_IOST_2G_PHY); have_5ghz_phy = !!(tmp & B43_BCMA_IOST_5G_PHY); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: if (dev->dev->core_rev >= 5) { tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); have_2ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_2GHZ_PHY); have_5ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_5GHZ_PHY); } else B43_WARN_ON(1); break; #endif } dev->phy.gmode = have_2ghz_phy; dev->phy.radio_on = true; b43_wireless_core_reset(dev, dev->phy.gmode); err = b43_phy_versioning(dev); if (err) goto err_powerdown; /* Check if this device supports multiband. */ if (!pdev || (pdev->device != 0x4312 && pdev->device != 0x4319 && pdev->device != 0x4324)) { /* No multiband support. */ have_2ghz_phy = false; have_5ghz_phy = false; switch (dev->phy.type) { case B43_PHYTYPE_A: have_5ghz_phy = true; break; case B43_PHYTYPE_LP: //FIXME not always! #if 0 //FIXME enabling 5GHz causes a NULL pointer dereference have_5ghz_phy = 1; #endif case B43_PHYTYPE_G: case B43_PHYTYPE_N: case B43_PHYTYPE_HT: case B43_PHYTYPE_LCN: have_2ghz_phy = true; break; default: B43_WARN_ON(1); } } if (dev->phy.type == B43_PHYTYPE_A) { /* FIXME */ b43err(wl, "IEEE 802.11a devices are unsupported\n"); err = -EOPNOTSUPP; goto err_powerdown; } if (1 /* disable A-PHY */) { /* FIXME: For now we disable the A-PHY on multi-PHY devices. */ if (dev->phy.type != B43_PHYTYPE_N && dev->phy.type != B43_PHYTYPE_LP) { have_2ghz_phy = true; have_5ghz_phy = false; } } err = b43_phy_allocate(dev); if (err) goto err_powerdown; dev->phy.gmode = have_2ghz_phy; b43_wireless_core_reset(dev, dev->phy.gmode); err = b43_validate_chipaccess(dev); if (err) goto err_phy_free; err = b43_setup_bands(dev, have_2ghz_phy, have_5ghz_phy); if (err) goto err_phy_free; /* Now set some default "current_dev" */ if (!wl->current_dev) wl->current_dev = dev; INIT_WORK(&dev->restart_work, b43_chip_reset); dev->phy.ops->switch_analog(dev, 0); b43_device_disable(dev, 0); b43_bus_may_powerdown(dev); out: return err; err_phy_free: b43_phy_free(dev); err_powerdown: b43_bus_may_powerdown(dev); return err; } static void b43_one_core_detach(struct b43_bus_dev *dev) { struct b43_wldev *wldev; struct b43_wl *wl; /* Do not cancel ieee80211-workqueue based work here. * See comment in b43_remove(). */ wldev = b43_bus_get_wldev(dev); wl = wldev->wl; b43_debugfs_remove_device(wldev); b43_wireless_core_detach(wldev); list_del(&wldev->list); wl->nr_devs--; b43_bus_set_wldev(dev, NULL); kfree(wldev); } static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) { struct b43_wldev *wldev; int err = -ENOMEM; wldev = kzalloc(sizeof(*wldev), GFP_KERNEL); if (!wldev) goto out; wldev->use_pio = b43_modparam_pio; wldev->dev = dev; wldev->wl = wl; b43_set_status(wldev, B43_STAT_UNINIT); wldev->bad_frames_preempt = modparam_bad_frames_preempt; INIT_LIST_HEAD(&wldev->list); err = b43_wireless_core_attach(wldev); if (err) goto err_kfree_wldev; list_add(&wldev->list, &wl->devlist); wl->nr_devs++; b43_bus_set_wldev(dev, wldev); b43_debugfs_add_device(wldev); out: return err; err_kfree_wldev: kfree(wldev); return err; } #define IS_PDEV(pdev, _vendor, _device, _subvendor, _subdevice) ( \ (pdev->vendor == PCI_VENDOR_ID_##_vendor) && \ (pdev->device == _device) && \ (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) && \ (pdev->subsystem_device == _subdevice) ) static void b43_sprom_fixup(struct ssb_bus *bus) { struct pci_dev *pdev; /* boardflags workarounds */ if (bus->boardinfo.vendor == SSB_BOARDVENDOR_DELL && bus->chip_id == 0x4301 && bus->sprom.board_rev == 0x74) bus->sprom.boardflags_lo |= B43_BFL_BTCOEXIST; if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && bus->boardinfo.type == 0x4E && bus->sprom.board_rev > 0x40) bus->sprom.boardflags_lo |= B43_BFL_PACTRL; if (bus->bustype == SSB_BUSTYPE_PCI) { pdev = bus->host_pci; if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) || IS_PDEV(pdev, BROADCOM, 0x4320, DELL, 0x0003) || IS_PDEV(pdev, BROADCOM, 0x4320, HP, 0x12f8) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0014) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013) || IS_PDEV(pdev, BROADCOM, 0x4320, MOTOROLA, 0x7010)) bus->sprom.boardflags_lo &= ~B43_BFL_BTCOEXIST; } } static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl) { struct ieee80211_hw *hw = wl->hw; ssb_set_devtypedata(dev->sdev, NULL); ieee80211_free_hw(hw); } static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) { struct ssb_sprom *sprom = dev->bus_sprom; struct ieee80211_hw *hw; struct b43_wl *wl; char chip_name[6]; int queue_num; hw = ieee80211_alloc_hw(sizeof(*wl), &b43_hw_ops); if (!hw) { b43err(NULL, "Could not allocate ieee80211 device\n"); return ERR_PTR(-ENOMEM); } wl = hw_to_b43_wl(hw); /* fill hw info */ hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_WDS) | BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; wl->hw_registred = false; hw->max_rates = 2; SET_IEEE80211_DEV(hw, dev->dev); if (is_valid_ether_addr(sprom->et1mac)) SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); else SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); /* Initialize struct b43_wl */ wl->hw = hw; mutex_init(&wl->mutex); spin_lock_init(&wl->hardirq_lock); INIT_LIST_HEAD(&wl->devlist); INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work); INIT_WORK(&wl->tx_work, b43_tx_work); /* Initialize queues and flags. */ for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { skb_queue_head_init(&wl->tx_queue[queue_num]); wl->tx_queue_stopped[queue_num] = 0; } snprintf(chip_name, ARRAY_SIZE(chip_name), (dev->chip_id > 0x9999) ? "%d" : "%04X", dev->chip_id); b43info(wl, "Broadcom %s WLAN found (core revision %u)\n", chip_name, dev->core_rev); return wl; } #ifdef CONFIG_B43_BCMA static int b43_bcma_probe(struct bcma_device *core) { struct b43_bus_dev *dev; struct b43_wl *wl; int err; dev = b43_bus_dev_bcma_init(core); if (!dev) return -ENODEV; wl = b43_wireless_init(dev); if (IS_ERR(wl)) { err = PTR_ERR(wl); goto bcma_out; } err = b43_one_core_attach(dev, wl); if (err) goto bcma_err_wireless_exit; /* setup and start work to load firmware */ INIT_WORK(&wl->firmware_load, b43_request_firmware); schedule_work(&wl->firmware_load); bcma_out: return err; bcma_err_wireless_exit: ieee80211_free_hw(wl->hw); return err; } static void b43_bcma_remove(struct bcma_device *core) { struct b43_wldev *wldev = bcma_get_drvdata(core); struct b43_wl *wl = wldev->wl; /* We must cancel any work here before unregistering from ieee80211, * as the ieee80211 unreg will destroy the workqueue. */ cancel_work_sync(&wldev->restart_work); B43_WARN_ON(!wl); if (wl->current_dev == wldev && wl->hw_registred) { b43_leds_stop(wldev); ieee80211_unregister_hw(wl->hw); } b43_one_core_detach(wldev->dev); b43_leds_unregister(wl); ieee80211_free_hw(wl->hw); } static struct bcma_driver b43_bcma_driver = { .name = KBUILD_MODNAME, .id_table = b43_bcma_tbl, .probe = b43_bcma_probe, .remove = b43_bcma_remove, }; #endif #ifdef CONFIG_B43_SSB static int b43_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *id) { struct b43_bus_dev *dev; struct b43_wl *wl; int err; int first = 0; dev = b43_bus_dev_ssb_init(sdev); if (!dev) return -ENOMEM; wl = ssb_get_devtypedata(sdev); if (!wl) { /* Probing the first core. Must setup common struct b43_wl */ first = 1; b43_sprom_fixup(sdev->bus); wl = b43_wireless_init(dev); if (IS_ERR(wl)) { err = PTR_ERR(wl); goto out; } ssb_set_devtypedata(sdev, wl); B43_WARN_ON(ssb_get_devtypedata(sdev) != wl); } err = b43_one_core_attach(dev, wl); if (err) goto err_wireless_exit; /* setup and start work to load firmware */ INIT_WORK(&wl->firmware_load, b43_request_firmware); schedule_work(&wl->firmware_load); out: return err; err_wireless_exit: if (first) b43_wireless_exit(dev, wl); return err; } static void b43_ssb_remove(struct ssb_device *sdev) { struct b43_wl *wl = ssb_get_devtypedata(sdev); struct b43_wldev *wldev = ssb_get_drvdata(sdev); struct b43_bus_dev *dev = wldev->dev; /* We must cancel any work here before unregistering from ieee80211, * as the ieee80211 unreg will destroy the workqueue. */ cancel_work_sync(&wldev->restart_work); B43_WARN_ON(!wl); if (wl->current_dev == wldev && wl->hw_registred) { b43_leds_stop(wldev); ieee80211_unregister_hw(wl->hw); } b43_one_core_detach(dev); if (list_empty(&wl->devlist)) { b43_leds_unregister(wl); /* Last core on the chip unregistered. * We can destroy common struct b43_wl. */ b43_wireless_exit(dev, wl); } } static struct ssb_driver b43_ssb_driver = { .name = KBUILD_MODNAME, .id_table = b43_ssb_tbl, .probe = b43_ssb_probe, .remove = b43_ssb_remove, }; #endif /* CONFIG_B43_SSB */ /* Perform a hardware reset. This can be called from any context. */ void b43_controller_restart(struct b43_wldev *dev, const char *reason) { /* Must avoid requeueing, if we are in shutdown. */ if (b43_status(dev) < B43_STAT_INITIALIZED) return; b43info(dev->wl, "Controller RESET (%s) ...\n", reason); ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } static void b43_print_driverinfo(void) { const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", *feat_leds = "", *feat_sdio = ""; #ifdef CONFIG_B43_PCI_AUTOSELECT feat_pci = "P"; #endif #ifdef CONFIG_B43_PCMCIA feat_pcmcia = "M"; #endif #ifdef CONFIG_B43_PHY_N feat_nphy = "N"; #endif #ifdef CONFIG_B43_LEDS feat_leds = "L"; #endif #ifdef CONFIG_B43_SDIO feat_sdio = "S"; #endif printk(KERN_INFO "Broadcom 43xx driver loaded " "[ Features: %s%s%s%s%s ]\n", feat_pci, feat_pcmcia, feat_nphy, feat_leds, feat_sdio); } static int __init b43_init(void) { int err; b43_debugfs_init(); err = b43_pcmcia_init(); if (err) goto err_dfs_exit; err = b43_sdio_init(); if (err) goto err_pcmcia_exit; #ifdef CONFIG_B43_BCMA err = bcma_driver_register(&b43_bcma_driver); if (err) goto err_sdio_exit; #endif #ifdef CONFIG_B43_SSB err = ssb_driver_register(&b43_ssb_driver); if (err) goto err_bcma_driver_exit; #endif b43_print_driverinfo(); return err; #ifdef CONFIG_B43_SSB err_bcma_driver_exit: #endif #ifdef CONFIG_B43_BCMA bcma_driver_unregister(&b43_bcma_driver); err_sdio_exit: #endif b43_sdio_exit(); err_pcmcia_exit: b43_pcmcia_exit(); err_dfs_exit: b43_debugfs_exit(); return err; } static void __exit b43_exit(void) { #ifdef CONFIG_B43_SSB ssb_driver_unregister(&b43_ssb_driver); #endif #ifdef CONFIG_B43_BCMA bcma_driver_unregister(&b43_bcma_driver); #endif b43_sdio_exit(); b43_pcmcia_exit(); b43_debugfs_exit(); } module_init(b43_init) module_exit(b43_exit) compat-drivers-2012-09-18/drivers/net/wireless/b43/pcmcia.c0000644000175000017500000001017112026211315022451 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver Copyright (c) 2007 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "pcmcia.h" #include #include #include #include #include #include #include static const struct pcmcia_device_id b43_pcmcia_tbl[] = { PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448), PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476), PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl); #ifdef CONFIG_PM static int b43_pcmcia_suspend(struct pcmcia_device *dev) { struct ssb_bus *ssb = dev->priv; return ssb_bus_suspend(ssb); } static int b43_pcmcia_resume(struct pcmcia_device *dev) { struct ssb_bus *ssb = dev->priv; return ssb_bus_resume(ssb); } #else /* CONFIG_PM */ # define b43_pcmcia_suspend NULL # define b43_pcmcia_resume NULL #endif /* CONFIG_PM */ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) { struct ssb_bus *ssb; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) win_req_t win; #endif int err = -ENOMEM; int res = 0; ssb = kzalloc(sizeof(*ssb), GFP_KERNEL); if (!ssb) goto out_error; err = -ENODEV; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) dev->config_flags |= CONF_ENABLE_IRQ; dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 | WIN_USE_WAIT; dev->resource[2]->start = 0; dev->resource[2]->end = SSB_CORE_SIZE; res = pcmcia_request_window(dev, dev->resource[2], 250); #else dev->conf.Attributes = CONF_ENABLE_IRQ; dev->conf.IntType = INT_MEMORY_AND_IO; win.Attributes = WIN_ENABLE | WIN_DATA_WIDTH_16 | WIN_USE_WAIT; win.Base = 0; win.Size = SSB_CORE_SIZE; win.AccessSpeed = 250; res = pcmcia_request_window(dev, &win, &dev->win); #endif if (res != 0) goto err_kfree_ssb; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) res = pcmcia_map_mem_page(dev, dev->resource[2], 0); #else res = pcmcia_map_mem_page(dev, dev->win, 0); #endif if (res != 0) goto err_disable; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (!dev->irq) #else dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; dev->irq.Handler = NULL; /* The handler is registered later. */ res = pcmcia_request_irq(dev, &dev->irq); if (res != 0) #endif goto err_disable; res = pcmcia_enable_device(dev); if (res != 0) goto err_disable; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start); #else err = ssb_bus_pcmciabus_register(ssb, dev, win.Base); #endif if (err) goto err_disable; dev->priv = ssb; return 0; err_disable: pcmcia_disable_device(dev); err_kfree_ssb: kfree(ssb); out_error: printk(KERN_ERR "b43-pcmcia: Initialization failed (%d, %d)\n", res, err); return err; } static void __devexit b43_pcmcia_remove(struct pcmcia_device *dev) { struct ssb_bus *ssb = dev->priv; ssb_bus_unregister(ssb); pcmcia_disable_device(dev); kfree(ssb); dev->priv = NULL; } static struct pcmcia_driver b43_pcmcia_driver = { .owner = THIS_MODULE, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "b43-pcmcia", #else .drv = { .name = "b43-pcmcia", }, #endif .id_table = b43_pcmcia_tbl, .probe = b43_pcmcia_probe, .remove = __devexit_p(b43_pcmcia_remove), .suspend = b43_pcmcia_suspend, .resume = b43_pcmcia_resume, }; int b43_pcmcia_init(void) { return pcmcia_register_driver(&b43_pcmcia_driver); } void b43_pcmcia_exit(void) { pcmcia_unregister_driver(&b43_pcmcia_driver); } compat-drivers-2012-09-18/drivers/net/wireless/b43/b43.h0000644000175000017500000012071012026211315021613 0ustar mcgrofmcgrof#ifndef B43_H_ #define B43_H_ #include #include #include #include #include #include #include #include "debugfs.h" #include "leds.h" #include "rfkill.h" #include "bus.h" #include "lo.h" #include "phy_common.h" #ifdef CONFIG_B43_DEBUG # define B43_DEBUG 1 #else # define B43_DEBUG 0 #endif /* MMIO offsets */ #define B43_MMIO_DMA0_REASON 0x20 #define B43_MMIO_DMA0_IRQ_MASK 0x24 #define B43_MMIO_DMA1_REASON 0x28 #define B43_MMIO_DMA1_IRQ_MASK 0x2C #define B43_MMIO_DMA2_REASON 0x30 #define B43_MMIO_DMA2_IRQ_MASK 0x34 #define B43_MMIO_DMA3_REASON 0x38 #define B43_MMIO_DMA3_IRQ_MASK 0x3C #define B43_MMIO_DMA4_REASON 0x40 #define B43_MMIO_DMA4_IRQ_MASK 0x44 #define B43_MMIO_DMA5_REASON 0x48 #define B43_MMIO_DMA5_IRQ_MASK 0x4C #define B43_MMIO_MACCTL 0x120 /* MAC control */ #define B43_MMIO_MACCMD 0x124 /* MAC command */ #define B43_MMIO_GEN_IRQ_REASON 0x128 #define B43_MMIO_GEN_IRQ_MASK 0x12C #define B43_MMIO_RAM_CONTROL 0x130 #define B43_MMIO_RAM_DATA 0x134 #define B43_MMIO_PS_STATUS 0x140 #define B43_MMIO_RADIO_HWENABLED_HI 0x158 #define B43_MMIO_SHM_CONTROL 0x160 #define B43_MMIO_SHM_DATA 0x164 #define B43_MMIO_SHM_DATA_UNALIGNED 0x166 #define B43_MMIO_XMITSTAT_0 0x170 #define B43_MMIO_XMITSTAT_1 0x174 #define B43_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ #define B43_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ #define B43_MMIO_TSF_CFP_REP 0x188 #define B43_MMIO_TSF_CFP_START 0x18C #define B43_MMIO_TSF_CFP_MAXDUR 0x190 /* 32-bit DMA */ #define B43_MMIO_DMA32_BASE0 0x200 #define B43_MMIO_DMA32_BASE1 0x220 #define B43_MMIO_DMA32_BASE2 0x240 #define B43_MMIO_DMA32_BASE3 0x260 #define B43_MMIO_DMA32_BASE4 0x280 #define B43_MMIO_DMA32_BASE5 0x2A0 /* 64-bit DMA */ #define B43_MMIO_DMA64_BASE0 0x200 #define B43_MMIO_DMA64_BASE1 0x240 #define B43_MMIO_DMA64_BASE2 0x280 #define B43_MMIO_DMA64_BASE3 0x2C0 #define B43_MMIO_DMA64_BASE4 0x300 #define B43_MMIO_DMA64_BASE5 0x340 /* PIO on core rev < 11 */ #define B43_MMIO_PIO_BASE0 0x300 #define B43_MMIO_PIO_BASE1 0x310 #define B43_MMIO_PIO_BASE2 0x320 #define B43_MMIO_PIO_BASE3 0x330 #define B43_MMIO_PIO_BASE4 0x340 #define B43_MMIO_PIO_BASE5 0x350 #define B43_MMIO_PIO_BASE6 0x360 #define B43_MMIO_PIO_BASE7 0x370 /* PIO on core rev >= 11 */ #define B43_MMIO_PIO11_BASE0 0x200 #define B43_MMIO_PIO11_BASE1 0x240 #define B43_MMIO_PIO11_BASE2 0x280 #define B43_MMIO_PIO11_BASE3 0x2C0 #define B43_MMIO_PIO11_BASE4 0x300 #define B43_MMIO_PIO11_BASE5 0x340 #define B43_MMIO_RADIO24_CONTROL 0x3D8 /* core rev >= 24 only */ #define B43_MMIO_RADIO24_DATA 0x3DA /* core rev >= 24 only */ #define B43_MMIO_PHY_VER 0x3E0 #define B43_MMIO_PHY_RADIO 0x3E2 #define B43_MMIO_PHY0 0x3E6 #define B43_MMIO_ANTENNA 0x3E8 #define B43_MMIO_CHANNEL 0x3F0 #define B43_MMIO_CHANNEL_EXT 0x3F4 #define B43_MMIO_RADIO_CONTROL 0x3F6 #define B43_MMIO_RADIO_DATA_HIGH 0x3F8 #define B43_MMIO_RADIO_DATA_LOW 0x3FA #define B43_MMIO_PHY_CONTROL 0x3FC #define B43_MMIO_PHY_DATA 0x3FE #define B43_MMIO_MACFILTER_CONTROL 0x420 #define B43_MMIO_MACFILTER_DATA 0x422 #define B43_MMIO_RCMTA_COUNT 0x43C #define B43_MMIO_PSM_PHY_HDR 0x492 #define B43_MMIO_RADIO_HWENABLED_LO 0x49A #define B43_MMIO_GPIO_CONTROL 0x49C #define B43_MMIO_GPIO_MASK 0x49E #define B43_MMIO_TXE0_CTL 0x500 #define B43_MMIO_TXE0_AUX 0x502 #define B43_MMIO_TXE0_TS_LOC 0x504 #define B43_MMIO_TXE0_TIME_OUT 0x506 #define B43_MMIO_TXE0_WM_0 0x508 #define B43_MMIO_TXE0_WM_1 0x50A #define B43_MMIO_TXE0_PHYCTL 0x50C #define B43_MMIO_TXE0_STATUS 0x50E #define B43_MMIO_TXE0_MMPLCP0 0x510 #define B43_MMIO_TXE0_MMPLCP1 0x512 #define B43_MMIO_TXE0_PHYCTL1 0x514 #define B43_MMIO_XMTFIFODEF 0x520 #define B43_MMIO_XMTFIFO_FRAME_CNT 0x522 /* core rev>= 16 only */ #define B43_MMIO_XMTFIFO_BYTE_CNT 0x524 /* core rev>= 16 only */ #define B43_MMIO_XMTFIFO_HEAD 0x526 /* core rev>= 16 only */ #define B43_MMIO_XMTFIFO_RD_PTR 0x528 /* core rev>= 16 only */ #define B43_MMIO_XMTFIFO_WR_PTR 0x52A /* core rev>= 16 only */ #define B43_MMIO_XMTFIFODEF1 0x52C /* core rev>= 16 only */ #define B43_MMIO_XMTFIFOCMD 0x540 #define B43_MMIO_XMTFIFOFLUSH 0x542 #define B43_MMIO_XMTFIFOTHRESH 0x544 #define B43_MMIO_XMTFIFORDY 0x546 #define B43_MMIO_XMTFIFOPRIRDY 0x548 #define B43_MMIO_XMTFIFORQPRI 0x54A #define B43_MMIO_XMTTPLATETXPTR 0x54C #define B43_MMIO_XMTTPLATEPTR 0x550 #define B43_MMIO_SMPL_CLCT_STRPTR 0x552 /* core rev>= 22 only */ #define B43_MMIO_SMPL_CLCT_STPPTR 0x554 /* core rev>= 22 only */ #define B43_MMIO_SMPL_CLCT_CURPTR 0x556 /* core rev>= 22 only */ #define B43_MMIO_XMTTPLATEDATALO 0x560 #define B43_MMIO_XMTTPLATEDATAHI 0x562 #define B43_MMIO_XMTSEL 0x568 #define B43_MMIO_XMTTXCNT 0x56A #define B43_MMIO_XMTTXSHMADDR 0x56C #define B43_MMIO_TSF_CFP_START_LOW 0x604 #define B43_MMIO_TSF_CFP_START_HIGH 0x606 #define B43_MMIO_TSF_CFP_PRETBTT 0x612 #define B43_MMIO_TSF_CLK_FRAC_LOW 0x62E #define B43_MMIO_TSF_CLK_FRAC_HIGH 0x630 #define B43_MMIO_TSF_0 0x632 /* core rev < 3 only */ #define B43_MMIO_TSF_1 0x634 /* core rev < 3 only */ #define B43_MMIO_TSF_2 0x636 /* core rev < 3 only */ #define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */ #define B43_MMIO_RNG 0x65A #define B43_MMIO_IFSSLOT 0x684 /* Interframe slot time */ #define B43_MMIO_IFSCTL 0x688 /* Interframe space control */ #define B43_MMIO_IFSSTAT 0x690 #define B43_MMIO_IFSMEDBUSYCTL 0x692 #define B43_MMIO_IFTXDUR 0x694 #define B43_MMIO_IFSCTL_USE_EDCF 0x0004 #define B43_MMIO_POWERUP_DELAY 0x6A8 #define B43_MMIO_BTCOEX_CTL 0x6B4 /* Bluetooth Coexistence Control */ #define B43_MMIO_BTCOEX_STAT 0x6B6 /* Bluetooth Coexistence Status */ #define B43_MMIO_BTCOEX_TXCTL 0x6B8 /* Bluetooth Coexistence Transmit Control */ #define B43_MMIO_WEPCTL 0x7C0 /* SPROM boardflags_lo values */ #define B43_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ #define B43_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ #define B43_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ #define B43_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ #define B43_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ #define B43_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ #define B43_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ #define B43_BFL_ENETADM 0x0080 /* has ADMtek switch */ #define B43_BFL_ENETVLAN 0x0100 /* can do vlan */ #define B43_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ #define B43_BFL_NOPCI 0x0400 /* leaves PCI floating */ #define B43_BFL_FEM 0x0800 /* supports the Front End Module */ #define B43_BFL_EXTLNA 0x1000 /* has an external LNA */ #define B43_BFL_HGPA 0x2000 /* had high gain PA */ #define B43_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ #define B43_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ /* SPROM boardflags_hi values */ #define B43_BFH_NOPA 0x0001 /* has no PA */ #define B43_BFH_RSSIINV 0x0002 /* RSSI uses positive slope (not TSSI) */ #define B43_BFH_PAREF 0x0004 /* uses the PARef LDO */ #define B43_BFH_3TSWITCH 0x0008 /* uses a triple throw switch shared * with bluetooth */ #define B43_BFH_PHASESHIFT 0x0010 /* can support phase shifter */ #define B43_BFH_BUCKBOOST 0x0020 /* has buck/booster */ #define B43_BFH_FEM_BT 0x0040 /* has FEM and switch to share antenna * with bluetooth */ #define B43_BFH_NOCBUCK 0x0080 #define B43_BFH_PALDO 0x0200 #define B43_BFH_EXTLNA_5GHZ 0x1000 /* has an external LNA (5GHz mode) */ /* SPROM boardflags2_lo values */ #define B43_BFL2_RXBB_INT_REG_DIS 0x0001 /* external RX BB regulator present */ #define B43_BFL2_APLL_WAR 0x0002 /* alternative A-band PLL settings implemented */ #define B43_BFL2_TXPWRCTRL_EN 0x0004 /* permits enabling TX Power Control */ #define B43_BFL2_2X4_DIV 0x0008 /* 2x4 diversity switch */ #define B43_BFL2_5G_PWRGAIN 0x0010 /* supports 5G band power gain */ #define B43_BFL2_PCIEWAR_OVR 0x0020 /* overrides ASPM and Clkreq settings */ #define B43_BFL2_CAESERS_BRD 0x0040 /* is Caesers board (unused) */ #define B43_BFL2_BTC3WIRE 0x0080 /* used 3-wire bluetooth coexist */ #define B43_BFL2_SKWRKFEM_BRD 0x0100 /* 4321mcm93 uses Skyworks FEM */ #define B43_BFL2_SPUR_WAR 0x0200 /* has a workaround for clock-harmonic spurs */ #define B43_BFL2_GPLL_WAR 0x0400 /* altenative G-band PLL settings implemented */ #define B43_BFL2_SINGLEANT_CCK 0x1000 #define B43_BFL2_2G_SPUR_WAR 0x2000 /* SPROM boardflags2_hi values */ #define B43_BFH2_GPLL_WAR2 0x0001 #define B43_BFH2_IPALVLSHIFT_3P3 0x0002 #define B43_BFH2_INTERNDET_TXIQCAL 0x0004 #define B43_BFH2_XTALBUFOUTEN 0x0008 /* GPIO register offset, in both ChipCommon and PCI core. */ #define B43_GPIO_CONTROL 0x6c /* SHM Routing */ enum { B43_SHM_UCODE, /* Microcode memory */ B43_SHM_SHARED, /* Shared memory */ B43_SHM_SCRATCH, /* Scratch memory */ B43_SHM_HW, /* Internal hardware register */ B43_SHM_RCMTA, /* Receive match transmitter address (rev >= 5 only) */ }; /* SHM Routing modifiers */ #define B43_SHM_AUTOINC_R 0x0200 /* Auto-increment address on read */ #define B43_SHM_AUTOINC_W 0x0100 /* Auto-increment address on write */ #define B43_SHM_AUTOINC_RW (B43_SHM_AUTOINC_R | \ B43_SHM_AUTOINC_W) /* Misc SHM_SHARED offsets */ #define B43_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */ #define B43_SHM_SH_PCTLWDPOS 0x0008 #define B43_SHM_SH_RXPADOFF 0x0034 /* RX Padding data offset (PIO only) */ #define B43_SHM_SH_FWCAPA 0x0042 /* Firmware capabilities (Opensource firmware only) */ #define B43_SHM_SH_PHYVER 0x0050 /* PHY version */ #define B43_SHM_SH_PHYTYPE 0x0052 /* PHY type */ #define B43_SHM_SH_ANTSWAP 0x005C /* Antenna swap threshold */ #define B43_SHM_SH_HOSTF1 0x005E /* Hostflags 1 for ucode options */ #define B43_SHM_SH_HOSTF2 0x0060 /* Hostflags 2 for ucode options */ #define B43_SHM_SH_HOSTF3 0x0062 /* Hostflags 3 for ucode options */ #define B43_SHM_SH_RFATT 0x0064 /* Current radio attenuation value */ #define B43_SHM_SH_RADAR 0x0066 /* Radar register */ #define B43_SHM_SH_PHYTXNOI 0x006E /* PHY noise directly after TX (lower 8bit only) */ #define B43_SHM_SH_RFRXSP1 0x0072 /* RF RX SP Register 1 */ #define B43_SHM_SH_HOSTF4 0x0078 /* Hostflags 4 for ucode options */ #define B43_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */ #define B43_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5 Ghz channel */ #define B43_SHM_SH_CHAN_40MHZ 0x0200 /* Bit set, if 40 Mhz channel width */ #define B43_SHM_SH_HOSTF5 0x00D4 /* Hostflags 5 for ucode options */ #define B43_SHM_SH_BCMCFIFOID 0x0108 /* Last posted cookie to the bcast/mcast FIFO */ /* TSSI information */ #define B43_SHM_SH_TSSI_CCK 0x0058 /* TSSI for last 4 CCK frames (32bit) */ #define B43_SHM_SH_TSSI_OFDM_A 0x0068 /* TSSI for last 4 OFDM frames (32bit) */ #define B43_SHM_SH_TSSI_OFDM_G 0x0070 /* TSSI for last 4 OFDM frames (32bit) */ #define B43_TSSI_MAX 0x7F /* Max value for one TSSI value */ /* SHM_SHARED TX FIFO variables */ #define B43_SHM_SH_SIZE01 0x0098 /* TX FIFO size for FIFO 0 (low) and 1 (high) */ #define B43_SHM_SH_SIZE23 0x009A /* TX FIFO size for FIFO 2 and 3 */ #define B43_SHM_SH_SIZE45 0x009C /* TX FIFO size for FIFO 4 and 5 */ #define B43_SHM_SH_SIZE67 0x009E /* TX FIFO size for FIFO 6 and 7 */ /* SHM_SHARED background noise */ #define B43_SHM_SH_JSSI0 0x0088 /* Measure JSSI 0 */ #define B43_SHM_SH_JSSI1 0x008A /* Measure JSSI 1 */ #define B43_SHM_SH_JSSIAUX 0x008C /* Measure JSSI AUX */ /* SHM_SHARED crypto engine */ #define B43_SHM_SH_DEFAULTIV 0x003C /* Default IV location */ #define B43_SHM_SH_NRRXTRANS 0x003E /* # of soft RX transmitter addresses (max 8) */ #define B43_SHM_SH_KTP 0x0056 /* Key table pointer */ #define B43_SHM_SH_TKIPTSCTTAK 0x0318 #define B43_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block (v4 firmware) */ #define B43_SHM_SH_PSM 0x05F4 /* PSM transmitter address match block (rev < 5) */ /* SHM_SHARED WME variables */ #define B43_SHM_SH_EDCFSTAT 0x000E /* EDCF status */ #define B43_SHM_SH_TXFCUR 0x0030 /* TXF current index */ #define B43_SHM_SH_EDCFQ 0x0240 /* EDCF Q info */ /* SHM_SHARED powersave mode related */ #define B43_SHM_SH_SLOTT 0x0010 /* Slot time */ #define B43_SHM_SH_DTIMPER 0x0012 /* DTIM period */ #define B43_SHM_SH_NOSLPZNATDTIM 0x004C /* NOSLPZNAT DTIM */ /* SHM_SHARED beacon/AP variables */ #define B43_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */ #define B43_SHM_SH_BTL1 0x001A /* Beacon template length 1 */ #define B43_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */ #define B43_SHM_SH_TIMBPOS 0x001E /* TIM B position in beacon */ #define B43_SHM_SH_DTIMP 0x0012 /* DTIP period */ #define B43_SHM_SH_MCASTCOOKIE 0x00A8 /* Last bcast/mcast frame ID */ #define B43_SHM_SH_SFFBLIM 0x0044 /* Short frame fallback retry limit */ #define B43_SHM_SH_LFFBLIM 0x0046 /* Long frame fallback retry limit */ #define B43_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word (see PHY TX control) */ #define B43_SHM_SH_EXTNPHYCTL 0x00B0 /* Extended bytes for beacon PHY control (N) */ /* SHM_SHARED ACK/CTS control */ #define B43_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word (see PHY TX control) */ /* SHM_SHARED probe response variables */ #define B43_SHM_SH_PRSSID 0x0160 /* Probe Response SSID */ #define B43_SHM_SH_PRSSIDLEN 0x0048 /* Probe Response SSID length */ #define B43_SHM_SH_PRTLEN 0x004A /* Probe Response template length */ #define B43_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ #define B43_SHM_SH_PRPHYCTL 0x0188 /* Probe Response PHY TX control word */ /* SHM_SHARED rate tables */ #define B43_SHM_SH_OFDMDIRECT 0x01C0 /* Pointer to OFDM direct map */ #define B43_SHM_SH_OFDMBASIC 0x01E0 /* Pointer to OFDM basic rate map */ #define B43_SHM_SH_CCKDIRECT 0x0200 /* Pointer to CCK direct map */ #define B43_SHM_SH_CCKBASIC 0x0220 /* Pointer to CCK basic rate map */ /* SHM_SHARED microcode soft registers */ #define B43_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ #define B43_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */ #define B43_SHM_SH_UCODEDATE 0x0004 /* Microcode date */ #define B43_SHM_SH_UCODETIME 0x0006 /* Microcode time */ #define B43_SHM_SH_UCODESTAT 0x0040 /* Microcode debug status code */ #define B43_SHM_SH_UCODESTAT_INVALID 0 #define B43_SHM_SH_UCODESTAT_INIT 1 #define B43_SHM_SH_UCODESTAT_ACTIVE 2 #define B43_SHM_SH_UCODESTAT_SUSP 3 /* suspended */ #define B43_SHM_SH_UCODESTAT_SLEEP 4 /* asleep (PS) */ #define B43_SHM_SH_MAXBFRAMES 0x0080 /* Maximum number of frames in a burst */ #define B43_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ #define B43_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ /* SHM_SHARED tx iq workarounds */ #define B43_SHM_SH_NPHY_TXIQW0 0x0700 #define B43_SHM_SH_NPHY_TXIQW1 0x0702 #define B43_SHM_SH_NPHY_TXIQW2 0x0704 #define B43_SHM_SH_NPHY_TXIQW3 0x0706 /* SHM_SHARED tx pwr ctrl */ #define B43_SHM_SH_NPHY_TXPWR_INDX0 0x0708 #define B43_SHM_SH_NPHY_TXPWR_INDX1 0x070E /* SHM_SCRATCH offsets */ #define B43_SHM_SC_MINCONT 0x0003 /* Minimum contention window */ #define B43_SHM_SC_MAXCONT 0x0004 /* Maximum contention window */ #define B43_SHM_SC_CURCONT 0x0005 /* Current contention window */ #define B43_SHM_SC_SRLIMIT 0x0006 /* Short retry count limit */ #define B43_SHM_SC_LRLIMIT 0x0007 /* Long retry count limit */ #define B43_SHM_SC_DTIMC 0x0008 /* Current DTIM count */ #define B43_SHM_SC_BTL0LEN 0x0015 /* Beacon 0 template length */ #define B43_SHM_SC_BTL1LEN 0x0016 /* Beacon 1 template length */ #define B43_SHM_SC_SCFB 0x0017 /* Short frame transmit count threshold for rate fallback */ #define B43_SHM_SC_LCFB 0x0018 /* Long frame transmit count threshold for rate fallback */ /* Hardware Radio Enable masks */ #define B43_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) #define B43_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) /* HostFlags. See b43_hf_read/write() */ #define B43_HF_ANTDIVHELP 0x000000000001ULL /* ucode antenna div helper */ #define B43_HF_SYMW 0x000000000002ULL /* G-PHY SYM workaround */ #define B43_HF_RXPULLW 0x000000000004ULL /* RX pullup workaround */ #define B43_HF_CCKBOOST 0x000000000008ULL /* 4dB CCK power boost (exclusive with OFDM boost) */ #define B43_HF_BTCOEX 0x000000000010ULL /* Bluetooth coexistance */ #define B43_HF_GDCW 0x000000000020ULL /* G-PHY DC canceller filter bw workaround */ #define B43_HF_OFDMPABOOST 0x000000000040ULL /* Enable PA gain boost for OFDM */ #define B43_HF_ACPR 0x000000000080ULL /* Disable for Japan, channel 14 */ #define B43_HF_EDCF 0x000000000100ULL /* on if WME and MAC suspended */ #define B43_HF_TSSIRPSMW 0x000000000200ULL /* TSSI reset PSM ucode workaround */ #define B43_HF_20IN40IQW 0x000000000200ULL /* 20 in 40 MHz I/Q workaround (rev >= 13 only) */ #define B43_HF_DSCRQ 0x000000000400ULL /* Disable slow clock request in ucode */ #define B43_HF_ACIW 0x000000000800ULL /* ACI workaround: shift bits by 2 on PHY CRS */ #define B43_HF_2060W 0x000000001000ULL /* 2060 radio workaround */ #define B43_HF_RADARW 0x000000002000ULL /* Radar workaround */ #define B43_HF_USEDEFKEYS 0x000000004000ULL /* Enable use of default keys */ #define B43_HF_AFTERBURNER 0x000000008000ULL /* Afterburner enabled */ #define B43_HF_BT4PRIOCOEX 0x000000010000ULL /* Bluetooth 4-priority coexistance */ #define B43_HF_FWKUP 0x000000020000ULL /* Fast wake-up ucode */ #define B43_HF_VCORECALC 0x000000040000ULL /* Force VCO recalculation when powering up synthpu */ #define B43_HF_PCISCW 0x000000080000ULL /* PCI slow clock workaround */ #define B43_HF_4318TSSI 0x000000200000ULL /* 4318 TSSI */ #define B43_HF_FBCMCFIFO 0x000000400000ULL /* Flush bcast/mcast FIFO immediately */ #define B43_HF_HWPCTL 0x000000800000ULL /* Enable hardwarre power control */ #define B43_HF_BTCOEXALT 0x000001000000ULL /* Bluetooth coexistance in alternate pins */ #define B43_HF_TXBTCHECK 0x000002000000ULL /* Bluetooth check during transmission */ #define B43_HF_SKCFPUP 0x000004000000ULL /* Skip CFP update */ #define B43_HF_N40W 0x000008000000ULL /* N PHY 40 MHz workaround (rev >= 13 only) */ #define B43_HF_ANTSEL 0x000020000000ULL /* Antenna selection (for testing antenna div.) */ #define B43_HF_BT3COEXT 0x000020000000ULL /* Bluetooth 3-wire coexistence (rev >= 13 only) */ #define B43_HF_BTCANT 0x000040000000ULL /* Bluetooth coexistence (antenna mode) (rev >= 13 only) */ #define B43_HF_ANTSELEN 0x000100000000ULL /* Antenna selection enabled (rev >= 13 only) */ #define B43_HF_ANTSELMODE 0x000200000000ULL /* Antenna selection mode (rev >= 13 only) */ #define B43_HF_MLADVW 0x001000000000ULL /* N PHY ML ADV workaround (rev >= 13 only) */ #define B43_HF_PR45960W 0x080000000000ULL /* PR 45960 workaround (rev >= 13 only) */ /* Firmware capabilities field in SHM (Opensource firmware only) */ #define B43_FWCAPA_HWCRYPTO 0x0001 #define B43_FWCAPA_QOS 0x0002 /* MacFilter offsets. */ #define B43_MACFILTER_SELF 0x0000 #define B43_MACFILTER_BSSID 0x0003 /* PowerControl */ #define B43_PCTL_IN 0xB0 #define B43_PCTL_OUT 0xB4 #define B43_PCTL_OUTENABLE 0xB8 #define B43_PCTL_XTAL_POWERUP 0x40 #define B43_PCTL_PLL_POWERDOWN 0x80 /* PowerControl Clock Modes */ #define B43_PCTL_CLK_FAST 0x00 #define B43_PCTL_CLK_SLOW 0x01 #define B43_PCTL_CLK_DYNAMIC 0x02 #define B43_PCTL_FORCE_SLOW 0x0800 #define B43_PCTL_FORCE_PLL 0x1000 #define B43_PCTL_DYN_XTAL 0x2000 /* PHYVersioning */ #define B43_PHYTYPE_A 0x00 #define B43_PHYTYPE_B 0x01 #define B43_PHYTYPE_G 0x02 #define B43_PHYTYPE_N 0x04 #define B43_PHYTYPE_LP 0x05 #define B43_PHYTYPE_SSLPN 0x06 #define B43_PHYTYPE_HT 0x07 #define B43_PHYTYPE_LCN 0x08 #define B43_PHYTYPE_LCNXN 0x09 #define B43_PHYTYPE_LCN40 0x0a #define B43_PHYTYPE_AC 0x0b /* PHYRegisters */ #define B43_PHY_ILT_A_CTRL 0x0072 #define B43_PHY_ILT_A_DATA1 0x0073 #define B43_PHY_ILT_A_DATA2 0x0074 #define B43_PHY_G_LO_CONTROL 0x0810 #define B43_PHY_ILT_G_CTRL 0x0472 #define B43_PHY_ILT_G_DATA1 0x0473 #define B43_PHY_ILT_G_DATA2 0x0474 #define B43_PHY_A_PCTL 0x007B #define B43_PHY_G_PCTL 0x0029 #define B43_PHY_A_CRS 0x0029 #define B43_PHY_RADIO_BITFIELD 0x0401 #define B43_PHY_G_CRS 0x0429 #define B43_PHY_NRSSILT_CTRL 0x0803 #define B43_PHY_NRSSILT_DATA 0x0804 /* RadioRegisters */ #define B43_RADIOCTL_ID 0x01 /* MAC Control bitfield */ #define B43_MACCTL_ENABLED 0x00000001 /* MAC Enabled */ #define B43_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */ #define B43_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */ #define B43_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */ #define B43_MACCTL_SHM_UPPER 0x00000200 /* SHM Upper */ #define B43_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */ #define B43_MACCTL_PSM_DBG 0x00002000 /* Microcode debugging enabled */ #define B43_MACCTL_GPOUTSMSK 0x0000C000 /* GPOUT Select Mask */ #define B43_MACCTL_BE 0x00010000 /* Big Endian mode */ #define B43_MACCTL_INFRA 0x00020000 /* Infrastructure mode */ #define B43_MACCTL_AP 0x00040000 /* AccessPoint mode */ #define B43_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ #define B43_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ #define B43_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep frames with bad PLCP */ #define B43_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ #define B43_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ #define B43_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ #define B43_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */ #define B43_MACCTL_AWAKE 0x04000000 /* Device is awake */ #define B43_MACCTL_CLOSEDNET 0x08000000 /* Closed net (no SSID bcast) */ #define B43_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */ #define B43_MACCTL_DISCTXSTAT 0x20000000 /* Discard TX status */ #define B43_MACCTL_DISCPMQ 0x40000000 /* Discard Power Management Queue */ #define B43_MACCTL_GMODE 0x80000000 /* G Mode */ /* MAC Command bitfield */ #define B43_MACCMD_BEACON0_VALID 0x00000001 /* Beacon 0 in template RAM is busy/valid */ #define B43_MACCMD_BEACON1_VALID 0x00000002 /* Beacon 1 in template RAM is busy/valid */ #define B43_MACCMD_DFQ_VALID 0x00000004 /* Directed frame queue valid (IBSS PS mode, ATIM) */ #define B43_MACCMD_CCA 0x00000008 /* Clear channel assessment */ #define B43_MACCMD_BGNOISE 0x00000010 /* Background noise */ /* BCMA 802.11 core specific IO Control (BCMA_IOCTL) flags */ #define B43_BCMA_IOCTL_PHY_CLKEN 0x00000004 /* PHY Clock Enable */ #define B43_BCMA_IOCTL_PHY_RESET 0x00000008 /* PHY Reset */ #define B43_BCMA_IOCTL_MACPHYCLKEN 0x00000010 /* MAC PHY Clock Control Enable */ #define B43_BCMA_IOCTL_PLLREFSEL 0x00000020 /* PLL Frequency Reference Select */ #define B43_BCMA_IOCTL_PHY_BW 0x000000C0 /* PHY band width and clock speed mask (N-PHY+ only?) */ #define B43_BCMA_IOCTL_PHY_BW_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */ #define B43_BCMA_IOCTL_PHY_BW_20MHZ 0x00000040 /* 20 MHz bandwidth, 80 MHz PHY */ #define B43_BCMA_IOCTL_PHY_BW_40MHZ 0x00000080 /* 40 MHz bandwidth, 160 MHz PHY */ #define B43_BCMA_IOCTL_GMODE 0x00002000 /* G Mode Enable */ /* BCMA 802.11 core specific IO status (BCMA_IOST) flags */ #define B43_BCMA_IOST_2G_PHY 0x00000001 /* 2.4G capable phy */ #define B43_BCMA_IOST_5G_PHY 0x00000002 /* 5G capable phy */ #define B43_BCMA_IOST_FASTCLKA 0x00000004 /* Fast Clock Available */ #define B43_BCMA_IOST_DUALB_PHY 0x00000008 /* Dualband phy */ /* 802.11 core specific TM State Low (SSB_TMSLOW) flags */ #define B43_TMSLOW_GMODE 0x20000000 /* G Mode Enable */ #define B43_TMSLOW_PHY_BANDWIDTH 0x00C00000 /* PHY band width and clock speed mask (N-PHY only) */ #define B43_TMSLOW_PHY_BANDWIDTH_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */ #define B43_TMSLOW_PHY_BANDWIDTH_20MHZ 0x00400000 /* 20 MHz bandwidth, 80 MHz PHY */ #define B43_TMSLOW_PHY_BANDWIDTH_40MHZ 0x00800000 /* 40 MHz bandwidth, 160 MHz PHY */ #define B43_TMSLOW_PLLREFSEL 0x00200000 /* PLL Frequency Reference Select (rev >= 5) */ #define B43_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Control Enable (rev >= 5) */ #define B43_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */ #define B43_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */ /* 802.11 core specific TM State High (SSB_TMSHIGH) flags */ #define B43_TMSHIGH_DUALBAND_PHY 0x00080000 /* Dualband PHY available */ #define B43_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available (rev >= 5) */ #define B43_TMSHIGH_HAVE_5GHZ_PHY 0x00020000 /* 5 GHz PHY available (rev >= 5) */ #define B43_TMSHIGH_HAVE_2GHZ_PHY 0x00010000 /* 2.4 GHz PHY available (rev >= 5) */ /* Generic-Interrupt reasons. */ #define B43_IRQ_MAC_SUSPENDED 0x00000001 #define B43_IRQ_BEACON 0x00000002 #define B43_IRQ_TBTT_INDI 0x00000004 #define B43_IRQ_BEACON_TX_OK 0x00000008 #define B43_IRQ_BEACON_CANCEL 0x00000010 #define B43_IRQ_ATIM_END 0x00000020 #define B43_IRQ_PMQ 0x00000040 #define B43_IRQ_PIO_WORKAROUND 0x00000100 #define B43_IRQ_MAC_TXERR 0x00000200 #define B43_IRQ_PHY_TXERR 0x00000800 #define B43_IRQ_PMEVENT 0x00001000 #define B43_IRQ_TIMER0 0x00002000 #define B43_IRQ_TIMER1 0x00004000 #define B43_IRQ_DMA 0x00008000 #define B43_IRQ_TXFIFO_FLUSH_OK 0x00010000 #define B43_IRQ_CCA_MEASURE_OK 0x00020000 #define B43_IRQ_NOISESAMPLE_OK 0x00040000 #define B43_IRQ_UCODE_DEBUG 0x08000000 #define B43_IRQ_RFKILL 0x10000000 #define B43_IRQ_TX_OK 0x20000000 #define B43_IRQ_PHY_G_CHANGED 0x40000000 #define B43_IRQ_TIMEOUT 0x80000000 #define B43_IRQ_ALL 0xFFFFFFFF #define B43_IRQ_MASKTEMPLATE (B43_IRQ_TBTT_INDI | \ B43_IRQ_ATIM_END | \ B43_IRQ_PMQ | \ B43_IRQ_MAC_TXERR | \ B43_IRQ_PHY_TXERR | \ B43_IRQ_DMA | \ B43_IRQ_TXFIFO_FLUSH_OK | \ B43_IRQ_NOISESAMPLE_OK | \ B43_IRQ_UCODE_DEBUG | \ B43_IRQ_RFKILL | \ B43_IRQ_TX_OK) /* The firmware register to fetch the debug-IRQ reason from. */ #define B43_DEBUGIRQ_REASON_REG 63 /* Debug-IRQ reasons. */ #define B43_DEBUGIRQ_PANIC 0 /* The firmware panic'ed */ #define B43_DEBUGIRQ_DUMP_SHM 1 /* Dump shared SHM */ #define B43_DEBUGIRQ_DUMP_REGS 2 /* Dump the microcode registers */ #define B43_DEBUGIRQ_MARKER 3 /* A "marker" was thrown by the firmware. */ #define B43_DEBUGIRQ_ACK 0xFFFF /* The host writes that to ACK the IRQ */ /* The firmware register that contains the "marker" line. */ #define B43_MARKER_ID_REG 2 #define B43_MARKER_LINE_REG 3 /* The firmware register to fetch the panic reason from. */ #define B43_FWPANIC_REASON_REG 3 /* Firmware panic reason codes */ #define B43_FWPANIC_DIE 0 /* Firmware died. Don't auto-restart it. */ #define B43_FWPANIC_RESTART 1 /* Firmware died. Schedule a controller reset. */ /* The firmware register that contains the watchdog counter. */ #define B43_WATCHDOG_REG 1 /* Device specific rate values. * The actual values defined here are (rate_in_mbps * 2). * Some code depends on this. Don't change it. */ #define B43_CCK_RATE_1MB 0x02 #define B43_CCK_RATE_2MB 0x04 #define B43_CCK_RATE_5MB 0x0B #define B43_CCK_RATE_11MB 0x16 #define B43_OFDM_RATE_6MB 0x0C #define B43_OFDM_RATE_9MB 0x12 #define B43_OFDM_RATE_12MB 0x18 #define B43_OFDM_RATE_18MB 0x24 #define B43_OFDM_RATE_24MB 0x30 #define B43_OFDM_RATE_36MB 0x48 #define B43_OFDM_RATE_48MB 0x60 #define B43_OFDM_RATE_54MB 0x6C /* Convert a b43 rate value to a rate in 100kbps */ #define B43_RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) #define B43_DEFAULT_SHORT_RETRY_LIMIT 7 #define B43_DEFAULT_LONG_RETRY_LIMIT 4 #define B43_PHY_TX_BADNESS_LIMIT 1000 /* Max size of a security key */ #define B43_SEC_KEYSIZE 16 /* Max number of group keys */ #define B43_NR_GROUP_KEYS 4 /* Max number of pairwise keys */ #define B43_NR_PAIRWISE_KEYS 50 /* Security algorithms. */ enum { B43_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ B43_SEC_ALGO_WEP40, B43_SEC_ALGO_TKIP, B43_SEC_ALGO_AES, B43_SEC_ALGO_WEP104, B43_SEC_ALGO_AES_LEGACY, }; struct b43_dmaring; /* The firmware file header */ #define B43_FW_TYPE_UCODE 'u' #define B43_FW_TYPE_PCM 'p' #define B43_FW_TYPE_IV 'i' struct b43_fw_header { /* File type */ u8 type; /* File format version */ u8 ver; u8 __padding[2]; /* Size of the data. For ucode and PCM this is in bytes. * For IV this is number-of-ivs. */ __be32 size; } __packed; /* Initial Value file format */ #define B43_IV_OFFSET_MASK 0x7FFF #define B43_IV_32BIT 0x8000 struct b43_iv { __be16 offset_size; union { __be16 d16; __be32 d32; } data __packed; } __packed; /* Data structures for DMA transmission, per 80211 core. */ struct b43_dma { struct b43_dmaring *tx_ring_AC_BK; /* Background */ struct b43_dmaring *tx_ring_AC_BE; /* Best Effort */ struct b43_dmaring *tx_ring_AC_VI; /* Video */ struct b43_dmaring *tx_ring_AC_VO; /* Voice */ struct b43_dmaring *tx_ring_mcast; /* Multicast */ struct b43_dmaring *rx_ring; u32 translation; /* Routing bits */ bool translation_in_low; /* Should translation bit go into low addr? */ bool parity; /* Check for parity */ }; struct b43_pio_txqueue; struct b43_pio_rxqueue; /* Data structures for PIO transmission, per 80211 core. */ struct b43_pio { struct b43_pio_txqueue *tx_queue_AC_BK; /* Background */ struct b43_pio_txqueue *tx_queue_AC_BE; /* Best Effort */ struct b43_pio_txqueue *tx_queue_AC_VI; /* Video */ struct b43_pio_txqueue *tx_queue_AC_VO; /* Voice */ struct b43_pio_txqueue *tx_queue_mcast; /* Multicast */ struct b43_pio_rxqueue *rx_queue; }; /* Context information for a noise calculation (Link Quality). */ struct b43_noise_calculation { bool calculation_running; u8 nr_samples; s8 samples[8][4]; }; struct b43_stats { u8 link_noise; }; struct b43_key { /* If keyconf is NULL, this key is disabled. * keyconf is a cookie. Don't derefenrence it outside of the set_key * path, because b43 doesn't own it. */ struct ieee80211_key_conf *keyconf; u8 algorithm; }; /* SHM offsets to the QOS data structures for the 4 different queues. */ #define B43_QOS_QUEUE_NUM 4 #define B43_QOS_PARAMS(queue) (B43_SHM_SH_EDCFQ + \ (B43_NR_QOSPARAMS * sizeof(u16) * (queue))) #define B43_QOS_BACKGROUND B43_QOS_PARAMS(0) #define B43_QOS_BESTEFFORT B43_QOS_PARAMS(1) #define B43_QOS_VIDEO B43_QOS_PARAMS(2) #define B43_QOS_VOICE B43_QOS_PARAMS(3) /* QOS parameter hardware data structure offsets. */ #define B43_NR_QOSPARAMS 16 enum { B43_QOSPARAM_TXOP = 0, B43_QOSPARAM_CWMIN, B43_QOSPARAM_CWMAX, B43_QOSPARAM_CWCUR, B43_QOSPARAM_AIFS, B43_QOSPARAM_BSLOTS, B43_QOSPARAM_REGGAP, B43_QOSPARAM_STATUS, }; /* QOS parameters for a queue. */ struct b43_qos_params { /* The QOS parameters */ struct ieee80211_tx_queue_params p; }; struct b43_wl; /* The type of the firmware file. */ enum b43_firmware_file_type { B43_FWTYPE_PROPRIETARY, B43_FWTYPE_OPENSOURCE, B43_NR_FWTYPES, }; /* Context data for fetching firmware. */ struct b43_request_fw_context { /* The device we are requesting the fw for. */ struct b43_wldev *dev; /* The type of firmware to request. */ enum b43_firmware_file_type req_type; /* Error messages for each firmware type. */ char errors[B43_NR_FWTYPES][128]; /* Temporary buffer for storing the firmware name. */ char fwname[64]; /* A fatal error occurred while requesting. Firmware request * can not continue, as any other request will also fail. */ int fatal_failure; }; /* In-memory representation of a cached microcode file. */ struct b43_firmware_file { const char *filename; const struct firmware *data; /* Type of the firmware file name. Note that this does only indicate * the type by the firmware name. NOT the file contents. * If you want to check for proprietary vs opensource, use (struct b43_firmware)->opensource * instead! The (struct b43_firmware)->opensource flag is derived from the actual firmware * binary code, not just the filename. */ enum b43_firmware_file_type type; }; enum b43_firmware_hdr_format { B43_FW_HDR_598, B43_FW_HDR_410, B43_FW_HDR_351, }; /* Pointers to the firmware data and meta information about it. */ struct b43_firmware { /* Microcode */ struct b43_firmware_file ucode; /* PCM code */ struct b43_firmware_file pcm; /* Initial MMIO values for the firmware */ struct b43_firmware_file initvals; /* Initial MMIO values for the firmware, band-specific */ struct b43_firmware_file initvals_band; /* Firmware revision */ u16 rev; /* Firmware patchlevel */ u16 patch; /* Format of header used by firmware */ enum b43_firmware_hdr_format hdr_format; /* Set to true, if we are using an opensource firmware. * Use this to check for proprietary vs opensource. */ bool opensource; /* Set to true, if the core needs a PCM firmware, but * we failed to load one. This is always false for * core rev > 10, as these don't need PCM firmware. */ bool pcm_request_failed; }; /* Device (802.11 core) initialization status. */ enum { B43_STAT_UNINIT = 0, /* Uninitialized. */ B43_STAT_INITIALIZED = 1, /* Initialized, but not started, yet. */ B43_STAT_STARTED = 2, /* Up and running. */ }; #define b43_status(wldev) atomic_read(&(wldev)->__init_status) #define b43_set_status(wldev, stat) do { \ atomic_set(&(wldev)->__init_status, (stat)); \ smp_wmb(); \ } while (0) /* Data structure for one wireless device (802.11 core) */ struct b43_wldev { struct b43_bus_dev *dev; struct b43_wl *wl; /* The device initialization status. * Use b43_status() to query. */ atomic_t __init_status; bool bad_frames_preempt; /* Use "Bad Frames Preemption" (default off) */ bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */ bool radio_hw_enable; /* saved state of radio hardware enabled state */ bool qos_enabled; /* TRUE, if QoS is used. */ bool hwcrypto_enabled; /* TRUE, if HW crypto acceleration is enabled. */ bool use_pio; /* TRUE if next init should use PIO */ /* PHY/Radio device. */ struct b43_phy phy; union { /* DMA engines. */ struct b43_dma dma; /* PIO engines. */ struct b43_pio pio; }; /* Use b43_using_pio_transfers() to check whether we are using * DMA or PIO data transfers. */ bool __using_pio_transfers; /* Various statistics about the physical device. */ struct b43_stats stats; /* Reason code of the last interrupt. */ u32 irq_reason; u32 dma_reason[6]; /* The currently active generic-interrupt mask. */ u32 irq_mask; /* Link Quality calculation context. */ struct b43_noise_calculation noisecalc; /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ int mac_suspended; /* Periodic tasks */ struct delayed_work periodic_work; unsigned int periodic_state; struct work_struct restart_work; /* encryption/decryption */ u16 ktp; /* Key table pointer */ struct b43_key key[B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS]; /* Firmware data */ struct b43_firmware fw; /* Devicelist in struct b43_wl (all 802.11 cores) */ struct list_head list; /* Debugging stuff follows. */ #ifdef CONFIG_B43_DEBUG struct b43_dfsentry *dfsentry; unsigned int irq_count; unsigned int irq_bit_count[32]; unsigned int tx_count; unsigned int rx_count; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) struct compat_threaded_irq irq_compat; #endif }; /* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ struct b43_wl { /* Pointer to the active wireless device on this chip */ struct b43_wldev *current_dev; /* Pointer to the ieee80211 hardware data structure */ struct ieee80211_hw *hw; /* Global driver mutex. Every operation must run with this mutex locked. */ struct mutex mutex; /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ * handler, only. This basically is just the IRQ mask register. */ spinlock_t hardirq_lock; /* Set this if we call ieee80211_register_hw() and check if we call * ieee80211_unregister_hw(). */ bool hw_registred; /* We can only have one operating interface (802.11 core) * at a time. General information about this interface follows. */ struct ieee80211_vif *vif; /* The MAC address of the operating interface. */ u8 mac_addr[ETH_ALEN]; /* Current BSSID */ u8 bssid[ETH_ALEN]; /* Interface type. (NL80211_IFTYPE_XXX) */ int if_type; /* Is the card operating in AP, STA or IBSS mode? */ bool operating; /* filter flags */ unsigned int filter_flags; /* Stats about the wireless interface */ struct ieee80211_low_level_stats ieee_stats; #ifdef CONFIG_B43_HWRNG struct hwrng rng; bool rng_initialized; char rng_name[30 + 1]; #endif /* CONFIG_B43_HWRNG */ /* List of all wireless devices on this chip */ struct list_head devlist; u8 nr_devs; bool radiotap_enabled; bool radio_enabled; /* The beacon we are currently using (AP or IBSS mode). */ struct sk_buff *current_beacon; bool beacon0_uploaded; bool beacon1_uploaded; bool beacon_templates_virgin; /* Never wrote the templates? */ struct work_struct beacon_update_trigger; /* The current QOS parameters for the 4 queues. */ struct b43_qos_params qos_params[B43_QOS_QUEUE_NUM]; /* Work for adjustment of the transmission power. * This is scheduled when we determine that the actual TX output * power doesn't match what we want. */ struct work_struct txpower_adjust_work; /* Packet transmit work */ struct work_struct tx_work; /* Queue of packets to be transmitted. */ struct sk_buff_head tx_queue[B43_QOS_QUEUE_NUM]; /* Flag that implement the queues stopping. */ bool tx_queue_stopped[B43_QOS_QUEUE_NUM]; /* firmware loading work */ struct work_struct firmware_load; /* The device LEDs. */ struct b43_leds leds; /* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */ u8 pio_scratchspace[118] __attribute__((__aligned__(8))); u8 pio_tailspace[4] __attribute__((__aligned__(8))); }; static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw) { return hw->priv; } static inline struct b43_wldev *dev_to_b43_wldev(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); return ssb_get_drvdata(ssb_dev); } /* Is the device operating in a specified mode (NL80211_IFTYPE_XXX). */ static inline int b43_is_mode(struct b43_wl *wl, int type) { return (wl->operating && wl->if_type == type); } /** * b43_current_band - Returns the currently used band. * Returns one of IEEE80211_BAND_2GHZ and IEEE80211_BAND_5GHZ. */ static inline enum ieee80211_band b43_current_band(struct b43_wl *wl) { return wl->hw->conf.channel->band; } static inline int b43_bus_may_powerdown(struct b43_wldev *wldev) { return wldev->dev->bus_may_powerdown(wldev->dev); } static inline int b43_bus_powerup(struct b43_wldev *wldev, bool dynamic_pctl) { return wldev->dev->bus_powerup(wldev->dev, dynamic_pctl); } static inline int b43_device_is_enabled(struct b43_wldev *wldev) { return wldev->dev->device_is_enabled(wldev->dev); } static inline void b43_device_enable(struct b43_wldev *wldev, u32 core_specific_flags) { wldev->dev->device_enable(wldev->dev, core_specific_flags); } static inline void b43_device_disable(struct b43_wldev *wldev, u32 core_specific_flags) { wldev->dev->device_disable(wldev->dev, core_specific_flags); } static inline u16 b43_read16(struct b43_wldev *dev, u16 offset) { return dev->dev->read16(dev->dev, offset); } static inline void b43_write16(struct b43_wldev *dev, u16 offset, u16 value) { dev->dev->write16(dev->dev, offset, value); } static inline void b43_maskset16(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) { b43_write16(dev, offset, (b43_read16(dev, offset) & mask) | set); } static inline u32 b43_read32(struct b43_wldev *dev, u16 offset) { return dev->dev->read32(dev->dev, offset); } static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value) { dev->dev->write32(dev->dev, offset, value); } static inline void b43_maskset32(struct b43_wldev *dev, u16 offset, u32 mask, u32 set) { b43_write32(dev, offset, (b43_read32(dev, offset) & mask) | set); } static inline void b43_block_read(struct b43_wldev *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { dev->dev->block_read(dev->dev, buffer, count, offset, reg_width); } static inline void b43_block_write(struct b43_wldev *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { dev->dev->block_write(dev->dev, buffer, count, offset, reg_width); } static inline bool b43_using_pio_transfers(struct b43_wldev *dev) { return dev->__using_pio_transfers; } /* Message printing */ __printf(2, 3) void b43info(struct b43_wl *wl, const char *fmt, ...); __printf(2, 3) void b43err(struct b43_wl *wl, const char *fmt, ...); __printf(2, 3) void b43warn(struct b43_wl *wl, const char *fmt, ...); __printf(2, 3) void b43dbg(struct b43_wl *wl, const char *fmt, ...); /* A WARN_ON variant that vanishes when b43 debugging is disabled. * This _also_ evaluates the arg with debugging disabled. */ #if B43_DEBUG # define B43_WARN_ON(x) WARN_ON(x) #else static inline bool __b43_warn_on_dummy(bool x) { return x; } # define B43_WARN_ON(x) __b43_warn_on_dummy(unlikely(!!(x))) #endif /* Convert an integer to a Q5.2 value */ #define INT_TO_Q52(i) ((i) << 2) /* Convert a Q5.2 value to an integer (precision loss!) */ #define Q52_TO_INT(q52) ((q52) >> 2) /* Macros for printing a value in Q5.2 format */ #define Q52_FMT "%u.%u" #define Q52_ARG(q52) Q52_TO_INT(q52), ((((q52) & 0x3) * 100) / 4) #endif /* B43_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_common.h0000644000175000017500000003271112026211315023376 0ustar mcgrofmcgrof#ifndef LINUX_B43_PHY_COMMON_H_ #define LINUX_B43_PHY_COMMON_H_ #include #include #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,28)) #include #endif struct b43_wldev; /* Complex number using 2 32-bit signed integers */ struct b43_c32 { s32 i, q; }; #define CORDIC_CONVERT(value) (((value) >= 0) ? \ ((((value) >> 15) + 1) >> 1) : \ -((((-(value)) >> 15) + 1) >> 1)) /* PHY register routing bits */ #define B43_PHYROUTE 0x0C00 /* PHY register routing bits mask */ #define B43_PHYROUTE_BASE 0x0000 /* Base registers */ #define B43_PHYROUTE_OFDM_GPHY 0x0400 /* OFDM register routing for G-PHYs */ #define B43_PHYROUTE_EXT_GPHY 0x0800 /* Extended G-PHY registers */ #define B43_PHYROUTE_N_BMODE 0x0C00 /* N-PHY BMODE registers */ /* CCK (B-PHY) registers. */ #define B43_PHY_CCK(reg) ((reg) | B43_PHYROUTE_BASE) /* N-PHY registers. */ #define B43_PHY_N(reg) ((reg) | B43_PHYROUTE_BASE) /* N-PHY BMODE registers. */ #define B43_PHY_N_BMODE(reg) ((reg) | B43_PHYROUTE_N_BMODE) /* OFDM (A-PHY) registers. */ #define B43_PHY_OFDM(reg) ((reg) | B43_PHYROUTE_OFDM_GPHY) /* Extended G-PHY registers. */ #define B43_PHY_EXTG(reg) ((reg) | B43_PHYROUTE_EXT_GPHY) /* Masks for the PHY versioning registers. */ #define B43_PHYVER_ANALOG 0xF000 #define B43_PHYVER_ANALOG_SHIFT 12 #define B43_PHYVER_TYPE 0x0F00 #define B43_PHYVER_TYPE_SHIFT 8 #define B43_PHYVER_VERSION 0x00FF /* PHY writes need to be flushed if we reach limit */ #define B43_MAX_WRITES_IN_ROW 24 /** * enum b43_interference_mitigation - Interference Mitigation mode * * @B43_INTERFMODE_NONE: Disabled * @B43_INTERFMODE_NONWLAN: Non-WLAN Interference Mitigation * @B43_INTERFMODE_MANUALWLAN: WLAN Interference Mitigation * @B43_INTERFMODE_AUTOWLAN: Automatic WLAN Interference Mitigation */ enum b43_interference_mitigation { B43_INTERFMODE_NONE, B43_INTERFMODE_NONWLAN, B43_INTERFMODE_MANUALWLAN, B43_INTERFMODE_AUTOWLAN, }; /* Antenna identifiers */ enum { B43_ANTENNA0 = 0, /* Antenna 0 */ B43_ANTENNA1 = 1, /* Antenna 1 */ B43_ANTENNA_AUTO0 = 2, /* Automatic, starting with antenna 0 */ B43_ANTENNA_AUTO1 = 3, /* Automatic, starting with antenna 1 */ B43_ANTENNA2 = 4, B43_ANTENNA3 = 8, B43_ANTENNA_AUTO = B43_ANTENNA_AUTO0, B43_ANTENNA_DEFAULT = B43_ANTENNA_AUTO, }; /** * enum b43_txpwr_result - Return value for the recalc_txpower PHY op. * * @B43_TXPWR_RES_NEED_ADJUST: Values changed. Hardware adjustment is needed. * @B43_TXPWR_RES_DONE: No more work to do. Everything is done. */ enum b43_txpwr_result { B43_TXPWR_RES_NEED_ADJUST, B43_TXPWR_RES_DONE, }; /** * struct b43_phy_operations - Function pointers for PHY ops. * * @allocate: Allocate and initialise the PHY data structures. * Must not be NULL. * @free: Destroy and free the PHY data structures. * Must not be NULL. * * @prepare_structs: Prepare the PHY data structures. * The data structures allocated in @allocate are * initialized here. * Must not be NULL. * @prepare_hardware: Prepare the PHY. This is called before b43_chip_init to * do some early early PHY hardware init. * Can be NULL, if not required. * @init: Initialize the PHY. * Must not be NULL. * @exit: Shutdown the PHY. * Can be NULL, if not required. * * @phy_read: Read from a PHY register. * Must not be NULL. * @phy_write: Write to a PHY register. * Must not be NULL. * @phy_maskset: Maskset a PHY register, taking shortcuts. * If it is NULL, a generic algorithm is used. * @radio_read: Read from a Radio register. * Must not be NULL. * @radio_write: Write to a Radio register. * Must not be NULL. * * @supports_hwpctl: Returns a boolean whether Hardware Power Control * is supported or not. * If NULL, hwpctl is assumed to be never supported. * @software_rfkill: Turn the radio ON or OFF. * Possible state values are * RFKILL_STATE_SOFT_BLOCKED or * RFKILL_STATE_UNBLOCKED * Must not be NULL. * @switch_analog: Turn the Analog on/off. * Must not be NULL. * @switch_channel: Switch the radio to another channel. * Must not be NULL. * @get_default_chan: Just returns the default channel number. * Must not be NULL. * @set_rx_antenna: Set the antenna used for RX. * Can be NULL, if not supported. * @interf_mitigation: Switch the Interference Mitigation mode. * Can be NULL, if not supported. * * @recalc_txpower: Recalculate the transmission power parameters. * This callback has to recalculate the TX power settings, * but does not need to write them to the hardware, yet. * Returns enum b43_txpwr_result to indicate whether the hardware * needs to be adjusted. * If B43_TXPWR_NEED_ADJUST is returned, @adjust_txpower * will be called later. * If the parameter "ignore_tssi" is true, the TSSI values should * be ignored and a recalculation of the power settings should be * done even if the TSSI values did not change. * This function may sleep, but should not. * Must not be NULL. * @adjust_txpower: Write the previously calculated TX power settings * (from @recalc_txpower) to the hardware. * This function may sleep. * Can be NULL, if (and ONLY if) @recalc_txpower _always_ * returns B43_TXPWR_RES_DONE. * * @pwork_15sec: Periodic work. Called every 15 seconds. * Can be NULL, if not required. * @pwork_60sec: Periodic work. Called every 60 seconds. * Can be NULL, if not required. */ struct b43_phy_operations { /* Initialisation */ int (*allocate)(struct b43_wldev *dev); void (*free)(struct b43_wldev *dev); void (*prepare_structs)(struct b43_wldev *dev); int (*prepare_hardware)(struct b43_wldev *dev); int (*init)(struct b43_wldev *dev); void (*exit)(struct b43_wldev *dev); /* Register access */ u16 (*phy_read)(struct b43_wldev *dev, u16 reg); void (*phy_write)(struct b43_wldev *dev, u16 reg, u16 value); void (*phy_maskset)(struct b43_wldev *dev, u16 reg, u16 mask, u16 set); u16 (*radio_read)(struct b43_wldev *dev, u16 reg); void (*radio_write)(struct b43_wldev *dev, u16 reg, u16 value); /* Radio */ bool (*supports_hwpctl)(struct b43_wldev *dev); void (*software_rfkill)(struct b43_wldev *dev, bool blocked); void (*switch_analog)(struct b43_wldev *dev, bool on); int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel); unsigned int (*get_default_chan)(struct b43_wldev *dev); void (*set_rx_antenna)(struct b43_wldev *dev, int antenna); int (*interf_mitigation)(struct b43_wldev *dev, enum b43_interference_mitigation new_mode); /* Transmission power adjustment */ enum b43_txpwr_result (*recalc_txpower)(struct b43_wldev *dev, bool ignore_tssi); void (*adjust_txpower)(struct b43_wldev *dev); /* Misc */ void (*pwork_15sec)(struct b43_wldev *dev); void (*pwork_60sec)(struct b43_wldev *dev); }; struct b43_phy_a; struct b43_phy_g; struct b43_phy_n; struct b43_phy_lp; struct b43_phy_ht; struct b43_phy_lcn; struct b43_phy { /* Hardware operation callbacks. */ const struct b43_phy_operations *ops; /* Most hardware context information is stored in the standard- * specific data structures pointed to by the pointers below. * Only one of them is valid (the currently enabled PHY). */ #ifdef CONFIG_B43_DEBUG /* No union for debug build to force NULL derefs in buggy code. */ struct { #else union { #endif /* A-PHY specific information */ struct b43_phy_a *a; /* G-PHY specific information */ struct b43_phy_g *g; /* N-PHY specific information */ struct b43_phy_n *n; /* LP-PHY specific information */ struct b43_phy_lp *lp; /* HT-PHY specific information */ struct b43_phy_ht *ht; /* LCN-PHY specific information */ struct b43_phy_lcn *lcn; }; /* Band support flags. */ bool supports_2ghz; bool supports_5ghz; /* HT info */ bool is_40mhz; /* GMODE bit enabled? */ bool gmode; /* Analog Type */ u8 analog; /* B43_PHYTYPE_ */ u8 type; /* PHY revision number. */ u8 rev; /* Count writes since last read */ u8 writes_counter; /* Radio versioning */ u16 radio_manuf; /* Radio manufacturer */ u16 radio_ver; /* Radio version */ u8 radio_rev; /* Radio revision */ /* Software state of the radio */ bool radio_on; /* Desired TX power level (in dBm). * This is set by the user and adjusted in b43_phy_xmitpower(). */ int desired_txpower; /* Hardware Power Control enabled? */ bool hardware_power_control; /* The time (in absolute jiffies) when the next TX power output * check is needed. */ unsigned long next_txpwr_check_time; /* Current channel */ unsigned int channel; u16 channel_freq; enum nl80211_channel_type channel_type; /* PHY TX errors counter. */ atomic_t txerr_cnt; #ifdef CONFIG_B43_DEBUG /* PHY registers locked (w.r.t. firmware) */ bool phy_locked; /* Radio registers locked (w.r.t. firmware) */ bool radio_locked; #endif /* B43_DEBUG */ }; /** * b43_phy_allocate - Allocate PHY structs * Allocate the PHY data structures, based on the current dev->phy.type */ int b43_phy_allocate(struct b43_wldev *dev); /** * b43_phy_free - Free PHY structs */ void b43_phy_free(struct b43_wldev *dev); /** * b43_phy_init - Initialise the PHY */ int b43_phy_init(struct b43_wldev *dev); /** * b43_phy_exit - Cleanup PHY */ void b43_phy_exit(struct b43_wldev *dev); /** * b43_has_hardware_pctl - Hardware Power Control supported? * Returns a boolean, whether hardware power control is supported. */ bool b43_has_hardware_pctl(struct b43_wldev *dev); /** * b43_phy_read - 16bit PHY register read access */ u16 b43_phy_read(struct b43_wldev *dev, u16 reg); /** * b43_phy_write - 16bit PHY register write access */ void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value); /** * b43_phy_copy - copy contents of 16bit PHY register to another */ void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg); /** * b43_phy_mask - Mask a PHY register with a mask */ void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask); /** * b43_phy_set - OR a PHY register with a bitmap */ void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set); /** * b43_phy_maskset - Mask and OR a PHY register with a mask and bitmap */ void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set); /** * b43_radio_read - 16bit Radio register read access */ u16 b43_radio_read(struct b43_wldev *dev, u16 reg); #define b43_radio_read16 b43_radio_read /* DEPRECATED */ /** * b43_radio_write - 16bit Radio register write access */ void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value); #define b43_radio_write16 b43_radio_write /* DEPRECATED */ /** * b43_radio_mask - Mask a 16bit radio register with a mask */ void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask); /** * b43_radio_set - OR a 16bit radio register with a bitmap */ void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set); /** * b43_radio_maskset - Mask and OR a radio register with a mask and bitmap */ void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set); /** * b43_radio_wait_value - Waits for a given value in masked register read */ bool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, u16 value, int delay, int timeout); /** * b43_radio_lock - Lock firmware radio register access */ void b43_radio_lock(struct b43_wldev *dev); /** * b43_radio_unlock - Unlock firmware radio register access */ void b43_radio_unlock(struct b43_wldev *dev); /** * b43_phy_lock - Lock firmware PHY register access */ void b43_phy_lock(struct b43_wldev *dev); /** * b43_phy_unlock - Unlock firmware PHY register access */ void b43_phy_unlock(struct b43_wldev *dev); /** * b43_switch_channel - Switch to another channel */ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel); /** * B43_DEFAULT_CHANNEL - Switch to the default channel. */ #define B43_DEFAULT_CHANNEL UINT_MAX /** * b43_software_rfkill - Turn the radio ON or OFF in software. */ void b43_software_rfkill(struct b43_wldev *dev, bool blocked); /** * b43_phy_txpower_check - Check TX power output. * * Compare the current TX power output to the desired power emission * and schedule an adjustment in case it mismatches. * * @flags: OR'ed enum b43_phy_txpower_check_flags flags. * See the docs below. */ void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags); /** * enum b43_phy_txpower_check_flags - Flags for b43_phy_txpower_check() * * @B43_TXPWR_IGNORE_TIME: Ignore the schedule time and force-redo * the check now. * @B43_TXPWR_IGNORE_TSSI: Redo the recalculation, even if the average * TSSI did not change. */ enum b43_phy_txpower_check_flags { B43_TXPWR_IGNORE_TIME = (1 << 0), B43_TXPWR_IGNORE_TSSI = (1 << 1), }; struct work_struct; void b43_phy_txpower_adjust_work(struct work_struct *work); /** * b43_phy_shm_tssi_read - Read the average of the last 4 TSSI from SHM. * * @shm_offset: The SHM address to read the values from. * * Returns the average of the 4 TSSI values, or a negative error code. */ int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset); /** * b43_phy_switch_analog_generic - Generic PHY operation for switching the Analog. * * It does the switching based on the PHY0 core register. * Do _not_ call this directly. Only use it as a switch_analog callback * for struct b43_phy_operations. */ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on); bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type); void b43_phy_force_clock(struct b43_wldev *dev, bool force); struct b43_c32 b43_cordic(int theta); #endif /* LINUX_B43_PHY_COMMON_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/xmit.h0000644000175000017500000003402612026211315022210 0ustar mcgrofmcgrof#ifndef B43_XMIT_H_ #define B43_XMIT_H_ #include "main.h" #include #define _b43_declare_plcp_hdr(size) \ struct b43_plcp_hdr##size { \ union { \ __le32 data; \ __u8 raw[size]; \ } __packed; \ } __packed /* struct b43_plcp_hdr4 */ _b43_declare_plcp_hdr(4); /* struct b43_plcp_hdr6 */ _b43_declare_plcp_hdr(6); #undef _b43_declare_plcp_hdr /* TX header for v4 firmware */ struct b43_txhdr { __le32 mac_ctl; /* MAC TX control */ __le16 mac_frame_ctl; /* Copy of the FrameControl field */ __le16 tx_fes_time_norm; /* TX FES Time Normal */ __le16 phy_ctl; /* PHY TX control */ __le16 phy_ctl1; /* PHY TX control word 1 */ __le16 phy_ctl1_fb; /* PHY TX control word 1 for fallback rates */ __le16 phy_ctl1_rts; /* PHY TX control word 1 RTS */ __le16 phy_ctl1_rts_fb; /* PHY TX control word 1 RTS for fallback rates */ __u8 phy_rate; /* PHY rate */ __u8 phy_rate_rts; /* PHY rate for RTS/CTS */ __u8 extra_ft; /* Extra Frame Types */ __u8 chan_radio_code; /* Channel Radio Code */ __u8 iv[16]; /* Encryption IV */ __u8 tx_receiver[6]; /* TX Frame Receiver address */ __le16 tx_fes_time_fb; /* TX FES Time Fallback */ struct b43_plcp_hdr6 rts_plcp_fb; /* RTS fallback PLCP header */ __le16 rts_dur_fb; /* RTS fallback duration */ struct b43_plcp_hdr6 plcp_fb; /* Fallback PLCP header */ __le16 dur_fb; /* Fallback duration */ __le16 mimo_modelen; /* MIMO mode length */ __le16 mimo_ratelen_fb; /* MIMO fallback rate length */ __le32 timeout; /* Timeout */ union { /* Tested with 598.314, 644.1001 and 666.2 */ struct { __le16 mimo_antenna; /* MIMO antenna select */ __le16 preload_size; /* Preload size */ PAD_BYTES(2); __le16 cookie; /* TX frame cookie */ __le16 tx_status; /* TX status */ __le16 max_n_mpdus; __le16 max_a_bytes_mrt; __le16 max_a_bytes_fbr; __le16 min_m_bytes; struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ __u8 rts_frame[16]; /* The RTS frame (if used) */ PAD_BYTES(2); struct b43_plcp_hdr6 plcp; /* Main PLCP header */ } format_598 __packed; /* Tested with 410.2160, 478.104 and 508.* */ struct { __le16 mimo_antenna; /* MIMO antenna select */ __le16 preload_size; /* Preload size */ PAD_BYTES(2); __le16 cookie; /* TX frame cookie */ __le16 tx_status; /* TX status */ struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ __u8 rts_frame[16]; /* The RTS frame (if used) */ PAD_BYTES(2); struct b43_plcp_hdr6 plcp; /* Main PLCP header */ } format_410 __packed; /* Tested with 351.126 */ struct { PAD_BYTES(2); __le16 cookie; /* TX frame cookie */ __le16 tx_status; /* TX status */ struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ __u8 rts_frame[16]; /* The RTS frame (if used) */ PAD_BYTES(2); struct b43_plcp_hdr6 plcp; /* Main PLCP header */ } format_351 __packed; } __packed; } __packed; struct b43_tx_legacy_rate_phy_ctl_entry { u8 bitrate; u16 coding_rate; u16 modulation; }; /* MAC TX control */ #define B43_TXH_MAC_USEFBR 0x10000000 /* Use fallback rate for this AMPDU */ #define B43_TXH_MAC_KEYIDX 0x0FF00000 /* Security key index */ #define B43_TXH_MAC_KEYIDX_SHIFT 20 #define B43_TXH_MAC_KEYALG 0x00070000 /* Security key algorithm */ #define B43_TXH_MAC_KEYALG_SHIFT 16 #define B43_TXH_MAC_AMIC 0x00008000 /* AMIC */ #define B43_TXH_MAC_RIFS 0x00004000 /* Use RIFS */ #define B43_TXH_MAC_LIFETIME 0x00002000 /* Lifetime */ #define B43_TXH_MAC_FRAMEBURST 0x00001000 /* Frameburst */ #define B43_TXH_MAC_SENDCTS 0x00000800 /* Send CTS-to-self */ #define B43_TXH_MAC_AMPDU 0x00000600 /* AMPDU status */ #define B43_TXH_MAC_AMPDU_MPDU 0x00000000 /* Regular MPDU, not an AMPDU */ #define B43_TXH_MAC_AMPDU_FIRST 0x00000200 /* First MPDU or AMPDU */ #define B43_TXH_MAC_AMPDU_INTER 0x00000400 /* Intermediate MPDU or AMPDU */ #define B43_TXH_MAC_AMPDU_LAST 0x00000600 /* Last (or only) MPDU of AMPDU */ #define B43_TXH_MAC_40MHZ 0x00000100 /* Use 40 MHz bandwidth */ #define B43_TXH_MAC_5GHZ 0x00000080 /* 5GHz band */ #define B43_TXH_MAC_DFCS 0x00000040 /* DFCS */ #define B43_TXH_MAC_IGNPMQ 0x00000020 /* Ignore PMQ */ #define B43_TXH_MAC_HWSEQ 0x00000010 /* Use Hardware Sequence Number */ #define B43_TXH_MAC_STMSDU 0x00000008 /* Start MSDU */ #define B43_TXH_MAC_SENDRTS 0x00000004 /* Send RTS */ #define B43_TXH_MAC_LONGFRAME 0x00000002 /* Long frame */ #define B43_TXH_MAC_ACK 0x00000001 /* Immediate ACK */ /* Extra Frame Types */ #define B43_TXH_EFT_FB 0x03 /* Data frame fallback encoding */ #define B43_TXH_EFT_FB_CCK 0x00 /* CCK */ #define B43_TXH_EFT_FB_OFDM 0x01 /* OFDM */ #define B43_TXH_EFT_FB_EWC 0x02 /* EWC */ #define B43_TXH_EFT_FB_N 0x03 /* N */ #define B43_TXH_EFT_RTS 0x0C /* RTS/CTS encoding */ #define B43_TXH_EFT_RTS_CCK 0x00 /* CCK */ #define B43_TXH_EFT_RTS_OFDM 0x04 /* OFDM */ #define B43_TXH_EFT_RTS_EWC 0x08 /* EWC */ #define B43_TXH_EFT_RTS_N 0x0C /* N */ #define B43_TXH_EFT_RTSFB 0x30 /* RTS/CTS fallback encoding */ #define B43_TXH_EFT_RTSFB_CCK 0x00 /* CCK */ #define B43_TXH_EFT_RTSFB_OFDM 0x10 /* OFDM */ #define B43_TXH_EFT_RTSFB_EWC 0x20 /* EWC */ #define B43_TXH_EFT_RTSFB_N 0x30 /* N */ /* PHY TX control word */ #define B43_TXH_PHY_ENC 0x0003 /* Data frame encoding */ #define B43_TXH_PHY_ENC_CCK 0x0000 /* CCK */ #define B43_TXH_PHY_ENC_OFDM 0x0001 /* OFDM */ #define B43_TXH_PHY_ENC_EWC 0x0002 /* EWC */ #define B43_TXH_PHY_ENC_N 0x0003 /* N */ #define B43_TXH_PHY_SHORTPRMBL 0x0010 /* Use short preamble */ #define B43_TXH_PHY_ANT 0x03C0 /* Antenna selection */ #define B43_TXH_PHY_ANT0 0x0000 /* Use antenna 0 */ #define B43_TXH_PHY_ANT1 0x0040 /* Use antenna 1 */ #define B43_TXH_PHY_ANT01AUTO 0x00C0 /* Use antenna 0/1 auto */ #define B43_TXH_PHY_ANT2 0x0100 /* Use antenna 2 */ #define B43_TXH_PHY_ANT3 0x0200 /* Use antenna 3 */ #define B43_TXH_PHY_TXPWR 0xFC00 /* TX power */ #define B43_TXH_PHY_TXPWR_SHIFT 10 /* PHY TX control word 1 */ #define B43_TXH_PHY1_BW 0x0007 /* Bandwidth */ #define B43_TXH_PHY1_BW_10 0x0000 /* 10 MHz */ #define B43_TXH_PHY1_BW_10U 0x0001 /* 10 MHz upper */ #define B43_TXH_PHY1_BW_20 0x0002 /* 20 MHz */ #define B43_TXH_PHY1_BW_20U 0x0003 /* 20 MHz upper */ #define B43_TXH_PHY1_BW_40 0x0004 /* 40 MHz */ #define B43_TXH_PHY1_BW_40DUP 0x0005 /* 50 MHz duplicate */ #define B43_TXH_PHY1_MODE 0x0038 /* Mode */ #define B43_TXH_PHY1_MODE_SISO 0x0000 /* SISO */ #define B43_TXH_PHY1_MODE_CDD 0x0008 /* CDD */ #define B43_TXH_PHY1_MODE_STBC 0x0010 /* STBC */ #define B43_TXH_PHY1_MODE_SDM 0x0018 /* SDM */ #define B43_TXH_PHY1_CRATE 0x0700 /* Coding rate */ #define B43_TXH_PHY1_CRATE_1_2 0x0000 /* 1/2 */ #define B43_TXH_PHY1_CRATE_2_3 0x0100 /* 2/3 */ #define B43_TXH_PHY1_CRATE_3_4 0x0200 /* 3/4 */ #define B43_TXH_PHY1_CRATE_4_5 0x0300 /* 4/5 */ #define B43_TXH_PHY1_CRATE_5_6 0x0400 /* 5/6 */ #define B43_TXH_PHY1_CRATE_7_8 0x0600 /* 7/8 */ #define B43_TXH_PHY1_MODUL 0x3800 /* Modulation scheme */ #define B43_TXH_PHY1_MODUL_BPSK 0x0000 /* BPSK */ #define B43_TXH_PHY1_MODUL_QPSK 0x0800 /* QPSK */ #define B43_TXH_PHY1_MODUL_QAM16 0x1000 /* QAM16 */ #define B43_TXH_PHY1_MODUL_QAM64 0x1800 /* QAM64 */ #define B43_TXH_PHY1_MODUL_QAM256 0x2000 /* QAM256 */ static inline size_t b43_txhdr_size(struct b43_wldev *dev) { switch (dev->fw.hdr_format) { case B43_FW_HDR_598: return 112 + sizeof(struct b43_plcp_hdr6); case B43_FW_HDR_410: return 104 + sizeof(struct b43_plcp_hdr6); case B43_FW_HDR_351: return 100 + sizeof(struct b43_plcp_hdr6); } return 0; } int b43_generate_txhdr(struct b43_wldev *dev, u8 * txhdr, struct sk_buff *skb_frag, struct ieee80211_tx_info *txctl, u16 cookie); /* Transmit Status */ struct b43_txstatus { u16 cookie; /* The cookie from the txhdr */ u16 seq; /* Sequence number */ u8 phy_stat; /* PHY TX status */ u8 frame_count; /* Frame transmit count */ u8 rts_count; /* RTS transmit count */ u8 supp_reason; /* Suppression reason */ /* flags */ u8 pm_indicated; /* PM mode indicated to AP */ u8 intermediate; /* Intermediate status notification (not final) */ u8 for_ampdu; /* Status is for an AMPDU (afterburner) */ u8 acked; /* Wireless ACK received */ }; /* txstatus supp_reason values */ enum { B43_TXST_SUPP_NONE, /* Not suppressed */ B43_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */ B43_TXST_SUPP_FLUSH, /* Suppressed due to flush request */ B43_TXST_SUPP_PREV, /* Previous fragment failed */ B43_TXST_SUPP_CHAN, /* Channel mismatch */ B43_TXST_SUPP_LIFE, /* Lifetime expired */ B43_TXST_SUPP_UNDER, /* Buffer underflow */ B43_TXST_SUPP_ABNACK, /* Afterburner NACK */ }; /* Receive header for v4 firmware. */ struct b43_rxhdr_fw4 { __le16 frame_len; /* Frame length */ PAD_BYTES(2); __le16 phy_status0; /* PHY RX Status 0 */ union { /* RSSI for A/B/G-PHYs */ struct { __u8 jssi; /* PHY RX Status 1: JSSI */ __u8 sig_qual; /* PHY RX Status 1: Signal Quality */ } __packed; /* RSSI for N-PHYs */ struct { __s8 power0; /* PHY RX Status 1: Power 0 */ __s8 power1; /* PHY RX Status 1: Power 1 */ } __packed; } __packed; union { /* HT-PHY */ struct { PAD_BYTES(1); __s8 phy_ht_power0; } __packed; /* RSSI for N-PHYs */ struct { __s8 power2; PAD_BYTES(1); } __packed; __le16 phy_status2; /* PHY RX Status 2 */ } __packed; union { /* HT-PHY */ struct { __s8 phy_ht_power1; __s8 phy_ht_power2; } __packed; __le16 phy_status3; /* PHY RX Status 3 */ } __packed; union { /* Tested with 598.314, 644.1001 and 666.2 */ struct { __le16 phy_status4; /* PHY RX Status 4 */ __le16 phy_status5; /* PHY RX Status 5 */ __le32 mac_status; /* MAC RX status */ __le16 mac_time; __le16 channel; } format_598 __packed; /* Tested with 351.126, 410.2160, 478.104 and 508.* */ struct { __le32 mac_status; /* MAC RX status */ __le16 mac_time; __le16 channel; } format_351 __packed; } __packed; } __packed; /* PHY RX Status 0 */ #define B43_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */ #define B43_RX_PHYST0_PLCPHCF 0x0200 #define B43_RX_PHYST0_PLCPFV 0x0100 #define B43_RX_PHYST0_SHORTPRMBL 0x0080 /* Received with Short Preamble */ #define B43_RX_PHYST0_LCRS 0x0040 #define B43_RX_PHYST0_ANT 0x0020 /* Antenna */ #define B43_RX_PHYST0_UNSRATE 0x0010 #define B43_RX_PHYST0_CLIP 0x000C #define B43_RX_PHYST0_CLIP_SHIFT 2 #define B43_RX_PHYST0_FTYPE 0x0003 /* Frame type */ #define B43_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */ #define B43_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */ #define B43_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */ #define B43_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */ /* PHY RX Status 2 */ #define B43_RX_PHYST2_LNAG 0xC000 /* LNA Gain */ #define B43_RX_PHYST2_LNAG_SHIFT 14 #define B43_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */ #define B43_RX_PHYST2_PNAG_SHIFT 10 #define B43_RX_PHYST2_FOFF 0x03FF /* F offset */ /* PHY RX Status 3 */ #define B43_RX_PHYST3_DIGG 0x1800 /* DIG Gain */ #define B43_RX_PHYST3_DIGG_SHIFT 11 #define B43_RX_PHYST3_TRSTATE 0x0400 /* TR state */ /* MAC RX Status */ #define B43_RX_MAC_RXST_VALID 0x01000000 /* PHY RXST valid */ #define B43_RX_MAC_TKIP_MICERR 0x00100000 /* TKIP MIC error */ #define B43_RX_MAC_TKIP_MICATT 0x00080000 /* TKIP MIC attempted */ #define B43_RX_MAC_AGGTYPE 0x00060000 /* Aggregation type */ #define B43_RX_MAC_AGGTYPE_SHIFT 17 #define B43_RX_MAC_AMSDU 0x00010000 /* A-MSDU mask */ #define B43_RX_MAC_BEACONSENT 0x00008000 /* Beacon sent flag */ #define B43_RX_MAC_KEYIDX 0x000007E0 /* Key index */ #define B43_RX_MAC_KEYIDX_SHIFT 5 #define B43_RX_MAC_DECERR 0x00000010 /* Decrypt error */ #define B43_RX_MAC_DEC 0x00000008 /* Decryption attempted */ #define B43_RX_MAC_PADDING 0x00000004 /* Pad bytes present */ #define B43_RX_MAC_RESP 0x00000002 /* Response frame transmitted */ #define B43_RX_MAC_FCSERR 0x00000001 /* FCS error */ /* RX channel */ #define B43_RX_CHAN_40MHZ 0x1000 /* 40 Mhz channel width */ #define B43_RX_CHAN_5GHZ 0x0800 /* 5 Ghz band */ #define B43_RX_CHAN_ID 0x07F8 /* Channel ID */ #define B43_RX_CHAN_ID_SHIFT 3 #define B43_RX_CHAN_PHYTYPE 0x0007 /* PHY type */ u8 b43_plcp_get_ratecode_cck(const u8 bitrate); u8 b43_plcp_get_ratecode_ofdm(const u8 bitrate); void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, const u16 octets, const u8 bitrate); void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr); void b43_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status); bool b43_fill_txstatus_report(struct b43_wldev *dev, struct ieee80211_tx_info *report, const struct b43_txstatus *status); void b43_tx_suspend(struct b43_wldev *dev); void b43_tx_resume(struct b43_wldev *dev); /* Helper functions for converting the key-table index from "firmware-format" * to "raw-format" and back. The firmware API changed for this at some revision. * We need to account for that here. */ static inline int b43_new_kidx_api(struct b43_wldev *dev) { /* FIXME: Not sure the change was at rev 351 */ return (dev->fw.rev >= 351); } static inline u8 b43_kidx_to_fw(struct b43_wldev *dev, u8 raw_kidx) { u8 firmware_kidx; if (b43_new_kidx_api(dev)) { firmware_kidx = raw_kidx; } else { if (raw_kidx >= 4) /* Is per STA key? */ firmware_kidx = raw_kidx - 4; else firmware_kidx = raw_kidx; /* TX default key */ } return firmware_kidx; } static inline u8 b43_kidx_to_raw(struct b43_wldev *dev, u8 firmware_kidx) { u8 raw_kidx; if (b43_new_kidx_api(dev)) raw_kidx = firmware_kidx; else raw_kidx = firmware_kidx + 4; /* RX default keys or per STA keys */ return raw_kidx; } /* struct b43_private_tx_info - TX info private to b43. * The structure is placed in (struct ieee80211_tx_info *)->rate_driver_data * * @bouncebuffer: DMA Bouncebuffer (if used) */ struct b43_private_tx_info { void *bouncebuffer; }; static inline struct b43_private_tx_info * b43_get_priv_tx_info(struct ieee80211_tx_info *info) { BUILD_BUG_ON(sizeof(struct b43_private_tx_info) > sizeof(info->rate_driver_data)); return (struct b43_private_tx_info *)info->rate_driver_data; } #endif /* B43_XMIT_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/xmit.c0000644000175000017500000006262012026211315022204 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver Transmission (TX/RX) related functions. Copyright (C) 2005 Martin Langer Copyright (C) 2005 Stefano Brivio Copyright (C) 2005, 2006 Michael Buesch Copyright (C) 2005 Danny van Dyk Copyright (C) 2005 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "xmit.h" #include "phy_common.h" #include "dma.h" #include "pio.h" static const struct b43_tx_legacy_rate_phy_ctl_entry b43_tx_legacy_rate_phy_ctl[] = { { B43_CCK_RATE_1MB, 0x0, 0x0 }, { B43_CCK_RATE_2MB, 0x0, 0x1 }, { B43_CCK_RATE_5MB, 0x0, 0x2 }, { B43_CCK_RATE_11MB, 0x0, 0x3 }, { B43_OFDM_RATE_6MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_BPSK }, { B43_OFDM_RATE_9MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_BPSK }, { B43_OFDM_RATE_12MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QPSK }, { B43_OFDM_RATE_18MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QPSK }, { B43_OFDM_RATE_24MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QAM16 }, { B43_OFDM_RATE_36MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM16 }, { B43_OFDM_RATE_48MB, B43_TXH_PHY1_CRATE_2_3, B43_TXH_PHY1_MODUL_QAM64 }, { B43_OFDM_RATE_54MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM64 }, }; static const struct b43_tx_legacy_rate_phy_ctl_entry * b43_tx_legacy_rate_phy_ctl_ent(u8 bitrate) { const struct b43_tx_legacy_rate_phy_ctl_entry *e; unsigned int i; for (i = 0; i < ARRAY_SIZE(b43_tx_legacy_rate_phy_ctl); i++) { e = &(b43_tx_legacy_rate_phy_ctl[i]); if (e->bitrate == bitrate) return e; } B43_WARN_ON(1); return NULL; } /* Extract the bitrate index out of a CCK PLCP header. */ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp) { switch (plcp->raw[0]) { case 0x0A: return 0; case 0x14: return 1; case 0x37: return 2; case 0x6E: return 3; } return -1; } /* Extract the bitrate index out of an OFDM PLCP header. */ static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy) { int base = aphy ? 0 : 4; switch (plcp->raw[0] & 0xF) { case 0xB: return base + 0; case 0xF: return base + 1; case 0xA: return base + 2; case 0xE: return base + 3; case 0x9: return base + 4; case 0xD: return base + 5; case 0x8: return base + 6; case 0xC: return base + 7; } return -1; } u8 b43_plcp_get_ratecode_cck(const u8 bitrate) { switch (bitrate) { case B43_CCK_RATE_1MB: return 0x0A; case B43_CCK_RATE_2MB: return 0x14; case B43_CCK_RATE_5MB: return 0x37; case B43_CCK_RATE_11MB: return 0x6E; } B43_WARN_ON(1); return 0; } u8 b43_plcp_get_ratecode_ofdm(const u8 bitrate) { switch (bitrate) { case B43_OFDM_RATE_6MB: return 0xB; case B43_OFDM_RATE_9MB: return 0xF; case B43_OFDM_RATE_12MB: return 0xA; case B43_OFDM_RATE_18MB: return 0xE; case B43_OFDM_RATE_24MB: return 0x9; case B43_OFDM_RATE_36MB: return 0xD; case B43_OFDM_RATE_48MB: return 0x8; case B43_OFDM_RATE_54MB: return 0xC; } B43_WARN_ON(1); return 0; } void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, const u16 octets, const u8 bitrate) { __u8 *raw = plcp->raw; if (b43_is_ofdm_rate(bitrate)) { u32 d; d = b43_plcp_get_ratecode_ofdm(bitrate); B43_WARN_ON(octets & 0xF000); d |= (octets << 5); plcp->data = cpu_to_le32(d); } else { u32 plen; plen = octets * 16 / bitrate; if ((octets * 16 % bitrate) > 0) { plen++; if ((bitrate == B43_CCK_RATE_11MB) && ((octets * 8 % 11) < 4)) { raw[1] = 0x84; } else raw[1] = 0x04; } else raw[1] = 0x04; plcp->data |= cpu_to_le32(plen << 16); raw[0] = b43_plcp_get_ratecode_cck(bitrate); } } /* TODO: verify if needed for SSLPN or LCN */ static u16 b43_generate_tx_phy_ctl1(struct b43_wldev *dev, u8 bitrate) { const struct b43_phy *phy = &dev->phy; const struct b43_tx_legacy_rate_phy_ctl_entry *e; u16 control = 0; u16 bw; if (phy->type == B43_PHYTYPE_LP) bw = B43_TXH_PHY1_BW_20; else /* FIXME */ bw = B43_TXH_PHY1_BW_20; if (0) { /* FIXME: MIMO */ } else if (b43_is_cck_rate(bitrate) && phy->type != B43_PHYTYPE_LP) { control = bw; } else { control = bw; e = b43_tx_legacy_rate_phy_ctl_ent(bitrate); if (e) { control |= e->coding_rate; control |= e->modulation; } control |= B43_TXH_PHY1_MODE_SISO; } return control; } static u8 b43_calc_fallback_rate(u8 bitrate) { switch (bitrate) { case B43_CCK_RATE_1MB: return B43_CCK_RATE_1MB; case B43_CCK_RATE_2MB: return B43_CCK_RATE_1MB; case B43_CCK_RATE_5MB: return B43_CCK_RATE_2MB; case B43_CCK_RATE_11MB: return B43_CCK_RATE_5MB; case B43_OFDM_RATE_6MB: return B43_CCK_RATE_5MB; case B43_OFDM_RATE_9MB: return B43_OFDM_RATE_6MB; case B43_OFDM_RATE_12MB: return B43_OFDM_RATE_9MB; case B43_OFDM_RATE_18MB: return B43_OFDM_RATE_12MB; case B43_OFDM_RATE_24MB: return B43_OFDM_RATE_18MB; case B43_OFDM_RATE_36MB: return B43_OFDM_RATE_24MB; case B43_OFDM_RATE_48MB: return B43_OFDM_RATE_36MB; case B43_OFDM_RATE_54MB: return B43_OFDM_RATE_48MB; } B43_WARN_ON(1); return 0; } /* Generate a TX data header. */ int b43_generate_txhdr(struct b43_wldev *dev, u8 *_txhdr, struct sk_buff *skb_frag, struct ieee80211_tx_info *info, u16 cookie) { const unsigned char *fragment_data = skb_frag->data; unsigned int fragment_len = skb_frag->len; struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr; const struct b43_phy *phy = &dev->phy; const struct ieee80211_hdr *wlhdr = (const struct ieee80211_hdr *)fragment_data; int use_encryption = !!info->control.hw_key; __le16 fctl = wlhdr->frame_control; struct ieee80211_rate *fbrate; u8 rate, rate_fb; int rate_ofdm, rate_fb_ofdm; unsigned int plcp_fragment_len; u32 mac_ctl = 0; u16 phy_ctl = 0; bool fill_phy_ctl1 = (phy->type == B43_PHYTYPE_LP || phy->type == B43_PHYTYPE_N || phy->type == B43_PHYTYPE_HT); u8 extra_ft = 0; struct ieee80211_rate *txrate; struct ieee80211_tx_rate *rates; memset(txhdr, 0, sizeof(*txhdr)); txrate = ieee80211_get_tx_rate(dev->wl->hw, info); rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB; rate_ofdm = b43_is_ofdm_rate(rate); fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : txrate; rate_fb = fbrate->hw_value; rate_fb_ofdm = b43_is_ofdm_rate(rate_fb); if (rate_ofdm) txhdr->phy_rate = b43_plcp_get_ratecode_ofdm(rate); else txhdr->phy_rate = b43_plcp_get_ratecode_cck(rate); txhdr->mac_frame_ctl = wlhdr->frame_control; memcpy(txhdr->tx_receiver, wlhdr->addr1, 6); /* Calculate duration for fallback rate */ if ((rate_fb == rate) || (wlhdr->duration_id & cpu_to_le16(0x8000)) || (wlhdr->duration_id == cpu_to_le16(0))) { /* If the fallback rate equals the normal rate or the * dur_id field contains an AID, CFP magic or 0, * use the original dur_id field. */ txhdr->dur_fb = wlhdr->duration_id; } else { txhdr->dur_fb = ieee80211_generic_frame_duration( dev->wl->hw, info->control.vif, info->band, fragment_len, fbrate); } plcp_fragment_len = fragment_len + FCS_LEN; if (use_encryption) { u8 key_idx = info->control.hw_key->hw_key_idx; struct b43_key *key; int wlhdr_len; size_t iv_len; B43_WARN_ON(key_idx >= ARRAY_SIZE(dev->key)); key = &(dev->key[key_idx]); if (unlikely(!key->keyconf)) { /* This key is invalid. This might only happen * in a short timeframe after machine resume before * we were able to reconfigure keys. * Drop this packet completely. Do not transmit it * unencrypted to avoid leaking information. */ return -ENOKEY; } /* Hardware appends ICV. */ plcp_fragment_len += info->control.hw_key->icv_len; key_idx = b43_kidx_to_fw(dev, key_idx); mac_ctl |= (key_idx << B43_TXH_MAC_KEYIDX_SHIFT) & B43_TXH_MAC_KEYIDX; mac_ctl |= (key->algorithm << B43_TXH_MAC_KEYALG_SHIFT) & B43_TXH_MAC_KEYALG; wlhdr_len = ieee80211_hdrlen(fctl); if (key->algorithm == B43_SEC_ALGO_TKIP) { u16 phase1key[5]; int i; /* we give the phase1key and iv16 here, the key is stored in * shm. With that the hardware can do phase 2 and encryption. */ ieee80211_get_tkip_p1k(info->control.hw_key, skb_frag, phase1key); /* phase1key is in host endian. Copy to little-endian txhdr->iv. */ for (i = 0; i < 5; i++) { txhdr->iv[i * 2 + 0] = phase1key[i]; txhdr->iv[i * 2 + 1] = phase1key[i] >> 8; } /* iv16 */ memcpy(txhdr->iv + 10, ((u8 *) wlhdr) + wlhdr_len, 3); } else { iv_len = min((size_t) info->control.hw_key->iv_len, ARRAY_SIZE(txhdr->iv)); memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len); } } switch (dev->fw.hdr_format) { case B43_FW_HDR_598: b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_598.plcp), plcp_fragment_len, rate); break; case B43_FW_HDR_351: b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_351.plcp), plcp_fragment_len, rate); break; case B43_FW_HDR_410: b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_410.plcp), plcp_fragment_len, rate); break; } b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->plcp_fb), plcp_fragment_len, rate_fb); /* Extra Frame Types */ if (rate_fb_ofdm) extra_ft |= B43_TXH_EFT_FB_OFDM; else extra_ft |= B43_TXH_EFT_FB_CCK; /* Set channel radio code. Note that the micrcode ORs 0x100 to * this value before comparing it to the value in SHM, if this * is a 5Ghz packet. */ txhdr->chan_radio_code = phy->channel; /* PHY TX Control word */ if (rate_ofdm) phy_ctl |= B43_TXH_PHY_ENC_OFDM; else phy_ctl |= B43_TXH_PHY_ENC_CCK; if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) phy_ctl |= B43_TXH_PHY_SHORTPRMBL; switch (b43_ieee80211_antenna_sanitize(dev, 0)) { case 0: /* Default */ phy_ctl |= B43_TXH_PHY_ANT01AUTO; break; case 1: /* Antenna 0 */ phy_ctl |= B43_TXH_PHY_ANT0; break; case 2: /* Antenna 1 */ phy_ctl |= B43_TXH_PHY_ANT1; break; case 3: /* Antenna 2 */ phy_ctl |= B43_TXH_PHY_ANT2; break; case 4: /* Antenna 3 */ phy_ctl |= B43_TXH_PHY_ANT3; break; default: B43_WARN_ON(1); } rates = info->control.rates; /* MAC control */ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) mac_ctl |= B43_TXH_MAC_ACK; /* use hardware sequence counter as the non-TID counter */ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) mac_ctl |= B43_TXH_MAC_HWSEQ; if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) mac_ctl |= B43_TXH_MAC_STMSDU; if (phy->type == B43_PHYTYPE_A) mac_ctl |= B43_TXH_MAC_5GHZ; /* Overwrite rates[0].count to make the retry calculation * in the tx status easier. need the actual retry limit to * detect whether the fallback rate was used. */ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) { rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count; mac_ctl |= B43_TXH_MAC_LONGFRAME; } else { rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count; } /* Generate the RTS or CTS-to-self frame */ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) { unsigned int len; struct ieee80211_hdr *uninitialized_var(hdr); int rts_rate, rts_rate_fb; int rts_rate_ofdm, rts_rate_fb_ofdm; struct b43_plcp_hdr6 *uninitialized_var(plcp); struct ieee80211_rate *rts_cts_rate; rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info); rts_rate = rts_cts_rate ? rts_cts_rate->hw_value : B43_CCK_RATE_1MB; rts_rate_ofdm = b43_is_ofdm_rate(rts_rate); rts_rate_fb = b43_calc_fallback_rate(rts_rate); rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb); if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { struct ieee80211_cts *uninitialized_var(cts); switch (dev->fw.hdr_format) { case B43_FW_HDR_598: cts = (struct ieee80211_cts *) (txhdr->format_598.rts_frame); break; case B43_FW_HDR_351: cts = (struct ieee80211_cts *) (txhdr->format_351.rts_frame); break; case B43_FW_HDR_410: cts = (struct ieee80211_cts *) (txhdr->format_410.rts_frame); break; } ieee80211_ctstoself_get(dev->wl->hw, info->control.vif, fragment_data, fragment_len, info, cts); mac_ctl |= B43_TXH_MAC_SENDCTS; len = sizeof(struct ieee80211_cts); } else { struct ieee80211_rts *uninitialized_var(rts); switch (dev->fw.hdr_format) { case B43_FW_HDR_598: rts = (struct ieee80211_rts *) (txhdr->format_598.rts_frame); break; case B43_FW_HDR_351: rts = (struct ieee80211_rts *) (txhdr->format_351.rts_frame); break; case B43_FW_HDR_410: rts = (struct ieee80211_rts *) (txhdr->format_410.rts_frame); break; } ieee80211_rts_get(dev->wl->hw, info->control.vif, fragment_data, fragment_len, info, rts); mac_ctl |= B43_TXH_MAC_SENDRTS; len = sizeof(struct ieee80211_rts); } len += FCS_LEN; /* Generate the PLCP headers for the RTS/CTS frame */ switch (dev->fw.hdr_format) { case B43_FW_HDR_598: plcp = &txhdr->format_598.rts_plcp; break; case B43_FW_HDR_351: plcp = &txhdr->format_351.rts_plcp; break; case B43_FW_HDR_410: plcp = &txhdr->format_410.rts_plcp; break; } b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp, len, rts_rate); plcp = &txhdr->rts_plcp_fb; b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp, len, rts_rate_fb); switch (dev->fw.hdr_format) { case B43_FW_HDR_598: hdr = (struct ieee80211_hdr *) (&txhdr->format_598.rts_frame); break; case B43_FW_HDR_351: hdr = (struct ieee80211_hdr *) (&txhdr->format_351.rts_frame); break; case B43_FW_HDR_410: hdr = (struct ieee80211_hdr *) (&txhdr->format_410.rts_frame); break; } txhdr->rts_dur_fb = hdr->duration_id; if (rts_rate_ofdm) { extra_ft |= B43_TXH_EFT_RTS_OFDM; txhdr->phy_rate_rts = b43_plcp_get_ratecode_ofdm(rts_rate); } else { extra_ft |= B43_TXH_EFT_RTS_CCK; txhdr->phy_rate_rts = b43_plcp_get_ratecode_cck(rts_rate); } if (rts_rate_fb_ofdm) extra_ft |= B43_TXH_EFT_RTSFB_OFDM; else extra_ft |= B43_TXH_EFT_RTSFB_CCK; if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS && fill_phy_ctl1) { txhdr->phy_ctl1_rts = cpu_to_le16( b43_generate_tx_phy_ctl1(dev, rts_rate)); txhdr->phy_ctl1_rts_fb = cpu_to_le16( b43_generate_tx_phy_ctl1(dev, rts_rate_fb)); } } /* Magic cookie */ switch (dev->fw.hdr_format) { case B43_FW_HDR_598: txhdr->format_598.cookie = cpu_to_le16(cookie); break; case B43_FW_HDR_351: txhdr->format_351.cookie = cpu_to_le16(cookie); break; case B43_FW_HDR_410: txhdr->format_410.cookie = cpu_to_le16(cookie); break; } if (fill_phy_ctl1) { txhdr->phy_ctl1 = cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate)); txhdr->phy_ctl1_fb = cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate_fb)); } /* Apply the bitfields */ txhdr->mac_ctl = cpu_to_le32(mac_ctl); txhdr->phy_ctl = cpu_to_le16(phy_ctl); txhdr->extra_ft = extra_ft; return 0; } static s8 b43_rssi_postprocess(struct b43_wldev *dev, u8 in_rssi, int ofdm, int adjust_2053, int adjust_2050) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; s32 tmp; switch (phy->radio_ver) { case 0x2050: if (ofdm) { tmp = in_rssi; if (tmp > 127) tmp -= 256; tmp *= 73; tmp /= 64; if (adjust_2050) tmp += 25; else tmp -= 3; } else { if (dev->dev->bus_sprom-> boardflags_lo & B43_BFL_RSSI) { if (in_rssi > 63) in_rssi = 63; B43_WARN_ON(phy->type != B43_PHYTYPE_G); tmp = gphy->nrssi_lt[in_rssi]; tmp = 31 - tmp; tmp *= -131; tmp /= 128; tmp -= 57; } else { tmp = in_rssi; tmp = 31 - tmp; tmp *= -149; tmp /= 128; tmp -= 68; } if (phy->type == B43_PHYTYPE_G && adjust_2050) tmp += 25; } break; case 0x2060: if (in_rssi > 127) tmp = in_rssi - 256; else tmp = in_rssi; break; default: tmp = in_rssi; tmp -= 11; tmp *= 103; tmp /= 64; if (adjust_2053) tmp -= 109; else tmp -= 83; } return (s8) tmp; } //TODO #if 0 static s8 b43_rssinoise_postprocess(struct b43_wldev *dev, u8 in_rssi) { struct b43_phy *phy = &dev->phy; s8 ret; if (phy->type == B43_PHYTYPE_A) { //TODO: Incomplete specs. ret = 0; } else ret = b43_rssi_postprocess(dev, in_rssi, 0, 1, 1); return ret; } #endif void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) { struct ieee80211_rx_status status; struct b43_plcp_hdr6 *plcp; struct ieee80211_hdr *wlhdr; const struct b43_rxhdr_fw4 *rxhdr = _rxhdr; __le16 fctl; u16 phystat0, phystat3; u16 uninitialized_var(chanstat), uninitialized_var(mactime); u32 uninitialized_var(macstat); u16 chanid; u16 phytype; int padding, rate_idx; memset(&status, 0, sizeof(status)); /* Get metadata about the frame from the header. */ phystat0 = le16_to_cpu(rxhdr->phy_status0); phystat3 = le16_to_cpu(rxhdr->phy_status3); switch (dev->fw.hdr_format) { case B43_FW_HDR_598: macstat = le32_to_cpu(rxhdr->format_598.mac_status); mactime = le16_to_cpu(rxhdr->format_598.mac_time); chanstat = le16_to_cpu(rxhdr->format_598.channel); break; case B43_FW_HDR_410: case B43_FW_HDR_351: macstat = le32_to_cpu(rxhdr->format_351.mac_status); mactime = le16_to_cpu(rxhdr->format_351.mac_time); chanstat = le16_to_cpu(rxhdr->format_351.channel); break; } phytype = chanstat & B43_RX_CHAN_PHYTYPE; if (unlikely(macstat & B43_RX_MAC_FCSERR)) { dev->wl->ieee_stats.dot11FCSErrorCount++; status.flag |= RX_FLAG_FAILED_FCS_CRC; } if (unlikely(phystat0 & (B43_RX_PHYST0_PLCPHCF | B43_RX_PHYST0_PLCPFV))) status.flag |= RX_FLAG_FAILED_PLCP_CRC; if (phystat0 & B43_RX_PHYST0_SHORTPRMBL) status.flag |= RX_FLAG_SHORTPRE; if (macstat & B43_RX_MAC_DECERR) { /* Decryption with the given key failed. * Drop the packet. We also won't be able to decrypt it with * the key in software. */ goto drop; } /* Skip PLCP and padding */ padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0; if (unlikely(skb->len < (sizeof(struct b43_plcp_hdr6) + padding))) { b43dbg(dev->wl, "RX: Packet size underrun (1)\n"); goto drop; } plcp = (struct b43_plcp_hdr6 *)(skb->data + padding); skb_pull(skb, sizeof(struct b43_plcp_hdr6) + padding); /* The skb contains the Wireless Header + payload data now */ if (unlikely(skb->len < (2 + 2 + 6 /*minimum hdr */ + FCS_LEN))) { b43dbg(dev->wl, "RX: Packet size underrun (2)\n"); goto drop; } wlhdr = (struct ieee80211_hdr *)(skb->data); fctl = wlhdr->frame_control; if (macstat & B43_RX_MAC_DEC) { unsigned int keyidx; int wlhdr_len; keyidx = ((macstat & B43_RX_MAC_KEYIDX) >> B43_RX_MAC_KEYIDX_SHIFT); /* We must adjust the key index here. We want the "physical" * key index, but the ucode passed it slightly different. */ keyidx = b43_kidx_to_raw(dev, keyidx); B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key)); if (dev->key[keyidx].algorithm != B43_SEC_ALGO_NONE) { wlhdr_len = ieee80211_hdrlen(fctl); if (unlikely(skb->len < (wlhdr_len + 3))) { b43dbg(dev->wl, "RX: Packet size underrun (3)\n"); goto drop; } status.flag |= RX_FLAG_DECRYPTED; } } /* Link quality statistics */ switch (chanstat & B43_RX_CHAN_PHYTYPE) { case B43_PHYTYPE_HT: /* TODO: is max the right choice? */ status.signal = max_t(__s8, max(rxhdr->phy_ht_power0, rxhdr->phy_ht_power1), rxhdr->phy_ht_power2); break; case B43_PHYTYPE_N: /* Broadcom has code for min and avg, but always uses max */ if (rxhdr->power0 == 16 || rxhdr->power0 == 32) status.signal = max(rxhdr->power1, rxhdr->power2); else status.signal = max(rxhdr->power0, rxhdr->power1); break; case B43_PHYTYPE_A: case B43_PHYTYPE_B: case B43_PHYTYPE_G: case B43_PHYTYPE_LP: status.signal = b43_rssi_postprocess(dev, rxhdr->jssi, (phystat0 & B43_RX_PHYST0_OFDM), (phystat0 & B43_RX_PHYST0_GAINCTL), (phystat3 & B43_RX_PHYST3_TRSTATE)); break; } if (phystat0 & B43_RX_PHYST0_OFDM) rate_idx = b43_plcp_get_bitrate_idx_ofdm(plcp, phytype == B43_PHYTYPE_A); else rate_idx = b43_plcp_get_bitrate_idx_cck(plcp); if (unlikely(rate_idx == -1)) { /* PLCP seems to be corrupted. * Drop the frame, if we are not interested in corrupted frames. */ if (!(dev->wl->filter_flags & FIF_PLCPFAIL)) goto drop; } status.rate_idx = rate_idx; status.antenna = !!(phystat0 & B43_RX_PHYST0_ANT); /* * All frames on monitor interfaces and beacons always need a full * 64-bit timestamp. Monitor interfaces need it for diagnostic * purposes and beacons for IBSS merging. * This code assumes we get to process the packet within 16 bits * of timestamp, i.e. about 65 milliseconds after the PHY received * the first symbol. */ if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) { u16 low_mactime_now; b43_tsf_read(dev, &status.mactime); low_mactime_now = status.mactime; status.mactime = status.mactime & ~0xFFFFULL; status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; status.flag |= RX_FLAG_MACTIME_MPDU; } chanid = (chanstat & B43_RX_CHAN_ID) >> B43_RX_CHAN_ID_SHIFT; switch (chanstat & B43_RX_CHAN_PHYTYPE) { case B43_PHYTYPE_A: status.band = IEEE80211_BAND_5GHZ; B43_WARN_ON(1); /* FIXME: We don't really know which value the "chanid" contains. * So the following assignment might be wrong. */ status.freq = b43_channel_to_freq_5ghz(chanid); break; case B43_PHYTYPE_G: status.band = IEEE80211_BAND_2GHZ; /* chanid is the radio channel cookie value as used * to tune the radio. */ status.freq = chanid + 2400; break; case B43_PHYTYPE_N: case B43_PHYTYPE_LP: case B43_PHYTYPE_HT: /* chanid is the SHM channel cookie. Which is the plain * channel number in b43. */ if (chanstat & B43_RX_CHAN_5GHZ) { status.band = IEEE80211_BAND_5GHZ; status.freq = b43_freq_to_channel_5ghz(chanid); } else { status.band = IEEE80211_BAND_2GHZ; status.freq = b43_freq_to_channel_2ghz(chanid); } break; default: B43_WARN_ON(1); goto drop; } memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); ieee80211_rx_ni(dev->wl->hw, skb); #if B43_DEBUG dev->rx_count++; #endif return; drop: dev_kfree_skb_any(skb); } void b43_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status) { b43_debugfs_log_txstat(dev, status); if (status->intermediate) return; if (status->for_ampdu) return; if (!status->acked) dev->wl->ieee_stats.dot11ACKFailureCount++; if (status->rts_count) { if (status->rts_count == 0xF) //FIXME dev->wl->ieee_stats.dot11RTSFailureCount++; else dev->wl->ieee_stats.dot11RTSSuccessCount++; } if (b43_using_pio_transfers(dev)) b43_pio_handle_txstatus(dev, status); else b43_dma_handle_txstatus(dev, status); b43_phy_txpower_check(dev, 0); } /* Fill out the mac80211 TXstatus report based on the b43-specific * txstatus report data. This returns a boolean whether the frame was * successfully transmitted. */ bool b43_fill_txstatus_report(struct b43_wldev *dev, struct ieee80211_tx_info *report, const struct b43_txstatus *status) { bool frame_success = true; int retry_limit; /* preserve the confiured retry limit before clearing the status * The xmit function has overwritten the rc's value with the actual * retry limit done by the hardware */ retry_limit = report->status.rates[0].count; ieee80211_tx_info_clear_status(report); if (status->acked) { /* The frame was ACKed. */ report->flags |= IEEE80211_TX_STAT_ACK; } else { /* The frame was not ACKed... */ if (!(report->flags & IEEE80211_TX_CTL_NO_ACK)) { /* ...but we expected an ACK. */ frame_success = false; } } if (status->frame_count == 0) { /* The frame was not transmitted at all. */ report->status.rates[0].count = 0; } else if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { /* * If the short retries (RTS, not data frame) have exceeded * the limit, the hw will not have tried the selected rate, * but will have used the fallback rate instead. * Don't let the rate control count attempts for the selected * rate in this case, otherwise the statistics will be off. */ report->status.rates[0].count = 0; report->status.rates[1].count = status->frame_count; } else { if (status->frame_count > retry_limit) { report->status.rates[0].count = retry_limit; report->status.rates[1].count = status->frame_count - retry_limit; } else { report->status.rates[0].count = status->frame_count; report->status.rates[1].idx = -1; } } return frame_success; } /* Stop any TX operation on the device (suspend the hardware queues) */ void b43_tx_suspend(struct b43_wldev *dev) { if (b43_using_pio_transfers(dev)) b43_pio_tx_suspend(dev); else b43_dma_tx_suspend(dev); } /* Resume any TX operation on the device (resume the hardware queues) */ void b43_tx_resume(struct b43_wldev *dev) { if (b43_using_pio_transfers(dev)) b43_pio_tx_resume(dev); else b43_dma_tx_resume(dev); } compat-drivers-2012-09-18/drivers/net/wireless/b43/wa.h0000644000175000017500000000022312026211315021626 0ustar mcgrofmcgrof#ifndef B43_WA_H_ #define B43_WA_H_ void b43_wa_initgains(struct b43_wldev *dev); void b43_wa_all(struct b43_wldev *dev); #endif /* B43_WA_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/wa.c0000644000175000017500000004365312026211315021637 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver PHY workarounds. Copyright (c) 2005-2007 Stefano Brivio Copyright (c) 2005-2007 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "main.h" #include "tables.h" #include "phy_common.h" #include "wa.h" static void b43_wa_papd(struct b43_wldev *dev) { u16 backup; backup = b43_ofdmtab_read16(dev, B43_OFDMTAB_PWRDYN2, 0); b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, 7); b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_APHY, 0, 0); b43_dummy_transmission(dev, true, true); b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, backup); } static void b43_wa_auxclipthr(struct b43_wldev *dev) { b43_phy_write(dev, B43_PHY_OFDM(0x8E), 0x3800); } static void b43_wa_afcdac(struct b43_wldev *dev) { b43_phy_write(dev, 0x0035, 0x03FF); b43_phy_write(dev, 0x0036, 0x0400); } static void b43_wa_txdc_offset(struct b43_wldev *dev) { b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 0, 0x0051); } void b43_wa_initgains(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; b43_phy_write(dev, B43_PHY_LNAHPFCTL, 0x1FF9); b43_phy_mask(dev, B43_PHY_LPFGAINCTL, 0xFF0F); if (phy->rev <= 2) b43_ofdmtab_write16(dev, B43_OFDMTAB_LPFGAIN, 0, 0x1FBF); b43_radio_write16(dev, 0x0002, 0x1FBF); b43_phy_write(dev, 0x0024, 0x4680); b43_phy_write(dev, 0x0020, 0x0003); b43_phy_write(dev, 0x001D, 0x0F40); b43_phy_write(dev, 0x001F, 0x1C00); if (phy->rev <= 3) b43_phy_maskset(dev, 0x002A, 0x00FF, 0x0400); else if (phy->rev == 5) { b43_phy_maskset(dev, 0x002A, 0x00FF, 0x1A00); b43_phy_write(dev, 0x00CC, 0x2121); } if (phy->rev >= 3) b43_phy_write(dev, 0x00BA, 0x3ED5); } static void b43_wa_divider(struct b43_wldev *dev) { b43_phy_mask(dev, 0x002B, ~0x0100); b43_phy_write(dev, 0x008E, 0x58C1); } static void b43_wa_gt(struct b43_wldev *dev) /* Gain table. */ { if (dev->phy.rev <= 2) { b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 0, 15); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 1, 31); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 2, 42); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 3, 48); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 4, 58); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 0, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 1, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 2, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 3, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 4, 21); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 5, 21); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 6, 25); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 0, 3); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 1, 3); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 2, 7); } else { b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 0, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 1, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 2, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 3, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 4, 21); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 5, 21); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 6, 25); } } static void b43_wa_rssi_lt(struct b43_wldev *dev) /* RSSI lookup table */ { int i; if (0 /* FIXME: For APHY.rev=2 this might be needed */) { for (i = 0; i < 8; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i + 8); for (i = 8; i < 16; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i - 8); } else { for (i = 0; i < 64; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i); } } static void b43_wa_analog(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u16 ofdmrev; ofdmrev = b43_phy_read(dev, B43_PHY_VERSION_OFDM) & B43_PHYVER_VERSION; if (ofdmrev > 2) { if (phy->type == B43_PHYTYPE_A) b43_phy_write(dev, B43_PHY_PWRDOWN, 0x1808); else b43_phy_write(dev, B43_PHY_PWRDOWN, 0x1000); } else { b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 3, 0x1044); b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 4, 0x7201); b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 6, 0x0040); } } static void b43_wa_dac(struct b43_wldev *dev) { if (dev->phy.analog == 1) b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, (b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 1) & ~0x0034) | 0x0008); else b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, (b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 1) & ~0x0078) | 0x0010); } static void b43_wa_fft(struct b43_wldev *dev) /* Fine frequency table */ { int i; if (dev->phy.type == B43_PHYTYPE_A) for (i = 0; i < B43_TAB_FINEFREQA_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_DACRFPABB, i, b43_tab_finefreqa[i]); else for (i = 0; i < B43_TAB_FINEFREQG_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_DACRFPABB, i, b43_tab_finefreqg[i]); } static void b43_wa_nft(struct b43_wldev *dev) /* Noise figure table */ { struct b43_phy *phy = &dev->phy; int i; if (phy->type == B43_PHYTYPE_A) { if (phy->rev == 2) for (i = 0; i < B43_TAB_NOISEA2_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noisea2[i]); else for (i = 0; i < B43_TAB_NOISEA3_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noisea3[i]); } else { if (phy->rev == 1) for (i = 0; i < B43_TAB_NOISEG1_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noiseg1[i]); else for (i = 0; i < B43_TAB_NOISEG2_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noiseg2[i]); } } static void b43_wa_rt(struct b43_wldev *dev) /* Rotor table */ { int i; for (i = 0; i < B43_TAB_ROTOR_SIZE; i++) b43_ofdmtab_write32(dev, B43_OFDMTAB_ROTOR, i, b43_tab_rotor[i]); } static void b43_write_null_nst(struct b43_wldev *dev) { int i; for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, 0); } static void b43_write_nst(struct b43_wldev *dev, const u16 *nst) { int i; for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, nst[i]); } static void b43_wa_nst(struct b43_wldev *dev) /* Noise scale table */ { struct b43_phy *phy = &dev->phy; if (phy->type == B43_PHYTYPE_A) { if (phy->rev <= 1) b43_write_null_nst(dev); else if (phy->rev == 2) b43_write_nst(dev, b43_tab_noisescalea2); else if (phy->rev == 3) b43_write_nst(dev, b43_tab_noisescalea3); else b43_write_nst(dev, b43_tab_noisescaleg3); } else { if (phy->rev >= 6) { if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN) b43_write_nst(dev, b43_tab_noisescaleg3); else b43_write_nst(dev, b43_tab_noisescaleg2); } else { b43_write_nst(dev, b43_tab_noisescaleg1); } } } static void b43_wa_art(struct b43_wldev *dev) /* ADV retard table */ { int i; for (i = 0; i < B43_TAB_RETARD_SIZE; i++) b43_ofdmtab_write32(dev, B43_OFDMTAB_ADVRETARD, i, b43_tab_retard[i]); } static void b43_wa_txlna_gain(struct b43_wldev *dev) { b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 13, 0x0000); } static void b43_wa_crs_reset(struct b43_wldev *dev) { b43_phy_write(dev, 0x002C, 0x0064); } static void b43_wa_2060txlna_gain(struct b43_wldev *dev) { b43_hf_write(dev, b43_hf_read(dev) | B43_HF_2060W); } static void b43_wa_lms(struct b43_wldev *dev) { b43_phy_maskset(dev, 0x0055, 0xFFC0, 0x0004); } static void b43_wa_mixedsignal(struct b43_wldev *dev) { b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, 3); } static void b43_wa_msst(struct b43_wldev *dev) /* Min sigma square table */ { struct b43_phy *phy = &dev->phy; int i; const u16 *tab; if (phy->type == B43_PHYTYPE_A) { tab = b43_tab_sigmasqr1; } else if (phy->type == B43_PHYTYPE_G) { tab = b43_tab_sigmasqr2; } else { B43_WARN_ON(1); return; } for (i = 0; i < B43_TAB_SIGMASQR_SIZE; i++) { b43_ofdmtab_write16(dev, B43_OFDMTAB_MINSIGSQ, i, tab[i]); } } static void b43_wa_iqadc(struct b43_wldev *dev) { if (dev->phy.analog == 4) b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 0, b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 0) & ~0xF000); } static void b43_wa_crs_ed(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; if (phy->rev == 1) { b43_phy_write(dev, B43_PHY_CRSTHRES1_R1, 0x4F19); } else if (phy->rev == 2) { b43_phy_write(dev, B43_PHY_CRSTHRES1, 0x1861); b43_phy_write(dev, B43_PHY_CRSTHRES2, 0x0271); b43_phy_set(dev, B43_PHY_ANTDWELL, 0x0800); } else { b43_phy_write(dev, B43_PHY_CRSTHRES1, 0x0098); b43_phy_write(dev, B43_PHY_CRSTHRES2, 0x0070); b43_phy_write(dev, B43_PHY_OFDM(0xC9), 0x0080); b43_phy_set(dev, B43_PHY_ANTDWELL, 0x0800); } } static void b43_wa_crs_thr(struct b43_wldev *dev) { b43_phy_maskset(dev, B43_PHY_CRS0, ~0x03C0, 0xD000); } static void b43_wa_crs_blank(struct b43_wldev *dev) { b43_phy_write(dev, B43_PHY_OFDM(0x2C), 0x005A); } static void b43_wa_cck_shiftbits(struct b43_wldev *dev) { b43_phy_write(dev, B43_PHY_CCKSHIFTBITS, 0x0026); } static void b43_wa_wrssi_offset(struct b43_wldev *dev) { int i; if (dev->phy.rev == 1) { for (i = 0; i < 16; i++) { b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI_R1, i, 0x0020); } } else { for (i = 0; i < 32; i++) { b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI, i, 0x0820); } } } static void b43_wa_txpuoff_rxpuon(struct b43_wldev *dev) { b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_0F, 2, 15); b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_0F, 3, 20); } static void b43_wa_altagc(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; if (phy->rev == 1) { b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 0, 254); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 1, 13); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 2, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 3, 25); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 0, 0x2710); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 1, 0x9B83); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 2, 0x9B83); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 3, 0x0F8D); b43_phy_write(dev, B43_PHY_LMS, 4); } else { b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0, 254); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 1, 13); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 2, 19); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 3, 25); } b43_phy_maskset(dev, B43_PHY_CCKSHIFTBITS_WA, 0x00FF, 0x5700); b43_phy_maskset(dev, B43_PHY_OFDM(0x1A), ~0x007F, 0x000F); b43_phy_maskset(dev, B43_PHY_OFDM(0x1A), ~0x3F80, 0x2B80); b43_phy_maskset(dev, B43_PHY_ANTWRSETT, 0xF0FF, 0x0300); b43_radio_set(dev, 0x7A, 0x0008); b43_phy_maskset(dev, B43_PHY_N1P1GAIN, ~0x000F, 0x0008); b43_phy_maskset(dev, B43_PHY_P1P2GAIN, ~0x0F00, 0x0600); b43_phy_maskset(dev, B43_PHY_N1N2GAIN, ~0x0F00, 0x0700); b43_phy_maskset(dev, B43_PHY_N1P1GAIN, ~0x0F00, 0x0100); if (phy->rev == 1) { b43_phy_maskset(dev, B43_PHY_N1N2GAIN, ~0x000F, 0x0007); } b43_phy_maskset(dev, B43_PHY_OFDM(0x88), ~0x00FF, 0x001C); b43_phy_maskset(dev, B43_PHY_OFDM(0x88), ~0x3F00, 0x0200); b43_phy_maskset(dev, B43_PHY_OFDM(0x96), ~0x00FF, 0x001C); b43_phy_maskset(dev, B43_PHY_OFDM(0x89), ~0x00FF, 0x0020); b43_phy_maskset(dev, B43_PHY_OFDM(0x89), ~0x3F00, 0x0200); b43_phy_maskset(dev, B43_PHY_OFDM(0x82), ~0x00FF, 0x002E); b43_phy_maskset(dev, B43_PHY_OFDM(0x96), 0x00FF, 0x1A00); b43_phy_maskset(dev, B43_PHY_OFDM(0x81), ~0x00FF, 0x0028); b43_phy_maskset(dev, B43_PHY_OFDM(0x81), 0x00FF, 0x2C00); if (phy->rev == 1) { b43_phy_write(dev, B43_PHY_PEAK_COUNT, 0x092B); b43_phy_maskset(dev, B43_PHY_OFDM(0x1B), ~0x001E, 0x0002); } else { b43_phy_mask(dev, B43_PHY_OFDM(0x1B), ~0x001E); b43_phy_write(dev, B43_PHY_OFDM(0x1F), 0x287A); b43_phy_maskset(dev, B43_PHY_LPFGAINCTL, ~0x000F, 0x0004); if (phy->rev >= 6) { b43_phy_write(dev, B43_PHY_OFDM(0x22), 0x287A); b43_phy_maskset(dev, B43_PHY_LPFGAINCTL, 0x0FFF, 0x3000); } } b43_phy_maskset(dev, B43_PHY_DIVSRCHIDX, 0x8080, 0x7874); b43_phy_write(dev, B43_PHY_OFDM(0x8E), 0x1C00); if (phy->rev == 1) { b43_phy_maskset(dev, B43_PHY_DIVP1P2GAIN, ~0x0F00, 0x0600); b43_phy_write(dev, B43_PHY_OFDM(0x8B), 0x005E); b43_phy_maskset(dev, B43_PHY_ANTWRSETT, ~0x00FF, 0x001E); b43_phy_write(dev, B43_PHY_OFDM(0x8D), 0x0002); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 0, 0); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 1, 7); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 2, 16); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 3, 28); } else { b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 0, 0); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 1, 7); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 2, 16); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 3, 28); } if (phy->rev >= 6) { b43_phy_mask(dev, B43_PHY_OFDM(0x26), ~0x0003); b43_phy_mask(dev, B43_PHY_OFDM(0x26), ~0x1000); } b43_phy_read(dev, B43_PHY_VERSION_OFDM); /* Dummy read */ } static void b43_wa_tr_ltov(struct b43_wldev *dev) /* TR Lookup Table Original Values */ { b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0xC480); } static void b43_wa_cpll_nonpilot(struct b43_wldev *dev) { b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_11, 0, 0); b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_11, 1, 0); } static void b43_wa_rssi_adc(struct b43_wldev *dev) { if (dev->phy.analog == 4) b43_phy_write(dev, 0x00DC, 0x7454); } static void b43_wa_boards_a(struct b43_wldev *dev) { if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM && dev->dev->board_type == SSB_BOARD_BU4306 && dev->dev->board_rev < 0x30) { b43_phy_write(dev, 0x0010, 0xE000); b43_phy_write(dev, 0x0013, 0x0140); b43_phy_write(dev, 0x0014, 0x0280); } else { if (dev->dev->board_type == SSB_BOARD_MP4318 && dev->dev->board_rev < 0x20) { b43_phy_write(dev, 0x0013, 0x0210); b43_phy_write(dev, 0x0014, 0x0840); } else { b43_phy_write(dev, 0x0013, 0x0140); b43_phy_write(dev, 0x0014, 0x0280); } if (dev->phy.rev <= 4) b43_phy_write(dev, 0x0010, 0xE000); else b43_phy_write(dev, 0x0010, 0x2000); b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 1, 0x0039); b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_APHY, 7, 0x0040); } } static void b43_wa_boards_g(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy *phy = &dev->phy; if (dev->dev->board_vendor != SSB_BOARDVENDOR_BCM || dev->dev->board_type != SSB_BOARD_BU4306 || dev->dev->board_rev != 0x17) { if (phy->rev < 2) { b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 1, 0x0002); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 2, 0x0001); } else { b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 1, 0x0002); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 2, 0x0001); if ((sprom->boardflags_lo & B43_BFL_EXTLNA) && (phy->rev >= 7)) { b43_phy_mask(dev, B43_PHY_EXTG(0x11), 0xF7FF); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0020, 0x0001); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0021, 0x0001); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0022, 0x0001); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0023, 0x0000); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0000, 0x0000); b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0003, 0x0002); } } } if (sprom->boardflags_lo & B43_BFL_FEM) { b43_phy_write(dev, B43_PHY_GTABCTL, 0x3120); b43_phy_write(dev, B43_PHY_GTABDATA, 0xC480); } } void b43_wa_all(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; if (phy->type == B43_PHYTYPE_A) { switch (phy->rev) { case 2: b43_wa_papd(dev); b43_wa_auxclipthr(dev); b43_wa_afcdac(dev); b43_wa_txdc_offset(dev); b43_wa_initgains(dev); b43_wa_divider(dev); b43_wa_gt(dev); b43_wa_rssi_lt(dev); b43_wa_analog(dev); b43_wa_dac(dev); b43_wa_fft(dev); b43_wa_nft(dev); b43_wa_rt(dev); b43_wa_nst(dev); b43_wa_art(dev); b43_wa_txlna_gain(dev); b43_wa_crs_reset(dev); b43_wa_2060txlna_gain(dev); b43_wa_lms(dev); break; case 3: b43_wa_papd(dev); b43_wa_mixedsignal(dev); b43_wa_rssi_lt(dev); b43_wa_txdc_offset(dev); b43_wa_initgains(dev); b43_wa_dac(dev); b43_wa_nft(dev); b43_wa_nst(dev); b43_wa_msst(dev); b43_wa_analog(dev); b43_wa_gt(dev); b43_wa_txpuoff_rxpuon(dev); b43_wa_txlna_gain(dev); break; case 5: b43_wa_iqadc(dev); case 6: b43_wa_papd(dev); b43_wa_rssi_lt(dev); b43_wa_txdc_offset(dev); b43_wa_initgains(dev); b43_wa_dac(dev); b43_wa_nft(dev); b43_wa_nst(dev); b43_wa_msst(dev); b43_wa_analog(dev); b43_wa_gt(dev); b43_wa_txpuoff_rxpuon(dev); b43_wa_txlna_gain(dev); break; case 7: b43_wa_iqadc(dev); b43_wa_papd(dev); b43_wa_rssi_lt(dev); b43_wa_txdc_offset(dev); b43_wa_initgains(dev); b43_wa_dac(dev); b43_wa_nft(dev); b43_wa_nst(dev); b43_wa_msst(dev); b43_wa_analog(dev); b43_wa_gt(dev); b43_wa_txpuoff_rxpuon(dev); b43_wa_txlna_gain(dev); b43_wa_rssi_adc(dev); default: B43_WARN_ON(1); } b43_wa_boards_a(dev); } else if (phy->type == B43_PHYTYPE_G) { switch (phy->rev) { case 1://XXX review rev1 b43_wa_crs_ed(dev); b43_wa_crs_thr(dev); b43_wa_crs_blank(dev); b43_wa_cck_shiftbits(dev); b43_wa_fft(dev); b43_wa_nft(dev); b43_wa_rt(dev); b43_wa_nst(dev); b43_wa_art(dev); b43_wa_wrssi_offset(dev); b43_wa_altagc(dev); break; case 2: case 6: case 7: case 8: case 9: b43_wa_tr_ltov(dev); b43_wa_crs_ed(dev); b43_wa_rssi_lt(dev); b43_wa_nft(dev); b43_wa_nst(dev); b43_wa_msst(dev); b43_wa_wrssi_offset(dev); b43_wa_altagc(dev); b43_wa_analog(dev); b43_wa_txpuoff_rxpuon(dev); break; default: B43_WARN_ON(1); } b43_wa_boards_g(dev); } else { /* No N PHY support so far, LP PHY is in phy_lp.c */ B43_WARN_ON(1); } b43_wa_cpll_nonpilot(dev); } compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_phy_lcn.h0000644000175000017500000000170212026211315024210 0ustar mcgrofmcgrof#ifndef B43_TABLES_PHY_LCN_H_ #define B43_TABLES_PHY_LCN_H_ /* The LCN-PHY tables. */ #define B43_LCNTAB_TYPEMASK 0xF0000000 #define B43_LCNTAB_8BIT 0x10000000 #define B43_LCNTAB_16BIT 0x20000000 #define B43_LCNTAB_32BIT 0x30000000 #define B43_LCNTAB8(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_8BIT) #define B43_LCNTAB16(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_16BIT) #define B43_LCNTAB32(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_32BIT) #define B43_LCNTAB_TX_GAIN_SIZE 128 u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset); void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data); void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value); void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *_data); void b43_phy_lcn_tables_init(struct b43_wldev *dev); #endif /* B43_TABLES_PHY_LCN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_phy_lcn.c0000644000175000017500000006115212026211315024210 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n LCN-PHY data tables Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "tables_phy_lcn.h" #include "phy_common.h" #include "phy_lcn.h" struct b43_lcntab_tx_gain_tbl_entry { u8 gm; u8 pga; u8 pad; u8 dac; u8 bb_mult; }; /************************************************** * Static tables. **************************************************/ static const u16 b43_lcntab_0x02[] = { 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, }; static const u16 b43_lcntab_0x01[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u32 b43_lcntab_0x0b[] = { 0x000141f8, 0x000021f8, 0x000021fb, 0x000041fb, 0x0001fedb, 0x0000217b, 0x00002133, 0x000040eb, 0x0001fea3, 0x0000024b, }; static const u32 b43_lcntab_0x0c[] = { 0x00100001, 0x00200010, 0x00300001, 0x00400010, 0x00500022, 0x00600122, 0x00700222, 0x00800322, 0x00900422, 0x00a00522, 0x00b00622, 0x00c00722, 0x00d00822, 0x00f00922, 0x00100a22, 0x00200b22, 0x00300c22, 0x00400d22, 0x00500e22, 0x00600f22, }; static const u32 b43_lcntab_0x0d[] = { 0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x20000000, 0x00000000, 0x30000000, 0x00000000, 0x40000000, 0x00000000, 0x50000000, 0x00000000, 0x60000000, 0x00000000, 0x70000000, 0x00000000, 0x80000000, 0x00000000, 0x90000000, 0x00000008, 0xa0000000, 0x00000008, 0xb0000000, 0x00000008, 0xc0000000, 0x00000008, 0xd0000000, 0x00000008, 0xe0000000, 0x00000008, 0xf0000000, 0x00000008, 0x00000000, 0x00000009, 0x10000000, 0x00000009, 0x20000000, 0x00000019, 0x30000000, 0x00000019, 0x40000000, 0x00000019, 0x50000000, 0x00000019, 0x60000000, 0x00000019, 0x70000000, 0x00000019, 0x80000000, 0x00000019, 0x90000000, 0x00000019, 0xa0000000, 0x00000019, 0xb0000000, 0x00000019, 0xc0000000, 0x00000019, 0xd0000000, 0x00000019, 0xe0000000, 0x00000019, 0xf0000000, 0x00000019, 0x00000000, 0x0000001a, 0x10000000, 0x0000001a, 0x20000000, 0x0000001a, 0x30000000, 0x0000001a, 0x40000000, 0x0000001a, 0x50000000, 0x00000002, 0x60000000, 0x00000002, 0x70000000, 0x00000002, 0x80000000, 0x00000002, 0x90000000, 0x00000002, 0xa0000000, 0x00000002, 0xb0000000, 0x00000002, 0xc0000000, 0x0000000a, 0xd0000000, 0x0000000a, 0xe0000000, 0x0000000a, 0xf0000000, 0x0000000a, 0x00000000, 0x0000000b, 0x10000000, 0x0000000b, 0x20000000, 0x0000000b, 0x30000000, 0x0000000b, 0x40000000, 0x0000000b, 0x50000000, 0x0000001b, 0x60000000, 0x0000001b, 0x70000000, 0x0000001b, 0x80000000, 0x0000001b, 0x90000000, 0x0000001b, 0xa0000000, 0x0000001b, 0xb0000000, 0x0000001b, 0xc0000000, 0x0000001b, 0xd0000000, 0x0000001b, 0xe0000000, 0x0000001b, 0xf0000000, 0x0000001b, 0x00000000, 0x0000001c, 0x10000000, 0x0000001c, 0x20000000, 0x0000001c, 0x30000000, 0x0000001c, 0x40000000, 0x0000001c, 0x50000000, 0x0000001c, 0x60000000, 0x0000001c, 0x70000000, 0x0000001c, 0x80000000, 0x0000001c, 0x90000000, 0x0000001c, }; static const u16 b43_lcntab_0x0e[] = { 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x058b, 0x058c, 0x058d, 0x058e, 0x058f, 0x0090, 0x0091, 0x0092, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, 0x019f, 0x01a0, 0x01a1, 0x01a2, 0x01a3, 0x01a4, 0x01a5, 0x0000, }; static const u16 b43_lcntab_0x0f[] = { 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, }; static const u16 b43_lcntab_0x10[] = { 0x005f, 0x0036, 0x0029, 0x001f, 0x005f, 0x0036, 0x0029, 0x001f, 0x005f, 0x0036, 0x0029, 0x001f, 0x005f, 0x0036, 0x0029, 0x001f, }; static const u16 b43_lcntab_0x11[] = { 0x0009, 0x000f, 0x0014, 0x0018, 0x00fe, 0x0007, 0x000b, 0x000f, 0x00fb, 0x00fe, 0x0001, 0x0005, 0x0008, 0x000b, 0x000e, 0x0011, 0x0014, 0x0017, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015, 0x0018, 0x001b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x00eb, 0x0000, 0x0000, }; static const u32 b43_lcntab_0x12[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000d, 0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf, 0x000000d3, 0x00000113, 0x00000513, 0x00000913, 0x00000953, 0x00000d53, 0x00001153, 0x00001193, 0x00005193, 0x00009193, 0x0000d193, 0x00011193, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000004, 0x00000008, 0x00000001, 0x00000005, 0x00000009, 0x0000000d, 0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf, 0x000000d3, 0x00000113, 0x00000513, 0x00000913, 0x00000953, 0x00000d53, 0x00001153, 0x00005153, 0x00009153, 0x0000d153, 0x00011153, 0x00015153, 0x00019153, 0x0001d153, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u16 b43_lcntab_0x14[] = { 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003, 0x0001, 0x0003, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003, 0x0001, 0x0003, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, }; static const u16 b43_lcntab_0x17[] = { 0x001a, 0x0034, 0x004e, 0x0068, 0x009c, 0x00d0, 0x00ea, 0x0104, 0x0034, 0x0068, 0x009c, 0x00d0, 0x0138, 0x01a0, 0x01d4, 0x0208, 0x004e, 0x009c, 0x00ea, 0x0138, 0x01d4, 0x0270, 0x02be, 0x030c, 0x0068, 0x00d0, 0x0138, 0x01a0, 0x0270, 0x0340, 0x03a8, 0x0410, 0x0018, 0x009c, 0x00d0, 0x0104, 0x00ea, 0x0138, 0x0186, 0x00d0, 0x0104, 0x0104, 0x0138, 0x016c, 0x016c, 0x01a0, 0x0138, 0x0186, 0x0186, 0x01d4, 0x0222, 0x0222, 0x0270, 0x0104, 0x0138, 0x016c, 0x0138, 0x016c, 0x01a0, 0x01d4, 0x01a0, 0x01d4, 0x0208, 0x0208, 0x023c, 0x0186, 0x01d4, 0x0222, 0x01d4, 0x0222, 0x0270, 0x02be, 0x0270, 0x02be, 0x030c, 0x030c, 0x035a, 0x0036, 0x006c, 0x00a2, 0x00d8, 0x0144, 0x01b0, 0x01e6, 0x021c, 0x006c, 0x00d8, 0x0144, 0x01b0, 0x0288, 0x0360, 0x03cc, 0x0438, 0x00a2, 0x0144, 0x01e6, 0x0288, 0x03cc, 0x0510, 0x05b2, 0x0654, 0x00d8, 0x01b0, 0x0288, 0x0360, 0x0510, 0x06c0, 0x0798, 0x0870, 0x0018, 0x0144, 0x01b0, 0x021c, 0x01e6, 0x0288, 0x032a, 0x01b0, 0x021c, 0x021c, 0x0288, 0x02f4, 0x02f4, 0x0360, 0x0288, 0x032a, 0x032a, 0x03cc, 0x046e, 0x046e, 0x0510, 0x021c, 0x0288, 0x02f4, 0x0288, 0x02f4, 0x0360, 0x03cc, 0x0360, 0x03cc, 0x0438, 0x0438, 0x04a4, 0x032a, 0x03cc, 0x046e, 0x03cc, 0x046e, 0x0510, 0x05b2, 0x0510, 0x05b2, 0x0654, 0x0654, 0x06f6, }; static const u16 b43_lcntab_0x00[] = { 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u32 b43_lcntab_0x18[] = { 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000, }; /************************************************** * TX gain. **************************************************/ const struct b43_lcntab_tx_gain_tbl_entry b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0[B43_LCNTAB_TX_GAIN_SIZE] = { { 0x03, 0x00, 0x1f, 0x0, 0x48 }, { 0x03, 0x00, 0x1f, 0x0, 0x46 }, { 0x03, 0x00, 0x1f, 0x0, 0x44 }, { 0x03, 0x00, 0x1e, 0x0, 0x43 }, { 0x03, 0x00, 0x1d, 0x0, 0x44 }, { 0x03, 0x00, 0x1c, 0x0, 0x44 }, { 0x03, 0x00, 0x1b, 0x0, 0x45 }, { 0x03, 0x00, 0x1a, 0x0, 0x46 }, { 0x03, 0x00, 0x19, 0x0, 0x46 }, { 0x03, 0x00, 0x18, 0x0, 0x47 }, { 0x03, 0x00, 0x17, 0x0, 0x48 }, { 0x03, 0x00, 0x17, 0x0, 0x46 }, { 0x03, 0x00, 0x16, 0x0, 0x47 }, { 0x03, 0x00, 0x15, 0x0, 0x48 }, { 0x03, 0x00, 0x15, 0x0, 0x46 }, { 0x03, 0x00, 0x15, 0x0, 0x44 }, { 0x03, 0x00, 0x15, 0x0, 0x42 }, { 0x03, 0x00, 0x15, 0x0, 0x40 }, { 0x03, 0x00, 0x15, 0x0, 0x3f }, { 0x03, 0x00, 0x14, 0x0, 0x40 }, { 0x03, 0x00, 0x13, 0x0, 0x41 }, { 0x03, 0x00, 0x13, 0x0, 0x40 }, { 0x03, 0x00, 0x12, 0x0, 0x41 }, { 0x03, 0x00, 0x12, 0x0, 0x40 }, { 0x03, 0x00, 0x11, 0x0, 0x41 }, { 0x03, 0x00, 0x11, 0x0, 0x40 }, { 0x03, 0x00, 0x10, 0x0, 0x41 }, { 0x03, 0x00, 0x10, 0x0, 0x40 }, { 0x03, 0x00, 0x10, 0x0, 0x3e }, { 0x03, 0x00, 0x10, 0x0, 0x3c }, { 0x03, 0x00, 0x10, 0x0, 0x3a }, { 0x03, 0x00, 0x0f, 0x0, 0x3d }, { 0x03, 0x00, 0x0f, 0x0, 0x3b }, { 0x03, 0x00, 0x0e, 0x0, 0x3d }, { 0x03, 0x00, 0x0e, 0x0, 0x3c }, { 0x03, 0x00, 0x0e, 0x0, 0x3a }, { 0x03, 0x00, 0x0d, 0x0, 0x3c }, { 0x03, 0x00, 0x0d, 0x0, 0x3b }, { 0x03, 0x00, 0x0c, 0x0, 0x3e }, { 0x03, 0x00, 0x0c, 0x0, 0x3c }, { 0x03, 0x00, 0x0c, 0x0, 0x3a }, { 0x03, 0x00, 0x0b, 0x0, 0x3e }, { 0x03, 0x00, 0x0b, 0x0, 0x3c }, { 0x03, 0x00, 0x0b, 0x0, 0x3b }, { 0x03, 0x00, 0x0b, 0x0, 0x39 }, { 0x03, 0x00, 0x0a, 0x0, 0x3d }, { 0x03, 0x00, 0x0a, 0x0, 0x3b }, { 0x03, 0x00, 0x0a, 0x0, 0x39 }, { 0x03, 0x00, 0x09, 0x0, 0x3e }, { 0x03, 0x00, 0x09, 0x0, 0x3c }, { 0x03, 0x00, 0x09, 0x0, 0x3a }, { 0x03, 0x00, 0x09, 0x0, 0x39 }, { 0x03, 0x00, 0x08, 0x0, 0x3e }, { 0x03, 0x00, 0x08, 0x0, 0x3c }, { 0x03, 0x00, 0x08, 0x0, 0x3a }, { 0x03, 0x00, 0x08, 0x0, 0x39 }, { 0x03, 0x00, 0x08, 0x0, 0x37 }, { 0x03, 0x00, 0x07, 0x0, 0x3d }, { 0x03, 0x00, 0x07, 0x0, 0x3c }, { 0x03, 0x00, 0x07, 0x0, 0x3a }, { 0x03, 0x00, 0x07, 0x0, 0x38 }, { 0x03, 0x00, 0x07, 0x0, 0x37 }, { 0x03, 0x00, 0x06, 0x0, 0x3e }, { 0x03, 0x00, 0x06, 0x0, 0x3c }, { 0x03, 0x00, 0x06, 0x0, 0x3a }, { 0x03, 0x00, 0x06, 0x0, 0x39 }, { 0x03, 0x00, 0x06, 0x0, 0x37 }, { 0x03, 0x00, 0x06, 0x0, 0x36 }, { 0x03, 0x00, 0x06, 0x0, 0x34 }, { 0x03, 0x00, 0x05, 0x0, 0x3d }, { 0x03, 0x00, 0x05, 0x0, 0x3b }, { 0x03, 0x00, 0x05, 0x0, 0x39 }, { 0x03, 0x00, 0x05, 0x0, 0x38 }, { 0x03, 0x00, 0x05, 0x0, 0x36 }, { 0x03, 0x00, 0x05, 0x0, 0x35 }, { 0x03, 0x00, 0x05, 0x0, 0x33 }, { 0x03, 0x00, 0x04, 0x0, 0x3e }, { 0x03, 0x00, 0x04, 0x0, 0x3c }, { 0x03, 0x00, 0x04, 0x0, 0x3a }, { 0x03, 0x00, 0x04, 0x0, 0x39 }, { 0x03, 0x00, 0x04, 0x0, 0x37 }, { 0x03, 0x00, 0x04, 0x0, 0x36 }, { 0x03, 0x00, 0x04, 0x0, 0x34 }, { 0x03, 0x00, 0x04, 0x0, 0x33 }, { 0x03, 0x00, 0x04, 0x0, 0x31 }, { 0x03, 0x00, 0x04, 0x0, 0x30 }, { 0x03, 0x00, 0x04, 0x0, 0x2e }, { 0x03, 0x00, 0x03, 0x0, 0x3c }, { 0x03, 0x00, 0x03, 0x0, 0x3a }, { 0x03, 0x00, 0x03, 0x0, 0x39 }, { 0x03, 0x00, 0x03, 0x0, 0x37 }, { 0x03, 0x00, 0x03, 0x0, 0x36 }, { 0x03, 0x00, 0x03, 0x0, 0x34 }, { 0x03, 0x00, 0x03, 0x0, 0x33 }, { 0x03, 0x00, 0x03, 0x0, 0x31 }, { 0x03, 0x00, 0x03, 0x0, 0x30 }, { 0x03, 0x00, 0x03, 0x0, 0x2e }, { 0x03, 0x00, 0x03, 0x0, 0x2d }, { 0x03, 0x00, 0x03, 0x0, 0x2c }, { 0x03, 0x00, 0x03, 0x0, 0x2b }, { 0x03, 0x00, 0x03, 0x0, 0x29 }, { 0x03, 0x00, 0x02, 0x0, 0x3d }, { 0x03, 0x00, 0x02, 0x0, 0x3b }, { 0x03, 0x00, 0x02, 0x0, 0x39 }, { 0x03, 0x00, 0x02, 0x0, 0x38 }, { 0x03, 0x00, 0x02, 0x0, 0x36 }, { 0x03, 0x00, 0x02, 0x0, 0x35 }, { 0x03, 0x00, 0x02, 0x0, 0x33 }, { 0x03, 0x00, 0x02, 0x0, 0x32 }, { 0x03, 0x00, 0x02, 0x0, 0x30 }, { 0x03, 0x00, 0x02, 0x0, 0x2f }, { 0x03, 0x00, 0x02, 0x0, 0x2e }, { 0x03, 0x00, 0x02, 0x0, 0x2c }, { 0x03, 0x00, 0x02, 0x0, 0x2b }, { 0x03, 0x00, 0x02, 0x0, 0x2a }, { 0x03, 0x00, 0x02, 0x0, 0x29 }, { 0x03, 0x00, 0x02, 0x0, 0x27 }, { 0x03, 0x00, 0x02, 0x0, 0x26 }, { 0x03, 0x00, 0x02, 0x0, 0x25 }, { 0x03, 0x00, 0x02, 0x0, 0x24 }, { 0x03, 0x00, 0x02, 0x0, 0x23 }, { 0x03, 0x00, 0x02, 0x0, 0x22 }, { 0x03, 0x00, 0x02, 0x0, 0x21 }, { 0x03, 0x00, 0x02, 0x0, 0x20 }, { 0x03, 0x00, 0x01, 0x0, 0x3f }, { 0x03, 0x00, 0x01, 0x0, 0x3d }, { 0x03, 0x00, 0x01, 0x0, 0x3b }, { 0x03, 0x00, 0x01, 0x0, 0x39 }, }; /************************************************** * SW control. **************************************************/ const u16 b43_lcntab_sw_ctl_4313_epa_rev0[] = { 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, }; /************************************************** * R/W ops. **************************************************/ u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset) { u32 type, value; type = offset & B43_LCNTAB_TYPEMASK; offset &= ~B43_LCNTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); switch (type) { case B43_LCNTAB_8BIT: b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO) & 0xFF; break; case B43_LCNTAB_16BIT: b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); break; case B43_LCNTAB_32BIT: b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); value |= (b43_phy_read(dev, B43_PHY_LCN_TABLE_DATAHI) << 16); break; default: B43_WARN_ON(1); value = 0; } return value; } void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data) { u32 type; u8 *data = _data; unsigned int i; type = offset & B43_LCNTAB_TYPEMASK; offset &= ~B43_LCNTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { switch (type) { case B43_LCNTAB_8BIT: *data = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO) & 0xFF; data++; break; case B43_LCNTAB_16BIT: *((u16 *)data) = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); data += 2; break; case B43_LCNTAB_32BIT: *((u32 *)data) = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); *((u32 *)data) |= (b43_phy_read(dev, B43_PHY_LCN_TABLE_DATAHI) << 16); data += 4; break; default: B43_WARN_ON(1); } } } void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value) { u32 type; type = offset & B43_LCNTAB_TYPEMASK; offset &= 0xFFFF; switch (type) { case B43_LCNTAB_8BIT: B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); break; case B43_LCNTAB_16BIT: B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); break; case B43_LCNTAB_32BIT: b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, value >> 16); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value & 0xFFFF); break; default: B43_WARN_ON(1); } return; } void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *_data) { u32 type, value; const u8 *data = _data; unsigned int i; type = offset & B43_LCNTAB_TYPEMASK; offset &= ~B43_LCNTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { switch (type) { case B43_LCNTAB_8BIT: value = *data; data++; B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); break; case B43_LCNTAB_16BIT: value = *((u16 *)data); data += 2; B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); break; case B43_LCNTAB_32BIT: value = *((u32 *)data); data += 4; b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, value >> 16); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value & 0xFFFF); break; default: B43_WARN_ON(1); } } } /************************************************** * Tables ops. **************************************************/ #define lcntab_upload(dev, offset, data) do { \ b43_lcntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ } while (0) static void b43_phy_lcn_upload_static_tables(struct b43_wldev *dev) { lcntab_upload(dev, B43_LCNTAB16(0x02, 0), b43_lcntab_0x02); lcntab_upload(dev, B43_LCNTAB16(0x01, 0), b43_lcntab_0x01); lcntab_upload(dev, B43_LCNTAB32(0x0b, 0), b43_lcntab_0x0b); lcntab_upload(dev, B43_LCNTAB32(0x0c, 0), b43_lcntab_0x0c); lcntab_upload(dev, B43_LCNTAB32(0x0d, 0), b43_lcntab_0x0d); lcntab_upload(dev, B43_LCNTAB16(0x0e, 0), b43_lcntab_0x0e); lcntab_upload(dev, B43_LCNTAB16(0x0f, 0), b43_lcntab_0x0f); lcntab_upload(dev, B43_LCNTAB16(0x10, 0), b43_lcntab_0x10); lcntab_upload(dev, B43_LCNTAB16(0x11, 0), b43_lcntab_0x11); lcntab_upload(dev, B43_LCNTAB32(0x12, 0), b43_lcntab_0x12); lcntab_upload(dev, B43_LCNTAB16(0x14, 0), b43_lcntab_0x14); lcntab_upload(dev, B43_LCNTAB16(0x17, 0), b43_lcntab_0x17); lcntab_upload(dev, B43_LCNTAB16(0x00, 0), b43_lcntab_0x00); lcntab_upload(dev, B43_LCNTAB32(0x18, 0), b43_lcntab_0x18); } void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev, const struct b43_lcntab_tx_gain_tbl_entry *gain_table) { u32 i; u32 val; u16 pa_gain = 0x70; if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) pa_gain = 0x10; for (i = 0; i < B43_LCNTAB_TX_GAIN_SIZE; i++) { val = ((pa_gain << 24) | (gain_table[i].pad << 16) | (gain_table[i].pga << 8) | gain_table[i].gm); b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0xc0 + i), val); /* brcmsmac doesn't maskset, we follow newer wl here */ val = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i)); val &= 0x000fffff; val |= ((gain_table[i].dac << 28) | (gain_table[i].bb_mult << 20)); b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x140 + i), val); } } /* wlc_lcnphy_load_rfpower */ static void b43_phy_lcn_load_rfpower(struct b43_wldev *dev) { u32 bbmult, rfgain; u8 i; for (i = 0; i < 128; i++) { bbmult = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i)); bbmult >>= 20; rfgain = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0xc0 + i)); /* TODO: calculate value for 0x240 + i table offset * b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), val); */ } } /* Not implemented in brcmsmac, noticed in wl in MMIO dump */ static void b43_phy_lcn_rewrite_rfpower_table(struct b43_wldev *dev) { int i; u32 tmp; for (i = 0; i < 128; i++) { tmp = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x240 + i)); b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), tmp); } } /* wlc_lcnphy_clear_papd_comptable */ static void b43_phy_lcn_clean_papd_comp_table(struct b43_wldev *dev) { u8 i; for (i = 0; i < 0x80; i++) b43_lcntab_write(dev, B43_LCNTAB32(0x18, i), 0x80000); } /* wlc_lcnphy_tbl_init */ void b43_phy_lcn_tables_init(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; b43_phy_lcn_upload_static_tables(dev); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if (sprom->boardflags_lo & B43_BFL_FEM) b43_phy_lcn_load_tx_gain_tab(dev, b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0); else b43err(dev->wl, "TX gain table unknown for this card\n"); } if (sprom->boardflags_lo & B43_BFL_FEM && !(sprom->boardflags_hi & B43_BFH_FEM_BT)) b43_lcntab_write_bulk(dev, B43_LCNTAB16(0xf, 0), ARRAY_SIZE(b43_lcntab_sw_ctl_4313_epa_rev0), b43_lcntab_sw_ctl_4313_epa_rev0); else b43err(dev->wl, "SW ctl table is unknown for this card\n"); b43_phy_lcn_load_rfpower(dev); b43_phy_lcn_rewrite_rfpower_table(dev); b43_phy_lcn_clean_papd_comp_table(dev); } compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_phy_ht.h0000644000175000017500000000205412026211315024050 0ustar mcgrofmcgrof#ifndef B43_TABLES_PHY_HT_H_ #define B43_TABLES_PHY_HT_H_ /* The HT-PHY tables. */ #define B43_HTTAB_TYPEMASK 0xF0000000 #define B43_HTTAB_8BIT 0x10000000 #define B43_HTTAB_16BIT 0x20000000 #define B43_HTTAB_32BIT 0x30000000 #define B43_HTTAB8(table, offset) (((table) << 10) | (offset) | B43_HTTAB_8BIT) #define B43_HTTAB16(table, offset) (((table) << 10) | (offset) | B43_HTTAB_16BIT) #define B43_HTTAB32(table, offset) (((table) << 10) | (offset) | B43_HTTAB_32BIT) u32 b43_httab_read(struct b43_wldev *dev, u32 offset); void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data); void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value); void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...); void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *_data); void b43_phy_ht_tables_init(struct b43_wldev *dev); #define B43_HTTAB_1A_C0_LATE_SIZE 128 extern const u32 b43_httab_0x1a_0xc0_late[]; #endif /* B43_TABLES_PHY_HT_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_phy_ht.c0000644000175000017500000010031212026211315024037 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n HT-PHY data tables Copyright (c) 2011 Rafał Miłecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "tables_phy_ht.h" #include "phy_common.h" #include "phy_ht.h" static const u16 b43_httab_0x12[] = { 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, }; static const u16 b43_httab_0x27[] = { 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, }; static const u16 b43_httab_0x26[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u32 b43_httab_0x25[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_httab_0x2f[] = { 0x00035700, 0x0002cc9a, 0x00026666, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x00035700, 0x0002cc9a, 0x00026666, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, }; static const u16 b43_httab_0x1a[] = { 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, 0x000b, 0x0007, 0x0002, 0x00fd, }; static const u16 b43_httab_0x1b[] = { 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, 0x000b, 0x0007, 0x0002, 0x00fd, }; static const u16 b43_httab_0x1c[] = { 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, 0x000b, 0x0007, 0x0002, 0x00fd, }; static const u32 b43_httab_0x1a_0xc0[] = { 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, }; static const u32 b43_httab_0x1a_0x140[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_httab_0x1b_0x140[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_httab_0x1c_0x140[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u16 b43_httab_0x1a_0x1c0[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 b43_httab_0x1b_0x1c0[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 b43_httab_0x1c_0x1c0[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 b43_httab_0x1a_0x240[] = { 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, }; static const u16 b43_httab_0x1b_0x240[] = { 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, }; static const u16 b43_httab_0x1c_0x240[] = { 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, }; static const u32 b43_httab_0x1f[] = { 0x00000000, 0x00000000, 0x00016023, 0x00006028, 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, }; static const u32 b43_httab_0x21[] = { 0x00000000, 0x00000000, 0x00016023, 0x00006028, 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, }; static const u32 b43_httab_0x23[] = { 0x00000000, 0x00000000, 0x00016023, 0x00006028, 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, }; static const u32 b43_httab_0x20[] = { 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, }; static const u32 b43_httab_0x22[] = { 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, }; static const u32 b43_httab_0x24[] = { 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, }; /* Some late-init table */ const u32 b43_httab_0x1a_0xc0_late[] = { 0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d, 0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b, 0x1091003b, 0x1089003a, 0x1081003a, 0x10790039, 0x10710039, 0x1069003a, 0x1061003b, 0x1059003d, 0x1051003f, 0x10490042, 0x1049003e, 0x1049003b, 0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b, 0x10390038, 0x10390035, 0x1031003a, 0x10310036, 0x10310033, 0x1029003a, 0x10290037, 0x10290034, 0x10290031, 0x10210039, 0x10210036, 0x10210033, 0x10210030, 0x1019003c, 0x10190039, 0x10190036, 0x10190033, 0x10190030, 0x1019002d, 0x1019002b, 0x10190028, 0x1011003a, 0x10110036, 0x10110033, 0x10110030, 0x1011002e, 0x1011002b, 0x10110029, 0x10110027, 0x10110024, 0x10110022, 0x10110020, 0x1011001f, 0x1011001d, 0x1009003a, 0x10090037, 0x10090034, 0x10090031, 0x1009002e, 0x1009002c, 0x10090029, 0x10090027, 0x10090025, 0x10090023, 0x10090021, 0x1009001f, 0x1009001d, 0x1009001b, 0x1009001a, 0x10090018, 0x10090017, 0x10090016, 0x10090015, 0x10090013, 0x10090012, 0x10090011, 0x10090010, 0x1009000f, 0x1009000f, 0x1009000e, 0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b, 0x1009000a, 0x1009000a, 0x10090009, 0x10090009, 0x10090008, 0x10090008, 0x10090007, 0x10090007, 0x10090007, 0x10090006, 0x10090006, 0x10090005, 0x10090005, 0x10090005, 0x10090005, 0x10090004, 0x10090004, 0x10090004, 0x10090004, 0x10090003, 0x10090003, 0x10090003, 0x10090003, 0x10090003, 0x10090003, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090002, 0x10090001, 0x10090001, 0x10090001, 0x10090001, 0x10090001, 0x10090001, }; /************************************************** * R/W ops. **************************************************/ u32 b43_httab_read(struct b43_wldev *dev, u32 offset) { u32 type, value; type = offset & B43_HTTAB_TYPEMASK; offset &= ~B43_HTTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); switch (type) { case B43_HTTAB_8BIT: b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF; break; case B43_HTTAB_16BIT: b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); break; case B43_HTTAB_32BIT: b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI); value <<= 16; value |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); break; default: B43_WARN_ON(1); value = 0; } return value; } void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data) { u32 type; u8 *data = _data; unsigned int i; type = offset & B43_HTTAB_TYPEMASK; offset &= ~B43_HTTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { switch (type) { case B43_HTTAB_8BIT: *data = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF; data++; break; case B43_HTTAB_16BIT: *((u16 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); data += 2; break; case B43_HTTAB_32BIT: *((u32 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI); *((u32 *)data) <<= 16; *((u32 *)data) |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); data += 4; break; default: B43_WARN_ON(1); } } } void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value) { u32 type; type = offset & B43_HTTAB_TYPEMASK; offset &= 0xFFFF; switch (type) { case B43_HTTAB_8BIT: B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); break; case B43_HTTAB_16BIT: B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); break; case B43_HTTAB_32BIT: b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value & 0xFFFF); break; default: B43_WARN_ON(1); } return; } void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...) { va_list args; u32 type, value; unsigned int i; type = offset & B43_HTTAB_TYPEMASK; offset &= 0xFFFF; va_start(args, num); switch (type) { case B43_HTTAB_8BIT: b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); for (i = 0; i < num; i++) { value = va_arg(args, int); B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); } break; case B43_HTTAB_16BIT: b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); for (i = 0; i < num; i++) { value = va_arg(args, int); B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); } break; case B43_HTTAB_32BIT: b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); for (i = 0; i < num; i++) { value = va_arg(args, int); b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value & 0xFFFF); } break; default: B43_WARN_ON(1); } va_end(args); return; } void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *_data) { u32 type, value; const u8 *data = _data; unsigned int i; type = offset & B43_HTTAB_TYPEMASK; offset &= ~B43_HTTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { switch (type) { case B43_HTTAB_8BIT: value = *data; data++; B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); break; case B43_HTTAB_16BIT: value = *((u16 *)data); data += 2; B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); break; case B43_HTTAB_32BIT: value = *((u32 *)data); data += 4; b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16); b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value & 0xFFFF); break; default: B43_WARN_ON(1); } } } /************************************************** * Tables ops. **************************************************/ #define httab_upload(dev, offset, data) do { \ b43_httab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ } while (0) void b43_phy_ht_tables_init(struct b43_wldev *dev) { BUILD_BUG_ON(ARRAY_SIZE(b43_httab_0x1a_0xc0_late) != B43_HTTAB_1A_C0_LATE_SIZE); httab_upload(dev, B43_HTTAB16(0x12, 0), b43_httab_0x12); httab_upload(dev, B43_HTTAB16(0x27, 0), b43_httab_0x27); httab_upload(dev, B43_HTTAB16(0x26, 0), b43_httab_0x26); httab_upload(dev, B43_HTTAB32(0x25, 0), b43_httab_0x25); httab_upload(dev, B43_HTTAB32(0x2f, 0), b43_httab_0x2f); httab_upload(dev, B43_HTTAB16(0x1a, 0), b43_httab_0x1a); httab_upload(dev, B43_HTTAB16(0x1b, 0), b43_httab_0x1b); httab_upload(dev, B43_HTTAB16(0x1c, 0), b43_httab_0x1c); httab_upload(dev, B43_HTTAB32(0x1a, 0x0c0), b43_httab_0x1a_0xc0); httab_upload(dev, B43_HTTAB32(0x1a, 0x140), b43_httab_0x1a_0x140); httab_upload(dev, B43_HTTAB32(0x1b, 0x140), b43_httab_0x1b_0x140); httab_upload(dev, B43_HTTAB32(0x1c, 0x140), b43_httab_0x1c_0x140); httab_upload(dev, B43_HTTAB16(0x1a, 0x1c0), b43_httab_0x1a_0x1c0); httab_upload(dev, B43_HTTAB16(0x1b, 0x1c0), b43_httab_0x1b_0x1c0); httab_upload(dev, B43_HTTAB16(0x1c, 0x1c0), b43_httab_0x1c_0x1c0); httab_upload(dev, B43_HTTAB16(0x1a, 0x240), b43_httab_0x1a_0x240); httab_upload(dev, B43_HTTAB16(0x1b, 0x240), b43_httab_0x1b_0x240); httab_upload(dev, B43_HTTAB16(0x1c, 0x240), b43_httab_0x1c_0x240); httab_upload(dev, B43_HTTAB32(0x1f, 0), b43_httab_0x1f); httab_upload(dev, B43_HTTAB32(0x21, 0), b43_httab_0x21); httab_upload(dev, B43_HTTAB32(0x23, 0), b43_httab_0x23); httab_upload(dev, B43_HTTAB32(0x20, 0), b43_httab_0x20); httab_upload(dev, B43_HTTAB32(0x22, 0), b43_httab_0x22); httab_upload(dev, B43_HTTAB32(0x24, 0), b43_httab_0x24); } compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_nphy.h0000644000175000017500000002102212026211315023527 0ustar mcgrofmcgrof#ifndef B43_TABLES_NPHY_H_ #define B43_TABLES_NPHY_H_ #include struct b43_phy_n_sfo_cfg { u16 phy_bw1a; u16 phy_bw2; u16 phy_bw3; u16 phy_bw4; u16 phy_bw5; u16 phy_bw6; }; struct b43_wldev; struct nphy_txiqcal_ladder { u8 percent; u8 g_env; }; struct nphy_rf_control_override_rev2 { u8 addr0; u8 addr1; u16 bmask; u8 shift; }; struct nphy_rf_control_override_rev3 { u16 val_mask; u8 val_shift; u8 en_addr0; u8 val_addr0; u8 en_addr1; u8 val_addr1; }; struct nphy_rf_control_override_rev7 { u16 field; u16 val_addr_core0; u16 val_addr_core1; u16 val_mask; u8 val_shift; }; struct nphy_gain_ctl_workaround_entry { s8 lna1_gain[4]; s8 lna2_gain[4]; u8 gain_db[10]; u8 gain_bits[10]; u16 init_gain; u16 rfseq_init[4]; u16 cliphi_gain; u16 clipmd_gain; u16 cliplo_gain; u16 crsmin; u16 crsminl; u16 crsminu; u16 nbclip; u16 wlclip; }; /* Get entry with workaround values for gain ctl. Does not return NULL. */ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( struct b43_wldev *dev, bool ghz5, bool ext_lna); /* The N-PHY tables. */ #define B43_NTAB_TYPEMASK 0xF0000000 #define B43_NTAB_8BIT 0x10000000 #define B43_NTAB_16BIT 0x20000000 #define B43_NTAB_32BIT 0x30000000 #define B43_NTAB8(table, offset) (((table) << 10) | (offset) | B43_NTAB_8BIT) #define B43_NTAB16(table, offset) (((table) << 10) | (offset) | B43_NTAB_16BIT) #define B43_NTAB32(table, offset) (((table) << 10) | (offset) | B43_NTAB_32BIT) /* Static N-PHY tables */ #define B43_NTAB_FRAMESTRUCT B43_NTAB32(0x0A, 0x000) /* Frame Struct Table */ #define B43_NTAB_FRAMESTRUCT_SIZE 832 #define B43_NTAB_FRAMELT B43_NTAB8 (0x18, 0x000) /* Frame Lookup Table */ #define B43_NTAB_FRAMELT_SIZE 32 #define B43_NTAB_TMAP B43_NTAB32(0x0C, 0x000) /* T Map Table */ #define B43_NTAB_TMAP_SIZE 448 #define B43_NTAB_TDTRN B43_NTAB32(0x0E, 0x000) /* TDTRN Table */ #define B43_NTAB_TDTRN_SIZE 704 #define B43_NTAB_INTLEVEL B43_NTAB32(0x0D, 0x000) /* Int Level Table */ #define B43_NTAB_INTLEVEL_SIZE 7 #define B43_NTAB_PILOT B43_NTAB16(0x0B, 0x000) /* Pilot Table */ #define B43_NTAB_PILOT_SIZE 88 #define B43_NTAB_PILOTLT B43_NTAB32(0x14, 0x000) /* Pilot Lookup Table */ #define B43_NTAB_PILOTLT_SIZE 6 #define B43_NTAB_TDI20A0 B43_NTAB32(0x13, 0x080) /* TDI Table 20 Antenna 0 */ #define B43_NTAB_TDI20A0_SIZE 55 #define B43_NTAB_TDI20A1 B43_NTAB32(0x13, 0x100) /* TDI Table 20 Antenna 1 */ #define B43_NTAB_TDI20A1_SIZE 55 #define B43_NTAB_TDI40A0 B43_NTAB32(0x13, 0x280) /* TDI Table 40 Antenna 0 */ #define B43_NTAB_TDI40A0_SIZE 110 #define B43_NTAB_TDI40A1 B43_NTAB32(0x13, 0x300) /* TDI Table 40 Antenna 1 */ #define B43_NTAB_TDI40A1_SIZE 110 #define B43_NTAB_BDI B43_NTAB16(0x15, 0x000) /* BDI Table */ #define B43_NTAB_BDI_SIZE 6 #define B43_NTAB_CHANEST B43_NTAB32(0x16, 0x000) /* Channel Estimate Table */ #define B43_NTAB_CHANEST_SIZE 96 #define B43_NTAB_MCS B43_NTAB8 (0x12, 0x000) /* MCS Table */ #define B43_NTAB_MCS_SIZE 128 /* Volatile N-PHY tables */ #define B43_NTAB_NOISEVAR10 B43_NTAB32(0x10, 0x000) /* Noise Var Table 10 */ #define B43_NTAB_NOISEVAR10_SIZE 256 #define B43_NTAB_NOISEVAR11 B43_NTAB32(0x10, 0x080) /* Noise Var Table 11 */ #define B43_NTAB_NOISEVAR11_SIZE 256 #define B43_NTAB_C0_ESTPLT B43_NTAB8 (0x1A, 0x000) /* Estimate Power Lookup Table Core 0 */ #define B43_NTAB_C0_ESTPLT_SIZE 64 #define B43_NTAB_C1_ESTPLT B43_NTAB8 (0x1B, 0x000) /* Estimate Power Lookup Table Core 1 */ #define B43_NTAB_C1_ESTPLT_SIZE 64 #define B43_NTAB_C0_ADJPLT B43_NTAB8 (0x1A, 0x040) /* Adjust Power Lookup Table Core 0 */ #define B43_NTAB_C0_ADJPLT_SIZE 128 #define B43_NTAB_C1_ADJPLT B43_NTAB8 (0x1B, 0x040) /* Adjust Power Lookup Table Core 1 */ #define B43_NTAB_C1_ADJPLT_SIZE 128 #define B43_NTAB_C0_GAINCTL B43_NTAB32(0x1A, 0x0C0) /* Gain Control Lookup Table Core 0 */ #define B43_NTAB_C0_GAINCTL_SIZE 128 #define B43_NTAB_C1_GAINCTL B43_NTAB32(0x1B, 0x0C0) /* Gain Control Lookup Table Core 1 */ #define B43_NTAB_C1_GAINCTL_SIZE 128 #define B43_NTAB_C0_IQLT B43_NTAB32(0x1A, 0x140) /* IQ Lookup Table Core 0 */ #define B43_NTAB_C0_IQLT_SIZE 128 #define B43_NTAB_C1_IQLT B43_NTAB32(0x1B, 0x140) /* IQ Lookup Table Core 1 */ #define B43_NTAB_C1_IQLT_SIZE 128 #define B43_NTAB_C0_LOFEEDTH B43_NTAB16(0x1A, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 0 */ #define B43_NTAB_C0_LOFEEDTH_SIZE 128 #define B43_NTAB_C1_LOFEEDTH B43_NTAB16(0x1B, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 1 */ #define B43_NTAB_C1_LOFEEDTH_SIZE 128 /* Volatile N-PHY tables, PHY revision >= 3 */ #define B43_NTAB_ANT_SW_CTL_R3 B43_NTAB16( 9, 0) /* antenna software control */ /* Static N-PHY tables, PHY revision >= 3 */ #define B43_NTAB_FRAMESTRUCT_R3 B43_NTAB32(10, 0) /* frame struct */ #define B43_NTAB_PILOT_R3 B43_NTAB16(11, 0) /* pilot */ #define B43_NTAB_TMAP_R3 B43_NTAB32(12, 0) /* TM AP */ #define B43_NTAB_INTLEVEL_R3 B43_NTAB32(13, 0) /* INT LV */ #define B43_NTAB_TDTRN_R3 B43_NTAB32(14, 0) /* TD TRN */ #define B43_NTAB_NOISEVAR0_R3 B43_NTAB32(16, 0) /* noise variance 0 */ #define B43_NTAB_NOISEVAR1_R3 B43_NTAB32(16, 128) /* noise variance 1 */ #define B43_NTAB_MCS_R3 B43_NTAB16(18, 0) /* MCS */ #define B43_NTAB_TDI20A0_R3 B43_NTAB32(19, 128) /* TDI 20/0 */ #define B43_NTAB_TDI20A1_R3 B43_NTAB32(19, 256) /* TDI 20/1 */ #define B43_NTAB_TDI40A0_R3 B43_NTAB32(19, 640) /* TDI 40/0 */ #define B43_NTAB_TDI40A1_R3 B43_NTAB32(19, 768) /* TDI 40/1 */ #define B43_NTAB_PILOTLT_R3 B43_NTAB32(20, 0) /* PLT lookup */ #define B43_NTAB_CHANEST_R3 B43_NTAB32(22, 0) /* channel estimate */ #define B43_NTAB_FRAMELT_R3 B43_NTAB8(24, 0) /* frame lookup */ #define B43_NTAB_C0_ESTPLT_R3 B43_NTAB8(26, 0) /* estimated power lookup 0 */ #define B43_NTAB_C1_ESTPLT_R3 B43_NTAB8(27, 0) /* estimated power lookup 1 */ #define B43_NTAB_C0_ADJPLT_R3 B43_NTAB8(26, 64) /* adjusted power lookup 0 */ #define B43_NTAB_C1_ADJPLT_R3 B43_NTAB8(27, 64) /* adjusted power lookup 1 */ #define B43_NTAB_C0_GAINCTL_R3 B43_NTAB32(26, 192) /* gain control lookup 0 */ #define B43_NTAB_C1_GAINCTL_R3 B43_NTAB32(27, 192) /* gain control lookup 1 */ #define B43_NTAB_C0_IQLT_R3 B43_NTAB32(26, 320) /* I/Q lookup 0 */ #define B43_NTAB_C1_IQLT_R3 B43_NTAB32(27, 320) /* I/Q lookup 1 */ #define B43_NTAB_C0_LOFEEDTH_R3 B43_NTAB16(26, 448) /* Local Oscillator Feed Through lookup 0 */ #define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */ #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_20_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3 11 #define B43_NTAB_TX_IQLO_CAL_STARTCOEFS 9 #define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3 12 #define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL 10 #define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL 10 #define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3 12 u32 b43_ntab_read(struct b43_wldev *dev, u32 offset); void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data); void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value); void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *_data); void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev); void b43_nphy_rev3plus_tables_init(struct b43_wldev *dev); const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev); extern const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[]; extern const u16 tbl_iqcal_gainparams[2][9][8]; extern const struct nphy_txiqcal_ladder ladder_lo[]; extern const struct nphy_txiqcal_ladder ladder_iq[]; extern const u16 loscale[]; extern const u16 tbl_tx_iqlo_cal_loft_ladder_40[]; extern const u16 tbl_tx_iqlo_cal_loft_ladder_20[]; extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[]; extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[]; extern const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[]; extern const u16 tbl_tx_iqlo_cal_startcoefs[]; extern const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[]; extern const u16 tbl_tx_iqlo_cal_cmds_recal[]; extern const u16 tbl_tx_iqlo_cal_cmds_fullcal[]; extern const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[]; extern const s16 tbl_tx_filter_coef_rev4[7][15]; extern const struct nphy_rf_control_override_rev2 tbl_rf_control_override_rev2[]; extern const struct nphy_rf_control_override_rev3 tbl_rf_control_override_rev3[]; const struct nphy_rf_control_override_rev7 *b43_nphy_get_rf_ctl_over_rev7( struct b43_wldev *dev, u16 field, u8 override); #endif /* B43_TABLES_NPHY_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_nphy.c0000644000175000017500000042144712026211315023541 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n PHY data tables Copyright (c) 2008 Michael Buesch Copyright (c) 2010 Rafał Miłecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "tables_nphy.h" #include "phy_common.h" #include "phy_n.h" static const u8 b43_ntab_adjustpower0[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u8 b43_ntab_adjustpower1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u16 b43_ntab_bdi[] = { 0x0070, 0x0126, 0x012C, 0x0246, 0x048D, 0x04D2, }; static const u32 b43_ntab_channelest[] = { 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, }; static const u8 b43_ntab_estimatepowerlt0[] = { 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; static const u8 b43_ntab_estimatepowerlt1[] = { 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; static const u8 b43_ntab_framelookup[] = { 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, 0x0A, 0x0C, 0x1C, 0x1C, 0x0B, 0x0D, 0x1E, 0x1E, 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1A, 0x1A, 0x0E, 0x10, 0x20, 0x28, 0x0F, 0x11, 0x22, 0x2A, }; static const u32 b43_ntab_framestruct[] = { 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, 0x09804506, 0x00100030, 0x09804507, 0x00100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004A0C, 0x00100004, 0x01000A0D, 0x00100024, 0x0980450E, 0x00100034, 0x0980450F, 0x00100034, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000A04, 0x00100000, 0x11008A05, 0x00100020, 0x1980C506, 0x00100030, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x01800504, 0x00100030, 0x11808505, 0x00100030, 0x29814507, 0x01100030, 0x00000A04, 0x00100000, 0x11008A05, 0x00100020, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, 0x1980C50E, 0x00100038, 0x2181050E, 0x00100038, 0x2181050E, 0x00100038, 0x0180050C, 0x00100038, 0x1180850D, 0x00100038, 0x2981450F, 0x01100038, 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, 0x2181050E, 0x00100038, 0x2181050E, 0x00100038, 0x2981450F, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, 0x1980C506, 0x00100030, 0x1980C506, 0x00100030, 0x11808504, 0x00100030, 0x3981CA05, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x10008A04, 0x00100000, 0x3981CA05, 0x00100030, 0x1980C506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004A0C, 0x00100008, 0x01000A0D, 0x00100028, 0x1980C50E, 0x00100038, 0x1980C50E, 0x00100038, 0x1180850C, 0x00100038, 0x3981CA0D, 0x00100038, 0x2981450F, 0x01100038, 0x00000000, 0x00000000, 0x10008A0C, 0x00100008, 0x3981CA0D, 0x00100038, 0x1980C50E, 0x00100038, 0x2981450F, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x02001405, 0x00100040, 0x0B004A06, 0x01900060, 0x13008A06, 0x01900060, 0x13008A06, 0x01900060, 0x43020A04, 0x00100060, 0x1B00CA05, 0x00100060, 0x23010A07, 0x01500060, 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, 0x13008A06, 0x01900060, 0x13008A06, 0x01900060, 0x23010A07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140C, 0x00100010, 0x0200140D, 0x00100050, 0x0B004A0E, 0x01900070, 0x13008A0E, 0x01900070, 0x13008A0E, 0x01900070, 0x43020A0C, 0x00100070, 0x1B00CA0D, 0x00100070, 0x23010A0F, 0x01500070, 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, 0x13008A0E, 0x01900070, 0x13008A0E, 0x01900070, 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x0B004A06, 0x01900060, 0x0B004A06, 0x01900060, 0x5B02CA04, 0x00100060, 0x3B01D405, 0x00100060, 0x23010A07, 0x01500060, 0x00000000, 0x00000000, 0x5802D404, 0x00100000, 0x3B01D405, 0x00100060, 0x0B004A06, 0x01900060, 0x23010A07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5002940C, 0x00100010, 0x3201940D, 0x00100050, 0x0B004A0E, 0x01900070, 0x0B004A0E, 0x01900070, 0x5B02CA0C, 0x00100070, 0x3B01D40D, 0x00100070, 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, 0x5802D40C, 0x00100010, 0x3B01D40D, 0x00100070, 0x0B004A0E, 0x01900070, 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x000F4800, 0x62031405, 0x00100040, 0x53028A06, 0x01900060, 0x53028A07, 0x01900060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140C, 0x000F4808, 0x6203140D, 0x00100048, 0x53028A0E, 0x01900068, 0x53028A0F, 0x01900068, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000A0C, 0x00100004, 0x11008A0D, 0x00100024, 0x1980C50E, 0x00100034, 0x2181050E, 0x00100034, 0x2181050E, 0x00100034, 0x0180050C, 0x00100038, 0x1180850D, 0x00100038, 0x1181850D, 0x00100038, 0x2981450F, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, 0x2181050E, 0x00100038, 0x2181050E, 0x00100038, 0x1181850D, 0x00100038, 0x2981450F, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, 0x0180C506, 0x00100030, 0x0180C506, 0x00100030, 0x2180C50C, 0x00100030, 0x49820A0D, 0x0016A130, 0x41824A0D, 0x0016A130, 0x2981450F, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2000CA0C, 0x00100000, 0x49820A0D, 0x0016A130, 0x1980C50E, 0x00100030, 0x41824A0D, 0x0016A130, 0x2981450F, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140C, 0x00100008, 0x0200140D, 0x00100048, 0x0B004A0E, 0x01900068, 0x13008A0E, 0x01900068, 0x13008A0E, 0x01900068, 0x43020A0C, 0x00100070, 0x1B00CA0D, 0x00100070, 0x1B014A0D, 0x00100070, 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, 0x13008A0E, 0x01900070, 0x13008A0E, 0x01900070, 0x1B014A0D, 0x00100070, 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x03004A06, 0x01900060, 0x03004A06, 0x01900060, 0x6B030A0C, 0x00100060, 0x4B02140D, 0x0016A160, 0x4302540D, 0x0016A160, 0x23010A0F, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6B03140C, 0x00100060, 0x4B02140D, 0x0016A160, 0x0B004A0E, 0x01900060, 0x4302540D, 0x0016A160, 0x23010A0F, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, 0x53028A06, 0x01900060, 0x5B02CA06, 0x01900060, 0x5B02CA06, 0x01900060, 0x43020A04, 0x00100060, 0x1B00CA05, 0x00100060, 0x53028A07, 0x0190C060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, 0x53028A0E, 0x01900070, 0x5B02CA0E, 0x01900070, 0x5B02CA0E, 0x01900070, 0x43020A0C, 0x00100070, 0x1B00CA0D, 0x00100070, 0x53028A0F, 0x0190C070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, 0x5B02CA06, 0x01900060, 0x5B02CA06, 0x01900060, 0x53028A07, 0x0190C060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, 0x5B02CA0E, 0x01900070, 0x5B02CA0E, 0x01900070, 0x53028A0F, 0x0190C070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_ntab_gainctl0[] = { 0x03CC2B44, 0x03CC2B42, 0x03CC2B40, 0x03CC2B3E, 0x03CC2B3D, 0x03CC2B3B, 0x03C82B44, 0x03C82B42, 0x03C82B40, 0x03C82B3E, 0x03C82B3D, 0x03C82B3B, 0x03C82B39, 0x03C82B38, 0x03C82B36, 0x03C82B34, 0x03C42B44, 0x03C42B42, 0x03C42B40, 0x03C42B3E, 0x03C42B3D, 0x03C42B3B, 0x03C42B39, 0x03C42B38, 0x03C42B36, 0x03C42B34, 0x03C42B33, 0x03C42B32, 0x03C42B30, 0x03C42B2F, 0x03C42B2D, 0x03C02B44, 0x03C02B42, 0x03C02B40, 0x03C02B3E, 0x03C02B3D, 0x03C02B3B, 0x03C02B39, 0x03C02B38, 0x03C02B36, 0x03C02B34, 0x03B02B44, 0x03B02B42, 0x03B02B40, 0x03B02B3E, 0x03B02B3D, 0x03B02B3B, 0x03B02B39, 0x03B02B38, 0x03B02B36, 0x03B02B34, 0x03B02B33, 0x03B02B32, 0x03B02B30, 0x03B02B2F, 0x03B02B2D, 0x03A02B44, 0x03A02B42, 0x03A02B40, 0x03A02B3E, 0x03A02B3D, 0x03A02B3B, 0x03A02B39, 0x03A02B38, 0x03A02B36, 0x03A02B34, 0x03902B44, 0x03902B42, 0x03902B40, 0x03902B3E, 0x03902B3D, 0x03902B3B, 0x03902B39, 0x03902B38, 0x03902B36, 0x03902B34, 0x03902B33, 0x03902B32, 0x03902B30, 0x03802B44, 0x03802B42, 0x03802B40, 0x03802B3E, 0x03802B3D, 0x03802B3B, 0x03802B39, 0x03802B38, 0x03802B36, 0x03802B34, 0x03802B33, 0x03802B32, 0x03802B30, 0x03802B2F, 0x03802B2D, 0x03802B2C, 0x03802B2B, 0x03802B2A, 0x03802B29, 0x03802B27, 0x03802B26, 0x03802B25, 0x03802B24, 0x03802B23, 0x03802B22, 0x03802B21, 0x03802B20, 0x03802B1F, 0x03802B1E, 0x03802B1E, 0x03802B1D, 0x03802B1C, 0x03802B1B, 0x03802B1A, 0x03802B1A, 0x03802B19, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x00002B00, }; static const u32 b43_ntab_gainctl1[] = { 0x03CC2B44, 0x03CC2B42, 0x03CC2B40, 0x03CC2B3E, 0x03CC2B3D, 0x03CC2B3B, 0x03C82B44, 0x03C82B42, 0x03C82B40, 0x03C82B3E, 0x03C82B3D, 0x03C82B3B, 0x03C82B39, 0x03C82B38, 0x03C82B36, 0x03C82B34, 0x03C42B44, 0x03C42B42, 0x03C42B40, 0x03C42B3E, 0x03C42B3D, 0x03C42B3B, 0x03C42B39, 0x03C42B38, 0x03C42B36, 0x03C42B34, 0x03C42B33, 0x03C42B32, 0x03C42B30, 0x03C42B2F, 0x03C42B2D, 0x03C02B44, 0x03C02B42, 0x03C02B40, 0x03C02B3E, 0x03C02B3D, 0x03C02B3B, 0x03C02B39, 0x03C02B38, 0x03C02B36, 0x03C02B34, 0x03B02B44, 0x03B02B42, 0x03B02B40, 0x03B02B3E, 0x03B02B3D, 0x03B02B3B, 0x03B02B39, 0x03B02B38, 0x03B02B36, 0x03B02B34, 0x03B02B33, 0x03B02B32, 0x03B02B30, 0x03B02B2F, 0x03B02B2D, 0x03A02B44, 0x03A02B42, 0x03A02B40, 0x03A02B3E, 0x03A02B3D, 0x03A02B3B, 0x03A02B39, 0x03A02B38, 0x03A02B36, 0x03A02B34, 0x03902B44, 0x03902B42, 0x03902B40, 0x03902B3E, 0x03902B3D, 0x03902B3B, 0x03902B39, 0x03902B38, 0x03902B36, 0x03902B34, 0x03902B33, 0x03902B32, 0x03902B30, 0x03802B44, 0x03802B42, 0x03802B40, 0x03802B3E, 0x03802B3D, 0x03802B3B, 0x03802B39, 0x03802B38, 0x03802B36, 0x03802B34, 0x03802B33, 0x03802B32, 0x03802B30, 0x03802B2F, 0x03802B2D, 0x03802B2C, 0x03802B2B, 0x03802B2A, 0x03802B29, 0x03802B27, 0x03802B26, 0x03802B25, 0x03802B24, 0x03802B23, 0x03802B22, 0x03802B21, 0x03802B20, 0x03802B1F, 0x03802B1E, 0x03802B1E, 0x03802B1D, 0x03802B1C, 0x03802B1B, 0x03802B1A, 0x03802B1A, 0x03802B19, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, 0x00002B00, }; static const u32 b43_ntab_intlevel[] = { 0x00802070, 0x0671188D, 0x0A60192C, 0x0A300E46, 0x00C1188D, 0x080024D2, 0x00000070, }; static const u32 b43_ntab_iqlt0[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, }; static const u32 b43_ntab_iqlt1[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, }; static const u16 b43_ntab_loftlt0[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, }; static const u16 b43_ntab_loftlt1[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, }; static const u8 b43_ntab_mcs[] = { 0x00, 0x08, 0x0A, 0x10, 0x12, 0x19, 0x1A, 0x1C, 0x40, 0x48, 0x4A, 0x50, 0x52, 0x59, 0x5A, 0x5C, 0x80, 0x88, 0x8A, 0x90, 0x92, 0x99, 0x9A, 0x9C, 0xC0, 0xC8, 0xCA, 0xD0, 0xD2, 0xD9, 0xDA, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x12, 0x14, 0x18, 0x19, 0x1A, 0x1C, 0x20, 0x21, 0x22, 0x24, 0x40, 0x41, 0x42, 0x44, 0x48, 0x49, 0x4A, 0x4C, 0x50, 0x51, 0x52, 0x54, 0x58, 0x59, 0x5A, 0x5C, 0x60, 0x61, 0x62, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u32 b43_ntab_noisevar10[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, }; static const u32 b43_ntab_noisevar11[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, }; static const u16 b43_ntab_pilot[] = { 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0xFF0A, 0xFF82, 0xFFA0, 0xFF28, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF82, 0xFFA0, 0xFF28, 0xFF0A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xF83F, 0xFA1F, 0xFA97, 0xFAB5, 0xF2BD, 0xF0BF, 0xFFFF, 0xFFFF, 0xF017, 0xF815, 0xF215, 0xF095, 0xF035, 0xF01D, 0xFFFF, 0xFFFF, 0xFF08, 0xFF02, 0xFF80, 0xFF20, 0xFF08, 0xFF02, 0xFF80, 0xFF20, 0xF01F, 0xF817, 0xFA15, 0xF295, 0xF0B5, 0xF03D, 0xFFFF, 0xFFFF, 0xF82A, 0xFA0A, 0xFA82, 0xFAA0, 0xF2A8, 0xF0AA, 0xFFFF, 0xFFFF, 0xF002, 0xF800, 0xF200, 0xF080, 0xF020, 0xF008, 0xFFFF, 0xFFFF, 0xF00A, 0xF802, 0xFA00, 0xF280, 0xF0A0, 0xF028, 0xFFFF, 0xFFFF, }; static const u32 b43_ntab_pilotlt[] = { 0x76540123, 0x62407351, 0x76543201, 0x76540213, 0x76540123, 0x76430521, }; static const u32 b43_ntab_tdi20a0[] = { 0x00091226, 0x000A1429, 0x000B56AD, 0x000C58B0, 0x000D5AB3, 0x000E9CB6, 0x000F9EBA, 0x0000C13D, 0x00020301, 0x00030504, 0x00040708, 0x0005090B, 0x00064B8E, 0x00095291, 0x000A5494, 0x000B9718, 0x000C9927, 0x000D9B2A, 0x000EDD2E, 0x000FDF31, 0x000101B4, 0x000243B7, 0x000345BB, 0x000447BE, 0x00058982, 0x00068C05, 0x00099309, 0x000A950C, 0x000BD78F, 0x000CD992, 0x000DDB96, 0x000F1D99, 0x00005FA8, 0x0001422C, 0x0002842F, 0x00038632, 0x00048835, 0x0005CA38, 0x0006CCBC, 0x0009D3BF, 0x000B1603, 0x000C1806, 0x000D1A0A, 0x000E1C0D, 0x000F5E10, 0x00008093, 0x00018297, 0x0002C49A, 0x0003C680, 0x0004C880, 0x00060B00, 0x00070D00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_ntab_tdi20a1[] = { 0x00014B26, 0x00028D29, 0x000393AD, 0x00049630, 0x0005D833, 0x0006DA36, 0x00099C3A, 0x000A9E3D, 0x000BC081, 0x000CC284, 0x000DC488, 0x000F068B, 0x0000488E, 0x00018B91, 0x0002D214, 0x0003D418, 0x0004D6A7, 0x000618AA, 0x00071AAE, 0x0009DCB1, 0x000B1EB4, 0x000C0137, 0x000D033B, 0x000E053E, 0x000F4702, 0x00008905, 0x00020C09, 0x0003128C, 0x0004148F, 0x00051712, 0x00065916, 0x00091B19, 0x000A1D28, 0x000B5F2C, 0x000C41AF, 0x000D43B2, 0x000E85B5, 0x000F87B8, 0x0000C9BC, 0x00024CBF, 0x00035303, 0x00045506, 0x0005978A, 0x0006998D, 0x00095B90, 0x000A5D93, 0x000B9F97, 0x000C821A, 0x000D8400, 0x000EC600, 0x000FC800, 0x00010A00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_ntab_tdi40a0[] = { 0x0011A346, 0x00136CCF, 0x0014F5D9, 0x001641E2, 0x0017CB6B, 0x00195475, 0x001B2383, 0x001CAD0C, 0x001E7616, 0x0000821F, 0x00020BA8, 0x0003D4B2, 0x00056447, 0x00072DD0, 0x0008B6DA, 0x000A02E3, 0x000B8C6C, 0x000D15F6, 0x0011E484, 0x0013AE0D, 0x00153717, 0x00168320, 0x00180CA9, 0x00199633, 0x001B6548, 0x001CEED1, 0x001EB7DB, 0x0000C3E4, 0x00024D6D, 0x000416F7, 0x0005A585, 0x00076F0F, 0x0008F818, 0x000A4421, 0x000BCDAB, 0x000D9734, 0x00122649, 0x0013EFD2, 0x001578DC, 0x0016C4E5, 0x00184E6E, 0x001A17F8, 0x001BA686, 0x001D3010, 0x001EF999, 0x00010522, 0x00028EAC, 0x00045835, 0x0005E74A, 0x0007B0D3, 0x00093A5D, 0x000A85E6, 0x000C0F6F, 0x000DD8F9, 0x00126787, 0x00143111, 0x0015BA9A, 0x00170623, 0x00188FAD, 0x001A5936, 0x001BE84B, 0x001DB1D4, 0x001F3B5E, 0x000146E7, 0x00031070, 0x000499FA, 0x00062888, 0x0007F212, 0x00097B9B, 0x000AC7A4, 0x000C50AE, 0x000E1A37, 0x0012A94C, 0x001472D5, 0x0015FC5F, 0x00174868, 0x0018D171, 0x001A9AFB, 0x001C2989, 0x001DF313, 0x001F7C9C, 0x000188A5, 0x000351AF, 0x0004DB38, 0x0006AA4D, 0x000833D7, 0x0009BD60, 0x000B0969, 0x000C9273, 0x000E5BFC, 0x00132A8A, 0x0014B414, 0x00163D9D, 0x001789A6, 0x001912B0, 0x001ADC39, 0x001C6BCE, 0x001E34D8, 0x001FBE61, 0x0001CA6A, 0x00039374, 0x00051CFD, 0x0006EC0B, 0x00087515, 0x0009FE9E, 0x000B4AA7, 0x000CD3B1, 0x000E9D3A, 0x00000000, 0x00000000, }; static const u32 b43_ntab_tdi40a1[] = { 0x001EDB36, 0x000129CA, 0x0002B353, 0x00047CDD, 0x0005C8E6, 0x000791EF, 0x00091BF9, 0x000AAA07, 0x000C3391, 0x000DFD1A, 0x00120923, 0x0013D22D, 0x00155C37, 0x0016EACB, 0x00187454, 0x001A3DDE, 0x001B89E7, 0x001D12F0, 0x001F1CFA, 0x00016B88, 0x00033492, 0x0004BE1B, 0x00060A24, 0x0007D32E, 0x00095D38, 0x000AEC4C, 0x000C7555, 0x000E3EDF, 0x00124AE8, 0x001413F1, 0x0015A37B, 0x00172C89, 0x0018B593, 0x001A419C, 0x001BCB25, 0x001D942F, 0x001F63B9, 0x0001AD4D, 0x00037657, 0x0004C260, 0x00068BE9, 0x000814F3, 0x0009A47C, 0x000B2D8A, 0x000CB694, 0x000E429D, 0x00128C26, 0x001455B0, 0x0015E4BA, 0x00176E4E, 0x0018F758, 0x001A8361, 0x001C0CEA, 0x001DD674, 0x001FA57D, 0x0001EE8B, 0x0003B795, 0x0005039E, 0x0006CD27, 0x000856B1, 0x0009E5C6, 0x000B6F4F, 0x000CF859, 0x000E8462, 0x00130DEB, 0x00149775, 0x00162603, 0x0017AF8C, 0x00193896, 0x001AC49F, 0x001C4E28, 0x001E17B2, 0x0000A6C7, 0x00023050, 0x0003F9DA, 0x00054563, 0x00070EEC, 0x00089876, 0x000A2704, 0x000BB08D, 0x000D3A17, 0x001185A0, 0x00134F29, 0x0014D8B3, 0x001667C8, 0x0017F151, 0x00197ADB, 0x001B0664, 0x001C8FED, 0x001E5977, 0x0000E805, 0x0002718F, 0x00043B18, 0x000586A1, 0x0007502B, 0x0008D9B4, 0x000A68C9, 0x000BF252, 0x000DBBDC, 0x0011C7E5, 0x001390EE, 0x00151A78, 0x0016A906, 0x00183290, 0x0019BC19, 0x001B4822, 0x001CD12C, 0x001E9AB5, 0x00000000, 0x00000000, }; static const u32 b43_ntab_tdtrn[] = { 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, 0x0C380000, 0x12F6FE52, 0xFE36F592, 0xEE680050, 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, 0x0C380000, 0x12F6FE52, 0xFE36F592, 0xEE680050, 0x05E305E3, 0x004DEF0C, 0xF5F3FE47, 0xFE611246, 0x00000BC7, 0xFE611246, 0xF5F3FE47, 0x004DEF0C, 0x05E305E3, 0xEF0C004D, 0xFE47F5F3, 0x1246FE61, 0x0BC70000, 0x1246FE61, 0xFE47F5F3, 0xEF0C004D, 0x05E305E3, 0x004DEF0C, 0xF5F3FE47, 0xFE611246, 0x00000BC7, 0xFE611246, 0xF5F3FE47, 0x004DEF0C, 0x05E305E3, 0xEF0C004D, 0xFE47F5F3, 0x1246FE61, 0x0BC70000, 0x1246FE61, 0xFE47F5F3, 0xEF0C004D, 0xFA58FA58, 0xF895043B, 0xFF4C09C0, 0xFBC6FFA8, 0xFB84F384, 0x0798F6F9, 0x05760122, 0x058409F6, 0x0B500000, 0x05B7F542, 0x08860432, 0x06DDFEE7, 0xFB84F384, 0xF9D90664, 0xF7E8025C, 0x00FFF7BD, 0x05A805A8, 0xF7BD00FF, 0x025CF7E8, 0x0664F9D9, 0xF384FB84, 0xFEE706DD, 0x04320886, 0xF54205B7, 0x00000B50, 0x09F60584, 0x01220576, 0xF6F90798, 0xF384FB84, 0xFFA8FBC6, 0x09C0FF4C, 0x043BF895, 0x02D402D4, 0x07DE0270, 0xFC96079C, 0xF90AFE94, 0xFE00FF2C, 0x02D4065D, 0x092A0096, 0x0014FBB8, 0xFD2CFD2C, 0x076AFB3C, 0x0096F752, 0xF991FD87, 0xFB2C0200, 0xFEB8F960, 0x08E0FC96, 0x049802A8, 0xFD2CFD2C, 0x02A80498, 0xFC9608E0, 0xF960FEB8, 0x0200FB2C, 0xFD87F991, 0xF7520096, 0xFB3C076A, 0xFD2CFD2C, 0xFBB80014, 0x0096092A, 0x065D02D4, 0xFF2CFE00, 0xFE94F90A, 0x079CFC96, 0x027007DE, 0x02D402D4, 0x027007DE, 0x079CFC96, 0xFE94F90A, 0xFF2CFE00, 0x065D02D4, 0x0096092A, 0xFBB80014, 0xFD2CFD2C, 0xFB3C076A, 0xF7520096, 0xFD87F991, 0x0200FB2C, 0xF960FEB8, 0xFC9608E0, 0x02A80498, 0xFD2CFD2C, 0x049802A8, 0x08E0FC96, 0xFEB8F960, 0xFB2C0200, 0xF991FD87, 0x0096F752, 0x076AFB3C, 0xFD2CFD2C, 0x0014FBB8, 0x092A0096, 0x02D4065D, 0xFE00FF2C, 0xF90AFE94, 0xFC96079C, 0x07DE0270, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x062A0000, 0xFEFA0759, 0x08B80908, 0xF396FC2D, 0xF9D6045C, 0xFC4EF608, 0xF748F596, 0x07B207BF, 0x062A062A, 0xF84EF841, 0xF748F596, 0x03B209F8, 0xF9D6045C, 0x0C6A03D3, 0x08B80908, 0x0106F8A7, 0x062A0000, 0xFEFAF8A7, 0x08B8F6F8, 0xF39603D3, 0xF9D6FBA4, 0xFC4E09F8, 0xF7480A6A, 0x07B2F841, 0x062AF9D6, 0xF84E07BF, 0xF7480A6A, 0x03B2F608, 0xF9D6FBA4, 0x0C6AFC2D, 0x08B8F6F8, 0x01060759, 0x062A0000, 0xFEFA0759, 0x08B80908, 0xF396FC2D, 0xF9D6045C, 0xFC4EF608, 0xF748F596, 0x07B207BF, 0x062A062A, 0xF84EF841, 0xF748F596, 0x03B209F8, 0xF9D6045C, 0x0C6A03D3, 0x08B80908, 0x0106F8A7, 0x062A0000, 0xFEFAF8A7, 0x08B8F6F8, 0xF39603D3, 0xF9D6FBA4, 0xFC4E09F8, 0xF7480A6A, 0x07B2F841, 0x062AF9D6, 0xF84E07BF, 0xF7480A6A, 0x03B2F608, 0xF9D6FBA4, 0x0C6AFC2D, 0x08B8F6F8, 0x01060759, 0x061C061C, 0xFF30009D, 0xFFB21141, 0xFD87FB54, 0xF65DFE59, 0x02EEF99E, 0x0166F03C, 0xFFF809B6, 0x000008A4, 0x000AF42B, 0x00EFF577, 0xFA840BF2, 0xFC02FF51, 0x08260F67, 0xFFF0036F, 0x0842F9C3, 0x00000000, 0x063DF7BE, 0xFC910010, 0xF099F7DA, 0x00AF03FE, 0xF40E057C, 0x0A89FF11, 0x0BD5FFF6, 0xF75C0000, 0xF64A0008, 0x0FC4FE9A, 0x0662FD12, 0x01A709A3, 0x04AC0279, 0xEEBF004E, 0xFF6300D0, 0xF9E4F9E4, 0x00D0FF63, 0x004EEEBF, 0x027904AC, 0x09A301A7, 0xFD120662, 0xFE9A0FC4, 0x0008F64A, 0x0000F75C, 0xFFF60BD5, 0xFF110A89, 0x057CF40E, 0x03FE00AF, 0xF7DAF099, 0x0010FC91, 0xF7BE063D, 0x00000000, 0xF9C30842, 0x036FFFF0, 0x0F670826, 0xFF51FC02, 0x0BF2FA84, 0xF57700EF, 0xF42B000A, 0x08A40000, 0x09B6FFF8, 0xF03C0166, 0xF99E02EE, 0xFE59F65D, 0xFB54FD87, 0x1141FFB2, 0x009DFF30, 0x05E30000, 0xFF060705, 0x085408A0, 0xF425FC59, 0xFA1D042A, 0xFC78F67A, 0xF7ACF60E, 0x075A0766, 0x05E305E3, 0xF8A6F89A, 0xF7ACF60E, 0x03880986, 0xFA1D042A, 0x0BDB03A7, 0x085408A0, 0x00FAF8FB, 0x05E30000, 0xFF06F8FB, 0x0854F760, 0xF42503A7, 0xFA1DFBD6, 0xFC780986, 0xF7AC09F2, 0x075AF89A, 0x05E3FA1D, 0xF8A60766, 0xF7AC09F2, 0x0388F67A, 0xFA1DFBD6, 0x0BDBFC59, 0x0854F760, 0x00FA0705, 0x05E30000, 0xFF060705, 0x085408A0, 0xF425FC59, 0xFA1D042A, 0xFC78F67A, 0xF7ACF60E, 0x075A0766, 0x05E305E3, 0xF8A6F89A, 0xF7ACF60E, 0x03880986, 0xFA1D042A, 0x0BDB03A7, 0x085408A0, 0x00FAF8FB, 0x05E30000, 0xFF06F8FB, 0x0854F760, 0xF42503A7, 0xFA1DFBD6, 0xFC780986, 0xF7AC09F2, 0x075AF89A, 0x05E3FA1D, 0xF8A60766, 0xF7AC09F2, 0x0388F67A, 0xFA1DFBD6, 0x0BDBFC59, 0x0854F760, 0x00FA0705, 0xFA58FA58, 0xF8F0FE00, 0x0448073D, 0xFDC9FE46, 0xF9910258, 0x089D0407, 0xFD5CF71A, 0x02AFFDE0, 0x083E0496, 0xFF5A0740, 0xFF7AFD97, 0x00FE01F1, 0x0009082E, 0xFA94FF75, 0xFECDF8EA, 0xFFB0F693, 0xFD2CFA58, 0x0433FF16, 0xFBA405DD, 0xFA610341, 0x06A606CB, 0x0039FD2D, 0x0677FA97, 0x01FA05E0, 0xF896003E, 0x075A068B, 0x012CFC3E, 0xFA23F98D, 0xFC7CFD43, 0xFF90FC0D, 0x01C10982, 0x00C601D6, 0xFD2CFD2C, 0x01D600C6, 0x098201C1, 0xFC0DFF90, 0xFD43FC7C, 0xF98DFA23, 0xFC3E012C, 0x068B075A, 0x003EF896, 0x05E001FA, 0xFA970677, 0xFD2D0039, 0x06CB06A6, 0x0341FA61, 0x05DDFBA4, 0xFF160433, 0xFA58FD2C, 0xF693FFB0, 0xF8EAFECD, 0xFF75FA94, 0x082E0009, 0x01F100FE, 0xFD97FF7A, 0x0740FF5A, 0x0496083E, 0xFDE002AF, 0xF71AFD5C, 0x0407089D, 0x0258F991, 0xFE46FDC9, 0x073D0448, 0xFE00F8F0, 0xFD2CFD2C, 0xFCE00500, 0xFC09FDDC, 0xFE680157, 0x04C70571, 0xFC3AFF21, 0xFCD70228, 0x056D0277, 0x0200FE00, 0x0022F927, 0xFE3C032B, 0xFC44FF3C, 0x03E9FBDB, 0x04570313, 0x04C9FF5C, 0x000D03B8, 0xFA580000, 0xFBE900D2, 0xF9D0FE0B, 0x0125FDF9, 0x042501BF, 0x0328FA2B, 0xFFA902F0, 0xFA250157, 0x0200FE00, 0x03740438, 0xFF0405FD, 0x030CFE52, 0x0037FB39, 0xFF6904C5, 0x04F8FD23, 0xFD31FC1B, 0xFD2CFD2C, 0xFC1BFD31, 0xFD2304F8, 0x04C5FF69, 0xFB390037, 0xFE52030C, 0x05FDFF04, 0x04380374, 0xFE000200, 0x0157FA25, 0x02F0FFA9, 0xFA2B0328, 0x01BF0425, 0xFDF90125, 0xFE0BF9D0, 0x00D2FBE9, 0x0000FA58, 0x03B8000D, 0xFF5C04C9, 0x03130457, 0xFBDB03E9, 0xFF3CFC44, 0x032BFE3C, 0xF9270022, 0xFE000200, 0x0277056D, 0x0228FCD7, 0xFF21FC3A, 0x057104C7, 0x0157FE68, 0xFDDCFC09, 0x0500FCE0, 0xFD2CFD2C, 0x0500FCE0, 0xFDDCFC09, 0x0157FE68, 0x057104C7, 0xFF21FC3A, 0x0228FCD7, 0x0277056D, 0xFE000200, 0xF9270022, 0x032BFE3C, 0xFF3CFC44, 0xFBDB03E9, 0x03130457, 0xFF5C04C9, 0x03B8000D, 0x0000FA58, 0x00D2FBE9, 0xFE0BF9D0, 0xFDF90125, 0x01BF0425, 0xFA2B0328, 0x02F0FFA9, 0x0157FA25, 0xFE000200, 0x04380374, 0x05FDFF04, 0xFE52030C, 0xFB390037, 0x04C5FF69, 0xFD2304F8, 0xFC1BFD31, 0xFD2CFD2C, 0xFD31FC1B, 0x04F8FD23, 0xFF6904C5, 0x0037FB39, 0x030CFE52, 0xFF0405FD, 0x03740438, 0x0200FE00, 0xFA250157, 0xFFA902F0, 0x0328FA2B, 0x042501BF, 0x0125FDF9, 0xF9D0FE0B, 0xFBE900D2, 0xFA580000, 0x000D03B8, 0x04C9FF5C, 0x04570313, 0x03E9FBDB, 0xFC44FF3C, 0xFE3C032B, 0x0022F927, 0x0200FE00, 0x056D0277, 0xFCD70228, 0xFC3AFF21, 0x04C70571, 0xFE680157, 0xFC09FDDC, 0xFCE00500, 0x05A80000, 0xFF1006BE, 0x0800084A, 0xF49CFC7E, 0xFA580400, 0xFC9CF6DA, 0xF800F672, 0x0710071C, 0x05A805A8, 0xF8F0F8E4, 0xF800F672, 0x03640926, 0xFA580400, 0x0B640382, 0x0800084A, 0x00F0F942, 0x05A80000, 0xFF10F942, 0x0800F7B6, 0xF49C0382, 0xFA58FC00, 0xFC9C0926, 0xF800098E, 0x0710F8E4, 0x05A8FA58, 0xF8F0071C, 0xF800098E, 0x0364F6DA, 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, 0x05A80000, 0xFF1006BE, 0x0800084A, 0xF49CFC7E, 0xFA580400, 0xFC9CF6DA, 0xF800F672, 0x0710071C, 0x05A805A8, 0xF8F0F8E4, 0xF800F672, 0x03640926, 0xFA580400, 0x0B640382, 0x0800084A, 0x00F0F942, 0x05A80000, 0xFF10F942, 0x0800F7B6, 0xF49C0382, 0xFA58FC00, 0xFC9C0926, 0xF800098E, 0x0710F8E4, 0x05A8FA58, 0xF8F0071C, 0xF800098E, 0x0364F6DA, 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, }; static const u32 b43_ntab_tmap[] = { 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, 0x11000000, 0x1111F111, 0x11111111, 0x111111F1, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x000AA888, 0x88880000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, 0xA2222220, 0x22222222, 0x22C22222, 0x00000222, 0x22000000, 0x2222A222, 0x22222222, 0x222222A2, 0xF1111110, 0x11111111, 0x11F11111, 0x00011111, 0x11110000, 0x1111F111, 0x11111111, 0x111111F1, 0xA8AA88A0, 0xA88888A8, 0xA8A8A88A, 0x00088AAA, 0xAAAA0000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, 0xAAA8AAA0, 0x8AAA8AAA, 0xAA8A8A8A, 0x000AAA88, 0x8AAA0000, 0xAAA8A888, 0x8AA88A8A, 0x8A88A888, 0x08080A00, 0x0A08080A, 0x080A0A08, 0x00080808, 0x080A0000, 0x080A0808, 0x080A0808, 0x0A0A0A08, 0xA0A0A0A0, 0x80A0A080, 0x8080A0A0, 0x00008080, 0x80A00000, 0x80A080A0, 0xA080A0A0, 0x8080A0A0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x99999000, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, 0x9B99BB90, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00AAA888, 0x22000000, 0x2222B222, 0x22222222, 0x222222B2, 0xB2222220, 0x22222222, 0x22D22222, 0x00000222, 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, 0x33000000, 0x3333B333, 0x33333333, 0x333333B3, 0xB3333330, 0x33333333, 0x33D33333, 0x00000333, 0x22000000, 0x2222A222, 0x22222222, 0x222222A2, 0xA2222220, 0x22222222, 0x22C22222, 0x00000222, 0x99B99B00, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, 0x9B99BB99, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x08AAA888, 0x22222200, 0x2222F222, 0x22222222, 0x222222F2, 0x22222222, 0x22222222, 0x22F22222, 0x00000222, 0x11000000, 0x1111F111, 0x11111111, 0x11111111, 0xF1111111, 0x11111111, 0x11F11111, 0x01111111, 0xBB9BB900, 0xB9B9BB99, 0xB99BBBBB, 0xBBBB9B9B, 0xB9BB99BB, 0xB99999B9, 0xB9B9B99B, 0x00000BBB, 0xAA000000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, 0xA8AA88AA, 0xA88888A8, 0xA8A8A88A, 0x0A888AAA, 0xAA000000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, 0xA8AA88A0, 0xA88888A8, 0xA8A8A88A, 0x00000AAA, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0xBBBBBB00, 0x999BBBBB, 0x9BB99B9B, 0xB9B9B9BB, 0xB9B99BBB, 0xB9B9B9BB, 0xB9BB9B99, 0x00000999, 0x8A000000, 0xAA88A888, 0xA88888AA, 0xA88A8A88, 0xA88AA88A, 0x88A8AAAA, 0xA8AA8AAA, 0x0888A88A, 0x0B0B0B00, 0x090B0B0B, 0x0B090B0B, 0x0909090B, 0x09090B0B, 0x09090B0B, 0x09090B09, 0x00000909, 0x0A000000, 0x0A080808, 0x080A080A, 0x080A0A08, 0x080A080A, 0x0808080A, 0x0A0A0A08, 0x0808080A, 0xB0B0B000, 0x9090B0B0, 0x90B09090, 0xB0B0B090, 0xB0B090B0, 0x90B0B0B0, 0xB0B09090, 0x00000090, 0x80000000, 0xA080A080, 0xA08080A0, 0xA0808080, 0xA080A080, 0x80A0A0A0, 0xA0A080A0, 0x00A0A0A0, 0x22000000, 0x2222F222, 0x22222222, 0x222222F2, 0xF2222220, 0x22222222, 0x22F22222, 0x00000222, 0x11000000, 0x1111F111, 0x11111111, 0x111111F1, 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, 0x33000000, 0x3333F333, 0x33333333, 0x333333F3, 0xF3333330, 0x33333333, 0x33F33333, 0x00000333, 0x22000000, 0x2222F222, 0x22222222, 0x222222F2, 0xF2222220, 0x22222222, 0x22F22222, 0x00000222, 0x99000000, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, 0x9B99BB90, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x88888000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00AAA888, 0x88A88A00, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x08AAA888, 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; /* static tables, PHY revision >= 3 */ static const u32 b43_ntab_framestruct_r3[] = { 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x09804506, 0x00100030, 0x09804507, 0x00100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a0c, 0x00100004, 0x01000a0d, 0x00100024, 0x0980450e, 0x00100034, 0x0980450f, 0x00100034, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, 0x1980c506, 0x00100030, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x01800504, 0x00100030, 0x11808505, 0x00100030, 0x29814507, 0x01100030, 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, 0x21810506, 0x00100030, 0x21810506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x1980c50e, 0x00100038, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x0180050c, 0x00100038, 0x1180850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x1980c506, 0x00100030, 0x1980c506, 0x00100030, 0x11808504, 0x00100030, 0x3981ca05, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x10008a04, 0x00100000, 0x3981ca05, 0x00100030, 0x1980c506, 0x00100030, 0x29814507, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a0c, 0x00100008, 0x01000a0d, 0x00100028, 0x1980c50e, 0x00100038, 0x1980c50e, 0x00100038, 0x1180850c, 0x00100038, 0x3981ca0d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x10008a0c, 0x00100008, 0x3981ca0d, 0x00100038, 0x1980c50e, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x02001405, 0x00100040, 0x0b004a06, 0x01900060, 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, 0x43020a04, 0x00100060, 0x1b00ca05, 0x00100060, 0x23010a07, 0x01500060, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x0200140d, 0x00100050, 0x0b004a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x23010a0f, 0x01500070, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x0b004a06, 0x01900060, 0x0b004a06, 0x01900060, 0x5b02ca04, 0x00100060, 0x3b01d405, 0x00100060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x5802d404, 0x00100000, 0x3b01d405, 0x00100060, 0x0b004a06, 0x01900060, 0x23010a07, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5002940c, 0x00100010, 0x3201940d, 0x00100050, 0x0b004a0e, 0x01900070, 0x0b004a0e, 0x01900070, 0x5b02ca0c, 0x00100070, 0x3b01d40d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x5802d40c, 0x00100010, 0x3b01d40d, 0x00100070, 0x0b004a0e, 0x01900070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x000f4800, 0x62031405, 0x00100040, 0x53028a06, 0x01900060, 0x53028a07, 0x01900060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x000f4808, 0x6203140d, 0x00100048, 0x53028a0e, 0x01900068, 0x53028a0f, 0x01900068, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100004, 0x11008a0d, 0x00100024, 0x1980c50e, 0x00100034, 0x2181050e, 0x00100034, 0x2181050e, 0x00100034, 0x0180050c, 0x00100038, 0x1180850d, 0x00100038, 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, 0x0180c506, 0x00100030, 0x0180c506, 0x00100030, 0x2180c50c, 0x00100030, 0x49820a0d, 0x0016a130, 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2000ca0c, 0x00100000, 0x49820a0d, 0x0016a130, 0x1980c50e, 0x00100030, 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100008, 0x0200140d, 0x00100048, 0x0b004a0e, 0x01900068, 0x13008a0e, 0x01900068, 0x13008a0e, 0x01900068, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50029404, 0x00100000, 0x32019405, 0x00100040, 0x03004a06, 0x01900060, 0x03004a06, 0x01900060, 0x6b030a0c, 0x00100060, 0x4b02140d, 0x0016a160, 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b03140c, 0x00100060, 0x4b02140d, 0x0016a160, 0x0b004a0e, 0x01900060, 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x53028a06, 0x01900060, 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, 0x43020a04, 0x00100060, 0x1b00ca05, 0x00100060, 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x53028a0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x43020a0c, 0x00100070, 0x1b00ca0d, 0x00100070, 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u16 b43_ntab_pilot_r3[] = { 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0xff0a, 0xff82, 0xffa0, 0xff28, 0xffff, 0xffff, 0xffff, 0xffff, 0xff82, 0xffa0, 0xff28, 0xff0a, 0xffff, 0xffff, 0xffff, 0xffff, 0xf83f, 0xfa1f, 0xfa97, 0xfab5, 0xf2bd, 0xf0bf, 0xffff, 0xffff, 0xf017, 0xf815, 0xf215, 0xf095, 0xf035, 0xf01d, 0xffff, 0xffff, 0xff08, 0xff02, 0xff80, 0xff20, 0xff08, 0xff02, 0xff80, 0xff20, 0xf01f, 0xf817, 0xfa15, 0xf295, 0xf0b5, 0xf03d, 0xffff, 0xffff, 0xf82a, 0xfa0a, 0xfa82, 0xfaa0, 0xf2a8, 0xf0aa, 0xffff, 0xffff, 0xf002, 0xf800, 0xf200, 0xf080, 0xf020, 0xf008, 0xffff, 0xffff, 0xf00a, 0xf802, 0xfa00, 0xf280, 0xf0a0, 0xf028, 0xffff, 0xffff, }; static const u32 b43_ntab_tmap_r3[] = { 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, 0x22222222, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x11111111, 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_ntab_intlevel_r3[] = { 0x00802070, 0x0671188d, 0x0a60192c, 0x0a300e46, 0x00c1188d, 0x080024d2, 0x00000070, }; static const u32 b43_ntab_tdtrn_r3[] = { 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, 0xfa58fa58, 0xf895043b, 0xff4c09c0, 0xfbc6ffa8, 0xfb84f384, 0x0798f6f9, 0x05760122, 0x058409f6, 0x0b500000, 0x05b7f542, 0x08860432, 0x06ddfee7, 0xfb84f384, 0xf9d90664, 0xf7e8025c, 0x00fff7bd, 0x05a805a8, 0xf7bd00ff, 0x025cf7e8, 0x0664f9d9, 0xf384fb84, 0xfee706dd, 0x04320886, 0xf54205b7, 0x00000b50, 0x09f60584, 0x01220576, 0xf6f90798, 0xf384fb84, 0xffa8fbc6, 0x09c0ff4c, 0x043bf895, 0x02d402d4, 0x07de0270, 0xfc96079c, 0xf90afe94, 0xfe00ff2c, 0x02d4065d, 0x092a0096, 0x0014fbb8, 0xfd2cfd2c, 0x076afb3c, 0x0096f752, 0xf991fd87, 0xfb2c0200, 0xfeb8f960, 0x08e0fc96, 0x049802a8, 0xfd2cfd2c, 0x02a80498, 0xfc9608e0, 0xf960feb8, 0x0200fb2c, 0xfd87f991, 0xf7520096, 0xfb3c076a, 0xfd2cfd2c, 0xfbb80014, 0x0096092a, 0x065d02d4, 0xff2cfe00, 0xfe94f90a, 0x079cfc96, 0x027007de, 0x02d402d4, 0x027007de, 0x079cfc96, 0xfe94f90a, 0xff2cfe00, 0x065d02d4, 0x0096092a, 0xfbb80014, 0xfd2cfd2c, 0xfb3c076a, 0xf7520096, 0xfd87f991, 0x0200fb2c, 0xf960feb8, 0xfc9608e0, 0x02a80498, 0xfd2cfd2c, 0x049802a8, 0x08e0fc96, 0xfeb8f960, 0xfb2c0200, 0xf991fd87, 0x0096f752, 0x076afb3c, 0xfd2cfd2c, 0x0014fbb8, 0x092a0096, 0x02d4065d, 0xfe00ff2c, 0xf90afe94, 0xfc96079c, 0x07de0270, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, 0x061c061c, 0xff30009d, 0xffb21141, 0xfd87fb54, 0xf65dfe59, 0x02eef99e, 0x0166f03c, 0xfff809b6, 0x000008a4, 0x000af42b, 0x00eff577, 0xfa840bf2, 0xfc02ff51, 0x08260f67, 0xfff0036f, 0x0842f9c3, 0x00000000, 0x063df7be, 0xfc910010, 0xf099f7da, 0x00af03fe, 0xf40e057c, 0x0a89ff11, 0x0bd5fff6, 0xf75c0000, 0xf64a0008, 0x0fc4fe9a, 0x0662fd12, 0x01a709a3, 0x04ac0279, 0xeebf004e, 0xff6300d0, 0xf9e4f9e4, 0x00d0ff63, 0x004eeebf, 0x027904ac, 0x09a301a7, 0xfd120662, 0xfe9a0fc4, 0x0008f64a, 0x0000f75c, 0xfff60bd5, 0xff110a89, 0x057cf40e, 0x03fe00af, 0xf7daf099, 0x0010fc91, 0xf7be063d, 0x00000000, 0xf9c30842, 0x036ffff0, 0x0f670826, 0xff51fc02, 0x0bf2fa84, 0xf57700ef, 0xf42b000a, 0x08a40000, 0x09b6fff8, 0xf03c0166, 0xf99e02ee, 0xfe59f65d, 0xfb54fd87, 0x1141ffb2, 0x009dff30, 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, 0xfa58fa58, 0xf8f0fe00, 0x0448073d, 0xfdc9fe46, 0xf9910258, 0x089d0407, 0xfd5cf71a, 0x02affde0, 0x083e0496, 0xff5a0740, 0xff7afd97, 0x00fe01f1, 0x0009082e, 0xfa94ff75, 0xfecdf8ea, 0xffb0f693, 0xfd2cfa58, 0x0433ff16, 0xfba405dd, 0xfa610341, 0x06a606cb, 0x0039fd2d, 0x0677fa97, 0x01fa05e0, 0xf896003e, 0x075a068b, 0x012cfc3e, 0xfa23f98d, 0xfc7cfd43, 0xff90fc0d, 0x01c10982, 0x00c601d6, 0xfd2cfd2c, 0x01d600c6, 0x098201c1, 0xfc0dff90, 0xfd43fc7c, 0xf98dfa23, 0xfc3e012c, 0x068b075a, 0x003ef896, 0x05e001fa, 0xfa970677, 0xfd2d0039, 0x06cb06a6, 0x0341fa61, 0x05ddfba4, 0xff160433, 0xfa58fd2c, 0xf693ffb0, 0xf8eafecd, 0xff75fa94, 0x082e0009, 0x01f100fe, 0xfd97ff7a, 0x0740ff5a, 0x0496083e, 0xfde002af, 0xf71afd5c, 0x0407089d, 0x0258f991, 0xfe46fdc9, 0x073d0448, 0xfe00f8f0, 0xfd2cfd2c, 0xfce00500, 0xfc09fddc, 0xfe680157, 0x04c70571, 0xfc3aff21, 0xfcd70228, 0x056d0277, 0x0200fe00, 0x0022f927, 0xfe3c032b, 0xfc44ff3c, 0x03e9fbdb, 0x04570313, 0x04c9ff5c, 0x000d03b8, 0xfa580000, 0xfbe900d2, 0xf9d0fe0b, 0x0125fdf9, 0x042501bf, 0x0328fa2b, 0xffa902f0, 0xfa250157, 0x0200fe00, 0x03740438, 0xff0405fd, 0x030cfe52, 0x0037fb39, 0xff6904c5, 0x04f8fd23, 0xfd31fc1b, 0xfd2cfd2c, 0xfc1bfd31, 0xfd2304f8, 0x04c5ff69, 0xfb390037, 0xfe52030c, 0x05fdff04, 0x04380374, 0xfe000200, 0x0157fa25, 0x02f0ffa9, 0xfa2b0328, 0x01bf0425, 0xfdf90125, 0xfe0bf9d0, 0x00d2fbe9, 0x0000fa58, 0x03b8000d, 0xff5c04c9, 0x03130457, 0xfbdb03e9, 0xff3cfc44, 0x032bfe3c, 0xf9270022, 0xfe000200, 0x0277056d, 0x0228fcd7, 0xff21fc3a, 0x057104c7, 0x0157fe68, 0xfddcfc09, 0x0500fce0, 0xfd2cfd2c, 0x0500fce0, 0xfddcfc09, 0x0157fe68, 0x057104c7, 0xff21fc3a, 0x0228fcd7, 0x0277056d, 0xfe000200, 0xf9270022, 0x032bfe3c, 0xff3cfc44, 0xfbdb03e9, 0x03130457, 0xff5c04c9, 0x03b8000d, 0x0000fa58, 0x00d2fbe9, 0xfe0bf9d0, 0xfdf90125, 0x01bf0425, 0xfa2b0328, 0x02f0ffa9, 0x0157fa25, 0xfe000200, 0x04380374, 0x05fdff04, 0xfe52030c, 0xfb390037, 0x04c5ff69, 0xfd2304f8, 0xfc1bfd31, 0xfd2cfd2c, 0xfd31fc1b, 0x04f8fd23, 0xff6904c5, 0x0037fb39, 0x030cfe52, 0xff0405fd, 0x03740438, 0x0200fe00, 0xfa250157, 0xffa902f0, 0x0328fa2b, 0x042501bf, 0x0125fdf9, 0xf9d0fe0b, 0xfbe900d2, 0xfa580000, 0x000d03b8, 0x04c9ff5c, 0x04570313, 0x03e9fbdb, 0xfc44ff3c, 0xfe3c032b, 0x0022f927, 0x0200fe00, 0x056d0277, 0xfcd70228, 0xfc3aff21, 0x04c70571, 0xfe680157, 0xfc09fddc, 0xfce00500, 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, }; static const u32 b43_ntab_noisevar0_r3[] = { 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, }; static const u32 b43_ntab_noisevar1_r3[] = { 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, }; static const u16 b43_ntab_mcs_r3[] = { 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, }; static const u32 b43_ntab_tdi20a0_r3[] = { 0x00091226, 0x000a1429, 0x000b56ad, 0x000c58b0, 0x000d5ab3, 0x000e9cb6, 0x000f9eba, 0x0000c13d, 0x00020301, 0x00030504, 0x00040708, 0x0005090b, 0x00064b8e, 0x00095291, 0x000a5494, 0x000b9718, 0x000c9927, 0x000d9b2a, 0x000edd2e, 0x000fdf31, 0x000101b4, 0x000243b7, 0x000345bb, 0x000447be, 0x00058982, 0x00068c05, 0x00099309, 0x000a950c, 0x000bd78f, 0x000cd992, 0x000ddb96, 0x000f1d99, 0x00005fa8, 0x0001422c, 0x0002842f, 0x00038632, 0x00048835, 0x0005ca38, 0x0006ccbc, 0x0009d3bf, 0x000b1603, 0x000c1806, 0x000d1a0a, 0x000e1c0d, 0x000f5e10, 0x00008093, 0x00018297, 0x0002c49a, 0x0003c680, 0x0004c880, 0x00060b00, 0x00070d00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_ntab_tdi20a1_r3[] = { 0x00014b26, 0x00028d29, 0x000393ad, 0x00049630, 0x0005d833, 0x0006da36, 0x00099c3a, 0x000a9e3d, 0x000bc081, 0x000cc284, 0x000dc488, 0x000f068b, 0x0000488e, 0x00018b91, 0x0002d214, 0x0003d418, 0x0004d6a7, 0x000618aa, 0x00071aae, 0x0009dcb1, 0x000b1eb4, 0x000c0137, 0x000d033b, 0x000e053e, 0x000f4702, 0x00008905, 0x00020c09, 0x0003128c, 0x0004148f, 0x00051712, 0x00065916, 0x00091b19, 0x000a1d28, 0x000b5f2c, 0x000c41af, 0x000d43b2, 0x000e85b5, 0x000f87b8, 0x0000c9bc, 0x00024cbf, 0x00035303, 0x00045506, 0x0005978a, 0x0006998d, 0x00095b90, 0x000a5d93, 0x000b9f97, 0x000c821a, 0x000d8400, 0x000ec600, 0x000fc800, 0x00010a00, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_ntab_tdi40a0_r3[] = { 0x0011a346, 0x00136ccf, 0x0014f5d9, 0x001641e2, 0x0017cb6b, 0x00195475, 0x001b2383, 0x001cad0c, 0x001e7616, 0x0000821f, 0x00020ba8, 0x0003d4b2, 0x00056447, 0x00072dd0, 0x0008b6da, 0x000a02e3, 0x000b8c6c, 0x000d15f6, 0x0011e484, 0x0013ae0d, 0x00153717, 0x00168320, 0x00180ca9, 0x00199633, 0x001b6548, 0x001ceed1, 0x001eb7db, 0x0000c3e4, 0x00024d6d, 0x000416f7, 0x0005a585, 0x00076f0f, 0x0008f818, 0x000a4421, 0x000bcdab, 0x000d9734, 0x00122649, 0x0013efd2, 0x001578dc, 0x0016c4e5, 0x00184e6e, 0x001a17f8, 0x001ba686, 0x001d3010, 0x001ef999, 0x00010522, 0x00028eac, 0x00045835, 0x0005e74a, 0x0007b0d3, 0x00093a5d, 0x000a85e6, 0x000c0f6f, 0x000dd8f9, 0x00126787, 0x00143111, 0x0015ba9a, 0x00170623, 0x00188fad, 0x001a5936, 0x001be84b, 0x001db1d4, 0x001f3b5e, 0x000146e7, 0x00031070, 0x000499fa, 0x00062888, 0x0007f212, 0x00097b9b, 0x000ac7a4, 0x000c50ae, 0x000e1a37, 0x0012a94c, 0x001472d5, 0x0015fc5f, 0x00174868, 0x0018d171, 0x001a9afb, 0x001c2989, 0x001df313, 0x001f7c9c, 0x000188a5, 0x000351af, 0x0004db38, 0x0006aa4d, 0x000833d7, 0x0009bd60, 0x000b0969, 0x000c9273, 0x000e5bfc, 0x00132a8a, 0x0014b414, 0x00163d9d, 0x001789a6, 0x001912b0, 0x001adc39, 0x001c6bce, 0x001e34d8, 0x001fbe61, 0x0001ca6a, 0x00039374, 0x00051cfd, 0x0006ec0b, 0x00087515, 0x0009fe9e, 0x000b4aa7, 0x000cd3b1, 0x000e9d3a, 0x00000000, 0x00000000, }; static const u32 b43_ntab_tdi40a1_r3[] = { 0x001edb36, 0x000129ca, 0x0002b353, 0x00047cdd, 0x0005c8e6, 0x000791ef, 0x00091bf9, 0x000aaa07, 0x000c3391, 0x000dfd1a, 0x00120923, 0x0013d22d, 0x00155c37, 0x0016eacb, 0x00187454, 0x001a3dde, 0x001b89e7, 0x001d12f0, 0x001f1cfa, 0x00016b88, 0x00033492, 0x0004be1b, 0x00060a24, 0x0007d32e, 0x00095d38, 0x000aec4c, 0x000c7555, 0x000e3edf, 0x00124ae8, 0x001413f1, 0x0015a37b, 0x00172c89, 0x0018b593, 0x001a419c, 0x001bcb25, 0x001d942f, 0x001f63b9, 0x0001ad4d, 0x00037657, 0x0004c260, 0x00068be9, 0x000814f3, 0x0009a47c, 0x000b2d8a, 0x000cb694, 0x000e429d, 0x00128c26, 0x001455b0, 0x0015e4ba, 0x00176e4e, 0x0018f758, 0x001a8361, 0x001c0cea, 0x001dd674, 0x001fa57d, 0x0001ee8b, 0x0003b795, 0x0005039e, 0x0006cd27, 0x000856b1, 0x0009e5c6, 0x000b6f4f, 0x000cf859, 0x000e8462, 0x00130deb, 0x00149775, 0x00162603, 0x0017af8c, 0x00193896, 0x001ac49f, 0x001c4e28, 0x001e17b2, 0x0000a6c7, 0x00023050, 0x0003f9da, 0x00054563, 0x00070eec, 0x00089876, 0x000a2704, 0x000bb08d, 0x000d3a17, 0x001185a0, 0x00134f29, 0x0014d8b3, 0x001667c8, 0x0017f151, 0x00197adb, 0x001b0664, 0x001c8fed, 0x001e5977, 0x0000e805, 0x0002718f, 0x00043b18, 0x000586a1, 0x0007502b, 0x0008d9b4, 0x000a68c9, 0x000bf252, 0x000dbbdc, 0x0011c7e5, 0x001390ee, 0x00151a78, 0x0016a906, 0x00183290, 0x0019bc19, 0x001b4822, 0x001cd12c, 0x001e9ab5, 0x00000000, 0x00000000, }; static const u32 b43_ntab_pilotlt_r3[] = { 0x76540213, 0x62407351, 0x76543210, 0x76540213, 0x76540213, 0x76430521, }; static const u32 b43_ntab_channelest_r3[] = { 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, }; static const u8 b43_ntab_framelookup_r3[] = { 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, 0x0a, 0x0c, 0x1c, 0x1c, 0x0b, 0x0d, 0x1e, 0x1e, 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1a, 0x1a, 0x0e, 0x10, 0x20, 0x28, 0x0f, 0x11, 0x22, 0x2a, }; static const u8 b43_ntab_estimatepowerlt0_r3[] = { 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, }; static const u8 b43_ntab_estimatepowerlt1_r3[] = { 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, }; static const u8 b43_ntab_adjustpower0_r3[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u8 b43_ntab_adjustpower1_r3[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u32 b43_ntab_gainctl0_r3[] = { 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, }; static const u32 b43_ntab_gainctl1_r3[] = { 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, }; static const u32 b43_ntab_iqlt0_r3[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u32 b43_ntab_iqlt1_r3[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const u16 b43_ntab_loftlt0_r3[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 b43_ntab_loftlt1_r3[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* volatile tables, PHY revision >= 3 */ /* indexed by antswctl2g */ static const u16 b43_ntab_antswctl2g_r3[4][32] = { { 0x0082, 0x0082, 0x0211, 0x0222, 0x0328, 0x0000, 0x0000, 0x0000, 0x0144, 0x0000, 0x0000, 0x0000, 0x0188, 0x0000, 0x0000, 0x0000, 0x0082, 0x0082, 0x0211, 0x0222, 0x0328, 0x0000, 0x0000, 0x0000, 0x0144, 0x0000, 0x0000, 0x0000, 0x0188, 0x0000, 0x0000, 0x0000, }, { 0x0022, 0x0022, 0x0011, 0x0022, 0x0022, 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, 0x0000, 0x0022, 0x0022, 0x0011, 0x0022, 0x0022, 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, 0x0000, }, { 0x0088, 0x0088, 0x0044, 0x0088, 0x0088, 0x0000, 0x0000, 0x0000, 0x0044, 0x0000, 0x0000, 0x0000, 0x0088, 0x0000, 0x0000, 0x0000, 0x0088, 0x0088, 0x0044, 0x0088, 0x0088, 0x0000, 0x0000, 0x0000, 0x0044, 0x0000, 0x0000, 0x0000, 0x0088, 0x0000, 0x0000, 0x0000, }, { 0x0022, 0x0022, 0x0011, 0x0022, 0x0000, 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, 0x03cc, 0x0022, 0x0022, 0x0011, 0x0022, 0x0000, 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, 0x03cc, } }; /* TX gain tables */ static const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, 0x03902942, 0x03902844, 0x03902842, 0x03902744, 0x03902742, 0x03902644, 0x03902642, 0x03902544, 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, 0x03802a42, 0x03802944, 0x03802942, 0x03802844, 0x03802842, 0x03802744, 0x03802742, 0x03802644, 0x03802642, 0x03802544, 0x03802542, 0x03802444, 0x03802442, 0x03802344, 0x03802342, 0x03802244, 0x03802242, 0x03802144, 0x03802142, 0x03802044, 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, 0x03801a42, 0x03801944, 0x03801942, 0x03801844, 0x03801842, 0x03801744, 0x03801742, 0x03801644, 0x03801642, 0x03801544, 0x03801542, 0x03801444, 0x03801442, 0x03801344, 0x03801342, 0x00002b00, }; static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = { 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, 0x19410044, 0x19410042, 0x19410040, 0x1941003e, 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, 0x18410044, 0x18410042, 0x18410040, 0x1841003e, 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, 0x17410044, 0x17410042, 0x17410040, 0x1741003e, 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, 0x16410044, 0x16410042, 0x16410040, 0x1641003e, 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, 0x15410044, 0x15410042, 0x15410040, 0x1541003e, 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, 0x14410044, 0x14410042, 0x14410040, 0x1441003e, 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, 0x13410044, 0x13410042, 0x13410040, 0x1341003e, 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, 0x12410044, 0x12410042, 0x12410040, 0x1241003e, 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, 0x11410044, 0x11410042, 0x11410040, 0x1141003e, 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, 0x10410044, 0x10410042, 0x10410040, 0x1041003e, 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, }; static const u32 b43_ntab_tx_gain_rev3_5ghz[] = { 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037, }; static const u32 b43_ntab_tx_gain_rev4_5ghz[] = { 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, }; static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = { 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, 0x09620039, 0x09620037, 0x09620035, 0x09620033, 0x08620044, 0x08620042, 0x08620040, 0x0862003e, 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, 0x07620043, 0x07620042, 0x07620040, 0x0762003f, 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, 0x06620039, 0x06620037, 0x06620035, 0x06620033, 0x05620046, 0x05620044, 0x05620042, 0x05620040, 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, 0x04620044, 0x04620042, 0x04620040, 0x0462003e, 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, 0x03620038, 0x03620037, 0x03620035, 0x03620033, 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, 0x02620046, 0x02620044, 0x02620043, 0x02620042, 0x0162004a, 0x01620048, 0x01620046, 0x01620044, 0x01620043, 0x01620042, 0x01620041, 0x01620040, 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, 0x0062003b, 0x00620039, 0x00620037, 0x00620035, }; static const u32 txpwrctrl_tx_gain_ipa[] = { 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025, }; static const u32 txpwrctrl_tx_gain_ipa_rev5[] = { 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025, }; static const u32 txpwrctrl_tx_gain_ipa_rev6[] = { 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, }; static const u32 txpwrctrl_tx_gain_ipa_5g[] = { 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f, }; const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = { -114, -108, -98, -91, -84, -78, -70, -62, -54, -46, -39, -31, -23, -15, -8, 0 }; const u16 tbl_iqcal_gainparams[2][9][8] = { { { 0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69 }, { 0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69 }, { 0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68 }, { 0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67 }, { 0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66 }, { 0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65 }, { 0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65 }, { 0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65 }, { 0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65 } }, { { 0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, { 0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, { 0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79 }, { 0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78 }, { 0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78 }, { 0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78 }, { 0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78 }, { 0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78 }, { 0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78 } } }; const struct nphy_txiqcal_ladder ladder_lo[] = { { 3, 0 }, { 4, 0 }, { 6, 0 }, { 9, 0 }, { 13, 0 }, { 18, 0 }, { 25, 0 }, { 25, 1 }, { 25, 2 }, { 25, 3 }, { 25, 4 }, { 25, 5 }, { 25, 6 }, { 25, 7 }, { 35, 7 }, { 50, 7 }, { 71, 7 }, { 100, 7 } }; const struct nphy_txiqcal_ladder ladder_iq[] = { { 3, 0 }, { 4, 0 }, { 6, 0 }, { 9, 0 }, { 13, 0 }, { 18, 0 }, { 25, 0 }, { 35, 0 }, { 50, 0 }, { 71, 0 }, { 100, 0 }, { 100, 1 }, { 100, 2 }, { 100, 3 }, { 100, 4 }, { 100, 5 }, { 100, 6 }, { 100, 7 } }; const u16 loscale[] = { 256, 256, 271, 271, 287, 256, 256, 271, 271, 287, 287, 304, 304, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 383, 383, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 256, 256, 271, 271, 287, 287, 304, 304, 322, 322, 341, 341, 362, 362, 383, 383, 406, 406, 430, 430, 455, 455, 482, 482, 511, 511, 541, 541, 573, 573, 607, 607, 643, 643, 681, 681, 722, 722, 764, 764, 810, 810, 858, 858, 908, 908, 962, 962, 1019, 1019, 256 }; const u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { 0x0200, 0x0300, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1201, 0x1202, 0x1203, 0x1204, 0x1205, 0x1206, 0x1207, 0x1907, 0x2307, 0x3207, 0x4707 }; const u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { 0x0300, 0x0500, 0x0700, 0x0900, 0x0d00, 0x1100, 0x1900, 0x1901, 0x1902, 0x1903, 0x1904, 0x1905, 0x1906, 0x1907, 0x2407, 0x3207, 0x4607, 0x6407 }; const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { 0x0100, 0x0200, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1900, 0x2300, 0x3200, 0x4700, 0x4701, 0x4702, 0x4703, 0x4704, 0x4705, 0x4706, 0x4707 }; const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { 0x0200, 0x0300, 0x0600, 0x0900, 0x0d00, 0x1100, 0x1900, 0x2400, 0x3200, 0x4600, 0x6400, 0x6401, 0x6402, 0x6403, 0x6404, 0x6405, 0x6406, 0x6407 }; const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3] = { }; const u16 tbl_tx_iqlo_cal_startcoefs[B43_NTAB_TX_IQLO_CAL_STARTCOEFS] = { }; const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { 0x8423, 0x8323, 0x8073, 0x8256, 0x8045, 0x8223, 0x9423, 0x9323, 0x9073, 0x9256, 0x9045, 0x9223 }; const u16 tbl_tx_iqlo_cal_cmds_recal[] = { 0x8101, 0x8253, 0x8053, 0x8234, 0x8034, 0x9101, 0x9253, 0x9053, 0x9234, 0x9034 }; const u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { 0x8123, 0x8264, 0x8086, 0x8245, 0x8056, 0x9123, 0x9264, 0x9086, 0x9245, 0x9056 }; const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234, 0x9434, 0x9334, 0x9084, 0x9267, 0x9056, 0x9234 }; const s16 tbl_tx_filter_coef_rev4[7][15] = { { -377, 137, -407, 208, -1527, 956, 93, 186, 93, 230, -44, 230, 201, -191, 201 }, { -77, 20, -98, 49, -93, 60, 56, 111, 56, 26, -5, 26, 34, -32, 34 }, { -360, 164, -376, 164, -1533, 576, 308, -314, 308, 121, -73, 121, 91, 124, 91 }, { -295, 200, -363, 142, -1391, 826, 151, 301, 151, 151, 301, 151, 602, -752, 602 }, { -92, 58, -96, 49, -104, 44, 17, 35, 17, 12, 25, 12, 13, 27, 13 }, { -375, 136, -399, 209, -1479, 949, 130, 260, 130, 230, -44, 230, 201, -191, 201 }, { 0xed9, 0xc8, 0xe95, 0x8e, 0xa91, 0x33a, 0x97, 0x12d, 0x97, 0x97, 0x12d, 0x97, 0x25a, 0xd10, 0x25a } }; /* addr0, addr1, bmask, shift */ const struct nphy_rf_control_override_rev2 tbl_rf_control_override_rev2[] = { { 0x78, 0x78, 0x0038, 3 }, /* for field == 0x0002 (fls == 2) */ { 0x7A, 0x7D, 0x0001, 0 }, /* for field == 0x0004 (fls == 3) */ { 0x7A, 0x7D, 0x0002, 1 }, /* for field == 0x0008 (fls == 4) */ { 0x7A, 0x7D, 0x0004, 2 }, /* for field == 0x0010 (fls == 5) */ { 0x7A, 0x7D, 0x0030, 4 }, /* for field == 0x0020 (fls == 6) */ { 0x7A, 0x7D, 0x00C0, 6 }, /* for field == 0x0040 (fls == 7) */ { 0x7A, 0x7D, 0x0100, 8 }, /* for field == 0x0080 (fls == 8) */ { 0x7A, 0x7D, 0x0200, 9 }, /* for field == 0x0100 (fls == 9) */ { 0x78, 0x78, 0x0004, 2 }, /* for field == 0x0200 (fls == 10) */ { 0x7B, 0x7E, 0x01FF, 0 }, /* for field == 0x0400 (fls == 11) */ { 0x7C, 0x7F, 0x01FF, 0 }, /* for field == 0x0800 (fls == 12) */ { 0x78, 0x78, 0x0100, 8 }, /* for field == 0x1000 (fls == 13) */ { 0x78, 0x78, 0x0200, 9 }, /* for field == 0x2000 (fls == 14) */ { 0x78, 0x78, 0xF000, 12 } /* for field == 0x4000 (fls == 15) */ }; /* val_mask, val_shift, en_addr0, val_addr0, en_addr1, val_addr1 */ const struct nphy_rf_control_override_rev3 tbl_rf_control_override_rev3[] = { { 0x8000, 15, 0xE5, 0xF9, 0xE6, 0xFB }, /* field == 0x0001 (fls 1) */ { 0x0001, 0, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0002 (fls 2) */ { 0x0002, 1, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0004 (fls 3) */ { 0x0004, 2, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0008 (fls 4) */ { 0x0010, 4, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0010 (fls 5) */ { 0x0020, 5, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0020 (fls 6) */ { 0x0040, 6, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0040 (fls 7) */ { 0x0080, 7, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0080 (fls 8) */ { 0x0100, 8, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0100 (fls 9) */ { 0x0007, 0, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0200 (fls 10) */ { 0x0070, 4, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0400 (fls 11) */ { 0xE000, 13, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0800 (fls 12) */ { 0xFFFF, 0, 0xE7, 0x7B, 0xEC, 0x7E }, /* field == 0x1000 (fls 13) */ { 0xFFFF, 0, 0xE7, 0x7C, 0xEC, 0x7F }, /* field == 0x2000 (fls 14) */ { 0x00C0, 6, 0xE7, 0xF9, 0xEC, 0xFB } /* field == 0x4000 (fls 15) */ }; /* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ static const struct nphy_rf_control_override_rev7 tbl_rf_control_override_rev7_over0[] = { { 0x0004, 0x07A, 0x07D, 0x0002, 1 }, { 0x0008, 0x07A, 0x07D, 0x0004, 2 }, { 0x0010, 0x07A, 0x07D, 0x0010, 4 }, { 0x0020, 0x07A, 0x07D, 0x0020, 5 }, { 0x0040, 0x07A, 0x07D, 0x0040, 6 }, { 0x0080, 0x0F8, 0x0FA, 0x0080, 7 }, { 0x0400, 0x0F8, 0x0FA, 0x0070, 4 }, { 0x0800, 0x07B, 0x07E, 0xFFFF, 0 }, { 0x1000, 0x07C, 0x07F, 0xFFFF, 0 }, { 0x6000, 0x348, 0x349, 0xFFFF, 0 }, { 0x2000, 0x348, 0x349, 0x000F, 0 }, }; /* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ static const struct nphy_rf_control_override_rev7 tbl_rf_control_override_rev7_over1[] = { { 0x0002, 0x340, 0x341, 0x0002, 1 }, { 0x0008, 0x340, 0x341, 0x0008, 3 }, { 0x0020, 0x340, 0x341, 0x0020, 5 }, { 0x0010, 0x340, 0x341, 0x0010, 4 }, { 0x0004, 0x340, 0x341, 0x0004, 2 }, { 0x0080, 0x340, 0x341, 0x0700, 8 }, { 0x0800, 0x340, 0x341, 0x4000, 14 }, { 0x0400, 0x340, 0x341, 0x2000, 13 }, { 0x0200, 0x340, 0x341, 0x0800, 12 }, { 0x0100, 0x340, 0x341, 0x0100, 11 }, { 0x0040, 0x340, 0x341, 0x0040, 6 }, { 0x0001, 0x340, 0x341, 0x0001, 0 }, }; /* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ static const struct nphy_rf_control_override_rev7 tbl_rf_control_override_rev7_over2[] = { { 0x0008, 0x344, 0x345, 0x0008, 3 }, { 0x0002, 0x344, 0x345, 0x0002, 1 }, { 0x0001, 0x344, 0x345, 0x0001, 0 }, { 0x0004, 0x344, 0x345, 0x0004, 2 }, { 0x0010, 0x344, 0x345, 0x0010, 4 }, }; struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_wa_phy6_radio11_ghz2 = { { 10, 14, 19, 27 }, { -5, 6, 10, 15 }, { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, 0x427E, { 0x413F, 0x413F, 0x413F, 0x413F }, 0x007E, 0x0066, 0x1074, 0x18, 0x18, 0x18, 0x01D0, 0x5, }; struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_workaround[2][4] = { { /* 2GHz */ { /* PHY rev 3 */ { 7, 11, 16, 23 }, { -5, 6, 10, 14 }, { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, 0x627E, { 0x613F, 0x613F, 0x613F, 0x613F }, 0x107E, 0x0066, 0x0074, 0x18, 0x18, 0x18, 0x020D, 0x5, }, { /* PHY rev 4 */ { 8, 12, 17, 25 }, { -5, 6, 10, 14 }, { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, 0x527E, { 0x513F, 0x513F, 0x513F, 0x513F }, 0x007E, 0x0066, 0x0074, 0x18, 0x18, 0x18, 0x01A1, 0x5, }, { /* PHY rev 5 */ { 9, 13, 18, 26 }, { -3, 7, 11, 16 }, { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, 0x427E, /* invalid for external LNA! */ { 0x413F, 0x413F, 0x413F, 0x413F }, /* invalid for external LNA! */ 0x1076, 0x0066, 0x0000, /* low is invalid (the last one) */ 0x18, 0x18, 0x18, 0x01D0, 0x9, }, { /* PHY rev 6+ */ { 8, 13, 18, 25 }, { -5, 6, 10, 14 }, { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, 0x527E, /* invalid for external LNA! */ { 0x513F, 0x513F, 0x513F, 0x513F }, /* invalid for external LNA! */ 0x1076, 0x0066, 0x0000, /* low is invalid (the last one) */ 0x18, 0x18, 0x18, 0x01D0, 0x5, }, }, { /* 5GHz */ { /* PHY rev 3 */ { 7, 11, 17, 23 }, { -6, 2, 6, 10 }, { 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 }, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, 0x52DE, { 0x516F, 0x516F, 0x516F, 0x516F }, 0x00DE, 0x00CA, 0x00CC, 0x1E, 0x1E, 0x1E, 0x01A1, 25, }, { /* PHY rev 4 */ { 8, 12, 18, 23 }, { -5, 2, 6, 10 }, { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, 0x629E, { 0x614F, 0x614F, 0x614F, 0x614F }, 0x029E, 0x1084, 0x0086, 0x24, 0x24, 0x24, 0x0107, 25, }, { /* PHY rev 5 */ { 6, 10, 16, 21 }, { -7, 0, 4, 8 }, { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, 0x729E, { 0x714F, 0x714F, 0x714F, 0x714F }, 0x029E, 0x2084, 0x2086, 0x24, 0x24, 0x24, 0x00A9, 25, }, { /* PHY rev 6+ */ { 6, 10, 16, 21 }, { -7, 0, 4, 8 }, { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, 0x729E, { 0x714F, 0x714F, 0x714F, 0x714F }, 0x029E, 0x2084, 0x2086, 0x24, 0x24, 0x24, /* low is invalid for radio rev 11! */ 0x00F0, 25, }, }, }; static inline void assert_ntab_array_sizes(void) { #undef check #define check(table, size) \ BUILD_BUG_ON(ARRAY_SIZE(b43_ntab_##table) != B43_NTAB_##size##_SIZE) check(adjustpower0, C0_ADJPLT); check(adjustpower1, C1_ADJPLT); check(bdi, BDI); check(channelest, CHANEST); check(estimatepowerlt0, C0_ESTPLT); check(estimatepowerlt1, C1_ESTPLT); check(framelookup, FRAMELT); check(framestruct, FRAMESTRUCT); check(gainctl0, C0_GAINCTL); check(gainctl1, C1_GAINCTL); check(intlevel, INTLEVEL); check(iqlt0, C0_IQLT); check(iqlt1, C1_IQLT); check(loftlt0, C0_LOFEEDTH); check(loftlt1, C1_LOFEEDTH); check(mcs, MCS); check(noisevar10, NOISEVAR10); check(noisevar11, NOISEVAR11); check(pilot, PILOT); check(pilotlt, PILOTLT); check(tdi20a0, TDI20A0); check(tdi20a1, TDI20A1); check(tdi40a0, TDI40A0); check(tdi40a1, TDI40A1); check(tdtrn, TDTRN); check(tmap, TMAP); #undef check } u32 b43_ntab_read(struct b43_wldev *dev, u32 offset) { u32 type, value; type = offset & B43_NTAB_TYPEMASK; offset &= ~B43_NTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); switch (type) { case B43_NTAB_8BIT: b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; break; case B43_NTAB_16BIT: b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); break; case B43_NTAB_32BIT: b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); value |= b43_phy_read(dev, B43_NPHY_TABLE_DATAHI) << 16; break; default: B43_WARN_ON(1); value = 0; } return value; } void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data) { u32 type; u8 *data = _data; unsigned int i; type = offset & B43_NTAB_TYPEMASK; offset &= ~B43_NTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { /* Auto increment broken + caching issue on BCM43224? */ if (dev->dev->chip_id == 43224 && dev->dev->chip_rev == 1) { b43_phy_read(dev, B43_NPHY_TABLE_DATALO); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset + i); } switch (type) { case B43_NTAB_8BIT: *data = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; data++; break; case B43_NTAB_16BIT: *((u16 *)data) = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); data += 2; break; case B43_NTAB_32BIT: *((u32 *)data) = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); *((u32 *)data) |= b43_phy_read(dev, B43_NPHY_TABLE_DATAHI) << 16; data += 4; break; default: B43_WARN_ON(1); } } } void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value) { u32 type; type = offset & B43_NTAB_TYPEMASK; offset &= 0xFFFF; switch (type) { case B43_NTAB_8BIT: B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); break; case B43_NTAB_16BIT: B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); break; case B43_NTAB_32BIT: b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, value >> 16); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value & 0xFFFF); break; default: B43_WARN_ON(1); } return; /* Some compiletime assertions... */ assert_ntab_array_sizes(); } void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *_data) { u32 type, value; const u8 *data = _data; unsigned int i; type = offset & B43_NTAB_TYPEMASK; offset &= ~B43_NTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { /* Auto increment broken + caching issue on BCM43224? */ if ((offset >> 10) == 9 && dev->dev->chip_id == 43224 && dev->dev->chip_rev == 1) { b43_phy_read(dev, B43_NPHY_TABLE_DATALO); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset + i); } switch (type) { case B43_NTAB_8BIT: value = *data; data++; B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); break; case B43_NTAB_16BIT: value = *((u16 *)data); data += 2; B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); break; case B43_NTAB_32BIT: value = *((u32 *)data); data += 4; b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, value >> 16); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value & 0xFFFF); break; default: B43_WARN_ON(1); } } } #define ntab_upload(dev, offset, data) do { \ b43_ntab_write_bulk(dev, offset, offset##_SIZE, data); \ } while (0) void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev) { /* Static tables */ ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); /* Volatile tables */ ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); ntab_upload(dev, B43_NTAB_PILOTLT, b43_ntab_pilotlt); ntab_upload(dev, B43_NTAB_C0_GAINCTL, b43_ntab_gainctl0); ntab_upload(dev, B43_NTAB_C1_GAINCTL, b43_ntab_gainctl1); ntab_upload(dev, B43_NTAB_C0_ESTPLT, b43_ntab_estimatepowerlt0); ntab_upload(dev, B43_NTAB_C1_ESTPLT, b43_ntab_estimatepowerlt1); ntab_upload(dev, B43_NTAB_C0_ADJPLT, b43_ntab_adjustpower0); ntab_upload(dev, B43_NTAB_C1_ADJPLT, b43_ntab_adjustpower1); ntab_upload(dev, B43_NTAB_C0_IQLT, b43_ntab_iqlt0); ntab_upload(dev, B43_NTAB_C1_IQLT, b43_ntab_iqlt1); ntab_upload(dev, B43_NTAB_C0_LOFEEDTH, b43_ntab_loftlt0); ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); } #define ntab_upload_r3(dev, offset, data) do { \ b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ } while (0) void b43_nphy_rev3plus_tables_init(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; /* Static tables */ ntab_upload_r3(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); ntab_upload_r3(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); ntab_upload_r3(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); ntab_upload_r3(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); ntab_upload_r3(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); ntab_upload_r3(dev, B43_NTAB_NOISEVAR0_R3, b43_ntab_noisevar0_r3); ntab_upload_r3(dev, B43_NTAB_NOISEVAR1_R3, b43_ntab_noisevar1_r3); ntab_upload_r3(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); ntab_upload_r3(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); ntab_upload_r3(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); ntab_upload_r3(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); ntab_upload_r3(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); ntab_upload_r3(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); ntab_upload_r3(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); ntab_upload_r3(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); ntab_upload_r3(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); ntab_upload_r3(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); ntab_upload_r3(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); ntab_upload_r3(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); ntab_upload_r3(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); ntab_upload_r3(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); ntab_upload_r3(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); ntab_upload_r3(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); ntab_upload_r3(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); ntab_upload_r3(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); /* Volatile tables */ if (sprom->fem.ghz2.antswlut < ARRAY_SIZE(b43_ntab_antswctl2g_r3)) ntab_upload_r3(dev, B43_NTAB_ANT_SW_CTL_R3, b43_ntab_antswctl2g_r3[sprom->fem.ghz2.antswlut]); else B43_WARN_ON(1); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if (dev->phy.rev >= 6) { if (dev->dev->chip_id == 47162) return txpwrctrl_tx_gain_ipa_rev5; return txpwrctrl_tx_gain_ipa_rev6; } else if (dev->phy.rev >= 5) { return txpwrctrl_tx_gain_ipa_rev5; } else { return txpwrctrl_tx_gain_ipa; } } else { return txpwrctrl_tx_gain_ipa_5g; } } const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) { enum ieee80211_band band = b43_current_band(dev->wl); struct ssb_sprom *sprom = dev->dev->bus_sprom; if (dev->phy.rev < 3) return b43_ntab_tx_gain_rev0_1_2; /* rev 3+ */ if ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) || (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) { return b43_nphy_get_ipa_gain_table(dev); } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { if (dev->phy.rev == 3) return b43_ntab_tx_gain_rev3_5ghz; if (dev->phy.rev == 4) return sprom->fem.ghz5.extpa_gain == 3 ? b43_ntab_tx_gain_rev4_5ghz : b43_ntab_tx_gain_rev4_5ghz; /* FIXME */ else return b43_ntab_tx_gain_rev5plus_5ghz; } else { if (dev->phy.rev >= 5 && sprom->fem.ghz5.extpa_gain == 3) return b43_ntab_tx_gain_rev3plus_2ghz; /* FIXME */ else return b43_ntab_tx_gain_rev3plus_2ghz; } } struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( struct b43_wldev *dev, bool ghz5, bool ext_lna) { struct nphy_gain_ctl_workaround_entry *e; u8 phy_idx; u8 tr_iso = ghz5 ? dev->dev->bus_sprom->fem.ghz5.tr_iso : dev->dev->bus_sprom->fem.ghz2.tr_iso; if (!ghz5 && dev->phy.rev >= 6 && dev->phy.radio_rev == 11) return &nphy_gain_ctl_wa_phy6_radio11_ghz2; B43_WARN_ON(dev->phy.rev < 3); if (dev->phy.rev >= 6) phy_idx = 3; else if (dev->phy.rev == 5) phy_idx = 2; else if (dev->phy.rev == 4) phy_idx = 1; else phy_idx = 0; e = &nphy_gain_ctl_workaround[ghz5][phy_idx]; /* Some workarounds to the workarounds... */ if (ghz5 && dev->phy.rev >= 6) { if (dev->phy.radio_rev == 11 && !b43_channel_type_is_40mhz(dev->phy.channel_type)) e->cliplo_gain = 0x2d; } else if (!ghz5 && dev->phy.rev >= 5) { if (ext_lna) { e->rfseq_init[0] &= ~0x4000; e->rfseq_init[1] &= ~0x4000; e->rfseq_init[2] &= ~0x4000; e->rfseq_init[3] &= ~0x4000; e->init_gain &= ~0x4000; } switch (tr_iso) { case 0: e->cliplo_gain = 0x0062; case 1: e->cliplo_gain = 0x0064; case 2: e->cliplo_gain = 0x006a; case 3: e->cliplo_gain = 0x106a; case 4: e->cliplo_gain = 0x106c; case 5: e->cliplo_gain = 0x1074; case 6: e->cliplo_gain = 0x107c; case 7: e->cliplo_gain = 0x207c; default: e->cliplo_gain = 0x106a; } } else if (ghz5 && dev->phy.rev == 4 && ext_lna) { e->rfseq_init[0] &= ~0x4000; e->rfseq_init[1] &= ~0x4000; e->rfseq_init[2] &= ~0x4000; e->rfseq_init[3] &= ~0x4000; e->init_gain &= ~0x4000; e->rfseq_init[0] |= 0x1000; e->rfseq_init[1] |= 0x1000; e->rfseq_init[2] |= 0x1000; e->rfseq_init[3] |= 0x1000; e->init_gain |= 0x1000; } return e; } const struct nphy_rf_control_override_rev7 *b43_nphy_get_rf_ctl_over_rev7( struct b43_wldev *dev, u16 field, u8 override) { const struct nphy_rf_control_override_rev7 *e; u8 size, i; switch (override) { case 0: e = tbl_rf_control_override_rev7_over0; size = ARRAY_SIZE(tbl_rf_control_override_rev7_over0); break; case 1: e = tbl_rf_control_override_rev7_over1; size = ARRAY_SIZE(tbl_rf_control_override_rev7_over1); break; case 2: e = tbl_rf_control_override_rev7_over2; size = ARRAY_SIZE(tbl_rf_control_override_rev7_over2); break; default: b43err(dev->wl, "Invalid override value %d\n", override); return NULL; } for (i = 0; i < size; i++) { if (e[i].field == field) return &e[i]; } return NULL; } compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_lpphy.h0000644000175000017500000000341712026211315023715 0ustar mcgrofmcgrof#ifndef B43_TABLES_LPPHY_H_ #define B43_TABLES_LPPHY_H_ #define B43_LPTAB_TYPEMASK 0xF0000000 #define B43_LPTAB_8BIT 0x10000000 #define B43_LPTAB_16BIT 0x20000000 #define B43_LPTAB_32BIT 0x30000000 #define B43_LPTAB8(table, offset) (((table) << 10) | (offset) | B43_LPTAB_8BIT) #define B43_LPTAB16(table, offset) (((table) << 10) | (offset) | B43_LPTAB_16BIT) #define B43_LPTAB32(table, offset) (((table) << 10) | (offset) | B43_LPTAB_32BIT) /* Table definitions */ #define B43_LPTAB_TXPWR_R2PLUS B43_LPTAB32(0x07, 0) /* TX power lookup table (rev >= 2) */ #define B43_LPTAB_TXPWR_R0_1 B43_LPTAB32(0xA0, 0) /* TX power lookup table (rev < 2) */ u32 b43_lptab_read(struct b43_wldev *dev, u32 offset); void b43_lptab_write(struct b43_wldev *dev, u32 offset, u32 value); /* Bulk table access. Note that these functions return the bulk data in * host endianness! The returned data is _not_ a bytearray, but an array * consisting of nr_elements of the data type. */ void b43_lptab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *data); void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *data); void b2062_upload_init_table(struct b43_wldev *dev); void b2063_upload_init_table(struct b43_wldev *dev); struct lpphy_tx_gain_table_entry { u8 gm, pga, pad, dac, bb_mult; }; void lpphy_write_gain_table(struct b43_wldev *dev, int offset, struct lpphy_tx_gain_table_entry data); void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, struct lpphy_tx_gain_table_entry *table); void lpphy_rev0_1_table_init(struct b43_wldev *dev); void lpphy_rev2plus_table_init(struct b43_wldev *dev); void lpphy_init_tx_gain_table(struct b43_wldev *dev); #endif /* B43_TABLES_LPPHY_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/tables_lpphy.c0000644000175000017500000045435212026211315023720 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11a/g LP-PHY and radio device data tables Copyright (c) 2009 Michael Buesch Copyright (c) 2009 Gábor Stefanik This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "tables_lpphy.h" #include "phy_common.h" #include "phy_lp.h" /* Entry of the 2062/2063 radio init table */ struct b206x_init_tab_entry { u16 offset; u16 value_a; u16 value_g; u8 flags; }; #define B206X_FLAG_A 0x01 /* Flag: Init in A mode */ #define B206X_FLAG_G 0x02 /* Flag: Init in G mode */ static const struct b206x_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_N_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = 0x0001, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_N_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_PDN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_N_PDN_CTL1, .value_a = 0x0000, .value_g = 0x00CA, .flags = B206X_FLAG_G, }, /* { .offset = B2062_N_PDN_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ { .offset = B2062_N_PDN_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_N_PDN_CTL4, .value_a = 0x0015, .value_g = 0x002A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_GEN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_IQ_CALIB, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ { .offset = B2062_N_LGENC, .value_a = 0x00DB, .value_g = 0x00FF, .flags = B206X_FLAG_A, }, /* { .offset = B2062_N_LGENA_LPF, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_BIAS0, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ /* { .offset = B2062_N_LGNEA_BIAS1, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL0, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_N_LGENA_TUNE0, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_LGENA_TUNE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_N_LGENA_TUNE2, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_N_LGENA_TUNE3, .value_a = 0x0077, .value_g = 0x00B5, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_N_LGENA_CTL3, .value_a = 0x0000, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_LGENA_CTL4, .value_a = 0x001F, .value_g = 0x001F, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL5, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL6, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ { .offset = B2062_N_LGENA_CTL7, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_RXA_CTL0, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ { .offset = B2062_N_RXA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, /* { .offset = B2062_N_RXA_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL3, .value_a = 0x0027, .value_g = 0x0027, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL4, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL5, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL7, .value_a = 0x0008, .value_g = 0x0008, .flags = 0, }, */ { .offset = B2062_N_RXBB_CTL0, .value_a = 0x0082, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_RXBB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_GAIN0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_N_RXBB_GAIN1, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_N_RXBB_GAIN2, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_RXBB_GAIN3, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI0, .value_a = 0x0043, .value_g = 0x0043, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_CALIB0, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_CALIB1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_CALIB2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_BIAS0, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_BIAS1, .value_a = 0x002A, .value_g = 0x002A, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_BIAS2, .value_a = 0x00AA, .value_g = 0x00AA, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_BIAS3, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_BIAS4, .value_a = 0x00AA, .value_g = 0x00AA, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_BIAS5, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI2, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI3, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI4, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI5, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL0, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL2, .value_a = 0x0084, .value_g = 0x0084, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_N_TX_CTL4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_N_TX_CTL5, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_TX_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL7, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL8, .value_a = 0x0082, .value_g = 0x0082, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL_A, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TX_GC2G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ /* { .offset = B2062_N_TX_GC5G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ { .offset = B2062_N_TX_TUNE, .value_a = 0x0088, .value_g = 0x001B, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_TX_PAD, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2062_N_TX_PGA, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2062_N_TX_PADAUX, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2062_N_TX_PGAAUX, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2062_N_TSSI_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TSSI_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TSSI_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_IQ_CALIB_CTL0, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2062_N_IQ_CALIB_CTL1, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_N_IQ_CALIB_CTL2, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_TS, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_CTL1, .value_a = 0x0015, .value_g = 0x0015, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_CTL2, .value_a = 0x000F, .value_g = 0x000F, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_DBG0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_DBG1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_DBG2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_CALIB_DBG3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_PSENSE_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_PSENSE_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_PSENSE_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TEST_BUF0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RADIO_ID_CODE, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_PDS_CTL0, .value_a = 0x00FF, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_PDS_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_PDS_CTL2, .value_a = 0x008E, .value_g = 0x008E, .flags = 0, }, */ /* { .offset = B2062_S_PDS_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_BG_CTL0, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ /* { .offset = B2062_S_BG_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_BG_CTL2, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ { .offset = B2062_S_LGENG_CTL0, .value_a = 0x00F8, .value_g = 0x00D8, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_LGENG_CTL1, .value_a = 0x003C, .value_g = 0x0024, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_LGENG_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL3, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL4, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL6, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_LGENG_CTL8, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_LGENG_CTL9, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ { .offset = B2062_S_LGENG_CTL10, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_LGENG_CTL11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL2, .value_a = 0x00AF, .value_g = 0x00AF, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL3, .value_a = 0x0012, .value_g = 0x0012, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL4, .value_a = 0x000B, .value_g = 0x000B, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL5, .value_a = 0x005F, .value_g = 0x005F, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL7, .value_a = 0x0040, .value_g = 0x0040, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL8, .value_a = 0x0052, .value_g = 0x0052, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL9, .value_a = 0x0026, .value_g = 0x0026, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL10, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL11, .value_a = 0x0036, .value_g = 0x0036, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL12, .value_a = 0x0057, .value_g = 0x0057, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL13, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL14, .value_a = 0x0075, .value_g = 0x0075, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL15, .value_a = 0x00B4, .value_g = 0x00B4, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_RFPLL_CTL0, .value_a = 0x0098, .value_g = 0x0098, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL1, .value_a = 0x0010, .value_g = 0x0010, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_RFPLL_CTL5, .value_a = 0x0043, .value_g = 0x0043, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL6, .value_a = 0x0047, .value_g = 0x0047, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL7, .value_a = 0x000C, .value_g = 0x000C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL8, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL9, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL10, .value_a = 0x000E, .value_g = 0x000E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL11, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL12, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL13, .value_a = 0x000A, .value_g = 0x000A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL14, .value_a = 0x0006, .value_g = 0x0006, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL17, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_RFPLL_CTL18, .value_a = 0x003E, .value_g = 0x003E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL19, .value_a = 0x0013, .value_g = 0x0013, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL20, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_RFPLL_CTL21, .value_a = 0x0062, .value_g = 0x0062, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL22, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL23, .value_a = 0x0016, .value_g = 0x0016, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL24, .value_a = 0x005C, .value_g = 0x005C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL25, .value_a = 0x0095, .value_g = 0x0095, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL26, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL27, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL28, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL29, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_RFPLL_CTL30, .value_a = 0x00A0, .value_g = 0x00A0, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL31, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL32, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2062_S_RFPLL_CTL33, .value_a = 0x00CC, .value_g = 0x00CC, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2062_S_RFPLL_CTL34, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RXG_CNT0, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT5, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT6, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ { .offset = B2062_S_RXG_CNT8, .value_a = 0x000F, .value_g = 0x000F, .flags = B206X_FLAG_A, }, /* { .offset = B2062_S_RXG_CNT9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT10, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT11, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT12, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT13, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT14, .value_a = 0x00A0, .value_g = 0x00A0, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT15, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT17, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ }; static const struct b206x_init_tab_entry b2063_init_tab[] = { { .offset = B2063_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, /* { .offset = B2063_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_COMM10, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A, }, /* { .offset = B2063_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_COMM14, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ /* { .offset = B2063_COMM15, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ { .offset = B2063_COMM16, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM17, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM18, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM19, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM20, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM21, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM22, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM23, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, { .offset = B2063_COMM24, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, /* { .offset = B2063_PWR_SWITCH_CTL, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ /* { .offset = B2063_PLL_SP1, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ /* { .offset = B2063_PLL_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_LOGEN_SP1, .value_a = 0x00e8, .value_g = 0x00d4, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2063_LOGEN_SP2, .value_a = 0x00a7, .value_g = 0x0053, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_LOGEN_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ { .offset = B2063_LOGEN_SP4, .value_a = 0x00f0, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_LOGEN_SP5, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ { .offset = B2063_G_RX_SP1, .value_a = 0x001f, .value_g = 0x005e, .flags = B206X_FLAG_G, }, { .offset = B2063_G_RX_SP2, .value_a = 0x007f, .value_g = 0x007e, .flags = B206X_FLAG_G, }, { .offset = B2063_G_RX_SP3, .value_a = 0x0030, .value_g = 0x00f0, .flags = B206X_FLAG_G, }, /* { .offset = B2063_G_RX_SP4, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ /* { .offset = B2063_G_RX_SP5, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ /* { .offset = B2063_G_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_G_RX_SP7, .value_a = 0x007f, .value_g = 0x007f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_G_RX_SP8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_SP9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_G_RX_SP10, .value_a = 0x000c, .value_g = 0x000c, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_G_RX_SP11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_A_RX_SP1, .value_a = 0x003c, .value_g = 0x003f, .flags = B206X_FLAG_A, }, { .offset = B2063_A_RX_SP2, .value_a = 0x00fc, .value_g = 0x00fe, .flags = B206X_FLAG_A, }, /* { .offset = B2063_A_RX_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ /* { .offset = B2063_A_RX_SP4, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ /* { .offset = B2063_A_RX_SP5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_A_RX_SP7, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_RX_BB_SP1, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ /* { .offset = B2063_RX_BB_SP2, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ /* { .offset = B2063_RX_BB_SP3, .value_a = 0x00a8, .value_g = 0x00a8, .flags = 0, }, */ { .offset = B2063_RX_BB_SP4, .value_a = 0x0060, .value_g = 0x0060, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_RX_BB_SP5, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ /* { .offset = B2063_RX_BB_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RX_BB_SP7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_RX_BB_SP8, .value_a = 0x0030, .value_g = 0x0030, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_TX_RF_SP1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP2, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ { .offset = B2063_TX_RF_SP3, .value_a = 0x000c, .value_g = 0x000b, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2063_TX_RF_SP4, .value_a = 0x0010, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_TX_RF_SP5, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP6, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP7, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP8, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP9, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP10, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP11, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP12, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP13, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP14, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP15, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP16, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ /* { .offset = B2063_TX_RF_SP17, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ { .offset = B2063_PA_SP1, .value_a = 0x003d, .value_g = 0x00fd, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_PA_SP2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ /* { .offset = B2063_PA_SP3, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ /* { .offset = B2063_PA_SP4, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ /* { .offset = B2063_PA_SP5, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ /* { .offset = B2063_PA_SP6, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ /* { .offset = B2063_PA_SP7, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ { .offset = B2063_TX_BB_SP1, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_TX_BB_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_TX_BB_SP3, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ /* { .offset = B2063_REG_SP1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_BANDGAP_CTL1, .value_a = 0x0056, .value_g = 0x0056, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_BANDGAP_CTL2, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ /* { .offset = B2063_LPO_CTL1, .value_a = 0x000e, .value_g = 0x000e, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL1, .value_a = 0x007e, .value_g = 0x007e, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL2, .value_a = 0x0015, .value_g = 0x0015, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL3, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RC_CALIB_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_CALNRST, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_IN_PLL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_IN_PLL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_CP1, .value_a = 0x00cf, .value_g = 0x00cf, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_CP2, .value_a = 0x0059, .value_g = 0x0059, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_CP3, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_CP4, .value_a = 0x0042, .value_g = 0x0042, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_LF1, .value_a = 0x00db, .value_g = 0x00db, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_LF2, .value_a = 0x0094, .value_g = 0x0094, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_LF3, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_LF4, .value_a = 0x0063, .value_g = 0x0063, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_SG1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_SG2, .value_a = 0x00d3, .value_g = 0x00d3, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_SG3, .value_a = 0x00b1, .value_g = 0x00b1, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_SG4, .value_a = 0x003b, .value_g = 0x003b, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_SG5, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO1, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ { .offset = B2063_PLL_JTAG_PLL_VCO2, .value_a = 0x00f7, .value_g = 0x00f7, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB3, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB5, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB6, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB7, .value_a = 0x0016, .value_g = 0x0016, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB8, .value_a = 0x006b, .value_g = 0x006b, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB10, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_XTAL_12, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ /* { .offset = B2063_PLL_JTAG_PLL_XTAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_ACL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_ACL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_ACL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_ACL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_ACL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_INPUTS, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_WAITCNT, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVR2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVAL1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVAL2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVAL3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVAL4, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVAL5, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVAL6, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_OVAL7, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CALVLD1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CALVLD2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CVAL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CVAL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CVAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CVAL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CVAL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CVAL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LO_CALIB_CVAL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_CALIB_EN, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_PEAKDET1, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ /* { .offset = B2063_LOGEN_RCCR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_VCOBUF1, .value_a = 0x0060, .value_g = 0x0060, .flags = 0, }, */ /* { .offset = B2063_LOGEN_MIXER1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_MIXER2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ /* { .offset = B2063_LOGEN_BUF1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_BUF2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ /* { .offset = B2063_LOGEN_DIV1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2063_LOGEN_DIV2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_DIV3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_CBUFRX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_CBUFRX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_CBUFTX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_CBUFTX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ /* { .offset = B2063_LOGEN_IDAC1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_SPARE1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2063_LOGEN_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_LOGEN_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_1ST1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_G_RX_1ST2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND1, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND2, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND7, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ /* { .offset = B2063_G_RX_2ND8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_PS1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_G_RX_PS2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_PS3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_G_RX_PS4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_MIX1, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ /* { .offset = B2063_G_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_G_RX_MIX3, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2063_G_RX_MIX4, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_G_RX_MIX5, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ /* { .offset = B2063_G_RX_MIX6, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2063_G_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ /* { .offset = B2063_G_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2063_G_RX_PDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_SPARES1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_SPARES2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_G_RX_SPARES3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_1ST1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_A_RX_1ST2, .value_a = 0x00f0, .value_g = 0x0030, .flags = B206X_FLAG_A, }, /* { .offset = B2063_A_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ /* { .offset = B2063_A_RX_1ST4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_A_RX_1ST5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_2ND1, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ /* { .offset = B2063_A_RX_2ND2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_2ND3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_2ND4, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ /* { .offset = B2063_A_RX_2ND5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_2ND7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ /* { .offset = B2063_A_RX_PS1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_PS2, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_A_RX_PS3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_PS4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2063_A_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_A_RX_PS6, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_A_RX_MIX1, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2063_A_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_MIX3, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ { .offset = B2063_A_RX_MIX4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2063_A_RX_MIX5, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, { .offset = B2063_A_RX_MIX6, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_A_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ /* { .offset = B2063_A_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2063_A_RX_PWRDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_SPARE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_A_RX_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_RX_TIA_CTL1, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_RX_TIA_CTL2, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ { .offset = B2063_RX_TIA_CTL3, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_RX_TIA_CTL4, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ /* { .offset = B2063_RX_TIA_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RX_TIA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_RX_BB_CTL1, .value_a = 0x0074, .value_g = 0x0074, .flags = 0, }, */ { .offset = B2063_RX_BB_CTL2, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_RX_BB_CTL3, .value_a = 0x00a2, .value_g = 0x00a2, .flags = 0, }, */ /* { .offset = B2063_RX_BB_CTL4, .value_a = 0x00aa, .value_g = 0x00aa, .flags = 0, }, */ /* { .offset = B2063_RX_BB_CTL5, .value_a = 0x0024, .value_g = 0x0024, .flags = 0, }, */ /* { .offset = B2063_RX_BB_CTL6, .value_a = 0x00a9, .value_g = 0x00a9, .flags = 0, }, */ /* { .offset = B2063_RX_BB_CTL7, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ /* { .offset = B2063_RX_BB_CTL8, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ /* { .offset = B2063_RX_BB_CTL9, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL1, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ /* { .offset = B2063_TX_RF_IDAC_LO_RF_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2063_TX_RF_IDAC_LO_RF_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2063_TX_RF_IDAC_LO_BB_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2063_TX_RF_IDAC_LO_BB_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL2, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL3, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL4, .value_a = 0x00b8, .value_g = 0x00b8, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL5, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL6, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL7, .value_a = 0x0078, .value_g = 0x0078, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL8, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL9, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_TX_RF_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_PA_CTL1, .value_a = 0x0000, .value_g = 0x0004, .flags = B206X_FLAG_A, }, /* { .offset = B2063_PA_CTL2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ /* { .offset = B2063_PA_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PA_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PA_CTL5, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ /* { .offset = B2063_PA_CTL6, .value_a = 0x0077, .value_g = 0x0077, .flags = 0, }, */ /* { .offset = B2063_PA_CTL7, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ /* { .offset = B2063_PA_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PA_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PA_CTL10, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ /* { .offset = B2063_PA_CTL11, .value_a = 0x0070, .value_g = 0x0070, .flags = 0, }, */ /* { .offset = B2063_PA_CTL12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_PA_CTL13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_TX_BB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_TX_BB_CTL2, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ /* { .offset = B2063_TX_BB_CTL3, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2063_TX_BB_CTL4, .value_a = 0x000b, .value_g = 0x000b, .flags = 0, }, */ /* { .offset = B2063_GPIO_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ { .offset = B2063_VREG_CTL1, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2063_AMUX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_IQ_CALIB_GVAR, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ /* { .offset = B2063_IQ_CALIB_CTL1, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2063_IQ_CALIB_CTL2, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ /* { .offset = B2063_TEMPSENSE_CTL1, .value_a = 0x0046, .value_g = 0x0046, .flags = 0, }, */ /* { .offset = B2063_TEMPSENSE_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_TX_RX_LOOPBACK1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_TX_RX_LOOPBACK2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2063_EXT_TSSI_CTL1, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ /* { .offset = B2063_EXT_TSSI_CTL2, .value_a = 0x0023, .value_g = 0x0023, .flags = 0, }, */ /* { .offset = B2063_AFE_CTL , .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ }; void b2062_upload_init_table(struct b43_wldev *dev) { const struct b206x_init_tab_entry *e; unsigned int i; for (i = 0; i < ARRAY_SIZE(b2062_init_tab); i++) { e = &b2062_init_tab[i]; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if (!(e->flags & B206X_FLAG_G)) continue; b43_radio_write(dev, e->offset, e->value_g); } else { if (!(e->flags & B206X_FLAG_A)) continue; b43_radio_write(dev, e->offset, e->value_a); } } } void b2063_upload_init_table(struct b43_wldev *dev) { const struct b206x_init_tab_entry *e; unsigned int i; for (i = 0; i < ARRAY_SIZE(b2063_init_tab); i++) { e = &b2063_init_tab[i]; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if (!(e->flags & B206X_FLAG_G)) continue; b43_radio_write(dev, e->offset, e->value_g); } else { if (!(e->flags & B206X_FLAG_A)) continue; b43_radio_write(dev, e->offset, e->value_a); } } } u32 b43_lptab_read(struct b43_wldev *dev, u32 offset) { u32 type, value; type = offset & B43_LPTAB_TYPEMASK; offset &= ~B43_LPTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); switch (type) { case B43_LPTAB_8BIT: b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_LPPHY_TABLEDATALO) & 0xFF; break; case B43_LPTAB_16BIT: b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_LPPHY_TABLEDATALO); break; case B43_LPTAB_32BIT: b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); value = b43_phy_read(dev, B43_LPPHY_TABLEDATAHI); value <<= 16; value |= b43_phy_read(dev, B43_LPPHY_TABLEDATALO); break; default: B43_WARN_ON(1); value = 0; } return value; } void b43_lptab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data) { u32 type; u8 *data = _data; unsigned int i; type = offset & B43_LPTAB_TYPEMASK; offset &= ~B43_LPTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { switch (type) { case B43_LPTAB_8BIT: *data = b43_phy_read(dev, B43_LPPHY_TABLEDATALO) & 0xFF; data++; break; case B43_LPTAB_16BIT: *((u16 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATALO); data += 2; break; case B43_LPTAB_32BIT: *((u32 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATAHI); *((u32 *)data) <<= 16; *((u32 *)data) |= b43_phy_read(dev, B43_LPPHY_TABLEDATALO); data += 4; break; default: B43_WARN_ON(1); } } } void b43_lptab_write(struct b43_wldev *dev, u32 offset, u32 value) { u32 type; type = offset & B43_LPTAB_TYPEMASK; offset &= ~B43_LPTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); switch (type) { case B43_LPTAB_8BIT: B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; case B43_LPTAB_16BIT: B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; case B43_LPTAB_32BIT: b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); b43_phy_write(dev, B43_LPPHY_TABLEDATAHI, value >> 16); b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; default: B43_WARN_ON(1); } } void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *_data) { u32 type, value; const u8 *data = _data; unsigned int i; type = offset & B43_LPTAB_TYPEMASK; offset &= ~B43_LPTAB_TYPEMASK; B43_WARN_ON(offset > 0xFFFF); b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); for (i = 0; i < nr_elements; i++) { switch (type) { case B43_LPTAB_8BIT: value = *data; data++; B43_WARN_ON(value & ~0xFF); b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; case B43_LPTAB_16BIT: value = *((u16 *)data); data += 2; B43_WARN_ON(value & ~0xFFFF); b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; case B43_LPTAB_32BIT: value = *((u32 *)data); data += 4; b43_phy_write(dev, B43_LPPHY_TABLEDATAHI, value >> 16); b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; default: B43_WARN_ON(1); } } } static const u8 lpphy_min_sig_sq_table[] = { 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd, 0xca, 0xc7, 0xc4, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0x00, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd, 0xcf, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, }; static const u16 lpphy_rev01_noise_scale_table[] = { 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa400, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0x00a4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c00, 0x2d36, 0x0000, 0x0000, 0x4c00, 0x2d36, }; static const u16 lpphy_rev2plus_noise_scale_table[] = { 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x0000, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, }; static const u16 lpphy_crs_gain_nft_table[] = { 0x0366, 0x036a, 0x036f, 0x0364, 0x0367, 0x036d, 0x0374, 0x037f, 0x036f, 0x037b, 0x038a, 0x0378, 0x0367, 0x036d, 0x0375, 0x0381, 0x0374, 0x0381, 0x0392, 0x03a9, 0x03c4, 0x03e1, 0x0001, 0x001f, 0x0040, 0x005e, 0x007f, 0x009e, 0x00bd, 0x00dd, 0x00fd, 0x011d, 0x013d, }; static const u16 lpphy_rev01_filter_control_table[] = { 0xa0fc, 0x10fc, 0x10db, 0x20b7, 0xff93, 0x10bf, 0x109b, 0x2077, 0xff53, 0x0127, }; static const u32 lpphy_rev2plus_filter_control_table[] = { 0x000141fc, 0x000021fc, 0x000021b7, 0x0000416f, 0x0001ff27, 0x0000217f, 0x00002137, 0x000040ef, 0x0001fea7, 0x0000024f, }; static const u32 lpphy_rev01_ps_control_table[] = { 0x00010000, 0x000000a0, 0x00040000, 0x00000048, 0x08080101, 0x00000080, 0x08080101, 0x00000040, 0x08080101, 0x000000c0, 0x08a81501, 0x000000c0, 0x0fe8fd01, 0x000000c0, 0x08300105, 0x000000c0, 0x08080201, 0x000000c0, 0x08280205, 0x000000c0, 0xe80802fe, 0x000000c7, 0x28080206, 0x000000c0, 0x08080202, 0x000000c0, 0x0ba87602, 0x000000c0, 0x1068013d, 0x000000c0, 0x10280105, 0x000000c0, 0x08880102, 0x000000c0, 0x08280106, 0x000000c0, 0xe80801fd, 0x000000c7, 0xa8080115, 0x000000c0, }; static const u32 lpphy_rev2plus_ps_control_table[] = { 0x00e38e08, 0x00e08e38, 0x00000000, 0x00000000, 0x00000000, 0x00002080, 0x00006180, 0x00003002, 0x00000040, 0x00002042, 0x00180047, 0x00080043, 0x00000041, 0x000020c1, 0x00046006, 0x00042002, 0x00040000, 0x00002003, 0x00180006, 0x00080002, }; static const u8 lpphy_pll_fraction_table[] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }; static const u16 lpphy_iqlo_cal_table[] = { 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 lpphy_rev0_ofdm_cck_gain_table[] = { 0x0001, 0x0001, 0x0001, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, }; static const u16 lpphy_rev1_ofdm_cck_gain_table[] = { 0x5000, 0x6000, 0x7000, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, }; static const u16 lpphy_gain_delta_table[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u32 lpphy_tx_power_control_table[] = { 0x00000050, 0x0000004f, 0x0000004e, 0x0000004d, 0x0000004c, 0x0000004b, 0x0000004a, 0x00000049, 0x00000048, 0x00000047, 0x00000046, 0x00000045, 0x00000044, 0x00000043, 0x00000042, 0x00000041, 0x00000040, 0x0000003f, 0x0000003e, 0x0000003d, 0x0000003c, 0x0000003b, 0x0000003a, 0x00000039, 0x00000038, 0x00000037, 0x00000036, 0x00000035, 0x00000034, 0x00000033, 0x00000032, 0x00000031, 0x00000030, 0x0000002f, 0x0000002e, 0x0000002d, 0x0000002c, 0x0000002b, 0x0000002a, 0x00000029, 0x00000028, 0x00000027, 0x00000026, 0x00000025, 0x00000024, 0x00000023, 0x00000022, 0x00000021, 0x00000020, 0x0000001f, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001b, 0x0000001a, 0x00000019, 0x00000018, 0x00000017, 0x00000016, 0x00000015, 0x00000014, 0x00000013, 0x00000012, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000075a0, 0x000075a0, 0x000075a1, 0x000075a1, 0x000075a2, 0x000075a2, 0x000075a3, 0x000075a3, 0x000074b0, 0x000074b0, 0x000074b1, 0x000074b1, 0x000074b2, 0x000074b2, 0x000074b3, 0x000074b3, 0x00006d20, 0x00006d20, 0x00006d21, 0x00006d21, 0x00006d22, 0x00006d22, 0x00006d23, 0x00006d23, 0x00004660, 0x00004660, 0x00004661, 0x00004661, 0x00004662, 0x00004662, 0x00004663, 0x00004663, 0x00003e60, 0x00003e60, 0x00003e61, 0x00003e61, 0x00003e62, 0x00003e62, 0x00003e63, 0x00003e63, 0x00003660, 0x00003660, 0x00003661, 0x00003661, 0x00003662, 0x00003662, 0x00003663, 0x00003663, 0x00002e60, 0x00002e60, 0x00002e61, 0x00002e61, 0x00002e62, 0x00002e62, 0x00002e63, 0x00002e63, 0x00002660, 0x00002660, 0x00002661, 0x00002661, 0x00002662, 0x00002662, 0x00002663, 0x00002663, 0x000025e0, 0x000025e0, 0x000025e1, 0x000025e1, 0x000025e2, 0x000025e2, 0x000025e3, 0x000025e3, 0x00001de0, 0x00001de0, 0x00001de1, 0x00001de1, 0x00001de2, 0x00001de2, 0x00001de3, 0x00001de3, 0x00001d60, 0x00001d60, 0x00001d61, 0x00001d61, 0x00001d62, 0x00001d62, 0x00001d63, 0x00001d63, 0x00001560, 0x00001560, 0x00001561, 0x00001561, 0x00001562, 0x00001562, 0x00001563, 0x00001563, 0x00000d60, 0x00000d60, 0x00000d61, 0x00000d61, 0x00000d62, 0x00000d62, 0x00000d63, 0x00000d63, 0x00000ce0, 0x00000ce0, 0x00000ce1, 0x00000ce1, 0x00000ce2, 0x00000ce2, 0x00000ce3, 0x00000ce3, 0x00000e10, 0x00000e10, 0x00000e11, 0x00000e11, 0x00000e12, 0x00000e12, 0x00000e13, 0x00000e13, 0x00000bf0, 0x00000bf0, 0x00000bf1, 0x00000bf1, 0x00000bf2, 0x00000bf2, 0x00000bf3, 0x00000bf3, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x000000ff, 0x000002fc, 0x0000fa08, 0x00000305, 0x00000206, 0x00000304, 0x0000fb04, 0x0000fcff, 0x000005fb, 0x0000fd01, 0x00000401, 0x00000006, 0x0000ff03, 0x000007fc, 0x0000fc08, 0x00000203, 0x0000fffb, 0x00000600, 0x0000fa01, 0x0000fc03, 0x0000fe06, 0x0000fe00, 0x00000102, 0x000007fd, 0x000004fb, 0x000006ff, 0x000004fd, 0x0000fdfa, 0x000007fb, 0x0000fdfa, 0x0000fa06, 0x00000500, 0x0000f902, 0x000007fa, 0x0000fafa, 0x00000500, 0x000007fa, 0x00000700, 0x00000305, 0x000004ff, 0x00000801, 0x00000503, 0x000005f9, 0x00000404, 0x0000fb08, 0x000005fd, 0x00000501, 0x00000405, 0x0000fb03, 0x000007fc, 0x00000403, 0x00000303, 0x00000402, 0x0000faff, 0x0000fe05, 0x000005fd, 0x0000fe01, 0x000007fa, 0x00000202, 0x00000504, 0x00000102, 0x000008fe, 0x0000fa04, 0x0000fafc, 0x0000fe08, 0x000000f9, 0x000002fa, 0x000003fe, 0x00000304, 0x000004f9, 0x00000100, 0x0000fd06, 0x000008fc, 0x00000701, 0x00000504, 0x0000fdfe, 0x0000fdfc, 0x000003fe, 0x00000704, 0x000002fc, 0x000004f9, 0x0000fdfd, 0x0000fa07, 0x00000205, 0x000003fd, 0x000005fb, 0x000004f9, 0x00000804, 0x0000fc06, 0x0000fcf9, 0x00000100, 0x0000fe05, 0x00000408, 0x0000fb02, 0x00000304, 0x000006fe, 0x000004fa, 0x00000305, 0x000008fc, 0x00000102, 0x000001fd, 0x000004fc, 0x0000fe03, 0x00000701, 0x000001fb, 0x000001f9, 0x00000206, 0x000006fd, 0x00000508, 0x00000700, 0x00000304, 0x000005fe, 0x000005ff, 0x0000fa04, 0x00000303, 0x0000fefb, 0x000007f9, 0x0000fefc, 0x000004fd, 0x000005fc, 0x0000fffd, 0x0000fc08, 0x0000fbf9, 0x0000fd07, 0x000008fb, 0x0000fe02, 0x000006fb, 0x00000702, }; static const u32 lpphy_gain_idx_table[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a, }; static const u16 lpphy_aux_gain_idx_table[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016, }; static const u32 lpphy_gain_value_table[] = { 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x000000f1, 0x00000000, 0x00000000, }; static const u16 lpphy_gain_table[] = { 0x0000, 0x0400, 0x0800, 0x0802, 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x080e, 0x080f, 0x0810, 0x0812, 0x0813, 0x0814, 0x0816, 0x0817, 0x081a, 0x081b, 0x081f, 0x0820, 0x0824, 0x0830, 0x0834, 0x0837, 0x083b, 0x083f, 0x0840, 0x0844, 0x0857, 0x085b, 0x085f, 0x08d7, 0x08db, 0x08df, 0x0957, 0x095b, 0x095f, 0x0b57, 0x0b5b, 0x0b5f, 0x0f5f, 0x135f, 0x175f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u32 lpphy_a0_gain_idx_table[] = { 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, }; static const u16 lpphy_a0_aux_gain_idx_table[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014, }; static const u32 lpphy_a0_gain_value_table[] = { 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x000000f7, 0x00000000, 0x00000000, }; static const u16 lpphy_a0_gain_table[] = { 0x0000, 0x0002, 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x000e, 0x000f, 0x0010, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x001a, 0x001b, 0x001f, 0x0020, 0x0024, 0x0030, 0x0034, 0x0037, 0x003b, 0x003f, 0x0040, 0x0044, 0x0057, 0x005b, 0x005f, 0x00d7, 0x00db, 0x00df, 0x0157, 0x015b, 0x015f, 0x0357, 0x035b, 0x035f, 0x075f, 0x0b5f, 0x0f5f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const u16 lpphy_sw_control_table[] = { 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, }; static const u8 lpphy_hf_table[] = { 0x4b, 0x36, 0x24, 0x18, 0x49, 0x34, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17, }; static const u32 lpphy_papd_eps_table[] = { 0x00000000, 0x00013ffc, 0x0001dff3, 0x0001bff0, 0x00023fe9, 0x00021fdf, 0x00028fdf, 0x00033fd2, 0x00039fcb, 0x00043fc7, 0x0004efc2, 0x00055fb5, 0x0005cfb0, 0x00063fa8, 0x00068fa3, 0x00071f98, 0x0007ef92, 0x00084f8b, 0x0008df82, 0x00097f77, 0x0009df69, 0x000a3f62, 0x000adf57, 0x000b6f4c, 0x000bff41, 0x000c9f39, 0x000cff30, 0x000dbf27, 0x000e4f1e, 0x000edf16, 0x000f7f13, 0x00102f11, 0x00110f10, 0x0011df11, 0x0012ef15, 0x00143f1c, 0x00158f27, 0x00172f35, 0x00193f47, 0x001baf5f, 0x001e6f7e, 0x0021cfa4, 0x0025bfd2, 0x002a2008, 0x002fb047, 0x00360090, 0x003d40e0, 0x0045c135, 0x004fb189, 0x005ae1d7, 0x0067221d, 0x0075025a, 0x007ff291, 0x007ff2bf, 0x007ff2e3, 0x007ff2ff, 0x007ff315, 0x007ff329, 0x007ff33f, 0x007ff356, 0x007ff36e, 0x007ff39c, 0x007ff441, 0x007ff506, }; static const u32 lpphy_papd_mult_table[] = { 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, }; static struct lpphy_tx_gain_table_entry lpphy_rev0_nopa_tx_gain_table[] = { { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev0_2ghz_tx_gain_table[] = { { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 71, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 69, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 67, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 58, }, { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 67, }, { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 58, }, { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 57, }, { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 83, }, { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 81, }, { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 78, }, { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 76, }, { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 74, }, { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 72, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev0_5ghz_tx_gain_table[] = { { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev1_nopa_tx_gain_table[] = { { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev1_2ghz_tx_gain_table[] = { { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev1_5ghz_tx_gain_table[] = { { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev2_nopa_tx_gain_table[] = { { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 152, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 147, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 143, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 139, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 135, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 131, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 128, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 124, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 121, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 117, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 114, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 111, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 107, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 104, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 101, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 99, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 96, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 93, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 90, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 88, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 85, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 83, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 81, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 78, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 76, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 74, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 72, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 70, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 68, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 66, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 192, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 176, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 171, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 157, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 144, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 140, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 136, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 105, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 91, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 86, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 79, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 248, .pad = 64, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 241, .pad = 62, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 234, .pad = 60, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 227, .pad = 59, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 221, .pad = 57, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 215, .pad = 55, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 208, .pad = 54, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 203, .pad = 52, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 197, .pad = 51, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 191, .pad = 49, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 186, .pad = 48, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 181, .pad = 47, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 175, .pad = 45, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 170, .pad = 44, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 166, .pad = 43, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 161, .pad = 42, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 156, .pad = 40, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 152, .pad = 39, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 148, .pad = 38, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 143, .pad = 37, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 139, .pad = 36, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 135, .pad = 35, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 132, .pad = 34, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 128, .pad = 33, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 124, .pad = 32, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 124, .pad = 31, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 121, .pad = 31, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 121, .pad = 30, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 117, .pad = 30, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 117, .pad = 29, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 111, .pad = 29, .dac = 0, .bb_mult = 64, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev2_2ghz_tx_gain_table[] = { { .gm = 7, .pga = 99, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 96, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 93, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 90, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 88, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 85, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 83, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 81, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 78, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 76, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 74, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 72, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 70, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 68, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 66, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 62, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 62, .pad = 248, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 60, .pad = 248, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 60, .pad = 241, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 59, .pad = 241, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 59, .pad = 234, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 57, .pad = 234, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 57, .pad = 227, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 55, .pad = 227, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 55, .pad = 221, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 54, .pad = 221, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 54, .pad = 215, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 52, .pad = 215, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 52, .pad = 208, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 51, .pad = 208, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 51, .pad = 203, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 49, .pad = 203, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 49, .pad = 197, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 48, .pad = 197, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 48, .pad = 191, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 47, .pad = 191, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 47, .pad = 186, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 45, .pad = 186, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 45, .pad = 181, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 44, .pad = 181, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 44, .pad = 175, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 43, .pad = 175, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 43, .pad = 170, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 42, .pad = 170, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 42, .pad = 166, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 40, .pad = 166, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 40, .pad = 161, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 39, .pad = 161, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 39, .pad = 156, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 38, .pad = 156, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 38, .pad = 152, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 37, .pad = 152, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 37, .pad = 148, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 36, .pad = 148, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 36, .pad = 143, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 35, .pad = 143, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 35, .pad = 139, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 34, .pad = 139, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 34, .pad = 135, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 33, .pad = 135, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 33, .pad = 132, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 32, .pad = 132, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 32, .pad = 128, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 31, .pad = 128, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 31, .pad = 124, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 30, .pad = 124, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 30, .pad = 121, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 29, .pad = 121, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 29, .pad = 114, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 28, .pad = 114, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 28, .pad = 111, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 27, .pad = 111, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 27, .pad = 108, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 26, .pad = 108, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 26, .pad = 104, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 25, .pad = 104, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 25, .pad = 99, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 24, .pad = 99, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 24, .pad = 96, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 23, .pad = 96, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 23, .pad = 90, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 22, .pad = 90, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 22, .pad = 88, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 21, .pad = 88, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 21, .pad = 83, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 20, .pad = 83, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 20, .pad = 78, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 19, .pad = 78, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 19, .pad = 74, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 18, .pad = 74, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 18, .pad = 70, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 17, .pad = 70, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 17, .pad = 66, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 16, .pad = 66, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 16, .pad = 62, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 62, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 15, .pad = 59, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 59, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 14, .pad = 54, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 54, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, }; static struct lpphy_tx_gain_table_entry lpphy_rev2_5ghz_tx_gain_table[] = { { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 152, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 147, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 143, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 139, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 135, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 131, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 128, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 124, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 121, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 117, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 114, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 111, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 107, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 104, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 101, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 99, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 96, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 93, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 90, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 88, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 85, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 83, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 81, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 78, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 76, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 74, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 72, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 70, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 68, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 66, }, { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 248, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 241, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 234, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 227, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 221, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 215, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 208, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 191, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 175, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 170, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 156, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 143, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 139, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 135, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 104, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 90, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 85, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 78, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 255, .pad = 62, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 248, .pad = 60, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 241, .pad = 59, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 234, .pad = 57, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 227, .pad = 55, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 221, .pad = 54, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 215, .pad = 52, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 208, .pad = 51, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 203, .pad = 49, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 197, .pad = 48, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 191, .pad = 47, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 186, .pad = 45, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 181, .pad = 44, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 175, .pad = 43, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 170, .pad = 42, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 166, .pad = 40, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 161, .pad = 39, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 156, .pad = 38, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 152, .pad = 37, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 148, .pad = 36, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 143, .pad = 35, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 139, .pad = 34, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 135, .pad = 33, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 132, .pad = 32, .dac = 0, .bb_mult = 64, }, { .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, }, }; void lpphy_rev0_1_table_init(struct b43_wldev *dev) { B43_WARN_ON(dev->phy.rev >= 2); b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), ARRAY_SIZE(lpphy_rev01_noise_scale_table), lpphy_rev01_noise_scale_table); b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), ARRAY_SIZE(lpphy_crs_gain_nft_table), lpphy_crs_gain_nft_table); b43_lptab_write_bulk(dev, B43_LPTAB16(8, 0), ARRAY_SIZE(lpphy_rev01_filter_control_table), lpphy_rev01_filter_control_table); b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), ARRAY_SIZE(lpphy_rev01_ps_control_table), lpphy_rev01_ps_control_table); b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); if (dev->phy.rev == 0) { b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); } else { b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); } b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), ARRAY_SIZE(lpphy_gain_delta_table), lpphy_gain_delta_table); b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), ARRAY_SIZE(lpphy_tx_power_control_table), lpphy_tx_power_control_table); } void lpphy_rev2plus_table_init(struct b43_wldev *dev) { int i; B43_WARN_ON(dev->phy.rev < 2); for (i = 0; i < 704; i++) b43_lptab_write(dev, B43_LPTAB32(7, i), 0); b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), ARRAY_SIZE(lpphy_rev2plus_noise_scale_table), lpphy_rev2plus_noise_scale_table); b43_lptab_write_bulk(dev, B43_LPTAB32(11, 0), ARRAY_SIZE(lpphy_rev2plus_filter_control_table), lpphy_rev2plus_filter_control_table); b43_lptab_write_bulk(dev, B43_LPTAB32(12, 0), ARRAY_SIZE(lpphy_rev2plus_ps_control_table), lpphy_rev2plus_ps_control_table); b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), ARRAY_SIZE(lpphy_gain_idx_table), lpphy_gain_idx_table); b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), ARRAY_SIZE(lpphy_aux_gain_idx_table), lpphy_aux_gain_idx_table); b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), ARRAY_SIZE(lpphy_sw_control_table), lpphy_sw_control_table); b43_lptab_write_bulk(dev, B43_LPTAB8(16, 0), ARRAY_SIZE(lpphy_hf_table), lpphy_hf_table); b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), ARRAY_SIZE(lpphy_gain_value_table), lpphy_gain_value_table); b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), ARRAY_SIZE(lpphy_gain_table), lpphy_gain_table); b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), ARRAY_SIZE(lpphy_papd_eps_table), lpphy_papd_eps_table); b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), ARRAY_SIZE(lpphy_papd_mult_table), lpphy_papd_mult_table); if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), ARRAY_SIZE(lpphy_a0_gain_idx_table), lpphy_a0_gain_idx_table); b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), ARRAY_SIZE(lpphy_a0_aux_gain_idx_table), lpphy_a0_aux_gain_idx_table); b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), ARRAY_SIZE(lpphy_a0_gain_value_table), lpphy_a0_gain_value_table); b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), ARRAY_SIZE(lpphy_a0_gain_table), lpphy_a0_gain_table); } } static void lpphy_rev0_1_write_gain_table(struct b43_wldev *dev, int offset, struct lpphy_tx_gain_table_entry data) { u32 tmp; B43_WARN_ON(dev->phy.rev >= 2); tmp = data.pad << 11; tmp |= data.pga << 7; tmp |= data.gm << 4; tmp |= data.dac; b43_lptab_write(dev, B43_LPTAB32(10, 0xC0 + offset), tmp); tmp = data.bb_mult << 20; b43_lptab_write(dev, B43_LPTAB32(10, 0x140 + offset), tmp); } static void lpphy_rev2plus_write_gain_table(struct b43_wldev *dev, int offset, struct lpphy_tx_gain_table_entry data) { u32 tmp; B43_WARN_ON(dev->phy.rev < 2); tmp = data.pad << 16; tmp |= data.pga << 8; tmp |= data.gm; if (dev->phy.rev >= 3) { if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) tmp |= 0x10 << 24; else tmp |= 0x70 << 24; } else { if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) tmp |= 0x14 << 24; else tmp |= 0x7F << 24; } b43_lptab_write(dev, B43_LPTAB32(7, 0xC0 + offset), tmp); tmp = data.bb_mult << 20; tmp |= data.dac << 28; b43_lptab_write(dev, B43_LPTAB32(7, 0x140 + offset), tmp); } void lpphy_write_gain_table(struct b43_wldev *dev, int offset, struct lpphy_tx_gain_table_entry data) { if (dev->phy.rev >= 2) lpphy_rev2plus_write_gain_table(dev, offset, data); else lpphy_rev0_1_write_gain_table(dev, offset, data); } void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, struct lpphy_tx_gain_table_entry *table) { int i; for (i = offset; i < count; i++) lpphy_write_gain_table(dev, i, table[i]); } void lpphy_init_tx_gain_table(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; switch (dev->phy.rev) { case 0: if ((sprom->boardflags_hi & B43_BFH_NOPA) || (sprom->boardflags_lo & B43_BFL_HGPA)) lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev0_nopa_tx_gain_table); else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev0_2ghz_tx_gain_table); else lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev0_5ghz_tx_gain_table); break; case 1: if ((sprom->boardflags_hi & B43_BFH_NOPA) || (sprom->boardflags_lo & B43_BFL_HGPA)) lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev1_nopa_tx_gain_table); else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev1_2ghz_tx_gain_table); else lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev1_5ghz_tx_gain_table); break; default: if (sprom->boardflags_hi & B43_BFH_NOPA) lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev2_nopa_tx_gain_table); else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev2_2ghz_tx_gain_table); else lpphy_write_gain_table_bulk(dev, 0, 128, lpphy_rev2_5ghz_tx_gain_table); } } compat-drivers-2012-09-18/drivers/net/wireless/b43/tables.h0000644000175000017500000000212212026211315022471 0ustar mcgrofmcgrof#ifndef B43_TABLES_H_ #define B43_TABLES_H_ #define B43_TAB_ROTOR_SIZE 53 extern const u32 b43_tab_rotor[]; #define B43_TAB_RETARD_SIZE 53 extern const u32 b43_tab_retard[]; #define B43_TAB_FINEFREQA_SIZE 256 extern const u16 b43_tab_finefreqa[]; #define B43_TAB_FINEFREQG_SIZE 256 extern const u16 b43_tab_finefreqg[]; #define B43_TAB_NOISEA2_SIZE 8 extern const u16 b43_tab_noisea2[]; #define B43_TAB_NOISEA3_SIZE 8 extern const u16 b43_tab_noisea3[]; #define B43_TAB_NOISEG1_SIZE 8 extern const u16 b43_tab_noiseg1[]; #define B43_TAB_NOISEG2_SIZE 8 extern const u16 b43_tab_noiseg2[]; #define B43_TAB_NOISESCALE_SIZE 27 extern const u16 b43_tab_noisescalea2[]; extern const u16 b43_tab_noisescalea3[]; extern const u16 b43_tab_noisescaleg1[]; extern const u16 b43_tab_noisescaleg2[]; extern const u16 b43_tab_noisescaleg3[]; #define B43_TAB_SIGMASQR_SIZE 53 extern const u16 b43_tab_sigmasqr1[]; extern const u16 b43_tab_sigmasqr2[]; #define B43_TAB_RSSIAGC1_SIZE 16 extern const u16 b43_tab_rssiagc1[]; #define B43_TAB_RSSIAGC2_SIZE 48 extern const u16 b43_tab_rssiagc2[]; #endif /* B43_TABLES_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/tables.c0000644000175000017500000003443412026211315022477 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver Copyright (c) 2005 Martin Langer , Copyright (c) 2005-2007 Stefano Brivio Copyright (c) 2006, 2006 Michael Buesch Copyright (c) 2005 Danny van Dyk Copyright (c) 2005 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "tables.h" #include "phy_g.h" const u32 b43_tab_rotor[] = { 0xFEB93FFD, 0xFEC63FFD, /* 0 */ 0xFED23FFD, 0xFEDF3FFD, 0xFEEC3FFE, 0xFEF83FFE, 0xFF053FFE, 0xFF113FFE, 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ 0xFF373FFF, 0xFF443FFF, 0xFF503FFF, 0xFF5D3FFF, 0xFF693FFF, 0xFF763FFF, 0xFF824000, 0xFF8F4000, /* 16 */ 0xFF9B4000, 0xFFA84000, 0xFFB54000, 0xFFC14000, 0xFFCE4000, 0xFFDA4000, 0xFFE74000, 0xFFF34000, /* 24 */ 0x00004000, 0x000D4000, 0x00194000, 0x00264000, 0x00324000, 0x003F4000, 0x004B4000, 0x00584000, /* 32 */ 0x00654000, 0x00714000, 0x007E4000, 0x008A3FFF, 0x00973FFF, 0x00A33FFF, 0x00B03FFF, 0x00BC3FFF, /* 40 */ 0x00C93FFF, 0x00D63FFF, 0x00E23FFE, 0x00EF3FFE, 0x00FB3FFE, 0x01083FFE, 0x01143FFE, 0x01213FFD, /* 48 */ 0x012E3FFD, 0x013A3FFD, 0x01473FFD, }; const u32 b43_tab_retard[] = { 0xDB93CB87, 0xD666CF64, /* 0 */ 0xD1FDD358, 0xCDA6D826, 0xCA38DD9F, 0xC729E2B4, 0xC469E88E, 0xC26AEE2B, 0xC0DEF46C, 0xC073FA62, /* 8 */ 0xC01D00D5, 0xC0760743, 0xC1560D1E, 0xC2E51369, 0xC4ED18FF, 0xC7AC1ED7, 0xCB2823B2, 0xCEFA28D9, /* 16 */ 0xD2F62D3F, 0xD7BB3197, 0xDCE53568, 0xE1FE3875, 0xE7D13B35, 0xED663D35, 0xF39B3EC4, 0xF98E3FA7, /* 24 */ 0x00004000, 0x06723FA7, 0x0C653EC4, 0x129A3D35, 0x182F3B35, 0x1E023875, 0x231B3568, 0x28453197, /* 32 */ 0x2D0A2D3F, 0x310628D9, 0x34D823B2, 0x38541ED7, 0x3B1318FF, 0x3D1B1369, 0x3EAA0D1E, 0x3F8A0743, /* 40 */ 0x3FE300D5, 0x3F8DFA62, 0x3F22F46C, 0x3D96EE2B, 0x3B97E88E, 0x38D7E2B4, 0x35C8DD9F, 0x325AD826, /* 48 */ 0x2E03D358, 0x299ACF64, 0x246DCB87, }; const u16 b43_tab_finefreqa[] = { 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ 0x0202, 0x0282, 0x0302, 0x0382, 0x0402, 0x0482, 0x0502, 0x0582, 0x05E2, 0x0662, 0x06E2, 0x0762, 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ 0x09C2, 0x0A22, 0x0AA2, 0x0B02, 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, 0x0D42, 0x0DA2, 0x0E02, 0x0E62, 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ 0x1062, 0x10C2, 0x1122, 0x1182, 0x11E2, 0x1242, 0x12A2, 0x12E2, 0x1342, 0x13A2, 0x1402, 0x1442, 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ 0x15E2, 0x1622, 0x1662, 0x16C1, 0x1701, 0x1741, 0x1781, 0x17E1, 0x1821, 0x1861, 0x18A1, 0x18E1, 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, 0x1B01, 0x1B41, 0x1B81, 0x1BA1, 0x1BE1, 0x1C21, 0x1C41, 0x1C81, 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, 0x1E21, 0x1E61, 0x1E81, 0x1EA1, 0x1EE1, 0x1F01, 0x1F21, 0x1F41, 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ 0x2001, 0x2041, 0x2061, 0x2081, 0x20A1, 0x20C1, 0x20E1, 0x2101, 0x2121, 0x2141, 0x2161, 0x2181, 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ 0x2221, 0x2241, 0x2261, 0x2281, 0x22A1, 0x22C1, 0x22C1, 0x22E1, 0x2301, 0x2321, 0x2341, 0x2361, 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ 0x23E1, 0x23E1, 0x2401, 0x2421, 0x2441, 0x2441, 0x2461, 0x2481, 0x2481, 0x24A1, 0x24C1, 0x24C1, 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ 0x2541, 0x2541, 0x2561, 0x2561, 0x2581, 0x25A1, 0x25A1, 0x25C1, 0x25C1, 0x25E1, 0x2601, 0x2601, 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ 0x2661, 0x2661, 0x2681, 0x2681, 0x26A1, 0x26A1, 0x26C1, 0x26C1, 0x26E1, 0x26E1, 0x2701, 0x2701, 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ 0x2760, 0x2760, 0x2780, 0x2780, 0x2780, 0x27A0, 0x27A0, 0x27C0, 0x27C0, 0x27E0, 0x27E0, 0x27E0, 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ 0x2820, 0x2840, 0x2840, 0x2840, 0x2860, 0x2860, 0x2880, 0x2880, 0x2880, 0x28A0, 0x28A0, 0x28A0, 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ 0x28E0, 0x28E0, 0x2900, 0x2900, 0x2900, 0x2920, 0x2920, 0x2920, 0x2940, 0x2940, 0x2940, 0x2960, 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ 0x2980, 0x2980, 0x29A0, 0x29A0, 0x29A0, 0x29A0, 0x29C0, 0x29C0, 0x29C0, 0x29E0, 0x29E0, 0x29E0, 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ 0x2A00, 0x2A20, 0x2A20, 0x2A20, 0x2A20, 0x2A40, 0x2A40, 0x2A40, 0x2A40, 0x2A60, 0x2A60, 0x2A60, }; const u16 b43_tab_finefreqg[] = { 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ 0x05A9, 0x0669, 0x0709, 0x0789, 0x0829, 0x08A9, 0x0929, 0x0989, 0x0A09, 0x0A69, 0x0AC9, 0x0B29, 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ 0x0D09, 0x0D69, 0x0DA9, 0x0E09, 0x0E69, 0x0EA9, 0x0F09, 0x0F49, 0x0FA9, 0x0FE9, 0x1029, 0x1089, 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ 0x11E9, 0x1229, 0x1289, 0x12C9, 0x1309, 0x1349, 0x1389, 0x13C9, 0x1409, 0x1449, 0x14A9, 0x14E9, 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ 0x1629, 0x1669, 0x16A9, 0x16E8, 0x1728, 0x1768, 0x17A8, 0x17E8, 0x1828, 0x1868, 0x18A8, 0x18E8, 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ 0x1E48, 0x1E88, 0x1EC8, 0x1F08, 0x1F48, 0x1F88, 0x1FE8, 0x2028, 0x2068, 0x20A8, 0x2108, 0x2148, 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ 0x22C8, 0x2308, 0x2348, 0x23A8, 0x23E8, 0x2448, 0x24A8, 0x24E8, 0x2548, 0x25A8, 0x2608, 0x2668, 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ 0x2847, 0x28C7, 0x2947, 0x29A7, 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, 0x2CA7, 0x2D67, 0x2E47, 0x2F67, 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ 0x3806, 0x38A6, 0x3946, 0x39E6, 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, 0x3C45, 0x3CA5, 0x3D05, 0x3D85, 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ 0x3F45, 0x3FA5, 0x4005, 0x4045, 0x40A5, 0x40E5, 0x4145, 0x4185, 0x41E5, 0x4225, 0x4265, 0x42C5, 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ 0x4424, 0x4464, 0x44C4, 0x4504, 0x4544, 0x4584, 0x45C4, 0x4604, 0x4644, 0x46A4, 0x46E4, 0x4724, 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ 0x4864, 0x48A4, 0x48E4, 0x4924, 0x4964, 0x49A4, 0x49E4, 0x4A24, 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ 0x5083, 0x50C3, 0x5103, 0x5143, 0x5183, 0x51E2, 0x5222, 0x5262, 0x52A2, 0x52E2, 0x5342, 0x5382, 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ 0x5502, 0x5542, 0x55A2, 0x55E2, 0x5642, 0x5682, 0x56E2, 0x5722, 0x5782, 0x57E1, 0x5841, 0x58A1, 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, 0x5C61, 0x5D01, 0x5D80, 0x5E20, 0x5EE0, 0x5FA0, 0x6080, 0x61C0, }; const u16 b43_tab_noisea2[] = { 0x0001, 0x0001, 0x0001, 0xFFFE, 0xFFFE, 0x3FFF, 0x1000, 0x0393, }; const u16 b43_tab_noisea3[] = { 0x5E5E, 0x5E5E, 0x5E5E, 0x3F48, 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, }; const u16 b43_tab_noiseg1[] = { 0x013C, 0x01F5, 0x031A, 0x0631, 0x0001, 0x0001, 0x0001, 0x0001, }; const u16 b43_tab_noiseg2[] = { 0x5484, 0x3C40, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; const u16 b43_tab_noisescalea2[] = { 0x6767, 0x6767, 0x6767, 0x6767, /* 0 */ 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x6700, 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, /* 16 */ 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x6767, 0x0067, }; const u16 b43_tab_noisescalea3[] = { 0x2323, 0x2323, 0x2323, 0x2323, /* 0 */ 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x2300, 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, /* 16 */ 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x2323, 0x0023, }; const u16 b43_tab_noisescaleg1[] = { 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ 0x2F2D, 0x2A2A, 0x2527, 0x1F21, 0x1A1D, 0x1719, 0x1616, 0x1414, 0x1414, 0x1400, 0x1414, 0x1614, 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ 0x2A27, 0x2F2A, 0x332D, 0x3B35, 0x5140, 0x6C62, 0x0077, }; const u16 b43_tab_noisescaleg2[] = { 0xD8DD, 0xCBD4, 0xBCC0, 0xB6B7, /* 0 */ 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, 0x969B, 0x9195, 0x8F8F, 0x8A8A, 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, 0xCBC0, 0xD8D4, 0x00DD, }; const u16 b43_tab_noisescaleg3[] = { 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, 0x00A4, }; const u16 b43_tab_sigmasqr1[] = { 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ 0x0067, 0x0063, 0x005E, 0x0059, 0x0054, 0x0050, 0x004B, 0x0046, 0x0042, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x0000, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ 0x003D, 0x003D, 0x003D, 0x003D, 0x0042, 0x0046, 0x004B, 0x0050, 0x0054, 0x0059, 0x005E, 0x0063, 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ 0x007A, }; const u16 b43_tab_sigmasqr2[] = { 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ 0x00D6, 0x00D4, 0x00D2, 0x00CF, 0x00CD, 0x00CA, 0x00C7, 0x00C4, 0x00C1, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x0000, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ 0x00BE, 0x00BE, 0x00BE, 0x00BE, 0x00C1, 0x00C4, 0x00C7, 0x00CA, 0x00CD, 0x00CF, 0x00D2, 0x00D4, 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ 0x00DE, }; const u16 b43_tab_rssiagc1[] = { 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, /* 0 */ 0xFFF8, 0xFFF9, 0xFFFC, 0xFFFE, 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, }; const u16 b43_tab_rssiagc2[] = { 0x0820, 0x0820, 0x0920, 0x0C38, /* 0 */ 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0920, 0x0A38, 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0920, 0x0A38, /* 16 */ 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0920, 0x0A38, 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0920, 0x0A38, /* 32 */ 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0820, 0x0920, 0x0A38, 0x0820, 0x0820, 0x0820, 0x0820, }; static inline void assert_sizes(void) { BUILD_BUG_ON(B43_TAB_ROTOR_SIZE != ARRAY_SIZE(b43_tab_rotor)); BUILD_BUG_ON(B43_TAB_RETARD_SIZE != ARRAY_SIZE(b43_tab_retard)); BUILD_BUG_ON(B43_TAB_FINEFREQA_SIZE != ARRAY_SIZE(b43_tab_finefreqa)); BUILD_BUG_ON(B43_TAB_FINEFREQG_SIZE != ARRAY_SIZE(b43_tab_finefreqg)); BUILD_BUG_ON(B43_TAB_NOISEA2_SIZE != ARRAY_SIZE(b43_tab_noisea2)); BUILD_BUG_ON(B43_TAB_NOISEA3_SIZE != ARRAY_SIZE(b43_tab_noisea3)); BUILD_BUG_ON(B43_TAB_NOISEG1_SIZE != ARRAY_SIZE(b43_tab_noiseg1)); BUILD_BUG_ON(B43_TAB_NOISEG2_SIZE != ARRAY_SIZE(b43_tab_noiseg2)); BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != ARRAY_SIZE(b43_tab_noisescalea2)); BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != ARRAY_SIZE(b43_tab_noisescalea3)); BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != ARRAY_SIZE(b43_tab_noisescaleg1)); BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != ARRAY_SIZE(b43_tab_noisescaleg2)); BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != ARRAY_SIZE(b43_tab_noisescaleg3)); BUILD_BUG_ON(B43_TAB_SIGMASQR_SIZE != ARRAY_SIZE(b43_tab_sigmasqr1)); BUILD_BUG_ON(B43_TAB_SIGMASQR_SIZE != ARRAY_SIZE(b43_tab_sigmasqr2)); BUILD_BUG_ON(B43_TAB_RSSIAGC1_SIZE != ARRAY_SIZE(b43_tab_rssiagc1)); BUILD_BUG_ON(B43_TAB_RSSIAGC2_SIZE != ARRAY_SIZE(b43_tab_rssiagc2)); } u16 b43_ofdmtab_read16(struct b43_wldev *dev, u16 table, u16 offset) { struct b43_phy_g *gphy = dev->phy.g; u16 addr; addr = table + offset; if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_READ) || (addr - 1 != gphy->ofdmtab_addr)) { /* The hardware has a different address in memory. Update it. */ b43_phy_write(dev, B43_PHY_OTABLECTL, addr); gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_READ; } gphy->ofdmtab_addr = addr; return b43_phy_read(dev, B43_PHY_OTABLEI); /* Some compiletime assertions... */ assert_sizes(); } void b43_ofdmtab_write16(struct b43_wldev *dev, u16 table, u16 offset, u16 value) { struct b43_phy_g *gphy = dev->phy.g; u16 addr; addr = table + offset; if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_WRITE) || (addr - 1 != gphy->ofdmtab_addr)) { /* The hardware has a different address in memory. Update it. */ b43_phy_write(dev, B43_PHY_OTABLECTL, addr); gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_WRITE; } gphy->ofdmtab_addr = addr; b43_phy_write(dev, B43_PHY_OTABLEI, value); } u32 b43_ofdmtab_read32(struct b43_wldev *dev, u16 table, u16 offset) { struct b43_phy_g *gphy = dev->phy.g; u32 ret; u16 addr; addr = table + offset; if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_READ) || (addr - 1 != gphy->ofdmtab_addr)) { /* The hardware has a different address in memory. Update it. */ b43_phy_write(dev, B43_PHY_OTABLECTL, addr); gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_READ; } gphy->ofdmtab_addr = addr; ret = b43_phy_read(dev, B43_PHY_OTABLEQ); ret <<= 16; ret |= b43_phy_read(dev, B43_PHY_OTABLEI); return ret; } void b43_ofdmtab_write32(struct b43_wldev *dev, u16 table, u16 offset, u32 value) { struct b43_phy_g *gphy = dev->phy.g; u16 addr; addr = table + offset; if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_WRITE) || (addr - 1 != gphy->ofdmtab_addr)) { /* The hardware has a different address in memory. Update it. */ b43_phy_write(dev, B43_PHY_OTABLECTL, addr); gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_WRITE; } gphy->ofdmtab_addr = addr; b43_phy_write(dev, B43_PHY_OTABLEI, value); b43_phy_write(dev, B43_PHY_OTABLEQ, (value >> 16)); } u16 b43_gtab_read(struct b43_wldev *dev, u16 table, u16 offset) { b43_phy_write(dev, B43_PHY_GTABCTL, table + offset); return b43_phy_read(dev, B43_PHY_GTABDATA); } void b43_gtab_write(struct b43_wldev *dev, u16 table, u16 offset, u16 value) { b43_phy_write(dev, B43_PHY_GTABCTL, table + offset); b43_phy_write(dev, B43_PHY_GTABDATA, value); } compat-drivers-2012-09-18/drivers/net/wireless/b43/sysfs.h0000644000175000017500000000027212026211315022372 0ustar mcgrofmcgrof#ifndef B43_SYSFS_H_ #define B43_SYSFS_H_ struct b43_wldev; int b43_sysfs_register(struct b43_wldev *dev); void b43_sysfs_unregister(struct b43_wldev *dev); #endif /* B43_SYSFS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/sysfs.c0000644000175000017500000000661012026211315022367 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver SYSFS support routines Copyright (c) 2006 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "b43.h" #include "sysfs.h" #include "main.h" #include "phy_common.h" #define GENERIC_FILESIZE 64 static int get_integer(const char *buf, size_t count) { char tmp[10 + 1] = { 0 }; int ret = -EINVAL; if (count == 0) goto out; count = min(count, (size_t) 10); memcpy(tmp, buf, count); ret = simple_strtol(tmp, NULL, 10); out: return ret; } static ssize_t b43_attr_interfmode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct b43_wldev *wldev = dev_to_b43_wldev(dev); ssize_t count = 0; if (!capable(CAP_NET_ADMIN)) return -EPERM; mutex_lock(&wldev->wl->mutex); if (wldev->phy.type != B43_PHYTYPE_G) { mutex_unlock(&wldev->wl->mutex); return -ENOSYS; } switch (wldev->phy.g->interfmode) { case B43_INTERFMODE_NONE: count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n"); break; case B43_INTERFMODE_NONWLAN: count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n"); break; case B43_INTERFMODE_MANUALWLAN: count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n"); break; default: B43_WARN_ON(1); } mutex_unlock(&wldev->wl->mutex); return count; } static ssize_t b43_attr_interfmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct b43_wldev *wldev = dev_to_b43_wldev(dev); int err; int mode; if (!capable(CAP_NET_ADMIN)) return -EPERM; mode = get_integer(buf, count); switch (mode) { case 0: mode = B43_INTERFMODE_NONE; break; case 1: mode = B43_INTERFMODE_NONWLAN; break; case 2: mode = B43_INTERFMODE_MANUALWLAN; break; case 3: mode = B43_INTERFMODE_AUTOWLAN; break; default: return -EINVAL; } mutex_lock(&wldev->wl->mutex); if (wldev->phy.ops->interf_mitigation) { err = wldev->phy.ops->interf_mitigation(wldev, mode); if (err) { b43err(wldev->wl, "Interference Mitigation not " "supported by device\n"); } } else err = -ENOSYS; mmiowb(); mutex_unlock(&wldev->wl->mutex); return err ? err : count; } static DEVICE_ATTR(interference, 0644, b43_attr_interfmode_show, b43_attr_interfmode_store); int b43_sysfs_register(struct b43_wldev *wldev) { struct device *dev = wldev->dev->dev; B43_WARN_ON(b43_status(wldev) != B43_STAT_INITIALIZED); return device_create_file(dev, &dev_attr_interference); } void b43_sysfs_unregister(struct b43_wldev *wldev) { struct device *dev = wldev->dev->dev; device_remove_file(dev, &dev_attr_interference); } compat-drivers-2012-09-18/drivers/net/wireless/b43/sdio.h0000644000175000017500000000137612026211315022167 0ustar mcgrofmcgrof#ifndef B43_SDIO_H_ #define B43_SDIO_H_ #include struct b43_wldev; #ifdef CONFIG_B43_SDIO struct b43_sdio { struct ssb_bus ssb; void *irq_handler_opaque; void (*irq_handler)(struct b43_wldev *dev); }; int b43_sdio_request_irq(struct b43_wldev *dev, void (*handler)(struct b43_wldev *dev)); void b43_sdio_free_irq(struct b43_wldev *dev); int b43_sdio_init(void); void b43_sdio_exit(void); #else /* CONFIG_B43_SDIO */ int b43_sdio_request_irq(struct b43_wldev *dev, void (*handler)(struct b43_wldev *dev)) { return -ENODEV; } void b43_sdio_free_irq(struct b43_wldev *dev) { } static inline int b43_sdio_init(void) { return 0; } static inline void b43_sdio_exit(void) { } #endif /* CONFIG_B43_SDIO */ #endif /* B43_SDIO_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/sdio.c0000644000175000017500000001122712026211315022156 0ustar mcgrofmcgrof/* * Broadcom B43 wireless driver * * SDIO over Sonics Silicon Backplane bus glue for b43. * * Copyright (C) 2009 Albert Herranz * Copyright (C) 2009 Michael Buesch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #include #include #include #include #include #include #include "sdio.h" #include "b43.h" #define HNBU_CHIPID 0x01 /* vendor & device id */ #define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ static const struct b43_sdio_quirk { u16 vendor; u16 device; unsigned int quirks; } b43_sdio_quirks[] = { { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, { }, }; static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) { const struct b43_sdio_quirk *q; for (q = b43_sdio_quirks; q->quirks; q++) { if (vendor == q->vendor && device == q->device) return q->quirks; } return 0; } static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) { struct b43_sdio *sdio = sdio_get_drvdata(func); struct b43_wldev *dev = sdio->irq_handler_opaque; if (unlikely(b43_status(dev) < B43_STAT_STARTED)) return; sdio_release_host(func); sdio->irq_handler(dev); sdio_claim_host(func); } int b43_sdio_request_irq(struct b43_wldev *dev, void (*handler)(struct b43_wldev *dev)) { struct ssb_bus *bus = dev->dev->sdev->bus; struct sdio_func *func = bus->host_sdio; struct b43_sdio *sdio = sdio_get_drvdata(func); int err; sdio->irq_handler_opaque = dev; sdio->irq_handler = handler; sdio_claim_host(func); err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); sdio_release_host(func); return err; } void b43_sdio_free_irq(struct b43_wldev *dev) { struct ssb_bus *bus = dev->dev->sdev->bus; struct sdio_func *func = bus->host_sdio; struct b43_sdio *sdio = sdio_get_drvdata(func); sdio_claim_host(func); sdio_release_irq(func); sdio_release_host(func); sdio->irq_handler_opaque = NULL; sdio->irq_handler = NULL; } static int __devinit b43_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct b43_sdio *sdio; struct sdio_func_tuple *tuple; u16 vendor = 0, device = 0; int error; /* Look for the card chip identifier. */ tuple = func->tuples; while (tuple) { switch (tuple->code) { case 0x80: switch (tuple->data[0]) { case HNBU_CHIPID: if (tuple->size != 5) break; vendor = tuple->data[1] | (tuple->data[2]<<8); device = tuple->data[3] | (tuple->data[4]<<8); dev_info(&func->dev, "Chip ID %04x:%04x\n", vendor, device); break; default: break; } break; default: break; } tuple = tuple->next; } if (!vendor || !device) { error = -ENODEV; goto out; } sdio_claim_host(func); error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); if (error) { dev_err(&func->dev, "failed to set block size to %u bytes," " error %d\n", B43_SDIO_BLOCK_SIZE, error); goto err_release_host; } error = sdio_enable_func(func); if (error) { dev_err(&func->dev, "failed to enable func, error %d\n", error); goto err_release_host; } sdio_release_host(func); sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); if (!sdio) { error = -ENOMEM; dev_err(&func->dev, "failed to allocate ssb bus\n"); goto err_disable_func; } error = ssb_bus_sdiobus_register(&sdio->ssb, func, b43_sdio_get_quirks(vendor, device)); if (error) { dev_err(&func->dev, "failed to register ssb sdio bus," " error %d\n", error); goto err_free_ssb; } sdio_set_drvdata(func, sdio); return 0; err_free_ssb: kfree(sdio); err_disable_func: sdio_claim_host(func); sdio_disable_func(func); err_release_host: sdio_release_host(func); out: return error; } static void __devexit b43_sdio_remove(struct sdio_func *func) { struct b43_sdio *sdio = sdio_get_drvdata(func); ssb_bus_unregister(&sdio->ssb); sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); kfree(sdio); sdio_set_drvdata(func, NULL); } static const struct sdio_device_id b43_sdio_ids[] = { { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */ { SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */ { }, }; static struct sdio_driver b43_sdio_driver = { .name = "b43-sdio", .id_table = b43_sdio_ids, .probe = b43_sdio_probe, .remove = __devexit_p(b43_sdio_remove), }; int b43_sdio_init(void) { return sdio_register_driver(&b43_sdio_driver); } void b43_sdio_exit(void) { sdio_unregister_driver(&b43_sdio_driver); } compat-drivers-2012-09-18/drivers/net/wireless/b43/rfkill.h0000644000175000017500000000032612026211315022506 0ustar mcgrofmcgrof#ifndef B43_RFKILL_H_ #define B43_RFKILL_H_ struct ieee80211_hw; struct b43_wldev; void b43_rfkill_poll(struct ieee80211_hw *hw); bool b43_is_hw_radio_enabled(struct b43_wldev *dev); #endif /* B43_RFKILL_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/rfkill.c0000644000175000017500000000364212026211315022505 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver RFKILL support Copyright (c) 2007 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" /* Returns TRUE, if the radio is enabled in hardware. */ bool b43_is_hw_radio_enabled(struct b43_wldev *dev) { return !(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI) & B43_MMIO_RADIO_HWENABLED_HI_MASK); } /* The poll callback for the hardware button. */ void b43_rfkill_poll(struct ieee80211_hw *hw) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; bool enabled; bool brought_up = false; mutex_lock(&wl->mutex); if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) { if (b43_bus_powerup(dev, 0)) { mutex_unlock(&wl->mutex); return; } b43_device_enable(dev, 0); brought_up = true; } enabled = b43_is_hw_radio_enabled(dev); if (unlikely(enabled != dev->radio_hw_enable)) { dev->radio_hw_enable = enabled; b43info(wl, "Radio hardware status changed to %s\n", enabled ? "ENABLED" : "DISABLED"); wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); if (enabled != dev->phy.radio_on) b43_software_rfkill(dev, !enabled); } if (brought_up) { b43_device_disable(dev, 0); b43_bus_may_powerdown(dev); } mutex_unlock(&wl->mutex); } compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2059.h0000644000175000017500000000215112026211315022776 0ustar mcgrofmcgrof#ifndef B43_RADIO_2059_H_ #define B43_RADIO_2059_H_ #include #include "phy_ht.h" #define R2059_SYN 0x000 #define R2059_TXRX0 0x400 #define R2059_RXRX1 0x800 #define R2059_ALL 0xC00 /* Values for various registers uploaded on channel switching */ struct b43_phy_ht_channeltab_e_radio2059 { /* The channel frequency in MHz */ u16 freq; /* Values for radio registers */ u8 radio_syn16; u8 radio_syn17; u8 radio_syn22; u8 radio_syn25; u8 radio_syn27; u8 radio_syn28; u8 radio_syn29; u8 radio_syn2c; u8 radio_syn2d; u8 radio_syn37; u8 radio_syn41; u8 radio_syn43; u8 radio_syn47; u8 radio_syn4a; u8 radio_syn58; u8 radio_syn5a; u8 radio_syn6a; u8 radio_syn6d; u8 radio_syn6e; u8 radio_syn92; u8 radio_syn98; u8 radio_rxtx4a; u8 radio_rxtx58; u8 radio_rxtx5a; u8 radio_rxtx6a; u8 radio_rxtx6d; u8 radio_rxtx6e; u8 radio_rxtx92; u8 radio_rxtx98; /* Values for PHY registers */ struct b43_phy_ht_channeltab_e_phy phy_regs; }; const struct b43_phy_ht_channeltab_e_radio2059 *b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq); #endif /* B43_RADIO_2059_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2059.c0000644000175000017500000001372612026211315023003 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n 2059 radio device data tables Copyright (c) 2011 Rafał Miłecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "radio_2059.h" #define RADIOREGS(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ r20, r21, r22, r23, r24, r25, r26, r27, r28) \ .radio_syn16 = r00, \ .radio_syn17 = r01, \ .radio_syn22 = r02, \ .radio_syn25 = r03, \ .radio_syn27 = r04, \ .radio_syn28 = r05, \ .radio_syn29 = r06, \ .radio_syn2c = r07, \ .radio_syn2d = r08, \ .radio_syn37 = r09, \ .radio_syn41 = r10, \ .radio_syn43 = r11, \ .radio_syn47 = r12, \ .radio_syn4a = r13, \ .radio_syn58 = r14, \ .radio_syn5a = r15, \ .radio_syn6a = r16, \ .radio_syn6d = r17, \ .radio_syn6e = r18, \ .radio_syn92 = r19, \ .radio_syn98 = r20, \ .radio_rxtx4a = r21, \ .radio_rxtx58 = r22, \ .radio_rxtx5a = r23, \ .radio_rxtx6a = r24, \ .radio_rxtx6d = r25, \ .radio_rxtx6e = r26, \ .radio_rxtx92 = r27, \ .radio_rxtx98 = r28 #define PHYREGS(r0, r1, r2, r3, r4, r5) \ .phy_regs.bw1 = r0, \ .phy_regs.bw2 = r1, \ .phy_regs.bw3 = r2, \ .phy_regs.bw4 = r3, \ .phy_regs.bw5 = r4, \ .phy_regs.bw6 = r5 static const struct b43_phy_ht_channeltab_e_radio2059 b43_phy_ht_channeltab_radio2059[] = { { .freq = 2412, RADIOREGS(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), }, { .freq = 2417, RADIOREGS(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), }, { .freq = 2422, RADIOREGS(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), }, { .freq = 2427, RADIOREGS(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), }, { .freq = 2432, RADIOREGS(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), }, { .freq = 2437, RADIOREGS(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), }, { .freq = 2442, RADIOREGS(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), }, { .freq = 2447, RADIOREGS(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), }, { .freq = 2452, RADIOREGS(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), }, { .freq = 2457, RADIOREGS(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), }, { .freq = 2462, RADIOREGS(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), }, { .freq = 2467, RADIOREGS(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), }, { .freq = 2472, RADIOREGS(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x00), PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), }, }; const struct b43_phy_ht_channeltab_e_radio2059 *b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq) { const struct b43_phy_ht_channeltab_e_radio2059 *e; unsigned int i; e = b43_phy_ht_channeltab_radio2059; for (i = 0; i < ARRAY_SIZE(b43_phy_ht_channeltab_radio2059); i++, e++) { if (e->freq == freq) return e; } return NULL; } compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2057.h0000644000175000017500000004120612026211315023000 0ustar mcgrofmcgrof#ifndef B43_RADIO_2057_H_ #define B43_RADIO_2057_H_ #include #include "tables_nphy.h" #define R2057_DACBUF_VINCM_CORE0 0x000 #define R2057_IDCODE 0x001 #define R2057_RCCAL_MASTER 0x002 #define R2057_RCCAL_CAP_SIZE 0x003 #define R2057_RCAL_CONFIG 0x004 #define R2057_GPAIO_CONFIG 0x005 #define R2057_GPAIO_SEL1 0x006 #define R2057_GPAIO_SEL0 0x007 #define R2057_CLPO_CONFIG 0x008 #define R2057_BANDGAP_CONFIG 0x009 #define R2057_BANDGAP_RCAL_TRIM 0x00a #define R2057_AFEREG_CONFIG 0x00b #define R2057_TEMPSENSE_CONFIG 0x00c #define R2057_XTAL_CONFIG1 0x00d #define R2057_XTAL_ICORE_SIZE 0x00e #define R2057_XTAL_BUF_SIZE 0x00f #define R2057_XTAL_PULLCAP_SIZE 0x010 #define R2057_RFPLL_MASTER 0x011 #define R2057_VCOMONITOR_VTH_L 0x012 #define R2057_VCOMONITOR_VTH_H 0x013 #define R2057_VCOCAL_BIASRESET_RFPLLREG_VOUT 0x014 #define R2057_VCO_VARCSIZE_IDAC 0x015 #define R2057_VCOCAL_COUNTVAL0 0x016 #define R2057_VCOCAL_COUNTVAL1 0x017 #define R2057_VCOCAL_INTCLK_COUNT 0x018 #define R2057_VCOCAL_MASTER 0x019 #define R2057_VCOCAL_NUMCAPCHANGE 0x01a #define R2057_VCOCAL_WINSIZE 0x01b #define R2057_VCOCAL_DELAY_AFTER_REFRESH 0x01c #define R2057_VCOCAL_DELAY_AFTER_CLOSELOOP 0x01d #define R2057_VCOCAL_DELAY_AFTER_OPENLOOP 0x01e #define R2057_VCOCAL_DELAY_BEFORE_OPENLOOP 0x01f #define R2057_VCO_FORCECAPEN_FORCECAP1 0x020 #define R2057_VCO_FORCECAP0 0x021 #define R2057_RFPLL_REFMASTER_SPAREXTALSIZE 0x022 #define R2057_RFPLL_PFD_RESET_PW 0x023 #define R2057_RFPLL_LOOPFILTER_R2 0x024 #define R2057_RFPLL_LOOPFILTER_R1 0x025 #define R2057_RFPLL_LOOPFILTER_C3 0x026 #define R2057_RFPLL_LOOPFILTER_C2 0x027 #define R2057_RFPLL_LOOPFILTER_C1 0x028 #define R2057_CP_KPD_IDAC 0x029 #define R2057_RFPLL_IDACS 0x02a #define R2057_RFPLL_MISC_EN 0x02b #define R2057_RFPLL_MMD0 0x02c #define R2057_RFPLL_MMD1 0x02d #define R2057_RFPLL_MISC_CAL_RESETN 0x02e #define R2057_JTAGXTAL_SIZE_CPBIAS_FILTRES 0x02f #define R2057_VCO_ALCREF_BBPLLXTAL_SIZE 0x030 #define R2057_VCOCAL_READCAP0 0x031 #define R2057_VCOCAL_READCAP1 0x032 #define R2057_VCOCAL_STATUS 0x033 #define R2057_LOGEN_PUS 0x034 #define R2057_LOGEN_PTAT_RESETS 0x035 #define R2057_VCOBUF_IDACS 0x036 #define R2057_VCOBUF_TUNE 0x037 #define R2057_CMOSBUF_TX2GQ_IDACS 0x038 #define R2057_CMOSBUF_TX2GI_IDACS 0x039 #define R2057_CMOSBUF_TX5GQ_IDACS 0x03a #define R2057_CMOSBUF_TX5GI_IDACS 0x03b #define R2057_CMOSBUF_RX2GQ_IDACS 0x03c #define R2057_CMOSBUF_RX2GI_IDACS 0x03d #define R2057_CMOSBUF_RX5GQ_IDACS 0x03e #define R2057_CMOSBUF_RX5GI_IDACS 0x03f #define R2057_LOGEN_MX2G_IDACS 0x040 #define R2057_LOGEN_MX2G_TUNE 0x041 #define R2057_LOGEN_MX5G_IDACS 0x042 #define R2057_LOGEN_MX5G_TUNE 0x043 #define R2057_LOGEN_MX5G_RCCR 0x044 #define R2057_LOGEN_INDBUF2G_IDAC 0x045 #define R2057_LOGEN_INDBUF2G_IBOOST 0x046 #define R2057_LOGEN_INDBUF2G_TUNE 0x047 #define R2057_LOGEN_INDBUF5G_IDAC 0x048 #define R2057_LOGEN_INDBUF5G_IBOOST 0x049 #define R2057_LOGEN_INDBUF5G_TUNE 0x04a #define R2057_CMOSBUF_TX_RCCR 0x04b #define R2057_CMOSBUF_RX_RCCR 0x04c #define R2057_LOGEN_SEL_PKDET 0x04d #define R2057_CMOSBUF_SHAREIQ_PTAT 0x04e #define R2057_RXTXBIAS_CONFIG_CORE0 0x04f #define R2057_TXGM_TXRF_PUS_CORE0 0x050 #define R2057_TXGM_IDAC_BLEED_CORE0 0x051 #define R2057_TXGM_GAIN_CORE0 0x056 #define R2057_TXGM2G_PKDET_PUS_CORE0 0x057 #define R2057_PAD2G_PTATS_CORE0 0x058 #define R2057_PAD2G_IDACS_CORE0 0x059 #define R2057_PAD2G_BOOST_PU_CORE0 0x05a #define R2057_PAD2G_CASCV_GAIN_CORE0 0x05b #define R2057_TXMIX2G_TUNE_BOOST_PU_CORE0 0x05c #define R2057_TXMIX2G_LODC_CORE0 0x05d #define R2057_PAD2G_TUNE_PUS_CORE0 0x05e #define R2057_IPA2G_GAIN_CORE0 0x05f #define R2057_TSSI2G_SPARE1_CORE0 0x060 #define R2057_TSSI2G_SPARE2_CORE0 0x061 #define R2057_IPA2G_TUNEV_CASCV_PTAT_CORE0 0x062 #define R2057_IPA2G_IMAIN_CORE0 0x063 #define R2057_IPA2G_CASCONV_CORE0 0x064 #define R2057_IPA2G_CASCOFFV_CORE0 0x065 #define R2057_IPA2G_BIAS_FILTER_CORE0 0x066 #define R2057_TX5G_PKDET_CORE0 0x069 #define R2057_PGA_PTAT_TXGM5G_PU_CORE0 0x06a #define R2057_PAD5G_PTATS1_CORE0 0x06b #define R2057_PAD5G_CLASS_PTATS2_CORE0 0x06c #define R2057_PGA_BOOSTPTAT_IMAIN_CORE0 0x06d #define R2057_PAD5G_CASCV_IMAIN_CORE0 0x06e #define R2057_TXMIX5G_IBOOST_PAD_IAUX_CORE0 0x06f #define R2057_PGA_BOOST_TUNE_CORE0 0x070 #define R2057_PGA_GAIN_CORE0 0x071 #define R2057_PAD5G_CASCOFFV_GAIN_PUS_CORE0 0x072 #define R2057_TXMIX5G_BOOST_TUNE_CORE0 0x073 #define R2057_PAD5G_TUNE_MISC_PUS_CORE0 0x074 #define R2057_IPA5G_IAUX_CORE0 0x075 #define R2057_IPA5G_GAIN_CORE0 0x076 #define R2057_TSSI5G_SPARE1_CORE0 0x077 #define R2057_TSSI5G_SPARE2_CORE0 0x078 #define R2057_IPA5G_CASCOFFV_PU_CORE0 0x079 #define R2057_IPA5G_PTAT_CORE0 0x07a #define R2057_IPA5G_IMAIN_CORE0 0x07b #define R2057_IPA5G_CASCONV_CORE0 0x07c #define R2057_IPA5G_BIAS_FILTER_CORE0 0x07d #define R2057_PAD_BIAS_FILTER_BWS_CORE0 0x080 #define R2057_TR2G_CONFIG1_CORE0_NU 0x081 #define R2057_TR2G_CONFIG2_CORE0_NU 0x082 #define R2057_LNA5G_RFEN_CORE0 0x083 #define R2057_TR5G_CONFIG2_CORE0_NU 0x084 #define R2057_RXRFBIAS_IBOOST_PU_CORE0 0x085 #define R2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE0 0x086 #define R2057_RXGM_CMFBITAIL_AUXPTAT_CORE0 0x087 #define R2057_RXMIX_ICORE_RXGM_IAUX_CORE0 0x088 #define R2057_RXMIX_CMFBITAIL_PU_CORE0 0x089 #define R2057_LNA2_IMAIN_PTAT_PU_CORE0 0x08a #define R2057_LNA2_IAUX_PTAT_CORE0 0x08b #define R2057_LNA1_IMAIN_PTAT_PU_CORE0 0x08c #define R2057_LNA15G_INPUT_MATCH_TUNE_CORE0 0x08d #define R2057_RXRFBIAS_BANDSEL_CORE0 0x08e #define R2057_TIA_CONFIG_CORE0 0x08f #define R2057_TIA_IQGAIN_CORE0 0x090 #define R2057_TIA_IBIAS2_CORE0 0x091 #define R2057_TIA_IBIAS1_CORE0 0x092 #define R2057_TIA_SPARE_Q_CORE0 0x093 #define R2057_TIA_SPARE_I_CORE0 0x094 #define R2057_RXMIX2G_PUS_CORE0 0x095 #define R2057_RXMIX2G_VCMREFS_CORE0 0x096 #define R2057_RXMIX2G_LODC_QI_CORE0 0x097 #define R2057_W12G_BW_LNA2G_PUS_CORE0 0x098 #define R2057_LNA2G_GAIN_CORE0 0x099 #define R2057_LNA2G_TUNE_CORE0 0x09a #define R2057_RXMIX5G_PUS_CORE0 0x09b #define R2057_RXMIX5G_VCMREFS_CORE0 0x09c #define R2057_RXMIX5G_LODC_QI_CORE0 0x09d #define R2057_W15G_BW_LNA5G_PUS_CORE0 0x09e #define R2057_LNA5G_GAIN_CORE0 0x09f #define R2057_LNA5G_TUNE_CORE0 0x0a0 #define R2057_LPFSEL_TXRX_RXBB_PUS_CORE0 0x0a1 #define R2057_RXBB_BIAS_MASTER_CORE0 0x0a2 #define R2057_RXBB_VGABUF_IDACS_CORE0 0x0a3 #define R2057_LPF_VCMREF_TXBUF_VCMREF_CORE0 0x0a4 #define R2057_TXBUF_VINCM_CORE0 0x0a5 #define R2057_TXBUF_IDACS_CORE0 0x0a6 #define R2057_LPF_RESP_RXBUF_BW_CORE0 0x0a7 #define R2057_RXBB_CC_CORE0 0x0a8 #define R2057_RXBB_SPARE3_CORE0 0x0a9 #define R2057_RXBB_RCCAL_HPC_CORE0 0x0aa #define R2057_LPF_IDACS_CORE0 0x0ab #define R2057_LPFBYP_DCLOOP_BYP_IDAC_CORE0 0x0ac #define R2057_TXBUF_GAIN_CORE0 0x0ad #define R2057_AFELOOPBACK_AACI_RESP_CORE0 0x0ae #define R2057_RXBUF_DEGEN_CORE0 0x0af #define R2057_RXBB_SPARE2_CORE0 0x0b0 #define R2057_RXBB_SPARE1_CORE0 0x0b1 #define R2057_RSSI_MASTER_CORE0 0x0b2 #define R2057_W2_MASTER_CORE0 0x0b3 #define R2057_NB_MASTER_CORE0 0x0b4 #define R2057_W2_IDACS0_Q_CORE0 0x0b5 #define R2057_W2_IDACS1_Q_CORE0 0x0b6 #define R2057_W2_IDACS0_I_CORE0 0x0b7 #define R2057_W2_IDACS1_I_CORE0 0x0b8 #define R2057_RSSI_GPAIOSEL_W1_IDACS_CORE0 0x0b9 #define R2057_NB_IDACS_Q_CORE0 0x0ba #define R2057_NB_IDACS_I_CORE0 0x0bb #define R2057_BACKUP4_CORE0 0x0c1 #define R2057_BACKUP3_CORE0 0x0c2 #define R2057_BACKUP2_CORE0 0x0c3 #define R2057_BACKUP1_CORE0 0x0c4 #define R2057_SPARE16_CORE0 0x0c5 #define R2057_SPARE15_CORE0 0x0c6 #define R2057_SPARE14_CORE0 0x0c7 #define R2057_SPARE13_CORE0 0x0c8 #define R2057_SPARE12_CORE0 0x0c9 #define R2057_SPARE11_CORE0 0x0ca #define R2057_TX2G_BIAS_RESETS_CORE0 0x0cb #define R2057_TX5G_BIAS_RESETS_CORE0 0x0cc #define R2057_IQTEST_SEL_PU 0x0cd #define R2057_XTAL_CONFIG2 0x0ce #define R2057_BUFS_MISC_LPFBW_CORE0 0x0cf #define R2057_TXLPF_RCCAL_CORE0 0x0d0 #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0 0x0d1 #define R2057_LPF_GAIN_CORE0 0x0d2 #define R2057_DACBUF_IDACS_BW_CORE0 0x0d3 #define R2057_RXTXBIAS_CONFIG_CORE1 0x0d4 #define R2057_TXGM_TXRF_PUS_CORE1 0x0d5 #define R2057_TXGM_IDAC_BLEED_CORE1 0x0d6 #define R2057_TXGM_GAIN_CORE1 0x0db #define R2057_TXGM2G_PKDET_PUS_CORE1 0x0dc #define R2057_PAD2G_PTATS_CORE1 0x0dd #define R2057_PAD2G_IDACS_CORE1 0x0de #define R2057_PAD2G_BOOST_PU_CORE1 0x0df #define R2057_PAD2G_CASCV_GAIN_CORE1 0x0e0 #define R2057_TXMIX2G_TUNE_BOOST_PU_CORE1 0x0e1 #define R2057_TXMIX2G_LODC_CORE1 0x0e2 #define R2057_PAD2G_TUNE_PUS_CORE1 0x0e3 #define R2057_IPA2G_GAIN_CORE1 0x0e4 #define R2057_TSSI2G_SPARE1_CORE1 0x0e5 #define R2057_TSSI2G_SPARE2_CORE1 0x0e6 #define R2057_IPA2G_TUNEV_CASCV_PTAT_CORE1 0x0e7 #define R2057_IPA2G_IMAIN_CORE1 0x0e8 #define R2057_IPA2G_CASCONV_CORE1 0x0e9 #define R2057_IPA2G_CASCOFFV_CORE1 0x0ea #define R2057_IPA2G_BIAS_FILTER_CORE1 0x0eb #define R2057_TX5G_PKDET_CORE1 0x0ee #define R2057_PGA_PTAT_TXGM5G_PU_CORE1 0x0ef #define R2057_PAD5G_PTATS1_CORE1 0x0f0 #define R2057_PAD5G_CLASS_PTATS2_CORE1 0x0f1 #define R2057_PGA_BOOSTPTAT_IMAIN_CORE1 0x0f2 #define R2057_PAD5G_CASCV_IMAIN_CORE1 0x0f3 #define R2057_TXMIX5G_IBOOST_PAD_IAUX_CORE1 0x0f4 #define R2057_PGA_BOOST_TUNE_CORE1 0x0f5 #define R2057_PGA_GAIN_CORE1 0x0f6 #define R2057_PAD5G_CASCOFFV_GAIN_PUS_CORE1 0x0f7 #define R2057_TXMIX5G_BOOST_TUNE_CORE1 0x0f8 #define R2057_PAD5G_TUNE_MISC_PUS_CORE1 0x0f9 #define R2057_IPA5G_IAUX_CORE1 0x0fa #define R2057_IPA5G_GAIN_CORE1 0x0fb #define R2057_TSSI5G_SPARE1_CORE1 0x0fc #define R2057_TSSI5G_SPARE2_CORE1 0x0fd #define R2057_IPA5G_CASCOFFV_PU_CORE1 0x0fe #define R2057_IPA5G_PTAT_CORE1 0x0ff #define R2057_IPA5G_IMAIN_CORE1 0x100 #define R2057_IPA5G_CASCONV_CORE1 0x101 #define R2057_IPA5G_BIAS_FILTER_CORE1 0x102 #define R2057_PAD_BIAS_FILTER_BWS_CORE1 0x105 #define R2057_TR2G_CONFIG1_CORE1_NU 0x106 #define R2057_TR2G_CONFIG2_CORE1_NU 0x107 #define R2057_LNA5G_RFEN_CORE1 0x108 #define R2057_TR5G_CONFIG2_CORE1_NU 0x109 #define R2057_RXRFBIAS_IBOOST_PU_CORE1 0x10a #define R2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE1 0x10b #define R2057_RXGM_CMFBITAIL_AUXPTAT_CORE1 0x10c #define R2057_RXMIX_ICORE_RXGM_IAUX_CORE1 0x10d #define R2057_RXMIX_CMFBITAIL_PU_CORE1 0x10e #define R2057_LNA2_IMAIN_PTAT_PU_CORE1 0x10f #define R2057_LNA2_IAUX_PTAT_CORE1 0x110 #define R2057_LNA1_IMAIN_PTAT_PU_CORE1 0x111 #define R2057_LNA15G_INPUT_MATCH_TUNE_CORE1 0x112 #define R2057_RXRFBIAS_BANDSEL_CORE1 0x113 #define R2057_TIA_CONFIG_CORE1 0x114 #define R2057_TIA_IQGAIN_CORE1 0x115 #define R2057_TIA_IBIAS2_CORE1 0x116 #define R2057_TIA_IBIAS1_CORE1 0x117 #define R2057_TIA_SPARE_Q_CORE1 0x118 #define R2057_TIA_SPARE_I_CORE1 0x119 #define R2057_RXMIX2G_PUS_CORE1 0x11a #define R2057_RXMIX2G_VCMREFS_CORE1 0x11b #define R2057_RXMIX2G_LODC_QI_CORE1 0x11c #define R2057_W12G_BW_LNA2G_PUS_CORE1 0x11d #define R2057_LNA2G_GAIN_CORE1 0x11e #define R2057_LNA2G_TUNE_CORE1 0x11f #define R2057_RXMIX5G_PUS_CORE1 0x120 #define R2057_RXMIX5G_VCMREFS_CORE1 0x121 #define R2057_RXMIX5G_LODC_QI_CORE1 0x122 #define R2057_W15G_BW_LNA5G_PUS_CORE1 0x123 #define R2057_LNA5G_GAIN_CORE1 0x124 #define R2057_LNA5G_TUNE_CORE1 0x125 #define R2057_LPFSEL_TXRX_RXBB_PUS_CORE1 0x126 #define R2057_RXBB_BIAS_MASTER_CORE1 0x127 #define R2057_RXBB_VGABUF_IDACS_CORE1 0x128 #define R2057_LPF_VCMREF_TXBUF_VCMREF_CORE1 0x129 #define R2057_TXBUF_VINCM_CORE1 0x12a #define R2057_TXBUF_IDACS_CORE1 0x12b #define R2057_LPF_RESP_RXBUF_BW_CORE1 0x12c #define R2057_RXBB_CC_CORE1 0x12d #define R2057_RXBB_SPARE3_CORE1 0x12e #define R2057_RXBB_RCCAL_HPC_CORE1 0x12f #define R2057_LPF_IDACS_CORE1 0x130 #define R2057_LPFBYP_DCLOOP_BYP_IDAC_CORE1 0x131 #define R2057_TXBUF_GAIN_CORE1 0x132 #define R2057_AFELOOPBACK_AACI_RESP_CORE1 0x133 #define R2057_RXBUF_DEGEN_CORE1 0x134 #define R2057_RXBB_SPARE2_CORE1 0x135 #define R2057_RXBB_SPARE1_CORE1 0x136 #define R2057_RSSI_MASTER_CORE1 0x137 #define R2057_W2_MASTER_CORE1 0x138 #define R2057_NB_MASTER_CORE1 0x139 #define R2057_W2_IDACS0_Q_CORE1 0x13a #define R2057_W2_IDACS1_Q_CORE1 0x13b #define R2057_W2_IDACS0_I_CORE1 0x13c #define R2057_W2_IDACS1_I_CORE1 0x13d #define R2057_RSSI_GPAIOSEL_W1_IDACS_CORE1 0x13e #define R2057_NB_IDACS_Q_CORE1 0x13f #define R2057_NB_IDACS_I_CORE1 0x140 #define R2057_BACKUP4_CORE1 0x146 #define R2057_BACKUP3_CORE1 0x147 #define R2057_BACKUP2_CORE1 0x148 #define R2057_BACKUP1_CORE1 0x149 #define R2057_SPARE16_CORE1 0x14a #define R2057_SPARE15_CORE1 0x14b #define R2057_SPARE14_CORE1 0x14c #define R2057_SPARE13_CORE1 0x14d #define R2057_SPARE12_CORE1 0x14e #define R2057_SPARE11_CORE1 0x14f #define R2057_TX2G_BIAS_RESETS_CORE1 0x150 #define R2057_TX5G_BIAS_RESETS_CORE1 0x151 #define R2057_SPARE8_CORE1 0x152 #define R2057_SPARE7_CORE1 0x153 #define R2057_BUFS_MISC_LPFBW_CORE1 0x154 #define R2057_TXLPF_RCCAL_CORE1 0x155 #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1 0x156 #define R2057_LPF_GAIN_CORE1 0x157 #define R2057_DACBUF_IDACS_BW_CORE1 0x158 #define R2057_DACBUF_VINCM_CORE1 0x159 #define R2057_RCCAL_START_R1_Q1_P1 0x15a #define R2057_RCCAL_X1 0x15b #define R2057_RCCAL_TRC0 0x15c #define R2057_RCCAL_TRC1 0x15d #define R2057_RCCAL_DONE_OSCCAP 0x15e #define R2057_RCCAL_N0_0 0x15f #define R2057_RCCAL_N0_1 0x160 #define R2057_RCCAL_N1_0 0x161 #define R2057_RCCAL_N1_1 0x162 #define R2057_RCAL_STATUS 0x163 #define R2057_XTALPUOVR_PINCTRL 0x164 #define R2057_OVR_REG0 0x165 #define R2057_OVR_REG1 0x166 #define R2057_OVR_REG2 0x167 #define R2057_OVR_REG3 0x168 #define R2057_OVR_REG4 0x169 #define R2057_RCCAL_SCAP_VAL 0x16a #define R2057_RCCAL_BCAP_VAL 0x16b #define R2057_RCCAL_HPC_VAL 0x16c #define R2057_RCCAL_OVERRIDES 0x16d #define R2057_TX0_IQCAL_GAIN_BW 0x170 #define R2057_TX0_LOFT_FINE_I 0x171 #define R2057_TX0_LOFT_FINE_Q 0x172 #define R2057_TX0_LOFT_COARSE_I 0x173 #define R2057_TX0_LOFT_COARSE_Q 0x174 #define R2057_TX0_TX_SSI_MASTER 0x175 #define R2057_TX0_IQCAL_VCM_HG 0x176 #define R2057_TX0_IQCAL_IDAC 0x177 #define R2057_TX0_TSSI_VCM 0x178 #define R2057_TX0_TX_SSI_MUX 0x179 #define R2057_TX0_TSSIA 0x17a #define R2057_TX0_TSSIG 0x17b #define R2057_TX0_TSSI_MISC1 0x17c #define R2057_TX0_TXRXCOUPLE_2G_ATTEN 0x17d #define R2057_TX0_TXRXCOUPLE_2G_PWRUP 0x17e #define R2057_TX0_TXRXCOUPLE_5G_ATTEN 0x17f #define R2057_TX0_TXRXCOUPLE_5G_PWRUP 0x180 #define R2057_TX1_IQCAL_GAIN_BW 0x190 #define R2057_TX1_LOFT_FINE_I 0x191 #define R2057_TX1_LOFT_FINE_Q 0x192 #define R2057_TX1_LOFT_COARSE_I 0x193 #define R2057_TX1_LOFT_COARSE_Q 0x194 #define R2057_TX1_TX_SSI_MASTER 0x195 #define R2057_TX1_IQCAL_VCM_HG 0x196 #define R2057_TX1_IQCAL_IDAC 0x197 #define R2057_TX1_TSSI_VCM 0x198 #define R2057_TX1_TX_SSI_MUX 0x199 #define R2057_TX1_TSSIA 0x19a #define R2057_TX1_TSSIG 0x19b #define R2057_TX1_TSSI_MISC1 0x19c #define R2057_TX1_TXRXCOUPLE_2G_ATTEN 0x19d #define R2057_TX1_TXRXCOUPLE_2G_PWRUP 0x19e #define R2057_TX1_TXRXCOUPLE_5G_ATTEN 0x19f #define R2057_TX1_TXRXCOUPLE_5G_PWRUP 0x1a0 #define R2057_AFE_VCM_CAL_MASTER_CORE0 0x1a1 #define R2057_AFE_SET_VCM_I_CORE0 0x1a2 #define R2057_AFE_SET_VCM_Q_CORE0 0x1a3 #define R2057_AFE_STATUS_VCM_IQADC_CORE0 0x1a4 #define R2057_AFE_STATUS_VCM_I_CORE0 0x1a5 #define R2057_AFE_STATUS_VCM_Q_CORE0 0x1a6 #define R2057_AFE_VCM_CAL_MASTER_CORE1 0x1a7 #define R2057_AFE_SET_VCM_I_CORE1 0x1a8 #define R2057_AFE_SET_VCM_Q_CORE1 0x1a9 #define R2057_AFE_STATUS_VCM_IQADC_CORE1 0x1aa #define R2057_AFE_STATUS_VCM_I_CORE1 0x1ab #define R2057_AFE_STATUS_VCM_Q_CORE1 0x1ac #define R2057v7_DACBUF_VINCM_CORE0 0x1ad #define R2057v7_RCCAL_MASTER 0x1ae #define R2057v7_TR2G_CONFIG3_CORE0_NU 0x1af #define R2057v7_TR2G_CONFIG3_CORE1_NU 0x1b0 #define R2057v7_LOGEN_PUS1 0x1b1 #define R2057v7_OVR_REG5 0x1b2 #define R2057v7_OVR_REG6 0x1b3 #define R2057v7_OVR_REG7 0x1b4 #define R2057v7_OVR_REG8 0x1b5 #define R2057v7_OVR_REG9 0x1b6 #define R2057v7_OVR_REG10 0x1b7 #define R2057v7_OVR_REG11 0x1b8 #define R2057v7_OVR_REG12 0x1b9 #define R2057v7_OVR_REG13 0x1ba #define R2057v7_OVR_REG14 0x1bb #define R2057v7_OVR_REG15 0x1bc #define R2057v7_OVR_REG16 0x1bd #define R2057v7_OVR_REG1 0x1be #define R2057v7_OVR_REG18 0x1bf #define R2057v7_OVR_REG19 0x1c0 #define R2057v7_OVR_REG20 0x1c1 #define R2057v7_OVR_REG21 0x1c2 #define R2057v7_OVR_REG2 0x1c3 #define R2057v7_OVR_REG23 0x1c4 #define R2057v7_OVR_REG24 0x1c5 #define R2057v7_OVR_REG25 0x1c6 #define R2057v7_OVR_REG26 0x1c7 #define R2057v7_OVR_REG27 0x1c8 #define R2057v7_OVR_REG28 0x1c9 #define R2057v7_IQTEST_SEL_PU2 0x1ca #define R2057_VCM_MASK 0x7 void r2057_upload_inittabs(struct b43_wldev *dev); #endif /* B43_RADIO_2057_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2057.c0000644000175000017500000001355312026211315022777 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n 2057 radio device data tables Copyright (c) 2010 Rafał Miłecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "radio_2057.h" #include "phy_common.h" static u16 r2057_rev4_init[42][2] = { { 0x0E, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x35, 0x26 }, { 0x3C, 0xff }, { 0x3D, 0xff }, { 0x3E, 0xff }, { 0x3F, 0xff }, { 0x62, 0x33 }, { 0x8A, 0xf0 }, { 0x8B, 0x10 }, { 0x8C, 0xf0 }, { 0x91, 0x3f }, { 0x92, 0x36 }, { 0xA4, 0x8c }, { 0xA8, 0x55 }, { 0xAF, 0x01 }, { 0x10F, 0xf0 }, { 0x110, 0x10 }, { 0x111, 0xf0 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x129, 0x8c }, { 0x12D, 0x55 }, { 0x134, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x169, 0x02 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, }; static u16 r2057_rev5_init[44][2] = { { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, { 0xA1, 0x20 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 }, }; static u16 r2057_rev5a_init[45][2] = { { 0x00, 0x15 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x14E, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 }, }; static u16 r2057_rev7_init[54][2] = { { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x13 }, { 0x66, 0xee }, { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, { 0x7C, 0x14 }, { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, { 0xA1, 0x20 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x13 }, { 0xEB, 0xee }, { 0xF3, 0x58 }, { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x14 }, { 0x102, 0xee }, { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; static u16 r2057_rev8_init[54][2] = { { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, { 0x7C, 0x0f }, { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, { 0xA1, 0x20 }, { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0xF3, 0x58 }, { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x0f }, { 0x102, 0xee }, { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x14E, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; void r2057_upload_inittabs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u16 *table = NULL; u16 size, i; if (phy->rev == 7) { table = r2057_rev4_init[0]; size = ARRAY_SIZE(r2057_rev4_init); } else if (phy->rev == 8 || phy->rev == 9) { if (phy->radio_rev == 5) { if (phy->radio_rev == 8) { table = r2057_rev5_init[0]; size = ARRAY_SIZE(r2057_rev5_init); } else { table = r2057_rev5a_init[0]; size = ARRAY_SIZE(r2057_rev5a_init); } } else if (phy->radio_rev == 7) { table = r2057_rev7_init[0]; size = ARRAY_SIZE(r2057_rev7_init); } else if (phy->radio_rev == 9) { table = r2057_rev8_init[0]; size = ARRAY_SIZE(r2057_rev8_init); } } if (table) { for (i = 0; i < 10; i++) { pr_info("radio_write 0x%X ", *table); table++; pr_info("0x%X\n", *table); table++; } } } compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2056.h0000644000175000017500000011405412026211315023001 0ustar mcgrofmcgrof#ifndef B43_RADIO_2056_H_ #define B43_RADIO_2056_H_ #include #include "tables_nphy.h" #define B2056_SYN (0x0 << 12) #define B2056_TX0 (0x2 << 12) #define B2056_TX1 (0x3 << 12) #define B2056_RX0 (0x6 << 12) #define B2056_RX1 (0x7 << 12) #define B2056_ALLTX (0xE << 12) #define B2056_ALLRX (0xF << 12) #define B2056_SYN_RESERVED_ADDR0 0x00 #define B2056_SYN_IDCODE 0x01 #define B2056_SYN_RESERVED_ADDR2 0x02 #define B2056_SYN_RESERVED_ADDR3 0x03 #define B2056_SYN_RESERVED_ADDR4 0x04 #define B2056_SYN_RESERVED_ADDR5 0x05 #define B2056_SYN_RESERVED_ADDR6 0x06 #define B2056_SYN_RESERVED_ADDR7 0x07 #define B2056_SYN_COM_CTRL 0x08 #define B2056_SYN_COM_PU 0x09 #define B2056_SYN_COM_OVR 0x0A #define B2056_SYN_COM_RESET 0x0B #define B2056_SYN_COM_RCAL 0x0C #define B2056_SYN_COM_RC_RXLPF 0x0D #define B2056_SYN_COM_RC_TXLPF 0x0E #define B2056_SYN_COM_RC_RXHPF 0x0F #define B2056_SYN_RESERVED_ADDR16 0x10 #define B2056_SYN_RESERVED_ADDR17 0x11 #define B2056_SYN_RESERVED_ADDR18 0x12 #define B2056_SYN_RESERVED_ADDR19 0x13 #define B2056_SYN_RESERVED_ADDR20 0x14 #define B2056_SYN_RESERVED_ADDR21 0x15 #define B2056_SYN_RESERVED_ADDR22 0x16 #define B2056_SYN_RESERVED_ADDR23 0x17 #define B2056_SYN_RESERVED_ADDR24 0x18 #define B2056_SYN_RESERVED_ADDR25 0x19 #define B2056_SYN_RESERVED_ADDR26 0x1A #define B2056_SYN_RESERVED_ADDR27 0x1B #define B2056_SYN_RESERVED_ADDR28 0x1C #define B2056_SYN_RESERVED_ADDR29 0x1D #define B2056_SYN_RESERVED_ADDR30 0x1E #define B2056_SYN_RESERVED_ADDR31 0x1F #define B2056_SYN_GPIO_MASTER1 0x20 #define B2056_SYN_GPIO_MASTER2 0x21 #define B2056_SYN_TOPBIAS_MASTER 0x22 #define B2056_SYN_TOPBIAS_RCAL 0x23 #define B2056_SYN_AFEREG 0x24 #define B2056_SYN_TEMPPROCSENSE 0x25 #define B2056_SYN_TEMPPROCSENSEIDAC 0x26 #define B2056_SYN_TEMPPROCSENSERCAL 0x27 #define B2056_SYN_LPO 0x28 #define B2056_SYN_VDDCAL_MASTER 0x29 #define B2056_SYN_VDDCAL_IDAC 0x2A #define B2056_SYN_VDDCAL_STATUS 0x2B #define B2056_SYN_RCAL_MASTER 0x2C #define B2056_SYN_RCAL_CODE_OUT 0x2D #define B2056_SYN_RCCAL_CTRL0 0x2E #define B2056_SYN_RCCAL_CTRL1 0x2F #define B2056_SYN_RCCAL_CTRL2 0x30 #define B2056_SYN_RCCAL_CTRL3 0x31 #define B2056_SYN_RCCAL_CTRL4 0x32 #define B2056_SYN_RCCAL_CTRL5 0x33 #define B2056_SYN_RCCAL_CTRL6 0x34 #define B2056_SYN_RCCAL_CTRL7 0x35 #define B2056_SYN_RCCAL_CTRL8 0x36 #define B2056_SYN_RCCAL_CTRL9 0x37 #define B2056_SYN_RCCAL_CTRL10 0x38 #define B2056_SYN_RCCAL_CTRL11 0x39 #define B2056_SYN_ZCAL_SPARE1 0x3A #define B2056_SYN_ZCAL_SPARE2 0x3B #define B2056_SYN_PLL_MAST1 0x3C #define B2056_SYN_PLL_MAST2 0x3D #define B2056_SYN_PLL_MAST3 0x3E #define B2056_SYN_PLL_BIAS_RESET 0x3F #define B2056_SYN_PLL_XTAL0 0x40 #define B2056_SYN_PLL_XTAL1 0x41 #define B2056_SYN_PLL_XTAL3 0x42 #define B2056_SYN_PLL_XTAL4 0x43 #define B2056_SYN_PLL_XTAL5 0x44 #define B2056_SYN_PLL_XTAL6 0x45 #define B2056_SYN_PLL_REFDIV 0x46 #define B2056_SYN_PLL_PFD 0x47 #define B2056_SYN_PLL_CP1 0x48 #define B2056_SYN_PLL_CP2 0x49 #define B2056_SYN_PLL_CP3 0x4A #define B2056_SYN_PLL_LOOPFILTER1 0x4B #define B2056_SYN_PLL_LOOPFILTER2 0x4C #define B2056_SYN_PLL_LOOPFILTER3 0x4D #define B2056_SYN_PLL_LOOPFILTER4 0x4E #define B2056_SYN_PLL_LOOPFILTER5 0x4F #define B2056_SYN_PLL_MMD1 0x50 #define B2056_SYN_PLL_MMD2 0x51 #define B2056_SYN_PLL_VCO1 0x52 #define B2056_SYN_PLL_VCO2 0x53 #define B2056_SYN_PLL_MONITOR1 0x54 #define B2056_SYN_PLL_MONITOR2 0x55 #define B2056_SYN_PLL_VCOCAL1 0x56 #define B2056_SYN_PLL_VCOCAL2 0x57 #define B2056_SYN_PLL_VCOCAL4 0x58 #define B2056_SYN_PLL_VCOCAL5 0x59 #define B2056_SYN_PLL_VCOCAL6 0x5A #define B2056_SYN_PLL_VCOCAL7 0x5B #define B2056_SYN_PLL_VCOCAL8 0x5C #define B2056_SYN_PLL_VCOCAL9 0x5D #define B2056_SYN_PLL_VCOCAL10 0x5E #define B2056_SYN_PLL_VCOCAL11 0x5F #define B2056_SYN_PLL_VCOCAL12 0x60 #define B2056_SYN_PLL_VCOCAL13 0x61 #define B2056_SYN_PLL_VREG 0x62 #define B2056_SYN_PLL_STATUS1 0x63 #define B2056_SYN_PLL_STATUS2 0x64 #define B2056_SYN_PLL_STATUS3 0x65 #define B2056_SYN_LOGEN_PU0 0x66 #define B2056_SYN_LOGEN_PU1 0x67 #define B2056_SYN_LOGEN_PU2 0x68 #define B2056_SYN_LOGEN_PU3 0x69 #define B2056_SYN_LOGEN_PU5 0x6A #define B2056_SYN_LOGEN_PU6 0x6B #define B2056_SYN_LOGEN_PU7 0x6C #define B2056_SYN_LOGEN_PU8 0x6D #define B2056_SYN_LOGEN_BIAS_RESET 0x6E #define B2056_SYN_LOGEN_RCCR1 0x6F #define B2056_SYN_LOGEN_VCOBUF1 0x70 #define B2056_SYN_LOGEN_MIXER1 0x71 #define B2056_SYN_LOGEN_MIXER2 0x72 #define B2056_SYN_LOGEN_BUF1 0x73 #define B2056_SYN_LOGENBUF2 0x74 #define B2056_SYN_LOGEN_BUF3 0x75 #define B2056_SYN_LOGEN_BUF4 0x76 #define B2056_SYN_LOGEN_DIV1 0x77 #define B2056_SYN_LOGEN_DIV2 0x78 #define B2056_SYN_LOGEN_DIV3 0x79 #define B2056_SYN_LOGEN_ACL1 0x7A #define B2056_SYN_LOGEN_ACL2 0x7B #define B2056_SYN_LOGEN_ACL3 0x7C #define B2056_SYN_LOGEN_ACL4 0x7D #define B2056_SYN_LOGEN_ACL5 0x7E #define B2056_SYN_LOGEN_ACL6 0x7F #define B2056_SYN_LOGEN_ACLOUT 0x80 #define B2056_SYN_LOGEN_ACLCAL1 0x81 #define B2056_SYN_LOGEN_ACLCAL2 0x82 #define B2056_SYN_LOGEN_ACLCAL3 0x83 #define B2056_SYN_CALEN 0x84 #define B2056_SYN_LOGEN_PEAKDET1 0x85 #define B2056_SYN_LOGEN_CORE_ACL_OVR 0x86 #define B2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 #define B2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 #define B2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 #define B2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8A #define B2056_SYN_LOGEN_VCOBUF2 0x8B #define B2056_SYN_LOGEN_MIXER3 0x8C #define B2056_SYN_LOGEN_BUF5 0x8D #define B2056_SYN_LOGEN_BUF6 0x8E #define B2056_SYN_LOGEN_CBUFRX1 0x8F #define B2056_SYN_LOGEN_CBUFRX2 0x90 #define B2056_SYN_LOGEN_CBUFRX3 0x91 #define B2056_SYN_LOGEN_CBUFRX4 0x92 #define B2056_SYN_LOGEN_CBUFTX1 0x93 #define B2056_SYN_LOGEN_CBUFTX2 0x94 #define B2056_SYN_LOGEN_CBUFTX3 0x95 #define B2056_SYN_LOGEN_CBUFTX4 0x96 #define B2056_SYN_LOGEN_CMOSRX1 0x97 #define B2056_SYN_LOGEN_CMOSRX2 0x98 #define B2056_SYN_LOGEN_CMOSRX3 0x99 #define B2056_SYN_LOGEN_CMOSRX4 0x9A #define B2056_SYN_LOGEN_CMOSTX1 0x9B #define B2056_SYN_LOGEN_CMOSTX2 0x9C #define B2056_SYN_LOGEN_CMOSTX3 0x9D #define B2056_SYN_LOGEN_CMOSTX4 0x9E #define B2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9F #define B2056_SYN_LOGEN_MIXER3_OVRVAL 0xA0 #define B2056_SYN_LOGEN_BUF5_OVRVAL 0xA1 #define B2056_SYN_LOGEN_BUF6_OVRVAL 0xA2 #define B2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xA3 #define B2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xA4 #define B2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xA5 #define B2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xA6 #define B2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xA7 #define B2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xA8 #define B2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xA9 #define B2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xAA #define B2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xAB #define B2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xAC #define B2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xAD #define B2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xAE #define B2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xAF #define B2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xB0 #define B2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xB1 #define B2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xB2 #define B2056_SYN_LOGEN_ACL_WAITCNT 0xB3 #define B2056_SYN_LOGEN_CORE_CALVALID 0xB4 #define B2056_SYN_LOGEN_RX_CMOS_CALVALID 0xB5 #define B2056_SYN_LOGEN_TX_CMOS_VALID 0xB6 #define B2056_TX_RESERVED_ADDR0 0x00 #define B2056_TX_IDCODE 0x01 #define B2056_TX_RESERVED_ADDR2 0x02 #define B2056_TX_RESERVED_ADDR3 0x03 #define B2056_TX_RESERVED_ADDR4 0x04 #define B2056_TX_RESERVED_ADDR5 0x05 #define B2056_TX_RESERVED_ADDR6 0x06 #define B2056_TX_RESERVED_ADDR7 0x07 #define B2056_TX_COM_CTRL 0x08 #define B2056_TX_COM_PU 0x09 #define B2056_TX_COM_OVR 0x0A #define B2056_TX_COM_RESET 0x0B #define B2056_TX_COM_RCAL 0x0C #define B2056_TX_COM_RC_RXLPF 0x0D #define B2056_TX_COM_RC_TXLPF 0x0E #define B2056_TX_COM_RC_RXHPF 0x0F #define B2056_TX_RESERVED_ADDR16 0x10 #define B2056_TX_RESERVED_ADDR17 0x11 #define B2056_TX_RESERVED_ADDR18 0x12 #define B2056_TX_RESERVED_ADDR19 0x13 #define B2056_TX_RESERVED_ADDR20 0x14 #define B2056_TX_RESERVED_ADDR21 0x15 #define B2056_TX_RESERVED_ADDR22 0x16 #define B2056_TX_RESERVED_ADDR23 0x17 #define B2056_TX_RESERVED_ADDR24 0x18 #define B2056_TX_RESERVED_ADDR25 0x19 #define B2056_TX_RESERVED_ADDR26 0x1A #define B2056_TX_RESERVED_ADDR27 0x1B #define B2056_TX_RESERVED_ADDR28 0x1C #define B2056_TX_RESERVED_ADDR29 0x1D #define B2056_TX_RESERVED_ADDR30 0x1E #define B2056_TX_RESERVED_ADDR31 0x1F #define B2056_TX_IQCAL_GAIN_BW 0x20 #define B2056_TX_LOFT_FINE_I 0x21 #define B2056_TX_LOFT_FINE_Q 0x22 #define B2056_TX_LOFT_COARSE_I 0x23 #define B2056_TX_LOFT_COARSE_Q 0x24 #define B2056_TX_TX_COM_MASTER1 0x25 #define B2056_TX_TX_COM_MASTER2 0x26 #define B2056_TX_RXIQCAL_TXMUX 0x27 #define B2056_TX_TX_SSI_MASTER 0x28 #define B2056_TX_IQCAL_VCM_HG 0x29 #define B2056_TX_IQCAL_IDAC 0x2A #define B2056_TX_TSSI_VCM 0x2B #define B2056_TX_TX_AMP_DET 0x2C #define B2056_TX_TX_SSI_MUX 0x2D #define B2056_TX_TSSIA 0x2E #define B2056_TX_TSSIG 0x2F #define B2056_TX_TSSI_MISC1 0x30 #define B2056_TX_TSSI_MISC2 0x31 #define B2056_TX_TSSI_MISC3 0x32 #define B2056_TX_PA_SPARE1 0x33 #define B2056_TX_PA_SPARE2 0x34 #define B2056_TX_INTPAA_MASTER 0x35 #define B2056_TX_INTPAA_GAIN 0x36 #define B2056_TX_INTPAA_BOOST_TUNE 0x37 #define B2056_TX_INTPAA_IAUX_STAT 0x38 #define B2056_TX_INTPAA_IAUX_DYN 0x39 #define B2056_TX_INTPAA_IMAIN_STAT 0x3A #define B2056_TX_INTPAA_IMAIN_DYN 0x3B #define B2056_TX_INTPAA_CASCBIAS 0x3C #define B2056_TX_INTPAA_PASLOPE 0x3D #define B2056_TX_INTPAA_PA_MISC 0x3E #define B2056_TX_INTPAG_MASTER 0x3F #define B2056_TX_INTPAG_GAIN 0x40 #define B2056_TX_INTPAG_BOOST_TUNE 0x41 #define B2056_TX_INTPAG_IAUX_STAT 0x42 #define B2056_TX_INTPAG_IAUX_DYN 0x43 #define B2056_TX_INTPAG_IMAIN_STAT 0x44 #define B2056_TX_INTPAG_IMAIN_DYN 0x45 #define B2056_TX_INTPAG_CASCBIAS 0x46 #define B2056_TX_INTPAG_PASLOPE 0x47 #define B2056_TX_INTPAG_PA_MISC 0x48 #define B2056_TX_PADA_MASTER 0x49 #define B2056_TX_PADA_IDAC 0x4A #define B2056_TX_PADA_CASCBIAS 0x4B #define B2056_TX_PADA_GAIN 0x4C #define B2056_TX_PADA_BOOST_TUNE 0x4D #define B2056_TX_PADA_SLOPE 0x4E #define B2056_TX_PADG_MASTER 0x4F #define B2056_TX_PADG_IDAC 0x50 #define B2056_TX_PADG_CASCBIAS 0x51 #define B2056_TX_PADG_GAIN 0x52 #define B2056_TX_PADG_BOOST_TUNE 0x53 #define B2056_TX_PADG_SLOPE 0x54 #define B2056_TX_PGAA_MASTER 0x55 #define B2056_TX_PGAA_IDAC 0x56 #define B2056_TX_PGAA_GAIN 0x57 #define B2056_TX_PGAA_BOOST_TUNE 0x58 #define B2056_TX_PGAA_SLOPE 0x59 #define B2056_TX_PGAA_MISC 0x5A #define B2056_TX_PGAG_MASTER 0x5B #define B2056_TX_PGAG_IDAC 0x5C #define B2056_TX_PGAG_GAIN 0x5D #define B2056_TX_PGAG_BOOST_TUNE 0x5E #define B2056_TX_PGAG_SLOPE 0x5F #define B2056_TX_PGAG_MISC 0x60 #define B2056_TX_MIXA_MASTER 0x61 #define B2056_TX_MIXA_BOOST_TUNE 0x62 #define B2056_TX_MIXG 0x63 #define B2056_TX_MIXG_BOOST_TUNE 0x64 #define B2056_TX_BB_GM_MASTER 0x65 #define B2056_TX_GMBB_GM 0x66 #define B2056_TX_GMBB_IDAC 0x67 #define B2056_TX_TXLPF_MASTER 0x68 #define B2056_TX_TXLPF_RCCAL 0x69 #define B2056_TX_TXLPF_RCCAL_OFF0 0x6A #define B2056_TX_TXLPF_RCCAL_OFF1 0x6B #define B2056_TX_TXLPF_RCCAL_OFF2 0x6C #define B2056_TX_TXLPF_RCCAL_OFF3 0x6D #define B2056_TX_TXLPF_RCCAL_OFF4 0x6E #define B2056_TX_TXLPF_RCCAL_OFF5 0x6F #define B2056_TX_TXLPF_RCCAL_OFF6 0x70 #define B2056_TX_TXLPF_BW 0x71 #define B2056_TX_TXLPF_GAIN 0x72 #define B2056_TX_TXLPF_IDAC 0x73 #define B2056_TX_TXLPF_IDAC_0 0x74 #define B2056_TX_TXLPF_IDAC_1 0x75 #define B2056_TX_TXLPF_IDAC_2 0x76 #define B2056_TX_TXLPF_IDAC_3 0x77 #define B2056_TX_TXLPF_IDAC_4 0x78 #define B2056_TX_TXLPF_IDAC_5 0x79 #define B2056_TX_TXLPF_IDAC_6 0x7A #define B2056_TX_TXLPF_OPAMP_IDAC 0x7B #define B2056_TX_TXLPF_MISC 0x7C #define B2056_TX_TXSPARE1 0x7D #define B2056_TX_TXSPARE2 0x7E #define B2056_TX_TXSPARE3 0x7F #define B2056_TX_TXSPARE4 0x80 #define B2056_TX_TXSPARE5 0x81 #define B2056_TX_TXSPARE6 0x82 #define B2056_TX_TXSPARE7 0x83 #define B2056_TX_TXSPARE8 0x84 #define B2056_TX_TXSPARE9 0x85 #define B2056_TX_TXSPARE10 0x86 #define B2056_TX_TXSPARE11 0x87 #define B2056_TX_TXSPARE12 0x88 #define B2056_TX_TXSPARE13 0x89 #define B2056_TX_TXSPARE14 0x8A #define B2056_TX_TXSPARE15 0x8B #define B2056_TX_TXSPARE16 0x8C #define B2056_TX_STATUS_INTPA_GAIN 0x8D #define B2056_TX_STATUS_PAD_GAIN 0x8E #define B2056_TX_STATUS_PGA_GAIN 0x8F #define B2056_TX_STATUS_GM_TXLPF_GAIN 0x90 #define B2056_TX_STATUS_TXLPF_BW 0x91 #define B2056_TX_STATUS_TXLPF_RC 0x92 #define B2056_TX_GMBB_IDAC0 0x93 #define B2056_TX_GMBB_IDAC1 0x94 #define B2056_TX_GMBB_IDAC2 0x95 #define B2056_TX_GMBB_IDAC3 0x96 #define B2056_TX_GMBB_IDAC4 0x97 #define B2056_TX_GMBB_IDAC5 0x98 #define B2056_TX_GMBB_IDAC6 0x99 #define B2056_TX_GMBB_IDAC7 0x9A #define B2056_RX_RESERVED_ADDR0 0x00 #define B2056_RX_IDCODE 0x01 #define B2056_RX_RESERVED_ADDR2 0x02 #define B2056_RX_RESERVED_ADDR3 0x03 #define B2056_RX_RESERVED_ADDR4 0x04 #define B2056_RX_RESERVED_ADDR5 0x05 #define B2056_RX_RESERVED_ADDR6 0x06 #define B2056_RX_RESERVED_ADDR7 0x07 #define B2056_RX_COM_CTRL 0x08 #define B2056_RX_COM_PU 0x09 #define B2056_RX_COM_OVR 0x0A #define B2056_RX_COM_RESET 0x0B #define B2056_RX_COM_RCAL 0x0C #define B2056_RX_COM_RC_RXLPF 0x0D #define B2056_RX_COM_RC_TXLPF 0x0E #define B2056_RX_COM_RC_RXHPF 0x0F #define B2056_RX_RESERVED_ADDR16 0x10 #define B2056_RX_RESERVED_ADDR17 0x11 #define B2056_RX_RESERVED_ADDR18 0x12 #define B2056_RX_RESERVED_ADDR19 0x13 #define B2056_RX_RESERVED_ADDR20 0x14 #define B2056_RX_RESERVED_ADDR21 0x15 #define B2056_RX_RESERVED_ADDR22 0x16 #define B2056_RX_RESERVED_ADDR23 0x17 #define B2056_RX_RESERVED_ADDR24 0x18 #define B2056_RX_RESERVED_ADDR25 0x19 #define B2056_RX_RESERVED_ADDR26 0x1A #define B2056_RX_RESERVED_ADDR27 0x1B #define B2056_RX_RESERVED_ADDR28 0x1C #define B2056_RX_RESERVED_ADDR29 0x1D #define B2056_RX_RESERVED_ADDR30 0x1E #define B2056_RX_RESERVED_ADDR31 0x1F #define B2056_RX_RXIQCAL_RXMUX 0x20 #define B2056_RX_RSSI_PU 0x21 #define B2056_RX_RSSI_SEL 0x22 #define B2056_RX_RSSI_GAIN 0x23 #define B2056_RX_RSSI_NB_IDAC 0x24 #define B2056_RX_RSSI_WB2I_IDAC_1 0x25 #define B2056_RX_RSSI_WB2I_IDAC_2 0x26 #define B2056_RX_RSSI_WB2Q_IDAC_1 0x27 #define B2056_RX_RSSI_WB2Q_IDAC_2 0x28 #define B2056_RX_RSSI_POLE 0x29 #define B2056_RX_RSSI_WB1_IDAC 0x2A #define B2056_RX_RSSI_MISC 0x2B #define B2056_RX_LNAA_MASTER 0x2C #define B2056_RX_LNAA_TUNE 0x2D #define B2056_RX_LNAA_GAIN 0x2E #define B2056_RX_LNA_A_SLOPE 0x2F #define B2056_RX_BIASPOLE_LNAA1_IDAC 0x30 #define B2056_RX_LNAA2_IDAC 0x31 #define B2056_RX_LNA1A_MISC 0x32 #define B2056_RX_LNAG_MASTER 0x33 #define B2056_RX_LNAG_TUNE 0x34 #define B2056_RX_LNAG_GAIN 0x35 #define B2056_RX_LNA_G_SLOPE 0x36 #define B2056_RX_BIASPOLE_LNAG1_IDAC 0x37 #define B2056_RX_LNAG2_IDAC 0x38 #define B2056_RX_LNA1G_MISC 0x39 #define B2056_RX_MIXA_MASTER 0x3A #define B2056_RX_MIXA_VCM 0x3B #define B2056_RX_MIXA_CTRLPTAT 0x3C #define B2056_RX_MIXA_LOB_BIAS 0x3D #define B2056_RX_MIXA_CORE_IDAC 0x3E #define B2056_RX_MIXA_CMFB_IDAC 0x3F #define B2056_RX_MIXA_BIAS_AUX 0x40 #define B2056_RX_MIXA_BIAS_MAIN 0x41 #define B2056_RX_MIXA_BIAS_MISC 0x42 #define B2056_RX_MIXA_MAST_BIAS 0x43 #define B2056_RX_MIXG_MASTER 0x44 #define B2056_RX_MIXG_VCM 0x45 #define B2056_RX_MIXG_CTRLPTAT 0x46 #define B2056_RX_MIXG_LOB_BIAS 0x47 #define B2056_RX_MIXG_CORE_IDAC 0x48 #define B2056_RX_MIXG_CMFB_IDAC 0x49 #define B2056_RX_MIXG_BIAS_AUX 0x4A #define B2056_RX_MIXG_BIAS_MAIN 0x4B #define B2056_RX_MIXG_BIAS_MISC 0x4C #define B2056_RX_MIXG_MAST_BIAS 0x4D #define B2056_RX_TIA_MASTER 0x4E #define B2056_RX_TIA_IOPAMP 0x4F #define B2056_RX_TIA_QOPAMP 0x50 #define B2056_RX_TIA_IMISC 0x51 #define B2056_RX_TIA_QMISC 0x52 #define B2056_RX_TIA_GAIN 0x53 #define B2056_RX_TIA_SPARE1 0x54 #define B2056_RX_TIA_SPARE2 0x55 #define B2056_RX_BB_LPF_MASTER 0x56 #define B2056_RX_AACI_MASTER 0x57 #define B2056_RX_RXLPF_IDAC 0x58 #define B2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 #define B2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5A #define B2056_RX_RXLPF_BIAS_DCCANCEL 0x5B #define B2056_RX_RXLPF_OUTVCM 0x5C #define B2056_RX_RXLPF_INVCM_BODY 0x5D #define B2056_RX_RXLPF_CC_OP 0x5E #define B2056_RX_RXLPF_GAIN 0x5F #define B2056_RX_RXLPF_Q_BW 0x60 #define B2056_RX_RXLPF_HP_CORNER_BW 0x61 #define B2056_RX_RXLPF_RCCAL_HPC 0x62 #define B2056_RX_RXHPF_OFF0 0x63 #define B2056_RX_RXHPF_OFF1 0x64 #define B2056_RX_RXHPF_OFF2 0x65 #define B2056_RX_RXHPF_OFF3 0x66 #define B2056_RX_RXHPF_OFF4 0x67 #define B2056_RX_RXHPF_OFF5 0x68 #define B2056_RX_RXHPF_OFF6 0x69 #define B2056_RX_RXHPF_OFF7 0x6A #define B2056_RX_RXLPF_RCCAL_LPC 0x6B #define B2056_RX_RXLPF_OFF_0 0x6C #define B2056_RX_RXLPF_OFF_1 0x6D #define B2056_RX_RXLPF_OFF_2 0x6E #define B2056_RX_RXLPF_OFF_3 0x6F #define B2056_RX_RXLPF_OFF_4 0x70 #define B2056_RX_UNUSED 0x71 #define B2056_RX_VGA_MASTER 0x72 #define B2056_RX_VGA_BIAS 0x73 #define B2056_RX_VGA_BIAS_DCCANCEL 0x74 #define B2056_RX_VGA_GAIN 0x75 #define B2056_RX_VGA_HP_CORNER_BW 0x76 #define B2056_RX_VGABUF_BIAS 0x77 #define B2056_RX_VGABUF_GAIN_BW 0x78 #define B2056_RX_TXFBMIX_A 0x79 #define B2056_RX_TXFBMIX_G 0x7A #define B2056_RX_RXSPARE1 0x7B #define B2056_RX_RXSPARE2 0x7C #define B2056_RX_RXSPARE3 0x7D #define B2056_RX_RXSPARE4 0x7E #define B2056_RX_RXSPARE5 0x7F #define B2056_RX_RXSPARE6 0x80 #define B2056_RX_RXSPARE7 0x81 #define B2056_RX_RXSPARE8 0x82 #define B2056_RX_RXSPARE9 0x83 #define B2056_RX_RXSPARE10 0x84 #define B2056_RX_RXSPARE11 0x85 #define B2056_RX_RXSPARE12 0x86 #define B2056_RX_RXSPARE13 0x87 #define B2056_RX_RXSPARE14 0x88 #define B2056_RX_RXSPARE15 0x89 #define B2056_RX_RXSPARE16 0x8A #define B2056_RX_STATUS_LNAA_GAIN 0x8B #define B2056_RX_STATUS_LNAG_GAIN 0x8C #define B2056_RX_STATUS_MIXTIA_GAIN 0x8D #define B2056_RX_STATUS_RXLPF_GAIN 0x8E #define B2056_RX_STATUS_VGA_BUF_GAIN 0x8F #define B2056_RX_STATUS_RXLPF_Q 0x90 #define B2056_RX_STATUS_RXLPF_BUF_BW 0x91 #define B2056_RX_STATUS_RXLPF_VGA_HPC 0x92 #define B2056_RX_STATUS_RXLPF_RC 0x93 #define B2056_RX_STATUS_HPC_RC 0x94 #define B2056_LNA1_A_PU 0x01 #define B2056_LNA2_A_PU 0x02 #define B2056_LNA1_G_PU 0x01 #define B2056_LNA2_G_PU 0x02 #define B2056_MIXA_PU_I 0x01 #define B2056_MIXA_PU_Q 0x02 #define B2056_MIXA_PU_GM 0x10 #define B2056_MIXG_PU_I 0x01 #define B2056_MIXG_PU_Q 0x02 #define B2056_MIXG_PU_GM 0x10 #define B2056_TIA_PU 0x01 #define B2056_BB_LPF_PU 0x20 #define B2056_W1_PU 0x02 #define B2056_W2_PU 0x04 #define B2056_NB_PU 0x08 #define B2056_RSSI_W1_SEL 0x02 #define B2056_RSSI_W2_SEL 0x04 #define B2056_RSSI_NB_SEL 0x08 #define B2056_VCM_MASK 0x1C #define B2056_RSSI_VCM_SHIFT 0x02 #define B2056_SYN (0x0 << 12) #define B2056_TX0 (0x2 << 12) #define B2056_TX1 (0x3 << 12) #define B2056_RX0 (0x6 << 12) #define B2056_RX1 (0x7 << 12) #define B2056_ALLTX (0xE << 12) #define B2056_ALLRX (0xF << 12) #define B2056_SYN_RESERVED_ADDR0 0x00 #define B2056_SYN_IDCODE 0x01 #define B2056_SYN_RESERVED_ADDR2 0x02 #define B2056_SYN_RESERVED_ADDR3 0x03 #define B2056_SYN_RESERVED_ADDR4 0x04 #define B2056_SYN_RESERVED_ADDR5 0x05 #define B2056_SYN_RESERVED_ADDR6 0x06 #define B2056_SYN_RESERVED_ADDR7 0x07 #define B2056_SYN_COM_CTRL 0x08 #define B2056_SYN_COM_PU 0x09 #define B2056_SYN_COM_OVR 0x0A #define B2056_SYN_COM_RESET 0x0B #define B2056_SYN_COM_RCAL 0x0C #define B2056_SYN_COM_RC_RXLPF 0x0D #define B2056_SYN_COM_RC_TXLPF 0x0E #define B2056_SYN_COM_RC_RXHPF 0x0F #define B2056_SYN_RESERVED_ADDR16 0x10 #define B2056_SYN_RESERVED_ADDR17 0x11 #define B2056_SYN_RESERVED_ADDR18 0x12 #define B2056_SYN_RESERVED_ADDR19 0x13 #define B2056_SYN_RESERVED_ADDR20 0x14 #define B2056_SYN_RESERVED_ADDR21 0x15 #define B2056_SYN_RESERVED_ADDR22 0x16 #define B2056_SYN_RESERVED_ADDR23 0x17 #define B2056_SYN_RESERVED_ADDR24 0x18 #define B2056_SYN_RESERVED_ADDR25 0x19 #define B2056_SYN_RESERVED_ADDR26 0x1A #define B2056_SYN_RESERVED_ADDR27 0x1B #define B2056_SYN_RESERVED_ADDR28 0x1C #define B2056_SYN_RESERVED_ADDR29 0x1D #define B2056_SYN_RESERVED_ADDR30 0x1E #define B2056_SYN_RESERVED_ADDR31 0x1F #define B2056_SYN_GPIO_MASTER1 0x20 #define B2056_SYN_GPIO_MASTER2 0x21 #define B2056_SYN_TOPBIAS_MASTER 0x22 #define B2056_SYN_TOPBIAS_RCAL 0x23 #define B2056_SYN_AFEREG 0x24 #define B2056_SYN_TEMPPROCSENSE 0x25 #define B2056_SYN_TEMPPROCSENSEIDAC 0x26 #define B2056_SYN_TEMPPROCSENSERCAL 0x27 #define B2056_SYN_LPO 0x28 #define B2056_SYN_VDDCAL_MASTER 0x29 #define B2056_SYN_VDDCAL_IDAC 0x2A #define B2056_SYN_VDDCAL_STATUS 0x2B #define B2056_SYN_RCAL_MASTER 0x2C #define B2056_SYN_RCAL_CODE_OUT 0x2D #define B2056_SYN_RCCAL_CTRL0 0x2E #define B2056_SYN_RCCAL_CTRL1 0x2F #define B2056_SYN_RCCAL_CTRL2 0x30 #define B2056_SYN_RCCAL_CTRL3 0x31 #define B2056_SYN_RCCAL_CTRL4 0x32 #define B2056_SYN_RCCAL_CTRL5 0x33 #define B2056_SYN_RCCAL_CTRL6 0x34 #define B2056_SYN_RCCAL_CTRL7 0x35 #define B2056_SYN_RCCAL_CTRL8 0x36 #define B2056_SYN_RCCAL_CTRL9 0x37 #define B2056_SYN_RCCAL_CTRL10 0x38 #define B2056_SYN_RCCAL_CTRL11 0x39 #define B2056_SYN_ZCAL_SPARE1 0x3A #define B2056_SYN_ZCAL_SPARE2 0x3B #define B2056_SYN_PLL_MAST1 0x3C #define B2056_SYN_PLL_MAST2 0x3D #define B2056_SYN_PLL_MAST3 0x3E #define B2056_SYN_PLL_BIAS_RESET 0x3F #define B2056_SYN_PLL_XTAL0 0x40 #define B2056_SYN_PLL_XTAL1 0x41 #define B2056_SYN_PLL_XTAL3 0x42 #define B2056_SYN_PLL_XTAL4 0x43 #define B2056_SYN_PLL_XTAL5 0x44 #define B2056_SYN_PLL_XTAL6 0x45 #define B2056_SYN_PLL_REFDIV 0x46 #define B2056_SYN_PLL_PFD 0x47 #define B2056_SYN_PLL_CP1 0x48 #define B2056_SYN_PLL_CP2 0x49 #define B2056_SYN_PLL_CP3 0x4A #define B2056_SYN_PLL_LOOPFILTER1 0x4B #define B2056_SYN_PLL_LOOPFILTER2 0x4C #define B2056_SYN_PLL_LOOPFILTER3 0x4D #define B2056_SYN_PLL_LOOPFILTER4 0x4E #define B2056_SYN_PLL_LOOPFILTER5 0x4F #define B2056_SYN_PLL_MMD1 0x50 #define B2056_SYN_PLL_MMD2 0x51 #define B2056_SYN_PLL_VCO1 0x52 #define B2056_SYN_PLL_VCO2 0x53 #define B2056_SYN_PLL_MONITOR1 0x54 #define B2056_SYN_PLL_MONITOR2 0x55 #define B2056_SYN_PLL_VCOCAL1 0x56 #define B2056_SYN_PLL_VCOCAL2 0x57 #define B2056_SYN_PLL_VCOCAL4 0x58 #define B2056_SYN_PLL_VCOCAL5 0x59 #define B2056_SYN_PLL_VCOCAL6 0x5A #define B2056_SYN_PLL_VCOCAL7 0x5B #define B2056_SYN_PLL_VCOCAL8 0x5C #define B2056_SYN_PLL_VCOCAL9 0x5D #define B2056_SYN_PLL_VCOCAL10 0x5E #define B2056_SYN_PLL_VCOCAL11 0x5F #define B2056_SYN_PLL_VCOCAL12 0x60 #define B2056_SYN_PLL_VCOCAL13 0x61 #define B2056_SYN_PLL_VREG 0x62 #define B2056_SYN_PLL_STATUS1 0x63 #define B2056_SYN_PLL_STATUS2 0x64 #define B2056_SYN_PLL_STATUS3 0x65 #define B2056_SYN_LOGEN_PU0 0x66 #define B2056_SYN_LOGEN_PU1 0x67 #define B2056_SYN_LOGEN_PU2 0x68 #define B2056_SYN_LOGEN_PU3 0x69 #define B2056_SYN_LOGEN_PU5 0x6A #define B2056_SYN_LOGEN_PU6 0x6B #define B2056_SYN_LOGEN_PU7 0x6C #define B2056_SYN_LOGEN_PU8 0x6D #define B2056_SYN_LOGEN_BIAS_RESET 0x6E #define B2056_SYN_LOGEN_RCCR1 0x6F #define B2056_SYN_LOGEN_VCOBUF1 0x70 #define B2056_SYN_LOGEN_MIXER1 0x71 #define B2056_SYN_LOGEN_MIXER2 0x72 #define B2056_SYN_LOGEN_BUF1 0x73 #define B2056_SYN_LOGENBUF2 0x74 #define B2056_SYN_LOGEN_BUF3 0x75 #define B2056_SYN_LOGEN_BUF4 0x76 #define B2056_SYN_LOGEN_DIV1 0x77 #define B2056_SYN_LOGEN_DIV2 0x78 #define B2056_SYN_LOGEN_DIV3 0x79 #define B2056_SYN_LOGEN_ACL1 0x7A #define B2056_SYN_LOGEN_ACL2 0x7B #define B2056_SYN_LOGEN_ACL3 0x7C #define B2056_SYN_LOGEN_ACL4 0x7D #define B2056_SYN_LOGEN_ACL5 0x7E #define B2056_SYN_LOGEN_ACL6 0x7F #define B2056_SYN_LOGEN_ACLOUT 0x80 #define B2056_SYN_LOGEN_ACLCAL1 0x81 #define B2056_SYN_LOGEN_ACLCAL2 0x82 #define B2056_SYN_LOGEN_ACLCAL3 0x83 #define B2056_SYN_CALEN 0x84 #define B2056_SYN_LOGEN_PEAKDET1 0x85 #define B2056_SYN_LOGEN_CORE_ACL_OVR 0x86 #define B2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 #define B2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 #define B2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 #define B2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8A #define B2056_SYN_LOGEN_VCOBUF2 0x8B #define B2056_SYN_LOGEN_MIXER3 0x8C #define B2056_SYN_LOGEN_BUF5 0x8D #define B2056_SYN_LOGEN_BUF6 0x8E #define B2056_SYN_LOGEN_CBUFRX1 0x8F #define B2056_SYN_LOGEN_CBUFRX2 0x90 #define B2056_SYN_LOGEN_CBUFRX3 0x91 #define B2056_SYN_LOGEN_CBUFRX4 0x92 #define B2056_SYN_LOGEN_CBUFTX1 0x93 #define B2056_SYN_LOGEN_CBUFTX2 0x94 #define B2056_SYN_LOGEN_CBUFTX3 0x95 #define B2056_SYN_LOGEN_CBUFTX4 0x96 #define B2056_SYN_LOGEN_CMOSRX1 0x97 #define B2056_SYN_LOGEN_CMOSRX2 0x98 #define B2056_SYN_LOGEN_CMOSRX3 0x99 #define B2056_SYN_LOGEN_CMOSRX4 0x9A #define B2056_SYN_LOGEN_CMOSTX1 0x9B #define B2056_SYN_LOGEN_CMOSTX2 0x9C #define B2056_SYN_LOGEN_CMOSTX3 0x9D #define B2056_SYN_LOGEN_CMOSTX4 0x9E #define B2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9F #define B2056_SYN_LOGEN_MIXER3_OVRVAL 0xA0 #define B2056_SYN_LOGEN_BUF5_OVRVAL 0xA1 #define B2056_SYN_LOGEN_BUF6_OVRVAL 0xA2 #define B2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xA3 #define B2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xA4 #define B2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xA5 #define B2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xA6 #define B2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xA7 #define B2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xA8 #define B2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xA9 #define B2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xAA #define B2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xAB #define B2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xAC #define B2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xAD #define B2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xAE #define B2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xAF #define B2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xB0 #define B2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xB1 #define B2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xB2 #define B2056_SYN_LOGEN_ACL_WAITCNT 0xB3 #define B2056_SYN_LOGEN_CORE_CALVALID 0xB4 #define B2056_SYN_LOGEN_RX_CMOS_CALVALID 0xB5 #define B2056_SYN_LOGEN_TX_CMOS_VALID 0xB6 #define B2056_TX_RESERVED_ADDR0 0x00 #define B2056_TX_IDCODE 0x01 #define B2056_TX_RESERVED_ADDR2 0x02 #define B2056_TX_RESERVED_ADDR3 0x03 #define B2056_TX_RESERVED_ADDR4 0x04 #define B2056_TX_RESERVED_ADDR5 0x05 #define B2056_TX_RESERVED_ADDR6 0x06 #define B2056_TX_RESERVED_ADDR7 0x07 #define B2056_TX_COM_CTRL 0x08 #define B2056_TX_COM_PU 0x09 #define B2056_TX_COM_OVR 0x0A #define B2056_TX_COM_RESET 0x0B #define B2056_TX_COM_RCAL 0x0C #define B2056_TX_COM_RC_RXLPF 0x0D #define B2056_TX_COM_RC_TXLPF 0x0E #define B2056_TX_COM_RC_RXHPF 0x0F #define B2056_TX_RESERVED_ADDR16 0x10 #define B2056_TX_RESERVED_ADDR17 0x11 #define B2056_TX_RESERVED_ADDR18 0x12 #define B2056_TX_RESERVED_ADDR19 0x13 #define B2056_TX_RESERVED_ADDR20 0x14 #define B2056_TX_RESERVED_ADDR21 0x15 #define B2056_TX_RESERVED_ADDR22 0x16 #define B2056_TX_RESERVED_ADDR23 0x17 #define B2056_TX_RESERVED_ADDR24 0x18 #define B2056_TX_RESERVED_ADDR25 0x19 #define B2056_TX_RESERVED_ADDR26 0x1A #define B2056_TX_RESERVED_ADDR27 0x1B #define B2056_TX_RESERVED_ADDR28 0x1C #define B2056_TX_RESERVED_ADDR29 0x1D #define B2056_TX_RESERVED_ADDR30 0x1E #define B2056_TX_RESERVED_ADDR31 0x1F #define B2056_TX_IQCAL_GAIN_BW 0x20 #define B2056_TX_LOFT_FINE_I 0x21 #define B2056_TX_LOFT_FINE_Q 0x22 #define B2056_TX_LOFT_COARSE_I 0x23 #define B2056_TX_LOFT_COARSE_Q 0x24 #define B2056_TX_TX_COM_MASTER1 0x25 #define B2056_TX_TX_COM_MASTER2 0x26 #define B2056_TX_RXIQCAL_TXMUX 0x27 #define B2056_TX_TX_SSI_MASTER 0x28 #define B2056_TX_IQCAL_VCM_HG 0x29 #define B2056_TX_IQCAL_IDAC 0x2A #define B2056_TX_TSSI_VCM 0x2B #define B2056_TX_TX_AMP_DET 0x2C #define B2056_TX_TX_SSI_MUX 0x2D #define B2056_TX_TSSIA 0x2E #define B2056_TX_TSSIG 0x2F #define B2056_TX_TSSI_MISC1 0x30 #define B2056_TX_TSSI_MISC2 0x31 #define B2056_TX_TSSI_MISC3 0x32 #define B2056_TX_PA_SPARE1 0x33 #define B2056_TX_PA_SPARE2 0x34 #define B2056_TX_INTPAA_MASTER 0x35 #define B2056_TX_INTPAA_GAIN 0x36 #define B2056_TX_INTPAA_BOOST_TUNE 0x37 #define B2056_TX_INTPAA_IAUX_STAT 0x38 #define B2056_TX_INTPAA_IAUX_DYN 0x39 #define B2056_TX_INTPAA_IMAIN_STAT 0x3A #define B2056_TX_INTPAA_IMAIN_DYN 0x3B #define B2056_TX_INTPAA_CASCBIAS 0x3C #define B2056_TX_INTPAA_PASLOPE 0x3D #define B2056_TX_INTPAA_PA_MISC 0x3E #define B2056_TX_INTPAG_MASTER 0x3F #define B2056_TX_INTPAG_GAIN 0x40 #define B2056_TX_INTPAG_BOOST_TUNE 0x41 #define B2056_TX_INTPAG_IAUX_STAT 0x42 #define B2056_TX_INTPAG_IAUX_DYN 0x43 #define B2056_TX_INTPAG_IMAIN_STAT 0x44 #define B2056_TX_INTPAG_IMAIN_DYN 0x45 #define B2056_TX_INTPAG_CASCBIAS 0x46 #define B2056_TX_INTPAG_PASLOPE 0x47 #define B2056_TX_INTPAG_PA_MISC 0x48 #define B2056_TX_PADA_MASTER 0x49 #define B2056_TX_PADA_IDAC 0x4A #define B2056_TX_PADA_CASCBIAS 0x4B #define B2056_TX_PADA_GAIN 0x4C #define B2056_TX_PADA_BOOST_TUNE 0x4D #define B2056_TX_PADA_SLOPE 0x4E #define B2056_TX_PADG_MASTER 0x4F #define B2056_TX_PADG_IDAC 0x50 #define B2056_TX_PADG_CASCBIAS 0x51 #define B2056_TX_PADG_GAIN 0x52 #define B2056_TX_PADG_BOOST_TUNE 0x53 #define B2056_TX_PADG_SLOPE 0x54 #define B2056_TX_PGAA_MASTER 0x55 #define B2056_TX_PGAA_IDAC 0x56 #define B2056_TX_PGAA_GAIN 0x57 #define B2056_TX_PGAA_BOOST_TUNE 0x58 #define B2056_TX_PGAA_SLOPE 0x59 #define B2056_TX_PGAA_MISC 0x5A #define B2056_TX_PGAG_MASTER 0x5B #define B2056_TX_PGAG_IDAC 0x5C #define B2056_TX_PGAG_GAIN 0x5D #define B2056_TX_PGAG_BOOST_TUNE 0x5E #define B2056_TX_PGAG_SLOPE 0x5F #define B2056_TX_PGAG_MISC 0x60 #define B2056_TX_MIXA_MASTER 0x61 #define B2056_TX_MIXA_BOOST_TUNE 0x62 #define B2056_TX_MIXG 0x63 #define B2056_TX_MIXG_BOOST_TUNE 0x64 #define B2056_TX_BB_GM_MASTER 0x65 #define B2056_TX_GMBB_GM 0x66 #define B2056_TX_GMBB_IDAC 0x67 #define B2056_TX_TXLPF_MASTER 0x68 #define B2056_TX_TXLPF_RCCAL 0x69 #define B2056_TX_TXLPF_RCCAL_OFF0 0x6A #define B2056_TX_TXLPF_RCCAL_OFF1 0x6B #define B2056_TX_TXLPF_RCCAL_OFF2 0x6C #define B2056_TX_TXLPF_RCCAL_OFF3 0x6D #define B2056_TX_TXLPF_RCCAL_OFF4 0x6E #define B2056_TX_TXLPF_RCCAL_OFF5 0x6F #define B2056_TX_TXLPF_RCCAL_OFF6 0x70 #define B2056_TX_TXLPF_BW 0x71 #define B2056_TX_TXLPF_GAIN 0x72 #define B2056_TX_TXLPF_IDAC 0x73 #define B2056_TX_TXLPF_IDAC_0 0x74 #define B2056_TX_TXLPF_IDAC_1 0x75 #define B2056_TX_TXLPF_IDAC_2 0x76 #define B2056_TX_TXLPF_IDAC_3 0x77 #define B2056_TX_TXLPF_IDAC_4 0x78 #define B2056_TX_TXLPF_IDAC_5 0x79 #define B2056_TX_TXLPF_IDAC_6 0x7A #define B2056_TX_TXLPF_OPAMP_IDAC 0x7B #define B2056_TX_TXLPF_MISC 0x7C #define B2056_TX_TXSPARE1 0x7D #define B2056_TX_TXSPARE2 0x7E #define B2056_TX_TXSPARE3 0x7F #define B2056_TX_TXSPARE4 0x80 #define B2056_TX_TXSPARE5 0x81 #define B2056_TX_TXSPARE6 0x82 #define B2056_TX_TXSPARE7 0x83 #define B2056_TX_TXSPARE8 0x84 #define B2056_TX_TXSPARE9 0x85 #define B2056_TX_TXSPARE10 0x86 #define B2056_TX_TXSPARE11 0x87 #define B2056_TX_TXSPARE12 0x88 #define B2056_TX_TXSPARE13 0x89 #define B2056_TX_TXSPARE14 0x8A #define B2056_TX_TXSPARE15 0x8B #define B2056_TX_TXSPARE16 0x8C #define B2056_TX_STATUS_INTPA_GAIN 0x8D #define B2056_TX_STATUS_PAD_GAIN 0x8E #define B2056_TX_STATUS_PGA_GAIN 0x8F #define B2056_TX_STATUS_GM_TXLPF_GAIN 0x90 #define B2056_TX_STATUS_TXLPF_BW 0x91 #define B2056_TX_STATUS_TXLPF_RC 0x92 #define B2056_TX_GMBB_IDAC0 0x93 #define B2056_TX_GMBB_IDAC1 0x94 #define B2056_TX_GMBB_IDAC2 0x95 #define B2056_TX_GMBB_IDAC3 0x96 #define B2056_TX_GMBB_IDAC4 0x97 #define B2056_TX_GMBB_IDAC5 0x98 #define B2056_TX_GMBB_IDAC6 0x99 #define B2056_TX_GMBB_IDAC7 0x9A #define B2056_RX_RESERVED_ADDR0 0x00 #define B2056_RX_IDCODE 0x01 #define B2056_RX_RESERVED_ADDR2 0x02 #define B2056_RX_RESERVED_ADDR3 0x03 #define B2056_RX_RESERVED_ADDR4 0x04 #define B2056_RX_RESERVED_ADDR5 0x05 #define B2056_RX_RESERVED_ADDR6 0x06 #define B2056_RX_RESERVED_ADDR7 0x07 #define B2056_RX_COM_CTRL 0x08 #define B2056_RX_COM_PU 0x09 #define B2056_RX_COM_OVR 0x0A #define B2056_RX_COM_RESET 0x0B #define B2056_RX_COM_RCAL 0x0C #define B2056_RX_COM_RC_RXLPF 0x0D #define B2056_RX_COM_RC_TXLPF 0x0E #define B2056_RX_COM_RC_RXHPF 0x0F #define B2056_RX_RESERVED_ADDR16 0x10 #define B2056_RX_RESERVED_ADDR17 0x11 #define B2056_RX_RESERVED_ADDR18 0x12 #define B2056_RX_RESERVED_ADDR19 0x13 #define B2056_RX_RESERVED_ADDR20 0x14 #define B2056_RX_RESERVED_ADDR21 0x15 #define B2056_RX_RESERVED_ADDR22 0x16 #define B2056_RX_RESERVED_ADDR23 0x17 #define B2056_RX_RESERVED_ADDR24 0x18 #define B2056_RX_RESERVED_ADDR25 0x19 #define B2056_RX_RESERVED_ADDR26 0x1A #define B2056_RX_RESERVED_ADDR27 0x1B #define B2056_RX_RESERVED_ADDR28 0x1C #define B2056_RX_RESERVED_ADDR29 0x1D #define B2056_RX_RESERVED_ADDR30 0x1E #define B2056_RX_RESERVED_ADDR31 0x1F #define B2056_RX_RXIQCAL_RXMUX 0x20 #define B2056_RX_RSSI_PU 0x21 #define B2056_RX_RSSI_SEL 0x22 #define B2056_RX_RSSI_GAIN 0x23 #define B2056_RX_RSSI_NB_IDAC 0x24 #define B2056_RX_RSSI_WB2I_IDAC_1 0x25 #define B2056_RX_RSSI_WB2I_IDAC_2 0x26 #define B2056_RX_RSSI_WB2Q_IDAC_1 0x27 #define B2056_RX_RSSI_WB2Q_IDAC_2 0x28 #define B2056_RX_RSSI_POLE 0x29 #define B2056_RX_RSSI_WB1_IDAC 0x2A #define B2056_RX_RSSI_MISC 0x2B #define B2056_RX_LNAA_MASTER 0x2C #define B2056_RX_LNAA_TUNE 0x2D #define B2056_RX_LNAA_GAIN 0x2E #define B2056_RX_LNA_A_SLOPE 0x2F #define B2056_RX_BIASPOLE_LNAA1_IDAC 0x30 #define B2056_RX_LNAA2_IDAC 0x31 #define B2056_RX_LNA1A_MISC 0x32 #define B2056_RX_LNAG_MASTER 0x33 #define B2056_RX_LNAG_TUNE 0x34 #define B2056_RX_LNAG_GAIN 0x35 #define B2056_RX_LNA_G_SLOPE 0x36 #define B2056_RX_BIASPOLE_LNAG1_IDAC 0x37 #define B2056_RX_LNAG2_IDAC 0x38 #define B2056_RX_LNA1G_MISC 0x39 #define B2056_RX_MIXA_MASTER 0x3A #define B2056_RX_MIXA_VCM 0x3B #define B2056_RX_MIXA_CTRLPTAT 0x3C #define B2056_RX_MIXA_LOB_BIAS 0x3D #define B2056_RX_MIXA_CORE_IDAC 0x3E #define B2056_RX_MIXA_CMFB_IDAC 0x3F #define B2056_RX_MIXA_BIAS_AUX 0x40 #define B2056_RX_MIXA_BIAS_MAIN 0x41 #define B2056_RX_MIXA_BIAS_MISC 0x42 #define B2056_RX_MIXA_MAST_BIAS 0x43 #define B2056_RX_MIXG_MASTER 0x44 #define B2056_RX_MIXG_VCM 0x45 #define B2056_RX_MIXG_CTRLPTAT 0x46 #define B2056_RX_MIXG_LOB_BIAS 0x47 #define B2056_RX_MIXG_CORE_IDAC 0x48 #define B2056_RX_MIXG_CMFB_IDAC 0x49 #define B2056_RX_MIXG_BIAS_AUX 0x4A #define B2056_RX_MIXG_BIAS_MAIN 0x4B #define B2056_RX_MIXG_BIAS_MISC 0x4C #define B2056_RX_MIXG_MAST_BIAS 0x4D #define B2056_RX_TIA_MASTER 0x4E #define B2056_RX_TIA_IOPAMP 0x4F #define B2056_RX_TIA_QOPAMP 0x50 #define B2056_RX_TIA_IMISC 0x51 #define B2056_RX_TIA_QMISC 0x52 #define B2056_RX_TIA_GAIN 0x53 #define B2056_RX_TIA_SPARE1 0x54 #define B2056_RX_TIA_SPARE2 0x55 #define B2056_RX_BB_LPF_MASTER 0x56 #define B2056_RX_AACI_MASTER 0x57 #define B2056_RX_RXLPF_IDAC 0x58 #define B2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 #define B2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5A #define B2056_RX_RXLPF_BIAS_DCCANCEL 0x5B #define B2056_RX_RXLPF_OUTVCM 0x5C #define B2056_RX_RXLPF_INVCM_BODY 0x5D #define B2056_RX_RXLPF_CC_OP 0x5E #define B2056_RX_RXLPF_GAIN 0x5F #define B2056_RX_RXLPF_Q_BW 0x60 #define B2056_RX_RXLPF_HP_CORNER_BW 0x61 #define B2056_RX_RXLPF_RCCAL_HPC 0x62 #define B2056_RX_RXHPF_OFF0 0x63 #define B2056_RX_RXHPF_OFF1 0x64 #define B2056_RX_RXHPF_OFF2 0x65 #define B2056_RX_RXHPF_OFF3 0x66 #define B2056_RX_RXHPF_OFF4 0x67 #define B2056_RX_RXHPF_OFF5 0x68 #define B2056_RX_RXHPF_OFF6 0x69 #define B2056_RX_RXHPF_OFF7 0x6A #define B2056_RX_RXLPF_RCCAL_LPC 0x6B #define B2056_RX_RXLPF_OFF_0 0x6C #define B2056_RX_RXLPF_OFF_1 0x6D #define B2056_RX_RXLPF_OFF_2 0x6E #define B2056_RX_RXLPF_OFF_3 0x6F #define B2056_RX_RXLPF_OFF_4 0x70 #define B2056_RX_UNUSED 0x71 #define B2056_RX_VGA_MASTER 0x72 #define B2056_RX_VGA_BIAS 0x73 #define B2056_RX_VGA_BIAS_DCCANCEL 0x74 #define B2056_RX_VGA_GAIN 0x75 #define B2056_RX_VGA_HP_CORNER_BW 0x76 #define B2056_RX_VGABUF_BIAS 0x77 #define B2056_RX_VGABUF_GAIN_BW 0x78 #define B2056_RX_TXFBMIX_A 0x79 #define B2056_RX_TXFBMIX_G 0x7A #define B2056_RX_RXSPARE1 0x7B #define B2056_RX_RXSPARE2 0x7C #define B2056_RX_RXSPARE3 0x7D #define B2056_RX_RXSPARE4 0x7E #define B2056_RX_RXSPARE5 0x7F #define B2056_RX_RXSPARE6 0x80 #define B2056_RX_RXSPARE7 0x81 #define B2056_RX_RXSPARE8 0x82 #define B2056_RX_RXSPARE9 0x83 #define B2056_RX_RXSPARE10 0x84 #define B2056_RX_RXSPARE11 0x85 #define B2056_RX_RXSPARE12 0x86 #define B2056_RX_RXSPARE13 0x87 #define B2056_RX_RXSPARE14 0x88 #define B2056_RX_RXSPARE15 0x89 #define B2056_RX_RXSPARE16 0x8A #define B2056_RX_STATUS_LNAA_GAIN 0x8B #define B2056_RX_STATUS_LNAG_GAIN 0x8C #define B2056_RX_STATUS_MIXTIA_GAIN 0x8D #define B2056_RX_STATUS_RXLPF_GAIN 0x8E #define B2056_RX_STATUS_VGA_BUF_GAIN 0x8F #define B2056_RX_STATUS_RXLPF_Q 0x90 #define B2056_RX_STATUS_RXLPF_BUF_BW 0x91 #define B2056_RX_STATUS_RXLPF_VGA_HPC 0x92 #define B2056_RX_STATUS_RXLPF_RC 0x93 #define B2056_RX_STATUS_HPC_RC 0x94 #define B2056_LNA1_A_PU 0x01 #define B2056_LNA2_A_PU 0x02 #define B2056_LNA1_G_PU 0x01 #define B2056_LNA2_G_PU 0x02 #define B2056_MIXA_PU_I 0x01 #define B2056_MIXA_PU_Q 0x02 #define B2056_MIXA_PU_GM 0x10 #define B2056_MIXG_PU_I 0x01 #define B2056_MIXG_PU_Q 0x02 #define B2056_MIXG_PU_GM 0x10 #define B2056_TIA_PU 0x01 #define B2056_BB_LPF_PU 0x20 #define B2056_W1_PU 0x02 #define B2056_W2_PU 0x04 #define B2056_NB_PU 0x08 #define B2056_RSSI_W1_SEL 0x02 #define B2056_RSSI_W2_SEL 0x04 #define B2056_RSSI_NB_SEL 0x08 #define B2056_VCM_MASK 0x1C #define B2056_RSSI_VCM_SHIFT 0x02 struct b43_nphy_channeltab_entry_rev3 { /* The channel frequency in MHz */ u16 freq; /* Radio register values on channelswitch */ u8 radio_syn_pll_vcocal1; u8 radio_syn_pll_vcocal2; u8 radio_syn_pll_refdiv; u8 radio_syn_pll_mmd2; u8 radio_syn_pll_mmd1; u8 radio_syn_pll_loopfilter1; u8 radio_syn_pll_loopfilter2; u8 radio_syn_pll_loopfilter3; u8 radio_syn_pll_loopfilter4; u8 radio_syn_pll_loopfilter5; u8 radio_syn_reserved_addr27; u8 radio_syn_reserved_addr28; u8 radio_syn_reserved_addr29; u8 radio_syn_logen_vcobuf1; u8 radio_syn_logen_mixer2; u8 radio_syn_logen_buf3; u8 radio_syn_logen_buf4; u8 radio_rx0_lnaa_tune; u8 radio_rx0_lnag_tune; u8 radio_tx0_intpaa_boost_tune; u8 radio_tx0_intpag_boost_tune; u8 radio_tx0_pada_boost_tune; u8 radio_tx0_padg_boost_tune; u8 radio_tx0_pgaa_boost_tune; u8 radio_tx0_pgag_boost_tune; u8 radio_tx0_mixa_boost_tune; u8 radio_tx0_mixg_boost_tune; u8 radio_rx1_lnaa_tune; u8 radio_rx1_lnag_tune; u8 radio_tx1_intpaa_boost_tune; u8 radio_tx1_intpag_boost_tune; u8 radio_tx1_pada_boost_tune; u8 radio_tx1_padg_boost_tune; u8 radio_tx1_pgaa_boost_tune; u8 radio_tx1_pgag_boost_tune; u8 radio_tx1_mixa_boost_tune; u8 radio_tx1_mixg_boost_tune; /* PHY register values on channelswitch */ struct b43_phy_n_sfo_cfg phy_regs; }; void b2056_upload_inittabs(struct b43_wldev *dev, bool ghz5, bool ignore_uploadflag); void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5); /* Get the NPHY Channel Switch Table entry for a channel. * Returns NULL on failure to find an entry. */ const struct b43_nphy_channeltab_entry_rev3 * b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq); #endif /* B43_RADIO_2056_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2056.c0000644000175000017500000164006212026211315023000 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n 2056 radio device data tables Copyright (c) 2010 Rafał Miłecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "radio_2056.h" #include "phy_common.h" struct b2056_inittab_entry { /* Value to write if we use the 5GHz band. */ u16 ghz5; /* Value to write if we use the 2.4GHz band. */ u16 ghz2; /* Flags */ u8 flags; }; #define B2056_INITTAB_ENTRY_OK 0x01 #define B2056_INITTAB_UPLOAD 0x02 #define UPLOAD .flags = B2056_INITTAB_ENTRY_OK | B2056_INITTAB_UPLOAD #define NOUPLOAD .flags = B2056_INITTAB_ENTRY_OK struct b2056_inittabs_pts { const struct b2056_inittab_entry *syn; unsigned int syn_length; const struct b2056_inittab_entry *tx; unsigned int tx_length; const struct b2056_inittab_entry *rx; unsigned int rx_length; }; static const struct b2056_inittab_entry b2056_inittab_rev3_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev3_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev3_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0099, .ghz2 = 0x0099, NOUPLOAD, }, [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0044, .ghz2 = 0x0044, UPLOAD, }, [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0099, .ghz2 = 0x0099, NOUPLOAD, }, [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_RX_TIA_IMISC] = { .ghz5 = 0x0057, .ghz2 = 0x0057, NOUPLOAD, }, [B2056_RX_TIA_QMISC] = { .ghz5 = 0x0057, .ghz2 = 0x0057, NOUPLOAD, }, [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev4_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev4_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev4_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0044, .ghz2 = 0x0044, UPLOAD, }, [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x002f, .ghz2 = 0x002f, UPLOAD, }, [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev5_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev5_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0073, .ghz2 = 0x0073, UPLOAD, }, [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev5_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev6_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_CP2] = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, }, [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, }, [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev6_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev6_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev7_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev7_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0073, .ghz2 = 0x0073, UPLOAD, }, [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev7_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev8_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev8_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, }; static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; #define INITTABSPTS(prefix) \ .syn = prefix##_syn, \ .syn_length = ARRAY_SIZE(prefix##_syn), \ .tx = prefix##_tx, \ .tx_length = ARRAY_SIZE(prefix##_tx), \ .rx = prefix##_rx, \ .rx_length = ARRAY_SIZE(prefix##_rx) struct b2056_inittabs_pts b2056_inittabs[] = { [3] = { INITTABSPTS(b2056_inittab_rev3) }, [4] = { INITTABSPTS(b2056_inittab_rev4) }, [5] = { INITTABSPTS(b2056_inittab_rev5) }, [6] = { INITTABSPTS(b2056_inittab_rev6) }, [7] = { INITTABSPTS(b2056_inittab_rev7) }, [8] = { INITTABSPTS(b2056_inittab_rev8) }, [9] = { INITTABSPTS(b2056_inittab_rev7) }, }; #define RADIOREGS3(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, \ r30, r31, r32, r33, r34, r35, r36) \ .radio_syn_pll_vcocal1 = r00, \ .radio_syn_pll_vcocal2 = r01, \ .radio_syn_pll_refdiv = r02, \ .radio_syn_pll_mmd2 = r03, \ .radio_syn_pll_mmd1 = r04, \ .radio_syn_pll_loopfilter1 = r05, \ .radio_syn_pll_loopfilter2 = r06, \ .radio_syn_pll_loopfilter3 = r07, \ .radio_syn_pll_loopfilter4 = r08, \ .radio_syn_pll_loopfilter5 = r09, \ .radio_syn_reserved_addr27 = r10, \ .radio_syn_reserved_addr28 = r11, \ .radio_syn_reserved_addr29 = r12, \ .radio_syn_logen_vcobuf1 = r13, \ .radio_syn_logen_mixer2 = r14, \ .radio_syn_logen_buf3 = r15, \ .radio_syn_logen_buf4 = r16, \ .radio_rx0_lnaa_tune = r17, \ .radio_rx0_lnag_tune = r18, \ .radio_tx0_intpaa_boost_tune = r19, \ .radio_tx0_intpag_boost_tune = r20, \ .radio_tx0_pada_boost_tune = r21, \ .radio_tx0_padg_boost_tune = r22, \ .radio_tx0_pgaa_boost_tune = r23, \ .radio_tx0_pgag_boost_tune = r24, \ .radio_tx0_mixa_boost_tune = r25, \ .radio_tx0_mixg_boost_tune = r26, \ .radio_rx1_lnaa_tune = r27, \ .radio_rx1_lnag_tune = r28, \ .radio_tx1_intpaa_boost_tune = r29, \ .radio_tx1_intpag_boost_tune = r30, \ .radio_tx1_pada_boost_tune = r31, \ .radio_tx1_padg_boost_tune = r32, \ .radio_tx1_pgaa_boost_tune = r33, \ .radio_tx1_pgag_boost_tune = r34, \ .radio_tx1_mixa_boost_tune = r35, \ .radio_tx1_mixg_boost_tune = r36 #define PHYREGS(r0, r1, r2, r3, r4, r5) \ .phy_regs.phy_bw1a = r0, \ .phy_regs.phy_bw2 = r1, \ .phy_regs.phy_bw3 = r2, \ .phy_regs.phy_bw4 = r3, \ .phy_regs.phy_bw5 = r4, \ .phy_regs.phy_bw6 = r5 /* http://bcm-v4.sipsolutions.net/802.11/Radio/2056/ChannelTable */ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev3[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), }, { .freq = 4930, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), }, { .freq = 4940, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), }, { .freq = 4950, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), }, { .freq = 4960, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), }, { .freq = 4970, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), }, { .freq = 4980, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), }, { .freq = 4990, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), }, { .freq = 5000, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), }, { .freq = 5010, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), }, { .freq = 5020, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), }, { .freq = 5030, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), }, { .freq = 5040, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), }, { .freq = 5050, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), }, { .freq = 5060, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), }, { .freq = 5070, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), }, { .freq = 5080, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), }, { .freq = 5090, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), }, { .freq = 5100, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xff, 0x00), PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), }, { .freq = 5110, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), }, { .freq = 5120, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), }, { .freq = 5130, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), }, { .freq = 5140, RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), }, { .freq = 5160, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), }, { .freq = 5170, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), }, { .freq = 5180, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), }, { .freq = 5190, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xfc, 0x00), PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), }, { .freq = 5200, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), }, { .freq = 5210, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), }, { .freq = 5220, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), }, { .freq = 5230, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), }, { .freq = 5240, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), }, { .freq = 5250, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), }, { .freq = 5260, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), }, { .freq = 5270, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), }, { .freq = 5280, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), }, { .freq = 5290, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xfc, 0x00), PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), }, { .freq = 5300, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfc, 0x00), PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), }, { .freq = 5310, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), }, { .freq = 5320, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), }, { .freq = 5330, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), }, { .freq = 5340, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), }, { .freq = 5350, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), }, { .freq = 5360, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), }, { .freq = 5370, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), }, { .freq = 5380, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), }, { .freq = 5390, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xfa, 0x00), PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), }, { .freq = 5400, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00), PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), }, { .freq = 5410, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00), PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), }, { .freq = 5420, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00), PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), }, { .freq = 5430, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00), PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), }, { .freq = 5440, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00), PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), }, { .freq = 5450, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xfa, 0x00), PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), }, { .freq = 5460, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00), PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), }, { .freq = 5470, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00), PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), }, { .freq = 5480, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00), PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), }, { .freq = 5490, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, 0x00, 0xf8, 0x00), PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), }, { .freq = 5500, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), }, { .freq = 5510, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), }, { .freq = 5520, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), }, { .freq = 5530, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), }, { .freq = 5540, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), }, { .freq = 5550, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), }, { .freq = 5560, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), }, { .freq = 5570, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), }, { .freq = 5580, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), }, { .freq = 5590, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), }, { .freq = 5600, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), }, { .freq = 5610, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), }, { .freq = 5620, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), }, { .freq = 5630, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), }, { .freq = 5640, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), }, { .freq = 5650, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf8, 0x00), PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), }, { .freq = 5660, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00), PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), }, { .freq = 5670, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00), PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), }, { .freq = 5680, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00), PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), }, { .freq = 5690, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf6, 0x00), PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), }, { .freq = 5700, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf6, 0x00), PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), }, { .freq = 5710, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), }, { .freq = 5720, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5725, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5730, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), }, { .freq = 5735, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), }, { .freq = 5740, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), }, { .freq = 5745, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), }, { .freq = 5750, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), }, { .freq = 5755, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), }, { .freq = 5760, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5765, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5770, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), }, { .freq = 5775, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), }, { .freq = 5780, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), }, { .freq = 5785, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5790, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5795, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), }, { .freq = 5800, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), }, { .freq = 5805, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), }, { .freq = 5810, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5815, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5820, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), }, { .freq = 5825, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), }, { .freq = 5830, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), }, { .freq = 5840, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), }, { .freq = 5850, RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf4, 0x00), PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), }, { .freq = 5860, RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00), PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), }, { .freq = 5870, RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00), PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), }, { .freq = 5880, RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00), PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), }, { .freq = 5890, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, 0x00, 0xf2, 0x00), PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), }, { .freq = 5900, RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00), PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), }, { .freq = 5910, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, 0x00, 0xf2, 0x00), PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), }, { .freq = 2412, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), }, { .freq = 2417, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), }, { .freq = 2422, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), }, { .freq = 2427, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), }, { .freq = 2432, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), }, { .freq = 2437, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), }, { .freq = 2442, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), }, { .freq = 2447, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), }, { .freq = 2452, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0f), PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), }, { .freq = 2457, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d), PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), }, { .freq = 2462, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d), PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), }, { .freq = 2467, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d), PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), }, { .freq = 2472, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d), PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), }, { .freq = 2484, RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0d), PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), }, }; static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev4[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), }, { .freq = 4930, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), }, { .freq = 4940, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), }, { .freq = 4950, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), }, { .freq = 4960, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), }, { .freq = 4970, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), }, { .freq = 4980, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), }, { .freq = 4990, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), }, { .freq = 5000, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), }, { .freq = 5010, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), }, { .freq = 5020, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), }, { .freq = 5030, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), }, { .freq = 5040, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), }, { .freq = 5050, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), }, { .freq = 5060, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), }, { .freq = 5070, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), }, { .freq = 5080, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), }, { .freq = 5090, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xff, 0x00), PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), }, { .freq = 5100, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), }, { .freq = 5110, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), }, { .freq = 5120, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), }, { .freq = 5130, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), }, { .freq = 5140, RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), }, { .freq = 5160, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), }, { .freq = 5170, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), }, { .freq = 5180, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), }, { .freq = 5190, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfe, 0x00), PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), }, { .freq = 5200, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), }, { .freq = 5210, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), }, { .freq = 5220, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), }, { .freq = 5230, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), }, { .freq = 5240, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), }, { .freq = 5250, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), }, { .freq = 5260, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), }, { .freq = 5270, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), }, { .freq = 5280, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), }, { .freq = 5290, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfc, 0x00), PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), }, { .freq = 5300, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), }, { .freq = 5310, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), }, { .freq = 5320, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), }, { .freq = 5330, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), }, { .freq = 5340, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), }, { .freq = 5350, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), }, { .freq = 5360, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), }, { .freq = 5370, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), }, { .freq = 5380, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), }, { .freq = 5390, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xfa, 0x00), PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), }, { .freq = 5400, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), }, { .freq = 5410, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), }, { .freq = 5420, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), }, { .freq = 5430, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), }, { .freq = 5440, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), }, { .freq = 5450, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), }, { .freq = 5460, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), }, { .freq = 5470, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), }, { .freq = 5480, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), }, { .freq = 5490, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, 0x00, 0xf8, 0x00), PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), }, { .freq = 5500, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), }, { .freq = 5510, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), }, { .freq = 5520, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), }, { .freq = 5530, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), }, { .freq = 5540, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), }, { .freq = 5550, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), }, { .freq = 5560, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), }, { .freq = 5570, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), }, { .freq = 5580, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), }, { .freq = 5590, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, 0x00, 0xf6, 0x00), PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), }, { .freq = 5600, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), }, { .freq = 5610, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), }, { .freq = 5620, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), }, { .freq = 5630, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), }, { .freq = 5640, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), }, { .freq = 5650, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), }, { .freq = 5660, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), }, { .freq = 5670, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), }, { .freq = 5680, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), }, { .freq = 5690, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, 0x00, 0xf4, 0x00), PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), }, { .freq = 5700, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), }, { .freq = 5710, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), }, { .freq = 5720, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5725, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5730, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), }, { .freq = 5735, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), }, { .freq = 5740, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), }, { .freq = 5745, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), }, { .freq = 5750, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), }, { .freq = 5755, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), }, { .freq = 5760, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5765, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5770, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), }, { .freq = 5775, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), }, { .freq = 5780, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), }, { .freq = 5785, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5790, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5795, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, 0x00, 0xf2, 0x00), PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), }, { .freq = 5800, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), }, { .freq = 5805, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), }, { .freq = 5810, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5815, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5820, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), }, { .freq = 5825, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), }, { .freq = 5830, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), }, { .freq = 5840, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), }, { .freq = 5850, RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), }, { .freq = 5860, RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), }, { .freq = 5870, RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), }, { .freq = 5880, RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), }, { .freq = 5890, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, 0x00, 0xf0, 0x00), PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), }, { .freq = 5900, RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00), PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), }, { .freq = 5910, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, 0x00, 0xf0, 0x00), PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), }, { .freq = 2412, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), }, { .freq = 2417, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), }, { .freq = 2422, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), }, { .freq = 2427, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), }, { .freq = 2432, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), }, { .freq = 2437, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), }, { .freq = 2442, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), }, { .freq = 2447, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), }, { .freq = 2452, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), }, { .freq = 2457, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), }, { .freq = 2462, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), }, { .freq = 2467, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), }, { .freq = 2472, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), }, { .freq = 2484, RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0e), PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), }, }; static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev5[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), }, { .freq = 4930, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), }, { .freq = 4940, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), }, { .freq = 4950, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), }, { .freq = 4960, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), }, { .freq = 4970, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), }, { .freq = 4980, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), }, { .freq = 4990, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), }, { .freq = 5000, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), }, { .freq = 5010, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), }, { .freq = 5020, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), }, { .freq = 5030, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), }, { .freq = 5040, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), }, { .freq = 5050, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), }, { .freq = 5060, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), }, { .freq = 5070, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), }, { .freq = 5080, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), }, { .freq = 5090, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), }, { .freq = 5100, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), }, { .freq = 5110, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), }, { .freq = 5120, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), }, { .freq = 5130, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), }, { .freq = 5140, RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), }, { .freq = 5160, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), }, { .freq = 5170, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), }, { .freq = 5180, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), }, { .freq = 5190, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), }, { .freq = 5200, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), }, { .freq = 5210, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), }, { .freq = 5220, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), }, { .freq = 5230, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6e, 0x00), PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), }, { .freq = 5240, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00), PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), }, { .freq = 5250, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00), PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), }, { .freq = 5260, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00), PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), }, { .freq = 5270, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), }, { .freq = 5280, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), }, { .freq = 5290, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), }, { .freq = 5300, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), }, { .freq = 5310, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), }, { .freq = 5320, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), }, { .freq = 5330, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00), PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), }, { .freq = 5340, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00), PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), }, { .freq = 5350, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00), PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), }, { .freq = 5360, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00), PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), }, { .freq = 5370, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5b, 0x00), PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), }, { .freq = 5380, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5a, 0x00), PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), }, { .freq = 5390, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5a, 0x00), PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), }, { .freq = 5400, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x5a, 0x00), PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), }, { .freq = 5410, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x5a, 0x00), PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), }, { .freq = 5420, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x5a, 0x00), PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), }, { .freq = 5430, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x59, 0x00), PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), }, { .freq = 5440, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x59, 0x00), PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), }, { .freq = 5450, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x59, 0x00), PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), }, { .freq = 5460, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), }, { .freq = 5470, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), }, { .freq = 5480, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), }, { .freq = 5490, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), }, { .freq = 5500, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), }, { .freq = 5510, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), }, { .freq = 5520, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), }, { .freq = 5530, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x78, 0x00), PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), }, { .freq = 5540, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00), PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), }, { .freq = 5550, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00), PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), }, { .freq = 5560, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00), PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), }, { .freq = 5570, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), }, { .freq = 5580, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), }, { .freq = 5590, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), }, { .freq = 5600, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), }, { .freq = 5610, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), }, { .freq = 5620, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), }, { .freq = 5630, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), }, { .freq = 5640, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x75, 0x00), PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), }, { .freq = 5650, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x75, 0x00), PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), }, { .freq = 5660, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x75, 0x00), PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), }, { .freq = 5670, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00), PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), }, { .freq = 5680, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00), PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), }, { .freq = 5690, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00), PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), }, { .freq = 5700, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00), PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), }, { .freq = 5710, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00), PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), }, { .freq = 5720, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00), PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5725, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x74, 0x00), PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5730, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x84, 0x00), PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), }, { .freq = 5735, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00), PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), }, { .freq = 5740, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00), PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), }, { .freq = 5745, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00), PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), }, { .freq = 5750, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00), PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), }, { .freq = 5755, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00), PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), }, { .freq = 5760, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x83, 0x00), PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5765, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5770, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), }, { .freq = 5775, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), }, { .freq = 5780, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), }, { .freq = 5785, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5790, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5795, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), }, { .freq = 5800, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), }, { .freq = 5805, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), }, { .freq = 5810, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5815, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5820, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), }, { .freq = 5825, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x82, 0x00), PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), }, { .freq = 5830, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00), PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), }, { .freq = 5840, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00), PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), }, { .freq = 5850, RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00), PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), }, { .freq = 5860, RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00), PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), }, { .freq = 5870, RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00), PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), }, { .freq = 5880, RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00), PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), }, { .freq = 5890, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00), PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), }, { .freq = 5900, RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00), PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), }, { .freq = 5910, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x71, 0x00), PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), }, { .freq = 2412, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b), PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), }, { .freq = 2417, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a), PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), }, { .freq = 2422, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a), PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), }, { .freq = 2427, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), }, { .freq = 2432, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), }, { .freq = 2437, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), }, { .freq = 2442, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), }, { .freq = 2447, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09), PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), }, { .freq = 2452, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09), PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), }, { .freq = 2457, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09), PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), }, { .freq = 2462, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09), PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), }, { .freq = 2467, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08), PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), }, { .freq = 2472, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08), PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), }, { .freq = 2484, RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08), PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), }, }; static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev6[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), }, { .freq = 4930, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), }, { .freq = 4940, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), }, { .freq = 4950, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), }, { .freq = 4960, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), }, { .freq = 4970, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), }, { .freq = 4980, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), }, { .freq = 4990, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), }, { .freq = 5000, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), }, { .freq = 5010, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), }, { .freq = 5020, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), }, { .freq = 5030, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), }, { .freq = 5040, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), }, { .freq = 5050, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), }, { .freq = 5060, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), }, { .freq = 5070, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), }, { .freq = 5080, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), }, { .freq = 5090, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), }, { .freq = 5100, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), }, { .freq = 5110, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), }, { .freq = 5120, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), }, { .freq = 5130, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), }, { .freq = 5140, RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), }, { .freq = 5160, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), }, { .freq = 5170, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), }, { .freq = 5180, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), }, { .freq = 5190, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), }, { .freq = 5200, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), }, { .freq = 5210, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), }, { .freq = 5220, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), }, { .freq = 5230, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), }, { .freq = 5240, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), }, { .freq = 5250, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), }, { .freq = 5260, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), }, { .freq = 5270, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), }, { .freq = 5280, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), }, { .freq = 5290, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), }, { .freq = 5300, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), }, { .freq = 5310, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), }, { .freq = 5320, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), }, { .freq = 5330, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), }, { .freq = 5340, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), }, { .freq = 5350, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), }, { .freq = 5360, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), }, { .freq = 5370, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), }, { .freq = 5380, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), }, { .freq = 5390, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), }, { .freq = 5400, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), }, { .freq = 5410, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), }, { .freq = 5420, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), }, { .freq = 5430, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), }, { .freq = 5440, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), }, { .freq = 5450, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), }, { .freq = 5460, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), }, { .freq = 5470, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), }, { .freq = 5480, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), }, { .freq = 5490, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), }, { .freq = 5500, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), }, { .freq = 5510, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), }, { .freq = 5520, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), }, { .freq = 5530, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), }, { .freq = 5540, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), }, { .freq = 5550, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), }, { .freq = 5560, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), }, { .freq = 5570, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), }, { .freq = 5580, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), }, { .freq = 5590, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), }, { .freq = 5600, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), }, { .freq = 5610, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), }, { .freq = 5620, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), }, { .freq = 5630, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), }, { .freq = 5640, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), }, { .freq = 5650, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), }, { .freq = 5660, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), }, { .freq = 5670, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), }, { .freq = 5680, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), }, { .freq = 5690, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), }, { .freq = 5700, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), }, { .freq = 5710, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), }, { .freq = 5720, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5725, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5730, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), }, { .freq = 5735, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00), PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), }, { .freq = 5740, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00), PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), }, { .freq = 5745, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00), PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), }, { .freq = 5750, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00), PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), }, { .freq = 5755, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00), PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), }, { .freq = 5760, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00), PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5765, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00), PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5770, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), }, { .freq = 5775, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), }, { .freq = 5780, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), }, { .freq = 5785, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5790, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5795, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), }, { .freq = 5800, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), }, { .freq = 5805, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), }, { .freq = 5810, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5815, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5820, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), }, { .freq = 5825, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00), PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), }, { .freq = 5830, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00), PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), }, { .freq = 5840, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), }, { .freq = 5850, RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), }, { .freq = 5860, RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), }, { .freq = 5870, RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), }, { .freq = 5880, RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), }, { .freq = 5890, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), }, { .freq = 5900, RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), }, { .freq = 5910, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), }, { .freq = 2412, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a), PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), }, { .freq = 2417, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a), PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), }, { .freq = 2422, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a), PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), }, { .freq = 2427, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), }, { .freq = 2432, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), }, { .freq = 2437, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), }, { .freq = 2442, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), }, { .freq = 2447, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09), PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), }, { .freq = 2452, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09), PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), }, { .freq = 2457, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09), PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), }, { .freq = 2462, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), }, { .freq = 2467, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), }, { .freq = 2472, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), }, { .freq = 2484, RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), }, }; static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev7_9[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), }, { .freq = 4930, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), }, { .freq = 4940, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), }, { .freq = 4950, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), }, { .freq = 4960, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), }, { .freq = 4970, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), }, { .freq = 4980, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), }, { .freq = 4990, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), }, { .freq = 5000, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), }, { .freq = 5010, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), }, { .freq = 5020, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), }, { .freq = 5030, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), }, { .freq = 5040, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), }, { .freq = 5050, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), }, { .freq = 5060, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), }, { .freq = 5070, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), }, { .freq = 5080, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), }, { .freq = 5090, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), }, { .freq = 5100, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), }, { .freq = 5110, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), }, { .freq = 5120, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), }, { .freq = 5130, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), }, { .freq = 5140, RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), }, { .freq = 5160, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), }, { .freq = 5170, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), }, { .freq = 5180, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), }, { .freq = 5190, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), }, { .freq = 5200, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), }, { .freq = 5210, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), }, { .freq = 5220, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, 0x00, 0x6e, 0x00), PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), }, { .freq = 5230, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6e, 0x00), PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), }, { .freq = 5240, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00), PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), }, { .freq = 5250, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00), PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), }, { .freq = 5260, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, 0x00, 0x6d, 0x00), PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), }, { .freq = 5270, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), }, { .freq = 5280, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), }, { .freq = 5290, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), }, { .freq = 5300, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), }, { .freq = 5310, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), }, { .freq = 5320, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6c, 0x00), PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), }, { .freq = 5330, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00), PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), }, { .freq = 5340, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, 0x00, 0x6b, 0x00), PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), }, { .freq = 5350, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00), PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), }, { .freq = 5360, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x6b, 0x00), PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), }, { .freq = 5370, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7b, 0x00), PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), }, { .freq = 5380, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7a, 0x00), PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), }, { .freq = 5390, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7a, 0x00), PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), }, { .freq = 5400, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, 0x00, 0x7a, 0x00), PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), }, { .freq = 5410, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x7a, 0x00), PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), }, { .freq = 5420, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, 0x00, 0x7a, 0x00), PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), }, { .freq = 5430, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x79, 0x00), PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), }, { .freq = 5440, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x79, 0x00), PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), }, { .freq = 5450, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, 0x00, 0x79, 0x00), PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), }, { .freq = 5460, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, 0x00, 0x79, 0x00), PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), }, { .freq = 5470, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x79, 0x00), PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), }, { .freq = 5480, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), }, { .freq = 5490, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), }, { .freq = 5500, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), }, { .freq = 5510, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), }, { .freq = 5520, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0x78, 0x00), PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), }, { .freq = 5530, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, 0x00, 0x78, 0x00), PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), }, { .freq = 5540, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00), PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), }, { .freq = 5550, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00), PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), }, { .freq = 5560, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, 0x00, 0x77, 0x00), PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), }, { .freq = 5570, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x76, 0x00), PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), }, { .freq = 5580, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00), PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), }, { .freq = 5590, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00), PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), }, { .freq = 5600, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00), PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), }, { .freq = 5610, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00), PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), }, { .freq = 5620, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00), PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), }, { .freq = 5630, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x86, 0x00), PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), }, { .freq = 5640, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, 0x00, 0x85, 0x00), PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), }, { .freq = 5650, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x85, 0x00), PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), }, { .freq = 5660, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x85, 0x00), PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), }, { .freq = 5670, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x84, 0x00), PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), }, { .freq = 5680, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x84, 0x00), PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), }, { .freq = 5690, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00), PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), }, { .freq = 5700, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00), PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), }, { .freq = 5710, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00), PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), }, { .freq = 5720, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00), PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5725, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00), PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5730, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x94, 0x00), PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), }, { .freq = 5735, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00), PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), }, { .freq = 5740, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00), PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), }, { .freq = 5745, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00), PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), }, { .freq = 5750, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00), PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), }, { .freq = 5755, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00), PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), }, { .freq = 5760, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x93, 0x00), PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5765, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5770, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), }, { .freq = 5775, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), }, { .freq = 5780, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), }, { .freq = 5785, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5790, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5795, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), }, { .freq = 5800, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), }, { .freq = 5805, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), }, { .freq = 5810, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5815, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5820, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), }, { .freq = 5825, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), }, { .freq = 5830, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), }, { .freq = 5840, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), }, { .freq = 5850, RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), }, { .freq = 5860, RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x92, 0x00), PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), }, { .freq = 5870, RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00), PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), }, { .freq = 5880, RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00), PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), }, { .freq = 5890, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00), PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), }, { .freq = 5900, RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00), PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), }, { .freq = 5910, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x91, 0x00), PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), }, { .freq = 2412, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0b), PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), }, { .freq = 2417, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a), PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), }, { .freq = 2422, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0f, 0x00, 0x0a), PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), }, { .freq = 2427, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), }, { .freq = 2432, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), }, { .freq = 2437, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), }, { .freq = 2442, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x0a), PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), }, { .freq = 2447, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09), PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), }, { .freq = 2452, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0e, 0x00, 0x09), PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), }, { .freq = 2457, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09), PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), }, { .freq = 2462, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x09), PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), }, { .freq = 2467, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08), PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), }, { .freq = 2472, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08), PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), }, { .freq = 2484, RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0d, 0x00, 0x08), PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), }, }; static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev8[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), }, { .freq = 4930, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), }, { .freq = 4940, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), }, { .freq = 4950, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), }, { .freq = 4960, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), }, { .freq = 4970, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), }, { .freq = 4980, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), }, { .freq = 4990, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), }, { .freq = 5000, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), }, { .freq = 5010, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), }, { .freq = 5020, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), }, { .freq = 5030, RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), }, { .freq = 5040, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), }, { .freq = 5050, RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), }, { .freq = 5060, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), }, { .freq = 5070, RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), }, { .freq = 5080, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), }, { .freq = 5090, RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), }, { .freq = 5100, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), }, { .freq = 5110, RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), }, { .freq = 5120, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), }, { .freq = 5130, RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), }, { .freq = 5140, RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, 0x00, 0x6f, 0x00), PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), }, { .freq = 5160, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), }, { .freq = 5170, RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), }, { .freq = 5180, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, 0x00, 0x6f, 0x00), PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), }, { .freq = 5190, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), }, { .freq = 5200, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), }, { .freq = 5210, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), }, { .freq = 5220, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), }, { .freq = 5230, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), }, { .freq = 5240, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), }, { .freq = 5250, RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), }, { .freq = 5260, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, 0x00, 0x6f, 0x00), PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), }, { .freq = 5270, RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), }, { .freq = 5280, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), }, { .freq = 5290, RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), }, { .freq = 5300, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), }, { .freq = 5310, RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), }, { .freq = 5320, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, 0x00, 0x6f, 0x00), PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), }, { .freq = 5330, RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), }, { .freq = 5340, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), }, { .freq = 5350, RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, 0x00, 0x6f, 0x00), PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), }, { .freq = 5360, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), }, { .freq = 5370, RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), }, { .freq = 5380, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), }, { .freq = 5390, RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), }, { .freq = 5400, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), }, { .freq = 5410, RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), }, { .freq = 5420, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), }, { .freq = 5430, RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, 0x00, 0x6f, 0x00), PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), }, { .freq = 5440, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), }, { .freq = 5450, RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), }, { .freq = 5460, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), }, { .freq = 5470, RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), }, { .freq = 5480, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), }, { .freq = 5490, RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), }, { .freq = 5500, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), }, { .freq = 5510, RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), }, { .freq = 5520, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), }, { .freq = 5530, RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), }, { .freq = 5540, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), }, { .freq = 5550, RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), }, { .freq = 5560, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), }, { .freq = 5570, RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, 0x00, 0x6f, 0x00), PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), }, { .freq = 5580, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), }, { .freq = 5590, RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), }, { .freq = 5600, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), }, { .freq = 5610, RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, 0x00, 0x6f, 0x00), PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), }, { .freq = 5620, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), }, { .freq = 5630, RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), }, { .freq = 5640, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), }, { .freq = 5650, RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, 0x00, 0x6f, 0x00), PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), }, { .freq = 5660, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), }, { .freq = 5670, RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), }, { .freq = 5680, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), }, { .freq = 5690, RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6f, 0x00), PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), }, { .freq = 5700, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), }, { .freq = 5710, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), }, { .freq = 5720, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5725, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), }, { .freq = 5730, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6e, 0x00), PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), }, { .freq = 5735, RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00), PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), }, { .freq = 5740, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00), PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), }, { .freq = 5745, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, 0x00, 0x6d, 0x00), PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), }, { .freq = 5750, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6d, 0x00), PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), }, { .freq = 5755, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00), PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), }, { .freq = 5760, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00), PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5765, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6c, 0x00), PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), }, { .freq = 5770, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), }, { .freq = 5775, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), }, { .freq = 5780, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), }, { .freq = 5785, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5790, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), }, { .freq = 5795, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), }, { .freq = 5800, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6b, 0x00), PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), }, { .freq = 5805, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), }, { .freq = 5810, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5815, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), }, { .freq = 5820, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x6a, 0x00), PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), }, { .freq = 5825, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00), PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), }, { .freq = 5830, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x00, 0x69, 0x00), PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), }, { .freq = 5840, RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), }, { .freq = 5850, RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), }, { .freq = 5860, RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x69, 0x00), PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), }, { .freq = 5870, RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), }, { .freq = 5880, RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), }, { .freq = 5890, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), }, { .freq = 5900, RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), }, { .freq = 5910, RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, 0x00, 0x68, 0x00), PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), }, { .freq = 2412, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a), PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), }, { .freq = 2417, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a), PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), }, { .freq = 2422, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0b, 0x00, 0x0a), PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), }, { .freq = 2427, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), }, { .freq = 2432, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), }, { .freq = 2437, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), }, { .freq = 2442, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x0a), PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), }, { .freq = 2447, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09), PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), }, { .freq = 2452, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09), PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), }, { .freq = 2457, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, 0x0a, 0x00, 0x09), PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), }, { .freq = 2462, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), }, { .freq = 2467, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), }, { .freq = 2472, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), }, { .freq = 2484, RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, 0x09, 0x00, 0x09), PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), }, }; static void b2056_upload_inittab(struct b43_wldev *dev, bool ghz5, bool ignore_uploadflag, u16 routing, const struct b2056_inittab_entry *e, unsigned int length) { unsigned int i; u16 value; for (i = 0; i < length; i++, e++) { if (!(e->flags & B2056_INITTAB_ENTRY_OK)) continue; if ((e->flags & B2056_INITTAB_UPLOAD) || ignore_uploadflag) { if (ghz5) value = e->ghz5; else value = e->ghz2; b43_radio_write(dev, routing | i, value); } } } void b2056_upload_inittabs(struct b43_wldev *dev, bool ghz5, bool ignore_uploadflag) { struct b2056_inittabs_pts *pts; if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) { B43_WARN_ON(1); return; } pts = &b2056_inittabs[dev->phy.rev]; b2056_upload_inittab(dev, ghz5, ignore_uploadflag, B2056_SYN, pts->syn, pts->syn_length); b2056_upload_inittab(dev, ghz5, ignore_uploadflag, B2056_TX0, pts->tx, pts->tx_length); b2056_upload_inittab(dev, ghz5, ignore_uploadflag, B2056_TX1, pts->tx, pts->tx_length); b2056_upload_inittab(dev, ghz5, ignore_uploadflag, B2056_RX0, pts->rx, pts->rx_length); b2056_upload_inittab(dev, ghz5, ignore_uploadflag, B2056_RX1, pts->rx, pts->rx_length); } void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5) { struct b2056_inittabs_pts *pts; const struct b2056_inittab_entry *e; if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) { B43_WARN_ON(1); return; } pts = &b2056_inittabs[dev->phy.rev]; e = &pts->syn[B2056_SYN_PLL_CP2]; b43_radio_write(dev, B2056_SYN_PLL_CP2, ghz5 ? e->ghz5 : e->ghz2); } const struct b43_nphy_channeltab_entry_rev3 * b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq) { const struct b43_nphy_channeltab_entry_rev3 *e; unsigned int length, i; switch (dev->phy.rev) { case 3: e = b43_nphy_channeltab_rev3; length = ARRAY_SIZE(b43_nphy_channeltab_rev3); break; case 4: e = b43_nphy_channeltab_rev4; length = ARRAY_SIZE(b43_nphy_channeltab_rev4); break; case 5: e = b43_nphy_channeltab_rev5; length = ARRAY_SIZE(b43_nphy_channeltab_rev5); break; case 6: e = b43_nphy_channeltab_rev6; length = ARRAY_SIZE(b43_nphy_channeltab_rev6); break; case 7: case 9: e = b43_nphy_channeltab_rev7_9; length = ARRAY_SIZE(b43_nphy_channeltab_rev7_9); break; case 8: e = b43_nphy_channeltab_rev8; length = ARRAY_SIZE(b43_nphy_channeltab_rev8); break; default: B43_WARN_ON(1); return NULL; } for (i = 0; i < length; i++, e++) { if (e->freq == freq) return e; } return NULL; } compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2055.h0000644000175000017500000003213512026211315022777 0ustar mcgrofmcgrof#ifndef B43_RADIO_2055_H_ #define B43_RADIO_2055_H_ #include #include "tables_nphy.h" #define B2055_GEN_SPARE 0x00 /* GEN spare */ #define B2055_SP_PINPD 0x02 /* SP PIN PD */ #define B2055_C1_SP_RSSI 0x03 /* SP RSSI Core 1 */ #define B2055_C1_SP_PDMISC 0x04 /* SP PD MISC Core 1 */ #define B2055_C2_SP_RSSI 0x05 /* SP RSSI Core 2 */ #define B2055_C2_SP_PDMISC 0x06 /* SP PD MISC Core 2 */ #define B2055_C1_SP_RXGC1 0x07 /* SP RX GC1 Core 1 */ #define B2055_C1_SP_RXGC2 0x08 /* SP RX GC2 Core 1 */ #define B2055_C2_SP_RXGC1 0x09 /* SP RX GC1 Core 2 */ #define B2055_C2_SP_RXGC2 0x0A /* SP RX GC2 Core 2 */ #define B2055_C1_SP_LPFBWSEL 0x0B /* SP LPF BW select Core 1 */ #define B2055_C2_SP_LPFBWSEL 0x0C /* SP LPF BW select Core 2 */ #define B2055_C1_SP_TXGC1 0x0D /* SP TX GC1 Core 1 */ #define B2055_C1_SP_TXGC2 0x0E /* SP TX GC2 Core 1 */ #define B2055_C2_SP_TXGC1 0x0F /* SP TX GC1 Core 2 */ #define B2055_C2_SP_TXGC2 0x10 /* SP TX GC2 Core 2 */ #define B2055_MASTER1 0x11 /* Master control 1 */ #define B2055_MASTER2 0x12 /* Master control 2 */ #define B2055_PD_LGEN 0x13 /* PD LGEN */ #define B2055_PD_PLLTS 0x14 /* PD PLL TS */ #define B2055_C1_PD_LGBUF 0x15 /* PD Core 1 LGBUF */ #define B2055_C1_PD_TX 0x16 /* PD Core 1 TX */ #define B2055_C1_PD_RXTX 0x17 /* PD Core 1 RXTX */ #define B2055_C1_PD_RSSIMISC 0x18 /* PD Core 1 RSSI MISC */ #define B2055_C2_PD_LGBUF 0x19 /* PD Core 2 LGBUF */ #define B2055_C2_PD_TX 0x1A /* PD Core 2 TX */ #define B2055_C2_PD_RXTX 0x1B /* PD Core 2 RXTX */ #define B2055_C2_PD_RSSIMISC 0x1C /* PD Core 2 RSSI MISC */ #define B2055_PWRDET_LGEN 0x1D /* PWRDET LGEN */ #define B2055_C1_PWRDET_LGBUF 0x1E /* PWRDET LGBUF Core 1 */ #define B2055_C1_PWRDET_RXTX 0x1F /* PWRDET RXTX Core 1 */ #define B2055_C2_PWRDET_LGBUF 0x20 /* PWRDET LGBUF Core 2 */ #define B2055_C2_PWRDET_RXTX 0x21 /* PWRDET RXTX Core 2 */ #define B2055_RRCCAL_CS 0x22 /* RRCCAL Control spare */ #define B2055_RRCCAL_NOPTSEL 0x23 /* RRCCAL N OPT SEL */ #define B2055_CAL_MISC 0x24 /* CAL MISC */ #define B2055_CAL_COUT 0x25 /* CAL Counter out */ #define B2055_CAL_COUT2 0x26 /* CAL Counter out 2 */ #define B2055_CAL_CVARCTL 0x27 /* CAL CVAR Control */ #define B2055_CAL_RVARCTL 0x28 /* CAL RVAR Control */ #define B2055_CAL_LPOCTL 0x29 /* CAL LPO Control */ #define B2055_CAL_TS 0x2A /* CAL TS */ #define B2055_CAL_RCCALRTS 0x2B /* CAL RCCAL READ TS */ #define B2055_CAL_RCALRTS 0x2C /* CAL RCAL READ TS */ #define B2055_PADDRV 0x2D /* PAD driver */ #define B2055_XOCTL1 0x2E /* XO Control 1 */ #define B2055_XOCTL2 0x2F /* XO Control 2 */ #define B2055_XOREGUL 0x30 /* XO Regulator */ #define B2055_XOMISC 0x31 /* XO misc */ #define B2055_PLL_LFC1 0x32 /* PLL LF C1 */ #define B2055_PLL_CALVTH 0x33 /* PLL CAL VTH */ #define B2055_PLL_LFC2 0x34 /* PLL LF C2 */ #define B2055_PLL_REF 0x35 /* PLL reference */ #define B2055_PLL_LFR1 0x36 /* PLL LF R1 */ #define B2055_PLL_PFDCP 0x37 /* PLL PFD CP */ #define B2055_PLL_IDAC_CPOPAMP 0x38 /* PLL IDAC CPOPAMP */ #define B2055_PLL_CPREG 0x39 /* PLL CP Regulator */ #define B2055_PLL_RCAL 0x3A /* PLL RCAL */ #define B2055_RF_PLLMOD0 0x3B /* RF PLL MOD0 */ #define B2055_RF_PLLMOD1 0x3C /* RF PLL MOD1 */ #define B2055_RF_MMDIDAC1 0x3D /* RF MMD IDAC 1 */ #define B2055_RF_MMDIDAC0 0x3E /* RF MMD IDAC 0 */ #define B2055_RF_MMDSP 0x3F /* RF MMD spare */ #define B2055_VCO_CAL1 0x40 /* VCO cal 1 */ #define B2055_VCO_CAL2 0x41 /* VCO cal 2 */ #define B2055_VCO_CAL3 0x42 /* VCO cal 3 */ #define B2055_VCO_CAL4 0x43 /* VCO cal 4 */ #define B2055_VCO_CAL5 0x44 /* VCO cal 5 */ #define B2055_VCO_CAL6 0x45 /* VCO cal 6 */ #define B2055_VCO_CAL7 0x46 /* VCO cal 7 */ #define B2055_VCO_CAL8 0x47 /* VCO cal 8 */ #define B2055_VCO_CAL9 0x48 /* VCO cal 9 */ #define B2055_VCO_CAL10 0x49 /* VCO cal 10 */ #define B2055_VCO_CAL11 0x4A /* VCO cal 11 */ #define B2055_VCO_CAL12 0x4B /* VCO cal 12 */ #define B2055_VCO_CAL13 0x4C /* VCO cal 13 */ #define B2055_VCO_CAL14 0x4D /* VCO cal 14 */ #define B2055_VCO_CAL15 0x4E /* VCO cal 15 */ #define B2055_VCO_CAL16 0x4F /* VCO cal 16 */ #define B2055_VCO_KVCO 0x50 /* VCO KVCO */ #define B2055_VCO_CAPTAIL 0x51 /* VCO CAP TAIL */ #define B2055_VCO_IDACVCO 0x52 /* VCO IDAC VCO */ #define B2055_VCO_REG 0x53 /* VCO Regulator */ #define B2055_PLL_RFVTH 0x54 /* PLL RF VTH */ #define B2055_LGBUF_CENBUF 0x55 /* LGBUF CEN BUF */ #define B2055_LGEN_TUNE1 0x56 /* LGEN tune 1 */ #define B2055_LGEN_TUNE2 0x57 /* LGEN tune 2 */ #define B2055_LGEN_IDAC1 0x58 /* LGEN IDAC 1 */ #define B2055_LGEN_IDAC2 0x59 /* LGEN IDAC 2 */ #define B2055_LGEN_BIASC 0x5A /* LGEN BIAS counter */ #define B2055_LGEN_BIASIDAC 0x5B /* LGEN BIAS IDAC */ #define B2055_LGEN_RCAL 0x5C /* LGEN RCAL */ #define B2055_LGEN_DIV 0x5D /* LGEN div */ #define B2055_LGEN_SPARE2 0x5E /* LGEN spare 2 */ #define B2055_C1_LGBUF_ATUNE 0x5F /* Core 1 LGBUF A tune */ #define B2055_C1_LGBUF_GTUNE 0x60 /* Core 1 LGBUF G tune */ #define B2055_C1_LGBUF_DIV 0x61 /* Core 1 LGBUF div */ #define B2055_C1_LGBUF_AIDAC 0x62 /* Core 1 LGBUF A IDAC */ #define B2055_C1_LGBUF_GIDAC 0x63 /* Core 1 LGBUF G IDAC */ #define B2055_C1_LGBUF_IDACFO 0x64 /* Core 1 LGBUF IDAC filter override */ #define B2055_C1_LGBUF_SPARE 0x65 /* Core 1 LGBUF spare */ #define B2055_C1_RX_RFSPC1 0x66 /* Core 1 RX RF SPC1 */ #define B2055_C1_RX_RFR1 0x67 /* Core 1 RX RF reg 1 */ #define B2055_C1_RX_RFR2 0x68 /* Core 1 RX RF reg 2 */ #define B2055_C1_RX_RFRCAL 0x69 /* Core 1 RX RF RCAL */ #define B2055_C1_RX_BB_BLCMP 0x6A /* Core 1 RX Baseband BUFI LPF CMP */ #define B2055_C1_RX_BB_LPF 0x6B /* Core 1 RX Baseband LPF */ #define B2055_C1_RX_BB_MIDACHP 0x6C /* Core 1 RX Baseband MIDAC High-pass */ #define B2055_C1_RX_BB_VGA1IDAC 0x6D /* Core 1 RX Baseband VGA1 IDAC */ #define B2055_C1_RX_BB_VGA2IDAC 0x6E /* Core 1 RX Baseband VGA2 IDAC */ #define B2055_C1_RX_BB_VGA3IDAC 0x6F /* Core 1 RX Baseband VGA3 IDAC */ #define B2055_C1_RX_BB_BUFOCTL 0x70 /* Core 1 RX Baseband BUFO Control */ #define B2055_C1_RX_BB_RCCALCTL 0x71 /* Core 1 RX Baseband RCCAL Control */ #define B2055_C1_RX_BB_RSSICTL1 0x72 /* Core 1 RX Baseband RSSI Control 1 */ #define B2055_C1_RX_BB_RSSICTL2 0x73 /* Core 1 RX Baseband RSSI Control 2 */ #define B2055_C1_RX_BB_RSSICTL3 0x74 /* Core 1 RX Baseband RSSI Control 3 */ #define B2055_C1_RX_BB_RSSICTL4 0x75 /* Core 1 RX Baseband RSSI Control 4 */ #define B2055_C1_RX_BB_RSSICTL5 0x76 /* Core 1 RX Baseband RSSI Control 5 */ #define B2055_C1_RX_BB_REG 0x77 /* Core 1 RX Baseband Regulator */ #define B2055_C1_RX_BB_SPARE1 0x78 /* Core 1 RX Baseband spare 1 */ #define B2055_C1_RX_TXBBRCAL 0x79 /* Core 1 RX TX BB RCAL */ #define B2055_C1_TX_RF_SPGA 0x7A /* Core 1 TX RF SGM PGA */ #define B2055_C1_TX_RF_SPAD 0x7B /* Core 1 TX RF SGM PAD */ #define B2055_C1_TX_RF_CNTPGA1 0x7C /* Core 1 TX RF counter PGA 1 */ #define B2055_C1_TX_RF_CNTPAD1 0x7D /* Core 1 TX RF counter PAD 1 */ #define B2055_C1_TX_RF_PGAIDAC 0x7E /* Core 1 TX RF PGA IDAC */ #define B2055_C1_TX_PGAPADTN 0x7F /* Core 1 TX PGA PAD TN */ #define B2055_C1_TX_PADIDAC1 0x80 /* Core 1 TX PAD IDAC 1 */ #define B2055_C1_TX_PADIDAC2 0x81 /* Core 1 TX PAD IDAC 2 */ #define B2055_C1_TX_MXBGTRIM 0x82 /* Core 1 TX MX B/G TRIM */ #define B2055_C1_TX_RF_RCAL 0x83 /* Core 1 TX RF RCAL */ #define B2055_C1_TX_RF_PADTSSI1 0x84 /* Core 1 TX RF PAD TSSI1 */ #define B2055_C1_TX_RF_PADTSSI2 0x85 /* Core 1 TX RF PAD TSSI2 */ #define B2055_C1_TX_RF_SPARE 0x86 /* Core 1 TX RF spare */ #define B2055_C1_TX_RF_IQCAL1 0x87 /* Core 1 TX RF I/Q CAL 1 */ #define B2055_C1_TX_RF_IQCAL2 0x88 /* Core 1 TX RF I/Q CAL 2 */ #define B2055_C1_TXBB_RCCAL 0x89 /* Core 1 TXBB RC CAL Control */ #define B2055_C1_TXBB_LPF1 0x8A /* Core 1 TXBB LPF 1 */ #define B2055_C1_TX_VOSCNCL 0x8B /* Core 1 TX VOS CNCL */ #define B2055_C1_TX_LPF_MXGMIDAC 0x8C /* Core 1 TX LPF MXGM IDAC */ #define B2055_C1_TX_BB_MXGM 0x8D /* Core 1 TX BB MXGM */ #define B2055_C2_LGBUF_ATUNE 0x8E /* Core 2 LGBUF A tune */ #define B2055_C2_LGBUF_GTUNE 0x8F /* Core 2 LGBUF G tune */ #define B2055_C2_LGBUF_DIV 0x90 /* Core 2 LGBUF div */ #define B2055_C2_LGBUF_AIDAC 0x91 /* Core 2 LGBUF A IDAC */ #define B2055_C2_LGBUF_GIDAC 0x92 /* Core 2 LGBUF G IDAC */ #define B2055_C2_LGBUF_IDACFO 0x93 /* Core 2 LGBUF IDAC filter override */ #define B2055_C2_LGBUF_SPARE 0x94 /* Core 2 LGBUF spare */ #define B2055_C2_RX_RFSPC1 0x95 /* Core 2 RX RF SPC1 */ #define B2055_C2_RX_RFR1 0x96 /* Core 2 RX RF reg 1 */ #define B2055_C2_RX_RFR2 0x97 /* Core 2 RX RF reg 2 */ #define B2055_C2_RX_RFRCAL 0x98 /* Core 2 RX RF RCAL */ #define B2055_C2_RX_BB_BLCMP 0x99 /* Core 2 RX Baseband BUFI LPF CMP */ #define B2055_C2_RX_BB_LPF 0x9A /* Core 2 RX Baseband LPF */ #define B2055_C2_RX_BB_MIDACHP 0x9B /* Core 2 RX Baseband MIDAC High-pass */ #define B2055_C2_RX_BB_VGA1IDAC 0x9C /* Core 2 RX Baseband VGA1 IDAC */ #define B2055_C2_RX_BB_VGA2IDAC 0x9D /* Core 2 RX Baseband VGA2 IDAC */ #define B2055_C2_RX_BB_VGA3IDAC 0x9E /* Core 2 RX Baseband VGA3 IDAC */ #define B2055_C2_RX_BB_BUFOCTL 0x9F /* Core 2 RX Baseband BUFO Control */ #define B2055_C2_RX_BB_RCCALCTL 0xA0 /* Core 2 RX Baseband RCCAL Control */ #define B2055_C2_RX_BB_RSSICTL1 0xA1 /* Core 2 RX Baseband RSSI Control 1 */ #define B2055_C2_RX_BB_RSSICTL2 0xA2 /* Core 2 RX Baseband RSSI Control 2 */ #define B2055_C2_RX_BB_RSSICTL3 0xA3 /* Core 2 RX Baseband RSSI Control 3 */ #define B2055_C2_RX_BB_RSSICTL4 0xA4 /* Core 2 RX Baseband RSSI Control 4 */ #define B2055_C2_RX_BB_RSSICTL5 0xA5 /* Core 2 RX Baseband RSSI Control 5 */ #define B2055_C2_RX_BB_REG 0xA6 /* Core 2 RX Baseband Regulator */ #define B2055_C2_RX_BB_SPARE1 0xA7 /* Core 2 RX Baseband spare 1 */ #define B2055_C2_RX_TXBBRCAL 0xA8 /* Core 2 RX TX BB RCAL */ #define B2055_C2_TX_RF_SPGA 0xA9 /* Core 2 TX RF SGM PGA */ #define B2055_C2_TX_RF_SPAD 0xAA /* Core 2 TX RF SGM PAD */ #define B2055_C2_TX_RF_CNTPGA1 0xAB /* Core 2 TX RF counter PGA 1 */ #define B2055_C2_TX_RF_CNTPAD1 0xAC /* Core 2 TX RF counter PAD 1 */ #define B2055_C2_TX_RF_PGAIDAC 0xAD /* Core 2 TX RF PGA IDAC */ #define B2055_C2_TX_PGAPADTN 0xAE /* Core 2 TX PGA PAD TN */ #define B2055_C2_TX_PADIDAC1 0xAF /* Core 2 TX PAD IDAC 1 */ #define B2055_C2_TX_PADIDAC2 0xB0 /* Core 2 TX PAD IDAC 2 */ #define B2055_C2_TX_MXBGTRIM 0xB1 /* Core 2 TX MX B/G TRIM */ #define B2055_C2_TX_RF_RCAL 0xB2 /* Core 2 TX RF RCAL */ #define B2055_C2_TX_RF_PADTSSI1 0xB3 /* Core 2 TX RF PAD TSSI1 */ #define B2055_C2_TX_RF_PADTSSI2 0xB4 /* Core 2 TX RF PAD TSSI2 */ #define B2055_C2_TX_RF_SPARE 0xB5 /* Core 2 TX RF spare */ #define B2055_C2_TX_RF_IQCAL1 0xB6 /* Core 2 TX RF I/Q CAL 1 */ #define B2055_C2_TX_RF_IQCAL2 0xB7 /* Core 2 TX RF I/Q CAL 2 */ #define B2055_C2_TXBB_RCCAL 0xB8 /* Core 2 TXBB RC CAL Control */ #define B2055_C2_TXBB_LPF1 0xB9 /* Core 2 TXBB LPF 1 */ #define B2055_C2_TX_VOSCNCL 0xBA /* Core 2 TX VOS CNCL */ #define B2055_C2_TX_LPF_MXGMIDAC 0xBB /* Core 2 TX LPF MXGM IDAC */ #define B2055_C2_TX_BB_MXGM 0xBC /* Core 2 TX BB MXGM */ #define B2055_PRG_GCHP21 0xBD /* PRG GC HPVGA23 21 */ #define B2055_PRG_GCHP22 0xBE /* PRG GC HPVGA23 22 */ #define B2055_PRG_GCHP23 0xBF /* PRG GC HPVGA23 23 */ #define B2055_PRG_GCHP24 0xC0 /* PRG GC HPVGA23 24 */ #define B2055_PRG_GCHP25 0xC1 /* PRG GC HPVGA23 25 */ #define B2055_PRG_GCHP26 0xC2 /* PRG GC HPVGA23 26 */ #define B2055_PRG_GCHP27 0xC3 /* PRG GC HPVGA23 27 */ #define B2055_PRG_GCHP28 0xC4 /* PRG GC HPVGA23 28 */ #define B2055_PRG_GCHP29 0xC5 /* PRG GC HPVGA23 29 */ #define B2055_PRG_GCHP30 0xC6 /* PRG GC HPVGA23 30 */ #define B2055_C1_LNA_GAINBST 0xCD /* Core 1 LNA GAINBST */ #define B2055_C1_B0NB_RSSIVCM 0xD2 /* Core 1 B0 narrow-band RSSI VCM */ #define B2055_C1_GENSPARE2 0xD6 /* Core 1 GEN spare 2 */ #define B2055_C2_LNA_GAINBST 0xD9 /* Core 2 LNA GAINBST */ #define B2055_C2_B0NB_RSSIVCM 0xDE /* Core 2 B0 narrow-band RSSI VCM */ #define B2055_C2_GENSPARE2 0xE2 /* Core 2 GEN spare 2 */ struct b43_nphy_channeltab_entry_rev2 { /* The channel number */ u8 channel; /* The channel frequency in MHz */ u16 freq; /* An unknown value */ u16 unk2; /* Radio register values on channelswitch */ u8 radio_pll_ref; u8 radio_rf_pllmod0; u8 radio_rf_pllmod1; u8 radio_vco_captail; u8 radio_vco_cal1; u8 radio_vco_cal2; u8 radio_pll_lfc1; u8 radio_pll_lfr1; u8 radio_pll_lfc2; u8 radio_lgbuf_cenbuf; u8 radio_lgen_tune1; u8 radio_lgen_tune2; u8 radio_c1_lgbuf_atune; u8 radio_c1_lgbuf_gtune; u8 radio_c1_rx_rfr1; u8 radio_c1_tx_pgapadtn; u8 radio_c1_tx_mxbgtrim; u8 radio_c2_lgbuf_atune; u8 radio_c2_lgbuf_gtune; u8 radio_c2_rx_rfr1; u8 radio_c2_tx_pgapadtn; u8 radio_c2_tx_mxbgtrim; /* PHY register values on channelswitch */ struct b43_phy_n_sfo_cfg phy_regs; }; /* Upload the default register value table. * If "ghz5" is true, we upload the 5Ghz table. Otherwise the 2.4Ghz * table is uploaded. If "ignore_uploadflag" is true, we upload any value * and ignore the "UPLOAD" flag. */ void b2055_upload_inittab(struct b43_wldev *dev, bool ghz5, bool ignore_uploadflag); /* Get the NPHY Channel Switch Table entry for a channel. * Returns NULL on failure to find an entry. */ const struct b43_nphy_channeltab_entry_rev2 * b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel); #endif /* B43_RADIO_2055_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/radio_2055.c0000644000175000017500000015141212026211315022772 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n PHY and radio device data tables Copyright (c) 2008 Michael Buesch Copyright (c) 2010 Rafał Miłecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "radio_2055.h" #include "phy_common.h" struct b2055_inittab_entry { /* Value to write if we use the 5GHz band. */ u16 ghz5; /* Value to write if we use the 2.4GHz band. */ u16 ghz2; /* Flags */ u8 flags; #define B2055_INITTAB_ENTRY_OK 0x01 #define B2055_INITTAB_UPLOAD 0x02 }; #define UPLOAD .flags = B2055_INITTAB_ENTRY_OK | B2055_INITTAB_UPLOAD #define NOUPLOAD .flags = B2055_INITTAB_ENTRY_OK static const struct b2055_inittab_entry b2055_inittab [] = { [B2055_SP_PINPD] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, [B2055_C1_SP_RSSI] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_SP_PDMISC] = { .ghz5 = 0x0027, .ghz2 = 0x0027, NOUPLOAD, }, [B2055_C2_SP_RSSI] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_SP_PDMISC] = { .ghz5 = 0x0027, .ghz2 = 0x0027, NOUPLOAD, }, [B2055_C1_SP_RXGC1] = { .ghz5 = 0x007F, .ghz2 = 0x007F, UPLOAD, }, [B2055_C1_SP_RXGC2] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2055_C2_SP_RXGC1] = { .ghz5 = 0x007F, .ghz2 = 0x007F, UPLOAD, }, [B2055_C2_SP_RXGC2] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, [B2055_C1_SP_LPFBWSEL] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2055_C2_SP_LPFBWSEL] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2055_C1_SP_TXGC1] = { .ghz5 = 0x004F, .ghz2 = 0x004F, UPLOAD, }, [B2055_C1_SP_TXGC2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, [B2055_C2_SP_TXGC1] = { .ghz5 = 0x004F, .ghz2 = 0x004F, UPLOAD, }, [B2055_C2_SP_TXGC2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, [B2055_MASTER1] = { .ghz5 = 0x00D0, .ghz2 = 0x00D0, NOUPLOAD, }, [B2055_MASTER2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2055_PD_LGEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_PD_PLLTS] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, [B2055_C1_PD_LGBUF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_PD_TX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_PD_RXTX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_PD_RSSIMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_PD_LGBUF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_PD_TX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_PD_RXTX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_PD_RSSIMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_PWRDET_LGEN] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, [B2055_C1_PWRDET_LGBUF] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, [B2055_C1_PWRDET_RXTX] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, [B2055_C2_PWRDET_LGBUF] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, [B2055_C2_PWRDET_RXTX] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, [B2055_RRCCAL_CS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_RRCCAL_NOPTSEL] = { .ghz5 = 0x002C, .ghz2 = 0x002C, NOUPLOAD, }, [B2055_CAL_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_COUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_COUT2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_CVARCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_RVARCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_LPOCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_TS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_RCCALRTS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_CAL_RCALRTS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_PADDRV] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, [B2055_XOCTL1] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, [B2055_XOCTL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_XOREGUL] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, [B2055_XOMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_PLL_LFC1] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, [B2055_PLL_CALVTH] = { .ghz5 = 0x0087, .ghz2 = 0x0087, NOUPLOAD, }, [B2055_PLL_LFC2] = { .ghz5 = 0x0009, .ghz2 = 0x0009, NOUPLOAD, }, [B2055_PLL_REF] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, [B2055_PLL_LFR1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, [B2055_PLL_PFDCP] = { .ghz5 = 0x0018, .ghz2 = 0x0018, UPLOAD, }, [B2055_PLL_IDAC_CPOPAMP] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_PLL_CPREG] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, [B2055_PLL_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_RF_PLLMOD0] = { .ghz5 = 0x009E, .ghz2 = 0x009E, NOUPLOAD, }, [B2055_RF_PLLMOD1] = { .ghz5 = 0x0009, .ghz2 = 0x0009, NOUPLOAD, }, [B2055_RF_MMDIDAC1] = { .ghz5 = 0x00C8, .ghz2 = 0x00C8, UPLOAD, }, [B2055_RF_MMDIDAC0] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_RF_MMDSP] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_CAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_CAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_CAL3] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, [B2055_VCO_CAL4] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2055_VCO_CAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, [B2055_VCO_CAL6] = { .ghz5 = 0x003E, .ghz2 = 0x003E, NOUPLOAD, }, [B2055_VCO_CAL7] = { .ghz5 = 0x003E, .ghz2 = 0x003E, NOUPLOAD, }, [B2055_VCO_CAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, [B2055_VCO_CAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, [B2055_VCO_CAL10] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2055_VCO_CAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2055_VCO_CAL12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_CAL13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_CAL14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_CAL15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_CAL16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_VCO_KVCO] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2055_VCO_CAPTAIL] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2055_VCO_IDACVCO] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_VCO_REG] = { .ghz5 = 0x0084, .ghz2 = 0x0084, UPLOAD, }, [B2055_PLL_RFVTH] = { .ghz5 = 0x00C3, .ghz2 = 0x00C3, NOUPLOAD, }, [B2055_LGBUF_CENBUF] = { .ghz5 = 0x008F, .ghz2 = 0x008F, NOUPLOAD, }, [B2055_LGEN_TUNE1] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, [B2055_LGEN_TUNE2] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, [B2055_LGEN_IDAC1] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_LGEN_IDAC2] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_LGEN_BIASC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_LGEN_BIASIDAC] = { .ghz5 = 0x00CC, .ghz2 = 0x00CC, NOUPLOAD, }, [B2055_LGEN_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_LGEN_DIV] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, [B2055_LGEN_SPARE2] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, [B2055_C1_LGBUF_ATUNE] = { .ghz5 = 0x00F8, .ghz2 = 0x00F8, NOUPLOAD, }, [B2055_C1_LGBUF_GTUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C1_LGBUF_DIV] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C1_LGBUF_AIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0008, UPLOAD, }, [B2055_C1_LGBUF_GIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C1_LGBUF_IDACFO] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_LGBUF_SPARE] = { .ghz5 = 0x0001, .ghz2 = 0x0001, UPLOAD, }, [B2055_C1_RX_RFSPC1] = { .ghz5 = 0x008A, .ghz2 = 0x008A, NOUPLOAD, }, [B2055_C1_RX_RFR1] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2055_C1_RX_RFR2] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2055_C1_RX_RFRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_C1_RX_BB_BLCMP] = { .ghz5 = 0x00A0, .ghz2 = 0x00A0, NOUPLOAD, }, [B2055_C1_RX_BB_LPF] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, [B2055_C1_RX_BB_MIDACHP] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, [B2055_C1_RX_BB_VGA1IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C1_RX_BB_VGA2IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C1_RX_BB_VGA3IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C1_RX_BB_BUFOCTL] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C1_RX_BB_RCCALCTL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2055_C1_RX_BB_RSSICTL1] = { .ghz5 = 0x006A, .ghz2 = 0x006A, UPLOAD, }, [B2055_C1_RX_BB_RSSICTL2] = { .ghz5 = 0x00AB, .ghz2 = 0x00AB, UPLOAD, }, [B2055_C1_RX_BB_RSSICTL3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, UPLOAD, }, [B2055_C1_RX_BB_RSSICTL4] = { .ghz5 = 0x00C1, .ghz2 = 0x00C1, UPLOAD, }, [B2055_C1_RX_BB_RSSICTL5] = { .ghz5 = 0x00AA, .ghz2 = 0x00AA, UPLOAD, }, [B2055_C1_RX_BB_REG] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, [B2055_C1_RX_BB_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_RX_TXBBRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_C1_TX_RF_SPGA] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2055_C1_TX_RF_SPAD] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2055_C1_TX_RF_CNTPGA1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2055_C1_TX_RF_CNTPAD1] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2055_C1_TX_RF_PGAIDAC] = { .ghz5 = 0x0097, .ghz2 = 0x0097, UPLOAD, }, [B2055_C1_TX_PGAPADTN] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2055_C1_TX_PADIDAC1] = { .ghz5 = 0x0014, .ghz2 = 0x0014, UPLOAD, }, [B2055_C1_TX_PADIDAC2] = { .ghz5 = 0x0033, .ghz2 = 0x0033, NOUPLOAD, }, [B2055_C1_TX_MXBGTRIM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C1_TX_RF_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_C1_TX_RF_PADTSSI1] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, [B2055_C1_TX_RF_PADTSSI2] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, [B2055_C1_TX_RF_SPARE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, [B2055_C1_TX_RF_IQCAL1] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C1_TX_RF_IQCAL2] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, [B2055_C1_TXBB_RCCAL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2055_C1_TXBB_LPF1] = { .ghz5 = 0x0028, .ghz2 = 0x0028, NOUPLOAD, }, [B2055_C1_TX_VOSCNCL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_TX_LPF_MXGMIDAC] = { .ghz5 = 0x004A, .ghz2 = 0x004A, NOUPLOAD, }, [B2055_C1_TX_BB_MXGM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_LGBUF_ATUNE] = { .ghz5 = 0x00F8, .ghz2 = 0x00F8, NOUPLOAD, }, [B2055_C2_LGBUF_GTUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C2_LGBUF_DIV] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C2_LGBUF_AIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0008, UPLOAD, }, [B2055_C2_LGBUF_GIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C2_LGBUF_IDACFO] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_LGBUF_SPARE] = { .ghz5 = 0x0001, .ghz2 = 0x0001, UPLOAD, }, [B2055_C2_RX_RFSPC1] = { .ghz5 = 0x008A, .ghz2 = 0x008A, NOUPLOAD, }, [B2055_C2_RX_RFR1] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2055_C2_RX_RFR2] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, [B2055_C2_RX_RFRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_C2_RX_BB_BLCMP] = { .ghz5 = 0x00A0, .ghz2 = 0x00A0, NOUPLOAD, }, [B2055_C2_RX_BB_LPF] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, [B2055_C2_RX_BB_MIDACHP] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, [B2055_C2_RX_BB_VGA1IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C2_RX_BB_VGA2IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C2_RX_BB_VGA3IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C2_RX_BB_BUFOCTL] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C2_RX_BB_RCCALCTL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2055_C2_RX_BB_RSSICTL1] = { .ghz5 = 0x006A, .ghz2 = 0x006A, UPLOAD, }, [B2055_C2_RX_BB_RSSICTL2] = { .ghz5 = 0x00AB, .ghz2 = 0x00AB, UPLOAD, }, [B2055_C2_RX_BB_RSSICTL3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, UPLOAD, }, [B2055_C2_RX_BB_RSSICTL4] = { .ghz5 = 0x00C1, .ghz2 = 0x00C1, UPLOAD, }, [B2055_C2_RX_BB_RSSICTL5] = { .ghz5 = 0x00AA, .ghz2 = 0x00AA, UPLOAD, }, [B2055_C2_RX_BB_REG] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, [B2055_C2_RX_BB_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_RX_TXBBRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_C2_TX_RF_SPGA] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2055_C2_TX_RF_SPAD] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, [B2055_C2_TX_RF_CNTPGA1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, [B2055_C2_TX_RF_CNTPAD1] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, [B2055_C2_TX_RF_PGAIDAC] = { .ghz5 = 0x0097, .ghz2 = 0x0097, UPLOAD, }, [B2055_C2_TX_PGAPADTN] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, [B2055_C2_TX_PADIDAC1] = { .ghz5 = 0x0014, .ghz2 = 0x0014, UPLOAD, }, [B2055_C2_TX_PADIDAC2] = { .ghz5 = 0x0033, .ghz2 = 0x0033, NOUPLOAD, }, [B2055_C2_TX_MXBGTRIM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [B2055_C2_TX_RF_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [B2055_C2_TX_RF_PADTSSI1] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, [B2055_C2_TX_RF_PADTSSI2] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, [B2055_C2_TX_RF_SPARE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, [B2055_C2_TX_RF_IQCAL1] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, [B2055_C2_TX_RF_IQCAL2] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, [B2055_C2_TXBB_RCCAL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2055_C2_TXBB_LPF1] = { .ghz5 = 0x0028, .ghz2 = 0x0028, NOUPLOAD, }, [B2055_C2_TX_VOSCNCL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_TX_LPF_MXGMIDAC] = { .ghz5 = 0x004A, .ghz2 = 0x004A, NOUPLOAD, }, [B2055_C2_TX_BB_MXGM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_PRG_GCHP21] = { .ghz5 = 0x0071, .ghz2 = 0x0071, NOUPLOAD, }, [B2055_PRG_GCHP22] = { .ghz5 = 0x0072, .ghz2 = 0x0072, NOUPLOAD, }, [B2055_PRG_GCHP23] = { .ghz5 = 0x0073, .ghz2 = 0x0073, NOUPLOAD, }, [B2055_PRG_GCHP24] = { .ghz5 = 0x0074, .ghz2 = 0x0074, NOUPLOAD, }, [B2055_PRG_GCHP25] = { .ghz5 = 0x0075, .ghz2 = 0x0075, NOUPLOAD, }, [B2055_PRG_GCHP26] = { .ghz5 = 0x0076, .ghz2 = 0x0076, NOUPLOAD, }, [B2055_PRG_GCHP27] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, [B2055_PRG_GCHP28] = { .ghz5 = 0x0078, .ghz2 = 0x0078, NOUPLOAD, }, [B2055_PRG_GCHP29] = { .ghz5 = 0x0079, .ghz2 = 0x0079, NOUPLOAD, }, [B2055_PRG_GCHP30] = { .ghz5 = 0x007A, .ghz2 = 0x007A, NOUPLOAD, }, [0xC7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xC8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xC9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xCA] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xCB] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xCC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_LNA_GAINBST] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xCE] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [0xCF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xD0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xD1] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2055_C1_B0NB_RSSIVCM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [0xD3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xD4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xD5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C1_GENSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xD7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xD8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_LNA_GAINBST] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xDA] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, [0xDB] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xDC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xDD] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, [B2055_C2_B0NB_RSSIVCM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, [0xDF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xE0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [0xE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2055_C2_GENSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; #define RADIOREGS(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, \ r12, r13, r14, r15, r16, r17, r18, r19, r20, r21) \ .radio_pll_ref = r0, \ .radio_rf_pllmod0 = r1, \ .radio_rf_pllmod1 = r2, \ .radio_vco_captail = r3, \ .radio_vco_cal1 = r4, \ .radio_vco_cal2 = r5, \ .radio_pll_lfc1 = r6, \ .radio_pll_lfr1 = r7, \ .radio_pll_lfc2 = r8, \ .radio_lgbuf_cenbuf = r9, \ .radio_lgen_tune1 = r10, \ .radio_lgen_tune2 = r11, \ .radio_c1_lgbuf_atune = r12, \ .radio_c1_lgbuf_gtune = r13, \ .radio_c1_rx_rfr1 = r14, \ .radio_c1_tx_pgapadtn = r15, \ .radio_c1_tx_mxbgtrim = r16, \ .radio_c2_lgbuf_atune = r17, \ .radio_c2_lgbuf_gtune = r18, \ .radio_c2_rx_rfr1 = r19, \ .radio_c2_tx_pgapadtn = r20, \ .radio_c2_tx_mxbgtrim = r21 #define PHYREGS(r0, r1, r2, r3, r4, r5) \ .phy_regs.phy_bw1a = r0, \ .phy_regs.phy_bw2 = r1, \ .phy_regs.phy_bw3 = r2, \ .phy_regs.phy_bw4 = r3, \ .phy_regs.phy_bw5 = r4, \ .phy_regs.phy_bw6 = r5 static const struct b43_nphy_channeltab_entry_rev2 b43_nphy_channeltab_rev2[] = { { .channel = 184, .freq = 4920, /* MHz */ .unk2 = 3280, RADIOREGS(0x71, 0xEC, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07B4, 0x07B0, 0x07AC, 0x0214, 0x0215, 0x0216), }, { .channel = 186, .freq = 4930, /* MHz */ .unk2 = 3287, RADIOREGS(0x71, 0xED, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07B8, 0x07B4, 0x07B0, 0x0213, 0x0214, 0x0215), }, { .channel = 188, .freq = 4940, /* MHz */ .unk2 = 3293, RADIOREGS(0x71, 0xEE, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07BC, 0x07B8, 0x07B4, 0x0212, 0x0213, 0x0214), }, { .channel = 190, .freq = 4950, /* MHz */ .unk2 = 3300, RADIOREGS(0x71, 0xEF, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07C0, 0x07BC, 0x07B8, 0x0211, 0x0212, 0x0213), }, { .channel = 192, .freq = 4960, /* MHz */ .unk2 = 3307, RADIOREGS(0x71, 0xF0, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07C4, 0x07C0, 0x07BC, 0x020F, 0x0211, 0x0212), }, { .channel = 194, .freq = 4970, /* MHz */ .unk2 = 3313, RADIOREGS(0x71, 0xF1, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07C8, 0x07C4, 0x07C0, 0x020E, 0x020F, 0x0211), }, { .channel = 196, .freq = 4980, /* MHz */ .unk2 = 3320, RADIOREGS(0x71, 0xF2, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07CC, 0x07C8, 0x07C4, 0x020D, 0x020E, 0x020F), }, { .channel = 198, .freq = 4990, /* MHz */ .unk2 = 3327, RADIOREGS(0x71, 0xF3, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07D0, 0x07CC, 0x07C8, 0x020C, 0x020D, 0x020E), }, { .channel = 200, .freq = 5000, /* MHz */ .unk2 = 3333, RADIOREGS(0x71, 0xF4, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07D4, 0x07D0, 0x07CC, 0x020B, 0x020C, 0x020D), }, { .channel = 202, .freq = 5010, /* MHz */ .unk2 = 3340, RADIOREGS(0x71, 0xF5, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07D8, 0x07D4, 0x07D0, 0x020A, 0x020B, 0x020C), }, { .channel = 204, .freq = 5020, /* MHz */ .unk2 = 3347, RADIOREGS(0x71, 0xF6, 0x01, 0x0E, 0xF7, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07DC, 0x07D8, 0x07D4, 0x0209, 0x020A, 0x020B), }, { .channel = 206, .freq = 5030, /* MHz */ .unk2 = 3353, RADIOREGS(0x71, 0xF7, 0x01, 0x0E, 0xF7, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07E0, 0x07DC, 0x07D8, 0x0208, 0x0209, 0x020A), }, { .channel = 208, .freq = 5040, /* MHz */ .unk2 = 3360, RADIOREGS(0x71, 0xF8, 0x01, 0x0D, 0xEF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07E4, 0x07E0, 0x07DC, 0x0207, 0x0208, 0x0209), }, { .channel = 210, .freq = 5050, /* MHz */ .unk2 = 3367, RADIOREGS(0x71, 0xF9, 0x01, 0x0D, 0xEF, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), PHYREGS(0x07E8, 0x07E4, 0x07E0, 0x0206, 0x0207, 0x0208), }, { .channel = 212, .freq = 5060, /* MHz */ .unk2 = 3373, RADIOREGS(0x71, 0xFA, 0x01, 0x0D, 0xE6, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E), PHYREGS(0x07EC, 0x07E8, 0x07E4, 0x0205, 0x0206, 0x0207), }, { .channel = 214, .freq = 5070, /* MHz */ .unk2 = 3380, RADIOREGS(0x71, 0xFB, 0x01, 0x0D, 0xE6, 0x01, 0x04, 0x0A, 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E), PHYREGS(0x07F0, 0x07EC, 0x07E8, 0x0204, 0x0205, 0x0206), }, { .channel = 216, .freq = 5080, /* MHz */ .unk2 = 3387, RADIOREGS(0x71, 0xFC, 0x01, 0x0D, 0xDE, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D), PHYREGS(0x07F4, 0x07F0, 0x07EC, 0x0203, 0x0204, 0x0205), }, { .channel = 218, .freq = 5090, /* MHz */ .unk2 = 3393, RADIOREGS(0x71, 0xFD, 0x01, 0x0D, 0xDE, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D), PHYREGS(0x07F8, 0x07F4, 0x07F0, 0x0202, 0x0203, 0x0204), }, { .channel = 220, .freq = 5100, /* MHz */ .unk2 = 3400, RADIOREGS(0x71, 0xFE, 0x01, 0x0C, 0xD6, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D), PHYREGS(0x07FC, 0x07F8, 0x07F4, 0x0201, 0x0202, 0x0203), }, { .channel = 222, .freq = 5110, /* MHz */ .unk2 = 3407, RADIOREGS(0x71, 0xFF, 0x01, 0x0C, 0xD6, 0x01, 0x04, 0x0A, 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D), PHYREGS(0x0800, 0x07FC, 0x07F8, 0x0200, 0x0201, 0x0202), }, { .channel = 224, .freq = 5120, /* MHz */ .unk2 = 3413, RADIOREGS(0x71, 0x00, 0x02, 0x0C, 0xCE, 0x01, 0x04, 0x0A, 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C), PHYREGS(0x0804, 0x0800, 0x07FC, 0x01FF, 0x0200, 0x0201), }, { .channel = 226, .freq = 5130, /* MHz */ .unk2 = 3420, RADIOREGS(0x71, 0x01, 0x02, 0x0C, 0xCE, 0x01, 0x04, 0x0A, 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C), PHYREGS(0x0808, 0x0804, 0x0800, 0x01FE, 0x01FF, 0x0200), }, { .channel = 228, .freq = 5140, /* MHz */ .unk2 = 3427, RADIOREGS(0x71, 0x02, 0x02, 0x0C, 0xC6, 0x01, 0x04, 0x0A, 0x00, 0x8D, 0x99, 0x99, 0xDD, 0x00, 0x0C, 0x0E, 0x8B, 0xDD, 0x00, 0x0C, 0x0E, 0x8B), PHYREGS(0x080C, 0x0808, 0x0804, 0x01FD, 0x01FE, 0x01FF), }, { .channel = 32, .freq = 5160, /* MHz */ .unk2 = 3440, RADIOREGS(0x71, 0x04, 0x02, 0x0B, 0xBE, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A), PHYREGS(0x0814, 0x0810, 0x080C, 0x01FB, 0x01FC, 0x01FD), }, { .channel = 34, .freq = 5170, /* MHz */ .unk2 = 3447, RADIOREGS(0x71, 0x05, 0x02, 0x0B, 0xBE, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A), PHYREGS(0x0818, 0x0814, 0x0810, 0x01FA, 0x01FB, 0x01FC), }, { .channel = 36, .freq = 5180, /* MHz */ .unk2 = 3453, RADIOREGS(0x71, 0x06, 0x02, 0x0B, 0xB6, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89), PHYREGS(0x081C, 0x0818, 0x0814, 0x01F9, 0x01FA, 0x01FB), }, { .channel = 38, .freq = 5190, /* MHz */ .unk2 = 3460, RADIOREGS(0x71, 0x07, 0x02, 0x0B, 0xB6, 0x01, 0x04, 0x0A, 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89), PHYREGS(0x0820, 0x081C, 0x0818, 0x01F8, 0x01F9, 0x01FA), }, { .channel = 40, .freq = 5200, /* MHz */ .unk2 = 3467, RADIOREGS(0x71, 0x08, 0x02, 0x0B, 0xAF, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89), PHYREGS(0x0824, 0x0820, 0x081C, 0x01F7, 0x01F8, 0x01F9), }, { .channel = 42, .freq = 5210, /* MHz */ .unk2 = 3473, RADIOREGS(0x71, 0x09, 0x02, 0x0B, 0xAF, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89), PHYREGS(0x0828, 0x0824, 0x0820, 0x01F6, 0x01F7, 0x01F8), }, { .channel = 44, .freq = 5220, /* MHz */ .unk2 = 3480, RADIOREGS(0x71, 0x0A, 0x02, 0x0A, 0xA7, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88), PHYREGS(0x082C, 0x0828, 0x0824, 0x01F5, 0x01F6, 0x01F7), }, { .channel = 46, .freq = 5230, /* MHz */ .unk2 = 3487, RADIOREGS(0x71, 0x0B, 0x02, 0x0A, 0xA7, 0x01, 0x04, 0x0A, 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88), PHYREGS(0x0830, 0x082C, 0x0828, 0x01F4, 0x01F5, 0x01F6), }, { .channel = 48, .freq = 5240, /* MHz */ .unk2 = 3493, RADIOREGS(0x71, 0x0C, 0x02, 0x0A, 0xA0, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87), PHYREGS(0x0834, 0x0830, 0x082C, 0x01F3, 0x01F4, 0x01F5), }, { .channel = 50, .freq = 5250, /* MHz */ .unk2 = 3500, RADIOREGS(0x71, 0x0D, 0x02, 0x0A, 0xA0, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87), PHYREGS(0x0838, 0x0834, 0x0830, 0x01F2, 0x01F3, 0x01F4), }, { .channel = 52, .freq = 5260, /* MHz */ .unk2 = 3507, RADIOREGS(0x71, 0x0E, 0x02, 0x0A, 0x98, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87), PHYREGS(0x083C, 0x0838, 0x0834, 0x01F1, 0x01F2, 0x01F3), }, { .channel = 54, .freq = 5270, /* MHz */ .unk2 = 3513, RADIOREGS(0x71, 0x0F, 0x02, 0x0A, 0x98, 0x01, 0x04, 0x0A, 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87), PHYREGS(0x0840, 0x083C, 0x0838, 0x01F0, 0x01F1, 0x01F2), }, { .channel = 56, .freq = 5280, /* MHz */ .unk2 = 3520, RADIOREGS(0x71, 0x10, 0x02, 0x09, 0x91, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, 0x08, 0x86), PHYREGS(0x0844, 0x0840, 0x083C, 0x01F0, 0x01F0, 0x01F1), }, { .channel = 58, .freq = 5290, /* MHz */ .unk2 = 3527, RADIOREGS(0x71, 0x11, 0x02, 0x09, 0x91, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, 0x08, 0x86), PHYREGS(0x0848, 0x0844, 0x0840, 0x01EF, 0x01F0, 0x01F0), }, { .channel = 60, .freq = 5300, /* MHz */ .unk2 = 3533, RADIOREGS(0x71, 0x12, 0x02, 0x09, 0x8A, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, 0x07, 0x85), PHYREGS(0x084C, 0x0848, 0x0844, 0x01EE, 0x01EF, 0x01F0), }, { .channel = 62, .freq = 5310, /* MHz */ .unk2 = 3540, RADIOREGS(0x71, 0x13, 0x02, 0x09, 0x8A, 0x01, 0x04, 0x0A, 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, 0x07, 0x85), PHYREGS(0x0850, 0x084C, 0x0848, 0x01ED, 0x01EE, 0x01EF), }, { .channel = 64, .freq = 5320, /* MHz */ .unk2 = 3547, RADIOREGS(0x71, 0x14, 0x02, 0x09, 0x83, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, 0x07, 0x84), PHYREGS(0x0854, 0x0850, 0x084C, 0x01EC, 0x01ED, 0x01EE), }, { .channel = 66, .freq = 5330, /* MHz */ .unk2 = 3553, RADIOREGS(0x71, 0x15, 0x02, 0x09, 0x83, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, 0x07, 0x84), PHYREGS(0x0858, 0x0854, 0x0850, 0x01EB, 0x01EC, 0x01ED), }, { .channel = 68, .freq = 5340, /* MHz */ .unk2 = 3560, RADIOREGS(0x71, 0x16, 0x02, 0x08, 0x7C, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, 0x06, 0x84), PHYREGS(0x085C, 0x0858, 0x0854, 0x01EA, 0x01EB, 0x01EC), }, { .channel = 70, .freq = 5350, /* MHz */ .unk2 = 3567, RADIOREGS(0x71, 0x17, 0x02, 0x08, 0x7C, 0x01, 0x04, 0x0A, 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, 0x06, 0x84), PHYREGS(0x0860, 0x085C, 0x0858, 0x01E9, 0x01EA, 0x01EB), }, { .channel = 72, .freq = 5360, /* MHz */ .unk2 = 3573, RADIOREGS(0x71, 0x18, 0x02, 0x08, 0x75, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, 0x05, 0x83), PHYREGS(0x0864, 0x0860, 0x085C, 0x01E8, 0x01E9, 0x01EA), }, { .channel = 74, .freq = 5370, /* MHz */ .unk2 = 3580, RADIOREGS(0x71, 0x19, 0x02, 0x08, 0x75, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, 0x05, 0x83), PHYREGS(0x0868, 0x0864, 0x0860, 0x01E7, 0x01E8, 0x01E9), }, { .channel = 76, .freq = 5380, /* MHz */ .unk2 = 3587, RADIOREGS(0x71, 0x1A, 0x02, 0x08, 0x6E, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, 0x04, 0x82), PHYREGS(0x086C, 0x0868, 0x0864, 0x01E6, 0x01E7, 0x01E8), }, { .channel = 78, .freq = 5390, /* MHz */ .unk2 = 3593, RADIOREGS(0x71, 0x1B, 0x02, 0x08, 0x6E, 0x01, 0x04, 0x0A, 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, 0x04, 0x82), PHYREGS(0x0870, 0x086C, 0x0868, 0x01E5, 0x01E6, 0x01E7), }, { .channel = 80, .freq = 5400, /* MHz */ .unk2 = 3600, RADIOREGS(0x71, 0x1C, 0x02, 0x07, 0x67, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, 0x04, 0x81), PHYREGS(0x0874, 0x0870, 0x086C, 0x01E5, 0x01E5, 0x01E6), }, { .channel = 82, .freq = 5410, /* MHz */ .unk2 = 3607, RADIOREGS(0x71, 0x1D, 0x02, 0x07, 0x67, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, 0x04, 0x81), PHYREGS(0x0878, 0x0874, 0x0870, 0x01E4, 0x01E5, 0x01E5), }, { .channel = 84, .freq = 5420, /* MHz */ .unk2 = 3613, RADIOREGS(0x71, 0x1E, 0x02, 0x07, 0x61, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, 0x03, 0x80), PHYREGS(0x087C, 0x0878, 0x0874, 0x01E3, 0x01E4, 0x01E5), }, { .channel = 86, .freq = 5430, /* MHz */ .unk2 = 3620, RADIOREGS(0x71, 0x1F, 0x02, 0x07, 0x61, 0x01, 0x04, 0x0A, 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, 0x03, 0x80), PHYREGS(0x0880, 0x087C, 0x0878, 0x01E2, 0x01E3, 0x01E4), }, { .channel = 88, .freq = 5440, /* MHz */ .unk2 = 3627, RADIOREGS(0x71, 0x20, 0x02, 0x07, 0x5A, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, 0x02, 0x80), PHYREGS(0x0884, 0x0880, 0x087C, 0x01E1, 0x01E2, 0x01E3), }, { .channel = 90, .freq = 5450, /* MHz */ .unk2 = 3633, RADIOREGS(0x71, 0x21, 0x02, 0x07, 0x5A, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, 0x02, 0x80), PHYREGS(0x0888, 0x0884, 0x0880, 0x01E0, 0x01E1, 0x01E2), }, { .channel = 92, .freq = 5460, /* MHz */ .unk2 = 3640, RADIOREGS(0x71, 0x22, 0x02, 0x06, 0x53, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, 0x01, 0x80), PHYREGS(0x088C, 0x0888, 0x0884, 0x01DF, 0x01E0, 0x01E1), }, { .channel = 94, .freq = 5470, /* MHz */ .unk2 = 3647, RADIOREGS(0x71, 0x23, 0x02, 0x06, 0x53, 0x01, 0x04, 0x0A, 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, 0x01, 0x80), PHYREGS(0x0890, 0x088C, 0x0888, 0x01DE, 0x01DF, 0x01E0), }, { .channel = 96, .freq = 5480, /* MHz */ .unk2 = 3653, RADIOREGS(0x71, 0x24, 0x02, 0x06, 0x4D, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), PHYREGS(0x0894, 0x0890, 0x088C, 0x01DD, 0x01DE, 0x01DF), }, { .channel = 98, .freq = 5490, /* MHz */ .unk2 = 3660, RADIOREGS(0x71, 0x25, 0x02, 0x06, 0x4D, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), PHYREGS(0x0898, 0x0894, 0x0890, 0x01DD, 0x01DD, 0x01DE), }, { .channel = 100, .freq = 5500, /* MHz */ .unk2 = 3667, RADIOREGS(0x71, 0x26, 0x02, 0x06, 0x47, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), PHYREGS(0x089C, 0x0898, 0x0894, 0x01DC, 0x01DD, 0x01DD), }, { .channel = 102, .freq = 5510, /* MHz */ .unk2 = 3673, RADIOREGS(0x71, 0x27, 0x02, 0x06, 0x47, 0x01, 0x04, 0x0A, 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), PHYREGS(0x08A0, 0x089C, 0x0898, 0x01DB, 0x01DC, 0x01DD), }, { .channel = 104, .freq = 5520, /* MHz */ .unk2 = 3680, RADIOREGS(0x71, 0x28, 0x02, 0x05, 0x40, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), PHYREGS(0x08A4, 0x08A0, 0x089C, 0x01DA, 0x01DB, 0x01DC), }, { .channel = 106, .freq = 5530, /* MHz */ .unk2 = 3687, RADIOREGS(0x71, 0x29, 0x02, 0x05, 0x40, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), PHYREGS(0x08A8, 0x08A4, 0x08A0, 0x01D9, 0x01DA, 0x01DB), }, { .channel = 108, .freq = 5540, /* MHz */ .unk2 = 3693, RADIOREGS(0x71, 0x2A, 0x02, 0x05, 0x3A, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), PHYREGS(0x08AC, 0x08A8, 0x08A4, 0x01D8, 0x01D9, 0x01DA), }, { .channel = 110, .freq = 5550, /* MHz */ .unk2 = 3700, RADIOREGS(0x71, 0x2B, 0x02, 0x05, 0x3A, 0x01, 0x04, 0x0A, 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), PHYREGS(0x08B0, 0x08AC, 0x08A8, 0x01D7, 0x01D8, 0x01D9), }, { .channel = 112, .freq = 5560, /* MHz */ .unk2 = 3707, RADIOREGS(0x71, 0x2C, 0x02, 0x05, 0x34, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), PHYREGS(0x08B4, 0x08B0, 0x08AC, 0x01D7, 0x01D7, 0x01D8), }, { .channel = 114, .freq = 5570, /* MHz */ .unk2 = 3713, RADIOREGS(0x71, 0x2D, 0x02, 0x05, 0x34, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), PHYREGS(0x08B8, 0x08B4, 0x08B0, 0x01D6, 0x01D7, 0x01D7), }, { .channel = 116, .freq = 5580, /* MHz */ .unk2 = 3720, RADIOREGS(0x71, 0x2E, 0x02, 0x04, 0x2E, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), PHYREGS(0x08BC, 0x08B8, 0x08B4, 0x01D5, 0x01D6, 0x01D7), }, { .channel = 118, .freq = 5590, /* MHz */ .unk2 = 3727, RADIOREGS(0x71, 0x2F, 0x02, 0x04, 0x2E, 0x01, 0x04, 0x0A, 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), PHYREGS(0x08C0, 0x08BC, 0x08B8, 0x01D4, 0x01D5, 0x01D6), }, { .channel = 120, .freq = 5600, /* MHz */ .unk2 = 3733, RADIOREGS(0x71, 0x30, 0x02, 0x04, 0x28, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, 0x00, 0x80), PHYREGS(0x08C4, 0x08C0, 0x08BC, 0x01D3, 0x01D4, 0x01D5), }, { .channel = 122, .freq = 5610, /* MHz */ .unk2 = 3740, RADIOREGS(0x71, 0x31, 0x02, 0x04, 0x28, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, 0x00, 0x80), PHYREGS(0x08C8, 0x08C4, 0x08C0, 0x01D2, 0x01D3, 0x01D4), }, { .channel = 124, .freq = 5620, /* MHz */ .unk2 = 3747, RADIOREGS(0x71, 0x32, 0x02, 0x04, 0x21, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08CC, 0x08C8, 0x08C4, 0x01D2, 0x01D2, 0x01D3), }, { .channel = 126, .freq = 5630, /* MHz */ .unk2 = 3753, RADIOREGS(0x71, 0x33, 0x02, 0x04, 0x21, 0x01, 0x04, 0x0A, 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08D0, 0x08CC, 0x08C8, 0x01D1, 0x01D2, 0x01D2), }, { .channel = 128, .freq = 5640, /* MHz */ .unk2 = 3760, RADIOREGS(0x71, 0x34, 0x02, 0x03, 0x1C, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08D4, 0x08D0, 0x08CC, 0x01D0, 0x01D1, 0x01D2), }, { .channel = 130, .freq = 5650, /* MHz */ .unk2 = 3767, RADIOREGS(0x71, 0x35, 0x02, 0x03, 0x1C, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08D8, 0x08D4, 0x08D0, 0x01CF, 0x01D0, 0x01D1), }, { .channel = 132, .freq = 5660, /* MHz */ .unk2 = 3773, RADIOREGS(0x71, 0x36, 0x02, 0x03, 0x16, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08DC, 0x08D8, 0x08D4, 0x01CE, 0x01CF, 0x01D0), }, { .channel = 134, .freq = 5670, /* MHz */ .unk2 = 3780, RADIOREGS(0x71, 0x37, 0x02, 0x03, 0x16, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08E0, 0x08DC, 0x08D8, 0x01CE, 0x01CE, 0x01CF), }, { .channel = 136, .freq = 5680, /* MHz */ .unk2 = 3787, RADIOREGS(0x71, 0x38, 0x02, 0x03, 0x10, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08E4, 0x08E0, 0x08DC, 0x01CD, 0x01CE, 0x01CE), }, { .channel = 138, .freq = 5690, /* MHz */ .unk2 = 3793, RADIOREGS(0x71, 0x39, 0x02, 0x03, 0x10, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08E8, 0x08E4, 0x08E0, 0x01CC, 0x01CD, 0x01CE), }, { .channel = 140, .freq = 5700, /* MHz */ .unk2 = 3800, RADIOREGS(0x71, 0x3A, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08EC, 0x08E8, 0x08E4, 0x01CB, 0x01CC, 0x01CD), }, { .channel = 142, .freq = 5710, /* MHz */ .unk2 = 3807, RADIOREGS(0x71, 0x3B, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08F0, 0x08EC, 0x08E8, 0x01CA, 0x01CB, 0x01CC), }, { .channel = 144, .freq = 5720, /* MHz */ .unk2 = 3813, RADIOREGS(0x71, 0x3C, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08F4, 0x08F0, 0x08EC, 0x01C9, 0x01CA, 0x01CB), }, { .channel = 145, .freq = 5725, /* MHz */ .unk2 = 3817, RADIOREGS(0x72, 0x79, 0x04, 0x02, 0x03, 0x01, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08F6, 0x08F2, 0x08EE, 0x01C9, 0x01CA, 0x01CB), }, { .channel = 146, .freq = 5730, /* MHz */ .unk2 = 3820, RADIOREGS(0x71, 0x3D, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08F8, 0x08F4, 0x08F0, 0x01C9, 0x01C9, 0x01CA), }, { .channel = 147, .freq = 5735, /* MHz */ .unk2 = 3823, RADIOREGS(0x72, 0x7B, 0x04, 0x02, 0x03, 0x01, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08FA, 0x08F6, 0x08F2, 0x01C8, 0x01C9, 0x01CA), }, { .channel = 148, .freq = 5740, /* MHz */ .unk2 = 3827, RADIOREGS(0x71, 0x3E, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08FC, 0x08F8, 0x08F4, 0x01C8, 0x01C9, 0x01C9), }, { .channel = 149, .freq = 5745, /* MHz */ .unk2 = 3830, RADIOREGS(0x72, 0x7D, 0x04, 0x02, 0xFE, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x08FE, 0x08FA, 0x08F6, 0x01C8, 0x01C8, 0x01C9), }, { .channel = 150, .freq = 5750, /* MHz */ .unk2 = 3833, RADIOREGS(0x71, 0x3F, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0900, 0x08FC, 0x08F8, 0x01C7, 0x01C8, 0x01C9), }, { .channel = 151, .freq = 5755, /* MHz */ .unk2 = 3837, RADIOREGS(0x72, 0x7F, 0x04, 0x02, 0xFE, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0902, 0x08FE, 0x08FA, 0x01C7, 0x01C8, 0x01C8), }, { .channel = 152, .freq = 5760, /* MHz */ .unk2 = 3840, RADIOREGS(0x71, 0x40, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0904, 0x0900, 0x08FC, 0x01C6, 0x01C7, 0x01C8), }, { .channel = 153, .freq = 5765, /* MHz */ .unk2 = 3843, RADIOREGS(0x72, 0x81, 0x04, 0x02, 0xF8, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0906, 0x0902, 0x08FE, 0x01C6, 0x01C7, 0x01C8), }, { .channel = 154, .freq = 5770, /* MHz */ .unk2 = 3847, RADIOREGS(0x71, 0x41, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0908, 0x0904, 0x0900, 0x01C6, 0x01C6, 0x01C7), }, { .channel = 155, .freq = 5775, /* MHz */ .unk2 = 3850, RADIOREGS(0x72, 0x83, 0x04, 0x02, 0xF8, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x090A, 0x0906, 0x0902, 0x01C5, 0x01C6, 0x01C7), }, { .channel = 156, .freq = 5780, /* MHz */ .unk2 = 3853, RADIOREGS(0x71, 0x42, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x090C, 0x0908, 0x0904, 0x01C5, 0x01C6, 0x01C6), }, { .channel = 157, .freq = 5785, /* MHz */ .unk2 = 3857, RADIOREGS(0x72, 0x85, 0x04, 0x02, 0xF2, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x090E, 0x090A, 0x0906, 0x01C4, 0x01C5, 0x01C6), }, { .channel = 158, .freq = 5790, /* MHz */ .unk2 = 3860, RADIOREGS(0x71, 0x43, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0910, 0x090C, 0x0908, 0x01C4, 0x01C5, 0x01C6), }, { .channel = 159, .freq = 5795, /* MHz */ .unk2 = 3863, RADIOREGS(0x72, 0x87, 0x04, 0x02, 0xF2, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0912, 0x090E, 0x090A, 0x01C4, 0x01C4, 0x01C5), }, { .channel = 160, .freq = 5800, /* MHz */ .unk2 = 3867, RADIOREGS(0x71, 0x44, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0914, 0x0910, 0x090C, 0x01C3, 0x01C4, 0x01C5), }, { .channel = 161, .freq = 5805, /* MHz */ .unk2 = 3870, RADIOREGS(0x72, 0x89, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0916, 0x0912, 0x090E, 0x01C3, 0x01C4, 0x01C4), }, { .channel = 162, .freq = 5810, /* MHz */ .unk2 = 3873, RADIOREGS(0x71, 0x45, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0918, 0x0914, 0x0910, 0x01C2, 0x01C3, 0x01C4), }, { .channel = 163, .freq = 5815, /* MHz */ .unk2 = 3877, RADIOREGS(0x72, 0x8B, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x091A, 0x0916, 0x0912, 0x01C2, 0x01C3, 0x01C4), }, { .channel = 164, .freq = 5820, /* MHz */ .unk2 = 3880, RADIOREGS(0x71, 0x46, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x091C, 0x0918, 0x0914, 0x01C2, 0x01C2, 0x01C3), }, { .channel = 165, .freq = 5825, /* MHz */ .unk2 = 3883, RADIOREGS(0x72, 0x8D, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x091E, 0x091A, 0x0916, 0x01C1, 0x01C2, 0x01C3), }, { .channel = 166, .freq = 5830, /* MHz */ .unk2 = 3887, RADIOREGS(0x71, 0x47, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0920, 0x091C, 0x0918, 0x01C1, 0x01C2, 0x01C2), }, { .channel = 168, .freq = 5840, /* MHz */ .unk2 = 3893, RADIOREGS(0x71, 0x48, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0924, 0x0920, 0x091C, 0x01C0, 0x01C1, 0x01C2), }, { .channel = 170, .freq = 5850, /* MHz */ .unk2 = 3900, RADIOREGS(0x71, 0x49, 0x02, 0x01, 0xE0, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0928, 0x0924, 0x0920, 0x01BF, 0x01C0, 0x01C1), }, { .channel = 172, .freq = 5860, /* MHz */ .unk2 = 3907, RADIOREGS(0x71, 0x4A, 0x02, 0x01, 0xDE, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x092C, 0x0928, 0x0924, 0x01BF, 0x01BF, 0x01C0), }, { .channel = 174, .freq = 5870, /* MHz */ .unk2 = 3913, RADIOREGS(0x71, 0x4B, 0x02, 0x00, 0xDB, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0930, 0x092C, 0x0928, 0x01BE, 0x01BF, 0x01BF), }, { .channel = 176, .freq = 5880, /* MHz */ .unk2 = 3920, RADIOREGS(0x71, 0x4C, 0x02, 0x00, 0xD8, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0934, 0x0930, 0x092C, 0x01BD, 0x01BE, 0x01BF), }, { .channel = 178, .freq = 5890, /* MHz */ .unk2 = 3927, RADIOREGS(0x71, 0x4D, 0x02, 0x00, 0xD6, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0938, 0x0934, 0x0930, 0x01BC, 0x01BD, 0x01BE), }, { .channel = 180, .freq = 5900, /* MHz */ .unk2 = 3933, RADIOREGS(0x71, 0x4E, 0x02, 0x00, 0xD3, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x093C, 0x0938, 0x0934, 0x01BC, 0x01BC, 0x01BD), }, { .channel = 182, .freq = 5910, /* MHz */ .unk2 = 3940, RADIOREGS(0x71, 0x4F, 0x02, 0x00, 0xD6, 0x00, 0x04, 0x0A, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), PHYREGS(0x0940, 0x093C, 0x0938, 0x01BB, 0x01BC, 0x01BC), }, { .channel = 1, .freq = 2412, /* MHz */ .unk2 = 3216, RADIOREGS(0x73, 0x6C, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0D, 0x0C, 0x80, 0xFF, 0x88, 0x0D, 0x0C, 0x80), PHYREGS(0x03C9, 0x03C5, 0x03C1, 0x043A, 0x043F, 0x0443), }, { .channel = 2, .freq = 2417, /* MHz */ .unk2 = 3223, RADIOREGS(0x73, 0x71, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0B, 0x80, 0xFF, 0x88, 0x0C, 0x0B, 0x80), PHYREGS(0x03CB, 0x03C7, 0x03C3, 0x0438, 0x043D, 0x0441), }, { .channel = 3, .freq = 2422, /* MHz */ .unk2 = 3229, RADIOREGS(0x73, 0x76, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80), PHYREGS(0x03CD, 0x03C9, 0x03C5, 0x0436, 0x043A, 0x043F), }, { .channel = 4, .freq = 2427, /* MHz */ .unk2 = 3236, RADIOREGS(0x73, 0x7B, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80), PHYREGS(0x03CF, 0x03CB, 0x03C7, 0x0434, 0x0438, 0x043D), }, { .channel = 5, .freq = 2432, /* MHz */ .unk2 = 3243, RADIOREGS(0x73, 0x80, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x09, 0x80, 0xFF, 0x88, 0x0C, 0x09, 0x80), PHYREGS(0x03D1, 0x03CD, 0x03C9, 0x0431, 0x0436, 0x043A), }, { .channel = 6, .freq = 2437, /* MHz */ .unk2 = 3249, RADIOREGS(0x73, 0x85, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0B, 0x08, 0x80, 0xFF, 0x88, 0x0B, 0x08, 0x80), PHYREGS(0x03D3, 0x03CF, 0x03CB, 0x042F, 0x0434, 0x0438), }, { .channel = 7, .freq = 2442, /* MHz */ .unk2 = 3256, RADIOREGS(0x73, 0x8A, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x07, 0x80, 0xFF, 0x88, 0x0A, 0x07, 0x80), PHYREGS(0x03D5, 0x03D1, 0x03CD, 0x042D, 0x0431, 0x0436), }, { .channel = 8, .freq = 2447, /* MHz */ .unk2 = 3263, RADIOREGS(0x73, 0x8F, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x06, 0x80, 0xFF, 0x88, 0x0A, 0x06, 0x80), PHYREGS(0x03D7, 0x03D3, 0x03CF, 0x042B, 0x042F, 0x0434), }, { .channel = 9, .freq = 2452, /* MHz */ .unk2 = 3269, RADIOREGS(0x73, 0x94, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x09, 0x06, 0x80, 0xFF, 0x88, 0x09, 0x06, 0x80), PHYREGS(0x03D9, 0x03D5, 0x03D1, 0x0429, 0x042D, 0x0431), }, { .channel = 10, .freq = 2457, /* MHz */ .unk2 = 3276, RADIOREGS(0x73, 0x99, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x05, 0x80, 0xFF, 0x88, 0x08, 0x05, 0x80), PHYREGS(0x03DB, 0x03D7, 0x03D3, 0x0427, 0x042B, 0x042F), }, { .channel = 11, .freq = 2462, /* MHz */ .unk2 = 3283, RADIOREGS(0x73, 0x9E, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x04, 0x80, 0xFF, 0x88, 0x08, 0x04, 0x80), PHYREGS(0x03DD, 0x03D9, 0x03D5, 0x0424, 0x0429, 0x042D), }, { .channel = 12, .freq = 2467, /* MHz */ .unk2 = 3289, RADIOREGS(0x73, 0xA3, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x03, 0x80, 0xFF, 0x88, 0x08, 0x03, 0x80), PHYREGS(0x03DF, 0x03DB, 0x03D7, 0x0422, 0x0427, 0x042B), }, { .channel = 13, .freq = 2472, /* MHz */ .unk2 = 3296, RADIOREGS(0x73, 0xA8, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x03, 0x80, 0xFF, 0x88, 0x07, 0x03, 0x80), PHYREGS(0x03E1, 0x03DD, 0x03D9, 0x0420, 0x0424, 0x0429), }, { .channel = 14, .freq = 2484, /* MHz */ .unk2 = 3312, RADIOREGS(0x73, 0xB4, 0x09, 0x0F, 0xFF, 0x01, 0x07, 0x15, 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x01, 0x80, 0xFF, 0x88, 0x07, 0x01, 0x80), PHYREGS(0x03E6, 0x03E2, 0x03DE, 0x041B, 0x041F, 0x0424), }, }; void b2055_upload_inittab(struct b43_wldev *dev, bool ghz5, bool ignore_uploadflag) { const struct b2055_inittab_entry *e; unsigned int i, writes = 0; u16 value; for (i = 0; i < ARRAY_SIZE(b2055_inittab); i++) { e = &(b2055_inittab[i]); if (!(e->flags & B2055_INITTAB_ENTRY_OK)) continue; if ((e->flags & B2055_INITTAB_UPLOAD) || ignore_uploadflag) { if (ghz5) value = e->ghz5; else value = e->ghz2; b43_radio_write16(dev, i, value); if (++writes % 4 == 0) b43_read32(dev, B43_MMIO_MACCTL); /* flush */ } } } const struct b43_nphy_channeltab_entry_rev2 * b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel) { const struct b43_nphy_channeltab_entry_rev2 *e; unsigned int i; for (i = 0; i < ARRAY_SIZE(b43_nphy_channeltab_rev2); i++) { e = &(b43_nphy_channeltab_rev2[i]); if (e->channel == channel) return e; } return NULL; } compat-drivers-2012-09-18/drivers/net/wireless/b43/pio.h0000644000175000017500000001063112026211315022012 0ustar mcgrofmcgrof#ifndef B43_PIO_H_ #define B43_PIO_H_ #include "b43.h" #include #include #include #include /*** Registers for PIO queues up to revision 7. ***/ /* TX queue. */ #define B43_PIO_TXCTL 0x00 #define B43_PIO_TXCTL_WRITELO 0x0001 #define B43_PIO_TXCTL_WRITEHI 0x0002 #define B43_PIO_TXCTL_EOF 0x0004 #define B43_PIO_TXCTL_FREADY 0x0008 #define B43_PIO_TXCTL_FLUSHREQ 0x0020 #define B43_PIO_TXCTL_FLUSHPEND 0x0040 #define B43_PIO_TXCTL_SUSPREQ 0x0080 #define B43_PIO_TXCTL_QSUSP 0x0100 #define B43_PIO_TXCTL_COMMCNT 0xFC00 #define B43_PIO_TXCTL_COMMCNT_SHIFT 10 #define B43_PIO_TXDATA 0x02 #define B43_PIO_TXQBUFSIZE 0x04 /* RX queue. */ #define B43_PIO_RXCTL 0x00 #define B43_PIO_RXCTL_FRAMERDY 0x0001 #define B43_PIO_RXCTL_DATARDY 0x0002 #define B43_PIO_RXDATA 0x02 /*** Registers for PIO queues revision 8 and later. ***/ /* TX queue */ #define B43_PIO8_TXCTL 0x00 #define B43_PIO8_TXCTL_0_7 0x00000001 #define B43_PIO8_TXCTL_8_15 0x00000002 #define B43_PIO8_TXCTL_16_23 0x00000004 #define B43_PIO8_TXCTL_24_31 0x00000008 #define B43_PIO8_TXCTL_EOF 0x00000010 #define B43_PIO8_TXCTL_FREADY 0x00000080 #define B43_PIO8_TXCTL_SUSPREQ 0x00000100 #define B43_PIO8_TXCTL_QSUSP 0x00000200 #define B43_PIO8_TXCTL_FLUSHREQ 0x00000400 #define B43_PIO8_TXCTL_FLUSHPEND 0x00000800 #define B43_PIO8_TXDATA 0x04 /* RX queue */ #define B43_PIO8_RXCTL 0x00 #define B43_PIO8_RXCTL_FRAMERDY 0x00000001 #define B43_PIO8_RXCTL_DATARDY 0x00000002 #define B43_PIO8_RXDATA 0x04 /* The maximum number of TX-packets the HW can handle. */ #define B43_PIO_MAX_NR_TXPACKETS 32 struct b43_pio_txpacket { /* Pointer to the TX queue we belong to. */ struct b43_pio_txqueue *queue; /* The TX data packet. */ struct sk_buff *skb; /* Index in the (struct b43_pio_txqueue)->packets array. */ u8 index; struct list_head list; }; struct b43_pio_txqueue { struct b43_wldev *dev; u16 mmio_base; /* The device queue buffer size in bytes. */ u16 buffer_size; /* The number of used bytes in the device queue buffer. */ u16 buffer_used; /* The number of packets that can still get queued. * This is decremented on queueing a packet and incremented * after receiving the transmit status. */ u16 free_packet_slots; /* True, if the mac80211 queue was stopped due to overflow at TX. */ bool stopped; /* Our b43 queue index number */ u8 index; /* The mac80211 QoS queue priority. */ u8 queue_prio; /* Buffer for TX packet meta data. */ struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS]; struct list_head packets_list; /* Shortcut to the 802.11 core revision. This is to * avoid horrible pointer dereferencing in the fastpaths. */ u8 rev; }; struct b43_pio_rxqueue { struct b43_wldev *dev; u16 mmio_base; /* Shortcut to the 802.11 core revision. This is to * avoid horrible pointer dereferencing in the fastpaths. */ u8 rev; }; static inline u16 b43_piotx_read16(struct b43_pio_txqueue *q, u16 offset) { return b43_read16(q->dev, q->mmio_base + offset); } static inline u32 b43_piotx_read32(struct b43_pio_txqueue *q, u16 offset) { return b43_read32(q->dev, q->mmio_base + offset); } static inline void b43_piotx_write16(struct b43_pio_txqueue *q, u16 offset, u16 value) { b43_write16(q->dev, q->mmio_base + offset, value); } static inline void b43_piotx_write32(struct b43_pio_txqueue *q, u16 offset, u32 value) { b43_write32(q->dev, q->mmio_base + offset, value); } static inline u16 b43_piorx_read16(struct b43_pio_rxqueue *q, u16 offset) { return b43_read16(q->dev, q->mmio_base + offset); } static inline u32 b43_piorx_read32(struct b43_pio_rxqueue *q, u16 offset) { return b43_read32(q->dev, q->mmio_base + offset); } static inline void b43_piorx_write16(struct b43_pio_rxqueue *q, u16 offset, u16 value) { b43_write16(q->dev, q->mmio_base + offset, value); } static inline void b43_piorx_write32(struct b43_pio_rxqueue *q, u16 offset, u32 value) { b43_write32(q->dev, q->mmio_base + offset, value); } int b43_pio_init(struct b43_wldev *dev); void b43_pio_free(struct b43_wldev *dev); int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb); void b43_pio_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status); void b43_pio_rx(struct b43_pio_rxqueue *q); void b43_pio_tx_suspend(struct b43_wldev *dev); void b43_pio_tx_resume(struct b43_wldev *dev); #endif /* B43_PIO_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/pio.c0000644000175000017500000005040412026211315022007 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver PIO data transfer Copyright (c) 2005-2008 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "pio.h" #include "dma.h" #include "main.h" #include "xmit.h" #include #include #include static u16 generate_cookie(struct b43_pio_txqueue *q, struct b43_pio_txpacket *pack) { u16 cookie; /* Use the upper 4 bits of the cookie as * PIO controller ID and store the packet index number * in the lower 12 bits. * Note that the cookie must never be 0, as this * is a special value used in RX path. * It can also not be 0xFFFF because that is special * for multicast frames. */ cookie = (((u16)q->index + 1) << 12); cookie |= pack->index; return cookie; } static struct b43_pio_txqueue *parse_cookie(struct b43_wldev *dev, u16 cookie, struct b43_pio_txpacket **pack) { struct b43_pio *pio = &dev->pio; struct b43_pio_txqueue *q = NULL; unsigned int pack_index; switch (cookie & 0xF000) { case 0x1000: q = pio->tx_queue_AC_BK; break; case 0x2000: q = pio->tx_queue_AC_BE; break; case 0x3000: q = pio->tx_queue_AC_VI; break; case 0x4000: q = pio->tx_queue_AC_VO; break; case 0x5000: q = pio->tx_queue_mcast; break; } if (B43_WARN_ON(!q)) return NULL; pack_index = (cookie & 0x0FFF); if (B43_WARN_ON(pack_index >= ARRAY_SIZE(q->packets))) return NULL; *pack = &q->packets[pack_index]; return q; } static u16 index_to_pioqueue_base(struct b43_wldev *dev, unsigned int index) { static const u16 bases[] = { B43_MMIO_PIO_BASE0, B43_MMIO_PIO_BASE1, B43_MMIO_PIO_BASE2, B43_MMIO_PIO_BASE3, B43_MMIO_PIO_BASE4, B43_MMIO_PIO_BASE5, B43_MMIO_PIO_BASE6, B43_MMIO_PIO_BASE7, }; static const u16 bases_rev11[] = { B43_MMIO_PIO11_BASE0, B43_MMIO_PIO11_BASE1, B43_MMIO_PIO11_BASE2, B43_MMIO_PIO11_BASE3, B43_MMIO_PIO11_BASE4, B43_MMIO_PIO11_BASE5, }; if (dev->dev->core_rev >= 11) { B43_WARN_ON(index >= ARRAY_SIZE(bases_rev11)); return bases_rev11[index]; } B43_WARN_ON(index >= ARRAY_SIZE(bases)); return bases[index]; } static u16 pio_txqueue_offset(struct b43_wldev *dev) { if (dev->dev->core_rev >= 11) return 0x18; return 0; } static u16 pio_rxqueue_offset(struct b43_wldev *dev) { if (dev->dev->core_rev >= 11) return 0x38; return 8; } static struct b43_pio_txqueue *b43_setup_pioqueue_tx(struct b43_wldev *dev, unsigned int index) { struct b43_pio_txqueue *q; struct b43_pio_txpacket *p; unsigned int i; q = kzalloc(sizeof(*q), GFP_KERNEL); if (!q) return NULL; q->dev = dev; q->rev = dev->dev->core_rev; q->mmio_base = index_to_pioqueue_base(dev, index) + pio_txqueue_offset(dev); q->index = index; q->free_packet_slots = B43_PIO_MAX_NR_TXPACKETS; if (q->rev >= 8) { q->buffer_size = 1920; //FIXME this constant is wrong. } else { q->buffer_size = b43_piotx_read16(q, B43_PIO_TXQBUFSIZE); q->buffer_size -= 80; } INIT_LIST_HEAD(&q->packets_list); for (i = 0; i < ARRAY_SIZE(q->packets); i++) { p = &(q->packets[i]); INIT_LIST_HEAD(&p->list); p->index = i; p->queue = q; list_add(&p->list, &q->packets_list); } return q; } static struct b43_pio_rxqueue *b43_setup_pioqueue_rx(struct b43_wldev *dev, unsigned int index) { struct b43_pio_rxqueue *q; q = kzalloc(sizeof(*q), GFP_KERNEL); if (!q) return NULL; q->dev = dev; q->rev = dev->dev->core_rev; q->mmio_base = index_to_pioqueue_base(dev, index) + pio_rxqueue_offset(dev); /* Enable Direct FIFO RX (PIO) on the engine. */ b43_dma_direct_fifo_rx(dev, index, 1); return q; } static void b43_pio_cancel_tx_packets(struct b43_pio_txqueue *q) { struct b43_pio_txpacket *pack; unsigned int i; for (i = 0; i < ARRAY_SIZE(q->packets); i++) { pack = &(q->packets[i]); if (pack->skb) { dev_kfree_skb_any(pack->skb); pack->skb = NULL; } } } static void b43_destroy_pioqueue_tx(struct b43_pio_txqueue *q, const char *name) { if (!q) return; b43_pio_cancel_tx_packets(q); kfree(q); } static void b43_destroy_pioqueue_rx(struct b43_pio_rxqueue *q, const char *name) { if (!q) return; kfree(q); } #define destroy_queue_tx(pio, queue) do { \ b43_destroy_pioqueue_tx((pio)->queue, __stringify(queue)); \ (pio)->queue = NULL; \ } while (0) #define destroy_queue_rx(pio, queue) do { \ b43_destroy_pioqueue_rx((pio)->queue, __stringify(queue)); \ (pio)->queue = NULL; \ } while (0) void b43_pio_free(struct b43_wldev *dev) { struct b43_pio *pio; if (!b43_using_pio_transfers(dev)) return; pio = &dev->pio; destroy_queue_rx(pio, rx_queue); destroy_queue_tx(pio, tx_queue_mcast); destroy_queue_tx(pio, tx_queue_AC_VO); destroy_queue_tx(pio, tx_queue_AC_VI); destroy_queue_tx(pio, tx_queue_AC_BE); destroy_queue_tx(pio, tx_queue_AC_BK); } int b43_pio_init(struct b43_wldev *dev) { struct b43_pio *pio = &dev->pio; int err = -ENOMEM; b43_write32(dev, B43_MMIO_MACCTL, b43_read32(dev, B43_MMIO_MACCTL) & ~B43_MACCTL_BE); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RXPADOFF, 0); pio->tx_queue_AC_BK = b43_setup_pioqueue_tx(dev, 0); if (!pio->tx_queue_AC_BK) goto out; pio->tx_queue_AC_BE = b43_setup_pioqueue_tx(dev, 1); if (!pio->tx_queue_AC_BE) goto err_destroy_bk; pio->tx_queue_AC_VI = b43_setup_pioqueue_tx(dev, 2); if (!pio->tx_queue_AC_VI) goto err_destroy_be; pio->tx_queue_AC_VO = b43_setup_pioqueue_tx(dev, 3); if (!pio->tx_queue_AC_VO) goto err_destroy_vi; pio->tx_queue_mcast = b43_setup_pioqueue_tx(dev, 4); if (!pio->tx_queue_mcast) goto err_destroy_vo; pio->rx_queue = b43_setup_pioqueue_rx(dev, 0); if (!pio->rx_queue) goto err_destroy_mcast; b43dbg(dev->wl, "PIO initialized\n"); err = 0; out: return err; err_destroy_mcast: destroy_queue_tx(pio, tx_queue_mcast); err_destroy_vo: destroy_queue_tx(pio, tx_queue_AC_VO); err_destroy_vi: destroy_queue_tx(pio, tx_queue_AC_VI); err_destroy_be: destroy_queue_tx(pio, tx_queue_AC_BE); err_destroy_bk: destroy_queue_tx(pio, tx_queue_AC_BK); return err; } /* Static mapping of mac80211's queues (priorities) to b43 PIO queues. */ static struct b43_pio_txqueue *select_queue_by_priority(struct b43_wldev *dev, u8 queue_prio) { struct b43_pio_txqueue *q; if (dev->qos_enabled) { /* 0 = highest priority */ switch (queue_prio) { default: B43_WARN_ON(1); /* fallthrough */ case 0: q = dev->pio.tx_queue_AC_VO; break; case 1: q = dev->pio.tx_queue_AC_VI; break; case 2: q = dev->pio.tx_queue_AC_BE; break; case 3: q = dev->pio.tx_queue_AC_BK; break; } } else q = dev->pio.tx_queue_AC_BE; return q; } static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, u16 ctl, const void *_data, unsigned int data_len) { struct b43_wldev *dev = q->dev; struct b43_wl *wl = dev->wl; const u8 *data = _data; ctl |= B43_PIO_TXCTL_WRITELO | B43_PIO_TXCTL_WRITEHI; b43_piotx_write16(q, B43_PIO_TXCTL, ctl); b43_block_write(dev, data, (data_len & ~1), q->mmio_base + B43_PIO_TXDATA, sizeof(u16)); if (data_len & 1) { u8 *tail = wl->pio_tailspace; BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); /* Write the last byte. */ ctl &= ~B43_PIO_TXCTL_WRITEHI; b43_piotx_write16(q, B43_PIO_TXCTL, ctl); tail[0] = data[data_len - 1]; tail[1] = 0; b43_block_write(dev, tail, 2, q->mmio_base + B43_PIO_TXDATA, sizeof(u16)); } return ctl; } static void pio_tx_frame_2byte_queue(struct b43_pio_txpacket *pack, const u8 *hdr, unsigned int hdrlen) { struct b43_pio_txqueue *q = pack->queue; const char *frame = pack->skb->data; unsigned int frame_len = pack->skb->len; u16 ctl; ctl = b43_piotx_read16(q, B43_PIO_TXCTL); ctl |= B43_PIO_TXCTL_FREADY; ctl &= ~B43_PIO_TXCTL_EOF; /* Transfer the header data. */ ctl = tx_write_2byte_queue(q, ctl, hdr, hdrlen); /* Transfer the frame data. */ ctl = tx_write_2byte_queue(q, ctl, frame, frame_len); ctl |= B43_PIO_TXCTL_EOF; b43_piotx_write16(q, B43_PIO_TXCTL, ctl); } static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, u32 ctl, const void *_data, unsigned int data_len) { struct b43_wldev *dev = q->dev; struct b43_wl *wl = dev->wl; const u8 *data = _data; ctl |= B43_PIO8_TXCTL_0_7 | B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31; b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); b43_block_write(dev, data, (data_len & ~3), q->mmio_base + B43_PIO8_TXDATA, sizeof(u32)); if (data_len & 3) { u8 *tail = wl->pio_tailspace; BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); memset(tail, 0, 4); /* Write the last few bytes. */ ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31); switch (data_len & 3) { case 3: ctl |= B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_8_15; tail[0] = data[data_len - 3]; tail[1] = data[data_len - 2]; tail[2] = data[data_len - 1]; break; case 2: ctl |= B43_PIO8_TXCTL_8_15; tail[0] = data[data_len - 2]; tail[1] = data[data_len - 1]; break; case 1: tail[0] = data[data_len - 1]; break; } b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); b43_block_write(dev, tail, 4, q->mmio_base + B43_PIO8_TXDATA, sizeof(u32)); } return ctl; } static void pio_tx_frame_4byte_queue(struct b43_pio_txpacket *pack, const u8 *hdr, unsigned int hdrlen) { struct b43_pio_txqueue *q = pack->queue; const char *frame = pack->skb->data; unsigned int frame_len = pack->skb->len; u32 ctl; ctl = b43_piotx_read32(q, B43_PIO8_TXCTL); ctl |= B43_PIO8_TXCTL_FREADY; ctl &= ~B43_PIO8_TXCTL_EOF; /* Transfer the header data. */ ctl = tx_write_4byte_queue(q, ctl, hdr, hdrlen); /* Transfer the frame data. */ ctl = tx_write_4byte_queue(q, ctl, frame, frame_len); ctl |= B43_PIO8_TXCTL_EOF; b43_piotx_write32(q, B43_PIO_TXCTL, ctl); } static int pio_tx_frame(struct b43_pio_txqueue *q, struct sk_buff *skb) { struct b43_wldev *dev = q->dev; struct b43_wl *wl = dev->wl; struct b43_pio_txpacket *pack; u16 cookie; int err; unsigned int hdrlen; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct b43_txhdr *txhdr = (struct b43_txhdr *)wl->pio_scratchspace; B43_WARN_ON(list_empty(&q->packets_list)); pack = list_entry(q->packets_list.next, struct b43_pio_txpacket, list); cookie = generate_cookie(q, pack); hdrlen = b43_txhdr_size(dev); BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(struct b43_txhdr)); B43_WARN_ON(sizeof(wl->pio_scratchspace) < hdrlen); err = b43_generate_txhdr(dev, (u8 *)txhdr, skb, info, cookie); if (err) return err; if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* Tell the firmware about the cookie of the last * mcast frame, so it can clear the more-data bit in it. */ b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_MCASTCOOKIE, cookie); } pack->skb = skb; if (q->rev >= 8) pio_tx_frame_4byte_queue(pack, (const u8 *)txhdr, hdrlen); else pio_tx_frame_2byte_queue(pack, (const u8 *)txhdr, hdrlen); /* Remove it from the list of available packet slots. * It will be put back when we receive the status report. */ list_del(&pack->list); /* Update the queue statistics. */ q->buffer_used += roundup(skb->len + hdrlen, 4); q->free_packet_slots -= 1; return 0; } int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb) { struct b43_pio_txqueue *q; struct ieee80211_hdr *hdr; unsigned int hdrlen, total_len; int err = 0; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); hdr = (struct ieee80211_hdr *)skb->data; if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* The multicast queue will be sent after the DTIM. */ q = dev->pio.tx_queue_mcast; /* Set the frame More-Data bit. Ucode will clear it * for us on the last frame. */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); } else { /* Decide by priority where to put this frame. */ q = select_queue_by_priority(dev, skb_get_queue_mapping(skb)); } hdrlen = b43_txhdr_size(dev); total_len = roundup(skb->len + hdrlen, 4); if (unlikely(total_len > q->buffer_size)) { err = -ENOBUFS; b43dbg(dev->wl, "PIO: TX packet longer than queue.\n"); goto out; } if (unlikely(q->free_packet_slots == 0)) { err = -ENOBUFS; b43warn(dev->wl, "PIO: TX packet overflow.\n"); goto out; } B43_WARN_ON(q->buffer_used > q->buffer_size); if (total_len > (q->buffer_size - q->buffer_used)) { /* Not enough memory on the queue. */ err = -EBUSY; ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb)); q->stopped = true; goto out; } /* Assign the queue number to the ring (if not already done before) * so TX status handling can use it. The mac80211-queue to b43-queue * mapping is static, so we don't need to store it per frame. */ q->queue_prio = skb_get_queue_mapping(skb); err = pio_tx_frame(q, skb); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ dev_kfree_skb_any(skb); err = 0; goto out; } if (unlikely(err)) { b43err(dev->wl, "PIO transmission failure\n"); goto out; } B43_WARN_ON(q->buffer_used > q->buffer_size); if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) || (q->free_packet_slots == 0)) { /* The queue is full. */ ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb)); q->stopped = true; } out: return err; } void b43_pio_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status) { struct b43_pio_txqueue *q; struct b43_pio_txpacket *pack = NULL; unsigned int total_len; struct ieee80211_tx_info *info; q = parse_cookie(dev, status->cookie, &pack); if (unlikely(!q)) return; B43_WARN_ON(!pack); info = IEEE80211_SKB_CB(pack->skb); b43_fill_txstatus_report(dev, info, status); total_len = pack->skb->len + b43_txhdr_size(dev); total_len = roundup(total_len, 4); q->buffer_used -= total_len; q->free_packet_slots += 1; ieee80211_tx_status(dev->wl->hw, pack->skb); pack->skb = NULL; list_add(&pack->list, &q->packets_list); if (q->stopped) { ieee80211_wake_queue(dev->wl->hw, q->queue_prio); q->stopped = false; } } /* Returns whether we should fetch another frame. */ static bool pio_rx_frame(struct b43_pio_rxqueue *q) { struct b43_wldev *dev = q->dev; struct b43_wl *wl = dev->wl; u16 len; u32 macstat = 0; unsigned int i, padding; struct sk_buff *skb; const char *err_msg = NULL; struct b43_rxhdr_fw4 *rxhdr = (struct b43_rxhdr_fw4 *)wl->pio_scratchspace; size_t rxhdr_size = sizeof(*rxhdr); BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(*rxhdr)); switch (dev->fw.hdr_format) { case B43_FW_HDR_410: case B43_FW_HDR_351: rxhdr_size -= sizeof(rxhdr->format_598) - sizeof(rxhdr->format_351); break; case B43_FW_HDR_598: break; } memset(rxhdr, 0, rxhdr_size); /* Check if we have data and wait for it to get ready. */ if (q->rev >= 8) { u32 ctl; ctl = b43_piorx_read32(q, B43_PIO8_RXCTL); if (!(ctl & B43_PIO8_RXCTL_FRAMERDY)) return 0; b43_piorx_write32(q, B43_PIO8_RXCTL, B43_PIO8_RXCTL_FRAMERDY); for (i = 0; i < 10; i++) { ctl = b43_piorx_read32(q, B43_PIO8_RXCTL); if (ctl & B43_PIO8_RXCTL_DATARDY) goto data_ready; udelay(10); } } else { u16 ctl; ctl = b43_piorx_read16(q, B43_PIO_RXCTL); if (!(ctl & B43_PIO_RXCTL_FRAMERDY)) return 0; b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_FRAMERDY); for (i = 0; i < 10; i++) { ctl = b43_piorx_read16(q, B43_PIO_RXCTL); if (ctl & B43_PIO_RXCTL_DATARDY) goto data_ready; udelay(10); } } b43dbg(q->dev->wl, "PIO RX timed out\n"); return 1; data_ready: /* Get the preamble (RX header) */ if (q->rev >= 8) { b43_block_read(dev, rxhdr, rxhdr_size, q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); } else { b43_block_read(dev, rxhdr, rxhdr_size, q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); } /* Sanity checks. */ len = le16_to_cpu(rxhdr->frame_len); if (unlikely(len > 0x700)) { err_msg = "len > 0x700"; goto rx_error; } if (unlikely(len == 0)) { err_msg = "len == 0"; goto rx_error; } switch (dev->fw.hdr_format) { case B43_FW_HDR_598: macstat = le32_to_cpu(rxhdr->format_598.mac_status); break; case B43_FW_HDR_410: case B43_FW_HDR_351: macstat = le32_to_cpu(rxhdr->format_351.mac_status); break; } if (macstat & B43_RX_MAC_FCSERR) { if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) { /* Drop frames with failed FCS. */ err_msg = "Frame FCS error"; goto rx_error; } } /* We always pad 2 bytes, as that's what upstream code expects * due to the RX-header being 30 bytes. In case the frame is * unaligned, we pad another 2 bytes. */ padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0; skb = dev_alloc_skb(len + padding + 2); if (unlikely(!skb)) { err_msg = "Out of memory"; goto rx_error; } skb_reserve(skb, 2); skb_put(skb, len + padding); if (q->rev >= 8) { b43_block_read(dev, skb->data + padding, (len & ~3), q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); if (len & 3) { u8 *tail = wl->pio_tailspace; BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); /* Read the last few bytes. */ b43_block_read(dev, tail, 4, q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); switch (len & 3) { case 3: skb->data[len + padding - 3] = tail[0]; skb->data[len + padding - 2] = tail[1]; skb->data[len + padding - 1] = tail[2]; break; case 2: skb->data[len + padding - 2] = tail[0]; skb->data[len + padding - 1] = tail[1]; break; case 1: skb->data[len + padding - 1] = tail[0]; break; } } } else { b43_block_read(dev, skb->data + padding, (len & ~1), q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); if (len & 1) { u8 *tail = wl->pio_tailspace; BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); /* Read the last byte. */ b43_block_read(dev, tail, 2, q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); skb->data[len + padding - 1] = tail[0]; } } b43_rx(q->dev, skb, rxhdr); return 1; rx_error: if (err_msg) b43dbg(q->dev->wl, "PIO RX error: %s\n", err_msg); if (q->rev >= 8) b43_piorx_write32(q, B43_PIO8_RXCTL, B43_PIO8_RXCTL_DATARDY); else b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY); return 1; } void b43_pio_rx(struct b43_pio_rxqueue *q) { unsigned int count = 0; bool stop; while (1) { stop = (pio_rx_frame(q) == 0); if (stop) break; cond_resched(); if (WARN_ON_ONCE(++count > 10000)) break; } } static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q) { if (q->rev >= 8) { b43_piotx_write32(q, B43_PIO8_TXCTL, b43_piotx_read32(q, B43_PIO8_TXCTL) | B43_PIO8_TXCTL_SUSPREQ); } else { b43_piotx_write16(q, B43_PIO_TXCTL, b43_piotx_read16(q, B43_PIO_TXCTL) | B43_PIO_TXCTL_SUSPREQ); } } static void b43_pio_tx_resume_queue(struct b43_pio_txqueue *q) { if (q->rev >= 8) { b43_piotx_write32(q, B43_PIO8_TXCTL, b43_piotx_read32(q, B43_PIO8_TXCTL) & ~B43_PIO8_TXCTL_SUSPREQ); } else { b43_piotx_write16(q, B43_PIO_TXCTL, b43_piotx_read16(q, B43_PIO_TXCTL) & ~B43_PIO_TXCTL_SUSPREQ); } } void b43_pio_tx_suspend(struct b43_wldev *dev) { b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BK); b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BE); b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VI); b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VO); b43_pio_tx_suspend_queue(dev->pio.tx_queue_mcast); } void b43_pio_tx_resume(struct b43_wldev *dev) { b43_pio_tx_resume_queue(dev->pio.tx_queue_mcast); b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VO); b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VI); b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BE); b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BK); b43_power_saving_ctl_bits(dev, 0); } compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_n.h0000644000175000017500000014757712026211315022364 0ustar mcgrofmcgrof#ifndef B43_NPHY_H_ #define B43_NPHY_H_ #include "phy_common.h" /* N-PHY registers. */ #define B43_NPHY_BBCFG B43_PHY_N(0x001) /* BB config */ #define B43_NPHY_BBCFG_RSTCCA 0x4000 /* Reset CCA */ #define B43_NPHY_BBCFG_RSTRX 0x8000 /* Reset RX */ #define B43_NPHY_CHANNEL B43_PHY_N(0x005) /* Channel */ #define B43_NPHY_TXERR B43_PHY_N(0x007) /* TX error */ #define B43_NPHY_BANDCTL B43_PHY_N(0x009) /* Band control */ #define B43_NPHY_BANDCTL_5GHZ 0x0001 /* Use the 5GHz band */ #define B43_NPHY_4WI_ADDR B43_PHY_N(0x00B) /* Four-wire bus address */ #define B43_NPHY_4WI_DATAHI B43_PHY_N(0x00C) /* Four-wire bus data high */ #define B43_NPHY_4WI_DATALO B43_PHY_N(0x00D) /* Four-wire bus data low */ #define B43_NPHY_BIST_STAT0 B43_PHY_N(0x00E) /* Built-in self test status 0 */ #define B43_NPHY_BIST_STAT1 B43_PHY_N(0x00F) /* Built-in self test status 1 */ #define B43_NPHY_C1_DESPWR B43_PHY_N(0x018) /* Core 1 desired power */ #define B43_NPHY_C1_CCK_DESPWR B43_PHY_N(0x019) /* Core 1 CCK desired power */ #define B43_NPHY_C1_BCLIPBKOFF B43_PHY_N(0x01A) /* Core 1 barely clip backoff */ #define B43_NPHY_C1_CCK_BCLIPBKOFF B43_PHY_N(0x01B) /* Core 1 CCK barely clip backoff */ #define B43_NPHY_C1_CGAINI B43_PHY_N(0x01C) /* Core 1 compute gain info */ #define B43_NPHY_C1_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ #define B43_NPHY_C1_CGAINI_GAINBKOFF_SHIFT 0 #define B43_NPHY_C1_CGAINI_CLIPGBKOFF 0x03E0 /* Clip gain backoff */ #define B43_NPHY_C1_CGAINI_CLIPGBKOFF_SHIFT 5 #define B43_NPHY_C1_CGAINI_GAINSTEP 0x1C00 /* Gain step */ #define B43_NPHY_C1_CGAINI_GAINSTEP_SHIFT 10 #define B43_NPHY_C1_CGAINI_CL2DETECT 0x2000 /* Clip 2 detect mask */ #define B43_NPHY_C1_CCK_CGAINI B43_PHY_N(0x01D) /* Core 1 CCK compute gain info */ #define B43_NPHY_C1_CCK_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ #define B43_NPHY_C1_CCK_CGAINI_CLIPGBKOFF 0x01E0 /* CCK barely clip gain backoff */ #define B43_NPHY_C1_MINMAX_GAIN B43_PHY_N(0x01E) /* Core 1 min/max gain */ #define B43_NPHY_C1_MINGAIN 0x00FF /* Minimum gain */ #define B43_NPHY_C1_MINGAIN_SHIFT 0 #define B43_NPHY_C1_MAXGAIN 0xFF00 /* Maximum gain */ #define B43_NPHY_C1_MAXGAIN_SHIFT 8 #define B43_NPHY_C1_CCK_MINMAX_GAIN B43_PHY_N(0x01F) /* Core 1 CCK min/max gain */ #define B43_NPHY_C1_CCK_MINGAIN 0x00FF /* Minimum gain */ #define B43_NPHY_C1_CCK_MINGAIN_SHIFT 0 #define B43_NPHY_C1_CCK_MAXGAIN 0xFF00 /* Maximum gain */ #define B43_NPHY_C1_CCK_MAXGAIN_SHIFT 8 #define B43_NPHY_C1_INITGAIN B43_PHY_N(0x020) /* Core 1 initial gain code */ #define B43_NPHY_C1_INITGAIN_EXTLNA 0x0001 /* External LNA index */ #define B43_NPHY_C1_INITGAIN_LNA 0x0006 /* LNA index */ #define B43_NPHY_C1_INITGAIN_LNAIDX_SHIFT 1 #define B43_NPHY_C1_INITGAIN_HPVGA1 0x0078 /* HPVGA1 index */ #define B43_NPHY_C1_INITGAIN_HPVGA1_SHIFT 3 #define B43_NPHY_C1_INITGAIN_HPVGA2 0x0F80 /* HPVGA2 index */ #define B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT 7 #define B43_NPHY_C1_INITGAIN_TRRX 0x1000 /* TR RX index */ #define B43_NPHY_C1_INITGAIN_TRTX 0x2000 /* TR TX index */ #define B43_NPHY_C1_CLIP1_HIGAIN B43_PHY_N(0x021) /* Core 1 clip1 high gain code */ #define B43_NPHY_C1_CLIP1_MEDGAIN B43_PHY_N(0x022) /* Core 1 clip1 medium gain code */ #define B43_NPHY_C1_CLIP1_LOGAIN B43_PHY_N(0x023) /* Core 1 clip1 low gain code */ #define B43_NPHY_C1_CLIP2_GAIN B43_PHY_N(0x024) /* Core 1 clip2 gain code */ #define B43_NPHY_C1_FILTERGAIN B43_PHY_N(0x025) /* Core 1 filter gain */ #define B43_NPHY_C1_LPF_QHPF_BW B43_PHY_N(0x026) /* Core 1 LPF Q HP F bandwidth */ #define B43_NPHY_C1_CLIPWBTHRES B43_PHY_N(0x027) /* Core 1 clip wideband threshold */ #define B43_NPHY_C1_CLIPWBTHRES_CLIP2 0x003F /* Clip 2 */ #define B43_NPHY_C1_CLIPWBTHRES_CLIP2_SHIFT 0 #define B43_NPHY_C1_CLIPWBTHRES_CLIP1 0x0FC0 /* Clip 1 */ #define B43_NPHY_C1_CLIPWBTHRES_CLIP1_SHIFT 6 #define B43_NPHY_C1_W1THRES B43_PHY_N(0x028) /* Core 1 W1 threshold */ #define B43_NPHY_C1_EDTHRES B43_PHY_N(0x029) /* Core 1 ED threshold */ #define B43_NPHY_C1_SMSIGTHRES B43_PHY_N(0x02A) /* Core 1 small sig threshold */ #define B43_NPHY_C1_NBCLIPTHRES B43_PHY_N(0x02B) /* Core 1 NB clip threshold */ #define B43_NPHY_C1_CLIP1THRES B43_PHY_N(0x02C) /* Core 1 clip1 threshold */ #define B43_NPHY_C1_CLIP2THRES B43_PHY_N(0x02D) /* Core 1 clip2 threshold */ #define B43_NPHY_C2_DESPWR B43_PHY_N(0x02E) /* Core 2 desired power */ #define B43_NPHY_C2_CCK_DESPWR B43_PHY_N(0x02F) /* Core 2 CCK desired power */ #define B43_NPHY_C2_BCLIPBKOFF B43_PHY_N(0x030) /* Core 2 barely clip backoff */ #define B43_NPHY_C2_CCK_BCLIPBKOFF B43_PHY_N(0x031) /* Core 2 CCK barely clip backoff */ #define B43_NPHY_C2_CGAINI B43_PHY_N(0x032) /* Core 2 compute gain info */ #define B43_NPHY_C2_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ #define B43_NPHY_C2_CGAINI_GAINBKOFF_SHIFT 0 #define B43_NPHY_C2_CGAINI_CLIPGBKOFF 0x03E0 /* Clip gain backoff */ #define B43_NPHY_C2_CGAINI_CLIPGBKOFF_SHIFT 5 #define B43_NPHY_C2_CGAINI_GAINSTEP 0x1C00 /* Gain step */ #define B43_NPHY_C2_CGAINI_GAINSTEP_SHIFT 10 #define B43_NPHY_C2_CGAINI_CL2DETECT 0x2000 /* Clip 2 detect mask */ #define B43_NPHY_C2_CCK_CGAINI B43_PHY_N(0x033) /* Core 2 CCK compute gain info */ #define B43_NPHY_C2_CCK_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ #define B43_NPHY_C2_CCK_CGAINI_CLIPGBKOFF 0x01E0 /* CCK barely clip gain backoff */ #define B43_NPHY_C2_MINMAX_GAIN B43_PHY_N(0x034) /* Core 2 min/max gain */ #define B43_NPHY_C2_MINGAIN 0x00FF /* Minimum gain */ #define B43_NPHY_C2_MINGAIN_SHIFT 0 #define B43_NPHY_C2_MAXGAIN 0xFF00 /* Maximum gain */ #define B43_NPHY_C2_MAXGAIN_SHIFT 8 #define B43_NPHY_C2_CCK_MINMAX_GAIN B43_PHY_N(0x035) /* Core 2 CCK min/max gain */ #define B43_NPHY_C2_CCK_MINGAIN 0x00FF /* Minimum gain */ #define B43_NPHY_C2_CCK_MINGAIN_SHIFT 0 #define B43_NPHY_C2_CCK_MAXGAIN 0xFF00 /* Maximum gain */ #define B43_NPHY_C2_CCK_MAXGAIN_SHIFT 8 #define B43_NPHY_C2_INITGAIN B43_PHY_N(0x036) /* Core 2 initial gain code */ #define B43_NPHY_C2_INITGAIN_EXTLNA 0x0001 /* External LNA index */ #define B43_NPHY_C2_INITGAIN_LNA 0x0006 /* LNA index */ #define B43_NPHY_C2_INITGAIN_LNAIDX_SHIFT 1 #define B43_NPHY_C2_INITGAIN_HPVGA1 0x0078 /* HPVGA1 index */ #define B43_NPHY_C2_INITGAIN_HPVGA1_SHIFT 3 #define B43_NPHY_C2_INITGAIN_HPVGA2 0x0F80 /* HPVGA2 index */ #define B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT 7 #define B43_NPHY_C2_INITGAIN_TRRX 0x1000 /* TR RX index */ #define B43_NPHY_C2_INITGAIN_TRTX 0x2000 /* TR TX index */ #define B43_NPHY_C2_CLIP1_HIGAIN B43_PHY_N(0x037) /* Core 2 clip1 high gain code */ #define B43_NPHY_C2_CLIP1_MEDGAIN B43_PHY_N(0x038) /* Core 2 clip1 medium gain code */ #define B43_NPHY_C2_CLIP1_LOGAIN B43_PHY_N(0x039) /* Core 2 clip1 low gain code */ #define B43_NPHY_C2_CLIP2_GAIN B43_PHY_N(0x03A) /* Core 2 clip2 gain code */ #define B43_NPHY_C2_FILTERGAIN B43_PHY_N(0x03B) /* Core 2 filter gain */ #define B43_NPHY_C2_LPF_QHPF_BW B43_PHY_N(0x03C) /* Core 2 LPF Q HP F bandwidth */ #define B43_NPHY_C2_CLIPWBTHRES B43_PHY_N(0x03D) /* Core 2 clip wideband threshold */ #define B43_NPHY_C2_CLIPWBTHRES_CLIP2 0x003F /* Clip 2 */ #define B43_NPHY_C2_CLIPWBTHRES_CLIP2_SHIFT 0 #define B43_NPHY_C2_CLIPWBTHRES_CLIP1 0x0FC0 /* Clip 1 */ #define B43_NPHY_C2_CLIPWBTHRES_CLIP1_SHIFT 6 #define B43_NPHY_C2_W1THRES B43_PHY_N(0x03E) /* Core 2 W1 threshold */ #define B43_NPHY_C2_EDTHRES B43_PHY_N(0x03F) /* Core 2 ED threshold */ #define B43_NPHY_C2_SMSIGTHRES B43_PHY_N(0x040) /* Core 2 small sig threshold */ #define B43_NPHY_C2_NBCLIPTHRES B43_PHY_N(0x041) /* Core 2 NB clip threshold */ #define B43_NPHY_C2_CLIP1THRES B43_PHY_N(0x042) /* Core 2 clip1 threshold */ #define B43_NPHY_C2_CLIP2THRES B43_PHY_N(0x043) /* Core 2 clip2 threshold */ #define B43_NPHY_CRS_THRES1 B43_PHY_N(0x044) /* CRS threshold 1 */ #define B43_NPHY_CRS_THRES2 B43_PHY_N(0x045) /* CRS threshold 2 */ #define B43_NPHY_CRS_THRES3 B43_PHY_N(0x046) /* CRS threshold 3 */ #define B43_NPHY_CRSCTL B43_PHY_N(0x047) /* CRS control */ #define B43_NPHY_DCFADDR B43_PHY_N(0x048) /* DC filter address */ #define B43_NPHY_RXF20_NUM0 B43_PHY_N(0x049) /* RX filter 20 numerator 0 */ #define B43_NPHY_RXF20_NUM1 B43_PHY_N(0x04A) /* RX filter 20 numerator 1 */ #define B43_NPHY_RXF20_NUM2 B43_PHY_N(0x04B) /* RX filter 20 numerator 2 */ #define B43_NPHY_RXF20_DENOM0 B43_PHY_N(0x04C) /* RX filter 20 denominator 0 */ #define B43_NPHY_RXF20_DENOM1 B43_PHY_N(0x04D) /* RX filter 20 denominator 1 */ #define B43_NPHY_RXF20_NUM10 B43_PHY_N(0x04E) /* RX filter 20 numerator 10 */ #define B43_NPHY_RXF20_NUM11 B43_PHY_N(0x04F) /* RX filter 20 numerator 11 */ #define B43_NPHY_RXF20_NUM12 B43_PHY_N(0x050) /* RX filter 20 numerator 12 */ #define B43_NPHY_RXF20_DENOM10 B43_PHY_N(0x051) /* RX filter 20 denominator 10 */ #define B43_NPHY_RXF20_DENOM11 B43_PHY_N(0x052) /* RX filter 20 denominator 11 */ #define B43_NPHY_RXF40_NUM0 B43_PHY_N(0x053) /* RX filter 40 numerator 0 */ #define B43_NPHY_RXF40_NUM1 B43_PHY_N(0x054) /* RX filter 40 numerator 1 */ #define B43_NPHY_RXF40_NUM2 B43_PHY_N(0x055) /* RX filter 40 numerator 2 */ #define B43_NPHY_RXF40_DENOM0 B43_PHY_N(0x056) /* RX filter 40 denominator 0 */ #define B43_NPHY_RXF40_DENOM1 B43_PHY_N(0x057) /* RX filter 40 denominator 1 */ #define B43_NPHY_RXF40_NUM10 B43_PHY_N(0x058) /* RX filter 40 numerator 10 */ #define B43_NPHY_RXF40_NUM11 B43_PHY_N(0x059) /* RX filter 40 numerator 11 */ #define B43_NPHY_RXF40_NUM12 B43_PHY_N(0x05A) /* RX filter 40 numerator 12 */ #define B43_NPHY_RXF40_DENOM10 B43_PHY_N(0x05B) /* RX filter 40 denominator 10 */ #define B43_NPHY_RXF40_DENOM11 B43_PHY_N(0x05C) /* RX filter 40 denominator 11 */ #define B43_NPHY_PPROC_RSTLEN B43_PHY_N(0x060) /* Packet processing reset length */ #define B43_NPHY_INITCARR_DLEN B43_PHY_N(0x061) /* Initial carrier detection length */ #define B43_NPHY_CLIP1CARR_DLEN B43_PHY_N(0x062) /* Clip1 carrier detection length */ #define B43_NPHY_CLIP2CARR_DLEN B43_PHY_N(0x063) /* Clip2 carrier detection length */ #define B43_NPHY_INITGAIN_SLEN B43_PHY_N(0x064) /* Initial gain settle length */ #define B43_NPHY_CLIP1GAIN_SLEN B43_PHY_N(0x065) /* Clip1 gain settle length */ #define B43_NPHY_CLIP2GAIN_SLEN B43_PHY_N(0x066) /* Clip2 gain settle length */ #define B43_NPHY_PACKGAIN_SLEN B43_PHY_N(0x067) /* Packet gain settle length */ #define B43_NPHY_CARRSRC_TLEN B43_PHY_N(0x068) /* Carrier search timeout length */ #define B43_NPHY_TISRC_TLEN B43_PHY_N(0x069) /* Timing search timeout length */ #define B43_NPHY_ENDROP_TLEN B43_PHY_N(0x06A) /* Energy drop timeout length */ #define B43_NPHY_CLIP1_NBDWELL_LEN B43_PHY_N(0x06B) /* Clip1 NB dwell length */ #define B43_NPHY_CLIP2_NBDWELL_LEN B43_PHY_N(0x06C) /* Clip2 NB dwell length */ #define B43_NPHY_W1CLIP1_DWELL_LEN B43_PHY_N(0x06D) /* W1 clip1 dwell length */ #define B43_NPHY_W1CLIP2_DWELL_LEN B43_PHY_N(0x06E) /* W1 clip2 dwell length */ #define B43_NPHY_W2CLIP1_DWELL_LEN B43_PHY_N(0x06F) /* W2 clip1 dwell length */ #define B43_NPHY_PLOAD_CSENSE_EXTLEN B43_PHY_N(0x070) /* Payload carrier sense extension length */ #define B43_NPHY_EDROP_CSENSE_EXTLEN B43_PHY_N(0x071) /* Energy drop carrier sense extension length */ #define B43_NPHY_TABLE_ADDR B43_PHY_N(0x072) /* Table address */ #define B43_NPHY_TABLE_DATALO B43_PHY_N(0x073) /* Table data low */ #define B43_NPHY_TABLE_DATAHI B43_PHY_N(0x074) /* Table data high */ #define B43_NPHY_WWISE_LENIDX B43_PHY_N(0x075) /* WWiSE length index */ #define B43_NPHY_TGNSYNC_LENIDX B43_PHY_N(0x076) /* TGNsync length index */ #define B43_NPHY_TXMACIF_HOLDOFF B43_PHY_N(0x077) /* TX MAC IF Hold off */ #define B43_NPHY_RFCTL_CMD B43_PHY_N(0x078) /* RF control (command) */ #define B43_NPHY_RFCTL_CMD_START 0x0001 /* Start sequence */ #define B43_NPHY_RFCTL_CMD_RXTX 0x0002 /* RX/TX */ #define B43_NPHY_RFCTL_CMD_CORESEL 0x0038 /* Core select */ #define B43_NPHY_RFCTL_CMD_CORESEL_SHIFT 3 #define B43_NPHY_RFCTL_CMD_PORFORCE 0x0040 /* POR force */ #define B43_NPHY_RFCTL_CMD_OEPORFORCE 0x0080 /* OE POR force */ #define B43_NPHY_RFCTL_CMD_RXEN 0x0100 /* RX enable */ #define B43_NPHY_RFCTL_CMD_TXEN 0x0200 /* TX enable */ #define B43_NPHY_RFCTL_CMD_CHIP0PU 0x0400 /* Chip0 PU */ #define B43_NPHY_RFCTL_CMD_EN 0x0800 /* Radio enabled */ #define B43_NPHY_RFCTL_CMD_SEQENCORE 0xF000 /* Seq en core */ #define B43_NPHY_RFCTL_CMD_SEQENCORE_SHIFT 12 #define B43_NPHY_RFCTL_RSSIO1 B43_PHY_N(0x07A) /* RF control (RSSI others 1) */ #define B43_NPHY_RFCTL_RSSIO1_RXPD 0x0001 /* RX PD */ #define B43_NPHY_RFCTL_RSSIO1_TXPD 0x0002 /* TX PD */ #define B43_NPHY_RFCTL_RSSIO1_PAPD 0x0004 /* PA PD */ #define B43_NPHY_RFCTL_RSSIO1_RSSICTL 0x0030 /* RSSI control */ #define B43_NPHY_RFCTL_RSSIO1_LPFBW 0x00C0 /* LPF bandwidth */ #define B43_NPHY_RFCTL_RSSIO1_HPFBWHI 0x0100 /* HPF bandwidth high */ #define B43_NPHY_RFCTL_RSSIO1_HIQDISCO 0x0200 /* HIQ dis core */ #define B43_NPHY_RFCTL_RXG1 B43_PHY_N(0x07B) /* RF control (RX gain 1) */ #define B43_NPHY_RFCTL_TXG1 B43_PHY_N(0x07C) /* RF control (TX gain 1) */ #define B43_NPHY_RFCTL_RSSIO2 B43_PHY_N(0x07D) /* RF control (RSSI others 2) */ #define B43_NPHY_RFCTL_RSSIO2_RXPD 0x0001 /* RX PD */ #define B43_NPHY_RFCTL_RSSIO2_TXPD 0x0002 /* TX PD */ #define B43_NPHY_RFCTL_RSSIO2_PAPD 0x0004 /* PA PD */ #define B43_NPHY_RFCTL_RSSIO2_RSSICTL 0x0030 /* RSSI control */ #define B43_NPHY_RFCTL_RSSIO2_LPFBW 0x00C0 /* LPF bandwidth */ #define B43_NPHY_RFCTL_RSSIO2_HPFBWHI 0x0100 /* HPF bandwidth high */ #define B43_NPHY_RFCTL_RSSIO2_HIQDISCO 0x0200 /* HIQ dis core */ #define B43_NPHY_RFCTL_RXG2 B43_PHY_N(0x07E) /* RF control (RX gain 2) */ #define B43_NPHY_RFCTL_TXG2 B43_PHY_N(0x07F) /* RF control (TX gain 2) */ #define B43_NPHY_RFCTL_RSSIO3 B43_PHY_N(0x080) /* RF control (RSSI others 3) */ #define B43_NPHY_RFCTL_RSSIO3_RXPD 0x0001 /* RX PD */ #define B43_NPHY_RFCTL_RSSIO3_TXPD 0x0002 /* TX PD */ #define B43_NPHY_RFCTL_RSSIO3_PAPD 0x0004 /* PA PD */ #define B43_NPHY_RFCTL_RSSIO3_RSSICTL 0x0030 /* RSSI control */ #define B43_NPHY_RFCTL_RSSIO3_LPFBW 0x00C0 /* LPF bandwidth */ #define B43_NPHY_RFCTL_RSSIO3_HPFBWHI 0x0100 /* HPF bandwidth high */ #define B43_NPHY_RFCTL_RSSIO3_HIQDISCO 0x0200 /* HIQ dis core */ #define B43_NPHY_RFCTL_RXG3 B43_PHY_N(0x081) /* RF control (RX gain 3) */ #define B43_NPHY_RFCTL_TXG3 B43_PHY_N(0x082) /* RF control (TX gain 3) */ #define B43_NPHY_RFCTL_RSSIO4 B43_PHY_N(0x083) /* RF control (RSSI others 4) */ #define B43_NPHY_RFCTL_RSSIO4_RXPD 0x0001 /* RX PD */ #define B43_NPHY_RFCTL_RSSIO4_TXPD 0x0002 /* TX PD */ #define B43_NPHY_RFCTL_RSSIO4_PAPD 0x0004 /* PA PD */ #define B43_NPHY_RFCTL_RSSIO4_RSSICTL 0x0030 /* RSSI control */ #define B43_NPHY_RFCTL_RSSIO4_LPFBW 0x00C0 /* LPF bandwidth */ #define B43_NPHY_RFCTL_RSSIO4_HPFBWHI 0x0100 /* HPF bandwidth high */ #define B43_NPHY_RFCTL_RSSIO4_HIQDISCO 0x0200 /* HIQ dis core */ #define B43_NPHY_RFCTL_RXG4 B43_PHY_N(0x084) /* RF control (RX gain 4) */ #define B43_NPHY_RFCTL_TXG4 B43_PHY_N(0x085) /* RF control (TX gain 4) */ #define B43_NPHY_C1_TXIQ_COMP_OFF B43_PHY_N(0x087) /* Core 1 TX I/Q comp offset */ #define B43_NPHY_C2_TXIQ_COMP_OFF B43_PHY_N(0x088) /* Core 2 TX I/Q comp offset */ #define B43_NPHY_C1_TXCTL B43_PHY_N(0x08B) /* Core 1 TX control */ #define B43_NPHY_C2_TXCTL B43_PHY_N(0x08C) /* Core 2 TX control */ #define B43_NPHY_AFECTL_OVER1 B43_PHY_N(0x08F) /* AFE control override 1 */ #define B43_NPHY_SCRAM_SIGCTL B43_PHY_N(0x090) /* Scram signal control */ #define B43_NPHY_SCRAM_SIGCTL_INITST 0x007F /* Initial state value */ #define B43_NPHY_SCRAM_SIGCTL_INITST_SHIFT 0 #define B43_NPHY_SCRAM_SIGCTL_SCM 0x0080 /* Scram control mode */ #define B43_NPHY_SCRAM_SIGCTL_SICE 0x0100 /* Scram index control enable */ #define B43_NPHY_SCRAM_SIGCTL_START 0xFE00 /* Scram start bit */ #define B43_NPHY_SCRAM_SIGCTL_START_SHIFT 9 #define B43_NPHY_RFCTL_INTC1 B43_PHY_N(0x091) /* RF control (intc 1) */ #define B43_NPHY_RFCTL_INTC2 B43_PHY_N(0x092) /* RF control (intc 2) */ #define B43_NPHY_RFCTL_INTC3 B43_PHY_N(0x093) /* RF control (intc 3) */ #define B43_NPHY_RFCTL_INTC4 B43_PHY_N(0x094) /* RF control (intc 4) */ #define B43_NPHY_NRDTO_WWISE B43_PHY_N(0x095) /* # datatones WWiSE */ #define B43_NPHY_NRDTO_TGNSYNC B43_PHY_N(0x096) /* # datatones TGNsync */ #define B43_NPHY_SIGFMOD_WWISE B43_PHY_N(0x097) /* Signal field mod WWiSE */ #define B43_NPHY_LEG_SIGFMOD_11N B43_PHY_N(0x098) /* Legacy signal field mod 11n */ #define B43_NPHY_HT_SIGFMOD_11N B43_PHY_N(0x099) /* HT signal field mod 11n */ #define B43_NPHY_C1_RXIQ_COMPA0 B43_PHY_N(0x09A) /* Core 1 RX I/Q comp A0 */ #define B43_NPHY_C1_RXIQ_COMPB0 B43_PHY_N(0x09B) /* Core 1 RX I/Q comp B0 */ #define B43_NPHY_C2_RXIQ_COMPA1 B43_PHY_N(0x09C) /* Core 2 RX I/Q comp A1 */ #define B43_NPHY_C2_RXIQ_COMPB1 B43_PHY_N(0x09D) /* Core 2 RX I/Q comp B1 */ #define B43_NPHY_RXCTL B43_PHY_N(0x0A0) /* RX control */ #define B43_NPHY_RXCTL_BSELU20 0x0010 /* Band select upper 20 */ #define B43_NPHY_RXCTL_RIFSEN 0x0080 /* RIFS enable */ #define B43_NPHY_RFSEQMODE B43_PHY_N(0x0A1) /* RF seq mode */ #define B43_NPHY_RFSEQMODE_CAOVER 0x0001 /* Core active override */ #define B43_NPHY_RFSEQMODE_TROVER 0x0002 /* Trigger override */ #define B43_NPHY_RFSEQCA B43_PHY_N(0x0A2) /* RF seq core active */ #define B43_NPHY_RFSEQCA_TXEN 0x000F /* TX enable */ #define B43_NPHY_RFSEQCA_TXEN_SHIFT 0 #define B43_NPHY_RFSEQCA_RXEN 0x00F0 /* RX enable */ #define B43_NPHY_RFSEQCA_RXEN_SHIFT 4 #define B43_NPHY_RFSEQCA_TXDIS 0x0F00 /* TX disable */ #define B43_NPHY_RFSEQCA_TXDIS_SHIFT 8 #define B43_NPHY_RFSEQCA_RXDIS 0xF000 /* RX disable */ #define B43_NPHY_RFSEQCA_RXDIS_SHIFT 12 #define B43_NPHY_RFSEQTR B43_PHY_N(0x0A3) /* RF seq trigger */ #define B43_NPHY_RFSEQTR_RX2TX 0x0001 /* RX2TX */ #define B43_NPHY_RFSEQTR_TX2RX 0x0002 /* TX2RX */ #define B43_NPHY_RFSEQTR_UPGH 0x0004 /* Update gain H */ #define B43_NPHY_RFSEQTR_UPGL 0x0008 /* Update gain L */ #define B43_NPHY_RFSEQTR_UPGU 0x0010 /* Update gain U */ #define B43_NPHY_RFSEQTR_RST2RX 0x0020 /* Reset to RX */ #define B43_NPHY_RFSEQST B43_PHY_N(0x0A4) /* RF seq status. Values same as trigger. */ #define B43_NPHY_AFECTL_OVER B43_PHY_N(0x0A5) /* AFE control override */ #define B43_NPHY_AFECTL_C1 B43_PHY_N(0x0A6) /* AFE control core 1 */ #define B43_NPHY_AFECTL_C2 B43_PHY_N(0x0A7) /* AFE control core 2 */ #define B43_NPHY_AFECTL_C3 B43_PHY_N(0x0A8) /* AFE control core 3 */ #define B43_NPHY_AFECTL_C4 B43_PHY_N(0x0A9) /* AFE control core 4 */ #define B43_NPHY_AFECTL_DACGAIN1 B43_PHY_N(0x0AA) /* AFE control DAC gain 1 */ #define B43_NPHY_AFECTL_DACGAIN2 B43_PHY_N(0x0AB) /* AFE control DAC gain 2 */ #define B43_NPHY_AFECTL_DACGAIN3 B43_PHY_N(0x0AC) /* AFE control DAC gain 3 */ #define B43_NPHY_AFECTL_DACGAIN4 B43_PHY_N(0x0AD) /* AFE control DAC gain 4 */ #define B43_NPHY_STR_ADDR1 B43_PHY_N(0x0AE) /* STR address 1 */ #define B43_NPHY_STR_ADDR2 B43_PHY_N(0x0AF) /* STR address 2 */ #define B43_NPHY_CLASSCTL B43_PHY_N(0x0B0) /* Classifier control */ #define B43_NPHY_CLASSCTL_CCKEN 0x0001 /* CCK enable */ #define B43_NPHY_CLASSCTL_OFDMEN 0x0002 /* OFDM enable */ #define B43_NPHY_CLASSCTL_WAITEDEN 0x0004 /* Waited enable */ #define B43_NPHY_IQFLIP B43_PHY_N(0x0B1) /* I/Q flip */ #define B43_NPHY_IQFLIP_ADC1 0x0001 /* ADC1 */ #define B43_NPHY_IQFLIP_ADC2 0x0010 /* ADC2 */ #define B43_NPHY_SISO_SNR_THRES B43_PHY_N(0x0B2) /* SISO SNR threshold */ #define B43_NPHY_SIGMA_N_MULT B43_PHY_N(0x0B3) /* Sigma N multiplier */ #define B43_NPHY_TXMACDELAY B43_PHY_N(0x0B4) /* TX MAC delay */ #define B43_NPHY_TXFRAMEDELAY B43_PHY_N(0x0B5) /* TX frame delay */ #define B43_NPHY_MLPARM B43_PHY_N(0x0B6) /* ML parameters */ #define B43_NPHY_MLCTL B43_PHY_N(0x0B7) /* ML control */ #define B43_NPHY_WWISE_20NCYCDAT B43_PHY_N(0x0B8) /* WWiSE 20 N cyc data */ #define B43_NPHY_WWISE_40NCYCDAT B43_PHY_N(0x0B9) /* WWiSE 40 N cyc data */ #define B43_NPHY_TGNSYNC_20NCYCDAT B43_PHY_N(0x0BA) /* TGNsync 20 N cyc data */ #define B43_NPHY_TGNSYNC_40NCYCDAT B43_PHY_N(0x0BB) /* TGNsync 40 N cyc data */ #define B43_NPHY_INITSWIZP B43_PHY_N(0x0BC) /* Initial swizzle pattern */ #define B43_NPHY_TXTAILCNT B43_PHY_N(0x0BD) /* TX tail count value */ #define B43_NPHY_BPHY_CTL1 B43_PHY_N(0x0BE) /* B PHY control 1 */ #define B43_NPHY_BPHY_CTL2 B43_PHY_N(0x0BF) /* B PHY control 2 */ #define B43_NPHY_BPHY_CTL2_LUT 0x001F /* LUT index */ #define B43_NPHY_BPHY_CTL2_LUT_SHIFT 0 #define B43_NPHY_BPHY_CTL2_MACDEL 0x7FE0 /* MAC delay */ #define B43_NPHY_BPHY_CTL2_MACDEL_SHIFT 5 #define B43_NPHY_IQLOCAL_CMD B43_PHY_N(0x0C0) /* I/Q LO cal command */ #define B43_NPHY_IQLOCAL_CMD_EN 0x8000 #define B43_NPHY_IQLOCAL_CMDNNUM B43_PHY_N(0x0C1) /* I/Q LO cal command N num */ #define B43_NPHY_IQLOCAL_CMDGCTL B43_PHY_N(0x0C2) /* I/Q LO cal command G control */ #define B43_NPHY_SAMP_CMD B43_PHY_N(0x0C3) /* Sample command */ #define B43_NPHY_SAMP_CMD_STOP 0x0002 /* Stop */ #define B43_NPHY_SAMP_LOOPCNT B43_PHY_N(0x0C4) /* Sample loop count */ #define B43_NPHY_SAMP_WAITCNT B43_PHY_N(0x0C5) /* Sample wait count */ #define B43_NPHY_SAMP_DEPCNT B43_PHY_N(0x0C6) /* Sample depth count */ #define B43_NPHY_SAMP_STAT B43_PHY_N(0x0C7) /* Sample status */ #define B43_NPHY_GPIO_LOOEN B43_PHY_N(0x0C8) /* GPIO low out enable */ #define B43_NPHY_GPIO_HIOEN B43_PHY_N(0x0C9) /* GPIO high out enable */ #define B43_NPHY_GPIO_SEL B43_PHY_N(0x0CA) /* GPIO select */ #define B43_NPHY_GPIO_CLKCTL B43_PHY_N(0x0CB) /* GPIO clock control */ #define B43_NPHY_TXF_20CO_AS0 B43_PHY_N(0x0CC) /* TX filter 20 coeff A stage 0 */ #define B43_NPHY_TXF_20CO_AS1 B43_PHY_N(0x0CD) /* TX filter 20 coeff A stage 1 */ #define B43_NPHY_TXF_20CO_AS2 B43_PHY_N(0x0CE) /* TX filter 20 coeff A stage 2 */ #define B43_NPHY_TXF_20CO_B32S0 B43_PHY_N(0x0CF) /* TX filter 20 coeff B32 stage 0 */ #define B43_NPHY_TXF_20CO_B1S0 B43_PHY_N(0x0D0) /* TX filter 20 coeff B1 stage 0 */ #define B43_NPHY_TXF_20CO_B32S1 B43_PHY_N(0x0D1) /* TX filter 20 coeff B32 stage 1 */ #define B43_NPHY_TXF_20CO_B1S1 B43_PHY_N(0x0D2) /* TX filter 20 coeff B1 stage 1 */ #define B43_NPHY_TXF_20CO_B32S2 B43_PHY_N(0x0D3) /* TX filter 20 coeff B32 stage 2 */ #define B43_NPHY_TXF_20CO_B1S2 B43_PHY_N(0x0D4) /* TX filter 20 coeff B1 stage 2 */ #define B43_NPHY_SIGFLDTOL B43_PHY_N(0x0D5) /* Signal fld tolerance */ #define B43_NPHY_TXSERFLD B43_PHY_N(0x0D6) /* TX service field */ #define B43_NPHY_AFESEQ_RX2TX_PUD B43_PHY_N(0x0D7) /* AFE seq RX2TX power up/down delay */ #define B43_NPHY_AFESEQ_TX2RX_PUD B43_PHY_N(0x0D8) /* AFE seq TX2RX power up/down delay */ #define B43_NPHY_TGNSYNC_SCRAMI0 B43_PHY_N(0x0D9) /* TGNsync scram init 0 */ #define B43_NPHY_TGNSYNC_SCRAMI1 B43_PHY_N(0x0DA) /* TGNsync scram init 1 */ #define B43_NPHY_INITSWIZPATTLEG B43_PHY_N(0x0DB) /* Initial swizzle pattern leg */ #define B43_NPHY_BPHY_CTL3 B43_PHY_N(0x0DC) /* B PHY control 3 */ #define B43_NPHY_BPHY_CTL3_SCALE 0x00FF /* Scale */ #define B43_NPHY_BPHY_CTL3_SCALE_SHIFT 0 #define B43_NPHY_BPHY_CTL3_FSC 0xFF00 /* Frame start count value */ #define B43_NPHY_BPHY_CTL3_FSC_SHIFT 8 #define B43_NPHY_BPHY_CTL4 B43_PHY_N(0x0DD) /* B PHY control 4 */ #define B43_NPHY_C1_TXBBMULT B43_PHY_N(0x0DE) /* Core 1 TX BB multiplier */ #define B43_NPHY_C2_TXBBMULT B43_PHY_N(0x0DF) /* Core 2 TX BB multiplier */ #define B43_NPHY_TXF_40CO_AS0 B43_PHY_N(0x0E1) /* TX filter 40 coeff A stage 0 */ #define B43_NPHY_TXF_40CO_AS1 B43_PHY_N(0x0E2) /* TX filter 40 coeff A stage 1 */ #define B43_NPHY_TXF_40CO_AS2 B43_PHY_N(0x0E3) /* TX filter 40 coeff A stage 2 */ #define B43_NPHY_TXF_40CO_B32S0 B43_PHY_N(0x0E4) /* TX filter 40 coeff B32 stage 0 */ #define B43_NPHY_TXF_40CO_B1S0 B43_PHY_N(0x0E5) /* TX filter 40 coeff B1 stage 0 */ #define B43_NPHY_TXF_40CO_B32S1 B43_PHY_N(0x0E6) /* TX filter 40 coeff B32 stage 1 */ #define B43_NPHY_TXF_40CO_B1S1 B43_PHY_N(0x0E7) /* TX filter 40 coeff B1 stage 1 */ #define B43_NPHY_TXF_40CO_B32S2 B43_PHY_N(0x0E8) /* TX filter 40 coeff B32 stage 2 */ #define B43_NPHY_TXF_40CO_B1S2 B43_PHY_N(0x0E9) /* TX filter 40 coeff B1 stage 2 */ #define B43_NPHY_BIST_STAT2 B43_PHY_N(0x0EA) /* BIST status 2 */ #define B43_NPHY_BIST_STAT3 B43_PHY_N(0x0EB) /* BIST status 3 */ #define B43_NPHY_RFCTL_OVER B43_PHY_N(0x0EC) /* RF control override */ #define B43_NPHY_MIMOCFG B43_PHY_N(0x0ED) /* MIMO config */ #define B43_NPHY_MIMOCFG_GFMIX 0x0004 /* Greenfield or mixed mode */ #define B43_NPHY_MIMOCFG_AUTO 0x0100 /* Greenfield/mixed mode auto */ #define B43_NPHY_RADAR_BLNKCTL B43_PHY_N(0x0EE) /* Radar blank control */ #define B43_NPHY_A0RADAR_FIFOCTL B43_PHY_N(0x0EF) /* Antenna 0 radar FIFO control */ #define B43_NPHY_A1RADAR_FIFOCTL B43_PHY_N(0x0F0) /* Antenna 1 radar FIFO control */ #define B43_NPHY_A0RADAR_FIFODAT B43_PHY_N(0x0F1) /* Antenna 0 radar FIFO data */ #define B43_NPHY_A1RADAR_FIFODAT B43_PHY_N(0x0F2) /* Antenna 1 radar FIFO data */ #define B43_NPHY_RADAR_THRES0 B43_PHY_N(0x0F3) /* Radar threshold 0 */ #define B43_NPHY_RADAR_THRES1 B43_PHY_N(0x0F4) /* Radar threshold 1 */ #define B43_NPHY_RADAR_THRES0R B43_PHY_N(0x0F5) /* Radar threshold 0R */ #define B43_NPHY_RADAR_THRES1R B43_PHY_N(0x0F6) /* Radar threshold 1R */ #define B43_NPHY_CSEN_20IN40_DLEN B43_PHY_N(0x0F7) /* Carrier sense 20 in 40 dwell length */ #define B43_NPHY_RFCTL_LUT_TRSW_LO1 B43_PHY_N(0x0F8) /* RF control LUT TRSW lower 1 */ #define B43_NPHY_RFCTL_LUT_TRSW_UP1 B43_PHY_N(0x0F9) /* RF control LUT TRSW upper 1 */ #define B43_NPHY_RFCTL_LUT_TRSW_LO2 B43_PHY_N(0x0FA) /* RF control LUT TRSW lower 2 */ #define B43_NPHY_RFCTL_LUT_TRSW_UP2 B43_PHY_N(0x0FB) /* RF control LUT TRSW upper 2 */ #define B43_NPHY_RFCTL_LUT_TRSW_LO3 B43_PHY_N(0x0FC) /* RF control LUT TRSW lower 3 */ #define B43_NPHY_RFCTL_LUT_TRSW_UP3 B43_PHY_N(0x0FD) /* RF control LUT TRSW upper 3 */ #define B43_NPHY_RFCTL_LUT_TRSW_LO4 B43_PHY_N(0x0FE) /* RF control LUT TRSW lower 4 */ #define B43_NPHY_RFCTL_LUT_TRSW_UP4 B43_PHY_N(0x0FF) /* RF control LUT TRSW upper 4 */ #define B43_NPHY_RFCTL_LUT_LNAPA1 B43_PHY_N(0x100) /* RF control LUT LNA PA 1 */ #define B43_NPHY_RFCTL_LUT_LNAPA2 B43_PHY_N(0x101) /* RF control LUT LNA PA 2 */ #define B43_NPHY_RFCTL_LUT_LNAPA3 B43_PHY_N(0x102) /* RF control LUT LNA PA 3 */ #define B43_NPHY_RFCTL_LUT_LNAPA4 B43_PHY_N(0x103) /* RF control LUT LNA PA 4 */ #define B43_NPHY_TGNSYNC_CRCM0 B43_PHY_N(0x104) /* TGNsync CRC mask 0 */ #define B43_NPHY_TGNSYNC_CRCM1 B43_PHY_N(0x105) /* TGNsync CRC mask 1 */ #define B43_NPHY_TGNSYNC_CRCM2 B43_PHY_N(0x106) /* TGNsync CRC mask 2 */ #define B43_NPHY_TGNSYNC_CRCM3 B43_PHY_N(0x107) /* TGNsync CRC mask 3 */ #define B43_NPHY_TGNSYNC_CRCM4 B43_PHY_N(0x108) /* TGNsync CRC mask 4 */ #define B43_NPHY_CRCPOLY B43_PHY_N(0x109) /* CRC polynomial */ #define B43_NPHY_SIGCNT B43_PHY_N(0x10A) /* # sig count */ #define B43_NPHY_SIGSTARTBIT_CTL B43_PHY_N(0x10B) /* Sig start bit control */ #define B43_NPHY_CRCPOLY_ORDER B43_PHY_N(0x10C) /* CRC polynomial order */ #define B43_NPHY_RFCTL_CST0 B43_PHY_N(0x10D) /* RF control core swap table 0 */ #define B43_NPHY_RFCTL_CST1 B43_PHY_N(0x10E) /* RF control core swap table 1 */ #define B43_NPHY_RFCTL_CST2O B43_PHY_N(0x10F) /* RF control core swap table 2 + others */ #define B43_NPHY_BPHY_CTL5 B43_PHY_N(0x111) /* B PHY control 5 */ #define B43_NPHY_RFSEQ_LPFBW B43_PHY_N(0x112) /* RF seq LPF bandwidth */ #define B43_NPHY_TSSIBIAS1 B43_PHY_N(0x114) /* TSSI bias val 1 */ #define B43_NPHY_TSSIBIAS2 B43_PHY_N(0x115) /* TSSI bias val 2 */ #define B43_NPHY_TSSIBIAS_BIAS 0x00FF /* Bias */ #define B43_NPHY_TSSIBIAS_BIAS_SHIFT 0 #define B43_NPHY_TSSIBIAS_VAL 0xFF00 /* Value */ #define B43_NPHY_TSSIBIAS_VAL_SHIFT 8 #define B43_NPHY_ESTPWR1 B43_PHY_N(0x118) /* Estimated power 1 */ #define B43_NPHY_ESTPWR2 B43_PHY_N(0x119) /* Estimated power 2 */ #define B43_NPHY_ESTPWR_PWR 0x00FF /* Estimated power */ #define B43_NPHY_ESTPWR_PWR_SHIFT 0 #define B43_NPHY_ESTPWR_VALID 0x0100 /* Estimated power valid */ #define B43_NPHY_TSSI_MAXTXFDT B43_PHY_N(0x11C) /* TSSI max TX frame delay time */ #define B43_NPHY_TSSI_MAXTXFDT_VAL 0x00FF /* max TX frame delay time */ #define B43_NPHY_TSSI_MAXTXFDT_VAL_SHIFT 0 #define B43_NPHY_TSSI_MAXTDT B43_PHY_N(0x11D) /* TSSI max TSSI delay time */ #define B43_NPHY_TSSI_MAXTDT_VAL 0x00FF /* max TSSI delay time */ #define B43_NPHY_TSSI_MAXTDT_VAL_SHIFT 0 #define B43_NPHY_ITSSI1 B43_PHY_N(0x11E) /* TSSI idle 1 */ #define B43_NPHY_ITSSI2 B43_PHY_N(0x11F) /* TSSI idle 2 */ #define B43_NPHY_ITSSI_VAL 0x00FF /* Idle TSSI */ #define B43_NPHY_ITSSI_VAL_SHIFT 0 #define B43_NPHY_TSSIMODE B43_PHY_N(0x122) /* TSSI mode */ #define B43_NPHY_TSSIMODE_EN 0x0001 /* TSSI enable */ #define B43_NPHY_TSSIMODE_PDEN 0x0002 /* Power det enable */ #define B43_NPHY_RXMACIFM B43_PHY_N(0x123) /* RX Macif mode */ #define B43_NPHY_CRSIT_COCNT_LO B43_PHY_N(0x124) /* CRS idle time CRS-on count (low) */ #define B43_NPHY_CRSIT_COCNT_HI B43_PHY_N(0x125) /* CRS idle time CRS-on count (high) */ #define B43_NPHY_CRSIT_MTCNT_LO B43_PHY_N(0x126) /* CRS idle time measure time count (low) */ #define B43_NPHY_CRSIT_MTCNT_HI B43_PHY_N(0x127) /* CRS idle time measure time count (high) */ #define B43_NPHY_SAMTWC B43_PHY_N(0x128) /* Sample tail wait count */ #define B43_NPHY_IQEST_CMD B43_PHY_N(0x129) /* I/Q estimate command */ #define B43_NPHY_IQEST_CMD_START 0x0001 /* Start */ #define B43_NPHY_IQEST_CMD_MODE 0x0002 /* Mode */ #define B43_NPHY_IQEST_WT B43_PHY_N(0x12A) /* I/Q estimate wait time */ #define B43_NPHY_IQEST_WT_VAL 0x00FF /* Wait time */ #define B43_NPHY_IQEST_WT_VAL_SHIFT 0 #define B43_NPHY_IQEST_SAMCNT B43_PHY_N(0x12B) /* I/Q estimate sample count */ #define B43_NPHY_IQEST_IQACC_LO0 B43_PHY_N(0x12C) /* I/Q estimate I/Q acc lo 0 */ #define B43_NPHY_IQEST_IQACC_HI0 B43_PHY_N(0x12D) /* I/Q estimate I/Q acc hi 0 */ #define B43_NPHY_IQEST_IPACC_LO0 B43_PHY_N(0x12E) /* I/Q estimate I power acc lo 0 */ #define B43_NPHY_IQEST_IPACC_HI0 B43_PHY_N(0x12F) /* I/Q estimate I power acc hi 0 */ #define B43_NPHY_IQEST_QPACC_LO0 B43_PHY_N(0x130) /* I/Q estimate Q power acc lo 0 */ #define B43_NPHY_IQEST_QPACC_HI0 B43_PHY_N(0x131) /* I/Q estimate Q power acc hi 0 */ #define B43_NPHY_IQEST_IQACC_LO1 B43_PHY_N(0x134) /* I/Q estimate I/Q acc lo 1 */ #define B43_NPHY_IQEST_IQACC_HI1 B43_PHY_N(0x135) /* I/Q estimate I/Q acc hi 1 */ #define B43_NPHY_IQEST_IPACC_LO1 B43_PHY_N(0x136) /* I/Q estimate I power acc lo 1 */ #define B43_NPHY_IQEST_IPACC_HI1 B43_PHY_N(0x137) /* I/Q estimate I power acc hi 1 */ #define B43_NPHY_IQEST_QPACC_LO1 B43_PHY_N(0x138) /* I/Q estimate Q power acc lo 1 */ #define B43_NPHY_IQEST_QPACC_HI1 B43_PHY_N(0x139) /* I/Q estimate Q power acc hi 1 */ #define B43_NPHY_MIMO_CRSTXEXT B43_PHY_N(0x13A) /* MIMO PHY CRS TX extension */ #define B43_NPHY_PWRDET1 B43_PHY_N(0x13B) /* Power det 1 */ #define B43_NPHY_PWRDET2 B43_PHY_N(0x13C) /* Power det 2 */ #define B43_NPHY_MAXRSSI_DTIME B43_PHY_N(0x13F) /* RSSI max RSSI delay time */ #define B43_NPHY_PIL_DW0 B43_PHY_N(0x141) /* Pilot data weight 0 */ #define B43_NPHY_PIL_DW1 B43_PHY_N(0x142) /* Pilot data weight 1 */ #define B43_NPHY_PIL_DW2 B43_PHY_N(0x143) /* Pilot data weight 2 */ #define B43_NPHY_PIL_DW_BPSK 0x000F /* BPSK */ #define B43_NPHY_PIL_DW_BPSK_SHIFT 0 #define B43_NPHY_PIL_DW_QPSK 0x00F0 /* QPSK */ #define B43_NPHY_PIL_DW_QPSK_SHIFT 4 #define B43_NPHY_PIL_DW_16QAM 0x0F00 /* 16-QAM */ #define B43_NPHY_PIL_DW_16QAM_SHIFT 8 #define B43_NPHY_PIL_DW_64QAM 0xF000 /* 64-QAM */ #define B43_NPHY_PIL_DW_64QAM_SHIFT 12 #define B43_NPHY_FMDEM_CFG B43_PHY_N(0x144) /* FM demodulation config */ #define B43_NPHY_PHASETR_A0 B43_PHY_N(0x145) /* Phase track alpha 0 */ #define B43_NPHY_PHASETR_A1 B43_PHY_N(0x146) /* Phase track alpha 1 */ #define B43_NPHY_PHASETR_A2 B43_PHY_N(0x147) /* Phase track alpha 2 */ #define B43_NPHY_PHASETR_B0 B43_PHY_N(0x148) /* Phase track beta 0 */ #define B43_NPHY_PHASETR_B1 B43_PHY_N(0x149) /* Phase track beta 1 */ #define B43_NPHY_PHASETR_B2 B43_PHY_N(0x14A) /* Phase track beta 2 */ #define B43_NPHY_PHASETR_CHG0 B43_PHY_N(0x14B) /* Phase track change 0 */ #define B43_NPHY_PHASETR_CHG1 B43_PHY_N(0x14C) /* Phase track change 1 */ #define B43_NPHY_PHASETW_OFF B43_PHY_N(0x14D) /* Phase track offset */ #define B43_NPHY_RFCTL_DBG B43_PHY_N(0x14E) /* RF control debug */ #define B43_NPHY_CCK_SHIFTB_REF B43_PHY_N(0x150) /* CCK shiftbits reference var */ #define B43_NPHY_OVER_DGAIN0 B43_PHY_N(0x152) /* Override digital gain 0 */ #define B43_NPHY_OVER_DGAIN1 B43_PHY_N(0x153) /* Override digital gain 1 */ #define B43_NPHY_OVER_DGAIN_FDGV 0x0007 /* Force digital gain value */ #define B43_NPHY_OVER_DGAIN_FDGV_SHIFT 0 #define B43_NPHY_OVER_DGAIN_FDGEN 0x0008 /* Force digital gain enable */ #define B43_NPHY_OVER_DGAIN_CCKDGECV 0xFF00 /* CCK digital gain enable count value */ #define B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT 8 #define B43_NPHY_BIST_STAT4 B43_PHY_N(0x156) /* BIST status 4 */ #define B43_NPHY_RADAR_MAL B43_PHY_N(0x157) /* Radar MA length */ #define B43_NPHY_RADAR_SRCCTL B43_PHY_N(0x158) /* Radar search control */ #define B43_NPHY_VLD_DTSIG B43_PHY_N(0x159) /* VLD data tones sig */ #define B43_NPHY_VLD_DTDAT B43_PHY_N(0x15A) /* VLD data tones data */ #define B43_NPHY_C1_BPHY_RXIQCA0 B43_PHY_N(0x15B) /* Core 1 B PHY RX I/Q comp A0 */ #define B43_NPHY_C1_BPHY_RXIQCB0 B43_PHY_N(0x15C) /* Core 1 B PHY RX I/Q comp B0 */ #define B43_NPHY_C2_BPHY_RXIQCA1 B43_PHY_N(0x15D) /* Core 2 B PHY RX I/Q comp A1 */ #define B43_NPHY_C2_BPHY_RXIQCB1 B43_PHY_N(0x15E) /* Core 2 B PHY RX I/Q comp B1 */ #define B43_NPHY_FREQGAIN0 B43_PHY_N(0x160) /* Frequency gain 0 */ #define B43_NPHY_FREQGAIN1 B43_PHY_N(0x161) /* Frequency gain 1 */ #define B43_NPHY_FREQGAIN2 B43_PHY_N(0x162) /* Frequency gain 2 */ #define B43_NPHY_FREQGAIN3 B43_PHY_N(0x163) /* Frequency gain 3 */ #define B43_NPHY_FREQGAIN4 B43_PHY_N(0x164) /* Frequency gain 4 */ #define B43_NPHY_FREQGAIN5 B43_PHY_N(0x165) /* Frequency gain 5 */ #define B43_NPHY_FREQGAIN6 B43_PHY_N(0x166) /* Frequency gain 6 */ #define B43_NPHY_FREQGAIN7 B43_PHY_N(0x167) /* Frequency gain 7 */ #define B43_NPHY_FREQGAIN_BYPASS B43_PHY_N(0x168) /* Frequency gain bypass */ #define B43_NPHY_TRLOSS B43_PHY_N(0x169) /* TR loss value */ #define B43_NPHY_C1_ADCCLIP B43_PHY_N(0x16A) /* Core 1 ADC clip */ #define B43_NPHY_C2_ADCCLIP B43_PHY_N(0x16B) /* Core 2 ADC clip */ #define B43_NPHY_LTRN_OFFGAIN B43_PHY_N(0x16F) /* LTRN offset gain */ #define B43_NPHY_LTRN_OFF B43_PHY_N(0x170) /* LTRN offset */ #define B43_NPHY_NRDATAT_WWISE20SIG B43_PHY_N(0x171) /* # data tones WWiSE 20 sig */ #define B43_NPHY_NRDATAT_WWISE40SIG B43_PHY_N(0x172) /* # data tones WWiSE 40 sig */ #define B43_NPHY_NRDATAT_TGNSYNC20SIG B43_PHY_N(0x173) /* # data tones TGNsync 20 sig */ #define B43_NPHY_NRDATAT_TGNSYNC40SIG B43_PHY_N(0x174) /* # data tones TGNsync 40 sig */ #define B43_NPHY_WWISE_CRCM0 B43_PHY_N(0x175) /* WWiSE CRC mask 0 */ #define B43_NPHY_WWISE_CRCM1 B43_PHY_N(0x176) /* WWiSE CRC mask 1 */ #define B43_NPHY_WWISE_CRCM2 B43_PHY_N(0x177) /* WWiSE CRC mask 2 */ #define B43_NPHY_WWISE_CRCM3 B43_PHY_N(0x178) /* WWiSE CRC mask 3 */ #define B43_NPHY_WWISE_CRCM4 B43_PHY_N(0x179) /* WWiSE CRC mask 4 */ #define B43_NPHY_CHANEST_CDDSH B43_PHY_N(0x17A) /* Channel estimate CDD shift */ #define B43_NPHY_HTAGC_WCNT B43_PHY_N(0x17B) /* HT ADC wait counters */ #define B43_NPHY_SQPARM B43_PHY_N(0x17C) /* SQ params */ #define B43_NPHY_MCSDUP6M B43_PHY_N(0x17D) /* MCS dup 6M */ #define B43_NPHY_NDATAT_DUP40 B43_PHY_N(0x17E) /* # data tones dup 40 */ #define B43_NPHY_DUP40_TGNSYNC_CYCD B43_PHY_N(0x17F) /* Dup40 TGNsync cycle data */ #define B43_NPHY_DUP40_GFBL B43_PHY_N(0x180) /* Dup40 GF format BL address */ #define B43_NPHY_DUP40_BL B43_PHY_N(0x181) /* Dup40 format BL address */ #define B43_NPHY_LEGDUP_FTA B43_PHY_N(0x182) /* Legacy dup frm table address */ #define B43_NPHY_PACPROC_DBG B43_PHY_N(0x183) /* Packet processing debug */ #define B43_NPHY_PIL_CYC1 B43_PHY_N(0x184) /* Pilot cycle counter 1 */ #define B43_NPHY_PIL_CYC2 B43_PHY_N(0x185) /* Pilot cycle counter 2 */ #define B43_NPHY_TXF_20CO_S0A1 B43_PHY_N(0x186) /* TX filter 20 coeff stage 0 A1 */ #define B43_NPHY_TXF_20CO_S0A2 B43_PHY_N(0x187) /* TX filter 20 coeff stage 0 A2 */ #define B43_NPHY_TXF_20CO_S1A1 B43_PHY_N(0x188) /* TX filter 20 coeff stage 1 A1 */ #define B43_NPHY_TXF_20CO_S1A2 B43_PHY_N(0x189) /* TX filter 20 coeff stage 1 A2 */ #define B43_NPHY_TXF_20CO_S2A1 B43_PHY_N(0x18A) /* TX filter 20 coeff stage 2 A1 */ #define B43_NPHY_TXF_20CO_S2A2 B43_PHY_N(0x18B) /* TX filter 20 coeff stage 2 A2 */ #define B43_NPHY_TXF_20CO_S0B1 B43_PHY_N(0x18C) /* TX filter 20 coeff stage 0 B1 */ #define B43_NPHY_TXF_20CO_S0B2 B43_PHY_N(0x18D) /* TX filter 20 coeff stage 0 B2 */ #define B43_NPHY_TXF_20CO_S0B3 B43_PHY_N(0x18E) /* TX filter 20 coeff stage 0 B3 */ #define B43_NPHY_TXF_20CO_S1B1 B43_PHY_N(0x18F) /* TX filter 20 coeff stage 1 B1 */ #define B43_NPHY_TXF_20CO_S1B2 B43_PHY_N(0x190) /* TX filter 20 coeff stage 1 B2 */ #define B43_NPHY_TXF_20CO_S1B3 B43_PHY_N(0x191) /* TX filter 20 coeff stage 1 B3 */ #define B43_NPHY_TXF_20CO_S2B1 B43_PHY_N(0x192) /* TX filter 20 coeff stage 2 B1 */ #define B43_NPHY_TXF_20CO_S2B2 B43_PHY_N(0x193) /* TX filter 20 coeff stage 2 B2 */ #define B43_NPHY_TXF_20CO_S2B3 B43_PHY_N(0x194) /* TX filter 20 coeff stage 2 B3 */ #define B43_NPHY_TXF_40CO_S0A1 B43_PHY_N(0x195) /* TX filter 40 coeff stage 0 A1 */ #define B43_NPHY_TXF_40CO_S0A2 B43_PHY_N(0x196) /* TX filter 40 coeff stage 0 A2 */ #define B43_NPHY_TXF_40CO_S1A1 B43_PHY_N(0x197) /* TX filter 40 coeff stage 1 A1 */ #define B43_NPHY_TXF_40CO_S1A2 B43_PHY_N(0x198) /* TX filter 40 coeff stage 1 A2 */ #define B43_NPHY_TXF_40CO_S2A1 B43_PHY_N(0x199) /* TX filter 40 coeff stage 2 A1 */ #define B43_NPHY_TXF_40CO_S2A2 B43_PHY_N(0x19A) /* TX filter 40 coeff stage 2 A2 */ #define B43_NPHY_TXF_40CO_S0B1 B43_PHY_N(0x19B) /* TX filter 40 coeff stage 0 B1 */ #define B43_NPHY_TXF_40CO_S0B2 B43_PHY_N(0x19C) /* TX filter 40 coeff stage 0 B2 */ #define B43_NPHY_TXF_40CO_S0B3 B43_PHY_N(0x19D) /* TX filter 40 coeff stage 0 B3 */ #define B43_NPHY_TXF_40CO_S1B1 B43_PHY_N(0x19E) /* TX filter 40 coeff stage 1 B1 */ #define B43_NPHY_TXF_40CO_S1B2 B43_PHY_N(0x19F) /* TX filter 40 coeff stage 1 B2 */ #define B43_NPHY_TXF_40CO_S1B3 B43_PHY_N(0x1A0) /* TX filter 40 coeff stage 1 B3 */ #define B43_NPHY_TXF_40CO_S2B1 B43_PHY_N(0x1A1) /* TX filter 40 coeff stage 2 B1 */ #define B43_NPHY_TXF_40CO_S2B2 B43_PHY_N(0x1A2) /* TX filter 40 coeff stage 2 B2 */ #define B43_NPHY_TXF_40CO_S2B3 B43_PHY_N(0x1A3) /* TX filter 40 coeff stage 2 B3 */ #define B43_NPHY_RSSIMC_0I_RSSI_X B43_PHY_N(0x1A4) /* RSSI multiplication coefficient 0 I RSSI X */ #define B43_NPHY_RSSIMC_0I_RSSI_Y B43_PHY_N(0x1A5) /* RSSI multiplication coefficient 0 I RSSI Y */ #define B43_NPHY_RSSIMC_0I_RSSI_Z B43_PHY_N(0x1A6) /* RSSI multiplication coefficient 0 I RSSI Z */ #define B43_NPHY_RSSIMC_0I_TBD B43_PHY_N(0x1A7) /* RSSI multiplication coefficient 0 I TBD */ #define B43_NPHY_RSSIMC_0I_PWRDET B43_PHY_N(0x1A8) /* RSSI multiplication coefficient 0 I power det */ #define B43_NPHY_RSSIMC_0I_TSSI B43_PHY_N(0x1A9) /* RSSI multiplication coefficient 0 I TSSI */ #define B43_NPHY_RSSIMC_0Q_RSSI_X B43_PHY_N(0x1AA) /* RSSI multiplication coefficient 0 Q RSSI X */ #define B43_NPHY_RSSIMC_0Q_RSSI_Y B43_PHY_N(0x1AB) /* RSSI multiplication coefficient 0 Q RSSI Y */ #define B43_NPHY_RSSIMC_0Q_RSSI_Z B43_PHY_N(0x1AC) /* RSSI multiplication coefficient 0 Q RSSI Z */ #define B43_NPHY_RSSIMC_0Q_TBD B43_PHY_N(0x1AD) /* RSSI multiplication coefficient 0 Q TBD */ #define B43_NPHY_RSSIMC_0Q_PWRDET B43_PHY_N(0x1AE) /* RSSI multiplication coefficient 0 Q power det */ #define B43_NPHY_RSSIMC_0Q_TSSI B43_PHY_N(0x1AF) /* RSSI multiplication coefficient 0 Q TSSI */ #define B43_NPHY_RSSIMC_1I_RSSI_X B43_PHY_N(0x1B0) /* RSSI multiplication coefficient 1 I RSSI X */ #define B43_NPHY_RSSIMC_1I_RSSI_Y B43_PHY_N(0x1B1) /* RSSI multiplication coefficient 1 I RSSI Y */ #define B43_NPHY_RSSIMC_1I_RSSI_Z B43_PHY_N(0x1B2) /* RSSI multiplication coefficient 1 I RSSI Z */ #define B43_NPHY_RSSIMC_1I_TBD B43_PHY_N(0x1B3) /* RSSI multiplication coefficient 1 I TBD */ #define B43_NPHY_RSSIMC_1I_PWRDET B43_PHY_N(0x1B4) /* RSSI multiplication coefficient 1 I power det */ #define B43_NPHY_RSSIMC_1I_TSSI B43_PHY_N(0x1B5) /* RSSI multiplication coefficient 1 I TSSI */ #define B43_NPHY_RSSIMC_1Q_RSSI_X B43_PHY_N(0x1B6) /* RSSI multiplication coefficient 1 Q RSSI X */ #define B43_NPHY_RSSIMC_1Q_RSSI_Y B43_PHY_N(0x1B7) /* RSSI multiplication coefficient 1 Q RSSI Y */ #define B43_NPHY_RSSIMC_1Q_RSSI_Z B43_PHY_N(0x1B8) /* RSSI multiplication coefficient 1 Q RSSI Z */ #define B43_NPHY_RSSIMC_1Q_TBD B43_PHY_N(0x1B9) /* RSSI multiplication coefficient 1 Q TBD */ #define B43_NPHY_RSSIMC_1Q_PWRDET B43_PHY_N(0x1BA) /* RSSI multiplication coefficient 1 Q power det */ #define B43_NPHY_RSSIMC_1Q_TSSI B43_PHY_N(0x1BB) /* RSSI multiplication coefficient 1 Q TSSI */ #define B43_NPHY_SAMC_WCNT B43_PHY_N(0x1BC) /* Sample collect wait counter */ #define B43_NPHY_PTHROUGH_CNT B43_PHY_N(0x1BD) /* Pass-through counter */ #define B43_NPHY_LTRN_OFF_G20L B43_PHY_N(0x1C4) /* LTRN offset gain 20L */ #define B43_NPHY_LTRN_OFF_20L B43_PHY_N(0x1C5) /* LTRN offset 20L */ #define B43_NPHY_LTRN_OFF_G20U B43_PHY_N(0x1C6) /* LTRN offset gain 20U */ #define B43_NPHY_LTRN_OFF_20U B43_PHY_N(0x1C7) /* LTRN offset 20U */ #define B43_NPHY_DSSSCCK_GAINSL B43_PHY_N(0x1C8) /* DSSS/CCK gain settle length */ #define B43_NPHY_GPIO_LOOUT B43_PHY_N(0x1C9) /* GPIO low out */ #define B43_NPHY_GPIO_HIOUT B43_PHY_N(0x1CA) /* GPIO high out */ #define B43_NPHY_CRS_CHECK B43_PHY_N(0x1CB) /* CRS check */ #define B43_NPHY_ML_LOGSS_RAT B43_PHY_N(0x1CC) /* ML/logss ratio */ #define B43_NPHY_DUPSCALE B43_PHY_N(0x1CD) /* Dup scale */ #define B43_NPHY_BW1A B43_PHY_N(0x1CE) /* BW 1A */ #define B43_NPHY_BW2 B43_PHY_N(0x1CF) /* BW 2 */ #define B43_NPHY_BW3 B43_PHY_N(0x1D0) /* BW 3 */ #define B43_NPHY_BW4 B43_PHY_N(0x1D1) /* BW 4 */ #define B43_NPHY_BW5 B43_PHY_N(0x1D2) /* BW 5 */ #define B43_NPHY_BW6 B43_PHY_N(0x1D3) /* BW 6 */ #define B43_NPHY_COALEN0 B43_PHY_N(0x1D4) /* Coarse length 0 */ #define B43_NPHY_COALEN1 B43_PHY_N(0x1D5) /* Coarse length 1 */ #define B43_NPHY_CRSTHRES_1U B43_PHY_N(0x1D6) /* CRS threshold 1 U */ #define B43_NPHY_CRSTHRES_2U B43_PHY_N(0x1D7) /* CRS threshold 2 U */ #define B43_NPHY_CRSTHRES_3U B43_PHY_N(0x1D8) /* CRS threshold 3 U */ #define B43_NPHY_CRSCTL_U B43_PHY_N(0x1D9) /* CRS control U */ #define B43_NPHY_CRSTHRES_1L B43_PHY_N(0x1DA) /* CRS threshold 1 L */ #define B43_NPHY_CRSTHRES_2L B43_PHY_N(0x1DB) /* CRS threshold 2 L */ #define B43_NPHY_CRSTHRES_3L B43_PHY_N(0x1DC) /* CRS threshold 3 L */ #define B43_NPHY_CRSCTL_L B43_PHY_N(0x1DD) /* CRS control L */ #define B43_NPHY_STRA_1U B43_PHY_N(0x1DE) /* STR address 1 U */ #define B43_NPHY_STRA_2U B43_PHY_N(0x1DF) /* STR address 2 U */ #define B43_NPHY_STRA_1L B43_PHY_N(0x1E0) /* STR address 1 L */ #define B43_NPHY_STRA_2L B43_PHY_N(0x1E1) /* STR address 2 L */ #define B43_NPHY_CRSCHECK1 B43_PHY_N(0x1E2) /* CRS check 1 */ #define B43_NPHY_CRSCHECK2 B43_PHY_N(0x1E3) /* CRS check 2 */ #define B43_NPHY_CRSCHECK3 B43_PHY_N(0x1E4) /* CRS check 3 */ #define B43_NPHY_JMPSTP0 B43_PHY_N(0x1E5) /* Jump step 0 */ #define B43_NPHY_JMPSTP1 B43_PHY_N(0x1E6) /* Jump step 1 */ #define B43_NPHY_TXPCTL_CMD B43_PHY_N(0x1E7) /* TX power control command */ #define B43_NPHY_TXPCTL_CMD_INIT 0x007F /* Init */ #define B43_NPHY_TXPCTL_CMD_INIT_SHIFT 0 #define B43_NPHY_TXPCTL_CMD_COEFF 0x2000 /* Power control coefficients */ #define B43_NPHY_TXPCTL_CMD_HWPCTLEN 0x4000 /* Hardware TX power control enable */ #define B43_NPHY_TXPCTL_CMD_PCTLEN 0x8000 /* TX power control enable */ #define B43_NPHY_TXPCTL_N B43_PHY_N(0x1E8) /* TX power control N num */ #define B43_NPHY_TXPCTL_N_TSSID 0x00FF /* N TSSI delay */ #define B43_NPHY_TXPCTL_N_TSSID_SHIFT 0 #define B43_NPHY_TXPCTL_N_NPTIL2 0x0700 /* N PT integer log2 */ #define B43_NPHY_TXPCTL_N_NPTIL2_SHIFT 8 #define B43_NPHY_TXPCTL_ITSSI B43_PHY_N(0x1E9) /* TX power control idle TSSI */ #define B43_NPHY_TXPCTL_ITSSI_0 0x003F /* Idle TSSI 0 */ #define B43_NPHY_TXPCTL_ITSSI_0_SHIFT 0 #define B43_NPHY_TXPCTL_ITSSI_1 0x3F00 /* Idle TSSI 1 */ #define B43_NPHY_TXPCTL_ITSSI_1_SHIFT 8 #define B43_NPHY_TXPCTL_ITSSI_BINF 0x8000 /* Raw TSSI offset bin format */ #define B43_NPHY_TXPCTL_TPWR B43_PHY_N(0x1EA) /* TX power control target power */ #define B43_NPHY_TXPCTL_TPWR_0 0x00FF /* Power 0 */ #define B43_NPHY_TXPCTL_TPWR_0_SHIFT 0 #define B43_NPHY_TXPCTL_TPWR_1 0xFF00 /* Power 1 */ #define B43_NPHY_TXPCTL_TPWR_1_SHIFT 8 #define B43_NPHY_TXPCTL_BIDX B43_PHY_N(0x1EB) /* TX power control base index */ #define B43_NPHY_TXPCTL_BIDX_0 0x007F /* uC base index 0 */ #define B43_NPHY_TXPCTL_BIDX_0_SHIFT 0 #define B43_NPHY_TXPCTL_BIDX_1 0x7F00 /* uC base index 1 */ #define B43_NPHY_TXPCTL_BIDX_1_SHIFT 8 #define B43_NPHY_TXPCTL_BIDX_LOAD 0x8000 /* Load base index */ #define B43_NPHY_TXPCTL_PIDX B43_PHY_N(0x1EC) /* TX power control power index */ #define B43_NPHY_TXPCTL_PIDX_0 0x007F /* uC power index 0 */ #define B43_NPHY_TXPCTL_PIDX_0_SHIFT 0 #define B43_NPHY_TXPCTL_PIDX_1 0x7F00 /* uC power index 1 */ #define B43_NPHY_TXPCTL_PIDX_1_SHIFT 8 #define B43_NPHY_C1_TXPCTL_STAT B43_PHY_N(0x1ED) /* Core 1 TX power control status */ #define B43_NPHY_C2_TXPCTL_STAT B43_PHY_N(0x1EE) /* Core 2 TX power control status */ #define B43_NPHY_TXPCTL_STAT_EST 0x00FF /* Estimated power */ #define B43_NPHY_TXPCTL_STAT_EST_SHIFT 0 #define B43_NPHY_TXPCTL_STAT_BIDX 0x7F00 /* Base index */ #define B43_NPHY_TXPCTL_STAT_BIDX_SHIFT 8 #define B43_NPHY_TXPCTL_STAT_ESTVALID 0x8000 /* Estimated power valid */ #define B43_NPHY_SMALLSGS_LEN B43_PHY_N(0x1EF) /* Small sig gain settle length */ #define B43_NPHY_PHYSTAT_GAIN0 B43_PHY_N(0x1F0) /* PHY stats gain info 0 */ #define B43_NPHY_PHYSTAT_GAIN1 B43_PHY_N(0x1F1) /* PHY stats gain info 1 */ #define B43_NPHY_PHYSTAT_FREQEST B43_PHY_N(0x1F2) /* PHY stats frequency estimate */ #define B43_NPHY_PHYSTAT_ADVRET B43_PHY_N(0x1F3) /* PHY stats ADV retard */ #define B43_NPHY_PHYLB_MODE B43_PHY_N(0x1F4) /* PHY loopback mode */ #define B43_NPHY_TONE_MIDX20_1 B43_PHY_N(0x1F5) /* Tone map index 20/1 */ #define B43_NPHY_TONE_MIDX20_2 B43_PHY_N(0x1F6) /* Tone map index 20/2 */ #define B43_NPHY_TONE_MIDX20_3 B43_PHY_N(0x1F7) /* Tone map index 20/3 */ #define B43_NPHY_TONE_MIDX40_1 B43_PHY_N(0x1F8) /* Tone map index 40/1 */ #define B43_NPHY_TONE_MIDX40_2 B43_PHY_N(0x1F9) /* Tone map index 40/2 */ #define B43_NPHY_TONE_MIDX40_3 B43_PHY_N(0x1FA) /* Tone map index 40/3 */ #define B43_NPHY_TONE_MIDX40_4 B43_PHY_N(0x1FB) /* Tone map index 40/4 */ #define B43_NPHY_PILTONE_MIDX1 B43_PHY_N(0x1FC) /* Pilot tone map index 1 */ #define B43_NPHY_PILTONE_MIDX2 B43_PHY_N(0x1FD) /* Pilot tone map index 2 */ #define B43_NPHY_PILTONE_MIDX3 B43_PHY_N(0x1FE) /* Pilot tone map index 3 */ #define B43_NPHY_TXRIFS_FRDEL B43_PHY_N(0x1FF) /* TX RIFS frame delay */ #define B43_NPHY_AFESEQ_RX2TX_PUD_40M B43_PHY_N(0x200) /* AFE seq rx2tx power up/down delay 40M */ #define B43_NPHY_AFESEQ_TX2RX_PUD_40M B43_PHY_N(0x201) /* AFE seq tx2rx power up/down delay 40M */ #define B43_NPHY_AFESEQ_RX2TX_PUD_20M B43_PHY_N(0x202) /* AFE seq rx2tx power up/down delay 20M */ #define B43_NPHY_AFESEQ_TX2RX_PUD_20M B43_PHY_N(0x203) /* AFE seq tx2rx power up/down delay 20M */ #define B43_NPHY_RX_SIGCTL B43_PHY_N(0x204) /* RX signal control */ #define B43_NPHY_RXPIL_CYCNT0 B43_PHY_N(0x205) /* RX pilot cycle counter 0 */ #define B43_NPHY_RXPIL_CYCNT1 B43_PHY_N(0x206) /* RX pilot cycle counter 1 */ #define B43_NPHY_RXPIL_CYCNT2 B43_PHY_N(0x207) /* RX pilot cycle counter 2 */ #define B43_NPHY_AFESEQ_RX2TX_PUD_10M B43_PHY_N(0x208) /* AFE seq rx2tx power up/down delay 10M */ #define B43_NPHY_AFESEQ_TX2RX_PUD_10M B43_PHY_N(0x209) /* AFE seq tx2rx power up/down delay 10M */ #define B43_NPHY_DSSSCCK_CRSEXTL B43_PHY_N(0x20A) /* DSSS/CCK CRS extension length */ #define B43_NPHY_ML_LOGSS_RATSLOPE B43_PHY_N(0x20B) /* ML/logss ratio slope */ #define B43_NPHY_RIFS_SRCTL B43_PHY_N(0x20C) /* RIFS search timeout length */ #define B43_NPHY_TXREALFD B43_PHY_N(0x20D) /* TX real frame delay */ #define B43_NPHY_HPANT_SWTHRES B43_PHY_N(0x20E) /* High power antenna switch threshold */ #define B43_NPHY_EDCRS_ASSTHRES0 B43_PHY_N(0x210) /* ED CRS assert threshold 0 */ #define B43_NPHY_EDCRS_ASSTHRES1 B43_PHY_N(0x211) /* ED CRS assert threshold 1 */ #define B43_NPHY_EDCRS_DEASSTHRES0 B43_PHY_N(0x212) /* ED CRS deassert threshold 0 */ #define B43_NPHY_EDCRS_DEASSTHRES1 B43_PHY_N(0x213) /* ED CRS deassert threshold 1 */ #define B43_NPHY_STR_WTIME20U B43_PHY_N(0x214) /* STR wait time 20U */ #define B43_NPHY_STR_WTIME20L B43_PHY_N(0x215) /* STR wait time 20L */ #define B43_NPHY_TONE_MIDX657M B43_PHY_N(0x216) /* Tone map index 657M */ #define B43_NPHY_HTSIGTONES B43_PHY_N(0x217) /* HT signal tones */ #define B43_NPHY_RSSI1 B43_PHY_N(0x219) /* RSSI value 1 */ #define B43_NPHY_RSSI2 B43_PHY_N(0x21A) /* RSSI value 2 */ #define B43_NPHY_CHAN_ESTHANG B43_PHY_N(0x21D) /* Channel estimate hang */ #define B43_NPHY_FINERX2_CGC B43_PHY_N(0x221) /* Fine RX 2 clock gate control */ #define B43_NPHY_FINERX2_CGC_DECGC 0x0008 /* Decode gated clocks */ #define B43_NPHY_TXPCTL_INIT B43_PHY_N(0x222) /* TX power control init */ #define B43_NPHY_TXPCTL_INIT_PIDXI1 0x00FF /* Power index init 1 */ #define B43_NPHY_TXPCTL_INIT_PIDXI1_SHIFT 0 #define B43_NPHY_PAPD_EN0 B43_PHY_N(0x297) /* PAPD Enable0 TBD */ #define B43_NPHY_EPS_TABLE_ADJ0 B43_PHY_N(0x298) /* EPS Table Adj0 TBD */ #define B43_NPHY_PAPD_EN1 B43_PHY_N(0x29B) /* PAPD Enable1 TBD */ #define B43_NPHY_EPS_TABLE_ADJ1 B43_PHY_N(0x29C) /* EPS Table Adj1 TBD */ #define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001) /* BB config */ #define B43_PHY_B_TEST B43_PHY_N_BMODE(0x00A) struct b43_wldev; enum b43_nphy_spur_avoid { B43_SPUR_AVOID_DISABLE, B43_SPUR_AVOID_AUTO, B43_SPUR_AVOID_FORCE, }; struct b43_chanspec { u16 center_freq; enum nl80211_channel_type channel_type; }; struct b43_phy_n_iq_comp { s16 a0; s16 b0; s16 a1; s16 b1; }; struct b43_phy_n_rssical_cache { u16 rssical_radio_regs_2G[2]; u16 rssical_phy_regs_2G[12]; u16 rssical_radio_regs_5G[2]; u16 rssical_phy_regs_5G[12]; }; struct b43_phy_n_cal_cache { u16 txcal_radio_regs_2G[8]; u16 txcal_coeffs_2G[8]; struct b43_phy_n_iq_comp rxcal_coeffs_2G; u16 txcal_radio_regs_5G[8]; u16 txcal_coeffs_5G[8]; struct b43_phy_n_iq_comp rxcal_coeffs_5G; }; struct b43_phy_n_txpwrindex { s8 index; s8 index_internal; s8 index_internal_save; u16 AfectrlOverride; u16 AfeCtrlDacGain; u16 rad_gain; u8 bbmult; u16 iqcomp_a; u16 iqcomp_b; u16 locomp; }; struct b43_phy_n_pwr_ctl_info { u8 idle_tssi_2g; u8 idle_tssi_5g; }; struct b43_phy_n { u8 antsel_type; u8 cal_orig_pwr_idx[2]; u8 measure_hold; u8 phyrxchain; u8 hw_phyrxchain; u8 hw_phytxchain; u8 perical; u32 deaf_count; u32 rxcalparams; bool hang_avoid; bool mute; u16 papd_epsilon_offset[2]; s32 preamble_override; u32 bb_mult_save; bool init_por; bool gain_boost; bool elna_gain_config; bool band5g_pwrgain; u8 mphase_cal_phase_id; u16 mphase_txcal_cmdidx; u16 mphase_txcal_numcmds; u16 mphase_txcal_bestcoeffs[11]; bool txpwrctrl; bool pwg_gain_5ghz; u8 tx_pwr_idx[2]; s8 tx_power_offset[101]; u16 adj_pwr_tbl[84]; u16 txcal_bbmult; u16 txiqlocal_bestc[11]; bool txiqlocal_coeffsvalid; struct b43_phy_n_txpwrindex txpwrindex[2]; struct b43_phy_n_pwr_ctl_info pwr_ctl_info[2]; struct b43_chanspec txiqlocal_chanspec; u8 txrx_chain; u16 tx_rx_cal_phy_saveregs[11]; u16 tx_rx_cal_radio_saveregs[22]; u16 rfctrl_intc1_save; u16 rfctrl_intc2_save; u16 classifier_state; u16 clip_state[2]; enum b43_nphy_spur_avoid spur_avoid; bool aband_spurwar_en; bool gband_spurwar_en; bool ipa2g_on; struct b43_chanspec iqcal_chanspec_2G; struct b43_chanspec rssical_chanspec_2G; bool ipa5g_on; struct b43_chanspec iqcal_chanspec_5G; struct b43_chanspec rssical_chanspec_5G; struct b43_phy_n_rssical_cache rssical_cache; struct b43_phy_n_cal_cache cal_cache; bool crsminpwr_adjusted; bool noisevars_adjusted; }; struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_n; #endif /* B43_NPHY_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_n.c0000644000175000017500000050462612026211315022347 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n PHY support Copyright (c) 2008 Michael Buesch Copyright (c) 2010-2011 RafaÅ‚ MiÅ‚ecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "b43.h" #include "phy_n.h" #include "tables_nphy.h" #include "radio_2055.h" #include "radio_2056.h" #include "radio_2057.h" #include "main.h" struct nphy_txgains { u16 txgm[2]; u16 pga[2]; u16 pad[2]; u16 ipa[2]; }; struct nphy_iqcal_params { u16 txgm; u16 pga; u16 pad; u16 ipa; u16 cal_gain; u16 ncorr[5]; }; struct nphy_iq_est { s32 iq0_prod; u32 i0_pwr; u32 q0_pwr; s32 iq1_prod; u32 i1_pwr; u32 q1_pwr; }; enum b43_nphy_rf_sequence { B43_RFSEQ_RX2TX, B43_RFSEQ_TX2RX, B43_RFSEQ_RESET2RX, B43_RFSEQ_UPDATE_GAINH, B43_RFSEQ_UPDATE_GAINL, B43_RFSEQ_UPDATE_GAINU, }; enum b43_nphy_rssi_type { B43_NPHY_RSSI_X = 0, B43_NPHY_RSSI_Y, B43_NPHY_RSSI_Z, B43_NPHY_RSSI_PWRDET, B43_NPHY_RSSI_TSSI_I, B43_NPHY_RSSI_TSSI_Q, B43_NPHY_RSSI_TBD, }; static inline bool b43_nphy_ipa(struct b43_wldev *dev) { enum ieee80211_band band = b43_current_band(dev->wl); return ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) || (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCoreGetState */ static u8 b43_nphy_get_rx_core_state(struct b43_wldev *dev) { return (b43_phy_read(dev, B43_NPHY_RFSEQCA) & B43_NPHY_RFSEQCA_RXEN) >> B43_NPHY_RFSEQCA_RXEN_SHIFT; } /************************************************** * RF (just without b43_nphy_rf_control_intc_override) **************************************************/ /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ForceRFSeq */ static void b43_nphy_force_rf_sequence(struct b43_wldev *dev, enum b43_nphy_rf_sequence seq) { static const u16 trigger[] = { [B43_RFSEQ_RX2TX] = B43_NPHY_RFSEQTR_RX2TX, [B43_RFSEQ_TX2RX] = B43_NPHY_RFSEQTR_TX2RX, [B43_RFSEQ_RESET2RX] = B43_NPHY_RFSEQTR_RST2RX, [B43_RFSEQ_UPDATE_GAINH] = B43_NPHY_RFSEQTR_UPGH, [B43_RFSEQ_UPDATE_GAINL] = B43_NPHY_RFSEQTR_UPGL, [B43_RFSEQ_UPDATE_GAINU] = B43_NPHY_RFSEQTR_UPGU, }; int i; u16 seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); B43_WARN_ON(seq >= ARRAY_SIZE(trigger)); b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER); b43_phy_set(dev, B43_NPHY_RFSEQTR, trigger[seq]); for (i = 0; i < 200; i++) { if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & trigger[seq])) goto ok; msleep(1); } b43err(dev->wl, "RF sequence status timeout\n"); ok: b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverrideRev7 */ static void b43_nphy_rf_control_override_rev7(struct b43_wldev *dev, u16 field, u16 value, u8 core, bool off, u8 override) { const struct nphy_rf_control_override_rev7 *e; u16 en_addrs[3][2] = { { 0x0E7, 0x0EC }, { 0x342, 0x343 }, { 0x346, 0x347 } }; u16 en_addr; u16 en_mask = field; u16 val_addr; u8 i; /* Remember: we can get NULL! */ e = b43_nphy_get_rf_ctl_over_rev7(dev, field, override); for (i = 0; i < 2; i++) { if (override >= ARRAY_SIZE(en_addrs)) { b43err(dev->wl, "Invalid override value %d\n", override); return; } en_addr = en_addrs[override][i]; val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1; if (off) { b43_phy_mask(dev, en_addr, ~en_mask); if (e) /* Do it safer, better than wl */ b43_phy_mask(dev, val_addr, ~e->val_mask); } else { if (!core || (core & (1 << i))) { b43_phy_set(dev, en_addr, en_mask); if (e) b43_phy_maskset(dev, val_addr, ~e->val_mask, (value << e->val_shift)); } } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */ static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field, u16 value, u8 core, bool off) { int i; u8 index = fls(field); u8 addr, en_addr, val_addr; /* we expect only one bit set */ B43_WARN_ON(field & (~(1 << (index - 1)))); if (dev->phy.rev >= 3) { const struct nphy_rf_control_override_rev3 *rf_ctrl; for (i = 0; i < 2; i++) { if (index == 0 || index == 16) { b43err(dev->wl, "Unsupported RF Ctrl Override call\n"); return; } rf_ctrl = &tbl_rf_control_override_rev3[index - 1]; en_addr = B43_PHY_N((i == 0) ? rf_ctrl->en_addr0 : rf_ctrl->en_addr1); val_addr = B43_PHY_N((i == 0) ? rf_ctrl->val_addr0 : rf_ctrl->val_addr1); if (off) { b43_phy_mask(dev, en_addr, ~(field)); b43_phy_mask(dev, val_addr, ~(rf_ctrl->val_mask)); } else { if (core == 0 || ((1 << i) & core)) { b43_phy_set(dev, en_addr, field); b43_phy_maskset(dev, val_addr, ~(rf_ctrl->val_mask), (value << rf_ctrl->val_shift)); } } } } else { const struct nphy_rf_control_override_rev2 *rf_ctrl; if (off) { b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~(field)); value = 0; } else { b43_phy_set(dev, B43_NPHY_RFCTL_OVER, field); } for (i = 0; i < 2; i++) { if (index <= 1 || index == 16) { b43err(dev->wl, "Unsupported RF Ctrl Override call\n"); return; } if (index == 2 || index == 10 || (index >= 13 && index <= 15)) { core = 1; } rf_ctrl = &tbl_rf_control_override_rev2[index - 2]; addr = B43_PHY_N((i == 0) ? rf_ctrl->addr0 : rf_ctrl->addr1); if ((1 << i) & core) b43_phy_maskset(dev, addr, ~(rf_ctrl->bmask), (value << rf_ctrl->shift)); b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_START); udelay(1); b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, 0xFFFE); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */ static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field, u16 value, u8 core) { u8 i, j; u16 reg, tmp, val; B43_WARN_ON(dev->phy.rev < 3); B43_WARN_ON(field > 4); for (i = 0; i < 2; i++) { if ((core == 1 && i == 1) || (core == 2 && !i)) continue; reg = (i == 0) ? B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2; b43_phy_set(dev, reg, 0x400); switch (field) { case 0: b43_phy_write(dev, reg, 0); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); break; case 1: if (!i) { b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1, 0xFC3F, (value << 6)); b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1, 0xFFFE, 1); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_START); for (j = 0; j < 100; j++) { if (!(b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START)) { j = 0; break; } udelay(10); } if (j) b43err(dev->wl, "intc override timeout\n"); b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, 0xFFFE); } else { b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2, 0xFC3F, (value << 6)); b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xFFFE, 1); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_RXTX); for (j = 0; j < 100; j++) { if (!(b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX)) { j = 0; break; } udelay(10); } if (j) b43err(dev->wl, "intc override timeout\n"); b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, 0xFFFE); } break; case 2: if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { tmp = 0x0020; val = value << 5; } else { tmp = 0x0010; val = value << 4; } b43_phy_maskset(dev, reg, ~tmp, val); break; case 3: if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { tmp = 0x0001; val = value; } else { tmp = 0x0004; val = value << 2; } b43_phy_maskset(dev, reg, ~tmp, val); break; case 4: if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { tmp = 0x0002; val = value << 1; } else { tmp = 0x0008; val = value << 3; } b43_phy_maskset(dev, reg, ~tmp, val); break; } } } /************************************************** * Various PHY ops **************************************************/ /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ static void b43_nphy_write_clip_detection(struct b43_wldev *dev, const u16 *clip_st) { b43_phy_write(dev, B43_NPHY_C1_CLIP1THRES, clip_st[0]); b43_phy_write(dev, B43_NPHY_C2_CLIP1THRES, clip_st[1]); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ static void b43_nphy_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) { clip_st[0] = b43_phy_read(dev, B43_NPHY_C1_CLIP1THRES); clip_st[1] = b43_phy_read(dev, B43_NPHY_C2_CLIP1THRES); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/classifier */ static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val) { u16 tmp; if (dev->dev->core_rev == 16) b43_mac_suspend(dev); tmp = b43_phy_read(dev, B43_NPHY_CLASSCTL); tmp &= (B43_NPHY_CLASSCTL_CCKEN | B43_NPHY_CLASSCTL_OFDMEN | B43_NPHY_CLASSCTL_WAITEDEN); tmp &= ~mask; tmp |= (val & mask); b43_phy_maskset(dev, B43_NPHY_CLASSCTL, 0xFFF8, tmp); if (dev->dev->core_rev == 16) b43_mac_enable(dev); return tmp; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CCA */ static void b43_nphy_reset_cca(struct b43_wldev *dev) { u16 bbcfg; b43_phy_force_clock(dev, 1); bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG); b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg | B43_NPHY_BBCFG_RSTCCA); udelay(1); b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg & ~B43_NPHY_BBCFG_RSTCCA); b43_phy_force_clock(dev, 0); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/carriersearch */ static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable) { struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = phy->n; if (enable) { static const u16 clip[] = { 0xFFFF, 0xFFFF }; if (nphy->deaf_count++ == 0) { nphy->classifier_state = b43_nphy_classifier(dev, 0, 0); b43_nphy_classifier(dev, 0x7, 0); b43_nphy_read_clip_detection(dev, nphy->clip_state); b43_nphy_write_clip_detection(dev, clip); } b43_nphy_reset_cca(dev); } else { if (--nphy->deaf_count == 0) { b43_nphy_classifier(dev, 0x7, nphy->classifier_state); b43_nphy_write_clip_detection(dev, nphy->clip_state); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/AdjustLnaGainTbl */ static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u8 i; s16 tmp; u16 data[4]; s16 gain[2]; u16 minmax[2]; static const u16 lna_gain[4] = { -2, 10, 19, 25 }; if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); if (nphy->gain_boost) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { gain[0] = 6; gain[1] = 6; } else { tmp = 40370 - 315 * dev->phy.channel; gain[0] = ((tmp >> 13) + ((tmp >> 12) & 1)); tmp = 23242 - 224 * dev->phy.channel; gain[1] = ((tmp >> 13) + ((tmp >> 12) & 1)); } } else { gain[0] = 0; gain[1] = 0; } for (i = 0; i < 2; i++) { if (nphy->elna_gain_config) { data[0] = 19 + gain[i]; data[1] = 25 + gain[i]; data[2] = 25 + gain[i]; data[3] = 25 + gain[i]; } else { data[0] = lna_gain[0] + gain[i]; data[1] = lna_gain[1] + gain[i]; data[2] = lna_gain[2] + gain[i]; data[3] = lna_gain[3] + gain[i]; } b43_ntab_write_bulk(dev, B43_NTAB16(i, 8), 4, data); minmax[i] = 23 + gain[i]; } b43_phy_maskset(dev, B43_NPHY_C1_MINMAX_GAIN, ~B43_NPHY_C1_MINGAIN, minmax[0] << B43_NPHY_C1_MINGAIN_SHIFT); b43_phy_maskset(dev, B43_NPHY_C2_MINMAX_GAIN, ~B43_NPHY_C2_MINGAIN, minmax[1] << B43_NPHY_C2_MINGAIN_SHIFT); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRfSeq */ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, u8 *events, u8 *delays, u8 length) { struct b43_phy_n *nphy = dev->phy.n; u8 i; u8 end = (dev->phy.rev >= 3) ? 0x1F : 0x0F; u16 offset1 = cmd << 4; u16 offset2 = offset1 + 0x80; if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, true); b43_ntab_write_bulk(dev, B43_NTAB8(7, offset1), length, events); b43_ntab_write_bulk(dev, B43_NTAB8(7, offset2), length, delays); for (i = length; i < 16; i++) { b43_ntab_write(dev, B43_NTAB8(7, offset1 + i), end); b43_ntab_write(dev, B43_NTAB8(7, offset2 + i), 1); } if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, false); } /************************************************** * Radio 0x2057 **************************************************/ /* http://bcm-v4.sipsolutions.net/PHY/radio2057_rcal */ static u8 b43_radio_2057_rcal(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u16 tmp; if (phy->radio_rev == 5) { b43_phy_mask(dev, 0x342, ~0x2); udelay(10); b43_radio_set(dev, R2057_IQTEST_SEL_PU, 0x1); b43_radio_maskset(dev, 0x1ca, ~0x2, 0x1); } b43_radio_set(dev, R2057_RCAL_CONFIG, 0x1); udelay(10); b43_radio_set(dev, R2057_RCAL_CONFIG, 0x3); if (!b43_radio_wait_value(dev, R2057_RCCAL_N1_1, 1, 1, 100, 1000000)) { b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); return 0; } b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2); tmp = b43_radio_read(dev, R2057_RCAL_STATUS) & 0x3E; b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1); if (phy->radio_rev == 5) { b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1); b43_radio_mask(dev, 0x1ca, ~0x2); } if (phy->radio_rev <= 4 || phy->radio_rev == 6) { b43_radio_maskset(dev, R2057_TEMPSENSE_CONFIG, ~0x3C, tmp); b43_radio_maskset(dev, R2057_BANDGAP_RCAL_TRIM, ~0xF0, tmp << 2); } return tmp & 0x3e; } /* http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal */ static u16 b43_radio_2057_rccal(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; bool special = (phy->radio_rev == 3 || phy->radio_rev == 4 || phy->radio_rev == 6); u16 tmp; if (special) { b43_radio_write(dev, R2057_RCCAL_MASTER, 0x61); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0); } else { b43_radio_write(dev, 0x1AE, 0x61); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1); } b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500, 5000000)) b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); if (special) { b43_radio_write(dev, R2057_RCCAL_MASTER, 0x69); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); } else { b43_radio_write(dev, 0x1AE, 0x69); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xD5); } b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500, 5000000)) b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); if (special) { b43_radio_write(dev, R2057_RCCAL_MASTER, 0x73); b43_radio_write(dev, R2057_RCCAL_X1, 0x28); b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); } else { b43_radio_write(dev, 0x1AE, 0x73); b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); b43_radio_write(dev, R2057_RCCAL_TRC0, 0x99); } b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500, 5000000)) { b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); return 0; } tmp = b43_radio_read(dev, R2057_RCCAL_DONE_OSCCAP); b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); return tmp; } static void b43_radio_2057_init_pre(struct b43_wldev *dev) { b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_CHIP0PU); /* Maybe wl meant to reset and set (order?) RFCTL_CMD_OEPORFORCE? */ b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_OEPORFORCE); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_OEPORFORCE); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_CHIP0PU); } static void b43_radio_2057_init_post(struct b43_wldev *dev) { b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x1); b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x78); b43_radio_set(dev, R2057_XTAL_CONFIG2, 0x80); mdelay(2); b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x78); b43_radio_mask(dev, R2057_XTAL_CONFIG2, ~0x80); if (dev->phy.n->init_por) { b43_radio_2057_rcal(dev); b43_radio_2057_rccal(dev); } b43_radio_mask(dev, R2057_RFPLL_MASTER, ~0x8); dev->phy.n->init_por = false; } /* http://bcm-v4.sipsolutions.net/802.11/Radio/2057/Init */ static void b43_radio_2057_init(struct b43_wldev *dev) { b43_radio_2057_init_pre(dev); r2057_upload_inittabs(dev); b43_radio_2057_init_post(dev); } /************************************************** * Radio 0x2056 **************************************************/ static void b43_chantab_radio_2056_upload(struct b43_wldev *dev, const struct b43_nphy_channeltab_entry_rev3 *e) { b43_radio_write(dev, B2056_SYN_PLL_VCOCAL1, e->radio_syn_pll_vcocal1); b43_radio_write(dev, B2056_SYN_PLL_VCOCAL2, e->radio_syn_pll_vcocal2); b43_radio_write(dev, B2056_SYN_PLL_REFDIV, e->radio_syn_pll_refdiv); b43_radio_write(dev, B2056_SYN_PLL_MMD2, e->radio_syn_pll_mmd2); b43_radio_write(dev, B2056_SYN_PLL_MMD1, e->radio_syn_pll_mmd1); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, e->radio_syn_pll_loopfilter1); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, e->radio_syn_pll_loopfilter2); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER3, e->radio_syn_pll_loopfilter3); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, e->radio_syn_pll_loopfilter4); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER5, e->radio_syn_pll_loopfilter5); b43_radio_write(dev, B2056_SYN_RESERVED_ADDR27, e->radio_syn_reserved_addr27); b43_radio_write(dev, B2056_SYN_RESERVED_ADDR28, e->radio_syn_reserved_addr28); b43_radio_write(dev, B2056_SYN_RESERVED_ADDR29, e->radio_syn_reserved_addr29); b43_radio_write(dev, B2056_SYN_LOGEN_VCOBUF1, e->radio_syn_logen_vcobuf1); b43_radio_write(dev, B2056_SYN_LOGEN_MIXER2, e->radio_syn_logen_mixer2); b43_radio_write(dev, B2056_SYN_LOGEN_BUF3, e->radio_syn_logen_buf3); b43_radio_write(dev, B2056_SYN_LOGEN_BUF4, e->radio_syn_logen_buf4); b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAA_TUNE, e->radio_rx0_lnaa_tune); b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAG_TUNE, e->radio_rx0_lnag_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_INTPAA_BOOST_TUNE, e->radio_tx0_intpaa_boost_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_INTPAG_BOOST_TUNE, e->radio_tx0_intpag_boost_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_PADA_BOOST_TUNE, e->radio_tx0_pada_boost_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_PADG_BOOST_TUNE, e->radio_tx0_padg_boost_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_PGAA_BOOST_TUNE, e->radio_tx0_pgaa_boost_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_PGAG_BOOST_TUNE, e->radio_tx0_pgag_boost_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_MIXA_BOOST_TUNE, e->radio_tx0_mixa_boost_tune); b43_radio_write(dev, B2056_TX0 | B2056_TX_MIXG_BOOST_TUNE, e->radio_tx0_mixg_boost_tune); b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAA_TUNE, e->radio_rx1_lnaa_tune); b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAG_TUNE, e->radio_rx1_lnag_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_INTPAA_BOOST_TUNE, e->radio_tx1_intpaa_boost_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_INTPAG_BOOST_TUNE, e->radio_tx1_intpag_boost_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_PADA_BOOST_TUNE, e->radio_tx1_pada_boost_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_PADG_BOOST_TUNE, e->radio_tx1_padg_boost_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_PGAA_BOOST_TUNE, e->radio_tx1_pgaa_boost_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_PGAG_BOOST_TUNE, e->radio_tx1_pgag_boost_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_MIXA_BOOST_TUNE, e->radio_tx1_mixa_boost_tune); b43_radio_write(dev, B2056_TX1 | B2056_TX_MIXG_BOOST_TUNE, e->radio_tx1_mixg_boost_tune); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2056Setup */ static void b43_radio_2056_setup(struct b43_wldev *dev, const struct b43_nphy_channeltab_entry_rev3 *e) { struct ssb_sprom *sprom = dev->dev->bus_sprom; enum ieee80211_band band = b43_current_band(dev->wl); u16 offset; u8 i; u16 bias, cbias; u16 pag_boost, padg_boost, pgag_boost, mixg_boost; u16 paa_boost, pada_boost, pgaa_boost, mixa_boost; B43_WARN_ON(dev->phy.rev < 3); b43_chantab_radio_2056_upload(dev, e); b2056_upload_syn_pll_cp2(dev, band == IEEE80211_BAND_5GHZ); if (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR && b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F); if (dev->dev->chip_id == 0x4716) { b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x14); b43_radio_write(dev, B2056_SYN_PLL_CP2, 0); } else { b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0B); b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x14); } } if (sprom->boardflags2_lo & B43_BFL2_APLL_WAR && b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x05); b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x0C); } if (dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) { for (i = 0; i < 2; i++) { offset = i ? B2056_TX1 : B2056_TX0; if (dev->phy.rev >= 5) { b43_radio_write(dev, offset | B2056_TX_PADG_IDAC, 0xcc); if (dev->dev->chip_id == 0x4716) { bias = 0x40; cbias = 0x45; pag_boost = 0x5; pgag_boost = 0x33; mixg_boost = 0x55; } else { bias = 0x25; cbias = 0x20; pag_boost = 0x4; pgag_boost = 0x03; mixg_boost = 0x65; } padg_boost = 0x77; b43_radio_write(dev, offset | B2056_TX_INTPAG_IMAIN_STAT, bias); b43_radio_write(dev, offset | B2056_TX_INTPAG_IAUX_STAT, bias); b43_radio_write(dev, offset | B2056_TX_INTPAG_CASCBIAS, cbias); b43_radio_write(dev, offset | B2056_TX_INTPAG_BOOST_TUNE, pag_boost); b43_radio_write(dev, offset | B2056_TX_PGAG_BOOST_TUNE, pgag_boost); b43_radio_write(dev, offset | B2056_TX_PADG_BOOST_TUNE, padg_boost); b43_radio_write(dev, offset | B2056_TX_MIXG_BOOST_TUNE, mixg_boost); } else { bias = dev->phy.is_40mhz ? 0x40 : 0x20; b43_radio_write(dev, offset | B2056_TX_INTPAG_IMAIN_STAT, bias); b43_radio_write(dev, offset | B2056_TX_INTPAG_IAUX_STAT, bias); b43_radio_write(dev, offset | B2056_TX_INTPAG_CASCBIAS, 0x30); } b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee); } } else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) { u16 freq = dev->phy.channel_freq; if (freq < 5100) { paa_boost = 0xA; pada_boost = 0x77; pgaa_boost = 0xF; mixa_boost = 0xF; } else if (freq < 5340) { paa_boost = 0x8; pada_boost = 0x77; pgaa_boost = 0xFB; mixa_boost = 0xF; } else if (freq < 5650) { paa_boost = 0x0; pada_boost = 0x77; pgaa_boost = 0xB; mixa_boost = 0xF; } else { paa_boost = 0x0; pada_boost = 0x77; if (freq != 5825) pgaa_boost = -(freq - 18) / 36 + 168; else pgaa_boost = 6; mixa_boost = 0xF; } for (i = 0; i < 2; i++) { offset = i ? B2056_TX1 : B2056_TX0; b43_radio_write(dev, offset | B2056_TX_INTPAA_BOOST_TUNE, paa_boost); b43_radio_write(dev, offset | B2056_TX_PADA_BOOST_TUNE, pada_boost); b43_radio_write(dev, offset | B2056_TX_PGAA_BOOST_TUNE, pgaa_boost); b43_radio_write(dev, offset | B2056_TX_MIXA_BOOST_TUNE, mixa_boost); b43_radio_write(dev, offset | B2056_TX_TXSPARE1, 0x30); b43_radio_write(dev, offset | B2056_TX_PA_SPARE2, 0xee); b43_radio_write(dev, offset | B2056_TX_PADA_CASCBIAS, 0x03); b43_radio_write(dev, offset | B2056_TX_INTPAA_IAUX_STAT, 0x50); b43_radio_write(dev, offset | B2056_TX_INTPAA_IMAIN_STAT, 0x50); b43_radio_write(dev, offset | B2056_TX_INTPAA_CASCBIAS, 0x30); } } udelay(50); /* VCO calibration */ b43_radio_write(dev, B2056_SYN_PLL_VCOCAL12, 0x00); b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x38); b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x18); b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x38); b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x39); udelay(300); } static u8 b43_radio_2056_rcal(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u16 mast2, tmp; if (phy->rev != 3) return 0; mast2 = b43_radio_read(dev, B2056_SYN_PLL_MAST2); b43_radio_write(dev, B2056_SYN_PLL_MAST2, mast2 | 0x7); udelay(10); b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x01); udelay(10); b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x09); if (!b43_radio_wait_value(dev, B2056_SYN_RCAL_CODE_OUT, 0x80, 0x80, 100, 1000000)) { b43err(dev->wl, "Radio recalibration timeout\n"); return 0; } b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x01); tmp = b43_radio_read(dev, B2056_SYN_RCAL_CODE_OUT); b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x00); b43_radio_write(dev, B2056_SYN_PLL_MAST2, mast2); return tmp & 0x1f; } static void b43_radio_init2056_pre(struct b43_wldev *dev) { b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_CHIP0PU); /* Maybe wl meant to reset and set (order?) RFCTL_CMD_OEPORFORCE? */ b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_OEPORFORCE); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_OEPORFORCE); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_CHIP0PU); } static void b43_radio_init2056_post(struct b43_wldev *dev) { b43_radio_set(dev, B2056_SYN_COM_CTRL, 0xB); b43_radio_set(dev, B2056_SYN_COM_PU, 0x2); b43_radio_set(dev, B2056_SYN_COM_RESET, 0x2); msleep(1); b43_radio_mask(dev, B2056_SYN_COM_RESET, ~0x2); b43_radio_mask(dev, B2056_SYN_PLL_MAST2, ~0xFC); b43_radio_mask(dev, B2056_SYN_RCCAL_CTRL0, ~0x1); if (dev->phy.n->init_por) b43_radio_2056_rcal(dev); } /* * Initialize a Broadcom 2056 N-radio * http://bcm-v4.sipsolutions.net/802.11/Radio/2056/Init */ static void b43_radio_init2056(struct b43_wldev *dev) { b43_radio_init2056_pre(dev); b2056_upload_inittabs(dev, 0, 0); b43_radio_init2056_post(dev); dev->phy.n->init_por = false; } /************************************************** * Radio 0x2055 **************************************************/ static void b43_chantab_radio_upload(struct b43_wldev *dev, const struct b43_nphy_channeltab_entry_rev2 *e) { b43_radio_write(dev, B2055_PLL_REF, e->radio_pll_ref); b43_radio_write(dev, B2055_RF_PLLMOD0, e->radio_rf_pllmod0); b43_radio_write(dev, B2055_RF_PLLMOD1, e->radio_rf_pllmod1); b43_radio_write(dev, B2055_VCO_CAPTAIL, e->radio_vco_captail); b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ b43_radio_write(dev, B2055_VCO_CAL1, e->radio_vco_cal1); b43_radio_write(dev, B2055_VCO_CAL2, e->radio_vco_cal2); b43_radio_write(dev, B2055_PLL_LFC1, e->radio_pll_lfc1); b43_radio_write(dev, B2055_PLL_LFR1, e->radio_pll_lfr1); b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ b43_radio_write(dev, B2055_PLL_LFC2, e->radio_pll_lfc2); b43_radio_write(dev, B2055_LGBUF_CENBUF, e->radio_lgbuf_cenbuf); b43_radio_write(dev, B2055_LGEN_TUNE1, e->radio_lgen_tune1); b43_radio_write(dev, B2055_LGEN_TUNE2, e->radio_lgen_tune2); b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ b43_radio_write(dev, B2055_C1_LGBUF_ATUNE, e->radio_c1_lgbuf_atune); b43_radio_write(dev, B2055_C1_LGBUF_GTUNE, e->radio_c1_lgbuf_gtune); b43_radio_write(dev, B2055_C1_RX_RFR1, e->radio_c1_rx_rfr1); b43_radio_write(dev, B2055_C1_TX_PGAPADTN, e->radio_c1_tx_pgapadtn); b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ b43_radio_write(dev, B2055_C1_TX_MXBGTRIM, e->radio_c1_tx_mxbgtrim); b43_radio_write(dev, B2055_C2_LGBUF_ATUNE, e->radio_c2_lgbuf_atune); b43_radio_write(dev, B2055_C2_LGBUF_GTUNE, e->radio_c2_lgbuf_gtune); b43_radio_write(dev, B2055_C2_RX_RFR1, e->radio_c2_rx_rfr1); b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ b43_radio_write(dev, B2055_C2_TX_PGAPADTN, e->radio_c2_tx_pgapadtn); b43_radio_write(dev, B2055_C2_TX_MXBGTRIM, e->radio_c2_tx_mxbgtrim); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2055Setup */ static void b43_radio_2055_setup(struct b43_wldev *dev, const struct b43_nphy_channeltab_entry_rev2 *e) { B43_WARN_ON(dev->phy.rev >= 3); b43_chantab_radio_upload(dev, e); udelay(50); b43_radio_write(dev, B2055_VCO_CAL10, 0x05); b43_radio_write(dev, B2055_VCO_CAL10, 0x45); b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ b43_radio_write(dev, B2055_VCO_CAL10, 0x65); udelay(300); } static void b43_radio_init2055_pre(struct b43_wldev *dev) { b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_PORFORCE); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_CHIP0PU | B43_NPHY_RFCTL_CMD_OEPORFORCE); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_PORFORCE); } static void b43_radio_init2055_post(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; struct ssb_sprom *sprom = dev->dev->bus_sprom; bool workaround = false; if (sprom->revision < 4) workaround = (dev->dev->board_vendor != PCI_VENDOR_ID_BROADCOM && dev->dev->board_type == 0x46D && dev->dev->board_rev >= 0x41); else workaround = !(sprom->boardflags2_lo & B43_BFL2_RXBB_INT_REG_DIS); b43_radio_mask(dev, B2055_MASTER1, 0xFFF3); if (workaround) { b43_radio_mask(dev, B2055_C1_RX_BB_REG, 0x7F); b43_radio_mask(dev, B2055_C2_RX_BB_REG, 0x7F); } b43_radio_maskset(dev, B2055_RRCCAL_NOPTSEL, 0xFFC0, 0x2C); b43_radio_write(dev, B2055_CAL_MISC, 0x3C); b43_radio_mask(dev, B2055_CAL_MISC, 0xFFBE); b43_radio_set(dev, B2055_CAL_LPOCTL, 0x80); b43_radio_set(dev, B2055_CAL_MISC, 0x1); msleep(1); b43_radio_set(dev, B2055_CAL_MISC, 0x40); if (!b43_radio_wait_value(dev, B2055_CAL_COUT2, 0x80, 0x80, 10, 2000)) b43err(dev->wl, "radio post init timeout\n"); b43_radio_mask(dev, B2055_CAL_LPOCTL, 0xFF7F); b43_switch_channel(dev, dev->phy.channel); b43_radio_write(dev, B2055_C1_RX_BB_LPF, 0x9); b43_radio_write(dev, B2055_C2_RX_BB_LPF, 0x9); b43_radio_write(dev, B2055_C1_RX_BB_MIDACHP, 0x83); b43_radio_write(dev, B2055_C2_RX_BB_MIDACHP, 0x83); b43_radio_maskset(dev, B2055_C1_LNA_GAINBST, 0xFFF8, 0x6); b43_radio_maskset(dev, B2055_C2_LNA_GAINBST, 0xFFF8, 0x6); if (!nphy->gain_boost) { b43_radio_set(dev, B2055_C1_RX_RFSPC1, 0x2); b43_radio_set(dev, B2055_C2_RX_RFSPC1, 0x2); } else { b43_radio_mask(dev, B2055_C1_RX_RFSPC1, 0xFFFD); b43_radio_mask(dev, B2055_C2_RX_RFSPC1, 0xFFFD); } udelay(2); } /* * Initialize a Broadcom 2055 N-radio * http://bcm-v4.sipsolutions.net/802.11/Radio/2055/Init */ static void b43_radio_init2055(struct b43_wldev *dev) { b43_radio_init2055_pre(dev); if (b43_status(dev) < B43_STAT_INITIALIZED) { /* Follow wl, not specs. Do not force uploading all regs */ b2055_upload_inittab(dev, 0, 0); } else { bool ghz5 = b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ; b2055_upload_inittab(dev, ghz5, 0); } b43_radio_init2055_post(dev); } /************************************************** * Samples **************************************************/ /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */ static int b43_nphy_load_samples(struct b43_wldev *dev, struct b43_c32 *samples, u16 len) { struct b43_phy_n *nphy = dev->phy.n; u16 i; u32 *data; data = kzalloc(len * sizeof(u32), GFP_KERNEL); if (!data) { b43err(dev->wl, "allocation for samples loading failed\n"); return -ENOMEM; } if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); for (i = 0; i < len; i++) { data[i] = (samples[i].i & 0x3FF << 10); data[i] |= samples[i].q & 0x3FF; } b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data); kfree(data); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); return 0; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max, bool test) { int i; u16 bw, len, rot, angle; struct b43_c32 *samples; bw = (dev->phy.is_40mhz) ? 40 : 20; len = bw << 3; if (test) { if (b43_phy_read(dev, B43_NPHY_BBCFG) & B43_NPHY_BBCFG_RSTRX) bw = 82; else bw = 80; if (dev->phy.is_40mhz) bw <<= 1; len = bw << 1; } samples = kcalloc(len, sizeof(struct b43_c32), GFP_KERNEL); if (!samples) { b43err(dev->wl, "allocation for samples generation failed\n"); return 0; } rot = (((freq * 36) / bw) << 16) / 100; angle = 0; for (i = 0; i < len; i++) { samples[i] = b43_cordic(angle); angle += rot; samples[i].q = CORDIC_CONVERT(samples[i].q * max); samples[i].i = CORDIC_CONVERT(samples[i].i * max); } i = b43_nphy_load_samples(dev, samples, len); kfree(samples); return (i < 0) ? 0 : len; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, u16 wait, bool iqmode, bool dac_test) { struct b43_phy_n *nphy = dev->phy.n; int i; u16 seq_mode; u32 tmp; if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, true); if ((nphy->bb_mult_save & 0x80000000) == 0) { tmp = b43_ntab_read(dev, B43_NTAB16(15, 87)); nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000; } if (!dev->phy.is_40mhz) tmp = 0x6464; else tmp = 0x4747; b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, false); b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1)); if (loops != 0xFFFF) b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, (loops - 1)); else b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, loops); b43_phy_write(dev, B43_NPHY_SAMP_WAITCNT, wait); seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER); if (iqmode) { b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000); } else { if (dac_test) b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5); else b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1); } for (i = 0; i < 100; i++) { if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & 1)) { i = 0; break; } udelay(10); } if (i) b43err(dev->wl, "run samples timeout\n"); b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); } /************************************************** * RSSI **************************************************/ /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ScaleOffsetRssi */ static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale, s8 offset, u8 core, u8 rail, enum b43_nphy_rssi_type type) { u16 tmp; bool core1or5 = (core == 1) || (core == 5); bool core2or5 = (core == 2) || (core == 5); offset = clamp_val(offset, -32, 31); tmp = ((scale & 0x3F) << 8) | (offset & 0x3F); if (core1or5 && (rail == 0) && (type == B43_NPHY_RSSI_Z)) b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, tmp); if (core1or5 && (rail == 1) && (type == B43_NPHY_RSSI_Z)) b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, tmp); if (core2or5 && (rail == 0) && (type == B43_NPHY_RSSI_Z)) b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, tmp); if (core2or5 && (rail == 1) && (type == B43_NPHY_RSSI_Z)) b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, tmp); if (core1or5 && (rail == 0) && (type == B43_NPHY_RSSI_X)) b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, tmp); if (core1or5 && (rail == 1) && (type == B43_NPHY_RSSI_X)) b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, tmp); if (core2or5 && (rail == 0) && (type == B43_NPHY_RSSI_X)) b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, tmp); if (core2or5 && (rail == 1) && (type == B43_NPHY_RSSI_X)) b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, tmp); if (core1or5 && (rail == 0) && (type == B43_NPHY_RSSI_Y)) b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, tmp); if (core1or5 && (rail == 1) && (type == B43_NPHY_RSSI_Y)) b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, tmp); if (core2or5 && (rail == 0) && (type == B43_NPHY_RSSI_Y)) b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, tmp); if (core2or5 && (rail == 1) && (type == B43_NPHY_RSSI_Y)) b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, tmp); if (core1or5 && (rail == 0) && (type == B43_NPHY_RSSI_TBD)) b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TBD, tmp); if (core1or5 && (rail == 1) && (type == B43_NPHY_RSSI_TBD)) b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TBD, tmp); if (core2or5 && (rail == 0) && (type == B43_NPHY_RSSI_TBD)) b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TBD, tmp); if (core2or5 && (rail == 1) && (type == B43_NPHY_RSSI_TBD)) b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TBD, tmp); if (core1or5 && (rail == 0) && (type == B43_NPHY_RSSI_PWRDET)) b43_phy_write(dev, B43_NPHY_RSSIMC_0I_PWRDET, tmp); if (core1or5 && (rail == 1) && (type == B43_NPHY_RSSI_PWRDET)) b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_PWRDET, tmp); if (core2or5 && (rail == 0) && (type == B43_NPHY_RSSI_PWRDET)) b43_phy_write(dev, B43_NPHY_RSSIMC_1I_PWRDET, tmp); if (core2or5 && (rail == 1) && (type == B43_NPHY_RSSI_PWRDET)) b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_PWRDET, tmp); if (core1or5 && (type == B43_NPHY_RSSI_TSSI_I)) b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TSSI, tmp); if (core2or5 && (type == B43_NPHY_RSSI_TSSI_I)) b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TSSI, tmp); if (core1or5 && (type == B43_NPHY_RSSI_TSSI_Q)) b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TSSI, tmp); if (core2or5 && (type == B43_NPHY_RSSI_TSSI_Q)) b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TSSI, tmp); } static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, u8 type) { u8 i; u16 reg, val; if (code == 0) { b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, 0xFDFF); b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, 0xFDFF); b43_phy_mask(dev, B43_NPHY_AFECTL_C1, 0xFCFF); b43_phy_mask(dev, B43_NPHY_AFECTL_C2, 0xFCFF); b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S0, 0xFFDF); b43_phy_mask(dev, B43_NPHY_TXF_40CO_B32S1, 0xFFDF); b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0xFFC3); b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0xFFC3); } else { for (i = 0; i < 2; i++) { if ((code == 1 && i == 1) || (code == 2 && !i)) continue; reg = (i == 0) ? B43_NPHY_AFECTL_OVER1 : B43_NPHY_AFECTL_OVER; b43_phy_maskset(dev, reg, 0xFDFF, 0x0200); if (type < 3) { reg = (i == 0) ? B43_NPHY_AFECTL_C1 : B43_NPHY_AFECTL_C2; b43_phy_maskset(dev, reg, 0xFCFF, 0); reg = (i == 0) ? B43_NPHY_RFCTL_LUT_TRSW_UP1 : B43_NPHY_RFCTL_LUT_TRSW_UP2; b43_phy_maskset(dev, reg, 0xFFC3, 0); if (type == 0) val = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 4 : 8; else if (type == 1) val = 16; else val = 32; b43_phy_set(dev, reg, val); reg = (i == 0) ? B43_NPHY_TXF_40CO_B1S0 : B43_NPHY_TXF_40CO_B32S1; b43_phy_set(dev, reg, 0x0020); } else { if (type == 6) val = 0x0100; else if (type == 3) val = 0x0200; else val = 0x0300; reg = (i == 0) ? B43_NPHY_AFECTL_C1 : B43_NPHY_AFECTL_C2; b43_phy_maskset(dev, reg, 0xFCFF, val); b43_phy_maskset(dev, reg, 0xF3FF, val << 2); if (type != 3 && type != 6) { enum ieee80211_band band = b43_current_band(dev->wl); if (b43_nphy_ipa(dev)) val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; else val = 0x11; reg = (i == 0) ? 0x2000 : 0x3000; reg |= B2055_PADDRV; b43_radio_write16(dev, reg, val); reg = (i == 0) ? B43_NPHY_AFECTL_OVER1 : B43_NPHY_AFECTL_OVER; b43_phy_set(dev, reg, 0x0200); } } } } } static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, u8 type) { u16 val; if (type < 3) val = 0; else if (type == 6) val = 1; else if (type == 3) val = 2; else val = 3; val = (val << 12) | (val << 14); b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val); b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val); if (type < 3) { b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF, (type + 1) << 4); b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF, (type + 1) << 4); } if (code == 0) { b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x3000); if (type < 3) { b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~(B43_NPHY_RFCTL_CMD_RXEN | B43_NPHY_RFCTL_CMD_CORESEL)); b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~(0x1 << 12 | 0x1 << 5 | 0x1 << 1 | 0x1)); b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_START); udelay(20); b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); } } else { b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x3000); if (type < 3) { b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, ~(B43_NPHY_RFCTL_CMD_RXEN | B43_NPHY_RFCTL_CMD_CORESEL), (B43_NPHY_RFCTL_CMD_RXEN | code << B43_NPHY_RFCTL_CMD_CORESEL_SHIFT)); b43_phy_set(dev, B43_NPHY_RFCTL_OVER, (0x1 << 12 | 0x1 << 5 | 0x1 << 1 | 0x1)); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_START); udelay(20); b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */ static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, u8 type) { if (dev->phy.rev >= 3) b43_nphy_rev3_rssi_select(dev, code, type); else b43_nphy_rev2_rssi_select(dev, code, type); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRssi2055Vcm */ static void b43_nphy_set_rssi_2055_vcm(struct b43_wldev *dev, u8 type, u8 *buf) { int i; for (i = 0; i < 2; i++) { if (type == 2) { if (i == 0) { b43_radio_maskset(dev, B2055_C1_B0NB_RSSIVCM, 0xFC, buf[0]); b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, 0xFC, buf[1]); } else { b43_radio_maskset(dev, B2055_C2_B0NB_RSSIVCM, 0xFC, buf[2 * i]); b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, 0xFC, buf[2 * i + 1]); } } else { if (i == 0) b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, 0xF3, buf[0] << 2); else b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, 0xF3, buf[2 * i + 1] << 2); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PollRssi */ static int b43_nphy_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf, u8 nsamp) { int i; int out; u16 save_regs_phy[9]; u16 s[2]; if (dev->phy.rev >= 3) { save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1); save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2); save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0); save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1); save_regs_phy[8] = 0; } else { save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_RFCTL_CMD); save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_RFCTL_OVER); save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO1); save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO2); save_regs_phy[7] = 0; save_regs_phy[8] = 0; } b43_nphy_rssi_select(dev, 5, type); if (dev->phy.rev < 2) { save_regs_phy[8] = b43_phy_read(dev, B43_NPHY_GPIO_SEL); b43_phy_write(dev, B43_NPHY_GPIO_SEL, 5); } for (i = 0; i < 4; i++) buf[i] = 0; for (i = 0; i < nsamp; i++) { if (dev->phy.rev < 2) { s[0] = b43_phy_read(dev, B43_NPHY_GPIO_LOOUT); s[1] = b43_phy_read(dev, B43_NPHY_GPIO_HIOUT); } else { s[0] = b43_phy_read(dev, B43_NPHY_RSSI1); s[1] = b43_phy_read(dev, B43_NPHY_RSSI2); } buf[0] += ((s8)((s[0] & 0x3F) << 2)) >> 2; buf[1] += ((s8)(((s[0] >> 8) & 0x3F) << 2)) >> 2; buf[2] += ((s8)((s[1] & 0x3F) << 2)) >> 2; buf[3] += ((s8)(((s[1] >> 8) & 0x3F) << 2)) >> 2; } out = (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF); if (dev->phy.rev < 2) b43_phy_write(dev, B43_NPHY_GPIO_SEL, save_regs_phy[8]); if (dev->phy.rev >= 3) { b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[0]); b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[1]); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, save_regs_phy[2]); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, save_regs_phy[3]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, save_regs_phy[4]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]); b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]); b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]); } else { b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[0]); b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[1]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[2]); b43_phy_write(dev, B43_NPHY_RFCTL_CMD, save_regs_phy[3]); b43_phy_write(dev, B43_NPHY_RFCTL_OVER, save_regs_phy[4]); b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO1, save_regs_phy[5]); b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO2, save_regs_phy[6]); } return out; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u16 saved_regs_phy_rfctl[2]; u16 saved_regs_phy[13]; u16 regs_to_store[] = { B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 }; u16 class; u16 clip_state[2]; u16 clip_off[2] = { 0xFFFF, 0xFFFF }; u8 vcm_final = 0; s8 offset[4]; s32 results[8][4] = { }; s32 results_min[4] = { }; s32 poll_results[4] = { }; u16 *rssical_radio_regs = NULL; u16 *rssical_phy_regs = NULL; u16 r; /* routing */ u8 rx_core_state; u8 core, i, j; class = b43_nphy_classifier(dev, 0, 0); b43_nphy_classifier(dev, 7, 4); b43_nphy_read_clip_detection(dev, clip_state); b43_nphy_write_clip_detection(dev, clip_off); saved_regs_phy_rfctl[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); saved_regs_phy_rfctl[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); for (i = 0; i < ARRAY_SIZE(regs_to_store); i++) saved_regs_phy[i] = b43_phy_read(dev, regs_to_store[i]); b43_nphy_rf_control_intc_override(dev, 0, 0, 7); b43_nphy_rf_control_intc_override(dev, 1, 1, 7); b43_nphy_rf_control_override(dev, 0x1, 0, 0, false); b43_nphy_rf_control_override(dev, 0x2, 1, 0, false); b43_nphy_rf_control_override(dev, 0x80, 1, 0, false); b43_nphy_rf_control_override(dev, 0x40, 1, 0, false); if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { b43_nphy_rf_control_override(dev, 0x20, 0, 0, false); b43_nphy_rf_control_override(dev, 0x10, 1, 0, false); } else { b43_nphy_rf_control_override(dev, 0x10, 0, 0, false); b43_nphy_rf_control_override(dev, 0x20, 1, 0, false); } rx_core_state = b43_nphy_get_rx_core_state(dev); for (core = 0; core < 2; core++) { if (!(rx_core_state & (1 << core))) continue; r = core ? B2056_RX1 : B2056_RX0; b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, 0, 2); b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, 1, 2); for (i = 0; i < 8; i++) { b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3, i << 2); b43_nphy_poll_rssi(dev, 2, results[i], 8); } for (i = 0; i < 4; i += 2) { s32 curr; s32 mind = 40; s32 minpoll = 249; u8 minvcm = 0; if (2 * core != i) continue; for (j = 0; j < 8; j++) { curr = results[j][i] * results[j][i] + results[j][i + 1] * results[j][i]; if (curr < mind) { mind = curr; minvcm = j; } if (results[j][i] < minpoll) minpoll = results[j][i]; } vcm_final = minvcm; results_min[i] = minpoll; } b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3, vcm_final << 2); for (i = 0; i < 4; i++) { if (core != i / 2) continue; offset[i] = -results[vcm_final][i]; if (offset[i] < 0) offset[i] = -((abs(offset[i]) + 4) / 8); else offset[i] = (offset[i] + 4) / 8; if (results_min[i] == 248) offset[i] = -32; b43_nphy_scale_offset_rssi(dev, 0, offset[i], (i / 2 == 0) ? 1 : 2, (i % 2 == 0) ? 0 : 1, 2); } } for (core = 0; core < 2; core++) { if (!(rx_core_state & (1 << core))) continue; for (i = 0; i < 2; i++) { b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, 0, i); b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, 1, i); b43_nphy_poll_rssi(dev, i, poll_results, 8); for (j = 0; j < 4; j++) { if (j / 2 == core) { offset[j] = 232 - poll_results[j]; if (offset[j] < 0) offset[j] = -(abs(offset[j] + 4) / 8); else offset[j] = (offset[j] + 4) / 8; b43_nphy_scale_offset_rssi(dev, 0, offset[2 * core], core + 1, j % 2, i); } } } } b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, saved_regs_phy_rfctl[0]); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, saved_regs_phy_rfctl[1]); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); b43_phy_set(dev, B43_NPHY_TXF_40CO_B1S1, 0x1); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_START); b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, ~0x1); b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_RXTX); b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, ~0x1); for (i = 0; i < ARRAY_SIZE(regs_to_store); i++) b43_phy_write(dev, regs_to_store[i], saved_regs_phy[i]); /* Store for future configuration */ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; } else { rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; } rssical_radio_regs[0] = b43_radio_read(dev, 0x602B); rssical_radio_regs[0] = b43_radio_read(dev, 0x702B); rssical_phy_regs[0] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_Z); rssical_phy_regs[1] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z); rssical_phy_regs[2] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_Z); rssical_phy_regs[3] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z); rssical_phy_regs[4] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_X); rssical_phy_regs[5] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_X); rssical_phy_regs[6] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_X); rssical_phy_regs[7] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_X); rssical_phy_regs[8] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_Y); rssical_phy_regs[9] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y); rssical_phy_regs[10] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_Y); rssical_phy_regs[11] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y); /* Remember for which channel we store configuration */ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) nphy->rssical_chanspec_2G.center_freq = dev->phy.channel_freq; else nphy->rssical_chanspec_5G.center_freq = dev->phy.channel_freq; /* End of calibration, restore configuration */ b43_nphy_classifier(dev, 7, class); b43_nphy_write_clip_detection(dev, clip_state); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */ static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, u8 type) { int i, j; u8 state[4]; u8 code, val; u16 class, override; u8 regs_save_radio[2]; u16 regs_save_phy[2]; s8 offset[4]; u8 core; u8 rail; u16 clip_state[2]; u16 clip_off[2] = { 0xFFFF, 0xFFFF }; s32 results_min[4] = { }; u8 vcm_final[4] = { }; s32 results[4][4] = { }; s32 miniq[4][2] = { }; if (type == 2) { code = 0; val = 6; } else if (type < 2) { code = 25; val = 4; } else { B43_WARN_ON(1); return; } class = b43_nphy_classifier(dev, 0, 0); b43_nphy_classifier(dev, 7, 4); b43_nphy_read_clip_detection(dev, clip_state); b43_nphy_write_clip_detection(dev, clip_off); if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) override = 0x140; else override = 0x110; regs_save_phy[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); regs_save_radio[0] = b43_radio_read16(dev, B2055_C1_PD_RXTX); b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, override); b43_radio_write16(dev, B2055_C1_PD_RXTX, val); regs_save_phy[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); regs_save_radio[1] = b43_radio_read16(dev, B2055_C2_PD_RXTX); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, override); b43_radio_write16(dev, B2055_C2_PD_RXTX, val); state[0] = b43_radio_read16(dev, B2055_C1_PD_RSSIMISC) & 0x07; state[1] = b43_radio_read16(dev, B2055_C2_PD_RSSIMISC) & 0x07; b43_radio_mask(dev, B2055_C1_PD_RSSIMISC, 0xF8); b43_radio_mask(dev, B2055_C2_PD_RSSIMISC, 0xF8); state[2] = b43_radio_read16(dev, B2055_C1_SP_RSSI) & 0x07; state[3] = b43_radio_read16(dev, B2055_C2_SP_RSSI) & 0x07; b43_nphy_rssi_select(dev, 5, type); b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 0, type); b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 1, type); for (i = 0; i < 4; i++) { u8 tmp[4]; for (j = 0; j < 4; j++) tmp[j] = i; if (type != 1) b43_nphy_set_rssi_2055_vcm(dev, type, tmp); b43_nphy_poll_rssi(dev, type, results[i], 8); if (type < 2) for (j = 0; j < 2; j++) miniq[i][j] = min(results[i][2 * j], results[i][2 * j + 1]); } for (i = 0; i < 4; i++) { s32 mind = 40; u8 minvcm = 0; s32 minpoll = 249; s32 curr; for (j = 0; j < 4; j++) { if (type == 2) curr = abs(results[j][i]); else curr = abs(miniq[j][i / 2] - code * 8); if (curr < mind) { mind = curr; minvcm = j; } if (results[j][i] < minpoll) minpoll = results[j][i]; } results_min[i] = minpoll; vcm_final[i] = minvcm; } if (type != 1) b43_nphy_set_rssi_2055_vcm(dev, type, vcm_final); for (i = 0; i < 4; i++) { offset[i] = (code * 8) - results[vcm_final[i]][i]; if (offset[i] < 0) offset[i] = -((abs(offset[i]) + 4) / 8); else offset[i] = (offset[i] + 4) / 8; if (results_min[i] == 248) offset[i] = code - 32; core = (i / 2) ? 2 : 1; rail = (i % 2) ? 1 : 0; b43_nphy_scale_offset_rssi(dev, 0, offset[i], core, rail, type); } b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[0]); b43_radio_maskset(dev, B2055_C2_PD_RSSIMISC, 0xF8, state[1]); switch (state[2]) { case 1: b43_nphy_rssi_select(dev, 1, 2); break; case 4: b43_nphy_rssi_select(dev, 1, 0); break; case 2: b43_nphy_rssi_select(dev, 1, 1); break; default: b43_nphy_rssi_select(dev, 1, 1); break; } switch (state[3]) { case 1: b43_nphy_rssi_select(dev, 2, 2); break; case 4: b43_nphy_rssi_select(dev, 2, 0); break; default: b43_nphy_rssi_select(dev, 2, 1); break; } b43_nphy_rssi_select(dev, 0, type); b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs_save_phy[0]); b43_radio_write16(dev, B2055_C1_PD_RXTX, regs_save_radio[0]); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs_save_phy[1]); b43_radio_write16(dev, B2055_C2_PD_RXTX, regs_save_radio[1]); b43_nphy_classifier(dev, 7, class); b43_nphy_write_clip_detection(dev, clip_state); /* Specs don't say about reset here, but it makes wl and b43 dumps identical, it really seems wl performs this */ b43_nphy_reset_cca(dev); } /* * RSSI Calibration * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */ static void b43_nphy_rssi_cal(struct b43_wldev *dev) { if (dev->phy.rev >= 3) { b43_nphy_rev3_rssi_cal(dev); } else { b43_nphy_rev2_rssi_cal(dev, B43_NPHY_RSSI_Z); b43_nphy_rev2_rssi_cal(dev, B43_NPHY_RSSI_X); b43_nphy_rev2_rssi_cal(dev, B43_NPHY_RSSI_Y); } } /************************************************** * Workarounds **************************************************/ static void b43_nphy_gain_ctl_workarounds_rev3plus(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; bool ghz5; bool ext_lna; u16 rssi_gain; struct nphy_gain_ctl_workaround_entry *e; u8 lpf_gain[6] = { 0x00, 0x06, 0x0C, 0x12, 0x12, 0x12 }; u8 lpf_bits[6] = { 0, 1, 2, 3, 3, 3 }; /* Prepare values */ ghz5 = b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ; ext_lna = ghz5 ? sprom->boardflags_hi & B43_BFH_EXTLNA_5GHZ : sprom->boardflags_lo & B43_BFL_EXTLNA; e = b43_nphy_get_gain_ctl_workaround_ent(dev, ghz5, ext_lna); if (ghz5 && dev->phy.rev >= 5) rssi_gain = 0x90; else rssi_gain = 0x50; b43_phy_set(dev, B43_NPHY_RXCTL, 0x0040); /* Set Clip 2 detect */ b43_phy_set(dev, B43_NPHY_C1_CGAINI, B43_NPHY_C1_CGAINI_CL2DETECT); b43_phy_set(dev, B43_NPHY_C2_CGAINI, B43_NPHY_C2_CGAINI_CL2DETECT); b43_radio_write(dev, B2056_RX0 | B2056_RX_BIASPOLE_LNAG1_IDAC, 0x17); b43_radio_write(dev, B2056_RX1 | B2056_RX_BIASPOLE_LNAG1_IDAC, 0x17); b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAG2_IDAC, 0xF0); b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAG2_IDAC, 0xF0); b43_radio_write(dev, B2056_RX0 | B2056_RX_RSSI_POLE, 0x00); b43_radio_write(dev, B2056_RX1 | B2056_RX_RSSI_POLE, 0x00); b43_radio_write(dev, B2056_RX0 | B2056_RX_RSSI_GAIN, rssi_gain); b43_radio_write(dev, B2056_RX1 | B2056_RX_RSSI_GAIN, rssi_gain); b43_radio_write(dev, B2056_RX0 | B2056_RX_BIASPOLE_LNAA1_IDAC, 0x17); b43_radio_write(dev, B2056_RX1 | B2056_RX_BIASPOLE_LNAA1_IDAC, 0x17); b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAA2_IDAC, 0xFF); b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAA2_IDAC, 0xFF); b43_ntab_write_bulk(dev, B43_NTAB8(0, 8), 4, e->lna1_gain); b43_ntab_write_bulk(dev, B43_NTAB8(1, 8), 4, e->lna1_gain); b43_ntab_write_bulk(dev, B43_NTAB8(0, 16), 4, e->lna2_gain); b43_ntab_write_bulk(dev, B43_NTAB8(1, 16), 4, e->lna2_gain); b43_ntab_write_bulk(dev, B43_NTAB8(0, 32), 10, e->gain_db); b43_ntab_write_bulk(dev, B43_NTAB8(1, 32), 10, e->gain_db); b43_ntab_write_bulk(dev, B43_NTAB8(2, 32), 10, e->gain_bits); b43_ntab_write_bulk(dev, B43_NTAB8(3, 32), 10, e->gain_bits); b43_ntab_write_bulk(dev, B43_NTAB8(0, 0x40), 6, lpf_gain); b43_ntab_write_bulk(dev, B43_NTAB8(1, 0x40), 6, lpf_gain); b43_ntab_write_bulk(dev, B43_NTAB8(2, 0x40), 6, lpf_bits); b43_ntab_write_bulk(dev, B43_NTAB8(3, 0x40), 6, lpf_bits); b43_phy_write(dev, B43_NPHY_C1_INITGAIN, e->init_gain); b43_phy_write(dev, 0x2A7, e->init_gain); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x106), 2, e->rfseq_init); /* TODO: check defines. Do not match variables names */ b43_phy_write(dev, B43_NPHY_C1_CLIP1_MEDGAIN, e->cliphi_gain); b43_phy_write(dev, 0x2A9, e->cliphi_gain); b43_phy_write(dev, B43_NPHY_C1_CLIP2_GAIN, e->clipmd_gain); b43_phy_write(dev, 0x2AB, e->clipmd_gain); b43_phy_write(dev, B43_NPHY_C2_CLIP1_HIGAIN, e->cliplo_gain); b43_phy_write(dev, 0x2AD, e->cliplo_gain); b43_phy_maskset(dev, 0x27D, 0xFF00, e->crsmin); b43_phy_maskset(dev, 0x280, 0xFF00, e->crsminl); b43_phy_maskset(dev, 0x283, 0xFF00, e->crsminu); b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, e->nbclip); b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, e->nbclip); b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, e->wlclip); b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, e->wlclip); b43_phy_write(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C); } static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u8 i, j; u8 code; u16 tmp; u8 rfseq_events[3] = { 6, 8, 7 }; u8 rfseq_delays[3] = { 10, 30, 1 }; /* Set Clip 2 detect */ b43_phy_set(dev, B43_NPHY_C1_CGAINI, B43_NPHY_C1_CGAINI_CL2DETECT); b43_phy_set(dev, B43_NPHY_C2_CGAINI, B43_NPHY_C2_CGAINI_CL2DETECT); /* Set narrowband clip threshold */ b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84); b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84); if (!dev->phy.is_40mhz) { /* Set dwell lengths */ b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B); b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B); b43_phy_write(dev, B43_NPHY_W1CLIP1_DWELL_LEN, 0x0009); b43_phy_write(dev, B43_NPHY_W1CLIP2_DWELL_LEN, 0x0009); } /* Set wideband clip 2 threshold */ b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, 21); b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21); if (!dev->phy.is_40mhz) { b43_phy_maskset(dev, B43_NPHY_C1_CGAINI, ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1); b43_phy_maskset(dev, B43_NPHY_C2_CGAINI, ~B43_NPHY_C2_CGAINI_GAINBKOFF, 0x1); b43_phy_maskset(dev, B43_NPHY_C1_CCK_CGAINI, ~B43_NPHY_C1_CCK_CGAINI_GAINBKOFF, 0x1); b43_phy_maskset(dev, B43_NPHY_C2_CCK_CGAINI, ~B43_NPHY_C2_CCK_CGAINI_GAINBKOFF, 0x1); } b43_phy_write(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C); if (nphy->gain_boost) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ && dev->phy.is_40mhz) code = 4; else code = 5; } else { code = dev->phy.is_40mhz ? 6 : 7; } /* Set HPVGA2 index */ b43_phy_maskset(dev, B43_NPHY_C1_INITGAIN, ~B43_NPHY_C1_INITGAIN_HPVGA2, code << B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT); b43_phy_maskset(dev, B43_NPHY_C2_INITGAIN, ~B43_NPHY_C2_INITGAIN_HPVGA2, code << B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); /* specs say about 2 loops, but wl does 4 */ for (i = 0; i < 4; i++) b43_phy_write(dev, B43_NPHY_TABLE_DATALO, (code << 8 | 0x7C)); b43_nphy_adjust_lna_gain_table(dev); if (nphy->elna_gain_config) { b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0808); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0C08); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); /* specs say about 2 loops, but wl does 4 */ for (i = 0; i < 4; i++) b43_phy_write(dev, B43_NPHY_TABLE_DATALO, (code << 8 | 0x74)); } if (dev->phy.rev == 2) { for (i = 0; i < 4; i++) { b43_phy_write(dev, B43_NPHY_TABLE_ADDR, (0x0400 * i) + 0x0020); for (j = 0; j < 21; j++) { tmp = j * (i < 2 ? 3 : 1); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, tmp); } } } b43_nphy_set_rf_sequence(dev, 5, rfseq_events, rfseq_delays, 3); b43_phy_maskset(dev, B43_NPHY_OVER_DGAIN1, ~B43_NPHY_OVER_DGAIN_CCKDGECV & 0xFFFF, 0x5A << B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_phy_maskset(dev, B43_PHY_N(0xC5D), 0xFF80, 4); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */ static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev) { if (dev->phy.rev >= 7) ; /* TODO */ else if (dev->phy.rev >= 3) b43_nphy_gain_ctl_workarounds_rev3plus(dev); else b43_nphy_gain_ctl_workarounds_rev1_2(dev); } /* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */ static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset) { if (!offset) offset = (dev->phy.is_40mhz) ? 0x159 : 0x154; return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7; } static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy *phy = &dev->phy; u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3, 0x1F }; u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; u16 ntab7_15e_16e[] = { 0x10f, 0x10f }; u8 ntab7_138_146[] = { 0x11, 0x11 }; u8 ntab7_133[] = { 0x77, 0x11, 0x11 }; u16 lpf_20, lpf_40, lpf_11b; u16 bcap_val, bcap_val_11b, bcap_val_11n_20, bcap_val_11n_40; u16 scap_val, scap_val_11b, scap_val_11n_20, scap_val_11n_40; bool rccal_ovrd = false; u16 rx2tx_lut_20_11b, rx2tx_lut_20_11n, rx2tx_lut_40_11n; u16 bias, conv, filt; u32 tmp32; u8 core; if (phy->rev == 7) { b43_phy_set(dev, B43_NPHY_FINERX2_CGC, 0x10); b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0xFF80, 0x0020); b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0x80FF, 0x2700); b43_phy_maskset(dev, B43_NPHY_FREQGAIN1, 0xFF80, 0x002E); b43_phy_maskset(dev, B43_NPHY_FREQGAIN1, 0x80FF, 0x3300); b43_phy_maskset(dev, B43_NPHY_FREQGAIN2, 0xFF80, 0x0037); b43_phy_maskset(dev, B43_NPHY_FREQGAIN2, 0x80FF, 0x3A00); b43_phy_maskset(dev, B43_NPHY_FREQGAIN3, 0xFF80, 0x003C); b43_phy_maskset(dev, B43_NPHY_FREQGAIN3, 0x80FF, 0x3E00); b43_phy_maskset(dev, B43_NPHY_FREQGAIN4, 0xFF80, 0x003E); b43_phy_maskset(dev, B43_NPHY_FREQGAIN4, 0x80FF, 0x3F00); b43_phy_maskset(dev, B43_NPHY_FREQGAIN5, 0xFF80, 0x0040); b43_phy_maskset(dev, B43_NPHY_FREQGAIN5, 0x80FF, 0x4000); b43_phy_maskset(dev, B43_NPHY_FREQGAIN6, 0xFF80, 0x0040); b43_phy_maskset(dev, B43_NPHY_FREQGAIN6, 0x80FF, 0x4000); b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0xFF80, 0x0040); b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0x80FF, 0x4000); } if (phy->rev <= 8) { b43_phy_write(dev, 0x23F, 0x1B0); b43_phy_write(dev, 0x240, 0x1B0); } if (phy->rev >= 8) b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0x72); b43_ntab_write(dev, B43_NTAB16(8, 0x00), 2); b43_ntab_write(dev, B43_NTAB16(8, 0x10), 2); tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0)); tmp32 &= 0xffffff; b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x15e), 2, ntab7_15e_16e); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x16e), 2, ntab7_15e_16e); if (b43_nphy_ipa(dev)) b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa, rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa)); b43_phy_maskset(dev, 0x299, 0x3FFF, 0x4000); b43_phy_maskset(dev, 0x29D, 0x3FFF, 0x4000); lpf_20 = b43_nphy_read_lpf_ctl(dev, 0x154); lpf_40 = b43_nphy_read_lpf_ctl(dev, 0x159); lpf_11b = b43_nphy_read_lpf_ctl(dev, 0x152); if (b43_nphy_ipa(dev)) { if ((phy->radio_rev == 5 && phy->is_40mhz) || phy->radio_rev == 7 || phy->radio_rev == 8) { bcap_val = b43_radio_read(dev, 0x16b); scap_val = b43_radio_read(dev, 0x16a); scap_val_11b = scap_val; bcap_val_11b = bcap_val; if (phy->radio_rev == 5 && phy->is_40mhz) { scap_val_11n_20 = scap_val; bcap_val_11n_20 = bcap_val; scap_val_11n_40 = bcap_val_11n_40 = 0xc; rccal_ovrd = true; } else { /* Rev 7/8 */ lpf_20 = 4; lpf_11b = 1; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { scap_val_11n_20 = 0xc; bcap_val_11n_20 = 0xc; scap_val_11n_40 = 0xa; bcap_val_11n_40 = 0xa; } else { scap_val_11n_20 = 0x14; bcap_val_11n_20 = 0x14; scap_val_11n_40 = 0xf; bcap_val_11n_40 = 0xf; } rccal_ovrd = true; } } } else { if (phy->radio_rev == 5) { lpf_20 = 1; lpf_40 = 3; bcap_val = b43_radio_read(dev, 0x16b); scap_val = b43_radio_read(dev, 0x16a); scap_val_11b = scap_val; bcap_val_11b = bcap_val; scap_val_11n_20 = 0x11; scap_val_11n_40 = 0x11; bcap_val_11n_20 = 0x13; bcap_val_11n_40 = 0x13; rccal_ovrd = true; } } if (rccal_ovrd) { rx2tx_lut_20_11b = (bcap_val_11b << 8) | (scap_val_11b << 3) | lpf_11b; rx2tx_lut_20_11n = (bcap_val_11n_20 << 8) | (scap_val_11n_20 << 3) | lpf_20; rx2tx_lut_40_11n = (bcap_val_11n_40 << 8) | (scap_val_11n_40 << 3) | lpf_40; for (core = 0; core < 2; core++) { b43_ntab_write(dev, B43_NTAB16(7, 0x152 + core * 16), rx2tx_lut_20_11b); b43_ntab_write(dev, B43_NTAB16(7, 0x153 + core * 16), rx2tx_lut_20_11n); b43_ntab_write(dev, B43_NTAB16(7, 0x154 + core * 16), rx2tx_lut_20_11n); b43_ntab_write(dev, B43_NTAB16(7, 0x155 + core * 16), rx2tx_lut_40_11n); b43_ntab_write(dev, B43_NTAB16(7, 0x156 + core * 16), rx2tx_lut_40_11n); b43_ntab_write(dev, B43_NTAB16(7, 0x157 + core * 16), rx2tx_lut_40_11n); b43_ntab_write(dev, B43_NTAB16(7, 0x158 + core * 16), rx2tx_lut_40_11n); b43_ntab_write(dev, B43_NTAB16(7, 0x159 + core * 16), rx2tx_lut_40_11n); } b43_nphy_rf_control_override_rev7(dev, 16, 1, 3, false, 2); } b43_phy_write(dev, 0x32F, 0x3); if (phy->radio_rev == 4 || phy->radio_rev == 6) b43_nphy_rf_control_override_rev7(dev, 4, 1, 3, false, 0); if (phy->radio_rev == 3 || phy->radio_rev == 4 || phy->radio_rev == 6) { if (sprom->revision && sprom->boardflags2_hi & B43_BFH2_IPALVLSHIFT_3P3) { b43_radio_write(dev, 0x5, 0x05); b43_radio_write(dev, 0x6, 0x30); b43_radio_write(dev, 0x7, 0x00); b43_radio_set(dev, 0x4f, 0x1); b43_radio_set(dev, 0xd4, 0x1); bias = 0x1f; conv = 0x6f; filt = 0xaa; } else { bias = 0x2b; conv = 0x7f; filt = 0xee; } if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { for (core = 0; core < 2; core++) { if (core == 0) { b43_radio_write(dev, 0x5F, bias); b43_radio_write(dev, 0x64, conv); b43_radio_write(dev, 0x66, filt); } else { b43_radio_write(dev, 0xE8, bias); b43_radio_write(dev, 0xE9, conv); b43_radio_write(dev, 0xEB, filt); } } } } if (b43_nphy_ipa(dev)) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if (phy->radio_rev == 3 || phy->radio_rev == 4 || phy->radio_rev == 6) { for (core = 0; core < 2; core++) { if (core == 0) b43_radio_write(dev, 0x51, 0x7f); else b43_radio_write(dev, 0xd6, 0x7f); } } if (phy->radio_rev == 3) { for (core = 0; core < 2; core++) { if (core == 0) { b43_radio_write(dev, 0x64, 0x13); b43_radio_write(dev, 0x5F, 0x1F); b43_radio_write(dev, 0x66, 0xEE); b43_radio_write(dev, 0x59, 0x8A); b43_radio_write(dev, 0x80, 0x3E); } else { b43_radio_write(dev, 0x69, 0x13); b43_radio_write(dev, 0xE8, 0x1F); b43_radio_write(dev, 0xEB, 0xEE); b43_radio_write(dev, 0xDE, 0x8A); b43_radio_write(dev, 0x105, 0x3E); } } } else if (phy->radio_rev == 7 || phy->radio_rev == 8) { if (!phy->is_40mhz) { b43_radio_write(dev, 0x5F, 0x14); b43_radio_write(dev, 0xE8, 0x12); } else { b43_radio_write(dev, 0x5F, 0x16); b43_radio_write(dev, 0xE8, 0x16); } } } else { u16 freq = phy->channel_freq; if ((freq >= 5180 && freq <= 5230) || (freq >= 5745 && freq <= 5805)) { b43_radio_write(dev, 0x7D, 0xFF); b43_radio_write(dev, 0xFE, 0xFF); } } } else { if (phy->radio_rev != 5) { for (core = 0; core < 2; core++) { if (core == 0) { b43_radio_write(dev, 0x5c, 0x61); b43_radio_write(dev, 0x51, 0x70); } else { b43_radio_write(dev, 0xe1, 0x61); b43_radio_write(dev, 0xd6, 0x70); } } } } if (phy->radio_rev == 4) { b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0x20); b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0x20); for (core = 0; core < 2; core++) { if (core == 0) { b43_radio_write(dev, 0x1a1, 0x00); b43_radio_write(dev, 0x1a2, 0x3f); b43_radio_write(dev, 0x1a6, 0x3f); } else { b43_radio_write(dev, 0x1a7, 0x00); b43_radio_write(dev, 0x1ab, 0x3f); b43_radio_write(dev, 0x1ac, 0x3f); } } } else { b43_phy_set(dev, B43_NPHY_AFECTL_C1, 0x4); b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x4); b43_phy_set(dev, B43_NPHY_AFECTL_C2, 0x4); b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4); b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x1); b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x1); b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x1); b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x1); b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0x20); b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0x20); b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x4); b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x4); b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x4); b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4); } b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, 0x2); b43_ntab_write(dev, B43_NTAB32(16, 0x100), 20); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x138), 2, ntab7_138_146); b43_ntab_write(dev, B43_NTAB16(7, 0x141), 0x77); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x133), 3, ntab7_133); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x146), 2, ntab7_138_146); b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77); b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77); if (!phy->is_40mhz) { b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x18D); b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x18D); } else { b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x14D); b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x14D); } b43_nphy_gain_ctl_workarounds(dev); /* TODO b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, aux_adc_vmid_rev7_core0); b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, aux_adc_vmid_rev7_core1); b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0C), 4, aux_adc_gain_rev7); b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1C), 4, aux_adc_gain_rev7); */ } static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; struct ssb_sprom *sprom = dev->dev->bus_sprom; /* TX to RX */ u8 tx2rx_events[8] = { 0x4, 0x3, 0x6, 0x5, 0x2, 0x1, 0x8, 0x1F }; u8 tx2rx_delays[8] = { 8, 4, 2, 2, 4, 4, 6, 1 }; /* RX to TX */ u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3, 0x1F }; u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F }; u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; u16 tmp16; u32 tmp32; b43_phy_write(dev, 0x23f, 0x1f8); b43_phy_write(dev, 0x240, 0x1f8); tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0)); tmp32 &= 0xffffff; b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32); b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125); b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01B3); b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105); b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016E); b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00CD); b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020); b43_phy_write(dev, B43_NPHY_C2_CLIP1_MEDGAIN, 0x000C); b43_phy_write(dev, 0x2AE, 0x000C); /* TX to RX */ b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays, ARRAY_SIZE(tx2rx_events)); /* RX to TX */ if (b43_nphy_ipa(dev)) b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa, rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa)); if (nphy->hw_phyrxchain != 3 && nphy->hw_phyrxchain != nphy->hw_phytxchain) { if (b43_nphy_ipa(dev)) { rx2tx_delays[5] = 59; rx2tx_delays[6] = 1; rx2tx_events[7] = 0x1F; } b43_nphy_set_rf_sequence(dev, 0, rx2tx_events, rx2tx_delays, ARRAY_SIZE(rx2tx_events)); } tmp16 = (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ? 0x2 : 0x9C40; b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, tmp16); b43_phy_maskset(dev, 0x294, 0xF0FF, 0x0700); if (!dev->phy.is_40mhz) { b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D); b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D); } else { b43_ntab_write(dev, B43_NTAB32(16, 3), 0x14D); b43_ntab_write(dev, B43_NTAB32(16, 127), 0x14D); } b43_nphy_gain_ctl_workarounds(dev); b43_ntab_write(dev, B43_NTAB16(8, 0), 2); b43_ntab_write(dev, B43_NTAB16(8, 16), 2); /* TODO */ b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00); b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00); b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_MAIN, 0x06); b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_MAIN, 0x06); b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_AUX, 0x07); b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_AUX, 0x07); b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_LOB_BIAS, 0x88); b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_LOB_BIAS, 0x88); b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_CMFB_IDAC, 0x00); b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_CMFB_IDAC, 0x00); b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXG_CMFB_IDAC, 0x00); b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXG_CMFB_IDAC, 0x00); /* N PHY WAR TX Chain Update with hw_phytxchain as argument */ if ((sprom->boardflags2_lo & B43_BFL2_APLL_WAR && b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) || (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR && b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) tmp32 = 0x00088888; else tmp32 = 0x88888888; b43_ntab_write(dev, B43_NTAB32(30, 1), tmp32); b43_ntab_write(dev, B43_NTAB32(30, 2), tmp32); b43_ntab_write(dev, B43_NTAB32(30, 3), tmp32); if (dev->phy.rev == 4 && b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { b43_radio_write(dev, B2056_TX0 | B2056_TX_GMBB_IDAC, 0x70); b43_radio_write(dev, B2056_TX1 | B2056_TX_GMBB_IDAC, 0x70); } /* Dropped probably-always-true condition */ b43_phy_write(dev, 0x224, 0x03eb); b43_phy_write(dev, 0x225, 0x03eb); b43_phy_write(dev, 0x226, 0x0341); b43_phy_write(dev, 0x227, 0x0341); b43_phy_write(dev, 0x228, 0x042b); b43_phy_write(dev, 0x229, 0x042b); b43_phy_write(dev, 0x22a, 0x0381); b43_phy_write(dev, 0x22b, 0x0381); b43_phy_write(dev, 0x22c, 0x042b); b43_phy_write(dev, 0x22d, 0x042b); b43_phy_write(dev, 0x22e, 0x0381); b43_phy_write(dev, 0x22f, 0x0381); if (dev->phy.rev >= 6 && sprom->boardflags2_lo & B43_BFL2_SINGLEANT_CCK) ; /* TODO: 0x0080000000000000 HF */ } static void b43_nphy_workarounds_rev1_2(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = phy->n; u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 }; u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 }; u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 }; u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 }; if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD || dev->dev->board_type == 0x8B) { delays1[0] = 0x1; delays1[5] = 0x14; } if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ && nphy->band5g_pwrgain) { b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8); b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8); } else { b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8); b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8); } b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0x000A); b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0x000A); if (dev->phy.rev < 3) { b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA); b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA); } if (dev->phy.rev < 2) { b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0x0000); b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0x0000); b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB); b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB); b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x0800); b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x0800); } b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7); b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7); b43_nphy_gain_ctl_workarounds(dev); if (dev->phy.rev < 2) { if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2) b43_hf_write(dev, b43_hf_read(dev) | B43_HF_MLADVW); } else if (dev->phy.rev == 2) { b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0); b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0); } if (dev->phy.rev < 2) b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL, ~B43_NPHY_SCRAM_SIGCTL_SCM); /* Set phase track alpha and beta */ b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125); b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3); b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105); b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E); b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD); b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20); if (dev->phy.rev < 3) { b43_phy_mask(dev, B43_NPHY_PIL_DW1, ~B43_NPHY_PIL_DW_64QAM & 0xFFFF); b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5); b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4); b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00); } if (dev->phy.rev == 2) b43_phy_set(dev, B43_NPHY_FINERX2_CGC, B43_NPHY_FINERX2_CGC_DECGC); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */ static void b43_nphy_workarounds(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = phy->n; if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) b43_nphy_classifier(dev, 1, 0); else b43_nphy_classifier(dev, 1, 1); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); b43_phy_set(dev, B43_NPHY_IQFLIP, B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2); if (dev->phy.rev >= 7) b43_nphy_workarounds_rev7plus(dev); else if (dev->phy.rev >= 3) b43_nphy_workarounds_rev3plus(dev); else b43_nphy_workarounds_rev1_2(dev); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } /************************************************** * Tx/Rx common **************************************************/ /* * Transmits a known value for LO calibration * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone */ static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val, bool iqmode, bool dac_test) { u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test); if (samp == 0) return -1; b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test); return 0; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Chains */ static void b43_nphy_update_txrx_chain(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; bool override = false; u16 chain = 0x33; if (nphy->txrx_chain == 0) { chain = 0x11; override = true; } else if (nphy->txrx_chain == 1) { chain = 0x22; override = true; } b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~(B43_NPHY_RFSEQCA_TXEN | B43_NPHY_RFSEQCA_RXEN), chain); if (override) b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER); else b43_phy_mask(dev, B43_NPHY_RFSEQMODE, ~B43_NPHY_RFSEQMODE_CAOVER); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/stop-playback */ static void b43_nphy_stop_playback(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u16 tmp; if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); tmp = b43_phy_read(dev, B43_NPHY_SAMP_STAT); if (tmp & 0x1) b43_phy_set(dev, B43_NPHY_SAMP_CMD, B43_NPHY_SAMP_CMD_STOP); else if (tmp & 0x2) b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); b43_phy_mask(dev, B43_NPHY_SAMP_CMD, ~0x0004); if (nphy->bb_mult_save & 0x80000000) { tmp = nphy->bb_mult_save & 0xFFFF; b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); nphy->bb_mult_save = 0; } if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IqCalGainParams */ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, struct nphy_txgains target, struct nphy_iqcal_params *params) { int i, j, indx; u16 gain; if (dev->phy.rev >= 3) { params->txgm = target.txgm[core]; params->pga = target.pga[core]; params->pad = target.pad[core]; params->ipa = target.ipa[core]; params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa); for (j = 0; j < 5; j++) params->ncorr[j] = 0x79; } else { gain = (target.pad[core]) | (target.pga[core] << 4) | (target.txgm[core] << 8); indx = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 1 : 0; for (i = 0; i < 9; i++) if (tbl_iqcal_gainparams[indx][i][0] == gain) break; i = min(i, 8); params->txgm = tbl_iqcal_gainparams[indx][i][1]; params->pga = tbl_iqcal_gainparams[indx][i][2]; params->pad = tbl_iqcal_gainparams[indx][i][3]; params->cal_gain = (params->txgm << 7) | (params->pga << 4) | (params->pad << 2); for (j = 0; j < 4; j++) params->ncorr[j] = tbl_iqcal_gainparams[indx][i][4 + j]; } } /************************************************** * Tx and Rx **************************************************/ void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna) {//TODO } static void b43_nphy_op_adjust_txpower(struct b43_wldev *dev) {//TODO } static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) {//TODO return B43_TXPWR_RES_DONE; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) { struct b43_phy_n *nphy = dev->phy.n; u8 i; u16 bmask, val, tmp; enum ieee80211_band band = b43_current_band(dev->wl); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); nphy->txpwrctrl = enable; if (!enable) { if (dev->phy.rev >= 3 && (b43_phy_read(dev, B43_NPHY_TXPCTL_CMD) & (B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN | B43_NPHY_TXPCTL_CMD_PCTLEN))) { /* We disable enabled TX pwr ctl, save it's state */ nphy->tx_pwr_idx[0] = b43_phy_read(dev, B43_NPHY_C1_TXPCTL_STAT) & 0x7f; nphy->tx_pwr_idx[1] = b43_phy_read(dev, B43_NPHY_C2_TXPCTL_STAT) & 0x7f; } b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6840); for (i = 0; i < 84; i++) b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6C40); for (i = 0; i < 84; i++) b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0); tmp = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN; if (dev->phy.rev >= 3) tmp |= B43_NPHY_TXPCTL_CMD_PCTLEN; b43_phy_mask(dev, B43_NPHY_TXPCTL_CMD, ~tmp); if (dev->phy.rev >= 3) { b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0100); b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0100); } else { b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4000); } if (dev->phy.rev == 2) b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~B43_NPHY_BPHY_CTL3_SCALE, 0x53); else if (dev->phy.rev < 2) b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~B43_NPHY_BPHY_CTL3_SCALE, 0x5A); if (dev->phy.rev < 2 && dev->phy.is_40mhz) b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW); } else { b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, nphy->adj_pwr_tbl); b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, nphy->adj_pwr_tbl); bmask = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN; /* wl does useless check for "enable" param here */ val = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN; if (dev->phy.rev >= 3) { bmask |= B43_NPHY_TXPCTL_CMD_PCTLEN; if (val) val |= B43_NPHY_TXPCTL_CMD_PCTLEN; } b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val); if (band == IEEE80211_BAND_5GHZ) { b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~B43_NPHY_TXPCTL_CMD_INIT, 0x64); if (dev->phy.rev > 1) b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, ~B43_NPHY_TXPCTL_INIT_PIDXI1, 0x64); } if (dev->phy.rev >= 3) { if (nphy->tx_pwr_idx[0] != 128 && nphy->tx_pwr_idx[1] != 128) { /* Recover TX pwr ctl state */ b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~B43_NPHY_TXPCTL_CMD_INIT, nphy->tx_pwr_idx[0]); if (dev->phy.rev > 1) b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, ~0xff, nphy->tx_pwr_idx[1]); } } if (dev->phy.rev >= 3) { b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100); b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100); } else { b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4000); } if (dev->phy.rev == 2) b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x3b); else if (dev->phy.rev < 2) b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40); if (dev->phy.rev < 2 && dev->phy.is_40mhz) b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW); if (b43_nphy_ipa(dev)) { b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x4); b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x4); } } if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */ static void b43_nphy_tx_power_fix(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; struct ssb_sprom *sprom = dev->dev->bus_sprom; u8 txpi[2], bbmult, i; u16 tmp, radio_gain, dac_gain; u16 freq = dev->phy.channel_freq; u32 txgain; /* u32 gaintbl; rev3+ */ if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); if (dev->phy.rev >= 7) { txpi[0] = txpi[1] = 30; } else if (dev->phy.rev >= 3) { txpi[0] = 40; txpi[1] = 40; } else if (sprom->revision < 4) { txpi[0] = 72; txpi[1] = 72; } else { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { txpi[0] = sprom->txpid2g[0]; txpi[1] = sprom->txpid2g[1]; } else if (freq >= 4900 && freq < 5100) { txpi[0] = sprom->txpid5gl[0]; txpi[1] = sprom->txpid5gl[1]; } else if (freq >= 5100 && freq < 5500) { txpi[0] = sprom->txpid5g[0]; txpi[1] = sprom->txpid5g[1]; } else if (freq >= 5500) { txpi[0] = sprom->txpid5gh[0]; txpi[1] = sprom->txpid5gh[1]; } else { txpi[0] = 91; txpi[1] = 91; } } if (dev->phy.rev < 7 && (txpi[0] < 40 || txpi[0] > 100 || txpi[1] < 40 || txpi[1] > 100)) txpi[0] = txpi[1] = 91; /* for (i = 0; i < 2; i++) { nphy->txpwrindex[i].index_internal = txpi[i]; nphy->txpwrindex[i].index_internal_save = txpi[i]; } */ for (i = 0; i < 2; i++) { txgain = *(b43_nphy_get_tx_gain_table(dev) + txpi[i]); if (dev->phy.rev >= 3) radio_gain = (txgain >> 16) & 0x1FFFF; else radio_gain = (txgain >> 16) & 0x1FFF; if (dev->phy.rev >= 7) dac_gain = (txgain >> 8) & 0x7; else dac_gain = (txgain >> 8) & 0x3F; bbmult = txgain & 0xFF; if (dev->phy.rev >= 3) { if (i == 0) b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0100); else b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0100); } else { b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4000); } if (i == 0) b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN1, dac_gain); else b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN2, dac_gain); b43_ntab_write(dev, B43_NTAB16(0x7, 0x110 + i), radio_gain); tmp = b43_ntab_read(dev, B43_NTAB16(0xF, 0x57)); if (i == 0) tmp = (tmp & 0x00FF) | (bbmult << 8); else tmp = (tmp & 0xFF00) | bbmult; b43_ntab_write(dev, B43_NTAB16(0xF, 0x57), tmp); if (b43_nphy_ipa(dev)) { u32 tmp32; u16 reg = (i == 0) ? B43_NPHY_PAPD_EN0 : B43_NPHY_PAPD_EN1; tmp32 = b43_ntab_read(dev, B43_NTAB32(26 + i, 576 + txpi[i])); b43_phy_maskset(dev, reg, 0xE00F, (u32) tmp32 << 4); b43_phy_set(dev, reg, 0x4); } } b43_phy_mask(dev, B43_NPHY_BPHY_CTL2, ~B43_NPHY_BPHY_CTL2_LUT); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } static void b43_nphy_ipa_internal_tssi_setup(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u8 core; u16 r; /* routing */ if (phy->rev >= 7) { for (core = 0; core < 2; core++) { r = core ? 0x190 : 0x170; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_radio_write(dev, r + 0x5, 0x5); b43_radio_write(dev, r + 0x9, 0xE); if (phy->rev != 5) b43_radio_write(dev, r + 0xA, 0); if (phy->rev != 7) b43_radio_write(dev, r + 0xB, 1); else b43_radio_write(dev, r + 0xB, 0x31); } else { b43_radio_write(dev, r + 0x5, 0x9); b43_radio_write(dev, r + 0x9, 0xC); b43_radio_write(dev, r + 0xB, 0x0); if (phy->rev != 5) b43_radio_write(dev, r + 0xA, 1); else b43_radio_write(dev, r + 0xA, 0x31); } b43_radio_write(dev, r + 0x6, 0); b43_radio_write(dev, r + 0x7, 0); b43_radio_write(dev, r + 0x8, 3); b43_radio_write(dev, r + 0xC, 0); } } else { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_radio_write(dev, B2056_SYN_RESERVED_ADDR31, 0x128); else b43_radio_write(dev, B2056_SYN_RESERVED_ADDR31, 0x80); b43_radio_write(dev, B2056_SYN_RESERVED_ADDR30, 0); b43_radio_write(dev, B2056_SYN_GPIO_MASTER1, 0x29); for (core = 0; core < 2; core++) { r = core ? B2056_TX1 : B2056_TX0; b43_radio_write(dev, r | B2056_TX_IQCAL_VCM_HG, 0); b43_radio_write(dev, r | B2056_TX_IQCAL_IDAC, 0); b43_radio_write(dev, r | B2056_TX_TSSI_VCM, 3); b43_radio_write(dev, r | B2056_TX_TX_AMP_DET, 0); b43_radio_write(dev, r | B2056_TX_TSSI_MISC1, 8); b43_radio_write(dev, r | B2056_TX_TSSI_MISC2, 0); b43_radio_write(dev, r | B2056_TX_TSSI_MISC3, 0); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_radio_write(dev, r | B2056_TX_TX_SSI_MASTER, 0x5); if (phy->rev != 5) b43_radio_write(dev, r | B2056_TX_TSSIA, 0x00); if (phy->rev >= 5) b43_radio_write(dev, r | B2056_TX_TSSIG, 0x31); else b43_radio_write(dev, r | B2056_TX_TSSIG, 0x11); b43_radio_write(dev, r | B2056_TX_TX_SSI_MUX, 0xE); } else { b43_radio_write(dev, r | B2056_TX_TX_SSI_MASTER, 0x9); b43_radio_write(dev, r | B2056_TX_TSSIA, 0x31); b43_radio_write(dev, r | B2056_TX_TSSIG, 0x0); b43_radio_write(dev, r | B2056_TX_TX_SSI_MUX, 0xC); } } } } /* * Stop radio and transmit known signal. Then check received signal strength to * get TSSI (Transmit Signal Strength Indicator). * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlIdleTssi */ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; u32 tmp; s32 rssi[4] = { }; /* TODO: check if we can transmit */ if (b43_nphy_ipa(dev)) b43_nphy_ipa_internal_tssi_setup(dev); if (phy->rev >= 7) b43_nphy_rf_control_override_rev7(dev, 0x2000, 0, 3, false, 0); else if (phy->rev >= 3) b43_nphy_rf_control_override(dev, 0x2000, 0, 3, false); b43_nphy_stop_playback(dev); b43_nphy_tx_tone(dev, 0xFA0, 0, false, false); udelay(20); tmp = b43_nphy_poll_rssi(dev, 4, rssi, 1); b43_nphy_stop_playback(dev); b43_nphy_rssi_select(dev, 0, 0); if (phy->rev >= 7) b43_nphy_rf_control_override_rev7(dev, 0x2000, 0, 3, true, 0); else if (phy->rev >= 3) b43_nphy_rf_control_override(dev, 0x2000, 0, 3, true); if (phy->rev >= 3) { nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 24) & 0xFF; nphy->pwr_ctl_info[1].idle_tssi_5g = (tmp >> 8) & 0xFF; } else { nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 16) & 0xFF; nphy->pwr_ctl_info[1].idle_tssi_5g = tmp & 0xFF; } nphy->pwr_ctl_info[0].idle_tssi_2g = (tmp >> 24) & 0xFF; nphy->pwr_ctl_info[1].idle_tssi_2g = (tmp >> 8) & 0xFF; } /* http://bcm-v4.sipsolutions.net/PHY/N/TxPwrLimitToTbl */ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u8 idx, delta; u8 i, stf_mode; for (i = 0; i < 4; i++) nphy->adj_pwr_tbl[i] = nphy->tx_power_offset[i]; for (stf_mode = 0; stf_mode < 4; stf_mode++) { delta = 0; switch (stf_mode) { case 0: if (dev->phy.is_40mhz && dev->phy.rev >= 5) { idx = 68; } else { delta = 1; idx = dev->phy.is_40mhz ? 52 : 4; } break; case 1: idx = dev->phy.is_40mhz ? 76 : 28; break; case 2: idx = dev->phy.is_40mhz ? 84 : 36; break; case 3: idx = dev->phy.is_40mhz ? 92 : 44; break; } for (i = 0; i < 20; i++) { nphy->adj_pwr_tbl[4 + 4 * i + stf_mode] = nphy->tx_power_offset[idx]; if (i == 0) idx += delta; if (i == 14) idx += 1 - delta; if (i == 3 || i == 4 || i == 7 || i == 8 || i == 11 || i == 13) idx += 1; } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; struct ssb_sprom *sprom = dev->dev->bus_sprom; s16 a1[2], b0[2], b1[2]; u8 idle[2]; s8 target[2]; s32 num, den, pwr; u32 regval[64]; u16 freq = dev->phy.channel_freq; u16 tmp; u16 r; /* routing */ u8 i, c; if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { b43_maskset32(dev, B43_MMIO_MACCTL, ~0, 0x200000); b43_read32(dev, B43_MMIO_MACCTL); udelay(1); } if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, true); b43_phy_set(dev, B43_NPHY_TSSIMODE, B43_NPHY_TSSIMODE_EN); if (dev->phy.rev >= 3) b43_phy_mask(dev, B43_NPHY_TXPCTL_CMD, ~B43_NPHY_TXPCTL_CMD_PCTLEN & 0xFFFF); else b43_phy_set(dev, B43_NPHY_TXPCTL_CMD, B43_NPHY_TXPCTL_CMD_PCTLEN); if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) b43_maskset32(dev, B43_MMIO_MACCTL, ~0x200000, 0); if (sprom->revision < 4) { idle[0] = nphy->pwr_ctl_info[0].idle_tssi_2g; idle[1] = nphy->pwr_ctl_info[1].idle_tssi_2g; target[0] = target[1] = 52; a1[0] = a1[1] = -424; b0[0] = b0[1] = 5612; b1[0] = b1[1] = -1393; } else { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { for (c = 0; c < 2; c++) { idle[c] = nphy->pwr_ctl_info[c].idle_tssi_2g; target[c] = sprom->core_pwr_info[c].maxpwr_2g; a1[c] = sprom->core_pwr_info[c].pa_2g[0]; b0[c] = sprom->core_pwr_info[c].pa_2g[1]; b1[c] = sprom->core_pwr_info[c].pa_2g[2]; } } else if (freq >= 4900 && freq < 5100) { for (c = 0; c < 2; c++) { idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; target[c] = sprom->core_pwr_info[c].maxpwr_5gl; a1[c] = sprom->core_pwr_info[c].pa_5gl[0]; b0[c] = sprom->core_pwr_info[c].pa_5gl[1]; b1[c] = sprom->core_pwr_info[c].pa_5gl[2]; } } else if (freq >= 5100 && freq < 5500) { for (c = 0; c < 2; c++) { idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; target[c] = sprom->core_pwr_info[c].maxpwr_5g; a1[c] = sprom->core_pwr_info[c].pa_5g[0]; b0[c] = sprom->core_pwr_info[c].pa_5g[1]; b1[c] = sprom->core_pwr_info[c].pa_5g[2]; } } else if (freq >= 5500) { for (c = 0; c < 2; c++) { idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; target[c] = sprom->core_pwr_info[c].maxpwr_5gh; a1[c] = sprom->core_pwr_info[c].pa_5gh[0]; b0[c] = sprom->core_pwr_info[c].pa_5gh[1]; b1[c] = sprom->core_pwr_info[c].pa_5gh[2]; } } else { idle[0] = nphy->pwr_ctl_info[0].idle_tssi_5g; idle[1] = nphy->pwr_ctl_info[1].idle_tssi_5g; target[0] = target[1] = 52; a1[0] = a1[1] = -424; b0[0] = b0[1] = 5612; b1[0] = b1[1] = -1393; } } /* target[0] = target[1] = nphy->tx_power_max; */ if (dev->phy.rev >= 3) { if (sprom->fem.ghz2.tssipos) b43_phy_set(dev, B43_NPHY_TXPCTL_ITSSI, 0x4000); if (dev->phy.rev >= 7) { for (c = 0; c < 2; c++) { r = c ? 0x190 : 0x170; if (b43_nphy_ipa(dev)) b43_radio_write(dev, r + 0x9, (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ? 0xE : 0xC); } } else { if (b43_nphy_ipa(dev)) { tmp = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; b43_radio_write(dev, B2056_TX0 | B2056_TX_TX_SSI_MUX, tmp); b43_radio_write(dev, B2056_TX1 | B2056_TX_TX_SSI_MUX, tmp); } else { b43_radio_write(dev, B2056_TX0 | B2056_TX_TX_SSI_MUX, 0x11); b43_radio_write(dev, B2056_TX1 | B2056_TX_TX_SSI_MUX, 0x11); } } } if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { b43_maskset32(dev, B43_MMIO_MACCTL, ~0, 0x200000); b43_read32(dev, B43_MMIO_MACCTL); udelay(1); } if (dev->phy.rev >= 7) { b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~B43_NPHY_TXPCTL_CMD_INIT, 0x19); b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, ~B43_NPHY_TXPCTL_INIT_PIDXI1, 0x19); } else { b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~B43_NPHY_TXPCTL_CMD_INIT, 0x40); if (dev->phy.rev > 1) b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, ~B43_NPHY_TXPCTL_INIT_PIDXI1, 0x40); } if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) b43_maskset32(dev, B43_MMIO_MACCTL, ~0x200000, 0); b43_phy_write(dev, B43_NPHY_TXPCTL_N, 0xF0 << B43_NPHY_TXPCTL_N_TSSID_SHIFT | 3 << B43_NPHY_TXPCTL_N_NPTIL2_SHIFT); b43_phy_write(dev, B43_NPHY_TXPCTL_ITSSI, idle[0] << B43_NPHY_TXPCTL_ITSSI_0_SHIFT | idle[1] << B43_NPHY_TXPCTL_ITSSI_1_SHIFT | B43_NPHY_TXPCTL_ITSSI_BINF); b43_phy_write(dev, B43_NPHY_TXPCTL_TPWR, target[0] << B43_NPHY_TXPCTL_TPWR_0_SHIFT | target[1] << B43_NPHY_TXPCTL_TPWR_1_SHIFT); for (c = 0; c < 2; c++) { for (i = 0; i < 64; i++) { num = 8 * (16 * b0[c] + b1[c] * i); den = 32768 + a1[c] * i; pwr = max((4 * num + den / 2) / den, -8); if (dev->phy.rev < 3 && (i <= (31 - idle[c] + 1))) pwr = max(pwr, target[c] + 1); regval[i] = pwr; } b43_ntab_write_bulk(dev, B43_NTAB32(26 + c, 0), 64, regval); } b43_nphy_tx_prepare_adjusted_power_table(dev); /* b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, nphy->adj_pwr_tbl); b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, nphy->adj_pwr_tbl); */ if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, false); } static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; const u32 *table = NULL; u32 rfpwr_offset; u8 pga_gain; int i; table = b43_nphy_get_tx_gain_table(dev); b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table); b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table); if (phy->rev >= 3) { #if 0 nphy->gmval = (table[0] >> 16) & 0x7000; #endif for (i = 0; i < 128; i++) { pga_gain = (table[i] >> 24) & 0xF; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain]; else rfpwr_offset = 0; /* FIXME */ b43_ntab_write(dev, B43_NTAB32(26, 576 + i), rfpwr_offset); b43_ntab_write(dev, B43_NTAB32(27, 576 + i), rfpwr_offset); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PA%20override */ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) { struct b43_phy_n *nphy = dev->phy.n; enum ieee80211_band band; u16 tmp; if (!enable) { nphy->rfctrl_intc1_save = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); nphy->rfctrl_intc2_save = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); band = b43_current_band(dev->wl); if (dev->phy.rev >= 3) { if (band == IEEE80211_BAND_5GHZ) tmp = 0x600; else tmp = 0x480; } else { if (band == IEEE80211_BAND_5GHZ) tmp = 0x180; else tmp = 0x120; } b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); } else { b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, nphy->rfctrl_intc1_save); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, nphy->rfctrl_intc2_save); } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */ static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev) { u16 tmp; if (dev->phy.rev >= 3) { if (b43_nphy_ipa(dev)) { tmp = 4; b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2, (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); } tmp = 1; b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2, (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqEst */ static void b43_nphy_rx_iq_est(struct b43_wldev *dev, struct nphy_iq_est *est, u16 samps, u8 time, bool wait) { int i; u16 tmp; b43_phy_write(dev, B43_NPHY_IQEST_SAMCNT, samps); b43_phy_maskset(dev, B43_NPHY_IQEST_WT, ~B43_NPHY_IQEST_WT_VAL, time); if (wait) b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_MODE); else b43_phy_mask(dev, B43_NPHY_IQEST_CMD, ~B43_NPHY_IQEST_CMD_MODE); b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_START); for (i = 1000; i; i--) { tmp = b43_phy_read(dev, B43_NPHY_IQEST_CMD); if (!(tmp & B43_NPHY_IQEST_CMD_START)) { est->i0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI0) << 16) | b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO0); est->q0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI0) << 16) | b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO0); est->iq0_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI0) << 16) | b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO0); est->i1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI1) << 16) | b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO1); est->q1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI1) << 16) | b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO1); est->iq1_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI1) << 16) | b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO1); return; } udelay(10); } memset(est, 0, sizeof(*est)); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqCoeffs */ static void b43_nphy_rx_iq_coeffs(struct b43_wldev *dev, bool write, struct b43_phy_n_iq_comp *pcomp) { if (write) { b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPA0, pcomp->a0); b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPB0, pcomp->b0); b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPA1, pcomp->a1); b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPB1, pcomp->b1); } else { pcomp->a0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPA0); pcomp->b0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPB0); pcomp->a1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPA1); pcomp->b1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPB1); } } #if 0 /* Ready but not used anywhere */ /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhyCleanup */ static void b43_nphy_rx_cal_phy_cleanup(struct b43_wldev *dev, u8 core) { u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; b43_phy_write(dev, B43_NPHY_RFSEQCA, regs[0]); if (core == 0) { b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[1]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); } else { b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); } b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[3]); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[4]); b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO1, regs[5]); b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO2, regs[6]); b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, regs[7]); b43_phy_write(dev, B43_NPHY_RFCTL_OVER, regs[8]); b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhySetup */ static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core) { u8 rxval, txval; u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; regs[0] = b43_phy_read(dev, B43_NPHY_RFSEQCA); if (core == 0) { regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); } else { regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); } regs[3] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); regs[4] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO1); regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO2); regs[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S1); regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_OVER); regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXDIS & 0xFFFF, ((1 - core) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, ((1 - core) << B43_NPHY_RFSEQCA_TXEN_SHIFT)); b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN, (core << B43_NPHY_RFSEQCA_RXEN_SHIFT)); b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXDIS, (core << B43_NPHY_RFSEQCA_TXDIS_SHIFT)); if (core == 0) { b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x0007); b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0007); } else { b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x0007); b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0007); } b43_nphy_rf_control_intc_override(dev, 2, 0, 3); b43_nphy_rf_control_override(dev, 8, 0, 3, false); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); if (core == 0) { rxval = 1; txval = 8; } else { rxval = 4; txval = 2; } b43_nphy_rf_control_intc_override(dev, 1, rxval, (core + 1)); b43_nphy_rf_control_intc_override(dev, 1, txval, (2 - core)); } #endif /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */ static void b43_nphy_calc_rx_iq_comp(struct b43_wldev *dev, u8 mask) { int i; s32 iq; u32 ii; u32 qq; int iq_nbits, qq_nbits; int arsh, brsh; u16 tmp, a, b; struct nphy_iq_est est; struct b43_phy_n_iq_comp old; struct b43_phy_n_iq_comp new = { }; bool error = false; if (mask == 0) return; b43_nphy_rx_iq_coeffs(dev, false, &old); b43_nphy_rx_iq_coeffs(dev, true, &new); b43_nphy_rx_iq_est(dev, &est, 0x4000, 32, false); new = old; for (i = 0; i < 2; i++) { if (i == 0 && (mask & 1)) { iq = est.iq0_prod; ii = est.i0_pwr; qq = est.q0_pwr; } else if (i == 1 && (mask & 2)) { iq = est.iq1_prod; ii = est.i1_pwr; qq = est.q1_pwr; } else { continue; } if (ii + qq < 2) { error = true; break; } iq_nbits = fls(abs(iq)); qq_nbits = fls(qq); arsh = iq_nbits - 20; if (arsh >= 0) { a = -((iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); tmp = ii >> arsh; } else { a = -((iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); tmp = ii << -arsh; } if (tmp == 0) { error = true; break; } a /= tmp; brsh = qq_nbits - 11; if (brsh >= 0) { b = (qq << (31 - qq_nbits)); tmp = ii >> brsh; } else { b = (qq << (31 - qq_nbits)); tmp = ii << -brsh; } if (tmp == 0) { error = true; break; } b = int_sqrt(b / tmp - a * a) - (1 << 10); if (i == 0 && (mask & 0x1)) { if (dev->phy.rev >= 3) { new.a0 = a & 0x3FF; new.b0 = b & 0x3FF; } else { new.a0 = b & 0x3FF; new.b0 = a & 0x3FF; } } else if (i == 1 && (mask & 0x2)) { if (dev->phy.rev >= 3) { new.a1 = a & 0x3FF; new.b1 = b & 0x3FF; } else { new.a1 = b & 0x3FF; new.b1 = a & 0x3FF; } } } if (error) new = old; b43_nphy_rx_iq_coeffs(dev, true, &new); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxIqWar */ static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev) { u16 array[4]; b43_ntab_read_bulk(dev, B43_NTAB16(0xF, 0x50), 4, array); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW2, array[2]); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW3, array[3]); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SpurWar */ static void b43_nphy_spur_workaround(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u8 channel = dev->phy.channel; int tone[2] = { 57, 58 }; u32 noise[2] = { 0x3FF, 0x3FF }; B43_WARN_ON(dev->phy.rev < 3); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); if (nphy->gband_spurwar_en) { /* TODO: N PHY Adjust Analog Pfbw (7) */ if (channel == 11 && dev->phy.is_40mhz) ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/ else ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ /* TODO: N PHY Adjust CRS Min Power (0x1E) */ } if (nphy->aband_spurwar_en) { if (channel == 54) { tone[0] = 0x20; noise[0] = 0x25F; } else if (channel == 38 || channel == 102 || channel == 118) { if (0 /* FIXME */) { tone[0] = 0x20; noise[0] = 0x21F; } else { tone[0] = 0; noise[0] = 0; } } else if (channel == 134) { tone[0] = 0x20; noise[0] = 0x21F; } else if (channel == 151) { tone[0] = 0x10; noise[0] = 0x23F; } else if (channel == 153 || channel == 161) { tone[0] = 0x30; noise[0] = 0x23F; } else { tone[0] = 0; noise[0] = 0; } if (!tone[0] && !noise[0]) ; /* TODO: N PHY Adjust Min Noise Var(1, tone, noise)*/ else ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ } if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlCoefSetup */ static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; int i, j; u32 tmp; u32 cur_real, cur_imag, real_part, imag_part; u16 buffer[7]; if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, true); b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); for (i = 0; i < 2; i++) { tmp = ((buffer[i * 2] & 0x3FF) << 10) | (buffer[i * 2 + 1] & 0x3FF); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, (((i + 26) << 10) | 320)); for (j = 0; j < 128; j++) { b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, ((tmp >> 16) & 0xFFFF)); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, (tmp & 0xFFFF)); } } for (i = 0; i < 2; i++) { tmp = buffer[5 + i]; real_part = (tmp >> 8) & 0xFF; imag_part = (tmp & 0xFF); b43_phy_write(dev, B43_NPHY_TABLE_ADDR, (((i + 26) << 10) | 448)); if (dev->phy.rev >= 3) { cur_real = real_part; cur_imag = imag_part; tmp = ((cur_real & 0xFF) << 8) | (cur_imag & 0xFF); } for (j = 0; j < 128; j++) { if (dev->phy.rev < 3) { cur_real = (real_part * loscale[j] + 128) >> 8; cur_imag = (imag_part * loscale[j] + 128) >> 8; tmp = ((cur_real & 0xFF) << 8) | (cur_imag & 0xFF); } b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, ((tmp >> 16) & 0xFFFF)); b43_phy_write(dev, B43_NPHY_TABLE_DATALO, (tmp & 0xFFFF)); } } if (dev->phy.rev >= 3) { b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXPWR_INDX0, 0xFFFF); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXPWR_INDX1, 0xFFFF); } if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, false); } /* * Restore RSSI Calibration * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreRssiCal */ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u16 *rssical_radio_regs = NULL; u16 *rssical_phy_regs = NULL; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if (!nphy->rssical_chanspec_2G.center_freq) return; rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; } else { if (!nphy->rssical_chanspec_5G.center_freq) return; rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; } /* TODO use some definitions */ b43_radio_maskset(dev, 0x602B, 0xE3, rssical_radio_regs[0]); b43_radio_maskset(dev, 0x702B, 0xE3, rssical_radio_regs[1]); b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, rssical_phy_regs[0]); b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, rssical_phy_regs[1]); b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, rssical_phy_regs[2]); b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, rssical_phy_regs[3]); b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, rssical_phy_regs[4]); b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, rssical_phy_regs[5]); b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, rssical_phy_regs[6]); b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, rssical_phy_regs[7]); b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, rssical_phy_regs[8]); b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, rssical_phy_regs[9]); b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, rssical_phy_regs[10]); b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */ static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u16 *save = nphy->tx_rx_cal_radio_saveregs; u16 tmp; u8 offset, i; if (dev->phy.rev >= 3) { for (i = 0; i < 2; i++) { tmp = (i == 0) ? 0x2000 : 0x3000; offset = i * 11; save[offset + 0] = b43_radio_read16(dev, B2055_CAL_RVARCTL); save[offset + 1] = b43_radio_read16(dev, B2055_CAL_LPOCTL); save[offset + 2] = b43_radio_read16(dev, B2055_CAL_TS); save[offset + 3] = b43_radio_read16(dev, B2055_CAL_RCCALRTS); save[offset + 4] = b43_radio_read16(dev, B2055_CAL_RCALRTS); save[offset + 5] = b43_radio_read16(dev, B2055_PADDRV); save[offset + 6] = b43_radio_read16(dev, B2055_XOCTL1); save[offset + 7] = b43_radio_read16(dev, B2055_XOCTL2); save[offset + 8] = b43_radio_read16(dev, B2055_XOREGUL); save[offset + 9] = b43_radio_read16(dev, B2055_XOMISC); save[offset + 10] = b43_radio_read16(dev, B2055_PLL_LFC1); if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { b43_radio_write16(dev, tmp | B2055_CAL_RVARCTL, 0x0A); b43_radio_write16(dev, tmp | B2055_CAL_LPOCTL, 0x40); b43_radio_write16(dev, tmp | B2055_CAL_TS, 0x55); b43_radio_write16(dev, tmp | B2055_CAL_RCCALRTS, 0); b43_radio_write16(dev, tmp | B2055_CAL_RCALRTS, 0); if (nphy->ipa5g_on) { b43_radio_write16(dev, tmp | B2055_PADDRV, 4); b43_radio_write16(dev, tmp | B2055_XOCTL1, 1); } else { b43_radio_write16(dev, tmp | B2055_PADDRV, 0); b43_radio_write16(dev, tmp | B2055_XOCTL1, 0x2F); } b43_radio_write16(dev, tmp | B2055_XOCTL2, 0); } else { b43_radio_write16(dev, tmp | B2055_CAL_RVARCTL, 0x06); b43_radio_write16(dev, tmp | B2055_CAL_LPOCTL, 0x40); b43_radio_write16(dev, tmp | B2055_CAL_TS, 0x55); b43_radio_write16(dev, tmp | B2055_CAL_RCCALRTS, 0); b43_radio_write16(dev, tmp | B2055_CAL_RCALRTS, 0); b43_radio_write16(dev, tmp | B2055_XOCTL1, 0); if (nphy->ipa2g_on) { b43_radio_write16(dev, tmp | B2055_PADDRV, 6); b43_radio_write16(dev, tmp | B2055_XOCTL2, (dev->phy.rev < 5) ? 0x11 : 0x01); } else { b43_radio_write16(dev, tmp | B2055_PADDRV, 0); b43_radio_write16(dev, tmp | B2055_XOCTL2, 0); } } b43_radio_write16(dev, tmp | B2055_XOREGUL, 0); b43_radio_write16(dev, tmp | B2055_XOMISC, 0); b43_radio_write16(dev, tmp | B2055_PLL_LFC1, 0); } } else { save[0] = b43_radio_read16(dev, B2055_C1_TX_RF_IQCAL1); b43_radio_write16(dev, B2055_C1_TX_RF_IQCAL1, 0x29); save[1] = b43_radio_read16(dev, B2055_C1_TX_RF_IQCAL2); b43_radio_write16(dev, B2055_C1_TX_RF_IQCAL2, 0x54); save[2] = b43_radio_read16(dev, B2055_C2_TX_RF_IQCAL1); b43_radio_write16(dev, B2055_C2_TX_RF_IQCAL1, 0x29); save[3] = b43_radio_read16(dev, B2055_C2_TX_RF_IQCAL2); b43_radio_write16(dev, B2055_C2_TX_RF_IQCAL2, 0x54); save[3] = b43_radio_read16(dev, B2055_C1_PWRDET_RXTX); save[4] = b43_radio_read16(dev, B2055_C2_PWRDET_RXTX); if (!(b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ)) { b43_radio_write16(dev, B2055_C1_PWRDET_RXTX, 0x04); b43_radio_write16(dev, B2055_C2_PWRDET_RXTX, 0x04); } else { b43_radio_write16(dev, B2055_C1_PWRDET_RXTX, 0x20); b43_radio_write16(dev, B2055_C2_PWRDET_RXTX, 0x20); } if (dev->phy.rev < 2) { b43_radio_set(dev, B2055_C1_TX_BB_MXGM, 0x20); b43_radio_set(dev, B2055_C2_TX_BB_MXGM, 0x20); } else { b43_radio_mask(dev, B2055_C1_TX_BB_MXGM, ~0x20); b43_radio_mask(dev, B2055_C2_TX_BB_MXGM, ~0x20); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/UpdateTxCalLadder */ static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core) { struct b43_phy_n *nphy = dev->phy.n; int i; u16 scale, entry; u16 tmp = nphy->txcal_bbmult; if (core == 0) tmp >>= 8; tmp &= 0xff; for (i = 0; i < 18; i++) { scale = (ladder_lo[i].percent * tmp) / 100; entry = ((scale & 0xFF) << 8) | ladder_lo[i].g_env; b43_ntab_write(dev, B43_NTAB16(15, i), entry); scale = (ladder_iq[i].percent * tmp) / 100; entry = ((scale & 0xFF) << 8) | ladder_iq[i].g_env; b43_ntab_write(dev, B43_NTAB16(15, i + 32), entry); } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ExtPaSetTxDigiFilts */ static void b43_nphy_ext_pa_set_tx_dig_filters(struct b43_wldev *dev) { int i; for (i = 0; i < 15; i++) b43_phy_write(dev, B43_PHY_N(0x2C5 + i), tbl_tx_filter_coef_rev4[2][i]); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IpaSetTxDigiFilts */ static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev) { int i, j; /* B43_NPHY_TXF_20CO_S0A1, B43_NPHY_TXF_40CO_S0A1, unknown */ static const u16 offset[] = { 0x186, 0x195, 0x2C5 }; for (i = 0; i < 3; i++) for (j = 0; j < 15; j++) b43_phy_write(dev, B43_PHY_N(offset[i] + j), tbl_tx_filter_coef_rev4[i][j]); if (dev->phy.is_40mhz) { for (j = 0; j < 15; j++) b43_phy_write(dev, B43_PHY_N(offset[0] + j), tbl_tx_filter_coef_rev4[3][j]); } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { for (j = 0; j < 15; j++) b43_phy_write(dev, B43_PHY_N(offset[0] + j), tbl_tx_filter_coef_rev4[5][j]); } if (dev->phy.channel == 14) for (j = 0; j < 15; j++) b43_phy_write(dev, B43_PHY_N(offset[0] + j), tbl_tx_filter_coef_rev4[6][j]); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u16 curr_gain[2]; struct nphy_txgains target; const u32 *table = NULL; if (!nphy->txpwrctrl) { int i; if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, true); b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, curr_gain); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, false); for (i = 0; i < 2; ++i) { if (dev->phy.rev >= 3) { target.ipa[i] = curr_gain[i] & 0x000F; target.pad[i] = (curr_gain[i] & 0x00F0) >> 4; target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; } else { target.ipa[i] = curr_gain[i] & 0x0003; target.pad[i] = (curr_gain[i] & 0x000C) >> 2; target.pga[i] = (curr_gain[i] & 0x0070) >> 4; target.txgm[i] = (curr_gain[i] & 0x0380) >> 7; } } } else { int i; u16 index[2]; index[0] = (b43_phy_read(dev, B43_NPHY_C1_TXPCTL_STAT) & B43_NPHY_TXPCTL_STAT_BIDX) >> B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; index[1] = (b43_phy_read(dev, B43_NPHY_C2_TXPCTL_STAT) & B43_NPHY_TXPCTL_STAT_BIDX) >> B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; for (i = 0; i < 2; ++i) { table = b43_nphy_get_tx_gain_table(dev); if (dev->phy.rev >= 3) { target.ipa[i] = (table[index[i]] >> 16) & 0xF; target.pad[i] = (table[index[i]] >> 20) & 0xF; target.pga[i] = (table[index[i]] >> 24) & 0xF; target.txgm[i] = (table[index[i]] >> 28) & 0xF; } else { target.ipa[i] = (table[index[i]] >> 16) & 0x3; target.pad[i] = (table[index[i]] >> 18) & 0x3; target.pga[i] = (table[index[i]] >> 20) & 0x7; target.txgm[i] = (table[index[i]] >> 23) & 0x7; } } } return target; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhyCleanup */ static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev) { u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; if (dev->phy.rev >= 3) { b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[0]); b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[3]); b43_phy_write(dev, B43_NPHY_BBCFG, regs[4]); b43_ntab_write(dev, B43_NTAB16(8, 3), regs[5]); b43_ntab_write(dev, B43_NTAB16(8, 19), regs[6]); b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[7]); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[8]); b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); b43_nphy_reset_cca(dev); } else { b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, regs[0]); b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, regs[1]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); b43_ntab_write(dev, B43_NTAB16(8, 2), regs[3]); b43_ntab_write(dev, B43_NTAB16(8, 18), regs[4]); b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[5]); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[6]); } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhySetup */ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) { u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; u16 tmp; regs[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); if (dev->phy.rev >= 3) { b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0xF0FF, 0x0A00); b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0xF0FF, 0x0A00); tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); regs[2] = tmp; b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, tmp | 0x0600); tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); regs[3] = tmp; b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x0600); regs[4] = b43_phy_read(dev, B43_NPHY_BBCFG); b43_phy_mask(dev, B43_NPHY_BBCFG, ~B43_NPHY_BBCFG_RSTRX & 0xFFFF); tmp = b43_ntab_read(dev, B43_NTAB16(8, 3)); regs[5] = tmp; b43_ntab_write(dev, B43_NTAB16(8, 3), 0); tmp = b43_ntab_read(dev, B43_NTAB16(8, 19)); regs[6] = tmp; b43_ntab_write(dev, B43_NTAB16(8, 19), 0); regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); b43_nphy_rf_control_intc_override(dev, 2, 1, 3); b43_nphy_rf_control_intc_override(dev, 1, 2, 1); b43_nphy_rf_control_intc_override(dev, 1, 8, 2); regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); } else { b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, 0xA000); b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, 0xA000); tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); regs[2] = tmp; b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x3000); tmp = b43_ntab_read(dev, B43_NTAB16(8, 2)); regs[3] = tmp; tmp |= 0x2000; b43_ntab_write(dev, B43_NTAB16(8, 2), tmp); tmp = b43_ntab_read(dev, B43_NTAB16(8, 18)); regs[4] = tmp; tmp |= 0x2000; b43_ntab_write(dev, B43_NTAB16(8, 18), tmp); regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) tmp = 0x0180; else tmp = 0x0120; b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */ static void b43_nphy_save_cal(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; u16 *txcal_radio_regs = NULL; struct b43_chanspec *iqcal_chanspec; u16 *table = NULL; if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; iqcal_chanspec = &nphy->iqcal_chanspec_2G; table = nphy->cal_cache.txcal_coeffs_2G; } else { rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; iqcal_chanspec = &nphy->iqcal_chanspec_5G; table = nphy->cal_cache.txcal_coeffs_5G; } b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs); /* TODO use some definitions */ if (dev->phy.rev >= 3) { txcal_radio_regs[0] = b43_radio_read(dev, 0x2021); txcal_radio_regs[1] = b43_radio_read(dev, 0x2022); txcal_radio_regs[2] = b43_radio_read(dev, 0x3021); txcal_radio_regs[3] = b43_radio_read(dev, 0x3022); txcal_radio_regs[4] = b43_radio_read(dev, 0x2023); txcal_radio_regs[5] = b43_radio_read(dev, 0x2024); txcal_radio_regs[6] = b43_radio_read(dev, 0x3023); txcal_radio_regs[7] = b43_radio_read(dev, 0x3024); } else { txcal_radio_regs[0] = b43_radio_read(dev, 0x8B); txcal_radio_regs[1] = b43_radio_read(dev, 0xBA); txcal_radio_regs[2] = b43_radio_read(dev, 0x8D); txcal_radio_regs[3] = b43_radio_read(dev, 0xBC); } iqcal_chanspec->center_freq = dev->phy.channel_freq; iqcal_chanspec->channel_type = dev->phy.channel_type; b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */ static void b43_nphy_restore_cal(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u16 coef[4]; u16 *loft = NULL; u16 *table = NULL; int i; u16 *txcal_radio_regs = NULL; struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if (!nphy->iqcal_chanspec_2G.center_freq) return; table = nphy->cal_cache.txcal_coeffs_2G; loft = &nphy->cal_cache.txcal_coeffs_2G[5]; } else { if (!nphy->iqcal_chanspec_5G.center_freq) return; table = nphy->cal_cache.txcal_coeffs_5G; loft = &nphy->cal_cache.txcal_coeffs_5G[5]; } b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, table); for (i = 0; i < 4; i++) { if (dev->phy.rev >= 3) table[i] = coef[i]; else coef[i] = 0; } b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, coef); b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, loft); b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, loft); if (dev->phy.rev < 2) b43_nphy_tx_iq_workaround(dev); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; } else { txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; } /* TODO use some definitions */ if (dev->phy.rev >= 3) { b43_radio_write(dev, 0x2021, txcal_radio_regs[0]); b43_radio_write(dev, 0x2022, txcal_radio_regs[1]); b43_radio_write(dev, 0x3021, txcal_radio_regs[2]); b43_radio_write(dev, 0x3022, txcal_radio_regs[3]); b43_radio_write(dev, 0x2023, txcal_radio_regs[4]); b43_radio_write(dev, 0x2024, txcal_radio_regs[5]); b43_radio_write(dev, 0x3023, txcal_radio_regs[6]); b43_radio_write(dev, 0x3024, txcal_radio_regs[7]); } else { b43_radio_write(dev, 0x8B, txcal_radio_regs[0]); b43_radio_write(dev, 0xBA, txcal_radio_regs[1]); b43_radio_write(dev, 0x8D, txcal_radio_regs[2]); b43_radio_write(dev, 0xBC, txcal_radio_regs[3]); } b43_nphy_rx_iq_coeffs(dev, true, rxcal_coeffs); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalTxIqlo */ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, struct nphy_txgains target, bool full, bool mphase) { struct b43_phy_n *nphy = dev->phy.n; int i; int error = 0; int freq; bool avoid = false; u8 length; u16 tmp, core, type, count, max, numb, last = 0, cmd; const u16 *table; bool phy6or5x; u16 buffer[11]; u16 diq_start = 0; u16 save[2]; u16 gain[2]; struct nphy_iqcal_params params[2]; bool updated[2] = { }; b43_nphy_stay_in_carrier_search(dev, true); if (dev->phy.rev >= 4) { avoid = nphy->hang_avoid; nphy->hang_avoid = false; } b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, save); for (i = 0; i < 2; i++) { b43_nphy_iq_cal_gain_params(dev, i, target, ¶ms[i]); gain[i] = params[i].cal_gain; } b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain); b43_nphy_tx_cal_radio_setup(dev); b43_nphy_tx_cal_phy_setup(dev); phy6or5x = dev->phy.rev >= 6 || (dev->phy.rev == 5 && nphy->ipa2g_on && b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ); if (phy6or5x) { if (dev->phy.is_40mhz) { b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, tbl_tx_iqlo_cal_loft_ladder_40); b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, tbl_tx_iqlo_cal_iqimb_ladder_40); } else { b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, tbl_tx_iqlo_cal_loft_ladder_20); b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, tbl_tx_iqlo_cal_iqimb_ladder_20); } } b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); if (!dev->phy.is_40mhz) freq = 2500; else freq = 5000; if (nphy->mphase_cal_phase_id > 2) b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8, 0xFFFF, 0, true, false); else error = b43_nphy_tx_tone(dev, freq, 250, true, false); if (error == 0) { if (nphy->mphase_cal_phase_id > 2) { table = nphy->mphase_txcal_bestcoeffs; length = 11; if (dev->phy.rev < 3) length -= 2; } else { if (!full && nphy->txiqlocal_coeffsvalid) { table = nphy->txiqlocal_bestc; length = 11; if (dev->phy.rev < 3) length -= 2; } else { full = true; if (dev->phy.rev >= 3) { table = tbl_tx_iqlo_cal_startcoefs_nphyrev3; length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3; } else { table = tbl_tx_iqlo_cal_startcoefs; length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS; } } } b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, table); if (full) { if (dev->phy.rev >= 3) max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3; else max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL; } else { if (dev->phy.rev >= 3) max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3; else max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL; } if (mphase) { count = nphy->mphase_txcal_cmdidx; numb = min(max, (u16)(count + nphy->mphase_txcal_numcmds)); } else { count = 0; numb = max; } for (; count < numb; count++) { if (full) { if (dev->phy.rev >= 3) cmd = tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[count]; else cmd = tbl_tx_iqlo_cal_cmds_fullcal[count]; } else { if (dev->phy.rev >= 3) cmd = tbl_tx_iqlo_cal_cmds_recal_nphyrev3[count]; else cmd = tbl_tx_iqlo_cal_cmds_recal[count]; } core = (cmd & 0x3000) >> 12; type = (cmd & 0x0F00) >> 8; if (phy6or5x && updated[core] == 0) { b43_nphy_update_tx_cal_ladder(dev, core); updated[core] = true; } tmp = (params[core].ncorr[type] << 8) | 0x66; b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDNNUM, tmp); if (type == 1 || type == 3 || type == 4) { buffer[0] = b43_ntab_read(dev, B43_NTAB16(15, 69 + core)); diq_start = buffer[0]; buffer[0] = 0; b43_ntab_write(dev, B43_NTAB16(15, 69 + core), 0); } b43_phy_write(dev, B43_NPHY_IQLOCAL_CMD, cmd); for (i = 0; i < 2000; i++) { tmp = b43_phy_read(dev, B43_NPHY_IQLOCAL_CMD); if (tmp & 0xC000) break; udelay(10); } b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, buffer); b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, buffer); if (type == 1 || type == 3 || type == 4) buffer[0] = diq_start; } if (mphase) nphy->mphase_txcal_cmdidx = (numb >= max) ? 0 : numb; last = (dev->phy.rev < 3) ? 6 : 7; if (!mphase || nphy->mphase_cal_phase_id == last) { b43_ntab_write_bulk(dev, B43_NTAB16(15, 96), 4, buffer); b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 4, buffer); if (dev->phy.rev < 3) { buffer[0] = 0; buffer[1] = 0; buffer[2] = 0; buffer[3] = 0; } b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, buffer); b43_ntab_read_bulk(dev, B43_NTAB16(15, 101), 2, buffer); b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, buffer); b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, buffer); length = 11; if (dev->phy.rev < 3) length -= 2; b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, nphy->txiqlocal_bestc); nphy->txiqlocal_coeffsvalid = true; nphy->txiqlocal_chanspec.center_freq = dev->phy.channel_freq; nphy->txiqlocal_chanspec.channel_type = dev->phy.channel_type; } else { length = 11; if (dev->phy.rev < 3) length -= 2; b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, nphy->mphase_txcal_bestcoeffs); } b43_nphy_stop_playback(dev); b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0); } b43_nphy_tx_cal_phy_cleanup(dev); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, save); if (dev->phy.rev < 2 && (!mphase || nphy->mphase_cal_phase_id == last)) b43_nphy_tx_iq_workaround(dev); if (dev->phy.rev >= 4) nphy->hang_avoid = avoid; b43_nphy_stay_in_carrier_search(dev, false); return error; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ReapplyTxCalCoeffs */ static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev) { struct b43_phy_n *nphy = dev->phy.n; u8 i; u16 buffer[7]; bool equal = true; if (!nphy->txiqlocal_coeffsvalid || nphy->txiqlocal_chanspec.center_freq != dev->phy.channel_freq || nphy->txiqlocal_chanspec.channel_type != dev->phy.channel_type) return; b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); for (i = 0; i < 4; i++) { if (buffer[i] != nphy->txiqlocal_bestc[i]) { equal = false; break; } } if (!equal) { b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, nphy->txiqlocal_bestc); for (i = 0; i < 4; i++) buffer[i] = 0; b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, buffer); b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, &nphy->txiqlocal_bestc[5]); b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, &nphy->txiqlocal_bestc[5]); } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev, struct nphy_txgains target, u8 type, bool debug) { struct b43_phy_n *nphy = dev->phy.n; int i, j, index; u8 rfctl[2]; u8 afectl_core; u16 tmp[6]; u16 uninitialized_var(cur_hpf1), uninitialized_var(cur_hpf2), cur_lna; u32 real, imag; enum ieee80211_band band; u8 use; u16 cur_hpf; u16 lna[3] = { 3, 3, 1 }; u16 hpf1[3] = { 7, 2, 0 }; u16 hpf2[3] = { 2, 0, 0 }; u32 power[3] = { }; u16 gain_save[2]; u16 cal_gain[2]; struct nphy_iqcal_params cal_params[2]; struct nphy_iq_est est; int ret = 0; bool playtone = true; int desired = 13; b43_nphy_stay_in_carrier_search(dev, 1); if (dev->phy.rev < 2) b43_nphy_reapply_tx_cal_coeffs(dev); b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); for (i = 0; i < 2; i++) { b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]); cal_gain[i] = cal_params[i].cal_gain; } b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, cal_gain); for (i = 0; i < 2; i++) { if (i == 0) { rfctl[0] = B43_NPHY_RFCTL_INTC1; rfctl[1] = B43_NPHY_RFCTL_INTC2; afectl_core = B43_NPHY_AFECTL_C1; } else { rfctl[0] = B43_NPHY_RFCTL_INTC2; rfctl[1] = B43_NPHY_RFCTL_INTC1; afectl_core = B43_NPHY_AFECTL_C2; } tmp[1] = b43_phy_read(dev, B43_NPHY_RFSEQCA); tmp[2] = b43_phy_read(dev, afectl_core); tmp[3] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); tmp[4] = b43_phy_read(dev, rfctl[0]); tmp[5] = b43_phy_read(dev, rfctl[1]); b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXDIS & 0xFFFF, ((1 - i) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, (1 - i)); b43_phy_set(dev, afectl_core, 0x0006); b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0006); band = b43_current_band(dev->wl); if (nphy->rxcalparams & 0xFF000000) { if (band == IEEE80211_BAND_5GHZ) b43_phy_write(dev, rfctl[0], 0x140); else b43_phy_write(dev, rfctl[0], 0x110); } else { if (band == IEEE80211_BAND_5GHZ) b43_phy_write(dev, rfctl[0], 0x180); else b43_phy_write(dev, rfctl[0], 0x120); } if (band == IEEE80211_BAND_5GHZ) b43_phy_write(dev, rfctl[1], 0x148); else b43_phy_write(dev, rfctl[1], 0x114); if (nphy->rxcalparams & 0x10000) { b43_radio_maskset(dev, B2055_C1_GENSPARE2, 0xFC, (i + 1)); b43_radio_maskset(dev, B2055_C2_GENSPARE2, 0xFC, (2 - i)); } for (j = 0; j < 4; j++) { if (j < 3) { cur_lna = lna[j]; cur_hpf1 = hpf1[j]; cur_hpf2 = hpf2[j]; } else { if (power[1] > 10000) { use = 1; cur_hpf = cur_hpf1; index = 2; } else { if (power[0] > 10000) { use = 1; cur_hpf = cur_hpf1; index = 1; } else { index = 0; use = 2; cur_hpf = cur_hpf2; } } cur_lna = lna[index]; cur_hpf1 = hpf1[index]; cur_hpf2 = hpf2[index]; cur_hpf += desired - hweight32(power[index]); cur_hpf = clamp_val(cur_hpf, 0, 10); if (use == 1) cur_hpf1 = cur_hpf; else cur_hpf2 = cur_hpf; } tmp[0] = ((cur_hpf2 << 8) | (cur_hpf1 << 4) | (cur_lna << 2)); b43_nphy_rf_control_override(dev, 0x400, tmp[0], 3, false); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); b43_nphy_stop_playback(dev); if (playtone) { ret = b43_nphy_tx_tone(dev, 4000, (nphy->rxcalparams & 0xFFFF), false, false); playtone = false; } else { b43_nphy_run_samples(dev, 160, 0xFFFF, 0, false, false); } if (ret == 0) { if (j < 3) { b43_nphy_rx_iq_est(dev, &est, 1024, 32, false); if (i == 0) { real = est.i0_pwr; imag = est.q0_pwr; } else { real = est.i1_pwr; imag = est.q1_pwr; } power[i] = ((real + imag) / 1024) + 1; } else { b43_nphy_calc_rx_iq_comp(dev, 1 << i); } b43_nphy_stop_playback(dev); } if (ret != 0) break; } b43_radio_mask(dev, B2055_C1_GENSPARE2, 0xFC); b43_radio_mask(dev, B2055_C2_GENSPARE2, 0xFC); b43_phy_write(dev, rfctl[1], tmp[5]); b43_phy_write(dev, rfctl[0], tmp[4]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp[3]); b43_phy_write(dev, afectl_core, tmp[2]); b43_phy_write(dev, B43_NPHY_RFSEQCA, tmp[1]); if (ret != 0) break; } b43_nphy_rf_control_override(dev, 0x400, 0, 3, true); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); b43_nphy_stay_in_carrier_search(dev, 0); return ret; } static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev, struct nphy_txgains target, u8 type, bool debug) { return -1; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIq */ static int b43_nphy_cal_rx_iq(struct b43_wldev *dev, struct nphy_txgains target, u8 type, bool debug) { if (dev->phy.rev >= 3) return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug); else return b43_nphy_rev2_cal_rx_iq(dev, target, type, debug); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCoreSetState */ static void b43_nphy_set_rx_core_state(struct b43_wldev *dev, u8 mask) { struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = phy->n; /* u16 buf[16]; it's rev3+ */ nphy->phyrxchain = mask; if (0 /* FIXME clk */) return; b43_mac_suspend(dev); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, true); b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN, (mask & 0x3) << B43_NPHY_RFSEQCA_RXEN_SHIFT); if ((mask & 0x3) != 0x3) { b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 1); if (dev->phy.rev >= 3) { /* TODO */ } } else { b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 0x1E); if (dev->phy.rev >= 3) { /* TODO */ } } b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, false); b43_mac_enable(dev); } /************************************************** * N-PHY init **************************************************/ /* * Upload the N-PHY tables. * http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */ static void b43_nphy_tables_init(struct b43_wldev *dev) { if (dev->phy.rev < 3) b43_nphy_rev0_1_2_tables_init(dev); else b43_nphy_rev3plus_tables_init(dev); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MIMOConfig */ static void b43_nphy_update_mimo_config(struct b43_wldev *dev, s32 preamble) { u16 mimocfg = b43_phy_read(dev, B43_NPHY_MIMOCFG); mimocfg |= B43_NPHY_MIMOCFG_AUTO; if (preamble == 1) mimocfg |= B43_NPHY_MIMOCFG_GFMIX; else mimocfg &= ~B43_NPHY_MIMOCFG_GFMIX; b43_phy_write(dev, B43_NPHY_MIMOCFG, mimocfg); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BPHYInit */ static void b43_nphy_bphy_init(struct b43_wldev *dev) { unsigned int i; u16 val; val = 0x1E1F; for (i = 0; i < 16; i++) { b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val); val -= 0x202; } val = 0x3E3F; for (i = 0; i < 16; i++) { b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val); val -= 0x202; } b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SuperSwitchInit */ static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init) { if (dev->phy.rev >= 3) { if (!init) return; if (0 /* FIXME */) { b43_ntab_write(dev, B43_NTAB16(9, 2), 0x211); b43_ntab_write(dev, B43_NTAB16(9, 3), 0x222); b43_ntab_write(dev, B43_NTAB16(9, 8), 0x144); b43_ntab_write(dev, B43_NTAB16(9, 12), 0x188); } } else { b43_phy_write(dev, B43_NPHY_GPIO_LOOEN, 0); b43_phy_write(dev, B43_NPHY_GPIO_HIOEN, 0); switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, 0xFC00, 0xFC00); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: ssb_chipco_gpio_control(&dev->dev->sdev->bus->chipco, 0xFC00, 0xFC00); break; #endif } b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xFC00); b43_maskset16(dev, B43_MMIO_GPIO_CONTROL, (~0xFC00 & 0xFFFF), 0); if (init) { b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N */ int b43_phy_initn(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = phy->n; u8 tx_pwr_state; struct nphy_txgains target; u16 tmp; enum ieee80211_band tmp2; bool do_rssi_cal; u16 clip[2]; bool do_cal = false; if ((dev->phy.rev >= 3) && (sprom->boardflags_lo & B43_BFL_EXTLNA) && (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) { switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: bcma_cc_set32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_CHIPCTL, 0x40); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: chipco_set32(&dev->dev->sdev->bus->chipco, SSB_CHIPCO_CHIPCTL, 0x40); break; #endif } } nphy->deaf_count = 0; b43_nphy_tables_init(dev); nphy->crsminpwr_adjusted = false; nphy->noisevars_adjusted = false; /* Clear all overrides */ if (dev->phy.rev >= 3) { b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0); b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0); b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0); } else { b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); } b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, 0); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, 0); if (dev->phy.rev < 6) { b43_phy_write(dev, B43_NPHY_RFCTL_INTC3, 0); b43_phy_write(dev, B43_NPHY_RFCTL_INTC4, 0); } b43_phy_mask(dev, B43_NPHY_RFSEQMODE, ~(B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER)); if (dev->phy.rev >= 3) b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, 0); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, 0); if (dev->phy.rev <= 2) { tmp = (dev->phy.rev == 2) ? 0x3B : 0x40; b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~B43_NPHY_BPHY_CTL3_SCALE, tmp << B43_NPHY_BPHY_CTL3_SCALE_SHIFT); } b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_20M, 0x20); b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_40M, 0x20); if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD || (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE && dev->dev->board_type == 0x8B)) b43_phy_write(dev, B43_NPHY_TXREALFD, 0xA0); else b43_phy_write(dev, B43_NPHY_TXREALFD, 0xB8); b43_phy_write(dev, B43_NPHY_MIMO_CRSTXEXT, 0xC8); b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50); b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30); b43_nphy_update_mimo_config(dev, nphy->preamble_override); b43_nphy_update_txrx_chain(dev); if (phy->rev < 2) { b43_phy_write(dev, B43_NPHY_DUP40_GFBL, 0xAA8); b43_phy_write(dev, B43_NPHY_DUP40_BL, 0x9A4); } tmp2 = b43_current_band(dev->wl); if (b43_nphy_ipa(dev)) { b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1); b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F, nphy->papd_epsilon_offset[0] << 7); b43_phy_set(dev, B43_NPHY_PAPD_EN1, 0x1); b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ1, 0x007F, nphy->papd_epsilon_offset[1] << 7); b43_nphy_int_pa_set_tx_dig_filters(dev); } else if (phy->rev >= 5) { b43_nphy_ext_pa_set_tx_dig_filters(dev); } b43_nphy_workarounds(dev); /* Reset CCA, in init code it differs a little from standard way */ b43_phy_force_clock(dev, 1); tmp = b43_phy_read(dev, B43_NPHY_BBCFG); b43_phy_write(dev, B43_NPHY_BBCFG, tmp | B43_NPHY_BBCFG_RSTCCA); b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA); b43_phy_force_clock(dev, 0); b43_mac_phy_clock_set(dev, true); b43_nphy_pa_override(dev, false); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); b43_nphy_pa_override(dev, true); b43_nphy_classifier(dev, 0, 0); b43_nphy_read_clip_detection(dev, clip); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_nphy_bphy_init(dev); tx_pwr_state = nphy->txpwrctrl; b43_nphy_tx_power_ctrl(dev, false); b43_nphy_tx_power_fix(dev); b43_nphy_tx_power_ctl_idle_tssi(dev); b43_nphy_tx_power_ctl_setup(dev); b43_nphy_tx_gain_table_upload(dev); if (nphy->phyrxchain != 3) b43_nphy_set_rx_core_state(dev, nphy->phyrxchain); if (nphy->mphase_cal_phase_id > 0) ;/* TODO PHY Periodic Calibration Multi-Phase Restart */ do_rssi_cal = false; if (phy->rev >= 3) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) do_rssi_cal = !nphy->rssical_chanspec_2G.center_freq; else do_rssi_cal = !nphy->rssical_chanspec_5G.center_freq; if (do_rssi_cal) b43_nphy_rssi_cal(dev); else b43_nphy_restore_rssi_cal(dev); } else { b43_nphy_rssi_cal(dev); } if (!((nphy->measure_hold & 0x6) != 0)) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) do_cal = !nphy->iqcal_chanspec_2G.center_freq; else do_cal = !nphy->iqcal_chanspec_5G.center_freq; if (nphy->mute) do_cal = false; if (do_cal) { target = b43_nphy_get_tx_gains(dev); if (nphy->antsel_type == 2) b43_nphy_superswitch_init(dev, true); if (nphy->perical != 2) { b43_nphy_rssi_cal(dev); if (phy->rev >= 3) { nphy->cal_orig_pwr_idx[0] = nphy->txpwrindex[0].index_internal; nphy->cal_orig_pwr_idx[1] = nphy->txpwrindex[1].index_internal; /* TODO N PHY Pre Calibrate TX Gain */ target = b43_nphy_get_tx_gains(dev); } if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0) b43_nphy_save_cal(dev); } else if (nphy->mphase_cal_phase_id == 0) ;/* N PHY Periodic Calibration with arg 3 */ } else { b43_nphy_restore_cal(dev); } } b43_nphy_tx_pwr_ctrl_coef_setup(dev); b43_nphy_tx_power_ctrl(dev, tx_pwr_state); b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015); b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320); if (phy->rev >= 3 && phy->rev <= 6) b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014); b43_nphy_tx_lp_fbw(dev); if (phy->rev >= 3) b43_nphy_spur_workaround(dev); return 0; } /************************************************** * Channel switching ops. **************************************************/ static void b43_chantab_phy_upload(struct b43_wldev *dev, const struct b43_phy_n_sfo_cfg *e) { b43_phy_write(dev, B43_NPHY_BW1A, e->phy_bw1a); b43_phy_write(dev, B43_NPHY_BW2, e->phy_bw2); b43_phy_write(dev, B43_NPHY_BW3, e->phy_bw3); b43_phy_write(dev, B43_NPHY_BW4, e->phy_bw4); b43_phy_write(dev, B43_NPHY_BW5, e->phy_bw5); b43_phy_write(dev, B43_NPHY_BW6, e->phy_bw6); } /* http://bcm-v4.sipsolutions.net/802.11/PmuSpurAvoid */ static void b43_nphy_pmu_spur_avoid(struct b43_wldev *dev, bool avoid) { struct bcma_drv_cc __maybe_unused *cc; u32 __maybe_unused pmu_ctl; switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: cc = &dev->dev->bdev->bus->drv_cc; if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) { if (avoid) { bcma_chipco_pll_write(cc, 0x0, 0x11500010); bcma_chipco_pll_write(cc, 0x1, 0x000C0C06); bcma_chipco_pll_write(cc, 0x2, 0x0F600a08); bcma_chipco_pll_write(cc, 0x3, 0x00000000); bcma_chipco_pll_write(cc, 0x4, 0x2001E920); bcma_chipco_pll_write(cc, 0x5, 0x88888815); } else { bcma_chipco_pll_write(cc, 0x0, 0x11100010); bcma_chipco_pll_write(cc, 0x1, 0x000c0c06); bcma_chipco_pll_write(cc, 0x2, 0x03000a08); bcma_chipco_pll_write(cc, 0x3, 0x00000000); bcma_chipco_pll_write(cc, 0x4, 0x200005c0); bcma_chipco_pll_write(cc, 0x5, 0x88888815); } pmu_ctl = BCMA_CC_PMU_CTL_PLL_UPD; } else if (dev->dev->chip_id == 0x4716) { if (avoid) { bcma_chipco_pll_write(cc, 0x0, 0x11500060); bcma_chipco_pll_write(cc, 0x1, 0x080C0C06); bcma_chipco_pll_write(cc, 0x2, 0x0F600000); bcma_chipco_pll_write(cc, 0x3, 0x00000000); bcma_chipco_pll_write(cc, 0x4, 0x2001E924); bcma_chipco_pll_write(cc, 0x5, 0x88888815); } else { bcma_chipco_pll_write(cc, 0x0, 0x11100060); bcma_chipco_pll_write(cc, 0x1, 0x080c0c06); bcma_chipco_pll_write(cc, 0x2, 0x03000000); bcma_chipco_pll_write(cc, 0x3, 0x00000000); bcma_chipco_pll_write(cc, 0x4, 0x200005c0); bcma_chipco_pll_write(cc, 0x5, 0x88888815); } pmu_ctl = BCMA_CC_PMU_CTL_PLL_UPD | BCMA_CC_PMU_CTL_NOILPONW; } else if (dev->dev->chip_id == 0x4322 || dev->dev->chip_id == 0x4340 || dev->dev->chip_id == 0x4341) { bcma_chipco_pll_write(cc, 0x0, 0x11100070); bcma_chipco_pll_write(cc, 0x1, 0x1014140a); bcma_chipco_pll_write(cc, 0x5, 0x88888854); if (avoid) bcma_chipco_pll_write(cc, 0x2, 0x05201828); else bcma_chipco_pll_write(cc, 0x2, 0x05001828); pmu_ctl = BCMA_CC_PMU_CTL_PLL_UPD; } else { return; } bcma_cc_set32(cc, BCMA_CC_PMU_CTL, pmu_ctl); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: /* FIXME */ break; #endif } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ChanspecSetup */ static void b43_nphy_channel_setup(struct b43_wldev *dev, const struct b43_phy_n_sfo_cfg *e, struct ieee80211_channel *new_channel) { struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; int ch = new_channel->hw_value; u16 old_band_5ghz; u32 tmp32; old_band_5ghz = b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ; if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) { tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR); b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4); b43_phy_set(dev, B43_PHY_B_BBCFG, 0xC000); b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32); b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ); } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) { b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR); b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4); b43_phy_mask(dev, B43_PHY_B_BBCFG, 0x3FFF); b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32); } b43_chantab_phy_upload(dev, e); if (new_channel->hw_value == 14) { b43_nphy_classifier(dev, 2, 0); b43_phy_set(dev, B43_PHY_B_TEST, 0x0800); } else { b43_nphy_classifier(dev, 2, 2); if (new_channel->band == IEEE80211_BAND_2GHZ) b43_phy_mask(dev, B43_PHY_B_TEST, ~0x840); } if (!nphy->txpwrctrl) b43_nphy_tx_power_fix(dev); if (dev->phy.rev < 3) b43_nphy_adjust_lna_gain_table(dev); b43_nphy_tx_lp_fbw(dev); if (dev->phy.rev >= 3 && dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) { bool avoid = false; if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) { avoid = true; } else if (!b43_channel_type_is_40mhz(phy->channel_type)) { if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14) avoid = true; } else { /* 40MHz */ if (nphy->aband_spurwar_en && (ch == 38 || ch == 102 || ch == 118)) avoid = dev->dev->chip_id == 0x4716; } b43_nphy_pmu_spur_avoid(dev, avoid); if (dev->dev->chip_id == 43222 || dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) { b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, avoid ? 0x5341 : 0x8889); b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); } if (dev->phy.rev == 3 || dev->phy.rev == 4) ; /* TODO: reset PLL */ if (avoid) b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTRX); else b43_phy_mask(dev, B43_NPHY_BBCFG, ~B43_NPHY_BBCFG_RSTRX & 0xFFFF); b43_nphy_reset_cca(dev); /* wl sets useless phy_isspuravoid here */ } b43_phy_write(dev, B43_NPHY_NDATAT_DUP40, 0x3830); if (phy->rev >= 3) b43_nphy_spur_workaround(dev); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetChanspec */ static int b43_nphy_set_channel(struct b43_wldev *dev, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type) { struct b43_phy *phy = &dev->phy; const struct b43_nphy_channeltab_entry_rev2 *tabent_r2 = NULL; const struct b43_nphy_channeltab_entry_rev3 *tabent_r3 = NULL; u8 tmp; if (dev->phy.rev >= 3) { tabent_r3 = b43_nphy_get_chantabent_rev3(dev, channel->center_freq); if (!tabent_r3) return -ESRCH; } else { tabent_r2 = b43_nphy_get_chantabent_rev2(dev, channel->hw_value); if (!tabent_r2) return -ESRCH; } /* Channel is set later in common code, but we need to set it on our own to let this function's subcalls work properly. */ phy->channel = channel->hw_value; phy->channel_freq = channel->center_freq; if (b43_channel_type_is_40mhz(phy->channel_type) != b43_channel_type_is_40mhz(channel_type)) ; /* TODO: BMAC BW Set (channel_type) */ if (channel_type == NL80211_CHAN_HT40PLUS) b43_phy_set(dev, B43_NPHY_RXCTL, B43_NPHY_RXCTL_BSELU20); else if (channel_type == NL80211_CHAN_HT40MINUS) b43_phy_mask(dev, B43_NPHY_RXCTL, ~B43_NPHY_RXCTL_BSELU20); if (dev->phy.rev >= 3) { tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 4 : 0; b43_radio_maskset(dev, 0x08, 0xFFFB, tmp); b43_radio_2056_setup(dev, tabent_r3); b43_nphy_channel_setup(dev, &(tabent_r3->phy_regs), channel); } else { tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 0x0020 : 0x0050; b43_radio_maskset(dev, B2055_MASTER1, 0xFF8F, tmp); b43_radio_2055_setup(dev, tabent_r2); b43_nphy_channel_setup(dev, &(tabent_r2->phy_regs), channel); } return 0; } /************************************************** * Basic PHY ops. **************************************************/ static int b43_nphy_op_allocate(struct b43_wldev *dev) { struct b43_phy_n *nphy; nphy = kzalloc(sizeof(*nphy), GFP_KERNEL); if (!nphy) return -ENOMEM; dev->phy.n = nphy; return 0; } static void b43_nphy_op_prepare_structs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = phy->n; struct ssb_sprom *sprom = dev->dev->bus_sprom; memset(nphy, 0, sizeof(*nphy)); nphy->hang_avoid = (phy->rev == 3 || phy->rev == 4); nphy->spur_avoid = (phy->rev >= 3) ? B43_SPUR_AVOID_AUTO : B43_SPUR_AVOID_DISABLE; nphy->init_por = true; nphy->gain_boost = true; /* this way we follow wl, assume it is true */ nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */ nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */ nphy->perical = 2; /* avoid additional rssi cal on init (like wl) */ /* 128 can mean disabled-by-default state of TX pwr ctl. Max value is * 0x7f == 127 and we check for 128 when restoring TX pwr ctl. */ nphy->tx_pwr_idx[0] = 128; nphy->tx_pwr_idx[1] = 128; /* Hardware TX power control and 5GHz power gain */ nphy->txpwrctrl = false; nphy->pwg_gain_5ghz = false; if (dev->phy.rev >= 3 || (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE && (dev->dev->core_rev == 11 || dev->dev->core_rev == 12))) { nphy->txpwrctrl = true; nphy->pwg_gain_5ghz = true; } else if (sprom->revision >= 4) { if (dev->phy.rev >= 2 && (sprom->boardflags2_lo & B43_BFL2_TXPWRCTRL_EN)) { nphy->txpwrctrl = true; #ifdef CONFIG_B43_SSB if (dev->dev->bus_type == B43_BUS_SSB && dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) { struct pci_dev *pdev = dev->dev->sdev->bus->host_pci; if (pdev->device == 0x4328 || pdev->device == 0x432a) nphy->pwg_gain_5ghz = true; } #endif } else if (sprom->boardflags2_lo & B43_BFL2_5G_PWRGAIN) { nphy->pwg_gain_5ghz = true; } } if (dev->phy.rev >= 3) { nphy->ipa2g_on = sprom->fem.ghz2.extpa_gain == 2; nphy->ipa5g_on = sprom->fem.ghz5.extpa_gain == 2; } nphy->init_por = true; } static void b43_nphy_op_free(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = phy->n; kfree(nphy); phy->n = NULL; } static int b43_nphy_op_init(struct b43_wldev *dev) { return b43_phy_initn(dev); } static inline void check_phyreg(struct b43_wldev *dev, u16 offset) { #if B43_DEBUG if ((offset & B43_PHYROUTE) == B43_PHYROUTE_OFDM_GPHY) { /* OFDM registers are onnly available on A/G-PHYs */ b43err(dev->wl, "Invalid OFDM PHY access at " "0x%04X on N-PHY\n", offset); dump_stack(); } if ((offset & B43_PHYROUTE) == B43_PHYROUTE_EXT_GPHY) { /* Ext-G registers are only available on G-PHYs */ b43err(dev->wl, "Invalid EXT-G PHY access at " "0x%04X on N-PHY\n", offset); dump_stack(); } #endif /* B43_DEBUG */ } static u16 b43_nphy_op_read(struct b43_wldev *dev, u16 reg) { check_phyreg(dev, reg); b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_nphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) { check_phyreg(dev, reg); b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } static void b43_nphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { check_phyreg(dev, reg); b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_maskset16(dev, B43_MMIO_PHY_DATA, mask, set); } static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); /* N-PHY needs 0x100 for read access */ reg |= 0x100; b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); } static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } /* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, bool blocked) { if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) b43err(dev->wl, "MAC not suspended\n"); if (blocked) { b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_CHIP0PU); if (dev->phy.rev >= 7) { /* TODO */ } else if (dev->phy.rev >= 3) { b43_radio_mask(dev, 0x09, ~0x2); b43_radio_write(dev, 0x204D, 0); b43_radio_write(dev, 0x2053, 0); b43_radio_write(dev, 0x2058, 0); b43_radio_write(dev, 0x205E, 0); b43_radio_mask(dev, 0x2062, ~0xF0); b43_radio_write(dev, 0x2064, 0); b43_radio_write(dev, 0x304D, 0); b43_radio_write(dev, 0x3053, 0); b43_radio_write(dev, 0x3058, 0); b43_radio_write(dev, 0x305E, 0); b43_radio_mask(dev, 0x3062, ~0xF0); b43_radio_write(dev, 0x3064, 0); } } else { if (dev->phy.rev >= 7) { b43_radio_2057_init(dev); b43_switch_channel(dev, dev->phy.channel); } else if (dev->phy.rev >= 3) { b43_radio_init2056(dev); b43_switch_channel(dev, dev->phy.channel); } else { b43_radio_init2055(dev); } } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore */ static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on) { u16 override = on ? 0x0 : 0x7FFF; u16 core = on ? 0xD : 0x00FD; if (dev->phy.rev >= 3) { if (on) { b43_phy_write(dev, B43_NPHY_AFECTL_C1, core); b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override); b43_phy_write(dev, B43_NPHY_AFECTL_C2, core); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); } else { b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override); b43_phy_write(dev, B43_NPHY_AFECTL_C1, core); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); b43_phy_write(dev, B43_NPHY_AFECTL_C2, core); } } else { b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); } } static int b43_nphy_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { struct ieee80211_channel *channel = dev->wl->hw->conf.channel; enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if ((new_channel < 1) || (new_channel > 14)) return -EINVAL; } else { if (new_channel > 200) return -EINVAL; } return b43_nphy_set_channel(dev, channel, channel_type); } static unsigned int b43_nphy_op_get_default_chan(struct b43_wldev *dev) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) return 1; return 36; } const struct b43_phy_operations b43_phyops_n = { .allocate = b43_nphy_op_allocate, .free = b43_nphy_op_free, .prepare_structs = b43_nphy_op_prepare_structs, .init = b43_nphy_op_init, .phy_read = b43_nphy_op_read, .phy_write = b43_nphy_op_write, .phy_maskset = b43_nphy_op_maskset, .radio_read = b43_nphy_op_radio_read, .radio_write = b43_nphy_op_radio_write, .software_rfkill = b43_nphy_op_software_rfkill, .switch_analog = b43_nphy_op_switch_analog, .switch_channel = b43_nphy_op_switch_channel, .get_default_chan = b43_nphy_op_get_default_chan, .recalc_txpower = b43_nphy_op_recalc_txpower, .adjust_txpower = b43_nphy_op_adjust_txpower, }; compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_lp.h0000644000175000017500000016651012026211315022526 0ustar mcgrofmcgrof#ifndef LINUX_B43_PHY_LP_H_ #define LINUX_B43_PHY_LP_H_ /* Definitions for the LP-PHY */ /* The CCK PHY register range. */ #define B43_LPPHY_B_VERSION B43_PHY_CCK(0x00) /* B PHY version */ #define B43_LPPHY_B_BBCONFIG B43_PHY_CCK(0x01) /* B PHY BBConfig */ #define B43_LPPHY_B_RX_STAT0 B43_PHY_CCK(0x04) /* B PHY RX Status0 */ #define B43_LPPHY_B_RX_STAT1 B43_PHY_CCK(0x05) /* B PHY RX Status1 */ #define B43_LPPHY_B_CRS_THRESH B43_PHY_CCK(0x06) /* B PHY CRS Thresh */ #define B43_LPPHY_B_TXERROR B43_PHY_CCK(0x07) /* B PHY TxError */ #define B43_LPPHY_B_CHANNEL B43_PHY_CCK(0x08) /* B PHY Channel */ #define B43_LPPHY_B_WORKAROUND B43_PHY_CCK(0x09) /* B PHY workaround */ #define B43_LPPHY_B_TEST B43_PHY_CCK(0x0A) /* B PHY Test */ #define B43_LPPHY_B_FOURWIRE_ADDR B43_PHY_CCK(0x0B) /* B PHY Fourwire Address */ #define B43_LPPHY_B_FOURWIRE_DATA_HI B43_PHY_CCK(0x0C) /* B PHY Fourwire Data Hi */ #define B43_LPPHY_B_FOURWIRE_DATA_LO B43_PHY_CCK(0x0D) /* B PHY Fourwire Data Lo */ #define B43_LPPHY_B_BIST_STAT B43_PHY_CCK(0x0E) /* B PHY Bist Status */ #define B43_LPPHY_PA_RAMP_TX_TO B43_PHY_CCK(0x10) /* PA Ramp TX Timeout */ #define B43_LPPHY_RF_SYNTH_DC_TIMER B43_PHY_CCK(0x11) /* RF Synth DC Timer */ #define B43_LPPHY_PA_RAMP_TX_TIME_IN B43_PHY_CCK(0x12) /* PA ramp TX Time in */ #define B43_LPPHY_RX_FILTER_TIME_IN B43_PHY_CCK(0x13) /* RX Filter Time in */ #define B43_LPPHY_PLL_COEFF_S B43_PHY_CCK(0x18) /* PLL Coefficient(s) */ #define B43_LPPHY_PLL_OUT B43_PHY_CCK(0x19) /* PLL Out */ #define B43_LPPHY_RSSI_THRES B43_PHY_CCK(0x20) /* RSSI Threshold */ #define B43_LPPHY_IQ_THRES_HH B43_PHY_CCK(0x21) /* IQ Threshold HH */ #define B43_LPPHY_IQ_THRES_H B43_PHY_CCK(0x22) /* IQ Threshold H */ #define B43_LPPHY_IQ_THRES_L B43_PHY_CCK(0x23) /* IQ Threshold L */ #define B43_LPPHY_IQ_THRES_LL B43_PHY_CCK(0x24) /* IQ Threshold LL */ #define B43_LPPHY_AGC_GAIN B43_PHY_CCK(0x25) /* AGC Gain */ #define B43_LPPHY_LNA_GAIN_RANGE B43_PHY_CCK(0x26) /* LNA Gain Range */ #define B43_LPPHY_JSSI B43_PHY_CCK(0x27) /* JSSI */ #define B43_LPPHY_TSSI_CTL B43_PHY_CCK(0x28) /* TSSI Control */ #define B43_LPPHY_TSSI B43_PHY_CCK(0x29) /* TSSI */ #define B43_LPPHY_TR_LOSS B43_PHY_CCK(0x2A) /* TR Loss */ #define B43_LPPHY_LO_LEAKAGE B43_PHY_CCK(0x2B) /* LO Leakage */ #define B43_LPPHY_LO_RSSIACC B43_PHY_CCK(0x2C) /* LO RSSIAcc */ #define B43_LPPHY_LO_IQ_MAG_ACC B43_PHY_CCK(0x2D) /* LO IQ Mag Acc */ #define B43_LPPHY_TX_DCOFFSET1 B43_PHY_CCK(0x2E) /* TX DCOffset1 */ #define B43_LPPHY_TX_DCOFFSET2 B43_PHY_CCK(0x2F) /* TX DCOffset2 */ #define B43_LPPHY_SYNCPEAKCNT B43_PHY_CCK(0x30) /* SyncPeakCnt */ #define B43_LPPHY_SYNCFREQ B43_PHY_CCK(0x31) /* SyncFreq */ #define B43_LPPHY_SYNCDIVERSITYCTL B43_PHY_CCK(0x32) /* SyncDiversityControl */ #define B43_LPPHY_PEAKENERGYL B43_PHY_CCK(0x33) /* PeakEnergyL */ #define B43_LPPHY_PEAKENERGYH B43_PHY_CCK(0x34) /* PeakEnergyH */ #define B43_LPPHY_SYNCCTL B43_PHY_CCK(0x35) /* SyncControl */ #define B43_LPPHY_DSSSSTEP B43_PHY_CCK(0x38) /* DsssStep */ #define B43_LPPHY_DSSSWARMUP B43_PHY_CCK(0x39) /* DsssWarmup */ #define B43_LPPHY_DSSSSIGPOW B43_PHY_CCK(0x3D) /* DsssSigPow */ #define B43_LPPHY_SFDDETECTBLOCKTIME B43_PHY_CCK(0x40) /* SfdDetectBlockTIme */ #define B43_LPPHY_SFDTO B43_PHY_CCK(0x41) /* SFDTimeOut */ #define B43_LPPHY_SFDCTL B43_PHY_CCK(0x42) /* SFDControl */ #define B43_LPPHY_RXDBG B43_PHY_CCK(0x43) /* rxDebug */ #define B43_LPPHY_RX_DELAYCOMP B43_PHY_CCK(0x44) /* RX DelayComp */ #define B43_LPPHY_CRSDROPOUTTO B43_PHY_CCK(0x45) /* CRSDropoutTimeout */ #define B43_LPPHY_PSEUDOSHORTTO B43_PHY_CCK(0x46) /* PseudoShortTimeout */ #define B43_LPPHY_PR3931 B43_PHY_CCK(0x47) /* PR3931 */ #define B43_LPPHY_DSSSCOEFF1 B43_PHY_CCK(0x48) /* DSSSCoeff1 */ #define B43_LPPHY_DSSSCOEFF2 B43_PHY_CCK(0x49) /* DSSSCoeff2 */ #define B43_LPPHY_CCKCOEFF1 B43_PHY_CCK(0x4A) /* CCKCoeff1 */ #define B43_LPPHY_CCKCOEFF2 B43_PHY_CCK(0x4B) /* CCKCoeff2 */ #define B43_LPPHY_TRCORR B43_PHY_CCK(0x4C) /* TRCorr */ #define B43_LPPHY_ANGLESCALE B43_PHY_CCK(0x4D) /* AngleScale */ #define B43_LPPHY_OPTIONALMODES2 B43_PHY_CCK(0x4F) /* OptionalModes2 */ #define B43_LPPHY_CCKLMSSTEPSIZE B43_PHY_CCK(0x50) /* CCKLMSStepSize */ #define B43_LPPHY_DFEBYPASS B43_PHY_CCK(0x51) /* DFEBypass */ #define B43_LPPHY_CCKSTARTDELAYLONG B43_PHY_CCK(0x52) /* CCKStartDelayLong */ #define B43_LPPHY_CCKSTARTDELAYSHORT B43_PHY_CCK(0x53) /* CCKStartDelayShort */ #define B43_LPPHY_PPROCCHDELAY B43_PHY_CCK(0x54) /* PprocChDelay */ #define B43_LPPHY_PPROCONOFF B43_PHY_CCK(0x55) /* PProcOnOff */ #define B43_LPPHY_LNAGAINTWOBIT10 B43_PHY_CCK(0x5B) /* LNAGainTwoBit10 */ #define B43_LPPHY_LNAGAINTWOBIT32 B43_PHY_CCK(0x5C) /* LNAGainTwoBit32 */ #define B43_LPPHY_OPTIONALMODES B43_PHY_CCK(0x5D) /* OptionalModes */ #define B43_LPPHY_B_RX_STAT2 B43_PHY_CCK(0x5E) /* B PHY RX Status2 */ #define B43_LPPHY_B_RX_STAT3 B43_PHY_CCK(0x5F) /* B PHY RX Status3 */ #define B43_LPPHY_PWDNDACDELAY B43_PHY_CCK(0x63) /* pwdnDacDelay */ #define B43_LPPHY_FINEDIGIGAIN_CTL B43_PHY_CCK(0x67) /* FineDigiGain Control */ #define B43_LPPHY_LG2GAINTBLLNA8 B43_PHY_CCK(0x68) /* Lg2GainTblLNA8 */ #define B43_LPPHY_LG2GAINTBLLNA28 B43_PHY_CCK(0x69) /* Lg2GainTblLNA28 */ #define B43_LPPHY_GAINTBLLNATRSW B43_PHY_CCK(0x6A) /* GainTblLNATrSw */ #define B43_LPPHY_PEAKENERGY B43_PHY_CCK(0x6B) /* PeakEnergy */ #define B43_LPPHY_LG2INITGAIN B43_PHY_CCK(0x6C) /* lg2InitGain */ #define B43_LPPHY_BLANKCOUNTLNAPGA B43_PHY_CCK(0x6D) /* BlankCountLnaPga */ #define B43_LPPHY_LNAGAINTWOBIT54 B43_PHY_CCK(0x6E) /* LNAGainTwoBit54 */ #define B43_LPPHY_LNAGAINTWOBIT76 B43_PHY_CCK(0x6F) /* LNAGainTwoBit76 */ #define B43_LPPHY_JSSICTL B43_PHY_CCK(0x70) /* JSSIControl */ #define B43_LPPHY_LG2GAINTBLLNA44 B43_PHY_CCK(0x71) /* Lg2GainTblLNA44 */ #define B43_LPPHY_LG2GAINTBLLNA62 B43_PHY_CCK(0x72) /* Lg2GainTblLNA62 */ /* The OFDM PHY register range. */ #define B43_LPPHY_VERSION B43_PHY_OFDM(0x00) /* Version */ #define B43_LPPHY_BBCONFIG B43_PHY_OFDM(0x01) /* BBConfig */ #define B43_LPPHY_RX_STAT0 B43_PHY_OFDM(0x04) /* RX Status0 */ #define B43_LPPHY_RX_STAT1 B43_PHY_OFDM(0x05) /* RX Status1 */ #define B43_LPPHY_TX_ERROR B43_PHY_OFDM(0x07) /* TX Error */ #define B43_LPPHY_CHANNEL B43_PHY_OFDM(0x08) /* Channel */ #define B43_LPPHY_WORKAROUND B43_PHY_OFDM(0x09) /* workaround */ #define B43_LPPHY_FOURWIRE_ADDR B43_PHY_OFDM(0x0B) /* Fourwire Address */ #define B43_LPPHY_FOURWIREDATAHI B43_PHY_OFDM(0x0C) /* FourwireDataHi */ #define B43_LPPHY_FOURWIREDATALO B43_PHY_OFDM(0x0D) /* FourwireDataLo */ #define B43_LPPHY_BISTSTAT0 B43_PHY_OFDM(0x0E) /* BistStatus0 */ #define B43_LPPHY_BISTSTAT1 B43_PHY_OFDM(0x0F) /* BistStatus1 */ #define B43_LPPHY_CRSGAIN_CTL B43_PHY_OFDM(0x10) /* crsgain Control */ #define B43_LPPHY_OFDMPWR_THRESH0 B43_PHY_OFDM(0x11) /* ofdmPower Thresh0 */ #define B43_LPPHY_OFDMPWR_THRESH1 B43_PHY_OFDM(0x12) /* ofdmPower Thresh1 */ #define B43_LPPHY_OFDMPWR_THRESH2 B43_PHY_OFDM(0x13) /* ofdmPower Thresh2 */ #define B43_LPPHY_DSSSPWR_THRESH0 B43_PHY_OFDM(0x14) /* dsssPower Thresh0 */ #define B43_LPPHY_DSSSPWR_THRESH1 B43_PHY_OFDM(0x15) /* dsssPower Thresh1 */ #define B43_LPPHY_MINPWR_LEVEL B43_PHY_OFDM(0x16) /* MinPower Level */ #define B43_LPPHY_OFDMSYNCTHRESH0 B43_PHY_OFDM(0x17) /* ofdmSyncThresh0 */ #define B43_LPPHY_OFDMSYNCTHRESH1 B43_PHY_OFDM(0x18) /* ofdmSyncThresh1 */ #define B43_LPPHY_FINEFREQEST B43_PHY_OFDM(0x19) /* FineFreqEst */ #define B43_LPPHY_IDLEAFTERPKTRXTO B43_PHY_OFDM(0x1A) /* IDLEafterPktRXTimeout */ #define B43_LPPHY_LTRN_CTL B43_PHY_OFDM(0x1B) /* LTRN Control */ #define B43_LPPHY_DCOFFSETTRANSIENT B43_PHY_OFDM(0x1C) /* DCOffsetTransient */ #define B43_LPPHY_PREAMBLEINTO B43_PHY_OFDM(0x1D) /* PreambleInTimeout */ #define B43_LPPHY_PREAMBLECONFIRMTO B43_PHY_OFDM(0x1E) /* PreambleConfirmTimeout */ #define B43_LPPHY_CLIPTHRESH B43_PHY_OFDM(0x1F) /* ClipThresh */ #define B43_LPPHY_CLIPCTRTHRESH B43_PHY_OFDM(0x20) /* ClipCtrThresh */ #define B43_LPPHY_OFDMSYNCTIMER_CTL B43_PHY_OFDM(0x21) /* ofdmSyncTimer Control */ #define B43_LPPHY_WAITFORPHYSELTO B43_PHY_OFDM(0x22) /* WaitforPHYSelTimeout */ #define B43_LPPHY_HIGAINDB B43_PHY_OFDM(0x23) /* HiGainDB */ #define B43_LPPHY_LOWGAINDB B43_PHY_OFDM(0x24) /* LowGainDB */ #define B43_LPPHY_VERYLOWGAINDB B43_PHY_OFDM(0x25) /* VeryLowGainDB */ #define B43_LPPHY_GAINMISMATCH B43_PHY_OFDM(0x26) /* gainMismatch */ #define B43_LPPHY_GAINDIRECTMISMATCH B43_PHY_OFDM(0x27) /* gaindirectMismatch */ #define B43_LPPHY_PWR_THRESH0 B43_PHY_OFDM(0x28) /* Power Thresh0 */ #define B43_LPPHY_PWR_THRESH1 B43_PHY_OFDM(0x29) /* Power Thresh1 */ #define B43_LPPHY_DETECTOR_DELAY_ADJUST B43_PHY_OFDM(0x2A) /* Detector Delay Adjust */ #define B43_LPPHY_REDUCED_DETECTOR_DELAY B43_PHY_OFDM(0x2B) /* Reduced Detector Delay */ #define B43_LPPHY_DATA_TO B43_PHY_OFDM(0x2C) /* data Timeout */ #define B43_LPPHY_CORRELATOR_DIS_DELAY B43_PHY_OFDM(0x2D) /* correlator Dis Delay */ #define B43_LPPHY_DIVERSITY_GAINBACK B43_PHY_OFDM(0x2E) /* Diversity GainBack */ #define B43_LPPHY_DSSS_CONFIRM_CNT B43_PHY_OFDM(0x2F) /* DSSS Confirm Cnt */ #define B43_LPPHY_DC_BLANK_INT B43_PHY_OFDM(0x30) /* DC Blank Interval */ #define B43_LPPHY_GAIN_MISMATCH_LIMIT B43_PHY_OFDM(0x31) /* gain Mismatch Limit */ #define B43_LPPHY_CRS_ED_THRESH B43_PHY_OFDM(0x32) /* crs ed thresh */ #define B43_LPPHY_PHASE_SHIFT_CTL B43_PHY_OFDM(0x33) /* phase shift Control */ #define B43_LPPHY_INPUT_PWRDB B43_PHY_OFDM(0x34) /* Input PowerDB */ #define B43_LPPHY_OFDM_SYNC_CTL B43_PHY_OFDM(0x35) /* ofdm sync Control */ #define B43_LPPHY_AFE_ADC_CTL_0 B43_PHY_OFDM(0x36) /* Afe ADC Control 0 */ #define B43_LPPHY_AFE_ADC_CTL_1 B43_PHY_OFDM(0x37) /* Afe ADC Control 1 */ #define B43_LPPHY_AFE_ADC_CTL_2 B43_PHY_OFDM(0x38) /* Afe ADC Control 2 */ #define B43_LPPHY_AFE_DAC_CTL B43_PHY_OFDM(0x39) /* Afe DAC Control */ #define B43_LPPHY_AFE_CTL B43_PHY_OFDM(0x3A) /* Afe Control */ #define B43_LPPHY_AFE_CTL_OVR B43_PHY_OFDM(0x3B) /* Afe Control Ovr */ #define B43_LPPHY_AFE_CTL_OVRVAL B43_PHY_OFDM(0x3C) /* Afe Control OvrVal */ #define B43_LPPHY_AFE_RSSI_CTL_0 B43_PHY_OFDM(0x3D) /* Afe RSSI Control 0 */ #define B43_LPPHY_AFE_RSSI_CTL_1 B43_PHY_OFDM(0x3E) /* Afe RSSI Control 1 */ #define B43_LPPHY_AFE_RSSI_SEL B43_PHY_OFDM(0x3F) /* Afe RSSI Sel */ #define B43_LPPHY_RADAR_THRESH B43_PHY_OFDM(0x40) /* Radar Thresh */ #define B43_LPPHY_RADAR_BLANK_INT B43_PHY_OFDM(0x41) /* Radar blank Interval */ #define B43_LPPHY_RADAR_MIN_FM_INT B43_PHY_OFDM(0x42) /* Radar min fm Interval */ #define B43_LPPHY_RADAR_GAIN_TO B43_PHY_OFDM(0x43) /* Radar gain timeout */ #define B43_LPPHY_RADAR_PULSE_TO B43_PHY_OFDM(0x44) /* Radar pulse timeout */ #define B43_LPPHY_RADAR_DETECT_FM_CTL B43_PHY_OFDM(0x45) /* Radar detect FM Control */ #define B43_LPPHY_RADAR_DETECT_EN B43_PHY_OFDM(0x46) /* Radar detect En */ #define B43_LPPHY_RADAR_RD_DATA_REG B43_PHY_OFDM(0x47) /* Radar Rd Data Reg */ #define B43_LPPHY_LP_PHY_CTL B43_PHY_OFDM(0x48) /* LP PHY Control */ #define B43_LPPHY_CLASSIFIER_CTL B43_PHY_OFDM(0x49) /* classifier Control */ #define B43_LPPHY_RESET_CTL B43_PHY_OFDM(0x4A) /* reset Control */ #define B43_LPPHY_CLKEN_CTL B43_PHY_OFDM(0x4B) /* ClkEn Control */ #define B43_LPPHY_RF_OVERRIDE_0 B43_PHY_OFDM(0x4C) /* RF Override 0 */ #define B43_LPPHY_RF_OVERRIDE_VAL_0 B43_PHY_OFDM(0x4D) /* RF Override Val 0 */ #define B43_LPPHY_TR_LOOKUP_1 B43_PHY_OFDM(0x4E) /* TR Lookup 1 */ #define B43_LPPHY_TR_LOOKUP_2 B43_PHY_OFDM(0x4F) /* TR Lookup 2 */ #define B43_LPPHY_RSSISELLOOKUP1 B43_PHY_OFDM(0x50) /* RssiSelLookup1 */ #define B43_LPPHY_IQLO_CAL_CMD B43_PHY_OFDM(0x51) /* iqlo Cal Cmd */ #define B43_LPPHY_IQLO_CAL_CMD_N_NUM B43_PHY_OFDM(0x52) /* iqlo Cal Cmd N num */ #define B43_LPPHY_IQLO_CAL_CMD_G_CTL B43_PHY_OFDM(0x53) /* iqlo Cal Cmd G control */ #define B43_LPPHY_MACINT_DBG_REGISTER B43_PHY_OFDM(0x54) /* macint Debug Register */ #define B43_LPPHY_TABLE_ADDR B43_PHY_OFDM(0x55) /* Table Address */ #define B43_LPPHY_TABLEDATALO B43_PHY_OFDM(0x56) /* TabledataLo */ #define B43_LPPHY_TABLEDATAHI B43_PHY_OFDM(0x57) /* TabledataHi */ #define B43_LPPHY_PHY_CRS_ENABLE_ADDR B43_PHY_OFDM(0x58) /* phy CRS Enable Address */ #define B43_LPPHY_IDLETIME_CTL B43_PHY_OFDM(0x59) /* Idletime Control */ #define B43_LPPHY_IDLETIME_CRS_ON_LO B43_PHY_OFDM(0x5A) /* Idletime CRS On Lo */ #define B43_LPPHY_IDLETIME_CRS_ON_HI B43_PHY_OFDM(0x5B) /* Idletime CRS On Hi */ #define B43_LPPHY_IDLETIME_MEAS_TIME_LO B43_PHY_OFDM(0x5C) /* Idletime Meas Time Lo */ #define B43_LPPHY_IDLETIME_MEAS_TIME_HI B43_PHY_OFDM(0x5D) /* Idletime Meas Time Hi */ #define B43_LPPHY_RESET_LEN_OFDM_TX_ADDR B43_PHY_OFDM(0x5E) /* Reset len Ofdm TX Address */ #define B43_LPPHY_RESET_LEN_OFDM_RX_ADDR B43_PHY_OFDM(0x5F) /* Reset len Ofdm RX Address */ #define B43_LPPHY_REG_CRS_ENABLE B43_PHY_OFDM(0x60) /* reg crs enable */ #define B43_LPPHY_PLCP_TMT_STR0_CTR_MIN B43_PHY_OFDM(0x61) /* PLCP Tmt Str0 Ctr Min */ #define B43_LPPHY_PKT_FSM_RESET_LEN_VAL B43_PHY_OFDM(0x62) /* Pkt fsm Reset Len Value */ #define B43_LPPHY_READSYM2RESET_CTL B43_PHY_OFDM(0x63) /* readsym2reset Control */ #define B43_LPPHY_DC_FILTER_DELAY1 B43_PHY_OFDM(0x64) /* Dc filter delay1 */ #define B43_LPPHY_PACKET_RX_ACTIVE_TO B43_PHY_OFDM(0x65) /* packet rx Active timeout */ #define B43_LPPHY_ED_TOVAL B43_PHY_OFDM(0x66) /* ed timeoutValue */ #define B43_LPPHY_HOLD_CRS_ON_VAL B43_PHY_OFDM(0x67) /* hold CRS On Value */ #define B43_LPPHY_OFDM_TX_PHY_CRS_DELAY_VAL B43_PHY_OFDM(0x69) /* ofdm tx phy CRS Delay Value */ #define B43_LPPHY_CCK_TX_PHY_CRS_DELAY_VAL B43_PHY_OFDM(0x6A) /* cck tx phy CRS Delay Value */ #define B43_LPPHY_ED_ON_CONFIRM_TIMER_VAL B43_PHY_OFDM(0x6B) /* Ed on confirm Timer Value */ #define B43_LPPHY_ED_OFFSET_CONFIRM_TIMER_VAL B43_PHY_OFDM(0x6C) /* Ed offset confirm Timer Value */ #define B43_LPPHY_PHY_CRS_OFFSET_TIMER_VAL B43_PHY_OFDM(0x6D) /* phy CRS offset Timer Value */ #define B43_LPPHY_ADC_COMPENSATION_CTL B43_PHY_OFDM(0x70) /* ADC Compensation Control */ #define B43_LPPHY_LOG2_RBPSK_ADDR B43_PHY_OFDM(0x71) /* log2 RBPSK Address */ #define B43_LPPHY_LOG2_RQPSK_ADDR B43_PHY_OFDM(0x72) /* log2 RQPSK Address */ #define B43_LPPHY_LOG2_R16QAM_ADDR B43_PHY_OFDM(0x73) /* log2 R16QAM Address */ #define B43_LPPHY_LOG2_R64QAM_ADDR B43_PHY_OFDM(0x74) /* log2 R64QAM Address */ #define B43_LPPHY_OFFSET_BPSK_ADDR B43_PHY_OFDM(0x75) /* offset BPSK Address */ #define B43_LPPHY_OFFSET_QPSK_ADDR B43_PHY_OFDM(0x76) /* offset QPSK Address */ #define B43_LPPHY_OFFSET_16QAM_ADDR B43_PHY_OFDM(0x77) /* offset 16QAM Address */ #define B43_LPPHY_OFFSET_64QAM_ADDR B43_PHY_OFDM(0x78) /* offset 64QAM Address */ #define B43_LPPHY_ALPHA1 B43_PHY_OFDM(0x79) /* Alpha1 */ #define B43_LPPHY_ALPHA2 B43_PHY_OFDM(0x7A) /* Alpha2 */ #define B43_LPPHY_BETA1 B43_PHY_OFDM(0x7B) /* Beta1 */ #define B43_LPPHY_BETA2 B43_PHY_OFDM(0x7C) /* Beta2 */ #define B43_LPPHY_LOOP_NUM_ADDR B43_PHY_OFDM(0x7D) /* Loop Num Address */ #define B43_LPPHY_STR_COLLMAX_SMPL_ADDR B43_PHY_OFDM(0x7E) /* Str Collmax Sample Address */ #define B43_LPPHY_MAX_SMPL_COARSE_FINE_ADDR B43_PHY_OFDM(0x7F) /* Max Sample Coarse/Fine Address */ #define B43_LPPHY_MAX_SMPL_COARSE_STR0CTR_ADDR B43_PHY_OFDM(0x80) /* Max Sample Coarse/Str0Ctr Address */ #define B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR B43_PHY_OFDM(0x81) /* IQ Enable Wait Time Address */ #define B43_LPPHY_IQ_NUM_SMPLS_ADDR B43_PHY_OFDM(0x82) /* IQ Num Samples Address */ #define B43_LPPHY_IQ_ACC_HI_ADDR B43_PHY_OFDM(0x83) /* IQ Acc Hi Address */ #define B43_LPPHY_IQ_ACC_LO_ADDR B43_PHY_OFDM(0x84) /* IQ Acc Lo Address */ #define B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR B43_PHY_OFDM(0x85) /* IQ I PWR Acc Hi Address */ #define B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR B43_PHY_OFDM(0x86) /* IQ I PWR Acc Lo Address */ #define B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR B43_PHY_OFDM(0x87) /* IQ Q PWR Acc Hi Address */ #define B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR B43_PHY_OFDM(0x88) /* IQ Q PWR Acc Lo Address */ #define B43_LPPHY_MAXNUMSTEPS B43_PHY_OFDM(0x89) /* MaxNumsteps */ #define B43_LPPHY_ROTORPHASE_ADDR B43_PHY_OFDM(0x8A) /* RotorPhase Address */ #define B43_LPPHY_ADVANCEDRETARDROTOR_ADDR B43_PHY_OFDM(0x8B) /* AdvancedRetardRotor Address */ #define B43_LPPHY_RSSIADCDELAY_CTL_ADDR B43_PHY_OFDM(0x8D) /* rssiAdcdelay Control Address */ #define B43_LPPHY_TSSISTAT_ADDR B43_PHY_OFDM(0x8E) /* tssiStatus Address */ #define B43_LPPHY_TEMPSENSESTAT_ADDR B43_PHY_OFDM(0x8F) /* tempsenseStatus Address */ #define B43_LPPHY_TEMPSENSE_CTL_ADDR B43_PHY_OFDM(0x90) /* tempsense Control Address */ #define B43_LPPHY_WRSSISTAT_ADDR B43_PHY_OFDM(0x91) /* wrssistatus Address */ #define B43_LPPHY_MUFACTORADDR B43_PHY_OFDM(0x92) /* mufactoraddr */ #define B43_LPPHY_SCRAMSTATE_ADDR B43_PHY_OFDM(0x93) /* scramstate Address */ #define B43_LPPHY_TXHOLDOFFADDR B43_PHY_OFDM(0x94) /* txholdoffaddr */ #define B43_LPPHY_PKTGAINVAL_ADDR B43_PHY_OFDM(0x95) /* pktgainval Address */ #define B43_LPPHY_COARSEESTIM_ADDR B43_PHY_OFDM(0x96) /* Coarseestim Address */ #define B43_LPPHY_STATE_TRANSITION_ADDR B43_PHY_OFDM(0x97) /* state Transition Address */ #define B43_LPPHY_TRN_OFFSET_ADDR B43_PHY_OFDM(0x98) /* TRN offset Address */ #define B43_LPPHY_NUM_ROTOR_ADDR B43_PHY_OFDM(0x99) /* Num Rotor Address */ #define B43_LPPHY_VITERBI_OFFSET_ADDR B43_PHY_OFDM(0x9A) /* Viterbi Offset Address */ #define B43_LPPHY_SMPL_COLLECT_WAIT_ADDR B43_PHY_OFDM(0x9B) /* Sample collect wait Address */ #define B43_LPPHY_A_PHY_CTL_ADDR B43_PHY_OFDM(0x9C) /* A PHY Control Address */ #define B43_LPPHY_NUM_PASS_THROUGH_ADDR B43_PHY_OFDM(0x9D) /* Num Pass Through Address */ #define B43_LPPHY_RX_COMP_COEFF_S B43_PHY_OFDM(0x9E) /* RX Comp coefficient(s) */ #define B43_LPPHY_CPAROTATEVAL B43_PHY_OFDM(0x9F) /* cpaRotateValue */ #define B43_LPPHY_SMPL_PLAY_COUNT B43_PHY_OFDM(0xA0) /* Sample play count */ #define B43_LPPHY_SMPL_PLAY_BUFFER_CTL B43_PHY_OFDM(0xA1) /* Sample play Buffer Control */ #define B43_LPPHY_FOURWIRE_CTL B43_PHY_OFDM(0xA2) /* fourwire Control */ #define B43_LPPHY_CPA_TAILCOUNT_VAL B43_PHY_OFDM(0xA3) /* CPA TailCount Value */ #define B43_LPPHY_TX_PWR_CTL_CMD B43_PHY_OFDM(0xA4) /* TX Power Control Cmd */ #define B43_LPPHY_TX_PWR_CTL_CMD_MODE 0xE000 /* TX power control mode mask */ #define B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF 0x0000 /* TX power control is OFF */ #define B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW 0x8000 /* TX power control is SOFTWARE */ #define B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW 0xE000 /* TX power control is HARDWARE */ #define B43_LPPHY_TX_PWR_CTL_NNUM B43_PHY_OFDM(0xA5) /* TX Power Control Nnum */ #define B43_LPPHY_TX_PWR_CTL_IDLETSSI B43_PHY_OFDM(0xA6) /* TX Power Control IdleTssi */ #define B43_LPPHY_TX_PWR_CTL_TARGETPWR B43_PHY_OFDM(0xA7) /* TX Power Control TargetPower */ #define B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT B43_PHY_OFDM(0xA8) /* TX Power Control DeltaPower Limit */ #define B43_LPPHY_TX_PWR_CTL_BASEINDEX B43_PHY_OFDM(0xA9) /* TX Power Control BaseIndex */ #define B43_LPPHY_TX_PWR_CTL_PWR_INDEX B43_PHY_OFDM(0xAA) /* TX Power Control Power Index */ #define B43_LPPHY_TX_PWR_CTL_STAT B43_PHY_OFDM(0xAB) /* TX Power Control Status */ #define B43_LPPHY_LP_RF_SIGNAL_LUT B43_PHY_OFDM(0xAC) /* LP RF signal LUT */ #define B43_LPPHY_RX_RADIO_CTL_FILTER_STATE B43_PHY_OFDM(0xAD) /* RX Radio Control Filter State */ #define B43_LPPHY_RX_RADIO_CTL B43_PHY_OFDM(0xAE) /* RX Radio Control */ #define B43_LPPHY_NRSSI_STAT_ADDR B43_PHY_OFDM(0xAF) /* NRSSI status Address */ #define B43_LPPHY_RF_OVERRIDE_2 B43_PHY_OFDM(0xB0) /* RF override 2 */ #define B43_LPPHY_RF_OVERRIDE_2_VAL B43_PHY_OFDM(0xB1) /* RF override 2 val */ #define B43_LPPHY_PS_CTL_OVERRIDE_VAL0 B43_PHY_OFDM(0xB2) /* PS Control override val0 */ #define B43_LPPHY_PS_CTL_OVERRIDE_VAL1 B43_PHY_OFDM(0xB3) /* PS Control override val1 */ #define B43_LPPHY_PS_CTL_OVERRIDE_VAL2 B43_PHY_OFDM(0xB4) /* PS Control override val2 */ #define B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL B43_PHY_OFDM(0xB5) /* TX gain Control override val */ #define B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL B43_PHY_OFDM(0xB6) /* RX gain Control override val */ #define B43_LPPHY_AFE_DDFS B43_PHY_OFDM(0xB7) /* AFE DDFS */ #define B43_LPPHY_AFE_DDFS_POINTER_INIT B43_PHY_OFDM(0xB8) /* AFE DDFS pointer init */ #define B43_LPPHY_AFE_DDFS_INCR_INIT B43_PHY_OFDM(0xB9) /* AFE DDFS incr init */ #define B43_LPPHY_MRCNOISEREDUCTION B43_PHY_OFDM(0xBA) /* mrcNoiseReduction */ #define B43_LPPHY_TR_LOOKUP_3 B43_PHY_OFDM(0xBB) /* TR Lookup 3 */ #define B43_LPPHY_TR_LOOKUP_4 B43_PHY_OFDM(0xBC) /* TR Lookup 4 */ #define B43_LPPHY_RADAR_FIFO_STAT B43_PHY_OFDM(0xBD) /* Radar FIFO Status */ #define B43_LPPHY_GPIO_OUTEN B43_PHY_OFDM(0xBE) /* GPIO Out enable */ #define B43_LPPHY_GPIO_SELECT B43_PHY_OFDM(0xBF) /* GPIO Select */ #define B43_LPPHY_GPIO_OUT B43_PHY_OFDM(0xC0) /* GPIO Out */ #define B43_LPPHY_4C3 B43_PHY_OFDM(0xC3) /* unknown, used during BB init */ #define B43_LPPHY_4C4 B43_PHY_OFDM(0xC4) /* unknown, used during BB init */ #define B43_LPPHY_4C5 B43_PHY_OFDM(0xC5) /* unknown, used during BB init */ #define B43_LPPHY_TR_LOOKUP_5 B43_PHY_OFDM(0xC7) /* TR Lookup 5 */ #define B43_LPPHY_TR_LOOKUP_6 B43_PHY_OFDM(0xC8) /* TR Lookup 6 */ #define B43_LPPHY_TR_LOOKUP_7 B43_PHY_OFDM(0xC9) /* TR Lookup 7 */ #define B43_LPPHY_TR_LOOKUP_8 B43_PHY_OFDM(0xCA) /* TR Lookup 8 */ #define B43_LPPHY_RF_PWR_OVERRIDE B43_PHY_OFDM(0xD3) /* RF power override */ /* Radio register access decorators. */ #define B43_LP_RADIO(radio_reg) (radio_reg) #define B43_LP_NORTH(radio_reg) B43_LP_RADIO(radio_reg) #define B43_LP_SOUTH(radio_reg) B43_LP_RADIO((radio_reg) | 0x4000) /*** Broadcom 2062 NORTH radio registers ***/ #define B2062_N_COMM1 B43_LP_NORTH(0x000) /* Common 01 (north) */ #define B2062_N_COMM2 B43_LP_NORTH(0x002) /* Common 02 (north) */ #define B2062_N_COMM3 B43_LP_NORTH(0x003) /* Common 03 (north) */ #define B2062_N_COMM4 B43_LP_NORTH(0x004) /* Common 04 (north) */ #define B2062_N_COMM5 B43_LP_NORTH(0x005) /* Common 05 (north) */ #define B2062_N_COMM6 B43_LP_NORTH(0x006) /* Common 06 (north) */ #define B2062_N_COMM7 B43_LP_NORTH(0x007) /* Common 07 (north) */ #define B2062_N_COMM8 B43_LP_NORTH(0x008) /* Common 08 (north) */ #define B2062_N_COMM9 B43_LP_NORTH(0x009) /* Common 09 (north) */ #define B2062_N_COMM10 B43_LP_NORTH(0x00A) /* Common 10 (north) */ #define B2062_N_COMM11 B43_LP_NORTH(0x00B) /* Common 11 (north) */ #define B2062_N_COMM12 B43_LP_NORTH(0x00C) /* Common 12 (north) */ #define B2062_N_COMM13 B43_LP_NORTH(0x00D) /* Common 13 (north) */ #define B2062_N_COMM14 B43_LP_NORTH(0x00E) /* Common 14 (north) */ #define B2062_N_COMM15 B43_LP_NORTH(0x00F) /* Common 15 (north) */ #define B2062_N_PDN_CTL0 B43_LP_NORTH(0x010) /* PDN Control 0 (north) */ #define B2062_N_PDN_CTL1 B43_LP_NORTH(0x011) /* PDN Control 1 (north) */ #define B2062_N_PDN_CTL2 B43_LP_NORTH(0x012) /* PDN Control 2 (north) */ #define B2062_N_PDN_CTL3 B43_LP_NORTH(0x013) /* PDN Control 3 (north) */ #define B2062_N_PDN_CTL4 B43_LP_NORTH(0x014) /* PDN Control 4 (north) */ #define B2062_N_GEN_CTL0 B43_LP_NORTH(0x015) /* GEN Control 0 (north) */ #define B2062_N_IQ_CALIB B43_LP_NORTH(0x016) /* IQ Calibration (north) */ #define B2062_N_LGENC B43_LP_NORTH(0x017) /* LGENC (north) */ #define B2062_N_LGENA_LPF B43_LP_NORTH(0x018) /* LGENA LPF (north) */ #define B2062_N_LGENA_BIAS0 B43_LP_NORTH(0x019) /* LGENA Bias 0 (north) */ #define B2062_N_LGNEA_BIAS1 B43_LP_NORTH(0x01A) /* LGNEA Bias 1 (north) */ #define B2062_N_LGENA_CTL0 B43_LP_NORTH(0x01B) /* LGENA Control 0 (north) */ #define B2062_N_LGENA_CTL1 B43_LP_NORTH(0x01C) /* LGENA Control 1 (north) */ #define B2062_N_LGENA_CTL2 B43_LP_NORTH(0x01D) /* LGENA Control 2 (north) */ #define B2062_N_LGENA_TUNE0 B43_LP_NORTH(0x01E) /* LGENA Tune 0 (north) */ #define B2062_N_LGENA_TUNE1 B43_LP_NORTH(0x01F) /* LGENA Tune 1 (north) */ #define B2062_N_LGENA_TUNE2 B43_LP_NORTH(0x020) /* LGENA Tune 2 (north) */ #define B2062_N_LGENA_TUNE3 B43_LP_NORTH(0x021) /* LGENA Tune 3 (north) */ #define B2062_N_LGENA_CTL3 B43_LP_NORTH(0x022) /* LGENA Control 3 (north) */ #define B2062_N_LGENA_CTL4 B43_LP_NORTH(0x023) /* LGENA Control 4 (north) */ #define B2062_N_LGENA_CTL5 B43_LP_NORTH(0x024) /* LGENA Control 5 (north) */ #define B2062_N_LGENA_CTL6 B43_LP_NORTH(0x025) /* LGENA Control 6 (north) */ #define B2062_N_LGENA_CTL7 B43_LP_NORTH(0x026) /* LGENA Control 7 (north) */ #define B2062_N_RXA_CTL0 B43_LP_NORTH(0x027) /* RXA Control 0 (north) */ #define B2062_N_RXA_CTL1 B43_LP_NORTH(0x028) /* RXA Control 1 (north) */ #define B2062_N_RXA_CTL2 B43_LP_NORTH(0x029) /* RXA Control 2 (north) */ #define B2062_N_RXA_CTL3 B43_LP_NORTH(0x02A) /* RXA Control 3 (north) */ #define B2062_N_RXA_CTL4 B43_LP_NORTH(0x02B) /* RXA Control 4 (north) */ #define B2062_N_RXA_CTL5 B43_LP_NORTH(0x02C) /* RXA Control 5 (north) */ #define B2062_N_RXA_CTL6 B43_LP_NORTH(0x02D) /* RXA Control 6 (north) */ #define B2062_N_RXA_CTL7 B43_LP_NORTH(0x02E) /* RXA Control 7 (north) */ #define B2062_N_RXBB_CTL0 B43_LP_NORTH(0x02F) /* RXBB Control 0 (north) */ #define B2062_N_RXBB_CTL1 B43_LP_NORTH(0x030) /* RXBB Control 1 (north) */ #define B2062_N_RXBB_CTL2 B43_LP_NORTH(0x031) /* RXBB Control 2 (north) */ #define B2062_N_RXBB_GAIN0 B43_LP_NORTH(0x032) /* RXBB Gain 0 (north) */ #define B2062_N_RXBB_GAIN1 B43_LP_NORTH(0x033) /* RXBB Gain 1 (north) */ #define B2062_N_RXBB_GAIN2 B43_LP_NORTH(0x034) /* RXBB Gain 2 (north) */ #define B2062_N_RXBB_GAIN3 B43_LP_NORTH(0x035) /* RXBB Gain 3 (north) */ #define B2062_N_RXBB_RSSI0 B43_LP_NORTH(0x036) /* RXBB RSSI 0 (north) */ #define B2062_N_RXBB_RSSI1 B43_LP_NORTH(0x037) /* RXBB RSSI 1 (north) */ #define B2062_N_RXBB_CALIB0 B43_LP_NORTH(0x038) /* RXBB Calibration0 (north) */ #define B2062_N_RXBB_CALIB1 B43_LP_NORTH(0x039) /* RXBB Calibration1 (north) */ #define B2062_N_RXBB_CALIB2 B43_LP_NORTH(0x03A) /* RXBB Calibration2 (north) */ #define B2062_N_RXBB_BIAS0 B43_LP_NORTH(0x03B) /* RXBB Bias 0 (north) */ #define B2062_N_RXBB_BIAS1 B43_LP_NORTH(0x03C) /* RXBB Bias 1 (north) */ #define B2062_N_RXBB_BIAS2 B43_LP_NORTH(0x03D) /* RXBB Bias 2 (north) */ #define B2062_N_RXBB_BIAS3 B43_LP_NORTH(0x03E) /* RXBB Bias 3 (north) */ #define B2062_N_RXBB_BIAS4 B43_LP_NORTH(0x03F) /* RXBB Bias 4 (north) */ #define B2062_N_RXBB_BIAS5 B43_LP_NORTH(0x040) /* RXBB Bias 5 (north) */ #define B2062_N_RXBB_RSSI2 B43_LP_NORTH(0x041) /* RXBB RSSI 2 (north) */ #define B2062_N_RXBB_RSSI3 B43_LP_NORTH(0x042) /* RXBB RSSI 3 (north) */ #define B2062_N_RXBB_RSSI4 B43_LP_NORTH(0x043) /* RXBB RSSI 4 (north) */ #define B2062_N_RXBB_RSSI5 B43_LP_NORTH(0x044) /* RXBB RSSI 5 (north) */ #define B2062_N_TX_CTL0 B43_LP_NORTH(0x045) /* TX Control 0 (north) */ #define B2062_N_TX_CTL1 B43_LP_NORTH(0x046) /* TX Control 1 (north) */ #define B2062_N_TX_CTL2 B43_LP_NORTH(0x047) /* TX Control 2 (north) */ #define B2062_N_TX_CTL3 B43_LP_NORTH(0x048) /* TX Control 3 (north) */ #define B2062_N_TX_CTL4 B43_LP_NORTH(0x049) /* TX Control 4 (north) */ #define B2062_N_TX_CTL5 B43_LP_NORTH(0x04A) /* TX Control 5 (north) */ #define B2062_N_TX_CTL6 B43_LP_NORTH(0x04B) /* TX Control 6 (north) */ #define B2062_N_TX_CTL7 B43_LP_NORTH(0x04C) /* TX Control 7 (north) */ #define B2062_N_TX_CTL8 B43_LP_NORTH(0x04D) /* TX Control 8 (north) */ #define B2062_N_TX_CTL9 B43_LP_NORTH(0x04E) /* TX Control 9 (north) */ #define B2062_N_TX_CTL_A B43_LP_NORTH(0x04F) /* TX Control A (north) */ #define B2062_N_TX_GC2G B43_LP_NORTH(0x050) /* TX GC2G (north) */ #define B2062_N_TX_GC5G B43_LP_NORTH(0x051) /* TX GC5G (north) */ #define B2062_N_TX_TUNE B43_LP_NORTH(0x052) /* TX Tune (north) */ #define B2062_N_TX_PAD B43_LP_NORTH(0x053) /* TX PAD (north) */ #define B2062_N_TX_PGA B43_LP_NORTH(0x054) /* TX PGA (north) */ #define B2062_N_TX_PADAUX B43_LP_NORTH(0x055) /* TX PADAUX (north) */ #define B2062_N_TX_PGAAUX B43_LP_NORTH(0x056) /* TX PGAAUX (north) */ #define B2062_N_TSSI_CTL0 B43_LP_NORTH(0x057) /* TSSI Control 0 (north) */ #define B2062_N_TSSI_CTL1 B43_LP_NORTH(0x058) /* TSSI Control 1 (north) */ #define B2062_N_TSSI_CTL2 B43_LP_NORTH(0x059) /* TSSI Control 2 (north) */ #define B2062_N_IQ_CALIB_CTL0 B43_LP_NORTH(0x05A) /* IQ Calibration Control 0 (north) */ #define B2062_N_IQ_CALIB_CTL1 B43_LP_NORTH(0x05B) /* IQ Calibration Control 1 (north) */ #define B2062_N_IQ_CALIB_CTL2 B43_LP_NORTH(0x05C) /* IQ Calibration Control 2 (north) */ #define B2062_N_CALIB_TS B43_LP_NORTH(0x05D) /* Calibration TS (north) */ #define B2062_N_CALIB_CTL0 B43_LP_NORTH(0x05E) /* Calibration Control 0 (north) */ #define B2062_N_CALIB_CTL1 B43_LP_NORTH(0x05F) /* Calibration Control 1 (north) */ #define B2062_N_CALIB_CTL2 B43_LP_NORTH(0x060) /* Calibration Control 2 (north) */ #define B2062_N_CALIB_CTL3 B43_LP_NORTH(0x061) /* Calibration Control 3 (north) */ #define B2062_N_CALIB_CTL4 B43_LP_NORTH(0x062) /* Calibration Control 4 (north) */ #define B2062_N_CALIB_DBG0 B43_LP_NORTH(0x063) /* Calibration Debug 0 (north) */ #define B2062_N_CALIB_DBG1 B43_LP_NORTH(0x064) /* Calibration Debug 1 (north) */ #define B2062_N_CALIB_DBG2 B43_LP_NORTH(0x065) /* Calibration Debug 2 (north) */ #define B2062_N_CALIB_DBG3 B43_LP_NORTH(0x066) /* Calibration Debug 3 (north) */ #define B2062_N_PSENSE_CTL0 B43_LP_NORTH(0x069) /* PSENSE Control 0 (north) */ #define B2062_N_PSENSE_CTL1 B43_LP_NORTH(0x06A) /* PSENSE Control 1 (north) */ #define B2062_N_PSENSE_CTL2 B43_LP_NORTH(0x06B) /* PSENSE Control 2 (north) */ #define B2062_N_TEST_BUF0 B43_LP_NORTH(0x06C) /* TEST BUF0 (north) */ /*** Broadcom 2062 SOUTH radio registers ***/ #define B2062_S_COMM1 B43_LP_SOUTH(0x000) /* Common 01 (south) */ #define B2062_S_RADIO_ID_CODE B43_LP_SOUTH(0x001) /* Radio ID code (south) */ #define B2062_S_COMM2 B43_LP_SOUTH(0x002) /* Common 02 (south) */ #define B2062_S_COMM3 B43_LP_SOUTH(0x003) /* Common 03 (south) */ #define B2062_S_COMM4 B43_LP_SOUTH(0x004) /* Common 04 (south) */ #define B2062_S_COMM5 B43_LP_SOUTH(0x005) /* Common 05 (south) */ #define B2062_S_COMM6 B43_LP_SOUTH(0x006) /* Common 06 (south) */ #define B2062_S_COMM7 B43_LP_SOUTH(0x007) /* Common 07 (south) */ #define B2062_S_COMM8 B43_LP_SOUTH(0x008) /* Common 08 (south) */ #define B2062_S_COMM9 B43_LP_SOUTH(0x009) /* Common 09 (south) */ #define B2062_S_COMM10 B43_LP_SOUTH(0x00A) /* Common 10 (south) */ #define B2062_S_COMM11 B43_LP_SOUTH(0x00B) /* Common 11 (south) */ #define B2062_S_COMM12 B43_LP_SOUTH(0x00C) /* Common 12 (south) */ #define B2062_S_COMM13 B43_LP_SOUTH(0x00D) /* Common 13 (south) */ #define B2062_S_COMM14 B43_LP_SOUTH(0x00E) /* Common 14 (south) */ #define B2062_S_COMM15 B43_LP_SOUTH(0x00F) /* Common 15 (south) */ #define B2062_S_PDS_CTL0 B43_LP_SOUTH(0x010) /* PDS Control 0 (south) */ #define B2062_S_PDS_CTL1 B43_LP_SOUTH(0x011) /* PDS Control 1 (south) */ #define B2062_S_PDS_CTL2 B43_LP_SOUTH(0x012) /* PDS Control 2 (south) */ #define B2062_S_PDS_CTL3 B43_LP_SOUTH(0x013) /* PDS Control 3 (south) */ #define B2062_S_BG_CTL0 B43_LP_SOUTH(0x014) /* BG Control 0 (south) */ #define B2062_S_BG_CTL1 B43_LP_SOUTH(0x015) /* BG Control 1 (south) */ #define B2062_S_BG_CTL2 B43_LP_SOUTH(0x016) /* BG Control 2 (south) */ #define B2062_S_LGENG_CTL0 B43_LP_SOUTH(0x017) /* LGENG Control 00 (south) */ #define B2062_S_LGENG_CTL1 B43_LP_SOUTH(0x018) /* LGENG Control 01 (south) */ #define B2062_S_LGENG_CTL2 B43_LP_SOUTH(0x019) /* LGENG Control 02 (south) */ #define B2062_S_LGENG_CTL3 B43_LP_SOUTH(0x01A) /* LGENG Control 03 (south) */ #define B2062_S_LGENG_CTL4 B43_LP_SOUTH(0x01B) /* LGENG Control 04 (south) */ #define B2062_S_LGENG_CTL5 B43_LP_SOUTH(0x01C) /* LGENG Control 05 (south) */ #define B2062_S_LGENG_CTL6 B43_LP_SOUTH(0x01D) /* LGENG Control 06 (south) */ #define B2062_S_LGENG_CTL7 B43_LP_SOUTH(0x01E) /* LGENG Control 07 (south) */ #define B2062_S_LGENG_CTL8 B43_LP_SOUTH(0x01F) /* LGENG Control 08 (south) */ #define B2062_S_LGENG_CTL9 B43_LP_SOUTH(0x020) /* LGENG Control 09 (south) */ #define B2062_S_LGENG_CTL10 B43_LP_SOUTH(0x021) /* LGENG Control 10 (south) */ #define B2062_S_LGENG_CTL11 B43_LP_SOUTH(0x022) /* LGENG Control 11 (south) */ #define B2062_S_REFPLL_CTL0 B43_LP_SOUTH(0x023) /* REFPLL Control 00 (south) */ #define B2062_S_REFPLL_CTL1 B43_LP_SOUTH(0x024) /* REFPLL Control 01 (south) */ #define B2062_S_REFPLL_CTL2 B43_LP_SOUTH(0x025) /* REFPLL Control 02 (south) */ #define B2062_S_REFPLL_CTL3 B43_LP_SOUTH(0x026) /* REFPLL Control 03 (south) */ #define B2062_S_REFPLL_CTL4 B43_LP_SOUTH(0x027) /* REFPLL Control 04 (south) */ #define B2062_S_REFPLL_CTL5 B43_LP_SOUTH(0x028) /* REFPLL Control 05 (south) */ #define B2062_S_REFPLL_CTL6 B43_LP_SOUTH(0x029) /* REFPLL Control 06 (south) */ #define B2062_S_REFPLL_CTL7 B43_LP_SOUTH(0x02A) /* REFPLL Control 07 (south) */ #define B2062_S_REFPLL_CTL8 B43_LP_SOUTH(0x02B) /* REFPLL Control 08 (south) */ #define B2062_S_REFPLL_CTL9 B43_LP_SOUTH(0x02C) /* REFPLL Control 09 (south) */ #define B2062_S_REFPLL_CTL10 B43_LP_SOUTH(0x02D) /* REFPLL Control 10 (south) */ #define B2062_S_REFPLL_CTL11 B43_LP_SOUTH(0x02E) /* REFPLL Control 11 (south) */ #define B2062_S_REFPLL_CTL12 B43_LP_SOUTH(0x02F) /* REFPLL Control 12 (south) */ #define B2062_S_REFPLL_CTL13 B43_LP_SOUTH(0x030) /* REFPLL Control 13 (south) */ #define B2062_S_REFPLL_CTL14 B43_LP_SOUTH(0x031) /* REFPLL Control 14 (south) */ #define B2062_S_REFPLL_CTL15 B43_LP_SOUTH(0x032) /* REFPLL Control 15 (south) */ #define B2062_S_REFPLL_CTL16 B43_LP_SOUTH(0x033) /* REFPLL Control 16 (south) */ #define B2062_S_RFPLL_CTL0 B43_LP_SOUTH(0x034) /* RFPLL Control 00 (south) */ #define B2062_S_RFPLL_CTL1 B43_LP_SOUTH(0x035) /* RFPLL Control 01 (south) */ #define B2062_S_RFPLL_CTL2 B43_LP_SOUTH(0x036) /* RFPLL Control 02 (south) */ #define B2062_S_RFPLL_CTL3 B43_LP_SOUTH(0x037) /* RFPLL Control 03 (south) */ #define B2062_S_RFPLL_CTL4 B43_LP_SOUTH(0x038) /* RFPLL Control 04 (south) */ #define B2062_S_RFPLL_CTL5 B43_LP_SOUTH(0x039) /* RFPLL Control 05 (south) */ #define B2062_S_RFPLL_CTL6 B43_LP_SOUTH(0x03A) /* RFPLL Control 06 (south) */ #define B2062_S_RFPLL_CTL7 B43_LP_SOUTH(0x03B) /* RFPLL Control 07 (south) */ #define B2062_S_RFPLL_CTL8 B43_LP_SOUTH(0x03C) /* RFPLL Control 08 (south) */ #define B2062_S_RFPLL_CTL9 B43_LP_SOUTH(0x03D) /* RFPLL Control 09 (south) */ #define B2062_S_RFPLL_CTL10 B43_LP_SOUTH(0x03E) /* RFPLL Control 10 (south) */ #define B2062_S_RFPLL_CTL11 B43_LP_SOUTH(0x03F) /* RFPLL Control 11 (south) */ #define B2062_S_RFPLL_CTL12 B43_LP_SOUTH(0x040) /* RFPLL Control 12 (south) */ #define B2062_S_RFPLL_CTL13 B43_LP_SOUTH(0x041) /* RFPLL Control 13 (south) */ #define B2062_S_RFPLL_CTL14 B43_LP_SOUTH(0x042) /* RFPLL Control 14 (south) */ #define B2062_S_RFPLL_CTL15 B43_LP_SOUTH(0x043) /* RFPLL Control 15 (south) */ #define B2062_S_RFPLL_CTL16 B43_LP_SOUTH(0x044) /* RFPLL Control 16 (south) */ #define B2062_S_RFPLL_CTL17 B43_LP_SOUTH(0x045) /* RFPLL Control 17 (south) */ #define B2062_S_RFPLL_CTL18 B43_LP_SOUTH(0x046) /* RFPLL Control 18 (south) */ #define B2062_S_RFPLL_CTL19 B43_LP_SOUTH(0x047) /* RFPLL Control 19 (south) */ #define B2062_S_RFPLL_CTL20 B43_LP_SOUTH(0x048) /* RFPLL Control 20 (south) */ #define B2062_S_RFPLL_CTL21 B43_LP_SOUTH(0x049) /* RFPLL Control 21 (south) */ #define B2062_S_RFPLL_CTL22 B43_LP_SOUTH(0x04A) /* RFPLL Control 22 (south) */ #define B2062_S_RFPLL_CTL23 B43_LP_SOUTH(0x04B) /* RFPLL Control 23 (south) */ #define B2062_S_RFPLL_CTL24 B43_LP_SOUTH(0x04C) /* RFPLL Control 24 (south) */ #define B2062_S_RFPLL_CTL25 B43_LP_SOUTH(0x04D) /* RFPLL Control 25 (south) */ #define B2062_S_RFPLL_CTL26 B43_LP_SOUTH(0x04E) /* RFPLL Control 26 (south) */ #define B2062_S_RFPLL_CTL27 B43_LP_SOUTH(0x04F) /* RFPLL Control 27 (south) */ #define B2062_S_RFPLL_CTL28 B43_LP_SOUTH(0x050) /* RFPLL Control 28 (south) */ #define B2062_S_RFPLL_CTL29 B43_LP_SOUTH(0x051) /* RFPLL Control 29 (south) */ #define B2062_S_RFPLL_CTL30 B43_LP_SOUTH(0x052) /* RFPLL Control 30 (south) */ #define B2062_S_RFPLL_CTL31 B43_LP_SOUTH(0x053) /* RFPLL Control 31 (south) */ #define B2062_S_RFPLL_CTL32 B43_LP_SOUTH(0x054) /* RFPLL Control 32 (south) */ #define B2062_S_RFPLL_CTL33 B43_LP_SOUTH(0x055) /* RFPLL Control 33 (south) */ #define B2062_S_RFPLL_CTL34 B43_LP_SOUTH(0x056) /* RFPLL Control 34 (south) */ #define B2062_S_RXG_CNT0 B43_LP_SOUTH(0x057) /* RXG Counter 00 (south) */ #define B2062_S_RXG_CNT1 B43_LP_SOUTH(0x058) /* RXG Counter 01 (south) */ #define B2062_S_RXG_CNT2 B43_LP_SOUTH(0x059) /* RXG Counter 02 (south) */ #define B2062_S_RXG_CNT3 B43_LP_SOUTH(0x05A) /* RXG Counter 03 (south) */ #define B2062_S_RXG_CNT4 B43_LP_SOUTH(0x05B) /* RXG Counter 04 (south) */ #define B2062_S_RXG_CNT5 B43_LP_SOUTH(0x05C) /* RXG Counter 05 (south) */ #define B2062_S_RXG_CNT6 B43_LP_SOUTH(0x05D) /* RXG Counter 06 (south) */ #define B2062_S_RXG_CNT7 B43_LP_SOUTH(0x05E) /* RXG Counter 07 (south) */ #define B2062_S_RXG_CNT8 B43_LP_SOUTH(0x05F) /* RXG Counter 08 (south) */ #define B2062_S_RXG_CNT9 B43_LP_SOUTH(0x060) /* RXG Counter 09 (south) */ #define B2062_S_RXG_CNT10 B43_LP_SOUTH(0x061) /* RXG Counter 10 (south) */ #define B2062_S_RXG_CNT11 B43_LP_SOUTH(0x062) /* RXG Counter 11 (south) */ #define B2062_S_RXG_CNT12 B43_LP_SOUTH(0x063) /* RXG Counter 12 (south) */ #define B2062_S_RXG_CNT13 B43_LP_SOUTH(0x064) /* RXG Counter 13 (south) */ #define B2062_S_RXG_CNT14 B43_LP_SOUTH(0x065) /* RXG Counter 14 (south) */ #define B2062_S_RXG_CNT15 B43_LP_SOUTH(0x066) /* RXG Counter 15 (south) */ #define B2062_S_RXG_CNT16 B43_LP_SOUTH(0x067) /* RXG Counter 16 (south) */ #define B2062_S_RXG_CNT17 B43_LP_SOUTH(0x068) /* RXG Counter 17 (south) */ /*** Broadcom 2063 radio registers ***/ #define B2063_RADIO_ID_CODE B43_LP_RADIO(0x001) /* Radio ID code */ #define B2063_COMM1 B43_LP_RADIO(0x000) /* Common 01 */ #define B2063_COMM2 B43_LP_RADIO(0x002) /* Common 02 */ #define B2063_COMM3 B43_LP_RADIO(0x003) /* Common 03 */ #define B2063_COMM4 B43_LP_RADIO(0x004) /* Common 04 */ #define B2063_COMM5 B43_LP_RADIO(0x005) /* Common 05 */ #define B2063_COMM6 B43_LP_RADIO(0x006) /* Common 06 */ #define B2063_COMM7 B43_LP_RADIO(0x007) /* Common 07 */ #define B2063_COMM8 B43_LP_RADIO(0x008) /* Common 08 */ #define B2063_COMM9 B43_LP_RADIO(0x009) /* Common 09 */ #define B2063_COMM10 B43_LP_RADIO(0x00A) /* Common 10 */ #define B2063_COMM11 B43_LP_RADIO(0x00B) /* Common 11 */ #define B2063_COMM12 B43_LP_RADIO(0x00C) /* Common 12 */ #define B2063_COMM13 B43_LP_RADIO(0x00D) /* Common 13 */ #define B2063_COMM14 B43_LP_RADIO(0x00E) /* Common 14 */ #define B2063_COMM15 B43_LP_RADIO(0x00F) /* Common 15 */ #define B2063_COMM16 B43_LP_RADIO(0x010) /* Common 16 */ #define B2063_COMM17 B43_LP_RADIO(0x011) /* Common 17 */ #define B2063_COMM18 B43_LP_RADIO(0x012) /* Common 18 */ #define B2063_COMM19 B43_LP_RADIO(0x013) /* Common 19 */ #define B2063_COMM20 B43_LP_RADIO(0x014) /* Common 20 */ #define B2063_COMM21 B43_LP_RADIO(0x015) /* Common 21 */ #define B2063_COMM22 B43_LP_RADIO(0x016) /* Common 22 */ #define B2063_COMM23 B43_LP_RADIO(0x017) /* Common 23 */ #define B2063_COMM24 B43_LP_RADIO(0x018) /* Common 24 */ #define B2063_PWR_SWITCH_CTL B43_LP_RADIO(0x019) /* POWER SWITCH Control */ #define B2063_PLL_SP1 B43_LP_RADIO(0x01A) /* PLL SP 1 */ #define B2063_PLL_SP2 B43_LP_RADIO(0x01B) /* PLL SP 2 */ #define B2063_LOGEN_SP1 B43_LP_RADIO(0x01C) /* LOGEN SP 1 */ #define B2063_LOGEN_SP2 B43_LP_RADIO(0x01D) /* LOGEN SP 2 */ #define B2063_LOGEN_SP3 B43_LP_RADIO(0x01E) /* LOGEN SP 3 */ #define B2063_LOGEN_SP4 B43_LP_RADIO(0x01F) /* LOGEN SP 4 */ #define B2063_LOGEN_SP5 B43_LP_RADIO(0x020) /* LOGEN SP 5 */ #define B2063_G_RX_SP1 B43_LP_RADIO(0x021) /* G RX SP 1 */ #define B2063_G_RX_SP2 B43_LP_RADIO(0x022) /* G RX SP 2 */ #define B2063_G_RX_SP3 B43_LP_RADIO(0x023) /* G RX SP 3 */ #define B2063_G_RX_SP4 B43_LP_RADIO(0x024) /* G RX SP 4 */ #define B2063_G_RX_SP5 B43_LP_RADIO(0x025) /* G RX SP 5 */ #define B2063_G_RX_SP6 B43_LP_RADIO(0x026) /* G RX SP 6 */ #define B2063_G_RX_SP7 B43_LP_RADIO(0x027) /* G RX SP 7 */ #define B2063_G_RX_SP8 B43_LP_RADIO(0x028) /* G RX SP 8 */ #define B2063_G_RX_SP9 B43_LP_RADIO(0x029) /* G RX SP 9 */ #define B2063_G_RX_SP10 B43_LP_RADIO(0x02A) /* G RX SP 10 */ #define B2063_G_RX_SP11 B43_LP_RADIO(0x02B) /* G RX SP 11 */ #define B2063_A_RX_SP1 B43_LP_RADIO(0x02C) /* A RX SP 1 */ #define B2063_A_RX_SP2 B43_LP_RADIO(0x02D) /* A RX SP 2 */ #define B2063_A_RX_SP3 B43_LP_RADIO(0x02E) /* A RX SP 3 */ #define B2063_A_RX_SP4 B43_LP_RADIO(0x02F) /* A RX SP 4 */ #define B2063_A_RX_SP5 B43_LP_RADIO(0x030) /* A RX SP 5 */ #define B2063_A_RX_SP6 B43_LP_RADIO(0x031) /* A RX SP 6 */ #define B2063_A_RX_SP7 B43_LP_RADIO(0x032) /* A RX SP 7 */ #define B2063_RX_BB_SP1 B43_LP_RADIO(0x033) /* RX BB SP 1 */ #define B2063_RX_BB_SP2 B43_LP_RADIO(0x034) /* RX BB SP 2 */ #define B2063_RX_BB_SP3 B43_LP_RADIO(0x035) /* RX BB SP 3 */ #define B2063_RX_BB_SP4 B43_LP_RADIO(0x036) /* RX BB SP 4 */ #define B2063_RX_BB_SP5 B43_LP_RADIO(0x037) /* RX BB SP 5 */ #define B2063_RX_BB_SP6 B43_LP_RADIO(0x038) /* RX BB SP 6 */ #define B2063_RX_BB_SP7 B43_LP_RADIO(0x039) /* RX BB SP 7 */ #define B2063_RX_BB_SP8 B43_LP_RADIO(0x03A) /* RX BB SP 8 */ #define B2063_TX_RF_SP1 B43_LP_RADIO(0x03B) /* TX RF SP 1 */ #define B2063_TX_RF_SP2 B43_LP_RADIO(0x03C) /* TX RF SP 2 */ #define B2063_TX_RF_SP3 B43_LP_RADIO(0x03D) /* TX RF SP 3 */ #define B2063_TX_RF_SP4 B43_LP_RADIO(0x03E) /* TX RF SP 4 */ #define B2063_TX_RF_SP5 B43_LP_RADIO(0x03F) /* TX RF SP 5 */ #define B2063_TX_RF_SP6 B43_LP_RADIO(0x040) /* TX RF SP 6 */ #define B2063_TX_RF_SP7 B43_LP_RADIO(0x041) /* TX RF SP 7 */ #define B2063_TX_RF_SP8 B43_LP_RADIO(0x042) /* TX RF SP 8 */ #define B2063_TX_RF_SP9 B43_LP_RADIO(0x043) /* TX RF SP 9 */ #define B2063_TX_RF_SP10 B43_LP_RADIO(0x044) /* TX RF SP 10 */ #define B2063_TX_RF_SP11 B43_LP_RADIO(0x045) /* TX RF SP 11 */ #define B2063_TX_RF_SP12 B43_LP_RADIO(0x046) /* TX RF SP 12 */ #define B2063_TX_RF_SP13 B43_LP_RADIO(0x047) /* TX RF SP 13 */ #define B2063_TX_RF_SP14 B43_LP_RADIO(0x048) /* TX RF SP 14 */ #define B2063_TX_RF_SP15 B43_LP_RADIO(0x049) /* TX RF SP 15 */ #define B2063_TX_RF_SP16 B43_LP_RADIO(0x04A) /* TX RF SP 16 */ #define B2063_TX_RF_SP17 B43_LP_RADIO(0x04B) /* TX RF SP 17 */ #define B2063_PA_SP1 B43_LP_RADIO(0x04C) /* PA SP 1 */ #define B2063_PA_SP2 B43_LP_RADIO(0x04D) /* PA SP 2 */ #define B2063_PA_SP3 B43_LP_RADIO(0x04E) /* PA SP 3 */ #define B2063_PA_SP4 B43_LP_RADIO(0x04F) /* PA SP 4 */ #define B2063_PA_SP5 B43_LP_RADIO(0x050) /* PA SP 5 */ #define B2063_PA_SP6 B43_LP_RADIO(0x051) /* PA SP 6 */ #define B2063_PA_SP7 B43_LP_RADIO(0x052) /* PA SP 7 */ #define B2063_TX_BB_SP1 B43_LP_RADIO(0x053) /* TX BB SP 1 */ #define B2063_TX_BB_SP2 B43_LP_RADIO(0x054) /* TX BB SP 2 */ #define B2063_TX_BB_SP3 B43_LP_RADIO(0x055) /* TX BB SP 3 */ #define B2063_REG_SP1 B43_LP_RADIO(0x056) /* REG SP 1 */ #define B2063_BANDGAP_CTL1 B43_LP_RADIO(0x057) /* BANDGAP Control 1 */ #define B2063_BANDGAP_CTL2 B43_LP_RADIO(0x058) /* BANDGAP Control 2 */ #define B2063_LPO_CTL1 B43_LP_RADIO(0x059) /* LPO Control 1 */ #define B2063_RC_CALIB_CTL1 B43_LP_RADIO(0x05A) /* RC Calibration Control 1 */ #define B2063_RC_CALIB_CTL2 B43_LP_RADIO(0x05B) /* RC Calibration Control 2 */ #define B2063_RC_CALIB_CTL3 B43_LP_RADIO(0x05C) /* RC Calibration Control 3 */ #define B2063_RC_CALIB_CTL4 B43_LP_RADIO(0x05D) /* RC Calibration Control 4 */ #define B2063_RC_CALIB_CTL5 B43_LP_RADIO(0x05E) /* RC Calibration Control 5 */ #define B2063_RC_CALIB_CTL6 B43_LP_RADIO(0x05F) /* RC Calibration Control 6 */ #define B2063_RC_CALIB_CTL7 B43_LP_RADIO(0x060) /* RC Calibration Control 7 */ #define B2063_RC_CALIB_CTL8 B43_LP_RADIO(0x061) /* RC Calibration Control 8 */ #define B2063_RC_CALIB_CTL9 B43_LP_RADIO(0x062) /* RC Calibration Control 9 */ #define B2063_RC_CALIB_CTL10 B43_LP_RADIO(0x063) /* RC Calibration Control 10 */ #define B2063_PLL_JTAG_CALNRST B43_LP_RADIO(0x064) /* PLL JTAG CALNRST */ #define B2063_PLL_JTAG_IN_PLL1 B43_LP_RADIO(0x065) /* PLL JTAG IN PLL 1 */ #define B2063_PLL_JTAG_IN_PLL2 B43_LP_RADIO(0x066) /* PLL JTAG IN PLL 2 */ #define B2063_PLL_JTAG_PLL_CP1 B43_LP_RADIO(0x067) /* PLL JTAG PLL CP 1 */ #define B2063_PLL_JTAG_PLL_CP2 B43_LP_RADIO(0x068) /* PLL JTAG PLL CP 2 */ #define B2063_PLL_JTAG_PLL_CP3 B43_LP_RADIO(0x069) /* PLL JTAG PLL CP 3 */ #define B2063_PLL_JTAG_PLL_CP4 B43_LP_RADIO(0x06A) /* PLL JTAG PLL CP 4 */ #define B2063_PLL_JTAG_PLL_CTL1 B43_LP_RADIO(0x06B) /* PLL JTAG PLL Control 1 */ #define B2063_PLL_JTAG_PLL_LF1 B43_LP_RADIO(0x06C) /* PLL JTAG PLL LF 1 */ #define B2063_PLL_JTAG_PLL_LF2 B43_LP_RADIO(0x06D) /* PLL JTAG PLL LF 2 */ #define B2063_PLL_JTAG_PLL_LF3 B43_LP_RADIO(0x06E) /* PLL JTAG PLL LF 3 */ #define B2063_PLL_JTAG_PLL_LF4 B43_LP_RADIO(0x06F) /* PLL JTAG PLL LF 4 */ #define B2063_PLL_JTAG_PLL_SG1 B43_LP_RADIO(0x070) /* PLL JTAG PLL SG 1 */ #define B2063_PLL_JTAG_PLL_SG2 B43_LP_RADIO(0x071) /* PLL JTAG PLL SG 2 */ #define B2063_PLL_JTAG_PLL_SG3 B43_LP_RADIO(0x072) /* PLL JTAG PLL SG 3 */ #define B2063_PLL_JTAG_PLL_SG4 B43_LP_RADIO(0x073) /* PLL JTAG PLL SG 4 */ #define B2063_PLL_JTAG_PLL_SG5 B43_LP_RADIO(0x074) /* PLL JTAG PLL SG 5 */ #define B2063_PLL_JTAG_PLL_VCO1 B43_LP_RADIO(0x075) /* PLL JTAG PLL VCO 1 */ #define B2063_PLL_JTAG_PLL_VCO2 B43_LP_RADIO(0x076) /* PLL JTAG PLL VCO 2 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB1 B43_LP_RADIO(0x077) /* PLL JTAG PLL VCO Calibration 1 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB2 B43_LP_RADIO(0x078) /* PLL JTAG PLL VCO Calibration 2 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB3 B43_LP_RADIO(0x079) /* PLL JTAG PLL VCO Calibration 3 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB4 B43_LP_RADIO(0x07A) /* PLL JTAG PLL VCO Calibration 4 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB5 B43_LP_RADIO(0x07B) /* PLL JTAG PLL VCO Calibration 5 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB6 B43_LP_RADIO(0x07C) /* PLL JTAG PLL VCO Calibration 6 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB7 B43_LP_RADIO(0x07D) /* PLL JTAG PLL VCO Calibration 7 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB8 B43_LP_RADIO(0x07E) /* PLL JTAG PLL VCO Calibration 8 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB9 B43_LP_RADIO(0x07F) /* PLL JTAG PLL VCO Calibration 9 */ #define B2063_PLL_JTAG_PLL_VCO_CALIB10 B43_LP_RADIO(0x080) /* PLL JTAG PLL VCO Calibration 10 */ #define B2063_PLL_JTAG_PLL_XTAL_12 B43_LP_RADIO(0x081) /* PLL JTAG PLL XTAL 1 2 */ #define B2063_PLL_JTAG_PLL_XTAL3 B43_LP_RADIO(0x082) /* PLL JTAG PLL XTAL 3 */ #define B2063_LOGEN_ACL1 B43_LP_RADIO(0x083) /* LOGEN ACL 1 */ #define B2063_LOGEN_ACL2 B43_LP_RADIO(0x084) /* LOGEN ACL 2 */ #define B2063_LOGEN_ACL3 B43_LP_RADIO(0x085) /* LOGEN ACL 3 */ #define B2063_LOGEN_ACL4 B43_LP_RADIO(0x086) /* LOGEN ACL 4 */ #define B2063_LOGEN_ACL5 B43_LP_RADIO(0x087) /* LOGEN ACL 5 */ #define B2063_LO_CALIB_INPUTS B43_LP_RADIO(0x088) /* LO Calibration INPUTS */ #define B2063_LO_CALIB_CTL1 B43_LP_RADIO(0x089) /* LO Calibration Control 1 */ #define B2063_LO_CALIB_CTL2 B43_LP_RADIO(0x08A) /* LO Calibration Control 2 */ #define B2063_LO_CALIB_CTL3 B43_LP_RADIO(0x08B) /* LO Calibration Control 3 */ #define B2063_LO_CALIB_WAITCNT B43_LP_RADIO(0x08C) /* LO Calibration WAITCNT */ #define B2063_LO_CALIB_OVR1 B43_LP_RADIO(0x08D) /* LO Calibration OVR 1 */ #define B2063_LO_CALIB_OVR2 B43_LP_RADIO(0x08E) /* LO Calibration OVR 2 */ #define B2063_LO_CALIB_OVAL1 B43_LP_RADIO(0x08F) /* LO Calibration OVAL 1 */ #define B2063_LO_CALIB_OVAL2 B43_LP_RADIO(0x090) /* LO Calibration OVAL 2 */ #define B2063_LO_CALIB_OVAL3 B43_LP_RADIO(0x091) /* LO Calibration OVAL 3 */ #define B2063_LO_CALIB_OVAL4 B43_LP_RADIO(0x092) /* LO Calibration OVAL 4 */ #define B2063_LO_CALIB_OVAL5 B43_LP_RADIO(0x093) /* LO Calibration OVAL 5 */ #define B2063_LO_CALIB_OVAL6 B43_LP_RADIO(0x094) /* LO Calibration OVAL 6 */ #define B2063_LO_CALIB_OVAL7 B43_LP_RADIO(0x095) /* LO Calibration OVAL 7 */ #define B2063_LO_CALIB_CALVLD1 B43_LP_RADIO(0x096) /* LO Calibration CALVLD 1 */ #define B2063_LO_CALIB_CALVLD2 B43_LP_RADIO(0x097) /* LO Calibration CALVLD 2 */ #define B2063_LO_CALIB_CVAL1 B43_LP_RADIO(0x098) /* LO Calibration CVAL 1 */ #define B2063_LO_CALIB_CVAL2 B43_LP_RADIO(0x099) /* LO Calibration CVAL 2 */ #define B2063_LO_CALIB_CVAL3 B43_LP_RADIO(0x09A) /* LO Calibration CVAL 3 */ #define B2063_LO_CALIB_CVAL4 B43_LP_RADIO(0x09B) /* LO Calibration CVAL 4 */ #define B2063_LO_CALIB_CVAL5 B43_LP_RADIO(0x09C) /* LO Calibration CVAL 5 */ #define B2063_LO_CALIB_CVAL6 B43_LP_RADIO(0x09D) /* LO Calibration CVAL 6 */ #define B2063_LO_CALIB_CVAL7 B43_LP_RADIO(0x09E) /* LO Calibration CVAL 7 */ #define B2063_LOGEN_CALIB_EN B43_LP_RADIO(0x09F) /* LOGEN Calibration EN */ #define B2063_LOGEN_PEAKDET1 B43_LP_RADIO(0x0A0) /* LOGEN PEAKDET 1 */ #define B2063_LOGEN_RCCR1 B43_LP_RADIO(0x0A1) /* LOGEN RCCR 1 */ #define B2063_LOGEN_VCOBUF1 B43_LP_RADIO(0x0A2) /* LOGEN VCOBUF 1 */ #define B2063_LOGEN_MIXER1 B43_LP_RADIO(0x0A3) /* LOGEN MIXER 1 */ #define B2063_LOGEN_MIXER2 B43_LP_RADIO(0x0A4) /* LOGEN MIXER 2 */ #define B2063_LOGEN_BUF1 B43_LP_RADIO(0x0A5) /* LOGEN BUF 1 */ #define B2063_LOGEN_BUF2 B43_LP_RADIO(0x0A6) /* LOGEN BUF 2 */ #define B2063_LOGEN_DIV1 B43_LP_RADIO(0x0A7) /* LOGEN DIV 1 */ #define B2063_LOGEN_DIV2 B43_LP_RADIO(0x0A8) /* LOGEN DIV 2 */ #define B2063_LOGEN_DIV3 B43_LP_RADIO(0x0A9) /* LOGEN DIV 3 */ #define B2063_LOGEN_CBUFRX1 B43_LP_RADIO(0x0AA) /* LOGEN CBUFRX 1 */ #define B2063_LOGEN_CBUFRX2 B43_LP_RADIO(0x0AB) /* LOGEN CBUFRX 2 */ #define B2063_LOGEN_CBUFTX1 B43_LP_RADIO(0x0AC) /* LOGEN CBUFTX 1 */ #define B2063_LOGEN_CBUFTX2 B43_LP_RADIO(0x0AD) /* LOGEN CBUFTX 2 */ #define B2063_LOGEN_IDAC1 B43_LP_RADIO(0x0AE) /* LOGEN IDAC 1 */ #define B2063_LOGEN_SPARE1 B43_LP_RADIO(0x0AF) /* LOGEN SPARE 1 */ #define B2063_LOGEN_SPARE2 B43_LP_RADIO(0x0B0) /* LOGEN SPARE 2 */ #define B2063_LOGEN_SPARE3 B43_LP_RADIO(0x0B1) /* LOGEN SPARE 3 */ #define B2063_G_RX_1ST1 B43_LP_RADIO(0x0B2) /* G RX 1ST 1 */ #define B2063_G_RX_1ST2 B43_LP_RADIO(0x0B3) /* G RX 1ST 2 */ #define B2063_G_RX_1ST3 B43_LP_RADIO(0x0B4) /* G RX 1ST 3 */ #define B2063_G_RX_2ND1 B43_LP_RADIO(0x0B5) /* G RX 2ND 1 */ #define B2063_G_RX_2ND2 B43_LP_RADIO(0x0B6) /* G RX 2ND 2 */ #define B2063_G_RX_2ND3 B43_LP_RADIO(0x0B7) /* G RX 2ND 3 */ #define B2063_G_RX_2ND4 B43_LP_RADIO(0x0B8) /* G RX 2ND 4 */ #define B2063_G_RX_2ND5 B43_LP_RADIO(0x0B9) /* G RX 2ND 5 */ #define B2063_G_RX_2ND6 B43_LP_RADIO(0x0BA) /* G RX 2ND 6 */ #define B2063_G_RX_2ND7 B43_LP_RADIO(0x0BB) /* G RX 2ND 7 */ #define B2063_G_RX_2ND8 B43_LP_RADIO(0x0BC) /* G RX 2ND 8 */ #define B2063_G_RX_PS1 B43_LP_RADIO(0x0BD) /* G RX PS 1 */ #define B2063_G_RX_PS2 B43_LP_RADIO(0x0BE) /* G RX PS 2 */ #define B2063_G_RX_PS3 B43_LP_RADIO(0x0BF) /* G RX PS 3 */ #define B2063_G_RX_PS4 B43_LP_RADIO(0x0C0) /* G RX PS 4 */ #define B2063_G_RX_PS5 B43_LP_RADIO(0x0C1) /* G RX PS 5 */ #define B2063_G_RX_MIX1 B43_LP_RADIO(0x0C2) /* G RX MIX 1 */ #define B2063_G_RX_MIX2 B43_LP_RADIO(0x0C3) /* G RX MIX 2 */ #define B2063_G_RX_MIX3 B43_LP_RADIO(0x0C4) /* G RX MIX 3 */ #define B2063_G_RX_MIX4 B43_LP_RADIO(0x0C5) /* G RX MIX 4 */ #define B2063_G_RX_MIX5 B43_LP_RADIO(0x0C6) /* G RX MIX 5 */ #define B2063_G_RX_MIX6 B43_LP_RADIO(0x0C7) /* G RX MIX 6 */ #define B2063_G_RX_MIX7 B43_LP_RADIO(0x0C8) /* G RX MIX 7 */ #define B2063_G_RX_MIX8 B43_LP_RADIO(0x0C9) /* G RX MIX 8 */ #define B2063_G_RX_PDET1 B43_LP_RADIO(0x0CA) /* G RX PDET 1 */ #define B2063_G_RX_SPARES1 B43_LP_RADIO(0x0CB) /* G RX SPARES 1 */ #define B2063_G_RX_SPARES2 B43_LP_RADIO(0x0CC) /* G RX SPARES 2 */ #define B2063_G_RX_SPARES3 B43_LP_RADIO(0x0CD) /* G RX SPARES 3 */ #define B2063_A_RX_1ST1 B43_LP_RADIO(0x0CE) /* A RX 1ST 1 */ #define B2063_A_RX_1ST2 B43_LP_RADIO(0x0CF) /* A RX 1ST 2 */ #define B2063_A_RX_1ST3 B43_LP_RADIO(0x0D0) /* A RX 1ST 3 */ #define B2063_A_RX_1ST4 B43_LP_RADIO(0x0D1) /* A RX 1ST 4 */ #define B2063_A_RX_1ST5 B43_LP_RADIO(0x0D2) /* A RX 1ST 5 */ #define B2063_A_RX_2ND1 B43_LP_RADIO(0x0D3) /* A RX 2ND 1 */ #define B2063_A_RX_2ND2 B43_LP_RADIO(0x0D4) /* A RX 2ND 2 */ #define B2063_A_RX_2ND3 B43_LP_RADIO(0x0D5) /* A RX 2ND 3 */ #define B2063_A_RX_2ND4 B43_LP_RADIO(0x0D6) /* A RX 2ND 4 */ #define B2063_A_RX_2ND5 B43_LP_RADIO(0x0D7) /* A RX 2ND 5 */ #define B2063_A_RX_2ND6 B43_LP_RADIO(0x0D8) /* A RX 2ND 6 */ #define B2063_A_RX_2ND7 B43_LP_RADIO(0x0D9) /* A RX 2ND 7 */ #define B2063_A_RX_PS1 B43_LP_RADIO(0x0DA) /* A RX PS 1 */ #define B2063_A_RX_PS2 B43_LP_RADIO(0x0DB) /* A RX PS 2 */ #define B2063_A_RX_PS3 B43_LP_RADIO(0x0DC) /* A RX PS 3 */ #define B2063_A_RX_PS4 B43_LP_RADIO(0x0DD) /* A RX PS 4 */ #define B2063_A_RX_PS5 B43_LP_RADIO(0x0DE) /* A RX PS 5 */ #define B2063_A_RX_PS6 B43_LP_RADIO(0x0DF) /* A RX PS 6 */ #define B2063_A_RX_MIX1 B43_LP_RADIO(0x0E0) /* A RX MIX 1 */ #define B2063_A_RX_MIX2 B43_LP_RADIO(0x0E1) /* A RX MIX 2 */ #define B2063_A_RX_MIX3 B43_LP_RADIO(0x0E2) /* A RX MIX 3 */ #define B2063_A_RX_MIX4 B43_LP_RADIO(0x0E3) /* A RX MIX 4 */ #define B2063_A_RX_MIX5 B43_LP_RADIO(0x0E4) /* A RX MIX 5 */ #define B2063_A_RX_MIX6 B43_LP_RADIO(0x0E5) /* A RX MIX 6 */ #define B2063_A_RX_MIX7 B43_LP_RADIO(0x0E6) /* A RX MIX 7 */ #define B2063_A_RX_MIX8 B43_LP_RADIO(0x0E7) /* A RX MIX 8 */ #define B2063_A_RX_PWRDET1 B43_LP_RADIO(0x0E8) /* A RX PWRDET 1 */ #define B2063_A_RX_SPARE1 B43_LP_RADIO(0x0E9) /* A RX SPARE 1 */ #define B2063_A_RX_SPARE2 B43_LP_RADIO(0x0EA) /* A RX SPARE 2 */ #define B2063_A_RX_SPARE3 B43_LP_RADIO(0x0EB) /* A RX SPARE 3 */ #define B2063_RX_TIA_CTL1 B43_LP_RADIO(0x0EC) /* RX TIA Control 1 */ #define B2063_RX_TIA_CTL2 B43_LP_RADIO(0x0ED) /* RX TIA Control 2 */ #define B2063_RX_TIA_CTL3 B43_LP_RADIO(0x0EE) /* RX TIA Control 3 */ #define B2063_RX_TIA_CTL4 B43_LP_RADIO(0x0EF) /* RX TIA Control 4 */ #define B2063_RX_TIA_CTL5 B43_LP_RADIO(0x0F0) /* RX TIA Control 5 */ #define B2063_RX_TIA_CTL6 B43_LP_RADIO(0x0F1) /* RX TIA Control 6 */ #define B2063_RX_BB_CTL1 B43_LP_RADIO(0x0F2) /* RX BB Control 1 */ #define B2063_RX_BB_CTL2 B43_LP_RADIO(0x0F3) /* RX BB Control 2 */ #define B2063_RX_BB_CTL3 B43_LP_RADIO(0x0F4) /* RX BB Control 3 */ #define B2063_RX_BB_CTL4 B43_LP_RADIO(0x0F5) /* RX BB Control 4 */ #define B2063_RX_BB_CTL5 B43_LP_RADIO(0x0F6) /* RX BB Control 5 */ #define B2063_RX_BB_CTL6 B43_LP_RADIO(0x0F7) /* RX BB Control 6 */ #define B2063_RX_BB_CTL7 B43_LP_RADIO(0x0F8) /* RX BB Control 7 */ #define B2063_RX_BB_CTL8 B43_LP_RADIO(0x0F9) /* RX BB Control 8 */ #define B2063_RX_BB_CTL9 B43_LP_RADIO(0x0FA) /* RX BB Control 9 */ #define B2063_TX_RF_CTL1 B43_LP_RADIO(0x0FB) /* TX RF Control 1 */ #define B2063_TX_RF_IDAC_LO_RF_I B43_LP_RADIO(0x0FC) /* TX RF IDAC LO RF I */ #define B2063_TX_RF_IDAC_LO_RF_Q B43_LP_RADIO(0x0FD) /* TX RF IDAC LO RF Q */ #define B2063_TX_RF_IDAC_LO_BB_I B43_LP_RADIO(0x0FE) /* TX RF IDAC LO BB I */ #define B2063_TX_RF_IDAC_LO_BB_Q B43_LP_RADIO(0x0FF) /* TX RF IDAC LO BB Q */ #define B2063_TX_RF_CTL2 B43_LP_RADIO(0x100) /* TX RF Control 2 */ #define B2063_TX_RF_CTL3 B43_LP_RADIO(0x101) /* TX RF Control 3 */ #define B2063_TX_RF_CTL4 B43_LP_RADIO(0x102) /* TX RF Control 4 */ #define B2063_TX_RF_CTL5 B43_LP_RADIO(0x103) /* TX RF Control 5 */ #define B2063_TX_RF_CTL6 B43_LP_RADIO(0x104) /* TX RF Control 6 */ #define B2063_TX_RF_CTL7 B43_LP_RADIO(0x105) /* TX RF Control 7 */ #define B2063_TX_RF_CTL8 B43_LP_RADIO(0x106) /* TX RF Control 8 */ #define B2063_TX_RF_CTL9 B43_LP_RADIO(0x107) /* TX RF Control 9 */ #define B2063_TX_RF_CTL10 B43_LP_RADIO(0x108) /* TX RF Control 10 */ #define B2063_TX_RF_CTL14 B43_LP_RADIO(0x109) /* TX RF Control 14 */ #define B2063_TX_RF_CTL15 B43_LP_RADIO(0x10A) /* TX RF Control 15 */ #define B2063_PA_CTL1 B43_LP_RADIO(0x10B) /* PA Control 1 */ #define B2063_PA_CTL2 B43_LP_RADIO(0x10C) /* PA Control 2 */ #define B2063_PA_CTL3 B43_LP_RADIO(0x10D) /* PA Control 3 */ #define B2063_PA_CTL4 B43_LP_RADIO(0x10E) /* PA Control 4 */ #define B2063_PA_CTL5 B43_LP_RADIO(0x10F) /* PA Control 5 */ #define B2063_PA_CTL6 B43_LP_RADIO(0x110) /* PA Control 6 */ #define B2063_PA_CTL7 B43_LP_RADIO(0x111) /* PA Control 7 */ #define B2063_PA_CTL8 B43_LP_RADIO(0x112) /* PA Control 8 */ #define B2063_PA_CTL9 B43_LP_RADIO(0x113) /* PA Control 9 */ #define B2063_PA_CTL10 B43_LP_RADIO(0x114) /* PA Control 10 */ #define B2063_PA_CTL11 B43_LP_RADIO(0x115) /* PA Control 11 */ #define B2063_PA_CTL12 B43_LP_RADIO(0x116) /* PA Control 12 */ #define B2063_PA_CTL13 B43_LP_RADIO(0x117) /* PA Control 13 */ #define B2063_TX_BB_CTL1 B43_LP_RADIO(0x118) /* TX BB Control 1 */ #define B2063_TX_BB_CTL2 B43_LP_RADIO(0x119) /* TX BB Control 2 */ #define B2063_TX_BB_CTL3 B43_LP_RADIO(0x11A) /* TX BB Control 3 */ #define B2063_TX_BB_CTL4 B43_LP_RADIO(0x11B) /* TX BB Control 4 */ #define B2063_GPIO_CTL1 B43_LP_RADIO(0x11C) /* GPIO Control 1 */ #define B2063_VREG_CTL1 B43_LP_RADIO(0x11D) /* VREG Control 1 */ #define B2063_AMUX_CTL1 B43_LP_RADIO(0x11E) /* AMUX Control 1 */ #define B2063_IQ_CALIB_GVAR B43_LP_RADIO(0x11F) /* IQ Calibration GVAR */ #define B2063_IQ_CALIB_CTL1 B43_LP_RADIO(0x120) /* IQ Calibration Control 1 */ #define B2063_IQ_CALIB_CTL2 B43_LP_RADIO(0x121) /* IQ Calibration Control 2 */ #define B2063_TEMPSENSE_CTL1 B43_LP_RADIO(0x122) /* TEMPSENSE Control 1 */ #define B2063_TEMPSENSE_CTL2 B43_LP_RADIO(0x123) /* TEMPSENSE Control 2 */ #define B2063_TX_RX_LOOPBACK1 B43_LP_RADIO(0x124) /* TX/RX LOOPBACK 1 */ #define B2063_TX_RX_LOOPBACK2 B43_LP_RADIO(0x125) /* TX/RX LOOPBACK 2 */ #define B2063_EXT_TSSI_CTL1 B43_LP_RADIO(0x126) /* EXT TSSI Control 1 */ #define B2063_EXT_TSSI_CTL2 B43_LP_RADIO(0x127) /* EXT TSSI Control 2 */ #define B2063_AFE_CTL B43_LP_RADIO(0x128) /* AFE Control */ enum b43_lpphy_txpctl_mode { B43_LPPHY_TXPCTL_UNKNOWN = 0, B43_LPPHY_TXPCTL_OFF, /* TX power control is OFF */ B43_LPPHY_TXPCTL_SW, /* TX power control is set to Software */ B43_LPPHY_TXPCTL_HW, /* TX power control is set to Hardware */ }; struct b43_phy_lp { /* Current TX power control mode. */ enum b43_lpphy_txpctl_mode txpctl_mode; /* Transmit isolation medium band */ u8 tx_isolation_med_band; /* Transmit isolation low band */ u8 tx_isolation_low_band; /* Transmit isolation high band */ u8 tx_isolation_hi_band; /* Max transmit power medium band */ u16 max_tx_pwr_med_band; /* Max transmit power low band */ u16 max_tx_pwr_low_band; /* Max transmit power high band */ u16 max_tx_pwr_hi_band; /* FIXME What are these used for? */ /* FIXME Is 15 the correct array size? */ u16 tx_max_rate[15]; u16 tx_max_ratel[15]; u16 tx_max_rateh[15]; /* Transmit power arrays */ s16 txpa[3], txpal[3], txpah[3]; /* Receive power offset */ u8 rx_pwr_offset; /* TSSI transmit count */ u16 tssi_tx_count; /* TSSI index */ u16 tssi_idx; /* FIXME initial value? */ /* TSSI npt */ u16 tssi_npt; /* FIXME initial value? */ /* Target TX frequency */ u16 tgt_tx_freq; /* FIXME initial value? */ /* Transmit power index override */ s8 tx_pwr_idx_over; /* FIXME initial value? */ /* RSSI vf */ u8 rssi_vf; /* RSSI vc */ u8 rssi_vc; /* RSSI gs */ u8 rssi_gs; /* RC cap */ u8 rc_cap; /* BX arch */ u8 bx_arch; /* Full calibration channel */ u8 full_calib_chan; /* Transmit iqlocal best coeffs */ bool tx_iqloc_best_coeffs_valid; u8 tx_iqloc_best_coeffs[11]; /* Used for "Save/Restore Dig Filt State" */ u16 dig_flt_state[9]; bool crs_usr_disable, crs_sys_disable; unsigned int pdiv; /* The channel we are tuned to */ u8 channel; /* The active antenna diversity mode */ int antenna; /* Frequency of the active TX tone */ int tx_tone_freq; }; enum tssi_mux_mode { TSSI_MUX_PREPA, TSSI_MUX_POSTPA, TSSI_MUX_EXT, }; struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_lp; #endif /* LINUX_B43_PHY_LP_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_lp.c0000644000175000017500000030242012026211315022511 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11a/g LP-PHY driver Copyright (c) 2008-2009 Michael Buesch Copyright (c) 2009 Gábor Stefanik This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "b43.h" #include "main.h" #include "phy_lp.h" #include "phy_common.h" #include "tables_lpphy.h" static inline u16 channel2freq_lp(u8 channel) { if (channel < 14) return (2407 + 5 * channel); else if (channel == 14) return 2484; else if (channel < 184) return (5000 + 5 * channel); else return (4000 + 5 * channel); } static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) return 1; return 36; } static int b43_lpphy_op_allocate(struct b43_wldev *dev) { struct b43_phy_lp *lpphy; lpphy = kzalloc(sizeof(*lpphy), GFP_KERNEL); if (!lpphy) return -ENOMEM; dev->phy.lp = lpphy; return 0; } static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_lp *lpphy = phy->lp; memset(lpphy, 0, sizeof(*lpphy)); lpphy->antenna = B43_ANTENNA_DEFAULT; //TODO } static void b43_lpphy_op_free(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; kfree(lpphy); dev->phy.lp = NULL; } /* http://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */ static void lpphy_read_band_sprom(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy_lp *lpphy = dev->phy.lp; u16 cckpo, maxpwr; u32 ofdmpo; int i; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { lpphy->tx_isolation_med_band = sprom->tri2g; lpphy->bx_arch = sprom->bxa2g; lpphy->rx_pwr_offset = sprom->rxpo2g; lpphy->rssi_vf = sprom->rssismf2g; lpphy->rssi_vc = sprom->rssismc2g; lpphy->rssi_gs = sprom->rssisav2g; lpphy->txpa[0] = sprom->pa0b0; lpphy->txpa[1] = sprom->pa0b1; lpphy->txpa[2] = sprom->pa0b2; maxpwr = sprom->maxpwr_bg; lpphy->max_tx_pwr_med_band = maxpwr; cckpo = sprom->cck2gpo; /* * We don't read SPROM's opo as specs say. On rev8 SPROMs * opo == ofdm2gpo and we don't know any SSB with LP-PHY * and SPROM rev below 8. */ B43_WARN_ON(sprom->revision < 8); ofdmpo = sprom->ofdm2gpo; if (cckpo) { for (i = 0; i < 4; i++) { lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2; ofdmpo >>= 4; } ofdmpo = sprom->ofdm2gpo; for (i = 4; i < 15; i++) { lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2; ofdmpo >>= 4; } } else { ofdmpo &= 0xFF; for (i = 0; i < 4; i++) lpphy->tx_max_rate[i] = maxpwr; for (i = 4; i < 15; i++) lpphy->tx_max_rate[i] = maxpwr - ofdmpo; } } else { /* 5GHz */ lpphy->tx_isolation_low_band = sprom->tri5gl; lpphy->tx_isolation_med_band = sprom->tri5g; lpphy->tx_isolation_hi_band = sprom->tri5gh; lpphy->bx_arch = sprom->bxa5g; lpphy->rx_pwr_offset = sprom->rxpo5g; lpphy->rssi_vf = sprom->rssismf5g; lpphy->rssi_vc = sprom->rssismc5g; lpphy->rssi_gs = sprom->rssisav5g; lpphy->txpa[0] = sprom->pa1b0; lpphy->txpa[1] = sprom->pa1b1; lpphy->txpa[2] = sprom->pa1b2; lpphy->txpal[0] = sprom->pa1lob0; lpphy->txpal[1] = sprom->pa1lob1; lpphy->txpal[2] = sprom->pa1lob2; lpphy->txpah[0] = sprom->pa1hib0; lpphy->txpah[1] = sprom->pa1hib1; lpphy->txpah[2] = sprom->pa1hib2; maxpwr = sprom->maxpwr_al; ofdmpo = sprom->ofdm5glpo; lpphy->max_tx_pwr_low_band = maxpwr; for (i = 4; i < 12; i++) { lpphy->tx_max_ratel[i] = maxpwr - (ofdmpo & 0xF) * 2; ofdmpo >>= 4; } maxpwr = sprom->maxpwr_a; ofdmpo = sprom->ofdm5gpo; lpphy->max_tx_pwr_med_band = maxpwr; for (i = 4; i < 12; i++) { lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2; ofdmpo >>= 4; } maxpwr = sprom->maxpwr_ah; ofdmpo = sprom->ofdm5ghpo; lpphy->max_tx_pwr_hi_band = maxpwr; for (i = 4; i < 12; i++) { lpphy->tx_max_rateh[i] = maxpwr - (ofdmpo & 0xF) * 2; ofdmpo >>= 4; } } } static void lpphy_adjust_gain_table(struct b43_wldev *dev, u32 freq) { struct b43_phy_lp *lpphy = dev->phy.lp; u16 temp[3]; u16 isolation; B43_WARN_ON(dev->phy.rev >= 2); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) isolation = lpphy->tx_isolation_med_band; else if (freq <= 5320) isolation = lpphy->tx_isolation_low_band; else if (freq <= 5700) isolation = lpphy->tx_isolation_med_band; else isolation = lpphy->tx_isolation_hi_band; temp[0] = ((isolation - 26) / 12) << 12; temp[1] = temp[0] + 0x1000; temp[2] = temp[0] + 0x2000; b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), 3, temp); b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), 3, temp); } static void lpphy_table_init(struct b43_wldev *dev) { u32 freq = channel2freq_lp(b43_lpphy_op_get_default_chan(dev)); if (dev->phy.rev < 2) lpphy_rev0_1_table_init(dev); else lpphy_rev2plus_table_init(dev); lpphy_init_tx_gain_table(dev); if (dev->phy.rev < 2) lpphy_adjust_gain_table(dev, freq); } static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev) { struct ssb_bus *bus = dev->dev->sdev->bus; struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy_lp *lpphy = dev->phy.lp; u16 tmp, tmp2; b43_phy_mask(dev, B43_LPPHY_AFE_DAC_CTL, 0xF7FF); b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0); b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0); b43_phy_set(dev, B43_LPPHY_AFE_DAC_CTL, 0x0004); b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x0078); b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800); b43_phy_write(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x0016); b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_0, 0xFFF8, 0x0004); b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5400); b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2400); b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100); b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0x0006); b43_phy_mask(dev, B43_LPPHY_RX_RADIO_CTL, 0xFFFE); b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x0005); b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0x0180); b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x3C00); b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFFF0, 0x0005); b43_phy_maskset(dev, B43_LPPHY_GAIN_MISMATCH_LIMIT, 0xFFC0, 0x001A); b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0x00B3); b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00); b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, 0xFF00, lpphy->rx_pwr_offset); if ((sprom->boardflags_lo & B43_BFL_FEM) && ((b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) || (sprom->boardflags_hi & B43_BFH_PAREF))) { ssb_pmu_set_ldo_voltage(&bus->chipco, LDO_PAREF, 0x28); ssb_pmu_set_ldo_paref(&bus->chipco, true); if (dev->phy.rev == 0) { b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, 0xFFCF, 0x0010); } b43_lptab_write(dev, B43_LPTAB16(11, 7), 60); } else { ssb_pmu_set_ldo_paref(&bus->chipco, false); b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, 0xFFCF, 0x0020); b43_lptab_write(dev, B43_LPTAB16(11, 7), 100); } tmp = lpphy->rssi_vf | lpphy->rssi_vc << 4 | 0xA000; b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, tmp); if (sprom->boardflags_hi & B43_BFH_RSSIINV) b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x0AAA); else b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x02AA); b43_lptab_write(dev, B43_LPTAB16(11, 1), 24); b43_phy_maskset(dev, B43_LPPHY_RX_RADIO_CTL, 0xFFF9, (lpphy->bx_arch << 1)); if (dev->phy.rev == 1 && (sprom->boardflags_hi & B43_BFH_FEM_BT)) { b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0x3F00, 0x0900); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0400); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0B00); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xC0FF, 0x0900); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xC0FF, 0x0B00); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xC0FF, 0x0900); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xC0FF, 0x0B00); } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ || (dev->dev->board_type == 0x048A) || ((dev->phy.rev == 0) && (sprom->boardflags_lo & B43_BFL_FEM))) { b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0001); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0400); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0001); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0500); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0800); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0A00); } else if (dev->phy.rev == 1 || (sprom->boardflags_lo & B43_BFL_FEM)) { b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0004); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0800); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0004); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0C00); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0100); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0300); } else { b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0900); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0006); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0500); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0006); b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0700); } if (dev->phy.rev == 1 && (sprom->boardflags_hi & B43_BFH_PAREF)) { b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_5, B43_LPPHY_TR_LOOKUP_1); b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_6, B43_LPPHY_TR_LOOKUP_2); b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_7, B43_LPPHY_TR_LOOKUP_3); b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_8, B43_LPPHY_TR_LOOKUP_4); } if ((sprom->boardflags_hi & B43_BFH_FEM_BT) && (dev->dev->chip_id == 0x5354) && (dev->dev->chip_pkg == SSB_CHIPPACK_BCM4712S)) { b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0006); b43_phy_write(dev, B43_LPPHY_GPIO_SELECT, 0x0005); b43_phy_write(dev, B43_LPPHY_GPIO_OUTEN, 0xFFFF); //FIXME the Broadcom driver caches & delays this HF write! b43_hf_write(dev, b43_hf_read(dev) | B43_HF_PR45960W); } if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x8000); b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0040); b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0xA400); b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0x0B00); b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x0007); b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFF8, 0x0003); b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFC7, 0x0020); b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); } else { /* 5GHz */ b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0x7FFF); b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFBF); } if (dev->phy.rev == 1) { tmp = b43_phy_read(dev, B43_LPPHY_CLIPCTRTHRESH); tmp2 = (tmp & 0x03E0) >> 5; tmp2 |= tmp2 << 5; b43_phy_write(dev, B43_LPPHY_4C3, tmp2); tmp = b43_phy_read(dev, B43_LPPHY_GAINDIRECTMISMATCH); tmp2 = (tmp & 0x1F00) >> 8; tmp2 |= tmp2 << 5; b43_phy_write(dev, B43_LPPHY_4C4, tmp2); tmp = b43_phy_read(dev, B43_LPPHY_VERYLOWGAINDB); tmp2 = tmp & 0x00FF; tmp2 |= tmp << 8; b43_phy_write(dev, B43_LPPHY_4C5, tmp2); } } static void lpphy_save_dig_flt_state(struct b43_wldev *dev) { static const u16 addr[] = { B43_PHY_OFDM(0xC1), B43_PHY_OFDM(0xC2), B43_PHY_OFDM(0xC3), B43_PHY_OFDM(0xC4), B43_PHY_OFDM(0xC5), B43_PHY_OFDM(0xC6), B43_PHY_OFDM(0xC7), B43_PHY_OFDM(0xC8), B43_PHY_OFDM(0xCF), }; static const u16 coefs[] = { 0xDE5E, 0xE832, 0xE331, 0x4D26, 0x0026, 0x1420, 0x0020, 0xFE08, 0x0008, }; struct b43_phy_lp *lpphy = dev->phy.lp; int i; for (i = 0; i < ARRAY_SIZE(addr); i++) { lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]); b43_phy_write(dev, addr[i], coefs[i]); } } static void lpphy_restore_dig_flt_state(struct b43_wldev *dev) { static const u16 addr[] = { B43_PHY_OFDM(0xC1), B43_PHY_OFDM(0xC2), B43_PHY_OFDM(0xC3), B43_PHY_OFDM(0xC4), B43_PHY_OFDM(0xC5), B43_PHY_OFDM(0xC6), B43_PHY_OFDM(0xC7), B43_PHY_OFDM(0xC8), B43_PHY_OFDM(0xCF), }; struct b43_phy_lp *lpphy = dev->phy.lp; int i; for (i = 0; i < ARRAY_SIZE(addr); i++) b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]); } static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; b43_phy_write(dev, B43_LPPHY_AFE_DAC_CTL, 0x50); b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0x8800); b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0); b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0); b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0); b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0); b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10); b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4); b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200); b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F); b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40); b43_phy_maskset(dev, B43_LPPHY_PREAMBLECONFIRMTO, 0xFF00, 0x2); b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000); b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000); b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1); if (dev->dev->board_rev >= 0x18) { b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC); b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14); } else { b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10); } b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4); b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100); b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48); b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0xFF00, 0x46); b43_phy_maskset(dev, B43_PHY_OFDM(0xE4), 0xFF00, 0x10); b43_phy_maskset(dev, B43_LPPHY_PWR_THRESH1, 0xFFF0, 0x9); b43_phy_mask(dev, B43_LPPHY_GAINDIRECTMISMATCH, ~0xF); b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5500); b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0xA0); b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xE0FF, 0x300); b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2A00); if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100); b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xA); } else { b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x1E00); b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xD); } b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFFE0, 0x1F); b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC); b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0xFF00, 0x19); b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0x03FF, 0x3C00); b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFC1F, 0x3E0); b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC); b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0x00FF, 0x1900); b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800); b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12); b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000); if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0); b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40); } if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40); b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0xB00); b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x6); b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0x9D00); b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0xFF00, 0xA1); b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); } else /* 5GHz */ b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x40); b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0xB3); b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00); b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, 0xFF00, lpphy->rx_pwr_offset); b43_phy_set(dev, B43_LPPHY_RESET_CTL, 0x44); b43_phy_write(dev, B43_LPPHY_RESET_CTL, 0x80); b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, 0xA954); b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0x2000 | ((u16)lpphy->rssi_gs << 10) | ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf); if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C); b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800); b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400); } lpphy_save_dig_flt_state(dev); } static void lpphy_baseband_init(struct b43_wldev *dev) { lpphy_table_init(dev); if (dev->phy.rev >= 2) lpphy_baseband_rev2plus_init(dev); else lpphy_baseband_rev0_1_init(dev); } struct b2062_freqdata { u16 freq; u8 data[6]; }; /* Initialize the 2062 radio. */ static void lpphy_2062_init(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; struct ssb_bus *bus = dev->dev->sdev->bus; u32 crystalfreq, tmp, ref; unsigned int i; const struct b2062_freqdata *fd = NULL; static const struct b2062_freqdata freqdata_tab[] = { { .freq = 12000, .data[0] = 6, .data[1] = 6, .data[2] = 6, .data[3] = 6, .data[4] = 10, .data[5] = 6, }, { .freq = 13000, .data[0] = 4, .data[1] = 4, .data[2] = 4, .data[3] = 4, .data[4] = 11, .data[5] = 7, }, { .freq = 14400, .data[0] = 3, .data[1] = 3, .data[2] = 3, .data[3] = 3, .data[4] = 12, .data[5] = 7, }, { .freq = 16200, .data[0] = 3, .data[1] = 3, .data[2] = 3, .data[3] = 3, .data[4] = 13, .data[5] = 8, }, { .freq = 18000, .data[0] = 2, .data[1] = 2, .data[2] = 2, .data[3] = 2, .data[4] = 14, .data[5] = 8, }, { .freq = 19200, .data[0] = 1, .data[1] = 1, .data[2] = 1, .data[3] = 1, .data[4] = 14, .data[5] = 9, }, }; b2062_upload_init_table(dev); b43_radio_write(dev, B2062_N_TX_CTL3, 0); b43_radio_write(dev, B2062_N_TX_CTL4, 0); b43_radio_write(dev, B2062_N_TX_CTL5, 0); b43_radio_write(dev, B2062_N_TX_CTL6, 0); b43_radio_write(dev, B2062_N_PDN_CTL0, 0x40); b43_radio_write(dev, B2062_N_PDN_CTL0, 0); b43_radio_write(dev, B2062_N_CALIB_TS, 0x10); b43_radio_write(dev, B2062_N_CALIB_TS, 0); if (dev->phy.rev > 0) { b43_radio_write(dev, B2062_S_BG_CTL1, (b43_radio_read(dev, B2062_N_COMM2) >> 1) | 0x80); } if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_radio_set(dev, B2062_N_TSSI_CTL0, 0x1); else b43_radio_mask(dev, B2062_N_TSSI_CTL0, ~0x1); /* Get the crystal freq, in Hz. */ crystalfreq = bus->chipco.pmu.crystalfreq * 1000; B43_WARN_ON(!(bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU)); B43_WARN_ON(crystalfreq == 0); if (crystalfreq <= 30000000) { lpphy->pdiv = 1; b43_radio_mask(dev, B2062_S_RFPLL_CTL1, 0xFFFB); } else { lpphy->pdiv = 2; b43_radio_set(dev, B2062_S_RFPLL_CTL1, 0x4); } tmp = (((800000000 * lpphy->pdiv + crystalfreq) / (2 * crystalfreq)) - 8) & 0xFF; b43_radio_write(dev, B2062_S_RFPLL_CTL7, tmp); tmp = (((100 * crystalfreq + 16000000 * lpphy->pdiv) / (32000000 * lpphy->pdiv)) - 1) & 0xFF; b43_radio_write(dev, B2062_S_RFPLL_CTL18, tmp); tmp = (((2 * crystalfreq + 1000000 * lpphy->pdiv) / (2000000 * lpphy->pdiv)) - 1) & 0xFF; b43_radio_write(dev, B2062_S_RFPLL_CTL19, tmp); ref = (1000 * lpphy->pdiv + 2 * crystalfreq) / (2000 * lpphy->pdiv); ref &= 0xFFFF; for (i = 0; i < ARRAY_SIZE(freqdata_tab); i++) { if (ref < freqdata_tab[i].freq) { fd = &freqdata_tab[i]; break; } } if (!fd) fd = &freqdata_tab[ARRAY_SIZE(freqdata_tab) - 1]; b43dbg(dev->wl, "b2062: Using crystal tab entry %u kHz.\n", fd->freq); /* FIXME: Keep this printk until the code is fully debugged. */ b43_radio_write(dev, B2062_S_RFPLL_CTL8, ((u16)(fd->data[1]) << 4) | fd->data[0]); b43_radio_write(dev, B2062_S_RFPLL_CTL9, ((u16)(fd->data[3]) << 4) | fd->data[2]); b43_radio_write(dev, B2062_S_RFPLL_CTL10, fd->data[4]); b43_radio_write(dev, B2062_S_RFPLL_CTL11, fd->data[5]); } /* Initialize the 2063 radio. */ static void lpphy_2063_init(struct b43_wldev *dev) { b2063_upload_init_table(dev); b43_radio_write(dev, B2063_LOGEN_SP5, 0); b43_radio_set(dev, B2063_COMM8, 0x38); b43_radio_write(dev, B2063_REG_SP1, 0x56); b43_radio_mask(dev, B2063_RX_BB_CTL2, ~0x2); b43_radio_write(dev, B2063_PA_SP7, 0); b43_radio_write(dev, B2063_TX_RF_SP6, 0x20); b43_radio_write(dev, B2063_TX_RF_SP9, 0x40); if (dev->phy.rev == 2) { b43_radio_write(dev, B2063_PA_SP3, 0xa0); b43_radio_write(dev, B2063_PA_SP4, 0xa0); b43_radio_write(dev, B2063_PA_SP2, 0x18); } else { b43_radio_write(dev, B2063_PA_SP3, 0x20); b43_radio_write(dev, B2063_PA_SP2, 0x20); } } struct lpphy_stx_table_entry { u16 phy_offset; u16 phy_shift; u16 rf_addr; u16 rf_shift; u16 mask; }; static const struct lpphy_stx_table_entry lpphy_stx_table[] = { { .phy_offset = 2, .phy_shift = 6, .rf_addr = 0x3d, .rf_shift = 3, .mask = 0x01, }, { .phy_offset = 1, .phy_shift = 12, .rf_addr = 0x4c, .rf_shift = 1, .mask = 0x01, }, { .phy_offset = 1, .phy_shift = 8, .rf_addr = 0x50, .rf_shift = 0, .mask = 0x7f, }, { .phy_offset = 0, .phy_shift = 8, .rf_addr = 0x44, .rf_shift = 0, .mask = 0xff, }, { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4a, .rf_shift = 0, .mask = 0xff, }, { .phy_offset = 0, .phy_shift = 4, .rf_addr = 0x4d, .rf_shift = 0, .mask = 0xff, }, { .phy_offset = 1, .phy_shift = 4, .rf_addr = 0x4e, .rf_shift = 0, .mask = 0xff, }, { .phy_offset = 0, .phy_shift = 12, .rf_addr = 0x4f, .rf_shift = 0, .mask = 0x0f, }, { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4f, .rf_shift = 4, .mask = 0x0f, }, { .phy_offset = 3, .phy_shift = 0, .rf_addr = 0x49, .rf_shift = 0, .mask = 0x0f, }, { .phy_offset = 4, .phy_shift = 3, .rf_addr = 0x46, .rf_shift = 4, .mask = 0x07, }, { .phy_offset = 3, .phy_shift = 15, .rf_addr = 0x46, .rf_shift = 0, .mask = 0x01, }, { .phy_offset = 4, .phy_shift = 0, .rf_addr = 0x46, .rf_shift = 1, .mask = 0x07, }, { .phy_offset = 3, .phy_shift = 8, .rf_addr = 0x48, .rf_shift = 4, .mask = 0x07, }, { .phy_offset = 3, .phy_shift = 11, .rf_addr = 0x48, .rf_shift = 0, .mask = 0x0f, }, { .phy_offset = 3, .phy_shift = 4, .rf_addr = 0x49, .rf_shift = 4, .mask = 0x0f, }, { .phy_offset = 2, .phy_shift = 15, .rf_addr = 0x45, .rf_shift = 0, .mask = 0x01, }, { .phy_offset = 5, .phy_shift = 13, .rf_addr = 0x52, .rf_shift = 4, .mask = 0x07, }, { .phy_offset = 6, .phy_shift = 0, .rf_addr = 0x52, .rf_shift = 7, .mask = 0x01, }, { .phy_offset = 5, .phy_shift = 3, .rf_addr = 0x41, .rf_shift = 5, .mask = 0x07, }, { .phy_offset = 5, .phy_shift = 6, .rf_addr = 0x41, .rf_shift = 0, .mask = 0x0f, }, { .phy_offset = 5, .phy_shift = 10, .rf_addr = 0x42, .rf_shift = 5, .mask = 0x07, }, { .phy_offset = 4, .phy_shift = 15, .rf_addr = 0x42, .rf_shift = 0, .mask = 0x01, }, { .phy_offset = 5, .phy_shift = 0, .rf_addr = 0x42, .rf_shift = 1, .mask = 0x07, }, { .phy_offset = 4, .phy_shift = 11, .rf_addr = 0x43, .rf_shift = 4, .mask = 0x0f, }, { .phy_offset = 4, .phy_shift = 7, .rf_addr = 0x43, .rf_shift = 0, .mask = 0x0f, }, { .phy_offset = 4, .phy_shift = 6, .rf_addr = 0x45, .rf_shift = 1, .mask = 0x01, }, { .phy_offset = 2, .phy_shift = 7, .rf_addr = 0x40, .rf_shift = 4, .mask = 0x0f, }, { .phy_offset = 2, .phy_shift = 11, .rf_addr = 0x40, .rf_shift = 0, .mask = 0x0f, }, }; static void lpphy_sync_stx(struct b43_wldev *dev) { const struct lpphy_stx_table_entry *e; unsigned int i; u16 tmp; for (i = 0; i < ARRAY_SIZE(lpphy_stx_table); i++) { e = &lpphy_stx_table[i]; tmp = b43_radio_read(dev, e->rf_addr); tmp >>= e->rf_shift; tmp <<= e->phy_shift; b43_phy_maskset(dev, B43_PHY_OFDM(0xF2 + e->phy_offset), ~(e->mask << e->phy_shift), tmp); } } static void lpphy_radio_init(struct b43_wldev *dev) { /* The radio is attached through the 4wire bus. */ b43_phy_set(dev, B43_LPPHY_FOURWIRE_CTL, 0x2); udelay(1); b43_phy_mask(dev, B43_LPPHY_FOURWIRE_CTL, 0xFFFD); udelay(1); if (dev->phy.radio_ver == 0x2062) { lpphy_2062_init(dev); } else { lpphy_2063_init(dev); lpphy_sync_stx(dev); b43_phy_write(dev, B43_PHY_OFDM(0xF0), 0x5F80); b43_phy_write(dev, B43_PHY_OFDM(0xF1), 0); if (dev->dev->chip_id == 0x4325) { // TODO SSB PMU recalibration } } } struct lpphy_iq_est { u32 iq_prod, i_pwr, q_pwr; }; static void lpphy_set_rc_cap(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; u8 rc_cap = (lpphy->rc_cap & 0x1F) >> 1; if (dev->phy.rev == 1) //FIXME check channel 14! rc_cap = min_t(u8, rc_cap + 5, 15); b43_radio_write(dev, B2062_N_RXBB_CALIB2, max_t(u8, lpphy->rc_cap - 4, 0x80)); b43_radio_write(dev, B2062_N_TX_CTL_A, rc_cap | 0x80); b43_radio_write(dev, B2062_S_RXG_CNT16, ((lpphy->rc_cap & 0x1F) >> 2) | 0x80); } static u8 lpphy_get_bb_mult(struct b43_wldev *dev) { return (b43_lptab_read(dev, B43_LPTAB16(0, 87)) & 0xFF00) >> 8; } static void lpphy_set_bb_mult(struct b43_wldev *dev, u8 bb_mult) { b43_lptab_write(dev, B43_LPTAB16(0, 87), (u16)bb_mult << 8); } static void lpphy_set_deaf(struct b43_wldev *dev, bool user) { struct b43_phy_lp *lpphy = dev->phy.lp; if (user) lpphy->crs_usr_disable = true; else lpphy->crs_sys_disable = true; b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x80); } static void lpphy_clear_deaf(struct b43_wldev *dev, bool user) { struct b43_phy_lp *lpphy = dev->phy.lp; if (user) lpphy->crs_usr_disable = false; else lpphy->crs_sys_disable = false; if (!lpphy->crs_usr_disable && !lpphy->crs_sys_disable) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x60); else b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x20); } } static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx) { u16 trsw = (tx << 1) | rx; b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); } static void lpphy_disable_crs(struct b43_wldev *dev, bool user) { lpphy_set_deaf(dev, user); lpphy_set_trsw_over(dev, false, true); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x10); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFDF); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFBF); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x7); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x38); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x100); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFDFF); b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL0, 0); b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL1, 1); b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL2, 0x20); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xF7FF); b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0); b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, 0x45AF); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0x3FF); } static void lpphy_restore_crs(struct b43_wldev *dev, bool user) { lpphy_clear_deaf(dev, user); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFF80); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFC00); } struct lpphy_tx_gains { u16 gm, pga, pad, dac; }; static void lpphy_disable_rx_gain_override(struct b43_wldev *dev) { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF); if (dev->phy.rev >= 2) { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF); b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7); } } else { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF); } } static void lpphy_enable_rx_gain_override(struct b43_wldev *dev) { b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); if (dev->phy.rev >= 2) { b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400); b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8); } } else { b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200); } } static void lpphy_disable_tx_gain_override(struct b43_wldev *dev) { if (dev->phy.rev < 2) b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); else { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF); } b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF); } static void lpphy_enable_tx_gain_override(struct b43_wldev *dev) { if (dev->phy.rev < 2) b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); else { b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000); } b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40); } static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev) { struct lpphy_tx_gains gains; u16 tmp; gains.dac = (b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0x380) >> 7; if (dev->phy.rev < 2) { tmp = b43_phy_read(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL) & 0x7FF; gains.gm = tmp & 0x0007; gains.pga = (tmp & 0x0078) >> 3; gains.pad = (tmp & 0x780) >> 7; } else { tmp = b43_phy_read(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL); gains.pad = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0xFF; gains.gm = tmp & 0xFF; gains.pga = (tmp >> 8) & 0xFF; } return gains; } static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac) { u16 ctl = b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0xC7F; ctl |= dac << 7; b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl); } static u16 lpphy_get_pa_gain(struct b43_wldev *dev) { return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F; } static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain) { b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6); b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8); } static void lpphy_set_tx_gains(struct b43_wldev *dev, struct lpphy_tx_gains gains) { u16 rf_gain, pa_gain; if (dev->phy.rev < 2) { rf_gain = (gains.pad << 7) | (gains.pga << 3) | gains.gm; b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0xF800, rf_gain); } else { pa_gain = lpphy_get_pa_gain(dev); b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, (gains.pga << 8) | gains.gm); /* * SPEC FIXME The spec calls for (pa_gain << 8) here, but that * conflicts with the spec for set_pa_gain! Vendor driver bug? */ b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0x8000, gains.pad | (pa_gain << 6)); b43_phy_write(dev, B43_PHY_OFDM(0xFC), (gains.pga << 8) | gains.gm); b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x8000, gains.pad | (pa_gain << 8)); } lpphy_set_dac_gain(dev, gains.dac); lpphy_enable_tx_gain_override(dev); } static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain) { u16 trsw = gain & 0x1; u16 lna = (gain & 0xFFFC) | ((gain & 0xC) >> 2); u16 ext_lna = (gain & 2) >> 1; b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF, ext_lna << 10); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xF7FF, ext_lna << 11); b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, lna); } static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain) { u16 low_gain = gain & 0xFFFF; u16 high_gain = (gain >> 16) & 0xF; u16 ext_lna = (gain >> 21) & 0x1; u16 trsw = ~(gain >> 20) & 0x1; u16 tmp; b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFDFF, ext_lna << 9); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF, ext_lna << 10); b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, low_gain); b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF0, high_gain); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { tmp = (gain >> 2) & 0x3; b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xE7FF, tmp<<11); b43_phy_maskset(dev, B43_PHY_OFDM(0xE6), 0xFFE7, tmp << 3); } } static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain) { if (dev->phy.rev < 2) lpphy_rev0_1_set_rx_gain(dev, gain); else lpphy_rev2plus_set_rx_gain(dev, gain); lpphy_enable_rx_gain_override(dev); } static void lpphy_set_rx_gain_by_index(struct b43_wldev *dev, u16 idx) { u32 gain = b43_lptab_read(dev, B43_LPTAB16(12, idx)); lpphy_set_rx_gain(dev, gain); } static void lpphy_stop_ddfs(struct b43_wldev *dev) { b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFD); b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xFFDF); } static void lpphy_run_ddfs(struct b43_wldev *dev, int i_on, int q_on, int incr1, int incr2, int scale_idx) { lpphy_stop_ddfs(dev); b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0xFF80); b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0x80FF); b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0xFF80, incr1); b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0x80FF, incr2 << 8); b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF7, i_on << 3); b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFEF, q_on << 4); b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFF9F, scale_idx << 5); b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFB); b43_phy_set(dev, B43_LPPHY_AFE_DDFS, 0x2); b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x20); } static bool lpphy_rx_iq_est(struct b43_wldev *dev, u16 samples, u8 time, struct lpphy_iq_est *iq_est) { int i; b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFF7); b43_phy_write(dev, B43_LPPHY_IQ_NUM_SMPLS_ADDR, samples); b43_phy_maskset(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFF00, time); b43_phy_mask(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFEFF); b43_phy_set(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0x200); for (i = 0; i < 500; i++) { if (!(b43_phy_read(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) break; msleep(1); } if ((b43_phy_read(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) { b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); return false; } iq_est->iq_prod = b43_phy_read(dev, B43_LPPHY_IQ_ACC_HI_ADDR); iq_est->iq_prod <<= 16; iq_est->iq_prod |= b43_phy_read(dev, B43_LPPHY_IQ_ACC_LO_ADDR); iq_est->i_pwr = b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR); iq_est->i_pwr <<= 16; iq_est->i_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR); iq_est->q_pwr = b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR); iq_est->q_pwr <<= 16; iq_est->q_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR); b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); return true; } static int lpphy_loopback(struct b43_wldev *dev) { struct lpphy_iq_est iq_est; int i, index = -1; u32 tmp; memset(&iq_est, 0, sizeof(iq_est)); lpphy_set_trsw_over(dev, true, true); b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1); b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x8); b43_radio_write(dev, B2062_N_TX_CTL_A, 0x80); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x80); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x80); for (i = 0; i < 32; i++) { lpphy_set_rx_gain_by_index(dev, i); lpphy_run_ddfs(dev, 1, 1, 5, 5, 0); if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) continue; tmp = (iq_est.i_pwr + iq_est.q_pwr) / 1000; if ((tmp > 4000) && (tmp < 10000)) { index = i; break; } } lpphy_stop_ddfs(dev); return index; } /* Fixed-point division algorithm using only integer math. */ static u32 lpphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision) { u32 quotient, remainder; if (divisor == 0) return 0; quotient = dividend / divisor; remainder = dividend % divisor; while (precision > 0) { quotient <<= 1; if (remainder << 1 >= divisor) { quotient++; remainder = (remainder << 1) - divisor; } precision--; } if (remainder << 1 >= divisor) quotient++; return quotient; } /* Read the TX power control mode from hardware. */ static void lpphy_read_tx_pctl_mode_from_hardware(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; u16 ctl; ctl = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_CMD); switch (ctl & B43_LPPHY_TX_PWR_CTL_CMD_MODE) { case B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF: lpphy->txpctl_mode = B43_LPPHY_TXPCTL_OFF; break; case B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW: lpphy->txpctl_mode = B43_LPPHY_TXPCTL_SW; break; case B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW: lpphy->txpctl_mode = B43_LPPHY_TXPCTL_HW; break; default: lpphy->txpctl_mode = B43_LPPHY_TXPCTL_UNKNOWN; B43_WARN_ON(1); break; } } /* Set the TX power control mode in hardware. */ static void lpphy_write_tx_pctl_mode_to_hardware(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; u16 ctl; switch (lpphy->txpctl_mode) { case B43_LPPHY_TXPCTL_OFF: ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF; break; case B43_LPPHY_TXPCTL_HW: ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW; break; case B43_LPPHY_TXPCTL_SW: ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW; break; default: ctl = 0; B43_WARN_ON(1); } b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, ctl); } static void lpphy_set_tx_power_control(struct b43_wldev *dev, enum b43_lpphy_txpctl_mode mode) { struct b43_phy_lp *lpphy = dev->phy.lp; enum b43_lpphy_txpctl_mode oldmode; lpphy_read_tx_pctl_mode_from_hardware(dev); oldmode = lpphy->txpctl_mode; if (oldmode == mode) return; lpphy->txpctl_mode = mode; if (oldmode == B43_LPPHY_TXPCTL_HW) { //TODO Update TX Power NPT //TODO Clear all TX Power offsets } else { if (mode == B43_LPPHY_TXPCTL_HW) { //TODO Recalculate target TX power b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, 0xFF80, lpphy->tssi_idx); b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, ((u16)lpphy->tssi_npt << 16)); //TODO Set "TSSI Transmit Count" variable to total transmitted frame count lpphy_disable_tx_gain_override(dev); lpphy->tx_pwr_idx_over = -1; } } if (dev->phy.rev >= 2) { if (mode == B43_LPPHY_TXPCTL_HW) b43_phy_set(dev, B43_PHY_OFDM(0xD0), 0x2); else b43_phy_mask(dev, B43_PHY_OFDM(0xD0), 0xFFFD); } lpphy_write_tx_pctl_mode_to_hardware(dev); } static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel); static void lpphy_rev0_1_rc_calib(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; struct lpphy_iq_est iq_est; struct lpphy_tx_gains tx_gains; static const u32 ideal_pwr_table[21] = { 0x10000, 0x10557, 0x10e2d, 0x113e0, 0x10f22, 0x0ff64, 0x0eda2, 0x0e5d4, 0x0efd1, 0x0fbe8, 0x0b7b8, 0x04b35, 0x01a5e, 0x00a0b, 0x00444, 0x001fd, 0x000ff, 0x00088, 0x0004c, 0x0002c, 0x0001a, }; bool old_txg_ovr; u8 old_bbmult; u16 old_rf_ovr, old_rf_ovrval, old_afe_ovr, old_afe_ovrval, old_rf2_ovr, old_rf2_ovrval, old_phy_ctl; enum b43_lpphy_txpctl_mode old_txpctl; u32 normal_pwr, ideal_pwr, mean_sq_pwr, tmp = 0, mean_sq_pwr_min = 0; int loopback, i, j, inner_sum, err; memset(&iq_est, 0, sizeof(iq_est)); err = b43_lpphy_op_switch_channel(dev, 7); if (err) { b43dbg(dev->wl, "RC calib: Failed to switch to channel 7, error = %d\n", err); } old_txg_ovr = !!(b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40); old_bbmult = lpphy_get_bb_mult(dev); if (old_txg_ovr) tx_gains = lpphy_get_tx_gains(dev); old_rf_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_0); old_rf_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_VAL_0); old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR); old_afe_ovrval = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVRVAL); old_rf2_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2); old_rf2_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2_VAL); old_phy_ctl = b43_phy_read(dev, B43_LPPHY_LP_PHY_CTL); lpphy_read_tx_pctl_mode_from_hardware(dev); old_txpctl = lpphy->txpctl_mode; lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); lpphy_disable_crs(dev, true); loopback = lpphy_loopback(dev); if (loopback == -1) goto finish; lpphy_set_rx_gain_by_index(dev, loopback); b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFFBF, 0x40); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFF8, 0x1); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFC7, 0x8); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F, 0xC0); for (i = 128; i <= 159; i++) { b43_radio_write(dev, B2062_N_RXBB_CALIB2, i); inner_sum = 0; for (j = 5; j <= 25; j++) { lpphy_run_ddfs(dev, 1, 1, j, j, 0); if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) goto finish; mean_sq_pwr = iq_est.i_pwr + iq_est.q_pwr; if (j == 5) tmp = mean_sq_pwr; ideal_pwr = ((ideal_pwr_table[j-5] >> 3) + 1) >> 1; normal_pwr = lpphy_qdiv_roundup(mean_sq_pwr, tmp, 12); mean_sq_pwr = ideal_pwr - normal_pwr; mean_sq_pwr *= mean_sq_pwr; inner_sum += mean_sq_pwr; if ((i == 128) || (inner_sum < mean_sq_pwr_min)) { lpphy->rc_cap = i; mean_sq_pwr_min = inner_sum; } } } lpphy_stop_ddfs(dev); finish: lpphy_restore_crs(dev, true); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, old_rf_ovrval); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, old_rf_ovr); b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, old_afe_ovrval); b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, old_afe_ovr); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, old_rf2_ovrval); b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, old_rf2_ovr); b43_phy_write(dev, B43_LPPHY_LP_PHY_CTL, old_phy_ctl); lpphy_set_bb_mult(dev, old_bbmult); if (old_txg_ovr) { /* * SPEC FIXME: The specs say "get_tx_gains" here, which is * illogical. According to lwfinger, vendor driver v4.150.10.5 * has a Set here, while v4.174.64.19 has a Get - regression in * the vendor driver? This should be tested this once the code * is testable. */ lpphy_set_tx_gains(dev, tx_gains); } lpphy_set_tx_power_control(dev, old_txpctl); if (lpphy->rc_cap) lpphy_set_rc_cap(dev); } static void lpphy_rev2plus_rc_calib(struct b43_wldev *dev) { struct ssb_bus *bus = dev->dev->sdev->bus; u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; u8 tmp = b43_radio_read(dev, B2063_RX_BB_SP8) & 0xFF; int i; b43_radio_write(dev, B2063_RX_BB_SP8, 0x0); b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); b43_radio_mask(dev, B2063_PLL_SP1, 0xF7); b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x15); b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x70); b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x52); b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7D); for (i = 0; i < 10000; i++) { if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) break; msleep(1); } if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) b43_radio_write(dev, B2063_RX_BB_SP8, tmp); tmp = b43_radio_read(dev, B2063_TX_BB_SP3) & 0xFF; b43_radio_write(dev, B2063_TX_BB_SP3, 0x0); b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x55); b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x76); if (crystal_freq == 24000000) { b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0xFC); b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x0); } else { b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x13); b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); } b43_radio_write(dev, B2063_PA_SP7, 0x7D); for (i = 0; i < 10000; i++) { if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) break; msleep(1); } if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) b43_radio_write(dev, B2063_TX_BB_SP3, tmp); b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); } static void lpphy_calibrate_rc(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; if (dev->phy.rev >= 2) { lpphy_rev2plus_rc_calib(dev); } else if (!lpphy->rc_cap) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) lpphy_rev0_1_rc_calib(dev); } else { lpphy_set_rc_cap(dev); } } static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) { if (dev->phy.rev >= 2) return; // rev2+ doesn't support antenna diversity if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1)) return; b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2); b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1); b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); dev->phy.lp->antenna = antenna; } static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b) { u16 tmp[2]; tmp[0] = a; tmp[1] = b; b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp); } static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index) { struct b43_phy_lp *lpphy = dev->phy.lp; struct lpphy_tx_gains gains; u32 iq_comp, tx_gain, coeff, rf_power; lpphy->tx_pwr_idx_over = index; lpphy_read_tx_pctl_mode_from_hardware(dev); if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF) lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW); if (dev->phy.rev >= 2) { iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320)); tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192)); gains.pad = (tx_gain >> 16) & 0xFF; gains.gm = tx_gain & 0xFF; gains.pga = (tx_gain >> 8) & 0xFF; gains.dac = (iq_comp >> 28) & 0xFF; lpphy_set_tx_gains(dev, gains); } else { iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320)); tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192)); b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0xF800, (tx_gain >> 4) & 0x7FFF); lpphy_set_dac_gain(dev, tx_gain & 0x7); lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F); } lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF); lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF); if (dev->phy.rev >= 2) { coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448)); } else { coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448)); } b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF); if (dev->phy.rev >= 2) { rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576)); b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, rf_power & 0xFFFF);//SPEC FIXME mask & set != 0 } lpphy_enable_tx_gain_override(dev); } static void lpphy_btcoex_override(struct b43_wldev *dev) { b43_write16(dev, B43_MMIO_BTCOEX_CTL, 0x3); b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF); } static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev, bool blocked) { //TODO check MAC control register if (blocked) { if (dev->phy.rev >= 2) { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808); } else { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018); } } else { b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF); if (dev->phy.rev >= 2) b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7); else b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7); } } /* This was previously called lpphy_japan_filter */ static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel) { struct b43_phy_lp *lpphy = dev->phy.lp; u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter! if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific? b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9); if ((dev->phy.rev == 1) && (lpphy->rc_cap)) lpphy_set_rc_cap(dev); } else { b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F); } } static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode) { if (mode != TSSI_MUX_EXT) { b43_radio_set(dev, B2063_PA_SP1, 0x2); b43_phy_set(dev, B43_PHY_OFDM(0xF3), 0x1000); b43_radio_write(dev, B2063_PA_CTL10, 0x51); if (mode == TSSI_MUX_POSTPA) { b43_radio_mask(dev, B2063_PA_SP1, 0xFFFE); b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFC7); } else { b43_radio_maskset(dev, B2063_PA_SP1, 0xFFFE, 0x1); b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFC7, 0x20); } } else { B43_WARN_ON(1); } } static void lpphy_tx_pctl_init_hw(struct b43_wldev *dev) { u16 tmp; int i; //SPEC TODO Call LP PHY Clear TX Power offsets for (i = 0; i < 64; i++) { if (dev->phy.rev >= 2) b43_lptab_write(dev, B43_LPTAB32(7, i + 1), i); else b43_lptab_write(dev, B43_LPTAB32(10, i + 1), i); } b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xFF00, 0xFF); b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, 0x5000); b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0xFFC0, 0x1F); if (dev->phy.rev < 2) { b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xEFFF); b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xDFFF, 0x2000); } else { b43_phy_mask(dev, B43_PHY_OFDM(0x103), 0xFFFE); b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFFB, 0x4); b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFEF, 0x10); b43_radio_maskset(dev, B2063_IQ_CALIB_CTL2, 0xF3, 0x1); lpphy_set_tssi_mux(dev, TSSI_MUX_POSTPA); } b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0x7FFF, 0x8000); b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xFF); b43_phy_write(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xA); b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF); b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xF8FF); b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW); if (dev->phy.rev < 2) { b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF, 0x1000); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xEFFF); } else { lpphy_set_tx_power_by_index(dev, 0x7F); } b43_dummy_transmission(dev, true, true); tmp = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_STAT); if (tmp & 0x8000) { b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0xFFC0, (tmp & 0xFF) - 32); } b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF); // (SPEC?) TODO Set "Target TX frequency" variable to 0 // SPEC FIXME "Set BB Multiplier to 0xE000" impossible - bb_mult is u8! } static void lpphy_tx_pctl_init_sw(struct b43_wldev *dev) { struct lpphy_tx_gains gains; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { gains.gm = 4; gains.pad = 12; gains.pga = 12; gains.dac = 0; } else { gains.gm = 7; gains.pad = 14; gains.pga = 15; gains.dac = 0; } lpphy_set_tx_gains(dev, gains); lpphy_set_bb_mult(dev, 150); } /* Initialize TX power control */ static void lpphy_tx_pctl_init(struct b43_wldev *dev) { if (0/*FIXME HWPCTL capable */) { lpphy_tx_pctl_init_hw(dev); } else { /* This device is only software TX power control capable. */ lpphy_tx_pctl_init_sw(dev); } } static void lpphy_pr41573_workaround(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; u32 *saved_tab; const unsigned int saved_tab_size = 256; enum b43_lpphy_txpctl_mode txpctl_mode; s8 tx_pwr_idx_over; u16 tssi_npt, tssi_idx; saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL); if (!saved_tab) { b43err(dev->wl, "PR41573 failed. Out of memory!\n"); return; } lpphy_read_tx_pctl_mode_from_hardware(dev); txpctl_mode = lpphy->txpctl_mode; tx_pwr_idx_over = lpphy->tx_pwr_idx_over; tssi_npt = lpphy->tssi_npt; tssi_idx = lpphy->tssi_idx; if (dev->phy.rev < 2) { b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140), saved_tab_size, saved_tab); } else { b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140), saved_tab_size, saved_tab); } //FIXME PHY reset lpphy_table_init(dev); //FIXME is table init needed? lpphy_baseband_init(dev); lpphy_tx_pctl_init(dev); b43_lpphy_op_software_rfkill(dev, false); lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); if (dev->phy.rev < 2) { b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140), saved_tab_size, saved_tab); } else { b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140), saved_tab_size, saved_tab); } b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel); lpphy->tssi_npt = tssi_npt; lpphy->tssi_idx = tssi_idx; lpphy_set_analog_filter(dev, lpphy->channel); if (tx_pwr_idx_over != -1) lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over); if (lpphy->rc_cap) lpphy_set_rc_cap(dev); b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna); lpphy_set_tx_power_control(dev, txpctl_mode); kfree(saved_tab); } struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; }; static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = { { .chan = 1, .c1 = -66, .c0 = 15, }, { .chan = 2, .c1 = -66, .c0 = 15, }, { .chan = 3, .c1 = -66, .c0 = 15, }, { .chan = 4, .c1 = -66, .c0 = 15, }, { .chan = 5, .c1 = -66, .c0 = 15, }, { .chan = 6, .c1 = -66, .c0 = 15, }, { .chan = 7, .c1 = -66, .c0 = 14, }, { .chan = 8, .c1 = -66, .c0 = 14, }, { .chan = 9, .c1 = -66, .c0 = 14, }, { .chan = 10, .c1 = -66, .c0 = 14, }, { .chan = 11, .c1 = -66, .c0 = 14, }, { .chan = 12, .c1 = -66, .c0 = 13, }, { .chan = 13, .c1 = -66, .c0 = 13, }, { .chan = 14, .c1 = -66, .c0 = 13, }, }; static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = { { .chan = 1, .c1 = -64, .c0 = 13, }, { .chan = 2, .c1 = -64, .c0 = 13, }, { .chan = 3, .c1 = -64, .c0 = 13, }, { .chan = 4, .c1 = -64, .c0 = 13, }, { .chan = 5, .c1 = -64, .c0 = 12, }, { .chan = 6, .c1 = -64, .c0 = 12, }, { .chan = 7, .c1 = -64, .c0 = 12, }, { .chan = 8, .c1 = -64, .c0 = 12, }, { .chan = 9, .c1 = -64, .c0 = 12, }, { .chan = 10, .c1 = -64, .c0 = 11, }, { .chan = 11, .c1 = -64, .c0 = 11, }, { .chan = 12, .c1 = -64, .c0 = 11, }, { .chan = 13, .c1 = -64, .c0 = 11, }, { .chan = 14, .c1 = -64, .c0 = 10, }, { .chan = 34, .c1 = -62, .c0 = 24, }, { .chan = 38, .c1 = -62, .c0 = 24, }, { .chan = 42, .c1 = -62, .c0 = 24, }, { .chan = 46, .c1 = -62, .c0 = 23, }, { .chan = 36, .c1 = -62, .c0 = 24, }, { .chan = 40, .c1 = -62, .c0 = 24, }, { .chan = 44, .c1 = -62, .c0 = 23, }, { .chan = 48, .c1 = -62, .c0 = 23, }, { .chan = 52, .c1 = -62, .c0 = 23, }, { .chan = 56, .c1 = -62, .c0 = 22, }, { .chan = 60, .c1 = -62, .c0 = 22, }, { .chan = 64, .c1 = -62, .c0 = 22, }, { .chan = 100, .c1 = -62, .c0 = 16, }, { .chan = 104, .c1 = -62, .c0 = 16, }, { .chan = 108, .c1 = -62, .c0 = 15, }, { .chan = 112, .c1 = -62, .c0 = 14, }, { .chan = 116, .c1 = -62, .c0 = 14, }, { .chan = 120, .c1 = -62, .c0 = 13, }, { .chan = 124, .c1 = -62, .c0 = 12, }, { .chan = 128, .c1 = -62, .c0 = 12, }, { .chan = 132, .c1 = -62, .c0 = 12, }, { .chan = 136, .c1 = -62, .c0 = 11, }, { .chan = 140, .c1 = -62, .c0 = 10, }, { .chan = 149, .c1 = -61, .c0 = 9, }, { .chan = 153, .c1 = -61, .c0 = 9, }, { .chan = 157, .c1 = -61, .c0 = 9, }, { .chan = 161, .c1 = -61, .c0 = 8, }, { .chan = 165, .c1 = -61, .c0 = 8, }, { .chan = 184, .c1 = -62, .c0 = 25, }, { .chan = 188, .c1 = -62, .c0 = 25, }, { .chan = 192, .c1 = -62, .c0 = 25, }, { .chan = 196, .c1 = -62, .c0 = 25, }, { .chan = 200, .c1 = -62, .c0 = 25, }, { .chan = 204, .c1 = -62, .c0 = 25, }, { .chan = 208, .c1 = -62, .c0 = 25, }, { .chan = 212, .c1 = -62, .c0 = 25, }, { .chan = 216, .c1 = -62, .c0 = 26, }, }; static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = { .chan = 0, .c1 = -64, .c0 = 0, }; static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) { struct lpphy_iq_est iq_est; u16 c0, c1; int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret; c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S); c0 = c1 >> 8; c1 |= 0xFF; b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0); b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF); ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est); if (!ret) goto out; prod = iq_est.iq_prod; ipwr = iq_est.i_pwr; qpwr = iq_est.q_pwr; if (ipwr + qpwr < 2) { ret = 0; goto out; } prod_msb = fls(abs(prod)); q_msb = fls(abs(qpwr)); tmp1 = prod_msb - 20; if (tmp1 >= 0) { tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) / (ipwr >> tmp1); } else { tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) / (ipwr << -tmp1); } tmp2 = q_msb - 11; if (tmp2 >= 0) tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2); else tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2); tmp4 -= tmp3 * tmp3; tmp4 = -int_sqrt(tmp4); c0 = tmp3 >> 3; c1 = tmp4 >> 4; out: b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1); b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8); return ret; } static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops, u16 wait) { b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0xFFC0, samples - 1); if (loops != 0xFFFF) loops--; b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops); b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6); b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1); } //SPEC FIXME what does a negative freq mean? static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max) { struct b43_phy_lp *lpphy = dev->phy.lp; u16 buf[64]; int i, samples = 0, angle = 0; int rotation = (((36 * freq) / 20) << 16) / 100; struct b43_c32 sample; lpphy->tx_tone_freq = freq; if (freq) { /* Find i for which abs(freq) integrally divides 20000 * i */ for (i = 1; samples * abs(freq) != 20000 * i; i++) { samples = (20000 * i) / abs(freq); if(B43_WARN_ON(samples > 63)) return; } } else { samples = 2; } for (i = 0; i < samples; i++) { sample = b43_cordic(angle); angle += rotation; buf[i] = CORDIC_CONVERT((sample.i * max) & 0xFF) << 8; buf[i] |= CORDIC_CONVERT((sample.q * max) & 0xFF); } b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf); lpphy_run_samples(dev, samples, 0xFFFF, 0); } static void lpphy_stop_tx_tone(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; int i; lpphy->tx_tone_freq = 0; b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000); for (i = 0; i < 31; i++) { if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1)) break; udelay(100); } } static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains, int mode, bool useindex, u8 index) { //TODO } static void lpphy_papd_cal_txpwr(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; struct lpphy_tx_gains gains, oldgains; int old_txpctl, old_afe_ovr, old_rf, old_bbmult; lpphy_read_tx_pctl_mode_from_hardware(dev); old_txpctl = lpphy->txpctl_mode; old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; if (old_afe_ovr) oldgains = lpphy_get_tx_gains(dev); old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF; old_bbmult = lpphy_get_bb_mult(dev); lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); if (dev->dev->chip_id == 0x4325 && dev->dev->chip_rev == 0) lpphy_papd_cal(dev, gains, 0, 1, 30); else lpphy_papd_cal(dev, gains, 0, 1, 65); if (old_afe_ovr) lpphy_set_tx_gains(dev, oldgains); lpphy_set_bb_mult(dev, old_bbmult); lpphy_set_tx_power_control(dev, old_txpctl); b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf); } static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx, bool rx, bool pa, struct lpphy_tx_gains *gains) { struct b43_phy_lp *lpphy = dev->phy.lp; const struct lpphy_rx_iq_comp *iqcomp = NULL; struct lpphy_tx_gains nogains, oldgains; u16 tmp; int i, ret; memset(&nogains, 0, sizeof(nogains)); memset(&oldgains, 0, sizeof(oldgains)); if (dev->dev->chip_id == 0x5354) { for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) { if (lpphy_5354_iq_table[i].chan == lpphy->channel) { iqcomp = &lpphy_5354_iq_table[i]; } } } else if (dev->phy.rev >= 2) { iqcomp = &lpphy_rev2plus_iq_comp; } else { for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) { if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) { iqcomp = &lpphy_rev0_1_iq_table[i]; } } } if (B43_WARN_ON(!iqcomp)) return 0; b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1); b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, iqcomp->c0 << 8); if (noise) { tx = true; rx = false; pa = false; } lpphy_set_trsw_over(dev, tx, rx); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7, pa << 3); } else { b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFDF, pa << 5); } tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; if (noise) lpphy_set_rx_gain(dev, 0x2D5D); else { if (tmp) oldgains = lpphy_get_tx_gains(dev); if (!gains) gains = &nogains; lpphy_set_tx_gains(dev, *gains); } b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); lpphy_set_deaf(dev, false); if (noise) ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0); else { lpphy_start_tx_tone(dev, 4000, 100); ret = lpphy_calc_rx_iq_comp(dev, 0x4000); lpphy_stop_tx_tone(dev); } lpphy_clear_deaf(dev, false); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7); b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF); if (!noise) { if (tmp) lpphy_set_tx_gains(dev, oldgains); else lpphy_disable_tx_gain_override(dev); } lpphy_disable_rx_gain_override(dev); b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF); return ret; } static void lpphy_calibration(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; enum b43_lpphy_txpctl_mode saved_pctl_mode; bool full_cal = false; if (lpphy->full_calib_chan != lpphy->channel) { full_cal = true; lpphy->full_calib_chan = lpphy->channel; } b43_mac_suspend(dev); lpphy_btcoex_override(dev); if (dev->phy.rev >= 2) lpphy_save_dig_flt_state(dev); lpphy_read_tx_pctl_mode_from_hardware(dev); saved_pctl_mode = lpphy->txpctl_mode; lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); //TODO Perform transmit power table I/Q LO calibration if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF)) lpphy_pr41573_workaround(dev); if ((dev->phy.rev >= 2) && full_cal) { lpphy_papd_cal_txpwr(dev); } lpphy_set_tx_power_control(dev, saved_pctl_mode); if (dev->phy.rev >= 2) lpphy_restore_dig_flt_state(dev); lpphy_rx_iq_cal(dev, true, true, false, false, NULL); b43_mac_enable(dev); } static u16 b43_lpphy_op_read(struct b43_wldev *dev, u16 reg) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_lpphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } static void b43_lpphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); } static u16 b43_lpphy_op_radio_read(struct b43_wldev *dev, u16 reg) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); /* LP-PHY needs a special bit set for read access */ if (dev->phy.rev < 2) { if (reg != 0x4001) reg |= 0x100; } else reg |= 0x200; b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); } static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } struct b206x_channel { u8 channel; u16 freq; u8 data[12]; }; static const struct b206x_channel b2062_chantbl[] = { { .channel = 1, .freq = 2412, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 2, .freq = 2417, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 3, .freq = 2422, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 4, .freq = 2427, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 5, .freq = 2432, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 6, .freq = 2437, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 7, .freq = 2442, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 8, .freq = 2447, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 9, .freq = 2452, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 10, .freq = 2457, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 11, .freq = 2462, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 12, .freq = 2467, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 13, .freq = 2472, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 14, .freq = 2484, .data[0] = 0xFF, .data[1] = 0xFF, .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, { .channel = 34, .freq = 5170, .data[0] = 0x00, .data[1] = 0x22, .data[2] = 0x20, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 38, .freq = 5190, .data[0] = 0x00, .data[1] = 0x11, .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 42, .freq = 5210, .data[0] = 0x00, .data[1] = 0x11, .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 46, .freq = 5230, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 36, .freq = 5180, .data[0] = 0x00, .data[1] = 0x11, .data[2] = 0x20, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 40, .freq = 5200, .data[0] = 0x00, .data[1] = 0x11, .data[2] = 0x10, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 44, .freq = 5220, .data[0] = 0x00, .data[1] = 0x11, .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 48, .freq = 5240, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 52, .freq = 5260, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 56, .freq = 5280, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 60, .freq = 5300, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x63, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 64, .freq = 5320, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x62, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 100, .freq = 5500, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x30, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 104, .freq = 5520, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 108, .freq = 5540, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 112, .freq = 5560, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 116, .freq = 5580, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x10, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 120, .freq = 5600, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 124, .freq = 5620, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 128, .freq = 5640, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 132, .freq = 5660, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 136, .freq = 5680, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 140, .freq = 5700, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 149, .freq = 5745, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 153, .freq = 5765, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 157, .freq = 5785, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 161, .freq = 5805, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 165, .freq = 5825, .data[0] = 0x00, .data[1] = 0x00, .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 184, .freq = 4920, .data[0] = 0x55, .data[1] = 0x77, .data[2] = 0x90, .data[3] = 0xF7, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, { .channel = 188, .freq = 4940, .data[0] = 0x44, .data[1] = 0x77, .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, { .channel = 192, .freq = 4960, .data[0] = 0x44, .data[1] = 0x66, .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, { .channel = 196, .freq = 4980, .data[0] = 0x33, .data[1] = 0x66, .data[2] = 0x70, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, { .channel = 200, .freq = 5000, .data[0] = 0x22, .data[1] = 0x55, .data[2] = 0x60, .data[3] = 0xD7, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, { .channel = 204, .freq = 5020, .data[0] = 0x22, .data[1] = 0x55, .data[2] = 0x60, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, { .channel = 208, .freq = 5040, .data[0] = 0x22, .data[1] = 0x44, .data[2] = 0x50, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, { .channel = 212, .freq = 5060, .data[0] = 0x11, .data[1] = 0x44, .data[2] = 0x50, .data[3] = 0xA5, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, { .channel = 216, .freq = 5080, .data[0] = 0x00, .data[1] = 0x44, .data[2] = 0x40, .data[3] = 0xB6, .data[4] = 0x3C, .data[5] = 0x77, .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, }; static const struct b206x_channel b2063_chantbl[] = { { .channel = 1, .freq = 2412, .data[0] = 0x6F, .data[1] = 0x3C, .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 2, .freq = 2417, .data[0] = 0x6F, .data[1] = 0x3C, .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 3, .freq = 2422, .data[0] = 0x6F, .data[1] = 0x3C, .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 4, .freq = 2427, .data[0] = 0x6F, .data[1] = 0x2C, .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 5, .freq = 2432, .data[0] = 0x6F, .data[1] = 0x2C, .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 6, .freq = 2437, .data[0] = 0x6F, .data[1] = 0x2C, .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 7, .freq = 2442, .data[0] = 0x6F, .data[1] = 0x2C, .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 8, .freq = 2447, .data[0] = 0x6F, .data[1] = 0x2C, .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 9, .freq = 2452, .data[0] = 0x6F, .data[1] = 0x1C, .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 10, .freq = 2457, .data[0] = 0x6F, .data[1] = 0x1C, .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 11, .freq = 2462, .data[0] = 0x6E, .data[1] = 0x1C, .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 12, .freq = 2467, .data[0] = 0x6E, .data[1] = 0x1C, .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 13, .freq = 2472, .data[0] = 0x6E, .data[1] = 0x1C, .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 14, .freq = 2484, .data[0] = 0x6E, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x80, .data[11] = 0x70, }, { .channel = 34, .freq = 5170, .data[0] = 0x6A, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x02, .data[5] = 0x05, .data[6] = 0x0D, .data[7] = 0x0D, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 36, .freq = 5180, .data[0] = 0x6A, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x05, .data[6] = 0x0D, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 38, .freq = 5190, .data[0] = 0x6A, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 40, .freq = 5200, .data[0] = 0x69, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 42, .freq = 5210, .data[0] = 0x69, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, .data[6] = 0x0B, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 44, .freq = 5220, .data[0] = 0x69, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x04, .data[6] = 0x0B, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 46, .freq = 5230, .data[0] = 0x69, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, .data[6] = 0x0A, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 48, .freq = 5240, .data[0] = 0x69, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, .data[6] = 0x0A, .data[7] = 0x0A, .data[8] = 0x77, .data[9] = 0x60, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 52, .freq = 5260, .data[0] = 0x68, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x02, .data[6] = 0x09, .data[7] = 0x09, .data[8] = 0x77, .data[9] = 0x60, .data[10] = 0x20, .data[11] = 0x00, }, { .channel = 56, .freq = 5280, .data[0] = 0x68, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, .data[10] = 0x10, .data[11] = 0x00, }, { .channel = 60, .freq = 5300, .data[0] = 0x68, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, .data[10] = 0x10, .data[11] = 0x00, }, { .channel = 64, .freq = 5320, .data[0] = 0x67, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, .data[10] = 0x10, .data[11] = 0x00, }, { .channel = 100, .freq = 5500, .data[0] = 0x64, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x02, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 104, .freq = 5520, .data[0] = 0x64, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x01, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 108, .freq = 5540, .data[0] = 0x63, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x01, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 112, .freq = 5560, .data[0] = 0x63, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 116, .freq = 5580, .data[0] = 0x62, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 120, .freq = 5600, .data[0] = 0x62, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 124, .freq = 5620, .data[0] = 0x62, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 128, .freq = 5640, .data[0] = 0x61, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 132, .freq = 5660, .data[0] = 0x61, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 136, .freq = 5680, .data[0] = 0x61, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 140, .freq = 5700, .data[0] = 0x60, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 149, .freq = 5745, .data[0] = 0x60, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 153, .freq = 5765, .data[0] = 0x60, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 157, .freq = 5785, .data[0] = 0x60, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 161, .freq = 5805, .data[0] = 0x60, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 165, .freq = 5825, .data[0] = 0x60, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, .data[10] = 0x00, .data[11] = 0x00, }, { .channel = 184, .freq = 4920, .data[0] = 0x6E, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0E, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xC0, .data[10] = 0x50, .data[11] = 0x00, }, { .channel = 188, .freq = 4940, .data[0] = 0x6E, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0D, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, .data[10] = 0x50, .data[11] = 0x00, }, { .channel = 192, .freq = 4960, .data[0] = 0x6E, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, .data[10] = 0x50, .data[11] = 0x00, }, { .channel = 196, .freq = 4980, .data[0] = 0x6D, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, .data[10] = 0x40, .data[11] = 0x00, }, { .channel = 200, .freq = 5000, .data[0] = 0x6D, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0B, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, .data[10] = 0x40, .data[11] = 0x00, }, { .channel = 204, .freq = 5020, .data[0] = 0x6D, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0A, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, .data[10] = 0x40, .data[11] = 0x00, }, { .channel = 208, .freq = 5040, .data[0] = 0x6C, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x07, .data[5] = 0x09, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, .data[10] = 0x40, .data[11] = 0x00, }, { .channel = 212, .freq = 5060, .data[0] = 0x6C, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x06, .data[5] = 0x08, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, .data[10] = 0x40, .data[11] = 0x00, }, { .channel = 216, .freq = 5080, .data[0] = 0x6C, .data[1] = 0x0C, .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x05, .data[5] = 0x08, .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, .data[10] = 0x40, .data[11] = 0x00, }, }; static void lpphy_b2062_reset_pll_bias(struct b43_wldev *dev) { b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0xFF); udelay(20); if (dev->dev->chip_id == 0x5354) { b43_radio_write(dev, B2062_N_COMM1, 4); b43_radio_write(dev, B2062_S_RFPLL_CTL2, 4); } else { b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0); } udelay(5); } static void lpphy_b2062_vco_calib(struct b43_wldev *dev) { b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x42); b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x62); udelay(200); } static int lpphy_b2062_tune(struct b43_wldev *dev, unsigned int channel) { struct b43_phy_lp *lpphy = dev->phy.lp; struct ssb_bus *bus = dev->dev->sdev->bus; const struct b206x_channel *chandata = NULL; u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9; int i, err = 0; for (i = 0; i < ARRAY_SIZE(b2062_chantbl); i++) { if (b2062_chantbl[i].channel == channel) { chandata = &b2062_chantbl[i]; break; } } if (B43_WARN_ON(!chandata)) return -EINVAL; b43_radio_set(dev, B2062_S_RFPLL_CTL14, 0x04); b43_radio_write(dev, B2062_N_LGENA_TUNE0, chandata->data[0]); b43_radio_write(dev, B2062_N_LGENA_TUNE2, chandata->data[1]); b43_radio_write(dev, B2062_N_LGENA_TUNE3, chandata->data[2]); b43_radio_write(dev, B2062_N_TX_TUNE, chandata->data[3]); b43_radio_write(dev, B2062_S_LGENG_CTL1, chandata->data[4]); b43_radio_write(dev, B2062_N_LGENA_CTL5, chandata->data[5]); b43_radio_write(dev, B2062_N_LGENA_CTL6, chandata->data[6]); b43_radio_write(dev, B2062_N_TX_PGA, chandata->data[7]); b43_radio_write(dev, B2062_N_TX_PAD, chandata->data[8]); tmp1 = crystal_freq / 1000; tmp2 = lpphy->pdiv * 1000; b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xCC); b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0x07); lpphy_b2062_reset_pll_bias(dev); tmp3 = tmp2 * channel2freq_lp(channel); if (channel2freq_lp(channel) < 4000) tmp3 *= 2; tmp4 = 48 * tmp1; tmp6 = tmp3 / tmp4; tmp7 = tmp3 % tmp4; b43_radio_write(dev, B2062_S_RFPLL_CTL26, tmp6); tmp5 = tmp7 * 0x100; tmp6 = tmp5 / tmp4; tmp7 = tmp5 % tmp4; b43_radio_write(dev, B2062_S_RFPLL_CTL27, tmp6); tmp5 = tmp7 * 0x100; tmp6 = tmp5 / tmp4; tmp7 = tmp5 % tmp4; b43_radio_write(dev, B2062_S_RFPLL_CTL28, tmp6); tmp5 = tmp7 * 0x100; tmp6 = tmp5 / tmp4; tmp7 = tmp5 % tmp4; b43_radio_write(dev, B2062_S_RFPLL_CTL29, tmp6 + ((2 * tmp7) / tmp4)); tmp8 = b43_radio_read(dev, B2062_S_RFPLL_CTL19); tmp9 = ((2 * tmp3 * (tmp8 + 1)) + (3 * tmp1)) / (6 * tmp1); b43_radio_write(dev, B2062_S_RFPLL_CTL23, (tmp9 >> 8) + 16); b43_radio_write(dev, B2062_S_RFPLL_CTL24, tmp9 & 0xFF); lpphy_b2062_vco_calib(dev); if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) { b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xFC); b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0); lpphy_b2062_reset_pll_bias(dev); lpphy_b2062_vco_calib(dev); if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) err = -EIO; } b43_radio_mask(dev, B2062_S_RFPLL_CTL14, ~0x04); return err; } static void lpphy_b2063_vco_calib(struct b43_wldev *dev) { u16 tmp; b43_radio_mask(dev, B2063_PLL_SP1, ~0x40); tmp = b43_radio_read(dev, B2063_PLL_JTAG_CALNRST) & 0xF8; b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp); udelay(1); b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x4); udelay(1); b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x6); udelay(1); b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x7); udelay(300); b43_radio_set(dev, B2063_PLL_SP1, 0x40); } static int lpphy_b2063_tune(struct b43_wldev *dev, unsigned int channel) { struct ssb_bus *bus = dev->dev->sdev->bus; static const struct b206x_channel *chandata = NULL; u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; u32 freqref, vco_freq, val1, val2, val3, timeout, timeoutref, count; u16 old_comm15, scale; u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; int i, div = (crystal_freq <= 26000000 ? 1 : 2); for (i = 0; i < ARRAY_SIZE(b2063_chantbl); i++) { if (b2063_chantbl[i].channel == channel) { chandata = &b2063_chantbl[i]; break; } } if (B43_WARN_ON(!chandata)) return -EINVAL; b43_radio_write(dev, B2063_LOGEN_VCOBUF1, chandata->data[0]); b43_radio_write(dev, B2063_LOGEN_MIXER2, chandata->data[1]); b43_radio_write(dev, B2063_LOGEN_BUF2, chandata->data[2]); b43_radio_write(dev, B2063_LOGEN_RCCR1, chandata->data[3]); b43_radio_write(dev, B2063_A_RX_1ST3, chandata->data[4]); b43_radio_write(dev, B2063_A_RX_2ND1, chandata->data[5]); b43_radio_write(dev, B2063_A_RX_2ND4, chandata->data[6]); b43_radio_write(dev, B2063_A_RX_2ND7, chandata->data[7]); b43_radio_write(dev, B2063_A_RX_PS6, chandata->data[8]); b43_radio_write(dev, B2063_TX_RF_CTL2, chandata->data[9]); b43_radio_write(dev, B2063_TX_RF_CTL5, chandata->data[10]); b43_radio_write(dev, B2063_PA_CTL11, chandata->data[11]); old_comm15 = b43_radio_read(dev, B2063_COMM15); b43_radio_set(dev, B2063_COMM15, 0x1E); if (chandata->freq > 4000) /* spec says 2484, but 4000 is safer */ vco_freq = chandata->freq << 1; else vco_freq = chandata->freq << 2; freqref = crystal_freq * 3; val1 = lpphy_qdiv_roundup(crystal_freq, 1000000, 16); val2 = lpphy_qdiv_roundup(crystal_freq, 1000000 * div, 16); val3 = lpphy_qdiv_roundup(vco_freq, 3, 16); timeout = ((((8 * crystal_freq) / (div * 5000000)) + 1) >> 1) - 1; b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB3, 0x2); b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB6, 0xFFF8, timeout >> 2); b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, 0xFF9F,timeout << 5); timeoutref = ((((8 * crystal_freq) / (div * (timeout + 1))) + 999999) / 1000000) + 1; b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB5, timeoutref); count = lpphy_qdiv_roundup(val3, val2 + 16, 16); count *= (timeout + 1) * (timeoutref + 1); count--; b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, 0xF0, count >> 8); b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB8, count & 0xFF); tmp1 = ((val3 * 62500) / freqref) << 4; tmp2 = ((val3 * 62500) % freqref) << 4; while (tmp2 >= freqref) { tmp1++; tmp2 -= freqref; } b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG1, 0xFFE0, tmp1 >> 4); b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFE0F, tmp1 << 4); b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFFF0, tmp1 >> 16); b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG3, (tmp2 >> 8) & 0xFF); b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG4, tmp2 & 0xFF); b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF1, 0xB9); b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF2, 0x88); b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF3, 0x28); b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF4, 0x63); tmp3 = ((41 * (val3 - 3000)) /1200) + 27; tmp4 = lpphy_qdiv_roundup(132000 * tmp1, 8451, 16); if ((tmp4 + tmp3 - 1) / tmp3 > 60) { scale = 1; tmp5 = ((tmp4 + tmp3) / (tmp3 << 1)) - 8; } else { scale = 0; tmp5 = ((tmp4 + (tmp3 >> 1)) / tmp3) - 8; } b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFC0, tmp5); b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFBF, scale << 6); tmp6 = lpphy_qdiv_roundup(100 * val1, val3, 16); tmp6 *= (tmp5 * 8) * (scale + 1); if (tmp6 > 150) tmp6 = 0; b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFE0, tmp6); b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFDF, scale << 5); b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFFFB, 0x4); if (crystal_freq > 26000000) b43_radio_set(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0x2); else b43_radio_mask(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFD); if (val1 == 45) b43_radio_set(dev, B2063_PLL_JTAG_PLL_VCO1, 0x2); else b43_radio_mask(dev, B2063_PLL_JTAG_PLL_VCO1, 0xFD); b43_radio_set(dev, B2063_PLL_SP2, 0x3); udelay(1); b43_radio_mask(dev, B2063_PLL_SP2, 0xFFFC); lpphy_b2063_vco_calib(dev); b43_radio_write(dev, B2063_COMM15, old_comm15); return 0; } static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { struct b43_phy_lp *lpphy = dev->phy.lp; int err; if (dev->phy.radio_ver == 0x2063) { err = lpphy_b2063_tune(dev, new_channel); if (err) return err; } else { err = lpphy_b2062_tune(dev, new_channel); if (err) return err; lpphy_set_analog_filter(dev, new_channel); lpphy_adjust_gain_table(dev, channel2freq_lp(new_channel)); } lpphy->channel = new_channel; b43_write16(dev, B43_MMIO_CHANNEL, new_channel); return 0; } static int b43_lpphy_op_init(struct b43_wldev *dev) { int err; if (dev->dev->bus_type != B43_BUS_SSB) { b43err(dev->wl, "LP-PHY is supported only on SSB!\n"); return -EOPNOTSUPP; } lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs? lpphy_baseband_init(dev); lpphy_radio_init(dev); lpphy_calibrate_rc(dev); err = b43_lpphy_op_switch_channel(dev, 7); if (err) { b43dbg(dev->wl, "Switch to channel 7 failed, error = %d.\n", err); } lpphy_tx_pctl_init(dev); lpphy_calibration(dev); //TODO ACI init return 0; } static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev) { //TODO } static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) { //TODO return B43_TXPWR_RES_DONE; } static void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on) { if (on) { b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8); } else { b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007); b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007); } } static void b43_lpphy_op_pwork_15sec(struct b43_wldev *dev) { //TODO } const struct b43_phy_operations b43_phyops_lp = { .allocate = b43_lpphy_op_allocate, .free = b43_lpphy_op_free, .prepare_structs = b43_lpphy_op_prepare_structs, .init = b43_lpphy_op_init, .phy_read = b43_lpphy_op_read, .phy_write = b43_lpphy_op_write, .phy_maskset = b43_lpphy_op_maskset, .radio_read = b43_lpphy_op_radio_read, .radio_write = b43_lpphy_op_radio_write, .software_rfkill = b43_lpphy_op_software_rfkill, .switch_analog = b43_lpphy_op_switch_analog, .switch_channel = b43_lpphy_op_switch_channel, .get_default_chan = b43_lpphy_op_get_default_chan, .set_rx_antenna = b43_lpphy_op_set_rx_antenna, .recalc_txpower = b43_lpphy_op_recalc_txpower, .adjust_txpower = b43_lpphy_op_adjust_txpower, .pwork_15sec = b43_lpphy_op_pwork_15sec, .pwork_60sec = lpphy_calibration, }; compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_lcn.h0000644000175000017500000000167112026211315022663 0ustar mcgrofmcgrof#ifndef B43_PHY_LCN_H_ #define B43_PHY_LCN_H_ #include "phy_common.h" #define B43_PHY_LCN_AFE_CTL1 B43_PHY_OFDM(0x03B) #define B43_PHY_LCN_AFE_CTL2 B43_PHY_OFDM(0x03C) #define B43_PHY_LCN_RF_CTL1 B43_PHY_OFDM(0x04C) #define B43_PHY_LCN_RF_CTL2 B43_PHY_OFDM(0x04D) #define B43_PHY_LCN_TABLE_ADDR B43_PHY_OFDM(0x055) /* Table address */ #define B43_PHY_LCN_TABLE_DATALO B43_PHY_OFDM(0x056) /* Table data low */ #define B43_PHY_LCN_TABLE_DATAHI B43_PHY_OFDM(0x057) /* Table data high */ #define B43_PHY_LCN_RF_CTL3 B43_PHY_OFDM(0x0B0) #define B43_PHY_LCN_RF_CTL4 B43_PHY_OFDM(0x0B1) #define B43_PHY_LCN_RF_CTL5 B43_PHY_OFDM(0x0B7) #define B43_PHY_LCN_RF_CTL6 B43_PHY_OFDM(0x0F9) #define B43_PHY_LCN_RF_CTL7 B43_PHY_OFDM(0x0FA) struct b43_phy_lcn { bool hw_pwr_ctl; bool hw_pwr_ctl_capable; u8 tx_pwr_curr_idx; }; struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_lcn; #endif /* B43_PHY_LCN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_lcn.c0000644000175000017500000006271612026211315022665 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n LCN-PHY support Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. This file incorporates work covered by the following copyright and permission notice: Copyright (c) 2010 Broadcom Corporation Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. */ #include #include "b43.h" #include "phy_lcn.h" #include "tables_phy_lcn.h" #include "main.h" struct lcn_tx_gains { u16 gm_gain; u16 pga_gain; u16 pad_gain; u16 dac_gain; }; struct lcn_tx_iir_filter { u8 type; u16 values[16]; }; enum lcn_sense_type { B43_SENSE_TEMP, B43_SENSE_VBAT, }; /* In theory it's PHY common function, move if needed */ /* brcms_b_switch_macfreq */ static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode) { if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) { switch (spurmode) { case 2: /* 126 Mhz */ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); break; case 1: /* 123 Mhz */ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); break; default: /* 120 Mhz */ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); break; } } else if (dev->phy.type == B43_PHYTYPE_LCN) { switch (spurmode) { case 1: /* 82 Mhz */ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); break; default: /* 80 Mhz */ b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); break; } } } /************************************************** * Radio 2064. **************************************************/ /* wlc_lcnphy_radio_2064_channel_tune_4313 */ static void b43_radio_2064_channel_setup(struct b43_wldev *dev) { u16 save[2]; b43_radio_set(dev, 0x09d, 0x4); b43_radio_write(dev, 0x09e, 0xf); /* Channel specific values in theory, in practice always the same */ b43_radio_write(dev, 0x02a, 0xb); b43_radio_maskset(dev, 0x030, ~0x3, 0xa); b43_radio_maskset(dev, 0x091, ~0x3, 0); b43_radio_maskset(dev, 0x038, ~0xf, 0x7); b43_radio_maskset(dev, 0x030, ~0xc, 0x8); b43_radio_maskset(dev, 0x05e, ~0xf, 0x8); b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80); b43_radio_write(dev, 0x06c, 0x80); save[0] = b43_radio_read(dev, 0x044); save[1] = b43_radio_read(dev, 0x12b); b43_radio_set(dev, 0x044, 0x7); b43_radio_set(dev, 0x12b, 0xe); /* TODO */ b43_radio_write(dev, 0x040, 0xfb); b43_radio_write(dev, 0x041, 0x9a); b43_radio_write(dev, 0x042, 0xa3); b43_radio_write(dev, 0x043, 0x0c); /* TODO */ b43_radio_set(dev, 0x044, 0x0c); udelay(1); b43_radio_write(dev, 0x044, save[0]); b43_radio_write(dev, 0x12b, save[1]); if (dev->phy.rev == 1) { /* brcmsmac uses outdated 0x3 for 0x038 */ b43_radio_write(dev, 0x038, 0x0); b43_radio_write(dev, 0x091, 0x7); } } /* wlc_radio_2064_init */ static void b43_radio_2064_init(struct b43_wldev *dev) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_radio_write(dev, 0x09c, 0x0020); b43_radio_write(dev, 0x105, 0x0008); } else { /* TODO */ } b43_radio_write(dev, 0x032, 0x0062); b43_radio_write(dev, 0x033, 0x0019); b43_radio_write(dev, 0x090, 0x0010); b43_radio_write(dev, 0x010, 0x0000); if (dev->phy.rev == 1) { b43_radio_write(dev, 0x060, 0x007f); b43_radio_write(dev, 0x061, 0x0072); b43_radio_write(dev, 0x062, 0x007f); } b43_radio_write(dev, 0x01d, 0x0002); b43_radio_write(dev, 0x01e, 0x0006); b43_phy_write(dev, 0x4ea, 0x4688); b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2); b43_phy_mask(dev, 0x4eb, ~0x01c0); b43_phy_maskset(dev, 0x46a, 0xff00, 0x19); b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0); b43_radio_mask(dev, 0x05b, (u16) ~0xff02); b43_radio_set(dev, 0x004, 0x40); b43_radio_set(dev, 0x120, 0x10); b43_radio_set(dev, 0x078, 0x80); b43_radio_set(dev, 0x129, 0x2); b43_radio_set(dev, 0x057, 0x1); b43_radio_set(dev, 0x05b, 0x2); /* TODO: wait for some bit to be set */ b43_radio_read(dev, 0x05c); b43_radio_mask(dev, 0x05b, (u16) ~0xff02); b43_radio_mask(dev, 0x057, (u16) ~0xff01); b43_phy_write(dev, 0x933, 0x2d6b); b43_phy_write(dev, 0x934, 0x2d6b); b43_phy_write(dev, 0x935, 0x2d6b); b43_phy_write(dev, 0x936, 0x2d6b); b43_phy_write(dev, 0x937, 0x016b); b43_radio_mask(dev, 0x057, (u16) ~0xff02); b43_radio_write(dev, 0x0c2, 0x006f); } /************************************************** * Various PHY ops **************************************************/ /* wlc_lcnphy_toggle_afe_pwdn */ static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev) { u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2); u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1); b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1); b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1); b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1); b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1); b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2); b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1); } /* wlc_lcnphy_get_pa_gain */ static u16 b43_phy_lcn_get_pa_gain(struct b43_wldev *dev) { return (b43_phy_read(dev, 0x4fb) & 0x7f00) >> 8; } /* wlc_lcnphy_set_dac_gain */ static void b43_phy_lcn_set_dac_gain(struct b43_wldev *dev, u16 dac_gain) { u16 dac_ctrl; dac_ctrl = b43_phy_read(dev, 0x439); dac_ctrl = dac_ctrl & 0xc7f; dac_ctrl = dac_ctrl | (dac_gain << 7); b43_phy_maskset(dev, 0x439, ~0xfff, dac_ctrl); } /* wlc_lcnphy_set_bbmult */ static void b43_phy_lcn_set_bbmult(struct b43_wldev *dev, u8 m0) { b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x57), m0 << 8); } /* wlc_lcnphy_clear_tx_power_offsets */ static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev) { u8 i; if (1) { /* FIXME */ b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340); for (i = 0; i < 30; i++) { b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0); } } b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80); for (i = 0; i < 64; i++) { b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0); b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0); } } /* wlc_lcnphy_rev0_baseband_init */ static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev) { b43_radio_write(dev, 0x11c, 0); b43_phy_write(dev, 0x43b, 0); b43_phy_write(dev, 0x43c, 0); b43_phy_write(dev, 0x44c, 0); b43_phy_write(dev, 0x4e6, 0); b43_phy_write(dev, 0x4f9, 0); b43_phy_write(dev, 0x4b0, 0); b43_phy_write(dev, 0x938, 0); b43_phy_write(dev, 0x4b0, 0); b43_phy_write(dev, 0x44e, 0); b43_phy_set(dev, 0x567, 0x03); b43_phy_set(dev, 0x44a, 0x44); b43_phy_write(dev, 0x44a, 0x80); if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM)) ; /* TODO */ b43_phy_maskset(dev, 0x634, ~0xff, 0xc); if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) { b43_phy_maskset(dev, 0x634, ~0xff, 0xa); b43_phy_write(dev, 0x910, 0x1); } b43_phy_write(dev, 0x910, 0x1); b43_phy_maskset(dev, 0x448, ~0x300, 0x100); b43_phy_maskset(dev, 0x608, ~0xff, 0x17); b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea); } /* wlc_lcnphy_bu_tweaks */ static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev) { b43_phy_set(dev, 0x805, 0x1); b43_phy_maskset(dev, 0x42f, ~0x7, 0x3); b43_phy_maskset(dev, 0x030, ~0x7, 0x3); b43_phy_write(dev, 0x414, 0x1e10); b43_phy_write(dev, 0x415, 0x0640); b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700); b43_phy_set(dev, 0x44a, 0x44); b43_phy_write(dev, 0x44a, 0x80); b43_phy_maskset(dev, 0x434, ~0xff, 0xfd); b43_phy_maskset(dev, 0x420, ~0xff, 0x10); if (dev->dev->bus_sprom->board_rev >= 0x1204) b43_radio_set(dev, 0x09b, 0xf0); b43_phy_write(dev, 0x7d6, 0x0902); b43_phy_maskset(dev, 0x429, ~0xf, 0x9); b43_phy_maskset(dev, 0x429, ~(0x3f << 4), 0xe << 4); if (dev->phy.rev == 1) { b43_phy_maskset(dev, 0x423, ~0xff, 0x46); b43_phy_maskset(dev, 0x411, ~0xff, 1); b43_phy_set(dev, 0x434, 0xff); /* FIXME: update to wl */ /* TODO: wl operates on PHY 0x416, brcmsmac is outdated here */ b43_phy_maskset(dev, 0x656, ~0xf, 2); b43_phy_set(dev, 0x44d, 4); b43_radio_set(dev, 0x0f7, 0x4); b43_radio_mask(dev, 0x0f1, ~0x3); b43_radio_maskset(dev, 0x0f2, ~0xf8, 0x90); b43_radio_maskset(dev, 0x0f3, ~0x3, 0x2); b43_radio_maskset(dev, 0x0f3, ~0xf0, 0xa0); b43_radio_set(dev, 0x11f, 0x2); b43_phy_lcn_clear_tx_power_offsets(dev); /* TODO: something more? */ } } /* wlc_lcnphy_vbat_temp_sense_setup */ static void b43_phy_lcn_sense_setup(struct b43_wldev *dev, enum lcn_sense_type sense_type) { u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain; u16 auxpga_vmid; u8 tx_pwr_idx; u8 i; u16 save_radio_regs[6][2] = { { 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 }, { 0x025, 0 }, { 0x112, 0 }, }; u16 save_phy_regs[14][2] = { { 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 }, { 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 }, { 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 }, { 0x40d, 0 }, { 0x4a2, 0 }, }; u16 save_radio_4a4; msleep(1); /* Save */ for (i = 0; i < 6; i++) save_radio_regs[i][1] = b43_radio_read(dev, save_radio_regs[i][0]); for (i = 0; i < 14; i++) save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]); b43_mac_suspend(dev); save_radio_4a4 = b43_radio_read(dev, 0x4a4); /* wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); */ tx_pwr_idx = dev->phy.lcn->tx_pwr_curr_idx; /* Setup */ /* TODO: wlc_lcnphy_set_tx_pwr_by_index(pi, 127); */ b43_radio_set(dev, 0x007, 0x1); b43_radio_set(dev, 0x0ff, 0x10); b43_radio_set(dev, 0x11f, 0x4); b43_phy_mask(dev, 0x503, ~0x1); b43_phy_mask(dev, 0x503, ~0x4); b43_phy_mask(dev, 0x4a4, ~0x4000); b43_phy_mask(dev, 0x4a4, (u16) ~0x8000); b43_phy_mask(dev, 0x4d0, ~0x20); b43_phy_set(dev, 0x4a5, 0xff); b43_phy_maskset(dev, 0x4a5, ~0x7000, 0x5000); b43_phy_mask(dev, 0x4a5, ~0x700); b43_phy_maskset(dev, 0x40d, ~0xff, 64); b43_phy_maskset(dev, 0x40d, ~0x700, 0x600); b43_phy_maskset(dev, 0x4a2, ~0xff, 64); b43_phy_maskset(dev, 0x4a2, ~0x700, 0x600); b43_phy_maskset(dev, 0x4d9, ~0x70, 0x20); b43_phy_maskset(dev, 0x4d9, ~0x700, 0x300); b43_phy_maskset(dev, 0x4d9, ~0x7000, 0x1000); b43_phy_mask(dev, 0x4da, ~0x1000); b43_phy_set(dev, 0x4da, 0x2000); b43_phy_set(dev, 0x4a6, 0x8000); b43_radio_write(dev, 0x025, 0xc); b43_radio_set(dev, 0x005, 0x8); b43_phy_set(dev, 0x938, 0x4); b43_phy_set(dev, 0x939, 0x4); b43_phy_set(dev, 0x4a4, 0x1000); /* FIXME: don't hardcode */ b43_lcntab_write(dev, B43_LCNTAB16(0x8, 0x6), 0x640); switch (sense_type) { case B43_SENSE_TEMP: b43_phy_set(dev, 0x4d7, 0x8); b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x1000); auxpga_vmidcourse = 8; auxpga_vmidfine = 0x4; auxpga_gain = 2; b43_radio_set(dev, 0x082, 0x20); break; case B43_SENSE_VBAT: b43_phy_set(dev, 0x4d7, 0x8); b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x3000); auxpga_vmidcourse = 7; auxpga_vmidfine = 0xa; auxpga_gain = 2; break; } auxpga_vmid = (0x200 | (auxpga_vmidcourse << 4) | auxpga_vmidfine); b43_phy_set(dev, 0x4d8, 0x1); b43_phy_maskset(dev, 0x4d8, ~(0x3ff << 2), auxpga_vmid << 2); b43_phy_set(dev, 0x4d8, 0x2); b43_phy_maskset(dev, 0x4d8, ~(0x7 << 12), auxpga_gain << 12); b43_phy_set(dev, 0x4d0, 0x20); b43_radio_write(dev, 0x112, 0x6); b43_dummy_transmission(dev, true, false); /* Wait if not done */ if (!(b43_phy_read(dev, 0x476) & 0x8000)) udelay(10); /* Restore */ for (i = 0; i < 6; i++) b43_radio_write(dev, save_radio_regs[i][0], save_radio_regs[i][1]); for (i = 0; i < 14; i++) b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]); /* TODO: wlc_lcnphy_set_tx_pwr_by_index(tx_pwr_idx) */ b43_radio_write(dev, 0x4a4, save_radio_4a4); b43_mac_enable(dev); msleep(1); } static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev, u8 filter_type) { int i, j; u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920, 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930, 0x931, 0x932 }; /* Table is from brcmsmac, values for type 25 were outdated, probably * others need updating too */ struct lcn_tx_iir_filter tx_iir_filters_cck[] = { { 0, { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, 1582, 64, 128, 64 } }, { 1, { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, 1863, 93, 167, 93 } }, { 2, { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192, 778, 1582, 64, 128, 64 } }, { 3, { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205, 754, 1760, 170, 340, 170 } }, { 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205, 767, 1760, 256, 185, 256 } }, { 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205, 767, 1760, 256, 273, 256 } }, { 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205, 767, 1760, 256, 352, 256 } }, { 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205, 767, 1760, 128, 233, 128 } }, { 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766, 1760, 256, 1881, 256 } }, { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765, 1760, 262, 1878, 262 } }, /* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */ { 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614, 1864, 128, 384, 288 } }, { 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576, 613, 1864, 128, 384, 288 } }, { 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205, 754, 1760, 170, 340, 170 } }, }; for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) { if (tx_iir_filters_cck[i].type == filter_type) { for (j = 0; j < 16; j++) b43_phy_write(dev, phy_regs[j], tx_iir_filters_cck[i].values[j]); return true; } } return false; } static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev, u8 filter_type) { int i, j; u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902, 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c, 0x90d, 0x90e }; struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = { { 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } }, { 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750, 0xFE2B, 212, 0xFFCE, 212 } }, { 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748, 0xFEF2, 128, 0xFFE2, 128 } }, }; for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) { if (tx_iir_filters_ofdm[i].type == filter_type) { for (j = 0; j < 16; j++) b43_phy_write(dev, phy_regs[j], tx_iir_filters_ofdm[i].values[j]); return true; } } return false; } /* wlc_lcnphy_set_tx_gain_override */ static void b43_phy_lcn_set_tx_gain_override(struct b43_wldev *dev, bool enable) { b43_phy_maskset(dev, 0x4b0, ~(0x1 << 7), enable << 7); b43_phy_maskset(dev, 0x4b0, ~(0x1 << 14), enable << 14); b43_phy_maskset(dev, 0x43b, ~(0x1 << 6), enable << 6); } /* wlc_lcnphy_set_tx_gain */ static void b43_phy_lcn_set_tx_gain(struct b43_wldev *dev, struct lcn_tx_gains *target_gains) { u16 pa_gain = b43_phy_lcn_get_pa_gain(dev); b43_phy_write(dev, 0x4b5, (target_gains->gm_gain | (target_gains->pga_gain << 8))); b43_phy_maskset(dev, 0x4fb, ~0x7fff, (target_gains->pad_gain | (pa_gain << 8))); b43_phy_write(dev, 0x4fc, (target_gains->gm_gain | (target_gains->pga_gain << 8))); b43_phy_maskset(dev, 0x4fd, ~0x7fff, (target_gains->pad_gain | (pa_gain << 8))); b43_phy_lcn_set_dac_gain(dev, target_gains->dac_gain); b43_phy_lcn_set_tx_gain_override(dev, true); } /* wlc_lcnphy_tx_pwr_ctrl_init */ static void b43_phy_lcn_tx_pwr_ctl_init(struct b43_wldev *dev) { struct lcn_tx_gains tx_gains; u8 bbmult; b43_mac_suspend(dev); if (!dev->phy.lcn->hw_pwr_ctl_capable) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { tx_gains.gm_gain = 4; tx_gains.pga_gain = 12; tx_gains.pad_gain = 12; tx_gains.dac_gain = 0; bbmult = 150; } else { tx_gains.gm_gain = 7; tx_gains.pga_gain = 15; tx_gains.pad_gain = 14; tx_gains.dac_gain = 0; bbmult = 150; } b43_phy_lcn_set_tx_gain(dev, &tx_gains); b43_phy_lcn_set_bbmult(dev, bbmult); b43_phy_lcn_sense_setup(dev, B43_SENSE_TEMP); } else { b43err(dev->wl, "TX power control not supported for this HW\n"); } b43_mac_enable(dev); } /* wlc_lcnphy_txrx_spur_avoidance_mode */ static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev, bool enable) { if (enable) { b43_phy_write(dev, 0x942, 0x7); b43_phy_write(dev, 0x93b, ((1 << 13) + 23)); b43_phy_write(dev, 0x93c, ((1 << 13) + 1989)); b43_phy_write(dev, 0x44a, 0x084); b43_phy_write(dev, 0x44a, 0x080); b43_phy_write(dev, 0x6d3, 0x2222); b43_phy_write(dev, 0x6d3, 0x2220); } else { b43_phy_write(dev, 0x942, 0x0); b43_phy_write(dev, 0x93b, ((0 << 13) + 23)); b43_phy_write(dev, 0x93c, ((0 << 13) + 1989)); } b43_phy_switch_macfreq(dev, enable); } /************************************************** * Channel switching ops. **************************************************/ /* wlc_lcnphy_set_chanspec_tweaks */ static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel) { struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100); if (channel == 1 || channel == 2 || channel == 3 || channel == 4 || channel == 9 || channel == 10 || channel == 11 || channel == 12) { bcma_chipco_pll_write(cc, 0x2, 0x03000c04); bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0); bcma_chipco_pll_write(cc, 0x4, 0x200005c0); bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); b43_phy_write(dev, 0x942, 0); b43_phy_lcn_txrx_spur_avoidance_mode(dev, false); b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00); b43_phy_write(dev, 0x425, 0x5907); } else { bcma_chipco_pll_write(cc, 0x2, 0x03140c04); bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333); bcma_chipco_pll_write(cc, 0x4, 0x202c2820); bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); b43_phy_write(dev, 0x942, 0); b43_phy_lcn_txrx_spur_avoidance_mode(dev, true); b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00); b43_phy_write(dev, 0x425, 0x590a); } b43_phy_set(dev, 0x44a, 0x44); b43_phy_write(dev, 0x44a, 0x80); } /* wlc_phy_chanspec_set_lcnphy */ static int b43_phy_lcn_set_channel(struct b43_wldev *dev, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type) { static const u16 sfo_cfg[14][2] = { {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078}, {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067}, {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055}, }; b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value); b43_phy_set(dev, 0x44a, 0x44); b43_phy_write(dev, 0x44a, 0x80); b43_radio_2064_channel_setup(dev); mdelay(1); b43_phy_lcn_afe_set_unset(dev); b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]); b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]); if (channel->hw_value == 14) { b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8); b43_phy_lcn_load_tx_iir_cck_filter(dev, 3); } else { b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8); /* brcmsmac uses filter_type 2, we follow wl with 25 */ b43_phy_lcn_load_tx_iir_cck_filter(dev, 25); } /* brcmsmac uses filter_type 2, we follow wl with 0 */ b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0); b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3); return 0; } /************************************************** * Basic PHY ops. **************************************************/ static int b43_phy_lcn_op_allocate(struct b43_wldev *dev) { struct b43_phy_lcn *phy_lcn; phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL); if (!phy_lcn) return -ENOMEM; dev->phy.lcn = phy_lcn; return 0; } static void b43_phy_lcn_op_free(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_lcn *phy_lcn = phy->lcn; kfree(phy_lcn); phy->lcn = NULL; } static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_lcn *phy_lcn = phy->lcn; memset(phy_lcn, 0, sizeof(*phy_lcn)); } /* wlc_phy_init_lcnphy */ static int b43_phy_lcn_op_init(struct b43_wldev *dev) { struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; b43_phy_set(dev, 0x44a, 0x80); b43_phy_mask(dev, 0x44a, 0x7f); b43_phy_set(dev, 0x6d1, 0x80); b43_phy_write(dev, 0x6d0, 0x7); b43_phy_lcn_afe_set_unset(dev); b43_phy_write(dev, 0x60a, 0xa0); b43_phy_write(dev, 0x46a, 0x19); b43_phy_maskset(dev, 0x663, 0xFF00, 0x64); b43_phy_lcn_tables_init(dev); b43_phy_lcn_rev0_baseband_init(dev); b43_phy_lcn_bu_tweaks(dev); if (dev->phy.radio_ver == 0x2064) b43_radio_2064_init(dev); else B43_WARN_ON(1); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_phy_lcn_tx_pwr_ctl_init(dev); b43_switch_channel(dev, dev->phy.channel); bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9); bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd); /* TODO */ b43_phy_set(dev, 0x448, 0x4000); udelay(100); b43_phy_mask(dev, 0x448, ~0x4000); /* TODO */ return 0; } static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev, bool blocked) { if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) b43err(dev->wl, "MAC not suspended\n"); if (blocked) { b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00); b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00); b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00); b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2); b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808); b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8); b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8); } else { b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00); b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808); b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8); } } static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on) { if (on) { b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7); } else { b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7); b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7); } } static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { struct ieee80211_channel *channel = dev->wl->hw->conf.channel; enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if ((new_channel < 1) || (new_channel > 14)) return -EINVAL; } else { return -EINVAL; } return b43_phy_lcn_set_channel(dev, channel, channel_type); } static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) return 1; return 36; } static enum b43_txpwr_result b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) { return B43_TXPWR_RES_DONE; } static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev) { } /************************************************** * R/W ops. **************************************************/ static u16 b43_phy_lcn_op_read(struct b43_wldev *dev, u16 reg) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_phy_lcn_op_write(struct b43_wldev *dev, u16 reg, u16 value) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); } static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg) { /* LCN-PHY needs 0x200 for read access */ reg |= 0x200; b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO24_DATA); } static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO24_DATA, value); } /************************************************** * PHY ops struct. **************************************************/ const struct b43_phy_operations b43_phyops_lcn = { .allocate = b43_phy_lcn_op_allocate, .free = b43_phy_lcn_op_free, .prepare_structs = b43_phy_lcn_op_prepare_structs, .init = b43_phy_lcn_op_init, .phy_read = b43_phy_lcn_op_read, .phy_write = b43_phy_lcn_op_write, .phy_maskset = b43_phy_lcn_op_maskset, .radio_read = b43_phy_lcn_op_radio_read, .radio_write = b43_phy_lcn_op_radio_write, .software_rfkill = b43_phy_lcn_op_software_rfkill, .switch_analog = b43_phy_lcn_op_switch_analog, .switch_channel = b43_phy_lcn_op_switch_channel, .get_default_chan = b43_phy_lcn_op_get_default_chan, .recalc_txpower = b43_phy_lcn_op_recalc_txpower, .adjust_txpower = b43_phy_lcn_op_adjust_txpower, }; compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_ht.h0000644000175000017500000000412312026211315022515 0ustar mcgrofmcgrof#ifndef B43_PHY_HT_H_ #define B43_PHY_HT_H_ #include "phy_common.h" #define B43_PHY_HT_BBCFG 0x001 /* BB config */ #define B43_PHY_HT_BBCFG_RSTCCA 0x4000 /* Reset CCA */ #define B43_PHY_HT_BBCFG_RSTRX 0x8000 /* Reset RX */ #define B43_PHY_HT_BANDCTL 0x009 /* Band control */ #define B43_PHY_HT_BANDCTL_5GHZ 0x0001 /* Use the 5GHz band */ #define B43_PHY_HT_TABLE_ADDR 0x072 /* Table address */ #define B43_PHY_HT_TABLE_DATALO 0x073 /* Table data low */ #define B43_PHY_HT_TABLE_DATAHI 0x074 /* Table data high */ #define B43_PHY_HT_BW1 0x1CE #define B43_PHY_HT_BW2 0x1CF #define B43_PHY_HT_BW3 0x1D0 #define B43_PHY_HT_BW4 0x1D1 #define B43_PHY_HT_BW5 0x1D2 #define B43_PHY_HT_BW6 0x1D3 #define B43_PHY_HT_C1_CLIP1THRES B43_PHY_OFDM(0x00E) #define B43_PHY_HT_C2_CLIP1THRES B43_PHY_OFDM(0x04E) #define B43_PHY_HT_C3_CLIP1THRES B43_PHY_OFDM(0x08E) #define B43_PHY_HT_RF_SEQ_MODE B43_PHY_EXTG(0x000) #define B43_PHY_HT_RF_SEQ_TRIG B43_PHY_EXTG(0x003) #define B43_PHY_HT_RF_SEQ_TRIG_RX2TX 0x0001 /* RX2TX */ #define B43_PHY_HT_RF_SEQ_TRIG_TX2RX 0x0002 /* TX2RX */ #define B43_PHY_HT_RF_SEQ_TRIG_UPGH 0x0004 /* Update gain H */ #define B43_PHY_HT_RF_SEQ_TRIG_UPGL 0x0008 /* Update gain L */ #define B43_PHY_HT_RF_SEQ_TRIG_UPGU 0x0010 /* Update gain U */ #define B43_PHY_HT_RF_SEQ_TRIG_RST2RX 0x0020 /* Reset to RX */ #define B43_PHY_HT_RF_SEQ_STATUS B43_PHY_EXTG(0x004) /* Values for the status are the same as for the trigger */ #define B43_PHY_HT_RF_CTL1 B43_PHY_EXTG(0x010) #define B43_PHY_HT_AFE_CTL1 B43_PHY_EXTG(0x110) #define B43_PHY_HT_AFE_CTL2 B43_PHY_EXTG(0x111) #define B43_PHY_HT_AFE_CTL3 B43_PHY_EXTG(0x114) #define B43_PHY_HT_AFE_CTL4 B43_PHY_EXTG(0x115) #define B43_PHY_HT_AFE_CTL5 B43_PHY_EXTG(0x118) #define B43_PHY_HT_AFE_CTL6 B43_PHY_EXTG(0x119) /* Values for PHY registers used on channel switching */ struct b43_phy_ht_channeltab_e_phy { u16 bw1; u16 bw2; u16 bw3; u16 bw4; u16 bw5; u16 bw6; }; struct b43_phy_ht { }; struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_ht; #endif /* B43_PHY_HT_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_ht.c0000644000175000017500000004323312026211315022515 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11n HT-PHY support Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "b43.h" #include "phy_ht.h" #include "tables_phy_ht.h" #include "radio_2059.h" #include "main.h" /************************************************** * Radio 2059. **************************************************/ static void b43_radio_2059_channel_setup(struct b43_wldev *dev, const struct b43_phy_ht_channeltab_e_radio2059 *e) { u8 i; u16 routing; b43_radio_write(dev, 0x16, e->radio_syn16); b43_radio_write(dev, 0x17, e->radio_syn17); b43_radio_write(dev, 0x22, e->radio_syn22); b43_radio_write(dev, 0x25, e->radio_syn25); b43_radio_write(dev, 0x27, e->radio_syn27); b43_radio_write(dev, 0x28, e->radio_syn28); b43_radio_write(dev, 0x29, e->radio_syn29); b43_radio_write(dev, 0x2c, e->radio_syn2c); b43_radio_write(dev, 0x2d, e->radio_syn2d); b43_radio_write(dev, 0x37, e->radio_syn37); b43_radio_write(dev, 0x41, e->radio_syn41); b43_radio_write(dev, 0x43, e->radio_syn43); b43_radio_write(dev, 0x47, e->radio_syn47); b43_radio_write(dev, 0x4a, e->radio_syn4a); b43_radio_write(dev, 0x58, e->radio_syn58); b43_radio_write(dev, 0x5a, e->radio_syn5a); b43_radio_write(dev, 0x6a, e->radio_syn6a); b43_radio_write(dev, 0x6d, e->radio_syn6d); b43_radio_write(dev, 0x6e, e->radio_syn6e); b43_radio_write(dev, 0x92, e->radio_syn92); b43_radio_write(dev, 0x98, e->radio_syn98); for (i = 0; i < 2; i++) { routing = i ? R2059_RXRX1 : R2059_TXRX0; b43_radio_write(dev, routing | 0x4a, e->radio_rxtx4a); b43_radio_write(dev, routing | 0x58, e->radio_rxtx58); b43_radio_write(dev, routing | 0x5a, e->radio_rxtx5a); b43_radio_write(dev, routing | 0x6a, e->radio_rxtx6a); b43_radio_write(dev, routing | 0x6d, e->radio_rxtx6d); b43_radio_write(dev, routing | 0x6e, e->radio_rxtx6e); b43_radio_write(dev, routing | 0x92, e->radio_rxtx92); b43_radio_write(dev, routing | 0x98, e->radio_rxtx98); } udelay(50); /* Calibration */ b43_radio_mask(dev, 0x2b, ~0x1); b43_radio_mask(dev, 0x2e, ~0x4); b43_radio_set(dev, 0x2e, 0x4); b43_radio_set(dev, 0x2b, 0x1); udelay(300); } static void b43_radio_2059_init(struct b43_wldev *dev) { const u16 routing[] = { R2059_SYN, R2059_TXRX0, R2059_RXRX1 }; const u16 radio_values[3][2] = { { 0x61, 0xE9 }, { 0x69, 0xD5 }, { 0x73, 0x99 }, }; u16 i, j; b43_radio_write(dev, R2059_ALL | 0x51, 0x0070); b43_radio_write(dev, R2059_ALL | 0x5a, 0x0003); for (i = 0; i < ARRAY_SIZE(routing); i++) b43_radio_set(dev, routing[i] | 0x146, 0x3); b43_radio_set(dev, 0x2e, 0x0078); b43_radio_set(dev, 0xc0, 0x0080); msleep(2); b43_radio_mask(dev, 0x2e, ~0x0078); b43_radio_mask(dev, 0xc0, ~0x0080); if (1) { /* FIXME */ b43_radio_set(dev, R2059_RXRX1 | 0x4, 0x1); udelay(10); b43_radio_set(dev, R2059_RXRX1 | 0x0BF, 0x1); b43_radio_maskset(dev, R2059_RXRX1 | 0x19B, 0x3, 0x2); b43_radio_set(dev, R2059_RXRX1 | 0x4, 0x2); udelay(100); b43_radio_mask(dev, R2059_RXRX1 | 0x4, ~0x2); for (i = 0; i < 10000; i++) { if (b43_radio_read(dev, R2059_RXRX1 | 0x145) & 1) { i = 0; break; } udelay(100); } if (i) b43err(dev->wl, "radio 0x945 timeout\n"); b43_radio_mask(dev, R2059_RXRX1 | 0x4, ~0x1); b43_radio_set(dev, 0xa, 0x60); for (i = 0; i < 3; i++) { b43_radio_write(dev, 0x17F, radio_values[i][0]); b43_radio_write(dev, 0x13D, 0x6E); b43_radio_write(dev, 0x13E, radio_values[i][1]); b43_radio_write(dev, 0x13C, 0x55); for (j = 0; j < 10000; j++) { if (b43_radio_read(dev, 0x140) & 2) { j = 0; break; } udelay(500); } if (j) b43err(dev->wl, "radio 0x140 timeout\n"); b43_radio_write(dev, 0x13C, 0x15); } b43_radio_mask(dev, 0x17F, ~0x1); } b43_radio_mask(dev, 0x11, ~0x0008); } /************************************************** * Various PHY ops **************************************************/ static void b43_phy_ht_zero_extg(struct b43_wldev *dev) { u8 i, j; u16 base[] = { 0x40, 0x60, 0x80 }; for (i = 0; i < ARRAY_SIZE(base); i++) { for (j = 0; j < 4; j++) b43_phy_write(dev, B43_PHY_EXTG(base[i] + j), 0); } for (i = 0; i < ARRAY_SIZE(base); i++) b43_phy_write(dev, B43_PHY_EXTG(base[i] + 0xc), 0); } /* Some unknown AFE (Analog Frondned) op */ static void b43_phy_ht_afe_unk1(struct b43_wldev *dev) { u8 i; const u16 ctl_regs[3][2] = { { B43_PHY_HT_AFE_CTL1, B43_PHY_HT_AFE_CTL2 }, { B43_PHY_HT_AFE_CTL3, B43_PHY_HT_AFE_CTL4 }, { B43_PHY_HT_AFE_CTL5, B43_PHY_HT_AFE_CTL6}, }; for (i = 0; i < 3; i++) { /* TODO: verify masks&sets */ b43_phy_set(dev, ctl_regs[i][1], 0x4); b43_phy_set(dev, ctl_regs[i][0], 0x4); b43_phy_mask(dev, ctl_regs[i][1], ~0x1); b43_phy_set(dev, ctl_regs[i][0], 0x1); b43_httab_write(dev, B43_HTTAB16(8, 5 + (i * 0x10)), 0); b43_phy_mask(dev, ctl_regs[i][0], ~0x4); } } static void b43_phy_ht_force_rf_sequence(struct b43_wldev *dev, u16 rf_seq) { u8 i; u16 save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE); b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, 0x3); b43_phy_set(dev, B43_PHY_HT_RF_SEQ_TRIG, rf_seq); for (i = 0; i < 200; i++) { if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & rf_seq)) { i = 0; break; } msleep(1); } if (i) b43err(dev->wl, "Forcing RF sequence timeout\n"); b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode); } static void b43_phy_ht_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) { clip_st[0] = b43_phy_read(dev, B43_PHY_HT_C1_CLIP1THRES); clip_st[1] = b43_phy_read(dev, B43_PHY_HT_C2_CLIP1THRES); clip_st[2] = b43_phy_read(dev, B43_PHY_HT_C3_CLIP1THRES); } static void b43_phy_ht_bphy_init(struct b43_wldev *dev) { unsigned int i; u16 val; val = 0x1E1F; for (i = 0; i < 16; i++) { b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val); val -= 0x202; } val = 0x3E3F; for (i = 0; i < 16; i++) { b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val); val -= 0x202; } b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); } /************************************************** * Channel switching ops. **************************************************/ static void b43_phy_ht_channel_setup(struct b43_wldev *dev, const struct b43_phy_ht_channeltab_e_phy *e, struct ieee80211_channel *new_channel) { bool old_band_5ghz; u8 i; old_band_5ghz = b43_phy_read(dev, B43_PHY_HT_BANDCTL) & 0; /* FIXME */ if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) { /* TODO */ } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) { /* TODO */ } b43_phy_write(dev, B43_PHY_HT_BW1, e->bw1); b43_phy_write(dev, B43_PHY_HT_BW2, e->bw2); b43_phy_write(dev, B43_PHY_HT_BW3, e->bw3); b43_phy_write(dev, B43_PHY_HT_BW4, e->bw4); b43_phy_write(dev, B43_PHY_HT_BW5, e->bw5); b43_phy_write(dev, B43_PHY_HT_BW6, e->bw6); /* TODO: some ops on PHY regs 0x0B0 and 0xC0A */ /* TODO: separated function? */ for (i = 0; i < 3; i++) { u16 mask; u32 tmp = b43_httab_read(dev, B43_HTTAB32(26, 0xE8)); if (0) /* FIXME */ mask = 0x2 << (i * 4); else mask = 0; b43_phy_mask(dev, B43_PHY_EXTG(0x108), mask); b43_httab_write(dev, B43_HTTAB16(7, 0x110 + i), tmp >> 16); b43_httab_write(dev, B43_HTTAB8(13, 0x63 + (i * 4)), tmp & 0xFF); b43_httab_write(dev, B43_HTTAB8(13, 0x73 + (i * 4)), tmp & 0xFF); } b43_phy_write(dev, 0x017e, 0x3830); } static int b43_phy_ht_set_channel(struct b43_wldev *dev, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type) { struct b43_phy *phy = &dev->phy; const struct b43_phy_ht_channeltab_e_radio2059 *chent_r2059 = NULL; if (phy->radio_ver == 0x2059) { chent_r2059 = b43_phy_ht_get_channeltab_e_r2059(dev, channel->center_freq); if (!chent_r2059) return -ESRCH; } else { return -ESRCH; } /* TODO: In case of N-PHY some bandwidth switching goes here */ if (phy->radio_ver == 0x2059) { b43_radio_2059_channel_setup(dev, chent_r2059); b43_phy_ht_channel_setup(dev, &(chent_r2059->phy_regs), channel); } else { return -ESRCH; } return 0; } /************************************************** * Basic PHY ops. **************************************************/ static int b43_phy_ht_op_allocate(struct b43_wldev *dev) { struct b43_phy_ht *phy_ht; phy_ht = kzalloc(sizeof(*phy_ht), GFP_KERNEL); if (!phy_ht) return -ENOMEM; dev->phy.ht = phy_ht; return 0; } static void b43_phy_ht_op_prepare_structs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_ht *phy_ht = phy->ht; memset(phy_ht, 0, sizeof(*phy_ht)); } static int b43_phy_ht_op_init(struct b43_wldev *dev) { u16 tmp; u16 clip_state[3]; b43_phy_ht_tables_init(dev); b43_phy_mask(dev, 0x0be, ~0x2); b43_phy_set(dev, 0x23f, 0x7ff); b43_phy_set(dev, 0x240, 0x7ff); b43_phy_set(dev, 0x241, 0x7ff); b43_phy_ht_zero_extg(dev); b43_phy_mask(dev, B43_PHY_EXTG(0), ~0x3); b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0); b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0); b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0); b43_phy_write(dev, B43_PHY_EXTG(0x103), 0x20); b43_phy_write(dev, B43_PHY_EXTG(0x101), 0x20); b43_phy_write(dev, 0x20d, 0xb8); b43_phy_write(dev, B43_PHY_EXTG(0x14f), 0xc8); b43_phy_write(dev, 0x70, 0x50); b43_phy_write(dev, 0x1ff, 0x30); if (0) /* TODO: condition */ ; /* TODO: PHY op on reg 0x217 */ b43_phy_read(dev, 0xb0); /* TODO: what for? */ b43_phy_set(dev, 0xb0, 0x1); b43_phy_set(dev, 0xb1, 0x91); b43_phy_write(dev, 0x32f, 0x0003); b43_phy_write(dev, 0x077, 0x0010); b43_phy_write(dev, 0x0b4, 0x0258); b43_phy_mask(dev, 0x17e, ~0x4000); b43_phy_write(dev, 0x0b9, 0x0072); b43_httab_write_few(dev, B43_HTTAB16(7, 0x14e), 2, 0x010f, 0x010f); b43_httab_write_few(dev, B43_HTTAB16(7, 0x15e), 2, 0x010f, 0x010f); b43_httab_write_few(dev, B43_HTTAB16(7, 0x16e), 2, 0x010f, 0x010f); b43_phy_ht_afe_unk1(dev); b43_httab_write_few(dev, B43_HTTAB16(7, 0x130), 9, 0x777, 0x111, 0x111, 0x777, 0x111, 0x111, 0x777, 0x111, 0x111); b43_httab_write(dev, B43_HTTAB16(7, 0x120), 0x0777); b43_httab_write(dev, B43_HTTAB16(7, 0x124), 0x0777); b43_httab_write(dev, B43_HTTAB16(8, 0x00), 0x02); b43_httab_write(dev, B43_HTTAB16(8, 0x10), 0x02); b43_httab_write(dev, B43_HTTAB16(8, 0x20), 0x02); b43_httab_write_few(dev, B43_HTTAB16(8, 0x08), 4, 0x8e, 0x96, 0x96, 0x96); b43_httab_write_few(dev, B43_HTTAB16(8, 0x18), 4, 0x8f, 0x9f, 0x9f, 0x9f); b43_httab_write_few(dev, B43_HTTAB16(8, 0x28), 4, 0x8f, 0x9f, 0x9f, 0x9f); b43_httab_write_few(dev, B43_HTTAB16(8, 0x0c), 4, 0x2, 0x2, 0x2, 0x2); b43_httab_write_few(dev, B43_HTTAB16(8, 0x1c), 4, 0x2, 0x2, 0x2, 0x2); b43_httab_write_few(dev, B43_HTTAB16(8, 0x2c), 4, 0x2, 0x2, 0x2, 0x2); b43_phy_maskset(dev, 0x0280, 0xff00, 0x3e); b43_phy_maskset(dev, 0x0283, 0xff00, 0x3e); b43_phy_maskset(dev, B43_PHY_OFDM(0x0141), 0xff00, 0x46); b43_phy_maskset(dev, 0x0283, 0xff00, 0x40); b43_httab_write_few(dev, B43_HTTAB16(00, 0x8), 4, 0x09, 0x0e, 0x13, 0x18); b43_httab_write_few(dev, B43_HTTAB16(01, 0x8), 4, 0x09, 0x0e, 0x13, 0x18); /* TODO: Did wl mean 2 instead of 40? */ b43_httab_write_few(dev, B43_HTTAB16(40, 0x8), 4, 0x09, 0x0e, 0x13, 0x18); b43_phy_maskset(dev, B43_PHY_OFDM(0x24), 0x3f, 0xd); b43_phy_maskset(dev, B43_PHY_OFDM(0x64), 0x3f, 0xd); b43_phy_maskset(dev, B43_PHY_OFDM(0xa4), 0x3f, 0xd); b43_phy_set(dev, B43_PHY_EXTG(0x060), 0x1); b43_phy_set(dev, B43_PHY_EXTG(0x064), 0x1); b43_phy_set(dev, B43_PHY_EXTG(0x080), 0x1); b43_phy_set(dev, B43_PHY_EXTG(0x084), 0x1); /* Copy some tables entries */ tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x144)); b43_httab_write(dev, B43_HTTAB16(7, 0x14a), tmp); tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x154)); b43_httab_write(dev, B43_HTTAB16(7, 0x15a), tmp); tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x164)); b43_httab_write(dev, B43_HTTAB16(7, 0x16a), tmp); /* Reset CCA */ b43_phy_force_clock(dev, true); tmp = b43_phy_read(dev, B43_PHY_HT_BBCFG); b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp | B43_PHY_HT_BBCFG_RSTCCA); b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp & ~B43_PHY_HT_BBCFG_RSTCCA); b43_phy_force_clock(dev, false); b43_mac_phy_clock_set(dev, true); b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RX2TX); b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX); /* TODO: PHY op on reg 0xb0 */ /* TODO: Should we restore it? Or store it in global PHY info? */ b43_phy_ht_read_clip_detection(dev, clip_state); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_phy_ht_bphy_init(dev); b43_httab_write_bulk(dev, B43_HTTAB32(0x1a, 0xc0), B43_HTTAB_1A_C0_LATE_SIZE, b43_httab_0x1a_0xc0_late); return 0; } static void b43_phy_ht_op_free(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_ht *phy_ht = phy->ht; kfree(phy_ht); phy->ht = NULL; } /* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */ static void b43_phy_ht_op_software_rfkill(struct b43_wldev *dev, bool blocked) { if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) b43err(dev->wl, "MAC not suspended\n"); /* In the following PHY ops we copy wl's dummy behaviour. * TODO: Find out if reads (currently hidden in masks/masksets) are * needed and replace following ops with just writes or w&r. * Note: B43_PHY_HT_RF_CTL1 register is tricky, wrong operation can * cause delayed (!) machine lock up. */ if (blocked) { b43_phy_mask(dev, B43_PHY_HT_RF_CTL1, 0); } else { b43_phy_mask(dev, B43_PHY_HT_RF_CTL1, 0); b43_phy_maskset(dev, B43_PHY_HT_RF_CTL1, 0, 0x1); b43_phy_mask(dev, B43_PHY_HT_RF_CTL1, 0); b43_phy_maskset(dev, B43_PHY_HT_RF_CTL1, 0, 0x2); if (dev->phy.radio_ver == 0x2059) b43_radio_2059_init(dev); else B43_WARN_ON(1); b43_switch_channel(dev, dev->phy.channel); } } static void b43_phy_ht_op_switch_analog(struct b43_wldev *dev, bool on) { if (on) { b43_phy_write(dev, B43_PHY_HT_AFE_CTL2, 0x00cd); b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0x0000); b43_phy_write(dev, B43_PHY_HT_AFE_CTL4, 0x00cd); b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0x0000); b43_phy_write(dev, B43_PHY_HT_AFE_CTL6, 0x00cd); b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0x0000); } else { b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0x07ff); b43_phy_write(dev, B43_PHY_HT_AFE_CTL2, 0x00fd); b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0x07ff); b43_phy_write(dev, B43_PHY_HT_AFE_CTL4, 0x00fd); b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0x07ff); b43_phy_write(dev, B43_PHY_HT_AFE_CTL6, 0x00fd); } } static int b43_phy_ht_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { struct ieee80211_channel *channel = dev->wl->hw->conf.channel; enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { if ((new_channel < 1) || (new_channel > 14)) return -EINVAL; } else { return -EINVAL; } return b43_phy_ht_set_channel(dev, channel, channel_type); } static unsigned int b43_phy_ht_op_get_default_chan(struct b43_wldev *dev) { if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) return 11; return 36; } /************************************************** * R/W ops. **************************************************/ static u16 b43_phy_ht_op_read(struct b43_wldev *dev, u16 reg) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_phy_ht_op_write(struct b43_wldev *dev, u16 reg, u16 value) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } static void b43_phy_ht_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); } static u16 b43_phy_ht_op_radio_read(struct b43_wldev *dev, u16 reg) { /* HT-PHY needs 0x200 for read access */ reg |= 0x200; b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO24_DATA); } static void b43_phy_ht_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO24_DATA, value); } static enum b43_txpwr_result b43_phy_ht_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) { return B43_TXPWR_RES_DONE; } static void b43_phy_ht_op_adjust_txpower(struct b43_wldev *dev) { } /************************************************** * PHY ops struct. **************************************************/ const struct b43_phy_operations b43_phyops_ht = { .allocate = b43_phy_ht_op_allocate, .free = b43_phy_ht_op_free, .prepare_structs = b43_phy_ht_op_prepare_structs, .init = b43_phy_ht_op_init, .phy_read = b43_phy_ht_op_read, .phy_write = b43_phy_ht_op_write, .phy_maskset = b43_phy_ht_op_maskset, .radio_read = b43_phy_ht_op_radio_read, .radio_write = b43_phy_ht_op_radio_write, .software_rfkill = b43_phy_ht_op_software_rfkill, .switch_analog = b43_phy_ht_op_switch_analog, .switch_channel = b43_phy_ht_op_switch_channel, .get_default_chan = b43_phy_ht_op_get_default_chan, .recalc_txpower = b43_phy_ht_op_recalc_txpower, .adjust_txpower = b43_phy_ht_op_adjust_txpower, }; compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_g.h0000644000175000017500000001564412026211315022342 0ustar mcgrofmcgrof#ifndef LINUX_B43_PHY_G_H_ #define LINUX_B43_PHY_G_H_ /* OFDM PHY registers are defined in the A-PHY header. */ #include "phy_a.h" /* CCK (B) PHY Registers */ #define B43_PHY_VERSION_CCK B43_PHY_CCK(0x00) /* Versioning register for B-PHY */ #define B43_PHY_CCKBBANDCFG B43_PHY_CCK(0x01) /* Contains antenna 0/1 control bit */ #define B43_PHY_PGACTL B43_PHY_CCK(0x15) /* PGA control */ #define B43_PHY_PGACTL_LPF 0x1000 /* Low pass filter (?) */ #define B43_PHY_PGACTL_LOWBANDW 0x0040 /* Low bandwidth flag */ #define B43_PHY_PGACTL_UNKNOWN 0xEFA0 #define B43_PHY_FBCTL1 B43_PHY_CCK(0x18) /* Frequency bandwidth control 1 */ #define B43_PHY_ITSSI B43_PHY_CCK(0x29) /* Idle TSSI */ #define B43_PHY_LO_LEAKAGE B43_PHY_CCK(0x2D) /* Measured LO leakage */ #define B43_PHY_ENERGY B43_PHY_CCK(0x33) /* Energy */ #define B43_PHY_SYNCCTL B43_PHY_CCK(0x35) #define B43_PHY_FBCTL2 B43_PHY_CCK(0x38) /* Frequency bandwidth control 2 */ #define B43_PHY_DACCTL B43_PHY_CCK(0x60) /* DAC control */ #define B43_PHY_RCCALOVER B43_PHY_CCK(0x78) /* RC calibration override */ /* Extended G-PHY Registers */ #define B43_PHY_CLASSCTL B43_PHY_EXTG(0x02) /* Classify control */ #define B43_PHY_GTABCTL B43_PHY_EXTG(0x03) /* G-PHY table control (see below) */ #define B43_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */ #define B43_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */ #define B43_PHY_GTABNR_SHIFT 10 #define B43_PHY_GTABDATA B43_PHY_EXTG(0x04) /* G-PHY table data */ #define B43_PHY_LO_MASK B43_PHY_EXTG(0x0F) /* Local Oscillator control mask */ #define B43_PHY_LO_CTL B43_PHY_EXTG(0x10) /* Local Oscillator control */ #define B43_PHY_RFOVER B43_PHY_EXTG(0x11) /* RF override */ #define B43_PHY_RFOVERVAL B43_PHY_EXTG(0x12) /* RF override value */ #define B43_PHY_RFOVERVAL_EXTLNA 0x8000 #define B43_PHY_RFOVERVAL_LNA 0x7000 #define B43_PHY_RFOVERVAL_LNA_SHIFT 12 #define B43_PHY_RFOVERVAL_PGA 0x0F00 #define B43_PHY_RFOVERVAL_PGA_SHIFT 8 #define B43_PHY_RFOVERVAL_UNK 0x0010 /* Unknown, always set. */ #define B43_PHY_RFOVERVAL_TRSWRX 0x00E0 #define B43_PHY_RFOVERVAL_BW 0x0003 /* Bandwidth flags */ #define B43_PHY_RFOVERVAL_BW_LPF 0x0001 /* Low Pass Filter */ #define B43_PHY_RFOVERVAL_BW_LBW 0x0002 /* Low Bandwidth (when set), high when unset */ #define B43_PHY_ANALOGOVER B43_PHY_EXTG(0x14) /* Analog override */ #define B43_PHY_ANALOGOVERVAL B43_PHY_EXTG(0x15) /* Analog override value */ /*** G-PHY table numbers */ #define B43_GTAB(number, offset) (((number) << B43_PHY_GTABNR_SHIFT) | (offset)) #define B43_GTAB_NRSSI B43_GTAB(0x00, 0) #define B43_GTAB_TRFEMW B43_GTAB(0x0C, 0x120) #define B43_GTAB_ORIGTR B43_GTAB(0x2E, 0x298) u16 b43_gtab_read(struct b43_wldev *dev, u16 table, u16 offset); void b43_gtab_write(struct b43_wldev *dev, u16 table, u16 offset, u16 value); /* Returns the boolean whether "TX Magnification" is enabled. */ #define has_tx_magnification(phy) \ (((phy)->rev >= 2) && \ ((phy)->radio_ver == 0x2050) && \ ((phy)->radio_rev == 8)) /* Card uses the loopback gain stuff */ #define has_loopback_gain(phy) \ (((phy)->rev > 1) || ((phy)->gmode)) /* Radio Attenuation (RF Attenuation) */ struct b43_rfatt { u8 att; /* Attenuation value */ bool with_padmix; /* Flag, PAD Mixer enabled. */ }; struct b43_rfatt_list { /* Attenuation values list */ const struct b43_rfatt *list; u8 len; /* Minimum/Maximum attenuation values */ u8 min_val; u8 max_val; }; /* Returns true, if the values are the same. */ static inline bool b43_compare_rfatt(const struct b43_rfatt *a, const struct b43_rfatt *b) { return ((a->att == b->att) && (a->with_padmix == b->with_padmix)); } /* Baseband Attenuation */ struct b43_bbatt { u8 att; /* Attenuation value */ }; struct b43_bbatt_list { /* Attenuation values list */ const struct b43_bbatt *list; u8 len; /* Minimum/Maximum attenuation values */ u8 min_val; u8 max_val; }; /* Returns true, if the values are the same. */ static inline bool b43_compare_bbatt(const struct b43_bbatt *a, const struct b43_bbatt *b) { return (a->att == b->att); } /* tx_control bits. */ #define B43_TXCTL_PA3DB 0x40 /* PA Gain 3dB */ #define B43_TXCTL_PA2DB 0x20 /* PA Gain 2dB */ #define B43_TXCTL_TXMIX 0x10 /* TX Mixer Gain */ struct b43_txpower_lo_control; struct b43_phy_g { /* ACI (adjacent channel interference) flags. */ bool aci_enable; bool aci_wlan_automatic; bool aci_hw_rssi; /* Radio switched on/off */ bool radio_on; struct { /* Values saved when turning the radio off. * They are needed when turning it on again. */ bool valid; u16 rfover; u16 rfoverval; } radio_off_context; u16 minlowsig[2]; u16 minlowsigpos[2]; /* Pointer to the table used to convert a * TSSI value to dBm-Q5.2 */ const s8 *tssi2dbm; /* tssi2dbm is kmalloc()ed. Only used for free()ing. */ bool dyn_tssi_tbl; /* Target idle TSSI */ int tgt_idle_tssi; /* Current idle TSSI */ int cur_idle_tssi; /* The current average TSSI. */ u8 average_tssi; /* Current TX power level attenuation control values */ struct b43_bbatt bbatt; struct b43_rfatt rfatt; u8 tx_control; /* B43_TXCTL_XXX */ /* The calculated attenuation deltas that are used later * when adjusting the actual power output. */ int bbatt_delta; int rfatt_delta; /* LocalOscillator control values. */ struct b43_txpower_lo_control *lo_control; /* Values from b43_calc_loopback_gain() */ s16 max_lb_gain; /* Maximum Loopback gain in hdB */ s16 trsw_rx_gain; /* TRSW RX gain in hdB */ s16 lna_lod_gain; /* LNA lod */ s16 lna_gain; /* LNA */ s16 pga_gain; /* PGA */ /* Current Interference Mitigation mode */ int interfmode; /* Stack of saved values from the Interference Mitigation code. * Each value in the stack is laid out as follows: * bit 0-11: offset * bit 12-15: register ID * bit 16-32: value * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT */ #define B43_INTERFSTACK_SIZE 26 u32 interfstack[B43_INTERFSTACK_SIZE]; //FIXME: use a data structure /* Saved values from the NRSSI Slope calculation */ s16 nrssi[2]; s32 nrssislope; /* In memory nrssi lookup table. */ s8 nrssi_lt[64]; u16 lofcal; u16 initval; //FIXME rename? /* The device does address auto increment for the OFDM tables. * We cache the previously used address here and omit the address * write on the next table access, if possible. */ u16 ofdmtab_addr; /* The address currently set in hardware. */ enum { /* The last data flow direction. */ B43_OFDMTAB_DIRECTION_UNKNOWN = 0, B43_OFDMTAB_DIRECTION_READ, B43_OFDMTAB_DIRECTION_WRITE, } ofdmtab_addr_direction; }; void b43_gphy_set_baseband_attenuation(struct b43_wldev *dev, u16 baseband_attenuation); void b43_gphy_channel_switch(struct b43_wldev *dev, unsigned int channel, bool synthetic_pu_workaround); u8 * b43_generate_dyn_tssi2dbm_tab(struct b43_wldev *dev, s16 pab0, s16 pab1, s16 pab2); struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_g; #endif /* LINUX_B43_PHY_G_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_g.c0000644000175000017500000024232412026211315022332 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11g PHY driver Copyright (c) 2005 Martin Langer , Copyright (c) 2005-2007 Stefano Brivio Copyright (c) 2005-2008 Michael Buesch Copyright (c) 2005, 2006 Danny van Dyk Copyright (c) 2005, 2006 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "phy_g.h" #include "phy_common.h" #include "lo.h" #include "main.h" #include #include static const s8 b43_tssi2dbm_g_table[] = { 77, 77, 77, 76, 76, 76, 75, 75, 74, 74, 73, 73, 73, 72, 72, 71, 71, 70, 70, 69, 68, 68, 67, 67, 66, 65, 65, 64, 63, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 50, 49, 47, 45, 43, 40, 37, 33, 28, 22, 14, 5, -7, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, }; static const u8 b43_radio_channel_codes_bg[] = { 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 84, }; static void b43_calc_nrssi_threshold(struct b43_wldev *dev); #define bitrev4(tmp) (bitrev8(tmp) >> 4) /* Get the freq, as it has to be written to the device. */ static inline u16 channel2freq_bg(u8 channel) { B43_WARN_ON(!(channel >= 1 && channel <= 14)); return b43_radio_channel_codes_bg[channel - 1]; } static void generate_rfatt_list(struct b43_wldev *dev, struct b43_rfatt_list *list) { struct b43_phy *phy = &dev->phy; /* APHY.rev < 5 || GPHY.rev < 6 */ static const struct b43_rfatt rfatt_0[] = { {.att = 3,.with_padmix = 0,}, {.att = 1,.with_padmix = 0,}, {.att = 5,.with_padmix = 0,}, {.att = 7,.with_padmix = 0,}, {.att = 9,.with_padmix = 0,}, {.att = 2,.with_padmix = 0,}, {.att = 0,.with_padmix = 0,}, {.att = 4,.with_padmix = 0,}, {.att = 6,.with_padmix = 0,}, {.att = 8,.with_padmix = 0,}, {.att = 1,.with_padmix = 1,}, {.att = 2,.with_padmix = 1,}, {.att = 3,.with_padmix = 1,}, {.att = 4,.with_padmix = 1,}, }; /* Radio.rev == 8 && Radio.version == 0x2050 */ static const struct b43_rfatt rfatt_1[] = { {.att = 2,.with_padmix = 1,}, {.att = 4,.with_padmix = 1,}, {.att = 6,.with_padmix = 1,}, {.att = 8,.with_padmix = 1,}, {.att = 10,.with_padmix = 1,}, {.att = 12,.with_padmix = 1,}, {.att = 14,.with_padmix = 1,}, }; /* Otherwise */ static const struct b43_rfatt rfatt_2[] = { {.att = 0,.with_padmix = 1,}, {.att = 2,.with_padmix = 1,}, {.att = 4,.with_padmix = 1,}, {.att = 6,.with_padmix = 1,}, {.att = 8,.with_padmix = 1,}, {.att = 9,.with_padmix = 1,}, {.att = 9,.with_padmix = 1,}, }; if (!b43_has_hardware_pctl(dev)) { /* Software pctl */ list->list = rfatt_0; list->len = ARRAY_SIZE(rfatt_0); list->min_val = 0; list->max_val = 9; return; } if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { /* Hardware pctl */ list->list = rfatt_1; list->len = ARRAY_SIZE(rfatt_1); list->min_val = 0; list->max_val = 14; return; } /* Hardware pctl */ list->list = rfatt_2; list->len = ARRAY_SIZE(rfatt_2); list->min_val = 0; list->max_val = 9; } static void generate_bbatt_list(struct b43_wldev *dev, struct b43_bbatt_list *list) { static const struct b43_bbatt bbatt_0[] = { {.att = 0,}, {.att = 1,}, {.att = 2,}, {.att = 3,}, {.att = 4,}, {.att = 5,}, {.att = 6,}, {.att = 7,}, {.att = 8,}, }; list->list = bbatt_0; list->len = ARRAY_SIZE(bbatt_0); list->min_val = 0; list->max_val = 8; } static void b43_shm_clear_tssi(struct b43_wldev *dev) { b43_shm_write16(dev, B43_SHM_SHARED, 0x0058, 0x7F7F); b43_shm_write16(dev, B43_SHM_SHARED, 0x005a, 0x7F7F); b43_shm_write16(dev, B43_SHM_SHARED, 0x0070, 0x7F7F); b43_shm_write16(dev, B43_SHM_SHARED, 0x0072, 0x7F7F); } /* Synthetic PU workaround */ static void b43_synth_pu_workaround(struct b43_wldev *dev, u8 channel) { struct b43_phy *phy = &dev->phy; might_sleep(); if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) { /* We do not need the workaround. */ return; } if (channel <= 10) { b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel + 4)); } else { b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(1)); } msleep(1); b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel)); } /* Set the baseband attenuation value on chip. */ void b43_gphy_set_baseband_attenuation(struct b43_wldev *dev, u16 baseband_attenuation) { struct b43_phy *phy = &dev->phy; if (phy->analog == 0) { b43_write16(dev, B43_MMIO_PHY0, (b43_read16(dev, B43_MMIO_PHY0) & 0xFFF0) | baseband_attenuation); } else if (phy->analog > 1) { b43_phy_maskset(dev, B43_PHY_DACCTL, 0xFFC3, (baseband_attenuation << 2)); } else { b43_phy_maskset(dev, B43_PHY_DACCTL, 0xFF87, (baseband_attenuation << 3)); } } /* Adjust the transmission power output (G-PHY) */ static void b43_set_txpower_g(struct b43_wldev *dev, const struct b43_bbatt *bbatt, const struct b43_rfatt *rfatt, u8 tx_control) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; u16 bb, rf; u16 tx_bias, tx_magn; bb = bbatt->att; rf = rfatt->att; tx_bias = lo->tx_bias; tx_magn = lo->tx_magn; if (unlikely(tx_bias == 0xFF)) tx_bias = 0; /* Save the values for later. Use memmove, because it's valid * to pass &gphy->rfatt as rfatt pointer argument. Same for bbatt. */ gphy->tx_control = tx_control; memmove(&gphy->rfatt, rfatt, sizeof(*rfatt)); gphy->rfatt.with_padmix = !!(tx_control & B43_TXCTL_TXMIX); memmove(&gphy->bbatt, bbatt, sizeof(*bbatt)); if (b43_debug(dev, B43_DBG_XMITPOWER)) { b43dbg(dev->wl, "Tuning TX-power to bbatt(%u), " "rfatt(%u), tx_control(0x%02X), " "tx_bias(0x%02X), tx_magn(0x%02X)\n", bb, rf, tx_control, tx_bias, tx_magn); } b43_gphy_set_baseband_attenuation(dev, bb); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RFATT, rf); if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { b43_radio_write16(dev, 0x43, (rf & 0x000F) | (tx_control & 0x0070)); } else { b43_radio_maskset(dev, 0x43, 0xFFF0, (rf & 0x000F)); b43_radio_maskset(dev, 0x52, ~0x0070, (tx_control & 0x0070)); } if (has_tx_magnification(phy)) { b43_radio_write16(dev, 0x52, tx_magn | tx_bias); } else { b43_radio_maskset(dev, 0x52, 0xFFF0, (tx_bias & 0x000F)); } b43_lo_g_adjust(dev); } /* GPHY_TSSI_Power_Lookup_Table_Init */ static void b43_gphy_tssi_power_lt_init(struct b43_wldev *dev) { struct b43_phy_g *gphy = dev->phy.g; int i; u16 value; for (i = 0; i < 32; i++) b43_ofdmtab_write16(dev, 0x3C20, i, gphy->tssi2dbm[i]); for (i = 32; i < 64; i++) b43_ofdmtab_write16(dev, 0x3C00, i - 32, gphy->tssi2dbm[i]); for (i = 0; i < 64; i += 2) { value = (u16) gphy->tssi2dbm[i]; value |= ((u16) gphy->tssi2dbm[i + 1]) << 8; b43_phy_write(dev, 0x380 + (i / 2), value); } } /* GPHY_Gain_Lookup_Table_Init */ static void b43_gphy_gain_lt_init(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; u16 nr_written = 0; u16 tmp; u8 rf, bb; for (rf = 0; rf < lo->rfatt_list.len; rf++) { for (bb = 0; bb < lo->bbatt_list.len; bb++) { if (nr_written >= 0x40) return; tmp = lo->bbatt_list.list[bb].att; tmp <<= 8; if (phy->radio_rev == 8) tmp |= 0x50; else tmp |= 0x40; tmp |= lo->rfatt_list.list[rf].att; b43_phy_write(dev, 0x3C0 + nr_written, tmp); nr_written++; } } } static void b43_set_all_gains(struct b43_wldev *dev, s16 first, s16 second, s16 third) { struct b43_phy *phy = &dev->phy; u16 i; u16 start = 0x08, end = 0x18; u16 tmp; u16 table; if (phy->rev <= 1) { start = 0x10; end = 0x20; } table = B43_OFDMTAB_GAINX; if (phy->rev <= 1) table = B43_OFDMTAB_GAINX_R1; for (i = 0; i < 4; i++) b43_ofdmtab_write16(dev, table, i, first); for (i = start; i < end; i++) b43_ofdmtab_write16(dev, table, i, second); if (third != -1) { tmp = ((u16) third << 14) | ((u16) third << 6); b43_phy_maskset(dev, 0x04A0, 0xBFBF, tmp); b43_phy_maskset(dev, 0x04A1, 0xBFBF, tmp); b43_phy_maskset(dev, 0x04A2, 0xBFBF, tmp); } b43_dummy_transmission(dev, false, true); } static void b43_set_original_gains(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u16 i, tmp; u16 table; u16 start = 0x0008, end = 0x0018; if (phy->rev <= 1) { start = 0x0010; end = 0x0020; } table = B43_OFDMTAB_GAINX; if (phy->rev <= 1) table = B43_OFDMTAB_GAINX_R1; for (i = 0; i < 4; i++) { tmp = (i & 0xFFFC); tmp |= (i & 0x0001) << 1; tmp |= (i & 0x0002) >> 1; b43_ofdmtab_write16(dev, table, i, tmp); } for (i = start; i < end; i++) b43_ofdmtab_write16(dev, table, i, i - start); b43_phy_maskset(dev, 0x04A0, 0xBFBF, 0x4040); b43_phy_maskset(dev, 0x04A1, 0xBFBF, 0x4040); b43_phy_maskset(dev, 0x04A2, 0xBFBF, 0x4000); b43_dummy_transmission(dev, false, true); } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ static void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val) { b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset); b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val); } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ static s16 b43_nrssi_hw_read(struct b43_wldev *dev, u16 offset) { u16 val; b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset); val = b43_phy_read(dev, B43_PHY_NRSSILT_DATA); return (s16) val; } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ static void b43_nrssi_hw_update(struct b43_wldev *dev, u16 val) { u16 i; s16 tmp; for (i = 0; i < 64; i++) { tmp = b43_nrssi_hw_read(dev, i); tmp -= val; tmp = clamp_val(tmp, -32, 31); b43_nrssi_hw_write(dev, i, tmp); } } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ static void b43_nrssi_mem_update(struct b43_wldev *dev) { struct b43_phy_g *gphy = dev->phy.g; s16 i, delta; s32 tmp; delta = 0x1F - gphy->nrssi[0]; for (i = 0; i < 64; i++) { tmp = (i - delta) * gphy->nrssislope; tmp /= 0x10000; tmp += 0x3A; tmp = clamp_val(tmp, 0, 0x3F); gphy->nrssi_lt[i] = tmp; } } static void b43_calc_nrssi_offset(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u16 backup[20] = { 0 }; s16 v47F; u16 i; u16 saved = 0xFFFF; backup[0] = b43_phy_read(dev, 0x0001); backup[1] = b43_phy_read(dev, 0x0811); backup[2] = b43_phy_read(dev, 0x0812); if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ backup[3] = b43_phy_read(dev, 0x0814); backup[4] = b43_phy_read(dev, 0x0815); } backup[5] = b43_phy_read(dev, 0x005A); backup[6] = b43_phy_read(dev, 0x0059); backup[7] = b43_phy_read(dev, 0x0058); backup[8] = b43_phy_read(dev, 0x000A); backup[9] = b43_phy_read(dev, 0x0003); backup[10] = b43_radio_read16(dev, 0x007A); backup[11] = b43_radio_read16(dev, 0x0043); b43_phy_mask(dev, 0x0429, 0x7FFF); b43_phy_maskset(dev, 0x0001, 0x3FFF, 0x4000); b43_phy_set(dev, 0x0811, 0x000C); b43_phy_maskset(dev, 0x0812, 0xFFF3, 0x0004); b43_phy_mask(dev, 0x0802, ~(0x1 | 0x2)); if (phy->rev >= 6) { backup[12] = b43_phy_read(dev, 0x002E); backup[13] = b43_phy_read(dev, 0x002F); backup[14] = b43_phy_read(dev, 0x080F); backup[15] = b43_phy_read(dev, 0x0810); backup[16] = b43_phy_read(dev, 0x0801); backup[17] = b43_phy_read(dev, 0x0060); backup[18] = b43_phy_read(dev, 0x0014); backup[19] = b43_phy_read(dev, 0x0478); b43_phy_write(dev, 0x002E, 0); b43_phy_write(dev, 0x002F, 0); b43_phy_write(dev, 0x080F, 0); b43_phy_write(dev, 0x0810, 0); b43_phy_set(dev, 0x0478, 0x0100); b43_phy_set(dev, 0x0801, 0x0040); b43_phy_set(dev, 0x0060, 0x0040); b43_phy_set(dev, 0x0014, 0x0200); } b43_radio_set(dev, 0x007A, 0x0070); b43_radio_set(dev, 0x007A, 0x0080); udelay(30); v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == 31) { for (i = 7; i >= 4; i--) { b43_radio_write16(dev, 0x007B, i); udelay(20); v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F < 31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 4; } else { b43_radio_mask(dev, 0x007A, 0x007F); if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ b43_phy_set(dev, 0x0814, 0x0001); b43_phy_mask(dev, 0x0815, 0xFFFE); } b43_phy_set(dev, 0x0811, 0x000C); b43_phy_set(dev, 0x0812, 0x000C); b43_phy_set(dev, 0x0811, 0x0030); b43_phy_set(dev, 0x0812, 0x0030); b43_phy_write(dev, 0x005A, 0x0480); b43_phy_write(dev, 0x0059, 0x0810); b43_phy_write(dev, 0x0058, 0x000D); if (phy->rev == 0) { b43_phy_write(dev, 0x0003, 0x0122); } else { b43_phy_set(dev, 0x000A, 0x2000); } if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ b43_phy_set(dev, 0x0814, 0x0004); b43_phy_mask(dev, 0x0815, 0xFFFB); } b43_phy_maskset(dev, 0x0003, 0xFF9F, 0x0040); b43_radio_set(dev, 0x007A, 0x000F); b43_set_all_gains(dev, 3, 0, 1); b43_radio_maskset(dev, 0x0043, 0x00F0, 0x000F); udelay(30); v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == -32) { for (i = 0; i < 4; i++) { b43_radio_write16(dev, 0x007B, i); udelay(20); v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F > -31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 3; } else saved = 0; } b43_radio_write16(dev, 0x007B, saved); if (phy->rev >= 6) { b43_phy_write(dev, 0x002E, backup[12]); b43_phy_write(dev, 0x002F, backup[13]); b43_phy_write(dev, 0x080F, backup[14]); b43_phy_write(dev, 0x0810, backup[15]); } if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ b43_phy_write(dev, 0x0814, backup[3]); b43_phy_write(dev, 0x0815, backup[4]); } b43_phy_write(dev, 0x005A, backup[5]); b43_phy_write(dev, 0x0059, backup[6]); b43_phy_write(dev, 0x0058, backup[7]); b43_phy_write(dev, 0x000A, backup[8]); b43_phy_write(dev, 0x0003, backup[9]); b43_radio_write16(dev, 0x0043, backup[11]); b43_radio_write16(dev, 0x007A, backup[10]); b43_phy_write(dev, 0x0802, b43_phy_read(dev, 0x0802) | 0x1 | 0x2); b43_phy_set(dev, 0x0429, 0x8000); b43_set_original_gains(dev); if (phy->rev >= 6) { b43_phy_write(dev, 0x0801, backup[16]); b43_phy_write(dev, 0x0060, backup[17]); b43_phy_write(dev, 0x0014, backup[18]); b43_phy_write(dev, 0x0478, backup[19]); } b43_phy_write(dev, 0x0001, backup[0]); b43_phy_write(dev, 0x0812, backup[2]); b43_phy_write(dev, 0x0811, backup[1]); } static void b43_calc_nrssi_slope(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 backup[18] = { 0 }; u16 tmp; s16 nrssi0, nrssi1; B43_WARN_ON(phy->type != B43_PHYTYPE_G); if (phy->radio_rev >= 9) return; if (phy->radio_rev == 8) b43_calc_nrssi_offset(dev); b43_phy_mask(dev, B43_PHY_G_CRS, 0x7FFF); b43_phy_mask(dev, 0x0802, 0xFFFC); backup[7] = b43_read16(dev, 0x03E2); b43_write16(dev, 0x03E2, b43_read16(dev, 0x03E2) | 0x8000); backup[0] = b43_radio_read16(dev, 0x007A); backup[1] = b43_radio_read16(dev, 0x0052); backup[2] = b43_radio_read16(dev, 0x0043); backup[3] = b43_phy_read(dev, 0x0015); backup[4] = b43_phy_read(dev, 0x005A); backup[5] = b43_phy_read(dev, 0x0059); backup[6] = b43_phy_read(dev, 0x0058); backup[8] = b43_read16(dev, 0x03E6); backup[9] = b43_read16(dev, B43_MMIO_CHANNEL_EXT); if (phy->rev >= 3) { backup[10] = b43_phy_read(dev, 0x002E); backup[11] = b43_phy_read(dev, 0x002F); backup[12] = b43_phy_read(dev, 0x080F); backup[13] = b43_phy_read(dev, B43_PHY_G_LO_CONTROL); backup[14] = b43_phy_read(dev, 0x0801); backup[15] = b43_phy_read(dev, 0x0060); backup[16] = b43_phy_read(dev, 0x0014); backup[17] = b43_phy_read(dev, 0x0478); b43_phy_write(dev, 0x002E, 0); b43_phy_write(dev, B43_PHY_G_LO_CONTROL, 0); switch (phy->rev) { case 4: case 6: case 7: b43_phy_set(dev, 0x0478, 0x0100); b43_phy_set(dev, 0x0801, 0x0040); break; case 3: case 5: b43_phy_mask(dev, 0x0801, 0xFFBF); break; } b43_phy_set(dev, 0x0060, 0x0040); b43_phy_set(dev, 0x0014, 0x0200); } b43_radio_set(dev, 0x007A, 0x0070); b43_set_all_gains(dev, 0, 8, 0); b43_radio_mask(dev, 0x007A, 0x00F7); if (phy->rev >= 2) { b43_phy_maskset(dev, 0x0811, 0xFFCF, 0x0030); b43_phy_maskset(dev, 0x0812, 0xFFCF, 0x0010); } b43_radio_set(dev, 0x007A, 0x0080); udelay(20); nrssi0 = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); if (nrssi0 >= 0x0020) nrssi0 -= 0x0040; b43_radio_mask(dev, 0x007A, 0x007F); if (phy->rev >= 2) { b43_phy_maskset(dev, 0x0003, 0xFF9F, 0x0040); } b43_write16(dev, B43_MMIO_CHANNEL_EXT, b43_read16(dev, B43_MMIO_CHANNEL_EXT) | 0x2000); b43_radio_set(dev, 0x007A, 0x000F); b43_phy_write(dev, 0x0015, 0xF330); if (phy->rev >= 2) { b43_phy_maskset(dev, 0x0812, 0xFFCF, 0x0020); b43_phy_maskset(dev, 0x0811, 0xFFCF, 0x0020); } b43_set_all_gains(dev, 3, 0, 1); if (phy->radio_rev == 8) { b43_radio_write16(dev, 0x0043, 0x001F); } else { tmp = b43_radio_read16(dev, 0x0052) & 0xFF0F; b43_radio_write16(dev, 0x0052, tmp | 0x0060); tmp = b43_radio_read16(dev, 0x0043) & 0xFFF0; b43_radio_write16(dev, 0x0043, tmp | 0x0009); } b43_phy_write(dev, 0x005A, 0x0480); b43_phy_write(dev, 0x0059, 0x0810); b43_phy_write(dev, 0x0058, 0x000D); udelay(20); nrssi1 = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); if (nrssi1 >= 0x0020) nrssi1 -= 0x0040; if (nrssi0 == nrssi1) gphy->nrssislope = 0x00010000; else gphy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); if (nrssi0 >= -4) { gphy->nrssi[0] = nrssi1; gphy->nrssi[1] = nrssi0; } if (phy->rev >= 3) { b43_phy_write(dev, 0x002E, backup[10]); b43_phy_write(dev, 0x002F, backup[11]); b43_phy_write(dev, 0x080F, backup[12]); b43_phy_write(dev, B43_PHY_G_LO_CONTROL, backup[13]); } if (phy->rev >= 2) { b43_phy_mask(dev, 0x0812, 0xFFCF); b43_phy_mask(dev, 0x0811, 0xFFCF); } b43_radio_write16(dev, 0x007A, backup[0]); b43_radio_write16(dev, 0x0052, backup[1]); b43_radio_write16(dev, 0x0043, backup[2]); b43_write16(dev, 0x03E2, backup[7]); b43_write16(dev, 0x03E6, backup[8]); b43_write16(dev, B43_MMIO_CHANNEL_EXT, backup[9]); b43_phy_write(dev, 0x0015, backup[3]); b43_phy_write(dev, 0x005A, backup[4]); b43_phy_write(dev, 0x0059, backup[5]); b43_phy_write(dev, 0x0058, backup[6]); b43_synth_pu_workaround(dev, phy->channel); b43_phy_set(dev, 0x0802, (0x0001 | 0x0002)); b43_set_original_gains(dev); b43_phy_set(dev, B43_PHY_G_CRS, 0x8000); if (phy->rev >= 3) { b43_phy_write(dev, 0x0801, backup[14]); b43_phy_write(dev, 0x0060, backup[15]); b43_phy_write(dev, 0x0014, backup[16]); b43_phy_write(dev, 0x0478, backup[17]); } b43_nrssi_mem_update(dev); b43_calc_nrssi_threshold(dev); } static void b43_calc_nrssi_threshold(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; s32 a, b; s16 tmp16; u16 tmp_u16; B43_WARN_ON(phy->type != B43_PHYTYPE_G); if (!phy->gmode || !(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) { tmp16 = b43_nrssi_hw_read(dev, 0x20); if (tmp16 >= 0x20) tmp16 -= 0x40; if (tmp16 < 3) { b43_phy_maskset(dev, 0x048A, 0xF000, 0x09EB); } else { b43_phy_maskset(dev, 0x048A, 0xF000, 0x0AED); } } else { if (gphy->interfmode == B43_INTERFMODE_NONWLAN) { a = 0xE; b = 0xA; } else if (!gphy->aci_wlan_automatic && gphy->aci_enable) { a = 0x13; b = 0x12; } else { a = 0xE; b = 0x11; } a = a * (gphy->nrssi[1] - gphy->nrssi[0]); a += (gphy->nrssi[0] << 6); if (a < 32) a += 31; else a += 32; a = a >> 6; a = clamp_val(a, -31, 31); b = b * (gphy->nrssi[1] - gphy->nrssi[0]); b += (gphy->nrssi[0] << 6); if (b < 32) b += 31; else b += 32; b = b >> 6; b = clamp_val(b, -31, 31); tmp_u16 = b43_phy_read(dev, 0x048A) & 0xF000; tmp_u16 |= ((u32) b & 0x0000003F); tmp_u16 |= (((u32) a & 0x0000003F) << 6); b43_phy_write(dev, 0x048A, tmp_u16); } } /* Stack implementation to save/restore values from the * interference mitigation code. * It is save to restore values in random order. */ static void _stack_save(u32 *_stackptr, size_t *stackidx, u8 id, u16 offset, u16 value) { u32 *stackptr = &(_stackptr[*stackidx]); B43_WARN_ON(offset & 0xF000); B43_WARN_ON(id & 0xF0); *stackptr = offset; *stackptr |= ((u32) id) << 12; *stackptr |= ((u32) value) << 16; (*stackidx)++; B43_WARN_ON(*stackidx >= B43_INTERFSTACK_SIZE); } static u16 _stack_restore(u32 *stackptr, u8 id, u16 offset) { size_t i; B43_WARN_ON(offset & 0xF000); B43_WARN_ON(id & 0xF0); for (i = 0; i < B43_INTERFSTACK_SIZE; i++, stackptr++) { if ((*stackptr & 0x00000FFF) != offset) continue; if (((*stackptr & 0x0000F000) >> 12) != id) continue; return ((*stackptr & 0xFFFF0000) >> 16); } B43_WARN_ON(1); return 0; } #define phy_stacksave(offset) \ do { \ _stack_save(stack, &stackidx, 0x1, (offset), \ b43_phy_read(dev, (offset))); \ } while (0) #define phy_stackrestore(offset) \ do { \ b43_phy_write(dev, (offset), \ _stack_restore(stack, 0x1, \ (offset))); \ } while (0) #define radio_stacksave(offset) \ do { \ _stack_save(stack, &stackidx, 0x2, (offset), \ b43_radio_read16(dev, (offset))); \ } while (0) #define radio_stackrestore(offset) \ do { \ b43_radio_write16(dev, (offset), \ _stack_restore(stack, 0x2, \ (offset))); \ } while (0) #define ofdmtab_stacksave(table, offset) \ do { \ _stack_save(stack, &stackidx, 0x3, (offset)|(table), \ b43_ofdmtab_read16(dev, (table), (offset))); \ } while (0) #define ofdmtab_stackrestore(table, offset) \ do { \ b43_ofdmtab_write16(dev, (table), (offset), \ _stack_restore(stack, 0x3, \ (offset)|(table))); \ } while (0) static void b43_radio_interference_mitigation_enable(struct b43_wldev *dev, int mode) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 tmp, flipped; size_t stackidx = 0; u32 *stack = gphy->interfstack; switch (mode) { case B43_INTERFMODE_NONWLAN: if (phy->rev != 1) { b43_phy_set(dev, 0x042B, 0x0800); b43_phy_mask(dev, B43_PHY_G_CRS, ~0x4000); break; } radio_stacksave(0x0078); tmp = (b43_radio_read16(dev, 0x0078) & 0x001E); B43_WARN_ON(tmp > 15); flipped = bitrev4(tmp); if (flipped < 10 && flipped >= 8) flipped = 7; else if (flipped >= 10) flipped -= 3; flipped = (bitrev4(flipped) << 1) | 0x0020; b43_radio_write16(dev, 0x0078, flipped); b43_calc_nrssi_threshold(dev); phy_stacksave(0x0406); b43_phy_write(dev, 0x0406, 0x7E28); b43_phy_set(dev, 0x042B, 0x0800); b43_phy_set(dev, B43_PHY_RADIO_BITFIELD, 0x1000); phy_stacksave(0x04A0); b43_phy_maskset(dev, 0x04A0, 0xC0C0, 0x0008); phy_stacksave(0x04A1); b43_phy_maskset(dev, 0x04A1, 0xC0C0, 0x0605); phy_stacksave(0x04A2); b43_phy_maskset(dev, 0x04A2, 0xC0C0, 0x0204); phy_stacksave(0x04A8); b43_phy_maskset(dev, 0x04A8, 0xC0C0, 0x0803); phy_stacksave(0x04AB); b43_phy_maskset(dev, 0x04AB, 0xC0C0, 0x0605); phy_stacksave(0x04A7); b43_phy_write(dev, 0x04A7, 0x0002); phy_stacksave(0x04A3); b43_phy_write(dev, 0x04A3, 0x287A); phy_stacksave(0x04A9); b43_phy_write(dev, 0x04A9, 0x2027); phy_stacksave(0x0493); b43_phy_write(dev, 0x0493, 0x32F5); phy_stacksave(0x04AA); b43_phy_write(dev, 0x04AA, 0x2027); phy_stacksave(0x04AC); b43_phy_write(dev, 0x04AC, 0x32F5); break; case B43_INTERFMODE_MANUALWLAN: if (b43_phy_read(dev, 0x0033) & 0x0800) break; gphy->aci_enable = true; phy_stacksave(B43_PHY_RADIO_BITFIELD); phy_stacksave(B43_PHY_G_CRS); if (phy->rev < 2) { phy_stacksave(0x0406); } else { phy_stacksave(0x04C0); phy_stacksave(0x04C1); } phy_stacksave(0x0033); phy_stacksave(0x04A7); phy_stacksave(0x04A3); phy_stacksave(0x04A9); phy_stacksave(0x04AA); phy_stacksave(0x04AC); phy_stacksave(0x0493); phy_stacksave(0x04A1); phy_stacksave(0x04A0); phy_stacksave(0x04A2); phy_stacksave(0x048A); phy_stacksave(0x04A8); phy_stacksave(0x04AB); if (phy->rev == 2) { phy_stacksave(0x04AD); phy_stacksave(0x04AE); } else if (phy->rev >= 3) { phy_stacksave(0x04AD); phy_stacksave(0x0415); phy_stacksave(0x0416); phy_stacksave(0x0417); ofdmtab_stacksave(0x1A00, 0x2); ofdmtab_stacksave(0x1A00, 0x3); } phy_stacksave(0x042B); phy_stacksave(0x048C); b43_phy_mask(dev, B43_PHY_RADIO_BITFIELD, ~0x1000); b43_phy_maskset(dev, B43_PHY_G_CRS, 0xFFFC, 0x0002); b43_phy_write(dev, 0x0033, 0x0800); b43_phy_write(dev, 0x04A3, 0x2027); b43_phy_write(dev, 0x04A9, 0x1CA8); b43_phy_write(dev, 0x0493, 0x287A); b43_phy_write(dev, 0x04AA, 0x1CA8); b43_phy_write(dev, 0x04AC, 0x287A); b43_phy_maskset(dev, 0x04A0, 0xFFC0, 0x001A); b43_phy_write(dev, 0x04A7, 0x000D); if (phy->rev < 2) { b43_phy_write(dev, 0x0406, 0xFF0D); } else if (phy->rev == 2) { b43_phy_write(dev, 0x04C0, 0xFFFF); b43_phy_write(dev, 0x04C1, 0x00A9); } else { b43_phy_write(dev, 0x04C0, 0x00C1); b43_phy_write(dev, 0x04C1, 0x0059); } b43_phy_maskset(dev, 0x04A1, 0xC0FF, 0x1800); b43_phy_maskset(dev, 0x04A1, 0xFFC0, 0x0015); b43_phy_maskset(dev, 0x04A8, 0xCFFF, 0x1000); b43_phy_maskset(dev, 0x04A8, 0xF0FF, 0x0A00); b43_phy_maskset(dev, 0x04AB, 0xCFFF, 0x1000); b43_phy_maskset(dev, 0x04AB, 0xF0FF, 0x0800); b43_phy_maskset(dev, 0x04AB, 0xFFCF, 0x0010); b43_phy_maskset(dev, 0x04AB, 0xFFF0, 0x0005); b43_phy_maskset(dev, 0x04A8, 0xFFCF, 0x0010); b43_phy_maskset(dev, 0x04A8, 0xFFF0, 0x0006); b43_phy_maskset(dev, 0x04A2, 0xF0FF, 0x0800); b43_phy_maskset(dev, 0x04A0, 0xF0FF, 0x0500); b43_phy_maskset(dev, 0x04A2, 0xFFF0, 0x000B); if (phy->rev >= 3) { b43_phy_mask(dev, 0x048A, 0x7FFF); b43_phy_maskset(dev, 0x0415, 0x8000, 0x36D8); b43_phy_maskset(dev, 0x0416, 0x8000, 0x36D8); b43_phy_maskset(dev, 0x0417, 0xFE00, 0x016D); } else { b43_phy_set(dev, 0x048A, 0x1000); b43_phy_maskset(dev, 0x048A, 0x9FFF, 0x2000); b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ACIW); } if (phy->rev >= 2) { b43_phy_set(dev, 0x042B, 0x0800); } b43_phy_maskset(dev, 0x048C, 0xF0FF, 0x0200); if (phy->rev == 2) { b43_phy_maskset(dev, 0x04AE, 0xFF00, 0x007F); b43_phy_maskset(dev, 0x04AD, 0x00FF, 0x1300); } else if (phy->rev >= 6) { b43_ofdmtab_write16(dev, 0x1A00, 0x3, 0x007F); b43_ofdmtab_write16(dev, 0x1A00, 0x2, 0x007F); b43_phy_mask(dev, 0x04AD, 0x00FF); } b43_calc_nrssi_slope(dev); break; default: B43_WARN_ON(1); } } static void b43_radio_interference_mitigation_disable(struct b43_wldev *dev, int mode) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u32 *stack = gphy->interfstack; switch (mode) { case B43_INTERFMODE_NONWLAN: if (phy->rev != 1) { b43_phy_mask(dev, 0x042B, ~0x0800); b43_phy_set(dev, B43_PHY_G_CRS, 0x4000); break; } radio_stackrestore(0x0078); b43_calc_nrssi_threshold(dev); phy_stackrestore(0x0406); b43_phy_mask(dev, 0x042B, ~0x0800); if (!dev->bad_frames_preempt) { b43_phy_mask(dev, B43_PHY_RADIO_BITFIELD, ~(1 << 11)); } b43_phy_set(dev, B43_PHY_G_CRS, 0x4000); phy_stackrestore(0x04A0); phy_stackrestore(0x04A1); phy_stackrestore(0x04A2); phy_stackrestore(0x04A8); phy_stackrestore(0x04AB); phy_stackrestore(0x04A7); phy_stackrestore(0x04A3); phy_stackrestore(0x04A9); phy_stackrestore(0x0493); phy_stackrestore(0x04AA); phy_stackrestore(0x04AC); break; case B43_INTERFMODE_MANUALWLAN: if (!(b43_phy_read(dev, 0x0033) & 0x0800)) break; gphy->aci_enable = false; phy_stackrestore(B43_PHY_RADIO_BITFIELD); phy_stackrestore(B43_PHY_G_CRS); phy_stackrestore(0x0033); phy_stackrestore(0x04A3); phy_stackrestore(0x04A9); phy_stackrestore(0x0493); phy_stackrestore(0x04AA); phy_stackrestore(0x04AC); phy_stackrestore(0x04A0); phy_stackrestore(0x04A7); if (phy->rev >= 2) { phy_stackrestore(0x04C0); phy_stackrestore(0x04C1); } else phy_stackrestore(0x0406); phy_stackrestore(0x04A1); phy_stackrestore(0x04AB); phy_stackrestore(0x04A8); if (phy->rev == 2) { phy_stackrestore(0x04AD); phy_stackrestore(0x04AE); } else if (phy->rev >= 3) { phy_stackrestore(0x04AD); phy_stackrestore(0x0415); phy_stackrestore(0x0416); phy_stackrestore(0x0417); ofdmtab_stackrestore(0x1A00, 0x2); ofdmtab_stackrestore(0x1A00, 0x3); } phy_stackrestore(0x04A2); phy_stackrestore(0x048A); phy_stackrestore(0x042B); phy_stackrestore(0x048C); b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ACIW); b43_calc_nrssi_slope(dev); break; default: B43_WARN_ON(1); } } #undef phy_stacksave #undef phy_stackrestore #undef radio_stacksave #undef radio_stackrestore #undef ofdmtab_stacksave #undef ofdmtab_stackrestore static u16 b43_radio_core_calibration_value(struct b43_wldev *dev) { u16 reg, index, ret; static const u8 rcc_table[] = { 0x02, 0x03, 0x01, 0x0F, 0x06, 0x07, 0x05, 0x0F, 0x0A, 0x0B, 0x09, 0x0F, 0x0E, 0x0F, 0x0D, 0x0F, }; reg = b43_radio_read16(dev, 0x60); index = (reg & 0x001E) >> 1; ret = rcc_table[index] << 1; ret |= (reg & 0x0001); ret |= 0x0020; return ret; } #define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) static u16 radio2050_rfover_val(struct b43_wldev *dev, u16 phy_register, unsigned int lpd) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct ssb_sprom *sprom = dev->dev->bus_sprom; if (!phy->gmode) return 0; if (has_loopback_gain(phy)) { int max_lb_gain = gphy->max_lb_gain; u16 extlna; u16 i; if (phy->radio_rev == 8) max_lb_gain += 0x3E; else max_lb_gain += 0x26; if (max_lb_gain >= 0x46) { extlna = 0x3000; max_lb_gain -= 0x46; } else if (max_lb_gain >= 0x3A) { extlna = 0x1000; max_lb_gain -= 0x3A; } else if (max_lb_gain >= 0x2E) { extlna = 0x2000; max_lb_gain -= 0x2E; } else { extlna = 0; max_lb_gain -= 0x10; } for (i = 0; i < 16; i++) { max_lb_gain -= (i * 6); if (max_lb_gain < 6) break; } if ((phy->rev < 7) || !(sprom->boardflags_lo & B43_BFL_EXTLNA)) { if (phy_register == B43_PHY_RFOVER) { return 0x1B3; } else if (phy_register == B43_PHY_RFOVERVAL) { extlna |= (i << 8); switch (lpd) { case LPD(0, 1, 1): return 0x0F92; case LPD(0, 0, 1): case LPD(1, 0, 1): return (0x0092 | extlna); case LPD(1, 0, 0): return (0x0093 | extlna); } B43_WARN_ON(1); } B43_WARN_ON(1); } else { if (phy_register == B43_PHY_RFOVER) { return 0x9B3; } else if (phy_register == B43_PHY_RFOVERVAL) { if (extlna) extlna |= 0x8000; extlna |= (i << 8); switch (lpd) { case LPD(0, 1, 1): return 0x8F92; case LPD(0, 0, 1): return (0x8092 | extlna); case LPD(1, 0, 1): return (0x2092 | extlna); case LPD(1, 0, 0): return (0x2093 | extlna); } B43_WARN_ON(1); } B43_WARN_ON(1); } } else { if ((phy->rev < 7) || !(sprom->boardflags_lo & B43_BFL_EXTLNA)) { if (phy_register == B43_PHY_RFOVER) { return 0x1B3; } else if (phy_register == B43_PHY_RFOVERVAL) { switch (lpd) { case LPD(0, 1, 1): return 0x0FB2; case LPD(0, 0, 1): return 0x00B2; case LPD(1, 0, 1): return 0x30B2; case LPD(1, 0, 0): return 0x30B3; } B43_WARN_ON(1); } B43_WARN_ON(1); } else { if (phy_register == B43_PHY_RFOVER) { return 0x9B3; } else if (phy_register == B43_PHY_RFOVERVAL) { switch (lpd) { case LPD(0, 1, 1): return 0x8FB2; case LPD(0, 0, 1): return 0x80B2; case LPD(1, 0, 1): return 0x20B2; case LPD(1, 0, 0): return 0x20B3; } B43_WARN_ON(1); } B43_WARN_ON(1); } } return 0; } struct init2050_saved_values { /* Core registers */ u16 reg_3EC; u16 reg_3E6; u16 reg_3F4; /* Radio registers */ u16 radio_43; u16 radio_51; u16 radio_52; /* PHY registers */ u16 phy_pgactl; u16 phy_cck_5A; u16 phy_cck_59; u16 phy_cck_58; u16 phy_cck_30; u16 phy_rfover; u16 phy_rfoverval; u16 phy_analogover; u16 phy_analogoverval; u16 phy_crs0; u16 phy_classctl; u16 phy_lo_mask; u16 phy_lo_ctl; u16 phy_syncctl; }; static u16 b43_radio_init2050(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct init2050_saved_values sav; u16 rcc; u16 radio78; u16 ret; u16 i, j; u32 tmp1 = 0, tmp2 = 0; memset(&sav, 0, sizeof(sav)); /* get rid of "may be used uninitialized..." */ sav.radio_43 = b43_radio_read16(dev, 0x43); sav.radio_51 = b43_radio_read16(dev, 0x51); sav.radio_52 = b43_radio_read16(dev, 0x52); sav.phy_pgactl = b43_phy_read(dev, B43_PHY_PGACTL); sav.phy_cck_5A = b43_phy_read(dev, B43_PHY_CCK(0x5A)); sav.phy_cck_59 = b43_phy_read(dev, B43_PHY_CCK(0x59)); sav.phy_cck_58 = b43_phy_read(dev, B43_PHY_CCK(0x58)); if (phy->type == B43_PHYTYPE_B) { sav.phy_cck_30 = b43_phy_read(dev, B43_PHY_CCK(0x30)); sav.reg_3EC = b43_read16(dev, 0x3EC); b43_phy_write(dev, B43_PHY_CCK(0x30), 0xFF); b43_write16(dev, 0x3EC, 0x3F3F); } else if (phy->gmode || phy->rev >= 2) { sav.phy_rfover = b43_phy_read(dev, B43_PHY_RFOVER); sav.phy_rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); sav.phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER); sav.phy_analogoverval = b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); sav.phy_crs0 = b43_phy_read(dev, B43_PHY_CRS0); sav.phy_classctl = b43_phy_read(dev, B43_PHY_CLASSCTL); b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0003); b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFC); b43_phy_mask(dev, B43_PHY_CRS0, 0x7FFF); b43_phy_mask(dev, B43_PHY_CLASSCTL, 0xFFFC); if (has_loopback_gain(phy)) { sav.phy_lo_mask = b43_phy_read(dev, B43_PHY_LO_MASK); sav.phy_lo_ctl = b43_phy_read(dev, B43_PHY_LO_CTL); if (phy->rev >= 3) b43_phy_write(dev, B43_PHY_LO_MASK, 0xC020); else b43_phy_write(dev, B43_PHY_LO_MASK, 0x8020); b43_phy_write(dev, B43_PHY_LO_CTL, 0); } b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(0, 1, 1))); b43_phy_write(dev, B43_PHY_RFOVER, radio2050_rfover_val(dev, B43_PHY_RFOVER, 0)); } b43_write16(dev, 0x3E2, b43_read16(dev, 0x3E2) | 0x8000); sav.phy_syncctl = b43_phy_read(dev, B43_PHY_SYNCCTL); b43_phy_mask(dev, B43_PHY_SYNCCTL, 0xFF7F); sav.reg_3E6 = b43_read16(dev, 0x3E6); sav.reg_3F4 = b43_read16(dev, 0x3F4); if (phy->analog == 0) { b43_write16(dev, 0x03E6, 0x0122); } else { if (phy->analog >= 2) { b43_phy_maskset(dev, B43_PHY_CCK(0x03), 0xFFBF, 0x40); } b43_write16(dev, B43_MMIO_CHANNEL_EXT, (b43_read16(dev, B43_MMIO_CHANNEL_EXT) | 0x2000)); } rcc = b43_radio_core_calibration_value(dev); if (phy->type == B43_PHYTYPE_B) b43_radio_write16(dev, 0x78, 0x26); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(0, 1, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xBFAF); b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x1403); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(0, 0, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xBFA0); b43_radio_set(dev, 0x51, 0x0004); if (phy->radio_rev == 8) { b43_radio_write16(dev, 0x43, 0x1F); } else { b43_radio_write16(dev, 0x52, 0); b43_radio_maskset(dev, 0x43, 0xFFF0, 0x0009); } b43_phy_write(dev, B43_PHY_CCK(0x58), 0); for (i = 0; i < 16; i++) { b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0480); b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); udelay(10); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xEFB0); udelay(10); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 0))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xFFF0); udelay(20); tmp1 += b43_phy_read(dev, B43_PHY_LO_LEAKAGE); b43_phy_write(dev, B43_PHY_CCK(0x58), 0); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); } udelay(10); b43_phy_write(dev, B43_PHY_CCK(0x58), 0); tmp1++; tmp1 >>= 9; for (i = 0; i < 16; i++) { radio78 = (bitrev4(i) << 1) | 0x0020; b43_radio_write16(dev, 0x78, radio78); udelay(10); for (j = 0; j < 16; j++) { b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0D80); b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); udelay(10); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xEFB0); udelay(10); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 0))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xFFF0); udelay(10); tmp2 += b43_phy_read(dev, B43_PHY_LO_LEAKAGE); b43_phy_write(dev, B43_PHY_CCK(0x58), 0); if (phy->gmode || phy->rev >= 2) { b43_phy_write(dev, B43_PHY_RFOVERVAL, radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, LPD(1, 0, 1))); } b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); } tmp2++; tmp2 >>= 8; if (tmp1 < tmp2) break; } /* Restore the registers */ b43_phy_write(dev, B43_PHY_PGACTL, sav.phy_pgactl); b43_radio_write16(dev, 0x51, sav.radio_51); b43_radio_write16(dev, 0x52, sav.radio_52); b43_radio_write16(dev, 0x43, sav.radio_43); b43_phy_write(dev, B43_PHY_CCK(0x5A), sav.phy_cck_5A); b43_phy_write(dev, B43_PHY_CCK(0x59), sav.phy_cck_59); b43_phy_write(dev, B43_PHY_CCK(0x58), sav.phy_cck_58); b43_write16(dev, 0x3E6, sav.reg_3E6); if (phy->analog != 0) b43_write16(dev, 0x3F4, sav.reg_3F4); b43_phy_write(dev, B43_PHY_SYNCCTL, sav.phy_syncctl); b43_synth_pu_workaround(dev, phy->channel); if (phy->type == B43_PHYTYPE_B) { b43_phy_write(dev, B43_PHY_CCK(0x30), sav.phy_cck_30); b43_write16(dev, 0x3EC, sav.reg_3EC); } else if (phy->gmode) { b43_write16(dev, B43_MMIO_PHY_RADIO, b43_read16(dev, B43_MMIO_PHY_RADIO) & 0x7FFF); b43_phy_write(dev, B43_PHY_RFOVER, sav.phy_rfover); b43_phy_write(dev, B43_PHY_RFOVERVAL, sav.phy_rfoverval); b43_phy_write(dev, B43_PHY_ANALOGOVER, sav.phy_analogover); b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, sav.phy_analogoverval); b43_phy_write(dev, B43_PHY_CRS0, sav.phy_crs0); b43_phy_write(dev, B43_PHY_CLASSCTL, sav.phy_classctl); if (has_loopback_gain(phy)) { b43_phy_write(dev, B43_PHY_LO_MASK, sav.phy_lo_mask); b43_phy_write(dev, B43_PHY_LO_CTL, sav.phy_lo_ctl); } } if (i > 15) ret = radio78; else ret = rcc; return ret; } static void b43_phy_initb5(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 offset, value; u8 old_channel; if (phy->analog == 1) { b43_radio_set(dev, 0x007A, 0x0050); } if ((dev->dev->board_vendor != SSB_BOARDVENDOR_BCM) && (dev->dev->board_type != SSB_BOARD_BU4306)) { value = 0x2120; for (offset = 0x00A8; offset < 0x00C7; offset++) { b43_phy_write(dev, offset, value); value += 0x202; } } b43_phy_maskset(dev, 0x0035, 0xF0FF, 0x0700); if (phy->radio_ver == 0x2050) b43_phy_write(dev, 0x0038, 0x0667); if (phy->gmode || phy->rev >= 2) { if (phy->radio_ver == 0x2050) { b43_radio_set(dev, 0x007A, 0x0020); b43_radio_set(dev, 0x0051, 0x0004); } b43_write16(dev, B43_MMIO_PHY_RADIO, 0x0000); b43_phy_set(dev, 0x0802, 0x0100); b43_phy_set(dev, 0x042B, 0x2000); b43_phy_write(dev, 0x001C, 0x186A); b43_phy_maskset(dev, 0x0013, 0x00FF, 0x1900); b43_phy_maskset(dev, 0x0035, 0xFFC0, 0x0064); b43_phy_maskset(dev, 0x005D, 0xFF80, 0x000A); } if (dev->bad_frames_preempt) { b43_phy_set(dev, B43_PHY_RADIO_BITFIELD, (1 << 11)); } if (phy->analog == 1) { b43_phy_write(dev, 0x0026, 0xCE00); b43_phy_write(dev, 0x0021, 0x3763); b43_phy_write(dev, 0x0022, 0x1BC3); b43_phy_write(dev, 0x0023, 0x06F9); b43_phy_write(dev, 0x0024, 0x037E); } else b43_phy_write(dev, 0x0026, 0xCC00); b43_phy_write(dev, 0x0030, 0x00C6); b43_write16(dev, 0x03EC, 0x3F22); if (phy->analog == 1) b43_phy_write(dev, 0x0020, 0x3E1C); else b43_phy_write(dev, 0x0020, 0x301C); if (phy->analog == 0) b43_write16(dev, 0x03E4, 0x3000); old_channel = phy->channel; /* Force to channel 7, even if not supported. */ b43_gphy_channel_switch(dev, 7, 0); if (phy->radio_ver != 0x2050) { b43_radio_write16(dev, 0x0075, 0x0080); b43_radio_write16(dev, 0x0079, 0x0081); } b43_radio_write16(dev, 0x0050, 0x0020); b43_radio_write16(dev, 0x0050, 0x0023); if (phy->radio_ver == 0x2050) { b43_radio_write16(dev, 0x0050, 0x0020); b43_radio_write16(dev, 0x005A, 0x0070); } b43_radio_write16(dev, 0x005B, 0x007B); b43_radio_write16(dev, 0x005C, 0x00B0); b43_radio_set(dev, 0x007A, 0x0007); b43_gphy_channel_switch(dev, old_channel, 0); b43_phy_write(dev, 0x0014, 0x0080); b43_phy_write(dev, 0x0032, 0x00CA); b43_phy_write(dev, 0x002A, 0x88A3); b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); if (phy->radio_ver == 0x2050) b43_radio_write16(dev, 0x005D, 0x000D); b43_write16(dev, 0x03E4, (b43_read16(dev, 0x03E4) & 0xFFC0) | 0x0004); } static void b43_phy_initb6(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 offset, val; u8 old_channel; b43_phy_write(dev, 0x003E, 0x817A); b43_radio_write16(dev, 0x007A, (b43_radio_read16(dev, 0x007A) | 0x0058)); if (phy->radio_rev == 4 || phy->radio_rev == 5) { b43_radio_write16(dev, 0x51, 0x37); b43_radio_write16(dev, 0x52, 0x70); b43_radio_write16(dev, 0x53, 0xB3); b43_radio_write16(dev, 0x54, 0x9B); b43_radio_write16(dev, 0x5A, 0x88); b43_radio_write16(dev, 0x5B, 0x88); b43_radio_write16(dev, 0x5D, 0x88); b43_radio_write16(dev, 0x5E, 0x88); b43_radio_write16(dev, 0x7D, 0x88); b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW); } B43_WARN_ON(phy->radio_rev == 6 || phy->radio_rev == 7); /* We had code for these revs here... */ if (phy->radio_rev == 8) { b43_radio_write16(dev, 0x51, 0); b43_radio_write16(dev, 0x52, 0x40); b43_radio_write16(dev, 0x53, 0xB7); b43_radio_write16(dev, 0x54, 0x98); b43_radio_write16(dev, 0x5A, 0x88); b43_radio_write16(dev, 0x5B, 0x6B); b43_radio_write16(dev, 0x5C, 0x0F); if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_ALTIQ) { b43_radio_write16(dev, 0x5D, 0xFA); b43_radio_write16(dev, 0x5E, 0xD8); } else { b43_radio_write16(dev, 0x5D, 0xF5); b43_radio_write16(dev, 0x5E, 0xB8); } b43_radio_write16(dev, 0x0073, 0x0003); b43_radio_write16(dev, 0x007D, 0x00A8); b43_radio_write16(dev, 0x007C, 0x0001); b43_radio_write16(dev, 0x007E, 0x0008); } val = 0x1E1F; for (offset = 0x0088; offset < 0x0098; offset++) { b43_phy_write(dev, offset, val); val -= 0x0202; } val = 0x3E3F; for (offset = 0x0098; offset < 0x00A8; offset++) { b43_phy_write(dev, offset, val); val -= 0x0202; } val = 0x2120; for (offset = 0x00A8; offset < 0x00C8; offset++) { b43_phy_write(dev, offset, (val & 0x3F3F)); val += 0x0202; } if (phy->type == B43_PHYTYPE_G) { b43_radio_set(dev, 0x007A, 0x0020); b43_radio_set(dev, 0x0051, 0x0004); b43_phy_set(dev, 0x0802, 0x0100); b43_phy_set(dev, 0x042B, 0x2000); b43_phy_write(dev, 0x5B, 0); b43_phy_write(dev, 0x5C, 0); } old_channel = phy->channel; if (old_channel >= 8) b43_gphy_channel_switch(dev, 1, 0); else b43_gphy_channel_switch(dev, 13, 0); b43_radio_write16(dev, 0x0050, 0x0020); b43_radio_write16(dev, 0x0050, 0x0023); udelay(40); if (phy->radio_rev < 6 || phy->radio_rev == 8) { b43_radio_write16(dev, 0x7C, (b43_radio_read16(dev, 0x7C) | 0x0002)); b43_radio_write16(dev, 0x50, 0x20); } if (phy->radio_rev <= 2) { b43_radio_write16(dev, 0x7C, 0x20); b43_radio_write16(dev, 0x5A, 0x70); b43_radio_write16(dev, 0x5B, 0x7B); b43_radio_write16(dev, 0x5C, 0xB0); } b43_radio_maskset(dev, 0x007A, 0x00F8, 0x0007); b43_gphy_channel_switch(dev, old_channel, 0); b43_phy_write(dev, 0x0014, 0x0200); if (phy->radio_rev >= 6) b43_phy_write(dev, 0x2A, 0x88C2); else b43_phy_write(dev, 0x2A, 0x8AC0); b43_phy_write(dev, 0x0038, 0x0668); b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); if (phy->radio_rev <= 5) { b43_phy_maskset(dev, 0x5D, 0xFF80, 0x0003); } if (phy->radio_rev <= 2) b43_radio_write16(dev, 0x005D, 0x000D); if (phy->analog == 4) { b43_write16(dev, 0x3E4, 9); b43_phy_mask(dev, 0x61, 0x0FFF); } else { b43_phy_maskset(dev, 0x0002, 0xFFC0, 0x0004); } if (phy->type == B43_PHYTYPE_B) B43_WARN_ON(1); else if (phy->type == B43_PHYTYPE_G) b43_write16(dev, 0x03E6, 0x0); } static void b43_calc_loopback_gain(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 backup_phy[16] = { 0 }; u16 backup_radio[3]; u16 backup_bband; u16 i, j, loop_i_max; u16 trsw_rx; u16 loop1_outer_done, loop1_inner_done; backup_phy[0] = b43_phy_read(dev, B43_PHY_CRS0); backup_phy[1] = b43_phy_read(dev, B43_PHY_CCKBBANDCFG); backup_phy[2] = b43_phy_read(dev, B43_PHY_RFOVER); backup_phy[3] = b43_phy_read(dev, B43_PHY_RFOVERVAL); if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ backup_phy[4] = b43_phy_read(dev, B43_PHY_ANALOGOVER); backup_phy[5] = b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); } backup_phy[6] = b43_phy_read(dev, B43_PHY_CCK(0x5A)); backup_phy[7] = b43_phy_read(dev, B43_PHY_CCK(0x59)); backup_phy[8] = b43_phy_read(dev, B43_PHY_CCK(0x58)); backup_phy[9] = b43_phy_read(dev, B43_PHY_CCK(0x0A)); backup_phy[10] = b43_phy_read(dev, B43_PHY_CCK(0x03)); backup_phy[11] = b43_phy_read(dev, B43_PHY_LO_MASK); backup_phy[12] = b43_phy_read(dev, B43_PHY_LO_CTL); backup_phy[13] = b43_phy_read(dev, B43_PHY_CCK(0x2B)); backup_phy[14] = b43_phy_read(dev, B43_PHY_PGACTL); backup_phy[15] = b43_phy_read(dev, B43_PHY_LO_LEAKAGE); backup_bband = gphy->bbatt.att; backup_radio[0] = b43_radio_read16(dev, 0x52); backup_radio[1] = b43_radio_read16(dev, 0x43); backup_radio[2] = b43_radio_read16(dev, 0x7A); b43_phy_mask(dev, B43_PHY_CRS0, 0x3FFF); b43_phy_set(dev, B43_PHY_CCKBBANDCFG, 0x8000); b43_phy_set(dev, B43_PHY_RFOVER, 0x0002); b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xFFFD); b43_phy_set(dev, B43_PHY_RFOVER, 0x0001); b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xFFFE); if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0001); b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFE); b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0002); b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFD); } b43_phy_set(dev, B43_PHY_RFOVER, 0x000C); b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x000C); b43_phy_set(dev, B43_PHY_RFOVER, 0x0030); b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xFFCF, 0x10); b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0780); b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); b43_phy_set(dev, B43_PHY_CCK(0x0A), 0x2000); if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0004); b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFB); } b43_phy_maskset(dev, B43_PHY_CCK(0x03), 0xFF9F, 0x40); if (phy->radio_rev == 8) { b43_radio_write16(dev, 0x43, 0x000F); } else { b43_radio_write16(dev, 0x52, 0); b43_radio_maskset(dev, 0x43, 0xFFF0, 0x9); } b43_gphy_set_baseband_attenuation(dev, 11); if (phy->rev >= 3) b43_phy_write(dev, B43_PHY_LO_MASK, 0xC020); else b43_phy_write(dev, B43_PHY_LO_MASK, 0x8020); b43_phy_write(dev, B43_PHY_LO_CTL, 0); b43_phy_maskset(dev, B43_PHY_CCK(0x2B), 0xFFC0, 0x01); b43_phy_maskset(dev, B43_PHY_CCK(0x2B), 0xC0FF, 0x800); b43_phy_set(dev, B43_PHY_RFOVER, 0x0100); b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xCFFF); if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA) { if (phy->rev >= 7) { b43_phy_set(dev, B43_PHY_RFOVER, 0x0800); b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x8000); } } b43_radio_mask(dev, 0x7A, 0x00F7); j = 0; loop_i_max = (phy->radio_rev == 8) ? 15 : 9; for (i = 0; i < loop_i_max; i++) { for (j = 0; j < 16; j++) { b43_radio_write16(dev, 0x43, i); b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xF0FF, (j << 8)); b43_phy_maskset(dev, B43_PHY_PGACTL, 0x0FFF, 0xA000); b43_phy_set(dev, B43_PHY_PGACTL, 0xF000); udelay(20); if (b43_phy_read(dev, B43_PHY_LO_LEAKAGE) >= 0xDFC) goto exit_loop1; } } exit_loop1: loop1_outer_done = i; loop1_inner_done = j; if (j >= 8) { b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x30); trsw_rx = 0x1B; for (j = j - 8; j < 16; j++) { b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xF0FF, (j << 8)); b43_phy_maskset(dev, B43_PHY_PGACTL, 0x0FFF, 0xA000); b43_phy_set(dev, B43_PHY_PGACTL, 0xF000); udelay(20); trsw_rx -= 3; if (b43_phy_read(dev, B43_PHY_LO_LEAKAGE) >= 0xDFC) goto exit_loop2; } } else trsw_rx = 0x18; exit_loop2: if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ b43_phy_write(dev, B43_PHY_ANALOGOVER, backup_phy[4]); b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, backup_phy[5]); } b43_phy_write(dev, B43_PHY_CCK(0x5A), backup_phy[6]); b43_phy_write(dev, B43_PHY_CCK(0x59), backup_phy[7]); b43_phy_write(dev, B43_PHY_CCK(0x58), backup_phy[8]); b43_phy_write(dev, B43_PHY_CCK(0x0A), backup_phy[9]); b43_phy_write(dev, B43_PHY_CCK(0x03), backup_phy[10]); b43_phy_write(dev, B43_PHY_LO_MASK, backup_phy[11]); b43_phy_write(dev, B43_PHY_LO_CTL, backup_phy[12]); b43_phy_write(dev, B43_PHY_CCK(0x2B), backup_phy[13]); b43_phy_write(dev, B43_PHY_PGACTL, backup_phy[14]); b43_gphy_set_baseband_attenuation(dev, backup_bband); b43_radio_write16(dev, 0x52, backup_radio[0]); b43_radio_write16(dev, 0x43, backup_radio[1]); b43_radio_write16(dev, 0x7A, backup_radio[2]); b43_phy_write(dev, B43_PHY_RFOVER, backup_phy[2] | 0x0003); udelay(10); b43_phy_write(dev, B43_PHY_RFOVER, backup_phy[2]); b43_phy_write(dev, B43_PHY_RFOVERVAL, backup_phy[3]); b43_phy_write(dev, B43_PHY_CRS0, backup_phy[0]); b43_phy_write(dev, B43_PHY_CCKBBANDCFG, backup_phy[1]); gphy->max_lb_gain = ((loop1_inner_done * 6) - (loop1_outer_done * 4)) - 11; gphy->trsw_rx_gain = trsw_rx * 2; } static void b43_hardware_pctl_early_init(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; if (!b43_has_hardware_pctl(dev)) { b43_phy_write(dev, 0x047A, 0xC111); return; } b43_phy_mask(dev, 0x0036, 0xFEFF); b43_phy_write(dev, 0x002F, 0x0202); b43_phy_set(dev, 0x047C, 0x0002); b43_phy_set(dev, 0x047A, 0xF000); if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { b43_phy_maskset(dev, 0x047A, 0xFF0F, 0x0010); b43_phy_set(dev, 0x005D, 0x8000); b43_phy_maskset(dev, 0x004E, 0xFFC0, 0x0010); b43_phy_write(dev, 0x002E, 0xC07F); b43_phy_set(dev, 0x0036, 0x0400); } else { b43_phy_set(dev, 0x0036, 0x0200); b43_phy_set(dev, 0x0036, 0x0400); b43_phy_mask(dev, 0x005D, 0x7FFF); b43_phy_mask(dev, 0x004F, 0xFFFE); b43_phy_maskset(dev, 0x004E, 0xFFC0, 0x0010); b43_phy_write(dev, 0x002E, 0xC07F); b43_phy_maskset(dev, 0x047A, 0xFF0F, 0x0010); } } /* Hardware power control for G-PHY */ static void b43_hardware_pctl_init_gphy(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; if (!b43_has_hardware_pctl(dev)) { /* No hardware power control */ b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_HWPCTL); return; } b43_phy_maskset(dev, 0x0036, 0xFFC0, (gphy->tgt_idle_tssi - gphy->cur_idle_tssi)); b43_phy_maskset(dev, 0x0478, 0xFF00, (gphy->tgt_idle_tssi - gphy->cur_idle_tssi)); b43_gphy_tssi_power_lt_init(dev); b43_gphy_gain_lt_init(dev); b43_phy_mask(dev, 0x0060, 0xFFBF); b43_phy_write(dev, 0x0014, 0x0000); B43_WARN_ON(phy->rev < 6); b43_phy_set(dev, 0x0478, 0x0800); b43_phy_mask(dev, 0x0478, 0xFEFF); b43_phy_mask(dev, 0x0801, 0xFFBF); b43_gphy_dc_lt_init(dev, 1); /* Enable hardware pctl in firmware. */ b43_hf_write(dev, b43_hf_read(dev) | B43_HF_HWPCTL); } /* Initialize B/G PHY power control */ static void b43_phy_init_pctl(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_rfatt old_rfatt; struct b43_bbatt old_bbatt; u8 old_tx_control = 0; B43_WARN_ON(phy->type != B43_PHYTYPE_G); if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && (dev->dev->board_type == SSB_BOARD_BU4306)) return; b43_phy_write(dev, 0x0028, 0x8018); /* This does something with the Analog... */ b43_write16(dev, B43_MMIO_PHY0, b43_read16(dev, B43_MMIO_PHY0) & 0xFFDF); if (!phy->gmode) return; b43_hardware_pctl_early_init(dev); if (gphy->cur_idle_tssi == 0) { if (phy->radio_ver == 0x2050 && phy->analog == 0) { b43_radio_maskset(dev, 0x0076, 0x00F7, 0x0084); } else { struct b43_rfatt rfatt; struct b43_bbatt bbatt; memcpy(&old_rfatt, &gphy->rfatt, sizeof(old_rfatt)); memcpy(&old_bbatt, &gphy->bbatt, sizeof(old_bbatt)); old_tx_control = gphy->tx_control; bbatt.att = 11; if (phy->radio_rev == 8) { rfatt.att = 15; rfatt.with_padmix = true; } else { rfatt.att = 9; rfatt.with_padmix = false; } b43_set_txpower_g(dev, &bbatt, &rfatt, 0); } b43_dummy_transmission(dev, false, true); gphy->cur_idle_tssi = b43_phy_read(dev, B43_PHY_ITSSI); if (B43_DEBUG) { /* Current-Idle-TSSI sanity check. */ if (abs(gphy->cur_idle_tssi - gphy->tgt_idle_tssi) >= 20) { b43dbg(dev->wl, "!WARNING! Idle-TSSI phy->cur_idle_tssi " "measuring failed. (cur=%d, tgt=%d). Disabling TX power " "adjustment.\n", gphy->cur_idle_tssi, gphy->tgt_idle_tssi); gphy->cur_idle_tssi = 0; } } if (phy->radio_ver == 0x2050 && phy->analog == 0) { b43_radio_mask(dev, 0x0076, 0xFF7B); } else { b43_set_txpower_g(dev, &old_bbatt, &old_rfatt, old_tx_control); } } b43_hardware_pctl_init_gphy(dev); b43_shm_clear_tssi(dev); } static void b43_phy_initg(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 tmp; if (phy->rev == 1) b43_phy_initb5(dev); else b43_phy_initb6(dev); if (phy->rev >= 2 || phy->gmode) b43_phy_inita(dev); if (phy->rev >= 2) { b43_phy_write(dev, B43_PHY_ANALOGOVER, 0); b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, 0); } if (phy->rev == 2) { b43_phy_write(dev, B43_PHY_RFOVER, 0); b43_phy_write(dev, B43_PHY_PGACTL, 0xC0); } if (phy->rev > 5) { b43_phy_write(dev, B43_PHY_RFOVER, 0x400); b43_phy_write(dev, B43_PHY_PGACTL, 0xC0); } if (phy->gmode || phy->rev >= 2) { tmp = b43_phy_read(dev, B43_PHY_VERSION_OFDM); tmp &= B43_PHYVER_VERSION; if (tmp == 3 || tmp == 5) { b43_phy_write(dev, B43_PHY_OFDM(0xC2), 0x1816); b43_phy_write(dev, B43_PHY_OFDM(0xC3), 0x8006); } if (tmp == 5) { b43_phy_maskset(dev, B43_PHY_OFDM(0xCC), 0x00FF, 0x1F00); } } if ((phy->rev <= 2 && phy->gmode) || phy->rev >= 2) b43_phy_write(dev, B43_PHY_OFDM(0x7E), 0x78); if (phy->radio_rev == 8) { b43_phy_set(dev, B43_PHY_EXTG(0x01), 0x80); b43_phy_set(dev, B43_PHY_OFDM(0x3E), 0x4); } if (has_loopback_gain(phy)) b43_calc_loopback_gain(dev); if (phy->radio_rev != 8) { if (gphy->initval == 0xFFFF) gphy->initval = b43_radio_init2050(dev); else b43_radio_write16(dev, 0x0078, gphy->initval); } b43_lo_g_init(dev); if (has_tx_magnification(phy)) { b43_radio_write16(dev, 0x52, (b43_radio_read16(dev, 0x52) & 0xFF00) | gphy->lo_control->tx_bias | gphy-> lo_control->tx_magn); } else { b43_radio_maskset(dev, 0x52, 0xFFF0, gphy->lo_control->tx_bias); } if (phy->rev >= 6) { b43_phy_maskset(dev, B43_PHY_CCK(0x36), 0x0FFF, (gphy->lo_control->tx_bias << 12)); } if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075); else b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F); if (phy->rev < 2) b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x101); else b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x202); if (phy->gmode || phy->rev >= 2) { b43_lo_g_adjust(dev); b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078); } if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) { /* The specs state to update the NRSSI LT with * the value 0x7FFFFFFF here. I think that is some weird * compiler optimization in the original driver. * Essentially, what we do here is resetting all NRSSI LT * entries to -32 (see the clamp_val() in nrssi_hw_update()) */ b43_nrssi_hw_update(dev, 0xFFFF); //FIXME? b43_calc_nrssi_threshold(dev); } else if (phy->gmode || phy->rev >= 2) { if (gphy->nrssi[0] == -1000) { B43_WARN_ON(gphy->nrssi[1] != -1000); b43_calc_nrssi_slope(dev); } else b43_calc_nrssi_threshold(dev); } if (phy->radio_rev == 8) b43_phy_write(dev, B43_PHY_EXTG(0x05), 0x3230); b43_phy_init_pctl(dev); /* FIXME: The spec says in the following if, the 0 should be replaced 'if OFDM may not be used in the current locale' but OFDM is legal everywhere */ if ((dev->dev->chip_id == 0x4306 && dev->dev->chip_pkg == 2) || 0) { b43_phy_mask(dev, B43_PHY_CRS0, 0xBFFF); b43_phy_mask(dev, B43_PHY_OFDM(0xC3), 0x7FFF); } } void b43_gphy_channel_switch(struct b43_wldev *dev, unsigned int channel, bool synthetic_pu_workaround) { if (synthetic_pu_workaround) b43_synth_pu_workaround(dev, channel); b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel)); if (channel == 14) { if (dev->dev->bus_sprom->country_code == SSB_SPROM1CCODE_JAPAN) b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ACPR); else b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ACPR); b43_write16(dev, B43_MMIO_CHANNEL_EXT, b43_read16(dev, B43_MMIO_CHANNEL_EXT) | (1 << 11)); } else { b43_write16(dev, B43_MMIO_CHANNEL_EXT, b43_read16(dev, B43_MMIO_CHANNEL_EXT) & 0xF7BF); } } static void default_baseband_attenuation(struct b43_wldev *dev, struct b43_bbatt *bb) { struct b43_phy *phy = &dev->phy; if (phy->radio_ver == 0x2050 && phy->radio_rev < 6) bb->att = 0; else bb->att = 2; } static void default_radio_attenuation(struct b43_wldev *dev, struct b43_rfatt *rf) { struct b43_bus_dev *bdev = dev->dev; struct b43_phy *phy = &dev->phy; rf->with_padmix = false; if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM && dev->dev->board_type == SSB_BOARD_BCM4309G) { if (dev->dev->board_rev < 0x43) { rf->att = 2; return; } else if (dev->dev->board_rev < 0x51) { rf->att = 3; return; } } if (phy->type == B43_PHYTYPE_A) { rf->att = 0x60; return; } switch (phy->radio_ver) { case 0x2053: switch (phy->radio_rev) { case 1: rf->att = 6; return; } break; case 0x2050: switch (phy->radio_rev) { case 0: rf->att = 5; return; case 1: if (phy->type == B43_PHYTYPE_G) { if (bdev->board_vendor == SSB_BOARDVENDOR_BCM && bdev->board_type == SSB_BOARD_BCM4309G && bdev->board_rev >= 30) rf->att = 3; else if (bdev->board_vendor == SSB_BOARDVENDOR_BCM && bdev->board_type == SSB_BOARD_BU4306) rf->att = 3; else rf->att = 1; } else { if (bdev->board_vendor == SSB_BOARDVENDOR_BCM && bdev->board_type == SSB_BOARD_BCM4309G && bdev->board_rev >= 30) rf->att = 7; else rf->att = 6; } return; case 2: if (phy->type == B43_PHYTYPE_G) { if (bdev->board_vendor == SSB_BOARDVENDOR_BCM && bdev->board_type == SSB_BOARD_BCM4309G && bdev->board_rev >= 30) rf->att = 3; else if (bdev->board_vendor == SSB_BOARDVENDOR_BCM && bdev->board_type == SSB_BOARD_BU4306) rf->att = 5; else if (bdev->chip_id == 0x4320) rf->att = 4; else rf->att = 3; } else rf->att = 6; return; case 3: rf->att = 5; return; case 4: case 5: rf->att = 1; return; case 6: case 7: rf->att = 5; return; case 8: rf->att = 0xA; rf->with_padmix = true; return; case 9: default: rf->att = 5; return; } } rf->att = 5; } static u16 default_tx_control(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; if (phy->radio_ver != 0x2050) return 0; if (phy->radio_rev == 1) return B43_TXCTL_PA2DB | B43_TXCTL_TXMIX; if (phy->radio_rev < 6) return B43_TXCTL_PA2DB; if (phy->radio_rev == 8) return B43_TXCTL_TXMIX; return 0; } static u8 b43_gphy_aci_detect(struct b43_wldev *dev, u8 channel) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u8 ret = 0; u16 saved, rssi, temp; int i, j = 0; saved = b43_phy_read(dev, 0x0403); b43_switch_channel(dev, channel); b43_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); if (gphy->aci_hw_rssi) rssi = b43_phy_read(dev, 0x048A) & 0x3F; else rssi = saved & 0x3F; /* clamp temp to signed 5bit */ if (rssi > 32) rssi -= 64; for (i = 0; i < 100; i++) { temp = (b43_phy_read(dev, 0x047F) >> 8) & 0x3F; if (temp > 32) temp -= 64; if (temp < rssi) j++; if (j >= 20) ret = 1; } b43_phy_write(dev, 0x0403, saved); return ret; } static u8 b43_gphy_aci_scan(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; u8 ret[13]; unsigned int channel = phy->channel; unsigned int i, j, start, end; if (!((phy->type == B43_PHYTYPE_G) && (phy->rev > 0))) return 0; b43_phy_lock(dev); b43_radio_lock(dev); b43_phy_mask(dev, 0x0802, 0xFFFC); b43_phy_mask(dev, B43_PHY_G_CRS, 0x7FFF); b43_set_all_gains(dev, 3, 8, 1); start = (channel - 5 > 0) ? channel - 5 : 1; end = (channel + 5 < 14) ? channel + 5 : 13; for (i = start; i <= end; i++) { if (abs(channel - i) > 2) ret[i - 1] = b43_gphy_aci_detect(dev, i); } b43_switch_channel(dev, channel); b43_phy_maskset(dev, 0x0802, 0xFFFC, 0x0003); b43_phy_mask(dev, 0x0403, 0xFFF8); b43_phy_set(dev, B43_PHY_G_CRS, 0x8000); b43_set_original_gains(dev); for (i = 0; i < 13; i++) { if (!ret[i]) continue; end = (i + 5 < 13) ? i + 5 : 13; for (j = i; j < end; j++) ret[j] = 1; } b43_radio_unlock(dev); b43_phy_unlock(dev); return ret[channel - 1]; } static s32 b43_tssi2dbm_ad(s32 num, s32 den) { if (num < 0) return num / den; else return (num + den / 2) / den; } static s8 b43_tssi2dbm_entry(s8 entry[], u8 index, s16 pab0, s16 pab1, s16 pab2) { s32 m1, m2, f = 256, q, delta; s8 i = 0; m1 = b43_tssi2dbm_ad(16 * pab0 + index * pab1, 32); m2 = max(b43_tssi2dbm_ad(32768 + index * pab2, 256), 1); do { if (i > 15) return -EINVAL; q = b43_tssi2dbm_ad(f * 4096 - b43_tssi2dbm_ad(m2 * f, 16) * f, 2048); delta = abs(q - f); f = q; i++; } while (delta >= 2); entry[index] = clamp_val(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128); return 0; } u8 *b43_generate_dyn_tssi2dbm_tab(struct b43_wldev *dev, s16 pab0, s16 pab1, s16 pab2) { unsigned int i; u8 *tab; int err; tab = kmalloc(64, GFP_KERNEL); if (!tab) { b43err(dev->wl, "Could not allocate memory " "for tssi2dbm table\n"); return NULL; } for (i = 0; i < 64; i++) { err = b43_tssi2dbm_entry(tab, i, pab0, pab1, pab2); if (err) { b43err(dev->wl, "Could not generate " "tssi2dBm table\n"); kfree(tab); return NULL; } } return tab; } /* Initialise the TSSI->dBm lookup table */ static int b43_gphy_init_tssi2dbm_table(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; s16 pab0, pab1, pab2; pab0 = (s16) (dev->dev->bus_sprom->pa0b0); pab1 = (s16) (dev->dev->bus_sprom->pa0b1); pab2 = (s16) (dev->dev->bus_sprom->pa0b2); B43_WARN_ON((dev->dev->chip_id == 0x4301) && (phy->radio_ver != 0x2050)); /* Not supported anymore */ gphy->dyn_tssi_tbl = false; if (pab0 != 0 && pab1 != 0 && pab2 != 0 && pab0 != -1 && pab1 != -1 && pab2 != -1) { /* The pabX values are set in SPROM. Use them. */ if ((s8) dev->dev->bus_sprom->itssi_bg != 0 && (s8) dev->dev->bus_sprom->itssi_bg != -1) { gphy->tgt_idle_tssi = (s8) (dev->dev->bus_sprom->itssi_bg); } else gphy->tgt_idle_tssi = 62; gphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0, pab1, pab2); if (!gphy->tssi2dbm) return -ENOMEM; gphy->dyn_tssi_tbl = true; } else { /* pabX values not set in SPROM. */ gphy->tgt_idle_tssi = 52; gphy->tssi2dbm = b43_tssi2dbm_g_table; } return 0; } static int b43_gphy_op_allocate(struct b43_wldev *dev) { struct b43_phy_g *gphy; struct b43_txpower_lo_control *lo; int err; gphy = kzalloc(sizeof(*gphy), GFP_KERNEL); if (!gphy) { err = -ENOMEM; goto error; } dev->phy.g = gphy; lo = kzalloc(sizeof(*lo), GFP_KERNEL); if (!lo) { err = -ENOMEM; goto err_free_gphy; } gphy->lo_control = lo; err = b43_gphy_init_tssi2dbm_table(dev); if (err) goto err_free_lo; return 0; err_free_lo: kfree(lo); err_free_gphy: kfree(gphy); error: return err; } static void b43_gphy_op_prepare_structs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; const void *tssi2dbm; int tgt_idle_tssi; struct b43_txpower_lo_control *lo; unsigned int i; /* tssi2dbm table is constant, so it is initialized at alloc time. * Save a copy of the pointer. */ tssi2dbm = gphy->tssi2dbm; tgt_idle_tssi = gphy->tgt_idle_tssi; /* Save the LO pointer. */ lo = gphy->lo_control; /* Zero out the whole PHY structure. */ memset(gphy, 0, sizeof(*gphy)); /* Restore pointers. */ gphy->tssi2dbm = tssi2dbm; gphy->tgt_idle_tssi = tgt_idle_tssi; gphy->lo_control = lo; memset(gphy->minlowsig, 0xFF, sizeof(gphy->minlowsig)); /* NRSSI */ for (i = 0; i < ARRAY_SIZE(gphy->nrssi); i++) gphy->nrssi[i] = -1000; for (i = 0; i < ARRAY_SIZE(gphy->nrssi_lt); i++) gphy->nrssi_lt[i] = i; gphy->lofcal = 0xFFFF; gphy->initval = 0xFFFF; gphy->interfmode = B43_INTERFMODE_NONE; /* OFDM-table address caching. */ gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_UNKNOWN; gphy->average_tssi = 0xFF; /* Local Osciallator structure */ lo->tx_bias = 0xFF; INIT_LIST_HEAD(&lo->calib_list); } static void b43_gphy_op_free(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; kfree(gphy->lo_control); if (gphy->dyn_tssi_tbl) kfree(gphy->tssi2dbm); gphy->dyn_tssi_tbl = false; gphy->tssi2dbm = NULL; kfree(gphy); dev->phy.g = NULL; } static int b43_gphy_op_prepare_hardware(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; B43_WARN_ON(phy->type != B43_PHYTYPE_G); default_baseband_attenuation(dev, &gphy->bbatt); default_radio_attenuation(dev, &gphy->rfatt); gphy->tx_control = (default_tx_control(dev) << 4); generate_rfatt_list(dev, &lo->rfatt_list); generate_bbatt_list(dev, &lo->bbatt_list); /* Commit previous writes */ b43_read32(dev, B43_MMIO_MACCTL); if (phy->rev == 1) { /* Workaround: Temporarly disable gmode through the early init * phase, as the gmode stuff is not needed for phy rev 1 */ phy->gmode = false; b43_wireless_core_reset(dev, 0); b43_phy_initg(dev); phy->gmode = true; b43_wireless_core_reset(dev, 1); } return 0; } static int b43_gphy_op_init(struct b43_wldev *dev) { b43_phy_initg(dev); return 0; } static void b43_gphy_op_exit(struct b43_wldev *dev) { b43_lo_g_cleanup(dev); } static u16 b43_gphy_op_read(struct b43_wldev *dev, u16 reg) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_gphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) { b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } static u16 b43_gphy_op_radio_read(struct b43_wldev *dev, u16 reg) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); /* G-PHY needs 0x80 for read access. */ reg |= 0x80; b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); } static void b43_gphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } static bool b43_gphy_op_supports_hwpctl(struct b43_wldev *dev) { return (dev->phy.rev >= 6); } static void b43_gphy_op_software_rfkill(struct b43_wldev *dev, bool blocked) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; unsigned int channel; might_sleep(); if (!blocked) { /* Turn radio ON */ if (phy->radio_on) return; b43_phy_write(dev, 0x0015, 0x8000); b43_phy_write(dev, 0x0015, 0xCC00); b43_phy_write(dev, 0x0015, (phy->gmode ? 0x00C0 : 0x0000)); if (gphy->radio_off_context.valid) { /* Restore the RFover values. */ b43_phy_write(dev, B43_PHY_RFOVER, gphy->radio_off_context.rfover); b43_phy_write(dev, B43_PHY_RFOVERVAL, gphy->radio_off_context.rfoverval); gphy->radio_off_context.valid = false; } channel = phy->channel; b43_gphy_channel_switch(dev, 6, 1); b43_gphy_channel_switch(dev, channel, 0); } else { /* Turn radio OFF */ u16 rfover, rfoverval; rfover = b43_phy_read(dev, B43_PHY_RFOVER); rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); gphy->radio_off_context.rfover = rfover; gphy->radio_off_context.rfoverval = rfoverval; gphy->radio_off_context.valid = true; b43_phy_write(dev, B43_PHY_RFOVER, rfover | 0x008C); b43_phy_write(dev, B43_PHY_RFOVERVAL, rfoverval & 0xFF73); } } static int b43_gphy_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { if ((new_channel < 1) || (new_channel > 14)) return -EINVAL; b43_gphy_channel_switch(dev, new_channel, 0); return 0; } static unsigned int b43_gphy_op_get_default_chan(struct b43_wldev *dev) { return 1; /* Default to channel 1 */ } static void b43_gphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) { struct b43_phy *phy = &dev->phy; u16 tmp; int autodiv = 0; if (antenna == B43_ANTENNA_AUTO0 || antenna == B43_ANTENNA_AUTO1) autodiv = 1; b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); b43_phy_maskset(dev, B43_PHY_BBANDCFG, ~B43_PHY_BBANDCFG_RXANT, (autodiv ? B43_ANTENNA_AUTO1 : antenna) << B43_PHY_BBANDCFG_RXANT_SHIFT); if (autodiv) { tmp = b43_phy_read(dev, B43_PHY_ANTDWELL); if (antenna == B43_ANTENNA_AUTO1) tmp &= ~B43_PHY_ANTDWELL_AUTODIV1; else tmp |= B43_PHY_ANTDWELL_AUTODIV1; b43_phy_write(dev, B43_PHY_ANTDWELL, tmp); } tmp = b43_phy_read(dev, B43_PHY_ANTWRSETT); if (autodiv) tmp |= B43_PHY_ANTWRSETT_ARXDIV; else tmp &= ~B43_PHY_ANTWRSETT_ARXDIV; b43_phy_write(dev, B43_PHY_ANTWRSETT, tmp); if (autodiv) b43_phy_set(dev, B43_PHY_ANTWRSETT, B43_PHY_ANTWRSETT_ARXDIV); else { b43_phy_mask(dev, B43_PHY_ANTWRSETT, B43_PHY_ANTWRSETT_ARXDIV); } if (phy->rev >= 2) { b43_phy_set(dev, B43_PHY_OFDM61, B43_PHY_OFDM61_10); b43_phy_maskset(dev, B43_PHY_DIVSRCHGAINBACK, 0xFF00, 0x15); if (phy->rev == 2) b43_phy_write(dev, B43_PHY_ADIVRELATED, 8); else b43_phy_maskset(dev, B43_PHY_ADIVRELATED, 0xFF00, 8); } if (phy->rev >= 6) b43_phy_write(dev, B43_PHY_OFDM9B, 0xDC); b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); } static int b43_gphy_op_interf_mitigation(struct b43_wldev *dev, enum b43_interference_mitigation mode) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; int currentmode; B43_WARN_ON(phy->type != B43_PHYTYPE_G); if ((phy->rev == 0) || (!phy->gmode)) return -ENODEV; gphy->aci_wlan_automatic = false; switch (mode) { case B43_INTERFMODE_AUTOWLAN: gphy->aci_wlan_automatic = true; if (gphy->aci_enable) mode = B43_INTERFMODE_MANUALWLAN; else mode = B43_INTERFMODE_NONE; break; case B43_INTERFMODE_NONE: case B43_INTERFMODE_NONWLAN: case B43_INTERFMODE_MANUALWLAN: break; default: return -EINVAL; } currentmode = gphy->interfmode; if (currentmode == mode) return 0; if (currentmode != B43_INTERFMODE_NONE) b43_radio_interference_mitigation_disable(dev, currentmode); if (mode == B43_INTERFMODE_NONE) { gphy->aci_enable = false; gphy->aci_hw_rssi = false; } else b43_radio_interference_mitigation_enable(dev, mode); gphy->interfmode = mode; return 0; } /* http://bcm-specs.sipsolutions.net/EstimatePowerOut * This function converts a TSSI value to dBm in Q5.2 */ static s8 b43_gphy_estimate_power_out(struct b43_wldev *dev, s8 tssi) { struct b43_phy_g *gphy = dev->phy.g; s8 dbm; s32 tmp; tmp = (gphy->tgt_idle_tssi - gphy->cur_idle_tssi + tssi); tmp = clamp_val(tmp, 0x00, 0x3F); dbm = gphy->tssi2dbm[tmp]; return dbm; } static void b43_put_attenuation_into_ranges(struct b43_wldev *dev, int *_bbatt, int *_rfatt) { int rfatt = *_rfatt; int bbatt = *_bbatt; struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; /* Get baseband and radio attenuation values into their permitted ranges. * Radio attenuation affects power level 4 times as much as baseband. */ /* Range constants */ const int rf_min = lo->rfatt_list.min_val; const int rf_max = lo->rfatt_list.max_val; const int bb_min = lo->bbatt_list.min_val; const int bb_max = lo->bbatt_list.max_val; while (1) { if (rfatt > rf_max && bbatt > bb_max - 4) break; /* Can not get it into ranges */ if (rfatt < rf_min && bbatt < bb_min + 4) break; /* Can not get it into ranges */ if (bbatt > bb_max && rfatt > rf_max - 1) break; /* Can not get it into ranges */ if (bbatt < bb_min && rfatt < rf_min + 1) break; /* Can not get it into ranges */ if (bbatt > bb_max) { bbatt -= 4; rfatt += 1; continue; } if (bbatt < bb_min) { bbatt += 4; rfatt -= 1; continue; } if (rfatt > rf_max) { rfatt -= 1; bbatt += 4; continue; } if (rfatt < rf_min) { rfatt += 1; bbatt -= 4; continue; } break; } *_rfatt = clamp_val(rfatt, rf_min, rf_max); *_bbatt = clamp_val(bbatt, bb_min, bb_max); } static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; int rfatt, bbatt; u8 tx_control; b43_mac_suspend(dev); /* Calculate the new attenuation values. */ bbatt = gphy->bbatt.att; bbatt += gphy->bbatt_delta; rfatt = gphy->rfatt.att; rfatt += gphy->rfatt_delta; b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); tx_control = gphy->tx_control; if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) { if (rfatt <= 1) { if (tx_control == 0) { tx_control = B43_TXCTL_PA2DB | B43_TXCTL_TXMIX; rfatt += 2; bbatt += 2; } else if (dev->dev->bus_sprom-> boardflags_lo & B43_BFL_PACTRL) { bbatt += 4 * (rfatt - 2); rfatt = 2; } } else if (rfatt > 4 && tx_control) { tx_control = 0; if (bbatt < 3) { rfatt -= 3; bbatt += 2; } else { rfatt -= 2; bbatt -= 2; } } } /* Save the control values */ gphy->tx_control = tx_control; b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); gphy->rfatt.att = rfatt; gphy->bbatt.att = bbatt; if (b43_debug(dev, B43_DBG_XMITPOWER)) b43dbg(dev->wl, "Adjusting TX power\n"); /* Adjust the hardware */ b43_phy_lock(dev); b43_radio_lock(dev); b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); b43_radio_unlock(dev); b43_phy_unlock(dev); b43_mac_enable(dev); } static enum b43_txpwr_result b43_gphy_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; unsigned int average_tssi; int cck_result, ofdm_result; int estimated_pwr, desired_pwr, pwr_adjust; int rfatt_delta, bbatt_delta; unsigned int max_pwr; /* First get the average TSSI */ cck_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_CCK); ofdm_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_OFDM_G); if ((cck_result < 0) && (ofdm_result < 0)) { /* No TSSI information available */ if (!ignore_tssi) goto no_adjustment_needed; cck_result = 0; ofdm_result = 0; } if (cck_result < 0) average_tssi = ofdm_result; else if (ofdm_result < 0) average_tssi = cck_result; else average_tssi = (cck_result + ofdm_result) / 2; /* Merge the average with the stored value. */ if (likely(gphy->average_tssi != 0xFF)) average_tssi = (average_tssi + gphy->average_tssi) / 2; gphy->average_tssi = average_tssi; B43_WARN_ON(average_tssi >= B43_TSSI_MAX); /* Estimate the TX power emission based on the TSSI */ estimated_pwr = b43_gphy_estimate_power_out(dev, average_tssi); B43_WARN_ON(phy->type != B43_PHYTYPE_G); max_pwr = dev->dev->bus_sprom->maxpwr_bg; if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) max_pwr -= 3; /* minus 0.75 */ if (unlikely(max_pwr >= INT_TO_Q52(30/*dBm*/))) { b43warn(dev->wl, "Invalid max-TX-power value in SPROM.\n"); max_pwr = INT_TO_Q52(20); /* fake it */ dev->dev->bus_sprom->maxpwr_bg = max_pwr; } /* Get desired power (in Q5.2) */ if (phy->desired_txpower < 0) desired_pwr = INT_TO_Q52(0); else desired_pwr = INT_TO_Q52(phy->desired_txpower); /* And limit it. max_pwr already is Q5.2 */ desired_pwr = clamp_val(desired_pwr, 0, max_pwr); if (b43_debug(dev, B43_DBG_XMITPOWER)) { b43dbg(dev->wl, "[TX power] current = " Q52_FMT " dBm, desired = " Q52_FMT " dBm, max = " Q52_FMT "\n", Q52_ARG(estimated_pwr), Q52_ARG(desired_pwr), Q52_ARG(max_pwr)); } /* Calculate the adjustment delta. */ pwr_adjust = desired_pwr - estimated_pwr; if (pwr_adjust == 0) goto no_adjustment_needed; /* RF attenuation delta. */ rfatt_delta = ((pwr_adjust + 7) / 8); /* Lower attenuation => Bigger power output. Negate it. */ rfatt_delta = -rfatt_delta; /* Baseband attenuation delta. */ bbatt_delta = pwr_adjust / 2; /* Lower attenuation => Bigger power output. Negate it. */ bbatt_delta = -bbatt_delta; /* RF att affects power level 4 times as much as * Baseband attennuation. Subtract it. */ bbatt_delta -= 4 * rfatt_delta; #if B43_DEBUG if (b43_debug(dev, B43_DBG_XMITPOWER)) { int dbm = pwr_adjust < 0 ? -pwr_adjust : pwr_adjust; b43dbg(dev->wl, "[TX power deltas] %s" Q52_FMT " dBm => " "bbatt-delta = %d, rfatt-delta = %d\n", (pwr_adjust < 0 ? "-" : ""), Q52_ARG(dbm), bbatt_delta, rfatt_delta); } #endif /* DEBUG */ /* So do we finally need to adjust something in hardware? */ if ((rfatt_delta == 0) && (bbatt_delta == 0)) goto no_adjustment_needed; /* Save the deltas for later when we adjust the power. */ gphy->bbatt_delta = bbatt_delta; gphy->rfatt_delta = rfatt_delta; /* We need to adjust the TX power on the device. */ return B43_TXPWR_RES_NEED_ADJUST; no_adjustment_needed: return B43_TXPWR_RES_DONE; } static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; b43_mac_suspend(dev); //TODO: update_aci_moving_average if (gphy->aci_enable && gphy->aci_wlan_automatic) { if (!gphy->aci_enable && 1 /*TODO: not scanning? */ ) { if (0 /*TODO: bunch of conditions */ ) { phy->ops->interf_mitigation(dev, B43_INTERFMODE_MANUALWLAN); } } else if (0 /*TODO*/) { if (/*(aci_average > 1000) &&*/ !b43_gphy_aci_scan(dev)) phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE); } } else if (gphy->interfmode == B43_INTERFMODE_NONWLAN && phy->rev == 1) { //TODO: implement rev1 workaround } b43_lo_g_maintanance_work(dev); b43_mac_enable(dev); } static void b43_gphy_op_pwork_60sec(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) return; b43_mac_suspend(dev); b43_calc_nrssi_slope(dev); if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 8)) { u8 old_chan = phy->channel; /* VCO Calibration */ if (old_chan >= 8) b43_switch_channel(dev, 1); else b43_switch_channel(dev, 13); b43_switch_channel(dev, old_chan); } b43_mac_enable(dev); } const struct b43_phy_operations b43_phyops_g = { .allocate = b43_gphy_op_allocate, .free = b43_gphy_op_free, .prepare_structs = b43_gphy_op_prepare_structs, .prepare_hardware = b43_gphy_op_prepare_hardware, .init = b43_gphy_op_init, .exit = b43_gphy_op_exit, .phy_read = b43_gphy_op_read, .phy_write = b43_gphy_op_write, .radio_read = b43_gphy_op_radio_read, .radio_write = b43_gphy_op_radio_write, .supports_hwpctl = b43_gphy_op_supports_hwpctl, .software_rfkill = b43_gphy_op_software_rfkill, .switch_analog = b43_phyop_switch_analog_generic, .switch_channel = b43_gphy_op_switch_channel, .get_default_chan = b43_gphy_op_get_default_chan, .set_rx_antenna = b43_gphy_op_set_rx_antenna, .interf_mitigation = b43_gphy_op_interf_mitigation, .recalc_txpower = b43_gphy_op_recalc_txpower, .adjust_txpower = b43_gphy_op_adjust_txpower, .pwork_15sec = b43_gphy_op_pwork_15sec, .pwork_60sec = b43_gphy_op_pwork_60sec, }; compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_common.c0000644000175000017500000003135312026211315023372 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver Common PHY routines Copyright (c) 2005 Martin Langer , Copyright (c) 2005-2007 Stefano Brivio Copyright (c) 2005-2008 Michael Buesch Copyright (c) 2005, 2006 Danny van Dyk Copyright (c) 2005, 2006 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "phy_common.h" #include "phy_g.h" #include "phy_a.h" #include "phy_n.h" #include "phy_lp.h" #include "phy_ht.h" #include "phy_lcn.h" #include "b43.h" #include "main.h" int b43_phy_allocate(struct b43_wldev *dev) { struct b43_phy *phy = &(dev->phy); int err; phy->ops = NULL; switch (phy->type) { case B43_PHYTYPE_A: phy->ops = &b43_phyops_a; break; case B43_PHYTYPE_G: phy->ops = &b43_phyops_g; break; case B43_PHYTYPE_N: #ifdef CONFIG_B43_PHY_N phy->ops = &b43_phyops_n; #endif break; case B43_PHYTYPE_LP: #ifdef CONFIG_B43_PHY_LP phy->ops = &b43_phyops_lp; #endif break; case B43_PHYTYPE_HT: #ifdef CONFIG_B43_PHY_HT phy->ops = &b43_phyops_ht; #endif break; case B43_PHYTYPE_LCN: #ifdef CONFIG_B43_PHY_LCN phy->ops = &b43_phyops_lcn; #endif break; } if (B43_WARN_ON(!phy->ops)) return -ENODEV; err = phy->ops->allocate(dev); if (err) phy->ops = NULL; return err; } void b43_phy_free(struct b43_wldev *dev) { dev->phy.ops->free(dev); dev->phy.ops = NULL; } int b43_phy_init(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; const struct b43_phy_operations *ops = phy->ops; int err; phy->channel = ops->get_default_chan(dev); ops->software_rfkill(dev, false); err = ops->init(dev); if (err) { b43err(dev->wl, "PHY init failed\n"); goto err_block_rf; } /* Make sure to switch hardware and firmware (SHM) to * the default channel. */ err = b43_switch_channel(dev, ops->get_default_chan(dev)); if (err) { b43err(dev->wl, "PHY init: Channel switch to default failed\n"); goto err_phy_exit; } return 0; err_phy_exit: if (ops->exit) ops->exit(dev); err_block_rf: ops->software_rfkill(dev, true); return err; } void b43_phy_exit(struct b43_wldev *dev) { const struct b43_phy_operations *ops = dev->phy.ops; ops->software_rfkill(dev, true); if (ops->exit) ops->exit(dev); } bool b43_has_hardware_pctl(struct b43_wldev *dev) { if (!dev->phy.hardware_power_control) return 0; if (!dev->phy.ops->supports_hwpctl) return 0; return dev->phy.ops->supports_hwpctl(dev); } void b43_radio_lock(struct b43_wldev *dev) { u32 macctl; #if B43_DEBUG B43_WARN_ON(dev->phy.radio_locked); dev->phy.radio_locked = true; #endif macctl = b43_read32(dev, B43_MMIO_MACCTL); macctl |= B43_MACCTL_RADIOLOCK; b43_write32(dev, B43_MMIO_MACCTL, macctl); /* Commit the write and wait for the firmware * to finish any radio register access. */ b43_read32(dev, B43_MMIO_MACCTL); udelay(10); } void b43_radio_unlock(struct b43_wldev *dev) { u32 macctl; #if B43_DEBUG B43_WARN_ON(!dev->phy.radio_locked); dev->phy.radio_locked = false; #endif /* Commit any write */ b43_read16(dev, B43_MMIO_PHY_VER); /* unlock */ macctl = b43_read32(dev, B43_MMIO_MACCTL); macctl &= ~B43_MACCTL_RADIOLOCK; b43_write32(dev, B43_MMIO_MACCTL, macctl); } void b43_phy_lock(struct b43_wldev *dev) { #if B43_DEBUG B43_WARN_ON(dev->phy.phy_locked); dev->phy.phy_locked = true; #endif B43_WARN_ON(dev->dev->core_rev < 3); if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); } void b43_phy_unlock(struct b43_wldev *dev) { #if B43_DEBUG B43_WARN_ON(!dev->phy.phy_locked); dev->phy.phy_locked = false; #endif B43_WARN_ON(dev->dev->core_rev < 3); if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) b43_power_saving_ctl_bits(dev, 0); } static inline void assert_mac_suspended(struct b43_wldev *dev) { if (!B43_DEBUG) return; if ((b43_status(dev) >= B43_STAT_INITIALIZED) && (dev->mac_suspended <= 0)) { b43dbg(dev->wl, "PHY/RADIO register access with " "enabled MAC.\n"); dump_stack(); } } u16 b43_radio_read(struct b43_wldev *dev, u16 reg) { assert_mac_suspended(dev); return dev->phy.ops->radio_read(dev, reg); } void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { assert_mac_suspended(dev); dev->phy.ops->radio_write(dev, reg, value); } void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask) { b43_radio_write16(dev, offset, b43_radio_read16(dev, offset) & mask); } void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set) { b43_radio_write16(dev, offset, b43_radio_read16(dev, offset) | set); } void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) { b43_radio_write16(dev, offset, (b43_radio_read16(dev, offset) & mask) | set); } bool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, u16 value, int delay, int timeout) { u16 val; int i; for (i = 0; i < timeout; i += delay) { val = b43_radio_read(dev, offset); if ((val & mask) == value) return true; udelay(delay); } return false; } u16 b43_phy_read(struct b43_wldev *dev, u16 reg) { assert_mac_suspended(dev); dev->phy.writes_counter = 0; return dev->phy.ops->phy_read(dev, reg); } void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) { assert_mac_suspended(dev); dev->phy.ops->phy_write(dev, reg, value); if (++dev->phy.writes_counter == B43_MAX_WRITES_IN_ROW) { b43_read16(dev, B43_MMIO_PHY_VER); dev->phy.writes_counter = 0; } } void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) { assert_mac_suspended(dev); dev->phy.ops->phy_write(dev, destreg, dev->phy.ops->phy_read(dev, srcreg)); } void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) { if (dev->phy.ops->phy_maskset) { assert_mac_suspended(dev); dev->phy.ops->phy_maskset(dev, offset, mask, 0); } else { b43_phy_write(dev, offset, b43_phy_read(dev, offset) & mask); } } void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) { if (dev->phy.ops->phy_maskset) { assert_mac_suspended(dev); dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set); } else { b43_phy_write(dev, offset, b43_phy_read(dev, offset) | set); } } void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) { if (dev->phy.ops->phy_maskset) { assert_mac_suspended(dev); dev->phy.ops->phy_maskset(dev, offset, mask, set); } else { b43_phy_write(dev, offset, (b43_phy_read(dev, offset) & mask) | set); } } int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { struct b43_phy *phy = &(dev->phy); u16 channelcookie, savedcookie; int err; if (new_channel == B43_DEFAULT_CHANNEL) new_channel = phy->ops->get_default_chan(dev); /* First we set the channel radio code to prevent the * firmware from sending ghost packets. */ channelcookie = new_channel; if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) channelcookie |= B43_SHM_SH_CHAN_5GHZ; /* FIXME: set 40Mhz flag if required */ if (0) channelcookie |= B43_SHM_SH_CHAN_40MHZ; savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN); b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie); /* Now try to switch the PHY hardware channel. */ err = phy->ops->switch_channel(dev, new_channel); if (err) goto err_restore_cookie; dev->phy.channel = new_channel; /* Wait for the radio to tune to the channel and stabilize. */ msleep(8); return 0; err_restore_cookie: b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, savedcookie); return err; } void b43_software_rfkill(struct b43_wldev *dev, bool blocked) { struct b43_phy *phy = &dev->phy; b43_mac_suspend(dev); phy->ops->software_rfkill(dev, blocked); phy->radio_on = !blocked; b43_mac_enable(dev); } /** * b43_phy_txpower_adjust_work - TX power workqueue. * * Workqueue for updating the TX power parameters in hardware. */ void b43_phy_txpower_adjust_work(struct work_struct *work) { struct b43_wl *wl = container_of(work, struct b43_wl, txpower_adjust_work); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED))) dev->phy.ops->adjust_txpower(dev); mutex_unlock(&wl->mutex); } void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) { struct b43_phy *phy = &dev->phy; unsigned long now = jiffies; enum b43_txpwr_result result; if (!(flags & B43_TXPWR_IGNORE_TIME)) { /* Check if it's time for a TXpower check. */ if (time_before(now, phy->next_txpwr_check_time)) return; /* Not yet */ } /* The next check will be needed in two seconds, or later. */ phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2)); if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && (dev->dev->board_type == SSB_BOARD_BU4306)) return; /* No software txpower adjustment needed */ result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI)); if (result == B43_TXPWR_RES_DONE) return; /* We are done. */ B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST); B43_WARN_ON(phy->ops->adjust_txpower == NULL); /* We must adjust the transmission power in hardware. * Schedule b43_phy_txpower_adjust_work(). */ ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); } int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) { const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK); unsigned int a, b, c, d; unsigned int average; u32 tmp; tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset); a = tmp & 0xFF; b = (tmp >> 8) & 0xFF; c = (tmp >> 16) & 0xFF; d = (tmp >> 24) & 0xFF; if (a == 0 || a == B43_TSSI_MAX || b == 0 || b == B43_TSSI_MAX || c == 0 || c == B43_TSSI_MAX || d == 0 || d == B43_TSSI_MAX) return -ENOENT; /* The values are OK. Clear them. */ tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) | (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24); b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp); if (is_ofdm) { a = (a + 32) & 0x3F; b = (b + 32) & 0x3F; c = (c + 32) & 0x3F; d = (d + 32) & 0x3F; } /* Get the average of the values with 0.5 added to each value. */ average = (a + b + c + d + 2) / 4; if (is_ofdm) { /* Adjust for CCK-boost */ if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1) & B43_HF_CCKBOOST) average = (average >= 13) ? (average - 13) : 0; } return average; } void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) { b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); } bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type) { return (channel_type == NL80211_CHAN_HT40MINUS || channel_type == NL80211_CHAN_HT40PLUS); } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ void b43_phy_force_clock(struct b43_wldev *dev, bool force) { u32 tmp; WARN_ON(dev->phy.type != B43_PHYTYPE_N && dev->phy.type != B43_PHYTYPE_HT); switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); if (force) tmp |= BCMA_IOCTL_FGC; else tmp &= ~BCMA_IOCTL_FGC; bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); if (force) tmp |= SSB_TMSLOW_FGC; else tmp &= ~SSB_TMSLOW_FGC; ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); break; #endif } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */ struct b43_c32 b43_cordic(int theta) { static const u32 arctg[] = { 2949120, 1740967, 919879, 466945, 234379, 117304, 58666, 29335, 14668, 7334, 3667, 1833, 917, 458, 229, 115, 57, 29, }; u8 i; s32 tmp; s8 signx = 1; u32 angle = 0; struct b43_c32 ret = { .i = 39797, .q = 0, }; while (theta > (180 << 16)) theta -= (360 << 16); while (theta < -(180 << 16)) theta += (360 << 16); if (theta > (90 << 16)) { theta -= (180 << 16); signx = -1; } else if (theta < -(90 << 16)) { theta += (180 << 16); signx = -1; } for (i = 0; i <= 17; i++) { if (theta > angle) { tmp = ret.i - (ret.q >> i); ret.q += ret.i >> i; ret.i = tmp; angle += arctg[i]; } else { tmp = ret.i + (ret.q >> i); ret.q -= ret.i >> i; ret.i = tmp; angle -= arctg[i]; } } ret.i *= signx; ret.q *= signx; return ret; } compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_a.h0000644000175000017500000001421312026211315022323 0ustar mcgrofmcgrof#ifndef LINUX_B43_PHY_A_H_ #define LINUX_B43_PHY_A_H_ #include "phy_common.h" /* OFDM (A) PHY Registers */ #define B43_PHY_VERSION_OFDM B43_PHY_OFDM(0x00) /* Versioning register for A-PHY */ #define B43_PHY_BBANDCFG B43_PHY_OFDM(0x01) /* Baseband config */ #define B43_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */ #define B43_PHY_BBANDCFG_RXANT_SHIFT 7 #define B43_PHY_PWRDOWN B43_PHY_OFDM(0x03) /* Powerdown */ #define B43_PHY_CRSTHRES1_R1 B43_PHY_OFDM(0x06) /* CRS Threshold 1 (phy.rev 1 only) */ #define B43_PHY_LNAHPFCTL B43_PHY_OFDM(0x1C) /* LNA/HPF control */ #define B43_PHY_LPFGAINCTL B43_PHY_OFDM(0x20) /* LPF Gain control */ #define B43_PHY_ADIVRELATED B43_PHY_OFDM(0x27) /* FIXME rename */ #define B43_PHY_CRS0 B43_PHY_OFDM(0x29) #define B43_PHY_CRS0_EN 0x4000 #define B43_PHY_PEAK_COUNT B43_PHY_OFDM(0x30) #define B43_PHY_ANTDWELL B43_PHY_OFDM(0x2B) /* Antenna dwell */ #define B43_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */ #define B43_PHY_ENCORE B43_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */ #define B43_PHY_ENCORE_EN 0x0200 /* Encore enable */ #define B43_PHY_LMS B43_PHY_OFDM(0x55) #define B43_PHY_OFDM61 B43_PHY_OFDM(0x61) /* FIXME rename */ #define B43_PHY_OFDM61_10 0x0010 /* FIXME rename */ #define B43_PHY_IQBAL B43_PHY_OFDM(0x69) /* I/Q balance */ #define B43_PHY_BBTXDC_BIAS B43_PHY_OFDM(0x6B) /* Baseband TX DC bias */ #define B43_PHY_OTABLECTL B43_PHY_OFDM(0x72) /* OFDM table control (see below) */ #define B43_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */ #define B43_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */ #define B43_PHY_OTABLENR_SHIFT 10 #define B43_PHY_OTABLEI B43_PHY_OFDM(0x73) /* OFDM table data I */ #define B43_PHY_OTABLEQ B43_PHY_OFDM(0x74) /* OFDM table data Q */ #define B43_PHY_HPWR_TSSICTL B43_PHY_OFDM(0x78) /* Hardware power TSSI control */ #define B43_PHY_ADCCTL B43_PHY_OFDM(0x7A) /* ADC control */ #define B43_PHY_IDLE_TSSI B43_PHY_OFDM(0x7B) #define B43_PHY_A_TEMP_SENSE B43_PHY_OFDM(0x7C) /* A PHY temperature sense */ #define B43_PHY_NRSSITHRES B43_PHY_OFDM(0x8A) /* NRSSI threshold */ #define B43_PHY_ANTWRSETT B43_PHY_OFDM(0x8C) /* Antenna WR settle */ #define B43_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */ #define B43_PHY_CLIPPWRDOWNT B43_PHY_OFDM(0x93) /* Clip powerdown threshold */ #define B43_PHY_OFDM9B B43_PHY_OFDM(0x9B) /* FIXME rename */ #define B43_PHY_N1P1GAIN B43_PHY_OFDM(0xA0) #define B43_PHY_P1P2GAIN B43_PHY_OFDM(0xA1) #define B43_PHY_N1N2GAIN B43_PHY_OFDM(0xA2) #define B43_PHY_CLIPTHRES B43_PHY_OFDM(0xA3) #define B43_PHY_CLIPN1P2THRES B43_PHY_OFDM(0xA4) #define B43_PHY_CCKSHIFTBITS_WA B43_PHY_OFDM(0xA5) /* CCK shiftbits workaround, FIXME rename */ #define B43_PHY_CCKSHIFTBITS B43_PHY_OFDM(0xA7) /* FIXME rename */ #define B43_PHY_DIVSRCHIDX B43_PHY_OFDM(0xA8) /* Divider search gain/index */ #define B43_PHY_CLIPP2THRES B43_PHY_OFDM(0xA9) #define B43_PHY_CLIPP3THRES B43_PHY_OFDM(0xAA) #define B43_PHY_DIVP1P2GAIN B43_PHY_OFDM(0xAB) #define B43_PHY_DIVSRCHGAINBACK B43_PHY_OFDM(0xAD) /* Divider search gain back */ #define B43_PHY_DIVSRCHGAINCHNG B43_PHY_OFDM(0xAE) /* Divider search gain change */ #define B43_PHY_CRSTHRES1 B43_PHY_OFDM(0xC0) /* CRS Threshold 1 (phy.rev >= 2 only) */ #define B43_PHY_CRSTHRES2 B43_PHY_OFDM(0xC1) /* CRS Threshold 2 (phy.rev >= 2 only) */ #define B43_PHY_TSSIP_LTBASE B43_PHY_OFDM(0x380) /* TSSI power lookup table base */ #define B43_PHY_DC_LTBASE B43_PHY_OFDM(0x3A0) /* DC lookup table base */ #define B43_PHY_GAIN_LTBASE B43_PHY_OFDM(0x3C0) /* Gain lookup table base */ /*** OFDM table numbers ***/ #define B43_OFDMTAB(number, offset) (((number) << B43_PHY_OTABLENR_SHIFT) | (offset)) #define B43_OFDMTAB_AGC1 B43_OFDMTAB(0x00, 0) #define B43_OFDMTAB_GAIN0 B43_OFDMTAB(0x00, 0) #define B43_OFDMTAB_GAINX B43_OFDMTAB(0x01, 0) //TODO rename #define B43_OFDMTAB_GAIN1 B43_OFDMTAB(0x01, 4) #define B43_OFDMTAB_AGC3 B43_OFDMTAB(0x02, 0) #define B43_OFDMTAB_GAIN2 B43_OFDMTAB(0x02, 3) #define B43_OFDMTAB_LNAHPFGAIN1 B43_OFDMTAB(0x03, 0) #define B43_OFDMTAB_WRSSI B43_OFDMTAB(0x04, 0) #define B43_OFDMTAB_LNAHPFGAIN2 B43_OFDMTAB(0x04, 0) #define B43_OFDMTAB_NOISESCALE B43_OFDMTAB(0x05, 0) #define B43_OFDMTAB_AGC2 B43_OFDMTAB(0x06, 0) #define B43_OFDMTAB_ROTOR B43_OFDMTAB(0x08, 0) #define B43_OFDMTAB_ADVRETARD B43_OFDMTAB(0x09, 0) #define B43_OFDMTAB_DAC B43_OFDMTAB(0x0C, 0) #define B43_OFDMTAB_DC B43_OFDMTAB(0x0E, 7) #define B43_OFDMTAB_PWRDYN2 B43_OFDMTAB(0x0E, 12) #define B43_OFDMTAB_LNAGAIN B43_OFDMTAB(0x0E, 13) #define B43_OFDMTAB_UNKNOWN_0F B43_OFDMTAB(0x0F, 0) //TODO rename #define B43_OFDMTAB_UNKNOWN_APHY B43_OFDMTAB(0x0F, 7) //TODO rename #define B43_OFDMTAB_LPFGAIN B43_OFDMTAB(0x0F, 12) #define B43_OFDMTAB_RSSI B43_OFDMTAB(0x10, 0) #define B43_OFDMTAB_UNKNOWN_11 B43_OFDMTAB(0x11, 4) //TODO rename #define B43_OFDMTAB_AGC1_R1 B43_OFDMTAB(0x13, 0) #define B43_OFDMTAB_GAINX_R1 B43_OFDMTAB(0x14, 0) //TODO remove! #define B43_OFDMTAB_MINSIGSQ B43_OFDMTAB(0x14, 0) #define B43_OFDMTAB_AGC3_R1 B43_OFDMTAB(0x15, 0) #define B43_OFDMTAB_WRSSI_R1 B43_OFDMTAB(0x15, 4) #define B43_OFDMTAB_TSSI B43_OFDMTAB(0x15, 0) #define B43_OFDMTAB_DACRFPABB B43_OFDMTAB(0x16, 0) #define B43_OFDMTAB_DACOFF B43_OFDMTAB(0x17, 0) #define B43_OFDMTAB_DCBIAS B43_OFDMTAB(0x18, 0) u16 b43_ofdmtab_read16(struct b43_wldev *dev, u16 table, u16 offset); void b43_ofdmtab_write16(struct b43_wldev *dev, u16 table, u16 offset, u16 value); u32 b43_ofdmtab_read32(struct b43_wldev *dev, u16 table, u16 offset); void b43_ofdmtab_write32(struct b43_wldev *dev, u16 table, u16 offset, u32 value); struct b43_phy_a { /* Pointer to the table used to convert a * TSSI value to dBm-Q5.2 */ const s8 *tssi2dbm; /* Target idle TSSI */ int tgt_idle_tssi; /* Current idle TSSI */ int cur_idle_tssi;//FIXME value currently not set /* A-PHY TX Power control value. */ u16 txpwr_offset; //TODO lots of missing stuff }; /** * b43_phy_inita - Lowlevel A-PHY init routine. * This is _only_ used by the G-PHY code. */ void b43_phy_inita(struct b43_wldev *dev); struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_a; #endif /* LINUX_B43_PHY_A_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/phy_a.c0000644000175000017500000003776312026211315022335 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver IEEE 802.11a PHY driver Copyright (c) 2005 Martin Langer , Copyright (c) 2005-2007 Stefano Brivio Copyright (c) 2005-2008 Michael Buesch Copyright (c) 2005, 2006 Danny van Dyk Copyright (c) 2005, 2006 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "b43.h" #include "phy_a.h" #include "phy_common.h" #include "wa.h" #include "tables.h" #include "main.h" /* Get the freq, as it has to be written to the device. */ static inline u16 channel2freq_a(u8 channel) { B43_WARN_ON(channel > 200); return (5000 + 5 * channel); } static inline u16 freq_r3A_value(u16 frequency) { u16 value; if (frequency < 5091) value = 0x0040; else if (frequency < 5321) value = 0x0000; else if (frequency < 5806) value = 0x0080; else value = 0x0040; return value; } #if 0 /* This function converts a TSSI value to dBm in Q5.2 */ static s8 b43_aphy_estimate_power_out(struct b43_wldev *dev, s8 tssi) { struct b43_phy *phy = &dev->phy; struct b43_phy_a *aphy = phy->a; s8 dbm = 0; s32 tmp; tmp = (aphy->tgt_idle_tssi - aphy->cur_idle_tssi + tssi); tmp += 0x80; tmp = clamp_val(tmp, 0x00, 0xFF); dbm = aphy->tssi2dbm[tmp]; //TODO: There's a FIXME on the specs return dbm; } #endif static void b43_radio_set_tx_iq(struct b43_wldev *dev) { static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; u16 tmp = b43_radio_read16(dev, 0x001E); int i, j; for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) { if (tmp == (data_high[i] << 4 | data_low[j])) { b43_phy_write(dev, 0x0069, (i - j) << 8 | 0x00C0); return; } } } } static void aphy_channel_switch(struct b43_wldev *dev, unsigned int channel) { u16 freq, r8, tmp; freq = channel2freq_a(channel); r8 = b43_radio_read16(dev, 0x0008); b43_write16(dev, 0x03F0, freq); b43_radio_write16(dev, 0x0008, r8); //TODO: write max channel TX power? to Radio 0x2D tmp = b43_radio_read16(dev, 0x002E); tmp &= 0x0080; //TODO: OR tmp with the Power out estimation for this channel? b43_radio_write16(dev, 0x002E, tmp); if (freq >= 4920 && freq <= 5500) { /* * r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F; * = (freq * 0.025862069 */ r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */ } b43_radio_write16(dev, 0x0007, (r8 << 4) | r8); b43_radio_write16(dev, 0x0020, (r8 << 4) | r8); b43_radio_write16(dev, 0x0021, (r8 << 4) | r8); b43_radio_maskset(dev, 0x0022, 0x000F, (r8 << 4)); b43_radio_write16(dev, 0x002A, (r8 << 4)); b43_radio_write16(dev, 0x002B, (r8 << 4)); b43_radio_maskset(dev, 0x0008, 0x00F0, (r8 << 4)); b43_radio_maskset(dev, 0x0029, 0xFF0F, 0x00B0); b43_radio_write16(dev, 0x0035, 0x00AA); b43_radio_write16(dev, 0x0036, 0x0085); b43_radio_maskset(dev, 0x003A, 0xFF20, freq_r3A_value(freq)); b43_radio_mask(dev, 0x003D, 0x00FF); b43_radio_maskset(dev, 0x0081, 0xFF7F, 0x0080); b43_radio_mask(dev, 0x0035, 0xFFEF); b43_radio_maskset(dev, 0x0035, 0xFFEF, 0x0010); b43_radio_set_tx_iq(dev); //TODO: TSSI2dbm workaround //FIXME b43_phy_xmitpower(dev); } static void b43_radio_init2060(struct b43_wldev *dev) { b43_radio_write16(dev, 0x0004, 0x00C0); b43_radio_write16(dev, 0x0005, 0x0008); b43_radio_write16(dev, 0x0009, 0x0040); b43_radio_write16(dev, 0x0005, 0x00AA); b43_radio_write16(dev, 0x0032, 0x008F); b43_radio_write16(dev, 0x0006, 0x008F); b43_radio_write16(dev, 0x0034, 0x008F); b43_radio_write16(dev, 0x002C, 0x0007); b43_radio_write16(dev, 0x0082, 0x0080); b43_radio_write16(dev, 0x0080, 0x0000); b43_radio_write16(dev, 0x003F, 0x00DA); b43_radio_mask(dev, 0x0005, ~0x0008); b43_radio_mask(dev, 0x0081, ~0x0010); b43_radio_mask(dev, 0x0081, ~0x0020); b43_radio_mask(dev, 0x0081, ~0x0020); msleep(1); /* delay 400usec */ b43_radio_maskset(dev, 0x0081, ~0x0020, 0x0010); msleep(1); /* delay 400usec */ b43_radio_maskset(dev, 0x0005, ~0x0008, 0x0008); b43_radio_mask(dev, 0x0085, ~0x0010); b43_radio_mask(dev, 0x0005, ~0x0008); b43_radio_mask(dev, 0x0081, ~0x0040); b43_radio_maskset(dev, 0x0081, ~0x0040, 0x0040); b43_radio_write16(dev, 0x0005, (b43_radio_read16(dev, 0x0081) & ~0x0008) | 0x0008); b43_phy_write(dev, 0x0063, 0xDDC6); b43_phy_write(dev, 0x0069, 0x07BE); b43_phy_write(dev, 0x006A, 0x0000); aphy_channel_switch(dev, dev->phy.ops->get_default_chan(dev)); msleep(1); } static void b43_phy_rssiagc(struct b43_wldev *dev, u8 enable) { int i; if (dev->phy.rev < 3) { if (enable) for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) { b43_ofdmtab_write16(dev, B43_OFDMTAB_LNAHPFGAIN1, i, 0xFFF8); b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI, i, 0xFFF8); } else for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) { b43_ofdmtab_write16(dev, B43_OFDMTAB_LNAHPFGAIN1, i, b43_tab_rssiagc1[i]); b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI, i, b43_tab_rssiagc1[i]); } } else { if (enable) for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI, i, 0x0820); else for (i = 0; i < B43_TAB_RSSIAGC2_SIZE; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI, i, b43_tab_rssiagc2[i]); } } static void b43_phy_ww(struct b43_wldev *dev) { u16 b, curr_s, best_s = 0xFFFF; int i; b43_phy_mask(dev, B43_PHY_CRS0, ~B43_PHY_CRS0_EN); b43_phy_set(dev, B43_PHY_OFDM(0x1B), 0x1000); b43_phy_maskset(dev, B43_PHY_OFDM(0x82), 0xF0FF, 0x0300); b43_radio_set(dev, 0x0009, 0x0080); b43_radio_maskset(dev, 0x0012, 0xFFFC, 0x0002); b43_wa_initgains(dev); b43_phy_write(dev, B43_PHY_OFDM(0xBA), 0x3ED5); b = b43_phy_read(dev, B43_PHY_PWRDOWN); b43_phy_write(dev, B43_PHY_PWRDOWN, (b & 0xFFF8) | 0x0005); b43_radio_set(dev, 0x0004, 0x0004); for (i = 0x10; i <= 0x20; i++) { b43_radio_write16(dev, 0x0013, i); curr_s = b43_phy_read(dev, B43_PHY_OTABLEQ) & 0x00FF; if (!curr_s) { best_s = 0x0000; break; } else if (curr_s >= 0x0080) curr_s = 0x0100 - curr_s; if (curr_s < best_s) best_s = curr_s; } b43_phy_write(dev, B43_PHY_PWRDOWN, b); b43_radio_mask(dev, 0x0004, 0xFFFB); b43_radio_write16(dev, 0x0013, best_s); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 0, 0xFFEC); b43_phy_write(dev, B43_PHY_OFDM(0xB7), 0x1E80); b43_phy_write(dev, B43_PHY_OFDM(0xB6), 0x1C00); b43_phy_write(dev, B43_PHY_OFDM(0xB5), 0x0EC0); b43_phy_write(dev, B43_PHY_OFDM(0xB2), 0x00C0); b43_phy_write(dev, B43_PHY_OFDM(0xB9), 0x1FFF); b43_phy_maskset(dev, B43_PHY_OFDM(0xBB), 0xF000, 0x0053); b43_phy_maskset(dev, B43_PHY_OFDM61, 0xFE1F, 0x0120); b43_phy_maskset(dev, B43_PHY_OFDM(0x13), 0x0FFF, 0x3000); b43_phy_maskset(dev, B43_PHY_OFDM(0x14), 0x0FFF, 0x3000); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 6, 0x0017); for (i = 0; i < 6; i++) b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, i, 0x000F); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0D, 0x000E); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0E, 0x0011); b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0F, 0x0013); b43_phy_write(dev, B43_PHY_OFDM(0x33), 0x5030); b43_phy_set(dev, B43_PHY_CRS0, B43_PHY_CRS0_EN); } static void hardware_pctl_init_aphy(struct b43_wldev *dev) { //TODO } void b43_phy_inita(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; /* This lowlevel A-PHY init is also called from G-PHY init. * So we must not access phy->a, if called from G-PHY code. */ B43_WARN_ON((phy->type != B43_PHYTYPE_A) && (phy->type != B43_PHYTYPE_G)); might_sleep(); if (phy->rev >= 6) { if (phy->type == B43_PHYTYPE_A) b43_phy_mask(dev, B43_PHY_OFDM(0x1B), ~0x1000); if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN) b43_phy_set(dev, B43_PHY_ENCORE, 0x0010); else b43_phy_mask(dev, B43_PHY_ENCORE, ~0x1010); } b43_wa_all(dev); if (phy->type == B43_PHYTYPE_A) { if (phy->gmode && (phy->rev < 3)) b43_phy_set(dev, 0x0034, 0x0001); b43_phy_rssiagc(dev, 0); b43_phy_set(dev, B43_PHY_CRS0, B43_PHY_CRS0_EN); b43_radio_init2060(dev); if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && ((dev->dev->board_type == SSB_BOARD_BU4306) || (dev->dev->board_type == SSB_BOARD_BU4309))) { ; //TODO: A PHY LO } if (phy->rev >= 3) b43_phy_ww(dev); hardware_pctl_init_aphy(dev); //TODO: radar detection } if ((phy->type == B43_PHYTYPE_G) && (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL)) { b43_phy_maskset(dev, B43_PHY_OFDM(0x6E), 0xE000, 0x3CF); } } /* Initialise the TSSI->dBm lookup table */ static int b43_aphy_init_tssi2dbm_table(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_a *aphy = phy->a; s16 pab0, pab1, pab2; pab0 = (s16) (dev->dev->bus_sprom->pa1b0); pab1 = (s16) (dev->dev->bus_sprom->pa1b1); pab2 = (s16) (dev->dev->bus_sprom->pa1b2); if (pab0 != 0 && pab1 != 0 && pab2 != 0 && pab0 != -1 && pab1 != -1 && pab2 != -1) { /* The pabX values are set in SPROM. Use them. */ if ((s8) dev->dev->bus_sprom->itssi_a != 0 && (s8) dev->dev->bus_sprom->itssi_a != -1) aphy->tgt_idle_tssi = (s8) (dev->dev->bus_sprom->itssi_a); else aphy->tgt_idle_tssi = 62; aphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0, pab1, pab2); if (!aphy->tssi2dbm) return -ENOMEM; } else { /* pabX values not set in SPROM, * but APHY needs a generated table. */ aphy->tssi2dbm = NULL; b43err(dev->wl, "Could not generate tssi2dBm " "table (wrong SPROM info)!\n"); return -ENODEV; } return 0; } static int b43_aphy_op_allocate(struct b43_wldev *dev) { struct b43_phy_a *aphy; int err; aphy = kzalloc(sizeof(*aphy), GFP_KERNEL); if (!aphy) return -ENOMEM; dev->phy.a = aphy; err = b43_aphy_init_tssi2dbm_table(dev); if (err) goto err_free_aphy; return 0; err_free_aphy: kfree(aphy); dev->phy.a = NULL; return err; } static void b43_aphy_op_prepare_structs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_a *aphy = phy->a; const void *tssi2dbm; int tgt_idle_tssi; /* tssi2dbm table is constant, so it is initialized at alloc time. * Save a copy of the pointer. */ tssi2dbm = aphy->tssi2dbm; tgt_idle_tssi = aphy->tgt_idle_tssi; /* Zero out the whole PHY structure. */ memset(aphy, 0, sizeof(*aphy)); aphy->tssi2dbm = tssi2dbm; aphy->tgt_idle_tssi = tgt_idle_tssi; //TODO init struct b43_phy_a } static void b43_aphy_op_free(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_a *aphy = phy->a; kfree(aphy->tssi2dbm); aphy->tssi2dbm = NULL; kfree(aphy); dev->phy.a = NULL; } static int b43_aphy_op_init(struct b43_wldev *dev) { b43_phy_inita(dev); return 0; } static inline u16 adjust_phyreg(struct b43_wldev *dev, u16 offset) { /* OFDM registers are base-registers for the A-PHY. */ if ((offset & B43_PHYROUTE) == B43_PHYROUTE_OFDM_GPHY) { offset &= ~B43_PHYROUTE; offset |= B43_PHYROUTE_BASE; } #if B43_DEBUG if ((offset & B43_PHYROUTE) == B43_PHYROUTE_EXT_GPHY) { /* Ext-G registers are only available on G-PHYs */ b43err(dev->wl, "Invalid EXT-G PHY access at " "0x%04X on A-PHY\n", offset); dump_stack(); } if ((offset & B43_PHYROUTE) == B43_PHYROUTE_N_BMODE) { /* N-BMODE registers are only available on N-PHYs */ b43err(dev->wl, "Invalid N-BMODE PHY access at " "0x%04X on A-PHY\n", offset); dump_stack(); } #endif /* B43_DEBUG */ return offset; } static u16 b43_aphy_op_read(struct b43_wldev *dev, u16 reg) { reg = adjust_phyreg(dev, reg); b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_aphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) { reg = adjust_phyreg(dev, reg); b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } static u16 b43_aphy_op_radio_read(struct b43_wldev *dev, u16 reg) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); /* A-PHY needs 0x40 for read access */ reg |= 0x40; b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); } static void b43_aphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } static bool b43_aphy_op_supports_hwpctl(struct b43_wldev *dev) { return (dev->phy.rev >= 5); } static void b43_aphy_op_software_rfkill(struct b43_wldev *dev, bool blocked) { struct b43_phy *phy = &dev->phy; if (!blocked) { if (phy->radio_on) return; b43_radio_write16(dev, 0x0004, 0x00C0); b43_radio_write16(dev, 0x0005, 0x0008); b43_phy_mask(dev, 0x0010, 0xFFF7); b43_phy_mask(dev, 0x0011, 0xFFF7); b43_radio_init2060(dev); } else { b43_radio_write16(dev, 0x0004, 0x00FF); b43_radio_write16(dev, 0x0005, 0x00FB); b43_phy_set(dev, 0x0010, 0x0008); b43_phy_set(dev, 0x0011, 0x0008); } } static int b43_aphy_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { if (new_channel > 200) return -EINVAL; aphy_channel_switch(dev, new_channel); return 0; } static unsigned int b43_aphy_op_get_default_chan(struct b43_wldev *dev) { return 36; /* Default to channel 36 */ } static void b43_aphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) {//TODO struct b43_phy *phy = &dev->phy; u16 tmp; int autodiv = 0; if (antenna == B43_ANTENNA_AUTO0 || antenna == B43_ANTENNA_AUTO1) autodiv = 1; b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); b43_phy_maskset(dev, B43_PHY_BBANDCFG, ~B43_PHY_BBANDCFG_RXANT, (autodiv ? B43_ANTENNA_AUTO1 : antenna) << B43_PHY_BBANDCFG_RXANT_SHIFT); if (autodiv) { tmp = b43_phy_read(dev, B43_PHY_ANTDWELL); if (antenna == B43_ANTENNA_AUTO1) tmp &= ~B43_PHY_ANTDWELL_AUTODIV1; else tmp |= B43_PHY_ANTDWELL_AUTODIV1; b43_phy_write(dev, B43_PHY_ANTDWELL, tmp); } if (phy->rev < 3) b43_phy_maskset(dev, B43_PHY_ANTDWELL, 0xFF00, 0x24); else { b43_phy_set(dev, B43_PHY_OFDM61, 0x10); if (phy->rev == 3) { b43_phy_write(dev, B43_PHY_CLIPPWRDOWNT, 0x1D); b43_phy_write(dev, B43_PHY_ADIVRELATED, 8); } else { b43_phy_write(dev, B43_PHY_CLIPPWRDOWNT, 0x3A); b43_phy_maskset(dev, B43_PHY_ADIVRELATED, 0xFF00, 8); } } b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); } static void b43_aphy_op_adjust_txpower(struct b43_wldev *dev) {//TODO } static enum b43_txpwr_result b43_aphy_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) {//TODO return B43_TXPWR_RES_DONE; } static void b43_aphy_op_pwork_15sec(struct b43_wldev *dev) {//TODO } static void b43_aphy_op_pwork_60sec(struct b43_wldev *dev) {//TODO } const struct b43_phy_operations b43_phyops_a = { .allocate = b43_aphy_op_allocate, .free = b43_aphy_op_free, .prepare_structs = b43_aphy_op_prepare_structs, .init = b43_aphy_op_init, .phy_read = b43_aphy_op_read, .phy_write = b43_aphy_op_write, .radio_read = b43_aphy_op_radio_read, .radio_write = b43_aphy_op_radio_write, .supports_hwpctl = b43_aphy_op_supports_hwpctl, .software_rfkill = b43_aphy_op_software_rfkill, .switch_analog = b43_phyop_switch_analog_generic, .switch_channel = b43_aphy_op_switch_channel, .get_default_chan = b43_aphy_op_get_default_chan, .set_rx_antenna = b43_aphy_op_set_rx_antenna, .recalc_txpower = b43_aphy_op_recalc_txpower, .adjust_txpower = b43_aphy_op_adjust_txpower, .pwork_15sec = b43_aphy_op_pwork_15sec, .pwork_60sec = b43_aphy_op_pwork_60sec, }; compat-drivers-2012-09-18/drivers/net/wireless/b43/pcmcia.h0000644000175000017500000000047512026211315022464 0ustar mcgrofmcgrof#ifndef B43_PCMCIA_H_ #define B43_PCMCIA_H_ #ifdef CONFIG_B43_PCMCIA int b43_pcmcia_init(void); void b43_pcmcia_exit(void); #else /* CONFIG_B43_PCMCIA */ static inline int b43_pcmcia_init(void) { return 0; } static inline void b43_pcmcia_exit(void) { } #endif /* CONFIG_B43_PCMCIA */ #endif /* B43_PCMCIA_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/main.h0000644000175000017500000001047012026211315022150 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver Copyright (c) 2005 Martin Langer , Stefano Brivio Michael Buesch Danny van Dyk Andreas Jaggi Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef B43_MAIN_H_ #define B43_MAIN_H_ #include "b43.h" #define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] #define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) /* Magic helper macro to pad structures. Ignore those above. It's magic. */ #define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes)) extern int b43_modparam_verbose; /* Logmessage verbosity levels. Update the b43_modparam_verbose helptext, if * you add or remove levels. */ enum b43_verbosity { B43_VERBOSITY_ERROR, B43_VERBOSITY_WARN, B43_VERBOSITY_INFO, B43_VERBOSITY_DEBUG, __B43_VERBOSITY_AFTERLAST, /* keep last */ B43_VERBOSITY_MAX = __B43_VERBOSITY_AFTERLAST - 1, #if B43_DEBUG B43_VERBOSITY_DEFAULT = B43_VERBOSITY_DEBUG, #else B43_VERBOSITY_DEFAULT = B43_VERBOSITY_INFO, #endif }; /* Lightweight function to convert a frequency (in Mhz) to a channel number. */ static inline u8 b43_freq_to_channel_5ghz(int freq) { return ((freq - 5000) / 5); } static inline u8 b43_freq_to_channel_2ghz(int freq) { u8 channel; if (freq == 2484) channel = 14; else channel = (freq - 2407) / 5; return channel; } /* Lightweight function to convert a channel number to a frequency (in Mhz). */ static inline int b43_channel_to_freq_5ghz(u8 channel) { return (5000 + (5 * channel)); } static inline int b43_channel_to_freq_2ghz(u8 channel) { int freq; if (channel == 14) freq = 2484; else freq = 2407 + (5 * channel); return freq; } static inline int b43_is_cck_rate(int rate) { return (rate == B43_CCK_RATE_1MB || rate == B43_CCK_RATE_2MB || rate == B43_CCK_RATE_5MB || rate == B43_CCK_RATE_11MB); } static inline int b43_is_ofdm_rate(int rate) { return !b43_is_cck_rate(rate); } u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev, u8 antenna_nr); void b43_tsf_read(struct b43_wldev *dev, u64 * tsf); void b43_tsf_write(struct b43_wldev *dev, u64 tsf); u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset); u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset); void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value); void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value); u64 b43_hf_read(struct b43_wldev *dev); void b43_hf_write(struct b43_wldev *dev, u64 value); void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on); void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode); void b43_controller_restart(struct b43_wldev *dev, const char *reason); #define B43_PS_ENABLED (1 << 0) /* Force enable hardware power saving */ #define B43_PS_DISABLED (1 << 1) /* Force disable hardware power saving */ #define B43_PS_AWAKE (1 << 2) /* Force device awake */ #define B43_PS_ASLEEP (1 << 3) /* Force device asleep */ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags); void b43_mac_suspend(struct b43_wldev *dev); void b43_mac_enable(struct b43_wldev *dev); void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); struct b43_request_fw_context; int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, struct b43_firmware_file *fw); void b43_do_release_fw(struct b43_firmware_file *fw); #endif /* B43_MAIN_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/lo.h0000644000175000017500000000472212026211315021641 0ustar mcgrofmcgrof#ifndef B43_LO_H_ #define B43_LO_H_ /* G-PHY Local Oscillator */ #include "phy_g.h" struct b43_wldev; /* Local Oscillator control value-pair. */ struct b43_loctl { /* Control values. */ s8 i; s8 q; }; /* Debugging: Poison value for i and q values. */ #define B43_LOCTL_POISON 111 /* This struct holds calibrated LO settings for a set of * Baseband and RF attenuation settings. */ struct b43_lo_calib { /* The set of attenuation values this set of LO * control values is calibrated for. */ struct b43_bbatt bbatt; struct b43_rfatt rfatt; /* The set of control values for the LO. */ struct b43_loctl ctl; /* The time when these settings were calibrated (in jiffies) */ unsigned long calib_time; /* List. */ struct list_head list; }; /* Size of the DC Lookup Table in 16bit words. */ #define B43_DC_LT_SIZE 32 /* Local Oscillator calibration information */ struct b43_txpower_lo_control { /* Lists of RF and BB attenuation values for this device. * Used for building hardware power control tables. */ struct b43_rfatt_list rfatt_list; struct b43_bbatt_list bbatt_list; /* The DC Lookup Table is cached in memory here. * Note that this is only used for Hardware Power Control. */ u16 dc_lt[B43_DC_LT_SIZE]; /* List of calibrated control values (struct b43_lo_calib). */ struct list_head calib_list; /* Last time the power vector was read (jiffies). */ unsigned long pwr_vec_read_time; /* Last time the txctl values were measured (jiffies). */ unsigned long txctl_measured_time; /* Current TX Bias value */ u8 tx_bias; /* Current TX Magnification Value (if used by the device) */ u8 tx_magn; /* Saved device PowerVector */ u64 power_vector; }; /* Calibration expire timeouts. * Timeouts must be multiple of 15 seconds. To make sure * the item really expired when the 15 second timer hits, we * subtract two additional seconds from the timeout. */ #define B43_LO_CALIB_EXPIRE (HZ * (30 - 2)) #define B43_LO_PWRVEC_EXPIRE (HZ * (30 - 2)) #define B43_LO_TXCTL_EXPIRE (HZ * (180 - 4)) /* Adjust the Local Oscillator to the saved attenuation * and txctl values. */ void b43_lo_g_adjust(struct b43_wldev *dev); /* Adjust to specific values. */ void b43_lo_g_adjust_to(struct b43_wldev *dev, u16 rfatt, u16 bbatt, u16 tx_control); void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all); void b43_lo_g_maintanance_work(struct b43_wldev *dev); void b43_lo_g_cleanup(struct b43_wldev *dev); void b43_lo_g_init(struct b43_wldev *dev); #endif /* B43_LO_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/lo.c0000644000175000017500000006551612026211315021644 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver G PHY LO (LocalOscillator) Measuring and Control routines Copyright (c) 2005 Martin Langer , Copyright (c) 2005, 2006 Stefano Brivio Copyright (c) 2005-2007 Michael Buesch Copyright (c) 2005, 2006 Danny van Dyk Copyright (c) 2005, 2006 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "lo.h" #include "phy_g.h" #include "main.h" #include #include #include static struct b43_lo_calib *b43_find_lo_calib(struct b43_txpower_lo_control *lo, const struct b43_bbatt *bbatt, const struct b43_rfatt *rfatt) { struct b43_lo_calib *c; list_for_each_entry(c, &lo->calib_list, list) { if (!b43_compare_bbatt(&c->bbatt, bbatt)) continue; if (!b43_compare_rfatt(&c->rfatt, rfatt)) continue; return c; } return NULL; } /* Write the LocalOscillator Control (adjust) value-pair. */ static void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control) { struct b43_phy *phy = &dev->phy; u16 value; if (B43_DEBUG) { if (unlikely(abs(control->i) > 16 || abs(control->q) > 16)) { b43dbg(dev->wl, "Invalid LO control pair " "(I: %d, Q: %d)\n", control->i, control->q); dump_stack(); return; } } B43_WARN_ON(phy->type != B43_PHYTYPE_G); value = (u8) (control->q); value |= ((u8) (control->i)) << 8; b43_phy_write(dev, B43_PHY_LO_CTL, value); } static u16 lo_measure_feedthrough(struct b43_wldev *dev, u16 lna, u16 pga, u16 trsw_rx) { struct b43_phy *phy = &dev->phy; u16 rfover; u16 feedthrough; if (phy->gmode) { lna <<= B43_PHY_RFOVERVAL_LNA_SHIFT; pga <<= B43_PHY_RFOVERVAL_PGA_SHIFT; B43_WARN_ON(lna & ~B43_PHY_RFOVERVAL_LNA); B43_WARN_ON(pga & ~B43_PHY_RFOVERVAL_PGA); /*FIXME This assertion fails B43_WARN_ON(trsw_rx & ~(B43_PHY_RFOVERVAL_TRSWRX | B43_PHY_RFOVERVAL_BW)); */ trsw_rx &= (B43_PHY_RFOVERVAL_TRSWRX | B43_PHY_RFOVERVAL_BW); /* Construct the RF Override Value */ rfover = B43_PHY_RFOVERVAL_UNK; rfover |= pga; rfover |= lna; rfover |= trsw_rx; if ((dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA) && phy->rev > 6) rfover |= B43_PHY_RFOVERVAL_EXTLNA; b43_phy_write(dev, B43_PHY_PGACTL, 0xE300); b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); udelay(10); rfover |= B43_PHY_RFOVERVAL_BW_LBW; b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); udelay(10); rfover |= B43_PHY_RFOVERVAL_BW_LPF; b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); udelay(10); b43_phy_write(dev, B43_PHY_PGACTL, 0xF300); } else { pga |= B43_PHY_PGACTL_UNKNOWN; b43_phy_write(dev, B43_PHY_PGACTL, pga); udelay(10); pga |= B43_PHY_PGACTL_LOWBANDW; b43_phy_write(dev, B43_PHY_PGACTL, pga); udelay(10); pga |= B43_PHY_PGACTL_LPF; b43_phy_write(dev, B43_PHY_PGACTL, pga); } udelay(21); feedthrough = b43_phy_read(dev, B43_PHY_LO_LEAKAGE); /* This is a good place to check if we need to relax a bit, * as this is the main function called regularly * in the LO calibration. */ cond_resched(); return feedthrough; } /* TXCTL Register and Value Table. * Returns the "TXCTL Register". * "value" is the "TXCTL Value". * "pad_mix_gain" is the PAD Mixer Gain. */ static u16 lo_txctl_register_table(struct b43_wldev *dev, u16 *value, u16 *pad_mix_gain) { struct b43_phy *phy = &dev->phy; u16 reg, v, padmix; if (phy->type == B43_PHYTYPE_B) { v = 0x30; if (phy->radio_rev <= 5) { reg = 0x43; padmix = 0; } else { reg = 0x52; padmix = 5; } } else { if (phy->rev >= 2 && phy->radio_rev == 8) { reg = 0x43; v = 0x10; padmix = 2; } else { reg = 0x52; v = 0x30; padmix = 5; } } if (value) *value = v; if (pad_mix_gain) *pad_mix_gain = padmix; return reg; } static void lo_measure_txctl_values(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; u16 reg, mask; u16 trsw_rx, pga; u16 radio_pctl_reg; static const u8 tx_bias_values[] = { 0x09, 0x08, 0x0A, 0x01, 0x00, 0x02, 0x05, 0x04, 0x06, }; static const u8 tx_magn_values[] = { 0x70, 0x40, }; if (!has_loopback_gain(phy)) { radio_pctl_reg = 6; trsw_rx = 2; pga = 0; } else { int lb_gain; /* Loopback gain (in dB) */ trsw_rx = 0; lb_gain = gphy->max_lb_gain / 2; if (lb_gain > 10) { radio_pctl_reg = 0; pga = abs(10 - lb_gain) / 6; pga = clamp_val(pga, 0, 15); } else { int cmp_val; int tmp; pga = 0; cmp_val = 0x24; if ((phy->rev >= 2) && (phy->radio_ver == 0x2050) && (phy->radio_rev == 8)) cmp_val = 0x3C; tmp = lb_gain; if ((10 - lb_gain) < cmp_val) tmp = (10 - lb_gain); if (tmp < 0) tmp += 6; else tmp += 3; cmp_val /= 4; tmp /= 4; if (tmp >= cmp_val) radio_pctl_reg = cmp_val; else radio_pctl_reg = tmp; } } b43_radio_maskset(dev, 0x43, 0xFFF0, radio_pctl_reg); b43_gphy_set_baseband_attenuation(dev, 2); reg = lo_txctl_register_table(dev, &mask, NULL); mask = ~mask; b43_radio_mask(dev, reg, mask); if (has_tx_magnification(phy)) { int i, j; int feedthrough; int min_feedth = 0xFFFF; u8 tx_magn, tx_bias; for (i = 0; i < ARRAY_SIZE(tx_magn_values); i++) { tx_magn = tx_magn_values[i]; b43_radio_maskset(dev, 0x52, 0xFF0F, tx_magn); for (j = 0; j < ARRAY_SIZE(tx_bias_values); j++) { tx_bias = tx_bias_values[j]; b43_radio_maskset(dev, 0x52, 0xFFF0, tx_bias); feedthrough = lo_measure_feedthrough(dev, 0, pga, trsw_rx); if (feedthrough < min_feedth) { lo->tx_bias = tx_bias; lo->tx_magn = tx_magn; min_feedth = feedthrough; } if (lo->tx_bias == 0) break; } b43_radio_write16(dev, 0x52, (b43_radio_read16(dev, 0x52) & 0xFF00) | lo->tx_bias | lo-> tx_magn); } } else { lo->tx_magn = 0; lo->tx_bias = 0; b43_radio_mask(dev, 0x52, 0xFFF0); /* TX bias == 0 */ } lo->txctl_measured_time = jiffies; } static void lo_read_power_vector(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; int i; u64 tmp; u64 power_vector = 0; for (i = 0; i < 8; i += 2) { tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x310 + i); power_vector |= (tmp << (i * 8)); /* Clear the vector on the device. */ b43_shm_write16(dev, B43_SHM_SHARED, 0x310 + i, 0); } if (power_vector) lo->power_vector = power_vector; lo->pwr_vec_read_time = jiffies; } /* 802.11/LO/GPHY/MeasuringGains */ static void lo_measure_gain_values(struct b43_wldev *dev, s16 max_rx_gain, int use_trsw_rx) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 tmp; if (max_rx_gain < 0) max_rx_gain = 0; if (has_loopback_gain(phy)) { int trsw_rx_gain; if (use_trsw_rx) { trsw_rx_gain = gphy->trsw_rx_gain / 2; if (max_rx_gain >= trsw_rx_gain) { trsw_rx_gain = max_rx_gain - trsw_rx_gain; } } else trsw_rx_gain = max_rx_gain; if (trsw_rx_gain < 9) { gphy->lna_lod_gain = 0; } else { gphy->lna_lod_gain = 1; trsw_rx_gain -= 8; } trsw_rx_gain = clamp_val(trsw_rx_gain, 0, 0x2D); gphy->pga_gain = trsw_rx_gain / 3; if (gphy->pga_gain >= 5) { gphy->pga_gain -= 5; gphy->lna_gain = 2; } else gphy->lna_gain = 0; } else { gphy->lna_gain = 0; gphy->trsw_rx_gain = 0x20; if (max_rx_gain >= 0x14) { gphy->lna_lod_gain = 1; gphy->pga_gain = 2; } else if (max_rx_gain >= 0x12) { gphy->lna_lod_gain = 1; gphy->pga_gain = 1; } else if (max_rx_gain >= 0xF) { gphy->lna_lod_gain = 1; gphy->pga_gain = 0; } else { gphy->lna_lod_gain = 0; gphy->pga_gain = 0; } } tmp = b43_radio_read16(dev, 0x7A); if (gphy->lna_lod_gain == 0) tmp &= ~0x0008; else tmp |= 0x0008; b43_radio_write16(dev, 0x7A, tmp); } struct lo_g_saved_values { u8 old_channel; /* Core registers */ u16 reg_3F4; u16 reg_3E2; /* PHY registers */ u16 phy_lo_mask; u16 phy_extg_01; u16 phy_dacctl_hwpctl; u16 phy_dacctl; u16 phy_cck_14; u16 phy_hpwr_tssictl; u16 phy_analogover; u16 phy_analogoverval; u16 phy_rfover; u16 phy_rfoverval; u16 phy_classctl; u16 phy_cck_3E; u16 phy_crs0; u16 phy_pgactl; u16 phy_cck_2A; u16 phy_syncctl; u16 phy_cck_30; u16 phy_cck_06; /* Radio registers */ u16 radio_43; u16 radio_7A; u16 radio_52; }; static void lo_measure_setup(struct b43_wldev *dev, struct lo_g_saved_values *sav) { struct ssb_sprom *sprom = dev->dev->bus_sprom; struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; u16 tmp; if (b43_has_hardware_pctl(dev)) { sav->phy_lo_mask = b43_phy_read(dev, B43_PHY_LO_MASK); sav->phy_extg_01 = b43_phy_read(dev, B43_PHY_EXTG(0x01)); sav->phy_dacctl_hwpctl = b43_phy_read(dev, B43_PHY_DACCTL); sav->phy_cck_14 = b43_phy_read(dev, B43_PHY_CCK(0x14)); sav->phy_hpwr_tssictl = b43_phy_read(dev, B43_PHY_HPWR_TSSICTL); b43_phy_set(dev, B43_PHY_HPWR_TSSICTL, 0x100); b43_phy_set(dev, B43_PHY_EXTG(0x01), 0x40); b43_phy_set(dev, B43_PHY_DACCTL, 0x40); b43_phy_set(dev, B43_PHY_CCK(0x14), 0x200); } if (phy->type == B43_PHYTYPE_B && phy->radio_ver == 0x2050 && phy->radio_rev < 6) { b43_phy_write(dev, B43_PHY_CCK(0x16), 0x410); b43_phy_write(dev, B43_PHY_CCK(0x17), 0x820); } if (phy->rev >= 2) { sav->phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER); sav->phy_analogoverval = b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); sav->phy_rfover = b43_phy_read(dev, B43_PHY_RFOVER); sav->phy_rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); sav->phy_classctl = b43_phy_read(dev, B43_PHY_CLASSCTL); sav->phy_cck_3E = b43_phy_read(dev, B43_PHY_CCK(0x3E)); sav->phy_crs0 = b43_phy_read(dev, B43_PHY_CRS0); b43_phy_mask(dev, B43_PHY_CLASSCTL, 0xFFFC); b43_phy_mask(dev, B43_PHY_CRS0, 0x7FFF); b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0003); b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFC); if (phy->type == B43_PHYTYPE_G) { if ((phy->rev >= 7) && (sprom->boardflags_lo & B43_BFL_EXTLNA)) { b43_phy_write(dev, B43_PHY_RFOVER, 0x933); } else { b43_phy_write(dev, B43_PHY_RFOVER, 0x133); } } else { b43_phy_write(dev, B43_PHY_RFOVER, 0); } b43_phy_write(dev, B43_PHY_CCK(0x3E), 0); } sav->reg_3F4 = b43_read16(dev, 0x3F4); sav->reg_3E2 = b43_read16(dev, 0x3E2); sav->radio_43 = b43_radio_read16(dev, 0x43); sav->radio_7A = b43_radio_read16(dev, 0x7A); sav->phy_pgactl = b43_phy_read(dev, B43_PHY_PGACTL); sav->phy_cck_2A = b43_phy_read(dev, B43_PHY_CCK(0x2A)); sav->phy_syncctl = b43_phy_read(dev, B43_PHY_SYNCCTL); sav->phy_dacctl = b43_phy_read(dev, B43_PHY_DACCTL); if (!has_tx_magnification(phy)) { sav->radio_52 = b43_radio_read16(dev, 0x52); sav->radio_52 &= 0x00F0; } if (phy->type == B43_PHYTYPE_B) { sav->phy_cck_30 = b43_phy_read(dev, B43_PHY_CCK(0x30)); sav->phy_cck_06 = b43_phy_read(dev, B43_PHY_CCK(0x06)); b43_phy_write(dev, B43_PHY_CCK(0x30), 0x00FF); b43_phy_write(dev, B43_PHY_CCK(0x06), 0x3F3F); } else { b43_write16(dev, 0x3E2, b43_read16(dev, 0x3E2) | 0x8000); } b43_write16(dev, 0x3F4, b43_read16(dev, 0x3F4) & 0xF000); tmp = (phy->type == B43_PHYTYPE_G) ? B43_PHY_LO_MASK : B43_PHY_CCK(0x2E); b43_phy_write(dev, tmp, 0x007F); tmp = sav->phy_syncctl; b43_phy_write(dev, B43_PHY_SYNCCTL, tmp & 0xFF7F); tmp = sav->radio_7A; b43_radio_write16(dev, 0x007A, tmp & 0xFFF0); b43_phy_write(dev, B43_PHY_CCK(0x2A), 0x8A3); if (phy->type == B43_PHYTYPE_G || (phy->type == B43_PHYTYPE_B && phy->radio_ver == 0x2050 && phy->radio_rev >= 6)) { b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x1003); } else b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x0802); if (phy->rev >= 2) b43_dummy_transmission(dev, false, true); b43_gphy_channel_switch(dev, 6, 0); b43_radio_read16(dev, 0x51); /* dummy read */ if (phy->type == B43_PHYTYPE_G) b43_phy_write(dev, B43_PHY_CCK(0x2F), 0); /* Re-measure the txctl values, if needed. */ if (time_before(lo->txctl_measured_time, jiffies - B43_LO_TXCTL_EXPIRE)) lo_measure_txctl_values(dev); if (phy->type == B43_PHYTYPE_G && phy->rev >= 3) { b43_phy_write(dev, B43_PHY_LO_MASK, 0xC078); } else { if (phy->type == B43_PHYTYPE_B) b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078); else b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078); } } static void lo_measure_restore(struct b43_wldev *dev, struct lo_g_saved_values *sav) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; u16 tmp; if (phy->rev >= 2) { b43_phy_write(dev, B43_PHY_PGACTL, 0xE300); tmp = (gphy->pga_gain << 8); b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA0); udelay(5); b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA2); udelay(2); b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA3); } else { tmp = (gphy->pga_gain | 0xEFA0); b43_phy_write(dev, B43_PHY_PGACTL, tmp); } if (phy->type == B43_PHYTYPE_G) { if (phy->rev >= 3) b43_phy_write(dev, B43_PHY_CCK(0x2E), 0xC078); else b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078); if (phy->rev >= 2) b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0202); else b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0101); } b43_write16(dev, 0x3F4, sav->reg_3F4); b43_phy_write(dev, B43_PHY_PGACTL, sav->phy_pgactl); b43_phy_write(dev, B43_PHY_CCK(0x2A), sav->phy_cck_2A); b43_phy_write(dev, B43_PHY_SYNCCTL, sav->phy_syncctl); b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl); b43_radio_write16(dev, 0x43, sav->radio_43); b43_radio_write16(dev, 0x7A, sav->radio_7A); if (!has_tx_magnification(phy)) { tmp = sav->radio_52; b43_radio_maskset(dev, 0x52, 0xFF0F, tmp); } b43_write16(dev, 0x3E2, sav->reg_3E2); if (phy->type == B43_PHYTYPE_B && phy->radio_ver == 0x2050 && phy->radio_rev <= 5) { b43_phy_write(dev, B43_PHY_CCK(0x30), sav->phy_cck_30); b43_phy_write(dev, B43_PHY_CCK(0x06), sav->phy_cck_06); } if (phy->rev >= 2) { b43_phy_write(dev, B43_PHY_ANALOGOVER, sav->phy_analogover); b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, sav->phy_analogoverval); b43_phy_write(dev, B43_PHY_CLASSCTL, sav->phy_classctl); b43_phy_write(dev, B43_PHY_RFOVER, sav->phy_rfover); b43_phy_write(dev, B43_PHY_RFOVERVAL, sav->phy_rfoverval); b43_phy_write(dev, B43_PHY_CCK(0x3E), sav->phy_cck_3E); b43_phy_write(dev, B43_PHY_CRS0, sav->phy_crs0); } if (b43_has_hardware_pctl(dev)) { tmp = (sav->phy_lo_mask & 0xBFFF); b43_phy_write(dev, B43_PHY_LO_MASK, tmp); b43_phy_write(dev, B43_PHY_EXTG(0x01), sav->phy_extg_01); b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl_hwpctl); b43_phy_write(dev, B43_PHY_CCK(0x14), sav->phy_cck_14); b43_phy_write(dev, B43_PHY_HPWR_TSSICTL, sav->phy_hpwr_tssictl); } b43_gphy_channel_switch(dev, sav->old_channel, 1); } struct b43_lo_g_statemachine { int current_state; int nr_measured; int state_val_multiplier; u16 lowest_feedth; struct b43_loctl min_loctl; }; /* Loop over each possible value in this state. */ static int lo_probe_possible_loctls(struct b43_wldev *dev, struct b43_loctl *probe_loctl, struct b43_lo_g_statemachine *d) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_loctl test_loctl; struct b43_loctl orig_loctl; struct b43_loctl prev_loctl = { .i = -100, .q = -100, }; int i; int begin, end; int found_lower = 0; u16 feedth; static const struct b43_loctl modifiers[] = { {.i = 1,.q = 1,}, {.i = 1,.q = 0,}, {.i = 1,.q = -1,}, {.i = 0,.q = -1,}, {.i = -1,.q = -1,}, {.i = -1,.q = 0,}, {.i = -1,.q = 1,}, {.i = 0,.q = 1,}, }; if (d->current_state == 0) { begin = 1; end = 8; } else if (d->current_state % 2 == 0) { begin = d->current_state - 1; end = d->current_state + 1; } else { begin = d->current_state - 2; end = d->current_state + 2; } if (begin < 1) begin += 8; if (end > 8) end -= 8; memcpy(&orig_loctl, probe_loctl, sizeof(struct b43_loctl)); i = begin; d->current_state = i; while (1) { B43_WARN_ON(!(i >= 1 && i <= 8)); memcpy(&test_loctl, &orig_loctl, sizeof(struct b43_loctl)); test_loctl.i += modifiers[i - 1].i * d->state_val_multiplier; test_loctl.q += modifiers[i - 1].q * d->state_val_multiplier; if ((test_loctl.i != prev_loctl.i || test_loctl.q != prev_loctl.q) && (abs(test_loctl.i) <= 16 && abs(test_loctl.q) <= 16)) { b43_lo_write(dev, &test_loctl); feedth = lo_measure_feedthrough(dev, gphy->lna_gain, gphy->pga_gain, gphy->trsw_rx_gain); if (feedth < d->lowest_feedth) { memcpy(probe_loctl, &test_loctl, sizeof(struct b43_loctl)); found_lower = 1; d->lowest_feedth = feedth; if ((d->nr_measured < 2) && !has_loopback_gain(phy)) break; } } memcpy(&prev_loctl, &test_loctl, sizeof(prev_loctl)); if (i == end) break; if (i == 8) i = 1; else i++; d->current_state = i; } return found_lower; } static void lo_probe_loctls_statemachine(struct b43_wldev *dev, struct b43_loctl *loctl, int *max_rx_gain) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_lo_g_statemachine d; u16 feedth; int found_lower; struct b43_loctl probe_loctl; int max_repeat = 1, repeat_cnt = 0; d.nr_measured = 0; d.state_val_multiplier = 1; if (has_loopback_gain(phy)) d.state_val_multiplier = 3; memcpy(&d.min_loctl, loctl, sizeof(struct b43_loctl)); if (has_loopback_gain(phy)) max_repeat = 4; do { b43_lo_write(dev, &d.min_loctl); feedth = lo_measure_feedthrough(dev, gphy->lna_gain, gphy->pga_gain, gphy->trsw_rx_gain); if (feedth < 0x258) { if (feedth >= 0x12C) *max_rx_gain += 6; else *max_rx_gain += 3; feedth = lo_measure_feedthrough(dev, gphy->lna_gain, gphy->pga_gain, gphy->trsw_rx_gain); } d.lowest_feedth = feedth; d.current_state = 0; do { B43_WARN_ON(! (d.current_state >= 0 && d.current_state <= 8)); memcpy(&probe_loctl, &d.min_loctl, sizeof(struct b43_loctl)); found_lower = lo_probe_possible_loctls(dev, &probe_loctl, &d); if (!found_lower) break; if ((probe_loctl.i == d.min_loctl.i) && (probe_loctl.q == d.min_loctl.q)) break; memcpy(&d.min_loctl, &probe_loctl, sizeof(struct b43_loctl)); d.nr_measured++; } while (d.nr_measured < 24); memcpy(loctl, &d.min_loctl, sizeof(struct b43_loctl)); if (has_loopback_gain(phy)) { if (d.lowest_feedth > 0x1194) *max_rx_gain -= 6; else if (d.lowest_feedth < 0x5DC) *max_rx_gain += 3; if (repeat_cnt == 0) { if (d.lowest_feedth <= 0x5DC) { d.state_val_multiplier = 1; repeat_cnt++; } else d.state_val_multiplier = 2; } else if (repeat_cnt == 2) d.state_val_multiplier = 1; } lo_measure_gain_values(dev, *max_rx_gain, has_loopback_gain(phy)); } while (++repeat_cnt < max_repeat); } static struct b43_lo_calib *b43_calibrate_lo_setting(struct b43_wldev *dev, const struct b43_bbatt *bbatt, const struct b43_rfatt *rfatt) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_loctl loctl = { .i = 0, .q = 0, }; int max_rx_gain; struct b43_lo_calib *cal; struct lo_g_saved_values uninitialized_var(saved_regs); /* Values from the "TXCTL Register and Value Table" */ u16 txctl_reg; u16 txctl_value; u16 pad_mix_gain; saved_regs.old_channel = phy->channel; b43_mac_suspend(dev); lo_measure_setup(dev, &saved_regs); txctl_reg = lo_txctl_register_table(dev, &txctl_value, &pad_mix_gain); b43_radio_maskset(dev, 0x43, 0xFFF0, rfatt->att); b43_radio_maskset(dev, txctl_reg, ~txctl_value, (rfatt->with_padmix ? txctl_value :0)); max_rx_gain = rfatt->att * 2; max_rx_gain += bbatt->att / 2; if (rfatt->with_padmix) max_rx_gain -= pad_mix_gain; if (has_loopback_gain(phy)) max_rx_gain += gphy->max_lb_gain; lo_measure_gain_values(dev, max_rx_gain, has_loopback_gain(phy)); b43_gphy_set_baseband_attenuation(dev, bbatt->att); lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain); lo_measure_restore(dev, &saved_regs); b43_mac_enable(dev); if (b43_debug(dev, B43_DBG_LO)) { b43dbg(dev->wl, "LO: Calibrated for BB(%u), RF(%u,%u) " "=> I=%d Q=%d\n", bbatt->att, rfatt->att, rfatt->with_padmix, loctl.i, loctl.q); } cal = kmalloc(sizeof(*cal), GFP_KERNEL); if (!cal) { b43warn(dev->wl, "LO calib: out of memory\n"); return NULL; } memcpy(&cal->bbatt, bbatt, sizeof(*bbatt)); memcpy(&cal->rfatt, rfatt, sizeof(*rfatt)); memcpy(&cal->ctl, &loctl, sizeof(loctl)); cal->calib_time = jiffies; INIT_LIST_HEAD(&cal->list); return cal; } /* Get a calibrated LO setting for the given attenuation values. * Might return a NULL pointer under OOM! */ static struct b43_lo_calib *b43_get_calib_lo_settings(struct b43_wldev *dev, const struct b43_bbatt *bbatt, const struct b43_rfatt *rfatt) { struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; struct b43_lo_calib *c; c = b43_find_lo_calib(lo, bbatt, rfatt); if (c) return c; /* Not in the list of calibrated LO settings. * Calibrate it now. */ c = b43_calibrate_lo_setting(dev, bbatt, rfatt); if (!c) return NULL; list_add(&c->list, &lo->calib_list); return c; } void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; int i; int rf_offset, bb_offset; const struct b43_rfatt *rfatt; const struct b43_bbatt *bbatt; u64 power_vector; bool table_changed = false; BUILD_BUG_ON(B43_DC_LT_SIZE != 32); B43_WARN_ON(lo->rfatt_list.len * lo->bbatt_list.len > 64); power_vector = lo->power_vector; if (!update_all && !power_vector) return; /* Nothing to do. */ /* Suspend the MAC now to avoid continuous suspend/enable * cycles in the loop. */ b43_mac_suspend(dev); for (i = 0; i < B43_DC_LT_SIZE * 2; i++) { struct b43_lo_calib *cal; int idx; u16 val; if (!update_all && !(power_vector & (((u64)1ULL) << i))) continue; /* Update the table entry for this power_vector bit. * The table rows are RFatt entries and columns are BBatt. */ bb_offset = i / lo->rfatt_list.len; rf_offset = i % lo->rfatt_list.len; bbatt = &(lo->bbatt_list.list[bb_offset]); rfatt = &(lo->rfatt_list.list[rf_offset]); cal = b43_calibrate_lo_setting(dev, bbatt, rfatt); if (!cal) { b43warn(dev->wl, "LO: Could not " "calibrate DC table entry\n"); continue; } /*FIXME: Is Q really in the low nibble? */ val = (u8)(cal->ctl.q); val |= ((u8)(cal->ctl.i)) << 4; kfree(cal); /* Get the index into the hardware DC LT. */ idx = i / 2; /* Change the table in memory. */ if (i % 2) { /* Change the high byte. */ lo->dc_lt[idx] = (lo->dc_lt[idx] & 0x00FF) | ((val & 0x00FF) << 8); } else { /* Change the low byte. */ lo->dc_lt[idx] = (lo->dc_lt[idx] & 0xFF00) | (val & 0x00FF); } table_changed = true; } if (table_changed) { /* The table changed in memory. Update the hardware table. */ for (i = 0; i < B43_DC_LT_SIZE; i++) b43_phy_write(dev, 0x3A0 + i, lo->dc_lt[i]); } b43_mac_enable(dev); } /* Fixup the RF attenuation value for the case where we are * using the PAD mixer. */ static inline void b43_lo_fixup_rfatt(struct b43_rfatt *rf) { if (!rf->with_padmix) return; if ((rf->att != 1) && (rf->att != 2) && (rf->att != 3)) rf->att = 4; } void b43_lo_g_adjust(struct b43_wldev *dev) { struct b43_phy_g *gphy = dev->phy.g; struct b43_lo_calib *cal; struct b43_rfatt rf; memcpy(&rf, &gphy->rfatt, sizeof(rf)); b43_lo_fixup_rfatt(&rf); cal = b43_get_calib_lo_settings(dev, &gphy->bbatt, &rf); if (!cal) return; b43_lo_write(dev, &cal->ctl); } void b43_lo_g_adjust_to(struct b43_wldev *dev, u16 rfatt, u16 bbatt, u16 tx_control) { struct b43_rfatt rf; struct b43_bbatt bb; struct b43_lo_calib *cal; memset(&rf, 0, sizeof(rf)); memset(&bb, 0, sizeof(bb)); rf.att = rfatt; bb.att = bbatt; b43_lo_fixup_rfatt(&rf); cal = b43_get_calib_lo_settings(dev, &bb, &rf); if (!cal) return; b43_lo_write(dev, &cal->ctl); } /* Periodic LO maintanance work */ void b43_lo_g_maintanance_work(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; struct b43_txpower_lo_control *lo = gphy->lo_control; unsigned long now; unsigned long expire; struct b43_lo_calib *cal, *tmp; bool current_item_expired = false; bool hwpctl; if (!lo) return; now = jiffies; hwpctl = b43_has_hardware_pctl(dev); if (hwpctl) { /* Read the power vector and update it, if needed. */ expire = now - B43_LO_PWRVEC_EXPIRE; if (time_before(lo->pwr_vec_read_time, expire)) { lo_read_power_vector(dev); b43_gphy_dc_lt_init(dev, 0); } //FIXME Recalc the whole DC table from time to time? } if (hwpctl) return; /* Search for expired LO settings. Remove them. * Recalibrate the current setting, if expired. */ expire = now - B43_LO_CALIB_EXPIRE; list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) { if (!time_before(cal->calib_time, expire)) continue; /* This item expired. */ if (b43_compare_bbatt(&cal->bbatt, &gphy->bbatt) && b43_compare_rfatt(&cal->rfatt, &gphy->rfatt)) { B43_WARN_ON(current_item_expired); current_item_expired = true; } if (b43_debug(dev, B43_DBG_LO)) { b43dbg(dev->wl, "LO: Item BB(%u), RF(%u,%u), " "I=%d, Q=%d expired\n", cal->bbatt.att, cal->rfatt.att, cal->rfatt.with_padmix, cal->ctl.i, cal->ctl.q); } list_del(&cal->list); kfree(cal); } if (current_item_expired || unlikely(list_empty(&lo->calib_list))) { /* Recalibrate currently used LO setting. */ if (b43_debug(dev, B43_DBG_LO)) b43dbg(dev->wl, "LO: Recalibrating current LO setting\n"); cal = b43_calibrate_lo_setting(dev, &gphy->bbatt, &gphy->rfatt); if (cal) { list_add(&cal->list, &lo->calib_list); b43_lo_write(dev, &cal->ctl); } else b43warn(dev->wl, "Failed to recalibrate current LO setting\n"); } } void b43_lo_g_cleanup(struct b43_wldev *dev) { struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; struct b43_lo_calib *cal, *tmp; if (!lo) return; list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) { list_del(&cal->list); kfree(cal); } } /* LO Initialization */ void b43_lo_g_init(struct b43_wldev *dev) { if (b43_has_hardware_pctl(dev)) { lo_read_power_vector(dev); b43_gphy_dc_lt_init(dev, 1); } } compat-drivers-2012-09-18/drivers/net/wireless/b43/leds.h0000644000175000017500000000353212026211315022154 0ustar mcgrofmcgrof#ifndef B43_LEDS_H_ #define B43_LEDS_H_ struct b43_wl; struct b43_wldev; #ifdef CONFIG_B43_LEDS #include #include #include #define B43_LED_MAX_NAME_LEN 31 struct b43_led { struct b43_wl *wl; /* The LED class device */ struct led_classdev led_dev; /* The index number of the LED. */ u8 index; /* If activelow is true, the LED is ON if the * bit is switched off. */ bool activelow; /* The unique name string for this LED device. */ char name[B43_LED_MAX_NAME_LEN + 1]; /* The current status of the LED. This is updated locklessly. */ atomic_t state; /* The active state in hardware. */ bool hw_state; }; struct b43_leds { struct b43_led led_tx; struct b43_led led_rx; struct b43_led led_radio; struct b43_led led_assoc; bool stop; struct work_struct work; }; #define B43_MAX_NR_LEDS 4 #define B43_LED_BEHAVIOUR 0x7F #define B43_LED_ACTIVELOW 0x80 /* LED behaviour values */ enum b43_led_behaviour { B43_LED_OFF, B43_LED_ON, B43_LED_ACTIVITY, B43_LED_RADIO_ALL, B43_LED_RADIO_A, B43_LED_RADIO_B, B43_LED_MODE_BG, B43_LED_TRANSFER, B43_LED_APTRANSFER, B43_LED_WEIRD, //FIXME B43_LED_ASSOC, B43_LED_INACTIVE, }; void b43_leds_register(struct b43_wldev *dev); void b43_leds_unregister(struct b43_wl *wl); void b43_leds_init(struct b43_wldev *dev); void b43_leds_exit(struct b43_wldev *dev); void b43_leds_stop(struct b43_wldev *dev); #else /* CONFIG_B43_LEDS */ /* LED support disabled */ struct b43_leds { /* empty */ }; static inline void b43_leds_register(struct b43_wldev *dev) { } static inline void b43_leds_unregister(struct b43_wl *wl) { } static inline void b43_leds_init(struct b43_wldev *dev) { } static inline void b43_leds_exit(struct b43_wldev *dev) { } static inline void b43_leds_stop(struct b43_wldev *dev) { } #endif /* CONFIG_B43_LEDS */ #endif /* B43_LEDS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/leds.c0000644000175000017500000002206712026211315022153 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver LED control Copyright (c) 2005 Martin Langer , Copyright (c) 2005 Stefano Brivio Copyright (c) 2005-2007 Michael Buesch Copyright (c) 2005 Danny van Dyk Copyright (c) 2005 Andreas Jaggi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "leds.h" #include "rfkill.h" static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, bool activelow) { u16 ctl; ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl &= ~(1 << led_index); else ctl |= (1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); } static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, bool activelow) { u16 ctl; ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl |= (1 << led_index); else ctl &= ~(1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); } static void b43_led_update(struct b43_wldev *dev, struct b43_led *led) { bool radio_enabled; bool turn_on; if (!led->wl) return; radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); /* The led->state read is racy, but we don't care. In case we raced * with the brightness_set handler, we will be called again soon * to fixup our state. */ if (radio_enabled) turn_on = atomic_read(&led->state) != LED_OFF; else turn_on = false; if (turn_on == led->hw_state) return; led->hw_state = turn_on; if (turn_on) b43_led_turn_on(dev, led->index, led->activelow); else b43_led_turn_off(dev, led->index, led->activelow); } static void b43_leds_work(struct work_struct *work) { struct b43_leds *leds = container_of(work, struct b43_leds, work); struct b43_wl *wl = container_of(leds, struct b43_wl, leds); struct b43_wldev *dev; mutex_lock(&wl->mutex); dev = wl->current_dev; if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) goto out_unlock; b43_led_update(dev, &wl->leds.led_tx); b43_led_update(dev, &wl->leds.led_rx); b43_led_update(dev, &wl->leds.led_radio); b43_led_update(dev, &wl->leds.led_assoc); out_unlock: mutex_unlock(&wl->mutex); } /* Callback from the LED subsystem. */ static void b43_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); struct b43_wl *wl = led->wl; if (likely(!wl->leds.stop)) { atomic_set(&led->state, brightness); ieee80211_queue_work(wl->hw, &wl->leds.work); } } static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, const char *name, const char *default_trigger, u8 led_index, bool activelow) { int err; if (led->wl) return -EEXIST; if (!default_trigger) return -EINVAL; led->wl = dev->wl; led->index = led_index; led->activelow = activelow; strncpy(led->name, name, sizeof(led->name)); atomic_set(&led->state, 0); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; led->led_dev.brightness_set = b43_led_brightness_set; err = led_classdev_register(dev->dev->dev, &led->led_dev); if (err) { b43warn(dev->wl, "LEDs: Failed to register %s\n", name); led->wl = NULL; return err; } return 0; } static void b43_unregister_led(struct b43_led *led) { if (!led->wl) return; led_classdev_unregister(&led->led_dev); led->wl = NULL; } static void b43_map_led(struct b43_wldev *dev, u8 led_index, enum b43_led_behaviour behaviour, bool activelow) { struct ieee80211_hw *hw = dev->wl->hw; char name[B43_LED_MAX_NAME_LEN + 1]; /* Map the b43 specific LED behaviour value to the * generic LED triggers. */ switch (behaviour) { case B43_LED_INACTIVE: case B43_LED_OFF: case B43_LED_ON: break; case B43_LED_ACTIVITY: case B43_LED_TRANSFER: case B43_LED_APTRANSFER: snprintf(name, sizeof(name), "b43-%s::tx", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->wl->leds.led_tx, name, ieee80211_get_tx_led_name(hw), led_index, activelow); snprintf(name, sizeof(name), "b43-%s::rx", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->wl->leds.led_rx, name, ieee80211_get_rx_led_name(hw), led_index, activelow); break; case B43_LED_RADIO_ALL: case B43_LED_RADIO_A: case B43_LED_RADIO_B: case B43_LED_MODE_BG: snprintf(name, sizeof(name), "b43-%s::radio", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->wl->leds.led_radio, name, ieee80211_get_radio_led_name(hw), led_index, activelow); break; case B43_LED_WEIRD: case B43_LED_ASSOC: snprintf(name, sizeof(name), "b43-%s::assoc", wiphy_name(hw->wiphy)); b43_register_led(dev, &dev->wl->leds.led_assoc, name, ieee80211_get_assoc_led_name(hw), led_index, activelow); break; default: b43warn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n", behaviour); break; } } static void b43_led_get_sprominfo(struct b43_wldev *dev, unsigned int led_index, enum b43_led_behaviour *behaviour, bool *activelow) { u8 sprom[4]; sprom[0] = dev->dev->bus_sprom->gpio0; sprom[1] = dev->dev->bus_sprom->gpio1; sprom[2] = dev->dev->bus_sprom->gpio2; sprom[3] = dev->dev->bus_sprom->gpio3; if (sprom[led_index] == 0xFF) { /* There is no LED information in the SPROM * for this LED. Hardcode it here. */ *activelow = false; switch (led_index) { case 0: *behaviour = B43_LED_ACTIVITY; *activelow = true; if (dev->dev->board_vendor == PCI_VENDOR_ID_COMPAQ) *behaviour = B43_LED_RADIO_ALL; break; case 1: *behaviour = B43_LED_RADIO_B; if (dev->dev->board_vendor == PCI_VENDOR_ID_ASUSTEK) *behaviour = B43_LED_ASSOC; break; case 2: *behaviour = B43_LED_RADIO_A; break; case 3: *behaviour = B43_LED_OFF; break; default: *behaviour = B43_LED_OFF; B43_WARN_ON(1); return; } } else { *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR; *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW); } } void b43_leds_init(struct b43_wldev *dev) { struct b43_led *led; unsigned int i; enum b43_led_behaviour behaviour; bool activelow; /* Sync the RF-kill LED state (if we have one) with radio and switch states. */ led = &dev->wl->leds.led_radio; if (led->wl) { if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) { b43_led_turn_on(dev, led->index, led->activelow); led->hw_state = true; atomic_set(&led->state, 1); } else { b43_led_turn_off(dev, led->index, led->activelow); led->hw_state = false; atomic_set(&led->state, 0); } } /* Initialize TX/RX/ASSOC leds */ led = &dev->wl->leds.led_tx; if (led->wl) { b43_led_turn_off(dev, led->index, led->activelow); led->hw_state = false; atomic_set(&led->state, 0); } led = &dev->wl->leds.led_rx; if (led->wl) { b43_led_turn_off(dev, led->index, led->activelow); led->hw_state = false; atomic_set(&led->state, 0); } led = &dev->wl->leds.led_assoc; if (led->wl) { b43_led_turn_off(dev, led->index, led->activelow); led->hw_state = false; atomic_set(&led->state, 0); } /* Initialize other LED states. */ for (i = 0; i < B43_MAX_NR_LEDS; i++) { b43_led_get_sprominfo(dev, i, &behaviour, &activelow); switch (behaviour) { case B43_LED_OFF: b43_led_turn_off(dev, i, activelow); break; case B43_LED_ON: b43_led_turn_on(dev, i, activelow); break; default: /* Leave others as-is. */ break; } } dev->wl->leds.stop = 0; } void b43_leds_exit(struct b43_wldev *dev) { struct b43_leds *leds = &dev->wl->leds; b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow); b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow); b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow); b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow); } void b43_leds_stop(struct b43_wldev *dev) { struct b43_leds *leds = &dev->wl->leds; leds->stop = 1; cancel_work_sync(&leds->work); } void b43_leds_register(struct b43_wldev *dev) { unsigned int i; enum b43_led_behaviour behaviour; bool activelow; INIT_WORK(&dev->wl->leds.work, b43_leds_work); /* Register the LEDs to the LED subsystem. */ for (i = 0; i < B43_MAX_NR_LEDS; i++) { b43_led_get_sprominfo(dev, i, &behaviour, &activelow); b43_map_led(dev, i, behaviour, activelow); } } void b43_leds_unregister(struct b43_wl *wl) { struct b43_leds *leds = &wl->leds; b43_unregister_led(&leds->led_tx); b43_unregister_led(&leds->led_rx); b43_unregister_led(&leds->led_assoc); b43_unregister_led(&leds->led_radio); } compat-drivers-2012-09-18/drivers/net/wireless/b43/dma.h0000644000175000017500000002335112026211315021767 0ustar mcgrofmcgrof#ifndef B43_DMA_H_ #define B43_DMA_H_ #include #include "b43.h" /* DMA-Interrupt reasons. */ #define B43_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ | (1 << 14) | (1 << 15)) #define B43_DMAIRQ_NONFATALMASK (1 << 13) #define B43_DMAIRQ_RX_DONE (1 << 16) /*** 32-bit DMA Engine. ***/ /* 32-bit DMA controller registers. */ #define B43_DMA32_TXCTL 0x00 #define B43_DMA32_TXENABLE 0x00000001 #define B43_DMA32_TXSUSPEND 0x00000002 #define B43_DMA32_TXLOOPBACK 0x00000004 #define B43_DMA32_TXFLUSH 0x00000010 #define B43_DMA32_TXPARITYDISABLE 0x00000800 #define B43_DMA32_TXADDREXT_MASK 0x00030000 #define B43_DMA32_TXADDREXT_SHIFT 16 #define B43_DMA32_TXRING 0x04 #define B43_DMA32_TXINDEX 0x08 #define B43_DMA32_TXSTATUS 0x0C #define B43_DMA32_TXDPTR 0x00000FFF #define B43_DMA32_TXSTATE 0x0000F000 #define B43_DMA32_TXSTAT_DISABLED 0x00000000 #define B43_DMA32_TXSTAT_ACTIVE 0x00001000 #define B43_DMA32_TXSTAT_IDLEWAIT 0x00002000 #define B43_DMA32_TXSTAT_STOPPED 0x00003000 #define B43_DMA32_TXSTAT_SUSP 0x00004000 #define B43_DMA32_TXERROR 0x000F0000 #define B43_DMA32_TXERR_NOERR 0x00000000 #define B43_DMA32_TXERR_PROT 0x00010000 #define B43_DMA32_TXERR_UNDERRUN 0x00020000 #define B43_DMA32_TXERR_BUFREAD 0x00030000 #define B43_DMA32_TXERR_DESCREAD 0x00040000 #define B43_DMA32_TXACTIVE 0xFFF00000 #define B43_DMA32_RXCTL 0x10 #define B43_DMA32_RXENABLE 0x00000001 #define B43_DMA32_RXFROFF_MASK 0x000000FE #define B43_DMA32_RXFROFF_SHIFT 1 #define B43_DMA32_RXDIRECTFIFO 0x00000100 #define B43_DMA32_RXPARITYDISABLE 0x00000800 #define B43_DMA32_RXADDREXT_MASK 0x00030000 #define B43_DMA32_RXADDREXT_SHIFT 16 #define B43_DMA32_RXRING 0x14 #define B43_DMA32_RXINDEX 0x18 #define B43_DMA32_RXSTATUS 0x1C #define B43_DMA32_RXDPTR 0x00000FFF #define B43_DMA32_RXSTATE 0x0000F000 #define B43_DMA32_RXSTAT_DISABLED 0x00000000 #define B43_DMA32_RXSTAT_ACTIVE 0x00001000 #define B43_DMA32_RXSTAT_IDLEWAIT 0x00002000 #define B43_DMA32_RXSTAT_STOPPED 0x00003000 #define B43_DMA32_RXERROR 0x000F0000 #define B43_DMA32_RXERR_NOERR 0x00000000 #define B43_DMA32_RXERR_PROT 0x00010000 #define B43_DMA32_RXERR_OVERFLOW 0x00020000 #define B43_DMA32_RXERR_BUFWRITE 0x00030000 #define B43_DMA32_RXERR_DESCREAD 0x00040000 #define B43_DMA32_RXACTIVE 0xFFF00000 /* 32-bit DMA descriptor. */ struct b43_dmadesc32 { __le32 control; __le32 address; } __packed; #define B43_DMA32_DCTL_BYTECNT 0x00001FFF #define B43_DMA32_DCTL_ADDREXT_MASK 0x00030000 #define B43_DMA32_DCTL_ADDREXT_SHIFT 16 #define B43_DMA32_DCTL_DTABLEEND 0x10000000 #define B43_DMA32_DCTL_IRQ 0x20000000 #define B43_DMA32_DCTL_FRAMEEND 0x40000000 #define B43_DMA32_DCTL_FRAMESTART 0x80000000 /*** 64-bit DMA Engine. ***/ /* 64-bit DMA controller registers. */ #define B43_DMA64_TXCTL 0x00 #define B43_DMA64_TXENABLE 0x00000001 #define B43_DMA64_TXSUSPEND 0x00000002 #define B43_DMA64_TXLOOPBACK 0x00000004 #define B43_DMA64_TXFLUSH 0x00000010 #define B43_DMA64_TXPARITYDISABLE 0x00000800 #define B43_DMA64_TXADDREXT_MASK 0x00030000 #define B43_DMA64_TXADDREXT_SHIFT 16 #define B43_DMA64_TXINDEX 0x04 #define B43_DMA64_TXRINGLO 0x08 #define B43_DMA64_TXRINGHI 0x0C #define B43_DMA64_TXSTATUS 0x10 #define B43_DMA64_TXSTATDPTR 0x00001FFF #define B43_DMA64_TXSTAT 0xF0000000 #define B43_DMA64_TXSTAT_DISABLED 0x00000000 #define B43_DMA64_TXSTAT_ACTIVE 0x10000000 #define B43_DMA64_TXSTAT_IDLEWAIT 0x20000000 #define B43_DMA64_TXSTAT_STOPPED 0x30000000 #define B43_DMA64_TXSTAT_SUSP 0x40000000 #define B43_DMA64_TXERROR 0x14 #define B43_DMA64_TXERRDPTR 0x0001FFFF #define B43_DMA64_TXERR 0xF0000000 #define B43_DMA64_TXERR_NOERR 0x00000000 #define B43_DMA64_TXERR_PROT 0x10000000 #define B43_DMA64_TXERR_UNDERRUN 0x20000000 #define B43_DMA64_TXERR_TRANSFER 0x30000000 #define B43_DMA64_TXERR_DESCREAD 0x40000000 #define B43_DMA64_TXERR_CORE 0x50000000 #define B43_DMA64_RXCTL 0x20 #define B43_DMA64_RXENABLE 0x00000001 #define B43_DMA64_RXFROFF_MASK 0x000000FE #define B43_DMA64_RXFROFF_SHIFT 1 #define B43_DMA64_RXDIRECTFIFO 0x00000100 #define B43_DMA64_RXPARITYDISABLE 0x00000800 #define B43_DMA64_RXADDREXT_MASK 0x00030000 #define B43_DMA64_RXADDREXT_SHIFT 16 #define B43_DMA64_RXINDEX 0x24 #define B43_DMA64_RXRINGLO 0x28 #define B43_DMA64_RXRINGHI 0x2C #define B43_DMA64_RXSTATUS 0x30 #define B43_DMA64_RXSTATDPTR 0x00001FFF #define B43_DMA64_RXSTAT 0xF0000000 #define B43_DMA64_RXSTAT_DISABLED 0x00000000 #define B43_DMA64_RXSTAT_ACTIVE 0x10000000 #define B43_DMA64_RXSTAT_IDLEWAIT 0x20000000 #define B43_DMA64_RXSTAT_STOPPED 0x30000000 #define B43_DMA64_RXSTAT_SUSP 0x40000000 #define B43_DMA64_RXERROR 0x34 #define B43_DMA64_RXERRDPTR 0x0001FFFF #define B43_DMA64_RXERR 0xF0000000 #define B43_DMA64_RXERR_NOERR 0x00000000 #define B43_DMA64_RXERR_PROT 0x10000000 #define B43_DMA64_RXERR_UNDERRUN 0x20000000 #define B43_DMA64_RXERR_TRANSFER 0x30000000 #define B43_DMA64_RXERR_DESCREAD 0x40000000 #define B43_DMA64_RXERR_CORE 0x50000000 /* 64-bit DMA descriptor. */ struct b43_dmadesc64 { __le32 control0; __le32 control1; __le32 address_low; __le32 address_high; } __packed; #define B43_DMA64_DCTL0_DTABLEEND 0x10000000 #define B43_DMA64_DCTL0_IRQ 0x20000000 #define B43_DMA64_DCTL0_FRAMEEND 0x40000000 #define B43_DMA64_DCTL0_FRAMESTART 0x80000000 #define B43_DMA64_DCTL1_BYTECNT 0x00001FFF #define B43_DMA64_DCTL1_ADDREXT_MASK 0x00030000 #define B43_DMA64_DCTL1_ADDREXT_SHIFT 16 struct b43_dmadesc_generic { union { struct b43_dmadesc32 dma32; struct b43_dmadesc64 dma64; } __packed; } __packed; /* Misc DMA constants */ #define B43_DMA32_RINGMEMSIZE 4096 #define B43_DMA64_RINGMEMSIZE 8192 /* Offset of frame with actual data */ #define B43_DMA0_RX_FW598_FO 38 #define B43_DMA0_RX_FW351_FO 30 /* DMA engine tuning knobs */ #define B43_TXRING_SLOTS 256 #define B43_RXRING_SLOTS 64 #define B43_DMA0_RX_FW598_BUFSIZE (B43_DMA0_RX_FW598_FO + IEEE80211_MAX_FRAME_LEN) #define B43_DMA0_RX_FW351_BUFSIZE (B43_DMA0_RX_FW351_FO + IEEE80211_MAX_FRAME_LEN) /* Pointer poison */ #define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) #define b43_dma_ptr_is_poisoned(ptr) (unlikely((ptr) == B43_DMA_PTR_POISON)) struct sk_buff; struct b43_private; struct b43_txstatus; struct b43_dmadesc_meta { /* The kernel DMA-able buffer. */ struct sk_buff *skb; /* DMA base bus-address of the descriptor buffer. */ dma_addr_t dmaaddr; /* ieee80211 TX status. Only used once per 802.11 frag. */ bool is_last_fragment; }; struct b43_dmaring; /* Lowlevel DMA operations that differ between 32bit and 64bit DMA. */ struct b43_dma_ops { struct b43_dmadesc_generic *(*idx2desc) (struct b43_dmaring * ring, int slot, struct b43_dmadesc_meta ** meta); void (*fill_descriptor) (struct b43_dmaring * ring, struct b43_dmadesc_generic * desc, dma_addr_t dmaaddr, u16 bufsize, int start, int end, int irq); void (*poke_tx) (struct b43_dmaring * ring, int slot); void (*tx_suspend) (struct b43_dmaring * ring); void (*tx_resume) (struct b43_dmaring * ring); int (*get_current_rxslot) (struct b43_dmaring * ring); void (*set_current_rxslot) (struct b43_dmaring * ring, int slot); }; enum b43_dmatype { B43_DMA_30BIT = 30, B43_DMA_32BIT = 32, B43_DMA_64BIT = 64, }; enum b43_addrtype { B43_DMA_ADDR_LOW, B43_DMA_ADDR_HIGH, B43_DMA_ADDR_EXT, }; struct b43_dmaring { /* Lowlevel DMA ops. */ const struct b43_dma_ops *ops; /* Kernel virtual base address of the ring memory. */ void *descbase; /* Meta data about all descriptors. */ struct b43_dmadesc_meta *meta; /* Cache of TX headers for each TX frame. * This is to avoid an allocation on each TX. * This is NULL for an RX ring. */ u8 *txhdr_cache; /* (Unadjusted) DMA base bus-address of the ring memory. */ dma_addr_t dmabase; /* Number of descriptor slots in the ring. */ int nr_slots; /* Number of used descriptor slots. */ int used_slots; /* Currently used slot in the ring. */ int current_slot; /* Frameoffset in octets. */ u32 frameoffset; /* Descriptor buffer size. */ u16 rx_buffersize; /* The MMIO base register of the DMA controller. */ u16 mmio_base; /* DMA controller index number (0-5). */ int index; /* Boolean. Is this a TX ring? */ bool tx; /* The type of DMA engine used. */ enum b43_dmatype type; /* Boolean. Is this ring stopped at ieee80211 level? */ bool stopped; /* The QOS priority assigned to this ring. Only used for TX rings. * This is the mac80211 "queue" value. */ u8 queue_prio; struct b43_wldev *dev; #ifdef CONFIG_B43_DEBUG /* Maximum number of used slots. */ int max_used_slots; /* Last time we injected a ring overflow. */ unsigned long last_injected_overflow; /* Statistics: Number of successfully transmitted packets */ u64 nr_succeed_tx_packets; /* Statistics: Number of failed TX packets */ u64 nr_failed_tx_packets; /* Statistics: Total number of TX plus all retries. */ u64 nr_total_packet_tries; #endif /* CONFIG_B43_DEBUG */ }; static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset) { return b43_read32(ring->dev, ring->mmio_base + offset); } static inline void b43_dma_write(struct b43_dmaring *ring, u16 offset, u32 value) { b43_write32(ring->dev, ring->mmio_base + offset, value); } int b43_dma_init(struct b43_wldev *dev); void b43_dma_free(struct b43_wldev *dev); void b43_dma_tx_suspend(struct b43_wldev *dev); void b43_dma_tx_resume(struct b43_wldev *dev); int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb); void b43_dma_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status); void b43_dma_rx(struct b43_dmaring *ring); void b43_dma_direct_fifo_rx(struct b43_wldev *dev, unsigned int engine_index, bool enable); #endif /* B43_DMA_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/dma.c0000644000175000017500000013447712026211315021776 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver DMA ringbuffer and descriptor allocation/management Copyright (c) 2005, 2006 Michael Buesch Some code in this file is derived from the b44.c driver Copyright (C) 2002 David S. Miller Copyright (C) Pekka Pietikainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "dma.h" #include "main.h" #include "debugfs.h" #include "xmit.h" #include #include #include #include #include #include #include /* Required number of TX DMA slots per TX frame. * This currently is 2, because we put the header and the ieee80211 frame * into separate slots. */ #define TX_SLOTS_PER_FRAME 2 static u32 b43_dma_address(struct b43_dma *dma, dma_addr_t dmaaddr, enum b43_addrtype addrtype) { u32 uninitialized_var(addr); switch (addrtype) { case B43_DMA_ADDR_LOW: addr = lower_32_bits(dmaaddr); if (dma->translation_in_low) { addr &= ~SSB_DMA_TRANSLATION_MASK; addr |= dma->translation; } break; case B43_DMA_ADDR_HIGH: addr = upper_32_bits(dmaaddr); if (!dma->translation_in_low) { addr &= ~SSB_DMA_TRANSLATION_MASK; addr |= dma->translation; } break; case B43_DMA_ADDR_EXT: if (dma->translation_in_low) addr = lower_32_bits(dmaaddr); else addr = upper_32_bits(dmaaddr); addr &= SSB_DMA_TRANSLATION_MASK; addr >>= SSB_DMA_TRANSLATION_SHIFT; break; } return addr; } /* 32bit DMA ops. */ static struct b43_dmadesc_generic *op32_idx2desc(struct b43_dmaring *ring, int slot, struct b43_dmadesc_meta **meta) { struct b43_dmadesc32 *desc; *meta = &(ring->meta[slot]); desc = ring->descbase; desc = &(desc[slot]); return (struct b43_dmadesc_generic *)desc; } static void op32_fill_descriptor(struct b43_dmaring *ring, struct b43_dmadesc_generic *desc, dma_addr_t dmaaddr, u16 bufsize, int start, int end, int irq) { struct b43_dmadesc32 *descbase = ring->descbase; int slot; u32 ctl; u32 addr; u32 addrext; slot = (int)(&(desc->dma32) - descbase); B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); addr = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); ctl = bufsize & B43_DMA32_DCTL_BYTECNT; if (slot == ring->nr_slots - 1) ctl |= B43_DMA32_DCTL_DTABLEEND; if (start) ctl |= B43_DMA32_DCTL_FRAMESTART; if (end) ctl |= B43_DMA32_DCTL_FRAMEEND; if (irq) ctl |= B43_DMA32_DCTL_IRQ; ctl |= (addrext << B43_DMA32_DCTL_ADDREXT_SHIFT) & B43_DMA32_DCTL_ADDREXT_MASK; desc->dma32.control = cpu_to_le32(ctl); desc->dma32.address = cpu_to_le32(addr); } static void op32_poke_tx(struct b43_dmaring *ring, int slot) { b43_dma_write(ring, B43_DMA32_TXINDEX, (u32) (slot * sizeof(struct b43_dmadesc32))); } static void op32_tx_suspend(struct b43_dmaring *ring) { b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) | B43_DMA32_TXSUSPEND); } static void op32_tx_resume(struct b43_dmaring *ring) { b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) & ~B43_DMA32_TXSUSPEND); } static int op32_get_current_rxslot(struct b43_dmaring *ring) { u32 val; val = b43_dma_read(ring, B43_DMA32_RXSTATUS); val &= B43_DMA32_RXDPTR; return (val / sizeof(struct b43_dmadesc32)); } static void op32_set_current_rxslot(struct b43_dmaring *ring, int slot) { b43_dma_write(ring, B43_DMA32_RXINDEX, (u32) (slot * sizeof(struct b43_dmadesc32))); } static const struct b43_dma_ops dma32_ops = { .idx2desc = op32_idx2desc, .fill_descriptor = op32_fill_descriptor, .poke_tx = op32_poke_tx, .tx_suspend = op32_tx_suspend, .tx_resume = op32_tx_resume, .get_current_rxslot = op32_get_current_rxslot, .set_current_rxslot = op32_set_current_rxslot, }; /* 64bit DMA ops. */ static struct b43_dmadesc_generic *op64_idx2desc(struct b43_dmaring *ring, int slot, struct b43_dmadesc_meta **meta) { struct b43_dmadesc64 *desc; *meta = &(ring->meta[slot]); desc = ring->descbase; desc = &(desc[slot]); return (struct b43_dmadesc_generic *)desc; } static void op64_fill_descriptor(struct b43_dmaring *ring, struct b43_dmadesc_generic *desc, dma_addr_t dmaaddr, u16 bufsize, int start, int end, int irq) { struct b43_dmadesc64 *descbase = ring->descbase; int slot; u32 ctl0 = 0, ctl1 = 0; u32 addrlo, addrhi; u32 addrext; slot = (int)(&(desc->dma64) - descbase); B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); addrlo = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); addrhi = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_HIGH); addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); if (slot == ring->nr_slots - 1) ctl0 |= B43_DMA64_DCTL0_DTABLEEND; if (start) ctl0 |= B43_DMA64_DCTL0_FRAMESTART; if (end) ctl0 |= B43_DMA64_DCTL0_FRAMEEND; if (irq) ctl0 |= B43_DMA64_DCTL0_IRQ; ctl1 |= bufsize & B43_DMA64_DCTL1_BYTECNT; ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) & B43_DMA64_DCTL1_ADDREXT_MASK; desc->dma64.control0 = cpu_to_le32(ctl0); desc->dma64.control1 = cpu_to_le32(ctl1); desc->dma64.address_low = cpu_to_le32(addrlo); desc->dma64.address_high = cpu_to_le32(addrhi); } static void op64_poke_tx(struct b43_dmaring *ring, int slot) { b43_dma_write(ring, B43_DMA64_TXINDEX, (u32) (slot * sizeof(struct b43_dmadesc64))); } static void op64_tx_suspend(struct b43_dmaring *ring) { b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) | B43_DMA64_TXSUSPEND); } static void op64_tx_resume(struct b43_dmaring *ring) { b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) & ~B43_DMA64_TXSUSPEND); } static int op64_get_current_rxslot(struct b43_dmaring *ring) { u32 val; val = b43_dma_read(ring, B43_DMA64_RXSTATUS); val &= B43_DMA64_RXSTATDPTR; return (val / sizeof(struct b43_dmadesc64)); } static void op64_set_current_rxslot(struct b43_dmaring *ring, int slot) { b43_dma_write(ring, B43_DMA64_RXINDEX, (u32) (slot * sizeof(struct b43_dmadesc64))); } static const struct b43_dma_ops dma64_ops = { .idx2desc = op64_idx2desc, .fill_descriptor = op64_fill_descriptor, .poke_tx = op64_poke_tx, .tx_suspend = op64_tx_suspend, .tx_resume = op64_tx_resume, .get_current_rxslot = op64_get_current_rxslot, .set_current_rxslot = op64_set_current_rxslot, }; static inline int free_slots(struct b43_dmaring *ring) { return (ring->nr_slots - ring->used_slots); } static inline int next_slot(struct b43_dmaring *ring, int slot) { B43_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); if (slot == ring->nr_slots - 1) return 0; return slot + 1; } static inline int prev_slot(struct b43_dmaring *ring, int slot) { B43_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); if (slot == 0) return ring->nr_slots - 1; return slot - 1; } #ifdef CONFIG_B43_DEBUG static void update_max_used_slots(struct b43_dmaring *ring, int current_used_slots) { if (current_used_slots <= ring->max_used_slots) return; ring->max_used_slots = current_used_slots; if (b43_debug(ring->dev, B43_DBG_DMAVERBOSE)) { b43dbg(ring->dev->wl, "max_used_slots increased to %d on %s ring %d\n", ring->max_used_slots, ring->tx ? "TX" : "RX", ring->index); } } #else static inline void update_max_used_slots(struct b43_dmaring *ring, int current_used_slots) { } #endif /* DEBUG */ /* Request a slot for usage. */ static inline int request_slot(struct b43_dmaring *ring) { int slot; B43_WARN_ON(!ring->tx); B43_WARN_ON(ring->stopped); B43_WARN_ON(free_slots(ring) == 0); slot = next_slot(ring, ring->current_slot); ring->current_slot = slot; ring->used_slots++; update_max_used_slots(ring, ring->used_slots); return slot; } static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx) { static const u16 map64[] = { B43_MMIO_DMA64_BASE0, B43_MMIO_DMA64_BASE1, B43_MMIO_DMA64_BASE2, B43_MMIO_DMA64_BASE3, B43_MMIO_DMA64_BASE4, B43_MMIO_DMA64_BASE5, }; static const u16 map32[] = { B43_MMIO_DMA32_BASE0, B43_MMIO_DMA32_BASE1, B43_MMIO_DMA32_BASE2, B43_MMIO_DMA32_BASE3, B43_MMIO_DMA32_BASE4, B43_MMIO_DMA32_BASE5, }; if (type == B43_DMA_64BIT) { B43_WARN_ON(!(controller_idx >= 0 && controller_idx < ARRAY_SIZE(map64))); return map64[controller_idx]; } B43_WARN_ON(!(controller_idx >= 0 && controller_idx < ARRAY_SIZE(map32))); return map32[controller_idx]; } static inline dma_addr_t map_descbuffer(struct b43_dmaring *ring, unsigned char *buf, size_t len, int tx) { dma_addr_t dmaaddr; if (tx) { dmaaddr = dma_map_single(ring->dev->dev->dma_dev, buf, len, DMA_TO_DEVICE); } else { dmaaddr = dma_map_single(ring->dev->dev->dma_dev, buf, len, DMA_FROM_DEVICE); } return dmaaddr; } static inline void unmap_descbuffer(struct b43_dmaring *ring, dma_addr_t addr, size_t len, int tx) { if (tx) { dma_unmap_single(ring->dev->dev->dma_dev, addr, len, DMA_TO_DEVICE); } else { dma_unmap_single(ring->dev->dev->dma_dev, addr, len, DMA_FROM_DEVICE); } } static inline void sync_descbuffer_for_cpu(struct b43_dmaring *ring, dma_addr_t addr, size_t len) { B43_WARN_ON(ring->tx); dma_sync_single_for_cpu(ring->dev->dev->dma_dev, addr, len, DMA_FROM_DEVICE); } static inline void sync_descbuffer_for_device(struct b43_dmaring *ring, dma_addr_t addr, size_t len) { B43_WARN_ON(ring->tx); dma_sync_single_for_device(ring->dev->dev->dma_dev, addr, len, DMA_FROM_DEVICE); } static inline void free_descriptor_buffer(struct b43_dmaring *ring, struct b43_dmadesc_meta *meta) { if (meta->skb) { dev_kfree_skb_any(meta->skb); meta->skb = NULL; } } static int alloc_ringmemory(struct b43_dmaring *ring) { gfp_t flags = GFP_KERNEL; /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K * alignment and 8K buffers for 64-bit DMA with 8K alignment. * In practice we could use smaller buffers for the latter, but the * alignment is really important because of the hardware bug. If bit * 0x00001000 is used in DMA address, some hardware (like BCM4331) * copies that bit into B43_DMA64_RXSTATUS and we get false values from * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use * more than 256 slots for ring. */ u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, ring_mem_size, &(ring->dmabase), flags); if (!ring->descbase) { b43err(ring->dev->wl, "DMA ringmemory allocation failed\n"); return -ENOMEM; } memset(ring->descbase, 0, ring_mem_size); return 0; } static void free_ringmemory(struct b43_dmaring *ring) { u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size, ring->descbase, ring->dmabase); } /* Reset the RX DMA channel */ static int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, enum b43_dmatype type) { int i; u32 value; u16 offset; might_sleep(); offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXCTL : B43_DMA32_RXCTL; b43_write32(dev, mmio_base + offset, 0); for (i = 0; i < 10; i++) { offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXSTATUS : B43_DMA32_RXSTATUS; value = b43_read32(dev, mmio_base + offset); if (type == B43_DMA_64BIT) { value &= B43_DMA64_RXSTAT; if (value == B43_DMA64_RXSTAT_DISABLED) { i = -1; break; } } else { value &= B43_DMA32_RXSTATE; if (value == B43_DMA32_RXSTAT_DISABLED) { i = -1; break; } } msleep(1); } if (i != -1) { b43err(dev->wl, "DMA RX reset timed out\n"); return -ENODEV; } return 0; } /* Reset the TX DMA channel */ static int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, enum b43_dmatype type) { int i; u32 value; u16 offset; might_sleep(); for (i = 0; i < 10; i++) { offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : B43_DMA32_TXSTATUS; value = b43_read32(dev, mmio_base + offset); if (type == B43_DMA_64BIT) { value &= B43_DMA64_TXSTAT; if (value == B43_DMA64_TXSTAT_DISABLED || value == B43_DMA64_TXSTAT_IDLEWAIT || value == B43_DMA64_TXSTAT_STOPPED) break; } else { value &= B43_DMA32_TXSTATE; if (value == B43_DMA32_TXSTAT_DISABLED || value == B43_DMA32_TXSTAT_IDLEWAIT || value == B43_DMA32_TXSTAT_STOPPED) break; } msleep(1); } offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXCTL : B43_DMA32_TXCTL; b43_write32(dev, mmio_base + offset, 0); for (i = 0; i < 10; i++) { offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : B43_DMA32_TXSTATUS; value = b43_read32(dev, mmio_base + offset); if (type == B43_DMA_64BIT) { value &= B43_DMA64_TXSTAT; if (value == B43_DMA64_TXSTAT_DISABLED) { i = -1; break; } } else { value &= B43_DMA32_TXSTATE; if (value == B43_DMA32_TXSTAT_DISABLED) { i = -1; break; } } msleep(1); } if (i != -1) { b43err(dev->wl, "DMA TX reset timed out\n"); return -ENODEV; } /* ensure the reset is completed. */ msleep(1); return 0; } /* Check if a DMA mapping address is invalid. */ static bool b43_dma_mapping_error(struct b43_dmaring *ring, dma_addr_t addr, size_t buffersize, bool dma_to_device) { if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) return 1; switch (ring->type) { case B43_DMA_30BIT: if ((u64)addr + buffersize > (1ULL << 30)) goto address_error; break; case B43_DMA_32BIT: if ((u64)addr + buffersize > (1ULL << 32)) goto address_error; break; case B43_DMA_64BIT: /* Currently we can't have addresses beyond * 64bit in the kernel. */ break; } /* The address is OK. */ return 0; address_error: /* We can't support this address. Unmap it again. */ unmap_descbuffer(ring, addr, buffersize, dma_to_device); return 1; } static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb) { unsigned char *f = skb->data + ring->frameoffset; return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xFF); } static void b43_poison_rx_buffer(struct b43_dmaring *ring, struct sk_buff *skb) { struct b43_rxhdr_fw4 *rxhdr; unsigned char *frame; /* This poisons the RX buffer to detect DMA failures. */ rxhdr = (struct b43_rxhdr_fw4 *)(skb->data); rxhdr->frame_len = 0; B43_WARN_ON(ring->rx_buffersize < ring->frameoffset + sizeof(struct b43_plcp_hdr6) + 2); frame = skb->data + ring->frameoffset; memset(frame, 0xFF, sizeof(struct b43_plcp_hdr6) + 2 /* padding */); } static int setup_rx_descbuffer(struct b43_dmaring *ring, struct b43_dmadesc_generic *desc, struct b43_dmadesc_meta *meta, gfp_t gfp_flags) { dma_addr_t dmaaddr; struct sk_buff *skb; B43_WARN_ON(ring->tx); skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); if (unlikely(!skb)) return -ENOMEM; b43_poison_rx_buffer(ring, skb); dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { /* ugh. try to realloc in zone_dma */ gfp_flags |= GFP_DMA; dev_kfree_skb_any(skb); skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); if (unlikely(!skb)) return -ENOMEM; b43_poison_rx_buffer(ring, skb); dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { b43err(ring->dev->wl, "RX DMA buffer allocation failed\n"); dev_kfree_skb_any(skb); return -EIO; } } meta->skb = skb; meta->dmaaddr = dmaaddr; ring->ops->fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); return 0; } /* Allocate the initial descbuffers. * This is used for an RX ring only. */ static int alloc_initial_descbuffers(struct b43_dmaring *ring) { int i, err = -ENOMEM; struct b43_dmadesc_generic *desc; struct b43_dmadesc_meta *meta; for (i = 0; i < ring->nr_slots; i++) { desc = ring->ops->idx2desc(ring, i, &meta); err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); if (err) { b43err(ring->dev->wl, "Failed to allocate initial descbuffers\n"); goto err_unwind; } } mb(); ring->used_slots = ring->nr_slots; err = 0; out: return err; err_unwind: for (i--; i >= 0; i--) { desc = ring->ops->idx2desc(ring, i, &meta); unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); dev_kfree_skb(meta->skb); } goto out; } /* Do initial setup of the DMA controller. * Reset the controller, write the ring busaddress * and switch the "enable" bit on. */ static int dmacontroller_setup(struct b43_dmaring *ring) { int err = 0; u32 value; u32 addrext; bool parity = ring->dev->dma.parity; u32 addrlo; u32 addrhi; if (ring->tx) { if (ring->type == B43_DMA_64BIT) { u64 ringbase = (u64) (ring->dmabase); addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); value = B43_DMA64_TXENABLE; value |= (addrext << B43_DMA64_TXADDREXT_SHIFT) & B43_DMA64_TXADDREXT_MASK; if (!parity) value |= B43_DMA64_TXPARITYDISABLE; b43_dma_write(ring, B43_DMA64_TXCTL, value); b43_dma_write(ring, B43_DMA64_TXRINGLO, addrlo); b43_dma_write(ring, B43_DMA64_TXRINGHI, addrhi); } else { u32 ringbase = (u32) (ring->dmabase); addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); value = B43_DMA32_TXENABLE; value |= (addrext << B43_DMA32_TXADDREXT_SHIFT) & B43_DMA32_TXADDREXT_MASK; if (!parity) value |= B43_DMA32_TXPARITYDISABLE; b43_dma_write(ring, B43_DMA32_TXCTL, value); b43_dma_write(ring, B43_DMA32_TXRING, addrlo); } } else { err = alloc_initial_descbuffers(ring); if (err) goto out; if (ring->type == B43_DMA_64BIT) { u64 ringbase = (u64) (ring->dmabase); addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); value = (ring->frameoffset << B43_DMA64_RXFROFF_SHIFT); value |= B43_DMA64_RXENABLE; value |= (addrext << B43_DMA64_RXADDREXT_SHIFT) & B43_DMA64_RXADDREXT_MASK; if (!parity) value |= B43_DMA64_RXPARITYDISABLE; b43_dma_write(ring, B43_DMA64_RXCTL, value); b43_dma_write(ring, B43_DMA64_RXRINGLO, addrlo); b43_dma_write(ring, B43_DMA64_RXRINGHI, addrhi); b43_dma_write(ring, B43_DMA64_RXINDEX, ring->nr_slots * sizeof(struct b43_dmadesc64)); } else { u32 ringbase = (u32) (ring->dmabase); addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); value = (ring->frameoffset << B43_DMA32_RXFROFF_SHIFT); value |= B43_DMA32_RXENABLE; value |= (addrext << B43_DMA32_RXADDREXT_SHIFT) & B43_DMA32_RXADDREXT_MASK; if (!parity) value |= B43_DMA32_RXPARITYDISABLE; b43_dma_write(ring, B43_DMA32_RXCTL, value); b43_dma_write(ring, B43_DMA32_RXRING, addrlo); b43_dma_write(ring, B43_DMA32_RXINDEX, ring->nr_slots * sizeof(struct b43_dmadesc32)); } } out: return err; } /* Shutdown the DMA controller. */ static void dmacontroller_cleanup(struct b43_dmaring *ring) { if (ring->tx) { b43_dmacontroller_tx_reset(ring->dev, ring->mmio_base, ring->type); if (ring->type == B43_DMA_64BIT) { b43_dma_write(ring, B43_DMA64_TXRINGLO, 0); b43_dma_write(ring, B43_DMA64_TXRINGHI, 0); } else b43_dma_write(ring, B43_DMA32_TXRING, 0); } else { b43_dmacontroller_rx_reset(ring->dev, ring->mmio_base, ring->type); if (ring->type == B43_DMA_64BIT) { b43_dma_write(ring, B43_DMA64_RXRINGLO, 0); b43_dma_write(ring, B43_DMA64_RXRINGHI, 0); } else b43_dma_write(ring, B43_DMA32_RXRING, 0); } } static void free_all_descbuffers(struct b43_dmaring *ring) { struct b43_dmadesc_meta *meta; int i; if (!ring->used_slots) return; for (i = 0; i < ring->nr_slots; i++) { /* get meta - ignore returned value */ ring->ops->idx2desc(ring, i, &meta); if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) { B43_WARN_ON(!ring->tx); continue; } if (ring->tx) { unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); } else { unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); } free_descriptor_buffer(ring, meta); } } static u64 supported_dma_mask(struct b43_wldev *dev) { u32 tmp; u16 mmio_base; switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); if (tmp & BCMA_IOST_DMA64) return DMA_BIT_MASK(64); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); if (tmp & SSB_TMSHIGH_DMA64) return DMA_BIT_MASK(64); break; #endif } mmio_base = b43_dmacontroller_base(0, 0); b43_write32(dev, mmio_base + B43_DMA32_TXCTL, B43_DMA32_TXADDREXT_MASK); tmp = b43_read32(dev, mmio_base + B43_DMA32_TXCTL); if (tmp & B43_DMA32_TXADDREXT_MASK) return DMA_BIT_MASK(32); return DMA_BIT_MASK(30); } static enum b43_dmatype dma_mask_to_engine_type(u64 dmamask) { if (dmamask == DMA_BIT_MASK(30)) return B43_DMA_30BIT; if (dmamask == DMA_BIT_MASK(32)) return B43_DMA_32BIT; if (dmamask == DMA_BIT_MASK(64)) return B43_DMA_64BIT; B43_WARN_ON(1); return B43_DMA_30BIT; } /* Main initialization function. */ static struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, int controller_index, int for_tx, enum b43_dmatype type) { struct b43_dmaring *ring; int i, err; dma_addr_t dma_test; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out; ring->nr_slots = B43_RXRING_SLOTS; if (for_tx) ring->nr_slots = B43_TXRING_SLOTS; ring->meta = kcalloc(ring->nr_slots, sizeof(struct b43_dmadesc_meta), GFP_KERNEL); if (!ring->meta) goto err_kfree_ring; for (i = 0; i < ring->nr_slots; i++) ring->meta->skb = B43_DMA_PTR_POISON; ring->type = type; ring->dev = dev; ring->mmio_base = b43_dmacontroller_base(type, controller_index); ring->index = controller_index; if (type == B43_DMA_64BIT) ring->ops = &dma64_ops; else ring->ops = &dma32_ops; if (for_tx) { ring->tx = true; ring->current_slot = -1; } else { if (ring->index == 0) { switch (dev->fw.hdr_format) { case B43_FW_HDR_598: ring->rx_buffersize = B43_DMA0_RX_FW598_BUFSIZE; ring->frameoffset = B43_DMA0_RX_FW598_FO; break; case B43_FW_HDR_410: case B43_FW_HDR_351: ring->rx_buffersize = B43_DMA0_RX_FW351_BUFSIZE; ring->frameoffset = B43_DMA0_RX_FW351_FO; break; } } else B43_WARN_ON(1); } #ifdef CONFIG_B43_DEBUG ring->last_injected_overflow = jiffies; #endif if (for_tx) { /* Assumption: B43_TXRING_SLOTS can be divided by TX_SLOTS_PER_FRAME */ BUILD_BUG_ON(B43_TXRING_SLOTS % TX_SLOTS_PER_FRAME != 0); ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, b43_txhdr_size(dev), GFP_KERNEL); if (!ring->txhdr_cache) goto err_kfree_meta; /* test for ability to dma to txhdr_cache */ dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, b43_txhdr_size(dev), DMA_TO_DEVICE); if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev), 1)) { /* ugh realloc */ kfree(ring->txhdr_cache); ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, b43_txhdr_size(dev), GFP_KERNEL | GFP_DMA); if (!ring->txhdr_cache) goto err_kfree_meta; dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, b43_txhdr_size(dev), DMA_TO_DEVICE); if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev), 1)) { b43err(dev->wl, "TXHDR DMA allocation failed\n"); goto err_kfree_txhdr_cache; } } dma_unmap_single(dev->dev->dma_dev, dma_test, b43_txhdr_size(dev), DMA_TO_DEVICE); } err = alloc_ringmemory(ring); if (err) goto err_kfree_txhdr_cache; err = dmacontroller_setup(ring); if (err) goto err_free_ringmemory; out: return ring; err_free_ringmemory: free_ringmemory(ring); err_kfree_txhdr_cache: kfree(ring->txhdr_cache); err_kfree_meta: kfree(ring->meta); err_kfree_ring: kfree(ring); ring = NULL; goto out; } #define divide(a, b) ({ \ typeof(a) __a = a; \ do_div(__a, b); \ __a; \ }) #define modulo(a, b) ({ \ typeof(a) __a = a; \ do_div(__a, b); \ }) /* Main cleanup function. */ static void b43_destroy_dmaring(struct b43_dmaring *ring, const char *ringname) { if (!ring) return; #ifdef CONFIG_B43_DEBUG { /* Print some statistics. */ u64 failed_packets = ring->nr_failed_tx_packets; u64 succeed_packets = ring->nr_succeed_tx_packets; u64 nr_packets = failed_packets + succeed_packets; u64 permille_failed = 0, average_tries = 0; if (nr_packets) permille_failed = divide(failed_packets * 1000, nr_packets); if (nr_packets) average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets); b43dbg(ring->dev->wl, "DMA-%u %s: " "Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, " "Average tries %llu.%02llu\n", (unsigned int)(ring->type), ringname, ring->max_used_slots, ring->nr_slots, (unsigned long long)failed_packets, (unsigned long long)nr_packets, (unsigned long long)divide(permille_failed, 10), (unsigned long long)modulo(permille_failed, 10), (unsigned long long)divide(average_tries, 100), (unsigned long long)modulo(average_tries, 100)); } #endif /* DEBUG */ /* Device IRQs are disabled prior entering this function, * so no need to take care of concurrency with rx handler stuff. */ dmacontroller_cleanup(ring); free_all_descbuffers(ring); free_ringmemory(ring); kfree(ring->txhdr_cache); kfree(ring->meta); kfree(ring); } #define destroy_ring(dma, ring) do { \ b43_destroy_dmaring((dma)->ring, __stringify(ring)); \ (dma)->ring = NULL; \ } while (0) void b43_dma_free(struct b43_wldev *dev) { struct b43_dma *dma; if (b43_using_pio_transfers(dev)) return; dma = &dev->dma; destroy_ring(dma, rx_ring); destroy_ring(dma, tx_ring_AC_BK); destroy_ring(dma, tx_ring_AC_BE); destroy_ring(dma, tx_ring_AC_VI); destroy_ring(dma, tx_ring_AC_VO); destroy_ring(dma, tx_ring_mcast); } static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask) { u64 orig_mask = mask; bool fallback = false; int err; /* Try to set the DMA mask. If it fails, try falling back to a * lower mask, as we can always also support a lower one. */ while (1) { err = dma_set_mask(dev->dev->dma_dev, mask); if (!err) { err = dma_set_coherent_mask(dev->dev->dma_dev, mask); if (!err) break; } if (mask == DMA_BIT_MASK(64)) { mask = DMA_BIT_MASK(32); fallback = true; continue; } if (mask == DMA_BIT_MASK(32)) { mask = DMA_BIT_MASK(30); fallback = true; continue; } b43err(dev->wl, "The machine/kernel does not support " "the required %u-bit DMA mask\n", (unsigned int)dma_mask_to_engine_type(orig_mask)); return -EOPNOTSUPP; } if (fallback) { b43info(dev->wl, "DMA mask fallback from %u-bit to %u-bit\n", (unsigned int)dma_mask_to_engine_type(orig_mask), (unsigned int)dma_mask_to_engine_type(mask)); } return 0; } /* Some hardware with 64-bit DMA seems to be bugged and looks for translation * bit in low address word instead of high one. */ static bool b43_dma_translation_in_low_word(struct b43_wldev *dev, enum b43_dmatype type) { if (type != B43_DMA_64BIT) return 1; #ifdef CONFIG_B43_SSB if (dev->dev->bus_type == B43_BUS_SSB && dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && !(pci_is_pcie(dev->dev->sdev->bus->host_pci) && ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64)) return 1; #endif return 0; } int b43_dma_init(struct b43_wldev *dev) { struct b43_dma *dma = &dev->dma; int err; u64 dmamask; enum b43_dmatype type; dmamask = supported_dma_mask(dev); type = dma_mask_to_engine_type(dmamask); err = b43_dma_set_mask(dev, dmamask); if (err) return err; switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: dma->translation = bcma_core_dma_translation(dev->dev->bdev); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: dma->translation = ssb_dma_translation(dev->dev->sdev); break; #endif } dma->translation_in_low = b43_dma_translation_in_low_word(dev, type); dma->parity = true; #ifdef CONFIG_B43_BCMA /* TODO: find out which SSB devices need disabling parity */ if (dev->dev->bus_type == B43_BUS_BCMA) dma->parity = false; #endif err = -ENOMEM; /* setup TX DMA channels. */ dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type); if (!dma->tx_ring_AC_BK) goto out; dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type); if (!dma->tx_ring_AC_BE) goto err_destroy_bk; dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type); if (!dma->tx_ring_AC_VI) goto err_destroy_be; dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type); if (!dma->tx_ring_AC_VO) goto err_destroy_vi; dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type); if (!dma->tx_ring_mcast) goto err_destroy_vo; /* setup RX DMA channel. */ dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type); if (!dma->rx_ring) goto err_destroy_mcast; /* No support for the TX status DMA ring. */ B43_WARN_ON(dev->dev->core_rev < 5); b43dbg(dev->wl, "%u-bit DMA initialized\n", (unsigned int)type); err = 0; out: return err; err_destroy_mcast: destroy_ring(dma, tx_ring_mcast); err_destroy_vo: destroy_ring(dma, tx_ring_AC_VO); err_destroy_vi: destroy_ring(dma, tx_ring_AC_VI); err_destroy_be: destroy_ring(dma, tx_ring_AC_BE); err_destroy_bk: destroy_ring(dma, tx_ring_AC_BK); return err; } /* Generate a cookie for the TX header. */ static u16 generate_cookie(struct b43_dmaring *ring, int slot) { u16 cookie; /* Use the upper 4 bits of the cookie as * DMA controller ID and store the slot number * in the lower 12 bits. * Note that the cookie must never be 0, as this * is a special value used in RX path. * It can also not be 0xFFFF because that is special * for multicast frames. */ cookie = (((u16)ring->index + 1) << 12); B43_WARN_ON(slot & ~0x0FFF); cookie |= (u16)slot; return cookie; } /* Inspect a cookie and find out to which controller/slot it belongs. */ static struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) { struct b43_dma *dma = &dev->dma; struct b43_dmaring *ring = NULL; switch (cookie & 0xF000) { case 0x1000: ring = dma->tx_ring_AC_BK; break; case 0x2000: ring = dma->tx_ring_AC_BE; break; case 0x3000: ring = dma->tx_ring_AC_VI; break; case 0x4000: ring = dma->tx_ring_AC_VO; break; case 0x5000: ring = dma->tx_ring_mcast; break; } *slot = (cookie & 0x0FFF); if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) { b43dbg(dev->wl, "TX-status contains " "invalid cookie: 0x%04X\n", cookie); return NULL; } return ring; } static int dma_tx_fragment(struct b43_dmaring *ring, struct sk_buff *skb) { const struct b43_dma_ops *ops = ring->ops; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(info); u8 *header; int slot, old_top_slot, old_used_slots; int err; struct b43_dmadesc_generic *desc; struct b43_dmadesc_meta *meta; struct b43_dmadesc_meta *meta_hdr; u16 cookie; size_t hdrsize = b43_txhdr_size(ring->dev); /* Important note: If the number of used DMA slots per TX frame * is changed here, the TX_SLOTS_PER_FRAME definition at the top of * the file has to be updated, too! */ old_top_slot = ring->current_slot; old_used_slots = ring->used_slots; /* Get a slot for the header. */ slot = request_slot(ring); desc = ops->idx2desc(ring, slot, &meta_hdr); memset(meta_hdr, 0, sizeof(*meta_hdr)); header = &(ring->txhdr_cache[(slot / TX_SLOTS_PER_FRAME) * hdrsize]); cookie = generate_cookie(ring, slot); err = b43_generate_txhdr(ring->dev, header, skb, info, cookie); if (unlikely(err)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; return err; } meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, hdrsize, 1); if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize, 1)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; return -EIO; } ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr, hdrsize, 1, 0, 0); /* Get a slot for the payload. */ slot = request_slot(ring); desc = ops->idx2desc(ring, slot, &meta); memset(meta, 0, sizeof(*meta)); meta->skb = skb; meta->is_last_fragment = true; priv_info->bouncebuffer = NULL; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); /* create a bounce buffer in zone_dma on mapping failure. */ if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { priv_info->bouncebuffer = kmemdup(skb->data, skb->len, GFP_ATOMIC | GFP_DMA); if (!priv_info->bouncebuffer) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; err = -ENOMEM; goto out_unmap_hdr; } meta->dmaaddr = map_descbuffer(ring, priv_info->bouncebuffer, skb->len, 1); if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { kfree(priv_info->bouncebuffer); priv_info->bouncebuffer = NULL; ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; err = -EIO; goto out_unmap_hdr; } } ops->fill_descriptor(ring, desc, meta->dmaaddr, skb->len, 0, 1, 1); if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* Tell the firmware about the cookie of the last * mcast frame, so it can clear the more-data bit in it. */ b43_shm_write16(ring->dev, B43_SHM_SHARED, B43_SHM_SH_MCASTCOOKIE, cookie); } /* Now transfer the whole frame. */ wmb(); ops->poke_tx(ring, next_slot(ring, slot)); return 0; out_unmap_hdr: unmap_descbuffer(ring, meta_hdr->dmaaddr, hdrsize, 1); return err; } static inline int should_inject_overflow(struct b43_dmaring *ring) { #ifdef CONFIG_B43_DEBUG if (unlikely(b43_debug(ring->dev, B43_DBG_DMAOVERFLOW))) { /* Check if we should inject another ringbuffer overflow * to test handling of this situation in the stack. */ unsigned long next_overflow; next_overflow = ring->last_injected_overflow + HZ; if (time_after(jiffies, next_overflow)) { ring->last_injected_overflow = jiffies; b43dbg(ring->dev->wl, "Injecting TX ring overflow on " "DMA controller %d\n", ring->index); return 1; } } #endif /* CONFIG_B43_DEBUG */ return 0; } /* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */ static struct b43_dmaring *select_ring_by_priority(struct b43_wldev *dev, u8 queue_prio) { struct b43_dmaring *ring; if (dev->qos_enabled) { /* 0 = highest priority */ switch (queue_prio) { default: B43_WARN_ON(1); /* fallthrough */ case 0: ring = dev->dma.tx_ring_AC_VO; break; case 1: ring = dev->dma.tx_ring_AC_VI; break; case 2: ring = dev->dma.tx_ring_AC_BE; break; case 3: ring = dev->dma.tx_ring_AC_BK; break; } } else ring = dev->dma.tx_ring_AC_BE; return ring; } int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) { struct b43_dmaring *ring; struct ieee80211_hdr *hdr; int err = 0; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); hdr = (struct ieee80211_hdr *)skb->data; if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* The multicast ring will be sent after the DTIM */ ring = dev->dma.tx_ring_mcast; /* Set the more-data bit. Ucode will clear it on * the last frame for us. */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); } else { /* Decide by priority where to put this frame. */ ring = select_ring_by_priority( dev, skb_get_queue_mapping(skb)); } B43_WARN_ON(!ring->tx); if (unlikely(ring->stopped)) { /* We get here only because of a bug in mac80211. * Because of a race, one packet may be queued after * the queue is stopped, thus we got called when we shouldn't. * For now, just refuse the transmit. */ if (b43_debug(dev, B43_DBG_DMAVERBOSE)) b43err(dev->wl, "Packet after queue stopped\n"); err = -ENOSPC; goto out; } if (unlikely(WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME))) { /* If we get here, we have a real error with the queue * full, but queues not stopped. */ b43err(dev->wl, "DMA queue overflow\n"); err = -ENOSPC; goto out; } /* Assign the queue number to the ring (if not already done before) * so TX status handling can use it. The queue to ring mapping is * static, so we don't need to store it per frame. */ ring->queue_prio = skb_get_queue_mapping(skb); err = dma_tx_fragment(ring, skb); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ dev_kfree_skb_any(skb); err = 0; goto out; } if (unlikely(err)) { b43err(dev->wl, "DMA tx mapping failure\n"); goto out; } if ((free_slots(ring) < TX_SLOTS_PER_FRAME) || should_inject_overflow(ring)) { /* This TX ring is full. */ unsigned int skb_mapping = skb_get_queue_mapping(skb); ieee80211_stop_queue(dev->wl->hw, skb_mapping); dev->wl->tx_queue_stopped[skb_mapping] = 1; ring->stopped = true; if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index); } } out: return err; } void b43_dma_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status) { const struct b43_dma_ops *ops; struct b43_dmaring *ring; struct b43_dmadesc_meta *meta; int slot, firstused; bool frame_succeed; ring = parse_cookie(dev, status->cookie, &slot); if (unlikely(!ring)) return; B43_WARN_ON(!ring->tx); /* Sanity check: TX packets are processed in-order on one ring. * Check if the slot deduced from the cookie really is the first * used slot. */ firstused = ring->current_slot - ring->used_slots + 1; if (firstused < 0) firstused = ring->nr_slots + firstused; if (unlikely(slot != firstused)) { /* This possibly is a firmware bug and will result in * malfunction, memory leaks and/or stall of DMA functionality. */ b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. " "Expected %d, but got %d\n", ring->index, firstused, slot); return; } ops = ring->ops; while (1) { B43_WARN_ON(slot < 0 || slot >= ring->nr_slots); /* get meta - ignore returned value */ ops->idx2desc(ring, slot, &meta); if (b43_dma_ptr_is_poisoned(meta->skb)) { b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) " "on ring %d\n", slot, firstused, ring->index); break; } if (meta->skb) { struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); kfree(priv_info->bouncebuffer); priv_info->bouncebuffer = NULL; } else { unmap_descbuffer(ring, meta->dmaaddr, b43_txhdr_size(dev), 1); } if (meta->is_last_fragment) { struct ieee80211_tx_info *info; if (unlikely(!meta->skb)) { /* This is a scatter-gather fragment of a frame, so * the skb pointer must not be NULL. */ b43dbg(dev->wl, "TX status unexpected NULL skb " "at slot %d (first=%d) on ring %d\n", slot, firstused, ring->index); break; } info = IEEE80211_SKB_CB(meta->skb); /* * Call back to inform the ieee80211 subsystem about * the status of the transmission. */ frame_succeed = b43_fill_txstatus_report(dev, info, status); #ifdef CONFIG_B43_DEBUG if (frame_succeed) ring->nr_succeed_tx_packets++; else ring->nr_failed_tx_packets++; ring->nr_total_packet_tries += status->frame_count; #endif /* DEBUG */ ieee80211_tx_status(dev->wl->hw, meta->skb); /* skb will be freed by ieee80211_tx_status(). * Poison our pointer. */ meta->skb = B43_DMA_PTR_POISON; } else { /* No need to call free_descriptor_buffer here, as * this is only the txhdr, which is not allocated. */ if (unlikely(meta->skb)) { b43dbg(dev->wl, "TX status unexpected non-NULL skb " "at slot %d (first=%d) on ring %d\n", slot, firstused, ring->index); break; } } /* Everything unmapped and free'd. So it's not used anymore. */ ring->used_slots--; if (meta->is_last_fragment) { /* This is the last scatter-gather * fragment of the frame. We are done. */ break; } slot = next_slot(ring, slot); } if (ring->stopped) { B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME); ring->stopped = false; } if (dev->wl->tx_queue_stopped[ring->queue_prio]) { dev->wl->tx_queue_stopped[ring->queue_prio] = 0; } else { /* If the driver queue is running wake the corresponding * mac80211 queue. */ ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index); } } /* Add work to the queue. */ ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); } static void dma_rx(struct b43_dmaring *ring, int *slot) { const struct b43_dma_ops *ops = ring->ops; struct b43_dmadesc_generic *desc; struct b43_dmadesc_meta *meta; struct b43_rxhdr_fw4 *rxhdr; struct sk_buff *skb; u16 len; int err; dma_addr_t dmaaddr; desc = ops->idx2desc(ring, *slot, &meta); sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); skb = meta->skb; rxhdr = (struct b43_rxhdr_fw4 *)skb->data; len = le16_to_cpu(rxhdr->frame_len); if (len == 0) { int i = 0; do { udelay(2); barrier(); len = le16_to_cpu(rxhdr->frame_len); } while (len == 0 && i++ < 5); if (unlikely(len == 0)) { dmaaddr = meta->dmaaddr; goto drop_recycle_buffer; } } if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) { /* Something went wrong with the DMA. * The device did not touch the buffer and did not overwrite the poison. */ b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n"); dmaaddr = meta->dmaaddr; goto drop_recycle_buffer; } if (unlikely(len + ring->frameoffset > ring->rx_buffersize)) { /* The data did not fit into one descriptor buffer * and is split over multiple buffers. * This should never happen, as we try to allocate buffers * big enough. So simply ignore this packet. */ int cnt = 0; s32 tmp = len; while (1) { desc = ops->idx2desc(ring, *slot, &meta); /* recycle the descriptor buffer. */ b43_poison_rx_buffer(ring, meta->skb); sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); *slot = next_slot(ring, *slot); cnt++; tmp -= ring->rx_buffersize; if (tmp <= 0) break; } b43err(ring->dev->wl, "DMA RX buffer too small " "(len: %u, buffer: %u, nr-dropped: %d)\n", len, ring->rx_buffersize, cnt); goto drop; } dmaaddr = meta->dmaaddr; err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); if (unlikely(err)) { b43dbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer() failed\n"); goto drop_recycle_buffer; } unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); skb_put(skb, len + ring->frameoffset); skb_pull(skb, ring->frameoffset); b43_rx(ring->dev, skb, rxhdr); drop: return; drop_recycle_buffer: /* Poison and recycle the RX buffer. */ b43_poison_rx_buffer(ring, skb); sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize); } void b43_dma_rx(struct b43_dmaring *ring) { const struct b43_dma_ops *ops = ring->ops; int slot, current_slot; int used_slots = 0; B43_WARN_ON(ring->tx); current_slot = ops->get_current_rxslot(ring); B43_WARN_ON(!(current_slot >= 0 && current_slot < ring->nr_slots)); slot = ring->current_slot; for (; slot != current_slot; slot = next_slot(ring, slot)) { dma_rx(ring, &slot); update_max_used_slots(ring, ++used_slots); } wmb(); ops->set_current_rxslot(ring, slot); ring->current_slot = slot; } static void b43_dma_tx_suspend_ring(struct b43_dmaring *ring) { B43_WARN_ON(!ring->tx); ring->ops->tx_suspend(ring); } static void b43_dma_tx_resume_ring(struct b43_dmaring *ring) { B43_WARN_ON(!ring->tx); ring->ops->tx_resume(ring); } void b43_dma_tx_suspend(struct b43_wldev *dev) { b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK); b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE); b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI); b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO); b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast); } void b43_dma_tx_resume(struct b43_wldev *dev) { b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast); b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO); b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI); b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE); b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK); b43_power_saving_ctl_bits(dev, 0); } static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type, u16 mmio_base, bool enable) { u32 ctl; if (type == B43_DMA_64BIT) { ctl = b43_read32(dev, mmio_base + B43_DMA64_RXCTL); ctl &= ~B43_DMA64_RXDIRECTFIFO; if (enable) ctl |= B43_DMA64_RXDIRECTFIFO; b43_write32(dev, mmio_base + B43_DMA64_RXCTL, ctl); } else { ctl = b43_read32(dev, mmio_base + B43_DMA32_RXCTL); ctl &= ~B43_DMA32_RXDIRECTFIFO; if (enable) ctl |= B43_DMA32_RXDIRECTFIFO; b43_write32(dev, mmio_base + B43_DMA32_RXCTL, ctl); } } /* Enable/Disable Direct FIFO Receive Mode (PIO) on a RX engine. * This is called from PIO code, so DMA structures are not available. */ void b43_dma_direct_fifo_rx(struct b43_wldev *dev, unsigned int engine_index, bool enable) { enum b43_dmatype type; u16 mmio_base; type = dma_mask_to_engine_type(supported_dma_mask(dev)); mmio_base = b43_dmacontroller_base(type, engine_index); direct_fifo_rx(dev, type, mmio_base, enable); } compat-drivers-2012-09-18/drivers/net/wireless/b43/debugfs.h0000644000175000017500000000507112026211315022644 0ustar mcgrofmcgrof#ifndef B43_DEBUGFS_H_ #define B43_DEBUGFS_H_ struct b43_wldev; struct b43_txstatus; enum b43_dyndbg { /* Dynamic debugging features */ B43_DBG_XMITPOWER, B43_DBG_DMAOVERFLOW, B43_DBG_DMAVERBOSE, B43_DBG_PWORK_FAST, B43_DBG_PWORK_STOP, B43_DBG_LO, B43_DBG_FIRMWARE, B43_DBG_KEYS, B43_DBG_VERBOSESTATS, __B43_NR_DYNDBG, }; #ifdef CONFIG_B43_DEBUG struct dentry; #define B43_NR_LOGGED_TXSTATUS 100 struct b43_txstatus_log { /* This structure is protected by wl->mutex */ struct b43_txstatus *log; int end; }; struct b43_dfs_file { struct dentry *dentry; char *buffer; size_t data_len; }; struct b43_dfsentry { struct b43_wldev *dev; struct dentry *subdir; struct b43_dfs_file file_shm16read; struct b43_dfs_file file_shm16write; struct b43_dfs_file file_shm32read; struct b43_dfs_file file_shm32write; struct b43_dfs_file file_mmio16read; struct b43_dfs_file file_mmio16write; struct b43_dfs_file file_mmio32read; struct b43_dfs_file file_mmio32write; struct b43_dfs_file file_txstat; struct b43_dfs_file file_txpower_g; struct b43_dfs_file file_restart; struct b43_dfs_file file_loctls; struct b43_txstatus_log txstatlog; /* The cached address for the next mmio16read file read */ u16 mmio16read_next; /* The cached address for the next mmio32read file read */ u16 mmio32read_next; /* The cached address for the next shm16read file read */ u32 shm16read_routing_next; u32 shm16read_addr_next; /* The cached address for the next shm32read file read */ u32 shm32read_routing_next; u32 shm32read_addr_next; /* Enabled/Disabled list for the dynamic debugging features. */ u32 dyn_debug[__B43_NR_DYNDBG]; /* Dentries for the dynamic debugging entries. */ struct dentry *dyn_debug_dentries[__B43_NR_DYNDBG]; }; bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature); void b43_debugfs_init(void); void b43_debugfs_exit(void); void b43_debugfs_add_device(struct b43_wldev *dev); void b43_debugfs_remove_device(struct b43_wldev *dev); void b43_debugfs_log_txstat(struct b43_wldev *dev, const struct b43_txstatus *status); #else /* CONFIG_B43_DEBUG */ static inline bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) { return 0; } static inline void b43_debugfs_init(void) { } static inline void b43_debugfs_exit(void) { } static inline void b43_debugfs_add_device(struct b43_wldev *dev) { } static inline void b43_debugfs_remove_device(struct b43_wldev *dev) { } static inline void b43_debugfs_log_txstat(struct b43_wldev *dev, const struct b43_txstatus *status) { } #endif /* CONFIG_B43_DEBUG */ #endif /* B43_DEBUGFS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/debugfs.c0000644000175000017500000004601212026211315022637 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver debugfs driver debugging code Copyright (c) 2005-2007 Michael Buesch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "b43.h" #include "main.h" #include "debugfs.h" #include "dma.h" #include "xmit.h" /* The root directory. */ static struct dentry *rootdir; struct b43_debugfs_fops { ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize); int (*write)(struct b43_wldev *dev, const char *buf, size_t count); struct file_operations fops; /* Offset of struct b43_dfs_file in struct b43_dfsentry */ size_t file_struct_offset; }; static inline struct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev, const struct b43_debugfs_fops *dfops) { void *p; p = dev->dfsentry; p += dfops->file_struct_offset; return p; } #define fappend(fmt, x...) \ do { \ if (bufsize - count) \ count += snprintf(buf + count, \ bufsize - count, \ fmt , ##x); \ else \ printk(KERN_ERR "b43: fappend overflow\n"); \ } while (0) /* The biggest address values for SHM access from the debugfs files. */ #define B43_MAX_SHM_ROUTING 4 #define B43_MAX_SHM_ADDR 0xFFFF static ssize_t shm16read__read_file(struct b43_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; unsigned int routing, addr; u16 val; routing = dev->dfsentry->shm16read_routing_next; addr = dev->dfsentry->shm16read_addr_next; if ((routing > B43_MAX_SHM_ROUTING) || (addr > B43_MAX_SHM_ADDR)) return -EDESTADDRREQ; val = b43_shm_read16(dev, routing, addr); fappend("0x%04X\n", val); return count; } static int shm16read__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int routing, addr; int res; res = sscanf(buf, "0x%X 0x%X", &routing, &addr); if (res != 2) return -EINVAL; if (routing > B43_MAX_SHM_ROUTING) return -EADDRNOTAVAIL; if (addr > B43_MAX_SHM_ADDR) return -EADDRNOTAVAIL; if (routing == B43_SHM_SHARED) { if ((addr % 2) != 0) return -EADDRNOTAVAIL; } dev->dfsentry->shm16read_routing_next = routing; dev->dfsentry->shm16read_addr_next = addr; return 0; } static int shm16write__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int routing, addr, mask, set; u16 val; int res; res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X", &routing, &addr, &mask, &set); if (res != 4) return -EINVAL; if (routing > B43_MAX_SHM_ROUTING) return -EADDRNOTAVAIL; if (addr > B43_MAX_SHM_ADDR) return -EADDRNOTAVAIL; if (routing == B43_SHM_SHARED) { if ((addr % 2) != 0) return -EADDRNOTAVAIL; } if ((mask > 0xFFFF) || (set > 0xFFFF)) return -E2BIG; if (mask == 0) val = 0; else val = b43_shm_read16(dev, routing, addr); val &= mask; val |= set; b43_shm_write16(dev, routing, addr, val); return 0; } static ssize_t shm32read__read_file(struct b43_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; unsigned int routing, addr; u32 val; routing = dev->dfsentry->shm32read_routing_next; addr = dev->dfsentry->shm32read_addr_next; if ((routing > B43_MAX_SHM_ROUTING) || (addr > B43_MAX_SHM_ADDR)) return -EDESTADDRREQ; val = b43_shm_read32(dev, routing, addr); fappend("0x%08X\n", val); return count; } static int shm32read__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int routing, addr; int res; res = sscanf(buf, "0x%X 0x%X", &routing, &addr); if (res != 2) return -EINVAL; if (routing > B43_MAX_SHM_ROUTING) return -EADDRNOTAVAIL; if (addr > B43_MAX_SHM_ADDR) return -EADDRNOTAVAIL; if (routing == B43_SHM_SHARED) { if ((addr % 2) != 0) return -EADDRNOTAVAIL; } dev->dfsentry->shm32read_routing_next = routing; dev->dfsentry->shm32read_addr_next = addr; return 0; } static int shm32write__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int routing, addr, mask, set; u32 val; int res; res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X", &routing, &addr, &mask, &set); if (res != 4) return -EINVAL; if (routing > B43_MAX_SHM_ROUTING) return -EADDRNOTAVAIL; if (addr > B43_MAX_SHM_ADDR) return -EADDRNOTAVAIL; if (routing == B43_SHM_SHARED) { if ((addr % 2) != 0) return -EADDRNOTAVAIL; } if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) return -E2BIG; if (mask == 0) val = 0; else val = b43_shm_read32(dev, routing, addr); val &= mask; val |= set; b43_shm_write32(dev, routing, addr, val); return 0; } /* The biggest MMIO address that we allow access to from the debugfs files. */ #define B43_MAX_MMIO_ACCESS (0xF00 - 1) static ssize_t mmio16read__read_file(struct b43_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; unsigned int addr; u16 val; addr = dev->dfsentry->mmio16read_next; if (addr > B43_MAX_MMIO_ACCESS) return -EDESTADDRREQ; val = b43_read16(dev, addr); fappend("0x%04X\n", val); return count; } static int mmio16read__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int addr; int res; res = sscanf(buf, "0x%X", &addr); if (res != 1) return -EINVAL; if (addr > B43_MAX_MMIO_ACCESS) return -EADDRNOTAVAIL; if ((addr % 2) != 0) return -EINVAL; dev->dfsentry->mmio16read_next = addr; return 0; } static int mmio16write__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int addr, mask, set; int res; u16 val; res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set); if (res != 3) return -EINVAL; if (addr > B43_MAX_MMIO_ACCESS) return -EADDRNOTAVAIL; if ((mask > 0xFFFF) || (set > 0xFFFF)) return -E2BIG; if ((addr % 2) != 0) return -EINVAL; if (mask == 0) val = 0; else val = b43_read16(dev, addr); val &= mask; val |= set; b43_write16(dev, addr, val); return 0; } static ssize_t mmio32read__read_file(struct b43_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; unsigned int addr; u32 val; addr = dev->dfsentry->mmio32read_next; if (addr > B43_MAX_MMIO_ACCESS) return -EDESTADDRREQ; val = b43_read32(dev, addr); fappend("0x%08X\n", val); return count; } static int mmio32read__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int addr; int res; res = sscanf(buf, "0x%X", &addr); if (res != 1) return -EINVAL; if (addr > B43_MAX_MMIO_ACCESS) return -EADDRNOTAVAIL; if ((addr % 4) != 0) return -EINVAL; dev->dfsentry->mmio32read_next = addr; return 0; } static int mmio32write__write_file(struct b43_wldev *dev, const char *buf, size_t count) { unsigned int addr, mask, set; int res; u32 val; res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set); if (res != 3) return -EINVAL; if (addr > B43_MAX_MMIO_ACCESS) return -EADDRNOTAVAIL; if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) return -E2BIG; if ((addr % 4) != 0) return -EINVAL; if (mask == 0) val = 0; else val = b43_read32(dev, addr); val &= mask; val |= set; b43_write32(dev, addr, val); return 0; } static ssize_t txstat_read_file(struct b43_wldev *dev, char *buf, size_t bufsize) { struct b43_txstatus_log *log = &dev->dfsentry->txstatlog; ssize_t count = 0; int i, idx; struct b43_txstatus *stat; if (log->end < 0) { fappend("Nothing transmitted, yet\n"); goto out; } fappend("b43 TX status reports:\n\n" "index | cookie | seq | phy_stat | frame_count | " "rts_count | supp_reason | pm_indicated | " "intermediate | for_ampdu | acked\n" "---\n"); i = log->end + 1; idx = 0; while (1) { if (i == B43_NR_LOGGED_TXSTATUS) i = 0; stat = &(log->log[i]); if (stat->cookie) { fappend("%03d | " "0x%04X | 0x%04X | 0x%02X | " "0x%X | 0x%X | " "%u | %u | " "%u | %u | %u\n", idx, stat->cookie, stat->seq, stat->phy_stat, stat->frame_count, stat->rts_count, stat->supp_reason, stat->pm_indicated, stat->intermediate, stat->for_ampdu, stat->acked); idx++; } if (i == log->end) break; i++; } out: return count; } static int restart_write_file(struct b43_wldev *dev, const char *buf, size_t count) { int err = 0; if (count > 0 && buf[0] == '1') { b43_controller_restart(dev, "manually restarted"); } else err = -EINVAL; return err; } static unsigned long calc_expire_secs(unsigned long now, unsigned long time, unsigned long expire) { expire = time + expire; if (time_after(now, expire)) return 0; /* expired */ if (expire < now) { /* jiffies wrapped */ expire -= MAX_JIFFY_OFFSET; now -= MAX_JIFFY_OFFSET; } B43_WARN_ON(expire < now); return (expire - now) / HZ; } static ssize_t loctls_read_file(struct b43_wldev *dev, char *buf, size_t bufsize) { ssize_t count = 0; struct b43_txpower_lo_control *lo; int i, err = 0; struct b43_lo_calib *cal; unsigned long now = jiffies; struct b43_phy *phy = &dev->phy; if (phy->type != B43_PHYTYPE_G) { fappend("Device is not a G-PHY\n"); err = -ENODEV; goto out; } lo = phy->g->lo_control; fappend("-- Local Oscillator calibration data --\n\n"); fappend("HW-power-control enabled: %d\n", dev->phy.hardware_power_control); fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n", lo->tx_bias, lo->tx_magn, calc_expire_secs(now, lo->txctl_measured_time, B43_LO_TXCTL_EXPIRE)); fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n", (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32), (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL), calc_expire_secs(now, lo->pwr_vec_read_time, B43_LO_PWRVEC_EXPIRE)); fappend("\nCalibrated settings:\n"); list_for_each_entry(cal, &lo->calib_list, list) { bool active; active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) && b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt)); fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d " "(expires in %lu sec)%s\n", cal->bbatt.att, cal->rfatt.att, cal->rfatt.with_padmix, cal->ctl.i, cal->ctl.q, calc_expire_secs(now, cal->calib_time, B43_LO_CALIB_EXPIRE), active ? " ACTIVE" : ""); } fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n"); for (i = 0; i < lo->rfatt_list.len; i++) { fappend("%u(%d), ", lo->rfatt_list.list[i].att, lo->rfatt_list.list[i].with_padmix); } fappend("\n"); fappend("\nUsed Baseband attenuation values:\n"); for (i = 0; i < lo->bbatt_list.len; i++) { fappend("%u, ", lo->bbatt_list.list[i].att); } fappend("\n"); out: return err ? err : count; } #undef fappend static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct b43_wldev *dev; struct b43_debugfs_fops *dfops; struct b43_dfs_file *dfile; ssize_t uninitialized_var(ret); char *buf; const size_t bufsize = 1024 * 16; /* 16 kiB buffer */ const size_t buforder = get_order(bufsize); int err = 0; if (!count) return 0; dev = file->private_data; if (!dev) return -ENODEV; mutex_lock(&dev->wl->mutex); if (b43_status(dev) < B43_STAT_INITIALIZED) { err = -ENODEV; goto out_unlock; } dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); if (!dfops->read) { err = -ENOSYS; goto out_unlock; } dfile = fops_to_dfs_file(dev, dfops); if (!dfile->buffer) { buf = (char *)__get_free_pages(GFP_KERNEL, buforder); if (!buf) { err = -ENOMEM; goto out_unlock; } memset(buf, 0, bufsize); ret = dfops->read(dev, buf, bufsize); if (ret <= 0) { free_pages((unsigned long)buf, buforder); err = ret; goto out_unlock; } dfile->data_len = ret; dfile->buffer = buf; } ret = simple_read_from_buffer(userbuf, count, ppos, dfile->buffer, dfile->data_len); if (*ppos >= dfile->data_len) { free_pages((unsigned long)dfile->buffer, buforder); dfile->buffer = NULL; dfile->data_len = 0; } out_unlock: mutex_unlock(&dev->wl->mutex); return err ? err : ret; } static ssize_t b43_debugfs_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct b43_wldev *dev; struct b43_debugfs_fops *dfops; char *buf; int err = 0; if (!count) return 0; if (count > PAGE_SIZE) return -E2BIG; dev = file->private_data; if (!dev) return -ENODEV; mutex_lock(&dev->wl->mutex); if (b43_status(dev) < B43_STAT_INITIALIZED) { err = -ENODEV; goto out_unlock; } dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); if (!dfops->write) { err = -ENOSYS; goto out_unlock; } buf = (char *)get_zeroed_page(GFP_KERNEL); if (!buf) { err = -ENOMEM; goto out_unlock; } if (copy_from_user(buf, userbuf, count)) { err = -EFAULT; goto out_freepage; } err = dfops->write(dev, buf, count); if (err) goto out_freepage; out_freepage: free_page((unsigned long)buf); out_unlock: mutex_unlock(&dev->wl->mutex); return err ? err : count; } #define B43_DEBUGFS_FOPS(name, _read, _write) \ static struct b43_debugfs_fops fops_##name = { \ .read = _read, \ .write = _write, \ .fops = { \ .open = simple_open, \ .read = b43_debugfs_read, \ .write = b43_debugfs_write, \ .llseek = generic_file_llseek, \ }, \ .file_struct_offset = offsetof(struct b43_dfsentry, \ file_##name), \ } B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file); B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file); B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file); B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file); B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file); B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file); B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file); B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file); B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL); B43_DEBUGFS_FOPS(restart, NULL, restart_write_file); B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL); bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) { bool enabled; enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]); if (unlikely(enabled)) { /* Force full debugging messages, if the user enabled * some dynamic debugging feature. */ b43_modparam_verbose = B43_VERBOSITY_MAX; } return enabled; } static void b43_remove_dynamic_debug(struct b43_wldev *dev) { struct b43_dfsentry *e = dev->dfsentry; int i; for (i = 0; i < __B43_NR_DYNDBG; i++) debugfs_remove(e->dyn_debug_dentries[i]); } static void b43_add_dynamic_debug(struct b43_wldev *dev) { struct b43_dfsentry *e = dev->dfsentry; struct dentry *d; #define add_dyn_dbg(name, id, initstate) do { \ e->dyn_debug[id] = (initstate); \ d = debugfs_create_bool(name, 0600, e->subdir, \ &(e->dyn_debug[id])); \ if (!IS_ERR(d)) \ e->dyn_debug_dentries[id] = d; \ } while (0) add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, 0); add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, 0); add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, 0); add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, 0); add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0); add_dyn_dbg("debug_lo", B43_DBG_LO, 0); add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0); add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0); add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, 0); #undef add_dyn_dbg } void b43_debugfs_add_device(struct b43_wldev *dev) { struct b43_dfsentry *e; struct b43_txstatus_log *log; char devdir[16]; B43_WARN_ON(!dev); e = kzalloc(sizeof(*e), GFP_KERNEL); if (!e) { b43err(dev->wl, "debugfs: add device OOM\n"); return; } e->dev = dev; log = &e->txstatlog; log->log = kcalloc(B43_NR_LOGGED_TXSTATUS, sizeof(struct b43_txstatus), GFP_KERNEL); if (!log->log) { b43err(dev->wl, "debugfs: add device txstatus OOM\n"); kfree(e); return; } log->end = -1; dev->dfsentry = e; snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); e->subdir = debugfs_create_dir(devdir, rootdir); if (!e->subdir || IS_ERR(e->subdir)) { if (e->subdir == ERR_PTR(-ENODEV)) { b43dbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not " "enabled in kernel config\n"); } else { b43err(dev->wl, "debugfs: cannot create %s directory\n", devdir); } dev->dfsentry = NULL; kfree(log->log); kfree(e); return; } e->mmio16read_next = 0xFFFF; /* invalid address */ e->mmio32read_next = 0xFFFF; /* invalid address */ e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */ e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */ e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */ e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */ #define ADD_FILE(name, mode) \ do { \ struct dentry *d; \ d = debugfs_create_file(__stringify(name), \ mode, e->subdir, dev, \ &fops_##name.fops); \ e->file_##name.dentry = NULL; \ if (!IS_ERR(d)) \ e->file_##name.dentry = d; \ } while (0) ADD_FILE(shm16read, 0600); ADD_FILE(shm16write, 0200); ADD_FILE(shm32read, 0600); ADD_FILE(shm32write, 0200); ADD_FILE(mmio16read, 0600); ADD_FILE(mmio16write, 0200); ADD_FILE(mmio32read, 0600); ADD_FILE(mmio32write, 0200); ADD_FILE(txstat, 0400); ADD_FILE(restart, 0200); ADD_FILE(loctls, 0400); #undef ADD_FILE b43_add_dynamic_debug(dev); } void b43_debugfs_remove_device(struct b43_wldev *dev) { struct b43_dfsentry *e; if (!dev) return; e = dev->dfsentry; if (!e) return; b43_remove_dynamic_debug(dev); debugfs_remove(e->file_shm16read.dentry); debugfs_remove(e->file_shm16write.dentry); debugfs_remove(e->file_shm32read.dentry); debugfs_remove(e->file_shm32write.dentry); debugfs_remove(e->file_mmio16read.dentry); debugfs_remove(e->file_mmio16write.dentry); debugfs_remove(e->file_mmio32read.dentry); debugfs_remove(e->file_mmio32write.dentry); debugfs_remove(e->file_txstat.dentry); debugfs_remove(e->file_restart.dentry); debugfs_remove(e->file_loctls.dentry); debugfs_remove(e->subdir); kfree(e->txstatlog.log); kfree(e); } void b43_debugfs_log_txstat(struct b43_wldev *dev, const struct b43_txstatus *status) { struct b43_dfsentry *e = dev->dfsentry; struct b43_txstatus_log *log; struct b43_txstatus *cur; int i; if (!e) return; log = &e->txstatlog; i = log->end + 1; if (i == B43_NR_LOGGED_TXSTATUS) i = 0; log->end = i; cur = &(log->log[i]); memcpy(cur, status, sizeof(*cur)); } void b43_debugfs_init(void) { rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); if (IS_ERR(rootdir)) rootdir = NULL; } void b43_debugfs_exit(void) { debugfs_remove(rootdir); } compat-drivers-2012-09-18/drivers/net/wireless/b43/bus.h0000644000175000017500000000350112026211315022012 0ustar mcgrofmcgrof#ifndef B43_BUS_H_ #define B43_BUS_H_ enum b43_bus_type { #ifdef CONFIG_B43_BCMA B43_BUS_BCMA, #endif B43_BUS_SSB, }; struct b43_bus_dev { enum b43_bus_type bus_type; union { struct bcma_device *bdev; struct ssb_device *sdev; }; int (*bus_may_powerdown)(struct b43_bus_dev *dev); int (*bus_powerup)(struct b43_bus_dev *dev, bool dynamic_pctl); int (*device_is_enabled)(struct b43_bus_dev *dev); void (*device_enable)(struct b43_bus_dev *dev, u32 core_specific_flags); void (*device_disable)(struct b43_bus_dev *dev, u32 core_specific_flags); u16 (*read16)(struct b43_bus_dev *dev, u16 offset); u32 (*read32)(struct b43_bus_dev *dev, u16 offset); void (*write16)(struct b43_bus_dev *dev, u16 offset, u16 value); void (*write32)(struct b43_bus_dev *dev, u16 offset, u32 value); void (*block_read)(struct b43_bus_dev *dev, void *buffer, size_t count, u16 offset, u8 reg_width); void (*block_write)(struct b43_bus_dev *dev, const void *buffer, size_t count, u16 offset, u8 reg_width); struct device *dev; struct device *dma_dev; unsigned int irq; u16 board_vendor; u16 board_type; u16 board_rev; u16 chip_id; u8 chip_rev; u8 chip_pkg; struct ssb_sprom *bus_sprom; u16 core_id; u8 core_rev; }; static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev) { return (dev->bus_type == B43_BUS_SSB && dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA); } static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev) { return (dev->bus_type == B43_BUS_SSB && dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO); } struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core); struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev); void *b43_bus_get_wldev(struct b43_bus_dev *dev); void b43_bus_set_wldev(struct b43_bus_dev *dev, void *data); #endif /* B43_BUS_H_ */ compat-drivers-2012-09-18/drivers/net/wireless/b43/bus.c0000644000175000017500000001555212026211315022016 0ustar mcgrofmcgrof/* Broadcom B43 wireless driver Bus abstraction layer Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "b43.h" #include "bus.h" /* BCMA */ #ifdef CONFIG_B43_BCMA static int b43_bus_bcma_bus_may_powerdown(struct b43_bus_dev *dev) { return 0; /* bcma_bus_may_powerdown(dev->bdev->bus); */ } static int b43_bus_bcma_bus_powerup(struct b43_bus_dev *dev, bool dynamic_pctl) { return 0; /* bcma_bus_powerup(dev->sdev->bus, dynamic_pctl); */ } static int b43_bus_bcma_device_is_enabled(struct b43_bus_dev *dev) { return bcma_core_is_enabled(dev->bdev); } static void b43_bus_bcma_device_enable(struct b43_bus_dev *dev, u32 core_specific_flags) { bcma_core_enable(dev->bdev, core_specific_flags); } static void b43_bus_bcma_device_disable(struct b43_bus_dev *dev, u32 core_specific_flags) { bcma_core_disable(dev->bdev, core_specific_flags); } static u16 b43_bus_bcma_read16(struct b43_bus_dev *dev, u16 offset) { return bcma_read16(dev->bdev, offset); } static u32 b43_bus_bcma_read32(struct b43_bus_dev *dev, u16 offset) { return bcma_read32(dev->bdev, offset); } static void b43_bus_bcma_write16(struct b43_bus_dev *dev, u16 offset, u16 value) { bcma_write16(dev->bdev, offset, value); } static void b43_bus_bcma_write32(struct b43_bus_dev *dev, u16 offset, u32 value) { bcma_write32(dev->bdev, offset, value); } static void b43_bus_bcma_block_read(struct b43_bus_dev *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { bcma_block_read(dev->bdev, buffer, count, offset, reg_width); } static void b43_bus_bcma_block_write(struct b43_bus_dev *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { bcma_block_write(dev->bdev, buffer, count, offset, reg_width); } struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core) { struct b43_bus_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; dev->bus_type = B43_BUS_BCMA; dev->bdev = core; dev->bus_may_powerdown = b43_bus_bcma_bus_may_powerdown; dev->bus_powerup = b43_bus_bcma_bus_powerup; dev->device_is_enabled = b43_bus_bcma_device_is_enabled; dev->device_enable = b43_bus_bcma_device_enable; dev->device_disable = b43_bus_bcma_device_disable; dev->read16 = b43_bus_bcma_read16; dev->read32 = b43_bus_bcma_read32; dev->write16 = b43_bus_bcma_write16; dev->write32 = b43_bus_bcma_write32; dev->block_read = b43_bus_bcma_block_read; dev->block_write = b43_bus_bcma_block_write; dev->dev = &core->dev; dev->dma_dev = core->dma_dev; dev->irq = core->irq; dev->board_vendor = core->bus->boardinfo.vendor; dev->board_type = core->bus->boardinfo.type; dev->board_rev = core->bus->sprom.board_rev; dev->chip_id = core->bus->chipinfo.id; dev->chip_rev = core->bus->chipinfo.rev; dev->chip_pkg = core->bus->chipinfo.pkg; dev->bus_sprom = &core->bus->sprom; dev->core_id = core->id.id; dev->core_rev = core->id.rev; return dev; } #endif /* CONFIG_B43_BCMA */ /* SSB */ #ifdef CONFIG_B43_SSB static int b43_bus_ssb_bus_may_powerdown(struct b43_bus_dev *dev) { return ssb_bus_may_powerdown(dev->sdev->bus); } static int b43_bus_ssb_bus_powerup(struct b43_bus_dev *dev, bool dynamic_pctl) { return ssb_bus_powerup(dev->sdev->bus, dynamic_pctl); } static int b43_bus_ssb_device_is_enabled(struct b43_bus_dev *dev) { return ssb_device_is_enabled(dev->sdev); } static void b43_bus_ssb_device_enable(struct b43_bus_dev *dev, u32 core_specific_flags) { ssb_device_enable(dev->sdev, core_specific_flags); } static void b43_bus_ssb_device_disable(struct b43_bus_dev *dev, u32 core_specific_flags) { ssb_device_disable(dev->sdev, core_specific_flags); } static u16 b43_bus_ssb_read16(struct b43_bus_dev *dev, u16 offset) { return ssb_read16(dev->sdev, offset); } static u32 b43_bus_ssb_read32(struct b43_bus_dev *dev, u16 offset) { return ssb_read32(dev->sdev, offset); } static void b43_bus_ssb_write16(struct b43_bus_dev *dev, u16 offset, u16 value) { ssb_write16(dev->sdev, offset, value); } static void b43_bus_ssb_write32(struct b43_bus_dev *dev, u16 offset, u32 value) { ssb_write32(dev->sdev, offset, value); } static void b43_bus_ssb_block_read(struct b43_bus_dev *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { ssb_block_read(dev->sdev, buffer, count, offset, reg_width); } static void b43_bus_ssb_block_write(struct b43_bus_dev *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { ssb_block_write(dev->sdev, buffer, count, offset, reg_width); } struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev) { struct b43_bus_dev *dev; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; dev->bus_type = B43_BUS_SSB; dev->sdev = sdev; dev->bus_may_powerdown = b43_bus_ssb_bus_may_powerdown; dev->bus_powerup = b43_bus_ssb_bus_powerup; dev->device_is_enabled = b43_bus_ssb_device_is_enabled; dev->device_enable = b43_bus_ssb_device_enable; dev->device_disable = b43_bus_ssb_device_disable; dev->read16 = b43_bus_ssb_read16; dev->read32 = b43_bus_ssb_read32; dev->write16 = b43_bus_ssb_write16; dev->write32 = b43_bus_ssb_write32; dev->block_read = b43_bus_ssb_block_read; dev->block_write = b43_bus_ssb_block_write; dev->dev = sdev->dev; dev->dma_dev = sdev->dma_dev; dev->irq = sdev->irq; dev->board_vendor = sdev->bus->boardinfo.vendor; dev->board_type = sdev->bus->boardinfo.type; dev->board_rev = sdev->bus->sprom.board_rev; dev->chip_id = sdev->bus->chip_id; dev->chip_rev = sdev->bus->chip_rev; dev->chip_pkg = sdev->bus->chip_package; dev->bus_sprom = &sdev->bus->sprom; dev->core_id = sdev->id.coreid; dev->core_rev = sdev->id.revision; return dev; } #endif /* CONFIG_B43_SSB */ void *b43_bus_get_wldev(struct b43_bus_dev *dev) { switch (dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: return bcma_get_drvdata(dev->bdev); #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: return ssb_get_drvdata(dev->sdev); #endif } return NULL; } void b43_bus_set_wldev(struct b43_bus_dev *dev, void *wldev) { switch (dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: bcma_set_drvdata(dev->bdev, wldev); break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: ssb_set_drvdata(dev->sdev, wldev); break; #endif } } compat-drivers-2012-09-18/drivers/net/wireless/b43/Makefile0000644000175000017500000000156412026211315022517 0ustar mcgrofmcgrofb43-y += main.o b43-y += bus.o b43-y += tables.o b43-$(CONFIG_B43_PHY_N) += tables_nphy.o b43-$(CONFIG_B43_PHY_N) += radio_2055.o b43-$(CONFIG_B43_PHY_N) += radio_2056.o b43-$(CONFIG_B43_PHY_N) += radio_2057.o b43-y += phy_common.o b43-y += phy_g.o b43-y += phy_a.o b43-$(CONFIG_B43_PHY_N) += phy_n.o b43-$(CONFIG_B43_PHY_LP) += phy_lp.o b43-$(CONFIG_B43_PHY_LP) += tables_lpphy.o b43-$(CONFIG_B43_PHY_HT) += phy_ht.o b43-$(CONFIG_B43_PHY_HT) += tables_phy_ht.o b43-$(CONFIG_B43_PHY_HT) += radio_2059.o b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o b43-y += sysfs.o b43-y += xmit.o b43-y += lo.o b43-y += wa.o b43-y += dma.o b43-y += pio.o b43-y += rfkill.o b43-$(CONFIG_B43_LEDS) += leds.o b43-$(CONFIG_B43_PCMCIA) += pcmcia.o b43-$(CONFIG_B43_SDIO) += sdio.o b43-$(CONFIG_B43_DEBUG) += debugfs.o obj-$(CONFIG_B43) += b43.o compat-drivers-2012-09-18/drivers/net/wireless/b43/Kconfig0000644000175000017500000001176112026211315022362 0ustar mcgrofmcgrofconfig B43 tristate "Broadcom 43xx wireless support (mac80211 stack)" depends on SSB_POSSIBLE && MAC80211 && HAS_DMA select SSB select FW_LOADER ---help--- b43 is a driver for the Broadcom 43xx series wireless devices. Check "lspci" for something like "Broadcom Corporation BCM43XX 802.11 Wireless LAN Controller" to determine whether you own such a device. This driver supports the new BCM43xx IEEE 802.11G devices, but not the old IEEE 802.11B devices. Old devices are supported by the b43legacy driver. Note that this has nothing to do with the standard that your AccessPoint supports (A, B, G or a combination). IEEE 802.11G devices can talk to IEEE 802.11B AccessPoints. It is safe to include both b43 and b43legacy as the underlying glue layer will automatically load the correct version for your device. This driver uses V4 firmware, which must be installed separately using b43-fwcutter. This driver can be built as a module (recommended) that will be called "b43". If unsure, say M. config B43_BCMA bool "Support for BCMA bus" depends on B43 && BCMA default y config B43_BCMA_EXTRA bool "Hardware support that overlaps with the brcmsmac driver" depends on B43_BCMA default n if BRCMSMAC default y config B43_SSB bool depends on B43 && SSB default y # Auto-select SSB PCI-HOST support, if possible config B43_PCI_AUTOSELECT bool depends on B43 && SSB_PCIHOST_POSSIBLE select SSB_PCIHOST select SSB_B43_PCI_BRIDGE default y # Auto-select SSB PCICORE driver, if possible config B43_PCICORE_AUTOSELECT bool depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE select SSB_DRIVER_PCICORE default y config B43_PCMCIA bool "Broadcom 43xx PCMCIA device support" depends on B43 && SSB_PCMCIAHOST_POSSIBLE select SSB_PCMCIAHOST ---help--- Broadcom 43xx PCMCIA device support. Support for 16bit PCMCIA devices. Please note that most PC-CARD devices are _NOT_ 16bit PCMCIA devices, but 32bit CardBUS devices. CardBUS devices are supported out of the box by b43. With this config option you can drive b43 cards in CompactFlash formfactor in a PCMCIA adaptor. CF b43 cards can sometimes be found in handheld PCs. It's safe to select Y here, even if you don't have a B43 PCMCIA device. If unsure, say N. config B43_SDIO bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)" depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL select SSB_SDIOHOST ---help--- Broadcom 43xx device support for Soft-MAC SDIO devices. With this config option you can drive Soft-MAC b43 cards with a Secure Digital I/O interface. This includes the WLAN daughter card found on the Nintendo Wii video game console. Note that this does not support Broadcom 43xx Full-MAC devices. It's safe to select Y here, even if you don't have a B43 SDIO device. If unsure, say N. #Data transfers to the device via PIO. We want it as a fallback even # if we can do DMA. config B43_BCMA_PIO bool depends on B43_BCMA select BCMA_BLOCKIO default y config B43_PIO bool depends on B43 select SSB_BLOCKIO default y config B43_PHY_N bool "Support for 802.11n (N-PHY) devices (EXPERIMENTAL)" depends on B43 && EXPERIMENTAL ---help--- Support for the N-PHY. This enables support for devices with N-PHY. Say N if you expect high stability and performance. Saying Y will not affect other devices support and may provide support for basic needs. config B43_PHY_LP bool "Support for low-power (LP-PHY) devices" depends on B43 default y ---help--- Support for the LP-PHY. The LP-PHY is a low-power PHY built into some notebooks and embedded devices. It supports 802.11a/b/g (802.11a support is optional, and currently disabled). config B43_PHY_HT bool "Support for HT-PHY (high throughput) devices (EXPERIMENTAL)" depends on B43 && EXPERIMENTAL ---help--- Support for the HT-PHY. Enables support for BCM4331 and possibly other chipsets with that PHY. config B43_PHY_LCN bool "Support for LCN-PHY devices (BROKEN)" depends on B43 && BROKEN ---help--- Support for the LCN-PHY. Say N, this is BROKEN and crashes driver. # This config option automatically enables b43 LEDS support, # if it's possible. config B43_LEDS bool depends on B43 && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = B43) default y # This config option automatically enables b43 HW-RNG support, # if the HW-RNG core is enabled. config B43_HWRNG bool depends on B43 && (HW_RANDOM = y || HW_RANDOM = B43) default y config B43_DEBUG bool "Broadcom 43xx debugging" depends on B43 ---help--- Broadcom 43xx debugging. This adds additional runtime sanity checks and statistics to the driver. These checks and statistics might me expensive and hurt runtime performance of your system. This also adds the b43 debugfs interface. Do not enable this, unless you are debugging the driver. Say N, if you are a distributor or user building a release kernel for production use. Only say Y, if you are debugging a problem in the b43 driver sourcecode. compat-drivers-2012-09-18/drivers/net/wireless/ath/0000755000175000017500000000000012026211315021235 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ath/regd.c0000644000175000017500000004227312026211315022332 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include "regd.h" #include "regd_common.h" static int __ath_regd_init(struct ath_regulatory *reg); /* * This is a set of common rules used by our world regulatory domains. * We have 12 world regulatory domains. To save space we consolidate * the regulatory domains in 5 structures by frequency and change * the flags on our reg_notifier() on a case by case basis. */ /* Only these channels all allow active scan on all world regulatory domains */ #define ATH9K_2GHZ_CH01_11 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0) /* We enable active scan on these a case by case basis by regulatory domain */ #define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\ NL80211_RRF_PASSIVE_SCAN) #define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM) /* We allow IBSS on these on a case by case basis by regulatory domain */ #define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 40, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) #define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 40, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) #define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 40, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ ATH9K_2GHZ_CH12_13, \ ATH9K_2GHZ_CH14 #define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \ ATH9K_5GHZ_5470_5850 /* This one skips what we call "mid band" */ #define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \ ATH9K_5GHZ_5725_5850 /* Can be used for: * 0x60, 0x61, 0x62 */ static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = { .n_reg_rules = 5, .alpha2 = "99", .reg_rules = { ATH9K_2GHZ_ALL, ATH9K_5GHZ_ALL, } }; /* Can be used by 0x63 and 0x65 */ static const struct ieee80211_regdomain ath_world_regdom_63_65 = { .n_reg_rules = 4, .alpha2 = "99", .reg_rules = { ATH9K_2GHZ_CH01_11, ATH9K_2GHZ_CH12_13, ATH9K_5GHZ_NO_MIDBAND, } }; /* Can be used by 0x64 only */ static const struct ieee80211_regdomain ath_world_regdom_64 = { .n_reg_rules = 3, .alpha2 = "99", .reg_rules = { ATH9K_2GHZ_CH01_11, ATH9K_5GHZ_NO_MIDBAND, } }; /* Can be used by 0x66 and 0x69 */ static const struct ieee80211_regdomain ath_world_regdom_66_69 = { .n_reg_rules = 3, .alpha2 = "99", .reg_rules = { ATH9K_2GHZ_CH01_11, ATH9K_5GHZ_ALL, } }; /* Can be used by 0x67, 0x68, 0x6A and 0x6C */ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = { .n_reg_rules = 4, .alpha2 = "99", .reg_rules = { ATH9K_2GHZ_CH01_11, ATH9K_2GHZ_CH12_13, ATH9K_5GHZ_ALL, } }; static inline bool is_wwr_sku(u16 regd) { return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) && (((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) || (regd == WORLD)); } static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg) { return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG; } bool ath_is_world_regd(struct ath_regulatory *reg) { return is_wwr_sku(ath_regd_get_eepromRD(reg)); } EXPORT_SYMBOL(ath_is_world_regd); static const struct ieee80211_regdomain *ath_default_world_regdomain(void) { /* this is the most restrictive */ return &ath_world_regdom_64; } static const struct ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg) { switch (reg->regpair->regDmnEnum) { case 0x60: case 0x61: case 0x62: return &ath_world_regdom_60_61_62; case 0x63: case 0x65: return &ath_world_regdom_63_65; case 0x64: return &ath_world_regdom_64; case 0x66: case 0x69: return &ath_world_regdom_66_69; case 0x67: case 0x68: case 0x6A: case 0x6C: return &ath_world_regdom_67_68_6A_6C; default: WARN_ON(1); return ath_default_world_regdomain(); } } bool ath_is_49ghz_allowed(u16 regdomain) { /* possibly more */ return regdomain == MKK9_MKKC; } EXPORT_SYMBOL(ath_is_49ghz_allowed); /* Frequency is one where radar detection is required */ static bool ath_is_radar_freq(u16 center_freq) { return (center_freq >= 5260 && center_freq <= 5700); } /* * N.B: These exception rules do not apply radar freqs. * * - We enable adhoc (or beaconing) if allowed by 11d * - We enable active scan if the channel is allowed by 11d * - If no country IE has been processed and a we determine we have * received a beacon on a channel we can enable active scan and * adhoc (or beaconing). */ static void ath_reg_apply_beaconing_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { enum ieee80211_band band; struct ieee80211_supported_band *sband; const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; u32 bandwidth = 0; int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (ath_is_radar_freq(ch->center_freq) || (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); if (r) continue; /* * If 11d had a rule for this channel ensure * we enable adhoc/beaconing if it allows us to * use it. Note that we would have disabled it * by applying our static world regdomain by * default during init, prior to calling our * regulatory_hint(). */ if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) ch->flags &= ~IEEE80211_CHAN_NO_IBSS; if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } else { if (ch->beacon_found) ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN); } } } } /* Allows active scan scan on Ch 12 and 13 */ static void ath_reg_apply_active_scan_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; u32 bandwidth = 0; int r; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; if (!sband) return; /* * If no country IE has been received always enable active scan * on these channels. This is only done for specific regulatory SKUs */ if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { ch = &sband->channels[11]; /* CH 12 */ if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; ch = &sband->channels[12]; /* CH 13 */ if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; return; } /* * If a country IE has been received check its rule for this * channel first before enabling active scan. The passive scan * would have been enforced by the initial processing of our * custom regulatory domain. */ ch = &sband->channels[11]; /* CH 12 */ r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } ch = &sband->channels[12]; /* CH 13 */ r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } } /* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */ static void ath_reg_apply_radar_flags(struct wiphy *wiphy) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; unsigned int i; if (!wiphy->bands[IEEE80211_BAND_5GHZ]) return; sband = wiphy->bands[IEEE80211_BAND_5GHZ]; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (!ath_is_radar_freq(ch->center_freq)) continue; /* We always enable radar detection/DFS on this * frequency range. Additionally we also apply on * this frequency range: * - If STA mode does not yet have DFS supports disable * active scanning * - If adhoc mode does not support DFS yet then * disable adhoc in the frequency. * - If AP mode does not yet support radar detection/DFS * do not allow AP mode */ if (!(ch->flags & IEEE80211_CHAN_DISABLED)) ch->flags |= IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN; } } static void ath_reg_apply_world_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, struct ath_regulatory *reg) { switch (reg->regpair->regDmnEnum) { case 0x60: case 0x63: case 0x66: case 0x67: case 0x6C: ath_reg_apply_beaconing_flags(wiphy, initiator); break; case 0x68: ath_reg_apply_beaconing_flags(wiphy, initiator); ath_reg_apply_active_scan_flags(wiphy, initiator); break; } } static u16 ath_regd_find_country_by_name(char *alpha2) { unsigned int i; for (i = 0; i < ARRAY_SIZE(allCountries); i++) { if (!memcmp(allCountries[i].isoName, alpha2, 2)) return allCountries[i].countryCode; } return -1; } int ath_reg_notifier_apply(struct wiphy *wiphy, struct regulatory_request *request, struct ath_regulatory *reg) { struct ath_common *common = container_of(reg, struct ath_common, regulatory); u16 country_code; /* We always apply this */ ath_reg_apply_radar_flags(wiphy); /* * This would happen when we have sent a custom regulatory request * a world regulatory domain and the scheduler hasn't yet processed * any pending requests in the queue. */ if (!request) return 0; switch (request->initiator) { case NL80211_REGDOM_SET_BY_CORE: /* * If common->reg_world_copy is world roaming it means we *were* * world roaming... so we now have to restore that data. */ if (!ath_is_world_regd(&common->reg_world_copy)) break; memcpy(reg, &common->reg_world_copy, sizeof(struct ath_regulatory)); break; case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_USER: break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (!ath_is_world_regd(reg)) break; country_code = ath_regd_find_country_by_name(request->alpha2); if (country_code == (u16) -1) break; reg->current_rd = COUNTRY_ERD_FLAG; reg->current_rd |= country_code; printk(KERN_DEBUG "ath: regdomain 0x%0x updated by CountryIE\n", reg->current_rd); __ath_regd_init(reg); ath_reg_apply_world_flags(wiphy, request->initiator, reg); break; } return 0; } EXPORT_SYMBOL(ath_reg_notifier_apply); static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg) { u16 rd = ath_regd_get_eepromRD(reg); int i; if (rd & COUNTRY_ERD_FLAG) { /* EEPROM value is a country code */ u16 cc = rd & ~COUNTRY_ERD_FLAG; printk(KERN_DEBUG "ath: EEPROM indicates we should expect " "a country code\n"); for (i = 0; i < ARRAY_SIZE(allCountries); i++) if (allCountries[i].countryCode == cc) return true; } else { /* EEPROM value is a regpair value */ if (rd != CTRY_DEFAULT) printk(KERN_DEBUG "ath: EEPROM indicates we " "should expect a direct regpair map\n"); for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) if (regDomainPairs[i].regDmnEnum == rd) return true; } printk(KERN_DEBUG "ath: invalid regulatory domain/country code 0x%x\n", rd); return false; } /* EEPROM country code to regpair mapping */ static struct country_code_to_enum_rd* ath_regd_find_country(u16 countryCode) { int i; for (i = 0; i < ARRAY_SIZE(allCountries); i++) { if (allCountries[i].countryCode == countryCode) return &allCountries[i]; } return NULL; } /* EEPROM rd code to regpair mapping */ static struct country_code_to_enum_rd* ath_regd_find_country_by_rd(int regdmn) { int i; for (i = 0; i < ARRAY_SIZE(allCountries); i++) { if (allCountries[i].regDmnEnum == regdmn) return &allCountries[i]; } return NULL; } /* Returns the map of the EEPROM set RD to a country code */ static u16 ath_regd_get_default_country(u16 rd) { if (rd & COUNTRY_ERD_FLAG) { struct country_code_to_enum_rd *country = NULL; u16 cc = rd & ~COUNTRY_ERD_FLAG; country = ath_regd_find_country(cc); if (country != NULL) return cc; } return CTRY_DEFAULT; } static struct reg_dmn_pair_mapping* ath_get_regpair(int regdmn) { int i; if (regdmn == NO_ENUMRD) return NULL; for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { if (regDomainPairs[i].regDmnEnum == regdmn) return ®DomainPairs[i]; } return NULL; } static int ath_regd_init_wiphy(struct ath_regulatory *reg, struct wiphy *wiphy, int (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)) { const struct ieee80211_regdomain *regd; wiphy->reg_notifier = reg_notifier; wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; if (ath_is_world_regd(reg)) { /* * Anything applied here (prior to wiphy registration) gets * saved on the wiphy orig_* parameters */ regd = ath_world_regdomain(reg); wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; } else { /* * This gets applied in the case of the absence of CRDA, * it's our own custom world regulatory domain, similar to * cfg80211's but we enable passive scanning. */ regd = ath_default_world_regdomain(); } wiphy_apply_custom_regulatory(wiphy, regd); ath_reg_apply_radar_flags(wiphy); ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); return 0; } /* * Some users have reported their EEPROM programmed with * 0x8000 set, this is not a supported regulatory domain * but since we have more than one user with it we need * a solution for them. We default to 0x64, which is the * default Atheros world regulatory domain. */ static void ath_regd_sanitize(struct ath_regulatory *reg) { if (reg->current_rd != COUNTRY_ERD_FLAG) return; printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n"); reg->current_rd = 0x64; } static int __ath_regd_init(struct ath_regulatory *reg) { struct country_code_to_enum_rd *country = NULL; u16 regdmn; if (!reg) return -EINVAL; ath_regd_sanitize(reg); printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd); if (!ath_regd_is_eeprom_valid(reg)) { pr_err("Invalid EEPROM contents\n"); return -EINVAL; } regdmn = ath_regd_get_eepromRD(reg); reg->country_code = ath_regd_get_default_country(regdmn); if (reg->country_code == CTRY_DEFAULT && regdmn == CTRY_DEFAULT) { printk(KERN_DEBUG "ath: EEPROM indicates default " "country code should be used\n"); reg->country_code = CTRY_UNITED_STATES; } if (reg->country_code == CTRY_DEFAULT) { country = NULL; } else { printk(KERN_DEBUG "ath: doing EEPROM country->regdmn " "map search\n"); country = ath_regd_find_country(reg->country_code); if (country == NULL) { printk(KERN_DEBUG "ath: no valid country maps found for " "country code: 0x%0x\n", reg->country_code); return -EINVAL; } else { regdmn = country->regDmnEnum; printk(KERN_DEBUG "ath: country maps to " "regdmn code: 0x%0x\n", regdmn); } } reg->regpair = ath_get_regpair(regdmn); if (!reg->regpair) { printk(KERN_DEBUG "ath: " "No regulatory domain pair found, cannot continue\n"); return -EINVAL; } if (!country) country = ath_regd_find_country_by_rd(regdmn); if (country) { reg->alpha2[0] = country->isoName[0]; reg->alpha2[1] = country->isoName[1]; } else { reg->alpha2[0] = '0'; reg->alpha2[1] = '0'; } printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n", reg->alpha2[0], reg->alpha2[1]); printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n", reg->regpair->regDmnEnum); return 0; } int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, int (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)) { struct ath_common *common = container_of(reg, struct ath_common, regulatory); int r; r = __ath_regd_init(reg); if (r) return r; if (ath_is_world_regd(reg)) memcpy(&common->reg_world_copy, reg, sizeof(struct ath_regulatory)); ath_regd_init_wiphy(reg, wiphy, reg_notifier); return 0; } EXPORT_SYMBOL(ath_regd_init); u32 ath_regd_get_band_ctl(struct ath_regulatory *reg, enum ieee80211_band band) { if (!reg->regpair || (reg->country_code == CTRY_DEFAULT && is_wwr_sku(ath_regd_get_eepromRD(reg)))) { return SD_NO_CTL; } switch (band) { case IEEE80211_BAND_2GHZ: return reg->regpair->reg_2ghz_ctl; case IEEE80211_BAND_5GHZ: return reg->regpair->reg_5ghz_ctl; default: return NO_CTL; } } EXPORT_SYMBOL(ath_regd_get_band_ctl); compat-drivers-2012-09-18/drivers/net/wireless/ath/main.c0000644000175000017500000000544612026211315022336 0ustar mcgrofmcgrof/* * Copyright (c) 2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include "ath.h" MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Shared library for Atheros wireless LAN cards."); MODULE_LICENSE("Dual BSD/GPL"); struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, u32 len, gfp_t gfp_mask) { struct sk_buff *skb; u32 off; /* * Cache-line-align. This is important (for the * 5210 at least) as not doing so causes bogus data * in rx'd frames. */ /* Note: the kernel can allocate a value greater than * what we ask it to give us. We really only need 4 KB as that * is this hardware supports and in fact we need at least 3849 * as that is the MAX AMSDU size this hardware supports. * Unfortunately this means we may get 8 KB here from the * kernel... and that is actually what is observed on some * systems :( */ skb = __dev_alloc_skb(len + common->cachelsz - 1, gfp_mask); if (skb != NULL) { off = ((unsigned long) skb->data) % common->cachelsz; if (off != 0) skb_reserve(skb, common->cachelsz - off); } else { pr_err("skbuff alloc of size %u failed\n", len); return NULL; } return skb; } EXPORT_SYMBOL(ath_rxbuf_alloc); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) void ath_printk(const char *level, const struct ath_common* common, const char *fmt, ...) { struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; if (common && common->hw && common->hw->wiphy) printk("%sath: %s: %pV", level, wiphy_name(common->hw->wiphy), &vaf); else printk("%sath: %pV", level, &vaf); va_end(args); } #else void ath_printk(const char *level, const struct ath_common* common, const char *fmt, ...) { va_list args; va_start(args, fmt); if (common && common->hw && common->hw->wiphy) printk("%sath: %s: ", level, wiphy_name(common->hw->wiphy)); else printk("%sath: ", level); vprintk(fmt, args); va_end(args); } #endif EXPORT_SYMBOL(ath_printk); compat-drivers-2012-09-18/drivers/net/wireless/ath/reg.h0000644000175000017500000000443612026211315022172 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ATH_REGISTERS_H #define ATH_REGISTERS_H #define AR_MIBC 0x0040 #define AR_MIBC_COW 0x00000001 #define AR_MIBC_FMC 0x00000002 #define AR_MIBC_CMC 0x00000004 #define AR_MIBC_MCS 0x00000008 /* * BSSID mask registers. See ath_hw_set_bssid_mask() * for detailed documentation about these registers. */ #define AR_BSSMSKL 0x80e0 #define AR_BSSMSKU 0x80e4 #define AR_TFCNT 0x80ec #define AR_RFCNT 0x80f0 #define AR_RCCNT 0x80f4 #define AR_CCCNT 0x80f8 #define AR_KEYTABLE_0 0x8800 #define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32)) #define AR_KEY_CACHE_SIZE 128 #define AR_RSVD_KEYTABLE_ENTRIES 4 #define AR_KEY_TYPE 0x00000007 #define AR_KEYTABLE_TYPE_40 0x00000000 #define AR_KEYTABLE_TYPE_104 0x00000001 #define AR_KEYTABLE_TYPE_128 0x00000003 #define AR_KEYTABLE_TYPE_TKIP 0x00000004 #define AR_KEYTABLE_TYPE_AES 0x00000005 #define AR_KEYTABLE_TYPE_CCM 0x00000006 #define AR_KEYTABLE_TYPE_CLR 0x00000007 #define AR_KEYTABLE_ANT 0x00000008 #define AR_KEYTABLE_VALID 0x00008000 #define AR_KEYTABLE_KEY0(_n) (AR_KEYTABLE(_n) + 0) #define AR_KEYTABLE_KEY1(_n) (AR_KEYTABLE(_n) + 4) #define AR_KEYTABLE_KEY2(_n) (AR_KEYTABLE(_n) + 8) #define AR_KEYTABLE_KEY3(_n) (AR_KEYTABLE(_n) + 12) #define AR_KEYTABLE_KEY4(_n) (AR_KEYTABLE(_n) + 16) #define AR_KEYTABLE_TYPE(_n) (AR_KEYTABLE(_n) + 20) #define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24) #define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28) #endif /* ATH_REGISTERS_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/regd.h0000644000175000017500000001422412026211315022332 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef REGD_H #define REGD_H #include #include #include "ath.h" enum ctl_group { CTL_FCC = 0x10, CTL_MKK = 0x40, CTL_ETSI = 0x30, }; #define NO_CTL 0xff #define SD_NO_CTL 0xE0 #define NO_CTL 0xff #define CTL_11A 0 #define CTL_11B 1 #define CTL_11G 2 #define CTL_2GHT20 5 #define CTL_5GHT20 6 #define CTL_2GHT40 7 #define CTL_5GHT40 8 #define CTRY_DEBUG 0x1ff #define CTRY_DEFAULT 0 #define COUNTRY_ERD_FLAG 0x8000 #define WORLDWIDE_ROAMING_FLAG 0x4000 #define MULTI_DOMAIN_MASK 0xFF00 #define WORLD_SKU_MASK 0x00F0 #define WORLD_SKU_PREFIX 0x0060 #define CHANNEL_HALF_BW 10 #define CHANNEL_QUARTER_BW 5 struct country_code_to_enum_rd { u16 countryCode; u16 regDmnEnum; const char *isoName; }; enum CountryCode { CTRY_ALBANIA = 8, CTRY_ALGERIA = 12, CTRY_ARGENTINA = 32, CTRY_ARMENIA = 51, CTRY_ARUBA = 533, CTRY_AUSTRALIA = 36, CTRY_AUSTRIA = 40, CTRY_AZERBAIJAN = 31, CTRY_BAHRAIN = 48, CTRY_BANGLADESH = 50, CTRY_BARBADOS = 52, CTRY_BELARUS = 112, CTRY_BELGIUM = 56, CTRY_BELIZE = 84, CTRY_BOLIVIA = 68, CTRY_BOSNIA_HERZ = 70, CTRY_BRAZIL = 76, CTRY_BRUNEI_DARUSSALAM = 96, CTRY_BULGARIA = 100, CTRY_CAMBODIA = 116, CTRY_CANADA = 124, CTRY_CHILE = 152, CTRY_CHINA = 156, CTRY_COLOMBIA = 170, CTRY_COSTA_RICA = 188, CTRY_CROATIA = 191, CTRY_CYPRUS = 196, CTRY_CZECH = 203, CTRY_DENMARK = 208, CTRY_DOMINICAN_REPUBLIC = 214, CTRY_ECUADOR = 218, CTRY_EGYPT = 818, CTRY_EL_SALVADOR = 222, CTRY_ESTONIA = 233, CTRY_FAEROE_ISLANDS = 234, CTRY_FINLAND = 246, CTRY_FRANCE = 250, CTRY_GEORGIA = 268, CTRY_GERMANY = 276, CTRY_GREECE = 300, CTRY_GREENLAND = 304, CTRY_GRENADA = 308, CTRY_GUAM = 316, CTRY_GUATEMALA = 320, CTRY_HAITI = 332, CTRY_HONDURAS = 340, CTRY_HONG_KONG = 344, CTRY_HUNGARY = 348, CTRY_ICELAND = 352, CTRY_INDIA = 356, CTRY_INDONESIA = 360, CTRY_IRAN = 364, CTRY_IRAQ = 368, CTRY_IRELAND = 372, CTRY_ISRAEL = 376, CTRY_ITALY = 380, CTRY_JAMAICA = 388, CTRY_JAPAN = 392, CTRY_JORDAN = 400, CTRY_KAZAKHSTAN = 398, CTRY_KENYA = 404, CTRY_KOREA_NORTH = 408, CTRY_KOREA_ROC = 410, CTRY_KOREA_ROC2 = 411, CTRY_KOREA_ROC3 = 412, CTRY_KUWAIT = 414, CTRY_LATVIA = 428, CTRY_LEBANON = 422, CTRY_LIBYA = 434, CTRY_LIECHTENSTEIN = 438, CTRY_LITHUANIA = 440, CTRY_LUXEMBOURG = 442, CTRY_MACAU = 446, CTRY_MACEDONIA = 807, CTRY_MALAYSIA = 458, CTRY_MALTA = 470, CTRY_MEXICO = 484, CTRY_MONACO = 492, CTRY_MOROCCO = 504, CTRY_NEPAL = 524, CTRY_NETHERLANDS = 528, CTRY_NETHERLANDS_ANTILLES = 530, CTRY_NEW_ZEALAND = 554, CTRY_NICARAGUA = 558, CTRY_NORWAY = 578, CTRY_OMAN = 512, CTRY_PAKISTAN = 586, CTRY_PANAMA = 591, CTRY_PAPUA_NEW_GUINEA = 598, CTRY_PARAGUAY = 600, CTRY_PERU = 604, CTRY_PHILIPPINES = 608, CTRY_POLAND = 616, CTRY_PORTUGAL = 620, CTRY_PUERTO_RICO = 630, CTRY_QATAR = 634, CTRY_ROMANIA = 642, CTRY_RUSSIA = 643, CTRY_SAUDI_ARABIA = 682, CTRY_SERBIA_MONTENEGRO = 891, CTRY_SINGAPORE = 702, CTRY_SLOVAKIA = 703, CTRY_SLOVENIA = 705, CTRY_SOUTH_AFRICA = 710, CTRY_SPAIN = 724, CTRY_SRI_LANKA = 144, CTRY_SWEDEN = 752, CTRY_SWITZERLAND = 756, CTRY_SYRIA = 760, CTRY_TAIWAN = 158, CTRY_THAILAND = 764, CTRY_TRINIDAD_Y_TOBAGO = 780, CTRY_TUNISIA = 788, CTRY_TURKEY = 792, CTRY_UAE = 784, CTRY_UKRAINE = 804, CTRY_UNITED_KINGDOM = 826, CTRY_UNITED_STATES = 840, CTRY_UNITED_STATES_FCC49 = 842, CTRY_URUGUAY = 858, CTRY_UZBEKISTAN = 860, CTRY_VENEZUELA = 862, CTRY_VIET_NAM = 704, CTRY_YEMEN = 887, CTRY_ZIMBABWE = 716, CTRY_JAPAN1 = 393, CTRY_JAPAN2 = 394, CTRY_JAPAN3 = 395, CTRY_JAPAN4 = 396, CTRY_JAPAN5 = 397, CTRY_JAPAN6 = 4006, CTRY_JAPAN7 = 4007, CTRY_JAPAN8 = 4008, CTRY_JAPAN9 = 4009, CTRY_JAPAN10 = 4010, CTRY_JAPAN11 = 4011, CTRY_JAPAN12 = 4012, CTRY_JAPAN13 = 4013, CTRY_JAPAN14 = 4014, CTRY_JAPAN15 = 4015, CTRY_JAPAN16 = 4016, CTRY_JAPAN17 = 4017, CTRY_JAPAN18 = 4018, CTRY_JAPAN19 = 4019, CTRY_JAPAN20 = 4020, CTRY_JAPAN21 = 4021, CTRY_JAPAN22 = 4022, CTRY_JAPAN23 = 4023, CTRY_JAPAN24 = 4024, CTRY_JAPAN25 = 4025, CTRY_JAPAN26 = 4026, CTRY_JAPAN27 = 4027, CTRY_JAPAN28 = 4028, CTRY_JAPAN29 = 4029, CTRY_JAPAN30 = 4030, CTRY_JAPAN31 = 4031, CTRY_JAPAN32 = 4032, CTRY_JAPAN33 = 4033, CTRY_JAPAN34 = 4034, CTRY_JAPAN35 = 4035, CTRY_JAPAN36 = 4036, CTRY_JAPAN37 = 4037, CTRY_JAPAN38 = 4038, CTRY_JAPAN39 = 4039, CTRY_JAPAN40 = 4040, CTRY_JAPAN41 = 4041, CTRY_JAPAN42 = 4042, CTRY_JAPAN43 = 4043, CTRY_JAPAN44 = 4044, CTRY_JAPAN45 = 4045, CTRY_JAPAN46 = 4046, CTRY_JAPAN47 = 4047, CTRY_JAPAN48 = 4048, CTRY_JAPAN49 = 4049, CTRY_JAPAN50 = 4050, CTRY_JAPAN51 = 4051, CTRY_JAPAN52 = 4052, CTRY_JAPAN53 = 4053, CTRY_JAPAN54 = 4054, CTRY_JAPAN55 = 4055, CTRY_JAPAN56 = 4056, CTRY_JAPAN57 = 4057, CTRY_JAPAN58 = 4058, CTRY_JAPAN59 = 4059, CTRY_AUSTRALIA2 = 5000, CTRY_CANADA2 = 5001, CTRY_BELGIUM2 = 5002 }; bool ath_is_world_regd(struct ath_regulatory *reg); bool ath_is_49ghz_allowed(u16 redomain); int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, int (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)); u32 ath_regd_get_band_ctl(struct ath_regulatory *reg, enum ieee80211_band band); int ath_reg_notifier_apply(struct wiphy *wiphy, struct regulatory_request *request, struct ath_regulatory *reg); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/regd_common.h0000644000175000017500000003253312026211315023705 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef REGD_COMMON_H #define REGD_COMMON_H enum EnumRd { NO_ENUMRD = 0x00, NULL1_WORLD = 0x03, NULL1_ETSIB = 0x07, NULL1_ETSIC = 0x08, FCC1_FCCA = 0x10, FCC1_WORLD = 0x11, FCC4_FCCA = 0x12, FCC5_FCCA = 0x13, FCC6_FCCA = 0x14, FCC2_FCCA = 0x20, FCC2_WORLD = 0x21, FCC2_ETSIC = 0x22, FCC6_WORLD = 0x23, FRANCE_RES = 0x31, FCC3_FCCA = 0x3A, FCC3_WORLD = 0x3B, ETSI1_WORLD = 0x37, ETSI3_ETSIA = 0x32, ETSI2_WORLD = 0x35, ETSI3_WORLD = 0x36, ETSI4_WORLD = 0x30, ETSI4_ETSIC = 0x38, ETSI5_WORLD = 0x39, ETSI6_WORLD = 0x34, ETSI_RESERVED = 0x33, MKK1_MKKA = 0x40, MKK1_MKKB = 0x41, APL4_WORLD = 0x42, MKK2_MKKA = 0x43, APL_RESERVED = 0x44, APL2_WORLD = 0x45, APL2_APLC = 0x46, APL3_WORLD = 0x47, MKK1_FCCA = 0x48, APL2_APLD = 0x49, MKK1_MKKA1 = 0x4A, MKK1_MKKA2 = 0x4B, MKK1_MKKC = 0x4C, APL3_FCCA = 0x50, APL1_WORLD = 0x52, APL1_FCCA = 0x53, APL1_APLA = 0x54, APL1_ETSIC = 0x55, APL2_ETSIC = 0x56, APL5_WORLD = 0x58, APL6_WORLD = 0x5B, APL7_FCCA = 0x5C, APL8_WORLD = 0x5D, APL9_WORLD = 0x5E, WOR0_WORLD = 0x60, WOR1_WORLD = 0x61, WOR2_WORLD = 0x62, WOR3_WORLD = 0x63, WOR4_WORLD = 0x64, WOR5_ETSIC = 0x65, WOR01_WORLD = 0x66, WOR02_WORLD = 0x67, EU1_WORLD = 0x68, WOR9_WORLD = 0x69, WORA_WORLD = 0x6A, WORB_WORLD = 0x6B, WORC_WORLD = 0x6C, MKK3_MKKB = 0x80, MKK3_MKKA2 = 0x81, MKK3_MKKC = 0x82, MKK4_MKKB = 0x83, MKK4_MKKA2 = 0x84, MKK4_MKKC = 0x85, MKK5_MKKB = 0x86, MKK5_MKKA2 = 0x87, MKK5_MKKC = 0x88, MKK6_MKKB = 0x89, MKK6_MKKA2 = 0x8A, MKK6_MKKC = 0x8B, MKK7_MKKB = 0x8C, MKK7_MKKA2 = 0x8D, MKK7_MKKC = 0x8E, MKK8_MKKB = 0x8F, MKK8_MKKA2 = 0x90, MKK8_MKKC = 0x91, MKK14_MKKA1 = 0x92, MKK15_MKKA1 = 0x93, MKK10_FCCA = 0xD0, MKK10_MKKA1 = 0xD1, MKK10_MKKC = 0xD2, MKK10_MKKA2 = 0xD3, MKK11_MKKA = 0xD4, MKK11_FCCA = 0xD5, MKK11_MKKA1 = 0xD6, MKK11_MKKC = 0xD7, MKK11_MKKA2 = 0xD8, MKK12_MKKA = 0xD9, MKK12_FCCA = 0xDA, MKK12_MKKA1 = 0xDB, MKK12_MKKC = 0xDC, MKK12_MKKA2 = 0xDD, MKK13_MKKB = 0xDE, MKK3_MKKA = 0xF0, MKK3_MKKA1 = 0xF1, MKK3_FCCA = 0xF2, MKK4_MKKA = 0xF3, MKK4_MKKA1 = 0xF4, MKK4_FCCA = 0xF5, MKK9_MKKA = 0xF6, MKK10_MKKA = 0xF7, MKK6_MKKA1 = 0xF8, MKK6_FCCA = 0xF9, MKK7_MKKA1 = 0xFA, MKK7_FCCA = 0xFB, MKK9_FCCA = 0xFC, MKK9_MKKA1 = 0xFD, MKK9_MKKC = 0xFE, MKK9_MKKA2 = 0xFF, WORLD = 0x0199, DEBUG_REG_DMN = 0x01ff, }; /* Regpair to CTL band mapping */ static struct reg_dmn_pair_mapping regDomainPairs[] = { /* regpair, 5 GHz CTL, 2 GHz CTL */ {NO_ENUMRD, DEBUG_REG_DMN, DEBUG_REG_DMN}, {NULL1_WORLD, NO_CTL, CTL_ETSI}, {NULL1_ETSIB, NO_CTL, CTL_ETSI}, {NULL1_ETSIC, NO_CTL, CTL_ETSI}, {FCC2_FCCA, CTL_FCC, CTL_FCC}, {FCC2_WORLD, CTL_FCC, CTL_ETSI}, {FCC2_ETSIC, CTL_FCC, CTL_ETSI}, {FCC3_FCCA, CTL_FCC, CTL_FCC}, {FCC3_WORLD, CTL_FCC, CTL_ETSI}, {FCC4_FCCA, CTL_FCC, CTL_FCC}, {FCC5_FCCA, CTL_FCC, CTL_FCC}, {FCC6_FCCA, CTL_FCC, CTL_FCC}, {FCC6_WORLD, CTL_FCC, CTL_ETSI}, {ETSI1_WORLD, CTL_ETSI, CTL_ETSI}, {ETSI2_WORLD, CTL_ETSI, CTL_ETSI}, {ETSI3_WORLD, CTL_ETSI, CTL_ETSI}, {ETSI4_WORLD, CTL_ETSI, CTL_ETSI}, {ETSI5_WORLD, CTL_ETSI, CTL_ETSI}, {ETSI6_WORLD, CTL_ETSI, CTL_ETSI}, /* XXX: For ETSI3_ETSIA, Was NO_CTL meant for the 2 GHz band ? */ {ETSI3_ETSIA, CTL_ETSI, CTL_ETSI}, {FRANCE_RES, CTL_ETSI, CTL_ETSI}, {FCC1_WORLD, CTL_FCC, CTL_ETSI}, {FCC1_FCCA, CTL_FCC, CTL_FCC}, {APL1_WORLD, CTL_FCC, CTL_ETSI}, {APL2_WORLD, CTL_FCC, CTL_ETSI}, {APL3_WORLD, CTL_FCC, CTL_ETSI}, {APL4_WORLD, CTL_FCC, CTL_ETSI}, {APL5_WORLD, CTL_FCC, CTL_ETSI}, {APL6_WORLD, CTL_ETSI, CTL_ETSI}, {APL8_WORLD, CTL_ETSI, CTL_ETSI}, {APL9_WORLD, CTL_ETSI, CTL_ETSI}, {APL3_FCCA, CTL_FCC, CTL_FCC}, {APL7_FCCA, CTL_FCC, CTL_FCC}, {APL1_ETSIC, CTL_FCC, CTL_ETSI}, {APL2_ETSIC, CTL_FCC, CTL_ETSI}, {APL2_APLD, CTL_FCC, NO_CTL}, {MKK1_MKKA, CTL_MKK, CTL_MKK}, {MKK1_MKKB, CTL_MKK, CTL_MKK}, {MKK1_FCCA, CTL_MKK, CTL_FCC}, {MKK1_MKKA1, CTL_MKK, CTL_MKK}, {MKK1_MKKA2, CTL_MKK, CTL_MKK}, {MKK1_MKKC, CTL_MKK, CTL_MKK}, {MKK2_MKKA, CTL_MKK, CTL_MKK}, {MKK3_MKKA, CTL_MKK, CTL_MKK}, {MKK3_MKKB, CTL_MKK, CTL_MKK}, {MKK3_MKKA1, CTL_MKK, CTL_MKK}, {MKK3_MKKA2, CTL_MKK, CTL_MKK}, {MKK3_MKKC, CTL_MKK, CTL_MKK}, {MKK3_FCCA, CTL_MKK, CTL_FCC}, {MKK4_MKKA, CTL_MKK, CTL_MKK}, {MKK4_MKKB, CTL_MKK, CTL_MKK}, {MKK4_MKKA1, CTL_MKK, CTL_MKK}, {MKK4_MKKA2, CTL_MKK, CTL_MKK}, {MKK4_MKKC, CTL_MKK, CTL_MKK}, {MKK4_FCCA, CTL_MKK, CTL_FCC}, {MKK5_MKKB, CTL_MKK, CTL_MKK}, {MKK5_MKKA2, CTL_MKK, CTL_MKK}, {MKK5_MKKC, CTL_MKK, CTL_MKK}, {MKK6_MKKB, CTL_MKK, CTL_MKK}, {MKK6_MKKA1, CTL_MKK, CTL_MKK}, {MKK6_MKKA2, CTL_MKK, CTL_MKK}, {MKK6_MKKC, CTL_MKK, CTL_MKK}, {MKK6_FCCA, CTL_MKK, CTL_FCC}, {MKK7_MKKB, CTL_MKK, CTL_MKK}, {MKK7_MKKA1, CTL_MKK, CTL_MKK}, {MKK7_MKKA2, CTL_MKK, CTL_MKK}, {MKK7_MKKC, CTL_MKK, CTL_MKK}, {MKK7_FCCA, CTL_MKK, CTL_FCC}, {MKK8_MKKB, CTL_MKK, CTL_MKK}, {MKK8_MKKA2, CTL_MKK, CTL_MKK}, {MKK8_MKKC, CTL_MKK, CTL_MKK}, {MKK9_MKKA, CTL_MKK, CTL_MKK}, {MKK9_FCCA, CTL_MKK, CTL_FCC}, {MKK9_MKKA1, CTL_MKK, CTL_MKK}, {MKK9_MKKA2, CTL_MKK, CTL_MKK}, {MKK9_MKKC, CTL_MKK, CTL_MKK}, {MKK10_MKKA, CTL_MKK, CTL_MKK}, {MKK10_FCCA, CTL_MKK, CTL_FCC}, {MKK10_MKKA1, CTL_MKK, CTL_MKK}, {MKK10_MKKA2, CTL_MKK, CTL_MKK}, {MKK10_MKKC, CTL_MKK, CTL_MKK}, {MKK11_MKKA, CTL_MKK, CTL_MKK}, {MKK11_FCCA, CTL_MKK, CTL_FCC}, {MKK11_MKKA1, CTL_MKK, CTL_MKK}, {MKK11_MKKA2, CTL_MKK, CTL_MKK}, {MKK11_MKKC, CTL_MKK, CTL_MKK}, {MKK12_MKKA, CTL_MKK, CTL_MKK}, {MKK12_FCCA, CTL_MKK, CTL_FCC}, {MKK12_MKKA1, CTL_MKK, CTL_MKK}, {MKK12_MKKA2, CTL_MKK, CTL_MKK}, {MKK12_MKKC, CTL_MKK, CTL_MKK}, {MKK13_MKKB, CTL_MKK, CTL_MKK}, {MKK14_MKKA1, CTL_MKK, CTL_MKK}, {MKK15_MKKA1, CTL_MKK, CTL_MKK}, {WOR0_WORLD, NO_CTL, NO_CTL}, {WOR1_WORLD, NO_CTL, NO_CTL}, {WOR2_WORLD, NO_CTL, NO_CTL}, {WOR3_WORLD, NO_CTL, NO_CTL}, {WOR4_WORLD, NO_CTL, NO_CTL}, {WOR5_ETSIC, NO_CTL, NO_CTL}, {WOR01_WORLD, NO_CTL, NO_CTL}, {WOR02_WORLD, NO_CTL, NO_CTL}, {EU1_WORLD, NO_CTL, NO_CTL}, {WOR9_WORLD, NO_CTL, NO_CTL}, {WORA_WORLD, NO_CTL, NO_CTL}, {WORB_WORLD, NO_CTL, NO_CTL}, {WORC_WORLD, NO_CTL, NO_CTL}, }; static struct country_code_to_enum_rd allCountries[] = { {CTRY_DEBUG, NO_ENUMRD, "DB"}, {CTRY_DEFAULT, FCC1_FCCA, "CO"}, {CTRY_ALBANIA, NULL1_WORLD, "AL"}, {CTRY_ALGERIA, NULL1_WORLD, "DZ"}, {CTRY_ARGENTINA, FCC3_WORLD, "AR"}, {CTRY_ARMENIA, ETSI4_WORLD, "AM"}, {CTRY_ARUBA, ETSI1_WORLD, "AW"}, {CTRY_AUSTRALIA, FCC2_WORLD, "AU"}, {CTRY_AUSTRALIA2, FCC6_WORLD, "AU"}, {CTRY_AUSTRIA, ETSI1_WORLD, "AT"}, {CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ"}, {CTRY_BAHRAIN, APL6_WORLD, "BH"}, {CTRY_BANGLADESH, NULL1_WORLD, "BD"}, {CTRY_BARBADOS, FCC2_WORLD, "BB"}, {CTRY_BELARUS, ETSI1_WORLD, "BY"}, {CTRY_BELGIUM, ETSI1_WORLD, "BE"}, {CTRY_BELGIUM2, ETSI4_WORLD, "BL"}, {CTRY_BELIZE, APL1_ETSIC, "BZ"}, {CTRY_BOLIVIA, APL1_ETSIC, "BO"}, {CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA"}, {CTRY_BRAZIL, FCC3_WORLD, "BR"}, {CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN"}, {CTRY_BULGARIA, ETSI6_WORLD, "BG"}, {CTRY_CAMBODIA, ETSI1_WORLD, "KH"}, {CTRY_CANADA, FCC3_FCCA, "CA"}, {CTRY_CANADA2, FCC6_FCCA, "CA"}, {CTRY_CHILE, APL6_WORLD, "CL"}, {CTRY_CHINA, APL1_WORLD, "CN"}, {CTRY_COLOMBIA, FCC1_FCCA, "CO"}, {CTRY_COSTA_RICA, FCC1_WORLD, "CR"}, {CTRY_CROATIA, ETSI1_WORLD, "HR"}, {CTRY_CYPRUS, ETSI1_WORLD, "CY"}, {CTRY_CZECH, ETSI3_WORLD, "CZ"}, {CTRY_DENMARK, ETSI1_WORLD, "DK"}, {CTRY_DOMINICAN_REPUBLIC, FCC1_FCCA, "DO"}, {CTRY_ECUADOR, FCC1_WORLD, "EC"}, {CTRY_EGYPT, ETSI3_WORLD, "EG"}, {CTRY_EL_SALVADOR, FCC1_WORLD, "SV"}, {CTRY_ESTONIA, ETSI1_WORLD, "EE"}, {CTRY_FINLAND, ETSI1_WORLD, "FI"}, {CTRY_FRANCE, ETSI1_WORLD, "FR"}, {CTRY_GEORGIA, ETSI4_WORLD, "GE"}, {CTRY_GERMANY, ETSI1_WORLD, "DE"}, {CTRY_GREECE, ETSI1_WORLD, "GR"}, {CTRY_GREENLAND, ETSI1_WORLD, "GL"}, {CTRY_GRENADA, FCC3_FCCA, "GD"}, {CTRY_GUAM, FCC1_FCCA, "GU"}, {CTRY_GUATEMALA, FCC1_FCCA, "GT"}, {CTRY_HAITI, ETSI1_WORLD, "HT"}, {CTRY_HONDURAS, NULL1_WORLD, "HN"}, {CTRY_HONG_KONG, FCC3_WORLD, "HK"}, {CTRY_HUNGARY, ETSI1_WORLD, "HU"}, {CTRY_ICELAND, ETSI1_WORLD, "IS"}, {CTRY_INDIA, APL6_WORLD, "IN"}, {CTRY_INDONESIA, NULL1_WORLD, "ID"}, {CTRY_IRAN, APL1_WORLD, "IR"}, {CTRY_IRELAND, ETSI1_WORLD, "IE"}, {CTRY_ISRAEL, NULL1_WORLD, "IL"}, {CTRY_ITALY, ETSI1_WORLD, "IT"}, {CTRY_JAMAICA, FCC3_WORLD, "JM"}, {CTRY_JAPAN, MKK1_MKKA, "JP"}, {CTRY_JAPAN1, MKK1_MKKB, "JP"}, {CTRY_JAPAN2, MKK1_FCCA, "JP"}, {CTRY_JAPAN3, MKK2_MKKA, "JP"}, {CTRY_JAPAN4, MKK1_MKKA1, "JP"}, {CTRY_JAPAN5, MKK1_MKKA2, "JP"}, {CTRY_JAPAN6, MKK1_MKKC, "JP"}, {CTRY_JAPAN7, MKK3_MKKB, "JP"}, {CTRY_JAPAN8, MKK3_MKKA2, "JP"}, {CTRY_JAPAN9, MKK3_MKKC, "JP"}, {CTRY_JAPAN10, MKK4_MKKB, "JP"}, {CTRY_JAPAN11, MKK4_MKKA2, "JP"}, {CTRY_JAPAN12, MKK4_MKKC, "JP"}, {CTRY_JAPAN13, MKK5_MKKB, "JP"}, {CTRY_JAPAN14, MKK5_MKKA2, "JP"}, {CTRY_JAPAN15, MKK5_MKKC, "JP"}, {CTRY_JAPAN16, MKK6_MKKB, "JP"}, {CTRY_JAPAN17, MKK6_MKKA2, "JP"}, {CTRY_JAPAN18, MKK6_MKKC, "JP"}, {CTRY_JAPAN19, MKK7_MKKB, "JP"}, {CTRY_JAPAN20, MKK7_MKKA2, "JP"}, {CTRY_JAPAN21, MKK7_MKKC, "JP"}, {CTRY_JAPAN22, MKK8_MKKB, "JP"}, {CTRY_JAPAN23, MKK8_MKKA2, "JP"}, {CTRY_JAPAN24, MKK8_MKKC, "JP"}, {CTRY_JAPAN25, MKK3_MKKA, "JP"}, {CTRY_JAPAN26, MKK3_MKKA1, "JP"}, {CTRY_JAPAN27, MKK3_FCCA, "JP"}, {CTRY_JAPAN28, MKK4_MKKA1, "JP"}, {CTRY_JAPAN29, MKK4_FCCA, "JP"}, {CTRY_JAPAN30, MKK6_MKKA1, "JP"}, {CTRY_JAPAN31, MKK6_FCCA, "JP"}, {CTRY_JAPAN32, MKK7_MKKA1, "JP"}, {CTRY_JAPAN33, MKK7_FCCA, "JP"}, {CTRY_JAPAN34, MKK9_MKKA, "JP"}, {CTRY_JAPAN35, MKK10_MKKA, "JP"}, {CTRY_JAPAN36, MKK4_MKKA, "JP"}, {CTRY_JAPAN37, MKK9_FCCA, "JP"}, {CTRY_JAPAN38, MKK9_MKKA1, "JP"}, {CTRY_JAPAN39, MKK9_MKKC, "JP"}, {CTRY_JAPAN40, MKK9_MKKA2, "JP"}, {CTRY_JAPAN41, MKK10_FCCA, "JP"}, {CTRY_JAPAN42, MKK10_MKKA1, "JP"}, {CTRY_JAPAN43, MKK10_MKKC, "JP"}, {CTRY_JAPAN44, MKK10_MKKA2, "JP"}, {CTRY_JAPAN45, MKK11_MKKA, "JP"}, {CTRY_JAPAN46, MKK11_FCCA, "JP"}, {CTRY_JAPAN47, MKK11_MKKA1, "JP"}, {CTRY_JAPAN48, MKK11_MKKC, "JP"}, {CTRY_JAPAN49, MKK11_MKKA2, "JP"}, {CTRY_JAPAN50, MKK12_MKKA, "JP"}, {CTRY_JAPAN51, MKK12_FCCA, "JP"}, {CTRY_JAPAN52, MKK12_MKKA1, "JP"}, {CTRY_JAPAN53, MKK12_MKKC, "JP"}, {CTRY_JAPAN54, MKK12_MKKA2, "JP"}, {CTRY_JAPAN57, MKK13_MKKB, "JP"}, {CTRY_JAPAN58, MKK14_MKKA1, "JP"}, {CTRY_JAPAN59, MKK15_MKKA1, "JP"}, {CTRY_JORDAN, ETSI2_WORLD, "JO"}, {CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ"}, {CTRY_KOREA_NORTH, APL9_WORLD, "KP"}, {CTRY_KOREA_ROC, APL9_WORLD, "KR"}, {CTRY_KOREA_ROC2, APL2_WORLD, "K2"}, {CTRY_KOREA_ROC3, APL9_WORLD, "K3"}, {CTRY_KUWAIT, ETSI3_WORLD, "KW"}, {CTRY_LATVIA, ETSI1_WORLD, "LV"}, {CTRY_LEBANON, NULL1_WORLD, "LB"}, {CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI"}, {CTRY_LITHUANIA, ETSI1_WORLD, "LT"}, {CTRY_LUXEMBOURG, ETSI1_WORLD, "LU"}, {CTRY_MACAU, FCC2_WORLD, "MO"}, {CTRY_MACEDONIA, NULL1_WORLD, "MK"}, {CTRY_MALAYSIA, APL8_WORLD, "MY"}, {CTRY_MALTA, ETSI1_WORLD, "MT"}, {CTRY_MEXICO, FCC1_FCCA, "MX"}, {CTRY_MONACO, ETSI4_WORLD, "MC"}, {CTRY_MOROCCO, APL4_WORLD, "MA"}, {CTRY_NEPAL, APL1_WORLD, "NP"}, {CTRY_NETHERLANDS, ETSI1_WORLD, "NL"}, {CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN"}, {CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ"}, {CTRY_NORWAY, ETSI1_WORLD, "NO"}, {CTRY_OMAN, FCC3_WORLD, "OM"}, {CTRY_PAKISTAN, NULL1_WORLD, "PK"}, {CTRY_PANAMA, FCC1_FCCA, "PA"}, {CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG"}, {CTRY_PERU, APL1_WORLD, "PE"}, {CTRY_PHILIPPINES, APL1_WORLD, "PH"}, {CTRY_POLAND, ETSI1_WORLD, "PL"}, {CTRY_PORTUGAL, ETSI1_WORLD, "PT"}, {CTRY_PUERTO_RICO, FCC1_FCCA, "PR"}, {CTRY_QATAR, APL1_WORLD, "QA"}, {CTRY_ROMANIA, NULL1_WORLD, "RO"}, {CTRY_RUSSIA, NULL1_WORLD, "RU"}, {CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA"}, {CTRY_SERBIA_MONTENEGRO, ETSI1_WORLD, "CS"}, {CTRY_SINGAPORE, APL6_WORLD, "SG"}, {CTRY_SLOVAKIA, ETSI1_WORLD, "SK"}, {CTRY_SLOVENIA, ETSI1_WORLD, "SI"}, {CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA"}, {CTRY_SPAIN, ETSI1_WORLD, "ES"}, {CTRY_SRI_LANKA, FCC3_WORLD, "LK"}, {CTRY_SWEDEN, ETSI1_WORLD, "SE"}, {CTRY_SWITZERLAND, ETSI1_WORLD, "CH"}, {CTRY_SYRIA, NULL1_WORLD, "SY"}, {CTRY_TAIWAN, APL3_FCCA, "TW"}, {CTRY_THAILAND, FCC3_WORLD, "TH"}, {CTRY_TRINIDAD_Y_TOBAGO, FCC3_WORLD, "TT"}, {CTRY_TUNISIA, ETSI3_WORLD, "TN"}, {CTRY_TURKEY, ETSI3_WORLD, "TR"}, {CTRY_UKRAINE, NULL1_WORLD, "UA"}, {CTRY_UAE, NULL1_WORLD, "AE"}, {CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"}, {CTRY_UNITED_STATES, FCC3_FCCA, "US"}, /* This "PS" is for US public safety actually... to support this we * would need to assign new special alpha2 to CRDA db as with the world * regdomain and use another alpha2 */ {CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS"}, {CTRY_URUGUAY, FCC3_WORLD, "UY"}, {CTRY_UZBEKISTAN, FCC3_FCCA, "UZ"}, {CTRY_VENEZUELA, APL2_ETSIC, "VE"}, {CTRY_VIET_NAM, NULL1_WORLD, "VN"}, {CTRY_YEMEN, NULL1_WORLD, "YE"}, {CTRY_ZIMBABWE, NULL1_WORLD, "ZW"}, }; #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/key.c0000644000175000017500000004164312026211315022201 0ustar mcgrofmcgrof/* * Copyright (c) 2009 Atheros Communications Inc. * Copyright (c) 2010 Bruno Randolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "ath.h" #include "reg.h" #define REG_READ (common->ops->read) #define REG_WRITE(_ah, _reg, _val) (common->ops->write)(_ah, _val, _reg) #define ENABLE_REGWRITE_BUFFER(_ah) \ if (common->ops->enable_write_buffer) \ common->ops->enable_write_buffer((_ah)); #define REGWRITE_BUFFER_FLUSH(_ah) \ if (common->ops->write_flush) \ common->ops->write_flush((_ah)); #define IEEE80211_WEP_NKID 4 /* number of key ids */ /************************/ /* Key Cache Management */ /************************/ bool ath_hw_keyreset(struct ath_common *common, u16 entry) { u32 keyType; void *ah = common->ah; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry)); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); if (keyType == AR_KEYTABLE_TYPE_TKIP) { u16 micentry = entry + 64; REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR); } } REGWRITE_BUFFER_FLUSH(ah); return true; } EXPORT_SYMBOL(ath_hw_keyreset); static bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac) { u32 macHi, macLo; u32 unicast_flag = AR_KEYTABLE_VALID; void *ah = common->ah; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } if (mac != NULL) { /* * AR_KEYTABLE_VALID indicates that the address is a unicast * address, which must match the transmitter address for * decrypting frames. * Not setting this bit allows the hardware to use the key * for multicast frame decryption. */ if (mac[0] & 0x01) unicast_flag = 0; macLo = get_unaligned_le32(mac); macHi = get_unaligned_le16(mac + 4); macLo >>= 1; macLo |= (macHi & 1) << 31; macHi >>= 1; } else { macLo = macHi = 0; } ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo); REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag); REGWRITE_BUFFER_FLUSH(ah); return true; } static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry, const struct ath_keyval *k, const u8 *mac) { void *ah = common->ah; u32 key0, key1, key2, key3, key4; u32 keyType; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } switch (k->kv_type) { case ATH_CIPHER_AES_OCB: keyType = AR_KEYTABLE_TYPE_AES; break; case ATH_CIPHER_AES_CCM: if (!(common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)) { ath_dbg(common, ANY, "AES-CCM not supported by this mac rev\n"); return false; } keyType = AR_KEYTABLE_TYPE_CCM; break; case ATH_CIPHER_TKIP: keyType = AR_KEYTABLE_TYPE_TKIP; if (entry + 64 >= common->keymax) { ath_dbg(common, ANY, "entry %u inappropriate for TKIP\n", entry); return false; } break; case ATH_CIPHER_WEP: if (k->kv_len < WLAN_KEY_LEN_WEP40) { ath_dbg(common, ANY, "WEP key length %u too small\n", k->kv_len); return false; } if (k->kv_len <= WLAN_KEY_LEN_WEP40) keyType = AR_KEYTABLE_TYPE_40; else if (k->kv_len <= WLAN_KEY_LEN_WEP104) keyType = AR_KEYTABLE_TYPE_104; else keyType = AR_KEYTABLE_TYPE_128; break; case ATH_CIPHER_CLR: keyType = AR_KEYTABLE_TYPE_CLR; break; default: ath_err(common, "cipher %u not supported\n", k->kv_type); return false; } key0 = get_unaligned_le32(k->kv_val + 0); key1 = get_unaligned_le16(k->kv_val + 4); key2 = get_unaligned_le32(k->kv_val + 6); key3 = get_unaligned_le16(k->kv_val + 10); key4 = get_unaligned_le32(k->kv_val + 12); if (k->kv_len <= WLAN_KEY_LEN_WEP104) key4 &= 0xff; /* * Note: Key cache registers access special memory area that requires * two 32-bit writes to actually update the values in the internal * memory. Consequently, the exact order and pairs used here must be * maintained. */ if (keyType == AR_KEYTABLE_TYPE_TKIP) { u16 micentry = entry + 64; /* * Write inverted key[47:0] first to avoid Michael MIC errors * on frames that could be sent or received at the same time. * The correct key will be written in the end once everything * else is ready. */ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); /* Write key[95:48] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); /* Write key[127:96] and key type */ REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); /* Write MAC address for the entry */ (void) ath_hw_keysetmac(common, entry, mac); if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { /* * TKIP uses two key cache entries: * Michael MIC TX/RX keys in the same key cache entry * (idx = main index + 64): * key0 [31:0] = RX key [31:0] * key1 [15:0] = TX key [31:16] * key1 [31:16] = reserved * key2 [31:0] = RX key [63:32] * key3 [15:0] = TX key [15:0] * key3 [31:16] = reserved * key4 [31:0] = TX key [63:32] */ u32 mic0, mic1, mic2, mic3, mic4; mic0 = get_unaligned_le32(k->kv_mic + 0); mic2 = get_unaligned_le32(k->kv_mic + 4); mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff; mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff; mic4 = get_unaligned_le32(k->kv_txmic + 4); ENABLE_REGWRITE_BUFFER(ah); /* Write RX[31:0] and TX[31:16] */ REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); /* Write RX[63:32] and TX[15:0] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); /* Write TX[63:32] and keyType(reserved) */ REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR); REGWRITE_BUFFER_FLUSH(ah); } else { /* * TKIP uses four key cache entries (two for group * keys): * Michael MIC TX/RX keys are in different key cache * entries (idx = main index + 64 for TX and * main index + 32 + 96 for RX): * key0 [31:0] = TX/RX MIC key [31:0] * key1 [31:0] = reserved * key2 [31:0] = TX/RX MIC key [63:32] * key3 [31:0] = reserved * key4 [31:0] = reserved * * Upper layer code will call this function separately * for TX and RX keys when these registers offsets are * used. */ u32 mic0, mic2; mic0 = get_unaligned_le32(k->kv_mic + 0); mic2 = get_unaligned_le32(k->kv_mic + 4); ENABLE_REGWRITE_BUFFER(ah); /* Write MIC key[31:0] */ REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); /* Write MIC key[63:32] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); /* Write TX[63:32] and keyType(reserved) */ REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR); REGWRITE_BUFFER_FLUSH(ah); } ENABLE_REGWRITE_BUFFER(ah); /* MAC address registers are reserved for the MIC entry */ REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); /* * Write the correct (un-inverted) key[47:0] last to enable * TKIP now that all other registers are set with correct * values. */ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); REGWRITE_BUFFER_FLUSH(ah); } else { ENABLE_REGWRITE_BUFFER(ah); /* Write key[47:0] */ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); /* Write key[95:48] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); /* Write key[127:96] and key type */ REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); REGWRITE_BUFFER_FLUSH(ah); /* Write MAC address for the entry */ (void) ath_hw_keysetmac(common, entry, mac); } return true; } static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, struct ath_keyval *hk, const u8 *addr, bool authenticator) { const u8 *key_rxmic; const u8 *key_txmic; key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; if (addr == NULL) { /* * Group key installation - only two key cache entries are used * regardless of splitmic capability since group key is only * used either for TX or RX. */ if (authenticator) { memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); } else { memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); } return ath_hw_set_keycache_entry(common, keyix, hk, addr); } if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { /* TX and RX keys share the same key cache entry. */ memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); return ath_hw_set_keycache_entry(common, keyix, hk, addr); } /* Separate key cache entries for TX and RX */ /* TX key goes at first index, RX key at +32. */ memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); if (!ath_hw_set_keycache_entry(common, keyix, hk, NULL)) { /* TX MIC entry failed. No need to proceed further */ ath_err(common, "Setting TX MIC Key Failed\n"); return 0; } memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); /* XXX delete tx key on failure? */ return ath_hw_set_keycache_entry(common, keyix + 32, hk, addr); } static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) { int i; for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { if (test_bit(i, common->keymap) || test_bit(i + 64, common->keymap)) continue; /* At least one part of TKIP key allocated */ if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) && (test_bit(i + 32, common->keymap) || test_bit(i + 64 + 32, common->keymap))) continue; /* At least one part of TKIP key allocated */ /* Found a free slot for a TKIP key */ return i; } return -1; } static int ath_reserve_key_cache_slot(struct ath_common *common, u32 cipher) { int i; if (cipher == WLAN_CIPHER_SUITE_TKIP) return ath_reserve_key_cache_slot_tkip(common); /* First, try to find slots that would not be available for TKIP. */ if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { if (!test_bit(i, common->keymap) && (test_bit(i + 32, common->keymap) || test_bit(i + 64, common->keymap) || test_bit(i + 64 + 32, common->keymap))) return i; if (!test_bit(i + 32, common->keymap) && (test_bit(i, common->keymap) || test_bit(i + 64, common->keymap) || test_bit(i + 64 + 32, common->keymap))) return i + 32; if (!test_bit(i + 64, common->keymap) && (test_bit(i , common->keymap) || test_bit(i + 32, common->keymap) || test_bit(i + 64 + 32, common->keymap))) return i + 64; if (!test_bit(i + 64 + 32, common->keymap) && (test_bit(i, common->keymap) || test_bit(i + 32, common->keymap) || test_bit(i + 64, common->keymap))) return i + 64 + 32; } } else { for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { if (!test_bit(i, common->keymap) && test_bit(i + 64, common->keymap)) return i; if (test_bit(i, common->keymap) && !test_bit(i + 64, common->keymap)) return i + 64; } } /* No partially used TKIP slots, pick any available slot */ for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) { /* Do not allow slots that could be needed for TKIP group keys * to be used. This limitation could be removed if we know that * TKIP will not be used. */ if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) continue; if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) continue; if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) continue; } if (!test_bit(i, common->keymap)) return i; /* Found a free slot for a key */ } /* No free slot found */ return -1; } /* * Configure encryption in the HW. */ int ath_key_config(struct ath_common *common, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath_keyval hk; const u8 *mac = NULL; u8 gmac[ETH_ALEN]; int ret = 0; int idx; memset(&hk, 0, sizeof(hk)); switch (key->cipher) { case 0: hk.kv_type = ATH_CIPHER_CLR; break; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: hk.kv_type = ATH_CIPHER_WEP; break; case WLAN_CIPHER_SUITE_TKIP: hk.kv_type = ATH_CIPHER_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: hk.kv_type = ATH_CIPHER_AES_CCM; break; default: return -EOPNOTSUPP; } hk.kv_len = key->keylen; if (key->keylen) memcpy(hk.kv_val, key->key, key->keylen); if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { switch (vif->type) { case NL80211_IFTYPE_AP: memcpy(gmac, vif->addr, ETH_ALEN); gmac[0] |= 0x01; mac = gmac; idx = ath_reserve_key_cache_slot(common, key->cipher); break; case NL80211_IFTYPE_ADHOC: if (!sta) { idx = key->keyidx; break; } memcpy(gmac, sta->addr, ETH_ALEN); gmac[0] |= 0x01; mac = gmac; idx = ath_reserve_key_cache_slot(common, key->cipher); break; default: idx = key->keyidx; break; } } else if (key->keyidx) { if (WARN_ON(!sta)) return -EOPNOTSUPP; mac = sta->addr; if (vif->type != NL80211_IFTYPE_AP) { /* Only keyidx 0 should be used with unicast key, but * allow this for client mode for now. */ idx = key->keyidx; } else return -EIO; } else { if (WARN_ON(!sta)) return -EOPNOTSUPP; mac = sta->addr; idx = ath_reserve_key_cache_slot(common, key->cipher); } if (idx < 0) return -ENOSPC; /* no free key cache entries */ if (key->cipher == WLAN_CIPHER_SUITE_TKIP) ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, vif->type == NL80211_IFTYPE_AP); else ret = ath_hw_set_keycache_entry(common, idx, &hk, mac); if (!ret) return -EIO; set_bit(idx, common->keymap); if (key->cipher == WLAN_CIPHER_SUITE_CCMP) set_bit(idx, common->ccmp_keymap); if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { set_bit(idx + 64, common->keymap); set_bit(idx, common->tkip_keymap); set_bit(idx + 64, common->tkip_keymap); if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { set_bit(idx + 32, common->keymap); set_bit(idx + 64 + 32, common->keymap); set_bit(idx + 32, common->tkip_keymap); set_bit(idx + 64 + 32, common->tkip_keymap); } } return idx; } EXPORT_SYMBOL(ath_key_config); /* * Delete Key. */ void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key) { ath_hw_keyreset(common, key->hw_key_idx); if (key->hw_key_idx < IEEE80211_WEP_NKID) return; clear_bit(key->hw_key_idx, common->keymap); clear_bit(key->hw_key_idx, common->ccmp_keymap); if (key->cipher != WLAN_CIPHER_SUITE_TKIP) return; clear_bit(key->hw_key_idx + 64, common->keymap); clear_bit(key->hw_key_idx, common->tkip_keymap); clear_bit(key->hw_key_idx + 64, common->tkip_keymap); if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { ath_hw_keyreset(common, key->hw_key_idx + 32); clear_bit(key->hw_key_idx + 32, common->keymap); clear_bit(key->hw_key_idx + 64 + 32, common->keymap); clear_bit(key->hw_key_idx + 32, common->tkip_keymap); clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap); } } EXPORT_SYMBOL(ath_key_delete); compat-drivers-2012-09-18/drivers/net/wireless/ath/hw.c0000644000175000017500000001422312026211315022021 0ustar mcgrofmcgrof/* * Copyright (c) 2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "ath.h" #include "reg.h" #define REG_READ (common->ops->read) #define REG_WRITE (common->ops->write) /** * ath_hw_set_bssid_mask - filter out bssids we listen * * @common: the ath_common struct for the device. * * BSSID masking is a method used by AR5212 and newer hardware to inform PCU * which bits of the interface's MAC address should be looked at when trying * to decide which packets to ACK. In station mode and AP mode with a single * BSS every bit matters since we lock to only one BSS. In AP mode with * multiple BSSes (virtual interfaces) not every bit matters because hw must * accept frames for all BSSes and so we tweak some bits of our mac address * in order to have multiple BSSes. * * NOTE: This is a simple filter and does *not* filter out all * relevant frames. Some frames that are not for us might get ACKed from us * by PCU because they just match the mask. * * When handling multiple BSSes you can get the BSSID mask by computing the * set of ~ ( MAC XOR BSSID ) for all bssids we handle. * * When you do this you are essentially computing the common bits of all your * BSSes. Later it is assumed the hardware will "and" (&) the BSSID mask with * the MAC address to obtain the relevant bits and compare the result with * (frame's BSSID & mask) to see if they match. * * Simple example: on your card you have have two BSSes you have created with * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. * There is another BSSID-03 but you are not part of it. For simplicity's sake, * assuming only 4 bits for a mac address and for BSSIDs you can then have: * * \ * MAC: 0001 | * BSSID-01: 0100 | --> Belongs to us * BSSID-02: 1001 | * / * ------------------- * BSSID-03: 0110 | --> External * ------------------- * * Our bssid_mask would then be: * * On loop iteration for BSSID-01: * ~(0001 ^ 0100) -> ~(0101) * -> 1010 * bssid_mask = 1010 * * On loop iteration for BSSID-02: * bssid_mask &= ~(0001 ^ 1001) * bssid_mask = (1010) & ~(0001 ^ 1001) * bssid_mask = (1010) & ~(1000) * bssid_mask = (1010) & (0111) * bssid_mask = 0010 * * A bssid_mask of 0010 means "only pay attention to the second least * significant bit". This is because its the only bit common * amongst the MAC and all BSSIDs we support. To findout what the real * common bit is we can simply "&" the bssid_mask now with any BSSID we have * or our MAC address (we assume the hardware uses the MAC address). * * Now, suppose there's an incoming frame for BSSID-03: * * IFRAME-01: 0110 * * An easy eye-inspeciton of this already should tell you that this frame * will not pass our check. This is because the bssid_mask tells the * hardware to only look at the second least significant bit and the * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB * as 1, which does not match 0. * * So with IFRAME-01 we *assume* the hardware will do: * * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; * --> allow = (0010) == 0000 ? 1 : 0; * --> allow = 0 * * Lets now test a frame that should work: * * IFRAME-02: 0001 (we should allow) * * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; * --> allow = (0000) == (0000) * --> allow = 1 * * Other examples: * * IFRAME-03: 0100 --> allowed * IFRAME-04: 1001 --> allowed * IFRAME-05: 1101 --> allowed but its not for us!!! * */ void ath_hw_setbssidmask(struct ath_common *common) { void *ah = common->ah; REG_WRITE(ah, get_unaligned_le32(common->bssidmask), AR_BSSMSKL); REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU); } EXPORT_SYMBOL(ath_hw_setbssidmask); /** * ath_hw_cycle_counters_update - common function to update cycle counters * * @common: the ath_common struct for the device. * * This function is used to update all cycle counters in one place. * It has to be called while holding common->cc_lock! */ void ath_hw_cycle_counters_update(struct ath_common *common) { u32 cycles, busy, rx, tx; void *ah = common->ah; /* freeze */ REG_WRITE(ah, AR_MIBC_FMC, AR_MIBC); /* read */ cycles = REG_READ(ah, AR_CCCNT); busy = REG_READ(ah, AR_RCCNT); rx = REG_READ(ah, AR_RFCNT); tx = REG_READ(ah, AR_TFCNT); /* clear */ REG_WRITE(ah, 0, AR_CCCNT); REG_WRITE(ah, 0, AR_RFCNT); REG_WRITE(ah, 0, AR_RCCNT); REG_WRITE(ah, 0, AR_TFCNT); /* unfreeze */ REG_WRITE(ah, 0, AR_MIBC); /* update all cycle counters here */ common->cc_ani.cycles += cycles; common->cc_ani.rx_busy += busy; common->cc_ani.rx_frame += rx; common->cc_ani.tx_frame += tx; common->cc_survey.cycles += cycles; common->cc_survey.rx_busy += busy; common->cc_survey.rx_frame += rx; common->cc_survey.tx_frame += tx; } EXPORT_SYMBOL(ath_hw_cycle_counters_update); int32_t ath_hw_get_listen_time(struct ath_common *common) { struct ath_cycle_counters *cc = &common->cc_ani; int32_t listen_time; listen_time = (cc->cycles - cc->rx_frame - cc->tx_frame) / (common->clockrate * 1000); memset(cc, 0, sizeof(*cc)); return listen_time; } EXPORT_SYMBOL(ath_hw_get_listen_time); compat-drivers-2012-09-18/drivers/net/wireless/ath/debug.c0000644000175000017500000000270112026211315022467 0ustar mcgrofmcgrof/* * Copyright (c) 2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "ath.h" const char *ath_opmode_to_string(enum nl80211_iftype opmode) { switch (opmode) { case NL80211_IFTYPE_UNSPECIFIED: return "UNSPEC"; case NL80211_IFTYPE_ADHOC: return "ADHOC"; case NL80211_IFTYPE_STATION: return "STATION"; case NL80211_IFTYPE_AP: return "AP"; case NL80211_IFTYPE_AP_VLAN: return "AP-VLAN"; case NL80211_IFTYPE_WDS: return "WDS"; case NL80211_IFTYPE_MONITOR: return "MONITOR"; case NL80211_IFTYPE_MESH_POINT: return "MESH"; case NL80211_IFTYPE_P2P_CLIENT: return "P2P-CLIENT"; case NL80211_IFTYPE_P2P_GO: return "P2P-GO"; default: return "UNKNOWN"; } } EXPORT_SYMBOL(ath_opmode_to_string); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath.h0000644000175000017500000002023212026211315022161 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ATH_H #define ATH_H #include #include #include #include /* * The key cache is used for h/w cipher state and also for * tracking station state such as the current tx antenna. * We also setup a mapping table between key cache slot indices * and station state to short-circuit node lookups on rx. * Different parts have different size key caches. We handle * up to ATH_KEYMAX entries (could dynamically allocate state). */ #define ATH_KEYMAX 128 /* max key cache size we handle */ static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct ath_ani { bool caldone; unsigned int longcal_timer; unsigned int shortcal_timer; unsigned int resetcal_timer; unsigned int checkani_timer; struct timer_list timer; }; struct ath_cycle_counters { u32 cycles; u32 rx_busy; u32 rx_frame; u32 tx_frame; }; enum ath_device_state { ATH_HW_UNAVAILABLE, ATH_HW_INITIALIZED, }; enum ath_bus_type { ATH_PCI, ATH_AHB, ATH_USB, }; struct reg_dmn_pair_mapping { u16 regDmnEnum; u16 reg_5ghz_ctl; u16 reg_2ghz_ctl; }; struct ath_regulatory { char alpha2[2]; u16 country_code; u16 max_power_level; u16 current_rd; int16_t power_limit; struct reg_dmn_pair_mapping *regpair; }; enum ath_crypt_caps { ATH_CRYPT_CAP_CIPHER_AESCCM = BIT(0), ATH_CRYPT_CAP_MIC_COMBINED = BIT(1), }; struct ath_keyval { u8 kv_type; u8 kv_pad; u16 kv_len; u8 kv_val[16]; /* TK */ u8 kv_mic[8]; /* Michael MIC key */ u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware * supports both MIC keys in the same key cache entry; * in that case, kv_mic is the RX key) */ }; enum ath_cipher { ATH_CIPHER_WEP = 0, ATH_CIPHER_AES_OCB = 1, ATH_CIPHER_AES_CCM = 2, ATH_CIPHER_CKIP = 3, ATH_CIPHER_TKIP = 4, ATH_CIPHER_CLR = 5, ATH_CIPHER_MIC = 127 }; /** * struct ath_ops - Register read/write operations * * @read: Register read * @multi_read: Multiple register read * @write: Register write * @enable_write_buffer: Enable multiple register writes * @write_flush: flush buffered register writes and disable buffering */ struct ath_ops { unsigned int (*read)(void *, u32 reg_offset); void (*multi_read)(void *, u32 *addr, u32 *val, u16 count); void (*write)(void *, u32 val, u32 reg_offset); void (*enable_write_buffer)(void *); void (*write_flush) (void *); u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr); }; struct ath_common; struct ath_bus_ops; struct ath_common { void *ah; void *priv; struct ieee80211_hw *hw; int debug_mask; enum ath_device_state state; struct ath_ani ani; u16 cachelsz; u16 curaid; u8 macaddr[ETH_ALEN]; u8 curbssid[ETH_ALEN]; u8 bssidmask[ETH_ALEN]; u32 rx_bufsize; u32 keymax; DECLARE_BITMAP(keymap, ATH_KEYMAX); DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); DECLARE_BITMAP(ccmp_keymap, ATH_KEYMAX); enum ath_crypt_caps crypt_caps; unsigned int clockrate; spinlock_t cc_lock; struct ath_cycle_counters cc_ani; struct ath_cycle_counters cc_survey; struct ath_regulatory regulatory; struct ath_regulatory reg_world_copy; const struct ath_ops *ops; const struct ath_bus_ops *bus_ops; bool btcoex_enabled; bool disable_ani; }; struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, u32 len, gfp_t gfp_mask); void ath_hw_setbssidmask(struct ath_common *common); void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key); int ath_key_config(struct ath_common *common, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); bool ath_hw_keyreset(struct ath_common *common, u16 entry); void ath_hw_cycle_counters_update(struct ath_common *common); int32_t ath_hw_get_listen_time(struct ath_common *common); __printf(3, 4) void ath_printk(const char *level, const struct ath_common *common, const char *fmt, ...); #define ath_emerg(common, fmt, ...) \ ath_printk(KERN_EMERG, common, fmt, ##__VA_ARGS__) #define ath_alert(common, fmt, ...) \ ath_printk(KERN_ALERT, common, fmt, ##__VA_ARGS__) #define ath_crit(common, fmt, ...) \ ath_printk(KERN_CRIT, common, fmt, ##__VA_ARGS__) #define ath_err(common, fmt, ...) \ ath_printk(KERN_ERR, common, fmt, ##__VA_ARGS__) #define ath_warn(common, fmt, ...) \ ath_printk(KERN_WARNING, common, fmt, ##__VA_ARGS__) #define ath_notice(common, fmt, ...) \ ath_printk(KERN_NOTICE, common, fmt, ##__VA_ARGS__) #define ath_info(common, fmt, ...) \ ath_printk(KERN_INFO, common, fmt, ##__VA_ARGS__) /** * enum ath_debug_level - atheros wireless debug level * * @ATH_DBG_RESET: reset processing * @ATH_DBG_QUEUE: hardware queue management * @ATH_DBG_EEPROM: eeprom processing * @ATH_DBG_CALIBRATE: periodic calibration * @ATH_DBG_INTERRUPT: interrupt processing * @ATH_DBG_REGULATORY: regulatory processing * @ATH_DBG_ANI: adaptive noise immunitive processing * @ATH_DBG_XMIT: basic xmit operation * @ATH_DBG_BEACON: beacon handling * @ATH_DBG_CONFIG: configuration of the hardware * @ATH_DBG_FATAL: fatal errors, this is the default, DBG_DEFAULT * @ATH_DBG_PS: power save processing * @ATH_DBG_HWTIMER: hardware timer handling * @ATH_DBG_BTCOEX: bluetooth coexistance * @ATH_DBG_BSTUCK: stuck beacons * @ATH_DBG_MCI: Message Coexistence Interface, a private protocol * used exclusively for WLAN-BT coexistence starting from * AR9462. * @ATH_DBG_DFS: radar datection * @ATH_DBG_WOW: Wake on Wireless * @ATH_DBG_ANY: enable all debugging * * The debug level is used to control the amount and type of debugging output * we want to see. Each driver has its own method for enabling debugging and * modifying debug level states -- but this is typically done through a * module parameter 'debug' along with a respective 'debug' debugfs file * entry. */ enum ATH_DEBUG { ATH_DBG_RESET = 0x00000001, ATH_DBG_QUEUE = 0x00000002, ATH_DBG_EEPROM = 0x00000004, ATH_DBG_CALIBRATE = 0x00000008, ATH_DBG_INTERRUPT = 0x00000010, ATH_DBG_REGULATORY = 0x00000020, ATH_DBG_ANI = 0x00000040, ATH_DBG_XMIT = 0x00000080, ATH_DBG_BEACON = 0x00000100, ATH_DBG_CONFIG = 0x00000200, ATH_DBG_FATAL = 0x00000400, ATH_DBG_PS = 0x00000800, ATH_DBG_HWTIMER = 0x00001000, ATH_DBG_BTCOEX = 0x00002000, ATH_DBG_WMI = 0x00004000, ATH_DBG_BSTUCK = 0x00008000, ATH_DBG_MCI = 0x00010000, ATH_DBG_DFS = 0x00020000, ATH_DBG_WOW = 0x00040000, ATH_DBG_ANY = 0xffffffff }; #define ATH_DBG_DEFAULT (ATH_DBG_FATAL) #ifdef CONFIG_ATH_DEBUG #define ath_dbg(common, dbg_mask, fmt, ...) \ do { \ if ((common)->debug_mask & ATH_DBG_##dbg_mask) \ ath_printk(KERN_DEBUG, common, fmt, ##__VA_ARGS__); \ } while (0) #define ATH_DBG_WARN(foo, arg...) WARN(foo, arg) #define ATH_DBG_WARN_ON_ONCE(foo) WARN_ON_ONCE(foo) #else static inline __attribute__ ((format (printf, 3, 4))) void _ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask, const char *fmt, ...) { } #define ath_dbg(common, dbg_mask, fmt, ...) \ _ath_dbg(common, ATH_DBG_##dbg_mask, fmt, ##__VA_ARGS__) #define ATH_DBG_WARN(foo, arg...) do {} while (0) #define ATH_DBG_WARN_ON_ONCE(foo) ({ \ int __ret_warn_once = !!(foo); \ unlikely(__ret_warn_once); \ }) #endif /* CONFIG_ATH_DEBUG */ /** Returns string describing opmode, or NULL if unknown mode. */ #ifdef CONFIG_ATH_DEBUG const char *ath_opmode_to_string(enum nl80211_iftype opmode); #else static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode) { return "UNKNOWN"; } #endif #endif /* ATH_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/Makefile0000644000175000017500000000044012026211315022673 0ustar mcgrofmcgrofobj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_ATH_COMMON) += ath.o ath-objs := main.o \ regd.o \ hw.o \ key.o ath-$(CONFIG_ATH_DEBUG) += debug.o ccflags-y += -D__CHECK_ENDIAN__ compat-drivers-2012-09-18/drivers/net/wireless/ath/Kconfig0000644000175000017500000000166312026211315022546 0ustar mcgrofmcgrofmenuconfig ATH_COMMON tristate "Atheros Wireless Cards" depends on CFG80211 && (!UML || BROKEN) ---help--- This will enable the support for the Atheros wireless drivers. ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option enables the common ath.ko module which shares common helpers. For more information and documentation on this module you can visit: http://wireless.kernel.org/en/users/Drivers/ath For information on all Atheros wireless drivers visit: http://wireless.kernel.org/en/users/Drivers/Atheros if ATH_COMMON config ATH_DEBUG bool "Atheros wireless debugging" ---help--- Say Y, if you want to debug atheros wireless drivers. Right now only ath9k makes use of this. source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/0000755000175000017500000000000012026211315022255 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/hif_usb.c0000644000175000017500000007573512026211315024061 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "htc.h" /* identify firmware images */ #define FIRMWARE_AR7010_1_1 "htc_7010.fw" #define FIRMWARE_AR9271 "htc_9271.fw" MODULE_FIRMWARE(FIRMWARE_AR7010_1_1); MODULE_FIRMWARE(FIRMWARE_AR9271); static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x9271) }, /* Atheros */ { USB_DEVICE(0x0cf3, 0x1006) }, /* Atheros */ { USB_DEVICE(0x0846, 0x9030) }, /* Netgear N150 */ { USB_DEVICE(0x07D1, 0x3A10) }, /* Dlink Wireless 150 */ { USB_DEVICE(0x13D3, 0x3327) }, /* Azurewave */ { USB_DEVICE(0x13D3, 0x3328) }, /* Azurewave */ { USB_DEVICE(0x13D3, 0x3346) }, /* IMC Networks */ { USB_DEVICE(0x13D3, 0x3348) }, /* Azurewave */ { USB_DEVICE(0x13D3, 0x3349) }, /* Azurewave */ { USB_DEVICE(0x13D3, 0x3350) }, /* Azurewave */ { USB_DEVICE(0x04CA, 0x4605) }, /* Liteon */ { USB_DEVICE(0x040D, 0x3801) }, /* VIA */ { USB_DEVICE(0x0cf3, 0xb003) }, /* Ubiquiti WifiStation Ext */ { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */ { USB_DEVICE(0x0cf3, 0x7015), .driver_info = AR9287_USB }, /* Atheros */ { USB_DEVICE(0x1668, 0x1200), .driver_info = AR9287_USB }, /* Verizon */ { USB_DEVICE(0x0cf3, 0x7010), .driver_info = AR9280_USB }, /* Atheros */ { USB_DEVICE(0x0846, 0x9018), .driver_info = AR9280_USB }, /* Netgear WNDA3200 */ { USB_DEVICE(0x083A, 0xA704), .driver_info = AR9280_USB }, /* SMC Networks */ { USB_DEVICE(0x0411, 0x017f), .driver_info = AR9280_USB }, /* Sony UWA-BR100 */ { USB_DEVICE(0x04da, 0x3904), .driver_info = AR9280_USB }, { USB_DEVICE(0x0cf3, 0x20ff), .driver_info = STORAGE_DEVICE }, { }, }; MODULE_DEVICE_TABLE(usb, ath9k_hif_usb_ids); static int __hif_usb_tx(struct hif_device_usb *hif_dev); static void hif_usb_regout_cb(struct urb *urb) { struct cmd_buf *cmd = (struct cmd_buf *)urb->context; switch (urb->status) { case 0: break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: goto free; default: break; } if (cmd) { ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle, cmd->skb, true); kfree(cmd); } return; free: kfree_skb(cmd->skb); kfree(cmd); } static int hif_usb_send_regout(struct hif_device_usb *hif_dev, struct sk_buff *skb) { struct urb *urb; struct cmd_buf *cmd; int ret = 0; urb = usb_alloc_urb(0, GFP_KERNEL); if (urb == NULL) return -ENOMEM; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) { usb_free_urb(urb); return -ENOMEM; } cmd->skb = skb; cmd->hif_dev = hif_dev; usb_fill_bulk_urb(urb, hif_dev->udev, usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE), skb->data, skb->len, hif_usb_regout_cb, cmd); usb_anchor_urb(urb, &hif_dev->regout_submitted); ret = usb_submit_urb(urb, GFP_KERNEL); if (ret) { usb_unanchor_urb(urb); kfree(cmd); } usb_free_urb(urb); return ret; } static void hif_usb_mgmt_cb(struct urb *urb) { struct cmd_buf *cmd = (struct cmd_buf *)urb->context; struct hif_device_usb *hif_dev; bool txok = true; if (!cmd || !cmd->skb || !cmd->hif_dev) return; hif_dev = cmd->hif_dev; switch (urb->status) { case 0: break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: txok = false; /* * If the URBs are being flushed, no need to complete * this packet. */ spin_lock(&hif_dev->tx.tx_lock); if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) { spin_unlock(&hif_dev->tx.tx_lock); dev_kfree_skb_any(cmd->skb); kfree(cmd); return; } spin_unlock(&hif_dev->tx.tx_lock); break; default: txok = false; break; } skb_pull(cmd->skb, 4); ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle, cmd->skb, txok); kfree(cmd); } static int hif_usb_send_mgmt(struct hif_device_usb *hif_dev, struct sk_buff *skb) { struct urb *urb; struct cmd_buf *cmd; int ret = 0; __le16 *hdr; urb = usb_alloc_urb(0, GFP_ATOMIC); if (urb == NULL) return -ENOMEM; cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); if (cmd == NULL) { usb_free_urb(urb); return -ENOMEM; } cmd->skb = skb; cmd->hif_dev = hif_dev; hdr = (__le16 *) skb_push(skb, 4); *hdr++ = cpu_to_le16(skb->len - 4); *hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG); usb_fill_bulk_urb(urb, hif_dev->udev, usb_sndbulkpipe(hif_dev->udev, USB_WLAN_TX_PIPE), skb->data, skb->len, hif_usb_mgmt_cb, cmd); usb_anchor_urb(urb, &hif_dev->mgmt_submitted); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) { usb_unanchor_urb(urb); kfree(cmd); } usb_free_urb(urb); return ret; } static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev, struct sk_buff_head *list) { struct sk_buff *skb; while ((skb = __skb_dequeue(list)) != NULL) { dev_kfree_skb_any(skb); } } static inline void ath9k_skb_queue_complete(struct hif_device_usb *hif_dev, struct sk_buff_head *queue, bool txok) { struct sk_buff *skb; while ((skb = __skb_dequeue(queue)) != NULL) { ath9k_htc_txcompletion_cb(hif_dev->htc_handle, skb, txok); if (txok) TX_STAT_INC(skb_success); else TX_STAT_INC(skb_failed); } } static void hif_usb_tx_cb(struct urb *urb) { struct tx_buf *tx_buf = (struct tx_buf *) urb->context; struct hif_device_usb *hif_dev; bool txok = true; if (!tx_buf || !tx_buf->hif_dev) return; hif_dev = tx_buf->hif_dev; switch (urb->status) { case 0: break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: txok = false; /* * If the URBs are being flushed, no need to add this * URB to the free list. */ spin_lock(&hif_dev->tx.tx_lock); if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) { spin_unlock(&hif_dev->tx.tx_lock); ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); return; } spin_unlock(&hif_dev->tx.tx_lock); break; default: txok = false; break; } ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, txok); /* Re-initialize the SKB queue */ tx_buf->len = tx_buf->offset = 0; __skb_queue_head_init(&tx_buf->skb_queue); /* Add this TX buffer to the free list */ spin_lock(&hif_dev->tx.tx_lock); list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf); hif_dev->tx.tx_buf_cnt++; if (!(hif_dev->tx.flags & HIF_USB_TX_STOP)) __hif_usb_tx(hif_dev); /* Check for pending SKBs */ TX_STAT_INC(buf_completed); spin_unlock(&hif_dev->tx.tx_lock); } /* TX lock has to be taken */ static int __hif_usb_tx(struct hif_device_usb *hif_dev) { struct tx_buf *tx_buf = NULL; struct sk_buff *nskb = NULL; int ret = 0, i; u16 tx_skb_cnt = 0; u8 *buf; __le16 *hdr; if (hif_dev->tx.tx_skb_cnt == 0) return 0; /* Check if a free TX buffer is available */ if (list_empty(&hif_dev->tx.tx_buf)) return 0; tx_buf = list_first_entry(&hif_dev->tx.tx_buf, struct tx_buf, list); list_move_tail(&tx_buf->list, &hif_dev->tx.tx_pending); hif_dev->tx.tx_buf_cnt--; tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM); for (i = 0; i < tx_skb_cnt; i++) { nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue); /* Should never be NULL */ BUG_ON(!nskb); hif_dev->tx.tx_skb_cnt--; buf = tx_buf->buf; buf += tx_buf->offset; hdr = (__le16 *)buf; *hdr++ = cpu_to_le16(nskb->len); *hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG); buf += 4; memcpy(buf, nskb->data, nskb->len); tx_buf->len = nskb->len + 4; if (i < (tx_skb_cnt - 1)) tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4; if (i == (tx_skb_cnt - 1)) tx_buf->len += tx_buf->offset; __skb_queue_tail(&tx_buf->skb_queue, nskb); TX_STAT_INC(skb_queued); } usb_fill_bulk_urb(tx_buf->urb, hif_dev->udev, usb_sndbulkpipe(hif_dev->udev, USB_WLAN_TX_PIPE), tx_buf->buf, tx_buf->len, hif_usb_tx_cb, tx_buf); ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC); if (ret) { tx_buf->len = tx_buf->offset = 0; ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, false); __skb_queue_head_init(&tx_buf->skb_queue); list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf); hif_dev->tx.tx_buf_cnt++; } if (!ret) TX_STAT_INC(buf_queued); return ret; } static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb) { struct ath9k_htc_tx_ctl *tx_ctl; unsigned long flags; int ret = 0; spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); if (hif_dev->tx.flags & HIF_USB_TX_STOP) { spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); return -ENODEV; } /* Check if the max queue count has been reached */ if (hif_dev->tx.tx_skb_cnt > MAX_TX_BUF_NUM) { spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); return -ENOMEM; } spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); tx_ctl = HTC_SKB_CB(skb); /* Mgmt/Beacon frames don't use the TX buffer pool */ if ((tx_ctl->type == ATH9K_HTC_MGMT) || (tx_ctl->type == ATH9K_HTC_BEACON)) { ret = hif_usb_send_mgmt(hif_dev, skb); } spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); if ((tx_ctl->type == ATH9K_HTC_NORMAL) || (tx_ctl->type == ATH9K_HTC_AMPDU)) { __skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb); hif_dev->tx.tx_skb_cnt++; } /* Check if AMPDUs have to be sent immediately */ if ((hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) && (hif_dev->tx.tx_skb_cnt < 2)) { __hif_usb_tx(hif_dev); } spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); return ret; } static void hif_usb_start(void *hif_handle) { struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; unsigned long flags; hif_dev->flags |= HIF_USB_START; spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); hif_dev->tx.flags &= ~HIF_USB_TX_STOP; spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); } static void hif_usb_stop(void *hif_handle) { struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; unsigned long flags; spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); ath9k_skb_queue_complete(hif_dev, &hif_dev->tx.tx_skb_queue, false); hif_dev->tx.tx_skb_cnt = 0; hif_dev->tx.flags |= HIF_USB_TX_STOP; spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); /* The pending URBs have to be canceled. */ list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_pending, list) { usb_kill_urb(tx_buf->urb); } usb_kill_anchored_urbs(&hif_dev->mgmt_submitted); } static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb) { struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; int ret = 0; switch (pipe_id) { case USB_WLAN_TX_PIPE: ret = hif_usb_send_tx(hif_dev, skb); break; case USB_REG_OUT_PIPE: ret = hif_usb_send_regout(hif_dev, skb); break; default: dev_err(&hif_dev->udev->dev, "ath9k_htc: Invalid TX pipe: %d\n", pipe_id); ret = -EINVAL; break; } return ret; } static inline bool check_index(struct sk_buff *skb, u8 idx) { struct ath9k_htc_tx_ctl *tx_ctl; tx_ctl = HTC_SKB_CB(skb); if ((tx_ctl->type == ATH9K_HTC_AMPDU) && (tx_ctl->sta_idx == idx)) return true; return false; } static void hif_usb_sta_drain(void *hif_handle, u8 idx) { struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; struct sk_buff *skb, *tmp; unsigned long flags; spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); skb_queue_walk_safe(&hif_dev->tx.tx_skb_queue, skb, tmp) { if (check_index(skb, idx)) { __skb_unlink(skb, &hif_dev->tx.tx_skb_queue); ath9k_htc_txcompletion_cb(hif_dev->htc_handle, skb, false); hif_dev->tx.tx_skb_cnt--; TX_STAT_INC(skb_failed); } } spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); } static struct ath9k_htc_hif hif_usb = { .transport = ATH9K_HIF_USB, .name = "ath9k_hif_usb", .control_ul_pipe = USB_REG_OUT_PIPE, .control_dl_pipe = USB_REG_IN_PIPE, .start = hif_usb_start, .stop = hif_usb_stop, .sta_drain = hif_usb_sta_drain, .send = hif_usb_send, }; static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, struct sk_buff *skb) { struct sk_buff *nskb, *skb_pool[MAX_PKT_NUM_IN_TRANSFER]; int index = 0, i = 0, len = skb->len; int rx_remain_len, rx_pkt_len; u16 pool_index = 0; u8 *ptr; spin_lock(&hif_dev->rx_lock); rx_remain_len = hif_dev->rx_remain_len; rx_pkt_len = hif_dev->rx_transfer_len; if (rx_remain_len != 0) { struct sk_buff *remain_skb = hif_dev->remain_skb; if (remain_skb) { ptr = (u8 *) remain_skb->data; index = rx_remain_len; rx_remain_len -= hif_dev->rx_pad_len; ptr += rx_pkt_len; memcpy(ptr, skb->data, rx_remain_len); rx_pkt_len += rx_remain_len; hif_dev->rx_remain_len = 0; skb_put(remain_skb, rx_pkt_len); skb_pool[pool_index++] = remain_skb; } else { index = rx_remain_len; } } spin_unlock(&hif_dev->rx_lock); while (index < len) { u16 pkt_len; u16 pkt_tag; u16 pad_len; int chk_idx; ptr = (u8 *) skb->data; pkt_len = get_unaligned_le16(ptr + index); pkt_tag = get_unaligned_le16(ptr + index + 2); if (pkt_tag != ATH_USB_RX_STREAM_MODE_TAG) { RX_STAT_INC(skb_dropped); return; } pad_len = 4 - (pkt_len & 0x3); if (pad_len == 4) pad_len = 0; chk_idx = index; index = index + 4 + pkt_len + pad_len; if (index > MAX_RX_BUF_SIZE) { spin_lock(&hif_dev->rx_lock); hif_dev->rx_remain_len = index - MAX_RX_BUF_SIZE; hif_dev->rx_transfer_len = MAX_RX_BUF_SIZE - chk_idx - 4; hif_dev->rx_pad_len = pad_len; nskb = __dev_alloc_skb(pkt_len + 32, GFP_ATOMIC); if (!nskb) { dev_err(&hif_dev->udev->dev, "ath9k_htc: RX memory allocation error\n"); spin_unlock(&hif_dev->rx_lock); goto err; } skb_reserve(nskb, 32); RX_STAT_INC(skb_allocated); memcpy(nskb->data, &(skb->data[chk_idx+4]), hif_dev->rx_transfer_len); /* Record the buffer pointer */ hif_dev->remain_skb = nskb; spin_unlock(&hif_dev->rx_lock); } else { nskb = __dev_alloc_skb(pkt_len + 32, GFP_ATOMIC); if (!nskb) { dev_err(&hif_dev->udev->dev, "ath9k_htc: RX memory allocation error\n"); goto err; } skb_reserve(nskb, 32); RX_STAT_INC(skb_allocated); memcpy(nskb->data, &(skb->data[chk_idx+4]), pkt_len); skb_put(nskb, pkt_len); skb_pool[pool_index++] = nskb; } } err: for (i = 0; i < pool_index; i++) { ath9k_htc_rx_msg(hif_dev->htc_handle, skb_pool[i], skb_pool[i]->len, USB_WLAN_RX_PIPE); RX_STAT_INC(skb_completed); } } static void ath9k_hif_usb_rx_cb(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct hif_device_usb *hif_dev = usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); int ret; if (!skb) return; if (!hif_dev) goto free; switch (urb->status) { case 0: break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: goto free; default: goto resubmit; } if (likely(urb->actual_length != 0)) { skb_put(skb, urb->actual_length); ath9k_hif_usb_rx_stream(hif_dev, skb); } resubmit: skb_reset_tail_pointer(skb); skb_trim(skb, 0); usb_anchor_urb(urb, &hif_dev->rx_submitted); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) { usb_unanchor_urb(urb); goto free; } return; free: kfree_skb(skb); } static void ath9k_hif_usb_reg_in_cb(struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct sk_buff *nskb; struct hif_device_usb *hif_dev = usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); int ret; if (!skb) return; if (!hif_dev) goto free; switch (urb->status) { case 0: break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: goto free; default: skb_reset_tail_pointer(skb); skb_trim(skb, 0); goto resubmit; } if (likely(urb->actual_length != 0)) { skb_put(skb, urb->actual_length); /* Process the command first */ ath9k_htc_rx_msg(hif_dev->htc_handle, skb, skb->len, USB_REG_IN_PIPE); nskb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC); if (!nskb) { dev_err(&hif_dev->udev->dev, "ath9k_htc: REG_IN memory allocation failure\n"); urb->context = NULL; return; } usb_fill_bulk_urb(urb, hif_dev->udev, usb_rcvbulkpipe(hif_dev->udev, USB_REG_IN_PIPE), nskb->data, MAX_REG_IN_BUF_SIZE, ath9k_hif_usb_reg_in_cb, nskb); } resubmit: usb_anchor_urb(urb, &hif_dev->reg_in_submitted); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) { usb_unanchor_urb(urb); goto free; } return; free: kfree_skb(skb); urb->context = NULL; } static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) { struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; unsigned long flags; list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) { usb_kill_urb(tx_buf->urb); list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); kfree(tx_buf->buf); kfree(tx_buf); } spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); hif_dev->tx.flags |= HIF_USB_TX_FLUSH; spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_pending, list) { usb_kill_urb(tx_buf->urb); list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); kfree(tx_buf->buf); kfree(tx_buf); } usb_kill_anchored_urbs(&hif_dev->mgmt_submitted); } static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) { struct tx_buf *tx_buf; int i; INIT_LIST_HEAD(&hif_dev->tx.tx_buf); INIT_LIST_HEAD(&hif_dev->tx.tx_pending); spin_lock_init(&hif_dev->tx.tx_lock); __skb_queue_head_init(&hif_dev->tx.tx_skb_queue); init_usb_anchor(&hif_dev->mgmt_submitted); for (i = 0; i < MAX_TX_URB_NUM; i++) { tx_buf = kzalloc(sizeof(struct tx_buf), GFP_KERNEL); if (!tx_buf) goto err; tx_buf->buf = kzalloc(MAX_TX_BUF_SIZE, GFP_KERNEL); if (!tx_buf->buf) goto err; tx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); if (!tx_buf->urb) goto err; tx_buf->hif_dev = hif_dev; __skb_queue_head_init(&tx_buf->skb_queue); list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf); } hif_dev->tx.tx_buf_cnt = MAX_TX_URB_NUM; return 0; err: if (tx_buf) { kfree(tx_buf->buf); kfree(tx_buf); } ath9k_hif_usb_dealloc_tx_urbs(hif_dev); return -ENOMEM; } static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev) { usb_kill_anchored_urbs(&hif_dev->rx_submitted); } static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev) { struct urb *urb = NULL; struct sk_buff *skb = NULL; int i, ret; init_usb_anchor(&hif_dev->rx_submitted); spin_lock_init(&hif_dev->rx_lock); for (i = 0; i < MAX_RX_URB_NUM; i++) { /* Allocate URB */ urb = usb_alloc_urb(0, GFP_KERNEL); if (urb == NULL) { ret = -ENOMEM; goto err_urb; } /* Allocate buffer */ skb = alloc_skb(MAX_RX_BUF_SIZE, GFP_KERNEL); if (!skb) { ret = -ENOMEM; goto err_skb; } usb_fill_bulk_urb(urb, hif_dev->udev, usb_rcvbulkpipe(hif_dev->udev, USB_WLAN_RX_PIPE), skb->data, MAX_RX_BUF_SIZE, ath9k_hif_usb_rx_cb, skb); /* Anchor URB */ usb_anchor_urb(urb, &hif_dev->rx_submitted); /* Submit URB */ ret = usb_submit_urb(urb, GFP_KERNEL); if (ret) { usb_unanchor_urb(urb); goto err_submit; } /* * Drop reference count. * This ensures that the URB is freed when killing them. */ usb_free_urb(urb); } return 0; err_submit: kfree_skb(skb); err_skb: usb_free_urb(urb); err_urb: ath9k_hif_usb_dealloc_rx_urbs(hif_dev); return ret; } static void ath9k_hif_usb_dealloc_reg_in_urbs(struct hif_device_usb *hif_dev) { usb_kill_anchored_urbs(&hif_dev->reg_in_submitted); } static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev) { struct urb *urb = NULL; struct sk_buff *skb = NULL; int i, ret; init_usb_anchor(&hif_dev->reg_in_submitted); for (i = 0; i < MAX_REG_IN_URB_NUM; i++) { /* Allocate URB */ urb = usb_alloc_urb(0, GFP_KERNEL); if (urb == NULL) { ret = -ENOMEM; goto err_urb; } /* Allocate buffer */ skb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL); if (!skb) { ret = -ENOMEM; goto err_skb; } usb_fill_bulk_urb(urb, hif_dev->udev, usb_rcvbulkpipe(hif_dev->udev, USB_REG_IN_PIPE), skb->data, MAX_REG_IN_BUF_SIZE, ath9k_hif_usb_reg_in_cb, skb); /* Anchor URB */ usb_anchor_urb(urb, &hif_dev->reg_in_submitted); /* Submit URB */ ret = usb_submit_urb(urb, GFP_KERNEL); if (ret) { usb_unanchor_urb(urb); goto err_submit; } /* * Drop reference count. * This ensures that the URB is freed when killing them. */ usb_free_urb(urb); } return 0; err_submit: kfree_skb(skb); err_skb: usb_free_urb(urb); err_urb: ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev); return ret; } static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev) { /* Register Write */ init_usb_anchor(&hif_dev->regout_submitted); /* TX */ if (ath9k_hif_usb_alloc_tx_urbs(hif_dev) < 0) goto err; /* RX */ if (ath9k_hif_usb_alloc_rx_urbs(hif_dev) < 0) goto err_rx; /* Register Read */ if (ath9k_hif_usb_alloc_reg_in_urbs(hif_dev) < 0) goto err_reg; return 0; err_reg: ath9k_hif_usb_dealloc_rx_urbs(hif_dev); err_rx: ath9k_hif_usb_dealloc_tx_urbs(hif_dev); err: return -ENOMEM; } static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev) { usb_kill_anchored_urbs(&hif_dev->regout_submitted); ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev); ath9k_hif_usb_dealloc_tx_urbs(hif_dev); ath9k_hif_usb_dealloc_rx_urbs(hif_dev); } static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) { int transfer, err; const void *data = hif_dev->fw_data; size_t len = hif_dev->fw_size; u32 addr = AR9271_FIRMWARE; u8 *buf = kzalloc(4096, GFP_KERNEL); u32 firm_offset; if (!buf) return -ENOMEM; while (len) { transfer = min_t(size_t, len, 4096); memcpy(buf, data, transfer); err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0), FIRMWARE_DOWNLOAD, 0x40 | USB_DIR_OUT, addr >> 8, 0, buf, transfer, HZ); if (err < 0) { kfree(buf); return err; } len -= transfer; data += transfer; addr += transfer; } kfree(buf); if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info)) firm_offset = AR7010_FIRMWARE_TEXT; else firm_offset = AR9271_FIRMWARE_TEXT; /* * Issue FW download complete command to firmware. */ err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0), FIRMWARE_DOWNLOAD_COMP, 0x40 | USB_DIR_OUT, firm_offset >> 8, 0, NULL, 0, HZ); if (err) return -EIO; dev_info(&hif_dev->udev->dev, "ath9k_htc: Transferred FW: %s, size: %ld\n", hif_dev->fw_name, (unsigned long) hif_dev->fw_size); return 0; } static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) { struct usb_host_interface *alt = &hif_dev->interface->altsetting[0]; struct usb_endpoint_descriptor *endp; int ret, idx; ret = ath9k_hif_usb_download_fw(hif_dev); if (ret) { dev_err(&hif_dev->udev->dev, "ath9k_htc: Firmware - %s download failed\n", hif_dev->fw_name); return ret; } /* On downloading the firmware to the target, the USB descriptor of EP4 * is 'patched' to change the type of the endpoint to Bulk. This will * bring down CPU usage during the scan period. */ for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) { endp = &alt->endpoint[idx].desc; if ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK; endp->bmAttributes |= USB_ENDPOINT_XFER_BULK; endp->bInterval = 0; } } /* Alloc URBs */ ret = ath9k_hif_usb_alloc_urbs(hif_dev); if (ret) { dev_err(&hif_dev->udev->dev, "ath9k_htc: Unable to allocate URBs\n"); return ret; } return 0; } static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) { ath9k_hif_usb_dealloc_urbs(hif_dev); } /* * If initialization fails or the FW cannot be retrieved, * detach the device. */ static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev) { struct device *parent = hif_dev->udev->dev.parent; complete(&hif_dev->fw_done); if (parent) device_lock(parent); device_release_driver(&hif_dev->udev->dev); if (parent) device_unlock(parent); } static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) { struct hif_device_usb *hif_dev = context; int ret; if (!fw) { dev_err(&hif_dev->udev->dev, "ath9k_htc: Failed to get firmware %s\n", hif_dev->fw_name); goto err_fw; } hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, &hif_dev->udev->dev); if (hif_dev->htc_handle == NULL) goto err_dev_alloc; hif_dev->fw_data = fw->data; hif_dev->fw_size = fw->size; /* Proceed with initialization */ ret = ath9k_hif_usb_dev_init(hif_dev); if (ret) goto err_dev_init; ret = ath9k_htc_hw_init(hif_dev->htc_handle, &hif_dev->interface->dev, hif_dev->usb_device_id->idProduct, hif_dev->udev->product, hif_dev->usb_device_id->driver_info); if (ret) { ret = -EINVAL; goto err_htc_hw_init; } release_firmware(fw); hif_dev->flags |= HIF_USB_READY; complete(&hif_dev->fw_done); return; err_htc_hw_init: ath9k_hif_usb_dev_deinit(hif_dev); err_dev_init: ath9k_htc_hw_free(hif_dev->htc_handle); err_dev_alloc: release_firmware(fw); err_fw: ath9k_hif_usb_firmware_fail(hif_dev); } /* * An exact copy of the function from zd1211rw. */ static int send_eject_command(struct usb_interface *interface) { struct usb_device *udev = interface_to_usbdev(interface); struct usb_host_interface *iface_desc = &interface->altsetting[0]; struct usb_endpoint_descriptor *endpoint; unsigned char *cmd; u8 bulk_out_ep; int r; /* Find bulk out endpoint */ for (r = 1; r >= 0; r--) { endpoint = &iface_desc->endpoint[r].desc; if (usb_endpoint_dir_out(endpoint) && usb_endpoint_xfer_bulk(endpoint)) { bulk_out_ep = endpoint->bEndpointAddress; break; } } if (r == -1) { dev_err(&udev->dev, "ath9k_htc: Could not find bulk out endpoint\n"); return -ENODEV; } cmd = kzalloc(31, GFP_KERNEL); if (cmd == NULL) return -ENODEV; /* USB bulk command block */ cmd[0] = 0x55; /* bulk command signature */ cmd[1] = 0x53; /* bulk command signature */ cmd[2] = 0x42; /* bulk command signature */ cmd[3] = 0x43; /* bulk command signature */ cmd[14] = 6; /* command length */ cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */ cmd[19] = 0x2; /* eject disc */ dev_info(&udev->dev, "Ejecting storage device...\n"); r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), cmd, 31, NULL, 2000); kfree(cmd); if (r) return r; /* At this point, the device disconnects and reconnects with the real * ID numbers. */ usb_set_intfdata(interface, NULL); return 0; } static int ath9k_hif_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); struct hif_device_usb *hif_dev; int ret = 0; if (id->driver_info == STORAGE_DEVICE) return send_eject_command(interface); hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL); if (!hif_dev) { ret = -ENOMEM; goto err_alloc; } usb_get_dev(udev); hif_dev->udev = udev; hif_dev->interface = interface; hif_dev->usb_device_id = id; #ifdef CONFIG_PM udev->reset_resume = 1; #endif usb_set_intfdata(interface, hif_dev); init_completion(&hif_dev->fw_done); /* Find out which firmware to load */ if (IS_AR7010_DEVICE(id->driver_info)) hif_dev->fw_name = FIRMWARE_AR7010_1_1; else hif_dev->fw_name = FIRMWARE_AR9271; ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, &hif_dev->udev->dev, GFP_KERNEL, hif_dev, ath9k_hif_usb_firmware_cb); if (ret) { dev_err(&hif_dev->udev->dev, "ath9k_htc: Async request for firmware %s failed\n", hif_dev->fw_name); goto err_fw_req; } dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", hif_dev->fw_name); return 0; err_fw_req: usb_set_intfdata(interface, NULL); kfree(hif_dev); usb_put_dev(udev); err_alloc: return ret; } static void ath9k_hif_usb_reboot(struct usb_device *udev) { u32 reboot_cmd = 0xffffffff; void *buf; int ret; buf = kmemdup(&reboot_cmd, 4, GFP_KERNEL); if (!buf) return; ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE), buf, 4, NULL, HZ); if (ret) dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n"); kfree(buf); } static void ath9k_hif_usb_disconnect(struct usb_interface *interface) { struct usb_device *udev = interface_to_usbdev(interface); struct hif_device_usb *hif_dev = usb_get_intfdata(interface); bool unplugged = (udev->state == USB_STATE_NOTATTACHED) ? true : false; if (!hif_dev) return; wait_for_completion(&hif_dev->fw_done); if (hif_dev->flags & HIF_USB_READY) { ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); ath9k_htc_hw_free(hif_dev->htc_handle); ath9k_hif_usb_dev_deinit(hif_dev); } usb_set_intfdata(interface, NULL); if (!unplugged && (hif_dev->flags & HIF_USB_START)) ath9k_hif_usb_reboot(udev); kfree(hif_dev); dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n"); usb_put_dev(udev); } #ifdef CONFIG_PM static int ath9k_hif_usb_suspend(struct usb_interface *interface, pm_message_t message) { struct hif_device_usb *hif_dev = usb_get_intfdata(interface); /* * The device has to be set to FULLSLEEP mode in case no * interface is up. */ if (!(hif_dev->flags & HIF_USB_START)) ath9k_htc_suspend(hif_dev->htc_handle); ath9k_hif_usb_dealloc_urbs(hif_dev); return 0; } static int ath9k_hif_usb_resume(struct usb_interface *interface) { struct hif_device_usb *hif_dev = usb_get_intfdata(interface); struct htc_target *htc_handle = hif_dev->htc_handle; int ret; const struct firmware *fw; ret = ath9k_hif_usb_alloc_urbs(hif_dev); if (ret) return ret; if (hif_dev->flags & HIF_USB_READY) { /* request cached firmware during suspend/resume cycle */ ret = request_firmware(&fw, hif_dev->fw_name, &hif_dev->udev->dev); if (ret) goto fail_resume; hif_dev->fw_data = fw->data; hif_dev->fw_size = fw->size; ret = ath9k_hif_usb_download_fw(hif_dev); release_firmware(fw); if (ret) goto fail_resume; } else { ath9k_hif_usb_dealloc_urbs(hif_dev); return -EIO; } mdelay(100); ret = ath9k_htc_resume(htc_handle); if (ret) goto fail_resume; return 0; fail_resume: ath9k_hif_usb_dealloc_urbs(hif_dev); return ret; } #endif static struct usb_driver ath9k_hif_usb_driver = { .name = KBUILD_MODNAME, .probe = ath9k_hif_usb_probe, .disconnect = ath9k_hif_usb_disconnect, #ifdef CONFIG_PM .suspend = ath9k_hif_usb_suspend, .resume = ath9k_hif_usb_resume, .reset_resume = ath9k_hif_usb_resume, #endif .id_table = ath9k_hif_usb_ids, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) .soft_unbind = 1, #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; int ath9k_hif_usb_init(void) { return usb_register(&ath9k_hif_usb_driver); } void ath9k_hif_usb_exit(void) { usb_deregister(&ath9k_hif_usb_driver); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_drv_init.c0000644000175000017500000006276512026211315025115 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "htc.h" MODULE_AUTHOR("Atheros Communications"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Atheros driver 802.11n HTC based wireless devices"); static unsigned int ath9k_debug = ATH_DBG_DEFAULT; module_param_named(debug, ath9k_debug, uint, 0); MODULE_PARM_DESC(debug, "Debugging mask"); int htc_modparam_nohwcrypt; module_param_named(nohwcrypt, htc_modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); static int ath9k_htc_btcoex_enable; module_param_named(btcoex_enable, ath9k_htc_btcoex_enable, int, 0444); MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); #define CHAN2G(_freq, _idx) { \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 20, \ } #define CHAN5G(_freq, _idx) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 20, \ } static struct ieee80211_channel ath9k_2ghz_channels[] = { CHAN2G(2412, 0), /* Channel 1 */ CHAN2G(2417, 1), /* Channel 2 */ CHAN2G(2422, 2), /* Channel 3 */ CHAN2G(2427, 3), /* Channel 4 */ CHAN2G(2432, 4), /* Channel 5 */ CHAN2G(2437, 5), /* Channel 6 */ CHAN2G(2442, 6), /* Channel 7 */ CHAN2G(2447, 7), /* Channel 8 */ CHAN2G(2452, 8), /* Channel 9 */ CHAN2G(2457, 9), /* Channel 10 */ CHAN2G(2462, 10), /* Channel 11 */ CHAN2G(2467, 11), /* Channel 12 */ CHAN2G(2472, 12), /* Channel 13 */ CHAN2G(2484, 13), /* Channel 14 */ }; static struct ieee80211_channel ath9k_5ghz_channels[] = { /* _We_ call this UNII 1 */ CHAN5G(5180, 14), /* Channel 36 */ CHAN5G(5200, 15), /* Channel 40 */ CHAN5G(5220, 16), /* Channel 44 */ CHAN5G(5240, 17), /* Channel 48 */ /* _We_ call this UNII 2 */ CHAN5G(5260, 18), /* Channel 52 */ CHAN5G(5280, 19), /* Channel 56 */ CHAN5G(5300, 20), /* Channel 60 */ CHAN5G(5320, 21), /* Channel 64 */ /* _We_ call this "Middle band" */ CHAN5G(5500, 22), /* Channel 100 */ CHAN5G(5520, 23), /* Channel 104 */ CHAN5G(5540, 24), /* Channel 108 */ CHAN5G(5560, 25), /* Channel 112 */ CHAN5G(5580, 26), /* Channel 116 */ CHAN5G(5600, 27), /* Channel 120 */ CHAN5G(5620, 28), /* Channel 124 */ CHAN5G(5640, 29), /* Channel 128 */ CHAN5G(5660, 30), /* Channel 132 */ CHAN5G(5680, 31), /* Channel 136 */ CHAN5G(5700, 32), /* Channel 140 */ /* _We_ call this UNII 3 */ CHAN5G(5745, 33), /* Channel 149 */ CHAN5G(5765, 34), /* Channel 153 */ CHAN5G(5785, 35), /* Channel 157 */ CHAN5G(5805, 36), /* Channel 161 */ CHAN5G(5825, 37), /* Channel 165 */ }; /* Atheros hardware rate code addition for short premble */ #define SHPCHECK(__hw_rate, __flags) \ ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04) : 0) #define RATE(_bitrate, _hw_rate, _flags) { \ .bitrate = (_bitrate), \ .flags = (_flags), \ .hw_value = (_hw_rate), \ .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ } static struct ieee80211_rate ath9k_legacy_rates[] = { RATE(10, 0x1b, 0), RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp : 0x1e */ RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp: 0x1d */ RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), /* short: 0x1c */ RATE(60, 0x0b, 0), RATE(90, 0x0f, 0), RATE(120, 0x0a, 0), RATE(180, 0x0e, 0), RATE(240, 0x09, 0), RATE(360, 0x0d, 0), RATE(480, 0x08, 0), RATE(540, 0x0c, 0), }; #ifdef CONFIG_MAC80211_LEDS static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = { { .throughput = 0 * 1024, .blink_time = 334 }, { .throughput = 1 * 1024, .blink_time = 260 }, { .throughput = 5 * 1024, .blink_time = 220 }, { .throughput = 10 * 1024, .blink_time = 190 }, { .throughput = 20 * 1024, .blink_time = 170 }, { .throughput = 50 * 1024, .blink_time = 150 }, { .throughput = 70 * 1024, .blink_time = 130 }, { .throughput = 100 * 1024, .blink_time = 110 }, { .throughput = 200 * 1024, .blink_time = 80 }, { .throughput = 300 * 1024, .blink_time = 50 }, }; #endif static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv) { int time_left; if (atomic_read(&priv->htc->tgt_ready) > 0) { atomic_dec(&priv->htc->tgt_ready); return 0; } /* Firmware can take up to 50ms to get ready, to be safe use 1 second */ time_left = wait_for_completion_timeout(&priv->htc->target_wait, HZ); if (!time_left) { dev_err(priv->dev, "ath9k_htc: Target is unresponsive\n"); return -ETIMEDOUT; } atomic_dec(&priv->htc->tgt_ready); return 0; } static void ath9k_deinit_priv(struct ath9k_htc_priv *priv) { ath9k_hw_deinit(priv->ah); kfree(priv->ah); priv->ah = NULL; } static void ath9k_deinit_device(struct ath9k_htc_priv *priv) { struct ieee80211_hw *hw = priv->hw; wiphy_rfkill_stop_polling(hw->wiphy); ath9k_deinit_leds(priv); ieee80211_unregister_hw(hw); ath9k_rx_cleanup(priv); ath9k_tx_cleanup(priv); ath9k_deinit_priv(priv); } static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv, u16 service_id, void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok), enum htc_endpoint_id *ep_id) { struct htc_service_connreq req; memset(&req, 0, sizeof(struct htc_service_connreq)); req.service_id = service_id; req.ep_callbacks.priv = priv; req.ep_callbacks.rx = ath9k_htc_rxep; req.ep_callbacks.tx = tx; return htc_connect_service(priv->htc, &req, ep_id); } static int ath9k_init_htc_services(struct ath9k_htc_priv *priv, u16 devid, u32 drv_info) { int ret; /* WMI CMD*/ ret = ath9k_wmi_connect(priv->htc, priv->wmi, &priv->wmi_cmd_ep); if (ret) goto err; /* Beacon */ ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, ath9k_htc_beaconep, &priv->beacon_ep); if (ret) goto err; /* CAB */ ret = ath9k_htc_connect_svc(priv, WMI_CAB_SVC, ath9k_htc_txep, &priv->cab_ep); if (ret) goto err; /* UAPSD */ ret = ath9k_htc_connect_svc(priv, WMI_UAPSD_SVC, ath9k_htc_txep, &priv->uapsd_ep); if (ret) goto err; /* MGMT */ ret = ath9k_htc_connect_svc(priv, WMI_MGMT_SVC, ath9k_htc_txep, &priv->mgmt_ep); if (ret) goto err; /* DATA BE */ ret = ath9k_htc_connect_svc(priv, WMI_DATA_BE_SVC, ath9k_htc_txep, &priv->data_be_ep); if (ret) goto err; /* DATA BK */ ret = ath9k_htc_connect_svc(priv, WMI_DATA_BK_SVC, ath9k_htc_txep, &priv->data_bk_ep); if (ret) goto err; /* DATA VI */ ret = ath9k_htc_connect_svc(priv, WMI_DATA_VI_SVC, ath9k_htc_txep, &priv->data_vi_ep); if (ret) goto err; /* DATA VO */ ret = ath9k_htc_connect_svc(priv, WMI_DATA_VO_SVC, ath9k_htc_txep, &priv->data_vo_ep); if (ret) goto err; /* * Setup required credits before initializing HTC. * This is a bit hacky, but, since queuing is done in * the HIF layer, shouldn't matter much. */ if (IS_AR7010_DEVICE(drv_info)) priv->htc->credits = 45; else priv->htc->credits = 33; ret = htc_init(priv->htc); if (ret) goto err; dev_info(priv->dev, "ath9k_htc: HTC initialized with %d credits\n", priv->htc->credits); return 0; err: dev_err(priv->dev, "ath9k_htc: Unable to initialize HTC services\n"); return ret; } static int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath9k_htc_priv *priv = hw->priv; return ath_reg_notifier_apply(wiphy, request, ath9k_hw_regulatory(priv->ah)); } static unsigned int ath9k_regread(void *hw_priv, u32 reg_offset) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; __be32 val, reg = cpu_to_be32(reg_offset); int r; r = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID, (u8 *) ®, sizeof(reg), (u8 *) &val, sizeof(val), 100); if (unlikely(r)) { ath_dbg(common, WMI, "REGISTER READ FAILED: (0x%04x, %d)\n", reg_offset, r); return -EIO; } return be32_to_cpu(val); } static void ath9k_multi_regread(void *hw_priv, u32 *addr, u32 *val, u16 count) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; __be32 tmpaddr[8]; __be32 tmpval[8]; int i, ret; for (i = 0; i < count; i++) { tmpaddr[i] = cpu_to_be32(addr[i]); } ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID, (u8 *)tmpaddr , sizeof(u32) * count, (u8 *)tmpval, sizeof(u32) * count, 100); if (unlikely(ret)) { ath_dbg(common, WMI, "Multiple REGISTER READ FAILED (count: %d)\n", count); } for (i = 0; i < count; i++) { val[i] = be32_to_cpu(tmpval[i]); } } static void ath9k_regwrite_single(void *hw_priv, u32 val, u32 reg_offset) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; const __be32 buf[2] = { cpu_to_be32(reg_offset), cpu_to_be32(val), }; int r; r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, (u8 *) &buf, sizeof(buf), (u8 *) &val, sizeof(val), 100); if (unlikely(r)) { ath_dbg(common, WMI, "REGISTER WRITE FAILED:(0x%04x, %d)\n", reg_offset, r); } } static void ath9k_regwrite_buffer(void *hw_priv, u32 val, u32 reg_offset) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; u32 rsp_status; int r; mutex_lock(&priv->wmi->multi_write_mutex); /* Store the register/value */ priv->wmi->multi_write[priv->wmi->multi_write_idx].reg = cpu_to_be32(reg_offset); priv->wmi->multi_write[priv->wmi->multi_write_idx].val = cpu_to_be32(val); priv->wmi->multi_write_idx++; /* If the buffer is full, send it out. */ if (priv->wmi->multi_write_idx == MAX_CMD_NUMBER) { r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, (u8 *) &priv->wmi->multi_write, sizeof(struct register_write) * priv->wmi->multi_write_idx, (u8 *) &rsp_status, sizeof(rsp_status), 100); if (unlikely(r)) { ath_dbg(common, WMI, "REGISTER WRITE FAILED, multi len: %d\n", priv->wmi->multi_write_idx); } priv->wmi->multi_write_idx = 0; } mutex_unlock(&priv->wmi->multi_write_mutex); } static void ath9k_regwrite(void *hw_priv, u32 val, u32 reg_offset) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; if (atomic_read(&priv->wmi->mwrite_cnt)) ath9k_regwrite_buffer(hw_priv, val, reg_offset); else ath9k_regwrite_single(hw_priv, val, reg_offset); } static void ath9k_enable_regwrite_buffer(void *hw_priv) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; atomic_inc(&priv->wmi->mwrite_cnt); } static void ath9k_regwrite_flush(void *hw_priv) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; u32 rsp_status; int r; atomic_dec(&priv->wmi->mwrite_cnt); mutex_lock(&priv->wmi->multi_write_mutex); if (priv->wmi->multi_write_idx) { r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, (u8 *) &priv->wmi->multi_write, sizeof(struct register_write) * priv->wmi->multi_write_idx, (u8 *) &rsp_status, sizeof(rsp_status), 100); if (unlikely(r)) { ath_dbg(common, WMI, "REGISTER WRITE FAILED, multi len: %d\n", priv->wmi->multi_write_idx); } priv->wmi->multi_write_idx = 0; } mutex_unlock(&priv->wmi->multi_write_mutex); } static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) { u32 val; val = ath9k_regread(hw_priv, reg_offset); val &= ~clr; val |= set; ath9k_regwrite(hw_priv, val, reg_offset); return val; } static void ath_usb_read_cachesize(struct ath_common *common, int *csz) { *csz = L1_CACHE_BYTES >> 2; } static bool ath_usb_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath_hw *ah = (struct ath_hw *) common->ah; (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); if (!ath9k_hw_wait(ah, AR_EEPROM_STATUS_DATA, AR_EEPROM_STATUS_DATA_BUSY | AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0, AH_WAIT_TIMEOUT)) return false; *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), AR_EEPROM_STATUS_DATA_VAL); return true; } static const struct ath_bus_ops ath9k_usb_bus_ops = { .ath_bus_type = ATH_USB, .read_cachesize = ath_usb_read_cachesize, .eeprom_read = ath_usb_eeprom_read, }; static void setup_ht_cap(struct ath9k_htc_priv *priv, struct ieee80211_sta_ht_cap *ht_info) { struct ath_common *common = ath9k_hw_common(priv->ah); u8 tx_streams, rx_streams; int i; ht_info->ht_supported = true; ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40; if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20) ht_info->cap |= IEEE80211_HT_CAP_SGI_20; ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); /* ath9k_htc supports only 1 or 2 stream devices */ tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2); rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2); ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", tx_streams, rx_streams); if (tx_streams != rx_streams) { ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); } for (i = 0; i < rx_streams; i++) ht_info->mcs.rx_mask[i] = 0xff; ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; } static int ath9k_init_queues(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); int i; for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++) priv->hwq_map[i] = -1; priv->beaconq = ath9k_hw_beaconq_setup(priv->ah); if (priv->beaconq == -1) { ath_err(common, "Unable to setup BEACON xmit queue\n"); goto err; } priv->cabq = ath9k_htc_cabq_setup(priv); if (priv->cabq == -1) { ath_err(common, "Unable to setup CAB xmit queue\n"); goto err; } if (!ath9k_htc_txq_setup(priv, WME_AC_BE)) { ath_err(common, "Unable to setup xmit queue for BE traffic\n"); goto err; } if (!ath9k_htc_txq_setup(priv, WME_AC_BK)) { ath_err(common, "Unable to setup xmit queue for BK traffic\n"); goto err; } if (!ath9k_htc_txq_setup(priv, WME_AC_VI)) { ath_err(common, "Unable to setup xmit queue for VI traffic\n"); goto err; } if (!ath9k_htc_txq_setup(priv, WME_AC_VO)) { ath_err(common, "Unable to setup xmit queue for VO traffic\n"); goto err; } return 0; err: return -EINVAL; } static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv) { if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { priv->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_channels; priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; priv->sbands[IEEE80211_BAND_2GHZ].n_channels = ARRAY_SIZE(ath9k_2ghz_channels); priv->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; priv->sbands[IEEE80211_BAND_2GHZ].n_bitrates = ARRAY_SIZE(ath9k_legacy_rates); } if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { priv->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_channels; priv->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; priv->sbands[IEEE80211_BAND_5GHZ].n_channels = ARRAY_SIZE(ath9k_5ghz_channels); priv->sbands[IEEE80211_BAND_5GHZ].bitrates = ath9k_legacy_rates + 4; priv->sbands[IEEE80211_BAND_5GHZ].n_bitrates = ARRAY_SIZE(ath9k_legacy_rates) - 4; } } static void ath9k_init_misc(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); priv->ah->opmode = NL80211_IFTYPE_STATION; } static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid, char *product, u32 drv_info) { struct ath_hw *ah = NULL; struct ath_common *common; int i, ret = 0, csz = 0; set_bit(OP_INVALID, &priv->op_flags); ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); if (!ah) return -ENOMEM; ah->hw_version.devid = devid; ah->hw_version.usbdev = drv_info; ah->ah_flags |= AH_USE_EEPROM; ah->reg_ops.read = ath9k_regread; ah->reg_ops.multi_read = ath9k_multi_regread; ah->reg_ops.write = ath9k_regwrite; ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer; ah->reg_ops.write_flush = ath9k_regwrite_flush; ah->reg_ops.rmw = ath9k_reg_rmw; priv->ah = ah; common = ath9k_hw_common(ah); common->ops = &ah->reg_ops; common->bus_ops = &ath9k_usb_bus_ops; common->ah = ah; common->hw = priv->hw; common->priv = priv; common->debug_mask = ath9k_debug; common->btcoex_enabled = ath9k_htc_btcoex_enable == 1; spin_lock_init(&priv->beacon_lock); spin_lock_init(&priv->tx.tx_lock); mutex_init(&priv->mutex); mutex_init(&priv->htc_pm_lock); tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, (unsigned long)priv); tasklet_init(&priv->tx_failed_tasklet, ath9k_tx_failed_tasklet, (unsigned long)priv); INIT_DELAYED_WORK(&priv->ani_work, ath9k_htc_ani_work); INIT_WORK(&priv->ps_work, ath9k_ps_work); INIT_WORK(&priv->fatal_work, ath9k_fatal_work); setup_timer(&priv->tx.cleanup_timer, ath9k_htc_tx_cleanup_timer, (unsigned long)priv); /* * Cache line size is used to size and align various * structures used to communicate with the hardware. */ ath_read_cachesize(common, &csz); common->cachelsz = csz << 2; /* convert to bytes */ ret = ath9k_hw_init(ah); if (ret) { ath_err(common, "Unable to initialize hardware; initialization status: %d\n", ret); goto err_hw; } ret = ath9k_init_queues(priv); if (ret) goto err_queues; for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) priv->cur_beacon_conf.bslot[i] = NULL; ath9k_cmn_init_crypto(ah); ath9k_init_channels_rates(priv); ath9k_init_misc(priv); ath9k_htc_init_btcoex(priv, product); return 0; err_queues: ath9k_hw_deinit(ah); err_hw: kfree(ah); priv->ah = NULL; return ret; } static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw) { struct ath_common *common = ath9k_hw_common(priv->ah); hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT); hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->queues = 4; hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->vif_data_size = sizeof(struct ath9k_htc_vif); hw->sta_data_size = sizeof(struct ath9k_htc_sta); /* tx_frame_hdr is larger than tx_mgmt_hdr anyway */ hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) + sizeof(struct htc_frame_hdr) + 4; if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->sbands[IEEE80211_BAND_2GHZ]; if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->sbands[IEEE80211_BAND_5GHZ]; if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) { if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) setup_ht_cap(priv, &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap); if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) setup_ht_cap(priv, &priv->sbands[IEEE80211_BAND_5GHZ].ht_cap); } SET_IEEE80211_PERM_ADDR(hw, common->macaddr); } static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv) { struct ieee80211_hw *hw = priv->hw; struct wmi_fw_version cmd_rsp; int ret; memset(&cmd_rsp, 0, sizeof(cmd_rsp)); WMI_CMD(WMI_GET_FW_VERSION); if (ret) return -EINVAL; priv->fw_version_major = be16_to_cpu(cmd_rsp.major); priv->fw_version_minor = be16_to_cpu(cmd_rsp.minor); snprintf(hw->wiphy->fw_version, ETHTOOL_BUSINFO_LEN, "%d.%d", priv->fw_version_major, priv->fw_version_minor); dev_info(priv->dev, "ath9k_htc: FW Version: %d.%d\n", priv->fw_version_major, priv->fw_version_minor); /* * Check if the available FW matches the driver's * required version. */ if (priv->fw_version_major != MAJOR_VERSION_REQ || priv->fw_version_minor != MINOR_VERSION_REQ) { dev_err(priv->dev, "ath9k_htc: Please upgrade to FW version %d.%d\n", MAJOR_VERSION_REQ, MINOR_VERSION_REQ); return -EINVAL; } return 0; } static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid, char *product, u32 drv_info) { struct ieee80211_hw *hw = priv->hw; struct ath_common *common; struct ath_hw *ah; int error = 0; struct ath_regulatory *reg; char hw_name[64]; /* Bring up device */ error = ath9k_init_priv(priv, devid, product, drv_info); if (error != 0) goto err_init; ah = priv->ah; common = ath9k_hw_common(ah); ath9k_set_hw_capab(priv, hw); error = ath9k_init_firmware_version(priv); if (error != 0) goto err_fw; /* Initialize regulatory */ error = ath_regd_init(&common->regulatory, priv->hw->wiphy, ath9k_reg_notifier); if (error) goto err_regd; reg = &common->regulatory; /* Setup TX */ error = ath9k_tx_init(priv); if (error != 0) goto err_tx; /* Setup RX */ error = ath9k_rx_init(priv); if (error != 0) goto err_rx; #ifdef CONFIG_MAC80211_LEDS /* must be initialized before ieee80211_register_hw */ priv->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(priv->hw, IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_htc_tpt_blink, ARRAY_SIZE(ath9k_htc_tpt_blink)); #endif /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) goto err_register; /* Handle world regulatory */ if (!ath_is_world_regd(reg)) { error = regulatory_hint(hw->wiphy, reg->alpha2); if (error) goto err_world; } error = ath9k_htc_init_debug(priv->ah); if (error) { ath_err(common, "Unable to create debugfs files\n"); goto err_world; } ath_dbg(common, CONFIG, "WMI:%d, BCN:%d, CAB:%d, UAPSD:%d, MGMT:%d, BE:%d, BK:%d, VI:%d, VO:%d\n", priv->wmi_cmd_ep, priv->beacon_ep, priv->cab_ep, priv->uapsd_ep, priv->mgmt_ep, priv->data_be_ep, priv->data_bk_ep, priv->data_vi_ep, priv->data_vo_ep); ath9k_hw_name(priv->ah, hw_name, sizeof(hw_name)); wiphy_info(hw->wiphy, "%s\n", hw_name); ath9k_init_leds(priv); ath9k_start_rfkill_poll(priv); return 0; err_world: ieee80211_unregister_hw(hw); err_register: ath9k_rx_cleanup(priv); err_rx: ath9k_tx_cleanup(priv); err_tx: /* Nothing */ err_regd: /* Nothing */ err_fw: ath9k_deinit_priv(priv); err_init: return error; } int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, u16 devid, char *product, u32 drv_info) { struct ieee80211_hw *hw; struct ath9k_htc_priv *priv; int ret; hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops); if (!hw) return -ENOMEM; priv = hw->priv; priv->hw = hw; priv->htc = htc_handle; priv->dev = dev; htc_handle->drv_priv = priv; SET_IEEE80211_DEV(hw, priv->dev); ret = ath9k_htc_wait_for_target(priv); if (ret) goto err_free; priv->wmi = ath9k_init_wmi(priv); if (!priv->wmi) { ret = -EINVAL; goto err_free; } ret = ath9k_init_htc_services(priv, devid, drv_info); if (ret) goto err_init; ret = ath9k_init_device(priv, devid, product, drv_info); if (ret) goto err_init; return 0; err_init: ath9k_deinit_wmi(priv); err_free: ieee80211_free_hw(hw); return ret; } void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug) { if (htc_handle->drv_priv) { /* Check if the device has been yanked out. */ if (hotunplug) htc_handle->drv_priv->ah->ah_flags |= AH_UNPLUGGED; ath9k_deinit_device(htc_handle->drv_priv); ath9k_deinit_wmi(htc_handle->drv_priv); ieee80211_free_hw(htc_handle->drv_priv->hw); } } #ifdef CONFIG_PM void ath9k_htc_suspend(struct htc_target *htc_handle) { ath9k_htc_setpower(htc_handle->drv_priv, ATH9K_PM_FULL_SLEEP); } int ath9k_htc_resume(struct htc_target *htc_handle) { struct ath9k_htc_priv *priv = htc_handle->drv_priv; int ret; ret = ath9k_htc_wait_for_target(priv); if (ret) return ret; ret = ath9k_init_htc_services(priv, priv->ah->hw_version.devid, priv->ah->hw_version.usbdev); return ret; } #endif static int __init ath9k_htc_init(void) { if (ath9k_hif_usb_init() < 0) { pr_err("No USB devices found, driver not installed\n"); return -ENODEV; } return 0; } module_init(ath9k_htc_init); static void __exit ath9k_htc_exit(void) { ath9k_hif_usb_exit(); pr_info("Driver unloaded\n"); } module_exit(ath9k_htc_exit); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_hst.c0000644000175000017500000002750712026211315024070 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "htc.h" static int htc_issue_send(struct htc_target *target, struct sk_buff* skb, u16 len, u8 flags, u8 epid) { struct htc_frame_hdr *hdr; struct htc_endpoint *endpoint = &target->endpoint[epid]; int status; hdr = (struct htc_frame_hdr *) skb_push(skb, sizeof(struct htc_frame_hdr)); hdr->endpoint_id = epid; hdr->flags = flags; hdr->payload_len = cpu_to_be16(len); status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb); return status; } static struct htc_endpoint *get_next_avail_ep(struct htc_endpoint *endpoint) { enum htc_endpoint_id avail_epid; for (avail_epid = (ENDPOINT_MAX - 1); avail_epid > ENDPOINT0; avail_epid--) if (endpoint[avail_epid].service_id == 0) return &endpoint[avail_epid]; return NULL; } static u8 service_to_ulpipe(u16 service_id) { switch (service_id) { case WMI_CONTROL_SVC: return 4; case WMI_BEACON_SVC: case WMI_CAB_SVC: case WMI_UAPSD_SVC: case WMI_MGMT_SVC: case WMI_DATA_VO_SVC: case WMI_DATA_VI_SVC: case WMI_DATA_BE_SVC: case WMI_DATA_BK_SVC: return 1; default: return 0; } } static u8 service_to_dlpipe(u16 service_id) { switch (service_id) { case WMI_CONTROL_SVC: return 3; case WMI_BEACON_SVC: case WMI_CAB_SVC: case WMI_UAPSD_SVC: case WMI_MGMT_SVC: case WMI_DATA_VO_SVC: case WMI_DATA_VI_SVC: case WMI_DATA_BE_SVC: case WMI_DATA_BK_SVC: return 2; default: return 0; } } static void htc_process_target_rdy(struct htc_target *target, void *buf) { struct htc_endpoint *endpoint; struct htc_ready_msg *htc_ready_msg = (struct htc_ready_msg *) buf; target->credit_size = be16_to_cpu(htc_ready_msg->credit_size); endpoint = &target->endpoint[ENDPOINT0]; endpoint->service_id = HTC_CTRL_RSVD_SVC; endpoint->max_msglen = HTC_MAX_CONTROL_MESSAGE_LENGTH; atomic_inc(&target->tgt_ready); complete(&target->target_wait); } static void htc_process_conn_rsp(struct htc_target *target, struct htc_frame_hdr *htc_hdr) { struct htc_conn_svc_rspmsg *svc_rspmsg; struct htc_endpoint *endpoint, *tmp_endpoint = NULL; u16 service_id; u16 max_msglen; enum htc_endpoint_id epid, tepid; svc_rspmsg = (struct htc_conn_svc_rspmsg *) ((void *) htc_hdr + sizeof(struct htc_frame_hdr)); if (svc_rspmsg->status == HTC_SERVICE_SUCCESS) { epid = svc_rspmsg->endpoint_id; service_id = be16_to_cpu(svc_rspmsg->service_id); max_msglen = be16_to_cpu(svc_rspmsg->max_msg_len); endpoint = &target->endpoint[epid]; for (tepid = (ENDPOINT_MAX - 1); tepid > ENDPOINT0; tepid--) { tmp_endpoint = &target->endpoint[tepid]; if (tmp_endpoint->service_id == service_id) { tmp_endpoint->service_id = 0; break; } } if (tepid == ENDPOINT0) return; endpoint->service_id = service_id; endpoint->max_txqdepth = tmp_endpoint->max_txqdepth; endpoint->ep_callbacks = tmp_endpoint->ep_callbacks; endpoint->ul_pipeid = tmp_endpoint->ul_pipeid; endpoint->dl_pipeid = tmp_endpoint->dl_pipeid; endpoint->max_msglen = max_msglen; target->conn_rsp_epid = epid; complete(&target->cmd_wait); } else { target->conn_rsp_epid = ENDPOINT_UNUSED; } } static int htc_config_pipe_credits(struct htc_target *target) { struct sk_buff *skb; struct htc_config_pipe_msg *cp_msg; int ret, time_left; skb = alloc_skb(50 + sizeof(struct htc_frame_hdr), GFP_ATOMIC); if (!skb) { dev_err(target->dev, "failed to allocate send buffer\n"); return -ENOMEM; } skb_reserve(skb, sizeof(struct htc_frame_hdr)); cp_msg = (struct htc_config_pipe_msg *) skb_put(skb, sizeof(struct htc_config_pipe_msg)); cp_msg->message_id = cpu_to_be16(HTC_MSG_CONFIG_PIPE_ID); cp_msg->pipe_id = USB_WLAN_TX_PIPE; cp_msg->credits = target->credits; target->htc_flags |= HTC_OP_CONFIG_PIPE_CREDITS; ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); if (ret) goto err; time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); if (!time_left) { dev_err(target->dev, "HTC credit config timeout\n"); return -ETIMEDOUT; } return 0; err: kfree_skb(skb); return -EINVAL; } static int htc_setup_complete(struct htc_target *target) { struct sk_buff *skb; struct htc_comp_msg *comp_msg; int ret = 0, time_left; skb = alloc_skb(50 + sizeof(struct htc_frame_hdr), GFP_ATOMIC); if (!skb) { dev_err(target->dev, "failed to allocate send buffer\n"); return -ENOMEM; } skb_reserve(skb, sizeof(struct htc_frame_hdr)); comp_msg = (struct htc_comp_msg *) skb_put(skb, sizeof(struct htc_comp_msg)); comp_msg->msg_id = cpu_to_be16(HTC_MSG_SETUP_COMPLETE_ID); target->htc_flags |= HTC_OP_START_WAIT; ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); if (ret) goto err; time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); if (!time_left) { dev_err(target->dev, "HTC start timeout\n"); return -ETIMEDOUT; } return 0; err: kfree_skb(skb); return -EINVAL; } /* HTC APIs */ int htc_init(struct htc_target *target) { int ret; ret = htc_config_pipe_credits(target); if (ret) return ret; return htc_setup_complete(target); } int htc_connect_service(struct htc_target *target, struct htc_service_connreq *service_connreq, enum htc_endpoint_id *conn_rsp_epid) { struct sk_buff *skb; struct htc_endpoint *endpoint; struct htc_conn_svc_msg *conn_msg; int ret, time_left; /* Find an available endpoint */ endpoint = get_next_avail_ep(target->endpoint); if (!endpoint) { dev_err(target->dev, "Endpoint is not available for" "service %d\n", service_connreq->service_id); return -EINVAL; } endpoint->service_id = service_connreq->service_id; endpoint->max_txqdepth = service_connreq->max_send_qdepth; endpoint->ul_pipeid = service_to_ulpipe(service_connreq->service_id); endpoint->dl_pipeid = service_to_dlpipe(service_connreq->service_id); endpoint->ep_callbacks = service_connreq->ep_callbacks; skb = alloc_skb(sizeof(struct htc_conn_svc_msg) + sizeof(struct htc_frame_hdr), GFP_ATOMIC); if (!skb) { dev_err(target->dev, "Failed to allocate buf to send" "service connect req\n"); return -ENOMEM; } skb_reserve(skb, sizeof(struct htc_frame_hdr)); conn_msg = (struct htc_conn_svc_msg *) skb_put(skb, sizeof(struct htc_conn_svc_msg)); conn_msg->service_id = cpu_to_be16(service_connreq->service_id); conn_msg->msg_id = cpu_to_be16(HTC_MSG_CONNECT_SERVICE_ID); conn_msg->con_flags = cpu_to_be16(service_connreq->con_flags); conn_msg->dl_pipeid = endpoint->dl_pipeid; conn_msg->ul_pipeid = endpoint->ul_pipeid; ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); if (ret) goto err; time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); if (!time_left) { dev_err(target->dev, "Service connection timeout for: %d\n", service_connreq->service_id); return -ETIMEDOUT; } *conn_rsp_epid = target->conn_rsp_epid; return 0; err: kfree_skb(skb); return ret; } int htc_send(struct htc_target *target, struct sk_buff *skb) { struct ath9k_htc_tx_ctl *tx_ctl; tx_ctl = HTC_SKB_CB(skb); return htc_issue_send(target, skb, skb->len, 0, tx_ctl->epid); } int htc_send_epid(struct htc_target *target, struct sk_buff *skb, enum htc_endpoint_id epid) { return htc_issue_send(target, skb, skb->len, 0, epid); } void htc_stop(struct htc_target *target) { target->hif->stop(target->hif_dev); } void htc_start(struct htc_target *target) { target->hif->start(target->hif_dev); } void htc_sta_drain(struct htc_target *target, u8 idx) { target->hif->sta_drain(target->hif_dev, idx); } void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, struct sk_buff *skb, bool txok) { struct htc_endpoint *endpoint; struct htc_frame_hdr *htc_hdr = NULL; if (htc_handle->htc_flags & HTC_OP_CONFIG_PIPE_CREDITS) { complete(&htc_handle->cmd_wait); htc_handle->htc_flags &= ~HTC_OP_CONFIG_PIPE_CREDITS; goto ret; } if (htc_handle->htc_flags & HTC_OP_START_WAIT) { complete(&htc_handle->cmd_wait); htc_handle->htc_flags &= ~HTC_OP_START_WAIT; goto ret; } if (skb) { htc_hdr = (struct htc_frame_hdr *) skb->data; endpoint = &htc_handle->endpoint[htc_hdr->endpoint_id]; skb_pull(skb, sizeof(struct htc_frame_hdr)); if (endpoint->ep_callbacks.tx) { endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv, skb, htc_hdr->endpoint_id, txok); } } return; ret: /* HTC-generated packets are freed here. */ if (htc_hdr && htc_hdr->endpoint_id != ENDPOINT0) dev_kfree_skb_any(skb); else kfree_skb(skb); } /* * HTC Messages are handled directly here and the obtained SKB * is freed. * * Service messages (Data, WMI) passed to the corresponding * endpoint RX handlers, which have to free the SKB. */ void ath9k_htc_rx_msg(struct htc_target *htc_handle, struct sk_buff *skb, u32 len, u8 pipe_id) { struct htc_frame_hdr *htc_hdr; enum htc_endpoint_id epid; struct htc_endpoint *endpoint; __be16 *msg_id; if (!htc_handle || !skb) return; htc_hdr = (struct htc_frame_hdr *) skb->data; epid = htc_hdr->endpoint_id; if (epid >= ENDPOINT_MAX) { if (pipe_id != USB_REG_IN_PIPE) dev_kfree_skb_any(skb); else kfree_skb(skb); return; } if (epid == ENDPOINT0) { /* Handle trailer */ if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) { if (be32_to_cpu(*(__be32 *) skb->data) == 0x00C60000) /* Move past the Watchdog pattern */ htc_hdr = (struct htc_frame_hdr *)(skb->data + 4); } /* Get the message ID */ msg_id = (__be16 *) ((void *) htc_hdr + sizeof(struct htc_frame_hdr)); /* Now process HTC messages */ switch (be16_to_cpu(*msg_id)) { case HTC_MSG_READY_ID: htc_process_target_rdy(htc_handle, htc_hdr); break; case HTC_MSG_CONNECT_SERVICE_RESPONSE_ID: htc_process_conn_rsp(htc_handle, htc_hdr); break; default: break; } kfree_skb(skb); } else { if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) skb_trim(skb, len - htc_hdr->control[0]); skb_pull(skb, sizeof(struct htc_frame_hdr)); endpoint = &htc_handle->endpoint[epid]; if (endpoint->ep_callbacks.rx) endpoint->ep_callbacks.rx(endpoint->ep_callbacks.priv, skb, epid); } } struct htc_target *ath9k_htc_hw_alloc(void *hif_handle, struct ath9k_htc_hif *hif, struct device *dev) { struct htc_endpoint *endpoint; struct htc_target *target; target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); if (!target) return NULL; init_completion(&target->target_wait); init_completion(&target->cmd_wait); target->hif = hif; target->hif_dev = hif_handle; target->dev = dev; /* Assign control endpoint pipe IDs */ endpoint = &target->endpoint[ENDPOINT0]; endpoint->ul_pipeid = hif->control_ul_pipe; endpoint->dl_pipeid = hif->control_dl_pipe; atomic_set(&target->tgt_ready, 0); return target; } void ath9k_htc_hw_free(struct htc_target *htc) { kfree(htc); } int ath9k_htc_hw_init(struct htc_target *target, struct device *dev, u16 devid, char *product, u32 drv_info) { if (ath9k_htc_probe_device(target, dev, devid, product, drv_info)) { pr_err("Failed to initialize the device\n"); return -ENODEV; } return 0; } void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug) { if (target) ath9k_htc_disconnect_device(target, hot_unplug); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/pci.c0000644000175000017500000002356012026211315023202 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include "ath9k.h" static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */ { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E AR9300 */ { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */ { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */ { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */ { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */ { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E AR9565 */ { 0 } }; /* return bus cachesize in 4B word units */ static void ath_pci_read_cachesize(struct ath_common *common, int *csz) { struct ath_softc *sc = (struct ath_softc *) common->priv; u8 u8tmp; pci_read_config_byte(to_pci_dev(sc->dev), PCI_CACHE_LINE_SIZE, &u8tmp); *csz = (int)u8tmp; /* * This check was put in to avoid "unpleasant" consequences if * the bootrom has not fully initialized all PCI devices. * Sometimes the cache line size register is not set */ if (*csz == 0) *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */ } static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath_softc *sc = (struct ath_softc *) common->priv; struct ath9k_platform_data *pdata = sc->dev->platform_data; if (pdata) { if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { ath_err(common, "%s: eeprom read failed, offset %08x is out of range\n", __func__, off); } *data = pdata->eeprom_data[off]; } else { struct ath_hw *ah = (struct ath_hw *) common->ah; common->ops->read(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); if (!ath9k_hw_wait(ah, AR_EEPROM_STATUS_DATA, AR_EEPROM_STATUS_DATA_BUSY | AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0, AH_WAIT_TIMEOUT)) { return false; } *data = MS(common->ops->read(ah, AR_EEPROM_STATUS_DATA), AR_EEPROM_STATUS_DATA_VAL); } return true; } static void ath_pci_extn_synch_enable(struct ath_common *common) { struct ath_softc *sc = (struct ath_softc *) common->priv; struct pci_dev *pdev = to_pci_dev(sc->dev); u8 lnkctl; pci_read_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, &lnkctl); lnkctl |= PCI_EXP_LNKCTL_ES; pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl); } /* Need to be called after we discover btcoex capabilities */ static void ath_pci_aspm_init(struct ath_common *common) { struct ath_softc *sc = (struct ath_softc *) common->priv; struct ath_hw *ah = sc->sc_ah; struct pci_dev *pdev = to_pci_dev(sc->dev); struct pci_dev *parent; u16 aspm; if (!ah->is_pciexpress) return; parent = pdev->bus->self; if (!parent) return; if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) { /* Bluetooth coexistance requires disabling ASPM. */ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); /* * Both upstream and downstream PCIe components should * have the same ASPM settings. */ pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); ath_info(common, "Disabling ASPM since BTCOEX is enabled\n"); return; } pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm); if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) { ah->aspm_enabled = true; /* Initialize PCIe PM and SERDES registers. */ ath9k_hw_configpcipowersave(ah, false); ath_info(common, "ASPM enabled: 0x%x\n", aspm); } } static const struct ath_bus_ops ath_pci_bus_ops = { .ath_bus_type = ATH_PCI, .read_cachesize = ath_pci_read_cachesize, .eeprom_read = ath_pci_eeprom_read, .extn_synch_en = ath_pci_extn_synch_enable, .aspm_init = ath_pci_aspm_init, }; static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { void __iomem *mem; struct ath_softc *sc; struct ieee80211_hw *hw; u8 csz; u32 val; int ret = 0; char hw_name[64]; if (pci_enable_device(pdev)) return -EIO; ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { pr_err("32-bit DMA not available\n"); goto err_dma; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { pr_err("32-bit DMA consistent DMA enable failed\n"); goto err_dma; } /* * Cache line size is used to size and align various * structures used to communicate with the hardware. */ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); if (csz == 0) { /* * Linux 2.4.18 (at least) writes the cache line size * register as a 16-bit wide register which is wrong. * We must have this setup properly for rx buffer * DMA to work so force a reasonable value here if it * comes up zero. */ csz = L1_CACHE_BYTES / sizeof(u32); pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); } /* * The default setting of latency timer yields poor results, * set it to the value used by other systems. It may be worth * tweaking this setting more. */ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); pci_set_master(pdev); /* * Disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state. */ pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); ret = pci_request_region(pdev, 0, "ath9k"); if (ret) { dev_err(&pdev->dev, "PCI memory region reserve error\n"); ret = -ENODEV; goto err_region; } mem = pci_iomap(pdev, 0, 0); if (!mem) { pr_err("PCI memory map error\n") ; ret = -EIO; goto err_iomap; } hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (!hw) { dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); ret = -ENOMEM; goto err_alloc_hw; } SET_IEEE80211_DEV(hw, &pdev->dev); pci_set_drvdata(pdev, hw); sc = hw->priv; sc->hw = hw; sc->dev = &pdev->dev; sc->mem = mem; /* Will be cleared in ath9k_start() */ set_bit(SC_OP_INVALID, &sc->sc_flags); ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); goto err_irq; } sc->irq = pdev->irq; ret = ath9k_init_device(id->device, sc, &ath_pci_bus_ops); if (ret) { dev_err(&pdev->dev, "Failed to initialize device\n"); goto err_init; } ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name)); wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n", hw_name, (unsigned long)mem, pdev->irq); return 0; err_init: free_irq(sc->irq, sc); err_irq: ieee80211_free_hw(hw); err_alloc_hw: pci_iounmap(pdev, mem); err_iomap: pci_release_region(pdev, 0); err_region: /* Nothing */ err_dma: pci_disable_device(pdev); return ret; } static void ath_pci_remove(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_softc *sc = hw->priv; void __iomem *mem = sc->mem; if (!is_ath9k_unloaded) sc->sc_ah->ah_flags |= AH_UNPLUGGED; ath9k_deinit_device(sc); free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); pci_iounmap(pdev, mem); pci_disable_device(pdev); pci_release_region(pdev, 0); } #ifdef CONFIG_PM static int ath_pci_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_softc *sc = hw->priv; if (sc->wow_enabled) return 0; /* The device has to be moved to FULLSLEEP forcibly. * Otherwise the chip never moved to full sleep, * when no interface is up. */ ath9k_stop_btcoex(sc); ath9k_hw_disable(sc->sc_ah); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); return 0; } static int ath_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); u32 val; /* * Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); return 0; } compat_pci_suspend(ath_pci_suspend) compat_pci_resume(ath_pci_resume) static SIMPLE_DEV_PM_OPS(ath9k_pm_ops, ath_pci_suspend, ath_pci_resume); #define ATH9K_PM_OPS (&ath9k_pm_ops) #else /* !CONFIG_PM */ #define ATH9K_PM_OPS NULL #endif /* !CONFIG_PM */ MODULE_DEVICE_TABLE(pci, ath_pci_id_table); static struct pci_driver ath_pci_driver = { .name = "ath9k", .id_table = ath_pci_id_table, .probe = ath_pci_probe, .remove = ath_pci_remove, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATH9K_PM_OPS, #elif defined(CONFIG_PM) .suspend = ath_pci_suspend_compat, .resume = ath_pci_resume_compat, #endif }; int ath_pci_init(void) { return pci_register_driver(&ath_pci_driver); } void ath_pci_exit(void) { pci_unregister_driver(&ath_pci_driver); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/init.c0000644000175000017500000006346112026211315023376 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include "ath9k.h" static char *dev_info = "ath9k"; MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); static unsigned int ath9k_debug = ATH_DBG_DEFAULT; module_param_named(debug, ath9k_debug, uint, 0); MODULE_PARM_DESC(debug, "Debugging mask"); int ath9k_modparam_nohwcrypt; module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); int led_blink; module_param_named(blink, led_blink, int, 0444); MODULE_PARM_DESC(blink, "Enable LED blink on activity"); static int ath9k_btcoex_enable; module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444); MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); bool is_ath9k_unloaded; /* We use the hw_value as an index into our private channel structure */ #define CHAN2G(_freq, _idx) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 20, \ } #define CHAN5G(_freq, _idx) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 20, \ } /* Some 2 GHz radios are actually tunable on 2312-2732 * on 5 MHz steps, we support the channels which we know * we have calibration data for all cards though to make * this static */ static const struct ieee80211_channel ath9k_2ghz_chantable[] = { CHAN2G(2412, 0), /* Channel 1 */ CHAN2G(2417, 1), /* Channel 2 */ CHAN2G(2422, 2), /* Channel 3 */ CHAN2G(2427, 3), /* Channel 4 */ CHAN2G(2432, 4), /* Channel 5 */ CHAN2G(2437, 5), /* Channel 6 */ CHAN2G(2442, 6), /* Channel 7 */ CHAN2G(2447, 7), /* Channel 8 */ CHAN2G(2452, 8), /* Channel 9 */ CHAN2G(2457, 9), /* Channel 10 */ CHAN2G(2462, 10), /* Channel 11 */ CHAN2G(2467, 11), /* Channel 12 */ CHAN2G(2472, 12), /* Channel 13 */ CHAN2G(2484, 13), /* Channel 14 */ }; /* Some 5 GHz radios are actually tunable on XXXX-YYYY * on 5 MHz steps, we support the channels which we know * we have calibration data for all cards though to make * this static */ static const struct ieee80211_channel ath9k_5ghz_chantable[] = { /* _We_ call this UNII 1 */ CHAN5G(5180, 14), /* Channel 36 */ CHAN5G(5200, 15), /* Channel 40 */ CHAN5G(5220, 16), /* Channel 44 */ CHAN5G(5240, 17), /* Channel 48 */ /* _We_ call this UNII 2 */ CHAN5G(5260, 18), /* Channel 52 */ CHAN5G(5280, 19), /* Channel 56 */ CHAN5G(5300, 20), /* Channel 60 */ CHAN5G(5320, 21), /* Channel 64 */ /* _We_ call this "Middle band" */ CHAN5G(5500, 22), /* Channel 100 */ CHAN5G(5520, 23), /* Channel 104 */ CHAN5G(5540, 24), /* Channel 108 */ CHAN5G(5560, 25), /* Channel 112 */ CHAN5G(5580, 26), /* Channel 116 */ CHAN5G(5600, 27), /* Channel 120 */ CHAN5G(5620, 28), /* Channel 124 */ CHAN5G(5640, 29), /* Channel 128 */ CHAN5G(5660, 30), /* Channel 132 */ CHAN5G(5680, 31), /* Channel 136 */ CHAN5G(5700, 32), /* Channel 140 */ /* _We_ call this UNII 3 */ CHAN5G(5745, 33), /* Channel 149 */ CHAN5G(5765, 34), /* Channel 153 */ CHAN5G(5785, 35), /* Channel 157 */ CHAN5G(5805, 36), /* Channel 161 */ CHAN5G(5825, 37), /* Channel 165 */ }; /* Atheros hardware rate code addition for short premble */ #define SHPCHECK(__hw_rate, __flags) \ ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) #define RATE(_bitrate, _hw_rate, _flags) { \ .bitrate = (_bitrate), \ .flags = (_flags), \ .hw_value = (_hw_rate), \ .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ } static struct ieee80211_rate ath9k_legacy_rates[] = { RATE(10, 0x1b, 0), RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), RATE(60, 0x0b, 0), RATE(90, 0x0f, 0), RATE(120, 0x0a, 0), RATE(180, 0x0e, 0), RATE(240, 0x09, 0), RATE(360, 0x0d, 0), RATE(480, 0x08, 0), RATE(540, 0x0c, 0), }; #ifdef CONFIG_MAC80211_LEDS static const struct ieee80211_tpt_blink ath9k_tpt_blink[] = { { .throughput = 0 * 1024, .blink_time = 334 }, { .throughput = 1 * 1024, .blink_time = 260 }, { .throughput = 5 * 1024, .blink_time = 220 }, { .throughput = 10 * 1024, .blink_time = 190 }, { .throughput = 20 * 1024, .blink_time = 170 }, { .throughput = 50 * 1024, .blink_time = 150 }, { .throughput = 70 * 1024, .blink_time = 130 }, { .throughput = 100 * 1024, .blink_time = 110 }, { .throughput = 200 * 1024, .blink_time = 80 }, { .throughput = 300 * 1024, .blink_time = 50 }, }; #endif static void ath9k_deinit_softc(struct ath_softc *sc); /* * Read and write, they both share the same lock. We do this to serialize * reads and writes on Atheros 802.11n PCI devices only. This is required * as the FIFO on these devices can only accept sanely 2 requests. */ static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath_softc *sc = (struct ath_softc *) common->priv; if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) { unsigned long flags; spin_lock_irqsave(&sc->sc_serial_rw, flags); iowrite32(val, sc->mem + reg_offset); spin_unlock_irqrestore(&sc->sc_serial_rw, flags); } else iowrite32(val, sc->mem + reg_offset); } static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath_softc *sc = (struct ath_softc *) common->priv; u32 val; if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) { unsigned long flags; spin_lock_irqsave(&sc->sc_serial_rw, flags); val = ioread32(sc->mem + reg_offset); spin_unlock_irqrestore(&sc->sc_serial_rw, flags); } else val = ioread32(sc->mem + reg_offset); return val; } static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset, u32 set, u32 clr) { u32 val; val = ioread32(sc->mem + reg_offset); val &= ~clr; val |= set; iowrite32(val, sc->mem + reg_offset); return val; } static unsigned int ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) { struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath_softc *sc = (struct ath_softc *) common->priv; unsigned long uninitialized_var(flags); u32 val; if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) { spin_lock_irqsave(&sc->sc_serial_rw, flags); val = __ath9k_reg_rmw(sc, reg_offset, set, clr); spin_unlock_irqrestore(&sc->sc_serial_rw, flags); } else val = __ath9k_reg_rmw(sc, reg_offset, set, clr); return val; } /**************************/ /* Initialization */ /**************************/ static void setup_ht_cap(struct ath_softc *sc, struct ieee80211_sta_ht_cap *ht_info) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u8 tx_streams, rx_streams; int i, max_streams; ht_info->ht_supported = true; ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_LDPC) ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20) ht_info->cap |= IEEE80211_HT_CAP_SGI_20; ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) max_streams = 1; else if (AR_SREV_9462(ah)) max_streams = 2; else if (AR_SREV_9300_20_OR_LATER(ah)) max_streams = 3; else max_streams = 2; if (AR_SREV_9280_20_OR_LATER(ah)) { if (max_streams >= 2) ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); } /* set up supported mcs set */ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams); rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams); ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", tx_streams, rx_streams); if (tx_streams != rx_streams) { ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); } for (i = 0; i < rx_streams; i++) ht_info->mcs.rx_mask[i] = 0xff; ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; } static int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_regulatory *reg = ath9k_hw_regulatory(ah); int ret; ret = ath_reg_notifier_apply(wiphy, request, reg); /* Set tx power */ if (ah->curchan) { sc->config.txpowlimit = 2 * ah->curchan->chan->max_power; ath9k_ps_wakeup(sc); ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit; ath9k_ps_restore(sc); } return ret; } /* * This function will allocate both the DMA descriptor structure, and the * buffers it contains. These are used to contain the descriptors used * by the system. */ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, struct list_head *head, const char *name, int nbuf, int ndesc, bool is_tx) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 *ds; struct ath_buf *bf; int i, bsize, error, desc_len; ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", name, nbuf, ndesc); INIT_LIST_HEAD(head); if (is_tx) desc_len = sc->sc_ah->caps.tx_desc_len; else desc_len = sizeof(struct ath_desc); /* ath_desc must be a multiple of DWORDs */ if ((desc_len % 4) != 0) { ath_err(common, "ath_desc not DWORD aligned\n"); BUG_ON((desc_len % 4) != 0); error = -ENOMEM; goto fail; } dd->dd_desc_len = desc_len * nbuf * ndesc; /* * Need additional DMA memory because we can't use * descriptors that cross the 4K page boundary. Assume * one skipped descriptor per 4K page. */ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { u32 ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); u32 dma_len; while (ndesc_skipped) { dma_len = ndesc_skipped * desc_len; dd->dd_desc_len += dma_len; ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); } } /* allocate descriptors */ dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, &dd->dd_desc_paddr, GFP_KERNEL); if (dd->dd_desc == NULL) { error = -ENOMEM; goto fail; } ds = (u8 *) dd->dd_desc; ath_dbg(common, CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", name, ds, (u32) dd->dd_desc_len, ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); /* allocate buffers */ bsize = sizeof(struct ath_buf) * nbuf; bf = kzalloc(bsize, GFP_KERNEL); if (bf == NULL) { error = -ENOMEM; goto fail2; } dd->dd_bufptr = bf; for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { bf->bf_desc = ds; bf->bf_daddr = DS2PHYS(dd, ds); if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { /* * Skip descriptor addresses which can cause 4KB * boundary crossing (addr + length) with a 32 dword * descriptor fetch. */ while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { BUG_ON((caddr_t) bf->bf_desc >= ((caddr_t) dd->dd_desc + dd->dd_desc_len)); ds += (desc_len * ndesc); bf->bf_desc = ds; bf->bf_daddr = DS2PHYS(dd, ds); } } list_add_tail(&bf->list, head); } return 0; fail2: dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); fail: memset(dd, 0, sizeof(*dd)); return error; } static int ath9k_init_queues(struct ath_softc *sc) { int i = 0; sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah); sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); for (i = 0; i < WME_NUM_AC; i++) { sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); sc->tx.txq_map[i]->mac80211_qnum = i; sc->tx.txq_max_pending[i] = ATH_MAX_QDEPTH; } return 0; } static int ath9k_init_channels_rates(struct ath_softc *sc) { void *channels; BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) + ARRAY_SIZE(ath9k_5ghz_chantable) != ATH9K_NUM_CHANNELS); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { channels = kmemdup(ath9k_2ghz_chantable, sizeof(ath9k_2ghz_chantable), GFP_KERNEL); if (!channels) return -ENOMEM; sc->sbands[IEEE80211_BAND_2GHZ].channels = channels; sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; sc->sbands[IEEE80211_BAND_2GHZ].n_channels = ARRAY_SIZE(ath9k_2ghz_chantable); sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates = ARRAY_SIZE(ath9k_legacy_rates); } if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { channels = kmemdup(ath9k_5ghz_chantable, sizeof(ath9k_5ghz_chantable), GFP_KERNEL); if (!channels) { if (sc->sbands[IEEE80211_BAND_2GHZ].channels) kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels); return -ENOMEM; } sc->sbands[IEEE80211_BAND_5GHZ].channels = channels; sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; sc->sbands[IEEE80211_BAND_5GHZ].n_channels = ARRAY_SIZE(ath9k_5ghz_chantable); sc->sbands[IEEE80211_BAND_5GHZ].bitrates = ath9k_legacy_rates + 4; sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = ARRAY_SIZE(ath9k_legacy_rates) - 4; } return 0; } static void ath9k_init_misc(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); int i = 0; setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); sc->last_rssi = ATH_RSSI_DUMMY_MARKER; sc->config.txpowlimit = ATH_TXPOWER_MAX; memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); sc->beacon.slottime = ATH9K_SLOT_TIME_9; for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) sc->beacon.bslot[i] = NULL; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT; } static int ath9k_init_softc(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { struct ath9k_platform_data *pdata = sc->dev->platform_data; struct ath_hw *ah = NULL; struct ath_common *common; int ret = 0, i; int csz = 0; ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); if (!ah) return -ENOMEM; ah->hw = sc->hw; ah->hw_version.devid = devid; ah->reg_ops.read = ath9k_ioread32; ah->reg_ops.write = ath9k_iowrite32; ah->reg_ops.rmw = ath9k_reg_rmw; atomic_set(&ah->intr_ref_cnt, -1); sc->sc_ah = ah; sc->dfs_detector = dfs_pattern_detector_init(NL80211_DFS_UNSET); if (!pdata) { ah->ah_flags |= AH_USE_EEPROM; sc->sc_ah->led_pin = -1; } else { sc->sc_ah->gpio_mask = pdata->gpio_mask; sc->sc_ah->gpio_val = pdata->gpio_val; sc->sc_ah->led_pin = pdata->led_pin; ah->is_clk_25mhz = pdata->is_clk_25mhz; ah->get_mac_revision = pdata->get_mac_revision; ah->external_reset = pdata->external_reset; } common = ath9k_hw_common(ah); common->ops = &ah->reg_ops; common->bus_ops = bus_ops; common->ah = ah; common->hw = sc->hw; common->priv = sc; common->debug_mask = ath9k_debug; common->btcoex_enabled = ath9k_btcoex_enable == 1; common->disable_ani = false; spin_lock_init(&common->cc_lock); spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); mutex_init(&sc->mutex); #ifdef CONFIG_ATH9K_DEBUGFS spin_lock_init(&sc->nodes_lock); INIT_LIST_HEAD(&sc->nodes); #endif #ifdef CONFIG_ATH9K_MAC_DEBUG spin_lock_init(&sc->debug.samp_lock); #endif tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, (unsigned long)sc); INIT_WORK(&sc->hw_reset_work, ath_reset_work); INIT_WORK(&sc->hw_check_work, ath_hw_check); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc); /* * Cache line size is used to size and align various * structures used to communicate with the hardware. */ ath_read_cachesize(common, &csz); common->cachelsz = csz << 2; /* convert to bytes */ /* Initializes the hardware for all supported chipsets */ ret = ath9k_hw_init(ah); if (ret) goto err_hw; if (pdata && pdata->macaddr) memcpy(common->macaddr, pdata->macaddr, ETH_ALEN); ret = ath9k_init_queues(sc); if (ret) goto err_queues; ret = ath9k_init_btcoex(sc); if (ret) goto err_btcoex; ret = ath9k_init_channels_rates(sc); if (ret) goto err_btcoex; ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); if (common->bus_ops->aspm_init) common->bus_ops->aspm_init(common); return 0; err_btcoex: for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); err_queues: ath9k_hw_deinit(ah); err_hw: kfree(ah); sc->sc_ah = NULL; return ret; } static void ath9k_init_band_txpower(struct ath_softc *sc, int band) { struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; struct ath_hw *ah = sc->sc_ah; int i; sband = &sc->sbands[band]; for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; ah->curchan = &ah->channels[chan->hw_value]; ath9k_cmn_update_ichannel(ah->curchan, chan, NL80211_CHAN_HT20); ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true); } } static void ath9k_init_txpower_limits(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath9k_channel *curchan = ah->curchan; if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ); if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) ath9k_init_band_txpower(sc, IEEE80211_BAND_5GHZ); ah->curchan = curchan; } void ath9k_reload_chainmask_settings(struct ath_softc *sc) { if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)) return; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); } static const struct ieee80211_iface_limit if_limits[] = { { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_WDS) }, { .max = 8, .types = #ifdef CONFIG_MAC80211_MESH BIT(NL80211_IFTYPE_MESH_POINT) | #endif BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) }, }; static const struct ieee80211_iface_combination if_comb = { .limits = if_limits, .n_limits = ARRAY_SIZE(if_limits), .max_interfaces = 2048, .num_different_channels = 1, }; void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt) hw->flags |= IEEE80211_HW_MFP_CAPABLE; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_WDS) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); hw->wiphy->iface_combinations = &if_comb; hw->wiphy->n_iface_combinations = 1; if (AR_SREV_5416(sc->sc_ah)) hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; #ifdef CONFIG_PM_SLEEP if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && device_can_wakeup(sc->dev)) { hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT; hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN; hw->wiphy->wowlan.pattern_min_len = 1; hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE; } atomic_set(&sc->wow_sleep_proc_intr, -1); atomic_set(&sc->wow_got_bmiss_intr, -1); #endif hw->queues = 4; hw->max_rates = 4; hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; /* single chain devices with rx diversity */ if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) hw->wiphy->available_antennas_rx = BIT(0) | BIT(1); sc->ant_rx = hw->wiphy->available_antennas_rx; sc->ant_tx = hw->wiphy->available_antennas_tx; #ifdef CONFIG_COMPAT_ATH9K_RATE_CONTROL hw->rate_control_algorithm = "ath9k_rate_control"; #endif if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &sc->sbands[IEEE80211_BAND_2GHZ]; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &sc->sbands[IEEE80211_BAND_5GHZ]; ath9k_reload_chainmask_settings(sc); SET_IEEE80211_PERM_ADDR(hw, common->macaddr); } int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { struct ieee80211_hw *hw = sc->hw; struct ath_common *common; struct ath_hw *ah; int error = 0; struct ath_regulatory *reg; /* Bring up device */ error = ath9k_init_softc(devid, sc, bus_ops); if (error != 0) goto error_init; ah = sc->sc_ah; common = ath9k_hw_common(ah); ath9k_set_hw_capab(sc, hw); /* Initialize regulatory */ error = ath_regd_init(&common->regulatory, sc->hw->wiphy, ath9k_reg_notifier); if (error) goto error_regd; reg = &common->regulatory; /* Setup TX DMA */ error = ath_tx_init(sc, ATH_TXBUF); if (error != 0) goto error_tx; /* Setup RX DMA */ error = ath_rx_init(sc, ATH_RXBUF); if (error != 0) goto error_rx; ath9k_init_txpower_limits(sc); #ifdef CONFIG_MAC80211_LEDS /* must be initialized before ieee80211_register_hw */ sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw, IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink, ARRAY_SIZE(ath9k_tpt_blink)); #endif /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) goto error_register; error = ath9k_init_debug(ah); if (error) { ath_err(common, "Unable to create debugfs files\n"); goto error_world; } /* Handle world regulatory */ if (!ath_is_world_regd(reg)) { error = regulatory_hint(hw->wiphy, reg->alpha2); if (error) goto error_world; } ath_init_leds(sc); ath_start_rfkill_poll(sc); return 0; error_world: ieee80211_unregister_hw(hw); error_register: ath_rx_cleanup(sc); error_rx: ath_tx_cleanup(sc); error_tx: /* Nothing */ error_regd: ath9k_deinit_softc(sc); error_init: return error; } /*****************************/ /* De-Initialization */ /*****************************/ static void ath9k_deinit_softc(struct ath_softc *sc) { int i = 0; if (sc->sbands[IEEE80211_BAND_2GHZ].channels) kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels); if (sc->sbands[IEEE80211_BAND_5GHZ].channels) kfree(sc->sbands[IEEE80211_BAND_5GHZ].channels); ath9k_deinit_btcoex(sc); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); ath9k_hw_deinit(sc->sc_ah); if (sc->dfs_detector != NULL) sc->dfs_detector->exit(sc->dfs_detector); kfree(sc->sc_ah); sc->sc_ah = NULL; } void ath9k_deinit_device(struct ath_softc *sc) { struct ieee80211_hw *hw = sc->hw; ath9k_ps_wakeup(sc); wiphy_rfkill_stop_polling(sc->hw->wiphy); ath_deinit_leds(sc); ath9k_ps_restore(sc); ieee80211_unregister_hw(hw); ath_rx_cleanup(sc); ath_tx_cleanup(sc); ath9k_deinit_softc(sc); } void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, struct list_head *head) { dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); INIT_LIST_HEAD(head); kfree(dd->dd_bufptr); memset(dd, 0, sizeof(*dd)); } /************************/ /* Module Hooks */ /************************/ static int __init ath9k_init(void) { int error; /* Register rate control algorithm */ error = ath_rate_control_register(); if (error != 0) { pr_err("Unable to register rate control algorithm: %d\n", error); goto err_out; } error = ath_pci_init(); if (error < 0) { pr_err("No PCI devices found, driver not installed\n"); error = -ENODEV; goto err_rate_unregister; } error = ath_ahb_init(); if (error < 0) { error = -ENODEV; goto err_pci_exit; } return 0; err_pci_exit: ath_pci_exit(); err_rate_unregister: ath_rate_control_unregister(); err_out: return error; } module_init(ath9k_init); static void __exit ath9k_exit(void) { is_ath9k_unloaded = true; ath_ahb_exit(); ath_pci_exit(); ath_rate_control_unregister(); pr_info("%s: Driver unloaded\n", dev_info); } module_exit(ath9k_exit); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/rc.h0000644000175000017500000001647212026211315023044 0ustar mcgrofmcgrof/* * Copyright (c) 2004 Sam Leffler, Errno Consulting * Copyright (c) 2004 Video54 Technologies, Inc. * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef RC_H #define RC_H #include "hw.h" struct ath_softc; #define ATH_RATE_MAX 30 #define RATE_TABLE_SIZE 72 #define RC_INVALID 0x0000 #define RC_LEGACY 0x0001 #define RC_SS 0x0002 #define RC_DS 0x0004 #define RC_TS 0x0008 #define RC_HT_20 0x0010 #define RC_HT_40 0x0020 #define RC_STREAM_MASK 0xe #define RC_DS_OR_LATER(f) ((((f) & RC_STREAM_MASK) == RC_DS) || \ (((f) & RC_STREAM_MASK) == (RC_DS | RC_TS))) #define RC_TS_ONLY(f) (((f) & RC_STREAM_MASK) == RC_TS) #define RC_SS_OR_LEGACY(f) ((f) & (RC_SS | RC_LEGACY)) #define RC_HT_2040 (RC_HT_20 | RC_HT_40) #define RC_ALL_STREAM (RC_SS | RC_DS | RC_TS) #define RC_L_SD (RC_LEGACY | RC_SS | RC_DS) #define RC_L_SDT (RC_LEGACY | RC_SS | RC_DS | RC_TS) #define RC_HT_S_20 (RC_HT_20 | RC_SS) #define RC_HT_D_20 (RC_HT_20 | RC_DS) #define RC_HT_T_20 (RC_HT_20 | RC_TS) #define RC_HT_S_40 (RC_HT_40 | RC_SS) #define RC_HT_D_40 (RC_HT_40 | RC_DS) #define RC_HT_T_40 (RC_HT_40 | RC_TS) #define RC_HT_SD_20 (RC_HT_20 | RC_SS | RC_DS) #define RC_HT_DT_20 (RC_HT_20 | RC_DS | RC_TS) #define RC_HT_SD_40 (RC_HT_40 | RC_SS | RC_DS) #define RC_HT_DT_40 (RC_HT_40 | RC_DS | RC_TS) #define RC_HT_SD_2040 (RC_HT_2040 | RC_SS | RC_DS) #define RC_HT_SDT_2040 (RC_HT_2040 | RC_SS | RC_DS | RC_TS) #define RC_HT_SDT_20 (RC_HT_20 | RC_SS | RC_DS | RC_TS) #define RC_HT_SDT_40 (RC_HT_40 | RC_SS | RC_DS | RC_TS) #define RC_ALL (RC_LEGACY | RC_HT_2040 | RC_ALL_STREAM) enum { WLAN_RC_PHY_OFDM, WLAN_RC_PHY_CCK, WLAN_RC_PHY_HT_20_SS, WLAN_RC_PHY_HT_20_DS, WLAN_RC_PHY_HT_20_TS, WLAN_RC_PHY_HT_40_SS, WLAN_RC_PHY_HT_40_DS, WLAN_RC_PHY_HT_40_TS, WLAN_RC_PHY_HT_20_SS_HGI, WLAN_RC_PHY_HT_20_DS_HGI, WLAN_RC_PHY_HT_20_TS_HGI, WLAN_RC_PHY_HT_40_SS_HGI, WLAN_RC_PHY_HT_40_DS_HGI, WLAN_RC_PHY_HT_40_TS_HGI, WLAN_RC_PHY_MAX }; #define WLAN_RC_PHY_DS(_phy) ((_phy == WLAN_RC_PHY_HT_20_DS) \ || (_phy == WLAN_RC_PHY_HT_40_DS) \ || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) #define WLAN_RC_PHY_TS(_phy) ((_phy == WLAN_RC_PHY_HT_20_TS) \ || (_phy == WLAN_RC_PHY_HT_40_TS) \ || (_phy == WLAN_RC_PHY_HT_20_TS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_TS_HGI)) #define WLAN_RC_PHY_20(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS) \ || (_phy == WLAN_RC_PHY_HT_20_DS) \ || (_phy == WLAN_RC_PHY_HT_20_TS) \ || (_phy == WLAN_RC_PHY_HT_20_SS_HGI) \ || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ || (_phy == WLAN_RC_PHY_HT_20_TS_HGI)) #define WLAN_RC_PHY_40(_phy) ((_phy == WLAN_RC_PHY_HT_40_SS) \ || (_phy == WLAN_RC_PHY_HT_40_DS) \ || (_phy == WLAN_RC_PHY_HT_40_TS) \ || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_DS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_TS_HGI)) #define WLAN_RC_PHY_SGI(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS_HGI) \ || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ || (_phy == WLAN_RC_PHY_HT_20_TS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_DS_HGI) \ || (_phy == WLAN_RC_PHY_HT_40_TS_HGI)) #define WLAN_RC_PHY_HT(_phy) (_phy >= WLAN_RC_PHY_HT_20_SS) #define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \ ((capflag & WLAN_RC_40_FLAG) ? RC_HT_40 : RC_HT_20) : RC_LEGACY)) #define WLAN_RC_CAP_STREAM(capflag) (((capflag & WLAN_RC_TS_FLAG) ? \ (RC_TS) : ((capflag & WLAN_RC_DS_FLAG) ? RC_DS : RC_SS))) /* Return TRUE if flag supports HT20 && client supports HT20 or * return TRUE if flag supports HT40 && client supports HT40. * This is used becos some rates overlap between HT20/HT40. */ #define WLAN_RC_PHY_HT_VALID(flag, capflag) \ (((flag & RC_HT_20) && !(capflag & WLAN_RC_40_FLAG)) || \ ((flag & RC_HT_40) && (capflag & WLAN_RC_40_FLAG))) #define WLAN_RC_DS_FLAG (0x01) #define WLAN_RC_TS_FLAG (0x02) #define WLAN_RC_40_FLAG (0x04) #define WLAN_RC_SGI_FLAG (0x08) #define WLAN_RC_HT_FLAG (0x10) /** * struct ath_rate_table - Rate Control table * @rate_cnt: total number of rates for the given wireless mode * @mcs_start: MCS rate index offset * @rate_flags: Rate Control flags * @phy: CCK/OFDM/HT20/HT40 * @ratekbps: rate in Kbits per second * @user_ratekbps: user rate in Kbits per second * @ratecode: rate that goes into HW descriptors * @dot11rate: value that goes into supported * rates info element of MLME * @ctrl_rate: Index of next lower basic rate, used for duration computation * @cw40index: Index of rates having 40MHz channel width * @sgi_index: Index of rates having Short Guard Interval * @ht_index: high throughput rates having 40MHz channel width and * Short Guard Interval * @probe_interval: interval for rate control to probe for other rates * @initial_ratemax: initial ratemax value */ struct ath_rate_table { int rate_cnt; int mcs_start; struct { u16 rate_flags; u8 phy; u32 ratekbps; u32 user_ratekbps; u8 ratecode; u8 dot11rate; } info[RATE_TABLE_SIZE]; u32 probe_interval; u8 initial_ratemax; }; struct ath_rateset { u8 rs_nrates; u8 rs_rates[ATH_RATE_MAX]; }; struct ath_rc_stats { u32 success; u32 retries; u32 xretries; u8 per; }; /** * struct ath_rate_priv - Rate Control priv data * @state: RC state * @probe_rate: rate we are probing at * @probe_time: msec timestamp for last probe * @hw_maxretry_pktcnt: num of packets since we got HW max retry error * @max_valid_rate: maximum number of valid rate * @per_down_time: msec timestamp for last PER down step * @valid_phy_ratecnt: valid rate count * @rate_max_phy: phy index for the max rate * @per: PER for every valid rate in % * @probe_interval: interval for ratectrl to probe for other rates * @ht_cap: HT capabilities * @neg_rates: Negotatied rates * @neg_ht_rates: Negotiated HT rates */ struct ath_rate_priv { u8 rate_table_size; u8 probe_rate; u8 hw_maxretry_pktcnt; u8 max_valid_rate; u8 valid_rate_index[RATE_TABLE_SIZE]; u8 ht_cap; u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX]; u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE]; u8 rate_max_phy; u8 per[RATE_TABLE_SIZE]; u32 probe_time; u32 per_down_time; u32 probe_interval; struct ath_rateset neg_rates; struct ath_rateset neg_ht_rates; const struct ath_rate_table *rate_table; struct dentry *debugfs_rcstats; struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; }; #ifdef CONFIG_COMPAT_ATH9K_RATE_CONTROL int ath_rate_control_register(void); void ath_rate_control_unregister(void); #else static inline int ath_rate_control_register(void) { return 0; } static inline void ath_rate_control_unregister(void) { } #endif #endif /* RC_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/Makefile0000644000175000017500000000240212026211315023713 0ustar mcgrofmcgrofath9k-y += beacon.o \ gpio.o \ init.o \ main.o \ recv.o \ xmit.o \ link.o \ antenna.o ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o ath9k-$(CONFIG_COMPAT_ATH9K_RATE_CONTROL) += rc.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o ath9k-$(CONFIG_ATH9K_AHB) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \ dfs.o \ dfs_pattern_detector.o \ dfs_pri_detector.o ath9k-$(CONFIG_PM_SLEEP) += wow.o obj-$(CONFIG_ATH9K) += ath9k.o ath9k_hw-y:= \ ar9002_hw.o \ ar9003_hw.o \ hw.o \ ar9003_phy.o \ ar9002_phy.o \ ar5008_phy.o \ ar9002_calib.o \ ar9003_calib.o \ ar9003_rtt.o \ calib.o \ eeprom.o \ eeprom_def.o \ eeprom_4k.o \ eeprom_9287.o \ ani.o \ mac.o \ ar9002_mac.o \ ar9003_mac.o \ ar9003_eeprom.o \ ar9003_paprd.o ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ ar9003_mci.o obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o ath9k_common-y:= common.o ath9k_htc-y += htc_hst.o \ hif_usb.o \ wmi.o \ htc_drv_txrx.o \ htc_drv_main.o \ htc_drv_beacon.o \ htc_drv_init.o \ htc_drv_gpio.o ath9k_htc-$(CONFIG_ATH9K_HTC_DEBUGFS) += htc_drv_debug.o obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/xmit.c0000644000175000017500000017740412026211315023417 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "ath9k.h" #include "ar9003_mac.h" #define BITS_PER_BYTE 8 #define OFDM_PLCP_BITS 22 #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) #define L_STF 8 #define L_LTF 8 #define L_SIG 4 #define HT_SIG 8 #define HT_STF 4 #define HT_LTF(_ns) (4 * (_ns)) #define SYMBOL_TIME(_ns) ((_ns) << 2) /* ns * 4 us */ #define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5) /* ns * 3.6 us */ #define TIME_SYMBOLS(t) ((t) >> 2) #define TIME_SYMBOLS_HALFGI(t) (((t) * 5 - 4) / 18) #define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2) #define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18) static u16 bits_per_symbol[][2] = { /* 20MHz 40MHz */ { 26, 54 }, /* 0: BPSK */ { 52, 108 }, /* 1: QPSK 1/2 */ { 78, 162 }, /* 2: QPSK 3/4 */ { 104, 216 }, /* 3: 16-QAM 1/2 */ { 156, 324 }, /* 4: 16-QAM 3/4 */ { 208, 432 }, /* 5: 64-QAM 2/3 */ { 234, 486 }, /* 6: 64-QAM 3/4 */ { 260, 540 }, /* 7: 64-QAM 5/6 */ }; #define IS_HT_RATE(_rate) ((_rate) & 0x80) static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb); static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, int tx_flags, struct ath_txq *txq); static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, struct ath_txq *txq, struct list_head *bf_q, struct ath_tx_status *ts, int txok); static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, struct list_head *head, bool internal); static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, int nframes, int nbad, int txok); static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, int seqno); static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb, bool dequeue); enum { MCS_HT20, MCS_HT20_SGI, MCS_HT40, MCS_HT40_SGI, }; /*********************/ /* Aggregation logic */ /*********************/ void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq) __acquires(&txq->axq_lock) { spin_lock_bh(&txq->axq_lock); } void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq) __releases(&txq->axq_lock) { spin_unlock_bh(&txq->axq_lock); } void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) __releases(&txq->axq_lock) { struct sk_buff_head q; struct sk_buff *skb; __skb_queue_head_init(&q); skb_queue_splice_init(&txq->complete_q, &q); spin_unlock_bh(&txq->axq_lock); while ((skb = __skb_dequeue(&q))) ieee80211_tx_status(sc->hw, skb); } static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) { struct ath_atx_ac *ac = tid->ac; if (tid->paused) return; if (tid->sched) return; tid->sched = true; list_add_tail(&tid->list, &ac->tid_q); if (ac->sched) return; ac->sched = true; list_add_tail(&ac->list, &txq->axq_acq); } static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { struct ath_txq *txq = tid->ac->txq; WARN_ON(!tid->paused); ath_txq_lock(sc, txq); tid->paused = false; if (skb_queue_empty(&tid->buf_q)) goto unlock; ath_tx_queue_tid(txq, tid); ath_txq_schedule(sc, txq); unlock: ath_txq_unlock_complete(sc, txq); } static struct ath_frame_info *get_frame_info(struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); BUILD_BUG_ON(sizeof(struct ath_frame_info) > sizeof(tx_info->rate_driver_data)); return (struct ath_frame_info *) &tx_info->rate_driver_data[0]; } static void ath_send_bar(struct ath_atx_tid *tid, u16 seqno) { ieee80211_send_bar(tid->an->vif, tid->an->sta->addr, tid->tidno, seqno << IEEE80211_SEQ_SEQ_SHIFT); } static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { struct ath_txq *txq = tid->ac->txq; struct sk_buff *skb; struct ath_buf *bf; struct list_head bf_head; struct ath_tx_status ts; struct ath_frame_info *fi; bool sendbar = false; INIT_LIST_HEAD(&bf_head); memset(&ts, 0, sizeof(ts)); while ((skb = __skb_dequeue(&tid->buf_q))) { fi = get_frame_info(skb); bf = fi->bf; if (bf && fi->retries) { list_add_tail(&bf->list, &bf_head); ath_tx_update_baw(sc, tid, bf->bf_state.seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); sendbar = true; } else { ath_tx_send_normal(sc, txq, NULL, skb); } } if (tid->baw_head == tid->baw_tail) { tid->state &= ~AGGR_ADDBA_COMPLETE; tid->state &= ~AGGR_CLEANUP; } if (sendbar) { ath_txq_unlock(sc, txq); ath_send_bar(tid, tid->seq_start); ath_txq_lock(sc, txq); } } static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, int seqno) { int index, cindex; index = ATH_BA_INDEX(tid->seq_start, seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); __clear_bit(cindex, tid->tx_buf); while (tid->baw_head != tid->baw_tail && !test_bit(tid->baw_head, tid->tx_buf)) { INCR(tid->seq_start, IEEE80211_SEQ_MAX); INCR(tid->baw_head, ATH_TID_MAX_BUFS); if (tid->bar_index >= 0) tid->bar_index--; } } static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid, u16 seqno) { int index, cindex; index = ATH_BA_INDEX(tid->seq_start, seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); __set_bit(cindex, tid->tx_buf); if (index >= ((tid->baw_tail - tid->baw_head) & (ATH_TID_MAX_BUFS - 1))) { tid->baw_tail = cindex; INCR(tid->baw_tail, ATH_TID_MAX_BUFS); } } /* * TODO: For frame(s) that are in the retry state, we will reuse the * sequence number(s) without setting the retry bit. The * alternative is to give up on these and BAR the receiver's window * forward. */ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid) { struct sk_buff *skb; struct ath_buf *bf; struct list_head bf_head; struct ath_tx_status ts; struct ath_frame_info *fi; memset(&ts, 0, sizeof(ts)); INIT_LIST_HEAD(&bf_head); while ((skb = __skb_dequeue(&tid->buf_q))) { fi = get_frame_info(skb); bf = fi->bf; if (!bf) { ath_tx_complete(sc, skb, ATH_TX_ERROR, txq); continue; } list_add_tail(&bf->list, &bf_head); if (fi->retries) ath_tx_update_baw(sc, tid, bf->bf_state.seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); } tid->seq_next = tid->seq_start; tid->baw_tail = tid->baw_head; tid->bar_index = -1; } static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq, struct sk_buff *skb, int count) { struct ath_frame_info *fi = get_frame_info(skb); struct ath_buf *bf = fi->bf; struct ieee80211_hdr *hdr; int prev = fi->retries; TX_STAT_INC(txq->axq_qnum, a_retries); fi->retries += count; if (prev > 0) return; hdr = (struct ieee80211_hdr *)skb->data; hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY); dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, sizeof(*hdr), DMA_TO_DEVICE); } static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc) { struct ath_buf *bf = NULL; spin_lock_bh(&sc->tx.txbuflock); if (unlikely(list_empty(&sc->tx.txbuf))) { spin_unlock_bh(&sc->tx.txbuflock); return NULL; } bf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list); list_del(&bf->list); spin_unlock_bh(&sc->tx.txbuflock); return bf; } static void ath_tx_return_buffer(struct ath_softc *sc, struct ath_buf *bf) { spin_lock_bh(&sc->tx.txbuflock); list_add_tail(&bf->list, &sc->tx.txbuf); spin_unlock_bh(&sc->tx.txbuflock); } static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf) { struct ath_buf *tbf; tbf = ath_tx_get_buffer(sc); if (WARN_ON(!tbf)) return NULL; ATH_TXBUF_RESET(tbf); tbf->bf_mpdu = bf->bf_mpdu; tbf->bf_buf_addr = bf->bf_buf_addr; memcpy(tbf->bf_desc, bf->bf_desc, sc->sc_ah->caps.tx_desc_len); tbf->bf_state = bf->bf_state; return tbf; } static void ath_tx_count_frames(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, int txok, int *nframes, int *nbad) { struct ath_frame_info *fi; u16 seq_st = 0; u32 ba[WME_BA_BMP_SIZE >> 5]; int ba_index; int isaggr = 0; *nbad = 0; *nframes = 0; isaggr = bf_isaggr(bf); if (isaggr) { seq_st = ts->ts_seqnum; memcpy(ba, &ts->ba_low, WME_BA_BMP_SIZE >> 3); } while (bf) { fi = get_frame_info(bf->bf_mpdu); ba_index = ATH_BA_INDEX(seq_st, bf->bf_state.seqno); (*nframes)++; if (!txok || (isaggr && !ATH_BA_ISSET(ba, ba_index))) (*nbad)++; bf = bf->bf_next; } } static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf, struct list_head *bf_q, struct ath_tx_status *ts, int txok, bool retry) { struct ath_node *an = NULL; struct sk_buff *skb; struct ieee80211_sta *sta; struct ieee80211_hw *hw = sc->hw; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info; struct ath_atx_tid *tid = NULL; struct ath_buf *bf_next, *bf_last = bf->bf_lastbf; struct list_head bf_head; struct sk_buff_head bf_pending; u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0, seq_first; u32 ba[WME_BA_BMP_SIZE >> 5]; int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; bool rc_update = true; struct ieee80211_tx_rate rates[4]; struct ath_frame_info *fi; int nframes; u8 tidno; bool flush = !!(ts->ts_status & ATH9K_TX_FLUSH); int i, retries; int bar_index = -1; skb = bf->bf_mpdu; hdr = (struct ieee80211_hdr *)skb->data; tx_info = IEEE80211_SKB_CB(skb); memcpy(rates, tx_info->control.rates, sizeof(rates)); retries = ts->ts_longretry + 1; for (i = 0; i < ts->ts_rateindex; i++) retries += rates[i].count; rcu_read_lock(); sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); if (!sta) { rcu_read_unlock(); INIT_LIST_HEAD(&bf_head); while (bf) { bf_next = bf->bf_next; if (!bf->bf_stale || bf_next != NULL) list_move_tail(&bf->list, &bf_head); ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0); bf = bf_next; } return; } an = (struct ath_node *)sta->drv_priv; tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; tid = ATH_AN_2_TID(an, tidno); seq_first = tid->seq_start; /* * The hardware occasionally sends a tx status for the wrong TID. * In this case, the BA status cannot be considered valid and all * subframes need to be retransmitted */ if (tidno != ts->tid) txok = false; isaggr = bf_isaggr(bf); memset(ba, 0, WME_BA_BMP_SIZE >> 3); if (isaggr && txok) { if (ts->ts_flags & ATH9K_TX_BA) { seq_st = ts->ts_seqnum; memcpy(ba, &ts->ba_low, WME_BA_BMP_SIZE >> 3); } else { /* * AR5416 can become deaf/mute when BA * issue happens. Chip needs to be reset. * But AP code may have sychronization issues * when perform internal reset in this routine. * Only enable reset in STA mode for now. */ if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) needreset = 1; } } __skb_queue_head_init(&bf_pending); ath_tx_count_frames(sc, bf, ts, txok, &nframes, &nbad); while (bf) { u16 seqno = bf->bf_state.seqno; txfail = txpending = sendbar = 0; bf_next = bf->bf_next; skb = bf->bf_mpdu; tx_info = IEEE80211_SKB_CB(skb); fi = get_frame_info(skb); if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, seqno))) { /* transmit completion, subframe is * acked by block ack */ acked_cnt++; } else if (!isaggr && txok) { /* transmit completion */ acked_cnt++; } else if ((tid->state & AGGR_CLEANUP) || !retry) { /* * cleanup in progress, just fail * the un-acked sub-frames */ txfail = 1; } else if (flush) { txpending = 1; } else if (fi->retries < ATH_MAX_SW_RETRIES) { if (txok || !an->sleeping) ath_tx_set_retry(sc, txq, bf->bf_mpdu, retries); txpending = 1; } else { txfail = 1; txfail_cnt++; bar_index = max_t(int, bar_index, ATH_BA_INDEX(seq_first, seqno)); } /* * Make sure the last desc is reclaimed if it * not a holding desc. */ INIT_LIST_HEAD(&bf_head); if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) || bf_next != NULL || !bf_last->bf_stale) list_move_tail(&bf->list, &bf_head); if (!txpending || (tid->state & AGGR_CLEANUP)) { /* * complete the acked-ones/xretried ones; update * block-ack window */ ath_tx_update_baw(sc, tid, seqno); if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { memcpy(tx_info->control.rates, rates, sizeof(rates)); ath_tx_rc_status(sc, bf, ts, nframes, nbad, txok); rc_update = false; } ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, !txfail); } else { /* retry the un-acked ones */ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && bf->bf_next == NULL && bf_last->bf_stale) { struct ath_buf *tbf; tbf = ath_clone_txbuf(sc, bf_last); /* * Update tx baw and complete the * frame with failed status if we * run out of tx buf. */ if (!tbf) { ath_tx_update_baw(sc, tid, seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0); bar_index = max_t(int, bar_index, ATH_BA_INDEX(seq_first, seqno)); break; } fi->bf = tbf; } /* * Put this buffer to the temporary pending * queue to retain ordering */ __skb_queue_tail(&bf_pending, skb); } bf = bf_next; } /* prepend un-acked frames to the beginning of the pending frame queue */ if (!skb_queue_empty(&bf_pending)) { if (an->sleeping) ieee80211_sta_set_buffered(sta, tid->tidno, true); skb_queue_splice(&bf_pending, &tid->buf_q); if (!an->sleeping) { ath_tx_queue_tid(txq, tid); if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY)) tid->ac->clear_ps_filter = true; } } if (bar_index >= 0) { u16 bar_seq = ATH_BA_INDEX2SEQ(seq_first, bar_index); if (BAW_WITHIN(tid->seq_start, tid->baw_size, bar_seq)) tid->bar_index = ATH_BA_INDEX(tid->seq_start, bar_seq); ath_txq_unlock(sc, txq); ath_send_bar(tid, ATH_BA_INDEX2SEQ(seq_first, bar_index + 1)); ath_txq_lock(sc, txq); } if (tid->state & AGGR_CLEANUP) ath_tx_flush_tid(sc, tid); rcu_read_unlock(); if (needreset) ath9k_queue_reset(sc, RESET_TYPE_TX_ERROR); } static bool ath_lookup_legacy(struct ath_buf *bf) { struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; int i; skb = bf->bf_mpdu; tx_info = IEEE80211_SKB_CB(skb); rates = tx_info->control.rates; for (i = 0; i < 4; i++) { if (!rates[i].count || rates[i].idx < 0) break; if (!(rates[i].flags & IEEE80211_TX_RC_MCS)) return true; } return false; } static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, struct ath_atx_tid *tid) { struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; u32 max_4ms_framelen, frmlen; u16 aggr_limit, bt_aggr_limit, legacy = 0; int q = tid->ac->txq->mac80211_qnum; int i; skb = bf->bf_mpdu; tx_info = IEEE80211_SKB_CB(skb); rates = tx_info->control.rates; /* * Find the lowest frame length among the rate series that will have a * 4ms (or TXOP limited) transmit duration. */ max_4ms_framelen = ATH_AMPDU_LIMIT_MAX; for (i = 0; i < 4; i++) { int modeidx; if (!rates[i].count) continue; if (!(rates[i].flags & IEEE80211_TX_RC_MCS)) { legacy = 1; break; } if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) modeidx = MCS_HT40; else modeidx = MCS_HT20; if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) modeidx++; frmlen = sc->tx.max_aggr_framelen[q][modeidx][rates[i].idx]; max_4ms_framelen = min(max_4ms_framelen, frmlen); } /* * limit aggregate size by the minimum rate if rate selected is * not a probe rate, if rate selected is a probe rate then * avoid aggregation of this packet. */ if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy) return 0; aggr_limit = min(max_4ms_framelen, (u32)ATH_AMPDU_LIMIT_MAX); /* * Override the default aggregation limit for BTCOEX. */ bt_aggr_limit = ath9k_btcoex_aggr_limit(sc, max_4ms_framelen); if (bt_aggr_limit) aggr_limit = bt_aggr_limit; /* * h/w can accept aggregates up to 16 bit lengths (65535). * The IE, however can hold up to 65536, which shows up here * as zero. Ignore 65536 since we are constrained by hw. */ if (tid->an->maxampdu) aggr_limit = min(aggr_limit, tid->an->maxampdu); return aggr_limit; } /* * Returns the number of delimiters to be added to * meet the minimum required mpdudensity. */ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, struct ath_buf *bf, u16 frmlen, bool first_subfrm) { #define FIRST_DESC_NDELIMS 60 struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); u32 nsymbits, nsymbols; u16 minlen; u8 flags, rix; int width, streams, half_gi, ndelim, mindelim; struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu); /* Select standard number of delimiters based on frame length alone */ ndelim = ATH_AGGR_GET_NDELIM(frmlen); /* * If encryption enabled, hardware requires some more padding between * subframes. * TODO - this could be improved to be dependent on the rate. * The hardware can keep up at lower rates, but not higher rates */ if ((fi->keyix != ATH9K_TXKEYIX_INVALID) && !(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)) ndelim += ATH_AGGR_ENCRYPTDELIM; /* * Add delimiter when using RTS/CTS with aggregation * and non enterprise AR9003 card */ if (first_subfrm && !AR_SREV_9580_10_OR_LATER(sc->sc_ah) && (sc->sc_ah->ent_mode & AR_ENT_OTP_MIN_PKT_SIZE_DISABLE)) ndelim = max(ndelim, FIRST_DESC_NDELIMS); /* * Convert desired mpdu density from microeconds to bytes based * on highest rate in rate series (i.e. first rate) to determine * required minimum length for subframe. Take into account * whether high rate is 20 or 40Mhz and half or full GI. * * If there is no mpdu density restriction, no further calculation * is needed. */ if (tid->an->mpdudensity == 0) return ndelim; rix = tx_info->control.rates[0].idx; flags = tx_info->control.rates[0].flags; width = (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ? 1 : 0; half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0; if (half_gi) nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(tid->an->mpdudensity); else nsymbols = NUM_SYMBOLS_PER_USEC(tid->an->mpdudensity); if (nsymbols == 0) nsymbols = 1; streams = HT_RC_2_STREAMS(rix); nsymbits = bits_per_symbol[rix % 8][width] * streams; minlen = (nsymbols * nsymbits) / BITS_PER_BYTE; if (frmlen < minlen) { mindelim = (minlen - frmlen) / ATH_AGGR_DELIM_SZ; ndelim = max(mindelim, ndelim); } return ndelim; } static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct list_head *bf_q, int *aggr_len) { #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL; int rl = 0, nframes = 0, ndelim, prev_al = 0; u16 aggr_limit = 0, al = 0, bpad = 0, al_delta, h_baw = tid->baw_size / 2; enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; struct ieee80211_tx_info *tx_info; struct ath_frame_info *fi; struct sk_buff *skb; u16 seqno; do { skb = skb_peek(&tid->buf_q); fi = get_frame_info(skb); bf = fi->bf; if (!fi->bf) bf = ath_tx_setup_buffer(sc, txq, tid, skb, true); if (!bf) continue; bf->bf_state.bf_type = BUF_AMPDU | BUF_AGGR; seqno = bf->bf_state.seqno; /* do not step over block-ack window */ if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) { status = ATH_AGGR_BAW_CLOSED; break; } if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) { struct ath_tx_status ts = {}; struct list_head bf_head; INIT_LIST_HEAD(&bf_head); list_add(&bf->list, &bf_head); __skb_unlink(skb, &tid->buf_q); ath_tx_update_baw(sc, tid, seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); continue; } if (!bf_first) bf_first = bf; if (!rl) { aggr_limit = ath_lookup_rate(sc, bf, tid); rl = 1; } /* do not exceed aggregation limit */ al_delta = ATH_AGGR_DELIM_SZ + fi->framelen; if (nframes && ((aggr_limit < (al + bpad + al_delta + prev_al)) || ath_lookup_legacy(bf))) { status = ATH_AGGR_LIMITED; break; } tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); if (nframes && (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) break; /* do not exceed subframe limit */ if (nframes >= min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) { status = ATH_AGGR_LIMITED; break; } /* add padding for previous frame to aggregation length */ al += bpad + al_delta; /* * Get the delimiters needed to meet the MPDU * density for this node. */ ndelim = ath_compute_num_delims(sc, tid, bf_first, fi->framelen, !nframes); bpad = PADBYTES(al_delta) + (ndelim << 2); nframes++; bf->bf_next = NULL; /* link buffers of this frame to the aggregate */ if (!fi->retries) ath_tx_addto_baw(sc, tid, seqno); bf->bf_state.ndelim = ndelim; __skb_unlink(skb, &tid->buf_q); list_add_tail(&bf->list, bf_q); if (bf_prev) bf_prev->bf_next = bf; bf_prev = bf; } while (!skb_queue_empty(&tid->buf_q)); *aggr_len = al; return status; #undef PADBYTES } /* * rix - rate index * pktlen - total bytes (delims + data + fcs + pads + pad delims) * width - 0 for 20 MHz, 1 for 40 MHz * half_gi - to use 4us v/s 3.6 us for symbol time */ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, int width, int half_gi, bool shortPreamble) { u32 nbits, nsymbits, duration, nsymbols; int streams; /* find number of symbols: PLCP + data */ streams = HT_RC_2_STREAMS(rix); nbits = (pktlen << 3) + OFDM_PLCP_BITS; nsymbits = bits_per_symbol[rix % 8][width] * streams; nsymbols = (nbits + nsymbits - 1) / nsymbits; if (!half_gi) duration = SYMBOL_TIME(nsymbols); else duration = SYMBOL_TIME_HALFGI(nsymbols); /* addup duration for legacy/ht training and signal fields */ duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams); return duration; } static int ath_max_framelen(int usec, int mcs, bool ht40, bool sgi) { int streams = HT_RC_2_STREAMS(mcs); int symbols, bits; int bytes = 0; symbols = sgi ? TIME_SYMBOLS_HALFGI(usec) : TIME_SYMBOLS(usec); bits = symbols * bits_per_symbol[mcs % 8][ht40] * streams; bits -= OFDM_PLCP_BITS; bytes = bits / 8; bytes -= L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams); if (bytes > 65532) bytes = 65532; return bytes; } void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop) { u16 *cur_ht20, *cur_ht20_sgi, *cur_ht40, *cur_ht40_sgi; int mcs; /* 4ms is the default (and maximum) duration */ if (!txop || txop > 4096) txop = 4096; cur_ht20 = sc->tx.max_aggr_framelen[queue][MCS_HT20]; cur_ht20_sgi = sc->tx.max_aggr_framelen[queue][MCS_HT20_SGI]; cur_ht40 = sc->tx.max_aggr_framelen[queue][MCS_HT40]; cur_ht40_sgi = sc->tx.max_aggr_framelen[queue][MCS_HT40_SGI]; for (mcs = 0; mcs < 32; mcs++) { cur_ht20[mcs] = ath_max_framelen(txop, mcs, false, false); cur_ht20_sgi[mcs] = ath_max_framelen(txop, mcs, false, true); cur_ht40[mcs] = ath_max_framelen(txop, mcs, true, false); cur_ht40_sgi[mcs] = ath_max_framelen(txop, mcs, true, true); } } static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_info *info, int len) { struct ath_hw *ah = sc->sc_ah; struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; const struct ieee80211_rate *rate; struct ieee80211_hdr *hdr; struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu); int i; u8 rix = 0; skb = bf->bf_mpdu; tx_info = IEEE80211_SKB_CB(skb); rates = tx_info->control.rates; hdr = (struct ieee80211_hdr *)skb->data; /* set dur_update_en for l-sig computation except for PS-Poll frames */ info->dur_update = !ieee80211_is_pspoll(hdr->frame_control); info->rtscts_rate = fi->rtscts_rate; for (i = 0; i < 4; i++) { bool is_40, is_sgi, is_sp; int phy; if (!rates[i].count || (rates[i].idx < 0)) continue; rix = rates[i].idx; info->rates[i].Tries = rates[i].count; if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) { info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; info->flags |= ATH9K_TXDESC_RTSENA; } else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; info->flags |= ATH9K_TXDESC_CTSENA; } if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) info->rates[i].RateFlags |= ATH9K_RATESERIES_2040; if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) info->rates[i].RateFlags |= ATH9K_RATESERIES_HALFGI; is_sgi = !!(rates[i].flags & IEEE80211_TX_RC_SHORT_GI); is_40 = !!(rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH); is_sp = !!(rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE); if (rates[i].flags & IEEE80211_TX_RC_MCS) { /* MCS rates */ info->rates[i].Rate = rix | 0x80; info->rates[i].ChSel = ath_txchainmask_reduction(sc, ah->txchainmask, info->rates[i].Rate); info->rates[i].PktDuration = ath_pkt_duration(sc, rix, len, is_40, is_sgi, is_sp); if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC)) info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC; continue; } /* legacy rates */ rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx]; if ((tx_info->band == IEEE80211_BAND_2GHZ) && !(rate->flags & IEEE80211_RATE_ERP_G)) phy = WLAN_RC_PHY_CCK; else phy = WLAN_RC_PHY_OFDM; info->rates[i].Rate = rate->hw_value; if (rate->hw_value_short) { if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) info->rates[i].Rate |= rate->hw_value_short; } else { is_sp = false; } if (bf->bf_state.bfs_paprd) info->rates[i].ChSel = ah->txchainmask; else info->rates[i].ChSel = ath_txchainmask_reduction(sc, ah->txchainmask, info->rates[i].Rate); info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah, phy, rate->bitrate * 100, len, rix, is_sp); } /* For AR5416 - RTS cannot be followed by a frame larger than 8K */ if (bf_isaggr(bf) && (len > sc->sc_ah->caps.rts_aggr_limit)) info->flags &= ~ATH9K_TXDESC_RTSENA; /* ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive. */ if (info->flags & ATH9K_TXDESC_RTSENA) info->flags &= ~ATH9K_TXDESC_CTSENA; } static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb) { struct ieee80211_hdr *hdr; enum ath9k_pkt_type htype; __le16 fc; hdr = (struct ieee80211_hdr *)skb->data; fc = hdr->frame_control; if (ieee80211_is_beacon(fc)) htype = ATH9K_PKT_TYPE_BEACON; else if (ieee80211_is_probe_resp(fc)) htype = ATH9K_PKT_TYPE_PROBE_RESP; else if (ieee80211_is_atim(fc)) htype = ATH9K_PKT_TYPE_ATIM; else if (ieee80211_is_pspoll(fc)) htype = ATH9K_PKT_TYPE_PSPOLL; else htype = ATH9K_PKT_TYPE_NORMAL; return htype; } static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, struct ath_txq *txq, int len) { struct ath_hw *ah = sc->sc_ah; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); struct ath_buf *bf_first = bf; struct ath_tx_info info; bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR); memset(&info, 0, sizeof(info)); info.is_first = true; info.is_last = true; info.txpower = MAX_RATE_POWER; info.qcu = txq->axq_qnum; info.flags = ATH9K_TXDESC_INTREQ; if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) info.flags |= ATH9K_TXDESC_NOACK; if (tx_info->flags & IEEE80211_TX_CTL_LDPC) info.flags |= ATH9K_TXDESC_LDPC; ath_buf_set_rate(sc, bf, &info, len); if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) info.flags |= ATH9K_TXDESC_CLRDMASK; if (bf->bf_state.bfs_paprd) info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S; while (bf) { struct sk_buff *skb = bf->bf_mpdu; struct ath_frame_info *fi = get_frame_info(skb); info.type = get_hw_packet_type(skb); if (bf->bf_next) info.link = bf->bf_next->bf_daddr; else info.link = 0; info.buf_addr[0] = bf->bf_buf_addr; info.buf_len[0] = skb->len; info.pkt_len = fi->framelen; info.keyix = fi->keyix; info.keytype = fi->keytype; if (aggr) { if (bf == bf_first) info.aggr = AGGR_BUF_FIRST; else if (!bf->bf_next) info.aggr = AGGR_BUF_LAST; else info.aggr = AGGR_BUF_MIDDLE; info.ndelim = bf->bf_state.ndelim; info.aggr_len = len; } ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); bf = bf->bf_next; } } static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid) { struct ath_buf *bf; enum ATH_AGGR_STATUS status; struct ieee80211_tx_info *tx_info; struct list_head bf_q; int aggr_len; do { if (skb_queue_empty(&tid->buf_q)) return; INIT_LIST_HEAD(&bf_q); status = ath_tx_form_aggr(sc, txq, tid, &bf_q, &aggr_len); /* * no frames picked up to be aggregated; * block-ack window is not open. */ if (list_empty(&bf_q)) break; bf = list_first_entry(&bf_q, struct ath_buf, list); bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list); tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); if (tid->ac->clear_ps_filter) { tid->ac->clear_ps_filter = false; tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; } else { tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT; } /* if only one frame, send as non-aggregate */ if (bf == bf->bf_lastbf) { aggr_len = get_frame_info(bf->bf_mpdu)->framelen; bf->bf_state.bf_type = BUF_AMPDU; } else { TX_STAT_INC(txq->axq_qnum, a_aggr); } ath_tx_fill_desc(sc, bf, txq, aggr_len); ath_tx_txqaddbuf(sc, txq, &bf_q, false); } while (txq->axq_ampdu_depth < ATH_AGGR_MIN_QDEPTH && status != ATH_AGGR_BAW_CLOSED); } int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { struct ath_atx_tid *txtid; struct ath_node *an; u8 density; an = (struct ath_node *)sta->drv_priv; txtid = ATH_AN_2_TID(an, tid); if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE)) return -EAGAIN; /* update ampdu factor/density, they may have changed. This may happen * in HT IBSS when a beacon with HT-info is received after the station * has already been added. */ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); density = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density); an->mpdudensity = density; } txtid->state |= AGGR_ADDBA_PROGRESS; txtid->paused = true; *ssn = txtid->seq_start = txtid->seq_next; txtid->bar_index = -1; memset(txtid->tx_buf, 0, sizeof(txtid->tx_buf)); txtid->baw_head = txtid->baw_tail = 0; return 0; } void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) { struct ath_node *an = (struct ath_node *)sta->drv_priv; struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); struct ath_txq *txq = txtid->ac->txq; if (txtid->state & AGGR_CLEANUP) return; if (!(txtid->state & AGGR_ADDBA_COMPLETE)) { txtid->state &= ~AGGR_ADDBA_PROGRESS; return; } ath_txq_lock(sc, txq); txtid->paused = true; /* * If frames are still being transmitted for this TID, they will be * cleaned up during tx completion. To prevent race conditions, this * TID can only be reused after all in-progress subframes have been * completed. */ if (txtid->baw_head != txtid->baw_tail) txtid->state |= AGGR_CLEANUP; else txtid->state &= ~AGGR_ADDBA_COMPLETE; ath_tx_flush_tid(sc, txtid); ath_txq_unlock_complete(sc, txq); } void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, struct ath_node *an) { struct ath_atx_tid *tid; struct ath_atx_ac *ac; struct ath_txq *txq; bool buffered; int tidno; for (tidno = 0, tid = &an->tid[tidno]; tidno < WME_NUM_TID; tidno++, tid++) { if (!tid->sched) continue; ac = tid->ac; txq = ac->txq; ath_txq_lock(sc, txq); buffered = !skb_queue_empty(&tid->buf_q); tid->sched = false; list_del(&tid->list); if (ac->sched) { ac->sched = false; list_del(&ac->list); } ath_txq_unlock(sc, txq); ieee80211_sta_set_buffered(sta, tidno, buffered); } } void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) { struct ath_atx_tid *tid; struct ath_atx_ac *ac; struct ath_txq *txq; int tidno; for (tidno = 0, tid = &an->tid[tidno]; tidno < WME_NUM_TID; tidno++, tid++) { ac = tid->ac; txq = ac->txq; ath_txq_lock(sc, txq); ac->clear_ps_filter = true; if (!skb_queue_empty(&tid->buf_q) && !tid->paused) { ath_tx_queue_tid(txq, tid); ath_txq_schedule(sc, txq); } ath_txq_unlock_complete(sc, txq); } } void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) { struct ath_atx_tid *txtid; struct ath_node *an; an = (struct ath_node *)sta->drv_priv; txtid = ATH_AN_2_TID(an, tid); txtid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; txtid->state |= AGGR_ADDBA_COMPLETE; txtid->state &= ~AGGR_ADDBA_PROGRESS; ath_tx_resume_tid(sc, txtid); } /********************/ /* Queue Management */ /********************/ static void ath_txq_drain_pending_buffers(struct ath_softc *sc, struct ath_txq *txq) { struct ath_atx_ac *ac, *ac_tmp; struct ath_atx_tid *tid, *tid_tmp; list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) { list_del(&ac->list); ac->sched = false; list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q, list) { list_del(&tid->list); tid->sched = false; ath_tid_drain(sc, txq, tid); } } } struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { struct ath_hw *ah = sc->sc_ah; struct ath9k_tx_queue_info qi; static const int subtype_txq_to_hwq[] = { [WME_AC_BE] = ATH_TXQ_AC_BE, [WME_AC_BK] = ATH_TXQ_AC_BK, [WME_AC_VI] = ATH_TXQ_AC_VI, [WME_AC_VO] = ATH_TXQ_AC_VO, }; int axq_qnum, i; memset(&qi, 0, sizeof(qi)); qi.tqi_subtype = subtype_txq_to_hwq[subtype]; qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; qi.tqi_physCompBuf = 0; /* * Enable interrupts only for EOL and DESC conditions. * We mark tx descriptors to receive a DESC interrupt * when a tx queue gets deep; otherwise waiting for the * EOL to reap descriptors. Note that this is done to * reduce interrupt load and this only defers reaping * descriptors, never transmitting frames. Aside from * reducing interrupts this also permits more concurrency. * The only potential downside is if the tx queue backs * up in which case the top half of the kernel may backup * due to a lack of tx descriptors. * * The UAPSD queue is an exception, since we take a desc- * based intr on the EOSP frames. */ if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { qi.tqi_qflags = TXQ_FLAG_TXINT_ENABLE; } else { if (qtype == ATH9K_TX_QUEUE_UAPSD) qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE; else qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE; } axq_qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi); if (axq_qnum == -1) { /* * NB: don't print a message, this happens * normally on parts with too few tx queues */ return NULL; } if (!ATH_TXQ_SETUP(sc, axq_qnum)) { struct ath_txq *txq = &sc->tx.txq[axq_qnum]; txq->axq_qnum = axq_qnum; txq->mac80211_qnum = -1; txq->axq_link = NULL; __skb_queue_head_init(&txq->complete_q); INIT_LIST_HEAD(&txq->axq_q); INIT_LIST_HEAD(&txq->axq_acq); spin_lock_init(&txq->axq_lock); txq->axq_depth = 0; txq->axq_ampdu_depth = 0; txq->axq_tx_inprogress = false; sc->tx.txqsetup |= 1<txq_headidx = txq->txq_tailidx = 0; for (i = 0; i < ATH_TXFIFO_DEPTH; i++) INIT_LIST_HEAD(&txq->txq_fifo[i]); } return &sc->tx.txq[axq_qnum]; } int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_tx_queue_info *qinfo) { struct ath_hw *ah = sc->sc_ah; int error = 0; struct ath9k_tx_queue_info qi; BUG_ON(sc->tx.txq[qnum].axq_qnum != qnum); ath9k_hw_get_txq_props(ah, qnum, &qi); qi.tqi_aifs = qinfo->tqi_aifs; qi.tqi_cwmin = qinfo->tqi_cwmin; qi.tqi_cwmax = qinfo->tqi_cwmax; qi.tqi_burstTime = qinfo->tqi_burstTime; qi.tqi_readyTime = qinfo->tqi_readyTime; if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) { ath_err(ath9k_hw_common(sc->sc_ah), "Unable to update hardware queue %u!\n", qnum); error = -EIO; } else { ath9k_hw_resettxqueue(ah, qnum); } return error; } int ath_cabq_update(struct ath_softc *sc) { struct ath9k_tx_queue_info qi; struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; int qnum = sc->beacon.cabq->axq_qnum; ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); /* * Ensure the readytime % is within the bounds. */ if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND) sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND; else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND) sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND; qi.tqi_readyTime = (cur_conf->beacon_interval * sc->config.cabqReadytime) / 100; ath_txq_update(sc, qnum, &qi); return 0; } static bool bf_is_ampdu_not_probing(struct ath_buf *bf) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(bf->bf_mpdu); return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); } static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, struct list_head *list, bool retry_tx) { struct ath_buf *bf, *lastbf; struct list_head bf_head; struct ath_tx_status ts; memset(&ts, 0, sizeof(ts)); ts.ts_status = ATH9K_TX_FLUSH; INIT_LIST_HEAD(&bf_head); while (!list_empty(list)) { bf = list_first_entry(list, struct ath_buf, list); if (bf->bf_stale) { list_del(&bf->list); ath_tx_return_buffer(sc, bf); continue; } lastbf = bf->bf_lastbf; list_cut_position(&bf_head, list, &lastbf->list); txq->axq_depth--; if (bf_is_ampdu_not_probing(bf)) txq->axq_ampdu_depth--; if (bf_isampdu(bf)) ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, 0, retry_tx); else ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); } } /* * Drain a given TX queue (could be Beacon or Data) * * This assumes output has been stopped and * we do not need to block ath_tx_tasklet. */ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) { ath_txq_lock(sc, txq); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { int idx = txq->txq_tailidx; while (!list_empty(&txq->txq_fifo[idx])) { ath_drain_txq_list(sc, txq, &txq->txq_fifo[idx], retry_tx); INCR(idx, ATH_TXFIFO_DEPTH); } txq->txq_tailidx = idx; } txq->axq_link = NULL; txq->axq_tx_inprogress = false; ath_drain_txq_list(sc, txq, &txq->axq_q, retry_tx); /* flush any pending frames if aggregation is enabled */ if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !retry_tx) ath_txq_drain_pending_buffers(sc, txq); ath_txq_unlock_complete(sc, txq); } bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_txq *txq; int i; u32 npend = 0; if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return true; ath9k_hw_abort_tx_dma(ah); /* Check if any queue remains active */ for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { if (!ATH_TXQ_SETUP(sc, i)) continue; if (ath9k_hw_numtxpending(ah, sc->tx.txq[i].axq_qnum)) npend |= BIT(i); } if (npend) ath_err(common, "Failed to stop TX DMA, queues=0x%03x!\n", npend); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { if (!ATH_TXQ_SETUP(sc, i)) continue; /* * The caller will resume queues with ieee80211_wake_queues. * Mark the queue as not stopped to prevent ath_tx_complete * from waking the queue too early. */ txq = &sc->tx.txq[i]; txq->stopped = false; ath_draintxq(sc, txq, retry_tx); } return !npend; } void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) { ath9k_hw_releasetxqueue(sc->sc_ah, txq->axq_qnum); sc->tx.txqsetup &= ~(1<axq_qnum); } /* For each axq_acq entry, for each tid, try to schedule packets * for transmit until ampdu_depth has reached min Q depth. */ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) { struct ath_atx_ac *ac, *ac_tmp, *last_ac; struct ath_atx_tid *tid, *last_tid; if (test_bit(SC_OP_HW_RESET, &sc->sc_flags) || list_empty(&txq->axq_acq) || txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) return; ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list); list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) { last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list); list_del(&ac->list); ac->sched = false; while (!list_empty(&ac->tid_q)) { tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list); list_del(&tid->list); tid->sched = false; if (tid->paused) continue; ath_tx_sched_aggr(sc, txq, tid); /* * add tid to round-robin queue if more frames * are pending for the tid */ if (!skb_queue_empty(&tid->buf_q)) ath_tx_queue_tid(txq, tid); if (tid == last_tid || txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) break; } if (!list_empty(&ac->tid_q) && !ac->sched) { ac->sched = true; list_add_tail(&ac->list, &txq->axq_acq); } if (ac == last_ac || txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) return; } } /***********/ /* TX, DMA */ /***********/ /* * Insert a chain of ath_buf (descriptors) on a txq and * assume the descriptors are already chained together by caller. */ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, struct list_head *head, bool internal) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf, *bf_last; bool puttxbuf = false; bool edma; /* * Insert the frame on the outbound list and * pass it on to the hardware. */ if (list_empty(head)) return; edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); bf = list_first_entry(head, struct ath_buf, list); bf_last = list_entry(head->prev, struct ath_buf, list); ath_dbg(common, QUEUE, "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth); if (edma && list_empty(&txq->txq_fifo[txq->txq_headidx])) { list_splice_tail_init(head, &txq->txq_fifo[txq->txq_headidx]); INCR(txq->txq_headidx, ATH_TXFIFO_DEPTH); puttxbuf = true; } else { list_splice_tail_init(head, &txq->axq_q); if (txq->axq_link) { ath9k_hw_set_desc_link(ah, txq->axq_link, bf->bf_daddr); ath_dbg(common, XMIT, "link[%u] (%p)=%llx (%p)\n", txq->axq_qnum, txq->axq_link, ito64(bf->bf_daddr), bf->bf_desc); } else if (!edma) puttxbuf = true; txq->axq_link = bf_last->bf_desc; } if (puttxbuf) { TX_STAT_INC(txq->axq_qnum, puttxbuf); ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); ath_dbg(common, XMIT, "TXDP[%u] = %llx (%p)\n", txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); } if (!edma) { TX_STAT_INC(txq->axq_qnum, txstart); ath9k_hw_txstart(ah, txq->axq_qnum); } if (!internal) { txq->axq_depth++; if (bf_is_ampdu_not_probing(bf)) txq->axq_ampdu_depth++; } } static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, struct sk_buff *skb, struct ath_tx_control *txctl) { struct ath_frame_info *fi = get_frame_info(skb); struct list_head bf_head; struct ath_buf *bf; /* * Do not queue to h/w when any of the following conditions is true: * - there are pending frames in software queue * - the TID is currently paused for ADDBA/BAR request * - seqno is not within block-ack window * - h/w queue depth exceeds low water mark */ if (!skb_queue_empty(&tid->buf_q) || tid->paused || !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) || txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) { /* * Add this frame to software queue for scheduling later * for aggregation. */ TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw); __skb_queue_tail(&tid->buf_q, skb); if (!txctl->an || !txctl->an->sleeping) ath_tx_queue_tid(txctl->txq, tid); return; } bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false); if (!bf) return; bf->bf_state.bf_type = BUF_AMPDU; INIT_LIST_HEAD(&bf_head); list_add(&bf->list, &bf_head); /* Add sub-frame to BAW */ ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); /* Queue to h/w without aggregation */ TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw); bf->bf_lastbf = bf; ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen); ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false); } static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb) { struct ath_frame_info *fi = get_frame_info(skb); struct list_head bf_head; struct ath_buf *bf; bf = fi->bf; if (!bf) bf = ath_tx_setup_buffer(sc, txq, tid, skb, false); if (!bf) return; INIT_LIST_HEAD(&bf_head); list_add_tail(&bf->list, &bf_head); bf->bf_state.bf_type = 0; bf->bf_lastbf = bf; ath_tx_fill_desc(sc, bf, txq, fi->framelen); ath_tx_txqaddbuf(sc, txq, &bf_head, false); TX_STAT_INC(txq->axq_qnum, queued); } static void setup_frame_info(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct sk_buff *skb, int framelen) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; const struct ieee80211_rate *rate; struct ath_frame_info *fi = get_frame_info(skb); struct ath_node *an = NULL; enum ath9k_key_type keytype; bool short_preamble = false; /* * We check if Short Preamble is needed for the CTS rate by * checking the BSS's global flag. * But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used. */ if (tx_info->control.vif && tx_info->control.vif->bss_conf.use_short_preamble) short_preamble = true; rate = ieee80211_get_rts_cts_rate(hw, tx_info); keytype = ath9k_cmn_get_hw_crypto_keytype(skb); if (sta) an = (struct ath_node *) sta->drv_priv; memset(fi, 0, sizeof(*fi)); if (hw_key) fi->keyix = hw_key->hw_key_idx; else if (an && ieee80211_is_data(hdr->frame_control) && an->ps_key > 0) fi->keyix = an->ps_key; else fi->keyix = ATH9K_TXKEYIX_INVALID; fi->keytype = keytype; fi->framelen = framelen; fi->rtscts_rate = rate->hw_value; if (short_preamble) fi->rtscts_rate |= rate->hw_value_short; } u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate) { struct ath_hw *ah = sc->sc_ah; struct ath9k_channel *curchan = ah->curchan; if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (curchan->channelFlags & CHANNEL_5GHZ) && (chainmask == 0x7) && (rate < 0x90)) return 0x3; else return chainmask; } /* * Assign a descriptor (and sequence number if necessary, * and map buffer for DMA. Frees skb on error */ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb, bool dequeue) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_frame_info *fi = get_frame_info(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ath_buf *bf; int fragno; u16 seqno; bf = ath_tx_get_buffer(sc); if (!bf) { ath_dbg(common, XMIT, "TX buffers are full\n"); goto error; } ATH_TXBUF_RESET(bf); if (tid) { fragno = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; seqno = tid->seq_next; hdr->seq_ctrl = cpu_to_le16(tid->seq_next << IEEE80211_SEQ_SEQ_SHIFT); if (fragno) hdr->seq_ctrl |= cpu_to_le16(fragno); if (!ieee80211_has_morefrags(hdr->frame_control)) INCR(tid->seq_next, IEEE80211_SEQ_MAX); bf->bf_state.seqno = seqno; } bf->bf_mpdu = skb; bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { bf->bf_mpdu = NULL; bf->bf_buf_addr = 0; ath_err(ath9k_hw_common(sc->sc_ah), "dma_mapping_error() on TX\n"); ath_tx_return_buffer(sc, bf); goto error; } fi->bf = bf; return bf; error: if (dequeue) __skb_unlink(skb, &tid->buf_q); dev_kfree_skb_any(skb); return NULL; } /* FIXME: tx power */ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb, struct ath_tx_control *txctl) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ath_atx_tid *tid = NULL; struct ath_buf *bf; u8 tidno; if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && txctl->an && ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; tid = ATH_AN_2_TID(txctl->an, tidno); WARN_ON(tid->ac->txq != txctl->txq); } if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && tid) { /* * Try aggregation if it's a unicast data frame * and the destination is HT capable. */ ath_tx_send_ampdu(sc, tid, skb, txctl); } else { bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false); if (!bf) return; bf->bf_state.bfs_paprd = txctl->paprd; if (txctl->paprd) bf->bf_state.bfs_paprd_timestamp = jiffies; ath_tx_send_normal(sc, txctl->txq, tid, skb); } } /* Upon failure caller should free skb */ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_tx_control *txctl) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; struct ath_softc *sc = hw->priv; struct ath_txq *txq = txctl->txq; int padpos, padsize; int frmlen = skb->len + FCS_LEN; int q; /* NOTE: sta can be NULL according to net/mac80211.h */ if (sta) txctl->an = (struct ath_node *)sta->drv_priv; if (info->control.hw_key) frmlen += info->control.hw_key->icv_len; /* * As a temporary workaround, assign seq# here; this will likely need * to be cleaned up to work better with Beacon transmission and virtual * BSSes. */ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) sc->tx.seq_no += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } /* Add the padding after the header if this is not already done */ padpos = ath9k_cmn_padpos(hdr->frame_control); padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) return -ENOMEM; skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); hdr = (struct ieee80211_hdr *) skb->data; } if ((vif && vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_AP_VLAN) || !ieee80211_is_data(hdr->frame_control)) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; setup_frame_info(hw, sta, skb, frmlen); /* * At this point, the vif, hw_key and sta pointers in the tx control * info are no longer valid (overwritten by the ath_frame_info data. */ q = skb_get_queue_mapping(skb); ath_txq_lock(sc, txq); if (txq == sc->tx.txq_map[q] && ++txq->pending_frames > sc->tx.txq_max_pending[q] && !txq->stopped) { ieee80211_stop_queue(sc->hw, q); txq->stopped = true; } ath_tx_start_dma(sc, skb, txctl); ath_txq_unlock(sc, txq); return 0; } /*****************/ /* TX Completion */ /*****************/ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, int tx_flags, struct ath_txq *txq) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data; int q, padpos, padsize; unsigned long flags; ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb); if (sc->sc_ah->caldata) sc->sc_ah->caldata->paprd_packet_sent = true; if (!(tx_flags & ATH_TX_ERROR)) /* Frame was ACKed */ tx_info->flags |= IEEE80211_TX_STAT_ACK; padpos = ath9k_cmn_padpos(hdr->frame_control); padsize = padpos & 3; if (padsize && skb->len>padpos+padsize) { /* * Remove MAC header padding before giving the frame back to * mac80211. */ memmove(skb->data + padsize, skb->data, padpos); skb_pull(skb, padsize); } spin_lock_irqsave(&sc->sc_pm_lock, flags); if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) { sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK; ath_dbg(common, PS, "Going back to sleep after having received TX status (0x%lx)\n", sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK)); } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); q = skb_get_queue_mapping(skb); if (txq == sc->tx.txq_map[q]) { if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; if (txq->stopped && txq->pending_frames < sc->tx.txq_max_pending[q]) { ieee80211_wake_queue(sc->hw, q); txq->stopped = false; } } __skb_queue_tail(&txq->complete_q, skb); } static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, struct ath_txq *txq, struct list_head *bf_q, struct ath_tx_status *ts, int txok) { struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); unsigned long flags; int tx_flags = 0; if (!txok) tx_flags |= ATH_TX_ERROR; if (ts->ts_status & ATH9K_TXERR_FILT) tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); bf->bf_buf_addr = 0; if (bf->bf_state.bfs_paprd) { if (time_after(jiffies, bf->bf_state.bfs_paprd_timestamp + msecs_to_jiffies(ATH_PAPRD_TIMEOUT))) dev_kfree_skb_any(skb); else complete(&sc->paprd_complete); } else { ath_debug_stat_tx(sc, bf, ts, txq, tx_flags); ath_tx_complete(sc, skb, tx_flags, txq); } /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't * accidentally reference it later. */ bf->bf_mpdu = NULL; /* * Return the list of ath_buf of this mpdu to free queue */ spin_lock_irqsave(&sc->tx.txbuflock, flags); list_splice_tail_init(bf_q, &sc->tx.txbuf); spin_unlock_irqrestore(&sc->tx.txbuflock, flags); } static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, int nframes, int nbad, int txok) { struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hw *hw = sc->hw; struct ath_hw *ah = sc->sc_ah; u8 i, tx_rateindex; if (txok) tx_info->status.ack_signal = ts->ts_rssi; tx_rateindex = ts->ts_rateindex; WARN_ON(tx_rateindex >= hw->max_rates); if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { tx_info->flags |= IEEE80211_TX_STAT_AMPDU; BUG_ON(nbad > nframes); } tx_info->status.ampdu_len = nframes; tx_info->status.ampdu_ack_len = nframes - nbad; if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 && (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) == 0) { /* * If an underrun error is seen assume it as an excessive * retry only if max frame trigger level has been reached * (2 KB for single stream, and 4 KB for dual stream). * Adjust the long retry as if the frame was tried * hw->max_rate_tries times to affect how rate control updates * PER for the failed rate. * In case of congestion on the bus penalizing this type of * underruns should help hardware actually transmit new frames * successfully by eventually preferring slower rates. * This itself should also alleviate congestion on the bus. */ if (unlikely(ts->ts_flags & (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN)) && ieee80211_is_data(hdr->frame_control) && ah->tx_trig_level >= sc->sc_ah->config.max_txtrig_level) tx_info->status.rates[tx_rateindex].count = hw->max_rate_tries; } for (i = tx_rateindex + 1; i < hw->max_rates; i++) { tx_info->status.rates[i].count = 0; tx_info->status.rates[i].idx = -1; } tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1; } static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf, struct list_head *bf_head) { int txok; txq->axq_depth--; txok = !(ts->ts_status & ATH9K_TXERR_MASK); txq->axq_tx_inprogress = false; if (bf_is_ampdu_not_probing(bf)) txq->axq_ampdu_depth--; if (!bf_isampdu(bf)) { ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok); ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok); } else ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok, true); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) ath_txq_schedule(sc, txq); } static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf, *lastbf, *bf_held = NULL; struct list_head bf_head; struct ath_desc *ds; struct ath_tx_status ts; int status; ath_dbg(common, QUEUE, "tx queue %d (%x), link %p\n", txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum), txq->axq_link); ath_txq_lock(sc, txq); for (;;) { if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) break; if (list_empty(&txq->axq_q)) { txq->axq_link = NULL; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) ath_txq_schedule(sc, txq); break; } bf = list_first_entry(&txq->axq_q, struct ath_buf, list); /* * There is a race condition that a BH gets scheduled * after sw writes TxE and before hw re-load the last * descriptor to get the newly chained one. * Software must keep the last DONE descriptor as a * holding descriptor - software does so by marking * it with the STALE flag. */ bf_held = NULL; if (bf->bf_stale) { bf_held = bf; if (list_is_last(&bf_held->list, &txq->axq_q)) break; bf = list_entry(bf_held->list.next, struct ath_buf, list); } lastbf = bf->bf_lastbf; ds = lastbf->bf_desc; memset(&ts, 0, sizeof(ts)); status = ath9k_hw_txprocdesc(ah, ds, &ts); if (status == -EINPROGRESS) break; TX_STAT_INC(txq->axq_qnum, txprocdesc); /* * Remove ath_buf's of the same transmit unit from txq, * however leave the last descriptor back as the holding * descriptor for hw. */ lastbf->bf_stale = true; INIT_LIST_HEAD(&bf_head); if (!list_is_singular(&lastbf->list)) list_cut_position(&bf_head, &txq->axq_q, lastbf->list.prev); if (bf_held) { list_del(&bf_held->list); ath_tx_return_buffer(sc, bf_held); } ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head); } ath_txq_unlock_complete(sc, txq); } void ath_tx_tasklet(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; u32 qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1) & ah->intr_txqs; int i; for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i) && (qcumask & (1 << i))) ath_tx_processq(sc, &sc->tx.txq[i]); } } void ath_tx_edma_tasklet(struct ath_softc *sc) { struct ath_tx_status ts; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; struct ath_txq *txq; struct ath_buf *bf, *lastbf; struct list_head bf_head; int status; for (;;) { if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) break; status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts); if (status == -EINPROGRESS) break; if (status == -EIO) { ath_dbg(common, XMIT, "Error processing tx status\n"); break; } /* Process beacon completions separately */ if (ts.qid == sc->beacon.beaconq) { sc->beacon.tx_processed = true; sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); continue; } txq = &sc->tx.txq[ts.qid]; ath_txq_lock(sc, txq); if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) { ath_txq_unlock(sc, txq); return; } bf = list_first_entry(&txq->txq_fifo[txq->txq_tailidx], struct ath_buf, list); lastbf = bf->bf_lastbf; INIT_LIST_HEAD(&bf_head); list_cut_position(&bf_head, &txq->txq_fifo[txq->txq_tailidx], &lastbf->list); if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) { INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH); if (!list_empty(&txq->axq_q)) { struct list_head bf_q; INIT_LIST_HEAD(&bf_q); txq->axq_link = NULL; list_splice_tail_init(&txq->axq_q, &bf_q); ath_tx_txqaddbuf(sc, txq, &bf_q, true); } } ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head); ath_txq_unlock_complete(sc, txq); } } /*****************/ /* Init, Cleanup */ /*****************/ static int ath_txstatus_setup(struct ath_softc *sc, int size) { struct ath_descdma *dd = &sc->txsdma; u8 txs_len = sc->sc_ah->caps.txs_len; dd->dd_desc_len = size * txs_len; dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, &dd->dd_desc_paddr, GFP_KERNEL); if (!dd->dd_desc) return -ENOMEM; return 0; } static int ath_tx_edma_init(struct ath_softc *sc) { int err; err = ath_txstatus_setup(sc, ATH_TXSTATUS_RING_SIZE); if (!err) ath9k_hw_setup_statusring(sc->sc_ah, sc->txsdma.dd_desc, sc->txsdma.dd_desc_paddr, ATH_TXSTATUS_RING_SIZE); return err; } static void ath_tx_edma_cleanup(struct ath_softc *sc) { struct ath_descdma *dd = &sc->txsdma; dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); } int ath_tx_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); int error = 0; spin_lock_init(&sc->tx.txbuflock); error = ath_descdma_setup(sc, &sc->tx.txdma, &sc->tx.txbuf, "tx", nbufs, 1, 1); if (error != 0) { ath_err(common, "Failed to allocate tx descriptors: %d\n", error); goto err; } error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf, "beacon", ATH_BCBUF, 1, 1); if (error != 0) { ath_err(common, "Failed to allocate beacon descriptors: %d\n", error); goto err; } INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { error = ath_tx_edma_init(sc); if (error) goto err; } err: if (error != 0) ath_tx_cleanup(sc); return error; } void ath_tx_cleanup(struct ath_softc *sc) { if (sc->beacon.bdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->beacon.bdma, &sc->beacon.bbuf); if (sc->tx.txdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->tx.txdma, &sc->tx.txbuf); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_tx_edma_cleanup(sc); } void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) { struct ath_atx_tid *tid; struct ath_atx_ac *ac; int tidno, acno; for (tidno = 0, tid = &an->tid[tidno]; tidno < WME_NUM_TID; tidno++, tid++) { tid->an = an; tid->tidno = tidno; tid->seq_start = tid->seq_next = 0; tid->baw_size = WME_MAX_BA; tid->baw_head = tid->baw_tail = 0; tid->sched = false; tid->paused = false; tid->state &= ~AGGR_CLEANUP; __skb_queue_head_init(&tid->buf_q); acno = TID_TO_WME_AC(tidno); tid->ac = &an->ac[acno]; tid->state &= ~AGGR_ADDBA_COMPLETE; tid->state &= ~AGGR_ADDBA_PROGRESS; } for (acno = 0, ac = &an->ac[acno]; acno < WME_NUM_AC; acno++, ac++) { ac->sched = false; ac->txq = sc->tx.txq_map[acno]; INIT_LIST_HEAD(&ac->tid_q); } } void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) { struct ath_atx_ac *ac; struct ath_atx_tid *tid; struct ath_txq *txq; int tidno; for (tidno = 0, tid = &an->tid[tidno]; tidno < WME_NUM_TID; tidno++, tid++) { ac = tid->ac; txq = ac->txq; ath_txq_lock(sc, txq); if (tid->sched) { list_del(&tid->list); tid->sched = false; } if (ac->sched) { list_del(&ac->list); tid->ac->sched = false; } ath_tid_drain(sc, txq, tid); tid->state &= ~AGGR_ADDBA_COMPLETE; tid->state &= ~AGGR_CLEANUP; ath_txq_unlock(sc, txq); } } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/wow.c0000644000175000017500000003327612026211315023250 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "ath9k.h" #include "reg.h" #include "hw-ops.h" const char *ath9k_hw_wow_event_to_string(u32 wow_event) { if (wow_event & AH_WOW_MAGIC_PATTERN_EN) return "Magic pattern"; if (wow_event & AH_WOW_USER_PATTERN_EN) return "User pattern"; if (wow_event & AH_WOW_LINK_CHANGE) return "Link change"; if (wow_event & AH_WOW_BEACON_MISS) return "Beacon miss"; return "unknown reason"; } EXPORT_SYMBOL(ath9k_hw_wow_event_to_string); static void ath9k_hw_config_serdes_wow_sleep(struct ath_hw *ah) { int i; for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++) REG_WRITE(ah, INI_RA(&ah->iniPcieSerdesWow, i, 0), INI_RA(&ah->iniPcieSerdesWow, i, 1)); usleep_range(1000, 1500); } static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); /* set rx disable bit */ REG_WRITE(ah, AR_CR, AR_CR_RXD); if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) { ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW)); return; } else { if (!AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_RXDP, 0x0); } /* AR9280 WoW has sleep issue, do not set it to sleep */ if (AR_SREV_9280_20(ah)) return; REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); } static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u8 sta_mac_addr[ETH_ALEN], ap_mac_addr[ETH_ALEN]; u32 ctl[13] = {0}; u32 data_word[KAL_NUM_DATA_WORDS]; u8 i; u32 wow_ka_data_word0; memcpy(sta_mac_addr, common->macaddr, ETH_ALEN); memcpy(ap_mac_addr, common->curbssid, ETH_ALEN); /* set the transmit buffer */ ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16)); if (!(AR_SREV_9300_20_OR_LATER(ah))) ctl[0] += (KAL_ANTENNA_MODE << 25); ctl[1] = 0; ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */ ctl[4] = 0; ctl[7] = (ah->txchainmask) << 2; if (AR_SREV_9300_20_OR_LATER(ah)) ctl[2] = 0xf << 16; /* tx_tries 0 */ else ctl[2] = 0x7 << 16; /* tx_tries 0 */ for (i = 0; i < KAL_NUM_DESC_WORDS; i++) REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); /* for AR9300 family 13 descriptor words */ if (AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) | (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16); data_word[1] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) | (ap_mac_addr[1] << 8) | (ap_mac_addr[0]); data_word[2] = (sta_mac_addr[1] << 24) | (sta_mac_addr[0] << 16) | (ap_mac_addr[5] << 8) | (ap_mac_addr[4]); data_word[3] = (sta_mac_addr[5] << 24) | (sta_mac_addr[4] << 16) | (sta_mac_addr[3] << 8) | (sta_mac_addr[2]); data_word[4] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) | (ap_mac_addr[1] << 8) | (ap_mac_addr[0]); data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]); if (AR_SREV_9462_20_OR_LATER(ah)) { /* AR9462 2.0 has an extra descriptor word (time based * discard) compared to other chips */ REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0); wow_ka_data_word0 = AR_WOW_TXBUF(13); } else { wow_ka_data_word0 = AR_WOW_TXBUF(12); } for (i = 0; i < KAL_NUM_DATA_WORDS; i++) REG_WRITE(ah, (wow_ka_data_word0 + i*4), data_word[i]); } void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, u8 *user_mask, int pattern_count, int pattern_len) { int i; u32 pattern_val, mask_val; u32 set, clr; /* FIXME: should check count by querying the hardware capability */ if (pattern_count >= MAX_NUM_PATTERN) return; REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count)); /* set the registers for pattern */ for (i = 0; i < MAX_PATTERN_SIZE; i += 4) { memcpy(&pattern_val, user_pattern, 4); REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i), pattern_val); user_pattern += 4; } /* set the registers for mask */ for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) { memcpy(&mask_val, user_mask, 4); REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val); user_mask += 4; } /* set the pattern length to be matched * * AR_WOW_LENGTH1_REG1 * bit 31:24 pattern 0 length * bit 23:16 pattern 1 length * bit 15:8 pattern 2 length * bit 7:0 pattern 3 length * * AR_WOW_LENGTH1_REG2 * bit 31:24 pattern 4 length * bit 23:16 pattern 5 length * bit 15:8 pattern 6 length * bit 7:0 pattern 7 length * * the below logic writes out the new * pattern length for the corresponding * pattern_count, while masking out the * other fields */ ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT); if (!AR_SREV_9285_12_OR_LATER(ah)) return; if (pattern_count < 4) { /* Pattern 0-3 uses AR_WOW_LENGTH1 register */ set = (pattern_len & AR_WOW_LENGTH_MAX) << AR_WOW_LEN1_SHIFT(pattern_count); clr = AR_WOW_LENGTH1_MASK(pattern_count); REG_RMW(ah, AR_WOW_LENGTH1, set, clr); } else { /* Pattern 4-7 uses AR_WOW_LENGTH2 register */ set = (pattern_len & AR_WOW_LENGTH_MAX) << AR_WOW_LEN2_SHIFT(pattern_count); clr = AR_WOW_LENGTH2_MASK(pattern_count); REG_RMW(ah, AR_WOW_LENGTH2, set, clr); } } EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern); u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) { u32 wow_status = 0; u32 val = 0, rval; /* * read the WoW status register to know * the wakeup reason */ rval = REG_READ(ah, AR_WOW_PATTERN); val = AR_WOW_STATUS(rval); /* * mask only the WoW events that we have enabled. Sometimes * we have spurious WoW events from the AR_WOW_PATTERN * register. This mask will clean it up. */ val &= ah->wow_event_mask; if (val) { if (val & AR_WOW_MAGIC_PAT_FOUND) wow_status |= AH_WOW_MAGIC_PATTERN_EN; if (AR_WOW_PATTERN_FOUND(val)) wow_status |= AH_WOW_USER_PATTERN_EN; if (val & AR_WOW_KEEP_ALIVE_FAIL) wow_status |= AH_WOW_LINK_CHANGE; if (val & AR_WOW_BEACON_FAIL) wow_status |= AH_WOW_BEACON_MISS; } /* * set and clear WOW_PME_CLEAR registers for the chip to * generate next wow signal. * disable D3 before accessing other registers ? */ /* do we need to check the bit value 0x01000000 (7-10) ?? */ REG_RMW(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR, AR_PMCTRL_PWR_STATE_D1D3); /* * clear all events */ REG_WRITE(ah, AR_WOW_PATTERN, AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN))); /* * tie reset register for AR9002 family of chipsets * NB: not tieing it back might have some repurcussions. */ if (!AR_SREV_9300_20_OR_LATER(ah)) { REG_SET_BIT(ah, AR_WA, AR_WA_UNTIE_RESET_EN | AR_WA_POR_SHORT | AR_WA_RESET_EN); } /* * restore the beacon threshold to init value */ REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); /* * Restore the way the PCI-E reset, Power-On-Reset, external * PCIE_POR_SHORT pins are tied to its original value. * Previously just before WoW sleep, we untie the PCI-E * reset to our Chip's Power On Reset so that any PCI-E * reset from the bus will not reset our chip */ if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress) ath9k_hw_configpcipowersave(ah, false); ah->wow_event_mask = 0; return wow_status; } EXPORT_SYMBOL(ath9k_hw_wow_wakeup); void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) { u32 wow_event_mask; u32 set, clr; /* * wow_event_mask is a mask to the AR_WOW_PATTERN register to * indicate which WoW events we have enabled. The WoW events * are from the 'pattern_enable' in this function and * 'pattern_count' of ath9k_hw_wow_apply_pattern() */ wow_event_mask = ah->wow_event_mask; /* * Untie Power-on-Reset from the PCI-E-Reset. When we are in * WOW sleep, we do want the Reset from the PCI-E to disturb * our hw state */ if (ah->is_pciexpress) { /* * we need to untie the internal POR (power-on-reset) * to the external PCI-E reset. We also need to tie * the PCI-E Phy reset to the PCI-E reset. */ if (AR_SREV_9300_20_OR_LATER(ah)) { set = AR_WA_RESET_EN | AR_WA_POR_SHORT; clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE; REG_RMW(ah, AR_WA, set, clr); } else { if (AR_SREV_9285(ah) || AR_SREV_9287(ah)) set = AR9285_WA_DEFAULT; else set = AR9280_WA_DEFAULT; /* * In AR9280 and AR9285, bit 14 in WA register * (disable L1) should only be set when device * enters D3 state and be cleared when device * comes back to D0 */ if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE) set |= AR_WA_D3_L1_DISABLE; clr = AR_WA_UNTIE_RESET_EN; set |= AR_WA_RESET_EN | AR_WA_POR_SHORT; REG_RMW(ah, AR_WA, set, clr); /* * for WoW sleep, we reprogram the SerDes so that the * PLL and CLK REQ are both enabled. This uses more * power but otherwise WoW sleep is unstable and the * chip may disappear. */ if (AR_SREV_9285_12_OR_LATER(ah)) ath9k_hw_config_serdes_wow_sleep(ah); } } /* * set the power states appropriately and enable PME */ set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA | AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR; /* * set and clear WOW_PME_CLEAR registers for the chip * to generate next wow signal. */ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); clr = AR_PMCTRL_WOW_PME_CLR; REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); /* * Setup for: * - beacon misses * - magic pattern * - keep alive timeout * - pattern matching */ /* * Program default values for pattern backoff, aifs/slot/KAL count, * beacon miss timeout, KAL timeout, etc. */ set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF); REG_SET_BIT(ah, AR_WOW_PATTERN, set); set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT); REG_SET_BIT(ah, AR_WOW_COUNT, set); if (pattern_enable & AH_WOW_BEACON_MISS) set = AR_WOW_BEACON_TIMO; /* We are not using beacon miss, program a large value */ else set = AR_WOW_BEACON_TIMO_MAX; REG_WRITE(ah, AR_WOW_BCN_TIMO, set); /* * Keep alive timo in ms except AR9280 */ if (!pattern_enable || AR_SREV_9280(ah)) set = AR_WOW_KEEP_ALIVE_NEVER; else set = KAL_TIMEOUT * 32; REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set); /* * Keep alive delay in us. based on 'power on clock', * therefore in usec */ set = KAL_DELAY * 1000; REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set); /* * Create keep alive pattern to respond to beacons */ ath9k_wow_create_keep_alive_pattern(ah); /* * Configure MAC WoW Registers */ set = 0; /* Send keep alive timeouts anyway */ clr = AR_WOW_KEEP_ALIVE_AUTO_DIS; if (pattern_enable & AH_WOW_LINK_CHANGE) wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL; else set = AR_WOW_KEEP_ALIVE_FAIL_DIS; /* * FIXME: For now disable keep alive frame * failure. This seems to sometimes trigger * unnecessary wake up with AR9485 chipsets. */ set = AR_WOW_KEEP_ALIVE_FAIL_DIS; REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr); /* * we are relying on a bmiss failure. ensure we have * enough threshold to prevent false positives */ REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, AR_WOW_BMISSTHRESHOLD); set = 0; clr = 0; if (pattern_enable & AH_WOW_BEACON_MISS) { set = AR_WOW_BEACON_FAIL_EN; wow_event_mask |= AR_WOW_BEACON_FAIL; } else { clr = AR_WOW_BEACON_FAIL_EN; } REG_RMW(ah, AR_WOW_BCN_EN, set, clr); set = 0; clr = 0; /* * Enable the magic packet registers */ if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) { set = AR_WOW_MAGIC_EN; wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND; } else { clr = AR_WOW_MAGIC_EN; } set |= AR_WOW_MAC_INTR_EN; REG_RMW(ah, AR_WOW_PATTERN, set, clr); /* * For AR9285 and later version of chipsets * enable WoW pattern match for packets less * than 256 bytes for all patterns */ if (AR_SREV_9285_12_OR_LATER(ah)) REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B, AR_WOW_PATTERN_SUPPORTED); /* * Set the power states appropriately and enable PME */ clr = 0; set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA; /* * This is needed for AR9300 chipsets to wake-up * the host. */ if (AR_SREV_9300_20_OR_LATER(ah)) clr = AR_PCIE_PM_CTRL_ENA; REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr); if (AR_SREV_9462(ah)) { /* * this is needed to prevent the chip waking up * the host within 3-4 seconds with certain * platform/BIOS. The fix is to enable * D1 & D3 to match original definition and * also match the OTP value. Anyway this * is more related to SW WOW. */ clr = AR_PMCTRL_PWR_STATE_D1D3; REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); set = AR_PMCTRL_PWR_STATE_D1D3_REAL; REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); } REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); if (AR_SREV_9300_20_OR_LATER(ah)) { /* to bring down WOW power low margin */ set = BIT(13); REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set); /* HW WoW */ clr = BIT(5); REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr); } ath9k_hw_set_powermode_wow_sleep(ah); ah->wow_event_mask = wow_event_mask; } EXPORT_SYMBOL(ath9k_hw_wow_enable); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/wmi.h0000644000175000017500000001111312026211315023217 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef WMI_H #define WMI_H struct wmi_event_txrate { __be32 txrate; struct { u8 rssi_thresh; u8 per; } rc_stats; } __packed; struct wmi_cmd_hdr { __be16 command_id; __be16 seq_no; } __packed; struct wmi_fw_version { __be16 major; __be16 minor; } __packed; struct wmi_event_swba { __be64 tsf; u8 beacon_pending; }; /* * 64 - HTC header - WMI header - 1 / txstatus * And some other hdr. space is also accounted for. * 12 seems to be the magic number. */ #define HTC_MAX_TX_STATUS 12 #define ATH9K_HTC_TXSTAT_ACK BIT(0) #define ATH9K_HTC_TXSTAT_FILT BIT(1) #define ATH9K_HTC_TXSTAT_RTC_CTS BIT(2) #define ATH9K_HTC_TXSTAT_MCS BIT(3) #define ATH9K_HTC_TXSTAT_CW40 BIT(4) #define ATH9K_HTC_TXSTAT_SGI BIT(5) /* * Legacy rates are indicated as indices. * HT rates are indicated as dot11 numbers. * This allows us to resrict the rate field * to 4 bits. */ #define ATH9K_HTC_TXSTAT_RATE 0x0f #define ATH9K_HTC_TXSTAT_RATE_S 0 #define ATH9K_HTC_TXSTAT_EPID 0xf0 #define ATH9K_HTC_TXSTAT_EPID_S 4 struct __wmi_event_txstatus { u8 cookie; u8 ts_rate; /* Also holds EP ID */ u8 ts_flags; }; struct wmi_event_txstatus { u8 cnt; struct __wmi_event_txstatus txstatus[HTC_MAX_TX_STATUS]; } __packed; enum wmi_cmd_id { WMI_ECHO_CMDID = 0x0001, WMI_ACCESS_MEMORY_CMDID, /* Commands to Target */ WMI_GET_FW_VERSION, WMI_DISABLE_INTR_CMDID, WMI_ENABLE_INTR_CMDID, WMI_ATH_INIT_CMDID, WMI_ABORT_TXQ_CMDID, WMI_STOP_TX_DMA_CMDID, WMI_ABORT_TX_DMA_CMDID, WMI_DRAIN_TXQ_CMDID, WMI_DRAIN_TXQ_ALL_CMDID, WMI_START_RECV_CMDID, WMI_STOP_RECV_CMDID, WMI_FLUSH_RECV_CMDID, WMI_SET_MODE_CMDID, WMI_NODE_CREATE_CMDID, WMI_NODE_REMOVE_CMDID, WMI_VAP_REMOVE_CMDID, WMI_VAP_CREATE_CMDID, WMI_REG_READ_CMDID, WMI_REG_WRITE_CMDID, WMI_RC_STATE_CHANGE_CMDID, WMI_RC_RATE_UPDATE_CMDID, WMI_TARGET_IC_UPDATE_CMDID, WMI_TX_AGGR_ENABLE_CMDID, WMI_TGT_DETACH_CMDID, WMI_NODE_UPDATE_CMDID, WMI_INT_STATS_CMDID, WMI_TX_STATS_CMDID, WMI_RX_STATS_CMDID, WMI_BITRATE_MASK_CMDID, }; enum wmi_event_id { WMI_TGT_RDY_EVENTID = 0x1001, WMI_SWBA_EVENTID, WMI_FATAL_EVENTID, WMI_TXTO_EVENTID, WMI_BMISS_EVENTID, WMI_DELBA_EVENTID, WMI_TXSTATUS_EVENTID, }; #define MAX_CMD_NUMBER 62 struct register_write { __be32 reg; __be32 val; }; struct ath9k_htc_tx_event { int count; struct __wmi_event_txstatus txs; struct list_head list; }; struct wmi { struct ath9k_htc_priv *drv_priv; struct htc_target *htc; enum htc_endpoint_id ctrl_epid; struct mutex op_mutex; struct completion cmd_wait; enum wmi_cmd_id last_cmd_id; struct sk_buff_head wmi_event_queue; struct tasklet_struct wmi_event_tasklet; u16 tx_seq_id; u8 *cmd_rsp_buf; u32 cmd_rsp_len; bool stopped; struct list_head pending_tx_events; spinlock_t event_lock; spinlock_t wmi_lock; atomic_t mwrite_cnt; struct register_write multi_write[MAX_CMD_NUMBER]; u32 multi_write_idx; struct mutex multi_write_mutex; }; struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); void ath9k_deinit_wmi(struct ath9k_htc_priv *priv); int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi, enum htc_endpoint_id *wmi_ctrl_epid); int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, u8 *cmd_buf, u32 cmd_len, u8 *rsp_buf, u32 rsp_len, u32 timeout); void ath9k_wmi_event_tasklet(unsigned long data); void ath9k_fatal_work(struct work_struct *work); void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv); #define WMI_CMD(_wmi_cmd) \ do { \ ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, NULL, 0, \ (u8 *) &cmd_rsp, \ sizeof(cmd_rsp), HZ*2); \ } while (0) #define WMI_CMD_BUF(_wmi_cmd, _buf) \ do { \ ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, \ (u8 *) _buf, sizeof(*_buf), \ &cmd_rsp, sizeof(cmd_rsp), HZ*2); \ } while (0) #endif /* WMI_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/wmi.c0000644000175000017500000002137312026211315023223 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "htc.h" static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd) { switch (wmi_cmd) { case WMI_ECHO_CMDID: return "WMI_ECHO_CMDID"; case WMI_ACCESS_MEMORY_CMDID: return "WMI_ACCESS_MEMORY_CMDID"; case WMI_GET_FW_VERSION: return "WMI_GET_FW_VERSION"; case WMI_DISABLE_INTR_CMDID: return "WMI_DISABLE_INTR_CMDID"; case WMI_ENABLE_INTR_CMDID: return "WMI_ENABLE_INTR_CMDID"; case WMI_ATH_INIT_CMDID: return "WMI_ATH_INIT_CMDID"; case WMI_ABORT_TXQ_CMDID: return "WMI_ABORT_TXQ_CMDID"; case WMI_STOP_TX_DMA_CMDID: return "WMI_STOP_TX_DMA_CMDID"; case WMI_ABORT_TX_DMA_CMDID: return "WMI_ABORT_TX_DMA_CMDID"; case WMI_DRAIN_TXQ_CMDID: return "WMI_DRAIN_TXQ_CMDID"; case WMI_DRAIN_TXQ_ALL_CMDID: return "WMI_DRAIN_TXQ_ALL_CMDID"; case WMI_START_RECV_CMDID: return "WMI_START_RECV_CMDID"; case WMI_STOP_RECV_CMDID: return "WMI_STOP_RECV_CMDID"; case WMI_FLUSH_RECV_CMDID: return "WMI_FLUSH_RECV_CMDID"; case WMI_SET_MODE_CMDID: return "WMI_SET_MODE_CMDID"; case WMI_NODE_CREATE_CMDID: return "WMI_NODE_CREATE_CMDID"; case WMI_NODE_REMOVE_CMDID: return "WMI_NODE_REMOVE_CMDID"; case WMI_VAP_REMOVE_CMDID: return "WMI_VAP_REMOVE_CMDID"; case WMI_VAP_CREATE_CMDID: return "WMI_VAP_CREATE_CMDID"; case WMI_REG_READ_CMDID: return "WMI_REG_READ_CMDID"; case WMI_REG_WRITE_CMDID: return "WMI_REG_WRITE_CMDID"; case WMI_RC_STATE_CHANGE_CMDID: return "WMI_RC_STATE_CHANGE_CMDID"; case WMI_RC_RATE_UPDATE_CMDID: return "WMI_RC_RATE_UPDATE_CMDID"; case WMI_TARGET_IC_UPDATE_CMDID: return "WMI_TARGET_IC_UPDATE_CMDID"; case WMI_TX_AGGR_ENABLE_CMDID: return "WMI_TX_AGGR_ENABLE_CMDID"; case WMI_TGT_DETACH_CMDID: return "WMI_TGT_DETACH_CMDID"; case WMI_NODE_UPDATE_CMDID: return "WMI_NODE_UPDATE_CMDID"; case WMI_INT_STATS_CMDID: return "WMI_INT_STATS_CMDID"; case WMI_TX_STATS_CMDID: return "WMI_TX_STATS_CMDID"; case WMI_RX_STATS_CMDID: return "WMI_RX_STATS_CMDID"; case WMI_BITRATE_MASK_CMDID: return "WMI_BITRATE_MASK_CMDID"; } return "Bogus"; } struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv) { struct wmi *wmi; wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL); if (!wmi) return NULL; wmi->drv_priv = priv; wmi->stopped = false; skb_queue_head_init(&wmi->wmi_event_queue); spin_lock_init(&wmi->wmi_lock); spin_lock_init(&wmi->event_lock); mutex_init(&wmi->op_mutex); mutex_init(&wmi->multi_write_mutex); init_completion(&wmi->cmd_wait); INIT_LIST_HEAD(&wmi->pending_tx_events); tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet, (unsigned long)wmi); return wmi; } void ath9k_deinit_wmi(struct ath9k_htc_priv *priv) { struct wmi *wmi = priv->wmi; mutex_lock(&wmi->op_mutex); wmi->stopped = true; mutex_unlock(&wmi->op_mutex); kfree(priv->wmi); } void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv) { unsigned long flags; tasklet_kill(&priv->wmi->wmi_event_tasklet); spin_lock_irqsave(&priv->wmi->wmi_lock, flags); __skb_queue_purge(&priv->wmi->wmi_event_queue); spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags); } void ath9k_wmi_event_tasklet(unsigned long data) { struct wmi *wmi = (struct wmi *)data; struct ath9k_htc_priv *priv = wmi->drv_priv; struct wmi_cmd_hdr *hdr; void *wmi_event; struct wmi_event_swba *swba; struct sk_buff *skb = NULL; unsigned long flags; u16 cmd_id; do { spin_lock_irqsave(&wmi->wmi_lock, flags); skb = __skb_dequeue(&wmi->wmi_event_queue); if (!skb) { spin_unlock_irqrestore(&wmi->wmi_lock, flags); return; } spin_unlock_irqrestore(&wmi->wmi_lock, flags); hdr = (struct wmi_cmd_hdr *) skb->data; cmd_id = be16_to_cpu(hdr->command_id); wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr)); switch (cmd_id) { case WMI_SWBA_EVENTID: swba = (struct wmi_event_swba *) wmi_event; ath9k_htc_swba(priv, swba); break; case WMI_FATAL_EVENTID: ieee80211_queue_work(wmi->drv_priv->hw, &wmi->drv_priv->fatal_work); break; case WMI_TXSTATUS_EVENTID: spin_lock_bh(&priv->tx.tx_lock); if (priv->tx.flags & ATH9K_HTC_OP_TX_DRAIN) { spin_unlock_bh(&priv->tx.tx_lock); break; } spin_unlock_bh(&priv->tx.tx_lock); ath9k_htc_txstatus(priv, wmi_event); break; default: break; } kfree_skb(skb); } while (1); } void ath9k_fatal_work(struct work_struct *work) { struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, fatal_work); struct ath_common *common = ath9k_hw_common(priv->ah); ath_dbg(common, FATAL, "FATAL Event received, resetting device\n"); ath9k_htc_reset(priv); } static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb) { skb_pull(skb, sizeof(struct wmi_cmd_hdr)); if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0) memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len); complete(&wmi->cmd_wait); } static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, enum htc_endpoint_id epid) { struct wmi *wmi = (struct wmi *) priv; struct wmi_cmd_hdr *hdr; u16 cmd_id; if (unlikely(wmi->stopped)) goto free_skb; hdr = (struct wmi_cmd_hdr *) skb->data; cmd_id = be16_to_cpu(hdr->command_id); if (cmd_id & 0x1000) { spin_lock(&wmi->wmi_lock); __skb_queue_tail(&wmi->wmi_event_queue, skb); spin_unlock(&wmi->wmi_lock); tasklet_schedule(&wmi->wmi_event_tasklet); return; } /* Check if there has been a timeout. */ spin_lock(&wmi->wmi_lock); if (cmd_id != wmi->last_cmd_id) { spin_unlock(&wmi->wmi_lock); goto free_skb; } spin_unlock(&wmi->wmi_lock); /* WMI command response */ ath9k_wmi_rsp_callback(wmi, skb); free_skb: kfree_skb(skb); } static void ath9k_wmi_ctrl_tx(void *priv, struct sk_buff *skb, enum htc_endpoint_id epid, bool txok) { kfree_skb(skb); } int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi, enum htc_endpoint_id *wmi_ctrl_epid) { struct htc_service_connreq connect; int ret; wmi->htc = htc; memset(&connect, 0, sizeof(connect)); connect.ep_callbacks.priv = wmi; connect.ep_callbacks.tx = ath9k_wmi_ctrl_tx; connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx; connect.service_id = WMI_CONTROL_SVC; ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid); if (ret) return ret; *wmi_ctrl_epid = wmi->ctrl_epid; return 0; } static int ath9k_wmi_cmd_issue(struct wmi *wmi, struct sk_buff *skb, enum wmi_cmd_id cmd, u16 len) { struct wmi_cmd_hdr *hdr; hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr)); hdr->command_id = cpu_to_be16(cmd); hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id); return htc_send_epid(wmi->htc, skb, wmi->ctrl_epid); } int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, u8 *cmd_buf, u32 cmd_len, u8 *rsp_buf, u32 rsp_len, u32 timeout) { struct ath_hw *ah = wmi->drv_priv->ah; struct ath_common *common = ath9k_hw_common(ah); u16 headroom = sizeof(struct htc_frame_hdr) + sizeof(struct wmi_cmd_hdr); struct sk_buff *skb; u8 *data; int time_left, ret = 0; unsigned long flags; if (ah->ah_flags & AH_UNPLUGGED) return 0; skb = alloc_skb(headroom + cmd_len, GFP_ATOMIC); if (!skb) return -ENOMEM; skb_reserve(skb, headroom); if (cmd_len != 0 && cmd_buf != NULL) { data = (u8 *) skb_put(skb, cmd_len); memcpy(data, cmd_buf, cmd_len); } mutex_lock(&wmi->op_mutex); /* check if wmi stopped flag is set */ if (unlikely(wmi->stopped)) { ret = -EPROTO; goto out; } /* record the rsp buffer and length */ wmi->cmd_rsp_buf = rsp_buf; wmi->cmd_rsp_len = rsp_len; spin_lock_irqsave(&wmi->wmi_lock, flags); wmi->last_cmd_id = cmd_id; spin_unlock_irqrestore(&wmi->wmi_lock, flags); ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len); if (ret) goto out; time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout); if (!time_left) { ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n", wmi_cmd_to_name(cmd_id)); mutex_unlock(&wmi->op_mutex); return -ETIMEDOUT; } mutex_unlock(&wmi->op_mutex); return 0; out: ath_dbg(common, WMI, "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id)); mutex_unlock(&wmi->op_mutex); kfree_skb(skb); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/reg.h0000644000175000017500000025740612026211315023221 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef REG_H #define REG_H #include "../reg.h" #define AR_CR 0x0008 #define AR_CR_RXE (AR_SREV_9300_20_OR_LATER(ah) ? 0x0000000c : 0x00000004) #define AR_CR_RXD 0x00000020 #define AR_CR_SWI 0x00000040 #define AR_RXDP 0x000C #define AR_CFG 0x0014 #define AR_CFG_SWTD 0x00000001 #define AR_CFG_SWTB 0x00000002 #define AR_CFG_SWRD 0x00000004 #define AR_CFG_SWRB 0x00000008 #define AR_CFG_SWRG 0x00000010 #define AR_CFG_AP_ADHOC_INDICATION 0x00000020 #define AR_CFG_PHOK 0x00000100 #define AR_CFG_CLK_GATE_DIS 0x00000400 #define AR_CFG_EEBS 0x00000200 #define AR_CFG_PCI_MASTER_REQ_Q_THRESH 0x00060000 #define AR_CFG_PCI_MASTER_REQ_Q_THRESH_S 17 #define AR_RXBP_THRESH 0x0018 #define AR_RXBP_THRESH_HP 0x0000000f #define AR_RXBP_THRESH_HP_S 0 #define AR_RXBP_THRESH_LP 0x00003f00 #define AR_RXBP_THRESH_LP_S 8 #define AR_MIRT 0x0020 #define AR_MIRT_VAL 0x0000ffff #define AR_MIRT_VAL_S 16 #define AR_IER 0x0024 #define AR_IER_ENABLE 0x00000001 #define AR_IER_DISABLE 0x00000000 #define AR_TIMT 0x0028 #define AR_TIMT_LAST 0x0000ffff #define AR_TIMT_LAST_S 0 #define AR_TIMT_FIRST 0xffff0000 #define AR_TIMT_FIRST_S 16 #define AR_RIMT 0x002C #define AR_RIMT_LAST 0x0000ffff #define AR_RIMT_LAST_S 0 #define AR_RIMT_FIRST 0xffff0000 #define AR_RIMT_FIRST_S 16 #define AR_DMASIZE_4B 0x00000000 #define AR_DMASIZE_8B 0x00000001 #define AR_DMASIZE_16B 0x00000002 #define AR_DMASIZE_32B 0x00000003 #define AR_DMASIZE_64B 0x00000004 #define AR_DMASIZE_128B 0x00000005 #define AR_DMASIZE_256B 0x00000006 #define AR_DMASIZE_512B 0x00000007 #define AR_TXCFG 0x0030 #define AR_TXCFG_DMASZ_MASK 0x00000007 #define AR_TXCFG_DMASZ_4B 0 #define AR_TXCFG_DMASZ_8B 1 #define AR_TXCFG_DMASZ_16B 2 #define AR_TXCFG_DMASZ_32B 3 #define AR_TXCFG_DMASZ_64B 4 #define AR_TXCFG_DMASZ_128B 5 #define AR_TXCFG_DMASZ_256B 6 #define AR_TXCFG_DMASZ_512B 7 #define AR_FTRIG 0x000003F0 #define AR_FTRIG_S 4 #define AR_FTRIG_IMMED 0x00000000 #define AR_FTRIG_64B 0x00000010 #define AR_FTRIG_128B 0x00000020 #define AR_FTRIG_192B 0x00000030 #define AR_FTRIG_256B 0x00000040 #define AR_FTRIG_512B 0x00000080 #define AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY 0x00000800 #define AR_RXCFG 0x0034 #define AR_RXCFG_CHIRP 0x00000008 #define AR_RXCFG_ZLFDMA 0x00000010 #define AR_RXCFG_DMASZ_MASK 0x00000007 #define AR_RXCFG_DMASZ_4B 0 #define AR_RXCFG_DMASZ_8B 1 #define AR_RXCFG_DMASZ_16B 2 #define AR_RXCFG_DMASZ_32B 3 #define AR_RXCFG_DMASZ_64B 4 #define AR_RXCFG_DMASZ_128B 5 #define AR_RXCFG_DMASZ_256B 6 #define AR_RXCFG_DMASZ_512B 7 #define AR_TOPS 0x0044 #define AR_TOPS_MASK 0x0000FFFF #define AR_RXNPTO 0x0048 #define AR_RXNPTO_MASK 0x000003FF #define AR_TXNPTO 0x004C #define AR_TXNPTO_MASK 0x000003FF #define AR_TXNPTO_QCU_MASK 0x000FFC00 #define AR_RPGTO 0x0050 #define AR_RPGTO_MASK 0x000003FF #define AR_RPCNT 0x0054 #define AR_RPCNT_MASK 0x0000001F #define AR_MACMISC 0x0058 #define AR_MACMISC_PCI_EXT_FORCE 0x00000010 #define AR_MACMISC_DMA_OBS 0x000001E0 #define AR_MACMISC_DMA_OBS_S 5 #define AR_MACMISC_DMA_OBS_LINE_0 0 #define AR_MACMISC_DMA_OBS_LINE_1 1 #define AR_MACMISC_DMA_OBS_LINE_2 2 #define AR_MACMISC_DMA_OBS_LINE_3 3 #define AR_MACMISC_DMA_OBS_LINE_4 4 #define AR_MACMISC_DMA_OBS_LINE_5 5 #define AR_MACMISC_DMA_OBS_LINE_6 6 #define AR_MACMISC_DMA_OBS_LINE_7 7 #define AR_MACMISC_DMA_OBS_LINE_8 8 #define AR_MACMISC_MISC_OBS 0x00000E00 #define AR_MACMISC_MISC_OBS_S 9 #define AR_MACMISC_MISC_OBS_BUS_LSB 0x00007000 #define AR_MACMISC_MISC_OBS_BUS_LSB_S 12 #define AR_MACMISC_MISC_OBS_BUS_MSB 0x00038000 #define AR_MACMISC_MISC_OBS_BUS_MSB_S 15 #define AR_MACMISC_MISC_OBS_BUS_1 1 #define AR_DATABUF_SIZE 0x0060 #define AR_DATABUF_SIZE_MASK 0x00000FFF #define AR_GTXTO 0x0064 #define AR_GTXTO_TIMEOUT_COUNTER 0x0000FFFF #define AR_GTXTO_TIMEOUT_LIMIT 0xFFFF0000 #define AR_GTXTO_TIMEOUT_LIMIT_S 16 #define AR_GTTM 0x0068 #define AR_GTTM_USEC 0x00000001 #define AR_GTTM_IGNORE_IDLE 0x00000002 #define AR_GTTM_RESET_IDLE 0x00000004 #define AR_GTTM_CST_USEC 0x00000008 #define AR_CST 0x006C #define AR_CST_TIMEOUT_COUNTER 0x0000FFFF #define AR_CST_TIMEOUT_LIMIT 0xFFFF0000 #define AR_CST_TIMEOUT_LIMIT_S 16 #define AR_HP_RXDP 0x0074 #define AR_LP_RXDP 0x0078 #define AR_ISR 0x0080 #define AR_ISR_RXOK 0x00000001 #define AR_ISR_RXDESC 0x00000002 #define AR_ISR_HP_RXOK 0x00000001 #define AR_ISR_LP_RXOK 0x00000002 #define AR_ISR_RXERR 0x00000004 #define AR_ISR_RXNOPKT 0x00000008 #define AR_ISR_RXEOL 0x00000010 #define AR_ISR_RXORN 0x00000020 #define AR_ISR_TXOK 0x00000040 #define AR_ISR_TXDESC 0x00000080 #define AR_ISR_TXERR 0x00000100 #define AR_ISR_TXNOPKT 0x00000200 #define AR_ISR_TXEOL 0x00000400 #define AR_ISR_TXURN 0x00000800 #define AR_ISR_MIB 0x00001000 #define AR_ISR_SWI 0x00002000 #define AR_ISR_RXPHY 0x00004000 #define AR_ISR_RXKCM 0x00008000 #define AR_ISR_SWBA 0x00010000 #define AR_ISR_BRSSI 0x00020000 #define AR_ISR_BMISS 0x00040000 #define AR_ISR_BNR 0x00100000 #define AR_ISR_RXCHIRP 0x00200000 #define AR_ISR_BCNMISC 0x00800000 #define AR_ISR_TIM 0x00800000 #define AR_ISR_QCBROVF 0x02000000 #define AR_ISR_QCBRURN 0x04000000 #define AR_ISR_QTRIG 0x08000000 #define AR_ISR_GENTMR 0x10000000 #define AR_ISR_TXMINTR 0x00080000 #define AR_ISR_RXMINTR 0x01000000 #define AR_ISR_TXINTM 0x40000000 #define AR_ISR_RXINTM 0x80000000 #define AR_ISR_S0 0x0084 #define AR_ISR_S0_QCU_TXOK 0x000003FF #define AR_ISR_S0_QCU_TXOK_S 0 #define AR_ISR_S0_QCU_TXDESC 0x03FF0000 #define AR_ISR_S0_QCU_TXDESC_S 16 #define AR_ISR_S1 0x0088 #define AR_ISR_S1_QCU_TXERR 0x000003FF #define AR_ISR_S1_QCU_TXERR_S 0 #define AR_ISR_S1_QCU_TXEOL 0x03FF0000 #define AR_ISR_S1_QCU_TXEOL_S 16 #define AR_ISR_S2 0x008c #define AR_ISR_S2_QCU_TXURN 0x000003FF #define AR_ISR_S2_BB_WATCHDOG 0x00010000 #define AR_ISR_S2_CST 0x00400000 #define AR_ISR_S2_GTT 0x00800000 #define AR_ISR_S2_TIM 0x01000000 #define AR_ISR_S2_CABEND 0x02000000 #define AR_ISR_S2_DTIMSYNC 0x04000000 #define AR_ISR_S2_BCNTO 0x08000000 #define AR_ISR_S2_CABTO 0x10000000 #define AR_ISR_S2_DTIM 0x20000000 #define AR_ISR_S2_TSFOOR 0x40000000 #define AR_ISR_S2_TBTT_TIME 0x80000000 #define AR_ISR_S3 0x0090 #define AR_ISR_S3_QCU_QCBROVF 0x000003FF #define AR_ISR_S3_QCU_QCBRURN 0x03FF0000 #define AR_ISR_S4 0x0094 #define AR_ISR_S4_QCU_QTRIG 0x000003FF #define AR_ISR_S4_RESV0 0xFFFFFC00 #define AR_ISR_S5 0x0098 #define AR_ISR_S5_TIMER_TRIG 0x000000FF #define AR_ISR_S5_TIMER_THRESH 0x0007FE00 #define AR_ISR_S5_TIM_TIMER 0x00000010 #define AR_ISR_S5_DTIM_TIMER 0x00000020 #define AR_IMR_S5 0x00b8 #define AR_IMR_S5_TIM_TIMER 0x00000010 #define AR_IMR_S5_DTIM_TIMER 0x00000020 #define AR_ISR_S5_GENTIMER_TRIG 0x0000FF80 #define AR_ISR_S5_GENTIMER_TRIG_S 0 #define AR_ISR_S5_GENTIMER_THRESH 0xFF800000 #define AR_ISR_S5_GENTIMER_THRESH_S 16 #define AR_IMR_S5_GENTIMER_TRIG 0x0000FF80 #define AR_IMR_S5_GENTIMER_TRIG_S 0 #define AR_IMR_S5_GENTIMER_THRESH 0xFF800000 #define AR_IMR_S5_GENTIMER_THRESH_S 16 #define AR_IMR 0x00a0 #define AR_IMR_RXOK 0x00000001 #define AR_IMR_RXDESC 0x00000002 #define AR_IMR_RXOK_HP 0x00000001 #define AR_IMR_RXOK_LP 0x00000002 #define AR_IMR_RXERR 0x00000004 #define AR_IMR_RXNOPKT 0x00000008 #define AR_IMR_RXEOL 0x00000010 #define AR_IMR_RXORN 0x00000020 #define AR_IMR_TXOK 0x00000040 #define AR_IMR_TXDESC 0x00000080 #define AR_IMR_TXERR 0x00000100 #define AR_IMR_TXNOPKT 0x00000200 #define AR_IMR_TXEOL 0x00000400 #define AR_IMR_TXURN 0x00000800 #define AR_IMR_MIB 0x00001000 #define AR_IMR_SWI 0x00002000 #define AR_IMR_RXPHY 0x00004000 #define AR_IMR_RXKCM 0x00008000 #define AR_IMR_SWBA 0x00010000 #define AR_IMR_BRSSI 0x00020000 #define AR_IMR_BMISS 0x00040000 #define AR_IMR_BNR 0x00100000 #define AR_IMR_RXCHIRP 0x00200000 #define AR_IMR_BCNMISC 0x00800000 #define AR_IMR_TIM 0x00800000 #define AR_IMR_QCBROVF 0x02000000 #define AR_IMR_QCBRURN 0x04000000 #define AR_IMR_QTRIG 0x08000000 #define AR_IMR_GENTMR 0x10000000 #define AR_IMR_TXMINTR 0x00080000 #define AR_IMR_RXMINTR 0x01000000 #define AR_IMR_TXINTM 0x40000000 #define AR_IMR_RXINTM 0x80000000 #define AR_IMR_S0 0x00a4 #define AR_IMR_S0_QCU_TXOK 0x000003FF #define AR_IMR_S0_QCU_TXOK_S 0 #define AR_IMR_S0_QCU_TXDESC 0x03FF0000 #define AR_IMR_S0_QCU_TXDESC_S 16 #define AR_IMR_S1 0x00a8 #define AR_IMR_S1_QCU_TXERR 0x000003FF #define AR_IMR_S1_QCU_TXERR_S 0 #define AR_IMR_S1_QCU_TXEOL 0x03FF0000 #define AR_IMR_S1_QCU_TXEOL_S 16 #define AR_IMR_S2 0x00ac #define AR_IMR_S2_QCU_TXURN 0x000003FF #define AR_IMR_S2_QCU_TXURN_S 0 #define AR_IMR_S2_CST 0x00400000 #define AR_IMR_S2_GTT 0x00800000 #define AR_IMR_S2_TIM 0x01000000 #define AR_IMR_S2_CABEND 0x02000000 #define AR_IMR_S2_DTIMSYNC 0x04000000 #define AR_IMR_S2_BCNTO 0x08000000 #define AR_IMR_S2_CABTO 0x10000000 #define AR_IMR_S2_DTIM 0x20000000 #define AR_IMR_S2_TSFOOR 0x40000000 #define AR_IMR_S3 0x00b0 #define AR_IMR_S3_QCU_QCBROVF 0x000003FF #define AR_IMR_S3_QCU_QCBRURN 0x03FF0000 #define AR_IMR_S3_QCU_QCBRURN_S 16 #define AR_IMR_S4 0x00b4 #define AR_IMR_S4_QCU_QTRIG 0x000003FF #define AR_IMR_S4_RESV0 0xFFFFFC00 #define AR_IMR_S5 0x00b8 #define AR_IMR_S5_TIMER_TRIG 0x000000FF #define AR_IMR_S5_TIMER_THRESH 0x0000FF00 #define AR_ISR_RAC 0x00c0 #define AR_ISR_S0_S 0x00c4 #define AR_ISR_S0_QCU_TXOK 0x000003FF #define AR_ISR_S0_QCU_TXOK_S 0 #define AR_ISR_S0_QCU_TXDESC 0x03FF0000 #define AR_ISR_S0_QCU_TXDESC_S 16 #define AR_ISR_S1_S 0x00c8 #define AR_ISR_S1_QCU_TXERR 0x000003FF #define AR_ISR_S1_QCU_TXERR_S 0 #define AR_ISR_S1_QCU_TXEOL 0x03FF0000 #define AR_ISR_S1_QCU_TXEOL_S 16 #define AR_ISR_S2_S (AR_SREV_9300_20_OR_LATER(ah) ? 0x00d0 : 0x00cc) #define AR_ISR_S3_S (AR_SREV_9300_20_OR_LATER(ah) ? 0x00d4 : 0x00d0) #define AR_ISR_S4_S (AR_SREV_9300_20_OR_LATER(ah) ? 0x00d8 : 0x00d4) #define AR_ISR_S5_S (AR_SREV_9300_20_OR_LATER(ah) ? 0x00dc : 0x00d8) #define AR_DMADBG_0 0x00e0 #define AR_DMADBG_1 0x00e4 #define AR_DMADBG_2 0x00e8 #define AR_DMADBG_3 0x00ec #define AR_DMADBG_4 0x00f0 #define AR_DMADBG_5 0x00f4 #define AR_DMADBG_6 0x00f8 #define AR_DMADBG_7 0x00fc #define AR_NUM_QCU 10 #define AR_QCU_0 0x0001 #define AR_QCU_1 0x0002 #define AR_QCU_2 0x0004 #define AR_QCU_3 0x0008 #define AR_QCU_4 0x0010 #define AR_QCU_5 0x0020 #define AR_QCU_6 0x0040 #define AR_QCU_7 0x0080 #define AR_QCU_8 0x0100 #define AR_QCU_9 0x0200 #define AR_Q0_TXDP 0x0800 #define AR_Q1_TXDP 0x0804 #define AR_Q2_TXDP 0x0808 #define AR_Q3_TXDP 0x080c #define AR_Q4_TXDP 0x0810 #define AR_Q5_TXDP 0x0814 #define AR_Q6_TXDP 0x0818 #define AR_Q7_TXDP 0x081c #define AR_Q8_TXDP 0x0820 #define AR_Q9_TXDP 0x0824 #define AR_QTXDP(_i) (AR_Q0_TXDP + ((_i)<<2)) #define AR_Q_STATUS_RING_START 0x830 #define AR_Q_STATUS_RING_END 0x834 #define AR_Q_TXE 0x0840 #define AR_Q_TXE_M 0x000003FF #define AR_Q_TXD 0x0880 #define AR_Q_TXD_M 0x000003FF #define AR_Q0_CBRCFG 0x08c0 #define AR_Q1_CBRCFG 0x08c4 #define AR_Q2_CBRCFG 0x08c8 #define AR_Q3_CBRCFG 0x08cc #define AR_Q4_CBRCFG 0x08d0 #define AR_Q5_CBRCFG 0x08d4 #define AR_Q6_CBRCFG 0x08d8 #define AR_Q7_CBRCFG 0x08dc #define AR_Q8_CBRCFG 0x08e0 #define AR_Q9_CBRCFG 0x08e4 #define AR_QCBRCFG(_i) (AR_Q0_CBRCFG + ((_i)<<2)) #define AR_Q_CBRCFG_INTERVAL 0x00FFFFFF #define AR_Q_CBRCFG_INTERVAL_S 0 #define AR_Q_CBRCFG_OVF_THRESH 0xFF000000 #define AR_Q_CBRCFG_OVF_THRESH_S 24 #define AR_Q0_RDYTIMECFG 0x0900 #define AR_Q1_RDYTIMECFG 0x0904 #define AR_Q2_RDYTIMECFG 0x0908 #define AR_Q3_RDYTIMECFG 0x090c #define AR_Q4_RDYTIMECFG 0x0910 #define AR_Q5_RDYTIMECFG 0x0914 #define AR_Q6_RDYTIMECFG 0x0918 #define AR_Q7_RDYTIMECFG 0x091c #define AR_Q8_RDYTIMECFG 0x0920 #define AR_Q9_RDYTIMECFG 0x0924 #define AR_QRDYTIMECFG(_i) (AR_Q0_RDYTIMECFG + ((_i)<<2)) #define AR_Q_RDYTIMECFG_DURATION 0x00FFFFFF #define AR_Q_RDYTIMECFG_DURATION_S 0 #define AR_Q_RDYTIMECFG_EN 0x01000000 #define AR_Q_ONESHOTARM_SC 0x0940 #define AR_Q_ONESHOTARM_SC_M 0x000003FF #define AR_Q_ONESHOTARM_SC_RESV0 0xFFFFFC00 #define AR_Q_ONESHOTARM_CC 0x0980 #define AR_Q_ONESHOTARM_CC_M 0x000003FF #define AR_Q_ONESHOTARM_CC_RESV0 0xFFFFFC00 #define AR_Q0_MISC 0x09c0 #define AR_Q1_MISC 0x09c4 #define AR_Q2_MISC 0x09c8 #define AR_Q3_MISC 0x09cc #define AR_Q4_MISC 0x09d0 #define AR_Q5_MISC 0x09d4 #define AR_Q6_MISC 0x09d8 #define AR_Q7_MISC 0x09dc #define AR_Q8_MISC 0x09e0 #define AR_Q9_MISC 0x09e4 #define AR_QMISC(_i) (AR_Q0_MISC + ((_i)<<2)) #define AR_Q_MISC_FSP 0x0000000F #define AR_Q_MISC_FSP_ASAP 0 #define AR_Q_MISC_FSP_CBR 1 #define AR_Q_MISC_FSP_DBA_GATED 2 #define AR_Q_MISC_FSP_TIM_GATED 3 #define AR_Q_MISC_FSP_BEACON_SENT_GATED 4 #define AR_Q_MISC_FSP_BEACON_RCVD_GATED 5 #define AR_Q_MISC_ONE_SHOT_EN 0x00000010 #define AR_Q_MISC_CBR_INCR_DIS1 0x00000020 #define AR_Q_MISC_CBR_INCR_DIS0 0x00000040 #define AR_Q_MISC_BEACON_USE 0x00000080 #define AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN 0x00000100 #define AR_Q_MISC_RDYTIME_EXP_POLICY 0x00000200 #define AR_Q_MISC_RESET_CBR_EXP_CTR 0x00000400 #define AR_Q_MISC_DCU_EARLY_TERM_REQ 0x00000800 #define AR_Q_MISC_RESV0 0xFFFFF000 #define AR_Q0_STS 0x0a00 #define AR_Q1_STS 0x0a04 #define AR_Q2_STS 0x0a08 #define AR_Q3_STS 0x0a0c #define AR_Q4_STS 0x0a10 #define AR_Q5_STS 0x0a14 #define AR_Q6_STS 0x0a18 #define AR_Q7_STS 0x0a1c #define AR_Q8_STS 0x0a20 #define AR_Q9_STS 0x0a24 #define AR_QSTS(_i) (AR_Q0_STS + ((_i)<<2)) #define AR_Q_STS_PEND_FR_CNT 0x00000003 #define AR_Q_STS_RESV0 0x000000FC #define AR_Q_STS_CBR_EXP_CNT 0x0000FF00 #define AR_Q_STS_RESV1 0xFFFF0000 #define AR_Q_RDYTIMESHDN 0x0a40 #define AR_Q_RDYTIMESHDN_M 0x000003FF /* MAC Descriptor CRC check */ #define AR_Q_DESC_CRCCHK 0xa44 /* Enable CRC check on the descriptor fetched from host */ #define AR_Q_DESC_CRCCHK_EN 1 #define AR_NUM_DCU 10 #define AR_DCU_0 0x0001 #define AR_DCU_1 0x0002 #define AR_DCU_2 0x0004 #define AR_DCU_3 0x0008 #define AR_DCU_4 0x0010 #define AR_DCU_5 0x0020 #define AR_DCU_6 0x0040 #define AR_DCU_7 0x0080 #define AR_DCU_8 0x0100 #define AR_DCU_9 0x0200 #define AR_D0_QCUMASK 0x1000 #define AR_D1_QCUMASK 0x1004 #define AR_D2_QCUMASK 0x1008 #define AR_D3_QCUMASK 0x100c #define AR_D4_QCUMASK 0x1010 #define AR_D5_QCUMASK 0x1014 #define AR_D6_QCUMASK 0x1018 #define AR_D7_QCUMASK 0x101c #define AR_D8_QCUMASK 0x1020 #define AR_D9_QCUMASK 0x1024 #define AR_DQCUMASK(_i) (AR_D0_QCUMASK + ((_i)<<2)) #define AR_D_QCUMASK 0x000003FF #define AR_D_QCUMASK_RESV0 0xFFFFFC00 #define AR_D_TXBLK_CMD 0x1038 #define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i)) #define AR_D0_LCL_IFS 0x1040 #define AR_D1_LCL_IFS 0x1044 #define AR_D2_LCL_IFS 0x1048 #define AR_D3_LCL_IFS 0x104c #define AR_D4_LCL_IFS 0x1050 #define AR_D5_LCL_IFS 0x1054 #define AR_D6_LCL_IFS 0x1058 #define AR_D7_LCL_IFS 0x105c #define AR_D8_LCL_IFS 0x1060 #define AR_D9_LCL_IFS 0x1064 #define AR_DLCL_IFS(_i) (AR_D0_LCL_IFS + ((_i)<<2)) #define AR_D_LCL_IFS_CWMIN 0x000003FF #define AR_D_LCL_IFS_CWMIN_S 0 #define AR_D_LCL_IFS_CWMAX 0x000FFC00 #define AR_D_LCL_IFS_CWMAX_S 10 #define AR_D_LCL_IFS_AIFS 0x0FF00000 #define AR_D_LCL_IFS_AIFS_S 20 #define AR_D_LCL_IFS_RESV0 0xF0000000 #define AR_D0_RETRY_LIMIT 0x1080 #define AR_D1_RETRY_LIMIT 0x1084 #define AR_D2_RETRY_LIMIT 0x1088 #define AR_D3_RETRY_LIMIT 0x108c #define AR_D4_RETRY_LIMIT 0x1090 #define AR_D5_RETRY_LIMIT 0x1094 #define AR_D6_RETRY_LIMIT 0x1098 #define AR_D7_RETRY_LIMIT 0x109c #define AR_D8_RETRY_LIMIT 0x10a0 #define AR_D9_RETRY_LIMIT 0x10a4 #define AR_DRETRY_LIMIT(_i) (AR_D0_RETRY_LIMIT + ((_i)<<2)) #define AR_D_RETRY_LIMIT_FR_SH 0x0000000F #define AR_D_RETRY_LIMIT_FR_SH_S 0 #define AR_D_RETRY_LIMIT_STA_SH 0x00003F00 #define AR_D_RETRY_LIMIT_STA_SH_S 8 #define AR_D_RETRY_LIMIT_STA_LG 0x000FC000 #define AR_D_RETRY_LIMIT_STA_LG_S 14 #define AR_D_RETRY_LIMIT_RESV0 0xFFF00000 #define AR_D0_CHNTIME 0x10c0 #define AR_D1_CHNTIME 0x10c4 #define AR_D2_CHNTIME 0x10c8 #define AR_D3_CHNTIME 0x10cc #define AR_D4_CHNTIME 0x10d0 #define AR_D5_CHNTIME 0x10d4 #define AR_D6_CHNTIME 0x10d8 #define AR_D7_CHNTIME 0x10dc #define AR_D8_CHNTIME 0x10e0 #define AR_D9_CHNTIME 0x10e4 #define AR_DCHNTIME(_i) (AR_D0_CHNTIME + ((_i)<<2)) #define AR_D_CHNTIME_DUR 0x000FFFFF #define AR_D_CHNTIME_DUR_S 0 #define AR_D_CHNTIME_EN 0x00100000 #define AR_D_CHNTIME_RESV0 0xFFE00000 #define AR_D0_MISC 0x1100 #define AR_D1_MISC 0x1104 #define AR_D2_MISC 0x1108 #define AR_D3_MISC 0x110c #define AR_D4_MISC 0x1110 #define AR_D5_MISC 0x1114 #define AR_D6_MISC 0x1118 #define AR_D7_MISC 0x111c #define AR_D8_MISC 0x1120 #define AR_D9_MISC 0x1124 #define AR_DMISC(_i) (AR_D0_MISC + ((_i)<<2)) #define AR_D_MISC_BKOFF_THRESH 0x0000003F #define AR_D_MISC_RETRY_CNT_RESET_EN 0x00000040 #define AR_D_MISC_CW_RESET_EN 0x00000080 #define AR_D_MISC_FRAG_WAIT_EN 0x00000100 #define AR_D_MISC_FRAG_BKOFF_EN 0x00000200 #define AR_D_MISC_CW_BKOFF_EN 0x00001000 #define AR_D_MISC_VIR_COL_HANDLING 0x0000C000 #define AR_D_MISC_VIR_COL_HANDLING_S 14 #define AR_D_MISC_VIR_COL_HANDLING_DEFAULT 0 #define AR_D_MISC_VIR_COL_HANDLING_IGNORE 1 #define AR_D_MISC_BEACON_USE 0x00010000 #define AR_D_MISC_ARB_LOCKOUT_CNTRL 0x00060000 #define AR_D_MISC_ARB_LOCKOUT_CNTRL_S 17 #define AR_D_MISC_ARB_LOCKOUT_CNTRL_NONE 0 #define AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR 1 #define AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL 2 #define AR_D_MISC_ARB_LOCKOUT_IGNORE 0x00080000 #define AR_D_MISC_SEQ_NUM_INCR_DIS 0x00100000 #define AR_D_MISC_POST_FR_BKOFF_DIS 0x00200000 #define AR_D_MISC_VIT_COL_CW_BKOFF_EN 0x00400000 #define AR_D_MISC_BLOWN_IFS_RETRY_EN 0x00800000 #define AR_D_MISC_RESV0 0xFF000000 #define AR_D_SEQNUM 0x1140 #define AR_D_GBL_IFS_SIFS 0x1030 #define AR_D_GBL_IFS_SIFS_M 0x0000FFFF #define AR_D_GBL_IFS_SIFS_RESV0 0xFFFFFFFF #define AR_D_TXBLK_BASE 0x1038 #define AR_D_TXBLK_WRITE_BITMASK 0x0000FFFF #define AR_D_TXBLK_WRITE_BITMASK_S 0 #define AR_D_TXBLK_WRITE_SLICE 0x000F0000 #define AR_D_TXBLK_WRITE_SLICE_S 16 #define AR_D_TXBLK_WRITE_DCU 0x00F00000 #define AR_D_TXBLK_WRITE_DCU_S 20 #define AR_D_TXBLK_WRITE_COMMAND 0x0F000000 #define AR_D_TXBLK_WRITE_COMMAND_S 24 #define AR_D_GBL_IFS_SLOT 0x1070 #define AR_D_GBL_IFS_SLOT_M 0x0000FFFF #define AR_D_GBL_IFS_SLOT_RESV0 0xFFFF0000 #define AR_D_GBL_IFS_EIFS 0x10b0 #define AR_D_GBL_IFS_EIFS_M 0x0000FFFF #define AR_D_GBL_IFS_EIFS_RESV0 0xFFFF0000 #define AR_D_GBL_IFS_EIFS_ASYNC_FIFO 363 #define AR_D_GBL_IFS_MISC 0x10f0 #define AR_D_GBL_IFS_MISC_LFSR_SLICE_SEL 0x00000007 #define AR_D_GBL_IFS_MISC_TURBO_MODE 0x00000008 #define AR_D_GBL_IFS_MISC_USEC_DURATION 0x000FFC00 #define AR_D_GBL_IFS_MISC_DCU_ARBITER_DLY 0x00300000 #define AR_D_GBL_IFS_MISC_RANDOM_LFSR_SLICE_DIS 0x01000000 #define AR_D_GBL_IFS_MISC_SLOT_XMIT_WIND_LEN 0x06000000 #define AR_D_GBL_IFS_MISC_FORCE_XMIT_SLOT_BOUND 0x08000000 #define AR_D_GBL_IFS_MISC_IGNORE_BACKOFF 0x10000000 #define AR_D_FPCTL 0x1230 #define AR_D_FPCTL_DCU 0x0000000F #define AR_D_FPCTL_DCU_S 0 #define AR_D_FPCTL_PREFETCH_EN 0x00000010 #define AR_D_FPCTL_BURST_PREFETCH 0x00007FE0 #define AR_D_FPCTL_BURST_PREFETCH_S 5 #define AR_D_TXPSE 0x1270 #define AR_D_TXPSE_CTRL 0x000003FF #define AR_D_TXPSE_RESV0 0x0000FC00 #define AR_D_TXPSE_STATUS 0x00010000 #define AR_D_TXPSE_RESV1 0xFFFE0000 #define AR_D_TXSLOTMASK 0x12f0 #define AR_D_TXSLOTMASK_NUM 0x0000000F #define AR_CFG_LED 0x1f04 #define AR_CFG_SCLK_RATE_IND 0x00000003 #define AR_CFG_SCLK_RATE_IND_S 0 #define AR_CFG_SCLK_32MHZ 0x00000000 #define AR_CFG_SCLK_4MHZ 0x00000001 #define AR_CFG_SCLK_1MHZ 0x00000002 #define AR_CFG_SCLK_32KHZ 0x00000003 #define AR_CFG_LED_BLINK_SLOW 0x00000008 #define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070 #define AR_CFG_LED_MODE_SEL 0x00000380 #define AR_CFG_LED_MODE_SEL_S 7 #define AR_CFG_LED_POWER 0x00000280 #define AR_CFG_LED_POWER_S 7 #define AR_CFG_LED_NETWORK 0x00000300 #define AR_CFG_LED_NETWORK_S 7 #define AR_CFG_LED_MODE_PROP 0x0 #define AR_CFG_LED_MODE_RPROP 0x1 #define AR_CFG_LED_MODE_SPLIT 0x2 #define AR_CFG_LED_MODE_RAND 0x3 #define AR_CFG_LED_MODE_POWER_OFF 0x4 #define AR_CFG_LED_MODE_POWER_ON 0x5 #define AR_CFG_LED_MODE_NETWORK_OFF 0x4 #define AR_CFG_LED_MODE_NETWORK_ON 0x6 #define AR_CFG_LED_ASSOC_CTL 0x00000c00 #define AR_CFG_LED_ASSOC_CTL_S 10 #define AR_CFG_LED_ASSOC_NONE 0x0 #define AR_CFG_LED_ASSOC_ACTIVE 0x1 #define AR_CFG_LED_ASSOC_PENDING 0x2 #define AR_CFG_LED_BLINK_SLOW 0x00000008 #define AR_CFG_LED_BLINK_SLOW_S 3 #define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070 #define AR_CFG_LED_BLINK_THRESH_SEL_S 4 #define AR_MAC_SLEEP 0x1f00 #define AR_MAC_SLEEP_MAC_AWAKE 0x00000000 #define AR_MAC_SLEEP_MAC_ASLEEP 0x00000001 #define AR_RC 0x4000 #define AR_RC_AHB 0x00000001 #define AR_RC_APB 0x00000002 #define AR_RC_HOSTIF 0x00000100 #define AR_WA (AR_SREV_9340(ah) ? 0x40c4 : 0x4004) #define AR_WA_BIT6 (1 << 6) #define AR_WA_BIT7 (1 << 7) #define AR_WA_BIT23 (1 << 23) #define AR_WA_D3_L1_DISABLE (1 << 14) #define AR_WA_UNTIE_RESET_EN (1 << 15) /* Enable PCI Reset to POR (power-on-reset) */ #define AR_WA_D3_TO_L1_DISABLE_REAL (1 << 16) #define AR_WA_ASPM_TIMER_BASED_DISABLE (1 << 17) #define AR_WA_RESET_EN (1 << 18) /* Enable PCI-Reset to POR (bit 15) */ #define AR_WA_ANALOG_SHIFT (1 << 20) #define AR_WA_POR_SHORT (1 << 21) /* PCI-E Phy reset control */ #define AR_WA_BIT22 (1 << 22) #define AR9285_WA_DEFAULT 0x004a050b #define AR9280_WA_DEFAULT 0x0040073b #define AR_WA_DEFAULT 0x0000073f #define AR_PM_STATE 0x4008 #define AR_PM_STATE_PME_D3COLD_VAUX 0x00100000 #define AR_HOST_TIMEOUT (AR_SREV_9340(ah) ? 0x4008 : 0x4018) #define AR_HOST_TIMEOUT_APB_CNTR 0x0000FFFF #define AR_HOST_TIMEOUT_APB_CNTR_S 0 #define AR_HOST_TIMEOUT_LCL_CNTR 0xFFFF0000 #define AR_HOST_TIMEOUT_LCL_CNTR_S 16 #define AR_EEPROM 0x401c #define AR_EEPROM_ABSENT 0x00000100 #define AR_EEPROM_CORRUPT 0x00000200 #define AR_EEPROM_PROT_MASK 0x03FFFC00 #define AR_EEPROM_PROT_MASK_S 10 #define EEPROM_PROTECT_RP_0_31 0x0001 #define EEPROM_PROTECT_WP_0_31 0x0002 #define EEPROM_PROTECT_RP_32_63 0x0004 #define EEPROM_PROTECT_WP_32_63 0x0008 #define EEPROM_PROTECT_RP_64_127 0x0010 #define EEPROM_PROTECT_WP_64_127 0x0020 #define EEPROM_PROTECT_RP_128_191 0x0040 #define EEPROM_PROTECT_WP_128_191 0x0080 #define EEPROM_PROTECT_RP_192_255 0x0100 #define EEPROM_PROTECT_WP_192_255 0x0200 #define EEPROM_PROTECT_RP_256_511 0x0400 #define EEPROM_PROTECT_WP_256_511 0x0800 #define EEPROM_PROTECT_RP_512_1023 0x1000 #define EEPROM_PROTECT_WP_512_1023 0x2000 #define EEPROM_PROTECT_RP_1024_2047 0x4000 #define EEPROM_PROTECT_WP_1024_2047 0x8000 #define AR_SREV \ ((AR_SREV_9100(ah)) ? 0x0600 : (AR_SREV_9340(ah) \ ? 0x400c : 0x4020)) #define AR_SREV_ID \ ((AR_SREV_9100(ah)) ? 0x00000FFF : 0x000000FF) #define AR_SREV_VERSION 0x000000F0 #define AR_SREV_VERSION_S 4 #define AR_SREV_REVISION 0x00000007 #define AR_SREV_ID2 0xFFFFFFFF #define AR_SREV_VERSION2 0xFFFC0000 #define AR_SREV_VERSION2_S 18 #define AR_SREV_TYPE2 0x0003F000 #define AR_SREV_TYPE2_S 12 #define AR_SREV_TYPE2_CHAIN 0x00001000 #define AR_SREV_TYPE2_HOST_MODE 0x00002000 #define AR_SREV_REVISION2 0x00000F00 #define AR_SREV_REVISION2_S 8 #define AR_SREV_VERSION_5416_PCI 0xD #define AR_SREV_VERSION_5416_PCIE 0xC #define AR_SREV_REVISION_5416_10 0 #define AR_SREV_REVISION_5416_20 1 #define AR_SREV_REVISION_5416_22 2 #define AR_SREV_VERSION_9100 0x14 #define AR_SREV_VERSION_9160 0x40 #define AR_SREV_REVISION_9160_10 0 #define AR_SREV_REVISION_9160_11 1 #define AR_SREV_VERSION_9280 0x80 #define AR_SREV_REVISION_9280_10 0 #define AR_SREV_REVISION_9280_20 1 #define AR_SREV_REVISION_9280_21 2 #define AR_SREV_VERSION_9285 0xC0 #define AR_SREV_REVISION_9285_10 0 #define AR_SREV_REVISION_9285_11 1 #define AR_SREV_REVISION_9285_12 2 #define AR_SREV_VERSION_9287 0x180 #define AR_SREV_REVISION_9287_10 0 #define AR_SREV_REVISION_9287_11 1 #define AR_SREV_REVISION_9287_12 2 #define AR_SREV_REVISION_9287_13 3 #define AR_SREV_VERSION_9271 0x140 #define AR_SREV_REVISION_9271_10 0 #define AR_SREV_REVISION_9271_11 1 #define AR_SREV_VERSION_9300 0x1c0 #define AR_SREV_REVISION_9300_20 2 /* 2.0 and 2.1 */ #define AR_SREV_VERSION_9330 0x200 #define AR_SREV_REVISION_9330_10 0 #define AR_SREV_REVISION_9330_11 1 #define AR_SREV_REVISION_9330_12 2 #define AR_SREV_VERSION_9485 0x240 #define AR_SREV_REVISION_9485_10 0 #define AR_SREV_REVISION_9485_11 1 #define AR_SREV_VERSION_9340 0x300 #define AR_SREV_VERSION_9580 0x1C0 #define AR_SREV_REVISION_9580_10 4 /* AR9580 1.0 */ #define AR_SREV_VERSION_9462 0x280 #define AR_SREV_REVISION_9462_20 2 #define AR_SREV_VERSION_9565 0x2C0 #define AR_SREV_REVISION_9565_10 0 #define AR_SREV_VERSION_9550 0x400 #define AR_SREV_5416(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ ((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE)) #define AR_SREV_5416_22_OR_LATER(_ah) \ (((AR_SREV_5416(_ah)) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_5416_22)) || \ ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9100)) #define AR_SREV_9100(ah) \ ((ah->hw_version.macVersion) == AR_SREV_VERSION_9100) #define AR_SREV_9100_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9100)) #define AR_SREV_9160(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9160)) #define AR_SREV_9160_10_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9160)) #define AR_SREV_9160_11(_ah) \ (AR_SREV_9160(_ah) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9160_11)) #define AR_SREV_9280(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280)) #define AR_SREV_9280_20_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9280)) #define AR_SREV_9280_20(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9280)) #define AR_SREV_9285(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9285)) #define AR_SREV_9285_12_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9285)) #define AR_SREV_9287(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287)) #define AR_SREV_9287_11_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9287)) #define AR_SREV_9287_11(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_11)) #define AR_SREV_9287_12(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_12)) #define AR_SREV_9287_12_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9287) || \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9287_12))) #define AR_SREV_9287_13_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9287) || \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9287_13))) #define AR_SREV_9271(_ah) \ (((_ah))->hw_version.macVersion == AR_SREV_VERSION_9271) #define AR_SREV_9271_10(_ah) \ (AR_SREV_9271(_ah) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9271_10)) #define AR_SREV_9271_11(_ah) \ (AR_SREV_9271(_ah) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9271_11)) #define AR_SREV_9300(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9300)) #define AR_SREV_9300_20_OR_LATER(_ah) \ ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9300) #define AR_SREV_9330(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9330)) #define AR_SREV_9330_10(_ah) \ (AR_SREV_9330((_ah)) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_10)) #define AR_SREV_9330_11(_ah) \ (AR_SREV_9330((_ah)) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_11)) #define AR_SREV_9330_12(_ah) \ (AR_SREV_9330((_ah)) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_12)) #define AR_SREV_9485(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485)) #define AR_SREV_9485_10(_ah) \ (AR_SREV_9485(_ah) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_10)) #define AR_SREV_9485_11(_ah) \ (AR_SREV_9485(_ah) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11)) #define AR_SREV_9485_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485)) #define AR_SREV_9340(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9340)) #define AR_SREV_9285E_20(_ah) \ (AR_SREV_9285_12_OR_LATER(_ah) && \ ((REG_READ(_ah, AR_AN_SYNTH9) & 0x7) == 0x1)) #define AR_SREV_9462(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462)) #define AR_SREV_9462_20(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20)) #define AR_SREV_9462_20_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_20)) #define AR_SREV_9565(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565)) #define AR_SREV_9565_10(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_10)) #define AR_SREV_9550(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9550)) #define AR_SREV_9580(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9580_10)) #define AR_SREV_9580_10(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9580_10)) /* NOTE: When adding chips newer than Peacock, add chip check here */ #define AR_SREV_9580_10_OR_LATER(_ah) \ (AR_SREV_9580(_ah)) enum ath_usb_dev { AR9280_USB = 1, /* AR7010 + AR9280, UB94 */ AR9287_USB = 2, /* AR7010 + AR9287, UB95 */ STORAGE_DEVICE = 3, }; #define AR_DEVID_7010(_ah) \ (((_ah)->hw_version.usbdev == AR9280_USB) || \ ((_ah)->hw_version.usbdev == AR9287_USB)) #define AR_RADIO_SREV_MAJOR 0xf0 #define AR_RAD5133_SREV_MAJOR 0xc0 #define AR_RAD2133_SREV_MAJOR 0xd0 #define AR_RAD5122_SREV_MAJOR 0xe0 #define AR_RAD2122_SREV_MAJOR 0xf0 #define AR_AHB_MODE 0x4024 #define AR_AHB_EXACT_WR_EN 0x00000000 #define AR_AHB_BUF_WR_EN 0x00000001 #define AR_AHB_EXACT_RD_EN 0x00000000 #define AR_AHB_CACHELINE_RD_EN 0x00000002 #define AR_AHB_PREFETCH_RD_EN 0x00000004 #define AR_AHB_PAGE_SIZE_1K 0x00000000 #define AR_AHB_PAGE_SIZE_2K 0x00000008 #define AR_AHB_PAGE_SIZE_4K 0x00000010 #define AR_AHB_CUSTOM_BURST_EN 0x000000C0 #define AR_AHB_CUSTOM_BURST_EN_S 6 #define AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL 3 #define AR_INTR_RTC_IRQ 0x00000001 #define AR_INTR_MAC_IRQ 0x00000002 #define AR_INTR_EEP_PROT_ACCESS 0x00000004 #define AR_INTR_MAC_AWAKE 0x00020000 #define AR_INTR_MAC_ASLEEP 0x00040000 #define AR_INTR_SPURIOUS 0xFFFFFFFF #define AR_INTR_SYNC_CAUSE (AR_SREV_9340(ah) ? 0x4010 : 0x4028) #define AR_INTR_SYNC_CAUSE_CLR (AR_SREV_9340(ah) ? 0x4010 : 0x4028) #define AR_INTR_SYNC_ENABLE (AR_SREV_9340(ah) ? 0x4014 : 0x402c) #define AR_INTR_SYNC_ENABLE_GPIO 0xFFFC0000 #define AR_INTR_SYNC_ENABLE_GPIO_S 18 enum { AR_INTR_SYNC_RTC_IRQ = 0x00000001, AR_INTR_SYNC_MAC_IRQ = 0x00000002, AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS = 0x00000004, AR_INTR_SYNC_APB_TIMEOUT = 0x00000008, AR_INTR_SYNC_PCI_MODE_CONFLICT = 0x00000010, AR_INTR_SYNC_HOST1_FATAL = 0x00000020, AR_INTR_SYNC_HOST1_PERR = 0x00000040, AR_INTR_SYNC_TRCV_FIFO_PERR = 0x00000080, AR_INTR_SYNC_RADM_CPL_EP = 0x00000100, AR_INTR_SYNC_RADM_CPL_DLLP_ABORT = 0x00000200, AR_INTR_SYNC_RADM_CPL_TLP_ABORT = 0x00000400, AR_INTR_SYNC_RADM_CPL_ECRC_ERR = 0x00000800, AR_INTR_SYNC_RADM_CPL_TIMEOUT = 0x00001000, AR_INTR_SYNC_LOCAL_TIMEOUT = 0x00002000, AR_INTR_SYNC_PM_ACCESS = 0x00004000, AR_INTR_SYNC_MAC_AWAKE = 0x00008000, AR_INTR_SYNC_MAC_ASLEEP = 0x00010000, AR_INTR_SYNC_MAC_SLEEP_ACCESS = 0x00020000, AR_INTR_SYNC_ALL = 0x0003FFFF, AR_INTR_SYNC_DEFAULT = (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR | AR_INTR_SYNC_RADM_CPL_EP | AR_INTR_SYNC_RADM_CPL_DLLP_ABORT | AR_INTR_SYNC_RADM_CPL_TLP_ABORT | AR_INTR_SYNC_RADM_CPL_ECRC_ERR | AR_INTR_SYNC_RADM_CPL_TIMEOUT | AR_INTR_SYNC_LOCAL_TIMEOUT | AR_INTR_SYNC_MAC_SLEEP_ACCESS), AR_INTR_SYNC_SPURIOUS = 0xFFFFFFFF, }; #define AR_INTR_ASYNC_MASK (AR_SREV_9340(ah) ? 0x4018 : 0x4030) #define AR_INTR_ASYNC_MASK_GPIO 0xFFFC0000 #define AR_INTR_ASYNC_MASK_GPIO_S 18 #define AR_INTR_ASYNC_MASK_MCI 0x00000080 #define AR_INTR_ASYNC_MASK_MCI_S 7 #define AR_INTR_SYNC_MASK (AR_SREV_9340(ah) ? 0x401c : 0x4034) #define AR_INTR_SYNC_MASK_GPIO 0xFFFC0000 #define AR_INTR_SYNC_MASK_GPIO_S 18 #define AR_INTR_ASYNC_CAUSE_CLR (AR_SREV_9340(ah) ? 0x4020 : 0x4038) #define AR_INTR_ASYNC_CAUSE (AR_SREV_9340(ah) ? 0x4020 : 0x4038) #define AR_INTR_ASYNC_CAUSE_MCI 0x00000080 #define AR_INTR_ASYNC_USED (AR_INTR_MAC_IRQ | \ AR_INTR_ASYNC_CAUSE_MCI) /* Asynchronous Interrupt Enable Register */ #define AR_INTR_ASYNC_ENABLE_MCI 0x00000080 #define AR_INTR_ASYNC_ENABLE_MCI_S 7 #define AR_INTR_ASYNC_ENABLE (AR_SREV_9340(ah) ? 0x4024 : 0x403c) #define AR_INTR_ASYNC_ENABLE_GPIO 0xFFFC0000 #define AR_INTR_ASYNC_ENABLE_GPIO_S 18 #define AR_PCIE_SERDES 0x4040 #define AR_PCIE_SERDES2 0x4044 #define AR_PCIE_PM_CTRL (AR_SREV_9340(ah) ? 0x4004 : 0x4014) #define AR_PCIE_PM_CTRL_ENA 0x00080000 #define AR_PCIE_PHY_REG3 0x18c08 #define AR_NUM_GPIO 14 #define AR928X_NUM_GPIO 10 #define AR9285_NUM_GPIO 12 #define AR9287_NUM_GPIO 11 #define AR9271_NUM_GPIO 16 #define AR9300_NUM_GPIO 17 #define AR7010_NUM_GPIO 16 #define AR_GPIO_IN_OUT (AR_SREV_9340(ah) ? 0x4028 : 0x4048) #define AR_GPIO_IN_VAL 0x0FFFC000 #define AR_GPIO_IN_VAL_S 14 #define AR928X_GPIO_IN_VAL 0x000FFC00 #define AR928X_GPIO_IN_VAL_S 10 #define AR9285_GPIO_IN_VAL 0x00FFF000 #define AR9285_GPIO_IN_VAL_S 12 #define AR9287_GPIO_IN_VAL 0x003FF800 #define AR9287_GPIO_IN_VAL_S 11 #define AR9271_GPIO_IN_VAL 0xFFFF0000 #define AR9271_GPIO_IN_VAL_S 16 #define AR7010_GPIO_IN_VAL 0x0000FFFF #define AR7010_GPIO_IN_VAL_S 0 #define AR_GPIO_IN (AR_SREV_9340(ah) ? 0x402c : 0x404c) #define AR9300_GPIO_IN_VAL 0x0001FFFF #define AR9300_GPIO_IN_VAL_S 0 #define AR_GPIO_OE_OUT (AR_SREV_9340(ah) ? 0x4030 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4050 : 0x404c)) #define AR_GPIO_OE_OUT_DRV 0x3 #define AR_GPIO_OE_OUT_DRV_NO 0x0 #define AR_GPIO_OE_OUT_DRV_LOW 0x1 #define AR_GPIO_OE_OUT_DRV_HI 0x2 #define AR_GPIO_OE_OUT_DRV_ALL 0x3 #define AR7010_GPIO_OE 0x52000 #define AR7010_GPIO_OE_MASK 0x1 #define AR7010_GPIO_OE_AS_OUTPUT 0x0 #define AR7010_GPIO_OE_AS_INPUT 0x1 #define AR7010_GPIO_IN 0x52004 #define AR7010_GPIO_OUT 0x52008 #define AR7010_GPIO_SET 0x5200C #define AR7010_GPIO_CLEAR 0x52010 #define AR7010_GPIO_INT 0x52014 #define AR7010_GPIO_INT_TYPE 0x52018 #define AR7010_GPIO_INT_POLARITY 0x5201C #define AR7010_GPIO_PENDING 0x52020 #define AR7010_GPIO_INT_MASK 0x52024 #define AR7010_GPIO_FUNCTION 0x52028 #define AR_GPIO_INTR_POL (AR_SREV_9340(ah) ? 0x4038 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4058 : 0x4050)) #define AR_GPIO_INTR_POL_VAL 0x0001FFFF #define AR_GPIO_INTR_POL_VAL_S 0 #define AR_GPIO_INPUT_EN_VAL (AR_SREV_9340(ah) ? 0x403c : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x405c : 0x4054)) #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF 0x00000004 #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_S 2 #define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF 0x00000008 #define AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_S 3 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_DEF 0x00000010 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_S 4 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7 #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00000400 #define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 10 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB 0x00001000 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S 12 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15 #define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000 #define AR_GPIO_JTAG_DISABLE 0x00020000 #define AR_GPIO_INPUT_MUX1 (AR_SREV_9340(ah) ? 0x4040 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4060 : 0x4058)) #define AR_GPIO_INPUT_MUX1_BT_ACTIVE 0x000f0000 #define AR_GPIO_INPUT_MUX1_BT_ACTIVE_S 16 #define AR_GPIO_INPUT_MUX1_BT_PRIORITY 0x00000f00 #define AR_GPIO_INPUT_MUX1_BT_PRIORITY_S 8 #define AR_GPIO_INPUT_MUX2 (AR_SREV_9340(ah) ? 0x4044 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4064 : 0x405c)) #define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f #define AR_GPIO_INPUT_MUX2_CLK25_S 0 #define AR_GPIO_INPUT_MUX2_RFSILENT 0x000000f0 #define AR_GPIO_INPUT_MUX2_RFSILENT_S 4 #define AR_GPIO_INPUT_MUX2_RTC_RESET 0x00000f00 #define AR_GPIO_INPUT_MUX2_RTC_RESET_S 8 #define AR_GPIO_OUTPUT_MUX1 (AR_SREV_9340(ah) ? 0x4048 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4068 : 0x4060)) #define AR_GPIO_OUTPUT_MUX2 (AR_SREV_9340(ah) ? 0x404c : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x406c : 0x4064)) #define AR_GPIO_OUTPUT_MUX3 (AR_SREV_9340(ah) ? 0x4050 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4070 : 0x4068)) #define AR_INPUT_STATE (AR_SREV_9340(ah) ? 0x4054 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4074 : 0x406c)) #define AR_EEPROM_STATUS_DATA (AR_SREV_9340(ah) ? 0x40c8 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4084 : 0x407c)) #define AR_EEPROM_STATUS_DATA_VAL 0x0000ffff #define AR_EEPROM_STATUS_DATA_VAL_S 0 #define AR_EEPROM_STATUS_DATA_BUSY 0x00010000 #define AR_EEPROM_STATUS_DATA_BUSY_ACCESS 0x00020000 #define AR_EEPROM_STATUS_DATA_PROT_ACCESS 0x00040000 #define AR_EEPROM_STATUS_DATA_ABSENT_ACCESS 0x00080000 #define AR_OBS (AR_SREV_9340(ah) ? 0x405c : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4088 : 0x4080)) #define AR_GPIO_PDPU (AR_SREV_9300_20_OR_LATER(ah) ? 0x4090 : 0x4088) #define AR_PCIE_MSI (AR_SREV_9340(ah) ? 0x40d8 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x40a4 : 0x4094)) #define AR_PCIE_MSI_ENABLE 0x00000001 #define AR_INTR_PRIO_SYNC_ENABLE (AR_SREV_9340(ah) ? 0x4088 : 0x40c4) #define AR_INTR_PRIO_ASYNC_MASK (AR_SREV_9340(ah) ? 0x408c : 0x40c8) #define AR_INTR_PRIO_SYNC_MASK (AR_SREV_9340(ah) ? 0x4090 : 0x40cc) #define AR_INTR_PRIO_ASYNC_ENABLE (AR_SREV_9340(ah) ? 0x4094 : 0x40d4) #define AR_ENT_OTP 0x40d8 #define AR_ENT_OTP_CHAIN2_DISABLE 0x00020000 #define AR_ENT_OTP_49GHZ_DISABLE 0x00100000 #define AR_ENT_OTP_MIN_PKT_SIZE_DISABLE 0x00800000 #define AR_CH0_BB_DPLL1 0x16180 #define AR_CH0_BB_DPLL1_REFDIV 0xF8000000 #define AR_CH0_BB_DPLL1_REFDIV_S 27 #define AR_CH0_BB_DPLL1_NINI 0x07FC0000 #define AR_CH0_BB_DPLL1_NINI_S 18 #define AR_CH0_BB_DPLL1_NFRAC 0x0003FFFF #define AR_CH0_BB_DPLL1_NFRAC_S 0 #define AR_CH0_BB_DPLL2 0x16184 #define AR_CH0_BB_DPLL2_LOCAL_PLL 0x40000000 #define AR_CH0_BB_DPLL2_LOCAL_PLL_S 30 #define AR_CH0_DPLL2_KI 0x3C000000 #define AR_CH0_DPLL2_KI_S 26 #define AR_CH0_DPLL2_KD 0x03F80000 #define AR_CH0_DPLL2_KD_S 19 #define AR_CH0_BB_DPLL2_EN_NEGTRIG 0x00040000 #define AR_CH0_BB_DPLL2_EN_NEGTRIG_S 18 #define AR_CH0_BB_DPLL2_PLL_PWD 0x00010000 #define AR_CH0_BB_DPLL2_PLL_PWD_S 16 #define AR_CH0_BB_DPLL2_OUTDIV 0x0000E000 #define AR_CH0_BB_DPLL2_OUTDIV_S 13 #define AR_CH0_BB_DPLL3 0x16188 #define AR_CH0_BB_DPLL3_PHASE_SHIFT 0x3F800000 #define AR_CH0_BB_DPLL3_PHASE_SHIFT_S 23 #define AR_CH0_DDR_DPLL2 0x16244 #define AR_CH0_DDR_DPLL3 0x16248 #define AR_CH0_DPLL3_PHASE_SHIFT 0x3F800000 #define AR_CH0_DPLL3_PHASE_SHIFT_S 23 #define AR_PHY_CCA_NOM_VAL_2GHZ -118 #define AR_RTC_9300_PLL_DIV 0x000003ff #define AR_RTC_9300_PLL_DIV_S 0 #define AR_RTC_9300_PLL_REFDIV 0x00003C00 #define AR_RTC_9300_PLL_REFDIV_S 10 #define AR_RTC_9300_PLL_CLKSEL 0x0000C000 #define AR_RTC_9300_PLL_CLKSEL_S 14 #define AR_RTC_9160_PLL_DIV 0x000003ff #define AR_RTC_9160_PLL_DIV_S 0 #define AR_RTC_9160_PLL_REFDIV 0x00003C00 #define AR_RTC_9160_PLL_REFDIV_S 10 #define AR_RTC_9160_PLL_CLKSEL 0x0000C000 #define AR_RTC_9160_PLL_CLKSEL_S 14 #define AR_RTC_BASE 0x00020000 #define AR_RTC_RC \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0000) : 0x7000) #define AR_RTC_RC_M 0x00000003 #define AR_RTC_RC_MAC_WARM 0x00000001 #define AR_RTC_RC_MAC_COLD 0x00000002 #define AR_RTC_RC_COLD_RESET 0x00000004 #define AR_RTC_RC_WARM_RESET 0x00000008 /* Crystal Control */ #define AR_RTC_XTAL_CONTROL 0x7004 /* Reg Control 0 */ #define AR_RTC_REG_CONTROL0 0x7008 /* Reg Control 1 */ #define AR_RTC_REG_CONTROL1 0x700c #define AR_RTC_REG_CONTROL1_SWREG_PROGRAM 0x00000001 #define AR_RTC_PLL_CONTROL \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0014) : 0x7014) #define AR_RTC_PLL_CONTROL2 0x703c #define AR_RTC_PLL_DIV 0x0000001f #define AR_RTC_PLL_DIV_S 0 #define AR_RTC_PLL_DIV2 0x00000020 #define AR_RTC_PLL_REFDIV_5 0x000000c0 #define AR_RTC_PLL_CLKSEL 0x00000300 #define AR_RTC_PLL_CLKSEL_S 8 #define AR_RTC_PLL_BYPASS 0x00010000 #define AR_RTC_PLL_NOPWD 0x00040000 #define AR_RTC_PLL_NOPWD_S 18 #define PLL3 0x16188 #define PLL3_DO_MEAS_MASK 0x40000000 #define PLL4 0x1618c #define PLL4_MEAS_DONE 0x8 #define SQSUM_DVC_MASK 0x007ffff8 #define AR_RTC_RESET \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0040) : 0x7040) #define AR_RTC_RESET_EN (0x00000001) #define AR_RTC_STATUS \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0044) : 0x7044) #define AR_RTC_STATUS_M \ ((AR_SREV_9100(ah)) ? 0x0000003f : 0x0000000f) #define AR_RTC_PM_STATUS_M 0x0000000f #define AR_RTC_STATUS_SHUTDOWN 0x00000001 #define AR_RTC_STATUS_ON 0x00000002 #define AR_RTC_STATUS_SLEEP 0x00000004 #define AR_RTC_STATUS_WAKEUP 0x00000008 #define AR_RTC_SLEEP_CLK \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0048) : 0x7048) #define AR_RTC_FORCE_DERIVED_CLK 0x2 #define AR_RTC_FORCE_SWREG_PRD 0x00000004 #define AR_RTC_FORCE_WAKE \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x004c) : 0x704c) #define AR_RTC_FORCE_WAKE_EN 0x00000001 #define AR_RTC_FORCE_WAKE_ON_INT 0x00000002 #define AR_RTC_INTR_CAUSE \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0050) : 0x7050) #define AR_RTC_INTR_ENABLE \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0054) : 0x7054) #define AR_RTC_INTR_MASK \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0058) : 0x7058) #define AR_RTC_KEEP_AWAKE 0x7034 /* RTC_DERIVED_* - only for AR9100 */ #define AR_RTC_DERIVED_CLK \ (AR_SREV_9100(ah) ? (AR_RTC_BASE + 0x0038) : 0x7038) #define AR_RTC_DERIVED_CLK_PERIOD 0x0000fffe #define AR_RTC_DERIVED_CLK_PERIOD_S 1 #define AR_SEQ_MASK 0x8060 #define AR_AN_RF2G1_CH0 0x7810 #define AR_AN_RF2G1_CH0_OB 0x03800000 #define AR_AN_RF2G1_CH0_OB_S 23 #define AR_AN_RF2G1_CH0_DB 0x1C000000 #define AR_AN_RF2G1_CH0_DB_S 26 #define AR_AN_RF5G1_CH0 0x7818 #define AR_AN_RF5G1_CH0_OB5 0x00070000 #define AR_AN_RF5G1_CH0_OB5_S 16 #define AR_AN_RF5G1_CH0_DB5 0x00380000 #define AR_AN_RF5G1_CH0_DB5_S 19 #define AR_AN_RF2G1_CH1 0x7834 #define AR_AN_RF2G1_CH1_OB 0x03800000 #define AR_AN_RF2G1_CH1_OB_S 23 #define AR_AN_RF2G1_CH1_DB 0x1C000000 #define AR_AN_RF2G1_CH1_DB_S 26 #define AR_AN_RF5G1_CH1 0x783C #define AR_AN_RF5G1_CH1_OB5 0x00070000 #define AR_AN_RF5G1_CH1_OB5_S 16 #define AR_AN_RF5G1_CH1_DB5 0x00380000 #define AR_AN_RF5G1_CH1_DB5_S 19 #define AR_AN_TOP1 0x7890 #define AR_AN_TOP1_DACIPMODE 0x00040000 #define AR_AN_TOP1_DACIPMODE_S 18 #define AR_AN_TOP2 0x7894 #define AR_AN_TOP2_XPABIAS_LVL 0xC0000000 #define AR_AN_TOP2_XPABIAS_LVL_S 30 #define AR_AN_TOP2_LOCALBIAS 0x00200000 #define AR_AN_TOP2_LOCALBIAS_S 21 #define AR_AN_TOP2_PWDCLKIND 0x00400000 #define AR_AN_TOP2_PWDCLKIND_S 22 #define AR_AN_SYNTH9 0x7868 #define AR_AN_SYNTH9_REFDIVA 0xf8000000 #define AR_AN_SYNTH9_REFDIVA_S 27 #define AR9285_AN_RF2G1 0x7820 #define AR9285_AN_RF2G1_ENPACAL 0x00000800 #define AR9285_AN_RF2G1_ENPACAL_S 11 #define AR9285_AN_RF2G1_PDPADRV1 0x02000000 #define AR9285_AN_RF2G1_PDPADRV1_S 25 #define AR9285_AN_RF2G1_PDPADRV2 0x01000000 #define AR9285_AN_RF2G1_PDPADRV2_S 24 #define AR9285_AN_RF2G1_PDPAOUT 0x00800000 #define AR9285_AN_RF2G1_PDPAOUT_S 23 #define AR9285_AN_RF2G2 0x7824 #define AR9285_AN_RF2G2_OFFCAL 0x00001000 #define AR9285_AN_RF2G2_OFFCAL_S 12 #define AR9285_AN_RF2G3 0x7828 #define AR9285_AN_RF2G3_PDVCCOMP 0x02000000 #define AR9285_AN_RF2G3_PDVCCOMP_S 25 #define AR9285_AN_RF2G3_OB_0 0x00E00000 #define AR9285_AN_RF2G3_OB_0_S 21 #define AR9285_AN_RF2G3_OB_1 0x001C0000 #define AR9285_AN_RF2G3_OB_1_S 18 #define AR9285_AN_RF2G3_OB_2 0x00038000 #define AR9285_AN_RF2G3_OB_2_S 15 #define AR9285_AN_RF2G3_OB_3 0x00007000 #define AR9285_AN_RF2G3_OB_3_S 12 #define AR9285_AN_RF2G3_OB_4 0x00000E00 #define AR9285_AN_RF2G3_OB_4_S 9 #define AR9285_AN_RF2G3_DB1_0 0x000001C0 #define AR9285_AN_RF2G3_DB1_0_S 6 #define AR9285_AN_RF2G3_DB1_1 0x00000038 #define AR9285_AN_RF2G3_DB1_1_S 3 #define AR9285_AN_RF2G3_DB1_2 0x00000007 #define AR9285_AN_RF2G3_DB1_2_S 0 #define AR9285_AN_RF2G4 0x782C #define AR9285_AN_RF2G4_DB1_3 0xE0000000 #define AR9285_AN_RF2G4_DB1_3_S 29 #define AR9285_AN_RF2G4_DB1_4 0x1C000000 #define AR9285_AN_RF2G4_DB1_4_S 26 #define AR9285_AN_RF2G4_DB2_0 0x03800000 #define AR9285_AN_RF2G4_DB2_0_S 23 #define AR9285_AN_RF2G4_DB2_1 0x00700000 #define AR9285_AN_RF2G4_DB2_1_S 20 #define AR9285_AN_RF2G4_DB2_2 0x000E0000 #define AR9285_AN_RF2G4_DB2_2_S 17 #define AR9285_AN_RF2G4_DB2_3 0x0001C000 #define AR9285_AN_RF2G4_DB2_3_S 14 #define AR9285_AN_RF2G4_DB2_4 0x00003800 #define AR9285_AN_RF2G4_DB2_4_S 11 #define AR9285_RF2G5 0x7830 #define AR9285_RF2G5_IC50TX 0xfffff8ff #define AR9285_RF2G5_IC50TX_SET 0x00000400 #define AR9285_RF2G5_IC50TX_XE_SET 0x00000500 #define AR9285_RF2G5_IC50TX_CLEAR 0x00000700 #define AR9285_RF2G5_IC50TX_CLEAR_S 8 /* AR9271 : 0x7828, 0x782c different setting from AR9285 */ #define AR9271_AN_RF2G3_OB_cck 0x001C0000 #define AR9271_AN_RF2G3_OB_cck_S 18 #define AR9271_AN_RF2G3_OB_psk 0x00038000 #define AR9271_AN_RF2G3_OB_psk_S 15 #define AR9271_AN_RF2G3_OB_qam 0x00007000 #define AR9271_AN_RF2G3_OB_qam_S 12 #define AR9271_AN_RF2G3_DB_1 0x00E00000 #define AR9271_AN_RF2G3_DB_1_S 21 #define AR9271_AN_RF2G3_CCOMP 0xFFF #define AR9271_AN_RF2G3_CCOMP_S 0 #define AR9271_AN_RF2G4_DB_2 0xE0000000 #define AR9271_AN_RF2G4_DB_2_S 29 #define AR9285_AN_RF2G6 0x7834 #define AR9285_AN_RF2G6_CCOMP 0x00007800 #define AR9285_AN_RF2G6_CCOMP_S 11 #define AR9285_AN_RF2G6_OFFS 0x03f00000 #define AR9285_AN_RF2G6_OFFS_S 20 #define AR9271_AN_RF2G6_OFFS 0x07f00000 #define AR9271_AN_RF2G6_OFFS_S 20 #define AR9285_AN_RF2G7 0x7838 #define AR9285_AN_RF2G7_PWDDB 0x00000002 #define AR9285_AN_RF2G7_PWDDB_S 1 #define AR9285_AN_RF2G7_PADRVGN2TAB0 0xE0000000 #define AR9285_AN_RF2G7_PADRVGN2TAB0_S 29 #define AR9285_AN_RF2G8 0x783C #define AR9285_AN_RF2G8_PADRVGN2TAB0 0x0001C000 #define AR9285_AN_RF2G8_PADRVGN2TAB0_S 14 #define AR9285_AN_RF2G9 0x7840 #define AR9285_AN_RXTXBB1 0x7854 #define AR9285_AN_RXTXBB1_PDRXTXBB1 0x00000020 #define AR9285_AN_RXTXBB1_PDRXTXBB1_S 5 #define AR9285_AN_RXTXBB1_PDV2I 0x00000080 #define AR9285_AN_RXTXBB1_PDV2I_S 7 #define AR9285_AN_RXTXBB1_PDDACIF 0x00000100 #define AR9285_AN_RXTXBB1_PDDACIF_S 8 #define AR9285_AN_RXTXBB1_SPARE9 0x00000001 #define AR9285_AN_RXTXBB1_SPARE9_S 0 #define AR9285_AN_TOP2 0x7868 #define AR9285_AN_TOP3 0x786c #define AR9285_AN_TOP3_XPABIAS_LVL 0x0000000C #define AR9285_AN_TOP3_XPABIAS_LVL_S 2 #define AR9285_AN_TOP3_PWDDAC 0x00800000 #define AR9285_AN_TOP3_PWDDAC_S 23 #define AR9285_AN_TOP4 0x7870 #define AR9285_AN_TOP4_DEFAULT 0x10142c00 #define AR9287_AN_RF2G3_CH0 0x7808 #define AR9287_AN_RF2G3_CH1 0x785c #define AR9287_AN_RF2G3_DB1 0xE0000000 #define AR9287_AN_RF2G3_DB1_S 29 #define AR9287_AN_RF2G3_DB2 0x1C000000 #define AR9287_AN_RF2G3_DB2_S 26 #define AR9287_AN_RF2G3_OB_CCK 0x03800000 #define AR9287_AN_RF2G3_OB_CCK_S 23 #define AR9287_AN_RF2G3_OB_PSK 0x00700000 #define AR9287_AN_RF2G3_OB_PSK_S 20 #define AR9287_AN_RF2G3_OB_QAM 0x000E0000 #define AR9287_AN_RF2G3_OB_QAM_S 17 #define AR9287_AN_RF2G3_OB_PAL_OFF 0x0001C000 #define AR9287_AN_RF2G3_OB_PAL_OFF_S 14 #define AR9287_AN_TXPC0 0x7898 #define AR9287_AN_TXPC0_TXPCMODE 0x0000C000 #define AR9287_AN_TXPC0_TXPCMODE_S 14 #define AR9287_AN_TXPC0_TXPCMODE_NORMAL 0 #define AR9287_AN_TXPC0_TXPCMODE_TEST 1 #define AR9287_AN_TXPC0_TXPCMODE_TEMPSENSE 2 #define AR9287_AN_TXPC0_TXPCMODE_ATBTEST 3 #define AR9287_AN_TOP2 0x78b4 #define AR9287_AN_TOP2_XPABIAS_LVL 0xC0000000 #define AR9287_AN_TOP2_XPABIAS_LVL_S 30 /* AR9271 specific stuff */ #define AR9271_RESET_POWER_DOWN_CONTROL 0x50044 #define AR9271_RADIO_RF_RST 0x20 #define AR9271_GATE_MAC_CTL 0x4000 #define AR_STA_ID0 0x8000 #define AR_STA_ID1 0x8004 #define AR_STA_ID1_SADH_MASK 0x0000FFFF #define AR_STA_ID1_STA_AP 0x00010000 #define AR_STA_ID1_ADHOC 0x00020000 #define AR_STA_ID1_PWR_SAV 0x00040000 #define AR_STA_ID1_KSRCHDIS 0x00080000 #define AR_STA_ID1_PCF 0x00100000 #define AR_STA_ID1_USE_DEFANT 0x00200000 #define AR_STA_ID1_DEFANT_UPDATE 0x00400000 #define AR_STA_ID1_AR9100_BA_FIX 0x00400000 #define AR_STA_ID1_RTS_USE_DEF 0x00800000 #define AR_STA_ID1_ACKCTS_6MB 0x01000000 #define AR_STA_ID1_BASE_RATE_11B 0x02000000 #define AR_STA_ID1_SECTOR_SELF_GEN 0x04000000 #define AR_STA_ID1_CRPT_MIC_ENABLE 0x08000000 #define AR_STA_ID1_KSRCH_MODE 0x10000000 #define AR_STA_ID1_PRESERVE_SEQNUM 0x20000000 #define AR_STA_ID1_CBCIV_ENDIAN 0x40000000 #define AR_STA_ID1_MCAST_KSRCH 0x80000000 #define AR_BSS_ID0 0x8008 #define AR_BSS_ID1 0x800C #define AR_BSS_ID1_U16 0x0000FFFF #define AR_BSS_ID1_AID 0x07FF0000 #define AR_BSS_ID1_AID_S 16 #define AR_BCN_RSSI_AVE 0x8010 #define AR_BCN_RSSI_AVE_MASK 0x00000FFF #define AR_TIME_OUT 0x8014 #define AR_TIME_OUT_ACK 0x00003FFF #define AR_TIME_OUT_ACK_S 0 #define AR_TIME_OUT_CTS 0x3FFF0000 #define AR_TIME_OUT_CTS_S 16 #define AR_RSSI_THR 0x8018 #define AR_RSSI_THR_MASK 0x000000FF #define AR_RSSI_THR_BM_THR 0x0000FF00 #define AR_RSSI_THR_BM_THR_S 8 #define AR_RSSI_BCN_WEIGHT 0x1F000000 #define AR_RSSI_BCN_WEIGHT_S 24 #define AR_RSSI_BCN_RSSI_RST 0x20000000 #define AR_USEC 0x801c #define AR_USEC_USEC 0x0000007F #define AR_USEC_TX_LAT 0x007FC000 #define AR_USEC_TX_LAT_S 14 #define AR_USEC_RX_LAT 0x1F800000 #define AR_USEC_RX_LAT_S 23 #define AR_USEC_ASYNC_FIFO 0x12E00074 #define AR_RESET_TSF 0x8020 #define AR_RESET_TSF_ONCE 0x01000000 #define AR_MAX_CFP_DUR 0x8038 #define AR_CFP_VAL 0x0000FFFF #define AR_RX_FILTER 0x803C #define AR_MCAST_FIL0 0x8040 #define AR_MCAST_FIL1 0x8044 /* * AR_DIAG_SW - Register which can be used for diagnostics and testing purposes. * * The force RX abort (AR_DIAG_RX_ABORT, bit 25) can be used in conjunction with * RX block (AR_DIAG_RX_DIS, bit 5) to help fast channel change to shut down * receive. The force RX abort bit will kill any frame which is currently being * transferred between the MAC and baseband. The RX block bit (AR_DIAG_RX_DIS) * will prevent any new frames from getting started. */ #define AR_DIAG_SW 0x8048 #define AR_DIAG_CACHE_ACK 0x00000001 #define AR_DIAG_ACK_DIS 0x00000002 #define AR_DIAG_CTS_DIS 0x00000004 #define AR_DIAG_ENCRYPT_DIS 0x00000008 #define AR_DIAG_DECRYPT_DIS 0x00000010 #define AR_DIAG_RX_DIS 0x00000020 /* RX block */ #define AR_DIAG_LOOP_BACK 0x00000040 #define AR_DIAG_CORR_FCS 0x00000080 #define AR_DIAG_CHAN_INFO 0x00000100 #define AR_DIAG_SCRAM_SEED 0x0001FE00 #define AR_DIAG_SCRAM_SEED_S 8 #define AR_DIAG_FRAME_NV0 0x00020000 #define AR_DIAG_OBS_PT_SEL1 0x000C0000 #define AR_DIAG_OBS_PT_SEL1_S 18 #define AR_DIAG_OBS_PT_SEL2 0x08000000 #define AR_DIAG_OBS_PT_SEL2_S 27 #define AR_DIAG_FORCE_RX_CLEAR 0x00100000 /* force rx_clear high */ #define AR_DIAG_IGNORE_VIRT_CS 0x00200000 #define AR_DIAG_FORCE_CH_IDLE_HIGH 0x00400000 #define AR_DIAG_EIFS_CTRL_ENA 0x00800000 #define AR_DIAG_DUAL_CHAIN_INFO 0x01000000 #define AR_DIAG_RX_ABORT 0x02000000 /* Force RX abort */ #define AR_DIAG_SATURATE_CYCLE_CNT 0x04000000 #define AR_DIAG_OBS_PT_SEL2 0x08000000 #define AR_DIAG_RX_CLEAR_CTL_LOW 0x10000000 #define AR_DIAG_RX_CLEAR_EXT_LOW 0x20000000 #define AR_TSF_L32 0x804c #define AR_TSF_U32 0x8050 #define AR_TST_ADDAC 0x8054 #define AR_DEF_ANTENNA 0x8058 #define AR_AES_MUTE_MASK0 0x805c #define AR_AES_MUTE_MASK0_FC 0x0000FFFF #define AR_AES_MUTE_MASK0_QOS 0xFFFF0000 #define AR_AES_MUTE_MASK0_QOS_S 16 #define AR_AES_MUTE_MASK1 0x8060 #define AR_AES_MUTE_MASK1_SEQ 0x0000FFFF #define AR_AES_MUTE_MASK1_FC_MGMT 0xFFFF0000 #define AR_AES_MUTE_MASK1_FC_MGMT_S 16 #define AR_GATED_CLKS 0x8064 #define AR_GATED_CLKS_TX 0x00000002 #define AR_GATED_CLKS_RX 0x00000004 #define AR_GATED_CLKS_REG 0x00000008 #define AR_OBS_BUS_CTRL 0x8068 #define AR_OBS_BUS_SEL_1 0x00040000 #define AR_OBS_BUS_SEL_2 0x00080000 #define AR_OBS_BUS_SEL_3 0x000C0000 #define AR_OBS_BUS_SEL_4 0x08040000 #define AR_OBS_BUS_SEL_5 0x08080000 #define AR_OBS_BUS_1 0x806c #define AR_OBS_BUS_1_PCU 0x00000001 #define AR_OBS_BUS_1_RX_END 0x00000002 #define AR_OBS_BUS_1_RX_WEP 0x00000004 #define AR_OBS_BUS_1_RX_BEACON 0x00000008 #define AR_OBS_BUS_1_RX_FILTER 0x00000010 #define AR_OBS_BUS_1_TX_HCF 0x00000020 #define AR_OBS_BUS_1_QUIET_TIME 0x00000040 #define AR_OBS_BUS_1_CHAN_IDLE 0x00000080 #define AR_OBS_BUS_1_TX_HOLD 0x00000100 #define AR_OBS_BUS_1_TX_FRAME 0x00000200 #define AR_OBS_BUS_1_RX_FRAME 0x00000400 #define AR_OBS_BUS_1_RX_CLEAR 0x00000800 #define AR_OBS_BUS_1_WEP_STATE 0x0003F000 #define AR_OBS_BUS_1_WEP_STATE_S 12 #define AR_OBS_BUS_1_RX_STATE 0x01F00000 #define AR_OBS_BUS_1_RX_STATE_S 20 #define AR_OBS_BUS_1_TX_STATE 0x7E000000 #define AR_OBS_BUS_1_TX_STATE_S 25 #define AR_LAST_TSTP 0x8080 #define AR_NAV 0x8084 #define AR_RTS_OK 0x8088 #define AR_RTS_FAIL 0x808c #define AR_ACK_FAIL 0x8090 #define AR_FCS_FAIL 0x8094 #define AR_BEACON_CNT 0x8098 #define AR_SLEEP1 0x80d4 #define AR_SLEEP1_ASSUME_DTIM 0x00080000 #define AR_SLEEP1_CAB_TIMEOUT 0xFFE00000 #define AR_SLEEP1_CAB_TIMEOUT_S 21 #define AR_SLEEP2 0x80d8 #define AR_SLEEP2_BEACON_TIMEOUT 0xFFE00000 #define AR_SLEEP2_BEACON_TIMEOUT_S 21 #define AR_TPC 0x80e8 #define AR_TPC_ACK 0x0000003f #define AR_TPC_ACK_S 0 #define AR_TPC_CTS 0x00003f00 #define AR_TPC_CTS_S 8 #define AR_TPC_CHIRP 0x003f0000 #define AR_TPC_CHIRP_S 16 #define AR_QUIET1 0x80fc #define AR_QUIET1_NEXT_QUIET_S 0 #define AR_QUIET1_NEXT_QUIET_M 0x0000ffff #define AR_QUIET1_QUIET_ENABLE 0x00010000 #define AR_QUIET1_QUIET_ACK_CTS_ENABLE 0x00020000 #define AR_QUIET1_QUIET_ACK_CTS_ENABLE_S 17 #define AR_QUIET2 0x8100 #define AR_QUIET2_QUIET_PERIOD_S 0 #define AR_QUIET2_QUIET_PERIOD_M 0x0000ffff #define AR_QUIET2_QUIET_DUR_S 16 #define AR_QUIET2_QUIET_DUR 0xffff0000 #define AR_TSF_PARM 0x8104 #define AR_TSF_INCREMENT_M 0x000000ff #define AR_TSF_INCREMENT_S 0x00 #define AR_QOS_NO_ACK 0x8108 #define AR_QOS_NO_ACK_TWO_BIT 0x0000000f #define AR_QOS_NO_ACK_TWO_BIT_S 0 #define AR_QOS_NO_ACK_BIT_OFF 0x00000070 #define AR_QOS_NO_ACK_BIT_OFF_S 4 #define AR_QOS_NO_ACK_BYTE_OFF 0x00000180 #define AR_QOS_NO_ACK_BYTE_OFF_S 7 #define AR_PHY_ERR 0x810c #define AR_PHY_ERR_DCHIRP 0x00000008 #define AR_PHY_ERR_RADAR 0x00000020 #define AR_PHY_ERR_OFDM_TIMING 0x00020000 #define AR_PHY_ERR_CCK_TIMING 0x02000000 #define AR_RXFIFO_CFG 0x8114 #define AR_MIC_QOS_CONTROL 0x8118 #define AR_MIC_QOS_SELECT 0x811c #define AR_PCU_MISC 0x8120 #define AR_PCU_FORCE_BSSID_MATCH 0x00000001 #define AR_PCU_MIC_NEW_LOC_ENA 0x00000004 #define AR_PCU_TX_ADD_TSF 0x00000008 #define AR_PCU_CCK_SIFS_MODE 0x00000010 #define AR_PCU_RX_ANT_UPDT 0x00000800 #define AR_PCU_TXOP_TBTT_LIMIT_ENA 0x00001000 #define AR_PCU_MISS_BCN_IN_SLEEP 0x00004000 #define AR_PCU_BUG_12306_FIX_ENA 0x00020000 #define AR_PCU_FORCE_QUIET_COLL 0x00040000 #define AR_PCU_TBTT_PROTECT 0x00200000 #define AR_PCU_CLEAR_VMF 0x01000000 #define AR_PCU_CLEAR_BA_VALID 0x04000000 #define AR_PCU_ALWAYS_PERFORM_KEYSEARCH 0x10000000 #define AR_PCU_BT_ANT_PREVENT_RX 0x00100000 #define AR_PCU_BT_ANT_PREVENT_RX_S 20 #define AR_FILT_OFDM 0x8124 #define AR_FILT_OFDM_COUNT 0x00FFFFFF #define AR_FILT_CCK 0x8128 #define AR_FILT_CCK_COUNT 0x00FFFFFF #define AR_PHY_ERR_1 0x812c #define AR_PHY_ERR_1_COUNT 0x00FFFFFF #define AR_PHY_ERR_MASK_1 0x8130 #define AR_PHY_ERR_2 0x8134 #define AR_PHY_ERR_2_COUNT 0x00FFFFFF #define AR_PHY_ERR_MASK_2 0x8138 #define AR_PHY_COUNTMAX (3 << 22) #define AR_MIBCNT_INTRMASK (3 << 22) #define AR_TSFOOR_THRESHOLD 0x813c #define AR_TSFOOR_THRESHOLD_VAL 0x0000FFFF #define AR_PHY_ERR_EIFS_MASK 0x8144 #define AR_PHY_ERR_3 0x8168 #define AR_PHY_ERR_3_COUNT 0x00FFFFFF #define AR_PHY_ERR_MASK_3 0x816c #define AR_BT_COEX_MODE 0x8170 #define AR_BT_TIME_EXTEND 0x000000ff #define AR_BT_TIME_EXTEND_S 0 #define AR_BT_TXSTATE_EXTEND 0x00000100 #define AR_BT_TXSTATE_EXTEND_S 8 #define AR_BT_TX_FRAME_EXTEND 0x00000200 #define AR_BT_TX_FRAME_EXTEND_S 9 #define AR_BT_MODE 0x00000c00 #define AR_BT_MODE_S 10 #define AR_BT_QUIET 0x00001000 #define AR_BT_QUIET_S 12 #define AR_BT_QCU_THRESH 0x0001e000 #define AR_BT_QCU_THRESH_S 13 #define AR_BT_RX_CLEAR_POLARITY 0x00020000 #define AR_BT_RX_CLEAR_POLARITY_S 17 #define AR_BT_PRIORITY_TIME 0x00fc0000 #define AR_BT_PRIORITY_TIME_S 18 #define AR_BT_FIRST_SLOT_TIME 0xff000000 #define AR_BT_FIRST_SLOT_TIME_S 24 #define AR_BT_COEX_WEIGHT 0x8174 #define AR_BT_COEX_WGHT 0xff55 #define AR_STOMP_ALL_WLAN_WGHT 0xfcfc #define AR_STOMP_LOW_WLAN_WGHT 0xa8a8 #define AR_STOMP_NONE_WLAN_WGHT 0x0000 #define AR_BTCOEX_BT_WGHT 0x0000ffff #define AR_BTCOEX_BT_WGHT_S 0 #define AR_BTCOEX_WL_WGHT 0xffff0000 #define AR_BTCOEX_WL_WGHT_S 16 #define AR_BT_COEX_WL_WEIGHTS0 0x8174 #define AR_BT_COEX_WL_WEIGHTS1 0x81c4 #define AR_MCI_COEX_WL_WEIGHTS(_i) (0x18b0 + (_i << 2)) #define AR_BT_COEX_BT_WEIGHTS(_i) (0x83ac + (_i << 2)) #define AR9300_BT_WGHT 0xcccc4444 #define AR_BT_COEX_MODE2 0x817c #define AR_BT_BCN_MISS_THRESH 0x000000ff #define AR_BT_BCN_MISS_THRESH_S 0 #define AR_BT_BCN_MISS_CNT 0x0000ff00 #define AR_BT_BCN_MISS_CNT_S 8 #define AR_BT_HOLD_RX_CLEAR 0x00010000 #define AR_BT_HOLD_RX_CLEAR_S 16 #define AR_BT_DISABLE_BT_ANT 0x00100000 #define AR_BT_DISABLE_BT_ANT_S 20 #define AR_TXSIFS 0x81d0 #define AR_TXSIFS_TIME 0x000000FF #define AR_TXSIFS_TX_LATENCY 0x00000F00 #define AR_TXSIFS_TX_LATENCY_S 8 #define AR_TXSIFS_ACK_SHIFT 0x00007000 #define AR_TXSIFS_ACK_SHIFT_S 12 #define AR_TXOP_X 0x81ec #define AR_TXOP_X_VAL 0x000000FF #define AR_TXOP_0_3 0x81f0 #define AR_TXOP_4_7 0x81f4 #define AR_TXOP_8_11 0x81f8 #define AR_TXOP_12_15 0x81fc #define AR_NEXT_NDP2_TIMER 0x8180 #define AR_GEN_TIMER_BANK_1_LEN 8 #define AR_FIRST_NDP_TIMER 7 #define AR_NDP2_PERIOD 0x81a0 #define AR_NDP2_TIMER_MODE 0x81c0 #define AR_GEN_TIMERS(_i) (0x8200 + ((_i) << 2)) #define AR_NEXT_TBTT_TIMER AR_GEN_TIMERS(0) #define AR_NEXT_DMA_BEACON_ALERT AR_GEN_TIMERS(1) #define AR_NEXT_SWBA AR_GEN_TIMERS(2) #define AR_NEXT_CFP AR_GEN_TIMERS(2) #define AR_NEXT_HCF AR_GEN_TIMERS(3) #define AR_NEXT_TIM AR_GEN_TIMERS(4) #define AR_NEXT_DTIM AR_GEN_TIMERS(5) #define AR_NEXT_QUIET_TIMER AR_GEN_TIMERS(6) #define AR_NEXT_NDP_TIMER AR_GEN_TIMERS(7) #define AR_BEACON_PERIOD AR_GEN_TIMERS(8) #define AR_DMA_BEACON_PERIOD AR_GEN_TIMERS(9) #define AR_SWBA_PERIOD AR_GEN_TIMERS(10) #define AR_HCF_PERIOD AR_GEN_TIMERS(11) #define AR_TIM_PERIOD AR_GEN_TIMERS(12) #define AR_DTIM_PERIOD AR_GEN_TIMERS(13) #define AR_QUIET_PERIOD AR_GEN_TIMERS(14) #define AR_NDP_PERIOD AR_GEN_TIMERS(15) #define AR_TIMER_MODE 0x8240 #define AR_TBTT_TIMER_EN 0x00000001 #define AR_DBA_TIMER_EN 0x00000002 #define AR_SWBA_TIMER_EN 0x00000004 #define AR_HCF_TIMER_EN 0x00000008 #define AR_TIM_TIMER_EN 0x00000010 #define AR_DTIM_TIMER_EN 0x00000020 #define AR_QUIET_TIMER_EN 0x00000040 #define AR_NDP_TIMER_EN 0x00000080 #define AR_TIMER_OVERFLOW_INDEX 0x00000700 #define AR_TIMER_OVERFLOW_INDEX_S 8 #define AR_TIMER_THRESH 0xFFFFF000 #define AR_TIMER_THRESH_S 12 #define AR_SLP32_MODE 0x8244 #define AR_SLP32_HALF_CLK_LATENCY 0x000FFFFF #define AR_SLP32_ENA 0x00100000 #define AR_SLP32_TSF_WRITE_STATUS 0x00200000 #define AR_SLP32_WAKE 0x8248 #define AR_SLP32_WAKE_XTL_TIME 0x0000FFFF #define AR_SLP32_INC 0x824c #define AR_SLP32_TST_INC 0x000FFFFF #define AR_SLP_CNT 0x8250 #define AR_SLP_CYCLE_CNT 0x8254 #define AR_SLP_MIB_CTRL 0x8258 #define AR_SLP_MIB_CLEAR 0x00000001 #define AR_SLP_MIB_PENDING 0x00000002 #define AR_MAC_PCU_LOGIC_ANALYZER 0x8264 #define AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768 0x20000000 #define AR_2040_MODE 0x8318 #define AR_2040_JOINED_RX_CLEAR 0x00000001 #define AR_EXTRCCNT 0x8328 #define AR_SELFGEN_MASK 0x832c #define AR_PCU_TXBUF_CTRL 0x8340 #define AR_PCU_TXBUF_CTRL_SIZE_MASK 0x7FF #define AR_PCU_TXBUF_CTRL_USABLE_SIZE 0x700 #define AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE 0x380 #define AR_PCU_MISC_MODE2 0x8344 #define AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE 0x00000002 #define AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT 0x00000004 #define AR_PCU_MISC_MODE2_RESERVED 0x00000038 #define AR_PCU_MISC_MODE2_ADHOC_MCAST_KEYID_ENABLE 0x00000040 #define AR_PCU_MISC_MODE2_CFP_IGNORE 0x00000080 #define AR_PCU_MISC_MODE2_MGMT_QOS 0x0000FF00 #define AR_PCU_MISC_MODE2_MGMT_QOS_S 8 #define AR_PCU_MISC_MODE2_ENABLE_LOAD_NAV_BEACON_DURATION 0x00010000 #define AR_PCU_MISC_MODE2_ENABLE_AGGWEP 0x00020000 #define AR_PCU_MISC_MODE2_HWWAR1 0x00100000 #define AR_PCU_MISC_MODE2_HWWAR2 0x02000000 #define AR_PCU_MISC_MODE2_RESERVED2 0xFFFE0000 #define AR_PCU_MISC_MODE3 0x83d0 #define AR_MAC_PCU_ASYNC_FIFO_REG3 0x8358 #define AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL 0x00000400 #define AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET 0x80000000 #define AR_MAC_PCU_GEN_TIMER_TSF_SEL 0x83d8 #define AR_AES_MUTE_MASK0 0x805c #define AR_AES_MUTE_MASK0_FC 0x0000FFFF #define AR_AES_MUTE_MASK0_QOS 0xFFFF0000 #define AR_AES_MUTE_MASK0_QOS_S 16 #define AR_AES_MUTE_MASK1 0x8060 #define AR_AES_MUTE_MASK1_SEQ 0x0000FFFF #define AR_AES_MUTE_MASK1_SEQ_S 0 #define AR_AES_MUTE_MASK1_FC_MGMT 0xFFFF0000 #define AR_AES_MUTE_MASK1_FC_MGMT_S 16 #define AR_RATE_DURATION_0 0x8700 #define AR_RATE_DURATION_31 0x87CC #define AR_RATE_DURATION_32 0x8780 #define AR_RATE_DURATION(_n) (AR_RATE_DURATION_0 + ((_n)<<2)) /* WoW - Wake On Wireless */ #define AR_PMCTRL_AUX_PWR_DET 0x10000000 /* Puts Chip in L2 state */ #define AR_PMCTRL_D3COLD_VAUX 0x00800000 #define AR_PMCTRL_HOST_PME_EN 0x00400000 /* Send OOB WAKE_L on WoW event */ #define AR_PMCTRL_WOW_PME_CLR 0x00200000 /* Clear WoW event */ #define AR_PMCTRL_PWR_STATE_MASK 0x0f000000 /* Power State Mask */ #define AR_PMCTRL_PWR_STATE_D1D3 0x0f000000 /* Activate D1 and D3 */ #define AR_PMCTRL_PWR_STATE_D1D3_REAL 0x0f000000 /* Activate D1 and D3 */ #define AR_PMCTRL_PWR_STATE_D0 0x08000000 /* Activate D0 */ #define AR_PMCTRL_PWR_PM_CTRL_ENA 0x00008000 /* Enable power mgmt */ #define AR_WOW_BEACON_TIMO_MAX 0xffffffff /* * MAC WoW Registers */ #define AR_WOW_PATTERN 0x825C #define AR_WOW_COUNT 0x8260 #define AR_WOW_BCN_EN 0x8270 #define AR_WOW_BCN_TIMO 0x8274 #define AR_WOW_KEEP_ALIVE_TIMO 0x8278 #define AR_WOW_KEEP_ALIVE 0x827c #define AR_WOW_US_SCALAR 0x8284 #define AR_WOW_KEEP_ALIVE_DELAY 0x8288 #define AR_WOW_PATTERN_MATCH 0x828c #define AR_WOW_PATTERN_OFF1 0x8290 /* pattern bytes 0 -> 3 */ #define AR_WOW_PATTERN_OFF2 0x8294 /* pattern bytes 4 -> 7 */ /* for AR9285 or later version of chips */ #define AR_WOW_EXACT 0x829c #define AR_WOW_LENGTH1 0x8360 #define AR_WOW_LENGTH2 0X8364 /* register to enable match for less than 256 bytes packets */ #define AR_WOW_PATTERN_MATCH_LT_256B 0x8368 #define AR_SW_WOW_CONTROL 0x20018 #define AR_SW_WOW_ENABLE 0x1 #define AR_SWITCH_TO_REFCLK 0x2 #define AR_RESET_CONTROL 0x4 #define AR_RESET_VALUE_MASK 0x8 #define AR_HW_WOW_DISABLE 0x10 #define AR_CLR_MAC_INTERRUPT 0x20 #define AR_CLR_KA_INTERRUPT 0x40 /* AR_WOW_PATTERN register values */ #define AR_WOW_BACK_OFF_SHIFT(x) ((x & 0xf) << 28) /* in usecs */ #define AR_WOW_MAC_INTR_EN 0x00040000 #define AR_WOW_MAGIC_EN 0x00010000 #define AR_WOW_PATTERN_EN(x) (x & 0xff) #define AR_WOW_PAT_FOUND_SHIFT 8 #define AR_WOW_PATTERN_FOUND(x) (x & (0xff << AR_WOW_PAT_FOUND_SHIFT)) #define AR_WOW_PATTERN_FOUND_MASK ((0xff) << AR_WOW_PAT_FOUND_SHIFT) #define AR_WOW_MAGIC_PAT_FOUND 0x00020000 #define AR_WOW_MAC_INTR 0x00080000 #define AR_WOW_KEEP_ALIVE_FAIL 0x00100000 #define AR_WOW_BEACON_FAIL 0x00200000 #define AR_WOW_STATUS(x) (x & (AR_WOW_PATTERN_FOUND_MASK | \ AR_WOW_MAGIC_PAT_FOUND | \ AR_WOW_KEEP_ALIVE_FAIL | \ AR_WOW_BEACON_FAIL)) #define AR_WOW_CLEAR_EVENTS(x) (x & ~(AR_WOW_PATTERN_EN(0xff) | \ AR_WOW_MAGIC_EN | \ AR_WOW_MAC_INTR_EN | \ AR_WOW_BEACON_FAIL | \ AR_WOW_KEEP_ALIVE_FAIL)) /* AR_WOW_COUNT register values */ #define AR_WOW_AIFS_CNT(x) (x & 0xff) #define AR_WOW_SLOT_CNT(x) ((x & 0xff) << 8) #define AR_WOW_KEEP_ALIVE_CNT(x) ((x & 0xff) << 16) /* AR_WOW_BCN_EN register */ #define AR_WOW_BEACON_FAIL_EN 0x00000001 /* AR_WOW_BCN_TIMO rgister */ #define AR_WOW_BEACON_TIMO 0x40000000 /* valid if BCN_EN is set */ /* AR_WOW_KEEP_ALIVE_TIMO register */ #define AR_WOW_KEEP_ALIVE_TIMO_VALUE #define AR_WOW_KEEP_ALIVE_NEVER 0xffffffff /* AR_WOW_KEEP_ALIVE register */ #define AR_WOW_KEEP_ALIVE_AUTO_DIS 0x00000001 #define AR_WOW_KEEP_ALIVE_FAIL_DIS 0x00000002 /* AR_WOW_KEEP_ALIVE_DELAY register */ #define AR_WOW_KEEP_ALIVE_DELAY_VALUE 0x000003e8 /* 1 msec */ /* * keep it long for beacon workaround - ensure no false alarm */ #define AR_WOW_BMISSTHRESHOLD 0x20 /* AR_WOW_PATTERN_MATCH register */ #define AR_WOW_PAT_END_OF_PKT(x) (x & 0xf) #define AR_WOW_PAT_OFF_MATCH(x) ((x & 0xf) << 8) /* * default values for Wow Configuration for backoff, aifs, slot, keep-alive * to be programmed into various registers. */ #define AR_WOW_PAT_BACKOFF 0x00000004 /* AR_WOW_PATTERN_REG */ #define AR_WOW_CNT_AIFS_CNT 0x00000022 /* AR_WOW_COUNT_REG */ #define AR_WOW_CNT_SLOT_CNT 0x00000009 /* AR_WOW_COUNT_REG */ /* * Keepalive count applicable for AR9280 2.0 and above. */ #define AR_WOW_CNT_KA_CNT 0x00000008 /* AR_WOW_COUNT register */ /* WoW - Transmit buffer for keep alive frames */ #define AR_WOW_TRANSMIT_BUFFER 0xe000 /* E000 - EFFC */ #define AR_WOW_TXBUF(i) (AR_WOW_TRANSMIT_BUFFER + ((i) << 2)) #define AR_WOW_KA_DESC_WORD2 0xe000 #define AR_WOW_KA_DATA_WORD0 0xe030 /* WoW Transmit Buffer for patterns */ #define AR_WOW_TB_PATTERN(i) (0xe100 + (i << 8)) #define AR_WOW_TB_MASK(i) (0xec00 + (i << 5)) /* Currently Pattern 0-7 are supported - so bit 0-7 are set */ #define AR_WOW_PATTERN_SUPPORTED 0xff #define AR_WOW_LENGTH_MAX 0xff #define AR_WOW_LEN1_SHIFT(_i) ((0x3 - ((_i) & 0x3)) << 0x3) #define AR_WOW_LENGTH1_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN1_SHIFT(_i)) #define AR_WOW_LEN2_SHIFT(_i) ((0x7 - ((_i) & 0x7)) << 0x3) #define AR_WOW_LENGTH2_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN2_SHIFT(_i)) #define AR9271_CORE_CLOCK 117 /* clock to 117Mhz */ #define AR9271_TARGET_BAUD_RATE 19200 /* 115200 */ #define AR_AGG_WEP_ENABLE_FIX 0x00000008 /* This allows the use of AR_AGG_WEP_ENABLE */ #define AR_ADHOC_MCAST_KEYID_ENABLE 0x00000040 /* This bit enables the Multicast search * based on both MAC Address and Key ID. * If bit is 0, then Multicast search is * based on MAC address only. * For Merlin and above only. */ #define AR_AGG_WEP_ENABLE 0x00020000 /* This field enables AGG_WEP feature, * when it is enable, AGG_WEP would takes * charge of the encryption interface of * pcu_txsm. */ #define AR9300_SM_BASE 0xa200 #define AR9002_PHY_AGC_CONTROL 0x9860 #define AR9003_PHY_AGC_CONTROL AR9300_SM_BASE + 0xc4 #define AR_PHY_AGC_CONTROL (AR_SREV_9300_20_OR_LATER(ah) ? AR9003_PHY_AGC_CONTROL : AR9002_PHY_AGC_CONTROL) #define AR_PHY_AGC_CONTROL_CAL 0x00000001 /* do internal calibration */ #define AR_PHY_AGC_CONTROL_NF 0x00000002 /* do noise-floor calibration */ #define AR_PHY_AGC_CONTROL_OFFSET_CAL 0x00000800 /* allow offset calibration */ #define AR_PHY_AGC_CONTROL_ENABLE_NF 0x00008000 /* enable noise floor calibration to happen */ #define AR_PHY_AGC_CONTROL_FLTR_CAL 0x00010000 /* allow tx filter calibration */ #define AR_PHY_AGC_CONTROL_NO_UPDATE_NF 0x00020000 /* don't update noise floor automatically */ #define AR_PHY_AGC_CONTROL_EXT_NF_PWR_MEAS 0x00040000 /* extend noise floor power measurement */ #define AR_PHY_AGC_CONTROL_CLC_SUCCESS 0x00080000 /* carrier leak calibration done */ #define AR_PHY_AGC_CONTROL_PKDET_CAL 0x00100000 #define AR_PHY_AGC_CONTROL_YCOK_MAX 0x000003c0 #define AR_PHY_AGC_CONTROL_YCOK_MAX_S 6 /* MCI Registers */ #define AR_MCI_COMMAND0 0x1800 #define AR_MCI_COMMAND0_HEADER 0xFF #define AR_MCI_COMMAND0_HEADER_S 0 #define AR_MCI_COMMAND0_LEN 0x1f00 #define AR_MCI_COMMAND0_LEN_S 8 #define AR_MCI_COMMAND0_DISABLE_TIMESTAMP 0x2000 #define AR_MCI_COMMAND0_DISABLE_TIMESTAMP_S 13 #define AR_MCI_COMMAND1 0x1804 #define AR_MCI_COMMAND2 0x1808 #define AR_MCI_COMMAND2_RESET_TX 0x01 #define AR_MCI_COMMAND2_RESET_TX_S 0 #define AR_MCI_COMMAND2_RESET_RX 0x02 #define AR_MCI_COMMAND2_RESET_RX_S 1 #define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES 0x3FC #define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES_S 2 #define AR_MCI_COMMAND2_RESET_REQ_WAKEUP 0x400 #define AR_MCI_COMMAND2_RESET_REQ_WAKEUP_S 10 #define AR_MCI_RX_CTRL 0x180c #define AR_MCI_TX_CTRL 0x1810 /* 0 = no division, 1 = divide by 2, 2 = divide by 4, 3 = divide by 8 */ #define AR_MCI_TX_CTRL_CLK_DIV 0x03 #define AR_MCI_TX_CTRL_CLK_DIV_S 0 #define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE 0x04 #define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE_S 2 #define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ 0xFFFFF8 #define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ_S 3 #define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM 0xF000000 #define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM_S 24 #define AR_MCI_MSG_ATTRIBUTES_TABLE 0x1814 #define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM 0xFFFF #define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM_S 0 #define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR 0xFFFF0000 #define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR_S 16 #define AR_MCI_SCHD_TABLE_0 0x1818 #define AR_MCI_SCHD_TABLE_1 0x181c #define AR_MCI_GPM_0 0x1820 #define AR_MCI_GPM_1 0x1824 #define AR_MCI_GPM_WRITE_PTR 0xFFFF0000 #define AR_MCI_GPM_WRITE_PTR_S 16 #define AR_MCI_GPM_BUF_LEN 0x0000FFFF #define AR_MCI_GPM_BUF_LEN_S 0 #define AR_MCI_INTERRUPT_RAW 0x1828 #define AR_MCI_INTERRUPT_EN 0x182c #define AR_MCI_INTERRUPT_SW_MSG_DONE 0x00000001 #define AR_MCI_INTERRUPT_SW_MSG_DONE_S 0 #define AR_MCI_INTERRUPT_CPU_INT_MSG 0x00000002 #define AR_MCI_INTERRUPT_CPU_INT_MSG_S 1 #define AR_MCI_INTERRUPT_RX_CKSUM_FAIL 0x00000004 #define AR_MCI_INTERRUPT_RX_CKSUM_FAIL_S 2 #define AR_MCI_INTERRUPT_RX_INVALID_HDR 0x00000008 #define AR_MCI_INTERRUPT_RX_INVALID_HDR_S 3 #define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL 0x00000010 #define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL_S 4 #define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL 0x00000020 #define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL_S 5 #define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL 0x00000080 #define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL_S 7 #define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL 0x00000100 #define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL_S 8 #define AR_MCI_INTERRUPT_RX_MSG 0x00000200 #define AR_MCI_INTERRUPT_RX_MSG_S 9 #define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE 0x00000400 #define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE_S 10 #define AR_MCI_INTERRUPT_BT_PRI 0x07fff800 #define AR_MCI_INTERRUPT_BT_PRI_S 11 #define AR_MCI_INTERRUPT_BT_PRI_THRESH 0x08000000 #define AR_MCI_INTERRUPT_BT_PRI_THRESH_S 27 #define AR_MCI_INTERRUPT_BT_FREQ 0x10000000 #define AR_MCI_INTERRUPT_BT_FREQ_S 28 #define AR_MCI_INTERRUPT_BT_STOMP 0x20000000 #define AR_MCI_INTERRUPT_BT_STOMP_S 29 #define AR_MCI_INTERRUPT_BB_AIC_IRQ 0x40000000 #define AR_MCI_INTERRUPT_BB_AIC_IRQ_S 30 #define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT 0x80000000 #define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT_S 31 #define AR_MCI_INTERRUPT_DEFAULT (AR_MCI_INTERRUPT_SW_MSG_DONE | \ AR_MCI_INTERRUPT_RX_INVALID_HDR | \ AR_MCI_INTERRUPT_RX_HW_MSG_FAIL | \ AR_MCI_INTERRUPT_RX_SW_MSG_FAIL | \ AR_MCI_INTERRUPT_TX_HW_MSG_FAIL | \ AR_MCI_INTERRUPT_TX_SW_MSG_FAIL | \ AR_MCI_INTERRUPT_RX_MSG | \ AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE | \ AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT) #define AR_MCI_INTERRUPT_MSG_FAIL_MASK (AR_MCI_INTERRUPT_RX_HW_MSG_FAIL | \ AR_MCI_INTERRUPT_RX_SW_MSG_FAIL | \ AR_MCI_INTERRUPT_TX_HW_MSG_FAIL | \ AR_MCI_INTERRUPT_TX_SW_MSG_FAIL) #define AR_MCI_REMOTE_CPU_INT 0x1830 #define AR_MCI_REMOTE_CPU_INT_EN 0x1834 #define AR_MCI_INTERRUPT_RX_MSG_RAW 0x1838 #define AR_MCI_INTERRUPT_RX_MSG_EN 0x183c #define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET 0x00000001 #define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET_S 0 #define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL 0x00000002 #define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL_S 1 #define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK 0x00000004 #define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK_S 2 #define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO 0x00000008 #define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO_S 3 #define AR_MCI_INTERRUPT_RX_MSG_CONT_RST 0x00000010 #define AR_MCI_INTERRUPT_RX_MSG_CONT_RST_S 4 #define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO 0x00000020 #define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO_S 5 #define AR_MCI_INTERRUPT_RX_MSG_CPU_INT 0x00000040 #define AR_MCI_INTERRUPT_RX_MSG_CPU_INT_S 6 #define AR_MCI_INTERRUPT_RX_MSG_GPM 0x00000100 #define AR_MCI_INTERRUPT_RX_MSG_GPM_S 8 #define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO 0x00000200 #define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO_S 9 #define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING 0x00000400 #define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING_S 10 #define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING 0x00000800 #define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING_S 11 #define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE 0x00001000 #define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE_S 12 #define AR_MCI_INTERRUPT_RX_HW_MSG_MASK (AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO | \ AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL| \ AR_MCI_INTERRUPT_RX_MSG_LNA_INFO | \ AR_MCI_INTERRUPT_RX_MSG_CONT_NACK | \ AR_MCI_INTERRUPT_RX_MSG_CONT_INFO | \ AR_MCI_INTERRUPT_RX_MSG_CONT_RST) #define AR_MCI_INTERRUPT_RX_MSG_DEFAULT (AR_MCI_INTERRUPT_RX_MSG_GPM | \ AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET| \ AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING | \ AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING| \ AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) #define AR_MCI_CPU_INT 0x1840 #define AR_MCI_RX_STATUS 0x1844 #define AR_MCI_RX_LAST_SCHD_MSG_INDEX 0x00000F00 #define AR_MCI_RX_LAST_SCHD_MSG_INDEX_S 8 #define AR_MCI_RX_REMOTE_SLEEP 0x00001000 #define AR_MCI_RX_REMOTE_SLEEP_S 12 #define AR_MCI_RX_MCI_CLK_REQ 0x00002000 #define AR_MCI_RX_MCI_CLK_REQ_S 13 #define AR_MCI_CONT_STATUS 0x1848 #define AR_MCI_CONT_RSSI_POWER 0x000000FF #define AR_MCI_CONT_RSSI_POWER_S 0 #define AR_MCI_CONT_PRIORITY 0x0000FF00 #define AR_MCI_CONT_PRIORITY_S 8 #define AR_MCI_CONT_TXRX 0x00010000 #define AR_MCI_CONT_TXRX_S 16 #define AR_MCI_BT_PRI0 0x184c #define AR_MCI_BT_PRI1 0x1850 #define AR_MCI_BT_PRI2 0x1854 #define AR_MCI_BT_PRI3 0x1858 #define AR_MCI_BT_PRI 0x185c #define AR_MCI_WL_FREQ0 0x1860 #define AR_MCI_WL_FREQ1 0x1864 #define AR_MCI_WL_FREQ2 0x1868 #define AR_MCI_GAIN 0x186c #define AR_MCI_WBTIMER1 0x1870 #define AR_MCI_WBTIMER2 0x1874 #define AR_MCI_WBTIMER3 0x1878 #define AR_MCI_WBTIMER4 0x187c #define AR_MCI_MAXGAIN 0x1880 #define AR_MCI_HW_SCHD_TBL_CTL 0x1884 #define AR_MCI_HW_SCHD_TBL_D0 0x1888 #define AR_MCI_HW_SCHD_TBL_D1 0x188c #define AR_MCI_HW_SCHD_TBL_D2 0x1890 #define AR_MCI_HW_SCHD_TBL_D3 0x1894 #define AR_MCI_TX_PAYLOAD0 0x1898 #define AR_MCI_TX_PAYLOAD1 0x189c #define AR_MCI_TX_PAYLOAD2 0x18a0 #define AR_MCI_TX_PAYLOAD3 0x18a4 #define AR_BTCOEX_WBTIMER 0x18a8 #define AR_BTCOEX_CTRL 0x18ac #define AR_BTCOEX_CTRL_AR9462_MODE 0x00000001 #define AR_BTCOEX_CTRL_AR9462_MODE_S 0 #define AR_BTCOEX_CTRL_WBTIMER_EN 0x00000002 #define AR_BTCOEX_CTRL_WBTIMER_EN_S 1 #define AR_BTCOEX_CTRL_MCI_MODE_EN 0x00000004 #define AR_BTCOEX_CTRL_MCI_MODE_EN_S 2 #define AR_BTCOEX_CTRL_LNA_SHARED 0x00000008 #define AR_BTCOEX_CTRL_LNA_SHARED_S 3 #define AR_BTCOEX_CTRL_PA_SHARED 0x00000010 #define AR_BTCOEX_CTRL_PA_SHARED_S 4 #define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN 0x00000020 #define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN_S 5 #define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN 0x00000040 #define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN_S 6 #define AR_BTCOEX_CTRL_NUM_ANTENNAS 0x00000180 #define AR_BTCOEX_CTRL_NUM_ANTENNAS_S 7 #define AR_BTCOEX_CTRL_RX_CHAIN_MASK 0x00000E00 #define AR_BTCOEX_CTRL_RX_CHAIN_MASK_S 9 #define AR_BTCOEX_CTRL_AGGR_THRESH 0x00007000 #define AR_BTCOEX_CTRL_AGGR_THRESH_S 12 #define AR_BTCOEX_CTRL_1_CHAIN_BCN 0x00080000 #define AR_BTCOEX_CTRL_1_CHAIN_BCN_S 19 #define AR_BTCOEX_CTRL_1_CHAIN_ACK 0x00100000 #define AR_BTCOEX_CTRL_1_CHAIN_ACK_S 20 #define AR_BTCOEX_CTRL_WAIT_BA_MARGIN 0x1FE00000 #define AR_BTCOEX_CTRL_WAIT_BA_MARGIN_S 28 #define AR_BTCOEX_CTRL_REDUCE_TXPWR 0x20000000 #define AR_BTCOEX_CTRL_REDUCE_TXPWR_S 29 #define AR_BTCOEX_CTRL_SPDT_ENABLE_10 0x40000000 #define AR_BTCOEX_CTRL_SPDT_ENABLE_10_S 30 #define AR_BTCOEX_CTRL_SPDT_POLARITY 0x80000000 #define AR_BTCOEX_CTRL_SPDT_POLARITY_S 31 #define AR_BTCOEX_MAX_TXPWR(_x) (0x18c0 + ((_x) << 2)) #define AR_BTCOEX_WL_LNA 0x1940 #define AR_BTCOEX_RFGAIN_CTRL 0x1944 #define AR_BTCOEX_CTRL2 0x1948 #define AR_BTCOEX_CTRL2_TXPWR_THRESH 0x0007F800 #define AR_BTCOEX_CTRL2_TXPWR_THRESH_S 11 #define AR_BTCOEX_CTRL2_TX_CHAIN_MASK 0x00380000 #define AR_BTCOEX_CTRL2_TX_CHAIN_MASK_S 19 #define AR_BTCOEX_CTRL2_RX_DEWEIGHT 0x00400000 #define AR_BTCOEX_CTRL2_RX_DEWEIGHT_S 22 #define AR_BTCOEX_CTRL2_GPIO_OBS_SEL 0x00800000 #define AR_BTCOEX_CTRL2_GPIO_OBS_SEL_S 23 #define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL 0x01000000 #define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL_S 24 #define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE 0x02000000 #define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE_S 25 #define AR_BTCOEX_CTRL_SPDT_ENABLE 0x00000001 #define AR_BTCOEX_CTRL_SPDT_ENABLE_S 0 #define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL 0x00000002 #define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL_S 1 #define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT 0x00000004 #define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT_S 2 #define AR_GLB_WLAN_UART_INTF_EN 0x00020000 #define AR_GLB_WLAN_UART_INTF_EN_S 17 #define AR_GLB_DS_JTAG_DISABLE 0x00040000 #define AR_GLB_DS_JTAG_DISABLE_S 18 #define AR_BTCOEX_RC 0x194c #define AR_BTCOEX_MAX_RFGAIN(_x) (0x1950 + ((_x) << 2)) #define AR_BTCOEX_DBG 0x1a50 #define AR_MCI_LAST_HW_MSG_HDR 0x1a54 #define AR_MCI_LAST_HW_MSG_BDY 0x1a58 #define AR_MCI_SCHD_TABLE_2 0x1a5c #define AR_MCI_SCHD_TABLE_2_MEM_BASED 0x00000001 #define AR_MCI_SCHD_TABLE_2_MEM_BASED_S 0 #define AR_MCI_SCHD_TABLE_2_HW_BASED 0x00000002 #define AR_MCI_SCHD_TABLE_2_HW_BASED_S 1 #define AR_BTCOEX_CTRL3 0x1a60 #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT 0x00000fff #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT_S 0 #define AR_GLB_SWREG_DISCONT_MODE 0x2002c #define AR_GLB_SWREG_DISCONT_EN_BT_WLAN 0x3 #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/recv.c0000644000175000017500000010171112026211315023361 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "ath9k.h" #include "ar9003_mac.h" #define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb)) static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) { return sc->ps_enabled && (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP); } /* * Setup and link descriptors. * * 11N: we can no longer afford to self link the last descriptor. * MAC acknowledges BA status as long as it copies frames to host * buffer (or rx fifo). This can incorrectly acknowledge packets * to a sender if last desc is self-linked. */ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_desc *ds; struct sk_buff *skb; ATH_RXBUF_RESET(bf); ds = bf->bf_desc; ds->ds_link = 0; /* link to null */ ds->ds_data = bf->bf_buf_addr; /* virtual addr of the beginning of the buffer. */ skb = bf->bf_mpdu; BUG_ON(skb == NULL); ds->ds_vdata = skb->data; /* * setup rx descriptors. The rx_bufsize here tells the hardware * how much data it can DMA to us and that we are prepared * to process */ ath9k_hw_setuprxdesc(ah, ds, common->rx_bufsize, 0); if (sc->rx.rxlink == NULL) ath9k_hw_putrxbuf(ah, bf->bf_daddr); else *sc->rx.rxlink = bf->bf_daddr; sc->rx.rxlink = &ds->ds_link; } static void ath_setdefantenna(struct ath_softc *sc, u32 antenna) { /* XXX block beacon interrupts */ ath9k_hw_setantenna(sc->sc_ah, antenna); sc->rx.defant = antenna; sc->rx.rxotherant = 0; } static void ath_opmode_init(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u32 rfilt, mfilt[2]; /* configure rx filter */ rfilt = ath_calcrxfilter(sc); ath9k_hw_setrxfilter(ah, rfilt); /* configure bssid mask */ ath_hw_setbssidmask(common); /* configure operational mode */ ath9k_hw_setopmode(ah); /* calculate and install multicast filter */ mfilt[0] = mfilt[1] = ~0; ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); } static bool ath_rx_edma_buf_link(struct ath_softc *sc, enum ath9k_rx_qtype qtype) { struct ath_hw *ah = sc->sc_ah; struct ath_rx_edma *rx_edma; struct sk_buff *skb; struct ath_buf *bf; rx_edma = &sc->rx.rx_edma[qtype]; if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize) return false; bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); list_del_init(&bf->list); skb = bf->bf_mpdu; ATH_RXBUF_RESET(bf); memset(skb->data, 0, ah->caps.rx_status_len); dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, ah->caps.rx_status_len, DMA_TO_DEVICE); SKB_CB_ATHBUF(skb) = bf; ath9k_hw_addrxbuf_edma(ah, bf->bf_buf_addr, qtype); skb_queue_tail(&rx_edma->rx_fifo, skb); return true; } static void ath_rx_addbuffer_edma(struct ath_softc *sc, enum ath9k_rx_qtype qtype, int size) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_buf *bf, *tbf; if (list_empty(&sc->rx.rxbuf)) { ath_dbg(common, QUEUE, "No free rx buf available\n"); return; } list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) if (!ath_rx_edma_buf_link(sc, qtype)) break; } static void ath_rx_remove_buffer(struct ath_softc *sc, enum ath9k_rx_qtype qtype) { struct ath_buf *bf; struct ath_rx_edma *rx_edma; struct sk_buff *skb; rx_edma = &sc->rx.rx_edma[qtype]; while ((skb = skb_dequeue(&rx_edma->rx_fifo)) != NULL) { bf = SKB_CB_ATHBUF(skb); BUG_ON(!bf); list_add_tail(&bf->list, &sc->rx.rxbuf); } } static void ath_rx_edma_cleanup(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf; ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP); ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP); list_for_each_entry(bf, &sc->rx.rxbuf, list) { if (bf->bf_mpdu) { dma_unmap_single(sc->dev, bf->bf_buf_addr, common->rx_bufsize, DMA_BIDIRECTIONAL); dev_kfree_skb_any(bf->bf_mpdu); bf->bf_buf_addr = 0; bf->bf_mpdu = NULL; } } INIT_LIST_HEAD(&sc->rx.rxbuf); kfree(sc->rx.rx_bufptr); sc->rx.rx_bufptr = NULL; } static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size) { skb_queue_head_init(&rx_edma->rx_fifo); rx_edma->rx_fifo_hwsize = size; } static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; struct sk_buff *skb; struct ath_buf *bf; int error = 0, i; u32 size; ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize - ah->caps.rx_status_len); ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_LP], ah->caps.rx_lp_qdepth); ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP], ah->caps.rx_hp_qdepth); size = sizeof(struct ath_buf) * nbufs; bf = kzalloc(size, GFP_KERNEL); if (!bf) return -ENOMEM; INIT_LIST_HEAD(&sc->rx.rxbuf); sc->rx.rx_bufptr = bf; for (i = 0; i < nbufs; i++, bf++) { skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_KERNEL); if (!skb) { error = -ENOMEM; goto rx_init_fail; } memset(skb->data, 0, common->rx_bufsize); bf->bf_mpdu = skb; bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, common->rx_bufsize, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; bf->bf_buf_addr = 0; ath_err(common, "dma_mapping_error() on RX init\n"); error = -ENOMEM; goto rx_init_fail; } list_add_tail(&bf->list, &sc->rx.rxbuf); } return 0; rx_init_fail: ath_rx_edma_cleanup(sc); return error; } static void ath_edma_start_recv(struct ath_softc *sc) { spin_lock_bh(&sc->rx.rxbuflock); ath9k_hw_rxena(sc->sc_ah); ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP, sc->rx.rx_edma[ATH9K_RX_QUEUE_HP].rx_fifo_hwsize); ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP, sc->rx.rx_edma[ATH9K_RX_QUEUE_LP].rx_fifo_hwsize); ath_opmode_init(sc); ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); spin_unlock_bh(&sc->rx.rxbuflock); } static void ath_edma_stop_recv(struct ath_softc *sc) { ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP); ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP); } int ath_rx_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct sk_buff *skb; struct ath_buf *bf; int error = 0; spin_lock_init(&sc->sc_pcu_lock); spin_lock_init(&sc->rx.rxbuflock); clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + sc->sc_ah->caps.rx_status_len; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { return ath_rx_edma_init(sc, nbufs); } else { ath_dbg(common, CONFIG, "cachelsz %u rxbufsize %u\n", common->cachelsz, common->rx_bufsize); /* Initialize rx descriptors */ error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf, "rx", nbufs, 1, 0); if (error != 0) { ath_err(common, "failed to allocate rx descriptors: %d\n", error); goto err; } list_for_each_entry(bf, &sc->rx.rxbuf, list) { skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_KERNEL); if (skb == NULL) { error = -ENOMEM; goto err; } bf->bf_mpdu = skb; bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, common->rx_bufsize, DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; bf->bf_buf_addr = 0; ath_err(common, "dma_mapping_error() on RX init\n"); error = -ENOMEM; goto err; } } sc->rx.rxlink = NULL; } err: if (error) ath_rx_cleanup(sc); return error; } void ath_rx_cleanup(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; struct ath_buf *bf; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { ath_rx_edma_cleanup(sc); return; } else { list_for_each_entry(bf, &sc->rx.rxbuf, list) { skb = bf->bf_mpdu; if (skb) { dma_unmap_single(sc->dev, bf->bf_buf_addr, common->rx_bufsize, DMA_FROM_DEVICE); dev_kfree_skb(skb); bf->bf_buf_addr = 0; bf->bf_mpdu = NULL; } } if (sc->rx.rxdma.dd_desc_len != 0) ath_descdma_cleanup(sc, &sc->rx.rxdma, &sc->rx.rxbuf); } } /* * Calculate the receive filter according to the * operating mode and state: * * o always accept unicast, broadcast, and multicast traffic * o maintain current state of phy error reception (the hal * may enable phy error frames for noise immunity work) * o probe request frames are accepted only when operating in * hostap, adhoc, or monitor modes * o enable promiscuous mode according to the interface state * o accept beacons: * - when operating in adhoc mode so the 802.11 layer creates * node table entries for peers, * - when operating in station mode for collecting rssi data when * the station is otherwise quiet, or * - when operating as a repeater so we see repeater-sta beacons * - when scanning */ u32 ath_calcrxfilter(struct ath_softc *sc) { u32 rfilt; rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; if (sc->rx.rxfilter & FIF_PROBE_REQ) rfilt |= ATH9K_RX_FILTER_PROBEREQ; /* * Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station * mode interface or when in monitor mode. AP mode does not need this * since it receives all in-BSS frames anyway. */ if (sc->sc_ah->is_monitoring) rfilt |= ATH9K_RX_FILTER_PROM; if (sc->rx.rxfilter & FIF_CONTROL) rfilt |= ATH9K_RX_FILTER_CONTROL; if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) && (sc->nvifs <= 1) && !(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC)) rfilt |= ATH9K_RX_FILTER_MYBEACON; else rfilt |= ATH9K_RX_FILTER_BEACON; if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || (sc->rx.rxfilter & FIF_PSPOLL)) rfilt |= ATH9K_RX_FILTER_PSPOLL; if (conf_is_ht(&sc->hw->conf)) rfilt |= ATH9K_RX_FILTER_COMP_BAR; if (sc->nvifs > 1 || (sc->rx.rxfilter & FIF_OTHER_BSS)) { /* The following may also be needed for other older chips */ if (sc->sc_ah->hw_version.macVersion == AR_SREV_VERSION_9160) rfilt |= ATH9K_RX_FILTER_PROM; rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; } if (AR_SREV_9550(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; return rfilt; } int ath_startrecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_buf *bf, *tbf; if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { ath_edma_start_recv(sc); return 0; } spin_lock_bh(&sc->rx.rxbuflock); if (list_empty(&sc->rx.rxbuf)) goto start_recv; sc->rx.rxlink = NULL; list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) { ath_rx_buf_link(sc, bf); } /* We could have deleted elements so the list may be empty now */ if (list_empty(&sc->rx.rxbuf)) goto start_recv; bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); ath9k_hw_putrxbuf(ah, bf->bf_daddr); ath9k_hw_rxena(ah); start_recv: ath_opmode_init(sc); ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); spin_unlock_bh(&sc->rx.rxbuflock); return 0; } bool ath_stoprecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; bool stopped, reset = false; spin_lock_bh(&sc->rx.rxbuflock); ath9k_hw_abortpcurecv(ah); ath9k_hw_setrxfilter(ah, 0); stopped = ath9k_hw_stopdmarecv(ah, &reset); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_edma_stop_recv(sc); else sc->rx.rxlink = NULL; spin_unlock_bh(&sc->rx.rxbuflock); if (!(ah->ah_flags & AH_UNPLUGGED) && unlikely(!stopped)) { ath_err(ath9k_hw_common(sc->sc_ah), "Could not stop RX, we could be " "confusing the DMA engine when we start RX up\n"); ATH_DBG_WARN_ON_ONCE(!stopped); } return stopped && !reset; } void ath_flushrecv(struct ath_softc *sc) { set_bit(SC_OP_RXFLUSH, &sc->sc_flags); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_rx_tasklet(sc, 1, true); ath_rx_tasklet(sc, 1, false); clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); } static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) { /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */ struct ieee80211_mgmt *mgmt; u8 *pos, *end, id, elen; struct ieee80211_tim_ie *tim; mgmt = (struct ieee80211_mgmt *)skb->data; pos = mgmt->u.beacon.variable; end = skb->data + skb->len; while (pos + 2 < end) { id = *pos++; elen = *pos++; if (pos + elen > end) break; if (id == WLAN_EID_TIM) { if (elen < sizeof(*tim)) break; tim = (struct ieee80211_tim_ie *) pos; if (tim->dtim_count != 0) break; return tim->bitmap_ctrl & 0x01; } pos += elen; } return false; } static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); if (skb->len < 24 + 8 + 2 + 2) return; sc->ps_flags &= ~PS_WAIT_FOR_BEACON; if (sc->ps_flags & PS_BEACON_SYNC) { sc->ps_flags &= ~PS_BEACON_SYNC; ath_dbg(common, PS, "Reconfigure Beacon timers based on timestamp from the AP\n"); ath9k_set_beacon(sc); } if (ath_beacon_dtim_pending_cab(skb)) { /* * Remain awake waiting for buffered broadcast/multicast * frames. If the last broadcast/multicast frame is not * received properly, the next beacon frame will work as * a backup trigger for returning into NETWORK SLEEP state, * so we are waiting for it as well. */ ath_dbg(common, PS, "Received DTIM beacon indicating buffered broadcast/multicast frame(s)\n"); sc->ps_flags |= PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON; return; } if (sc->ps_flags & PS_WAIT_FOR_CAB) { /* * This can happen if a broadcast frame is dropped or the AP * fails to send a frame indicating that all CAB frames have * been delivered. */ sc->ps_flags &= ~PS_WAIT_FOR_CAB; ath_dbg(common, PS, "PS wait for CAB frames timed out\n"); } } static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon) { struct ieee80211_hdr *hdr; struct ath_common *common = ath9k_hw_common(sc->sc_ah); hdr = (struct ieee80211_hdr *)skb->data; /* Process Beacon and CAB receive in PS state */ if (((sc->ps_flags & PS_WAIT_FOR_BEACON) || ath9k_check_auto_sleep(sc)) && mybeacon) { ath_rx_ps_beacon(sc, skb); } else if ((sc->ps_flags & PS_WAIT_FOR_CAB) && (ieee80211_is_data(hdr->frame_control) || ieee80211_is_action(hdr->frame_control)) && is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_moredata(hdr->frame_control)) { /* * No more broadcast/multicast frames to be received at this * point. */ sc->ps_flags &= ~(PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON); ath_dbg(common, PS, "All PS CAB frames received, back to sleep\n"); } else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) && !is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_morefrags(hdr->frame_control)) { sc->ps_flags &= ~PS_WAIT_FOR_PSPOLL_DATA; ath_dbg(common, PS, "Going back to sleep after having received PS-Poll data (0x%lx)\n", sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK)); } } static bool ath_edma_get_buffers(struct ath_softc *sc, enum ath9k_rx_qtype qtype, struct ath_rx_status *rs, struct ath_buf **dest) { struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype]; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; struct ath_buf *bf; int ret; skb = skb_peek(&rx_edma->rx_fifo); if (!skb) return false; bf = SKB_CB_ATHBUF(skb); BUG_ON(!bf); dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr, common->rx_bufsize, DMA_FROM_DEVICE); ret = ath9k_hw_process_rxdesc_edma(ah, rs, skb->data); if (ret == -EINPROGRESS) { /*let device gain the buffer again*/ dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, common->rx_bufsize, DMA_FROM_DEVICE); return false; } __skb_unlink(skb, &rx_edma->rx_fifo); if (ret == -EINVAL) { /* corrupt descriptor, skip this one and the following one */ list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); skb = skb_peek(&rx_edma->rx_fifo); if (skb) { bf = SKB_CB_ATHBUF(skb); BUG_ON(!bf); __skb_unlink(skb, &rx_edma->rx_fifo); list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); } bf = NULL; } *dest = bf; return true; } static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc, struct ath_rx_status *rs, enum ath9k_rx_qtype qtype) { struct ath_buf *bf = NULL; while (ath_edma_get_buffers(sc, qtype, rs, &bf)) { if (!bf) continue; return bf; } return NULL; } static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, struct ath_rx_status *rs) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_desc *ds; struct ath_buf *bf; int ret; if (list_empty(&sc->rx.rxbuf)) { sc->rx.rxlink = NULL; return NULL; } bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); ds = bf->bf_desc; /* * Must provide the virtual address of the current * descriptor, the physical address, and the virtual * address of the next descriptor in the h/w chain. * This allows the HAL to look ahead to see if the * hardware is done with a descriptor by checking the * done bit in the following descriptor and the address * of the current descriptor the DMA engine is working * on. All this is necessary because of our use of * a self-linked list to avoid rx overruns. */ ret = ath9k_hw_rxprocdesc(ah, ds, rs); if (ret == -EINPROGRESS) { struct ath_rx_status trs; struct ath_buf *tbf; struct ath_desc *tds; memset(&trs, 0, sizeof(trs)); if (list_is_last(&bf->list, &sc->rx.rxbuf)) { sc->rx.rxlink = NULL; return NULL; } tbf = list_entry(bf->list.next, struct ath_buf, list); /* * On some hardware the descriptor status words could * get corrupted, including the done bit. Because of * this, check if the next descriptor's done bit is * set or not. * * If the next descriptor's done bit is set, the current * descriptor has been corrupted. Force s/w to discard * this descriptor and continue... */ tds = tbf->bf_desc; ret = ath9k_hw_rxprocdesc(ah, tds, &trs); if (ret == -EINPROGRESS) return NULL; } if (!bf->bf_mpdu) return bf; /* * Synchronize the DMA transfer with CPU before * 1. accessing the frame * 2. requeueing the same buffer to h/w */ dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr, common->rx_bufsize, DMA_FROM_DEVICE); return bf; } /* Assumes you've already done the endian to CPU conversion */ static bool ath9k_rx_accept(struct ath_common *common, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *rxs, struct ath_rx_status *rx_stats, bool *decrypt_error) { struct ath_softc *sc = (struct ath_softc *) common->priv; bool is_mc, is_valid_tkip, strip_mic, mic_error; struct ath_hw *ah = common->ah; __le16 fc; u8 rx_status_len = ah->caps.rx_status_len; fc = hdr->frame_control; is_mc = !!is_multicast_ether_addr(hdr->addr1); is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID && test_bit(rx_stats->rs_keyix, common->tkip_keymap); strip_mic = is_valid_tkip && ieee80211_is_data(fc) && ieee80211_has_protected(fc) && !(rx_stats->rs_status & (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC | ATH9K_RXERR_KEYMISS)); /* * Key miss events are only relevant for pairwise keys where the * descriptor does contain a valid key index. This has been observed * mostly with CCMP encryption. */ if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID || !test_bit(rx_stats->rs_keyix, common->ccmp_keymap)) rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; if (!rx_stats->rs_datalen) { RX_STAT_INC(rx_len_err); return false; } /* * rs_status follows rs_datalen so if rs_datalen is too large * we can take a hint that hardware corrupted it, so ignore * those frames. */ if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len)) { RX_STAT_INC(rx_len_err); return false; } /* Only use error bits from the last fragment */ if (rx_stats->rs_more) return true; mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) && !ieee80211_has_morefrags(fc) && !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) && (rx_stats->rs_status & ATH9K_RXERR_MIC); /* * The rx_stats->rs_status will not be set until the end of the * chained descriptors so it can be ignored if rs_more is set. The * rs_more will be false at the last element of the chained * descriptors. */ if (rx_stats->rs_status != 0) { u8 status_mask; if (rx_stats->rs_status & ATH9K_RXERR_CRC) { rxs->flag |= RX_FLAG_FAILED_FCS_CRC; mic_error = false; } if (rx_stats->rs_status & ATH9K_RXERR_PHY) return false; if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) || (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) { *decrypt_error = true; mic_error = false; } /* * Reject error frames with the exception of * decryption and MIC failures. For monitor mode, * we also ignore the CRC error. */ status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | ATH9K_RXERR_KEYMISS; if (ah->is_monitoring && (sc->rx.rxfilter & FIF_FCSFAIL)) status_mask |= ATH9K_RXERR_CRC; if (rx_stats->rs_status & ~status_mask) return false; } /* * For unicast frames the MIC error bit can have false positives, * so all MIC error reports need to be validated in software. * False negatives are not common, so skip software verification * if the hardware considers the MIC valid. */ if (strip_mic) rxs->flag |= RX_FLAG_MMIC_STRIPPED; else if (is_mc && mic_error) rxs->flag |= RX_FLAG_MMIC_ERROR; return true; } static int ath9k_process_rate(struct ath_common *common, struct ieee80211_hw *hw, struct ath_rx_status *rx_stats, struct ieee80211_rx_status *rxs) { struct ieee80211_supported_band *sband; enum ieee80211_band band; unsigned int i = 0; struct ath_softc __maybe_unused *sc = common->priv; band = hw->conf.channel->band; sband = hw->wiphy->bands[band]; if (rx_stats->rs_rate & 0x80) { /* HT rate */ rxs->flag |= RX_FLAG_HT; if (rx_stats->rs_flags & ATH9K_RX_2040) rxs->flag |= RX_FLAG_40MHZ; if (rx_stats->rs_flags & ATH9K_RX_GI) rxs->flag |= RX_FLAG_SHORT_GI; rxs->rate_idx = rx_stats->rs_rate & 0x7f; return 0; } for (i = 0; i < sband->n_bitrates; i++) { if (sband->bitrates[i].hw_value == rx_stats->rs_rate) { rxs->rate_idx = i; return 0; } if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) { rxs->flag |= RX_FLAG_SHORTPRE; rxs->rate_idx = i; return 0; } } /* * No valid hardware bitrate found -- we should not get here * because hardware has already validated this frame as OK. */ ath_dbg(common, ANY, "unsupported hw bitrate detected 0x%02x using 1 Mbit\n", rx_stats->rs_rate); RX_STAT_INC(rx_rate_err); return -EINVAL; } static void ath9k_process_rssi(struct ath_common *common, struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ath_rx_status *rx_stats) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = common->ah; int last_rssi; int rssi = rx_stats->rs_rssi; if (!rx_stats->is_mybeacon || ((ah->opmode != NL80211_IFTYPE_STATION) && (ah->opmode != NL80211_IFTYPE_ADHOC))) return; if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr) ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi); last_rssi = sc->last_rssi; if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); if (rssi < 0) rssi = 0; /* Update Beacon RSSI, this is used by ANI. */ ah->stats.avgbrssi = rssi; } /* * For Decrypt or Demic errors, we only mark packet status here and always push * up the frame up to let mac80211 handle the actual error case, be it no * decryption key or real decryption error. This let us keep statistics there. */ static int ath9k_rx_skb_preprocess(struct ath_common *common, struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ath_rx_status *rx_stats, struct ieee80211_rx_status *rx_status, bool *decrypt_error) { struct ath_hw *ah = common->ah; /* * everything but the rate is checked here, the rate check is done * separately to avoid doing two lookups for a rate for each frame. */ if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) return -EINVAL; /* Only use status info from the last fragment */ if (rx_stats->rs_more) return 0; ath9k_process_rssi(common, hw, hdr, rx_stats); if (ath9k_process_rate(common, hw, rx_stats, rx_status)) return -EINVAL; rx_status->band = hw->conf.channel->band; rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = ah->noise + rx_stats->rs_rssi; rx_status->antenna = rx_stats->rs_antenna; rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (rx_stats->rs_moreaggr) rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; return 0; } static void ath9k_rx_skb_postprocess(struct ath_common *common, struct sk_buff *skb, struct ath_rx_status *rx_stats, struct ieee80211_rx_status *rxs, bool decrypt_error) { struct ath_hw *ah = common->ah; struct ieee80211_hdr *hdr; int hdrlen, padpos, padsize; u8 keyix; __le16 fc; /* see if any padding is done by the hw and remove it */ hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_get_hdrlen_from_skb(skb); fc = hdr->frame_control; padpos = ath9k_cmn_padpos(hdr->frame_control); /* The MAC header is padded to have 32-bit boundary if the * packet payload is non-zero. The general calculation for * padsize would take into account odd header lengths: * padsize = (4 - padpos % 4) % 4; However, since only * even-length headers are used, padding can only be 0 or 2 * bytes and we can optimize this a bit. In addition, we must * not try to remove padding from short control frames that do * not have payload. */ padsize = padpos & 3; if (padsize && skb->len>=padpos+padsize+FCS_LEN) { memmove(skb->data + padsize, skb->data, padpos); skb_pull(skb, padsize); } keyix = rx_stats->rs_keyix; if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error && ieee80211_has_protected(fc)) { rxs->flag |= RX_FLAG_DECRYPTED; } else if (ieee80211_has_protected(fc) && !decrypt_error && skb->len >= hdrlen + 4) { keyix = skb->data[hdrlen + 3] >> 6; if (test_bit(keyix, common->keymap)) rxs->flag |= RX_FLAG_DECRYPTED; } if (ah->sw_mgmt_crypto && (rxs->flag & RX_FLAG_DECRYPTED) && ieee80211_is_mgmt(fc)) /* Use software decrypt for management frames. */ rxs->flag &= ~RX_FLAG_DECRYPTED; } int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) { struct ath_buf *bf; struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb; struct ieee80211_rx_status *rxs; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_hw *hw = sc->hw; struct ieee80211_hdr *hdr; int retval; struct ath_rx_status rs; enum ath9k_rx_qtype qtype; bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); int dma_type; u8 rx_status_len = ah->caps.rx_status_len; u64 tsf = 0; u32 tsf_lower = 0; unsigned long flags; if (edma) dma_type = DMA_BIDIRECTIONAL; else dma_type = DMA_FROM_DEVICE; qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP; spin_lock_bh(&sc->rx.rxbuflock); tsf = ath9k_hw_gettsf64(ah); tsf_lower = tsf & 0xffffffff; do { bool decrypt_error = false; /* If handling rx interrupt and flush is in progress => exit */ if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0)) break; memset(&rs, 0, sizeof(rs)); if (edma) bf = ath_edma_get_next_rx_buf(sc, &rs, qtype); else bf = ath_get_next_rx_buf(sc, &rs); if (!bf) break; skb = bf->bf_mpdu; if (!skb) continue; /* * Take frame header from the first fragment and RX status from * the last one. */ if (sc->rx.frag) hdr_skb = sc->rx.frag; else hdr_skb = skb; hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len); rxs = IEEE80211_SKB_RXCB(hdr_skb); if (ieee80211_is_beacon(hdr->frame_control)) { RX_STAT_INC(rx_beacons); if (!is_zero_ether_addr(common->curbssid) && ether_addr_equal(hdr->addr3, common->curbssid)) rs.is_mybeacon = true; else rs.is_mybeacon = false; } else rs.is_mybeacon = false; sc->rx.num_pkts++; ath_debug_stat_rx(sc, &rs); /* * If we're asked to flush receive queue, directly * chain it back at the queue without processing it. */ if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) { RX_STAT_INC(rx_drop_rxflush); goto requeue_drop_frag; } memset(rxs, 0, sizeof(struct ieee80211_rx_status)); rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; if (rs.rs_tstamp > tsf_lower && unlikely(rs.rs_tstamp - tsf_lower > 0x10000000)) rxs->mactime -= 0x100000000ULL; if (rs.rs_tstamp < tsf_lower && unlikely(tsf_lower - rs.rs_tstamp > 0x10000000)) rxs->mactime += 0x100000000ULL; retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, rxs, &decrypt_error); if (retval) goto requeue_drop_frag; if (rs.is_mybeacon) { sc->hw_busy_count = 0; ath_start_rx_poll(sc, 3); } /* Ensure we always have an skb to requeue once we are done * processing the current buffer's skb */ requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); /* If there is no memory we ignore the current RX'd frame, * tell hardware it can give us a new frame using the old * skb and put it at the tail of the sc->rx.rxbuf list for * processing. */ if (!requeue_skb) { RX_STAT_INC(rx_oom_err); goto requeue_drop_frag; } /* Unmap the frame */ dma_unmap_single(sc->dev, bf->bf_buf_addr, common->rx_bufsize, dma_type); skb_put(skb, rs.rs_datalen + ah->caps.rx_status_len); if (ah->caps.rx_status_len) skb_pull(skb, ah->caps.rx_status_len); if (!rs.rs_more) ath9k_rx_skb_postprocess(common, hdr_skb, &rs, rxs, decrypt_error); /* We will now give hardware our shiny new allocated skb */ bf->bf_mpdu = requeue_skb; bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data, common->rx_bufsize, dma_type); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(requeue_skb); bf->bf_mpdu = NULL; bf->bf_buf_addr = 0; ath_err(common, "dma_mapping_error() on RX\n"); ieee80211_rx(hw, skb); break; } if (rs.rs_more) { RX_STAT_INC(rx_frags); /* * rs_more indicates chained descriptors which can be * used to link buffers together for a sort of * scatter-gather operation. */ if (sc->rx.frag) { /* too many fragments - cannot handle frame */ dev_kfree_skb_any(sc->rx.frag); dev_kfree_skb_any(skb); RX_STAT_INC(rx_too_many_frags_err); skb = NULL; } sc->rx.frag = skb; goto requeue; } if (sc->rx.frag) { int space = skb->len - skb_tailroom(hdr_skb); if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) { dev_kfree_skb(skb); RX_STAT_INC(rx_oom_err); goto requeue_drop_frag; } sc->rx.frag = NULL; skb_copy_from_linear_data(skb, skb_put(hdr_skb, skb->len), skb->len); dev_kfree_skb_any(skb); skb = hdr_skb; } if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) { /* * change the default rx antenna if rx diversity * chooses the other antenna 3 times in a row. */ if (sc->rx.defant != rs.rs_antenna) { if (++sc->rx.rxotherant >= 3) ath_setdefantenna(sc, rs.rs_antenna); } else { sc->rx.rxotherant = 0; } } if (rxs->flag & RX_FLAG_MMIC_STRIPPED) skb_trim(skb, skb->len - 8); spin_lock_irqsave(&sc->sc_pm_lock, flags); if ((sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA)) || ath9k_check_auto_sleep(sc)) ath_rx_ps(sc, skb, rs.is_mybeacon); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3) ath_ant_comb_scan(sc, &rs); ieee80211_rx(hw, skb); requeue_drop_frag: if (sc->rx.frag) { dev_kfree_skb_any(sc->rx.frag); sc->rx.frag = NULL; } requeue: if (edma) { list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); } else { list_move_tail(&bf->list, &sc->rx.rxbuf); ath_rx_buf_link(sc, bf); if (!flush) ath9k_hw_rxena(ah); } } while (1); spin_unlock_bh(&sc->rx.rxbuflock); if (!(ah->imask & ATH9K_INT_RXEOL)) { ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN); ath9k_hw_set_interrupts(ah); } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/rc.c0000644000175000017500000012647612026211315023045 0ustar mcgrofmcgrof/* * Copyright (c) 2004 Video54 Technologies, Inc. * Copyright (c) 2004-2011 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "ath9k.h" static const struct ath_rate_table ar5416_11na_ratetable = { 68, 8, /* MCS start */ { [0] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000, 5400, 0, 12 }, /* 6 Mb */ [1] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000, 7800, 1, 18 }, /* 9 Mb */ [2] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, 10000, 2, 24 }, /* 12 Mb */ [3] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, 13900, 3, 36 }, /* 18 Mb */ [4] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, 17300, 4, 48 }, /* 24 Mb */ [5] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, 23000, 5, 72 }, /* 36 Mb */ [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, 27400, 6, 96 }, /* 48 Mb */ [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, 29300, 7, 108 }, /* 54 Mb */ [8] = { RC_HT_SDT_2040, WLAN_RC_PHY_HT_20_SS, 6500, 6400, 0, 0 }, /* 6.5 Mb */ [9] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000, 12700, 1, 1 }, /* 13 Mb */ [10] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500, 18800, 2, 2 }, /* 19.5 Mb */ [11] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000, 25000, 3, 3 }, /* 26 Mb */ [12] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000, 36700, 4, 4 }, /* 39 Mb */ [13] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000, 48100, 5, 5 }, /* 52 Mb */ [14] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500, 53500, 6, 6 }, /* 58.5 Mb */ [15] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000, 59000, 7, 7 }, /* 65 Mb */ [16] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200, 65400, 7, 7 }, /* 75 Mb */ [17] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000, 12700, 8, 8 }, /* 13 Mb */ [18] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000, 24800, 9, 9 }, /* 26 Mb */ [19] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000, 36600, 10, 10 }, /* 39 Mb */ [20] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000, 48100, 11, 11 }, /* 52 Mb */ [21] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000, 69500, 12, 12 }, /* 78 Mb */ [22] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000, 89500, 13, 13 }, /* 104 Mb */ [23] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000, 98900, 14, 14 }, /* 117 Mb */ [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000, 108300, 15, 15 }, /* 130 Mb */ [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400, 120000, 15, 15 }, /* 144.4 Mb */ [26] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500, 17400, 16, 16 }, /* 19.5 Mb */ [27] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000, 35100, 17, 17 }, /* 39 Mb */ [28] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500, 52600, 18, 18 }, /* 58.5 Mb */ [29] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000, 70400, 19, 19 }, /* 78 Mb */ [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000, 104900, 20, 20 }, /* 117 Mb */ [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000, 115800, 20, 20 }, /* 130 Mb*/ [32] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000, 137200, 21, 21 }, /* 156 Mb */ [33] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300, 151100, 21, 21 }, /* 173.3 Mb */ [34] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500, 152800, 22, 22 }, /* 175.5 Mb */ [35] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000, 168400, 22, 22 }, /* 195 Mb*/ [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000, 168400, 23, 23 }, /* 195 Mb */ [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700, 185000, 23, 23 }, /* 216.7 Mb */ [38] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500, 13200, 0, 0 }, /* 13.5 Mb*/ [39] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500, 25900, 1, 1 }, /* 27.0 Mb*/ [40] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500, 38600, 2, 2 }, /* 40.5 Mb*/ [41] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000, 49800, 3, 3 }, /* 54 Mb */ [42] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500, 72200, 4, 4 }, /* 81 Mb */ [43] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 108000, 92900, 5, 5 }, /* 108 Mb */ [44] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500, 102700, 6, 6 }, /* 121.5 Mb*/ [45] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000, 112000, 7, 7 }, /* 135 Mb */ [46] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, 122000, 7, 7 }, /* 150 Mb */ [47] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000, 25800, 8, 8 }, /* 27 Mb */ [48] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000, 49800, 9, 9 }, /* 54 Mb */ [49] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000, 71900, 10, 10 }, /* 81 Mb */ [50] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000, 92500, 11, 11 }, /* 108 Mb */ [51] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000, 130300, 12, 12 }, /* 162 Mb */ [52] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000, 162800, 13, 13 }, /* 216 Mb */ [53] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000, 178200, 14, 14 }, /* 243 Mb */ [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000, 192100, 15, 15 }, /* 270 Mb */ [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000, 207000, 15, 15 }, /* 300 Mb */ [56] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500, 36100, 16, 16 }, /* 40.5 Mb */ [57] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000, 72900, 17, 17 }, /* 81 Mb */ [58] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500, 108300, 18, 18 }, /* 121.5 Mb */ [59] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000, 142000, 19, 19 }, /* 162 Mb */ [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000, 205100, 20, 20 }, /* 243 Mb */ [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000, 224700, 20, 20 }, /* 270 Mb */ [62] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000, 263100, 21, 21 }, /* 324 Mb */ [63] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000, 288000, 21, 21 }, /* 360 Mb */ [64] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500, 290700, 22, 22 }, /* 364.5 Mb */ [65] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000, 317200, 22, 22 }, /* 405 Mb */ [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000, 317200, 23, 23 }, /* 405 Mb */ [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000, 346400, 23, 23 }, /* 450 Mb */ }, 50, /* probe interval */ WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ }; /* 4ms frame limit not used for NG mode. The values filled * for HT are the 64K max aggregate limit */ static const struct ath_rate_table ar5416_11ng_ratetable = { 72, 12, /* MCS start */ { [0] = { RC_ALL, WLAN_RC_PHY_CCK, 1000, 900, 0, 2 }, /* 1 Mb */ [1] = { RC_ALL, WLAN_RC_PHY_CCK, 2000, 1900, 1, 4 }, /* 2 Mb */ [2] = { RC_ALL, WLAN_RC_PHY_CCK, 5500, 4900, 2, 11 }, /* 5.5 Mb */ [3] = { RC_ALL, WLAN_RC_PHY_CCK, 11000, 8100, 3, 22 }, /* 11 Mb */ [4] = { RC_INVALID, WLAN_RC_PHY_OFDM, 6000, 5400, 4, 12 }, /* 6 Mb */ [5] = { RC_INVALID, WLAN_RC_PHY_OFDM, 9000, 7800, 5, 18 }, /* 9 Mb */ [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, 10100, 6, 24 }, /* 12 Mb */ [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, 14100, 7, 36 }, /* 18 Mb */ [8] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, 17700, 8, 48 }, /* 24 Mb */ [9] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, 23700, 9, 72 }, /* 36 Mb */ [10] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, 27400, 10, 96 }, /* 48 Mb */ [11] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, 30900, 11, 108 }, /* 54 Mb */ [12] = { RC_INVALID, WLAN_RC_PHY_HT_20_SS, 6500, 6400, 0, 0 }, /* 6.5 Mb */ [13] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000, 12700, 1, 1 }, /* 13 Mb */ [14] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500, 18800, 2, 2 }, /* 19.5 Mb*/ [15] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000, 25000, 3, 3 }, /* 26 Mb */ [16] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000, 36700, 4, 4 }, /* 39 Mb */ [17] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000, 48100, 5, 5 }, /* 52 Mb */ [18] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500, 53500, 6, 6 }, /* 58.5 Mb */ [19] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000, 59000, 7, 7 }, /* 65 Mb */ [20] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200, 65400, 7, 7 }, /* 65 Mb*/ [21] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000, 12700, 8, 8 }, /* 13 Mb */ [22] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000, 24800, 9, 9 }, /* 26 Mb */ [23] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000, 36600, 10, 10 }, /* 39 Mb */ [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000, 48100, 11, 11 }, /* 52 Mb */ [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000, 69500, 12, 12 }, /* 78 Mb */ [26] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000, 89500, 13, 13 }, /* 104 Mb */ [27] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000, 98900, 14, 14 }, /* 117 Mb */ [28] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000, 108300, 15, 15 }, /* 130 Mb */ [29] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400, 120000, 15, 15 }, /* 144.4 Mb */ [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500, 17400, 16, 16 }, /* 19.5 Mb */ [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000, 35100, 17, 17 }, /* 39 Mb */ [32] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500, 52600, 18, 18 }, /* 58.5 Mb */ [33] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000, 70400, 19, 19 }, /* 78 Mb */ [34] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000, 104900, 20, 20 }, /* 117 Mb */ [35] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000, 115800, 20, 20 }, /* 130 Mb */ [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000, 137200, 21, 21 }, /* 156 Mb */ [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300, 151100, 21, 21 }, /* 173.3 Mb */ [38] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500, 152800, 22, 22 }, /* 175.5 Mb */ [39] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000, 168400, 22, 22 }, /* 195 Mb */ [40] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000, 168400, 23, 23 }, /* 195 Mb */ [41] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700, 185000, 23, 23 }, /* 216.7 Mb */ [42] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500, 13200, 0, 0 }, /* 13.5 Mb */ [43] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500, 25900, 1, 1 }, /* 27.0 Mb */ [44] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500, 38600, 2, 2 }, /* 40.5 Mb */ [45] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000, 49800, 3, 3 }, /* 54 Mb */ [46] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500, 72200, 4, 4 }, /* 81 Mb */ [47] = { RC_HT_S_40 , WLAN_RC_PHY_HT_40_SS, 108000, 92900, 5, 5 }, /* 108 Mb */ [48] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500, 102700, 6, 6 }, /* 121.5 Mb */ [49] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000, 112000, 7, 7 }, /* 135 Mb */ [50] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, 122000, 7, 7 }, /* 150 Mb */ [51] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000, 25800, 8, 8 }, /* 27 Mb */ [52] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000, 49800, 9, 9 }, /* 54 Mb */ [53] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000, 71900, 10, 10 }, /* 81 Mb */ [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000, 92500, 11, 11 }, /* 108 Mb */ [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000, 130300, 12, 12 }, /* 162 Mb */ [56] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000, 162800, 13, 13 }, /* 216 Mb */ [57] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000, 178200, 14, 14 }, /* 243 Mb */ [58] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000, 192100, 15, 15 }, /* 270 Mb */ [59] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000, 207000, 15, 15 }, /* 300 Mb */ [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500, 36100, 16, 16 }, /* 40.5 Mb */ [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000, 72900, 17, 17 }, /* 81 Mb */ [62] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500, 108300, 18, 18 }, /* 121.5 Mb */ [63] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000, 142000, 19, 19 }, /* 162 Mb */ [64] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000, 205100, 20, 20 }, /* 243 Mb */ [65] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000, 224700, 20, 20 }, /* 270 Mb */ [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000, 263100, 21, 21 }, /* 324 Mb */ [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000, 288000, 21, 21 }, /* 360 Mb */ [68] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500, 290700, 22, 22 }, /* 364.5 Mb */ [69] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000, 317200, 22, 22 }, /* 405 Mb */ [70] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000, 317200, 23, 23 }, /* 405 Mb */ [71] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000, 346400, 23, 23 }, /* 450 Mb */ }, 50, /* probe interval */ WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ }; static const struct ath_rate_table ar5416_11a_ratetable = { 8, 0, { { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ 5400, 0, 12}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ 7800, 1, 18}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ 10000, 2, 24}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ 13900, 3, 36}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ 17300, 4, 48}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ 23000, 5, 72}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ 27400, 6, 96}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ 29300, 7, 108}, }, 50, /* probe interval */ 0, /* Phy rates allowed initially */ }; static const struct ath_rate_table ar5416_11g_ratetable = { 12, 0, { { RC_L_SDT, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ 900, 0, 2}, { RC_L_SDT, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ 1900, 1, 4}, { RC_L_SDT, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ 4900, 2, 11}, { RC_L_SDT, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ 8100, 3, 22}, { RC_INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ 5400, 4, 12}, { RC_INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ 7800, 5, 18}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ 10000, 6, 24}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ 13900, 7, 36}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ 17300, 8, 48}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ 23000, 9, 72}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ 27400, 10, 96}, { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ 29300, 11, 108}, }, 50, /* probe interval */ 0, /* Phy rates allowed initially */ }; static int ath_rc_get_rateindex(struct ath_rate_priv *ath_rc_priv, struct ieee80211_tx_rate *rate) { const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; int rix, i, idx = 0; if (!(rate->flags & IEEE80211_TX_RC_MCS)) return rate->idx; for (i = 0; i < ath_rc_priv->max_valid_rate; i++) { idx = ath_rc_priv->valid_rate_index[i]; if (WLAN_RC_PHY_HT(rate_table->info[idx].phy) && rate_table->info[idx].ratecode == rate->idx) break; } rix = idx; if (rate->flags & IEEE80211_TX_RC_SHORT_GI) rix++; return rix; } static void ath_rc_sort_validrates(struct ath_rate_priv *ath_rc_priv) { const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; u8 i, j, idx, idx_next; for (i = ath_rc_priv->max_valid_rate - 1; i > 0; i--) { for (j = 0; j <= i-1; j++) { idx = ath_rc_priv->valid_rate_index[j]; idx_next = ath_rc_priv->valid_rate_index[j+1]; if (rate_table->info[idx].ratekbps > rate_table->info[idx_next].ratekbps) { ath_rc_priv->valid_rate_index[j] = idx_next; ath_rc_priv->valid_rate_index[j+1] = idx; } } } } static inline int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table, struct ath_rate_priv *ath_rc_priv, u8 cur_valid_txrate, u8 *next_idx) { u8 i; for (i = 0; i < ath_rc_priv->max_valid_rate - 1; i++) { if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) { *next_idx = ath_rc_priv->valid_rate_index[i+1]; return 1; } } /* No more valid rates */ *next_idx = 0; return 0; } /* Return true only for single stream */ static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) { if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG)) return 0; if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG)) return 0; if (WLAN_RC_PHY_TS(phy) && !(capflag & WLAN_RC_TS_FLAG)) return 0; if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG)) return 0; if (!ignore_cw && WLAN_RC_PHY_HT(phy)) if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG)) return 0; return 1; } static inline int ath_rc_get_lower_rix(struct ath_rate_priv *ath_rc_priv, u8 cur_valid_txrate, u8 *next_idx) { int8_t i; for (i = 1; i < ath_rc_priv->max_valid_rate ; i++) { if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) { *next_idx = ath_rc_priv->valid_rate_index[i-1]; return 1; } } return 0; } static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv) { const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; u8 i, hi = 0; for (i = 0; i < rate_table->rate_cnt; i++) { if (rate_table->info[i].rate_flags & RC_LEGACY) { u32 phy = rate_table->info[i].phy; u8 valid_rate_count = 0; if (!ath_rc_valid_phyrate(phy, ath_rc_priv->ht_cap, 0)) continue; valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy]; ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = i; ath_rc_priv->valid_phy_ratecnt[phy] += 1; ath_rc_priv->valid_rate_index[i] = true; hi = i; } } return hi; } static inline bool ath_rc_check_legacy(u8 rate, u8 dot11rate, u16 rate_flags, u32 phy, u32 capflag) { if (rate != dot11rate || WLAN_RC_PHY_HT(phy)) return false; if ((rate_flags & WLAN_RC_CAP_MODE(capflag)) != WLAN_RC_CAP_MODE(capflag)) return false; if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag))) return false; return true; } static inline bool ath_rc_check_ht(u8 rate, u8 dot11rate, u16 rate_flags, u32 phy, u32 capflag) { if (rate != dot11rate || !WLAN_RC_PHY_HT(phy)) return false; if (!WLAN_RC_PHY_HT_VALID(rate_flags, capflag)) return false; if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag))) return false; return true; } static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, bool legacy) { const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; struct ath_rateset *rateset; u32 phy, capflag = ath_rc_priv->ht_cap; u16 rate_flags; u8 i, j, hi = 0, rate, dot11rate, valid_rate_count; if (legacy) rateset = &ath_rc_priv->neg_rates; else rateset = &ath_rc_priv->neg_ht_rates; for (i = 0; i < rateset->rs_nrates; i++) { for (j = 0; j < rate_table->rate_cnt; j++) { phy = rate_table->info[j].phy; rate_flags = rate_table->info[j].rate_flags; rate = rateset->rs_rates[i]; dot11rate = rate_table->info[j].dot11rate; if (legacy && !ath_rc_check_legacy(rate, dot11rate, rate_flags, phy, capflag)) continue; if (!legacy && !ath_rc_check_ht(rate, dot11rate, rate_flags, phy, capflag)) continue; if (!ath_rc_valid_phyrate(phy, capflag, 0)) continue; valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy]; ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = j; ath_rc_priv->valid_phy_ratecnt[phy] += 1; ath_rc_priv->valid_rate_index[j] = true; hi = max(hi, j); } } return hi; } static u8 ath_rc_get_highest_rix(struct ath_rate_priv *ath_rc_priv, int *is_probing) { const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; u32 best_thruput, this_thruput, now_msec; u8 rate, next_rate, best_rate, maxindex, minindex; int8_t index = 0; now_msec = jiffies_to_msecs(jiffies); *is_probing = 0; best_thruput = 0; maxindex = ath_rc_priv->max_valid_rate-1; minindex = 0; best_rate = minindex; /* * Try the higher rate first. It will reduce memory moving time * if we have very good channel characteristics. */ for (index = maxindex; index >= minindex ; index--) { u8 per_thres; rate = ath_rc_priv->valid_rate_index[index]; if (rate > ath_rc_priv->rate_max_phy) continue; /* * For TCP the average collision rate is around 11%, * so we ignore PERs less than this. This is to * prevent the rate we are currently using (whose * PER might be in the 10-15 range because of TCP * collisions) looking worse than the next lower * rate whose PER has decayed close to 0. If we * used to next lower rate, its PER would grow to * 10-15 and we would be worse off then staying * at the current rate. */ per_thres = ath_rc_priv->per[rate]; if (per_thres < 12) per_thres = 12; this_thruput = rate_table->info[rate].user_ratekbps * (100 - per_thres); if (best_thruput <= this_thruput) { best_thruput = this_thruput; best_rate = rate; } } rate = best_rate; /* * Must check the actual rate (ratekbps) to account for * non-monoticity of 11g's rate table */ if (rate >= ath_rc_priv->rate_max_phy) { rate = ath_rc_priv->rate_max_phy; /* Probe the next allowed phy state */ if (ath_rc_get_nextvalid_txrate(rate_table, ath_rc_priv, rate, &next_rate) && (now_msec - ath_rc_priv->probe_time > rate_table->probe_interval) && (ath_rc_priv->hw_maxretry_pktcnt >= 1)) { rate = next_rate; ath_rc_priv->probe_rate = rate; ath_rc_priv->probe_time = now_msec; ath_rc_priv->hw_maxretry_pktcnt = 0; *is_probing = 1; } } if (rate > (ath_rc_priv->rate_table_size - 1)) rate = ath_rc_priv->rate_table_size - 1; if (RC_TS_ONLY(rate_table->info[rate].rate_flags) && (ath_rc_priv->ht_cap & WLAN_RC_TS_FLAG)) return rate; if (RC_DS_OR_LATER(rate_table->info[rate].rate_flags) && (ath_rc_priv->ht_cap & (WLAN_RC_DS_FLAG | WLAN_RC_TS_FLAG))) return rate; if (RC_SS_OR_LEGACY(rate_table->info[rate].rate_flags)) return rate; /* This should not happen */ WARN_ON_ONCE(1); rate = ath_rc_priv->valid_rate_index[0]; return rate; } static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table, struct ieee80211_tx_rate *rate, struct ieee80211_tx_rate_control *txrc, u8 tries, u8 rix, int rtsctsenable) { rate->count = tries; rate->idx = rate_table->info[rix].ratecode; if (txrc->rts || rtsctsenable) rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) { rate->flags |= IEEE80211_TX_RC_MCS; if (WLAN_RC_PHY_40(rate_table->info[rix].phy) && conf_is_ht40(&txrc->hw->conf)) rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy)) rate->flags |= IEEE80211_TX_RC_SHORT_GI; } } static void ath_rc_rate_set_rtscts(struct ath_softc *sc, const struct ath_rate_table *rate_table, struct ieee80211_tx_info *tx_info) { struct ieee80211_bss_conf *bss_conf; if (!tx_info->control.vif) return; /* * For legacy frames, mac80211 takes care of CTS protection. */ if (!(tx_info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) return; bss_conf = &tx_info->control.vif->bss_conf; if (!bss_conf->basic_rates) return; /* * For now, use the lowest allowed basic rate for HT frames. */ tx_info->control.rts_cts_rate_idx = __ffs(bss_conf->basic_rates); } static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; const struct ath_rate_table *rate_table; struct sk_buff *skb = txrc->skb; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *rates = tx_info->control.rates; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 fc = hdr->frame_control; u8 try_per_rate, i = 0, rix; int is_probe = 0; if (rate_control_send_low(sta, priv_sta, txrc)) return; /* * For Multi Rate Retry we use a different number of * retry attempt counts. This ends up looking like this: * * MRR[0] = 4 * MRR[1] = 4 * MRR[2] = 4 * MRR[3] = 8 * */ try_per_rate = 4; rate_table = ath_rc_priv->rate_table; rix = ath_rc_get_highest_rix(ath_rc_priv, &is_probe); if (conf_is_ht(&sc->hw->conf) && (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) tx_info->flags |= IEEE80211_TX_CTL_LDPC; if (conf_is_ht(&sc->hw->conf) && (sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) tx_info->flags |= (1 << IEEE80211_TX_CTL_STBC_SHIFT); if (is_probe) { /* * Set one try for probe rates. For the * probes don't enable RTS. */ ath_rc_rate_set_series(rate_table, &rates[i++], txrc, 1, rix, 0); /* * Get the next tried/allowed rate. * No RTS for the next series after the probe rate. */ ath_rc_get_lower_rix(ath_rc_priv, rix, &rix); ath_rc_rate_set_series(rate_table, &rates[i++], txrc, try_per_rate, rix, 0); tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { /* * Set the chosen rate. No RTS for first series entry. */ ath_rc_rate_set_series(rate_table, &rates[i++], txrc, try_per_rate, rix, 0); } for ( ; i < 4; i++) { /* * Use twice the number of tries for the last MRR segment. */ if (i + 1 == 4) try_per_rate = 8; ath_rc_get_lower_rix(ath_rc_priv, rix, &rix); /* * All other rates in the series have RTS enabled. */ ath_rc_rate_set_series(rate_table, &rates[i], txrc, try_per_rate, rix, 1); } /* * NB:Change rate series to enable aggregation when operating * at lower MCS rates. When first rate in series is MCS2 * in HT40 @ 2.4GHz, series should look like: * * {MCS2, MCS1, MCS0, MCS0}. * * When first rate in series is MCS3 in HT20 @ 2.4GHz, series should * look like: * * {MCS3, MCS2, MCS1, MCS1} * * So, set fourth rate in series to be same as third one for * above conditions. */ if ((sc->hw->conf.channel->band == IEEE80211_BAND_2GHZ) && (conf_is_ht(&sc->hw->conf))) { u8 dot11rate = rate_table->info[rix].dot11rate; u8 phy = rate_table->info[rix].phy; if (i == 4 && ((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) || (dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) { rates[3].idx = rates[2].idx; rates[3].flags = rates[2].flags; } } /* * Force hardware to use computed duration for next * fragment by disabling multi-rate retry, which * updates duration based on the multi-rate duration table. * * FIXME: Fix duration */ if (ieee80211_has_morefrags(fc) || (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) { rates[1].count = rates[2].count = rates[3].count = 0; rates[1].idx = rates[2].idx = rates[3].idx = 0; rates[0].count = ATH_TXMAXTRY; } ath_rc_rate_set_rtscts(sc, rate_table, tx_info); } static void ath_rc_update_per(struct ath_softc *sc, const struct ath_rate_table *rate_table, struct ath_rate_priv *ath_rc_priv, struct ieee80211_tx_info *tx_info, int tx_rate, int xretries, int retries, u32 now_msec) { int count, n_bad_frames; u8 last_per; static const u32 nretry_to_per_lookup[10] = { 100 * 0 / 1, 100 * 1 / 4, 100 * 1 / 2, 100 * 3 / 4, 100 * 4 / 5, 100 * 5 / 6, 100 * 6 / 7, 100 * 7 / 8, 100 * 8 / 9, 100 * 9 / 10 }; last_per = ath_rc_priv->per[tx_rate]; n_bad_frames = tx_info->status.ampdu_len - tx_info->status.ampdu_ack_len; if (xretries) { if (xretries == 1) { ath_rc_priv->per[tx_rate] += 30; if (ath_rc_priv->per[tx_rate] > 100) ath_rc_priv->per[tx_rate] = 100; } else { /* xretries == 2 */ count = ARRAY_SIZE(nretry_to_per_lookup); if (retries >= count) retries = count - 1; /* new_PER = 7/8*old_PER + 1/8*(currentPER) */ ath_rc_priv->per[tx_rate] = (u8)(last_per - (last_per >> 3) + (100 >> 3)); } /* xretries == 1 or 2 */ if (ath_rc_priv->probe_rate == tx_rate) ath_rc_priv->probe_rate = 0; } else { /* xretries == 0 */ count = ARRAY_SIZE(nretry_to_per_lookup); if (retries >= count) retries = count - 1; if (n_bad_frames) { /* new_PER = 7/8*old_PER + 1/8*(currentPER) * Assuming that n_frames is not 0. The current PER * from the retries is 100 * retries / (retries+1), * since the first retries attempts failed, and the * next one worked. For the one that worked, * n_bad_frames subframes out of n_frames wored, * so the PER for that part is * 100 * n_bad_frames / n_frames, and it contributes * 100 * n_bad_frames / (n_frames * (retries+1)) to * the above PER. The expression below is a * simplified version of the sum of these two terms. */ if (tx_info->status.ampdu_len > 0) { int n_frames, n_bad_tries; u8 cur_per, new_per; n_bad_tries = retries * tx_info->status.ampdu_len + n_bad_frames; n_frames = tx_info->status.ampdu_len * (retries + 1); cur_per = (100 * n_bad_tries / n_frames) >> 3; new_per = (u8)(last_per - (last_per >> 3) + cur_per); ath_rc_priv->per[tx_rate] = new_per; } } else { ath_rc_priv->per[tx_rate] = (u8)(last_per - (last_per >> 3) + (nretry_to_per_lookup[retries] >> 3)); } /* * If we got at most one retry then increase the max rate if * this was a probe. Otherwise, ignore the probe. */ if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) { if (retries > 0 || 2 * n_bad_frames > tx_info->status.ampdu_len) { /* * Since we probed with just a single attempt, * any retries means the probe failed. Also, * if the attempt worked, but more than half * the subframes were bad then also consider * the probe a failure. */ ath_rc_priv->probe_rate = 0; } else { u8 probe_rate = 0; ath_rc_priv->rate_max_phy = ath_rc_priv->probe_rate; probe_rate = ath_rc_priv->probe_rate; if (ath_rc_priv->per[probe_rate] > 30) ath_rc_priv->per[probe_rate] = 20; ath_rc_priv->probe_rate = 0; /* * Since this probe succeeded, we allow the next * probe twice as soon. This allows the maxRate * to move up faster if the probes are * successful. */ ath_rc_priv->probe_time = now_msec - rate_table->probe_interval / 2; } } if (retries > 0) { /* * Don't update anything. We don't know if * this was because of collisions or poor signal. */ ath_rc_priv->hw_maxretry_pktcnt = 0; } else { /* * It worked with no retries. First ignore bogus (small) * rssi_ack values. */ if (tx_rate == ath_rc_priv->rate_max_phy && ath_rc_priv->hw_maxretry_pktcnt < 255) { ath_rc_priv->hw_maxretry_pktcnt++; } } } } static void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, int xretries, int retries, u8 per) { struct ath_rc_stats *stats = &rc->rcstats[rix]; stats->xretries += xretries; stats->retries += retries; stats->per = per; } static void ath_rc_update_ht(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct ieee80211_tx_info *tx_info, int tx_rate, int xretries, int retries) { u32 now_msec = jiffies_to_msecs(jiffies); int rate; u8 last_per; const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; int size = ath_rc_priv->rate_table_size; if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt)) return; last_per = ath_rc_priv->per[tx_rate]; /* Update PER first */ ath_rc_update_per(sc, rate_table, ath_rc_priv, tx_info, tx_rate, xretries, retries, now_msec); /* * If this rate looks bad (high PER) then stop using it for * a while (except if we are probing). */ if (ath_rc_priv->per[tx_rate] >= 55 && tx_rate > 0 && rate_table->info[tx_rate].ratekbps <= rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) { ath_rc_get_lower_rix(ath_rc_priv, (u8)tx_rate, &ath_rc_priv->rate_max_phy); /* Don't probe for a little while. */ ath_rc_priv->probe_time = now_msec; } /* Make sure the rates below this have lower PER */ /* Monotonicity is kept only for rates below the current rate. */ if (ath_rc_priv->per[tx_rate] < last_per) { for (rate = tx_rate - 1; rate >= 0; rate--) { if (ath_rc_priv->per[rate] > ath_rc_priv->per[rate+1]) { ath_rc_priv->per[rate] = ath_rc_priv->per[rate+1]; } } } /* Maintain monotonicity for rates above the current rate */ for (rate = tx_rate; rate < size - 1; rate++) { if (ath_rc_priv->per[rate+1] < ath_rc_priv->per[rate]) ath_rc_priv->per[rate+1] = ath_rc_priv->per[rate]; } /* Every so often, we reduce the thresholds * and PER (different for CCK and OFDM). */ if (now_msec - ath_rc_priv->per_down_time >= rate_table->probe_interval) { for (rate = 0; rate < size; rate++) { ath_rc_priv->per[rate] = 7 * ath_rc_priv->per[rate] / 8; } ath_rc_priv->per_down_time = now_msec; } ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries, ath_rc_priv->per[tx_rate]); } static void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) { struct ath_rc_stats *stats; stats = &rc->rcstats[final_rate]; stats->success++; } static void ath_rc_tx_status(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *rates = tx_info->status.rates; struct ieee80211_tx_rate *rate; int final_ts_idx = 0, xretries = 0, long_retry = 0; u8 flags; u32 i = 0, rix; for (i = 0; i < sc->hw->max_rates; i++) { rate = &tx_info->status.rates[i]; if (rate->idx < 0 || !rate->count) break; final_ts_idx = i; long_retry = rate->count - 1; } if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) xretries = 1; /* * If the first rate is not the final index, there * are intermediate rate failures to be processed. */ if (final_ts_idx != 0) { for (i = 0; i < final_ts_idx ; i++) { if (rates[i].count != 0 && (rates[i].idx >= 0)) { flags = rates[i].flags; /* If HT40 and we have switched mode from * 40 to 20 => don't update */ if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG)) return; rix = ath_rc_get_rateindex(ath_rc_priv, &rates[i]); ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries ? 1 : 2, rates[i].count); } } } flags = rates[final_ts_idx].flags; /* If HT40 and we have switched mode from 40 to 20 => don't update */ if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG)) return; rix = ath_rc_get_rateindex(ath_rc_priv, &rates[final_ts_idx]); ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry); ath_debug_stat_rc(ath_rc_priv, rix); } static const struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, enum ieee80211_band band, bool is_ht) { switch(band) { case IEEE80211_BAND_2GHZ: if (is_ht) return &ar5416_11ng_ratetable; return &ar5416_11g_ratetable; case IEEE80211_BAND_5GHZ: if (is_ht) return &ar5416_11na_ratetable; return &ar5416_11a_ratetable; default: return NULL; } } static void ath_rc_init(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv) { const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; struct ath_rateset *rateset = &ath_rc_priv->neg_rates; struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 i, j, k, hi = 0, hthi = 0; ath_rc_priv->rate_table_size = RATE_TABLE_SIZE; for (i = 0 ; i < ath_rc_priv->rate_table_size; i++) { ath_rc_priv->per[i] = 0; ath_rc_priv->valid_rate_index[i] = 0; } for (i = 0; i < WLAN_RC_PHY_MAX; i++) { for (j = 0; j < RATE_TABLE_SIZE; j++) ath_rc_priv->valid_phy_rateidx[i][j] = 0; ath_rc_priv->valid_phy_ratecnt[i] = 0; } if (!rateset->rs_nrates) { hi = ath_rc_init_validrates(ath_rc_priv); } else { hi = ath_rc_setvalid_rates(ath_rc_priv, true); if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG) hthi = ath_rc_setvalid_rates(ath_rc_priv, false); hi = max(hi, hthi); } ath_rc_priv->rate_table_size = hi + 1; ath_rc_priv->rate_max_phy = 0; WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE); for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) { for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) { ath_rc_priv->valid_rate_index[k++] = ath_rc_priv->valid_phy_rateidx[i][j]; } if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, 1) || !ath_rc_priv->valid_phy_ratecnt[i]) continue; ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1]; } WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE); WARN_ON(k > RATE_TABLE_SIZE); ath_rc_priv->max_valid_rate = k; ath_rc_sort_validrates(ath_rc_priv); ath_rc_priv->rate_max_phy = (k > 4) ? ath_rc_priv->valid_rate_index[k-4] : ath_rc_priv->valid_rate_index[k-1]; ath_dbg(common, CONFIG, "RC Initialized with capabilities: 0x%x\n", ath_rc_priv->ht_cap); } static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta) { u8 caps = 0; if (sta->ht_cap.ht_supported) { caps = WLAN_RC_HT_FLAG; if (sta->ht_cap.mcs.rx_mask[1] && sta->ht_cap.mcs.rx_mask[2]) caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG; else if (sta->ht_cap.mcs.rx_mask[1]) caps |= WLAN_RC_DS_FLAG; if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) caps |= WLAN_RC_40_FLAG; if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40 || sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) caps |= WLAN_RC_SGI_FLAG; } return caps; } static bool ath_tx_aggr_check(struct ath_softc *sc, struct ieee80211_sta *sta, u8 tidno) { struct ath_node *an = (struct ath_node *)sta->drv_priv; struct ath_atx_tid *txtid; if (!sta->ht_cap.ht_supported) return false; txtid = ATH_AN_2_TID(an, tidno); if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS))) return true; return false; } /***********************************/ /* mac80211 Rate Control callbacks */ /***********************************/ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 fc = hdr->frame_control; if (!priv_sta || !ieee80211_is_data(fc)) return; /* This packet was aggregated but doesn't carry status info */ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && !(tx_info->flags & IEEE80211_TX_STAT_AMPDU)) return; if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) return; ath_rc_tx_status(sc, ath_rc_priv, skb); /* Check if aggregation has to be enabled for this tid */ if (conf_is_ht(&sc->hw->conf) && !(skb->protocol == cpu_to_be16(ETH_P_PAE))) { if (ieee80211_is_data_qos(fc) && skb_get_queue_mapping(skb) != IEEE80211_AC_VO) { u8 *qc, tid; qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; if(ath_tx_aggr_check(sc, sta, tid)) ieee80211_start_tx_ba_session(sta, tid, 0); } } } static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { struct ath_softc *sc = priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_rate_priv *ath_rc_priv = priv_sta; int i, j = 0; for (i = 0; i < sband->n_bitrates; i++) { if (sta->supp_rates[sband->band] & BIT(i)) { ath_rc_priv->neg_rates.rs_rates[j] = (sband->bitrates[i].bitrate * 2) / 10; j++; } } ath_rc_priv->neg_rates.rs_nrates = j; if (sta->ht_cap.ht_supported) { for (i = 0, j = 0; i < 77; i++) { if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) ath_rc_priv->neg_ht_rates.rs_rates[j++] = i; if (j == ATH_RATE_MAX) break; } ath_rc_priv->neg_ht_rates.rs_nrates = j; } ath_rc_priv->rate_table = ath_choose_rate_table(sc, sband->band, sta->ht_cap.ht_supported); if (!ath_rc_priv->rate_table) { ath_err(common, "No rate table chosen\n"); return; } ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta); ath_rc_init(sc, priv_sta); } static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, u32 changed) { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; if (changed & IEEE80211_RC_BW_CHANGED) { ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta); ath_rc_init(sc, priv_sta); ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, "Operating HT Bandwidth changed to: %d\n", sc->hw->conf.channel_type); } } #ifdef CONFIG_ATH9K_DEBUGFS static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_rate_priv *rc = file->private_data; char *buf; unsigned int len = 0, max; int rix; ssize_t retval; if (rc->rate_table == NULL) return 0; max = 80 + rc->rate_table_size * 1024 + 1; buf = kmalloc(max, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += sprintf(buf, "%6s %6s %6s " "%10s %10s %10s %10s\n", "HT", "MCS", "Rate", "Success", "Retries", "XRetries", "PER"); for (rix = 0; rix < rc->max_valid_rate; rix++) { u8 i = rc->valid_rate_index[rix]; u32 ratekbps = rc->rate_table->info[i].ratekbps; struct ath_rc_stats *stats = &rc->rcstats[i]; char mcs[5]; char htmode[5]; int used_mcs = 0, used_htmode = 0; if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) { used_mcs = snprintf(mcs, 5, "%d", rc->rate_table->info[i].ratecode); if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy)) used_htmode = snprintf(htmode, 5, "HT40"); else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy)) used_htmode = snprintf(htmode, 5, "HT20"); else used_htmode = snprintf(htmode, 5, "????"); } mcs[used_mcs] = '\0'; htmode[used_htmode] = '\0'; len += snprintf(buf + len, max - len, "%6s %6s %3u.%d: " "%10u %10u %10u %10u\n", htmode, mcs, ratekbps / 1000, (ratekbps % 1000) / 100, stats->success, stats->retries, stats->xretries, stats->per); } if (len > max) len = max; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static const struct file_operations fops_rcstat = { .read = read_file_rcstat, .open = simple_open, .owner = THIS_MODULE }; static void ath_rate_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) { struct ath_rate_priv *rc = priv_sta; debugfs_create_file("rc_stats", S_IRUGO, dir, rc, &fops_rcstat); } #endif /* CONFIG_ATH9K_DEBUGFS */ static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { return hw->priv; } static void ath_rate_free(void *priv) { return; } static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) { struct ath_softc *sc = priv; struct ath_rate_priv *rate_priv; rate_priv = kzalloc(sizeof(struct ath_rate_priv), gfp); if (!rate_priv) { ath_err(ath9k_hw_common(sc->sc_ah), "Unable to allocate private rc structure\n"); return NULL; } return rate_priv; } static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) { struct ath_rate_priv *rate_priv = priv_sta; kfree(rate_priv); } static struct rate_control_ops ath_rate_ops = { .module = NULL, .name = "ath9k_rate_control", .tx_status = ath_tx_status, .get_rate = ath_get_rate, .rate_init = ath_rate_init, .rate_update = ath_rate_update, .alloc = ath_rate_alloc, .free = ath_rate_free, .alloc_sta = ath_rate_alloc_sta, .free_sta = ath_rate_free_sta, #ifdef CONFIG_ATH9K_DEBUGFS .add_sta_debugfs = ath_rate_add_sta_debugfs, #endif }; int ath_rate_control_register(void) { return ieee80211_rate_control_register(&ath_rate_ops); } void ath_rate_control_unregister(void) { ieee80211_rate_control_unregister(&ath_rate_ops); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/phy.h0000644000175000017500000000337512026211315023236 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef PHY_H #define PHY_H #define CHANSEL_DIV 15 #define CHANSEL_2G(_freq) (((_freq) * 0x10000) / CHANSEL_DIV) #define CHANSEL_5G(_freq) (((_freq) * 0x8000) / CHANSEL_DIV) #define AR_PHY_BASE 0x9800 #define AR_PHY(_n) (AR_PHY_BASE + ((_n)<<2)) #define AR_PHY_TX_PWRCTRL_TX_GAIN_TAB_MAX 0x0007E000 #define AR_PHY_TX_PWRCTRL_TX_GAIN_TAB_MAX_S 13 #define AR_PHY_TX_GAIN_CLC 0x0000001E #define AR_PHY_TX_GAIN_CLC_S 1 #define AR_PHY_TX_GAIN 0x0007F000 #define AR_PHY_TX_GAIN_S 12 #define AR_PHY_CLC_TBL1 0xa35c #define AR_PHY_CLC_I0 0x07ff0000 #define AR_PHY_CLC_I0_S 16 #define AR_PHY_CLC_Q0 0x0000ffd0 #define AR_PHY_CLC_Q0_S 5 #define ANTSWAP_AB 0x0001 #define REDUCE_CHAIN_0 0x00000050 #define REDUCE_CHAIN_1 0x00000051 #define AR_PHY_CHIP_ID 0x9818 #define AR_PHY_TIMING11_SPUR_FREQ_SD 0x3FF00000 #define AR_PHY_TIMING11_SPUR_FREQ_SD_S 20 #define AR_PHY_PLL_CONTROL 0x16180 #define AR_PHY_PLL_MODE 0x16184 #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/mci.h0000644000175000017500000000730512026211315023203 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef MCI_H #define MCI_H #include "ar9003_mci.h" #define ATH_MCI_SCHED_BUF_SIZE (16 * 16) /* 16 entries, 4 dword each */ #define ATH_MCI_GPM_MAX_ENTRY 16 #define ATH_MCI_GPM_BUF_SIZE (ATH_MCI_GPM_MAX_ENTRY * 16) #define ATH_MCI_DEF_BT_PERIOD 40 #define ATH_MCI_BDR_DUTY_CYCLE 20 #define ATH_MCI_MAX_DUTY_CYCLE 90 #define ATH_MCI_DEF_AGGR_LIMIT 6 /* in 0.24 ms */ #define ATH_MCI_MAX_ACL_PROFILE 7 #define ATH_MCI_MAX_SCO_PROFILE 1 #define ATH_MCI_MAX_PROFILE (ATH_MCI_MAX_ACL_PROFILE +\ ATH_MCI_MAX_SCO_PROFILE) #define INC_PROF(_mci, _info) do { \ switch (_info->type) { \ case MCI_GPM_COEX_PROFILE_RFCOMM:\ _mci->num_other_acl++; \ break; \ case MCI_GPM_COEX_PROFILE_A2DP: \ _mci->num_a2dp++; \ if (!_info->edr) \ _mci->num_bdr++; \ break; \ case MCI_GPM_COEX_PROFILE_HID: \ _mci->num_hid++; \ break; \ case MCI_GPM_COEX_PROFILE_BNEP: \ _mci->num_pan++; \ break; \ case MCI_GPM_COEX_PROFILE_VOICE: \ _mci->num_sco++; \ break; \ default: \ break; \ } \ } while (0) #define DEC_PROF(_mci, _info) do { \ switch (_info->type) { \ case MCI_GPM_COEX_PROFILE_RFCOMM:\ _mci->num_other_acl--; \ break; \ case MCI_GPM_COEX_PROFILE_A2DP: \ _mci->num_a2dp--; \ if (!_info->edr) \ _mci->num_bdr--; \ break; \ case MCI_GPM_COEX_PROFILE_HID: \ _mci->num_hid--; \ break; \ case MCI_GPM_COEX_PROFILE_BNEP: \ _mci->num_pan--; \ break; \ case MCI_GPM_COEX_PROFILE_VOICE: \ _mci->num_sco--; \ break; \ default: \ break; \ } \ } while (0) #define NUM_PROF(_mci) (_mci->num_other_acl + _mci->num_a2dp + \ _mci->num_hid + _mci->num_pan + _mci->num_sco) struct ath_mci_profile_info { u8 type; u8 conn_handle; bool start; bool master; bool edr; u8 voice_type; u16 T; /* Voice: Tvoice, HID: Tsniff, in slots */ u8 W; /* Voice: Wvoice, HID: Sniff timeout, in slots */ u8 A; /* HID: Sniff attempt, in slots */ struct list_head list; }; struct ath_mci_profile_status { bool is_critical; bool is_link; u8 conn_handle; }; struct ath_mci_profile { struct list_head info; DECLARE_BITMAP(status, ATH_MCI_MAX_PROFILE); u16 aggr_limit; u8 num_mgmt; u8 num_sco; u8 num_a2dp; u8 num_hid; u8 num_pan; u8 num_other_acl; u8 num_bdr; }; struct ath_mci_buf { void *bf_addr; /* virtual addr of desc */ dma_addr_t bf_paddr; /* physical addr of buffer */ u32 bf_len; /* len of data */ }; struct ath_mci_coex { struct ath_mci_buf sched_buf; struct ath_mci_buf gpm_buf; }; void ath_mci_flush_profile(struct ath_mci_profile *mci); int ath_mci_setup(struct ath_softc *sc); void ath_mci_cleanup(struct ath_softc *sc); void ath_mci_intr(struct ath_softc *sc); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void ath_mci_enable(struct ath_softc *sc); #else static inline void ath_mci_enable(struct ath_softc *sc) { } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #endif /* MCI_H*/ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/mci.c0000644000175000017500000003753512026211315023206 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "ath9k.h" #include "mci.h" static const u8 ath_mci_duty_cycle[] = { 55, 50, 60, 70, 80, 85, 90, 95, 98 }; static struct ath_mci_profile_info* ath_mci_find_profile(struct ath_mci_profile *mci, struct ath_mci_profile_info *info) { struct ath_mci_profile_info *entry; if (list_empty(&mci->info)) return NULL; list_for_each_entry(entry, &mci->info, list) { if (entry->conn_handle == info->conn_handle) return entry; } return NULL; } static bool ath_mci_add_profile(struct ath_common *common, struct ath_mci_profile *mci, struct ath_mci_profile_info *info) { struct ath_mci_profile_info *entry; if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && (info->type == MCI_GPM_COEX_PROFILE_VOICE)) return false; if (((NUM_PROF(mci) - mci->num_sco) == ATH_MCI_MAX_ACL_PROFILE) && (info->type != MCI_GPM_COEX_PROFILE_VOICE)) return false; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return false; memcpy(entry, info, 10); INC_PROF(mci, info); list_add_tail(&entry->list, &mci->info); return true; } static void ath_mci_del_profile(struct ath_common *common, struct ath_mci_profile *mci, struct ath_mci_profile_info *entry) { if (!entry) return; DEC_PROF(mci, entry); list_del(&entry->list); kfree(entry); } void ath_mci_flush_profile(struct ath_mci_profile *mci) { struct ath_mci_profile_info *info, *tinfo; mci->aggr_limit = 0; if (list_empty(&mci->info)) return; list_for_each_entry_safe(info, tinfo, &mci->info, list) { list_del(&info->list); DEC_PROF(mci, info); kfree(info); } } static void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex) { struct ath_mci_profile *mci = &btcoex->mci; u32 wlan_airtime = btcoex->btcoex_period * (100 - btcoex->duty_cycle) / 100; /* * Scale: wlan_airtime is in ms, aggr_limit is in 0.25 ms. * When wlan_airtime is less than 4ms, aggregation limit has to be * adjusted half of wlan_airtime to ensure that the aggregation can fit * without collision with BT traffic. */ if ((wlan_airtime <= 4) && (!mci->aggr_limit || (mci->aggr_limit > (2 * wlan_airtime)))) mci->aggr_limit = 2 * wlan_airtime; } static void ath_mci_update_scheme(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; struct ath_mci_profile_info *info; u32 num_profile = NUM_PROF(mci); if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING) goto skip_tuning; btcoex->duty_cycle = ath_mci_duty_cycle[num_profile]; if (num_profile == 1) { info = list_first_entry(&mci->info, struct ath_mci_profile_info, list); if (mci->num_sco) { if (info->T == 12) mci->aggr_limit = 8; else if (info->T == 6) { mci->aggr_limit = 6; btcoex->duty_cycle = 30; } ath_dbg(common, MCI, "Single SCO, aggregation limit %d 1/4 ms\n", mci->aggr_limit); } else if (mci->num_pan || mci->num_other_acl) { /* * For single PAN/FTP profile, allocate 35% for BT * to improve WLAN throughput. */ btcoex->duty_cycle = 35; btcoex->btcoex_period = 53; ath_dbg(common, MCI, "Single PAN/FTP bt period %d ms dutycycle %d\n", btcoex->duty_cycle, btcoex->btcoex_period); } else if (mci->num_hid) { btcoex->duty_cycle = 30; mci->aggr_limit = 6; ath_dbg(common, MCI, "Multiple attempt/timeout single HID " "aggregation limit 1.5 ms dutycycle 30%%\n"); } } else if (num_profile == 2) { if (mci->num_hid == 2) btcoex->duty_cycle = 30; mci->aggr_limit = 6; ath_dbg(common, MCI, "Two BT profiles aggr limit 1.5 ms dutycycle %d%%\n", btcoex->duty_cycle); } else if (num_profile >= 3) { mci->aggr_limit = 4; ath_dbg(common, MCI, "Three or more profiles aggregation limit 1 ms\n"); } skip_tuning: if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) { if (IS_CHAN_HT(sc->sc_ah->curchan)) ath_mci_adjust_aggr_limit(btcoex); else btcoex->btcoex_period >>= 1; } ath9k_btcoex_timer_pause(sc); ath9k_hw_btcoex_disable(sc->sc_ah); if (IS_CHAN_5GHZ(sc->sc_ah->curchan)) return; btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_BDR_DUTY_CYCLE : 0); if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; btcoex->btcoex_no_stomp = btcoex->btcoex_period * 1000 * (100 - btcoex->duty_cycle) / 100; ath9k_hw_btcoex_enable(sc->sc_ah); ath9k_btcoex_timer_resume(sc); } static void ath_mci_wait_btcal_done(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; /* Stop tx & rx */ ieee80211_stop_queues(sc->hw); ath_stoprecv(sc); ath_drain_all_txq(sc, false); /* Wait for cal done */ ar9003_mci_start_reset(ah, ah->curchan); /* Resume tx & rx */ ath_startrecv(sc); ieee80211_wake_queues(sc->hw); } static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; switch (opcode) { case MCI_GPM_BT_CAL_REQ: if (mci_hw->bt_state == MCI_BT_AWAKE) { mci_hw->bt_state = MCI_BT_CAL_START; ath_mci_wait_btcal_done(sc); } ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state); break; case MCI_GPM_BT_CAL_GRANT: MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); ar9003_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, 16, false, true); break; default: ath_dbg(common, MCI, "Unknown GPM CAL message\n"); break; } } static void ath9k_mci_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, mci_work); ath_mci_update_scheme(sc); } static void ath_mci_process_profile(struct ath_softc *sc, struct ath_mci_profile_info *info) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; struct ath_mci_profile_info *entry = NULL; entry = ath_mci_find_profile(mci, info); if (entry) { /* * Two MCI interrupts are generated while connecting to * headset and A2DP profile, but only one MCI interrupt * is generated with last added profile type while disconnecting * both profiles. * So while adding second profile type decrement * the first one. */ if (entry->type != info->type) { DEC_PROF(mci, entry); INC_PROF(mci, info); } memcpy(entry, info, 10); } if (info->start) { if (!entry && !ath_mci_add_profile(common, mci, info)) return; } else ath_mci_del_profile(common, mci, entry); btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; mci->aggr_limit = mci->num_sco ? 6 : 0; btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; if (NUM_PROF(mci)) btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; else btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : ATH_BTCOEX_STOMP_LOW; ieee80211_queue_work(sc->hw, &sc->mci_work); } static void ath_mci_process_status(struct ath_softc *sc, struct ath_mci_profile_status *status) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; struct ath_mci_profile_info info; int i = 0, old_num_mgmt = mci->num_mgmt; /* Link status type are not handled */ if (status->is_link) return; info.conn_handle = status->conn_handle; if (ath_mci_find_profile(mci, &info)) return; if (status->conn_handle >= ATH_MCI_MAX_PROFILE) return; if (status->is_critical) __set_bit(status->conn_handle, mci->status); else __clear_bit(status->conn_handle, mci->status); mci->num_mgmt = 0; do { if (test_bit(i, mci->status)) mci->num_mgmt++; } while (++i < ATH_MCI_MAX_PROFILE); if (old_num_mgmt != mci->num_mgmt) ieee80211_queue_work(sc->hw, &sc->mci_work); } static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) { struct ath_hw *ah = sc->sc_ah; struct ath_mci_profile_info profile_info; struct ath_mci_profile_status profile_status; struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 major, minor; u32 seq_num; switch (opcode) { case MCI_GPM_COEX_VERSION_QUERY: ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION); break; case MCI_GPM_COEX_VERSION_RESPONSE: major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION); minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION); ar9003_mci_set_bt_version(ah, major, minor); break; case MCI_GPM_COEX_STATUS_QUERY: ar9003_mci_send_wlan_channels(ah); break; case MCI_GPM_COEX_BT_PROFILE_INFO: memcpy(&profile_info, (rx_payload + MCI_GPM_COEX_B_PROFILE_TYPE), 10); if ((profile_info.type == MCI_GPM_COEX_PROFILE_UNKNOWN) || (profile_info.type >= MCI_GPM_COEX_PROFILE_MAX)) { ath_dbg(common, MCI, "Illegal profile type = %d, state = %d\n", profile_info.type, profile_info.start); break; } ath_mci_process_profile(sc, &profile_info); break; case MCI_GPM_COEX_BT_STATUS_UPDATE: profile_status.is_link = *(rx_payload + MCI_GPM_COEX_B_STATUS_TYPE); profile_status.conn_handle = *(rx_payload + MCI_GPM_COEX_B_STATUS_LINKID); profile_status.is_critical = *(rx_payload + MCI_GPM_COEX_B_STATUS_STATE); seq_num = *((u32 *)(rx_payload + 12)); ath_dbg(common, MCI, "BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%u\n", profile_status.is_link, profile_status.conn_handle, profile_status.is_critical, seq_num); ath_mci_process_status(sc, &profile_status); break; default: ath_dbg(common, MCI, "Unknown GPM COEX message = 0x%02x\n", opcode); break; } } int ath_mci_setup(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_mci_coex *mci = &sc->mci_coex; struct ath_mci_buf *buf = &mci->sched_buf; buf->bf_addr = dma_alloc_coherent(sc->dev, ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, &buf->bf_paddr, GFP_KERNEL); if (buf->bf_addr == NULL) { ath_dbg(common, FATAL, "MCI buffer alloc failed\n"); return -ENOMEM; } memset(buf->bf_addr, MCI_GPM_RSVD_PATTERN, ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE); mci->sched_buf.bf_len = ATH_MCI_SCHED_BUF_SIZE; mci->gpm_buf.bf_len = ATH_MCI_GPM_BUF_SIZE; mci->gpm_buf.bf_addr = (u8 *)mci->sched_buf.bf_addr + mci->sched_buf.bf_len; mci->gpm_buf.bf_paddr = mci->sched_buf.bf_paddr + mci->sched_buf.bf_len; ar9003_mci_setup(sc->sc_ah, mci->gpm_buf.bf_paddr, mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4), mci->sched_buf.bf_paddr); INIT_WORK(&sc->mci_work, ath9k_mci_work); ath_dbg(common, MCI, "MCI Initialized\n"); return 0; } void ath_mci_cleanup(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; struct ath_mci_coex *mci = &sc->mci_coex; struct ath_mci_buf *buf = &mci->sched_buf; if (buf->bf_addr) dma_free_coherent(sc->dev, ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, buf->bf_addr, buf->bf_paddr); ar9003_mci_cleanup(ah); ath_dbg(common, MCI, "MCI De-Initialized\n"); } void ath_mci_intr(struct ath_softc *sc) { struct ath_mci_coex *mci = &sc->mci_coex; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 mci_int, mci_int_rxmsg; u32 offset, subtype, opcode; u32 *pgpm; u32 more_data = MCI_GPM_MORE; bool skip_gpm = false; ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg); if (ar9003_mci_state(ah, MCI_STATE_ENABLE) == 0) { ar9003_mci_get_next_gpm_offset(ah, true, NULL); return; } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) { u32 payload[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff00}; /* * The following REMOTE_RESET and SYS_WAKING used to sent * only when BT wake up. Now they are always sent, as a * recovery method to reset BT MCI's RX alignment. */ ar9003_mci_send_message(ah, MCI_REMOTE_RESET, 0, payload, 16, true, false); ar9003_mci_send_message(ah, MCI_SYS_WAKING, 0, NULL, 0, true, false); mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE; ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE); /* * always do this for recovery and 2G/5G toggling and LNA_TRANS */ ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING; if ((mci_hw->bt_state == MCI_BT_SLEEP) && (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != MCI_BT_SLEEP)) ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; if ((mci_hw->bt_state == MCI_BT_AWAKE) && (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != MCI_BT_AWAKE)) mci_hw->bt_state = MCI_BT_SLEEP; } if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { ar9003_mci_state(ah, MCI_STATE_RECOVER_RX); skip_gpm = true; } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO; offset = ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_GPM) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM; while (more_data == MCI_GPM_MORE) { pgpm = mci->gpm_buf.bf_addr; offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); if (offset == MCI_GPM_INVALID) break; pgpm += (offset >> 2); /* * The first dword is timer. * The real data starts from 2nd dword. */ subtype = MCI_GPM_TYPE(pgpm); opcode = MCI_GPM_OPCODE(pgpm); if (skip_gpm) goto recycle; if (MCI_GPM_IS_CAL_TYPE(subtype)) { ath_mci_cal_msg(sc, subtype, (u8 *)pgpm); } else { switch (subtype) { case MCI_GPM_COEX_AGENT: ath_mci_msg(sc, opcode, (u8 *)pgpm); break; default: break; } } recycle: MCI_GPM_RECYCLE(pgpm); } } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_HW_MSG_MASK) { if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL; if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_INFO) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_INFO; if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) { int value_dbm = MS(mci_hw->cont_status, AR_MCI_CONT_RSSI_POWER); mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_INFO; ath_dbg(common, MCI, "MCI CONT_INFO: (%s) pri = %d pwr = %d dBm\n", MS(mci_hw->cont_status, AR_MCI_CONT_TXRX) ? "tx" : "rx", MS(mci_hw->cont_status, AR_MCI_CONT_PRIORITY), value_dbm); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_NACK) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_NACK; if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_RST) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_RST; } if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR | AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT); } void ath_mci_enable(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); if (!common->btcoex_enabled) return; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) sc->sc_ah->imask |= ATH9K_INT_MCI; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/main.c0000644000175000017500000016453512026211315023363 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "ath9k.h" #include "btcoex.h" static void ath9k_set_assoc_state(struct ath_softc *sc, struct ieee80211_vif *vif); u8 ath9k_parse_mpdudensity(u8 mpdudensity) { /* * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": * 0 for no restriction * 1 for 1/4 us * 2 for 1/2 us * 3 for 1 us * 4 for 2 us * 5 for 4 us * 6 for 8 us * 7 for 16 us */ switch (mpdudensity) { case 0: return 0; case 1: case 2: case 3: /* Our lower layer calculations limit our precision to 1 microsecond */ return 1; case 4: return 2; case 5: return 4; case 6: return 8; case 7: return 16; default: return 0; } } static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq) { bool pending = false; spin_lock_bh(&txq->axq_lock); if (txq->axq_depth || !list_empty(&txq->axq_acq)) pending = true; spin_unlock_bh(&txq->axq_lock); return pending; } static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) { unsigned long flags; bool ret; spin_lock_irqsave(&sc->sc_pm_lock, flags); ret = ath9k_hw_setpower(sc->sc_ah, mode); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); return ret; } void ath9k_ps_wakeup(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; enum ath9k_power_mode power_mode; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (++sc->ps_usecount != 1) goto unlock; power_mode = sc->sc_ah->power_mode; ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); /* * While the hardware is asleep, the cycle counters contain no * useful data. Better clear them now so that they don't mess up * survey data results. */ if (power_mode != ATH9K_PM_AWAKE) { spin_lock(&common->cc_lock); ath_hw_cycle_counters_update(common); memset(&common->cc_survey, 0, sizeof(common->cc_survey)); memset(&common->cc_ani, 0, sizeof(common->cc_ani)); spin_unlock(&common->cc_lock); } unlock: spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } void ath9k_ps_restore(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); enum ath9k_power_mode mode; unsigned long flags; bool reset; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (--sc->ps_usecount != 0) goto unlock; if (sc->ps_idle) { ath9k_hw_setrxabort(sc->sc_ah, 1); ath9k_hw_stopdmarecv(sc->sc_ah, &reset); mode = ATH9K_PM_FULL_SLEEP; } else if (sc->ps_enabled && !(sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK))) { mode = ATH9K_PM_NETWORK_SLEEP; if (ath9k_hw_btcoex_is_enabled(sc->sc_ah)) ath9k_btcoex_stop_gen_timer(sc); } else { goto unlock; } spin_lock(&common->cc_lock); ath_hw_cycle_counters_update(common); spin_unlock(&common->cc_lock); ath9k_hw_setpower(sc->sc_ah, mode); unlock: spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } static void __ath_cancel_work(struct ath_softc *sc) { cancel_work_sync(&sc->paprd_work); cancel_work_sync(&sc->hw_check_work); cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT if (ath9k_hw_mci_is_enabled(sc->sc_ah)) cancel_work_sync(&sc->mci_work); #endif } static void ath_cancel_work(struct ath_softc *sc) { __ath_cancel_work(sc); cancel_work_sync(&sc->hw_reset_work); } static void ath_restart_work(struct ath_softc *sc) { ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9485(sc->sc_ah) || AR_SREV_9550(sc->sc_ah)) ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); ath_start_rx_poll(sc, 3); ath_start_ani(sc); } static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) { struct ath_hw *ah = sc->sc_ah; bool ret = true; ieee80211_stop_queues(sc->hw); sc->hw_busy_count = 0; ath_stop_ani(sc); del_timer_sync(&sc->rx_poll_timer); ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); if (!ath_stoprecv(sc)) ret = false; if (!ath_drain_all_txq(sc, retry_tx)) ret = false; if (!flush) { if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_rx_tasklet(sc, 1, true); ath_rx_tasklet(sc, 1, false); } else { ath_flushrecv(sc); } return ret; } static bool ath_complete_reset(struct ath_softc *sc, bool start) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); return false; } ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); clear_bit(SC_OP_HW_RESET, &sc->sc_flags); ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) goto work; ath9k_set_beacon(sc); if (ah->opmode == NL80211_IFTYPE_STATION && test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { spin_lock_irqsave(&sc->sc_pm_lock, flags); sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } work: ath_restart_work(sc); } if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) ath_ant_comb_update(sc); ieee80211_wake_queues(sc->hw); return true; } static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = NULL; bool fastcc = true; bool flush = false; int r; __ath_cancel_work(sc); spin_lock_bh(&sc->sc_pcu_lock); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { fastcc = false; caldata = &sc->caldata; } if (!hchan) { fastcc = false; flush = true; hchan = ah->curchan; } if (!ath_prepare_reset(sc, retry_tx, flush)) fastcc = false; ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", hchan->channel, IS_CHAN_HT40(hchan), fastcc); r = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (r) { ath_err(common, "Unable to reset channel, reset status %d\n", r); goto out; } if (!ath_complete_reset(sc, true)) r = -EIO; out: spin_unlock_bh(&sc->sc_pcu_lock); return r; } /* * Set/change channels. If the channel is really being changed, it's done * by reseting the chip. To accomplish this we must first cleanup any pending * DMA, then restart stuff. */ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { int r; if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return -EIO; r = ath_reset_internal(sc, hchan, false); return r; } static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { struct ath_node *an; u8 density; an = (struct ath_node *)sta->drv_priv; #ifdef CONFIG_ATH9K_DEBUGFS spin_lock(&sc->nodes_lock); list_add(&an->list, &sc->nodes); spin_unlock(&sc->nodes_lock); #endif an->sta = sta; an->vif = vif; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { ath_tx_node_init(sc, an); an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); density = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density); an->mpdudensity = density; } } static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) { struct ath_node *an = (struct ath_node *)sta->drv_priv; #ifdef CONFIG_ATH9K_DEBUGFS spin_lock(&sc->nodes_lock); list_del(&an->list); spin_unlock(&sc->nodes_lock); an->sta = NULL; #endif if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) ath_tx_node_cleanup(sc, an); } void ath9k_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); enum ath_reset_type type; unsigned long flags; u32 status = sc->intrstatus; u32 rxmask; ath9k_ps_wakeup(sc); spin_lock(&sc->sc_pcu_lock); if ((status & ATH9K_INT_FATAL) || (status & ATH9K_INT_BB_WATCHDOG)) { if (status & ATH9K_INT_FATAL) type = RESET_TYPE_FATAL_INT; else type = RESET_TYPE_BB_WATCHDOG; ath9k_queue_reset(sc, type); goto out; } spin_lock_irqsave(&sc->sc_pm_lock, flags); if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { /* * TSF sync does not look correct; remain awake to sync with * the next Beacon. */ ath_dbg(common, PS, "TSFOOR - Sync with next Beacon\n"); sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC; } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); else rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); if (status & rxmask) { /* Check for high priority Rx first */ if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && (status & ATH9K_INT_RXHP)) ath_rx_tasklet(sc, 0, true); ath_rx_tasklet(sc, 0, false); } if (status & ATH9K_INT_TX) { if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_tx_edma_tasklet(sc); else ath_tx_tasklet(sc); } ath9k_btcoex_handle_interrupt(sc, status); out: /* re-enable hardware interrupt */ ath9k_hw_enable_interrupts(ah); spin_unlock(&sc->sc_pcu_lock); ath9k_ps_restore(sc); } irqreturn_t ath_isr(int irq, void *dev) { #define SCHED_INTR ( \ ATH9K_INT_FATAL | \ ATH9K_INT_BB_WATCHDOG | \ ATH9K_INT_RXORN | \ ATH9K_INT_RXEOL | \ ATH9K_INT_RX | \ ATH9K_INT_RXLP | \ ATH9K_INT_RXHP | \ ATH9K_INT_TX | \ ATH9K_INT_BMISS | \ ATH9K_INT_CST | \ ATH9K_INT_TSFOOR | \ ATH9K_INT_GENTIMER | \ ATH9K_INT_MCI) struct ath_softc *sc = dev; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); enum ath9k_int status; bool sched = false; /* * The hardware is not ready/present, don't * touch anything. Note this can happen early * on if the IRQ is shared. */ if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return IRQ_NONE; /* shared irq, not for us */ if (!ath9k_hw_intrpend(ah)) return IRQ_NONE; if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) { ath9k_hw_kill_interrupts(ah); return IRQ_HANDLED; } /* * Figure out the reason(s) for the interrupt. Note * that the hal returns a pseudo-ISR that may include * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ status &= ah->imask; /* discard unasked-for bits */ /* * If there are no status bits set, then this interrupt was not * for me (should have been caught above). */ if (!status) return IRQ_NONE; /* Cache the status */ sc->intrstatus = status; if (status & SCHED_INTR) sched = true; #ifdef CONFIG_PM_SLEEP if (status & ATH9K_INT_BMISS) { if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { ath_dbg(common, ANY, "during WoW we got a BMISS\n"); atomic_inc(&sc->wow_got_bmiss_intr); atomic_dec(&sc->wow_sleep_proc_intr); } ath_dbg(common, INTERRUPT, "beacon miss interrupt\n"); } #endif /* * If a FATAL or RXORN interrupt is received, we have to reset the * chip immediately. */ if ((status & ATH9K_INT_FATAL) || ((status & ATH9K_INT_RXORN) && !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA))) goto chip_reset; if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && (status & ATH9K_INT_BB_WATCHDOG)) { spin_lock(&common->cc_lock); ath_hw_cycle_counters_update(common); ar9003_hw_bb_watchdog_dbg_info(ah); spin_unlock(&common->cc_lock); goto chip_reset; } if (status & ATH9K_INT_SWBA) tasklet_schedule(&sc->bcon_tasklet); if (status & ATH9K_INT_TXURN) ath9k_hw_updatetxtriglevel(ah, true); if (status & ATH9K_INT_RXEOL) { ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN); ath9k_hw_set_interrupts(ah); } if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) if (status & ATH9K_INT_TIM_TIMER) { if (ATH_DBG_WARN_ON_ONCE(sc->ps_idle)) goto chip_reset; /* Clear RxAbort bit so that we can * receive frames */ ath9k_setpower(sc, ATH9K_PM_AWAKE); spin_lock(&sc->sc_pm_lock); ath9k_hw_setrxabort(sc->sc_ah, 0); sc->ps_flags |= PS_WAIT_FOR_BEACON; spin_unlock(&sc->sc_pm_lock); } chip_reset: ath_debug_stat_interrupt(sc, status); if (sched) { /* turn off every interrupt */ ath9k_hw_disable_interrupts(ah); tasklet_schedule(&sc->intr_tq); } return IRQ_HANDLED; #undef SCHED_INTR } static int ath_reset(struct ath_softc *sc, bool retry_tx) { int r; ath9k_ps_wakeup(sc); r = ath_reset_internal(sc, NULL, retry_tx); if (retry_tx) { int i; for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { spin_lock_bh(&sc->tx.txq[i].axq_lock); ath_txq_schedule(sc, &sc->tx.txq[i]); spin_unlock_bh(&sc->tx.txq[i].axq_lock); } } } ath9k_ps_restore(sc); return r; } void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type) { #ifdef CONFIG_ATH9K_DEBUGFS RESET_STAT_INC(sc, type); #endif set_bit(SC_OP_HW_RESET, &sc->sc_flags); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); } void ath_reset_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work); ath_reset(sc, true); } /**********************/ /* mac80211 callbacks */ /**********************/ static int ath9k_start(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *curchan = hw->conf.channel; struct ath9k_channel *init_channel; int r; ath_dbg(common, CONFIG, "Starting driver with initial channel: %d MHz\n", curchan->center_freq); ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); init_channel = ath9k_cmn_get_curchannel(hw, ah); /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); /* * The basic interface to setting the hardware in a good * state is ``reset''. On return the hardware is known to * be powered up and with interrupts disabled. This must * be followed by initialization of the appropriate bits * and then setup of the interrupt mask. */ spin_lock_bh(&sc->sc_pcu_lock); atomic_set(&ah->intr_ref_cnt, -1); r = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (r) { ath_err(common, "Unable to reset hardware; reset status %d (freq %u MHz)\n", r, curchan->center_freq); spin_unlock_bh(&sc->sc_pcu_lock); goto mutex_unlock; } /* Setup our intr mask. */ ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ah->imask |= ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_BB_WATCHDOG; else ah->imask |= ATH9K_INT_RX; ah->imask |= ATH9K_INT_GTT; if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; ath_mci_enable(sc); clear_bit(SC_OP_INVALID, &sc->sc_flags); sc->sc_ah->is_monitoring = false; if (!ath_complete_reset(sc, false)) { r = -EIO; spin_unlock_bh(&sc->sc_pcu_lock); goto mutex_unlock; } if (ah->led_pin >= 0) { ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ath9k_hw_set_gpio(ah, ah->led_pin, 0); } /* * Reset key cache to sane defaults (all entries cleared) instead of * semi-random values after suspend/resume. */ ath9k_cmn_init_crypto(sc->sc_ah); spin_unlock_bh(&sc->sc_pcu_lock); if (ah->caps.pcie_lcr_extsync_en && common->bus_ops->extn_synch_en) common->bus_ops->extn_synch_en(common); mutex_unlock: mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); return r; } static void ath9k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_tx_control txctl; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; unsigned long flags; if (sc->ps_enabled) { /* * mac80211 does not set PM field for normal data frames, so we * need to update that based on the current PS mode. */ if (ieee80211_is_data(hdr->frame_control) && !ieee80211_is_nullfunc(hdr->frame_control) && !ieee80211_has_pm(hdr->frame_control)) { ath_dbg(common, PS, "Add PM=1 for a TX frame while in PS mode\n"); hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); } } if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP)) { /* * We are using PS-Poll and mac80211 can request TX while in * power save mode. Need to wake up hardware for the TX to be * completed and if needed, also for RX of buffered frames. */ ath9k_ps_wakeup(sc); spin_lock_irqsave(&sc->sc_pm_lock, flags); if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) ath9k_hw_setrxabort(sc->sc_ah, 0); if (ieee80211_is_pspoll(hdr->frame_control)) { ath_dbg(common, PS, "Sending PS-Poll to pick a buffered frame\n"); sc->ps_flags |= PS_WAIT_FOR_PSPOLL_DATA; } else { ath_dbg(common, PS, "Wake up to complete TX\n"); sc->ps_flags |= PS_WAIT_FOR_TX_ACK; } /* * The actual restore operation will happen only after * the ps_flags bit is cleared. We are just dropping * the ps_usecount here. */ spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_ps_restore(sc); } /* * Cannot tx while the hardware is in full sleep, it first needs a full * chip reset to recover from that */ if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP)) { ath_err(common, "TX while HW is in FULL_SLEEP mode\n"); goto exit; } memset(&txctl, 0, sizeof(struct ath_tx_control)); txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)]; txctl.sta = control->sta; ath_dbg(common, XMIT, "transmitting packet, skb: %p\n", skb); if (ath_tx_start(hw, skb, &txctl) != 0) { ath_dbg(common, XMIT, "TX failed\n"); TX_STAT_INC(txctl.txq->axq_qnum, txfailed); goto exit; } return; exit: dev_kfree_skb_any(skb); } static void ath9k_stop(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); bool prev_idle; mutex_lock(&sc->mutex); ath_cancel_work(sc); del_timer_sync(&sc->rx_poll_timer); if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; } /* Ensure HW is awake when we try to shut it down. */ ath9k_ps_wakeup(sc); spin_lock_bh(&sc->sc_pcu_lock); /* prevent tasklets to enable interrupts once we disable them */ ah->imask &= ~ATH9K_INT_GLOBAL; /* make sure h/w will not generate any interrupt * before setting the invalid flag. */ ath9k_hw_disable_interrupts(ah); spin_unlock_bh(&sc->sc_pcu_lock); /* we can now sync irq and kill any running tasklets, since we already * disabled interrupts and not holding a spin lock */ synchronize_irq(sc->irq); tasklet_kill(&sc->intr_tq); tasklet_kill(&sc->bcon_tasklet); prev_idle = sc->ps_idle; sc->ps_idle = true; spin_lock_bh(&sc->sc_pcu_lock); if (ah->led_pin >= 0) { ath9k_hw_set_gpio(ah, ah->led_pin, 1); ath9k_hw_cfg_gpio_input(ah, ah->led_pin); } ath_prepare_reset(sc, false, true); if (sc->rx.frag) { dev_kfree_skb_any(sc->rx.frag); sc->rx.frag = NULL; } if (!ah->curchan) ah->curchan = ath9k_cmn_get_curchannel(hw, ah); ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); ath9k_hw_phy_disable(ah); ath9k_hw_configpcipowersave(ah, true); spin_unlock_bh(&sc->sc_pcu_lock); ath9k_ps_restore(sc); set_bit(SC_OP_INVALID, &sc->sc_flags); sc->ps_idle = prev_idle; mutex_unlock(&sc->mutex); ath_dbg(common, CONFIG, "Driver halt\n"); } bool ath9k_uses_beacons(int type) { switch (type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: return true; default: return false; } } static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath9k_vif_iter_data *iter_data = data; int i; if (iter_data->hw_macaddr) for (i = 0; i < ETH_ALEN; i++) iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); switch (vif->type) { case NL80211_IFTYPE_AP: iter_data->naps++; break; case NL80211_IFTYPE_STATION: iter_data->nstations++; break; case NL80211_IFTYPE_ADHOC: iter_data->nadhocs++; break; case NL80211_IFTYPE_MESH_POINT: iter_data->nmeshes++; break; case NL80211_IFTYPE_WDS: iter_data->nwds++; break; default: break; } } static void ath9k_sta_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath_softc *sc = data; struct ath_vif *avp = (void *)vif->drv_priv; if (vif->type != NL80211_IFTYPE_STATION) return; if (avp->primary_sta_vif) ath9k_set_assoc_state(sc, vif); } /* Called with sc->mutex held. */ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ath9k_vif_iter_data *iter_data) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); /* * Use the hardware MAC address as reference, the hardware uses it * together with the BSSID mask when matching addresses. */ memset(iter_data, 0, sizeof(*iter_data)); iter_data->hw_macaddr = common->macaddr; memset(&iter_data->mask, 0xff, ETH_ALEN); if (vif) ath9k_vif_iter(iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter, iter_data); } /* Called with sc->mutex held. */ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_vif_iter_data iter_data; enum nl80211_iftype old_opmode = ah->opmode; ath9k_calculate_iter_data(hw, vif, &iter_data); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); if (iter_data.naps > 0) { ath9k_hw_set_tsfadjust(ah, true); ah->opmode = NL80211_IFTYPE_AP; } else { ath9k_hw_set_tsfadjust(ah, false); if (iter_data.nmeshes) ah->opmode = NL80211_IFTYPE_MESH_POINT; else if (iter_data.nwds) ah->opmode = NL80211_IFTYPE_AP; else if (iter_data.nadhocs) ah->opmode = NL80211_IFTYPE_ADHOC; else ah->opmode = NL80211_IFTYPE_STATION; } ath9k_hw_setopmode(ah); if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0) ah->imask |= ATH9K_INT_TSFOOR; else ah->imask &= ~ATH9K_INT_TSFOOR; ath9k_hw_set_interrupts(ah); /* * If we are changing the opmode to STATION, * a beacon sync needs to be done. */ if (ah->opmode == NL80211_IFTYPE_STATION && old_opmode == NL80211_IFTYPE_AP && test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_sta_vif_iter, sc); } } static int ath9k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); mutex_lock(&sc->mutex); ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; ath9k_ps_wakeup(sc); ath9k_calculate_summary_state(hw, vif); ath9k_ps_restore(sc); if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); mutex_unlock(&sc->mutex); return 0; } static int ath9k_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype new_type, bool p2p) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); ath_dbg(common, CONFIG, "Change Interface\n"); mutex_lock(&sc->mutex); if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); vif->type = new_type; vif->p2p = p2p; ath9k_ps_wakeup(sc); ath9k_calculate_summary_state(hw, vif); ath9k_ps_restore(sc); if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); mutex_unlock(&sc->mutex); return 0; } static void ath9k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); ath_dbg(common, CONFIG, "Detach Interface\n"); mutex_lock(&sc->mutex); sc->nvifs--; if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); ath9k_ps_wakeup(sc); ath9k_calculate_summary_state(hw, NULL); ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); } static void ath9k_enable_ps(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); sc->ps_enabled = true; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) { ah->imask |= ATH9K_INT_TIM_TIMER; ath9k_hw_set_interrupts(ah); } ath9k_hw_setrxabort(ah, 1); } ath_dbg(common, PS, "PowerSave enabled\n"); } static void ath9k_disable_ps(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); sc->ps_enabled = false; ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { ath9k_hw_setrxabort(ah, 0); sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK); if (ah->imask & ATH9K_INT_TIM_TIMER) { ah->imask &= ~ATH9K_INT_TIM_TIMER; ath9k_hw_set_interrupts(ah); } } ath_dbg(common, PS, "PowerSave disabled\n"); } static int ath9k_config(struct ieee80211_hw *hw, u32 changed) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; bool reset_channel = false; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); if (changed & IEEE80211_CONF_CHANGE_IDLE) { sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); if (sc->ps_idle) { ath_cancel_work(sc); ath9k_stop_btcoex(sc); } else { ath9k_start_btcoex(sc); /* * The chip needs a reset to properly wake up from * full sleep */ reset_channel = ah->chip_fullsleep; } } /* * We just prepare to enable PS. We have to wait until our AP has * ACK'd our null data frame to disable RX otherwise we'll ignore * those ACKs and end up retransmitting the same null data frames. * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode. */ if (changed & IEEE80211_CONF_CHANGE_PS) { unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (conf->flags & IEEE80211_CONF_PS) ath9k_enable_ps(sc); else ath9k_disable_ps(sc); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (conf->flags & IEEE80211_CONF_MONITOR) { ath_dbg(common, CONFIG, "Monitor mode is enabled\n"); sc->sc_ah->is_monitoring = true; } else { ath_dbg(common, CONFIG, "Monitor mode is disabled\n"); sc->sc_ah->is_monitoring = false; } } if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; int old_pos = -1; unsigned long flags; if (ah->curchan) old_pos = ah->curchan - &ah->channels[0]; ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n", curchan->center_freq, conf->channel_type); /* update survey stats for the old channel before switching */ spin_lock_irqsave(&common->cc_lock, flags); ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); /* * Preserve the current channel values, before updating * the same channel */ if (ah->curchan && (old_pos == pos)) ath9k_hw_getnf(ah, ah->curchan); ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos], curchan, conf->channel_type); /* * If the operating channel changes, change the survey in-use flags * along with it. * Reset the survey data for the new channel, unless we're switching * back to the operating channel from an off-channel operation. */ if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && sc->cur_survey != &sc->survey[pos]) { if (sc->cur_survey) sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; sc->cur_survey = &sc->survey[pos]; memset(sc->cur_survey, 0, sizeof(struct survey_info)); sc->cur_survey->filled |= SURVEY_INFO_IN_USE; } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { memset(&sc->survey[pos], 0, sizeof(struct survey_info)); } if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); return -EINVAL; } /* * The most recent snapshot of channel->noisefloor for the old * channel is only available after the hardware reset. Copy it to * the survey stats now. */ if (old_pos >= 0) ath_update_survey_nf(sc, old_pos); } if (changed & IEEE80211_CONF_CHANGE_POWER) { ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level); sc->config.txpowlimit = 2 * conf->power_level; ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); } mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); return 0; } #define SUPPORTED_FILTERS \ (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_CONTROL | \ FIF_PSPOLL | \ FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_PROBE_REQ | \ FIF_FCSFAIL) /* FIXME: sc->sc_full_reset ? */ static void ath9k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct ath_softc *sc = hw->priv; u32 rfilt; changed_flags &= SUPPORTED_FILTERS; *total_flags &= SUPPORTED_FILTERS; sc->rx.rxfilter = *total_flags; ath9k_ps_wakeup(sc); rfilt = ath_calcrxfilter(sc); ath9k_hw_setrxfilter(sc->sc_ah, rfilt); ath9k_ps_restore(sc); ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, "Set HW RX filter: 0x%x\n", rfilt); } static int ath9k_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_node *an = (struct ath_node *) sta->drv_priv; struct ieee80211_key_conf ps_key = { }; ath_node_attach(sc, sta, vif); if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_AP_VLAN) return 0; an->ps_key = ath_key_config(common, vif, sta, &ps_key); return 0; } static void ath9k_del_ps_key(struct ath_softc *sc, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_node *an = (struct ath_node *) sta->drv_priv; struct ieee80211_key_conf ps_key = { .hw_key_idx = an->ps_key }; if (!an->ps_key) return; ath_key_delete(common, &ps_key); } static int ath9k_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath_softc *sc = hw->priv; ath9k_del_ps_key(sc, vif, sta); ath_node_detach(sc, sta); return 0; } static void ath9k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { struct ath_softc *sc = hw->priv; struct ath_node *an = (struct ath_node *) sta->drv_priv; if (!sta->ht_cap.ht_supported) return; switch (cmd) { case STA_NOTIFY_SLEEP: an->sleeping = true; ath_tx_aggr_sleep(sta, sc, an); break; case STA_NOTIFY_AWAKE: an->sleeping = false; ath_tx_aggr_wakeup(sc, an); break; } } static int ath9k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_txq *txq; struct ath9k_tx_queue_info qi; int ret = 0; if (queue >= WME_NUM_AC) return 0; txq = sc->tx.txq_map[queue]; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); qi.tqi_aifs = params->aifs; qi.tqi_cwmin = params->cw_min; qi.tqi_cwmax = params->cw_max; qi.tqi_burstTime = params->txop * 32; ath_dbg(common, CONFIG, "Configure tx [queue/halq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", queue, txq->axq_qnum, params->aifs, params->cw_min, params->cw_max, params->txop); ath_update_max_aggr_framelen(sc, queue, qi.tqi_burstTime); ret = ath_txq_update(sc, txq->axq_qnum, &qi); if (ret) ath_err(common, "TXQ Update failed\n"); mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); return ret; } static int ath9k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); int ret = 0; if (ath9k_modparam_nohwcrypt) return -ENOSPC; if ((vif->type == NL80211_IFTYPE_ADHOC || vif->type == NL80211_IFTYPE_MESH_POINT) && (key->cipher == WLAN_CIPHER_SUITE_TKIP || key->cipher == WLAN_CIPHER_SUITE_CCMP) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { /* * For now, disable hw crypto for the RSN IBSS group keys. This * could be optimized in the future to use a modified key cache * design to support per-STA RX GTK, but until that gets * implemented, use of software crypto for group addressed * frames is a acceptable to allow RSN IBSS to be used. */ return -EOPNOTSUPP; } mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); ath_dbg(common, CONFIG, "Set HW Key\n"); switch (cmd) { case SET_KEY: if (sta) ath9k_del_ps_key(sc, vif, sta); ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (sc->sc_ah->sw_mgmt_crypto && key->cipher == WLAN_CIPHER_SUITE_CCMP) key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; ret = 0; } break; case DISABLE_KEY: ath_key_delete(common, key); break; default: ret = -EINVAL; } ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); return ret; } static void ath9k_set_assoc_state(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; unsigned long flags; set_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags); avp->primary_sta_vif = true; /* * Set the AID, BSSID and do beacon-sync only when * the HW opmode is STATION. * * But the primary bit is set above in any case. */ if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) return; memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = bss_conf->aid; ath9k_hw_write_associd(sc->sc_ah); sc->last_rssi = ATH_RSSI_DUMMY_MARKER; sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; spin_lock_irqsave(&sc->sc_pm_lock, flags); sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath_dbg(common, CONFIG, "Primary Station interface: %pM, BSSID: %pM\n", vif->addr, common->curbssid); } static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath_softc *sc = data; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; if (test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) return; if (bss_conf->assoc) ath9k_set_assoc_state(sc, vif); } static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { #define CHECK_ANI \ (BSS_CHANGED_ASSOC | \ BSS_CHANGED_IBSS | \ BSS_CHANGED_BEACON_ENABLED) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; int slottime; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); if (changed & BSS_CHANGED_ASSOC) { ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n", bss_conf->bssid, bss_conf->assoc); if (avp->primary_sta_vif && !bss_conf->assoc) { clear_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags); avp->primary_sta_vif = false; if (ah->opmode == NL80211_IFTYPE_STATION) clear_bit(SC_OP_BEACONS, &sc->sc_flags); } ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_bss_assoc_iter, sc); if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) && ah->opmode == NL80211_IFTYPE_STATION) { memset(common->curbssid, 0, ETH_ALEN); common->curaid = 0; ath9k_hw_write_associd(sc->sc_ah); } } if (changed & BSS_CHANGED_IBSS) { memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = bss_conf->aid; ath9k_hw_write_associd(sc->sc_ah); } if ((changed & BSS_CHANGED_BEACON_ENABLED) || (changed & BSS_CHANGED_BEACON_INT)) { if (ah->opmode == NL80211_IFTYPE_AP && bss_conf->enable_beacon) ath9k_set_tsfadjust(sc, vif); if (ath9k_allow_beacon_config(sc, vif)) ath9k_beacon_config(sc, vif, changed); } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) slottime = 9; else slottime = 20; if (vif->type == NL80211_IFTYPE_AP) { /* * Defer update, so that connected stations can adjust * their settings at the same time. * See beacon.c for more details */ sc->beacon.slottime = slottime; sc->beacon.updateslot = UPDATE; } else { ah->slottime = slottime; ath9k_hw_init_global_settings(ah); } } if (changed & CHECK_ANI) ath_check_ani(sc); mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); #undef CHECK_ANI } static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; u64 tsf; mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); tsf = ath9k_hw_gettsf64(sc->sc_ah); ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); return tsf; } static void ath9k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct ath_softc *sc = hw->priv; mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); ath9k_hw_settsf64(sc->sc_ah, tsf); ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); } static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); ath9k_hw_reset_tsf(sc->sc_ah); ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); } static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct ath_softc *sc = hw->priv; int ret = 0; local_bh_disable(); switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: ath9k_ps_wakeup(sc); ret = ath_tx_aggr_start(sc, sta, tid, ssn); if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ath9k_ps_restore(sc); break; case IEEE80211_AMPDU_TX_STOP: ath9k_ps_wakeup(sc); ath_tx_aggr_stop(sc, sta, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ath9k_ps_restore(sc); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ath9k_ps_wakeup(sc); ath_tx_aggr_resume(sc, sta, tid); ath9k_ps_restore(sc); break; default: ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n"); } local_bh_enable(); return ret; } static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; unsigned long flags; int pos; spin_lock_irqsave(&common->cc_lock, flags); if (idx == 0) ath_update_survey_stats(sc); sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; if (sband && idx >= sband->n_channels) { idx -= sband->n_channels; sband = NULL; } if (!sband) sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; if (!sband || idx >= sband->n_channels) { spin_unlock_irqrestore(&common->cc_lock, flags); return -ENOENT; } chan = &sband->channels[idx]; pos = chan->hw_value; memcpy(survey, &sc->survey[pos], sizeof(*survey)); survey->channel = chan; spin_unlock_irqrestore(&common->cc_lock, flags); return 0; } static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; mutex_lock(&sc->mutex); ah->coverage_class = coverage_class; ath9k_ps_wakeup(sc); ath9k_hw_init_global_settings(ah); ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); } static void ath9k_flush(struct ieee80211_hw *hw, bool drop) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); int timeout = 200; /* ms */ int i, j; bool drain_txq; mutex_lock(&sc->mutex); cancel_delayed_work_sync(&sc->tx_complete_work); if (ah->ah_flags & AH_UNPLUGGED) { ath_dbg(common, ANY, "Device has been unplugged!\n"); mutex_unlock(&sc->mutex); return; } if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; } for (j = 0; j < timeout; j++) { bool npend = false; if (j) usleep_range(1000, 2000); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { if (!ATH_TXQ_SETUP(sc, i)) continue; npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]); if (npend) break; } if (!npend) break; } if (drop) { ath9k_ps_wakeup(sc); spin_lock_bh(&sc->sc_pcu_lock); drain_txq = ath_drain_all_txq(sc, false); spin_unlock_bh(&sc->sc_pcu_lock); if (!drain_txq) ath_reset(sc, false); ath9k_ps_restore(sc); ieee80211_wake_queues(hw); } ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); mutex_unlock(&sc->mutex); } static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; int i; for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { if (!ATH_TXQ_SETUP(sc, i)) continue; if (ath9k_has_pending_frames(sc, &sc->tx.txq[i])) return true; } return false; } static int ath9k_tx_last_beacon(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ieee80211_vif *vif; struct ath_vif *avp; struct ath_buf *bf; struct ath_tx_status ts; bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); int status; vif = sc->beacon.bslot[0]; if (!vif) return 0; if (!vif->bss_conf.enable_beacon) return 0; avp = (void *)vif->drv_priv; if (!sc->beacon.tx_processed && !edma) { tasklet_disable(&sc->bcon_tasklet); bf = avp->av_bcbuf; if (!bf || !bf->bf_mpdu) goto skip; status = ath9k_hw_txprocdesc(ah, bf->bf_desc, &ts); if (status == -EINPROGRESS) goto skip; sc->beacon.tx_processed = true; sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); skip: tasklet_enable(&sc->bcon_tasklet); } return sc->beacon.tx_last; } static int ath9k_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats; stats->dot11ACKFailureCount = mib_stats->ackrcv_bad; stats->dot11RTSFailureCount = mib_stats->rts_bad; stats->dot11FCSErrorCount = mib_stats->fcs_bad; stats->dot11RTSSuccessCount = mib_stats->rts_good; return 0; } static u32 fill_chainmask(u32 cap, u32 new) { u32 filled = 0; int i; for (i = 0; cap && new; i++, cap >>= 1) { if (!(cap & BIT(0))) continue; if (new & BIT(0)) filled |= BIT(i); new >>= 1; } return filled; } static bool validate_antenna_mask(struct ath_hw *ah, u32 val) { switch (val & 0x7) { case 0x1: case 0x3: case 0x7: return true; case 0x2: return (ah->caps.rx_chainmask == 1); default: return false; } } static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; if (ah->caps.rx_chainmask != 1) rx_ant |= tx_ant; if (!validate_antenna_mask(ah, rx_ant) || !tx_ant) return -EINVAL; sc->ant_rx = rx_ant; sc->ant_tx = tx_ant; if (ah->caps.rx_chainmask == 1) return 0; /* AR9100 runs into calibration issues if not all rx chains are enabled */ if (AR_SREV_9100(ah)) ah->rxchainmask = 0x7; else ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant); ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant); ath9k_reload_chainmask_settings(sc); return 0; } static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) { struct ath_softc *sc = hw->priv; *tx_ant = sc->ant_tx; *rx_ant = sc->ant_rx; return 0; } #ifdef CONFIG_ATH9K_DEBUGFS /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" static const char ath9k_gstrings_stats[][ETH_GSTRING_LEN] = { "tx_pkts_nic", "tx_bytes_nic", "rx_pkts_nic", "rx_bytes_nic", AMKSTR(d_tx_pkts), AMKSTR(d_tx_bytes), AMKSTR(d_tx_mpdus_queued), AMKSTR(d_tx_mpdus_completed), AMKSTR(d_tx_mpdu_xretries), AMKSTR(d_tx_aggregates), AMKSTR(d_tx_ampdus_queued_hw), AMKSTR(d_tx_ampdus_queued_sw), AMKSTR(d_tx_ampdus_completed), AMKSTR(d_tx_ampdu_retries), AMKSTR(d_tx_ampdu_xretries), AMKSTR(d_tx_fifo_underrun), AMKSTR(d_tx_op_exceeded), AMKSTR(d_tx_timer_expiry), AMKSTR(d_tx_desc_cfg_err), AMKSTR(d_tx_data_underrun), AMKSTR(d_tx_delim_underrun), "d_rx_decrypt_crc_err", "d_rx_phy_err", "d_rx_mic_err", "d_rx_pre_delim_crc_err", "d_rx_post_delim_crc_err", "d_rx_decrypt_busy_err", "d_rx_phyerr_radar", "d_rx_phyerr_ofdm_timing", "d_rx_phyerr_cck_timing", }; #define ATH9K_SSTATS_LEN ARRAY_SIZE(ath9k_gstrings_stats) static void ath9k_get_et_strings(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 sset, u8 *data) { if (sset == ETH_SS_STATS) memcpy(data, *ath9k_gstrings_stats, sizeof(ath9k_gstrings_stats)); } static int ath9k_get_et_sset_count(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int sset) { if (sset == ETH_SS_STATS) return ATH9K_SSTATS_LEN; return 0; } #define PR_QNUM(_n) (sc->tx.txq_map[_n]->axq_qnum) #define AWDATA(elem) \ do { \ data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].elem; \ data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].elem; \ data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].elem; \ data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].elem; \ } while (0) #define AWDATA_RX(elem) \ do { \ data[i++] = sc->debug.stats.rxstats.elem; \ } while (0) static void ath9k_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ethtool_stats *stats, u64 *data) { struct ath_softc *sc = hw->priv; int i = 0; data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_pkts_all + sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_pkts_all + sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_pkts_all + sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_pkts_all); data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_bytes_all + sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_bytes_all + sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_bytes_all + sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_bytes_all); AWDATA_RX(rx_pkts_all); AWDATA_RX(rx_bytes_all); AWDATA(tx_pkts_all); AWDATA(tx_bytes_all); AWDATA(queued); AWDATA(completed); AWDATA(xretries); AWDATA(a_aggr); AWDATA(a_queued_hw); AWDATA(a_queued_sw); AWDATA(a_completed); AWDATA(a_retries); AWDATA(a_xretries); AWDATA(fifo_underrun); AWDATA(xtxop); AWDATA(timer_exp); AWDATA(desc_cfg_err); AWDATA(data_underrun); AWDATA(delim_underrun); AWDATA_RX(decrypt_crc_err); AWDATA_RX(phy_err); AWDATA_RX(mic_err); AWDATA_RX(pre_delim_crc_err); AWDATA_RX(post_delim_crc_err); AWDATA_RX(decrypt_busy_err); AWDATA_RX(phy_err_stats[ATH9K_PHYERR_RADAR]); AWDATA_RX(phy_err_stats[ATH9K_PHYERR_OFDM_TIMING]); AWDATA_RX(phy_err_stats[ATH9K_PHYERR_CCK_TIMING]); WARN_ON(i != ATH9K_SSTATS_LEN); } /* End of ethtool get-stats functions */ #endif #ifdef CONFIG_PM_SLEEP static void ath9k_wow_map_triggers(struct ath_softc *sc, struct cfg80211_wowlan *wowlan, u32 *wow_triggers) { if (wowlan->disconnect) *wow_triggers |= AH_WOW_LINK_CHANGE | AH_WOW_BEACON_MISS; if (wowlan->magic_pkt) *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; if (wowlan->n_patterns) *wow_triggers |= AH_WOW_USER_PATTERN_EN; sc->wow_enabled = *wow_triggers; } static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_capabilities *pcaps = &ah->caps; int pattern_count = 0; int i, byte_cnt; u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; u8 dis_deauth_mask[MAX_PATTERN_SIZE]; memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE); memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE); /* * Create Dissassociate / Deauthenticate packet filter * * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes * +--------------+----------+---------+--------+--------+---- * + Frame Control+ Duration + DA + SA + BSSID + * +--------------+----------+---------+--------+--------+---- * * The above is the management frame format for disassociate/ * deauthenticate pattern, from this we need to match the first byte * of 'Frame Control' and DA, SA, and BSSID fields * (skipping 2nd byte of FC and Duration feild. * * Disassociate pattern * -------------------- * Frame control = 00 00 1010 * DA, SA, BSSID = x:x:x:x:x:x * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x * | x:x:x:x:x:x -- 22 bytes * * Deauthenticate pattern * ---------------------- * Frame control = 00 00 1100 * DA, SA, BSSID = x:x:x:x:x:x * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x * | x:x:x:x:x:x -- 22 bytes */ /* Create Disassociate Pattern first */ byte_cnt = 0; /* Fill out the mask with all FF's */ for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++) dis_deauth_mask[i] = 0xff; /* copy the first byte of frame control field */ dis_deauth_pattern[byte_cnt] = 0xa0; byte_cnt++; /* skip 2nd byte of frame control and Duration field */ byte_cnt += 3; /* * need not match the destination mac address, it can be a broadcast * mac address or an unicast to this station */ byte_cnt += 6; /* copy the source mac address */ memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); byte_cnt += 6; /* copy the bssid, its same as the source mac address */ memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); /* Create Disassociate pattern mask */ if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT) { if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD) { /* * for AR9280, because of hardware limitation, the * first 4 bytes have to be matched for all patterns. * the mask for disassociation and de-auth pattern * matching need to enable the first 4 bytes. * also the duration field needs to be filled. */ dis_deauth_mask[0] = 0xf0; /* * fill in duration field FIXME: what is the exact value ? */ dis_deauth_pattern[2] = 0xff; dis_deauth_pattern[3] = 0xff; } else { dis_deauth_mask[0] = 0xfe; } dis_deauth_mask[1] = 0x03; dis_deauth_mask[2] = 0xc0; } else { dis_deauth_mask[0] = 0xef; dis_deauth_mask[1] = 0x3f; dis_deauth_mask[2] = 0x00; dis_deauth_mask[3] = 0xfc; } ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n"); ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, pattern_count, byte_cnt); pattern_count++; /* * for de-authenticate pattern, only the first byte of the frame * control field gets changed from 0xA0 to 0xC0 */ dis_deauth_pattern[0] = 0xC0; ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, pattern_count, byte_cnt); } static void ath9k_wow_add_pattern(struct ath_softc *sc, struct cfg80211_wowlan *wowlan) { struct ath_hw *ah = sc->sc_ah; struct ath9k_wow_pattern *wow_pattern = NULL; struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns; int mask_len; s8 i = 0; if (!wowlan->n_patterns) return; /* * Add the new user configured patterns */ for (i = 0; i < wowlan->n_patterns; i++) { wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL); if (!wow_pattern) return; /* * TODO: convert the generic user space pattern to * appropriate chip specific/802.11 pattern. */ mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE); memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE); memcpy(wow_pattern->pattern_bytes, patterns[i].pattern, patterns[i].pattern_len); memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len); wow_pattern->pattern_len = patterns[i].pattern_len; /* * just need to take care of deauth and disssoc pattern, * make sure we don't overwrite them. */ ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes, wow_pattern->mask_bytes, i + 2, wow_pattern->pattern_len); kfree(wow_pattern); } } static int ath9k_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u32 wow_triggers_enabled = 0; int ret = 0; mutex_lock(&sc->mutex); ath_cancel_work(sc); ath_stop_ani(sc); del_timer_sync(&sc->rx_poll_timer); if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { ath_dbg(common, ANY, "Device not present\n"); ret = -EINVAL; goto fail_wow; } if (WARN_ON(!wowlan)) { ath_dbg(common, WOW, "None of the WoW triggers enabled\n"); ret = -EINVAL; goto fail_wow; } if (!device_can_wakeup(sc->dev)) { ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n"); ret = 1; goto fail_wow; } /* * none of the sta vifs are associated * and we are not currently handling multivif * cases, for instance we have to seperately * configure 'keep alive frame' for each * STA. */ if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { ath_dbg(common, WOW, "None of the STA vifs are associated\n"); ret = 1; goto fail_wow; } if (sc->nvifs > 1) { ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); ret = 1; goto fail_wow; } ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled); ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n", wow_triggers_enabled); ath9k_ps_wakeup(sc); ath9k_stop_btcoex(sc); /* * Enable wake up on recieving disassoc/deauth * frame by default. */ ath9k_wow_add_disassoc_deauth_pattern(sc); if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN) ath9k_wow_add_pattern(sc, wowlan); spin_lock_bh(&sc->sc_pcu_lock); /* * To avoid false wake, we enable beacon miss interrupt only * when we go to sleep. We save the current interrupt mask * so we can restore it after the system wakes up */ sc->wow_intr_before_sleep = ah->imask; ah->imask &= ~ATH9K_INT_GLOBAL; ath9k_hw_disable_interrupts(ah); ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); spin_unlock_bh(&sc->sc_pcu_lock); /* * we can now sync irq and kill any running tasklets, since we already * disabled interrupts and not holding a spin lock */ synchronize_irq(sc->irq); tasklet_kill(&sc->intr_tq); ath9k_hw_wow_enable(ah, wow_triggers_enabled); ath9k_ps_restore(sc); ath_dbg(common, ANY, "WoW enabled in ath9k\n"); atomic_inc(&sc->wow_sleep_proc_intr); fail_wow: mutex_unlock(&sc->mutex); return ret; } static int ath9k_resume(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u32 wow_status; mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); spin_lock_bh(&sc->sc_pcu_lock); ath9k_hw_disable_interrupts(ah); ah->imask = sc->wow_intr_before_sleep; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); spin_unlock_bh(&sc->sc_pcu_lock); wow_status = ath9k_hw_wow_wakeup(ah); if (atomic_read(&sc->wow_got_bmiss_intr) == 0) { /* * some devices may not pick beacon miss * as the reason they woke up so we add * that here for that shortcoming. */ wow_status |= AH_WOW_BEACON_MISS; atomic_dec(&sc->wow_got_bmiss_intr); ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n"); } atomic_dec(&sc->wow_sleep_proc_intr); if (wow_status) { ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n", ath9k_hw_wow_event_to_string(wow_status), wow_status); } ath_restart_work(sc); ath9k_start_btcoex(sc); ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); return 0; } static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct ath_softc *sc = hw->priv; mutex_lock(&sc->mutex); device_init_wakeup(sc->dev, 1); device_set_wakeup_enable(sc->dev, enabled); mutex_unlock(&sc->mutex); } #endif struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, .stop = ath9k_stop, .add_interface = ath9k_add_interface, .change_interface = ath9k_change_interface, .remove_interface = ath9k_remove_interface, .config = ath9k_config, .configure_filter = ath9k_configure_filter, .sta_add = ath9k_sta_add, .sta_remove = ath9k_sta_remove, .sta_notify = ath9k_sta_notify, .conf_tx = ath9k_conf_tx, .bss_info_changed = ath9k_bss_info_changed, .set_key = ath9k_set_key, .get_tsf = ath9k_get_tsf, .set_tsf = ath9k_set_tsf, .reset_tsf = ath9k_reset_tsf, .ampdu_action = ath9k_ampdu_action, .get_survey = ath9k_get_survey, .rfkill_poll = ath9k_rfkill_poll_state, .set_coverage_class = ath9k_set_coverage_class, .flush = ath9k_flush, .tx_frames_pending = ath9k_tx_frames_pending, .tx_last_beacon = ath9k_tx_last_beacon, .get_stats = ath9k_get_stats, .set_antenna = ath9k_set_antenna, .get_antenna = ath9k_get_antenna, #ifdef CONFIG_PM_SLEEP .suspend = ath9k_suspend, .resume = ath9k_resume, .set_wakeup = ath9k_set_wakeup, #endif #ifdef CONFIG_ATH9K_DEBUGFS .get_et_sset_count = ath9k_get_et_sset_count, .get_et_stats = ath9k_get_et_stats, .get_et_strings = ath9k_get_et_strings, #endif }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/mac.h0000644000175000017500000005237212026211315023177 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef MAC_H #define MAC_H #define set11nTries(_series, _index) \ (SM((_series)[_index].Tries, AR_XmitDataTries##_index)) #define set11nRate(_series, _index) \ (SM((_series)[_index].Rate, AR_XmitRate##_index)) #define set11nPktDurRTSCTS(_series, _index) \ (SM((_series)[_index].PktDuration, AR_PacketDur##_index) | \ ((_series)[_index].RateFlags & ATH9K_RATESERIES_RTS_CTS ? \ AR_RTSCTSQual##_index : 0)) #define set11nRateFlags(_series, _index) \ (((_series)[_index].RateFlags & ATH9K_RATESERIES_2040 ? \ AR_2040_##_index : 0) \ |((_series)[_index].RateFlags & ATH9K_RATESERIES_HALFGI ? \ AR_GI##_index : 0) \ |((_series)[_index].RateFlags & ATH9K_RATESERIES_STBC ? \ AR_STBC##_index : 0) \ |SM((_series)[_index].ChSel, AR_ChainSel##_index)) #define CCK_SIFS_TIME 10 #define CCK_PREAMBLE_BITS 144 #define CCK_PLCP_BITS 48 #define OFDM_SIFS_TIME 16 #define OFDM_PREAMBLE_TIME 20 #define OFDM_PLCP_BITS 22 #define OFDM_SYMBOL_TIME 4 #define OFDM_SIFS_TIME_HALF 32 #define OFDM_PREAMBLE_TIME_HALF 40 #define OFDM_PLCP_BITS_HALF 22 #define OFDM_SYMBOL_TIME_HALF 8 #define OFDM_SIFS_TIME_QUARTER 64 #define OFDM_PREAMBLE_TIME_QUARTER 80 #define OFDM_PLCP_BITS_QUARTER 22 #define OFDM_SYMBOL_TIME_QUARTER 16 #define INIT_AIFS 2 #define INIT_CWMIN 15 #define INIT_CWMIN_11B 31 #define INIT_CWMAX 1023 #define INIT_SH_RETRY 10 #define INIT_LG_RETRY 10 #define INIT_SSH_RETRY 32 #define INIT_SLG_RETRY 32 #define ATH9K_SLOT_TIME_6 6 #define ATH9K_SLOT_TIME_9 9 #define ATH9K_SLOT_TIME_20 20 #define ATH9K_TXERR_XRETRY 0x01 #define ATH9K_TXERR_FILT 0x02 #define ATH9K_TXERR_FIFO 0x04 #define ATH9K_TXERR_XTXOP 0x08 #define ATH9K_TXERR_TIMER_EXPIRED 0x10 #define ATH9K_TX_ACKED 0x20 #define ATH9K_TX_FLUSH 0x40 #define ATH9K_TXERR_MASK \ (ATH9K_TXERR_XRETRY | ATH9K_TXERR_FILT | ATH9K_TXERR_FIFO | \ ATH9K_TXERR_XTXOP | ATH9K_TXERR_TIMER_EXPIRED | ATH9K_TX_FLUSH) #define ATH9K_TX_BA 0x01 #define ATH9K_TX_PWRMGMT 0x02 #define ATH9K_TX_DESC_CFG_ERR 0x04 #define ATH9K_TX_DATA_UNDERRUN 0x08 #define ATH9K_TX_DELIM_UNDERRUN 0x10 #define ATH9K_TX_SW_FILTERED 0x80 /* 64 bytes */ #define MIN_TX_FIFO_THRESHOLD 0x1 /* * Single stream device AR9285 and AR9271 require 2 KB * to work around a hardware issue, all other devices * have can use the max 4 KB limit. */ #define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1) struct ath_tx_status { u32 ts_tstamp; u16 ts_seqnum; u8 ts_status; u8 ts_rateindex; int8_t ts_rssi; u8 ts_shortretry; u8 ts_longretry; u8 ts_virtcol; u8 ts_flags; int8_t ts_rssi_ctl0; int8_t ts_rssi_ctl1; int8_t ts_rssi_ctl2; int8_t ts_rssi_ext0; int8_t ts_rssi_ext1; int8_t ts_rssi_ext2; u8 qid; u16 desc_id; u8 tid; u32 ba_low; u32 ba_high; u32 evm0; u32 evm1; u32 evm2; }; struct ath_rx_status { u32 rs_tstamp; u16 rs_datalen; u8 rs_status; u8 rs_phyerr; int8_t rs_rssi; u8 rs_keyix; u8 rs_rate; u8 rs_antenna; u8 rs_more; int8_t rs_rssi_ctl0; int8_t rs_rssi_ctl1; int8_t rs_rssi_ctl2; int8_t rs_rssi_ext0; int8_t rs_rssi_ext1; int8_t rs_rssi_ext2; u8 rs_isaggr; u8 rs_moreaggr; u8 rs_num_delims; u8 rs_flags; bool is_mybeacon; u32 evm0; u32 evm1; u32 evm2; u32 evm3; u32 evm4; }; struct ath_htc_rx_status { __be64 rs_tstamp; __be16 rs_datalen; u8 rs_status; u8 rs_phyerr; int8_t rs_rssi; int8_t rs_rssi_ctl0; int8_t rs_rssi_ctl1; int8_t rs_rssi_ctl2; int8_t rs_rssi_ext0; int8_t rs_rssi_ext1; int8_t rs_rssi_ext2; u8 rs_keyix; u8 rs_rate; u8 rs_antenna; u8 rs_more; u8 rs_isaggr; u8 rs_moreaggr; u8 rs_num_delims; u8 rs_flags; u8 rs_dummy; __be32 evm0; __be32 evm1; __be32 evm2; }; #define ATH9K_RXERR_CRC 0x01 #define ATH9K_RXERR_PHY 0x02 #define ATH9K_RXERR_FIFO 0x04 #define ATH9K_RXERR_DECRYPT 0x08 #define ATH9K_RXERR_MIC 0x10 #define ATH9K_RXERR_KEYMISS 0x20 #define ATH9K_RX_MORE 0x01 #define ATH9K_RX_MORE_AGGR 0x02 #define ATH9K_RX_GI 0x04 #define ATH9K_RX_2040 0x08 #define ATH9K_RX_DELIM_CRC_PRE 0x10 #define ATH9K_RX_DELIM_CRC_POST 0x20 #define ATH9K_RX_DECRYPT_BUSY 0x40 #define ATH9K_RXKEYIX_INVALID ((u8)-1) #define ATH9K_TXKEYIX_INVALID ((u8)-1) enum ath9k_phyerr { ATH9K_PHYERR_UNDERRUN = 0, /* Transmit underrun */ ATH9K_PHYERR_TIMING = 1, /* Timing error */ ATH9K_PHYERR_PARITY = 2, /* Illegal parity */ ATH9K_PHYERR_RATE = 3, /* Illegal rate */ ATH9K_PHYERR_LENGTH = 4, /* Illegal length */ ATH9K_PHYERR_RADAR = 5, /* Radar detect */ ATH9K_PHYERR_SERVICE = 6, /* Illegal service */ ATH9K_PHYERR_TOR = 7, /* Transmit override receive */ ATH9K_PHYERR_OFDM_TIMING = 17, ATH9K_PHYERR_OFDM_SIGNAL_PARITY = 18, ATH9K_PHYERR_OFDM_RATE_ILLEGAL = 19, ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL = 20, ATH9K_PHYERR_OFDM_POWER_DROP = 21, ATH9K_PHYERR_OFDM_SERVICE = 22, ATH9K_PHYERR_OFDM_RESTART = 23, ATH9K_PHYERR_FALSE_RADAR_EXT = 24, ATH9K_PHYERR_CCK_TIMING = 25, ATH9K_PHYERR_CCK_HEADER_CRC = 26, ATH9K_PHYERR_CCK_RATE_ILLEGAL = 27, ATH9K_PHYERR_CCK_SERVICE = 30, ATH9K_PHYERR_CCK_RESTART = 31, ATH9K_PHYERR_CCK_LENGTH_ILLEGAL = 32, ATH9K_PHYERR_CCK_POWER_DROP = 33, ATH9K_PHYERR_HT_CRC_ERROR = 34, ATH9K_PHYERR_HT_LENGTH_ILLEGAL = 35, ATH9K_PHYERR_HT_RATE_ILLEGAL = 36, ATH9K_PHYERR_MAX = 37, }; struct ath_desc { u32 ds_link; u32 ds_data; u32 ds_ctl0; u32 ds_ctl1; u32 ds_hw[20]; void *ds_vdata; } __packed __aligned(4); #define ATH9K_TXDESC_NOACK 0x0002 #define ATH9K_TXDESC_RTSENA 0x0004 #define ATH9K_TXDESC_CTSENA 0x0008 /* ATH9K_TXDESC_INTREQ forces a tx interrupt to be generated for * the descriptor its marked on. We take a tx interrupt to reap * descriptors when the h/w hits an EOL condition or * when the descriptor is specifically marked to generate * an interrupt with this flag. Descriptors should be * marked periodically to insure timely replenishing of the * supply needed for sending frames. Defering interrupts * reduces system load and potentially allows more concurrent * work to be done but if done to aggressively can cause * senders to backup. When the hardware queue is left too * large rate control information may also be too out of * date. An Alternative for this is TX interrupt mitigation * but this needs more testing. */ #define ATH9K_TXDESC_INTREQ 0x0010 #define ATH9K_TXDESC_VEOL 0x0020 #define ATH9K_TXDESC_EXT_ONLY 0x0040 #define ATH9K_TXDESC_EXT_AND_CTL 0x0080 #define ATH9K_TXDESC_VMF 0x0100 #define ATH9K_TXDESC_FRAG_IS_ON 0x0200 #define ATH9K_TXDESC_LOWRXCHAIN 0x0400 #define ATH9K_TXDESC_LDPC 0x0800 #define ATH9K_TXDESC_CLRDMASK 0x1000 #define ATH9K_TXDESC_PAPRD 0x70000 #define ATH9K_TXDESC_PAPRD_S 16 #define ATH9K_RXDESC_INTREQ 0x0020 struct ar5416_desc { u32 ds_link; u32 ds_data; u32 ds_ctl0; u32 ds_ctl1; union { struct { u32 ctl2; u32 ctl3; u32 ctl4; u32 ctl5; u32 ctl6; u32 ctl7; u32 ctl8; u32 ctl9; u32 ctl10; u32 ctl11; u32 status0; u32 status1; u32 status2; u32 status3; u32 status4; u32 status5; u32 status6; u32 status7; u32 status8; u32 status9; } tx; struct { u32 status0; u32 status1; u32 status2; u32 status3; u32 status4; u32 status5; u32 status6; u32 status7; u32 status8; } rx; } u; } __packed __aligned(4); #define AR5416DESC(_ds) ((struct ar5416_desc *)(_ds)) #define AR5416DESC_CONST(_ds) ((const struct ar5416_desc *)(_ds)) #define ds_ctl2 u.tx.ctl2 #define ds_ctl3 u.tx.ctl3 #define ds_ctl4 u.tx.ctl4 #define ds_ctl5 u.tx.ctl5 #define ds_ctl6 u.tx.ctl6 #define ds_ctl7 u.tx.ctl7 #define ds_ctl8 u.tx.ctl8 #define ds_ctl9 u.tx.ctl9 #define ds_ctl10 u.tx.ctl10 #define ds_ctl11 u.tx.ctl11 #define ds_txstatus0 u.tx.status0 #define ds_txstatus1 u.tx.status1 #define ds_txstatus2 u.tx.status2 #define ds_txstatus3 u.tx.status3 #define ds_txstatus4 u.tx.status4 #define ds_txstatus5 u.tx.status5 #define ds_txstatus6 u.tx.status6 #define ds_txstatus7 u.tx.status7 #define ds_txstatus8 u.tx.status8 #define ds_txstatus9 u.tx.status9 #define ds_rxstatus0 u.rx.status0 #define ds_rxstatus1 u.rx.status1 #define ds_rxstatus2 u.rx.status2 #define ds_rxstatus3 u.rx.status3 #define ds_rxstatus4 u.rx.status4 #define ds_rxstatus5 u.rx.status5 #define ds_rxstatus6 u.rx.status6 #define ds_rxstatus7 u.rx.status7 #define ds_rxstatus8 u.rx.status8 #define AR_FrameLen 0x00000fff #define AR_VirtMoreFrag 0x00001000 #define AR_TxCtlRsvd00 0x0000e000 #define AR_XmitPower 0x003f0000 #define AR_XmitPower_S 16 #define AR_RTSEnable 0x00400000 #define AR_VEOL 0x00800000 #define AR_ClrDestMask 0x01000000 #define AR_TxCtlRsvd01 0x1e000000 #define AR_TxIntrReq 0x20000000 #define AR_DestIdxValid 0x40000000 #define AR_CTSEnable 0x80000000 #define AR_TxMore 0x00001000 #define AR_DestIdx 0x000fe000 #define AR_DestIdx_S 13 #define AR_FrameType 0x00f00000 #define AR_FrameType_S 20 #define AR_NoAck 0x01000000 #define AR_InsertTS 0x02000000 #define AR_CorruptFCS 0x04000000 #define AR_ExtOnly 0x08000000 #define AR_ExtAndCtl 0x10000000 #define AR_MoreAggr 0x20000000 #define AR_IsAggr 0x40000000 #define AR_BurstDur 0x00007fff #define AR_BurstDur_S 0 #define AR_DurUpdateEna 0x00008000 #define AR_XmitDataTries0 0x000f0000 #define AR_XmitDataTries0_S 16 #define AR_XmitDataTries1 0x00f00000 #define AR_XmitDataTries1_S 20 #define AR_XmitDataTries2 0x0f000000 #define AR_XmitDataTries2_S 24 #define AR_XmitDataTries3 0xf0000000 #define AR_XmitDataTries3_S 28 #define AR_XmitRate0 0x000000ff #define AR_XmitRate0_S 0 #define AR_XmitRate1 0x0000ff00 #define AR_XmitRate1_S 8 #define AR_XmitRate2 0x00ff0000 #define AR_XmitRate2_S 16 #define AR_XmitRate3 0xff000000 #define AR_XmitRate3_S 24 #define AR_PacketDur0 0x00007fff #define AR_PacketDur0_S 0 #define AR_RTSCTSQual0 0x00008000 #define AR_PacketDur1 0x7fff0000 #define AR_PacketDur1_S 16 #define AR_RTSCTSQual1 0x80000000 #define AR_PacketDur2 0x00007fff #define AR_PacketDur2_S 0 #define AR_RTSCTSQual2 0x00008000 #define AR_PacketDur3 0x7fff0000 #define AR_PacketDur3_S 16 #define AR_RTSCTSQual3 0x80000000 #define AR_AggrLen 0x0000ffff #define AR_AggrLen_S 0 #define AR_TxCtlRsvd60 0x00030000 #define AR_PadDelim 0x03fc0000 #define AR_PadDelim_S 18 #define AR_EncrType 0x0c000000 #define AR_EncrType_S 26 #define AR_TxCtlRsvd61 0xf0000000 #define AR_LDPC 0x80000000 #define AR_2040_0 0x00000001 #define AR_GI0 0x00000002 #define AR_ChainSel0 0x0000001c #define AR_ChainSel0_S 2 #define AR_2040_1 0x00000020 #define AR_GI1 0x00000040 #define AR_ChainSel1 0x00000380 #define AR_ChainSel1_S 7 #define AR_2040_2 0x00000400 #define AR_GI2 0x00000800 #define AR_ChainSel2 0x00007000 #define AR_ChainSel2_S 12 #define AR_2040_3 0x00008000 #define AR_GI3 0x00010000 #define AR_ChainSel3 0x000e0000 #define AR_ChainSel3_S 17 #define AR_RTSCTSRate 0x0ff00000 #define AR_RTSCTSRate_S 20 #define AR_STBC0 0x10000000 #define AR_STBC1 0x20000000 #define AR_STBC2 0x40000000 #define AR_STBC3 0x80000000 #define AR_TxRSSIAnt00 0x000000ff #define AR_TxRSSIAnt00_S 0 #define AR_TxRSSIAnt01 0x0000ff00 #define AR_TxRSSIAnt01_S 8 #define AR_TxRSSIAnt02 0x00ff0000 #define AR_TxRSSIAnt02_S 16 #define AR_TxStatusRsvd00 0x3f000000 #define AR_TxBaStatus 0x40000000 #define AR_TxStatusRsvd01 0x80000000 /* * AR_FrmXmitOK - Frame transmission success flag. If set, the frame was * transmitted successfully. If clear, no ACK or BA was received to indicate * successful transmission when we were expecting an ACK or BA. */ #define AR_FrmXmitOK 0x00000001 #define AR_ExcessiveRetries 0x00000002 #define AR_FIFOUnderrun 0x00000004 #define AR_Filtered 0x00000008 #define AR_RTSFailCnt 0x000000f0 #define AR_RTSFailCnt_S 4 #define AR_DataFailCnt 0x00000f00 #define AR_DataFailCnt_S 8 #define AR_VirtRetryCnt 0x0000f000 #define AR_VirtRetryCnt_S 12 #define AR_TxDelimUnderrun 0x00010000 #define AR_TxDataUnderrun 0x00020000 #define AR_DescCfgErr 0x00040000 #define AR_TxTimerExpired 0x00080000 #define AR_TxStatusRsvd10 0xfff00000 #define AR_SendTimestamp ds_txstatus2 #define AR_BaBitmapLow ds_txstatus3 #define AR_BaBitmapHigh ds_txstatus4 #define AR_TxRSSIAnt10 0x000000ff #define AR_TxRSSIAnt10_S 0 #define AR_TxRSSIAnt11 0x0000ff00 #define AR_TxRSSIAnt11_S 8 #define AR_TxRSSIAnt12 0x00ff0000 #define AR_TxRSSIAnt12_S 16 #define AR_TxRSSICombined 0xff000000 #define AR_TxRSSICombined_S 24 #define AR_TxTid 0xf0000000 #define AR_TxTid_S 28 #define AR_TxEVM0 ds_txstatus5 #define AR_TxEVM1 ds_txstatus6 #define AR_TxEVM2 ds_txstatus7 #define AR_TxDone 0x00000001 #define AR_SeqNum 0x00001ffe #define AR_SeqNum_S 1 #define AR_TxStatusRsvd80 0x0001e000 #define AR_TxOpExceeded 0x00020000 #define AR_TxStatusRsvd81 0x001c0000 #define AR_FinalTxIdx 0x00600000 #define AR_FinalTxIdx_S 21 #define AR_TxStatusRsvd82 0x01800000 #define AR_PowerMgmt 0x02000000 #define AR_TxStatusRsvd83 0xfc000000 #define AR_RxCTLRsvd00 0xffffffff #define AR_RxCtlRsvd00 0x00001000 #define AR_RxIntrReq 0x00002000 #define AR_RxCtlRsvd01 0xffffc000 #define AR_RxRSSIAnt00 0x000000ff #define AR_RxRSSIAnt00_S 0 #define AR_RxRSSIAnt01 0x0000ff00 #define AR_RxRSSIAnt01_S 8 #define AR_RxRSSIAnt02 0x00ff0000 #define AR_RxRSSIAnt02_S 16 #define AR_RxRate 0xff000000 #define AR_RxRate_S 24 #define AR_RxStatusRsvd00 0xff000000 #define AR_DataLen 0x00000fff #define AR_RxMore 0x00001000 #define AR_NumDelim 0x003fc000 #define AR_NumDelim_S 14 #define AR_RxStatusRsvd10 0xff800000 #define AR_RcvTimestamp ds_rxstatus2 #define AR_GI 0x00000001 #define AR_2040 0x00000002 #define AR_Parallel40 0x00000004 #define AR_Parallel40_S 2 #define AR_RxStatusRsvd30 0x000000f8 #define AR_RxAntenna 0xffffff00 #define AR_RxAntenna_S 8 #define AR_RxRSSIAnt10 0x000000ff #define AR_RxRSSIAnt10_S 0 #define AR_RxRSSIAnt11 0x0000ff00 #define AR_RxRSSIAnt11_S 8 #define AR_RxRSSIAnt12 0x00ff0000 #define AR_RxRSSIAnt12_S 16 #define AR_RxRSSICombined 0xff000000 #define AR_RxRSSICombined_S 24 #define AR_RxEVM0 ds_rxstatus4 #define AR_RxEVM1 ds_rxstatus5 #define AR_RxEVM2 ds_rxstatus6 #define AR_RxDone 0x00000001 #define AR_RxFrameOK 0x00000002 #define AR_CRCErr 0x00000004 #define AR_DecryptCRCErr 0x00000008 #define AR_PHYErr 0x00000010 #define AR_MichaelErr 0x00000020 #define AR_PreDelimCRCErr 0x00000040 #define AR_RxStatusRsvd70 0x00000080 #define AR_RxKeyIdxValid 0x00000100 #define AR_KeyIdx 0x0000fe00 #define AR_KeyIdx_S 9 #define AR_PHYErrCode 0x0000ff00 #define AR_PHYErrCode_S 8 #define AR_RxMoreAggr 0x00010000 #define AR_RxAggr 0x00020000 #define AR_PostDelimCRCErr 0x00040000 #define AR_RxStatusRsvd71 0x3ff80000 #define AR_DecryptBusyErr 0x40000000 #define AR_KeyMiss 0x80000000 enum ath9k_tx_queue { ATH9K_TX_QUEUE_INACTIVE = 0, ATH9K_TX_QUEUE_DATA, ATH9K_TX_QUEUE_BEACON, ATH9K_TX_QUEUE_CAB, ATH9K_TX_QUEUE_UAPSD, ATH9K_TX_QUEUE_PSPOLL }; #define ATH9K_NUM_TX_QUEUES 10 /* Used as a queue subtype instead of a WMM AC */ #define ATH9K_WME_UPSD 4 enum ath9k_tx_queue_flags { TXQ_FLAG_TXINT_ENABLE = 0x0001, TXQ_FLAG_TXDESCINT_ENABLE = 0x0002, TXQ_FLAG_TXEOLINT_ENABLE = 0x0004, TXQ_FLAG_TXURNINT_ENABLE = 0x0008, TXQ_FLAG_BACKOFF_DISABLE = 0x0010, TXQ_FLAG_COMPRESSION_ENABLE = 0x0020, TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040, TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080, }; #define ATH9K_TXQ_USEDEFAULT ((u32) -1) #define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 #define ATH9K_DECOMP_MASK_SIZE 128 #define ATH9K_READY_TIME_LO_BOUND 50 #define ATH9K_READY_TIME_HI_BOUND 96 enum ath9k_pkt_type { ATH9K_PKT_TYPE_NORMAL = 0, ATH9K_PKT_TYPE_ATIM, ATH9K_PKT_TYPE_PSPOLL, ATH9K_PKT_TYPE_BEACON, ATH9K_PKT_TYPE_PROBE_RESP, ATH9K_PKT_TYPE_CHIRP, ATH9K_PKT_TYPE_GRP_POLL, }; struct ath9k_tx_queue_info { u32 tqi_ver; enum ath9k_tx_queue tqi_type; int tqi_subtype; enum ath9k_tx_queue_flags tqi_qflags; u32 tqi_priority; u32 tqi_aifs; u32 tqi_cwmin; u32 tqi_cwmax; u16 tqi_shretry; u16 tqi_lgretry; u32 tqi_cbrPeriod; u32 tqi_cbrOverflowLimit; u32 tqi_burstTime; u32 tqi_readyTime; u32 tqi_physCompBuf; u32 tqi_intFlags; }; enum ath9k_rx_filter { ATH9K_RX_FILTER_UCAST = 0x00000001, ATH9K_RX_FILTER_MCAST = 0x00000002, ATH9K_RX_FILTER_BCAST = 0x00000004, ATH9K_RX_FILTER_CONTROL = 0x00000008, ATH9K_RX_FILTER_BEACON = 0x00000010, ATH9K_RX_FILTER_PROM = 0x00000020, ATH9K_RX_FILTER_PROBEREQ = 0x00000080, ATH9K_RX_FILTER_PHYERR = 0x00000100, ATH9K_RX_FILTER_MYBEACON = 0x00000200, ATH9K_RX_FILTER_COMP_BAR = 0x00000400, ATH9K_RX_FILTER_COMP_BA = 0x00000800, ATH9K_RX_FILTER_UNCOMP_BA_BAR = 0x00001000, ATH9K_RX_FILTER_PSPOLL = 0x00004000, ATH9K_RX_FILTER_PHYRADAR = 0x00002000, ATH9K_RX_FILTER_MCAST_BCAST_ALL = 0x00008000, ATH9K_RX_FILTER_CONTROL_WRAPPER = 0x00080000, ATH9K_RX_FILTER_4ADDRESS = 0x00100000, }; #define ATH9K_RATESERIES_RTS_CTS 0x0001 #define ATH9K_RATESERIES_2040 0x0002 #define ATH9K_RATESERIES_HALFGI 0x0004 #define ATH9K_RATESERIES_STBC 0x0008 struct ath9k_11n_rate_series { u32 Tries; u32 Rate; u32 PktDuration; u32 ChSel; u32 RateFlags; }; enum aggr_type { AGGR_BUF_NONE, AGGR_BUF_FIRST, AGGR_BUF_MIDDLE, AGGR_BUF_LAST, }; enum ath9k_key_type { ATH9K_KEY_TYPE_CLEAR, ATH9K_KEY_TYPE_WEP, ATH9K_KEY_TYPE_AES, ATH9K_KEY_TYPE_TKIP, }; struct ath_tx_info { u8 qcu; bool is_first; bool is_last; enum aggr_type aggr; u8 ndelim; u16 aggr_len; dma_addr_t link; int pkt_len; u32 flags; dma_addr_t buf_addr[4]; int buf_len[4]; struct ath9k_11n_rate_series rates[4]; u8 rtscts_rate; bool dur_update; enum ath9k_pkt_type type; enum ath9k_key_type keytype; u8 keyix; u8 txpower; }; struct ath_hw; struct ath9k_channel; enum ath9k_int; u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q); void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp); void ath9k_hw_txstart(struct ath_hw *ah, u32 q); u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q); bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel); bool ath9k_hw_stop_dma_queue(struct ath_hw *ah, u32 q); void ath9k_hw_abort_tx_dma(struct ath_hw *ah); bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, const struct ath9k_tx_queue_info *qinfo); bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, struct ath9k_tx_queue_info *qinfo); int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, const struct ath9k_tx_queue_info *qinfo); bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q); bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q); int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, struct ath_rx_status *rs); void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags); bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set); void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); void ath9k_hw_abortpcurecv(struct ath_hw *ah); bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset); int ath9k_hw_beaconq_setup(struct ath_hw *ah); /* Interrupt Handling */ bool ath9k_hw_intrpend(struct ath_hw *ah); void ath9k_hw_set_interrupts(struct ath_hw *ah); void ath9k_hw_enable_interrupts(struct ath_hw *ah); void ath9k_hw_disable_interrupts(struct ath_hw *ah); void ath9k_hw_kill_interrupts(struct ath_hw *ah); void ar9002_hw_attach_mac_ops(struct ath_hw *ah); #endif /* MAC_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/mac.c0000644000175000017500000006230312026211315023165 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "hw-ops.h" #include static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah, struct ath9k_tx_queue_info *qi) { ath_dbg(ath9k_hw_common(ah), INTERRUPT, "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", ah->txok_interrupt_mask, ah->txerr_interrupt_mask, ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask, ah->txurn_interrupt_mask); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_IMR_S0, SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK) | SM(ah->txdesc_interrupt_mask, AR_IMR_S0_QCU_TXDESC)); REG_WRITE(ah, AR_IMR_S1, SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR) | SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL)); ah->imrs2_reg &= ~AR_IMR_S2_QCU_TXURN; ah->imrs2_reg |= (ah->txurn_interrupt_mask & AR_IMR_S2_QCU_TXURN); REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg); REGWRITE_BUFFER_FLUSH(ah); } u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q) { return REG_READ(ah, AR_QTXDP(q)); } EXPORT_SYMBOL(ath9k_hw_gettxbuf); void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp) { REG_WRITE(ah, AR_QTXDP(q), txdp); } EXPORT_SYMBOL(ath9k_hw_puttxbuf); void ath9k_hw_txstart(struct ath_hw *ah, u32 q) { ath_dbg(ath9k_hw_common(ah), QUEUE, "Enable TXE on queue: %u\n", q); REG_WRITE(ah, AR_Q_TXE, 1 << q); } EXPORT_SYMBOL(ath9k_hw_txstart); u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q) { u32 npend; npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT; if (npend == 0) { if (REG_READ(ah, AR_Q_TXE) & (1 << q)) npend = 1; } return npend; } EXPORT_SYMBOL(ath9k_hw_numtxpending); /** * ath9k_hw_updatetxtriglevel - adjusts the frame trigger level * * @ah: atheros hardware struct * @bIncTrigLevel: whether or not the frame trigger level should be updated * * The frame trigger level specifies the minimum number of bytes, * in units of 64 bytes, that must be DMA'ed into the PCU TX FIFO * before the PCU will initiate sending the frame on the air. This can * mean we initiate transmit before a full frame is on the PCU TX FIFO. * Resets to 0x1 (meaning 64 bytes or a full frame, whichever occurs * first) * * Caution must be taken to ensure to set the frame trigger level based * on the DMA request size. For example if the DMA request size is set to * 128 bytes the trigger level cannot exceed 6 * 64 = 384. This is because * there need to be enough space in the tx FIFO for the requested transfer * size. Hence the tx FIFO will stop with 512 - 128 = 384 bytes. If we set * the threshold to a value beyond 6, then the transmit will hang. * * Current dual stream devices have a PCU TX FIFO size of 8 KB. * Current single stream devices have a PCU TX FIFO size of 4 KB, however, * there is a hardware issue which forces us to use 2 KB instead so the * frame trigger level must not exceed 2 KB for these chipsets. */ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) { u32 txcfg, curLevel, newLevel; if (ah->tx_trig_level >= ah->config.max_txtrig_level) return false; ath9k_hw_disable_interrupts(ah); txcfg = REG_READ(ah, AR_TXCFG); curLevel = MS(txcfg, AR_FTRIG); newLevel = curLevel; if (bIncTrigLevel) { if (curLevel < ah->config.max_txtrig_level) newLevel++; } else if (curLevel > MIN_TX_FIFO_THRESHOLD) newLevel--; if (newLevel != curLevel) REG_WRITE(ah, AR_TXCFG, (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG)); ath9k_hw_enable_interrupts(ah); ah->tx_trig_level = newLevel; return newLevel != curLevel; } EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel); void ath9k_hw_abort_tx_dma(struct ath_hw *ah) { int maxdelay = 1000; int i, q; if (ah->curchan) { if (IS_CHAN_HALF_RATE(ah->curchan)) maxdelay *= 2; else if (IS_CHAN_QUARTER_RATE(ah->curchan)) maxdelay *= 4; } REG_WRITE(ah, AR_Q_TXD, AR_Q_TXD_M); REG_SET_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF); REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); REG_SET_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF); for (q = 0; q < AR_NUM_QCU; q++) { for (i = 0; i < maxdelay; i++) { if (i) udelay(5); if (!ath9k_hw_numtxpending(ah, q)) break; } } REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF); REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); REG_CLR_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF); REG_WRITE(ah, AR_Q_TXD, 0); } EXPORT_SYMBOL(ath9k_hw_abort_tx_dma); bool ath9k_hw_stop_dma_queue(struct ath_hw *ah, u32 q) { #define ATH9K_TX_STOP_DMA_TIMEOUT 1000 /* usec */ #define ATH9K_TIME_QUANTUM 100 /* usec */ int wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM; int wait; REG_WRITE(ah, AR_Q_TXD, 1 << q); for (wait = wait_time; wait != 0; wait--) { if (wait != wait_time) udelay(ATH9K_TIME_QUANTUM); if (ath9k_hw_numtxpending(ah, q) == 0) break; } REG_WRITE(ah, AR_Q_TXD, 0); return wait != 0; #undef ATH9K_TX_STOP_DMA_TIMEOUT #undef ATH9K_TIME_QUANTUM } EXPORT_SYMBOL(ath9k_hw_stop_dma_queue); bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, const struct ath9k_tx_queue_info *qinfo) { u32 cw; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info *qi; qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { ath_dbg(common, QUEUE, "Set TXQ properties, inactive queue: %u\n", q); return false; } ath_dbg(common, QUEUE, "Set queue properties for: %u\n", q); qi->tqi_ver = qinfo->tqi_ver; qi->tqi_subtype = qinfo->tqi_subtype; qi->tqi_qflags = qinfo->tqi_qflags; qi->tqi_priority = qinfo->tqi_priority; if (qinfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT) qi->tqi_aifs = min(qinfo->tqi_aifs, 255U); else qi->tqi_aifs = INIT_AIFS; if (qinfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) { cw = min(qinfo->tqi_cwmin, 1024U); qi->tqi_cwmin = 1; while (qi->tqi_cwmin < cw) qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; } else qi->tqi_cwmin = qinfo->tqi_cwmin; if (qinfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) { cw = min(qinfo->tqi_cwmax, 1024U); qi->tqi_cwmax = 1; while (qi->tqi_cwmax < cw) qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; } else qi->tqi_cwmax = INIT_CWMAX; if (qinfo->tqi_shretry != 0) qi->tqi_shretry = min((u32) qinfo->tqi_shretry, 15U); else qi->tqi_shretry = INIT_SH_RETRY; if (qinfo->tqi_lgretry != 0) qi->tqi_lgretry = min((u32) qinfo->tqi_lgretry, 15U); else qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_cbrPeriod = qinfo->tqi_cbrPeriod; qi->tqi_cbrOverflowLimit = qinfo->tqi_cbrOverflowLimit; qi->tqi_burstTime = qinfo->tqi_burstTime; qi->tqi_readyTime = qinfo->tqi_readyTime; switch (qinfo->tqi_subtype) { case ATH9K_WME_UPSD: if (qi->tqi_type == ATH9K_TX_QUEUE_DATA) qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS; break; default: break; } return true; } EXPORT_SYMBOL(ath9k_hw_set_txq_props); bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, struct ath9k_tx_queue_info *qinfo) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info *qi; qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { ath_dbg(common, QUEUE, "Get TXQ properties, inactive queue: %u\n", q); return false; } qinfo->tqi_qflags = qi->tqi_qflags; qinfo->tqi_ver = qi->tqi_ver; qinfo->tqi_subtype = qi->tqi_subtype; qinfo->tqi_qflags = qi->tqi_qflags; qinfo->tqi_priority = qi->tqi_priority; qinfo->tqi_aifs = qi->tqi_aifs; qinfo->tqi_cwmin = qi->tqi_cwmin; qinfo->tqi_cwmax = qi->tqi_cwmax; qinfo->tqi_shretry = qi->tqi_shretry; qinfo->tqi_lgretry = qi->tqi_lgretry; qinfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; qinfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; qinfo->tqi_burstTime = qi->tqi_burstTime; qinfo->tqi_readyTime = qi->tqi_readyTime; return true; } EXPORT_SYMBOL(ath9k_hw_get_txq_props); int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, const struct ath9k_tx_queue_info *qinfo) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info *qi; int q; switch (type) { case ATH9K_TX_QUEUE_BEACON: q = ATH9K_NUM_TX_QUEUES - 1; break; case ATH9K_TX_QUEUE_CAB: q = ATH9K_NUM_TX_QUEUES - 2; break; case ATH9K_TX_QUEUE_PSPOLL: q = 1; break; case ATH9K_TX_QUEUE_UAPSD: q = ATH9K_NUM_TX_QUEUES - 3; break; case ATH9K_TX_QUEUE_DATA: for (q = 0; q < ATH9K_NUM_TX_QUEUES; q++) if (ah->txq[q].tqi_type == ATH9K_TX_QUEUE_INACTIVE) break; if (q == ATH9K_NUM_TX_QUEUES) { ath_err(common, "No available TX queue\n"); return -1; } break; default: ath_err(common, "Invalid TX queue type: %u\n", type); return -1; } ath_dbg(common, QUEUE, "Setup TX queue: %u\n", q); qi = &ah->txq[q]; if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { ath_err(common, "TX queue: %u already active\n", q); return -1; } memset(qi, 0, sizeof(struct ath9k_tx_queue_info)); qi->tqi_type = type; qi->tqi_physCompBuf = qinfo->tqi_physCompBuf; (void) ath9k_hw_set_txq_props(ah, q, qinfo); return q; } EXPORT_SYMBOL(ath9k_hw_setuptxqueue); static void ath9k_hw_clear_queue_interrupts(struct ath_hw *ah, u32 q) { ah->txok_interrupt_mask &= ~(1 << q); ah->txerr_interrupt_mask &= ~(1 << q); ah->txdesc_interrupt_mask &= ~(1 << q); ah->txeol_interrupt_mask &= ~(1 << q); ah->txurn_interrupt_mask &= ~(1 << q); } bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info *qi; qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { ath_dbg(common, QUEUE, "Release TXQ, inactive queue: %u\n", q); return false; } ath_dbg(common, QUEUE, "Release TX queue: %u\n", q); qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE; ath9k_hw_clear_queue_interrupts(ah, q); ath9k_hw_set_txq_interrupts(ah, qi); return true; } EXPORT_SYMBOL(ath9k_hw_releasetxqueue); bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ath9k_tx_queue_info *qi; u32 cwMin, chanCwMin, value; qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { ath_dbg(common, QUEUE, "Reset TXQ, inactive queue: %u\n", q); return true; } ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q); if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { if (chan && IS_CHAN_B(chan)) chanCwMin = INIT_CWMIN_11B; else chanCwMin = INIT_CWMIN; for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1); } else cwMin = qi->tqi_cwmin; ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_DLCL_IFS(q), SM(cwMin, AR_D_LCL_IFS_CWMIN) | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); REG_WRITE(ah, AR_DRETRY_LIMIT(q), SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH)); REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ); if (AR_SREV_9340(ah)) REG_WRITE(ah, AR_DMISC(q), AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x1); else REG_WRITE(ah, AR_DMISC(q), AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2); if (qi->tqi_cbrPeriod) { REG_WRITE(ah, AR_QCBRCFG(q), SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) | SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_OVF_THRESH)); REG_SET_BIT(ah, AR_QMISC(q), AR_Q_MISC_FSP_CBR | (qi->tqi_cbrOverflowLimit ? AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN : 0)); } if (qi->tqi_readyTime && (qi->tqi_type != ATH9K_TX_QUEUE_CAB)) { REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) | AR_Q_RDYTIMECFG_EN); } REG_WRITE(ah, AR_DCHNTIME(q), SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0)); if (qi->tqi_burstTime && (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)) REG_SET_BIT(ah, AR_QMISC(q), AR_Q_MISC_RDYTIME_EXP_POLICY); if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) REG_SET_BIT(ah, AR_DMISC(q), AR_D_MISC_POST_FR_BKOFF_DIS); REGWRITE_BUFFER_FLUSH(ah); if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) REG_SET_BIT(ah, AR_DMISC(q), AR_D_MISC_FRAG_BKOFF_EN); switch (qi->tqi_type) { case ATH9K_TX_QUEUE_BEACON: ENABLE_REGWRITE_BUFFER(ah); REG_SET_BIT(ah, AR_QMISC(q), AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_BEACON_USE | AR_Q_MISC_CBR_INCR_DIS1); REG_SET_BIT(ah, AR_DMISC(q), (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << AR_D_MISC_ARB_LOCKOUT_CNTRL_S) | AR_D_MISC_BEACON_USE | AR_D_MISC_POST_FR_BKOFF_DIS); REGWRITE_BUFFER_FLUSH(ah); /* * cwmin and cwmax should be 0 for beacon queue * but not for IBSS as we would create an imbalance * on beaconing fairness for participating nodes. */ if (AR_SREV_9300_20_OR_LATER(ah) && ah->opmode != NL80211_IFTYPE_ADHOC) { REG_WRITE(ah, AR_DLCL_IFS(q), SM(0, AR_D_LCL_IFS_CWMIN) | SM(0, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); } break; case ATH9K_TX_QUEUE_CAB: ENABLE_REGWRITE_BUFFER(ah); REG_SET_BIT(ah, AR_QMISC(q), AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0); value = (qi->tqi_readyTime - (ah->config.sw_beacon_response_time - ah->config.dma_beacon_response_time) - ah->config.additional_swba_backoff) * 1024; REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_EN); REG_SET_BIT(ah, AR_DMISC(q), (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << AR_D_MISC_ARB_LOCKOUT_CNTRL_S)); REGWRITE_BUFFER_FLUSH(ah); break; case ATH9K_TX_QUEUE_PSPOLL: REG_SET_BIT(ah, AR_QMISC(q), AR_Q_MISC_CBR_INCR_DIS1); break; case ATH9K_TX_QUEUE_UAPSD: REG_SET_BIT(ah, AR_DMISC(q), AR_D_MISC_POST_FR_BKOFF_DIS); break; default: break; } if (qi->tqi_intFlags & ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS) { REG_SET_BIT(ah, AR_DMISC(q), SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL) | AR_D_MISC_POST_FR_BKOFF_DIS); } if (AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_Q_DESC_CRCCHK, AR_Q_DESC_CRCCHK_EN); ath9k_hw_clear_queue_interrupts(ah, q); if (qi->tqi_qflags & TXQ_FLAG_TXINT_ENABLE) { ah->txok_interrupt_mask |= 1 << q; ah->txerr_interrupt_mask |= 1 << q; } if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE) ah->txdesc_interrupt_mask |= 1 << q; if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE) ah->txeol_interrupt_mask |= 1 << q; if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE) ah->txurn_interrupt_mask |= 1 << q; ath9k_hw_set_txq_interrupts(ah, qi); return true; } EXPORT_SYMBOL(ath9k_hw_resettxqueue); int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, struct ath_rx_status *rs) { struct ar5416_desc ads; struct ar5416_desc *adsp = AR5416DESC(ds); u32 phyerr; if ((adsp->ds_rxstatus8 & AR_RxDone) == 0) return -EINPROGRESS; ads.u.rx = adsp->u.rx; rs->rs_status = 0; rs->rs_flags = 0; rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen; rs->rs_tstamp = ads.AR_RcvTimestamp; if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) { rs->rs_rssi = ATH9K_RSSI_BAD; rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD; rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD; rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD; rs->rs_rssi_ext0 = ATH9K_RSSI_BAD; rs->rs_rssi_ext1 = ATH9K_RSSI_BAD; rs->rs_rssi_ext2 = ATH9K_RSSI_BAD; } else { rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00); rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01); rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02); rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10); rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11); rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12); } if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) rs->rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx); else rs->rs_keyix = ATH9K_RXKEYIX_INVALID; rs->rs_rate = MS(ads.ds_rxstatus0, AR_RxRate); rs->rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0; rs->rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0; rs->rs_moreaggr = (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); rs->rs_flags = (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0; rs->rs_flags |= (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0; if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE; if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) rs->rs_flags |= ATH9K_RX_DELIM_CRC_POST; if (ads.ds_rxstatus8 & AR_DecryptBusyErr) rs->rs_flags |= ATH9K_RX_DECRYPT_BUSY; if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) { /* * Treat these errors as mutually exclusive to avoid spurious * extra error reports from the hardware. If a CRC error is * reported, then decryption and MIC errors are irrelevant, * the frame is going to be dropped either way */ if (ads.ds_rxstatus8 & AR_CRCErr) rs->rs_status |= ATH9K_RXERR_CRC; else if (ads.ds_rxstatus8 & AR_PHYErr) { rs->rs_status |= ATH9K_RXERR_PHY; phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode); rs->rs_phyerr = phyerr; } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) rs->rs_status |= ATH9K_RXERR_DECRYPT; else if (ads.ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= ATH9K_RXERR_MIC; } if (ads.ds_rxstatus8 & AR_KeyMiss) rs->rs_status |= ATH9K_RXERR_KEYMISS; return 0; } EXPORT_SYMBOL(ath9k_hw_rxprocdesc); /* * This can stop or re-enables RX. * * If bool is set this will kill any frame which is currently being * transferred between the MAC and baseband and also prevent any new * frames from getting started. */ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set) { u32 reg; if (set) { REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); if (!ath9k_hw_wait(ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE, 0, AH_WAIT_TIMEOUT)) { REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); reg = REG_READ(ah, AR_OBS_BUS_1); ath_err(ath9k_hw_common(ah), "RX failed to go idle in 10 ms RXSM=0x%x\n", reg); return false; } } else { REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); } return true; } EXPORT_SYMBOL(ath9k_hw_setrxabort); void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp) { REG_WRITE(ah, AR_RXDP, rxdp); } EXPORT_SYMBOL(ath9k_hw_putrxbuf); void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning) { ath9k_enable_mib_counters(ah); ath9k_ani_reset(ah, is_scanning); REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); } EXPORT_SYMBOL(ath9k_hw_startpcureceive); void ath9k_hw_abortpcurecv(struct ath_hw *ah) { REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS); ath9k_hw_disable_mib_counters(ah); } EXPORT_SYMBOL(ath9k_hw_abortpcurecv); bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset) { #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ struct ath_common *common = ath9k_hw_common(ah); u32 mac_status, last_mac_status = 0; int i; /* Enable access to the DMA observation bus */ REG_WRITE(ah, AR_MACMISC, ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Wait for rx enable bit to go low */ for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) break; if (!AR_SREV_9300_20_OR_LATER(ah)) { mac_status = REG_READ(ah, AR_DMADBG_7) & 0x7f0; if (mac_status == 0x1c0 && mac_status == last_mac_status) { *reset = true; break; } last_mac_status = mac_status; } udelay(AH_TIME_QUANTUM); } if (i == 0) { ath_err(common, "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x DMADBG_7=0x%08x\n", AH_RX_STOP_DMA_TIMEOUT / 1000, REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW), REG_READ(ah, AR_DMADBG_7)); return false; } else { return true; } #undef AH_RX_STOP_DMA_TIMEOUT } EXPORT_SYMBOL(ath9k_hw_stopdmarecv); int ath9k_hw_beaconq_setup(struct ath_hw *ah) { struct ath9k_tx_queue_info qi; memset(&qi, 0, sizeof(qi)); qi.tqi_aifs = 1; qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) qi.tqi_qflags = TXQ_FLAG_TXINT_ENABLE; return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); } EXPORT_SYMBOL(ath9k_hw_beaconq_setup); bool ath9k_hw_intrpend(struct ath_hw *ah) { u32 host_isr; if (AR_SREV_9100(ah)) return true; host_isr = REG_READ(ah, AR_INTR_ASYNC_CAUSE); if (((host_isr & AR_INTR_MAC_IRQ) || (host_isr & AR_INTR_ASYNC_MASK_MCI)) && (host_isr != AR_INTR_SPURIOUS)) return true; host_isr = REG_READ(ah, AR_INTR_SYNC_CAUSE); if ((host_isr & AR_INTR_SYNC_DEFAULT) && (host_isr != AR_INTR_SPURIOUS)) return true; return false; } EXPORT_SYMBOL(ath9k_hw_intrpend); void ath9k_hw_kill_interrupts(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); ath_dbg(common, INTERRUPT, "disable IER\n"); REG_WRITE(ah, AR_IER, AR_IER_DISABLE); (void) REG_READ(ah, AR_IER); if (!AR_SREV_9100(ah)) { REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0); (void) REG_READ(ah, AR_INTR_ASYNC_ENABLE); REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); (void) REG_READ(ah, AR_INTR_SYNC_ENABLE); } } EXPORT_SYMBOL(ath9k_hw_kill_interrupts); void ath9k_hw_disable_interrupts(struct ath_hw *ah) { if (!(ah->imask & ATH9K_INT_GLOBAL)) atomic_set(&ah->intr_ref_cnt, -1); else atomic_dec(&ah->intr_ref_cnt); ath9k_hw_kill_interrupts(ah); } EXPORT_SYMBOL(ath9k_hw_disable_interrupts); void ath9k_hw_enable_interrupts(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 sync_default = AR_INTR_SYNC_DEFAULT; u32 async_mask; if (!(ah->imask & ATH9K_INT_GLOBAL)) return; if (!atomic_inc_and_test(&ah->intr_ref_cnt)) { ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", atomic_read(&ah->intr_ref_cnt)); return; } if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; async_mask = AR_INTR_MAC_IRQ; if (ah->imask & ATH9K_INT_MCI) async_mask |= AR_INTR_ASYNC_MASK_MCI; ath_dbg(common, INTERRUPT, "enable IER\n"); REG_WRITE(ah, AR_IER, AR_IER_ENABLE); if (!AR_SREV_9100(ah)) { REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, async_mask); REG_WRITE(ah, AR_INTR_ASYNC_MASK, async_mask); REG_WRITE(ah, AR_INTR_SYNC_ENABLE, sync_default); REG_WRITE(ah, AR_INTR_SYNC_MASK, sync_default); } ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); } EXPORT_SYMBOL(ath9k_hw_enable_interrupts); void ath9k_hw_set_interrupts(struct ath_hw *ah) { enum ath9k_int ints = ah->imask; u32 mask, mask2; struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_common *common = ath9k_hw_common(ah); if (!(ints & ATH9K_INT_GLOBAL)) ath9k_hw_disable_interrupts(ah); ath_dbg(common, INTERRUPT, "New interrupt mask 0x%x\n", ints); mask = ints & ATH9K_INT_COMMON; mask2 = 0; if (ints & ATH9K_INT_TX) { if (ah->config.tx_intr_mitigation) mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM; else { if (ah->txok_interrupt_mask) mask |= AR_IMR_TXOK; if (ah->txdesc_interrupt_mask) mask |= AR_IMR_TXDESC; } if (ah->txerr_interrupt_mask) mask |= AR_IMR_TXERR; if (ah->txeol_interrupt_mask) mask |= AR_IMR_TXEOL; } if (ints & ATH9K_INT_RX) { if (AR_SREV_9300_20_OR_LATER(ah)) { mask |= AR_IMR_RXERR | AR_IMR_RXOK_HP; if (ah->config.rx_intr_mitigation) { mask &= ~AR_IMR_RXOK_LP; mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; } else { mask |= AR_IMR_RXOK_LP; } } else { if (ah->config.rx_intr_mitigation) mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; else mask |= AR_IMR_RXOK | AR_IMR_RXDESC; } if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) mask |= AR_IMR_GENTMR; } if (ints & ATH9K_INT_GENTIMER) mask |= AR_IMR_GENTMR; if (ints & (ATH9K_INT_BMISC)) { mask |= AR_IMR_BCNMISC; if (ints & ATH9K_INT_TIM) mask2 |= AR_IMR_S2_TIM; if (ints & ATH9K_INT_DTIM) mask2 |= AR_IMR_S2_DTIM; if (ints & ATH9K_INT_DTIMSYNC) mask2 |= AR_IMR_S2_DTIMSYNC; if (ints & ATH9K_INT_CABEND) mask2 |= AR_IMR_S2_CABEND; if (ints & ATH9K_INT_TSFOOR) mask2 |= AR_IMR_S2_TSFOOR; } if (ints & (ATH9K_INT_GTT | ATH9K_INT_CST)) { mask |= AR_IMR_BCNMISC; if (ints & ATH9K_INT_GTT) mask2 |= AR_IMR_S2_GTT; if (ints & ATH9K_INT_CST) mask2 |= AR_IMR_S2_CST; } ath_dbg(common, INTERRUPT, "new IMR 0x%x\n", mask); REG_WRITE(ah, AR_IMR, mask); ah->imrs2_reg &= ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC | AR_IMR_S2_CABEND | AR_IMR_S2_CABTO | AR_IMR_S2_TSFOOR | AR_IMR_S2_GTT | AR_IMR_S2_CST); ah->imrs2_reg |= mask2; REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg); if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if (ints & ATH9K_INT_TIM_TIMER) REG_SET_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); else REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); } return; } EXPORT_SYMBOL(ath9k_hw_set_interrupts); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/link.c0000644000175000017500000003440712026211315023366 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "ath9k.h" /* * TX polling - checks if the TX engine is stuck somewhere * and issues a chip reset if so. */ void ath_tx_complete_poll_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, tx_complete_work.work); struct ath_txq *txq; int i; bool needreset = false; #ifdef CONFIG_ATH9K_DEBUGFS sc->tx_complete_poll_work_seen++; #endif for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) { txq = &sc->tx.txq[i]; ath_txq_lock(sc, txq); if (txq->axq_depth) { if (txq->axq_tx_inprogress) { needreset = true; ath_txq_unlock(sc, txq); break; } else { txq->axq_tx_inprogress = true; } } ath_txq_unlock_complete(sc, txq); } if (needreset) { ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, "tx hung, resetting the chip\n"); ath9k_queue_reset(sc, RESET_TYPE_TX_HANG); return; } ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); } /* * Checks if the BB/MAC is hung. */ void ath_hw_check(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; int busy; u8 is_alive, nbeacon = 1; enum ath_reset_type type; ath9k_ps_wakeup(sc); is_alive = ath9k_hw_check_alive(sc->sc_ah); if (is_alive && !AR_SREV_9300(sc->sc_ah)) goto out; else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { ath_dbg(common, RESET, "DCU stuck is detected. Schedule chip reset\n"); type = RESET_TYPE_MAC_HANG; goto sched_reset; } spin_lock_irqsave(&common->cc_lock, flags); busy = ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", busy, sc->hw_busy_count + 1); if (busy >= 99) { if (++sc->hw_busy_count >= 3) { type = RESET_TYPE_BB_HANG; goto sched_reset; } } else if (busy >= 0) { sc->hw_busy_count = 0; nbeacon = 3; } ath_start_rx_poll(sc, nbeacon); goto out; sched_reset: ath9k_queue_reset(sc, type); out: ath9k_ps_restore(sc); } /* * PLL-WAR for AR9485/AR9340 */ static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) { static int count; struct ath_common *common = ath9k_hw_common(sc->sc_ah); if (pll_sqsum >= 0x40000) { count++; if (count == 3) { ath_dbg(common, RESET, "PLL WAR, resetting the chip\n"); ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG); count = 0; return true; } } else { count = 0; } return false; } void ath_hw_pll_work(struct work_struct *work) { u32 pll_sqsum; struct ath_softc *sc = container_of(work, struct ath_softc, hw_pll_work.work); /* * ensure that the PLL WAR is executed only * after the STA is associated (or) if the * beaconing had started in interfaces that * uses beacons. */ if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) return; ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ath9k_ps_restore(sc); if (ath_hw_pll_rx_hang_check(sc, pll_sqsum)) return; ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); } /* * RX Polling - monitors baseband hangs. */ void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) { if (!AR_SREV_9300(sc->sc_ah)) return; if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) return; mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies (nbeacon * sc->cur_beacon_conf.beacon_interval)); } void ath_rx_poll(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; ieee80211_queue_work(sc->hw, &sc->hw_check_work); } /* * PA Pre-distortion. */ static void ath_paprd_activate(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath9k_hw_cal_data *caldata = ah->caldata; int chain; if (!caldata || !caldata->paprd_done) return; ath9k_ps_wakeup(sc); ar9003_paprd_enable(ah, false); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(ah->txchainmask & BIT(chain))) continue; ar9003_paprd_populate_single_table(ah, caldata, chain); } ar9003_paprd_enable(ah, true); ath9k_ps_restore(sc); } static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) { struct ieee80211_hw *hw = sc->hw; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_tx_control txctl; int time_left; memset(&txctl, 0, sizeof(txctl)); txctl.txq = sc->tx.txq_map[WME_AC_BE]; memset(tx_info, 0, sizeof(*tx_info)); tx_info->band = hw->conf.channel->band; tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; tx_info->control.rates[0].idx = 0; tx_info->control.rates[0].count = 1; tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS; tx_info->control.rates[1].idx = -1; init_completion(&sc->paprd_complete); txctl.paprd = BIT(chain); if (ath_tx_start(hw, skb, &txctl) != 0) { ath_dbg(common, CALIBRATE, "PAPRD TX failed\n"); dev_kfree_skb_any(skb); return false; } time_left = wait_for_completion_timeout(&sc->paprd_complete, msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); if (!time_left) ath_dbg(common, CALIBRATE, "Timeout waiting for paprd training on TX chain %d\n", chain); return !!time_left; } void ath_paprd_calibrate(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); struct ieee80211_hw *hw = sc->hw; struct ath_hw *ah = sc->sc_ah; struct ieee80211_hdr *hdr; struct sk_buff *skb = NULL; struct ath9k_hw_cal_data *caldata = ah->caldata; struct ath_common *common = ath9k_hw_common(ah); int ftype; int chain_ok = 0; int chain; int len = 1800; int ret; if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done) return; ath9k_ps_wakeup(sc); if (ar9003_paprd_init_table(ah) < 0) goto fail_paprd; skb = alloc_skb(len, GFP_KERNEL); if (!skb) goto fail_paprd; skb_put(skb, len); memset(skb->data, 0, len); hdr = (struct ieee80211_hdr *)skb->data; ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; hdr->frame_control = cpu_to_le16(ftype); hdr->duration_id = cpu_to_le16(10); memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(ah->txchainmask & BIT(chain))) continue; chain_ok = 0; ar9003_paprd_setup_gain_table(ah, chain); ath_dbg(common, CALIBRATE, "Sending PAPRD training frame on chain %d\n", chain); if (!ath_paprd_send_frame(sc, skb, chain)) goto fail_paprd; if (!ar9003_paprd_is_done(ah)) { ath_dbg(common, CALIBRATE, "PAPRD not yet done on chain %d\n", chain); break; } ret = ar9003_paprd_create_curve(ah, caldata, chain); if (ret == -EINPROGRESS) { ath_dbg(common, CALIBRATE, "PAPRD curve on chain %d needs to be re-trained\n", chain); break; } else if (ret) { ath_dbg(common, CALIBRATE, "PAPRD create curve failed on chain %d\n", chain); break; } chain_ok = 1; } kfree_skb(skb); if (chain_ok) { caldata->paprd_done = true; ath_paprd_activate(sc); } fail_paprd: ath9k_ps_restore(sc); } /* * ANI performs periodic noise floor calibration * that is used to adjust and optimize the chip performance. This * takes environmental changes (location, temperature) into account. * When the task is complete, it reschedules itself depending on the * appropriate interval that was calculated. */ void ath_ani_calibrate(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); bool longcal = false; bool shortcal = false; bool aniflag = false; unsigned int timestamp = jiffies_to_msecs(jiffies); u32 cal_interval, short_cal_interval, long_cal_interval; unsigned long flags; if (ah->caldata && ah->caldata->nfcal_interference) long_cal_interval = ATH_LONG_CALINTERVAL_INT; else long_cal_interval = ATH_LONG_CALINTERVAL; short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; /* Only calibrate if awake */ if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) goto set_timer; ath9k_ps_wakeup(sc); /* Long calibration runs independently of short calibration. */ if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { longcal = true; common->ani.longcal_timer = timestamp; } /* Short calibration applies only while caldone is false */ if (!common->ani.caldone) { if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; common->ani.shortcal_timer = timestamp; common->ani.resetcal_timer = timestamp; } } else { if ((timestamp - common->ani.resetcal_timer) >= ATH_RESTART_CALINTERVAL) { common->ani.caldone = ath9k_hw_reset_calvalid(ah); if (common->ani.caldone) common->ani.resetcal_timer = timestamp; } } /* Verify whether we must check ANI */ if (sc->sc_ah->config.enable_ani && (timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) { aniflag = true; common->ani.checkani_timer = timestamp; } /* Call ANI routine if necessary */ if (aniflag) { spin_lock_irqsave(&common->cc_lock, flags); ath9k_hw_ani_monitor(ah, ah->curchan); ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); } /* Perform calibration if necessary */ if (longcal || shortcal) { common->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask, longcal); } ath_dbg(common, ANI, "Calibration @%lu finished: %s %s %s, caldone: %s\n", jiffies, longcal ? "long" : "", shortcal ? "short" : "", aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); ath9k_debug_samp_bb_mac(sc); ath9k_ps_restore(sc); set_timer: /* * Set timer interval based on previous results. * The interval must be the shortest necessary to satisfy ANI, * short calibration and long calibration. */ cal_interval = ATH_LONG_CALINTERVAL; if (sc->sc_ah->config.enable_ani) cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval); if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD) && ah->caldata) { if (!ah->caldata->paprd_done) ieee80211_queue_work(sc->hw, &sc->paprd_work); else if (!ah->paprd_table_write_done) ath_paprd_activate(sc); } } void ath_start_ani(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long timestamp = jiffies_to_msecs(jiffies); if (common->disable_ani || !test_bit(SC_OP_ANI_RUN, &sc->sc_flags) || (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) return; common->ani.longcal_timer = timestamp; common->ani.shortcal_timer = timestamp; common->ani.checkani_timer = timestamp; ath_dbg(common, ANI, "Starting ANI\n"); mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval)); } void ath_stop_ani(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); ath_dbg(common, ANI, "Stopping ANI\n"); del_timer_sync(&common->ani.timer); } void ath_check_ani(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; /* * Check for the various conditions in which ANI has to * be stopped. */ if (ah->opmode == NL80211_IFTYPE_ADHOC) { if (!cur_conf->enable_beacon) goto stop_ani; } else if (ah->opmode == NL80211_IFTYPE_AP) { if (!cur_conf->enable_beacon) { /* * Disable ANI only when there are no * associated stations. */ if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) goto stop_ani; } } else if (ah->opmode == NL80211_IFTYPE_STATION) { if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) goto stop_ani; } if (!test_bit(SC_OP_ANI_RUN, &sc->sc_flags)) { set_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_start_ani(sc); } return; stop_ani: clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_stop_ani(sc); } void ath_update_survey_nf(struct ath_softc *sc, int channel) { struct ath_hw *ah = sc->sc_ah; struct ath9k_channel *chan = &ah->channels[channel]; struct survey_info *survey = &sc->survey[channel]; if (chan->noisefloor) { survey->filled |= SURVEY_INFO_NOISE_DBM; survey->noise = ath9k_hw_getchan_noise(ah, chan); } } /* * Updates the survey statistics and returns the busy time since last * update in %, if the measurement duration was long enough for the * result to be useful, -1 otherwise. */ int ath_update_survey_stats(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); int pos = ah->curchan - &ah->channels[0]; struct survey_info *survey = &sc->survey[pos]; struct ath_cycle_counters *cc = &common->cc_survey; unsigned int div = common->clockrate * 1000; int ret = 0; if (!ah->curchan) return -1; if (ah->power_mode == ATH9K_PM_AWAKE) ath_hw_cycle_counters_update(common); if (cc->cycles > 0) { survey->filled |= SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_RX | SURVEY_INFO_CHANNEL_TIME_TX; survey->channel_time += cc->cycles / div; survey->channel_time_busy += cc->rx_busy / div; survey->channel_time_rx += cc->rx_frame / div; survey->channel_time_tx += cc->tx_frame / div; } if (cc->cycles < div) return -1; if (cc->cycles > 0) ret = cc->rx_busy * 100 / cc->cycles; memset(cc, 0, sizeof(*cc)); ath_update_survey_nf(sc, pos); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/hw-ops.h0000644000175000017500000001342112026211315023644 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ATH9K_HW_OPS_H #define ATH9K_HW_OPS_H #include "hw.h" /* Hardware core and driver accessible callbacks */ static inline void ath9k_hw_configpcipowersave(struct ath_hw *ah, bool power_off) { if (!ah->aspm_enabled) return; ath9k_hw_ops(ah)->config_pci_powersave(ah, power_off); } static inline void ath9k_hw_rxena(struct ath_hw *ah) { ath9k_hw_ops(ah)->rx_enable(ah); } static inline void ath9k_hw_set_desc_link(struct ath_hw *ah, void *ds, u32 link) { ath9k_hw_ops(ah)->set_desc_link(ds, link); } static inline bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, u8 rxchainmask, bool longcal) { return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal); } static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) { return ath9k_hw_ops(ah)->get_isr(ah, masked); } static inline void ath9k_hw_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) { return ath9k_hw_ops(ah)->set_txdesc(ah, ds, i); } static inline int ath9k_hw_txprocdesc(struct ath_hw *ah, void *ds, struct ath_tx_status *ts) { return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts); } static inline void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf) { ath9k_hw_ops(ah)->antdiv_comb_conf_get(ah, antconf); } static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf) { ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); } /* Private hardware call ops */ /* PHY ops */ static inline int ath9k_hw_rf_set_freq(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->rf_set_freq(ah, chan); } static inline void ath9k_hw_spur_mitigate_freq(struct ath_hw *ah, struct ath9k_channel *chan) { ath9k_hw_private_ops(ah)->spur_mitigate_freq(ah, chan); } static inline int ath9k_hw_rf_alloc_ext_banks(struct ath_hw *ah) { if (!ath9k_hw_private_ops(ah)->rf_alloc_ext_banks) return 0; return ath9k_hw_private_ops(ah)->rf_alloc_ext_banks(ah); } static inline void ath9k_hw_rf_free_ext_banks(struct ath_hw *ah) { if (!ath9k_hw_private_ops(ah)->rf_free_ext_banks) return; ath9k_hw_private_ops(ah)->rf_free_ext_banks(ah); } static inline bool ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, u16 modesIndex) { if (!ath9k_hw_private_ops(ah)->set_rf_regs) return true; return ath9k_hw_private_ops(ah)->set_rf_regs(ah, chan, modesIndex); } static inline void ath9k_hw_init_bb(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->init_bb(ah, chan); } static inline void ath9k_hw_set_channel_regs(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->set_channel_regs(ah, chan); } static inline int ath9k_hw_process_ini(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->process_ini(ah, chan); } static inline void ath9k_olc_init(struct ath_hw *ah) { if (!ath9k_hw_private_ops(ah)->olc_init) return; return ath9k_hw_private_ops(ah)->olc_init(ah); } static inline void ath9k_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->set_rfmode(ah, chan); } static inline void ath9k_hw_mark_phy_inactive(struct ath_hw *ah) { return ath9k_hw_private_ops(ah)->mark_phy_inactive(ah); } static inline void ath9k_hw_set_delta_slope(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->set_delta_slope(ah, chan); } static inline bool ath9k_hw_rfbus_req(struct ath_hw *ah) { return ath9k_hw_private_ops(ah)->rfbus_req(ah); } static inline void ath9k_hw_rfbus_done(struct ath_hw *ah) { return ath9k_hw_private_ops(ah)->rfbus_done(ah); } static inline void ath9k_hw_restore_chainmask(struct ath_hw *ah) { if (!ath9k_hw_private_ops(ah)->restore_chainmask) return; return ath9k_hw_private_ops(ah)->restore_chainmask(ah); } static inline bool ath9k_hw_ani_control(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { return ath9k_hw_private_ops(ah)->ani_control(ah, cmd, param); } static inline void ath9k_hw_do_getnf(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]) { ath9k_hw_private_ops(ah)->do_getnf(ah, nfarray); } static inline bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->init_cal(ah, chan); } static inline void ath9k_hw_setup_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal) { ath9k_hw_private_ops(ah)->setup_calibration(ah, currCal); } static inline int ath9k_hw_fast_chan_change(struct ath_hw *ah, struct ath9k_channel *chan, u8 *ini_reloaded) { return ath9k_hw_private_ops(ah)->fast_chan_change(ah, chan, ini_reloaded); } static inline void ath9k_hw_set_radar_params(struct ath_hw *ah) { if (!ath9k_hw_private_ops(ah)->set_radar_params) return; ath9k_hw_private_ops(ah)->set_radar_params(ah, &ah->radar_conf); } #endif /* ATH9K_HW_OPS_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/hw.h0000644000175000017500000010222412026211315023045 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HW_H #define HW_H #include #include #include #include "mac.h" #include "ani.h" #include "eeprom.h" #include "calib.h" #include "reg.h" #include "phy.h" #include "btcoex.h" #include "../regd.h" #define ATHEROS_VENDOR_ID 0x168c #define AR5416_DEVID_PCI 0x0023 #define AR5416_DEVID_PCIE 0x0024 #define AR9160_DEVID_PCI 0x0027 #define AR9280_DEVID_PCI 0x0029 #define AR9280_DEVID_PCIE 0x002a #define AR9285_DEVID_PCIE 0x002b #define AR2427_DEVID_PCIE 0x002c #define AR9287_DEVID_PCI 0x002d #define AR9287_DEVID_PCIE 0x002e #define AR9300_DEVID_PCIE 0x0030 #define AR9300_DEVID_AR9340 0x0031 #define AR9300_DEVID_AR9485_PCIE 0x0032 #define AR9300_DEVID_AR9580 0x0033 #define AR9300_DEVID_AR9462 0x0034 #define AR9300_DEVID_AR9330 0x0035 #define AR9300_DEVID_QCA955X 0x0038 #define AR9485_DEVID_AR1111 0x0037 #define AR9300_DEVID_AR9565 0x0036 #define AR5416_AR9100_DEVID 0x000b #define AR_SUBVENDOR_ID_NOG 0x0e11 #define AR_SUBVENDOR_ID_NEW_A 0x7065 #define AR5416_MAGIC 0x19641014 #define AR9280_COEX2WIRE_SUBSYSID 0x309b #define AT9285_COEX3WIRE_SA_SUBSYSID 0x30aa #define AT9285_COEX3WIRE_DA_SUBSYSID 0x30ab #define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1) #define ATH_DEFAULT_NOISE_FLOOR -95 #define ATH9K_RSSI_BAD -128 #define ATH9K_NUM_CHANNELS 38 /* Register read/write primitives */ #define REG_WRITE(_ah, _reg, _val) \ (_ah)->reg_ops.write((_ah), (_val), (_reg)) #define REG_READ(_ah, _reg) \ (_ah)->reg_ops.read((_ah), (_reg)) #define REG_READ_MULTI(_ah, _addr, _val, _cnt) \ (_ah)->reg_ops.multi_read((_ah), (_addr), (_val), (_cnt)) #define REG_RMW(_ah, _reg, _set, _clr) \ (_ah)->reg_ops.rmw((_ah), (_reg), (_set), (_clr)) #define ENABLE_REGWRITE_BUFFER(_ah) \ do { \ if ((_ah)->reg_ops.enable_write_buffer) \ (_ah)->reg_ops.enable_write_buffer((_ah)); \ } while (0) #define REGWRITE_BUFFER_FLUSH(_ah) \ do { \ if ((_ah)->reg_ops.write_flush) \ (_ah)->reg_ops.write_flush((_ah)); \ } while (0) #define PR_EEP(_s, _val) \ do { \ len += snprintf(buf + len, size - len, "%20s : %10d\n", \ _s, (_val)); \ } while (0) #define SM(_v, _f) (((_v) << _f##_S) & _f) #define MS(_v, _f) (((_v) & _f) >> _f##_S) #define REG_RMW_FIELD(_a, _r, _f, _v) \ REG_RMW(_a, _r, (((_v) << _f##_S) & _f), (_f)) #define REG_READ_FIELD(_a, _r, _f) \ (((REG_READ(_a, _r) & _f) >> _f##_S)) #define REG_SET_BIT(_a, _r, _f) \ REG_RMW(_a, _r, (_f), 0) #define REG_CLR_BIT(_a, _r, _f) \ REG_RMW(_a, _r, 0, (_f)) #define DO_DELAY(x) do { \ if (((++(x) % 64) == 0) && \ (ath9k_hw_common(ah)->bus_ops->ath_bus_type \ != ATH_USB)) \ udelay(1); \ } while (0) #define REG_WRITE_ARRAY(iniarray, column, regWr) \ ath9k_hw_write_array(ah, iniarray, column, &(regWr)) #define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED 2 #define AR_GPIO_OUTPUT_MUX_AS_TX_FRAME 3 #define AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL 4 #define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5 #define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6 #define AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA 0x16 #define AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK 0x17 #define AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA 0x18 #define AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK 0x19 #define AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX 0x14 #define AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX 0x13 #define AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX 9 #define AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX 8 #define AR_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE 0x1d #define AR_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA 0x1e #define AR_GPIOD_MASK 0x00001FFF #define AR_GPIO_BIT(_gpio) (1 << (_gpio)) #define BASE_ACTIVATE_DELAY 100 #define RTC_PLL_SETTLE_DELAY (AR_SREV_9340(ah) ? 1000 : 100) #define COEF_SCALE_S 24 #define HT40_CHANNEL_CENTER_SHIFT 10 #define ATH9K_ANTENNA0_CHAINMASK 0x1 #define ATH9K_ANTENNA1_CHAINMASK 0x2 #define ATH9K_NUM_DMA_DEBUG_REGS 8 #define ATH9K_NUM_QUEUES 10 #define MAX_RATE_POWER 63 #define AH_WAIT_TIMEOUT 100000 /* (us) */ #define AH_TSF_WRITE_TIMEOUT 100 /* (us) */ #define AH_TIME_QUANTUM 10 #define AR_KEYTABLE_SIZE 128 #define POWER_UP_TIME 10000 #define SPUR_RSSI_THRESH 40 #define UPPER_5G_SUB_BAND_START 5700 #define MID_5G_SUB_BAND_START 5400 #define CAB_TIMEOUT_VAL 10 #define BEACON_TIMEOUT_VAL 10 #define MIN_BEACON_TIMEOUT_VAL 1 #define SLEEP_SLOP 3 #define INIT_CONFIG_STATUS 0x00000000 #define INIT_RSSI_THR 0x00000700 #define INIT_BCON_CNTRL_REG 0x00000000 #define TU_TO_USEC(_tu) ((_tu) << 10) #define ATH9K_HW_RX_HP_QDEPTH 16 #define ATH9K_HW_RX_LP_QDEPTH 128 #define PAPRD_GAIN_TABLE_ENTRIES 32 #define PAPRD_TABLE_SZ 24 #define PAPRD_IDEAL_AGC2_PWR_RANGE 0xe0 /* * Wake on Wireless */ /* Keep Alive Frame */ #define KAL_FRAME_LEN 28 #define KAL_FRAME_TYPE 0x2 /* data frame */ #define KAL_FRAME_SUB_TYPE 0x4 /* null data frame */ #define KAL_DURATION_ID 0x3d #define KAL_NUM_DATA_WORDS 6 #define KAL_NUM_DESC_WORDS 12 #define KAL_ANTENNA_MODE 1 #define KAL_TO_DS 1 #define KAL_DELAY 4 /*delay of 4ms between 2 KAL frames */ #define KAL_TIMEOUT 900 #define MAX_PATTERN_SIZE 256 #define MAX_PATTERN_MASK_SIZE 32 #define MAX_NUM_PATTERN 8 #define MAX_NUM_USER_PATTERN 6 /* deducting the disassociate and deauthenticate packets */ /* * WoW trigger mapping to hardware code */ #define AH_WOW_USER_PATTERN_EN BIT(0) #define AH_WOW_MAGIC_PATTERN_EN BIT(1) #define AH_WOW_LINK_CHANGE BIT(2) #define AH_WOW_BEACON_MISS BIT(3) enum ath_hw_txq_subtype { ATH_TXQ_AC_BE = 0, ATH_TXQ_AC_BK = 1, ATH_TXQ_AC_VI = 2, ATH_TXQ_AC_VO = 3, }; enum ath_ini_subsys { ATH_INI_PRE = 0, ATH_INI_CORE, ATH_INI_POST, ATH_INI_NUM_SPLIT, }; enum ath9k_hw_caps { ATH9K_HW_CAP_HT = BIT(0), ATH9K_HW_CAP_RFSILENT = BIT(1), ATH9K_HW_CAP_AUTOSLEEP = BIT(2), ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(3), ATH9K_HW_CAP_EDMA = BIT(4), ATH9K_HW_CAP_RAC_SUPPORTED = BIT(5), ATH9K_HW_CAP_LDPC = BIT(6), ATH9K_HW_CAP_FASTCLOCK = BIT(7), ATH9K_HW_CAP_SGI_20 = BIT(8), ATH9K_HW_CAP_ANT_DIV_COMB = BIT(10), ATH9K_HW_CAP_2GHZ = BIT(11), ATH9K_HW_CAP_5GHZ = BIT(12), ATH9K_HW_CAP_APM = BIT(13), ATH9K_HW_CAP_RTT = BIT(14), ATH9K_HW_CAP_MCI = BIT(15), ATH9K_HW_CAP_DFS = BIT(16), ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17), ATH9K_HW_WOW_PATTERN_MATCH_EXACT = BIT(18), ATH9K_HW_WOW_PATTERN_MATCH_DWORD = BIT(19), }; /* * WoW device capabilities * @ATH9K_HW_WOW_DEVICE_CAPABLE: device revision is capable of WoW. * @ATH9K_HW_WOW_PATTERN_MATCH_EXACT: device is capable of matching * an exact user defined pattern or de-authentication/disassoc pattern. * @ATH9K_HW_WOW_PATTERN_MATCH_DWORD: device requires the first four * bytes of the pattern for user defined pattern, de-authentication and * disassociation patterns for all types of possible frames recieved * of those types. */ struct ath9k_hw_capabilities { u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */ u16 rts_aggr_limit; u8 tx_chainmask; u8 rx_chainmask; u8 max_txchains; u8 max_rxchains; u8 num_gpio_pins; u8 rx_hp_qdepth; u8 rx_lp_qdepth; u8 rx_status_len; u8 tx_desc_len; u8 txs_len; u16 pcie_lcr_offset; bool pcie_lcr_extsync_en; }; struct ath9k_ops_config { int dma_beacon_response_time; int sw_beacon_response_time; int additional_swba_backoff; int ack_6mb; u32 cwm_ignore_extcca; bool pcieSerDesWrite; u8 pcie_clock_req; u32 pcie_waen; u8 analog_shiftreg; u32 ofdm_trig_low; u32 ofdm_trig_high; u32 cck_trig_high; u32 cck_trig_low; u32 enable_ani; u32 enable_paprd; int serialize_regmode; bool rx_intr_mitigation; bool tx_intr_mitigation; #define SPUR_DISABLE 0 #define SPUR_ENABLE_IOCTL 1 #define SPUR_ENABLE_EEPROM 2 #define AR_SPUR_5413_1 1640 #define AR_SPUR_5413_2 1200 #define AR_NO_SPUR 0x8000 #define AR_BASE_FREQ_2GHZ 2300 #define AR_BASE_FREQ_5GHZ 4900 #define AR_SPUR_FEEQ_BOUND_HT40 19 #define AR_SPUR_FEEQ_BOUND_HT20 10 int spurmode; u16 spurchans[AR_EEPROM_MODAL_SPURS][2]; u8 max_txtrig_level; u16 ani_poll_interval; /* ANI poll interval in ms */ }; enum ath9k_int { ATH9K_INT_RX = 0x00000001, ATH9K_INT_RXDESC = 0x00000002, ATH9K_INT_RXHP = 0x00000001, ATH9K_INT_RXLP = 0x00000002, ATH9K_INT_RXNOFRM = 0x00000008, ATH9K_INT_RXEOL = 0x00000010, ATH9K_INT_RXORN = 0x00000020, ATH9K_INT_TX = 0x00000040, ATH9K_INT_TXDESC = 0x00000080, ATH9K_INT_TIM_TIMER = 0x00000100, ATH9K_INT_MCI = 0x00000200, ATH9K_INT_BB_WATCHDOG = 0x00000400, ATH9K_INT_TXURN = 0x00000800, ATH9K_INT_MIB = 0x00001000, ATH9K_INT_RXPHY = 0x00004000, ATH9K_INT_RXKCM = 0x00008000, ATH9K_INT_SWBA = 0x00010000, ATH9K_INT_BMISS = 0x00040000, ATH9K_INT_BNR = 0x00100000, ATH9K_INT_TIM = 0x00200000, ATH9K_INT_DTIM = 0x00400000, ATH9K_INT_DTIMSYNC = 0x00800000, ATH9K_INT_GPIO = 0x01000000, ATH9K_INT_CABEND = 0x02000000, ATH9K_INT_TSFOOR = 0x04000000, ATH9K_INT_GENTIMER = 0x08000000, ATH9K_INT_CST = 0x10000000, ATH9K_INT_GTT = 0x20000000, ATH9K_INT_FATAL = 0x40000000, ATH9K_INT_GLOBAL = 0x80000000, ATH9K_INT_BMISC = ATH9K_INT_TIM | ATH9K_INT_DTIM | ATH9K_INT_DTIMSYNC | ATH9K_INT_TSFOOR | ATH9K_INT_CABEND, ATH9K_INT_COMMON = ATH9K_INT_RXNOFRM | ATH9K_INT_RXDESC | ATH9K_INT_RXEOL | ATH9K_INT_RXORN | ATH9K_INT_TXURN | ATH9K_INT_TXDESC | ATH9K_INT_MIB | ATH9K_INT_RXPHY | ATH9K_INT_RXKCM | ATH9K_INT_SWBA | ATH9K_INT_BMISS | ATH9K_INT_GPIO, ATH9K_INT_NOCARD = 0xffffffff }; #define CHANNEL_CW_INT 0x00002 #define CHANNEL_CCK 0x00020 #define CHANNEL_OFDM 0x00040 #define CHANNEL_2GHZ 0x00080 #define CHANNEL_5GHZ 0x00100 #define CHANNEL_PASSIVE 0x00200 #define CHANNEL_DYN 0x00400 #define CHANNEL_HALF 0x04000 #define CHANNEL_QUARTER 0x08000 #define CHANNEL_HT20 0x10000 #define CHANNEL_HT40PLUS 0x20000 #define CHANNEL_HT40MINUS 0x40000 #define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) #define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) #define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) #define CHANNEL_G_HT20 (CHANNEL_2GHZ|CHANNEL_HT20) #define CHANNEL_A_HT20 (CHANNEL_5GHZ|CHANNEL_HT20) #define CHANNEL_G_HT40PLUS (CHANNEL_2GHZ|CHANNEL_HT40PLUS) #define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS) #define CHANNEL_A_HT40PLUS (CHANNEL_5GHZ|CHANNEL_HT40PLUS) #define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS) #define CHANNEL_ALL \ (CHANNEL_OFDM| \ CHANNEL_CCK| \ CHANNEL_2GHZ | \ CHANNEL_5GHZ | \ CHANNEL_HT20 | \ CHANNEL_HT40PLUS | \ CHANNEL_HT40MINUS) #define MAX_RTT_TABLE_ENTRY 6 #define MAX_IQCAL_MEASUREMENT 8 #define MAX_CL_TAB_ENTRY 16 struct ath9k_hw_cal_data { u16 channel; u32 channelFlags; int32_t CalValid; int8_t iCoff; int8_t qCoff; bool rtt_done; bool paprd_packet_sent; bool paprd_done; bool nfcal_pending; bool nfcal_interference; bool done_txiqcal_once; bool done_txclcal_once; u16 small_signal_gain[AR9300_MAX_CHAINS]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; u32 num_measures[AR9300_MAX_CHAINS]; int tx_corr_coeff[MAX_IQCAL_MEASUREMENT][AR9300_MAX_CHAINS]; u32 tx_clcal[AR9300_MAX_CHAINS][MAX_CL_TAB_ENTRY]; u32 rtt_table[AR9300_MAX_CHAINS][MAX_RTT_TABLE_ENTRY]; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; }; struct ath9k_channel { struct ieee80211_channel *chan; struct ar5416AniState ani; u16 channel; u32 channelFlags; u32 chanmode; s16 noisefloor; }; #define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \ (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \ (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS)) #define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0) #define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0) #define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0) #define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0) #define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0) #define IS_CHAN_A_FAST_CLOCK(_ah, _c) \ ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \ ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)) /* These macros check chanmode and not channelFlags */ #define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B) #define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \ ((_c)->chanmode == CHANNEL_G_HT20)) #define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \ ((_c)->chanmode == CHANNEL_A_HT40MINUS) || \ ((_c)->chanmode == CHANNEL_G_HT40PLUS) || \ ((_c)->chanmode == CHANNEL_G_HT40MINUS)) #define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c))) enum ath9k_power_mode { ATH9K_PM_AWAKE = 0, ATH9K_PM_FULL_SLEEP, ATH9K_PM_NETWORK_SLEEP, ATH9K_PM_UNDEFINED }; enum ser_reg_mode { SER_REG_MODE_OFF = 0, SER_REG_MODE_ON = 1, SER_REG_MODE_AUTO = 2, }; enum ath9k_rx_qtype { ATH9K_RX_QUEUE_HP, ATH9K_RX_QUEUE_LP, ATH9K_RX_QUEUE_MAX, }; struct ath9k_beacon_state { u32 bs_nexttbtt; u32 bs_nextdtim; u32 bs_intval; #define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */ u32 bs_dtimperiod; u16 bs_cfpperiod; u16 bs_cfpmaxduration; u32 bs_cfpnext; u16 bs_timoffset; u16 bs_bmissthreshold; u32 bs_sleepduration; u32 bs_tsfoor_threshold; }; struct chan_centers { u16 synth_center; u16 ctl_center; u16 ext_center; }; enum { ATH9K_RESET_POWER_ON, ATH9K_RESET_WARM, ATH9K_RESET_COLD, }; struct ath9k_hw_version { u32 magic; u16 devid; u16 subvendorid; u32 macVersion; u16 macRev; u16 phyRev; u16 analog5GhzRev; u16 analog2GhzRev; enum ath_usb_dev usbdev; }; /* Generic TSF timer definitions */ #define ATH_MAX_GEN_TIMER 16 #define AR_GENTMR_BIT(_index) (1 << (_index)) /* * Using de Bruijin sequence to look up 1's index in a 32 bit number * debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001 */ #define debruijn32 0x077CB531U struct ath_gen_timer_configuration { u32 next_addr; u32 period_addr; u32 mode_addr; u32 mode_mask; }; struct ath_gen_timer { void (*trigger)(void *arg); void (*overflow)(void *arg); void *arg; u8 index; }; struct ath_gen_timer_table { u32 gen_timer_index[32]; struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER]; union { unsigned long timer_bits; u16 val; } timer_mask; }; struct ath_hw_antcomb_conf { u8 main_lna_conf; u8 alt_lna_conf; u8 fast_div_bias; u8 main_gaintb; u8 alt_gaintb; int lna1_lna2_delta; u8 div_group; }; /** * struct ath_hw_radar_conf - radar detection initialization parameters * * @pulse_inband: threshold for checking the ratio of in-band power * to total power for short radar pulses (half dB steps) * @pulse_inband_step: threshold for checking an in-band power to total * power ratio increase for short radar pulses (half dB steps) * @pulse_height: threshold for detecting the beginning of a short * radar pulse (dB step) * @pulse_rssi: threshold for detecting if a short radar pulse is * gone (dB step) * @pulse_maxlen: maximum pulse length (0.8 us steps) * * @radar_rssi: RSSI threshold for starting long radar detection (dB steps) * @radar_inband: threshold for checking the ratio of in-band power * to total power for long radar pulses (half dB steps) * @fir_power: threshold for detecting the end of a long radar pulse (dB) * * @ext_channel: enable extension channel radar detection */ struct ath_hw_radar_conf { unsigned int pulse_inband; unsigned int pulse_inband_step; unsigned int pulse_height; unsigned int pulse_rssi; unsigned int pulse_maxlen; unsigned int radar_rssi; unsigned int radar_inband; int fir_power; bool ext_channel; }; /** * struct ath_hw_private_ops - callbacks used internally by hardware code * * This structure contains private callbacks designed to only be used internally * by the hardware core. * * @init_cal_settings: setup types of calibrations supported * @init_cal: starts actual calibration * * @init_mode_regs: Initializes mode registers * @init_mode_gain_regs: Initialize TX/RX gain registers * * @rf_set_freq: change frequency * @spur_mitigate_freq: spur mitigation * @rf_alloc_ext_banks: * @rf_free_ext_banks: * @set_rf_regs: * @compute_pll_control: compute the PLL control value to use for * AR_RTC_PLL_CONTROL for a given channel * @setup_calibration: set up calibration * @iscal_supported: used to query if a type of calibration is supported * * @ani_cache_ini_regs: cache the values for ANI from the initial * register settings through the register initialization. */ struct ath_hw_private_ops { /* Calibration ops */ void (*init_cal_settings)(struct ath_hw *ah); bool (*init_cal)(struct ath_hw *ah, struct ath9k_channel *chan); void (*init_mode_regs)(struct ath_hw *ah); void (*init_mode_gain_regs)(struct ath_hw *ah); void (*setup_calibration)(struct ath_hw *ah, struct ath9k_cal_list *currCal); /* PHY ops */ int (*rf_set_freq)(struct ath_hw *ah, struct ath9k_channel *chan); void (*spur_mitigate_freq)(struct ath_hw *ah, struct ath9k_channel *chan); int (*rf_alloc_ext_banks)(struct ath_hw *ah); void (*rf_free_ext_banks)(struct ath_hw *ah); bool (*set_rf_regs)(struct ath_hw *ah, struct ath9k_channel *chan, u16 modesIndex); void (*set_channel_regs)(struct ath_hw *ah, struct ath9k_channel *chan); void (*init_bb)(struct ath_hw *ah, struct ath9k_channel *chan); int (*process_ini)(struct ath_hw *ah, struct ath9k_channel *chan); void (*olc_init)(struct ath_hw *ah); void (*set_rfmode)(struct ath_hw *ah, struct ath9k_channel *chan); void (*mark_phy_inactive)(struct ath_hw *ah); void (*set_delta_slope)(struct ath_hw *ah, struct ath9k_channel *chan); bool (*rfbus_req)(struct ath_hw *ah); void (*rfbus_done)(struct ath_hw *ah); void (*restore_chainmask)(struct ath_hw *ah); u32 (*compute_pll_control)(struct ath_hw *ah, struct ath9k_channel *chan); bool (*ani_control)(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param); void (*do_getnf)(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]); void (*set_radar_params)(struct ath_hw *ah, struct ath_hw_radar_conf *conf); int (*fast_chan_change)(struct ath_hw *ah, struct ath9k_channel *chan, u8 *ini_reloaded); /* ANI */ void (*ani_cache_ini_regs)(struct ath_hw *ah); }; /** * struct ath_hw_ops - callbacks used by hardware code and driver code * * This structure contains callbacks designed to to be used internally by * hardware code and also by the lower level driver. * * @config_pci_powersave: * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC */ struct ath_hw_ops { void (*config_pci_powersave)(struct ath_hw *ah, bool power_off); void (*rx_enable)(struct ath_hw *ah); void (*set_desc_link)(void *ds, u32 link); bool (*calibrate)(struct ath_hw *ah, struct ath9k_channel *chan, u8 rxchainmask, bool longcal); bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked); void (*set_txdesc)(struct ath_hw *ah, void *ds, struct ath_tx_info *i); int (*proc_txdesc)(struct ath_hw *ah, void *ds, struct ath_tx_status *ts); void (*antdiv_comb_conf_get)(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf); void (*antdiv_comb_conf_set)(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf); }; struct ath_nf_limits { s16 max; s16 min; s16 nominal; }; enum ath_cal_list { TX_IQ_CAL = BIT(0), TX_IQ_ON_AGC_CAL = BIT(1), TX_CL_CAL = BIT(2), }; /* ah_flags */ #define AH_USE_EEPROM 0x1 #define AH_UNPLUGGED 0x2 /* The card has been physically removed. */ #define AH_FASTCC 0x4 struct ath_hw { struct ath_ops reg_ops; struct ieee80211_hw *hw; struct ath_common common; struct ath9k_hw_version hw_version; struct ath9k_ops_config config; struct ath9k_hw_capabilities caps; struct ath9k_channel channels[ATH9K_NUM_CHANNELS]; struct ath9k_channel *curchan; union { struct ar5416_eeprom_def def; struct ar5416_eeprom_4k map4k; struct ar9287_eeprom map9287; struct ar9300_eeprom ar9300_eep; } eeprom; const struct eeprom_ops *eep_ops; bool sw_mgmt_crypto; bool is_pciexpress; bool aspm_enabled; bool is_monitoring; bool need_an_top2_fixup; u16 tx_trig_level; u32 nf_regs[6]; struct ath_nf_limits nf_2g; struct ath_nf_limits nf_5g; u16 rfsilent; u32 rfkill_gpio; u32 rfkill_polarity; u32 ah_flags; bool htc_reset_init; enum nl80211_iftype opmode; enum ath9k_power_mode power_mode; s8 noise; struct ath9k_hw_cal_data *caldata; struct ath9k_pacal_info pacal_info; struct ar5416Stats stats; struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES]; enum ath9k_int imask; u32 imrs2_reg; u32 txok_interrupt_mask; u32 txerr_interrupt_mask; u32 txdesc_interrupt_mask; u32 txeol_interrupt_mask; u32 txurn_interrupt_mask; atomic_t intr_ref_cnt; bool chip_fullsleep; u32 atim_window; u32 modes_index; /* Calibration */ u32 supp_cals; struct ath9k_cal_list iq_caldata; struct ath9k_cal_list adcgain_caldata; struct ath9k_cal_list adcdc_caldata; struct ath9k_cal_list tempCompCalData; struct ath9k_cal_list *cal_list; struct ath9k_cal_list *cal_list_last; struct ath9k_cal_list *cal_list_curr; #define totalPowerMeasI meas0.unsign #define totalPowerMeasQ meas1.unsign #define totalIqCorrMeas meas2.sign #define totalAdcIOddPhase meas0.unsign #define totalAdcIEvenPhase meas1.unsign #define totalAdcQOddPhase meas2.unsign #define totalAdcQEvenPhase meas3.unsign #define totalAdcDcOffsetIOddPhase meas0.sign #define totalAdcDcOffsetIEvenPhase meas1.sign #define totalAdcDcOffsetQOddPhase meas2.sign #define totalAdcDcOffsetQEvenPhase meas3.sign union { u32 unsign[AR5416_MAX_CHAINS]; int32_t sign[AR5416_MAX_CHAINS]; } meas0; union { u32 unsign[AR5416_MAX_CHAINS]; int32_t sign[AR5416_MAX_CHAINS]; } meas1; union { u32 unsign[AR5416_MAX_CHAINS]; int32_t sign[AR5416_MAX_CHAINS]; } meas2; union { u32 unsign[AR5416_MAX_CHAINS]; int32_t sign[AR5416_MAX_CHAINS]; } meas3; u16 cal_samples; u8 enabled_cals; u32 sta_id1_defaults; u32 misc_mode; /* Private to hardware code */ struct ath_hw_private_ops private_ops; /* Accessed by the lower level driver */ struct ath_hw_ops ops; /* Used to program the radio on non single-chip devices */ u32 *analogBank0Data; u32 *analogBank1Data; u32 *analogBank2Data; u32 *analogBank3Data; u32 *analogBank6Data; u32 *analogBank6TPCData; u32 *analogBank7Data; u32 *bank6Temp; int coverage_class; u32 slottime; u32 globaltxtimeout; /* ANI */ u32 proc_phyerr; u32 aniperiod; int totalSizeDesired[5]; int coarse_high[5]; int coarse_low[5]; int firpwr[5]; enum ath9k_ani_cmd ani_function; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex_hw btcoex_hw; #endif u32 intr_txqs; u8 txchainmask; u8 rxchainmask; struct ath_hw_radar_conf radar_conf; u32 originalGain[22]; int initPDADC; int PDADCdelta; int led_pin; u32 gpio_mask; u32 gpio_val; struct ar5416IniArray iniModes; struct ar5416IniArray iniCommon; struct ar5416IniArray iniBank0; struct ar5416IniArray iniBB_RfGain; struct ar5416IniArray iniBank1; struct ar5416IniArray iniBank2; struct ar5416IniArray iniBank3; struct ar5416IniArray iniBank6; struct ar5416IniArray iniBank6TPC; struct ar5416IniArray iniBank7; struct ar5416IniArray iniAddac; struct ar5416IniArray iniPcieSerdes; #ifdef CONFIG_PM_SLEEP struct ar5416IniArray iniPcieSerdesWow; #endif struct ar5416IniArray iniPcieSerdesLowPower; struct ar5416IniArray iniModesFastClock; struct ar5416IniArray iniAdditional; struct ar5416IniArray iniModesRxGain; struct ar5416IniArray ini_modes_rx_gain_bounds; struct ar5416IniArray iniModesTxGain; struct ar5416IniArray iniCckfirNormal; struct ar5416IniArray iniCckfirJapan2484; struct ar5416IniArray ini_japan2484; struct ar5416IniArray iniModes_9271_ANI_reg; struct ar5416IniArray ini_radio_post_sys2ant; struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT]; struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT]; struct ar5416IniArray iniRadio[ATH_INI_NUM_SPLIT]; struct ar5416IniArray iniSOC[ATH_INI_NUM_SPLIT]; u32 intr_gen_timer_trigger; u32 intr_gen_timer_thresh; struct ath_gen_timer_table hw_gen_timers; struct ar9003_txs *ts_ring; u32 ts_paddr_start; u32 ts_paddr_end; u16 ts_tail; u16 ts_size; u32 bb_watchdog_last_status; u32 bb_watchdog_timeout_ms; /* in ms, 0 to disable */ u8 bb_hang_rx_ofdm; /* true if bb hang due to rx_ofdm */ unsigned int paprd_target_power; unsigned int paprd_training_power; unsigned int paprd_ratemask; unsigned int paprd_ratemask_ht40; bool paprd_table_write_done; u32 paprd_gain_table_entries[PAPRD_GAIN_TABLE_ENTRIES]; u8 paprd_gain_table_index[PAPRD_GAIN_TABLE_ENTRIES]; /* * Store the permanent value of Reg 0x4004in WARegVal * so we dont have to R/M/W. We should not be reading * this register when in sleep states. */ u32 WARegVal; /* Enterprise mode cap */ u32 ent_mode; #ifdef CONFIG_PM_SLEEP u32 wow_event_mask; #endif bool is_clk_25mhz; int (*get_mac_revision)(void); int (*external_reset)(void); }; struct ath_bus_ops { enum ath_bus_type ath_bus_type; void (*read_cachesize)(struct ath_common *common, int *csz); bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data); void (*bt_coex_prep)(struct ath_common *common); void (*extn_synch_en)(struct ath_common *common); void (*aspm_init)(struct ath_common *common); }; static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah) { return &ah->common; } static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah) { return &(ath9k_hw_common(ah)->regulatory); } static inline struct ath_hw_private_ops *ath9k_hw_private_ops(struct ath_hw *ah) { return &ah->private_ops; } static inline struct ath_hw_ops *ath9k_hw_ops(struct ath_hw *ah) { return &ah->ops; } static inline u8 get_streams(int mask) { return !!(mask & BIT(0)) + !!(mask & BIT(1)) + !!(mask & BIT(2)); } /* Initialization, Detach, Reset */ void ath9k_hw_deinit(struct ath_hw *ah); int ath9k_hw_init(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata, bool fastcc); int ath9k_hw_fill_cap_info(struct ath_hw *ah); u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan); /* GPIO / RFKILL / Antennae */ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio); u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio); void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, u32 ah_signal_type); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); /* General Operation */ void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, int hw_delay); bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array, int column, unsigned int *writecnt); u32 ath9k_hw_reverse_bits(u32 val, u32 n); u16 ath9k_hw_computetxtime(struct ath_hw *ah, u8 phy, int kbps, u32 frameLen, u16 rateix, bool shortPreamble); void ath9k_hw_get_channel_centers(struct ath_hw *ah, struct ath9k_channel *chan, struct chan_centers *centers); u32 ath9k_hw_getrxfilter(struct ath_hw *ah); void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits); bool ath9k_hw_phy_disable(struct ath_hw *ah); bool ath9k_hw_disable(struct ath_hw *ah); void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test); void ath9k_hw_setopmode(struct ath_hw *ah); void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1); void ath9k_hw_write_associd(struct ath_hw *ah); u32 ath9k_hw_gettsf32(struct ath_hw *ah); u64 ath9k_hw_gettsf64(struct ath_hw *ah); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set); void ath9k_hw_init_global_settings(struct ath_hw *ah); u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah); void ath9k_hw_set11nmac2040(struct ath_hw *ah); void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs); bool ath9k_hw_check_alive(struct ath_hw *ah); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); #ifdef CONFIG_ATH9K_DEBUGFS void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause); #else static inline void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause) {} #endif /* Generic hw timer primitives */ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, void (*trigger)(void *), void (*overflow)(void *), void *arg, u8 timer_index); void ath9k_hw_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, u32 timer_next, u32 timer_period); void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer); void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer); void ath_gen_timer_isr(struct ath_hw *hw); void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len); /* PHY */ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, u32 *coef_mantissa, u32 *coef_exponent); void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, bool test); /* * Code Specific to AR5008, AR9001 or AR9002, * we stuff these here to avoid callbacks for AR9003. */ int ar9002_hw_rf_claim(struct ath_hw *ah); void ar9002_hw_enable_async_fifo(struct ath_hw *ah); /* * Code specific to AR9003, we stuff these here to avoid callbacks * for older families */ void ar9003_hw_bb_watchdog_config(struct ath_hw *ah); void ar9003_hw_bb_watchdog_read(struct ath_hw *ah); void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah); void ar9003_hw_disable_phy_restart(struct ath_hw *ah); void ar9003_paprd_enable(struct ath_hw *ah, bool val); void ar9003_paprd_populate_single_table(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata, int chain); int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata, int chain); int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); /* Hardware family op attach helpers */ void ar5008_hw_attach_phy_ops(struct ath_hw *ah); void ar9002_hw_attach_phy_ops(struct ath_hw *ah); void ar9003_hw_attach_phy_ops(struct ath_hw *ah); void ar9002_hw_attach_calib_ops(struct ath_hw *ah); void ar9003_hw_attach_calib_ops(struct ath_hw *ah); void ar9002_hw_attach_ops(struct ath_hw *ah); void ar9003_hw_attach_ops(struct ath_hw *ah); void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning); void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) { return ah->btcoex_hw.enabled; } static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah) { return ah->common.btcoex_enabled && (ah->caps.hw_caps & ATH9K_HW_CAP_MCI); } void ath9k_hw_btcoex_enable(struct ath_hw *ah); static inline enum ath_btcoex_scheme ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) { return ah->btcoex_hw.scheme; } #else static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) { return false; } static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah) { return false; } static inline void ath9k_hw_btcoex_enable(struct ath_hw *ah) { } static inline enum ath_btcoex_scheme ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) { return ATH_BTCOEX_CFG_NONE; } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #ifdef CONFIG_PM_SLEEP const char *ath9k_hw_wow_event_to_string(u32 wow_event); void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, u8 *user_mask, int pattern_count, int pattern_len); u32 ath9k_hw_wow_wakeup(struct ath_hw *ah); void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable); #else static inline const char *ath9k_hw_wow_event_to_string(u32 wow_event) { return NULL; } static inline void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, u8 *user_mask, int pattern_count, int pattern_len) { } static inline u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) { return 0; } static inline void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) { } #endif #define ATH9K_CLOCK_RATE_CCK 22 #define ATH9K_CLOCK_RATE_5GHZ_OFDM 40 #define ATH9K_CLOCK_RATE_2GHZ_OFDM 44 #define ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM 44 #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/hw.c0000644000175000017500000024401212026211315023042 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "hw.h" #include "hw-ops.h" #include "rc.h" #include "ar9003_mac.h" #include "ar9003_mci.h" #include "debug.h" #include "ath9k.h" static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); static int __init ath9k_init(void) { return 0; } module_init(ath9k_init); static void __exit ath9k_exit(void) { return; } module_exit(ath9k_exit); /* Private hardware callbacks */ static void ath9k_hw_init_cal_settings(struct ath_hw *ah) { ath9k_hw_private_ops(ah)->init_cal_settings(ah); } static void ath9k_hw_init_mode_regs(struct ath_hw *ah) { ath9k_hw_private_ops(ah)->init_mode_regs(ah); } static u32 ath9k_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_private_ops(ah)->compute_pll_control(ah, chan); } static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) { if (!ath9k_hw_private_ops(ah)->init_mode_gain_regs) return; ath9k_hw_private_ops(ah)->init_mode_gain_regs(ah); } static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah) { /* You will not have this callback if using the old ANI */ if (!ath9k_hw_private_ops(ah)->ani_cache_ini_regs) return; ath9k_hw_private_ops(ah)->ani_cache_ini_regs(ah); } /********************/ /* Helper Functions */ /********************/ #ifdef CONFIG_ATH9K_DEBUGFS void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause) { struct ath_softc *sc = common->priv; if (sync_cause) sc->debug.stats.istats.sync_cause_all++; if (sync_cause & AR_INTR_SYNC_RTC_IRQ) sc->debug.stats.istats.sync_rtc_irq++; if (sync_cause & AR_INTR_SYNC_MAC_IRQ) sc->debug.stats.istats.sync_mac_irq++; if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS) sc->debug.stats.istats.eeprom_illegal_access++; if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT) sc->debug.stats.istats.apb_timeout++; if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT) sc->debug.stats.istats.pci_mode_conflict++; if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) sc->debug.stats.istats.host1_fatal++; if (sync_cause & AR_INTR_SYNC_HOST1_PERR) sc->debug.stats.istats.host1_perr++; if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR) sc->debug.stats.istats.trcv_fifo_perr++; if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP) sc->debug.stats.istats.radm_cpl_ep++; if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT) sc->debug.stats.istats.radm_cpl_dllp_abort++; if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT) sc->debug.stats.istats.radm_cpl_tlp_abort++; if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR) sc->debug.stats.istats.radm_cpl_ecrc_err++; if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) sc->debug.stats.istats.radm_cpl_timeout++; if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) sc->debug.stats.istats.local_timeout++; if (sync_cause & AR_INTR_SYNC_PM_ACCESS) sc->debug.stats.istats.pm_access++; if (sync_cause & AR_INTR_SYNC_MAC_AWAKE) sc->debug.stats.istats.mac_awake++; if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP) sc->debug.stats.istats.mac_asleep++; if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS) sc->debug.stats.istats.mac_sleep_access++; } #endif static void ath9k_hw_set_clockrate(struct ath_hw *ah) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; struct ath_common *common = ath9k_hw_common(ah); unsigned int clockrate; /* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */ if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) clockrate = 117; else if (!ah->curchan) /* should really check for CCK instead */ clockrate = ATH9K_CLOCK_RATE_CCK; else if (conf->channel->band == IEEE80211_BAND_2GHZ) clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM; else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; else clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; if (conf_is_ht40(conf)) clockrate *= 2; if (ah->curchan) { if (IS_CHAN_HALF_RATE(ah->curchan)) clockrate /= 2; if (IS_CHAN_QUARTER_RATE(ah->curchan)) clockrate /= 4; } common->clockrate = clockrate; } static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs) { struct ath_common *common = ath9k_hw_common(ah); return usecs * common->clockrate; } bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) { int i; BUG_ON(timeout < AH_TIME_QUANTUM); for (i = 0; i < (timeout / AH_TIME_QUANTUM); i++) { if ((REG_READ(ah, reg) & mask) == val) return true; udelay(AH_TIME_QUANTUM); } ath_dbg(ath9k_hw_common(ah), ANY, "timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", timeout, reg, REG_READ(ah, reg), mask, val); return false; } EXPORT_SYMBOL(ath9k_hw_wait); void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, int hw_delay) { if (IS_CHAN_B(chan)) hw_delay = (4 * hw_delay) / 22; else hw_delay /= 10; if (IS_CHAN_HALF_RATE(chan)) hw_delay *= 2; else if (IS_CHAN_QUARTER_RATE(chan)) hw_delay *= 4; udelay(hw_delay + BASE_ACTIVATE_DELAY); } void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array, int column, unsigned int *writecnt) { int r; ENABLE_REGWRITE_BUFFER(ah); for (r = 0; r < array->ia_rows; r++) { REG_WRITE(ah, INI_RA(array, r, 0), INI_RA(array, r, column)); DO_DELAY(*writecnt); } REGWRITE_BUFFER_FLUSH(ah); } u32 ath9k_hw_reverse_bits(u32 val, u32 n) { u32 retval; int i; for (i = 0, retval = 0; i < n; i++) { retval = (retval << 1) | (val & 1); val >>= 1; } return retval; } u16 ath9k_hw_computetxtime(struct ath_hw *ah, u8 phy, int kbps, u32 frameLen, u16 rateix, bool shortPreamble) { u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime; if (kbps == 0) return 0; switch (phy) { case WLAN_RC_PHY_CCK: phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; if (shortPreamble) phyTime >>= 1; numBits = frameLen << 3; txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000) / kbps); break; case WLAN_RC_PHY_OFDM: if (ah->curchan && IS_CHAN_QUARTER_RATE(ah->curchan)) { bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); txTime = OFDM_SIFS_TIME_QUARTER + OFDM_PREAMBLE_TIME_QUARTER + (numSymbols * OFDM_SYMBOL_TIME_QUARTER); } else if (ah->curchan && IS_CHAN_HALF_RATE(ah->curchan)) { bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); txTime = OFDM_SIFS_TIME_HALF + OFDM_PREAMBLE_TIME_HALF + (numSymbols * OFDM_SYMBOL_TIME_HALF); } else { bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME + (numSymbols * OFDM_SYMBOL_TIME); } break; default: ath_err(ath9k_hw_common(ah), "Unknown phy %u (rate ix %u)\n", phy, rateix); txTime = 0; break; } return txTime; } EXPORT_SYMBOL(ath9k_hw_computetxtime); void ath9k_hw_get_channel_centers(struct ath_hw *ah, struct ath9k_channel *chan, struct chan_centers *centers) { int8_t extoff; if (!IS_CHAN_HT40(chan)) { centers->ctl_center = centers->ext_center = centers->synth_center = chan->channel; return; } if ((chan->chanmode == CHANNEL_A_HT40PLUS) || (chan->chanmode == CHANNEL_G_HT40PLUS)) { centers->synth_center = chan->channel + HT40_CHANNEL_CENTER_SHIFT; extoff = 1; } else { centers->synth_center = chan->channel - HT40_CHANNEL_CENTER_SHIFT; extoff = -1; } centers->ctl_center = centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT); /* 25 MHz spacing is supported by hw but not on upper layers */ centers->ext_center = centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT); } /******************/ /* Chip Revisions */ /******************/ static void ath9k_hw_read_revisions(struct ath_hw *ah) { u32 val; switch (ah->hw_version.devid) { case AR5416_AR9100_DEVID: ah->hw_version.macVersion = AR_SREV_VERSION_9100; break; case AR9300_DEVID_AR9330: ah->hw_version.macVersion = AR_SREV_VERSION_9330; if (ah->get_mac_revision) { ah->hw_version.macRev = ah->get_mac_revision(); } else { val = REG_READ(ah, AR_SREV); ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); } return; case AR9300_DEVID_AR9340: ah->hw_version.macVersion = AR_SREV_VERSION_9340; val = REG_READ(ah, AR_SREV); ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); return; case AR9300_DEVID_QCA955X: ah->hw_version.macVersion = AR_SREV_VERSION_9550; return; } val = REG_READ(ah, AR_SREV) & AR_SREV_ID; if (val == 0xFF) { val = REG_READ(ah, AR_SREV); ah->hw_version.macVersion = (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) ah->is_pciexpress = true; else ah->is_pciexpress = (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1; } else { if (!AR_SREV_9100(ah)) ah->hw_version.macVersion = MS(val, AR_SREV_VERSION); ah->hw_version.macRev = val & AR_SREV_REVISION; if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE) ah->is_pciexpress = true; } } /************************************/ /* HW Attach, Detach, Init Routines */ /************************************/ static void ath9k_hw_disablepcie(struct ath_hw *ah) { if (!AR_SREV_5416(ah)) return; REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029); REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824); REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579); REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000); REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007); REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); } /* This should work for all families including legacy */ static bool ath9k_hw_chip_test(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 regAddr[2] = { AR_STA_ID0 }; u32 regHold[2]; static const u32 patternData[4] = { 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999 }; int i, j, loop_max; if (!AR_SREV_9300_20_OR_LATER(ah)) { loop_max = 2; regAddr[1] = AR_PHY_BASE + (8 << 2); } else loop_max = 1; for (i = 0; i < loop_max; i++) { u32 addr = regAddr[i]; u32 wrData, rdData; regHold[i] = REG_READ(ah, addr); for (j = 0; j < 0x100; j++) { wrData = (j << 16) | j; REG_WRITE(ah, addr, wrData); rdData = REG_READ(ah, addr); if (rdData != wrData) { ath_err(common, "address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", addr, wrData, rdData); return false; } } for (j = 0; j < 4; j++) { wrData = patternData[j]; REG_WRITE(ah, addr, wrData); rdData = REG_READ(ah, addr); if (wrData != rdData) { ath_err(common, "address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", addr, wrData, rdData); return false; } } REG_WRITE(ah, regAddr[i], regHold[i]); } udelay(100); return true; } static void ath9k_hw_init_config(struct ath_hw *ah) { int i; ah->config.dma_beacon_response_time = 1; ah->config.sw_beacon_response_time = 6; ah->config.additional_swba_backoff = 0; ah->config.ack_6mb = 0x0; ah->config.cwm_ignore_extcca = 0; ah->config.pcie_clock_req = 0; ah->config.pcie_waen = 0; ah->config.analog_shiftreg = 1; ah->config.enable_ani = true; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { ah->config.spurchans[i][0] = AR_NO_SPUR; ah->config.spurchans[i][1] = AR_NO_SPUR; } ah->config.rx_intr_mitigation = true; ah->config.pcieSerDesWrite = true; /* * We need this for PCI devices only (Cardbus, PCI, miniPCI) * _and_ if on non-uniprocessor systems (Multiprocessor/HT). * This means we use it for all AR5416 devices, and the few * minor PCI AR9280 devices out there. * * Serialization is required because these devices do not handle * well the case of two concurrent reads/writes due to the latency * involved. During one read/write another read/write can be issued * on another CPU while the previous read/write may still be working * on our hardware, if we hit this case the hardware poops in a loop. * We prevent this by serializing reads and writes. * * This issue is not present on PCI-Express devices or pre-AR5416 * devices (legacy, 802.11abg). */ if (num_possible_cpus() > 1) ah->config.serialize_regmode = SER_REG_MODE_AUTO; } static void ath9k_hw_init_defaults(struct ath_hw *ah) { struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); regulatory->country_code = CTRY_DEFAULT; regulatory->power_limit = MAX_RATE_POWER; ah->hw_version.magic = AR5416_MAGIC; ah->hw_version.subvendorid = 0; ah->atim_window = 0; ah->sta_id1_defaults = AR_STA_ID1_CRPT_MIC_ENABLE | AR_STA_ID1_MCAST_KSRCH; if (AR_SREV_9100(ah)) ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX; ah->slottime = ATH9K_SLOT_TIME_9; ah->globaltxtimeout = (u32) -1; ah->power_mode = ATH9K_PM_UNDEFINED; ah->htc_reset_init = true; } static int ath9k_hw_init_macaddr(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 sum; int i; u16 eeval; static const u32 EEP_MAC[] = { EEP_MAC_LSW, EEP_MAC_MID, EEP_MAC_MSW }; sum = 0; for (i = 0; i < 3; i++) { eeval = ah->eep_ops->get_eeprom(ah, EEP_MAC[i]); sum += eeval; common->macaddr[2 * i] = eeval >> 8; common->macaddr[2 * i + 1] = eeval & 0xff; } if (sum == 0 || sum == 0xffff * 3) return -EADDRNOTAVAIL; return 0; } static int ath9k_hw_post_init(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); int ecode; if (common->bus_ops->ath_bus_type != ATH_USB) { if (!ath9k_hw_chip_test(ah)) return -ENODEV; } if (!AR_SREV_9300_20_OR_LATER(ah)) { ecode = ar9002_hw_rf_claim(ah); if (ecode != 0) return ecode; } ecode = ath9k_hw_eeprom_init(ah); if (ecode != 0) return ecode; ath_dbg(ath9k_hw_common(ah), CONFIG, "Eeprom VER: %d, REV: %d\n", ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); ecode = ath9k_hw_rf_alloc_ext_banks(ah); if (ecode) { ath_err(ath9k_hw_common(ah), "Failed allocating banks for external radio\n"); ath9k_hw_rf_free_ext_banks(ah); return ecode; } if (ah->config.enable_ani) { ath9k_hw_ani_setup(ah); ath9k_hw_ani_init(ah); } return 0; } static void ath9k_hw_attach_ops(struct ath_hw *ah) { if (AR_SREV_9300_20_OR_LATER(ah)) ar9003_hw_attach_ops(ah); else ar9002_hw_attach_ops(ah); } /* Called for all hardware families */ static int __ath9k_hw_init(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); int r = 0; ath9k_hw_read_revisions(ah); /* * Read back AR_WA into a permanent copy and set bits 14 and 17. * We need to do this to avoid RMW of this register. We cannot * read the reg when chip is asleep. */ ah->WARegVal = REG_READ(ah, AR_WA); ah->WARegVal |= (AR_WA_D3_L1_DISABLE | AR_WA_ASPM_TIMER_BASED_DISABLE); if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { ath_err(common, "Couldn't reset chip\n"); return -EIO; } if (AR_SREV_9462(ah)) ah->WARegVal &= ~AR_WA_D3_L1_DISABLE; if (AR_SREV_9565(ah)) { ah->WARegVal |= AR_WA_BIT22; REG_WRITE(ah, AR_WA, ah->WARegVal); } ath9k_hw_init_defaults(ah); ath9k_hw_init_config(ah); ath9k_hw_attach_ops(ah); if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { ath_err(common, "Couldn't wakeup chip\n"); return -EIO; } if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_AUTO) { if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || ((AR_SREV_9160(ah) || AR_SREV_9280(ah) || AR_SREV_9287(ah)) && !ah->is_pciexpress)) { ah->config.serialize_regmode = SER_REG_MODE_ON; } else { ah->config.serialize_regmode = SER_REG_MODE_OFF; } } ath_dbg(common, RESET, "serialize_regmode is %d\n", ah->config.serialize_regmode); if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1; else ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD; switch (ah->hw_version.macVersion) { case AR_SREV_VERSION_5416_PCI: case AR_SREV_VERSION_5416_PCIE: case AR_SREV_VERSION_9160: case AR_SREV_VERSION_9100: case AR_SREV_VERSION_9280: case AR_SREV_VERSION_9285: case AR_SREV_VERSION_9287: case AR_SREV_VERSION_9271: case AR_SREV_VERSION_9300: case AR_SREV_VERSION_9330: case AR_SREV_VERSION_9485: case AR_SREV_VERSION_9340: case AR_SREV_VERSION_9462: case AR_SREV_VERSION_9550: case AR_SREV_VERSION_9565: break; default: ath_err(common, "Mac Chip Rev 0x%02x.%x is not supported by this driver\n", ah->hw_version.macVersion, ah->hw_version.macRev); return -EOPNOTSUPP; } if (AR_SREV_9271(ah) || AR_SREV_9100(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) || AR_SREV_9550(ah)) ah->is_pciexpress = false; ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID); ath9k_hw_init_cal_settings(ah); ah->ani_function = ATH9K_ANI_ALL; if (AR_SREV_9280_20_OR_LATER(ah) && !AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; if (!AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_MRC_CCK; ath9k_hw_init_mode_regs(ah); if (!ah->is_pciexpress) ath9k_hw_disablepcie(ah); r = ath9k_hw_post_init(ah); if (r) return r; ath9k_hw_init_mode_gain_regs(ah); r = ath9k_hw_fill_cap_info(ah); if (r) return r; r = ath9k_hw_init_macaddr(ah); if (r) { ath_err(common, "Failed to initialize MAC address\n"); return r; } if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S); else ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); if (AR_SREV_9330(ah)) ah->bb_watchdog_timeout_ms = 85; else ah->bb_watchdog_timeout_ms = 25; common->state = ATH_HW_INITIALIZED; return 0; } int ath9k_hw_init(struct ath_hw *ah) { int ret; struct ath_common *common = ath9k_hw_common(ah); /* These are all the AR5008/AR9001/AR9002/AR9003 hardware family of chipsets */ switch (ah->hw_version.devid) { case AR5416_DEVID_PCI: case AR5416_DEVID_PCIE: case AR5416_AR9100_DEVID: case AR9160_DEVID_PCI: case AR9280_DEVID_PCI: case AR9280_DEVID_PCIE: case AR9285_DEVID_PCIE: case AR9287_DEVID_PCI: case AR9287_DEVID_PCIE: case AR2427_DEVID_PCIE: case AR9300_DEVID_PCIE: case AR9300_DEVID_AR9485_PCIE: case AR9300_DEVID_AR9330: case AR9300_DEVID_AR9340: case AR9300_DEVID_QCA955X: case AR9300_DEVID_AR9580: case AR9300_DEVID_AR9462: case AR9485_DEVID_AR1111: case AR9300_DEVID_AR9565: break; default: if (common->bus_ops->ath_bus_type == ATH_USB) break; ath_err(common, "Hardware device ID 0x%04x not supported\n", ah->hw_version.devid); return -EOPNOTSUPP; } ret = __ath9k_hw_init(ah); if (ret) { ath_err(common, "Unable to initialize hardware; initialization status: %d\n", ret); return ret; } return 0; } EXPORT_SYMBOL(ath9k_hw_init); static void ath9k_hw_init_qos(struct ath_hw *ah) { ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa); REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210); REG_WRITE(ah, AR_QOS_NO_ACK, SM(2, AR_QOS_NO_ACK_TWO_BIT) | SM(5, AR_QOS_NO_ACK_BIT_OFF) | SM(0, AR_QOS_NO_ACK_BYTE_OFF)); REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); REGWRITE_BUFFER_FLUSH(ah); } u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); int i = 0; REG_CLR_BIT(ah, PLL3, PLL3_DO_MEAS_MASK); udelay(100); REG_SET_BIT(ah, PLL3, PLL3_DO_MEAS_MASK); while ((REG_READ(ah, PLL4) & PLL4_MEAS_DONE) == 0) { udelay(100); if (WARN_ON_ONCE(i >= 100)) { ath_err(common, "PLL4 meaurement not done\n"); break; } i++; } return (REG_READ(ah, PLL3) & SQSUM_DVC_MASK) >> 3; } EXPORT_SYMBOL(ar9003_get_pll_sqsum_dvc); static void ath9k_hw_init_pll(struct ath_hw *ah, struct ath9k_channel *chan) { u32 pll; if (AR_SREV_9485(ah) || AR_SREV_9565(ah)) { /* program BB PLL ki and kd value, ki=0x4, kd=0x40 */ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_BB_DPLL2_PLL_PWD, 0x1); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KD, 0x40); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KI, 0x4); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL1, AR_CH0_BB_DPLL1_REFDIV, 0x5); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL1, AR_CH0_BB_DPLL1_NINI, 0x58); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL1, AR_CH0_BB_DPLL1_NFRAC, 0x0); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_BB_DPLL2_OUTDIV, 0x1); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_BB_DPLL2_LOCAL_PLL, 0x1); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_BB_DPLL2_EN_NEGTRIG, 0x1); /* program BB PLL phase_shift to 0x6 */ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3, AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x6); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_BB_DPLL2_PLL_PWD, 0x0); udelay(1000); } else if (AR_SREV_9330(ah)) { u32 ddr_dpll2, pll_control2, kd; if (ah->is_clk_25mhz) { ddr_dpll2 = 0x18e82f01; pll_control2 = 0xe04a3d; kd = 0x1d; } else { ddr_dpll2 = 0x19e82f01; pll_control2 = 0x886666; kd = 0x3d; } /* program DDR PLL ki and kd value */ REG_WRITE(ah, AR_CH0_DDR_DPLL2, ddr_dpll2); /* program DDR PLL phase_shift */ REG_RMW_FIELD(ah, AR_CH0_DDR_DPLL3, AR_CH0_DPLL3_PHASE_SHIFT, 0x1); REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); udelay(1000); /* program refdiv, nint, frac to RTC register */ REG_WRITE(ah, AR_RTC_PLL_CONTROL2, pll_control2); /* program BB PLL kd and ki value */ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KD, kd); REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KI, 0x06); /* program BB PLL phase_shift */ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3, AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x1); } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) { u32 regval, pll2_divint, pll2_divfrac, refdiv; REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); udelay(1000); REG_SET_BIT(ah, AR_PHY_PLL_MODE, 0x1 << 16); udelay(100); if (ah->is_clk_25mhz) { pll2_divint = 0x54; pll2_divfrac = 0x1eb85; refdiv = 3; } else { if (AR_SREV_9340(ah)) { pll2_divint = 88; pll2_divfrac = 0; refdiv = 5; } else { pll2_divint = 0x11; pll2_divfrac = 0x26666; refdiv = 1; } } regval = REG_READ(ah, AR_PHY_PLL_MODE); regval |= (0x1 << 16); REG_WRITE(ah, AR_PHY_PLL_MODE, regval); udelay(100); REG_WRITE(ah, AR_PHY_PLL_CONTROL, (refdiv << 27) | (pll2_divint << 18) | pll2_divfrac); udelay(100); regval = REG_READ(ah, AR_PHY_PLL_MODE); if (AR_SREV_9340(ah)) regval = (regval & 0x80071fff) | (0x1 << 30) | (0x1 << 13) | (0x4 << 26) | (0x18 << 19); else regval = (regval & 0x80071fff) | (0x3 << 30) | (0x1 << 13) | (0x4 << 26) | (0x60 << 19); REG_WRITE(ah, AR_PHY_PLL_MODE, regval); REG_WRITE(ah, AR_PHY_PLL_MODE, REG_READ(ah, AR_PHY_PLL_MODE) & 0xfffeffff); udelay(1000); } pll = ath9k_hw_compute_pll_control(ah, chan); if (AR_SREV_9565(ah)) pll |= 0x40000; REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) || AR_SREV_9550(ah)) udelay(1000); /* Switch the core clock for ar9271 to 117Mhz */ if (AR_SREV_9271(ah)) { udelay(500); REG_WRITE(ah, 0x50040, 0x304); } udelay(RTC_PLL_SETTLE_DELAY); REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK); if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) { if (ah->is_clk_25mhz) { REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1); REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7); REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae); } else { REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1); REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400); REG_WRITE(ah, AR_SLP32_INC, 0x0001e800); } udelay(100); } } static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, enum nl80211_iftype opmode) { u32 sync_default = AR_INTR_SYNC_DEFAULT; u32 imr_reg = AR_IMR_TXERR | AR_IMR_TXURN | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_BCNMISC; if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; if (AR_SREV_9300_20_OR_LATER(ah)) { imr_reg |= AR_IMR_RXOK_HP; if (ah->config.rx_intr_mitigation) imr_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; else imr_reg |= AR_IMR_RXOK_LP; } else { if (ah->config.rx_intr_mitigation) imr_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; else imr_reg |= AR_IMR_RXOK; } if (ah->config.tx_intr_mitigation) imr_reg |= AR_IMR_TXINTM | AR_IMR_TXMINTR; else imr_reg |= AR_IMR_TXOK; ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_IMR, imr_reg); ah->imrs2_reg |= AR_IMR_S2_GTT; REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg); if (!AR_SREV_9100(ah)) { REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF); REG_WRITE(ah, AR_INTR_SYNC_ENABLE, sync_default); REG_WRITE(ah, AR_INTR_SYNC_MASK, 0); } REGWRITE_BUFFER_FLUSH(ah); if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_INTR_PRIO_ASYNC_ENABLE, 0); REG_WRITE(ah, AR_INTR_PRIO_ASYNC_MASK, 0); REG_WRITE(ah, AR_INTR_PRIO_SYNC_ENABLE, 0); REG_WRITE(ah, AR_INTR_PRIO_SYNC_MASK, 0); } } static void ath9k_hw_set_sifs_time(struct ath_hw *ah, u32 us) { u32 val = ath9k_hw_mac_to_clks(ah, us - 2); val = min(val, (u32) 0xFFFF); REG_WRITE(ah, AR_D_GBL_IFS_SIFS, val); } static void ath9k_hw_setslottime(struct ath_hw *ah, u32 us) { u32 val = ath9k_hw_mac_to_clks(ah, us); val = min(val, (u32) 0xFFFF); REG_WRITE(ah, AR_D_GBL_IFS_SLOT, val); } static void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) { u32 val = ath9k_hw_mac_to_clks(ah, us); val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_ACK)); REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, val); } static void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) { u32 val = ath9k_hw_mac_to_clks(ah, us); val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_CTS)); REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_CTS, val); } static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) { if (tu > 0xFFFF) { ath_dbg(ath9k_hw_common(ah), XMIT, "bad global tx timeout %u\n", tu); ah->globaltxtimeout = (u32) -1; return false; } else { REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu); ah->globaltxtimeout = tu; return true; } } void ath9k_hw_init_global_settings(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; const struct ath9k_channel *chan = ah->curchan; int acktimeout, ctstimeout, ack_offset = 0; int slottime; int sifstime; int rx_lat = 0, tx_lat = 0, eifs = 0; u32 reg; ath_dbg(ath9k_hw_common(ah), RESET, "ah->misc_mode 0x%x\n", ah->misc_mode); if (!chan) return; if (ah->misc_mode != 0) REG_SET_BIT(ah, AR_PCU_MISC, ah->misc_mode); if (IS_CHAN_A_FAST_CLOCK(ah, chan)) rx_lat = 41; else rx_lat = 37; tx_lat = 54; if (IS_CHAN_5GHZ(chan)) sifstime = 16; else sifstime = 10; if (IS_CHAN_HALF_RATE(chan)) { eifs = 175; rx_lat *= 2; tx_lat *= 2; if (IS_CHAN_A_FAST_CLOCK(ah, chan)) tx_lat += 11; sifstime *= 2; ack_offset = 16; slottime = 13; } else if (IS_CHAN_QUARTER_RATE(chan)) { eifs = 340; rx_lat = (rx_lat * 4) - 1; tx_lat *= 4; if (IS_CHAN_A_FAST_CLOCK(ah, chan)) tx_lat += 22; sifstime *= 4; ack_offset = 32; slottime = 21; } else { if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) { eifs = AR_D_GBL_IFS_EIFS_ASYNC_FIFO; reg = AR_USEC_ASYNC_FIFO; } else { eifs = REG_READ(ah, AR_D_GBL_IFS_EIFS)/ common->clockrate; reg = REG_READ(ah, AR_USEC); } rx_lat = MS(reg, AR_USEC_RX_LAT); tx_lat = MS(reg, AR_USEC_TX_LAT); slottime = ah->slottime; } /* As defined by IEEE 802.11-2007 17.3.8.6 */ acktimeout = slottime + sifstime + 3 * ah->coverage_class + ack_offset; ctstimeout = acktimeout; /* * Workaround for early ACK timeouts, add an offset to match the * initval's 64us ack timeout value. Use 48us for the CTS timeout. * This was initially only meant to work around an issue with delayed * BA frames in some implementations, but it has been found to fix ACK * timeout issues in other cases as well. */ if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ && !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) { acktimeout += 64 - sifstime - ah->slottime; ctstimeout += 48 - sifstime - ah->slottime; } ath9k_hw_set_sifs_time(ah, sifstime); ath9k_hw_setslottime(ah, slottime); ath9k_hw_set_ack_timeout(ah, acktimeout); ath9k_hw_set_cts_timeout(ah, ctstimeout); if (ah->globaltxtimeout != (u32) -1) ath9k_hw_set_global_txtimeout(ah, ah->globaltxtimeout); REG_WRITE(ah, AR_D_GBL_IFS_EIFS, ath9k_hw_mac_to_clks(ah, eifs)); REG_RMW(ah, AR_USEC, (common->clockrate - 1) | SM(rx_lat, AR_USEC_RX_LAT) | SM(tx_lat, AR_USEC_TX_LAT), AR_USEC_TX_LAT | AR_USEC_RX_LAT | AR_USEC_USEC); } EXPORT_SYMBOL(ath9k_hw_init_global_settings); void ath9k_hw_deinit(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); if (common->state < ATH_HW_INITIALIZED) goto free_hw; ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); free_hw: ath9k_hw_rf_free_ext_banks(ah); } EXPORT_SYMBOL(ath9k_hw_deinit); /*******/ /* INI */ /*******/ u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan) { u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band); if (IS_CHAN_B(chan)) ctl |= CTL_11B; else if (IS_CHAN_G(chan)) ctl |= CTL_11G; else ctl |= CTL_11A; return ctl; } /****************************************/ /* Reset and Channel Switching Routines */ /****************************************/ static inline void ath9k_hw_set_dma(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); ENABLE_REGWRITE_BUFFER(ah); /* * set AHB_MODE not to do cacheline prefetches */ if (!AR_SREV_9300_20_OR_LATER(ah)) REG_SET_BIT(ah, AR_AHB_MODE, AR_AHB_PREFETCH_RD_EN); /* * let mac dma reads be in 128 byte chunks */ REG_RMW(ah, AR_TXCFG, AR_TXCFG_DMASZ_128B, AR_TXCFG_DMASZ_MASK); REGWRITE_BUFFER_FLUSH(ah); /* * Restore TX Trigger Level to its pre-reset value. * The initial value depends on whether aggregation is enabled, and is * adjusted whenever underruns are detected. */ if (!AR_SREV_9300_20_OR_LATER(ah)) REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, ah->tx_trig_level); ENABLE_REGWRITE_BUFFER(ah); /* * let mac dma writes be in 128 byte chunks */ REG_RMW(ah, AR_RXCFG, AR_RXCFG_DMASZ_128B, AR_RXCFG_DMASZ_MASK); /* * Setup receive FIFO threshold to hold off TX activities */ REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); if (AR_SREV_9300_20_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_HP, 0x1); REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_LP, 0x1); ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize - ah->caps.rx_status_len); } /* * reduce the number of usable entries in PCU TXBUF to avoid * wrap around issues. */ if (AR_SREV_9285(ah)) { /* For AR9285 the number of Fifos are reduced to half. * So set the usable tx buf size also to half to * avoid data/delimiter underruns */ REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE); } else if (!AR_SREV_9271(ah)) { REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); } REGWRITE_BUFFER_FLUSH(ah); if (AR_SREV_9300_20_OR_LATER(ah)) ath9k_hw_reset_txstatus_ring(ah); } static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) { u32 mask = AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC; u32 set = AR_STA_ID1_KSRCH_MODE; switch (opmode) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: set |= AR_STA_ID1_ADHOC; REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; /* fall through */ case NL80211_IFTYPE_STATION: REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; default: if (!ah->is_monitoring) set = 0; break; } REG_RMW(ah, AR_STA_ID1, set, mask); } void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, u32 *coef_mantissa, u32 *coef_exponent) { u32 coef_exp, coef_man; for (coef_exp = 31; coef_exp > 0; coef_exp--) if ((coef_scaled >> coef_exp) & 0x1) break; coef_exp = 14 - (coef_exp - COEF_SCALE_S); coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); *coef_exponent = coef_exp - 16; } static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) { u32 rst_flags; u32 tmpReg; if (AR_SREV_9100(ah)) { REG_RMW_FIELD(ah, AR_RTC_DERIVED_CLK, AR_RTC_DERIVED_CLK_PERIOD, 1); (void)REG_READ(ah, AR_RTC_DERIVED_CLK); } ENABLE_REGWRITE_BUFFER(ah); if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_WA, ah->WARegVal); udelay(10); } REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); if (AR_SREV_9100(ah)) { rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD | AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET; } else { tmpReg = REG_READ(ah, AR_INTR_SYNC_CAUSE); if (tmpReg & (AR_INTR_SYNC_LOCAL_TIMEOUT | AR_INTR_SYNC_RADM_CPL_TIMEOUT)) { u32 val; REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); val = AR_RC_HOSTIF; if (!AR_SREV_9300_20_OR_LATER(ah)) val |= AR_RC_AHB; REG_WRITE(ah, AR_RC, val); } else if (!AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_RC, AR_RC_AHB); rst_flags = AR_RTC_RC_MAC_WARM; if (type == ATH9K_RESET_COLD) rst_flags |= AR_RTC_RC_MAC_COLD; } if (AR_SREV_9330(ah)) { int npend = 0; int i; /* AR9330 WAR: * call external reset function to reset WMAC if: * - doing a cold reset * - we have pending frames in the TX queues */ for (i = 0; i < AR_NUM_QCU; i++) { npend = ath9k_hw_numtxpending(ah, i); if (npend) break; } if (ah->external_reset && (npend || type == ATH9K_RESET_COLD)) { int reset_err = 0; ath_dbg(ath9k_hw_common(ah), RESET, "reset MAC via external reset\n"); reset_err = ah->external_reset(); if (reset_err) { ath_err(ath9k_hw_common(ah), "External reset failed, err=%d\n", reset_err); return false; } REG_WRITE(ah, AR_RTC_RESET, 1); } } if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_check_gpm_offset(ah); REG_WRITE(ah, AR_RTC_RC, rst_flags); REGWRITE_BUFFER_FLUSH(ah); udelay(50); REG_WRITE(ah, AR_RTC_RC, 0); if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) { ath_dbg(ath9k_hw_common(ah), RESET, "RTC stuck in MAC reset\n"); return false; } if (!AR_SREV_9100(ah)) REG_WRITE(ah, AR_RC, 0); if (AR_SREV_9100(ah)) udelay(50); return true; } static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) { ENABLE_REGWRITE_BUFFER(ah); if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_WA, ah->WARegVal); udelay(10); } REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_RC, AR_RC_AHB); REG_WRITE(ah, AR_RTC_RESET, 0); REGWRITE_BUFFER_FLUSH(ah); if (!AR_SREV_9300_20_OR_LATER(ah)) udelay(2); if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_RC, 0); REG_WRITE(ah, AR_RTC_RESET, 1); if (!ath9k_hw_wait(ah, AR_RTC_STATUS, AR_RTC_STATUS_M, AR_RTC_STATUS_ON, AH_WAIT_TIMEOUT)) { ath_dbg(ath9k_hw_common(ah), RESET, "RTC not waking up\n"); return false; } return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM); } static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type) { bool ret = false; if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_WA, ah->WARegVal); udelay(10); } REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); switch (type) { case ATH9K_RESET_POWER_ON: ret = ath9k_hw_set_reset_power_on(ah); break; case ATH9K_RESET_WARM: case ATH9K_RESET_COLD: ret = ath9k_hw_set_reset(ah, type); break; default: break; } return ret; } static bool ath9k_hw_chip_reset(struct ath_hw *ah, struct ath9k_channel *chan) { int reset_type = ATH9K_RESET_WARM; if (AR_SREV_9280(ah)) { if (ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) reset_type = ATH9K_RESET_POWER_ON; else reset_type = ATH9K_RESET_COLD; } if (!ath9k_hw_set_reset_reg(ah, reset_type)) return false; if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return false; ah->chip_fullsleep = false; if (AR_SREV_9330(ah)) ar9003_hw_internal_regulator_apply(ah); ath9k_hw_init_pll(ah, chan); ath9k_hw_set_rfmode(ah, chan); return true; } static bool ath9k_hw_channel_change(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); u32 qnum; int r; bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); bool band_switch, mode_diff; u8 ini_reloaded; band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) != (ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)); mode_diff = (chan->chanmode != ah->curchan->chanmode); for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { if (ath9k_hw_numtxpending(ah, qnum)) { ath_dbg(common, QUEUE, "Transmit frames pending on queue %d\n", qnum); return false; } } if (!ath9k_hw_rfbus_req(ah)) { ath_err(common, "Could not kill baseband RX\n"); return false; } if (edma && (band_switch || mode_diff)) { ath9k_hw_mark_phy_inactive(ah); udelay(5); ath9k_hw_init_pll(ah, NULL); if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) { ath_err(common, "Failed to do fast channel change\n"); return false; } } ath9k_hw_set_channel_regs(ah, chan); r = ath9k_hw_rf_set_freq(ah, chan); if (r) { ath_err(common, "Failed to set channel\n"); return false; } ath9k_hw_set_clockrate(ah); ath9k_hw_apply_txpower(ah, chan, false); ath9k_hw_rfbus_done(ah); if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) ath9k_hw_set_delta_slope(ah, chan); ath9k_hw_spur_mitigate_freq(ah, chan); if (edma && (band_switch || mode_diff)) { ah->ah_flags |= AH_FASTCC; if (band_switch || ini_reloaded) ah->eep_ops->set_board_values(ah, chan); ath9k_hw_init_bb(ah, chan); if (band_switch || ini_reloaded) ath9k_hw_init_cal(ah, chan); ah->ah_flags &= ~AH_FASTCC; } return true; } static void ath9k_hw_apply_gpio_override(struct ath_hw *ah) { u32 gpio_mask = ah->gpio_mask; int i; for (i = 0; gpio_mask; i++, gpio_mask >>= 1) { if (!(gpio_mask & 1)) continue; ath9k_hw_cfg_output(ah, i, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ath9k_hw_set_gpio(ah, i, !!(ah->gpio_val & BIT(i))); } } static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states, int *hang_state, int *hang_pos) { static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */ u32 chain_state, dcs_pos, i; for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) { chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f; for (i = 0; i < 3; i++) { if (chain_state == dcu_chain_state[i]) { *hang_state = chain_state; *hang_pos = dcs_pos; return true; } } } return false; } #define DCU_COMPLETE_STATE 1 #define DCU_COMPLETE_STATE_MASK 0x3 #define NUM_STATUS_READS 50 static bool ath9k_hw_detect_mac_hang(struct ath_hw *ah) { u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4; u32 i, hang_pos, hang_state, num_state = 6; comp_state = REG_READ(ah, AR_DMADBG_6); if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) { ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature not found at DCU complete\n"); return false; } chain_state = REG_READ(ah, dcs_reg); if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos)) goto hang_check_iter; dcs_reg = AR_DMADBG_5; num_state = 4; chain_state = REG_READ(ah, dcs_reg); if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos)) goto hang_check_iter; ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 not found\n"); return false; hang_check_iter: ath_dbg(ath9k_hw_common(ah), RESET, "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n", chain_state, comp_state, hang_state, hang_pos); for (i = 0; i < NUM_STATUS_READS; i++) { chain_state = REG_READ(ah, dcs_reg); chain_state = (chain_state >> (5 * hang_pos)) & 0x1f; comp_state = REG_READ(ah, AR_DMADBG_6); if (((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) || (chain_state != hang_state)) return false; } ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n"); return true; } bool ath9k_hw_check_alive(struct ath_hw *ah) { int count = 50; u32 reg; if (AR_SREV_9300(ah)) return !ath9k_hw_detect_mac_hang(ah); if (AR_SREV_9285_12_OR_LATER(ah)) return true; do { reg = REG_READ(ah, AR_OBS_BUS_1); if ((reg & 0x7E7FFFEF) == 0x00702400) continue; switch (reg & 0x7E000B00) { case 0x1E000000: case 0x52000B00: case 0x18000B00: continue; default: return true; } } while (count-- > 0); return false; } EXPORT_SYMBOL(ath9k_hw_check_alive); /* * Fast channel change: * (Change synthesizer based on channel freq without resetting chip) * * Don't do FCC when * - Flag is not set * - Chip is just coming out of full sleep * - Channel to be set is same as current channel * - Channel flags are different, (eg.,moving from 2GHz to 5GHz channel) */ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); int ret; if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI) goto fail; if (ah->chip_fullsleep) goto fail; if (!ah->curchan) goto fail; if (chan->channel == ah->curchan->channel) goto fail; if ((ah->curchan->channelFlags | chan->channelFlags) & (CHANNEL_HALF | CHANNEL_QUARTER)) goto fail; if ((chan->channelFlags & CHANNEL_ALL) != (ah->curchan->channelFlags & CHANNEL_ALL)) goto fail; if (!ath9k_hw_check_alive(ah)) goto fail; /* * For AR9462, make sure that calibration data for * re-using are present. */ if (AR_SREV_9462(ah) && (ah->caldata && (!ah->caldata->done_txiqcal_once || !ah->caldata->done_txclcal_once || !ah->caldata->rtt_done))) goto fail; ath_dbg(common, RESET, "FastChannelChange for %d -> %d\n", ah->curchan->channel, chan->channel); ret = ath9k_hw_channel_change(ah, chan); if (!ret) goto fail; ath9k_hw_loadnf(ah, ah->curchan); ath9k_hw_start_nfcal(ah, true); if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_2g5g_switch(ah, false); if (AR_SREV_9271(ah)) ar9002_hw_load_ani_reg(ah, chan); return 0; fail: return -EINVAL; } int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata, bool fastcc) { struct ath_common *common = ath9k_hw_common(ah); u32 saveLedState; u32 saveDefAntenna; u32 macStaId1; u64 tsf = 0; int i, r; bool start_mci_reset = false; bool save_fullsleep = ah->chip_fullsleep; if (ath9k_hw_mci_is_enabled(ah)) { start_mci_reset = ar9003_mci_start_reset(ah, chan); if (start_mci_reset) return 0; } if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; if (ah->curchan && !ah->chip_fullsleep) ath9k_hw_getnf(ah, ah->curchan); ah->caldata = caldata; if (caldata && (chan->channel != caldata->channel || (chan->channelFlags & ~CHANNEL_CW_INT) != (caldata->channelFlags & ~CHANNEL_CW_INT))) { /* Operating channel changed, reset channel calibration data */ memset(caldata, 0, sizeof(*caldata)); ath9k_init_nfcal_hist_buffer(ah, chan); } else if (caldata) { caldata->paprd_packet_sent = false; } ah->noise = ath9k_hw_getchan_noise(ah, chan); if (fastcc) { r = ath9k_hw_do_fastcc(ah, chan); if (!r) return r; } if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_stop_bt(ah, save_fullsleep); saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA); if (saveDefAntenna == 0) saveDefAntenna = 1; macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; /* For chips on which RTC reset is done, save TSF before it gets cleared */ if (AR_SREV_9100(ah) || (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))) tsf = ath9k_hw_gettsf64(ah); saveLedState = REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); ath9k_hw_mark_phy_inactive(ah); ah->paprd_table_write_done = false; /* Only required on the first reset */ if (AR_SREV_9271(ah) && ah->htc_reset_init) { REG_WRITE(ah, AR9271_RESET_POWER_DOWN_CONTROL, AR9271_RADIO_RF_RST); udelay(50); } if (!ath9k_hw_chip_reset(ah, chan)) { ath_err(common, "Chip reset failed\n"); return -EINVAL; } /* Only required on the first reset */ if (AR_SREV_9271(ah) && ah->htc_reset_init) { ah->htc_reset_init = false; REG_WRITE(ah, AR9271_RESET_POWER_DOWN_CONTROL, AR9271_GATE_MAC_CTL); udelay(50); } /* Restore TSF */ if (tsf) ath9k_hw_settsf64(ah, tsf); if (AR_SREV_9280_20_OR_LATER(ah)) REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); if (!AR_SREV_9300_20_OR_LATER(ah)) ar9002_hw_enable_async_fifo(ah); r = ath9k_hw_process_ini(ah, chan); if (r) return r; if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep); /* * Some AR91xx SoC devices frequently fail to accept TSF writes * right after the chip reset. When that happens, write a new * value after the initvals have been applied, with an offset * based on measured time difference */ if (AR_SREV_9100(ah) && (ath9k_hw_gettsf64(ah) < tsf)) { tsf += 1500; ath9k_hw_settsf64(ah, tsf); } /* Setup MFP options for CCMP */ if (AR_SREV_9280_20_OR_LATER(ah)) { /* Mask Retry(b11), PwrMgt(b12), MoreData(b13) to 0 in mgmt * frames when constructing CCMP AAD. */ REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT, 0xc7ff); ah->sw_mgmt_crypto = false; } else if (AR_SREV_9160_10_OR_LATER(ah)) { /* Disable hardware crypto for management frames */ REG_CLR_BIT(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE); REG_SET_BIT(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT); ah->sw_mgmt_crypto = true; } else ah->sw_mgmt_crypto = true; if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) ath9k_hw_set_delta_slope(ah, chan); ath9k_hw_spur_mitigate_freq(ah, chan); ah->eep_ops->set_board_values(ah, chan); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(common->macaddr)); REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(common->macaddr + 4) | macStaId1 | AR_STA_ID1_RTS_USE_DEF | (ah->config. ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0) | ah->sta_id1_defaults); ath_hw_setbssidmask(common); REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); ath9k_hw_write_associd(ah); REG_WRITE(ah, AR_ISR, ~0); REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); REGWRITE_BUFFER_FLUSH(ah); ath9k_hw_set_operating_mode(ah, ah->opmode); r = ath9k_hw_rf_set_freq(ah, chan); if (r) return r; ath9k_hw_set_clockrate(ah); ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < AR_NUM_DCU; i++) REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); REGWRITE_BUFFER_FLUSH(ah); ah->intr_txqs = 0; for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) ath9k_hw_resettxqueue(ah, i); ath9k_hw_init_interrupt_masks(ah, ah->opmode); ath9k_hw_ani_cache_ini_regs(ah); ath9k_hw_init_qos(ah); if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) ath9k_hw_cfg_gpio_input(ah, ah->rfkill_gpio); ath9k_hw_init_global_settings(ah); if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) { REG_SET_BIT(ah, AR_MAC_PCU_LOGIC_ANALYZER, AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768); REG_RMW_FIELD(ah, AR_AHB_MODE, AR_AHB_CUSTOM_BURST_EN, AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL); REG_SET_BIT(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_ENABLE_AGGWEP); } REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); ath9k_hw_set_dma(ah); if (!ath9k_hw_mci_is_enabled(ah)) REG_WRITE(ah, AR_OBS, 8); if (ah->config.rx_intr_mitigation) { REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000); } if (ah->config.tx_intr_mitigation) { REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300); REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750); } ath9k_hw_init_bb(ah, chan); if (caldata) { caldata->done_txiqcal_once = false; caldata->done_txclcal_once = false; } if (!ath9k_hw_init_cal(ah, chan)) return -EIO; if (ath9k_hw_mci_is_enabled(ah) && ar9003_mci_end_reset(ah, chan, caldata)) return -EIO; ENABLE_REGWRITE_BUFFER(ah); ath9k_hw_restore_chainmask(ah); REG_WRITE(ah, AR_CFG_LED, saveLedState | AR_CFG_SCLK_32KHZ); REGWRITE_BUFFER_FLUSH(ah); /* * For big endian systems turn on swapping for descriptors */ if (AR_SREV_9100(ah)) { u32 mask; mask = REG_READ(ah, AR_CFG); if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) { ath_dbg(common, RESET, "CFG Byte Swap Set 0x%x\n", mask); } else { mask = INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB; REG_WRITE(ah, AR_CFG, mask); ath_dbg(common, RESET, "Setting CFG 0x%x\n", REG_READ(ah, AR_CFG)); } } else { if (common->bus_ops->ath_bus_type == ATH_USB) { /* Configure AR9271 target WLAN */ if (AR_SREV_9271(ah)) REG_WRITE(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB); else REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); } #ifdef __BIG_ENDIAN else if (AR_SREV_9330(ah) || AR_SREV_9340(ah) || AR_SREV_9550(ah)) REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0); else REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); #endif } if (ath9k_hw_btcoex_is_enabled(ah)) ath9k_hw_btcoex_enable(ah); if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_check_bt(ah); ath9k_hw_loadnf(ah, chan); ath9k_hw_start_nfcal(ah, true); if (AR_SREV_9300_20_OR_LATER(ah)) { ar9003_hw_bb_watchdog_config(ah); ar9003_hw_disable_phy_restart(ah); } ath9k_hw_apply_gpio_override(ah); return 0; } EXPORT_SYMBOL(ath9k_hw_reset); /******************************/ /* Power Management (Chipset) */ /******************************/ /* * Notify Power Mgt is disabled in self-generated frames. * If requested, force chip to sleep. */ static void ath9k_set_power_sleep(struct ath_hw *ah) { REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_CLR_BIT(ah, AR_TIMER_MODE, 0xff); REG_CLR_BIT(ah, AR_NDP2_TIMER_MODE, 0xff); REG_CLR_BIT(ah, AR_SLP32_INC, 0xfffff); /* xxx Required for WLAN only case ? */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0); udelay(100); } /* * Clear the RTC force wake bit to allow the * mac to go to sleep. */ REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); if (ath9k_hw_mci_is_enabled(ah)) udelay(100); if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); /* Shutdown chip. Active low */ if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) { REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN); udelay(2); } /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */ if (AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE); } /* * Notify Power Management is enabled in self-generating * frames. If request, set power mode of chip to * auto/normal. Duration in units of 128us (1/8 TU). */ static void ath9k_set_power_network_sleep(struct ath_hw *ah) { struct ath9k_hw_capabilities *pCap = &ah->caps; REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { /* Set WakeOnInterrupt bit; clear ForceWake bit */ REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); } else { /* When chip goes into network sleep, it could be waken * up by MCI_INT interrupt caused by BT's HW messages * (LNA_xxx, CONT_xxx) which chould be in a very fast * rate (~100us). This will cause chip to leave and * re-enter network sleep mode frequently, which in * consequence will have WLAN MCI HW to generate lots of * SYS_WAKING and SYS_SLEEPING messages which will make * BT CPU to busy to process. */ if (ath9k_hw_mci_is_enabled(ah)) REG_CLR_BIT(ah, AR_MCI_INTERRUPT_RX_MSG_EN, AR_MCI_INTERRUPT_RX_HW_MSG_MASK); /* * Clear the RTC force wake bit to allow the * mac to go to sleep. */ REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); if (ath9k_hw_mci_is_enabled(ah)) udelay(30); } /* Clear Bit 14 of AR_WA after putting chip into Net Sleep mode. */ if (AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE); } static bool ath9k_hw_set_power_awake(struct ath_hw *ah) { u32 val; int i; /* Set Bits 14 and 17 of AR_WA before powering on the chip. */ if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_WA, ah->WARegVal); udelay(10); } if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) { if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { return false; } if (!AR_SREV_9300_20_OR_LATER(ah)) ath9k_hw_init_pll(ah, NULL); } if (AR_SREV_9100(ah)) REG_SET_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN); REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); udelay(50); if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_set_power_awake(ah); for (i = POWER_UP_TIME / 50; i > 0; i--) { val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; if (val == AR_RTC_STATUS_ON) break; udelay(50); REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); } if (i == 0) { ath_err(ath9k_hw_common(ah), "Failed to wakeup in %uus\n", POWER_UP_TIME / 20); return false; } REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); return true; } bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) { struct ath_common *common = ath9k_hw_common(ah); int status = true; static const char *modes[] = { "AWAKE", "FULL-SLEEP", "NETWORK SLEEP", "UNDEFINED" }; if (ah->power_mode == mode) return status; ath_dbg(common, RESET, "%s -> %s\n", modes[ah->power_mode], modes[mode]); switch (mode) { case ATH9K_PM_AWAKE: status = ath9k_hw_set_power_awake(ah); break; case ATH9K_PM_FULL_SLEEP: if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_set_full_sleep(ah); ath9k_set_power_sleep(ah); ah->chip_fullsleep = true; break; case ATH9K_PM_NETWORK_SLEEP: ath9k_set_power_network_sleep(ah); break; default: ath_err(common, "Unknown power mode %u\n", mode); return false; } ah->power_mode = mode; /* * XXX: If this warning never comes up after a while then * simply keep the ATH_DBG_WARN_ON_ONCE() but make * ath9k_hw_setpower() return type void. */ if (!(ah->ah_flags & AH_UNPLUGGED)) ATH_DBG_WARN_ON_ONCE(!status); return status; } EXPORT_SYMBOL(ath9k_hw_setpower); /*******************/ /* Beacon Handling */ /*******************/ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) { int flags = 0; ENABLE_REGWRITE_BUFFER(ah); switch (ah->opmode) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon + TU_TO_USEC(ah->atim_window ? ah->atim_window : 1)); flags |= AR_NDP_TIMER_EN; case NL80211_IFTYPE_AP: REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon - TU_TO_USEC(ah->config.dma_beacon_response_time)); REG_WRITE(ah, AR_NEXT_SWBA, next_beacon - TU_TO_USEC(ah->config.sw_beacon_response_time)); flags |= AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; break; default: ath_dbg(ath9k_hw_common(ah), BEACON, "%s: unsupported opmode: %d\n", __func__, ah->opmode); return; break; } REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period); REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period); REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period); REG_WRITE(ah, AR_NDP_PERIOD, beacon_period); REGWRITE_BUFFER_FLUSH(ah); REG_SET_BIT(ah, AR_TIMER_MODE, flags); } EXPORT_SYMBOL(ath9k_hw_beaconinit); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs) { u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_common *common = ath9k_hw_common(ah); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); REG_WRITE(ah, AR_BEACON_PERIOD, TU_TO_USEC(bs->bs_intval)); REG_WRITE(ah, AR_DMA_BEACON_PERIOD, TU_TO_USEC(bs->bs_intval)); REGWRITE_BUFFER_FLUSH(ah); REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold); beaconintval = bs->bs_intval; if (bs->bs_sleepduration > beaconintval) beaconintval = bs->bs_sleepduration; dtimperiod = bs->bs_dtimperiod; if (bs->bs_sleepduration > dtimperiod) dtimperiod = bs->bs_sleepduration; if (beaconintval == dtimperiod) nextTbtt = bs->bs_nextdtim; else nextTbtt = bs->bs_nexttbtt; ath_dbg(common, BEACON, "next DTIM %d\n", bs->bs_nextdtim); ath_dbg(common, BEACON, "next beacon %d\n", nextTbtt); ath_dbg(common, BEACON, "beacon period %d\n", beaconintval); ath_dbg(common, BEACON, "DTIM period %d\n", dtimperiod); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_NEXT_DTIM, TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP)); REG_WRITE(ah, AR_SLEEP1, SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT) | AR_SLEEP1_ASSUME_DTIM); if (pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP) beacontimeout = (BEACON_TIMEOUT_VAL << 3); else beacontimeout = MIN_BEACON_TIMEOUT_VAL; REG_WRITE(ah, AR_SLEEP2, SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT)); REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval)); REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod)); REGWRITE_BUFFER_FLUSH(ah); REG_SET_BIT(ah, AR_TIMER_MODE, AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN | AR_DTIM_TIMER_EN); /* TSF Out of Range Threshold */ REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold); } EXPORT_SYMBOL(ath9k_hw_set_sta_beacon_timers); /*******************/ /* HW Capabilities */ /*******************/ static u8 fixup_chainmask(u8 chip_chainmask, u8 eeprom_chainmask) { eeprom_chainmask &= chip_chainmask; if (eeprom_chainmask) return eeprom_chainmask; else return chip_chainmask; } /** * ath9k_hw_dfs_tested - checks if DFS has been tested with used chipset * @ah: the atheros hardware data structure * * We enable DFS support upstream on chipsets which have passed a series * of tests. The testing requirements are going to be documented. Desired * test requirements are documented at: * * http://wireless.kernel.org/en/users/Drivers/ath9k/dfs * * Once a new chipset gets properly tested an individual commit can be used * to document the testing for DFS for that chipset. */ static bool ath9k_hw_dfs_tested(struct ath_hw *ah) { switch (ah->hw_version.macVersion) { /* AR9580 will likely be our first target to get testing on */ case AR_SREV_VERSION_9580: default: return false; } } int ath9k_hw_fill_cap_info(struct ath_hw *ah) { struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ath_common *common = ath9k_hw_common(ah); unsigned int chip_chainmask; u16 eeval; u8 ant_div_ctl1, tx_chainmask, rx_chainmask; eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0); regulatory->current_rd = eeval; if (ah->opmode != NL80211_IFTYPE_AP && ah->hw_version.subvendorid == AR_SUBVENDOR_ID_NEW_A) { if (regulatory->current_rd == 0x64 || regulatory->current_rd == 0x65) regulatory->current_rd += 5; else if (regulatory->current_rd == 0x41) regulatory->current_rd = 0x43; ath_dbg(common, REGULATORY, "regdomain mapped to 0x%x\n", regulatory->current_rd); } eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) { ath_err(common, "no band has been marked as supported in EEPROM\n"); return -EINVAL; } if (eeval & AR5416_OPFLAGS_11A) pCap->hw_caps |= ATH9K_HW_CAP_5GHZ; if (eeval & AR5416_OPFLAGS_11G) pCap->hw_caps |= ATH9K_HW_CAP_2GHZ; if (AR_SREV_9485(ah) || AR_SREV_9285(ah) || AR_SREV_9330(ah) || AR_SREV_9565(ah)) chip_chainmask = 1; else if (AR_SREV_9462(ah)) chip_chainmask = 3; else if (!AR_SREV_9280_20_OR_LATER(ah)) chip_chainmask = 7; else if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9340(ah)) chip_chainmask = 3; else chip_chainmask = 7; pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK); /* * For AR9271 we will temporarilly uses the rx chainmax as read from * the EEPROM. */ if ((ah->hw_version.devid == AR5416_DEVID_PCI) && !(eeval & AR5416_OPFLAGS_11A) && !(AR_SREV_9271(ah))) /* CB71: GPIO 0 is pulled down to indicate 3 rx chains */ pCap->rx_chainmask = ath9k_hw_gpio_get(ah, 0) ? 0x5 : 0x7; else if (AR_SREV_9100(ah)) pCap->rx_chainmask = 0x7; else /* Use rx_chainmask from EEPROM. */ pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK); pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask); pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask); ah->txchainmask = pCap->tx_chainmask; ah->rxchainmask = pCap->rx_chainmask; ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; /* enable key search for every frame in an aggregate */ if (AR_SREV_9300_20_OR_LATER(ah)) ah->misc_mode |= AR_PCU_ALWAYS_PERFORM_KEYSEARCH; common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM; if (ah->hw_version.devid != AR2427_DEVID_PCIE) pCap->hw_caps |= ATH9K_HW_CAP_HT; else pCap->hw_caps &= ~ATH9K_HW_CAP_HT; if (AR_SREV_9271(ah)) pCap->num_gpio_pins = AR9271_NUM_GPIO; else if (AR_DEVID_7010(ah)) pCap->num_gpio_pins = AR7010_NUM_GPIO; else if (AR_SREV_9300_20_OR_LATER(ah)) pCap->num_gpio_pins = AR9300_NUM_GPIO; else if (AR_SREV_9287_11_OR_LATER(ah)) pCap->num_gpio_pins = AR9287_NUM_GPIO; else if (AR_SREV_9285_12_OR_LATER(ah)) pCap->num_gpio_pins = AR9285_NUM_GPIO; else if (AR_SREV_9280_20_OR_LATER(ah)) pCap->num_gpio_pins = AR928X_NUM_GPIO; else pCap->num_gpio_pins = AR_NUM_GPIO; if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX; else pCap->rts_aggr_limit = (8 * 1024); #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT); if (ah->rfsilent & EEP_RFSILENT_ENABLED) { ah->rfkill_gpio = MS(ah->rfsilent, EEP_RFSILENT_GPIO_SEL); ah->rfkill_polarity = MS(ah->rfsilent, EEP_RFSILENT_POLARITY); pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT; } #endif if (AR_SREV_9271(ah) || AR_SREV_9300_20_OR_LATER(ah)) pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP; else pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP; if (AR_SREV_9280(ah) || AR_SREV_9285(ah)) pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS; else pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS; if (AR_SREV_9300_20_OR_LATER(ah)) { pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_FASTCLOCK; if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah) && !AR_SREV_9565(ah)) pCap->hw_caps |= ATH9K_HW_CAP_LDPC; pCap->rx_hp_qdepth = ATH9K_HW_RX_HP_QDEPTH; pCap->rx_lp_qdepth = ATH9K_HW_RX_LP_QDEPTH; pCap->rx_status_len = sizeof(struct ar9003_rxs); pCap->tx_desc_len = sizeof(struct ar9003_txc); pCap->txs_len = sizeof(struct ar9003_txs); } else { pCap->tx_desc_len = sizeof(struct ath_desc); if (AR_SREV_9280_20(ah)) pCap->hw_caps |= ATH9K_HW_CAP_FASTCLOCK; } if (AR_SREV_9300_20_OR_LATER(ah)) pCap->hw_caps |= ATH9K_HW_CAP_RAC_SUPPORTED; if (AR_SREV_9300_20_OR_LATER(ah)) ah->ent_mode = REG_READ(ah, AR_ENT_OTP); if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah)) pCap->hw_caps |= ATH9K_HW_CAP_SGI_20; if (AR_SREV_9285(ah)) if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) { ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB; } if (AR_SREV_9300_20_OR_LATER(ah)) { if (ah->eep_ops->get_eeprom(ah, EEP_CHAIN_MASK_REDUCE)) pCap->hw_caps |= ATH9K_HW_CAP_APM; } if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) { ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); /* * enable the diversity-combining algorithm only when * both enable_lna_div and enable_fast_div are set * Table for Diversity * ant_div_alt_lnaconf bit 0-1 * ant_div_main_lnaconf bit 2-3 * ant_div_alt_gaintb bit 4 * ant_div_main_gaintb bit 5 * enable_ant_div_lnadiv bit 6 * enable_ant_fast_div bit 7 */ if ((ant_div_ctl1 >> 0x6) == 0x3) pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB; } if (AR_SREV_9485_10(ah)) { pCap->pcie_lcr_extsync_en = true; pCap->pcie_lcr_offset = 0x80; } if (ath9k_hw_dfs_tested(ah)) pCap->hw_caps |= ATH9K_HW_CAP_DFS; tx_chainmask = pCap->tx_chainmask; rx_chainmask = pCap->rx_chainmask; while (tx_chainmask || rx_chainmask) { if (tx_chainmask & BIT(0)) pCap->max_txchains++; if (rx_chainmask & BIT(0)) pCap->max_rxchains++; tx_chainmask >>= 1; rx_chainmask >>= 1; } if (AR_SREV_9300_20_OR_LATER(ah)) { ah->enabled_cals |= TX_IQ_CAL; if (AR_SREV_9485_OR_LATER(ah)) ah->enabled_cals |= TX_IQ_ON_AGC_CAL; } if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { if (!(ah->ent_mode & AR_ENT_OTP_49GHZ_DISABLE)) pCap->hw_caps |= ATH9K_HW_CAP_MCI; if (AR_SREV_9462_20(ah)) pCap->hw_caps |= ATH9K_HW_CAP_RTT; } if (AR_SREV_9280_20_OR_LATER(ah)) { pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE | ATH9K_HW_WOW_PATTERN_MATCH_EXACT; if (AR_SREV_9280(ah)) pCap->hw_caps |= ATH9K_HW_WOW_PATTERN_MATCH_DWORD; } return 0; } /****************************/ /* GPIO / RFKILL / Antennae */ /****************************/ static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah, u32 gpio, u32 type) { int addr; u32 gpio_shift, tmp; if (gpio > 11) addr = AR_GPIO_OUTPUT_MUX3; else if (gpio > 5) addr = AR_GPIO_OUTPUT_MUX2; else addr = AR_GPIO_OUTPUT_MUX1; gpio_shift = (gpio % 6) * 5; if (AR_SREV_9280_20_OR_LATER(ah) || (addr != AR_GPIO_OUTPUT_MUX1)) { REG_RMW(ah, addr, (type << gpio_shift), (0x1f << gpio_shift)); } else { tmp = REG_READ(ah, addr); tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0); tmp &= ~(0x1f << gpio_shift); tmp |= (type << gpio_shift); REG_WRITE(ah, addr, tmp); } } void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio) { u32 gpio_shift; BUG_ON(gpio >= ah->caps.num_gpio_pins); if (AR_DEVID_7010(ah)) { gpio_shift = gpio; REG_RMW(ah, AR7010_GPIO_OE, (AR7010_GPIO_OE_AS_INPUT << gpio_shift), (AR7010_GPIO_OE_MASK << gpio_shift)); return; } gpio_shift = gpio << 1; REG_RMW(ah, AR_GPIO_OE_OUT, (AR_GPIO_OE_OUT_DRV_NO << gpio_shift), (AR_GPIO_OE_OUT_DRV << gpio_shift)); } EXPORT_SYMBOL(ath9k_hw_cfg_gpio_input); u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) { #define MS_REG_READ(x, y) \ (MS(REG_READ(ah, AR_GPIO_IN_OUT), x##_GPIO_IN_VAL) & (AR_GPIO_BIT(y))) if (gpio >= ah->caps.num_gpio_pins) return 0xffffffff; if (AR_DEVID_7010(ah)) { u32 val; val = REG_READ(ah, AR7010_GPIO_IN); return (MS(val, AR7010_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) == 0; } else if (AR_SREV_9300_20_OR_LATER(ah)) return (MS(REG_READ(ah, AR_GPIO_IN), AR9300_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) != 0; else if (AR_SREV_9271(ah)) return MS_REG_READ(AR9271, gpio) != 0; else if (AR_SREV_9287_11_OR_LATER(ah)) return MS_REG_READ(AR9287, gpio) != 0; else if (AR_SREV_9285_12_OR_LATER(ah)) return MS_REG_READ(AR9285, gpio) != 0; else if (AR_SREV_9280_20_OR_LATER(ah)) return MS_REG_READ(AR928X, gpio) != 0; else return MS_REG_READ(AR, gpio) != 0; } EXPORT_SYMBOL(ath9k_hw_gpio_get); void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, u32 ah_signal_type) { u32 gpio_shift; if (AR_DEVID_7010(ah)) { gpio_shift = gpio; REG_RMW(ah, AR7010_GPIO_OE, (AR7010_GPIO_OE_AS_OUTPUT << gpio_shift), (AR7010_GPIO_OE_MASK << gpio_shift)); return; } ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type); gpio_shift = 2 * gpio; REG_RMW(ah, AR_GPIO_OE_OUT, (AR_GPIO_OE_OUT_DRV_ALL << gpio_shift), (AR_GPIO_OE_OUT_DRV << gpio_shift)); } EXPORT_SYMBOL(ath9k_hw_cfg_output); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) { if (AR_DEVID_7010(ah)) { val = val ? 0 : 1; REG_RMW(ah, AR7010_GPIO_OUT, ((val&1) << gpio), AR_GPIO_BIT(gpio)); return; } if (AR_SREV_9271(ah)) val = ~val; REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio), AR_GPIO_BIT(gpio)); } EXPORT_SYMBOL(ath9k_hw_set_gpio); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna) { REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); } EXPORT_SYMBOL(ath9k_hw_setantenna); /*********************/ /* General Operation */ /*********************/ u32 ath9k_hw_getrxfilter(struct ath_hw *ah) { u32 bits = REG_READ(ah, AR_RX_FILTER); u32 phybits = REG_READ(ah, AR_PHY_ERR); if (phybits & AR_PHY_ERR_RADAR) bits |= ATH9K_RX_FILTER_PHYRADAR; if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING)) bits |= ATH9K_RX_FILTER_PHYERR; return bits; } EXPORT_SYMBOL(ath9k_hw_getrxfilter); void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits) { u32 phybits; ENABLE_REGWRITE_BUFFER(ah); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) bits |= ATH9K_RX_FILTER_CONTROL_WRAPPER; REG_WRITE(ah, AR_RX_FILTER, bits); phybits = 0; if (bits & ATH9K_RX_FILTER_PHYRADAR) phybits |= AR_PHY_ERR_RADAR; if (bits & ATH9K_RX_FILTER_PHYERR) phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING; REG_WRITE(ah, AR_PHY_ERR, phybits); if (phybits) REG_SET_BIT(ah, AR_RXCFG, AR_RXCFG_ZLFDMA); else REG_CLR_BIT(ah, AR_RXCFG, AR_RXCFG_ZLFDMA); REGWRITE_BUFFER_FLUSH(ah); } EXPORT_SYMBOL(ath9k_hw_setrxfilter); bool ath9k_hw_phy_disable(struct ath_hw *ah) { if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_bt_gain_ctrl(ah); if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) return false; ath9k_hw_init_pll(ah, NULL); ah->htc_reset_init = true; return true; } EXPORT_SYMBOL(ath9k_hw_phy_disable); bool ath9k_hw_disable(struct ath_hw *ah) { if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return false; if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD)) return false; ath9k_hw_init_pll(ah, NULL); return true; } EXPORT_SYMBOL(ath9k_hw_disable); static int get_antenna_gain(struct ath_hw *ah, struct ath9k_channel *chan) { enum eeprom_param gain_param; if (IS_CHAN_2GHZ(chan)) gain_param = EEP_ANTENNA_GAIN_2G; else gain_param = EEP_ANTENNA_GAIN_5G; return ah->eep_ops->get_eeprom(ah, gain_param); } void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, bool test) { struct ath_regulatory *reg = ath9k_hw_regulatory(ah); struct ieee80211_channel *channel; int chan_pwr, new_pwr, max_gain; int ant_gain, ant_reduction = 0; if (!chan) return; channel = chan->chan; chan_pwr = min_t(int, channel->max_power * 2, MAX_RATE_POWER); new_pwr = min_t(int, chan_pwr, reg->power_limit); max_gain = chan_pwr - new_pwr + channel->max_antenna_gain * 2; ant_gain = get_antenna_gain(ah, chan); if (ant_gain > max_gain) ant_reduction = ant_gain - max_gain; ah->eep_ops->set_txpower(ah, chan, ath9k_regd_get_ctl(reg, chan), ant_reduction, new_pwr, test); } void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test) { struct ath_regulatory *reg = ath9k_hw_regulatory(ah); struct ath9k_channel *chan = ah->curchan; struct ieee80211_channel *channel = chan->chan; reg->power_limit = min_t(u32, limit, MAX_RATE_POWER); if (test) channel->max_power = MAX_RATE_POWER / 2; ath9k_hw_apply_txpower(ah, chan, test); if (test) channel->max_power = DIV_ROUND_UP(reg->max_power_level, 2); } EXPORT_SYMBOL(ath9k_hw_set_txpowerlimit); void ath9k_hw_setopmode(struct ath_hw *ah) { ath9k_hw_set_operating_mode(ah, ah->opmode); } EXPORT_SYMBOL(ath9k_hw_setopmode); void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1) { REG_WRITE(ah, AR_MCAST_FIL0, filter0); REG_WRITE(ah, AR_MCAST_FIL1, filter1); } EXPORT_SYMBOL(ath9k_hw_setmcastfilter); void ath9k_hw_write_associd(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(common->curbssid)); REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(common->curbssid + 4) | ((common->curaid & 0x3fff) << AR_BSS_ID1_AID_S)); } EXPORT_SYMBOL(ath9k_hw_write_associd); #define ATH9K_MAX_TSF_READ 10 u64 ath9k_hw_gettsf64(struct ath_hw *ah) { u32 tsf_lower, tsf_upper1, tsf_upper2; int i; tsf_upper1 = REG_READ(ah, AR_TSF_U32); for (i = 0; i < ATH9K_MAX_TSF_READ; i++) { tsf_lower = REG_READ(ah, AR_TSF_L32); tsf_upper2 = REG_READ(ah, AR_TSF_U32); if (tsf_upper2 == tsf_upper1) break; tsf_upper1 = tsf_upper2; } WARN_ON( i == ATH9K_MAX_TSF_READ ); return (((u64)tsf_upper1 << 32) | tsf_lower); } EXPORT_SYMBOL(ath9k_hw_gettsf64); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64) { REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff); REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff); } EXPORT_SYMBOL(ath9k_hw_settsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah) { if (!ath9k_hw_wait(ah, AR_SLP32_MODE, AR_SLP32_TSF_WRITE_STATUS, 0, AH_TSF_WRITE_TIMEOUT)) ath_dbg(ath9k_hw_common(ah), RESET, "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n"); REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); } EXPORT_SYMBOL(ath9k_hw_reset_tsf); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set) { if (set) ah->misc_mode |= AR_PCU_TX_ADD_TSF; else ah->misc_mode &= ~AR_PCU_TX_ADD_TSF; } EXPORT_SYMBOL(ath9k_hw_set_tsfadjust); void ath9k_hw_set11nmac2040(struct ath_hw *ah) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; u32 macmode; if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca) macmode = AR_2040_JOINED_RX_CLEAR; else macmode = 0; REG_WRITE(ah, AR_2040_MODE, macmode); } /* HW Generic timers configuration */ static const struct ath_gen_timer_configuration gen_tmr_configuration[] = { {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, {AR_NEXT_NDP2_TIMER, AR_NDP2_PERIOD, AR_NDP2_TIMER_MODE, 0x0001}, {AR_NEXT_NDP2_TIMER + 1*4, AR_NDP2_PERIOD + 1*4, AR_NDP2_TIMER_MODE, 0x0002}, {AR_NEXT_NDP2_TIMER + 2*4, AR_NDP2_PERIOD + 2*4, AR_NDP2_TIMER_MODE, 0x0004}, {AR_NEXT_NDP2_TIMER + 3*4, AR_NDP2_PERIOD + 3*4, AR_NDP2_TIMER_MODE, 0x0008}, {AR_NEXT_NDP2_TIMER + 4*4, AR_NDP2_PERIOD + 4*4, AR_NDP2_TIMER_MODE, 0x0010}, {AR_NEXT_NDP2_TIMER + 5*4, AR_NDP2_PERIOD + 5*4, AR_NDP2_TIMER_MODE, 0x0020}, {AR_NEXT_NDP2_TIMER + 6*4, AR_NDP2_PERIOD + 6*4, AR_NDP2_TIMER_MODE, 0x0040}, {AR_NEXT_NDP2_TIMER + 7*4, AR_NDP2_PERIOD + 7*4, AR_NDP2_TIMER_MODE, 0x0080} }; /* HW generic timer primitives */ /* compute and clear index of rightmost 1 */ static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask) { u32 b; b = *mask; b &= (0-b); *mask &= ~b; b *= debruijn32; b >>= 27; return timer_table->gen_timer_index[b]; } u32 ath9k_hw_gettsf32(struct ath_hw *ah) { return REG_READ(ah, AR_TSF_L32); } EXPORT_SYMBOL(ath9k_hw_gettsf32); struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, void (*trigger)(void *), void (*overflow)(void *), void *arg, u8 timer_index) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer *timer; timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL); if (timer == NULL) { ath_err(ath9k_hw_common(ah), "Failed to allocate memory for hw timer[%d]\n", timer_index); return NULL; } /* allocate a hardware generic timer slot */ timer_table->timers[timer_index] = timer; timer->index = timer_index; timer->trigger = trigger; timer->overflow = overflow; timer->arg = arg; return timer; } EXPORT_SYMBOL(ath_gen_timer_alloc); void ath9k_hw_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, u32 trig_timeout, u32 timer_period) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; u32 tsf, timer_next; BUG_ON(!timer_period); set_bit(timer->index, &timer_table->timer_mask.timer_bits); tsf = ath9k_hw_gettsf32(ah); timer_next = tsf + trig_timeout; ath_dbg(ath9k_hw_common(ah), HWTIMER, "current tsf %x period %x timer_next %x\n", tsf, timer_period, timer_next); /* * Program generic timer registers */ REG_WRITE(ah, gen_tmr_configuration[timer->index].next_addr, timer_next); REG_WRITE(ah, gen_tmr_configuration[timer->index].period_addr, timer_period); REG_SET_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, gen_tmr_configuration[timer->index].mode_mask); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { /* * Starting from AR9462, each generic timer can select which tsf * to use. But we still follow the old rule, 0 - 7 use tsf and * 8 - 15 use tsf2. */ if ((timer->index < AR_GEN_TIMER_BANK_1_LEN)) REG_CLR_BIT(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL, (1 << timer->index)); else REG_SET_BIT(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL, (1 << timer->index)); } /* Enable both trigger and thresh interrupt masks */ REG_SET_BIT(ah, AR_IMR_S5, (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); } EXPORT_SYMBOL(ath9k_hw_gen_timer_start); void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; if ((timer->index < AR_FIRST_NDP_TIMER) || (timer->index >= ATH_MAX_GEN_TIMER)) { return; } /* Clear generic timer enable bits. */ REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, gen_tmr_configuration[timer->index].mode_mask); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { /* * Need to switch back to TSF if it was using TSF2. */ if ((timer->index >= AR_GEN_TIMER_BANK_1_LEN)) { REG_CLR_BIT(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL, (1 << timer->index)); } } /* Disable both trigger and thresh interrupt masks */ REG_CLR_BIT(ah, AR_IMR_S5, (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); clear_bit(timer->index, &timer_table->timer_mask.timer_bits); } EXPORT_SYMBOL(ath9k_hw_gen_timer_stop); void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; /* free the hardware generic timer slot */ timer_table->timers[timer->index] = NULL; kfree(timer); } EXPORT_SYMBOL(ath_gen_timer_free); /* * Generic Timer Interrupts handling */ void ath_gen_timer_isr(struct ath_hw *ah) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer *timer; struct ath_common *common = ath9k_hw_common(ah); u32 trigger_mask, thresh_mask, index; /* get hardware generic timer interrupt status */ trigger_mask = ah->intr_gen_timer_trigger; thresh_mask = ah->intr_gen_timer_thresh; trigger_mask &= timer_table->timer_mask.val; thresh_mask &= timer_table->timer_mask.val; trigger_mask &= ~thresh_mask; while (thresh_mask) { index = rightmost_index(timer_table, &thresh_mask); timer = timer_table->timers[index]; BUG_ON(!timer); ath_dbg(common, HWTIMER, "TSF overflow for Gen timer %d\n", index); timer->overflow(timer->arg); } while (trigger_mask) { index = rightmost_index(timer_table, &trigger_mask); timer = timer_table->timers[index]; BUG_ON(!timer); ath_dbg(common, HWTIMER, "Gen timer[%d] trigger\n", index); timer->trigger(timer->arg); } } EXPORT_SYMBOL(ath_gen_timer_isr); /********/ /* HTC */ /********/ static struct { u32 version; const char * name; } ath_mac_bb_names[] = { /* Devices with external radios */ { AR_SREV_VERSION_5416_PCI, "5416" }, { AR_SREV_VERSION_5416_PCIE, "5418" }, { AR_SREV_VERSION_9100, "9100" }, { AR_SREV_VERSION_9160, "9160" }, /* Single-chip solutions */ { AR_SREV_VERSION_9280, "9280" }, { AR_SREV_VERSION_9285, "9285" }, { AR_SREV_VERSION_9287, "9287" }, { AR_SREV_VERSION_9271, "9271" }, { AR_SREV_VERSION_9300, "9300" }, { AR_SREV_VERSION_9330, "9330" }, { AR_SREV_VERSION_9340, "9340" }, { AR_SREV_VERSION_9485, "9485" }, { AR_SREV_VERSION_9462, "9462" }, { AR_SREV_VERSION_9550, "9550" }, { AR_SREV_VERSION_9565, "9565" }, }; /* For devices with external radios */ static struct { u16 version; const char * name; } ath_rf_names[] = { { 0, "5133" }, { AR_RAD5133_SREV_MAJOR, "5133" }, { AR_RAD5122_SREV_MAJOR, "5122" }, { AR_RAD2133_SREV_MAJOR, "2133" }, { AR_RAD2122_SREV_MAJOR, "2122" } }; /* * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown. */ static const char *ath9k_hw_mac_bb_name(u32 mac_bb_version) { int i; for (i=0; i= AR9280 are single-chip */ if (AR_SREV_9280_20_OR_LATER(ah)) { used = snprintf(hw_name, len, "Atheros AR%s Rev:%x", ath9k_hw_mac_bb_name(ah->hw_version.macVersion), ah->hw_version.macRev); } else { used = snprintf(hw_name, len, "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x", ath9k_hw_mac_bb_name(ah->hw_version.macVersion), ah->hw_version.macRev, ath9k_hw_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)), ah->hw_version.phyRev); } hw_name[used] = '\0'; } EXPORT_SYMBOL(ath9k_hw_name); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_hst.h0000644000175000017500000001321012026211315024057 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HTC_HST_H #define HTC_HST_H struct ath9k_htc_priv; struct htc_target; struct ath9k_htc_tx_ctl; enum ath9k_hif_transports { ATH9K_HIF_USB, }; struct ath9k_htc_hif { struct list_head list; const enum ath9k_hif_transports transport; const char *name; u8 control_dl_pipe; u8 control_ul_pipe; void (*start) (void *hif_handle); void (*stop) (void *hif_handle); void (*sta_drain) (void *hif_handle, u8 idx); int (*send) (void *hif_handle, u8 pipe, struct sk_buff *buf); }; enum htc_endpoint_id { ENDPOINT_UNUSED = -1, ENDPOINT0 = 0, ENDPOINT1 = 1, ENDPOINT2 = 2, ENDPOINT3 = 3, ENDPOINT4 = 4, ENDPOINT5 = 5, ENDPOINT6 = 6, ENDPOINT7 = 7, ENDPOINT8 = 8, ENDPOINT_MAX = 22 }; /* Htc frame hdr flags */ #define HTC_FLAGS_RECV_TRAILER (1 << 1) struct htc_frame_hdr { u8 endpoint_id; u8 flags; __be16 payload_len; u8 control[4]; } __packed; struct htc_ready_msg { __be16 message_id; __be16 credits; __be16 credit_size; u8 max_endpoints; u8 pad; } __packed; struct htc_config_pipe_msg { __be16 message_id; u8 pipe_id; u8 credits; } __packed; struct htc_ep_callbacks { void *priv; void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok); void (*rx) (void *, struct sk_buff *, enum htc_endpoint_id); }; struct htc_endpoint { u16 service_id; struct htc_ep_callbacks ep_callbacks; u32 max_txqdepth; int max_msglen; u8 ul_pipeid; u8 dl_pipeid; }; #define HTC_MAX_CONTROL_MESSAGE_LENGTH 255 #define HTC_CONTROL_BUFFER_SIZE \ (HTC_MAX_CONTROL_MESSAGE_LENGTH + sizeof(struct htc_frame_hdr)) #define HTC_OP_START_WAIT BIT(0) #define HTC_OP_CONFIG_PIPE_CREDITS BIT(1) struct htc_target { void *hif_dev; struct ath9k_htc_priv *drv_priv; struct device *dev; struct ath9k_htc_hif *hif; struct htc_endpoint endpoint[ENDPOINT_MAX]; struct completion target_wait; struct completion cmd_wait; struct list_head list; enum htc_endpoint_id conn_rsp_epid; u16 credits; u16 credit_size; u8 htc_flags; atomic_t tgt_ready; }; enum htc_msg_id { HTC_MSG_READY_ID = 1, HTC_MSG_CONNECT_SERVICE_ID, HTC_MSG_CONNECT_SERVICE_RESPONSE_ID, HTC_MSG_SETUP_COMPLETE_ID, HTC_MSG_CONFIG_PIPE_ID, HTC_MSG_CONFIG_PIPE_RESPONSE_ID, }; struct htc_service_connreq { u16 service_id; u16 con_flags; u32 max_send_qdepth; struct htc_ep_callbacks ep_callbacks; }; /* Current service IDs */ enum htc_service_group_ids{ RSVD_SERVICE_GROUP = 0, WMI_SERVICE_GROUP = 1, HTC_SERVICE_GROUP_LAST = 255 }; #define MAKE_SERVICE_ID(group, index) \ (int)(((int)group << 8) | (int)(index)) /* NOTE: service ID of 0x0000 is reserved and should never be used */ #define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1) #define HTC_LOOPBACK_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 2) #define WMI_CONTROL_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0) #define WMI_BEACON_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1) #define WMI_CAB_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2) #define WMI_UAPSD_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3) #define WMI_MGMT_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4) #define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 5) #define WMI_DATA_VI_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 6) #define WMI_DATA_BE_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 7) #define WMI_DATA_BK_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 8) struct htc_conn_svc_msg { __be16 msg_id; __be16 service_id; __be16 con_flags; u8 dl_pipeid; u8 ul_pipeid; u8 svc_meta_len; u8 pad; } __packed; /* connect response status codes */ #define HTC_SERVICE_SUCCESS 0 #define HTC_SERVICE_NOT_FOUND 1 #define HTC_SERVICE_FAILED 2 #define HTC_SERVICE_NO_RESOURCES 3 #define HTC_SERVICE_NO_MORE_EP 4 struct htc_conn_svc_rspmsg { __be16 msg_id; __be16 service_id; u8 status; u8 endpoint_id; __be16 max_msg_len; u8 svc_meta_len; u8 pad; } __packed; struct htc_comp_msg { __be16 msg_id; } __packed; int htc_init(struct htc_target *target); int htc_connect_service(struct htc_target *target, struct htc_service_connreq *service_connreq, enum htc_endpoint_id *conn_rsp_eid); int htc_send(struct htc_target *target, struct sk_buff *skb); int htc_send_epid(struct htc_target *target, struct sk_buff *skb, enum htc_endpoint_id epid); void htc_stop(struct htc_target *target); void htc_start(struct htc_target *target); void htc_sta_drain(struct htc_target *target, u8 idx); void ath9k_htc_rx_msg(struct htc_target *htc_handle, struct sk_buff *skb, u32 len, u8 pipe_id); void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, struct sk_buff *skb, bool txok); struct htc_target *ath9k_htc_hw_alloc(void *hif_handle, struct ath9k_htc_hif *hif, struct device *dev); void ath9k_htc_hw_free(struct htc_target *htc); int ath9k_htc_hw_init(struct htc_target *target, struct device *dev, u16 devid, char *product, u32 drv_info); void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug); #endif /* HTC_HST_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc.h0000644000175000017500000003514412026211315023213 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HTC_H #define HTC_H #include #include #include #include #include #include #include #include #include "common.h" #include "htc_hst.h" #include "hif_usb.h" #include "wmi.h" #define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */ #define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ #define ATH_ANI_POLLINTERVAL 100 /* 100 ms */ #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ #define ATH_DEFAULT_BMISS_LIMIT 10 #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) #define TSF_TO_TU(_h, _l) \ ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) extern struct ieee80211_ops ath9k_htc_ops; extern int htc_modparam_nohwcrypt; enum htc_phymode { HTC_MODE_11NA = 0, HTC_MODE_11NG = 1 }; enum htc_opmode { HTC_M_STA = 1, HTC_M_IBSS = 0, HTC_M_AHDEMO = 3, HTC_M_HOSTAP = 6, HTC_M_MONITOR = 8, HTC_M_WDS = 2 }; #define ATH9K_HTC_AMPDU 1 #define ATH9K_HTC_NORMAL 2 #define ATH9K_HTC_BEACON 3 #define ATH9K_HTC_MGMT 4 #define ATH9K_HTC_TX_CTSONLY 0x1 #define ATH9K_HTC_TX_RTSCTS 0x2 struct tx_frame_hdr { u8 data_type; u8 node_idx; u8 vif_idx; u8 tidno; __be32 flags; /* ATH9K_HTC_TX_* */ u8 key_type; u8 keyix; u8 cookie; u8 pad; } __packed; struct tx_mgmt_hdr { u8 node_idx; u8 vif_idx; u8 tidno; u8 flags; u8 key_type; u8 keyix; u8 cookie; u8 pad; } __packed; struct tx_beacon_header { u8 vif_index; u8 len_changed; u16 rev; } __packed; #define MAX_TX_AMPDU_SUBFRAMES_9271 17 #define MAX_TX_AMPDU_SUBFRAMES_7010 22 struct ath9k_htc_cap_target { __be32 ampdu_limit; u8 ampdu_subframes; u8 enable_coex; u8 tx_chainmask; u8 pad; } __packed; struct ath9k_htc_target_vif { u8 index; u8 opmode; u8 myaddr[ETH_ALEN]; u8 ath_cap; __be16 rtsthreshold; u8 pad; } __packed; struct ath9k_htc_target_sta { u8 macaddr[ETH_ALEN]; u8 bssid[ETH_ALEN]; u8 sta_index; u8 vif_index; u8 is_vif_sta; __be16 flags; __be16 htcap; __be16 maxampdu; u8 pad; } __packed; struct ath9k_htc_target_aggr { u8 sta_index; u8 tidno; u8 aggr_enable; u8 padding; } __packed; #define ATH_HTC_RATE_MAX 30 #define WLAN_RC_DS_FLAG 0x01 #define WLAN_RC_40_FLAG 0x02 #define WLAN_RC_SGI_FLAG 0x04 #define WLAN_RC_HT_FLAG 0x08 struct ath9k_htc_rateset { u8 rs_nrates; u8 rs_rates[ATH_HTC_RATE_MAX]; }; struct ath9k_htc_rate { struct ath9k_htc_rateset legacy_rates; struct ath9k_htc_rateset ht_rates; } __packed; struct ath9k_htc_target_rate { u8 sta_index; u8 isnew; __be32 capflags; struct ath9k_htc_rate rates; }; struct ath9k_htc_target_rate_mask { u8 vif_index; u8 band; __be32 mask; u16 pad; } __packed; struct ath9k_htc_target_int_stats { __be32 rx; __be32 rxorn; __be32 rxeol; __be32 txurn; __be32 txto; __be32 cst; } __packed; struct ath9k_htc_target_tx_stats { __be32 xretries; __be32 fifoerr; __be32 filtered; __be32 timer_exp; __be32 shortretries; __be32 longretries; __be32 qnull; __be32 encap_fail; __be32 nobuf; } __packed; struct ath9k_htc_target_rx_stats { __be32 nobuf; __be32 host_send; __be32 host_done; } __packed; #define ATH9K_HTC_MAX_VIF 2 #define ATH9K_HTC_MAX_BCN_VIF 2 #define INC_VIF(_priv, _type) do { \ switch (_type) { \ case NL80211_IFTYPE_STATION: \ _priv->num_sta_vif++; \ break; \ case NL80211_IFTYPE_ADHOC: \ _priv->num_ibss_vif++; \ break; \ case NL80211_IFTYPE_AP: \ _priv->num_ap_vif++; \ break; \ default: \ break; \ } \ } while (0) #define DEC_VIF(_priv, _type) do { \ switch (_type) { \ case NL80211_IFTYPE_STATION: \ _priv->num_sta_vif--; \ break; \ case NL80211_IFTYPE_ADHOC: \ _priv->num_ibss_vif--; \ break; \ case NL80211_IFTYPE_AP: \ _priv->num_ap_vif--; \ break; \ default: \ break; \ } \ } while (0) struct ath9k_htc_vif { u8 index; u16 seq_no; bool beacon_configured; int bslot; __le64 tsfadjust; }; struct ath9k_vif_iter_data { const u8 *hw_macaddr; u8 mask[ETH_ALEN]; }; #define ATH9K_HTC_MAX_STA 8 #define ATH9K_HTC_MAX_TID 8 enum tid_aggr_state { AGGR_STOP = 0, AGGR_PROGRESS, AGGR_START, AGGR_OPERATIONAL }; struct ath9k_htc_sta { u8 index; enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID]; }; #define ATH9K_HTC_RXBUF 256 #define HTC_RX_FRAME_HEADER_SIZE 40 struct ath9k_htc_rxbuf { bool in_process; struct sk_buff *skb; struct ath_htc_rx_status rxstatus; struct list_head list; }; struct ath9k_htc_rx { int last_rssi; /* FIXME: per-STA */ struct list_head rxbuf; spinlock_t rxbuflock; }; #define ATH9K_HTC_TX_CLEANUP_INTERVAL 50 /* ms */ #define ATH9K_HTC_TX_TIMEOUT_INTERVAL 3000 /* ms */ #define ATH9K_HTC_TX_RESERVE 10 #define ATH9K_HTC_TX_TIMEOUT_COUNT 40 #define ATH9K_HTC_TX_THRESHOLD (MAX_TX_BUF_NUM - ATH9K_HTC_TX_RESERVE) #define ATH9K_HTC_OP_TX_QUEUES_STOP BIT(0) #define ATH9K_HTC_OP_TX_DRAIN BIT(1) struct ath9k_htc_tx { u8 flags; int queued_cnt; struct sk_buff_head mgmt_ep_queue; struct sk_buff_head cab_ep_queue; struct sk_buff_head data_be_queue; struct sk_buff_head data_bk_queue; struct sk_buff_head data_vi_queue; struct sk_buff_head data_vo_queue; struct sk_buff_head tx_failed; DECLARE_BITMAP(tx_slot, MAX_TX_BUF_NUM); struct timer_list cleanup_timer; spinlock_t tx_lock; }; struct ath9k_htc_tx_ctl { u8 type; /* ATH9K_HTC_* */ u8 epid; u8 txok; u8 sta_idx; unsigned long timestamp; }; static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); BUILD_BUG_ON(sizeof(struct ath9k_htc_tx_ctl) > IEEE80211_TX_INFO_DRIVER_DATA_SIZE); return (struct ath9k_htc_tx_ctl *) &tx_info->driver_data; } #ifdef CONFIG_ATH9K_HTC_DEBUGFS #define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) #define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++) #define CAB_STAT_INC priv->debug.tx_stats.cab_queued++ #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++) void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, struct ath_htc_rx_status *rxs); struct ath_tx_stats { u32 buf_queued; u32 buf_completed; u32 skb_queued; u32 skb_success; u32 skb_failed; u32 cab_queued; u32 queue_stats[WME_NUM_AC]; }; struct ath_rx_stats { u32 skb_allocated; u32 skb_completed; u32 skb_dropped; u32 err_crc; u32 err_decrypt_crc; u32 err_mic; u32 err_pre_delim; u32 err_post_delim; u32 err_decrypt_busy; u32 err_phy; u32 err_phy_stats[ATH9K_PHYERR_MAX]; }; struct ath9k_debug { struct dentry *debugfs_phy; struct ath_tx_stats tx_stats; struct ath_rx_stats rx_stats; }; #else #define TX_STAT_INC(c) do { } while (0) #define RX_STAT_INC(c) do { } while (0) #define CAB_STAT_INC do { } while (0) #define TX_QSTAT_INC(c) do { } while (0) static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, struct ath_htc_rx_status *rxs) { } #endif /* CONFIG_ATH9K_HTC_DEBUGFS */ #define ATH_LED_PIN_DEF 1 #define ATH_LED_PIN_9287 10 #define ATH_LED_PIN_9271 15 #define ATH_LED_PIN_7010 12 #define BSTUCK_THRESHOLD 10 /* * Adjust these when the max. no of beaconing interfaces is * increased. */ #define DEFAULT_SWBA_RESPONSE 40 /* in TUs */ #define MIN_SWBA_RESPONSE 10 /* in TUs */ struct htc_beacon_config { struct ieee80211_vif *bslot[ATH9K_HTC_MAX_BCN_VIF]; u16 beacon_interval; u16 dtim_period; u16 bmiss_timeout; u32 bmiss_cnt; }; struct ath_btcoex { u32 bt_priority_cnt; unsigned long bt_priority_time; int bt_stomp_type; /* Types of BT stomping */ u32 btcoex_no_stomp; u32 btcoex_period; u32 btscan_no_stomp; }; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product); void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv); void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv); #else static inline void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product) { } static inline void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv) { } static inline void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv) { } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #define OP_INVALID BIT(0) #define OP_SCANNING BIT(1) #define OP_ENABLE_BEACON BIT(2) #define OP_BT_PRIORITY_DETECTED BIT(3) #define OP_BT_SCAN BIT(4) #define OP_ANI_RUNNING BIT(5) #define OP_TSF_RESET BIT(6) struct ath9k_htc_priv { struct device *dev; struct ieee80211_hw *hw; struct ath_hw *ah; struct htc_target *htc; struct wmi *wmi; u16 fw_version_major; u16 fw_version_minor; enum htc_endpoint_id wmi_cmd_ep; enum htc_endpoint_id beacon_ep; enum htc_endpoint_id cab_ep; enum htc_endpoint_id uapsd_ep; enum htc_endpoint_id mgmt_ep; enum htc_endpoint_id data_be_ep; enum htc_endpoint_id data_bk_ep; enum htc_endpoint_id data_vi_ep; enum htc_endpoint_id data_vo_ep; u8 vif_slot; u8 mon_vif_idx; u8 sta_slot; u8 vif_sta_pos[ATH9K_HTC_MAX_VIF]; u8 num_ibss_vif; u8 num_sta_vif; u8 num_sta_assoc_vif; u8 num_ap_vif; u16 curtxpow; u16 txpowlimit; u16 nvifs; u16 nstations; bool rearm_ani; bool reconfig_beacon; unsigned int rxfilter; unsigned long op_flags; struct ath9k_hw_cal_data caldata; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; spinlock_t beacon_lock; struct htc_beacon_config cur_beacon_conf; struct ath9k_htc_rx rx; struct ath9k_htc_tx tx; struct tasklet_struct swba_tasklet; struct tasklet_struct rx_tasklet; struct delayed_work ani_work; struct tasklet_struct tx_failed_tasklet; struct work_struct ps_work; struct work_struct fatal_work; struct mutex htc_pm_lock; unsigned long ps_usecount; bool ps_enabled; bool ps_idle; #ifdef CONFIG_MAC80211_LEDS enum led_brightness brightness; bool led_registered; char led_name[32]; struct led_classdev led_cdev; struct work_struct led_work; #endif int beaconq; int cabq; int hwq_map[WME_NUM_AC]; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; #endif struct delayed_work coex_period_work; struct delayed_work duty_cycle_work; #ifdef CONFIG_ATH9K_HTC_DEBUGFS struct ath9k_debug debug; #endif struct mutex mutex; }; static inline void ath_read_cachesize(struct ath_common *common, int *csz) { common->bus_ops->read_cachesize(common, csz); } void ath9k_htc_reset(struct ath9k_htc_priv *priv); void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif); void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif); void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif); void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv); void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif); void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv); void ath9k_htc_swba(struct ath9k_htc_priv *priv, struct wmi_event_swba *swba); void ath9k_htc_rxep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id); void ath9k_htc_txep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, bool txok); void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, bool txok); int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv, u8 enable_coex); void ath9k_htc_ani_work(struct work_struct *work); void ath9k_htc_start_ani(struct ath9k_htc_priv *priv); void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv); int ath9k_tx_init(struct ath9k_htc_priv *priv); int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct ieee80211_sta *sta, struct sk_buff *skb, u8 slot, bool is_cab); void ath9k_tx_cleanup(struct ath9k_htc_priv *priv); bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, int subtype); int ath9k_htc_cabq_setup(struct ath9k_htc_priv *priv); int get_hw_qnum(u16 queue, int *hwq_map); int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum, struct ath9k_tx_queue_info *qinfo); void ath9k_htc_check_stop_queues(struct ath9k_htc_priv *priv); void ath9k_htc_check_wake_queues(struct ath9k_htc_priv *priv); int ath9k_htc_tx_get_slot(struct ath9k_htc_priv *priv); void ath9k_htc_tx_clear_slot(struct ath9k_htc_priv *priv, int slot); void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv); void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event); void ath9k_tx_failed_tasklet(unsigned long data); void ath9k_htc_tx_cleanup_timer(unsigned long data); int ath9k_rx_init(struct ath9k_htc_priv *priv); void ath9k_rx_cleanup(struct ath9k_htc_priv *priv); void ath9k_host_rx_init(struct ath9k_htc_priv *priv); void ath9k_rx_tasklet(unsigned long data); u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv); void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv); void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv); void ath9k_ps_work(struct work_struct *work); bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, enum ath9k_power_mode mode); void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv); void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw); #ifdef CONFIG_MAC80211_LEDS void ath9k_init_leds(struct ath9k_htc_priv *priv); void ath9k_deinit_leds(struct ath9k_htc_priv *priv); void ath9k_led_work(struct work_struct *work); #else static inline void ath9k_init_leds(struct ath9k_htc_priv *priv) { } static inline void ath9k_deinit_leds(struct ath9k_htc_priv *priv) { } static inline void ath9k_led_work(struct work_struct *work) { } #endif int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, u16 devid, char *product, u32 drv_info); void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug); #ifdef CONFIG_PM void ath9k_htc_suspend(struct htc_target *htc_handle); int ath9k_htc_resume(struct htc_target *htc_handle); #endif #ifdef CONFIG_ATH9K_HTC_DEBUGFS int ath9k_htc_init_debug(struct ath_hw *ah); #else static inline int ath9k_htc_init_debug(struct ath_hw *ah) { return 0; }; #endif /* CONFIG_ATH9K_HTC_DEBUGFS */ #endif /* HTC_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c0000644000175000017500000007466212026211315025156 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "htc.h" /******/ /* TX */ /******/ static const int subtype_txq_to_hwq[] = { [WME_AC_BE] = ATH_TXQ_AC_BE, [WME_AC_BK] = ATH_TXQ_AC_BK, [WME_AC_VI] = ATH_TXQ_AC_VI, [WME_AC_VO] = ATH_TXQ_AC_VO, }; #define ATH9K_HTC_INIT_TXQ(subtype) do { \ qi.tqi_subtype = subtype_txq_to_hwq[subtype]; \ qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; \ qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; \ qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; \ qi.tqi_physCompBuf = 0; \ qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | \ TXQ_FLAG_TXDESCINT_ENABLE; \ } while (0) int get_hw_qnum(u16 queue, int *hwq_map) { switch (queue) { case 0: return hwq_map[WME_AC_VO]; case 1: return hwq_map[WME_AC_VI]; case 2: return hwq_map[WME_AC_BE]; case 3: return hwq_map[WME_AC_BK]; default: return hwq_map[WME_AC_BE]; } } void ath9k_htc_check_stop_queues(struct ath9k_htc_priv *priv) { spin_lock_bh(&priv->tx.tx_lock); priv->tx.queued_cnt++; if ((priv->tx.queued_cnt >= ATH9K_HTC_TX_THRESHOLD) && !(priv->tx.flags & ATH9K_HTC_OP_TX_QUEUES_STOP)) { priv->tx.flags |= ATH9K_HTC_OP_TX_QUEUES_STOP; ieee80211_stop_queues(priv->hw); } spin_unlock_bh(&priv->tx.tx_lock); } void ath9k_htc_check_wake_queues(struct ath9k_htc_priv *priv) { spin_lock_bh(&priv->tx.tx_lock); if ((priv->tx.queued_cnt < ATH9K_HTC_TX_THRESHOLD) && (priv->tx.flags & ATH9K_HTC_OP_TX_QUEUES_STOP)) { priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP; ieee80211_wake_queues(priv->hw); } spin_unlock_bh(&priv->tx.tx_lock); } int ath9k_htc_tx_get_slot(struct ath9k_htc_priv *priv) { int slot; spin_lock_bh(&priv->tx.tx_lock); slot = find_first_zero_bit(priv->tx.tx_slot, MAX_TX_BUF_NUM); if (slot >= MAX_TX_BUF_NUM) { spin_unlock_bh(&priv->tx.tx_lock); return -ENOBUFS; } __set_bit(slot, priv->tx.tx_slot); spin_unlock_bh(&priv->tx.tx_lock); return slot; } void ath9k_htc_tx_clear_slot(struct ath9k_htc_priv *priv, int slot) { spin_lock_bh(&priv->tx.tx_lock); __clear_bit(slot, priv->tx.tx_slot); spin_unlock_bh(&priv->tx.tx_lock); } static inline enum htc_endpoint_id get_htc_epid(struct ath9k_htc_priv *priv, u16 qnum) { enum htc_endpoint_id epid; switch (qnum) { case 0: TX_QSTAT_INC(WME_AC_VO); epid = priv->data_vo_ep; break; case 1: TX_QSTAT_INC(WME_AC_VI); epid = priv->data_vi_ep; break; case 2: TX_QSTAT_INC(WME_AC_BE); epid = priv->data_be_ep; break; case 3: default: TX_QSTAT_INC(WME_AC_BK); epid = priv->data_bk_ep; break; } return epid; } static inline struct sk_buff_head* get_htc_epid_queue(struct ath9k_htc_priv *priv, u8 epid) { struct ath_common *common = ath9k_hw_common(priv->ah); struct sk_buff_head *epid_queue = NULL; if (epid == priv->mgmt_ep) epid_queue = &priv->tx.mgmt_ep_queue; else if (epid == priv->cab_ep) epid_queue = &priv->tx.cab_ep_queue; else if (epid == priv->data_be_ep) epid_queue = &priv->tx.data_be_queue; else if (epid == priv->data_bk_ep) epid_queue = &priv->tx.data_bk_queue; else if (epid == priv->data_vi_ep) epid_queue = &priv->tx.data_vi_queue; else if (epid == priv->data_vo_ep) epid_queue = &priv->tx.data_vo_queue; else ath_err(common, "Invalid EPID: %d\n", epid); return epid_queue; } /* * Removes the driver header and returns the TX slot number */ static inline int strip_drv_header(struct ath9k_htc_priv *priv, struct sk_buff *skb) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_tx_ctl *tx_ctl; int slot; tx_ctl = HTC_SKB_CB(skb); if (tx_ctl->epid == priv->mgmt_ep) { struct tx_mgmt_hdr *tx_mhdr = (struct tx_mgmt_hdr *)skb->data; slot = tx_mhdr->cookie; skb_pull(skb, sizeof(struct tx_mgmt_hdr)); } else if ((tx_ctl->epid == priv->data_bk_ep) || (tx_ctl->epid == priv->data_be_ep) || (tx_ctl->epid == priv->data_vi_ep) || (tx_ctl->epid == priv->data_vo_ep) || (tx_ctl->epid == priv->cab_ep)) { struct tx_frame_hdr *tx_fhdr = (struct tx_frame_hdr *)skb->data; slot = tx_fhdr->cookie; skb_pull(skb, sizeof(struct tx_frame_hdr)); } else { ath_err(common, "Unsupported EPID: %d\n", tx_ctl->epid); slot = -EINVAL; } return slot; } int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum, struct ath9k_tx_queue_info *qinfo) { struct ath_hw *ah = priv->ah; int error = 0; struct ath9k_tx_queue_info qi; ath9k_hw_get_txq_props(ah, qnum, &qi); qi.tqi_aifs = qinfo->tqi_aifs; qi.tqi_cwmin = qinfo->tqi_cwmin / 2; /* XXX */ qi.tqi_cwmax = qinfo->tqi_cwmax; qi.tqi_burstTime = qinfo->tqi_burstTime; qi.tqi_readyTime = qinfo->tqi_readyTime; if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) { ath_err(ath9k_hw_common(ah), "Unable to update hardware queue %u!\n", qnum); error = -EIO; } else { ath9k_hw_resettxqueue(ah, qnum); } return error; } static void ath9k_htc_tx_mgmt(struct ath9k_htc_priv *priv, struct ath9k_htc_vif *avp, struct sk_buff *skb, u8 sta_idx, u8 vif_idx, u8 slot) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_mgmt *mgmt; struct ieee80211_hdr *hdr; struct tx_mgmt_hdr mgmt_hdr; struct ath9k_htc_tx_ctl *tx_ctl; u8 *tx_fhdr; tx_ctl = HTC_SKB_CB(skb); hdr = (struct ieee80211_hdr *) skb->data; memset(tx_ctl, 0, sizeof(*tx_ctl)); memset(&mgmt_hdr, 0, sizeof(struct tx_mgmt_hdr)); /* * Set the TSF adjust value for probe response * frame also. */ if (avp && unlikely(ieee80211_is_probe_resp(hdr->frame_control))) { mgmt = (struct ieee80211_mgmt *)skb->data; mgmt->u.probe_resp.timestamp = avp->tsfadjust; } tx_ctl->type = ATH9K_HTC_MGMT; mgmt_hdr.node_idx = sta_idx; mgmt_hdr.vif_idx = vif_idx; mgmt_hdr.tidno = 0; mgmt_hdr.flags = 0; mgmt_hdr.cookie = slot; mgmt_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb); if (mgmt_hdr.key_type == ATH9K_KEY_TYPE_CLEAR) mgmt_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID; else mgmt_hdr.keyix = tx_info->control.hw_key->hw_key_idx; tx_fhdr = skb_push(skb, sizeof(mgmt_hdr)); memcpy(tx_fhdr, (u8 *) &mgmt_hdr, sizeof(mgmt_hdr)); tx_ctl->epid = priv->mgmt_ep; } static void ath9k_htc_tx_data(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct sk_buff *skb, u8 sta_idx, u8 vif_idx, u8 slot, bool is_cab) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr; struct ath9k_htc_tx_ctl *tx_ctl; struct tx_frame_hdr tx_hdr; u32 flags = 0; u8 *qc, *tx_fhdr; u16 qnum; tx_ctl = HTC_SKB_CB(skb); hdr = (struct ieee80211_hdr *) skb->data; memset(tx_ctl, 0, sizeof(*tx_ctl)); memset(&tx_hdr, 0, sizeof(struct tx_frame_hdr)); tx_hdr.node_idx = sta_idx; tx_hdr.vif_idx = vif_idx; tx_hdr.cookie = slot; /* * This is a bit redundant but it helps to get * the per-packet index quickly when draining the * TX queue in the HIF layer. Otherwise we would * have to parse the packet contents ... */ tx_ctl->sta_idx = sta_idx; if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { tx_ctl->type = ATH9K_HTC_AMPDU; tx_hdr.data_type = ATH9K_HTC_AMPDU; } else { tx_ctl->type = ATH9K_HTC_NORMAL; tx_hdr.data_type = ATH9K_HTC_NORMAL; } if (ieee80211_is_data_qos(hdr->frame_control)) { qc = ieee80211_get_qos_ctl(hdr); tx_hdr.tidno = qc[0] & IEEE80211_QOS_CTL_TID_MASK; } /* Check for RTS protection */ if (priv->hw->wiphy->rts_threshold != (u32) -1) if (skb->len > priv->hw->wiphy->rts_threshold) flags |= ATH9K_HTC_TX_RTSCTS; /* CTS-to-self */ if (!(flags & ATH9K_HTC_TX_RTSCTS) && (vif && vif->bss_conf.use_cts_prot)) flags |= ATH9K_HTC_TX_CTSONLY; tx_hdr.flags = cpu_to_be32(flags); tx_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb); if (tx_hdr.key_type == ATH9K_KEY_TYPE_CLEAR) tx_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID; else tx_hdr.keyix = tx_info->control.hw_key->hw_key_idx; tx_fhdr = skb_push(skb, sizeof(tx_hdr)); memcpy(tx_fhdr, (u8 *) &tx_hdr, sizeof(tx_hdr)); if (is_cab) { CAB_STAT_INC; tx_ctl->epid = priv->cab_ep; return; } qnum = skb_get_queue_mapping(skb); tx_ctl->epid = get_htc_epid(priv, qnum); } int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct ieee80211_sta *sta, struct sk_buff *skb, u8 slot, bool is_cab) { struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = tx_info->control.vif; struct ath9k_htc_sta *ista; struct ath9k_htc_vif *avp = NULL; u8 sta_idx, vif_idx; hdr = (struct ieee80211_hdr *) skb->data; /* * Find out on which interface this packet has to be * sent out. */ if (vif) { avp = (struct ath9k_htc_vif *) vif->drv_priv; vif_idx = avp->index; } else { if (!priv->ah->is_monitoring) { ath_dbg(ath9k_hw_common(priv->ah), XMIT, "VIF is null, but no monitor interface !\n"); return -EINVAL; } vif_idx = priv->mon_vif_idx; } /* * Find out which station this packet is destined for. */ if (sta) { ista = (struct ath9k_htc_sta *) sta->drv_priv; sta_idx = ista->index; } else { sta_idx = priv->vif_sta_pos[vif_idx]; } if (ieee80211_is_data(hdr->frame_control)) ath9k_htc_tx_data(priv, vif, skb, sta_idx, vif_idx, slot, is_cab); else ath9k_htc_tx_mgmt(priv, avp, skb, sta_idx, vif_idx, slot); return htc_send(priv->htc, skb); } static inline bool __ath9k_htc_check_tx_aggr(struct ath9k_htc_priv *priv, struct ath9k_htc_sta *ista, u8 tid) { bool ret = false; spin_lock_bh(&priv->tx.tx_lock); if ((tid < ATH9K_HTC_MAX_TID) && (ista->tid_state[tid] == AGGR_STOP)) ret = true; spin_unlock_bh(&priv->tx.tx_lock); return ret; } static void ath9k_htc_check_tx_aggr(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct sk_buff *skb) { struct ieee80211_sta *sta; struct ieee80211_hdr *hdr; __le16 fc; hdr = (struct ieee80211_hdr *) skb->data; fc = hdr->frame_control; rcu_read_lock(); sta = ieee80211_find_sta(vif, hdr->addr1); if (!sta) { rcu_read_unlock(); return; } if (sta && conf_is_ht(&priv->hw->conf) && !(skb->protocol == cpu_to_be16(ETH_P_PAE))) { if (ieee80211_is_data_qos(fc)) { u8 *qc, tid; struct ath9k_htc_sta *ista; qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; ista = (struct ath9k_htc_sta *)sta->drv_priv; if (__ath9k_htc_check_tx_aggr(priv, ista, tid)) { ieee80211_start_tx_ba_session(sta, tid, 0); spin_lock_bh(&priv->tx.tx_lock); ista->tid_state[tid] = AGGR_PROGRESS; spin_unlock_bh(&priv->tx.tx_lock); } } } rcu_read_unlock(); } static void ath9k_htc_tx_process(struct ath9k_htc_priv *priv, struct sk_buff *skb, struct __wmi_event_txstatus *txs) { struct ieee80211_vif *vif; struct ath9k_htc_tx_ctl *tx_ctl; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rate; struct ieee80211_conf *cur_conf = &priv->hw->conf; bool txok; int slot; slot = strip_drv_header(priv, skb); if (slot < 0) { dev_kfree_skb_any(skb); return; } tx_ctl = HTC_SKB_CB(skb); txok = tx_ctl->txok; tx_info = IEEE80211_SKB_CB(skb); vif = tx_info->control.vif; rate = &tx_info->status.rates[0]; memset(&tx_info->status, 0, sizeof(tx_info->status)); /* * URB submission failed for this frame, it never reached * the target. */ if (!txok || !vif || !txs) goto send_mac80211; if (txs->ts_flags & ATH9K_HTC_TXSTAT_ACK) tx_info->flags |= IEEE80211_TX_STAT_ACK; if (txs->ts_flags & ATH9K_HTC_TXSTAT_FILT) tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; if (txs->ts_flags & ATH9K_HTC_TXSTAT_RTC_CTS) rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; rate->count = 1; rate->idx = MS(txs->ts_rate, ATH9K_HTC_TXSTAT_RATE); if (txs->ts_flags & ATH9K_HTC_TXSTAT_MCS) { rate->flags |= IEEE80211_TX_RC_MCS; if (txs->ts_flags & ATH9K_HTC_TXSTAT_CW40) rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (txs->ts_flags & ATH9K_HTC_TXSTAT_SGI) rate->flags |= IEEE80211_TX_RC_SHORT_GI; } else { if (cur_conf->channel->band == IEEE80211_BAND_5GHZ) rate->idx += 4; /* No CCK rates */ } ath9k_htc_check_tx_aggr(priv, vif, skb); send_mac80211: spin_lock_bh(&priv->tx.tx_lock); if (WARN_ON(--priv->tx.queued_cnt < 0)) priv->tx.queued_cnt = 0; spin_unlock_bh(&priv->tx.tx_lock); ath9k_htc_tx_clear_slot(priv, slot); /* Send status to mac80211 */ ieee80211_tx_status(priv->hw, skb); } static inline void ath9k_htc_tx_drainq(struct ath9k_htc_priv *priv, struct sk_buff_head *queue) { struct sk_buff *skb; while ((skb = skb_dequeue(queue)) != NULL) { ath9k_htc_tx_process(priv, skb, NULL); } } void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv) { struct ath9k_htc_tx_event *event, *tmp; spin_lock_bh(&priv->tx.tx_lock); priv->tx.flags |= ATH9K_HTC_OP_TX_DRAIN; spin_unlock_bh(&priv->tx.tx_lock); /* * Ensure that all pending TX frames are flushed, * and that the TX completion/failed tasklets is killed. */ htc_stop(priv->htc); tasklet_kill(&priv->wmi->wmi_event_tasklet); tasklet_kill(&priv->tx_failed_tasklet); ath9k_htc_tx_drainq(priv, &priv->tx.mgmt_ep_queue); ath9k_htc_tx_drainq(priv, &priv->tx.cab_ep_queue); ath9k_htc_tx_drainq(priv, &priv->tx.data_be_queue); ath9k_htc_tx_drainq(priv, &priv->tx.data_bk_queue); ath9k_htc_tx_drainq(priv, &priv->tx.data_vi_queue); ath9k_htc_tx_drainq(priv, &priv->tx.data_vo_queue); ath9k_htc_tx_drainq(priv, &priv->tx.tx_failed); /* * The TX cleanup timer has already been killed. */ spin_lock_bh(&priv->wmi->event_lock); list_for_each_entry_safe(event, tmp, &priv->wmi->pending_tx_events, list) { list_del(&event->list); kfree(event); } spin_unlock_bh(&priv->wmi->event_lock); spin_lock_bh(&priv->tx.tx_lock); priv->tx.flags &= ~ATH9K_HTC_OP_TX_DRAIN; spin_unlock_bh(&priv->tx.tx_lock); } void ath9k_tx_failed_tasklet(unsigned long data) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; spin_lock_bh(&priv->tx.tx_lock); if (priv->tx.flags & ATH9K_HTC_OP_TX_DRAIN) { spin_unlock_bh(&priv->tx.tx_lock); return; } spin_unlock_bh(&priv->tx.tx_lock); ath9k_htc_tx_drainq(priv, &priv->tx.tx_failed); } static inline bool check_cookie(struct ath9k_htc_priv *priv, struct sk_buff *skb, u8 cookie, u8 epid) { u8 fcookie = 0; if (epid == priv->mgmt_ep) { struct tx_mgmt_hdr *hdr; hdr = (struct tx_mgmt_hdr *) skb->data; fcookie = hdr->cookie; } else if ((epid == priv->data_bk_ep) || (epid == priv->data_be_ep) || (epid == priv->data_vi_ep) || (epid == priv->data_vo_ep) || (epid == priv->cab_ep)) { struct tx_frame_hdr *hdr; hdr = (struct tx_frame_hdr *) skb->data; fcookie = hdr->cookie; } if (fcookie == cookie) return true; return false; } static struct sk_buff* ath9k_htc_tx_get_packet(struct ath9k_htc_priv *priv, struct __wmi_event_txstatus *txs) { struct ath_common *common = ath9k_hw_common(priv->ah); struct sk_buff_head *epid_queue; struct sk_buff *skb, *tmp; unsigned long flags; u8 epid = MS(txs->ts_rate, ATH9K_HTC_TXSTAT_EPID); epid_queue = get_htc_epid_queue(priv, epid); if (!epid_queue) return NULL; spin_lock_irqsave(&epid_queue->lock, flags); skb_queue_walk_safe(epid_queue, skb, tmp) { if (check_cookie(priv, skb, txs->cookie, epid)) { __skb_unlink(skb, epid_queue); spin_unlock_irqrestore(&epid_queue->lock, flags); return skb; } } spin_unlock_irqrestore(&epid_queue->lock, flags); ath_dbg(common, XMIT, "No matching packet for cookie: %d, epid: %d\n", txs->cookie, epid); return NULL; } void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event) { struct wmi_event_txstatus *txs = (struct wmi_event_txstatus *)wmi_event; struct __wmi_event_txstatus *__txs; struct sk_buff *skb; struct ath9k_htc_tx_event *tx_pend; int i; for (i = 0; i < txs->cnt; i++) { WARN_ON(txs->cnt > HTC_MAX_TX_STATUS); __txs = &txs->txstatus[i]; skb = ath9k_htc_tx_get_packet(priv, __txs); if (!skb) { /* * Store this event, so that the TX cleanup * routine can check later for the needed packet. */ tx_pend = kzalloc(sizeof(struct ath9k_htc_tx_event), GFP_ATOMIC); if (!tx_pend) continue; memcpy(&tx_pend->txs, __txs, sizeof(struct __wmi_event_txstatus)); spin_lock(&priv->wmi->event_lock); list_add_tail(&tx_pend->list, &priv->wmi->pending_tx_events); spin_unlock(&priv->wmi->event_lock); continue; } ath9k_htc_tx_process(priv, skb, __txs); } /* Wake TX queues if needed */ ath9k_htc_check_wake_queues(priv); } void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, bool txok) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) drv_priv; struct ath9k_htc_tx_ctl *tx_ctl; struct sk_buff_head *epid_queue; tx_ctl = HTC_SKB_CB(skb); tx_ctl->txok = txok; tx_ctl->timestamp = jiffies; if (!txok) { skb_queue_tail(&priv->tx.tx_failed, skb); tasklet_schedule(&priv->tx_failed_tasklet); return; } epid_queue = get_htc_epid_queue(priv, ep_id); if (!epid_queue) { dev_kfree_skb_any(skb); return; } skb_queue_tail(epid_queue, skb); } static inline bool check_packet(struct ath9k_htc_priv *priv, struct sk_buff *skb) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_tx_ctl *tx_ctl; tx_ctl = HTC_SKB_CB(skb); if (time_after(jiffies, tx_ctl->timestamp + msecs_to_jiffies(ATH9K_HTC_TX_TIMEOUT_INTERVAL))) { ath_dbg(common, XMIT, "Dropping a packet due to TX timeout\n"); return true; } return false; } static void ath9k_htc_tx_cleanup_queue(struct ath9k_htc_priv *priv, struct sk_buff_head *epid_queue) { bool process = false; unsigned long flags; struct sk_buff *skb, *tmp; struct sk_buff_head queue; skb_queue_head_init(&queue); spin_lock_irqsave(&epid_queue->lock, flags); skb_queue_walk_safe(epid_queue, skb, tmp) { if (check_packet(priv, skb)) { __skb_unlink(skb, epid_queue); __skb_queue_tail(&queue, skb); process = true; } } spin_unlock_irqrestore(&epid_queue->lock, flags); if (process) { skb_queue_walk_safe(&queue, skb, tmp) { __skb_unlink(skb, &queue); ath9k_htc_tx_process(priv, skb, NULL); } } } void ath9k_htc_tx_cleanup_timer(unsigned long data) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) data; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_tx_event *event, *tmp; struct sk_buff *skb; spin_lock(&priv->wmi->event_lock); list_for_each_entry_safe(event, tmp, &priv->wmi->pending_tx_events, list) { skb = ath9k_htc_tx_get_packet(priv, &event->txs); if (skb) { ath_dbg(common, XMIT, "Found packet for cookie: %d, epid: %d\n", event->txs.cookie, MS(event->txs.ts_rate, ATH9K_HTC_TXSTAT_EPID)); ath9k_htc_tx_process(priv, skb, &event->txs); list_del(&event->list); kfree(event); continue; } if (++event->count >= ATH9K_HTC_TX_TIMEOUT_COUNT) { list_del(&event->list); kfree(event); } } spin_unlock(&priv->wmi->event_lock); /* * Check if status-pending packets have to be cleaned up. */ ath9k_htc_tx_cleanup_queue(priv, &priv->tx.mgmt_ep_queue); ath9k_htc_tx_cleanup_queue(priv, &priv->tx.cab_ep_queue); ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_be_queue); ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_bk_queue); ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_vi_queue); ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_vo_queue); /* Wake TX queues if needed */ ath9k_htc_check_wake_queues(priv); mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); } int ath9k_tx_init(struct ath9k_htc_priv *priv) { skb_queue_head_init(&priv->tx.mgmt_ep_queue); skb_queue_head_init(&priv->tx.cab_ep_queue); skb_queue_head_init(&priv->tx.data_be_queue); skb_queue_head_init(&priv->tx.data_bk_queue); skb_queue_head_init(&priv->tx.data_vi_queue); skb_queue_head_init(&priv->tx.data_vo_queue); skb_queue_head_init(&priv->tx.tx_failed); return 0; } void ath9k_tx_cleanup(struct ath9k_htc_priv *priv) { } bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, int subtype) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info qi; int qnum; memset(&qi, 0, sizeof(qi)); ATH9K_HTC_INIT_TXQ(subtype); qnum = ath9k_hw_setuptxqueue(priv->ah, ATH9K_TX_QUEUE_DATA, &qi); if (qnum == -1) return false; if (qnum >= ARRAY_SIZE(priv->hwq_map)) { ath_err(common, "qnum %u out of range, max %zu!\n", qnum, ARRAY_SIZE(priv->hwq_map)); ath9k_hw_releasetxqueue(ah, qnum); return false; } priv->hwq_map[subtype] = qnum; return true; } int ath9k_htc_cabq_setup(struct ath9k_htc_priv *priv) { struct ath9k_tx_queue_info qi; memset(&qi, 0, sizeof(qi)); ATH9K_HTC_INIT_TXQ(0); return ath9k_hw_setuptxqueue(priv->ah, ATH9K_TX_QUEUE_CAB, &qi); } /******/ /* RX */ /******/ /* * Calculate the RX filter to be set in the HW. */ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) { #define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR) struct ath_hw *ah = priv->ah; u32 rfilt; rfilt = (ath9k_hw_getrxfilter(ah) & RX_FILTER_PRESERVE) | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; if (priv->rxfilter & FIF_PROBE_REQ) rfilt |= ATH9K_RX_FILTER_PROBEREQ; /* * Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station * mode interface or when in monitor mode. AP mode does not need this * since it receives all in-BSS frames anyway. */ if (((ah->opmode != NL80211_IFTYPE_AP) && (priv->rxfilter & FIF_PROMISC_IN_BSS)) || ah->is_monitoring) rfilt |= ATH9K_RX_FILTER_PROM; if (priv->rxfilter & FIF_CONTROL) rfilt |= ATH9K_RX_FILTER_CONTROL; if ((ah->opmode == NL80211_IFTYPE_STATION) && (priv->nvifs <= 1) && !(priv->rxfilter & FIF_BCN_PRBRESP_PROMISC)) rfilt |= ATH9K_RX_FILTER_MYBEACON; else rfilt |= ATH9K_RX_FILTER_BEACON; if (conf_is_ht(&priv->hw->conf)) { rfilt |= ATH9K_RX_FILTER_COMP_BAR; rfilt |= ATH9K_RX_FILTER_UNCOMP_BA_BAR; } if (priv->rxfilter & FIF_PSPOLL) rfilt |= ATH9K_RX_FILTER_PSPOLL; if (priv->nvifs > 1) rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; return rfilt; #undef RX_FILTER_PRESERVE } /* * Recv initialization for opmode change. */ static void ath9k_htc_opmode_init(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; u32 rfilt, mfilt[2]; /* configure rx filter */ rfilt = ath9k_htc_calcrxfilter(priv); ath9k_hw_setrxfilter(ah, rfilt); /* calculate and install multicast filter */ mfilt[0] = mfilt[1] = ~0; ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); } void ath9k_host_rx_init(struct ath9k_htc_priv *priv) { ath9k_hw_rxena(priv->ah); ath9k_htc_opmode_init(priv); ath9k_hw_startpcureceive(priv->ah, test_bit(OP_SCANNING, &priv->op_flags)); priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER; } static void ath9k_process_rate(struct ieee80211_hw *hw, struct ieee80211_rx_status *rxs, u8 rx_rate, u8 rs_flags) { struct ieee80211_supported_band *sband; enum ieee80211_band band; unsigned int i = 0; if (rx_rate & 0x80) { /* HT rate */ rxs->flag |= RX_FLAG_HT; if (rs_flags & ATH9K_RX_2040) rxs->flag |= RX_FLAG_40MHZ; if (rs_flags & ATH9K_RX_GI) rxs->flag |= RX_FLAG_SHORT_GI; rxs->rate_idx = rx_rate & 0x7f; return; } band = hw->conf.channel->band; sband = hw->wiphy->bands[band]; for (i = 0; i < sband->n_bitrates; i++) { if (sband->bitrates[i].hw_value == rx_rate) { rxs->rate_idx = i; return; } if (sband->bitrates[i].hw_value_short == rx_rate) { rxs->rate_idx = i; rxs->flag |= RX_FLAG_SHORTPRE; return; } } } static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, struct ath9k_htc_rxbuf *rxbuf, struct ieee80211_rx_status *rx_status) { struct ieee80211_hdr *hdr; struct ieee80211_hw *hw = priv->hw; struct sk_buff *skb = rxbuf->skb; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath_htc_rx_status *rxstatus; int hdrlen, padpos, padsize; int last_rssi = ATH_RSSI_DUMMY_MARKER; __le16 fc; if (skb->len < HTC_RX_FRAME_HEADER_SIZE) { ath_err(common, "Corrupted RX frame, dropping (len: %d)\n", skb->len); goto rx_next; } rxstatus = (struct ath_htc_rx_status *)skb->data; if (be16_to_cpu(rxstatus->rs_datalen) - (skb->len - HTC_RX_FRAME_HEADER_SIZE) != 0) { ath_err(common, "Corrupted RX data len, dropping (dlen: %d, skblen: %d)\n", rxstatus->rs_datalen, skb->len); goto rx_next; } ath9k_htc_err_stat_rx(priv, rxstatus); /* Get the RX status information */ memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE); skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE); hdr = (struct ieee80211_hdr *)skb->data; fc = hdr->frame_control; hdrlen = ieee80211_get_hdrlen_from_skb(skb); padpos = ath9k_cmn_padpos(fc); padsize = padpos & 3; if (padsize && skb->len >= padpos+padsize+FCS_LEN) { memmove(skb->data + padsize, skb->data, padpos); skb_pull(skb, padsize); } memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); if (rxbuf->rxstatus.rs_status != 0) { if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_CRC) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_PHY) goto rx_next; if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT) { /* FIXME */ } else if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_MIC) { if (ieee80211_is_ctl(fc)) /* * Sometimes, we get invalid * MIC failures on valid control frames. * Remove these mic errors. */ rxbuf->rxstatus.rs_status &= ~ATH9K_RXERR_MIC; else rx_status->flag |= RX_FLAG_MMIC_ERROR; } /* * Reject error frames with the exception of * decryption and MIC failures. For monitor mode, * we also ignore the CRC error. */ if (priv->ah->opmode == NL80211_IFTYPE_MONITOR) { if (rxbuf->rxstatus.rs_status & ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | ATH9K_RXERR_CRC)) goto rx_next; } else { if (rxbuf->rxstatus.rs_status & ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { goto rx_next; } } } if (!(rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT)) { u8 keyix; keyix = rxbuf->rxstatus.rs_keyix; if (keyix != ATH9K_RXKEYIX_INVALID) { rx_status->flag |= RX_FLAG_DECRYPTED; } else if (ieee80211_has_protected(fc) && skb->len >= hdrlen + 4) { keyix = skb->data[hdrlen + 3] >> 6; if (test_bit(keyix, common->keymap)) rx_status->flag |= RX_FLAG_DECRYPTED; } } ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate, rxbuf->rxstatus.rs_flags); if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD && !rxbuf->rxstatus.rs_moreaggr) ATH_RSSI_LPF(priv->rx.last_rssi, rxbuf->rxstatus.rs_rssi); last_rssi = priv->rx.last_rssi; if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) rxbuf->rxstatus.rs_rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); if (rxbuf->rxstatus.rs_rssi < 0) rxbuf->rxstatus.rs_rssi = 0; if (ieee80211_is_beacon(fc)) priv->ah->stats.avgbrssi = rxbuf->rxstatus.rs_rssi; rx_status->mactime = be64_to_cpu(rxbuf->rxstatus.rs_tstamp); rx_status->band = hw->conf.channel->band; rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; rx_status->antenna = rxbuf->rxstatus.rs_antenna; rx_status->flag |= RX_FLAG_MACTIME_MPDU; return true; rx_next: return false; } /* * FIXME: Handle FLUSH later on. */ void ath9k_rx_tasklet(unsigned long data) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; struct ieee80211_rx_status rx_status; struct sk_buff *skb; unsigned long flags; struct ieee80211_hdr *hdr; do { spin_lock_irqsave(&priv->rx.rxbuflock, flags); list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) { if (tmp_buf->in_process) { rxbuf = tmp_buf; break; } } if (rxbuf == NULL) { spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); break; } if (!rxbuf->skb) goto requeue; if (!ath9k_rx_prepare(priv, rxbuf, &rx_status)) { dev_kfree_skb_any(rxbuf->skb); goto requeue; } memcpy(IEEE80211_SKB_RXCB(rxbuf->skb), &rx_status, sizeof(struct ieee80211_rx_status)); skb = rxbuf->skb; hdr = (struct ieee80211_hdr *) skb->data; if (ieee80211_is_beacon(hdr->frame_control) && priv->ps_enabled) ieee80211_queue_work(priv->hw, &priv->ps_work); spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); ieee80211_rx(priv->hw, skb); spin_lock_irqsave(&priv->rx.rxbuflock, flags); requeue: rxbuf->in_process = false; rxbuf->skb = NULL; list_move_tail(&rxbuf->list, &priv->rx.rxbuf); rxbuf = NULL; spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); } while (1); } void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb, enum htc_endpoint_id ep_id) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)drv_priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; spin_lock(&priv->rx.rxbuflock); list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) { if (!tmp_buf->in_process) { rxbuf = tmp_buf; break; } } spin_unlock(&priv->rx.rxbuflock); if (rxbuf == NULL) { ath_dbg(common, ANY, "No free RX buffer\n"); goto err; } spin_lock(&priv->rx.rxbuflock); rxbuf->skb = skb; rxbuf->in_process = true; spin_unlock(&priv->rx.rxbuflock); tasklet_schedule(&priv->rx_tasklet); return; err: dev_kfree_skb_any(skb); } /* FIXME: Locking for cleanup/init */ void ath9k_rx_cleanup(struct ath9k_htc_priv *priv) { struct ath9k_htc_rxbuf *rxbuf, *tbuf; list_for_each_entry_safe(rxbuf, tbuf, &priv->rx.rxbuf, list) { list_del(&rxbuf->list); if (rxbuf->skb) dev_kfree_skb_any(rxbuf->skb); kfree(rxbuf); } } int ath9k_rx_init(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_rxbuf *rxbuf; int i = 0; INIT_LIST_HEAD(&priv->rx.rxbuf); spin_lock_init(&priv->rx.rxbuflock); for (i = 0; i < ATH9K_HTC_RXBUF; i++) { rxbuf = kzalloc(sizeof(struct ath9k_htc_rxbuf), GFP_KERNEL); if (rxbuf == NULL) { ath_err(common, "Unable to allocate RX buffers\n"); goto err; } list_add_tail(&rxbuf->list, &priv->rx.rxbuf); } return 0; err: ath9k_rx_cleanup(priv); return -ENOMEM; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_drv_main.c0000644000175000017500000013303312026211315025061 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "htc.h" /*************/ /* Utilities */ /*************/ /* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */ static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, struct ath9k_channel *ichan) { enum htc_phymode mode; mode = -EINVAL; switch (ichan->chanmode) { case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: mode = HTC_MODE_11NG; break; case CHANNEL_A: case CHANNEL_A_HT20: case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: mode = HTC_MODE_11NA; break; default: break; } WARN_ON(mode < 0); return mode; } bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, enum ath9k_power_mode mode) { bool ret; mutex_lock(&priv->htc_pm_lock); ret = ath9k_hw_setpower(priv->ah, mode); mutex_unlock(&priv->htc_pm_lock); return ret; } void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv) { mutex_lock(&priv->htc_pm_lock); if (++priv->ps_usecount != 1) goto unlock; ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE); unlock: mutex_unlock(&priv->htc_pm_lock); } void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv) { bool reset; mutex_lock(&priv->htc_pm_lock); if (--priv->ps_usecount != 0) goto unlock; if (priv->ps_idle) { ath9k_hw_setrxabort(priv->ah, true); ath9k_hw_stopdmarecv(priv->ah, &reset); ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP); } else if (priv->ps_enabled) { ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP); } unlock: mutex_unlock(&priv->htc_pm_lock); } void ath9k_ps_work(struct work_struct *work) { struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, ps_work); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); /* The chip wakes up after receiving the first beacon while network sleep is enabled. For the driver to be in sync with the hw, set the chip to awake and only then set it to sleep. */ ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); } static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = data; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon) priv->reconfig_beacon = true; if (bss_conf->assoc) { priv->rearm_ani = true; priv->reconfig_beacon = true; } } static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv) { priv->rearm_ani = false; priv->reconfig_beacon = false; ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_vif_iter, priv); if (priv->rearm_ani) ath9k_htc_start_ani(priv); if (priv->reconfig_beacon) { ath9k_htc_ps_wakeup(priv); ath9k_htc_beacon_reconfig(priv); ath9k_htc_ps_restore(priv); } } static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath9k_vif_iter_data *iter_data = data; int i; for (i = 0; i < ETH_ALEN; i++) iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); } static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_vif_iter_data iter_data; /* * Use the hardware MAC address as reference, the hardware uses it * together with the BSSID mask when matching addresses. */ iter_data.hw_macaddr = common->macaddr; memset(&iter_data.mask, 0xff, ETH_ALEN); if (vif) ath9k_htc_bssid_iter(&iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter, &iter_data); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); } static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv) { if (priv->num_ibss_vif) priv->ah->opmode = NL80211_IFTYPE_ADHOC; else if (priv->num_ap_vif) priv->ah->opmode = NL80211_IFTYPE_AP; else priv->ah->opmode = NL80211_IFTYPE_STATION; ath9k_hw_setopmode(priv->ah); } void ath9k_htc_reset(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *channel = priv->hw->conf.channel; struct ath9k_hw_cal_data *caldata = NULL; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; int ret; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); ath9k_htc_stop_ani(priv); ieee80211_stop_queues(priv->hw); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); ath9k_wmi_event_drain(priv); caldata = &priv->caldata; ret = ath9k_hw_reset(ah, ah->curchan, caldata, false); if (ret) { ath_err(common, "Unable to reset device (%u Mhz) reset status %d\n", channel->center_freq, ret); } ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); WMI_CMD(WMI_START_RECV_CMDID); ath9k_host_rx_init(priv); mode = ath9k_htc_get_curmode(priv, ah->curchan); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); WMI_CMD(WMI_ENABLE_INTR_CMDID); htc_start(priv->htc); ath9k_htc_vif_reconfig(priv); ieee80211_wake_queues(priv->hw); mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc; struct ieee80211_channel *channel = hw->conf.channel; struct ath9k_hw_cal_data *caldata = NULL; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; int ret; if (test_bit(OP_INVALID, &priv->op_flags)) return -EIO; fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); ath9k_htc_ps_wakeup(priv); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); ath9k_wmi_event_drain(priv); ath_dbg(common, CONFIG, "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", priv->ah->curchan->channel, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), fastcc); if (!fastcc) caldata = &priv->caldata; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { ath_err(common, "Unable to reset channel (%u Mhz) reset status %d\n", channel->center_freq, ret); goto err; } ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); WMI_CMD(WMI_START_RECV_CMDID); if (ret) goto err; ath9k_host_rx_init(priv); mode = ath9k_htc_get_curmode(priv, hchan); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); if (ret) goto err; WMI_CMD(WMI_ENABLE_INTR_CMDID); if (ret) goto err; htc_start(priv->htc); if (!test_bit(OP_SCANNING, &priv->op_flags) && !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) ath9k_htc_vif_reconfig(priv); mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); err: ath9k_htc_ps_restore(priv); return ret; } /* * Monitor mode handling is a tad complicated because the firmware requires * an interface to be created exclusively, while mac80211 doesn't associate * an interface with the mode. * * So, for now, only one monitor interface can be configured. */ static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); hvif.index = priv->mon_vif_idx; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); if (ret) { ath_err(common, "Unable to remove monitor interface at idx: %d\n", priv->mon_vif_idx); } priv->nvifs--; priv->vif_slot &= ~(1 << priv->mon_vif_idx); } static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; struct ath9k_htc_target_sta tsta; int ret = 0, sta_idx; u8 cmd_rsp; if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) || (priv->nstations >= ATH9K_HTC_MAX_STA)) { ret = -ENOBUFS; goto err_vif; } sta_idx = ffz(priv->sta_slot); if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) { ret = -ENOBUFS; goto err_vif; } /* * Add an interface. */ memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); hvif.opmode = HTC_M_MONITOR; hvif.index = ffz(priv->vif_slot); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto err_vif; /* * Assign the monitor interface index as a special case here. * This is needed when the interface is brought down. */ priv->mon_vif_idx = hvif.index; priv->vif_slot |= (1 << hvif.index); /* * Set the hardware mode to monitor only if there are no * other interfaces. */ if (!priv->nvifs) priv->ah->opmode = NL80211_IFTYPE_MONITOR; priv->nvifs++; /* * Associate a station with the interface for packet injection. */ memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN); tsta.is_vif_sta = 1; tsta.sta_index = sta_idx; tsta.vif_index = hvif.index; tsta.maxampdu = cpu_to_be16(0xffff); WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { ath_err(common, "Unable to add station entry for monitor mode\n"); goto err_sta; } priv->sta_slot |= (1 << sta_idx); priv->nstations++; priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx; priv->ah->is_monitoring = true; ath_dbg(common, CONFIG, "Attached a monitor interface at idx: %d, sta idx: %d\n", priv->mon_vif_idx, sta_idx); return 0; err_sta: /* * Remove the interface from the target. */ __ath9k_htc_remove_monitor_interface(priv); err_vif: ath_dbg(common, FATAL, "Unable to attach a monitor interface\n"); return ret; } static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); int ret = 0; u8 cmd_rsp, sta_idx; __ath9k_htc_remove_monitor_interface(priv); sta_idx = priv->vif_sta_pos[priv->mon_vif_idx]; WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); if (ret) { ath_err(common, "Unable to remove station entry for monitor mode\n"); return ret; } priv->sta_slot &= ~(1 << sta_idx); priv->nstations--; priv->ah->is_monitoring = false; ath_dbg(common, CONFIG, "Removed a monitor interface at idx: %d, sta idx: %d\n", priv->mon_vif_idx, sta_idx); return 0; } static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_sta tsta; struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; int ret, sta_idx; u8 cmd_rsp; u16 maxampdu; if (priv->nstations >= ATH9K_HTC_MAX_STA) return -ENOBUFS; sta_idx = ffz(priv->sta_slot); if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) return -ENOBUFS; memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); if (sta) { ista = (struct ath9k_htc_sta *) sta->drv_priv; memcpy(&tsta.macaddr, sta->addr, ETH_ALEN); memcpy(&tsta.bssid, common->curbssid, ETH_ALEN); ista->index = sta_idx; tsta.is_vif_sta = 0; maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); tsta.maxampdu = cpu_to_be16(maxampdu); } else { memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); tsta.is_vif_sta = 1; tsta.maxampdu = cpu_to_be16(0xffff); } tsta.sta_index = sta_idx; tsta.vif_index = avp->index; WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { if (sta) ath_err(common, "Unable to add station entry for: %pM\n", sta->addr); return ret; } if (sta) { ath_dbg(common, CONFIG, "Added a station entry for: %pM (idx: %d)\n", sta->addr, tsta.sta_index); } else { ath_dbg(common, CONFIG, "Added a station entry for VIF %d (idx: %d)\n", avp->index, tsta.sta_index); } priv->sta_slot |= (1 << sta_idx); priv->nstations++; if (!sta) priv->vif_sta_pos[avp->index] = sta_idx; return 0; } static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; int ret; u8 cmd_rsp, sta_idx; if (sta) { ista = (struct ath9k_htc_sta *) sta->drv_priv; sta_idx = ista->index; } else { sta_idx = priv->vif_sta_pos[avp->index]; } WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); if (ret) { if (sta) ath_err(common, "Unable to remove station entry for: %pM\n", sta->addr); return ret; } if (sta) { ath_dbg(common, CONFIG, "Removed a station entry for: %pM (idx: %d)\n", sta->addr, sta_idx); } else { ath_dbg(common, CONFIG, "Removed a station entry for VIF %d (idx: %d)\n", avp->index, sta_idx); } priv->sta_slot &= ~(1 << sta_idx); priv->nstations--; return 0; } int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv, u8 enable_coex) { struct ath9k_htc_cap_target tcap; int ret; u8 cmd_rsp; memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target)); tcap.ampdu_limit = cpu_to_be32(0xffff); tcap.ampdu_subframes = 0xff; tcap.enable_coex = enable_coex; tcap.tx_chainmask = priv->ah->caps.tx_chainmask; WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap); return ret; } static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv, struct ieee80211_sta *sta, struct ath9k_htc_target_rate *trate) { struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; struct ieee80211_supported_band *sband; u32 caps = 0; int i, j; sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band]; for (i = 0, j = 0; i < sband->n_bitrates; i++) { if (sta->supp_rates[sband->band] & BIT(i)) { trate->rates.legacy_rates.rs_rates[j] = (sband->bitrates[i].bitrate * 2) / 10; j++; } } trate->rates.legacy_rates.rs_nrates = j; if (sta->ht_cap.ht_supported) { for (i = 0, j = 0; i < 77; i++) { if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) trate->rates.ht_rates.rs_rates[j++] = i; if (j == ATH_HTC_RATE_MAX) break; } trate->rates.ht_rates.rs_nrates = j; caps = WLAN_RC_HT_FLAG; if (sta->ht_cap.mcs.rx_mask[1]) caps |= WLAN_RC_DS_FLAG; if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && (conf_is_ht40(&priv->hw->conf))) caps |= WLAN_RC_40_FLAG; if (conf_is_ht40(&priv->hw->conf) && (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) caps |= WLAN_RC_SGI_FLAG; else if (conf_is_ht20(&priv->hw->conf) && (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)) caps |= WLAN_RC_SGI_FLAG; } trate->sta_index = ista->index; trate->isnew = 1; trate->capflags = cpu_to_be32(caps); } static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv, struct ath9k_htc_target_rate *trate) { struct ath_common *common = ath9k_hw_common(priv->ah); int ret; u8 cmd_rsp; WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate); if (ret) { ath_err(common, "Unable to initialize Rate information on target\n"); } return ret; } static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_rate trate; int ret; memset(&trate, 0, sizeof(struct ath9k_htc_target_rate)); ath9k_htc_setup_rate(priv, sta, &trate); ret = ath9k_htc_send_rate_cmd(priv, &trate); if (!ret) ath_dbg(common, CONFIG, "Updated target sta: %pM, rate caps: 0x%X\n", sta->addr, be32_to_cpu(trate.capflags)); } static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_rate trate; struct ieee80211_sta *sta; int ret; memset(&trate, 0, sizeof(struct ath9k_htc_target_rate)); rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!sta) { rcu_read_unlock(); return; } ath9k_htc_setup_rate(priv, sta, &trate); rcu_read_unlock(); ret = ath9k_htc_send_rate_cmd(priv, &trate); if (!ret) ath_dbg(common, CONFIG, "Updated target sta: %pM, rate caps: 0x%X\n", bss_conf->bssid, be32_to_cpu(trate.capflags)); } static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum ieee80211_ampdu_mlme_action action, u16 tid) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_aggr aggr; struct ath9k_htc_sta *ista; int ret = 0; u8 cmd_rsp; if (tid >= ATH9K_HTC_MAX_TID) return -EINVAL; memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr)); ista = (struct ath9k_htc_sta *) sta->drv_priv; aggr.sta_index = ista->index; aggr.tidno = tid & 0xf; aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false; WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr); if (ret) ath_dbg(common, CONFIG, "Unable to %s TX aggregation for (%pM, %d)\n", (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid); else ath_dbg(common, CONFIG, "%s TX aggregation for (%pM, %d)\n", (aggr.aggr_enable) ? "Starting" : "Stopping", sta->addr, tid); spin_lock_bh(&priv->tx.tx_lock); ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP; spin_unlock_bh(&priv->tx.tx_lock); return ret; } /*******/ /* ANI */ /*******/ void ath9k_htc_start_ani(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); unsigned long timestamp = jiffies_to_msecs(jiffies); common->ani.longcal_timer = timestamp; common->ani.shortcal_timer = timestamp; common->ani.checkani_timer = timestamp; set_bit(OP_ANI_RUNNING, &priv->op_flags); ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); } void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv) { cancel_delayed_work_sync(&priv->ani_work); clear_bit(OP_ANI_RUNNING, &priv->op_flags); } void ath9k_htc_ani_work(struct work_struct *work) { struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, ani_work.work); struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); bool longcal = false; bool shortcal = false; bool aniflag = false; unsigned int timestamp = jiffies_to_msecs(jiffies); u32 cal_interval, short_cal_interval; short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; /* Only calibrate if awake */ if (ah->power_mode != ATH9K_PM_AWAKE) goto set_timer; /* Long calibration runs independently of short calibration. */ if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { longcal = true; ath_dbg(common, ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; } /* Short calibration applies only while caldone is false */ if (!common->ani.caldone) { if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; ath_dbg(common, ANI, "shortcal @%lu\n", jiffies); common->ani.shortcal_timer = timestamp; common->ani.resetcal_timer = timestamp; } } else { if ((timestamp - common->ani.resetcal_timer) >= ATH_RESTART_CALINTERVAL) { common->ani.caldone = ath9k_hw_reset_calvalid(ah); if (common->ani.caldone) common->ani.resetcal_timer = timestamp; } } /* Verify whether we must check ANI */ if (ah->config.enable_ani && (timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { aniflag = true; common->ani.checkani_timer = timestamp; } /* Skip all processing if there's nothing to do. */ if (longcal || shortcal || aniflag) { ath9k_htc_ps_wakeup(priv); /* Call ANI routine if necessary */ if (aniflag) ath9k_hw_ani_monitor(ah, ah->curchan); /* Perform calibration if necessary */ if (longcal || shortcal) common->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask, longcal); ath9k_htc_ps_restore(priv); } set_timer: /* * Set timer interval based on previous results. * The interval must be the shortest necessary to satisfy ANI, * short calibration and long calibration. */ cal_interval = ATH_LONG_CALINTERVAL; if (ah->config.enable_ani) cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(cal_interval)); } /**********************/ /* mac80211 Callbacks */ /**********************/ static void ath9k_htc_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); int padpos, padsize, ret, slot; hdr = (struct ieee80211_hdr *) skb->data; /* Add the padding after the header if this is not already done */ padpos = ath9k_cmn_padpos(hdr->frame_control); padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) { ath_dbg(common, XMIT, "No room for padding\n"); goto fail_tx; } skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); } slot = ath9k_htc_tx_get_slot(priv); if (slot < 0) { ath_dbg(common, XMIT, "No free TX slot\n"); goto fail_tx; } ret = ath9k_htc_tx_start(priv, control->sta, skb, slot, false); if (ret != 0) { ath_dbg(common, XMIT, "Tx failed\n"); goto clear_slot; } ath9k_htc_check_stop_queues(priv); return; clear_slot: ath9k_htc_tx_clear_slot(priv, slot); fail_tx: dev_kfree_skb_any(skb); } static int ath9k_htc_start(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *curchan = hw->conf.channel; struct ath9k_channel *init_channel; int ret = 0; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; mutex_lock(&priv->mutex); ath_dbg(common, CONFIG, "Starting driver with initial channel: %d MHz\n", curchan->center_freq); /* Ensure that HW is awake before flushing RX */ ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); WMI_CMD(WMI_FLUSH_RECV_CMDID); /* setup initial channel */ init_channel = ath9k_cmn_get_curchannel(hw, ah); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { ath_err(common, "Unable to reset hardware; reset status %d (freq %u MHz)\n", ret, curchan->center_freq); mutex_unlock(&priv->mutex); return ret; } ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); mode = ath9k_htc_get_curmode(priv, init_channel); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); WMI_CMD(WMI_ATH_INIT_CMDID); WMI_CMD(WMI_START_RECV_CMDID); ath9k_host_rx_init(priv); ret = ath9k_htc_update_cap_target(priv, 0); if (ret) ath_dbg(common, CONFIG, "Failed to update capability in target\n"); clear_bit(OP_INVALID, &priv->op_flags); htc_start(priv->htc); spin_lock_bh(&priv->tx.tx_lock); priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP; spin_unlock_bh(&priv->tx.tx_lock); ieee80211_wake_queues(hw); mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); ath9k_htc_start_btcoex(priv); mutex_unlock(&priv->mutex); return ret; } static void ath9k_htc_stop(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); int ret __attribute__ ((unused)); u8 cmd_rsp; mutex_lock(&priv->mutex); if (test_bit(OP_INVALID, &priv->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&priv->mutex); return; } ath9k_htc_ps_wakeup(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); tasklet_kill(&priv->rx_tasklet); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); ath9k_wmi_event_drain(priv); mutex_unlock(&priv->mutex); /* Cancel all the running timers/work .. */ cancel_work_sync(&priv->fatal_work); cancel_work_sync(&priv->ps_work); #ifdef CONFIG_MAC80211_LEDS cancel_work_sync(&priv->led_work); #endif ath9k_htc_stop_ani(priv); mutex_lock(&priv->mutex); ath9k_htc_stop_btcoex(priv); /* Remove a monitor interface if it's present. */ if (priv->ah->is_monitoring) ath9k_htc_remove_monitor_interface(priv); ath9k_hw_phy_disable(ah); ath9k_hw_disable(ah); ath9k_htc_ps_restore(priv); ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); set_bit(OP_INVALID, &priv->op_flags); ath_dbg(common, CONFIG, "Driver halt\n"); mutex_unlock(&priv->mutex); } static int ath9k_htc_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); if (priv->nvifs >= ATH9K_HTC_MAX_VIF) { mutex_unlock(&priv->mutex); return -ENOBUFS; } if (priv->num_ibss_vif || (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { ath_err(common, "IBSS coexistence with other modes is not allowed\n"); mutex_unlock(&priv->mutex); return -ENOBUFS; } if (((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC)) && ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) { ath_err(common, "Max. number of beaconing interfaces reached\n"); mutex_unlock(&priv->mutex); return -ENOBUFS; } ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); switch (vif->type) { case NL80211_IFTYPE_STATION: hvif.opmode = HTC_M_STA; break; case NL80211_IFTYPE_ADHOC: hvif.opmode = HTC_M_IBSS; break; case NL80211_IFTYPE_AP: hvif.opmode = HTC_M_HOSTAP; break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } /* Index starts from zero on the target */ avp->index = hvif.index = ffz(priv->vif_slot); hvif.rtsthreshold = cpu_to_be16(2304); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto out; /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); if (ret) { WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); goto out; } ath9k_htc_set_bssid_mask(priv, vif); priv->vif_slot |= (1 << avp->index); priv->nvifs++; INC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_assign_bslot(priv, vif); ath9k_htc_set_opmode(priv); if ((priv->ah->opmode == NL80211_IFTYPE_AP) && !test_bit(OP_ANI_RUNNING, &priv->op_flags)) { ath9k_hw_set_tsfadjust(priv->ah, true); ath9k_htc_start_ani(priv); } ath_dbg(common, CONFIG, "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; } static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); hvif.index = avp->index; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); if (ret) { ath_err(common, "Unable to remove interface at idx: %d\n", avp->index); } priv->nvifs--; priv->vif_slot &= ~(1 << avp->index); ath9k_htc_remove_station(priv, vif, NULL); DEC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_remove_bslot(priv, vif); ath9k_htc_set_opmode(priv); ath9k_htc_set_bssid_mask(priv, vif); /* * Stop ANI only if there are no associated station interfaces. */ if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) { priv->rearm_ani = false; ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_vif_iter, priv); if (!priv->rearm_ani) ath9k_htc_stop_ani(priv); } ath_dbg(common, CONFIG, "Detach Interface at idx: %d\n", avp->index); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_conf *conf = &hw->conf; bool chip_reset = false; int ret = 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & IEEE80211_CONF_CHANGE_IDLE) { mutex_lock(&priv->htc_pm_lock); priv->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); if (priv->ps_idle) chip_reset = true; mutex_unlock(&priv->htc_pm_lock); } /* * Monitor interface should be added before * IEEE80211_CONF_CHANGE_CHANNEL is handled. */ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if ((conf->flags & IEEE80211_CONF_MONITOR) && !priv->ah->is_monitoring) ath9k_htc_add_monitor_interface(priv); else if (priv->ah->is_monitoring) ath9k_htc_remove_monitor_interface(priv); } if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; ath_dbg(common, CONFIG, "Set channel: %d MHz\n", curchan->center_freq); ath9k_cmn_update_ichannel(&priv->ah->channels[pos], hw->conf.channel, hw->conf.channel_type); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); ret = -EINVAL; goto out; } } if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); priv->ps_enabled = true; } else { priv->ps_enabled = false; cancel_work_sync(&priv->ps_work); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); } } if (changed & IEEE80211_CONF_CHANGE_POWER) { priv->txpowlimit = 2 * conf->power_level; ath9k_cmn_update_txpow(priv->ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); } out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; } #define SUPPORTED_FILTERS \ (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_CONTROL | \ FIF_PSPOLL | \ FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_PROBE_REQ | \ FIF_FCSFAIL) static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct ath9k_htc_priv *priv = hw->priv; u32 rfilt; mutex_lock(&priv->mutex); changed_flags &= SUPPORTED_FILTERS; *total_flags &= SUPPORTED_FILTERS; if (test_bit(OP_INVALID, &priv->op_flags)) { ath_dbg(ath9k_hw_common(priv->ah), ANY, "Unable to configure filter on invalid state\n"); mutex_unlock(&priv->mutex); return; } ath9k_htc_ps_wakeup(priv); priv->rxfilter = *total_flags; rfilt = ath9k_htc_calcrxfilter(priv); ath9k_hw_setrxfilter(priv->ah, rfilt); ath_dbg(ath9k_hw_common(priv->ah), CONFIG, "Set HW RX filter: 0x%x\n", rfilt); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static int ath9k_htc_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath9k_htc_priv *priv = hw->priv; int ret; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); ret = ath9k_htc_add_station(priv, vif, sta); if (!ret) ath9k_htc_init_rate(priv, sta); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; } static int ath9k_htc_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); ista = (struct ath9k_htc_sta *) sta->drv_priv; htc_sta_drain(priv->htc, ista->index); ret = ath9k_htc_remove_station(priv, vif, sta); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; } static void ath9k_htc_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_rate trate; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { memset(&trate, 0, sizeof(struct ath9k_htc_target_rate)); ath9k_htc_setup_rate(priv, sta, &trate); if (!ath9k_htc_send_rate_cmd(priv, &trate)) ath_dbg(common, CONFIG, "Supported rates for sta: %pM updated, rate caps: 0x%X\n", sta->addr, be32_to_cpu(trate.capflags)); else ath_dbg(common, CONFIG, "Unable to update supported rates for sta: %pM\n", sta->addr); } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_tx_queue_info qi; int ret = 0, qnum; if (queue >= WME_NUM_AC) return 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); qi.tqi_aifs = params->aifs; qi.tqi_cwmin = params->cw_min; qi.tqi_cwmax = params->cw_max; qi.tqi_burstTime = params->txop * 32; qnum = get_hw_qnum(queue, priv->hwq_map); ath_dbg(common, CONFIG, "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", queue, qnum, params->aifs, params->cw_min, params->cw_max, params->txop); ret = ath_htc_txq_update(priv, qnum, &qi); if (ret) { ath_err(common, "TXQ Update failed\n"); goto out; } if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) && (qnum == priv->hwq_map[WME_AC_BE])) ath9k_htc_beaconq_config(priv); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; } static int ath9k_htc_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); int ret = 0; if (htc_modparam_nohwcrypt) return -ENOSPC; if ((vif->type == NL80211_IFTYPE_ADHOC || vif->type == NL80211_IFTYPE_MESH_POINT) && (key->cipher == WLAN_CIPHER_SUITE_TKIP || key->cipher == WLAN_CIPHER_SUITE_CCMP) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { /* * For now, disable hw crypto for the RSN IBSS group keys. This * could be optimized in the future to use a modified key cache * design to support per-STA RX GTK, but until that gets * implemented, use of software crypto for group addressed * frames is a acceptable to allow RSN IBSS to be used. */ return -EOPNOTSUPP; } mutex_lock(&priv->mutex); ath_dbg(common, CONFIG, "Set HW Key\n"); ath9k_htc_ps_wakeup(priv); switch (cmd) { case SET_KEY: ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (priv->ah->sw_mgmt_crypto && key->cipher == WLAN_CIPHER_SUITE_CCMP) key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; ret = 0; } break; case DISABLE_KEY: ath_key_delete(common, key); break; default: ret = -EINVAL; } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; } static void ath9k_htc_set_bssid(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); ath9k_hw_write_associd(priv->ah); ath_dbg(common, CONFIG, "BSSID: %pM aid: 0x%x\n", common->curbssid, common->curaid); } static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; if ((vif->type == NL80211_IFTYPE_STATION) && bss_conf->assoc) { common->curaid = bss_conf->aid; memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); } } static void ath9k_htc_choose_set_bssid(struct ath9k_htc_priv *priv) { if (priv->num_sta_assoc_vif == 1) { ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bss_iter, priv); ath9k_htc_set_bssid(priv); } } static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & BSS_CHANGED_ASSOC) { ath_dbg(common, CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); bss_conf->assoc ? priv->num_sta_assoc_vif++ : priv->num_sta_assoc_vif--; if (priv->ah->opmode == NL80211_IFTYPE_STATION) { ath9k_htc_choose_set_bssid(priv); if (bss_conf->assoc && (priv->num_sta_assoc_vif == 1)) ath9k_htc_start_ani(priv); else if (priv->num_sta_assoc_vif == 0) ath9k_htc_stop_ani(priv); } } if (changed & BSS_CHANGED_IBSS) { if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { common->curaid = bss_conf->aid; memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); ath9k_htc_set_bssid(priv); } } if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) { ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n", bss_conf->bssid); ath9k_htc_set_tsfadjust(priv, vif); set_bit(OP_ENABLE_BEACON, &priv->op_flags); ath9k_htc_beacon_config(priv, vif); } if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { /* * Disable SWBA interrupt only if there are no * AP/IBSS interfaces. */ if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { ath_dbg(common, CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); clear_bit(OP_ENABLE_BEACON, &priv->op_flags); ath9k_htc_beacon_config(priv, vif); } } if (changed & BSS_CHANGED_BEACON_INT) { /* * Reset the HW TSF for the first AP interface. */ if ((priv->ah->opmode == NL80211_IFTYPE_AP) && (priv->nvifs == 1) && (priv->num_ap_vif == 1) && (vif->type == NL80211_IFTYPE_AP)) { set_bit(OP_TSF_RESET, &priv->op_flags); } ath_dbg(common, CONFIG, "Beacon interval changed for BSS: %pM\n", bss_conf->bssid); ath9k_htc_beacon_config(priv, vif); } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ah->slottime = 9; else ah->slottime = 20; ath9k_hw_init_global_settings(ah); } if (changed & BSS_CHANGED_HT) ath9k_htc_update_rate(priv, vif, bss_conf); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; u64 tsf; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); tsf = ath9k_hw_gettsf64(priv->ah); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return tsf; } static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct ath9k_htc_priv *priv = hw->priv; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); ath9k_hw_settsf64(priv->ah, tsf); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); ath9k_hw_reset_tsf(priv->ah); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret = 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ista = (struct ath9k_htc_sta *) sta->drv_priv; spin_lock_bh(&priv->tx.tx_lock); ista->tid_state[tid] = AGGR_OPERATIONAL; spin_unlock_bh(&priv->tx.tx_lock); break; default: ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; } static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; mutex_lock(&priv->mutex); spin_lock_bh(&priv->beacon_lock); set_bit(OP_SCANNING, &priv->op_flags); spin_unlock_bh(&priv->beacon_lock); cancel_work_sync(&priv->ps_work); ath9k_htc_stop_ani(priv); mutex_unlock(&priv->mutex); } static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; mutex_lock(&priv->mutex); spin_lock_bh(&priv->beacon_lock); clear_bit(OP_SCANNING, &priv->op_flags); spin_unlock_bh(&priv->beacon_lock); ath9k_htc_ps_wakeup(priv); ath9k_htc_vif_reconfig(priv); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { return 0; } static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) { struct ath9k_htc_priv *priv = hw->priv; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); priv->ah->coverage_class = coverage_class; ath9k_hw_init_global_settings(priv->ah); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } /* * Currently, this is used only for selecting the minimum rate * for management frames, rate selection for data frames remain * unaffected. */ static int ath9k_htc_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_rate_mask tmask; struct ath9k_htc_vif *avp = (void *)vif->drv_priv; int ret = 0; u8 cmd_rsp; memset(&tmask, 0, sizeof(struct ath9k_htc_target_rate_mask)); tmask.vif_index = avp->index; tmask.band = IEEE80211_BAND_2GHZ; tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_2GHZ].legacy); WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask); if (ret) { ath_err(common, "Unable to set 2G rate mask for " "interface at idx: %d\n", avp->index); goto out; } tmask.band = IEEE80211_BAND_5GHZ; tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_5GHZ].legacy); WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask); if (ret) { ath_err(common, "Unable to set 5G rate mask for " "interface at idx: %d\n", avp->index); goto out; } ath_dbg(common, CONFIG, "Set bitrate masks: 0x%x, 0x%x\n", mask->control[IEEE80211_BAND_2GHZ].legacy, mask->control[IEEE80211_BAND_5GHZ].legacy); out: return ret; } static int ath9k_htc_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats; stats->dot11ACKFailureCount = mib_stats->ackrcv_bad; stats->dot11RTSFailureCount = mib_stats->rts_bad; stats->dot11FCSErrorCount = mib_stats->fcs_bad; stats->dot11RTSSuccessCount = mib_stats->rts_good; return 0; } struct ieee80211_ops ath9k_htc_ops = { .tx = ath9k_htc_tx, .start = ath9k_htc_start, .stop = ath9k_htc_stop, .add_interface = ath9k_htc_add_interface, .remove_interface = ath9k_htc_remove_interface, .config = ath9k_htc_config, .configure_filter = ath9k_htc_configure_filter, .sta_add = ath9k_htc_sta_add, .sta_remove = ath9k_htc_sta_remove, .conf_tx = ath9k_htc_conf_tx, .sta_rc_update = ath9k_htc_sta_rc_update, .bss_info_changed = ath9k_htc_bss_info_changed, .set_key = ath9k_htc_set_key, .get_tsf = ath9k_htc_get_tsf, .set_tsf = ath9k_htc_set_tsf, .reset_tsf = ath9k_htc_reset_tsf, .ampdu_action = ath9k_htc_ampdu_action, .sw_scan_start = ath9k_htc_sw_scan_start, .sw_scan_complete = ath9k_htc_sw_scan_complete, .set_rts_threshold = ath9k_htc_set_rts_threshold, .rfkill_poll = ath9k_htc_rfkill_poll_state, .set_coverage_class = ath9k_htc_set_coverage_class, .set_bitrate_mask = ath9k_htc_set_bitrate_mask, .get_stats = ath9k_htc_get_stats, }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c0000644000175000017500000002174312026211315025077 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "htc.h" /******************/ /* BTCOEX */ /******************/ #define ATH_HTC_BTCOEX_PRODUCT_ID "wb193" #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT /* * Detects if there is any priority bt traffic */ static void ath_detect_bt_priority(struct ath9k_htc_priv *priv) { struct ath_btcoex *btcoex = &priv->btcoex; struct ath_hw *ah = priv->ah; if (ath9k_hw_gpio_get(ah, ah->btcoex_hw.btpriority_gpio)) btcoex->bt_priority_cnt++; if (time_after(jiffies, btcoex->bt_priority_time + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { clear_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); clear_bit(OP_BT_SCAN, &priv->op_flags); /* Detect if colocated bt started scanning */ if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { ath_dbg(ath9k_hw_common(ah), BTCOEX, "BT scan detected\n"); set_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); set_bit(OP_BT_SCAN, &priv->op_flags); } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { ath_dbg(ath9k_hw_common(ah), BTCOEX, "BT priority traffic detected\n"); set_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); } btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; } } /* * This is the master bt coex work which runs for every * 45ms, bt traffic will be given priority during 55% of this * period while wlan gets remaining 45% */ static void ath_btcoex_period_work(struct work_struct *work) { struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, coex_period_work.work); struct ath_btcoex *btcoex = &priv->btcoex; struct ath_common *common = ath9k_hw_common(priv->ah); u32 timer_period; int ret; ath_detect_bt_priority(priv); ret = ath9k_htc_update_cap_target(priv, test_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags)); if (ret) { ath_err(common, "Unable to set BTCOEX parameters\n"); return; } ath9k_hw_btcoex_bt_stomp(priv->ah, test_bit(OP_BT_SCAN, &priv->op_flags) ? ATH_BTCOEX_STOMP_ALL : btcoex->bt_stomp_type); ath9k_hw_btcoex_enable(priv->ah); timer_period = test_bit(OP_BT_SCAN, &priv->op_flags) ? btcoex->btscan_no_stomp : btcoex->btcoex_no_stomp; ieee80211_queue_delayed_work(priv->hw, &priv->duty_cycle_work, msecs_to_jiffies(timer_period)); ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, msecs_to_jiffies(btcoex->btcoex_period)); } /* * Work to time slice between wlan and bt traffic and * configure weight registers */ static void ath_btcoex_duty_cycle_work(struct work_struct *work) { struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, duty_cycle_work.work); struct ath_hw *ah = priv->ah; struct ath_btcoex *btcoex = &priv->btcoex; struct ath_common *common = ath9k_hw_common(ah); ath_dbg(common, BTCOEX, "time slice work for bt and wlan\n"); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || test_bit(OP_BT_SCAN, &priv->op_flags)) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); ath9k_hw_btcoex_enable(priv->ah); } static void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv) { struct ath_btcoex *btcoex = &priv->btcoex; btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD; btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * btcoex->btcoex_period / 100; btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * btcoex->btcoex_period / 100; INIT_DELAYED_WORK(&priv->coex_period_work, ath_btcoex_period_work); INIT_DELAYED_WORK(&priv->duty_cycle_work, ath_btcoex_duty_cycle_work); } /* * (Re)start btcoex work */ static void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv) { struct ath_btcoex *btcoex = &priv->btcoex; struct ath_hw *ah = priv->ah; ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex work\n"); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; clear_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); clear_bit(OP_BT_SCAN, &priv->op_flags); ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, 0); } /* * Cancel btcoex and bt duty cycle work. */ static void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv) { cancel_delayed_work_sync(&priv->coex_period_work); cancel_delayed_work_sync(&priv->duty_cycle_work); } void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) { ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, AR_STOMP_LOW_WLAN_WGHT); ath9k_hw_btcoex_enable(ah); ath_htc_resume_btcoex_work(priv); } } void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; if (ah->btcoex_hw.enabled && ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) { if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) ath_htc_cancel_btcoex_work(priv); ath9k_hw_btcoex_disable(ah); } } void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); int qnum; /* * Check if BTCOEX is globally disabled. */ if (!common->btcoex_enabled) { ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_NONE; return; } if (product && strncmp(product, ATH_HTC_BTCOEX_PRODUCT_ID, 5) == 0) { ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_3WIRE; } switch (ath9k_hw_get_btcoex_scheme(priv->ah)) { case ATH_BTCOEX_CFG_NONE: break; case ATH_BTCOEX_CFG_3WIRE: priv->ah->btcoex_hw.btactive_gpio = 7; priv->ah->btcoex_hw.btpriority_gpio = 6; priv->ah->btcoex_hw.wlanactive_gpio = 8; priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; ath9k_hw_btcoex_init_3wire(priv->ah); ath_htc_init_btcoex_work(priv); qnum = priv->hwq_map[WME_AC_BE]; ath9k_hw_init_btcoex_hw(priv->ah, qnum); break; default: WARN_ON(1); break; } } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ /*******/ /* LED */ /*******/ #ifdef CONFIG_MAC80211_LEDS void ath9k_led_work(struct work_struct *work) { struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, led_work); ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, (priv->brightness == LED_OFF)); } static void ath9k_led_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { struct ath9k_htc_priv *priv = container_of(led_cdev, struct ath9k_htc_priv, led_cdev); /* Not locked, but it's just a tiny green light..*/ priv->brightness = brightness; ieee80211_queue_work(priv->hw, &priv->led_work); } void ath9k_deinit_leds(struct ath9k_htc_priv *priv) { if (!priv->led_registered) return; ath9k_led_brightness(&priv->led_cdev, LED_OFF); led_classdev_unregister(&priv->led_cdev); cancel_work_sync(&priv->led_work); } void ath9k_init_leds(struct ath9k_htc_priv *priv) { int ret; if (AR_SREV_9287(priv->ah)) priv->ah->led_pin = ATH_LED_PIN_9287; else if (AR_SREV_9271(priv->ah)) priv->ah->led_pin = ATH_LED_PIN_9271; else if (AR_DEVID_7010(priv->ah)) priv->ah->led_pin = ATH_LED_PIN_7010; else priv->ah->led_pin = ATH_LED_PIN_DEF; /* Configure gpio 1 for output */ ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); /* LED off, active low */ ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); snprintf(priv->led_name, sizeof(priv->led_name), "ath9k_htc-%s", wiphy_name(priv->hw->wiphy)); priv->led_cdev.name = priv->led_name; priv->led_cdev.brightness_set = ath9k_led_brightness; ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &priv->led_cdev); if (ret < 0) return; INIT_WORK(&priv->led_work, ath9k_led_work); priv->led_registered = true; return; } #endif /*******************/ /* Rfkill */ /*******************/ static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv) { bool is_blocked; ath9k_htc_ps_wakeup(priv); is_blocked = ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == priv->ah->rfkill_polarity; ath9k_htc_ps_restore(priv); return is_blocked; } void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; bool blocked = !!ath_is_rfkill_set(priv); wiphy_rfkill_set_hw_state(hw->wiphy, blocked); } void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv) { if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) wiphy_rfkill_start_polling(priv->hw->wiphy); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_drv_debug.c0000644000175000017500000007355512026211315025237 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "htc.h" static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath9k_htc_target_int_stats cmd_rsp; char buf[512]; unsigned int len = 0; int ret = 0; memset(&cmd_rsp, 0, sizeof(cmd_rsp)); ath9k_htc_ps_wakeup(priv); WMI_CMD(WMI_INT_STATS_CMDID); if (ret) { ath9k_htc_ps_restore(priv); return -EINVAL; } ath9k_htc_ps_restore(priv); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "RX", be32_to_cpu(cmd_rsp.rx)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "RXORN", be32_to_cpu(cmd_rsp.rxorn)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "RXEOL", be32_to_cpu(cmd_rsp.rxeol)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "TXURN", be32_to_cpu(cmd_rsp.txurn)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "TXTO", be32_to_cpu(cmd_rsp.txto)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "CST", be32_to_cpu(cmd_rsp.cst)); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_tgt_int_stats = { .read = read_file_tgt_int_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_tgt_tx_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath9k_htc_target_tx_stats cmd_rsp; char buf[512]; unsigned int len = 0; int ret = 0; memset(&cmd_rsp, 0, sizeof(cmd_rsp)); ath9k_htc_ps_wakeup(priv); WMI_CMD(WMI_TX_STATS_CMDID); if (ret) { ath9k_htc_ps_restore(priv); return -EINVAL; } ath9k_htc_ps_restore(priv); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Xretries", be32_to_cpu(cmd_rsp.xretries)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "FifoErr", be32_to_cpu(cmd_rsp.fifoerr)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Filtered", be32_to_cpu(cmd_rsp.filtered)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "TimerExp", be32_to_cpu(cmd_rsp.timer_exp)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "ShortRetries", be32_to_cpu(cmd_rsp.shortretries)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "LongRetries", be32_to_cpu(cmd_rsp.longretries)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "QueueNull", be32_to_cpu(cmd_rsp.qnull)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "EncapFail", be32_to_cpu(cmd_rsp.encap_fail)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "NoBuf", be32_to_cpu(cmd_rsp.nobuf)); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_tgt_tx_stats = { .read = read_file_tgt_tx_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_tgt_rx_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath9k_htc_target_rx_stats cmd_rsp; char buf[512]; unsigned int len = 0; int ret = 0; memset(&cmd_rsp, 0, sizeof(cmd_rsp)); ath9k_htc_ps_wakeup(priv); WMI_CMD(WMI_RX_STATS_CMDID); if (ret) { ath9k_htc_ps_restore(priv); return -EINVAL; } ath9k_htc_ps_restore(priv); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "NoBuf", be32_to_cpu(cmd_rsp.nobuf)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "HostSend", be32_to_cpu(cmd_rsp.host_send)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "HostDone", be32_to_cpu(cmd_rsp.host_done)); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_tgt_rx_stats = { .read = read_file_tgt_rx_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_xmit(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; char buf[512]; unsigned int len = 0; len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Buffers queued", priv->debug.tx_stats.buf_queued); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Buffers completed", priv->debug.tx_stats.buf_completed); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "SKBs queued", priv->debug.tx_stats.skb_queued); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "SKBs success", priv->debug.tx_stats.skb_success); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "SKBs failed", priv->debug.tx_stats.skb_failed); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "CAB queued", priv->debug.tx_stats.cab_queued); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "BE queued", priv->debug.tx_stats.queue_stats[WME_AC_BE]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "BK queued", priv->debug.tx_stats.queue_stats[WME_AC_BK]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "VI queued", priv->debug.tx_stats.queue_stats[WME_AC_VI]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "VO queued", priv->debug.tx_stats.queue_stats[WME_AC_VO]); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_xmit = { .read = read_file_xmit, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, struct ath_htc_rx_status *rxs) { #define RX_PHY_ERR_INC(c) priv->debug.rx_stats.err_phy_stats[c]++ if (rxs->rs_status & ATH9K_RXERR_CRC) priv->debug.rx_stats.err_crc++; if (rxs->rs_status & ATH9K_RXERR_DECRYPT) priv->debug.rx_stats.err_decrypt_crc++; if (rxs->rs_status & ATH9K_RXERR_MIC) priv->debug.rx_stats.err_mic++; if (rxs->rs_status & ATH9K_RX_DELIM_CRC_PRE) priv->debug.rx_stats.err_pre_delim++; if (rxs->rs_status & ATH9K_RX_DELIM_CRC_POST) priv->debug.rx_stats.err_post_delim++; if (rxs->rs_status & ATH9K_RX_DECRYPT_BUSY) priv->debug.rx_stats.err_decrypt_busy++; if (rxs->rs_status & ATH9K_RXERR_PHY) { priv->debug.rx_stats.err_phy++; if (rxs->rs_phyerr < ATH9K_PHYERR_MAX) RX_PHY_ERR_INC(rxs->rs_phyerr); } #undef RX_PHY_ERR_INC } static ssize_t read_file_recv(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PHY_ERR(s, p) \ len += snprintf(buf + len, size - len, "%20s : %10u\n", s, \ priv->debug.rx_stats.err_phy_stats[p]); struct ath9k_htc_priv *priv = file->private_data; char *buf; unsigned int len = 0, size = 1500; ssize_t retval = 0; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += snprintf(buf + len, size - len, "%20s : %10u\n", "SKBs allocated", priv->debug.rx_stats.skb_allocated); len += snprintf(buf + len, size - len, "%20s : %10u\n", "SKBs completed", priv->debug.rx_stats.skb_completed); len += snprintf(buf + len, size - len, "%20s : %10u\n", "SKBs Dropped", priv->debug.rx_stats.skb_dropped); len += snprintf(buf + len, size - len, "%20s : %10u\n", "CRC ERR", priv->debug.rx_stats.err_crc); len += snprintf(buf + len, size - len, "%20s : %10u\n", "DECRYPT CRC ERR", priv->debug.rx_stats.err_decrypt_crc); len += snprintf(buf + len, size - len, "%20s : %10u\n", "MIC ERR", priv->debug.rx_stats.err_mic); len += snprintf(buf + len, size - len, "%20s : %10u\n", "PRE-DELIM CRC ERR", priv->debug.rx_stats.err_pre_delim); len += snprintf(buf + len, size - len, "%20s : %10u\n", "POST-DELIM CRC ERR", priv->debug.rx_stats.err_post_delim); len += snprintf(buf + len, size - len, "%20s : %10u\n", "DECRYPT BUSY ERR", priv->debug.rx_stats.err_decrypt_busy); len += snprintf(buf + len, size - len, "%20s : %10u\n", "TOTAL PHY ERR", priv->debug.rx_stats.err_phy); PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN); PHY_ERR("TIMING", ATH9K_PHYERR_TIMING); PHY_ERR("PARITY", ATH9K_PHYERR_PARITY); PHY_ERR("RATE", ATH9K_PHYERR_RATE); PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH); PHY_ERR("RADAR", ATH9K_PHYERR_RADAR); PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE); PHY_ERR("TOR", ATH9K_PHYERR_TOR); PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING); PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP); PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE); PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART); PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT); PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING); PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC); PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL); PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE); PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART); PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP); PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR); PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; #undef PHY_ERR } static const struct file_operations fops_recv = { .read = read_file_recv, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_slot(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; char buf[512]; unsigned int len = 0; spin_lock_bh(&priv->tx.tx_lock); len += snprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : "); len += bitmap_scnprintf(buf + len, sizeof(buf) - len, priv->tx.tx_slot, MAX_TX_BUF_NUM); len += snprintf(buf + len, sizeof(buf) - len, "\n"); len += snprintf(buf + len, sizeof(buf) - len, "Used slots : %d\n", bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM)); spin_unlock_bh(&priv->tx.tx_lock); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_slot = { .read = read_file_slot, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_queue(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; char buf[512]; unsigned int len = 0; len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Mgmt endpoint", skb_queue_len(&priv->tx.mgmt_ep_queue)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Cab endpoint", skb_queue_len(&priv->tx.cab_ep_queue)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Data BE endpoint", skb_queue_len(&priv->tx.data_be_queue)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Data BK endpoint", skb_queue_len(&priv->tx.data_bk_queue)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Data VI endpoint", skb_queue_len(&priv->tx.data_vi_queue)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Data VO endpoint", skb_queue_len(&priv->tx.data_vo_queue)); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Failed queue", skb_queue_len(&priv->tx.tx_failed)); spin_lock_bh(&priv->tx.tx_lock); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "Queued count", priv->tx.queued_cnt); spin_unlock_bh(&priv->tx.tx_lock); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_queue = { .read = read_file_queue, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_debug(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath_common *common = ath9k_hw_common(priv->ah); char buf[32]; unsigned int len; len = sprintf(buf, "0x%08x\n", common->debug_mask); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_debug(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath_common *common = ath9k_hw_common(priv->ah); unsigned long mask; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, &mask)) return -EINVAL; common->debug_mask = mask; return count; } static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath_common *common = ath9k_hw_common(priv->ah); struct base_eep_header *pBase = NULL; unsigned int len = 0, size = 1500; ssize_t retval = 0; char *buf; /* * This can be done since all the 3 EEPROM families have the * same base header upto a certain point, and we are interested in * the data only upto that point. */ if (AR_SREV_9271(priv->ah)) pBase = (struct base_eep_header *) &priv->ah->eeprom.map4k.baseEepHeader; else if (priv->ah->hw_version.usbdev == AR9280_USB) pBase = (struct base_eep_header *) &priv->ah->eeprom.def.baseEepHeader; else if (priv->ah->hw_version.usbdev == AR9287_USB) pBase = (struct base_eep_header *) &priv->ah->eeprom.map9287.baseEepHeader; if (pBase == NULL) { ath_err(common, "Unknown EEPROM type\n"); return 0; } buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += snprintf(buf + len, size - len, "%20s : %10d\n", "Major Version", pBase->version >> 12); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Minor Version", pBase->version & 0xFFF); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Checksum", pBase->checksum); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Length", pBase->length); len += snprintf(buf + len, size - len, "%20s : %10d\n", "RegDomain1", pBase->regDmn[0]); len += snprintf(buf + len, size - len, "%20s : %10d\n", "RegDomain2", pBase->regDmn[1]); len += snprintf(buf + len, size - len, "%20s : %10d\n", "TX Mask", pBase->txMask); len += snprintf(buf + len, size - len, "%20s : %10d\n", "RX Mask", pBase->rxMask); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 2GHz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 2GHz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 5Ghz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 5Ghz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Big Endian", !!(pBase->eepMisc & 0x01)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); /* * UB91 specific data. */ if (AR_SREV_9271(priv->ah)) { struct base_eep_header_4k *pBase4k = &priv->ah->eeprom.map4k.baseEepHeader; len += snprintf(buf + len, size - len, "%20s : %10d\n", "TX Gain type", pBase4k->txGainType); } /* * UB95 specific data. */ if (priv->ah->hw_version.usbdev == AR9287_USB) { struct base_eep_ar9287_header *pBase9287 = &priv->ah->eeprom.map9287.baseEepHeader; len += snprintf(buf + len, size - len, "%20s : %10ddB\n", "Power Table Offset", pBase9287->pwrTableOffset); len += snprintf(buf + len, size - len, "%20s : %10d\n", "OpenLoop Power Ctrl", pBase9287->openLoopPwrCntl); } len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", pBase->macAddr); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static const struct file_operations fops_base_eeprom = { .read = read_file_base_eeprom, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_4k_modal_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PR_EEP(_s, _val) \ do { \ len += snprintf(buf + len, size - len, "%20s : %10d\n", \ _s, (_val)); \ } while (0) struct ath9k_htc_priv *priv = file->private_data; struct modal_eep_4k_header *pModal = &priv->ah->eeprom.map4k.modalHeader; unsigned int len = 0, size = 2048; ssize_t retval = 0; char *buf; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); PR_EEP("Ant. Common Control", pModal->antCtrlCommon); PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); PR_EEP("Switch Settle", pModal->switchSettling); PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); PR_EEP("ADC Desired size", pModal->adcDesiredSize); PR_EEP("PGA Desired size", pModal->pgaDesiredSize); PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]); PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); PR_EEP("txEndToRxOn", pModal->txEndToRxOn); PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); PR_EEP("CCA Threshold)", pModal->thresh62); PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); PR_EEP("xpdGain", pModal->xpdGain); PR_EEP("External PD", pModal->xpd); PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); PR_EEP("pdGainOverlap", pModal->pdGainOverlap); PR_EEP("O/D Bias Version", pModal->version); PR_EEP("CCK OutputBias", pModal->ob_0); PR_EEP("BPSK OutputBias", pModal->ob_1); PR_EEP("QPSK OutputBias", pModal->ob_2); PR_EEP("16QAM OutputBias", pModal->ob_3); PR_EEP("64QAM OutputBias", pModal->ob_4); PR_EEP("CCK Driver1_Bias", pModal->db1_0); PR_EEP("BPSK Driver1_Bias", pModal->db1_1); PR_EEP("QPSK Driver1_Bias", pModal->db1_2); PR_EEP("16QAM Driver1_Bias", pModal->db1_3); PR_EEP("64QAM Driver1_Bias", pModal->db1_4); PR_EEP("CCK Driver2_Bias", pModal->db2_0); PR_EEP("BPSK Driver2_Bias", pModal->db2_1); PR_EEP("QPSK Driver2_Bias", pModal->db2_2); PR_EEP("16QAM Driver2_Bias", pModal->db2_3); PR_EEP("64QAM Driver2_Bias", pModal->db2_4); PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]); PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]); PR_EEP("Ant. Diversity ctl1", pModal->antdiv_ctl1); PR_EEP("Ant. Diversity ctl2", pModal->antdiv_ctl2); PR_EEP("TX Diversity", pModal->tx_diversity); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; #undef PR_EEP } static ssize_t read_def_modal_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PR_EEP(_s, _val) \ do { \ if (pBase->opCapFlags & AR5416_OPFLAGS_11G) { \ pModal = &priv->ah->eeprom.def.modalHeader[1]; \ len += snprintf(buf + len, size - len, "%20s : %8d%7s", \ _s, (_val), "|"); \ } \ if (pBase->opCapFlags & AR5416_OPFLAGS_11A) { \ pModal = &priv->ah->eeprom.def.modalHeader[0]; \ len += snprintf(buf + len, size - len, "%9d\n", \ (_val)); \ } \ } while (0) struct ath9k_htc_priv *priv = file->private_data; struct base_eep_header *pBase = &priv->ah->eeprom.def.baseEepHeader; struct modal_eep_header *pModal = NULL; unsigned int len = 0, size = 3500; ssize_t retval = 0; char *buf; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += snprintf(buf + len, size - len, "%31s %15s\n", "2G", "5G"); len += snprintf(buf + len, size - len, "%32s %16s\n", "====", "====\n"); PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]); PR_EEP("Chain2 Ant. Control", pModal->antCtrlChain[2]); PR_EEP("Ant. Common Control", pModal->antCtrlCommon); PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]); PR_EEP("Chain2 Ant. Gain", pModal->antennaGainCh[2]); PR_EEP("Switch Settle", pModal->switchSettling); PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]); PR_EEP("Chain2 TxRxAtten", pModal->txRxAttenCh[2]); PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]); PR_EEP("Chain2 RxTxMargin", pModal->rxTxMarginCh[2]); PR_EEP("ADC Desired size", pModal->adcDesiredSize); PR_EEP("PGA Desired size", pModal->pgaDesiredSize); PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]); PR_EEP("Chain1 xlna Gain", pModal->xlnaGainCh[1]); PR_EEP("Chain2 xlna Gain", pModal->xlnaGainCh[2]); PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); PR_EEP("txEndToRxOn", pModal->txEndToRxOn); PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); PR_EEP("CCA Threshold)", pModal->thresh62); PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]); PR_EEP("Chain2 NF Threshold", pModal->noiseFloorThreshCh[2]); PR_EEP("xpdGain", pModal->xpdGain); PR_EEP("External PD", pModal->xpd); PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]); PR_EEP("Chain2 I Coefficient", pModal->iqCalICh[2]); PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]); PR_EEP("Chain2 Q Coefficient", pModal->iqCalQCh[2]); PR_EEP("pdGainOverlap", pModal->pdGainOverlap); PR_EEP("Chain0 OutputBias", pModal->ob); PR_EEP("Chain0 DriverBias", pModal->db); PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); PR_EEP("2chain pwr decrease", pModal->pwrDecreaseFor2Chain); PR_EEP("3chain pwr decrease", pModal->pwrDecreaseFor3Chain); PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]); PR_EEP("Chain2 bswAtten", pModal->bswAtten[2]); PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]); PR_EEP("Chain2 bswMargin", pModal->bswMargin[2]); PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]); PR_EEP("Chain1 xatten2Db", pModal->xatten2Db[1]); PR_EEP("Chain2 xatten2Db", pModal->xatten2Db[2]); PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]); PR_EEP("Chain1 xatten2Margin", pModal->xatten2Margin[1]); PR_EEP("Chain2 xatten2Margin", pModal->xatten2Margin[2]); PR_EEP("Chain1 OutputBias", pModal->ob_ch1); PR_EEP("Chain1 DriverBias", pModal->db_ch1); PR_EEP("LNA Control", pModal->lna_ctl); PR_EEP("XPA Bias Freq0", pModal->xpaBiasLvlFreq[0]); PR_EEP("XPA Bias Freq1", pModal->xpaBiasLvlFreq[1]); PR_EEP("XPA Bias Freq2", pModal->xpaBiasLvlFreq[2]); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; #undef PR_EEP } static ssize_t read_9287_modal_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PR_EEP(_s, _val) \ do { \ len += snprintf(buf + len, size - len, "%20s : %10d\n", \ _s, (_val)); \ } while (0) struct ath9k_htc_priv *priv = file->private_data; struct modal_eep_ar9287_header *pModal = &priv->ah->eeprom.map9287.modalHeader; unsigned int len = 0, size = 3000; ssize_t retval = 0; char *buf; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]); PR_EEP("Ant. Common Control", pModal->antCtrlCommon); PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]); PR_EEP("Switch Settle", pModal->switchSettling); PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]); PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]); PR_EEP("ADC Desired size", pModal->adcDesiredSize); PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); PR_EEP("txEndToRxOn", pModal->txEndToRxOn); PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); PR_EEP("CCA Threshold)", pModal->thresh62); PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]); PR_EEP("xpdGain", pModal->xpdGain); PR_EEP("External PD", pModal->xpd); PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]); PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]); PR_EEP("pdGainOverlap", pModal->pdGainOverlap); PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]); PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]); PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); PR_EEP("AR92x7 Version", pModal->version); PR_EEP("DriverBias1", pModal->db1); PR_EEP("DriverBias2", pModal->db1); PR_EEP("CCK OutputBias", pModal->ob_cck); PR_EEP("PSK OutputBias", pModal->ob_psk); PR_EEP("QAM OutputBias", pModal->ob_qam); PR_EEP("PAL_OFF OutputBias", pModal->ob_pal_off); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; #undef PR_EEP } static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; if (AR_SREV_9271(priv->ah)) return read_4k_modal_eeprom(file, user_buf, count, ppos); else if (priv->ah->hw_version.usbdev == AR9280_USB) return read_def_modal_eeprom(file, user_buf, count, ppos); else if (priv->ah->hw_version.usbdev == AR9287_USB) return read_9287_modal_eeprom(file, user_buf, count, ppos); return 0; } static const struct file_operations fops_modal_eeprom = { .read = read_file_modal_eeprom, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; int ath9k_htc_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; priv->debug.debugfs_phy = debugfs_create_dir(KBUILD_MODNAME, priv->hw->wiphy->debugfsdir); if (!priv->debug.debugfs_phy) return -ENOMEM; debugfs_create_file("tgt_int_stats", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_tgt_int_stats); debugfs_create_file("tgt_tx_stats", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_tgt_tx_stats); debugfs_create_file("tgt_rx_stats", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_tgt_rx_stats); debugfs_create_file("xmit", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_xmit); debugfs_create_file("recv", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_recv); debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_slot); debugfs_create_file("queue", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_queue); debugfs_create_file("debug", S_IRUSR | S_IWUSR, priv->debug.debugfs_phy, priv, &fops_debug); debugfs_create_file("base_eeprom", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_base_eeprom); debugfs_create_file("modal_eeprom", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_modal_eeprom); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c0000644000175000017500000004366312026211315025375 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "htc.h" #define FUDGE 2 void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; struct ath9k_tx_queue_info qi, qi_be; memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info)); ath9k_hw_get_txq_props(ah, priv->beaconq, &qi); if (priv->ah->opmode == NL80211_IFTYPE_AP) { qi.tqi_aifs = 1; qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; } else if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { int qnum = priv->hwq_map[WME_AC_BE]; ath9k_hw_get_txq_props(ah, qnum, &qi_be); qi.tqi_aifs = qi_be.tqi_aifs; /* * For WIFI Beacon Distribution * Long slot time : 2x cwmin * Short slot time : 4x cwmin */ if (ah->slottime == ATH9K_SLOT_TIME_20) qi.tqi_cwmin = 2*qi_be.tqi_cwmin; else qi.tqi_cwmin = 4*qi_be.tqi_cwmin; qi.tqi_cwmax = qi_be.tqi_cwmax; } if (!ath9k_hw_set_txq_props(ah, priv->beaconq, &qi)) { ath_err(ath9k_hw_common(ah), "Unable to update beacon queue %u!\n", priv->beaconq); } else { ath9k_hw_resettxqueue(ah, priv->beaconq); } } static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, struct htc_beacon_config *bss_conf) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_beacon_state bs; enum ath9k_int imask = 0; int dtimperiod, dtimcount, sleepduration; int cfpperiod, cfpcount, bmiss_timeout; u32 nexttbtt = 0, intval, tsftu; __be32 htc_imask = 0; u64 tsf; int num_beacons, offset, dtim_dec_count, cfp_dec_count; int ret __attribute__ ((unused)); u8 cmd_rsp; memset(&bs, 0, sizeof(bs)); intval = bss_conf->beacon_interval; bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval); /* * Setup dtim and cfp parameters according to * last beacon we received (which may be none). */ dtimperiod = bss_conf->dtim_period; if (dtimperiod <= 0) /* NB: 0 if not known */ dtimperiod = 1; dtimcount = 1; if (dtimcount >= dtimperiod) /* NB: sanity check */ dtimcount = 0; cfpperiod = 1; /* NB: no PCF support yet */ cfpcount = 0; sleepduration = intval; if (sleepduration <= 0) sleepduration = intval; /* * Pull nexttbtt forward to reflect the current * TSF and calculate dtim+cfp state for the result. */ tsf = ath9k_hw_gettsf64(priv->ah); tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; num_beacons = tsftu / intval + 1; offset = tsftu % intval; nexttbtt = tsftu - offset; if (offset) nexttbtt += intval; /* DTIM Beacon every dtimperiod Beacon */ dtim_dec_count = num_beacons % dtimperiod; /* CFP every cfpperiod DTIM Beacon */ cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; if (dtim_dec_count) cfp_dec_count++; dtimcount -= dtim_dec_count; if (dtimcount < 0) dtimcount += dtimperiod; cfpcount -= cfp_dec_count; if (cfpcount < 0) cfpcount += cfpperiod; bs.bs_intval = intval; bs.bs_nexttbtt = nexttbtt; bs.bs_dtimperiod = dtimperiod*intval; bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; bs.bs_cfpmaxduration = 0; /* * Calculate the number of consecutive beacons to miss* before taking * a BMISS interrupt. The configuration is specified in TU so we only * need calculate based on the beacon interval. Note that we clamp the * result to at most 15 beacons. */ if (sleepduration > intval) { bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2; } else { bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval); if (bs.bs_bmissthreshold > 15) bs.bs_bmissthreshold = 15; else if (bs.bs_bmissthreshold <= 0) bs.bs_bmissthreshold = 1; } /* * Calculate sleep duration. The configuration is given in ms. * We ensure a multiple of the beacon period is used. Also, if the sleep * duration is greater than the DTIM period then it makes senses * to make it a multiple of that. * * XXX fixed at 100ms */ bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); if (bs.bs_sleepduration > bs.bs_dtimperiod) bs.bs_sleepduration = bs.bs_dtimperiod; /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; ath_dbg(common, CONFIG, "intval: %u tsf: %llu tsftu: %u\n", intval, tsf, tsftu); ath_dbg(common, CONFIG, "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", bs.bs_bmissthreshold, bs.bs_sleepduration, bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); /* Set the computed STA beacon timers */ WMI_CMD(WMI_DISABLE_INTR_CMDID); ath9k_hw_set_sta_beacon_timers(priv->ah, &bs); imask |= ATH9K_INT_BMISS; htc_imask = cpu_to_be32(imask); WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); } static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, struct htc_beacon_config *bss_conf) { struct ath_common *common = ath9k_hw_common(priv->ah); enum ath9k_int imask = 0; u32 nexttbtt, intval, tsftu; __be32 htc_imask = 0; int ret __attribute__ ((unused)); u8 cmd_rsp; u64 tsf; intval = bss_conf->beacon_interval; intval /= ATH9K_HTC_MAX_BCN_VIF; nexttbtt = intval; /* * To reduce beacon misses under heavy TX load, * set the beacon response time to a larger value. */ if (intval > DEFAULT_SWBA_RESPONSE) priv->ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE; else priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; if (test_bit(OP_TSF_RESET, &priv->op_flags)) { ath9k_hw_reset_tsf(priv->ah); clear_bit(OP_TSF_RESET, &priv->op_flags); } else { /* * Pull nexttbtt forward to reflect the current TSF. */ tsf = ath9k_hw_gettsf64(priv->ah); tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; do { nexttbtt += intval; } while (nexttbtt < tsftu); } if (test_bit(OP_ENABLE_BEACON, &priv->op_flags)) imask |= ATH9K_INT_SWBA; ath_dbg(common, CONFIG, "AP Beacon config, intval: %d, nexttbtt: %u, resp_time: %d imask: 0x%x\n", bss_conf->beacon_interval, nexttbtt, priv->ah->config.sw_beacon_response_time, imask); ath9k_htc_beaconq_config(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval)); priv->cur_beacon_conf.bmiss_cnt = 0; htc_imask = cpu_to_be32(imask); WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); } static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, struct htc_beacon_config *bss_conf) { struct ath_common *common = ath9k_hw_common(priv->ah); enum ath9k_int imask = 0; u32 nexttbtt, intval, tsftu; __be32 htc_imask = 0; int ret __attribute__ ((unused)); u8 cmd_rsp; u64 tsf; intval = bss_conf->beacon_interval; nexttbtt = intval; /* * Pull nexttbtt forward to reflect the current TSF. */ tsf = ath9k_hw_gettsf64(priv->ah); tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; do { nexttbtt += intval; } while (nexttbtt < tsftu); /* * Only one IBSS interfce is allowed. */ if (intval > DEFAULT_SWBA_RESPONSE) priv->ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE; else priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; if (test_bit(OP_ENABLE_BEACON, &priv->op_flags)) imask |= ATH9K_INT_SWBA; ath_dbg(common, CONFIG, "IBSS Beacon config, intval: %d, nexttbtt: %u, resp_time: %d, imask: 0x%x\n", bss_conf->beacon_interval, nexttbtt, priv->ah->config.sw_beacon_response_time, imask); WMI_CMD(WMI_DISABLE_INTR_CMDID); ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval)); priv->cur_beacon_conf.bmiss_cnt = 0; htc_imask = cpu_to_be32(imask); WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); } void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, bool txok) { dev_kfree_skb_any(skb); } static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv, int slot) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_vif *vif; struct sk_buff *skb; struct ieee80211_hdr *hdr; int padpos, padsize, ret, tx_slot; spin_lock_bh(&priv->beacon_lock); vif = priv->cur_beacon_conf.bslot[slot]; skb = ieee80211_get_buffered_bc(priv->hw, vif); while(skb) { hdr = (struct ieee80211_hdr *) skb->data; padpos = ath9k_cmn_padpos(hdr->frame_control); padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) { dev_kfree_skb_any(skb); goto next; } skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); } tx_slot = ath9k_htc_tx_get_slot(priv); if (tx_slot < 0) { ath_dbg(common, XMIT, "No free CAB slot\n"); dev_kfree_skb_any(skb); goto next; } ret = ath9k_htc_tx_start(priv, NULL, skb, tx_slot, true); if (ret != 0) { ath9k_htc_tx_clear_slot(priv, tx_slot); dev_kfree_skb_any(skb); ath_dbg(common, XMIT, "Failed to send CAB frame\n"); } else { spin_lock_bh(&priv->tx.tx_lock); priv->tx.queued_cnt++; spin_unlock_bh(&priv->tx.tx_lock); } next: skb = ieee80211_get_buffered_bc(priv->hw, vif); } spin_unlock_bh(&priv->beacon_lock); } static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv, int slot) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_vif *vif; struct ath9k_htc_vif *avp; struct tx_beacon_header beacon_hdr; struct ath9k_htc_tx_ctl *tx_ctl; struct ieee80211_tx_info *info; struct ieee80211_mgmt *mgmt; struct sk_buff *beacon; u8 *tx_fhdr; int ret; memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); spin_lock_bh(&priv->beacon_lock); vif = priv->cur_beacon_conf.bslot[slot]; avp = (struct ath9k_htc_vif *)vif->drv_priv; if (unlikely(test_bit(OP_SCANNING, &priv->op_flags))) { spin_unlock_bh(&priv->beacon_lock); return; } /* Get a new beacon */ beacon = ieee80211_beacon_get(priv->hw, vif); if (!beacon) { spin_unlock_bh(&priv->beacon_lock); return; } /* * Update the TSF adjust value here, the HW will * add this value for every beacon. */ mgmt = (struct ieee80211_mgmt *)beacon->data; mgmt->u.beacon.timestamp = avp->tsfadjust; info = IEEE80211_SKB_CB(beacon); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) beacon->data; avp->seq_no += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(avp->seq_no); } tx_ctl = HTC_SKB_CB(beacon); memset(tx_ctl, 0, sizeof(*tx_ctl)); tx_ctl->type = ATH9K_HTC_BEACON; tx_ctl->epid = priv->beacon_ep; beacon_hdr.vif_index = avp->index; tx_fhdr = skb_push(beacon, sizeof(beacon_hdr)); memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); ret = htc_send(priv->htc, beacon); if (ret != 0) { if (ret == -ENOMEM) { ath_dbg(common, BSTUCK, "Failed to send beacon, no free TX buffer\n"); } dev_kfree_skb_any(beacon); } spin_unlock_bh(&priv->beacon_lock); } static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv, struct wmi_event_swba *swba) { struct ath_common *common = ath9k_hw_common(priv->ah); u64 tsf; u32 tsftu; u16 intval; int slot; intval = priv->cur_beacon_conf.beacon_interval; tsf = be64_to_cpu(swba->tsf); tsftu = TSF_TO_TU(tsf >> 32, tsf); slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval; slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1; ath_dbg(common, BEACON, "Choose slot: %d, tsf: %llu, tsftu: %u, intval: %u\n", slot, tsf, tsftu, intval); return slot; } void ath9k_htc_swba(struct ath9k_htc_priv *priv, struct wmi_event_swba *swba) { struct ath_common *common = ath9k_hw_common(priv->ah); int slot; if (swba->beacon_pending != 0) { priv->cur_beacon_conf.bmiss_cnt++; if (priv->cur_beacon_conf.bmiss_cnt > BSTUCK_THRESHOLD) { ath_dbg(common, BSTUCK, "Beacon stuck, HW reset\n"); ieee80211_queue_work(priv->hw, &priv->fatal_work); } return; } if (priv->cur_beacon_conf.bmiss_cnt) { ath_dbg(common, BSTUCK, "Resuming beacon xmit after %u misses\n", priv->cur_beacon_conf.bmiss_cnt); priv->cur_beacon_conf.bmiss_cnt = 0; } slot = ath9k_htc_choose_bslot(priv, swba); spin_lock_bh(&priv->beacon_lock); if (priv->cur_beacon_conf.bslot[slot] == NULL) { spin_unlock_bh(&priv->beacon_lock); return; } spin_unlock_bh(&priv->beacon_lock); ath9k_htc_send_buffered(priv, slot); ath9k_htc_send_beacon(priv, slot); } void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; int i = 0; spin_lock_bh(&priv->beacon_lock); for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) { if (priv->cur_beacon_conf.bslot[i] == NULL) { avp->bslot = i; break; } } priv->cur_beacon_conf.bslot[avp->bslot] = vif; spin_unlock_bh(&priv->beacon_lock); ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n", avp->bslot); } void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; spin_lock_bh(&priv->beacon_lock); priv->cur_beacon_conf.bslot[avp->bslot] = NULL; spin_unlock_bh(&priv->beacon_lock); ath_dbg(common, CONFIG, "Removed interface at beacon slot: %d\n", avp->bslot); } /* * Calculate the TSF adjustment value for all slots * other than zero. */ void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; u64 tsfadjust; if (avp->bslot == 0) return; /* * The beacon interval cannot be different for multi-AP mode, * and we reach here only for VIF slots greater than zero, * so beacon_interval is guaranteed to be set in cur_conf. */ tsfadjust = cur_conf->beacon_interval * avp->bslot / ATH9K_HTC_MAX_BCN_VIF; avp->tsfadjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n", (unsigned long long)tsfadjust, avp->bslot); } static void ath9k_htc_beacon_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { bool *beacon_configured = (bool *)data; struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; if (vif->type == NL80211_IFTYPE_STATION && avp->beacon_configured) *beacon_configured = true; } static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; bool beacon_configured; /* * Changing the beacon interval when multiple AP interfaces * are configured will affect beacon transmission of all * of them. */ if ((priv->ah->opmode == NL80211_IFTYPE_AP) && (priv->num_ap_vif > 1) && (vif->type == NL80211_IFTYPE_AP) && (cur_conf->beacon_interval != bss_conf->beacon_int)) { ath_dbg(common, CONFIG, "Changing beacon interval of multiple AP interfaces !\n"); return false; } /* * If the HW is operating in AP mode, any new station interfaces that * are added cannot change the beacon parameters. */ if (priv->num_ap_vif && (vif->type != NL80211_IFTYPE_AP)) { ath_dbg(common, CONFIG, "HW in AP mode, cannot set STA beacon parameters\n"); return false; } /* * The beacon parameters are configured only for the first * station interface. */ if ((priv->ah->opmode == NL80211_IFTYPE_STATION) && (priv->num_sta_vif > 1) && (vif->type == NL80211_IFTYPE_STATION)) { beacon_configured = false; ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_beacon_iter, &beacon_configured); if (beacon_configured) { ath_dbg(common, CONFIG, "Beacon already configured for a station interface\n"); return false; } } return true; } void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; if (!ath9k_htc_check_beacon_config(priv, vif)) return; cur_conf->beacon_interval = bss_conf->beacon_int; if (cur_conf->beacon_interval == 0) cur_conf->beacon_interval = 100; cur_conf->dtim_period = bss_conf->dtim_period; cur_conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; switch (vif->type) { case NL80211_IFTYPE_STATION: ath9k_htc_beacon_config_sta(priv, cur_conf); avp->beacon_configured = true; break; case NL80211_IFTYPE_ADHOC: ath9k_htc_beacon_config_adhoc(priv, cur_conf); break; case NL80211_IFTYPE_AP: ath9k_htc_beacon_config_ap(priv, cur_conf); break; default: ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); return; } } void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; switch (priv->ah->opmode) { case NL80211_IFTYPE_STATION: ath9k_htc_beacon_config_sta(priv, cur_conf); break; case NL80211_IFTYPE_ADHOC: ath9k_htc_beacon_config_adhoc(priv, cur_conf); break; case NL80211_IFTYPE_AP: ath9k_htc_beacon_config_ap(priv, cur_conf); break; default: ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); return; } } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/hif_usb.h0000644000175000017500000000566112026211315024055 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HTC_USB_H #define HTC_USB_H #define MAJOR_VERSION_REQ 1 #define MINOR_VERSION_REQ 3 #define IS_AR7010_DEVICE(_v) (((_v) == AR9280_USB) || ((_v) == AR9287_USB)) #define AR9271_FIRMWARE 0x501000 #define AR9271_FIRMWARE_TEXT 0x903000 #define AR7010_FIRMWARE_TEXT 0x906000 #define FIRMWARE_DOWNLOAD 0x30 #define FIRMWARE_DOWNLOAD_COMP 0x31 #define ATH_USB_RX_STREAM_MODE_TAG 0x4e00 #define ATH_USB_TX_STREAM_MODE_TAG 0x697e /* FIXME: Verify these numbers (with Windows) */ #define MAX_TX_URB_NUM 8 #define MAX_TX_BUF_NUM 256 #define MAX_TX_BUF_SIZE 32768 #define MAX_TX_AGGR_NUM 20 #define MAX_RX_URB_NUM 8 #define MAX_RX_BUF_SIZE 16384 #define MAX_PKT_NUM_IN_TRANSFER 10 #define MAX_REG_OUT_URB_NUM 1 #define MAX_REG_IN_URB_NUM 64 #define MAX_REG_IN_BUF_SIZE 64 /* USB Endpoint definition */ #define USB_WLAN_TX_PIPE 1 #define USB_WLAN_RX_PIPE 2 #define USB_REG_IN_PIPE 3 #define USB_REG_OUT_PIPE 4 #define HIF_USB_MAX_RXPIPES 2 #define HIF_USB_MAX_TXPIPES 4 struct tx_buf { u8 *buf; u16 len; u16 offset; struct urb *urb; struct sk_buff_head skb_queue; struct hif_device_usb *hif_dev; struct list_head list; }; #define HIF_USB_TX_STOP BIT(0) #define HIF_USB_TX_FLUSH BIT(1) struct hif_usb_tx { u8 flags; u8 tx_buf_cnt; u16 tx_skb_cnt; struct sk_buff_head tx_skb_queue; struct list_head tx_buf; struct list_head tx_pending; spinlock_t tx_lock; }; struct cmd_buf { struct sk_buff *skb; struct hif_device_usb *hif_dev; }; #define HIF_USB_START BIT(0) #define HIF_USB_READY BIT(1) struct hif_device_usb { struct usb_device *udev; struct usb_interface *interface; const struct usb_device_id *usb_device_id; const void *fw_data; size_t fw_size; struct completion fw_done; struct htc_target *htc_handle; struct hif_usb_tx tx; struct usb_anchor regout_submitted; struct usb_anchor rx_submitted; struct usb_anchor reg_in_submitted; struct usb_anchor mgmt_submitted; struct sk_buff *remain_skb; const char *fw_name; int rx_remain_len; int rx_pkt_len; int rx_transfer_len; int rx_pad_len; spinlock_t rx_lock; u8 flags; /* HIF_USB_* */ }; int ath9k_hif_usb_init(void); void ath9k_hif_usb_exit(void); #endif /* HTC_USB_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/gpio.c0000644000175000017500000003072512026211315023366 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "ath9k.h" /********************************/ /* LED functions */ /********************************/ #ifdef CONFIG_MAC80211_LEDS static void ath_led_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev); ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF)); } void ath_deinit_leds(struct ath_softc *sc) { if (!sc->led_registered) return; ath_led_brightness(&sc->led_cdev, LED_OFF); led_classdev_unregister(&sc->led_cdev); } void ath_init_leds(struct ath_softc *sc) { int ret; if (AR_SREV_9100(sc->sc_ah)) return; if (sc->sc_ah->led_pin < 0) { if (AR_SREV_9287(sc->sc_ah)) sc->sc_ah->led_pin = ATH_LED_PIN_9287; else if (AR_SREV_9485(sc->sc_ah)) sc->sc_ah->led_pin = ATH_LED_PIN_9485; else if (AR_SREV_9300(sc->sc_ah)) sc->sc_ah->led_pin = ATH_LED_PIN_9300; else if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah)) sc->sc_ah->led_pin = ATH_LED_PIN_9462; else sc->sc_ah->led_pin = ATH_LED_PIN_DEF; } /* Configure gpio 1 for output */ ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); /* LED off, active low */ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); if (!led_blink) sc->led_cdev.default_trigger = ieee80211_get_radio_led_name(sc->hw); snprintf(sc->led_name, sizeof(sc->led_name), "ath9k-%s", wiphy_name(sc->hw->wiphy)); sc->led_cdev.name = sc->led_name; sc->led_cdev.brightness_set = ath_led_brightness; ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev); if (ret < 0) return; sc->led_registered = true; } #endif /*******************/ /* Rfkill */ /*******************/ static bool ath_is_rfkill_set(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; bool is_blocked; ath9k_ps_wakeup(sc); is_blocked = ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == ah->rfkill_polarity; ath9k_ps_restore(sc); return is_blocked; } void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; bool blocked = !!ath_is_rfkill_set(sc); wiphy_rfkill_set_hw_state(hw->wiphy, blocked); } void ath_start_rfkill_poll(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) wiphy_rfkill_start_polling(sc->hw->wiphy); } #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT /******************/ /* BTCOEX */ /******************/ /* * Detects if there is any priority bt traffic */ static void ath_detect_bt_priority(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_hw *ah = sc->sc_ah; if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio)) btcoex->bt_priority_cnt++; if (time_after(jiffies, btcoex->bt_priority_time + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); clear_bit(BT_OP_SCAN, &btcoex->op_flags); /* Detect if colocated bt started scanning */ if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX, "BT scan detected\n"); set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); set_bit(BT_OP_SCAN, &btcoex->op_flags); } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX, "BT priority traffic detected\n"); set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); } btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; } } static void ath9k_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, u32 trig_timeout, u32 timer_period) { ath9k_hw_gen_timer_start(ah, timer, trig_timeout, timer_period); if ((ah->imask & ATH9K_INT_GENTIMER) == 0) { ath9k_hw_disable_interrupts(ah); ah->imask |= ATH9K_INT_GENTIMER; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); } } static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; ath9k_hw_gen_timer_stop(ah, timer); /* if no timer is enabled, turn off interrupt mask */ if (timer_table->timer_mask.val == 0) { ath9k_hw_disable_interrupts(ah); ah->imask &= ~ATH9K_INT_GENTIMER; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); } } /* * This is the master bt coex timer which runs for every * 45ms, bt traffic will be given priority during 55% of this * period while wlan gets remaining 45% */ static void ath_btcoex_period_timer(unsigned long data) { struct ath_softc *sc = (struct ath_softc *) data; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; u32 timer_period; bool is_btscan; unsigned long flags; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) { spin_unlock_irqrestore(&sc->sc_pm_lock, flags); goto skip_hw_wakeup; } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_ps_wakeup(sc); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) ath_detect_bt_priority(sc); is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags); btcoex->bt_wait_time += btcoex->btcoex_period; if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) { if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) && (mci->num_pan || mci->num_other_acl)) ah->btcoex_hw.mci.stomp_ftp = (sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH); else ah->btcoex_hw.mci.stomp_ftp = false; btcoex->bt_wait_time = 0; sc->rx.num_pkts = 0; } spin_lock_bh(&btcoex->btcoex_lock); ath9k_hw_btcoex_bt_stomp(ah, is_btscan ? ATH_BTCOEX_STOMP_ALL : btcoex->bt_stomp_type); ath9k_hw_btcoex_enable(ah); spin_unlock_bh(&btcoex->btcoex_lock); /* * btcoex_period is in msec while (btocex/btscan_)no_stomp are in usec, * ensure that we properly convert btcoex_period to usec * for any comparision with (btcoex/btscan_)no_stomp. */ if (btcoex->btcoex_period * 1000 != btcoex->btcoex_no_stomp) { if (btcoex->hw_timer_enabled) ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); timer_period = is_btscan ? btcoex->btscan_no_stomp : btcoex->btcoex_no_stomp; ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period, timer_period * 10); btcoex->hw_timer_enabled = true; } ath9k_ps_restore(sc); skip_hw_wakeup: timer_period = btcoex->btcoex_period; mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period)); } /* * Generic tsf based hw timer which configures weight * registers to time slice between wlan and bt traffic */ static void ath_btcoex_no_stomp_timer(void *arg) { struct ath_softc *sc = (struct ath_softc *)arg; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath_common *common = ath9k_hw_common(ah); ath_dbg(common, BTCOEX, "no stomp timer running\n"); ath9k_ps_wakeup(sc); spin_lock_bh(&btcoex->btcoex_lock); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || test_bit(BT_OP_SCAN, &btcoex->op_flags)) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); ath9k_hw_btcoex_enable(ah); spin_unlock_bh(&btcoex->btcoex_lock); ath9k_ps_restore(sc); } static int ath_init_btcoex_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD; btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * 1000 * btcoex->btcoex_period / 100; btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * 1000 * btcoex->btcoex_period / 100; setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, (unsigned long) sc); spin_lock_init(&btcoex->btcoex_lock); btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, ath_btcoex_no_stomp_timer, ath_btcoex_no_stomp_timer, (void *) sc, AR_FIRST_NDP_TIMER); if (!btcoex->no_stomp_timer) return -ENOMEM; return 0; } /* * (Re)start btcoex timers */ void ath9k_btcoex_timer_resume(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_hw *ah = sc->sc_ah; ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ if (btcoex->hw_timer_enabled) ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); clear_bit(BT_OP_SCAN, &btcoex->op_flags); mod_timer(&btcoex->period_timer, jiffies); } /* * Pause btcoex timer and bt duty cycle timer */ void ath9k_btcoex_timer_pause(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_hw *ah = sc->sc_ah; del_timer_sync(&btcoex->period_timer); if (btcoex->hw_timer_enabled) ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); btcoex->hw_timer_enabled = false; } void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; if (btcoex->hw_timer_enabled) ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); } u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &sc->btcoex.mci; u16 aggr_limit = 0; if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit) aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4; else if (test_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags)) aggr_limit = min((max_4ms_framelen * 3) / 8, (u32)ATH_AMPDU_LIMIT_MAX); return aggr_limit; } void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status) { struct ath_hw *ah = sc->sc_ah; if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) if (status & ATH9K_INT_GENTIMER) ath_gen_timer_isr(sc->sc_ah); if (status & ATH9K_INT_MCI) ath_mci_intr(sc); } void ath9k_start_btcoex(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) && !ah->btcoex_hw.enabled) { if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, AR_STOMP_LOW_WLAN_WGHT); ath9k_hw_btcoex_enable(ah); if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) ath9k_btcoex_timer_resume(sc); } } void ath9k_stop_btcoex(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; if (ah->btcoex_hw.enabled && ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) { if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) ath9k_btcoex_timer_pause(sc); ath9k_hw_btcoex_disable(ah); if (AR_SREV_9462(ah)) ath_mci_flush_profile(&sc->btcoex.mci); } } void ath9k_deinit_btcoex(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; if ((sc->btcoex.no_stomp_timer) && ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE) ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); if (ath9k_hw_mci_is_enabled(ah)) ath_mci_cleanup(sc); } int ath9k_init_btcoex(struct ath_softc *sc) { struct ath_txq *txq; struct ath_hw *ah = sc->sc_ah; int r; ath9k_hw_btcoex_init_scheme(ah); switch (ath9k_hw_get_btcoex_scheme(sc->sc_ah)) { case ATH_BTCOEX_CFG_NONE: break; case ATH_BTCOEX_CFG_2WIRE: ath9k_hw_btcoex_init_2wire(sc->sc_ah); break; case ATH_BTCOEX_CFG_3WIRE: ath9k_hw_btcoex_init_3wire(sc->sc_ah); r = ath_init_btcoex_timer(sc); if (r) return -1; txq = sc->tx.txq_map[WME_AC_BE]; ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; if (ath9k_hw_mci_is_enabled(ah)) { sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; INIT_LIST_HEAD(&sc->btcoex.mci.info); r = ath_mci_setup(sc); if (r) return r; ath9k_hw_btcoex_init_mci(ah); } break; default: WARN_ON(1); break; } return 0; } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/eeprom.h0000644000175000017500000005045712026211315023730 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef EEPROM_H #define EEPROM_H #define AR_EEPROM_MODAL_SPURS 5 #include "../ath.h" #include #include "ar9003_eeprom.h" #ifdef __BIG_ENDIAN #define AR5416_EEPROM_MAGIC 0x5aa5 #else #define AR5416_EEPROM_MAGIC 0xa55a #endif #define CTRY_DEBUG 0x1ff #define CTRY_DEFAULT 0 #define AR_EEPROM_EEPCAP_COMPRESS_DIS 0x0001 #define AR_EEPROM_EEPCAP_AES_DIS 0x0002 #define AR_EEPROM_EEPCAP_FASTFRAME_DIS 0x0004 #define AR_EEPROM_EEPCAP_BURST_DIS 0x0008 #define AR_EEPROM_EEPCAP_MAXQCU 0x01F0 #define AR_EEPROM_EEPCAP_MAXQCU_S 4 #define AR_EEPROM_EEPCAP_HEAVY_CLIP_EN 0x0200 #define AR_EEPROM_EEPCAP_KC_ENTRIES 0xF000 #define AR_EEPROM_EEPCAP_KC_ENTRIES_S 12 #define AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND 0x0040 #define AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN 0x0080 #define AR_EEPROM_EEREGCAP_EN_KK_U2 0x0100 #define AR_EEPROM_EEREGCAP_EN_KK_MIDBAND 0x0200 #define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD 0x0400 #define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A 0x0800 #define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD_PRE4_0 0x4000 #define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A_PRE4_0 0x8000 #define AR5416_EEPROM_MAGIC_OFFSET 0x0 #define AR5416_EEPROM_S 2 #define AR5416_EEPROM_OFFSET 0x2000 #define AR5416_EEPROM_MAX 0xae0 #define AR5416_EEPROM_START_ADDR \ (AR_SREV_9100(ah)) ? 0x1fff1000 : 0x503f1200 #define SD_NO_CTL 0xE0 #define NO_CTL 0xff #define CTL_MODE_M 0xf #define CTL_11A 0 #define CTL_11B 1 #define CTL_11G 2 #define CTL_2GHT20 5 #define CTL_5GHT20 6 #define CTL_2GHT40 7 #define CTL_5GHT40 8 #define EXT_ADDITIVE (0x8000) #define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) #define SUB_NUM_CTL_MODES_AT_5G_40 2 #define SUB_NUM_CTL_MODES_AT_2G_40 3 #define POWER_CORRECTION_FOR_TWO_CHAIN 6 /* 10*log10(2)*2 */ #define POWER_CORRECTION_FOR_THREE_CHAIN 10 /* 10*log10(3)*2 */ /* * For AR9285 and later chipsets, the following bits are not being programmed * in EEPROM and so need to be enabled always. * * Bit 0: en_fcc_mid * Bit 1: en_jap_mid * Bit 2: en_fcc_dfs_ht40 * Bit 3: en_jap_ht40 * Bit 4: en_jap_dfs_ht40 */ #define AR9285_RDEXT_DEFAULT 0x1F #define ATH9K_POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) #define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5)) #define FBIN2FREQ(x, y) ((y) ? (2300 + x) : (4800 + 5 * x)) #define ath9k_hw_use_flash(_ah) (!(_ah->ah_flags & AH_USE_EEPROM)) #define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) #define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \ ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) #define OLC_FOR_AR9287_10_LATER (AR_SREV_9287_11_OR_LATER(ah) && \ ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) #define EEP_RFSILENT_ENABLED 0x0001 #define EEP_RFSILENT_ENABLED_S 0 #define EEP_RFSILENT_POLARITY 0x0002 #define EEP_RFSILENT_POLARITY_S 1 #define EEP_RFSILENT_GPIO_SEL ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x00fc : 0x001c) #define EEP_RFSILENT_GPIO_SEL_S 2 #define AR5416_OPFLAGS_11A 0x01 #define AR5416_OPFLAGS_11G 0x02 #define AR5416_OPFLAGS_N_5G_HT40 0x04 #define AR5416_OPFLAGS_N_2G_HT40 0x08 #define AR5416_OPFLAGS_N_5G_HT20 0x10 #define AR5416_OPFLAGS_N_2G_HT20 0x20 #define AR5416_EEP_NO_BACK_VER 0x1 #define AR5416_EEP_VER 0xE #define AR5416_EEP_VER_MINOR_MASK 0x0FFF #define AR5416_EEP_MINOR_VER_2 0x2 #define AR5416_EEP_MINOR_VER_3 0x3 #define AR5416_EEP_MINOR_VER_7 0x7 #define AR5416_EEP_MINOR_VER_9 0x9 #define AR5416_EEP_MINOR_VER_16 0x10 #define AR5416_EEP_MINOR_VER_17 0x11 #define AR5416_EEP_MINOR_VER_19 0x13 #define AR5416_EEP_MINOR_VER_20 0x14 #define AR5416_EEP_MINOR_VER_21 0x15 #define AR5416_EEP_MINOR_VER_22 0x16 #define AR5416_NUM_5G_CAL_PIERS 8 #define AR5416_NUM_2G_CAL_PIERS 4 #define AR5416_NUM_5G_20_TARGET_POWERS 8 #define AR5416_NUM_5G_40_TARGET_POWERS 8 #define AR5416_NUM_2G_CCK_TARGET_POWERS 3 #define AR5416_NUM_2G_20_TARGET_POWERS 4 #define AR5416_NUM_2G_40_TARGET_POWERS 4 #define AR5416_NUM_CTLS 24 #define AR5416_NUM_BAND_EDGES 8 #define AR5416_NUM_PD_GAINS 4 #define AR5416_PD_GAINS_IN_MASK 4 #define AR5416_PD_GAIN_ICEPTS 5 #define AR5416_NUM_PDADC_VALUES 128 #define AR5416_BCHAN_UNUSED 0xFF #define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64 #define AR5416_MAX_CHAINS 3 #define AR9300_MAX_CHAINS 3 #define AR5416_PWR_TABLE_OFFSET_DB -5 /* Rx gain type values */ #define AR5416_EEP_RXGAIN_23DB_BACKOFF 0 #define AR5416_EEP_RXGAIN_13DB_BACKOFF 1 #define AR5416_EEP_RXGAIN_ORIG 2 /* Tx gain type values */ #define AR5416_EEP_TXGAIN_ORIGINAL 0 #define AR5416_EEP_TXGAIN_HIGH_POWER 1 #define AR5416_EEP4K_START_LOC 64 #define AR5416_EEP4K_NUM_2G_CAL_PIERS 3 #define AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS 3 #define AR5416_EEP4K_NUM_2G_20_TARGET_POWERS 3 #define AR5416_EEP4K_NUM_2G_40_TARGET_POWERS 3 #define AR5416_EEP4K_NUM_CTLS 12 #define AR5416_EEP4K_NUM_BAND_EDGES 4 #define AR5416_EEP4K_NUM_PD_GAINS 2 #define AR5416_EEP4K_MAX_CHAINS 1 #define AR9280_TX_GAIN_TABLE_SIZE 22 #define AR9287_EEP_VER 0xE #define AR9287_EEP_VER_MINOR_MASK 0xFFF #define AR9287_EEP_MINOR_VER_1 0x1 #define AR9287_EEP_MINOR_VER_2 0x2 #define AR9287_EEP_MINOR_VER_3 0x3 #define AR9287_EEP_MINOR_VER AR9287_EEP_MINOR_VER_3 #define AR9287_EEP_MINOR_VER_b AR9287_EEP_MINOR_VER #define AR9287_EEP_NO_BACK_VER AR9287_EEP_MINOR_VER_1 #define AR9287_EEP_START_LOC 128 #define AR9287_HTC_EEP_START_LOC 256 #define AR9287_NUM_2G_CAL_PIERS 3 #define AR9287_NUM_2G_CCK_TARGET_POWERS 3 #define AR9287_NUM_2G_20_TARGET_POWERS 3 #define AR9287_NUM_2G_40_TARGET_POWERS 3 #define AR9287_NUM_CTLS 12 #define AR9287_NUM_BAND_EDGES 4 #define AR9287_PD_GAIN_ICEPTS 1 #define AR9287_EEPMISC_BIG_ENDIAN 0x01 #define AR9287_EEPMISC_WOW 0x02 #define AR9287_MAX_CHAINS 2 #define AR9287_ANT_16S 32 #define AR9287_DATA_SZ 32 #define AR9287_PWR_TABLE_OFFSET_DB -5 #define AR9287_CHECKSUM_LOCATION (AR9287_EEP_START_LOC + 1) #define CTL_EDGE_TPOWER(_ctl) ((_ctl) & 0x3f) #define CTL_EDGE_FLAGS(_ctl) (((_ctl) >> 6) & 0x03) #define LNA_CTL_BUF_MODE BIT(0) #define LNA_CTL_ISEL_LO BIT(1) #define LNA_CTL_ISEL_HI BIT(2) #define LNA_CTL_BUF_IN BIT(3) #define LNA_CTL_FEM_BAND BIT(4) #define LNA_CTL_LOCAL_BIAS BIT(5) #define LNA_CTL_FORCE_XPA BIT(6) #define LNA_CTL_USE_ANT1 BIT(7) enum eeprom_param { EEP_NFTHRESH_5, EEP_NFTHRESH_2, EEP_MAC_MSW, EEP_MAC_MID, EEP_MAC_LSW, EEP_REG_0, EEP_OP_CAP, EEP_OP_MODE, EEP_RF_SILENT, EEP_OB_5, EEP_DB_5, EEP_OB_2, EEP_DB_2, EEP_MINOR_REV, EEP_TX_MASK, EEP_RX_MASK, EEP_FSTCLK_5G, EEP_RXGAIN_TYPE, EEP_OL_PWRCTRL, EEP_TXGAIN_TYPE, EEP_RC_CHAIN_MASK, EEP_DAC_HPWR_5G, EEP_FRAC_N_5G, EEP_DEV_TYPE, EEP_TEMPSENSE_SLOPE, EEP_TEMPSENSE_SLOPE_PAL_ON, EEP_PWR_TABLE_OFFSET, EEP_PAPRD, EEP_MODAL_VER, EEP_ANT_DIV_CTL1, EEP_CHAIN_MASK_REDUCE, EEP_ANTENNA_GAIN_2G, EEP_ANTENNA_GAIN_5G, }; enum ar5416_rates { rate6mb, rate9mb, rate12mb, rate18mb, rate24mb, rate36mb, rate48mb, rate54mb, rate1l, rate2l, rate2s, rate5_5l, rate5_5s, rate11l, rate11s, rateXr, rateHt20_0, rateHt20_1, rateHt20_2, rateHt20_3, rateHt20_4, rateHt20_5, rateHt20_6, rateHt20_7, rateHt40_0, rateHt40_1, rateHt40_2, rateHt40_3, rateHt40_4, rateHt40_5, rateHt40_6, rateHt40_7, rateDupCck, rateDupOfdm, rateExtCck, rateExtOfdm, Ar5416RateSize }; enum ath9k_hal_freq_band { ATH9K_HAL_FREQ_BAND_5GHZ = 0, ATH9K_HAL_FREQ_BAND_2GHZ = 1 }; struct base_eep_header { u16 length; u16 checksum; u16 version; u8 opCapFlags; u8 eepMisc; u16 regDmn[2]; u8 macAddr[6]; u8 rxMask; u8 txMask; u16 rfSilent; u16 blueToothOptions; u16 deviceCap; u32 binBuildNumber; u8 deviceType; u8 pwdclkind; u8 fastClk5g; u8 divChain; u8 rxGainType; u8 dacHiPwrMode_5G; u8 openLoopPwrCntl; u8 dacLpMode; u8 txGainType; u8 rcChainMask; u8 desiredScaleCCK; u8 pwr_table_offset; u8 frac_n_5g; u8 futureBase_3[21]; } __packed; struct base_eep_header_4k { u16 length; u16 checksum; u16 version; u8 opCapFlags; u8 eepMisc; u16 regDmn[2]; u8 macAddr[6]; u8 rxMask; u8 txMask; u16 rfSilent; u16 blueToothOptions; u16 deviceCap; u32 binBuildNumber; u8 deviceType; u8 txGainType; } __packed; struct spur_chan { u16 spurChan; u8 spurRangeLow; u8 spurRangeHigh; } __packed; struct modal_eep_header { u32 antCtrlChain[AR5416_MAX_CHAINS]; u32 antCtrlCommon; u8 antennaGainCh[AR5416_MAX_CHAINS]; u8 switchSettling; u8 txRxAttenCh[AR5416_MAX_CHAINS]; u8 rxTxMarginCh[AR5416_MAX_CHAINS]; u8 adcDesiredSize; u8 pgaDesiredSize; u8 xlnaGainCh[AR5416_MAX_CHAINS]; u8 txEndToXpaOff; u8 txEndToRxOn; u8 txFrameToXpaOn; u8 thresh62; u8 noiseFloorThreshCh[AR5416_MAX_CHAINS]; u8 xpdGain; u8 xpd; u8 iqCalICh[AR5416_MAX_CHAINS]; u8 iqCalQCh[AR5416_MAX_CHAINS]; u8 pdGainOverlap; u8 ob; u8 db; u8 xpaBiasLvl; u8 pwrDecreaseFor2Chain; u8 pwrDecreaseFor3Chain; u8 txFrameToDataStart; u8 txFrameToPaOn; u8 ht40PowerIncForPdadc; u8 bswAtten[AR5416_MAX_CHAINS]; u8 bswMargin[AR5416_MAX_CHAINS]; u8 swSettleHt40; u8 xatten2Db[AR5416_MAX_CHAINS]; u8 xatten2Margin[AR5416_MAX_CHAINS]; u8 ob_ch1; u8 db_ch1; u8 lna_ctl; u8 miscBits; u16 xpaBiasLvlFreq[3]; u8 futureModal[6]; struct spur_chan spurChans[AR_EEPROM_MODAL_SPURS]; } __packed; struct calDataPerFreqOpLoop { u8 pwrPdg[2][5]; u8 vpdPdg[2][5]; u8 pcdac[2][5]; u8 empty[2][5]; } __packed; struct modal_eep_4k_header { u32 antCtrlChain[AR5416_EEP4K_MAX_CHAINS]; u32 antCtrlCommon; u8 antennaGainCh[AR5416_EEP4K_MAX_CHAINS]; u8 switchSettling; u8 txRxAttenCh[AR5416_EEP4K_MAX_CHAINS]; u8 rxTxMarginCh[AR5416_EEP4K_MAX_CHAINS]; u8 adcDesiredSize; u8 pgaDesiredSize; u8 xlnaGainCh[AR5416_EEP4K_MAX_CHAINS]; u8 txEndToXpaOff; u8 txEndToRxOn; u8 txFrameToXpaOn; u8 thresh62; u8 noiseFloorThreshCh[AR5416_EEP4K_MAX_CHAINS]; u8 xpdGain; u8 xpd; u8 iqCalICh[AR5416_EEP4K_MAX_CHAINS]; u8 iqCalQCh[AR5416_EEP4K_MAX_CHAINS]; u8 pdGainOverlap; #ifdef __BIG_ENDIAN_BITFIELD u8 ob_1:4, ob_0:4; u8 db1_1:4, db1_0:4; #else u8 ob_0:4, ob_1:4; u8 db1_0:4, db1_1:4; #endif u8 xpaBiasLvl; u8 txFrameToDataStart; u8 txFrameToPaOn; u8 ht40PowerIncForPdadc; u8 bswAtten[AR5416_EEP4K_MAX_CHAINS]; u8 bswMargin[AR5416_EEP4K_MAX_CHAINS]; u8 swSettleHt40; u8 xatten2Db[AR5416_EEP4K_MAX_CHAINS]; u8 xatten2Margin[AR5416_EEP4K_MAX_CHAINS]; #ifdef __BIG_ENDIAN_BITFIELD u8 db2_1:4, db2_0:4; #else u8 db2_0:4, db2_1:4; #endif u8 version; #ifdef __BIG_ENDIAN_BITFIELD u8 ob_3:4, ob_2:4; u8 antdiv_ctl1:4, ob_4:4; u8 db1_3:4, db1_2:4; u8 antdiv_ctl2:4, db1_4:4; u8 db2_2:4, db2_3:4; u8 reserved:4, db2_4:4; #else u8 ob_2:4, ob_3:4; u8 ob_4:4, antdiv_ctl1:4; u8 db1_2:4, db1_3:4; u8 db1_4:4, antdiv_ctl2:4; u8 db2_2:4, db2_3:4; u8 db2_4:4, reserved:4; #endif u8 tx_diversity; u8 flc_pwr_thresh; u8 bb_scale_smrt_antenna; #define EEP_4K_BB_DESIRED_SCALE_MASK 0x1f u8 futureModal[1]; struct spur_chan spurChans[AR_EEPROM_MODAL_SPURS]; } __packed; struct base_eep_ar9287_header { u16 length; u16 checksum; u16 version; u8 opCapFlags; u8 eepMisc; u16 regDmn[2]; u8 macAddr[6]; u8 rxMask; u8 txMask; u16 rfSilent; u16 blueToothOptions; u16 deviceCap; u32 binBuildNumber; u8 deviceType; u8 openLoopPwrCntl; int8_t pwrTableOffset; int8_t tempSensSlope; int8_t tempSensSlopePalOn; u8 futureBase[29]; } __packed; struct modal_eep_ar9287_header { u32 antCtrlChain[AR9287_MAX_CHAINS]; u32 antCtrlCommon; int8_t antennaGainCh[AR9287_MAX_CHAINS]; u8 switchSettling; u8 txRxAttenCh[AR9287_MAX_CHAINS]; u8 rxTxMarginCh[AR9287_MAX_CHAINS]; int8_t adcDesiredSize; u8 txEndToXpaOff; u8 txEndToRxOn; u8 txFrameToXpaOn; u8 thresh62; int8_t noiseFloorThreshCh[AR9287_MAX_CHAINS]; u8 xpdGain; u8 xpd; int8_t iqCalICh[AR9287_MAX_CHAINS]; int8_t iqCalQCh[AR9287_MAX_CHAINS]; u8 pdGainOverlap; u8 xpaBiasLvl; u8 txFrameToDataStart; u8 txFrameToPaOn; u8 ht40PowerIncForPdadc; u8 bswAtten[AR9287_MAX_CHAINS]; u8 bswMargin[AR9287_MAX_CHAINS]; u8 swSettleHt40; u8 version; u8 db1; u8 db2; u8 ob_cck; u8 ob_psk; u8 ob_qam; u8 ob_pal_off; u8 futureModal[30]; struct spur_chan spurChans[AR_EEPROM_MODAL_SPURS]; } __packed; struct cal_data_per_freq { u8 pwrPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; u8 vpdPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; } __packed; struct cal_data_per_freq_4k { u8 pwrPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; u8 vpdPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; } __packed; struct cal_target_power_leg { u8 bChannel; u8 tPow2x[4]; } __packed; struct cal_target_power_ht { u8 bChannel; u8 tPow2x[8]; } __packed; struct cal_ctl_edges { u8 bChannel; u8 ctl; } __packed; struct cal_data_op_loop_ar9287 { u8 pwrPdg[2][5]; u8 vpdPdg[2][5]; u8 pcdac[2][5]; u8 empty[2][5]; } __packed; struct cal_data_per_freq_ar9287 { u8 pwrPdg[AR5416_NUM_PD_GAINS][AR9287_PD_GAIN_ICEPTS]; u8 vpdPdg[AR5416_NUM_PD_GAINS][AR9287_PD_GAIN_ICEPTS]; } __packed; union cal_data_per_freq_ar9287_u { struct cal_data_op_loop_ar9287 calDataOpen; struct cal_data_per_freq_ar9287 calDataClose; } __packed; struct cal_ctl_data_ar9287 { struct cal_ctl_edges ctlEdges[AR9287_MAX_CHAINS][AR9287_NUM_BAND_EDGES]; } __packed; struct cal_ctl_data { struct cal_ctl_edges ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; } __packed; struct cal_ctl_data_4k { struct cal_ctl_edges ctlEdges[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_BAND_EDGES]; } __packed; struct ar5416_eeprom_def { struct base_eep_header baseEepHeader; u8 custData[64]; struct modal_eep_header modalHeader[2]; u8 calFreqPier5G[AR5416_NUM_5G_CAL_PIERS]; u8 calFreqPier2G[AR5416_NUM_2G_CAL_PIERS]; struct cal_data_per_freq calPierData5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS]; struct cal_data_per_freq calPierData2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]; struct cal_target_power_leg calTargetPower5G[AR5416_NUM_5G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower5GHT20[AR5416_NUM_5G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower5GHT40[AR5416_NUM_5G_40_TARGET_POWERS]; struct cal_target_power_leg calTargetPowerCck[AR5416_NUM_2G_CCK_TARGET_POWERS]; struct cal_target_power_leg calTargetPower2G[AR5416_NUM_2G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower2GHT20[AR5416_NUM_2G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower2GHT40[AR5416_NUM_2G_40_TARGET_POWERS]; u8 ctlIndex[AR5416_NUM_CTLS]; struct cal_ctl_data ctlData[AR5416_NUM_CTLS]; u8 padding; } __packed; struct ar5416_eeprom_4k { struct base_eep_header_4k baseEepHeader; u8 custData[20]; struct modal_eep_4k_header modalHeader; u8 calFreqPier2G[AR5416_EEP4K_NUM_2G_CAL_PIERS]; struct cal_data_per_freq_4k calPierData2G[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_2G_CAL_PIERS]; struct cal_target_power_leg calTargetPowerCck[AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS]; struct cal_target_power_leg calTargetPower2G[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower2GHT20[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower2GHT40[AR5416_EEP4K_NUM_2G_40_TARGET_POWERS]; u8 ctlIndex[AR5416_EEP4K_NUM_CTLS]; struct cal_ctl_data_4k ctlData[AR5416_EEP4K_NUM_CTLS]; u8 padding; } __packed; struct ar9287_eeprom { struct base_eep_ar9287_header baseEepHeader; u8 custData[AR9287_DATA_SZ]; struct modal_eep_ar9287_header modalHeader; u8 calFreqPier2G[AR9287_NUM_2G_CAL_PIERS]; union cal_data_per_freq_ar9287_u calPierData2G[AR9287_MAX_CHAINS][AR9287_NUM_2G_CAL_PIERS]; struct cal_target_power_leg calTargetPowerCck[AR9287_NUM_2G_CCK_TARGET_POWERS]; struct cal_target_power_leg calTargetPower2G[AR9287_NUM_2G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower2GHT20[AR9287_NUM_2G_20_TARGET_POWERS]; struct cal_target_power_ht calTargetPower2GHT40[AR9287_NUM_2G_40_TARGET_POWERS]; u8 ctlIndex[AR9287_NUM_CTLS]; struct cal_ctl_data_ar9287 ctlData[AR9287_NUM_CTLS]; u8 padding; } __packed; enum reg_ext_bitmap { REG_EXT_FCC_MIDBAND = 0, REG_EXT_JAPAN_MIDBAND = 1, REG_EXT_FCC_DFS_HT40 = 2, REG_EXT_JAPAN_NONDFS_HT40 = 3, REG_EXT_JAPAN_DFS_HT40 = 4 }; struct ath9k_country_entry { u16 countryCode; u16 regDmnEnum; u16 regDmn5G; u16 regDmn2G; u8 isMultidomain; u8 iso[3]; }; struct eeprom_ops { int (*check_eeprom)(struct ath_hw *hw); u32 (*get_eeprom)(struct ath_hw *hw, enum eeprom_param param); bool (*fill_eeprom)(struct ath_hw *hw); u32 (*dump_eeprom)(struct ath_hw *hw, bool dump_base_hdr, u8 *buf, u32 len, u32 size); int (*get_eeprom_ver)(struct ath_hw *hw); int (*get_eeprom_rev)(struct ath_hw *hw); void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan); void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan); void (*set_txpower)(struct ath_hw *hw, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, u8 powerLimit, bool test); u16 (*get_spur_channel)(struct ath_hw *ah, u16 i, bool is2GHz); }; void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val); void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, u32 shift, u32 val); int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, int16_t targetLeft, int16_t targetRight); bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, u16 *indexL, u16 *indexR); bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data); void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, int eep_start_loc, int size); void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, u8 *pVpdList, u16 numIntercepts, u8 *pRetVpdList); void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, struct ath9k_channel *chan, struct cal_target_power_leg *powInfo, u16 numChannels, struct cal_target_power_leg *pNewPower, u16 numRates, bool isExtTarget); void ath9k_hw_get_target_powers(struct ath_hw *ah, struct ath9k_channel *chan, struct cal_target_power_ht *powInfo, u16 numChannels, struct cal_target_power_ht *pNewPower, u16 numRates, bool isHt40Target); u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, bool is2GHz, int num_band_edges); u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit, u8 antenna_reduction); void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah); int ath9k_hw_eeprom_init(struct ath_hw *ah); void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, struct ath9k_channel *chan, void *pRawDataSet, u8 *bChans, u16 availPiers, u16 tPdGainOverlap, u16 *pPdGainBoundaries, u8 *pPDADCValues, u16 numXpdGains); static inline u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz) { if (fbin == AR5416_BCHAN_UNUSED) return fbin; return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); } #define ar5416_get_ntxchains(_txchainmask) \ (((_txchainmask >> 2) & 1) + \ ((_txchainmask >> 1) & 1) + (_txchainmask & 1)) extern const struct eeprom_ops eep_def_ops; extern const struct eeprom_ops eep_4k_ops; extern const struct eeprom_ops eep_ar9287_ops; extern const struct eeprom_ops eep_ar9287_ops; extern const struct eeprom_ops eep_ar9300_ops; #endif /* EEPROM_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/eeprom_def.c0000644000175000017500000012251212026211315024531 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar9002_phy.h" static void ath9k_get_txgain_index(struct ath_hw *ah, struct ath9k_channel *chan, struct calDataPerFreqOpLoop *rawDatasetOpLoop, u8 *calChans, u16 availPiers, u8 *pwr, u8 *pcdacIdx) { u8 pcdac, i = 0; u16 idxL = 0, idxR = 0, numPiers; bool match; struct chan_centers centers; ath9k_hw_get_channel_centers(ah, chan, ¢ers); for (numPiers = 0; numPiers < availPiers; numPiers++) if (calChans[numPiers] == AR5416_BCHAN_UNUSED) break; match = ath9k_hw_get_lower_upper_index( (u8)FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)), calChans, numPiers, &idxL, &idxR); if (match) { pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; } else { pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; } while (pcdac > ah->originalGain[i] && i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) i++; *pcdacIdx = i; } static void ath9k_olc_get_pdadcs(struct ath_hw *ah, u32 initTxGain, int txPower, u8 *pPDADCValues) { u32 i; u32 offset; REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); offset = txPower; for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) if (i < offset) pPDADCValues[i] = 0x0; else pPDADCValues[i] = 0xFF; } static int ath9k_hw_def_get_eeprom_ver(struct ath_hw *ah) { return ((ah->eeprom.def.baseEepHeader.version >> 12) & 0xF); } static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah) { return ((ah->eeprom.def.baseEepHeader.version) & 0xFFF); } #define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16)) static bool __ath9k_hw_def_fill_eeprom(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data = (u16 *)&ah->eeprom.def; int addr, ar5416_eep_start_loc = 0x100; for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { if (!ath9k_hw_nvram_read(common, addr + ar5416_eep_start_loc, eep_data)) { ath_err(ath9k_hw_common(ah), "Unable to read eeprom region\n"); return false; } eep_data++; } return true; } static bool __ath9k_hw_usb_def_fill_eeprom(struct ath_hw *ah) { u16 *eep_data = (u16 *)&ah->eeprom.def; ath9k_hw_usb_gen_fill_eeprom(ah, eep_data, 0x100, SIZE_EEPROM_DEF); return true; } static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { ath_dbg(common, EEPROM, "Reading from EEPROM, not flash\n"); } if (common->bus_ops->ath_bus_type == ATH_USB) return __ath9k_hw_usb_def_fill_eeprom(ah); else return __ath9k_hw_def_fill_eeprom(ah); } #undef SIZE_EEPROM_DEF #if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS) static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_header *modal_hdr) { PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]); PR_EEP("Chain1 Ant. Control", modal_hdr->antCtrlChain[1]); PR_EEP("Chain2 Ant. Control", modal_hdr->antCtrlChain[2]); PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon); PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]); PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]); PR_EEP("Chain2 Ant. Gain", modal_hdr->antennaGainCh[2]); PR_EEP("Switch Settle", modal_hdr->switchSettling); PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]); PR_EEP("Chain1 TxRxAtten", modal_hdr->txRxAttenCh[1]); PR_EEP("Chain2 TxRxAtten", modal_hdr->txRxAttenCh[2]); PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]); PR_EEP("Chain1 RxTxMargin", modal_hdr->rxTxMarginCh[1]); PR_EEP("Chain2 RxTxMargin", modal_hdr->rxTxMarginCh[2]); PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize); PR_EEP("PGA Desired size", modal_hdr->pgaDesiredSize); PR_EEP("Chain0 xlna Gain", modal_hdr->xlnaGainCh[0]); PR_EEP("Chain1 xlna Gain", modal_hdr->xlnaGainCh[1]); PR_EEP("Chain2 xlna Gain", modal_hdr->xlnaGainCh[2]); PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff); PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn); PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn); PR_EEP("CCA Threshold)", modal_hdr->thresh62); PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]); PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]); PR_EEP("Chain2 NF Threshold", modal_hdr->noiseFloorThreshCh[2]); PR_EEP("xpdGain", modal_hdr->xpdGain); PR_EEP("External PD", modal_hdr->xpd); PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]); PR_EEP("Chain1 I Coefficient", modal_hdr->iqCalICh[1]); PR_EEP("Chain2 I Coefficient", modal_hdr->iqCalICh[2]); PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]); PR_EEP("Chain1 Q Coefficient", modal_hdr->iqCalQCh[1]); PR_EEP("Chain2 Q Coefficient", modal_hdr->iqCalQCh[2]); PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap); PR_EEP("Chain0 OutputBias", modal_hdr->ob); PR_EEP("Chain0 DriverBias", modal_hdr->db); PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl); PR_EEP("2chain pwr decrease", modal_hdr->pwrDecreaseFor2Chain); PR_EEP("3chain pwr decrease", modal_hdr->pwrDecreaseFor3Chain); PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart); PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn); PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc); PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]); PR_EEP("Chain1 bswAtten", modal_hdr->bswAtten[1]); PR_EEP("Chain2 bswAtten", modal_hdr->bswAtten[2]); PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]); PR_EEP("Chain1 bswMargin", modal_hdr->bswMargin[1]); PR_EEP("Chain2 bswMargin", modal_hdr->bswMargin[2]); PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40); PR_EEP("Chain0 xatten2Db", modal_hdr->xatten2Db[0]); PR_EEP("Chain1 xatten2Db", modal_hdr->xatten2Db[1]); PR_EEP("Chain2 xatten2Db", modal_hdr->xatten2Db[2]); PR_EEP("Chain0 xatten2Margin", modal_hdr->xatten2Margin[0]); PR_EEP("Chain1 xatten2Margin", modal_hdr->xatten2Margin[1]); PR_EEP("Chain2 xatten2Margin", modal_hdr->xatten2Margin[2]); PR_EEP("Chain1 OutputBias", modal_hdr->ob_ch1); PR_EEP("Chain1 DriverBias", modal_hdr->db_ch1); PR_EEP("LNA Control", modal_hdr->lna_ctl); PR_EEP("XPA Bias Freq0", modal_hdr->xpaBiasLvlFreq[0]); PR_EEP("XPA Bias Freq1", modal_hdr->xpaBiasLvlFreq[1]); PR_EEP("XPA Bias Freq2", modal_hdr->xpaBiasLvlFreq[2]); return len; } static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct base_eep_header *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[0]); len += snprintf(buf + len, size - len, "%20s :\n", "5GHz modal Header"); len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[1]); goto out; } PR_EEP("Major Version", pBase->version >> 12); PR_EEP("Minor Version", pBase->version & 0xFFF); PR_EEP("Checksum", pBase->checksum); PR_EEP("Length", pBase->length); PR_EEP("RegDomain1", pBase->regDmn[0]); PR_EEP("RegDomain2", pBase->regDmn[1]); PR_EEP("TX Mask", pBase->txMask); PR_EEP("RX Mask", pBase->rxMask); PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01)); PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF); PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF); PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl); len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", pBase->macAddr); out: if (len > size) len = size; return len; } #else static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { return 0; } #endif static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) { struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct ath_common *common = ath9k_hw_common(ah); u16 *eepdata, temp, magic, magic2; u32 sum = 0, el; bool need_swap = false; int i, addr, size; if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { ath_err(common, "Reading Magic # failed\n"); return false; } if (!ath9k_hw_use_flash(ah)) { ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); if (magic2 == AR5416_EEPROM_MAGIC) { size = sizeof(struct ar5416_eeprom_def); need_swap = true; eepdata = (u16 *) (&ah->eeprom); for (addr = 0; addr < size / sizeof(u16); addr++) { temp = swab16(*eepdata); *eepdata = temp; eepdata++; } } else { ath_err(common, "Invalid EEPROM Magic. Endianness mismatch.\n"); return -EINVAL; } } } ath_dbg(common, EEPROM, "need_swap = %s\n", need_swap ? "True" : "False"); if (need_swap) el = swab16(ah->eeprom.def.baseEepHeader.length); else el = ah->eeprom.def.baseEepHeader.length; if (el > sizeof(struct ar5416_eeprom_def)) el = sizeof(struct ar5416_eeprom_def) / sizeof(u16); else el = el / sizeof(u16); eepdata = (u16 *)(&ah->eeprom); for (i = 0; i < el; i++) sum ^= *eepdata++; if (need_swap) { u32 integer, j; u16 word; ath_dbg(common, EEPROM, "EEPROM Endianness is not native.. Changing.\n"); word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; word = swab16(eep->baseEepHeader.checksum); eep->baseEepHeader.checksum = word; word = swab16(eep->baseEepHeader.version); eep->baseEepHeader.version = word; word = swab16(eep->baseEepHeader.regDmn[0]); eep->baseEepHeader.regDmn[0] = word; word = swab16(eep->baseEepHeader.regDmn[1]); eep->baseEepHeader.regDmn[1] = word; word = swab16(eep->baseEepHeader.rfSilent); eep->baseEepHeader.rfSilent = word; word = swab16(eep->baseEepHeader.blueToothOptions); eep->baseEepHeader.blueToothOptions = word; word = swab16(eep->baseEepHeader.deviceCap); eep->baseEepHeader.deviceCap = word; for (j = 0; j < ARRAY_SIZE(eep->modalHeader); j++) { struct modal_eep_header *pModal = &eep->modalHeader[j]; integer = swab32(pModal->antCtrlCommon); pModal->antCtrlCommon = integer; for (i = 0; i < AR5416_MAX_CHAINS; i++) { integer = swab32(pModal->antCtrlChain[i]); pModal->antCtrlChain[i] = integer; } for (i = 0; i < 3; i++) { word = swab16(pModal->xpaBiasLvlFreq[i]); pModal->xpaBiasLvlFreq[i] = word; } for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { word = swab16(pModal->spurChans[i].spurChan); pModal->spurChans[i].spurChan = word; } } } if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; } /* Enable fixup for AR_AN_TOP2 if necessary */ if ((ah->hw_version.devid == AR9280_DEVID_PCI) && ((eep->baseEepHeader.version & 0xff) > 0x0a) && (eep->baseEepHeader.pwdclkind == 0)) ah->need_an_top2_fixup = true; if ((common->bus_ops->ath_bus_type == ATH_USB) && (AR_SREV_9280(ah))) eep->modalHeader[0].xpaBiasLvl = 0; return 0; } static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct modal_eep_header *pModal = eep->modalHeader; struct base_eep_header *pBase = &eep->baseEepHeader; int band = 0; switch (param) { case EEP_NFTHRESH_5: return pModal[0].noiseFloorThreshCh[0]; case EEP_NFTHRESH_2: return pModal[1].noiseFloorThreshCh[0]; case EEP_MAC_LSW: return get_unaligned_be16(pBase->macAddr); case EEP_MAC_MID: return get_unaligned_be16(pBase->macAddr + 2); case EEP_MAC_MSW: return get_unaligned_be16(pBase->macAddr + 4); case EEP_REG_0: return pBase->regDmn[0]; case EEP_OP_CAP: return pBase->deviceCap; case EEP_OP_MODE: return pBase->opCapFlags; case EEP_RF_SILENT: return pBase->rfSilent; case EEP_OB_5: return pModal[0].ob; case EEP_DB_5: return pModal[0].db; case EEP_OB_2: return pModal[1].ob; case EEP_DB_2: return pModal[1].db; case EEP_MINOR_REV: return AR5416_VER_MASK; case EEP_TX_MASK: return pBase->txMask; case EEP_RX_MASK: return pBase->rxMask; case EEP_FSTCLK_5G: return pBase->fastClk5g; case EEP_RXGAIN_TYPE: return pBase->rxGainType; case EEP_TXGAIN_TYPE: return pBase->txGainType; case EEP_OL_PWRCTRL: if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) return pBase->openLoopPwrCntl ? true : false; else return false; case EEP_RC_CHAIN_MASK: if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) return pBase->rcChainMask; else return 0; case EEP_DAC_HPWR_5G: if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) return pBase->dacHiPwrMode_5G; else return 0; case EEP_FRAC_N_5G: if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_22) return pBase->frac_n_5g; else return 0; case EEP_PWR_TABLE_OFFSET: if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_21) return pBase->pwr_table_offset; else return AR5416_PWR_TABLE_OFFSET_DB; case EEP_ANTENNA_GAIN_2G: band = 1; /* fall through */ case EEP_ANTENNA_GAIN_5G: return max_t(u8, max_t(u8, pModal[band].antennaGainCh[0], pModal[band].antennaGainCh[1]), pModal[band].antennaGainCh[2]); default: return 0; } } static void ath9k_hw_def_set_gain(struct ath_hw *ah, struct modal_eep_header *pModal, struct ar5416_eeprom_def *eep, u8 txRxAttenLocal, int regChainOffset, int i) { if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { txRxAttenLocal = pModal->txRxAttenCh[i]; if (AR_SREV_9280_20_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[i]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[i]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, pModal->xatten2Margin[i]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[i]); } else { REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | SM(pModal-> bswMargin[i], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | SM(pModal->bswAtten[i], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); } } if (AR_SREV_9280_20_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]); } else { REG_WRITE(ah, AR_PHY_RXGAIN + regChainOffset, (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & ~AR_PHY_RXGAIN_TXRX_ATTEN) | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN)); REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); } } static void ath9k_hw_def_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { struct modal_eep_header *pModal; struct ar5416_eeprom_def *eep = &ah->eeprom.def; int i, regChainOffset; u8 txRxAttenLocal; pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44; REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon & 0xffff); for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (AR_SREV_9280(ah)) { if (i >= 2) break; } if ((ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0)) regChainOffset = (i == 1) ? 0x2000 : 0x1000; else regChainOffset = i * 0x1000; REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[i]); REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset, (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal, regChainOffset, i); } if (AR_SREV_9280_20_OR_LATER(ah)) { if (IS_CHAN_2GHZ(chan)) { ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0, AR_AN_RF2G1_CH0_OB, AR_AN_RF2G1_CH0_OB_S, pModal->ob); ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0, AR_AN_RF2G1_CH0_DB, AR_AN_RF2G1_CH0_DB_S, pModal->db); ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1, AR_AN_RF2G1_CH1_OB, AR_AN_RF2G1_CH1_OB_S, pModal->ob_ch1); ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1, AR_AN_RF2G1_CH1_DB, AR_AN_RF2G1_CH1_DB_S, pModal->db_ch1); } else { ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0, AR_AN_RF5G1_CH0_OB5, AR_AN_RF5G1_CH0_OB5_S, pModal->ob); ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0, AR_AN_RF5G1_CH0_DB5, AR_AN_RF5G1_CH0_DB5_S, pModal->db); ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1, AR_AN_RF5G1_CH1_OB5, AR_AN_RF5G1_CH1_OB5_S, pModal->ob_ch1); ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1, AR_AN_RF5G1_CH1_DB5, AR_AN_RF5G1_CH1_DB5_S, pModal->db_ch1); } ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2, AR_AN_TOP2_XPABIAS_LVL, AR_AN_TOP2_XPABIAS_LVL_S, pModal->xpaBiasLvl); ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2, AR_AN_TOP2_LOCALBIAS, AR_AN_TOP2_LOCALBIAS_S, !!(pModal->lna_ctl & LNA_CTL_LOCAL_BIAS)); REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG, !!(pModal->lna_ctl & LNA_CTL_FORCE_XPA)); } REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); if (!AR_SREV_9280_20_OR_LATER(ah)) REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pModal->pgaDesiredSize); REG_WRITE(ah, AR_PHY_RF_CTL4, SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); if (AR_SREV_9280_20_OR_LATER(ah)) { REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, pModal->thresh62); } else { REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62, pModal->thresh62); REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CCA_THRESH62, pModal->thresh62); } if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_2) { REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_DATA_START, pModal->txFrameToDataStart); REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON, pModal->txFrameToPaOn); } if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { if (IS_CHAN_HT40(chan)) REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); } if (AR_SREV_9280_20_OR_LATER(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL, AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, pModal->miscBits); if (AR_SREV_9280_20(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) { if (IS_CHAN_2GHZ(chan)) REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); else if (eep->baseEepHeader.dacHiPwrMode_5G) REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0); else REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); udelay(100); REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, pModal->miscBits >> 2); REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9, AR_PHY_TX_DESIRED_SCALE_CCK, eep->baseEepHeader.desiredScaleCCK); } } static void ath9k_hw_def_set_addac(struct ath_hw *ah, struct ath9k_channel *chan) { #define XPA_LVL_FREQ(cnt) (pModal->xpaBiasLvlFreq[cnt]) struct modal_eep_header *pModal; struct ar5416_eeprom_def *eep = &ah->eeprom.def; u8 biaslevel; if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) return; if (ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_MINOR_VER_7) return; pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); if (pModal->xpaBiasLvl != 0xff) { biaslevel = pModal->xpaBiasLvl; } else { u16 resetFreqBin, freqBin, freqCount = 0; struct chan_centers centers; ath9k_hw_get_channel_centers(ah, chan, ¢ers); resetFreqBin = FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)); freqBin = XPA_LVL_FREQ(0) & 0xff; biaslevel = (u8) (XPA_LVL_FREQ(0) >> 14); freqCount++; while (freqCount < 3) { if (XPA_LVL_FREQ(freqCount) == 0x0) break; freqBin = XPA_LVL_FREQ(freqCount) & 0xff; if (resetFreqBin >= freqBin) biaslevel = (u8)(XPA_LVL_FREQ(freqCount) >> 14); else break; freqCount++; } } if (IS_CHAN_2GHZ(chan)) { INI_RA(&ah->iniAddac, 7, 1) = (INI_RA(&ah->iniAddac, 7, 1) & (~0x18)) | biaslevel << 3; } else { INI_RA(&ah->iniAddac, 6, 1) = (INI_RA(&ah->iniAddac, 6, 1) & (~0xc0)) | biaslevel << 6; } #undef XPA_LVL_FREQ } static int16_t ath9k_change_gain_boundary_setting(struct ath_hw *ah, u16 *gb, u16 numXpdGain, u16 pdGainOverlap_t2, int8_t pwr_table_offset, int16_t *diff) { u16 k; /* Prior to writing the boundaries or the pdadc vs. power table * into the chip registers the default starting point on the pdadc * vs. power table needs to be checked and the curve boundaries * adjusted accordingly */ if (AR_SREV_9280_20_OR_LATER(ah)) { u16 gb_limit; if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { /* get the difference in dB */ *diff = (u16)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB); /* get the number of half dB steps */ *diff *= 2; /* change the original gain boundary settings * by the number of half dB steps */ for (k = 0; k < numXpdGain; k++) gb[k] = (u16)(gb[k] - *diff); } /* Because of a hardware limitation, ensure the gain boundary * is not larger than (63 - overlap) */ gb_limit = (u16)(MAX_RATE_POWER - pdGainOverlap_t2); for (k = 0; k < numXpdGain; k++) gb[k] = (u16)min(gb_limit, gb[k]); } return *diff; } static void ath9k_adjust_pdadc_values(struct ath_hw *ah, int8_t pwr_table_offset, int16_t diff, u8 *pdadcValues) { #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff) u16 k; /* If this is a board that has a pwrTableOffset that differs from * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the * pdadc vs pwr table needs to be adjusted prior to writing to the * chip. */ if (AR_SREV_9280_20_OR_LATER(ah)) { if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { /* shift the table to start at the new offset */ for (k = 0; k < (u16)NUM_PDADC(diff); k++ ) { pdadcValues[k] = pdadcValues[k + diff]; } /* fill the back of the table */ for (k = (u16)NUM_PDADC(diff); k < NUM_PDADC(0); k++) { pdadcValues[k] = pdadcValues[NUM_PDADC(diff)]; } } } #undef NUM_PDADC } static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, struct ath9k_channel *chan) { #define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x) #define SM_PDGAIN_B(x, y) \ SM((gainBoundaries[x]), AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##y) struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; struct cal_data_per_freq *pRawDataset; u8 *pCalBChans = NULL; u16 pdGainOverlap_t2; static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK]; u16 numPiers, i, j; int16_t diff = 0; u16 numXpdGain, xpdMask; u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 }; u32 reg32, regOffset, regChainOffset; int16_t modalIdx; int8_t pwr_table_offset; modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0; xpdMask = pEepData->modalHeader[modalIdx].xpdGain; pwr_table_offset = ah->eep_ops->get_eeprom(ah, EEP_PWR_TABLE_OFFSET); if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_2) { pdGainOverlap_t2 = pEepData->modalHeader[modalIdx].pdGainOverlap; } else { pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); } if (IS_CHAN_2GHZ(chan)) { pCalBChans = pEepData->calFreqPier2G; numPiers = AR5416_NUM_2G_CAL_PIERS; } else { pCalBChans = pEepData->calFreqPier5G; numPiers = AR5416_NUM_5G_CAL_PIERS; } if (OLC_FOR_AR9280_20_LATER && IS_CHAN_2GHZ(chan)) { pRawDataset = pEepData->calPierData2G[0]; ah->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0]; } numXpdGain = 0; for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { if (numXpdGain >= AR5416_NUM_PD_GAINS) break; xpdGainValues[numXpdGain] = (u16)(AR5416_PD_GAINS_IN_MASK - i); numXpdGain++; } } REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (numXpdGain - 1) & 0x3); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, xpdGainValues[0]); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, xpdGainValues[1]); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, xpdGainValues[2]); for (i = 0; i < AR5416_MAX_CHAINS; i++) { if ((ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0)) { regChainOffset = (i == 1) ? 0x2000 : 0x1000; } else regChainOffset = i * 0x1000; if (pEepData->baseEepHeader.txMask & (1 << i)) { if (IS_CHAN_2GHZ(chan)) pRawDataset = pEepData->calPierData2G[i]; else pRawDataset = pEepData->calPierData5G[i]; if (OLC_FOR_AR9280_20_LATER) { u8 pcdacIdx; u8 txPower; ath9k_get_txgain_index(ah, chan, (struct calDataPerFreqOpLoop *)pRawDataset, pCalBChans, numPiers, &txPower, &pcdacIdx); ath9k_olc_get_pdadcs(ah, pcdacIdx, txPower/2, pdadcValues); } else { ath9k_hw_get_gain_boundaries_pdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, gainBoundaries, pdadcValues, numXpdGain); } diff = ath9k_change_gain_boundary_setting(ah, gainBoundaries, numXpdGain, pdGainOverlap_t2, pwr_table_offset, &diff); ENABLE_REGWRITE_BUFFER(ah); if (OLC_FOR_AR9280_20_LATER) { REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM_PD_GAIN(1) | SM_PD_GAIN(2) | SM_PD_GAIN(3) | SM_PD_GAIN(4)); } else { REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP)| SM_PDGAIN_B(0, 1) | SM_PDGAIN_B(1, 2) | SM_PDGAIN_B(2, 3) | SM_PDGAIN_B(3, 4)); } ath9k_adjust_pdadc_values(ah, pwr_table_offset, diff, pdadcValues); regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { reg32 = get_unaligned_le32(&pdadcValues[4 * j]); REG_WRITE(ah, regOffset, reg32); ath_dbg(common, EEPROM, "PDADC (%d,%4x): %4.4x %8.8x\n", i, regChainOffset, regOffset, reg32); ath_dbg(common, EEPROM, "PDADC: Chain %d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d |\n", i, 4 * j, pdadcValues[4 * j], 4 * j + 1, pdadcValues[4 * j + 1], 4 * j + 2, pdadcValues[4 * j + 2], 4 * j + 3, pdadcValues[4 * j + 3]); regOffset += 4; } REGWRITE_BUFFER_FLUSH(ah); } } #undef SM_PD_GAIN #undef SM_PDGAIN_B } static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, struct ath9k_channel *chan, int16_t *ratesArray, u16 cfgCtl, u16 antenna_reduction, u16 powerLimit) { struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; u16 twiceMaxEdgePower; int i; struct cal_ctl_data *rep; struct cal_target_power_leg targetPowerOfdm, targetPowerCck = { 0, { 0, 0, 0, 0} }; struct cal_target_power_leg targetPowerOfdmExt = { 0, { 0, 0, 0, 0} }, targetPowerCckExt = { 0, { 0, 0, 0, 0 } }; struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = { 0, {0, 0, 0, 0} }; u16 scaledPower = 0, minCtlPower; static const u16 ctlModesFor11a[] = { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 }; static const u16 ctlModesFor11g[] = { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 }; u16 numCtlModes; const u16 *pCtlMode; u16 ctlMode, freq; struct chan_centers centers; int tx_chainmask; u16 twiceMinEdgePower; tx_chainmask = ah->txchainmask; ath9k_hw_get_channel_centers(ah, chan, ¢ers); scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit, antenna_reduction); if (IS_CHAN_2GHZ(chan)) { numCtlModes = ARRAY_SIZE(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; pCtlMode = ctlModesFor11g; ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, false); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, false); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower2GHT20, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, false); if (IS_CHAN_HT40(chan)) { numCtlModes = ARRAY_SIZE(ctlModesFor11g); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower2GHT40, AR5416_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, true); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, true); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, true); } } else { numCtlModes = ARRAY_SIZE(ctlModesFor11a) - SUB_NUM_CTL_MODES_AT_5G_40; pCtlMode = ctlModesFor11a; ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdm, 4, false); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower5GHT20, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerHt20, 8, false); if (IS_CHAN_HT40(chan)) { numCtlModes = ARRAY_SIZE(ctlModesFor11a); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower5GHT40, AR5416_NUM_5G_40_TARGET_POWERS, &targetPowerHt40, 8, true); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, true); } } for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || (pCtlMode[ctlMode] == CTL_2GHT40); if (isHt40CtlMode) freq = centers.synth_center; else if (pCtlMode[ctlMode] & EXT_ADDITIVE) freq = centers.ext_center; else freq = centers.ctl_center; twiceMaxEdgePower = MAX_RATE_POWER; for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == pEepData->ctlIndex[i]) || (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { rep = &(pEepData->ctlData[i]); twiceMinEdgePower = ath9k_hw_get_max_edge_power(freq, rep->ctlEdges[ar5416_get_ntxchains(tx_chainmask) - 1], IS_CHAN_2GHZ(chan), AR5416_NUM_BAND_EDGES); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { twiceMaxEdgePower = min(twiceMaxEdgePower, twiceMinEdgePower); } else { twiceMaxEdgePower = twiceMinEdgePower; break; } } } minCtlPower = min(twiceMaxEdgePower, scaledPower); switch (pCtlMode[ctlMode]) { case CTL_11B: for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); i++) { targetPowerCck.tPow2x[i] = min((u16)targetPowerCck.tPow2x[i], minCtlPower); } break; case CTL_11A: case CTL_11G: for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) { targetPowerOfdm.tPow2x[i] = min((u16)targetPowerOfdm.tPow2x[i], minCtlPower); } break; case CTL_5GHT20: case CTL_2GHT20: for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) { targetPowerHt20.tPow2x[i] = min((u16)targetPowerHt20.tPow2x[i], minCtlPower); } break; case CTL_11B_EXT: targetPowerCckExt.tPow2x[0] = min((u16) targetPowerCckExt.tPow2x[0], minCtlPower); break; case CTL_11A_EXT: case CTL_11G_EXT: targetPowerOfdmExt.tPow2x[0] = min((u16) targetPowerOfdmExt.tPow2x[0], minCtlPower); break; case CTL_5GHT40: case CTL_2GHT40: for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { targetPowerHt40.tPow2x[i] = min((u16)targetPowerHt40.tPow2x[i], minCtlPower); } break; default: break; } } ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm.tPow2x[0]; ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1]; ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2]; ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3]; ratesArray[rateXr] = targetPowerOfdm.tPow2x[0]; for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i]; if (IS_CHAN_2GHZ(chan)) { ratesArray[rate1l] = targetPowerCck.tPow2x[0]; ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck.tPow2x[1]; ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck.tPow2x[2]; ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck.tPow2x[3]; } if (IS_CHAN_HT40(chan)) { for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { ratesArray[rateHt40_0 + i] = targetPowerHt40.tPow2x[i]; } ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0]; ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0]; ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0]; if (IS_CHAN_2GHZ(chan)) { ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; } } } static void ath9k_hw_def_set_txpower(struct ath_hw *ah, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, u8 powerLimit, bool test) { #define RT_AR_DELTA(x) (ratesArray[x] - cck_ofdm_delta) struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; struct modal_eep_header *pModal = &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]); int16_t ratesArray[Ar5416RateSize]; u8 ht40PowerIncForPdadc = 2; int i, cck_ofdm_delta = 0; memset(ratesArray, 0, sizeof(ratesArray)); if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_2) { ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; } ath9k_hw_set_def_power_per_rate_table(ah, chan, &ratesArray[0], cfgCtl, twiceAntennaReduction, powerLimit); ath9k_hw_set_def_power_cal_table(ah, chan); regulatory->max_power_level = 0; for (i = 0; i < ARRAY_SIZE(ratesArray); i++) { if (ratesArray[i] > MAX_RATE_POWER) ratesArray[i] = MAX_RATE_POWER; if (ratesArray[i] > regulatory->max_power_level) regulatory->max_power_level = ratesArray[i]; } ath9k_hw_update_regulatory_maxpower(ah); if (test) return; if (AR_SREV_9280_20_OR_LATER(ah)) { for (i = 0; i < Ar5416RateSize; i++) { int8_t pwr_table_offset; pwr_table_offset = ah->eep_ops->get_eeprom(ah, EEP_PWR_TABLE_OFFSET); ratesArray[i] -= pwr_table_offset * 2; } } ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, ATH9K_POW_SM(ratesArray[rate18mb], 24) | ATH9K_POW_SM(ratesArray[rate12mb], 16) | ATH9K_POW_SM(ratesArray[rate9mb], 8) | ATH9K_POW_SM(ratesArray[rate6mb], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, ATH9K_POW_SM(ratesArray[rate54mb], 24) | ATH9K_POW_SM(ratesArray[rate48mb], 16) | ATH9K_POW_SM(ratesArray[rate36mb], 8) | ATH9K_POW_SM(ratesArray[rate24mb], 0)); if (IS_CHAN_2GHZ(chan)) { if (OLC_FOR_AR9280_20_LATER) { cck_ofdm_delta = 2; REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, ATH9K_POW_SM(RT_AR_DELTA(rate2s), 24) | ATH9K_POW_SM(RT_AR_DELTA(rate2l), 16) | ATH9K_POW_SM(ratesArray[rateXr], 8) | ATH9K_POW_SM(RT_AR_DELTA(rate1l), 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, ATH9K_POW_SM(RT_AR_DELTA(rate11s), 24) | ATH9K_POW_SM(RT_AR_DELTA(rate11l), 16) | ATH9K_POW_SM(RT_AR_DELTA(rate5_5s), 8) | ATH9K_POW_SM(RT_AR_DELTA(rate5_5l), 0)); } else { REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, ATH9K_POW_SM(ratesArray[rate2s], 24) | ATH9K_POW_SM(ratesArray[rate2l], 16) | ATH9K_POW_SM(ratesArray[rateXr], 8) | ATH9K_POW_SM(ratesArray[rate1l], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, ATH9K_POW_SM(ratesArray[rate11s], 24) | ATH9K_POW_SM(ratesArray[rate11l], 16) | ATH9K_POW_SM(ratesArray[rate5_5s], 8) | ATH9K_POW_SM(ratesArray[rate5_5l], 0)); } } REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, ATH9K_POW_SM(ratesArray[rateHt20_3], 24) | ATH9K_POW_SM(ratesArray[rateHt20_2], 16) | ATH9K_POW_SM(ratesArray[rateHt20_1], 8) | ATH9K_POW_SM(ratesArray[rateHt20_0], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, ATH9K_POW_SM(ratesArray[rateHt20_7], 24) | ATH9K_POW_SM(ratesArray[rateHt20_6], 16) | ATH9K_POW_SM(ratesArray[rateHt20_5], 8) | ATH9K_POW_SM(ratesArray[rateHt20_4], 0)); if (IS_CHAN_HT40(chan)) { REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, ATH9K_POW_SM(ratesArray[rateHt40_3] + ht40PowerIncForPdadc, 24) | ATH9K_POW_SM(ratesArray[rateHt40_2] + ht40PowerIncForPdadc, 16) | ATH9K_POW_SM(ratesArray[rateHt40_1] + ht40PowerIncForPdadc, 8) | ATH9K_POW_SM(ratesArray[rateHt40_0] + ht40PowerIncForPdadc, 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, ATH9K_POW_SM(ratesArray[rateHt40_7] + ht40PowerIncForPdadc, 24) | ATH9K_POW_SM(ratesArray[rateHt40_6] + ht40PowerIncForPdadc, 16) | ATH9K_POW_SM(ratesArray[rateHt40_5] + ht40PowerIncForPdadc, 8) | ATH9K_POW_SM(ratesArray[rateHt40_4] + ht40PowerIncForPdadc, 0)); if (OLC_FOR_AR9280_20_LATER) { REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) | ATH9K_POW_SM(RT_AR_DELTA(rateExtCck), 16) | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) | ATH9K_POW_SM(RT_AR_DELTA(rateDupCck), 0)); } else { REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) | ATH9K_POW_SM(ratesArray[rateExtCck], 16) | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) | ATH9K_POW_SM(ratesArray[rateDupCck], 0)); } } REG_WRITE(ah, AR_PHY_POWER_TX_SUB, ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6) | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0)); REGWRITE_BUFFER_FLUSH(ah); } static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { #define EEP_DEF_SPURCHAN \ (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan) struct ath_common *common = ath9k_hw_common(ah); u16 spur_val = AR_NO_SPUR; ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { case SPUR_DISABLE: break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_DEF_SPURCHAN; break; } return spur_val; #undef EEP_DEF_SPURCHAN } const struct eeprom_ops eep_def_ops = { .check_eeprom = ath9k_hw_def_check_eeprom, .get_eeprom = ath9k_hw_def_get_eeprom, .fill_eeprom = ath9k_hw_def_fill_eeprom, .dump_eeprom = ath9k_hw_def_dump_eeprom, .get_eeprom_ver = ath9k_hw_def_get_eeprom_ver, .get_eeprom_rev = ath9k_hw_def_get_eeprom_rev, .set_board_values = ath9k_hw_def_set_board_values, .set_addac = ath9k_hw_def_set_addac, .set_txpower = ath9k_hw_def_set_txpower, .get_spur_channel = ath9k_hw_def_get_spur_channel }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/eeprom.c0000644000175000017500000003437012026211315023717 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val) { REG_WRITE(ah, reg, val); if (ah->config.analog_shiftreg) udelay(100); } void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, u32 shift, u32 val) { u32 regVal; regVal = REG_READ(ah, reg) & ~mask; regVal |= (val << shift) & mask; REG_WRITE(ah, reg, regVal); if (ah->config.analog_shiftreg) udelay(100); } int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight == srcLeft) { rv = targetLeft; } else { rv = (int16_t) (((target - srcLeft) * targetRight + (srcRight - target) * targetLeft) / (srcRight - srcLeft)); } return rv; } bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, u16 *indexL, u16 *indexR) { u16 i; if (target <= pList[0]) { *indexL = *indexR = 0; return true; } if (target >= pList[listSize - 1]) { *indexL = *indexR = (u16) (listSize - 1); return true; } for (i = 0; i < listSize - 1; i++) { if (pList[i] == target) { *indexL = *indexR = i; return true; } if (target < pList[i + 1]) { *indexL = i; *indexR = (u16) (i + 1); return false; } } return false; } void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, int eep_start_loc, int size) { int i = 0, j, addr; u32 addrdata[8]; u32 data[8]; for (addr = 0; addr < size; addr++) { addrdata[i] = AR5416_EEPROM_OFFSET + ((addr + eep_start_loc) << AR5416_EEPROM_S); i++; if (i == 8) { REG_READ_MULTI(ah, addrdata, data, i); for (j = 0; j < i; j++) { *eep_data = data[j]; eep_data++; } i = 0; } } if (i != 0) { REG_READ_MULTI(ah, addrdata, data, i); for (j = 0; j < i; j++) { *eep_data = data[j]; eep_data++; } } } bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data) { return common->bus_ops->eeprom_read(common, off, data); } void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, u8 *pVpdList, u16 numIntercepts, u8 *pRetVpdList) { u16 i, k; u8 currPwr = pwrMin; u16 idxL = 0, idxR = 0; for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { ath9k_hw_get_lower_upper_index(currPwr, pPwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; if (idxL == numIntercepts - 1) idxL = (u16) (numIntercepts - 2); if (pPwrList[idxL] == pPwrList[idxR]) k = pVpdList[idxL]; else k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / (pPwrList[idxR] - pPwrList[idxL])); pRetVpdList[i] = (u8) k; currPwr += 2; } } void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, struct ath9k_channel *chan, struct cal_target_power_leg *powInfo, u16 numChannels, struct cal_target_power_leg *pNewPower, u16 numRates, bool isExtTarget) { struct chan_centers centers; u16 clo, chi; int i; int matchIndex = -1, lowIndex = -1; u16 freq; ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = (isExtTarget) ? centers.ext_center : centers.ctl_center; if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, IS_CHAN_2GHZ(chan)) && i > 0 && freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, IS_CHAN_2GHZ(chan))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) matchIndex = i - 1; } if (matchIndex != -1) { *pNewPower = powInfo[matchIndex]; } else { clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, IS_CHAN_2GHZ(chan)); chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } void ath9k_hw_get_target_powers(struct ath_hw *ah, struct ath9k_channel *chan, struct cal_target_power_ht *powInfo, u16 numChannels, struct cal_target_power_ht *pNewPower, u16 numRates, bool isHt40Target) { struct chan_centers centers; u16 clo, chi; int i; int matchIndex = -1, lowIndex = -1; u16 freq; ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = isHt40Target ? centers.synth_center : centers.ctl_center; if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, IS_CHAN_2GHZ(chan)) && i > 0 && freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, IS_CHAN_2GHZ(chan))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) matchIndex = i - 1; } if (matchIndex != -1) { *pNewPower = powInfo[matchIndex]; } else { clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, IS_CHAN_2GHZ(chan)); chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, bool is2GHz, int num_band_edges) { u16 twiceMaxEdgePower = MAX_RATE_POWER; int i; for (i = 0; (i < num_band_edges) && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) { twiceMaxEdgePower = CTL_EDGE_TPOWER(pRdEdgesPower[i].ctl); break; } else if ((i > 0) && (freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz))) { if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel, is2GHz) < freq && CTL_EDGE_FLAGS(pRdEdgesPower[i - 1].ctl)) { twiceMaxEdgePower = CTL_EDGE_TPOWER(pRdEdgesPower[i - 1].ctl); } break; } } return twiceMaxEdgePower; } u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit, u8 antenna_reduction) { u16 reduction = antenna_reduction; /* * Reduce scaled Power by number of chains active * to get the per chain tx power level. */ switch (ar5416_get_ntxchains(ah->txchainmask)) { case 1: break; case 2: reduction += POWER_CORRECTION_FOR_TWO_CHAIN; break; case 3: reduction += POWER_CORRECTION_FOR_THREE_CHAIN; break; } if (power_limit > reduction) power_limit -= reduction; else power_limit = 0; return power_limit; } void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); switch (ar5416_get_ntxchains(ah->txchainmask)) { case 1: break; case 2: regulatory->max_power_level += POWER_CORRECTION_FOR_TWO_CHAIN; break; case 3: regulatory->max_power_level += POWER_CORRECTION_FOR_THREE_CHAIN; break; default: ath_dbg(common, EEPROM, "Invalid chainmask configuration\n"); break; } } void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, struct ath9k_channel *chan, void *pRawDataSet, u8 *bChans, u16 availPiers, u16 tPdGainOverlap, u16 *pPdGainBoundaries, u8 *pPDADCValues, u16 numXpdGains) { int i, j, k; int16_t ss; u16 idxL = 0, idxR = 0, numPiers; static u8 vpdTableL[AR5416_NUM_PD_GAINS] [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; static u8 vpdTableR[AR5416_NUM_PD_GAINS] [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; static u8 vpdTableI[AR5416_NUM_PD_GAINS] [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; u8 minPwrT4[AR5416_NUM_PD_GAINS]; u8 maxPwrT4[AR5416_NUM_PD_GAINS]; int16_t vpdStep; int16_t tmpVal; u16 sizeCurrVpdTable, maxIndex, tgtIndex; bool match; int16_t minDelta = 0; struct chan_centers centers; int pdgain_boundary_default; struct cal_data_per_freq *data_def = pRawDataSet; struct cal_data_per_freq_4k *data_4k = pRawDataSet; struct cal_data_per_freq_ar9287 *data_9287 = pRawDataSet; bool eeprom_4k = AR_SREV_9285(ah) || AR_SREV_9271(ah); int intercepts; if (AR_SREV_9287(ah)) intercepts = AR9287_PD_GAIN_ICEPTS; else intercepts = AR5416_PD_GAIN_ICEPTS; memset(&minPwrT4, 0, AR5416_NUM_PD_GAINS); ath9k_hw_get_channel_centers(ah, chan, ¢ers); for (numPiers = 0; numPiers < availPiers; numPiers++) { if (bChans[numPiers] == AR5416_BCHAN_UNUSED) break; } match = ath9k_hw_get_lower_upper_index((u8)FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)), bChans, numPiers, &idxL, &idxR); if (match) { if (AR_SREV_9287(ah)) { /* FIXME: array overrun? */ for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_9287[idxL].pwrPdg[i][0]; maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_9287[idxL].pwrPdg[i], data_9287[idxL].vpdPdg[i], intercepts, vpdTableI[i]); } } else if (eeprom_4k) { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_4k[idxL].pwrPdg[i][0]; maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_4k[idxL].pwrPdg[i], data_4k[idxL].vpdPdg[i], intercepts, vpdTableI[i]); } } else { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_def[idxL].pwrPdg[i][0]; maxPwrT4[i] = data_def[idxL].pwrPdg[i][4]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_def[idxL].pwrPdg[i], data_def[idxL].vpdPdg[i], intercepts, vpdTableI[i]); } } } else { for (i = 0; i < numXpdGains; i++) { if (AR_SREV_9287(ah)) { pVpdL = data_9287[idxL].vpdPdg[i]; pPwrL = data_9287[idxL].pwrPdg[i]; pVpdR = data_9287[idxR].vpdPdg[i]; pPwrR = data_9287[idxR].pwrPdg[i]; } else if (eeprom_4k) { pVpdL = data_4k[idxL].vpdPdg[i]; pPwrL = data_4k[idxL].pwrPdg[i]; pVpdR = data_4k[idxR].vpdPdg[i]; pPwrR = data_4k[idxR].pwrPdg[i]; } else { pVpdL = data_def[idxL].vpdPdg[i]; pPwrL = data_def[idxL].pwrPdg[i]; pVpdR = data_def[idxR].vpdPdg[i]; pPwrR = data_def[idxR].pwrPdg[i]; } minPwrT4[i] = max(pPwrL[0], pPwrR[0]); maxPwrT4[i] = min(pPwrL[intercepts - 1], pPwrR[intercepts - 1]); ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], pPwrL, pVpdL, intercepts, vpdTableL[i]); ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], pPwrR, pVpdR, intercepts, vpdTableR[i]); for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { vpdTableI[i][j] = (u8)(ath9k_hw_interpolate((u16) FREQ2FBIN(centers. synth_center, IS_CHAN_2GHZ (chan)), bChans[idxL], bChans[idxR], vpdTableL[i][j], vpdTableR[i][j])); } } } k = 0; for (i = 0; i < numXpdGains; i++) { if (i == (numXpdGains - 1)) pPdGainBoundaries[i] = (u16)(maxPwrT4[i] / 2); else pPdGainBoundaries[i] = (u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4); pPdGainBoundaries[i] = min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]); minDelta = 0; if (i == 0) { if (AR_SREV_9280_20_OR_LATER(ah)) ss = (int16_t)(0 - (minPwrT4[i] / 2)); else ss = 0; } else { ss = (int16_t)((pPdGainBoundaries[i - 1] - (minPwrT4[i] / 2)) - tPdGainOverlap + 1 + minDelta); } vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1); tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap - (minPwrT4[i] / 2)); maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { pPDADCValues[k++] = vpdTableI[i][ss++]; } vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - vpdTableI[i][sizeCurrVpdTable - 2]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); if (tgtIndex >= maxIndex) { while ((ss <= tgtIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex + 1) * vpdStep)); pPDADCValues[k++] = (u8)((tmpVal > 255) ? 255 : tmpVal); ss++; } } } if (eeprom_4k) pdgain_boundary_default = 58; else pdgain_boundary_default = pPdGainBoundaries[i - 1]; while (i < AR5416_PD_GAINS_IN_MASK) { pPdGainBoundaries[i] = pdgain_boundary_default; i++; } while (k < AR5416_NUM_PDADC_VALUES) { pPDADCValues[k] = pPDADCValues[k - 1]; k++; } } int ath9k_hw_eeprom_init(struct ath_hw *ah) { int status; if (AR_SREV_9300_20_OR_LATER(ah)) ah->eep_ops = &eep_ar9300_ops; else if (AR_SREV_9287(ah)) { ah->eep_ops = &eep_ar9287_ops; } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { ah->eep_ops = &eep_4k_ops; } else { ah->eep_ops = &eep_def_ops; } if (!ah->eep_ops->fill_eeprom(ah)) return -EIO; status = ah->eep_ops->check_eeprom(ah); return status; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/eeprom_9287.c0000644000175000017500000007642612026211315024420 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar9002_phy.h" #define SIZE_EEPROM_AR9287 (sizeof(struct ar9287_eeprom) / sizeof(u16)) static int ath9k_hw_ar9287_get_eeprom_ver(struct ath_hw *ah) { return (ah->eeprom.map9287.baseEepHeader.version >> 12) & 0xF; } static int ath9k_hw_ar9287_get_eeprom_rev(struct ath_hw *ah) { return (ah->eeprom.map9287.baseEepHeader.version) & 0xFFF; } static bool __ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah) { struct ar9287_eeprom *eep = &ah->eeprom.map9287; struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data; int addr, eep_start_loc = AR9287_EEP_START_LOC; eep_data = (u16 *)eep; for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) { if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, eep_data)) { ath_dbg(common, EEPROM, "Unable to read eeprom region\n"); return false; } eep_data++; } return true; } static bool __ath9k_hw_usb_ar9287_fill_eeprom(struct ath_hw *ah) { u16 *eep_data = (u16 *)&ah->eeprom.map9287; ath9k_hw_usb_gen_fill_eeprom(ah, eep_data, AR9287_HTC_EEP_START_LOC, SIZE_EEPROM_AR9287); return true; } static bool ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { ath_dbg(common, EEPROM, "Reading from EEPROM, not flash\n"); } if (common->bus_ops->ath_bus_type == ATH_USB) return __ath9k_hw_usb_ar9287_fill_eeprom(ah); else return __ath9k_hw_ar9287_fill_eeprom(ah); } #if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS) static u32 ar9287_dump_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_ar9287_header *modal_hdr) { PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]); PR_EEP("Chain1 Ant. Control", modal_hdr->antCtrlChain[1]); PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon); PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]); PR_EEP("Chain1 Ant. Gain", modal_hdr->antennaGainCh[1]); PR_EEP("Switch Settle", modal_hdr->switchSettling); PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]); PR_EEP("Chain1 TxRxAtten", modal_hdr->txRxAttenCh[1]); PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]); PR_EEP("Chain1 RxTxMargin", modal_hdr->rxTxMarginCh[1]); PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize); PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff); PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn); PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn); PR_EEP("CCA Threshold)", modal_hdr->thresh62); PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]); PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]); PR_EEP("xpdGain", modal_hdr->xpdGain); PR_EEP("External PD", modal_hdr->xpd); PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]); PR_EEP("Chain1 I Coefficient", modal_hdr->iqCalICh[1]); PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]); PR_EEP("Chain1 Q Coefficient", modal_hdr->iqCalQCh[1]); PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap); PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl); PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart); PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn); PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc); PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]); PR_EEP("Chain1 bswAtten", modal_hdr->bswAtten[1]); PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]); PR_EEP("Chain1 bswMargin", modal_hdr->bswMargin[1]); PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40); PR_EEP("AR92x7 Version", modal_hdr->version); PR_EEP("DriverBias1", modal_hdr->db1); PR_EEP("DriverBias2", modal_hdr->db1); PR_EEP("CCK OutputBias", modal_hdr->ob_cck); PR_EEP("PSK OutputBias", modal_hdr->ob_psk); PR_EEP("QAM OutputBias", modal_hdr->ob_qam); PR_EEP("PAL_OFF OutputBias", modal_hdr->ob_pal_off); return len; } static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { struct ar9287_eeprom *eep = &ah->eeprom.map9287; struct base_eep_ar9287_header *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); len = ar9287_dump_modal_eeprom(buf, len, size, &eep->modalHeader); goto out; } PR_EEP("Major Version", pBase->version >> 12); PR_EEP("Minor Version", pBase->version & 0xFFF); PR_EEP("Checksum", pBase->checksum); PR_EEP("Length", pBase->length); PR_EEP("RegDomain1", pBase->regDmn[0]); PR_EEP("RegDomain2", pBase->regDmn[1]); PR_EEP("TX Mask", pBase->txMask); PR_EEP("RX Mask", pBase->rxMask); PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01)); PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF); PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF); PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); PR_EEP("Power Table Offset", pBase->pwrTableOffset); PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl); len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", pBase->macAddr); out: if (len > size) len = size; return len; } #else static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { return 0; } #endif static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) { u32 sum = 0, el, integer; u16 temp, word, magic, magic2, *eepdata; int i, addr; bool need_swap = false; struct ar9287_eeprom *eep = &ah->eeprom.map9287; struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { ath_err(common, "Reading Magic # failed\n"); return false; } ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); if (magic2 == AR5416_EEPROM_MAGIC) { need_swap = true; eepdata = (u16 *)(&ah->eeprom); for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) { temp = swab16(*eepdata); *eepdata = temp; eepdata++; } } else { ath_err(common, "Invalid EEPROM Magic. Endianness mismatch.\n"); return -EINVAL; } } } ath_dbg(common, EEPROM, "need_swap = %s\n", need_swap ? "True" : "False"); if (need_swap) el = swab16(ah->eeprom.map9287.baseEepHeader.length); else el = ah->eeprom.map9287.baseEepHeader.length; if (el > sizeof(struct ar9287_eeprom)) el = sizeof(struct ar9287_eeprom) / sizeof(u16); else el = el / sizeof(u16); eepdata = (u16 *)(&ah->eeprom); for (i = 0; i < el; i++) sum ^= *eepdata++; if (need_swap) { word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; word = swab16(eep->baseEepHeader.checksum); eep->baseEepHeader.checksum = word; word = swab16(eep->baseEepHeader.version); eep->baseEepHeader.version = word; word = swab16(eep->baseEepHeader.regDmn[0]); eep->baseEepHeader.regDmn[0] = word; word = swab16(eep->baseEepHeader.regDmn[1]); eep->baseEepHeader.regDmn[1] = word; word = swab16(eep->baseEepHeader.rfSilent); eep->baseEepHeader.rfSilent = word; word = swab16(eep->baseEepHeader.blueToothOptions); eep->baseEepHeader.blueToothOptions = word; word = swab16(eep->baseEepHeader.deviceCap); eep->baseEepHeader.deviceCap = word; integer = swab32(eep->modalHeader.antCtrlCommon); eep->modalHeader.antCtrlCommon = integer; for (i = 0; i < AR9287_MAX_CHAINS; i++) { integer = swab32(eep->modalHeader.antCtrlChain[i]); eep->modalHeader.antCtrlChain[i] = integer; } for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { word = swab16(eep->modalHeader.spurChans[i].spurChan); eep->modalHeader.spurChans[i].spurChan = word; } } if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; } return 0; } static u32 ath9k_hw_ar9287_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { struct ar9287_eeprom *eep = &ah->eeprom.map9287; struct modal_eep_ar9287_header *pModal = &eep->modalHeader; struct base_eep_ar9287_header *pBase = &eep->baseEepHeader; u16 ver_minor; ver_minor = pBase->version & AR9287_EEP_VER_MINOR_MASK; switch (param) { case EEP_NFTHRESH_2: return pModal->noiseFloorThreshCh[0]; case EEP_MAC_LSW: return get_unaligned_be16(pBase->macAddr); case EEP_MAC_MID: return get_unaligned_be16(pBase->macAddr + 2); case EEP_MAC_MSW: return get_unaligned_be16(pBase->macAddr + 4); case EEP_REG_0: return pBase->regDmn[0]; case EEP_OP_CAP: return pBase->deviceCap; case EEP_OP_MODE: return pBase->opCapFlags; case EEP_RF_SILENT: return pBase->rfSilent; case EEP_MINOR_REV: return ver_minor; case EEP_TX_MASK: return pBase->txMask; case EEP_RX_MASK: return pBase->rxMask; case EEP_DEV_TYPE: return pBase->deviceType; case EEP_OL_PWRCTRL: return pBase->openLoopPwrCntl; case EEP_TEMPSENSE_SLOPE: if (ver_minor >= AR9287_EEP_MINOR_VER_2) return pBase->tempSensSlope; else return 0; case EEP_TEMPSENSE_SLOPE_PAL_ON: if (ver_minor >= AR9287_EEP_MINOR_VER_3) return pBase->tempSensSlopePalOn; else return 0; case EEP_ANTENNA_GAIN_2G: return max_t(u8, pModal->antennaGainCh[0], pModal->antennaGainCh[1]); default: return 0; } } static void ar9287_eeprom_get_tx_gain_index(struct ath_hw *ah, struct ath9k_channel *chan, struct cal_data_op_loop_ar9287 *pRawDatasetOpLoop, u8 *pCalChans, u16 availPiers, int8_t *pPwr) { u16 idxL = 0, idxR = 0, numPiers; bool match; struct chan_centers centers; ath9k_hw_get_channel_centers(ah, chan, ¢ers); for (numPiers = 0; numPiers < availPiers; numPiers++) { if (pCalChans[numPiers] == AR5416_BCHAN_UNUSED) break; } match = ath9k_hw_get_lower_upper_index( (u8)FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)), pCalChans, numPiers, &idxL, &idxR); if (match) { *pPwr = (int8_t) pRawDatasetOpLoop[idxL].pwrPdg[0][0]; } else { *pPwr = ((int8_t) pRawDatasetOpLoop[idxL].pwrPdg[0][0] + (int8_t) pRawDatasetOpLoop[idxR].pwrPdg[0][0])/2; } } static void ar9287_eeprom_olpc_set_pdadcs(struct ath_hw *ah, int32_t txPower, u16 chain) { u32 tmpVal; u32 a; /* Enable OLPC for chain 0 */ tmpVal = REG_READ(ah, 0xa270); tmpVal = tmpVal & 0xFCFFFFFF; tmpVal = tmpVal | (0x3 << 24); REG_WRITE(ah, 0xa270, tmpVal); /* Enable OLPC for chain 1 */ tmpVal = REG_READ(ah, 0xb270); tmpVal = tmpVal & 0xFCFFFFFF; tmpVal = tmpVal | (0x3 << 24); REG_WRITE(ah, 0xb270, tmpVal); /* Write the OLPC ref power for chain 0 */ if (chain == 0) { tmpVal = REG_READ(ah, 0xa398); tmpVal = tmpVal & 0xff00ffff; a = (txPower)&0xff; tmpVal = tmpVal | (a << 16); REG_WRITE(ah, 0xa398, tmpVal); } /* Write the OLPC ref power for chain 1 */ if (chain == 1) { tmpVal = REG_READ(ah, 0xb398); tmpVal = tmpVal & 0xff00ffff; a = (txPower)&0xff; tmpVal = tmpVal | (a << 16); REG_WRITE(ah, 0xb398, tmpVal); } } static void ath9k_hw_set_ar9287_power_cal_table(struct ath_hw *ah, struct ath9k_channel *chan) { struct cal_data_per_freq_ar9287 *pRawDataset; struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop; u8 *pCalBChans = NULL; u16 pdGainOverlap_t2; u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK]; u16 numPiers = 0, i, j; u16 numXpdGain, xpdMask; u16 xpdGainValues[AR5416_NUM_PD_GAINS] = {0, 0, 0, 0}; u32 reg32, regOffset, regChainOffset, regval; int16_t diff = 0; struct ar9287_eeprom *pEepData = &ah->eeprom.map9287; xpdMask = pEepData->modalHeader.xpdGain; if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >= AR9287_EEP_MINOR_VER_2) pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap; else pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); if (IS_CHAN_2GHZ(chan)) { pCalBChans = pEepData->calFreqPier2G; numPiers = AR9287_NUM_2G_CAL_PIERS; if (ath9k_hw_ar9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { pRawDatasetOpenLoop = (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[0]; ah->initPDADC = pRawDatasetOpenLoop->vpdPdg[0][0]; } } numXpdGain = 0; /* Calculate the value of xpdgains from the xpdGain Mask */ for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { if (numXpdGain >= AR5416_NUM_PD_GAINS) break; xpdGainValues[numXpdGain] = (u16)(AR5416_PD_GAINS_IN_MASK-i); numXpdGain++; } } REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (numXpdGain - 1) & 0x3); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, xpdGainValues[0]); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, xpdGainValues[1]); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, xpdGainValues[2]); for (i = 0; i < AR9287_MAX_CHAINS; i++) { regChainOffset = i * 0x1000; if (pEepData->baseEepHeader.txMask & (1 << i)) { pRawDatasetOpenLoop = (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[i]; if (ath9k_hw_ar9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { int8_t txPower; ar9287_eeprom_get_tx_gain_index(ah, chan, pRawDatasetOpenLoop, pCalBChans, numPiers, &txPower); ar9287_eeprom_olpc_set_pdadcs(ah, txPower, i); } else { pRawDataset = (struct cal_data_per_freq_ar9287 *) pEepData->calPierData2G[i]; ath9k_hw_get_gain_boundaries_pdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, gainBoundaries, pdadcValues, numXpdGain); } ENABLE_REGWRITE_BUFFER(ah); if (i == 0) { if (!ath9k_hw_ar9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { regval = SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4); REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, regval); } } if ((int32_t)AR9287_PWR_TABLE_OFFSET_DB != pEepData->baseEepHeader.pwrTableOffset) { diff = (u16)(pEepData->baseEepHeader.pwrTableOffset - (int32_t)AR9287_PWR_TABLE_OFFSET_DB); diff *= 2; for (j = 0; j < ((u16)AR5416_NUM_PDADC_VALUES-diff); j++) pdadcValues[j] = pdadcValues[j+diff]; for (j = (u16)(AR5416_NUM_PDADC_VALUES-diff); j < AR5416_NUM_PDADC_VALUES; j++) pdadcValues[j] = pdadcValues[AR5416_NUM_PDADC_VALUES-diff]; } if (!ath9k_hw_ar9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { reg32 = get_unaligned_le32(&pdadcValues[4 * j]); REG_WRITE(ah, regOffset, reg32); regOffset += 4; } } REGWRITE_BUFFER_FLUSH(ah); } } } static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah, struct ath9k_channel *chan, int16_t *ratesArray, u16 cfgCtl, u16 antenna_reduction, u16 powerLimit) { #define CMP_CTL \ (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == \ pEepData->ctlIndex[i]) #define CMP_NO_CTL \ (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == \ ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL)) u16 twiceMaxEdgePower; int i; struct cal_ctl_data_ar9287 *rep; struct cal_target_power_leg targetPowerOfdm = {0, {0, 0, 0, 0} }, targetPowerCck = {0, {0, 0, 0, 0} }; struct cal_target_power_leg targetPowerOfdmExt = {0, {0, 0, 0, 0} }, targetPowerCckExt = {0, {0, 0, 0, 0} }; struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = {0, {0, 0, 0, 0} }; u16 scaledPower = 0, minCtlPower; static const u16 ctlModesFor11g[] = { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 }; u16 numCtlModes = 0; const u16 *pCtlMode = NULL; u16 ctlMode, freq; struct chan_centers centers; int tx_chainmask; u16 twiceMinEdgePower; struct ar9287_eeprom *pEepData = &ah->eeprom.map9287; tx_chainmask = ah->txchainmask; ath9k_hw_get_channel_centers(ah, chan, ¢ers); scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit, antenna_reduction); /* * Get TX power from EEPROM. */ if (IS_CHAN_2GHZ(chan)) { /* CTL_11B, CTL_11G, CTL_2GHT20 */ numCtlModes = ARRAY_SIZE(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; pCtlMode = ctlModesFor11g; ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPowerCck, AR9287_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, false); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower2G, AR9287_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, false); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower2GHT20, AR9287_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, false); if (IS_CHAN_HT40(chan)) { /* All 2G CTLs */ numCtlModes = ARRAY_SIZE(ctlModesFor11g); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower2GHT40, AR9287_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, true); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPowerCck, AR9287_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, true); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower2G, AR9287_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, true); } } for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_2GHT40) ? true : false; if (isHt40CtlMode) freq = centers.synth_center; else if (pCtlMode[ctlMode] & EXT_ADDITIVE) freq = centers.ext_center; else freq = centers.ctl_center; twiceMaxEdgePower = MAX_RATE_POWER; /* Walk through the CTL indices stored in EEPROM */ for (i = 0; (i < AR9287_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { struct cal_ctl_edges *pRdEdgesPower; /* * Compare test group from regulatory channel list * with test mode from pCtlMode list */ if (CMP_CTL || CMP_NO_CTL) { rep = &(pEepData->ctlData[i]); pRdEdgesPower = rep->ctlEdges[ar5416_get_ntxchains(tx_chainmask) - 1]; twiceMinEdgePower = ath9k_hw_get_max_edge_power(freq, pRdEdgesPower, IS_CHAN_2GHZ(chan), AR5416_NUM_BAND_EDGES); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { twiceMaxEdgePower = min(twiceMaxEdgePower, twiceMinEdgePower); } else { twiceMaxEdgePower = twiceMinEdgePower; break; } } } minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower); /* Apply ctl mode to correct target power set */ switch (pCtlMode[ctlMode]) { case CTL_11B: for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); i++) { targetPowerCck.tPow2x[i] = (u8)min((u16)targetPowerCck.tPow2x[i], minCtlPower); } break; case CTL_11A: case CTL_11G: for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) { targetPowerOfdm.tPow2x[i] = (u8)min((u16)targetPowerOfdm.tPow2x[i], minCtlPower); } break; case CTL_5GHT20: case CTL_2GHT20: for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) { targetPowerHt20.tPow2x[i] = (u8)min((u16)targetPowerHt20.tPow2x[i], minCtlPower); } break; case CTL_11B_EXT: targetPowerCckExt.tPow2x[0] = (u8)min((u16)targetPowerCckExt.tPow2x[0], minCtlPower); break; case CTL_11A_EXT: case CTL_11G_EXT: targetPowerOfdmExt.tPow2x[0] = (u8)min((u16)targetPowerOfdmExt.tPow2x[0], minCtlPower); break; case CTL_5GHT40: case CTL_2GHT40: for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { targetPowerHt40.tPow2x[i] = (u8)min((u16)targetPowerHt40.tPow2x[i], minCtlPower); } break; default: break; } } /* Now set the rates array */ ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm.tPow2x[0]; ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1]; ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2]; ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3]; ratesArray[rateXr] = targetPowerOfdm.tPow2x[0]; for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i]; if (IS_CHAN_2GHZ(chan)) { ratesArray[rate1l] = targetPowerCck.tPow2x[0]; ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck.tPow2x[1]; ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck.tPow2x[2]; ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck.tPow2x[3]; } if (IS_CHAN_HT40(chan)) { for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) ratesArray[rateHt40_0 + i] = targetPowerHt40.tPow2x[i]; ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0]; ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0]; ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0]; if (IS_CHAN_2GHZ(chan)) ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; } #undef CMP_CTL #undef CMP_NO_CTL } static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, u8 powerLimit, bool test) { struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ar9287_eeprom *pEepData = &ah->eeprom.map9287; struct modal_eep_ar9287_header *pModal = &pEepData->modalHeader; int16_t ratesArray[Ar5416RateSize]; u8 ht40PowerIncForPdadc = 2; int i; memset(ratesArray, 0, sizeof(ratesArray)); if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >= AR9287_EEP_MINOR_VER_2) ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; ath9k_hw_set_ar9287_power_per_rate_table(ah, chan, &ratesArray[0], cfgCtl, twiceAntennaReduction, powerLimit); ath9k_hw_set_ar9287_power_cal_table(ah, chan); regulatory->max_power_level = 0; for (i = 0; i < ARRAY_SIZE(ratesArray); i++) { if (ratesArray[i] > MAX_RATE_POWER) ratesArray[i] = MAX_RATE_POWER; if (ratesArray[i] > regulatory->max_power_level) regulatory->max_power_level = ratesArray[i]; } ath9k_hw_update_regulatory_maxpower(ah); if (test) return; for (i = 0; i < Ar5416RateSize; i++) ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2; ENABLE_REGWRITE_BUFFER(ah); /* OFDM power per rate */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, ATH9K_POW_SM(ratesArray[rate18mb], 24) | ATH9K_POW_SM(ratesArray[rate12mb], 16) | ATH9K_POW_SM(ratesArray[rate9mb], 8) | ATH9K_POW_SM(ratesArray[rate6mb], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, ATH9K_POW_SM(ratesArray[rate54mb], 24) | ATH9K_POW_SM(ratesArray[rate48mb], 16) | ATH9K_POW_SM(ratesArray[rate36mb], 8) | ATH9K_POW_SM(ratesArray[rate24mb], 0)); /* CCK power per rate */ if (IS_CHAN_2GHZ(chan)) { REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, ATH9K_POW_SM(ratesArray[rate2s], 24) | ATH9K_POW_SM(ratesArray[rate2l], 16) | ATH9K_POW_SM(ratesArray[rateXr], 8) | ATH9K_POW_SM(ratesArray[rate1l], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, ATH9K_POW_SM(ratesArray[rate11s], 24) | ATH9K_POW_SM(ratesArray[rate11l], 16) | ATH9K_POW_SM(ratesArray[rate5_5s], 8) | ATH9K_POW_SM(ratesArray[rate5_5l], 0)); } /* HT20 power per rate */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, ATH9K_POW_SM(ratesArray[rateHt20_3], 24) | ATH9K_POW_SM(ratesArray[rateHt20_2], 16) | ATH9K_POW_SM(ratesArray[rateHt20_1], 8) | ATH9K_POW_SM(ratesArray[rateHt20_0], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, ATH9K_POW_SM(ratesArray[rateHt20_7], 24) | ATH9K_POW_SM(ratesArray[rateHt20_6], 16) | ATH9K_POW_SM(ratesArray[rateHt20_5], 8) | ATH9K_POW_SM(ratesArray[rateHt20_4], 0)); /* HT40 power per rate */ if (IS_CHAN_HT40(chan)) { if (ath9k_hw_ar9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, ATH9K_POW_SM(ratesArray[rateHt40_3], 24) | ATH9K_POW_SM(ratesArray[rateHt40_2], 16) | ATH9K_POW_SM(ratesArray[rateHt40_1], 8) | ATH9K_POW_SM(ratesArray[rateHt40_0], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, ATH9K_POW_SM(ratesArray[rateHt40_7], 24) | ATH9K_POW_SM(ratesArray[rateHt40_6], 16) | ATH9K_POW_SM(ratesArray[rateHt40_5], 8) | ATH9K_POW_SM(ratesArray[rateHt40_4], 0)); } else { REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, ATH9K_POW_SM(ratesArray[rateHt40_3] + ht40PowerIncForPdadc, 24) | ATH9K_POW_SM(ratesArray[rateHt40_2] + ht40PowerIncForPdadc, 16) | ATH9K_POW_SM(ratesArray[rateHt40_1] + ht40PowerIncForPdadc, 8) | ATH9K_POW_SM(ratesArray[rateHt40_0] + ht40PowerIncForPdadc, 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, ATH9K_POW_SM(ratesArray[rateHt40_7] + ht40PowerIncForPdadc, 24) | ATH9K_POW_SM(ratesArray[rateHt40_6] + ht40PowerIncForPdadc, 16) | ATH9K_POW_SM(ratesArray[rateHt40_5] + ht40PowerIncForPdadc, 8) | ATH9K_POW_SM(ratesArray[rateHt40_4] + ht40PowerIncForPdadc, 0)); } /* Dup/Ext power per rate */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) | ATH9K_POW_SM(ratesArray[rateExtCck], 16) | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) | ATH9K_POW_SM(ratesArray[rateDupCck], 0)); } REGWRITE_BUFFER_FLUSH(ah); } static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { struct ar9287_eeprom *eep = &ah->eeprom.map9287; struct modal_eep_ar9287_header *pModal = &eep->modalHeader; u32 regChainOffset, regval; u8 txRxAttenLocal; int i; pModal = &eep->modalHeader; REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon); for (i = 0; i < AR9287_MAX_CHAINS; i++) { regChainOffset = i * 0x1000; REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[i]); REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset, (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); txRxAttenLocal = pModal->txRxAttenCh[i]; REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[i]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[i]); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]); } if (IS_CHAN_HT40(chan)) REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); else REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); REG_WRITE(ah, AR_PHY_RF_CTL4, SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, pModal->thresh62); regval = REG_READ(ah, AR9287_AN_RF2G3_CH0); regval &= ~(AR9287_AN_RF2G3_DB1 | AR9287_AN_RF2G3_DB2 | AR9287_AN_RF2G3_OB_CCK | AR9287_AN_RF2G3_OB_PSK | AR9287_AN_RF2G3_OB_QAM | AR9287_AN_RF2G3_OB_PAL_OFF); regval |= (SM(pModal->db1, AR9287_AN_RF2G3_DB1) | SM(pModal->db2, AR9287_AN_RF2G3_DB2) | SM(pModal->ob_cck, AR9287_AN_RF2G3_OB_CCK) | SM(pModal->ob_psk, AR9287_AN_RF2G3_OB_PSK) | SM(pModal->ob_qam, AR9287_AN_RF2G3_OB_QAM) | SM(pModal->ob_pal_off, AR9287_AN_RF2G3_OB_PAL_OFF)); ath9k_hw_analog_shift_regwrite(ah, AR9287_AN_RF2G3_CH0, regval); regval = REG_READ(ah, AR9287_AN_RF2G3_CH1); regval &= ~(AR9287_AN_RF2G3_DB1 | AR9287_AN_RF2G3_DB2 | AR9287_AN_RF2G3_OB_CCK | AR9287_AN_RF2G3_OB_PSK | AR9287_AN_RF2G3_OB_QAM | AR9287_AN_RF2G3_OB_PAL_OFF); regval |= (SM(pModal->db1, AR9287_AN_RF2G3_DB1) | SM(pModal->db2, AR9287_AN_RF2G3_DB2) | SM(pModal->ob_cck, AR9287_AN_RF2G3_OB_CCK) | SM(pModal->ob_psk, AR9287_AN_RF2G3_OB_PSK) | SM(pModal->ob_qam, AR9287_AN_RF2G3_OB_QAM) | SM(pModal->ob_pal_off, AR9287_AN_RF2G3_OB_PAL_OFF)); ath9k_hw_analog_shift_regwrite(ah, AR9287_AN_RF2G3_CH1, regval); REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_DATA_START, pModal->txFrameToDataStart); REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON, pModal->txFrameToPaOn); ath9k_hw_analog_shift_rmw(ah, AR9287_AN_TOP2, AR9287_AN_TOP2_XPABIAS_LVL, AR9287_AN_TOP2_XPABIAS_LVL_S, pModal->xpaBiasLvl); } static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { #define EEP_MAP9287_SPURCHAN \ (ah->eeprom.map9287.modalHeader.spurChans[i].spurChan) struct ath_common *common = ath9k_hw_common(ah); u16 spur_val = AR_NO_SPUR; ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { case SPUR_DISABLE: break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_MAP9287_SPURCHAN; break; } return spur_val; #undef EEP_MAP9287_SPURCHAN } const struct eeprom_ops eep_ar9287_ops = { .check_eeprom = ath9k_hw_ar9287_check_eeprom, .get_eeprom = ath9k_hw_ar9287_get_eeprom, .fill_eeprom = ath9k_hw_ar9287_fill_eeprom, .dump_eeprom = ath9k_hw_ar9287_dump_eeprom, .get_eeprom_ver = ath9k_hw_ar9287_get_eeprom_ver, .get_eeprom_rev = ath9k_hw_ar9287_get_eeprom_rev, .set_board_values = ath9k_hw_ar9287_set_board_values, .set_txpower = ath9k_hw_ar9287_set_txpower, .get_spur_channel = ath9k_hw_ar9287_get_spur_channel }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/eeprom_4k.c0000644000175000017500000010144112026211315024307 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar9002_phy.h" static int ath9k_hw_4k_get_eeprom_ver(struct ath_hw *ah) { return ((ah->eeprom.map4k.baseEepHeader.version >> 12) & 0xF); } static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah) { return ((ah->eeprom.map4k.baseEepHeader.version) & 0xFFF); } #define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) static bool __ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u16 *eep_data = (u16 *)&ah->eeprom.map4k; int addr, eep_start_loc = 64; for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, eep_data)) { ath_dbg(common, EEPROM, "Unable to read eeprom region\n"); return false; } eep_data++; } return true; } static bool __ath9k_hw_usb_4k_fill_eeprom(struct ath_hw *ah) { u16 *eep_data = (u16 *)&ah->eeprom.map4k; ath9k_hw_usb_gen_fill_eeprom(ah, eep_data, 64, SIZE_EEPROM_4K); return true; } static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { ath_dbg(common, EEPROM, "Reading from EEPROM, not flash\n"); } if (common->bus_ops->ath_bus_type == ATH_USB) return __ath9k_hw_usb_4k_fill_eeprom(ah); else return __ath9k_hw_4k_fill_eeprom(ah); } #if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS) static u32 ath9k_dump_4k_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_4k_header *modal_hdr) { PR_EEP("Chain0 Ant. Control", modal_hdr->antCtrlChain[0]); PR_EEP("Ant. Common Control", modal_hdr->antCtrlCommon); PR_EEP("Chain0 Ant. Gain", modal_hdr->antennaGainCh[0]); PR_EEP("Switch Settle", modal_hdr->switchSettling); PR_EEP("Chain0 TxRxAtten", modal_hdr->txRxAttenCh[0]); PR_EEP("Chain0 RxTxMargin", modal_hdr->rxTxMarginCh[0]); PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize); PR_EEP("PGA Desired size", modal_hdr->pgaDesiredSize); PR_EEP("Chain0 xlna Gain", modal_hdr->xlnaGainCh[0]); PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff); PR_EEP("txEndToRxOn", modal_hdr->txEndToRxOn); PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn); PR_EEP("CCA Threshold)", modal_hdr->thresh62); PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]); PR_EEP("xpdGain", modal_hdr->xpdGain); PR_EEP("External PD", modal_hdr->xpd); PR_EEP("Chain0 I Coefficient", modal_hdr->iqCalICh[0]); PR_EEP("Chain0 Q Coefficient", modal_hdr->iqCalQCh[0]); PR_EEP("pdGainOverlap", modal_hdr->pdGainOverlap); PR_EEP("O/D Bias Version", modal_hdr->version); PR_EEP("CCK OutputBias", modal_hdr->ob_0); PR_EEP("BPSK OutputBias", modal_hdr->ob_1); PR_EEP("QPSK OutputBias", modal_hdr->ob_2); PR_EEP("16QAM OutputBias", modal_hdr->ob_3); PR_EEP("64QAM OutputBias", modal_hdr->ob_4); PR_EEP("CCK Driver1_Bias", modal_hdr->db1_0); PR_EEP("BPSK Driver1_Bias", modal_hdr->db1_1); PR_EEP("QPSK Driver1_Bias", modal_hdr->db1_2); PR_EEP("16QAM Driver1_Bias", modal_hdr->db1_3); PR_EEP("64QAM Driver1_Bias", modal_hdr->db1_4); PR_EEP("CCK Driver2_Bias", modal_hdr->db2_0); PR_EEP("BPSK Driver2_Bias", modal_hdr->db2_1); PR_EEP("QPSK Driver2_Bias", modal_hdr->db2_2); PR_EEP("16QAM Driver2_Bias", modal_hdr->db2_3); PR_EEP("64QAM Driver2_Bias", modal_hdr->db2_4); PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl); PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart); PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn); PR_EEP("HT40 Power Inc.", modal_hdr->ht40PowerIncForPdadc); PR_EEP("Chain0 bswAtten", modal_hdr->bswAtten[0]); PR_EEP("Chain0 bswMargin", modal_hdr->bswMargin[0]); PR_EEP("HT40 Switch Settle", modal_hdr->swSettleHt40); PR_EEP("Chain0 xatten2Db", modal_hdr->xatten2Db[0]); PR_EEP("Chain0 xatten2Margin", modal_hdr->xatten2Margin[0]); PR_EEP("Ant. Diversity ctl1", modal_hdr->antdiv_ctl1); PR_EEP("Ant. Diversity ctl2", modal_hdr->antdiv_ctl2); PR_EEP("TX Diversity", modal_hdr->tx_diversity); return len; } static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; struct base_eep_header_4k *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); len = ath9k_dump_4k_modal_eeprom(buf, len, size, &eep->modalHeader); goto out; } PR_EEP("Major Version", pBase->version >> 12); PR_EEP("Minor Version", pBase->version & 0xFFF); PR_EEP("Checksum", pBase->checksum); PR_EEP("Length", pBase->length); PR_EEP("RegDomain1", pBase->regDmn[0]); PR_EEP("RegDomain2", pBase->regDmn[1]); PR_EEP("TX Mask", pBase->txMask); PR_EEP("RX Mask", pBase->rxMask); PR_EEP("Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); PR_EEP("Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); PR_EEP("Big Endian", !!(pBase->eepMisc & 0x01)); PR_EEP("Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF); PR_EEP("Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF); PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); PR_EEP("TX Gain type", pBase->txGainType); len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", pBase->macAddr); out: if (len > size) len = size; return len; } #else static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { return 0; } #endif #undef SIZE_EEPROM_4K static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) { #define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; u16 *eepdata, temp, magic, magic2; u32 sum = 0, el; bool need_swap = false; int i, addr; if (!ath9k_hw_use_flash(ah)) { if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { ath_err(common, "Reading Magic # failed\n"); return false; } ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); if (magic2 == AR5416_EEPROM_MAGIC) { need_swap = true; eepdata = (u16 *) (&ah->eeprom); for (addr = 0; addr < EEPROM_4K_SIZE; addr++) { temp = swab16(*eepdata); *eepdata = temp; eepdata++; } } else { ath_err(common, "Invalid EEPROM Magic. Endianness mismatch.\n"); return -EINVAL; } } } ath_dbg(common, EEPROM, "need_swap = %s\n", need_swap ? "True" : "False"); if (need_swap) el = swab16(ah->eeprom.map4k.baseEepHeader.length); else el = ah->eeprom.map4k.baseEepHeader.length; if (el > sizeof(struct ar5416_eeprom_4k)) el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16); else el = el / sizeof(u16); eepdata = (u16 *)(&ah->eeprom); for (i = 0; i < el; i++) sum ^= *eepdata++; if (need_swap) { u32 integer; u16 word; ath_dbg(common, EEPROM, "EEPROM Endianness is not native.. Changing\n"); word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; word = swab16(eep->baseEepHeader.checksum); eep->baseEepHeader.checksum = word; word = swab16(eep->baseEepHeader.version); eep->baseEepHeader.version = word; word = swab16(eep->baseEepHeader.regDmn[0]); eep->baseEepHeader.regDmn[0] = word; word = swab16(eep->baseEepHeader.regDmn[1]); eep->baseEepHeader.regDmn[1] = word; word = swab16(eep->baseEepHeader.rfSilent); eep->baseEepHeader.rfSilent = word; word = swab16(eep->baseEepHeader.blueToothOptions); eep->baseEepHeader.blueToothOptions = word; word = swab16(eep->baseEepHeader.deviceCap); eep->baseEepHeader.deviceCap = word; integer = swab32(eep->modalHeader.antCtrlCommon); eep->modalHeader.antCtrlCommon = integer; for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { integer = swab32(eep->modalHeader.antCtrlChain[i]); eep->modalHeader.antCtrlChain[i] = integer; } for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { word = swab16(eep->modalHeader.spurChans[i].spurChan); eep->modalHeader.spurChans[i].spurChan = word; } } if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", sum, ah->eep_ops->get_eeprom_ver(ah)); return -EINVAL; } return 0; #undef EEPROM_4K_SIZE } static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; struct modal_eep_4k_header *pModal = &eep->modalHeader; struct base_eep_header_4k *pBase = &eep->baseEepHeader; u16 ver_minor; ver_minor = pBase->version & AR5416_EEP_VER_MINOR_MASK; switch (param) { case EEP_NFTHRESH_2: return pModal->noiseFloorThreshCh[0]; case EEP_MAC_LSW: return get_unaligned_be16(pBase->macAddr); case EEP_MAC_MID: return get_unaligned_be16(pBase->macAddr + 2); case EEP_MAC_MSW: return get_unaligned_be16(pBase->macAddr + 4); case EEP_REG_0: return pBase->regDmn[0]; case EEP_OP_CAP: return pBase->deviceCap; case EEP_OP_MODE: return pBase->opCapFlags; case EEP_RF_SILENT: return pBase->rfSilent; case EEP_OB_2: return pModal->ob_0; case EEP_DB_2: return pModal->db1_1; case EEP_MINOR_REV: return ver_minor; case EEP_TX_MASK: return pBase->txMask; case EEP_RX_MASK: return pBase->rxMask; case EEP_FRAC_N_5G: return 0; case EEP_PWR_TABLE_OFFSET: return AR5416_PWR_TABLE_OFFSET_DB; case EEP_MODAL_VER: return pModal->version; case EEP_ANT_DIV_CTL1: return pModal->antdiv_ctl1; case EEP_TXGAIN_TYPE: return pBase->txGainType; case EEP_ANTENNA_GAIN_2G: return pModal->antennaGainCh[0]; default: return 0; } } static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; struct cal_data_per_freq_4k *pRawDataset; u8 *pCalBChans = NULL; u16 pdGainOverlap_t2; static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK]; u16 numPiers, i, j; u16 numXpdGain, xpdMask; u16 xpdGainValues[AR5416_EEP4K_NUM_PD_GAINS] = { 0, 0 }; u32 reg32, regOffset, regChainOffset; xpdMask = pEepData->modalHeader.xpdGain; if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_2) { pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap; } else { pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); } pCalBChans = pEepData->calFreqPier2G; numPiers = AR5416_EEP4K_NUM_2G_CAL_PIERS; numXpdGain = 0; for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { if (numXpdGain >= AR5416_EEP4K_NUM_PD_GAINS) break; xpdGainValues[numXpdGain] = (u16)(AR5416_PD_GAINS_IN_MASK - i); numXpdGain++; } } REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (numXpdGain - 1) & 0x3); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, xpdGainValues[0]); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, xpdGainValues[1]); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0); for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { regChainOffset = i * 0x1000; if (pEepData->baseEepHeader.txMask & (1 << i)) { pRawDataset = pEepData->calPierData2G[i]; ath9k_hw_get_gain_boundaries_pdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, gainBoundaries, pdadcValues, numXpdGain); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { reg32 = get_unaligned_le32(&pdadcValues[4 * j]); REG_WRITE(ah, regOffset, reg32); ath_dbg(common, EEPROM, "PDADC (%d,%4x): %4.4x %8.8x\n", i, regChainOffset, regOffset, reg32); ath_dbg(common, EEPROM, "PDADC: Chain %d | " "PDADC %3d Value %3d | " "PDADC %3d Value %3d | " "PDADC %3d Value %3d | " "PDADC %3d Value %3d |\n", i, 4 * j, pdadcValues[4 * j], 4 * j + 1, pdadcValues[4 * j + 1], 4 * j + 2, pdadcValues[4 * j + 2], 4 * j + 3, pdadcValues[4 * j + 3]); regOffset += 4; } REGWRITE_BUFFER_FLUSH(ah); } } } static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah, struct ath9k_channel *chan, int16_t *ratesArray, u16 cfgCtl, u16 antenna_reduction, u16 powerLimit) { #define CMP_TEST_GRP \ (((cfgCtl & ~CTL_MODE_M)| (pCtlMode[ctlMode] & CTL_MODE_M)) == \ pEepData->ctlIndex[i]) \ || (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == \ ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL)) int i; u16 twiceMinEdgePower; u16 twiceMaxEdgePower; u16 scaledPower = 0, minCtlPower; u16 numCtlModes; const u16 *pCtlMode; u16 ctlMode, freq; struct chan_centers centers; struct cal_ctl_data_4k *rep; struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; struct cal_target_power_leg targetPowerOfdm, targetPowerCck = { 0, { 0, 0, 0, 0} }; struct cal_target_power_leg targetPowerOfdmExt = { 0, { 0, 0, 0, 0} }, targetPowerCckExt = { 0, { 0, 0, 0, 0 } }; struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = { 0, {0, 0, 0, 0} }; static const u16 ctlModesFor11g[] = { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 }; ath9k_hw_get_channel_centers(ah, chan, ¢ers); scaledPower = powerLimit - antenna_reduction; numCtlModes = ARRAY_SIZE(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; pCtlMode = ctlModesFor11g; ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, false); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, false); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower2GHT20, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, false); if (IS_CHAN_HT40(chan)) { numCtlModes = ARRAY_SIZE(ctlModesFor11g); ath9k_hw_get_target_powers(ah, chan, pEepData->calTargetPower2GHT40, AR5416_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, true); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, true); ath9k_hw_get_legacy_target_powers(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, true); } for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || (pCtlMode[ctlMode] == CTL_2GHT40); if (isHt40CtlMode) freq = centers.synth_center; else if (pCtlMode[ctlMode] & EXT_ADDITIVE) freq = centers.ext_center; else freq = centers.ctl_center; twiceMaxEdgePower = MAX_RATE_POWER; for (i = 0; (i < AR5416_EEP4K_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { if (CMP_TEST_GRP) { rep = &(pEepData->ctlData[i]); twiceMinEdgePower = ath9k_hw_get_max_edge_power( freq, rep->ctlEdges[ ar5416_get_ntxchains(ah->txchainmask) - 1], IS_CHAN_2GHZ(chan), AR5416_EEP4K_NUM_BAND_EDGES); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { twiceMaxEdgePower = min(twiceMaxEdgePower, twiceMinEdgePower); } else { twiceMaxEdgePower = twiceMinEdgePower; break; } } } minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower); switch (pCtlMode[ctlMode]) { case CTL_11B: for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); i++) { targetPowerCck.tPow2x[i] = min((u16)targetPowerCck.tPow2x[i], minCtlPower); } break; case CTL_11G: for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) { targetPowerOfdm.tPow2x[i] = min((u16)targetPowerOfdm.tPow2x[i], minCtlPower); } break; case CTL_2GHT20: for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) { targetPowerHt20.tPow2x[i] = min((u16)targetPowerHt20.tPow2x[i], minCtlPower); } break; case CTL_11B_EXT: targetPowerCckExt.tPow2x[0] = min((u16)targetPowerCckExt.tPow2x[0], minCtlPower); break; case CTL_11G_EXT: targetPowerOfdmExt.tPow2x[0] = min((u16)targetPowerOfdmExt.tPow2x[0], minCtlPower); break; case CTL_2GHT40: for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { targetPowerHt40.tPow2x[i] = min((u16)targetPowerHt40.tPow2x[i], minCtlPower); } break; default: break; } } ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm.tPow2x[0]; ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1]; ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2]; ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3]; ratesArray[rateXr] = targetPowerOfdm.tPow2x[0]; for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i]; ratesArray[rate1l] = targetPowerCck.tPow2x[0]; ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck.tPow2x[1]; ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck.tPow2x[2]; ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck.tPow2x[3]; if (IS_CHAN_HT40(chan)) { for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { ratesArray[rateHt40_0 + i] = targetPowerHt40.tPow2x[i]; } ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0]; ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0]; ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0]; ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; } #undef CMP_TEST_GRP } static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, u8 powerLimit, bool test) { struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; struct modal_eep_4k_header *pModal = &pEepData->modalHeader; int16_t ratesArray[Ar5416RateSize]; u8 ht40PowerIncForPdadc = 2; int i; memset(ratesArray, 0, sizeof(ratesArray)); if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_2) { ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; } ath9k_hw_set_4k_power_per_rate_table(ah, chan, &ratesArray[0], cfgCtl, twiceAntennaReduction, powerLimit); ath9k_hw_set_4k_power_cal_table(ah, chan); regulatory->max_power_level = 0; for (i = 0; i < ARRAY_SIZE(ratesArray); i++) { if (ratesArray[i] > MAX_RATE_POWER) ratesArray[i] = MAX_RATE_POWER; if (ratesArray[i] > regulatory->max_power_level) regulatory->max_power_level = ratesArray[i]; } if (test) return; for (i = 0; i < Ar5416RateSize; i++) ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; ENABLE_REGWRITE_BUFFER(ah); /* OFDM power per rate */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, ATH9K_POW_SM(ratesArray[rate18mb], 24) | ATH9K_POW_SM(ratesArray[rate12mb], 16) | ATH9K_POW_SM(ratesArray[rate9mb], 8) | ATH9K_POW_SM(ratesArray[rate6mb], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, ATH9K_POW_SM(ratesArray[rate54mb], 24) | ATH9K_POW_SM(ratesArray[rate48mb], 16) | ATH9K_POW_SM(ratesArray[rate36mb], 8) | ATH9K_POW_SM(ratesArray[rate24mb], 0)); /* CCK power per rate */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, ATH9K_POW_SM(ratesArray[rate2s], 24) | ATH9K_POW_SM(ratesArray[rate2l], 16) | ATH9K_POW_SM(ratesArray[rateXr], 8) | ATH9K_POW_SM(ratesArray[rate1l], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, ATH9K_POW_SM(ratesArray[rate11s], 24) | ATH9K_POW_SM(ratesArray[rate11l], 16) | ATH9K_POW_SM(ratesArray[rate5_5s], 8) | ATH9K_POW_SM(ratesArray[rate5_5l], 0)); /* HT20 power per rate */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, ATH9K_POW_SM(ratesArray[rateHt20_3], 24) | ATH9K_POW_SM(ratesArray[rateHt20_2], 16) | ATH9K_POW_SM(ratesArray[rateHt20_1], 8) | ATH9K_POW_SM(ratesArray[rateHt20_0], 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, ATH9K_POW_SM(ratesArray[rateHt20_7], 24) | ATH9K_POW_SM(ratesArray[rateHt20_6], 16) | ATH9K_POW_SM(ratesArray[rateHt20_5], 8) | ATH9K_POW_SM(ratesArray[rateHt20_4], 0)); /* HT40 power per rate */ if (IS_CHAN_HT40(chan)) { REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, ATH9K_POW_SM(ratesArray[rateHt40_3] + ht40PowerIncForPdadc, 24) | ATH9K_POW_SM(ratesArray[rateHt40_2] + ht40PowerIncForPdadc, 16) | ATH9K_POW_SM(ratesArray[rateHt40_1] + ht40PowerIncForPdadc, 8) | ATH9K_POW_SM(ratesArray[rateHt40_0] + ht40PowerIncForPdadc, 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, ATH9K_POW_SM(ratesArray[rateHt40_7] + ht40PowerIncForPdadc, 24) | ATH9K_POW_SM(ratesArray[rateHt40_6] + ht40PowerIncForPdadc, 16) | ATH9K_POW_SM(ratesArray[rateHt40_5] + ht40PowerIncForPdadc, 8) | ATH9K_POW_SM(ratesArray[rateHt40_4] + ht40PowerIncForPdadc, 0)); REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) | ATH9K_POW_SM(ratesArray[rateExtCck], 16) | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) | ATH9K_POW_SM(ratesArray[rateDupCck], 0)); } REGWRITE_BUFFER_FLUSH(ah); } static void ath9k_hw_4k_set_gain(struct ath_hw *ah, struct modal_eep_4k_header *pModal, struct ar5416_eeprom_4k *eep, u8 txRxAttenLocal) { REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0, pModal->antCtrlChain[0]); REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), (REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_3) { txRxAttenLocal = pModal->txRxAttenCh[0]; REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, pModal->xatten2Margin[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); /* Set the block 1 value to block 0 value */ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, pModal->xatten2Margin[0]); REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); } REG_RMW_FIELD(ah, AR_PHY_RXGAIN, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); REG_RMW_FIELD(ah, AR_PHY_RXGAIN, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); } /* * Read EEPROM header info and program the device for correct operation * given the channel value. */ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { struct modal_eep_4k_header *pModal; struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; struct base_eep_header_4k *pBase = &eep->baseEepHeader; u8 txRxAttenLocal; u8 ob[5], db1[5], db2[5]; u8 ant_div_control1, ant_div_control2; u8 bb_desired_scale; u32 regVal; pModal = &eep->modalHeader; txRxAttenLocal = 23; REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon); /* Single chain for 4K EEPROM*/ ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal); /* Initialize Ant Diversity settings from EEPROM */ if (pModal->version >= 3) { ant_div_control1 = pModal->antdiv_ctl1; ant_div_control2 = pModal->antdiv_ctl2; regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL)); regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL); regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF); regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB); regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB); REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); regVal = REG_READ(ah, AR_PHY_CCK_DETECT); regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); regVal |= SM((ant_div_control1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal); regVal = REG_READ(ah, AR_PHY_CCK_DETECT); } if (pModal->version >= 2) { ob[0] = pModal->ob_0; ob[1] = pModal->ob_1; ob[2] = pModal->ob_2; ob[3] = pModal->ob_3; ob[4] = pModal->ob_4; db1[0] = pModal->db1_0; db1[1] = pModal->db1_1; db1[2] = pModal->db1_2; db1[3] = pModal->db1_3; db1[4] = pModal->db1_4; db2[0] = pModal->db2_0; db2[1] = pModal->db2_1; db2[2] = pModal->db2_2; db2[3] = pModal->db2_3; db2[4] = pModal->db2_4; } else if (pModal->version == 1) { ob[0] = pModal->ob_0; ob[1] = ob[2] = ob[3] = ob[4] = pModal->ob_1; db1[0] = pModal->db1_0; db1[1] = db1[2] = db1[3] = db1[4] = pModal->db1_1; db2[0] = pModal->db2_0; db2[1] = db2[2] = db2[3] = db2[4] = pModal->db2_1; } else { int i; for (i = 0; i < 5; i++) { ob[i] = pModal->ob_0; db1[i] = pModal->db1_0; db2[i] = pModal->db1_0; } } if (AR_SREV_9271(ah)) { ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_OB_cck, AR9271_AN_RF2G3_OB_cck_S, ob[0]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_OB_psk, AR9271_AN_RF2G3_OB_psk_S, ob[1]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_OB_qam, AR9271_AN_RF2G3_OB_qam_S, ob[2]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_DB_1, AR9271_AN_RF2G3_DB_1_S, db1[0]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9271_AN_RF2G4_DB_2, AR9271_AN_RF2G4_DB_2_S, db2[0]); } else { ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_OB_0, AR9285_AN_RF2G3_OB_0_S, ob[0]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_OB_1, AR9285_AN_RF2G3_OB_1_S, ob[1]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_OB_2, AR9285_AN_RF2G3_OB_2_S, ob[2]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_OB_3, AR9285_AN_RF2G3_OB_3_S, ob[3]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_OB_4, AR9285_AN_RF2G3_OB_4_S, ob[4]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_DB1_0, AR9285_AN_RF2G3_DB1_0_S, db1[0]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_DB1_1, AR9285_AN_RF2G3_DB1_1_S, db1[1]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_DB1_2, AR9285_AN_RF2G3_DB1_2_S, db1[2]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9285_AN_RF2G4_DB1_3, AR9285_AN_RF2G4_DB1_3_S, db1[3]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9285_AN_RF2G4_DB1_4, AR9285_AN_RF2G4_DB1_4_S, db1[4]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9285_AN_RF2G4_DB2_0, AR9285_AN_RF2G4_DB2_0_S, db2[0]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9285_AN_RF2G4_DB2_1, AR9285_AN_RF2G4_DB2_1_S, db2[1]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9285_AN_RF2G4_DB2_2, AR9285_AN_RF2G4_DB2_2_S, db2[2]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9285_AN_RF2G4_DB2_3, AR9285_AN_RF2G4_DB2_3_S, db2[3]); ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, AR9285_AN_RF2G4_DB2_4, AR9285_AN_RF2G4_DB2_4_S, db2[4]); } REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); REG_WRITE(ah, AR_PHY_RF_CTL4, SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); if (AR_SREV_9271_10(ah)) REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, pModal->thresh62); if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_2) { REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_DATA_START, pModal->txFrameToDataStart); REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON, pModal->txFrameToPaOn); } if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_3) { if (IS_CHAN_HT40(chan)) REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); } bb_desired_scale = (pModal->bb_scale_smrt_antenna & EEP_4K_BB_DESIRED_SCALE_MASK); if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) { u32 pwrctrl, mask, clr; mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25); pwrctrl = mask * bb_desired_scale; clr = mask * 0x1f; REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr); REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr); REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr); mask = BIT(0)|BIT(5)|BIT(15); pwrctrl = mask * bb_desired_scale; clr = mask * 0x1f; REG_RMW(ah, AR_PHY_TX_PWRCTRL9, pwrctrl, clr); mask = BIT(0)|BIT(5); pwrctrl = mask * bb_desired_scale; clr = mask * 0x1f; REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr); REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr); } } static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { #define EEP_MAP4K_SPURCHAN \ (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan) struct ath_common *common = ath9k_hw_common(ah); u16 spur_val = AR_NO_SPUR; ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { case SPUR_DISABLE: break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_MAP4K_SPURCHAN; break; } return spur_val; #undef EEP_MAP4K_SPURCHAN } const struct eeprom_ops eep_4k_ops = { .check_eeprom = ath9k_hw_4k_check_eeprom, .get_eeprom = ath9k_hw_4k_get_eeprom, .fill_eeprom = ath9k_hw_4k_fill_eeprom, .dump_eeprom = ath9k_hw_4k_dump_eeprom, .get_eeprom_ver = ath9k_hw_4k_get_eeprom_ver, .get_eeprom_rev = ath9k_hw_4k_get_eeprom_rev, .set_board_values = ath9k_hw_4k_set_board_values, .set_txpower = ath9k_hw_4k_set_txpower, .get_spur_channel = ath9k_hw_4k_get_spur_channel }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h0000644000175000017500000000367012026211315025753 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef DFS_PRI_DETECTOR_H #define DFS_PRI_DETECTOR_H #include /** * struct pri_detector - PRI detector element for a dedicated radar type * @exit(): destructor * @add_pulse(): add pulse event, returns true if pattern was detected * @reset(): clear states and reset to given time stamp * @rs: detector specs for this detector element * @last_ts: last pulse time stamp considered for this element in usecs * @sequences: list_head holding potential pulse sequences * @pulses: list connecting pulse_elem objects * @count: number of pulses in queue * @max_count: maximum number of pulses to be queued * @window_size: window size back from newest pulse time stamp in usecs */ struct pri_detector { void (*exit) (struct pri_detector *de); bool (*add_pulse)(struct pri_detector *de, struct pulse_event *e); void (*reset) (struct pri_detector *de, u64 ts); /* private: internal use only */ const struct radar_detector_specs *rs; u64 last_ts; struct list_head sequences; struct list_head pulses; u32 count; u32 max_count; u32 window_size; }; struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs); #endif /* DFS_PRI_DETECTOR_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c0000644000175000017500000002615712026211315025753 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "ath9k.h" #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" #include "dfs_debug.h" /** * struct pri_sequence - sequence of pulses matching one PRI * @head: list_head * @pri: pulse repetition interval (PRI) in usecs * @dur: duration of sequence in usecs * @count: number of pulses in this sequence * @count_falses: number of not matching pulses in this sequence * @first_ts: time stamp of first pulse in usecs * @last_ts: time stamp of last pulse in usecs * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur) */ struct pri_sequence { struct list_head head; u32 pri; u32 dur; u32 count; u32 count_falses; u64 first_ts; u64 last_ts; u64 deadline_ts; }; /** * struct pulse_elem - elements in pulse queue * @ts: time stamp in usecs */ struct pulse_elem { struct list_head head; u64 ts; }; /** * pde_get_multiple() - get number of multiples considering a given tolerance * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise */ static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance) { u32 remainder; u32 factor; u32 delta; if (fraction == 0) return 0; delta = (val < fraction) ? (fraction - val) : (val - fraction); if (delta <= tolerance) /* val and fraction are within tolerance */ return 1; factor = val / fraction; remainder = val % fraction; if (remainder > tolerance) { /* no exact match */ if ((fraction - remainder) <= tolerance) /* remainder is within tolerance */ factor++; else factor = 0; } return factor; } /** * DOC: Singleton Pulse and Sequence Pools * * Instances of pri_sequence and pulse_elem are kept in singleton pools to * reduce the number of dynamic allocations. They are shared between all * instances and grow up to the peak number of simultaneously used objects. * * Memory is freed after all references to the pools are released. */ static u32 singleton_pool_references; static LIST_HEAD(pulse_pool); static LIST_HEAD(pseq_pool); static DEFINE_SPINLOCK(pool_lock); static void pool_register_ref(void) { spin_lock_bh(&pool_lock); singleton_pool_references++; DFS_POOL_STAT_INC(pool_reference); spin_unlock_bh(&pool_lock); } static void pool_deregister_ref(void) { spin_lock_bh(&pool_lock); singleton_pool_references--; DFS_POOL_STAT_DEC(pool_reference); if (singleton_pool_references == 0) { /* free singleton pools with no references left */ struct pri_sequence *ps, *ps0; struct pulse_elem *p, *p0; list_for_each_entry_safe(p, p0, &pulse_pool, head) { list_del(&p->head); DFS_POOL_STAT_DEC(pulse_allocated); kfree(p); } list_for_each_entry_safe(ps, ps0, &pseq_pool, head) { list_del(&ps->head); DFS_POOL_STAT_DEC(pseq_allocated); kfree(ps); } } spin_unlock_bh(&pool_lock); } static void pool_put_pulse_elem(struct pulse_elem *pe) { spin_lock_bh(&pool_lock); list_add(&pe->head, &pulse_pool); DFS_POOL_STAT_DEC(pulse_used); spin_unlock_bh(&pool_lock); } static void pool_put_pseq_elem(struct pri_sequence *pse) { spin_lock_bh(&pool_lock); list_add(&pse->head, &pseq_pool); DFS_POOL_STAT_DEC(pseq_used); spin_unlock_bh(&pool_lock); } static struct pri_sequence *pool_get_pseq_elem(void) { struct pri_sequence *pse = NULL; spin_lock_bh(&pool_lock); if (!list_empty(&pseq_pool)) { pse = list_first_entry(&pseq_pool, struct pri_sequence, head); list_del(&pse->head); DFS_POOL_STAT_INC(pseq_used); } spin_unlock_bh(&pool_lock); return pse; } static struct pulse_elem *pool_get_pulse_elem(void) { struct pulse_elem *pe = NULL; spin_lock_bh(&pool_lock); if (!list_empty(&pulse_pool)) { pe = list_first_entry(&pulse_pool, struct pulse_elem, head); list_del(&pe->head); DFS_POOL_STAT_INC(pulse_used); } spin_unlock_bh(&pool_lock); return pe; } static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde) { struct list_head *l = &pde->pulses; if (list_empty(l)) return NULL; return list_entry(l->prev, struct pulse_elem, head); } static bool pulse_queue_dequeue(struct pri_detector *pde) { struct pulse_elem *p = pulse_queue_get_tail(pde); if (p != NULL) { list_del_init(&p->head); pde->count--; /* give it back to pool */ pool_put_pulse_elem(p); } return (pde->count > 0); } /* remove pulses older than window */ static void pulse_queue_check_window(struct pri_detector *pde) { u64 min_valid_ts; struct pulse_elem *p; /* there is no delta time with less than 2 pulses */ if (pde->count < 2) return; if (pde->last_ts <= pde->window_size) return; min_valid_ts = pde->last_ts - pde->window_size; while ((p = pulse_queue_get_tail(pde)) != NULL) { if (p->ts >= min_valid_ts) return; pulse_queue_dequeue(pde); } } static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts) { struct pulse_elem *p = pool_get_pulse_elem(); if (p == NULL) { p = kmalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) { DFS_POOL_STAT_INC(pulse_alloc_error); return false; } DFS_POOL_STAT_INC(pulse_allocated); DFS_POOL_STAT_INC(pulse_used); } INIT_LIST_HEAD(&p->head); p->ts = ts; list_add(&p->head, &pde->pulses); pde->count++; pde->last_ts = ts; pulse_queue_check_window(pde); if (pde->count >= pde->max_count) pulse_queue_dequeue(pde); return true; } static bool pseq_handler_create_sequences(struct pri_detector *pde, u64 ts, u32 min_count) { struct pulse_elem *p; list_for_each_entry(p, &pde->pulses, head) { struct pri_sequence ps, *new_ps; struct pulse_elem *p2; u32 tmp_false_count; u64 min_valid_ts; u32 delta_ts = ts - p->ts; if (delta_ts < pde->rs->pri_min) /* ignore too small pri */ continue; if (delta_ts > pde->rs->pri_max) /* stop on too large pri (sorted list) */ break; /* build a new sequence with new potential pri */ ps.count = 2; ps.count_falses = 0; ps.first_ts = p->ts; ps.last_ts = ts; ps.pri = ts - p->ts; ps.dur = ps.pri * (pde->rs->ppb - 1) + 2 * pde->rs->max_pri_tolerance; p2 = p; tmp_false_count = 0; min_valid_ts = ts - ps.dur; /* check which past pulses are candidates for new sequence */ list_for_each_entry_continue(p2, &pde->pulses, head) { u32 factor; if (p2->ts < min_valid_ts) /* stop on crossing window border */ break; /* check if pulse match (multi)PRI */ factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri, pde->rs->max_pri_tolerance); if (factor > 0) { ps.count++; ps.first_ts = p2->ts; /* * on match, add the intermediate falses * and reset counter */ ps.count_falses += tmp_false_count; tmp_false_count = 0; } else { /* this is a potential false one */ tmp_false_count++; } } if (ps.count < min_count) /* did not reach minimum count, drop sequence */ continue; /* this is a valid one, add it */ ps.deadline_ts = ps.first_ts + ps.dur; new_ps = pool_get_pseq_elem(); if (new_ps == NULL) { new_ps = kmalloc(sizeof(*new_ps), GFP_KERNEL); if (new_ps == NULL) { DFS_POOL_STAT_INC(pseq_alloc_error); return false; } DFS_POOL_STAT_INC(pseq_allocated); DFS_POOL_STAT_INC(pseq_used); } memcpy(new_ps, &ps, sizeof(ps)); INIT_LIST_HEAD(&new_ps->head); list_add(&new_ps->head, &pde->sequences); } return true; } /* check new ts and add to all matching existing sequences */ static u32 pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts) { u32 max_count = 0; struct pri_sequence *ps, *ps2; list_for_each_entry_safe(ps, ps2, &pde->sequences, head) { u32 delta_ts; u32 factor; /* first ensure that sequence is within window */ if (ts > ps->deadline_ts) { list_del_init(&ps->head); pool_put_pseq_elem(ps); continue; } delta_ts = ts - ps->last_ts; factor = pde_get_multiple(delta_ts, ps->pri, pde->rs->max_pri_tolerance); if (factor > 0) { ps->last_ts = ts; ps->count++; if (max_count < ps->count) max_count = ps->count; } else { ps->count_falses++; } } return max_count; } static struct pri_sequence * pseq_handler_check_detection(struct pri_detector *pde) { struct pri_sequence *ps; if (list_empty(&pde->sequences)) return NULL; list_for_each_entry(ps, &pde->sequences, head) { /* * we assume to have enough matching confidence if we * 1) have enough pulses * 2) have more matching than false pulses */ if ((ps->count >= pde->rs->ppb_thresh) && (ps->count * pde->rs->num_pri >= ps->count_falses)) return ps; } return NULL; } /* free pulse queue and sequences list and give objects back to pools */ static void pri_detector_reset(struct pri_detector *pde, u64 ts) { struct pri_sequence *ps, *ps0; struct pulse_elem *p, *p0; list_for_each_entry_safe(ps, ps0, &pde->sequences, head) { list_del_init(&ps->head); pool_put_pseq_elem(ps); } list_for_each_entry_safe(p, p0, &pde->pulses, head) { list_del_init(&p->head); pool_put_pulse_elem(p); } pde->count = 0; pde->last_ts = ts; } static void pri_detector_exit(struct pri_detector *de) { pri_detector_reset(de, 0); pool_deregister_ref(); kfree(de); } static bool pri_detector_add_pulse(struct pri_detector *de, struct pulse_event *event) { u32 max_updated_seq; struct pri_sequence *ps; u64 ts = event->ts; const struct radar_detector_specs *rs = de->rs; /* ignore pulses not within width range */ if ((rs->width_min > event->width) || (rs->width_max < event->width)) return false; if ((ts - de->last_ts) < rs->max_pri_tolerance) /* if delta to last pulse is too short, don't use this pulse */ return false; de->last_ts = ts; max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts); if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) { pr_err("failed to create pulse sequences\n"); pri_detector_reset(de, ts); return false; } ps = pseq_handler_check_detection(de); if (ps != NULL) { pr_info("DFS: radar found: pri=%d, count=%d, count_false=%d\n", ps->pri, ps->count, ps->count_falses); pri_detector_reset(de, ts); return true; } pulse_queue_enqueue(de, ts); return false; } struct pri_detector * pri_detector_init(const struct radar_detector_specs *rs) { struct pri_detector *de; de = kzalloc(sizeof(*de), GFP_KERNEL); if (de == NULL) return NULL; de->exit = pri_detector_exit; de->add_pulse = pri_detector_add_pulse; de->reset = pri_detector_reset; INIT_LIST_HEAD(&de->sequences); INIT_LIST_HEAD(&de->pulses); de->window_size = rs->pri_max * rs->ppb * rs->num_pri; de->max_count = rs->ppb * 2; de->rs = rs; pool_register_ref(); return de; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h0000644000175000017500000000656612026211315026645 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef DFS_PATTERN_DETECTOR_H #define DFS_PATTERN_DETECTOR_H #include #include #include /** * struct pulse_event - describing pulses reported by PHY * @ts: pulse time stamp in us * @freq: channel frequency in MHz * @width: pulse duration in us * @rssi: rssi of radar event */ struct pulse_event { u64 ts; u16 freq; u8 width; u8 rssi; }; /** * struct radar_detector_specs - detector specs for a radar pattern type * @type_id: pattern type, as defined by regulatory * @width_min: minimum radar pulse width in [us] * @width_max: maximum radar pulse width in [us] * @pri_min: minimum pulse repetition interval in [us] (including tolerance) * @pri_max: minimum pri in [us] (including tolerance) * @num_pri: maximum number of different pri for this type * @ppb: pulses per bursts for this type * @ppb_thresh: number of pulses required to trigger detection * @max_pri_tolerance: pulse time stamp tolerance on both sides [us] */ struct radar_detector_specs { u8 type_id; u8 width_min; u8 width_max; u16 pri_min; u16 pri_max; u8 num_pri; u8 ppb; u8 ppb_thresh; u8 max_pri_tolerance; }; /** * struct dfs_pattern_detector - DFS pattern detector * @exit(): destructor * @set_domain(): set DFS domain, resets detector lines upon domain changes * @add_pulse(): add radar pulse to detector, returns true on detection * @region: active DFS region, NL80211_DFS_UNSET until set * @num_radar_types: number of different radar types * @last_pulse_ts: time stamp of last valid pulse in usecs * @radar_detector_specs: array of radar detection specs * @channel_detectors: list connecting channel_detector elements */ struct dfs_pattern_detector { void (*exit)(struct dfs_pattern_detector *dpd); bool (*set_domain)(struct dfs_pattern_detector *dpd, enum nl80211_dfs_regions region); bool (*add_pulse)(struct dfs_pattern_detector *dpd, struct pulse_event *pe); enum nl80211_dfs_regions region; u8 num_radar_types; u64 last_pulse_ts; const struct radar_detector_specs *radar_spec; struct list_head channel_detectors; }; /** * dfs_pattern_detector_init() - constructor for pattern detector class * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation * @return instance pointer on success, NULL otherwise */ #if defined(CONFIG_ATH9K_DFS_CERTIFIED) extern struct dfs_pattern_detector * dfs_pattern_detector_init(enum nl80211_dfs_regions region); #else static inline struct dfs_pattern_detector * dfs_pattern_detector_init(enum nl80211_dfs_regions region) { return NULL; } #endif /* CONFIG_ATH9K_DFS_CERTIFIED */ #endif /* DFS_PATTERN_DETECTOR_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c0000644000175000017500000002034312026211315026625 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" /* * tolerated deviation of radar time stamp in usecs on both sides * TODO: this might need to be HW-dependent */ #define PRI_TOLERANCE 16 /** * struct radar_types - contains array of patterns defined for one DFS domain * @domain: DFS regulatory domain * @num_radar_types: number of radar types to follow * @radar_types: radar types array */ struct radar_types { enum nl80211_dfs_regions region; u32 num_radar_types; const struct radar_detector_specs *radar_types; }; /* percentage on ppb threshold to trigger detection */ #define MIN_PPB_THRESH 50 #define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100) #define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF) #define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ { \ ID, WMIN, WMAX, (PRF2PRI(PMAX) - PRI_TOLERANCE), \ (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF, \ PPB_THRESH(PPB), PRI_TOLERANCE, \ } /* radar types as defined by ETSI EN-301-893 v1.5.1 */ static const struct radar_detector_specs etsi_radar_ref_types_v15[] = { ETSI_PATTERN(0, 0, 1, 700, 700, 1, 18), ETSI_PATTERN(1, 0, 5, 200, 1000, 1, 10), ETSI_PATTERN(2, 0, 15, 200, 1600, 1, 15), ETSI_PATTERN(3, 0, 15, 2300, 4000, 1, 25), ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20), ETSI_PATTERN(5, 0, 2, 300, 400, 3, 10), ETSI_PATTERN(6, 0, 2, 400, 1200, 3, 15), }; static const struct radar_types etsi_radar_types_v15 = { .region = NL80211_DFS_ETSI, .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v15), .radar_types = etsi_radar_ref_types_v15, }; /* for now, we support ETSI radar types, FCC and JP are TODO */ static const struct radar_types *dfs_domains[] = { &etsi_radar_types_v15, }; /** * get_dfs_domain_radar_types() - get radar types for a given DFS domain * @param domain DFS domain * @return radar_types ptr on success, NULL if DFS domain is not supported */ static const struct radar_types * get_dfs_domain_radar_types(enum nl80211_dfs_regions region) { u32 i; for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) { if (dfs_domains[i]->region == region) return dfs_domains[i]; } return NULL; } /** * struct channel_detector - detector elements for a DFS channel * @head: list_head * @freq: frequency for this channel detector in MHz * @detectors: array of dynamically created detector elements for this freq * * Channel detectors are required to provide multi-channel DFS detection, e.g. * to support off-channel scanning. A pattern detector has a list of channels * radar pulses have been reported for in the past. */ struct channel_detector { struct list_head head; u16 freq; struct pri_detector **detectors; }; /* channel_detector_reset() - reset detector lines for a given channel */ static void channel_detector_reset(struct dfs_pattern_detector *dpd, struct channel_detector *cd) { u32 i; if (cd == NULL) return; for (i = 0; i < dpd->num_radar_types; i++) cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts); } /* channel_detector_exit() - destructor */ static void channel_detector_exit(struct dfs_pattern_detector *dpd, struct channel_detector *cd) { u32 i; if (cd == NULL) return; list_del(&cd->head); for (i = 0; i < dpd->num_radar_types; i++) { struct pri_detector *de = cd->detectors[i]; if (de != NULL) de->exit(de); } kfree(cd->detectors); kfree(cd); } static struct channel_detector * channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) { u32 sz, i; struct channel_detector *cd; cd = kmalloc(sizeof(*cd), GFP_KERNEL); if (cd == NULL) goto fail; INIT_LIST_HEAD(&cd->head); cd->freq = freq; sz = sizeof(cd->detectors) * dpd->num_radar_types; cd->detectors = kzalloc(sz, GFP_KERNEL); if (cd->detectors == NULL) goto fail; for (i = 0; i < dpd->num_radar_types; i++) { const struct radar_detector_specs *rs = &dpd->radar_spec[i]; struct pri_detector *de = pri_detector_init(rs); if (de == NULL) goto fail; cd->detectors[i] = de; } list_add(&cd->head, &dpd->channel_detectors); return cd; fail: pr_err("failed to allocate channel_detector for freq=%d\n", freq); channel_detector_exit(dpd, cd); return NULL; } /** * channel_detector_get() - get channel detector for given frequency * @param dpd instance pointer * @param freq frequency in MHz * @return pointer to channel detector on success, NULL otherwise * * Return existing channel detector for the given frequency or return a * newly create one. */ static struct channel_detector * channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq) { struct channel_detector *cd; list_for_each_entry(cd, &dpd->channel_detectors, head) { if (cd->freq == freq) return cd; } return channel_detector_create(dpd, freq); } /* * DFS Pattern Detector */ /* dpd_reset(): reset all channel detectors */ static void dpd_reset(struct dfs_pattern_detector *dpd) { struct channel_detector *cd; if (!list_empty(&dpd->channel_detectors)) list_for_each_entry(cd, &dpd->channel_detectors, head) channel_detector_reset(dpd, cd); } static void dpd_exit(struct dfs_pattern_detector *dpd) { struct channel_detector *cd, *cd0; if (!list_empty(&dpd->channel_detectors)) list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) channel_detector_exit(dpd, cd); kfree(dpd); } static bool dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) { u32 i; bool ts_wraparound; struct channel_detector *cd; if (dpd->region == NL80211_DFS_UNSET) { /* * pulses received for a non-supported or un-initialized * domain are treated as detected radars */ return true; } cd = channel_detector_get(dpd, event->freq); if (cd == NULL) return false; ts_wraparound = (event->ts < dpd->last_pulse_ts); dpd->last_pulse_ts = event->ts; if (ts_wraparound) { /* * reset detector on time stamp wraparound * with monotonic time stamps, this should never happen */ pr_warn("DFS: time stamp wraparound detected, resetting\n"); dpd_reset(dpd); } /* do type individual pattern matching */ for (i = 0; i < dpd->num_radar_types; i++) { if (cd->detectors[i]->add_pulse(cd->detectors[i], event) != 0) { channel_detector_reset(dpd, cd); return true; } } return false; } static bool dpd_set_domain(struct dfs_pattern_detector *dpd, enum nl80211_dfs_regions region) { const struct radar_types *rt; struct channel_detector *cd, *cd0; if (dpd->region == region) return true; dpd->region = NL80211_DFS_UNSET; rt = get_dfs_domain_radar_types(region); if (rt == NULL) return false; /* delete all channel detectors for previous DFS domain */ if (!list_empty(&dpd->channel_detectors)) list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) channel_detector_exit(dpd, cd); dpd->radar_spec = rt->radar_types; dpd->num_radar_types = rt->num_radar_types; dpd->region = region; return true; } static struct dfs_pattern_detector default_dpd = { .exit = dpd_exit, .set_domain = dpd_set_domain, .add_pulse = dpd_add_pulse, .region = NL80211_DFS_UNSET, }; struct dfs_pattern_detector * dfs_pattern_detector_init(enum nl80211_dfs_regions region) { struct dfs_pattern_detector *dpd; dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); if (dpd == NULL) { pr_err("allocation of dfs_pattern_detector failed\n"); return NULL; } *dpd = default_dpd; INIT_LIST_HEAD(&dpd->channel_detectors); if (dpd->set_domain(dpd, region)) return dpd; pr_err("Could not set DFS domain to %d. ", region); return NULL; } EXPORT_SYMBOL(dfs_pattern_detector_init); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs.h0000644000175000017500000000325612026211315023210 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * Copyright (c) 2011 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ATH9K_DFS_H #define ATH9K_DFS_H #include "dfs_pattern_detector.h" #if defined(CONFIG_ATH9K_DFS_CERTIFIED) /** * ath9k_dfs_process_phyerr - process radar PHY error * @sc: ath_softc * @data: RX payload data * @rs: RX status after processing descriptor * @mactime: receive time * * This function is called whenever the HW DFS module detects a radar * pulse and reports it as a PHY error. * * The radar information provided as raw payload data is validated and * filtered for false pulses. Events passing all tests are forwarded to * the DFS detector for pattern detection. */ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, struct ath_rx_status *rs, u64 mactime); #else static inline void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, struct ath_rx_status *rs, u64 mactime) { } #endif #endif /* ATH9K_DFS_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs_debug.h0000644000175000017500000000524612026211315024357 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * Copyright (c) 2011 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ATH9K_DFS_DEBUG_H #define ATH9K_DFS_DEBUG_H #include "hw.h" /** * struct ath_dfs_stats - DFS Statistics per wiphy * @pulses_total: pulses reported by HW * @pulses_no_dfs: pulses wrongly reported as DFS * @pulses_detected: pulses detected so far * @datalen_discards: pulses discarded due to invalid datalen * @rssi_discards: pulses discarded due to invalid RSSI * @bwinfo_discards: pulses discarded due to invalid BW info * @pri_phy_errors: pulses reported for primary channel * @ext_phy_errors: pulses reported for extension channel * @dc_phy_errors: pulses reported for primary + extension channel * @pulses_processed: pulses forwarded to detector * @radar_detected: radars detected */ struct ath_dfs_stats { /* pulse stats */ u32 pulses_total; u32 pulses_no_dfs; u32 pulses_detected; u32 datalen_discards; u32 rssi_discards; u32 bwinfo_discards; u32 pri_phy_errors; u32 ext_phy_errors; u32 dc_phy_errors; /* pattern detection stats */ u32 pulses_processed; u32 radar_detected; }; /** * struct ath_dfs_pool_stats - DFS Statistics for global pools */ struct ath_dfs_pool_stats { u32 pool_reference; u32 pulse_allocated; u32 pulse_alloc_error; u32 pulse_used; u32 pseq_allocated; u32 pseq_alloc_error; u32 pseq_used; }; #if defined(CONFIG_ATH9K_DFS_DEBUGFS) #define DFS_STAT_INC(sc, c) (sc->debug.stats.dfs_stats.c++) void ath9k_dfs_init_debug(struct ath_softc *sc); #define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) #define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) extern struct ath_dfs_pool_stats global_dfs_pool_stats; #else #define DFS_STAT_INC(sc, c) do { } while (0) static inline void ath9k_dfs_init_debug(struct ath_softc *sc) { } #define DFS_POOL_STAT_INC(c) do { } while (0) #define DFS_POOL_STAT_DEC(c) do { } while (0) #endif /* CONFIG_ATH9K_DFS_DEBUGFS */ #endif /* ATH9K_DFS_DEBUG_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs_debug.c0000644000175000017500000001033012026211315024340 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * Copyright (c) 2011 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "ath9k.h" #include "dfs_debug.h" struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 }; #define ATH9K_DFS_STAT(s, p) \ len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ sc->debug.stats.dfs_stats.p); #define ATH9K_DFS_POOL_STAT(s, p) \ len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ global_dfs_pool_stats.p); static ssize_t read_file_dfs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath9k_hw_version *hw_ver = &sc->sc_ah->hw_version; char *buf; unsigned int len = 0, size = 8000; ssize_t retval = 0; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += snprintf(buf + len, size - len, "DFS support for " "macVersion = 0x%x, macRev = 0x%x: %s\n", hw_ver->macVersion, hw_ver->macRev, (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ? "enabled" : "disabled"); len += snprintf(buf + len, size - len, "Pulse detector statistics:\n"); ATH9K_DFS_STAT("pulse events reported ", pulses_total); ATH9K_DFS_STAT("invalid pulse events ", pulses_no_dfs); ATH9K_DFS_STAT("DFS pulses detected ", pulses_detected); ATH9K_DFS_STAT("Datalen discards ", datalen_discards); ATH9K_DFS_STAT("RSSI discards ", rssi_discards); ATH9K_DFS_STAT("BW info discards ", bwinfo_discards); ATH9K_DFS_STAT("Primary channel pulses ", pri_phy_errors); ATH9K_DFS_STAT("Secondary channel pulses", ext_phy_errors); ATH9K_DFS_STAT("Dual channel pulses ", dc_phy_errors); len += snprintf(buf + len, size - len, "Radar detector statistics " "(current DFS region: %d)\n", sc->dfs_detector->region); ATH9K_DFS_STAT("Pulse events processed ", pulses_processed); ATH9K_DFS_STAT("Radars detected ", radar_detected); len += snprintf(buf + len, size - len, "Global Pool statistics:\n"); ATH9K_DFS_POOL_STAT("Pool references ", pool_reference); ATH9K_DFS_POOL_STAT("Pulses allocated ", pulse_allocated); ATH9K_DFS_POOL_STAT("Pulses alloc error ", pulse_alloc_error); ATH9K_DFS_POOL_STAT("Pulses in use ", pulse_used); ATH9K_DFS_POOL_STAT("Seqs. allocated ", pseq_allocated); ATH9K_DFS_POOL_STAT("Seqs. alloc error ", pseq_alloc_error); ATH9K_DFS_POOL_STAT("Seqs. in use ", pseq_used); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } /* magic number to prevent accidental reset of DFS statistics */ #define DFS_STATS_RESET_MAGIC 0x80000000 static ssize_t write_file_dfs(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; unsigned long val; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, &val)) return -EINVAL; if (val == DFS_STATS_RESET_MAGIC) memset(&sc->debug.stats.dfs_stats, 0, sizeof(sc->debug.stats.dfs_stats)); return count; } static const struct file_operations fops_dfs_stats = { .read = read_file_dfs, .write = write_file_dfs, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath9k_dfs_init_debug(struct ath_softc *sc) { debugfs_create_file("dfs_stats", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dfs_stats); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/dfs.c0000644000175000017500000001256112026211315023202 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * Copyright (c) 2011 Neratec Solutions AG * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "hw-ops.h" #include "ath9k.h" #include "dfs.h" #include "dfs_debug.h" /* internal struct to pass radar data */ struct ath_radar_data { u8 pulse_bw_info; u8 rssi; u8 ext_rssi; u8 pulse_length_ext; u8 pulse_length_pri; }; /* convert pulse duration to usecs, considering clock mode */ static u32 dur_to_usecs(struct ath_hw *ah, u32 dur) { const u32 AR93X_NSECS_PER_DUR = 800; const u32 AR93X_NSECS_PER_DUR_FAST = (8000 / 11); u32 nsecs; if (IS_CHAN_A_FAST_CLOCK(ah, ah->curchan)) nsecs = dur * AR93X_NSECS_PER_DUR_FAST; else nsecs = dur * AR93X_NSECS_PER_DUR; return (nsecs + 500) / 1000; } #define PRI_CH_RADAR_FOUND 0x01 #define EXT_CH_RADAR_FOUND 0x02 static bool ath9k_postprocess_radar_event(struct ath_softc *sc, struct ath_radar_data *ard, struct pulse_event *pe) { u8 rssi; u16 dur; ath_dbg(ath9k_hw_common(sc->sc_ah), DFS, "pulse_bw_info=0x%x, pri,ext len/rssi=(%u/%u, %u/%u)\n", ard->pulse_bw_info, ard->pulse_length_pri, ard->rssi, ard->pulse_length_ext, ard->ext_rssi); /* * Only the last 2 bits of the BW info are relevant, they indicate * which channel the radar was detected in. */ ard->pulse_bw_info &= 0x03; switch (ard->pulse_bw_info) { case PRI_CH_RADAR_FOUND: /* radar in ctrl channel */ dur = ard->pulse_length_pri; DFS_STAT_INC(sc, pri_phy_errors); /* * cannot use ctrl channel RSSI * if extension channel is stronger */ rssi = (ard->ext_rssi >= (ard->rssi + 3)) ? 0 : ard->rssi; break; case EXT_CH_RADAR_FOUND: /* radar in extension channel */ dur = ard->pulse_length_ext; DFS_STAT_INC(sc, ext_phy_errors); /* * cannot use extension channel RSSI * if control channel is stronger */ rssi = (ard->rssi >= (ard->ext_rssi + 12)) ? 0 : ard->ext_rssi; break; case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): /* * Conducted testing, when pulse is on DC, both pri and ext * durations are reported to be same * * Radiated testing, when pulse is on DC, different pri and * ext durations are reported, so take the larger of the two */ if (ard->pulse_length_ext >= ard->pulse_length_pri) dur = ard->pulse_length_ext; else dur = ard->pulse_length_pri; DFS_STAT_INC(sc, dc_phy_errors); /* when both are present use stronger one */ rssi = (ard->rssi < ard->ext_rssi) ? ard->ext_rssi : ard->rssi; break; default: /* * Bogus bandwidth info was received in descriptor, * so ignore this PHY error */ DFS_STAT_INC(sc, bwinfo_discards); return false; } if (rssi == 0) { DFS_STAT_INC(sc, rssi_discards); return false; } /* * TODO: check chirping pulses * checks for chirping are dependent on the DFS regulatory domain * used, which is yet TBD */ /* convert duration to usecs */ pe->width = dur_to_usecs(sc->sc_ah, dur); pe->rssi = rssi; DFS_STAT_INC(sc, pulses_detected); return true; } #undef PRI_CH_RADAR_FOUND #undef EXT_CH_RADAR_FOUND /* * DFS: check PHY-error for radar pulse and feed the detector */ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, struct ath_rx_status *rs, u64 mactime) { struct ath_radar_data ard; u16 datalen; char *vdata_end; struct pulse_event pe; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); DFS_STAT_INC(sc, pulses_total); if ((rs->rs_phyerr != ATH9K_PHYERR_RADAR) && (rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT)) { ath_dbg(common, DFS, "Error: rs_phyer=0x%x not a radar error\n", rs->rs_phyerr); DFS_STAT_INC(sc, pulses_no_dfs); return; } datalen = rs->rs_datalen; if (datalen == 0) { DFS_STAT_INC(sc, datalen_discards); return; } ard.rssi = rs->rs_rssi_ctl0; ard.ext_rssi = rs->rs_rssi_ext0; /* * hardware stores this as 8 bit signed value. * we will cap it at 0 if it is a negative number */ if (ard.rssi & 0x80) ard.rssi = 0; if (ard.ext_rssi & 0x80) ard.ext_rssi = 0; vdata_end = (char *)data + datalen; ard.pulse_bw_info = vdata_end[-1]; ard.pulse_length_ext = vdata_end[-2]; ard.pulse_length_pri = vdata_end[-3]; pe.freq = ah->curchan->channel; pe.ts = mactime; if (ath9k_postprocess_radar_event(sc, &ard, &pe)) { struct dfs_pattern_detector *pd = sc->dfs_detector; static u64 last_ts; ath_dbg(common, DFS, "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, " "width=%d, rssi=%d, delta_ts=%llu\n", pe.freq, pe.ts, pe.width, pe.rssi, pe.ts-last_ts); last_ts = pe.ts; DFS_STAT_INC(sc, pulses_processed); if (pd != NULL && pd->add_pulse(pd, &pe)) { DFS_STAT_INC(sc, radar_detected); /* * TODO: forward radar event to DFS management layer */ } } } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/debug.h0000644000175000017500000002110612026211315023514 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef DEBUG_H #define DEBUG_H #include "hw.h" #include "rc.h" #include "dfs_debug.h" struct ath_txq; struct ath_buf; #ifdef CONFIG_ATH9K_DEBUGFS #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++ #define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++ #else #define TX_STAT_INC(q, c) do { } while (0) #define RESET_STAT_INC(sc, type) do { } while (0) #endif enum ath_reset_type { RESET_TYPE_BB_HANG, RESET_TYPE_BB_WATCHDOG, RESET_TYPE_FATAL_INT, RESET_TYPE_TX_ERROR, RESET_TYPE_TX_HANG, RESET_TYPE_PLL_HANG, RESET_TYPE_MAC_HANG, RESET_TYPE_BEACON_STUCK, __RESET_TYPE_MAX }; #ifdef CONFIG_ATH9K_DEBUGFS /** * struct ath_interrupt_stats - Contains statistics about interrupts * @total: Total no. of interrupts generated so far * @rxok: RX with no errors * @rxlp: RX with low priority RX * @rxhp: RX with high priority, uapsd only * @rxeol: RX with no more RXDESC available * @rxorn: RX FIFO overrun * @txok: TX completed at the requested rate * @txurn: TX FIFO underrun * @mib: MIB regs reaching its threshold * @rxphyerr: RX with phy errors * @rx_keycache_miss: RX with key cache misses * @swba: Software Beacon Alert * @bmiss: Beacon Miss * @bnr: Beacon Not Ready * @cst: Carrier Sense TImeout * @gtt: Global TX Timeout * @tim: RX beacon TIM occurrence * @cabend: RX End of CAB traffic * @dtimsync: DTIM sync lossage * @dtim: RX Beacon with DTIM * @bb_watchdog: Baseband watchdog * @tsfoor: TSF out of range, indicates that the corrected TSF received * from a beacon differs from the PCU's internal TSF by more than a * (programmable) threshold * @local_timeout: Internal bus timeout. * @mci: MCI interrupt, specific to MCI based BTCOEX chipsets * @gen_timer: Generic hardware timer interrupt */ struct ath_interrupt_stats { u32 total; u32 rxok; u32 rxlp; u32 rxhp; u32 rxeol; u32 rxorn; u32 txok; u32 txeol; u32 txurn; u32 mib; u32 rxphyerr; u32 rx_keycache_miss; u32 swba; u32 bmiss; u32 bnr; u32 cst; u32 gtt; u32 tim; u32 cabend; u32 dtimsync; u32 dtim; u32 bb_watchdog; u32 tsfoor; u32 mci; u32 gen_timer; /* Sync-cause stats */ u32 sync_cause_all; u32 sync_rtc_irq; u32 sync_mac_irq; u32 eeprom_illegal_access; u32 apb_timeout; u32 pci_mode_conflict; u32 host1_fatal; u32 host1_perr; u32 trcv_fifo_perr; u32 radm_cpl_ep; u32 radm_cpl_dllp_abort; u32 radm_cpl_tlp_abort; u32 radm_cpl_ecrc_err; u32 radm_cpl_timeout; u32 local_timeout; u32 pm_access; u32 mac_awake; u32 mac_asleep; u32 mac_sleep_access; }; /** * struct ath_tx_stats - Statistics about TX * @tx_pkts_all: No. of total frames transmitted, including ones that may have had errors. * @tx_bytes_all: No. of total bytes transmitted, including ones that may have had errors. * @queued: Total MPDUs (non-aggr) queued * @completed: Total MPDUs (non-aggr) completed * @a_aggr: Total no. of aggregates queued * @a_queued_hw: Total AMPDUs queued to hardware * @a_queued_sw: Total AMPDUs queued to software queues * @a_completed: Total AMPDUs completed * @a_retries: No. of AMPDUs retried (SW) * @a_xretries: No. of AMPDUs dropped due to xretries * @fifo_underrun: FIFO underrun occurrences Valid only for: - non-aggregate condition. - first packet of aggregate. * @xtxop: No. of frames filtered because of TXOP limit * @timer_exp: Transmit timer expiry * @desc_cfg_err: Descriptor configuration errors * @data_urn: TX data underrun errors * @delim_urn: TX delimiter underrun errors * @puttxbuf: Number of times hardware was given txbuf to write. * @txstart: Number of times hardware was told to start tx. * @txprocdesc: Number of times tx descriptor was processed * @txfailed: Out-of-memory or other errors in xmit path. */ struct ath_tx_stats { u32 tx_pkts_all; u32 tx_bytes_all; u32 queued; u32 completed; u32 xretries; u32 a_aggr; u32 a_queued_hw; u32 a_queued_sw; u32 a_completed; u32 a_retries; u32 a_xretries; u32 fifo_underrun; u32 xtxop; u32 timer_exp; u32 desc_cfg_err; u32 data_underrun; u32 delim_underrun; u32 puttxbuf; u32 txstart; u32 txprocdesc; u32 txfailed; }; #define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) /** * struct ath_rx_stats - RX Statistics * @rx_pkts_all: No. of total frames received, including ones that may have had errors. * @rx_bytes_all: No. of total bytes received, including ones that may have had errors. * @crc_err: No. of frames with incorrect CRC value * @decrypt_crc_err: No. of frames whose CRC check failed after decryption process completed * @phy_err: No. of frames whose reception failed because the PHY encountered an error * @mic_err: No. of frames with incorrect TKIP MIC verification failure * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections * @post_delim_crc_err: Post-Frame delimiter CRC error detections * @decrypt_busy_err: Decryption interruptions counter * @phy_err_stats: Individual PHY error statistics * @rx_len_err: No. of frames discarded due to bad length. * @rx_oom_err: No. of frames dropped due to OOM issues. * @rx_rate_err: No. of frames dropped due to rate errors. * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. * @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH. * @rx_beacons: No. of beacons received. * @rx_frags: No. of rx-fragements received. */ struct ath_rx_stats { u32 rx_pkts_all; u32 rx_bytes_all; u32 crc_err; u32 decrypt_crc_err; u32 phy_err; u32 mic_err; u32 pre_delim_crc_err; u32 post_delim_crc_err; u32 decrypt_busy_err; u32 phy_err_stats[ATH9K_PHYERR_MAX]; u32 rx_len_err; u32 rx_oom_err; u32 rx_rate_err; u32 rx_too_many_frags_err; u32 rx_drop_rxflush; u32 rx_beacons; u32 rx_frags; }; struct ath_stats { struct ath_interrupt_stats istats; struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; struct ath_rx_stats rxstats; struct ath_dfs_stats dfs_stats; u32 reset[__RESET_TYPE_MAX]; }; #define ATH_DBG_MAX_SAMPLES 10 struct ath_dbg_bb_mac_samp { u32 dma_dbg_reg_vals[ATH9K_NUM_DMA_DEBUG_REGS]; u32 pcu_obs, pcu_cr, noise; struct { u64 jiffies; int8_t rssi_ctl0; int8_t rssi_ctl1; int8_t rssi_ctl2; int8_t rssi_ext0; int8_t rssi_ext1; int8_t rssi_ext2; int8_t rssi; bool isok; u8 rts_fail_cnt; u8 data_fail_cnt; u8 rateindex; u8 qid; u8 tid; u32 ba_low; u32 ba_high; } ts[ATH_DBG_MAX_SAMPLES]; struct { u64 jiffies; int8_t rssi_ctl0; int8_t rssi_ctl1; int8_t rssi_ctl2; int8_t rssi_ext0; int8_t rssi_ext1; int8_t rssi_ext2; int8_t rssi; bool is_mybeacon; u8 antenna; u8 rate; } rs[ATH_DBG_MAX_SAMPLES]; struct ath_cycle_counters cc; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; }; struct ath9k_debug { struct dentry *debugfs_phy; u32 regidx; struct ath_stats stats; #ifdef CONFIG_ATH9K_MAC_DEBUG spinlock_t samp_lock; struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES]; u8 sampidx; u8 tsidx; u8 rsidx; #endif }; int ath9k_init_debug(struct ath_hw *ah); void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, unsigned int flags); void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs); #else #define RX_STAT_INC(c) /* NOP */ static inline int ath9k_init_debug(struct ath_hw *ah) { return 0; } static inline void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) { } static inline void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, unsigned int flags) { } static inline void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { } #endif /* CONFIG_ATH9K_DEBUGFS */ #ifdef CONFIG_ATH9K_MAC_DEBUG void ath9k_debug_samp_bb_mac(struct ath_softc *sc); #else static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc) { } #endif #endif /* DEBUG_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/debug.c0000644000175000017500000013677412026211315023531 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "ath9k.h" #define REG_WRITE_D(_ah, _reg, _val) \ ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg)) #define REG_READ_D(_ah, _reg) \ ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { u8 *buf = file->private_data; return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); } static int ath9k_debugfs_release_buf(struct inode *inode, struct file *file) { vfree(file->private_data); return 0; } #ifdef CONFIG_ATH_DEBUG static ssize_t read_file_debug(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); char buf[32]; unsigned int len; len = sprintf(buf, "0x%08x\n", common->debug_mask); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_debug(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long mask; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, &mask)) return -EINVAL; common->debug_mask = mask; return count; } static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; #endif #define DMA_BUF_LEN 1024 static ssize_t read_file_tx_chainmask(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; char buf[32]; unsigned int len; len = sprintf(buf, "0x%08x\n", ah->txchainmask); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_tx_chainmask(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; unsigned long mask; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, &mask)) return -EINVAL; ah->txchainmask = mask; ah->caps.tx_chainmask = mask; return count; } static const struct file_operations fops_tx_chainmask = { .read = read_file_tx_chainmask, .write = write_file_tx_chainmask, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_rx_chainmask(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; char buf[32]; unsigned int len; len = sprintf(buf, "0x%08x\n", ah->rxchainmask); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_rx_chainmask(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; unsigned long mask; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, &mask)) return -EINVAL; ah->rxchainmask = mask; ah->caps.rx_chainmask = mask; return count; } static const struct file_operations fops_rx_chainmask = { .read = read_file_rx_chainmask, .write = write_file_rx_chainmask, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_disable_ani(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); char buf[32]; unsigned int len; len = sprintf(buf, "%d\n", common->disable_ani); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_disable_ani(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long disable_ani; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, &disable_ani)) return -EINVAL; common->disable_ani = !!disable_ani; if (disable_ani) { clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_stop_ani(sc); } else { ath_check_ani(sc); } return count; } static const struct file_operations fops_disable_ani = { .read = read_file_disable_ani, .write = write_file_disable_ani, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_dma(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; char *buf; int retval; unsigned int len = 0; u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; int i, qcuOffset = 0, dcuOffset = 0; u32 *qcuBase = &val[0], *dcuBase = &val[4]; buf = kmalloc(DMA_BUF_LEN, GFP_KERNEL); if (!buf) return -ENOMEM; ath9k_ps_wakeup(sc); REG_WRITE_D(ah, AR_MACMISC, ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); len += snprintf(buf + len, DMA_BUF_LEN - len, "Raw DMA Debug values:\n"); for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { if (i % 4 == 0) len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32))); len += snprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ", i, val[i]); } len += snprintf(buf + len, DMA_BUF_LEN - len, "\n\n"); len += snprintf(buf + len, DMA_BUF_LEN - len, "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) { if (i == 8) { qcuOffset = 0; qcuBase++; } if (i == 6) { dcuOffset = 0; dcuBase++; } len += snprintf(buf + len, DMA_BUF_LEN - len, "%2d %2x %1x %2x %2x\n", i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), val[2] & (0x7 << (i * 3)) >> (i * 3), (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); } len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); len += snprintf(buf + len, DMA_BUF_LEN - len, "qcu_stitch state: %2x qcu_fetch state: %2x\n", (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); len += snprintf(buf + len, DMA_BUF_LEN - len, "qcu_complete state: %2x dcu_complete state: %2x\n", (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); len += snprintf(buf + len, DMA_BUF_LEN - len, "dcu_arb state: %2x dcu_fp state: %2x\n", (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); len += snprintf(buf + len, DMA_BUF_LEN - len, "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); len += snprintf(buf + len, DMA_BUF_LEN - len, "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); len += snprintf(buf + len, DMA_BUF_LEN - len, "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); len += snprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n", REG_READ_D(ah, AR_OBS_BUS_1)); len += snprintf(buf + len, DMA_BUF_LEN - len, "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR)); ath9k_ps_restore(sc); if (len > DMA_BUF_LEN) len = DMA_BUF_LEN; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static const struct file_operations fops_dma = { .read = read_file_dma, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) { if (status) sc->debug.stats.istats.total++; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { if (status & ATH9K_INT_RXLP) sc->debug.stats.istats.rxlp++; if (status & ATH9K_INT_RXHP) sc->debug.stats.istats.rxhp++; if (status & ATH9K_INT_BB_WATCHDOG) sc->debug.stats.istats.bb_watchdog++; } else { if (status & ATH9K_INT_RX) sc->debug.stats.istats.rxok++; } if (status & ATH9K_INT_RXEOL) sc->debug.stats.istats.rxeol++; if (status & ATH9K_INT_RXORN) sc->debug.stats.istats.rxorn++; if (status & ATH9K_INT_TX) sc->debug.stats.istats.txok++; if (status & ATH9K_INT_TXURN) sc->debug.stats.istats.txurn++; if (status & ATH9K_INT_RXPHY) sc->debug.stats.istats.rxphyerr++; if (status & ATH9K_INT_RXKCM) sc->debug.stats.istats.rx_keycache_miss++; if (status & ATH9K_INT_SWBA) sc->debug.stats.istats.swba++; if (status & ATH9K_INT_BMISS) sc->debug.stats.istats.bmiss++; if (status & ATH9K_INT_BNR) sc->debug.stats.istats.bnr++; if (status & ATH9K_INT_CST) sc->debug.stats.istats.cst++; if (status & ATH9K_INT_GTT) sc->debug.stats.istats.gtt++; if (status & ATH9K_INT_TIM) sc->debug.stats.istats.tim++; if (status & ATH9K_INT_CABEND) sc->debug.stats.istats.cabend++; if (status & ATH9K_INT_DTIMSYNC) sc->debug.stats.istats.dtimsync++; if (status & ATH9K_INT_DTIM) sc->debug.stats.istats.dtim++; if (status & ATH9K_INT_TSFOOR) sc->debug.stats.istats.tsfoor++; if (status & ATH9K_INT_MCI) sc->debug.stats.istats.mci++; if (status & ATH9K_INT_GENTIMER) sc->debug.stats.istats.gen_timer++; } static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; unsigned int len = 0; int rv; int mxlen = 4000; char *buf = kmalloc(mxlen, GFP_KERNEL); if (!buf) return -ENOMEM; #define PR_IS(a, s) \ do { \ len += snprintf(buf + len, mxlen - len, \ "%21s: %10u\n", a, \ sc->debug.stats.istats.s); \ } while (0) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { PR_IS("RXLP", rxlp); PR_IS("RXHP", rxhp); PR_IS("WATHDOG", bb_watchdog); } else { PR_IS("RX", rxok); } PR_IS("RXEOL", rxeol); PR_IS("RXORN", rxorn); PR_IS("TX", txok); PR_IS("TXURN", txurn); PR_IS("MIB", mib); PR_IS("RXPHY", rxphyerr); PR_IS("RXKCM", rx_keycache_miss); PR_IS("SWBA", swba); PR_IS("BMISS", bmiss); PR_IS("BNR", bnr); PR_IS("CST", cst); PR_IS("GTT", gtt); PR_IS("TIM", tim); PR_IS("CABEND", cabend); PR_IS("DTIMSYNC", dtimsync); PR_IS("DTIM", dtim); PR_IS("TSFOOR", tsfoor); PR_IS("MCI", mci); PR_IS("GENTIMER", gen_timer); PR_IS("TOTAL", total); len += snprintf(buf + len, mxlen - len, "SYNC_CAUSE stats:\n"); PR_IS("Sync-All", sync_cause_all); PR_IS("RTC-IRQ", sync_rtc_irq); PR_IS("MAC-IRQ", sync_mac_irq); PR_IS("EEPROM-Illegal-Access", eeprom_illegal_access); PR_IS("APB-Timeout", apb_timeout); PR_IS("PCI-Mode-Conflict", pci_mode_conflict); PR_IS("HOST1-Fatal", host1_fatal); PR_IS("HOST1-Perr", host1_perr); PR_IS("TRCV-FIFO-Perr", trcv_fifo_perr); PR_IS("RADM-CPL-EP", radm_cpl_ep); PR_IS("RADM-CPL-DLLP-Abort", radm_cpl_dllp_abort); PR_IS("RADM-CPL-TLP-Abort", radm_cpl_tlp_abort); PR_IS("RADM-CPL-ECRC-Err", radm_cpl_ecrc_err); PR_IS("RADM-CPL-Timeout", radm_cpl_timeout); PR_IS("Local-Bus-Timeout", local_timeout); PR_IS("PM-Access", pm_access); PR_IS("MAC-Awake", mac_awake); PR_IS("MAC-Asleep", mac_asleep); PR_IS("MAC-Sleep-Access", mac_sleep_access); if (len > mxlen) len = mxlen; rv = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return rv; } static const struct file_operations fops_interrupt = { .read = read_file_interrupt, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; #define PR_QNUM(_n) sc->tx.txq_map[_n]->axq_qnum #define PR(str, elem) \ do { \ len += snprintf(buf + len, size - len, \ "%s%13u%11u%10u%10u\n", str, \ sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].elem, \ sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].elem, \ sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].elem, \ sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].elem); \ if (len >= size) \ goto done; \ } while(0) #define PRX(str, elem) \ do { \ len += snprintf(buf + len, size - len, \ "%s%13u%11u%10u%10u\n", str, \ (unsigned int)(sc->tx.txq_map[WME_AC_BE]->elem), \ (unsigned int)(sc->tx.txq_map[WME_AC_BK]->elem), \ (unsigned int)(sc->tx.txq_map[WME_AC_VI]->elem), \ (unsigned int)(sc->tx.txq_map[WME_AC_VO]->elem)); \ if (len >= size) \ goto done; \ } while(0) #define PRQLE(str, elem) \ do { \ len += snprintf(buf + len, size - len, \ "%s%13i%11i%10i%10i\n", str, \ list_empty(&sc->tx.txq_map[WME_AC_BE]->elem), \ list_empty(&sc->tx.txq_map[WME_AC_BK]->elem), \ list_empty(&sc->tx.txq_map[WME_AC_VI]->elem), \ list_empty(&sc->tx.txq_map[WME_AC_VO]->elem)); \ if (len >= size) \ goto done; \ } while (0) static ssize_t read_file_xmit(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; char *buf; unsigned int len = 0, size = 8000; int i; ssize_t retval = 0; char tmp[32]; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x" " poll-work-seen: %u\n" "%30s %10s%10s%10s\n\n", ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup, sc->tx_complete_poll_work_seen, "BE", "BK", "VI", "VO"); PR("MPDUs Queued: ", queued); PR("MPDUs Completed: ", completed); PR("MPDUs XRetried: ", xretries); PR("Aggregates: ", a_aggr); PR("AMPDUs Queued HW:", a_queued_hw); PR("AMPDUs Queued SW:", a_queued_sw); PR("AMPDUs Completed:", a_completed); PR("AMPDUs Retried: ", a_retries); PR("AMPDUs XRetried: ", a_xretries); PR("FIFO Underrun: ", fifo_underrun); PR("TXOP Exceeded: ", xtxop); PR("TXTIMER Expiry: ", timer_exp); PR("DESC CFG Error: ", desc_cfg_err); PR("DATA Underrun: ", data_underrun); PR("DELIM Underrun: ", delim_underrun); PR("TX-Pkts-All: ", tx_pkts_all); PR("TX-Bytes-All: ", tx_bytes_all); PR("hw-put-tx-buf: ", puttxbuf); PR("hw-tx-start: ", txstart); PR("hw-tx-proc-desc: ", txprocdesc); PR("TX-Failed: ", txfailed); len += snprintf(buf + len, size - len, "%s%11p%11p%10p%10p\n", "txq-memory-address:", sc->tx.txq_map[WME_AC_BE], sc->tx.txq_map[WME_AC_BK], sc->tx.txq_map[WME_AC_VI], sc->tx.txq_map[WME_AC_VO]); if (len >= size) goto done; PRX("axq-qnum: ", axq_qnum); PRX("axq-depth: ", axq_depth); PRX("axq-ampdu_depth: ", axq_ampdu_depth); PRX("axq-stopped ", stopped); PRX("tx-in-progress ", axq_tx_inprogress); PRX("pending-frames ", pending_frames); PRX("txq_headidx: ", txq_headidx); PRX("txq_tailidx: ", txq_headidx); PRQLE("axq_q empty: ", axq_q); PRQLE("axq_acq empty: ", axq_acq); for (i = 0; i < ATH_TXFIFO_DEPTH; i++) { snprintf(tmp, sizeof(tmp) - 1, "txq_fifo[%i] empty: ", i); PRQLE(tmp, txq_fifo[i]); } /* Print out more detailed queue-info */ for (i = 0; i <= WME_AC_BK; i++) { struct ath_txq *txq = &(sc->tx.txq[i]); struct ath_atx_ac *ac; struct ath_atx_tid *tid; if (len >= size) goto done; spin_lock_bh(&txq->axq_lock); if (!list_empty(&txq->axq_acq)) { ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); len += snprintf(buf + len, size - len, "txq[%i] first-ac: %p sched: %i\n", i, ac, ac->sched); if (list_empty(&ac->tid_q) || (len >= size)) goto done_for; tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list); len += snprintf(buf + len, size - len, " first-tid: %p sched: %i paused: %i\n", tid, tid->sched, tid->paused); } done_for: spin_unlock_bh(&txq->axq_lock); } done: if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static ssize_t read_file_stations(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; char *buf; unsigned int len = 0, size = 64000; struct ath_node *an = NULL; ssize_t retval = 0; int q; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += snprintf(buf + len, size - len, "Stations:\n" " tid: addr sched paused buf_q-empty an ac baw\n" " ac: addr sched tid_q-empty txq\n"); spin_lock(&sc->nodes_lock); list_for_each_entry(an, &sc->nodes, list) { unsigned short ma = an->maxampdu; if (ma == 0) ma = 65535; /* see ath_lookup_rate */ len += snprintf(buf + len, size - len, "iface: %pM sta: %pM max-ampdu: %hu mpdu-density: %uus\n", an->vif->addr, an->sta->addr, ma, (unsigned int)(an->mpdudensity)); if (len >= size) goto done; for (q = 0; q < WME_NUM_TID; q++) { struct ath_atx_tid *tid = &(an->tid[q]); len += snprintf(buf + len, size - len, " tid: %p %s %s %i %p %p %hu\n", tid, tid->sched ? "sched" : "idle", tid->paused ? "paused" : "running", skb_queue_empty(&tid->buf_q), tid->an, tid->ac, tid->baw_size); if (len >= size) goto done; } for (q = 0; q < WME_NUM_AC; q++) { struct ath_atx_ac *ac = &(an->ac[q]); len += snprintf(buf + len, size - len, " ac: %p %s %i %p\n", ac, ac->sched ? "sched" : "idle", list_empty(&ac->tid_q), ac->txq); if (len >= size) goto done; } } done: spin_unlock(&sc->nodes_lock); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static ssize_t read_file_misc(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_hw *hw = sc->hw; struct ath9k_vif_iter_data iter_data; char buf[512]; unsigned int len = 0; ssize_t retval = 0; unsigned int reg; u32 rxfilter; len += snprintf(buf + len, sizeof(buf) - len, "BSSID: %pM\n", common->curbssid); len += snprintf(buf + len, sizeof(buf) - len, "BSSID-MASK: %pM\n", common->bssidmask); len += snprintf(buf + len, sizeof(buf) - len, "OPMODE: %s\n", ath_opmode_to_string(sc->sc_ah->opmode)); ath9k_ps_wakeup(sc); rxfilter = ath9k_hw_getrxfilter(sc->sc_ah); ath9k_ps_restore(sc); len += snprintf(buf + len, sizeof(buf) - len, "RXFILTER: 0x%x", rxfilter); if (rxfilter & ATH9K_RX_FILTER_UCAST) len += snprintf(buf + len, sizeof(buf) - len, " UCAST"); if (rxfilter & ATH9K_RX_FILTER_MCAST) len += snprintf(buf + len, sizeof(buf) - len, " MCAST"); if (rxfilter & ATH9K_RX_FILTER_BCAST) len += snprintf(buf + len, sizeof(buf) - len, " BCAST"); if (rxfilter & ATH9K_RX_FILTER_CONTROL) len += snprintf(buf + len, sizeof(buf) - len, " CONTROL"); if (rxfilter & ATH9K_RX_FILTER_BEACON) len += snprintf(buf + len, sizeof(buf) - len, " BEACON"); if (rxfilter & ATH9K_RX_FILTER_PROM) len += snprintf(buf + len, sizeof(buf) - len, " PROM"); if (rxfilter & ATH9K_RX_FILTER_PROBEREQ) len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); if (rxfilter & ATH9K_RX_FILTER_PHYERR) len += snprintf(buf + len, sizeof(buf) - len, " PHYERR"); if (rxfilter & ATH9K_RX_FILTER_MYBEACON) len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON"); if (rxfilter & ATH9K_RX_FILTER_COMP_BAR) len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); if (rxfilter & ATH9K_RX_FILTER_PSPOLL) len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL"); if (rxfilter & ATH9K_RX_FILTER_PHYRADAR) len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); if (rxfilter & ATH9K_RX_FILTER_MCAST_BCAST_ALL) len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL"); if (rxfilter & ATH9K_RX_FILTER_CONTROL_WRAPPER) len += snprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER"); len += snprintf(buf + len, sizeof(buf) - len, "\n"); reg = sc->sc_ah->imask; len += snprintf(buf + len, sizeof(buf) - len, "INTERRUPT-MASK: 0x%x", reg); if (reg & ATH9K_INT_SWBA) len += snprintf(buf + len, sizeof(buf) - len, " SWBA"); if (reg & ATH9K_INT_BMISS) len += snprintf(buf + len, sizeof(buf) - len, " BMISS"); if (reg & ATH9K_INT_CST) len += snprintf(buf + len, sizeof(buf) - len, " CST"); if (reg & ATH9K_INT_RX) len += snprintf(buf + len, sizeof(buf) - len, " RX"); if (reg & ATH9K_INT_RXHP) len += snprintf(buf + len, sizeof(buf) - len, " RXHP"); if (reg & ATH9K_INT_RXLP) len += snprintf(buf + len, sizeof(buf) - len, " RXLP"); if (reg & ATH9K_INT_BB_WATCHDOG) len += snprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG"); len += snprintf(buf + len, sizeof(buf) - len, "\n"); ath9k_calculate_iter_data(hw, NULL, &iter_data); len += snprintf(buf + len, sizeof(buf) - len, "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", iter_data.naps, iter_data.nstations, iter_data.nmeshes, iter_data.nwds, iter_data.nadhocs, sc->nvifs, sc->nbcnvifs); if (len > sizeof(buf)) len = sizeof(buf); retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); return retval; } static ssize_t read_file_reset(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; char buf[512]; unsigned int len = 0; len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "Baseband Hang", sc->debug.stats.reset[RESET_TYPE_BB_HANG]); len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "Baseband Watchdog", sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]); len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "Fatal HW Error", sc->debug.stats.reset[RESET_TYPE_FATAL_INT]); len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "TX HW error", sc->debug.stats.reset[RESET_TYPE_TX_ERROR]); len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "TX Path Hang", sc->debug.stats.reset[RESET_TYPE_TX_HANG]); len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "PLL RX Hang", sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, unsigned int flags) { #define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\ [sc->debug.tsidx].c) int qnum = txq->axq_qnum; TX_STAT_INC(qnum, tx_pkts_all); sc->debug.stats.txstats[qnum].tx_bytes_all += bf->bf_mpdu->len; if (bf_isampdu(bf)) { if (flags & ATH_TX_ERROR) TX_STAT_INC(qnum, a_xretries); else TX_STAT_INC(qnum, a_completed); } else { if (ts->ts_status & ATH9K_TXERR_XRETRY) TX_STAT_INC(qnum, xretries); else TX_STAT_INC(qnum, completed); } if (ts->ts_status & ATH9K_TXERR_FIFO) TX_STAT_INC(qnum, fifo_underrun); if (ts->ts_status & ATH9K_TXERR_XTXOP) TX_STAT_INC(qnum, xtxop); if (ts->ts_status & ATH9K_TXERR_TIMER_EXPIRED) TX_STAT_INC(qnum, timer_exp); if (ts->ts_flags & ATH9K_TX_DESC_CFG_ERR) TX_STAT_INC(qnum, desc_cfg_err); if (ts->ts_flags & ATH9K_TX_DATA_UNDERRUN) TX_STAT_INC(qnum, data_underrun); if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN) TX_STAT_INC(qnum, delim_underrun); #ifdef CONFIG_ATH9K_MAC_DEBUG spin_lock(&sc->debug.samp_lock); TX_SAMP_DBG(jiffies) = jiffies; TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0; TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1; TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2; TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0; TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1; TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2; TX_SAMP_DBG(rateindex) = ts->ts_rateindex; TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK); TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry; TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry; TX_SAMP_DBG(rssi) = ts->ts_rssi; TX_SAMP_DBG(tid) = ts->tid; TX_SAMP_DBG(qid) = ts->qid; if (ts->ts_flags & ATH9K_TX_BA) { TX_SAMP_DBG(ba_low) = ts->ba_low; TX_SAMP_DBG(ba_high) = ts->ba_high; } else { TX_SAMP_DBG(ba_low) = 0; TX_SAMP_DBG(ba_high) = 0; } sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES; spin_unlock(&sc->debug.samp_lock); #endif #undef TX_SAMP_DBG } static const struct file_operations fops_xmit = { .read = read_file_xmit, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_stations = { .read = read_file_stations, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_misc = { .read = read_file_misc, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_reset = { .read = read_file_reset, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_recv(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PHY_ERR(s, p) \ len += snprintf(buf + len, size - len, "%22s : %10u\n", s, \ sc->debug.stats.rxstats.phy_err_stats[p]); #define RXS_ERR(s, e) \ do { \ len += snprintf(buf + len, size - len, \ "%22s : %10u\n", s, \ sc->debug.stats.rxstats.e); \ } while (0) struct ath_softc *sc = file->private_data; char *buf; unsigned int len = 0, size = 1600; ssize_t retval = 0; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; RXS_ERR("CRC ERR", crc_err); RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); RXS_ERR("PHY ERR", phy_err); RXS_ERR("MIC ERR", mic_err); RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err); RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err); RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err); RXS_ERR("RX-LENGTH-ERR", rx_len_err); RXS_ERR("RX-OOM-ERR", rx_oom_err); RXS_ERR("RX-RATE-ERR", rx_rate_err); RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush); RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err); PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); RXS_ERR("RX-Pkts-All", rx_pkts_all); RXS_ERR("RX-Bytes-All", rx_bytes_all); RXS_ERR("RX-Beacons", rx_beacons); RXS_ERR("RX-Frags", rx_frags); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; #undef RXS_ERR #undef PHY_ERR } void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { #define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ #define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\ [sc->debug.rsidx].c) RX_STAT_INC(rx_pkts_all); sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; if (rs->rs_status & ATH9K_RXERR_CRC) RX_STAT_INC(crc_err); if (rs->rs_status & ATH9K_RXERR_DECRYPT) RX_STAT_INC(decrypt_crc_err); if (rs->rs_status & ATH9K_RXERR_MIC) RX_STAT_INC(mic_err); if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE) RX_STAT_INC(pre_delim_crc_err); if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST) RX_STAT_INC(post_delim_crc_err); if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY) RX_STAT_INC(decrypt_busy_err); if (rs->rs_status & ATH9K_RXERR_PHY) { RX_STAT_INC(phy_err); if (rs->rs_phyerr < ATH9K_PHYERR_MAX) RX_PHY_ERR_INC(rs->rs_phyerr); } #ifdef CONFIG_ATH9K_MAC_DEBUG spin_lock(&sc->debug.samp_lock); RX_SAMP_DBG(jiffies) = jiffies; RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0; RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1; RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2; RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0; RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1; RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2; RX_SAMP_DBG(antenna) = rs->rs_antenna; RX_SAMP_DBG(rssi) = rs->rs_rssi; RX_SAMP_DBG(rate) = rs->rs_rate; RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon; sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES; spin_unlock(&sc->debug.samp_lock); #endif #undef RX_PHY_ERR_INC #undef RX_SAMP_DBG } static const struct file_operations fops_recv = { .read = read_file_recv, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_regidx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; char buf[32]; unsigned int len; len = sprintf(buf, "0x%08x\n", sc->debug.regidx); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_regidx(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; unsigned long regidx; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, ®idx)) return -EINVAL; sc->debug.regidx = regidx; return count; } static const struct file_operations fops_regidx = { .read = read_file_regidx, .write = write_file_regidx, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_regval(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; char buf[32]; unsigned int len; u32 regval; ath9k_ps_wakeup(sc); regval = REG_READ_D(ah, sc->debug.regidx); ath9k_ps_restore(sc); len = sprintf(buf, "0x%08x\n", regval); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_regval(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; unsigned long regval; char buf[32]; ssize_t len; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (strict_strtoul(buf, 0, ®val)) return -EINVAL; ath9k_ps_wakeup(sc); REG_WRITE_D(ah, sc->debug.regidx, regval); ath9k_ps_restore(sc); return count; } static const struct file_operations fops_regval = { .read = read_file_regval, .write = write_file_regval, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; #define REGDUMP_LINE_SIZE 20 static int open_file_regdump(struct inode *inode, struct file *file) { struct ath_softc *sc = inode->i_private; unsigned int len = 0; u8 *buf; int i; unsigned long num_regs, regdump_len, max_reg_offset; max_reg_offset = AR_SREV_9300_20_OR_LATER(sc->sc_ah) ? 0x16bd4 : 0xb500; num_regs = max_reg_offset / 4 + 1; regdump_len = num_regs * REGDUMP_LINE_SIZE + 1; buf = vmalloc(regdump_len); if (!buf) return -ENOMEM; ath9k_ps_wakeup(sc); for (i = 0; i < num_regs; i++) len += scnprintf(buf + len, regdump_len - len, "0x%06x 0x%08x\n", i << 2, REG_READ(sc->sc_ah, i << 2)); ath9k_ps_restore(sc); file->private_data = buf; return 0; } static const struct file_operations fops_regdump = { .open = open_file_regdump, .read = ath9k_debugfs_read_buf, .release = ath9k_debugfs_release_buf, .owner = THIS_MODULE, .llseek = default_llseek,/* read accesses f_pos */ }; static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; u32 len = 0, size = 1500; u32 i, j; ssize_t retval = 0; char *buf; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; u8 nread; buf = kzalloc(size, GFP_KERNEL); if (!buf) return -ENOMEM; len += snprintf(buf + len, size - len, "Channel Noise Floor : %d\n", ah->noise); len += snprintf(buf + len, size - len, "Chain | privNF | # Readings | NF Readings\n"); for (i = 0; i < NUM_NF_READINGS; i++) { if (!(chainmask & (1 << i)) || ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) continue; nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - h[i].invalidNFcount; len += snprintf(buf + len, size - len, " %d\t %d\t %d\t\t", i, h[i].privNF, nread); for (j = 0; j < nread; j++) len += snprintf(buf + len, size - len, " %d", h[i].nfCalBuffer[j]); len += snprintf(buf + len, size - len, "\n"); } if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static const struct file_operations fops_dump_nfcal = { .read = read_file_dump_nfcal, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; u32 len = 0, size = 1500; ssize_t retval = 0; char *buf; buf = kzalloc(size, GFP_KERNEL); if (!buf) return -ENOMEM; len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size); retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static const struct file_operations fops_base_eeprom = { .read = read_file_base_eeprom, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; u32 len = 0, size = 6000; char *buf; size_t retval; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size); retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; } static const struct file_operations fops_modal_eeprom = { .read = read_file_modal_eeprom, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; #ifdef CONFIG_ATH9K_MAC_DEBUG void ath9k_debug_samp_bb_mac(struct ath_softc *sc) { #define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; int i; ath9k_ps_wakeup(sc); spin_lock_bh(&sc->debug.samp_lock); spin_lock_irqsave(&common->cc_lock, flags); ath_hw_cycle_counters_update(common); ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles; ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy; ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame; ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame; spin_unlock_irqrestore(&common->cc_lock, flags); ATH_SAMP_DBG(noise) = ah->noise; REG_WRITE_D(ah, AR_MACMISC, ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32))); ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1); ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR); memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist, sizeof(ATH_SAMP_DBG(nfCalHist))); sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES; spin_unlock_bh(&sc->debug.samp_lock); ath9k_ps_restore(sc); #undef ATH_SAMP_DBG } static int open_file_bb_mac_samps(struct inode *inode, struct file *file) { #define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c struct ath_softc *sc = inode->i_private; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; struct ath_dbg_bb_mac_samp *bb_mac_samp; struct ath9k_nfcal_hist *h; int i, j, qcuOffset = 0, dcuOffset = 0; u32 *qcuBase, *dcuBase, size = 30000, len = 0; u32 sampidx = 0; u8 *buf; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; u8 nread; if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return -EAGAIN; buf = vmalloc(size); if (!buf) return -ENOMEM; bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES); if (!bb_mac_samp) { vfree(buf); return -ENOMEM; } /* Account the current state too */ ath9k_debug_samp_bb_mac(sc); spin_lock_bh(&sc->debug.samp_lock); memcpy(bb_mac_samp, sc->debug.bb_mac_samp, sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES); len += snprintf(buf + len, size - len, "Current Sample Index: %d\n", sc->debug.sampidx); spin_unlock_bh(&sc->debug.samp_lock); len += snprintf(buf + len, size - len, "Raw DMA Debug Dump:\n"); len += snprintf(buf + len, size - len, "Sample |\t"); for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i); len += snprintf(buf + len, size - len, "\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { len += snprintf(buf + len, size - len, "%d\t", sampidx); for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) len += snprintf(buf + len, size - len, " %08x\t", ATH_SAMP_DBG(dma_dbg_reg_vals[i])); len += snprintf(buf + len, size - len, "\n"); } len += snprintf(buf + len, size - len, "\n"); len += snprintf(buf + len, size - len, "Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]); dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]); for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) { if (i == 8) { qcuOffset = 0; qcuBase++; } if (i == 6) { dcuOffset = 0; dcuBase++; } if (!sc->debug.stats.txstats[i].queued) continue; len += snprintf(buf + len, size - len, "%4d %7d %2x %1x %2x %2x\n", sampidx, i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), ATH_SAMP_DBG(dma_dbg_reg_vals[2]) & (0x7 << (i * 3)) >> (i * 3), (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); } len += snprintf(buf + len, size - len, "\n"); } len += snprintf(buf + len, size - len, "samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp " "ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 " "txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]); dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]); len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx, (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18, (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22); len += snprintf(buf + len, size - len, "%7x %8x ", (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26, (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3)); len += snprintf(buf + len, size - len, "%7x %7x ", (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25, (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27); len += snprintf(buf + len, size - len, "%7d %12d ", (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2, (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10); len += snprintf(buf + len, size - len, "%12d %12d ", (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11, (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12); len += snprintf(buf + len, size - len, "%12d %12d ", (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13, (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17); len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n", ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr)); } len += snprintf(buf + len, size - len, "Sample ChNoise Chain privNF #Reading Readings\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { h = ATH_SAMP_DBG(nfCalHist); if (!ATH_SAMP_DBG(noise)) continue; for (i = 0; i < NUM_NF_READINGS; i++) { if (!(chainmask & (1 << i)) || ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) continue; nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - h[i].invalidNFcount; len += snprintf(buf + len, size - len, "%4d %5d %4d\t %d\t %d\t", sampidx, ATH_SAMP_DBG(noise), i, h[i].privNF, nread); for (j = 0; j < nread; j++) len += snprintf(buf + len, size - len, " %d", h[i].nfCalBuffer[j]); len += snprintf(buf + len, size - len, "\n"); } } len += snprintf(buf + len, size - len, "\nCycle counters:\n" "Sample Total Rxbusy Rxframes Txframes\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { if (!ATH_SAMP_DBG(cc.cycles)) continue; len += snprintf(buf + len, size - len, "%4d %08x %08x %08x %08x\n", sampidx, ATH_SAMP_DBG(cc.cycles), ATH_SAMP_DBG(cc.rx_busy), ATH_SAMP_DBG(cc.rx_frame), ATH_SAMP_DBG(cc.tx_frame)); } len += snprintf(buf + len, size - len, "Tx status Dump :\n"); len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb " "isok rts_fail data_fail rate tid qid " "ba_low ba_high tx_before(ms)\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { if (!ATH_SAMP_DBG(ts[i].jiffies)) continue; len += snprintf(buf + len, size - len, "%-14d" "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d " "%-9d %-4d %-3d %-3d %08x %08x %-11d\n", sampidx, ATH_SAMP_DBG(ts[i].rssi_ctl0), ATH_SAMP_DBG(ts[i].rssi_ctl1), ATH_SAMP_DBG(ts[i].rssi_ctl2), ATH_SAMP_DBG(ts[i].rssi_ext0), ATH_SAMP_DBG(ts[i].rssi_ext1), ATH_SAMP_DBG(ts[i].rssi_ext2), ATH_SAMP_DBG(ts[i].rssi), ATH_SAMP_DBG(ts[i].isok), ATH_SAMP_DBG(ts[i].rts_fail_cnt), ATH_SAMP_DBG(ts[i].data_fail_cnt), ATH_SAMP_DBG(ts[i].rateindex), ATH_SAMP_DBG(ts[i].tid), ATH_SAMP_DBG(ts[i].qid), ATH_SAMP_DBG(ts[i].ba_low), ATH_SAMP_DBG(ts[i].ba_high), jiffies_to_msecs(jiffies - ATH_SAMP_DBG(ts[i].jiffies))); } } len += snprintf(buf + len, size - len, "Rx status Dump :\n"); len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 " "ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { if (!ATH_SAMP_DBG(rs[i].jiffies)) continue; len += snprintf(buf + len, size - len, "%-14d" "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n", sampidx, ATH_SAMP_DBG(rs[i].rssi_ctl0), ATH_SAMP_DBG(rs[i].rssi_ctl1), ATH_SAMP_DBG(rs[i].rssi_ctl2), ATH_SAMP_DBG(rs[i].rssi_ext0), ATH_SAMP_DBG(rs[i].rssi_ext1), ATH_SAMP_DBG(rs[i].rssi_ext2), ATH_SAMP_DBG(rs[i].rssi), ATH_SAMP_DBG(rs[i].is_mybeacon) ? "True" : "False", ATH_SAMP_DBG(rs[i].antenna), ATH_SAMP_DBG(rs[i].rate), jiffies_to_msecs(jiffies - ATH_SAMP_DBG(rs[i].jiffies))); } } vfree(bb_mac_samp); file->private_data = buf; return 0; #undef ATH_SAMP_DBG } static const struct file_operations fops_samps = { .open = open_file_bb_mac_samps, .read = ath9k_debugfs_read_buf, .release = ath9k_debugfs_release_buf, .owner = THIS_MODULE, .llseek = default_llseek, }; #endif int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath_softc *sc = (struct ath_softc *) common->priv; sc->debug.debugfs_phy = debugfs_create_dir("ath9k", sc->hw->wiphy->debugfsdir); if (!sc->debug.debugfs_phy) return -ENOMEM; #ifdef CONFIG_ATH_DEBUG debugfs_create_file("debug", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug); #endif ath9k_dfs_init_debug(sc); debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dma); debugfs_create_file("interrupt", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_interrupt); debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_xmit); debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[WME_AC_BK]); debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[WME_AC_BE]); debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[WME_AC_VI]); debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[WME_AC_VO]); debugfs_create_file("stations", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_stations); debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_misc); debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_reset); debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_recv); debugfs_create_file("rx_chainmask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_rx_chainmask); debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_tx_chainmask); debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_disable_ani); debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->config.enable_paprd); debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_regidx); debugfs_create_file("regval", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_regval); debugfs_create_bool("ignore_extcca", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &ah->config.cwm_ignore_extcca); debugfs_create_file("regdump", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_regdump); debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dump_nfcal); debugfs_create_file("base_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_base_eeprom); debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_modal_eeprom); #ifdef CONFIG_ATH9K_MAC_DEBUG debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_samps); #endif debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_val); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/common.h0000644000175000017500000000510112026211315023713 0ustar mcgrofmcgrof/* * Copyright (c) 2009-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "../ath.h" #include "hw.h" #include "hw-ops.h" /* Common header for Atheros 802.11n base driver cores */ #define WME_NUM_TID 16 #define WME_BA_BMP_SIZE 64 #define WME_MAX_BA WME_BA_BMP_SIZE #define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) /* These must match mac80211 skb queue mapping numbers */ #define WME_AC_VO 0 #define WME_AC_VI 1 #define WME_AC_BE 2 #define WME_AC_BK 3 #define WME_NUM_AC 4 #define ATH_RSSI_DUMMY_MARKER 0x127 #define ATH_RSSI_LPF_LEN 10 #define RSSI_LPF_THRESHOLD -20 #define ATH_RSSI_EP_MULTIPLIER (1<<7) #define ATH_EP_MUL(x, mul) ((x) * (mul)) #define ATH_RSSI_IN(x) (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER)) #define ATH_LPF_RSSI(x, y, len) \ ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) #define ATH_RSSI_LPF(x, y) do { \ if ((y) >= RSSI_LPF_THRESHOLD) \ x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ } while (0) #define ATH_EP_RND(x, mul) \ ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) int ath9k_cmn_padpos(__le16 frame_control); int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, struct ath_hw *ah); int ath9k_cmn_count_streams(unsigned int chainmask, int max); void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common, enum ath_stomp_type stomp_type); void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow, u16 new_txpow, u16 *txpower); void ath9k_cmn_init_crypto(struct ath_hw *ah); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/common.c0000644000175000017500000001216312026211315023714 0ustar mcgrofmcgrof/* * Copyright (c) 2009-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Module for common driver code between ath9k and ath9k_htc */ #include #include #include "common.h" MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Shared library for Atheros wireless 802.11n LAN cards."); MODULE_LICENSE("Dual BSD/GPL"); int ath9k_cmn_padpos(__le16 frame_control) { int padpos = 24; if (ieee80211_has_a4(frame_control)) { padpos += ETH_ALEN; } if (ieee80211_is_data_qos(frame_control)) { padpos += IEEE80211_QOS_CTL_LEN; } return padpos; } EXPORT_SYMBOL(ath9k_cmn_padpos); int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); if (tx_info->control.hw_key) { switch (tx_info->control.hw_key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: return ATH9K_KEY_TYPE_WEP; case WLAN_CIPHER_SUITE_TKIP: return ATH9K_KEY_TYPE_TKIP; case WLAN_CIPHER_SUITE_CCMP: return ATH9K_KEY_TYPE_AES; default: break; } } return ATH9K_KEY_TYPE_CLEAR; } EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype); static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { u32 chanmode = 0; switch (chan->band) { case IEEE80211_BAND_2GHZ: switch (channel_type) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: chanmode = CHANNEL_G_HT20; break; case NL80211_CHAN_HT40PLUS: chanmode = CHANNEL_G_HT40PLUS; break; case NL80211_CHAN_HT40MINUS: chanmode = CHANNEL_G_HT40MINUS; break; } break; case IEEE80211_BAND_5GHZ: switch (channel_type) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: chanmode = CHANNEL_A_HT20; break; case NL80211_CHAN_HT40PLUS: chanmode = CHANNEL_A_HT40PLUS; break; case NL80211_CHAN_HT40MINUS: chanmode = CHANNEL_A_HT40MINUS; break; } break; default: break; } return chanmode; } /* * Update internal channel flags. */ void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { ichan->channel = chan->center_freq; ichan->chan = chan; if (chan->band == IEEE80211_BAND_2GHZ) { ichan->chanmode = CHANNEL_G; ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM; } else { ichan->chanmode = CHANNEL_A; ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; } if (channel_type != NL80211_CHAN_NO_HT) ichan->chanmode = ath9k_get_extchanmode(chan, channel_type); } EXPORT_SYMBOL(ath9k_cmn_update_ichannel); /* * Get the internal channel reference. */ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, struct ath_hw *ah) { struct ieee80211_channel *curchan = hw->conf.channel; struct ath9k_channel *channel; u8 chan_idx; chan_idx = curchan->hw_value; channel = &ah->channels[chan_idx]; ath9k_cmn_update_ichannel(channel, curchan, hw->conf.channel_type); return channel; } EXPORT_SYMBOL(ath9k_cmn_get_curchannel); int ath9k_cmn_count_streams(unsigned int chainmask, int max) { int streams = 0; do { if (++streams == max) break; } while ((chainmask = chainmask & (chainmask - 1))); return streams; } EXPORT_SYMBOL(ath9k_cmn_count_streams); void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow, u16 new_txpow, u16 *txpower) { struct ath_regulatory *reg = ath9k_hw_regulatory(ah); if (reg->power_limit != new_txpow) { ath9k_hw_set_txpowerlimit(ah, new_txpow, false); /* read back in case value is clamped */ *txpower = reg->max_power_level; } } EXPORT_SYMBOL(ath9k_cmn_update_txpow); void ath9k_cmn_init_crypto(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); int i = 0; /* Get the hardware key cache size. */ common->keymax = AR_KEYTABLE_SIZE; /* * Check whether the separate key cache entries * are required to handle both tx+rx MIC keys. * With split mic keys the number of stations is limited * to 27 otherwise 59. */ if (ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED; /* * Reset the key cache since some parts do not * reset the contents on initial power up. */ for (i = 0; i < common->keymax; i++) ath_hw_keyreset(common, (u16) i); } EXPORT_SYMBOL(ath9k_cmn_init_crypto); static int __init ath9k_cmn_init(void) { return 0; } module_init(ath9k_cmn_init); static void __exit ath9k_cmn_exit(void) { return; } module_exit(ath9k_cmn_exit); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/calib.h0000644000175000017500000000641612026211315023507 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef CALIB_H #define CALIB_H #include "hw.h" #define AR_PHY_CCA_FILTERWINDOW_LENGTH 5 #define NUM_NF_READINGS 6 #define ATH9K_NF_CAL_HIST_MAX 5 struct ar5416IniArray { u32 *ia_array; u32 ia_rows; u32 ia_columns; }; #define INIT_INI_ARRAY(iniarray, array) do { \ (iniarray)->ia_array = (u32 *)(array); \ (iniarray)->ia_rows = ARRAY_SIZE(array); \ (iniarray)->ia_columns = ARRAY_SIZE(array[0]); \ } while (0) #define INI_RA(iniarray, row, column) \ (((iniarray)->ia_array)[(row) * ((iniarray)->ia_columns) + (column)]) #define INIT_CAL(_perCal) do { \ (_perCal)->calState = CAL_WAITING; \ (_perCal)->calNext = NULL; \ } while (0) #define INSERT_CAL(_ahp, _perCal) \ do { \ if ((_ahp)->cal_list_last == NULL) { \ (_ahp)->cal_list = \ (_ahp)->cal_list_last = (_perCal); \ ((_ahp)->cal_list_last)->calNext = (_perCal); \ } else { \ ((_ahp)->cal_list_last)->calNext = (_perCal); \ (_ahp)->cal_list_last = (_perCal); \ (_perCal)->calNext = (_ahp)->cal_list; \ } \ } while (0) enum ath9k_cal_state { CAL_INACTIVE, CAL_WAITING, CAL_RUNNING, CAL_DONE }; #define MIN_CAL_SAMPLES 1 #define MAX_CAL_SAMPLES 64 #define INIT_LOG_COUNT 5 #define PER_MIN_LOG_COUNT 2 #define PER_MAX_LOG_COUNT 10 struct ath9k_percal_data { u32 calType; u32 calNumSamples; u32 calCountMax; void (*calCollect) (struct ath_hw *); void (*calPostProc) (struct ath_hw *, u8); }; struct ath9k_cal_list { const struct ath9k_percal_data *calData; enum ath9k_cal_state calState; struct ath9k_cal_list *calNext; }; struct ath9k_nfcal_hist { int16_t nfCalBuffer[ATH9K_NF_CAL_HIST_MAX]; u8 currIndex; int16_t privNF; u8 invalidNFcount; }; #define MAX_PACAL_SKIPCOUNT 8 struct ath9k_pacal_info{ int32_t prev_offset; /* Previous value of PA offset value */ int8_t max_skipcount; /* Max No. of times PACAL can be skipped */ int8_t skipcount; /* No. of times the PACAL to be skipped */ }; bool ath9k_hw_reset_calvalid(struct ath_hw *ah); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update); void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_hw_bstuck_nfcal(struct ath_hw *ah); void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal); s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); #endif /* CALIB_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/calib.c0000644000175000017500000002707112026211315023502 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "hw-ops.h" #include /* Common calibration code */ static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) { int16_t nfval; int16_t sort[ATH9K_NF_CAL_HIST_MAX]; int i, j; for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) sort[i] = nfCalBuffer[i]; for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { if (sort[j] > sort[j - 1]) { nfval = sort[j]; sort[j] = sort[j - 1]; sort[j - 1] = nfval; } } } nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; return nfval; } static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_nf_limits *limit; if (!chan || IS_CHAN_2GHZ(chan)) limit = &ah->nf_2g; else limit = &ah->nf_5g; return limit; } static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, struct ath9k_channel *chan) { return ath9k_hw_get_nf_limits(ah, chan)->nominal; } s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) { s8 noise = ATH_DEFAULT_NOISE_FLOOR; if (chan && chan->noisefloor) { s8 delta = chan->noisefloor - ath9k_hw_get_default_nf(ah, chan); if (delta > 0) noise += delta; } return noise; } EXPORT_SYMBOL(ath9k_hw_getchan_noise); static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, struct ath9k_hw_cal_data *cal, int16_t *nfarray) { struct ath_common *common = ath9k_hw_common(ah); struct ath_nf_limits *limit; struct ath9k_nfcal_hist *h; bool high_nf_mid = false; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; int i; h = cal->nfCalHist; limit = ath9k_hw_get_nf_limits(ah, ah->curchan); for (i = 0; i < NUM_NF_READINGS; i++) { if (!(chainmask & (1 << i)) || ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(ah->curchan))) continue; h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) h[i].currIndex = 0; if (h[i].invalidNFcount > 0) { h[i].invalidNFcount--; h[i].privNF = nfarray[i]; } else { h[i].privNF = ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); } if (!h[i].privNF) continue; if (h[i].privNF > limit->max) { high_nf_mid = true; ath_dbg(common, CALIBRATE, "NFmid[%d] (%d) > MAX (%d), %s\n", i, h[i].privNF, limit->max, (cal->nfcal_interference ? "not corrected (due to interference)" : "correcting to MAX")); /* * Normally we limit the average noise floor by the * hardware specific maximum here. However if we have * encountered stuck beacons because of interference, * we bypass this limit here in order to better deal * with our environment. */ if (!cal->nfcal_interference) h[i].privNF = limit->max; } } /* * If the noise floor seems normal for all chains, assume that * there is no significant interference in the environment anymore. * Re-enable the enforcement of the NF maximum again. */ if (!high_nf_mid) cal->nfcal_interference = false; } static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, enum ieee80211_band band, int16_t *nft) { switch (band) { case IEEE80211_BAND_5GHZ: *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5); break; case IEEE80211_BAND_2GHZ: *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2); break; default: BUG_ON(1); return false; } return true; } void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal) { int i; ath9k_hw_setup_calibration(ah, currCal); currCal->calState = CAL_RUNNING; for (i = 0; i < AR5416_MAX_CHAINS; i++) { ah->meas0.sign[i] = 0; ah->meas1.sign[i] = 0; ah->meas2.sign[i] = 0; ah->meas3.sign[i] = 0; } ah->cal_samples = 0; } /* This is done for the currently configured channel */ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; struct ath9k_cal_list *currCal = ah->cal_list_curr; if (!ah->caldata) return true; if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) return true; if (currCal == NULL) return true; if (currCal->calState != CAL_DONE) { ath_dbg(common, CALIBRATE, "Calibration state incorrect, %d\n", currCal->calState); return true; } if (!(ah->supp_cals & currCal->calData->calType)) return true; ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", currCal->calData->calType, conf->channel->center_freq); ah->caldata->CalValid &= ~currCal->calData->calType; currCal->calState = CAL_WAITING; return false; } EXPORT_SYMBOL(ath9k_hw_reset_calvalid); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) { if (ah->caldata) ah->caldata->nfcal_pending = true; REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); if (update) REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); else REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); } void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath9k_nfcal_hist *h = NULL; unsigned i, j; int32_t val; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; s16 default_nf = ath9k_hw_get_default_nf(ah, chan); if (ah->caldata) h = ah->caldata->nfCalHist; for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { s16 nfval; if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) continue; if (h) nfval = h[i].privNF; else nfval = default_nf; val = REG_READ(ah, ah->nf_regs[i]); val &= 0xFFFFFE00; val |= (((u32) nfval << 1) & 0x1ff); REG_WRITE(ah, ah->nf_regs[i], val); } } /* * Load software filtered NF value into baseband internal minCCApwr * variable. */ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); /* * Wait for load to complete, should be fast, a few 10s of us. * The max delay was changed from an original 250us to 10000us * since 250us often results in NF load timeout and causes deaf * condition during stress testing 12/12/2009 */ for (j = 0; j < 10000; j++) { if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0) break; udelay(10); } /* * We timed out waiting for the noisefloor to load, probably due to an * in-progress rx. Simply return here and allow the load plenty of time * to complete before the next calibration interval. We need to avoid * trying to load -50 (which happens below) while the previous load is * still in progress as this can cause rx deafness. Instead by returning * here, the baseband nf cal will just be capped by our present * noisefloor until the next calibration timer. */ if (j == 10000) { ath_dbg(common, ANY, "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", REG_READ(ah, AR_PHY_AGC_CONTROL)); return; } /* * Restore maxCCAPower register parameter again so that we're not capped * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) continue; val = REG_READ(ah, ah->nf_regs[i]); val &= 0xFFFFFE00; val |= (((u32) (-50) << 1) & 0x1ff); REG_WRITE(ah, ah->nf_regs[i], val); } } REGWRITE_BUFFER_FLUSH(ah); } static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) { struct ath_common *common = ath9k_hw_common(ah); struct ath_nf_limits *limit; int i; if (IS_CHAN_2GHZ(ah->curchan)) limit = &ah->nf_2g; else limit = &ah->nf_5g; for (i = 0; i < NUM_NF_READINGS; i++) { if (!nf[i]) continue; ath_dbg(common, CALIBRATE, "NF calibrated [%s] [chain %d] is %d\n", (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); if (nf[i] > limit->max) { ath_dbg(common, CALIBRATE, "NF[%d] (%d) > MAX (%d), correcting to MAX\n", i, nf[i], limit->max); nf[i] = limit->max; } else if (nf[i] < limit->min) { ath_dbg(common, CALIBRATE, "NF[%d] (%d) < MIN (%d), correcting to NOM\n", i, nf[i], limit->min); nf[i] = limit->nominal; } } } bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); int16_t nf, nfThresh; int16_t nfarray[NUM_NF_READINGS] = { 0 }; struct ath9k_nfcal_hist *h; struct ieee80211_channel *c = chan->chan; struct ath9k_hw_cal_data *caldata = ah->caldata; chan->channelFlags &= (~CHANNEL_CW_INT); if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { ath_dbg(common, CALIBRATE, "NF did not complete in calibration window\n"); return false; } ath9k_hw_do_getnf(ah, nfarray); ath9k_hw_nf_sanitize(ah, nfarray); nf = nfarray[0]; if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) && nf > nfThresh) { ath_dbg(common, CALIBRATE, "noise floor failed detected; detected %d, threshold %d\n", nf, nfThresh); chan->channelFlags |= CHANNEL_CW_INT; } if (!caldata) { chan->noisefloor = nf; ah->noise = ath9k_hw_getchan_noise(ah, chan); return false; } h = caldata->nfCalHist; caldata->nfcal_pending = false; ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); chan->noisefloor = h[0].privNF; ah->noise = ath9k_hw_getchan_noise(ah, chan); return true; } EXPORT_SYMBOL(ath9k_hw_getnf); void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath9k_nfcal_hist *h; s16 default_nf; int i, j; ah->caldata->channel = chan->channel; ah->caldata->channelFlags = chan->channelFlags & ~CHANNEL_CW_INT; h = ah->caldata->nfCalHist; default_nf = ath9k_hw_get_default_nf(ah, chan); for (i = 0; i < NUM_NF_READINGS; i++) { h[i].currIndex = 0; h[i].privNF = default_nf; h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH; for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { h[i].nfCalBuffer[j] = default_nf; } } } void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) { struct ath9k_hw_cal_data *caldata = ah->caldata; if (unlikely(!caldata)) return; /* * If beacons are stuck, the most likely cause is interference. * Triggering a noise floor calibration at this point helps the * hardware adapt to a noisy environment much faster. * To ensure that we recover from stuck beacons quickly, let * the baseband update the internal NF value itself, similar to * what is being done after a full reset. */ if (!caldata->nfcal_pending) ath9k_hw_start_nfcal(ah, true); else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)) ath9k_hw_getnf(ah, ah->curchan); caldata->nfcal_interference = true; } EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/btcoex.h0000644000175000017500000000634312026211315023720 0ustar mcgrofmcgrof/* * Copyright (c) 2009-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef BTCOEX_H #define BTCOEX_H #include "hw.h" #define ATH_WLANACTIVE_GPIO_9280 5 #define ATH_BTACTIVE_GPIO_9280 6 #define ATH_BTPRIORITY_GPIO_9285 7 #define ATH_WLANACTIVE_GPIO_9300 5 #define ATH_BTACTIVE_GPIO_9300 4 #define ATH_BTPRIORITY_GPIO_9300 8 #define ATH_BTCOEX_DEF_BT_PERIOD 45 #define ATH_BTCOEX_DEF_DUTY_CYCLE 55 #define ATH_BTCOEX_BTSCAN_DUTY_CYCLE 90 #define ATH_BTCOEX_BMISS_THRESH 50 #define ATH_BT_PRIORITY_TIME_THRESHOLD 1000 /* ms */ #define ATH_BT_CNT_THRESHOLD 3 #define ATH_BT_CNT_SCAN_THRESHOLD 15 #define ATH_BTCOEX_RX_WAIT_TIME 100 #define ATH_BTCOEX_STOMP_FTP_THRESH 5 #define AR9300_NUM_BT_WEIGHTS 4 #define AR9300_NUM_WLAN_WEIGHTS 4 /* Defines the BT AR_BT_COEX_WGHT used */ enum ath_stomp_type { ATH_BTCOEX_STOMP_ALL, ATH_BTCOEX_STOMP_LOW, ATH_BTCOEX_STOMP_NONE, ATH_BTCOEX_STOMP_LOW_FTP, ATH_BTCOEX_STOMP_MAX }; enum ath_btcoex_scheme { ATH_BTCOEX_CFG_NONE, ATH_BTCOEX_CFG_2WIRE, ATH_BTCOEX_CFG_3WIRE, }; struct ath9k_hw_mci { u32 raw_intr; u32 rx_msg_intr; u32 cont_status; u32 gpm_addr; u32 gpm_len; u32 gpm_idx; u32 sched_addr; u32 wlan_channels[4]; u32 wlan_cal_seq; u32 wlan_cal_done; u32 config; u8 *gpm_buf; bool ready; bool update_2g5g; bool is_2g; bool query_bt; bool unhalt_bt_gpm; /* need send UNHALT */ bool halted_bt_gpm; /* HALT sent */ bool need_flush_btinfo; bool bt_version_known; bool wlan_channels_update; u8 wlan_ver_major; u8 wlan_ver_minor; u8 bt_ver_major; u8 bt_ver_minor; u8 bt_state; u8 stomp_ftp; }; struct ath_btcoex_hw { enum ath_btcoex_scheme scheme; struct ath9k_hw_mci mci; bool enabled; u8 wlanactive_gpio; u8 btactive_gpio; u8 btpriority_gpio; u32 bt_coex_mode; /* Register setting for AR_BT_COEX_MODE */ u32 bt_coex_weights; /* Register setting for AR_BT_COEX_WEIGHT */ u32 bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */ u32 bt_weight[AR9300_NUM_BT_WEIGHTS]; u32 wlan_weight[AR9300_NUM_WLAN_WEIGHTS]; }; void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah); void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah); void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah); void ath9k_hw_btcoex_init_mci(struct ath_hw *ah); void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum); void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, u32 bt_weight, u32 wlan_weight); void ath9k_hw_btcoex_disable(struct ath_hw *ah); void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah, enum ath_stomp_type stomp_type); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/btcoex.c0000644000175000017500000002647312026211315023721 0ustar mcgrofmcgrof/* * Copyright (c) 2009-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" enum ath_bt_mode { ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */ ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */ ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */ ATH_BT_COEX_MODE_DISABLED, /* coexistence disabled */ }; struct ath_btcoex_config { u8 bt_time_extend; bool bt_txstate_extend; bool bt_txframe_extend; enum ath_bt_mode bt_mode; /* coexistence mode */ bool bt_quiet_collision; bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/ u8 bt_priority_time; u8 bt_first_slot_time; bool bt_hold_rx_clear; }; static const u32 ar9003_wlan_weights[ATH_BTCOEX_STOMP_MAX] [AR9300_NUM_WLAN_WEIGHTS] = { { 0xfffffff0, 0xfffffff0, 0xfffffff0, 0xfffffff0 }, /* STOMP_ALL */ { 0x88888880, 0x88888880, 0x88888880, 0x88888880 }, /* STOMP_LOW */ { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* STOMP_NONE */ }; static const u32 ar9462_wlan_weights[ATH_BTCOEX_STOMP_MAX] [AR9300_NUM_WLAN_WEIGHTS] = { { 0x01017d01, 0x41414101, 0x41414101, 0x41414141 }, /* STOMP_ALL */ { 0x01017d01, 0x3b3b3b01, 0x3b3b3b01, 0x3b3b3b3b }, /* STOMP_LOW */ { 0x01017d01, 0x01010101, 0x01010101, 0x01010101 }, /* STOMP_NONE */ { 0x01017d01, 0x013b0101, 0x3b3b0101, 0x3b3b013b }, /* STOMP_LOW_FTP */ }; void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; const struct ath_btcoex_config ath_bt_config = { .bt_time_extend = 0, .bt_txstate_extend = true, .bt_txframe_extend = true, .bt_mode = ATH_BT_COEX_MODE_SLOTTED, .bt_quiet_collision = true, .bt_rxclear_polarity = true, .bt_priority_time = 2, .bt_first_slot_time = 5, .bt_hold_rx_clear = true, }; u32 i, idx; bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity; if (AR_SREV_9300_20_OR_LATER(ah)) rxclear_polarity = !ath_bt_config.bt_rxclear_polarity; btcoex_hw->bt_coex_mode = (btcoex_hw->bt_coex_mode & AR_BT_QCU_THRESH) | SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) | SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | SM(ath_bt_config.bt_mode, AR_BT_MODE) | SM(ath_bt_config.bt_quiet_collision, AR_BT_QUIET) | SM(rxclear_polarity, AR_BT_RX_CLEAR_POLARITY) | SM(ath_bt_config.bt_priority_time, AR_BT_PRIORITY_TIME) | SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) | SM(qnum, AR_BT_QCU_THRESH); btcoex_hw->bt_coex_mode2 = SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | AR_BT_DISABLE_BT_ANT; for (i = 0; i < 32; i++) { idx = (debruijn32 << i) >> 27; ah->hw_gen_timers.gen_timer_index[idx] = i; } } EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw); void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; /* * Check if BTCOEX is globally disabled. */ if (!common->btcoex_enabled) { btcoex_hw->scheme = ATH_BTCOEX_CFG_NONE; return; } if (AR_SREV_9300_20_OR_LATER(ah)) { btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300; btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300; btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9300; } else if (AR_SREV_9280_20_OR_LATER(ah)) { btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9280; btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9280; if (AR_SREV_9285(ah)) { btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9285; } else { btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE; } } } EXPORT_SYMBOL(ath9k_hw_btcoex_init_scheme); void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; /* connect bt_active to baseband */ REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); /* Set input mux for bt_active to gpio pin */ REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, AR_GPIO_INPUT_MUX1_BT_ACTIVE, btcoex_hw->btactive_gpio); /* Configure the desired gpio port for input */ ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio); } EXPORT_SYMBOL(ath9k_hw_btcoex_init_2wire); void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; /* btcoex 3-wire */ REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); /* Set input mux for bt_prority_async and * bt_active_async to GPIO pins */ REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, AR_GPIO_INPUT_MUX1_BT_ACTIVE, btcoex_hw->btactive_gpio); REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, AR_GPIO_INPUT_MUX1_BT_PRIORITY, btcoex_hw->btpriority_gpio); /* Configure the desired GPIO ports for input */ ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio); ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btpriority_gpio); } EXPORT_SYMBOL(ath9k_hw_btcoex_init_3wire); void ath9k_hw_btcoex_init_mci(struct ath_hw *ah) { ah->btcoex_hw.mci.ready = false; ah->btcoex_hw.mci.bt_state = 0; ah->btcoex_hw.mci.bt_ver_major = 3; ah->btcoex_hw.mci.bt_ver_minor = 0; ah->btcoex_hw.mci.bt_version_known = false; ah->btcoex_hw.mci.update_2g5g = true; ah->btcoex_hw.mci.is_2g = true; ah->btcoex_hw.mci.wlan_channels_update = false; ah->btcoex_hw.mci.wlan_channels[0] = 0x00000000; ah->btcoex_hw.mci.wlan_channels[1] = 0xffffffff; ah->btcoex_hw.mci.wlan_channels[2] = 0xffffffff; ah->btcoex_hw.mci.wlan_channels[3] = 0x7fffffff; ah->btcoex_hw.mci.query_bt = true; ah->btcoex_hw.mci.unhalt_bt_gpm = true; ah->btcoex_hw.mci.halted_bt_gpm = false; ah->btcoex_hw.mci.need_flush_btinfo = false; ah->btcoex_hw.mci.wlan_cal_seq = 0; ah->btcoex_hw.mci.wlan_cal_done = 0; ah->btcoex_hw.mci.config = 0x2201; } EXPORT_SYMBOL(ath9k_hw_btcoex_init_mci); static void ath9k_hw_btcoex_enable_2wire(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; /* Configure the desired GPIO port for TX_FRAME output */ ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio, AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); } void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, u32 bt_weight, u32 wlan_weight) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; btcoex_hw->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) | SM(wlan_weight, AR_BTCOEX_WL_WGHT); } EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight); static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; u32 val; int i; /* * Program coex mode and weight registers to * enable coex 3-wire */ REG_WRITE(ah, AR_BT_COEX_MODE, btcoex->bt_coex_mode); REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2); if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS0, btcoex->wlan_weight[0]); REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS1, btcoex->wlan_weight[1]); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS(i), btcoex->bt_weight[i]); } else REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex->bt_coex_weights); if (AR_SREV_9271(ah)) { val = REG_READ(ah, 0x50040); val &= 0xFFFFFEFF; REG_WRITE(ah, 0x50040, val); } REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); ath9k_hw_cfg_output(ah, btcoex->wlanactive_gpio, AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL); } static void ath9k_hw_btcoex_enable_mci(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; int i; for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) REG_WRITE(ah, AR_MCI_COEX_WL_WEIGHTS(i), btcoex->wlan_weight[i]); REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); btcoex->enabled = true; } void ath9k_hw_btcoex_enable(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; switch (ath9k_hw_get_btcoex_scheme(ah)) { case ATH_BTCOEX_CFG_NONE: return; case ATH_BTCOEX_CFG_2WIRE: ath9k_hw_btcoex_enable_2wire(ah); break; case ATH_BTCOEX_CFG_3WIRE: if (AR_SREV_9462(ah)) { ath9k_hw_btcoex_enable_mci(ah); return; } ath9k_hw_btcoex_enable_3wire(ah); break; } REG_RMW(ah, AR_GPIO_PDPU, (0x2 << (btcoex_hw->btactive_gpio * 2)), (0x3 << (btcoex_hw->btactive_gpio * 2))); ah->btcoex_hw.enabled = true; } EXPORT_SYMBOL(ath9k_hw_btcoex_enable); void ath9k_hw_btcoex_disable(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; int i; btcoex_hw->enabled = false; if (AR_SREV_9462(ah)) { ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) REG_WRITE(ah, AR_MCI_COEX_WL_WEIGHTS(i), btcoex_hw->wlan_weight[i]); return; } ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0); ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); if (btcoex_hw->scheme == ATH_BTCOEX_CFG_3WIRE) { REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); REG_WRITE(ah, AR_BT_COEX_MODE2, 0); if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS0, 0); REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS1, 0); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS(i), 0); } else REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); } } EXPORT_SYMBOL(ath9k_hw_btcoex_disable); static void ar9003_btcoex_bt_stomp(struct ath_hw *ah, enum ath_stomp_type stomp_type) { struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; const u32 *weight = ar9003_wlan_weights[stomp_type]; int i; if (AR_SREV_9462(ah)) { if ((stomp_type == ATH_BTCOEX_STOMP_LOW) && btcoex->mci.stomp_ftp) stomp_type = ATH_BTCOEX_STOMP_LOW_FTP; weight = ar9462_wlan_weights[stomp_type]; } for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) { btcoex->bt_weight[i] = AR9300_BT_WGHT; btcoex->wlan_weight[i] = weight[i]; } } /* * Configures appropriate weight based on stomp type. */ void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah, enum ath_stomp_type stomp_type) { if (AR_SREV_9300_20_OR_LATER(ah)) { ar9003_btcoex_bt_stomp(ah, stomp_type); return; } switch (stomp_type) { case ATH_BTCOEX_STOMP_ALL: ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, AR_STOMP_ALL_WLAN_WGHT); break; case ATH_BTCOEX_STOMP_LOW: ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, AR_STOMP_LOW_WLAN_WGHT); break; case ATH_BTCOEX_STOMP_NONE: ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, AR_STOMP_NONE_WLAN_WGHT); break; default: ath_dbg(ath9k_hw_common(ah), BTCOEX, "Invalid Stomptype\n"); break; } } EXPORT_SYMBOL(ath9k_hw_btcoex_bt_stomp); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/beacon.c0000644000175000017500000005101012026211315023645 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "ath9k.h" #define FUDGE 2 static void ath9k_reset_beacon_status(struct ath_softc *sc) { sc->beacon.tx_processed = false; sc->beacon.tx_last = false; } /* * This function will modify certain transmit queue properties depending on * the operating mode of the station (AP or AdHoc). Parameters are AIFS * settings and channel width min/max */ static void ath9k_beaconq_config(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info qi, qi_be; struct ath_txq *txq; ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { /* Always burst out beacon and CAB traffic. */ qi.tqi_aifs = 1; qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; } else { /* Adhoc mode; important thing is to use 2x cwmin. */ txq = sc->tx.txq_map[WME_AC_BE]; ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); qi.tqi_aifs = qi_be.tqi_aifs; if (ah->slottime == ATH9K_SLOT_TIME_20) qi.tqi_cwmin = 2*qi_be.tqi_cwmin; else qi.tqi_cwmin = 4*qi_be.tqi_cwmin; qi.tqi_cwmax = qi_be.tqi_cwmax; } if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) { ath_err(common, "Unable to update h/w beacon queue parameters\n"); } else { ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); } } /* * Associates the beacon frame buffer with a transmit descriptor. Will set * up rate codes, and channel flags. Beacons are always sent out at the * lowest rate, and are not retried. */ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, struct ath_buf *bf, int rateidx) { struct sk_buff *skb = bf->bf_mpdu; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_tx_info info; struct ieee80211_supported_band *sband; u8 chainmask = ah->txchainmask; u8 rate = 0; sband = &sc->sbands[common->hw->conf.channel->band]; rate = sband->bitrates[rateidx].hw_value; if (vif->bss_conf.use_short_preamble) rate |= sband->bitrates[rateidx].hw_value_short; memset(&info, 0, sizeof(info)); info.pkt_len = skb->len + FCS_LEN; info.type = ATH9K_PKT_TYPE_BEACON; info.txpower = MAX_RATE_POWER; info.keyix = ATH9K_TXKEYIX_INVALID; info.keytype = ATH9K_KEY_TYPE_CLEAR; info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK; info.buf_addr[0] = bf->bf_buf_addr; info.buf_len[0] = roundup(skb->len, 4); info.is_first = true; info.is_last = true; info.qcu = sc->beacon.beaconq; info.rates[0].Tries = 1; info.rates[0].Rate = rate; info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate); ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); } static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_tx_control txctl; memset(&txctl, 0, sizeof(struct ath_tx_control)); txctl.txq = sc->beacon.cabq; ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb); if (ath_tx_start(hw, skb, &txctl) != 0) { ath_dbg(common, XMIT, "CABQ TX failed\n"); dev_kfree_skb_any(skb); } } static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_buf *bf; struct ath_vif *avp = (void *)vif->drv_priv; struct sk_buff *skb; struct ath_txq *cabq = sc->beacon.cabq; struct ieee80211_tx_info *info; struct ieee80211_mgmt *mgmt_hdr; int cabq_depth; if (avp->av_bcbuf == NULL) return NULL; bf = avp->av_bcbuf; skb = bf->bf_mpdu; if (skb) { dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_buf_addr = 0; } skb = ieee80211_beacon_get(hw, vif); if (skb == NULL) return NULL; bf->bf_mpdu = skb; mgmt_hdr = (struct ieee80211_mgmt *)skb->data; mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust; info = IEEE80211_SKB_CB(skb); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { /* * TODO: make sure the seq# gets assigned properly (vs. other * TX frames) */ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; sc->tx.seq_no += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; bf->bf_buf_addr = 0; ath_err(common, "dma_mapping_error on beaconing\n"); return NULL; } skb = ieee80211_get_buffered_bc(hw, vif); /* * if the CABQ traffic from previous DTIM is pending and the current * beacon is also a DTIM. * 1) if there is only one vif let the cab traffic continue. * 2) if there are more than one vif and we are using staggered * beacons, then drain the cabq by dropping all the frames in * the cabq so that the current vifs cab traffic can be scheduled. */ spin_lock_bh(&cabq->axq_lock); cabq_depth = cabq->axq_depth; spin_unlock_bh(&cabq->axq_lock); if (skb && cabq_depth) { if (sc->nvifs > 1) { ath_dbg(common, BEACON, "Flushing previous cabq traffic\n"); ath_draintxq(sc, cabq, false); } } ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx); while (skb) { ath9k_tx_cabq(hw, skb); skb = ieee80211_get_buffered_bc(hw, vif); } return bf; } void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; int slot; avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list); list_del(&avp->av_bcbuf->list); for (slot = 0; slot < ATH_BCBUF; slot++) { if (sc->beacon.bslot[slot] == NULL) { avp->av_bslot = slot; break; } } sc->beacon.bslot[avp->av_bslot] = vif; sc->nbcnvifs++; ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n", avp->av_bslot); } void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_buf *bf = avp->av_bcbuf; ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", avp->av_bslot); tasklet_disable(&sc->bcon_tasklet); if (bf && bf->bf_mpdu) { struct sk_buff *skb = bf->bf_mpdu; dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; bf->bf_buf_addr = 0; } avp->av_bcbuf = NULL; sc->beacon.bslot[avp->av_bslot] = NULL; sc->nbcnvifs--; list_add_tail(&bf->list, &sc->beacon.bbuf); tasklet_enable(&sc->bcon_tasklet); } static int ath9k_beacon_choose_slot(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; u16 intval; u32 tsftu; u64 tsf; int slot; if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) { ath_dbg(common, BEACON, "slot 0, tsf: %llu\n", ath9k_hw_gettsf64(sc->sc_ah)); return 0; } intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL; tsf = ath9k_hw_gettsf64(sc->sc_ah); tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time); tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF); slot = (tsftu % (intval * ATH_BCBUF)) / intval; ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n", slot, tsf, tsftu / ATH_BCBUF); return slot; } void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; struct ath_vif *avp = (void *)vif->drv_priv; u64 tsfadjust; if (avp->av_bslot == 0) return; tsfadjust = cur_conf->beacon_interval * avp->av_bslot / ATH_BCBUF; avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n", (unsigned long long)tsfadjust, avp->av_bslot); } void ath9k_beacon_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf = NULL; struct ieee80211_vif *vif; bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); int slot; if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) { ath_dbg(common, RESET, "reset work is pending, skip beaconing now\n"); return; } /* * Check if the previous beacon has gone out. If * not don't try to post another, skip this period * and wait for the next. Missed beacons indicate * a problem and should not occur. If we miss too * many consecutive beacons reset the device. */ if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { sc->beacon.bmisscnt++; if (!ath9k_hw_check_alive(ah)) ieee80211_queue_work(sc->hw, &sc->hw_check_work); if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { ath_dbg(common, BSTUCK, "missed %u consecutive beacons\n", sc->beacon.bmisscnt); ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq); if (sc->beacon.bmisscnt > 3) ath9k_hw_bstuck_nfcal(ah); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); sc->beacon.bmisscnt = 0; ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK); } return; } slot = ath9k_beacon_choose_slot(sc); vif = sc->beacon.bslot[slot]; if (!vif || !vif->bss_conf.enable_beacon) return; bf = ath9k_beacon_generate(sc->hw, vif); WARN_ON(!bf); if (sc->beacon.bmisscnt != 0) { ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n", sc->beacon.bmisscnt); sc->beacon.bmisscnt = 0; } /* * Handle slot time change when a non-ERP station joins/leaves * an 11g network. The 802.11 layer notifies us via callback, * we mark updateslot, then wait one beacon before effecting * the change. This gives associated stations at least one * beacon interval to note the state change. * * NB: The slot time change state machine is clocked according * to whether we are bursting or staggering beacons. We * recognize the request to update and record the current * slot then don't transition until that slot is reached * again. If we miss a beacon for that slot then we'll be * slow to transition but we'll be sure at least one beacon * interval has passed. When bursting slot is always left * set to ATH_BCBUF so this check is a noop. */ if (sc->beacon.updateslot == UPDATE) { sc->beacon.updateslot = COMMIT; sc->beacon.slotupdate = slot; } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { ah->slottime = sc->beacon.slottime; ath9k_hw_init_global_settings(ah); sc->beacon.updateslot = OK; } if (bf) { ath9k_reset_beacon_status(sc); ath_dbg(common, BEACON, "Transmitting beacon for slot: %d\n", slot); /* NB: cabq traffic should already be queued and primed */ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); if (!edma) ath9k_hw_txstart(ah, sc->beacon.beaconq); } } static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, u32 intval) { struct ath_hw *ah = sc->sc_ah; ath9k_hw_disable_interrupts(ah); ath9k_hw_reset_tsf(ah); ath9k_beaconq_config(sc); ath9k_hw_beaconinit(ah, nexttbtt, intval); sc->beacon.bmisscnt = 0; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); } /* * For multi-bss ap support beacons are either staggered evenly over N slots or * burst together. For the former arrange for the SWBA to be delivered for each * slot. Slots that are not occupied will generate nothing. */ static void ath9k_beacon_config_ap(struct ath_softc *sc, struct ath_beacon_config *conf) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u32 nexttbtt, intval; /* NB: the beacon interval is kept internally in TU's */ intval = TU_TO_USEC(conf->beacon_interval); intval /= ATH_BCBUF; nexttbtt = intval; if (conf->enable_beacon) ah->imask |= ATH9K_INT_SWBA; else ah->imask &= ~ATH9K_INT_SWBA; ath_dbg(common, BEACON, "AP nexttbtt: %u intval: %u conf_intval: %u\n", nexttbtt, intval, conf->beacon_interval); ath9k_beacon_init(sc, nexttbtt, intval); } /* * This sets up the beacon timers according to the timestamp of the last * received beacon and the current TSF, configures PCF and DTIM * handling, programs the sleep registers so the hardware will wakeup in * time to receive beacons, and configures the beacon miss handling so * we'll receive a BMISS interrupt when we stop seeing beacons from the AP * we've associated with. */ static void ath9k_beacon_config_sta(struct ath_softc *sc, struct ath_beacon_config *conf) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_beacon_state bs; int dtimperiod, dtimcount, sleepduration; int cfpperiod, cfpcount; u32 nexttbtt = 0, intval, tsftu; u64 tsf; int num_beacons, offset, dtim_dec_count, cfp_dec_count; /* No need to configure beacon if we are not associated */ if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { ath_dbg(common, BEACON, "STA is not yet associated..skipping beacon config\n"); return; } memset(&bs, 0, sizeof(bs)); intval = conf->beacon_interval; /* * Setup dtim and cfp parameters according to * last beacon we received (which may be none). */ dtimperiod = conf->dtim_period; dtimcount = conf->dtim_count; if (dtimcount >= dtimperiod) /* NB: sanity check */ dtimcount = 0; cfpperiod = 1; /* NB: no PCF support yet */ cfpcount = 0; sleepduration = conf->listen_interval * intval; /* * Pull nexttbtt forward to reflect the current * TSF and calculate dtim+cfp state for the result. */ tsf = ath9k_hw_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; num_beacons = tsftu / intval + 1; offset = tsftu % intval; nexttbtt = tsftu - offset; if (offset) nexttbtt += intval; /* DTIM Beacon every dtimperiod Beacon */ dtim_dec_count = num_beacons % dtimperiod; /* CFP every cfpperiod DTIM Beacon */ cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; if (dtim_dec_count) cfp_dec_count++; dtimcount -= dtim_dec_count; if (dtimcount < 0) dtimcount += dtimperiod; cfpcount -= cfp_dec_count; if (cfpcount < 0) cfpcount += cfpperiod; bs.bs_intval = intval; bs.bs_nexttbtt = nexttbtt; bs.bs_dtimperiod = dtimperiod*intval; bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; bs.bs_cfpmaxduration = 0; /* * Calculate the number of consecutive beacons to miss* before taking * a BMISS interrupt. The configuration is specified in TU so we only * need calculate based on the beacon interval. Note that we clamp the * result to at most 15 beacons. */ if (sleepduration > intval) { bs.bs_bmissthreshold = conf->listen_interval * ATH_DEFAULT_BMISS_LIMIT / 2; } else { bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval); if (bs.bs_bmissthreshold > 15) bs.bs_bmissthreshold = 15; else if (bs.bs_bmissthreshold <= 0) bs.bs_bmissthreshold = 1; } /* * Calculate sleep duration. The configuration is given in ms. * We ensure a multiple of the beacon period is used. Also, if the sleep * duration is greater than the DTIM period then it makes senses * to make it a multiple of that. * * XXX fixed at 100ms */ bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); if (bs.bs_sleepduration > bs.bs_dtimperiod) bs.bs_sleepduration = bs.bs_dtimperiod; /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; ath_dbg(common, BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); ath_dbg(common, BEACON, "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", bs.bs_bmissthreshold, bs.bs_sleepduration, bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); /* Set the computed STA beacon timers */ ath9k_hw_disable_interrupts(ah); ath9k_hw_set_sta_beacon_timers(ah, &bs); ah->imask |= ATH9K_INT_BMISS; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); } static void ath9k_beacon_config_adhoc(struct ath_softc *sc, struct ath_beacon_config *conf) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u32 intval, nexttbtt; ath9k_reset_beacon_status(sc); intval = TU_TO_USEC(conf->beacon_interval); nexttbtt = intval; if (conf->enable_beacon) ah->imask |= ATH9K_INT_SWBA; else ah->imask &= ~ATH9K_INT_SWBA; ath_dbg(common, BEACON, "IBSS nexttbtt: %u intval: %u conf_intval: %u\n", nexttbtt, intval, conf->beacon_interval); ath9k_beacon_init(sc, nexttbtt, intval); } bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { if ((vif->type != NL80211_IFTYPE_AP) || (sc->nbcnvifs > 1)) { ath_dbg(common, CONFIG, "An AP interface is already present !\n"); return false; } } if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { if ((vif->type == NL80211_IFTYPE_STATION) && test_bit(SC_OP_BEACONS, &sc->sc_flags) && !avp->primary_sta_vif) { ath_dbg(common, CONFIG, "Beacon already configured for a station interface\n"); return false; } } return true; } static void ath9k_cache_beacon_config(struct ath_softc *sc, struct ieee80211_bss_conf *bss_conf) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; ath_dbg(common, BEACON, "Caching beacon data for BSS: %pM\n", bss_conf->bssid); cur_conf->beacon_interval = bss_conf->beacon_int; cur_conf->dtim_period = bss_conf->dtim_period; cur_conf->listen_interval = 1; cur_conf->dtim_count = 1; cur_conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; /* * It looks like mac80211 may end up using beacon interval of zero in * some cases (at least for mesh point). Avoid getting into an * infinite loop by using a bit safer value instead. To be safe, * do sanity check on beacon interval for all operating modes. */ if (cur_conf->beacon_interval == 0) cur_conf->beacon_interval = 100; /* * We don't parse dtim period from mac80211 during the driver * initialization as it breaks association with hidden-ssid * AP and it causes latency in roaming */ if (cur_conf->dtim_period == 0) cur_conf->dtim_period = 1; } void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, u32 changed) { struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { ath9k_cache_beacon_config(sc, bss_conf); ath9k_set_beacon(sc); set_bit(SC_OP_BEACONS, &sc->sc_flags); } else { /* * Take care of multiple interfaces when * enabling/disabling SWBA. */ if (changed & BSS_CHANGED_BEACON_ENABLED) { if (!bss_conf->enable_beacon && (sc->nbcnvifs <= 1)) { cur_conf->enable_beacon = false; } else if (bss_conf->enable_beacon) { cur_conf->enable_beacon = true; ath9k_cache_beacon_config(sc, bss_conf); } } if (cur_conf->beacon_interval) { ath9k_set_beacon(sc); if (cur_conf->enable_beacon) set_bit(SC_OP_BEACONS, &sc->sc_flags); else clear_bit(SC_OP_BEACONS, &sc->sc_flags); } } } void ath9k_set_beacon(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; switch (sc->sc_ah->opmode) { case NL80211_IFTYPE_AP: ath9k_beacon_config_ap(sc, cur_conf); break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: ath9k_beacon_config_adhoc(sc, cur_conf); break; case NL80211_IFTYPE_STATION: ath9k_beacon_config_sta(sc, cur_conf); break; default: ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); return; } } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ath9k.h0000644000175000017500000005275012026211315023457 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ATH9K_H #define ATH9K_H #include #include #include #include #include #include "debug.h" #include "common.h" #include "mci.h" #include "dfs.h" /* * Header for the ath9k.ko driver core *only* -- hw code nor any other driver * should rely on this file or its contents. */ struct ath_node; /* Macro to expand scalars to 64-bit objects */ #define ito64(x) (sizeof(x) == 1) ? \ (((unsigned long long int)(x)) & (0xff)) : \ (sizeof(x) == 2) ? \ (((unsigned long long int)(x)) & 0xffff) : \ ((sizeof(x) == 4) ? \ (((unsigned long long int)(x)) & 0xffffffff) : \ (unsigned long long int)(x)) /* increment with wrap-around */ #define INCR(_l, _sz) do { \ (_l)++; \ (_l) &= ((_sz) - 1); \ } while (0) /* decrement with wrap-around */ #define DECR(_l, _sz) do { \ (_l)--; \ (_l) &= ((_sz) - 1); \ } while (0) #define TSF_TO_TU(_h,_l) \ ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) #define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<bf_stale = false; \ (_bf)->bf_lastbf = NULL; \ (_bf)->bf_next = NULL; \ memset(&((_bf)->bf_state), 0, \ sizeof(struct ath_buf_state)); \ } while (0) #define ATH_RXBUF_RESET(_bf) do { \ (_bf)->bf_stale = false; \ } while (0) /** * enum buffer_type - Buffer type flags * * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX) * @BUF_AGGR: Indicates whether the buffer can be aggregated * (used in aggregation scheduling) */ enum buffer_type { BUF_AMPDU = BIT(0), BUF_AGGR = BIT(1), }; #define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU) #define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR) #define ATH_TXSTATUS_RING_SIZE 512 #define DS2PHYS(_dd, _ds) \ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) #define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) #define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) struct ath_descdma { void *dd_desc; dma_addr_t dd_desc_paddr; u32 dd_desc_len; struct ath_buf *dd_bufptr; }; int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, struct list_head *head, const char *name, int nbuf, int ndesc, bool is_tx); void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, struct list_head *head); /***********/ /* RX / TX */ /***********/ #define ATH_RXBUF 512 #define ATH_TXBUF 512 #define ATH_TXBUF_RESERVE 5 #define ATH_MAX_QDEPTH (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE) #define ATH_TXMAXTRY 13 #define TID_TO_WME_AC(_tid) \ ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ WME_AC_VO) #define ATH_AGGR_DELIM_SZ 4 #define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */ /* number of delimiters for encryption padding */ #define ATH_AGGR_ENCRYPTDELIM 10 /* minimum h/w qdepth to be sustained to maximize aggregation */ #define ATH_AGGR_MIN_QDEPTH 2 #define ATH_AMPDU_SUBFRAME_DEFAULT 32 #define IEEE80211_SEQ_SEQ_SHIFT 4 #define IEEE80211_SEQ_MAX 4096 #define IEEE80211_WEP_IVLEN 3 #define IEEE80211_WEP_KIDLEN 1 #define IEEE80211_WEP_CRCLEN 4 #define IEEE80211_MAX_MPDU_LEN (3840 + FCS_LEN + \ (IEEE80211_WEP_IVLEN + \ IEEE80211_WEP_KIDLEN + \ IEEE80211_WEP_CRCLEN)) /* return whether a bit at index _n in bitmap _bm is set * _sz is the size of the bitmap */ #define ATH_BA_ISSET(_bm, _n) (((_n) < (WME_BA_BMP_SIZE)) && \ ((_bm)[(_n) >> 5] & (1 << ((_n) & 31)))) /* return block-ack bitmap index given sequence and starting sequence */ #define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_MAX - 1)) /* return the seqno for _start + _offset */ #define ATH_BA_INDEX2SEQ(_seq, _offset) (((_seq) + (_offset)) & (IEEE80211_SEQ_MAX - 1)) /* returns delimiter padding required given the packet length */ #define ATH_AGGR_GET_NDELIM(_len) \ (((_len) >= ATH_AGGR_MINPLEN) ? 0 : \ DIV_ROUND_UP(ATH_AGGR_MINPLEN - (_len), ATH_AGGR_DELIM_SZ)) #define BAW_WITHIN(_start, _bawsz, _seqno) \ ((((_seqno) - (_start)) & 4095) < (_bawsz)) #define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)]) #define ATH_TX_COMPLETE_POLL_INT 1000 enum ATH_AGGR_STATUS { ATH_AGGR_DONE, ATH_AGGR_BAW_CLOSED, ATH_AGGR_LIMITED, }; #define ATH_TXFIFO_DEPTH 8 struct ath_txq { int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */ u32 axq_qnum; /* ath9k hardware queue number */ void *axq_link; struct list_head axq_q; spinlock_t axq_lock; u32 axq_depth; u32 axq_ampdu_depth; bool stopped; bool axq_tx_inprogress; struct list_head axq_acq; struct list_head txq_fifo[ATH_TXFIFO_DEPTH]; u8 txq_headidx; u8 txq_tailidx; int pending_frames; struct sk_buff_head complete_q; }; struct ath_atx_ac { struct ath_txq *txq; int sched; struct list_head list; struct list_head tid_q; bool clear_ps_filter; }; struct ath_frame_info { struct ath_buf *bf; int framelen; enum ath9k_key_type keytype; u8 keyix; u8 retries; u8 rtscts_rate; }; struct ath_buf_state { u8 bf_type; u8 bfs_paprd; u8 ndelim; u16 seqno; unsigned long bfs_paprd_timestamp; }; struct ath_buf { struct list_head list; struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or an aggregate) */ struct ath_buf *bf_next; /* next subframe in the aggregate */ struct sk_buff *bf_mpdu; /* enclosing frame structure */ void *bf_desc; /* virtual addr of desc */ dma_addr_t bf_daddr; /* physical addr of desc */ dma_addr_t bf_buf_addr; /* physical addr of data buffer, for DMA */ bool bf_stale; struct ath_buf_state bf_state; }; struct ath_atx_tid { struct list_head list; struct sk_buff_head buf_q; struct ath_node *an; struct ath_atx_ac *ac; unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)]; int bar_index; u16 seq_start; u16 seq_next; u16 baw_size; int tidno; int baw_head; /* first un-acked tx buffer */ int baw_tail; /* next unused tx buffer slot */ int sched; int paused; u8 state; }; struct ath_node { #ifdef CONFIG_ATH9K_DEBUGFS struct list_head list; /* for sc->nodes */ #endif struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ struct ath_atx_tid tid[WME_NUM_TID]; struct ath_atx_ac ac[WME_NUM_AC]; int ps_key; u16 maxampdu; u8 mpdudensity; bool sleeping; }; #define AGGR_CLEANUP BIT(1) #define AGGR_ADDBA_COMPLETE BIT(2) #define AGGR_ADDBA_PROGRESS BIT(3) struct ath_tx_control { struct ath_txq *txq; struct ath_node *an; u8 paprd; struct ieee80211_sta *sta; }; #define ATH_TX_ERROR 0x01 /** * @txq_map: Index is mac80211 queue number. This is * not necessarily the same as the hardware queue number * (axq_qnum). */ struct ath_tx { u16 seq_no; u32 txqsetup; spinlock_t txbuflock; struct list_head txbuf; struct ath_txq txq[ATH9K_NUM_TX_QUEUES]; struct ath_descdma txdma; struct ath_txq *txq_map[WME_NUM_AC]; u32 txq_max_pending[WME_NUM_AC]; u16 max_aggr_framelen[WME_NUM_AC][4][32]; }; struct ath_rx_edma { struct sk_buff_head rx_fifo; u32 rx_fifo_hwsize; }; struct ath_rx { u8 defant; u8 rxotherant; u32 *rxlink; u32 num_pkts; unsigned int rxfilter; spinlock_t rxbuflock; struct list_head rxbuf; struct ath_descdma rxdma; struct ath_buf *rx_bufptr; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; struct sk_buff *frag; }; int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); void ath_flushrecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); int ath_rx_init(struct ath_softc *sc, int nbufs); void ath_rx_cleanup(struct ath_softc *sc); int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp); struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype); void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq); void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq); void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq); void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx); void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx); void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an); void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); int ath_tx_init(struct ath_softc *sc, int nbufs); void ath_tx_cleanup(struct ath_softc *sc); int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_tx_queue_info *q); void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop); int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_tx_control *txctl); void ath_tx_tasklet(struct ath_softc *sc); void ath_tx_edma_tasklet(struct ath_softc *sc); int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn); void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an); void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, struct ath_node *an); /********/ /* VIFs */ /********/ struct ath_vif { int av_bslot; bool primary_sta_vif; __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ struct ath_buf *av_bcbuf; }; /*******************/ /* Beacon Handling */ /*******************/ /* * Regardless of the number of beacons we stagger, (i.e. regardless of the * number of BSSIDs) if a given beacon does not go out even after waiting this * number of beacon intervals, the game's up. */ #define BSTUCK_THRESH 9 #define ATH_BCBUF 8 #define ATH_DEFAULT_BINTVAL 100 /* TU */ #define ATH_DEFAULT_BMISS_LIMIT 10 #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) struct ath_beacon_config { int beacon_interval; u16 listen_interval; u16 dtim_period; u16 bmiss_timeout; u8 dtim_count; bool enable_beacon; }; struct ath_beacon { enum { OK, /* no change needed */ UPDATE, /* update pending */ COMMIT /* beacon sent, commit change */ } updateslot; /* slot time update fsm */ u32 beaconq; u32 bmisscnt; u32 bc_tstamp; struct ieee80211_vif *bslot[ATH_BCBUF]; int slottime; int slotupdate; struct ath9k_tx_queue_info beacon_qi; struct ath_descdma bdma; struct ath_txq *cabq; struct list_head bbuf; bool tx_processed; bool tx_last; }; void ath9k_beacon_tasklet(unsigned long data); bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, u32 changed); void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_set_beacon(struct ath_softc *sc); /*******************/ /* Link Monitoring */ /*******************/ #define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */ #define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ #define ATH_ANI_POLLINTERVAL_OLD 100 /* 100 ms */ #define ATH_ANI_POLLINTERVAL_NEW 1000 /* 1000 ms */ #define ATH_LONG_CALINTERVAL_INT 1000 /* 1000 ms */ #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ #define ATH_PAPRD_TIMEOUT 100 /* msecs */ #define ATH_PLL_WORK_INTERVAL 100 void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); void ath_hw_check(struct work_struct *work); void ath_hw_pll_work(struct work_struct *work); void ath_rx_poll(unsigned long data); void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon); void ath_paprd_calibrate(struct work_struct *work); void ath_ani_calibrate(unsigned long data); void ath_start_ani(struct ath_softc *sc); void ath_stop_ani(struct ath_softc *sc); void ath_check_ani(struct ath_softc *sc); int ath_update_survey_stats(struct ath_softc *sc); void ath_update_survey_nf(struct ath_softc *sc, int channel); void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); /**********/ /* BTCOEX */ /**********/ enum bt_op_flags { BT_OP_PRIORITY_DETECTED, BT_OP_SCAN, }; struct ath_btcoex { bool hw_timer_enabled; spinlock_t btcoex_lock; struct timer_list period_timer; /* Timer for BT period */ u32 bt_priority_cnt; unsigned long bt_priority_time; unsigned long op_flags; int bt_stomp_type; /* Types of BT stomping */ u32 btcoex_no_stomp; /* in usec */ u32 btcoex_period; /* in msec */ u32 btscan_no_stomp; /* in usec */ u32 duty_cycle; u32 bt_wait_time; struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ struct ath_mci_profile mci; }; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT int ath9k_init_btcoex(struct ath_softc *sc); void ath9k_deinit_btcoex(struct ath_softc *sc); void ath9k_start_btcoex(struct ath_softc *sc); void ath9k_stop_btcoex(struct ath_softc *sc); void ath9k_btcoex_timer_resume(struct ath_softc *sc); void ath9k_btcoex_timer_pause(struct ath_softc *sc); void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status); u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen); void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc); #else static inline int ath9k_init_btcoex(struct ath_softc *sc) { return 0; } static inline void ath9k_deinit_btcoex(struct ath_softc *sc) { } static inline void ath9k_start_btcoex(struct ath_softc *sc) { } static inline void ath9k_stop_btcoex(struct ath_softc *sc) { } static inline void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status) { } static inline u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) { return 0; } static inline void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ struct ath9k_wow_pattern { u8 pattern_bytes[MAX_PATTERN_SIZE]; u8 mask_bytes[MAX_PATTERN_SIZE]; u32 pattern_len; }; /********************/ /* LED Control */ /********************/ #define ATH_LED_PIN_DEF 1 #define ATH_LED_PIN_9287 8 #define ATH_LED_PIN_9300 10 #define ATH_LED_PIN_9485 6 #define ATH_LED_PIN_9462 4 #ifdef CONFIG_MAC80211_LEDS void ath_init_leds(struct ath_softc *sc); void ath_deinit_leds(struct ath_softc *sc); #else static inline void ath_init_leds(struct ath_softc *sc) { } static inline void ath_deinit_leds(struct ath_softc *sc) { } #endif /*******************************/ /* Antenna diversity/combining */ /*******************************/ #define ATH_ANT_RX_CURRENT_SHIFT 4 #define ATH_ANT_RX_MAIN_SHIFT 2 #define ATH_ANT_RX_MASK 0x3 #define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50 #define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100 #define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200 #define ATH_ANT_DIV_COMB_INIT_COUNT 95 #define ATH_ANT_DIV_COMB_MAX_COUNT 100 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20 #define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1 #define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4 #define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2 #define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2 enum ath9k_ant_div_comb_lna_conf { ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2, ATH_ANT_DIV_COMB_LNA2, ATH_ANT_DIV_COMB_LNA1, ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2, }; struct ath_ant_comb { u16 count; u16 total_pkt_count; bool scan; bool scan_not_start; int main_total_rssi; int alt_total_rssi; int alt_recv_cnt; int main_recv_cnt; int rssi_lna1; int rssi_lna2; int rssi_add; int rssi_sub; int rssi_first; int rssi_second; int rssi_third; bool alt_good; int quick_scan_cnt; int main_conf; enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf; enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf; int first_bias; int second_bias; bool first_ratio; bool second_ratio; unsigned long scan_start_time; }; void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); void ath_ant_comb_update(struct ath_softc *sc); /********************/ /* Main driver core */ /********************/ /* * Default cache line size, in bytes. * Used when PCI device not fully initialized by bootrom/BIOS */ #define DEFAULT_CACHELINE 32 #define ATH_REGCLASSIDS_MAX 10 #define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ #define ATH_MAX_SW_RETRIES 30 #define ATH_CHAN_MAX 255 #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ #define ATH_RATE_DUMMY_MARKER 0 enum sc_op_flags { SC_OP_INVALID, SC_OP_BEACONS, SC_OP_RXFLUSH, SC_OP_ANI_RUN, SC_OP_PRIM_STA_VIF, SC_OP_HW_RESET, }; /* Powersave flags */ #define PS_WAIT_FOR_BEACON BIT(0) #define PS_WAIT_FOR_CAB BIT(1) #define PS_WAIT_FOR_PSPOLL_DATA BIT(2) #define PS_WAIT_FOR_TX_ACK BIT(3) #define PS_BEACON_SYNC BIT(4) struct ath_rate_table; struct ath9k_vif_iter_data { const u8 *hw_macaddr; /* phy's hardware address, set * before starting iteration for * valid bssid mask. */ u8 mask[ETH_ALEN]; /* bssid mask */ int naps; /* number of AP vifs */ int nmeshes; /* number of mesh vifs */ int nstations; /* number of station vifs */ int nwds; /* number of WDS vifs */ int nadhocs; /* number of adhoc vifs */ }; struct ath_softc { struct ieee80211_hw *hw; struct device *dev; struct survey_info *cur_survey; struct survey_info survey[ATH9K_NUM_CHANNELS]; struct tasklet_struct intr_tq; struct tasklet_struct bcon_tasklet; struct ath_hw *sc_ah; void __iomem *mem; int irq; spinlock_t sc_serial_rw; spinlock_t sc_pm_lock; spinlock_t sc_pcu_lock; struct mutex mutex; struct work_struct paprd_work; struct work_struct hw_check_work; struct work_struct hw_reset_work; struct completion paprd_complete; unsigned int hw_busy_count; unsigned long sc_flags; u32 intrstatus; u16 ps_flags; /* PS_* */ u16 curtxpow; bool ps_enabled; bool ps_idle; short nbcnvifs; short nvifs; unsigned long ps_usecount; struct ath_config config; struct ath_rx rx; struct ath_tx tx; struct ath_beacon beacon; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; #ifdef CONFIG_MAC80211_LEDS bool led_registered; char led_name[32]; struct led_classdev led_cdev; #endif struct ath9k_hw_cal_data caldata; int last_rssi; #ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; spinlock_t nodes_lock; struct list_head nodes; /* basically, stations */ unsigned int tx_complete_poll_work_seen; #endif struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; struct timer_list rx_poll_timer; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; struct ath_mci_coex mci_coex; struct work_struct mci_work; #endif struct ath_descdma txsdma; struct ath_ant_comb ant_comb; u8 ant_tx, ant_rx; struct dfs_pattern_detector *dfs_detector; u32 wow_enabled; #ifdef CONFIG_PM_SLEEP atomic_t wow_got_bmiss_intr; atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */ u32 wow_intr_before_sleep; #endif }; void ath9k_tasklet(unsigned long data); int ath_cabq_update(struct ath_softc *); static inline void ath_read_cachesize(struct ath_common *common, int *csz) { common->bus_ops->read_cachesize(common, csz); } extern struct ieee80211_ops ath9k_ops; extern int ath9k_modparam_nohwcrypt; extern int led_blink; extern bool is_ath9k_unloaded; u8 ath9k_parse_mpdudensity(u8 mpdudensity); irqreturn_t ath_isr(int irq, void *dev); int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops); void ath9k_deinit_device(struct ath_softc *sc); void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_reload_chainmask_settings(struct ath_softc *sc); bool ath9k_uses_beacons(int type); #ifdef CONFIG_ATH9K_PCI int ath_pci_init(void); void ath_pci_exit(void); #else static inline int ath_pci_init(void) { return 0; }; static inline void ath_pci_exit(void) {}; #endif #ifdef CONFIG_ATH9K_AHB int ath_ahb_init(void); void ath_ahb_exit(void); #else static inline int ath_ahb_init(void) { return 0; }; static inline void ath_ahb_exit(void) {}; #endif void ath9k_ps_wakeup(struct ath_softc *sc); void ath9k_ps_restore(struct ath_softc *sc); u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate); void ath_start_rfkill_poll(struct ath_softc *sc); extern void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); void ath9k_calculate_iter_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ath9k_vif_iter_data *iter_data); #endif /* ATH9K_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h0000644000175000017500000011316112026211315025752 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9580_1P0_H #define INITVALS_9580_1P0_H /* AR9580 1.0 */ #define ar9580_1p0_modes_fast_clock ar9300Modes_fast_clock_2p2 static const u32 ar9580_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31}, {0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800}, {0x000160b0, 0x03284f3e, 0x03284f3e, 0x05d08f20, 0x05d08f20}, {0x0001610c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, {0x0001650c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, {0x0001690c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; static const u32 ar9580_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a9f6b}, {0x0000980c, 0x04900000}, {0x00009814, 0x3280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x6400a290}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x0d000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009c04, 0xff55ff55}, {0x00009c08, 0x0320ff55}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, {0x00009e24, 0x990bb515}, {0x00009e28, 0x0c6f0000}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0x00000000}, {0x00009fc0, 0x803e4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x00009fd0, 0x01193b93}, {0x0000a20c, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, {0x0000a2c0, 0x00000001}, {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0xaaaaaaaa}, {0x0000a3ac, 0x3c466478}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000000}, {0x0000a3f8, 0x0c9bd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00100000}, {0x0000a440, 0x00000000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x05000080}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a458, 0x00000000}, {0x0000a640, 0x00000000}, {0x0000a644, 0x3fad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, {0x0000a67c, 0x13121110}, {0x0000a680, 0x31301514}, {0x0000a684, 0x35343332}, {0x0000a688, 0x00000036}, {0x0000a690, 0x00000838}, {0x0000a7c0, 0x00000000}, {0x0000a7c4, 0xfffffffc}, {0x0000a7c8, 0x00000000}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000000}, {0x0000a8d0, 0x004b6a8e}, {0x0000a8d4, 0x00000820}, {0x0000a8dc, 0x00000000}, {0x0000a8f0, 0x00000000}, {0x0000a8f4, 0x00000000}, {0x0000b2d0, 0x00000080}, {0x0000b2d4, 0x00000000}, {0x0000b2ec, 0x00000000}, {0x0000b2f0, 0x00000000}, {0x0000b2f4, 0x00000000}, {0x0000b2f8, 0x00000000}, {0x0000b408, 0x0e79e5c0}, {0x0000b40c, 0x00820820}, {0x0000b420, 0x00000000}, {0x0000b8d0, 0x004b6a8e}, {0x0000b8d4, 0x00000820}, {0x0000b8dc, 0x00000000}, {0x0000b8f0, 0x00000000}, {0x0000b8f4, 0x00000000}, {0x0000c2d0, 0x00000080}, {0x0000c2d4, 0x00000000}, {0x0000c2ec, 0x00000000}, {0x0000c2f0, 0x00000000}, {0x0000c2f4, 0x00000000}, {0x0000c2f8, 0x00000000}, {0x0000c408, 0x0e79e5c0}, {0x0000c40c, 0x00820820}, {0x0000c420, 0x00000000}, }; #define ar9580_1p0_mac_postamble ar9300_2p2_mac_postamble static const u32 ar9580_1p0_low_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83}, {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402}, {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861}, {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81}, {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x47801a83, 0x47801a83}, {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x4a801c84, 0x4a801c84}, {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x4e801ce3, 0x4e801ce3}, {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x52801ce5, 0x52801ce5}, {0x0000a5dc, 0x7082708c, 0x7082708c, 0x56801ce9, 0x56801ce9}, {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x5a801ceb, 0x5a801ceb}, {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; #define ar9580_1p0_high_power_tx_gain_table ar9580_1p0_low_ob_db_tx_gain_table static const u32 ar9580_1p0_lowest_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83}, {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402}, {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861}, {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81}, {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x47801a83, 0x47801a83}, {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x4a801c84, 0x4a801c84}, {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x4e801ce3, 0x4e801ce3}, {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x52801ce5, 0x52801ce5}, {0x0000a5dc, 0x7082708c, 0x7082708c, 0x56801ce9, 0x56801ce9}, {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x5a801ceb, 0x5a801ceb}, {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016048, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016448, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016848, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; #define ar9580_1p0_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 static const u32 ar9580_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c22}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00a00000}, {0x000080d8, 0x00400000}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x000081c0, 0x00000000}, {0x000081c4, 0x33332210}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000800}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x9bc00010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000140}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xaa48105b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a0, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x000301ff}, }; static const u32 ar9580_1p0_mixed_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x11000400, 0x11000400}, {0x0000a518, 0x21002220, 0x21002220, 0x15000402, 0x15000402}, {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000603, 0x1b000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x23000a04, 0x23000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x26000a20, 0x26000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2a000e20, 0x2a000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x2e000e22, 0x2e000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x31000e24, 0x31000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x34001640, 0x34001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38001660, 0x38001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3b001861, 0x3b001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x3e001a81, 0x3e001a81}, {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x42001a83, 0x42001a83}, {0x0000a550, 0x61024a6c, 0x61024a6c, 0x44001c84, 0x44001c84}, {0x0000a554, 0x66026a6c, 0x66026a6c, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x7002708c, 0x7002708c, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x7302b08a, 0x7302b08a, 0x54001ceb, 0x54001ceb}, {0x0000a564, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a568, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a570, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a574, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a578, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x11800400, 0x11800400}, {0x0000a598, 0x21802220, 0x21802220, 0x15800402, 0x15800402}, {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800603, 0x1b800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800a02, 0x1f800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x23800a04, 0x23800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x26800a20, 0x26800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2a800e20, 0x2a800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2e800e22, 0x2e800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x31800e24, 0x31800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x34801640, 0x34801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38801660, 0x38801660}, {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3b801861, 0x3b801861}, {0x0000a5c8, 0x5782286c, 0x5782286c, 0x3e801a81, 0x3e801a81}, {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x42801a83, 0x42801a83}, {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x44801c84, 0x44801c84}, {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x48801ce3, 0x48801ce3}, {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x4c801ce5, 0x4c801ce5}, {0x0000a5dc, 0x7082708c, 0x7082708c, 0x50801ce9, 0x50801ce9}, {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x54801ceb, 0x54801ceb}, {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec}, {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec}, {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec}, {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec}, {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x56801eec, 0x56801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4}, {0x00016048, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4}, {0x00016448, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4}, {0x00016848, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; #define ar9580_1p0_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2 #define ar9580_1p0_soc_postamble ar9300_2p2_soc_postamble #define ar9580_1p0_high_ob_db_tx_gain_table ar9300Modes_high_ob_db_tx_gain_table_2p2 static const u32 ar9580_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, {0x00007008, 0x00000000}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00007048, 0x00000008}, }; #define ar9580_1p0_rx_gain_table ar9462_common_rx_gain_table_2p0 static const u32 ar9580_1p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73f00000}, {0x0001600c, 0x00000000}, {0x00016040, 0x7f80fff8}, {0x0001604c, 0x76d005b5}, {0x00016050, 0x556cf031}, {0x00016054, 0x13449440}, {0x00016058, 0x0c51c92c}, {0x0001605c, 0x3db7fffc}, {0x00016060, 0xfffffffc}, {0x00016064, 0x000f0278}, {0x0001606c, 0x6db60000}, {0x00016080, 0x00000000}, {0x00016084, 0x0e48048c}, {0x00016088, 0x54214514}, {0x0001608c, 0x119f481e}, {0x00016090, 0x24926490}, {0x00016098, 0xd2888888}, {0x000160a0, 0x0a108ffe}, {0x000160a4, 0x812fc370}, {0x000160a8, 0x423c8000}, {0x000160b4, 0x92480080}, {0x000160c0, 0x00adb6d0}, {0x000160c4, 0x6db6db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x01e6c000}, {0x00016100, 0x3fffbe01}, {0x00016104, 0xfff80000}, {0x00016108, 0x00080010}, {0x00016144, 0x02084080}, {0x00016148, 0x00000000}, {0x00016280, 0x058a0001}, {0x00016284, 0x3d840208}, {0x00016288, 0x05a20408}, {0x0001628c, 0x00038c07}, {0x00016290, 0x00000004}, {0x00016294, 0x458aa14f}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00800700}, {0x0001638c, 0x00800700}, {0x00016390, 0x00800700}, {0x00016394, 0x00000000}, {0x00016398, 0x00000000}, {0x0001639c, 0x00000000}, {0x000163a0, 0x00000001}, {0x000163a4, 0x00000001}, {0x000163a8, 0x00000000}, {0x000163ac, 0x00000000}, {0x000163b0, 0x00000000}, {0x000163b4, 0x00000000}, {0x000163b8, 0x00000000}, {0x000163bc, 0x00000000}, {0x000163c0, 0x000000a0}, {0x000163c4, 0x000c0000}, {0x000163c8, 0x14021402}, {0x000163cc, 0x00001402}, {0x000163d0, 0x00000000}, {0x000163d4, 0x00000000}, {0x00016400, 0x36db6db6}, {0x00016404, 0x6db6db40}, {0x00016408, 0x73f00000}, {0x0001640c, 0x00000000}, {0x00016440, 0x7f80fff8}, {0x0001644c, 0x76d005b5}, {0x00016450, 0x556cf031}, {0x00016454, 0x13449440}, {0x00016458, 0x0c51c92c}, {0x0001645c, 0x3db7fffc}, {0x00016460, 0xfffffffc}, {0x00016464, 0x000f0278}, {0x0001646c, 0x6db60000}, {0x00016500, 0x3fffbe01}, {0x00016504, 0xfff80000}, {0x00016508, 0x00080010}, {0x00016544, 0x02084080}, {0x00016548, 0x00000000}, {0x00016780, 0x00000000}, {0x00016784, 0x00000000}, {0x00016788, 0x00800700}, {0x0001678c, 0x00800700}, {0x00016790, 0x00800700}, {0x00016794, 0x00000000}, {0x00016798, 0x00000000}, {0x0001679c, 0x00000000}, {0x000167a0, 0x00000001}, {0x000167a4, 0x00000001}, {0x000167a8, 0x00000000}, {0x000167ac, 0x00000000}, {0x000167b0, 0x00000000}, {0x000167b4, 0x00000000}, {0x000167b8, 0x00000000}, {0x000167bc, 0x00000000}, {0x000167c0, 0x000000a0}, {0x000167c4, 0x000c0000}, {0x000167c8, 0x14021402}, {0x000167cc, 0x00001402}, {0x000167d0, 0x00000000}, {0x000167d4, 0x00000000}, {0x00016800, 0x36db6db6}, {0x00016804, 0x6db6db40}, {0x00016808, 0x73f00000}, {0x0001680c, 0x00000000}, {0x00016840, 0x7f80fff8}, {0x0001684c, 0x76d005b5}, {0x00016850, 0x556cf031}, {0x00016854, 0x13449440}, {0x00016858, 0x0c51c92c}, {0x0001685c, 0x3db7fffc}, {0x00016860, 0xfffffffc}, {0x00016864, 0x000f0278}, {0x0001686c, 0x6db60000}, {0x00016900, 0x3fffbe01}, {0x00016904, 0xfff80000}, {0x00016908, 0x00080010}, {0x00016944, 0x02084080}, {0x00016948, 0x00000000}, {0x00016b80, 0x00000000}, {0x00016b84, 0x00000000}, {0x00016b88, 0x00800700}, {0x00016b8c, 0x00800700}, {0x00016b90, 0x00800700}, {0x00016b94, 0x00000000}, {0x00016b98, 0x00000000}, {0x00016b9c, 0x00000000}, {0x00016ba0, 0x00000001}, {0x00016ba4, 0x00000001}, {0x00016ba8, 0x00000000}, {0x00016bac, 0x00000000}, {0x00016bb0, 0x00000000}, {0x00016bb4, 0x00000000}, {0x00016bb8, 0x00000000}, {0x00016bbc, 0x00000000}, {0x00016bc0, 0x000000a0}, {0x00016bc4, 0x000c0000}, {0x00016bc8, 0x14021402}, {0x00016bcc, 0x00001402}, {0x00016bd0, 0x00000000}, {0x00016bd4, 0x00000000}, }; static const u32 ar9580_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x000036c0, 0x000036c4, 0x000036c4, 0x000036c0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000b284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000b830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000be04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000be1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000be20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000c284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, }; static const u32 ar9580_1p0_pcie_phy_clkreq_enable_L1[][2] = { /* Addr allmodes */ {0x00004040, 0x0835365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; static const u32 ar9580_1p0_pcie_phy_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00004040, 0x0831365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; static const u32 ar9580_1p0_pcie_phy_pll_on_clkreq[][2] = { /* Addr allmodes */ {0x00004040, 0x0831265e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; #endif /* INITVALS_9580_1P0_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h0000644000175000017500000012543512026211315025764 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9565_1P0_H #define INITVALS_9565_1P0_H /* AR9565 1.0 */ static const u32 ar9565_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x000a0085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00001810, 0x0f000003}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008050, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c20}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00b00005}, {0x000080d8, 0x00400002}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x18486200}, {0x00008174, 0x33332210}, {0x00008178, 0x00000000}, {0x0000817c, 0x00020000}, {0x000081c4, 0x33332210}, {0x000081c8, 0x00000000}, {0x000081cc, 0x00000000}, {0x000081d4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f424}, {0x00008248, 0x00000800}, {0x0000824c, 0x0001e848}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x9d400010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000140}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x0000001f}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0xffff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xaa48105b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x800301ff}, }; static const u32 ar9565_1p0_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; static const u32 ar9565_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a8f6b}, {0x0000980c, 0x04800000}, {0x00009814, 0x9280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x6400a290}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x0d000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098e4, 0x01ffffff}, {0x000098e8, 0x01ffffff}, {0x000098ec, 0x01ffffff}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009bf0, 0x80000000}, {0x00009c04, 0xff55ff55}, {0x00009c08, 0x0320ff55}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x1883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c00400}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0078230c}, {0x00009e24, 0x990bb515}, {0x00009e28, 0x126f0000}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0xe4c355c7}, {0x00009e5c, 0xe9198724}, {0x00009fc0, 0x823e4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x0000a20c, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, {0x0000a2c0, 0x00000001}, {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0xaaaaaaaa}, {0x0000a3ac, 0x3c466478}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000006}, {0x0000a3f8, 0x0cdbd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00000000}, {0x0000a440, 0x00000000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x05000096}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a454, 0x03000000}, {0x0000a458, 0x00000000}, {0x0000a644, 0xbfad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, {0x0000a67c, 0x13121110}, {0x0000a680, 0x31301514}, {0x0000a684, 0x35343332}, {0x0000a688, 0x00000036}, {0x0000a690, 0x00000838}, {0x0000a6b4, 0x00512c01}, {0x0000a7c0, 0x00000000}, {0x0000a7c4, 0xfffffffc}, {0x0000a7c8, 0x00000000}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000001}, {0x0000a7f0, 0x80000000}, }; static const u32 ar9565_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a800d}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x63c640da}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x09143c81}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e}, {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x033187c0, 0x033187c4, 0x033187c4, 0x033187c0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00100510, 0x00100510, 0x00100510, 0x00100510}, {0x0000a28c, 0x00021551, 0x00021551, 0x00021551, 0x00021551}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982}, {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae04, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar9565_1p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73f00000}, {0x0001600c, 0x00000000}, {0x00016010, 0x6d823601}, {0x00016040, 0x7f80fff8}, {0x0001604c, 0x1c99e04f}, {0x00016050, 0x6db6db6c}, {0x00016058, 0x6c200000}, {0x00016080, 0x000c0000}, {0x00016084, 0x9a68048c}, {0x00016088, 0x54214514}, {0x0001608c, 0x1203040b}, {0x00016090, 0x24926490}, {0x00016098, 0xd28b3330}, {0x000160a0, 0x0a108ffe}, {0x000160a4, 0x812fc491}, {0x000160a8, 0x423c8000}, {0x000160b4, 0x92000000}, {0x000160b8, 0x0285dddc}, {0x000160bc, 0x02908888}, {0x000160c0, 0x006db6d0}, {0x000160c4, 0x6dd6db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x6de6c1b0}, {0x00016100, 0x3fffbe04}, {0x00016104, 0xfff80000}, {0x00016108, 0x00200400}, {0x00016110, 0x00000000}, {0x00016144, 0x02084080}, {0x00016148, 0x000080c0}, {0x00016280, 0x050a0001}, {0x00016284, 0x3d841400}, {0x00016288, 0x00000000}, {0x0001628c, 0xe3000000}, {0x00016290, 0xa1004080}, {0x00016294, 0x40000028}, {0x00016298, 0x55aa2900}, {0x00016340, 0x131c827a}, {0x00016344, 0x00300000}, }; static const u32 ar9565_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524}, {0x000160ac, 0xa4646c08, 0xa4646c08, 0xa4646c08, 0xa4646c08}, {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70}, {0x0001610c, 0x40000000, 0x40000000, 0x40000000, 0x40000000}, {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; static const u32 ar9565_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x00004078, 0x00000002}, {0x000040a4, 0x00a0c9c9}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, }; static const u32 ar9565_1p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00002233, 0x00002233, 0x00002233, 0x00002233}, }; static const u32 ar9565_1p0_Common_rx_gain_table[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x01910190}, {0x0000a030, 0x01930192}, {0x0000a034, 0x01950194}, {0x0000a038, 0x038a0196}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x22222229}, {0x0000a084, 0x1d1d1d1d}, {0x0000a088, 0x1d1d1d1d}, {0x0000a08c, 0x1d1d1d1d}, {0x0000a090, 0x171d1d1d}, {0x0000a094, 0x11111717}, {0x0000a098, 0x00030311}, {0x0000a09c, 0x00000000}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x32323232}, {0x0000b084, 0x2f2f3232}, {0x0000b088, 0x23282a2d}, {0x0000b08c, 0x1c1e2123}, {0x0000b090, 0x14171919}, {0x0000b094, 0x0e0e1214}, {0x0000b098, 0x03050707}, {0x0000b09c, 0x00030303}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar9565_1p0_Modes_lowest_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0xfc0a9380, 0xfc0a9380, 0xfdab5b52, 0xfdab5b52}, {0x0000a2e0, 0xffecec00, 0xffecec00, 0xfd339c84, 0xfd339c84}, {0x0000a2e4, 0xfc0f0000, 0xfc0f0000, 0xfec3e000, 0xfec3e000}, {0x0000a2e8, 0xfc100000, 0xfc100000, 0xfffc0000, 0xfffc0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a618, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a61c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a620, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a624, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a628, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a62c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a630, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a634, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a638, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a63c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, {0x00016048, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016054, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar9565_1p0_pciephy_pll_on_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x18212ede}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0003780c}, }; static const u32 ar9565_1p0_modes_fast_clock[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, {0x000010b0, 0x00000fd0, 0x00001fa0}, {0x00008014, 0x044c044c, 0x08980898}, {0x0000801c, 0x148ec02b, 0x148ec057}, {0x00008318, 0x000044c0, 0x00008980}, {0x00009e00, 0x03721821, 0x03721821}, {0x0000a230, 0x0000400b, 0x00004016}, {0x0000a254, 0x00000898, 0x00001130}, }; static const u32 ar9565_1p0_common_wo_xlna_rx_gain_table[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x03820190}, {0x0000a030, 0x03840383}, {0x0000a034, 0x03880385}, {0x0000a038, 0x038a0389}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x29292929}, {0x0000a084, 0x29292929}, {0x0000a088, 0x29292929}, {0x0000a08c, 0x29292929}, {0x0000a090, 0x22292929}, {0x0000a094, 0x1d1d2222}, {0x0000a098, 0x0c111117}, {0x0000a09c, 0x00030303}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x301f3000}, {0x0000a0c4, 0x41004101}, {0x0000a0c8, 0x411e411f}, {0x0000a0cc, 0x411c411d}, {0x0000a0d0, 0x42434244}, {0x0000a0d4, 0x42414242}, {0x0000a0d8, 0x425f4240}, {0x0000a0dc, 0x5342425e}, {0x0000a0e0, 0x53405341}, {0x0000a0e4, 0x535e535f}, {0x0000a0e8, 0x7402535d}, {0x0000a0ec, 0x74007401}, {0x0000a0f0, 0x741e741f}, {0x0000a0f4, 0x7522741d}, {0x0000a0f8, 0x75207521}, {0x0000a0fc, 0x753e753f}, {0x0000a100, 0x76617662}, {0x0000a104, 0x767f7660}, {0x0000a108, 0x767d767e}, {0x0000a10c, 0x77e277e3}, {0x0000a110, 0x77e077e1}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x301f3000}, {0x0000a144, 0x41004101}, {0x0000a148, 0x411e411f}, {0x0000a14c, 0x411c411d}, {0x0000a150, 0x42434244}, {0x0000a154, 0x42414242}, {0x0000a158, 0x425f4240}, {0x0000a15c, 0x5342425e}, {0x0000a160, 0x53405341}, {0x0000a164, 0x535e535f}, {0x0000a168, 0x7402535d}, {0x0000a16c, 0x74007401}, {0x0000a170, 0x741e741f}, {0x0000a174, 0x7522741d}, {0x0000a178, 0x75207521}, {0x0000a17c, 0x753e753f}, {0x0000a180, 0x76617662}, {0x0000a184, 0x767f7660}, {0x0000a188, 0x767d767e}, {0x0000a18c, 0x77e277e3}, {0x0000a190, 0x77e077e1}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x32323232}, {0x0000b084, 0x2f2f3232}, {0x0000b088, 0x23282a2d}, {0x0000b08c, 0x1c1e2123}, {0x0000b090, 0x14171919}, {0x0000b094, 0x0e0e1214}, {0x0000b098, 0x03050707}, {0x0000b09c, 0x00030303}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar9565_1p0_modes_low_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0xfc0a9380, 0xfc0a9380, 0xfdab5b52, 0xfdab5b52}, {0x0000a2e0, 0xffecec00, 0xffecec00, 0xfd339c84, 0xfd339c84}, {0x0000a2e4, 0xfc0f0000, 0xfc0f0000, 0xfec3e000, 0xfec3e000}, {0x0000a2e8, 0xfc100000, 0xfc100000, 0xfffc0000, 0xfffc0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a618, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a61c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a620, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a624, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a628, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a62c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a630, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a634, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a638, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a63c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, {0x00016048, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016054, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar9565_1p0_modes_high_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0xfc0a9380, 0xfc0a9380, 0xfdab5b52, 0xfdab5b52}, {0x0000a2e0, 0xffecec00, 0xffecec00, 0xfd339c84, 0xfd339c84}, {0x0000a2e4, 0xfc0f0000, 0xfc0f0000, 0xfec3e000, 0xfec3e000}, {0x0000a2e8, 0xfc100000, 0xfc100000, 0xfffc0000, 0xfffc0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, {0x0000a508, 0x0b022220, 0x0b022220, 0x08000004, 0x08000004}, {0x0000a50c, 0x10022223, 0x10022223, 0x0c000200, 0x0c000200}, {0x0000a510, 0x15022620, 0x15022620, 0x10000202, 0x10000202}, {0x0000a514, 0x19022622, 0x19022622, 0x13000400, 0x13000400}, {0x0000a518, 0x1c022822, 0x1c022822, 0x17000402, 0x17000402}, {0x0000a51c, 0x21022842, 0x21022842, 0x1b000404, 0x1b000404}, {0x0000a520, 0x24022c41, 0x24022c41, 0x1e000603, 0x1e000603}, {0x0000a524, 0x29023042, 0x29023042, 0x23000a02, 0x23000a02}, {0x0000a528, 0x2d023044, 0x2d023044, 0x27000a04, 0x27000a04}, {0x0000a52c, 0x31023644, 0x31023644, 0x2a000a20, 0x2a000a20}, {0x0000a530, 0x36025643, 0x36025643, 0x2e000e20, 0x2e000e20}, {0x0000a534, 0x3a025a44, 0x3a025a44, 0x32000e22, 0x32000e22}, {0x0000a538, 0x3d025e45, 0x3d025e45, 0x36000e24, 0x36000e24}, {0x0000a53c, 0x43025e4a, 0x43025e4a, 0x3a001640, 0x3a001640}, {0x0000a540, 0x4a025e6c, 0x4a025e6c, 0x3e001660, 0x3e001660}, {0x0000a544, 0x50025e8e, 0x50025e8e, 0x41001861, 0x41001861}, {0x0000a548, 0x56025eb2, 0x56025eb2, 0x45001a81, 0x45001a81}, {0x0000a54c, 0x5c025eb5, 0x5c025eb5, 0x49001a83, 0x49001a83}, {0x0000a550, 0x62025ef6, 0x62025ef6, 0x4c001c84, 0x4c001c84}, {0x0000a554, 0x65025f56, 0x65025f56, 0x4f001ce3, 0x4f001ce3}, {0x0000a558, 0x69027f56, 0x69027f56, 0x53001ce5, 0x53001ce5}, {0x0000a55c, 0x6d029f56, 0x6d029f56, 0x57001ce9, 0x57001ce9}, {0x0000a560, 0x73049f56, 0x73049f56, 0x5b001ceb, 0x5b001ceb}, {0x0000a564, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, {0x0000a610, 0x00804201, 0x00804201, 0x00000000, 0x00000000}, {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, {0x0000a618, 0x00804201, 0x00804201, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008201, 0x02008201, 0x02008501, 0x02008501}, {0x0000a620, 0x02c10a03, 0x02c10a03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x04815205, 0x04815205, 0x02c10b04, 0x02c10b04}, {0x0000a628, 0x0581d406, 0x0581d406, 0x03814b04, 0x03814b04}, {0x0000a62c, 0x0581d607, 0x0581d607, 0x05018e05, 0x05018e05}, {0x0000a630, 0x0581d607, 0x0581d607, 0x05019406, 0x05019406}, {0x0000a634, 0x0581d607, 0x0581d607, 0x05019406, 0x05019406}, {0x0000a638, 0x0581d607, 0x0581d607, 0x05019406, 0x05019406}, {0x0000a63c, 0x0581d607, 0x0581d607, 0x05019406, 0x05019406}, {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060}, {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, }; static const u32 ar9565_1p0_modes_high_power_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0xfc0a9380, 0xfc0a9380, 0xfdab5b52, 0xfdab5b52}, {0x0000a2e0, 0xffecec00, 0xffecec00, 0xfd339c84, 0xfd339c84}, {0x0000a2e4, 0xfc0f0000, 0xfc0f0000, 0xfec3e000, 0xfec3e000}, {0x0000a2e8, 0xfc100000, 0xfc100000, 0xfffc0000, 0xfffc0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400}, {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402}, {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603}, {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04}, {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20}, {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20}, {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22}, {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24}, {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640}, {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81}, {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83}, {0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84}, {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb}, {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a618, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a61c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a620, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a624, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a628, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a62c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a630, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a634, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a638, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a63c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016044, 0x056d82e6, 0x056d82e6, 0x056d82e6, 0x056d82e6}, {0x00016048, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016054, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; #endif /* INITVALS_9565_1P0_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h0000644000175000017500000014032212026211315026056 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_955X_1P0_H #define INITVALS_955X_1P0_H /* AR955X 1.0 */ static const u32 ar955x_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00016098, 0xd2dd5554, 0xd2dd5554, 0xd28b3330, 0xd28b3330}, {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x06345f2a, 0x06345f2a}, {0x000160ac, 0xa4647c00, 0xa4647c00, 0xa4646800, 0xa4646800}, {0x000160b0, 0x01885f52, 0x01885f52, 0x04accf3a, 0x04accf3a}, {0x00016104, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001}, {0x0001610c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016140, 0x10804008, 0x10804008, 0x10804008, 0x10804008}, {0x00016504, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001}, {0x0001650c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016540, 0x10804008, 0x10804008, 0x10804008, 0x10804008}, {0x00016904, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001}, {0x0001690c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016940, 0x10804008, 0x10804008, 0x10804008, 0x10804008}, }; static const u32 ar955x_1p0_baseband_core_txfir_coeff_japan_2484[][2] = { /* Addr allmodes */ {0x0000a398, 0x00000000}, {0x0000a39c, 0x6f7f0301}, {0x0000a3a0, 0xca9228ee}, }; static const u32 ar955x_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822}, {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33}, {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, {0x0000b830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000be04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000be1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000be20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000c284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, }; static const u32 ar955x_1p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73f00000}, {0x0001600c, 0x00000000}, {0x00016040, 0x7f80fff8}, {0x0001604c, 0x76d005b5}, {0x00016050, 0x557cf031}, {0x00016054, 0x13449440}, {0x00016058, 0x0c51c92c}, {0x0001605c, 0x3db7fffc}, {0x00016060, 0xfffffffc}, {0x00016064, 0x000f0278}, {0x00016068, 0x6db6db6c}, {0x0001606c, 0x6db60000}, {0x00016080, 0x00080000}, {0x00016084, 0x0e48048c}, {0x00016088, 0x14214514}, {0x0001608c, 0x119f101e}, {0x00016090, 0x24926490}, {0x00016094, 0x00000000}, {0x000160a0, 0x0a108ffe}, {0x000160a4, 0x812fc370}, {0x000160a8, 0x423c8000}, {0x000160b4, 0x92480080}, {0x000160c0, 0x006db6d0}, {0x000160c4, 0x6db6db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x01e6c000}, {0x00016100, 0x11999601}, {0x00016108, 0x00080010}, {0x00016144, 0x02084080}, {0x00016148, 0x000080c0}, {0x00016280, 0x01800804}, {0x00016284, 0x00038dc5}, {0x00016288, 0x00000000}, {0x0001628c, 0x00000040}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00400705}, {0x0001638c, 0x00800700}, {0x00016390, 0x00800700}, {0x00016394, 0x00000000}, {0x00016398, 0x00000000}, {0x0001639c, 0x00000000}, {0x000163a0, 0x00000001}, {0x000163a4, 0x00000001}, {0x000163a8, 0x00000000}, {0x000163ac, 0x00000000}, {0x000163b0, 0x00000000}, {0x000163b4, 0x00000000}, {0x000163b8, 0x00000000}, {0x000163bc, 0x00000000}, {0x000163c0, 0x000000a0}, {0x000163c4, 0x000c0000}, {0x000163c8, 0x14021402}, {0x000163cc, 0x00001402}, {0x000163d0, 0x00000000}, {0x000163d4, 0x00000000}, {0x00016400, 0x36db6db6}, {0x00016404, 0x6db6db40}, {0x00016408, 0x73f00000}, {0x0001640c, 0x00000000}, {0x00016440, 0x7f80fff8}, {0x0001644c, 0x76d005b5}, {0x00016450, 0x557cf031}, {0x00016454, 0x13449440}, {0x00016458, 0x0c51c92c}, {0x0001645c, 0x3db7fffc}, {0x00016460, 0xfffffffc}, {0x00016464, 0x000f0278}, {0x00016468, 0x6db6db6c}, {0x0001646c, 0x6db60000}, {0x00016500, 0x11999601}, {0x00016508, 0x00080010}, {0x00016544, 0x02084080}, {0x00016548, 0x000080c0}, {0x00016780, 0x00000000}, {0x00016784, 0x00000000}, {0x00016788, 0x00400705}, {0x0001678c, 0x00800700}, {0x00016790, 0x00800700}, {0x00016794, 0x00000000}, {0x00016798, 0x00000000}, {0x0001679c, 0x00000000}, {0x000167a0, 0x00000001}, {0x000167a4, 0x00000001}, {0x000167a8, 0x00000000}, {0x000167ac, 0x00000000}, {0x000167b0, 0x00000000}, {0x000167b4, 0x00000000}, {0x000167b8, 0x00000000}, {0x000167bc, 0x00000000}, {0x000167c0, 0x000000a0}, {0x000167c4, 0x000c0000}, {0x000167c8, 0x14021402}, {0x000167cc, 0x00001402}, {0x000167d0, 0x00000000}, {0x000167d4, 0x00000000}, {0x00016800, 0x36db6db6}, {0x00016804, 0x6db6db40}, {0x00016808, 0x73f00000}, {0x0001680c, 0x00000000}, {0x00016840, 0x7f80fff8}, {0x0001684c, 0x76d005b5}, {0x00016850, 0x557cf031}, {0x00016854, 0x13449440}, {0x00016858, 0x0c51c92c}, {0x0001685c, 0x3db7fffc}, {0x00016860, 0xfffffffc}, {0x00016864, 0x000f0278}, {0x00016868, 0x6db6db6c}, {0x0001686c, 0x6db60000}, {0x00016900, 0x11999601}, {0x00016908, 0x00080010}, {0x00016944, 0x02084080}, {0x00016948, 0x000080c0}, {0x00016b80, 0x00000000}, {0x00016b84, 0x00000000}, {0x00016b88, 0x00400705}, {0x00016b8c, 0x00800700}, {0x00016b90, 0x00800700}, {0x00016b94, 0x00000000}, {0x00016b98, 0x00000000}, {0x00016b9c, 0x00000000}, {0x00016ba0, 0x00000001}, {0x00016ba4, 0x00000001}, {0x00016ba8, 0x00000000}, {0x00016bac, 0x00000000}, {0x00016bb0, 0x00000000}, {0x00016bb4, 0x00000000}, {0x00016bb8, 0x00000000}, {0x00016bbc, 0x00000000}, {0x00016bc0, 0x000000a0}, {0x00016bc4, 0x000c0000}, {0x00016bc8, 0x14021402}, {0x00016bcc, 0x00001402}, {0x00016bd0, 0x00000000}, {0x00016bd4, 0x00000000}, }; static const u32 ar955x_1p0_modes_xpa_tx_gain_table[][9] = { /* Addr 5G_HT20_L 5G_HT40_L 5G_HT20_M 5G_HT40_M 5G_HT20_H 5G_HT40_H 2G_HT40 2G_HT20 */ {0x0000a2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa}, {0x0000a2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc}, {0x0000a2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0}, {0x0000a2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00}, {0x0000a410, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050da, 0x000050da}, {0x0000a500, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000000, 0x00000000}, {0x0000a504, 0x04000005, 0x04000005, 0x04000005, 0x04000005, 0x04000005, 0x04000005, 0x04000002, 0x04000002}, {0x0000a508, 0x08000009, 0x08000009, 0x08000009, 0x08000009, 0x08000009, 0x08000009, 0x08000004, 0x08000004}, {0x0000a50c, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c000006, 0x0c000006}, {0x0000a510, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x0f00000a, 0x0f00000a}, {0x0000a514, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x1300000c, 0x1300000c}, {0x0000a518, 0x19004008, 0x19004008, 0x19004008, 0x19004008, 0x18004008, 0x18004008, 0x1700000e, 0x1700000e}, {0x0000a51c, 0x1d00400a, 0x1d00400a, 0x1d00400a, 0x1d00400a, 0x1c00400a, 0x1c00400a, 0x1b000064, 0x1b000064}, {0x0000a520, 0x230020a2, 0x230020a2, 0x210020a2, 0x210020a2, 0x200020a2, 0x200020a2, 0x1f000242, 0x1f000242}, {0x0000a524, 0x2500006e, 0x2500006e, 0x2500006e, 0x2500006e, 0x2400006e, 0x2400006e, 0x23000229, 0x23000229}, {0x0000a528, 0x29022221, 0x29022221, 0x28022221, 0x28022221, 0x27022221, 0x27022221, 0x270002a2, 0x270002a2}, {0x0000a52c, 0x2d00062a, 0x2d00062a, 0x2c00062a, 0x2c00062a, 0x2a00062a, 0x2a00062a, 0x2c001203, 0x2c001203}, {0x0000a530, 0x340220a5, 0x340220a5, 0x320220a5, 0x320220a5, 0x2f0220a5, 0x2f0220a5, 0x30001803, 0x30001803}, {0x0000a534, 0x380022c5, 0x380022c5, 0x350022c5, 0x350022c5, 0x320022c5, 0x320022c5, 0x33000881, 0x33000881}, {0x0000a538, 0x3b002486, 0x3b002486, 0x39002486, 0x39002486, 0x36002486, 0x36002486, 0x38001809, 0x38001809}, {0x0000a53c, 0x3f00248a, 0x3f00248a, 0x3d00248a, 0x3d00248a, 0x3a00248a, 0x3a00248a, 0x3a000814, 0x3a000814}, {0x0000a540, 0x4202242c, 0x4202242c, 0x4102242c, 0x4102242c, 0x3f02242c, 0x3f02242c, 0x3f001a0c, 0x3f001a0c}, {0x0000a544, 0x490044c6, 0x490044c6, 0x460044c6, 0x460044c6, 0x420044c6, 0x420044c6, 0x43001a0e, 0x43001a0e}, {0x0000a548, 0x4d024485, 0x4d024485, 0x4a024485, 0x4a024485, 0x46024485, 0x46024485, 0x46001812, 0x46001812}, {0x0000a54c, 0x51044483, 0x51044483, 0x4e044483, 0x4e044483, 0x4a044483, 0x4a044483, 0x49001884, 0x49001884}, {0x0000a550, 0x5404a40c, 0x5404a40c, 0x5204a40c, 0x5204a40c, 0x4d04a40c, 0x4d04a40c, 0x4d001e84, 0x4d001e84}, {0x0000a554, 0x57024632, 0x57024632, 0x55024632, 0x55024632, 0x52024632, 0x52024632, 0x50001e69, 0x50001e69}, {0x0000a558, 0x5c00a634, 0x5c00a634, 0x5900a634, 0x5900a634, 0x5600a634, 0x5600a634, 0x550006f4, 0x550006f4}, {0x0000a55c, 0x5f026832, 0x5f026832, 0x5d026832, 0x5d026832, 0x5a026832, 0x5a026832, 0x59000ad3, 0x59000ad3}, {0x0000a560, 0x6602b012, 0x6602b012, 0x6202b012, 0x6202b012, 0x5d02b012, 0x5d02b012, 0x5e000ad5, 0x5e000ad5}, {0x0000a564, 0x6e02d0e1, 0x6e02d0e1, 0x6802d0e1, 0x6802d0e1, 0x6002d0e1, 0x6002d0e1, 0x61001ced, 0x61001ced}, {0x0000a568, 0x7202b4c4, 0x7202b4c4, 0x6c02b4c4, 0x6c02b4c4, 0x6502b4c4, 0x6502b4c4, 0x660018d4, 0x660018d4}, {0x0000a56c, 0x75007894, 0x75007894, 0x70007894, 0x70007894, 0x6b007894, 0x6b007894, 0x660018d4, 0x660018d4}, {0x0000a570, 0x7b025c74, 0x7b025c74, 0x75025c74, 0x75025c74, 0x70025c74, 0x70025c74, 0x660018d4, 0x660018d4}, {0x0000a574, 0x8300bcb5, 0x8300bcb5, 0x7a00bcb5, 0x7a00bcb5, 0x7600bcb5, 0x7600bcb5, 0x660018d4, 0x660018d4}, {0x0000a578, 0x8a04dc74, 0x8a04dc74, 0x7f04dc74, 0x7f04dc74, 0x7c04dc74, 0x7c04dc74, 0x660018d4, 0x660018d4}, {0x0000a57c, 0x8a04dc74, 0x8a04dc74, 0x7f04dc74, 0x7f04dc74, 0x7c04dc74, 0x7c04dc74, 0x660018d4, 0x660018d4}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x03804000, 0x03804000}, {0x0000a610, 0x04c08c01, 0x04c08c01, 0x04808b01, 0x04808b01, 0x04808a01, 0x04808a01, 0x0300ca02, 0x0300ca02}, {0x0000a614, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00000e04, 0x00000e04}, {0x0000a618, 0x04010c01, 0x04010c01, 0x03c10b01, 0x03c10b01, 0x03810a01, 0x03810a01, 0x03014000, 0x03014000}, {0x0000a61c, 0x03814e05, 0x03814e05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03414d05, 0x00000000, 0x00000000}, {0x0000a620, 0x04010303, 0x04010303, 0x03c10303, 0x03c10303, 0x03810303, 0x03810303, 0x00000000, 0x00000000}, {0x0000a624, 0x03814e05, 0x03814e05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03014000, 0x03014000}, {0x0000a628, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x03804c05, 0x03804c05}, {0x0000a62c, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x0701de06, 0x0701de06}, {0x0000a630, 0x03418000, 0x03418000, 0x03018000, 0x03018000, 0x02c18000, 0x02c18000, 0x07819c07, 0x07819c07}, {0x0000a634, 0x03815004, 0x03815004, 0x03414f04, 0x03414f04, 0x03414e04, 0x03414e04, 0x0701dc07, 0x0701dc07}, {0x0000a638, 0x03005302, 0x03005302, 0x02c05202, 0x02c05202, 0x02805202, 0x02805202, 0x0701dc07, 0x0701dc07}, {0x0000a63c, 0x04c09302, 0x04c09302, 0x04809202, 0x04809202, 0x04809202, 0x04809202, 0x0701dc07, 0x0701dc07}, {0x0000b2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa}, {0x0000b2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc}, {0x0000b2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0}, {0x0000b2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00}, {0x0000c2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa}, {0x0000c2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc}, {0x0000c2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0}, {0x0000c2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00}, {0x00016044, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4}, {0x00016048, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401}, {0x00016280, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01808e84, 0x01808e84}, {0x00016444, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4}, {0x00016448, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401}, {0x00016844, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4}, {0x00016848, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401}, }; static const u32 ar955x_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c22}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00a00000}, {0x000080d8, 0x00400000}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008140, 0x000000fe}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x000081c0, 0x00000000}, {0x000081c4, 0x33332210}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000800}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x9d400010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00001d40}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x0000001f}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0xffff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xaa48107b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a0, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x8c7901ff}, }; static const u32 ar955x_1p0_common_rx_gain_table[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x01910190}, {0x0000a030, 0x01930192}, {0x0000a034, 0x01950194}, {0x0000a038, 0x038a0196}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x22222229}, {0x0000a084, 0x1d1d1d1d}, {0x0000a088, 0x1d1d1d1d}, {0x0000a08c, 0x1d1d1d1d}, {0x0000a090, 0x171d1d1d}, {0x0000a094, 0x11111717}, {0x0000a098, 0x00030311}, {0x0000a09c, 0x00000000}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x23232323}, {0x0000b084, 0x21232323}, {0x0000b088, 0x19191c1e}, {0x0000b08c, 0x12141417}, {0x0000b090, 0x07070e0e}, {0x0000b094, 0x03030305}, {0x0000b098, 0x00000003}, {0x0000b09c, 0x00000000}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar955x_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a9f6b}, {0x0000980c, 0x04900000}, {0x00009814, 0x0280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x6400a190}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x14000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x32840bbe}, {0x000098bc, 0x00000002}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009c04, 0xff55ff55}, {0x00009c08, 0x0320ff55}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, {0x00009e24, 0x990bb515}, {0x00009e28, 0x0c6f0000}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009fc0, 0x813e4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x00009fd0, 0x01193b93}, {0x0000a20c, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a248, 0x00000140}, {0x0000a2a0, 0x00000007}, {0x0000a2c0, 0x00000007}, {0x0000a2c8, 0x00000000}, {0x0000a2d4, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x1f020503}, {0x0000a39c, 0x29180c03}, {0x0000a3a0, 0x9a8b6844}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0xaaaaaaaa}, {0x0000a3ac, 0x3c466478}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000000}, {0x0000a3f8, 0x0c9bd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00100000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x05000080}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a458, 0x00000000}, {0x0000a644, 0x3fad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, {0x0000a67c, 0x13121110}, {0x0000a680, 0x31301514}, {0x0000a684, 0x35343332}, {0x0000a688, 0x00000036}, {0x0000a690, 0x00000838}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000000}, {0x0000a8d0, 0x004b6a8e}, {0x0000a8d4, 0x00000820}, {0x0000a8dc, 0x00000000}, {0x0000a8f0, 0x00000000}, {0x0000a8f4, 0x00000000}, {0x0000b2d0, 0x00000080}, {0x0000b2d4, 0x00000000}, {0x0000b2ec, 0x00000000}, {0x0000b2f0, 0x00000000}, {0x0000b2f4, 0x00000000}, {0x0000b2f8, 0x00000000}, {0x0000b408, 0x0e79e5c0}, {0x0000b40c, 0x00820820}, {0x0000b420, 0x00000000}, {0x0000b8d0, 0x004b6a8e}, {0x0000b8d4, 0x00000820}, {0x0000b8dc, 0x00000000}, {0x0000b8f0, 0x00000000}, {0x0000b8f4, 0x00000000}, {0x0000c2d0, 0x00000080}, {0x0000c2d4, 0x00000000}, {0x0000c2ec, 0x00000000}, {0x0000c2f0, 0x00000000}, {0x0000c2f4, 0x00000000}, {0x0000c2f8, 0x00000000}, {0x0000c408, 0x0e79e5c0}, {0x0000c40c, 0x00820820}, {0x0000c420, 0x00000000}, }; static const u32 ar955x_1p0_common_wo_xlna_rx_gain_table[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x03820190}, {0x0000a030, 0x03840383}, {0x0000a034, 0x03880385}, {0x0000a038, 0x038a0389}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x29292929}, {0x0000a084, 0x29292929}, {0x0000a088, 0x29292929}, {0x0000a08c, 0x29292929}, {0x0000a090, 0x22292929}, {0x0000a094, 0x1d1d2222}, {0x0000a098, 0x0c111117}, {0x0000a09c, 0x00030303}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x32323232}, {0x0000b084, 0x2f2f3232}, {0x0000b088, 0x23282a2d}, {0x0000b08c, 0x1c1e2123}, {0x0000b090, 0x14171919}, {0x0000b094, 0x0e0e1214}, {0x0000b098, 0x03050707}, {0x0000b09c, 0x00030303}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar955x_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x00007000, 0x00000000}, {0x00007004, 0x00000000}, {0x00007008, 0x00000000}, {0x0000700c, 0x00000000}, {0x0000701c, 0x00000000}, {0x00007020, 0x00000000}, {0x00007024, 0x00000000}, {0x00007028, 0x00000000}, {0x0000702c, 0x00000000}, {0x00007030, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00007048, 0x00000000}, }; static const u32 ar955x_1p0_common_wo_xlna_rx_gain_bounds[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, }; static const u32 ar955x_1p0_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; static const u32 ar955x_1p0_common_rx_gain_bounds[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302018, 0x50302018}, }; static const u32 ar955x_1p0_modes_no_xpa_tx_gain_table[][9] = { /* Addr 5G_HT20_L 5G_HT40_L 5G_HT20_M 5G_HT40_M 5G_HT20_H 5G_HT40_H 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0xfffe5aaa, 0xfffe5aaa}, {0x0000a2e0, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0xfffe9ccc, 0xfffe9ccc}, {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0xffffe0f0, 0xffffe0f0}, {0x0000a2e8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffef00, 0xffffef00}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d8, 0x000050d8, 0x000050d8, 0x000050d8, 0x000050d7, 0x000050d7}, {0x0000a500, 0x00002220, 0x00002220, 0x00002220, 0x00002220, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x04002222, 0x04002222, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, {0x0000a508, 0x09002421, 0x09002421, 0x09002421, 0x09002421, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, {0x0000a50c, 0x0d002621, 0x0d002621, 0x0d002621, 0x0d002621, 0x0d002621, 0x0d002621, 0x0b000006, 0x0b000006}, {0x0000a510, 0x13004620, 0x13004620, 0x13004620, 0x13004620, 0x13004620, 0x13004620, 0x0f00000a, 0x0f00000a}, {0x0000a514, 0x19004a20, 0x19004a20, 0x19004a20, 0x19004a20, 0x19004a20, 0x19004a20, 0x1300000c, 0x1300000c}, {0x0000a518, 0x1d004e20, 0x1d004e20, 0x1d004e20, 0x1d004e20, 0x1d004e20, 0x1d004e20, 0x1700000e, 0x1700000e}, {0x0000a51c, 0x21005420, 0x21005420, 0x21005420, 0x21005420, 0x21005420, 0x21005420, 0x1b000012, 0x1b000012}, {0x0000a520, 0x26005e20, 0x26005e20, 0x26005e20, 0x26005e20, 0x26005e20, 0x26005e20, 0x1f00004a, 0x1f00004a}, {0x0000a524, 0x2b005e40, 0x2b005e40, 0x2b005e40, 0x2b005e40, 0x2b005e40, 0x2b005e40, 0x23000244, 0x23000244}, {0x0000a528, 0x2f005e42, 0x2f005e42, 0x2f005e42, 0x2f005e42, 0x2f005e42, 0x2f005e42, 0x2700022b, 0x2700022b}, {0x0000a52c, 0x33005e44, 0x33005e44, 0x33005e44, 0x33005e44, 0x33005e44, 0x33005e44, 0x2b000625, 0x2b000625}, {0x0000a530, 0x38005e65, 0x38005e65, 0x38005e65, 0x38005e65, 0x38005e65, 0x38005e65, 0x2f001006, 0x2f001006}, {0x0000a534, 0x3c005e69, 0x3c005e69, 0x3c005e69, 0x3c005e69, 0x3c005e69, 0x3c005e69, 0x330008a0, 0x330008a0}, {0x0000a538, 0x40005e6b, 0x40005e6b, 0x40005e6b, 0x40005e6b, 0x40005e6b, 0x40005e6b, 0x37000a2a, 0x37000a2a}, {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x44005e6d, 0x44005e6d, 0x44005e6d, 0x44005e6d, 0x3b001c23, 0x3b001c23}, {0x0000a540, 0x49005e72, 0x49005e72, 0x49005e72, 0x49005e72, 0x49005e72, 0x49005e72, 0x3f0014a0, 0x3f0014a0}, {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x4e005eb2, 0x4e005eb2, 0x4e005eb2, 0x4e005eb2, 0x43001882, 0x43001882}, {0x0000a548, 0x53005f12, 0x53005f12, 0x53005f12, 0x53005f12, 0x53005f12, 0x53005f12, 0x47001ca2, 0x47001ca2}, {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x59025eb2, 0x59025eb2, 0x59025eb2, 0x59025eb2, 0x4b001ec3, 0x4b001ec3}, {0x0000a550, 0x5e025f12, 0x5e025f12, 0x5e025f12, 0x5e025f12, 0x5e025f12, 0x5e025f12, 0x4f00148c, 0x4f00148c}, {0x0000a554, 0x61027f12, 0x61027f12, 0x61027f12, 0x61027f12, 0x61027f12, 0x61027f12, 0x53001c6e, 0x53001c6e}, {0x0000a558, 0x6702bf12, 0x6702bf12, 0x6702bf12, 0x6702bf12, 0x6702bf12, 0x6702bf12, 0x57001c92, 0x57001c92}, {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x6b02bf14, 0x6b02bf14, 0x6b02bf14, 0x6b02bf14, 0x5c001af6, 0x5c001af6}, {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x6f02bf16, 0x5c001af6, 0x5c001af6}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00804000, 0x00804000, 0x00804000, 0x00804000, 0x00804000, 0x00804000, 0x04005001, 0x04005001}, {0x0000a614, 0x00804201, 0x00804201, 0x00804201, 0x00804201, 0x00804201, 0x00804201, 0x03808e02, 0x03808e02}, {0x0000a618, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802, 0x0300c000, 0x0300c000}, {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x0280ca03, 0x0280ca03, 0x0280ca03, 0x0280ca03, 0x03808e02, 0x03808e02}, {0x0000a620, 0x04c15104, 0x04c15104, 0x04c15104, 0x04c15104, 0x04c15104, 0x04c15104, 0x03410c03, 0x03410c03}, {0x0000a624, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04014c03, 0x04014c03}, {0x0000a628, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x05818d04, 0x05818d04}, {0x0000a62c, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x0801cd04, 0x0801cd04}, {0x0000a630, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x0801e007, 0x0801e007}, {0x0000a634, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x0801e007, 0x0801e007}, {0x0000a638, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x0801e007, 0x0801e007}, {0x0000a63c, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x04c15305, 0x0801e007, 0x0801e007}, {0x0000b2dc, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0xfffe5aaa, 0xfffe5aaa}, {0x0000b2e0, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0xfffe9ccc, 0xfffe9ccc}, {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0xffffe0f0, 0xffffe0f0}, {0x0000b2e8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffef00, 0xffffef00}, {0x0000c2dc, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0x01feee00, 0xfffe5aaa, 0xfffe5aaa}, {0x0000c2e0, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0x0000f000, 0xfffe9ccc, 0xfffe9ccc}, {0x0000c2e4, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0x01ff0000, 0xffffe0f0, 0xffffe0f0}, {0x0000c2e8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffef00, 0xffffef00}, {0x00016044, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x054922d4, 0x054922d4}, {0x00016048, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401}, {0x00016444, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x054922d4, 0x054922d4}, {0x00016448, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401}, {0x00016844, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x054922d4, 0x054922d4}, {0x00016848, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401}, }; static const u32 ar955x_1p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; static const u32 ar955x_1p0_modes_fast_clock[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, {0x000010b0, 0x00000fd0, 0x00001fa0}, {0x00008014, 0x044c044c, 0x08980898}, {0x0000801c, 0x148ec02b, 0x148ec057}, {0x00008318, 0x000044c0, 0x00008980}, {0x00009e00, 0x0372131c, 0x0372131c}, {0x0000a230, 0x0000000b, 0x00000016}, {0x0000a254, 0x00000898, 0x00001130}, }; #endif /* INITVALS_955X_1P0_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9485_initvals.h0000644000175000017500000006510412026211315025301 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9485_H #define INITVALS_9485_H /* AR9485 1.0 */ #define ar9485_1_1_mac_postamble ar9300_2p2_mac_postamble static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x18012e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { /* Addr allmodes */ {0x0000a000, 0x00060005}, {0x0000a004, 0x00810080}, {0x0000a008, 0x00830082}, {0x0000a00c, 0x00850084}, {0x0000a010, 0x01820181}, {0x0000a014, 0x01840183}, {0x0000a018, 0x01880185}, {0x0000a01c, 0x018a0189}, {0x0000a020, 0x02850284}, {0x0000a024, 0x02890288}, {0x0000a028, 0x028b028a}, {0x0000a02c, 0x03850384}, {0x0000a030, 0x03890388}, {0x0000a034, 0x038b038a}, {0x0000a038, 0x038d038c}, {0x0000a03c, 0x03910390}, {0x0000a040, 0x03930392}, {0x0000a044, 0x03950394}, {0x0000a048, 0x00000396}, {0x0000a04c, 0x00000000}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x28282828}, {0x0000a084, 0x28282828}, {0x0000a088, 0x28282828}, {0x0000a08c, 0x28282828}, {0x0000a090, 0x28282828}, {0x0000a094, 0x24242428}, {0x0000a098, 0x171e1e1e}, {0x0000a09c, 0x02020b0b}, {0x0000a0a0, 0x02020202}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x22072208}, {0x0000a0c4, 0x22052206}, {0x0000a0c8, 0x22032204}, {0x0000a0cc, 0x22012202}, {0x0000a0d0, 0x221f2200}, {0x0000a0d4, 0x221d221e}, {0x0000a0d8, 0x33023303}, {0x0000a0dc, 0x33003301}, {0x0000a0e0, 0x331e331f}, {0x0000a0e4, 0x4402331d}, {0x0000a0e8, 0x44004401}, {0x0000a0ec, 0x441e441f}, {0x0000a0f0, 0x55025503}, {0x0000a0f4, 0x55005501}, {0x0000a0f8, 0x551e551f}, {0x0000a0fc, 0x6602551d}, {0x0000a100, 0x66006601}, {0x0000a104, 0x661e661f}, {0x0000a108, 0x7703661d}, {0x0000a10c, 0x77017702}, {0x0000a110, 0x00007700}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x111f1100}, {0x0000a148, 0x111d111e}, {0x0000a14c, 0x111b111c}, {0x0000a150, 0x22032204}, {0x0000a154, 0x22012202}, {0x0000a158, 0x221f2200}, {0x0000a15c, 0x221d221e}, {0x0000a160, 0x33013302}, {0x0000a164, 0x331f3300}, {0x0000a168, 0x4402331e}, {0x0000a16c, 0x44004401}, {0x0000a170, 0x441e441f}, {0x0000a174, 0x55015502}, {0x0000a178, 0x551f5500}, {0x0000a17c, 0x6602551e}, {0x0000a180, 0x66006601}, {0x0000a184, 0x661e661f}, {0x0000a188, 0x7703661d}, {0x0000a18c, 0x77017702}, {0x0000a190, 0x00007700}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000296}, }; static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; #define ar9485Modes_high_ob_db_tx_gain_1_1 ar9485Modes_high_power_tx_gain_1_1 #define ar9485Modes_low_ob_db_tx_gain_1_1 ar9485Modes_high_ob_db_tx_gain_1_1 #define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 static const u32 ar9485_1_1[][2] = { /* Addr allmodes */ {0x0000a580, 0x00000000}, {0x0000a584, 0x00000000}, {0x0000a588, 0x00000000}, {0x0000a58c, 0x00000000}, {0x0000a590, 0x00000000}, {0x0000a594, 0x00000000}, {0x0000a598, 0x00000000}, {0x0000a59c, 0x00000000}, {0x0000a5a0, 0x00000000}, {0x0000a5a4, 0x00000000}, {0x0000a5a8, 0x00000000}, {0x0000a5ac, 0x00000000}, {0x0000a5b0, 0x00000000}, {0x0000a5b4, 0x00000000}, {0x0000a5b8, 0x00000000}, {0x0000a5bc, 0x00000000}, }; static const u32 ar9485_1_1_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73800000}, {0x0001600c, 0x00000000}, {0x00016040, 0x7f80fff8}, {0x0001604c, 0x000f0278}, {0x00016050, 0x4db6db8c}, {0x00016054, 0x6db60000}, {0x00016080, 0x00080000}, {0x00016084, 0x0e48048c}, {0x00016088, 0x14214514}, {0x0001608c, 0x119f081e}, {0x00016090, 0x24926490}, {0x00016098, 0xd28b3330}, {0x000160a0, 0xc2108ffe}, {0x000160a4, 0x812fc370}, {0x000160a8, 0x423c8000}, {0x000160b4, 0x92480040}, {0x000160c0, 0x006db6db}, {0x000160c4, 0x0186db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x6de6fbe0}, {0x000160d0, 0xf7dfcf3c}, {0x00016100, 0x04cb0001}, {0x00016104, 0xfff80015}, {0x00016108, 0x00080010}, {0x00016144, 0x01884080}, {0x00016148, 0x00008040}, {0x00016240, 0x08400000}, {0x00016244, 0x1bf90f00}, {0x00016248, 0x00000000}, {0x0001624c, 0x00000000}, {0x00016280, 0x01000015}, {0x00016284, 0x00d30000}, {0x00016288, 0x00318000}, {0x0001628c, 0x50000000}, {0x00016290, 0x4b96210f}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00800700}, {0x0001638c, 0x00800700}, {0x00016390, 0x00800700}, {0x00016394, 0x00000000}, {0x00016398, 0x00000000}, {0x0001639c, 0x00000000}, {0x000163a0, 0x00000001}, {0x000163a4, 0x00000001}, {0x000163a8, 0x00000000}, {0x000163ac, 0x00000000}, {0x000163b0, 0x00000000}, {0x000163b4, 0x00000000}, {0x000163b8, 0x00000000}, {0x000163bc, 0x00000000}, {0x000163c0, 0x000000a0}, {0x000163c4, 0x000c0000}, {0x000163c8, 0x14021402}, {0x000163cc, 0x00001402}, {0x000163d0, 0x00000000}, {0x000163d4, 0x00000000}, {0x00016c40, 0x13188278}, {0x00016c44, 0x12000000}, }; static const u32 ar9485_1_1_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a8f6b}, {0x0000980c, 0x04800000}, {0x00009814, 0x9280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x5f3ca3de}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x14750600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x52440bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009c04, 0x00000000}, {0x00009c08, 0x03200000}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x1883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c00400}, {0x00009d18, 0x00000000}, {0x00009d1c, 0x00000000}, {0x00009e08, 0x0038233c}, {0x00009e24, 0x9927b515}, {0x00009e28, 0x12ef0200}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009fc0, 0x80be4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x0000a20c, 0x00000000}, {0x0000a210, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, {0x0000a2c0, 0x00000001}, {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, {0x0000a2dc, 0x00000000}, {0x0000a2e0, 0x00000000}, {0x0000a2e4, 0x00000000}, {0x0000a2e8, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, {0x0000a3a4, 0x000000ff}, {0x0000a3a8, 0x3b3b3b3b}, {0x0000a3ac, 0x2f2f2f2f}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000006}, {0x0000a3f8, 0x0cdbd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739cf}, {0x0000a418, 0x2d0019ce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00000000}, {0x0000a440, 0x00000000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x04000000}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a5c4, 0xbfad9d74}, {0x0000a5c8, 0x0048060a}, {0x0000a5cc, 0x00000637}, {0x0000a760, 0x03020100}, {0x0000a764, 0x09080504}, {0x0000a768, 0x0d0c0b0a}, {0x0000a76c, 0x13121110}, {0x0000a770, 0x31301514}, {0x0000a774, 0x35343332}, {0x0000a778, 0x00000036}, {0x0000a780, 0x00000838}, {0x0000a7c0, 0x00000000}, {0x0000a7c4, 0xfffffffc}, {0x0000a7c8, 0x00000000}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000000}, }; static const u32 ar9485_common_rx_gain_1_1[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x01800082}, {0x0000a014, 0x01820181}, {0x0000a018, 0x01840183}, {0x0000a01c, 0x01880185}, {0x0000a020, 0x018a0189}, {0x0000a024, 0x02850284}, {0x0000a028, 0x02890288}, {0x0000a02c, 0x03850384}, {0x0000a030, 0x03890388}, {0x0000a034, 0x038b038a}, {0x0000a038, 0x038d038c}, {0x0000a03c, 0x03910390}, {0x0000a040, 0x03930392}, {0x0000a044, 0x03950394}, {0x0000a048, 0x00000396}, {0x0000a04c, 0x00000000}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x28282828}, {0x0000a084, 0x28282828}, {0x0000a088, 0x28282828}, {0x0000a08c, 0x28282828}, {0x0000a090, 0x28282828}, {0x0000a094, 0x21212128}, {0x0000a098, 0x171c1c1c}, {0x0000a09c, 0x02020212}, {0x0000a0a0, 0x00000202}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x111f1100}, {0x0000a0c8, 0x111d111e}, {0x0000a0cc, 0x111b111c}, {0x0000a0d0, 0x22032204}, {0x0000a0d4, 0x22012202}, {0x0000a0d8, 0x221f2200}, {0x0000a0dc, 0x221d221e}, {0x0000a0e0, 0x33013302}, {0x0000a0e4, 0x331f3300}, {0x0000a0e8, 0x4402331e}, {0x0000a0ec, 0x44004401}, {0x0000a0f0, 0x441e441f}, {0x0000a0f4, 0x55015502}, {0x0000a0f8, 0x551f5500}, {0x0000a0fc, 0x6602551e}, {0x0000a100, 0x66006601}, {0x0000a104, 0x661e661f}, {0x0000a108, 0x7703661d}, {0x0000a10c, 0x77017702}, {0x0000a110, 0x00007700}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x111f1100}, {0x0000a148, 0x111d111e}, {0x0000a14c, 0x111b111c}, {0x0000a150, 0x22032204}, {0x0000a154, 0x22012202}, {0x0000a158, 0x221f2200}, {0x0000a15c, 0x221d221e}, {0x0000a160, 0x33013302}, {0x0000a164, 0x331f3300}, {0x0000a168, 0x4402331e}, {0x0000a16c, 0x44004401}, {0x0000a170, 0x441e441f}, {0x0000a174, 0x55015502}, {0x0000a178, 0x551f5500}, {0x0000a17c, 0x6602551e}, {0x0000a180, 0x66006601}, {0x0000a184, 0x661e661f}, {0x0000a188, 0x7703661d}, {0x0000a18c, 0x77017702}, {0x0000a190, 0x00007700}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000296}, }; static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x18052e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x18053e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; static const u32 ar9485_1_1_soc_preamble[][2] = { /* Addr allmodes */ {0x00004014, 0xba280400}, {0x00004090, 0x00aa10aa}, {0x000040a4, 0x00a0c9c9}, {0x00007010, 0x00000022}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00007048, 0x00000002}, }; static const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00009e00, 0x03721821, 0x03721821}, {0x0000a230, 0x0000400b, 0x00004016}, {0x0000a254, 0x00000898, 0x00001130}, }; static const u32 ar9485_1_1_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e}, {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x01303fc0, 0x01303fc4, 0x01303fc4, 0x01303fc0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, {0x0000a234, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x000002a0, 0x000002a0}, {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00071981, 0x00071981, 0x00071982, 0x00071982}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000be04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x18013e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; static const u32 ar9485_1_1_radio_postamble[][2] = { /* Addr allmodes */ {0x0001609c, 0x0b283f31}, {0x000160ac, 0x24611800}, {0x000160b0, 0x03284f3e}, {0x0001610c, 0x00170000}, {0x00016140, 0x50804008}, }; static const u32 ar9485_1_1_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c22}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00a00000}, {0x000080d8, 0x00400000}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x18486200}, {0x00008174, 0x33332210}, {0x00008178, 0x00000000}, {0x0000817c, 0x00020000}, {0x000081c0, 0x00000000}, {0x000081c4, 0x33332210}, {0x000081d4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000800}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x9ca00010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000140}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xa248105b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a0, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x000301ff}, }; #endif /* INITVALS_9485_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h0000644000175000017500000013334412026211315025757 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9462_2P0_H #define INITVALS_9462_2P0_H /* AR9462 2.0 */ static const u32 ar9462_modes_fast_clock_2p0[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, {0x000010b0, 0x00000fd0, 0x00001fa0}, {0x00008014, 0x044c044c, 0x08980898}, {0x0000801c, 0x148ec02b, 0x148ec057}, {0x00008318, 0x000044c0, 0x00008980}, {0x00009e00, 0x0372131c, 0x0372131c}, {0x0000a230, 0x0000400b, 0x00004016}, {0x0000a254, 0x00000898, 0x00001130}, }; static const u32 ar9462_pciephy_clkreq_enable_L1_2p0[][2] = { /* Addr allmodes */ {0x00018c00, 0x18253ede}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0003580c}, }; static const u32 ar9462_2p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae}, {0x00009824, 0x5ac640de, 0x5ac640d0, 0x5ac640d0, 0x63c640da}, {0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x09143e81}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8}, {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e}, {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3376605e, 0x32395d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946222, 0xcf946222, 0xcfd5c782, 0xcfd5c282}, {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x01318fc0, 0x01318fc4, 0x01318fc4, 0x01318fc0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a3a4, 0x00000010, 0x00000010, 0x00000000, 0x00000000}, {0x0000a3a8, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa}, {0x0000a3ac, 0xaaaaaa00, 0xaaaaaa30, 0xaaaaaa00, 0xaaaaaa00}, {0x0000a41c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, {0x0000a420, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce}, {0x0000a424, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, {0x0000a428, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce}, {0x0000a42c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, {0x0000a430, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550}, }; static const u32 ar9462_common_rx_gain_table_2p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x01910190}, {0x0000a030, 0x01930192}, {0x0000a034, 0x01950194}, {0x0000a038, 0x038a0196}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x22222229}, {0x0000a084, 0x1d1d1d1d}, {0x0000a088, 0x1d1d1d1d}, {0x0000a08c, 0x1d1d1d1d}, {0x0000a090, 0x171d1d1d}, {0x0000a094, 0x11111717}, {0x0000a098, 0x00030311}, {0x0000a09c, 0x00000000}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x2a2d2f32}, {0x0000b084, 0x21232328}, {0x0000b088, 0x19191c1e}, {0x0000b08c, 0x12141417}, {0x0000b090, 0x07070e0e}, {0x0000b094, 0x03030305}, {0x0000b098, 0x00000003}, {0x0000b09c, 0x00000000}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar9462_pciephy_clkreq_disable_L1_2p0[][2] = { /* Addr allmodes */ {0x00018c00, 0x18213ede}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0003580c}, }; static const u32 ar9462_pciephy_pll_on_clkreq_disable_L1_2p0[][2] = { /* Addr allmodes */ {0x00018c00, 0x18212ede}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0003580c}, }; static const u32 ar9462_2p0_radio_postamble_sys2ant[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; static const u32 ar9462_common_wo_xlna_rx_gain_table_2p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x03820190}, {0x0000a030, 0x03840383}, {0x0000a034, 0x03880385}, {0x0000a038, 0x038a0389}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x29292929}, {0x0000a084, 0x29292929}, {0x0000a088, 0x29292929}, {0x0000a08c, 0x29292929}, {0x0000a090, 0x22292929}, {0x0000a094, 0x1d1d2222}, {0x0000a098, 0x0c111117}, {0x0000a09c, 0x00030303}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x32323232}, {0x0000b084, 0x2f2f3232}, {0x0000b088, 0x23282a2d}, {0x0000b08c, 0x1c1e2123}, {0x0000b090, 0x14171919}, {0x0000b094, 0x0e0e1214}, {0x0000b098, 0x03050707}, {0x0000b09c, 0x00030303}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar9462_2p0_baseband_core_txfir_coeff_japan_2484[][2] = { /* Addr allmodes */ {0x0000a398, 0x00000000}, {0x0000a39c, 0x6f7f0301}, {0x0000a3a0, 0xca9228ee}, }; static const u32 ar9462_modes_low_ob_db_tx_gain_table_2p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, {0x00016048, 0x64992060, 0x64992060, 0x64992060, 0x64992060}, {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, {0x00016444, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, {0x00016448, 0x64992000, 0x64992000, 0x64992000, 0x64992000}, {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, }; static const u32 ar9462_2p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000033, 0x00000033, 0x00000033, 0x00000033}, }; static const u32 ar9462_2p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a9f6b}, {0x0000980c, 0x04900000}, {0x00009814, 0x9280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x6400a290}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x0d000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x32440bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098e4, 0x01ffffff}, {0x000098e8, 0x01ffffff}, {0x000098ec, 0x01ffffff}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009bf0, 0x80000000}, {0x00009c04, 0xff55ff55}, {0x00009c08, 0x0320ff55}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, {0x00009e24, 0x990bb515}, {0x00009e28, 0x0c6f0000}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x15262820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0xe4c555c2}, {0x00009e58, 0xfd857722}, {0x00009e5c, 0xe9198724}, {0x00009fc0, 0x803e4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x00009fd0, 0x01193b93}, {0x0000a20c, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, {0x0000a2c0, 0x00000001}, {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000006}, {0x0000a3f8, 0x0c9bd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00100000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x05000080}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a454, 0x07000000}, {0x0000a644, 0xbfad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00002037}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, {0x0000a67c, 0x13121110}, {0x0000a680, 0x31301514}, {0x0000a684, 0x35343332}, {0x0000a688, 0x00000036}, {0x0000a690, 0x00000838}, {0x0000a6b0, 0x0000000a}, {0x0000a6b4, 0x00512c01}, {0x0000a7c0, 0x00000000}, {0x0000a7c4, 0xfffffffc}, {0x0000a7c8, 0x00000000}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000001}, {0x0000a7f0, 0x80000000}, {0x0000a8d0, 0x004b6a8e}, {0x0000a8d4, 0x00000820}, {0x0000a8dc, 0x00000000}, {0x0000a8f0, 0x00000000}, {0x0000a8f4, 0x00000000}, {0x0000abf0, 0x80000000}, {0x0000b2d0, 0x00000080}, {0x0000b2d4, 0x00000000}, {0x0000b2ec, 0x00000000}, {0x0000b2f0, 0x00000000}, {0x0000b2f4, 0x00000000}, {0x0000b2f8, 0x00000000}, {0x0000b408, 0x0e79e5c0}, {0x0000b40c, 0x00820820}, {0x0000b420, 0x00000000}, {0x0000b6b0, 0x0000000a}, {0x0000b6b4, 0x00000001}, }; static const u32 ar9462_2p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524}, {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70}, {0x0001610c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, }; static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400}, {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402}, {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603}, {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04}, {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20}, {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20}, {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22}, {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24}, {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640}, {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81}, {0x0000a54c, 0x59025eb6, 0x59025eb6, 0x42001a83, 0x42001a83}, {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001c84, 0x44001c84}, {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb}, {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060}, {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000}, {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, }; static const u32 ar9462_2p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73f00000}, {0x0001600c, 0x00000000}, {0x00016010, 0x6d820001}, {0x00016040, 0x7f80fff8}, {0x0001604c, 0x2699e04f}, {0x00016050, 0x6db6db6c}, {0x00016058, 0x6c200000}, {0x00016080, 0x000c0000}, {0x00016084, 0x9a68048c}, {0x00016088, 0x54214514}, {0x0001608c, 0x1203040b}, {0x00016090, 0x24926490}, {0x00016098, 0xd2888888}, {0x000160a0, 0x0a108ffe}, {0x000160a4, 0x812fc491}, {0x000160a8, 0x423c8000}, {0x000160b4, 0x92000000}, {0x000160b8, 0x0285dddc}, {0x000160bc, 0x02908888}, {0x000160c0, 0x00adb6d0}, {0x000160c4, 0x6db6db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x0de6c1b0}, {0x00016100, 0x3fffbe04}, {0x00016104, 0xfff80000}, {0x00016108, 0x00200400}, {0x00016110, 0x00000000}, {0x00016144, 0x02084080}, {0x00016148, 0x000080c0}, {0x00016280, 0x050a0001}, {0x00016284, 0x3d841418}, {0x00016288, 0x00000000}, {0x0001628c, 0xe3000000}, {0x00016290, 0xa1005080}, {0x00016294, 0x00000020}, {0x00016298, 0x54a82900}, {0x00016340, 0x121e4276}, {0x00016344, 0x00300000}, {0x00016400, 0x36db6db6}, {0x00016404, 0x6db6db40}, {0x00016408, 0x73f00000}, {0x0001640c, 0x00000000}, {0x00016410, 0x6c800001}, {0x00016440, 0x7f80fff8}, {0x0001644c, 0x4699e04f}, {0x00016450, 0x6db6db6c}, {0x00016500, 0x3fffbe04}, {0x00016504, 0xfff80000}, {0x00016508, 0x00200400}, {0x00016510, 0x00000000}, {0x00016544, 0x02084080}, {0x00016548, 0x000080c0}, }; static const u32 ar9462_2p0_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, }; static const u32 ar9462_2p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x000e0085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00001810, 0x0f000003}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00080000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008050, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c20}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00b00005}, {0x000080d8, 0x00400002}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x18486e00}, {0x00008174, 0x33332210}, {0x00008178, 0x00000000}, {0x0000817c, 0x00020000}, {0x000081c4, 0x33332210}, {0x000081c8, 0x00000000}, {0x000081cc, 0x00000000}, {0x000081d4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f424}, {0x00008248, 0x00000800}, {0x0000824c, 0x0001e848}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x99c00010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000140}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x0000001f}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0xffff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xaa48105b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x000301ff}, }; static const u32 ar9462_2p0_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x03820190}, {0x0000a030, 0x03840383}, {0x0000a034, 0x03880385}, {0x0000a038, 0x038a0389}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x29292929}, {0x0000a084, 0x29292929}, {0x0000a088, 0x29292929}, {0x0000a08c, 0x29292929}, {0x0000a090, 0x22292929}, {0x0000a094, 0x1d1d2222}, {0x0000a098, 0x0c111117}, {0x0000a09c, 0x00030303}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x2a2d2f32}, {0x0000b084, 0x21232328}, {0x0000b088, 0x19191c1e}, {0x0000b08c, 0x12141417}, {0x0000b090, 0x07070e0e}, {0x0000b094, 0x03030305}, {0x0000b098, 0x00000003}, {0x0000b09c, 0x00000000}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; #endif /* INITVALS_9462_2P0_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9340_initvals.h0000644000175000017500000016154112026211315025271 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9340_H #define INITVALS_9340_H static const u32 ar9340_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646800, 0xa4646800, 0xa4646800, 0xa4646800}, {0x0001610c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016140, 0x10804000, 0x10804000, 0x50804000, 0x50804000}, {0x0001650c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016540, 0x10804000, 0x10804000, 0x50804000, 0x50804000}, }; static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, {0x0000a598, 0x21820220, 0x21820220, 0x16800402, 0x16800402}, {0x0000a59c, 0x27820223, 0x27820223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861}, {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81}, {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x47801a83, 0x47801a83}, {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x4a801c84, 0x4a801c84}, {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x4e801ce3, 0x4e801ce3}, {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x52801ce5, 0x52801ce5}, {0x0000a5dc, 0x7086308c, 0x7086308c, 0x56801ce9, 0x56801ce9}, {0x0000a5e0, 0x738a308a, 0x738a308a, 0x5a801ceb, 0x5a801ceb}, {0x0000a5e4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5e8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5ec, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5f0, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5f4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x00016044, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, {0x00016048, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, {0x00016444, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; #define ar9340Modes_fast_clock_1p0 ar9300Modes_fast_clock_2p2 static const u32 ar9340_1p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73f00000}, {0x0001600c, 0x00000000}, {0x00016040, 0x7f80fff8}, {0x00016044, 0x03b6d2db}, {0x00016048, 0x24925266}, {0x0001604c, 0x000f0278}, {0x00016050, 0x6db6db6c}, {0x00016054, 0x6db60000}, {0x00016080, 0x00080000}, {0x00016084, 0x0e48048c}, {0x00016088, 0x14214514}, {0x0001608c, 0x119f081c}, {0x00016090, 0x24926490}, {0x00016094, 0x00000000}, {0x00016098, 0xd411eb84}, {0x0001609c, 0x03e47f32}, {0x000160a0, 0xc2108ffe}, {0x000160a4, 0x812fc370}, {0x000160a8, 0x423c8000}, {0x000160ac, 0xa4646800}, {0x000160b0, 0x00fe7f46}, {0x000160b4, 0x92480000}, {0x000160c0, 0x006db6db}, {0x000160c4, 0x6db6db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x6de6db6c}, {0x000160d0, 0xb6da4924}, {0x00016100, 0x04cb0001}, {0x00016104, 0xfff80000}, {0x00016108, 0x00080010}, {0x00016140, 0x50804008}, {0x00016144, 0x01884080}, {0x00016148, 0x000080c0}, {0x00016280, 0x01000015}, {0x00016284, 0x15530000}, {0x00016288, 0x00318000}, {0x0001628c, 0x50000000}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00800700}, {0x0001638c, 0x00800700}, {0x00016390, 0x00800700}, {0x00016394, 0x00000000}, {0x00016398, 0x00000000}, {0x0001639c, 0x00000000}, {0x000163a0, 0x00000001}, {0x000163a4, 0x00000001}, {0x000163a8, 0x00000000}, {0x000163ac, 0x00000000}, {0x000163b0, 0x00000000}, {0x000163b4, 0x00000000}, {0x000163b8, 0x00000000}, {0x000163bc, 0x00000000}, {0x000163c0, 0x000000a0}, {0x000163c4, 0x000c0000}, {0x000163c8, 0x14021402}, {0x000163cc, 0x00001402}, {0x000163d0, 0x00000000}, {0x000163d4, 0x00000000}, {0x00016400, 0x36db6db6}, {0x00016404, 0x6db6db40}, {0x00016408, 0x73f00000}, {0x0001640c, 0x00000000}, {0x00016440, 0x7f80fff8}, {0x00016444, 0x03b6d2db}, {0x00016448, 0x24927266}, {0x0001644c, 0x000f0278}, {0x00016450, 0x6db6db6c}, {0x00016454, 0x6db60000}, {0x00016500, 0x04cb0001}, {0x00016504, 0xfff80000}, {0x00016508, 0x00080010}, {0x0001650c, 0x00000000}, {0x00016540, 0x50804008}, {0x00016544, 0x01884080}, {0x00016548, 0x000080c0}, {0x00016780, 0x00000000}, {0x00016784, 0x00000000}, {0x00016788, 0x00800700}, {0x0001678c, 0x00800700}, {0x00016790, 0x00800700}, {0x00016794, 0x00000000}, {0x00016798, 0x00000000}, {0x0001679c, 0x00000000}, {0x000167a0, 0x00000001}, {0x000167a4, 0x00000001}, {0x000167a8, 0x00000000}, {0x000167ac, 0x00000000}, {0x000167b0, 0x00000000}, {0x000167b4, 0x00000000}, {0x000167b8, 0x00000000}, {0x000167bc, 0x00000000}, {0x000167c0, 0x000000a0}, {0x000167c4, 0x000c0000}, {0x000167c8, 0x14021402}, {0x000167cc, 0x00001402}, {0x000167d0, 0x00000000}, {0x000167d4, 0x00000000}, }; static const u32 ar9340_1p0_radio_core_40M[][2] = { /* Addr allmodes */ {0x0001609c, 0x02566f3a}, {0x000160ac, 0xa4647c00}, {0x000160b0, 0x01885f5a}, }; #define ar9340_1p0_mac_postamble ar9300_2p2_mac_postamble #define ar9340_1p0_soc_postamble ar9300_2p2_soc_postamble static const u32 ar9340_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a022e, 0x206a022e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec88d2e, 0x7ec88d2e}, {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x00003ec0, 0x00003ec4, 0x00003ec4, 0x00003ec0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00000220, 0x00000220, 0x00000110, 0x00000110}, {0x0000a28c, 0x00011111, 0x00011111, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00041983, 0x00041983, 0x00041982, 0x00041982}, {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000b284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, }; static const u32 ar9340_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a9f6b}, {0x0000980c, 0x04900000}, {0x00009814, 0x3280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x6400a190}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x14000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009c04, 0xff55ff55}, {0x00009c08, 0x0320ff55}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, {0x00009e24, 0x990bb515}, {0x00009e28, 0x0c6f0000}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0x00000000}, {0x00009fc0, 0x803e4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x00009fd0, 0x01193b93}, {0x0000a20c, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, {0x0000a2c0, 0x00000001}, {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0xaaaaaaaa}, {0x0000a3ac, 0x3c466478}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000000}, {0x0000a3f8, 0x0cdbd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00100000}, {0x0000a440, 0x00000000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x05000080}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a458, 0x00000000}, {0x0000a640, 0x00000000}, {0x0000a644, 0x3fad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, {0x0000a67c, 0x13121110}, {0x0000a680, 0x31301514}, {0x0000a684, 0x35343332}, {0x0000a688, 0x00000036}, {0x0000a690, 0x00000838}, {0x0000a7c0, 0x00000000}, {0x0000a7c4, 0xfffffffc}, {0x0000a7c8, 0x00000000}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000000}, {0x0000a8d0, 0x004b6a8e}, {0x0000a8d4, 0x00000820}, {0x0000a8dc, 0x00000000}, {0x0000a8f0, 0x00000000}, {0x0000a8f4, 0x00000000}, {0x0000b2d0, 0x00000080}, {0x0000b2d4, 0x00000000}, {0x0000b2ec, 0x00000000}, {0x0000b2f0, 0x00000000}, {0x0000b2f4, 0x00000000}, {0x0000b2f8, 0x00000000}, {0x0000b408, 0x0e79e5c0}, {0x0000b40c, 0x00820820}, {0x0000b420, 0x00000000}, }; static const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x02000001, 0x02000001}, {0x0000a508, 0x09002421, 0x09002421, 0x05000003, 0x05000003}, {0x0000a50c, 0x0d002621, 0x0d002621, 0x0a000005, 0x0a000005}, {0x0000a510, 0x13004620, 0x13004620, 0x0e000201, 0x0e000201}, {0x0000a514, 0x19004a20, 0x19004a20, 0x11000203, 0x11000203}, {0x0000a518, 0x1d004e20, 0x1d004e20, 0x14000401, 0x14000401}, {0x0000a51c, 0x21005420, 0x21005420, 0x18000403, 0x18000403}, {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000602, 0x1b000602}, {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000802, 0x1f000802}, {0x0000a528, 0x2f005e42, 0x2f005e42, 0x21000620, 0x21000620}, {0x0000a52c, 0x33005e44, 0x33005e44, 0x25000820, 0x25000820}, {0x0000a530, 0x38005e65, 0x38005e65, 0x29000822, 0x29000822}, {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2d000824, 0x2d000824}, {0x0000a538, 0x40005e6b, 0x40005e6b, 0x30000828, 0x30000828}, {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x3400082a, 0x3400082a}, {0x0000a540, 0x49005e72, 0x49005e72, 0x38000849, 0x38000849}, {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b000a2c, 0x3b000a2c}, {0x0000a548, 0x53005f12, 0x53005f12, 0x3e000e2b, 0x3e000e2b}, {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42000e2d, 0x42000e2d}, {0x0000a550, 0x5e025f12, 0x5e025f12, 0x4500124a, 0x4500124a}, {0x0000a554, 0x61027f12, 0x61027f12, 0x4900124c, 0x4900124c}, {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c00126c, 0x4c00126c}, {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x4f00128c, 0x4f00128c}, {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x52001290, 0x52001290}, {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, {0x0000a584, 0x04802222, 0x04802222, 0x02800001, 0x02800001}, {0x0000a588, 0x09802421, 0x09802421, 0x05800003, 0x05800003}, {0x0000a58c, 0x0d802621, 0x0d802621, 0x0a800005, 0x0a800005}, {0x0000a590, 0x13804620, 0x13804620, 0x0e800201, 0x0e800201}, {0x0000a594, 0x19804a20, 0x19804a20, 0x11800203, 0x11800203}, {0x0000a598, 0x1d804e20, 0x1d804e20, 0x14800401, 0x14800401}, {0x0000a59c, 0x21805420, 0x21805420, 0x18800403, 0x18800403}, {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800602, 0x1b800602}, {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800802, 0x1f800802}, {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x21800620, 0x21800620}, {0x0000a5ac, 0x33805e44, 0x33805e44, 0x25800820, 0x25800820}, {0x0000a5b0, 0x38805e65, 0x38805e65, 0x29800822, 0x29800822}, {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2d800824, 0x2d800824}, {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x30800828, 0x30800828}, {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x3480082a, 0x3480082a}, {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38800849, 0x38800849}, {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b800a2c, 0x3b800a2c}, {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e800e2b, 0x3e800e2b}, {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42800e2d, 0x42800e2d}, {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x4580124a, 0x4580124a}, {0x0000a5d4, 0x61827f12, 0x61827f12, 0x4980124c, 0x4980124c}, {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c80126c, 0x4c80126c}, {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x4f80128c, 0x4f80128c}, {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x52801290, 0x52801290}, {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, {0x00016044, 0x056db2db, 0x056db2db, 0x022492db, 0x022492db}, {0x00016048, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, {0x00016444, 0x056db2db, 0x056db2db, 0x022492db, 0x022492db}, {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, {0x00016048, 0x8e481666, 0x8e481666, 0x8e481266, 0x8e481266}, {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, {0x00016444, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, {0x00016448, 0x8e481666, 0x8e481666, 0x8e481266, 0x8e481266}, }; static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a00ae, 0x206a00ae}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec82d2e, 0x7ec82d2e}, {0x0000a2dc, 0xfef5d402, 0xfef5d402, 0xfdab5b52, 0xfdab5b52}, {0x0000a2e0, 0xfe896600, 0xfe896600, 0xfd339c84, 0xfd339c84}, {0x0000a2e4, 0xff01f800, 0xff01f800, 0xfec3e000, 0xfec3e000}, {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffc0000, 0xfffc0000}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83}, {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x00016044, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, {0x00016048, 0x8e480086, 0x8e480086, 0x8e480086, 0x8e480086}, {0x00016444, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, {0x00016448, 0x8e480086, 0x8e480086, 0x8e480086, 0x8e480086}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000b2dc, 0xfef5d402, 0xfef5d402, 0xfdab5b52, 0xfdab5b52}, {0x0000b2e0, 0xfe896600, 0xfe896600, 0xfd339c84, 0xfd339c84}, {0x0000b2e4, 0xff01f800, 0xff01f800, 0xfec3e000, 0xfec3e000}, {0x0000b2e8, 0xfffe0000, 0xfffe0000, 0xfffc0000, 0xfffc0000}, }; static const u32 ar9340Common_rx_gain_table_1p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x01910190}, {0x0000a030, 0x01930192}, {0x0000a034, 0x01950194}, {0x0000a038, 0x038a0196}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x22222229}, {0x0000a084, 0x1d1d1d1d}, {0x0000a088, 0x1d1d1d1d}, {0x0000a08c, 0x1d1d1d1d}, {0x0000a090, 0x171d1d1d}, {0x0000a094, 0x11111717}, {0x0000a098, 0x00030311}, {0x0000a09c, 0x00000000}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x23232323}, {0x0000b084, 0x21232323}, {0x0000b088, 0x19191c1e}, {0x0000b08c, 0x12141417}, {0x0000b090, 0x07070e0e}, {0x0000b094, 0x03030305}, {0x0000b098, 0x00000003}, {0x0000b09c, 0x00000000}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83}, {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, {0x0000a598, 0x21820220, 0x21820220, 0x16800402, 0x16800402}, {0x0000a59c, 0x27820223, 0x27820223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861}, {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81}, {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x47801a83, 0x47801a83}, {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x4a801c84, 0x4a801c84}, {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x4e801ce3, 0x4e801ce3}, {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x52801ce5, 0x52801ce5}, {0x0000a5dc, 0x7086308c, 0x7086308c, 0x56801ce9, 0x56801ce9}, {0x0000a5e0, 0x738a308a, 0x738a308a, 0x5a801ceb, 0x5a801ceb}, {0x0000a5e4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5e8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5ec, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5f0, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5f4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, {0x00016048, 0x24925666, 0x24925666, 0x24925266, 0x24925266}, {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, {0x00016288, 0xf0318000, 0xf0318000, 0xf0318000, 0xf0318000}, {0x00016444, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, {0x00016448, 0x24925666, 0x24925666, 0x24925266, 0x24925266}, }; static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x11000400, 0x11000400}, {0x0000a518, 0x21002220, 0x21002220, 0x15000402, 0x15000402}, {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000603, 0x1b000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x23000a04, 0x23000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x26000a20, 0x26000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2a000e20, 0x2a000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x2e000e22, 0x2e000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x31000e24, 0x31000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x34001640, 0x34001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38001660, 0x38001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3b001861, 0x3b001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x3e001a81, 0x3e001a81}, {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x42001a83, 0x42001a83}, {0x0000a550, 0x61024a6c, 0x61024a6c, 0x44001c84, 0x44001c84}, {0x0000a554, 0x66026a6c, 0x66026a6c, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x7002708c, 0x7002708c, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x7302b08a, 0x7302b08a, 0x54001ceb, 0x54001ceb}, {0x0000a564, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a568, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a570, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a574, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a578, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x11800400, 0x11800400}, {0x0000a598, 0x21820220, 0x21820220, 0x15800402, 0x15800402}, {0x0000a59c, 0x27820223, 0x27820223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800603, 0x1b800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800a02, 0x1f800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x23800a04, 0x23800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x26800a20, 0x26800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2a800e20, 0x2a800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2e800e22, 0x2e800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x31800e24, 0x31800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x34801640, 0x34801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38801660, 0x38801660}, {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3b801861, 0x3b801861}, {0x0000a5c8, 0x5782286c, 0x5782286c, 0x3e801a81, 0x3e801a81}, {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x42801a83, 0x42801a83}, {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x44801c84, 0x44801c84}, {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x48801ce3, 0x48801ce3}, {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x4c801ce5, 0x4c801ce5}, {0x0000a5dc, 0x7086308c, 0x7086308c, 0x50801ce9, 0x50801ce9}, {0x0000a5e0, 0x738a308a, 0x738a308a, 0x54801ceb, 0x54801ceb}, {0x0000a5e4, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5e8, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5ec, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5f0, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5f4, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x056db2db, 0x056db2db, 0x03b6d2e4, 0x03b6d2e4}, {0x00016048, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266}, {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, {0x00016288, 0x30318000, 0x30318000, 0x00318000, 0x00318000}, {0x00016444, 0x056db2db, 0x056db2db, 0x03b6d2e4, 0x03b6d2e4}, {0x00016448, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266}, }; static const u32 ar9340_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008010, 0x00080800}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c22}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00a00000}, {0x000080d8, 0x00400000}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x18486200}, {0x00008174, 0x33332210}, {0x00008178, 0x00000000}, {0x0000817c, 0x00020000}, {0x000081c0, 0x00000000}, {0x000081c4, 0x33332210}, {0x000081c8, 0x00000000}, {0x000081cc, 0x00000000}, {0x000081d4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f424}, {0x00008248, 0x00000800}, {0x0000824c, 0x0001e848}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x9d400010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000140}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xaa48105b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a0, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x000101ff}, }; #define ar9340Common_wo_xlna_rx_gain_table_1p0 ar9300Common_wo_xlna_rx_gain_table_2p2 static const u32 ar9340_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x00007008, 0x00000000}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, }; #endif /* INITVALS_9340_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h0000644000175000017500000002631112026211315025745 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9330_1P2_H #define INITVALS_9330_1P2_H static const u32 ar9331_modes_high_ob_db_tx_gain_1p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620}, {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641}, {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642}, {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644}, {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81}, {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83}, {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84}, {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3}, {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec}, {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec}, {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec}, {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03}, {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, }; #define ar9331_modes_high_power_tx_gain_1p2 ar9331_modes_high_ob_db_tx_gain_1p2 #define ar9331_modes_low_ob_db_tx_gain_1p2 ar9331_modes_high_power_tx_gain_1p2 #define ar9331_modes_lowest_ob_db_tx_gain_1p2 ar9331_modes_low_ob_db_tx_gain_1p2 static const u32 ar9331_1p2_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4}, {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e}, {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221}, {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222}, {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x00003fc0, 0x00003fc4, 0x00003fc4, 0x00003fc0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x0000a234, 0x00000fff, 0x00000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar9331_1p2_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73800000}, {0x0001600c, 0x00000000}, {0x00016040, 0x7f80fff8}, {0x00016044, 0x03d6d2db}, {0x00016048, 0x6c924268}, {0x0001604c, 0x000f0278}, {0x00016050, 0x4db6db8c}, {0x00016054, 0x6db60000}, {0x00016080, 0x00080000}, {0x00016084, 0x0e48048c}, {0x00016088, 0x14214514}, {0x0001608c, 0x119f081c}, {0x00016090, 0x24926490}, {0x00016098, 0xd411eb84}, {0x000160a0, 0xc2108ffe}, {0x000160a4, 0x812fc370}, {0x000160a8, 0x423c8000}, {0x000160ac, 0x24651800}, {0x000160b0, 0x03284f3e}, {0x000160b4, 0x92480040}, {0x000160c0, 0x006db6db}, {0x000160c4, 0x0186db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x6de6c300}, {0x000160d0, 0x14500820}, {0x00016100, 0x04cb0001}, {0x00016104, 0xfff80015}, {0x00016108, 0x00080010}, {0x0001610c, 0x00170000}, {0x00016140, 0x10804000}, {0x00016144, 0x01884080}, {0x00016148, 0x000080c0}, {0x00016280, 0x01000015}, {0x00016284, 0x14d20000}, {0x00016288, 0x00318000}, {0x0001628c, 0x50000000}, {0x00016290, 0x4b96210f}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00800700}, {0x0001638c, 0x00800700}, {0x00016390, 0x00800700}, {0x00016394, 0x00000000}, {0x00016398, 0x00000000}, {0x0001639c, 0x00000000}, {0x000163a0, 0x00000001}, {0x000163a4, 0x00000001}, {0x000163a8, 0x00000000}, {0x000163ac, 0x00000000}, {0x000163b0, 0x00000000}, {0x000163b4, 0x00000000}, {0x000163b8, 0x00000000}, {0x000163bc, 0x00000000}, {0x000163c0, 0x000000a0}, {0x000163c4, 0x000c0000}, {0x000163c8, 0x14021402}, {0x000163cc, 0x00001402}, {0x000163d0, 0x00000000}, {0x000163d4, 0x00000000}, }; #define ar9331_1p2_baseband_core_txfir_coeff_japan_2484 ar9331_1p1_baseband_core_txfir_coeff_japan_2484 #define ar9331_1p2_xtal_25M ar9331_1p1_xtal_25M #define ar9331_1p2_xtal_40M ar9331_1p1_xtal_40M #define ar9331_1p2_baseband_core ar9331_1p1_baseband_core #define ar9331_1p2_soc_postamble ar9331_1p1_soc_postamble #define ar9331_1p2_mac_postamble ar9331_1p1_mac_postamble #define ar9331_1p2_soc_preamble ar9331_1p1_soc_preamble #define ar9331_1p2_mac_core ar9331_1p1_mac_core #define ar9331_common_wo_xlna_rx_gain_1p2 ar9331_common_wo_xlna_rx_gain_1p1 #define ar9331_common_rx_gain_1p2 ar9485_common_rx_gain_1_1 #endif /* INITVALS_9330_1P2_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h0000644000175000017500000012603512026211315025750 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9330_1P1_H #define INITVALS_9330_1P1_H static const u32 ar9331_1p1_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4}, {0x00009e04, 0x00202020, 0x00202020, 0x00202020, 0x00202020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e}, {0x00009e14, 0x31365d5e, 0x3136605e, 0x3136605e, 0x31365d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221}, {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222}, {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x00003fc0, 0x00003fc4, 0x00003fc4, 0x00003fc0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x0000a234, 0x00000fff, 0x00000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52}, {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000}, {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000}, {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, {0x0000a52c, 0x41023e85, 0x41023e85, 0x2d000a20, 0x2d000a20}, {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000a22, 0x31000a22}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000a24, 0x35000a24}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x38000a43, 0x38000a43}, {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x3b000e42, 0x3b000e42}, {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x3f000e44, 0x3f000e44}, {0x0000a544, 0x6502feca, 0x6502feca, 0x42000e64, 0x42000e64}, {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x46000e66, 0x46000e66}, {0x0000a54c, 0x7203feca, 0x7203feca, 0x4a000ea6, 0x4a000ea6}, {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a560, 0x900fff0b, 0x900fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a564, 0x960fffcb, 0x960fffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802}, {0x0000a624, 0x03010a03, 0x03010a03, 0x03010a03, 0x03010a03}, {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a62c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a630, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a634, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a638, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a63c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x00016044, 0x034922db, 0x034922db, 0x034922db, 0x034922db}, {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000}, }; static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52}, {0x0000a2e0, 0xffb31c84, 0xffb31c84, 0xffb31c84, 0xffb31c84}, {0x0000a2e4, 0xff43e000, 0xff43e000, 0xff43e000, 0xff43e000}, {0x0000a2e8, 0xfffc0000, 0xfffc0000, 0xfffc0000, 0xfffc0000}, {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, {0x0000a52c, 0x41023e85, 0x41023e85, 0x3d001620, 0x3d001620}, {0x0000a530, 0x48023ec6, 0x48023ec6, 0x3f001621, 0x3f001621}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x42001640, 0x42001640}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x44001641, 0x44001641}, {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x46001642, 0x46001642}, {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49001644, 0x49001644}, {0x0000a544, 0x6502feca, 0x6502feca, 0x4c001a81, 0x4c001a81}, {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4f001a83, 0x4f001a83}, {0x0000a54c, 0x7203feca, 0x7203feca, 0x52001c84, 0x52001c84}, {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001ce3, 0x55001ce3}, {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x59001ce5, 0x59001ce5}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5d001ce9, 0x5d001ce9}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x64001eec, 0x64001eec}, {0x0000a560, 0x900fff0b, 0x900fff0b, 0x64001eec, 0x64001eec}, {0x0000a564, 0x960fffcb, 0x960fffcb, 0x64001eec, 0x64001eec}, {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec}, {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec}, {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec}, {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x64001eec, 0x64001eec}, {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802}, {0x0000a624, 0x0280ca03, 0x0280ca03, 0x0280ca03, 0x0280ca03}, {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a62c, 0x04015005, 0x04015005, 0x04015005, 0x04015005}, {0x0000a630, 0x04015005, 0x04015005, 0x04015005, 0x04015005}, {0x0000a634, 0x04015005, 0x04015005, 0x04015005, 0x04015005}, {0x0000a638, 0x04015005, 0x04015005, 0x04015005, 0x04015005}, {0x0000a63c, 0x04015005, 0x04015005, 0x04015005, 0x04015005}, }; static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52}, {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000}, {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000}, {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, {0x0000a52c, 0x41023e85, 0x41023e85, 0x2d000a20, 0x2d000a20}, {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000a22, 0x31000a22}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000a24, 0x35000a24}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x38000a43, 0x38000a43}, {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x3b000e42, 0x3b000e42}, {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x3f000e44, 0x3f000e44}, {0x0000a544, 0x6502feca, 0x6502feca, 0x42000e64, 0x42000e64}, {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x46000e66, 0x46000e66}, {0x0000a54c, 0x7203feca, 0x7203feca, 0x4a000ea6, 0x4a000ea6}, {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a560, 0x900fff0b, 0x900fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a564, 0x960fffcb, 0x960fffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802}, {0x0000a624, 0x03010a03, 0x03010a03, 0x03010a03, 0x03010a03}, {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a62c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a630, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a634, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a638, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a63c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x00016044, 0x034922db, 0x034922db, 0x034922db, 0x034922db}, {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000}, }; #define ar9331_1p1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 static const u32 ar9331_1p1_xtal_25M[][2] = { /* Addr allmodes */ {0x00007038, 0x000002f8}, {0x00008244, 0x0010f3d7}, {0x0000824c, 0x0001e7ae}, {0x0001609c, 0x0f508f29}, }; static const u32 ar9331_1p1_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73800000}, {0x0001600c, 0x00000000}, {0x00016040, 0x7f80fff8}, {0x00016044, 0x03db62db}, {0x00016048, 0x6c924268}, {0x0001604c, 0x000f0278}, {0x00016050, 0x4db6db8c}, {0x00016054, 0x6db60000}, {0x00016080, 0x00080000}, {0x00016084, 0x0e48048c}, {0x00016088, 0x14214514}, {0x0001608c, 0x119f081c}, {0x00016090, 0x24926490}, {0x00016098, 0xd411eb84}, {0x000160a0, 0xc2108ffe}, {0x000160a4, 0x812fc370}, {0x000160a8, 0x423c8000}, {0x000160ac, 0x24651800}, {0x000160b0, 0x03284f3e}, {0x000160b4, 0x92480040}, {0x000160c0, 0x006db6db}, {0x000160c4, 0x0186db60}, {0x000160c8, 0x6db4db6c}, {0x000160cc, 0x6de6c300}, {0x000160d0, 0x14500820}, {0x00016100, 0x04cb0001}, {0x00016104, 0xfff80015}, {0x00016108, 0x00080010}, {0x0001610c, 0x00170000}, {0x00016140, 0x10800000}, {0x00016144, 0x01884080}, {0x00016148, 0x000080c0}, {0x00016280, 0x01000015}, {0x00016284, 0x14d20000}, {0x00016288, 0x00318000}, {0x0001628c, 0x50000000}, {0x00016290, 0x4b96210f}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00800700}, {0x0001638c, 0x00800700}, {0x00016390, 0x00800700}, {0x00016394, 0x00000000}, {0x00016398, 0x00000000}, {0x0001639c, 0x00000000}, {0x000163a0, 0x00000001}, {0x000163a4, 0x00000001}, {0x000163a8, 0x00000000}, {0x000163ac, 0x00000000}, {0x000163b0, 0x00000000}, {0x000163b4, 0x00000000}, {0x000163b8, 0x00000000}, {0x000163bc, 0x00000000}, {0x000163c0, 0x000000a0}, {0x000163c4, 0x000c0000}, {0x000163c8, 0x14021402}, {0x000163cc, 0x00001402}, {0x000163d0, 0x00000000}, {0x000163d4, 0x00000000}, }; static const u32 ar9331_1p1_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000022, 0x00000022, 0x00000022, 0x00000022}, }; static const u32 ar9331_common_wo_xlna_rx_gain_1p1[][2] = { /* Addr allmodes */ {0x0000a000, 0x00060005}, {0x0000a004, 0x00810080}, {0x0000a008, 0x00830082}, {0x0000a00c, 0x00850084}, {0x0000a010, 0x01820181}, {0x0000a014, 0x01840183}, {0x0000a018, 0x01880185}, {0x0000a01c, 0x018a0189}, {0x0000a020, 0x02850284}, {0x0000a024, 0x02890288}, {0x0000a028, 0x028b028a}, {0x0000a02c, 0x03850384}, {0x0000a030, 0x03890388}, {0x0000a034, 0x038b038a}, {0x0000a038, 0x038d038c}, {0x0000a03c, 0x03910390}, {0x0000a040, 0x03930392}, {0x0000a044, 0x03950394}, {0x0000a048, 0x00000396}, {0x0000a04c, 0x00000000}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x28282828}, {0x0000a084, 0x28282828}, {0x0000a088, 0x28282828}, {0x0000a08c, 0x28282828}, {0x0000a090, 0x28282828}, {0x0000a094, 0x24242428}, {0x0000a098, 0x171e1e1e}, {0x0000a09c, 0x02020b0b}, {0x0000a0a0, 0x02020202}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x22072208}, {0x0000a0c4, 0x22052206}, {0x0000a0c8, 0x22032204}, {0x0000a0cc, 0x22012202}, {0x0000a0d0, 0x221f2200}, {0x0000a0d4, 0x221d221e}, {0x0000a0d8, 0x33023303}, {0x0000a0dc, 0x33003301}, {0x0000a0e0, 0x331e331f}, {0x0000a0e4, 0x4402331d}, {0x0000a0e8, 0x44004401}, {0x0000a0ec, 0x441e441f}, {0x0000a0f0, 0x55025503}, {0x0000a0f4, 0x55005501}, {0x0000a0f8, 0x551e551f}, {0x0000a0fc, 0x6602551d}, {0x0000a100, 0x66006601}, {0x0000a104, 0x661e661f}, {0x0000a108, 0x7703661d}, {0x0000a10c, 0x77017702}, {0x0000a110, 0x00007700}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x111f1100}, {0x0000a148, 0x111d111e}, {0x0000a14c, 0x111b111c}, {0x0000a150, 0x22032204}, {0x0000a154, 0x22012202}, {0x0000a158, 0x221f2200}, {0x0000a15c, 0x221d221e}, {0x0000a160, 0x33013302}, {0x0000a164, 0x331f3300}, {0x0000a168, 0x4402331e}, {0x0000a16c, 0x44004401}, {0x0000a170, 0x441e441f}, {0x0000a174, 0x55015502}, {0x0000a178, 0x551f5500}, {0x0000a17c, 0x6602551e}, {0x0000a180, 0x66006601}, {0x0000a184, 0x661e661f}, {0x0000a188, 0x7703661d}, {0x0000a18c, 0x77017702}, {0x0000a190, 0x00007700}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000296}, }; static const u32 ar9331_1p1_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a8f6b}, {0x0000980c, 0x04800000}, {0x00009814, 0x9280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x5f3ca3de}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x14750600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009c04, 0x00000000}, {0x00009c08, 0x03200000}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x1883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c00400}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038233c}, {0x00009e24, 0x9927b515}, {0x00009e28, 0x12ef0200}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009fc0, 0x803e4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x0000a20c, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, {0x0000a2c0, 0x00000001}, {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, {0x0000a2dc, 0x00000000}, {0x0000a2e0, 0x00000000}, {0x0000a2e4, 0x00000000}, {0x0000a2e8, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0xaaaaaaaa}, {0x0000a3ac, 0x3c466478}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000006}, {0x0000a3f8, 0x0cdbd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00000000}, {0x0000a440, 0x00000000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x04000000}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a458, 0x00000000}, {0x0000a640, 0x00000000}, {0x0000a644, 0x3fad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, {0x0000a67c, 0x13121110}, {0x0000a680, 0x31301514}, {0x0000a684, 0x35343332}, {0x0000a688, 0x00000036}, {0x0000a690, 0x00000838}, {0x0000a7c0, 0x00000000}, {0x0000a7c4, 0xfffffffc}, {0x0000a7c8, 0x00000000}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000001}, }; static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52}, {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000}, {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000}, {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, {0x0000a52c, 0x41023e85, 0x41023e85, 0x2d000a20, 0x2d000a20}, {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000a22, 0x31000a22}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000a24, 0x35000a24}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x38000a43, 0x38000a43}, {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x3b000e42, 0x3b000e42}, {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x3f000e44, 0x3f000e44}, {0x0000a544, 0x6502feca, 0x6502feca, 0x42000e64, 0x42000e64}, {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x46000e66, 0x46000e66}, {0x0000a54c, 0x7203feca, 0x7203feca, 0x4a000ea6, 0x4a000ea6}, {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a560, 0x900fff0b, 0x900fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a564, 0x960fffcb, 0x960fffcb, 0x4a000ea6, 0x4a000ea6}, {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802}, {0x0000a624, 0x03010a03, 0x03010a03, 0x03010a03, 0x03010a03}, {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a62c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a630, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a634, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a638, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x0000a63c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, {0x00016044, 0x034922db, 0x034922db, 0x034922db, 0x034922db}, {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000}, }; #define ar9331_1p1_mac_postamble ar9300_2p2_mac_postamble static const u32 ar9331_1p1_soc_preamble[][2] = { /* Addr allmodes */ {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000002f8}, }; static const u32 ar9331_1p1_xtal_40M[][2] = { /* Addr allmodes */ {0x00007038, 0x000004c2}, {0x00008244, 0x0010f400}, {0x0000824c, 0x0001e800}, {0x0001609c, 0x0b283f31}, }; static const u32 ar9331_1p1_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c20}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00a00000}, {0x000080d8, 0x00400000}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x18486200}, {0x00008174, 0x33332210}, {0x00008178, 0x00000000}, {0x0000817c, 0x00020000}, {0x000081c0, 0x00000000}, {0x000081c4, 0x33332210}, {0x000081c8, 0x00000000}, {0x000081cc, 0x00000000}, {0x000081d4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008248, 0x00000800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x9d400010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000140}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xaa48105b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a0, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x000301ff}, }; static const u32 ar9331_common_rx_gain_1p1[][2] = { /* Addr allmodes */ {0x00009e18, 0x05000000}, {0x0000a000, 0x00060005}, {0x0000a004, 0x00810080}, {0x0000a008, 0x00830082}, {0x0000a00c, 0x00850084}, {0x0000a010, 0x01820181}, {0x0000a014, 0x01840183}, {0x0000a018, 0x01880185}, {0x0000a01c, 0x018a0189}, {0x0000a020, 0x02850284}, {0x0000a024, 0x02890288}, {0x0000a028, 0x028b028a}, {0x0000a02c, 0x03850384}, {0x0000a030, 0x03890388}, {0x0000a034, 0x038b038a}, {0x0000a038, 0x038d038c}, {0x0000a03c, 0x03910390}, {0x0000a040, 0x03930392}, {0x0000a044, 0x03950394}, {0x0000a048, 0x00000396}, {0x0000a04c, 0x00000000}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x28282828}, {0x0000a084, 0x28282828}, {0x0000a088, 0x28282828}, {0x0000a08c, 0x28282828}, {0x0000a090, 0x28282828}, {0x0000a094, 0x24242428}, {0x0000a098, 0x171e1e1e}, {0x0000a09c, 0x02020b0b}, {0x0000a0a0, 0x02020202}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x22072208}, {0x0000a0c4, 0x22052206}, {0x0000a0c8, 0x22032204}, {0x0000a0cc, 0x22012202}, {0x0000a0d0, 0x221f2200}, {0x0000a0d4, 0x221d221e}, {0x0000a0d8, 0x33023303}, {0x0000a0dc, 0x33003301}, {0x0000a0e0, 0x331e331f}, {0x0000a0e4, 0x4402331d}, {0x0000a0e8, 0x44004401}, {0x0000a0ec, 0x441e441f}, {0x0000a0f0, 0x55025503}, {0x0000a0f4, 0x55005501}, {0x0000a0f8, 0x551e551f}, {0x0000a0fc, 0x6602551d}, {0x0000a100, 0x66006601}, {0x0000a104, 0x661e661f}, {0x0000a108, 0x7703661d}, {0x0000a10c, 0x77017702}, {0x0000a110, 0x00007700}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x111f1100}, {0x0000a148, 0x111d111e}, {0x0000a14c, 0x111b111c}, {0x0000a150, 0x22032204}, {0x0000a154, 0x22012202}, {0x0000a158, 0x221f2200}, {0x0000a15c, 0x221d221e}, {0x0000a160, 0x33013302}, {0x0000a164, 0x331f3300}, {0x0000a168, 0x4402331e}, {0x0000a16c, 0x44004401}, {0x0000a170, 0x441e441f}, {0x0000a174, 0x55015502}, {0x0000a178, 0x551f5500}, {0x0000a17c, 0x6602551e}, {0x0000a180, 0x66006601}, {0x0000a184, 0x661e661f}, {0x0000a188, 0x7703661d}, {0x0000a18c, 0x77017702}, {0x0000a190, 0x00007700}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000296}, }; static const u32 ar9331_common_tx_gain_offset1_1[][1] = { {0x00000000}, {0x00000003}, {0x00000000}, {0x00000000}, }; #endif /* INITVALS_9330_1P1_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_rtt.h0000644000175000017500000000237512026211315024244 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AR9003_RTT_H #define AR9003_RTT_H void ar9003_hw_rtt_enable(struct ath_hw *ah); void ar9003_hw_rtt_disable(struct ath_hw *ah); void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask); bool ar9003_hw_rtt_force_restore(struct ath_hw *ah); void ar9003_hw_rtt_load_hist(struct ath_hw *ah); void ar9003_hw_rtt_fill_hist(struct ath_hw *ah); void ar9003_hw_rtt_clear_hist(struct ath_hw *ah); bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_rtt.c0000644000175000017500000001300312026211315024225 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "hw-ops.h" #include "ar9003_phy.h" #include "ar9003_rtt.h" #define RTT_RESTORE_TIMEOUT 1000 #define RTT_ACCESS_TIMEOUT 100 #define RTT_BAD_VALUE 0x0bad0bad /* * RTT (Radio Retention Table) hardware implementation information * * There is an internal table (i.e. the rtt) for each chain (or bank). * Each table contains 6 entries and each entry is corresponding to * a specific calibration parameter as depicted below. * 0~2 - DC offset DAC calibration: loop, low, high (offsetI/Q_...) * 3 - Filter cal (filterfc) * 4 - RX gain settings * 5 - Peak detector offset calibration (agc_caldac) */ void ar9003_hw_rtt_enable(struct ath_hw *ah) { REG_WRITE(ah, AR_PHY_RTT_CTRL, 1); } void ar9003_hw_rtt_disable(struct ath_hw *ah) { REG_WRITE(ah, AR_PHY_RTT_CTRL, 0); } void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask) { REG_RMW_FIELD(ah, AR_PHY_RTT_CTRL, AR_PHY_RTT_CTRL_RESTORE_MASK, rtt_mask); } bool ar9003_hw_rtt_force_restore(struct ath_hw *ah) { if (!ath9k_hw_wait(ah, AR_PHY_RTT_CTRL, AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE, 0, RTT_RESTORE_TIMEOUT)) return false; REG_RMW_FIELD(ah, AR_PHY_RTT_CTRL, AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE, 1); if (!ath9k_hw_wait(ah, AR_PHY_RTT_CTRL, AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE, 0, RTT_RESTORE_TIMEOUT)) return false; return true; } static void ar9003_hw_rtt_load_hist_entry(struct ath_hw *ah, u8 chain, u32 index, u32 data28) { u32 val; val = SM(data28, AR_PHY_RTT_SW_RTT_TABLE_DATA); REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain), val); val = SM(0, AR_PHY_RTT_SW_RTT_TABLE_ACCESS) | SM(1, AR_PHY_RTT_SW_RTT_TABLE_WRITE) | SM(index, AR_PHY_RTT_SW_RTT_TABLE_ADDR); REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val); udelay(1); val |= SM(1, AR_PHY_RTT_SW_RTT_TABLE_ACCESS); REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val); udelay(1); if (!ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0, RTT_ACCESS_TIMEOUT)) return; val &= ~SM(1, AR_PHY_RTT_SW_RTT_TABLE_WRITE); REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val); udelay(1); ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0, RTT_ACCESS_TIMEOUT); } void ar9003_hw_rtt_load_hist(struct ath_hw *ah) { int chain, i; for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(ah->rxchainmask & (1 << chain))) continue; for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { ar9003_hw_rtt_load_hist_entry(ah, chain, i, ah->caldata->rtt_table[chain][i]); ath_dbg(ath9k_hw_common(ah), CALIBRATE, "Load RTT value at idx %d, chain %d: 0x%x\n", i, chain, ah->caldata->rtt_table[chain][i]); } } } static int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index) { u32 val; val = SM(0, AR_PHY_RTT_SW_RTT_TABLE_ACCESS) | SM(0, AR_PHY_RTT_SW_RTT_TABLE_WRITE) | SM(index, AR_PHY_RTT_SW_RTT_TABLE_ADDR); REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val); udelay(1); val |= SM(1, AR_PHY_RTT_SW_RTT_TABLE_ACCESS); REG_WRITE(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), val); udelay(1); if (!ath9k_hw_wait(ah, AR_PHY_RTT_TABLE_SW_INTF_B(chain), AR_PHY_RTT_SW_RTT_TABLE_ACCESS, 0, RTT_ACCESS_TIMEOUT)) return RTT_BAD_VALUE; val = MS(REG_READ(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain)), AR_PHY_RTT_SW_RTT_TABLE_DATA); return val; } void ar9003_hw_rtt_fill_hist(struct ath_hw *ah) { int chain, i; for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(ah->rxchainmask & (1 << chain))) continue; for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { ah->caldata->rtt_table[chain][i] = ar9003_hw_rtt_fill_hist_entry(ah, chain, i); ath_dbg(ath9k_hw_common(ah), CALIBRATE, "RTT value at idx %d, chain %d is: 0x%x\n", i, chain, ah->caldata->rtt_table[chain][i]); } } ah->caldata->rtt_done = true; } void ar9003_hw_rtt_clear_hist(struct ath_hw *ah) { int chain, i; for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if (!(ah->rxchainmask & (1 << chain))) continue; for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) ar9003_hw_rtt_load_hist_entry(ah, chain, i, 0); } if (ah->caldata) ah->caldata->rtt_done = false; } bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) { bool restore; if (!ah->caldata) return false; if (!ah->caldata->rtt_done) return false; ar9003_hw_rtt_enable(ah); ar9003_hw_rtt_set_mask(ah, 0x10); if (!ath9k_hw_rfbus_req(ah)) { ath_err(ath9k_hw_common(ah), "Could not stop baseband\n"); restore = false; goto fail; } ar9003_hw_rtt_load_hist(ah); restore = ar9003_hw_rtt_force_restore(ah); fail: ath9k_hw_rfbus_done(ah); ar9003_hw_rtt_disable(ah); return restore; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_phy.h0000644000175000017500000015353212026211315024235 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AR9003_PHY_H #define AR9003_PHY_H /* * Channel Register Map */ #define AR_CHAN_BASE 0x9800 #define AR_PHY_TIMING1 (AR_CHAN_BASE + 0x0) #define AR_PHY_TIMING2 (AR_CHAN_BASE + 0x4) #define AR_PHY_TIMING3 (AR_CHAN_BASE + 0x8) #define AR_PHY_TIMING4 (AR_CHAN_BASE + 0xc) #define AR_PHY_TIMING5 (AR_CHAN_BASE + 0x10) #define AR_PHY_TIMING6 (AR_CHAN_BASE + 0x14) #define AR_PHY_TIMING11 (AR_CHAN_BASE + 0x18) #define AR_PHY_SPUR_REG (AR_CHAN_BASE + 0x1c) #define AR_PHY_RX_IQCAL_CORR_B0 (AR_CHAN_BASE + 0xdc) #define AR_PHY_TX_IQCAL_CONTROL_3 (AR_CHAN_BASE + 0xb0) #define AR_PHY_TIMING11_SPUR_FREQ_SD 0x3FF00000 #define AR_PHY_TIMING11_SPUR_FREQ_SD_S 20 #define AR_PHY_TIMING11_SPUR_DELTA_PHASE 0x000FFFFF #define AR_PHY_TIMING11_SPUR_DELTA_PHASE_S 0 #define AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC 0x40000000 #define AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC_S 30 #define AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR 0x80000000 #define AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR_S 31 #define AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT 0x4000000 #define AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT_S 26 #define AR_PHY_SPUR_REG_ENABLE_MASK_PPM 0x20000 /* bins move with freq offset */ #define AR_PHY_SPUR_REG_ENABLE_MASK_PPM_S 17 #define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH 0x000000FF #define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH_S 0 #define AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI 0x00000100 #define AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI_S 8 #define AR_PHY_SPUR_REG_MASK_RATE_CNTL 0x03FC0000 #define AR_PHY_SPUR_REG_MASK_RATE_CNTL_S 18 #define AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN 0x20000000 #define AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN_S 29 #define AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN 0x80000000 #define AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN_S 31 #define AR_PHY_FIND_SIG_LOW (AR_CHAN_BASE + 0x20) #define AR_PHY_SFCORR (AR_CHAN_BASE + 0x24) #define AR_PHY_SFCORR_LOW (AR_CHAN_BASE + 0x28) #define AR_PHY_SFCORR_EXT (AR_CHAN_BASE + 0x2c) #define AR_PHY_EXT_CCA (AR_CHAN_BASE + 0x30) #define AR_PHY_RADAR_0 (AR_CHAN_BASE + 0x34) #define AR_PHY_RADAR_1 (AR_CHAN_BASE + 0x38) #define AR_PHY_RADAR_EXT (AR_CHAN_BASE + 0x3c) #define AR_PHY_MULTICHAIN_CTRL (AR_CHAN_BASE + 0x80) #define AR_PHY_PERCHAIN_CSD (AR_CHAN_BASE + 0x84) #define AR_PHY_TX_PHASE_RAMP_0 (AR_CHAN_BASE + 0xd0) #define AR_PHY_ADC_GAIN_DC_CORR_0 (AR_CHAN_BASE + 0xd4) #define AR_PHY_IQ_ADC_MEAS_0_B0 (AR_CHAN_BASE + 0xc0) #define AR_PHY_IQ_ADC_MEAS_1_B0 (AR_CHAN_BASE + 0xc4) #define AR_PHY_IQ_ADC_MEAS_2_B0 (AR_CHAN_BASE + 0xc8) #define AR_PHY_IQ_ADC_MEAS_3_B0 (AR_CHAN_BASE + 0xcc) /* The following registers changed position from AR9300 1.0 to AR9300 2.0 */ #define AR_PHY_TX_PHASE_RAMP_0_9300_10 (AR_CHAN_BASE + 0xd0 - 0x10) #define AR_PHY_ADC_GAIN_DC_CORR_0_9300_10 (AR_CHAN_BASE + 0xd4 - 0x10) #define AR_PHY_IQ_ADC_MEAS_0_B0_9300_10 (AR_CHAN_BASE + 0xc0 + 0x8) #define AR_PHY_IQ_ADC_MEAS_1_B0_9300_10 (AR_CHAN_BASE + 0xc4 + 0x8) #define AR_PHY_IQ_ADC_MEAS_2_B0_9300_10 (AR_CHAN_BASE + 0xc8 + 0x8) #define AR_PHY_IQ_ADC_MEAS_3_B0_9300_10 (AR_CHAN_BASE + 0xcc + 0x8) #define AR_PHY_TX_CRC (AR_CHAN_BASE + 0xa0) #define AR_PHY_TST_DAC_CONST (AR_CHAN_BASE + 0xa4) #define AR_PHY_SPUR_REPORT_0 (AR_CHAN_BASE + 0xa8) #define AR_PHY_CHAN_INFO_TAB_0 (AR_CHAN_BASE + 0x300) /* * Channel Field Definitions */ #define AR_PHY_TIMING2_USE_FORCE_PPM 0x00001000 #define AR_PHY_TIMING2_FORCE_PPM_VAL 0x00000fff #define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000 #define AR_PHY_TIMING3_DSC_MAN_S 17 #define AR_PHY_TIMING3_DSC_EXP 0x0001E000 #define AR_PHY_TIMING3_DSC_EXP_S 13 #define AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX 0xF000 #define AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX_S 12 #define AR_PHY_TIMING4_DO_CAL 0x10000 #define AR_PHY_TIMING4_ENABLE_PILOT_MASK 0x10000000 #define AR_PHY_TIMING4_ENABLE_PILOT_MASK_S 28 #define AR_PHY_TIMING4_ENABLE_CHAN_MASK 0x20000000 #define AR_PHY_TIMING4_ENABLE_CHAN_MASK_S 29 #define AR_PHY_TIMING4_ENABLE_SPUR_FILTER 0x40000000 #define AR_PHY_TIMING4_ENABLE_SPUR_FILTER_S 30 #define AR_PHY_TIMING4_ENABLE_SPUR_RSSI 0x80000000 #define AR_PHY_TIMING4_ENABLE_SPUR_RSSI_S 31 #define AR_PHY_NEW_ADC_GAIN_CORR_ENABLE 0x40000000 #define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000 #define AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001 #define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW 0x00003F00 #define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8 #define AR_PHY_SFCORR_LOW_M1_THRESH_LOW 0x001FC000 #define AR_PHY_SFCORR_LOW_M1_THRESH_LOW_S 14 #define AR_PHY_SFCORR_LOW_M2_THRESH_LOW 0x0FE00000 #define AR_PHY_SFCORR_LOW_M2_THRESH_LOW_S 21 #define AR_PHY_SFCORR_M2COUNT_THR 0x0000001F #define AR_PHY_SFCORR_M2COUNT_THR_S 0 #define AR_PHY_SFCORR_M1_THRESH 0x00FE0000 #define AR_PHY_SFCORR_M1_THRESH_S 17 #define AR_PHY_SFCORR_M2_THRESH 0x7F000000 #define AR_PHY_SFCORR_M2_THRESH_S 24 #define AR_PHY_SFCORR_EXT_M1_THRESH 0x0000007F #define AR_PHY_SFCORR_EXT_M1_THRESH_S 0 #define AR_PHY_SFCORR_EXT_M2_THRESH 0x00003F80 #define AR_PHY_SFCORR_EXT_M2_THRESH_S 7 #define AR_PHY_SFCORR_EXT_M1_THRESH_LOW 0x001FC000 #define AR_PHY_SFCORR_EXT_M1_THRESH_LOW_S 14 #define AR_PHY_SFCORR_EXT_M2_THRESH_LOW 0x0FE00000 #define AR_PHY_SFCORR_EXT_M2_THRESH_LOW_S 21 #define AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD 0x10000000 #define AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD_S 28 #define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28 #define AR_PHY_EXT_CCA_THRESH62 0x007F0000 #define AR_PHY_EXT_CCA_THRESH62_S 16 #define AR_PHY_EXT_MINCCA_PWR 0x01FF0000 #define AR_PHY_EXT_MINCCA_PWR_S 16 #define AR_PHY_EXT_CYCPWR_THR1 0x0000FE00L #define AR_PHY_EXT_CYCPWR_THR1_S 9 #define AR_PHY_TIMING5_CYCPWR_THR1 0x000000FE #define AR_PHY_TIMING5_CYCPWR_THR1_S 1 #define AR_PHY_TIMING5_CYCPWR_THR1_ENABLE 0x00000001 #define AR_PHY_TIMING5_CYCPWR_THR1_ENABLE_S 0 #define AR_PHY_TIMING5_CYCPWR_THR1A 0x007F0000 #define AR_PHY_TIMING5_CYCPWR_THR1A_S 16 #define AR_PHY_TIMING5_RSSI_THR1A (0x7F << 16) #define AR_PHY_TIMING5_RSSI_THR1A_S 16 #define AR_PHY_TIMING5_RSSI_THR1A_ENA (0x1 << 15) #define AR_PHY_RADAR_0_ENA 0x00000001 #define AR_PHY_RADAR_0_FFT_ENA 0x80000000 #define AR_PHY_RADAR_0_INBAND 0x0000003e #define AR_PHY_RADAR_0_INBAND_S 1 #define AR_PHY_RADAR_0_PRSSI 0x00000FC0 #define AR_PHY_RADAR_0_PRSSI_S 6 #define AR_PHY_RADAR_0_HEIGHT 0x0003F000 #define AR_PHY_RADAR_0_HEIGHT_S 12 #define AR_PHY_RADAR_0_RRSSI 0x00FC0000 #define AR_PHY_RADAR_0_RRSSI_S 18 #define AR_PHY_RADAR_0_FIRPWR 0x7F000000 #define AR_PHY_RADAR_0_FIRPWR_S 24 #define AR_PHY_RADAR_1_RELPWR_ENA 0x00800000 #define AR_PHY_RADAR_1_USE_FIR128 0x00400000 #define AR_PHY_RADAR_1_RELPWR_THRESH 0x003F0000 #define AR_PHY_RADAR_1_RELPWR_THRESH_S 16 #define AR_PHY_RADAR_1_BLOCK_CHECK 0x00008000 #define AR_PHY_RADAR_1_MAX_RRSSI 0x00004000 #define AR_PHY_RADAR_1_RELSTEP_CHECK 0x00002000 #define AR_PHY_RADAR_1_RELSTEP_THRESH 0x00001F00 #define AR_PHY_RADAR_1_RELSTEP_THRESH_S 8 #define AR_PHY_RADAR_1_MAXLEN 0x000000FF #define AR_PHY_RADAR_1_MAXLEN_S 0 #define AR_PHY_RADAR_EXT_ENA 0x00004000 #define AR_PHY_RADAR_DC_PWR_THRESH 0x007f8000 #define AR_PHY_RADAR_DC_PWR_THRESH_S 15 #define AR_PHY_RADAR_LB_DC_CAP 0x7f800000 #define AR_PHY_RADAR_LB_DC_CAP_S 23 #define AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW (0x3f << 6) #define AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW_S 6 #define AR_PHY_FIND_SIG_LOW_FIRPWR (0x7f << 12) #define AR_PHY_FIND_SIG_LOW_FIRPWR_S 12 #define AR_PHY_FIND_SIG_LOW_FIRPWR_SIGN_BIT 19 #define AR_PHY_FIND_SIG_LOW_RELSTEP 0x1f #define AR_PHY_FIND_SIG_LOW_RELSTEP_S 0 #define AR_PHY_FIND_SIG_LOW_RELSTEP_SIGN_BIT 5 #define AR_PHY_CHAN_INFO_TAB_S2_READ 0x00000008 #define AR_PHY_CHAN_INFO_TAB_S2_READ_S 3 #define AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF 0x0000007F #define AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF_S 0 #define AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF 0x00003F80 #define AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF_S 7 #define AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE 0x00004000 #define AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_Q_COFF 0x003f8000 #define AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_Q_COFF_S 15 #define AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_I_COFF 0x1fc00000 #define AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_I_COFF_S 22 /* * MRC Register Map */ #define AR_MRC_BASE 0x9c00 #define AR_PHY_TIMING_3A (AR_MRC_BASE + 0x0) #define AR_PHY_LDPC_CNTL1 (AR_MRC_BASE + 0x4) #define AR_PHY_LDPC_CNTL2 (AR_MRC_BASE + 0x8) #define AR_PHY_PILOT_SPUR_MASK (AR_MRC_BASE + 0xc) #define AR_PHY_CHAN_SPUR_MASK (AR_MRC_BASE + 0x10) #define AR_PHY_SGI_DELTA (AR_MRC_BASE + 0x14) #define AR_PHY_ML_CNTL_1 (AR_MRC_BASE + 0x18) #define AR_PHY_ML_CNTL_2 (AR_MRC_BASE + 0x1c) #define AR_PHY_TST_ADC (AR_MRC_BASE + 0x20) #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A 0x00000FE0 #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A_S 5 #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A 0x1F #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A_S 0 #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_B 0x00FE0000 #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_B_S 17 #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_B 0x0001F000 #define AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_B_S 12 #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A 0x00000FE0 #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A_S 5 #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A 0x1F #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A_S 0 #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_B 0x00FE0000 #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_B_S 17 #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_B 0x0001F000 #define AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_B_S 12 /* * MRC Feild Definitions */ #define AR_PHY_SGI_DSC_MAN 0x0007FFF0 #define AR_PHY_SGI_DSC_MAN_S 4 #define AR_PHY_SGI_DSC_EXP 0x0000000F #define AR_PHY_SGI_DSC_EXP_S 0 /* * BBB Register Map */ #define AR_BBB_BASE 0x9d00 /* * AGC Register Map */ #define AR_AGC_BASE 0x9e00 #define AR_PHY_SETTLING (AR_AGC_BASE + 0x0) #define AR_PHY_FORCEMAX_GAINS_0 (AR_AGC_BASE + 0x4) #define AR_PHY_GAINS_MINOFF0 (AR_AGC_BASE + 0x8) #define AR_PHY_DESIRED_SZ (AR_AGC_BASE + 0xc) #define AR_PHY_FIND_SIG (AR_AGC_BASE + 0x10) #define AR_PHY_AGC (AR_AGC_BASE + 0x14) #define AR_PHY_EXT_ATTEN_CTL_0 (AR_AGC_BASE + 0x18) #define AR_PHY_CCA_0 (AR_AGC_BASE + 0x1c) #define AR_PHY_EXT_CCA0 (AR_AGC_BASE + 0x20) #define AR_PHY_RESTART (AR_AGC_BASE + 0x24) /* * Antenna Diversity settings */ #define AR_PHY_MC_GAIN_CTRL (AR_AGC_BASE + 0x28) #define AR_ANT_DIV_CTRL_ALL 0x7e000000 #define AR_ANT_DIV_CTRL_ALL_S 25 #define AR_ANT_DIV_ENABLE 0x1000000 #define AR_ANT_DIV_ENABLE_S 24 #define AR_PHY_ANT_FAST_DIV_BIAS 0x00007e00 #define AR_PHY_ANT_FAST_DIV_BIAS_S 9 #define AR_PHY_ANT_DIV_LNADIV 0x01000000 #define AR_PHY_ANT_DIV_LNADIV_S 24 #define AR_PHY_ANT_DIV_ALT_LNACONF 0x06000000 #define AR_PHY_ANT_DIV_ALT_LNACONF_S 25 #define AR_PHY_ANT_DIV_MAIN_LNACONF 0x18000000 #define AR_PHY_ANT_DIV_MAIN_LNACONF_S 27 #define AR_PHY_ANT_DIV_ALT_GAINTB 0x20000000 #define AR_PHY_ANT_DIV_ALT_GAINTB_S 29 #define AR_PHY_ANT_DIV_MAIN_GAINTB 0x40000000 #define AR_PHY_ANT_DIV_MAIN_GAINTB_S 30 #define AR_PHY_ANT_DIV_LNA1_MINUS_LNA2 0x0 #define AR_PHY_ANT_DIV_LNA2 0x1 #define AR_PHY_ANT_DIV_LNA1 0x2 #define AR_PHY_ANT_DIV_LNA1_PLUS_LNA2 0x3 #define AR_PHY_EXTCHN_PWRTHR1 (AR_AGC_BASE + 0x2c) #define AR_PHY_EXT_CHN_WIN (AR_AGC_BASE + 0x30) #define AR_PHY_20_40_DET_THR (AR_AGC_BASE + 0x34) #define AR_PHY_RIFS_SRCH (AR_AGC_BASE + 0x38) #define AR_PHY_PEAK_DET_CTRL_1 (AR_AGC_BASE + 0x3c) #define AR_PHY_PEAK_DET_CTRL_2 (AR_AGC_BASE + 0x40) #define AR_PHY_RX_GAIN_BOUNDS_1 (AR_AGC_BASE + 0x44) #define AR_PHY_RX_GAIN_BOUNDS_2 (AR_AGC_BASE + 0x48) #define AR_PHY_RSSI_0 (AR_AGC_BASE + 0x180) #define AR_PHY_SPUR_CCK_REP0 (AR_AGC_BASE + 0x184) #define AR_PHY_CCK_DETECT (AR_AGC_BASE + 0x1c0) #define AR_FAST_DIV_ENABLE 0x2000 #define AR_FAST_DIV_ENABLE_S 13 #define AR_PHY_DAG_CTRLCCK (AR_AGC_BASE + 0x1c4) #define AR_PHY_IQCORR_CTRL_CCK (AR_AGC_BASE + 0x1c8) #define AR_PHY_CCK_SPUR_MIT (AR_AGC_BASE + 0x1cc) #define AR_PHY_CCK_SPUR_MIT_SPUR_RSSI_THR 0x000001fe #define AR_PHY_CCK_SPUR_MIT_SPUR_RSSI_THR_S 1 #define AR_PHY_CCK_SPUR_MIT_SPUR_FILTER_TYPE 0x60000000 #define AR_PHY_CCK_SPUR_MIT_SPUR_FILTER_TYPE_S 29 #define AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT 0x00000001 #define AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT_S 0 #define AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ 0x1ffffe00 #define AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ_S 9 #define AR_PHY_MRC_CCK_CTRL (AR_AGC_BASE + 0x1d0) #define AR_PHY_MRC_CCK_ENABLE 0x00000001 #define AR_PHY_MRC_CCK_ENABLE_S 0 #define AR_PHY_MRC_CCK_MUX_REG 0x00000002 #define AR_PHY_MRC_CCK_MUX_REG_S 1 #define AR_PHY_RX_OCGAIN (AR_AGC_BASE + 0x200) #define AR_PHY_CCA_NOM_VAL_9300_2GHZ -110 #define AR_PHY_CCA_NOM_VAL_9300_5GHZ -115 #define AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ -125 #define AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ -125 #define AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ -95 #define AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ -100 #define AR_PHY_CCA_NOM_VAL_9462_2GHZ -127 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_2GHZ -127 #define AR_PHY_CCA_NOM_VAL_9462_5GHZ -127 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_5GHZ -127 #define AR_PHY_CCA_NOM_VAL_9330_2GHZ -118 /* * AGC Field Definitions */ #define AR_PHY_EXT_ATTEN_CTL_RXTX_MARGIN 0x00FC0000 #define AR_PHY_EXT_ATTEN_CTL_RXTX_MARGIN_S 18 #define AR_PHY_EXT_ATTEN_CTL_BSW_MARGIN 0x00003C00 #define AR_PHY_EXT_ATTEN_CTL_BSW_MARGIN_S 10 #define AR_PHY_EXT_ATTEN_CTL_BSW_ATTEN 0x0000001F #define AR_PHY_EXT_ATTEN_CTL_BSW_ATTEN_S 0 #define AR_PHY_EXT_ATTEN_CTL_XATTEN2_MARGIN 0x003E0000 #define AR_PHY_EXT_ATTEN_CTL_XATTEN2_MARGIN_S 17 #define AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN 0x0001F000 #define AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN_S 12 #define AR_PHY_EXT_ATTEN_CTL_XATTEN2_DB 0x00000FC0 #define AR_PHY_EXT_ATTEN_CTL_XATTEN2_DB_S 6 #define AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB 0x0000003F #define AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB_S 0 #define AR_PHY_RXGAIN_TXRX_ATTEN 0x0003F000 #define AR_PHY_RXGAIN_TXRX_ATTEN_S 12 #define AR_PHY_RXGAIN_TXRX_RF_MAX 0x007C0000 #define AR_PHY_RXGAIN_TXRX_RF_MAX_S 18 #define AR9280_PHY_RXGAIN_TXRX_ATTEN 0x00003F80 #define AR9280_PHY_RXGAIN_TXRX_ATTEN_S 7 #define AR9280_PHY_RXGAIN_TXRX_MARGIN 0x001FC000 #define AR9280_PHY_RXGAIN_TXRX_MARGIN_S 14 #define AR_PHY_SETTLING_SWITCH 0x00003F80 #define AR_PHY_SETTLING_SWITCH_S 7 #define AR_PHY_DESIRED_SZ_ADC 0x000000FF #define AR_PHY_DESIRED_SZ_ADC_S 0 #define AR_PHY_DESIRED_SZ_PGA 0x0000FF00 #define AR_PHY_DESIRED_SZ_PGA_S 8 #define AR_PHY_DESIRED_SZ_TOT_DES 0x0FF00000 #define AR_PHY_DESIRED_SZ_TOT_DES_S 20 #define AR_PHY_MINCCA_PWR 0x1FF00000 #define AR_PHY_MINCCA_PWR_S 20 #define AR_PHY_CCA_THRESH62 0x0007F000 #define AR_PHY_CCA_THRESH62_S 12 #define AR9280_PHY_MINCCA_PWR 0x1FF00000 #define AR9280_PHY_MINCCA_PWR_S 20 #define AR9280_PHY_CCA_THRESH62 0x000FF000 #define AR9280_PHY_CCA_THRESH62_S 12 #define AR_PHY_EXT_CCA0_THRESH62 0x000000FF #define AR_PHY_EXT_CCA0_THRESH62_S 0 #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003F #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 #define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001FC0 #define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME_S 6 #define AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000 #define AR_PHY_DAG_CTRLCCK_EN_RSSI_THR 0x00000200 #define AR_PHY_DAG_CTRLCCK_EN_RSSI_THR_S 9 #define AR_PHY_DAG_CTRLCCK_RSSI_THR 0x0001FC00 #define AR_PHY_DAG_CTRLCCK_RSSI_THR_S 10 #define AR_PHY_RIFS_INIT_DELAY 0x3ff0000 #define AR_PHY_AGC_QUICK_DROP 0x03c00000 #define AR_PHY_AGC_QUICK_DROP_S 22 #define AR_PHY_AGC_COARSE_LOW 0x00007F80 #define AR_PHY_AGC_COARSE_LOW_S 7 #define AR_PHY_AGC_COARSE_HIGH 0x003F8000 #define AR_PHY_AGC_COARSE_HIGH_S 15 #define AR_PHY_AGC_COARSE_PWR_CONST 0x0000007F #define AR_PHY_AGC_COARSE_PWR_CONST_S 0 #define AR_PHY_FIND_SIG_FIRSTEP 0x0003F000 #define AR_PHY_FIND_SIG_FIRSTEP_S 12 #define AR_PHY_FIND_SIG_FIRPWR 0x03FC0000 #define AR_PHY_FIND_SIG_FIRPWR_S 18 #define AR_PHY_FIND_SIG_FIRPWR_SIGN_BIT 25 #define AR_PHY_FIND_SIG_RELPWR (0x1f << 6) #define AR_PHY_FIND_SIG_RELPWR_S 6 #define AR_PHY_FIND_SIG_RELPWR_SIGN_BIT 11 #define AR_PHY_FIND_SIG_RELSTEP 0x1f #define AR_PHY_FIND_SIG_RELSTEP_S 0 #define AR_PHY_FIND_SIG_RELSTEP_SIGN_BIT 5 #define AR_PHY_RESTART_DIV_GC 0x001C0000 #define AR_PHY_RESTART_DIV_GC_S 18 #define AR_PHY_RESTART_ENA 0x01 #define AR_PHY_DC_RESTART_DIS 0x40000000 #define AR_PHY_TPC_OLPC_GAIN_DELTA_PAL_ON 0xFF000000 #define AR_PHY_TPC_OLPC_GAIN_DELTA_PAL_ON_S 24 #define AR_PHY_TPC_OLPC_GAIN_DELTA 0x00FF0000 #define AR_PHY_TPC_OLPC_GAIN_DELTA_S 16 #define AR_PHY_TPC_6_ERROR_EST_MODE 0x03000000 #define AR_PHY_TPC_6_ERROR_EST_MODE_S 24 /* * SM Register Map */ #define AR_SM_BASE 0xa200 #define AR_PHY_D2_CHIP_ID (AR_SM_BASE + 0x0) #define AR_PHY_GEN_CTRL (AR_SM_BASE + 0x4) #define AR_PHY_MODE (AR_SM_BASE + 0x8) #define AR_PHY_ACTIVE (AR_SM_BASE + 0xc) #define AR_PHY_SPUR_MASK_A (AR_SM_BASE + 0x20) #define AR_PHY_SPUR_MASK_B (AR_SM_BASE + 0x24) #define AR_PHY_SPECTRAL_SCAN (AR_SM_BASE + 0x28) #define AR_PHY_RADAR_BW_FILTER (AR_SM_BASE + 0x2c) #define AR_PHY_SEARCH_START_DELAY (AR_SM_BASE + 0x30) #define AR_PHY_MAX_RX_LEN (AR_SM_BASE + 0x34) #define AR_PHY_FRAME_CTL (AR_SM_BASE + 0x38) #define AR_PHY_RFBUS_REQ (AR_SM_BASE + 0x3c) #define AR_PHY_RFBUS_GRANT (AR_SM_BASE + 0x40) #define AR_PHY_RIFS (AR_SM_BASE + 0x44) #define AR_PHY_RX_CLR_DELAY (AR_SM_BASE + 0x50) #define AR_PHY_RX_DELAY (AR_SM_BASE + 0x54) #define AR_PHY_XPA_TIMING_CTL (AR_SM_BASE + 0x64) #define AR_PHY_MISC_PA_CTL (AR_SM_BASE + 0x80) #define AR_PHY_SWITCH_CHAIN_0 (AR_SM_BASE + 0x84) #define AR_PHY_SWITCH_COM (AR_SM_BASE + 0x88) #define AR_PHY_SWITCH_COM_2 (AR_SM_BASE + 0x8c) #define AR_PHY_RX_CHAINMASK (AR_SM_BASE + 0xa0) #define AR_PHY_CAL_CHAINMASK (AR_SM_BASE + 0xc0) #define AR_PHY_CALMODE (AR_SM_BASE + 0xc8) #define AR_PHY_FCAL_1 (AR_SM_BASE + 0xcc) #define AR_PHY_FCAL_2_0 (AR_SM_BASE + 0xd0) #define AR_PHY_DFT_TONE_CTL_0 (AR_SM_BASE + 0xd4) #define AR_PHY_CL_CAL_CTL (AR_SM_BASE + 0xd8) #define AR_PHY_CL_TAB_0 (AR_SM_BASE + 0x100) #define AR_PHY_SYNTH_CONTROL (AR_SM_BASE + 0x140) #define AR_PHY_ADDAC_CLK_SEL (AR_SM_BASE + 0x144) #define AR_PHY_PLL_CTL (AR_SM_BASE + 0x148) #define AR_PHY_ANALOG_SWAP (AR_SM_BASE + 0x14c) #define AR_PHY_ADDAC_PARA_CTL (AR_SM_BASE + 0x150) #define AR_PHY_XPA_CFG (AR_SM_BASE + 0x158) #define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW 3 #define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW_S 0 #define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A 0x0001FC00 #define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A_S 10 #define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A 0x3FF #define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A_S 0 #define AR_PHY_TEST (AR_SM_BASE + 0x160) #define AR_PHY_TEST_BBB_OBS_SEL 0x780000 #define AR_PHY_TEST_BBB_OBS_SEL_S 19 #define AR_PHY_TEST_RX_OBS_SEL_BIT5_S 23 #define AR_PHY_TEST_RX_OBS_SEL_BIT5 (1 << AR_PHY_TEST_RX_OBS_SEL_BIT5_S) #define AR_PHY_TEST_CHAIN_SEL 0xC0000000 #define AR_PHY_TEST_CHAIN_SEL_S 30 #define AR_PHY_TEST_CTL_STATUS (AR_SM_BASE + 0x164) #define AR_PHY_TEST_CTL_TSTDAC_EN 0x1 #define AR_PHY_TEST_CTL_TSTDAC_EN_S 0 #define AR_PHY_TEST_CTL_TX_OBS_SEL 0x1C #define AR_PHY_TEST_CTL_TX_OBS_SEL_S 2 #define AR_PHY_TEST_CTL_TX_OBS_MUX_SEL 0x60 #define AR_PHY_TEST_CTL_TX_OBS_MUX_SEL_S 5 #define AR_PHY_TEST_CTL_TSTADC_EN 0x100 #define AR_PHY_TEST_CTL_TSTADC_EN_S 8 #define AR_PHY_TEST_CTL_RX_OBS_SEL 0x3C00 #define AR_PHY_TEST_CTL_RX_OBS_SEL_S 10 #define AR_PHY_TEST_CTL_DEBUGPORT_SEL 0xe0000000 #define AR_PHY_TEST_CTL_DEBUGPORT_SEL_S 29 #define AR_PHY_TSTDAC (AR_SM_BASE + 0x168) #define AR_PHY_CHAN_STATUS (AR_SM_BASE + 0x16c) #define AR_PHY_CHAN_INFO_MEMORY (AR_SM_BASE + 0x170) #define AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ 0x00000008 #define AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ_S 3 #define AR_PHY_CHNINFO_NOISEPWR (AR_SM_BASE + 0x174) #define AR_PHY_CHNINFO_GAINDIFF (AR_SM_BASE + 0x178) #define AR_PHY_CHNINFO_FINETIM (AR_SM_BASE + 0x17c) #define AR_PHY_CHAN_INFO_GAIN_0 (AR_SM_BASE + 0x180) #define AR_PHY_SCRAMBLER_SEED (AR_SM_BASE + 0x190) #define AR_PHY_CCK_TX_CTRL (AR_SM_BASE + 0x194) #define AR_PHY_HEAVYCLIP_CTL (AR_SM_BASE + 0x1a4) #define AR_PHY_HEAVYCLIP_20 (AR_SM_BASE + 0x1a8) #define AR_PHY_HEAVYCLIP_40 (AR_SM_BASE + 0x1ac) #define AR_PHY_ILLEGAL_TXRATE (AR_SM_BASE + 0x1b0) #define AR_PHY_POWER_TX_RATE(_d) (AR_SM_BASE + 0x1c0 + ((_d) << 2)) #define AR_PHY_PWRTX_MAX (AR_SM_BASE + 0x1f0) #define AR_PHY_POWER_TX_SUB (AR_SM_BASE + 0x1f4) #define AR_PHY_TPC_1 (AR_SM_BASE + 0x1f8) #define AR_PHY_TPC_1_FORCED_DAC_GAIN 0x0000003e #define AR_PHY_TPC_1_FORCED_DAC_GAIN_S 1 #define AR_PHY_TPC_1_FORCE_DAC_GAIN 0x00000001 #define AR_PHY_TPC_1_FORCE_DAC_GAIN_S 0 #define AR_PHY_TPC_4_B0 (AR_SM_BASE + 0x204) #define AR_PHY_TPC_5_B0 (AR_SM_BASE + 0x208) #define AR_PHY_TPC_6_B0 (AR_SM_BASE + 0x20c) #define AR_PHY_TPC_11_B0 (AR_SM_BASE + 0x220) #define AR_PHY_TPC_11_B1 (AR_SM1_BASE + 0x220) #define AR_PHY_TPC_11_B2 (AR_SM2_BASE + 0x220) #define AR_PHY_TPC_11_OLPC_GAIN_DELTA 0x00ff0000 #define AR_PHY_TPC_11_OLPC_GAIN_DELTA_S 16 #define AR_PHY_TPC_12 (AR_SM_BASE + 0x224) #define AR_PHY_TPC_12_DESIRED_SCALE_HT40_5 0x3e000000 #define AR_PHY_TPC_12_DESIRED_SCALE_HT40_5_S 25 #define AR_PHY_TPC_18 (AR_SM_BASE + 0x23c) #define AR_PHY_TPC_18_THERM_CAL_VALUE 0x000000ff #define AR_PHY_TPC_18_THERM_CAL_VALUE_S 0 #define AR_PHY_TPC_18_VOLT_CAL_VALUE 0x0000ff00 #define AR_PHY_TPC_18_VOLT_CAL_VALUE_S 8 #define AR_PHY_TPC_19 (AR_SM_BASE + 0x240) #define AR_PHY_TPC_19_ALPHA_VOLT 0x001f0000 #define AR_PHY_TPC_19_ALPHA_VOLT_S 16 #define AR_PHY_TPC_19_ALPHA_THERM 0xff #define AR_PHY_TPC_19_ALPHA_THERM_S 0 #define AR_PHY_TX_FORCED_GAIN (AR_SM_BASE + 0x258) #define AR_PHY_TX_FORCED_GAIN_FORCE_TX_GAIN 0x00000001 #define AR_PHY_TX_FORCED_GAIN_FORCE_TX_GAIN_S 0 #define AR_PHY_TX_FORCED_GAIN_FORCED_TXBB1DBGAIN 0x0000000e #define AR_PHY_TX_FORCED_GAIN_FORCED_TXBB1DBGAIN_S 1 #define AR_PHY_TX_FORCED_GAIN_FORCED_TXBB6DBGAIN 0x00000030 #define AR_PHY_TX_FORCED_GAIN_FORCED_TXBB6DBGAIN_S 4 #define AR_PHY_TX_FORCED_GAIN_FORCED_TXMXRGAIN 0x000003c0 #define AR_PHY_TX_FORCED_GAIN_FORCED_TXMXRGAIN_S 6 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNA 0x00003c00 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNA_S 10 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNB 0x0003c000 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNB_S 14 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNC 0x003c0000 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNC_S 18 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGND 0x00c00000 #define AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGND_S 22 #define AR_PHY_TX_FORCED_GAIN_FORCED_ENABLE_PAL 0x01000000 #define AR_PHY_TX_FORCED_GAIN_FORCED_ENABLE_PAL_S 24 #define AR_PHY_PDADC_TAB_0 (AR_SM_BASE + 0x280) #define AR_PHY_TXGAIN_TABLE (AR_SM_BASE + 0x300) #define AR_PHY_TX_IQCAL_CONTROL_0 (AR_SM_BASE + (AR_SREV_9485(ah) ? \ 0x3c4 : 0x444)) #define AR_PHY_TX_IQCAL_CONTROL_1 (AR_SM_BASE + (AR_SREV_9485(ah) ? \ 0x3c8 : 0x448)) #define AR_PHY_TX_IQCAL_START (AR_SM_BASE + (AR_SREV_9485(ah) ? \ 0x3c4 : 0x440)) #define AR_PHY_TX_IQCAL_STATUS_B0 (AR_SM_BASE + (AR_SREV_9485(ah) ? \ 0x3f0 : 0x48c)) #define AR_PHY_TX_IQCAL_CORR_COEFF_B0(_i) (AR_SM_BASE + \ (AR_SREV_9485(ah) ? \ 0x3d0 : 0x450) + ((_i) << 2)) #define AR_PHY_RTT_CTRL (AR_SM_BASE + 0x380) #define AR_PHY_WATCHDOG_STATUS (AR_SM_BASE + 0x5c0) #define AR_PHY_WATCHDOG_CTL_1 (AR_SM_BASE + 0x5c4) #define AR_PHY_WATCHDOG_CTL_2 (AR_SM_BASE + 0x5c8) #define AR_PHY_WATCHDOG_CTL (AR_SM_BASE + 0x5cc) #define AR_PHY_ONLY_WARMRESET (AR_SM_BASE + 0x5d0) #define AR_PHY_ONLY_CTL (AR_SM_BASE + 0x5d4) #define AR_PHY_ECO_CTRL (AR_SM_BASE + 0x5dc) #define AR_PHY_BB_THERM_ADC_1 (AR_SM_BASE + 0x248) #define AR_PHY_BB_THERM_ADC_1_INIT_THERM 0x000000ff #define AR_PHY_BB_THERM_ADC_1_INIT_THERM_S 0 #define AR_PHY_BB_THERM_ADC_3 (AR_SM_BASE + 0x250) #define AR_PHY_BB_THERM_ADC_3_THERM_ADC_SCALE_GAIN 0x0001ff00 #define AR_PHY_BB_THERM_ADC_3_THERM_ADC_SCALE_GAIN_S 8 #define AR_PHY_BB_THERM_ADC_3_THERM_ADC_OFFSET 0x000000ff #define AR_PHY_BB_THERM_ADC_3_THERM_ADC_OFFSET_S 0 #define AR_PHY_BB_THERM_ADC_4 (AR_SM_BASE + 0x254) #define AR_PHY_BB_THERM_ADC_4_LATEST_THERM_VALUE 0x000000ff #define AR_PHY_BB_THERM_ADC_4_LATEST_THERM_VALUE_S 0 #define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE 0x0000ff00 #define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE_S 8 /* AIC Registers */ #define AR_PHY_AIC_CTRL_0_B0 (AR_SM_BASE + 0x4b0) #define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4) #define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8) #define AR_PHY_AIC_CTRL_3_B0 (AR_SM_BASE + 0x4bc) #define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + 0x4c4)) #define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + 0x4c8)) #define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0) #define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc) #define AR_PHY_65NM_CH0_TXRF3 0x16048 #define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G 0x0000001e #define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G_S 1 #define AR_PHY_65NM_CH0_SYNTH4 0x1608c #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x00000001 : 0x00000002) #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0 : 1) #define AR_PHY_65NM_CH0_SYNTH7 0x16098 #define AR_PHY_65NM_CH0_BIAS1 0x160c0 #define AR_PHY_65NM_CH0_BIAS2 0x160c4 #define AR_PHY_65NM_CH0_BIAS4 0x160cc #define AR_PHY_65NM_CH0_RXTX4 0x1610c #define AR_PHY_65NM_CH1_RXTX4 0x1650c #define AR_PHY_65NM_CH2_RXTX4 0x1690c #define AR_CH0_TOP (AR_SREV_9300(ah) ? 0x16288 : \ (((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x1628c : 0x16280))) #define AR_CH0_TOP_XPABIASLVL (AR_SREV_9550(ah) ? 0x3c0 : 0x300) #define AR_CH0_TOP_XPABIASLVL_S (AR_SREV_9550(ah) ? 6 : 8) #define AR_CH0_THERM (AR_SREV_9300(ah) ? 0x16290 : \ ((AR_SREV_9485(ah) ? 0x1628c : 0x16294))) #define AR_CH0_THERM_XPABIASLVL_MSB 0x3 #define AR_CH0_THERM_XPABIASLVL_MSB_S 0 #define AR_CH0_THERM_XPASHORT2GND 0x4 #define AR_CH0_THERM_XPASHORT2GND_S 2 #define AR_SWITCH_TABLE_COM_ALL (0xffff) #define AR_SWITCH_TABLE_COM_ALL_S (0) #define AR_SWITCH_TABLE_COM_AR9462_ALL (0xffffff) #define AR_SWITCH_TABLE_COM_AR9462_ALL_S (0) #define AR_SWITCH_TABLE_COM_AR9550_ALL (0xffffff) #define AR_SWITCH_TABLE_COM_AR9550_ALL_S (0) #define AR_SWITCH_TABLE_COM_SPDT (0x00f00000) #define AR_SWITCH_TABLE_COM_SPDT_ALL (0x0000fff0) #define AR_SWITCH_TABLE_COM_SPDT_ALL_S (4) #define AR_SWITCH_TABLE_COM2_ALL (0xffffff) #define AR_SWITCH_TABLE_COM2_ALL_S (0) #define AR_SWITCH_TABLE_ALL (0xfff) #define AR_SWITCH_TABLE_ALL_S (0) #define AR_PHY_65NM_CH0_THERM (AR_SREV_9300(ah) ? 0x16290 :\ ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16294 : 0x1628c)) #define AR_PHY_65NM_CH0_THERM_LOCAL 0x80000000 #define AR_PHY_65NM_CH0_THERM_LOCAL_S 31 #define AR_PHY_65NM_CH0_THERM_START 0x20000000 #define AR_PHY_65NM_CH0_THERM_START_S 29 #define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT 0x0000ff00 #define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT_S 8 #define AR_PHY_65NM_CH0_RXTX1 0x16100 #define AR_PHY_65NM_CH0_RXTX2 0x16104 #define AR_PHY_65NM_CH1_RXTX1 0x16500 #define AR_PHY_65NM_CH1_RXTX2 0x16504 #define AR_PHY_65NM_CH2_RXTX1 0x16900 #define AR_PHY_65NM_CH2_RXTX2 0x16904 #define AR_CH0_TOP2 (AR_SREV_9300(ah) ? 0x1628c : \ (AR_SREV_9462(ah) ? 0x16290 : 0x16284)) #define AR_CH0_TOP2_XPABIASLVL 0xf000 #define AR_CH0_TOP2_XPABIASLVL_S 12 #define AR_CH0_XTAL (AR_SREV_9300(ah) ? 0x16294 : \ ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16298 : 0x16290)) #define AR_CH0_XTAL_CAPINDAC 0x7f000000 #define AR_CH0_XTAL_CAPINDAC_S 24 #define AR_CH0_XTAL_CAPOUTDAC 0x00fe0000 #define AR_CH0_XTAL_CAPOUTDAC_S 17 #define AR_PHY_PMU1 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16340 : 0x16c40) #define AR_PHY_PMU1_PWD 0x1 #define AR_PHY_PMU1_PWD_S 0 #define AR_PHY_PMU2 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16344 : 0x16c44) #define AR_PHY_PMU2_PGM 0x00200000 #define AR_PHY_PMU2_PGM_S 21 #define AR_PHY_RX1DB_BIQUAD_LONG_SHIFT 0x00380000 #define AR_PHY_RX1DB_BIQUAD_LONG_SHIFT_S 19 #define AR_PHY_RX6DB_BIQUAD_LONG_SHIFT 0x00c00000 #define AR_PHY_RX6DB_BIQUAD_LONG_SHIFT_S 22 #define AR_PHY_LNAGAIN_LONG_SHIFT 0xe0000000 #define AR_PHY_LNAGAIN_LONG_SHIFT_S 29 #define AR_PHY_MXRGAIN_LONG_SHIFT 0x03000000 #define AR_PHY_MXRGAIN_LONG_SHIFT_S 24 #define AR_PHY_VGAGAIN_LONG_SHIFT 0x1c000000 #define AR_PHY_VGAGAIN_LONG_SHIFT_S 26 #define AR_PHY_SCFIR_GAIN_LONG_SHIFT 0x00000001 #define AR_PHY_SCFIR_GAIN_LONG_SHIFT_S 0 #define AR_PHY_MANRXGAIN_LONG_SHIFT 0x00000002 #define AR_PHY_MANRXGAIN_LONG_SHIFT_S 1 /* * SM Field Definitions */ #define AR_PHY_CL_CAL_ENABLE 0x00000002 #define AR_PHY_PARALLEL_CAL_ENABLE 0x00000001 #define AR_PHY_TPCRG1_PD_CAL_ENABLE 0x00400000 #define AR_PHY_TPCRG1_PD_CAL_ENABLE_S 22 #define AR_PHY_ADDAC_PARACTL_OFF_PWDADC 0x00008000 #define AR_PHY_FCAL20_CAP_STATUS_0 0x01f00000 #define AR_PHY_FCAL20_CAP_STATUS_0_S 20 #define AR_PHY_RFBUS_REQ_EN 0x00000001 /* request for RF bus */ #define AR_PHY_RFBUS_GRANT_EN 0x00000001 /* RF bus granted */ #define AR_PHY_GC_TURBO_MODE 0x00000001 /* set turbo mode bits */ #define AR_PHY_GC_TURBO_SHORT 0x00000002 /* set short symbols to turbo mode setting */ #define AR_PHY_GC_DYN2040_EN 0x00000004 /* enable dyn 20/40 mode */ #define AR_PHY_GC_DYN2040_PRI_ONLY 0x00000008 /* dyn 20/40 - primary only */ #define AR_PHY_GC_DYN2040_PRI_CH 0x00000010 /* dyn 20/40 - primary ch offset (0=+10MHz, 1=-10MHz)*/ #define AR_PHY_GC_DYN2040_PRI_CH_S 4 #define AR_PHY_GC_DYN2040_EXT_CH 0x00000020 /* dyn 20/40 - ext ch spacing (0=20MHz/ 1=25MHz) */ #define AR_PHY_GC_HT_EN 0x00000040 /* ht enable */ #define AR_PHY_GC_SHORT_GI_40 0x00000080 /* allow short GI for HT 40 */ #define AR_PHY_GC_WALSH 0x00000100 /* walsh spatial spreading for 2 chains,2 streams TX */ #define AR_PHY_GC_SINGLE_HT_LTF1 0x00000200 /* single length (4us) 1st HT long training symbol */ #define AR_PHY_GC_GF_DETECT_EN 0x00000400 /* enable Green Field detection. Only affects rx, not tx */ #define AR_PHY_GC_ENABLE_DAC_FIFO 0x00000800 /* fifo between bb and dac */ #define AR_PHY_RX_DELAY_DELAY 0x00003FFF /* delay from wakeup to rx ena */ #define AR_PHY_CALMODE_IQ 0x00000000 #define AR_PHY_CALMODE_ADC_GAIN 0x00000001 #define AR_PHY_CALMODE_ADC_DC_PER 0x00000002 #define AR_PHY_CALMODE_ADC_DC_INIT 0x00000003 #define AR_PHY_SWAP_ALT_CHAIN 0x00000040 #define AR_PHY_MODE_OFDM 0x00000000 #define AR_PHY_MODE_CCK 0x00000001 #define AR_PHY_MODE_DYNAMIC 0x00000004 #define AR_PHY_MODE_DYNAMIC_S 2 #define AR_PHY_MODE_HALF 0x00000020 #define AR_PHY_MODE_QUARTER 0x00000040 #define AR_PHY_MAC_CLK_MODE 0x00000080 #define AR_PHY_MODE_DYN_CCK_DISABLE 0x00000100 #define AR_PHY_MODE_SVD_HALF 0x00000200 #define AR_PHY_ACTIVE_EN 0x00000001 #define AR_PHY_ACTIVE_DIS 0x00000000 #define AR_PHY_FORCE_XPA_CFG 0x000000001 #define AR_PHY_FORCE_XPA_CFG_S 0 #define AR_PHY_XPA_TIMING_CTL_TX_END_XPAB_OFF 0xFF000000 #define AR_PHY_XPA_TIMING_CTL_TX_END_XPAB_OFF_S 24 #define AR_PHY_XPA_TIMING_CTL_TX_END_XPAA_OFF 0x00FF0000 #define AR_PHY_XPA_TIMING_CTL_TX_END_XPAA_OFF_S 16 #define AR_PHY_XPA_TIMING_CTL_FRAME_XPAB_ON 0x0000FF00 #define AR_PHY_XPA_TIMING_CTL_FRAME_XPAB_ON_S 8 #define AR_PHY_XPA_TIMING_CTL_FRAME_XPAA_ON 0x000000FF #define AR_PHY_XPA_TIMING_CTL_FRAME_XPAA_ON_S 0 #define AR_PHY_TX_END_TO_A2_RX_ON 0x00FF0000 #define AR_PHY_TX_END_TO_A2_RX_ON_S 16 #define AR_PHY_TX_END_DATA_START 0x000000FF #define AR_PHY_TX_END_DATA_START_S 0 #define AR_PHY_TX_END_PA_ON 0x0000FF00 #define AR_PHY_TX_END_PA_ON_S 8 #define AR_PHY_TPCRG5_PD_GAIN_OVERLAP 0x0000000F #define AR_PHY_TPCRG5_PD_GAIN_OVERLAP_S 0 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1 0x000003F0 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S 4 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2 0x0000FC00 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S 10 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3 0x003F0000 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S 16 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4 0x0FC00000 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S 22 #define AR_PHY_TPCRG1_NUM_PD_GAIN 0x0000c000 #define AR_PHY_TPCRG1_NUM_PD_GAIN_S 14 #define AR_PHY_TPCRG1_PD_GAIN_1 0x00030000 #define AR_PHY_TPCRG1_PD_GAIN_1_S 16 #define AR_PHY_TPCRG1_PD_GAIN_2 0x000C0000 #define AR_PHY_TPCRG1_PD_GAIN_2_S 18 #define AR_PHY_TPCRG1_PD_GAIN_3 0x00300000 #define AR_PHY_TPCRG1_PD_GAIN_3_S 20 #define AR_PHY_TPCGR1_FORCED_DAC_GAIN 0x0000003e #define AR_PHY_TPCGR1_FORCED_DAC_GAIN_S 1 #define AR_PHY_TPCGR1_FORCE_DAC_GAIN 0x00000001 #define AR_PHY_TXGAIN_FORCE 0x00000001 #define AR_PHY_TXGAIN_FORCE_S 0 #define AR_PHY_TXGAIN_FORCED_PADVGNRA 0x00003c00 #define AR_PHY_TXGAIN_FORCED_PADVGNRA_S 10 #define AR_PHY_TXGAIN_FORCED_PADVGNRB 0x0003c000 #define AR_PHY_TXGAIN_FORCED_PADVGNRB_S 14 #define AR_PHY_TXGAIN_FORCED_PADVGNRD 0x00c00000 #define AR_PHY_TXGAIN_FORCED_PADVGNRD_S 22 #define AR_PHY_TXGAIN_FORCED_TXMXRGAIN 0x000003c0 #define AR_PHY_TXGAIN_FORCED_TXMXRGAIN_S 6 #define AR_PHY_TXGAIN_FORCED_TXBB1DBGAIN 0x0000000e #define AR_PHY_TXGAIN_FORCED_TXBB1DBGAIN_S 1 #define AR_PHY_POWER_TX_RATE1 0x9934 #define AR_PHY_POWER_TX_RATE2 0x9938 #define AR_PHY_POWER_TX_RATE_MAX 0x993c #define AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040 #define PHY_AGC_CLR 0x10000000 #define RFSILENT_BB 0x00002000 #define AR_PHY_CHAN_INFO_GAIN_DIFF_PPM_MASK 0xFFF #define AR_PHY_CHAN_INFO_GAIN_DIFF_PPM_SIGNED_BIT 0x800 #define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320 #define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001 #define AR_PHY_RX_DELAY_DELAY 0x00003FFF #define AR_PHY_CCK_TX_CTRL_JAPAN 0x00000010 #define AR_PHY_SPECTRAL_SCAN_ENABLE 0x00000001 #define AR_PHY_SPECTRAL_SCAN_ENABLE_S 0 #define AR_PHY_SPECTRAL_SCAN_ACTIVE 0x00000002 #define AR_PHY_SPECTRAL_SCAN_ACTIVE_S 1 #define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD 0x000000F0 #define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S 4 #define AR_PHY_SPECTRAL_SCAN_PERIOD 0x0000FF00 #define AR_PHY_SPECTRAL_SCAN_PERIOD_S 8 #define AR_PHY_SPECTRAL_SCAN_COUNT 0x0FFF0000 #define AR_PHY_SPECTRAL_SCAN_COUNT_S 16 #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x10000000 #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 28 #define AR_PHY_SPECTRAL_SCAN_PRIORITY 0x20000000 #define AR_PHY_SPECTRAL_SCAN_PRIORITY_S 29 #define AR_PHY_SPECTRAL_SCAN_USE_ERR5 0x40000000 #define AR_PHY_SPECTRAL_SCAN_USE_ERR5_S 30 #define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT 0x80000000 #define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT_S 31 #define AR_PHY_CHANNEL_STATUS_RX_CLEAR 0x00000004 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION 0x00000001 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION_S 0 #define AR_PHY_RTT_CTRL_RESTORE_MASK 0x0000007E #define AR_PHY_RTT_CTRL_RESTORE_MASK_S 1 #define AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE 0x00000080 #define AR_PHY_RTT_CTRL_FORCE_RADIO_RESTORE_S 7 #define AR_PHY_RTT_SW_RTT_TABLE_ACCESS 0x00000001 #define AR_PHY_RTT_SW_RTT_TABLE_ACCESS_S 0 #define AR_PHY_RTT_SW_RTT_TABLE_WRITE 0x00000002 #define AR_PHY_RTT_SW_RTT_TABLE_WRITE_S 1 #define AR_PHY_RTT_SW_RTT_TABLE_ADDR 0x0000001C #define AR_PHY_RTT_SW_RTT_TABLE_ADDR_S 2 #define AR_PHY_RTT_SW_RTT_TABLE_DATA 0xFFFFFFF0 #define AR_PHY_RTT_SW_RTT_TABLE_DATA_S 4 #define AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL 0x80000000 #define AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL_S 31 #define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT 0x01fc0000 #define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT_S 18 #define AR_PHY_TX_IQCAL_START_DO_CAL 0x00000001 #define AR_PHY_TX_IQCAL_START_DO_CAL_S 0 #define AR_PHY_TX_IQCAL_STATUS_FAILED 0x00000001 #define AR_PHY_CALIBRATED_GAINS_0 0x3e #define AR_PHY_CALIBRATED_GAINS_0_S 1 #define AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE 0x00003fff #define AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE_S 0 #define AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE 0x0fffc000 #define AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE_S 14 #define AR_PHY_65NM_CH0_RXTX4_THERM_ON 0x10000000 #define AR_PHY_65NM_CH0_RXTX4_THERM_ON_S 28 #define AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR 0x20000000 #define AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR_S 29 #define AR_PHY_65NM_RXTX4_XLNA_BIAS 0xC0000000 #define AR_PHY_65NM_RXTX4_XLNA_BIAS_S 30 /* * Channel 1 Register Map */ #define AR_CHAN1_BASE 0xa800 #define AR_PHY_EXT_CCA_1 (AR_CHAN1_BASE + 0x30) #define AR_PHY_TX_PHASE_RAMP_1 (AR_CHAN1_BASE + 0xd0) #define AR_PHY_ADC_GAIN_DC_CORR_1 (AR_CHAN1_BASE + 0xd4) #define AR_PHY_SPUR_REPORT_1 (AR_CHAN1_BASE + 0xa8) #define AR_PHY_CHAN_INFO_TAB_1 (AR_CHAN1_BASE + 0x300) #define AR_PHY_RX_IQCAL_CORR_B1 (AR_CHAN1_BASE + 0xdc) /* * Channel 1 Field Definitions */ #define AR_PHY_CH1_EXT_MINCCA_PWR 0x01FF0000 #define AR_PHY_CH1_EXT_MINCCA_PWR_S 16 /* * AGC 1 Register Map */ #define AR_AGC1_BASE 0xae00 #define AR_PHY_FORCEMAX_GAINS_1 (AR_AGC1_BASE + 0x4) #define AR_PHY_EXT_ATTEN_CTL_1 (AR_AGC1_BASE + 0x18) #define AR_PHY_CCA_1 (AR_AGC1_BASE + 0x1c) #define AR_PHY_CCA_CTRL_1 (AR_AGC1_BASE + 0x20) #define AR_PHY_RSSI_1 (AR_AGC1_BASE + 0x180) #define AR_PHY_SPUR_CCK_REP_1 (AR_AGC1_BASE + 0x184) #define AR_PHY_RX_OCGAIN_2 (AR_AGC1_BASE + 0x200) /* * AGC 1 Field Definitions */ #define AR_PHY_CH1_MINCCA_PWR 0x1FF00000 #define AR_PHY_CH1_MINCCA_PWR_S 20 /* * SM 1 Register Map */ #define AR_SM1_BASE 0xb200 #define AR_PHY_SWITCH_CHAIN_1 (AR_SM1_BASE + 0x84) #define AR_PHY_FCAL_2_1 (AR_SM1_BASE + 0xd0) #define AR_PHY_DFT_TONE_CTL_1 (AR_SM1_BASE + 0xd4) #define AR_PHY_CL_TAB_1 (AR_SM1_BASE + 0x100) #define AR_PHY_CHAN_INFO_GAIN_1 (AR_SM1_BASE + 0x180) #define AR_PHY_TPC_4_B1 (AR_SM1_BASE + 0x204) #define AR_PHY_TPC_5_B1 (AR_SM1_BASE + 0x208) #define AR_PHY_TPC_6_B1 (AR_SM1_BASE + 0x20c) #define AR_PHY_TPC_11_B1 (AR_SM1_BASE + 0x220) #define AR_PHY_PDADC_TAB_1 (AR_SM1_BASE + (AR_SREV_AR9462(ah) ? \ 0x280 : 0x240)) #define AR_PHY_TPC_19_B1 (AR_SM1_BASE + 0x240) #define AR_PHY_TPC_19_B1_ALPHA_THERM 0xff #define AR_PHY_TPC_19_B1_ALPHA_THERM_S 0 #define AR_PHY_TX_IQCAL_STATUS_B1 (AR_SM1_BASE + 0x48c) #define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i) (AR_SM1_BASE + 0x450 + ((_i) << 2)) /* SM 1 AIC Registers */ #define AR_PHY_AIC_CTRL_0_B1 (AR_SM1_BASE + 0x4b0) #define AR_PHY_AIC_CTRL_1_B1 (AR_SM1_BASE + 0x4b4) #define AR_PHY_AIC_CTRL_2_B1 (AR_SM1_BASE + 0x4b8) #define AR_PHY_AIC_STAT_0_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \ 0x4c0 : 0x4c4)) #define AR_PHY_AIC_STAT_1_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \ 0x4c4 : 0x4c8)) #define AR_PHY_AIC_CTRL_4_B1 (AR_SM1_BASE + 0x4c0) #define AR_PHY_AIC_STAT_2_B1 (AR_SM1_BASE + 0x4cc) #define AR_PHY_AIC_SRAM_ADDR_B1 (AR_SM1_BASE + 0x5f0) #define AR_PHY_AIC_SRAM_DATA_B1 (AR_SM1_BASE + 0x5f4) #define AR_PHY_RTT_TABLE_SW_INTF_B(i) (0x384 + ((i) ? \ AR_SM1_BASE : AR_SM_BASE)) #define AR_PHY_RTT_TABLE_SW_INTF_1_B(i) (0x388 + ((i) ? \ AR_SM1_BASE : AR_SM_BASE)) /* * Channel 2 Register Map */ #define AR_CHAN2_BASE 0xb800 #define AR_PHY_EXT_CCA_2 (AR_CHAN2_BASE + 0x30) #define AR_PHY_TX_PHASE_RAMP_2 (AR_CHAN2_BASE + 0xd0) #define AR_PHY_ADC_GAIN_DC_CORR_2 (AR_CHAN2_BASE + 0xd4) #define AR_PHY_SPUR_REPORT_2 (AR_CHAN2_BASE + 0xa8) #define AR_PHY_CHAN_INFO_TAB_2 (AR_CHAN2_BASE + 0x300) #define AR_PHY_RX_IQCAL_CORR_B2 (AR_CHAN2_BASE + 0xdc) /* * Channel 2 Field Definitions */ #define AR_PHY_CH2_EXT_MINCCA_PWR 0x01FF0000 #define AR_PHY_CH2_EXT_MINCCA_PWR_S 16 /* * AGC 2 Register Map */ #define AR_AGC2_BASE 0xbe00 #define AR_PHY_FORCEMAX_GAINS_2 (AR_AGC2_BASE + 0x4) #define AR_PHY_EXT_ATTEN_CTL_2 (AR_AGC2_BASE + 0x18) #define AR_PHY_CCA_2 (AR_AGC2_BASE + 0x1c) #define AR_PHY_CCA_CTRL_2 (AR_AGC2_BASE + 0x20) #define AR_PHY_RSSI_2 (AR_AGC2_BASE + 0x180) /* * AGC 2 Field Definitions */ #define AR_PHY_CH2_MINCCA_PWR 0x1FF00000 #define AR_PHY_CH2_MINCCA_PWR_S 20 /* * SM 2 Register Map */ #define AR_SM2_BASE 0xc200 #define AR_PHY_SWITCH_CHAIN_2 (AR_SM2_BASE + 0x84) #define AR_PHY_FCAL_2_2 (AR_SM2_BASE + 0xd0) #define AR_PHY_DFT_TONE_CTL_2 (AR_SM2_BASE + 0xd4) #define AR_PHY_CL_TAB_2 (AR_SM2_BASE + 0x100) #define AR_PHY_CHAN_INFO_GAIN_2 (AR_SM2_BASE + 0x180) #define AR_PHY_TPC_4_B2 (AR_SM2_BASE + 0x204) #define AR_PHY_TPC_5_B2 (AR_SM2_BASE + 0x208) #define AR_PHY_TPC_6_B2 (AR_SM2_BASE + 0x20c) #define AR_PHY_TPC_11_B2 (AR_SM2_BASE + 0x220) #define AR_PHY_PDADC_TAB_2 (AR_SM2_BASE + 0x240) #define AR_PHY_TX_IQCAL_STATUS_B2 (AR_SM2_BASE + 0x48c) #define AR_PHY_TX_IQCAL_CORR_COEFF_B2(_i) (AR_SM2_BASE + 0x450 + ((_i) << 2)) #define AR_PHY_TX_IQCAL_STATUS_B2_FAILED 0x00000001 /* * AGC 3 Register Map */ #define AR_AGC3_BASE 0xce00 #define AR_PHY_RSSI_3 (AR_AGC3_BASE + 0x180) /* GLB Registers */ #define AR_GLB_BASE 0x20000 #define AR_GLB_GPIO_CONTROL (AR_GLB_BASE) #define AR_PHY_GLB_CONTROL (AR_GLB_BASE + 0x44) #define AR_GLB_SCRATCH(_ah) (AR_GLB_BASE + \ (AR_SREV_9462_20(_ah) ? 0x4c : 0x50)) #define AR_GLB_STATUS (AR_GLB_BASE + 0x48) /* * Misc helper defines */ #define AR_PHY_CHAIN_OFFSET (AR_CHAN1_BASE - AR_CHAN_BASE) #define AR_PHY_NEW_ADC_DC_GAIN_CORR(_i) (AR_PHY_ADC_GAIN_DC_CORR_0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_NEW_ADC_DC_GAIN_CORR_9300_10(_i) (AR_PHY_ADC_GAIN_DC_CORR_0_9300_10 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_SWITCH_CHAIN(_i) (AR_PHY_SWITCH_CHAIN_0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_EXT_ATTEN_CTL(_i) (AR_PHY_EXT_ATTEN_CTL_0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_RXGAIN(_i) (AR_PHY_FORCEMAX_GAINS_0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_TPCRG5(_i) (AR_PHY_TPC_5_B0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_PDADC_TAB(_i) (AR_PHY_PDADC_TAB_0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_0(_i) (AR_PHY_IQ_ADC_MEAS_0_B0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_1(_i) (AR_PHY_IQ_ADC_MEAS_1_B0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_2(_i) (AR_PHY_IQ_ADC_MEAS_2_B0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_3(_i) (AR_PHY_IQ_ADC_MEAS_3_B0 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_0_9300_10(_i) (AR_PHY_IQ_ADC_MEAS_0_B0_9300_10 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_1_9300_10(_i) (AR_PHY_IQ_ADC_MEAS_1_B0_9300_10 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_2_9300_10(_i) (AR_PHY_IQ_ADC_MEAS_2_B0_9300_10 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_CAL_MEAS_3_9300_10(_i) (AR_PHY_IQ_ADC_MEAS_3_B0_9300_10 + (AR_PHY_CHAIN_OFFSET * (_i))) #define AR_PHY_WATCHDOG_NON_IDLE_ENABLE 0x00000001 #define AR_PHY_WATCHDOG_IDLE_ENABLE 0x00000002 #define AR_PHY_WATCHDOG_IDLE_MASK 0xFFFF0000 #define AR_PHY_WATCHDOG_NON_IDLE_MASK 0x0000FFFC #define AR_PHY_WATCHDOG_RST_ENABLE 0x00000002 #define AR_PHY_WATCHDOG_IRQ_ENABLE 0x00000004 #define AR_PHY_WATCHDOG_CNTL2_MASK 0xFFFFFFF9 #define AR_PHY_WATCHDOG_INFO 0x00000007 #define AR_PHY_WATCHDOG_INFO_S 0 #define AR_PHY_WATCHDOG_DET_HANG 0x00000008 #define AR_PHY_WATCHDOG_DET_HANG_S 3 #define AR_PHY_WATCHDOG_RADAR_SM 0x000000F0 #define AR_PHY_WATCHDOG_RADAR_SM_S 4 #define AR_PHY_WATCHDOG_RX_OFDM_SM 0x00000F00 #define AR_PHY_WATCHDOG_RX_OFDM_SM_S 8 #define AR_PHY_WATCHDOG_RX_CCK_SM 0x0000F000 #define AR_PHY_WATCHDOG_RX_CCK_SM_S 12 #define AR_PHY_WATCHDOG_TX_OFDM_SM 0x000F0000 #define AR_PHY_WATCHDOG_TX_OFDM_SM_S 16 #define AR_PHY_WATCHDOG_TX_CCK_SM 0x00F00000 #define AR_PHY_WATCHDOG_TX_CCK_SM_S 20 #define AR_PHY_WATCHDOG_AGC_SM 0x0F000000 #define AR_PHY_WATCHDOG_AGC_SM_S 24 #define AR_PHY_WATCHDOG_SRCH_SM 0xF0000000 #define AR_PHY_WATCHDOG_SRCH_SM_S 28 #define AR_PHY_WATCHDOG_STATUS_CLR 0x00000008 /* * PAPRD registers */ #define AR_PHY_XPA_TIMING_CTL (AR_SM_BASE + 0x64) #define AR_PHY_PAPRD_AM2AM (AR_CHAN_BASE + 0xe4) #define AR_PHY_PAPRD_AM2AM_MASK 0x01ffffff #define AR_PHY_PAPRD_AM2AM_MASK_S 0 #define AR_PHY_PAPRD_AM2PM (AR_CHAN_BASE + 0xe8) #define AR_PHY_PAPRD_AM2PM_MASK 0x01ffffff #define AR_PHY_PAPRD_AM2PM_MASK_S 0 #define AR_PHY_PAPRD_HT40 (AR_CHAN_BASE + 0xec) #define AR_PHY_PAPRD_HT40_MASK 0x01ffffff #define AR_PHY_PAPRD_HT40_MASK_S 0 #define AR_PHY_PAPRD_CTRL0_B0 (AR_CHAN_BASE + 0xf0) #define AR_PHY_PAPRD_CTRL0_B1 (AR_CHAN1_BASE + 0xf0) #define AR_PHY_PAPRD_CTRL0_B2 (AR_CHAN2_BASE + 0xf0) #define AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE 0x00000001 #define AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE_S 0 #define AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK 0x00000002 #define AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK_S 1 #define AR_PHY_PAPRD_CTRL0_PAPRD_MAG_THRSH 0xf8000000 #define AR_PHY_PAPRD_CTRL0_PAPRD_MAG_THRSH_S 27 #define AR_PHY_PAPRD_CTRL1_B0 (AR_CHAN_BASE + 0xf4) #define AR_PHY_PAPRD_CTRL1_B1 (AR_CHAN1_BASE + 0xf4) #define AR_PHY_PAPRD_CTRL1_B2 (AR_CHAN2_BASE + 0xf4) #define AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA 0x00000001 #define AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA_S 0 #define AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2AM_ENABLE 0x00000002 #define AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2AM_ENABLE_S 1 #define AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2PM_ENABLE 0x00000004 #define AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2PM_ENABLE_S 2 #define AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL 0x000001f8 #define AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL_S 3 #define AR_PHY_PAPRD_CTRL1_PA_GAIN_SCALE_FACT_MASK 0x0001fe00 #define AR_PHY_PAPRD_CTRL1_PA_GAIN_SCALE_FACT_MASK_S 9 #define AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT 0x0ffe0000 #define AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT_S 17 #define AR_PHY_PAPRD_TRAINER_CNTL1 (AR_SM_BASE + \ (AR_SREV_9485(ah) ? \ 0x580 : 0x490)) #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE 0x00000001 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE_S 0 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING 0x0000007e #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING_S 1 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_IQCORR_ENABLE 0x00000100 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_IQCORR_ENABLE_S 8 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_RX_BB_GAIN_FORCE 0x00000200 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_RX_BB_GAIN_FORCE_S 9 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_TX_GAIN_FORCE 0x00000400 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_TX_GAIN_FORCE_S 10 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_ENABLE 0x00000800 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_ENABLE_S 11 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP 0x0003f000 #define AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP_S 12 #define AR_PHY_PAPRD_TRAINER_CNTL2 (AR_SM_BASE + \ (AR_SREV_9485(ah) ? \ 0x584 : 0x494)) #define AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN 0xFFFFFFFF #define AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN_S 0 #define AR_PHY_PAPRD_TRAINER_CNTL3 (AR_SM_BASE + \ (AR_SREV_9485(ah) ? \ 0x588 : 0x498)) #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE 0x0000003f #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE_S 0 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP 0x00000fc0 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP_S 6 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL 0x0001f000 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL_S 12 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES 0x000e0000 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES_S 17 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_COARSE_CORR_LEN 0x00f00000 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_COARSE_CORR_LEN_S 20 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_FINE_CORR_LEN 0x0f000000 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_FINE_CORR_LEN_S 24 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE 0x20000000 #define AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE_S 29 #define AR_PHY_PAPRD_TRAINER_CNTL4 (AR_SM_BASE + \ (AR_SREV_9485(ah) ? \ 0x58c : 0x49c)) #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES 0x03ff0000 #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES_S 16 #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_SAFETY_DELTA 0x0000f000 #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_SAFETY_DELTA_S 12 #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_MIN_CORR 0x00000fff #define AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_MIN_CORR_S 0 #define AR_PHY_PAPRD_PRE_POST_SCALE_0_B0 (AR_CHAN_BASE + 0x100) #define AR_PHY_PAPRD_PRE_POST_SCALE_1_B0 (AR_CHAN_BASE + 0x104) #define AR_PHY_PAPRD_PRE_POST_SCALE_2_B0 (AR_CHAN_BASE + 0x108) #define AR_PHY_PAPRD_PRE_POST_SCALE_3_B0 (AR_CHAN_BASE + 0x10c) #define AR_PHY_PAPRD_PRE_POST_SCALE_4_B0 (AR_CHAN_BASE + 0x110) #define AR_PHY_PAPRD_PRE_POST_SCALE_5_B0 (AR_CHAN_BASE + 0x114) #define AR_PHY_PAPRD_PRE_POST_SCALE_6_B0 (AR_CHAN_BASE + 0x118) #define AR_PHY_PAPRD_PRE_POST_SCALE_7_B0 (AR_CHAN_BASE + 0x11c) #define AR_PHY_PAPRD_PRE_POST_SCALING 0x3FFFF #define AR_PHY_PAPRD_PRE_POST_SCALING_S 0 #define AR_PHY_PAPRD_TRAINER_STAT1 (AR_SM_BASE + 0x4a0) #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE 0x00000001 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE_S 0 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_INCOMPLETE 0x00000002 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_INCOMPLETE_S 1 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_CORR_ERR 0x00000004 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_CORR_ERR_S 2 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_ACTIVE 0x00000008 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_ACTIVE_S 3 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_RX_GAIN_IDX 0x000001f0 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_RX_GAIN_IDX_S 4 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR 0x0001fe00 #define AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR_S 9 #define AR_PHY_PAPRD_TRAINER_STAT2 (AR_SM_BASE + 0x4a4) #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_VAL 0x0000ffff #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_VAL_S 0 #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_COARSE_IDX 0x001f0000 #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_COARSE_IDX_S 16 #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_IDX 0x00600000 #define AR_PHY_PAPRD_TRAINER_STAT2_PAPRD_FINE_IDX_S 21 #define AR_PHY_PAPRD_TRAINER_STAT3 (AR_SM_BASE + 0x4a8) #define AR_PHY_PAPRD_TRAINER_STAT3_PAPRD_TRAIN_SAMPLES_CNT 0x000fffff #define AR_PHY_PAPRD_TRAINER_STAT3_PAPRD_TRAIN_SAMPLES_CNT_S 0 #define AR_PHY_PAPRD_MEM_TAB_B0 (AR_CHAN_BASE + 0x120) #define AR_PHY_PAPRD_MEM_TAB_B1 (AR_CHAN1_BASE + 0x120) #define AR_PHY_PAPRD_MEM_TAB_B2 (AR_CHAN2_BASE + 0x120) #define AR_PHY_PA_GAIN123_B0 (AR_CHAN_BASE + 0xf8) #define AR_PHY_PA_GAIN123_B1 (AR_CHAN1_BASE + 0xf8) #define AR_PHY_PA_GAIN123_B2 (AR_CHAN2_BASE + 0xf8) #define AR_PHY_PA_GAIN123_PA_GAIN1 0x3FF #define AR_PHY_PA_GAIN123_PA_GAIN1_S 0 #define AR_PHY_POWERTX_RATE5 (AR_SM_BASE + 0x1d0) #define AR_PHY_POWERTX_RATE5_POWERTXHT20_0 0x3F #define AR_PHY_POWERTX_RATE5_POWERTXHT20_0_S 0 #define AR_PHY_POWERTX_RATE6 (AR_SM_BASE + 0x1d4) #define AR_PHY_POWERTX_RATE6_POWERTXHT20_5 0x3F00 #define AR_PHY_POWERTX_RATE6_POWERTXHT20_5_S 8 #define AR_PHY_POWERTX_RATE8 (AR_SM_BASE + 0x1dc) #define AR_PHY_POWERTX_RATE8_POWERTXHT40_5 0x3F00 #define AR_PHY_POWERTX_RATE8_POWERTXHT40_5_S 8 #define AR_PHY_CL_TAB_CL_GAIN_MOD 0x1f #define AR_PHY_CL_TAB_CL_GAIN_MOD_S 0 #endif /* AR9003_PHY_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_phy.c0000644000175000017500000013004612026211315024223 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar9003_phy.h" static const int firstep_table[] = /* level: 0 1 2 3 4 5 6 7 8 */ { -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */ static const int cycpwrThr1_table[] = /* level: 0 1 2 3 4 5 6 7 8 */ { -6, -4, -2, 0, 2, 4, 6, 8 }; /* lvl 0-7, default 3 */ /* * register values to turn OFDM weak signal detection OFF */ static const int m1ThreshLow_off = 127; static const int m2ThreshLow_off = 127; static const int m1Thresh_off = 127; static const int m2Thresh_off = 127; static const int m2CountThr_off = 31; static const int m2CountThrLow_off = 63; static const int m1ThreshLowExt_off = 127; static const int m2ThreshLowExt_off = 127; static const int m1ThreshExt_off = 127; static const int m2ThreshExt_off = 127; /** * ar9003_hw_set_channel - set channel on single-chip device * @ah: atheros hardware structure * @chan: * * This is the function to change channel on single-chip devices, that is * for AR9300 family of chipsets. * * This function takes the channel value in MHz and sets * hardware channel value. Assumes writes have been enabled to analog bus. * * Actual Expression, * * For 2GHz channel, * Channel Frequency = (3/4) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17) * (freq_ref = 40MHz) * * For 5GHz channel, * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^10) * (freq_ref = 40MHz/(24>>amodeRefSel)) * * For 5GHz channels which are 5MHz spaced, * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17) * (freq_ref = 40MHz) */ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) { u16 bMode, fracMode = 0, aModeRefSel = 0; u32 freq, channelSel = 0, reg32 = 0; struct chan_centers centers; int loadSynthChannel; ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; if (freq < 4800) { /* 2 GHz, fractional mode */ if (AR_SREV_9330(ah)) { u32 chan_frac; u32 div; if (ah->is_clk_25mhz) div = 75; else div = 120; channelSel = (freq * 4) / div; chan_frac = (((freq * 4) % div) * 0x20000) / div; channelSel = (channelSel << 17) | chan_frac; } else if (AR_SREV_9485(ah) || AR_SREV_9565(ah)) { u32 chan_frac; /* * freq_ref = 40 / (refdiva >> amoderefsel); where refdiva=1 and amoderefsel=0 * ndiv = ((chan_mhz * 4) / 3) / freq_ref; * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000 */ channelSel = (freq * 4) / 120; chan_frac = (((freq * 4) % 120) * 0x20000) / 120; channelSel = (channelSel << 17) | chan_frac; } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) { if (ah->is_clk_25mhz) { u32 chan_frac; channelSel = (freq * 2) / 75; chan_frac = (((freq * 2) % 75) * 0x20000) / 75; channelSel = (channelSel << 17) | chan_frac; } else channelSel = CHANSEL_2G(freq) >> 1; } else channelSel = CHANSEL_2G(freq); /* Set to 2G mode */ bMode = 1; } else { if ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) && ah->is_clk_25mhz) { u32 chan_frac; channelSel = freq / 75; chan_frac = ((freq % 75) * 0x20000) / 75; channelSel = (channelSel << 17) | chan_frac; } else { channelSel = CHANSEL_5G(freq); /* Doubler is ON, so, divide channelSel by 2. */ channelSel >>= 1; } /* Set to 5G mode */ bMode = 0; } /* Enable fractional mode for all channels */ fracMode = 1; aModeRefSel = 0; loadSynthChannel = 0; reg32 = (bMode << 29); REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32); /* Enable Long shift Select for Synthesizer */ REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_SYNTH4, AR_PHY_SYNTH4_LONG_SHIFT_SELECT, 1); /* Program Synth. setting */ reg32 = (channelSel << 2) | (fracMode << 30) | (aModeRefSel << 28) | (loadSynthChannel << 31); REG_WRITE(ah, AR_PHY_65NM_CH0_SYNTH7, reg32); /* Toggle Load Synth channel bit */ loadSynthChannel = 1; reg32 = (channelSel << 2) | (fracMode << 30) | (aModeRefSel << 28) | (loadSynthChannel << 31); REG_WRITE(ah, AR_PHY_65NM_CH0_SYNTH7, reg32); ah->curchan = chan; return 0; } /** * ar9003_hw_spur_mitigate_mrc_cck - convert baseband spur frequency * @ah: atheros hardware structure * @chan: * * For single-chip solutions. Converts to baseband spur frequency given the * input channel frequency and compute register settings below. * * Spur mitigation for MRC CCK */ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah, struct ath9k_channel *chan) { static const u32 spur_freq[4] = { 2420, 2440, 2464, 2480 }; int cur_bb_spur, negative = 0, cck_spur_freq; int i; int range, max_spur_cnts, synth_freq; u8 *spur_fbin_ptr = ar9003_get_spur_chan_ptr(ah, IS_CHAN_2GHZ(chan)); /* * Need to verify range +/- 10 MHz in control channel, otherwise spur * is out-of-band and can be ignored. */ if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) || AR_SREV_9550(ah)) { if (spur_fbin_ptr[0] == 0) /* No spur */ return; max_spur_cnts = 5; if (IS_CHAN_HT40(chan)) { range = 19; if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0) synth_freq = chan->channel + 10; else synth_freq = chan->channel - 10; } else { range = 10; synth_freq = chan->channel; } } else { range = AR_SREV_9462(ah) ? 5 : 10; max_spur_cnts = 4; synth_freq = chan->channel; } for (i = 0; i < max_spur_cnts; i++) { if (AR_SREV_9462(ah) && (i == 0 || i == 3)) continue; negative = 0; if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) || AR_SREV_9550(ah)) cur_bb_spur = ath9k_hw_fbin2freq(spur_fbin_ptr[i], IS_CHAN_2GHZ(chan)); else cur_bb_spur = spur_freq[i]; cur_bb_spur -= synth_freq; if (cur_bb_spur < 0) { negative = 1; cur_bb_spur = -cur_bb_spur; } if (cur_bb_spur < range) { cck_spur_freq = (int)((cur_bb_spur << 19) / 11); if (negative == 1) cck_spur_freq = -cck_spur_freq; cck_spur_freq = cck_spur_freq & 0xfffff; REG_RMW_FIELD(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x7); REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_RSSI_THR, 0x7f); REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_FILTER_TYPE, 0x2); REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x1); REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, cck_spur_freq); return; } } REG_RMW_FIELD(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x5); REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x0); REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, 0x0); } /* Clean all spur register fields */ static void ar9003_hw_spur_ofdm_clear(struct ath_hw *ah) { REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0); REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, 0); REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, 0); REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, 0); REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0); REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, 0); REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0); REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 0); REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 0); REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0); REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0); REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0); REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, 0); REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, 0); REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, 0); REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0); REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0); REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0); REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0); } static void ar9003_hw_spur_ofdm(struct ath_hw *ah, int freq_offset, int spur_freq_sd, int spur_delta_phase, int spur_subchannel_sd, int range, int synth_freq) { int mask_index = 0; /* OFDM Spur mitigation */ REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0x1); REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, spur_freq_sd); REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, spur_delta_phase); REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, spur_subchannel_sd); REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0x1); if (!(AR_SREV_9565(ah) && range == 10 && synth_freq == 2437)) REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, 0x1); REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0x1); REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH, 34); REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 1); if (REG_READ_FIELD(ah, AR_PHY_MODE, AR_PHY_MODE_DYNAMIC) == 0x1) REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 1); mask_index = (freq_offset << 4) / 5; if (mask_index < 0) mask_index = mask_index - 1; mask_index = mask_index & 0x7f; REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0x1); REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0x1); REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0x1); REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, mask_index); REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, mask_index); REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, mask_index); REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0xc); REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0xc); REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0xa0); REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0xff); } static void ar9003_hw_spur_ofdm_9565(struct ath_hw *ah, int freq_offset) { int mask_index = 0; mask_index = (freq_offset << 4) / 5; if (mask_index < 0) mask_index = mask_index - 1; mask_index = mask_index & 0x7f; REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_B, mask_index); /* A == B */ REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_B, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, mask_index); REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_B, mask_index); REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_B, 0xe); REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_B, 0xe); /* A == B */ REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_B, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0xa0); } static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, struct ath9k_channel *chan, int freq_offset, int range, int synth_freq) { int spur_freq_sd = 0; int spur_subchannel_sd = 0; int spur_delta_phase = 0; if (IS_CHAN_HT40(chan)) { if (freq_offset < 0) { if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) spur_subchannel_sd = 1; else spur_subchannel_sd = 0; spur_freq_sd = ((freq_offset + 10) << 9) / 11; } else { if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) spur_subchannel_sd = 0; else spur_subchannel_sd = 1; spur_freq_sd = ((freq_offset - 10) << 9) / 11; } spur_delta_phase = (freq_offset << 17) / 5; } else { spur_subchannel_sd = 0; spur_freq_sd = (freq_offset << 9) /11; spur_delta_phase = (freq_offset << 18) / 5; } spur_freq_sd = spur_freq_sd & 0x3ff; spur_delta_phase = spur_delta_phase & 0xfffff; ar9003_hw_spur_ofdm(ah, freq_offset, spur_freq_sd, spur_delta_phase, spur_subchannel_sd, range, synth_freq); } /* Spur mitigation for OFDM */ static void ar9003_hw_spur_mitigate_ofdm(struct ath_hw *ah, struct ath9k_channel *chan) { int synth_freq; int range = 10; int freq_offset = 0; int mode; u8* spurChansPtr; unsigned int i; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; if (IS_CHAN_5GHZ(chan)) { spurChansPtr = &(eep->modalHeader5G.spurChans[0]); mode = 0; } else { spurChansPtr = &(eep->modalHeader2G.spurChans[0]); mode = 1; } if (spurChansPtr[0] == 0) return; /* No spur in the mode */ if (IS_CHAN_HT40(chan)) { range = 19; if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) synth_freq = chan->channel - 10; else synth_freq = chan->channel + 10; } else { range = 10; synth_freq = chan->channel; } ar9003_hw_spur_ofdm_clear(ah); for (i = 0; i < AR_EEPROM_MODAL_SPURS && spurChansPtr[i]; i++) { freq_offset = ath9k_hw_fbin2freq(spurChansPtr[i], mode); freq_offset -= synth_freq; if (abs(freq_offset) < range) { ar9003_hw_spur_ofdm_work(ah, chan, freq_offset, range, synth_freq); if (AR_SREV_9565(ah) && (i < 4)) { freq_offset = ath9k_hw_fbin2freq(spurChansPtr[i + 1], mode); freq_offset -= synth_freq; if (abs(freq_offset) < range) ar9003_hw_spur_ofdm_9565(ah, freq_offset); } break; } } } static void ar9003_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan) { if (!AR_SREV_9565(ah)) ar9003_hw_spur_mitigate_mrc_cck(ah, chan); ar9003_hw_spur_mitigate_ofdm(ah, chan); } static u32 ar9003_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { u32 pll; pll = SM(0x5, AR_RTC_9300_PLL_REFDIV); if (chan && IS_CHAN_HALF_RATE(chan)) pll |= SM(0x1, AR_RTC_9300_PLL_CLKSEL); else if (chan && IS_CHAN_QUARTER_RATE(chan)) pll |= SM(0x2, AR_RTC_9300_PLL_CLKSEL); pll |= SM(0x2c, AR_RTC_9300_PLL_DIV); return pll; } static void ar9003_hw_set_channel_regs(struct ath_hw *ah, struct ath9k_channel *chan) { u32 phymode; u32 enableDacFifo = 0; enableDacFifo = (REG_READ(ah, AR_PHY_GEN_CTRL) & AR_PHY_GC_ENABLE_DAC_FIFO); /* Enable 11n HT, 20 MHz */ phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 | AR_PHY_GC_SHORT_GI_40 | enableDacFifo; /* Configure baseband for dynamic 20/40 operation */ if (IS_CHAN_HT40(chan)) { phymode |= AR_PHY_GC_DYN2040_EN; /* Configure control (primary) channel at +-10MHz */ if ((chan->chanmode == CHANNEL_A_HT40PLUS) || (chan->chanmode == CHANNEL_G_HT40PLUS)) phymode |= AR_PHY_GC_DYN2040_PRI_CH; } /* make sure we preserve INI settings */ phymode |= REG_READ(ah, AR_PHY_GEN_CTRL); /* turn off Green Field detection for STA for now */ phymode &= ~AR_PHY_GC_GF_DETECT_EN; REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode); /* Configure MAC for 20/40 operation */ ath9k_hw_set11nmac2040(ah); /* global transmit timeout (25 TUs default)*/ REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); /* carrier sense timeout */ REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); } static void ar9003_hw_init_bb(struct ath_hw *ah, struct ath9k_channel *chan) { u32 synthDelay; /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; /* Activate the PHY (includes baseband activate + synthesizer on) */ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); ath9k_hw_synth_delay(ah, chan, synthDelay); } static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) { switch (rx) { case 0x5: REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); case 0x3: case 0x1: case 0x2: case 0x7: REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx); break; default: break; } if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7)) REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); else if (AR_SREV_9462(ah)) /* xxx only when MCI support is enabled */ REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); else REG_WRITE(ah, AR_SELFGEN_MASK, tx); if (tx == 0x5) { REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); } } /* * Override INI values with chip specific configuration. */ static void ar9003_hw_override_ini(struct ath_hw *ah) { u32 val; /* * Set the RX_ABORT and RX_DIS and clear it only after * RXE is set for MAC. This prevents frames with * corrupted descriptor status. */ REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); /* * For AR9280 and above, there is a new feature that allows * Multicast search based on both MAC Address and Key ID. By default, * this feature is enabled. But since the driver is not using this * feature, we switch it off; otherwise multicast search based on * MAC addr only will fail. */ val = REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE); REG_WRITE(ah, AR_PCU_MISC_MODE2, val | AR_AGG_WEP_ENABLE_FIX | AR_AGG_WEP_ENABLE); REG_SET_BIT(ah, AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); } static void ar9003_hw_prog_ini(struct ath_hw *ah, struct ar5416IniArray *iniArr, int column) { unsigned int i, regWrites = 0; /* New INI format: Array may be undefined (pre, core, post arrays) */ if (!iniArr->ia_array) return; /* * New INI format: Pre, core, and post arrays for a given subsystem * may be modal (> 2 columns) or non-modal (2 columns). Determine if * the array is non-modal and force the column to 1. */ if (column >= iniArr->ia_columns) column = 1; for (i = 0; i < iniArr->ia_rows; i++) { u32 reg = INI_RA(iniArr, i, 0); u32 val = INI_RA(iniArr, i, column); REG_WRITE(ah, reg, val); DO_DELAY(regWrites); } } static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah, struct ath9k_channel *chan) { int ret; switch (chan->chanmode) { case CHANNEL_A: case CHANNEL_A_HT20: if (chan->channel <= 5350) ret = 1; else if ((chan->channel > 5350) && (chan->channel <= 5600)) ret = 3; else ret = 5; break; case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: if (chan->channel <= 5350) ret = 2; else if ((chan->channel > 5350) && (chan->channel <= 5600)) ret = 4; else ret = 6; break; case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_B: ret = 8; break; case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: ret = 7; break; default: ret = -EINVAL; } return ret; } static int ar9003_hw_process_ini(struct ath_hw *ah, struct ath9k_channel *chan) { unsigned int regWrites = 0, i; u32 modesIndex; switch (chan->chanmode) { case CHANNEL_A: case CHANNEL_A_HT20: modesIndex = 1; break; case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: modesIndex = 2; break; case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_B: modesIndex = 4; break; case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: modesIndex = 3; break; default: return -EINVAL; } for (i = 0; i < ATH_INI_NUM_SPLIT; i++) { ar9003_hw_prog_ini(ah, &ah->iniSOC[i], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniMac[i], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniBB[i], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniRadio[i], modesIndex); if (i == ATH_INI_POST && AR_SREV_9462_20(ah)) ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant, modesIndex); } REG_WRITE_ARRAY(&ah->iniModesRxGain, 1, regWrites); if (AR_SREV_9550(ah)) REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex, regWrites); if (AR_SREV_9550(ah)) { int modes_txgain_index; modes_txgain_index = ar9550_hw_get_modes_txgain_index(ah, chan); if (modes_txgain_index < 0) return -EINVAL; REG_WRITE_ARRAY(&ah->iniModesTxGain, modes_txgain_index, regWrites); } else { REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); } /* * For 5GHz channels requiring Fast Clock, apply * different modal values. */ if (IS_CHAN_A_FAST_CLOCK(ah, chan)) REG_WRITE_ARRAY(&ah->iniModesFastClock, modesIndex, regWrites); REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); if (chan->channel == 2484) ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, AR_GLB_SWREG_DISCONT_EN_BT_WLAN); ah->modes_index = modesIndex; ar9003_hw_override_ini(ah); ar9003_hw_set_channel_regs(ah, chan); ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); ath9k_hw_apply_txpower(ah, chan, false); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) ah->enabled_cals |= TX_IQ_CAL; else ah->enabled_cals &= ~TX_IQ_CAL; if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) ah->enabled_cals |= TX_CL_CAL; else ah->enabled_cals &= ~TX_CL_CAL; } return 0; } static void ar9003_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan) { u32 rfMode = 0; if (chan == NULL) return; rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; if (IS_CHAN_A_FAST_CLOCK(ah, chan)) rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); if (IS_CHAN_QUARTER_RATE(chan)) rfMode |= AR_PHY_MODE_QUARTER; if (IS_CHAN_HALF_RATE(chan)) rfMode |= AR_PHY_MODE_HALF; if (rfMode & (AR_PHY_MODE_QUARTER | AR_PHY_MODE_HALF)) REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW, 3); REG_WRITE(ah, AR_PHY_MODE, rfMode); } static void ar9003_hw_mark_phy_inactive(struct ath_hw *ah) { REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); } static void ar9003_hw_set_delta_slope(struct ath_hw *ah, struct ath9k_channel *chan) { u32 coef_scaled, ds_coef_exp, ds_coef_man; u32 clockMhzScaled = 0x64000000; struct chan_centers centers; /* * half and quarter rate can divide the scaled clock by 2 or 4 * scale for selected channel bandwidth */ if (IS_CHAN_HALF_RATE(chan)) clockMhzScaled = clockMhzScaled >> 1; else if (IS_CHAN_QUARTER_RATE(chan)) clockMhzScaled = clockMhzScaled >> 2; /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ ath9k_hw_get_channel_centers(ah, chan, ¢ers); coef_scaled = clockMhzScaled / centers.synth_center; ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); /* * For Short GI, * scaled coeff is 9/10 that of normal coeff */ coef_scaled = (9 * coef_scaled) / 10; ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); /* for short gi */ REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_MAN, ds_coef_man); REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_EXP, ds_coef_exp); } static bool ar9003_hw_rfbus_req(struct ath_hw *ah) { REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); return ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN, AH_WAIT_TIMEOUT); } /* * Wait for the frequency synth to settle (synth goes on via PHY_ACTIVE_EN). * Read the phy active delay register. Value is in 100ns increments. */ static void ar9003_hw_rfbus_done(struct ath_hw *ah) { u32 synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; ath9k_hw_synth_delay(ah, ah->curchan, synthDelay); REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); } static bool ar9003_hw_ani_control(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ar5416AniState *aniState = &chan->ani; s32 value, value2; switch (cmd & ah->ani_function) { case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ /* * on == 1 means ofdm weak signal detection is ON * on == 1 is the default, for less noise immunity * * on == 0 means ofdm weak signal detection is OFF * on == 0 means more noise imm */ u32 on = param ? 1 : 0; if (on) REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); else REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); if (on != aniState->ofdmWeakSigDetect) { ath_dbg(common, ANI, "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; aniState->ofdmWeakSigDetect = on; } break; } case ATH9K_ANI_FIRSTEP_LEVEL:{ u32 level = param; if (level >= ARRAY_SIZE(firstep_table)) { ath_dbg(common, ANI, "ATH9K_ANI_FIRSTEP_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(firstep_table)); return false; } /* * make register setting relative to default * from INI file & cap value */ value = firstep_table[level] - firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstep; if (value < ATH9K_SIG_FIRSTEP_SETTING_MIN) value = ATH9K_SIG_FIRSTEP_SETTING_MIN; if (value > ATH9K_SIG_FIRSTEP_SETTING_MAX) value = ATH9K_SIG_FIRSTEP_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, value); /* * we need to set first step low register too * make register setting relative to default * from INI file & cap value */ value2 = firstep_table[level] - firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstepLow; if (value2 < ATH9K_SIG_FIRSTEP_SETTING_MIN) value2 = ATH9K_SIG_FIRSTEP_SETTING_MIN; if (value2 > ATH9K_SIG_FIRSTEP_SETTING_MAX) value2 = ATH9K_SIG_FIRSTEP_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW, value2); if (level != aniState->firstepLevel) { ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, level, ATH9K_ANI_FIRSTEP_LVL, value, aniState->iniDef.firstep); ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] firstep_low[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, level, ATH9K_ANI_FIRSTEP_LVL, value2, aniState->iniDef.firstepLow); if (level > aniState->firstepLevel) ah->stats.ast_ani_stepup++; else if (level < aniState->firstepLevel) ah->stats.ast_ani_stepdown++; aniState->firstepLevel = level; } break; } case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{ u32 level = param; if (level >= ARRAY_SIZE(cycpwrThr1_table)) { ath_dbg(common, ANI, "ATH9K_ANI_SPUR_IMMUNITY_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(cycpwrThr1_table)); return false; } /* * make register setting relative to default * from INI file & cap value */ value = cycpwrThr1_table[level] - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1; if (value < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value = ATH9K_SIG_SPUR_IMM_SETTING_MIN; if (value > ATH9K_SIG_SPUR_IMM_SETTING_MAX) value = ATH9K_SIG_SPUR_IMM_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, value); /* * set AR_PHY_EXT_CCA for extension channel * make register setting relative to default * from INI file & cap value */ value2 = cycpwrThr1_table[level] - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1Ext; if (value2 < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value2 = ATH9K_SIG_SPUR_IMM_SETTING_MIN; if (value2 > ATH9K_SIG_SPUR_IMM_SETTING_MAX) value2 = ATH9K_SIG_SPUR_IMM_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1, value2); if (level != aniState->spurImmunityLevel) { ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, level, ATH9K_ANI_SPUR_IMMUNE_LVL, value, aniState->iniDef.cycpwrThr1); ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1Ext[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, level, ATH9K_ANI_SPUR_IMMUNE_LVL, value2, aniState->iniDef.cycpwrThr1Ext); if (level > aniState->spurImmunityLevel) ah->stats.ast_ani_spurup++; else if (level < aniState->spurImmunityLevel) ah->stats.ast_ani_spurdown++; aniState->spurImmunityLevel = level; } break; } case ATH9K_ANI_MRC_CCK:{ /* * is_on == 1 means MRC CCK ON (default, less noise imm) * is_on == 0 means MRC CCK is OFF (more noise imm) */ bool is_on = param ? 1 : 0; REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_ENABLE, is_on); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_MUX_REG, is_on); if (is_on != aniState->mrcCCK) { ath_dbg(common, ANI, "** ch %d: MRC CCK: %s=>%s\n", chan->channel, aniState->mrcCCK ? "on" : "off", is_on ? "on" : "off"); if (is_on) ah->stats.ast_ani_ccklow++; else ah->stats.ast_ani_cckhigh++; aniState->mrcCCK = is_on; } break; } case ATH9K_ANI_PRESENT: break; default: ath_dbg(common, ANI, "invalid cmd %u\n", cmd); return false; } ath_dbg(common, ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, aniState->mrcCCK ? "on" : "off", aniState->listenTime, aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); return true; } static void ar9003_hw_do_getnf(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]) { #define AR_PHY_CH_MINCCA_PWR 0x1FF00000 #define AR_PHY_CH_MINCCA_PWR_S 20 #define AR_PHY_CH_EXT_MINCCA_PWR 0x01FF0000 #define AR_PHY_CH_EXT_MINCCA_PWR_S 16 int16_t nf; int i; for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (ah->rxchainmask & BIT(i)) { nf = MS(REG_READ(ah, ah->nf_regs[i]), AR_PHY_CH_MINCCA_PWR); nfarray[i] = sign_extend32(nf, 8); if (IS_CHAN_HT40(ah->curchan)) { u8 ext_idx = AR9300_MAX_CHAINS + i; nf = MS(REG_READ(ah, ah->nf_regs[ext_idx]), AR_PHY_CH_EXT_MINCCA_PWR); nfarray[ext_idx] = sign_extend32(nf, 8); } } } } static void ar9003_hw_set_nf_limits(struct ath_hw *ah) { ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9300_2GHZ; ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ; ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ; ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9300_5GHZ; if (AR_SREV_9330(ah)) ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9330_2GHZ; if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9462_2GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9462_2GHZ; ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9462_5GHZ; ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9462_5GHZ; } } /* * Initialize the ANI register values with default (ini) values. * This routine is called during a (full) hardware reset after * all the registers are initialised from the INI. */ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) { struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ath9k_ani_default *iniDef; u32 val; aniState = &ah->curchan->ani; iniDef = &aniState->iniDef; ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, chan->channel, chan->channelFlags); val = REG_READ(ah, AR_PHY_SFCORR); iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); iniDef->m2Thresh = MS(val, AR_PHY_SFCORR_M2_THRESH); iniDef->m2CountThr = MS(val, AR_PHY_SFCORR_M2COUNT_THR); val = REG_READ(ah, AR_PHY_SFCORR_LOW); iniDef->m1ThreshLow = MS(val, AR_PHY_SFCORR_LOW_M1_THRESH_LOW); iniDef->m2ThreshLow = MS(val, AR_PHY_SFCORR_LOW_M2_THRESH_LOW); iniDef->m2CountThrLow = MS(val, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW); val = REG_READ(ah, AR_PHY_SFCORR_EXT); iniDef->m1ThreshExt = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH); iniDef->m2ThreshExt = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH); iniDef->m1ThreshLowExt = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH_LOW); iniDef->m2ThreshLowExt = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH_LOW); iniDef->firstep = REG_READ_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP); iniDef->firstepLow = REG_READ_FIELD(ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW); iniDef->cycpwrThr1 = REG_READ_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1); iniDef->cycpwrThr1Ext = REG_READ_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1); /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCK = true; } static void ar9003_hw_set_radar_params(struct ath_hw *ah, struct ath_hw_radar_conf *conf) { u32 radar_0 = 0, radar_1 = 0; if (!conf) { REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); return; } radar_0 |= AR_PHY_RADAR_0_ENA | AR_PHY_RADAR_0_FFT_ENA; radar_0 |= SM(conf->fir_power, AR_PHY_RADAR_0_FIRPWR); radar_0 |= SM(conf->radar_rssi, AR_PHY_RADAR_0_RRSSI); radar_0 |= SM(conf->pulse_height, AR_PHY_RADAR_0_HEIGHT); radar_0 |= SM(conf->pulse_rssi, AR_PHY_RADAR_0_PRSSI); radar_0 |= SM(conf->pulse_inband, AR_PHY_RADAR_0_INBAND); radar_1 |= AR_PHY_RADAR_1_MAX_RRSSI; radar_1 |= AR_PHY_RADAR_1_BLOCK_CHECK; radar_1 |= SM(conf->pulse_maxlen, AR_PHY_RADAR_1_MAXLEN); radar_1 |= SM(conf->pulse_inband_step, AR_PHY_RADAR_1_RELSTEP_THRESH); radar_1 |= SM(conf->radar_inband, AR_PHY_RADAR_1_RELPWR_THRESH); REG_WRITE(ah, AR_PHY_RADAR_0, radar_0); REG_WRITE(ah, AR_PHY_RADAR_1, radar_1); if (conf->ext_channel) REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); else REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); } static void ar9003_hw_set_radar_conf(struct ath_hw *ah) { struct ath_hw_radar_conf *conf = &ah->radar_conf; conf->fir_power = -28; conf->radar_rssi = 0; conf->pulse_height = 10; conf->pulse_rssi = 24; conf->pulse_inband = 8; conf->pulse_maxlen = 255; conf->pulse_inband_step = 12; conf->radar_inband = 8; } static void ar9003_hw_antdiv_comb_conf_get(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf) { u32 regval; regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); antconf->main_lna_conf = (regval & AR_PHY_ANT_DIV_MAIN_LNACONF) >> AR_PHY_ANT_DIV_MAIN_LNACONF_S; antconf->alt_lna_conf = (regval & AR_PHY_ANT_DIV_ALT_LNACONF) >> AR_PHY_ANT_DIV_ALT_LNACONF_S; antconf->fast_div_bias = (regval & AR_PHY_ANT_FAST_DIV_BIAS) >> AR_PHY_ANT_FAST_DIV_BIAS_S; if (AR_SREV_9330_11(ah)) { antconf->lna1_lna2_delta = -9; antconf->div_group = 1; } else if (AR_SREV_9485(ah)) { antconf->lna1_lna2_delta = -9; antconf->div_group = 2; } else { antconf->lna1_lna2_delta = -3; antconf->div_group = 0; } } static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf) { u32 regval; regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF | AR_PHY_ANT_DIV_ALT_LNACONF | AR_PHY_ANT_FAST_DIV_BIAS | AR_PHY_ANT_DIV_MAIN_GAINTB | AR_PHY_ANT_DIV_ALT_GAINTB); regval |= ((antconf->main_lna_conf << AR_PHY_ANT_DIV_MAIN_LNACONF_S) & AR_PHY_ANT_DIV_MAIN_LNACONF); regval |= ((antconf->alt_lna_conf << AR_PHY_ANT_DIV_ALT_LNACONF_S) & AR_PHY_ANT_DIV_ALT_LNACONF); regval |= ((antconf->fast_div_bias << AR_PHY_ANT_FAST_DIV_BIAS_S) & AR_PHY_ANT_FAST_DIV_BIAS); regval |= ((antconf->main_gaintb << AR_PHY_ANT_DIV_MAIN_GAINTB_S) & AR_PHY_ANT_DIV_MAIN_GAINTB); regval |= ((antconf->alt_gaintb << AR_PHY_ANT_DIV_ALT_GAINTB_S) & AR_PHY_ANT_DIV_ALT_GAINTB); REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } static int ar9003_hw_fast_chan_change(struct ath_hw *ah, struct ath9k_channel *chan, u8 *ini_reloaded) { unsigned int regWrites = 0; u32 modesIndex; switch (chan->chanmode) { case CHANNEL_A: case CHANNEL_A_HT20: modesIndex = 1; break; case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: modesIndex = 2; break; case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_B: modesIndex = 4; break; case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: modesIndex = 3; break; default: return -EINVAL; } if (modesIndex == ah->modes_index) { *ini_reloaded = false; goto set_rfmode; } ar9003_hw_prog_ini(ah, &ah->iniSOC[ATH_INI_POST], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniMac[ATH_INI_POST], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniBB[ATH_INI_POST], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniRadio[ATH_INI_POST], modesIndex); if (AR_SREV_9462_20(ah)) ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant, modesIndex); REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); /* * For 5GHz channels requiring Fast Clock, apply * different modal values. */ if (IS_CHAN_A_FAST_CLOCK(ah, chan)) REG_WRITE_ARRAY(&ah->iniModesFastClock, modesIndex, regWrites); if (AR_SREV_9565(ah)) REG_WRITE_ARRAY(&ah->iniModesFastClock, 1, regWrites); REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); ah->modes_index = modesIndex; *ini_reloaded = true; set_rfmode: ar9003_hw_set_rfmode(ah, chan); return 0; } void ar9003_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); static const u32 ar9300_cca_regs[6] = { AR_PHY_CCA_0, AR_PHY_CCA_1, AR_PHY_CCA_2, AR_PHY_EXT_CCA, AR_PHY_EXT_CCA_1, AR_PHY_EXT_CCA_2, }; priv_ops->rf_set_freq = ar9003_hw_set_channel; priv_ops->spur_mitigate_freq = ar9003_hw_spur_mitigate; priv_ops->compute_pll_control = ar9003_hw_compute_pll_control; priv_ops->set_channel_regs = ar9003_hw_set_channel_regs; priv_ops->init_bb = ar9003_hw_init_bb; priv_ops->process_ini = ar9003_hw_process_ini; priv_ops->set_rfmode = ar9003_hw_set_rfmode; priv_ops->mark_phy_inactive = ar9003_hw_mark_phy_inactive; priv_ops->set_delta_slope = ar9003_hw_set_delta_slope; priv_ops->rfbus_req = ar9003_hw_rfbus_req; priv_ops->rfbus_done = ar9003_hw_rfbus_done; priv_ops->ani_control = ar9003_hw_ani_control; priv_ops->do_getnf = ar9003_hw_do_getnf; priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs; priv_ops->set_radar_params = ar9003_hw_set_radar_params; priv_ops->fast_chan_change = ar9003_hw_fast_chan_change; ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs)); } void ar9003_hw_bb_watchdog_config(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 idle_tmo_ms = ah->bb_watchdog_timeout_ms; u32 val, idle_count; if (!idle_tmo_ms) { /* disable IRQ, disable chip-reset for BB panic */ REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_2, REG_READ(ah, AR_PHY_WATCHDOG_CTL_2) & ~(AR_PHY_WATCHDOG_RST_ENABLE | AR_PHY_WATCHDOG_IRQ_ENABLE)); /* disable watchdog in non-IDLE mode, disable in IDLE mode */ REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_1, REG_READ(ah, AR_PHY_WATCHDOG_CTL_1) & ~(AR_PHY_WATCHDOG_NON_IDLE_ENABLE | AR_PHY_WATCHDOG_IDLE_ENABLE)); ath_dbg(common, RESET, "Disabled BB Watchdog\n"); return; } /* enable IRQ, disable chip-reset for BB watchdog */ val = REG_READ(ah, AR_PHY_WATCHDOG_CTL_2) & AR_PHY_WATCHDOG_CNTL2_MASK; REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_2, (val | AR_PHY_WATCHDOG_IRQ_ENABLE) & ~AR_PHY_WATCHDOG_RST_ENABLE); /* bound limit to 10 secs */ if (idle_tmo_ms > 10000) idle_tmo_ms = 10000; /* * The time unit for watchdog event is 2^15 44/88MHz cycles. * * For HT20 we have a time unit of 2^15/44 MHz = .74 ms per tick * For HT40 we have a time unit of 2^15/88 MHz = .37 ms per tick * * Given we use fast clock now in 5 GHz, these time units should * be common for both 2 GHz and 5 GHz. */ idle_count = (100 * idle_tmo_ms) / 74; if (ah->curchan && IS_CHAN_HT40(ah->curchan)) idle_count = (100 * idle_tmo_ms) / 37; /* * enable watchdog in non-IDLE mode, disable in IDLE mode, * set idle time-out. */ REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_1, AR_PHY_WATCHDOG_NON_IDLE_ENABLE | AR_PHY_WATCHDOG_IDLE_MASK | (AR_PHY_WATCHDOG_NON_IDLE_MASK & (idle_count << 2))); ath_dbg(common, RESET, "Enabled BB Watchdog timeout (%u ms)\n", idle_tmo_ms); } void ar9003_hw_bb_watchdog_read(struct ath_hw *ah) { /* * we want to avoid printing in ISR context so we save the * watchdog status to be printed later in bottom half context. */ ah->bb_watchdog_last_status = REG_READ(ah, AR_PHY_WATCHDOG_STATUS); /* * the watchdog timer should reset on status read but to be sure * sure we write 0 to the watchdog status bit. */ REG_WRITE(ah, AR_PHY_WATCHDOG_STATUS, ah->bb_watchdog_last_status & ~AR_PHY_WATCHDOG_STATUS_CLR); } void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 status; if (likely(!(common->debug_mask & ATH_DBG_RESET))) return; status = ah->bb_watchdog_last_status; ath_dbg(common, RESET, "\n==== BB update: BB status=0x%08x ====\n", status); ath_dbg(common, RESET, "** BB state: wd=%u det=%u rdar=%u rOFDM=%d rCCK=%u tOFDM=%u tCCK=%u agc=%u src=%u **\n", MS(status, AR_PHY_WATCHDOG_INFO), MS(status, AR_PHY_WATCHDOG_DET_HANG), MS(status, AR_PHY_WATCHDOG_RADAR_SM), MS(status, AR_PHY_WATCHDOG_RX_OFDM_SM), MS(status, AR_PHY_WATCHDOG_RX_CCK_SM), MS(status, AR_PHY_WATCHDOG_TX_OFDM_SM), MS(status, AR_PHY_WATCHDOG_TX_CCK_SM), MS(status, AR_PHY_WATCHDOG_AGC_SM), MS(status, AR_PHY_WATCHDOG_SRCH_SM)); ath_dbg(common, RESET, "** BB WD cntl: cntl1=0x%08x cntl2=0x%08x **\n", REG_READ(ah, AR_PHY_WATCHDOG_CTL_1), REG_READ(ah, AR_PHY_WATCHDOG_CTL_2)); ath_dbg(common, RESET, "** BB mode: BB_gen_controls=0x%08x **\n", REG_READ(ah, AR_PHY_GEN_CTRL)); #define PCT(_field) (common->cc_survey._field * 100 / common->cc_survey.cycles) if (common->cc_survey.cycles) ath_dbg(common, RESET, "** BB busy times: rx_clear=%d%%, rx_frame=%d%%, tx_frame=%d%% **\n", PCT(rx_busy), PCT(rx_frame), PCT(tx_frame)); ath_dbg(common, RESET, "==== BB update: done ====\n\n"); } EXPORT_SYMBOL(ar9003_hw_bb_watchdog_dbg_info); void ar9003_hw_disable_phy_restart(struct ath_hw *ah) { u32 val; /* While receiving unsupported rate frame rx state machine * gets into a state 0xb and if phy_restart happens in that * state, BB would go hang. If RXSM is in 0xb state after * first bb panic, ensure to disable the phy_restart. */ if (!((MS(ah->bb_watchdog_last_status, AR_PHY_WATCHDOG_RX_OFDM_SM) == 0xb) || ah->bb_hang_rx_ofdm)) return; ah->bb_hang_rx_ofdm = true; val = REG_READ(ah, AR_PHY_RESTART); val &= ~AR_PHY_RESTART_ENA; REG_WRITE(ah, AR_PHY_RESTART, val); } EXPORT_SYMBOL(ar9003_hw_disable_phy_restart); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_paprd.c0000644000175000017500000006620312026211315024534 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar9003_phy.h" void ar9003_paprd_enable(struct ath_hw *ah, bool val) { struct ath9k_channel *chan = ah->curchan; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; /* * 3 bits for modalHeader5G.papdRateMaskHt20 * is used for sub-band disabling of PAPRD. * 5G band is divided into 3 sub-bands -- upper, * middle, lower. * if bit 30 of modalHeader5G.papdRateMaskHt20 is set * -- disable PAPRD for upper band 5GHz * if bit 29 of modalHeader5G.papdRateMaskHt20 is set * -- disable PAPRD for middle band 5GHz * if bit 28 of modalHeader5G.papdRateMaskHt20 is set * -- disable PAPRD for lower band 5GHz */ if (IS_CHAN_5GHZ(chan)) { if (chan->channel >= UPPER_5G_SUB_BAND_START) { if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20) & BIT(30)) val = false; } else if (chan->channel >= MID_5G_SUB_BAND_START) { if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20) & BIT(29)) val = false; } else { if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20) & BIT(28)) val = false; } } if (val) { ah->paprd_table_write_done = true; ath9k_hw_apply_txpower(ah, chan, false); } REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B0, AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val); if (ah->caps.tx_chainmask & BIT(1)) REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B1, AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val); if (ah->caps.tx_chainmask & BIT(2)) REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B2, AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val); } EXPORT_SYMBOL(ar9003_paprd_enable); static int ar9003_get_training_power_2g(struct ath_hw *ah) { struct ath9k_channel *chan = ah->curchan; unsigned int power, scale, delta; scale = ar9003_get_paprd_scale_factor(ah, chan); power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5, AR_PHY_POWERTX_RATE5_POWERTXHT20_0); delta = abs((int) ah->paprd_target_power - (int) power); if (delta > scale) return -1; if (delta < 4) power -= 4 - delta; return power; } static int ar9003_get_training_power_5g(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; unsigned int power, scale, delta; scale = ar9003_get_paprd_scale_factor(ah, chan); if (IS_CHAN_HT40(chan)) power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE8, AR_PHY_POWERTX_RATE8_POWERTXHT40_5); else power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE6, AR_PHY_POWERTX_RATE6_POWERTXHT20_5); power += scale; delta = abs((int) ah->paprd_target_power - (int) power); if (delta > scale) return -1; switch (get_streams(ah->txchainmask)) { case 1: delta = 6; break; case 2: delta = 4; break; case 3: delta = 2; break; default: delta = 0; ath_dbg(common, CALIBRATE, "Invalid tx-chainmask: %u\n", ah->txchainmask); } power += delta; return power; } static int ar9003_paprd_setup_single_table(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); static const u32 ctrl0[3] = { AR_PHY_PAPRD_CTRL0_B0, AR_PHY_PAPRD_CTRL0_B1, AR_PHY_PAPRD_CTRL0_B2 }; static const u32 ctrl1[3] = { AR_PHY_PAPRD_CTRL1_B0, AR_PHY_PAPRD_CTRL1_B1, AR_PHY_PAPRD_CTRL1_B2 }; int training_power; int i, val; u32 am2pm_mask = ah->paprd_ratemask; if (IS_CHAN_2GHZ(ah->curchan)) training_power = ar9003_get_training_power_2g(ah); else training_power = ar9003_get_training_power_5g(ah); ath_dbg(common, CALIBRATE, "Training power: %d, Target power: %d\n", training_power, ah->paprd_target_power); if (training_power < 0) { ath_dbg(common, CALIBRATE, "PAPRD target power delta out of range\n"); return -ERANGE; } ah->paprd_training_power = training_power; if (AR_SREV_9330(ah)) am2pm_mask = 0; REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK, ah->paprd_ratemask); REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK, am2pm_mask); REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK, ah->paprd_ratemask_ht40); for (i = 0; i < ah->caps.max_txchains; i++) { REG_RMW_FIELD(ah, ctrl0[i], AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK, 1); REG_RMW_FIELD(ah, ctrl1[i], AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2PM_ENABLE, 1); REG_RMW_FIELD(ah, ctrl1[i], AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2AM_ENABLE, 1); REG_RMW_FIELD(ah, ctrl1[i], AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0); REG_RMW_FIELD(ah, ctrl1[i], AR_PHY_PAPRD_CTRL1_PA_GAIN_SCALE_FACT_MASK, 181); REG_RMW_FIELD(ah, ctrl1[i], AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT, 361); REG_RMW_FIELD(ah, ctrl1[i], AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0); REG_RMW_FIELD(ah, ctrl0[i], AR_PHY_PAPRD_CTRL0_PAPRD_MAG_THRSH, 3); } ar9003_paprd_enable(ah, false); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP, 0x30); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_ENABLE, 1); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_TX_GAIN_FORCE, 1); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_RX_BB_GAIN_FORCE, 0); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_IQCORR_ENABLE, 0); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING, 28); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE, 1); val = AR_SREV_9462(ah) ? 0x91 : 147; REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL2, AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN, val); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_FINE_CORR_LEN, 4); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_COARSE_CORR_LEN, 4); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES, 7); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL, 1); if (AR_SREV_9485(ah) || AR_SREV_9462(ah) || AR_SREV_9550(ah)) REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, -3); else REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, -6); val = AR_SREV_9462(ah) ? -10 : -15; REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE, val); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE, 1); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_SAFETY_DELTA, 0); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_MIN_CORR, 400); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES, 100); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_0_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 261376); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_1_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 248079); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_2_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 233759); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_3_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 220464); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_4_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 208194); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_5_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 196949); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_6_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 185706); REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_7_B0, AR_PHY_PAPRD_PRE_POST_SCALING, 175487); return 0; } static void ar9003_paprd_get_gain_table(struct ath_hw *ah) { u32 *entry = ah->paprd_gain_table_entries; u8 *index = ah->paprd_gain_table_index; u32 reg = AR_PHY_TXGAIN_TABLE; int i; memset(entry, 0, sizeof(ah->paprd_gain_table_entries)); memset(index, 0, sizeof(ah->paprd_gain_table_index)); for (i = 0; i < PAPRD_GAIN_TABLE_ENTRIES; i++) { entry[i] = REG_READ(ah, reg); index[i] = (entry[i] >> 24) & 0xff; reg += 4; } } static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain, int target_power) { int olpc_gain_delta = 0, cl_gain_mod; int alpha_therm, alpha_volt; int therm_cal_value, volt_cal_value; int therm_value, volt_value; int thermal_gain_corr, voltage_gain_corr; int desired_scale, desired_gain = 0; u32 reg_olpc = 0, reg_cl_gain = 0; REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); desired_scale = REG_READ_FIELD(ah, AR_PHY_TPC_12, AR_PHY_TPC_12_DESIRED_SCALE_HT40_5); alpha_therm = REG_READ_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM); alpha_volt = REG_READ_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_VOLT); therm_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18, AR_PHY_TPC_18_THERM_CAL_VALUE); volt_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18, AR_PHY_TPC_18_VOLT_CAL_VALUE); therm_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4, AR_PHY_BB_THERM_ADC_4_LATEST_THERM_VALUE); volt_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4, AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE); switch (chain) { case 0: reg_olpc = AR_PHY_TPC_11_B0; reg_cl_gain = AR_PHY_CL_TAB_0; break; case 1: reg_olpc = AR_PHY_TPC_11_B1; reg_cl_gain = AR_PHY_CL_TAB_1; break; case 2: reg_olpc = AR_PHY_TPC_11_B2; reg_cl_gain = AR_PHY_CL_TAB_2; break; default: ath_dbg(ath9k_hw_common(ah), CALIBRATE, "Invalid chainmask: %d\n", chain); break; } olpc_gain_delta = REG_READ_FIELD(ah, reg_olpc, AR_PHY_TPC_11_OLPC_GAIN_DELTA); cl_gain_mod = REG_READ_FIELD(ah, reg_cl_gain, AR_PHY_CL_TAB_CL_GAIN_MOD); if (olpc_gain_delta >= 128) olpc_gain_delta = olpc_gain_delta - 256; thermal_gain_corr = (alpha_therm * (therm_value - therm_cal_value) + (256 / 2)) / 256; voltage_gain_corr = (alpha_volt * (volt_value - volt_cal_value) + (128 / 2)) / 128; desired_gain = target_power - olpc_gain_delta - thermal_gain_corr - voltage_gain_corr + desired_scale + cl_gain_mod; return desired_gain; } static void ar9003_tx_force_gain(struct ath_hw *ah, unsigned int gain_index) { int selected_gain_entry, txbb1dbgain, txbb6dbgain, txmxrgain; int padrvgnA, padrvgnB, padrvgnC, padrvgnD; u32 *gain_table_entries = ah->paprd_gain_table_entries; selected_gain_entry = gain_table_entries[gain_index]; txbb1dbgain = selected_gain_entry & 0x7; txbb6dbgain = (selected_gain_entry >> 3) & 0x3; txmxrgain = (selected_gain_entry >> 5) & 0xf; padrvgnA = (selected_gain_entry >> 9) & 0xf; padrvgnB = (selected_gain_entry >> 13) & 0xf; padrvgnC = (selected_gain_entry >> 17) & 0xf; padrvgnD = (selected_gain_entry >> 21) & 0x3; REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_TXBB1DBGAIN, txbb1dbgain); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_TXBB6DBGAIN, txbb6dbgain); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_TXMXRGAIN, txmxrgain); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNA, padrvgnA); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNB, padrvgnB); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNC, padrvgnC); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGND, padrvgnD); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCED_ENABLE_PAL, 0); REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TX_FORCED_GAIN_FORCE_TX_GAIN, 0); REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCED_DAC_GAIN, 0); REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCE_DAC_GAIN, 0); } static inline int find_expn(int num) { return fls(num) - 1; } static inline int find_proper_scale(int expn, int N) { return (expn > N) ? expn - 10 : 0; } #define NUM_BIN 23 static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) { unsigned int thresh_accum_cnt; int x_est[NUM_BIN + 1], Y[NUM_BIN + 1], theta[NUM_BIN + 1]; int PA_in[NUM_BIN + 1]; int B1_tmp[NUM_BIN + 1], B2_tmp[NUM_BIN + 1]; unsigned int B1_abs_max, B2_abs_max; int max_index, scale_factor; int y_est[NUM_BIN + 1]; int x_est_fxp1_nonlin, x_tilde[NUM_BIN + 1]; unsigned int x_tilde_abs; int G_fxp, Y_intercept, order_x_by_y, M, I, L, sum_y_sqr, sum_y_quad; int Q_x, Q_B1, Q_B2, beta_raw, alpha_raw, scale_B; int Q_scale_B, Q_beta, Q_alpha, alpha, beta, order_1, order_2; int order1_5x, order2_3x, order1_5x_rem, order2_3x_rem; int y5, y3, tmp; int theta_low_bin = 0; int i; /* disregard any bin that contains <= 16 samples */ thresh_accum_cnt = 16; scale_factor = 5; max_index = 0; memset(theta, 0, sizeof(theta)); memset(x_est, 0, sizeof(x_est)); memset(Y, 0, sizeof(Y)); memset(y_est, 0, sizeof(y_est)); memset(x_tilde, 0, sizeof(x_tilde)); for (i = 0; i < NUM_BIN; i++) { s32 accum_cnt, accum_tx, accum_rx, accum_ang; /* number of samples */ accum_cnt = data_L[i] & 0xffff; if (accum_cnt <= thresh_accum_cnt) continue; /* sum(tx amplitude) */ accum_tx = ((data_L[i] >> 16) & 0xffff) | ((data_U[i] & 0x7ff) << 16); /* sum(rx amplitude distance to lower bin edge) */ accum_rx = ((data_U[i] >> 11) & 0x1f) | ((data_L[i + 23] & 0xffff) << 5); /* sum(angles) */ accum_ang = ((data_L[i + 23] >> 16) & 0xffff) | ((data_U[i + 23] & 0x7ff) << 16); accum_tx <<= scale_factor; accum_rx <<= scale_factor; x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >> scale_factor; Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> scale_factor) + (1 << scale_factor) * max_index + 16; if (accum_ang >= (1 << 26)) accum_ang -= 1 << 27; theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) / accum_cnt; max_index++; } /* * Find average theta of first 5 bin and all of those to same value. * Curve is linear at that range. */ for (i = 1; i < 6; i++) theta_low_bin += theta[i]; theta_low_bin = theta_low_bin / 5; for (i = 1; i < 6; i++) theta[i] = theta_low_bin; /* Set values at origin */ theta[0] = theta_low_bin; for (i = 0; i <= max_index; i++) theta[i] -= theta_low_bin; x_est[0] = 0; Y[0] = 0; scale_factor = 8; /* low signal gain */ if (x_est[6] == x_est[3]) return false; G_fxp = (((Y[6] - Y[3]) * 1 << scale_factor) + (x_est[6] - x_est[3])) / (x_est[6] - x_est[3]); /* prevent division by zero */ if (G_fxp == 0) return false; Y_intercept = (G_fxp * (x_est[0] - x_est[3]) + (1 << scale_factor)) / (1 << scale_factor) + Y[3]; for (i = 0; i <= max_index; i++) y_est[i] = Y[i] - Y_intercept; for (i = 0; i <= 3; i++) { y_est[i] = i * 32; x_est[i] = ((y_est[i] * 1 << scale_factor) + G_fxp) / G_fxp; } if (y_est[max_index] == 0) return false; x_est_fxp1_nonlin = x_est[max_index] - ((1 << scale_factor) * y_est[max_index] + G_fxp) / G_fxp; order_x_by_y = (x_est_fxp1_nonlin + y_est[max_index]) / y_est[max_index]; if (order_x_by_y == 0) M = 10; else if (order_x_by_y == 1) M = 9; else M = 8; I = (max_index > 15) ? 7 : max_index >> 1; L = max_index - I; scale_factor = 8; sum_y_sqr = 0; sum_y_quad = 0; x_tilde_abs = 0; for (i = 0; i <= L; i++) { unsigned int y_sqr; unsigned int y_quad; unsigned int tmp_abs; /* prevent division by zero */ if (y_est[i + I] == 0) return false; x_est_fxp1_nonlin = x_est[i + I] - ((1 << scale_factor) * y_est[i + I] + G_fxp) / G_fxp; x_tilde[i] = (x_est_fxp1_nonlin * (1 << M) + y_est[i + I]) / y_est[i + I]; x_tilde[i] = (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I]; x_tilde[i] = (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I]; y_sqr = (y_est[i + I] * y_est[i + I] + (scale_factor * scale_factor)) / (scale_factor * scale_factor); tmp_abs = abs(x_tilde[i]); if (tmp_abs > x_tilde_abs) x_tilde_abs = tmp_abs; y_quad = y_sqr * y_sqr; sum_y_sqr = sum_y_sqr + y_sqr; sum_y_quad = sum_y_quad + y_quad; B1_tmp[i] = y_sqr * (L + 1); B2_tmp[i] = y_sqr; } B1_abs_max = 0; B2_abs_max = 0; for (i = 0; i <= L; i++) { int abs_val; B1_tmp[i] -= sum_y_sqr; B2_tmp[i] = sum_y_quad - sum_y_sqr * B2_tmp[i]; abs_val = abs(B1_tmp[i]); if (abs_val > B1_abs_max) B1_abs_max = abs_val; abs_val = abs(B2_tmp[i]); if (abs_val > B2_abs_max) B2_abs_max = abs_val; } Q_x = find_proper_scale(find_expn(x_tilde_abs), 10); Q_B1 = find_proper_scale(find_expn(B1_abs_max), 10); Q_B2 = find_proper_scale(find_expn(B2_abs_max), 10); beta_raw = 0; alpha_raw = 0; for (i = 0; i <= L; i++) { x_tilde[i] = x_tilde[i] / (1 << Q_x); B1_tmp[i] = B1_tmp[i] / (1 << Q_B1); B2_tmp[i] = B2_tmp[i] / (1 << Q_B2); beta_raw = beta_raw + B1_tmp[i] * x_tilde[i]; alpha_raw = alpha_raw + B2_tmp[i] * x_tilde[i]; } scale_B = ((sum_y_quad / scale_factor) * (L + 1) - (sum_y_sqr / scale_factor) * sum_y_sqr) * scale_factor; Q_scale_B = find_proper_scale(find_expn(abs(scale_B)), 10); scale_B = scale_B / (1 << Q_scale_B); if (scale_B == 0) return false; Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10); Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10); beta_raw = beta_raw / (1 << Q_beta); alpha_raw = alpha_raw / (1 << Q_alpha); alpha = (alpha_raw << 10) / scale_B; beta = (beta_raw << 10) / scale_B; order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B; order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B; order1_5x = order_1 / 5; order2_3x = order_2 / 3; order1_5x_rem = order_1 - 5 * order1_5x; order2_3x_rem = order_2 - 3 * order2_3x; for (i = 0; i < PAPRD_TABLE_SZ; i++) { tmp = i * 32; y5 = ((beta * tmp) >> 6) >> order1_5x; y5 = (y5 * tmp) >> order1_5x; y5 = (y5 * tmp) >> order1_5x; y5 = (y5 * tmp) >> order1_5x; y5 = (y5 * tmp) >> order1_5x; y5 = y5 >> order1_5x_rem; y3 = (alpha * tmp) >> order2_3x; y3 = (y3 * tmp) >> order2_3x; y3 = (y3 * tmp) >> order2_3x; y3 = y3 >> order2_3x_rem; PA_in[i] = y5 + y3 + (256 * tmp) / G_fxp; if (i >= 2) { tmp = PA_in[i] - PA_in[i - 1]; if (tmp < 0) PA_in[i] = PA_in[i - 1] + (PA_in[i - 1] - PA_in[i - 2]); } PA_in[i] = (PA_in[i] < 1400) ? PA_in[i] : 1400; } beta_raw = 0; alpha_raw = 0; for (i = 0; i <= L; i++) { int theta_tilde = ((theta[i + I] << M) + y_est[i + I]) / y_est[i + I]; theta_tilde = ((theta_tilde << M) + y_est[i + I]) / y_est[i + I]; theta_tilde = ((theta_tilde << M) + y_est[i + I]) / y_est[i + I]; beta_raw = beta_raw + B1_tmp[i] * theta_tilde; alpha_raw = alpha_raw + B2_tmp[i] * theta_tilde; } Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10); Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10); beta_raw = beta_raw / (1 << Q_beta); alpha_raw = alpha_raw / (1 << Q_alpha); alpha = (alpha_raw << 10) / scale_B; beta = (beta_raw << 10) / scale_B; order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B + 5; order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B + 5; order1_5x = order_1 / 5; order2_3x = order_2 / 3; order1_5x_rem = order_1 - 5 * order1_5x; order2_3x_rem = order_2 - 3 * order2_3x; for (i = 0; i < PAPRD_TABLE_SZ; i++) { int PA_angle; /* pa_table[4] is calculated from PA_angle for i=5 */ if (i == 4) continue; tmp = i * 32; if (beta > 0) y5 = (((beta * tmp - 64) >> 6) - (1 << order1_5x)) / (1 << order1_5x); else y5 = ((((beta * tmp - 64) >> 6) + (1 << order1_5x)) / (1 << order1_5x)); y5 = (y5 * tmp) / (1 << order1_5x); y5 = (y5 * tmp) / (1 << order1_5x); y5 = (y5 * tmp) / (1 << order1_5x); y5 = (y5 * tmp) / (1 << order1_5x); y5 = y5 / (1 << order1_5x_rem); if (beta > 0) y3 = (alpha * tmp - (1 << order2_3x)) / (1 << order2_3x); else y3 = (alpha * tmp + (1 << order2_3x)) / (1 << order2_3x); y3 = (y3 * tmp) / (1 << order2_3x); y3 = (y3 * tmp) / (1 << order2_3x); y3 = y3 / (1 << order2_3x_rem); if (i < 4) { PA_angle = 0; } else { PA_angle = y5 + y3; if (PA_angle < -150) PA_angle = -150; else if (PA_angle > 150) PA_angle = 150; } pa_table[i] = ((PA_in[i] & 0x7ff) << 11) + (PA_angle & 0x7ff); if (i == 5) { PA_angle = (PA_angle + 2) >> 1; pa_table[i - 1] = ((PA_in[i - 1] & 0x7ff) << 11) + (PA_angle & 0x7ff); } } *gain = G_fxp; return true; } void ar9003_paprd_populate_single_table(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata, int chain) { u32 *paprd_table_val = caldata->pa_table[chain]; u32 small_signal_gain = caldata->small_signal_gain[chain]; u32 training_power = ah->paprd_training_power; u32 reg = 0; int i; if (chain == 0) reg = AR_PHY_PAPRD_MEM_TAB_B0; else if (chain == 1) reg = AR_PHY_PAPRD_MEM_TAB_B1; else if (chain == 2) reg = AR_PHY_PAPRD_MEM_TAB_B2; for (i = 0; i < PAPRD_TABLE_SZ; i++) { REG_WRITE(ah, reg, paprd_table_val[i]); reg = reg + 4; } if (chain == 0) reg = AR_PHY_PA_GAIN123_B0; else if (chain == 1) reg = AR_PHY_PA_GAIN123_B1; else reg = AR_PHY_PA_GAIN123_B2; REG_RMW_FIELD(ah, reg, AR_PHY_PA_GAIN123_PA_GAIN1, small_signal_gain); REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B0, AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, training_power); if (ah->caps.tx_chainmask & BIT(1)) REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B1, AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, training_power); if (ah->caps.tx_chainmask & BIT(2)) /* val AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL correct? */ REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B2, AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, training_power); } EXPORT_SYMBOL(ar9003_paprd_populate_single_table); int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain) { unsigned int i, desired_gain, gain_index; unsigned int train_power = ah->paprd_training_power; desired_gain = ar9003_get_desired_gain(ah, chain, train_power); gain_index = 0; for (i = 0; i < PAPRD_GAIN_TABLE_ENTRIES; i++) { if (ah->paprd_gain_table_index[i] >= desired_gain) break; gain_index++; } ar9003_tx_force_gain(ah, gain_index); REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); return 0; } EXPORT_SYMBOL(ar9003_paprd_setup_gain_table); static bool ar9003_paprd_retrain_pa_in(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata, int chain) { u32 *pa_in = caldata->pa_table[chain]; int capdiv_offset, quick_drop_offset; int capdiv2g, quick_drop; int count = 0; int i; if (!AR_SREV_9485(ah) && !AR_SREV_9330(ah)) return false; capdiv2g = REG_READ_FIELD(ah, AR_PHY_65NM_CH0_TXRF3, AR_PHY_65NM_CH0_TXRF3_CAPDIV2G); quick_drop = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP); if (quick_drop) quick_drop -= 0x40; for (i = 0; i < NUM_BIN + 1; i++) { if (pa_in[i] == 1400) count++; } if (AR_SREV_9485(ah)) { if (pa_in[23] < 800) { capdiv_offset = (int)((1000 - pa_in[23] + 75) / 150); capdiv2g += capdiv_offset; if (capdiv2g > 7) { capdiv2g = 7; if (pa_in[23] < 600) { quick_drop++; if (quick_drop > 0) quick_drop = 0; } } } else if (pa_in[23] == 1400) { quick_drop_offset = min_t(int, count / 3, 2); quick_drop += quick_drop_offset; capdiv2g += quick_drop_offset / 2; if (capdiv2g > 7) capdiv2g = 7; if (quick_drop > 0) { quick_drop = 0; capdiv2g -= quick_drop_offset; if (capdiv2g < 0) capdiv2g = 0; } } else { return false; } } else if (AR_SREV_9330(ah)) { if (pa_in[23] < 1000) { capdiv_offset = (1000 - pa_in[23]) / 100; capdiv2g += capdiv_offset; if (capdiv_offset > 3) { capdiv_offset = 1; quick_drop--; } capdiv2g += capdiv_offset; if (capdiv2g > 6) capdiv2g = 6; if (quick_drop < -4) quick_drop = -4; } else if (pa_in[23] == 1400) { if (count > 3) { quick_drop++; capdiv2g -= count / 4; if (quick_drop > -2) quick_drop = -2; } else { capdiv2g--; } if (capdiv2g < 0) capdiv2g = 0; } else { return false; } } REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TXRF3, AR_PHY_65NM_CH0_TXRF3_CAPDIV2G, capdiv2g); REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, quick_drop); return true; } int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata, int chain) { u16 *small_signal_gain = &caldata->small_signal_gain[chain]; u32 *pa_table = caldata->pa_table[chain]; u32 *data_L, *data_U; int i, status = 0; u32 *buf; u32 reg; memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain])); buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC); if (!buf) return -ENOMEM; data_L = &buf[0]; data_U = &buf[48]; REG_CLR_BIT(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ); reg = AR_PHY_CHAN_INFO_TAB_0; for (i = 0; i < 48; i++) data_L[i] = REG_READ(ah, reg + (i << 2)); REG_SET_BIT(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ); for (i = 0; i < 48; i++) data_U[i] = REG_READ(ah, reg + (i << 2)); if (!create_pa_curve(data_L, data_U, pa_table, small_signal_gain)) status = -2; if (ar9003_paprd_retrain_pa_in(ah, caldata, chain)) status = -EINPROGRESS; REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); kfree(buf); return status; } EXPORT_SYMBOL(ar9003_paprd_create_curve); int ar9003_paprd_init_table(struct ath_hw *ah) { int ret; ret = ar9003_paprd_setup_single_table(ah); if (ret < 0) return ret; ar9003_paprd_get_gain_table(ah); return 0; } EXPORT_SYMBOL(ar9003_paprd_init_table); bool ar9003_paprd_is_done(struct ath_hw *ah) { int paprd_done, agc2_pwr; paprd_done = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); if (paprd_done == 0x1) { agc2_pwr = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR); ath_dbg(ath9k_hw_common(ah), CALIBRATE, "AGC2_PWR = 0x%x training done = 0x%x\n", agc2_pwr, paprd_done); /* * agc2_pwr range should not be less than 'IDEAL_AGC2_PWR_CHANGE' * when the training is completely done, otherwise retraining is * done to make sure the value is in ideal range */ if (agc2_pwr <= PAPRD_IDEAL_AGC2_PWR_RANGE) paprd_done = 0; } return !!paprd_done; } EXPORT_SYMBOL(ar9003_paprd_is_done); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_mci.h0000644000175000017500000002533312026211315024202 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AR9003_MCI_H #define AR9003_MCI_H #define MCI_FLAG_DISABLE_TIMESTAMP 0x00000001 /* Disable time stamp */ /* Default remote BT device MCI COEX version */ #define MCI_GPM_COEX_MAJOR_VERSION_DEFAULT 3 #define MCI_GPM_COEX_MINOR_VERSION_DEFAULT 0 /* Local WLAN MCI COEX version */ #define MCI_GPM_COEX_MAJOR_VERSION_WLAN 3 #define MCI_GPM_COEX_MINOR_VERSION_WLAN 0 enum mci_gpm_coex_query_type { MCI_GPM_COEX_QUERY_BT_ALL_INFO = BIT(0), MCI_GPM_COEX_QUERY_BT_TOPOLOGY = BIT(1), MCI_GPM_COEX_QUERY_BT_DEBUG = BIT(2), }; enum mci_gpm_coex_halt_bt_gpm { MCI_GPM_COEX_BT_GPM_UNHALT, MCI_GPM_COEX_BT_GPM_HALT }; enum mci_gpm_coex_bt_update_flags_op { MCI_GPM_COEX_BT_FLAGS_READ, MCI_GPM_COEX_BT_FLAGS_SET, MCI_GPM_COEX_BT_FLAGS_CLEAR }; #define MCI_NUM_BT_CHANNELS 79 #define MCI_BT_MCI_FLAGS_UPDATE_CORR 0x00000002 #define MCI_BT_MCI_FLAGS_UPDATE_HDR 0x00000004 #define MCI_BT_MCI_FLAGS_UPDATE_PLD 0x00000008 #define MCI_BT_MCI_FLAGS_LNA_CTRL 0x00000010 #define MCI_BT_MCI_FLAGS_DEBUG 0x00000020 #define MCI_BT_MCI_FLAGS_SCHED_MSG 0x00000040 #define MCI_BT_MCI_FLAGS_CONT_MSG 0x00000080 #define MCI_BT_MCI_FLAGS_COEX_GPM 0x00000100 #define MCI_BT_MCI_FLAGS_CPU_INT_MSG 0x00000200 #define MCI_BT_MCI_FLAGS_MCI_MODE 0x00000400 #define MCI_BT_MCI_FLAGS_AR9462_MODE 0x00001000 #define MCI_BT_MCI_FLAGS_OTHER 0x00010000 #define MCI_DEFAULT_BT_MCI_FLAGS 0x00011dde #define MCI_TOGGLE_BT_MCI_FLAGS (MCI_BT_MCI_FLAGS_UPDATE_CORR | \ MCI_BT_MCI_FLAGS_UPDATE_HDR | \ MCI_BT_MCI_FLAGS_UPDATE_PLD | \ MCI_BT_MCI_FLAGS_MCI_MODE) #define MCI_2G_FLAGS_CLEAR_MASK 0x00000000 #define MCI_2G_FLAGS_SET_MASK MCI_TOGGLE_BT_MCI_FLAGS #define MCI_2G_FLAGS MCI_DEFAULT_BT_MCI_FLAGS #define MCI_5G_FLAGS_CLEAR_MASK MCI_TOGGLE_BT_MCI_FLAGS #define MCI_5G_FLAGS_SET_MASK 0x00000000 #define MCI_5G_FLAGS (MCI_DEFAULT_BT_MCI_FLAGS & \ ~MCI_TOGGLE_BT_MCI_FLAGS) /* * Default value for AR9462 is 0x00002201 */ #define ATH_MCI_CONFIG_CONCUR_TX 0x00000003 #define ATH_MCI_CONFIG_MCI_OBS_MCI 0x00000004 #define ATH_MCI_CONFIG_MCI_OBS_TXRX 0x00000008 #define ATH_MCI_CONFIG_MCI_OBS_BT 0x00000010 #define ATH_MCI_CONFIG_DISABLE_MCI_CAL 0x00000020 #define ATH_MCI_CONFIG_DISABLE_OSLA 0x00000040 #define ATH_MCI_CONFIG_DISABLE_FTP_STOMP 0x00000080 #define ATH_MCI_CONFIG_AGGR_THRESH 0x00000700 #define ATH_MCI_CONFIG_AGGR_THRESH_S 8 #define ATH_MCI_CONFIG_DISABLE_AGGR_THRESH 0x00000800 #define ATH_MCI_CONFIG_CLK_DIV 0x00003000 #define ATH_MCI_CONFIG_CLK_DIV_S 12 #define ATH_MCI_CONFIG_DISABLE_TUNING 0x00004000 #define ATH_MCI_CONFIG_MCI_WEIGHT_DBG 0x40000000 #define ATH_MCI_CONFIG_DISABLE_MCI 0x80000000 #define ATH_MCI_CONFIG_MCI_OBS_MASK (ATH_MCI_CONFIG_MCI_OBS_MCI | \ ATH_MCI_CONFIG_MCI_OBS_TXRX | \ ATH_MCI_CONFIG_MCI_OBS_BT) #define ATH_MCI_CONFIG_MCI_OBS_GPIO 0x0000002F enum mci_message_header { /* length of payload */ MCI_LNA_CTRL = 0x10, /* len = 0 */ MCI_CONT_NACK = 0x20, /* len = 0 */ MCI_CONT_INFO = 0x30, /* len = 4 */ MCI_CONT_RST = 0x40, /* len = 0 */ MCI_SCHD_INFO = 0x50, /* len = 16 */ MCI_CPU_INT = 0x60, /* len = 4 */ MCI_SYS_WAKING = 0x70, /* len = 0 */ MCI_GPM = 0x80, /* len = 16 */ MCI_LNA_INFO = 0x90, /* len = 1 */ MCI_LNA_STATE = 0x94, MCI_LNA_TAKE = 0x98, MCI_LNA_TRANS = 0x9c, MCI_SYS_SLEEPING = 0xa0, /* len = 0 */ MCI_REQ_WAKE = 0xc0, /* len = 0 */ MCI_DEBUG_16 = 0xfe, /* len = 2 */ MCI_REMOTE_RESET = 0xff /* len = 16 */ }; enum ath_mci_gpm_coex_profile_type { MCI_GPM_COEX_PROFILE_UNKNOWN, MCI_GPM_COEX_PROFILE_RFCOMM, MCI_GPM_COEX_PROFILE_A2DP, MCI_GPM_COEX_PROFILE_HID, MCI_GPM_COEX_PROFILE_BNEP, MCI_GPM_COEX_PROFILE_VOICE, MCI_GPM_COEX_PROFILE_MAX }; /* MCI GPM/Coex opcode/type definitions */ enum { MCI_GPM_COEX_W_GPM_PAYLOAD = 1, MCI_GPM_COEX_B_GPM_TYPE = 4, MCI_GPM_COEX_B_GPM_OPCODE = 5, /* MCI_GPM_WLAN_CAL_REQ, MCI_GPM_WLAN_CAL_DONE */ MCI_GPM_WLAN_CAL_W_SEQUENCE = 2, /* MCI_GPM_COEX_VERSION_QUERY */ /* MCI_GPM_COEX_VERSION_RESPONSE */ MCI_GPM_COEX_B_MAJOR_VERSION = 6, MCI_GPM_COEX_B_MINOR_VERSION = 7, /* MCI_GPM_COEX_STATUS_QUERY */ MCI_GPM_COEX_B_BT_BITMAP = 6, MCI_GPM_COEX_B_WLAN_BITMAP = 7, /* MCI_GPM_COEX_HALT_BT_GPM */ MCI_GPM_COEX_B_HALT_STATE = 6, /* MCI_GPM_COEX_WLAN_CHANNELS */ MCI_GPM_COEX_B_CHANNEL_MAP = 6, /* MCI_GPM_COEX_BT_PROFILE_INFO */ MCI_GPM_COEX_B_PROFILE_TYPE = 6, MCI_GPM_COEX_B_PROFILE_LINKID = 7, MCI_GPM_COEX_B_PROFILE_STATE = 8, MCI_GPM_COEX_B_PROFILE_ROLE = 9, MCI_GPM_COEX_B_PROFILE_RATE = 10, MCI_GPM_COEX_B_PROFILE_VOTYPE = 11, MCI_GPM_COEX_H_PROFILE_T = 12, MCI_GPM_COEX_B_PROFILE_W = 14, MCI_GPM_COEX_B_PROFILE_A = 15, /* MCI_GPM_COEX_BT_STATUS_UPDATE */ MCI_GPM_COEX_B_STATUS_TYPE = 6, MCI_GPM_COEX_B_STATUS_LINKID = 7, MCI_GPM_COEX_B_STATUS_STATE = 8, /* MCI_GPM_COEX_BT_UPDATE_FLAGS */ MCI_GPM_COEX_W_BT_FLAGS = 6, MCI_GPM_COEX_B_BT_FLAGS_OP = 10 }; enum mci_gpm_subtype { MCI_GPM_BT_CAL_REQ = 0, MCI_GPM_BT_CAL_GRANT = 1, MCI_GPM_BT_CAL_DONE = 2, MCI_GPM_WLAN_CAL_REQ = 3, MCI_GPM_WLAN_CAL_GRANT = 4, MCI_GPM_WLAN_CAL_DONE = 5, MCI_GPM_COEX_AGENT = 0x0c, MCI_GPM_RSVD_PATTERN = 0xfe, MCI_GPM_RSVD_PATTERN32 = 0xfefefefe, MCI_GPM_BT_DEBUG = 0xff }; enum mci_bt_state { MCI_BT_SLEEP, MCI_BT_AWAKE, MCI_BT_CAL_START, MCI_BT_CAL }; /* Type of state query */ enum mci_state_type { MCI_STATE_ENABLE, MCI_STATE_SET_BT_AWAKE, MCI_STATE_LAST_SCHD_MSG_OFFSET, MCI_STATE_REMOTE_SLEEP, MCI_STATE_RESET_REQ_WAKE, MCI_STATE_SEND_WLAN_COEX_VERSION, MCI_STATE_SEND_VERSION_QUERY, MCI_STATE_SEND_STATUS_QUERY, MCI_STATE_SET_CONCUR_TX_PRI, MCI_STATE_RECOVER_RX, MCI_STATE_NEED_FTP_STOMP, MCI_STATE_DEBUG, MCI_STATE_MAX }; enum mci_gpm_coex_opcode { MCI_GPM_COEX_VERSION_QUERY, MCI_GPM_COEX_VERSION_RESPONSE, MCI_GPM_COEX_STATUS_QUERY, MCI_GPM_COEX_HALT_BT_GPM, MCI_GPM_COEX_WLAN_CHANNELS, MCI_GPM_COEX_BT_PROFILE_INFO, MCI_GPM_COEX_BT_STATUS_UPDATE, MCI_GPM_COEX_BT_UPDATE_FLAGS }; #define MCI_GPM_NOMORE 0 #define MCI_GPM_MORE 1 #define MCI_GPM_INVALID 0xffffffff #define MCI_GPM_RECYCLE(_p_gpm) do { \ *(((u32 *)_p_gpm) + MCI_GPM_COEX_W_GPM_PAYLOAD) = \ MCI_GPM_RSVD_PATTERN32; \ } while (0) #define MCI_GPM_TYPE(_p_gpm) \ (*(((u8 *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) & 0xff) #define MCI_GPM_OPCODE(_p_gpm) \ (*(((u8 *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_OPCODE) & 0xff) #define MCI_GPM_SET_CAL_TYPE(_p_gpm, _cal_type) do { \ *(((u8 *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) = (_cal_type) & 0xff;\ } while (0) #define MCI_GPM_SET_TYPE_OPCODE(_p_gpm, _type, _opcode) do { \ *(((u8 *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) = (_type) & 0xff; \ *(((u8 *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_OPCODE) = (_opcode) & 0xff;\ } while (0) #define MCI_GPM_IS_CAL_TYPE(_type) ((_type) <= MCI_GPM_WLAN_CAL_DONE) /* * Functions that are available to the MCI driver core. */ bool ar9003_mci_send_message(struct ath_hw *ah, u8 header, u32 flag, u32 *payload, u8 len, bool wait_done, bool check_bt); u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type); void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, u16 len, u32 sched_addr); void ar9003_mci_cleanup(struct ath_hw *ah); void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, u32 *rx_msg_intr); u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more); void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor); void ar9003_mci_send_wlan_channels(struct ath_hw *ah); /* * These functions are used by ath9k_hw. */ #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep); void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable); void ar9003_mci_init_cal_done(struct ath_hw *ah); void ar9003_mci_set_full_sleep(struct ath_hw *ah); void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force); void ar9003_mci_check_bt(struct ath_hw *ah); bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan); int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata); void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, bool is_full_sleep); void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked); void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah); void ar9003_mci_set_power_awake(struct ath_hw *ah); void ar9003_mci_check_gpm_offset(struct ath_hw *ah); #else static inline void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep) { } static inline void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable) { } static inline void ar9003_mci_init_cal_done(struct ath_hw *ah) { } static inline void ar9003_mci_set_full_sleep(struct ath_hw *ah) { } static inline void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool wait_done) { } static inline void ar9003_mci_check_bt(struct ath_hw *ah) { } static inline bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) { return false; } static inline int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata) { return 0; } static inline void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, bool is_full_sleep) { } static inline void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked) { } static inline void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) { } static inline void ar9003_mci_set_power_awake(struct ath_hw *ah) { } static inline void ar9003_mci_check_gpm_offset(struct ath_hw *ah) { } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_mci.c0000644000175000017500000011417212026211315024175 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "hw-ops.h" #include "ar9003_phy.h" #include "ar9003_mci.h" static void ar9003_mci_reset_req_wakeup(struct ath_hw *ah) { REG_RMW_FIELD(ah, AR_MCI_COMMAND2, AR_MCI_COMMAND2_RESET_REQ_WAKEUP, 1); udelay(1); REG_RMW_FIELD(ah, AR_MCI_COMMAND2, AR_MCI_COMMAND2_RESET_REQ_WAKEUP, 0); } static int ar9003_mci_wait_for_interrupt(struct ath_hw *ah, u32 address, u32 bit_position, int time_out) { struct ath_common *common = ath9k_hw_common(ah); while (time_out) { if (!(REG_READ(ah, address) & bit_position)) { udelay(10); time_out -= 10; if (time_out < 0) break; else continue; } REG_WRITE(ah, address, bit_position); if (address != AR_MCI_INTERRUPT_RX_MSG_RAW) break; if (bit_position & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) ar9003_mci_reset_req_wakeup(ah); if (bit_position & (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING | AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_RX_MSG); break; } if (time_out <= 0) { ath_dbg(common, MCI, "MCI Wait for Reg 0x%08x = 0x%08x timeout\n", address, bit_position); ath_dbg(common, MCI, "MCI INT_RAW = 0x%08x, RX_MSG_RAW = 0x%08x\n", REG_READ(ah, AR_MCI_INTERRUPT_RAW), REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW)); time_out = 0; } return time_out; } static void ar9003_mci_remote_reset(struct ath_hw *ah, bool wait_done) { u32 payload[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff00}; ar9003_mci_send_message(ah, MCI_REMOTE_RESET, 0, payload, 16, wait_done, false); udelay(5); } static void ar9003_mci_send_lna_transfer(struct ath_hw *ah, bool wait_done) { u32 payload = 0x00000000; ar9003_mci_send_message(ah, MCI_LNA_TRANS, 0, &payload, 1, wait_done, false); } static void ar9003_mci_send_req_wake(struct ath_hw *ah, bool wait_done) { ar9003_mci_send_message(ah, MCI_REQ_WAKE, MCI_FLAG_DISABLE_TIMESTAMP, NULL, 0, wait_done, false); udelay(5); } static void ar9003_mci_send_sys_waking(struct ath_hw *ah, bool wait_done) { ar9003_mci_send_message(ah, MCI_SYS_WAKING, MCI_FLAG_DISABLE_TIMESTAMP, NULL, 0, wait_done, false); } static void ar9003_mci_send_lna_take(struct ath_hw *ah, bool wait_done) { u32 payload = 0x70000000; ar9003_mci_send_message(ah, MCI_LNA_TAKE, 0, &payload, 1, wait_done, false); } static void ar9003_mci_send_sys_sleeping(struct ath_hw *ah, bool wait_done) { ar9003_mci_send_message(ah, MCI_SYS_SLEEPING, MCI_FLAG_DISABLE_TIMESTAMP, NULL, 0, wait_done, false); } static void ar9003_mci_send_coex_version_query(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; if (mci->bt_version_known || (mci->bt_state == MCI_BT_SLEEP)) return; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_VERSION_QUERY); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); } static void ar9003_mci_send_coex_version_response(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_VERSION_RESPONSE); *(((u8 *)payload) + MCI_GPM_COEX_B_MAJOR_VERSION) = mci->wlan_ver_major; *(((u8 *)payload) + MCI_GPM_COEX_B_MINOR_VERSION) = mci->wlan_ver_minor; ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); } static void ar9003_mci_send_coex_wlan_channels(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 *payload = &mci->wlan_channels[0]; if (!mci->wlan_channels_update || (mci->bt_state == MCI_BT_SLEEP)) return; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_WLAN_CHANNELS); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff); } static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah, bool wait_done, u8 query_type) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; bool query_btinfo; if (mci->bt_state == MCI_BT_SLEEP) return; query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO | MCI_GPM_COEX_QUERY_BT_TOPOLOGY)); MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_STATUS_QUERY); *(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type; /* * If bt_status_query message is not sent successfully, * then need_flush_btinfo should be set again. */ if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true)) { if (query_btinfo) mci->need_flush_btinfo = true; } if (query_btinfo) mci->query_bt = false; } static void ar9003_mci_send_coex_halt_bt_gpm(struct ath_hw *ah, bool halt, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_HALT_BT_GPM); if (halt) { mci->query_bt = true; /* Send next unhalt no matter halt sent or not */ mci->unhalt_bt_gpm = true; mci->need_flush_btinfo = true; *(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) = MCI_GPM_COEX_BT_GPM_HALT; } else *(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) = MCI_GPM_COEX_BT_GPM_UNHALT; ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); } static void ar9003_mci_prep_interface(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 saved_mci_int_en; u32 mci_timeout = 150; mci->bt_state = MCI_BT_SLEEP; saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW)); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, REG_READ(ah, AR_MCI_INTERRUPT_RAW)); ar9003_mci_remote_reset(ah, true); ar9003_mci_send_req_wake(ah, true); if (!ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) goto clear_redunt; mci->bt_state = MCI_BT_AWAKE; /* * we don't need to send more remote_reset at this moment. * If BT receive first remote_reset, then BT HW will * be cleaned up and will be able to receive req_wake * and BT HW will respond sys_waking. * In this case, WLAN will receive BT's HW sys_waking. * Otherwise, if BT SW missed initial remote_reset, * that remote_reset will still clean up BT MCI RX, * and the req_wake will wake BT up, * and BT SW will respond this req_wake with a remote_reset and * sys_waking. In this case, WLAN will receive BT's SW * sys_waking. In either case, BT's RX is cleaned up. So we * don't need to reply BT's remote_reset now, if any. * Similarly, if in any case, WLAN can receive BT's sys_waking, * that means WLAN's RX is also fine. */ ar9003_mci_send_sys_waking(ah, true); udelay(10); /* * Set BT priority interrupt value to be 0xff to * avoid having too many BT PRIORITY interrupts. */ REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF); /* * A contention reset will be received after send out * sys_waking. Also BT priority interrupt bits will be set. * Clear those bits before the next step. */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_CONT_RST); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI); if (mci->is_2g) { ar9003_mci_send_lna_transfer(ah, true); udelay(5); } if ((mci->is_2g && !mci->update_2g5g)) { if (ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_LNA_INFO, mci_timeout)) ath_dbg(common, MCI, "MCI WLAN has control over the LNA & BT obeys it\n"); else ath_dbg(common, MCI, "MCI BT didn't respond to LNA_TRANS\n"); } clear_redunt: /* Clear the extra redundant SYS_WAKING from BT */ if ((mci->bt_state == MCI_BT_AWAKE) && (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) && (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) == 0)) { REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); } REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); } void ar9003_mci_set_full_sleep(struct ath_hw *ah) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; if (ar9003_mci_state(ah, MCI_STATE_ENABLE) && (mci->bt_state != MCI_BT_SLEEP) && !mci->halted_bt_gpm) { ar9003_mci_send_coex_halt_bt_gpm(ah, true, true); } mci->ready = false; } static void ar9003_mci_disable_interrupt(struct ath_hw *ah) { REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0); } static void ar9003_mci_enable_interrupt(struct ath_hw *ah) { REG_WRITE(ah, AR_MCI_INTERRUPT_EN, AR_MCI_INTERRUPT_DEFAULT); REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, AR_MCI_INTERRUPT_RX_MSG_DEFAULT); } static bool ar9003_mci_check_int(struct ath_hw *ah, u32 ints) { u32 intr; intr = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW); return ((intr & ints) == ints); } void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, u32 *rx_msg_intr) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; *raw_intr = mci->raw_intr; *rx_msg_intr = mci->rx_msg_intr; /* Clean int bits after the values are read. */ mci->raw_intr = 0; mci->rx_msg_intr = 0; } EXPORT_SYMBOL(ar9003_mci_get_interrupt); void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 raw_intr, rx_msg_intr; rx_msg_intr = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW); raw_intr = REG_READ(ah, AR_MCI_INTERRUPT_RAW); if ((raw_intr == 0xdeadbeef) || (rx_msg_intr == 0xdeadbeef)) { ath_dbg(common, MCI, "MCI gets 0xdeadbeef during int processing\n"); } else { mci->rx_msg_intr |= rx_msg_intr; mci->raw_intr |= raw_intr; *masked |= ATH9K_INT_MCI; if (rx_msg_intr & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) mci->cont_status = REG_READ(ah, AR_MCI_CONT_STATUS); REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, rx_msg_intr); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, raw_intr); } } static void ar9003_mci_2g5g_changed(struct ath_hw *ah, bool is_2g) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; if (!mci->update_2g5g && (mci->is_2g != is_2g)) mci->update_2g5g = true; mci->is_2g = is_2g; } static bool ar9003_mci_is_gpm_valid(struct ath_hw *ah, u32 msg_index) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 *payload; u32 recv_type, offset; if (msg_index == MCI_GPM_INVALID) return false; offset = msg_index << 4; payload = (u32 *)(mci->gpm_buf + offset); recv_type = MCI_GPM_TYPE(payload); if (recv_type == MCI_GPM_RSVD_PATTERN) return false; return true; } static void ar9003_mci_observation_set_up(struct ath_hw *ah) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MCI) { ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA); ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK); ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA); ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK); } else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_TXRX) { ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX); ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX); ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX); ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX); ath9k_hw_cfg_output(ah, 5, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); } else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_BT) { ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX); ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX); ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA); ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK); } else return; REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_GLB_DS_JTAG_DISABLE, 1); REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_GLB_WLAN_UART_INTF_EN, 0); REG_SET_BIT(ah, AR_GLB_GPIO_CONTROL, ATH_MCI_CONFIG_MCI_OBS_GPIO); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_GPIO_OBS_SEL, 0); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL, 1); REG_WRITE(ah, AR_OBS, 0x4b); REG_RMW_FIELD(ah, AR_DIAG_SW, AR_DIAG_OBS_PT_SEL1, 0x03); REG_RMW_FIELD(ah, AR_DIAG_SW, AR_DIAG_OBS_PT_SEL2, 0x01); REG_RMW_FIELD(ah, AR_MACMISC, AR_MACMISC_MISC_OBS_BUS_LSB, 0x02); REG_RMW_FIELD(ah, AR_MACMISC, AR_MACMISC_MISC_OBS_BUS_MSB, 0x03); REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_DEBUGPORT_SEL, 0x07); } static bool ar9003_mci_send_coex_bt_flags(struct ath_hw *ah, bool wait_done, u8 opcode, u32 bt_flags) { u32 pld[4] = {0, 0, 0, 0}; MCI_GPM_SET_TYPE_OPCODE(pld, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_BT_UPDATE_FLAGS); *(((u8 *)pld) + MCI_GPM_COEX_B_BT_FLAGS_OP) = opcode; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 0) = bt_flags & 0xFF; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 1) = (bt_flags >> 8) & 0xFF; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 2) = (bt_flags >> 16) & 0xFF; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 3) = (bt_flags >> 24) & 0xFF; return ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, wait_done, true); } static void ar9003_mci_sync_bt_state(struct ath_hw *ah) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 cur_bt_state; cur_bt_state = ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP); if (mci->bt_state != cur_bt_state) mci->bt_state = cur_bt_state; if (mci->bt_state != MCI_BT_SLEEP) { ar9003_mci_send_coex_version_query(ah, true); ar9003_mci_send_coex_wlan_channels(ah, true); if (mci->unhalt_bt_gpm == true) ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); } } void ar9003_mci_check_bt(struct ath_hw *ah) { struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; if (!mci_hw->ready) return; /* * check BT state again to make * sure it's not changed. */ ar9003_mci_sync_bt_state(ah); ar9003_mci_2g5g_switch(ah, true); if ((mci_hw->bt_state == MCI_BT_AWAKE) && (mci_hw->query_bt == true)) { mci_hw->need_flush_btinfo = true; } } static void ar9003_mci_process_gpm_extra(struct ath_hw *ah, u8 gpm_type, u8 gpm_opcode, u32 *p_gpm) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u8 *p_data = (u8 *) p_gpm; if (gpm_type != MCI_GPM_COEX_AGENT) return; switch (gpm_opcode) { case MCI_GPM_COEX_VERSION_QUERY: ath_dbg(common, MCI, "MCI Recv GPM COEX Version Query\n"); ar9003_mci_send_coex_version_response(ah, true); break; case MCI_GPM_COEX_VERSION_RESPONSE: ath_dbg(common, MCI, "MCI Recv GPM COEX Version Response\n"); mci->bt_ver_major = *(p_data + MCI_GPM_COEX_B_MAJOR_VERSION); mci->bt_ver_minor = *(p_data + MCI_GPM_COEX_B_MINOR_VERSION); mci->bt_version_known = true; ath_dbg(common, MCI, "MCI BT Coex version: %d.%d\n", mci->bt_ver_major, mci->bt_ver_minor); break; case MCI_GPM_COEX_STATUS_QUERY: ath_dbg(common, MCI, "MCI Recv GPM COEX Status Query = 0x%02X\n", *(p_data + MCI_GPM_COEX_B_WLAN_BITMAP)); mci->wlan_channels_update = true; ar9003_mci_send_coex_wlan_channels(ah, true); break; case MCI_GPM_COEX_BT_PROFILE_INFO: mci->query_bt = true; ath_dbg(common, MCI, "MCI Recv GPM COEX BT_Profile_Info\n"); break; case MCI_GPM_COEX_BT_STATUS_UPDATE: mci->query_bt = true; ath_dbg(common, MCI, "MCI Recv GPM COEX BT_Status_Update SEQ=%d (drop&query)\n", *(p_gpm + 3)); break; default: break; } } static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, u8 gpm_opcode, int time_out) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 *p_gpm = NULL, mismatch = 0, more_data; u32 offset; u8 recv_type = 0, recv_opcode = 0; bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE); more_data = time_out ? MCI_GPM_NOMORE : MCI_GPM_MORE; while (time_out > 0) { if (p_gpm) { MCI_GPM_RECYCLE(p_gpm); p_gpm = NULL; } if (more_data != MCI_GPM_MORE) time_out = ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_GPM, time_out); if (!time_out) break; offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); if (offset == MCI_GPM_INVALID) continue; p_gpm = (u32 *) (mci->gpm_buf + offset); recv_type = MCI_GPM_TYPE(p_gpm); recv_opcode = MCI_GPM_OPCODE(p_gpm); if (MCI_GPM_IS_CAL_TYPE(recv_type)) { if (recv_type == gpm_type) { if ((gpm_type == MCI_GPM_BT_CAL_DONE) && !b_is_bt_cal_done) { gpm_type = MCI_GPM_BT_CAL_GRANT; continue; } break; } } else if ((recv_type == gpm_type) && (recv_opcode == gpm_opcode)) break; /* * check if it's cal_grant * * When we're waiting for cal_grant in reset routine, * it's possible that BT sends out cal_request at the * same time. Since BT's calibration doesn't happen * that often, we'll let BT completes calibration then * we continue to wait for cal_grant from BT. * Orginal: Wait BT_CAL_GRANT. * New: Receive BT_CAL_REQ -> send WLAN_CAL_GRANT->wait * BT_CAL_DONE -> Wait BT_CAL_GRANT. */ if ((gpm_type == MCI_GPM_BT_CAL_GRANT) && (recv_type == MCI_GPM_BT_CAL_REQ)) { u32 payload[4] = {0, 0, 0, 0}; gpm_type = MCI_GPM_BT_CAL_DONE; MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, false, false); continue; } else { ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n", *(p_gpm + 1)); mismatch++; ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); } } if (p_gpm) { MCI_GPM_RECYCLE(p_gpm); p_gpm = NULL; } if (time_out <= 0) time_out = 0; while (more_data == MCI_GPM_MORE) { offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); if (offset == MCI_GPM_INVALID) break; p_gpm = (u32 *) (mci->gpm_buf + offset); recv_type = MCI_GPM_TYPE(p_gpm); recv_opcode = MCI_GPM_OPCODE(p_gpm); if (!MCI_GPM_IS_CAL_TYPE(recv_type)) ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); MCI_GPM_RECYCLE(p_gpm); } return time_out; } bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; ar9003_mci_2g5g_changed(ah, IS_CHAN_2GHZ(chan)); if (mci_hw->bt_state != MCI_BT_CAL_START) return false; mci_hw->bt_state = MCI_BT_CAL; /* * MCI FIX: disable mci interrupt here. This is to avoid * SW_MSG_DONE or RX_MSG bits to trigger MCI_INT and * lead to mci_intr reentry. */ ar9003_mci_disable_interrupt(ah); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, true, false); /* Wait BT calibration to be completed for 25ms */ if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, 0, 25000)) ath_dbg(common, MCI, "MCI BT_CAL_DONE received\n"); else ath_dbg(common, MCI, "MCI BT_CAL_DONE not received\n"); mci_hw->bt_state = MCI_BT_AWAKE; /* MCI FIX: enable mci interrupt here */ ar9003_mci_enable_interrupt(ah); return true; } EXPORT_SYMBOL(ar9003_mci_start_reset); int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata) { struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; if (!mci_hw->ready) return 0; if (!IS_CHAN_2GHZ(chan) || (mci_hw->bt_state != MCI_BT_SLEEP)) goto exit; if (!ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) && !ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) goto exit; /* * BT is sleeping. Check if BT wakes up during * WLAN calibration. If BT wakes up during * WLAN calibration, need to go through all * message exchanges again and recal. */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)); ar9003_mci_remote_reset(ah, true); ar9003_mci_send_sys_waking(ah, true); udelay(1); if (IS_CHAN_2GHZ(chan)) ar9003_mci_send_lna_transfer(ah, true); mci_hw->bt_state = MCI_BT_AWAKE; if (caldata) { caldata->done_txiqcal_once = false; caldata->done_txclcal_once = false; caldata->rtt_done = false; } if (!ath9k_hw_init_cal(ah, chan)) return -EIO; exit: ar9003_mci_enable_interrupt(ah); return 0; } static void ar9003_mci_mute_bt(struct ath_hw *ah) { /* disable all MCI messages */ REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, 0xffff0000); REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); /* wait pending HW messages to flush out */ udelay(10); /* * Send LNA_TAKE and SYS_SLEEPING when * 1. reset not after resuming from full sleep * 2. before reset MCI RX, to quiet BT and avoid MCI RX misalignment */ ar9003_mci_send_lna_take(ah, true); udelay(5); ar9003_mci_send_sys_sleeping(ah, true); } static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 thresh; if (!enable) { REG_CLR_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); return; } REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_HW_BASED, 1); REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_MEM_BASED, 1); if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) { thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_AGGR_THRESH, thresh); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1); } else REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1); } void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, bool is_full_sleep) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 regval; ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n", is_full_sleep, is_2g); if (!mci->gpm_addr && !mci->sched_addr) { ath_dbg(common, MCI, "MCI GPM and schedule buffers are not allocated\n"); return; } if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) { ath_dbg(common, MCI, "BTCOEX control register is dead\n"); return; } /* Program MCI DMA related registers */ REG_WRITE(ah, AR_MCI_GPM_0, mci->gpm_addr); REG_WRITE(ah, AR_MCI_GPM_1, mci->gpm_len); REG_WRITE(ah, AR_MCI_SCHD_TABLE_0, mci->sched_addr); /* * To avoid MCI state machine be affected by incoming remote MCI msgs, * MCI mode will be enabled later, right before reset the MCI TX and RX. */ regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) | SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | SM(1, AR_BTCOEX_CTRL_PA_SHARED) | SM(1, AR_BTCOEX_CTRL_LNA_SHARED) | SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK) | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); REG_WRITE(ah, AR_BTCOEX_CTRL, regval); if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) ar9003_mci_osla_setup(ah, true); else ar9003_mci_osla_setup(ah, false); REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_SPDT_ENABLE); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3, AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 1); REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV); REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval); REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN); /* Resetting the Rx and Tx paths of MCI */ regval = REG_READ(ah, AR_MCI_COMMAND2); regval |= SM(1, AR_MCI_COMMAND2_RESET_TX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); udelay(1); regval &= ~SM(1, AR_MCI_COMMAND2_RESET_TX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); if (is_full_sleep) { ar9003_mci_mute_bt(ah); udelay(100); } /* Check pending GPM msg before MCI Reset Rx */ ar9003_mci_check_gpm_offset(ah); regval |= SM(1, AR_MCI_COMMAND2_RESET_RX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); udelay(1); regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); ar9003_mci_get_next_gpm_offset(ah, true, NULL); REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) | SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM))); REG_CLR_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); ar9003_mci_observation_set_up(ah); mci->ready = true; ar9003_mci_prep_interface(ah); if (en_int) ar9003_mci_enable_interrupt(ah); } void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep) { struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; ar9003_mci_disable_interrupt(ah); if (mci_hw->ready && !save_fullsleep) { ar9003_mci_mute_bt(ah); udelay(20); REG_WRITE(ah, AR_BTCOEX_CTRL, 0); } mci_hw->bt_state = MCI_BT_SLEEP; mci_hw->ready = false; } static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 new_flags, to_set, to_clear; if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP)) return; if (mci->is_2g) { new_flags = MCI_2G_FLAGS; to_clear = MCI_2G_FLAGS_CLEAR_MASK; to_set = MCI_2G_FLAGS_SET_MASK; } else { new_flags = MCI_5G_FLAGS; to_clear = MCI_5G_FLAGS_CLEAR_MASK; to_set = MCI_5G_FLAGS_SET_MASK; } if (to_clear) ar9003_mci_send_coex_bt_flags(ah, wait_done, MCI_GPM_COEX_BT_FLAGS_CLEAR, to_clear); if (to_set) ar9003_mci_send_coex_bt_flags(ah, wait_done, MCI_GPM_COEX_BT_FLAGS_SET, to_set); } static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header, u32 *payload, bool queue) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u8 type, opcode; /* check if the message is to be queued */ if (header != MCI_GPM) return; type = MCI_GPM_TYPE(payload); opcode = MCI_GPM_OPCODE(payload); if (type != MCI_GPM_COEX_AGENT) return; switch (opcode) { case MCI_GPM_COEX_BT_UPDATE_FLAGS: if (*(((u8 *)payload) + MCI_GPM_COEX_B_BT_FLAGS_OP) == MCI_GPM_COEX_BT_FLAGS_READ) break; mci->update_2g5g = queue; break; case MCI_GPM_COEX_WLAN_CHANNELS: mci->wlan_channels_update = queue; break; case MCI_GPM_COEX_HALT_BT_GPM: if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == MCI_GPM_COEX_BT_GPM_UNHALT) { mci->unhalt_bt_gpm = queue; if (!queue) mci->halted_bt_gpm = false; } if (*(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) == MCI_GPM_COEX_BT_GPM_HALT) { mci->halted_bt_gpm = !queue; } break; default: break; } } void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; if (!mci->update_2g5g && !force) return; if (mci->is_2g) { ar9003_mci_send_2g5g_status(ah, true); ar9003_mci_send_lna_transfer(ah, true); udelay(5); REG_CLR_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) ar9003_mci_osla_setup(ah, true); } else { ar9003_mci_send_lna_take(ah, true); udelay(5); REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); ar9003_mci_osla_setup(ah, false); ar9003_mci_send_2g5g_status(ah, true); } } bool ar9003_mci_send_message(struct ath_hw *ah, u8 header, u32 flag, u32 *payload, u8 len, bool wait_done, bool check_bt) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; bool msg_sent = false; u32 regval; u32 saved_mci_int_en; int i; saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); regval = REG_READ(ah, AR_BTCOEX_CTRL); if ((regval == 0xdeadbeef) || !(regval & AR_BTCOEX_CTRL_MCI_MODE_EN)) { ath_dbg(common, MCI, "MCI Not sending 0x%x. MCI is not enabled. full_sleep = %d\n", header, (ah->power_mode == ATH9K_PM_FULL_SLEEP) ? 1 : 0); ar9003_mci_queue_unsent_gpm(ah, header, payload, true); return false; } else if (check_bt && (mci->bt_state == MCI_BT_SLEEP)) { ath_dbg(common, MCI, "MCI Don't send message 0x%x. BT is in sleep state\n", header); ar9003_mci_queue_unsent_gpm(ah, header, payload, true); return false; } if (wait_done) REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); /* Need to clear SW_MSG_DONE raw bit before wait */ REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, (AR_MCI_INTERRUPT_SW_MSG_DONE | AR_MCI_INTERRUPT_MSG_FAIL_MASK)); if (payload) { for (i = 0; (i * 4) < len; i++) REG_WRITE(ah, (AR_MCI_TX_PAYLOAD0 + i * 4), *(payload + i)); } REG_WRITE(ah, AR_MCI_COMMAND0, (SM((flag & MCI_FLAG_DISABLE_TIMESTAMP), AR_MCI_COMMAND0_DISABLE_TIMESTAMP) | SM(len, AR_MCI_COMMAND0_LEN) | SM(header, AR_MCI_COMMAND0_HEADER))); if (wait_done && !(ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_SW_MSG_DONE, 500))) ar9003_mci_queue_unsent_gpm(ah, header, payload, true); else { ar9003_mci_queue_unsent_gpm(ah, header, payload, false); msg_sent = true; } if (wait_done) REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); return msg_sent; } EXPORT_SYMBOL(ar9003_mci_send_message); void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 pld[4] = {0, 0, 0, 0}; if ((mci_hw->bt_state != MCI_BT_AWAKE) || (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) return; MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_REQ); pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_seq++; ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n"); } else { *is_reusable = false; ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n"); } } void ar9003_mci_init_cal_done(struct ath_hw *ah) { struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 pld[4] = {0, 0, 0, 0}; if ((mci_hw->bt_state != MCI_BT_AWAKE) || (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) return; MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_DONE); pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_done++; ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); } void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, u16 len, u32 sched_addr) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; mci->gpm_addr = gpm_addr; mci->gpm_buf = gpm_buf; mci->gpm_len = len; mci->sched_addr = sched_addr; ar9003_mci_reset(ah, true, true, true); } EXPORT_SYMBOL(ar9003_mci_setup); void ar9003_mci_cleanup(struct ath_hw *ah) { /* Turn off MCI and Jupiter mode. */ REG_WRITE(ah, AR_BTCOEX_CTRL, 0x00); ar9003_mci_disable_interrupt(ah); } EXPORT_SYMBOL(ar9003_mci_cleanup); u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 value = 0; u8 query_type; switch (state_type) { case MCI_STATE_ENABLE: if (mci->ready) { value = REG_READ(ah, AR_BTCOEX_CTRL); if ((value == 0xdeadbeef) || (value == 0xffffffff)) value = 0; } value &= AR_BTCOEX_CTRL_MCI_MODE_EN; break; case MCI_STATE_LAST_SCHD_MSG_OFFSET: value = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_LAST_SCHD_MSG_INDEX); /* Make it in bytes */ value <<= 4; break; case MCI_STATE_REMOTE_SLEEP: value = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_REMOTE_SLEEP) ? MCI_BT_SLEEP : MCI_BT_AWAKE; break; case MCI_STATE_SET_BT_AWAKE: mci->bt_state = MCI_BT_AWAKE; ar9003_mci_send_coex_version_query(ah, true); ar9003_mci_send_coex_wlan_channels(ah, true); if (mci->unhalt_bt_gpm) ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); ar9003_mci_2g5g_switch(ah, false); break; case MCI_STATE_RESET_REQ_WAKE: ar9003_mci_reset_req_wakeup(ah); mci->update_2g5g = true; if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MASK) { /* Check if we still have control of the GPIOs */ if ((REG_READ(ah, AR_GLB_GPIO_CONTROL) & ATH_MCI_CONFIG_MCI_OBS_GPIO) != ATH_MCI_CONFIG_MCI_OBS_GPIO) { ar9003_mci_observation_set_up(ah); } } break; case MCI_STATE_SEND_WLAN_COEX_VERSION: ar9003_mci_send_coex_version_response(ah, true); break; case MCI_STATE_SEND_VERSION_QUERY: ar9003_mci_send_coex_version_query(ah, true); break; case MCI_STATE_SEND_STATUS_QUERY: query_type = MCI_GPM_COEX_QUERY_BT_TOPOLOGY; ar9003_mci_send_coex_bt_status_query(ah, true, query_type); break; case MCI_STATE_RECOVER_RX: ar9003_mci_prep_interface(ah); mci->query_bt = true; mci->need_flush_btinfo = true; ar9003_mci_send_coex_wlan_channels(ah, true); ar9003_mci_2g5g_switch(ah, false); break; case MCI_STATE_NEED_FTP_STOMP: value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); break; default: break; } return value; } EXPORT_SYMBOL(ar9003_mci_state); void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n"); ar9003_mci_send_lna_take(ah, true); udelay(50); REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); mci->is_2g = false; mci->update_2g5g = true; ar9003_mci_send_2g5g_status(ah, true); /* Force another 2g5g update at next scanning */ mci->update_2g5g = true; } void ar9003_mci_set_power_awake(struct ath_hw *ah) { u32 btcoex_ctrl2, diag_sw; int i; u8 lna_ctrl, bt_sleep; for (i = 0; i < AH_WAIT_TIMEOUT; i++) { btcoex_ctrl2 = REG_READ(ah, AR_BTCOEX_CTRL2); if (btcoex_ctrl2 != 0xdeadbeef) break; udelay(AH_TIME_QUANTUM); } REG_WRITE(ah, AR_BTCOEX_CTRL2, (btcoex_ctrl2 | BIT(23))); for (i = 0; i < AH_WAIT_TIMEOUT; i++) { diag_sw = REG_READ(ah, AR_DIAG_SW); if (diag_sw != 0xdeadbeef) break; udelay(AH_TIME_QUANTUM); } REG_WRITE(ah, AR_DIAG_SW, (diag_sw | BIT(27) | BIT(19) | BIT(18))); lna_ctrl = REG_READ(ah, AR_OBS_BUS_CTRL) & 0x3; bt_sleep = REG_READ(ah, AR_MCI_RX_STATUS) & AR_MCI_RX_REMOTE_SLEEP; REG_WRITE(ah, AR_BTCOEX_CTRL2, btcoex_ctrl2); REG_WRITE(ah, AR_DIAG_SW, diag_sw); if (bt_sleep && (lna_ctrl == 2)) { REG_SET_BIT(ah, AR_BTCOEX_RC, 0x1); REG_CLR_BIT(ah, AR_BTCOEX_RC, 0x1); udelay(50); } } void ar9003_mci_check_gpm_offset(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 offset; /* * This should only be called before "MAC Warm Reset" or "MCI Reset Rx". */ offset = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); if (mci->gpm_idx == offset) return; ath_dbg(common, MCI, "GPM cached write pointer mismatch %d %d\n", mci->gpm_idx, offset); mci->query_bt = true; mci->need_flush_btinfo = true; mci->gpm_idx = 0; } u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 offset, more_gpm = 0, gpm_ptr; if (first) { gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); if (gpm_ptr >= mci->gpm_len) gpm_ptr = 0; mci->gpm_idx = gpm_ptr; return gpm_ptr; } /* * This could be useful to avoid new GPM message interrupt which * may lead to spurious interrupt after power sleep, or multiple * entry of ath_mci_intr(). * Adding empty GPM check by returning HAL_MCI_GPM_INVALID can * alleviate this effect, but clearing GPM RX interrupt bit is * safe, because whether this is called from hw or driver code * there must be an interrupt bit set/triggered initially */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_GPM); gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); offset = gpm_ptr; if (!offset) offset = mci->gpm_len - 1; else if (offset >= mci->gpm_len) { if (offset != 0xFFFF) offset = 0; } else { offset--; } if ((offset == 0xFFFF) || (gpm_ptr == mci->gpm_idx)) { offset = MCI_GPM_INVALID; more_gpm = MCI_GPM_NOMORE; goto out; } for (;;) { u32 temp_index; /* skip reserved GPM if any */ if (offset != mci->gpm_idx) more_gpm = MCI_GPM_MORE; else more_gpm = MCI_GPM_NOMORE; temp_index = mci->gpm_idx; if (temp_index >= mci->gpm_len) temp_index = 0; mci->gpm_idx++; if (mci->gpm_idx >= mci->gpm_len) mci->gpm_idx = 0; if (ar9003_mci_is_gpm_valid(ah, temp_index)) { offset = temp_index; break; } if (more_gpm == MCI_GPM_NOMORE) { offset = MCI_GPM_INVALID; break; } } if (offset != MCI_GPM_INVALID) offset <<= 4; out: if (more) *more = more_gpm; return offset; } EXPORT_SYMBOL(ar9003_mci_get_next_gpm_offset); void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; mci->bt_ver_major = major; mci->bt_ver_minor = minor; mci->bt_version_known = true; ath_dbg(ath9k_hw_common(ah), MCI, "MCI BT version set: %d.%d\n", mci->bt_ver_major, mci->bt_ver_minor); } EXPORT_SYMBOL(ar9003_mci_set_bt_version); void ar9003_mci_send_wlan_channels(struct ath_hw *ah) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; mci->wlan_channels_update = true; ar9003_mci_send_coex_wlan_channels(ah, true); } EXPORT_SYMBOL(ar9003_mci_send_wlan_channels); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_mac.h0000644000175000017500000000677112026211315024177 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AR9003_MAC_H #define AR9003_MAC_H #define AR_DescId 0xffff0000 #define AR_DescId_S 16 #define AR_CtrlStat 0x00004000 #define AR_CtrlStat_S 14 #define AR_TxRxDesc 0x00008000 #define AR_TxRxDesc_S 15 #define AR_TxQcuNum 0x00000f00 #define AR_TxQcuNum_S 8 #define AR_BufLen 0x0fff0000 #define AR_BufLen_S 16 #define AR_TxDescId 0xffff0000 #define AR_TxDescId_S 16 #define AR_TxPtrChkSum 0x0000ffff #define AR_LowRxChain 0x00004000 #define AR_Not_Sounding 0x20000000 /* ctl 12 */ #define AR_PAPRDChainMask 0x00000e00 #define AR_PAPRDChainMask_S 9 #define MAP_ISR_S2_CST 6 #define MAP_ISR_S2_GTT 6 #define MAP_ISR_S2_TIM 3 #define MAP_ISR_S2_CABEND 0 #define MAP_ISR_S2_DTIMSYNC 7 #define MAP_ISR_S2_DTIM 7 #define MAP_ISR_S2_TSFOOR 4 #define MAP_ISR_S2_BB_WATCHDOG 6 #define AR9003TXC_CONST(_ds) ((const struct ar9003_txc *) _ds) struct ar9003_rxs { u32 ds_info; u32 status1; u32 status2; u32 status3; u32 status4; u32 status5; u32 status6; u32 status7; u32 status8; u32 status9; u32 status10; u32 status11; } __packed __aligned(4); /* Transmit Control Descriptor */ struct ar9003_txc { u32 info; /* descriptor information */ u32 link; /* link pointer */ u32 data0; /* data pointer to 1st buffer */ u32 ctl3; /* DMA control 3 */ u32 data1; /* data pointer to 2nd buffer */ u32 ctl5; /* DMA control 5 */ u32 data2; /* data pointer to 3rd buffer */ u32 ctl7; /* DMA control 7 */ u32 data3; /* data pointer to 4th buffer */ u32 ctl9; /* DMA control 9 */ u32 ctl10; /* DMA control 10 */ u32 ctl11; /* DMA control 11 */ u32 ctl12; /* DMA control 12 */ u32 ctl13; /* DMA control 13 */ u32 ctl14; /* DMA control 14 */ u32 ctl15; /* DMA control 15 */ u32 ctl16; /* DMA control 16 */ u32 ctl17; /* DMA control 17 */ u32 ctl18; /* DMA control 18 */ u32 ctl19; /* DMA control 19 */ u32 ctl20; /* DMA control 20 */ u32 ctl21; /* DMA control 21 */ u32 ctl22; /* DMA control 22 */ u32 ctl23; /* DMA control 23 */ u32 pad[8]; /* pad to cache line (128 bytes/32 dwords) */ } __packed __aligned(4); struct ar9003_txs { u32 ds_info; u32 status1; u32 status2; u32 status3; u32 status4; u32 status5; u32 status6; u32 status7; u32 status8; } __packed __aligned(4); void ar9003_hw_attach_mac_ops(struct ath_hw *hw); void ath9k_hw_set_rx_bufsize(struct ath_hw *ah, u16 buf_size); void ath9k_hw_addrxbuf_edma(struct ath_hw *ah, u32 rxdp, enum ath9k_rx_qtype qtype); int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, void *buf_addr); void ath9k_hw_reset_txstatus_ring(struct ath_hw *ah); void ath9k_hw_setup_statusring(struct ath_hw *ah, void *ts_start, u32 ts_paddr_start, u16 size); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_mac.c0000644000175000017500000003770112026211315024167 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar9003_mac.h" #include "ar9003_mci.h" static void ar9003_hw_rx_enable(struct ath_hw *hw) { REG_WRITE(hw, AR_CR, 0); } static void ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) { struct ar9003_txc *ads = ds; int checksum = 0; u32 val, ctl12, ctl17; u8 desc_len; desc_len = ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x18 : 0x17); val = (ATHEROS_VENDOR_ID << AR_DescId_S) | (1 << AR_TxRxDesc_S) | (1 << AR_CtrlStat_S) | (i->qcu << AR_TxQcuNum_S) | desc_len; checksum += val; ACCESS_ONCE(ads->info) = val; checksum += i->link; ACCESS_ONCE(ads->link) = i->link; checksum += i->buf_addr[0]; ACCESS_ONCE(ads->data0) = i->buf_addr[0]; checksum += i->buf_addr[1]; ACCESS_ONCE(ads->data1) = i->buf_addr[1]; checksum += i->buf_addr[2]; ACCESS_ONCE(ads->data2) = i->buf_addr[2]; checksum += i->buf_addr[3]; ACCESS_ONCE(ads->data3) = i->buf_addr[3]; checksum += (val = (i->buf_len[0] << AR_BufLen_S) & AR_BufLen); ACCESS_ONCE(ads->ctl3) = val; checksum += (val = (i->buf_len[1] << AR_BufLen_S) & AR_BufLen); ACCESS_ONCE(ads->ctl5) = val; checksum += (val = (i->buf_len[2] << AR_BufLen_S) & AR_BufLen); ACCESS_ONCE(ads->ctl7) = val; checksum += (val = (i->buf_len[3] << AR_BufLen_S) & AR_BufLen); ACCESS_ONCE(ads->ctl9) = val; checksum = (u16) (((checksum & 0xffff) + (checksum >> 16)) & 0xffff); ACCESS_ONCE(ads->ctl10) = checksum; if (i->is_first || i->is_last) { ACCESS_ONCE(ads->ctl13) = set11nTries(i->rates, 0) | set11nTries(i->rates, 1) | set11nTries(i->rates, 2) | set11nTries(i->rates, 3) | (i->dur_update ? AR_DurUpdateEna : 0) | SM(0, AR_BurstDur); ACCESS_ONCE(ads->ctl14) = set11nRate(i->rates, 0) | set11nRate(i->rates, 1) | set11nRate(i->rates, 2) | set11nRate(i->rates, 3); } else { ACCESS_ONCE(ads->ctl13) = 0; ACCESS_ONCE(ads->ctl14) = 0; } ads->ctl20 = 0; ads->ctl21 = 0; ads->ctl22 = 0; ads->ctl23 = 0; ctl17 = SM(i->keytype, AR_EncrType); if (!i->is_first) { ACCESS_ONCE(ads->ctl11) = 0; ACCESS_ONCE(ads->ctl12) = i->is_last ? 0 : AR_TxMore; ACCESS_ONCE(ads->ctl15) = 0; ACCESS_ONCE(ads->ctl16) = 0; ACCESS_ONCE(ads->ctl17) = ctl17; ACCESS_ONCE(ads->ctl18) = 0; ACCESS_ONCE(ads->ctl19) = 0; return; } ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen) | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) | SM(i->txpower, AR_XmitPower) | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0) | (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0) | (i->flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) | (i->flags & ATH9K_TXDESC_RTSENA ? AR_RTSEnable : (i->flags & ATH9K_TXDESC_CTSENA ? AR_CTSEnable : 0)); ctl12 = (i->keyix != ATH9K_TXKEYIX_INVALID ? SM(i->keyix, AR_DestIdx) : 0) | SM(i->type, AR_FrameType) | (i->flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0) | (i->flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0) | (i->flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0); ctl17 |= (i->flags & ATH9K_TXDESC_LDPC ? AR_LDPC : 0); switch (i->aggr) { case AGGR_BUF_FIRST: ctl17 |= SM(i->aggr_len, AR_AggrLen); /* fall through */ case AGGR_BUF_MIDDLE: ctl12 |= AR_IsAggr | AR_MoreAggr; ctl17 |= SM(i->ndelim, AR_PadDelim); break; case AGGR_BUF_LAST: ctl12 |= AR_IsAggr; break; case AGGR_BUF_NONE: break; } val = (i->flags & ATH9K_TXDESC_PAPRD) >> ATH9K_TXDESC_PAPRD_S; ctl12 |= SM(val, AR_PAPRDChainMask); ACCESS_ONCE(ads->ctl12) = ctl12; ACCESS_ONCE(ads->ctl17) = ctl17; ACCESS_ONCE(ads->ctl15) = set11nPktDurRTSCTS(i->rates, 0) | set11nPktDurRTSCTS(i->rates, 1); ACCESS_ONCE(ads->ctl16) = set11nPktDurRTSCTS(i->rates, 2) | set11nPktDurRTSCTS(i->rates, 3); ACCESS_ONCE(ads->ctl18) = set11nRateFlags(i->rates, 0) | set11nRateFlags(i->rates, 1) | set11nRateFlags(i->rates, 2) | set11nRateFlags(i->rates, 3) | SM(i->rtscts_rate, AR_RTSCTSRate); ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding; } static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads) { int checksum; checksum = ads->info + ads->link + ads->data0 + ads->ctl3 + ads->data1 + ads->ctl5 + ads->data2 + ads->ctl7 + ads->data3 + ads->ctl9; return ((checksum & 0xffff) + (checksum >> 16)) & AR_TxPtrChkSum; } static void ar9003_hw_set_desc_link(void *ds, u32 ds_link) { struct ar9003_txc *ads = ds; ads->link = ds_link; ads->ctl10 &= ~AR_TxPtrChkSum; ads->ctl10 |= ar9003_calc_ptr_chksum(ads); } static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) { u32 isr = 0; u32 mask2 = 0; struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_common *common = ath9k_hw_common(ah); u32 sync_cause = 0, async_cause, async_mask = AR_INTR_MAC_IRQ; if (ath9k_hw_mci_is_enabled(ah)) async_mask |= AR_INTR_ASYNC_MASK_MCI; async_cause = REG_READ(ah, AR_INTR_ASYNC_CAUSE); if (async_cause & async_mask) { if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON) isr = REG_READ(ah, AR_ISR); } sync_cause = REG_READ(ah, AR_INTR_SYNC_CAUSE) & AR_INTR_SYNC_DEFAULT; *masked = 0; if (!isr && !sync_cause && !async_cause) return false; if (isr) { if (isr & AR_ISR_BCNMISC) { u32 isr2; isr2 = REG_READ(ah, AR_ISR_S2); mask2 |= ((isr2 & AR_ISR_S2_TIM) >> MAP_ISR_S2_TIM); mask2 |= ((isr2 & AR_ISR_S2_DTIM) >> MAP_ISR_S2_DTIM); mask2 |= ((isr2 & AR_ISR_S2_DTIMSYNC) >> MAP_ISR_S2_DTIMSYNC); mask2 |= ((isr2 & AR_ISR_S2_CABEND) >> MAP_ISR_S2_CABEND); mask2 |= ((isr2 & AR_ISR_S2_GTT) << MAP_ISR_S2_GTT); mask2 |= ((isr2 & AR_ISR_S2_CST) << MAP_ISR_S2_CST); mask2 |= ((isr2 & AR_ISR_S2_TSFOOR) >> MAP_ISR_S2_TSFOOR); mask2 |= ((isr2 & AR_ISR_S2_BB_WATCHDOG) >> MAP_ISR_S2_BB_WATCHDOG); if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { REG_WRITE(ah, AR_ISR_S2, isr2); isr &= ~AR_ISR_BCNMISC; } } if ((pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) isr = REG_READ(ah, AR_ISR_RAC); if (isr == 0xffffffff) { *masked = 0; return false; } *masked = isr & ATH9K_INT_COMMON; if (ah->config.rx_intr_mitigation) if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM)) *masked |= ATH9K_INT_RXLP; if (ah->config.tx_intr_mitigation) if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM)) *masked |= ATH9K_INT_TX; if (isr & (AR_ISR_LP_RXOK | AR_ISR_RXERR)) *masked |= ATH9K_INT_RXLP; if (isr & AR_ISR_HP_RXOK) *masked |= ATH9K_INT_RXHP; if (isr & (AR_ISR_TXOK | AR_ISR_TXERR | AR_ISR_TXEOL)) { *masked |= ATH9K_INT_TX; if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { u32 s0, s1; s0 = REG_READ(ah, AR_ISR_S0); REG_WRITE(ah, AR_ISR_S0, s0); s1 = REG_READ(ah, AR_ISR_S1); REG_WRITE(ah, AR_ISR_S1, s1); isr &= ~(AR_ISR_TXOK | AR_ISR_TXERR | AR_ISR_TXEOL); } } if (isr & AR_ISR_GENTMR) { u32 s5; if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) s5 = REG_READ(ah, AR_ISR_S5_S); else s5 = REG_READ(ah, AR_ISR_S5); ah->intr_gen_timer_trigger = MS(s5, AR_ISR_S5_GENTIMER_TRIG); ah->intr_gen_timer_thresh = MS(s5, AR_ISR_S5_GENTIMER_THRESH); if (ah->intr_gen_timer_trigger) *masked |= ATH9K_INT_GENTIMER; if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { REG_WRITE(ah, AR_ISR_S5, s5); isr &= ~AR_ISR_GENTMR; } } *masked |= mask2; if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { REG_WRITE(ah, AR_ISR, isr); (void) REG_READ(ah, AR_ISR); } if (*masked & ATH9K_INT_BB_WATCHDOG) ar9003_hw_bb_watchdog_read(ah); } if (async_cause & AR_INTR_ASYNC_MASK_MCI) ar9003_mci_get_isr(ah, masked); if (sync_cause) { ath9k_debug_sync_cause(common, sync_cause); if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); REG_WRITE(ah, AR_RC, 0); *masked |= ATH9K_INT_FATAL; } if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) ath_dbg(common, INTERRUPT, "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); (void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR); } return true; } static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_status *ts) { struct ar9003_txs *ads; u32 status; ads = &ah->ts_ring[ah->ts_tail]; status = ACCESS_ONCE(ads->status8); if ((status & AR_TxDone) == 0) return -EINPROGRESS; ah->ts_tail = (ah->ts_tail + 1) % ah->ts_size; if ((MS(ads->ds_info, AR_DescId) != ATHEROS_VENDOR_ID) || (MS(ads->ds_info, AR_TxRxDesc) != 1)) { ath_dbg(ath9k_hw_common(ah), XMIT, "Tx Descriptor error %x\n", ads->ds_info); memset(ads, 0, sizeof(*ads)); return -EIO; } ts->ts_rateindex = MS(status, AR_FinalTxIdx); ts->ts_seqnum = MS(status, AR_SeqNum); ts->tid = MS(status, AR_TxTid); ts->qid = MS(ads->ds_info, AR_TxQcuNum); ts->desc_id = MS(ads->status1, AR_TxDescId); ts->ts_tstamp = ads->status4; ts->ts_status = 0; ts->ts_flags = 0; if (status & AR_TxOpExceeded) ts->ts_status |= ATH9K_TXERR_XTXOP; status = ACCESS_ONCE(ads->status2); ts->ts_rssi_ctl0 = MS(status, AR_TxRSSIAnt00); ts->ts_rssi_ctl1 = MS(status, AR_TxRSSIAnt01); ts->ts_rssi_ctl2 = MS(status, AR_TxRSSIAnt02); if (status & AR_TxBaStatus) { ts->ts_flags |= ATH9K_TX_BA; ts->ba_low = ads->status5; ts->ba_high = ads->status6; } status = ACCESS_ONCE(ads->status3); if (status & AR_ExcessiveRetries) ts->ts_status |= ATH9K_TXERR_XRETRY; if (status & AR_Filtered) ts->ts_status |= ATH9K_TXERR_FILT; if (status & AR_FIFOUnderrun) { ts->ts_status |= ATH9K_TXERR_FIFO; ath9k_hw_updatetxtriglevel(ah, true); } if (status & AR_TxTimerExpired) ts->ts_status |= ATH9K_TXERR_TIMER_EXPIRED; if (status & AR_DescCfgErr) ts->ts_flags |= ATH9K_TX_DESC_CFG_ERR; if (status & AR_TxDataUnderrun) { ts->ts_flags |= ATH9K_TX_DATA_UNDERRUN; ath9k_hw_updatetxtriglevel(ah, true); } if (status & AR_TxDelimUnderrun) { ts->ts_flags |= ATH9K_TX_DELIM_UNDERRUN; ath9k_hw_updatetxtriglevel(ah, true); } ts->ts_shortretry = MS(status, AR_RTSFailCnt); ts->ts_longretry = MS(status, AR_DataFailCnt); ts->ts_virtcol = MS(status, AR_VirtRetryCnt); status = ACCESS_ONCE(ads->status7); ts->ts_rssi = MS(status, AR_TxRSSICombined); ts->ts_rssi_ext0 = MS(status, AR_TxRSSIAnt10); ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11); ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12); memset(ads, 0, sizeof(*ads)); return 0; } void ar9003_hw_attach_mac_ops(struct ath_hw *hw) { struct ath_hw_ops *ops = ath9k_hw_ops(hw); ops->rx_enable = ar9003_hw_rx_enable; ops->set_desc_link = ar9003_hw_set_desc_link; ops->get_isr = ar9003_hw_get_isr; ops->set_txdesc = ar9003_set_txdesc; ops->proc_txdesc = ar9003_hw_proc_txdesc; } void ath9k_hw_set_rx_bufsize(struct ath_hw *ah, u16 buf_size) { REG_WRITE(ah, AR_DATABUF_SIZE, buf_size & AR_DATABUF_SIZE_MASK); } EXPORT_SYMBOL(ath9k_hw_set_rx_bufsize); void ath9k_hw_addrxbuf_edma(struct ath_hw *ah, u32 rxdp, enum ath9k_rx_qtype qtype) { if (qtype == ATH9K_RX_QUEUE_HP) REG_WRITE(ah, AR_HP_RXDP, rxdp); else REG_WRITE(ah, AR_LP_RXDP, rxdp); } EXPORT_SYMBOL(ath9k_hw_addrxbuf_edma); int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, void *buf_addr) { struct ar9003_rxs *rxsp = (struct ar9003_rxs *) buf_addr; unsigned int phyerr; if ((rxsp->status11 & AR_RxDone) == 0) return -EINPROGRESS; if (MS(rxsp->ds_info, AR_DescId) != 0x168c) return -EINVAL; if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0) return -EINPROGRESS; rxs->rs_status = 0; rxs->rs_flags = 0; rxs->rs_datalen = rxsp->status2 & AR_DataLen; rxs->rs_tstamp = rxsp->status3; /* XXX: Keycache */ rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined); rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00); rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01); rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02); rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10); rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11); rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12); if (rxsp->status11 & AR_RxKeyIdxValid) rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx); else rxs->rs_keyix = ATH9K_RXKEYIX_INVALID; rxs->rs_rate = MS(rxsp->status1, AR_RxRate); rxs->rs_more = (rxsp->status2 & AR_RxMore) ? 1 : 0; rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0; rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0; rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7); rxs->rs_flags = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0; rxs->rs_flags |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0; rxs->evm0 = rxsp->status6; rxs->evm1 = rxsp->status7; rxs->evm2 = rxsp->status8; rxs->evm3 = rxsp->status9; rxs->evm4 = (rxsp->status10 & 0xffff); if (rxsp->status11 & AR_PreDelimCRCErr) rxs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE; if (rxsp->status11 & AR_PostDelimCRCErr) rxs->rs_flags |= ATH9K_RX_DELIM_CRC_POST; if (rxsp->status11 & AR_DecryptBusyErr) rxs->rs_flags |= ATH9K_RX_DECRYPT_BUSY; if ((rxsp->status11 & AR_RxFrameOK) == 0) { /* * AR_CRCErr will bet set to true if we're on the last * subframe and the AR_PostDelimCRCErr is caught. * In a way this also gives us a guarantee that when * (!(AR_CRCErr) && (AR_PostDelimCRCErr)) we cannot * possibly be reviewing the last subframe. AR_CRCErr * is the CRC of the actual data. */ if (rxsp->status11 & AR_CRCErr) rxs->rs_status |= ATH9K_RXERR_CRC; else if (rxsp->status11 & AR_DecryptCRCErr) rxs->rs_status |= ATH9K_RXERR_DECRYPT; else if (rxsp->status11 & AR_MichaelErr) rxs->rs_status |= ATH9K_RXERR_MIC; if (rxsp->status11 & AR_PHYErr) { phyerr = MS(rxsp->status11, AR_PHYErrCode); /* * If we reach a point here where AR_PostDelimCRCErr is * true it implies we're *not* on the last subframe. In * in that case that we know already that the CRC of * the frame was OK, and MAC would send an ACK for that * subframe, even if we did get a phy error of type * ATH9K_PHYERR_OFDM_RESTART. This is only applicable * to frame that are prior to the last subframe. * The AR_PostDelimCRCErr is the CRC for the MPDU * delimiter, which contains the 4 reserved bits, * the MPDU length (12 bits), and follows the MPDU * delimiter for an A-MPDU subframe (0x4E = 'N' ASCII). */ if ((phyerr == ATH9K_PHYERR_OFDM_RESTART) && (rxsp->status11 & AR_PostDelimCRCErr)) { rxs->rs_phyerr = 0; } else { rxs->rs_status |= ATH9K_RXERR_PHY; rxs->rs_phyerr = phyerr; } } } if (rxsp->status11 & AR_KeyMiss) rxs->rs_status |= ATH9K_RXERR_KEYMISS; return 0; } EXPORT_SYMBOL(ath9k_hw_process_rxdesc_edma); void ath9k_hw_reset_txstatus_ring(struct ath_hw *ah) { ah->ts_tail = 0; memset((void *) ah->ts_ring, 0, ah->ts_size * sizeof(struct ar9003_txs)); ath_dbg(ath9k_hw_common(ah), XMIT, "TS Start 0x%x End 0x%x Virt %p, Size %d\n", ah->ts_paddr_start, ah->ts_paddr_end, ah->ts_ring, ah->ts_size); REG_WRITE(ah, AR_Q_STATUS_RING_START, ah->ts_paddr_start); REG_WRITE(ah, AR_Q_STATUS_RING_END, ah->ts_paddr_end); } void ath9k_hw_setup_statusring(struct ath_hw *ah, void *ts_start, u32 ts_paddr_start, u16 size) { ah->ts_paddr_start = ts_paddr_start; ah->ts_paddr_end = ts_paddr_start + (size * sizeof(struct ar9003_txs)); ah->ts_size = size; ah->ts_ring = (struct ar9003_txs *) ts_start; ath9k_hw_reset_txstatus_ring(ah); } EXPORT_SYMBOL(ath9k_hw_setup_statusring); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_hw.c0000644000175000017500000004715712026211315024053 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "ar9003_mac.h" #include "ar9003_2p2_initvals.h" #include "ar9485_initvals.h" #include "ar9340_initvals.h" #include "ar9330_1p1_initvals.h" #include "ar9330_1p2_initvals.h" #include "ar955x_1p0_initvals.h" #include "ar9580_1p0_initvals.h" #include "ar9462_2p0_initvals.h" #include "ar9565_1p0_initvals.h" /* General hardware code for the AR9003 hadware family */ /* * The AR9003 family uses a new INI format (pre, core, post * arrays per subsystem). This provides support for the * AR9003 2.2 chipsets. */ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) { #define AR9462_BB_CTX_COEFJ(x) \ ar9462_##x##_baseband_core_txfir_coeff_japan_2484 #define AR9462_BBC_TXIFR_COEFFJ \ ar9462_2p0_baseband_core_txfir_coeff_japan_2484 if (AR_SREV_9330_11(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9331_1p1_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9331_1p1_mac_postamble); /* bb */ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9331_1p1_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9331_1p1_baseband_postamble); /* radio */ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9331_1p1_radio_core); /* soc */ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9331_1p1_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar9331_1p1_soc_postamble); /* rx/tx gain */ INIT_INI_ARRAY(&ah->iniModesRxGain, ar9331_common_rx_gain_1p1); INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p1); /* additional clock settings */ if (ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, ar9331_1p1_xtal_25M); else INIT_INI_ARRAY(&ah->iniAdditional, ar9331_1p1_xtal_40M); } else if (AR_SREV_9330_12(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9331_1p2_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9331_1p2_mac_postamble); /* bb */ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9331_1p2_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9331_1p2_baseband_postamble); /* radio */ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9331_1p2_radio_core); /* soc */ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9331_1p2_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar9331_1p2_soc_postamble); /* rx/tx gain */ INIT_INI_ARRAY(&ah->iniModesRxGain, ar9331_common_rx_gain_1p2); INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p2); /* additional clock settings */ if (ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, ar9331_1p2_xtal_25M); else INIT_INI_ARRAY(&ah->iniAdditional, ar9331_1p2_xtal_40M); } else if (AR_SREV_9340(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9340_1p0_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9340_1p0_mac_postamble); /* bb */ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9340_1p0_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9340_1p0_baseband_postamble); /* radio */ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9340_1p0_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], ar9340_1p0_radio_postamble); /* soc */ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9340_1p0_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar9340_1p0_soc_postamble); /* rx/tx gain */ INIT_INI_ARRAY(&ah->iniModesRxGain, ar9340Common_wo_xlna_rx_gain_table_1p0); INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_high_ob_db_tx_gain_table_1p0); INIT_INI_ARRAY(&ah->iniModesFastClock, ar9340Modes_fast_clock_1p0); if (!ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, ar9340_1p0_radio_core_40M); } else if (AR_SREV_9485_11(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9485_1_1_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9485_1_1_mac_postamble); /* bb */ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], ar9485_1_1); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9485_1_1_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9485_1_1_baseband_postamble); /* radio */ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9485_1_1_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], ar9485_1_1_radio_postamble); /* soc */ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9485_1_1_soc_preamble); /* rx/tx gain */ INIT_INI_ARRAY(&ah->iniModesRxGain, ar9485Common_wo_xlna_rx_gain_1_1); INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485_modes_lowest_ob_db_tx_gain_1_1); /* Load PCIE SERDES settings from INI */ /* Awake Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdes, ar9485_1_1_pcie_phy_clkreq_disable_L1); /* Sleep Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, ar9485_1_1_pcie_phy_clkreq_disable_L1); } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9462_2p0_mac_postamble); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9462_2p0_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9462_2p0_baseband_postamble); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9462_2p0_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], ar9462_2p0_radio_postamble); INIT_INI_ARRAY(&ah->ini_radio_post_sys2ant, ar9462_2p0_radio_postamble_sys2ant); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9462_2p0_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar9462_2p0_soc_postamble); INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_rx_gain_table_2p0); /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdes, ar9462_pciephy_pll_on_clkreq_disable_L1_2p0); /* Sleep -> Awake Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, ar9462_pciephy_pll_on_clkreq_disable_L1_2p0); /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, ar9462_modes_fast_clock_2p0); INIT_INI_ARRAY(&ah->iniCckfirJapan2484, AR9462_BB_CTX_COEFJ(2p0)); INIT_INI_ARRAY(&ah->ini_japan2484, AR9462_BBC_TXIFR_COEFFJ); } else if (AR_SREV_9550(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar955x_1p0_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar955x_1p0_mac_postamble); /* bb */ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar955x_1p0_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar955x_1p0_baseband_postamble); /* radio */ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar955x_1p0_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], ar955x_1p0_radio_postamble); /* soc */ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar955x_1p0_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar955x_1p0_soc_postamble); /* rx/tx gain */ INIT_INI_ARRAY(&ah->iniModesRxGain, ar955x_1p0_common_wo_xlna_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, ar955x_1p0_common_wo_xlna_rx_gain_bounds); INIT_INI_ARRAY(&ah->iniModesTxGain, ar955x_1p0_modes_xpa_tx_gain_table); /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, ar955x_1p0_modes_fast_clock); } else if (AR_SREV_9580(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9580_1p0_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9580_1p0_mac_postamble); /* bb */ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9580_1p0_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9580_1p0_baseband_postamble); /* radio */ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9580_1p0_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], ar9580_1p0_radio_postamble); /* soc */ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9580_1p0_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar9580_1p0_soc_postamble); /* rx/tx gain */ INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_rx_gain_table); INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_low_ob_db_tx_gain_table); INIT_INI_ARRAY(&ah->iniModesFastClock, ar9580_1p0_modes_fast_clock); } else if (AR_SREV_9565(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9565_1p0_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9565_1p0_mac_postamble); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9565_1p0_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9565_1p0_baseband_postamble); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9565_1p0_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], ar9565_1p0_radio_postamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9565_1p0_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar9565_1p0_soc_postamble); INIT_INI_ARRAY(&ah->iniModesRxGain, ar9565_1p0_Common_rx_gain_table); INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_Modes_lowest_ob_db_tx_gain_table); INIT_INI_ARRAY(&ah->iniPcieSerdes, ar9565_1p0_pciephy_pll_on_clkreq_disable_L1); INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, ar9565_1p0_pciephy_pll_on_clkreq_disable_L1); INIT_INI_ARRAY(&ah->iniModesFastClock, ar9565_1p0_modes_fast_clock); } else { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9300_2p2_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], ar9300_2p2_mac_postamble); /* bb */ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], ar9300_2p2_baseband_core); INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], ar9300_2p2_baseband_postamble); /* radio */ INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], ar9300_2p2_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], ar9300_2p2_radio_postamble); /* soc */ INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], ar9300_2p2_soc_preamble); INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], ar9300_2p2_soc_postamble); /* rx/tx gain */ INIT_INI_ARRAY(&ah->iniModesRxGain, ar9300Common_rx_gain_table_2p2); INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_lowest_ob_db_tx_gain_table_2p2); /* Load PCIE SERDES settings from INI */ /* Awake Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdes, ar9300PciePhy_pll_on_clkreq_disable_L1_2p2); /* Sleep Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, ar9300PciePhy_pll_on_clkreq_disable_L1_2p2); /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, ar9300Modes_fast_clock_2p2); } } static void ar9003_tx_gain_table_mode0(struct ath_hw *ah) { if (AR_SREV_9330_12(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p2); else if (AR_SREV_9330_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p1); else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_lowest_ob_db_tx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485_modes_lowest_ob_db_tx_gain_1_1); else if (AR_SREV_9550(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar955x_1p0_modes_xpa_tx_gain_table); else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_lowest_ob_db_tx_gain_table); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_modes_low_ob_db_tx_gain_table_2p0); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_low_ob_db_tx_gain_table); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_lowest_ob_db_tx_gain_table_2p2); } static void ar9003_tx_gain_table_mode1(struct ath_hw *ah) { if (AR_SREV_9330_12(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_high_ob_db_tx_gain_1p2); else if (AR_SREV_9330_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_high_ob_db_tx_gain_1p1); else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_high_ob_db_tx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_high_ob_db_tx_gain_1_1); else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_high_ob_db_tx_gain_table); else if (AR_SREV_9550(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar955x_1p0_modes_no_xpa_tx_gain_table); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_modes_high_ob_db_tx_gain_table_2p0); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_high_ob_db_tx_gain_table); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_high_ob_db_tx_gain_table_2p2); } static void ar9003_tx_gain_table_mode2(struct ath_hw *ah) { if (AR_SREV_9330_12(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_low_ob_db_tx_gain_1p2); else if (AR_SREV_9330_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_low_ob_db_tx_gain_1p1); else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_low_ob_db_tx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_low_ob_db_tx_gain_1_1); else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_low_ob_db_tx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_low_ob_db_tx_gain_table); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_low_ob_db_tx_gain_table_2p2); } static void ar9003_tx_gain_table_mode3(struct ath_hw *ah) { if (AR_SREV_9330_12(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_high_power_tx_gain_1p2); else if (AR_SREV_9330_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_high_power_tx_gain_1p1); else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_high_power_tx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_high_power_tx_gain_1_1); else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_high_power_tx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_high_power_tx_gain_table); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_high_power_tx_gain_table_2p2); } static void ar9003_tx_gain_table_mode4(struct ath_hw *ah) { if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_mixed_ob_db_tx_gain_table_1p0); else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_mixed_ob_db_tx_gain_table); } static void ar9003_tx_gain_table_apply(struct ath_hw *ah) { switch (ar9003_hw_get_tx_gain_idx(ah)) { case 0: default: ar9003_tx_gain_table_mode0(ah); break; case 1: ar9003_tx_gain_table_mode1(ah); break; case 2: ar9003_tx_gain_table_mode2(ah); break; case 3: ar9003_tx_gain_table_mode3(ah); break; case 4: ar9003_tx_gain_table_mode4(ah); break; } } static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) { if (AR_SREV_9330_12(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9331_common_rx_gain_1p2); else if (AR_SREV_9330_11(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9331_common_rx_gain_1p1); else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9340Common_rx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9485Common_wo_xlna_rx_gain_1_1); else if (AR_SREV_9550(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar955x_1p0_common_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, ar955x_1p0_common_rx_gain_bounds); } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_rx_gain_table); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_rx_gain_table_2p0); else INIT_INI_ARRAY(&ah->iniModesRxGain, ar9300Common_rx_gain_table_2p2); } static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) { if (AR_SREV_9330_12(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9331_common_wo_xlna_rx_gain_1p2); else if (AR_SREV_9330_11(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9331_common_wo_xlna_rx_gain_1p1); else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9340Common_wo_xlna_rx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9485Common_wo_xlna_rx_gain_1_1); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_wo_xlna_rx_gain_table_2p0); else if (AR_SREV_9550(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar955x_1p0_common_wo_xlna_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, ar955x_1p0_common_wo_xlna_rx_gain_bounds); } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_wo_xlna_rx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9565_1p0_common_wo_xlna_rx_gain_table); else INIT_INI_ARRAY(&ah->iniModesRxGain, ar9300Common_wo_xlna_rx_gain_table_2p2); } static void ar9003_rx_gain_table_mode2(struct ath_hw *ah) { if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_mixed_rx_gain_table_2p0); } static void ar9003_rx_gain_table_apply(struct ath_hw *ah) { switch (ar9003_hw_get_rx_gain_idx(ah)) { case 0: default: ar9003_rx_gain_table_mode0(ah); break; case 1: ar9003_rx_gain_table_mode1(ah); break; case 2: ar9003_rx_gain_table_mode2(ah); break; } } /* set gain table pointers according to values read from the eeprom */ static void ar9003_hw_init_mode_gain_regs(struct ath_hw *ah) { ar9003_tx_gain_table_apply(ah); ar9003_rx_gain_table_apply(ah); } /* * Helper for ASPM support. * * Disable PLL when in L0s as well as receiver clock when in L1. * This power saving option must be enabled through the SerDes. * * Programming the SerDes must go through the same 288 bit serial shift * register as the other analog registers. Hence the 9 writes. */ static void ar9003_hw_configpcipowersave(struct ath_hw *ah, bool power_off) { /* Nothing to do on restore for 11N */ if (!power_off /* !restore */) { /* set bit 19 to allow forcing of pcie core into L1 state */ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); /* Several PCIe massages to ensure proper behaviour */ if (ah->config.pcie_waen) REG_WRITE(ah, AR_WA, ah->config.pcie_waen); else REG_WRITE(ah, AR_WA, ah->WARegVal); } /* * Configire PCIE after Ini init. SERDES values now come from ini file * This enables PCIe low power mode. */ if (ah->config.pcieSerDesWrite) { unsigned int i; struct ar5416IniArray *array; array = power_off ? &ah->iniPcieSerdes : &ah->iniPcieSerdesLowPower; for (i = 0; i < array->ia_rows; i++) { REG_WRITE(ah, INI_RA(array, i, 0), INI_RA(array, i, 1)); } } } /* Sets up the AR9003 hardware familiy callbacks */ void ar9003_hw_attach_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); priv_ops->init_mode_regs = ar9003_hw_init_mode_regs; priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs; ops->config_pci_powersave = ar9003_hw_configpcipowersave; ar9003_hw_attach_phy_ops(ah); ar9003_hw_attach_calib_ops(ah); ar9003_hw_attach_mac_ops(ah); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h0000644000175000017500000002315612026211315024722 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AR9003_EEPROM_H #define AR9003_EEPROM_H #include #define AR9300_EEP_VER 0xD000 #define AR9300_EEP_VER_MINOR_MASK 0xFFF #define AR9300_EEP_MINOR_VER_1 0x1 #define AR9300_EEP_MINOR_VER AR9300_EEP_MINOR_VER_1 /* 16-bit offset location start of calibration struct */ #define AR9300_EEP_START_LOC 256 #define AR9300_NUM_5G_CAL_PIERS 8 #define AR9300_NUM_2G_CAL_PIERS 3 #define AR9300_NUM_5G_20_TARGET_POWERS 8 #define AR9300_NUM_5G_40_TARGET_POWERS 8 #define AR9300_NUM_2G_CCK_TARGET_POWERS 2 #define AR9300_NUM_2G_20_TARGET_POWERS 3 #define AR9300_NUM_2G_40_TARGET_POWERS 3 /* #define AR9300_NUM_CTLS 21 */ #define AR9300_NUM_CTLS_5G 9 #define AR9300_NUM_CTLS_2G 12 #define AR9300_NUM_BAND_EDGES_5G 8 #define AR9300_NUM_BAND_EDGES_2G 4 #define AR9300_EEPMISC_BIG_ENDIAN 0x01 #define AR9300_EEPMISC_WOW 0x02 #define AR9300_CUSTOMER_DATA_SIZE 20 #define AR9300_MAX_CHAINS 3 #define AR9300_ANT_16S 25 #define AR9300_FUTURE_MODAL_SZ 6 #define AR9300_PAPRD_RATE_MASK 0x01ffffff #define AR9300_PAPRD_SCALE_1 0x0e000000 #define AR9300_PAPRD_SCALE_1_S 25 #define AR9300_PAPRD_SCALE_2 0x70000000 #define AR9300_PAPRD_SCALE_2_S 28 /* Delta from which to start power to pdadc table */ /* This offset is used in both open loop and closed loop power control * schemes. In open loop power control, it is not really needed, but for * the "sake of consistency" it was kept. For certain AP designs, this * value is overwritten by the value in the flag "pwrTableOffset" just * before writing the pdadc vs pwr into the chip registers. */ #define AR9300_PWR_TABLE_OFFSET 0 /* byte addressable */ #define AR9300_EEPROM_SIZE (16*1024) #define AR9300_BASE_ADDR_4K 0xfff #define AR9300_BASE_ADDR 0x3ff #define AR9300_BASE_ADDR_512 0x1ff #define AR9300_OTP_BASE 0x14000 #define AR9300_OTP_STATUS 0x15f18 #define AR9300_OTP_STATUS_TYPE 0x7 #define AR9300_OTP_STATUS_VALID 0x4 #define AR9300_OTP_STATUS_ACCESS_BUSY 0x2 #define AR9300_OTP_STATUS_SM_BUSY 0x1 #define AR9300_OTP_READ_DATA 0x15f1c enum targetPowerHTRates { HT_TARGET_RATE_0_8_16, HT_TARGET_RATE_1_3_9_11_17_19, HT_TARGET_RATE_4, HT_TARGET_RATE_5, HT_TARGET_RATE_6, HT_TARGET_RATE_7, HT_TARGET_RATE_12, HT_TARGET_RATE_13, HT_TARGET_RATE_14, HT_TARGET_RATE_15, HT_TARGET_RATE_20, HT_TARGET_RATE_21, HT_TARGET_RATE_22, HT_TARGET_RATE_23 }; enum targetPowerLegacyRates { LEGACY_TARGET_RATE_6_24, LEGACY_TARGET_RATE_36, LEGACY_TARGET_RATE_48, LEGACY_TARGET_RATE_54 }; enum targetPowerCckRates { LEGACY_TARGET_RATE_1L_5L, LEGACY_TARGET_RATE_5S, LEGACY_TARGET_RATE_11L, LEGACY_TARGET_RATE_11S }; enum ar9300_Rates { ALL_TARGET_LEGACY_6_24, ALL_TARGET_LEGACY_36, ALL_TARGET_LEGACY_48, ALL_TARGET_LEGACY_54, ALL_TARGET_LEGACY_1L_5L, ALL_TARGET_LEGACY_5S, ALL_TARGET_LEGACY_11L, ALL_TARGET_LEGACY_11S, ALL_TARGET_HT20_0_8_16, ALL_TARGET_HT20_1_3_9_11_17_19, ALL_TARGET_HT20_4, ALL_TARGET_HT20_5, ALL_TARGET_HT20_6, ALL_TARGET_HT20_7, ALL_TARGET_HT20_12, ALL_TARGET_HT20_13, ALL_TARGET_HT20_14, ALL_TARGET_HT20_15, ALL_TARGET_HT20_20, ALL_TARGET_HT20_21, ALL_TARGET_HT20_22, ALL_TARGET_HT20_23, ALL_TARGET_HT40_0_8_16, ALL_TARGET_HT40_1_3_9_11_17_19, ALL_TARGET_HT40_4, ALL_TARGET_HT40_5, ALL_TARGET_HT40_6, ALL_TARGET_HT40_7, ALL_TARGET_HT40_12, ALL_TARGET_HT40_13, ALL_TARGET_HT40_14, ALL_TARGET_HT40_15, ALL_TARGET_HT40_20, ALL_TARGET_HT40_21, ALL_TARGET_HT40_22, ALL_TARGET_HT40_23, ar9300RateSize, }; struct eepFlags { u8 opFlags; u8 eepMisc; } __packed; enum CompressAlgorithm { _CompressNone = 0, _CompressLzma, _CompressPairs, _CompressBlock, _Compress4, _Compress5, _Compress6, _Compress7, }; struct ar9300_base_eep_hdr { __le16 regDmn[2]; /* 4 bits tx and 4 bits rx */ u8 txrxMask; struct eepFlags opCapFlags; u8 rfSilent; u8 blueToothOptions; u8 deviceCap; /* takes lower byte in eeprom location */ u8 deviceType; /* offset in dB to be added to beginning * of pdadc table in calibration */ int8_t pwrTableOffset; u8 params_for_tuning_caps[2]; /* * bit0 - enable tx temp comp * bit1 - enable tx volt comp * bit2 - enable fastClock - default to 1 * bit3 - enable doubling - default to 1 * bit4 - enable internal regulator - default to 1 */ u8 featureEnable; /* misc flags: bit0 - turn down drivestrength */ u8 miscConfiguration; u8 eepromWriteEnableGpio; u8 wlanDisableGpio; u8 wlanLedGpio; u8 rxBandSelectGpio; u8 txrxgain; /* SW controlled internal regulator fields */ __le32 swreg; } __packed; struct ar9300_modal_eep_header { /* 4 idle, t1, t2, b (4 bits per setting) */ __le32 antCtrlCommon; /* 4 ra1l1, ra2l1, ra1l2, ra2l2, ra12 */ __le32 antCtrlCommon2; /* 6 idle, t, r, rx1, rx12, b (2 bits each) */ __le16 antCtrlChain[AR9300_MAX_CHAINS]; /* 3 xatten1_db for AR9280 (0xa20c/b20c 5:0) */ u8 xatten1DB[AR9300_MAX_CHAINS]; /* 3 xatten1_margin for merlin (0xa20c/b20c 16:12 */ u8 xatten1Margin[AR9300_MAX_CHAINS]; int8_t tempSlope; int8_t voltSlope; /* spur channels in usual fbin coding format */ u8 spurChans[AR_EEPROM_MODAL_SPURS]; /* 3 Check if the register is per chain */ int8_t noiseFloorThreshCh[AR9300_MAX_CHAINS]; u8 reserved[11]; int8_t quick_drop; u8 xpaBiasLvl; u8 txFrameToDataStart; u8 txFrameToPaOn; u8 txClip; int8_t antennaGain; u8 switchSettling; int8_t adcDesiredSize; u8 txEndToXpaOff; u8 txEndToRxOn; u8 txFrameToXpaOn; u8 thresh62; __le32 papdRateMaskHt20; __le32 papdRateMaskHt40; __le16 switchcomspdt; u8 xlna_bias_strength; u8 futureModal[7]; } __packed; struct ar9300_cal_data_per_freq_op_loop { int8_t refPower; /* pdadc voltage at power measurement */ u8 voltMeas; /* pcdac used for power measurement */ u8 tempMeas; /* range is -60 to -127 create a mapping equation 1db resolution */ int8_t rxNoisefloorCal; /*range is same as noisefloor */ int8_t rxNoisefloorPower; /* temp measured when noisefloor cal was performed */ u8 rxTempMeas; } __packed; struct cal_tgt_pow_legacy { u8 tPow2x[4]; } __packed; struct cal_tgt_pow_ht { u8 tPow2x[14]; } __packed; struct cal_ctl_data_2g { u8 ctlEdges[AR9300_NUM_BAND_EDGES_2G]; } __packed; struct cal_ctl_data_5g { u8 ctlEdges[AR9300_NUM_BAND_EDGES_5G]; } __packed; struct ar9300_BaseExtension_1 { u8 ant_div_control; u8 future[3]; u8 tempslopextension[8]; int8_t quick_drop_low; int8_t quick_drop_high; } __packed; struct ar9300_BaseExtension_2 { int8_t tempSlopeLow; int8_t tempSlopeHigh; u8 xatten1DBLow[AR9300_MAX_CHAINS]; u8 xatten1MarginLow[AR9300_MAX_CHAINS]; u8 xatten1DBHigh[AR9300_MAX_CHAINS]; u8 xatten1MarginHigh[AR9300_MAX_CHAINS]; } __packed; struct ar9300_eeprom { u8 eepromVersion; u8 templateVersion; u8 macAddr[6]; u8 custData[AR9300_CUSTOMER_DATA_SIZE]; struct ar9300_base_eep_hdr baseEepHeader; struct ar9300_modal_eep_header modalHeader2G; struct ar9300_BaseExtension_1 base_ext1; u8 calFreqPier2G[AR9300_NUM_2G_CAL_PIERS]; struct ar9300_cal_data_per_freq_op_loop calPierData2G[AR9300_MAX_CHAINS][AR9300_NUM_2G_CAL_PIERS]; u8 calTarget_freqbin_Cck[AR9300_NUM_2G_CCK_TARGET_POWERS]; u8 calTarget_freqbin_2G[AR9300_NUM_2G_20_TARGET_POWERS]; u8 calTarget_freqbin_2GHT20[AR9300_NUM_2G_20_TARGET_POWERS]; u8 calTarget_freqbin_2GHT40[AR9300_NUM_2G_40_TARGET_POWERS]; struct cal_tgt_pow_legacy calTargetPowerCck[AR9300_NUM_2G_CCK_TARGET_POWERS]; struct cal_tgt_pow_legacy calTargetPower2G[AR9300_NUM_2G_20_TARGET_POWERS]; struct cal_tgt_pow_ht calTargetPower2GHT20[AR9300_NUM_2G_20_TARGET_POWERS]; struct cal_tgt_pow_ht calTargetPower2GHT40[AR9300_NUM_2G_40_TARGET_POWERS]; u8 ctlIndex_2G[AR9300_NUM_CTLS_2G]; u8 ctl_freqbin_2G[AR9300_NUM_CTLS_2G][AR9300_NUM_BAND_EDGES_2G]; struct cal_ctl_data_2g ctlPowerData_2G[AR9300_NUM_CTLS_2G]; struct ar9300_modal_eep_header modalHeader5G; struct ar9300_BaseExtension_2 base_ext2; u8 calFreqPier5G[AR9300_NUM_5G_CAL_PIERS]; struct ar9300_cal_data_per_freq_op_loop calPierData5G[AR9300_MAX_CHAINS][AR9300_NUM_5G_CAL_PIERS]; u8 calTarget_freqbin_5G[AR9300_NUM_5G_20_TARGET_POWERS]; u8 calTarget_freqbin_5GHT20[AR9300_NUM_5G_20_TARGET_POWERS]; u8 calTarget_freqbin_5GHT40[AR9300_NUM_5G_40_TARGET_POWERS]; struct cal_tgt_pow_legacy calTargetPower5G[AR9300_NUM_5G_20_TARGET_POWERS]; struct cal_tgt_pow_ht calTargetPower5GHT20[AR9300_NUM_5G_20_TARGET_POWERS]; struct cal_tgt_pow_ht calTargetPower5GHT40[AR9300_NUM_5G_40_TARGET_POWERS]; u8 ctlIndex_5G[AR9300_NUM_CTLS_5G]; u8 ctl_freqbin_5G[AR9300_NUM_CTLS_5G][AR9300_NUM_BAND_EDGES_5G]; struct cal_ctl_data_5g ctlPowerData_5G[AR9300_NUM_CTLS_5G]; } __packed; s32 ar9003_hw_get_tx_gain_idx(struct ath_hw *ah); s32 ar9003_hw_get_rx_gain_idx(struct ath_hw *ah); u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz); unsigned int ar9003_get_paprd_scale_factor(struct ath_hw *ah, struct ath9k_channel *chan); void ar9003_hw_internal_regulator_apply(struct ath_hw *ah); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c0000644000175000017500000045142212026211315024716 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar9003_phy.h" #include "ar9003_eeprom.h" #define COMP_HDR_LEN 4 #define COMP_CKSUM_LEN 2 #define LE16(x) __constant_cpu_to_le16(x) #define LE32(x) __constant_cpu_to_le32(x) /* Local defines to distinguish between extension and control CTL's */ #define EXT_ADDITIVE (0x8000) #define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) #define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */ #define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ #define CTL(_tpower, _flag) ((_tpower) | ((_flag) << 6)) #define EEPROM_DATA_LEN_9485 1088 static int ar9003_hw_power_interpolate(int32_t x, int32_t *px, int32_t *py, u_int16_t np); static const struct ar9300_eeprom ar9300_default = { .eepromVersion = 2, .templateVersion = 2, .macAddr = {0, 2, 3, 4, 5, 6}, .custData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .baseEepHeader = { .regDmn = { LE16(0), LE16(0x1f) }, .txrxMask = 0x77, /* 4 bits tx and 4 bits rx */ .opCapFlags = { .opFlags = AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A, .eepMisc = 0, }, .rfSilent = 0, .blueToothOptions = 0, .deviceCap = 0, .deviceType = 5, /* takes lower byte in eeprom location */ .pwrTableOffset = AR9300_PWR_TABLE_OFFSET, .params_for_tuning_caps = {0, 0}, .featureEnable = 0x0c, /* * bit0 - enable tx temp comp - disabled * bit1 - enable tx volt comp - disabled * bit2 - enable fastClock - enabled * bit3 - enable doubling - enabled * bit4 - enable internal regulator - disabled * bit5 - enable pa predistortion - disabled */ .miscConfiguration = 0, /* bit0 - turn down drivestrength */ .eepromWriteEnableGpio = 3, .wlanDisableGpio = 0, .wlanLedGpio = 8, .rxBandSelectGpio = 0xff, .txrxgain = 0, .swreg = 0, }, .modalHeader2G = { /* ar9300_modal_eep_header 2g */ /* 4 idle,t1,t2,b(4 bits per setting) */ .antCtrlCommon = LE32(0x110), /* 4 ra1l1, ra2l1, ra1l2, ra2l2, ra12 */ .antCtrlCommon2 = LE32(0x22222), /* * antCtrlChain[AR9300_MAX_CHAINS]; 6 idle, t, r, * rx1, rx12, b (2 bits each) */ .antCtrlChain = { LE16(0x150), LE16(0x150), LE16(0x150) }, /* * xatten1DB[AR9300_MAX_CHAINS]; 3 xatten1_db * for ar9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0, 0, 0}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for ar9280 (0xa20c/b20c 16:12 */ .xatten1Margin = {0, 0, 0}, .tempSlope = 36, .voltSlope = 0, /* * spurChans[OSPREY_EEPROM_MODAL_SPURS]; spur * channels in usual fbin coding format */ .spurChans = {0, 0, 0, 0, 0}, /* * noiseFloorThreshCh[AR9300_MAX_CHAINS]; 3 Check * if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2c, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext1 = { .ant_div_control = 0, .future = {0, 0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1), }, /* ar9300_cal_data_per_freq_op_loop 2g */ .calPierData2G = { { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, }, .calTarget_freqbin_Cck = { FREQ2FBIN(2412, 1), FREQ2FBIN(2484, 1), }, .calTarget_freqbin_2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT20 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT40 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTargetPowerCck = { /* 1L-5L,5S,11L,11S */ { {36, 36, 36, 36} }, { {36, 36, 36, 36} }, }, .calTargetPower2G = { /* 6-24,36,48,54 */ { {32, 32, 28, 24} }, { {32, 32, 28, 24} }, { {32, 32, 28, 24} }, }, .calTargetPower2GHT20 = { { {32, 32, 32, 32, 28, 20, 32, 32, 28, 20, 32, 32, 28, 20} }, { {32, 32, 32, 32, 28, 20, 32, 32, 28, 20, 32, 32, 28, 20} }, { {32, 32, 32, 32, 28, 20, 32, 32, 28, 20, 32, 32, 28, 20} }, }, .calTargetPower2GHT40 = { { {32, 32, 32, 32, 28, 20, 32, 32, 28, 20, 32, 32, 28, 20} }, { {32, 32, 32, 32, 28, 20, 32, 32, 28, 20, 32, 32, 28, 20} }, { {32, 32, 32, 32, 28, 20, 32, 32, 28, 20, 32, 32, 28, 20} }, }, .ctlIndex_2G = { 0x11, 0x12, 0x15, 0x17, 0x41, 0x42, 0x45, 0x47, 0x31, 0x32, 0x35, 0x37, }, .ctl_freqbin_2G = { { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2457, 1), FREQ2FBIN(2462, 1) }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2422, 1), FREQ2FBIN(2427, 1), FREQ2FBIN(2447, 1), FREQ2FBIN(2452, 1) }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(2484, 1), }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0, }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), FREQ2FBIN(2472, 1), 0, }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), }, { /* Data[9].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[9].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[9].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[10].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[10].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[10].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[11].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[11].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[11].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[11].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), } }, .ctlPowerData_2G = { { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, }, .modalHeader5G = { /* 4 idle,t1,t2,b (4 bits per setting) */ .antCtrlCommon = LE32(0x110), /* 4 ra1l1, ra2l1, ra1l2,ra2l2,ra12 */ .antCtrlCommon2 = LE32(0x22222), /* antCtrlChain 6 idle, t,r,rx1,rx12,b (2 bits each) */ .antCtrlChain = { LE16(0x000), LE16(0x000), LE16(0x000), }, /* xatten1DB 3 xatten1_db for AR9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0, 0, 0}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for merlin (0xa20c/b20c 16:12 */ .xatten1Margin = {0, 0, 0}, .tempSlope = 68, .voltSlope = 0, /* spurChans spur channels in usual fbin coding format */ .spurChans = {0, 0, 0, 0, 0}, /* noiseFloorThreshCh Check if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2d, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext2 = { .tempSlopeLow = 0, .tempSlopeHigh = 0, .xatten1DBLow = {0, 0, 0}, .xatten1MarginLow = {0, 0, 0}, .xatten1DBHigh = {0, 0, 0}, .xatten1MarginHigh = {0, 0, 0} }, .calFreqPier5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5725, 0), FREQ2FBIN(5825, 0) }, .calPierData5G = { { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, }, .calTarget_freqbin_5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5725, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT20 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5725, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT40 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5725, 0), FREQ2FBIN(5825, 0) }, .calTargetPower5G = { /* 6-24,36,48,54 */ { {20, 20, 20, 10} }, { {20, 20, 20, 10} }, { {20, 20, 20, 10} }, { {20, 20, 20, 10} }, { {20, 20, 20, 10} }, { {20, 20, 20, 10} }, { {20, 20, 20, 10} }, { {20, 20, 20, 10} }, }, .calTargetPower5GHT20 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, }, .calTargetPower5GHT40 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, { {20, 20, 10, 10, 0, 0, 10, 10, 0, 0, 10, 10, 0, 0} }, }, .ctlIndex_5G = { 0x10, 0x16, 0x18, 0x40, 0x46, 0x48, 0x30, 0x36, 0x38 }, .ctl_freqbin_5G = { { /* Data[0].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[0].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[0].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[0].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[0].ctlEdges[4].bChannel */ FREQ2FBIN(5600, 0), /* Data[0].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[0].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[0].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[1].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[1].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[1].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[1].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[1].ctlEdges[4].bChannel */ FREQ2FBIN(5520, 0), /* Data[1].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[1].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[1].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[2].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[2].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[2].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[2].ctlEdges[3].bChannel */ FREQ2FBIN(5310, 0), /* Data[2].ctlEdges[4].bChannel */ FREQ2FBIN(5510, 0), /* Data[2].ctlEdges[5].bChannel */ FREQ2FBIN(5550, 0), /* Data[2].ctlEdges[6].bChannel */ FREQ2FBIN(5670, 0), /* Data[2].ctlEdges[7].bChannel */ FREQ2FBIN(5755, 0) }, { /* Data[3].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[3].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[3].ctlEdges[2].bChannel */ FREQ2FBIN(5260, 0), /* Data[3].ctlEdges[3].bChannel */ FREQ2FBIN(5320, 0), /* Data[3].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[3].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[3].ctlEdges[6].bChannel */ 0xFF, /* Data[3].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(5500, 0), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(5700, 0), /* Data[4].ctlEdges[4].bChannel */ 0xFF, /* Data[4].ctlEdges[5].bChannel */ 0xFF, /* Data[4].ctlEdges[6].bChannel */ 0xFF, /* Data[4].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(5270, 0), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(5310, 0), /* Data[5].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[5].ctlEdges[4].bChannel */ FREQ2FBIN(5590, 0), /* Data[5].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[5].ctlEdges[6].bChannel */ 0xFF, /* Data[5].ctlEdges[7].bChannel */ 0xFF }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[6].ctlEdges[2].bChannel */ FREQ2FBIN(5220, 0), /* Data[6].ctlEdges[3].bChannel */ FREQ2FBIN(5260, 0), /* Data[6].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[6].ctlEdges[5].bChannel */ FREQ2FBIN(5600, 0), /* Data[6].ctlEdges[6].bChannel */ FREQ2FBIN(5700, 0), /* Data[6].ctlEdges[7].bChannel */ FREQ2FBIN(5745, 0) }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(5320, 0), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[7].ctlEdges[4].bChannel */ FREQ2FBIN(5560, 0), /* Data[7].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[7].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[7].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[8].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[8].ctlEdges[4].bChannel */ FREQ2FBIN(5550, 0), /* Data[8].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[8].ctlEdges[6].bChannel */ FREQ2FBIN(5755, 0), /* Data[8].ctlEdges[7].bChannel */ FREQ2FBIN(5795, 0) } }, .ctlPowerData_5G = { { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), } }, } }; static const struct ar9300_eeprom ar9300_x113 = { .eepromVersion = 2, .templateVersion = 6, .macAddr = {0x00, 0x03, 0x7f, 0x0, 0x0, 0x0}, .custData = {"x113-023-f0000"}, .baseEepHeader = { .regDmn = { LE16(0), LE16(0x1f) }, .txrxMask = 0x77, /* 4 bits tx and 4 bits rx */ .opCapFlags = { .opFlags = AR5416_OPFLAGS_11A, .eepMisc = 0, }, .rfSilent = 0, .blueToothOptions = 0, .deviceCap = 0, .deviceType = 5, /* takes lower byte in eeprom location */ .pwrTableOffset = AR9300_PWR_TABLE_OFFSET, .params_for_tuning_caps = {0, 0}, .featureEnable = 0x0d, /* * bit0 - enable tx temp comp - disabled * bit1 - enable tx volt comp - disabled * bit2 - enable fastClock - enabled * bit3 - enable doubling - enabled * bit4 - enable internal regulator - disabled * bit5 - enable pa predistortion - disabled */ .miscConfiguration = 0, /* bit0 - turn down drivestrength */ .eepromWriteEnableGpio = 6, .wlanDisableGpio = 0, .wlanLedGpio = 8, .rxBandSelectGpio = 0xff, .txrxgain = 0x21, .swreg = 0, }, .modalHeader2G = { /* ar9300_modal_eep_header 2g */ /* 4 idle,t1,t2,b(4 bits per setting) */ .antCtrlCommon = LE32(0x110), /* 4 ra1l1, ra2l1, ra1l2, ra2l2, ra12 */ .antCtrlCommon2 = LE32(0x44444), /* * antCtrlChain[AR9300_MAX_CHAINS]; 6 idle, t, r, * rx1, rx12, b (2 bits each) */ .antCtrlChain = { LE16(0x150), LE16(0x150), LE16(0x150) }, /* * xatten1DB[AR9300_MAX_CHAINS]; 3 xatten1_db * for ar9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0, 0, 0}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for ar9280 (0xa20c/b20c 16:12 */ .xatten1Margin = {0, 0, 0}, .tempSlope = 25, .voltSlope = 0, /* * spurChans[OSPREY_EEPROM_MODAL_SPURS]; spur * channels in usual fbin coding format */ .spurChans = {FREQ2FBIN(2464, 1), 0, 0, 0, 0}, /* * noiseFloorThreshCh[AR9300_MAX_CHAINS]; 3 Check * if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2c, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext1 = { .ant_div_control = 0, .future = {0, 0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1), }, /* ar9300_cal_data_per_freq_op_loop 2g */ .calPierData2G = { { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, }, .calTarget_freqbin_Cck = { FREQ2FBIN(2412, 1), FREQ2FBIN(2472, 1), }, .calTarget_freqbin_2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT20 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT40 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTargetPowerCck = { /* 1L-5L,5S,11L,11S */ { {34, 34, 34, 34} }, { {34, 34, 34, 34} }, }, .calTargetPower2G = { /* 6-24,36,48,54 */ { {34, 34, 32, 32} }, { {34, 34, 32, 32} }, { {34, 34, 32, 32} }, }, .calTargetPower2GHT20 = { { {32, 32, 32, 32, 32, 28, 32, 32, 30, 28, 0, 0, 0, 0} }, { {32, 32, 32, 32, 32, 28, 32, 32, 30, 28, 0, 0, 0, 0} }, { {32, 32, 32, 32, 32, 28, 32, 32, 30, 28, 0, 0, 0, 0} }, }, .calTargetPower2GHT40 = { { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 0, 0, 0, 0} }, { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 0, 0, 0, 0} }, { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 0, 0, 0, 0} }, }, .ctlIndex_2G = { 0x11, 0x12, 0x15, 0x17, 0x41, 0x42, 0x45, 0x47, 0x31, 0x32, 0x35, 0x37, }, .ctl_freqbin_2G = { { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2457, 1), FREQ2FBIN(2462, 1) }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2422, 1), FREQ2FBIN(2427, 1), FREQ2FBIN(2447, 1), FREQ2FBIN(2452, 1) }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(2484, 1), }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0, }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), FREQ2FBIN(2472, 1), 0, }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), }, { /* Data[9].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[9].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[9].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[10].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[10].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[10].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[11].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[11].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[11].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[11].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), } }, .ctlPowerData_2G = { { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, }, .modalHeader5G = { /* 4 idle,t1,t2,b (4 bits per setting) */ .antCtrlCommon = LE32(0x220), /* 4 ra1l1, ra2l1, ra1l2,ra2l2,ra12 */ .antCtrlCommon2 = LE32(0x11111), /* antCtrlChain 6 idle, t,r,rx1,rx12,b (2 bits each) */ .antCtrlChain = { LE16(0x150), LE16(0x150), LE16(0x150), }, /* xatten1DB 3 xatten1_db for AR9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0, 0, 0}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for merlin (0xa20c/b20c 16:12 */ .xatten1Margin = {0, 0, 0}, .tempSlope = 68, .voltSlope = 0, /* spurChans spur channels in usual fbin coding format */ .spurChans = {FREQ2FBIN(5500, 0), 0, 0, 0, 0}, /* noiseFloorThreshCh Check if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0xf, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2d, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext2 = { .tempSlopeLow = 72, .tempSlopeHigh = 105, .xatten1DBLow = {0, 0, 0}, .xatten1MarginLow = {0, 0, 0}, .xatten1DBHigh = {0, 0, 0}, .xatten1MarginHigh = {0, 0, 0} }, .calFreqPier5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5785, 0) }, .calPierData5G = { { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, }, .calTarget_freqbin_5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5785, 0) }, .calTarget_freqbin_5GHT20 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT40 = { FREQ2FBIN(5190, 0), FREQ2FBIN(5230, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5410, 0), FREQ2FBIN(5510, 0), FREQ2FBIN(5670, 0), FREQ2FBIN(5755, 0), FREQ2FBIN(5825, 0) }, .calTargetPower5G = { /* 6-24,36,48,54 */ { {42, 40, 40, 34} }, { {42, 40, 40, 34} }, { {42, 40, 40, 34} }, { {42, 40, 40, 34} }, { {42, 40, 40, 34} }, { {42, 40, 40, 34} }, { {42, 40, 40, 34} }, { {42, 40, 40, 34} }, }, .calTargetPower5GHT20 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {40, 40, 40, 40, 32, 28, 40, 40, 32, 28, 40, 40, 32, 20} }, { {40, 40, 40, 40, 32, 28, 40, 40, 32, 28, 40, 40, 32, 20} }, { {40, 40, 40, 40, 32, 28, 40, 40, 32, 28, 40, 40, 32, 20} }, { {40, 40, 40, 40, 32, 28, 40, 40, 32, 28, 40, 40, 32, 20} }, { {40, 40, 40, 40, 32, 28, 40, 40, 32, 28, 40, 40, 32, 20} }, { {40, 40, 40, 40, 32, 28, 40, 40, 32, 28, 40, 40, 32, 20} }, { {38, 38, 38, 38, 32, 28, 38, 38, 32, 28, 38, 38, 32, 26} }, { {36, 36, 36, 36, 32, 28, 36, 36, 32, 28, 36, 36, 32, 26} }, }, .calTargetPower5GHT40 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {40, 40, 40, 38, 30, 26, 40, 40, 30, 26, 40, 40, 30, 24} }, { {40, 40, 40, 38, 30, 26, 40, 40, 30, 26, 40, 40, 30, 24} }, { {40, 40, 40, 38, 30, 26, 40, 40, 30, 26, 40, 40, 30, 24} }, { {40, 40, 40, 38, 30, 26, 40, 40, 30, 26, 40, 40, 30, 24} }, { {40, 40, 40, 38, 30, 26, 40, 40, 30, 26, 40, 40, 30, 24} }, { {40, 40, 40, 38, 30, 26, 40, 40, 30, 26, 40, 40, 30, 24} }, { {36, 36, 36, 36, 30, 26, 36, 36, 30, 26, 36, 36, 30, 24} }, { {34, 34, 34, 34, 30, 26, 34, 34, 30, 26, 34, 34, 30, 24} }, }, .ctlIndex_5G = { 0x10, 0x16, 0x18, 0x40, 0x46, 0x48, 0x30, 0x36, 0x38 }, .ctl_freqbin_5G = { { /* Data[0].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[0].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[0].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[0].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[0].ctlEdges[4].bChannel */ FREQ2FBIN(5600, 0), /* Data[0].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[0].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[0].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[1].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[1].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[1].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[1].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[1].ctlEdges[4].bChannel */ FREQ2FBIN(5520, 0), /* Data[1].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[1].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[1].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[2].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[2].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[2].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[2].ctlEdges[3].bChannel */ FREQ2FBIN(5310, 0), /* Data[2].ctlEdges[4].bChannel */ FREQ2FBIN(5510, 0), /* Data[2].ctlEdges[5].bChannel */ FREQ2FBIN(5550, 0), /* Data[2].ctlEdges[6].bChannel */ FREQ2FBIN(5670, 0), /* Data[2].ctlEdges[7].bChannel */ FREQ2FBIN(5755, 0) }, { /* Data[3].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[3].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[3].ctlEdges[2].bChannel */ FREQ2FBIN(5260, 0), /* Data[3].ctlEdges[3].bChannel */ FREQ2FBIN(5320, 0), /* Data[3].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[3].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[3].ctlEdges[6].bChannel */ 0xFF, /* Data[3].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(5500, 0), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(5700, 0), /* Data[4].ctlEdges[4].bChannel */ 0xFF, /* Data[4].ctlEdges[5].bChannel */ 0xFF, /* Data[4].ctlEdges[6].bChannel */ 0xFF, /* Data[4].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(5270, 0), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(5310, 0), /* Data[5].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[5].ctlEdges[4].bChannel */ FREQ2FBIN(5590, 0), /* Data[5].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[5].ctlEdges[6].bChannel */ 0xFF, /* Data[5].ctlEdges[7].bChannel */ 0xFF }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[6].ctlEdges[2].bChannel */ FREQ2FBIN(5220, 0), /* Data[6].ctlEdges[3].bChannel */ FREQ2FBIN(5260, 0), /* Data[6].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[6].ctlEdges[5].bChannel */ FREQ2FBIN(5600, 0), /* Data[6].ctlEdges[6].bChannel */ FREQ2FBIN(5700, 0), /* Data[6].ctlEdges[7].bChannel */ FREQ2FBIN(5745, 0) }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(5320, 0), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[7].ctlEdges[4].bChannel */ FREQ2FBIN(5560, 0), /* Data[7].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[7].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[7].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[8].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[8].ctlEdges[4].bChannel */ FREQ2FBIN(5550, 0), /* Data[8].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[8].ctlEdges[6].bChannel */ FREQ2FBIN(5755, 0), /* Data[8].ctlEdges[7].bChannel */ FREQ2FBIN(5795, 0) } }, .ctlPowerData_5G = { { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), } }, } }; static const struct ar9300_eeprom ar9300_h112 = { .eepromVersion = 2, .templateVersion = 3, .macAddr = {0x00, 0x03, 0x7f, 0x0, 0x0, 0x0}, .custData = {"h112-241-f0000"}, .baseEepHeader = { .regDmn = { LE16(0), LE16(0x1f) }, .txrxMask = 0x77, /* 4 bits tx and 4 bits rx */ .opCapFlags = { .opFlags = AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A, .eepMisc = 0, }, .rfSilent = 0, .blueToothOptions = 0, .deviceCap = 0, .deviceType = 5, /* takes lower byte in eeprom location */ .pwrTableOffset = AR9300_PWR_TABLE_OFFSET, .params_for_tuning_caps = {0, 0}, .featureEnable = 0x0d, /* * bit0 - enable tx temp comp - disabled * bit1 - enable tx volt comp - disabled * bit2 - enable fastClock - enabled * bit3 - enable doubling - enabled * bit4 - enable internal regulator - disabled * bit5 - enable pa predistortion - disabled */ .miscConfiguration = 0, /* bit0 - turn down drivestrength */ .eepromWriteEnableGpio = 6, .wlanDisableGpio = 0, .wlanLedGpio = 8, .rxBandSelectGpio = 0xff, .txrxgain = 0x10, .swreg = 0, }, .modalHeader2G = { /* ar9300_modal_eep_header 2g */ /* 4 idle,t1,t2,b(4 bits per setting) */ .antCtrlCommon = LE32(0x110), /* 4 ra1l1, ra2l1, ra1l2, ra2l2, ra12 */ .antCtrlCommon2 = LE32(0x44444), /* * antCtrlChain[AR9300_MAX_CHAINS]; 6 idle, t, r, * rx1, rx12, b (2 bits each) */ .antCtrlChain = { LE16(0x150), LE16(0x150), LE16(0x150) }, /* * xatten1DB[AR9300_MAX_CHAINS]; 3 xatten1_db * for ar9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0, 0, 0}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for ar9280 (0xa20c/b20c 16:12 */ .xatten1Margin = {0, 0, 0}, .tempSlope = 25, .voltSlope = 0, /* * spurChans[OSPREY_EEPROM_MODAL_SPURS]; spur * channels in usual fbin coding format */ .spurChans = {FREQ2FBIN(2464, 1), 0, 0, 0, 0}, /* * noiseFloorThreshCh[AR9300_MAX_CHAINS]; 3 Check * if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2c, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext1 = { .ant_div_control = 0, .future = {0, 0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2462, 1), }, /* ar9300_cal_data_per_freq_op_loop 2g */ .calPierData2G = { { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, }, .calTarget_freqbin_Cck = { FREQ2FBIN(2412, 1), FREQ2FBIN(2472, 1), }, .calTarget_freqbin_2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT20 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT40 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTargetPowerCck = { /* 1L-5L,5S,11L,11S */ { {34, 34, 34, 34} }, { {34, 34, 34, 34} }, }, .calTargetPower2G = { /* 6-24,36,48,54 */ { {34, 34, 32, 32} }, { {34, 34, 32, 32} }, { {34, 34, 32, 32} }, }, .calTargetPower2GHT20 = { { {32, 32, 32, 32, 32, 30, 32, 32, 30, 28, 28, 28, 28, 24} }, { {32, 32, 32, 32, 32, 30, 32, 32, 30, 28, 28, 28, 28, 24} }, { {32, 32, 32, 32, 32, 30, 32, 32, 30, 28, 28, 28, 28, 24} }, }, .calTargetPower2GHT40 = { { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 26, 26, 26, 22} }, { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 26, 26, 26, 22} }, { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 26, 26, 26, 22} }, }, .ctlIndex_2G = { 0x11, 0x12, 0x15, 0x17, 0x41, 0x42, 0x45, 0x47, 0x31, 0x32, 0x35, 0x37, }, .ctl_freqbin_2G = { { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2457, 1), FREQ2FBIN(2462, 1) }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2422, 1), FREQ2FBIN(2427, 1), FREQ2FBIN(2447, 1), FREQ2FBIN(2452, 1) }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(2484, 1), }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0, }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), FREQ2FBIN(2472, 1), 0, }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), }, { /* Data[9].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[9].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[9].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[10].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[10].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[10].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[11].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[11].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[11].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[11].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), } }, .ctlPowerData_2G = { { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, }, .modalHeader5G = { /* 4 idle,t1,t2,b (4 bits per setting) */ .antCtrlCommon = LE32(0x220), /* 4 ra1l1, ra2l1, ra1l2,ra2l2,ra12 */ .antCtrlCommon2 = LE32(0x44444), /* antCtrlChain 6 idle, t,r,rx1,rx12,b (2 bits each) */ .antCtrlChain = { LE16(0x150), LE16(0x150), LE16(0x150), }, /* xatten1DB 3 xatten1_db for AR9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0, 0, 0}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for merlin (0xa20c/b20c 16:12 */ .xatten1Margin = {0, 0, 0}, .tempSlope = 45, .voltSlope = 0, /* spurChans spur channels in usual fbin coding format */ .spurChans = {0, 0, 0, 0, 0}, /* noiseFloorThreshCh Check if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2d, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext2 = { .tempSlopeLow = 40, .tempSlopeHigh = 50, .xatten1DBLow = {0, 0, 0}, .xatten1MarginLow = {0, 0, 0}, .xatten1DBHigh = {0, 0, 0}, .xatten1MarginHigh = {0, 0, 0} }, .calFreqPier5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5785, 0) }, .calPierData5G = { { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, }, .calTarget_freqbin_5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT20 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT40 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5825, 0) }, .calTargetPower5G = { /* 6-24,36,48,54 */ { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, }, .calTargetPower5GHT20 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {30, 30, 30, 28, 24, 20, 30, 28, 24, 20, 20, 20, 20, 16} }, { {30, 30, 30, 28, 24, 20, 30, 28, 24, 20, 20, 20, 20, 16} }, { {30, 30, 30, 26, 22, 18, 30, 26, 22, 18, 18, 18, 18, 16} }, { {30, 30, 30, 26, 22, 18, 30, 26, 22, 18, 18, 18, 18, 16} }, { {30, 30, 30, 24, 20, 16, 30, 24, 20, 16, 16, 16, 16, 14} }, { {30, 30, 30, 24, 20, 16, 30, 24, 20, 16, 16, 16, 16, 14} }, { {30, 30, 30, 22, 18, 14, 30, 22, 18, 14, 14, 14, 14, 12} }, { {30, 30, 30, 22, 18, 14, 30, 22, 18, 14, 14, 14, 14, 12} }, }, .calTargetPower5GHT40 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {28, 28, 28, 26, 22, 18, 28, 26, 22, 18, 18, 18, 18, 14} }, { {28, 28, 28, 26, 22, 18, 28, 26, 22, 18, 18, 18, 18, 14} }, { {28, 28, 28, 24, 20, 16, 28, 24, 20, 16, 16, 16, 16, 12} }, { {28, 28, 28, 24, 20, 16, 28, 24, 20, 16, 16, 16, 16, 12} }, { {28, 28, 28, 22, 18, 14, 28, 22, 18, 14, 14, 14, 14, 10} }, { {28, 28, 28, 22, 18, 14, 28, 22, 18, 14, 14, 14, 14, 10} }, { {28, 28, 28, 20, 16, 12, 28, 20, 16, 12, 12, 12, 12, 8} }, { {28, 28, 28, 20, 16, 12, 28, 20, 16, 12, 12, 12, 12, 8} }, }, .ctlIndex_5G = { 0x10, 0x16, 0x18, 0x40, 0x46, 0x48, 0x30, 0x36, 0x38 }, .ctl_freqbin_5G = { { /* Data[0].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[0].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[0].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[0].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[0].ctlEdges[4].bChannel */ FREQ2FBIN(5600, 0), /* Data[0].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[0].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[0].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[1].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[1].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[1].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[1].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[1].ctlEdges[4].bChannel */ FREQ2FBIN(5520, 0), /* Data[1].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[1].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[1].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[2].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[2].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[2].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[2].ctlEdges[3].bChannel */ FREQ2FBIN(5310, 0), /* Data[2].ctlEdges[4].bChannel */ FREQ2FBIN(5510, 0), /* Data[2].ctlEdges[5].bChannel */ FREQ2FBIN(5550, 0), /* Data[2].ctlEdges[6].bChannel */ FREQ2FBIN(5670, 0), /* Data[2].ctlEdges[7].bChannel */ FREQ2FBIN(5755, 0) }, { /* Data[3].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[3].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[3].ctlEdges[2].bChannel */ FREQ2FBIN(5260, 0), /* Data[3].ctlEdges[3].bChannel */ FREQ2FBIN(5320, 0), /* Data[3].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[3].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[3].ctlEdges[6].bChannel */ 0xFF, /* Data[3].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(5500, 0), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(5700, 0), /* Data[4].ctlEdges[4].bChannel */ 0xFF, /* Data[4].ctlEdges[5].bChannel */ 0xFF, /* Data[4].ctlEdges[6].bChannel */ 0xFF, /* Data[4].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(5270, 0), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(5310, 0), /* Data[5].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[5].ctlEdges[4].bChannel */ FREQ2FBIN(5590, 0), /* Data[5].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[5].ctlEdges[6].bChannel */ 0xFF, /* Data[5].ctlEdges[7].bChannel */ 0xFF }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[6].ctlEdges[2].bChannel */ FREQ2FBIN(5220, 0), /* Data[6].ctlEdges[3].bChannel */ FREQ2FBIN(5260, 0), /* Data[6].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[6].ctlEdges[5].bChannel */ FREQ2FBIN(5600, 0), /* Data[6].ctlEdges[6].bChannel */ FREQ2FBIN(5700, 0), /* Data[6].ctlEdges[7].bChannel */ FREQ2FBIN(5745, 0) }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(5320, 0), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[7].ctlEdges[4].bChannel */ FREQ2FBIN(5560, 0), /* Data[7].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[7].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[7].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[8].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[8].ctlEdges[4].bChannel */ FREQ2FBIN(5550, 0), /* Data[8].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[8].ctlEdges[6].bChannel */ FREQ2FBIN(5755, 0), /* Data[8].ctlEdges[7].bChannel */ FREQ2FBIN(5795, 0) } }, .ctlPowerData_5G = { { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), } }, } }; static const struct ar9300_eeprom ar9300_x112 = { .eepromVersion = 2, .templateVersion = 5, .macAddr = {0x00, 0x03, 0x7f, 0x0, 0x0, 0x0}, .custData = {"x112-041-f0000"}, .baseEepHeader = { .regDmn = { LE16(0), LE16(0x1f) }, .txrxMask = 0x77, /* 4 bits tx and 4 bits rx */ .opCapFlags = { .opFlags = AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A, .eepMisc = 0, }, .rfSilent = 0, .blueToothOptions = 0, .deviceCap = 0, .deviceType = 5, /* takes lower byte in eeprom location */ .pwrTableOffset = AR9300_PWR_TABLE_OFFSET, .params_for_tuning_caps = {0, 0}, .featureEnable = 0x0d, /* * bit0 - enable tx temp comp - disabled * bit1 - enable tx volt comp - disabled * bit2 - enable fastclock - enabled * bit3 - enable doubling - enabled * bit4 - enable internal regulator - disabled * bit5 - enable pa predistortion - disabled */ .miscConfiguration = 0, /* bit0 - turn down drivestrength */ .eepromWriteEnableGpio = 6, .wlanDisableGpio = 0, .wlanLedGpio = 8, .rxBandSelectGpio = 0xff, .txrxgain = 0x0, .swreg = 0, }, .modalHeader2G = { /* ar9300_modal_eep_header 2g */ /* 4 idle,t1,t2,b(4 bits per setting) */ .antCtrlCommon = LE32(0x110), /* 4 ra1l1, ra2l1, ra1l2, ra2l2, ra12 */ .antCtrlCommon2 = LE32(0x22222), /* * antCtrlChain[ar9300_max_chains]; 6 idle, t, r, * rx1, rx12, b (2 bits each) */ .antCtrlChain = { LE16(0x10), LE16(0x10), LE16(0x10) }, /* * xatten1DB[AR9300_max_chains]; 3 xatten1_db * for ar9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0x1b, 0x1b, 0x1b}, /* * xatten1Margin[ar9300_max_chains]; 3 xatten1_margin * for ar9280 (0xa20c/b20c 16:12 */ .xatten1Margin = {0x15, 0x15, 0x15}, .tempSlope = 50, .voltSlope = 0, /* * spurChans[OSPrey_eeprom_modal_sPURS]; spur * channels in usual fbin coding format */ .spurChans = {FREQ2FBIN(2464, 1), 0, 0, 0, 0}, /* * noiseFloorThreshch[ar9300_max_cHAINS]; 3 Check * if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2c, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext1 = { .ant_div_control = 0, .future = {0, 0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1), }, /* ar9300_cal_data_per_freq_op_loop 2g */ .calPierData2G = { { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, }, .calTarget_freqbin_Cck = { FREQ2FBIN(2412, 1), FREQ2FBIN(2472, 1), }, .calTarget_freqbin_2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT20 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT40 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTargetPowerCck = { /* 1L-5L,5S,11L,11s */ { {38, 38, 38, 38} }, { {38, 38, 38, 38} }, }, .calTargetPower2G = { /* 6-24,36,48,54 */ { {38, 38, 36, 34} }, { {38, 38, 36, 34} }, { {38, 38, 34, 32} }, }, .calTargetPower2GHT20 = { { {36, 36, 36, 36, 36, 34, 34, 32, 30, 28, 28, 28, 28, 26} }, { {36, 36, 36, 36, 36, 34, 36, 34, 32, 30, 30, 30, 28, 26} }, { {36, 36, 36, 36, 36, 34, 34, 32, 30, 28, 28, 28, 28, 26} }, }, .calTargetPower2GHT40 = { { {36, 36, 36, 36, 34, 32, 32, 30, 28, 26, 26, 26, 26, 24} }, { {36, 36, 36, 36, 34, 32, 34, 32, 30, 28, 28, 28, 28, 24} }, { {36, 36, 36, 36, 34, 32, 32, 30, 28, 26, 26, 26, 26, 24} }, }, .ctlIndex_2G = { 0x11, 0x12, 0x15, 0x17, 0x41, 0x42, 0x45, 0x47, 0x31, 0x32, 0x35, 0x37, }, .ctl_freqbin_2G = { { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2457, 1), FREQ2FBIN(2462, 1) }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2422, 1), FREQ2FBIN(2427, 1), FREQ2FBIN(2447, 1), FREQ2FBIN(2452, 1) }, { /* Data[4].ctledges[0].bchannel */ FREQ2FBIN(2412, 1), /* Data[4].ctledges[1].bchannel */ FREQ2FBIN(2417, 1), /* Data[4].ctledges[2].bchannel */ FREQ2FBIN(2472, 1), /* Data[4].ctledges[3].bchannel */ FREQ2FBIN(2484, 1), }, { /* Data[5].ctledges[0].bchannel */ FREQ2FBIN(2412, 1), /* Data[5].ctledges[1].bchannel */ FREQ2FBIN(2417, 1), /* Data[5].ctledges[2].bchannel */ FREQ2FBIN(2472, 1), 0, }, { /* Data[6].ctledges[0].bchannel */ FREQ2FBIN(2412, 1), /* Data[6].ctledges[1].bchannel */ FREQ2FBIN(2417, 1), FREQ2FBIN(2472, 1), 0, }, { /* Data[7].ctledges[0].bchannel */ FREQ2FBIN(2422, 1), /* Data[7].ctledges[1].bchannel */ FREQ2FBIN(2427, 1), /* Data[7].ctledges[2].bchannel */ FREQ2FBIN(2447, 1), /* Data[7].ctledges[3].bchannel */ FREQ2FBIN(2462, 1), }, { /* Data[8].ctledges[0].bchannel */ FREQ2FBIN(2412, 1), /* Data[8].ctledges[1].bchannel */ FREQ2FBIN(2417, 1), /* Data[8].ctledges[2].bchannel */ FREQ2FBIN(2472, 1), }, { /* Data[9].ctledges[0].bchannel */ FREQ2FBIN(2412, 1), /* Data[9].ctledges[1].bchannel */ FREQ2FBIN(2417, 1), /* Data[9].ctledges[2].bchannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[10].ctledges[0].bchannel */ FREQ2FBIN(2412, 1), /* Data[10].ctledges[1].bchannel */ FREQ2FBIN(2417, 1), /* Data[10].ctledges[2].bchannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[11].ctledges[0].bchannel */ FREQ2FBIN(2422, 1), /* Data[11].ctledges[1].bchannel */ FREQ2FBIN(2427, 1), /* Data[11].ctledges[2].bchannel */ FREQ2FBIN(2447, 1), /* Data[11].ctledges[3].bchannel */ FREQ2FBIN(2462, 1), } }, .ctlPowerData_2G = { { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, }, .modalHeader5G = { /* 4 idle,t1,t2,b (4 bits per setting) */ .antCtrlCommon = LE32(0x110), /* 4 ra1l1, ra2l1, ra1l2,ra2l2,ra12 */ .antCtrlCommon2 = LE32(0x22222), /* antCtrlChain 6 idle, t,r,rx1,rx12,b (2 bits each) */ .antCtrlChain = { LE16(0x0), LE16(0x0), LE16(0x0), }, /* xatten1DB 3 xatten1_db for ar9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0x13, 0x19, 0x17}, /* * xatten1Margin[ar9300_max_chains]; 3 xatten1_margin * for merlin (0xa20c/b20c 16:12 */ .xatten1Margin = {0x19, 0x19, 0x19}, .tempSlope = 70, .voltSlope = 15, /* spurChans spur channels in usual fbin coding format */ .spurChans = {0, 0, 0, 0, 0}, /* noiseFloorThreshch check if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2d, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext2 = { .tempSlopeLow = 72, .tempSlopeHigh = 105, .xatten1DBLow = {0x10, 0x14, 0x10}, .xatten1MarginLow = {0x19, 0x19 , 0x19}, .xatten1DBHigh = {0x1d, 0x20, 0x24}, .xatten1MarginHigh = {0x10, 0x10, 0x10} }, .calFreqPier5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5785, 0) }, .calPierData5G = { { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, }, .calTarget_freqbin_5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5725, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT20 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5725, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT40 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5725, 0), FREQ2FBIN(5825, 0) }, .calTargetPower5G = { /* 6-24,36,48,54 */ { {32, 32, 28, 26} }, { {32, 32, 28, 26} }, { {32, 32, 28, 26} }, { {32, 32, 26, 24} }, { {32, 32, 26, 24} }, { {32, 32, 24, 22} }, { {30, 30, 24, 22} }, { {30, 30, 24, 22} }, }, .calTargetPower5GHT20 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {32, 32, 32, 32, 28, 26, 32, 28, 26, 24, 24, 24, 22, 22} }, { {32, 32, 32, 32, 28, 26, 32, 28, 26, 24, 24, 24, 22, 22} }, { {32, 32, 32, 32, 28, 26, 32, 28, 26, 24, 24, 24, 22, 22} }, { {32, 32, 32, 32, 28, 26, 32, 26, 24, 22, 22, 22, 20, 20} }, { {32, 32, 32, 32, 28, 26, 32, 26, 24, 22, 20, 18, 16, 16} }, { {32, 32, 32, 32, 28, 26, 32, 24, 20, 16, 18, 16, 14, 14} }, { {30, 30, 30, 30, 28, 26, 30, 24, 20, 16, 18, 16, 14, 14} }, { {30, 30, 30, 30, 28, 26, 30, 24, 20, 16, 18, 16, 14, 14} }, }, .calTargetPower5GHT40 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {32, 32, 32, 30, 28, 26, 30, 28, 26, 24, 24, 24, 22, 22} }, { {32, 32, 32, 30, 28, 26, 30, 28, 26, 24, 24, 24, 22, 22} }, { {32, 32, 32, 30, 28, 26, 30, 28, 26, 24, 24, 24, 22, 22} }, { {32, 32, 32, 30, 28, 26, 30, 26, 24, 22, 22, 22, 20, 20} }, { {32, 32, 32, 30, 28, 26, 30, 26, 24, 22, 20, 18, 16, 16} }, { {32, 32, 32, 30, 28, 26, 30, 22, 20, 16, 18, 16, 14, 14} }, { {30, 30, 30, 30, 28, 26, 30, 22, 20, 16, 18, 16, 14, 14} }, { {30, 30, 30, 30, 28, 26, 30, 22, 20, 16, 18, 16, 14, 14} }, }, .ctlIndex_5G = { 0x10, 0x16, 0x18, 0x40, 0x46, 0x48, 0x30, 0x36, 0x38 }, .ctl_freqbin_5G = { { /* Data[0].ctledges[0].bchannel */ FREQ2FBIN(5180, 0), /* Data[0].ctledges[1].bchannel */ FREQ2FBIN(5260, 0), /* Data[0].ctledges[2].bchannel */ FREQ2FBIN(5280, 0), /* Data[0].ctledges[3].bchannel */ FREQ2FBIN(5500, 0), /* Data[0].ctledges[4].bchannel */ FREQ2FBIN(5600, 0), /* Data[0].ctledges[5].bchannel */ FREQ2FBIN(5700, 0), /* Data[0].ctledges[6].bchannel */ FREQ2FBIN(5745, 0), /* Data[0].ctledges[7].bchannel */ FREQ2FBIN(5825, 0) }, { /* Data[1].ctledges[0].bchannel */ FREQ2FBIN(5180, 0), /* Data[1].ctledges[1].bchannel */ FREQ2FBIN(5260, 0), /* Data[1].ctledges[2].bchannel */ FREQ2FBIN(5280, 0), /* Data[1].ctledges[3].bchannel */ FREQ2FBIN(5500, 0), /* Data[1].ctledges[4].bchannel */ FREQ2FBIN(5520, 0), /* Data[1].ctledges[5].bchannel */ FREQ2FBIN(5700, 0), /* Data[1].ctledges[6].bchannel */ FREQ2FBIN(5745, 0), /* Data[1].ctledges[7].bchannel */ FREQ2FBIN(5825, 0) }, { /* Data[2].ctledges[0].bchannel */ FREQ2FBIN(5190, 0), /* Data[2].ctledges[1].bchannel */ FREQ2FBIN(5230, 0), /* Data[2].ctledges[2].bchannel */ FREQ2FBIN(5270, 0), /* Data[2].ctledges[3].bchannel */ FREQ2FBIN(5310, 0), /* Data[2].ctledges[4].bchannel */ FREQ2FBIN(5510, 0), /* Data[2].ctledges[5].bchannel */ FREQ2FBIN(5550, 0), /* Data[2].ctledges[6].bchannel */ FREQ2FBIN(5670, 0), /* Data[2].ctledges[7].bchannel */ FREQ2FBIN(5755, 0) }, { /* Data[3].ctledges[0].bchannel */ FREQ2FBIN(5180, 0), /* Data[3].ctledges[1].bchannel */ FREQ2FBIN(5200, 0), /* Data[3].ctledges[2].bchannel */ FREQ2FBIN(5260, 0), /* Data[3].ctledges[3].bchannel */ FREQ2FBIN(5320, 0), /* Data[3].ctledges[4].bchannel */ FREQ2FBIN(5500, 0), /* Data[3].ctledges[5].bchannel */ FREQ2FBIN(5700, 0), /* Data[3].ctledges[6].bchannel */ 0xFF, /* Data[3].ctledges[7].bchannel */ 0xFF, }, { /* Data[4].ctledges[0].bchannel */ FREQ2FBIN(5180, 0), /* Data[4].ctledges[1].bchannel */ FREQ2FBIN(5260, 0), /* Data[4].ctledges[2].bchannel */ FREQ2FBIN(5500, 0), /* Data[4].ctledges[3].bchannel */ FREQ2FBIN(5700, 0), /* Data[4].ctledges[4].bchannel */ 0xFF, /* Data[4].ctledges[5].bchannel */ 0xFF, /* Data[4].ctledges[6].bchannel */ 0xFF, /* Data[4].ctledges[7].bchannel */ 0xFF, }, { /* Data[5].ctledges[0].bchannel */ FREQ2FBIN(5190, 0), /* Data[5].ctledges[1].bchannel */ FREQ2FBIN(5270, 0), /* Data[5].ctledges[2].bchannel */ FREQ2FBIN(5310, 0), /* Data[5].ctledges[3].bchannel */ FREQ2FBIN(5510, 0), /* Data[5].ctledges[4].bchannel */ FREQ2FBIN(5590, 0), /* Data[5].ctledges[5].bchannel */ FREQ2FBIN(5670, 0), /* Data[5].ctledges[6].bchannel */ 0xFF, /* Data[5].ctledges[7].bchannel */ 0xFF }, { /* Data[6].ctledges[0].bchannel */ FREQ2FBIN(5180, 0), /* Data[6].ctledges[1].bchannel */ FREQ2FBIN(5200, 0), /* Data[6].ctledges[2].bchannel */ FREQ2FBIN(5220, 0), /* Data[6].ctledges[3].bchannel */ FREQ2FBIN(5260, 0), /* Data[6].ctledges[4].bchannel */ FREQ2FBIN(5500, 0), /* Data[6].ctledges[5].bchannel */ FREQ2FBIN(5600, 0), /* Data[6].ctledges[6].bchannel */ FREQ2FBIN(5700, 0), /* Data[6].ctledges[7].bchannel */ FREQ2FBIN(5745, 0) }, { /* Data[7].ctledges[0].bchannel */ FREQ2FBIN(5180, 0), /* Data[7].ctledges[1].bchannel */ FREQ2FBIN(5260, 0), /* Data[7].ctledges[2].bchannel */ FREQ2FBIN(5320, 0), /* Data[7].ctledges[3].bchannel */ FREQ2FBIN(5500, 0), /* Data[7].ctledges[4].bchannel */ FREQ2FBIN(5560, 0), /* Data[7].ctledges[5].bchannel */ FREQ2FBIN(5700, 0), /* Data[7].ctledges[6].bchannel */ FREQ2FBIN(5745, 0), /* Data[7].ctledges[7].bchannel */ FREQ2FBIN(5825, 0) }, { /* Data[8].ctledges[0].bchannel */ FREQ2FBIN(5190, 0), /* Data[8].ctledges[1].bchannel */ FREQ2FBIN(5230, 0), /* Data[8].ctledges[2].bchannel */ FREQ2FBIN(5270, 0), /* Data[8].ctledges[3].bchannel */ FREQ2FBIN(5510, 0), /* Data[8].ctledges[4].bchannel */ FREQ2FBIN(5550, 0), /* Data[8].ctledges[5].bchannel */ FREQ2FBIN(5670, 0), /* Data[8].ctledges[6].bchannel */ FREQ2FBIN(5755, 0), /* Data[8].ctledges[7].bchannel */ FREQ2FBIN(5795, 0) } }, .ctlPowerData_5G = { { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), } }, } }; static const struct ar9300_eeprom ar9300_h116 = { .eepromVersion = 2, .templateVersion = 4, .macAddr = {0x00, 0x03, 0x7f, 0x0, 0x0, 0x0}, .custData = {"h116-041-f0000"}, .baseEepHeader = { .regDmn = { LE16(0), LE16(0x1f) }, .txrxMask = 0x33, /* 4 bits tx and 4 bits rx */ .opCapFlags = { .opFlags = AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A, .eepMisc = 0, }, .rfSilent = 0, .blueToothOptions = 0, .deviceCap = 0, .deviceType = 5, /* takes lower byte in eeprom location */ .pwrTableOffset = AR9300_PWR_TABLE_OFFSET, .params_for_tuning_caps = {0, 0}, .featureEnable = 0x0d, /* * bit0 - enable tx temp comp - disabled * bit1 - enable tx volt comp - disabled * bit2 - enable fastClock - enabled * bit3 - enable doubling - enabled * bit4 - enable internal regulator - disabled * bit5 - enable pa predistortion - disabled */ .miscConfiguration = 0, /* bit0 - turn down drivestrength */ .eepromWriteEnableGpio = 6, .wlanDisableGpio = 0, .wlanLedGpio = 8, .rxBandSelectGpio = 0xff, .txrxgain = 0x10, .swreg = 0, }, .modalHeader2G = { /* ar9300_modal_eep_header 2g */ /* 4 idle,t1,t2,b(4 bits per setting) */ .antCtrlCommon = LE32(0x110), /* 4 ra1l1, ra2l1, ra1l2, ra2l2, ra12 */ .antCtrlCommon2 = LE32(0x44444), /* * antCtrlChain[AR9300_MAX_CHAINS]; 6 idle, t, r, * rx1, rx12, b (2 bits each) */ .antCtrlChain = { LE16(0x10), LE16(0x10), LE16(0x10) }, /* * xatten1DB[AR9300_MAX_CHAINS]; 3 xatten1_db * for ar9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0x1f, 0x1f, 0x1f}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for ar9280 (0xa20c/b20c 16:12 */ .xatten1Margin = {0x12, 0x12, 0x12}, .tempSlope = 25, .voltSlope = 0, /* * spurChans[OSPREY_EEPROM_MODAL_SPURS]; spur * channels in usual fbin coding format */ .spurChans = {FREQ2FBIN(2464, 1), 0, 0, 0, 0}, /* * noiseFloorThreshCh[AR9300_MAX_CHAINS]; 3 Check * if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2c, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80C080), .papdRateMaskHt40 = LE32(0x0080C080), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext1 = { .ant_div_control = 0, .future = {0, 0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2462, 1), }, /* ar9300_cal_data_per_freq_op_loop 2g */ .calPierData2G = { { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }, }, .calTarget_freqbin_Cck = { FREQ2FBIN(2412, 1), FREQ2FBIN(2472, 1), }, .calTarget_freqbin_2G = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT20 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTarget_freqbin_2GHT40 = { FREQ2FBIN(2412, 1), FREQ2FBIN(2437, 1), FREQ2FBIN(2472, 1) }, .calTargetPowerCck = { /* 1L-5L,5S,11L,11S */ { {34, 34, 34, 34} }, { {34, 34, 34, 34} }, }, .calTargetPower2G = { /* 6-24,36,48,54 */ { {34, 34, 32, 32} }, { {34, 34, 32, 32} }, { {34, 34, 32, 32} }, }, .calTargetPower2GHT20 = { { {32, 32, 32, 32, 32, 30, 32, 32, 30, 28, 0, 0, 0, 0} }, { {32, 32, 32, 32, 32, 30, 32, 32, 30, 28, 0, 0, 0, 0} }, { {32, 32, 32, 32, 32, 30, 32, 32, 30, 28, 0, 0, 0, 0} }, }, .calTargetPower2GHT40 = { { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 0, 0, 0, 0} }, { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 0, 0, 0, 0} }, { {30, 30, 30, 30, 30, 28, 30, 30, 28, 26, 0, 0, 0, 0} }, }, .ctlIndex_2G = { 0x11, 0x12, 0x15, 0x17, 0x41, 0x42, 0x45, 0x47, 0x31, 0x32, 0x35, 0x37, }, .ctl_freqbin_2G = { { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2457, 1), FREQ2FBIN(2462, 1) }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2412, 1), FREQ2FBIN(2417, 1), FREQ2FBIN(2462, 1), 0xFF, }, { FREQ2FBIN(2422, 1), FREQ2FBIN(2427, 1), FREQ2FBIN(2447, 1), FREQ2FBIN(2452, 1) }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(2484, 1), }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0, }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), FREQ2FBIN(2472, 1), 0, }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), }, { /* Data[9].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[9].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[9].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[10].ctlEdges[0].bChannel */ FREQ2FBIN(2412, 1), /* Data[10].ctlEdges[1].bChannel */ FREQ2FBIN(2417, 1), /* Data[10].ctlEdges[2].bChannel */ FREQ2FBIN(2472, 1), 0 }, { /* Data[11].ctlEdges[0].bChannel */ FREQ2FBIN(2422, 1), /* Data[11].ctlEdges[1].bChannel */ FREQ2FBIN(2427, 1), /* Data[11].ctlEdges[2].bChannel */ FREQ2FBIN(2447, 1), /* Data[11].ctlEdges[3].bChannel */ FREQ2FBIN(2462, 1), } }, .ctlPowerData_2G = { { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1) } }, }, .modalHeader5G = { /* 4 idle,t1,t2,b (4 bits per setting) */ .antCtrlCommon = LE32(0x220), /* 4 ra1l1, ra2l1, ra1l2,ra2l2,ra12 */ .antCtrlCommon2 = LE32(0x44444), /* antCtrlChain 6 idle, t,r,rx1,rx12,b (2 bits each) */ .antCtrlChain = { LE16(0x150), LE16(0x150), LE16(0x150), }, /* xatten1DB 3 xatten1_db for AR9280 (0xa20c/b20c 5:0) */ .xatten1DB = {0x19, 0x19, 0x19}, /* * xatten1Margin[AR9300_MAX_CHAINS]; 3 xatten1_margin * for merlin (0xa20c/b20c 16:12 */ .xatten1Margin = {0x14, 0x14, 0x14}, .tempSlope = 70, .voltSlope = 0, /* spurChans spur channels in usual fbin coding format */ .spurChans = {0, 0, 0, 0, 0}, /* noiseFloorThreshCh Check if the register is per chain */ .noiseFloorThreshCh = {-1, 0, 0}, .reserved = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .quick_drop = 0, .xpaBiasLvl = 0, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ .antennaGain = 0, .switchSettling = 0x2d, .adcDesiredSize = -30, .txEndToXpaOff = 0, .txEndToRxOn = 0x2, .txFrameToXpaOn = 0xe, .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, }, }, .base_ext2 = { .tempSlopeLow = 35, .tempSlopeHigh = 50, .xatten1DBLow = {0, 0, 0}, .xatten1MarginLow = {0, 0, 0}, .xatten1DBHigh = {0, 0, 0}, .xatten1MarginHigh = {0, 0, 0} }, .calFreqPier5G = { FREQ2FBIN(5160, 0), FREQ2FBIN(5220, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5785, 0) }, .calPierData5G = { { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, { {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, }, }, .calTarget_freqbin_5G = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5600, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT20 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5825, 0) }, .calTarget_freqbin_5GHT40 = { FREQ2FBIN(5180, 0), FREQ2FBIN(5240, 0), FREQ2FBIN(5320, 0), FREQ2FBIN(5400, 0), FREQ2FBIN(5500, 0), FREQ2FBIN(5700, 0), FREQ2FBIN(5745, 0), FREQ2FBIN(5825, 0) }, .calTargetPower5G = { /* 6-24,36,48,54 */ { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, { {30, 30, 28, 24} }, }, .calTargetPower5GHT20 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {30, 30, 30, 28, 24, 20, 30, 28, 24, 20, 0, 0, 0, 0} }, { {30, 30, 30, 28, 24, 20, 30, 28, 24, 20, 0, 0, 0, 0} }, { {30, 30, 30, 26, 22, 18, 30, 26, 22, 18, 0, 0, 0, 0} }, { {30, 30, 30, 26, 22, 18, 30, 26, 22, 18, 0, 0, 0, 0} }, { {30, 30, 30, 24, 20, 16, 30, 24, 20, 16, 0, 0, 0, 0} }, { {30, 30, 30, 24, 20, 16, 30, 24, 20, 16, 0, 0, 0, 0} }, { {30, 30, 30, 22, 18, 14, 30, 22, 18, 14, 0, 0, 0, 0} }, { {30, 30, 30, 22, 18, 14, 30, 22, 18, 14, 0, 0, 0, 0} }, }, .calTargetPower5GHT40 = { /* * 0_8_16,1-3_9-11_17-19, * 4,5,6,7,12,13,14,15,20,21,22,23 */ { {28, 28, 28, 26, 22, 18, 28, 26, 22, 18, 0, 0, 0, 0} }, { {28, 28, 28, 26, 22, 18, 28, 26, 22, 18, 0, 0, 0, 0} }, { {28, 28, 28, 24, 20, 16, 28, 24, 20, 16, 0, 0, 0, 0} }, { {28, 28, 28, 24, 20, 16, 28, 24, 20, 16, 0, 0, 0, 0} }, { {28, 28, 28, 22, 18, 14, 28, 22, 18, 14, 0, 0, 0, 0} }, { {28, 28, 28, 22, 18, 14, 28, 22, 18, 14, 0, 0, 0, 0} }, { {28, 28, 28, 20, 16, 12, 28, 20, 16, 12, 0, 0, 0, 0} }, { {28, 28, 28, 20, 16, 12, 28, 20, 16, 12, 0, 0, 0, 0} }, }, .ctlIndex_5G = { 0x10, 0x16, 0x18, 0x40, 0x46, 0x48, 0x30, 0x36, 0x38 }, .ctl_freqbin_5G = { { /* Data[0].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[0].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[0].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[0].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[0].ctlEdges[4].bChannel */ FREQ2FBIN(5600, 0), /* Data[0].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[0].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[0].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[1].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[1].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[1].ctlEdges[2].bChannel */ FREQ2FBIN(5280, 0), /* Data[1].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[1].ctlEdges[4].bChannel */ FREQ2FBIN(5520, 0), /* Data[1].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[1].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[1].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[2].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[2].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[2].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[2].ctlEdges[3].bChannel */ FREQ2FBIN(5310, 0), /* Data[2].ctlEdges[4].bChannel */ FREQ2FBIN(5510, 0), /* Data[2].ctlEdges[5].bChannel */ FREQ2FBIN(5550, 0), /* Data[2].ctlEdges[6].bChannel */ FREQ2FBIN(5670, 0), /* Data[2].ctlEdges[7].bChannel */ FREQ2FBIN(5755, 0) }, { /* Data[3].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[3].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[3].ctlEdges[2].bChannel */ FREQ2FBIN(5260, 0), /* Data[3].ctlEdges[3].bChannel */ FREQ2FBIN(5320, 0), /* Data[3].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[3].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[3].ctlEdges[6].bChannel */ 0xFF, /* Data[3].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[4].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[4].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[4].ctlEdges[2].bChannel */ FREQ2FBIN(5500, 0), /* Data[4].ctlEdges[3].bChannel */ FREQ2FBIN(5700, 0), /* Data[4].ctlEdges[4].bChannel */ 0xFF, /* Data[4].ctlEdges[5].bChannel */ 0xFF, /* Data[4].ctlEdges[6].bChannel */ 0xFF, /* Data[4].ctlEdges[7].bChannel */ 0xFF, }, { /* Data[5].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[5].ctlEdges[1].bChannel */ FREQ2FBIN(5270, 0), /* Data[5].ctlEdges[2].bChannel */ FREQ2FBIN(5310, 0), /* Data[5].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[5].ctlEdges[4].bChannel */ FREQ2FBIN(5590, 0), /* Data[5].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[5].ctlEdges[6].bChannel */ 0xFF, /* Data[5].ctlEdges[7].bChannel */ 0xFF }, { /* Data[6].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[6].ctlEdges[1].bChannel */ FREQ2FBIN(5200, 0), /* Data[6].ctlEdges[2].bChannel */ FREQ2FBIN(5220, 0), /* Data[6].ctlEdges[3].bChannel */ FREQ2FBIN(5260, 0), /* Data[6].ctlEdges[4].bChannel */ FREQ2FBIN(5500, 0), /* Data[6].ctlEdges[5].bChannel */ FREQ2FBIN(5600, 0), /* Data[6].ctlEdges[6].bChannel */ FREQ2FBIN(5700, 0), /* Data[6].ctlEdges[7].bChannel */ FREQ2FBIN(5745, 0) }, { /* Data[7].ctlEdges[0].bChannel */ FREQ2FBIN(5180, 0), /* Data[7].ctlEdges[1].bChannel */ FREQ2FBIN(5260, 0), /* Data[7].ctlEdges[2].bChannel */ FREQ2FBIN(5320, 0), /* Data[7].ctlEdges[3].bChannel */ FREQ2FBIN(5500, 0), /* Data[7].ctlEdges[4].bChannel */ FREQ2FBIN(5560, 0), /* Data[7].ctlEdges[5].bChannel */ FREQ2FBIN(5700, 0), /* Data[7].ctlEdges[6].bChannel */ FREQ2FBIN(5745, 0), /* Data[7].ctlEdges[7].bChannel */ FREQ2FBIN(5825, 0) }, { /* Data[8].ctlEdges[0].bChannel */ FREQ2FBIN(5190, 0), /* Data[8].ctlEdges[1].bChannel */ FREQ2FBIN(5230, 0), /* Data[8].ctlEdges[2].bChannel */ FREQ2FBIN(5270, 0), /* Data[8].ctlEdges[3].bChannel */ FREQ2FBIN(5510, 0), /* Data[8].ctlEdges[4].bChannel */ FREQ2FBIN(5550, 0), /* Data[8].ctlEdges[5].bChannel */ FREQ2FBIN(5670, 0), /* Data[8].ctlEdges[6].bChannel */ FREQ2FBIN(5755, 0), /* Data[8].ctlEdges[7].bChannel */ FREQ2FBIN(5795, 0) } }, .ctlPowerData_5G = { { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), } }, { { CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), } }, { { CTL(60, 1), CTL(60, 0), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 1), CTL(60, 0), CTL(60, 1), } }, } }; static const struct ar9300_eeprom *ar9300_eep_templates[] = { &ar9300_default, &ar9300_x112, &ar9300_h116, &ar9300_h112, &ar9300_x113, }; static const struct ar9300_eeprom *ar9003_eeprom_struct_find_by_id(int id) { #define N_LOOP (sizeof(ar9300_eep_templates) / sizeof(ar9300_eep_templates[0])) int it; for (it = 0; it < N_LOOP; it++) if (ar9300_eep_templates[it]->templateVersion == id) return ar9300_eep_templates[it]; return NULL; #undef N_LOOP } static int ath9k_hw_ar9300_check_eeprom(struct ath_hw *ah) { return 0; } static int interpolate(int x, int xa, int xb, int ya, int yb) { int bf, factor, plus; bf = 2 * (yb - ya) * (x - xa) / (xb - xa); factor = bf / 2; plus = bf % 2; return ya + factor + plus; } static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ar9300_base_eep_hdr *pBase = &eep->baseEepHeader; switch (param) { case EEP_MAC_LSW: return get_unaligned_be16(eep->macAddr); case EEP_MAC_MID: return get_unaligned_be16(eep->macAddr + 2); case EEP_MAC_MSW: return get_unaligned_be16(eep->macAddr + 4); case EEP_REG_0: return le16_to_cpu(pBase->regDmn[0]); case EEP_OP_CAP: return pBase->deviceCap; case EEP_OP_MODE: return pBase->opCapFlags.opFlags; case EEP_RF_SILENT: return pBase->rfSilent; case EEP_TX_MASK: return (pBase->txrxMask >> 4) & 0xf; case EEP_RX_MASK: return pBase->txrxMask & 0xf; case EEP_PAPRD: if (AR_SREV_9462(ah)) return false; if (!ah->config.enable_paprd); return false; return !!(pBase->featureEnable & BIT(5)); case EEP_CHAIN_MASK_REDUCE: return (pBase->miscConfiguration >> 0x3) & 0x1; case EEP_ANT_DIV_CTL1: return eep->base_ext1.ant_div_control; case EEP_ANTENNA_GAIN_5G: return eep->modalHeader5G.antennaGain; case EEP_ANTENNA_GAIN_2G: return eep->modalHeader2G.antennaGain; default: return 0; } } static bool ar9300_eeprom_read_byte(struct ath_common *common, int address, u8 *buffer) { u16 val; if (unlikely(!ath9k_hw_nvram_read(common, address / 2, &val))) return false; *buffer = (val >> (8 * (address % 2))) & 0xff; return true; } static bool ar9300_eeprom_read_word(struct ath_common *common, int address, u8 *buffer) { u16 val; if (unlikely(!ath9k_hw_nvram_read(common, address / 2, &val))) return false; buffer[0] = val >> 8; buffer[1] = val & 0xff; return true; } static bool ar9300_read_eeprom(struct ath_hw *ah, int address, u8 *buffer, int count) { struct ath_common *common = ath9k_hw_common(ah); int i; if ((address < 0) || ((address + count) / 2 > AR9300_EEPROM_SIZE - 1)) { ath_dbg(common, EEPROM, "eeprom address not in range\n"); return false; } /* * Since we're reading the bytes in reverse order from a little-endian * word stream, an even address means we only use the lower half of * the 16-bit word at that address */ if (address % 2 == 0) { if (!ar9300_eeprom_read_byte(common, address--, buffer++)) goto error; count--; } for (i = 0; i < count / 2; i++) { if (!ar9300_eeprom_read_word(common, address, buffer)) goto error; address -= 2; buffer += 2; } if (count % 2) if (!ar9300_eeprom_read_byte(common, address, buffer)) goto error; return true; error: ath_dbg(common, EEPROM, "unable to read eeprom region at offset %d\n", address); return false; } static bool ar9300_otp_read_word(struct ath_hw *ah, int addr, u32 *data) { REG_READ(ah, AR9300_OTP_BASE + (4 * addr)); if (!ath9k_hw_wait(ah, AR9300_OTP_STATUS, AR9300_OTP_STATUS_TYPE, AR9300_OTP_STATUS_VALID, 1000)) return false; *data = REG_READ(ah, AR9300_OTP_READ_DATA); return true; } static bool ar9300_read_otp(struct ath_hw *ah, int address, u8 *buffer, int count) { u32 data; int i; for (i = 0; i < count; i++) { int offset = 8 * ((address - i) % 4); if (!ar9300_otp_read_word(ah, (address - i) / 4, &data)) return false; buffer[i] = (data >> offset) & 0xff; } return true; } static void ar9300_comp_hdr_unpack(u8 *best, int *code, int *reference, int *length, int *major, int *minor) { unsigned long value[4]; value[0] = best[0]; value[1] = best[1]; value[2] = best[2]; value[3] = best[3]; *code = ((value[0] >> 5) & 0x0007); *reference = (value[0] & 0x001f) | ((value[1] >> 2) & 0x0020); *length = ((value[1] << 4) & 0x07f0) | ((value[2] >> 4) & 0x000f); *major = (value[2] & 0x000f); *minor = (value[3] & 0x00ff); } static u16 ar9300_comp_cksum(u8 *data, int dsize) { int it, checksum = 0; for (it = 0; it < dsize; it++) { checksum += data[it]; checksum &= 0xffff; } return checksum; } static bool ar9300_uncompress_block(struct ath_hw *ah, u8 *mptr, int mdataSize, u8 *block, int size) { int it; int spot; int offset; int length; struct ath_common *common = ath9k_hw_common(ah); spot = 0; for (it = 0; it < size; it += (length+2)) { offset = block[it]; offset &= 0xff; spot += offset; length = block[it+1]; length &= 0xff; if (length > 0 && spot >= 0 && spot+length <= mdataSize) { ath_dbg(common, EEPROM, "Restore at %d: spot=%d offset=%d length=%d\n", it, spot, offset, length); memcpy(&mptr[spot], &block[it+2], length); spot += length; } else if (length > 0) { ath_dbg(common, EEPROM, "Bad restore at %d: spot=%d offset=%d length=%d\n", it, spot, offset, length); return false; } } return true; } static int ar9300_compress_decision(struct ath_hw *ah, int it, int code, int reference, u8 *mptr, u8 *word, int length, int mdata_size) { struct ath_common *common = ath9k_hw_common(ah); const struct ar9300_eeprom *eep = NULL; switch (code) { case _CompressNone: if (length != mdata_size) { ath_dbg(common, EEPROM, "EEPROM structure size mismatch memory=%d eeprom=%d\n", mdata_size, length); return -1; } memcpy(mptr, word + COMP_HDR_LEN, length); ath_dbg(common, EEPROM, "restored eeprom %d: uncompressed, length %d\n", it, length); break; case _CompressBlock: if (reference == 0) { } else { eep = ar9003_eeprom_struct_find_by_id(reference); if (eep == NULL) { ath_dbg(common, EEPROM, "can't find reference eeprom struct %d\n", reference); return -1; } memcpy(mptr, eep, mdata_size); } ath_dbg(common, EEPROM, "restore eeprom %d: block, reference %d, length %d\n", it, reference, length); ar9300_uncompress_block(ah, mptr, mdata_size, (word + COMP_HDR_LEN), length); break; default: ath_dbg(common, EEPROM, "unknown compression code %d\n", code); return -1; } return 0; } typedef bool (*eeprom_read_op)(struct ath_hw *ah, int address, u8 *buffer, int count); static bool ar9300_check_header(void *data) { u32 *word = data; return !(*word == 0 || *word == ~0); } static bool ar9300_check_eeprom_header(struct ath_hw *ah, eeprom_read_op read, int base_addr) { u8 header[4]; if (!read(ah, base_addr, header, 4)) return false; return ar9300_check_header(header); } static int ar9300_eeprom_restore_flash(struct ath_hw *ah, u8 *mptr, int mdata_size) { struct ath_common *common = ath9k_hw_common(ah); u16 *data = (u16 *) mptr; int i; for (i = 0; i < mdata_size / 2; i++, data++) ath9k_hw_nvram_read(common, i, data); return 0; } /* * Read the configuration data from the eeprom. * The data can be put in any specified memory buffer. * * Returns -1 on error. * Returns address of next memory location on success. */ static int ar9300_eeprom_restore_internal(struct ath_hw *ah, u8 *mptr, int mdata_size) { #define MDEFAULT 15 #define MSTATE 100 int cptr; u8 *word; int code; int reference, length, major, minor; int osize; int it; u16 checksum, mchecksum; struct ath_common *common = ath9k_hw_common(ah); struct ar9300_eeprom *eep; eeprom_read_op read; if (ath9k_hw_use_flash(ah)) { u8 txrx; ar9300_eeprom_restore_flash(ah, mptr, mdata_size); /* check if eeprom contains valid data */ eep = (struct ar9300_eeprom *) mptr; txrx = eep->baseEepHeader.txrxMask; if (txrx != 0 && txrx != 0xff) return 0; } word = kzalloc(2048, GFP_KERNEL); if (!word) return -ENOMEM; memcpy(mptr, &ar9300_default, mdata_size); read = ar9300_read_eeprom; if (AR_SREV_9485(ah)) cptr = AR9300_BASE_ADDR_4K; else if (AR_SREV_9330(ah)) cptr = AR9300_BASE_ADDR_512; else cptr = AR9300_BASE_ADDR; ath_dbg(common, EEPROM, "Trying EEPROM access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; cptr = AR9300_BASE_ADDR_512; ath_dbg(common, EEPROM, "Trying EEPROM access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; read = ar9300_read_otp; cptr = AR9300_BASE_ADDR; ath_dbg(common, EEPROM, "Trying OTP access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; cptr = AR9300_BASE_ADDR_512; ath_dbg(common, EEPROM, "Trying OTP access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; goto fail; found: ath_dbg(common, EEPROM, "Found valid EEPROM data\n"); for (it = 0; it < MSTATE; it++) { if (!read(ah, cptr, word, COMP_HDR_LEN)) goto fail; if (!ar9300_check_header(word)) break; ar9300_comp_hdr_unpack(word, &code, &reference, &length, &major, &minor); ath_dbg(common, EEPROM, "Found block at %x: code=%d ref=%d length=%d major=%d minor=%d\n", cptr, code, reference, length, major, minor); if ((!AR_SREV_9485(ah) && length >= 1024) || (AR_SREV_9485(ah) && length > EEPROM_DATA_LEN_9485)) { ath_dbg(common, EEPROM, "Skipping bad header\n"); cptr -= COMP_HDR_LEN; continue; } osize = length; read(ah, cptr, word, COMP_HDR_LEN + osize + COMP_CKSUM_LEN); checksum = ar9300_comp_cksum(&word[COMP_HDR_LEN], length); mchecksum = get_unaligned_le16(&word[COMP_HDR_LEN + osize]); ath_dbg(common, EEPROM, "checksum %x %x\n", checksum, mchecksum); if (checksum == mchecksum) { ar9300_compress_decision(ah, it, code, reference, mptr, word, length, mdata_size); } else { ath_dbg(common, EEPROM, "skipping block with bad checksum\n"); } cptr -= (COMP_HDR_LEN + osize + COMP_CKSUM_LEN); } kfree(word); return cptr; fail: kfree(word); return -1; } /* * Restore the configuration structure by reading the eeprom. * This function destroys any existing in-memory structure * content. */ static bool ath9k_hw_ar9300_fill_eeprom(struct ath_hw *ah) { u8 *mptr = (u8 *) &ah->eeprom.ar9300_eep; if (ar9300_eeprom_restore_internal(ah, mptr, sizeof(struct ar9300_eeprom)) < 0) return false; return true; } #if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS) static u32 ar9003_dump_modal_eeprom(char *buf, u32 len, u32 size, struct ar9300_modal_eep_header *modal_hdr) { PR_EEP("Chain0 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[0])); PR_EEP("Chain1 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[1])); PR_EEP("Chain2 Ant. Control", le16_to_cpu(modal_hdr->antCtrlChain[2])); PR_EEP("Ant. Common Control", le32_to_cpu(modal_hdr->antCtrlCommon)); PR_EEP("Ant. Common Control2", le32_to_cpu(modal_hdr->antCtrlCommon2)); PR_EEP("Ant. Gain", modal_hdr->antennaGain); PR_EEP("Switch Settle", modal_hdr->switchSettling); PR_EEP("Chain0 xatten1DB", modal_hdr->xatten1DB[0]); PR_EEP("Chain1 xatten1DB", modal_hdr->xatten1DB[1]); PR_EEP("Chain2 xatten1DB", modal_hdr->xatten1DB[2]); PR_EEP("Chain0 xatten1Margin", modal_hdr->xatten1Margin[0]); PR_EEP("Chain1 xatten1Margin", modal_hdr->xatten1Margin[1]); PR_EEP("Chain2 xatten1Margin", modal_hdr->xatten1Margin[2]); PR_EEP("Temp Slope", modal_hdr->tempSlope); PR_EEP("Volt Slope", modal_hdr->voltSlope); PR_EEP("spur Channels0", modal_hdr->spurChans[0]); PR_EEP("spur Channels1", modal_hdr->spurChans[1]); PR_EEP("spur Channels2", modal_hdr->spurChans[2]); PR_EEP("spur Channels3", modal_hdr->spurChans[3]); PR_EEP("spur Channels4", modal_hdr->spurChans[4]); PR_EEP("Chain0 NF Threshold", modal_hdr->noiseFloorThreshCh[0]); PR_EEP("Chain1 NF Threshold", modal_hdr->noiseFloorThreshCh[1]); PR_EEP("Chain2 NF Threshold", modal_hdr->noiseFloorThreshCh[2]); PR_EEP("Quick Drop", modal_hdr->quick_drop); PR_EEP("txEndToXpaOff", modal_hdr->txEndToXpaOff); PR_EEP("xPA Bias Level", modal_hdr->xpaBiasLvl); PR_EEP("txFrameToDataStart", modal_hdr->txFrameToDataStart); PR_EEP("txFrameToPaOn", modal_hdr->txFrameToPaOn); PR_EEP("txFrameToXpaOn", modal_hdr->txFrameToXpaOn); PR_EEP("txClip", modal_hdr->txClip); PR_EEP("ADC Desired size", modal_hdr->adcDesiredSize); return len; } static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ar9300_base_eep_hdr *pBase; if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader2G); len += snprintf(buf + len, size - len, "%20s :\n", "5GHz modal Header"); len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader5G); goto out; } pBase = &eep->baseEepHeader; PR_EEP("EEPROM Version", ah->eeprom.ar9300_eep.eepromVersion); PR_EEP("RegDomain1", le16_to_cpu(pBase->regDmn[0])); PR_EEP("RegDomain2", le16_to_cpu(pBase->regDmn[1])); PR_EEP("TX Mask", (pBase->txrxMask >> 4)); PR_EEP("RX Mask", (pBase->txrxMask & 0x0f)); PR_EEP("Allow 5GHz", !!(pBase->opCapFlags.opFlags & AR5416_OPFLAGS_11A)); PR_EEP("Allow 2GHz", !!(pBase->opCapFlags.opFlags & AR5416_OPFLAGS_11G)); PR_EEP("Disable 2GHz HT20", !!(pBase->opCapFlags.opFlags & AR5416_OPFLAGS_N_2G_HT20)); PR_EEP("Disable 2GHz HT40", !!(pBase->opCapFlags.opFlags & AR5416_OPFLAGS_N_2G_HT40)); PR_EEP("Disable 5Ghz HT20", !!(pBase->opCapFlags.opFlags & AR5416_OPFLAGS_N_5G_HT20)); PR_EEP("Disable 5Ghz HT40", !!(pBase->opCapFlags.opFlags & AR5416_OPFLAGS_N_5G_HT40)); PR_EEP("Big Endian", !!(pBase->opCapFlags.eepMisc & 0x01)); PR_EEP("RF Silent", pBase->rfSilent); PR_EEP("BT option", pBase->blueToothOptions); PR_EEP("Device Cap", pBase->deviceCap); PR_EEP("Device Type", pBase->deviceType); PR_EEP("Power Table Offset", pBase->pwrTableOffset); PR_EEP("Tuning Caps1", pBase->params_for_tuning_caps[0]); PR_EEP("Tuning Caps2", pBase->params_for_tuning_caps[1]); PR_EEP("Enable Tx Temp Comp", !!(pBase->featureEnable & BIT(0))); PR_EEP("Enable Tx Volt Comp", !!(pBase->featureEnable & BIT(1))); PR_EEP("Enable fast clock", !!(pBase->featureEnable & BIT(2))); PR_EEP("Enable doubling", !!(pBase->featureEnable & BIT(3))); PR_EEP("Internal regulator", !!(pBase->featureEnable & BIT(4))); PR_EEP("Enable Paprd", !!(pBase->featureEnable & BIT(5))); PR_EEP("Driver Strength", !!(pBase->miscConfiguration & BIT(0))); PR_EEP("Quick Drop", !!(pBase->miscConfiguration & BIT(1))); PR_EEP("Chain mask Reduce", (pBase->miscConfiguration >> 0x3) & 0x1); PR_EEP("Write enable Gpio", pBase->eepromWriteEnableGpio); PR_EEP("WLAN Disable Gpio", pBase->wlanDisableGpio); PR_EEP("WLAN LED Gpio", pBase->wlanLedGpio); PR_EEP("Rx Band Select Gpio", pBase->rxBandSelectGpio); PR_EEP("Tx Gain", pBase->txrxgain >> 4); PR_EEP("Rx Gain", pBase->txrxgain & 0xf); PR_EEP("SW Reg", le32_to_cpu(pBase->swreg)); len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", ah->eeprom.ar9300_eep.macAddr); out: if (len > size) len = size; return len; } #else static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, u8 *buf, u32 len, u32 size) { return 0; } #endif /* XXX: review hardware docs */ static int ath9k_hw_ar9300_get_eeprom_ver(struct ath_hw *ah) { return ah->eeprom.ar9300_eep.eepromVersion; } /* XXX: could be read from the eepromVersion, not sure yet */ static int ath9k_hw_ar9300_get_eeprom_rev(struct ath_hw *ah) { return 0; } static struct ar9300_modal_eep_header *ar9003_modal_header(struct ath_hw *ah, bool is2ghz) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; if (is2ghz) return &eep->modalHeader2G; else return &eep->modalHeader5G; } static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz) { int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl; if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias); else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias); else { REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias); REG_RMW_FIELD(ah, AR_CH0_THERM, AR_CH0_THERM_XPABIASLVL_MSB, bias >> 2); REG_RMW_FIELD(ah, AR_CH0_THERM, AR_CH0_THERM_XPASHORT2GND, 1); } } static u16 ar9003_switch_com_spdt_get(struct ath_hw *ah, bool is2ghz) { return le16_to_cpu(ar9003_modal_header(ah, is2ghz)->switchcomspdt); } static u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz) { return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon); } static u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz) { return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon2); } static u16 ar9003_hw_ant_ctrl_chain_get(struct ath_hw *ah, int chain, bool is2ghz) { __le16 val = ar9003_modal_header(ah, is2ghz)->antCtrlChain[chain]; return le16_to_cpu(val); } static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) { int chain; u32 regval; u32 ant_div_ctl1; static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = { AR_PHY_SWITCH_CHAIN_0, AR_PHY_SWITCH_CHAIN_1, AR_PHY_SWITCH_CHAIN_2, }; u32 value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_AR9462_ALL, value); } else if (AR_SREV_9550(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_AR9550_ALL, value); } else REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_ALL, value); /* * AR9462 defines new switch table for BT/WLAN, * here's new field name in XXX.ref for both 2G and 5G. * Register: [GLB_CONTROL] GLB_CONTROL (@0x20044) * 15:12 R/W SWITCH_TABLE_COM_SPDT_WLAN_RX * SWITCH_TABLE_COM_SPDT_WLAN_RX * * 11:8 R/W SWITCH_TABLE_COM_SPDT_WLAN_TX * SWITCH_TABLE_COM_SPDT_WLAN_TX * * 7:4 R/W SWITCH_TABLE_COM_SPDT_WLAN_IDLE * SWITCH_TABLE_COM_SPDT_WLAN_IDLE */ if (AR_SREV_9462_20_OR_LATER(ah)) { value = ar9003_switch_com_spdt_get(ah, is2ghz); REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_SWITCH_TABLE_COM_SPDT_ALL, value); REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_SPDT_ENABLE); } value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz); REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value); for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { if ((ah->rxchainmask & BIT(chain)) || (ah->txchainmask & BIT(chain))) { value = ar9003_hw_ant_ctrl_chain_get(ah, chain, is2ghz); REG_RMW_FIELD(ah, switch_chain_reg[chain], AR_SWITCH_TABLE_ALL, value); } } if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) { value = ath9k_hw_ar9300_get_eeprom(ah, EEP_ANT_DIV_CTL1); /* * main_lnaconf, alt_lnaconf, main_tb, alt_tb * are the fields present */ regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); regval &= (~AR_ANT_DIV_CTRL_ALL); regval |= (value & 0x3f) << AR_ANT_DIV_CTRL_ALL_S; /* enable_lnadiv */ regval &= (~AR_PHY_ANT_DIV_LNADIV); regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S; REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); /*enable fast_div */ regval = REG_READ(ah, AR_PHY_CCK_DETECT); regval &= (~AR_FAST_DIV_ENABLE); regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); /* check whether antenna diversity is enabled */ if ((ant_div_ctl1 >> 0x6) == 0x3) { regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); /* * clear bits 25-30 main_lnaconf, alt_lnaconf, * main_tb, alt_tb */ regval &= (~(AR_PHY_ANT_DIV_MAIN_LNACONF | AR_PHY_ANT_DIV_ALT_LNACONF | AR_PHY_ANT_DIV_ALT_GAINTB | AR_PHY_ANT_DIV_MAIN_GAINTB)); /* by default use LNA1 for the main antenna */ regval |= (AR_PHY_ANT_DIV_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S); regval |= (AR_PHY_ANT_DIV_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S); REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } } } static void ar9003_hw_drive_strength_apply(struct ath_hw *ah) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ar9300_base_eep_hdr *pBase = &eep->baseEepHeader; int drive_strength; unsigned long reg; drive_strength = pBase->miscConfiguration & BIT(0); if (!drive_strength) return; reg = REG_READ(ah, AR_PHY_65NM_CH0_BIAS1); reg &= ~0x00ffffc0; reg |= 0x5 << 21; reg |= 0x5 << 18; reg |= 0x5 << 15; reg |= 0x5 << 12; reg |= 0x5 << 9; reg |= 0x5 << 6; REG_WRITE(ah, AR_PHY_65NM_CH0_BIAS1, reg); reg = REG_READ(ah, AR_PHY_65NM_CH0_BIAS2); reg &= ~0xffffffe0; reg |= 0x5 << 29; reg |= 0x5 << 26; reg |= 0x5 << 23; reg |= 0x5 << 20; reg |= 0x5 << 17; reg |= 0x5 << 14; reg |= 0x5 << 11; reg |= 0x5 << 8; reg |= 0x5 << 5; REG_WRITE(ah, AR_PHY_65NM_CH0_BIAS2, reg); reg = REG_READ(ah, AR_PHY_65NM_CH0_BIAS4); reg &= ~0xff800000; reg |= 0x5 << 29; reg |= 0x5 << 26; reg |= 0x5 << 23; REG_WRITE(ah, AR_PHY_65NM_CH0_BIAS4, reg); } static u16 ar9003_hw_atten_chain_get(struct ath_hw *ah, int chain, struct ath9k_channel *chan) { int f[3], t[3]; u16 value; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; if (chain >= 0 && chain < 3) { if (IS_CHAN_2GHZ(chan)) return eep->modalHeader2G.xatten1DB[chain]; else if (eep->base_ext2.xatten1DBLow[chain] != 0) { t[0] = eep->base_ext2.xatten1DBLow[chain]; f[0] = 5180; t[1] = eep->modalHeader5G.xatten1DB[chain]; f[1] = 5500; t[2] = eep->base_ext2.xatten1DBHigh[chain]; f[2] = 5785; value = ar9003_hw_power_interpolate((s32) chan->channel, f, t, 3); return value; } else return eep->modalHeader5G.xatten1DB[chain]; } return 0; } static u16 ar9003_hw_atten_chain_get_margin(struct ath_hw *ah, int chain, struct ath9k_channel *chan) { int f[3], t[3]; u16 value; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; if (chain >= 0 && chain < 3) { if (IS_CHAN_2GHZ(chan)) return eep->modalHeader2G.xatten1Margin[chain]; else if (eep->base_ext2.xatten1MarginLow[chain] != 0) { t[0] = eep->base_ext2.xatten1MarginLow[chain]; f[0] = 5180; t[1] = eep->modalHeader5G.xatten1Margin[chain]; f[1] = 5500; t[2] = eep->base_ext2.xatten1MarginHigh[chain]; f[2] = 5785; value = ar9003_hw_power_interpolate((s32) chan->channel, f, t, 3); return value; } else return eep->modalHeader5G.xatten1Margin[chain]; } return 0; } static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan) { int i; u16 value; unsigned long ext_atten_reg[3] = {AR_PHY_EXT_ATTEN_CTL_0, AR_PHY_EXT_ATTEN_CTL_1, AR_PHY_EXT_ATTEN_CTL_2, }; /* Test value. if 0 then attenuation is unused. Don't load anything. */ for (i = 0; i < 3; i++) { if (ah->txchainmask & BIT(i)) { value = ar9003_hw_atten_chain_get(ah, i, chan); REG_RMW_FIELD(ah, ext_atten_reg[i], AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value); value = ar9003_hw_atten_chain_get_margin(ah, i, chan); REG_RMW_FIELD(ah, ext_atten_reg[i], AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); } } } static bool is_pmu_set(struct ath_hw *ah, u32 pmu_reg, int pmu_set) { int timeout = 100; while (pmu_set != REG_READ(ah, pmu_reg)) { if (timeout-- == 0) return false; REG_WRITE(ah, pmu_reg, pmu_set); udelay(10); } return true; } void ar9003_hw_internal_regulator_apply(struct ath_hw *ah) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ar9300_base_eep_hdr *pBase = &eep->baseEepHeader; u32 reg_val; if (pBase->featureEnable & BIT(4)) { if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) { int reg_pmu_set; reg_pmu_set = REG_READ(ah, AR_PHY_PMU2) & ~AR_PHY_PMU2_PGM; REG_WRITE(ah, AR_PHY_PMU2, reg_pmu_set); if (!is_pmu_set(ah, AR_PHY_PMU2, reg_pmu_set)) return; if (AR_SREV_9330(ah)) { if (ah->is_clk_25mhz) { reg_pmu_set = (3 << 1) | (8 << 4) | (3 << 8) | (1 << 14) | (6 << 17) | (1 << 20) | (3 << 24); } else { reg_pmu_set = (4 << 1) | (7 << 4) | (3 << 8) | (1 << 14) | (6 << 17) | (1 << 20) | (3 << 24); } } else { reg_pmu_set = (5 << 1) | (7 << 4) | (2 << 8) | (2 << 14) | (6 << 17) | (1 << 20) | (3 << 24) | (1 << 28); } REG_WRITE(ah, AR_PHY_PMU1, reg_pmu_set); if (!is_pmu_set(ah, AR_PHY_PMU1, reg_pmu_set)) return; reg_pmu_set = (REG_READ(ah, AR_PHY_PMU2) & ~0xFFC00000) | (4 << 26); REG_WRITE(ah, AR_PHY_PMU2, reg_pmu_set); if (!is_pmu_set(ah, AR_PHY_PMU2, reg_pmu_set)) return; reg_pmu_set = (REG_READ(ah, AR_PHY_PMU2) & ~0x00200000) | (1 << 21); REG_WRITE(ah, AR_PHY_PMU2, reg_pmu_set); if (!is_pmu_set(ah, AR_PHY_PMU2, reg_pmu_set)) return; } else if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { reg_val = le32_to_cpu(pBase->swreg); REG_WRITE(ah, AR_PHY_PMU1, reg_val); } else { /* Internal regulator is ON. Write swreg register. */ reg_val = le32_to_cpu(pBase->swreg); REG_WRITE(ah, AR_RTC_REG_CONTROL1, REG_READ(ah, AR_RTC_REG_CONTROL1) & (~AR_RTC_REG_CONTROL1_SWREG_PROGRAM)); REG_WRITE(ah, AR_RTC_REG_CONTROL0, reg_val); /* Set REG_CONTROL1.SWREG_PROGRAM */ REG_WRITE(ah, AR_RTC_REG_CONTROL1, REG_READ(ah, AR_RTC_REG_CONTROL1) | AR_RTC_REG_CONTROL1_SWREG_PROGRAM); } } else { if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) { REG_RMW_FIELD(ah, AR_PHY_PMU2, AR_PHY_PMU2_PGM, 0); while (REG_READ_FIELD(ah, AR_PHY_PMU2, AR_PHY_PMU2_PGM)) udelay(10); REG_RMW_FIELD(ah, AR_PHY_PMU1, AR_PHY_PMU1_PWD, 0x1); while (!REG_READ_FIELD(ah, AR_PHY_PMU1, AR_PHY_PMU1_PWD)) udelay(10); REG_RMW_FIELD(ah, AR_PHY_PMU2, AR_PHY_PMU2_PGM, 0x1); while (!REG_READ_FIELD(ah, AR_PHY_PMU2, AR_PHY_PMU2_PGM)) udelay(10); } else if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) REG_RMW_FIELD(ah, AR_PHY_PMU1, AR_PHY_PMU1_PWD, 0x1); else { reg_val = REG_READ(ah, AR_RTC_SLEEP_CLK) | AR_RTC_FORCE_SWREG_PRD; REG_WRITE(ah, AR_RTC_SLEEP_CLK, reg_val); } } } static void ar9003_hw_apply_tuning_caps(struct ath_hw *ah) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; u8 tuning_caps_param = eep->baseEepHeader.params_for_tuning_caps[0]; if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah)) return; if (eep->baseEepHeader.featureEnable & 0x40) { tuning_caps_param &= 0x7f; REG_RMW_FIELD(ah, AR_CH0_XTAL, AR_CH0_XTAL_CAPINDAC, tuning_caps_param); REG_RMW_FIELD(ah, AR_CH0_XTAL, AR_CH0_XTAL_CAPOUTDAC, tuning_caps_param); } } static void ar9003_hw_quick_drop_apply(struct ath_hw *ah, u16 freq) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ar9300_base_eep_hdr *pBase = &eep->baseEepHeader; int quick_drop; s32 t[3], f[3] = {5180, 5500, 5785}; if (!(pBase->miscConfiguration & BIT(1))) return; if (freq < 4000) quick_drop = eep->modalHeader2G.quick_drop; else { t[0] = eep->base_ext1.quick_drop_low; t[1] = eep->modalHeader5G.quick_drop; t[2] = eep->base_ext1.quick_drop_high; quick_drop = ar9003_hw_power_interpolate(freq, f, t, 3); } REG_RMW_FIELD(ah, AR_PHY_AGC, AR_PHY_AGC_QUICK_DROP, quick_drop); } static void ar9003_hw_txend_to_xpa_off_apply(struct ath_hw *ah, bool is2ghz) { u32 value; value = ar9003_modal_header(ah, is2ghz)->txEndToXpaOff; REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_TX_END_XPAB_OFF, value); REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_TX_END_XPAA_OFF, value); } static void ar9003_hw_xpa_timing_control_apply(struct ath_hw *ah, bool is2ghz) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; u8 xpa_ctl; if (!(eep->baseEepHeader.featureEnable & 0x80)) return; if (!AR_SREV_9300(ah) && !AR_SREV_9340(ah) && !AR_SREV_9580(ah)) return; xpa_ctl = ar9003_modal_header(ah, is2ghz)->txFrameToXpaOn; if (is2ghz) REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_FRAME_XPAB_ON, xpa_ctl); else REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_FRAME_XPAA_ON, xpa_ctl); } static void ar9003_hw_xlna_bias_strength_apply(struct ath_hw *ah, bool is2ghz) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; u8 bias; if (!(eep->baseEepHeader.featureEnable & 0x40)) return; if (!AR_SREV_9300(ah)) return; bias = ar9003_modal_header(ah, is2ghz)->xlna_bias_strength; REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4, AR_PHY_65NM_RXTX4_XLNA_BIAS, bias & 0x3); bias >>= 2; REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4, AR_PHY_65NM_RXTX4_XLNA_BIAS, bias & 0x3); bias >>= 2; REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4, AR_PHY_65NM_RXTX4_XLNA_BIAS, bias & 0x3); } static int ar9003_hw_get_thermometer(struct ath_hw *ah) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ar9300_base_eep_hdr *pBase = &eep->baseEepHeader; int thermometer = (pBase->miscConfiguration >> 1) & 0x3; return --thermometer; } static void ar9003_hw_thermometer_apply(struct ath_hw *ah) { int thermometer = ar9003_hw_get_thermometer(ah); u8 therm_on = (thermometer < 0) ? 0 : 1; REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4, AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on); if (ah->caps.tx_chainmask & BIT(1)) REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4, AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on); if (ah->caps.tx_chainmask & BIT(2)) REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4, AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on); therm_on = (thermometer < 0) ? 0 : (thermometer == 0); REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4, AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on); if (ah->caps.tx_chainmask & BIT(1)) { therm_on = (thermometer < 0) ? 0 : (thermometer == 1); REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4, AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on); } if (ah->caps.tx_chainmask & BIT(2)) { therm_on = (thermometer < 0) ? 0 : (thermometer == 2); REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4, AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on); } } static void ar9003_hw_thermo_cal_apply(struct ath_hw *ah) { u32 data, ko, kg; if (!AR_SREV_9462_20(ah)) return; ar9300_otp_read_word(ah, 1, &data); ko = data & 0xff; kg = (data >> 8) & 0xff; if (ko || kg) { REG_RMW_FIELD(ah, AR_PHY_BB_THERM_ADC_3, AR_PHY_BB_THERM_ADC_3_THERM_ADC_OFFSET, ko); REG_RMW_FIELD(ah, AR_PHY_BB_THERM_ADC_3, AR_PHY_BB_THERM_ADC_3_THERM_ADC_SCALE_GAIN, kg + 256); } } static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { bool is2ghz = IS_CHAN_2GHZ(chan); ar9003_hw_xpa_timing_control_apply(ah, is2ghz); ar9003_hw_xpa_bias_level_apply(ah, is2ghz); ar9003_hw_ant_ctrl_apply(ah, is2ghz); ar9003_hw_drive_strength_apply(ah); ar9003_hw_xlna_bias_strength_apply(ah, is2ghz); ar9003_hw_atten_apply(ah, chan); ar9003_hw_quick_drop_apply(ah, chan->channel); if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9550(ah)) ar9003_hw_internal_regulator_apply(ah); ar9003_hw_apply_tuning_caps(ah); ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz); ar9003_hw_thermometer_apply(ah); ar9003_hw_thermo_cal_apply(ah); } static void ath9k_hw_ar9300_set_addac(struct ath_hw *ah, struct ath9k_channel *chan) { } /* * Returns the interpolated y value corresponding to the specified x value * from the np ordered pairs of data (px,py). * The pairs do not have to be in any order. * If the specified x value is less than any of the px, * the returned y value is equal to the py for the lowest px. * If the specified x value is greater than any of the px, * the returned y value is equal to the py for the highest px. */ static int ar9003_hw_power_interpolate(int32_t x, int32_t *px, int32_t *py, u_int16_t np) { int ip = 0; int lx = 0, ly = 0, lhave = 0; int hx = 0, hy = 0, hhave = 0; int dx = 0; int y = 0; lhave = 0; hhave = 0; /* identify best lower and higher x calibration measurement */ for (ip = 0; ip < np; ip++) { dx = x - px[ip]; /* this measurement is higher than our desired x */ if (dx <= 0) { if (!hhave || dx > (x - hx)) { /* new best higher x measurement */ hx = px[ip]; hy = py[ip]; hhave = 1; } } /* this measurement is lower than our desired x */ if (dx >= 0) { if (!lhave || dx < (x - lx)) { /* new best lower x measurement */ lx = px[ip]; ly = py[ip]; lhave = 1; } } } /* the low x is good */ if (lhave) { /* so is the high x */ if (hhave) { /* they're the same, so just pick one */ if (hx == lx) y = ly; else /* interpolate */ y = interpolate(x, lx, hx, ly, hy); } else /* only low is good, use it */ y = ly; } else if (hhave) /* only high is good, use it */ y = hy; else /* nothing is good,this should never happen unless np=0, ???? */ y = -(1 << 30); return y; } static u8 ar9003_hw_eeprom_get_tgt_pwr(struct ath_hw *ah, u16 rateIndex, u16 freq, bool is2GHz) { u16 numPiers, i; s32 targetPowerArray[AR9300_NUM_5G_20_TARGET_POWERS]; s32 freqArray[AR9300_NUM_5G_20_TARGET_POWERS]; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct cal_tgt_pow_legacy *pEepromTargetPwr; u8 *pFreqBin; if (is2GHz) { numPiers = AR9300_NUM_2G_20_TARGET_POWERS; pEepromTargetPwr = eep->calTargetPower2G; pFreqBin = eep->calTarget_freqbin_2G; } else { numPiers = AR9300_NUM_5G_20_TARGET_POWERS; pEepromTargetPwr = eep->calTargetPower5G; pFreqBin = eep->calTarget_freqbin_5G; } /* * create array of channels and targetpower from * targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], is2GHz); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } /* interpolate to get target power for given frequency */ return (u8) ar9003_hw_power_interpolate((s32) freq, freqArray, targetPowerArray, numPiers); } static u8 ar9003_hw_eeprom_get_ht20_tgt_pwr(struct ath_hw *ah, u16 rateIndex, u16 freq, bool is2GHz) { u16 numPiers, i; s32 targetPowerArray[AR9300_NUM_5G_20_TARGET_POWERS]; s32 freqArray[AR9300_NUM_5G_20_TARGET_POWERS]; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct cal_tgt_pow_ht *pEepromTargetPwr; u8 *pFreqBin; if (is2GHz) { numPiers = AR9300_NUM_2G_20_TARGET_POWERS; pEepromTargetPwr = eep->calTargetPower2GHT20; pFreqBin = eep->calTarget_freqbin_2GHT20; } else { numPiers = AR9300_NUM_5G_20_TARGET_POWERS; pEepromTargetPwr = eep->calTargetPower5GHT20; pFreqBin = eep->calTarget_freqbin_5GHT20; } /* * create array of channels and targetpower * from targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], is2GHz); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } /* interpolate to get target power for given frequency */ return (u8) ar9003_hw_power_interpolate((s32) freq, freqArray, targetPowerArray, numPiers); } static u8 ar9003_hw_eeprom_get_ht40_tgt_pwr(struct ath_hw *ah, u16 rateIndex, u16 freq, bool is2GHz) { u16 numPiers, i; s32 targetPowerArray[AR9300_NUM_5G_40_TARGET_POWERS]; s32 freqArray[AR9300_NUM_5G_40_TARGET_POWERS]; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct cal_tgt_pow_ht *pEepromTargetPwr; u8 *pFreqBin; if (is2GHz) { numPiers = AR9300_NUM_2G_40_TARGET_POWERS; pEepromTargetPwr = eep->calTargetPower2GHT40; pFreqBin = eep->calTarget_freqbin_2GHT40; } else { numPiers = AR9300_NUM_5G_40_TARGET_POWERS; pEepromTargetPwr = eep->calTargetPower5GHT40; pFreqBin = eep->calTarget_freqbin_5GHT40; } /* * create array of channels and targetpower from * targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], is2GHz); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } /* interpolate to get target power for given frequency */ return (u8) ar9003_hw_power_interpolate((s32) freq, freqArray, targetPowerArray, numPiers); } static u8 ar9003_hw_eeprom_get_cck_tgt_pwr(struct ath_hw *ah, u16 rateIndex, u16 freq) { u16 numPiers = AR9300_NUM_2G_CCK_TARGET_POWERS, i; s32 targetPowerArray[AR9300_NUM_2G_CCK_TARGET_POWERS]; s32 freqArray[AR9300_NUM_2G_CCK_TARGET_POWERS]; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct cal_tgt_pow_legacy *pEepromTargetPwr = eep->calTargetPowerCck; u8 *pFreqBin = eep->calTarget_freqbin_Cck; /* * create array of channels and targetpower from * targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], 1); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } /* interpolate to get target power for given frequency */ return (u8) ar9003_hw_power_interpolate((s32) freq, freqArray, targetPowerArray, numPiers); } /* Set tx power registers to array of values passed in */ static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray) { #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) /* make sure forced gain is not set */ REG_WRITE(ah, AR_PHY_TX_FORCED_GAIN, 0); /* Write the OFDM power per rate set */ /* 6 (LSB), 9, 12, 18 (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(0), POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 24) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 16) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 8) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 0)); /* 24 (LSB), 36, 48, 54 (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(1), POW_SM(pPwrArray[ALL_TARGET_LEGACY_54], 24) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_48], 16) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_36], 8) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 0)); /* Write the CCK power per rate set */ /* 1L (LSB), reserved, 2L, 2S (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(2), POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 24) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 16) | /* POW_SM(txPowerTimes2, 8) | this is reserved for AR9003 */ POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 0)); /* 5.5L (LSB), 5.5S, 11L, 11S (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(3), POW_SM(pPwrArray[ALL_TARGET_LEGACY_11S], 24) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_11L], 16) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_5S], 8) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 0) ); /* Write the power for duplicated frames - HT40 */ /* dup40_cck (LSB), dup40_ofdm, ext20_cck, ext20_ofdm (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(8), POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 24) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 16) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 8) | POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 0) ); /* Write the HT20 power per rate set */ /* 0/8/16 (LSB), 1-3/9-11/17-19, 4, 5 (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(4), POW_SM(pPwrArray[ALL_TARGET_HT20_5], 24) | POW_SM(pPwrArray[ALL_TARGET_HT20_4], 16) | POW_SM(pPwrArray[ALL_TARGET_HT20_1_3_9_11_17_19], 8) | POW_SM(pPwrArray[ALL_TARGET_HT20_0_8_16], 0) ); /* 6 (LSB), 7, 12, 13 (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(5), POW_SM(pPwrArray[ALL_TARGET_HT20_13], 24) | POW_SM(pPwrArray[ALL_TARGET_HT20_12], 16) | POW_SM(pPwrArray[ALL_TARGET_HT20_7], 8) | POW_SM(pPwrArray[ALL_TARGET_HT20_6], 0) ); /* 14 (LSB), 15, 20, 21 */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(9), POW_SM(pPwrArray[ALL_TARGET_HT20_21], 24) | POW_SM(pPwrArray[ALL_TARGET_HT20_20], 16) | POW_SM(pPwrArray[ALL_TARGET_HT20_15], 8) | POW_SM(pPwrArray[ALL_TARGET_HT20_14], 0) ); /* Mixed HT20 and HT40 rates */ /* HT20 22 (LSB), HT20 23, HT40 22, HT40 23 (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(10), POW_SM(pPwrArray[ALL_TARGET_HT40_23], 24) | POW_SM(pPwrArray[ALL_TARGET_HT40_22], 16) | POW_SM(pPwrArray[ALL_TARGET_HT20_23], 8) | POW_SM(pPwrArray[ALL_TARGET_HT20_22], 0) ); /* * Write the HT40 power per rate set * correct PAR difference between HT40 and HT20/LEGACY * 0/8/16 (LSB), 1-3/9-11/17-19, 4, 5 (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(6), POW_SM(pPwrArray[ALL_TARGET_HT40_5], 24) | POW_SM(pPwrArray[ALL_TARGET_HT40_4], 16) | POW_SM(pPwrArray[ALL_TARGET_HT40_1_3_9_11_17_19], 8) | POW_SM(pPwrArray[ALL_TARGET_HT40_0_8_16], 0) ); /* 6 (LSB), 7, 12, 13 (MSB) */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(7), POW_SM(pPwrArray[ALL_TARGET_HT40_13], 24) | POW_SM(pPwrArray[ALL_TARGET_HT40_12], 16) | POW_SM(pPwrArray[ALL_TARGET_HT40_7], 8) | POW_SM(pPwrArray[ALL_TARGET_HT40_6], 0) ); /* 14 (LSB), 15, 20, 21 */ REG_WRITE(ah, AR_PHY_POWER_TX_RATE(11), POW_SM(pPwrArray[ALL_TARGET_HT40_21], 24) | POW_SM(pPwrArray[ALL_TARGET_HT40_20], 16) | POW_SM(pPwrArray[ALL_TARGET_HT40_15], 8) | POW_SM(pPwrArray[ALL_TARGET_HT40_14], 0) ); return 0; #undef POW_SM } static void ar9003_hw_get_legacy_target_powers(struct ath_hw *ah, u16 freq, u8 *targetPowerValT2, bool is2GHz) { targetPowerValT2[ALL_TARGET_LEGACY_6_24] = ar9003_hw_eeprom_get_tgt_pwr(ah, LEGACY_TARGET_RATE_6_24, freq, is2GHz); targetPowerValT2[ALL_TARGET_LEGACY_36] = ar9003_hw_eeprom_get_tgt_pwr(ah, LEGACY_TARGET_RATE_36, freq, is2GHz); targetPowerValT2[ALL_TARGET_LEGACY_48] = ar9003_hw_eeprom_get_tgt_pwr(ah, LEGACY_TARGET_RATE_48, freq, is2GHz); targetPowerValT2[ALL_TARGET_LEGACY_54] = ar9003_hw_eeprom_get_tgt_pwr(ah, LEGACY_TARGET_RATE_54, freq, is2GHz); } static void ar9003_hw_get_cck_target_powers(struct ath_hw *ah, u16 freq, u8 *targetPowerValT2) { targetPowerValT2[ALL_TARGET_LEGACY_1L_5L] = ar9003_hw_eeprom_get_cck_tgt_pwr(ah, LEGACY_TARGET_RATE_1L_5L, freq); targetPowerValT2[ALL_TARGET_LEGACY_5S] = ar9003_hw_eeprom_get_cck_tgt_pwr(ah, LEGACY_TARGET_RATE_5S, freq); targetPowerValT2[ALL_TARGET_LEGACY_11L] = ar9003_hw_eeprom_get_cck_tgt_pwr(ah, LEGACY_TARGET_RATE_11L, freq); targetPowerValT2[ALL_TARGET_LEGACY_11S] = ar9003_hw_eeprom_get_cck_tgt_pwr(ah, LEGACY_TARGET_RATE_11S, freq); } static void ar9003_hw_get_ht20_target_powers(struct ath_hw *ah, u16 freq, u8 *targetPowerValT2, bool is2GHz) { targetPowerValT2[ALL_TARGET_HT20_0_8_16] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_0_8_16, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_1_3_9_11_17_19] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_1_3_9_11_17_19, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_4] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_4, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_5] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_5, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_6] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_6, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_7] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_7, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_12] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_12, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_13] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_13, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_14] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_14, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_15] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_15, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_20] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_20, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_21] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_21, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_22] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_22, freq, is2GHz); targetPowerValT2[ALL_TARGET_HT20_23] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_23, freq, is2GHz); } static void ar9003_hw_get_ht40_target_powers(struct ath_hw *ah, u16 freq, u8 *targetPowerValT2, bool is2GHz) { /* XXX: hard code for now, need to get from eeprom struct */ u8 ht40PowerIncForPdadc = 0; targetPowerValT2[ALL_TARGET_HT40_0_8_16] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_0_8_16, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_1_3_9_11_17_19] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_1_3_9_11_17_19, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_4] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_4, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_5] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_5, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_6] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_6, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_7] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_7, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_12] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_12, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_13] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_13, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_14] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_14, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_15] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_15, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_20] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_20, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_21] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_21, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_22] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_22, freq, is2GHz) + ht40PowerIncForPdadc; targetPowerValT2[ALL_TARGET_HT40_23] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_23, freq, is2GHz) + ht40PowerIncForPdadc; } static void ar9003_hw_get_target_power_eeprom(struct ath_hw *ah, struct ath9k_channel *chan, u8 *targetPowerValT2) { bool is2GHz = IS_CHAN_2GHZ(chan); unsigned int i = 0; struct ath_common *common = ath9k_hw_common(ah); u16 freq = chan->channel; if (is2GHz) ar9003_hw_get_cck_target_powers(ah, freq, targetPowerValT2); ar9003_hw_get_legacy_target_powers(ah, freq, targetPowerValT2, is2GHz); ar9003_hw_get_ht20_target_powers(ah, freq, targetPowerValT2, is2GHz); if (IS_CHAN_HT40(chan)) ar9003_hw_get_ht40_target_powers(ah, freq, targetPowerValT2, is2GHz); for (i = 0; i < ar9300RateSize; i++) { ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]); } } static int ar9003_hw_cal_pier_get(struct ath_hw *ah, int mode, int ipier, int ichain, int *pfrequency, int *pcorrection, int *ptemperature, int *pvoltage) { u8 *pCalPier; struct ar9300_cal_data_per_freq_op_loop *pCalPierStruct; int is2GHz; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ath_common *common = ath9k_hw_common(ah); if (ichain >= AR9300_MAX_CHAINS) { ath_dbg(common, EEPROM, "Invalid chain index, must be less than %d\n", AR9300_MAX_CHAINS); return -1; } if (mode) { /* 5GHz */ if (ipier >= AR9300_NUM_5G_CAL_PIERS) { ath_dbg(common, EEPROM, "Invalid 5GHz cal pier index, must be less than %d\n", AR9300_NUM_5G_CAL_PIERS); return -1; } pCalPier = &(eep->calFreqPier5G[ipier]); pCalPierStruct = &(eep->calPierData5G[ichain][ipier]); is2GHz = 0; } else { if (ipier >= AR9300_NUM_2G_CAL_PIERS) { ath_dbg(common, EEPROM, "Invalid 2GHz cal pier index, must be less than %d\n", AR9300_NUM_2G_CAL_PIERS); return -1; } pCalPier = &(eep->calFreqPier2G[ipier]); pCalPierStruct = &(eep->calPierData2G[ichain][ipier]); is2GHz = 1; } *pfrequency = ath9k_hw_fbin2freq(*pCalPier, is2GHz); *pcorrection = pCalPierStruct->refPower; *ptemperature = pCalPierStruct->tempMeas; *pvoltage = pCalPierStruct->voltMeas; return 0; } static int ar9003_hw_power_control_override(struct ath_hw *ah, int frequency, int *correction, int *voltage, int *temperature) { int tempSlope = 0; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; int f[8], t[8], i; REG_RMW(ah, AR_PHY_TPC_11_B0, (correction[0] << AR_PHY_TPC_OLPC_GAIN_DELTA_S), AR_PHY_TPC_OLPC_GAIN_DELTA); if (ah->caps.tx_chainmask & BIT(1)) REG_RMW(ah, AR_PHY_TPC_11_B1, (correction[1] << AR_PHY_TPC_OLPC_GAIN_DELTA_S), AR_PHY_TPC_OLPC_GAIN_DELTA); if (ah->caps.tx_chainmask & BIT(2)) REG_RMW(ah, AR_PHY_TPC_11_B2, (correction[2] << AR_PHY_TPC_OLPC_GAIN_DELTA_S), AR_PHY_TPC_OLPC_GAIN_DELTA); /* enable open loop power control on chip */ REG_RMW(ah, AR_PHY_TPC_6_B0, (3 << AR_PHY_TPC_6_ERROR_EST_MODE_S), AR_PHY_TPC_6_ERROR_EST_MODE); if (ah->caps.tx_chainmask & BIT(1)) REG_RMW(ah, AR_PHY_TPC_6_B1, (3 << AR_PHY_TPC_6_ERROR_EST_MODE_S), AR_PHY_TPC_6_ERROR_EST_MODE); if (ah->caps.tx_chainmask & BIT(2)) REG_RMW(ah, AR_PHY_TPC_6_B2, (3 << AR_PHY_TPC_6_ERROR_EST_MODE_S), AR_PHY_TPC_6_ERROR_EST_MODE); /* * enable temperature compensation * Need to use register names */ if (frequency < 4000) tempSlope = eep->modalHeader2G.tempSlope; else if ((eep->baseEepHeader.miscConfiguration & 0x20) != 0) { for (i = 0; i < 8; i++) { t[i] = eep->base_ext1.tempslopextension[i]; f[i] = FBIN2FREQ(eep->calFreqPier5G[i], 0); } tempSlope = ar9003_hw_power_interpolate((s32) frequency, f, t, 8); } else if (eep->base_ext2.tempSlopeLow != 0) { t[0] = eep->base_ext2.tempSlopeLow; f[0] = 5180; t[1] = eep->modalHeader5G.tempSlope; f[1] = 5500; t[2] = eep->base_ext2.tempSlopeHigh; f[2] = 5785; tempSlope = ar9003_hw_power_interpolate((s32) frequency, f, t, 3); } else tempSlope = eep->modalHeader5G.tempSlope; REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, tempSlope); if (AR_SREV_9462_20(ah)) REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, AR_PHY_TPC_19_B1_ALPHA_THERM, tempSlope); REG_RMW_FIELD(ah, AR_PHY_TPC_18, AR_PHY_TPC_18_THERM_CAL_VALUE, temperature[0]); return 0; } /* Apply the recorded correction values. */ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency) { int ichain, ipier, npier; int mode; int lfrequency[AR9300_MAX_CHAINS], lcorrection[AR9300_MAX_CHAINS], ltemperature[AR9300_MAX_CHAINS], lvoltage[AR9300_MAX_CHAINS]; int hfrequency[AR9300_MAX_CHAINS], hcorrection[AR9300_MAX_CHAINS], htemperature[AR9300_MAX_CHAINS], hvoltage[AR9300_MAX_CHAINS]; int fdiff; int correction[AR9300_MAX_CHAINS], voltage[AR9300_MAX_CHAINS], temperature[AR9300_MAX_CHAINS]; int pfrequency, pcorrection, ptemperature, pvoltage; struct ath_common *common = ath9k_hw_common(ah); mode = (frequency >= 4000); if (mode) npier = AR9300_NUM_5G_CAL_PIERS; else npier = AR9300_NUM_2G_CAL_PIERS; for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) { lfrequency[ichain] = 0; hfrequency[ichain] = 100000; } /* identify best lower and higher frequency calibration measurement */ for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) { for (ipier = 0; ipier < npier; ipier++) { if (!ar9003_hw_cal_pier_get(ah, mode, ipier, ichain, &pfrequency, &pcorrection, &ptemperature, &pvoltage)) { fdiff = frequency - pfrequency; /* * this measurement is higher than * our desired frequency */ if (fdiff <= 0) { if (hfrequency[ichain] <= 0 || hfrequency[ichain] >= 100000 || fdiff > (frequency - hfrequency[ichain])) { /* * new best higher * frequency measurement */ hfrequency[ichain] = pfrequency; hcorrection[ichain] = pcorrection; htemperature[ichain] = ptemperature; hvoltage[ichain] = pvoltage; } } if (fdiff >= 0) { if (lfrequency[ichain] <= 0 || fdiff < (frequency - lfrequency[ichain])) { /* * new best lower * frequency measurement */ lfrequency[ichain] = pfrequency; lcorrection[ichain] = pcorrection; ltemperature[ichain] = ptemperature; lvoltage[ichain] = pvoltage; } } } } } /* interpolate */ for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) { ath_dbg(common, EEPROM, "ch=%d f=%d low=%d %d h=%d %d\n", ichain, frequency, lfrequency[ichain], lcorrection[ichain], hfrequency[ichain], hcorrection[ichain]); /* they're the same, so just pick one */ if (hfrequency[ichain] == lfrequency[ichain]) { correction[ichain] = lcorrection[ichain]; voltage[ichain] = lvoltage[ichain]; temperature[ichain] = ltemperature[ichain]; } /* the low frequency is good */ else if (frequency - lfrequency[ichain] < 1000) { /* so is the high frequency, interpolate */ if (hfrequency[ichain] - frequency < 1000) { correction[ichain] = interpolate(frequency, lfrequency[ichain], hfrequency[ichain], lcorrection[ichain], hcorrection[ichain]); temperature[ichain] = interpolate(frequency, lfrequency[ichain], hfrequency[ichain], ltemperature[ichain], htemperature[ichain]); voltage[ichain] = interpolate(frequency, lfrequency[ichain], hfrequency[ichain], lvoltage[ichain], hvoltage[ichain]); } /* only low is good, use it */ else { correction[ichain] = lcorrection[ichain]; temperature[ichain] = ltemperature[ichain]; voltage[ichain] = lvoltage[ichain]; } } /* only high is good, use it */ else if (hfrequency[ichain] - frequency < 1000) { correction[ichain] = hcorrection[ichain]; temperature[ichain] = htemperature[ichain]; voltage[ichain] = hvoltage[ichain]; } else { /* nothing is good, presume 0???? */ correction[ichain] = 0; temperature[ichain] = 0; voltage[ichain] = 0; } } ar9003_hw_power_control_override(ah, frequency, correction, voltage, temperature); ath_dbg(common, EEPROM, "for frequency=%d, calibration correction = %d %d %d\n", frequency, correction[0], correction[1], correction[2]); return 0; } static u16 ar9003_hw_get_direct_edge_power(struct ar9300_eeprom *eep, int idx, int edge, bool is2GHz) { struct cal_ctl_data_2g *ctl_2g = eep->ctlPowerData_2G; struct cal_ctl_data_5g *ctl_5g = eep->ctlPowerData_5G; if (is2GHz) return CTL_EDGE_TPOWER(ctl_2g[idx].ctlEdges[edge]); else return CTL_EDGE_TPOWER(ctl_5g[idx].ctlEdges[edge]); } static u16 ar9003_hw_get_indirect_edge_power(struct ar9300_eeprom *eep, int idx, unsigned int edge, u16 freq, bool is2GHz) { struct cal_ctl_data_2g *ctl_2g = eep->ctlPowerData_2G; struct cal_ctl_data_5g *ctl_5g = eep->ctlPowerData_5G; u8 *ctl_freqbin = is2GHz ? &eep->ctl_freqbin_2G[idx][0] : &eep->ctl_freqbin_5G[idx][0]; if (is2GHz) { if (ath9k_hw_fbin2freq(ctl_freqbin[edge - 1], 1) < freq && CTL_EDGE_FLAGS(ctl_2g[idx].ctlEdges[edge - 1])) return CTL_EDGE_TPOWER(ctl_2g[idx].ctlEdges[edge - 1]); } else { if (ath9k_hw_fbin2freq(ctl_freqbin[edge - 1], 0) < freq && CTL_EDGE_FLAGS(ctl_5g[idx].ctlEdges[edge - 1])) return CTL_EDGE_TPOWER(ctl_5g[idx].ctlEdges[edge - 1]); } return MAX_RATE_POWER; } /* * Find the maximum conformance test limit for the given channel and CTL info */ static u16 ar9003_hw_get_max_edge_power(struct ar9300_eeprom *eep, u16 freq, int idx, bool is2GHz) { u16 twiceMaxEdgePower = MAX_RATE_POWER; u8 *ctl_freqbin = is2GHz ? &eep->ctl_freqbin_2G[idx][0] : &eep->ctl_freqbin_5G[idx][0]; u16 num_edges = is2GHz ? AR9300_NUM_BAND_EDGES_2G : AR9300_NUM_BAND_EDGES_5G; unsigned int edge; /* Get the edge power */ for (edge = 0; (edge < num_edges) && (ctl_freqbin[edge] != AR5416_BCHAN_UNUSED); edge++) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rdEdgePower */ if (freq == ath9k_hw_fbin2freq(ctl_freqbin[edge], is2GHz)) { twiceMaxEdgePower = ar9003_hw_get_direct_edge_power(eep, idx, edge, is2GHz); break; } else if ((edge > 0) && (freq < ath9k_hw_fbin2freq(ctl_freqbin[edge], is2GHz))) { twiceMaxEdgePower = ar9003_hw_get_indirect_edge_power(eep, idx, edge, freq, is2GHz); /* * Leave loop - no more affecting edges possible in * this monotonic increasing list */ break; } } return twiceMaxEdgePower; } static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, struct ath9k_channel *chan, u8 *pPwrArray, u16 cfgCtl, u8 antenna_reduction, u16 powerLimit) { struct ath_common *common = ath9k_hw_common(ah); struct ar9300_eeprom *pEepData = &ah->eeprom.ar9300_eep; u16 twiceMaxEdgePower; int i; u16 scaledPower = 0, minCtlPower; static const u16 ctlModesFor11a[] = { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 }; static const u16 ctlModesFor11g[] = { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 }; u16 numCtlModes; const u16 *pCtlMode; u16 ctlMode, freq; struct chan_centers centers; u8 *ctlIndex; u8 ctlNum; u16 twiceMinEdgePower; bool is2ghz = IS_CHAN_2GHZ(chan); ath9k_hw_get_channel_centers(ah, chan, ¢ers); scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit, antenna_reduction); if (is2ghz) { /* Setup for CTL modes */ /* CTL_11B, CTL_11G, CTL_2GHT20 */ numCtlModes = ARRAY_SIZE(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; pCtlMode = ctlModesFor11g; if (IS_CHAN_HT40(chan)) /* All 2G CTL's */ numCtlModes = ARRAY_SIZE(ctlModesFor11g); } else { /* Setup for CTL modes */ /* CTL_11A, CTL_5GHT20 */ numCtlModes = ARRAY_SIZE(ctlModesFor11a) - SUB_NUM_CTL_MODES_AT_5G_40; pCtlMode = ctlModesFor11a; if (IS_CHAN_HT40(chan)) /* All 5G CTL's */ numCtlModes = ARRAY_SIZE(ctlModesFor11a); } /* * For MIMO, need to apply regulatory caps individually across * dynamically running modes: CCK, OFDM, HT20, HT40 * * The outer loop walks through each possible applicable runtime mode. * The inner loop walks through each ctlIndex entry in EEPROM. * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. */ for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || (pCtlMode[ctlMode] == CTL_2GHT40); if (isHt40CtlMode) freq = centers.synth_center; else if (pCtlMode[ctlMode] & EXT_ADDITIVE) freq = centers.ext_center; else freq = centers.ctl_center; ath_dbg(common, REGULATORY, "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, EXT_ADDITIVE %d\n", ctlMode, numCtlModes, isHt40CtlMode, (pCtlMode[ctlMode] & EXT_ADDITIVE)); /* walk through each CTL index stored in EEPROM */ if (is2ghz) { ctlIndex = pEepData->ctlIndex_2G; ctlNum = AR9300_NUM_CTLS_2G; } else { ctlIndex = pEepData->ctlIndex_5G; ctlNum = AR9300_NUM_CTLS_5G; } twiceMaxEdgePower = MAX_RATE_POWER; for (i = 0; (i < ctlNum) && ctlIndex[i]; i++) { ath_dbg(common, REGULATORY, "LOOP-Ctlidx %d: cfgCtl 0x%2.2x pCtlMode 0x%2.2x ctlIndex 0x%2.2x chan %d\n", i, cfgCtl, pCtlMode[ctlMode], ctlIndex[i], chan->channel); /* * compare test group from regulatory * channel list with test mode from pCtlMode * list */ if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ctlIndex[i]) || (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ((ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { twiceMinEdgePower = ar9003_hw_get_max_edge_power(pEepData, freq, i, is2ghz); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) /* * Find the minimum of all CTL * edge powers that apply to * this channel */ twiceMaxEdgePower = min(twiceMaxEdgePower, twiceMinEdgePower); else { /* specific */ twiceMaxEdgePower = twiceMinEdgePower; break; } } } minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower); ath_dbg(common, REGULATORY, "SEL-Min ctlMode %d pCtlMode %d 2xMaxEdge %d sP %d minCtlPwr %d\n", ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower, scaledPower, minCtlPower); /* Apply ctl mode to correct target power set */ switch (pCtlMode[ctlMode]) { case CTL_11B: for (i = ALL_TARGET_LEGACY_1L_5L; i <= ALL_TARGET_LEGACY_11S; i++) pPwrArray[i] = (u8)min((u16)pPwrArray[i], minCtlPower); break; case CTL_11A: case CTL_11G: for (i = ALL_TARGET_LEGACY_6_24; i <= ALL_TARGET_LEGACY_54; i++) pPwrArray[i] = (u8)min((u16)pPwrArray[i], minCtlPower); break; case CTL_5GHT20: case CTL_2GHT20: for (i = ALL_TARGET_HT20_0_8_16; i <= ALL_TARGET_HT20_23; i++) pPwrArray[i] = (u8)min((u16)pPwrArray[i], minCtlPower); break; case CTL_5GHT40: case CTL_2GHT40: for (i = ALL_TARGET_HT40_0_8_16; i <= ALL_TARGET_HT40_23; i++) pPwrArray[i] = (u8)min((u16)pPwrArray[i], minCtlPower); break; default: break; } } /* end ctl mode checking */ } static inline u8 mcsidx_to_tgtpwridx(unsigned int mcs_idx, u8 base_pwridx) { u8 mod_idx = mcs_idx % 8; if (mod_idx <= 3) return mod_idx ? (base_pwridx + 1) : base_pwridx; else return base_pwridx + 4 * (mcs_idx / 8) + mod_idx - 2; } static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, struct ath9k_channel *chan, u16 cfgCtl, u8 twiceAntennaReduction, u8 powerLimit, bool test) { struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ath_common *common = ath9k_hw_common(ah); struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; struct ar9300_modal_eep_header *modal_hdr; u8 targetPowerValT2[ar9300RateSize]; u8 target_power_val_t2_eep[ar9300RateSize]; unsigned int i = 0, paprd_scale_factor = 0; u8 pwr_idx, min_pwridx = 0; memset(targetPowerValT2, 0 , sizeof(targetPowerValT2)); /* * Get target powers from EEPROM - our baseline for TX Power */ ar9003_hw_get_target_power_eeprom(ah, chan, targetPowerValT2); if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) { if (IS_CHAN_2GHZ(chan)) modal_hdr = &eep->modalHeader2G; else modal_hdr = &eep->modalHeader5G; ah->paprd_ratemask = le32_to_cpu(modal_hdr->papdRateMaskHt20) & AR9300_PAPRD_RATE_MASK; ah->paprd_ratemask_ht40 = le32_to_cpu(modal_hdr->papdRateMaskHt40) & AR9300_PAPRD_RATE_MASK; paprd_scale_factor = ar9003_get_paprd_scale_factor(ah, chan); min_pwridx = IS_CHAN_HT40(chan) ? ALL_TARGET_HT40_0_8_16 : ALL_TARGET_HT20_0_8_16; if (!ah->paprd_table_write_done) { memcpy(target_power_val_t2_eep, targetPowerValT2, sizeof(targetPowerValT2)); for (i = 0; i < 24; i++) { pwr_idx = mcsidx_to_tgtpwridx(i, min_pwridx); if (ah->paprd_ratemask & (1 << i)) { if (targetPowerValT2[pwr_idx] && targetPowerValT2[pwr_idx] == target_power_val_t2_eep[pwr_idx]) targetPowerValT2[pwr_idx] -= paprd_scale_factor; } } } memcpy(target_power_val_t2_eep, targetPowerValT2, sizeof(targetPowerValT2)); } ar9003_hw_set_power_per_rate_table(ah, chan, targetPowerValT2, cfgCtl, twiceAntennaReduction, powerLimit); if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) { for (i = 0; i < ar9300RateSize; i++) { if ((ah->paprd_ratemask & (1 << i)) && (abs(targetPowerValT2[i] - target_power_val_t2_eep[i]) > paprd_scale_factor)) { ah->paprd_ratemask &= ~(1 << i); ath_dbg(common, EEPROM, "paprd disabled for mcs %d\n", i); } } } regulatory->max_power_level = 0; for (i = 0; i < ar9300RateSize; i++) { if (targetPowerValT2[i] > regulatory->max_power_level) regulatory->max_power_level = targetPowerValT2[i]; } ath9k_hw_update_regulatory_maxpower(ah); if (test) return; for (i = 0; i < ar9300RateSize; i++) { ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]); } /* Write target power array to registers */ ar9003_hw_tx_power_regwrite(ah, targetPowerValT2); ar9003_hw_calibration_apply(ah, chan->channel); if (IS_CHAN_2GHZ(chan)) { if (IS_CHAN_HT40(chan)) i = ALL_TARGET_HT40_0_8_16; else i = ALL_TARGET_HT20_0_8_16; } else { if (IS_CHAN_HT40(chan)) i = ALL_TARGET_HT40_7; else i = ALL_TARGET_HT20_7; } ah->paprd_target_power = targetPowerValT2[i]; } static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { return AR_NO_SPUR; } s32 ar9003_hw_get_tx_gain_idx(struct ath_hw *ah) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; return (eep->baseEepHeader.txrxgain >> 4) & 0xf; /* bits 7:4 */ } s32 ar9003_hw_get_rx_gain_idx(struct ath_hw *ah) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; return (eep->baseEepHeader.txrxgain) & 0xf; /* bits 3:0 */ } u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is2ghz) { return ar9003_modal_header(ah, is2ghz)->spurChans; } unsigned int ar9003_get_paprd_scale_factor(struct ath_hw *ah, struct ath9k_channel *chan) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; if (IS_CHAN_2GHZ(chan)) return MS(le32_to_cpu(eep->modalHeader2G.papdRateMaskHt20), AR9300_PAPRD_SCALE_1); else { if (chan->channel >= 5700) return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20), AR9300_PAPRD_SCALE_1); else if (chan->channel >= 5400) return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40), AR9300_PAPRD_SCALE_2); else return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40), AR9300_PAPRD_SCALE_1); } } const struct eeprom_ops eep_ar9300_ops = { .check_eeprom = ath9k_hw_ar9300_check_eeprom, .get_eeprom = ath9k_hw_ar9300_get_eeprom, .fill_eeprom = ath9k_hw_ar9300_fill_eeprom, .dump_eeprom = ath9k_hw_ar9003_dump_eeprom, .get_eeprom_ver = ath9k_hw_ar9300_get_eeprom_ver, .get_eeprom_rev = ath9k_hw_ar9300_get_eeprom_rev, .set_board_values = ath9k_hw_ar9300_set_board_values, .set_addac = ath9k_hw_ar9300_set_addac, .set_txpower = ath9k_hw_ar9300_set_txpower, .get_spur_channel = ath9k_hw_ar9300_get_spur_channel }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_calib.c0000644000175000017500000007200212026211315024472 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "hw-ops.h" #include "ar9003_phy.h" #include "ar9003_rtt.h" #include "ar9003_mci.h" #define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT #define MAX_MAG_DELTA 11 #define MAX_PHS_DELTA 10 struct coeff { int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT]; int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT]; int iqc_coeff[2]; }; enum ar9003_cal_types { IQ_MISMATCH_CAL = BIT(0), TEMP_COMP_CAL = BIT(1), }; static void ar9003_hw_setup_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal) { struct ath_common *common = ath9k_hw_common(ah); /* Select calibration to run */ switch (currCal->calData->calType) { case IQ_MISMATCH_CAL: /* * Start calibration with * 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, currCal->calData->calCountMax); REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); ath_dbg(common, CALIBRATE, "starting IQ Mismatch Calibration\n"); /* Kick-off cal */ REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); break; case TEMP_COMP_CAL: REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_LOCAL, 1); REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); ath_dbg(common, CALIBRATE, "starting Temperature Compensation Calibration\n"); break; } } /* * Generic calibration routine. * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ static bool ar9003_hw_per_calibration(struct ath_hw *ah, struct ath9k_channel *ichan, u8 rxchainmask, struct ath9k_cal_list *currCal) { struct ath9k_hw_cal_data *caldata = ah->caldata; /* Cal is assumed not done until explicitly set below */ bool iscaldone = false; /* Calibration in progress. */ if (currCal->calState == CAL_RUNNING) { /* Check to see if it has finished. */ if (!(REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) { /* * Accumulate cal measures for active chains */ currCal->calData->calCollect(ah); ah->cal_samples++; if (ah->cal_samples >= currCal->calData->calNumSamples) { unsigned int i, numChains = 0; for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (rxchainmask & (1 << i)) numChains++; } /* * Process accumulated data */ currCal->calData->calPostProc(ah, numChains); /* Calibration has finished. */ caldata->CalValid |= currCal->calData->calType; currCal->calState = CAL_DONE; iscaldone = true; } else { /* * Set-up collection of another sub-sample until we * get desired number */ ar9003_hw_setup_calibration(ah, currCal); } } } else if (!(caldata->CalValid & currCal->calData->calType)) { /* If current cal is marked invalid in channel, kick it off */ ath9k_hw_reset_calibration(ah, currCal); } return iscaldone; } static bool ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, u8 rxchainmask, bool longcal) { bool iscaldone = true; struct ath9k_cal_list *currCal = ah->cal_list_curr; /* * For given calibration: * 1. Call generic cal routine * 2. When this cal is done (isCalDone) if we have more cals waiting * (eg after reset), mask this to upper layers by not propagating * isCalDone if it is set to TRUE. * Instead, change isCalDone to FALSE and setup the waiting cal(s) * to be run. */ if (currCal && (currCal->calState == CAL_RUNNING || currCal->calState == CAL_WAITING)) { iscaldone = ar9003_hw_per_calibration(ah, chan, rxchainmask, currCal); if (iscaldone) { ah->cal_list_curr = currCal = currCal->calNext; if (currCal->calState == CAL_WAITING) { iscaldone = false; ath9k_hw_reset_calibration(ah, currCal); } } } /* * Do NF cal only at longer intervals. Get the value from * the previous NF cal and update history buffer. */ if (longcal && ath9k_hw_getnf(ah, chan)) { /* * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a historical * value. */ ath9k_hw_loadnf(ah, ah->curchan); /* start NF calibration, without updating BB NF register */ ath9k_hw_start_nfcal(ah, false); } return iscaldone; } static void ar9003_hw_iqcal_collect(struct ath_hw *ah) { int i; /* Accumulate IQ cal measures for active chains */ for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (ah->txchainmask & BIT(i)) { ah->totalPowerMeasI[i] += REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); ah->totalPowerMeasQ[i] += REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ah->totalIqCorrMeas[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); ath_dbg(ath9k_hw_common(ah), CALIBRATE, "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", ah->cal_samples, i, ah->totalPowerMeasI[i], ah->totalPowerMeasQ[i], ah->totalIqCorrMeas[i]); } } } static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) { struct ath_common *common = ath9k_hw_common(ah); u32 powerMeasQ, powerMeasI, iqCorrMeas; u32 qCoffDenom, iCoffDenom; int32_t qCoff, iCoff; int iqCorrNeg, i; static const u_int32_t offset_array[3] = { AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B1, AR_PHY_RX_IQCAL_CORR_B2, }; for (i = 0; i < numChains; i++) { powerMeasI = ah->totalPowerMeasI[i]; powerMeasQ = ah->totalPowerMeasQ[i]; iqCorrMeas = ah->totalIqCorrMeas[i]; ath_dbg(common, CALIBRATE, "Starting IQ Cal and Correction for Chain %d\n", i); ath_dbg(common, CALIBRATE, "Original: Chn %d iq_corr_meas = 0x%08x\n", i, ah->totalIqCorrMeas[i]); iqCorrNeg = 0; if (iqCorrMeas > 0x80000000) { iqCorrMeas = (0xffffffff - iqCorrMeas) + 1; iqCorrNeg = 1; } ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); ath_dbg(common, CALIBRATE, "iqCorrNeg is 0x%08x\n", iqCorrNeg); iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 256; qCoffDenom = powerMeasQ / 64; if ((iCoffDenom != 0) && (qCoffDenom != 0)) { iCoff = iqCorrMeas / iCoffDenom; qCoff = powerMeasI / qCoffDenom - 64; ath_dbg(common, CALIBRATE, "Chn %d iCoff = 0x%08x\n", i, iCoff); ath_dbg(common, CALIBRATE, "Chn %d qCoff = 0x%08x\n", i, qCoff); /* Force bounds on iCoff */ if (iCoff >= 63) iCoff = 63; else if (iCoff <= -63) iCoff = -63; /* Negate iCoff if iqCorrNeg == 0 */ if (iqCorrNeg == 0x0) iCoff = -iCoff; /* Force bounds on qCoff */ if (qCoff >= 63) qCoff = 63; else if (qCoff <= -63) qCoff = -63; iCoff = iCoff & 0x7f; qCoff = qCoff & 0x7f; ath_dbg(common, CALIBRATE, "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", i, iCoff, qCoff); ath_dbg(common, CALIBRATE, "Register offset (0x%04x) before update = 0x%x\n", offset_array[i], REG_READ(ah, offset_array[i])); REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, iCoff); REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, qCoff); ath_dbg(common, CALIBRATE, "Register offset (0x%04x) QI COFF (bitfields 0x%08x) after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, REG_READ(ah, offset_array[i])); ath_dbg(common, CALIBRATE, "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, REG_READ(ah, offset_array[i])); ath_dbg(common, CALIBRATE, "IQ Cal and Correction done for Chain %d\n", i); } } REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); ath_dbg(common, CALIBRATE, "IQ Cal and Correction (offset 0x%04x) enabled (bit position 0x%08x). New Value 0x%08x\n", (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); } static const struct ath9k_percal_data iq_cal_single_sample = { IQ_MISMATCH_CAL, MIN_CAL_SAMPLES, PER_MAX_LOG_COUNT, ar9003_hw_iqcal_collect, ar9003_hw_iqcalibrate }; static void ar9003_hw_init_cal_settings(struct ath_hw *ah) { ah->iq_caldata.calData = &iq_cal_single_sample; } /* * solve 4x4 linear equation used in loopback iq cal. */ static bool ar9003_hw_solve_iq_cal(struct ath_hw *ah, s32 sin_2phi_1, s32 cos_2phi_1, s32 sin_2phi_2, s32 cos_2phi_2, s32 mag_a0_d0, s32 phs_a0_d0, s32 mag_a1_d0, s32 phs_a1_d0, s32 solved_eq[]) { s32 f1 = cos_2phi_1 - cos_2phi_2, f3 = sin_2phi_1 - sin_2phi_2, f2; s32 mag_tx, phs_tx, mag_rx, phs_rx; const s32 result_shift = 1 << 15; struct ath_common *common = ath9k_hw_common(ah); f2 = (f1 * f1 + f3 * f3) / result_shift; if (!f2) { ath_dbg(common, CALIBRATE, "Divide by 0\n"); return false; } /* mag mismatch, tx */ mag_tx = f1 * (mag_a0_d0 - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0); /* phs mismatch, tx */ phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0); mag_tx = (mag_tx / f2); phs_tx = (phs_tx / f2); /* mag mismatch, rx */ mag_rx = mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) / result_shift; /* phs mismatch, rx */ phs_rx = phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) / result_shift; solved_eq[0] = mag_tx; solved_eq[1] = phs_tx; solved_eq[2] = mag_rx; solved_eq[3] = phs_rx; return true; } static s32 ar9003_hw_find_mag_approx(struct ath_hw *ah, s32 in_re, s32 in_im) { s32 abs_i = abs(in_re), abs_q = abs(in_im), max_abs, min_abs; if (abs_i > abs_q) { max_abs = abs_i; min_abs = abs_q; } else { max_abs = abs_q; min_abs = abs_i; } return max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4); } #define DELPT 32 static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, s32 chain_idx, const s32 iq_res[], s32 iqc_coeff[]) { s32 i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0, i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1, i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0, i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1; s32 mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1, phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1, sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2; s32 mag_tx, phs_tx, mag_rx, phs_rx; s32 solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx, q_q_coff, q_i_coff; const s32 res_scale = 1 << 15; const s32 delpt_shift = 1 << 8; s32 mag1, mag2; struct ath_common *common = ath9k_hw_common(ah); i2_m_q2_a0_d0 = iq_res[0] & 0xfff; i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff; iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8); if (i2_m_q2_a0_d0 > 0x800) i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1); if (i2_p_q2_a0_d0 > 0x800) i2_p_q2_a0_d0 = -((0xfff - i2_p_q2_a0_d0) + 1); if (iq_corr_a0_d0 > 0x800) iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1); i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff; i2_p_q2_a0_d1 = (iq_res[2] & 0xfff); iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff; if (i2_m_q2_a0_d1 > 0x800) i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); if (i2_p_q2_a0_d1 > 0x800) i2_p_q2_a0_d1 = -((0xfff - i2_p_q2_a0_d1) + 1); if (iq_corr_a0_d1 > 0x800) iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8); i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff; iq_corr_a1_d0 = iq_res[4] & 0xfff; if (i2_m_q2_a1_d0 > 0x800) i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1); if (i2_p_q2_a1_d0 > 0x800) i2_p_q2_a1_d0 = -((0xfff - i2_p_q2_a1_d0) + 1); if (iq_corr_a1_d0 > 0x800) iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1); i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff; i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8); iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff; if (i2_m_q2_a1_d1 > 0x800) i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1); if (i2_p_q2_a1_d1 > 0x800) i2_p_q2_a1_d1 = -((0xfff - i2_p_q2_a1_d1) + 1); if (iq_corr_a1_d1 > 0x800) iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1); if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) || (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) { ath_dbg(common, CALIBRATE, "Divide by 0:\n" "a0_d0=%d\n" "a0_d1=%d\n" "a2_d0=%d\n" "a1_d1=%d\n", i2_p_q2_a0_d0, i2_p_q2_a0_d1, i2_p_q2_a1_d0, i2_p_q2_a1_d1); return false; } mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1; phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1; mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0; phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0; mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1; phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1; /* w/o analog phase shift */ sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT); /* w/o analog phase shift */ cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT); /* w/ analog phase shift */ sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT); /* w/ analog phase shift */ cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT); /* * force sin^2 + cos^2 = 1; * find magnitude by approximation */ mag1 = ar9003_hw_find_mag_approx(ah, cos_2phi_1, sin_2phi_1); mag2 = ar9003_hw_find_mag_approx(ah, cos_2phi_2, sin_2phi_2); if ((mag1 == 0) || (mag2 == 0)) { ath_dbg(common, CALIBRATE, "Divide by 0: mag1=%d, mag2=%d\n", mag1, mag2); return false; } /* normalization sin and cos by mag */ sin_2phi_1 = (sin_2phi_1 * res_scale / mag1); cos_2phi_1 = (cos_2phi_1 * res_scale / mag1); sin_2phi_2 = (sin_2phi_2 * res_scale / mag2); cos_2phi_2 = (cos_2phi_2 * res_scale / mag2); /* calculate IQ mismatch */ if (!ar9003_hw_solve_iq_cal(ah, sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2, mag_a0_d0, phs_a0_d0, mag_a1_d0, phs_a1_d0, solved_eq)) { ath_dbg(common, CALIBRATE, "Call to ar9003_hw_solve_iq_cal() failed\n"); return false; } mag_tx = solved_eq[0]; phs_tx = solved_eq[1]; mag_rx = solved_eq[2]; phs_rx = solved_eq[3]; ath_dbg(common, CALIBRATE, "chain %d: mag mismatch=%d phase mismatch=%d\n", chain_idx, mag_tx/res_scale, phs_tx/res_scale); if (res_scale == mag_tx) { ath_dbg(common, CALIBRATE, "Divide by 0: mag_tx=%d, res_scale=%d\n", mag_tx, res_scale); return false; } /* calculate and quantize Tx IQ correction factor */ mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx); phs_corr_tx = -phs_tx; q_q_coff = (mag_corr_tx * 128 / res_scale); q_i_coff = (phs_corr_tx * 256 / res_scale); ath_dbg(common, CALIBRATE, "tx chain %d: mag corr=%d phase corr=%d\n", chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) q_i_coff = -63; if (q_i_coff > 63) q_i_coff = 63; if (q_q_coff < -63) q_q_coff = -63; if (q_q_coff > 63) q_q_coff = 63; iqc_coeff[0] = (q_q_coff * 128) + q_i_coff; ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[0]); if (-mag_rx == res_scale) { ath_dbg(common, CALIBRATE, "Divide by 0: mag_rx=%d, res_scale=%d\n", mag_rx, res_scale); return false; } /* calculate and quantize Rx IQ correction factors */ mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx); phs_corr_rx = -phs_rx; q_q_coff = (mag_corr_rx * 128 / res_scale); q_i_coff = (phs_corr_rx * 256 / res_scale); ath_dbg(common, CALIBRATE, "rx chain %d: mag corr=%d phase corr=%d\n", chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) q_i_coff = -63; if (q_i_coff > 63) q_i_coff = 63; if (q_q_coff < -63) q_q_coff = -63; if (q_q_coff > 63) q_q_coff = 63; iqc_coeff[1] = (q_q_coff * 128) + q_i_coff; ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[1]); return true; } static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, int max_delta) { int mp_max = -64, max_idx = 0; int mp_min = 63, min_idx = 0; int mp_avg = 0, i, outlier_idx = 0, mp_count = 0; /* find min/max mismatch across all calibrated gains */ for (i = 0; i < nmeasurement; i++) { if (mp_coeff[i] > mp_max) { mp_max = mp_coeff[i]; max_idx = i; } else if (mp_coeff[i] < mp_min) { mp_min = mp_coeff[i]; min_idx = i; } } /* find average (exclude max abs value) */ for (i = 0; i < nmeasurement; i++) { if ((abs(mp_coeff[i]) < abs(mp_max)) || (abs(mp_coeff[i]) < abs(mp_min))) { mp_avg += mp_coeff[i]; mp_count++; } } /* * finding mean magnitude/phase if possible, otherwise * just use the last value as the mean */ if (mp_count) mp_avg /= mp_count; else mp_avg = mp_coeff[nmeasurement - 1]; /* detect outlier */ if (abs(mp_max - mp_min) > max_delta) { if (abs(mp_max - mp_avg) > abs(mp_min - mp_avg)) outlier_idx = max_idx; else outlier_idx = min_idx; mp_coeff[outlier_idx] = mp_avg; } } static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, struct coeff *coeff, bool is_reusable) { int i, im, nmeasurement; u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS]; struct ath9k_hw_cal_data *caldata = ah->caldata; memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff)); for (i = 0; i < MAX_MEASUREMENT / 2; i++) { tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_B0(i); if (!AR_SREV_9485(ah)) { tx_corr_coeff[i * 2][1] = tx_corr_coeff[(i * 2) + 1][1] = AR_PHY_TX_IQCAL_CORR_COEFF_B1(i); tx_corr_coeff[i * 2][2] = tx_corr_coeff[(i * 2) + 1][2] = AR_PHY_TX_IQCAL_CORR_COEFF_B2(i); } } /* Load the average of 2 passes */ for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue; nmeasurement = REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0, AR_PHY_CALIBRATED_GAINS_0); if (nmeasurement > MAX_MEASUREMENT) nmeasurement = MAX_MEASUREMENT; /* detect outlier only if nmeasurement > 1 */ if (nmeasurement > 1) { /* Detect magnitude outlier */ ar9003_hw_detect_outlier(coeff->mag_coeff[i], nmeasurement, MAX_MAG_DELTA); /* Detect phase outlier */ ar9003_hw_detect_outlier(coeff->phs_coeff[i], nmeasurement, MAX_PHS_DELTA); } for (im = 0; im < nmeasurement; im++) { coeff->iqc_coeff[0] = (coeff->mag_coeff[i][im] & 0x7f) | ((coeff->phs_coeff[i][im] & 0x7f) << 7); if ((im % 2) == 0) REG_RMW_FIELD(ah, tx_corr_coeff[im][i], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, coeff->iqc_coeff[0]); else REG_RMW_FIELD(ah, tx_corr_coeff[im][i], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, coeff->iqc_coeff[0]); if (caldata) caldata->tx_corr_coeff[im][i] = coeff->iqc_coeff[0]; } if (caldata) caldata->num_measures[i] = nmeasurement; } REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); if (caldata) caldata->done_txiqcal_once = is_reusable; return; } static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u8 tx_gain_forced; tx_gain_forced = REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE); if (tx_gain_forced) REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE, 0); REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START, AR_PHY_TX_IQCAL_START_DO_CAL, 1); if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START, AR_PHY_TX_IQCAL_START_DO_CAL, 0, AH_WAIT_TIMEOUT)) { ath_dbg(common, CALIBRATE, "Tx IQ Cal is not completed\n"); return false; } return true; } static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) { struct ath_common *common = ath9k_hw_common(ah); const u32 txiqcal_status[AR9300_MAX_CHAINS] = { AR_PHY_TX_IQCAL_STATUS_B0, AR_PHY_TX_IQCAL_STATUS_B1, AR_PHY_TX_IQCAL_STATUS_B2, }; const u_int32_t chan_info_tab[] = { AR_PHY_CHAN_INFO_TAB_0, AR_PHY_CHAN_INFO_TAB_1, AR_PHY_CHAN_INFO_TAB_2, }; struct coeff coeff; s32 iq_res[6]; int i, im, j; int nmeasurement; for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue; nmeasurement = REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0, AR_PHY_CALIBRATED_GAINS_0); if (nmeasurement > MAX_MEASUREMENT) nmeasurement = MAX_MEASUREMENT; for (im = 0; im < nmeasurement; im++) { ath_dbg(common, CALIBRATE, "Doing Tx IQ Cal for chain %d\n", i); if (REG_READ(ah, txiqcal_status[i]) & AR_PHY_TX_IQCAL_STATUS_FAILED) { ath_dbg(common, CALIBRATE, "Tx IQ Cal failed for chain %d\n", i); goto tx_iqcal_fail; } for (j = 0; j < 3; j++) { u32 idx = 2 * j, offset = 4 * (3 * im + j); REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_TAB_S2_READ, 0); /* 32 bits */ iq_res[idx] = REG_READ(ah, chan_info_tab[i] + offset); REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_TAB_S2_READ, 1); /* 16 bits */ iq_res[idx + 1] = 0xffff & REG_READ(ah, chan_info_tab[i] + offset); ath_dbg(common, CALIBRATE, "IQ_RES[%d]=0x%x IQ_RES[%d]=0x%x\n", idx, iq_res[idx], idx + 1, iq_res[idx + 1]); } if (!ar9003_hw_calc_iq_corr(ah, i, iq_res, coeff.iqc_coeff)) { ath_dbg(common, CALIBRATE, "Failed in calculation of IQ correction\n"); goto tx_iqcal_fail; } coeff.mag_coeff[i][im] = coeff.iqc_coeff[0] & 0x7f; coeff.phs_coeff[i][im] = (coeff.iqc_coeff[0] >> 7) & 0x7f; if (coeff.mag_coeff[i][im] > 63) coeff.mag_coeff[i][im] -= 128; if (coeff.phs_coeff[i][im] > 63) coeff.phs_coeff[i][im] -= 128; } } ar9003_hw_tx_iqcal_load_avg_2_passes(ah, &coeff, is_reusable); return; tx_iqcal_fail: ath_dbg(common, CALIBRATE, "Tx IQ Cal failed\n"); return; } static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) { struct ath9k_hw_cal_data *caldata = ah->caldata; u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS]; int i, im; memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff)); for (i = 0; i < MAX_MEASUREMENT / 2; i++) { tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_B0(i); if (!AR_SREV_9485(ah)) { tx_corr_coeff[i * 2][1] = tx_corr_coeff[(i * 2) + 1][1] = AR_PHY_TX_IQCAL_CORR_COEFF_B1(i); tx_corr_coeff[i * 2][2] = tx_corr_coeff[(i * 2) + 1][2] = AR_PHY_TX_IQCAL_CORR_COEFF_B2(i); } } for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue; for (im = 0; im < caldata->num_measures[i]; im++) { if ((im % 2) == 0) REG_RMW_FIELD(ah, tx_corr_coeff[im][i], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, caldata->tx_corr_coeff[im][i]); else REG_RMW_FIELD(ah, tx_corr_coeff[im][i], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, caldata->tx_corr_coeff[im][i]); } } REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); } static bool ar9003_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = ah->caldata; bool txiqcal_done = false, txclcal_done = false; bool is_reusable = true, status = true; bool run_rtt_cal = false, run_agc_cal; bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | AR_PHY_AGC_CONTROL_PKDET_CAL; int i, j; u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0, AR_PHY_CL_TAB_1, AR_PHY_CL_TAB_2 }; if (rtt) { if (!ar9003_hw_rtt_restore(ah, chan)) run_rtt_cal = true; if (run_rtt_cal) ath_dbg(common, CALIBRATE, "RTT calibration to be done\n"); } run_agc_cal = run_rtt_cal; if (run_rtt_cal) { ar9003_hw_rtt_enable(ah); ar9003_hw_rtt_set_mask(ah, 0x00); ar9003_hw_rtt_clear_hist(ah); } if (rtt && !run_rtt_cal) { agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL); agc_supp_cals &= agc_ctrl; agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | AR_PHY_AGC_CONTROL_PKDET_CAL); REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); } if (ah->enabled_cals & TX_CL_CAL) { if (caldata && caldata->done_txclcal_once) REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); else { REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); run_agc_cal = true; } } if (!(ah->enabled_cals & TX_IQ_CAL)) goto skip_tx_iqcal; /* Do Tx IQ Calibration */ REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1, AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, DELPT); /* * For AR9485 or later chips, TxIQ cal runs as part of * AGC calibration */ if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { if (caldata && !caldata->done_txiqcal_once) REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); else REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); txiqcal_done = run_agc_cal = true; goto skip_tx_iqcal; } else if (caldata && !caldata->done_txiqcal_once) run_agc_cal = true; if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_req(ah, &is_reusable); if (!(IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))) { txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); udelay(5); REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); } skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { /* Calibrate the AGC */ REG_WRITE(ah, AR_PHY_AGC_CONTROL, REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration complete */ status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); } if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_done(ah); if (rtt && !run_rtt_cal) { agc_ctrl |= agc_supp_cals; REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); } if (!status) { if (run_rtt_cal) ar9003_hw_rtt_disable(ah); ath_dbg(common, CALIBRATE, "offset calibration failed to complete in 1ms; noisy environment?\n"); return false; } if (txiqcal_done) ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable); else if (caldata && caldata->done_txiqcal_once) ar9003_hw_tx_iq_cal_reload(ah); #define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j)) if (caldata && (ah->enabled_cals & TX_CL_CAL)) { txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_CLC_SUCCESS); if (caldata->done_txclcal_once) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue; for (j = 0; j < MAX_CL_TAB_ENTRY; j++) REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]), caldata->tx_clcal[i][j]); } } else if (is_reusable && txclcal_done) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue; for (j = 0; j < MAX_CL_TAB_ENTRY; j++) caldata->tx_clcal[i][j] = REG_READ(ah, CL_TAB_ENTRY(cl_idx[i])); } caldata->done_txclcal_once = true; } } #undef CL_TAB_ENTRY if (run_rtt_cal && caldata) { if (is_reusable) { if (!ath9k_hw_rfbus_req(ah)) ath_err(ath9k_hw_common(ah), "Could not stop baseband\n"); else ar9003_hw_rtt_fill_hist(ah); ath9k_hw_rfbus_done(ah); } ar9003_hw_rtt_disable(ah); } /* Initialize list pointers */ ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; ah->supp_cals = IQ_MISMATCH_CAL; if (ah->supp_cals & IQ_MISMATCH_CAL) { INIT_CAL(&ah->iq_caldata); INSERT_CAL(ah, &ah->iq_caldata); ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); } if (ah->supp_cals & TEMP_COMP_CAL) { INIT_CAL(&ah->tempCompCalData); INSERT_CAL(ah, &ah->tempCompCalData); ath_dbg(common, CALIBRATE, "enabling Temperature Compensation Calibration\n"); } /* Initialize current pointer to first element in list */ ah->cal_list_curr = ah->cal_list; if (ah->cal_list_curr) ath9k_hw_reset_calibration(ah, ah->cal_list_curr); if (caldata) caldata->CalValid = 0; return true; } void ar9003_hw_attach_calib_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); priv_ops->init_cal_settings = ar9003_hw_init_cal_settings; priv_ops->init_cal = ar9003_hw_init_cal; priv_ops->setup_calibration = ar9003_hw_setup_calibration; ops->calibrate = ar9003_hw_calibrate; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h0000644000175000017500000016454612026211315025760 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INITVALS_9003_2P2_H #define INITVALS_9003_2P2_H /* AR9003 2.2 */ static const u32 ar9300_2p2_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31}, {0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800}, {0x000160b0, 0x03284f3e, 0x03284f3e, 0x05d08f20, 0x05d08f20}, {0x0001610c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, {0x0001650c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, {0x0001690c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; static const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x52022470, 0x52022470, 0x3f001861, 0x3f001861}, {0x0000a548, 0x55022490, 0x55022490, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x59022492, 0x59022492, 0x47001a83, 0x47001a83}, {0x0000a550, 0x5d022692, 0x5d022692, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x61022892, 0x61022892, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x65024890, 0x65024890, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x69024892, 0x69024892, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x6e024c92, 0x6e024c92, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402}, {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, {0x0000a5c4, 0x52822470, 0x52822470, 0x3f801861, 0x3f801861}, {0x0000a5c8, 0x55822490, 0x55822490, 0x43801a81, 0x43801a81}, {0x0000a5cc, 0x59822492, 0x59822492, 0x47801a83, 0x47801a83}, {0x0000a5d0, 0x5d822692, 0x5d822692, 0x4a801c84, 0x4a801c84}, {0x0000a5d4, 0x61822892, 0x61822892, 0x4e801ce3, 0x4e801ce3}, {0x0000a5d8, 0x65824890, 0x65824890, 0x52801ce5, 0x52801ce5}, {0x0000a5dc, 0x69824892, 0x69824892, 0x56801ce9, 0x56801ce9}, {0x0000a5e0, 0x6e824c92, 0x6e824c92, 0x5a801ceb, 0x5a801ceb}, {0x0000a5e4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5e8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5ec, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5f0, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5f4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x02004000, 0x02004000, 0x01404000, 0x01404000}, {0x0000a618, 0x02004801, 0x02004801, 0x01404501, 0x01404501}, {0x0000a61c, 0x02808a02, 0x02808a02, 0x02008501, 0x02008501}, {0x0000a620, 0x0380ce03, 0x0380ce03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x04411104, 0x04411104, 0x03010c04, 0x03010c04}, {0x0000a628, 0x04411104, 0x04411104, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a630, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a634, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a638, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a63c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000b2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000c2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000c2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, {0x0000c2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016048, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016448, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016848, 0x62480001, 0x62480001, 0x62480001, 0x62480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; static const u32 ar9300Modes_fast_clock_2p2[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, {0x000010b0, 0x00000fd0, 0x00001fa0}, {0x00008014, 0x044c044c, 0x08980898}, {0x0000801c, 0x148ec02b, 0x148ec057}, {0x00008318, 0x000044c0, 0x00008980}, {0x00009e00, 0x0372131c, 0x0372131c}, {0x0000a230, 0x0000000b, 0x00000016}, {0x0000a254, 0x00000898, 0x00001130}, }; static const u32 ar9300_2p2_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73f00000}, {0x0001600c, 0x00000000}, {0x00016040, 0x7f80fff8}, {0x0001604c, 0x76d005b5}, {0x00016050, 0x556cf031}, {0x00016054, 0x13449440}, {0x00016058, 0x0c51c92c}, {0x0001605c, 0x3db7fffc}, {0x00016060, 0xfffffffc}, {0x00016064, 0x000f0278}, {0x0001606c, 0x6db60000}, {0x00016080, 0x00000000}, {0x00016084, 0x0e48048c}, {0x00016088, 0x54214514}, {0x0001608c, 0x119f481e}, {0x00016090, 0x24926490}, {0x00016098, 0xd2888888}, {0x000160a0, 0x0a108ffe}, {0x000160a4, 0x812fc370}, {0x000160a8, 0x423c8000}, {0x000160b4, 0x92480080}, {0x000160c0, 0x00adb6d0}, {0x000160c4, 0x6db6db60}, {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x01e6c000}, {0x00016100, 0x3fffbe01}, {0x00016104, 0xfff80000}, {0x00016108, 0x00080010}, {0x00016144, 0x02084080}, {0x00016148, 0x00000000}, {0x00016280, 0x058a0001}, {0x00016284, 0x3d840208}, {0x00016288, 0x05a20408}, {0x0001628c, 0x00038c07}, {0x00016290, 0x00000004}, {0x00016294, 0x458a214f}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00800700}, {0x0001638c, 0x00800700}, {0x00016390, 0x00800700}, {0x00016394, 0x00000000}, {0x00016398, 0x00000000}, {0x0001639c, 0x00000000}, {0x000163a0, 0x00000001}, {0x000163a4, 0x00000001}, {0x000163a8, 0x00000000}, {0x000163ac, 0x00000000}, {0x000163b0, 0x00000000}, {0x000163b4, 0x00000000}, {0x000163b8, 0x00000000}, {0x000163bc, 0x00000000}, {0x000163c0, 0x000000a0}, {0x000163c4, 0x000c0000}, {0x000163c8, 0x14021402}, {0x000163cc, 0x00001402}, {0x000163d0, 0x00000000}, {0x000163d4, 0x00000000}, {0x00016400, 0x36db6db6}, {0x00016404, 0x6db6db40}, {0x00016408, 0x73f00000}, {0x0001640c, 0x00000000}, {0x00016440, 0x7f80fff8}, {0x0001644c, 0x76d005b5}, {0x00016450, 0x556cf031}, {0x00016454, 0x13449440}, {0x00016458, 0x0c51c92c}, {0x0001645c, 0x3db7fffc}, {0x00016460, 0xfffffffc}, {0x00016464, 0x000f0278}, {0x0001646c, 0x6db60000}, {0x00016500, 0x3fffbe01}, {0x00016504, 0xfff80000}, {0x00016508, 0x00080010}, {0x00016544, 0x02084080}, {0x00016548, 0x00000000}, {0x00016780, 0x00000000}, {0x00016784, 0x00000000}, {0x00016788, 0x00800700}, {0x0001678c, 0x00800700}, {0x00016790, 0x00800700}, {0x00016794, 0x00000000}, {0x00016798, 0x00000000}, {0x0001679c, 0x00000000}, {0x000167a0, 0x00000001}, {0x000167a4, 0x00000001}, {0x000167a8, 0x00000000}, {0x000167ac, 0x00000000}, {0x000167b0, 0x00000000}, {0x000167b4, 0x00000000}, {0x000167b8, 0x00000000}, {0x000167bc, 0x00000000}, {0x000167c0, 0x000000a0}, {0x000167c4, 0x000c0000}, {0x000167c8, 0x14021402}, {0x000167cc, 0x00001402}, {0x000167d0, 0x00000000}, {0x000167d4, 0x00000000}, {0x00016800, 0x36db6db6}, {0x00016804, 0x6db6db40}, {0x00016808, 0x73f00000}, {0x0001680c, 0x00000000}, {0x00016840, 0x7f80fff8}, {0x0001684c, 0x76d005b5}, {0x00016850, 0x556cf031}, {0x00016854, 0x13449440}, {0x00016858, 0x0c51c92c}, {0x0001685c, 0x3db7fffc}, {0x00016860, 0xfffffffc}, {0x00016864, 0x000f0278}, {0x0001686c, 0x6db60000}, {0x00016900, 0x3fffbe01}, {0x00016904, 0xfff80000}, {0x00016908, 0x00080010}, {0x00016944, 0x02084080}, {0x00016948, 0x00000000}, {0x00016b80, 0x00000000}, {0x00016b84, 0x00000000}, {0x00016b88, 0x00800700}, {0x00016b8c, 0x00800700}, {0x00016b90, 0x00800700}, {0x00016b94, 0x00000000}, {0x00016b98, 0x00000000}, {0x00016b9c, 0x00000000}, {0x00016ba0, 0x00000001}, {0x00016ba4, 0x00000001}, {0x00016ba8, 0x00000000}, {0x00016bac, 0x00000000}, {0x00016bb0, 0x00000000}, {0x00016bb4, 0x00000000}, {0x00016bb8, 0x00000000}, {0x00016bbc, 0x00000000}, {0x00016bc0, 0x000000a0}, {0x00016bc4, 0x000c0000}, {0x00016bc8, 0x14021402}, {0x00016bcc, 0x00001402}, {0x00016bd0, 0x00000000}, {0x00016bd4, 0x00000000}, }; static const u32 ar9300_2p2_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; static const u32 ar9300_2p2_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; static const u32 ar9300_2p2_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x000036c0, 0x000036c4, 0x000036c4, 0x000036c0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x01026a2f, 0x01026a2f, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000b284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000b830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000be04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000be1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000be20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, {0x0000c284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, }; static const u32 ar9300_2p2_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a9f6b}, {0x0000980c, 0x04900000}, {0x00009814, 0x9280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, {0x00009834, 0x6400a290}, {0x00009838, 0x0108ecff}, {0x0000983c, 0x0d000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, {0x000098f0, 0x00000000}, {0x000098f4, 0x00000000}, {0x00009c04, 0xff55ff55}, {0x00009c08, 0x0320ff55}, {0x00009c0c, 0x00000000}, {0x00009c10, 0x00000000}, {0x00009c14, 0x00046384}, {0x00009c18, 0x05b6b440}, {0x00009c1c, 0x00b6b440}, {0x00009d00, 0xc080a333}, {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, {0x00009d10, 0x01834061}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, {0x00009e24, 0x990bb515}, {0x00009e28, 0x0c6f0000}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0x00000000}, {0x00009fc0, 0x803e4788}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x00009fd0, 0x01193b93}, {0x0000a20c, 0x00000000}, {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, {0x0000a2c0, 0x00000001}, {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, {0x0000a2f8, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a34c, 0x00000000}, {0x0000a350, 0x0000a000}, {0x0000a364, 0x00000000}, {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0xaaaaaaaa}, {0x0000a3ac, 0x3c466478}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3d8, 0x20202020}, {0x0000a3dc, 0x20202020}, {0x0000a3e0, 0x20202020}, {0x0000a3e4, 0x20202020}, {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000246}, {0x0000a3f8, 0x0c9bd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00100000}, {0x0000a440, 0x00000000}, {0x0000a444, 0x00000000}, {0x0000a448, 0x05000080}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a458, 0x00000000}, {0x0000a640, 0x00000000}, {0x0000a644, 0x3fad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, {0x0000a67c, 0x13121110}, {0x0000a680, 0x31301514}, {0x0000a684, 0x35343332}, {0x0000a688, 0x00000036}, {0x0000a690, 0x00000838}, {0x0000a7c0, 0x00000000}, {0x0000a7c4, 0xfffffffc}, {0x0000a7c8, 0x00000000}, {0x0000a7cc, 0x00000000}, {0x0000a7d0, 0x00000000}, {0x0000a7d4, 0x00000004}, {0x0000a7dc, 0x00000001}, {0x0000a8d0, 0x004b6a8e}, {0x0000a8d4, 0x00000820}, {0x0000a8dc, 0x00000000}, {0x0000a8f0, 0x00000000}, {0x0000a8f4, 0x00000000}, {0x0000b2d0, 0x00000080}, {0x0000b2d4, 0x00000000}, {0x0000b2ec, 0x00000000}, {0x0000b2f0, 0x00000000}, {0x0000b2f4, 0x00000000}, {0x0000b2f8, 0x00000000}, {0x0000b408, 0x0e79e5c0}, {0x0000b40c, 0x00820820}, {0x0000b420, 0x00000000}, {0x0000b8d0, 0x004b6a8e}, {0x0000b8d4, 0x00000820}, {0x0000b8dc, 0x00000000}, {0x0000b8f0, 0x00000000}, {0x0000b8f4, 0x00000000}, {0x0000c2d0, 0x00000080}, {0x0000c2d4, 0x00000000}, {0x0000c2ec, 0x00000000}, {0x0000c2f0, 0x00000000}, {0x0000c2f4, 0x00000000}, {0x0000c2f8, 0x00000000}, {0x0000c408, 0x0e79e5c0}, {0x0000c40c, 0x00820820}, {0x0000c420, 0x00000000}, }; static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400}, {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402}, {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404}, {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02}, {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640}, {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660}, {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x5a08442e, 0x5a08442e, 0x47001a83, 0x47001a83}, {0x0000a550, 0x5e0a4431, 0x5e0a4431, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x15800028, 0x15800028, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1b80002b, 0x1b80002b, 0x12800400, 0x12800400}, {0x0000a598, 0x1f820028, 0x1f820028, 0x16800402, 0x16800402}, {0x0000a59c, 0x2582002b, 0x2582002b, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2a84002a, 0x2a84002a, 0x1c800603, 0x1c800603}, {0x0000a5a4, 0x2e86002a, 0x2e86002a, 0x21800a02, 0x21800a02}, {0x0000a5a8, 0x3382202d, 0x3382202d, 0x25800a04, 0x25800a04}, {0x0000a5ac, 0x3884202c, 0x3884202c, 0x28800a20, 0x28800a20}, {0x0000a5b0, 0x3c86202c, 0x3c86202c, 0x2c800e20, 0x2c800e20}, {0x0000a5b4, 0x4188202d, 0x4188202d, 0x30800e22, 0x30800e22}, {0x0000a5b8, 0x4586402d, 0x4586402d, 0x34800e24, 0x34800e24}, {0x0000a5bc, 0x4986222d, 0x4986222d, 0x38801640, 0x38801640}, {0x0000a5c0, 0x4d862231, 0x4d862231, 0x3c801660, 0x3c801660}, {0x0000a5c4, 0x50882231, 0x50882231, 0x3f801861, 0x3f801861}, {0x0000a5c8, 0x5688422e, 0x5688422e, 0x43801a81, 0x43801a81}, {0x0000a5cc, 0x5a88442e, 0x5a88442e, 0x47801a83, 0x47801a83}, {0x0000a5d0, 0x5e8a4431, 0x5e8a4431, 0x4a801c84, 0x4a801c84}, {0x0000a5d4, 0x648a4432, 0x648a4432, 0x4e801ce3, 0x4e801ce3}, {0x0000a5d8, 0x688a4434, 0x688a4434, 0x52801ce5, 0x52801ce5}, {0x0000a5dc, 0x6c8a6434, 0x6c8a6434, 0x56801ce9, 0x56801ce9}, {0x0000a5e0, 0x6f8a6633, 0x6f8a6633, 0x5a801ceb, 0x5a801ceb}, {0x0000a5e4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a5e8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a5ec, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a5f0, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a5f4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000}, {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501}, {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501}, {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016048, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016448, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016848, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000c2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, {0x0000c2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000c2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, {0x00016048, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, {0x00016448, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, {0x00016848, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; static const u32 ar9300Common_rx_gain_table_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x01910190}, {0x0000a030, 0x01930192}, {0x0000a034, 0x01950194}, {0x0000a038, 0x038a0196}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x1a1a1a1a}, {0x0000a084, 0x1a1a1a1a}, {0x0000a088, 0x1a1a1a1a}, {0x0000a08c, 0x1a1a1a1a}, {0x0000a090, 0x171a1a1a}, {0x0000a094, 0x11111717}, {0x0000a098, 0x00030311}, {0x0000a09c, 0x00000000}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x2a2d2f32}, {0x0000b084, 0x21232328}, {0x0000b088, 0x19191c1e}, {0x0000b08c, 0x12141417}, {0x0000b090, 0x07070e0e}, {0x0000b094, 0x03030305}, {0x0000b098, 0x00000003}, {0x0000b09c, 0x00000000}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar9300Modes_low_ob_db_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x52022470, 0x52022470, 0x3f001861, 0x3f001861}, {0x0000a548, 0x55022490, 0x55022490, 0x43001a81, 0x43001a81}, {0x0000a54c, 0x59022492, 0x59022492, 0x47001a83, 0x47001a83}, {0x0000a550, 0x5d022692, 0x5d022692, 0x4a001c84, 0x4a001c84}, {0x0000a554, 0x61022892, 0x61022892, 0x4e001ce3, 0x4e001ce3}, {0x0000a558, 0x65024890, 0x65024890, 0x52001ce5, 0x52001ce5}, {0x0000a55c, 0x69024892, 0x69024892, 0x56001ce9, 0x56001ce9}, {0x0000a560, 0x6e024c92, 0x6e024c92, 0x5a001ceb, 0x5a001ceb}, {0x0000a564, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a568, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a56c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a570, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a574, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a578, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a57c, 0x74026e92, 0x74026e92, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402}, {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, {0x0000a5c4, 0x52822470, 0x52822470, 0x3f801861, 0x3f801861}, {0x0000a5c8, 0x55822490, 0x55822490, 0x43801a81, 0x43801a81}, {0x0000a5cc, 0x59822492, 0x59822492, 0x47801a83, 0x47801a83}, {0x0000a5d0, 0x5d822692, 0x5d822692, 0x4a801c84, 0x4a801c84}, {0x0000a5d4, 0x61822892, 0x61822892, 0x4e801ce3, 0x4e801ce3}, {0x0000a5d8, 0x65824890, 0x65824890, 0x52801ce5, 0x52801ce5}, {0x0000a5dc, 0x69824892, 0x69824892, 0x56801ce9, 0x56801ce9}, {0x0000a5e0, 0x6e824c92, 0x6e824c92, 0x5a801ceb, 0x5a801ceb}, {0x0000a5e4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5e8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5ec, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5f0, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5f4, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x74826e92, 0x74826e92, 0x5d801eec, 0x5d801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a614, 0x02004000, 0x02004000, 0x01404000, 0x01404000}, {0x0000a618, 0x02004801, 0x02004801, 0x01404501, 0x01404501}, {0x0000a61c, 0x02808a02, 0x02808a02, 0x02008501, 0x02008501}, {0x0000a620, 0x0380ce03, 0x0380ce03, 0x0280ca03, 0x0280ca03}, {0x0000a624, 0x04411104, 0x04411104, 0x03010c04, 0x03010c04}, {0x0000a628, 0x04411104, 0x04411104, 0x04014c04, 0x04014c04}, {0x0000a62c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a630, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a634, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a638, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000a63c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, {0x0000b2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000b2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, {0x0000b2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000c2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, {0x0000c2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, {0x0000c2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, {0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; static const u32 ar9300_2p2_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000000}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x000010f0, 0x00000100}, {0x00001270, 0x00000000}, {0x000012b0, 0x00000000}, {0x000012f0, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00008000, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, {0x0000804c, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000310}, {0x00008074, 0x00000020}, {0x00008078, 0x00000000}, {0x0000809c, 0x0000000f}, {0x000080a0, 0x00000000}, {0x000080a4, 0x02ff0000}, {0x000080a8, 0x0e070605}, {0x000080ac, 0x0000000d}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, {0x000080c8, 0x13881c20}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00a00000}, {0x000080d8, 0x00400000}, {0x000080dc, 0x00000000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x3f3f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00000000}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000000}, {0x00008114, 0x000007ff}, {0x00008118, 0x000000aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x0000ffff}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x000081c0, 0x00000000}, {0x000081c4, 0x33332210}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f424}, {0x00008248, 0x00000800}, {0x0000824c, 0x0001e848}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, {0x00008264, 0x9d400010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000004}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000140}, {0x00008314, 0x00000000}, {0x0000831c, 0x0000010d}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000700}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x02400000}, {0x00008340, 0x000107ff}, {0x00008344, 0xaa48105b}, {0x00008348, 0x008f0000}, {0x0000835c, 0x00000000}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0xffffffff}, {0x00008394, 0xffffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a0, 0x00000000}, {0x000083a4, 0x0000fa14}, {0x000083a8, 0x000f0c00}, {0x000083ac, 0x33332210}, {0x000083b0, 0x33332210}, {0x000083b4, 0x33332210}, {0x000083b8, 0x33332210}, {0x000083bc, 0x00000000}, {0x000083c0, 0x00000000}, {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, {0x000083d0, 0x000301ff}, }; static const u32 ar9300Common_wo_xlna_rx_gain_table_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, {0x0000a00c, 0x00810080}, {0x0000a010, 0x00830082}, {0x0000a014, 0x01810180}, {0x0000a018, 0x01830182}, {0x0000a01c, 0x01850184}, {0x0000a020, 0x01890188}, {0x0000a024, 0x018b018a}, {0x0000a028, 0x018d018c}, {0x0000a02c, 0x03820190}, {0x0000a030, 0x03840383}, {0x0000a034, 0x03880385}, {0x0000a038, 0x038a0389}, {0x0000a03c, 0x038c038b}, {0x0000a040, 0x0390038d}, {0x0000a044, 0x03920391}, {0x0000a048, 0x03940393}, {0x0000a04c, 0x03960395}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, {0x0000a05c, 0x00000000}, {0x0000a060, 0x00000000}, {0x0000a064, 0x00000000}, {0x0000a068, 0x00000000}, {0x0000a06c, 0x00000000}, {0x0000a070, 0x00000000}, {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, {0x0000a080, 0x29292929}, {0x0000a084, 0x29292929}, {0x0000a088, 0x29292929}, {0x0000a08c, 0x29292929}, {0x0000a090, 0x22292929}, {0x0000a094, 0x1d1d2222}, {0x0000a098, 0x0c111117}, {0x0000a09c, 0x00030303}, {0x0000a0a0, 0x00000000}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, {0x0000a0b0, 0x00000000}, {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, {0x0000a0c0, 0x001f0000}, {0x0000a0c4, 0x01000101}, {0x0000a0c8, 0x011e011f}, {0x0000a0cc, 0x011c011d}, {0x0000a0d0, 0x02030204}, {0x0000a0d4, 0x02010202}, {0x0000a0d8, 0x021f0200}, {0x0000a0dc, 0x0302021e}, {0x0000a0e0, 0x03000301}, {0x0000a0e4, 0x031e031f}, {0x0000a0e8, 0x0402031d}, {0x0000a0ec, 0x04000401}, {0x0000a0f0, 0x041e041f}, {0x0000a0f4, 0x0502041d}, {0x0000a0f8, 0x05000501}, {0x0000a0fc, 0x051e051f}, {0x0000a100, 0x06010602}, {0x0000a104, 0x061f0600}, {0x0000a108, 0x061d061e}, {0x0000a10c, 0x07020703}, {0x0000a110, 0x07000701}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, {0x0000a120, 0x00000000}, {0x0000a124, 0x00000000}, {0x0000a128, 0x00000000}, {0x0000a12c, 0x00000000}, {0x0000a130, 0x00000000}, {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, {0x0000a144, 0x01000101}, {0x0000a148, 0x011e011f}, {0x0000a14c, 0x011c011d}, {0x0000a150, 0x02030204}, {0x0000a154, 0x02010202}, {0x0000a158, 0x021f0200}, {0x0000a15c, 0x0302021e}, {0x0000a160, 0x03000301}, {0x0000a164, 0x031e031f}, {0x0000a168, 0x0402031d}, {0x0000a16c, 0x04000401}, {0x0000a170, 0x041e041f}, {0x0000a174, 0x0502041d}, {0x0000a178, 0x05000501}, {0x0000a17c, 0x051e051f}, {0x0000a180, 0x06010602}, {0x0000a184, 0x061f0600}, {0x0000a188, 0x061d061e}, {0x0000a18c, 0x07020703}, {0x0000a190, 0x07000701}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, {0x0000a1a0, 0x00000000}, {0x0000a1a4, 0x00000000}, {0x0000a1a8, 0x00000000}, {0x0000a1ac, 0x00000000}, {0x0000a1b0, 0x00000000}, {0x0000a1b4, 0x00000000}, {0x0000a1b8, 0x00000000}, {0x0000a1bc, 0x00000000}, {0x0000a1c0, 0x00000000}, {0x0000a1c4, 0x00000000}, {0x0000a1c8, 0x00000000}, {0x0000a1cc, 0x00000000}, {0x0000a1d0, 0x00000000}, {0x0000a1d4, 0x00000000}, {0x0000a1d8, 0x00000000}, {0x0000a1dc, 0x00000000}, {0x0000a1e0, 0x00000000}, {0x0000a1e4, 0x00000000}, {0x0000a1e8, 0x00000000}, {0x0000a1ec, 0x00000000}, {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, {0x0000a1fc, 0x00000196}, {0x0000b000, 0x00010000}, {0x0000b004, 0x00030002}, {0x0000b008, 0x00050004}, {0x0000b00c, 0x00810080}, {0x0000b010, 0x00830082}, {0x0000b014, 0x01810180}, {0x0000b018, 0x01830182}, {0x0000b01c, 0x01850184}, {0x0000b020, 0x02810280}, {0x0000b024, 0x02830282}, {0x0000b028, 0x02850284}, {0x0000b02c, 0x02890288}, {0x0000b030, 0x028b028a}, {0x0000b034, 0x0388028c}, {0x0000b038, 0x038a0389}, {0x0000b03c, 0x038c038b}, {0x0000b040, 0x0390038d}, {0x0000b044, 0x03920391}, {0x0000b048, 0x03940393}, {0x0000b04c, 0x03960395}, {0x0000b050, 0x00000000}, {0x0000b054, 0x00000000}, {0x0000b058, 0x00000000}, {0x0000b05c, 0x00000000}, {0x0000b060, 0x00000000}, {0x0000b064, 0x00000000}, {0x0000b068, 0x00000000}, {0x0000b06c, 0x00000000}, {0x0000b070, 0x00000000}, {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, {0x0000b080, 0x32323232}, {0x0000b084, 0x2f2f3232}, {0x0000b088, 0x23282a2d}, {0x0000b08c, 0x1c1e2123}, {0x0000b090, 0x14171919}, {0x0000b094, 0x0e0e1214}, {0x0000b098, 0x03050707}, {0x0000b09c, 0x00030303}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, {0x0000b0ac, 0x00000000}, {0x0000b0b0, 0x00000000}, {0x0000b0b4, 0x00000000}, {0x0000b0b8, 0x00000000}, {0x0000b0bc, 0x00000000}, {0x0000b0c0, 0x003f0020}, {0x0000b0c4, 0x00400041}, {0x0000b0c8, 0x0140005f}, {0x0000b0cc, 0x0160015f}, {0x0000b0d0, 0x017e017f}, {0x0000b0d4, 0x02410242}, {0x0000b0d8, 0x025f0240}, {0x0000b0dc, 0x027f0260}, {0x0000b0e0, 0x0341027e}, {0x0000b0e4, 0x035f0340}, {0x0000b0e8, 0x037f0360}, {0x0000b0ec, 0x04400441}, {0x0000b0f0, 0x0460045f}, {0x0000b0f4, 0x0541047f}, {0x0000b0f8, 0x055f0540}, {0x0000b0fc, 0x057f0560}, {0x0000b100, 0x06400641}, {0x0000b104, 0x0660065f}, {0x0000b108, 0x067e067f}, {0x0000b10c, 0x07410742}, {0x0000b110, 0x075f0740}, {0x0000b114, 0x077f0760}, {0x0000b118, 0x07800781}, {0x0000b11c, 0x07a0079f}, {0x0000b120, 0x07c107bf}, {0x0000b124, 0x000007c0}, {0x0000b128, 0x00000000}, {0x0000b12c, 0x00000000}, {0x0000b130, 0x00000000}, {0x0000b134, 0x00000000}, {0x0000b138, 0x00000000}, {0x0000b13c, 0x00000000}, {0x0000b140, 0x003f0020}, {0x0000b144, 0x00400041}, {0x0000b148, 0x0140005f}, {0x0000b14c, 0x0160015f}, {0x0000b150, 0x017e017f}, {0x0000b154, 0x02410242}, {0x0000b158, 0x025f0240}, {0x0000b15c, 0x027f0260}, {0x0000b160, 0x0341027e}, {0x0000b164, 0x035f0340}, {0x0000b168, 0x037f0360}, {0x0000b16c, 0x04400441}, {0x0000b170, 0x0460045f}, {0x0000b174, 0x0541047f}, {0x0000b178, 0x055f0540}, {0x0000b17c, 0x057f0560}, {0x0000b180, 0x06400641}, {0x0000b184, 0x0660065f}, {0x0000b188, 0x067e067f}, {0x0000b18c, 0x07410742}, {0x0000b190, 0x075f0740}, {0x0000b194, 0x077f0760}, {0x0000b198, 0x07800781}, {0x0000b19c, 0x07a0079f}, {0x0000b1a0, 0x07c107bf}, {0x0000b1a4, 0x000007c0}, {0x0000b1a8, 0x00000000}, {0x0000b1ac, 0x00000000}, {0x0000b1b0, 0x00000000}, {0x0000b1b4, 0x00000000}, {0x0000b1b8, 0x00000000}, {0x0000b1bc, 0x00000000}, {0x0000b1c0, 0x00000000}, {0x0000b1c4, 0x00000000}, {0x0000b1c8, 0x00000000}, {0x0000b1cc, 0x00000000}, {0x0000b1d0, 0x00000000}, {0x0000b1d4, 0x00000000}, {0x0000b1d8, 0x00000000}, {0x0000b1dc, 0x00000000}, {0x0000b1e0, 0x00000000}, {0x0000b1e4, 0x00000000}, {0x0000b1e8, 0x00000000}, {0x0000b1ec, 0x00000000}, {0x0000b1f0, 0x00000396}, {0x0000b1f4, 0x00000396}, {0x0000b1f8, 0x00000396}, {0x0000b1fc, 0x00000196}, }; static const u32 ar9300_2p2_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, {0x00007008, 0x00000000}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00007048, 0x00000008}, }; static const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x0821265e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; static const u32 ar9300PciePhy_clkreq_enable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x08253e5e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; static const u32 ar9300PciePhy_clkreq_disable_L1_2p2[][2] = { /* Addr allmodes */ {0x00004040, 0x08213e5e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; #endif /* INITVALS_9003_2P2_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9002_phy.h0000644000175000017500000005610712026211315024234 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AR9002_PHY_H #define AR9002_PHY_H #define AR_PHY_TEST 0x9800 #define PHY_AGC_CLR 0x10000000 #define RFSILENT_BB 0x00002000 #define AR_PHY_TURBO 0x9804 #define AR_PHY_FC_TURBO_MODE 0x00000001 #define AR_PHY_FC_TURBO_SHORT 0x00000002 #define AR_PHY_FC_DYN2040_EN 0x00000004 #define AR_PHY_FC_DYN2040_PRI_ONLY 0x00000008 #define AR_PHY_FC_DYN2040_PRI_CH 0x00000010 /* For 25 MHz channel spacing -- not used but supported by hw */ #define AR_PHY_FC_DYN2040_EXT_CH 0x00000020 #define AR_PHY_FC_HT_EN 0x00000040 #define AR_PHY_FC_SHORT_GI_40 0x00000080 #define AR_PHY_FC_WALSH 0x00000100 #define AR_PHY_FC_SINGLE_HT_LTF1 0x00000200 #define AR_PHY_FC_ENABLE_DAC_FIFO 0x00000800 #define AR_PHY_TEST2 0x9808 #define AR_PHY_TIMING2 0x9810 #define AR_PHY_TIMING3 0x9814 #define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000 #define AR_PHY_TIMING3_DSC_MAN_S 17 #define AR_PHY_TIMING3_DSC_EXP 0x0001E000 #define AR_PHY_TIMING3_DSC_EXP_S 13 #define AR_PHY_CHIP_ID_REV_0 0x80 #define AR_PHY_CHIP_ID_REV_1 0x81 #define AR_PHY_CHIP_ID_9160_REV_0 0xb0 #define AR_PHY_ACTIVE 0x981C #define AR_PHY_ACTIVE_EN 0x00000001 #define AR_PHY_ACTIVE_DIS 0x00000000 #define AR_PHY_RF_CTL2 0x9824 #define AR_PHY_TX_END_DATA_START 0x000000FF #define AR_PHY_TX_END_DATA_START_S 0 #define AR_PHY_TX_END_PA_ON 0x0000FF00 #define AR_PHY_TX_END_PA_ON_S 8 #define AR_PHY_RF_CTL3 0x9828 #define AR_PHY_TX_END_TO_A2_RX_ON 0x00FF0000 #define AR_PHY_TX_END_TO_A2_RX_ON_S 16 #define AR_PHY_TX_END_TO_ADC_ON 0xFF000000 #define AR_PHY_TX_END_TO_ADC_ON_S 24 #define AR_PHY_ADC_CTL 0x982C #define AR_PHY_ADC_CTL_OFF_INBUFGAIN 0x00000003 #define AR_PHY_ADC_CTL_OFF_INBUFGAIN_S 0 #define AR_PHY_ADC_CTL_OFF_PWDDAC 0x00002000 #define AR_PHY_ADC_CTL_OFF_PWDBANDGAP 0x00004000 #define AR_PHY_ADC_CTL_OFF_PWDADC 0x00008000 #define AR_PHY_ADC_CTL_ON_INBUFGAIN 0x00030000 #define AR_PHY_ADC_CTL_ON_INBUFGAIN_S 16 #define AR_PHY_ADC_SERIAL_CTL 0x9830 #define AR_PHY_SEL_INTERNAL_ADDAC 0x00000000 #define AR_PHY_SEL_EXTERNAL_RADIO 0x00000001 #define AR_PHY_RF_CTL4 0x9834 #define AR_PHY_RF_CTL4_TX_END_XPAB_OFF 0xFF000000 #define AR_PHY_RF_CTL4_TX_END_XPAB_OFF_S 24 #define AR_PHY_RF_CTL4_TX_END_XPAA_OFF 0x00FF0000 #define AR_PHY_RF_CTL4_TX_END_XPAA_OFF_S 16 #define AR_PHY_RF_CTL4_FRAME_XPAB_ON 0x0000FF00 #define AR_PHY_RF_CTL4_FRAME_XPAB_ON_S 8 #define AR_PHY_RF_CTL4_FRAME_XPAA_ON 0x000000FF #define AR_PHY_RF_CTL4_FRAME_XPAA_ON_S 0 #define AR_PHY_TSTDAC_CONST 0x983c #define AR_PHY_SETTLING 0x9844 #define AR_PHY_SETTLING_SWITCH 0x00003F80 #define AR_PHY_SETTLING_SWITCH_S 7 #define AR_PHY_RXGAIN 0x9848 #define AR_PHY_RXGAIN_TXRX_ATTEN 0x0003F000 #define AR_PHY_RXGAIN_TXRX_ATTEN_S 12 #define AR_PHY_RXGAIN_TXRX_RF_MAX 0x007C0000 #define AR_PHY_RXGAIN_TXRX_RF_MAX_S 18 #define AR9280_PHY_RXGAIN_TXRX_ATTEN 0x00003F80 #define AR9280_PHY_RXGAIN_TXRX_ATTEN_S 7 #define AR9280_PHY_RXGAIN_TXRX_MARGIN 0x001FC000 #define AR9280_PHY_RXGAIN_TXRX_MARGIN_S 14 #define AR_PHY_DESIRED_SZ 0x9850 #define AR_PHY_DESIRED_SZ_ADC 0x000000FF #define AR_PHY_DESIRED_SZ_ADC_S 0 #define AR_PHY_DESIRED_SZ_PGA 0x0000FF00 #define AR_PHY_DESIRED_SZ_PGA_S 8 #define AR_PHY_DESIRED_SZ_TOT_DES 0x0FF00000 #define AR_PHY_DESIRED_SZ_TOT_DES_S 20 #define AR_PHY_FIND_SIG 0x9858 #define AR_PHY_FIND_SIG_FIRSTEP 0x0003F000 #define AR_PHY_FIND_SIG_FIRSTEP_S 12 #define AR_PHY_FIND_SIG_FIRPWR 0x03FC0000 #define AR_PHY_FIND_SIG_FIRPWR_S 18 #define AR_PHY_FIND_SIG_LOW 0x9840 #define AR_PHY_FIND_SIG_FIRSTEP_LOW 0x00000FC0L #define AR_PHY_FIND_SIG_FIRSTEP_LOW_S 6 #define AR_PHY_AGC_CTL1 0x985C #define AR_PHY_AGC_CTL1_COARSE_LOW 0x00007F80 #define AR_PHY_AGC_CTL1_COARSE_LOW_S 7 #define AR_PHY_AGC_CTL1_COARSE_HIGH 0x003F8000 #define AR_PHY_AGC_CTL1_COARSE_HIGH_S 15 #define AR_PHY_CCA 0x9864 #define AR_PHY_MINCCA_PWR 0x0FF80000 #define AR_PHY_MINCCA_PWR_S 19 #define AR_PHY_CCA_THRESH62 0x0007F000 #define AR_PHY_CCA_THRESH62_S 12 #define AR9280_PHY_MINCCA_PWR 0x1FF00000 #define AR9280_PHY_MINCCA_PWR_S 20 #define AR9280_PHY_CCA_THRESH62 0x000FF000 #define AR9280_PHY_CCA_THRESH62_S 12 #define AR_PHY_SFCORR_LOW 0x986C #define AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001 #define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW 0x00003F00 #define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8 #define AR_PHY_SFCORR_LOW_M1_THRESH_LOW 0x001FC000 #define AR_PHY_SFCORR_LOW_M1_THRESH_LOW_S 14 #define AR_PHY_SFCORR_LOW_M2_THRESH_LOW 0x0FE00000 #define AR_PHY_SFCORR_LOW_M2_THRESH_LOW_S 21 #define AR_PHY_SFCORR 0x9868 #define AR_PHY_SFCORR_M2COUNT_THR 0x0000001F #define AR_PHY_SFCORR_M2COUNT_THR_S 0 #define AR_PHY_SFCORR_M1_THRESH 0x00FE0000 #define AR_PHY_SFCORR_M1_THRESH_S 17 #define AR_PHY_SFCORR_M2_THRESH 0x7F000000 #define AR_PHY_SFCORR_M2_THRESH_S 24 #define AR_PHY_SLEEP_CTR_CONTROL 0x9870 #define AR_PHY_SLEEP_CTR_LIMIT 0x9874 #define AR_PHY_SYNTH_CONTROL 0x9874 #define AR_PHY_SLEEP_SCAL 0x9878 #define AR_PHY_PLL_CTL 0x987c #define AR_PHY_PLL_CTL_40 0xaa #define AR_PHY_PLL_CTL_40_5413 0x04 #define AR_PHY_PLL_CTL_44 0xab #define AR_PHY_PLL_CTL_44_2133 0xeb #define AR_PHY_PLL_CTL_40_2133 0xea #define AR_PHY_SPECTRAL_SCAN 0x9910 /* AR9280 spectral scan configuration register */ #define AR_PHY_SPECTRAL_SCAN_ENABLE 0x1 #define AR_PHY_SPECTRAL_SCAN_ENA 0x00000001 /* Enable spectral scan, reg 68, bit 0 */ #define AR_PHY_SPECTRAL_SCAN_ENA_S 0 /* Enable spectral scan, reg 68, bit 0 */ #define AR_PHY_SPECTRAL_SCAN_ACTIVE 0x00000002 /* Activate spectral scan reg 68, bit 1*/ #define AR_PHY_SPECTRAL_SCAN_ACTIVE_S 1 /* Activate spectral scan reg 68, bit 1*/ #define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD 0x000000F0 /* Interval for FFT reports, reg 68, bits 4-7*/ #define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S 4 #define AR_PHY_SPECTRAL_SCAN_PERIOD 0x0000FF00 /* Interval for FFT reports, reg 68, bits 8-15*/ #define AR_PHY_SPECTRAL_SCAN_PERIOD_S 8 #define AR_PHY_SPECTRAL_SCAN_COUNT 0x00FF0000 /* Number of reports, reg 68, bits 16-23*/ #define AR_PHY_SPECTRAL_SCAN_COUNT_S 16 #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x01000000 /* Short repeat, reg 68, bit 24*/ #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24 /* Short repeat, reg 68, bit 24*/ #define AR_PHY_RX_DELAY 0x9914 #define AR_PHY_SEARCH_START_DELAY 0x9918 #define AR_PHY_RX_DELAY_DELAY 0x00003FFF #define AR_PHY_TIMING_CTRL4(_i) (0x9920 + ((_i) << 12)) #define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01F #define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0 #define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7E0 #define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5 #define AR_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800 #define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xF000 #define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12 #define AR_PHY_TIMING_CTRL4_DO_CAL 0x10000 #define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI 0x80000000 #define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER 0x40000000 #define AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK 0x20000000 #define AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK 0x10000000 #define AR_PHY_TIMING5 0x9924 #define AR_PHY_TIMING5_CYCPWR_THR1 0x000000FE #define AR_PHY_TIMING5_CYCPWR_THR1_S 1 #define AR_PHY_POWER_TX_RATE1 0x9934 #define AR_PHY_POWER_TX_RATE2 0x9938 #define AR_PHY_POWER_TX_RATE_MAX 0x993c #define AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040 #define AR_PHY_FRAME_CTL 0x9944 #define AR_PHY_FRAME_CTL_TX_CLIP 0x00000038 #define AR_PHY_FRAME_CTL_TX_CLIP_S 3 #define AR_PHY_TXPWRADJ 0x994C #define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA 0x00000FC0 #define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA_S 6 #define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX 0x00FC0000 #define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX_S 18 #define AR_PHY_RADAR_EXT 0x9940 #define AR_PHY_RADAR_EXT_ENA 0x00004000 #define AR_PHY_RADAR_0 0x9954 #define AR_PHY_RADAR_0_ENA 0x00000001 #define AR_PHY_RADAR_0_FFT_ENA 0x80000000 #define AR_PHY_RADAR_0_INBAND 0x0000003e #define AR_PHY_RADAR_0_INBAND_S 1 #define AR_PHY_RADAR_0_PRSSI 0x00000FC0 #define AR_PHY_RADAR_0_PRSSI_S 6 #define AR_PHY_RADAR_0_HEIGHT 0x0003F000 #define AR_PHY_RADAR_0_HEIGHT_S 12 #define AR_PHY_RADAR_0_RRSSI 0x00FC0000 #define AR_PHY_RADAR_0_RRSSI_S 18 #define AR_PHY_RADAR_0_FIRPWR 0x7F000000 #define AR_PHY_RADAR_0_FIRPWR_S 24 #define AR_PHY_RADAR_1 0x9958 #define AR_PHY_RADAR_1_RELPWR_ENA 0x00800000 #define AR_PHY_RADAR_1_USE_FIR128 0x00400000 #define AR_PHY_RADAR_1_RELPWR_THRESH 0x003F0000 #define AR_PHY_RADAR_1_RELPWR_THRESH_S 16 #define AR_PHY_RADAR_1_BLOCK_CHECK 0x00008000 #define AR_PHY_RADAR_1_MAX_RRSSI 0x00004000 #define AR_PHY_RADAR_1_RELSTEP_CHECK 0x00002000 #define AR_PHY_RADAR_1_RELSTEP_THRESH 0x00001F00 #define AR_PHY_RADAR_1_RELSTEP_THRESH_S 8 #define AR_PHY_RADAR_1_MAXLEN 0x000000FF #define AR_PHY_RADAR_1_MAXLEN_S 0 #define AR_PHY_SWITCH_CHAIN_0 0x9960 #define AR_PHY_SWITCH_COM 0x9964 #define AR_PHY_SIGMA_DELTA 0x996C #define AR_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 #define AR_PHY_SIGMA_DELTA_ADC_SEL_S 0 #define AR_PHY_SIGMA_DELTA_FILT2 0x000000F8 #define AR_PHY_SIGMA_DELTA_FILT2_S 3 #define AR_PHY_SIGMA_DELTA_FILT1 0x00001F00 #define AR_PHY_SIGMA_DELTA_FILT1_S 8 #define AR_PHY_SIGMA_DELTA_ADC_CLIP 0x01FFE000 #define AR_PHY_SIGMA_DELTA_ADC_CLIP_S 13 #define AR_PHY_RESTART 0x9970 #define AR_PHY_RESTART_DIV_GC 0x001C0000 #define AR_PHY_RESTART_DIV_GC_S 18 #define AR_PHY_RFBUS_REQ 0x997C #define AR_PHY_RFBUS_REQ_EN 0x00000001 #define AR_PHY_TIMING7 0x9980 #define AR_PHY_TIMING8 0x9984 #define AR_PHY_TIMING8_PILOT_MASK_2 0x000FFFFF #define AR_PHY_TIMING8_PILOT_MASK_2_S 0 #define AR_PHY_BIN_MASK2_1 0x9988 #define AR_PHY_BIN_MASK2_2 0x998c #define AR_PHY_BIN_MASK2_3 0x9990 #define AR_PHY_BIN_MASK2_4 0x9994 #define AR_PHY_BIN_MASK_1 0x9900 #define AR_PHY_BIN_MASK_2 0x9904 #define AR_PHY_BIN_MASK_3 0x9908 #define AR_PHY_MASK_CTL 0x990c #define AR_PHY_BIN_MASK2_4_MASK_4 0x00003FFF #define AR_PHY_BIN_MASK2_4_MASK_4_S 0 #define AR_PHY_TIMING9 0x9998 #define AR_PHY_TIMING10 0x999c #define AR_PHY_TIMING10_PILOT_MASK_2 0x000FFFFF #define AR_PHY_TIMING10_PILOT_MASK_2_S 0 #define AR_PHY_TIMING11 0x99a0 #define AR_PHY_TIMING11_SPUR_DELTA_PHASE 0x000FFFFF #define AR_PHY_TIMING11_SPUR_DELTA_PHASE_S 0 #define AR_PHY_TIMING11_USE_SPUR_IN_AGC 0x40000000 #define AR_PHY_TIMING11_USE_SPUR_IN_SELFCOR 0x80000000 #define AR_PHY_RX_CHAINMASK 0x99a4 #define AR_PHY_NEW_ADC_DC_GAIN_CORR(_i) (0x99b4 + ((_i) << 12)) #define AR_PHY_NEW_ADC_GAIN_CORR_ENABLE 0x40000000 #define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000 #define AR_PHY_MULTICHAIN_GAIN_CTL 0x99ac #define AR_PHY_9285_FAST_DIV_BIAS 0x00007E00 #define AR_PHY_9285_FAST_DIV_BIAS_S 9 #define AR_PHY_9285_ANT_DIV_CTL_ALL 0x7f000000 #define AR_PHY_9285_ANT_DIV_CTL 0x01000000 #define AR_PHY_9285_ANT_DIV_CTL_S 24 #define AR_PHY_9285_ANT_DIV_ALT_LNACONF 0x06000000 #define AR_PHY_9285_ANT_DIV_ALT_LNACONF_S 25 #define AR_PHY_9285_ANT_DIV_MAIN_LNACONF 0x18000000 #define AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S 27 #define AR_PHY_9285_ANT_DIV_ALT_GAINTB 0x20000000 #define AR_PHY_9285_ANT_DIV_ALT_GAINTB_S 29 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB 0x40000000 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB_S 30 #define AR_PHY_9285_ANT_DIV_LNA1 2 #define AR_PHY_9285_ANT_DIV_LNA2 1 #define AR_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2 3 #define AR_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0 #define AR_PHY_9285_ANT_DIV_GAINTB_0 0 #define AR_PHY_9285_ANT_DIV_GAINTB_1 1 #define AR_PHY_EXT_CCA0 0x99b8 #define AR_PHY_EXT_CCA0_THRESH62 0x000000FF #define AR_PHY_EXT_CCA0_THRESH62_S 0 #define AR_PHY_EXT_CCA 0x99bc #define AR_PHY_EXT_CCA_CYCPWR_THR1 0x0000FE00 #define AR_PHY_EXT_CCA_CYCPWR_THR1_S 9 #define AR_PHY_EXT_CCA_THRESH62 0x007F0000 #define AR_PHY_EXT_CCA_THRESH62_S 16 #define AR_PHY_EXT_TIMING5_CYCPWR_THR1 0x0000FE00L #define AR_PHY_EXT_TIMING5_CYCPWR_THR1_S 9 #define AR_PHY_EXT_MINCCA_PWR 0xFF800000 #define AR_PHY_EXT_MINCCA_PWR_S 23 #define AR9280_PHY_EXT_MINCCA_PWR 0x01FF0000 #define AR9280_PHY_EXT_MINCCA_PWR_S 16 #define AR_PHY_SFCORR_EXT 0x99c0 #define AR_PHY_SFCORR_EXT_M1_THRESH 0x0000007F #define AR_PHY_SFCORR_EXT_M1_THRESH_S 0 #define AR_PHY_SFCORR_EXT_M2_THRESH 0x00003F80 #define AR_PHY_SFCORR_EXT_M2_THRESH_S 7 #define AR_PHY_SFCORR_EXT_M1_THRESH_LOW 0x001FC000 #define AR_PHY_SFCORR_EXT_M1_THRESH_LOW_S 14 #define AR_PHY_SFCORR_EXT_M2_THRESH_LOW 0x0FE00000 #define AR_PHY_SFCORR_EXT_M2_THRESH_LOW_S 21 #define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28 #define AR_PHY_HALFGI 0x99D0 #define AR_PHY_HALFGI_DSC_MAN 0x0007FFF0 #define AR_PHY_HALFGI_DSC_MAN_S 4 #define AR_PHY_HALFGI_DSC_EXP 0x0000000F #define AR_PHY_HALFGI_DSC_EXP_S 0 #define AR_PHY_CHAN_INFO_MEMORY 0x99DC #define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001 #define AR_PHY_HEAVY_CLIP_ENABLE 0x99E0 #define AR_PHY_HEAVY_CLIP_FACTOR_RIFS 0x99EC #define AR_PHY_RIFS_INIT_DELAY 0x03ff0000 #define AR_PHY_M_SLEEP 0x99f0 #define AR_PHY_REFCLKDLY 0x99f4 #define AR_PHY_REFCLKPD 0x99f8 #define AR_PHY_CALMODE 0x99f0 #define AR_PHY_CALMODE_IQ 0x00000000 #define AR_PHY_CALMODE_ADC_GAIN 0x00000001 #define AR_PHY_CALMODE_ADC_DC_PER 0x00000002 #define AR_PHY_CALMODE_ADC_DC_INIT 0x00000003 #define AR_PHY_CAL_MEAS_0(_i) (0x9c10 + ((_i) << 12)) #define AR_PHY_CAL_MEAS_1(_i) (0x9c14 + ((_i) << 12)) #define AR_PHY_CAL_MEAS_2(_i) (0x9c18 + ((_i) << 12)) #define AR_PHY_CAL_MEAS_3(_i) (0x9c1c + ((_i) << 12)) #define AR_PHY_CURRENT_RSSI 0x9c1c #define AR9280_PHY_CURRENT_RSSI 0x9c3c #define AR_PHY_RFBUS_GRANT 0x9C20 #define AR_PHY_RFBUS_GRANT_EN 0x00000001 #define AR_PHY_CHAN_INFO_GAIN_DIFF 0x9CF4 #define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320 #define AR_PHY_CHAN_INFO_GAIN 0x9CFC #define AR_PHY_MODE 0xA200 #define AR_PHY_MODE_ASYNCFIFO 0x80 #define AR_PHY_MODE_AR2133 0x08 #define AR_PHY_MODE_AR5111 0x00 #define AR_PHY_MODE_AR5112 0x08 #define AR_PHY_MODE_DYNAMIC 0x04 #define AR_PHY_MODE_RF2GHZ 0x02 #define AR_PHY_MODE_RF5GHZ 0x00 #define AR_PHY_MODE_CCK 0x01 #define AR_PHY_MODE_OFDM 0x00 #define AR_PHY_MODE_DYN_CCK_DISABLE 0x100 #define AR_PHY_CCK_TX_CTRL 0xA204 #define AR_PHY_CCK_TX_CTRL_JAPAN 0x00000010 #define AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK 0x0000000C #define AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK_S 2 #define AR_PHY_CCK_DETECT 0xA208 #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003F #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 /* [12:6] settling time for antenna switch */ #define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001FC0 #define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME_S 6 #define AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000 #define AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV_S 13 #define AR_PHY_GAIN_2GHZ 0xA20C #define AR_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00FC0000 #define AR_PHY_GAIN_2GHZ_RXTX_MARGIN_S 18 #define AR_PHY_GAIN_2GHZ_BSW_MARGIN 0x00003C00 #define AR_PHY_GAIN_2GHZ_BSW_MARGIN_S 10 #define AR_PHY_GAIN_2GHZ_BSW_ATTEN 0x0000001F #define AR_PHY_GAIN_2GHZ_BSW_ATTEN_S 0 #define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN 0x003E0000 #define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S 17 #define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN 0x0001F000 #define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S 12 #define AR_PHY_GAIN_2GHZ_XATTEN2_DB 0x00000FC0 #define AR_PHY_GAIN_2GHZ_XATTEN2_DB_S 6 #define AR_PHY_GAIN_2GHZ_XATTEN1_DB 0x0000003F #define AR_PHY_GAIN_2GHZ_XATTEN1_DB_S 0 #define AR_PHY_CCK_RXCTRL4 0xA21C #define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT 0x01F80000 #define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S 19 #define AR_PHY_DAG_CTRLCCK 0xA228 #define AR_PHY_DAG_CTRLCCK_EN_RSSI_THR 0x00000200 #define AR_PHY_DAG_CTRLCCK_RSSI_THR 0x0001FC00 #define AR_PHY_DAG_CTRLCCK_RSSI_THR_S 10 #define AR_PHY_FORCE_CLKEN_CCK 0xA22C #define AR_PHY_FORCE_CLKEN_CCK_MRC_MUX 0x00000040 #define AR_PHY_POWER_TX_RATE3 0xA234 #define AR_PHY_POWER_TX_RATE4 0xA238 #define AR_PHY_SCRM_SEQ_XR 0xA23C #define AR_PHY_HEADER_DETECT_XR 0xA240 #define AR_PHY_CHIRP_DETECTED_XR 0xA244 #define AR_PHY_BLUETOOTH 0xA254 #define AR_PHY_TPCRG1 0xA258 #define AR_PHY_TPCRG1_NUM_PD_GAIN 0x0000c000 #define AR_PHY_TPCRG1_NUM_PD_GAIN_S 14 #define AR_PHY_TPCRG1_PD_GAIN_1 0x00030000 #define AR_PHY_TPCRG1_PD_GAIN_1_S 16 #define AR_PHY_TPCRG1_PD_GAIN_2 0x000C0000 #define AR_PHY_TPCRG1_PD_GAIN_2_S 18 #define AR_PHY_TPCRG1_PD_GAIN_3 0x00300000 #define AR_PHY_TPCRG1_PD_GAIN_3_S 20 #define AR_PHY_TPCRG1_PD_CAL_ENABLE 0x00400000 #define AR_PHY_TPCRG1_PD_CAL_ENABLE_S 22 #define AR_PHY_TX_PWRCTRL4 0xa264 #define AR_PHY_TX_PWRCTRL_PD_AVG_VALID 0x00000001 #define AR_PHY_TX_PWRCTRL_PD_AVG_VALID_S 0 #define AR_PHY_TX_PWRCTRL_PD_AVG_OUT 0x000001FE #define AR_PHY_TX_PWRCTRL_PD_AVG_OUT_S 1 #define AR_PHY_TX_PWRCTRL6_0 0xa270 #define AR_PHY_TX_PWRCTRL6_1 0xb270 #define AR_PHY_TX_PWRCTRL_ERR_EST_MODE 0x03000000 #define AR_PHY_TX_PWRCTRL_ERR_EST_MODE_S 24 #define AR_PHY_TX_PWRCTRL7 0xa274 #define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN 0x01F80000 #define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN_S 19 #define AR_PHY_TX_PWRCTRL8 0xa278 #define AR_PHY_TX_PWRCTRL9 0xa27C #define AR_PHY_TX_PWRCTRL10 0xa394 #define AR_PHY_TX_DESIRED_SCALE_CCK 0x00007C00 #define AR_PHY_TX_DESIRED_SCALE_CCK_S 10 #define AR_PHY_TX_PWRCTRL9_RES_DC_REMOVAL 0x80000000 #define AR_PHY_TX_PWRCTRL9_RES_DC_REMOVAL_S 31 #define AR_PHY_TX_GAIN_TBL1 0xa300 #define AR_PHY_TX_GAIN 0x0007F000 #define AR_PHY_TX_GAIN_S 12 #define AR_PHY_CH0_TX_PWRCTRL11 0xa398 #define AR_PHY_CH1_TX_PWRCTRL11 0xb398 #define AR_PHY_CH0_TX_PWRCTRL12 0xa3dc #define AR_PHY_CH0_TX_PWRCTRL13 0xa3e0 #define AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP 0x0000FC00 #define AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP_S 10 #define AR_PHY_VIT_MASK2_M_46_61 0xa3a0 #define AR_PHY_MASK2_M_31_45 0xa3a4 #define AR_PHY_MASK2_M_16_30 0xa3a8 #define AR_PHY_MASK2_M_00_15 0xa3ac #define AR_PHY_MASK2_P_15_01 0xa3b8 #define AR_PHY_MASK2_P_30_16 0xa3bc #define AR_PHY_MASK2_P_45_31 0xa3c0 #define AR_PHY_MASK2_P_61_45 0xa3c4 #define AR_PHY_SPUR_REG 0x994c #define AR_PHY_SPUR_REG_MASK_RATE_CNTL (0xFF << 18) #define AR_PHY_SPUR_REG_MASK_RATE_CNTL_S 18 #define AR_PHY_SPUR_REG_ENABLE_MASK_PPM 0x20000 #define AR_PHY_SPUR_REG_MASK_RATE_SELECT (0xFF << 9) #define AR_PHY_SPUR_REG_MASK_RATE_SELECT_S 9 #define AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI 0x100 #define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH 0x7F #define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH_S 0 #define AR_PHY_PILOT_MASK_01_30 0xa3b0 #define AR_PHY_PILOT_MASK_31_60 0xa3b4 #define AR_PHY_CHANNEL_MASK_01_30 0x99d4 #define AR_PHY_CHANNEL_MASK_31_60 0x99d8 #define AR_PHY_ANALOG_SWAP 0xa268 #define AR_PHY_SWAP_ALT_CHAIN 0x00000040 #define AR_PHY_TPCRG5 0xA26C #define AR_PHY_TPCRG5_PD_GAIN_OVERLAP 0x0000000F #define AR_PHY_TPCRG5_PD_GAIN_OVERLAP_S 0 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1 0x000003F0 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S 4 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2 0x0000FC00 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S 10 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3 0x003F0000 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S 16 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4 0x0FC00000 #define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S 22 /* Carrier leak calibration control, do it after AGC calibration */ #define AR_PHY_CL_CAL_CTL 0xA358 #define AR_PHY_CL_CAL_ENABLE 0x00000002 #define AR_PHY_PARALLEL_CAL_ENABLE 0x00000001 #define AR_PHY_POWER_TX_RATE5 0xA38C #define AR_PHY_POWER_TX_RATE6 0xA390 #define AR_PHY_CAL_CHAINMASK 0xA39C #define AR_PHY_POWER_TX_SUB 0xA3C8 #define AR_PHY_POWER_TX_RATE7 0xA3CC #define AR_PHY_POWER_TX_RATE8 0xA3D0 #define AR_PHY_POWER_TX_RATE9 0xA3D4 #define AR_PHY_XPA_CFG 0xA3D8 #define AR_PHY_FORCE_XPA_CFG 0x000000001 #define AR_PHY_FORCE_XPA_CFG_S 0 #define AR_PHY_CH1_CCA 0xa864 #define AR_PHY_CH1_MINCCA_PWR 0x0FF80000 #define AR_PHY_CH1_MINCCA_PWR_S 19 #define AR9280_PHY_CH1_MINCCA_PWR 0x1FF00000 #define AR9280_PHY_CH1_MINCCA_PWR_S 20 #define AR_PHY_CH2_CCA 0xb864 #define AR_PHY_CH2_MINCCA_PWR 0x0FF80000 #define AR_PHY_CH2_MINCCA_PWR_S 19 #define AR_PHY_CH1_EXT_CCA 0xa9bc #define AR_PHY_CH1_EXT_MINCCA_PWR 0xFF800000 #define AR_PHY_CH1_EXT_MINCCA_PWR_S 23 #define AR9280_PHY_CH1_EXT_MINCCA_PWR 0x01FF0000 #define AR9280_PHY_CH1_EXT_MINCCA_PWR_S 16 #define AR_PHY_CH2_EXT_CCA 0xb9bc #define AR_PHY_CH2_EXT_MINCCA_PWR 0xFF800000 #define AR_PHY_CH2_EXT_MINCCA_PWR_S 23 #define AR_PHY_CCA_NOM_VAL_5416_2GHZ -90 #define AR_PHY_CCA_NOM_VAL_5416_5GHZ -100 #define AR_PHY_CCA_MIN_GOOD_VAL_5416_2GHZ -100 #define AR_PHY_CCA_MIN_GOOD_VAL_5416_5GHZ -110 #define AR_PHY_CCA_MAX_GOOD_VAL_5416_2GHZ -80 #define AR_PHY_CCA_MAX_GOOD_VAL_5416_5GHZ -90 #define AR_PHY_CCA_NOM_VAL_9280_2GHZ -112 #define AR_PHY_CCA_NOM_VAL_9280_5GHZ -112 #define AR_PHY_CCA_MIN_GOOD_VAL_9280_2GHZ -127 #define AR_PHY_CCA_MIN_GOOD_VAL_9280_5GHZ -122 #define AR_PHY_CCA_MAX_GOOD_VAL_9280_2GHZ -97 #define AR_PHY_CCA_MAX_GOOD_VAL_9280_5GHZ -102 #define AR_PHY_CCA_NOM_VAL_9285_2GHZ -118 #define AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ -127 #define AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ -108 #define AR_PHY_CCA_NOM_VAL_9271_2GHZ -118 #define AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ -127 #define AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ -116 #define AR_PHY_CCA_NOM_VAL_9287_2GHZ -120 #define AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ -127 #define AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ -110 #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9002_phy.c0000644000175000017500000004160112026211315024220 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: Programming Atheros 802.11n analog front end radios * * AR5416 MAC based PCI devices and AR518 MAC based PCI-Express * devices have either an external AR2133 analog front end radio for single * band 2.4 GHz communication or an AR5133 analog front end radio for dual * band 2.4 GHz / 5 GHz communication. * * All devices after the AR5416 and AR5418 family starting with the AR9280 * have their analog front radios, MAC/BB and host PCIe/USB interface embedded * into a single-chip and require less programming. * * The following single-chips exist with a respective embedded radio: * * AR9280 - 11n dual-band 2x2 MIMO for PCIe * AR9281 - 11n single-band 1x2 MIMO for PCIe * AR9285 - 11n single-band 1x1 for PCIe * AR9287 - 11n single-band 2x2 MIMO for PCIe * * AR9220 - 11n dual-band 2x2 MIMO for PCI * AR9223 - 11n single-band 2x2 MIMO for PCI * * AR9287 - 11n single-band 1x1 MIMO for USB */ #include "hw.h" #include "ar9002_phy.h" /** * ar9002_hw_set_channel - set channel on single-chip device * @ah: atheros hardware structure * @chan: * * This is the function to change channel on single-chip devices, that is * all devices after ar9280. * * This function takes the channel value in MHz and sets * hardware channel value. Assumes writes have been enabled to analog bus. * * Actual Expression, * * For 2GHz channel, * Channel Frequency = (3/4) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17) * (freq_ref = 40MHz) * * For 5GHz channel, * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^10) * (freq_ref = 40MHz/(24>>amodeRefSel)) */ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) { u16 bMode, fracMode, aModeRefSel = 0; u32 freq, ndiv, channelSel = 0, channelFrac = 0, reg32 = 0; struct chan_centers centers; u32 refDivA = 24; ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; reg32 = REG_READ(ah, AR_PHY_SYNTH_CONTROL); reg32 &= 0xc0000000; if (freq < 4800) { /* 2 GHz, fractional mode */ u32 txctl; int regWrites = 0; bMode = 1; fracMode = 1; aModeRefSel = 0; channelSel = CHANSEL_2G(freq); if (AR_SREV_9287_11_OR_LATER(ah)) { if (freq == 2484) { /* Enable channel spreading for channel 14 */ REG_WRITE_ARRAY(&ah->iniCckfirJapan2484, 1, regWrites); } else { REG_WRITE_ARRAY(&ah->iniCckfirNormal, 1, regWrites); } } else { txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); if (freq == 2484) { /* Enable channel spreading for channel 14 */ REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl | AR_PHY_CCK_TX_CTRL_JAPAN); } else { REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); } } } else { bMode = 0; fracMode = 0; switch (ah->eep_ops->get_eeprom(ah, EEP_FRAC_N_5G)) { case 0: if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) aModeRefSel = 0; else if ((freq % 20) == 0) aModeRefSel = 3; else if ((freq % 10) == 0) aModeRefSel = 2; if (aModeRefSel) break; case 1: default: aModeRefSel = 0; /* * Enable 2G (fractional) mode for channels * which are 5MHz spaced. */ fracMode = 1; refDivA = 1; channelSel = CHANSEL_5G(freq); /* RefDivA setting */ ath9k_hw_analog_shift_rmw(ah, AR_AN_SYNTH9, AR_AN_SYNTH9_REFDIVA, AR_AN_SYNTH9_REFDIVA_S, refDivA); } if (!fracMode) { ndiv = (freq * (refDivA >> aModeRefSel)) / 60; channelSel = ndiv & 0x1ff; channelFrac = (ndiv & 0xfffffe00) * 2; channelSel = (channelSel << 17) | channelFrac; } } reg32 = reg32 | (bMode << 29) | (fracMode << 28) | (aModeRefSel << 26) | (channelSel); REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32); ah->curchan = chan; return 0; } /** * ar9002_hw_spur_mitigate - convert baseband spur frequency * @ah: atheros hardware structure * @chan: * * For single-chip solutions. Converts to baseband spur frequency given the * input channel frequency and compute register settings below. */ static void ar9002_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan) { int bb_spur = AR_NO_SPUR; int freq; int bin, cur_bin; int bb_spur_off, spur_subchannel_sd; int spur_freq_sd; int spur_delta_phase; int denominator; int upper, lower, cur_vit_mask; int tmp, newVal; int i; static const int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 }; static const int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 }; static const int inc[4] = { 0, 100, 0, 0 }; struct chan_centers centers; int8_t mask_m[123]; int8_t mask_p[123]; int8_t mask_amt; int tmp_mask; int cur_bb_spur; bool is2GHz = IS_CHAN_2GHZ(chan); memset(&mask_m, 0, sizeof(int8_t) * 123); memset(&mask_p, 0, sizeof(int8_t) * 123); ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; ah->config.spurmode = SPUR_ENABLE_EEPROM; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); if (AR_NO_SPUR == cur_bb_spur) break; if (is2GHz) cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ; else cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ; cur_bb_spur = cur_bb_spur - freq; if (IS_CHAN_HT40(chan)) { if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) && (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) { bb_spur = cur_bb_spur; break; } } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) && (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) { bb_spur = cur_bb_spur; break; } } if (AR_NO_SPUR == bb_spur) { REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); return; } else { REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); } bin = bb_spur * 320; tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); ENABLE_REGWRITE_BUFFER(ah); newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), newVal); newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | AR_PHY_SPUR_REG_ENABLE_MASK_PPM | AR_PHY_SPUR_REG_MASK_RATE_SELECT | AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); REG_WRITE(ah, AR_PHY_SPUR_REG, newVal); if (IS_CHAN_HT40(chan)) { if (bb_spur < 0) { spur_subchannel_sd = 1; bb_spur_off = bb_spur + 10; } else { spur_subchannel_sd = 0; bb_spur_off = bb_spur - 10; } } else { spur_subchannel_sd = 0; bb_spur_off = bb_spur; } if (IS_CHAN_HT40(chan)) spur_delta_phase = ((bb_spur * 262144) / 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; else spur_delta_phase = ((bb_spur * 524288) / 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; denominator = IS_CHAN_2GHZ(chan) ? 44 : 40; spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff; newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); REG_WRITE(ah, AR_PHY_TIMING11, newVal); newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S; REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal); cur_bin = -6000; upper = bin + 100; lower = bin - 100; for (i = 0; i < 4; i++) { int pilot_mask = 0; int chan_mask = 0; int bp = 0; for (bp = 0; bp < 30; bp++) { if ((cur_bin > lower) && (cur_bin < upper)) { pilot_mask = pilot_mask | 0x1 << bp; chan_mask = chan_mask | 0x1 << bp; } cur_bin += 100; } cur_bin += inc[i]; REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); REG_WRITE(ah, chan_mask_reg[i], chan_mask); } cur_vit_mask = 6100; upper = bin + 120; lower = bin - 120; for (i = 0; i < 123; i++) { if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { /* workaround for gcc bug #37014 */ volatile int tmp_v = abs(cur_vit_mask - bin); if (tmp_v < 75) mask_amt = 1; else mask_amt = 0; if (cur_vit_mask < 0) mask_m[abs(cur_vit_mask / 100)] = mask_amt; else mask_p[cur_vit_mask / 100] = mask_amt; } cur_vit_mask -= 100; } tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) | (mask_m[48] << 26) | (mask_m[49] << 24) | (mask_m[50] << 22) | (mask_m[51] << 20) | (mask_m[52] << 18) | (mask_m[53] << 16) | (mask_m[54] << 14) | (mask_m[55] << 12) | (mask_m[56] << 10) | (mask_m[57] << 8) | (mask_m[58] << 6) | (mask_m[59] << 4) | (mask_m[60] << 2) | (mask_m[61] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); tmp_mask = (mask_m[31] << 28) | (mask_m[32] << 26) | (mask_m[33] << 24) | (mask_m[34] << 22) | (mask_m[35] << 20) | (mask_m[36] << 18) | (mask_m[37] << 16) | (mask_m[48] << 14) | (mask_m[39] << 12) | (mask_m[40] << 10) | (mask_m[41] << 8) | (mask_m[42] << 6) | (mask_m[43] << 4) | (mask_m[44] << 2) | (mask_m[45] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) | (mask_m[18] << 26) | (mask_m[18] << 24) | (mask_m[20] << 22) | (mask_m[20] << 20) | (mask_m[22] << 18) | (mask_m[22] << 16) | (mask_m[24] << 14) | (mask_m[24] << 12) | (mask_m[25] << 10) | (mask_m[26] << 8) | (mask_m[27] << 6) | (mask_m[28] << 4) | (mask_m[29] << 2) | (mask_m[30] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) | (mask_m[2] << 26) | (mask_m[3] << 24) | (mask_m[4] << 22) | (mask_m[5] << 20) | (mask_m[6] << 18) | (mask_m[7] << 16) | (mask_m[8] << 14) | (mask_m[9] << 12) | (mask_m[10] << 10) | (mask_m[11] << 8) | (mask_m[12] << 6) | (mask_m[13] << 4) | (mask_m[14] << 2) | (mask_m[15] << 0); REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); tmp_mask = (mask_p[15] << 28) | (mask_p[14] << 26) | (mask_p[13] << 24) | (mask_p[12] << 22) | (mask_p[11] << 20) | (mask_p[10] << 18) | (mask_p[9] << 16) | (mask_p[8] << 14) | (mask_p[7] << 12) | (mask_p[6] << 10) | (mask_p[5] << 8) | (mask_p[4] << 6) | (mask_p[3] << 4) | (mask_p[2] << 2) | (mask_p[1] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); tmp_mask = (mask_p[30] << 28) | (mask_p[29] << 26) | (mask_p[28] << 24) | (mask_p[27] << 22) | (mask_p[26] << 20) | (mask_p[25] << 18) | (mask_p[24] << 16) | (mask_p[23] << 14) | (mask_p[22] << 12) | (mask_p[21] << 10) | (mask_p[20] << 8) | (mask_p[19] << 6) | (mask_p[18] << 4) | (mask_p[17] << 2) | (mask_p[16] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); tmp_mask = (mask_p[45] << 28) | (mask_p[44] << 26) | (mask_p[43] << 24) | (mask_p[42] << 22) | (mask_p[41] << 20) | (mask_p[40] << 18) | (mask_p[39] << 16) | (mask_p[38] << 14) | (mask_p[37] << 12) | (mask_p[36] << 10) | (mask_p[35] << 8) | (mask_p[34] << 6) | (mask_p[33] << 4) | (mask_p[32] << 2) | (mask_p[31] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) | (mask_p[59] << 26) | (mask_p[58] << 24) | (mask_p[57] << 22) | (mask_p[56] << 20) | (mask_p[55] << 18) | (mask_p[54] << 16) | (mask_p[53] << 14) | (mask_p[52] << 12) | (mask_p[51] << 10) | (mask_p[50] << 8) | (mask_p[49] << 6) | (mask_p[48] << 4) | (mask_p[47] << 2) | (mask_p[46] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); REGWRITE_BUFFER_FLUSH(ah); } static void ar9002_olc_init(struct ath_hw *ah) { u32 i; if (!OLC_FOR_AR9280_20_LATER) return; if (OLC_FOR_AR9287_10_LATER) { REG_SET_BIT(ah, AR_PHY_TX_PWRCTRL9, AR_PHY_TX_PWRCTRL9_RES_DC_REMOVAL); ath9k_hw_analog_shift_rmw(ah, AR9287_AN_TXPC0, AR9287_AN_TXPC0_TXPCMODE, AR9287_AN_TXPC0_TXPCMODE_S, AR9287_AN_TXPC0_TXPCMODE_TEMPSENSE); udelay(100); } else { for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) ah->originalGain[i] = MS(REG_READ(ah, AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN); ah->PDADCdelta = 0; } } static u32 ar9002_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { int ref_div = 5; int pll_div = 0x2c; u32 pll; if (chan && IS_CHAN_5GHZ(chan) && !IS_CHAN_A_FAST_CLOCK(ah, chan)) { if (AR_SREV_9280_20(ah)) { ref_div = 10; pll_div = 0x50; } else { pll_div = 0x28; } } pll = SM(ref_div, AR_RTC_9160_PLL_REFDIV); pll |= SM(pll_div, AR_RTC_9160_PLL_DIV); if (chan && IS_CHAN_HALF_RATE(chan)) pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL); else if (chan && IS_CHAN_QUARTER_RATE(chan)) pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL); return pll; } static void ar9002_hw_do_getnf(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]) { int16_t nf; nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR); nfarray[0] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR9280_PHY_EXT_MINCCA_PWR); if (IS_CHAN_HT40(ah->curchan)) nfarray[3] = sign_extend32(nf, 8); if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) return; nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR); nfarray[1] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR9280_PHY_CH1_EXT_MINCCA_PWR); if (IS_CHAN_HT40(ah->curchan)) nfarray[4] = sign_extend32(nf, 8); } static void ar9002_hw_set_nf_limits(struct ath_hw *ah) { if (AR_SREV_9285(ah)) { ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9285_2GHZ; } else if (AR_SREV_9287(ah)) { ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9287_2GHZ; } else if (AR_SREV_9271(ah)) { ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9271_2GHZ; } else { ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9280_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9280_2GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9280_2GHZ; ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9280_5GHZ; ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9280_5GHZ; ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9280_5GHZ; } } static void ar9002_hw_antdiv_comb_conf_get(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf) { u32 regval; regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); antconf->main_lna_conf = (regval & AR_PHY_9285_ANT_DIV_MAIN_LNACONF) >> AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S; antconf->alt_lna_conf = (regval & AR_PHY_9285_ANT_DIV_ALT_LNACONF) >> AR_PHY_9285_ANT_DIV_ALT_LNACONF_S; antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >> AR_PHY_9285_FAST_DIV_BIAS_S; antconf->lna1_lna2_delta = -3; antconf->div_group = 0; } static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf) { u32 regval; regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); regval &= ~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | AR_PHY_9285_ANT_DIV_ALT_LNACONF | AR_PHY_9285_FAST_DIV_BIAS); regval |= ((antconf->main_lna_conf << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S) & AR_PHY_9285_ANT_DIV_MAIN_LNACONF); regval |= ((antconf->alt_lna_conf << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S) & AR_PHY_9285_ANT_DIV_ALT_LNACONF); regval |= ((antconf->fast_div_bias << AR_PHY_9285_FAST_DIV_BIAS_S) & AR_PHY_9285_FAST_DIV_BIAS); REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval); } void ar9002_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); priv_ops->set_rf_regs = NULL; priv_ops->rf_alloc_ext_banks = NULL; priv_ops->rf_free_ext_banks = NULL; priv_ops->rf_set_freq = ar9002_hw_set_channel; priv_ops->spur_mitigate_freq = ar9002_hw_spur_mitigate; priv_ops->olc_init = ar9002_olc_init; priv_ops->compute_pll_control = ar9002_hw_compute_pll_control; priv_ops->do_getnf = ar9002_hw_do_getnf; ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get; ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set; ar9002_hw_set_nf_limits(ah); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9002_mac.c0000644000175000017500000002352012026211315024160 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include #define AR_BufLen 0x00000fff static void ar9002_hw_rx_enable(struct ath_hw *ah) { REG_WRITE(ah, AR_CR, AR_CR_RXE); } static void ar9002_hw_set_desc_link(void *ds, u32 ds_link) { ((struct ath_desc*) ds)->ds_link = ds_link; } static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) { u32 isr = 0; u32 mask2 = 0; struct ath9k_hw_capabilities *pCap = &ah->caps; u32 sync_cause = 0; bool fatal_int = false; struct ath_common *common = ath9k_hw_common(ah); if (!AR_SREV_9100(ah)) { if (REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) { if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON) { isr = REG_READ(ah, AR_ISR); } } sync_cause = REG_READ(ah, AR_INTR_SYNC_CAUSE) & AR_INTR_SYNC_DEFAULT; *masked = 0; if (!isr && !sync_cause) return false; } else { *masked = 0; isr = REG_READ(ah, AR_ISR); } if (isr) { if (isr & AR_ISR_BCNMISC) { u32 isr2; isr2 = REG_READ(ah, AR_ISR_S2); if (isr2 & AR_ISR_S2_TIM) mask2 |= ATH9K_INT_TIM; if (isr2 & AR_ISR_S2_DTIM) mask2 |= ATH9K_INT_DTIM; if (isr2 & AR_ISR_S2_DTIMSYNC) mask2 |= ATH9K_INT_DTIMSYNC; if (isr2 & (AR_ISR_S2_CABEND)) mask2 |= ATH9K_INT_CABEND; if (isr2 & AR_ISR_S2_GTT) mask2 |= ATH9K_INT_GTT; if (isr2 & AR_ISR_S2_CST) mask2 |= ATH9K_INT_CST; if (isr2 & AR_ISR_S2_TSFOOR) mask2 |= ATH9K_INT_TSFOOR; } isr = REG_READ(ah, AR_ISR_RAC); if (isr == 0xffffffff) { *masked = 0; return false; } *masked = isr & ATH9K_INT_COMMON; if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM | AR_ISR_RXOK | AR_ISR_RXERR)) *masked |= ATH9K_INT_RX; if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | AR_ISR_TXEOL)) { u32 s0_s, s1_s; *masked |= ATH9K_INT_TX; s0_s = REG_READ(ah, AR_ISR_S0_S); ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK); ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC); s1_s = REG_READ(ah, AR_ISR_S1_S); ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR); ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL); } if (isr & AR_ISR_RXORN) { ath_dbg(common, INTERRUPT, "receive FIFO overrun interrupt\n"); } *masked |= mask2; } if (AR_SREV_9100(ah)) return true; if (isr & AR_ISR_GENTMR) { u32 s5_s; s5_s = REG_READ(ah, AR_ISR_S5_S); ah->intr_gen_timer_trigger = MS(s5_s, AR_ISR_S5_GENTIMER_TRIG); ah->intr_gen_timer_thresh = MS(s5_s, AR_ISR_S5_GENTIMER_THRESH); if (ah->intr_gen_timer_trigger) *masked |= ATH9K_INT_GENTIMER; if ((s5_s & AR_ISR_S5_TIM_TIMER) && !(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) *masked |= ATH9K_INT_TIM_TIMER; } if (sync_cause) { ath9k_debug_sync_cause(common, sync_cause); fatal_int = (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) ? true : false; if (fatal_int) { if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) { ath_dbg(common, ANY, "received PCI FATAL interrupt\n"); } if (sync_cause & AR_INTR_SYNC_HOST1_PERR) { ath_dbg(common, ANY, "received PCI PERR interrupt\n"); } *masked |= ATH9K_INT_FATAL; } if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { ath_dbg(common, INTERRUPT, "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n"); REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); REG_WRITE(ah, AR_RC, 0); *masked |= ATH9K_INT_FATAL; } if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) { ath_dbg(common, INTERRUPT, "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); } REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); (void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR); } return true; } static void ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) { struct ar5416_desc *ads = AR5416DESC(ds); u32 ctl1, ctl6; ads->ds_txstatus0 = ads->ds_txstatus1 = 0; ads->ds_txstatus2 = ads->ds_txstatus3 = 0; ads->ds_txstatus4 = ads->ds_txstatus5 = 0; ads->ds_txstatus6 = ads->ds_txstatus7 = 0; ads->ds_txstatus8 = ads->ds_txstatus9 = 0; ACCESS_ONCE(ads->ds_link) = i->link; ACCESS_ONCE(ads->ds_data) = i->buf_addr[0]; ctl1 = i->buf_len[0] | (i->is_last ? 0 : AR_TxMore); ctl6 = SM(i->keytype, AR_EncrType); if (AR_SREV_9285(ah)) { ads->ds_ctl8 = 0; ads->ds_ctl9 = 0; ads->ds_ctl10 = 0; ads->ds_ctl11 = 0; } if ((i->is_first || i->is_last) && i->aggr != AGGR_BUF_MIDDLE && i->aggr != AGGR_BUF_LAST) { ACCESS_ONCE(ads->ds_ctl2) = set11nTries(i->rates, 0) | set11nTries(i->rates, 1) | set11nTries(i->rates, 2) | set11nTries(i->rates, 3) | (i->dur_update ? AR_DurUpdateEna : 0) | SM(0, AR_BurstDur); ACCESS_ONCE(ads->ds_ctl3) = set11nRate(i->rates, 0) | set11nRate(i->rates, 1) | set11nRate(i->rates, 2) | set11nRate(i->rates, 3); } else { ACCESS_ONCE(ads->ds_ctl2) = 0; ACCESS_ONCE(ads->ds_ctl3) = 0; } if (!i->is_first) { ACCESS_ONCE(ads->ds_ctl0) = 0; ACCESS_ONCE(ads->ds_ctl1) = ctl1; ACCESS_ONCE(ads->ds_ctl6) = ctl6; return; } ctl1 |= (i->keyix != ATH9K_TXKEYIX_INVALID ? SM(i->keyix, AR_DestIdx) : 0) | SM(i->type, AR_FrameType) | (i->flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0) | (i->flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0) | (i->flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0); switch (i->aggr) { case AGGR_BUF_FIRST: ctl6 |= SM(i->aggr_len, AR_AggrLen); /* fall through */ case AGGR_BUF_MIDDLE: ctl1 |= AR_IsAggr | AR_MoreAggr; ctl6 |= SM(i->ndelim, AR_PadDelim); break; case AGGR_BUF_LAST: ctl1 |= AR_IsAggr; break; case AGGR_BUF_NONE: break; } ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen) | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) | SM(i->txpower, AR_XmitPower) | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) | (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0) | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0) | (i->flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) | (i->flags & ATH9K_TXDESC_RTSENA ? AR_RTSEnable : (i->flags & ATH9K_TXDESC_CTSENA ? AR_CTSEnable : 0)); ACCESS_ONCE(ads->ds_ctl1) = ctl1; ACCESS_ONCE(ads->ds_ctl6) = ctl6; if (i->aggr == AGGR_BUF_MIDDLE || i->aggr == AGGR_BUF_LAST) return; ACCESS_ONCE(ads->ds_ctl4) = set11nPktDurRTSCTS(i->rates, 0) | set11nPktDurRTSCTS(i->rates, 1); ACCESS_ONCE(ads->ds_ctl5) = set11nPktDurRTSCTS(i->rates, 2) | set11nPktDurRTSCTS(i->rates, 3); ACCESS_ONCE(ads->ds_ctl7) = set11nRateFlags(i->rates, 0) | set11nRateFlags(i->rates, 1) | set11nRateFlags(i->rates, 2) | set11nRateFlags(i->rates, 3) | SM(i->rtscts_rate, AR_RTSCTSRate); } static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_status *ts) { struct ar5416_desc *ads = AR5416DESC(ds); u32 status; status = ACCESS_ONCE(ads->ds_txstatus9); if ((status & AR_TxDone) == 0) return -EINPROGRESS; ts->ts_tstamp = ads->AR_SendTimestamp; ts->ts_status = 0; ts->ts_flags = 0; if (status & AR_TxOpExceeded) ts->ts_status |= ATH9K_TXERR_XTXOP; ts->tid = MS(status, AR_TxTid); ts->ts_rateindex = MS(status, AR_FinalTxIdx); ts->ts_seqnum = MS(status, AR_SeqNum); status = ACCESS_ONCE(ads->ds_txstatus0); ts->ts_rssi_ctl0 = MS(status, AR_TxRSSIAnt00); ts->ts_rssi_ctl1 = MS(status, AR_TxRSSIAnt01); ts->ts_rssi_ctl2 = MS(status, AR_TxRSSIAnt02); if (status & AR_TxBaStatus) { ts->ts_flags |= ATH9K_TX_BA; ts->ba_low = ads->AR_BaBitmapLow; ts->ba_high = ads->AR_BaBitmapHigh; } status = ACCESS_ONCE(ads->ds_txstatus1); if (status & AR_FrmXmitOK) ts->ts_status |= ATH9K_TX_ACKED; else { if (status & AR_ExcessiveRetries) ts->ts_status |= ATH9K_TXERR_XRETRY; if (status & AR_Filtered) ts->ts_status |= ATH9K_TXERR_FILT; if (status & AR_FIFOUnderrun) { ts->ts_status |= ATH9K_TXERR_FIFO; ath9k_hw_updatetxtriglevel(ah, true); } } if (status & AR_TxTimerExpired) ts->ts_status |= ATH9K_TXERR_TIMER_EXPIRED; if (status & AR_DescCfgErr) ts->ts_flags |= ATH9K_TX_DESC_CFG_ERR; if (status & AR_TxDataUnderrun) { ts->ts_flags |= ATH9K_TX_DATA_UNDERRUN; ath9k_hw_updatetxtriglevel(ah, true); } if (status & AR_TxDelimUnderrun) { ts->ts_flags |= ATH9K_TX_DELIM_UNDERRUN; ath9k_hw_updatetxtriglevel(ah, true); } ts->ts_shortretry = MS(status, AR_RTSFailCnt); ts->ts_longretry = MS(status, AR_DataFailCnt); ts->ts_virtcol = MS(status, AR_VirtRetryCnt); status = ACCESS_ONCE(ads->ds_txstatus5); ts->ts_rssi = MS(status, AR_TxRSSICombined); ts->ts_rssi_ext0 = MS(status, AR_TxRSSIAnt10); ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11); ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12); ts->evm0 = ads->AR_TxEVM0; ts->evm1 = ads->AR_TxEVM1; ts->evm2 = ads->AR_TxEVM2; return 0; } void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl1 = size & AR_BufLen; if (flags & ATH9K_RXDESC_INTREQ) ads->ds_ctl1 |= AR_RxIntrReq; memset(&ads->u.rx, 0, sizeof(ads->u.rx)); } EXPORT_SYMBOL(ath9k_hw_setuprxdesc); void ar9002_hw_attach_mac_ops(struct ath_hw *ah) { struct ath_hw_ops *ops = ath9k_hw_ops(ah); ops->rx_enable = ar9002_hw_rx_enable; ops->set_desc_link = ar9002_hw_set_desc_link; ops->get_isr = ar9002_hw_get_isr; ops->set_txdesc = ar9002_set_txdesc; ops->proc_txdesc = ar9002_hw_proc_txdesc; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9002_initvals.h0000644000175000017500000043706612026211315025274 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ static const u32 ar9280Modes_9280_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300}, {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007}, {0x00009840, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, {0x00009850, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009858, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20}, {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00}, {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x00009918, 0x0000000a, 0x00000014, 0x00000268, 0x0000000b}, {0x00009924, 0xd00a8a0b, 0xd00a8a0b, 0xd00a8a0d, 0xd00a8a0d}, {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010}, {0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010}, {0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010}, {0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210}, {0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x000099b8, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c}, {0x000099bc, 0x00000a00, 0x00000a00, 0x00000c00, 0x00000c00}, {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444}, {0x0000a20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019}, {0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019}, {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a}, {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a23c, 0x13c88000, 0x13c88000, 0x13c88001, 0x13c88000}, {0x0000a250, 0x001ff000, 0x001ff000, 0x0004a000, 0x0004a000}, {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e}, {0x0000a388, 0x0c000000, 0x0c000000, 0x08000000, 0x0c000000}, {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000}, }; static const u32 ar9280Common_9280_2[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000008}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00000054, 0x0000001f}, {0x00000800, 0x00000000}, {0x00000804, 0x00000000}, {0x00000808, 0x00000000}, {0x0000080c, 0x00000000}, {0x00000810, 0x00000000}, {0x00000814, 0x00000000}, {0x00000818, 0x00000000}, {0x0000081c, 0x00000000}, {0x00000820, 0x00000000}, {0x00000824, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x00001230, 0x00000000}, {0x00001270, 0x00000000}, {0x00001038, 0x00000000}, {0x00001078, 0x00000000}, {0x000010b8, 0x00000000}, {0x000010f8, 0x00000000}, {0x00001138, 0x00000000}, {0x00001178, 0x00000000}, {0x000011b8, 0x00000000}, {0x000011f8, 0x00000000}, {0x00001238, 0x00000000}, {0x00001278, 0x00000000}, {0x000012b8, 0x00000000}, {0x000012f8, 0x00000000}, {0x00001338, 0x00000000}, {0x00001378, 0x00000000}, {0x000013b8, 0x00000000}, {0x000013f8, 0x00000000}, {0x00001438, 0x00000000}, {0x00001478, 0x00000000}, {0x000014b8, 0x00000000}, {0x000014f8, 0x00000000}, {0x00001538, 0x00000000}, {0x00001578, 0x00000000}, {0x000015b8, 0x00000000}, {0x000015f8, 0x00000000}, {0x00001638, 0x00000000}, {0x00001678, 0x00000000}, {0x000016b8, 0x00000000}, {0x000016f8, 0x00000000}, {0x00001738, 0x00000000}, {0x00001778, 0x00000000}, {0x000017b8, 0x00000000}, {0x000017f8, 0x00000000}, {0x0000103c, 0x00000000}, {0x0000107c, 0x00000000}, {0x000010bc, 0x00000000}, {0x000010fc, 0x00000000}, {0x0000113c, 0x00000000}, {0x0000117c, 0x00000000}, {0x000011bc, 0x00000000}, {0x000011fc, 0x00000000}, {0x0000123c, 0x00000000}, {0x0000127c, 0x00000000}, {0x000012bc, 0x00000000}, {0x000012fc, 0x00000000}, {0x0000133c, 0x00000000}, {0x0000137c, 0x00000000}, {0x000013bc, 0x00000000}, {0x000013fc, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00004030, 0x00000002}, {0x0000403c, 0x00000002}, {0x00004024, 0x0000001f}, {0x00004060, 0x00000000}, {0x00004064, 0x00000000}, {0x00007010, 0x00000033}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000700}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008048, 0x40000000}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000000}, {0x000080c0, 0x2a80001a}, {0x000080c4, 0x05dc01e0}, {0x000080c8, 0x1f402710}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00001e00}, {0x000080d4, 0x00000000}, {0x000080d8, 0x00400000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x003f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080f8, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00020000}, {0x00008104, 0x00000001}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000168}, {0x00008118, 0x000100aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x00000000}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x32143320}, {0x00008174, 0xfaa4fa50}, {0x00008178, 0x00000100}, {0x0000817c, 0x00000000}, {0x000081c0, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008200, 0x00000000}, {0x00008204, 0x00000000}, {0x00008208, 0x00000000}, {0x0000820c, 0x00000000}, {0x00008210, 0x00000000}, {0x00008214, 0x00000000}, {0x00008218, 0x00000000}, {0x0000821c, 0x00000000}, {0x00008220, 0x00000000}, {0x00008224, 0x00000000}, {0x00008228, 0x00000000}, {0x0000822c, 0x00000000}, {0x00008230, 0x00000000}, {0x00008234, 0x00000000}, {0x00008238, 0x00000000}, {0x0000823c, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000100}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x400000ff}, {0x00008260, 0x00080922}, {0x00008264, 0x88a00010}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000000}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x00000000}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000040}, {0x00008314, 0x00000000}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000e00}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x00000000}, {0x00008340, 0x000107ff}, {0x00008344, 0x00481043}, {0x00009808, 0x00000000}, {0x0000980c, 0xafa68e30}, {0x00009810, 0xfd14e000}, {0x00009814, 0x9c0a9f6b}, {0x0000981c, 0x00000000}, {0x0000982c, 0x0000a000}, {0x00009830, 0x00000000}, {0x0000983c, 0x00200400}, {0x0000984c, 0x0040233c}, {0x0000a84c, 0x0040233c}, {0x00009854, 0x00000044}, {0x00009900, 0x00000000}, {0x00009904, 0x00000000}, {0x00009908, 0x00000000}, {0x0000990c, 0x00000000}, {0x00009910, 0x01002310}, {0x0000991c, 0x10000fff}, {0x00009920, 0x04900000}, {0x0000a920, 0x04900000}, {0x00009928, 0x00000001}, {0x0000992c, 0x00000004}, {0x00009934, 0x1e1f2022}, {0x00009938, 0x0a0b0c0d}, {0x0000993c, 0x00000000}, {0x00009948, 0x9280c00a}, {0x0000994c, 0x00020028}, {0x00009954, 0x5f3ca3de}, {0x00009958, 0x2108ecff}, {0x00009940, 0x14750604}, {0x0000c95c, 0x004b6a8e}, {0x00009970, 0x190fb514}, {0x00009974, 0x00000000}, {0x00009978, 0x00000001}, {0x0000997c, 0x00000000}, {0x00009980, 0x00000000}, {0x00009984, 0x00000000}, {0x00009988, 0x00000000}, {0x0000998c, 0x00000000}, {0x00009990, 0x00000000}, {0x00009994, 0x00000000}, {0x00009998, 0x00000000}, {0x0000999c, 0x00000000}, {0x000099a0, 0x00000000}, {0x000099a4, 0x00000001}, {0x000099a8, 0x201fff00}, {0x000099ac, 0x006f0000}, {0x000099b0, 0x03051000}, {0x000099b4, 0x00000820}, {0x000099c4, 0x06336f77}, {0x000099c8, 0x6af6532f}, {0x000099cc, 0x08f186c8}, {0x000099d0, 0x00046384}, {0x000099d4, 0x00000000}, {0x000099d8, 0x00000000}, {0x000099dc, 0x00000000}, {0x000099e0, 0x00000000}, {0x000099e4, 0xaaaaaaaa}, {0x000099e8, 0x3c466478}, {0x000099ec, 0x0cc80caa}, {0x000099f0, 0x00000000}, {0x000099fc, 0x00001042}, {0x0000a208, 0x803e4788}, {0x0000a210, 0x4080a333}, {0x0000a214, 0x40206c10}, {0x0000a218, 0x009c4060}, {0x0000a220, 0x01834061}, {0x0000a224, 0x00000400}, {0x0000a228, 0x000003b5}, {0x0000a22c, 0x233f7180}, {0x0000a234, 0x20202020}, {0x0000a238, 0x20202020}, {0x0000a240, 0x38490a20}, {0x0000a244, 0x00007bb6}, {0x0000a248, 0x0fff3ffc}, {0x0000a24c, 0x00000000}, {0x0000a254, 0x00000000}, {0x0000a258, 0x0cdbd380}, {0x0000a25c, 0x0f0f0f01}, {0x0000a260, 0xdfa91f01}, {0x0000a268, 0x00000000}, {0x0000a26c, 0x0e79e5c6}, {0x0000b26c, 0x0e79e5c6}, {0x0000d270, 0x00820820}, {0x0000a278, 0x1ce739ce}, {0x0000d35c, 0x07ffffef}, {0x0000d360, 0x0fffffe7}, {0x0000d364, 0x17ffffe5}, {0x0000d368, 0x1fffffe4}, {0x0000d36c, 0x37ffffe3}, {0x0000d370, 0x3fffffe3}, {0x0000d374, 0x57ffffe3}, {0x0000d378, 0x5fffffe2}, {0x0000d37c, 0x7fffffe2}, {0x0000d380, 0x7f3c7bba}, {0x0000d384, 0xf3307ff0}, {0x0000a38c, 0x20202020}, {0x0000a390, 0x20202020}, {0x0000a394, 0x1ce739ce}, {0x0000a398, 0x000001ce}, {0x0000a39c, 0x00000001}, {0x0000a3a0, 0x00000000}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0x00000000}, {0x0000a3ac, 0x00000000}, {0x0000a3b0, 0x00000000}, {0x0000a3b4, 0x00000000}, {0x0000a3b8, 0x00000000}, {0x0000a3bc, 0x00000000}, {0x0000a3c0, 0x00000000}, {0x0000a3c4, 0x00000000}, {0x0000a3c8, 0x00000246}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3dc, 0x1ce739ce}, {0x0000a3e0, 0x000001ce}, {0x0000a3e4, 0x00000000}, {0x0000a3e8, 0x18c43433}, {0x00007800, 0x00040000}, {0x00007804, 0xdb005012}, {0x00007808, 0x04924914}, {0x0000780c, 0x21084210}, {0x00007810, 0x6d801300}, {0x00007818, 0x07e41000}, {0x00007824, 0x00040000}, {0x00007828, 0xdb005012}, {0x0000782c, 0x04924914}, {0x00007830, 0x21084210}, {0x00007834, 0x6d801300}, {0x0000783c, 0x07e40000}, {0x00007848, 0x00100000}, {0x0000784c, 0x773f0567}, {0x00007850, 0x54214514}, {0x00007854, 0x12035828}, {0x00007858, 0x9259269a}, {0x00007860, 0x52802000}, {0x00007864, 0x0a8e370e}, {0x00007868, 0xc0102850}, {0x0000786c, 0x812d4000}, {0x00007870, 0x807ec400}, {0x00007874, 0x001b6db0}, {0x00007878, 0x00376b63}, {0x0000787c, 0x06db6db6}, {0x00007880, 0x006d8000}, {0x00007884, 0xffeffffe}, {0x00007888, 0xffeffffe}, {0x0000788c, 0x00010000}, {0x00007890, 0x02060aeb}, {0x00007898, 0x2a850160}, }; static const u32 ar9280Modes_fast_clock_9280_2[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, {0x000010b0, 0x00000fd0, 0x00001fa0}, {0x00008014, 0x044c044c, 0x08980898}, {0x0000801c, 0x148ec02b, 0x148ec057}, {0x00008318, 0x000044c0, 0x00008980}, {0x00009820, 0x02020200, 0x02020200}, {0x00009824, 0x01000f0f, 0x01000f0f}, {0x00009828, 0x0b020001, 0x0b020001}, {0x00009834, 0x00000f0f, 0x00000f0f}, {0x00009844, 0x03721821, 0x03721821}, {0x00009914, 0x00000898, 0x00001130}, {0x00009918, 0x0000000b, 0x00000016}, }; static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290}, {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304}, {0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308}, {0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c}, {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000}, {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004}, {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008}, {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c}, {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080}, {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084}, {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088}, {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c}, {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100}, {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104}, {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108}, {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c}, {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110}, {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114}, {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180}, {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184}, {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188}, {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c}, {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190}, {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194}, {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0}, {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c}, {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8}, {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284}, {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288}, {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224}, {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290}, {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300}, {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304}, {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308}, {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c}, {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380}, {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384}, {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700}, {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704}, {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708}, {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c}, {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780}, {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784}, {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00}, {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04}, {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08}, {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c}, {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b10, 0x00008b10}, {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b80, 0x00008b80}, {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b84, 0x00008b84}, {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b88, 0x00008b88}, {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b8c, 0x00008b8c}, {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008b90, 0x00008b90}, {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008b94, 0x00008b94}, {0x00009adc, 0x0000b390, 0x0000b390, 0x00008b98, 0x00008b98}, {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008ba4, 0x00008ba4}, {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008ba8, 0x00008ba8}, {0x00009ae8, 0x0000b780, 0x0000b780, 0x00008bac, 0x00008bac}, {0x00009aec, 0x0000b784, 0x0000b784, 0x00008bb0, 0x00008bb0}, {0x00009af0, 0x0000b788, 0x0000b788, 0x00008bb4, 0x00008bb4}, {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00008ba1, 0x00008ba1}, {0x00009af8, 0x0000b790, 0x0000b790, 0x00008ba5, 0x00008ba5}, {0x00009afc, 0x0000b794, 0x0000b794, 0x00008ba9, 0x00008ba9}, {0x00009b00, 0x0000b798, 0x0000b798, 0x00008bad, 0x00008bad}, {0x00009b04, 0x0000d784, 0x0000d784, 0x00008bb1, 0x00008bb1}, {0x00009b08, 0x0000d788, 0x0000d788, 0x00008bb5, 0x00008bb5}, {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00008ba2, 0x00008ba2}, {0x00009b10, 0x0000d790, 0x0000d790, 0x00008ba6, 0x00008ba6}, {0x00009b14, 0x0000f780, 0x0000f780, 0x00008baa, 0x00008baa}, {0x00009b18, 0x0000f784, 0x0000f784, 0x00008bae, 0x00008bae}, {0x00009b1c, 0x0000f788, 0x0000f788, 0x00008bb2, 0x00008bb2}, {0x00009b20, 0x0000f78c, 0x0000f78c, 0x00008bb6, 0x00008bb6}, {0x00009b24, 0x0000f790, 0x0000f790, 0x00008ba3, 0x00008ba3}, {0x00009b28, 0x0000f794, 0x0000f794, 0x00008ba7, 0x00008ba7}, {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x00008bab, 0x00008bab}, {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00008baf, 0x00008baf}, {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00008bb3, 0x00008bb3}, {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00008bb7, 0x00008bb7}, {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00008bc3, 0x00008bc3}, {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x00008bc7, 0x00008bc7}, {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x00008bcb, 0x00008bcb}, {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00008bcf, 0x00008bcf}, {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00008bd3, 0x00008bd3}, {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00008bd7, 0x00008bd7}, {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00008bdb, 0x00008bdb}, {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x00008bdb, 0x00008bdb}, {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x00008bdb, 0x00008bdb}, {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00008bdb, 0x00008bdb}, {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00008bdb, 0x00008bdb}, {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x00008bdb, 0x00008bdb}, {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x00008bdb, 0x00008bdb}, {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x00008bdb, 0x00008bdb}, {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x00008bdb, 0x00008bdb}, {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x00008bdb, 0x00008bdb}, {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x00008bdb, 0x00008bdb}, {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x00008bdb, 0x00008bdb}, {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x00008bdb, 0x00008bdb}, {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x00008bdb, 0x00008bdb}, {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x00008bdb, 0x00008bdb}, {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x00008bdb, 0x00008bdb}, {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x00008bdb, 0x00008bdb}, {0x00009b98, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bac, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009be0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009be4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009be8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bec, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x00008bdb, 0x00008bdb}, {0x00009848, 0x00001066, 0x00001066, 0x00001055, 0x00001055}, {0x0000a848, 0x00001066, 0x00001066, 0x00001055, 0x00001055}, }; static const u32 ar9280Modes_original_rxgain_9280_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009a00, 0x00008184, 0x00008184, 0x00008000, 0x00008000}, {0x00009a04, 0x00008188, 0x00008188, 0x00008000, 0x00008000}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00008000, 0x00008000}, {0x00009a0c, 0x00008190, 0x00008190, 0x00008000, 0x00008000}, {0x00009a10, 0x00008194, 0x00008194, 0x00008000, 0x00008000}, {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000}, {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004}, {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008}, {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c}, {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080}, {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084}, {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088}, {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c}, {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100}, {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104}, {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108}, {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c}, {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110}, {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114}, {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180}, {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184}, {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188}, {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c}, {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190}, {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194}, {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0}, {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c}, {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8}, {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284}, {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288}, {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224}, {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290}, {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300}, {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304}, {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308}, {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c}, {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380}, {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384}, {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700}, {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704}, {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708}, {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c}, {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780}, {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784}, {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00}, {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04}, {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08}, {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c}, {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80}, {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84}, {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88}, {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c}, {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90}, {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80}, {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84}, {0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88}, {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c}, {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90}, {0x00009ae8, 0x0000b780, 0x0000b780, 0x0000930c, 0x0000930c}, {0x00009aec, 0x0000b784, 0x0000b784, 0x00009310, 0x00009310}, {0x00009af0, 0x0000b788, 0x0000b788, 0x00009384, 0x00009384}, {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009388, 0x00009388}, {0x00009af8, 0x0000b790, 0x0000b790, 0x00009324, 0x00009324}, {0x00009afc, 0x0000b794, 0x0000b794, 0x00009704, 0x00009704}, {0x00009b00, 0x0000b798, 0x0000b798, 0x000096a4, 0x000096a4}, {0x00009b04, 0x0000d784, 0x0000d784, 0x000096a8, 0x000096a8}, {0x00009b08, 0x0000d788, 0x0000d788, 0x00009710, 0x00009710}, {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009714, 0x00009714}, {0x00009b10, 0x0000d790, 0x0000d790, 0x00009720, 0x00009720}, {0x00009b14, 0x0000f780, 0x0000f780, 0x00009724, 0x00009724}, {0x00009b18, 0x0000f784, 0x0000f784, 0x00009728, 0x00009728}, {0x00009b1c, 0x0000f788, 0x0000f788, 0x0000972c, 0x0000972c}, {0x00009b20, 0x0000f78c, 0x0000f78c, 0x000097a0, 0x000097a0}, {0x00009b24, 0x0000f790, 0x0000f790, 0x000097a4, 0x000097a4}, {0x00009b28, 0x0000f794, 0x0000f794, 0x000097a8, 0x000097a8}, {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x000097b0, 0x000097b0}, {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x000097b4, 0x000097b4}, {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x000097b8, 0x000097b8}, {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x000097a5, 0x000097a5}, {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x000097a9, 0x000097a9}, {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x000097ad, 0x000097ad}, {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x000097b1, 0x000097b1}, {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x000097b5, 0x000097b5}, {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x000097b9, 0x000097b9}, {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x000097c5, 0x000097c5}, {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x000097c9, 0x000097c9}, {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x000097d1, 0x000097d1}, {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x000097d5, 0x000097d5}, {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x000097d9, 0x000097d9}, {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x000097c6, 0x000097c6}, {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x000097ca, 0x000097ca}, {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x000097ce, 0x000097ce}, {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x000097d2, 0x000097d2}, {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x000097d6, 0x000097d6}, {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x000097c3, 0x000097c3}, {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x000097c7, 0x000097c7}, {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x000097cb, 0x000097cb}, {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x000097cf, 0x000097cf}, {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x000097d7, 0x000097d7}, {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x000097db, 0x000097db}, {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x000097db, 0x000097db}, {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x000097db, 0x000097db}, {0x00009b98, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bac, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009be0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009be4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009be8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bec, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db}, {0x00009848, 0x00001066, 0x00001066, 0x00001063, 0x00001063}, {0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063}, }; static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290}, {0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300}, {0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304}, {0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308}, {0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c}, {0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000}, {0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004}, {0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008}, {0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c}, {0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080}, {0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084}, {0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088}, {0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c}, {0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100}, {0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104}, {0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108}, {0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c}, {0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110}, {0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114}, {0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180}, {0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184}, {0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188}, {0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c}, {0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190}, {0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194}, {0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0}, {0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c}, {0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8}, {0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284}, {0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288}, {0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224}, {0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290}, {0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300}, {0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304}, {0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308}, {0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c}, {0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380}, {0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384}, {0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700}, {0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704}, {0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708}, {0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c}, {0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780}, {0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784}, {0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00}, {0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04}, {0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08}, {0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c}, {0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80}, {0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84}, {0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88}, {0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c}, {0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90}, {0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80}, {0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84}, {0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88}, {0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c}, {0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90}, {0x00009ae8, 0x0000b780, 0x0000b780, 0x00009310, 0x00009310}, {0x00009aec, 0x0000b784, 0x0000b784, 0x00009314, 0x00009314}, {0x00009af0, 0x0000b788, 0x0000b788, 0x00009320, 0x00009320}, {0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009324, 0x00009324}, {0x00009af8, 0x0000b790, 0x0000b790, 0x00009328, 0x00009328}, {0x00009afc, 0x0000b794, 0x0000b794, 0x0000932c, 0x0000932c}, {0x00009b00, 0x0000b798, 0x0000b798, 0x00009330, 0x00009330}, {0x00009b04, 0x0000d784, 0x0000d784, 0x00009334, 0x00009334}, {0x00009b08, 0x0000d788, 0x0000d788, 0x00009321, 0x00009321}, {0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009325, 0x00009325}, {0x00009b10, 0x0000d790, 0x0000d790, 0x00009329, 0x00009329}, {0x00009b14, 0x0000f780, 0x0000f780, 0x0000932d, 0x0000932d}, {0x00009b18, 0x0000f784, 0x0000f784, 0x00009331, 0x00009331}, {0x00009b1c, 0x0000f788, 0x0000f788, 0x00009335, 0x00009335}, {0x00009b20, 0x0000f78c, 0x0000f78c, 0x00009322, 0x00009322}, {0x00009b24, 0x0000f790, 0x0000f790, 0x00009326, 0x00009326}, {0x00009b28, 0x0000f794, 0x0000f794, 0x0000932a, 0x0000932a}, {0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x0000932e, 0x0000932e}, {0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00009332, 0x00009332}, {0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00009336, 0x00009336}, {0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00009323, 0x00009323}, {0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00009327, 0x00009327}, {0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x0000932b, 0x0000932b}, {0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x0000932f, 0x0000932f}, {0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00009333, 0x00009333}, {0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00009337, 0x00009337}, {0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00009343, 0x00009343}, {0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00009347, 0x00009347}, {0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x0000934b, 0x0000934b}, {0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x0000934f, 0x0000934f}, {0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00009353, 0x00009353}, {0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00009357, 0x00009357}, {0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x0000935b, 0x0000935b}, {0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x0000935b, 0x0000935b}, {0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x0000935b, 0x0000935b}, {0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x0000935b, 0x0000935b}, {0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x0000935b, 0x0000935b}, {0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x0000935b, 0x0000935b}, {0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x0000935b, 0x0000935b}, {0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x0000935b, 0x0000935b}, {0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x0000935b, 0x0000935b}, {0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x0000935b, 0x0000935b}, {0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x0000935b, 0x0000935b}, {0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x0000935b, 0x0000935b}, {0x00009b98, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009b9c, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009ba0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009ba4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009ba8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bac, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bb0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bb4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bb8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bbc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bc0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bc4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bc8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bcc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bd0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bd4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bd8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bdc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009be0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009be4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009be8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bec, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bf0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bf4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bf8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009bfc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b}, {0x00009848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a}, {0x0000a848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a}, }; static const u32 ar9280Modes_high_power_tx_gain_9280_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652}, {0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce}, {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00003002, 0x00003002, 0x00004002, 0x00004002}, {0x0000a308, 0x00006004, 0x00006004, 0x00007008, 0x00007008}, {0x0000a30c, 0x0000a006, 0x0000a006, 0x0000c010, 0x0000c010}, {0x0000a310, 0x0000e012, 0x0000e012, 0x00010012, 0x00010012}, {0x0000a314, 0x00011014, 0x00011014, 0x00013014, 0x00013014}, {0x0000a318, 0x0001504a, 0x0001504a, 0x0001820a, 0x0001820a}, {0x0000a31c, 0x0001904c, 0x0001904c, 0x0001b211, 0x0001b211}, {0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213}, {0x0000a324, 0x00021092, 0x00021092, 0x00022411, 0x00022411}, {0x0000a328, 0x0002510a, 0x0002510a, 0x00025413, 0x00025413}, {0x0000a32c, 0x0002910c, 0x0002910c, 0x00029811, 0x00029811}, {0x0000a330, 0x0002c18b, 0x0002c18b, 0x0002c813, 0x0002c813}, {0x0000a334, 0x0002f1cc, 0x0002f1cc, 0x00030a14, 0x00030a14}, {0x0000a338, 0x000321eb, 0x000321eb, 0x00035a50, 0x00035a50}, {0x0000a33c, 0x000341ec, 0x000341ec, 0x00039c4c, 0x00039c4c}, {0x0000a340, 0x000341ec, 0x000341ec, 0x0003de8a, 0x0003de8a}, {0x0000a344, 0x000341ec, 0x000341ec, 0x00042e92, 0x00042e92}, {0x0000a348, 0x000341ec, 0x000341ec, 0x00046ed2, 0x00046ed2}, {0x0000a34c, 0x000341ec, 0x000341ec, 0x0004bed5, 0x0004bed5}, {0x0000a350, 0x000341ec, 0x000341ec, 0x0004ff54, 0x0004ff54}, {0x0000a354, 0x000341ec, 0x000341ec, 0x00055fd5, 0x00055fd5}, {0x0000a3ec, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081}, {0x00007814, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff}, {0x00007838, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff}, {0x0000781c, 0x00172000, 0x00172000, 0x00172000, 0x00172000}, {0x00007840, 0x00172000, 0x00172000, 0x00172000, 0x00172000}, {0x00007820, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480}, {0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480}, }; static const u32 ar9280Modes_original_tx_gain_9280_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652}, {0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce}, {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002}, {0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009}, {0x0000a30c, 0x0000a006, 0x0000a006, 0x0000b00b, 0x0000b00b}, {0x0000a310, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012}, {0x0000a314, 0x00011014, 0x00011014, 0x00012048, 0x00012048}, {0x0000a318, 0x0001504a, 0x0001504a, 0x0001604a, 0x0001604a}, {0x0000a31c, 0x0001904c, 0x0001904c, 0x0001a211, 0x0001a211}, {0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213}, {0x0000a324, 0x00020092, 0x00020092, 0x0002121b, 0x0002121b}, {0x0000a328, 0x0002410a, 0x0002410a, 0x00024412, 0x00024412}, {0x0000a32c, 0x0002710c, 0x0002710c, 0x00028414, 0x00028414}, {0x0000a330, 0x0002b18b, 0x0002b18b, 0x0002b44a, 0x0002b44a}, {0x0000a334, 0x0002e1cc, 0x0002e1cc, 0x00030649, 0x00030649}, {0x0000a338, 0x000321ec, 0x000321ec, 0x0003364b, 0x0003364b}, {0x0000a33c, 0x000321ec, 0x000321ec, 0x00038a49, 0x00038a49}, {0x0000a340, 0x000321ec, 0x000321ec, 0x0003be48, 0x0003be48}, {0x0000a344, 0x000321ec, 0x000321ec, 0x0003ee4a, 0x0003ee4a}, {0x0000a348, 0x000321ec, 0x000321ec, 0x00042e88, 0x00042e88}, {0x0000a34c, 0x000321ec, 0x000321ec, 0x00046e8a, 0x00046e8a}, {0x0000a350, 0x000321ec, 0x000321ec, 0x00049ec9, 0x00049ec9}, {0x0000a354, 0x000321ec, 0x000321ec, 0x0004bf42, 0x0004bf42}, {0x0000a3ec, 0x00f70081, 0x00f70081, 0x00f70081, 0x00f70081}, {0x00007814, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff}, {0x00007838, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff}, {0x0000781c, 0x00392000, 0x00392000, 0x00392000, 0x00392000}, {0x00007840, 0x00392000, 0x00392000, 0x00392000, 0x00392000}, {0x00007820, 0x92592480, 0x92592480, 0x92592480, 0x92592480}, {0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480}, }; static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, {0x00004040, 0xa8000019}, {0x00004040, 0x13160820}, {0x00004040, 0xe5980560}, {0x00004040, 0xc01dcffc}, {0x00004040, 0x1aaabe41}, {0x00004040, 0xbe105554}, {0x00004040, 0x00043007}, {0x00004044, 0x00000000}, }; static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, {0x00004040, 0xa8000019}, {0x00004040, 0x13160820}, {0x00004040, 0xe5980560}, {0x00004040, 0xc01dcffd}, {0x00004040, 0x1aaabe41}, {0x00004040, 0xbe105554}, {0x00004040, 0x00043007}, {0x00004044, 0x00000000}, }; static const u32 ar9280PciePhy_awow[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, {0x00004040, 0x24924924}, {0x00004040, 0xa8000019}, {0x00004040, 0x13160820}, {0x00004040, 0xe5980560}, {0x00004040, 0xc01dcffd}, {0x00004040, 0x1aaabe41}, {0x00004040, 0xbe105554}, {0x00004040, 0x00043007}, {0x00004044, 0x00000000}, }; static const u32 ar9285Modes_9285_1_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300}, {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007}, {0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e}, {0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620}, {0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053}, {0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053}, {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2}, {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e}, {0x00009860, 0x00058d18, 0x00058d18, 0x00058d20, 0x00058d20}, {0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00}, {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d}, {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020}, {0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c}, {0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00}, {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77}, {0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f}, {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8}, {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384}, {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009a00, 0x00000000, 0x00000000, 0x00058084, 0x00058084}, {0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088}, {0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c}, {0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100}, {0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104}, {0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108}, {0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c}, {0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110}, {0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114}, {0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180}, {0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184}, {0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188}, {0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c}, {0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190}, {0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194}, {0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0}, {0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c}, {0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8}, {0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284}, {0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288}, {0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224}, {0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290}, {0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300}, {0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304}, {0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308}, {0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c}, {0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380}, {0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384}, {0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700}, {0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704}, {0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708}, {0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c}, {0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780}, {0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784}, {0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00}, {0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04}, {0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08}, {0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c}, {0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80}, {0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84}, {0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88}, {0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c}, {0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90}, {0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80}, {0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84}, {0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88}, {0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c}, {0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90}, {0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c}, {0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310}, {0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384}, {0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388}, {0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324}, {0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704}, {0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4}, {0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8}, {0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710}, {0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714}, {0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720}, {0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724}, {0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728}, {0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c}, {0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0}, {0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4}, {0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8}, {0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0}, {0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4}, {0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8}, {0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5}, {0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9}, {0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad}, {0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1}, {0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5}, {0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9}, {0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5}, {0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9}, {0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1}, {0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5}, {0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9}, {0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6}, {0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca}, {0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce}, {0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2}, {0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6}, {0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3}, {0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7}, {0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb}, {0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf}, {0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7}, {0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084}, {0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088}, {0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c}, {0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100}, {0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104}, {0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108}, {0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c}, {0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110}, {0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114}, {0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180}, {0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184}, {0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188}, {0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c}, {0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190}, {0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194}, {0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0}, {0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c}, {0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8}, {0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284}, {0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288}, {0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224}, {0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290}, {0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300}, {0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304}, {0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308}, {0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c}, {0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380}, {0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384}, {0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700}, {0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704}, {0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708}, {0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c}, {0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780}, {0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784}, {0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00}, {0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04}, {0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08}, {0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c}, {0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80}, {0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84}, {0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88}, {0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c}, {0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90}, {0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80}, {0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84}, {0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88}, {0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c}, {0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90}, {0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c}, {0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310}, {0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384}, {0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388}, {0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324}, {0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704}, {0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4}, {0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8}, {0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710}, {0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714}, {0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720}, {0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724}, {0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728}, {0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c}, {0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0}, {0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4}, {0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8}, {0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0}, {0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4}, {0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8}, {0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5}, {0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9}, {0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad}, {0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1}, {0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5}, {0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9}, {0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5}, {0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9}, {0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1}, {0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5}, {0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9}, {0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6}, {0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca}, {0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce}, {0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2}, {0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6}, {0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3}, {0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7}, {0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb}, {0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf}, {0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7}, {0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004}, {0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000}, {0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000}, {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a}, {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000}, {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e}, }; static const u32 ar9285Common_9285_1_2[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020045}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000008}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00000054, 0x0000001f}, {0x00000800, 0x00000000}, {0x00000804, 0x00000000}, {0x00000808, 0x00000000}, {0x0000080c, 0x00000000}, {0x00000810, 0x00000000}, {0x00000814, 0x00000000}, {0x00000818, 0x00000000}, {0x0000081c, 0x00000000}, {0x00000820, 0x00000000}, {0x00000824, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x00001230, 0x00000000}, {0x00001270, 0x00000000}, {0x00001038, 0x00000000}, {0x00001078, 0x00000000}, {0x000010b8, 0x00000000}, {0x000010f8, 0x00000000}, {0x00001138, 0x00000000}, {0x00001178, 0x00000000}, {0x000011b8, 0x00000000}, {0x000011f8, 0x00000000}, {0x00001238, 0x00000000}, {0x00001278, 0x00000000}, {0x000012b8, 0x00000000}, {0x000012f8, 0x00000000}, {0x00001338, 0x00000000}, {0x00001378, 0x00000000}, {0x000013b8, 0x00000000}, {0x000013f8, 0x00000000}, {0x00001438, 0x00000000}, {0x00001478, 0x00000000}, {0x000014b8, 0x00000000}, {0x000014f8, 0x00000000}, {0x00001538, 0x00000000}, {0x00001578, 0x00000000}, {0x000015b8, 0x00000000}, {0x000015f8, 0x00000000}, {0x00001638, 0x00000000}, {0x00001678, 0x00000000}, {0x000016b8, 0x00000000}, {0x000016f8, 0x00000000}, {0x00001738, 0x00000000}, {0x00001778, 0x00000000}, {0x000017b8, 0x00000000}, {0x000017f8, 0x00000000}, {0x0000103c, 0x00000000}, {0x0000107c, 0x00000000}, {0x000010bc, 0x00000000}, {0x000010fc, 0x00000000}, {0x0000113c, 0x00000000}, {0x0000117c, 0x00000000}, {0x000011bc, 0x00000000}, {0x000011fc, 0x00000000}, {0x0000123c, 0x00000000}, {0x0000127c, 0x00000000}, {0x000012bc, 0x00000000}, {0x000012fc, 0x00000000}, {0x0000133c, 0x00000000}, {0x0000137c, 0x00000000}, {0x000013bc, 0x00000000}, {0x000013fc, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00004030, 0x00000002}, {0x0000403c, 0x00000002}, {0x00004024, 0x0000001f}, {0x00004060, 0x00000000}, {0x00004064, 0x00000000}, {0x00007010, 0x00000031}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000700}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008048, 0x00000000}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000000}, {0x000080c0, 0x2a80001a}, {0x000080c4, 0x05dc01e0}, {0x000080c8, 0x1f402710}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00001e00}, {0x000080d4, 0x00000000}, {0x000080d8, 0x00400000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x003f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080f8, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00020000}, {0x00008104, 0x00000001}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000168}, {0x00008118, 0x000100aa}, {0x0000811c, 0x00003210}, {0x00008120, 0x08f04810}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x00000000}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x32143320}, {0x00008174, 0xfaa4fa50}, {0x00008178, 0x00000100}, {0x0000817c, 0x00000000}, {0x000081c0, 0x00000000}, {0x000081d0, 0x0000320a}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008200, 0x00000000}, {0x00008204, 0x00000000}, {0x00008208, 0x00000000}, {0x0000820c, 0x00000000}, {0x00008210, 0x00000000}, {0x00008214, 0x00000000}, {0x00008218, 0x00000000}, {0x0000821c, 0x00000000}, {0x00008220, 0x00000000}, {0x00008224, 0x00000000}, {0x00008228, 0x00000000}, {0x0000822c, 0x00000000}, {0x00008230, 0x00000000}, {0x00008234, 0x00000000}, {0x00008238, 0x00000000}, {0x0000823c, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000100}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x400000ff}, {0x00008260, 0x00080922}, {0x00008264, 0x88a00010}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000000}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x00000000}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000040}, {0x00008314, 0x00000000}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000001}, {0x00008330, 0x00000302}, {0x00008334, 0x00000e00}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x00000000}, {0x00008340, 0x00010380}, {0x00008344, 0x00481043}, {0x00009808, 0x00000000}, {0x0000980c, 0xafe68e30}, {0x00009810, 0xfd14e000}, {0x00009814, 0x9c0a9f6b}, {0x0000981c, 0x00000000}, {0x0000982c, 0x0000a000}, {0x00009830, 0x00000000}, {0x0000983c, 0x00200400}, {0x0000984c, 0x0040233c}, {0x00009854, 0x00000044}, {0x00009900, 0x00000000}, {0x00009904, 0x00000000}, {0x00009908, 0x00000000}, {0x0000990c, 0x00000000}, {0x00009910, 0x01002310}, {0x0000991c, 0x10000fff}, {0x00009920, 0x04900000}, {0x00009928, 0x00000001}, {0x0000992c, 0x00000004}, {0x00009934, 0x1e1f2022}, {0x00009938, 0x0a0b0c0d}, {0x0000993c, 0x00000000}, {0x00009940, 0x14750604}, {0x00009948, 0x9280c00a}, {0x0000994c, 0x00020028}, {0x00009954, 0x5f3ca3de}, {0x00009958, 0x2108ecff}, {0x00009968, 0x000003ce}, {0x00009970, 0x192bb514}, {0x00009974, 0x00000000}, {0x00009978, 0x00000001}, {0x0000997c, 0x00000000}, {0x00009980, 0x00000000}, {0x00009984, 0x00000000}, {0x00009988, 0x00000000}, {0x0000998c, 0x00000000}, {0x00009990, 0x00000000}, {0x00009994, 0x00000000}, {0x00009998, 0x00000000}, {0x0000999c, 0x00000000}, {0x000099a0, 0x00000000}, {0x000099a4, 0x00000001}, {0x000099a8, 0x201fff00}, {0x000099ac, 0x2def0400}, {0x000099b0, 0x03051000}, {0x000099b4, 0x00000820}, {0x000099dc, 0x00000000}, {0x000099e0, 0x00000000}, {0x000099e4, 0xaaaaaaaa}, {0x000099e8, 0x3c466478}, {0x000099ec, 0x0cc80caa}, {0x000099f0, 0x00000000}, {0x0000a208, 0x803e68c8}, {0x0000a210, 0x4080a333}, {0x0000a214, 0x00206c10}, {0x0000a218, 0x009c4060}, {0x0000a220, 0x01834061}, {0x0000a224, 0x00000400}, {0x0000a228, 0x000003b5}, {0x0000a22c, 0x00000000}, {0x0000a234, 0x20202020}, {0x0000a238, 0x20202020}, {0x0000a244, 0x00000000}, {0x0000a248, 0xfffffffc}, {0x0000a24c, 0x00000000}, {0x0000a254, 0x00000000}, {0x0000a258, 0x0ccb5380}, {0x0000a25c, 0x15151501}, {0x0000a260, 0xdfa90f01}, {0x0000a268, 0x00000000}, {0x0000a26c, 0x0ebae9e6}, {0x0000d270, 0x0d820820}, {0x0000d35c, 0x07ffffef}, {0x0000d360, 0x0fffffe7}, {0x0000d364, 0x17ffffe5}, {0x0000d368, 0x1fffffe4}, {0x0000d36c, 0x37ffffe3}, {0x0000d370, 0x3fffffe3}, {0x0000d374, 0x57ffffe3}, {0x0000d378, 0x5fffffe2}, {0x0000d37c, 0x7fffffe2}, {0x0000d380, 0x7f3c7bba}, {0x0000d384, 0xf3307ff0}, {0x0000a388, 0x0c000000}, {0x0000a38c, 0x20202020}, {0x0000a390, 0x20202020}, {0x0000a39c, 0x00000001}, {0x0000a3a0, 0x00000000}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0x00000000}, {0x0000a3ac, 0x00000000}, {0x0000a3b0, 0x00000000}, {0x0000a3b4, 0x00000000}, {0x0000a3b8, 0x00000000}, {0x0000a3bc, 0x00000000}, {0x0000a3c0, 0x00000000}, {0x0000a3c4, 0x00000000}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3e4, 0x00000000}, {0x0000a3e8, 0x18c43433}, {0x0000a3ec, 0x00f70081}, {0x00007800, 0x00140000}, {0x00007804, 0x0e4548d8}, {0x00007808, 0x54214514}, {0x0000780c, 0x02025830}, {0x00007810, 0x71c0d388}, {0x0000781c, 0x00000000}, {0x00007824, 0x00d86fff}, {0x0000782c, 0x6e36d97b}, {0x00007834, 0x71400087}, {0x00007844, 0x000c0db6}, {0x00007848, 0x6db6246f}, {0x0000784c, 0x6d9b66db}, {0x00007850, 0x6d8c6dba}, {0x00007854, 0x00040000}, {0x00007858, 0xdb003012}, {0x0000785c, 0x04924914}, {0x00007860, 0x21084210}, {0x00007864, 0xf7d7ffde}, {0x00007868, 0xc2034080}, {0x00007870, 0x10142c00}, }; static const u32 ar9285Modes_high_power_tx_gain_9285_1_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200}, {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201}, {0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240}, {0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241}, {0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600}, {0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800}, {0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802}, {0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805}, {0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80}, {0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00}, {0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40}, {0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80}, {0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82}, {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e}, {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e}, {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x00007814, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8}, {0x00007828, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b}, {0x00007830, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e}, {0x00007838, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803}, {0x0000783c, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe}, {0x00007840, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20}, {0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe}, {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00}, {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652}, {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7}, {0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7}, {0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7}, {0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, {0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7}, {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, }; static const u32 ar9285Modes_original_tx_gain_9285_1_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208}, {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608}, {0x0000a310, 0x00000000, 0x00000000, 0x00022618, 0x00022618}, {0x0000a314, 0x00000000, 0x00000000, 0x0002a6c9, 0x0002a6c9}, {0x0000a318, 0x00000000, 0x00000000, 0x00031710, 0x00031710}, {0x0000a31c, 0x00000000, 0x00000000, 0x00035718, 0x00035718}, {0x0000a320, 0x00000000, 0x00000000, 0x00038758, 0x00038758}, {0x0000a324, 0x00000000, 0x00000000, 0x0003c75a, 0x0003c75a}, {0x0000a328, 0x00000000, 0x00000000, 0x0004075c, 0x0004075c}, {0x0000a32c, 0x00000000, 0x00000000, 0x0004475e, 0x0004475e}, {0x0000a330, 0x00000000, 0x00000000, 0x0004679f, 0x0004679f}, {0x0000a334, 0x00000000, 0x00000000, 0x000487df, 0x000487df}, {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e}, {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e}, {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x00007814, 0x924934a8, 0x924934a8, 0x924934a8, 0x924934a8}, {0x00007828, 0x26d2491b, 0x26d2491b, 0x26d2491b, 0x26d2491b}, {0x00007830, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e, 0xedb6d96e}, {0x00007838, 0xfac68801, 0xfac68801, 0xfac68801, 0xfac68801}, {0x0000783c, 0x0001fffe, 0x0001fffe, 0x0001fffe, 0x0001fffe}, {0x00007840, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20, 0xffeb1a20}, {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4}, {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04}, {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21a652, 0x0a21a652}, {0x0000a278, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c}, {0x0000a27c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c}, {0x0000a394, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c}, {0x0000a398, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, {0x0000a3dc, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c}, {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, }; static const u32 ar9285Modes_XE2_0_normal_power[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208}, {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608}, {0x0000a310, 0x00000000, 0x00000000, 0x00022618, 0x00022618}, {0x0000a314, 0x00000000, 0x00000000, 0x0002a6c9, 0x0002a6c9}, {0x0000a318, 0x00000000, 0x00000000, 0x00031710, 0x00031710}, {0x0000a31c, 0x00000000, 0x00000000, 0x00035718, 0x00035718}, {0x0000a320, 0x00000000, 0x00000000, 0x00038758, 0x00038758}, {0x0000a324, 0x00000000, 0x00000000, 0x0003c75a, 0x0003c75a}, {0x0000a328, 0x00000000, 0x00000000, 0x0004075c, 0x0004075c}, {0x0000a32c, 0x00000000, 0x00000000, 0x0004475e, 0x0004475e}, {0x0000a330, 0x00000000, 0x00000000, 0x0004679f, 0x0004679f}, {0x0000a334, 0x00000000, 0x00000000, 0x000487df, 0x000487df}, {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e}, {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e}, {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x00007814, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8}, {0x00007828, 0x4ad2491b, 0x4ad2491b, 0x2ad2491b, 0x4ad2491b}, {0x00007830, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e}, {0x00007838, 0xdac71441, 0xdac71441, 0xdac71441, 0xdac71441}, {0x0000783c, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe}, {0x00007840, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c}, {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4}, {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04}, {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21a652, 0x0a21a652}, {0x0000a278, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c}, {0x0000a27c, 0x050e039c, 0x050e039c, 0x050e039c, 0x050e039c}, {0x0000a394, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c}, {0x0000a398, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, {0x0000a3dc, 0x39ce739c, 0x39ce739c, 0x39ce739c, 0x39ce739c}, {0x0000a3e0, 0x0000039c, 0x0000039c, 0x0000039c, 0x0000039c}, }; static const u32 ar9285Modes_XE2_0_high_power[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200}, {0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201}, {0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240}, {0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241}, {0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600}, {0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800}, {0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802}, {0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805}, {0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80}, {0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00}, {0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40}, {0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80}, {0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82}, {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e}, {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e}, {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x00007814, 0x92497ca8, 0x92497ca8, 0x92497ca8, 0x92497ca8}, {0x00007828, 0x4ad2491b, 0x4ad2491b, 0x2ad2491b, 0x4ad2491b}, {0x00007830, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e, 0xedb6da6e}, {0x00007838, 0xdac71443, 0xdac71443, 0xdac71443, 0xdac71443}, {0x0000783c, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe, 0x2481f6fe}, {0x00007840, 0xba5f638c, 0xba5f638c, 0xba5f638c, 0xba5f638c}, {0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe}, {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00}, {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652}, {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7}, {0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7}, {0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7}, {0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, {0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7}, {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, }; static const u32 ar9287Modes_9287_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160}, {0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000000, 0x00000000, 0x00007c70, 0x00003e38}, {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00008014, 0x00000000, 0x00000000, 0x10801600, 0x08400b00}, {0x0000801c, 0x00000000, 0x00000000, 0x12e00057, 0x12e0002b}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003200, 0x00003200, 0x0000320a, 0x0000320a}, {0x00008318, 0x00000000, 0x00000000, 0x00006880, 0x00003440}, {0x00009804, 0x00000000, 0x00000000, 0x000003c4, 0x00000300}, {0x00009820, 0x00000000, 0x00000000, 0x02020200, 0x02020200}, {0x00009824, 0x00000000, 0x00000000, 0x01000e0e, 0x01000e0e}, {0x00009828, 0x00000000, 0x00000000, 0x3a020001, 0x3a020001}, {0x00009834, 0x00000000, 0x00000000, 0x00000e0e, 0x00000e0e}, {0x00009838, 0x00000003, 0x00000003, 0x00000007, 0x00000007}, {0x00009840, 0x206a002e, 0x206a002e, 0x206a012e, 0x206a012e}, {0x00009844, 0x03720000, 0x03720000, 0x037216a0, 0x037216a0}, {0x00009850, 0x60000000, 0x60000000, 0x6d4000e2, 0x6c4000e2}, {0x00009858, 0x7c000d00, 0x7c000d00, 0x7ec84d2e, 0x7ec84d2e}, {0x0000985c, 0x3100005e, 0x3100005e, 0x3139605e, 0x31395d5e}, {0x00009860, 0x00058d00, 0x00058d00, 0x00058d20, 0x00058d20}, {0x00009864, 0x00000e00, 0x00000e00, 0x0001ce00, 0x0001ce00}, {0x00009868, 0x000040c0, 0x000040c0, 0x5ac640d0, 0x5ac640d0}, {0x0000986c, 0x00000080, 0x00000080, 0x06903881, 0x06903881}, {0x00009914, 0x00000000, 0x00000000, 0x00001130, 0x00000898}, {0x00009918, 0x00000000, 0x00000000, 0x00000016, 0x0000000b}, {0x00009924, 0xd00a8a01, 0xd00a8a01, 0xd00a8a0d, 0xd00a8a0d}, {0x00009944, 0xefbc0000, 0xefbc0000, 0xefbc1010, 0xefbc1010}, {0x00009960, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, {0x0000a960, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, {0x00009964, 0x00000000, 0x00000000, 0x00000210, 0x00000210}, {0x0000c968, 0x00000200, 0x00000200, 0x000003ce, 0x000003ce}, {0x000099b8, 0x00000000, 0x00000000, 0x0000001c, 0x0000001c}, {0x000099bc, 0x00000000, 0x00000000, 0x00000c00, 0x00000c00}, {0x000099c0, 0x00000000, 0x00000000, 0x05eea6d4, 0x05eea6d4}, {0x0000a204, 0x00000440, 0x00000440, 0x00000444, 0x00000444}, {0x0000a20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a21c, 0x1803800a, 0x1803800a, 0x1883800a, 0x1883800a}, {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a250, 0x00000000, 0x00000000, 0x0004a000, 0x0004a000}, {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e}, {0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar9287Common_9287_1_1[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000008}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00000054, 0x0000001f}, {0x00000800, 0x00000000}, {0x00000804, 0x00000000}, {0x00000808, 0x00000000}, {0x0000080c, 0x00000000}, {0x00000810, 0x00000000}, {0x00000814, 0x00000000}, {0x00000818, 0x00000000}, {0x0000081c, 0x00000000}, {0x00000820, 0x00000000}, {0x00000824, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x00001230, 0x00000000}, {0x00001270, 0x00000000}, {0x00001038, 0x00000000}, {0x00001078, 0x00000000}, {0x000010b8, 0x00000000}, {0x000010f8, 0x00000000}, {0x00001138, 0x00000000}, {0x00001178, 0x00000000}, {0x000011b8, 0x00000000}, {0x000011f8, 0x00000000}, {0x00001238, 0x00000000}, {0x00001278, 0x00000000}, {0x000012b8, 0x00000000}, {0x000012f8, 0x00000000}, {0x00001338, 0x00000000}, {0x00001378, 0x00000000}, {0x000013b8, 0x00000000}, {0x000013f8, 0x00000000}, {0x00001438, 0x00000000}, {0x00001478, 0x00000000}, {0x000014b8, 0x00000000}, {0x000014f8, 0x00000000}, {0x00001538, 0x00000000}, {0x00001578, 0x00000000}, {0x000015b8, 0x00000000}, {0x000015f8, 0x00000000}, {0x00001638, 0x00000000}, {0x00001678, 0x00000000}, {0x000016b8, 0x00000000}, {0x000016f8, 0x00000000}, {0x00001738, 0x00000000}, {0x00001778, 0x00000000}, {0x000017b8, 0x00000000}, {0x000017f8, 0x00000000}, {0x0000103c, 0x00000000}, {0x0000107c, 0x00000000}, {0x000010bc, 0x00000000}, {0x000010fc, 0x00000000}, {0x0000113c, 0x00000000}, {0x0000117c, 0x00000000}, {0x000011bc, 0x00000000}, {0x000011fc, 0x00000000}, {0x0000123c, 0x00000000}, {0x0000127c, 0x00000000}, {0x000012bc, 0x00000000}, {0x000012fc, 0x00000000}, {0x0000133c, 0x00000000}, {0x0000137c, 0x00000000}, {0x000013bc, 0x00000000}, {0x000013fc, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00004030, 0x00000002}, {0x0000403c, 0x00000002}, {0x00004024, 0x0000001f}, {0x00004060, 0x00000000}, {0x00004064, 0x00000000}, {0x00007010, 0x00000033}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000700}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008048, 0x40000000}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000000}, {0x000080c0, 0x2a80001a}, {0x000080c4, 0x05dc01e0}, {0x000080c8, 0x1f402710}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00001e00}, {0x000080d4, 0x00000000}, {0x000080d8, 0x00400000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x003f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080f8, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00020000}, {0x00008104, 0x00000001}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000168}, {0x00008118, 0x000100aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x00000000}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x18487320}, {0x00008174, 0xfaa4fa50}, {0x00008178, 0x00000100}, {0x0000817c, 0x00000000}, {0x000081c0, 0x00000000}, {0x000081c4, 0x00000000}, {0x000081d4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008200, 0x00000000}, {0x00008204, 0x00000000}, {0x00008208, 0x00000000}, {0x0000820c, 0x00000000}, {0x00008210, 0x00000000}, {0x00008214, 0x00000000}, {0x00008218, 0x00000000}, {0x0000821c, 0x00000000}, {0x00008220, 0x00000000}, {0x00008224, 0x00000000}, {0x00008228, 0x00000000}, {0x0000822c, 0x00000000}, {0x00008230, 0x00000000}, {0x00008234, 0x00000000}, {0x00008238, 0x00000000}, {0x0000823c, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000100}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x400000ff}, {0x00008260, 0x00080922}, {0x00008264, 0x88a00010}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000000}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x000000ff}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000040}, {0x00008314, 0x00000000}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000e00}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x00000000}, {0x00008340, 0x000107ff}, {0x00008344, 0x01c81043}, {0x00008360, 0xffffffff}, {0x00008364, 0xffffffff}, {0x00008368, 0x00000000}, {0x00008370, 0x00000000}, {0x00008374, 0x000000ff}, {0x00008378, 0x00000000}, {0x0000837c, 0x00000000}, {0x00008380, 0xffffffff}, {0x00008384, 0xffffffff}, {0x00008390, 0x0fffffff}, {0x00008394, 0x0fffffff}, {0x00008398, 0x00000000}, {0x0000839c, 0x00000000}, {0x000083a0, 0x00000000}, {0x00009808, 0x00000000}, {0x0000980c, 0xafe68e30}, {0x00009810, 0xfd14e000}, {0x00009814, 0x9c0a9f6b}, {0x0000981c, 0x00000000}, {0x0000982c, 0x0000a000}, {0x00009830, 0x00000000}, {0x0000983c, 0x00200400}, {0x0000984c, 0x0040233c}, {0x0000a84c, 0x0040233c}, {0x00009854, 0x00000044}, {0x00009900, 0x00000000}, {0x00009904, 0x00000000}, {0x00009908, 0x00000000}, {0x0000990c, 0x00000000}, {0x00009910, 0x10002310}, {0x0000991c, 0x10000fff}, {0x00009920, 0x04900000}, {0x0000a920, 0x04900000}, {0x00009928, 0x00000001}, {0x0000992c, 0x00000004}, {0x00009930, 0x00000000}, {0x0000a930, 0x00000000}, {0x00009934, 0x1e1f2022}, {0x00009938, 0x0a0b0c0d}, {0x0000993c, 0x00000000}, {0x00009948, 0x9280c00a}, {0x0000994c, 0x00020028}, {0x00009954, 0x5f3ca3de}, {0x00009958, 0x0108ecff}, {0x00009940, 0x14750604}, {0x0000c95c, 0x004b6a8e}, {0x00009970, 0x990bb514}, {0x00009974, 0x00000000}, {0x00009978, 0x00000001}, {0x0000997c, 0x00000000}, {0x000099a0, 0x00000000}, {0x000099a4, 0x00000001}, {0x000099a8, 0x201fff00}, {0x000099ac, 0x0c6f0000}, {0x000099b0, 0x03051000}, {0x000099b4, 0x00000820}, {0x000099c4, 0x06336f77}, {0x000099c8, 0x6af6532f}, {0x000099cc, 0x08f186c8}, {0x000099d0, 0x00046384}, {0x000099dc, 0x00000000}, {0x000099e0, 0x00000000}, {0x000099e4, 0xaaaaaaaa}, {0x000099e8, 0x3c466478}, {0x000099ec, 0x0cc80caa}, {0x000099f0, 0x00000000}, {0x000099fc, 0x00001042}, {0x0000a208, 0x803e4788}, {0x0000a210, 0x4080a333}, {0x0000a214, 0x40206c10}, {0x0000a218, 0x009c4060}, {0x0000a220, 0x01834061}, {0x0000a224, 0x00000400}, {0x0000a228, 0x000003b5}, {0x0000a22c, 0x233f7180}, {0x0000a234, 0x20202020}, {0x0000a238, 0x20202020}, {0x0000a23c, 0x13c889af}, {0x0000a240, 0x38490a20}, {0x0000a244, 0x00000000}, {0x0000a248, 0xfffffffc}, {0x0000a24c, 0x00000000}, {0x0000a254, 0x00000000}, {0x0000a258, 0x0cdbd380}, {0x0000a25c, 0x0f0f0f01}, {0x0000a260, 0xdfa91f01}, {0x0000a264, 0x00418a11}, {0x0000b264, 0x00418a11}, {0x0000a268, 0x00000000}, {0x0000a26c, 0x0e79e5c6}, {0x0000b26c, 0x0e79e5c6}, {0x0000d270, 0x00820820}, {0x0000a278, 0x1ce739ce}, {0x0000a27c, 0x050701ce}, {0x0000d35c, 0x07ffffef}, {0x0000d360, 0x0fffffe7}, {0x0000d364, 0x17ffffe5}, {0x0000d368, 0x1fffffe4}, {0x0000d36c, 0x37ffffe3}, {0x0000d370, 0x3fffffe3}, {0x0000d374, 0x57ffffe3}, {0x0000d378, 0x5fffffe2}, {0x0000d37c, 0x7fffffe2}, {0x0000d380, 0x7f3c7bba}, {0x0000d384, 0xf3307ff0}, {0x0000a388, 0x0c000000}, {0x0000a38c, 0x20202020}, {0x0000a390, 0x20202020}, {0x0000a394, 0x1ce739ce}, {0x0000a398, 0x000001ce}, {0x0000b398, 0x000001ce}, {0x0000a39c, 0x00000001}, {0x0000a3c8, 0x00000246}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3dc, 0x1ce739ce}, {0x0000a3e0, 0x000001ce}, {0x0000a3e4, 0x00000000}, {0x0000a3e8, 0x18c43433}, {0x0000a3ec, 0x00f70081}, {0x0000a3f0, 0x01036a1e}, {0x0000a3f4, 0x00000000}, {0x0000b3f4, 0x00000000}, {0x0000a7d8, 0x000003f1}, {0x00007800, 0x00000800}, {0x00007804, 0x6c35ffd2}, {0x00007808, 0x6db6c000}, {0x0000780c, 0x6db6cb30}, {0x00007810, 0x6db6cb6c}, {0x00007814, 0x0501e200}, {0x00007818, 0x0094128d}, {0x0000781c, 0x976ee392}, {0x00007820, 0xf75ff6fc}, {0x00007824, 0x00040000}, {0x00007828, 0xdb003012}, {0x0000782c, 0x04924914}, {0x00007830, 0x21084210}, {0x00007834, 0x00140000}, {0x00007838, 0x0e4548d8}, {0x0000783c, 0x54214514}, {0x00007840, 0x02025830}, {0x00007844, 0x71c0d388}, {0x00007848, 0x934934a8}, {0x00007850, 0x00000000}, {0x00007854, 0x00000800}, {0x00007858, 0x6c35ffd2}, {0x0000785c, 0x6db6c000}, {0x00007860, 0x6db6cb30}, {0x00007864, 0x6db6cb6c}, {0x00007868, 0x0501e200}, {0x0000786c, 0x0094128d}, {0x00007870, 0x976ee392}, {0x00007874, 0xf75ff6fc}, {0x00007878, 0x00040000}, {0x0000787c, 0xdb003012}, {0x00007880, 0x04924914}, {0x00007884, 0x21084210}, {0x00007888, 0x001b6db0}, {0x0000788c, 0x00376b63}, {0x00007890, 0x06db6db6}, {0x00007894, 0x006d8000}, {0x00007898, 0x48100000}, {0x0000789c, 0x00000000}, {0x000078a0, 0x08000000}, {0x000078a4, 0x0007ffd8}, {0x000078a8, 0x0007ffd8}, {0x000078ac, 0x001c0020}, {0x000078b0, 0x00060aeb}, {0x000078b4, 0x40008080}, {0x000078b8, 0x2a850160}, }; static const u32 ar9287Common_normal_cck_fir_coeff_9287_1_1[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00fffeff}, {0x0000a1f8, 0x00f5f9ff}, {0x0000a1fc, 0xb79f6427}, }; static const u32 ar9287Common_japan_2484_cck_fir_coeff_9287_1_1[][2] = { /* Addr allmodes */ {0x0000a1f4, 0x00000000}, {0x0000a1f8, 0xefff0301}, {0x0000a1fc, 0xca9228ee}, }; static const u32 ar9287Modes_tx_gain_9287_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00004002, 0x00004002}, {0x0000a308, 0x00000000, 0x00000000, 0x00008004, 0x00008004}, {0x0000a30c, 0x00000000, 0x00000000, 0x0000c00a, 0x0000c00a}, {0x0000a310, 0x00000000, 0x00000000, 0x0001000c, 0x0001000c}, {0x0000a314, 0x00000000, 0x00000000, 0x0001420b, 0x0001420b}, {0x0000a318, 0x00000000, 0x00000000, 0x0001824a, 0x0001824a}, {0x0000a31c, 0x00000000, 0x00000000, 0x0001c44a, 0x0001c44a}, {0x0000a320, 0x00000000, 0x00000000, 0x0002064a, 0x0002064a}, {0x0000a324, 0x00000000, 0x00000000, 0x0002484a, 0x0002484a}, {0x0000a328, 0x00000000, 0x00000000, 0x00028a4a, 0x00028a4a}, {0x0000a32c, 0x00000000, 0x00000000, 0x0002cc4a, 0x0002cc4a}, {0x0000a330, 0x00000000, 0x00000000, 0x00030e4a, 0x00030e4a}, {0x0000a334, 0x00000000, 0x00000000, 0x00034e8a, 0x00034e8a}, {0x0000a338, 0x00000000, 0x00000000, 0x00038e8c, 0x00038e8c}, {0x0000a33c, 0x00000000, 0x00000000, 0x0003cecc, 0x0003cecc}, {0x0000a340, 0x00000000, 0x00000000, 0x00040ed4, 0x00040ed4}, {0x0000a344, 0x00000000, 0x00000000, 0x00044edc, 0x00044edc}, {0x0000a348, 0x00000000, 0x00000000, 0x00048ede, 0x00048ede}, {0x0000a34c, 0x00000000, 0x00000000, 0x0004cf1e, 0x0004cf1e}, {0x0000a350, 0x00000000, 0x00000000, 0x00050f5e, 0x00050f5e}, {0x0000a354, 0x00000000, 0x00000000, 0x00054f9e, 0x00054f9e}, {0x0000a780, 0x00000000, 0x00000000, 0x00000062, 0x00000062}, {0x0000a784, 0x00000000, 0x00000000, 0x00004064, 0x00004064}, {0x0000a788, 0x00000000, 0x00000000, 0x000080a4, 0x000080a4}, {0x0000a78c, 0x00000000, 0x00000000, 0x0000c0aa, 0x0000c0aa}, {0x0000a790, 0x00000000, 0x00000000, 0x000100ac, 0x000100ac}, {0x0000a794, 0x00000000, 0x00000000, 0x000140b4, 0x000140b4}, {0x0000a798, 0x00000000, 0x00000000, 0x000180f4, 0x000180f4}, {0x0000a79c, 0x00000000, 0x00000000, 0x0001c134, 0x0001c134}, {0x0000a7a0, 0x00000000, 0x00000000, 0x00020174, 0x00020174}, {0x0000a7a4, 0x00000000, 0x00000000, 0x0002417c, 0x0002417c}, {0x0000a7a8, 0x00000000, 0x00000000, 0x0002817e, 0x0002817e}, {0x0000a7ac, 0x00000000, 0x00000000, 0x0002c1be, 0x0002c1be}, {0x0000a7b0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7b4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7b8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7bc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7c0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7c4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7c8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7cc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7d0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a7d4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe}, {0x0000a274, 0x0a180000, 0x0a180000, 0x0a1aa000, 0x0a1aa000}, }; static const u32 ar9287Modes_rx_gain_9287_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009a00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120}, {0x00009a04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124}, {0x00009a08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128}, {0x00009a0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c}, {0x00009a10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130}, {0x00009a14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194}, {0x00009a18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198}, {0x00009a1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c}, {0x00009a20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210}, {0x00009a24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284}, {0x00009a28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288}, {0x00009a2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c}, {0x00009a30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290}, {0x00009a34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294}, {0x00009a38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0}, {0x00009a3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4}, {0x00009a40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8}, {0x00009a44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac}, {0x00009a48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0}, {0x00009a4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4}, {0x00009a50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8}, {0x00009a54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4}, {0x00009a58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708}, {0x00009a5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c}, {0x00009a60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710}, {0x00009a64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04}, {0x00009a68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08}, {0x00009a6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c}, {0x00009a70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10}, {0x00009a74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14}, {0x00009a78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18}, {0x00009a7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c}, {0x00009a80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90}, {0x00009a84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94}, {0x00009a88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98}, {0x00009a8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4}, {0x00009a90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8}, {0x00009a94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04}, {0x00009a98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08}, {0x00009a9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c}, {0x00009aa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10}, {0x00009aa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14}, {0x00009aa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18}, {0x00009aac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c}, {0x00009ab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90}, {0x00009ab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18}, {0x00009ab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24}, {0x00009abc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28}, {0x00009ac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314}, {0x00009ac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318}, {0x00009ac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c}, {0x00009acc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390}, {0x00009ad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394}, {0x00009ad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398}, {0x00009ad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4}, {0x00009adc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8}, {0x00009ae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac}, {0x00009ae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0}, {0x00009ae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380}, {0x00009aec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384}, {0x00009af0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388}, {0x00009af4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710}, {0x00009af8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714}, {0x00009afc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718}, {0x00009b00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10}, {0x00009b04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14}, {0x00009b08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18}, {0x00009b0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c}, {0x00009b10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90}, {0x00009b14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94}, {0x00009b18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c}, {0x00009b1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90}, {0x00009b20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94}, {0x00009b24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0}, {0x00009b28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4}, {0x00009b2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8}, {0x00009b30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac}, {0x00009b34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0}, {0x00009b38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4}, {0x00009b3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1}, {0x00009b40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5}, {0x00009b44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9}, {0x00009b48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad}, {0x00009b4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1}, {0x00009b50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5}, {0x00009b54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9}, {0x00009b58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5}, {0x00009b5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9}, {0x00009b60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd}, {0x00009b64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1}, {0x00009b68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5}, {0x00009b6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2}, {0x00009b70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6}, {0x00009b74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca}, {0x00009b78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce}, {0x00009b7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2}, {0x00009b80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6}, {0x00009b84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda}, {0x00009b88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7}, {0x00009b8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb}, {0x00009b90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf}, {0x00009b94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3}, {0x00009b98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7}, {0x00009b9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009ba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009ba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009ba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009be0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009be4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009be8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009bfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000aa00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120}, {0x0000aa04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124}, {0x0000aa08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128}, {0x0000aa0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c}, {0x0000aa10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130}, {0x0000aa14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194}, {0x0000aa18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198}, {0x0000aa1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c}, {0x0000aa20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210}, {0x0000aa24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284}, {0x0000aa28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288}, {0x0000aa2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c}, {0x0000aa30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290}, {0x0000aa34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294}, {0x0000aa38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0}, {0x0000aa3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4}, {0x0000aa40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8}, {0x0000aa44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac}, {0x0000aa48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0}, {0x0000aa4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4}, {0x0000aa50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8}, {0x0000aa54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4}, {0x0000aa58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708}, {0x0000aa5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c}, {0x0000aa60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710}, {0x0000aa64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04}, {0x0000aa68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08}, {0x0000aa6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c}, {0x0000aa70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10}, {0x0000aa74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14}, {0x0000aa78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18}, {0x0000aa7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c}, {0x0000aa80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90}, {0x0000aa84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94}, {0x0000aa88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98}, {0x0000aa8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4}, {0x0000aa90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8}, {0x0000aa94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04}, {0x0000aa98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08}, {0x0000aa9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c}, {0x0000aaa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10}, {0x0000aaa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14}, {0x0000aaa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18}, {0x0000aaac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c}, {0x0000aab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90}, {0x0000aab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18}, {0x0000aab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24}, {0x0000aabc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28}, {0x0000aac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314}, {0x0000aac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318}, {0x0000aac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c}, {0x0000aacc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390}, {0x0000aad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394}, {0x0000aad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398}, {0x0000aad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4}, {0x0000aadc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8}, {0x0000aae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac}, {0x0000aae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0}, {0x0000aae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380}, {0x0000aaec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384}, {0x0000aaf0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388}, {0x0000aaf4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710}, {0x0000aaf8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714}, {0x0000aafc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718}, {0x0000ab00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10}, {0x0000ab04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14}, {0x0000ab08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18}, {0x0000ab0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c}, {0x0000ab10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90}, {0x0000ab14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94}, {0x0000ab18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c}, {0x0000ab1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90}, {0x0000ab20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94}, {0x0000ab24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0}, {0x0000ab28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4}, {0x0000ab2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8}, {0x0000ab30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac}, {0x0000ab34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0}, {0x0000ab38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4}, {0x0000ab3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1}, {0x0000ab40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5}, {0x0000ab44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9}, {0x0000ab48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad}, {0x0000ab4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1}, {0x0000ab50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5}, {0x0000ab54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9}, {0x0000ab58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5}, {0x0000ab5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9}, {0x0000ab60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd}, {0x0000ab64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1}, {0x0000ab68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5}, {0x0000ab6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2}, {0x0000ab70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6}, {0x0000ab74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca}, {0x0000ab78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce}, {0x0000ab7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2}, {0x0000ab80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6}, {0x0000ab84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda}, {0x0000ab88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7}, {0x0000ab8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb}, {0x0000ab90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf}, {0x0000ab94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3}, {0x0000ab98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7}, {0x0000ab9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000aba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000aba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000aba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abe0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abe4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abe8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x0000abfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb}, {0x00009848, 0x00000000, 0x00000000, 0x00001067, 0x00001067}, {0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067}, }; static const u32 ar9271Modes_9271[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300}, {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, {0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, {0x00009828, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001}, {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007}, {0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e}, {0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620}, {0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053}, {0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053}, {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2}, {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e}, {0x00009860, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18}, {0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00}, {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x00009910, 0x30002310, 0x30002310, 0x30002310, 0x30002310}, {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d}, {0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020}, {0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c}, {0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00}, {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77}, {0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f}, {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8}, {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384}, {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009a00, 0x00000000, 0x00000000, 0x00058084, 0x00058084}, {0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088}, {0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c}, {0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100}, {0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104}, {0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108}, {0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c}, {0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110}, {0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114}, {0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180}, {0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184}, {0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188}, {0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c}, {0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190}, {0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194}, {0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0}, {0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c}, {0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8}, {0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284}, {0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288}, {0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224}, {0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290}, {0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300}, {0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304}, {0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308}, {0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c}, {0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380}, {0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384}, {0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700}, {0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704}, {0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708}, {0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c}, {0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780}, {0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784}, {0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00}, {0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04}, {0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08}, {0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c}, {0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80}, {0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84}, {0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88}, {0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c}, {0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90}, {0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80}, {0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84}, {0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88}, {0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c}, {0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90}, {0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c}, {0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310}, {0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384}, {0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388}, {0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324}, {0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704}, {0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4}, {0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8}, {0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710}, {0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714}, {0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720}, {0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724}, {0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728}, {0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c}, {0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0}, {0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4}, {0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8}, {0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0}, {0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4}, {0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8}, {0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5}, {0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9}, {0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad}, {0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1}, {0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5}, {0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9}, {0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5}, {0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9}, {0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1}, {0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5}, {0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9}, {0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6}, {0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca}, {0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce}, {0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2}, {0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6}, {0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3}, {0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7}, {0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb}, {0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf}, {0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7}, {0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084}, {0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088}, {0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c}, {0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100}, {0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104}, {0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108}, {0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c}, {0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110}, {0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114}, {0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180}, {0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184}, {0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188}, {0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c}, {0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190}, {0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194}, {0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0}, {0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c}, {0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8}, {0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284}, {0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288}, {0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224}, {0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290}, {0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300}, {0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304}, {0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308}, {0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c}, {0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380}, {0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384}, {0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700}, {0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704}, {0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708}, {0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c}, {0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780}, {0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784}, {0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00}, {0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04}, {0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08}, {0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c}, {0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80}, {0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84}, {0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88}, {0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c}, {0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90}, {0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80}, {0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84}, {0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88}, {0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c}, {0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90}, {0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c}, {0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310}, {0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384}, {0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388}, {0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324}, {0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704}, {0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4}, {0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8}, {0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710}, {0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714}, {0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720}, {0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724}, {0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728}, {0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c}, {0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0}, {0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4}, {0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8}, {0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0}, {0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4}, {0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8}, {0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5}, {0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9}, {0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad}, {0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1}, {0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5}, {0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9}, {0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5}, {0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9}, {0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1}, {0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5}, {0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9}, {0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6}, {0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca}, {0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce}, {0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2}, {0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6}, {0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3}, {0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7}, {0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb}, {0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf}, {0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7}, {0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db}, {0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004}, {0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000}, {0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000}, {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a}, {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000}, {0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e}, }; static const u32 ar9271Common_9271[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020045}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000008}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00000054, 0x0000001f}, {0x00000800, 0x00000000}, {0x00000804, 0x00000000}, {0x00000808, 0x00000000}, {0x0000080c, 0x00000000}, {0x00000810, 0x00000000}, {0x00000814, 0x00000000}, {0x00000818, 0x00000000}, {0x0000081c, 0x00000000}, {0x00000820, 0x00000000}, {0x00000824, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x00001230, 0x00000000}, {0x00001270, 0x00000000}, {0x00001038, 0x00000000}, {0x00001078, 0x00000000}, {0x000010b8, 0x00000000}, {0x000010f8, 0x00000000}, {0x00001138, 0x00000000}, {0x00001178, 0x00000000}, {0x000011b8, 0x00000000}, {0x000011f8, 0x00000000}, {0x00001238, 0x00000000}, {0x00001278, 0x00000000}, {0x000012b8, 0x00000000}, {0x000012f8, 0x00000000}, {0x00001338, 0x00000000}, {0x00001378, 0x00000000}, {0x000013b8, 0x00000000}, {0x000013f8, 0x00000000}, {0x00001438, 0x00000000}, {0x00001478, 0x00000000}, {0x000014b8, 0x00000000}, {0x000014f8, 0x00000000}, {0x00001538, 0x00000000}, {0x00001578, 0x00000000}, {0x000015b8, 0x00000000}, {0x000015f8, 0x00000000}, {0x00001638, 0x00000000}, {0x00001678, 0x00000000}, {0x000016b8, 0x00000000}, {0x000016f8, 0x00000000}, {0x00001738, 0x00000000}, {0x00001778, 0x00000000}, {0x000017b8, 0x00000000}, {0x000017f8, 0x00000000}, {0x0000103c, 0x00000000}, {0x0000107c, 0x00000000}, {0x000010bc, 0x00000000}, {0x000010fc, 0x00000000}, {0x0000113c, 0x00000000}, {0x0000117c, 0x00000000}, {0x000011bc, 0x00000000}, {0x000011fc, 0x00000000}, {0x0000123c, 0x00000000}, {0x0000127c, 0x00000000}, {0x000012bc, 0x00000000}, {0x000012fc, 0x00000000}, {0x0000133c, 0x00000000}, {0x0000137c, 0x00000000}, {0x000013bc, 0x00000000}, {0x000013fc, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00004030, 0x00000002}, {0x0000403c, 0x00000002}, {0x00004024, 0x0000001f}, {0x00004060, 0x00000000}, {0x00004064, 0x00000000}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000700}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008048, 0x00000000}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x00008070, 0x00000000}, {0x000080b0, 0x00000000}, {0x000080b4, 0x00000000}, {0x000080b8, 0x00000000}, {0x000080bc, 0x00000000}, {0x000080c0, 0x2a80001a}, {0x000080c4, 0x05dc01e0}, {0x000080c8, 0x1f402710}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00001e00}, {0x000080d4, 0x00000000}, {0x000080d8, 0x00400000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x003f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080f8, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00020000}, {0x00008104, 0x00000001}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000168}, {0x00008118, 0x000100aa}, {0x0000811c, 0x00003210}, {0x00008120, 0x08f04810}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x00000000}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x32143320}, {0x00008174, 0xfaa4fa50}, {0x00008178, 0x00000100}, {0x0000817c, 0x00000000}, {0x000081c0, 0x00000000}, {0x000081d0, 0x0000320a}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008200, 0x00000000}, {0x00008204, 0x00000000}, {0x00008208, 0x00000000}, {0x0000820c, 0x00000000}, {0x00008210, 0x00000000}, {0x00008214, 0x00000000}, {0x00008218, 0x00000000}, {0x0000821c, 0x00000000}, {0x00008220, 0x00000000}, {0x00008224, 0x00000000}, {0x00008228, 0x00000000}, {0x0000822c, 0x00000000}, {0x00008230, 0x00000000}, {0x00008234, 0x00000000}, {0x00008238, 0x00000000}, {0x0000823c, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000100}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x400000ff}, {0x00008260, 0x00080922}, {0x00008264, 0x88a00010}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000000}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x00000000}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x0000829c, 0x00000000}, {0x00008300, 0x00000040}, {0x00008314, 0x00000000}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000001}, {0x00008330, 0x00000302}, {0x00008334, 0x00000e00}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x00000000}, {0x00008340, 0x00010380}, {0x00008344, 0x00581043}, {0x00007010, 0x00000030}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, {0x00007800, 0x00140000}, {0x00007804, 0x0e4548d8}, {0x00007808, 0x54214514}, {0x0000780c, 0x02025820}, {0x00007810, 0x71c0d388}, {0x00007814, 0x924934a8}, {0x0000781c, 0x00000000}, {0x00007828, 0x66964300}, {0x0000782c, 0x8db6d961}, {0x00007830, 0x8db6d96c}, {0x00007834, 0x6140008b}, {0x0000783c, 0x72ee0a72}, {0x00007840, 0xbbfffffc}, {0x00007844, 0x000c0db6}, {0x00007848, 0x6db6246f}, {0x0000784c, 0x6d9b66db}, {0x00007850, 0x6d8c6dba}, {0x00007854, 0x00040000}, {0x00007858, 0xdb003012}, {0x0000785c, 0x04924914}, {0x00007860, 0x21084210}, {0x00007864, 0xf7d7ffde}, {0x00007868, 0xc2034080}, {0x00007870, 0x10142c00}, {0x00009808, 0x00000000}, {0x0000980c, 0xafe68e30}, {0x00009810, 0xfd14e000}, {0x00009814, 0x9c0a9f6b}, {0x0000981c, 0x00000000}, {0x0000982c, 0x0000a000}, {0x00009830, 0x00000000}, {0x0000983c, 0x00200400}, {0x0000984c, 0x0040233c}, {0x00009854, 0x00000044}, {0x00009900, 0x00000000}, {0x00009904, 0x00000000}, {0x00009908, 0x00000000}, {0x0000990c, 0x00000000}, {0x0000991c, 0x10000fff}, {0x00009920, 0x04900000}, {0x00009928, 0x00000001}, {0x0000992c, 0x00000004}, {0x00009934, 0x1e1f2022}, {0x00009938, 0x0a0b0c0d}, {0x0000993c, 0x00000000}, {0x00009940, 0x14750604}, {0x00009948, 0x9280c00a}, {0x0000994c, 0x00020028}, {0x00009954, 0x5f3ca3de}, {0x00009958, 0x0108ecff}, {0x00009968, 0x000003ce}, {0x00009970, 0x192bb514}, {0x00009974, 0x00000000}, {0x00009978, 0x00000001}, {0x0000997c, 0x00000000}, {0x00009980, 0x00000000}, {0x00009984, 0x00000000}, {0x00009988, 0x00000000}, {0x0000998c, 0x00000000}, {0x00009990, 0x00000000}, {0x00009994, 0x00000000}, {0x00009998, 0x00000000}, {0x0000999c, 0x00000000}, {0x000099a0, 0x00000000}, {0x000099a4, 0x00000001}, {0x000099a8, 0x201fff00}, {0x000099ac, 0x2def0400}, {0x000099b0, 0x03051000}, {0x000099b4, 0x00000820}, {0x000099dc, 0x00000000}, {0x000099e0, 0x00000000}, {0x000099e4, 0xaaaaaaaa}, {0x000099e8, 0x3c466478}, {0x000099ec, 0x0cc80caa}, {0x000099f0, 0x00000000}, {0x0000a208, 0x803e68c8}, {0x0000a210, 0x4080a333}, {0x0000a214, 0x00206c10}, {0x0000a218, 0x009c4060}, {0x0000a220, 0x01834061}, {0x0000a224, 0x00000400}, {0x0000a228, 0x000003b5}, {0x0000a22c, 0x00000000}, {0x0000a234, 0x20202020}, {0x0000a238, 0x20202020}, {0x0000a244, 0x00000000}, {0x0000a248, 0xfffffffc}, {0x0000a24c, 0x00000000}, {0x0000a254, 0x00000000}, {0x0000a258, 0x0ccb5380}, {0x0000a25c, 0x15151501}, {0x0000a260, 0xdfa90f01}, {0x0000a268, 0x00000000}, {0x0000a26c, 0x0ebae9e6}, {0x0000a388, 0x0c000000}, {0x0000a38c, 0x20202020}, {0x0000a390, 0x20202020}, {0x0000a39c, 0x00000001}, {0x0000a3a0, 0x00000000}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0x00000000}, {0x0000a3ac, 0x00000000}, {0x0000a3b0, 0x00000000}, {0x0000a3b4, 0x00000000}, {0x0000a3b8, 0x00000000}, {0x0000a3bc, 0x00000000}, {0x0000a3c0, 0x00000000}, {0x0000a3c4, 0x00000000}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3e4, 0x00000000}, {0x0000a3e8, 0x18c43433}, {0x0000a3ec, 0x00f70081}, {0x0000a3f0, 0x01036a2f}, {0x0000a3f4, 0x00000000}, {0x0000d270, 0x0d820820}, {0x0000d35c, 0x07ffffef}, {0x0000d360, 0x0fffffe7}, {0x0000d364, 0x17ffffe5}, {0x0000d368, 0x1fffffe4}, {0x0000d36c, 0x37ffffe3}, {0x0000d370, 0x3fffffe3}, {0x0000d374, 0x57ffffe3}, {0x0000d378, 0x5fffffe2}, {0x0000d37c, 0x7fffffe2}, {0x0000d380, 0x7f3c7bba}, {0x0000d384, 0xf3307ff0}, }; static const u32 ar9271Modes_9271_ANI_reg[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2}, {0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e}, {0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881}, {0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x0000a208, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8}, {0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d}, {0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, }; static const u32 ar9271Modes_normal_power_tx_gain_9271[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200}, {0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208}, {0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608}, {0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610}, {0x0000a314, 0x00000000, 0x00000000, 0x00024650, 0x00024650}, {0x0000a318, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0}, {0x0000a31c, 0x00000000, 0x00000000, 0x000316d2, 0x000316d2}, {0x0000a320, 0x00000000, 0x00000000, 0x00039758, 0x00039758}, {0x0000a324, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759}, {0x0000a328, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a}, {0x0000a32c, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c}, {0x0000a330, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e}, {0x0000a334, 0x000368de, 0x000368de, 0x0004979f, 0x0004979f}, {0x0000a338, 0x0003891e, 0x0003891e, 0x0004d7df, 0x0004d7df}, {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e}, {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029}, {0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff}, {0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4}, {0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04}, {0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a21c652, 0x0a21c652}, {0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd}, {0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd}, {0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd}, {0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd}, {0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd}, {0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd}, }; static const u32 ar9271Modes_high_power_tx_gain_9271[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000}, {0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200}, {0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201}, {0x0000a30c, 0x00000000, 0x00000000, 0x0001b240, 0x0001b240}, {0x0000a310, 0x00000000, 0x00000000, 0x0001d241, 0x0001d241}, {0x0000a314, 0x00000000, 0x00000000, 0x0001f600, 0x0001f600}, {0x0000a318, 0x00000000, 0x00000000, 0x00022800, 0x00022800}, {0x0000a31c, 0x00000000, 0x00000000, 0x00026802, 0x00026802}, {0x0000a320, 0x00000000, 0x00000000, 0x0002b805, 0x0002b805}, {0x0000a324, 0x00000000, 0x00000000, 0x0002ea41, 0x0002ea41}, {0x0000a328, 0x00000000, 0x00000000, 0x00038b00, 0x00038b00}, {0x0000a32c, 0x00000000, 0x00000000, 0x0003ab40, 0x0003ab40}, {0x0000a330, 0x00000000, 0x00000000, 0x0003cd80, 0x0003cd80}, {0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de}, {0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e}, {0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e}, {0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df}, {0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b}, {0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff}, {0x0000786c, 0x08609eb6, 0x08609eb6, 0x08609eba, 0x08609eba}, {0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00}, {0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a214652, 0x0a214652}, {0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7}, {0x0000a27c, 0x05018063, 0x05038063, 0x05018063, 0x05018063}, {0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63}, {0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063}, {0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63}, {0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063}, }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9002_hw.c0000644000175000017500000003172212026211315024041 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hw.h" #include "ar5008_initvals.h" #include "ar9001_initvals.h" #include "ar9002_initvals.h" #include "ar9002_phy.h" /* General hardware code for the A5008/AR9001/AR9002 hadware families */ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) { if (AR_SREV_9271(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271); INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271); INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg); return; } if (ah->config.pcie_clock_req) INIT_INI_ARRAY(&ah->iniPcieSerdes, ar9280PciePhy_clkreq_off_L1_9280); else INIT_INI_ARRAY(&ah->iniPcieSerdes, ar9280PciePhy_clkreq_always_on_L1_9280); #ifdef CONFIG_PM_SLEEP INIT_INI_ARRAY(&ah->iniPcieSerdesWow, ar9280PciePhy_awow); #endif if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1); INIT_INI_ARRAY(&ah->iniCommon, ar9287Common_9287_1_1); } else if (AR_SREV_9285_12_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285_1_2); INIT_INI_ARRAY(&ah->iniCommon, ar9285Common_9285_1_2); } else if (AR_SREV_9280_20_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9280Modes_9280_2); INIT_INI_ARRAY(&ah->iniCommon, ar9280Common_9280_2); INIT_INI_ARRAY(&ah->iniModesFastClock, ar9280Modes_fast_clock_9280_2); } else if (AR_SREV_9160_10_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar5416Modes_9160); INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9160); if (AR_SREV_9160_11(ah)) { INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9160_1_1); } else { INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9160); } } else if (AR_SREV_9100_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar5416Modes_9100); INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9100); INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6_9100); INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9100); } else { INIT_INI_ARRAY(&ah->iniModes, ar5416Modes); INIT_INI_ARRAY(&ah->iniCommon, ar5416Common); INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC); INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac); } if (!AR_SREV_9280_20_OR_LATER(ah)) { /* Common for AR5416, AR913x, AR9160 */ INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain); INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0); INIT_INI_ARRAY(&ah->iniBank1, ar5416Bank1); INIT_INI_ARRAY(&ah->iniBank2, ar5416Bank2); INIT_INI_ARRAY(&ah->iniBank3, ar5416Bank3); INIT_INI_ARRAY(&ah->iniBank7, ar5416Bank7); /* Common for AR5416, AR9160 */ if (!AR_SREV_9100(ah)) INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6); /* Common for AR913x, AR9160 */ if (!AR_SREV_5416(ah)) INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC_9100); } /* iniAddac needs to be modified for these chips */ if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) { struct ar5416IniArray *addac = &ah->iniAddac; u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns; u32 *data; data = kmalloc(size, GFP_KERNEL); if (!data) return; memcpy(data, addac->ia_array, size); addac->ia_array = data; if (!AR_SREV_5416_22_OR_LATER(ah)) { /* override CLKDRV value */ INI_RA(addac, 31,1) = 0; } } if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniCckfirNormal, ar9287Common_normal_cck_fir_coeff_9287_1_1); INIT_INI_ARRAY(&ah->iniCckfirJapan2484, ar9287Common_japan_2484_cck_fir_coeff_9287_1_1); } } static void ar9280_20_hw_init_rxgain_ini(struct ath_hw *ah) { u32 rxgain_type; if (ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_17) { rxgain_type = ah->eep_ops->get_eeprom(ah, EEP_RXGAIN_TYPE); if (rxgain_type == AR5416_EEP_RXGAIN_13DB_BACKOFF) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9280Modes_backoff_13db_rxgain_9280_2); else if (rxgain_type == AR5416_EEP_RXGAIN_23DB_BACKOFF) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9280Modes_backoff_23db_rxgain_9280_2); else INIT_INI_ARRAY(&ah->iniModesRxGain, ar9280Modes_original_rxgain_9280_2); } else { INIT_INI_ARRAY(&ah->iniModesRxGain, ar9280Modes_original_rxgain_9280_2); } } static void ar9280_20_hw_init_txgain_ini(struct ath_hw *ah, u32 txgain_type) { if (ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_19) { if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9280Modes_high_power_tx_gain_9280_2); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9280Modes_original_tx_gain_9280_2); } else { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9280Modes_original_tx_gain_9280_2); } } static void ar9271_hw_init_txgain_ini(struct ath_hw *ah, u32 txgain_type) { if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9271Modes_high_power_tx_gain_9271); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9271Modes_normal_power_tx_gain_9271); } static void ar9002_hw_init_mode_gain_regs(struct ath_hw *ah) { u32 txgain_type = ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE); if (AR_SREV_9287_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9287Modes_rx_gain_9287_1_1); else if (AR_SREV_9280_20(ah)) ar9280_20_hw_init_rxgain_ini(ah); if (AR_SREV_9271(ah)) { ar9271_hw_init_txgain_ini(ah, txgain_type); } else if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9287Modes_tx_gain_9287_1_1); } else if (AR_SREV_9280_20(ah)) { ar9280_20_hw_init_txgain_ini(ah, txgain_type); } else if (AR_SREV_9285_12_OR_LATER(ah)) { /* txgain table */ if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) { if (AR_SREV_9285E_20(ah)) { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9285Modes_XE2_0_high_power); } else { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9285Modes_high_power_tx_gain_9285_1_2); } } else { if (AR_SREV_9285E_20(ah)) { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9285Modes_XE2_0_normal_power); } else { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9285Modes_original_tx_gain_9285_1_2); } } } } /* * Helper for ASPM support. * * Disable PLL when in L0s as well as receiver clock when in L1. * This power saving option must be enabled through the SerDes. * * Programming the SerDes must go through the same 288 bit serial shift * register as the other analog registers. Hence the 9 writes. */ static void ar9002_hw_configpcipowersave(struct ath_hw *ah, bool power_off) { u8 i; u32 val; /* Nothing to do on restore for 11N */ if (!power_off /* !restore */) { if (AR_SREV_9280_20_OR_LATER(ah)) { /* * AR9280 2.0 or later chips use SerDes values from the * initvals.h initialized depending on chipset during * __ath9k_hw_init() */ for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) { REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0), INI_RA(&ah->iniPcieSerdes, i, 1)); } } else { ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); /* RX shut off when elecidle is asserted */ REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); /* * Ignore ah->ah_config.pcie_clock_req setting for * pre-AR9280 11n */ REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); /* Load the new settings */ REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); REGWRITE_BUFFER_FLUSH(ah); } udelay(1000); } if (power_off) { /* clear bit 19 to disable L1 */ REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); val = REG_READ(ah, AR_WA); /* * Set PCIe workaround bits * In AR9280 and AR9285, bit 14 in WA register (disable L1) * should only be set when device enters D3 and be * cleared when device comes back to D0. */ if (ah->config.pcie_waen) { if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE) val |= AR_WA_D3_L1_DISABLE; } else { if (((AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) && (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) || (AR_SREV_9280(ah) && (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) { val |= AR_WA_D3_L1_DISABLE; } } if (AR_SREV_9280(ah) || AR_SREV_9285(ah) || AR_SREV_9287(ah)) { /* * Disable bit 6 and 7 before entering D3 to * prevent system hang. */ val &= ~(AR_WA_BIT6 | AR_WA_BIT7); } if (AR_SREV_9280(ah)) val |= AR_WA_BIT22; if (AR_SREV_9285E_20(ah)) val |= AR_WA_BIT23; REG_WRITE(ah, AR_WA, val); } else { if (ah->config.pcie_waen) { val = ah->config.pcie_waen; if (!power_off) val &= (~AR_WA_D3_L1_DISABLE); } else { if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) { val = AR9285_WA_DEFAULT; if (!power_off) val &= (~AR_WA_D3_L1_DISABLE); } else if (AR_SREV_9280(ah)) { /* * For AR9280 chips, bit 22 of 0x4004 * needs to be set. */ val = AR9280_WA_DEFAULT; if (!power_off) val &= (~AR_WA_D3_L1_DISABLE); } else { val = AR_WA_DEFAULT; } } /* WAR for ASPM system hang */ if (AR_SREV_9285(ah) || AR_SREV_9287(ah)) val |= (AR_WA_BIT6 | AR_WA_BIT7); if (AR_SREV_9285E_20(ah)) val |= AR_WA_BIT23; REG_WRITE(ah, AR_WA, val); /* set bit 19 to allow forcing of pcie core into L1 state */ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); } } static int ar9002_hw_get_radiorev(struct ath_hw *ah) { u32 val; int i; ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY(0x36), 0x00007058); for (i = 0; i < 8; i++) REG_WRITE(ah, AR_PHY(0x20), 0x00010000); REGWRITE_BUFFER_FLUSH(ah); val = (REG_READ(ah, AR_PHY(256)) >> 24) & 0xff; val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); return ath9k_hw_reverse_bits(val, 8); } int ar9002_hw_rf_claim(struct ath_hw *ah) { u32 val; REG_WRITE(ah, AR_PHY(0), 0x00000007); val = ar9002_hw_get_radiorev(ah); switch (val & AR_RADIO_SREV_MAJOR) { case 0: val = AR_RAD5133_SREV_MAJOR; break; case AR_RAD5133_SREV_MAJOR: case AR_RAD5122_SREV_MAJOR: case AR_RAD2133_SREV_MAJOR: case AR_RAD2122_SREV_MAJOR: break; default: ath_err(ath9k_hw_common(ah), "Radio Chip Rev 0x%02X not supported\n", val & AR_RADIO_SREV_MAJOR); return -EOPNOTSUPP; } ah->hw_version.analog5GhzRev = val; return 0; } void ar9002_hw_enable_async_fifo(struct ath_hw *ah) { if (AR_SREV_9287_13_OR_LATER(ah)) { REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL); REG_SET_BIT(ah, AR_PHY_MODE, AR_PHY_MODE_ASYNCFIFO); REG_CLR_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); } } /* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */ void ar9002_hw_attach_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); priv_ops->init_mode_regs = ar9002_hw_init_mode_regs; priv_ops->init_mode_gain_regs = ar9002_hw_init_mode_gain_regs; ops->config_pci_powersave = ar9002_hw_configpcipowersave; ar5008_hw_attach_phy_ops(ah); if (AR_SREV_9280_20_OR_LATER(ah)) ar9002_hw_attach_phy_ops(ah); ar9002_hw_attach_calib_ops(ah); ar9002_hw_attach_mac_ops(ah); } void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan) { u32 modesIndex; int i; switch (chan->chanmode) { case CHANNEL_A: case CHANNEL_A_HT20: modesIndex = 1; break; case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: modesIndex = 2; break; case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_B: modesIndex = 4; break; case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: modesIndex = 3; break; default: return; } ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < ah->iniModes_9271_ANI_reg.ia_rows; i++) { u32 reg = INI_RA(&ah->iniModes_9271_ANI_reg, i, 0); u32 val = INI_RA(&ah->iniModes_9271_ANI_reg, i, modesIndex); u32 val_orig; if (reg == AR_PHY_CCK_DETECT) { val_orig = REG_READ(ah, reg); val &= AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK; val_orig &= ~AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK; REG_WRITE(ah, reg, val|val_orig); } else REG_WRITE(ah, reg, val); } REGWRITE_BUFFER_FLUSH(ah); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9002_calib.c0000644000175000017500000007052212026211315024476 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "hw-ops.h" #include "ar9002_phy.h" #define AR9285_CLCAL_REDO_THRESH 1 enum ar9002_cal_types { ADC_GAIN_CAL = BIT(0), ADC_DC_CAL = BIT(1), IQ_MISMATCH_CAL = BIT(2), }; static bool ar9002_hw_is_cal_supported(struct ath_hw *ah, struct ath9k_channel *chan, enum ar9002_cal_types cal_type) { bool supported = false; switch (ah->supp_cals & cal_type) { case IQ_MISMATCH_CAL: /* Run IQ Mismatch for non-CCK only */ if (!IS_CHAN_B(chan)) supported = true; break; case ADC_GAIN_CAL: case ADC_DC_CAL: /* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */ if (!IS_CHAN_B(chan) && !((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) && IS_CHAN_HT20(chan))) supported = true; break; } return supported; } static void ar9002_hw_setup_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal) { struct ath_common *common = ath9k_hw_common(ah); REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0), AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, currCal->calData->calCountMax); switch (currCal->calData->calType) { case IQ_MISMATCH_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); ath_dbg(common, CALIBRATE, "starting IQ Mismatch Calibration\n"); break; case ADC_GAIN_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN); ath_dbg(common, CALIBRATE, "starting ADC Gain Calibration\n"); break; case ADC_DC_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER); ath_dbg(common, CALIBRATE, "starting ADC DC Calibration\n"); break; } REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), AR_PHY_TIMING_CTRL4_DO_CAL); } static bool ar9002_hw_per_calibration(struct ath_hw *ah, struct ath9k_channel *ichan, u8 rxchainmask, struct ath9k_cal_list *currCal) { struct ath9k_hw_cal_data *caldata = ah->caldata; bool iscaldone = false; if (currCal->calState == CAL_RUNNING) { if (!(REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) & AR_PHY_TIMING_CTRL4_DO_CAL)) { currCal->calData->calCollect(ah); ah->cal_samples++; if (ah->cal_samples >= currCal->calData->calNumSamples) { int i, numChains = 0; for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (rxchainmask & (1 << i)) numChains++; } currCal->calData->calPostProc(ah, numChains); caldata->CalValid |= currCal->calData->calType; currCal->calState = CAL_DONE; iscaldone = true; } else { ar9002_hw_setup_calibration(ah, currCal); } } } else if (!(caldata->CalValid & currCal->calData->calType)) { ath9k_hw_reset_calibration(ah, currCal); } return iscaldone; } static void ar9002_hw_iqcal_collect(struct ath_hw *ah) { int i; for (i = 0; i < AR5416_MAX_CHAINS; i++) { ah->totalPowerMeasI[i] += REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); ah->totalPowerMeasQ[i] += REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ah->totalIqCorrMeas[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); ath_dbg(ath9k_hw_common(ah), CALIBRATE, "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", ah->cal_samples, i, ah->totalPowerMeasI[i], ah->totalPowerMeasQ[i], ah->totalIqCorrMeas[i]); } } static void ar9002_hw_adc_gaincal_collect(struct ath_hw *ah) { int i; for (i = 0; i < AR5416_MAX_CHAINS; i++) { ah->totalAdcIOddPhase[i] += REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); ah->totalAdcIEvenPhase[i] += REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ah->totalAdcQOddPhase[i] += REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); ah->totalAdcQEvenPhase[i] += REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); ath_dbg(ath9k_hw_common(ah), CALIBRATE, "%d: Chn %d oddi=0x%08x; eveni=0x%08x; oddq=0x%08x; evenq=0x%08x;\n", ah->cal_samples, i, ah->totalAdcIOddPhase[i], ah->totalAdcIEvenPhase[i], ah->totalAdcQOddPhase[i], ah->totalAdcQEvenPhase[i]); } } static void ar9002_hw_adc_dccal_collect(struct ath_hw *ah) { int i; for (i = 0; i < AR5416_MAX_CHAINS; i++) { ah->totalAdcDcOffsetIOddPhase[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); ah->totalAdcDcOffsetIEvenPhase[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ah->totalAdcDcOffsetQOddPhase[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); ah->totalAdcDcOffsetQEvenPhase[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); ath_dbg(ath9k_hw_common(ah), CALIBRATE, "%d: Chn %d oddi=0x%08x; eveni=0x%08x; oddq=0x%08x; evenq=0x%08x;\n", ah->cal_samples, i, ah->totalAdcDcOffsetIOddPhase[i], ah->totalAdcDcOffsetIEvenPhase[i], ah->totalAdcDcOffsetQOddPhase[i], ah->totalAdcDcOffsetQEvenPhase[i]); } } static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) { struct ath_common *common = ath9k_hw_common(ah); u32 powerMeasQ, powerMeasI, iqCorrMeas; u32 qCoffDenom, iCoffDenom; int32_t qCoff, iCoff; int iqCorrNeg, i; for (i = 0; i < numChains; i++) { powerMeasI = ah->totalPowerMeasI[i]; powerMeasQ = ah->totalPowerMeasQ[i]; iqCorrMeas = ah->totalIqCorrMeas[i]; ath_dbg(common, CALIBRATE, "Starting IQ Cal and Correction for Chain %d\n", i); ath_dbg(common, CALIBRATE, "Original: Chn %d iq_corr_meas = 0x%08x\n", i, ah->totalIqCorrMeas[i]); iqCorrNeg = 0; if (iqCorrMeas > 0x80000000) { iqCorrMeas = (0xffffffff - iqCorrMeas) + 1; iqCorrNeg = 1; } ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); ath_dbg(common, CALIBRATE, "iqCorrNeg is 0x%08x\n", iqCorrNeg); iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; qCoffDenom = powerMeasQ / 64; if ((powerMeasQ != 0) && (iCoffDenom != 0) && (qCoffDenom != 0)) { iCoff = iqCorrMeas / iCoffDenom; qCoff = powerMeasI / qCoffDenom - 64; ath_dbg(common, CALIBRATE, "Chn %d iCoff = 0x%08x\n", i, iCoff); ath_dbg(common, CALIBRATE, "Chn %d qCoff = 0x%08x\n", i, qCoff); iCoff = iCoff & 0x3f; ath_dbg(common, CALIBRATE, "New: Chn %d iCoff = 0x%08x\n", i, iCoff); if (iqCorrNeg == 0x0) iCoff = 0x40 - iCoff; if (qCoff > 15) qCoff = 15; else if (qCoff <= -16) qCoff = -16; ath_dbg(common, CALIBRATE, "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", i, iCoff, qCoff); REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff); REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); ath_dbg(common, CALIBRATE, "IQ Cal and Correction done for Chain %d\n", i); } } REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); } static void ar9002_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains) { struct ath_common *common = ath9k_hw_common(ah); u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset, qEvenMeasOffset; u32 qGainMismatch, iGainMismatch, val, i; for (i = 0; i < numChains; i++) { iOddMeasOffset = ah->totalAdcIOddPhase[i]; iEvenMeasOffset = ah->totalAdcIEvenPhase[i]; qOddMeasOffset = ah->totalAdcQOddPhase[i]; qEvenMeasOffset = ah->totalAdcQEvenPhase[i]; ath_dbg(common, CALIBRATE, "Starting ADC Gain Cal for Chain %d\n", i); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_i = 0x%08x\n", i, iOddMeasOffset); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_i = 0x%08x\n", i, iEvenMeasOffset); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_q = 0x%08x\n", i, qOddMeasOffset); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_q = 0x%08x\n", i, qEvenMeasOffset); if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) { iGainMismatch = ((iEvenMeasOffset * 32) / iOddMeasOffset) & 0x3f; qGainMismatch = ((qOddMeasOffset * 32) / qEvenMeasOffset) & 0x3f; ath_dbg(common, CALIBRATE, "Chn %d gain_mismatch_i = 0x%08x\n", i, iGainMismatch); ath_dbg(common, CALIBRATE, "Chn %d gain_mismatch_q = 0x%08x\n", i, qGainMismatch); val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); val &= 0xfffff000; val |= (qGainMismatch) | (iGainMismatch << 6); REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); ath_dbg(common, CALIBRATE, "ADC Gain Cal done for Chain %d\n", i); } } REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0), REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) | AR_PHY_NEW_ADC_GAIN_CORR_ENABLE); } static void ar9002_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains) { struct ath_common *common = ath9k_hw_common(ah); u32 iOddMeasOffset, iEvenMeasOffset, val, i; int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch; const struct ath9k_percal_data *calData = ah->cal_list_curr->calData; u32 numSamples = (1 << (calData->calCountMax + 5)) * calData->calNumSamples; for (i = 0; i < numChains; i++) { iOddMeasOffset = ah->totalAdcDcOffsetIOddPhase[i]; iEvenMeasOffset = ah->totalAdcDcOffsetIEvenPhase[i]; qOddMeasOffset = ah->totalAdcDcOffsetQOddPhase[i]; qEvenMeasOffset = ah->totalAdcDcOffsetQEvenPhase[i]; ath_dbg(common, CALIBRATE, "Starting ADC DC Offset Cal for Chain %d\n", i); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_i = %d\n", i, iOddMeasOffset); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_i = %d\n", i, iEvenMeasOffset); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_q = %d\n", i, qOddMeasOffset); ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_q = %d\n", i, qEvenMeasOffset); iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) / numSamples) & 0x1ff; qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) / numSamples) & 0x1ff; ath_dbg(common, CALIBRATE, "Chn %d dc_offset_mismatch_i = 0x%08x\n", i, iDcMismatch); ath_dbg(common, CALIBRATE, "Chn %d dc_offset_mismatch_q = 0x%08x\n", i, qDcMismatch); val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); val &= 0xc0000fff; val |= (qDcMismatch << 12) | (iDcMismatch << 21); REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); ath_dbg(common, CALIBRATE, "ADC DC Offset Cal done for Chain %d\n", i); } REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0), REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) | AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE); } static void ar9287_hw_olc_temp_compensation(struct ath_hw *ah) { u32 rddata; int32_t delta, currPDADC, slope; rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4); currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); if (ah->initPDADC == 0 || currPDADC == 0) { /* * Zero value indicates that no frames have been transmitted * yet, can't do temperature compensation until frames are * transmitted. */ return; } else { slope = ah->eep_ops->get_eeprom(ah, EEP_TEMPSENSE_SLOPE); if (slope == 0) { /* to avoid divide by zero case */ delta = 0; } else { delta = ((currPDADC - ah->initPDADC)*4) / slope; } REG_RMW_FIELD(ah, AR_PHY_CH0_TX_PWRCTRL11, AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); REG_RMW_FIELD(ah, AR_PHY_CH1_TX_PWRCTRL11, AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); } } static void ar9280_hw_olc_temp_compensation(struct ath_hw *ah) { u32 rddata, i; int delta, currPDADC, regval; rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4); currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); if (ah->initPDADC == 0 || currPDADC == 0) return; if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G)) delta = (currPDADC - ah->initPDADC + 4) / 8; else delta = (currPDADC - ah->initPDADC + 5) / 10; if (delta != ah->PDADCdelta) { ah->PDADCdelta = delta; for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { regval = ah->originalGain[i] - delta; if (regval < 0) regval = 0; REG_RMW_FIELD(ah, AR_PHY_TX_GAIN_TBL1 + i * 4, AR_PHY_TX_GAIN, regval); } } } static void ar9271_hw_pa_cal(struct ath_hw *ah, bool is_reset) { u32 regVal; unsigned int i; u32 regList[][2] = { { 0x786c, 0 }, { 0x7854, 0 }, { 0x7820, 0 }, { 0x7824, 0 }, { 0x7868, 0 }, { 0x783c, 0 }, { 0x7838, 0 } , { 0x7828, 0 } , }; for (i = 0; i < ARRAY_SIZE(regList); i++) regList[i][1] = REG_READ(ah, regList[i][0]); regVal = REG_READ(ah, 0x7834); regVal &= (~(0x1)); REG_WRITE(ah, 0x7834, regVal); regVal = REG_READ(ah, 0x9808); regVal |= (0x1 << 27); REG_WRITE(ah, 0x9808, regVal); /* 786c,b23,1, pwddac=1 */ REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); /* 7854, b5,1, pdrxtxbb=1 */ REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); /* 7854, b7,1, pdv2i=1 */ REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); /* 7854, b8,1, pddacinterface=1 */ REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); /* 7824,b12,0, offcal=0 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); /* 7838, b1,0, pwddb=0 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); /* 7820,b11,0, enpacal=0 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); /* 7820,b25,1, pdpadrv1=0 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); /* 7820,b24,0, pdpadrv2=0 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); /* 7820,b23,0, pdpaout=0 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); /* 783c,b14-16,7, padrvgn2tab_0=7 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); /* * 7838,b29-31,0, padrvgn1tab_0=0 * does not matter since we turn it off */ REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff); /* Set: * localmode=1,bmode=1,bmoderxtx=1,synthon=1, * txon=1,paon=1,oscon=1,synthon_force=1 */ REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); udelay(30); REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0); /* find off_6_1; */ for (i = 6; i > 0; i--) { regVal = REG_READ(ah, 0x7834); regVal |= (1 << (20 + i)); REG_WRITE(ah, 0x7834, regVal); udelay(1); /* regVal = REG_READ(ah, 0x7834); */ regVal &= (~(0x1 << (20 + i))); regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9) << (20 + i)); REG_WRITE(ah, 0x7834, regVal); } regVal = (regVal >> 20) & 0x7f; /* Update PA cal info */ if ((!is_reset) && (ah->pacal_info.prev_offset == regVal)) { if (ah->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT) ah->pacal_info.max_skipcount = 2 * ah->pacal_info.max_skipcount; ah->pacal_info.skipcount = ah->pacal_info.max_skipcount; } else { ah->pacal_info.max_skipcount = 1; ah->pacal_info.skipcount = 0; ah->pacal_info.prev_offset = regVal; } ENABLE_REGWRITE_BUFFER(ah); regVal = REG_READ(ah, 0x7834); regVal |= 0x1; REG_WRITE(ah, 0x7834, regVal); regVal = REG_READ(ah, 0x9808); regVal &= (~(0x1 << 27)); REG_WRITE(ah, 0x9808, regVal); for (i = 0; i < ARRAY_SIZE(regList); i++) REG_WRITE(ah, regList[i][0], regList[i][1]); REGWRITE_BUFFER_FLUSH(ah); } static inline void ar9285_hw_pa_cal(struct ath_hw *ah, bool is_reset) { struct ath_common *common = ath9k_hw_common(ah); u32 regVal; int i, offset, offs_6_1, offs_0; u32 ccomp_org, reg_field; u32 regList[][2] = { { 0x786c, 0 }, { 0x7854, 0 }, { 0x7820, 0 }, { 0x7824, 0 }, { 0x7868, 0 }, { 0x783c, 0 }, { 0x7838, 0 }, }; ath_dbg(common, CALIBRATE, "Running PA Calibration\n"); /* PA CAL is not needed for high power solution */ if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == AR5416_EEP_TXGAIN_HIGH_POWER) return; for (i = 0; i < ARRAY_SIZE(regList); i++) regList[i][1] = REG_READ(ah, regList[i][0]); regVal = REG_READ(ah, 0x7834); regVal &= (~(0x1)); REG_WRITE(ah, 0x7834, regVal); regVal = REG_READ(ah, 0x9808); regVal |= (0x1 << 27); REG_WRITE(ah, 0x9808, regVal); REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); ccomp_org = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP); REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 0xf); REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); udelay(30); REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 0); for (i = 6; i > 0; i--) { regVal = REG_READ(ah, 0x7834); regVal |= (1 << (19 + i)); REG_WRITE(ah, 0x7834, regVal); udelay(1); regVal = REG_READ(ah, 0x7834); regVal &= (~(0x1 << (19 + i))); reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9); regVal |= (reg_field << (19 + i)); REG_WRITE(ah, 0x7834, regVal); } REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 1); udelay(1); reg_field = MS(REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9); REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, reg_field); offs_6_1 = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS); offs_0 = MS(REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP); offset = (offs_6_1<<1) | offs_0; offset = offset - 0; offs_6_1 = offset>>1; offs_0 = offset & 1; if ((!is_reset) && (ah->pacal_info.prev_offset == offset)) { if (ah->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT) ah->pacal_info.max_skipcount = 2 * ah->pacal_info.max_skipcount; ah->pacal_info.skipcount = ah->pacal_info.max_skipcount; } else { ah->pacal_info.max_skipcount = 1; ah->pacal_info.skipcount = 0; ah->pacal_info.prev_offset = offset; } REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1); REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0); regVal = REG_READ(ah, 0x7834); regVal |= 0x1; REG_WRITE(ah, 0x7834, regVal); regVal = REG_READ(ah, 0x9808); regVal &= (~(0x1 << 27)); REG_WRITE(ah, 0x9808, regVal); for (i = 0; i < ARRAY_SIZE(regList); i++) REG_WRITE(ah, regList[i][0], regList[i][1]); REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org); } static void ar9002_hw_pa_cal(struct ath_hw *ah, bool is_reset) { if (AR_SREV_9271(ah)) { if (is_reset || !ah->pacal_info.skipcount) ar9271_hw_pa_cal(ah, is_reset); else ah->pacal_info.skipcount--; } else if (AR_SREV_9285_12_OR_LATER(ah)) { if (is_reset || !ah->pacal_info.skipcount) ar9285_hw_pa_cal(ah, is_reset); else ah->pacal_info.skipcount--; } } static void ar9002_hw_olc_temp_compensation(struct ath_hw *ah) { if (OLC_FOR_AR9287_10_LATER) ar9287_hw_olc_temp_compensation(ah); else if (OLC_FOR_AR9280_20_LATER) ar9280_hw_olc_temp_compensation(ah); } static bool ar9002_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, u8 rxchainmask, bool longcal) { bool iscaldone = true; struct ath9k_cal_list *currCal = ah->cal_list_curr; bool nfcal, nfcal_pending = false; nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF); if (ah->caldata) nfcal_pending = ah->caldata->nfcal_pending; if (currCal && !nfcal && (currCal->calState == CAL_RUNNING || currCal->calState == CAL_WAITING)) { iscaldone = ar9002_hw_per_calibration(ah, chan, rxchainmask, currCal); if (iscaldone) { ah->cal_list_curr = currCal = currCal->calNext; if (currCal->calState == CAL_WAITING) { iscaldone = false; ath9k_hw_reset_calibration(ah, currCal); } } } /* Do NF cal only at longer intervals */ if (longcal || nfcal_pending) { /* * Get the value from the previous NF cal and update * history buffer. */ if (ath9k_hw_getnf(ah, chan)) { /* * Load the NF from history buffer of the current * channel. * NF is slow time-variant, so it is OK to use a * historical value. */ ath9k_hw_loadnf(ah, ah->curchan); } if (longcal) { ath9k_hw_start_nfcal(ah, false); /* Do periodic PAOffset Cal */ ar9002_hw_pa_cal(ah, false); ar9002_hw_olc_temp_compensation(ah); } } return iscaldone; } /* Carrier leakage Calibration fix */ static bool ar9285_hw_cl_cal(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); if (IS_CHAN_HT20(chan)) { REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); REG_SET_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); REG_CLR_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { ath_dbg(common, CALIBRATE, "offset calibration failed to complete in 1ms; noisy environment?\n"); return false; } REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); } REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); REG_SET_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { ath_dbg(common, CALIBRATE, "offset calibration failed to complete in 1ms; noisy environment?\n"); return false; } REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); return true; } static bool ar9285_hw_clc(struct ath_hw *ah, struct ath9k_channel *chan) { int i; u_int32_t txgain_max; u_int32_t clc_gain, gain_mask = 0, clc_num = 0; u_int32_t reg_clc_I0, reg_clc_Q0; u_int32_t i0_num = 0; u_int32_t q0_num = 0; u_int32_t total_num = 0; u_int32_t reg_rf2g5_org; bool retv = true; if (!(ar9285_hw_cl_cal(ah, chan))) return false; txgain_max = MS(REG_READ(ah, AR_PHY_TX_PWRCTRL7), AR_PHY_TX_PWRCTRL_TX_GAIN_TAB_MAX); for (i = 0; i < (txgain_max+1); i++) { clc_gain = (REG_READ(ah, (AR_PHY_TX_GAIN_TBL1+(i<<2))) & AR_PHY_TX_GAIN_CLC) >> AR_PHY_TX_GAIN_CLC_S; if (!(gain_mask & (1 << clc_gain))) { gain_mask |= (1 << clc_gain); clc_num++; } } for (i = 0; i < clc_num; i++) { reg_clc_I0 = (REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) & AR_PHY_CLC_I0) >> AR_PHY_CLC_I0_S; reg_clc_Q0 = (REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) & AR_PHY_CLC_Q0) >> AR_PHY_CLC_Q0_S; if (reg_clc_I0 == 0) i0_num++; if (reg_clc_Q0 == 0) q0_num++; } total_num = i0_num + q0_num; if (total_num > AR9285_CLCAL_REDO_THRESH) { reg_rf2g5_org = REG_READ(ah, AR9285_RF2G5); if (AR_SREV_9285E_20(ah)) { REG_WRITE(ah, AR9285_RF2G5, (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | AR9285_RF2G5_IC50TX_XE_SET); } else { REG_WRITE(ah, AR9285_RF2G5, (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | AR9285_RF2G5_IC50TX_SET); } retv = ar9285_hw_cl_cal(ah, chan); REG_WRITE(ah, AR9285_RF2G5, reg_rf2g5_org); } return retv; } static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); if (AR_SREV_9271(ah)) { if (!ar9285_hw_cl_cal(ah, chan)) return false; } else if (AR_SREV_9285(ah) && AR_SREV_9285_12_OR_LATER(ah)) { if (!ar9285_hw_clc(ah, chan)) return false; } else { if (AR_SREV_9280_20_OR_LATER(ah)) { if (!AR_SREV_9287_11_OR_LATER(ah)) REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); } /* Calibrate the AGC */ REG_WRITE(ah, AR_PHY_AGC_CONTROL, REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration complete */ if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { ath_dbg(common, CALIBRATE, "offset calibration failed to complete in 1ms; noisy environment?\n"); return false; } if (AR_SREV_9280_20_OR_LATER(ah)) { if (!AR_SREV_9287_11_OR_LATER(ah)) REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); } } /* Do PA Calibration */ ar9002_hw_pa_cal(ah, true); if (ah->caldata) ah->caldata->nfcal_pending = true; ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; /* Enable IQ, ADC Gain and ADC DC offset CALs */ if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) { ah->supp_cals = IQ_MISMATCH_CAL; if (AR_SREV_9160_10_OR_LATER(ah)) ah->supp_cals |= ADC_GAIN_CAL | ADC_DC_CAL; if (AR_SREV_9287(ah)) ah->supp_cals &= ~ADC_GAIN_CAL; if (ar9002_hw_is_cal_supported(ah, chan, ADC_GAIN_CAL)) { INIT_CAL(&ah->adcgain_caldata); INSERT_CAL(ah, &ah->adcgain_caldata); ath_dbg(common, CALIBRATE, "enabling ADC Gain Calibration\n"); } if (ar9002_hw_is_cal_supported(ah, chan, ADC_DC_CAL)) { INIT_CAL(&ah->adcdc_caldata); INSERT_CAL(ah, &ah->adcdc_caldata); ath_dbg(common, CALIBRATE, "enabling ADC DC Calibration\n"); } if (ar9002_hw_is_cal_supported(ah, chan, IQ_MISMATCH_CAL)) { INIT_CAL(&ah->iq_caldata); INSERT_CAL(ah, &ah->iq_caldata); ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); } ah->cal_list_curr = ah->cal_list; if (ah->cal_list_curr) ath9k_hw_reset_calibration(ah, ah->cal_list_curr); } if (ah->caldata) ah->caldata->CalValid = 0; return true; } static const struct ath9k_percal_data iq_cal_multi_sample = { IQ_MISMATCH_CAL, MAX_CAL_SAMPLES, PER_MIN_LOG_COUNT, ar9002_hw_iqcal_collect, ar9002_hw_iqcalibrate }; static const struct ath9k_percal_data iq_cal_single_sample = { IQ_MISMATCH_CAL, MIN_CAL_SAMPLES, PER_MAX_LOG_COUNT, ar9002_hw_iqcal_collect, ar9002_hw_iqcalibrate }; static const struct ath9k_percal_data adc_gain_cal_multi_sample = { ADC_GAIN_CAL, MAX_CAL_SAMPLES, PER_MIN_LOG_COUNT, ar9002_hw_adc_gaincal_collect, ar9002_hw_adc_gaincal_calibrate }; static const struct ath9k_percal_data adc_gain_cal_single_sample = { ADC_GAIN_CAL, MIN_CAL_SAMPLES, PER_MAX_LOG_COUNT, ar9002_hw_adc_gaincal_collect, ar9002_hw_adc_gaincal_calibrate }; static const struct ath9k_percal_data adc_dc_cal_multi_sample = { ADC_DC_CAL, MAX_CAL_SAMPLES, PER_MIN_LOG_COUNT, ar9002_hw_adc_dccal_collect, ar9002_hw_adc_dccal_calibrate }; static const struct ath9k_percal_data adc_dc_cal_single_sample = { ADC_DC_CAL, MIN_CAL_SAMPLES, PER_MAX_LOG_COUNT, ar9002_hw_adc_dccal_collect, ar9002_hw_adc_dccal_calibrate }; static void ar9002_hw_init_cal_settings(struct ath_hw *ah) { if (AR_SREV_9100(ah)) { ah->iq_caldata.calData = &iq_cal_multi_sample; ah->supp_cals = IQ_MISMATCH_CAL; return; } if (AR_SREV_9160_10_OR_LATER(ah)) { if (AR_SREV_9280_20_OR_LATER(ah)) { ah->iq_caldata.calData = &iq_cal_single_sample; ah->adcgain_caldata.calData = &adc_gain_cal_single_sample; ah->adcdc_caldata.calData = &adc_dc_cal_single_sample; } else { ah->iq_caldata.calData = &iq_cal_multi_sample; ah->adcgain_caldata.calData = &adc_gain_cal_multi_sample; ah->adcdc_caldata.calData = &adc_dc_cal_multi_sample; } ah->supp_cals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; if (AR_SREV_9287(ah)) ah->supp_cals &= ~ADC_GAIN_CAL; } } void ar9002_hw_attach_calib_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); priv_ops->init_cal_settings = ar9002_hw_init_cal_settings; priv_ops->init_cal = ar9002_hw_init_cal; priv_ops->setup_calibration = ar9002_hw_setup_calibration; ops->calibrate = ar9002_hw_calibrate; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar9001_initvals.h0000644000175000017500000010433512026211315025261 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ static const u32 ar5416Modes_9100[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300}, {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007}, {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x00009850, 0x6c48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6c48b0e2}, {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e}, {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20}, {0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00}, {0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0}, {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081}, {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d}, {0x00009940, 0x00750604, 0x00754604, 0xfff81204, 0xfff81204}, {0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020}, {0x00009954, 0x5f3ca3de, 0x5f3ca3de, 0xe250a51e, 0xe250a51e}, {0x00009958, 0x2108ecff, 0x2108ecff, 0x3388ffff, 0x3388ffff}, {0x00009960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0}, {0x0000a960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0}, {0x0000b960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0}, {0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120}, {0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a1000, 0x001a0c00}, {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be}, {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77}, {0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329}, {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8}, {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384}, {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880}, {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788}, {0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120}, {0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120}, {0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120}, {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a}, {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa}, {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000}, {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402}, {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06}, {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b}, {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b}, {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a}, {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf}, {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f}, {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f}, {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f}, {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000}, {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar5416Common_9100[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000008}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00000054, 0x0000001f}, {0x00000800, 0x00000000}, {0x00000804, 0x00000000}, {0x00000808, 0x00000000}, {0x0000080c, 0x00000000}, {0x00000810, 0x00000000}, {0x00000814, 0x00000000}, {0x00000818, 0x00000000}, {0x0000081c, 0x00000000}, {0x00000820, 0x00000000}, {0x00000824, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x00001230, 0x00000000}, {0x00001270, 0x00000000}, {0x00001038, 0x00000000}, {0x00001078, 0x00000000}, {0x000010b8, 0x00000000}, {0x000010f8, 0x00000000}, {0x00001138, 0x00000000}, {0x00001178, 0x00000000}, {0x000011b8, 0x00000000}, {0x000011f8, 0x00000000}, {0x00001238, 0x00000000}, {0x00001278, 0x00000000}, {0x000012b8, 0x00000000}, {0x000012f8, 0x00000000}, {0x00001338, 0x00000000}, {0x00001378, 0x00000000}, {0x000013b8, 0x00000000}, {0x000013f8, 0x00000000}, {0x00001438, 0x00000000}, {0x00001478, 0x00000000}, {0x000014b8, 0x00000000}, {0x000014f8, 0x00000000}, {0x00001538, 0x00000000}, {0x00001578, 0x00000000}, {0x000015b8, 0x00000000}, {0x000015f8, 0x00000000}, {0x00001638, 0x00000000}, {0x00001678, 0x00000000}, {0x000016b8, 0x00000000}, {0x000016f8, 0x00000000}, {0x00001738, 0x00000000}, {0x00001778, 0x00000000}, {0x000017b8, 0x00000000}, {0x000017f8, 0x00000000}, {0x0000103c, 0x00000000}, {0x0000107c, 0x00000000}, {0x000010bc, 0x00000000}, {0x000010fc, 0x00000000}, {0x0000113c, 0x00000000}, {0x0000117c, 0x00000000}, {0x000011bc, 0x00000000}, {0x000011fc, 0x00000000}, {0x0000123c, 0x00000000}, {0x0000127c, 0x00000000}, {0x000012bc, 0x00000000}, {0x000012fc, 0x00000000}, {0x0000133c, 0x00000000}, {0x0000137c, 0x00000000}, {0x000013bc, 0x00000000}, {0x000013fc, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00020010, 0x00000003}, {0x00020038, 0x000004c2}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000700}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008048, 0x40000000}, {0x00008054, 0x00004000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x000080c0, 0x2a82301a}, {0x000080c4, 0x05dc01e0}, {0x000080c8, 0x1f402710}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00001e00}, {0x000080d4, 0x00000000}, {0x000080d8, 0x00400000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x003f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080f8, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00020000}, {0x00008104, 0x00000001}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000168}, {0x00008118, 0x000100aa}, {0x0000811c, 0x00003210}, {0x00008120, 0x08f04800}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x00000000}, {0x00008144, 0x00000000}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x32143320}, {0x00008174, 0xfaa4fa50}, {0x00008178, 0x00000100}, {0x0000817c, 0x00000000}, {0x000081c4, 0x00000000}, {0x000081d0, 0x00003210}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008200, 0x00000000}, {0x00008204, 0x00000000}, {0x00008208, 0x00000000}, {0x0000820c, 0x00000000}, {0x00008210, 0x00000000}, {0x00008214, 0x00000000}, {0x00008218, 0x00000000}, {0x0000821c, 0x00000000}, {0x00008220, 0x00000000}, {0x00008224, 0x00000000}, {0x00008228, 0x00000000}, {0x0000822c, 0x00000000}, {0x00008230, 0x00000000}, {0x00008234, 0x00000000}, {0x00008238, 0x00000000}, {0x0000823c, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000100}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x400000ff}, {0x00008260, 0x00080922}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000000}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x00000000}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x00008300, 0x00000000}, {0x00008304, 0x00000000}, {0x00008308, 0x00000000}, {0x0000830c, 0x00000000}, {0x00008310, 0x00000000}, {0x00008314, 0x00000000}, {0x00008318, 0x00000000}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000e00}, {0x00008338, 0x00000000}, {0x0000833c, 0x00000000}, {0x00008340, 0x000107ff}, {0x00009808, 0x00000000}, {0x0000980c, 0xad848e19}, {0x00009810, 0x7d14e000}, {0x00009814, 0x9c0a9f6b}, {0x0000981c, 0x00000000}, {0x0000982c, 0x0000a000}, {0x00009830, 0x00000000}, {0x0000983c, 0x00200400}, {0x00009840, 0x206a01ae}, {0x0000984c, 0x1284233c}, {0x00009854, 0x00000859}, {0x00009900, 0x00000000}, {0x00009904, 0x00000000}, {0x00009908, 0x00000000}, {0x0000990c, 0x00000000}, {0x0000991c, 0x10000fff}, {0x00009920, 0x05100000}, {0x0000a920, 0x05100000}, {0x0000b920, 0x05100000}, {0x00009928, 0x00000001}, {0x0000992c, 0x00000004}, {0x00009934, 0x1e1f2022}, {0x00009938, 0x0a0b0c0d}, {0x0000993c, 0x00000000}, {0x00009948, 0x9280b212}, {0x0000994c, 0x00020028}, {0x0000c95c, 0x004b6a8e}, {0x0000c968, 0x000003ce}, {0x00009970, 0x190fb515}, {0x00009974, 0x00000000}, {0x00009978, 0x00000001}, {0x0000997c, 0x00000000}, {0x00009980, 0x00000000}, {0x00009984, 0x00000000}, {0x00009988, 0x00000000}, {0x0000998c, 0x00000000}, {0x00009990, 0x00000000}, {0x00009994, 0x00000000}, {0x00009998, 0x00000000}, {0x0000999c, 0x00000000}, {0x000099a0, 0x00000000}, {0x000099a4, 0x00000001}, {0x000099a8, 0x201fff00}, {0x000099ac, 0x006f0000}, {0x000099b0, 0x03051000}, {0x000099dc, 0x00000000}, {0x000099e0, 0x00000200}, {0x000099e4, 0xaaaaaaaa}, {0x000099e8, 0x3c466478}, {0x000099ec, 0x0cc80caa}, {0x000099fc, 0x00001042}, {0x00009b00, 0x00000000}, {0x00009b04, 0x00000001}, {0x00009b08, 0x00000002}, {0x00009b0c, 0x00000003}, {0x00009b10, 0x00000004}, {0x00009b14, 0x00000005}, {0x00009b18, 0x00000008}, {0x00009b1c, 0x00000009}, {0x00009b20, 0x0000000a}, {0x00009b24, 0x0000000b}, {0x00009b28, 0x0000000c}, {0x00009b2c, 0x0000000d}, {0x00009b30, 0x00000010}, {0x00009b34, 0x00000011}, {0x00009b38, 0x00000012}, {0x00009b3c, 0x00000013}, {0x00009b40, 0x00000014}, {0x00009b44, 0x00000015}, {0x00009b48, 0x00000018}, {0x00009b4c, 0x00000019}, {0x00009b50, 0x0000001a}, {0x00009b54, 0x0000001b}, {0x00009b58, 0x0000001c}, {0x00009b5c, 0x0000001d}, {0x00009b60, 0x00000020}, {0x00009b64, 0x00000021}, {0x00009b68, 0x00000022}, {0x00009b6c, 0x00000023}, {0x00009b70, 0x00000024}, {0x00009b74, 0x00000025}, {0x00009b78, 0x00000028}, {0x00009b7c, 0x00000029}, {0x00009b80, 0x0000002a}, {0x00009b84, 0x0000002b}, {0x00009b88, 0x0000002c}, {0x00009b8c, 0x0000002d}, {0x00009b90, 0x00000030}, {0x00009b94, 0x00000031}, {0x00009b98, 0x00000032}, {0x00009b9c, 0x00000033}, {0x00009ba0, 0x00000034}, {0x00009ba4, 0x00000035}, {0x00009ba8, 0x00000035}, {0x00009bac, 0x00000035}, {0x00009bb0, 0x00000035}, {0x00009bb4, 0x00000035}, {0x00009bb8, 0x00000035}, {0x00009bbc, 0x00000035}, {0x00009bc0, 0x00000035}, {0x00009bc4, 0x00000035}, {0x00009bc8, 0x00000035}, {0x00009bcc, 0x00000035}, {0x00009bd0, 0x00000035}, {0x00009bd4, 0x00000035}, {0x00009bd8, 0x00000035}, {0x00009bdc, 0x00000035}, {0x00009be0, 0x00000035}, {0x00009be4, 0x00000035}, {0x00009be8, 0x00000035}, {0x00009bec, 0x00000035}, {0x00009bf0, 0x00000035}, {0x00009bf4, 0x00000035}, {0x00009bf8, 0x00000010}, {0x00009bfc, 0x0000001a}, {0x0000a210, 0x40806333}, {0x0000a214, 0x00106c10}, {0x0000a218, 0x009c4060}, {0x0000a220, 0x018830c6}, {0x0000a224, 0x00000400}, {0x0000a228, 0x001a0bb5}, {0x0000a22c, 0x00000000}, {0x0000a234, 0x20202020}, {0x0000a238, 0x20202020}, {0x0000a23c, 0x13c889af}, {0x0000a240, 0x38490a20}, {0x0000a244, 0x00007bb6}, {0x0000a248, 0x0fff3ffc}, {0x0000a24c, 0x00000001}, {0x0000a250, 0x0000e000}, {0x0000a254, 0x00000000}, {0x0000a258, 0x0cc75380}, {0x0000a25c, 0x0f0f0f01}, {0x0000a260, 0xdfa91f01}, {0x0000a268, 0x00000001}, {0x0000a26c, 0x0ebae9c6}, {0x0000b26c, 0x0ebae9c6}, {0x0000c26c, 0x0ebae9c6}, {0x0000d270, 0x00820820}, {0x0000a278, 0x1ce739ce}, {0x0000a27c, 0x050701ce}, {0x0000a338, 0x00000000}, {0x0000a33c, 0x00000000}, {0x0000a340, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a348, 0x3fffffff}, {0x0000a34c, 0x3fffffff}, {0x0000a350, 0x3fffffff}, {0x0000a354, 0x0003ffff}, {0x0000a358, 0x79a8aa33}, {0x0000d35c, 0x07ffffef}, {0x0000d360, 0x0fffffe7}, {0x0000d364, 0x17ffffe5}, {0x0000d368, 0x1fffffe4}, {0x0000d36c, 0x37ffffe3}, {0x0000d370, 0x3fffffe3}, {0x0000d374, 0x57ffffe3}, {0x0000d378, 0x5fffffe2}, {0x0000d37c, 0x7fffffe2}, {0x0000d380, 0x7f3c7bba}, {0x0000d384, 0xf3307ff0}, {0x0000a388, 0x0c000000}, {0x0000a38c, 0x20202020}, {0x0000a390, 0x20202020}, {0x0000a394, 0x1ce739ce}, {0x0000a398, 0x000001ce}, {0x0000a39c, 0x00000001}, {0x0000a3a0, 0x00000000}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0x00000000}, {0x0000a3ac, 0x00000000}, {0x0000a3b0, 0x00000000}, {0x0000a3b4, 0x00000000}, {0x0000a3b8, 0x00000000}, {0x0000a3bc, 0x00000000}, {0x0000a3c0, 0x00000000}, {0x0000a3c4, 0x00000000}, {0x0000a3c8, 0x00000246}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3dc, 0x1ce739ce}, {0x0000a3e0, 0x000001ce}, }; static const u32 ar5416Bank6_9100[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00e00000, 0x00e00000}, {0x0000989c, 0x005e0000, 0x005e0000}, {0x0000989c, 0x00120000, 0x00120000}, {0x0000989c, 0x00620000, 0x00620000}, {0x0000989c, 0x00020000, 0x00020000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x005f0000, 0x005f0000}, {0x0000989c, 0x00870000, 0x00870000}, {0x0000989c, 0x00f90000, 0x00f90000}, {0x0000989c, 0x007b0000, 0x007b0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00f50000, 0x00f50000}, {0x0000989c, 0x00dc0000, 0x00dc0000}, {0x0000989c, 0x00110000, 0x00110000}, {0x0000989c, 0x006100a8, 0x006100a8}, {0x0000989c, 0x004210a2, 0x004210a2}, {0x0000989c, 0x0014000f, 0x0014000f}, {0x0000989c, 0x00c40002, 0x00c40002}, {0x0000989c, 0x003000f2, 0x003000f2}, {0x0000989c, 0x00440016, 0x00440016}, {0x0000989c, 0x00410040, 0x00410040}, {0x0000989c, 0x000180d6, 0x000180d6}, {0x0000989c, 0x0000c0aa, 0x0000c0aa}, {0x0000989c, 0x000000b1, 0x000000b1}, {0x0000989c, 0x00002000, 0x00002000}, {0x0000989c, 0x000000d4, 0x000000d4}, {0x000098d0, 0x0000000f, 0x0010000f}, }; static const u32 ar5416Bank6TPC_9100[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00e00000, 0x00e00000}, {0x0000989c, 0x005e0000, 0x005e0000}, {0x0000989c, 0x00120000, 0x00120000}, {0x0000989c, 0x00620000, 0x00620000}, {0x0000989c, 0x00020000, 0x00020000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x40ff0000, 0x40ff0000}, {0x0000989c, 0x005f0000, 0x005f0000}, {0x0000989c, 0x00870000, 0x00870000}, {0x0000989c, 0x00f90000, 0x00f90000}, {0x0000989c, 0x007b0000, 0x007b0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00f50000, 0x00f50000}, {0x0000989c, 0x00dc0000, 0x00dc0000}, {0x0000989c, 0x00110000, 0x00110000}, {0x0000989c, 0x006100a8, 0x006100a8}, {0x0000989c, 0x00423022, 0x00423022}, {0x0000989c, 0x2014008f, 0x2014008f}, {0x0000989c, 0x00c40002, 0x00c40002}, {0x0000989c, 0x003000f2, 0x003000f2}, {0x0000989c, 0x00440016, 0x00440016}, {0x0000989c, 0x00410040, 0x00410040}, {0x0000989c, 0x0001805e, 0x0001805e}, {0x0000989c, 0x0000c0ab, 0x0000c0ab}, {0x0000989c, 0x000000e1, 0x000000e1}, {0x0000989c, 0x00007080, 0x00007080}, {0x0000989c, 0x000000d4, 0x000000d4}, {0x000098d0, 0x0000000f, 0x0010000f}, }; static const u32 ar5416Addac_9100[][2] = { /* Addr allmodes */ {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000010}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x000000c0}, {0x0000989c, 0x00000015}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x000098cc, 0x00000000}, }; static const u32 ar5416Modes_9160[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300}, {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007}, {0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x00009850, 0x6c48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6c48b0e2}, {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e}, {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, {0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20}, {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00}, {0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0}, {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081}, {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, {0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d}, {0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020}, {0x00009960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40}, {0x0000a960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40}, {0x0000b960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40}, {0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120}, {0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x000099bc, 0x001a0600, 0x001a0600, 0x001a0c00, 0x001a0c00}, {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be}, {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77}, {0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329}, {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8}, {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384}, {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880}, {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788}, {0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120}, {0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120}, {0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120}, {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a}, {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa}, {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000}, {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402}, {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06}, {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b}, {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b}, {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a}, {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf}, {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f}, {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f}, {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f}, {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000}, {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar5416Common_9160[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000008}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00000054, 0x0000001f}, {0x00000800, 0x00000000}, {0x00000804, 0x00000000}, {0x00000808, 0x00000000}, {0x0000080c, 0x00000000}, {0x00000810, 0x00000000}, {0x00000814, 0x00000000}, {0x00000818, 0x00000000}, {0x0000081c, 0x00000000}, {0x00000820, 0x00000000}, {0x00000824, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x00001230, 0x00000000}, {0x00001270, 0x00000000}, {0x00001038, 0x00000000}, {0x00001078, 0x00000000}, {0x000010b8, 0x00000000}, {0x000010f8, 0x00000000}, {0x00001138, 0x00000000}, {0x00001178, 0x00000000}, {0x000011b8, 0x00000000}, {0x000011f8, 0x00000000}, {0x00001238, 0x00000000}, {0x00001278, 0x00000000}, {0x000012b8, 0x00000000}, {0x000012f8, 0x00000000}, {0x00001338, 0x00000000}, {0x00001378, 0x00000000}, {0x000013b8, 0x00000000}, {0x000013f8, 0x00000000}, {0x00001438, 0x00000000}, {0x00001478, 0x00000000}, {0x000014b8, 0x00000000}, {0x000014f8, 0x00000000}, {0x00001538, 0x00000000}, {0x00001578, 0x00000000}, {0x000015b8, 0x00000000}, {0x000015f8, 0x00000000}, {0x00001638, 0x00000000}, {0x00001678, 0x00000000}, {0x000016b8, 0x00000000}, {0x000016f8, 0x00000000}, {0x00001738, 0x00000000}, {0x00001778, 0x00000000}, {0x000017b8, 0x00000000}, {0x000017f8, 0x00000000}, {0x0000103c, 0x00000000}, {0x0000107c, 0x00000000}, {0x000010bc, 0x00000000}, {0x000010fc, 0x00000000}, {0x0000113c, 0x00000000}, {0x0000117c, 0x00000000}, {0x000011bc, 0x00000000}, {0x000011fc, 0x00000000}, {0x0000123c, 0x00000000}, {0x0000127c, 0x00000000}, {0x000012bc, 0x00000000}, {0x000012fc, 0x00000000}, {0x0000133c, 0x00000000}, {0x0000137c, 0x00000000}, {0x000013bc, 0x00000000}, {0x000013fc, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00004030, 0x00000002}, {0x0000403c, 0x00000002}, {0x00007010, 0x00000020}, {0x00007038, 0x000004c2}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000700}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008048, 0x40000000}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x000080c0, 0x2a82301a}, {0x000080c4, 0x05dc01e0}, {0x000080c8, 0x1f402710}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00001e00}, {0x000080d4, 0x00000000}, {0x000080d8, 0x00400000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x003f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080f8, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00020000}, {0x00008104, 0x00000001}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000168}, {0x00008118, 0x000100aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x00000000}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x32143320}, {0x00008174, 0xfaa4fa50}, {0x00008178, 0x00000100}, {0x0000817c, 0x00000000}, {0x000081c4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008200, 0x00000000}, {0x00008204, 0x00000000}, {0x00008208, 0x00000000}, {0x0000820c, 0x00000000}, {0x00008210, 0x00000000}, {0x00008214, 0x00000000}, {0x00008218, 0x00000000}, {0x0000821c, 0x00000000}, {0x00008220, 0x00000000}, {0x00008224, 0x00000000}, {0x00008228, 0x00000000}, {0x0000822c, 0x00000000}, {0x00008230, 0x00000000}, {0x00008234, 0x00000000}, {0x00008238, 0x00000000}, {0x0000823c, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000100}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x400000ff}, {0x00008260, 0x00080922}, {0x00008264, 0x88a00010}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000000}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x00000000}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x00008300, 0x00000000}, {0x00008304, 0x00000000}, {0x00008308, 0x00000000}, {0x0000830c, 0x00000000}, {0x00008310, 0x00000000}, {0x00008314, 0x00000000}, {0x00008318, 0x00000000}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000e00}, {0x00008338, 0x00ff0000}, {0x0000833c, 0x00000000}, {0x00008340, 0x000107ff}, {0x00009808, 0x00000000}, {0x0000980c, 0xad848e19}, {0x00009810, 0x7d14e000}, {0x00009814, 0x9c0a9f6b}, {0x0000981c, 0x00000000}, {0x0000982c, 0x0000a000}, {0x00009830, 0x00000000}, {0x0000983c, 0x00200400}, {0x00009840, 0x206a01ae}, {0x0000984c, 0x1284233c}, {0x00009854, 0x00000859}, {0x00009900, 0x00000000}, {0x00009904, 0x00000000}, {0x00009908, 0x00000000}, {0x0000990c, 0x00000000}, {0x0000991c, 0x10000fff}, {0x00009920, 0x05100000}, {0x0000a920, 0x05100000}, {0x0000b920, 0x05100000}, {0x00009928, 0x00000001}, {0x0000992c, 0x00000004}, {0x00009934, 0x1e1f2022}, {0x00009938, 0x0a0b0c0d}, {0x0000993c, 0x00000000}, {0x00009948, 0x9280b212}, {0x0000994c, 0x00020028}, {0x00009954, 0x5f3ca3de}, {0x00009958, 0x2108ecff}, {0x00009940, 0x00750604}, {0x0000c95c, 0x004b6a8e}, {0x00009970, 0x190fb515}, {0x00009974, 0x00000000}, {0x00009978, 0x00000001}, {0x0000997c, 0x00000000}, {0x00009980, 0x00000000}, {0x00009984, 0x00000000}, {0x00009988, 0x00000000}, {0x0000998c, 0x00000000}, {0x00009990, 0x00000000}, {0x00009994, 0x00000000}, {0x00009998, 0x00000000}, {0x0000999c, 0x00000000}, {0x000099a0, 0x00000000}, {0x000099a4, 0x00000001}, {0x000099a8, 0x201fff00}, {0x000099ac, 0x006f0000}, {0x000099b0, 0x03051000}, {0x000099dc, 0x00000000}, {0x000099e0, 0x00000200}, {0x000099e4, 0xaaaaaaaa}, {0x000099e8, 0x3c466478}, {0x000099ec, 0x0cc80caa}, {0x000099fc, 0x00001042}, {0x00009b00, 0x00000000}, {0x00009b04, 0x00000001}, {0x00009b08, 0x00000002}, {0x00009b0c, 0x00000003}, {0x00009b10, 0x00000004}, {0x00009b14, 0x00000005}, {0x00009b18, 0x00000008}, {0x00009b1c, 0x00000009}, {0x00009b20, 0x0000000a}, {0x00009b24, 0x0000000b}, {0x00009b28, 0x0000000c}, {0x00009b2c, 0x0000000d}, {0x00009b30, 0x00000010}, {0x00009b34, 0x00000011}, {0x00009b38, 0x00000012}, {0x00009b3c, 0x00000013}, {0x00009b40, 0x00000014}, {0x00009b44, 0x00000015}, {0x00009b48, 0x00000018}, {0x00009b4c, 0x00000019}, {0x00009b50, 0x0000001a}, {0x00009b54, 0x0000001b}, {0x00009b58, 0x0000001c}, {0x00009b5c, 0x0000001d}, {0x00009b60, 0x00000020}, {0x00009b64, 0x00000021}, {0x00009b68, 0x00000022}, {0x00009b6c, 0x00000023}, {0x00009b70, 0x00000024}, {0x00009b74, 0x00000025}, {0x00009b78, 0x00000028}, {0x00009b7c, 0x00000029}, {0x00009b80, 0x0000002a}, {0x00009b84, 0x0000002b}, {0x00009b88, 0x0000002c}, {0x00009b8c, 0x0000002d}, {0x00009b90, 0x00000030}, {0x00009b94, 0x00000031}, {0x00009b98, 0x00000032}, {0x00009b9c, 0x00000033}, {0x00009ba0, 0x00000034}, {0x00009ba4, 0x00000035}, {0x00009ba8, 0x00000035}, {0x00009bac, 0x00000035}, {0x00009bb0, 0x00000035}, {0x00009bb4, 0x00000035}, {0x00009bb8, 0x00000035}, {0x00009bbc, 0x00000035}, {0x00009bc0, 0x00000035}, {0x00009bc4, 0x00000035}, {0x00009bc8, 0x00000035}, {0x00009bcc, 0x00000035}, {0x00009bd0, 0x00000035}, {0x00009bd4, 0x00000035}, {0x00009bd8, 0x00000035}, {0x00009bdc, 0x00000035}, {0x00009be0, 0x00000035}, {0x00009be4, 0x00000035}, {0x00009be8, 0x00000035}, {0x00009bec, 0x00000035}, {0x00009bf0, 0x00000035}, {0x00009bf4, 0x00000035}, {0x00009bf8, 0x00000010}, {0x00009bfc, 0x0000001a}, {0x0000a210, 0x40806333}, {0x0000a214, 0x00106c10}, {0x0000a218, 0x009c4060}, {0x0000a220, 0x018830c6}, {0x0000a224, 0x00000400}, {0x0000a228, 0x001a0bb5}, {0x0000a22c, 0x00000000}, {0x0000a234, 0x20202020}, {0x0000a238, 0x20202020}, {0x0000a23c, 0x13c889af}, {0x0000a240, 0x38490a20}, {0x0000a244, 0x00007bb6}, {0x0000a248, 0x0fff3ffc}, {0x0000a24c, 0x00000001}, {0x0000a250, 0x0000e000}, {0x0000a254, 0x00000000}, {0x0000a258, 0x0cc75380}, {0x0000a25c, 0x0f0f0f01}, {0x0000a260, 0xdfa91f01}, {0x0000a268, 0x00000001}, {0x0000a26c, 0x0e79e5c6}, {0x0000b26c, 0x0e79e5c6}, {0x0000c26c, 0x0e79e5c6}, {0x0000d270, 0x00820820}, {0x0000a278, 0x1ce739ce}, {0x0000a27c, 0x050701ce}, {0x0000a338, 0x00000000}, {0x0000a33c, 0x00000000}, {0x0000a340, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a348, 0x3fffffff}, {0x0000a34c, 0x3fffffff}, {0x0000a350, 0x3fffffff}, {0x0000a354, 0x0003ffff}, {0x0000a358, 0x79bfaa03}, {0x0000d35c, 0x07ffffef}, {0x0000d360, 0x0fffffe7}, {0x0000d364, 0x17ffffe5}, {0x0000d368, 0x1fffffe4}, {0x0000d36c, 0x37ffffe3}, {0x0000d370, 0x3fffffe3}, {0x0000d374, 0x57ffffe3}, {0x0000d378, 0x5fffffe2}, {0x0000d37c, 0x7fffffe2}, {0x0000d380, 0x7f3c7bba}, {0x0000d384, 0xf3307ff0}, {0x0000a388, 0x0c000000}, {0x0000a38c, 0x20202020}, {0x0000a390, 0x20202020}, {0x0000a394, 0x1ce739ce}, {0x0000a398, 0x000001ce}, {0x0000a39c, 0x00000001}, {0x0000a3a0, 0x00000000}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0x00000000}, {0x0000a3ac, 0x00000000}, {0x0000a3b0, 0x00000000}, {0x0000a3b4, 0x00000000}, {0x0000a3b8, 0x00000000}, {0x0000a3bc, 0x00000000}, {0x0000a3c0, 0x00000000}, {0x0000a3c4, 0x00000000}, {0x0000a3c8, 0x00000246}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3dc, 0x1ce739ce}, {0x0000a3e0, 0x000001ce}, }; static const u32 ar5416Addac_9160[][2] = { /* Addr allmodes */ {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x000000c0}, {0x0000989c, 0x00000018}, {0x0000989c, 0x00000004}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x000000c0}, {0x0000989c, 0x00000019}, {0x0000989c, 0x00000004}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000004}, {0x0000989c, 0x00000003}, {0x0000989c, 0x00000008}, {0x0000989c, 0x00000000}, {0x000098cc, 0x00000000}, }; static const u32 ar5416Addac_9160_1_1[][2] = { /* Addr allmodes */ {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x000000c0}, {0x0000989c, 0x00000018}, {0x0000989c, 0x00000004}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x000000c0}, {0x0000989c, 0x00000019}, {0x0000989c, 0x00000004}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x000098cc, 0x00000000}, }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar5008_phy.c0000644000175000017500000011774412026211315024236 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hw.h" #include "hw-ops.h" #include "../regd.h" #include "ar9002_phy.h" /* All code below is for AR5008, AR9001, AR9002 */ static const int firstep_table[] = /* level: 0 1 2 3 4 5 6 7 8 */ { -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */ static const int cycpwrThr1_table[] = /* level: 0 1 2 3 4 5 6 7 8 */ { -6, -4, -2, 0, 2, 4, 6, 8 }; /* lvl 0-7, default 3 */ /* * register values to turn OFDM weak signal detection OFF */ static const int m1ThreshLow_off = 127; static const int m2ThreshLow_off = 127; static const int m1Thresh_off = 127; static const int m2Thresh_off = 127; static const int m2CountThr_off = 31; static const int m2CountThrLow_off = 63; static const int m1ThreshLowExt_off = 127; static const int m2ThreshLowExt_off = 127; static const int m1ThreshExt_off = 127; static const int m2ThreshExt_off = 127; static void ar5008_rf_bank_setup(u32 *bank, struct ar5416IniArray *array, int col) { int i; for (i = 0; i < array->ia_rows; i++) bank[i] = INI_RA(array, i, col); } #define REG_WRITE_RF_ARRAY(iniarray, regData, regWr) \ ar5008_write_rf_array(ah, iniarray, regData, &(regWr)) static void ar5008_write_rf_array(struct ath_hw *ah, struct ar5416IniArray *array, u32 *data, unsigned int *writecnt) { int r; ENABLE_REGWRITE_BUFFER(ah); for (r = 0; r < array->ia_rows; r++) { REG_WRITE(ah, INI_RA(array, r, 0), data[r]); DO_DELAY(*writecnt); } REGWRITE_BUFFER_FLUSH(ah); } /** * ar5008_hw_phy_modify_rx_buffer() - perform analog swizzling of parameters * @rfbuf: * @reg32: * @numBits: * @firstBit: * @column: * * Performs analog "swizzling" of parameters into their location. * Used on external AR2133/AR5133 radios. */ static void ar5008_hw_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32, u32 numBits, u32 firstBit, u32 column) { u32 tmp32, mask, arrayEntry, lastBit; int32_t bitPosition, bitsLeft; tmp32 = ath9k_hw_reverse_bits(reg32, numBits); arrayEntry = (firstBit - 1) / 8; bitPosition = (firstBit - 1) % 8; bitsLeft = numBits; while (bitsLeft > 0) { lastBit = (bitPosition + bitsLeft > 8) ? 8 : bitPosition + bitsLeft; mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << (column * 8); rfBuf[arrayEntry] &= ~mask; rfBuf[arrayEntry] |= ((tmp32 << bitPosition) << (column * 8)) & mask; bitsLeft -= 8 - bitPosition; tmp32 = tmp32 >> (8 - bitPosition); bitPosition = 0; arrayEntry++; } } /* * Fix on 2.4 GHz band for orientation sensitivity issue by increasing * rf_pwd_icsyndiv. * * Theoretical Rules: * if 2 GHz band * if forceBiasAuto * if synth_freq < 2412 * bias = 0 * else if 2412 <= synth_freq <= 2422 * bias = 1 * else // synth_freq > 2422 * bias = 2 * else if forceBias > 0 * bias = forceBias & 7 * else * no change, use value from ini file * else * no change, invalid band * * 1st Mod: * 2422 also uses value of 2 * * * 2nd Mod: * Less than 2412 uses value of 0, 2412 and above uses value of 2 */ static void ar5008_hw_force_bias(struct ath_hw *ah, u16 synth_freq) { struct ath_common *common = ath9k_hw_common(ah); u32 tmp_reg; int reg_writes = 0; u32 new_bias = 0; if (!AR_SREV_5416(ah) || synth_freq >= 3000) return; BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); if (synth_freq < 2412) new_bias = 0; else if (synth_freq < 2422) new_bias = 1; else new_bias = 2; /* pre-reverse this field */ tmp_reg = ath9k_hw_reverse_bits(new_bias, 3); ath_dbg(common, CONFIG, "Force rf_pwd_icsyndiv to %1d on %4d\n", new_bias, synth_freq); /* swizzle rf_pwd_icsyndiv */ ar5008_hw_phy_modify_rx_buffer(ah->analogBank6Data, tmp_reg, 3, 181, 3); /* write Bank 6 with new params */ REG_WRITE_RF_ARRAY(&ah->iniBank6, ah->analogBank6Data, reg_writes); } /** * ar5008_hw_set_channel - tune to a channel on the external AR2133/AR5133 radios * @ah: atheros hardware structure * @chan: * * For the external AR2133/AR5133 radios, takes the MHz channel value and set * the channel value. Assumes writes enabled to analog bus and bank6 register * cache in ah->analogBank6Data. */ static int ar5008_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); u32 channelSel = 0; u32 bModeSynth = 0; u32 aModeRefSel = 0; u32 reg32 = 0; u16 freq; struct chan_centers centers; ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; if (freq < 4800) { u32 txctl; if (((freq - 2192) % 5) == 0) { channelSel = ((freq - 672) * 2 - 3040) / 10; bModeSynth = 0; } else if (((freq - 2224) % 5) == 0) { channelSel = ((freq - 704) * 2 - 3040) / 10; bModeSynth = 1; } else { ath_err(common, "Invalid channel %u MHz\n", freq); return -EINVAL; } channelSel = (channelSel << 2) & 0xff; channelSel = ath9k_hw_reverse_bits(channelSel, 8); txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); if (freq == 2484) { REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl | AR_PHY_CCK_TX_CTRL_JAPAN); } else { REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); } } else if ((freq % 20) == 0 && freq >= 5120) { channelSel = ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8); aModeRefSel = ath9k_hw_reverse_bits(1, 2); } else if ((freq % 10) == 0) { channelSel = ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8); if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) aModeRefSel = ath9k_hw_reverse_bits(2, 2); else aModeRefSel = ath9k_hw_reverse_bits(1, 2); } else if ((freq % 5) == 0) { channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8); aModeRefSel = ath9k_hw_reverse_bits(1, 2); } else { ath_err(common, "Invalid channel %u MHz\n", freq); return -EINVAL; } ar5008_hw_force_bias(ah, freq); reg32 = (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) | (1 << 5) | 0x1; REG_WRITE(ah, AR_PHY(0x37), reg32); ah->curchan = chan; return 0; } /** * ar5008_hw_spur_mitigate - convert baseband spur frequency for external radios * @ah: atheros hardware structure * @chan: * * For non single-chip solutions. Converts to baseband spur frequency given the * input channel frequency and compute register settings below. */ static void ar5008_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan) { int bb_spur = AR_NO_SPUR; int bin, cur_bin; int spur_freq_sd; int spur_delta_phase; int denominator; int upper, lower, cur_vit_mask; int tmp, new; int i; static int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 }; static int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 }; static int inc[4] = { 0, 100, 0, 0 }; int8_t mask_m[123]; int8_t mask_p[123]; int8_t mask_amt; int tmp_mask; int cur_bb_spur; bool is2GHz = IS_CHAN_2GHZ(chan); memset(&mask_m, 0, sizeof(int8_t) * 123); memset(&mask_p, 0, sizeof(int8_t) * 123); for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); if (AR_NO_SPUR == cur_bb_spur) break; cur_bb_spur = cur_bb_spur - (chan->channel * 10); if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) { bb_spur = cur_bb_spur; break; } } if (AR_NO_SPUR == bb_spur) return; bin = bb_spur * 32; tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), new); new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | AR_PHY_SPUR_REG_ENABLE_MASK_PPM | AR_PHY_SPUR_REG_MASK_RATE_SELECT | AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); REG_WRITE(ah, AR_PHY_SPUR_REG, new); spur_delta_phase = ((bb_spur * 524288) / 100) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; denominator = IS_CHAN_2GHZ(chan) ? 440 : 400; spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff; new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); REG_WRITE(ah, AR_PHY_TIMING11, new); cur_bin = -6000; upper = bin + 100; lower = bin - 100; for (i = 0; i < 4; i++) { int pilot_mask = 0; int chan_mask = 0; int bp = 0; for (bp = 0; bp < 30; bp++) { if ((cur_bin > lower) && (cur_bin < upper)) { pilot_mask = pilot_mask | 0x1 << bp; chan_mask = chan_mask | 0x1 << bp; } cur_bin += 100; } cur_bin += inc[i]; REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); REG_WRITE(ah, chan_mask_reg[i], chan_mask); } cur_vit_mask = 6100; upper = bin + 120; lower = bin - 120; for (i = 0; i < 123; i++) { if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { /* workaround for gcc bug #37014 */ volatile int tmp_v = abs(cur_vit_mask - bin); if (tmp_v < 75) mask_amt = 1; else mask_amt = 0; if (cur_vit_mask < 0) mask_m[abs(cur_vit_mask / 100)] = mask_amt; else mask_p[cur_vit_mask / 100] = mask_amt; } cur_vit_mask -= 100; } tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) | (mask_m[48] << 26) | (mask_m[49] << 24) | (mask_m[50] << 22) | (mask_m[51] << 20) | (mask_m[52] << 18) | (mask_m[53] << 16) | (mask_m[54] << 14) | (mask_m[55] << 12) | (mask_m[56] << 10) | (mask_m[57] << 8) | (mask_m[58] << 6) | (mask_m[59] << 4) | (mask_m[60] << 2) | (mask_m[61] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); tmp_mask = (mask_m[31] << 28) | (mask_m[32] << 26) | (mask_m[33] << 24) | (mask_m[34] << 22) | (mask_m[35] << 20) | (mask_m[36] << 18) | (mask_m[37] << 16) | (mask_m[48] << 14) | (mask_m[39] << 12) | (mask_m[40] << 10) | (mask_m[41] << 8) | (mask_m[42] << 6) | (mask_m[43] << 4) | (mask_m[44] << 2) | (mask_m[45] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) | (mask_m[18] << 26) | (mask_m[18] << 24) | (mask_m[20] << 22) | (mask_m[20] << 20) | (mask_m[22] << 18) | (mask_m[22] << 16) | (mask_m[24] << 14) | (mask_m[24] << 12) | (mask_m[25] << 10) | (mask_m[26] << 8) | (mask_m[27] << 6) | (mask_m[28] << 4) | (mask_m[29] << 2) | (mask_m[30] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) | (mask_m[2] << 26) | (mask_m[3] << 24) | (mask_m[4] << 22) | (mask_m[5] << 20) | (mask_m[6] << 18) | (mask_m[7] << 16) | (mask_m[8] << 14) | (mask_m[9] << 12) | (mask_m[10] << 10) | (mask_m[11] << 8) | (mask_m[12] << 6) | (mask_m[13] << 4) | (mask_m[14] << 2) | (mask_m[15] << 0); REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); tmp_mask = (mask_p[15] << 28) | (mask_p[14] << 26) | (mask_p[13] << 24) | (mask_p[12] << 22) | (mask_p[11] << 20) | (mask_p[10] << 18) | (mask_p[9] << 16) | (mask_p[8] << 14) | (mask_p[7] << 12) | (mask_p[6] << 10) | (mask_p[5] << 8) | (mask_p[4] << 6) | (mask_p[3] << 4) | (mask_p[2] << 2) | (mask_p[1] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); tmp_mask = (mask_p[30] << 28) | (mask_p[29] << 26) | (mask_p[28] << 24) | (mask_p[27] << 22) | (mask_p[26] << 20) | (mask_p[25] << 18) | (mask_p[24] << 16) | (mask_p[23] << 14) | (mask_p[22] << 12) | (mask_p[21] << 10) | (mask_p[20] << 8) | (mask_p[19] << 6) | (mask_p[18] << 4) | (mask_p[17] << 2) | (mask_p[16] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); tmp_mask = (mask_p[45] << 28) | (mask_p[44] << 26) | (mask_p[43] << 24) | (mask_p[42] << 22) | (mask_p[41] << 20) | (mask_p[40] << 18) | (mask_p[39] << 16) | (mask_p[38] << 14) | (mask_p[37] << 12) | (mask_p[36] << 10) | (mask_p[35] << 8) | (mask_p[34] << 6) | (mask_p[33] << 4) | (mask_p[32] << 2) | (mask_p[31] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) | (mask_p[59] << 26) | (mask_p[58] << 24) | (mask_p[57] << 22) | (mask_p[56] << 20) | (mask_p[55] << 18) | (mask_p[54] << 16) | (mask_p[53] << 14) | (mask_p[52] << 12) | (mask_p[51] << 10) | (mask_p[50] << 8) | (mask_p[49] << 6) | (mask_p[48] << 4) | (mask_p[47] << 2) | (mask_p[46] << 0); REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); } /** * ar5008_hw_rf_alloc_ext_banks - allocates banks for external radio programming * @ah: atheros hardware structure * * Only required for older devices with external AR2133/AR5133 radios. */ static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah) { #define ATH_ALLOC_BANK(bank, size) do { \ bank = kzalloc((sizeof(u32) * size), GFP_KERNEL); \ if (!bank) { \ ath_err(common, "Cannot allocate RF banks\n"); \ return -ENOMEM; \ } \ } while (0); struct ath_common *common = ath9k_hw_common(ah); BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); ATH_ALLOC_BANK(ah->analogBank0Data, ah->iniBank0.ia_rows); ATH_ALLOC_BANK(ah->analogBank1Data, ah->iniBank1.ia_rows); ATH_ALLOC_BANK(ah->analogBank2Data, ah->iniBank2.ia_rows); ATH_ALLOC_BANK(ah->analogBank3Data, ah->iniBank3.ia_rows); ATH_ALLOC_BANK(ah->analogBank6Data, ah->iniBank6.ia_rows); ATH_ALLOC_BANK(ah->analogBank6TPCData, ah->iniBank6TPC.ia_rows); ATH_ALLOC_BANK(ah->analogBank7Data, ah->iniBank7.ia_rows); ATH_ALLOC_BANK(ah->bank6Temp, ah->iniBank6.ia_rows); return 0; #undef ATH_ALLOC_BANK } /** * ar5008_hw_rf_free_ext_banks - Free memory for analog bank scratch buffers * @ah: atheros hardware struture * For the external AR2133/AR5133 radios banks. */ static void ar5008_hw_rf_free_ext_banks(struct ath_hw *ah) { #define ATH_FREE_BANK(bank) do { \ kfree(bank); \ bank = NULL; \ } while (0); BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); ATH_FREE_BANK(ah->analogBank0Data); ATH_FREE_BANK(ah->analogBank1Data); ATH_FREE_BANK(ah->analogBank2Data); ATH_FREE_BANK(ah->analogBank3Data); ATH_FREE_BANK(ah->analogBank6Data); ATH_FREE_BANK(ah->analogBank6TPCData); ATH_FREE_BANK(ah->analogBank7Data); ATH_FREE_BANK(ah->bank6Temp); #undef ATH_FREE_BANK } /* * * ar5008_hw_set_rf_regs - programs rf registers based on EEPROM * @ah: atheros hardware structure * @chan: * @modesIndex: * * Used for the external AR2133/AR5133 radios. * * Reads the EEPROM header info from the device structure and programs * all rf registers. This routine requires access to the analog * rf device. This is not required for single-chip devices. */ static bool ar5008_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, u16 modesIndex) { u32 eepMinorRev; u32 ob5GHz = 0, db5GHz = 0; u32 ob2GHz = 0, db2GHz = 0; int regWrites = 0; /* * Software does not need to program bank data * for single chip devices, that is AR9280 or anything * after that. */ if (AR_SREV_9280_20_OR_LATER(ah)) return true; /* Setup rf parameters */ eepMinorRev = ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV); /* Setup Bank 0 Write */ ar5008_rf_bank_setup(ah->analogBank0Data, &ah->iniBank0, 1); /* Setup Bank 1 Write */ ar5008_rf_bank_setup(ah->analogBank1Data, &ah->iniBank1, 1); /* Setup Bank 2 Write */ ar5008_rf_bank_setup(ah->analogBank2Data, &ah->iniBank2, 1); /* Setup Bank 6 Write */ ar5008_rf_bank_setup(ah->analogBank3Data, &ah->iniBank3, modesIndex); { int i; for (i = 0; i < ah->iniBank6TPC.ia_rows; i++) { ah->analogBank6Data[i] = INI_RA(&ah->iniBank6TPC, i, modesIndex); } } /* Only the 5 or 2 GHz OB/DB need to be set for a mode */ if (eepMinorRev >= 2) { if (IS_CHAN_2GHZ(chan)) { ob2GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_2); db2GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_2); ar5008_hw_phy_modify_rx_buffer(ah->analogBank6Data, ob2GHz, 3, 197, 0); ar5008_hw_phy_modify_rx_buffer(ah->analogBank6Data, db2GHz, 3, 194, 0); } else { ob5GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_5); db5GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_5); ar5008_hw_phy_modify_rx_buffer(ah->analogBank6Data, ob5GHz, 3, 203, 0); ar5008_hw_phy_modify_rx_buffer(ah->analogBank6Data, db5GHz, 3, 200, 0); } } /* Setup Bank 7 Setup */ ar5008_rf_bank_setup(ah->analogBank7Data, &ah->iniBank7, 1); /* Write Analog registers */ REG_WRITE_RF_ARRAY(&ah->iniBank0, ah->analogBank0Data, regWrites); REG_WRITE_RF_ARRAY(&ah->iniBank1, ah->analogBank1Data, regWrites); REG_WRITE_RF_ARRAY(&ah->iniBank2, ah->analogBank2Data, regWrites); REG_WRITE_RF_ARRAY(&ah->iniBank3, ah->analogBank3Data, regWrites); REG_WRITE_RF_ARRAY(&ah->iniBank6TPC, ah->analogBank6Data, regWrites); REG_WRITE_RF_ARRAY(&ah->iniBank7, ah->analogBank7Data, regWrites); return true; } static void ar5008_hw_init_bb(struct ath_hw *ah, struct ath9k_channel *chan) { u32 synthDelay; synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); ath9k_hw_synth_delay(ah, chan, synthDelay); } static void ar5008_hw_init_chain_masks(struct ath_hw *ah) { int rx_chainmask, tx_chainmask; rx_chainmask = ah->rxchainmask; tx_chainmask = ah->txchainmask; switch (rx_chainmask) { case 0x5: REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); case 0x3: if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) { REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); break; } case 0x1: case 0x2: case 0x7: ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); break; default: ENABLE_REGWRITE_BUFFER(ah); break; } REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); REGWRITE_BUFFER_FLUSH(ah); if (tx_chainmask == 0x5) { REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); } if (AR_SREV_9100(ah)) REG_WRITE(ah, AR_PHY_ANALOG_SWAP, REG_READ(ah, AR_PHY_ANALOG_SWAP) | 0x00000001); } static void ar5008_hw_override_ini(struct ath_hw *ah, struct ath9k_channel *chan) { u32 val; /* * Set the RX_ABORT and RX_DIS and clear if off only after * RXE is set for MAC. This prevents frames with corrupted * descriptor status. */ REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); if (AR_SREV_9280_20_OR_LATER(ah)) { val = REG_READ(ah, AR_PCU_MISC_MODE2); if (!AR_SREV_9271(ah)) val &= ~AR_PCU_MISC_MODE2_HWWAR1; if (AR_SREV_9287_11_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); REG_WRITE(ah, AR_PCU_MISC_MODE2, val); } REG_SET_BIT(ah, AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); if (AR_SREV_9280_20_OR_LATER(ah)) return; /* * Disable BB clock gating * Necessary to avoid issues on AR5416 2.0 */ REG_WRITE(ah, 0x9800 + (651 << 2), 0x11); /* * Disable RIFS search on some chips to avoid baseband * hang issues. */ if (AR_SREV_9100(ah) || AR_SREV_9160(ah)) { val = REG_READ(ah, AR_PHY_HEAVY_CLIP_FACTOR_RIFS); val &= ~AR_PHY_RIFS_INIT_DELAY; REG_WRITE(ah, AR_PHY_HEAVY_CLIP_FACTOR_RIFS, val); } } static void ar5008_hw_set_channel_regs(struct ath_hw *ah, struct ath9k_channel *chan) { u32 phymode; u32 enableDacFifo = 0; if (AR_SREV_9285_12_OR_LATER(ah)) enableDacFifo = (REG_READ(ah, AR_PHY_TURBO) & AR_PHY_FC_ENABLE_DAC_FIFO); phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH | enableDacFifo; if (IS_CHAN_HT40(chan)) { phymode |= AR_PHY_FC_DYN2040_EN; if ((chan->chanmode == CHANNEL_A_HT40PLUS) || (chan->chanmode == CHANNEL_G_HT40PLUS)) phymode |= AR_PHY_FC_DYN2040_PRI_CH; } REG_WRITE(ah, AR_PHY_TURBO, phymode); ath9k_hw_set11nmac2040(ah); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); REGWRITE_BUFFER_FLUSH(ah); } static int ar5008_hw_process_ini(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); int i, regWrites = 0; u32 modesIndex, freqIndex; switch (chan->chanmode) { case CHANNEL_A: case CHANNEL_A_HT20: modesIndex = 1; freqIndex = 1; break; case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: modesIndex = 2; freqIndex = 1; break; case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_B: modesIndex = 4; freqIndex = 2; break; case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: modesIndex = 3; freqIndex = 2; break; default: return -EINVAL; } /* * Set correct baseband to analog shift setting to * access analog chips. */ REG_WRITE(ah, AR_PHY(0), 0x00000007); /* Write ADDAC shifts */ REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); if (ah->eep_ops->set_addac) ah->eep_ops->set_addac(ah, chan); REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites); REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < ah->iniModes.ia_rows; i++) { u32 reg = INI_RA(&ah->iniModes, i, 0); u32 val = INI_RA(&ah->iniModes, i, modesIndex); if (reg == AR_AN_TOP2 && ah->need_an_top2_fixup) val &= ~AR_AN_TOP2_PWDCLKIND; REG_WRITE(ah, reg, val); if (reg >= 0x7800 && reg < 0x78a0 && ah->config.analog_shiftreg && (common->bus_ops->ath_bus_type != ATH_USB)) { udelay(100); } DO_DELAY(regWrites); } REGWRITE_BUFFER_FLUSH(ah); if (AR_SREV_9280(ah) || AR_SREV_9287_11_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesRxGain, modesIndex, regWrites); if (AR_SREV_9280(ah) || AR_SREV_9285_12_OR_LATER(ah) || AR_SREV_9287_11_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); if (AR_SREV_9271_10(ah)) { REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENA); REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_ADC_ON, 0xa); } ENABLE_REGWRITE_BUFFER(ah); /* Write common array parameters */ for (i = 0; i < ah->iniCommon.ia_rows; i++) { u32 reg = INI_RA(&ah->iniCommon, i, 0); u32 val = INI_RA(&ah->iniCommon, i, 1); REG_WRITE(ah, reg, val); if (reg >= 0x7800 && reg < 0x78a0 && ah->config.analog_shiftreg && (common->bus_ops->ath_bus_type != ATH_USB)) { udelay(100); } DO_DELAY(regWrites); } REGWRITE_BUFFER_FLUSH(ah); REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites); if (IS_CHAN_A_FAST_CLOCK(ah, chan)) REG_WRITE_ARRAY(&ah->iniModesFastClock, modesIndex, regWrites); ar5008_hw_override_ini(ah, chan); ar5008_hw_set_channel_regs(ah, chan); ar5008_hw_init_chain_masks(ah); ath9k_olc_init(ah); ath9k_hw_apply_txpower(ah, chan, false); /* Write analog registers */ if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { ath_err(ath9k_hw_common(ah), "ar5416SetRfRegs failed\n"); return -EIO; } return 0; } static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan) { u32 rfMode = 0; if (chan == NULL) return; rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; if (!AR_SREV_9280_20_OR_LATER(ah)) rfMode |= (IS_CHAN_5GHZ(chan)) ? AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ; if (IS_CHAN_A_FAST_CLOCK(ah, chan)) rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); REG_WRITE(ah, AR_PHY_MODE, rfMode); } static void ar5008_hw_mark_phy_inactive(struct ath_hw *ah) { REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); } static void ar5008_hw_set_delta_slope(struct ath_hw *ah, struct ath9k_channel *chan) { u32 coef_scaled, ds_coef_exp, ds_coef_man; u32 clockMhzScaled = 0x64000000; struct chan_centers centers; if (IS_CHAN_HALF_RATE(chan)) clockMhzScaled = clockMhzScaled >> 1; else if (IS_CHAN_QUARTER_RATE(chan)) clockMhzScaled = clockMhzScaled >> 2; ath9k_hw_get_channel_centers(ah, chan, ¢ers); coef_scaled = clockMhzScaled / centers.synth_center; ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); coef_scaled = (9 * coef_scaled) / 10; ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_MAN, ds_coef_man); REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_EXP, ds_coef_exp); } static bool ar5008_hw_rfbus_req(struct ath_hw *ah) { REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); return ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN, AH_WAIT_TIMEOUT); } static void ar5008_hw_rfbus_done(struct ath_hw *ah) { u32 synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; ath9k_hw_synth_delay(ah, ah->curchan, synthDelay); REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); } static void ar5008_restore_chainmask(struct ath_hw *ah) { int rx_chainmask = ah->rxchainmask; if ((rx_chainmask == 0x5) || (rx_chainmask == 0x3)) { REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); } } static u32 ar9160_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { u32 pll; pll = SM(0x5, AR_RTC_9160_PLL_REFDIV); if (chan && IS_CHAN_HALF_RATE(chan)) pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL); else if (chan && IS_CHAN_QUARTER_RATE(chan)) pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL); if (chan && IS_CHAN_5GHZ(chan)) pll |= SM(0x50, AR_RTC_9160_PLL_DIV); else pll |= SM(0x58, AR_RTC_9160_PLL_DIV); return pll; } static u32 ar5008_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { u32 pll; pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2; if (chan && IS_CHAN_HALF_RATE(chan)) pll |= SM(0x1, AR_RTC_PLL_CLKSEL); else if (chan && IS_CHAN_QUARTER_RATE(chan)) pll |= SM(0x2, AR_RTC_PLL_CLKSEL); if (chan && IS_CHAN_5GHZ(chan)) pll |= SM(0xa, AR_RTC_PLL_DIV); else pll |= SM(0xb, AR_RTC_PLL_DIV); return pll; } static bool ar5008_hw_ani_control_new(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ar5416AniState *aniState = &chan->ani; s32 value, value2; switch (cmd & ah->ani_function) { case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ /* * on == 1 means ofdm weak signal detection is ON * on == 1 is the default, for less noise immunity * * on == 0 means ofdm weak signal detection is OFF * on == 0 means more noise imm */ u32 on = param ? 1 : 0; /* * make register setting for default * (weak sig detect ON) come from INI file */ int m1ThreshLow = on ? aniState->iniDef.m1ThreshLow : m1ThreshLow_off; int m2ThreshLow = on ? aniState->iniDef.m2ThreshLow : m2ThreshLow_off; int m1Thresh = on ? aniState->iniDef.m1Thresh : m1Thresh_off; int m2Thresh = on ? aniState->iniDef.m2Thresh : m2Thresh_off; int m2CountThr = on ? aniState->iniDef.m2CountThr : m2CountThr_off; int m2CountThrLow = on ? aniState->iniDef.m2CountThrLow : m2CountThrLow_off; int m1ThreshLowExt = on ? aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off; int m2ThreshLowExt = on ? aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off; int m1ThreshExt = on ? aniState->iniDef.m1ThreshExt : m1ThreshExt_off; int m2ThreshExt = on ? aniState->iniDef.m2ThreshExt : m2ThreshExt_off; REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow); REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow); REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH, m1Thresh); REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH, m2Thresh); REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR, m2CountThr); REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow); REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt); REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt); REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt); REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt); if (on) REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); else REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); if (on != aniState->ofdmWeakSigDetect) { ath_dbg(common, ANI, "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; aniState->ofdmWeakSigDetect = on; } break; } case ATH9K_ANI_FIRSTEP_LEVEL:{ u32 level = param; if (level >= ARRAY_SIZE(firstep_table)) { ath_dbg(common, ANI, "ATH9K_ANI_FIRSTEP_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(firstep_table)); return false; } /* * make register setting relative to default * from INI file & cap value */ value = firstep_table[level] - firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstep; if (value < ATH9K_SIG_FIRSTEP_SETTING_MIN) value = ATH9K_SIG_FIRSTEP_SETTING_MIN; if (value > ATH9K_SIG_FIRSTEP_SETTING_MAX) value = ATH9K_SIG_FIRSTEP_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, value); /* * we need to set first step low register too * make register setting relative to default * from INI file & cap value */ value2 = firstep_table[level] - firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstepLow; if (value2 < ATH9K_SIG_FIRSTEP_SETTING_MIN) value2 = ATH9K_SIG_FIRSTEP_SETTING_MIN; if (value2 > ATH9K_SIG_FIRSTEP_SETTING_MAX) value2 = ATH9K_SIG_FIRSTEP_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_FIRSTEP_LOW, value2); if (level != aniState->firstepLevel) { ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, level, ATH9K_ANI_FIRSTEP_LVL, value, aniState->iniDef.firstep); ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] firstep_low[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, level, ATH9K_ANI_FIRSTEP_LVL, value2, aniState->iniDef.firstepLow); if (level > aniState->firstepLevel) ah->stats.ast_ani_stepup++; else if (level < aniState->firstepLevel) ah->stats.ast_ani_stepdown++; aniState->firstepLevel = level; } break; } case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{ u32 level = param; if (level >= ARRAY_SIZE(cycpwrThr1_table)) { ath_dbg(common, ANI, "ATH9K_ANI_SPUR_IMMUNITY_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(cycpwrThr1_table)); return false; } /* * make register setting relative to default * from INI file & cap value */ value = cycpwrThr1_table[level] - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1; if (value < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value = ATH9K_SIG_SPUR_IMM_SETTING_MIN; if (value > ATH9K_SIG_SPUR_IMM_SETTING_MAX) value = ATH9K_SIG_SPUR_IMM_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, value); /* * set AR_PHY_EXT_CCA for extension channel * make register setting relative to default * from INI file & cap value */ value2 = cycpwrThr1_table[level] - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1Ext; if (value2 < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value2 = ATH9K_SIG_SPUR_IMM_SETTING_MIN; if (value2 > ATH9K_SIG_SPUR_IMM_SETTING_MAX) value2 = ATH9K_SIG_SPUR_IMM_SETTING_MAX; REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_TIMING5_CYCPWR_THR1, value2); if (level != aniState->spurImmunityLevel) { ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, level, ATH9K_ANI_SPUR_IMMUNE_LVL, value, aniState->iniDef.cycpwrThr1); ath_dbg(common, ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1Ext[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, level, ATH9K_ANI_SPUR_IMMUNE_LVL, value2, aniState->iniDef.cycpwrThr1Ext); if (level > aniState->spurImmunityLevel) ah->stats.ast_ani_spurup++; else if (level < aniState->spurImmunityLevel) ah->stats.ast_ani_spurdown++; aniState->spurImmunityLevel = level; } break; } case ATH9K_ANI_MRC_CCK: /* * You should not see this as AR5008, AR9001, AR9002 * does not have hardware support for MRC CCK. */ WARN_ON(1); break; case ATH9K_ANI_PRESENT: break; default: ath_dbg(common, ANI, "invalid cmd %u\n", cmd); return false; } ath_dbg(common, ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, aniState->mrcCCK ? "on" : "off", aniState->listenTime, aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); return true; } static void ar5008_hw_do_getnf(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]) { int16_t nf; nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR); nfarray[0] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR_PHY_CH1_MINCCA_PWR); nfarray[1] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH2_CCA), AR_PHY_CH2_MINCCA_PWR); nfarray[2] = sign_extend32(nf, 8); if (!IS_CHAN_HT40(ah->curchan)) return; nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR_PHY_EXT_MINCCA_PWR); nfarray[3] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR_PHY_CH1_EXT_MINCCA_PWR); nfarray[4] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA), AR_PHY_CH2_EXT_MINCCA_PWR); nfarray[5] = sign_extend32(nf, 8); } /* * Initialize the ANI register values with default (ini) values. * This routine is called during a (full) hardware reset after * all the registers are initialised from the INI. */ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ar5416AniState *aniState = &chan->ani; struct ath9k_ani_default *iniDef; u32 val; iniDef = &aniState->iniDef; ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, chan->channel, chan->channelFlags); val = REG_READ(ah, AR_PHY_SFCORR); iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); iniDef->m2Thresh = MS(val, AR_PHY_SFCORR_M2_THRESH); iniDef->m2CountThr = MS(val, AR_PHY_SFCORR_M2COUNT_THR); val = REG_READ(ah, AR_PHY_SFCORR_LOW); iniDef->m1ThreshLow = MS(val, AR_PHY_SFCORR_LOW_M1_THRESH_LOW); iniDef->m2ThreshLow = MS(val, AR_PHY_SFCORR_LOW_M2_THRESH_LOW); iniDef->m2CountThrLow = MS(val, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW); val = REG_READ(ah, AR_PHY_SFCORR_EXT); iniDef->m1ThreshExt = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH); iniDef->m2ThreshExt = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH); iniDef->m1ThreshLowExt = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH_LOW); iniDef->m2ThreshLowExt = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH_LOW); iniDef->firstep = REG_READ_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP); iniDef->firstepLow = REG_READ_FIELD(ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_FIRSTEP_LOW); iniDef->cycpwrThr1 = REG_READ_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1); iniDef->cycpwrThr1Ext = REG_READ_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_TIMING5_CYCPWR_THR1); /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCK = false; /* not available on pre AR9003 */ } static void ar5008_hw_set_nf_limits(struct ath_hw *ah) { ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_2GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_5416_2GHZ; ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_5GHZ; ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_5GHZ; ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_5416_5GHZ; } static void ar5008_hw_set_radar_params(struct ath_hw *ah, struct ath_hw_radar_conf *conf) { u32 radar_0 = 0, radar_1 = 0; if (!conf) { REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); return; } radar_0 |= AR_PHY_RADAR_0_ENA | AR_PHY_RADAR_0_FFT_ENA; radar_0 |= SM(conf->fir_power, AR_PHY_RADAR_0_FIRPWR); radar_0 |= SM(conf->radar_rssi, AR_PHY_RADAR_0_RRSSI); radar_0 |= SM(conf->pulse_height, AR_PHY_RADAR_0_HEIGHT); radar_0 |= SM(conf->pulse_rssi, AR_PHY_RADAR_0_PRSSI); radar_0 |= SM(conf->pulse_inband, AR_PHY_RADAR_0_INBAND); radar_1 |= AR_PHY_RADAR_1_MAX_RRSSI; radar_1 |= AR_PHY_RADAR_1_BLOCK_CHECK; radar_1 |= SM(conf->pulse_maxlen, AR_PHY_RADAR_1_MAXLEN); radar_1 |= SM(conf->pulse_inband_step, AR_PHY_RADAR_1_RELSTEP_THRESH); radar_1 |= SM(conf->radar_inband, AR_PHY_RADAR_1_RELPWR_THRESH); REG_WRITE(ah, AR_PHY_RADAR_0, radar_0); REG_WRITE(ah, AR_PHY_RADAR_1, radar_1); if (conf->ext_channel) REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); else REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); } static void ar5008_hw_set_radar_conf(struct ath_hw *ah) { struct ath_hw_radar_conf *conf = &ah->radar_conf; conf->fir_power = -33; conf->radar_rssi = 20; conf->pulse_height = 10; conf->pulse_rssi = 24; conf->pulse_inband = 15; conf->pulse_maxlen = 255; conf->pulse_inband_step = 12; conf->radar_inband = 8; } void ar5008_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); static const u32 ar5416_cca_regs[6] = { AR_PHY_CCA, AR_PHY_CH1_CCA, AR_PHY_CH2_CCA, AR_PHY_EXT_CCA, AR_PHY_CH1_EXT_CCA, AR_PHY_CH2_EXT_CCA }; priv_ops->rf_set_freq = ar5008_hw_set_channel; priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate; priv_ops->rf_alloc_ext_banks = ar5008_hw_rf_alloc_ext_banks; priv_ops->rf_free_ext_banks = ar5008_hw_rf_free_ext_banks; priv_ops->set_rf_regs = ar5008_hw_set_rf_regs; priv_ops->set_channel_regs = ar5008_hw_set_channel_regs; priv_ops->init_bb = ar5008_hw_init_bb; priv_ops->process_ini = ar5008_hw_process_ini; priv_ops->set_rfmode = ar5008_hw_set_rfmode; priv_ops->mark_phy_inactive = ar5008_hw_mark_phy_inactive; priv_ops->set_delta_slope = ar5008_hw_set_delta_slope; priv_ops->rfbus_req = ar5008_hw_rfbus_req; priv_ops->rfbus_done = ar5008_hw_rfbus_done; priv_ops->restore_chainmask = ar5008_restore_chainmask; priv_ops->do_getnf = ar5008_hw_do_getnf; priv_ops->set_radar_params = ar5008_hw_set_radar_params; priv_ops->ani_control = ar5008_hw_ani_control_new; priv_ops->ani_cache_ini_regs = ar5008_hw_ani_cache_ini_regs; if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) priv_ops->compute_pll_control = ar9160_hw_compute_pll_control; else priv_ops->compute_pll_control = ar5008_hw_compute_pll_control; ar5008_hw_set_nf_limits(ah); ar5008_hw_set_radar_conf(ah); memcpy(ah->nf_regs, ar5416_cca_regs, sizeof(ah->nf_regs)); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ar5008_initvals.h0000644000175000017500000005306012026211315025262 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ static const u32 ar5416Modes[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab}, {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300}, {0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, {0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, {0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, {0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007}, {0x00009844, 0x1372161e, 0x1372161e, 0x137216a0, 0x137216a0}, {0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68}, {0x00009850, 0x6c48b4e0, 0x6d48b4e0, 0x6d48b0de, 0x6c48b0de}, {0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e}, {0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, {0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18}, {0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00}, {0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190}, {0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081}, {0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x00009918, 0x000001b8, 0x00000370, 0x00000268, 0x00000134}, {0x00009924, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b}, {0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020}, {0x00009960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80}, {0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80}, {0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80}, {0x00009964, 0x00000000, 0x00000000, 0x00001120, 0x00001120}, {0x000099bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00}, {0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be}, {0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77}, {0x000099c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c}, {0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8}, {0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384}, {0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880}, {0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788}, {0x0000a20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120}, {0x0000b20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120}, {0x0000c20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120}, {0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a}, {0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa}, {0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000}, {0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402}, {0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06}, {0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b}, {0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b}, {0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a}, {0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf}, {0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f}, {0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f}, {0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f}, {0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000}, {0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; static const u32 ar5416Common[][2] = { /* Addr allmodes */ {0x0000000c, 0x00000000}, {0x00000030, 0x00020015}, {0x00000034, 0x00000005}, {0x00000040, 0x00000000}, {0x00000044, 0x00000008}, {0x00000048, 0x00000008}, {0x0000004c, 0x00000010}, {0x00000050, 0x00000000}, {0x00000054, 0x0000001f}, {0x00000800, 0x00000000}, {0x00000804, 0x00000000}, {0x00000808, 0x00000000}, {0x0000080c, 0x00000000}, {0x00000810, 0x00000000}, {0x00000814, 0x00000000}, {0x00000818, 0x00000000}, {0x0000081c, 0x00000000}, {0x00000820, 0x00000000}, {0x00000824, 0x00000000}, {0x00001040, 0x002ffc0f}, {0x00001044, 0x002ffc0f}, {0x00001048, 0x002ffc0f}, {0x0000104c, 0x002ffc0f}, {0x00001050, 0x002ffc0f}, {0x00001054, 0x002ffc0f}, {0x00001058, 0x002ffc0f}, {0x0000105c, 0x002ffc0f}, {0x00001060, 0x002ffc0f}, {0x00001064, 0x002ffc0f}, {0x00001230, 0x00000000}, {0x00001270, 0x00000000}, {0x00001038, 0x00000000}, {0x00001078, 0x00000000}, {0x000010b8, 0x00000000}, {0x000010f8, 0x00000000}, {0x00001138, 0x00000000}, {0x00001178, 0x00000000}, {0x000011b8, 0x00000000}, {0x000011f8, 0x00000000}, {0x00001238, 0x00000000}, {0x00001278, 0x00000000}, {0x000012b8, 0x00000000}, {0x000012f8, 0x00000000}, {0x00001338, 0x00000000}, {0x00001378, 0x00000000}, {0x000013b8, 0x00000000}, {0x000013f8, 0x00000000}, {0x00001438, 0x00000000}, {0x00001478, 0x00000000}, {0x000014b8, 0x00000000}, {0x000014f8, 0x00000000}, {0x00001538, 0x00000000}, {0x00001578, 0x00000000}, {0x000015b8, 0x00000000}, {0x000015f8, 0x00000000}, {0x00001638, 0x00000000}, {0x00001678, 0x00000000}, {0x000016b8, 0x00000000}, {0x000016f8, 0x00000000}, {0x00001738, 0x00000000}, {0x00001778, 0x00000000}, {0x000017b8, 0x00000000}, {0x000017f8, 0x00000000}, {0x0000103c, 0x00000000}, {0x0000107c, 0x00000000}, {0x000010bc, 0x00000000}, {0x000010fc, 0x00000000}, {0x0000113c, 0x00000000}, {0x0000117c, 0x00000000}, {0x000011bc, 0x00000000}, {0x000011fc, 0x00000000}, {0x0000123c, 0x00000000}, {0x0000127c, 0x00000000}, {0x000012bc, 0x00000000}, {0x000012fc, 0x00000000}, {0x0000133c, 0x00000000}, {0x0000137c, 0x00000000}, {0x000013bc, 0x00000000}, {0x000013fc, 0x00000000}, {0x0000143c, 0x00000000}, {0x0000147c, 0x00000000}, {0x00004030, 0x00000002}, {0x0000403c, 0x00000002}, {0x00007010, 0x00000000}, {0x00007038, 0x000004c2}, {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, {0x00008018, 0x00000700}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, {0x0000803c, 0x00000000}, {0x00008048, 0x40000000}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, {0x00008060, 0x0000000f}, {0x00008064, 0x00000000}, {0x000080c0, 0x2a82301a}, {0x000080c4, 0x05dc01e0}, {0x000080c8, 0x1f402710}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00001e00}, {0x000080d4, 0x00000000}, {0x000080d8, 0x00400000}, {0x000080e0, 0xffffffff}, {0x000080e4, 0x0000ffff}, {0x000080e8, 0x003f3f3f}, {0x000080ec, 0x00000000}, {0x000080f0, 0x00000000}, {0x000080f4, 0x00000000}, {0x000080f8, 0x00000000}, {0x000080fc, 0x00020000}, {0x00008100, 0x00020000}, {0x00008104, 0x00000001}, {0x00008108, 0x00000052}, {0x0000810c, 0x00000000}, {0x00008110, 0x00000168}, {0x00008118, 0x000100aa}, {0x0000811c, 0x00003210}, {0x00008124, 0x00000000}, {0x00008128, 0x00000000}, {0x0000812c, 0x00000000}, {0x00008130, 0x00000000}, {0x00008134, 0x00000000}, {0x00008138, 0x00000000}, {0x0000813c, 0x00000000}, {0x00008144, 0xffffffff}, {0x00008168, 0x00000000}, {0x0000816c, 0x00000000}, {0x00008170, 0x32143320}, {0x00008174, 0xfaa4fa50}, {0x00008178, 0x00000100}, {0x0000817c, 0x00000000}, {0x000081c4, 0x00000000}, {0x000081ec, 0x00000000}, {0x000081f0, 0x00000000}, {0x000081f4, 0x00000000}, {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008200, 0x00000000}, {0x00008204, 0x00000000}, {0x00008208, 0x00000000}, {0x0000820c, 0x00000000}, {0x00008210, 0x00000000}, {0x00008214, 0x00000000}, {0x00008218, 0x00000000}, {0x0000821c, 0x00000000}, {0x00008220, 0x00000000}, {0x00008224, 0x00000000}, {0x00008228, 0x00000000}, {0x0000822c, 0x00000000}, {0x00008230, 0x00000000}, {0x00008234, 0x00000000}, {0x00008238, 0x00000000}, {0x0000823c, 0x00000000}, {0x00008240, 0x00100000}, {0x00008244, 0x0010f400}, {0x00008248, 0x00000100}, {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, {0x0000825c, 0x400000ff}, {0x00008260, 0x00080922}, {0x00008264, 0x88000010}, {0x00008270, 0x00000000}, {0x00008274, 0x40000000}, {0x00008278, 0x003e4180}, {0x0000827c, 0x00000000}, {0x00008284, 0x0000002c}, {0x00008288, 0x0000002c}, {0x0000828c, 0x00000000}, {0x00008294, 0x00000000}, {0x00008298, 0x00000000}, {0x00008300, 0x00000000}, {0x00008304, 0x00000000}, {0x00008308, 0x00000000}, {0x0000830c, 0x00000000}, {0x00008310, 0x00000000}, {0x00008314, 0x00000000}, {0x00008318, 0x00000000}, {0x00008328, 0x00000000}, {0x0000832c, 0x00000007}, {0x00008330, 0x00000302}, {0x00008334, 0x00000e00}, {0x00008338, 0x00070000}, {0x0000833c, 0x00000000}, {0x00008340, 0x000107ff}, {0x00009808, 0x00000000}, {0x0000980c, 0xad848e19}, {0x00009810, 0x7d14e000}, {0x00009814, 0x9c0a9f6b}, {0x0000981c, 0x00000000}, {0x0000982c, 0x0000a000}, {0x00009830, 0x00000000}, {0x0000983c, 0x00200400}, {0x00009840, 0x206a002e}, {0x0000984c, 0x1284233c}, {0x00009854, 0x00000859}, {0x00009900, 0x00000000}, {0x00009904, 0x00000000}, {0x00009908, 0x00000000}, {0x0000990c, 0x00000000}, {0x0000991c, 0x10000fff}, {0x00009920, 0x05100000}, {0x0000a920, 0x05100000}, {0x0000b920, 0x05100000}, {0x00009928, 0x00000001}, {0x0000992c, 0x00000004}, {0x00009934, 0x1e1f2022}, {0x00009938, 0x0a0b0c0d}, {0x0000993c, 0x00000000}, {0x00009948, 0x9280b212}, {0x0000994c, 0x00020028}, {0x00009954, 0x5d50e188}, {0x00009958, 0x00081fff}, {0x0000c95c, 0x004b6a8e}, {0x0000c968, 0x000003ce}, {0x00009970, 0x190fb515}, {0x00009974, 0x00000000}, {0x00009978, 0x00000001}, {0x0000997c, 0x00000000}, {0x00009980, 0x00000000}, {0x00009984, 0x00000000}, {0x00009988, 0x00000000}, {0x0000998c, 0x00000000}, {0x00009990, 0x00000000}, {0x00009994, 0x00000000}, {0x00009998, 0x00000000}, {0x0000999c, 0x00000000}, {0x000099a0, 0x00000000}, {0x000099a4, 0x00000001}, {0x000099a8, 0x001fff00}, {0x000099ac, 0x00000000}, {0x000099b0, 0x03051000}, {0x000099dc, 0x00000000}, {0x000099e0, 0x00000200}, {0x000099e4, 0xaaaaaaaa}, {0x000099e8, 0x3c466478}, {0x000099ec, 0x000000aa}, {0x000099fc, 0x00001042}, {0x00009b00, 0x00000000}, {0x00009b04, 0x00000001}, {0x00009b08, 0x00000002}, {0x00009b0c, 0x00000003}, {0x00009b10, 0x00000004}, {0x00009b14, 0x00000005}, {0x00009b18, 0x00000008}, {0x00009b1c, 0x00000009}, {0x00009b20, 0x0000000a}, {0x00009b24, 0x0000000b}, {0x00009b28, 0x0000000c}, {0x00009b2c, 0x0000000d}, {0x00009b30, 0x00000010}, {0x00009b34, 0x00000011}, {0x00009b38, 0x00000012}, {0x00009b3c, 0x00000013}, {0x00009b40, 0x00000014}, {0x00009b44, 0x00000015}, {0x00009b48, 0x00000018}, {0x00009b4c, 0x00000019}, {0x00009b50, 0x0000001a}, {0x00009b54, 0x0000001b}, {0x00009b58, 0x0000001c}, {0x00009b5c, 0x0000001d}, {0x00009b60, 0x00000020}, {0x00009b64, 0x00000021}, {0x00009b68, 0x00000022}, {0x00009b6c, 0x00000023}, {0x00009b70, 0x00000024}, {0x00009b74, 0x00000025}, {0x00009b78, 0x00000028}, {0x00009b7c, 0x00000029}, {0x00009b80, 0x0000002a}, {0x00009b84, 0x0000002b}, {0x00009b88, 0x0000002c}, {0x00009b8c, 0x0000002d}, {0x00009b90, 0x00000030}, {0x00009b94, 0x00000031}, {0x00009b98, 0x00000032}, {0x00009b9c, 0x00000033}, {0x00009ba0, 0x00000034}, {0x00009ba4, 0x00000035}, {0x00009ba8, 0x00000035}, {0x00009bac, 0x00000035}, {0x00009bb0, 0x00000035}, {0x00009bb4, 0x00000035}, {0x00009bb8, 0x00000035}, {0x00009bbc, 0x00000035}, {0x00009bc0, 0x00000035}, {0x00009bc4, 0x00000035}, {0x00009bc8, 0x00000035}, {0x00009bcc, 0x00000035}, {0x00009bd0, 0x00000035}, {0x00009bd4, 0x00000035}, {0x00009bd8, 0x00000035}, {0x00009bdc, 0x00000035}, {0x00009be0, 0x00000035}, {0x00009be4, 0x00000035}, {0x00009be8, 0x00000035}, {0x00009bec, 0x00000035}, {0x00009bf0, 0x00000035}, {0x00009bf4, 0x00000035}, {0x00009bf8, 0x00000010}, {0x00009bfc, 0x0000001a}, {0x0000a210, 0x40806333}, {0x0000a214, 0x00106c10}, {0x0000a218, 0x009c4060}, {0x0000a220, 0x018830c6}, {0x0000a224, 0x00000400}, {0x0000a228, 0x00000bb5}, {0x0000a22c, 0x00000011}, {0x0000a234, 0x20202020}, {0x0000a238, 0x20202020}, {0x0000a23c, 0x13c889af}, {0x0000a240, 0x38490a20}, {0x0000a244, 0x00007bb6}, {0x0000a248, 0x0fff3ffc}, {0x0000a24c, 0x00000001}, {0x0000a250, 0x0000a000}, {0x0000a254, 0x00000000}, {0x0000a258, 0x0cc75380}, {0x0000a25c, 0x0f0f0f01}, {0x0000a260, 0xdfa91f01}, {0x0000a268, 0x00000000}, {0x0000a26c, 0x0e79e5c6}, {0x0000b26c, 0x0e79e5c6}, {0x0000c26c, 0x0e79e5c6}, {0x0000d270, 0x00820820}, {0x0000a278, 0x1ce739ce}, {0x0000a27c, 0x051701ce}, {0x0000a338, 0x00000000}, {0x0000a33c, 0x00000000}, {0x0000a340, 0x00000000}, {0x0000a344, 0x00000000}, {0x0000a348, 0x3fffffff}, {0x0000a34c, 0x3fffffff}, {0x0000a350, 0x3fffffff}, {0x0000a354, 0x0003ffff}, {0x0000a358, 0x79a8aa1f}, {0x0000d35c, 0x07ffffef}, {0x0000d360, 0x0fffffe7}, {0x0000d364, 0x17ffffe5}, {0x0000d368, 0x1fffffe4}, {0x0000d36c, 0x37ffffe3}, {0x0000d370, 0x3fffffe3}, {0x0000d374, 0x57ffffe3}, {0x0000d378, 0x5fffffe2}, {0x0000d37c, 0x7fffffe2}, {0x0000d380, 0x7f3c7bba}, {0x0000d384, 0xf3307ff0}, {0x0000a388, 0x08000000}, {0x0000a38c, 0x20202020}, {0x0000a390, 0x20202020}, {0x0000a394, 0x1ce739ce}, {0x0000a398, 0x000001ce}, {0x0000a39c, 0x00000001}, {0x0000a3a0, 0x00000000}, {0x0000a3a4, 0x00000000}, {0x0000a3a8, 0x00000000}, {0x0000a3ac, 0x00000000}, {0x0000a3b0, 0x00000000}, {0x0000a3b4, 0x00000000}, {0x0000a3b8, 0x00000000}, {0x0000a3bc, 0x00000000}, {0x0000a3c0, 0x00000000}, {0x0000a3c4, 0x00000000}, {0x0000a3c8, 0x00000246}, {0x0000a3cc, 0x20202020}, {0x0000a3d0, 0x20202020}, {0x0000a3d4, 0x20202020}, {0x0000a3dc, 0x1ce739ce}, {0x0000a3e0, 0x000001ce}, }; static const u32 ar5416Bank0[][2] = { /* Addr allmodes */ {0x000098b0, 0x1e5795e5}, {0x000098e0, 0x02008020}, }; static const u32 ar5416BB_RfGain[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00009a00, 0x00000000, 0x00000000}, {0x00009a04, 0x00000040, 0x00000040}, {0x00009a08, 0x00000080, 0x00000080}, {0x00009a0c, 0x000001a1, 0x00000141}, {0x00009a10, 0x000001e1, 0x00000181}, {0x00009a14, 0x00000021, 0x000001c1}, {0x00009a18, 0x00000061, 0x00000001}, {0x00009a1c, 0x00000168, 0x00000041}, {0x00009a20, 0x000001a8, 0x000001a8}, {0x00009a24, 0x000001e8, 0x000001e8}, {0x00009a28, 0x00000028, 0x00000028}, {0x00009a2c, 0x00000068, 0x00000068}, {0x00009a30, 0x00000189, 0x000000a8}, {0x00009a34, 0x000001c9, 0x00000169}, {0x00009a38, 0x00000009, 0x000001a9}, {0x00009a3c, 0x00000049, 0x000001e9}, {0x00009a40, 0x00000089, 0x00000029}, {0x00009a44, 0x00000170, 0x00000069}, {0x00009a48, 0x000001b0, 0x00000190}, {0x00009a4c, 0x000001f0, 0x000001d0}, {0x00009a50, 0x00000030, 0x00000010}, {0x00009a54, 0x00000070, 0x00000050}, {0x00009a58, 0x00000191, 0x00000090}, {0x00009a5c, 0x000001d1, 0x00000151}, {0x00009a60, 0x00000011, 0x00000191}, {0x00009a64, 0x00000051, 0x000001d1}, {0x00009a68, 0x00000091, 0x00000011}, {0x00009a6c, 0x000001b8, 0x00000051}, {0x00009a70, 0x000001f8, 0x00000198}, {0x00009a74, 0x00000038, 0x000001d8}, {0x00009a78, 0x00000078, 0x00000018}, {0x00009a7c, 0x00000199, 0x00000058}, {0x00009a80, 0x000001d9, 0x00000098}, {0x00009a84, 0x00000019, 0x00000159}, {0x00009a88, 0x00000059, 0x00000199}, {0x00009a8c, 0x00000099, 0x000001d9}, {0x00009a90, 0x000000d9, 0x00000019}, {0x00009a94, 0x000000f9, 0x00000059}, {0x00009a98, 0x000000f9, 0x00000099}, {0x00009a9c, 0x000000f9, 0x000000d9}, {0x00009aa0, 0x000000f9, 0x000000f9}, {0x00009aa4, 0x000000f9, 0x000000f9}, {0x00009aa8, 0x000000f9, 0x000000f9}, {0x00009aac, 0x000000f9, 0x000000f9}, {0x00009ab0, 0x000000f9, 0x000000f9}, {0x00009ab4, 0x000000f9, 0x000000f9}, {0x00009ab8, 0x000000f9, 0x000000f9}, {0x00009abc, 0x000000f9, 0x000000f9}, {0x00009ac0, 0x000000f9, 0x000000f9}, {0x00009ac4, 0x000000f9, 0x000000f9}, {0x00009ac8, 0x000000f9, 0x000000f9}, {0x00009acc, 0x000000f9, 0x000000f9}, {0x00009ad0, 0x000000f9, 0x000000f9}, {0x00009ad4, 0x000000f9, 0x000000f9}, {0x00009ad8, 0x000000f9, 0x000000f9}, {0x00009adc, 0x000000f9, 0x000000f9}, {0x00009ae0, 0x000000f9, 0x000000f9}, {0x00009ae4, 0x000000f9, 0x000000f9}, {0x00009ae8, 0x000000f9, 0x000000f9}, {0x00009aec, 0x000000f9, 0x000000f9}, {0x00009af0, 0x000000f9, 0x000000f9}, {0x00009af4, 0x000000f9, 0x000000f9}, {0x00009af8, 0x000000f9, 0x000000f9}, {0x00009afc, 0x000000f9, 0x000000f9}, }; static const u32 ar5416Bank1[][2] = { /* Addr allmodes */ {0x000098b0, 0x02108421}, {0x000098ec, 0x00000008}, }; static const u32 ar5416Bank2[][2] = { /* Addr allmodes */ {0x000098b0, 0x0e73ff17}, {0x000098e0, 0x00000420}, }; static const u32 ar5416Bank3[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x000098f0, 0x01400018, 0x01c00018}, }; static const u32 ar5416Bank6[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00e00000, 0x00e00000}, {0x0000989c, 0x005e0000, 0x005e0000}, {0x0000989c, 0x00120000, 0x00120000}, {0x0000989c, 0x00620000, 0x00620000}, {0x0000989c, 0x00020000, 0x00020000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x40ff0000, 0x40ff0000}, {0x0000989c, 0x005f0000, 0x005f0000}, {0x0000989c, 0x00870000, 0x00870000}, {0x0000989c, 0x00f90000, 0x00f90000}, {0x0000989c, 0x007b0000, 0x007b0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00f50000, 0x00f50000}, {0x0000989c, 0x00dc0000, 0x00dc0000}, {0x0000989c, 0x00110000, 0x00110000}, {0x0000989c, 0x006100a8, 0x006100a8}, {0x0000989c, 0x004210a2, 0x004210a2}, {0x0000989c, 0x0014008f, 0x0014008f}, {0x0000989c, 0x00c40003, 0x00c40003}, {0x0000989c, 0x003000f2, 0x003000f2}, {0x0000989c, 0x00440016, 0x00440016}, {0x0000989c, 0x00410040, 0x00410040}, {0x0000989c, 0x0001805e, 0x0001805e}, {0x0000989c, 0x0000c0ab, 0x0000c0ab}, {0x0000989c, 0x000000f1, 0x000000f1}, {0x0000989c, 0x00002081, 0x00002081}, {0x0000989c, 0x000000d4, 0x000000d4}, {0x000098d0, 0x0000000f, 0x0010000f}, }; static const u32 ar5416Bank6TPC[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00e00000, 0x00e00000}, {0x0000989c, 0x005e0000, 0x005e0000}, {0x0000989c, 0x00120000, 0x00120000}, {0x0000989c, 0x00620000, 0x00620000}, {0x0000989c, 0x00020000, 0x00020000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x40ff0000, 0x40ff0000}, {0x0000989c, 0x005f0000, 0x005f0000}, {0x0000989c, 0x00870000, 0x00870000}, {0x0000989c, 0x00f90000, 0x00f90000}, {0x0000989c, 0x007b0000, 0x007b0000}, {0x0000989c, 0x00ff0000, 0x00ff0000}, {0x0000989c, 0x00f50000, 0x00f50000}, {0x0000989c, 0x00dc0000, 0x00dc0000}, {0x0000989c, 0x00110000, 0x00110000}, {0x0000989c, 0x006100a8, 0x006100a8}, {0x0000989c, 0x00423022, 0x00423022}, {0x0000989c, 0x201400df, 0x201400df}, {0x0000989c, 0x00c40002, 0x00c40002}, {0x0000989c, 0x003000f2, 0x003000f2}, {0x0000989c, 0x00440016, 0x00440016}, {0x0000989c, 0x00410040, 0x00410040}, {0x0000989c, 0x0001805e, 0x0001805e}, {0x0000989c, 0x0000c0ab, 0x0000c0ab}, {0x0000989c, 0x000000e1, 0x000000e1}, {0x0000989c, 0x00007081, 0x00007081}, {0x0000989c, 0x000000d4, 0x000000d4}, {0x000098d0, 0x0000000f, 0x0010000f}, }; static const u32 ar5416Bank7[][2] = { /* Addr allmodes */ {0x0000989c, 0x00000500}, {0x0000989c, 0x00000800}, {0x000098cc, 0x0000000e}, }; static const u32 ar5416Addac[][2] = { /* Addr allmodes */ {0x0000989c, 0x00000000}, {0x0000989c, 0x00000003}, {0x0000989c, 0x00000000}, {0x0000989c, 0x0000000c}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000030}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000060}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000058}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x0000989c, 0x00000000}, {0x000098c4, 0x00000000}, }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/antenna.c0000644000175000017500000005466012026211315024060 0ustar mcgrofmcgrof/* * Copyright (c) 2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "ath9k.h" static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta, int main_rssi_avg, int alt_rssi_avg, int pkt_count) { return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && (alt_rssi_avg > main_rssi_avg + maxdelta)) || (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); } static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio, int curr_main_set, int curr_alt_set, int alt_rssi_avg, int main_rssi_avg) { bool result = false; switch (div_group) { case 0: if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) result = true; break; case 1: case 2: if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) && (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) && (alt_rssi_avg >= (main_rssi_avg - 5))) || ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) && (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) && (alt_rssi_avg >= (main_rssi_avg - 2)))) && (alt_rssi_avg >= 4)) result = true; else result = false; break; } return result; } static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, struct ath_hw_antcomb_conf ant_conf, int main_rssi_avg) { antcomb->quick_scan_cnt = 0; if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2) antcomb->rssi_lna2 = main_rssi_avg; else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1) antcomb->rssi_lna1 = main_rssi_avg; switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) { case 0x10: /* LNA2 A-B */ antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->first_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; break; case 0x20: /* LNA1 A-B */ antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->first_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; break; case 0x21: /* LNA1 LNA2 */ antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2; antcomb->first_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case 0x12: /* LNA2 LNA1 */ antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1; antcomb->first_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case 0x13: /* LNA2 A+B */ antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->first_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; break; case 0x23: /* LNA1 A+B */ antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; antcomb->first_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; break; default: break; } } static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, struct ath_hw_antcomb_conf *div_ant_conf, int main_rssi_avg, int alt_rssi_avg, int alt_ratio) { /* alt_good */ switch (antcomb->quick_scan_cnt) { case 0: /* set alt to main, and alt to first conf */ div_ant_conf->main_lna_conf = antcomb->main_conf; div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; break; case 1: /* set alt to main, and alt to first conf */ div_ant_conf->main_lna_conf = antcomb->main_conf; div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; antcomb->rssi_first = main_rssi_avg; antcomb->rssi_second = alt_rssi_avg; if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { /* main is LNA1 */ if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_HI, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->first_ratio = true; else antcomb->first_ratio = false; } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_MID, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->first_ratio = true; else antcomb->first_ratio = false; } else { if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && (alt_rssi_avg > main_rssi_avg + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || (alt_rssi_avg > main_rssi_avg)) && (antcomb->total_pkt_count > 50)) antcomb->first_ratio = true; else antcomb->first_ratio = false; } break; case 2: antcomb->alt_good = false; antcomb->scan_not_start = false; antcomb->scan = false; antcomb->rssi_first = main_rssi_avg; antcomb->rssi_third = alt_rssi_avg; if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) antcomb->rssi_lna1 = alt_rssi_avg; else if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2) antcomb->rssi_lna2 = alt_rssi_avg; else if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) { if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) antcomb->rssi_lna2 = main_rssi_avg; else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) antcomb->rssi_lna1 = main_rssi_avg; } if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; else div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_HI, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->second_ratio = true; else antcomb->second_ratio = false; } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { if (ath_is_alt_ant_ratio_better(alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_MID, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, antcomb->total_pkt_count)) antcomb->second_ratio = true; else antcomb->second_ratio = false; } else { if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && (alt_rssi_avg > main_rssi_avg + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || (alt_rssi_avg > main_rssi_avg)) && (antcomb->total_pkt_count > 50)) antcomb->second_ratio = true; else antcomb->second_ratio = false; } /* set alt to the conf with maximun ratio */ if (antcomb->first_ratio && antcomb->second_ratio) { if (antcomb->rssi_second > antcomb->rssi_third) { /* first alt*/ if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2*/ if (div_ant_conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) { /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; } else { /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; } } else if (antcomb->first_ratio) { /* first alt */ if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; } else if (antcomb->second_ratio) { /* second alt */ if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; } else { /* main is largest */ if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) /* Set alt LNA1 or LNA2 */ if (div_ant_conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; else div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; else /* Set alt to A+B or A-B */ div_ant_conf->alt_lna_conf = antcomb->main_conf; } break; default: break; } } static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, struct ath_ant_comb *antcomb, int alt_ratio) { if (ant_conf->div_group == 0) { /* Adjust the fast_div_bias based on main and alt lna conf */ switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { case 0x01: /* A-B LNA2 */ ant_conf->fast_div_bias = 0x3b; break; case 0x02: /* A-B LNA1 */ ant_conf->fast_div_bias = 0x3d; break; case 0x03: /* A-B A+B */ ant_conf->fast_div_bias = 0x1; break; case 0x10: /* LNA2 A-B */ ant_conf->fast_div_bias = 0x7; break; case 0x12: /* LNA2 LNA1 */ ant_conf->fast_div_bias = 0x2; break; case 0x13: /* LNA2 A+B */ ant_conf->fast_div_bias = 0x7; break; case 0x20: /* LNA1 A-B */ ant_conf->fast_div_bias = 0x6; break; case 0x21: /* LNA1 LNA2 */ ant_conf->fast_div_bias = 0x0; break; case 0x23: /* LNA1 A+B */ ant_conf->fast_div_bias = 0x6; break; case 0x30: /* A+B A-B */ ant_conf->fast_div_bias = 0x1; break; case 0x31: /* A+B LNA2 */ ant_conf->fast_div_bias = 0x3b; break; case 0x32: /* A+B LNA1 */ ant_conf->fast_div_bias = 0x3d; break; default: break; } } else if (ant_conf->div_group == 1) { /* Adjust the fast_div_bias based on main and alt_lna_conf */ switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { case 0x01: /* A-B LNA2 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x02: /* A-B LNA1 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x03: /* A-B A+B */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x10: /* LNA2 A-B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x12: /* LNA2 LNA1 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x13: /* LNA2 A+B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x20: /* LNA1 A-B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x21: /* LNA1 LNA2 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x23: /* LNA1 A+B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x30: /* A+B A-B */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x31: /* A+B LNA2 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x32: /* A+B LNA1 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; default: break; } } else if (ant_conf->div_group == 2) { /* Adjust the fast_div_bias based on main and alt_lna_conf */ switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { case 0x01: /* A-B LNA2 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x02: /* A-B LNA1 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x03: /* A-B A+B */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x10: /* LNA2 A-B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x12: /* LNA2 LNA1 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x13: /* LNA2 A+B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x20: /* LNA1 A-B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x21: /* LNA1 LNA2 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x23: /* LNA1 A+B */ if (!(antcomb->scan) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x30: /* A+B A-B */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x31: /* A+B LNA2 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; case 0x32: /* A+B LNA1 */ ant_conf->fast_div_bias = 0x1; ant_conf->main_gaintb = 0; ant_conf->alt_gaintb = 0; break; default: break; } } } void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) { struct ath_hw_antcomb_conf div_ant_conf; struct ath_ant_comb *antcomb = &sc->ant_comb; int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; int curr_main_set; int main_rssi = rs->rs_rssi_ctl0; int alt_rssi = rs->rs_rssi_ctl1; int rx_ant_conf, main_ant_conf; bool short_scan = false; rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & ATH_ANT_RX_MASK; main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & ATH_ANT_RX_MASK; /* Record packet only when both main_rssi and alt_rssi is positive */ if (main_rssi > 0 && alt_rssi > 0) { antcomb->total_pkt_count++; antcomb->main_total_rssi += main_rssi; antcomb->alt_total_rssi += alt_rssi; if (main_ant_conf == rx_ant_conf) antcomb->main_recv_cnt++; else antcomb->alt_recv_cnt++; } /* Short scan check */ if (antcomb->scan && antcomb->alt_good) { if (time_after(jiffies, antcomb->scan_start_time + msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) short_scan = true; else if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { alt_ratio = ((antcomb->alt_recv_cnt * 100) / antcomb->total_pkt_count); if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) short_scan = true; } } if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || rs->rs_moreaggr) && !short_scan) return; if (antcomb->total_pkt_count) { alt_ratio = ((antcomb->alt_recv_cnt * 100) / antcomb->total_pkt_count); main_rssi_avg = (antcomb->main_total_rssi / antcomb->total_pkt_count); alt_rssi_avg = (antcomb->alt_total_rssi / antcomb->total_pkt_count); } ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf); curr_alt_set = div_ant_conf.alt_lna_conf; curr_main_set = div_ant_conf.main_lna_conf; antcomb->count++; if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, main_rssi_avg); antcomb->alt_good = true; } else { antcomb->alt_good = false; } antcomb->count = 0; antcomb->scan = true; antcomb->scan_not_start = true; } if (!antcomb->scan) { if (ath_ant_div_comb_alt_check(div_ant_conf.div_group, alt_ratio, curr_main_set, curr_alt_set, alt_rssi_avg, main_rssi_avg)) { if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { /* Switch main and alt LNA */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; } goto div_comb_done; } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { /* Set alt to another LNA */ if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; goto div_comb_done; } if ((alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta))) goto div_comb_done; } if (!antcomb->scan_not_start) { switch (curr_alt_set) { case ATH_ANT_DIV_COMB_LNA2: antcomb->rssi_lna2 = alt_rssi_avg; antcomb->rssi_lna1 = main_rssi_avg; antcomb->scan = true; /* set to A+B */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case ATH_ANT_DIV_COMB_LNA1: antcomb->rssi_lna1 = alt_rssi_avg; antcomb->rssi_lna2 = main_rssi_avg; antcomb->scan = true; /* set to A+B */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; break; case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: antcomb->rssi_add = alt_rssi_avg; antcomb->scan = true; /* set to A-B */ div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; break; case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: antcomb->rssi_sub = alt_rssi_avg; antcomb->scan = false; if (antcomb->rssi_lna2 > (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { /* use LNA2 as main LNA */ if ((antcomb->rssi_add > antcomb->rssi_lna1) && (antcomb->rssi_add > antcomb->rssi_sub)) { /* set to A+B */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; } else if (antcomb->rssi_sub > antcomb->rssi_lna1) { /* set to A-B */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; } else { /* set to LNA1 */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; } } else { /* use LNA1 as main LNA */ if ((antcomb->rssi_add > antcomb->rssi_lna2) && (antcomb->rssi_add > antcomb->rssi_sub)) { /* set to A+B */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; } else if (antcomb->rssi_sub > antcomb->rssi_lna1) { /* set to A-B */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; } else { /* set to LNA2 */ div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; } } break; default: break; } } else { if (!antcomb->alt_good) { antcomb->scan_not_start = false; /* Set alt to another LNA */ if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; } goto div_comb_done; } } ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, main_rssi_avg, alt_rssi_avg, alt_ratio); antcomb->quick_scan_cnt++; div_comb_done: ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio); ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf); antcomb->scan_start_time = jiffies; antcomb->total_pkt_count = 0; antcomb->main_total_rssi = 0; antcomb->alt_total_rssi = 0; antcomb->main_recv_cnt = 0; antcomb->alt_recv_cnt = 0; } void ath_ant_comb_update(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_hw_antcomb_conf div_ant_conf; u8 lna_conf; ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); if (sc->ant_rx == 1) lna_conf = ATH_ANT_DIV_COMB_LNA1; else lna_conf = ATH_ANT_DIV_COMB_LNA2; div_ant_conf.main_lna_conf = lna_conf; div_ant_conf.alt_lna_conf = lna_conf; ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ani.h0000644000175000017500000000774312026211315023210 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ANI_H #define ANI_H #define HAL_PROCESS_ANI 0x00000001 #define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan) #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi) /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_HIGH 3500 #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000 /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_LOW 400 #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900 /* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_HIGH 600 /* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_LOW 300 #define ATH9K_ANI_NOISE_IMMUNE_LVL 4 #define ATH9K_ANI_USE_OFDM_WEAK_SIG true #define ATH9K_ANI_CCK_WEAK_SIG_THR false #define ATH9K_ANI_SPUR_IMMUNE_LVL 3 #define ATH9K_ANI_FIRSTEP_LVL 2 #define ATH9K_ANI_RSSI_THR_HIGH 40 #define ATH9K_ANI_RSSI_THR_LOW 7 #define ATH9K_ANI_PERIOD 300 /* in ms */ #define ATH9K_ANI_POLLINTERVAL 1000 #define HAL_NOISE_IMMUNE_MAX 4 #define HAL_SPUR_IMMUNE_MAX 7 #define HAL_FIRST_STEP_MAX 2 #define ATH9K_SIG_FIRSTEP_SETTING_MIN 0 #define ATH9K_SIG_FIRSTEP_SETTING_MAX 20 #define ATH9K_SIG_SPUR_IMM_SETTING_MIN 0 #define ATH9K_SIG_SPUR_IMM_SETTING_MAX 22 /* values here are relative to the INI */ enum ath9k_ani_cmd { ATH9K_ANI_PRESENT = 0x1, ATH9K_ANI_NOISE_IMMUNITY_LEVEL = 0x2, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION = 0x4, ATH9K_ANI_CCK_WEAK_SIGNAL_THR = 0x8, ATH9K_ANI_FIRSTEP_LEVEL = 0x10, ATH9K_ANI_SPUR_IMMUNITY_LEVEL = 0x20, ATH9K_ANI_MODE = 0x40, ATH9K_ANI_PHYERR_RESET = 0x80, ATH9K_ANI_MRC_CCK = 0x100, ATH9K_ANI_ALL = 0xfff }; struct ath9k_mib_stats { u32 ackrcv_bad; u32 rts_bad; u32 rts_good; u32 fcs_bad; u32 beacons; }; /* INI default values for ANI registers */ struct ath9k_ani_default { u16 m1ThreshLow; u16 m2ThreshLow; u16 m1Thresh; u16 m2Thresh; u16 m2CountThr; u16 m2CountThrLow; u16 m1ThreshLowExt; u16 m2ThreshLowExt; u16 m1ThreshExt; u16 m2ThreshExt; u16 firstep; u16 firstepLow; u16 cycpwrThr1; u16 cycpwrThr1Ext; }; struct ar5416AniState { struct ath9k_channel *c; u8 noiseImmunityLevel; u8 ofdmNoiseImmunityLevel; u8 cckNoiseImmunityLevel; bool ofdmsTurn; u8 mrcCCK; u8 spurImmunityLevel; u8 firstepLevel; u8 ofdmWeakSigDetect; u8 cckWeakSigThreshold; u32 listenTime; int32_t rssiThrLow; int32_t rssiThrHigh; u32 ofdmPhyErrCount; u32 cckPhyErrCount; int16_t pktRssi[2]; int16_t ofdmErrRssi[2]; int16_t cckErrRssi[2]; struct ath9k_ani_default iniDef; }; struct ar5416Stats { u32 ast_ani_niup; u32 ast_ani_nidown; u32 ast_ani_spurup; u32 ast_ani_spurdown; u32 ast_ani_ofdmon; u32 ast_ani_ofdmoff; u32 ast_ani_cckhigh; u32 ast_ani_ccklow; u32 ast_ani_stepup; u32 ast_ani_stepdown; u32 ast_ani_ofdmerrs; u32 ast_ani_cckerrs; u32 ast_ani_reset; u32 ast_ani_lneg_or_lzero; u32 avgbrssi; struct ath9k_mib_stats ast_mibstats; }; #define ah_mibStats stats.ast_mibstats void ath9k_enable_mib_counters(struct ath_hw *ah); void ath9k_hw_disable_mib_counters(struct ath_hw *ah); void ath9k_hw_ani_setup(struct ath_hw *ah); void ath9k_hw_ani_init(struct ath_hw *ah); #endif /* ANI_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ani.c0000644000175000017500000004001612026211315023171 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "hw.h" #include "hw-ops.h" struct ani_ofdm_level_entry { int spur_immunity_level; int fir_step_level; int ofdm_weak_signal_on; }; /* values here are relative to the INI */ /* * Legend: * * SI: Spur immunity * FS: FIR Step * WS: OFDM / CCK Weak Signal detection * MRC-CCK: Maximal Ratio Combining for CCK */ static const struct ani_ofdm_level_entry ofdm_level_table[] = { /* SI FS WS */ { 0, 0, 1 }, /* lvl 0 */ { 1, 1, 1 }, /* lvl 1 */ { 2, 2, 1 }, /* lvl 2 */ { 3, 2, 1 }, /* lvl 3 (default) */ { 4, 3, 1 }, /* lvl 4 */ { 5, 4, 1 }, /* lvl 5 */ { 6, 5, 1 }, /* lvl 6 */ { 7, 6, 1 }, /* lvl 7 */ { 7, 6, 0 }, /* lvl 8 */ { 7, 7, 0 } /* lvl 9 */ }; #define ATH9K_ANI_OFDM_NUM_LEVEL \ ARRAY_SIZE(ofdm_level_table) #define ATH9K_ANI_OFDM_MAX_LEVEL \ (ATH9K_ANI_OFDM_NUM_LEVEL-1) #define ATH9K_ANI_OFDM_DEF_LEVEL \ 3 /* default level - matches the INI settings */ /* * MRC (Maximal Ratio Combining) has always been used with multi-antenna ofdm. * With OFDM for single stream you just add up all antenna inputs, you're * only interested in what you get after FFT. Signal aligment is also not * required for OFDM because any phase difference adds up in the frequency * domain. * * MRC requires extra work for use with CCK. You need to align the antenna * signals from the different antenna before you can add the signals together. * You need aligment of signals as CCK is in time domain, so addition can cancel * your signal completely if phase is 180 degrees (think of adding sine waves). * You also need to remove noise before the addition and this is where ANI * MRC CCK comes into play. One of the antenna inputs may be stronger but * lower SNR, so just adding after alignment can be dangerous. * * Regardless of alignment in time, the antenna signals add constructively after * FFT and improve your reception. For more information: * * http://en.wikipedia.org/wiki/Maximal-ratio_combining */ struct ani_cck_level_entry { int fir_step_level; int mrc_cck_on; }; static const struct ani_cck_level_entry cck_level_table[] = { /* FS MRC-CCK */ { 0, 1 }, /* lvl 0 */ { 1, 1 }, /* lvl 1 */ { 2, 1 }, /* lvl 2 (default) */ { 3, 1 }, /* lvl 3 */ { 4, 0 }, /* lvl 4 */ { 5, 0 }, /* lvl 5 */ { 6, 0 }, /* lvl 6 */ { 6, 0 }, /* lvl 7 (only for high rssi) */ { 7, 0 } /* lvl 8 (only for high rssi) */ }; #define ATH9K_ANI_CCK_NUM_LEVEL \ ARRAY_SIZE(cck_level_table) #define ATH9K_ANI_CCK_MAX_LEVEL \ (ATH9K_ANI_CCK_NUM_LEVEL-1) #define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \ (ATH9K_ANI_CCK_NUM_LEVEL-3) #define ATH9K_ANI_CCK_DEF_LEVEL \ 2 /* default level - matches the INI settings */ static void ath9k_hw_update_mibstats(struct ath_hw *ah, struct ath9k_mib_stats *stats) { stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL); stats->rts_bad += REG_READ(ah, AR_RTS_FAIL); stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL); stats->rts_good += REG_READ(ah, AR_RTS_OK); stats->beacons += REG_READ(ah, AR_BEACON_CNT); } static void ath9k_ani_restart(struct ath_hw *ah) { struct ar5416AniState *aniState; if (!DO_ANI(ah)) return; aniState = &ah->curchan->ani; aniState->listenTime = 0; ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY_ERR_1, 0); REG_WRITE(ah, AR_PHY_ERR_2, 0); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); aniState->ofdmPhyErrCount = 0; aniState->cckPhyErrCount = 0; } /* Adjust the OFDM Noise Immunity Level */ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, bool scan) { struct ar5416AniState *aniState = &ah->curchan->ani; struct ath_common *common = ath9k_hw_common(ah); const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; bool weak_sig; ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->ofdmNoiseImmunityLevel, immunityLevel, BEACON_RSSI(ah), aniState->rssiThrLow, aniState->rssiThrHigh); if (!scan) aniState->ofdmNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; if (aniState->spurImmunityLevel != entry_ofdm->spur_immunity_level) ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, entry_ofdm->spur_immunity_level); if (aniState->firstepLevel != entry_ofdm->fir_step_level && entry_ofdm->fir_step_level >= entry_cck->fir_step_level) ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, entry_ofdm->fir_step_level); weak_sig = entry_ofdm->ofdm_weak_signal_on; if (ah->opmode == NL80211_IFTYPE_STATION && BEACON_RSSI(ah) <= aniState->rssiThrHigh) weak_sig = true; if (aniState->ofdmWeakSigDetect != weak_sig) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, entry_ofdm->ofdm_weak_signal_on); if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) { ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI; } else { ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI; ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; } } static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) { struct ar5416AniState *aniState; if (!DO_ANI(ah)) return; aniState = &ah->curchan->ani; if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false); } /* * Set the ANI settings to match an CCK level. */ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, bool scan) { struct ar5416AniState *aniState = &ah->curchan->ani; struct ath_common *common = ath9k_hw_common(ah); const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->cckNoiseImmunityLevel, immunityLevel, BEACON_RSSI(ah), aniState->rssiThrLow, aniState->rssiThrHigh); if (ah->opmode == NL80211_IFTYPE_STATION && BEACON_RSSI(ah) <= aniState->rssiThrLow && immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; if (!scan) aniState->cckNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; if (aniState->firstepLevel != entry_cck->fir_step_level && entry_cck->fir_step_level >= entry_ofdm->fir_step_level) ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, entry_cck->fir_step_level); /* Skip MRC CCK for pre AR9003 families */ if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) return; if (aniState->mrcCCK != entry_cck->mrc_cck_on) ath9k_hw_ani_control(ah, ATH9K_ANI_MRC_CCK, entry_cck->mrc_cck_on); } static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) { struct ar5416AniState *aniState; if (!DO_ANI(ah)) return; aniState = &ah->curchan->ani; if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1, false); } /* * only lower either OFDM or CCK errors per turn * we lower the other one next time */ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) { struct ar5416AniState *aniState; aniState = &ah->curchan->ani; /* lower OFDM noise immunity */ if (aniState->ofdmNoiseImmunityLevel > 0 && (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) { ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1, false); return; } /* lower CCK noise immunity */ if (aniState->cckNoiseImmunityLevel > 0) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1, false); } /* * Restore the ANI parameters in the HAL and reset the statistics. * This routine should be called for every hardware reset and for * every channel change. */ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) { struct ar5416AniState *aniState = &ah->curchan->ani; struct ath9k_channel *chan = ah->curchan; struct ath_common *common = ath9k_hw_common(ah); int ofdm_nil, cck_nil; if (!DO_ANI(ah)) return; BUG_ON(aniState == NULL); ah->stats.ast_ani_reset++; /* only allow a subset of functions in AP mode */ if (ah->opmode == NL80211_IFTYPE_AP) { if (IS_CHAN_2GHZ(chan)) { ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | ATH9K_ANI_FIRSTEP_LEVEL); if (AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function |= ATH9K_ANI_MRC_CCK; } else ah->ani_function = 0; } /* always allow mode (on/off) to be controlled */ ah->ani_function |= ATH9K_ANI_MODE; ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL, aniState->ofdmNoiseImmunityLevel); cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL, aniState->cckNoiseImmunityLevel); if (is_scanning || (ah->opmode != NL80211_IFTYPE_STATION && ah->opmode != NL80211_IFTYPE_ADHOC)) { /* * If we're scanning or in AP mode, the defaults (ini) * should be in place. For an AP we assume the historical * levels for this channel are probably outdated so start * from defaults instead. */ if (aniState->ofdmNoiseImmunityLevel != ATH9K_ANI_OFDM_DEF_LEVEL || aniState->cckNoiseImmunityLevel != ATH9K_ANI_CCK_DEF_LEVEL) { ath_dbg(common, ANI, "Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, chan->channelFlags, is_scanning, aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); ofdm_nil = ATH9K_ANI_OFDM_DEF_LEVEL; cck_nil = ATH9K_ANI_CCK_DEF_LEVEL; } } else { /* * restore historical levels for this channel */ ath_dbg(common, ANI, "Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, chan->channelFlags, is_scanning, aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); } ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning); ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning); /* * enable phy counters if hw supports or if not, enable phy * interrupts (so we can count each one) */ ath9k_ani_restart(ah); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); } static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ar5416AniState *aniState = &ah->curchan->ani; u32 phyCnt1, phyCnt2; int32_t listenTime; ath_hw_cycle_counters_update(common); listenTime = ath_hw_get_listen_time(common); if (listenTime <= 0) { ah->stats.ast_ani_lneg_or_lzero++; ath9k_ani_restart(ah); return false; } aniState->listenTime += listenTime; ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); ah->stats.ast_ani_ofdmerrs += phyCnt1 - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = phyCnt1; ah->stats.ast_ani_cckerrs += phyCnt2 - aniState->cckPhyErrCount; aniState->cckPhyErrCount = phyCnt2; return true; } void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) { struct ar5416AniState *aniState; struct ath_common *common = ath9k_hw_common(ah); u32 ofdmPhyErrRate, cckPhyErrRate; if (!DO_ANI(ah)) return; aniState = &ah->curchan->ani; if (WARN_ON(!aniState)) return; if (!ath9k_hw_ani_read_counters(ah)) return; ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 / aniState->listenTime; cckPhyErrRate = aniState->cckPhyErrCount * 1000 / aniState->listenTime; ath_dbg(common, ANI, "listenTime=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n", aniState->listenTime, aniState->ofdmNoiseImmunityLevel, ofdmPhyErrRate, aniState->cckNoiseImmunityLevel, cckPhyErrRate, aniState->ofdmsTurn); if (aniState->listenTime > ah->aniperiod) { if (cckPhyErrRate < ah->config.cck_trig_low && ofdmPhyErrRate < ah->config.ofdm_trig_low) { ath9k_hw_ani_lower_immunity(ah); aniState->ofdmsTurn = !aniState->ofdmsTurn; } else if (ofdmPhyErrRate > ah->config.ofdm_trig_high) { ath9k_hw_ani_ofdm_err_trigger(ah); aniState->ofdmsTurn = false; } else if (cckPhyErrRate > ah->config.cck_trig_high) { ath9k_hw_ani_cck_err_trigger(ah); aniState->ofdmsTurn = true; } ath9k_ani_restart(ah); } } EXPORT_SYMBOL(ath9k_hw_ani_monitor); void ath9k_enable_mib_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); ath_dbg(common, ANI, "Enable MIB counters\n"); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_FILT_OFDM, 0); REG_WRITE(ah, AR_FILT_CCK, 0); REG_WRITE(ah, AR_MIBC, ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); REGWRITE_BUFFER_FLUSH(ah); } /* Freeze the MIB counters, get the stats and then clear them */ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); ath_dbg(common, ANI, "Disable MIB counters\n"); REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC); REG_WRITE(ah, AR_FILT_OFDM, 0); REG_WRITE(ah, AR_FILT_CCK, 0); } EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); void ath9k_hw_ani_setup(struct ath_hw *ah) { int i; static const int totalSizeDesired[] = { -55, -55, -55, -55, -62 }; static const int coarseHigh[] = { -14, -14, -14, -14, -12 }; static const int coarseLow[] = { -64, -64, -64, -64, -70 }; static const int firpwr[] = { -78, -78, -78, -78, -80 }; for (i = 0; i < 5; i++) { ah->totalSizeDesired[i] = totalSizeDesired[i]; ah->coarse_high[i] = coarseHigh[i]; ah->coarse_low[i] = coarseLow[i]; ah->firpwr[i] = firpwr[i]; } } void ath9k_hw_ani_init(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); int i; ath_dbg(common, ANI, "Initialize ANI\n"); ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH; ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW; for (i = 0; i < ARRAY_SIZE(ah->channels); i++) { struct ath9k_channel *chan = &ah->channels[i]; struct ar5416AniState *ani = &chan->ani; ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false; ani->ofdmsTurn = true; ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; } /* * since we expect some ongoing maintenance on the tables, let's sanity * check here default level should not modify INI setting. */ ah->aniperiod = ATH9K_ANI_PERIOD; ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL; if (ah->config.enable_ani) ah->proc_phyerr |= HAL_PROCESS_ANI; ath9k_ani_restart(ah); ath9k_enable_mib_counters(ah); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/ahb.c0000644000175000017500000001151312026211315023154 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2011 Atheros Communications Inc. * Copyright (c) 2009 Gabor Juhos * Copyright (c) 2009 Imre Kaloz * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "ath9k.h" static const struct platform_device_id ath9k_platform_id_table[] = { { .name = "ath9k", .driver_data = AR5416_AR9100_DEVID, }, { .name = "ar933x_wmac", .driver_data = AR9300_DEVID_AR9330, }, { .name = "ar934x_wmac", .driver_data = AR9300_DEVID_AR9340, }, { .name = "qca955x_wmac", .driver_data = AR9300_DEVID_QCA955X, }, {}, }; /* return bus cachesize in 4B word units */ static void ath_ahb_read_cachesize(struct ath_common *common, int *csz) { *csz = L1_CACHE_BYTES >> 2; } static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath_softc *sc = (struct ath_softc *)common->priv; struct platform_device *pdev = to_platform_device(sc->dev); struct ath9k_platform_data *pdata; pdata = (struct ath9k_platform_data *) pdev->dev.platform_data; if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { ath_err(common, "%s: flash read failed, offset %08x is out of range\n", __func__, off); return false; } *data = pdata->eeprom_data[off]; return true; } static struct ath_bus_ops ath_ahb_bus_ops = { .ath_bus_type = ATH_AHB, .read_cachesize = ath_ahb_read_cachesize, .eeprom_read = ath_ahb_eeprom_read, }; static int ath_ahb_probe(struct platform_device *pdev) { void __iomem *mem; struct ath_softc *sc; struct ieee80211_hw *hw; struct resource *res; const struct platform_device_id *id = platform_get_device_id(pdev); int irq; int ret = 0; struct ath_hw *ah; char hw_name[64]; if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data specified\n"); ret = -EINVAL; goto err_out; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "no memory resource found\n"); ret = -ENXIO; goto err_out; } mem = ioremap_nocache(res->start, resource_size(res)); if (mem == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -ENOMEM; goto err_out; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(&pdev->dev, "no IRQ resource found\n"); ret = -ENXIO; goto err_iounmap; } irq = res->start; hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); ret = -ENOMEM; goto err_iounmap; } SET_IEEE80211_DEV(hw, &pdev->dev); platform_set_drvdata(pdev, hw); sc = hw->priv; sc->hw = hw; sc->dev = &pdev->dev; sc->mem = mem; sc->irq = irq; /* Will be cleared in ath9k_start() */ set_bit(SC_OP_INVALID, &sc->sc_flags); ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); goto err_free_hw; } ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops); if (ret) { dev_err(&pdev->dev, "failed to initialize device\n"); goto err_irq; } ah = sc->sc_ah; ath9k_hw_name(ah, hw_name, sizeof(hw_name)); wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n", hw_name, (unsigned long)mem, irq); return 0; err_irq: free_irq(irq, sc); err_free_hw: ieee80211_free_hw(hw); platform_set_drvdata(pdev, NULL); err_iounmap: iounmap(mem); err_out: return ret; } static int ath_ahb_remove(struct platform_device *pdev) { struct ieee80211_hw *hw = platform_get_drvdata(pdev); if (hw) { struct ath_softc *sc = hw->priv; void __iomem *mem = sc->mem; ath9k_deinit_device(sc); free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); iounmap(mem); platform_set_drvdata(pdev, NULL); } return 0; } static struct platform_driver ath_ahb_driver = { .probe = ath_ahb_probe, .remove = ath_ahb_remove, .driver = { .name = "ath9k", .owner = THIS_MODULE, }, .id_table = ath9k_platform_id_table, }; MODULE_DEVICE_TABLE(platform, ath9k_platform_id_table); int ath_ahb_init(void) { return platform_driver_register(&ath_ahb_driver); } void ath_ahb_exit(void) { platform_driver_unregister(&ath_ahb_driver); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath9k/Kconfig0000644000175000017500000000700112026211315023556 0ustar mcgrofmcgrofconfig ATH9K_HW tristate config ATH9K_COMMON tristate config ATH9K_DFS_DEBUGFS def_bool y depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED config ATH9K_BTCOEX_SUPPORT bool "Atheros bluetooth coexistence support" depends on (ATH9K || ATH9K_HTC) default y ---help--- Say Y, if you want to use the ath9k/ath9k_htc radios together with Bluetooth modules in the same system. config ATH9K tristate "Atheros 802.11n wireless cards support" depends on MAC80211 select ATH9K_HW select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS select ATH9K_COMMON ---help--- This module adds support for wireless adapters based on Atheros IEEE 802.11n AR5008, AR9001 and AR9002 family of chipsets. For a specific list of supported external cards, laptops that already ship with these cards and APs that come with these cards refer to to ath9k wiki products page: http://wireless.kernel.org/en/users/Drivers/ath9k/products If you choose to build a module, it'll be called ath9k. config ATH9K_PCI bool "Atheros ath9k PCI/PCIe bus support" default y depends on ATH9K && PCI ---help--- This option enables the PCI bus support in ath9k. Say Y, if you have a compatible PCI/PCIe wireless card. config ATH9K_AHB bool "Atheros ath9k AHB bus support" depends on ATH9K default n ---help--- This option enables the AHB bus support in ath9k. Say Y, if you have a SoC with a compatible built-in wireless MAC. Say N if unsure. config ATH9K_DEBUGFS bool "Atheros ath9k debugging" depends on ATH9K && DEBUG_FS ---help--- Say Y, if you need access to ath9k's statistics for interrupts, rate control, etc. Also required for changing debug message flags at run time. config ATH9K_DFS_CERTIFIED bool "Atheros DFS support for certified platforms" depends on ATH9K && CFG80211_CERTIFICATION_ONUS default n ---help--- This option enables DFS support for initiating radiation on ath9k. There is no way to dynamically detect if a card was DFS certified and as such this is left as a build time option. This option should only be enabled by system integrators that can guarantee that all the platforms that their kernel will run on have obtained appropriate regulatory body certification for a respective Atheros card by using ath9k on the target shipping platforms. This is currently only a placeholder for future DFS support, as DFS support requires more components that still need to be developed. At this point enabling this option won't do anything except increase code size. config ATH9K_MAC_DEBUG bool "Atheros MAC statistics" depends on ATH9K_DEBUGFS default y ---help--- This option enables collection of statistics for Rx/Tx status data and some other MAC related statistics config ATH9K_RATE_CONTROL bool "Atheros ath9k rate control" depends on ATH9K default y ---help--- Say Y, if you want to use the ath9k specific rate control module instead of minstrel_ht. config ATH9K_HTC tristate "Atheros HTC based wireless cards support" depends on USB && MAC80211 select ATH9K_HW select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS select ATH9K_COMMON ---help--- Support for Atheros HTC based cards. Chipsets supported: AR9271 For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc The built module will be ath9k_htc. config ATH9K_HTC_DEBUGFS bool "Atheros ath9k_htc debugging" depends on ATH9K_HTC && DEBUG_FS ---help--- Say Y, if you need access to ath9k_htc's statistics. compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/0000755000175000017500000000000012026211315022426 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/usb.c0000644000175000017500000007451712026211315023401 0ustar mcgrofmcgrof/* * Copyright (c) 2007-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "debug.h" #include "core.h" /* constants */ #define TX_URB_COUNT 32 #define RX_URB_COUNT 32 #define ATH6KL_USB_RX_BUFFER_SIZE 1700 /* tx/rx pipes for usb */ enum ATH6KL_USB_PIPE_ID { ATH6KL_USB_PIPE_TX_CTRL = 0, ATH6KL_USB_PIPE_TX_DATA_LP, ATH6KL_USB_PIPE_TX_DATA_MP, ATH6KL_USB_PIPE_TX_DATA_HP, ATH6KL_USB_PIPE_RX_CTRL, ATH6KL_USB_PIPE_RX_DATA, ATH6KL_USB_PIPE_RX_DATA2, ATH6KL_USB_PIPE_RX_INT, ATH6KL_USB_PIPE_MAX }; #define ATH6KL_USB_PIPE_INVALID ATH6KL_USB_PIPE_MAX struct ath6kl_usb_pipe { struct list_head urb_list_head; struct usb_anchor urb_submitted; u32 urb_alloc; u32 urb_cnt; u32 urb_cnt_thresh; unsigned int usb_pipe_handle; u32 flags; u8 ep_address; u8 logical_pipe_num; struct ath6kl_usb *ar_usb; u16 max_packet_size; struct work_struct io_complete_work; struct sk_buff_head io_comp_queue; struct usb_endpoint_descriptor *ep_desc; }; #define ATH6KL_USB_PIPE_FLAG_TX (1 << 0) /* usb device object */ struct ath6kl_usb { /* protects pipe->urb_list_head and pipe->urb_cnt */ spinlock_t cs_lock; struct usb_device *udev; struct usb_interface *interface; struct ath6kl_usb_pipe pipes[ATH6KL_USB_PIPE_MAX]; u8 *diag_cmd_buffer; u8 *diag_resp_buffer; struct ath6kl *ar; }; /* usb urb object */ struct ath6kl_urb_context { struct list_head link; struct ath6kl_usb_pipe *pipe; struct sk_buff *skb; struct ath6kl *ar; }; /* USB endpoint definitions */ #define ATH6KL_USB_EP_ADDR_APP_CTRL_IN 0x81 #define ATH6KL_USB_EP_ADDR_APP_DATA_IN 0x82 #define ATH6KL_USB_EP_ADDR_APP_DATA2_IN 0x83 #define ATH6KL_USB_EP_ADDR_APP_INT_IN 0x84 #define ATH6KL_USB_EP_ADDR_APP_CTRL_OUT 0x01 #define ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT 0x02 #define ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT 0x03 #define ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT 0x04 /* diagnostic command defnitions */ #define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1 #define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2 #define ATH6KL_USB_CONTROL_REQ_DIAG_CMD 3 #define ATH6KL_USB_CONTROL_REQ_DIAG_RESP 4 #define ATH6KL_USB_CTRL_DIAG_CC_READ 0 #define ATH6KL_USB_CTRL_DIAG_CC_WRITE 1 struct ath6kl_usb_ctrl_diag_cmd_write { __le32 cmd; __le32 address; __le32 value; __le32 _pad[1]; } __packed; struct ath6kl_usb_ctrl_diag_cmd_read { __le32 cmd; __le32 address; } __packed; struct ath6kl_usb_ctrl_diag_resp_read { __le32 value; } __packed; /* function declarations */ static void ath6kl_usb_recv_complete(struct urb *urb); #define ATH6KL_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02) #define ATH6KL_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03) #define ATH6KL_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01) #define ATH6KL_USB_IS_DIR_IN(addr) ((addr) & 0x80) /* pipe/urb operations */ static struct ath6kl_urb_context * ath6kl_usb_alloc_urb_from_pipe(struct ath6kl_usb_pipe *pipe) { struct ath6kl_urb_context *urb_context = NULL; unsigned long flags; spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); if (!list_empty(&pipe->urb_list_head)) { urb_context = list_first_entry(&pipe->urb_list_head, struct ath6kl_urb_context, link); list_del(&urb_context->link); pipe->urb_cnt--; } spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); return urb_context; } static void ath6kl_usb_free_urb_to_pipe(struct ath6kl_usb_pipe *pipe, struct ath6kl_urb_context *urb_context) { unsigned long flags; spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); pipe->urb_cnt++; list_add(&urb_context->link, &pipe->urb_list_head); spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); } static void ath6kl_usb_cleanup_recv_urb(struct ath6kl_urb_context *urb_context) { if (urb_context->skb != NULL) { dev_kfree_skb(urb_context->skb); urb_context->skb = NULL; } ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); } static inline struct ath6kl_usb *ath6kl_usb_priv(struct ath6kl *ar) { return ar->hif_priv; } /* pipe resource allocation/cleanup */ static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, int urb_cnt) { struct ath6kl_urb_context *urb_context; int status = 0, i; INIT_LIST_HEAD(&pipe->urb_list_head); init_usb_anchor(&pipe->urb_submitted); for (i = 0; i < urb_cnt; i++) { urb_context = kzalloc(sizeof(struct ath6kl_urb_context), GFP_KERNEL); if (urb_context == NULL) /* FIXME: set status to -ENOMEM */ break; urb_context->pipe = pipe; /* * we are only allocate the urb contexts here, the actual URB * is allocated from the kernel as needed to do a transaction */ pipe->urb_alloc++; ath6kl_usb_free_urb_to_pipe(pipe, urb_context); } ath6kl_dbg(ATH6KL_DBG_USB, "ath6kl usb: alloc resources lpipe:%d hpipe:0x%X urbs:%d\n", pipe->logical_pipe_num, pipe->usb_pipe_handle, pipe->urb_alloc); return status; } static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe) { struct ath6kl_urb_context *urb_context; if (pipe->ar_usb == NULL) { /* nothing allocated for this pipe */ return; } ath6kl_dbg(ATH6KL_DBG_USB, "ath6kl usb: free resources lpipe:%d" "hpipe:0x%X urbs:%d avail:%d\n", pipe->logical_pipe_num, pipe->usb_pipe_handle, pipe->urb_alloc, pipe->urb_cnt); if (pipe->urb_alloc != pipe->urb_cnt) { ath6kl_dbg(ATH6KL_DBG_USB, "ath6kl usb: urb leak! lpipe:%d" "hpipe:0x%X urbs:%d avail:%d\n", pipe->logical_pipe_num, pipe->usb_pipe_handle, pipe->urb_alloc, pipe->urb_cnt); } while (true) { urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); if (urb_context == NULL) break; kfree(urb_context); } } static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb) { int i; for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) ath6kl_usb_free_pipe_resources(&ar_usb->pipes[i]); } static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *ar_usb, u8 ep_address, int *urb_count) { u8 pipe_num = ATH6KL_USB_PIPE_INVALID; switch (ep_address) { case ATH6KL_USB_EP_ADDR_APP_CTRL_IN: pipe_num = ATH6KL_USB_PIPE_RX_CTRL; *urb_count = RX_URB_COUNT; break; case ATH6KL_USB_EP_ADDR_APP_DATA_IN: pipe_num = ATH6KL_USB_PIPE_RX_DATA; *urb_count = RX_URB_COUNT; break; case ATH6KL_USB_EP_ADDR_APP_INT_IN: pipe_num = ATH6KL_USB_PIPE_RX_INT; *urb_count = RX_URB_COUNT; break; case ATH6KL_USB_EP_ADDR_APP_DATA2_IN: pipe_num = ATH6KL_USB_PIPE_RX_DATA2; *urb_count = RX_URB_COUNT; break; case ATH6KL_USB_EP_ADDR_APP_CTRL_OUT: pipe_num = ATH6KL_USB_PIPE_TX_CTRL; *urb_count = TX_URB_COUNT; break; case ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT: pipe_num = ATH6KL_USB_PIPE_TX_DATA_LP; *urb_count = TX_URB_COUNT; break; case ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT: pipe_num = ATH6KL_USB_PIPE_TX_DATA_MP; *urb_count = TX_URB_COUNT; break; case ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT: pipe_num = ATH6KL_USB_PIPE_TX_DATA_HP; *urb_count = TX_URB_COUNT; break; default: /* note: there may be endpoints not currently used */ break; } return pipe_num; } static int ath6kl_usb_setup_pipe_resources(struct ath6kl_usb *ar_usb) { struct usb_interface *interface = ar_usb->interface; struct usb_host_interface *iface_desc = interface->cur_altsetting; struct usb_endpoint_descriptor *endpoint; struct ath6kl_usb_pipe *pipe; int i, urbcount, status = 0; u8 pipe_num; ath6kl_dbg(ATH6KL_DBG_USB, "setting up USB Pipes using interface\n"); /* walk decriptors and setup pipes */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { ath6kl_dbg(ATH6KL_DBG_USB, "%s Bulk Ep:0x%2.2X maxpktsz:%d\n", ATH6KL_USB_IS_DIR_IN (endpoint->bEndpointAddress) ? "RX" : "TX", endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize)); } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { ath6kl_dbg(ATH6KL_DBG_USB, "%s Int Ep:0x%2.2X maxpktsz:%d interval:%d\n", ATH6KL_USB_IS_DIR_IN (endpoint->bEndpointAddress) ? "RX" : "TX", endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval); } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { /* TODO for ISO */ ath6kl_dbg(ATH6KL_DBG_USB, "%s ISOC Ep:0x%2.2X maxpktsz:%d interval:%d\n", ATH6KL_USB_IS_DIR_IN (endpoint->bEndpointAddress) ? "RX" : "TX", endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval); } urbcount = 0; pipe_num = ath6kl_usb_get_logical_pipe_num(ar_usb, endpoint->bEndpointAddress, &urbcount); if (pipe_num == ATH6KL_USB_PIPE_INVALID) continue; pipe = &ar_usb->pipes[pipe_num]; if (pipe->ar_usb != NULL) { /* hmmm..pipe was already setup */ continue; } pipe->ar_usb = ar_usb; pipe->logical_pipe_num = pipe_num; pipe->ep_address = endpoint->bEndpointAddress; pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { pipe->usb_pipe_handle = usb_rcvbulkpipe(ar_usb->udev, pipe->ep_address); } else { pipe->usb_pipe_handle = usb_sndbulkpipe(ar_usb->udev, pipe->ep_address); } } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { pipe->usb_pipe_handle = usb_rcvintpipe(ar_usb->udev, pipe->ep_address); } else { pipe->usb_pipe_handle = usb_sndintpipe(ar_usb->udev, pipe->ep_address); } } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { /* TODO for ISO */ if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { pipe->usb_pipe_handle = usb_rcvisocpipe(ar_usb->udev, pipe->ep_address); } else { pipe->usb_pipe_handle = usb_sndisocpipe(ar_usb->udev, pipe->ep_address); } } pipe->ep_desc = endpoint; if (!ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) pipe->flags |= ATH6KL_USB_PIPE_FLAG_TX; status = ath6kl_usb_alloc_pipe_resources(pipe, urbcount); if (status != 0) break; } return status; } /* pipe operations */ static void ath6kl_usb_post_recv_transfers(struct ath6kl_usb_pipe *recv_pipe, int buffer_length) { struct ath6kl_urb_context *urb_context; struct urb *urb; int usb_status; while (true) { urb_context = ath6kl_usb_alloc_urb_from_pipe(recv_pipe); if (urb_context == NULL) break; urb_context->skb = dev_alloc_skb(buffer_length); if (urb_context->skb == NULL) goto err_cleanup_urb; urb = usb_alloc_urb(0, GFP_ATOMIC); if (urb == NULL) goto err_cleanup_urb; usb_fill_bulk_urb(urb, recv_pipe->ar_usb->udev, recv_pipe->usb_pipe_handle, urb_context->skb->data, buffer_length, ath6kl_usb_recv_complete, urb_context); ath6kl_dbg(ATH6KL_DBG_USB_BULK, "ath6kl usb: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n", recv_pipe->logical_pipe_num, recv_pipe->usb_pipe_handle, recv_pipe->ep_address, buffer_length, urb_context->skb); usb_anchor_urb(urb, &recv_pipe->urb_submitted); usb_status = usb_submit_urb(urb, GFP_ATOMIC); if (usb_status) { ath6kl_dbg(ATH6KL_DBG_USB_BULK, "ath6kl usb : usb bulk recv failed %d\n", usb_status); usb_unanchor_urb(urb); usb_free_urb(urb); goto err_cleanup_urb; } usb_free_urb(urb); } return; err_cleanup_urb: ath6kl_usb_cleanup_recv_urb(urb_context); return; } static void ath6kl_usb_flush_all(struct ath6kl_usb *ar_usb) { int i; for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { if (ar_usb->pipes[i].ar_usb != NULL) usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted); } /* * Flushing any pending I/O may schedule work this call will block * until all scheduled work runs to completion. */ flush_scheduled_work(); } static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb) { /* * note: control pipe is no longer used * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_cnt_thresh = * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_alloc/2; * ath6kl_usb_post_recv_transfers(&ar_usb-> * pipes[ATH6KL_USB_PIPE_RX_CTRL], * ATH6KL_USB_RX_BUFFER_SIZE); */ ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2; ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA], ATH6KL_USB_RX_BUFFER_SIZE); } /* hif usb rx/tx completion functions */ static void ath6kl_usb_recv_complete(struct urb *urb) { struct ath6kl_urb_context *urb_context = urb->context; struct ath6kl_usb_pipe *pipe = urb_context->pipe; struct sk_buff *skb = NULL; int status = 0; ath6kl_dbg(ATH6KL_DBG_USB_BULK, "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__, pipe->logical_pipe_num, urb->status, urb->actual_length, urb); if (urb->status != 0) { status = -EIO; switch (urb->status) { case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* * no need to spew these errors when device * removed or urb killed due to driver shutdown */ status = -ECANCELED; break; default: ath6kl_dbg(ATH6KL_DBG_USB_BULK, "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", __func__, pipe->logical_pipe_num, pipe->ep_address, urb->status); break; } goto cleanup_recv_urb; } if (urb->actual_length == 0) goto cleanup_recv_urb; skb = urb_context->skb; /* we are going to pass it up */ urb_context->skb = NULL; skb_put(skb, urb->actual_length); /* note: queue implements a lock */ skb_queue_tail(&pipe->io_comp_queue, skb); schedule_work(&pipe->io_complete_work); cleanup_recv_urb: ath6kl_usb_cleanup_recv_urb(urb_context); if (status == 0 && pipe->urb_cnt >= pipe->urb_cnt_thresh) { /* our free urbs are piling up, post more transfers */ ath6kl_usb_post_recv_transfers(pipe, ATH6KL_USB_RX_BUFFER_SIZE); } } static void ath6kl_usb_usb_transmit_complete(struct urb *urb) { struct ath6kl_urb_context *urb_context = urb->context; struct ath6kl_usb_pipe *pipe = urb_context->pipe; struct sk_buff *skb; ath6kl_dbg(ATH6KL_DBG_USB_BULK, "%s: pipe: %d, stat:%d, len:%d\n", __func__, pipe->logical_pipe_num, urb->status, urb->actual_length); if (urb->status != 0) { ath6kl_dbg(ATH6KL_DBG_USB_BULK, "%s: pipe: %d, failed:%d\n", __func__, pipe->logical_pipe_num, urb->status); } skb = urb_context->skb; urb_context->skb = NULL; ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); /* note: queue implements a lock */ skb_queue_tail(&pipe->io_comp_queue, skb); schedule_work(&pipe->io_complete_work); } static void ath6kl_usb_io_comp_work(struct work_struct *work) { struct ath6kl_usb_pipe *pipe = container_of(work, struct ath6kl_usb_pipe, io_complete_work); struct ath6kl_usb *ar_usb; struct sk_buff *skb; ar_usb = pipe->ar_usb; while ((skb = skb_dequeue(&pipe->io_comp_queue))) { if (pipe->flags & ATH6KL_USB_PIPE_FLAG_TX) { ath6kl_dbg(ATH6KL_DBG_USB_BULK, "ath6kl usb xmit callback buf:0x%p\n", skb); ath6kl_core_tx_complete(ar_usb->ar, skb); } else { ath6kl_dbg(ATH6KL_DBG_USB_BULK, "ath6kl usb recv callback buf:0x%p\n", skb); ath6kl_core_rx_complete(ar_usb->ar, skb, pipe->logical_pipe_num); } } } #define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)) #define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read)) static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) { ath6kl_usb_flush_all(ar_usb); ath6kl_usb_cleanup_pipe_resources(ar_usb); usb_set_intfdata(ar_usb->interface, NULL); kfree(ar_usb->diag_cmd_buffer); kfree(ar_usb->diag_resp_buffer); kfree(ar_usb); } static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) { struct usb_device *dev = interface_to_usbdev(interface); struct ath6kl_usb *ar_usb; struct ath6kl_usb_pipe *pipe; int status = 0; int i; ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL); if (ar_usb == NULL) goto fail_ath6kl_usb_create; usb_set_intfdata(interface, ar_usb); spin_lock_init(&(ar_usb->cs_lock)); ar_usb->udev = dev; ar_usb->interface = interface; for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { pipe = &ar_usb->pipes[i]; INIT_WORK(&pipe->io_complete_work, ath6kl_usb_io_comp_work); skb_queue_head_init(&pipe->io_comp_queue); } ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL); if (ar_usb->diag_cmd_buffer == NULL) { status = -ENOMEM; goto fail_ath6kl_usb_create; } ar_usb->diag_resp_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_RESP, GFP_KERNEL); if (ar_usb->diag_resp_buffer == NULL) { status = -ENOMEM; goto fail_ath6kl_usb_create; } status = ath6kl_usb_setup_pipe_resources(ar_usb); fail_ath6kl_usb_create: if (status != 0) { ath6kl_usb_destroy(ar_usb); ar_usb = NULL; } return ar_usb; } static void ath6kl_usb_device_detached(struct usb_interface *interface) { struct ath6kl_usb *ar_usb; ar_usb = usb_get_intfdata(interface); if (ar_usb == NULL) return; ath6kl_stop_txrx(ar_usb->ar); /* Delay to wait for the target to reboot */ mdelay(20); ath6kl_core_cleanup(ar_usb->ar); ath6kl_usb_destroy(ar_usb); } /* exported hif usb APIs for htc pipe */ static void hif_start(struct ath6kl *ar) { struct ath6kl_usb *device = ath6kl_usb_priv(ar); int i; ath6kl_usb_start_recv_pipes(device); /* set the TX resource avail threshold for each TX pipe */ for (i = ATH6KL_USB_PIPE_TX_CTRL; i <= ATH6KL_USB_PIPE_TX_DATA_HP; i++) { device->pipes[i].urb_cnt_thresh = device->pipes[i].urb_alloc / 2; } } static int ath6kl_usb_send(struct ath6kl *ar, u8 PipeID, struct sk_buff *hdr_skb, struct sk_buff *skb) { struct ath6kl_usb *device = ath6kl_usb_priv(ar); struct ath6kl_usb_pipe *pipe = &device->pipes[PipeID]; struct ath6kl_urb_context *urb_context; int usb_status, status = 0; struct urb *urb; u8 *data; u32 len; ath6kl_dbg(ATH6KL_DBG_USB_BULK, "+%s pipe : %d, buf:0x%p\n", __func__, PipeID, skb); urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); if (urb_context == NULL) { /* * TODO: it is possible to run out of urbs if * 2 endpoints map to the same pipe ID */ ath6kl_dbg(ATH6KL_DBG_USB_BULK, "%s pipe:%d no urbs left. URB Cnt : %d\n", __func__, PipeID, pipe->urb_cnt); status = -ENOMEM; goto fail_hif_send; } urb_context->skb = skb; data = skb->data; len = skb->len; urb = usb_alloc_urb(0, GFP_ATOMIC); if (urb == NULL) { status = -ENOMEM; ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); goto fail_hif_send; } usb_fill_bulk_urb(urb, device->udev, pipe->usb_pipe_handle, data, len, ath6kl_usb_usb_transmit_complete, urb_context); if ((len % pipe->max_packet_size) == 0) { /* hit a max packet boundary on this pipe */ urb->transfer_flags |= URB_ZERO_PACKET; } ath6kl_dbg(ATH6KL_DBG_USB_BULK, "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n", pipe->logical_pipe_num, pipe->usb_pipe_handle, pipe->ep_address, len); usb_anchor_urb(urb, &pipe->urb_submitted); usb_status = usb_submit_urb(urb, GFP_ATOMIC); if (usb_status) { ath6kl_dbg(ATH6KL_DBG_USB_BULK, "ath6kl usb : usb bulk transmit failed %d\n", usb_status); usb_unanchor_urb(urb); ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); status = -EINVAL; } usb_free_urb(urb); fail_hif_send: return status; } static void hif_stop(struct ath6kl *ar) { struct ath6kl_usb *device = ath6kl_usb_priv(ar); ath6kl_usb_flush_all(device); } static void ath6kl_usb_get_default_pipe(struct ath6kl *ar, u8 *ul_pipe, u8 *dl_pipe) { *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL; *dl_pipe = ATH6KL_USB_PIPE_RX_CTRL; } static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, u8 *ul_pipe, u8 *dl_pipe) { int status = 0; switch (svc_id) { case HTC_CTRL_RSVD_SVC: case WMI_CONTROL_SVC: *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL; /* due to large control packets, shift to data pipe */ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; case WMI_DATA_BE_SVC: case WMI_DATA_BK_SVC: *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; /* * Disable rxdata2 directly, it will be enabled * if FW enable rxdata2 */ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; case WMI_DATA_VI_SVC: *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; /* * Disable rxdata2 directly, it will be enabled * if FW enable rxdata2 */ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; case WMI_DATA_VO_SVC: *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_HP; /* * Disable rxdata2 directly, it will be enabled * if FW enable rxdata2 */ *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; default: status = -EPERM; break; } return status; } static u16 ath6kl_usb_get_free_queue_number(struct ath6kl *ar, u8 pipe_id) { struct ath6kl_usb *device = ath6kl_usb_priv(ar); return device->pipes[pipe_id].urb_cnt; } static void hif_detach_htc(struct ath6kl *ar) { struct ath6kl_usb *device = ath6kl_usb_priv(ar); ath6kl_usb_flush_all(device); } static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb, u8 req, u16 value, u16 index, void *data, u32 size) { u8 *buf = NULL; int ret; if (size > 0) { buf = kmalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; memcpy(buf, data, size); } /* note: if successful returns number of bytes transfered */ ret = usb_control_msg(ar_usb->udev, usb_sndctrlpipe(ar_usb->udev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, buf, size, 1000); if (ret < 0) { ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n", __func__, ret); } kfree(buf); return 0; } static int ath6kl_usb_submit_ctrl_in(struct ath6kl_usb *ar_usb, u8 req, u16 value, u16 index, void *data, u32 size) { u8 *buf = NULL; int ret; if (size > 0) { buf = kmalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; } /* note: if successful returns number of bytes transfered */ ret = usb_control_msg(ar_usb->udev, usb_rcvctrlpipe(ar_usb->udev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, buf, size, 2 * HZ); if (ret < 0) { ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n", __func__, ret); } memcpy((u8 *) data, buf, size); kfree(buf); return 0; } static int ath6kl_usb_ctrl_msg_exchange(struct ath6kl_usb *ar_usb, u8 req_val, u8 *req_buf, u32 req_len, u8 resp_val, u8 *resp_buf, u32 *resp_len) { int ret; /* send command */ ret = ath6kl_usb_submit_ctrl_out(ar_usb, req_val, 0, 0, req_buf, req_len); if (ret != 0) return ret; if (resp_buf == NULL) { /* no expected response */ return ret; } /* get response */ ret = ath6kl_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0, resp_buf, *resp_len); return ret; } static int ath6kl_usb_diag_read32(struct ath6kl *ar, u32 address, u32 *data) { struct ath6kl_usb *ar_usb = ar->hif_priv; struct ath6kl_usb_ctrl_diag_resp_read *resp; struct ath6kl_usb_ctrl_diag_cmd_read *cmd; u32 resp_len; int ret; cmd = (struct ath6kl_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer; memset(cmd, 0, sizeof(*cmd)); cmd->cmd = ATH6KL_USB_CTRL_DIAG_CC_READ; cmd->address = cpu_to_le32(address); resp_len = sizeof(*resp); ret = ath6kl_usb_ctrl_msg_exchange(ar_usb, ATH6KL_USB_CONTROL_REQ_DIAG_CMD, (u8 *) cmd, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write), ATH6KL_USB_CONTROL_REQ_DIAG_RESP, ar_usb->diag_resp_buffer, &resp_len); if (ret) return ret; resp = (struct ath6kl_usb_ctrl_diag_resp_read *) ar_usb->diag_resp_buffer; *data = le32_to_cpu(resp->value); return ret; } static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data) { struct ath6kl_usb *ar_usb = ar->hif_priv; struct ath6kl_usb_ctrl_diag_cmd_write *cmd; cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer; memset(cmd, 0, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)); cmd->cmd = cpu_to_le32(ATH6KL_USB_CTRL_DIAG_CC_WRITE); cmd->address = cpu_to_le32(address); cmd->value = data; return ath6kl_usb_ctrl_msg_exchange(ar_usb, ATH6KL_USB_CONTROL_REQ_DIAG_CMD, (u8 *) cmd, sizeof(*cmd), 0, NULL, NULL); } static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) { struct ath6kl_usb *ar_usb = ar->hif_priv; int ret; /* get response */ ret = ath6kl_usb_submit_ctrl_in(ar_usb, ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP, 0, 0, buf, len); if (ret != 0) { ath6kl_err("Unable to read the bmi data from the device: %d\n", ret); return ret; } return 0; } static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) { struct ath6kl_usb *ar_usb = ar->hif_priv; int ret; /* send command */ ret = ath6kl_usb_submit_ctrl_out(ar_usb, ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD, 0, 0, buf, len); if (ret != 0) { ath6kl_err("unable to send the bmi data to the device: %d\n", ret); return ret; } return 0; } static int ath6kl_usb_power_on(struct ath6kl *ar) { hif_start(ar); return 0; } static int ath6kl_usb_power_off(struct ath6kl *ar) { hif_detach_htc(ar); return 0; } static void ath6kl_usb_stop(struct ath6kl *ar) { hif_stop(ar); } static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar) { /* * USB doesn't support it. Just return. */ return; } static const struct ath6kl_hif_ops ath6kl_usb_ops = { .diag_read32 = ath6kl_usb_diag_read32, .diag_write32 = ath6kl_usb_diag_write32, .bmi_read = ath6kl_usb_bmi_read, .bmi_write = ath6kl_usb_bmi_write, .power_on = ath6kl_usb_power_on, .power_off = ath6kl_usb_power_off, .stop = ath6kl_usb_stop, .pipe_send = ath6kl_usb_send, .pipe_get_default = ath6kl_usb_get_default_pipe, .pipe_map_service = ath6kl_usb_map_service_pipe, .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number, .cleanup_scatter = ath6kl_usb_cleanup_scatter, }; /* ath6kl usb driver registered functions */ static int ath6kl_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(interface); struct ath6kl *ar; struct ath6kl_usb *ar_usb = NULL; int vendor_id, product_id; int ret = 0; usb_get_dev(dev); vendor_id = le16_to_cpu(dev->descriptor.idVendor); product_id = le16_to_cpu(dev->descriptor.idProduct); ath6kl_dbg(ATH6KL_DBG_USB, "vendor_id = %04x\n", vendor_id); ath6kl_dbg(ATH6KL_DBG_USB, "product_id = %04x\n", product_id); if (interface->cur_altsetting) ath6kl_dbg(ATH6KL_DBG_USB, "USB Interface %d\n", interface->cur_altsetting->desc.bInterfaceNumber); if (dev->speed == USB_SPEED_HIGH) ath6kl_dbg(ATH6KL_DBG_USB, "USB 2.0 Host\n"); else ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n"); ar_usb = ath6kl_usb_create(interface); if (ar_usb == NULL) { ret = -ENOMEM; goto err_usb_put; } ar = ath6kl_core_create(&ar_usb->udev->dev); if (ar == NULL) { ath6kl_err("Failed to alloc ath6kl core\n"); ret = -ENOMEM; goto err_usb_destroy; } ar->hif_priv = ar_usb; ar->hif_type = ATH6KL_HIF_TYPE_USB; ar->hif_ops = &ath6kl_usb_ops; ar->mbox_info.block_size = 16; ar->bmi.max_data_size = 252; ar_usb->ar = ar; ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_PIPE); if (ret) { ath6kl_err("Failed to init ath6kl core: %d\n", ret); goto err_core_free; } return ret; err_core_free: ath6kl_core_destroy(ar); err_usb_destroy: ath6kl_usb_destroy(ar_usb); err_usb_put: usb_put_dev(dev); return ret; } static void ath6kl_usb_remove(struct usb_interface *interface) { usb_put_dev(interface_to_usbdev(interface)); ath6kl_usb_device_detached(interface); } #ifdef CONFIG_PM static int ath6kl_usb_suspend(struct usb_interface *interface, pm_message_t message) { struct ath6kl_usb *device; device = usb_get_intfdata(interface); ath6kl_usb_flush_all(device); return 0; } static int ath6kl_usb_resume(struct usb_interface *interface) { struct ath6kl_usb *device; device = usb_get_intfdata(interface); ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA], ATH6KL_USB_RX_BUFFER_SIZE); ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA2], ATH6KL_USB_RX_BUFFER_SIZE); return 0; } static int ath6kl_usb_reset_resume(struct usb_interface *intf) { if (usb_get_intfdata(intf)) ath6kl_usb_remove(intf); return 0; } #else #define ath6kl_usb_suspend NULL #define ath6kl_usb_resume NULL #define ath6kl_usb_reset_resume NULL #endif /* table of devices that work with this driver */ static struct usb_device_id ath6kl_usb_ids[] = { {USB_DEVICE(0x0cf3, 0x9374)}, { /* Terminating entry */ }, }; MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids); static struct usb_driver ath6kl_usb_driver = { .name = "ath6kl_usb", .probe = ath6kl_usb_probe, .suspend = ath6kl_usb_suspend, .resume = ath6kl_usb_resume, .reset_resume = ath6kl_usb_reset_resume, .disconnect = ath6kl_usb_remove, .id_table = ath6kl_usb_ids, .supports_autosuspend = true, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; static int ath6kl_usb_init(void) { usb_register(&ath6kl_usb_driver); return 0; } static void ath6kl_usb_exit(void) { usb_deregister(&ath6kl_usb_driver); } module_init(ath6kl_usb_init); module_exit(ath6kl_usb_exit); MODULE_AUTHOR("Atheros Communications, Inc."); MODULE_DESCRIPTION("Driver support for Atheros AR600x USB devices"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/txrx.c0000644000175000017500000013015012026211315023577 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "core.h" #include "debug.h" #include "htc-ops.h" /* * tid - tid_mux0..tid_mux3 * aid - tid_mux4..tid_mux7 */ #define ATH6KL_TID_MASK 0xf #define ATH6KL_AID_SHIFT 4 static inline u8 ath6kl_get_tid(u8 tid_mux) { return tid_mux & ATH6KL_TID_MASK; } static inline u8 ath6kl_get_aid(u8 tid_mux) { return tid_mux >> ATH6KL_AID_SHIFT; } static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev, u32 *map_no) { struct ath6kl *ar = ath6kl_priv(dev); struct ethhdr *eth_hdr; u32 i, ep_map = -1; u8 *datap; *map_no = 0; datap = skb->data; eth_hdr = (struct ethhdr *) (datap + sizeof(struct wmi_data_hdr)); if (is_multicast_ether_addr(eth_hdr->h_dest)) return ENDPOINT_2; for (i = 0; i < ar->node_num; i++) { if (memcmp(eth_hdr->h_dest, ar->node_map[i].mac_addr, ETH_ALEN) == 0) { *map_no = i + 1; ar->node_map[i].tx_pend++; return ar->node_map[i].ep_id; } if ((ep_map == -1) && !ar->node_map[i].tx_pend) ep_map = i; } if (ep_map == -1) { ep_map = ar->node_num; ar->node_num++; if (ar->node_num > MAX_NODE_NUM) return ENDPOINT_UNUSED; } memcpy(ar->node_map[ep_map].mac_addr, eth_hdr->h_dest, ETH_ALEN); for (i = ENDPOINT_2; i <= ENDPOINT_5; i++) { if (!ar->tx_pending[i]) { ar->node_map[ep_map].ep_id = i; break; } /* * No free endpoint is available, start redistribution on * the inuse endpoints. */ if (i == ENDPOINT_5) { ar->node_map[ep_map].ep_id = ar->next_ep_id; ar->next_ep_id++; if (ar->next_ep_id > ENDPOINT_5) ar->next_ep_id = ENDPOINT_2; } } *map_no = ep_map + 1; ar->node_map[ep_map].tx_pend++; return ar->node_map[ep_map].ep_id; } static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn, struct ath6kl_vif *vif, struct sk_buff *skb, u32 *flags) { struct ath6kl *ar = vif->ar; bool is_apsdq_empty = false; struct ethhdr *datap = (struct ethhdr *) skb->data; u8 up = 0, traffic_class, *ip_hdr; u16 ether_type; struct ath6kl_llc_snap_hdr *llc_hdr; if (conn->sta_flags & STA_PS_APSD_TRIGGER) { /* * This tx is because of a uAPSD trigger, determine * more and EOSP bit. Set EOSP if queue is empty * or sufficient frames are delivered for this trigger. */ spin_lock_bh(&conn->psq_lock); if (!skb_queue_empty(&conn->apsdq)) *flags |= WMI_DATA_HDR_FLAGS_MORE; else if (conn->sta_flags & STA_PS_APSD_EOSP) *flags |= WMI_DATA_HDR_FLAGS_EOSP; *flags |= WMI_DATA_HDR_FLAGS_UAPSD; spin_unlock_bh(&conn->psq_lock); return false; } else if (!conn->apsd_info) return false; if (test_bit(WMM_ENABLED, &vif->flags)) { ether_type = be16_to_cpu(datap->h_proto); if (is_ethertype(ether_type)) { /* packet is in DIX format */ ip_hdr = (u8 *)(datap + 1); } else { /* packet is in 802.3 format */ llc_hdr = (struct ath6kl_llc_snap_hdr *) (datap + 1); ether_type = be16_to_cpu(llc_hdr->eth_type); ip_hdr = (u8 *)(llc_hdr + 1); } if (ether_type == IP_ETHERTYPE) up = ath6kl_wmi_determine_user_priority( ip_hdr, 0); } traffic_class = ath6kl_wmi_get_traffic_class(up); if ((conn->apsd_info & (1 << traffic_class)) == 0) return false; /* Queue the frames if the STA is sleeping */ spin_lock_bh(&conn->psq_lock); is_apsdq_empty = skb_queue_empty(&conn->apsdq); skb_queue_tail(&conn->apsdq, skb); spin_unlock_bh(&conn->psq_lock); /* * If this is the first pkt getting queued * for this STA, update the PVB for this STA */ if (is_apsdq_empty) { ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi, vif->fw_vif_idx, conn->aid, 1, 0); } *flags |= WMI_DATA_HDR_FLAGS_UAPSD; return true; } static bool ath6kl_process_psq(struct ath6kl_sta *conn, struct ath6kl_vif *vif, struct sk_buff *skb, u32 *flags) { bool is_psq_empty = false; struct ath6kl *ar = vif->ar; if (conn->sta_flags & STA_PS_POLLED) { spin_lock_bh(&conn->psq_lock); if (!skb_queue_empty(&conn->psq)) *flags |= WMI_DATA_HDR_FLAGS_MORE; spin_unlock_bh(&conn->psq_lock); return false; } /* Queue the frames if the STA is sleeping */ spin_lock_bh(&conn->psq_lock); is_psq_empty = skb_queue_empty(&conn->psq); skb_queue_tail(&conn->psq, skb); spin_unlock_bh(&conn->psq_lock); /* * If this is the first pkt getting queued * for this STA, update the PVB for this * STA. */ if (is_psq_empty) ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 1); return true; } static bool ath6kl_powersave_ap(struct ath6kl_vif *vif, struct sk_buff *skb, u32 *flags) { struct ethhdr *datap = (struct ethhdr *) skb->data; struct ath6kl_sta *conn = NULL; bool ps_queued = false; struct ath6kl *ar = vif->ar; if (is_multicast_ether_addr(datap->h_dest)) { u8 ctr = 0; bool q_mcast = false; for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { if (ar->sta_list[ctr].sta_flags & STA_PS_SLEEP) { q_mcast = true; break; } } if (q_mcast) { /* * If this transmit is not because of a Dtim Expiry * q it. */ if (!test_bit(DTIM_EXPIRED, &vif->flags)) { bool is_mcastq_empty = false; spin_lock_bh(&ar->mcastpsq_lock); is_mcastq_empty = skb_queue_empty(&ar->mcastpsq); skb_queue_tail(&ar->mcastpsq, skb); spin_unlock_bh(&ar->mcastpsq_lock); /* * If this is the first Mcast pkt getting * queued indicate to the target to set the * BitmapControl LSB of the TIM IE. */ if (is_mcastq_empty) ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, MCAST_AID, 1); ps_queued = true; } else { /* * This transmit is because of Dtim expiry. * Determine if MoreData bit has to be set. */ spin_lock_bh(&ar->mcastpsq_lock); if (!skb_queue_empty(&ar->mcastpsq)) *flags |= WMI_DATA_HDR_FLAGS_MORE; spin_unlock_bh(&ar->mcastpsq_lock); } } } else { conn = ath6kl_find_sta(vif, datap->h_dest); if (!conn) { dev_kfree_skb(skb); /* Inform the caller that the skb is consumed */ return true; } if (conn->sta_flags & STA_PS_SLEEP) { ps_queued = ath6kl_process_uapsdq(conn, vif, skb, flags); if (!(*flags & WMI_DATA_HDR_FLAGS_UAPSD)) ps_queued = ath6kl_process_psq(conn, vif, skb, flags); } } return ps_queued; } /* Tx functions */ int ath6kl_control_tx(void *devt, struct sk_buff *skb, enum htc_endpoint_id eid) { struct ath6kl *ar = devt; int status = 0; struct ath6kl_cookie *cookie = NULL; if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) return -EACCES; spin_lock_bh(&ar->lock); ath6kl_dbg(ATH6KL_DBG_WLAN_TX, "%s: skb=0x%p, len=0x%x eid =%d\n", __func__, skb, skb->len, eid); if (test_bit(WMI_CTRL_EP_FULL, &ar->flag) && (eid == ar->ctrl_ep)) { /* * Control endpoint is full, don't allocate resources, we * are just going to drop this packet. */ cookie = NULL; ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n", skb, skb->len); } else cookie = ath6kl_alloc_cookie(ar); if (cookie == NULL) { spin_unlock_bh(&ar->lock); status = -ENOMEM; goto fail_ctrl_tx; } ar->tx_pending[eid]++; if (eid != ar->ctrl_ep) ar->total_tx_data_pend++; spin_unlock_bh(&ar->lock); cookie->skb = skb; cookie->map_no = 0; set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, eid, ATH6KL_CONTROL_PKT_TAG); cookie->htc_pkt.skb = skb; /* * This interface is asynchronous, if there is an error, cleanup * will happen in the TX completion callback. */ ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt); return 0; fail_ctrl_tx: dev_kfree_skb(skb); return status; } int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_cookie *cookie = NULL; enum htc_endpoint_id eid = ENDPOINT_UNUSED; struct ath6kl_vif *vif = netdev_priv(dev); u32 map_no = 0; u16 htc_tag = ATH6KL_DATA_PKT_TAG; u8 ac = 99 ; /* initialize to unmapped ac */ bool chk_adhoc_ps_mapping = false; int ret; struct wmi_tx_meta_v2 meta_v2; void *meta; u8 csum_start = 0, csum_dest = 0, csum = skb->ip_summed; u8 meta_ver = 0; u32 flags = 0; ath6kl_dbg(ATH6KL_DBG_WLAN_TX, "%s: skb=0x%p, data=0x%p, len=0x%x\n", __func__, skb, skb->data, skb->len); /* If target is not associated */ if (!test_bit(CONNECTED, &vif->flags)) goto fail_tx; if (WARN_ON_ONCE(ar->state != ATH6KL_STATE_ON)) goto fail_tx; if (!test_bit(WMI_READY, &ar->flag)) goto fail_tx; /* AP mode Power saving processing */ if (vif->nw_type == AP_NETWORK) { if (ath6kl_powersave_ap(vif, skb, &flags)) return 0; } if (test_bit(WMI_ENABLED, &ar->flag)) { if ((dev->features & NETIF_F_IP_CSUM) && (csum == CHECKSUM_PARTIAL)) { csum_start = skb->csum_start - (skb_network_header(skb) - skb->head) + sizeof(struct ath6kl_llc_snap_hdr); csum_dest = skb->csum_offset + csum_start; } if (skb_headroom(skb) < dev->needed_headroom) { struct sk_buff *tmp_skb = skb; skb = skb_realloc_headroom(skb, dev->needed_headroom); kfree_skb(tmp_skb); if (skb == NULL) { vif->net_stats.tx_dropped++; return 0; } } if (ath6kl_wmi_dix_2_dot3(ar->wmi, skb)) { ath6kl_err("ath6kl_wmi_dix_2_dot3 failed\n"); goto fail_tx; } if ((dev->features & NETIF_F_IP_CSUM) && (csum == CHECKSUM_PARTIAL)) { meta_v2.csum_start = csum_start; meta_v2.csum_dest = csum_dest; /* instruct target to calculate checksum */ meta_v2.csum_flags = WMI_META_V2_FLAG_CSUM_OFFLOAD; meta_ver = WMI_META_VERSION_2; meta = &meta_v2; } else { meta_ver = 0; meta = NULL; } ret = ath6kl_wmi_data_hdr_add(ar->wmi, skb, DATA_MSGTYPE, flags, 0, meta_ver, meta, vif->fw_vif_idx); if (ret) { ath6kl_warn("failed to add wmi data header:%d\n" , ret); goto fail_tx; } if ((vif->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags)) chk_adhoc_ps_mapping = true; else { /* get the stream mapping */ ret = ath6kl_wmi_implicit_create_pstream(ar->wmi, vif->fw_vif_idx, skb, 0, test_bit(WMM_ENABLED, &vif->flags), &ac); if (ret) goto fail_tx; } } else goto fail_tx; spin_lock_bh(&ar->lock); if (chk_adhoc_ps_mapping) eid = ath6kl_ibss_map_epid(skb, dev, &map_no); else eid = ar->ac2ep_map[ac]; if (eid == 0 || eid == ENDPOINT_UNUSED) { ath6kl_err("eid %d is not mapped!\n", eid); spin_unlock_bh(&ar->lock); goto fail_tx; } /* allocate resource for this packet */ cookie = ath6kl_alloc_cookie(ar); if (!cookie) { spin_unlock_bh(&ar->lock); goto fail_tx; } /* update counts while the lock is held */ ar->tx_pending[eid]++; ar->total_tx_data_pend++; spin_unlock_bh(&ar->lock); if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) && skb_cloned(skb)) { /* * We will touch (move the buffer data to align it. Since the * skb buffer is cloned and not only the header is changed, we * have to copy it to allow the changes. Since we are copying * the data here, we may as well align it by reserving suitable * headroom to avoid the memmove in ath6kl_htc_tx_buf_align(). */ struct sk_buff *nskb; nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC); if (nskb == NULL) goto fail_tx; kfree_skb(skb); skb = nskb; } cookie->skb = skb; cookie->map_no = map_no; set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, eid, htc_tag); cookie->htc_pkt.skb = skb; ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ", skb->data, skb->len); /* * HTC interface is asynchronous, if this fails, cleanup will * happen in the ath6kl_tx_complete callback. */ ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt); return 0; fail_tx: dev_kfree_skb(skb); vif->net_stats.tx_dropped++; vif->net_stats.tx_aborted_errors++; return 0; } /* indicate tx activity or inactivity on a WMI stream */ void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active) { struct ath6kl *ar = devt; enum htc_endpoint_id eid; int i; eid = ar->ac2ep_map[traffic_class]; if (!test_bit(WMI_ENABLED, &ar->flag)) goto notify_htc; spin_lock_bh(&ar->lock); ar->ac_stream_active[traffic_class] = active; if (active) { /* * Keep track of the active stream with the highest * priority. */ if (ar->ac_stream_pri_map[traffic_class] > ar->hiac_stream_active_pri) /* set the new highest active priority */ ar->hiac_stream_active_pri = ar->ac_stream_pri_map[traffic_class]; } else { /* * We may have to search for the next active stream * that is the highest priority. */ if (ar->hiac_stream_active_pri == ar->ac_stream_pri_map[traffic_class]) { /* * The highest priority stream just went inactive * reset and search for the "next" highest "active" * priority stream. */ ar->hiac_stream_active_pri = 0; for (i = 0; i < WMM_NUM_AC; i++) { if (ar->ac_stream_active[i] && (ar->ac_stream_pri_map[i] > ar->hiac_stream_active_pri)) /* * Set the new highest active * priority. */ ar->hiac_stream_active_pri = ar->ac_stream_pri_map[i]; } } } spin_unlock_bh(&ar->lock); notify_htc: /* notify HTC, this may cause credit distribution changes */ ath6kl_htc_activity_changed(ar->htc_target, eid, active); } enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, struct htc_packet *packet) { struct ath6kl *ar = target->dev->ar; struct ath6kl_vif *vif; enum htc_endpoint_id endpoint = packet->endpoint; enum htc_send_full_action action = HTC_SEND_FULL_KEEP; if (endpoint == ar->ctrl_ep) { /* * Under normal WMI if this is getting full, then something * is running rampant the host should not be exhausting the * WMI queue with too many commands the only exception to * this is during testing using endpointping. */ set_bit(WMI_CTRL_EP_FULL, &ar->flag); ath6kl_err("wmi ctrl ep is full\n"); return action; } if (packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG) return action; /* * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for * the highest active stream. */ if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] < ar->hiac_stream_active_pri && ar->cookie_count <= target->endpoint[endpoint].tx_drop_packet_threshold) /* * Give preference to the highest priority stream by * dropping the packets which overflowed. */ action = HTC_SEND_FULL_DROP; /* FIXME: Locking */ spin_lock_bh(&ar->list_lock); list_for_each_entry(vif, &ar->vif_list, list) { if (vif->nw_type == ADHOC_NETWORK || action != HTC_SEND_FULL_DROP) { spin_unlock_bh(&ar->list_lock); set_bit(NETQ_STOPPED, &vif->flags); netif_stop_queue(vif->ndev); return action; } } spin_unlock_bh(&ar->list_lock); return action; } /* TODO this needs to be looked at */ static void ath6kl_tx_clear_node_map(struct ath6kl_vif *vif, enum htc_endpoint_id eid, u32 map_no) { struct ath6kl *ar = vif->ar; u32 i; if (vif->nw_type != ADHOC_NETWORK) return; if (!ar->ibss_ps_enable) return; if (eid == ar->ctrl_ep) return; if (map_no == 0) return; map_no--; ar->node_map[map_no].tx_pend--; if (ar->node_map[map_no].tx_pend) return; if (map_no != (ar->node_num - 1)) return; for (i = ar->node_num; i > 0; i--) { if (ar->node_map[i - 1].tx_pend) break; memset(&ar->node_map[i - 1], 0, sizeof(struct ath6kl_node_mapping)); ar->node_num--; } } void ath6kl_tx_complete(struct htc_target *target, struct list_head *packet_queue) { struct ath6kl *ar = target->dev->ar; struct sk_buff_head skb_queue; struct htc_packet *packet; struct sk_buff *skb; struct ath6kl_cookie *ath6kl_cookie; u32 map_no = 0; int status; enum htc_endpoint_id eid; bool wake_event = false; bool flushing[ATH6KL_VIF_MAX] = {false}; u8 if_idx; struct ath6kl_vif *vif; skb_queue_head_init(&skb_queue); /* lock the driver as we update internal state */ spin_lock_bh(&ar->lock); /* reap completed packets */ while (!list_empty(packet_queue)) { packet = list_first_entry(packet_queue, struct htc_packet, list); list_del(&packet->list); ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt; if (!ath6kl_cookie) goto fatal; status = packet->status; skb = ath6kl_cookie->skb; eid = packet->endpoint; map_no = ath6kl_cookie->map_no; if (!skb || !skb->data) goto fatal; __skb_queue_tail(&skb_queue, skb); if (!status && (packet->act_len != skb->len)) goto fatal; ar->tx_pending[eid]--; if (eid != ar->ctrl_ep) ar->total_tx_data_pend--; if (eid == ar->ctrl_ep) { if (test_bit(WMI_CTRL_EP_FULL, &ar->flag)) clear_bit(WMI_CTRL_EP_FULL, &ar->flag); if (ar->tx_pending[eid] == 0) wake_event = true; } if (eid == ar->ctrl_ep) { if_idx = wmi_cmd_hdr_get_if_idx( (struct wmi_cmd_hdr *) packet->buf); } else { if_idx = wmi_data_hdr_get_if_idx( (struct wmi_data_hdr *) packet->buf); } vif = ath6kl_get_vif_by_index(ar, if_idx); if (!vif) { ath6kl_free_cookie(ar, ath6kl_cookie); continue; } if (status) { if (status == -ECANCELED) /* a packet was flushed */ flushing[if_idx] = true; vif->net_stats.tx_errors++; if (status != -ENOSPC && status != -ECANCELED) ath6kl_warn("tx complete error: %d\n", status); ath6kl_dbg(ATH6KL_DBG_WLAN_TX, "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n", __func__, skb, packet->buf, packet->act_len, eid, "error!"); } else { ath6kl_dbg(ATH6KL_DBG_WLAN_TX, "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n", __func__, skb, packet->buf, packet->act_len, eid, "OK"); flushing[if_idx] = false; vif->net_stats.tx_packets++; vif->net_stats.tx_bytes += skb->len; } ath6kl_tx_clear_node_map(vif, eid, map_no); ath6kl_free_cookie(ar, ath6kl_cookie); if (test_bit(NETQ_STOPPED, &vif->flags)) clear_bit(NETQ_STOPPED, &vif->flags); } spin_unlock_bh(&ar->lock); __skb_queue_purge(&skb_queue); /* FIXME: Locking */ spin_lock_bh(&ar->list_lock); list_for_each_entry(vif, &ar->vif_list, list) { if (test_bit(CONNECTED, &vif->flags) && !flushing[vif->fw_vif_idx]) { spin_unlock_bh(&ar->list_lock); netif_wake_queue(vif->ndev); spin_lock_bh(&ar->list_lock); } } spin_unlock_bh(&ar->list_lock); if (wake_event) wake_up(&ar->event_wq); return; fatal: WARN_ON(1); spin_unlock_bh(&ar->lock); return; } void ath6kl_tx_data_cleanup(struct ath6kl *ar) { int i; /* flush all the data (non-control) streams */ for (i = 0; i < WMM_NUM_AC; i++) ath6kl_htc_flush_txep(ar->htc_target, ar->ac2ep_map[i], ATH6KL_DATA_PKT_TAG); } /* Rx functions */ static void ath6kl_deliver_frames_to_nw_stack(struct net_device *dev, struct sk_buff *skb) { if (!skb) return; skb->dev = dev; if (!(skb->dev->flags & IFF_UP)) { dev_kfree_skb(skb); return; } skb->protocol = eth_type_trans(skb, skb->dev); netif_rx_ni(skb); } static void ath6kl_alloc_netbufs(struct sk_buff_head *q, u16 num) { struct sk_buff *skb; while (num) { skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE); if (!skb) { ath6kl_err("netbuf allocation failed\n"); return; } skb_queue_tail(q, skb); num--; } } static struct sk_buff *aggr_get_free_skb(struct aggr_info *p_aggr) { struct sk_buff *skb = NULL; if (skb_queue_len(&p_aggr->rx_amsdu_freeq) < (AGGR_NUM_OF_FREE_NETBUFS >> 2)) ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq, AGGR_NUM_OF_FREE_NETBUFS); skb = skb_dequeue(&p_aggr->rx_amsdu_freeq); return skb; } void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) { struct ath6kl *ar = target->dev->ar; struct sk_buff *skb; int rx_buf; int n_buf_refill; struct htc_packet *packet; struct list_head queue; n_buf_refill = ATH6KL_MAX_RX_BUFFERS - ath6kl_htc_get_rxbuf_num(ar->htc_target, endpoint); if (n_buf_refill <= 0) return; INIT_LIST_HEAD(&queue); ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: providing htc with %d buffers at eid=%d\n", __func__, n_buf_refill, endpoint); for (rx_buf = 0; rx_buf < n_buf_refill; rx_buf++) { skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE); if (!skb) break; packet = (struct htc_packet *) skb->head; if (!IS_ALIGNED((unsigned long) skb->data, 4)) skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_BUFFER_SIZE, endpoint); packet->skb = skb; list_add_tail(&packet->list, &queue); } if (!list_empty(&queue)) ath6kl_htc_add_rxbuf_multiple(ar->htc_target, &queue); } void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) { struct htc_packet *packet; struct sk_buff *skb; while (count) { skb = ath6kl_buf_alloc(ATH6KL_AMSDU_BUFFER_SIZE); if (!skb) return; packet = (struct htc_packet *) skb->head; if (!IS_ALIGNED((unsigned long) skb->data, 4)) skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_AMSDU_BUFFER_SIZE, 0); packet->skb = skb; spin_lock_bh(&ar->lock); list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue); spin_unlock_bh(&ar->lock); count--; } } /* * Callback to allocate a receive buffer for a pending packet. We use a * pre-allocated list of buffers of maximum AMSDU size (4K). */ struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target, enum htc_endpoint_id endpoint, int len) { struct ath6kl *ar = target->dev->ar; struct htc_packet *packet = NULL; struct list_head *pkt_pos; int refill_cnt = 0, depth = 0; ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: eid=%d, len:%d\n", __func__, endpoint, len); if ((len <= ATH6KL_BUFFER_SIZE) || (len > ATH6KL_AMSDU_BUFFER_SIZE)) return NULL; spin_lock_bh(&ar->lock); if (list_empty(&ar->amsdu_rx_buffer_queue)) { spin_unlock_bh(&ar->lock); refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS; goto refill_buf; } packet = list_first_entry(&ar->amsdu_rx_buffer_queue, struct htc_packet, list); list_del(&packet->list); list_for_each(pkt_pos, &ar->amsdu_rx_buffer_queue) depth++; refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS - depth; spin_unlock_bh(&ar->lock); /* set actual endpoint ID */ packet->endpoint = endpoint; refill_buf: if (refill_cnt >= ATH6KL_AMSDU_REFILL_THRESHOLD) ath6kl_refill_amsdu_rxbufs(ar, refill_cnt); return packet; } static void aggr_slice_amsdu(struct aggr_info *p_aggr, struct rxtid *rxtid, struct sk_buff *skb) { struct sk_buff *new_skb; struct ethhdr *hdr; u16 frame_8023_len, payload_8023_len, mac_hdr_len, amsdu_len; u8 *framep; mac_hdr_len = sizeof(struct ethhdr); framep = skb->data + mac_hdr_len; amsdu_len = skb->len - mac_hdr_len; while (amsdu_len > mac_hdr_len) { hdr = (struct ethhdr *) framep; payload_8023_len = ntohs(hdr->h_proto); if (payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN || payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) { ath6kl_err("802.3 AMSDU frame bound check failed. len %d\n", payload_8023_len); break; } frame_8023_len = payload_8023_len + mac_hdr_len; new_skb = aggr_get_free_skb(p_aggr); if (!new_skb) { ath6kl_err("no buffer available\n"); break; } memcpy(new_skb->data, framep, frame_8023_len); skb_put(new_skb, frame_8023_len); if (ath6kl_wmi_dot3_2_dix(new_skb)) { ath6kl_err("dot3_2_dix error\n"); dev_kfree_skb(new_skb); break; } skb_queue_tail(&rxtid->q, new_skb); /* Is this the last subframe within this aggregate ? */ if ((amsdu_len - frame_8023_len) == 0) break; /* Add the length of A-MSDU subframe padding bytes - * Round to nearest word. */ frame_8023_len = ALIGN(frame_8023_len, 4); framep += frame_8023_len; amsdu_len -= frame_8023_len; } dev_kfree_skb(skb); } static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid, u16 seq_no, u8 order) { struct sk_buff *skb; struct rxtid *rxtid; struct skb_hold_q *node; u16 idx, idx_end, seq_end; struct rxtid_stats *stats; rxtid = &agg_conn->rx_tid[tid]; stats = &agg_conn->stat[tid]; spin_lock_bh(&rxtid->lock); idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); /* * idx_end is typically the last possible frame in the window, * but changes to 'the' seq_no, when BAR comes. If seq_no * is non-zero, we will go up to that and stop. * Note: last seq no in current window will occupy the same * index position as index that is just previous to start. * An imp point : if win_sz is 7, for seq_no space of 4095, * then, there would be holes when sequence wrap around occurs. * Target should judiciously choose the win_sz, based on * this condition. For 4095, (TID_WINDOW_SZ = 2 x win_sz * 2, 4, 8, 16 win_sz works fine). * We must deque from "idx" to "idx_end", including both. */ seq_end = seq_no ? seq_no : rxtid->seq_next; idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz); do { node = &rxtid->hold_q[idx]; if ((order == 1) && (!node->skb)) break; if (node->skb) { if (node->is_amsdu) aggr_slice_amsdu(agg_conn->aggr_info, rxtid, node->skb); else skb_queue_tail(&rxtid->q, node->skb); node->skb = NULL; } else stats->num_hole++; rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next); idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); } while (idx != idx_end); spin_unlock_bh(&rxtid->lock); stats->num_delivered += skb_queue_len(&rxtid->q); while ((skb = skb_dequeue(&rxtid->q))) ath6kl_deliver_frames_to_nw_stack(agg_conn->dev, skb); } static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, u16 seq_no, bool is_amsdu, struct sk_buff *frame) { struct rxtid *rxtid; struct rxtid_stats *stats; struct sk_buff *skb; struct skb_hold_q *node; u16 idx, st, cur, end; bool is_queued = false; u16 extended_end; rxtid = &agg_conn->rx_tid[tid]; stats = &agg_conn->stat[tid]; stats->num_into_aggr++; if (!rxtid->aggr) { if (is_amsdu) { aggr_slice_amsdu(agg_conn->aggr_info, rxtid, frame); is_queued = true; stats->num_amsdu++; while ((skb = skb_dequeue(&rxtid->q))) ath6kl_deliver_frames_to_nw_stack(agg_conn->dev, skb); } return is_queued; } /* Check the incoming sequence no, if it's in the window */ st = rxtid->seq_next; cur = seq_no; end = (st + rxtid->hold_q_sz-1) & ATH6KL_MAX_SEQ_NO; if (((st < end) && (cur < st || cur > end)) || ((st > end) && (cur > end) && (cur < st))) { extended_end = (end + rxtid->hold_q_sz - 1) & ATH6KL_MAX_SEQ_NO; if (((end < extended_end) && (cur < end || cur > extended_end)) || ((end > extended_end) && (cur > extended_end) && (cur < end))) { aggr_deque_frms(agg_conn, tid, 0, 0); spin_lock_bh(&rxtid->lock); if (cur >= rxtid->hold_q_sz - 1) rxtid->seq_next = cur - (rxtid->hold_q_sz - 1); else rxtid->seq_next = ATH6KL_MAX_SEQ_NO - (rxtid->hold_q_sz - 2 - cur); spin_unlock_bh(&rxtid->lock); } else { /* * Dequeue only those frames that are outside the * new shifted window. */ if (cur >= rxtid->hold_q_sz - 1) st = cur - (rxtid->hold_q_sz - 1); else st = ATH6KL_MAX_SEQ_NO - (rxtid->hold_q_sz - 2 - cur); aggr_deque_frms(agg_conn, tid, st, 0); } stats->num_oow++; } idx = AGGR_WIN_IDX(seq_no, rxtid->hold_q_sz); node = &rxtid->hold_q[idx]; spin_lock_bh(&rxtid->lock); /* * Is the cur frame duplicate or something beyond our window(hold_q * -> which is 2x, already)? * * 1. Duplicate is easy - drop incoming frame. * 2. Not falling in current sliding window. * 2a. is the frame_seq_no preceding current tid_seq_no? * -> drop the frame. perhaps sender did not get our ACK. * this is taken care of above. * 2b. is the frame_seq_no beyond window(st, TID_WINDOW_SZ); * -> Taken care of it above, by moving window forward. */ dev_kfree_skb(node->skb); stats->num_dups++; node->skb = frame; is_queued = true; node->is_amsdu = is_amsdu; node->seq_no = seq_no; if (node->is_amsdu) stats->num_amsdu++; else stats->num_mpdu++; spin_unlock_bh(&rxtid->lock); aggr_deque_frms(agg_conn, tid, 0, 1); if (agg_conn->timer_scheduled) return is_queued; spin_lock_bh(&rxtid->lock); for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { if (rxtid->hold_q[idx].skb) { /* * There is a frame in the queue and no * timer so start a timer to ensure that * the frame doesn't remain stuck * forever. */ agg_conn->timer_scheduled = true; mod_timer(&agg_conn->timer, (jiffies + (HZ * AGGR_RX_TIMEOUT) / 1000)); rxtid->timer_mon = true; break; } } spin_unlock_bh(&rxtid->lock); return is_queued; } static void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif, struct ath6kl_sta *conn) { struct ath6kl *ar = vif->ar; bool is_apsdq_empty, is_apsdq_empty_at_start; u32 num_frames_to_deliver, flags; struct sk_buff *skb = NULL; /* * If the APSD q for this STA is not empty, dequeue and * send a pkt from the head of the q. Also update the * More data bit in the WMI_DATA_HDR if there are * more pkts for this STA in the APSD q. * If there are no more pkts for this STA, * update the APSD bitmap for this STA. */ num_frames_to_deliver = (conn->apsd_info >> ATH6KL_APSD_NUM_OF_AC) & ATH6KL_APSD_FRAME_MASK; /* * Number of frames to send in a service period is * indicated by the station * in the QOS_INFO of the association request * If it is zero, send all frames */ if (!num_frames_to_deliver) num_frames_to_deliver = ATH6KL_APSD_ALL_FRAME; spin_lock_bh(&conn->psq_lock); is_apsdq_empty = skb_queue_empty(&conn->apsdq); spin_unlock_bh(&conn->psq_lock); is_apsdq_empty_at_start = is_apsdq_empty; while ((!is_apsdq_empty) && (num_frames_to_deliver)) { spin_lock_bh(&conn->psq_lock); skb = skb_dequeue(&conn->apsdq); is_apsdq_empty = skb_queue_empty(&conn->apsdq); spin_unlock_bh(&conn->psq_lock); /* * Set the STA flag to Trigger delivery, * so that the frame will go out */ conn->sta_flags |= STA_PS_APSD_TRIGGER; num_frames_to_deliver--; /* Last frame in the service period, set EOSP or queue empty */ if ((is_apsdq_empty) || (!num_frames_to_deliver)) conn->sta_flags |= STA_PS_APSD_EOSP; ath6kl_data_tx(skb, vif->ndev); conn->sta_flags &= ~(STA_PS_APSD_TRIGGER); conn->sta_flags &= ~(STA_PS_APSD_EOSP); } if (is_apsdq_empty) { if (is_apsdq_empty_at_start) flags = WMI_AP_APSD_NO_DELIVERY_FRAMES; else flags = 0; ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi, vif->fw_vif_idx, conn->aid, 0, flags); } return; } void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) { struct ath6kl *ar = target->dev->ar; struct sk_buff *skb = packet->pkt_cntxt; struct wmi_rx_meta_v2 *meta; struct wmi_data_hdr *dhdr; int min_hdr_len; u8 meta_type, dot11_hdr = 0; u8 pad_before_data_start; int status = packet->status; enum htc_endpoint_id ept = packet->endpoint; bool is_amsdu, prev_ps, ps_state = false; bool trig_state = false; struct ath6kl_sta *conn = NULL; struct sk_buff *skb1 = NULL; struct ethhdr *datap = NULL; struct ath6kl_vif *vif; struct aggr_info_conn *aggr_conn; u16 seq_no, offset; u8 tid, if_idx; ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: ar=0x%p eid=%d, skb=0x%p, data=0x%p, len=0x%x status:%d", __func__, ar, ept, skb, packet->buf, packet->act_len, status); if (status || !(skb->data + HTC_HDR_LENGTH)) { dev_kfree_skb(skb); return; } skb_put(skb, packet->act_len + HTC_HDR_LENGTH); skb_pull(skb, HTC_HDR_LENGTH); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ", skb->data, skb->len); if (ept == ar->ctrl_ep) { if (test_bit(WMI_ENABLED, &ar->flag)) { ath6kl_check_wow_status(ar); ath6kl_wmi_control_rx(ar->wmi, skb); return; } if_idx = wmi_cmd_hdr_get_if_idx((struct wmi_cmd_hdr *) skb->data); } else { if_idx = wmi_data_hdr_get_if_idx((struct wmi_data_hdr *) skb->data); } vif = ath6kl_get_vif_by_index(ar, if_idx); if (!vif) { dev_kfree_skb(skb); return; } /* * Take lock to protect buffer counts and adaptive power throughput * state. */ spin_lock_bh(&vif->if_lock); vif->net_stats.rx_packets++; vif->net_stats.rx_bytes += packet->act_len; spin_unlock_bh(&vif->if_lock); skb->dev = vif->ndev; if (!test_bit(WMI_ENABLED, &ar->flag)) { if (EPPING_ALIGNMENT_PAD > 0) skb_pull(skb, EPPING_ALIGNMENT_PAD); ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); return; } ath6kl_check_wow_status(ar); min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) + sizeof(struct ath6kl_llc_snap_hdr); dhdr = (struct wmi_data_hdr *) skb->data; /* * In the case of AP mode we may receive NULL data frames * that do not have LLC hdr. They are 16 bytes in size. * Allow these frames in the AP mode. */ if (vif->nw_type != AP_NETWORK && ((packet->act_len < min_hdr_len) || (packet->act_len > WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH))) { ath6kl_info("frame len is too short or too long\n"); vif->net_stats.rx_errors++; vif->net_stats.rx_length_errors++; dev_kfree_skb(skb); return; } /* Get the Power save state of the STA */ if (vif->nw_type == AP_NETWORK) { meta_type = wmi_data_hdr_get_meta(dhdr); ps_state = !!((dhdr->info >> WMI_DATA_HDR_PS_SHIFT) & WMI_DATA_HDR_PS_MASK); offset = sizeof(struct wmi_data_hdr); trig_state = !!(le16_to_cpu(dhdr->info3) & WMI_DATA_HDR_TRIG); switch (meta_type) { case 0: break; case WMI_META_VERSION_1: offset += sizeof(struct wmi_rx_meta_v1); break; case WMI_META_VERSION_2: offset += sizeof(struct wmi_rx_meta_v2); break; default: break; } datap = (struct ethhdr *) (skb->data + offset); conn = ath6kl_find_sta(vif, datap->h_source); if (!conn) { dev_kfree_skb(skb); return; } /* * If there is a change in PS state of the STA, * take appropriate steps: * * 1. If Sleep-->Awake, flush the psq for the STA * Clear the PVB for the STA. * 2. If Awake-->Sleep, Starting queueing frames * the STA. */ prev_ps = !!(conn->sta_flags & STA_PS_SLEEP); if (ps_state) conn->sta_flags |= STA_PS_SLEEP; else conn->sta_flags &= ~STA_PS_SLEEP; /* Accept trigger only when the station is in sleep */ if ((conn->sta_flags & STA_PS_SLEEP) && trig_state) ath6kl_uapsd_trigger_frame_rx(vif, conn); if (prev_ps ^ !!(conn->sta_flags & STA_PS_SLEEP)) { if (!(conn->sta_flags & STA_PS_SLEEP)) { struct sk_buff *skbuff = NULL; bool is_apsdq_empty; struct ath6kl_mgmt_buff *mgmt; u8 idx; spin_lock_bh(&conn->psq_lock); while (conn->mgmt_psq_len > 0) { mgmt = list_first_entry( &conn->mgmt_psq, struct ath6kl_mgmt_buff, list); list_del(&mgmt->list); conn->mgmt_psq_len--; spin_unlock_bh(&conn->psq_lock); idx = vif->fw_vif_idx; ath6kl_wmi_send_mgmt_cmd(ar->wmi, idx, mgmt->id, mgmt->freq, mgmt->wait, mgmt->buf, mgmt->len, mgmt->no_cck); kfree(mgmt); spin_lock_bh(&conn->psq_lock); } conn->mgmt_psq_len = 0; while ((skbuff = skb_dequeue(&conn->psq))) { spin_unlock_bh(&conn->psq_lock); ath6kl_data_tx(skbuff, vif->ndev); spin_lock_bh(&conn->psq_lock); } is_apsdq_empty = skb_queue_empty(&conn->apsdq); while ((skbuff = skb_dequeue(&conn->apsdq))) { spin_unlock_bh(&conn->psq_lock); ath6kl_data_tx(skbuff, vif->ndev); spin_lock_bh(&conn->psq_lock); } spin_unlock_bh(&conn->psq_lock); if (!is_apsdq_empty) ath6kl_wmi_set_apsd_bfrd_traf( ar->wmi, vif->fw_vif_idx, conn->aid, 0, 0); /* Clear the PVB for this STA */ ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 0); } } /* drop NULL data frames here */ if ((packet->act_len < min_hdr_len) || (packet->act_len > WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH)) { dev_kfree_skb(skb); return; } } is_amsdu = wmi_data_hdr_is_amsdu(dhdr) ? true : false; tid = wmi_data_hdr_get_up(dhdr); seq_no = wmi_data_hdr_get_seqno(dhdr); meta_type = wmi_data_hdr_get_meta(dhdr); dot11_hdr = wmi_data_hdr_get_dot11(dhdr); pad_before_data_start = (le16_to_cpu(dhdr->info3) >> WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT) & WMI_DATA_HDR_PAD_BEFORE_DATA_MASK; skb_pull(skb, sizeof(struct wmi_data_hdr)); switch (meta_type) { case WMI_META_VERSION_1: skb_pull(skb, sizeof(struct wmi_rx_meta_v1)); break; case WMI_META_VERSION_2: meta = (struct wmi_rx_meta_v2 *) skb->data; if (meta->csum_flags & 0x1) { skb->ip_summed = CHECKSUM_COMPLETE; skb->csum = (__force __wsum) meta->csum; } skb_pull(skb, sizeof(struct wmi_rx_meta_v2)); break; default: break; } skb_pull(skb, pad_before_data_start); if (dot11_hdr) status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb); else if (!is_amsdu) status = ath6kl_wmi_dot3_2_dix(skb); if (status) { /* * Drop frames that could not be processed (lack of * memory, etc.) */ dev_kfree_skb(skb); return; } if (!(vif->ndev->flags & IFF_UP)) { dev_kfree_skb(skb); return; } if (vif->nw_type == AP_NETWORK) { datap = (struct ethhdr *) skb->data; if (is_multicast_ether_addr(datap->h_dest)) /* * Bcast/Mcast frames should be sent to the * OS stack as well as on the air. */ skb1 = skb_copy(skb, GFP_ATOMIC); else { /* * Search for a connected STA with dstMac * as the Mac address. If found send the * frame to it on the air else send the * frame up the stack. */ conn = ath6kl_find_sta(vif, datap->h_dest); if (conn && ar->intra_bss) { skb1 = skb; skb = NULL; } else if (conn && !ar->intra_bss) { dev_kfree_skb(skb); skb = NULL; } } if (skb1) ath6kl_data_tx(skb1, vif->ndev); if (skb == NULL) { /* nothing to deliver up the stack */ return; } } datap = (struct ethhdr *) skb->data; if (is_unicast_ether_addr(datap->h_dest)) { if (vif->nw_type == AP_NETWORK) { conn = ath6kl_find_sta(vif, datap->h_source); if (!conn) return; aggr_conn = conn->aggr_conn; } else aggr_conn = vif->aggr_cntxt->aggr_conn; if (aggr_process_recv_frm(aggr_conn, tid, seq_no, is_amsdu, skb)) { /* aggregation code will handle the skb */ return; } } else if (!is_broadcast_ether_addr(datap->h_dest)) vif->net_stats.multicast++; ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); } static void aggr_timeout(unsigned long arg) { u8 i, j; struct aggr_info_conn *aggr_conn = (struct aggr_info_conn *) arg; struct rxtid *rxtid; struct rxtid_stats *stats; for (i = 0; i < NUM_OF_TIDS; i++) { rxtid = &aggr_conn->rx_tid[i]; stats = &aggr_conn->stat[i]; if (!rxtid->aggr || !rxtid->timer_mon) continue; stats->num_timeouts++; ath6kl_dbg(ATH6KL_DBG_AGGR, "aggr timeout (st %d end %d)\n", rxtid->seq_next, ((rxtid->seq_next + rxtid->hold_q_sz-1) & ATH6KL_MAX_SEQ_NO)); aggr_deque_frms(aggr_conn, i, 0, 0); } aggr_conn->timer_scheduled = false; for (i = 0; i < NUM_OF_TIDS; i++) { rxtid = &aggr_conn->rx_tid[i]; if (rxtid->aggr && rxtid->hold_q) { spin_lock_bh(&rxtid->lock); for (j = 0; j < rxtid->hold_q_sz; j++) { if (rxtid->hold_q[j].skb) { aggr_conn->timer_scheduled = true; rxtid->timer_mon = true; break; } } spin_unlock_bh(&rxtid->lock); if (j >= rxtid->hold_q_sz) rxtid->timer_mon = false; } } if (aggr_conn->timer_scheduled) mod_timer(&aggr_conn->timer, jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT)); } static void aggr_delete_tid_state(struct aggr_info_conn *aggr_conn, u8 tid) { struct rxtid *rxtid; struct rxtid_stats *stats; if (!aggr_conn || tid >= NUM_OF_TIDS) return; rxtid = &aggr_conn->rx_tid[tid]; stats = &aggr_conn->stat[tid]; if (rxtid->aggr) aggr_deque_frms(aggr_conn, tid, 0, 0); rxtid->aggr = false; rxtid->timer_mon = false; rxtid->win_sz = 0; rxtid->seq_next = 0; rxtid->hold_q_sz = 0; kfree(rxtid->hold_q); rxtid->hold_q = NULL; memset(stats, 0, sizeof(struct rxtid_stats)); } void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no, u8 win_sz) { struct ath6kl_sta *sta; struct aggr_info_conn *aggr_conn = NULL; struct rxtid *rxtid; struct rxtid_stats *stats; u16 hold_q_size; u8 tid, aid; if (vif->nw_type == AP_NETWORK) { aid = ath6kl_get_aid(tid_mux); sta = ath6kl_find_sta_by_aid(vif->ar, aid); if (sta) aggr_conn = sta->aggr_conn; } else aggr_conn = vif->aggr_cntxt->aggr_conn; if (!aggr_conn) return; tid = ath6kl_get_tid(tid_mux); if (tid >= NUM_OF_TIDS) return; rxtid = &aggr_conn->rx_tid[tid]; stats = &aggr_conn->stat[tid]; if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", __func__, win_sz, tid); if (rxtid->aggr) aggr_delete_tid_state(aggr_conn, tid); rxtid->seq_next = seq_no; hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q); rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL); if (!rxtid->hold_q) return; rxtid->win_sz = win_sz; rxtid->hold_q_sz = TID_WINDOW_SZ(win_sz); if (!skb_queue_empty(&rxtid->q)) return; rxtid->aggr = true; } void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info, struct aggr_info_conn *aggr_conn) { struct rxtid *rxtid; u8 i; aggr_conn->aggr_sz = AGGR_SZ_DEFAULT; aggr_conn->dev = vif->ndev; init_timer(&aggr_conn->timer); aggr_conn->timer.function = aggr_timeout; aggr_conn->timer.data = (unsigned long) aggr_conn; aggr_conn->aggr_info = aggr_info; aggr_conn->timer_scheduled = false; for (i = 0; i < NUM_OF_TIDS; i++) { rxtid = &aggr_conn->rx_tid[i]; rxtid->aggr = false; rxtid->timer_mon = false; skb_queue_head_init(&rxtid->q); spin_lock_init(&rxtid->lock); } } struct aggr_info *aggr_init(struct ath6kl_vif *vif) { struct aggr_info *p_aggr = NULL; p_aggr = kzalloc(sizeof(struct aggr_info), GFP_KERNEL); if (!p_aggr) { ath6kl_err("failed to alloc memory for aggr_node\n"); return NULL; } p_aggr->aggr_conn = kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL); if (!p_aggr->aggr_conn) { ath6kl_err("failed to alloc memory for connection specific aggr info\n"); kfree(p_aggr); return NULL; } aggr_conn_init(vif, p_aggr, p_aggr->aggr_conn); skb_queue_head_init(&p_aggr->rx_amsdu_freeq); ath6kl_alloc_netbufs(&p_aggr->rx_amsdu_freeq, AGGR_NUM_OF_FREE_NETBUFS); return p_aggr; } void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid_mux) { struct ath6kl_sta *sta; struct rxtid *rxtid; struct aggr_info_conn *aggr_conn = NULL; u8 tid, aid; if (vif->nw_type == AP_NETWORK) { aid = ath6kl_get_aid(tid_mux); sta = ath6kl_find_sta_by_aid(vif->ar, aid); if (sta) aggr_conn = sta->aggr_conn; } else aggr_conn = vif->aggr_cntxt->aggr_conn; if (!aggr_conn) return; tid = ath6kl_get_tid(tid_mux); if (tid >= NUM_OF_TIDS) return; rxtid = &aggr_conn->rx_tid[tid]; if (rxtid->aggr) aggr_delete_tid_state(aggr_conn, tid); } void aggr_reset_state(struct aggr_info_conn *aggr_conn) { u8 tid; if (!aggr_conn) return; if (aggr_conn->timer_scheduled) { del_timer(&aggr_conn->timer); aggr_conn->timer_scheduled = false; } for (tid = 0; tid < NUM_OF_TIDS; tid++) aggr_delete_tid_state(aggr_conn, tid); } /* clean up our amsdu buffer list */ void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar) { struct htc_packet *packet, *tmp_pkt; spin_lock_bh(&ar->lock); if (list_empty(&ar->amsdu_rx_buffer_queue)) { spin_unlock_bh(&ar->lock); return; } list_for_each_entry_safe(packet, tmp_pkt, &ar->amsdu_rx_buffer_queue, list) { list_del(&packet->list); spin_unlock_bh(&ar->lock); dev_kfree_skb(packet->pkt_cntxt); spin_lock_bh(&ar->lock); } spin_unlock_bh(&ar->lock); } void aggr_module_destroy(struct aggr_info *aggr_info) { if (!aggr_info) return; aggr_reset_state(aggr_info->aggr_conn); skb_queue_purge(&aggr_info->rx_amsdu_freeq); kfree(aggr_info->aggr_conn); kfree(aggr_info); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/main.c0000644000175000017500000010500512026211315023517 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "core.h" #include "hif-ops.h" #include "cfg80211.h" #include "target.h" #include "debug.h" struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr) { struct ath6kl *ar = vif->ar; struct ath6kl_sta *conn = NULL; u8 i, max_conn; max_conn = (vif->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0; for (i = 0; i < max_conn; i++) { if (memcmp(node_addr, ar->sta_list[i].mac, ETH_ALEN) == 0) { conn = &ar->sta_list[i]; break; } } return conn; } struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid) { struct ath6kl_sta *conn = NULL; u8 ctr; for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { if (ar->sta_list[ctr].aid == aid) { conn = &ar->sta_list[ctr]; break; } } return conn; } static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid, u8 *wpaie, size_t ielen, u8 keymgmt, u8 ucipher, u8 auth, u8 apsd_info) { struct ath6kl *ar = vif->ar; struct ath6kl_sta *sta; u8 free_slot; free_slot = aid - 1; sta = &ar->sta_list[free_slot]; memcpy(sta->mac, mac, ETH_ALEN); if (ielen <= ATH6KL_MAX_IE) memcpy(sta->wpa_ie, wpaie, ielen); sta->aid = aid; sta->keymgmt = keymgmt; sta->ucipher = ucipher; sta->auth = auth; sta->apsd_info = apsd_info; ar->sta_list_index = ar->sta_list_index | (1 << free_slot); ar->ap_stats.sta[free_slot].aid = cpu_to_le32(aid); aggr_conn_init(vif, vif->aggr_cntxt, sta->aggr_conn); } static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i) { struct ath6kl_sta *sta = &ar->sta_list[i]; struct ath6kl_mgmt_buff *entry, *tmp; /* empty the queued pkts in the PS queue if any */ spin_lock_bh(&sta->psq_lock); skb_queue_purge(&sta->psq); skb_queue_purge(&sta->apsdq); if (sta->mgmt_psq_len != 0) { list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) { kfree(entry); } INIT_LIST_HEAD(&sta->mgmt_psq); sta->mgmt_psq_len = 0; } spin_unlock_bh(&sta->psq_lock); memset(&ar->ap_stats.sta[sta->aid - 1], 0, sizeof(struct wmi_per_sta_stat)); memset(sta->mac, 0, ETH_ALEN); memset(sta->wpa_ie, 0, ATH6KL_MAX_IE); sta->aid = 0; sta->sta_flags = 0; ar->sta_list_index = ar->sta_list_index & ~(1 << i); aggr_reset_state(sta->aggr_conn); } static u8 ath6kl_remove_sta(struct ath6kl *ar, u8 *mac, u16 reason) { u8 i, removed = 0; if (is_zero_ether_addr(mac)) return removed; if (is_broadcast_ether_addr(mac)) { ath6kl_dbg(ATH6KL_DBG_TRC, "deleting all station\n"); for (i = 0; i < AP_MAX_NUM_STA; i++) { if (!is_zero_ether_addr(ar->sta_list[i].mac)) { ath6kl_sta_cleanup(ar, i); removed = 1; } } } else { for (i = 0; i < AP_MAX_NUM_STA; i++) { if (memcmp(ar->sta_list[i].mac, mac, ETH_ALEN) == 0) { ath6kl_dbg(ATH6KL_DBG_TRC, "deleting station %pM aid=%d reason=%d\n", mac, ar->sta_list[i].aid, reason); ath6kl_sta_cleanup(ar, i); removed = 1; break; } } } return removed; } enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac) { struct ath6kl *ar = devt; return ar->ac2ep_map[ac]; } struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar) { struct ath6kl_cookie *cookie; cookie = ar->cookie_list; if (cookie != NULL) { ar->cookie_list = cookie->arc_list_next; ar->cookie_count--; } return cookie; } void ath6kl_cookie_init(struct ath6kl *ar) { u32 i; ar->cookie_list = NULL; ar->cookie_count = 0; memset(ar->cookie_mem, 0, sizeof(ar->cookie_mem)); for (i = 0; i < MAX_COOKIE_NUM; i++) ath6kl_free_cookie(ar, &ar->cookie_mem[i]); } void ath6kl_cookie_cleanup(struct ath6kl *ar) { ar->cookie_list = NULL; ar->cookie_count = 0; } void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie) { /* Insert first */ if (!ar || !cookie) return; cookie->arc_list_next = ar->cookie_list; ar->cookie_list = cookie; ar->cookie_count++; } /* * Read from the hardware through its diagnostic window. No cooperation * from the firmware is required for this. */ int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value) { int ret; ret = ath6kl_hif_diag_read32(ar, address, value); if (ret) { ath6kl_warn("failed to read32 through diagnose window: %d\n", ret); return ret; } return 0; } /* * Write to the ATH6KL through its diagnostic window. No cooperation from * the Target is required for this. */ int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value) { int ret; ret = ath6kl_hif_diag_write32(ar, address, value); if (ret) { ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n", address, value); return ret; } return 0; } int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length) { u32 count, *buf = data; int ret; if (WARN_ON(length % 4)) return -EINVAL; for (count = 0; count < length / 4; count++, address += 4) { ret = ath6kl_diag_read32(ar, address, &buf[count]); if (ret) return ret; } return 0; } int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length) { u32 count; __le32 *buf = data; int ret; if (WARN_ON(length % 4)) return -EINVAL; for (count = 0; count < length / 4; count++, address += 4) { ret = ath6kl_diag_write32(ar, address, buf[count]); if (ret) return ret; } return 0; } int ath6kl_read_fwlogs(struct ath6kl *ar) { struct ath6kl_dbglog_hdr debug_hdr; struct ath6kl_dbglog_buf debug_buf; u32 address, length, dropped, firstbuf, debug_hdr_addr; int ret, loop; u8 *buf; buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; address = TARG_VTOP(ar->target_type, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_dbglog_hdr))); ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr); if (ret) goto out; /* Get the contents of the ring buffer */ if (debug_hdr_addr == 0) { ath6kl_warn("Invalid address for debug_hdr_addr\n"); ret = -EINVAL; goto out; } address = TARG_VTOP(ar->target_type, debug_hdr_addr); ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr)); address = TARG_VTOP(ar->target_type, le32_to_cpu(debug_hdr.dbuf_addr)); firstbuf = address; dropped = le32_to_cpu(debug_hdr.dropped); ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); loop = 100; do { address = TARG_VTOP(ar->target_type, le32_to_cpu(debug_buf.buffer_addr)); length = le32_to_cpu(debug_buf.length); if (length != 0 && (le32_to_cpu(debug_buf.length) <= le32_to_cpu(debug_buf.bufsize))) { length = ALIGN(length, 4); ret = ath6kl_diag_read(ar, address, buf, length); if (ret) goto out; ath6kl_debug_fwlog_event(ar, buf, length); } address = TARG_VTOP(ar->target_type, le32_to_cpu(debug_buf.next)); ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); if (ret) goto out; loop--; if (WARN_ON(loop == 0)) { ret = -ETIMEDOUT; goto out; } } while (address != firstbuf); out: kfree(buf); return ret; } /* FIXME: move to a better place, target.h? */ #define AR6003_RESET_CONTROL_ADDRESS 0x00004000 #define AR6004_RESET_CONTROL_ADDRESS 0x00004000 void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, bool wait_fot_compltn, bool cold_reset) { int status = 0; u32 address; __le32 data; if (target_type != TARGET_TYPE_AR6003 && target_type != TARGET_TYPE_AR6004) return; data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) : cpu_to_le32(RESET_CONTROL_MBOX_RST); switch (target_type) { case TARGET_TYPE_AR6003: address = AR6003_RESET_CONTROL_ADDRESS; break; case TARGET_TYPE_AR6004: address = AR6004_RESET_CONTROL_ADDRESS; break; } status = ath6kl_diag_write32(ar, address, data); if (status) ath6kl_err("failed to reset target\n"); } static void ath6kl_install_static_wep_keys(struct ath6kl_vif *vif) { u8 index; u8 keyusage; for (index = 0; index <= WMI_MAX_KEY_INDEX; index++) { if (vif->wep_key_list[index].key_len) { keyusage = GROUP_USAGE; if (index == vif->def_txkey_index) keyusage |= TX_USAGE; ath6kl_wmi_addkey_cmd(vif->ar->wmi, vif->fw_vif_idx, index, WEP_CRYPT, keyusage, vif->wep_key_list[index].key_len, NULL, 0, vif->wep_key_list[index].key, KEY_OP_INIT_VAL, NULL, NO_SYNC_WMIFLAG); } } } void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) { struct ath6kl *ar = vif->ar; struct ath6kl_req_key *ik; int res; u8 key_rsc[ATH6KL_KEY_SEQ_LEN]; ik = &ar->ap_mode_bkey; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel); switch (vif->auth_mode) { case NONE_AUTH: if (vif->prwise_crypto == WEP_CRYPT) ath6kl_install_static_wep_keys(vif); if (!ik->valid || ik->key_type != WAPI_CRYPT) break; /* for WAPI, we need to set the delayed group key, continue: */ case WPA_PSK_AUTH: case WPA2_PSK_AUTH: case (WPA_PSK_AUTH | WPA2_PSK_AUTH): if (!ik->valid) break; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for the initial group key for AP mode\n"); memset(key_rsc, 0, sizeof(key_rsc)); res = ath6kl_wmi_addkey_cmd( ar->wmi, vif->fw_vif_idx, ik->key_index, ik->key_type, GROUP_USAGE, ik->key_len, key_rsc, ATH6KL_KEY_SEQ_LEN, ik->key, KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); if (res) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey failed: %d\n", res); } break; } if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) { ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); /* we actually don't know the phymode, default to HT20 */ ath6kl_cfg80211_ch_switch_notify(vif, channel, WMI_11G_HT20); } ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &vif->flags); netif_carrier_on(vif->ndev); } void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, u8 keymgmt, u8 ucipher, u8 auth, u8 assoc_req_len, u8 *assoc_info, u8 apsd_info) { u8 *ies = NULL, *wpa_ie = NULL, *pos; size_t ies_len = 0; struct station_info sinfo; ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid); if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) assoc_info; if (ieee80211_is_assoc_req(mgmt->frame_control) && assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) + sizeof(mgmt->u.assoc_req)) { ies = mgmt->u.assoc_req.variable; ies_len = assoc_info + assoc_req_len - ies; } else if (ieee80211_is_reassoc_req(mgmt->frame_control) && assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) + sizeof(mgmt->u.reassoc_req)) { ies = mgmt->u.reassoc_req.variable; ies_len = assoc_info + assoc_req_len - ies; } } pos = ies; while (pos && pos + 1 < ies + ies_len) { if (pos + 2 + pos[1] > ies + ies_len) break; if (pos[0] == WLAN_EID_RSN) wpa_ie = pos; /* RSN IE */ else if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) { if (pos[5] == 0x01) wpa_ie = pos; /* WPA IE */ else if (pos[5] == 0x04) { wpa_ie = pos; /* WPS IE */ break; /* overrides WPA/RSN IE */ } } else if (pos[0] == 0x44 && wpa_ie == NULL) { /* * Note: WAPI Parameter Set IE re-uses Element ID that * was officially allocated for BSS AC Access Delay. As * such, we need to be a bit more careful on when * parsing the frame. However, BSS AC Access Delay * element is not supposed to be included in * (Re)Association Request frames, so this should not * cause problems. */ wpa_ie = pos; /* WAPI IE */ break; } pos += 2 + pos[1]; } ath6kl_add_new_sta(vif, mac_addr, aid, wpa_ie, wpa_ie ? 2 + wpa_ie[1] : 0, keymgmt, ucipher, auth, apsd_info); /* send event to application */ memset(&sinfo, 0, sizeof(sinfo)); /* TODO: sinfo.generation */ sinfo.assoc_req_ies = ies; sinfo.assoc_req_ies_len = ies_len; sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL); netif_wake_queue(vif->ndev); } void disconnect_timer_handler(unsigned long ptr) { struct net_device *dev = (struct net_device *)ptr; struct ath6kl_vif *vif = netdev_priv(dev); ath6kl_init_profile_info(vif); ath6kl_disconnect(vif); } void ath6kl_disconnect(struct ath6kl_vif *vif) { if (test_bit(CONNECTED, &vif->flags) || test_bit(CONNECT_PEND, &vif->flags)) { ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx); /* * Disconnect command is issued, clear the connect pending * flag. The connected flag will be cleared in * disconnect event notification. */ clear_bit(CONNECT_PEND, &vif->flags); } } /* WMI Event handlers */ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver, enum wmi_phy_cap cap) { struct ath6kl *ar = devt; memcpy(ar->mac_addr, datap, ETH_ALEN); ath6kl_dbg(ATH6KL_DBG_BOOT, "ready event mac addr %pM sw_ver 0x%x abi_ver 0x%x cap 0x%x\n", ar->mac_addr, sw_ver, abi_ver, cap); ar->version.wlan_ver = sw_ver; ar->version.abi_ver = abi_ver; ar->hw.cap = cap; if (strlen(ar->wiphy->fw_version) == 0) { snprintf(ar->wiphy->fw_version, sizeof(ar->wiphy->fw_version), "%u.%u.%u.%u", (ar->version.wlan_ver & 0xf0000000) >> 28, (ar->version.wlan_ver & 0x0f000000) >> 24, (ar->version.wlan_ver & 0x00ff0000) >> 16, (ar->version.wlan_ver & 0x0000ffff)); } /* indicate to the waiting thread that the ready event was received */ set_bit(WMI_READY, &ar->flag); wake_up(&ar->event_wq); } void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) { struct ath6kl *ar = vif->ar; bool aborted = false; if (status != WMI_SCAN_STATUS_SUCCESS) aborted = true; ath6kl_cfg80211_scan_complete_event(vif, aborted); if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); } ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); } static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) { struct ath6kl *ar = vif->ar; vif->profile.ch = cpu_to_le16(channel); switch (vif->nw_type) { case AP_NETWORK: return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &vif->profile); default: ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type); return -ENOTSUPP; } } static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) { struct ath6kl_vif *vif; int res = 0; if (!ar->want_ch_switch) return; spin_lock_bh(&ar->list_lock); list_for_each_entry(vif, &ar->vif_list, list) { if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) res = ath6kl_commit_ch_switch(vif, channel); if (res) ath6kl_err("channel switch failed nw_type %d res %d\n", vif->nw_type, res); } spin_unlock_bh(&ar->list_lock); } void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_int, u16 beacon_int, enum network_type net_type, u8 beacon_ie_len, u8 assoc_req_len, u8 assoc_resp_len, u8 *assoc_info) { struct ath6kl *ar = vif->ar; ath6kl_cfg80211_connect_event(vif, channel, bssid, listen_int, beacon_int, net_type, beacon_ie_len, assoc_req_len, assoc_resp_len, assoc_info); memcpy(vif->bssid, bssid, sizeof(vif->bssid)); vif->bss_ch = channel; if ((vif->nw_type == INFRA_NETWORK)) { ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); ath6kl_check_ch_switch(ar, channel); } netif_wake_queue(vif->ndev); /* Update connect & link status atomically */ spin_lock_bh(&vif->if_lock); set_bit(CONNECTED, &vif->flags); clear_bit(CONNECT_PEND, &vif->flags); netif_carrier_on(vif->ndev); spin_unlock_bh(&vif->if_lock); aggr_reset_state(vif->aggr_cntxt->aggr_conn); vif->reconnect_flag = 0; if ((vif->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) { memset(ar->node_map, 0, sizeof(ar->node_map)); ar->node_num = 0; ar->next_ep_id = ENDPOINT_2; } if (!ar->usr_bss_filter) { set_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, CURRENT_BSS_FILTER, 0); } } void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast) { struct ath6kl_sta *sta; struct ath6kl *ar = vif->ar; u8 tsc[6]; /* * For AP case, keyid will have aid of STA which sent pkt with * MIC error. Use this aid to get MAC & send it to hostapd. */ if (vif->nw_type == AP_NETWORK) { sta = ath6kl_find_sta_by_aid(ar, (keyid >> 2)); if (!sta) return; ath6kl_dbg(ATH6KL_DBG_TRC, "ap tkip mic error received from aid=%d\n", keyid); memset(tsc, 0, sizeof(tsc)); /* FIX: get correct TSC */ cfg80211_michael_mic_failure(vif->ndev, sta->mac, NL80211_KEYTYPE_PAIRWISE, keyid, tsc, GFP_KERNEL); } else ath6kl_cfg80211_tkip_micerr_event(vif, keyid, ismcast); } static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) { struct wmi_target_stats *tgt_stats = (struct wmi_target_stats *) ptr; struct ath6kl *ar = vif->ar; struct target_stats *stats = &vif->target_stats; struct tkip_ccmp_stats *ccmp_stats; u8 ac; if (len < sizeof(*tgt_stats)) return; ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n"); stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt); stats->tx_byte += le32_to_cpu(tgt_stats->stats.tx.byte); stats->tx_ucast_pkt += le32_to_cpu(tgt_stats->stats.tx.ucast_pkt); stats->tx_ucast_byte += le32_to_cpu(tgt_stats->stats.tx.ucast_byte); stats->tx_mcast_pkt += le32_to_cpu(tgt_stats->stats.tx.mcast_pkt); stats->tx_mcast_byte += le32_to_cpu(tgt_stats->stats.tx.mcast_byte); stats->tx_bcast_pkt += le32_to_cpu(tgt_stats->stats.tx.bcast_pkt); stats->tx_bcast_byte += le32_to_cpu(tgt_stats->stats.tx.bcast_byte); stats->tx_rts_success_cnt += le32_to_cpu(tgt_stats->stats.tx.rts_success_cnt); for (ac = 0; ac < WMM_NUM_AC; ac++) stats->tx_pkt_per_ac[ac] += le32_to_cpu(tgt_stats->stats.tx.pkt_per_ac[ac]); stats->tx_err += le32_to_cpu(tgt_stats->stats.tx.err); stats->tx_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.fail_cnt); stats->tx_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.retry_cnt); stats->tx_mult_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt); stats->tx_rts_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt); stats->tx_ucast_rate = ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate)); stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt); stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte); stats->rx_ucast_pkt += le32_to_cpu(tgt_stats->stats.rx.ucast_pkt); stats->rx_ucast_byte += le32_to_cpu(tgt_stats->stats.rx.ucast_byte); stats->rx_mcast_pkt += le32_to_cpu(tgt_stats->stats.rx.mcast_pkt); stats->rx_mcast_byte += le32_to_cpu(tgt_stats->stats.rx.mcast_byte); stats->rx_bcast_pkt += le32_to_cpu(tgt_stats->stats.rx.bcast_pkt); stats->rx_bcast_byte += le32_to_cpu(tgt_stats->stats.rx.bcast_byte); stats->rx_frgment_pkt += le32_to_cpu(tgt_stats->stats.rx.frgment_pkt); stats->rx_err += le32_to_cpu(tgt_stats->stats.rx.err); stats->rx_crc_err += le32_to_cpu(tgt_stats->stats.rx.crc_err); stats->rx_key_cache_miss += le32_to_cpu(tgt_stats->stats.rx.key_cache_miss); stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err); stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame); stats->rx_ucast_rate = ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate)); ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats; stats->tkip_local_mic_fail += le32_to_cpu(ccmp_stats->tkip_local_mic_fail); stats->tkip_cnter_measures_invoked += le32_to_cpu(ccmp_stats->tkip_cnter_measures_invoked); stats->tkip_fmt_err += le32_to_cpu(ccmp_stats->tkip_fmt_err); stats->ccmp_fmt_err += le32_to_cpu(ccmp_stats->ccmp_fmt_err); stats->ccmp_replays += le32_to_cpu(ccmp_stats->ccmp_replays); stats->pwr_save_fail_cnt += le32_to_cpu(tgt_stats->pm_stats.pwr_save_failure_cnt); stats->noise_floor_calib = a_sle32_to_cpu(tgt_stats->noise_floor_calib); stats->cs_bmiss_cnt += le32_to_cpu(tgt_stats->cserv_stats.cs_bmiss_cnt); stats->cs_low_rssi_cnt += le32_to_cpu(tgt_stats->cserv_stats.cs_low_rssi_cnt); stats->cs_connect_cnt += le16_to_cpu(tgt_stats->cserv_stats.cs_connect_cnt); stats->cs_discon_cnt += le16_to_cpu(tgt_stats->cserv_stats.cs_discon_cnt); stats->cs_ave_beacon_rssi = a_sle16_to_cpu(tgt_stats->cserv_stats.cs_ave_beacon_rssi); stats->cs_last_roam_msec = tgt_stats->cserv_stats.cs_last_roam_msec; stats->cs_snr = tgt_stats->cserv_stats.cs_snr; stats->cs_rssi = a_sle16_to_cpu(tgt_stats->cserv_stats.cs_rssi); stats->lq_val = le32_to_cpu(tgt_stats->lq_val); stats->wow_pkt_dropped += le32_to_cpu(tgt_stats->wow_stats.wow_pkt_dropped); stats->wow_host_pkt_wakeups += tgt_stats->wow_stats.wow_host_pkt_wakeups; stats->wow_host_evt_wakeups += tgt_stats->wow_stats.wow_host_evt_wakeups; stats->wow_evt_discarded += le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded); stats->arp_received = le32_to_cpu(tgt_stats->arp_stats.arp_received); stats->arp_replied = le32_to_cpu(tgt_stats->arp_stats.arp_replied); stats->arp_matched = le32_to_cpu(tgt_stats->arp_stats.arp_matched); if (test_bit(STATS_UPDATE_PEND, &vif->flags)) { clear_bit(STATS_UPDATE_PEND, &vif->flags); wake_up(&ar->event_wq); } } static void ath6kl_add_le32(__le32 *var, __le32 val) { *var = cpu_to_le32(le32_to_cpu(*var) + le32_to_cpu(val)); } void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len) { struct wmi_ap_mode_stat *p = (struct wmi_ap_mode_stat *) ptr; struct ath6kl *ar = vif->ar; struct wmi_ap_mode_stat *ap = &ar->ap_stats; struct wmi_per_sta_stat *st_ap, *st_p; u8 ac; if (vif->nw_type == AP_NETWORK) { if (len < sizeof(*p)) return; for (ac = 0; ac < AP_MAX_NUM_STA; ac++) { st_ap = &ap->sta[ac]; st_p = &p->sta[ac]; ath6kl_add_le32(&st_ap->tx_bytes, st_p->tx_bytes); ath6kl_add_le32(&st_ap->tx_pkts, st_p->tx_pkts); ath6kl_add_le32(&st_ap->tx_error, st_p->tx_error); ath6kl_add_le32(&st_ap->tx_discard, st_p->tx_discard); ath6kl_add_le32(&st_ap->rx_bytes, st_p->rx_bytes); ath6kl_add_le32(&st_ap->rx_pkts, st_p->rx_pkts); ath6kl_add_le32(&st_ap->rx_error, st_p->rx_error); ath6kl_add_le32(&st_ap->rx_discard, st_p->rx_discard); } } else { ath6kl_update_target_stats(vif, ptr, len); } } void ath6kl_wakeup_event(void *dev) { struct ath6kl *ar = (struct ath6kl *) dev; wake_up(&ar->event_wq); } void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr) { struct ath6kl *ar = (struct ath6kl *) devt; ar->tx_pwr = tx_pwr; wake_up(&ar->event_wq); } void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid) { struct ath6kl_sta *conn; struct sk_buff *skb; bool psq_empty = false; struct ath6kl *ar = vif->ar; struct ath6kl_mgmt_buff *mgmt_buf; conn = ath6kl_find_sta_by_aid(ar, aid); if (!conn) return; /* * Send out a packet queued on ps queue. When the ps queue * becomes empty update the PVB for this station. */ spin_lock_bh(&conn->psq_lock); psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0); spin_unlock_bh(&conn->psq_lock); if (psq_empty) /* TODO: Send out a NULL data frame */ return; spin_lock_bh(&conn->psq_lock); if (conn->mgmt_psq_len > 0) { mgmt_buf = list_first_entry(&conn->mgmt_psq, struct ath6kl_mgmt_buff, list); list_del(&mgmt_buf->list); conn->mgmt_psq_len--; spin_unlock_bh(&conn->psq_lock); conn->sta_flags |= STA_PS_POLLED; ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, mgmt_buf->id, mgmt_buf->freq, mgmt_buf->wait, mgmt_buf->buf, mgmt_buf->len, mgmt_buf->no_cck); conn->sta_flags &= ~STA_PS_POLLED; kfree(mgmt_buf); } else { skb = skb_dequeue(&conn->psq); spin_unlock_bh(&conn->psq_lock); conn->sta_flags |= STA_PS_POLLED; ath6kl_data_tx(skb, vif->ndev); conn->sta_flags &= ~STA_PS_POLLED; } spin_lock_bh(&conn->psq_lock); psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0); spin_unlock_bh(&conn->psq_lock); if (psq_empty) ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 0); } void ath6kl_dtimexpiry_event(struct ath6kl_vif *vif) { bool mcastq_empty = false; struct sk_buff *skb; struct ath6kl *ar = vif->ar; /* * If there are no associated STAs, ignore the DTIM expiry event. * There can be potential race conditions where the last associated * STA may disconnect & before the host could clear the 'Indicate * DTIM' request to the firmware, the firmware would have just * indicated a DTIM expiry event. The race is between 'clear DTIM * expiry cmd' going from the host to the firmware & the DTIM * expiry event happening from the firmware to the host. */ if (!ar->sta_list_index) return; spin_lock_bh(&ar->mcastpsq_lock); mcastq_empty = skb_queue_empty(&ar->mcastpsq); spin_unlock_bh(&ar->mcastpsq_lock); if (mcastq_empty) return; /* set the STA flag to dtim_expired for the frame to go out */ set_bit(DTIM_EXPIRED, &vif->flags); spin_lock_bh(&ar->mcastpsq_lock); while ((skb = skb_dequeue(&ar->mcastpsq)) != NULL) { spin_unlock_bh(&ar->mcastpsq_lock); ath6kl_data_tx(skb, vif->ndev); spin_lock_bh(&ar->mcastpsq_lock); } spin_unlock_bh(&ar->mcastpsq_lock); clear_bit(DTIM_EXPIRED, &vif->flags); /* clear the LSB of the BitMapCtl field of the TIM IE */ ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, MCAST_AID, 0); } void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 prot_reason_status) { struct ath6kl *ar = vif->ar; if (vif->nw_type == AP_NETWORK) { /* disconnect due to other STA vif switching channels */ if (reason == BSS_DISCONNECTED && prot_reason_status == WMI_AP_REASON_STA_ROAM) ar->want_ch_switch |= 1 << vif->fw_vif_idx; if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; /* if no more associated STAs, empty the mcast PS q */ if (ar->sta_list_index == 0) { spin_lock_bh(&ar->mcastpsq_lock); skb_queue_purge(&ar->mcastpsq); spin_unlock_bh(&ar->mcastpsq_lock); /* clear the LSB of the TIM IE's BitMapCtl field */ if (test_bit(WMI_READY, &ar->flag)) ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, MCAST_AID, 0); } if (!is_broadcast_ether_addr(bssid)) { /* send event to application */ cfg80211_del_sta(vif->ndev, bssid, GFP_KERNEL); } if (memcmp(vif->ndev->dev_addr, bssid, ETH_ALEN) == 0) { memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); clear_bit(CONNECTED, &vif->flags); } return; } ath6kl_cfg80211_disconnect_event(vif, reason, bssid, assoc_resp_len, assoc_info, prot_reason_status); aggr_reset_state(vif->aggr_cntxt->aggr_conn); del_timer(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason); /* * If the event is due to disconnect cmd from the host, only they * the target would stop trying to connect. Under any other * condition, target would keep trying to connect. */ if (reason == DISCONNECT_CMD) { if (!ar->usr_bss_filter && test_bit(WMI_READY, &ar->flag)) ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); } else { set_bit(CONNECT_PEND, &vif->flags); if (((reason == ASSOC_FAILED) && (prot_reason_status == 0x11)) || ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) && (vif->reconnect_flag == 1))) { set_bit(CONNECTED, &vif->flags); return; } } /* update connect & link status atomically */ spin_lock_bh(&vif->if_lock); clear_bit(CONNECTED, &vif->flags); netif_carrier_off(vif->ndev); spin_unlock_bh(&vif->if_lock); if ((reason != CSERV_DISCONNECT) || (vif->reconnect_flag != 1)) vif->reconnect_flag = 0; if (reason != CSERV_DISCONNECT) ar->user_key_ctrl = 0; netif_stop_queue(vif->ndev); memset(vif->bssid, 0, sizeof(vif->bssid)); vif->bss_ch = 0; ath6kl_tx_data_cleanup(ar); } struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar) { struct ath6kl_vif *vif; spin_lock_bh(&ar->list_lock); if (list_empty(&ar->vif_list)) { spin_unlock_bh(&ar->list_lock); return NULL; } vif = list_first_entry(&ar->vif_list, struct ath6kl_vif, list); spin_unlock_bh(&ar->list_lock); return vif; } static int ath6kl_open(struct net_device *dev) { struct ath6kl_vif *vif = netdev_priv(dev); set_bit(WLAN_ENABLED, &vif->flags); if (test_bit(CONNECTED, &vif->flags)) { netif_carrier_on(dev); netif_wake_queue(dev); } else netif_carrier_off(dev); return 0; } static int ath6kl_close(struct net_device *dev) { struct ath6kl_vif *vif = netdev_priv(dev); netif_stop_queue(dev); ath6kl_cfg80211_stop(vif); clear_bit(WLAN_ENABLED, &vif->flags); return 0; } static struct net_device_stats *ath6kl_get_stats(struct net_device *dev) { struct ath6kl_vif *vif = netdev_priv(dev); return &vif->net_stats; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static int ath6kl_set_features(struct net_device *dev, netdev_features_t features) { struct ath6kl_vif *vif = netdev_priv(dev); struct ath6kl *ar = vif->ar; int err = 0; if ((features & NETIF_F_RXCSUM) && (ar->rx_meta_ver != WMI_META_VERSION_2)) { ar->rx_meta_ver = WMI_META_VERSION_2; err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, vif->fw_vif_idx, ar->rx_meta_ver, 0, 0); if (err) { dev->features = features & ~NETIF_F_RXCSUM; return err; } } else if (!(features & NETIF_F_RXCSUM) && (ar->rx_meta_ver == WMI_META_VERSION_2)) { ar->rx_meta_ver = 0; err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, vif->fw_vif_idx, ar->rx_meta_ver, 0, 0); if (err) { dev->features = features | NETIF_F_RXCSUM; return err; } } return err; } #endif static void ath6kl_set_multicast_list(struct net_device *ndev) { struct ath6kl_vif *vif = netdev_priv(ndev); bool mc_all_on = false; int mc_count = netdev_mc_count(ndev); struct netdev_hw_addr *ha; bool found; struct ath6kl_mc_filter *mc_filter, *tmp; struct list_head mc_filter_new; int ret; if (!test_bit(WMI_READY, &vif->ar->flag) || !test_bit(WLAN_ENABLED, &vif->flags)) return; /* Enable multicast-all filter. */ mc_all_on = !!(ndev->flags & IFF_PROMISC) || !!(ndev->flags & IFF_ALLMULTI) || !!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST); if (mc_all_on) set_bit(NETDEV_MCAST_ALL_ON, &vif->flags); else clear_bit(NETDEV_MCAST_ALL_ON, &vif->flags); if (test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, vif->ar->fw_capabilities)) { mc_all_on = mc_all_on || (vif->ar->state == ATH6KL_STATE_ON); } if (!(ndev->flags & IFF_MULTICAST)) { mc_all_on = false; set_bit(NETDEV_MCAST_ALL_OFF, &vif->flags); } else { clear_bit(NETDEV_MCAST_ALL_OFF, &vif->flags); } /* Enable/disable "multicast-all" filter*/ ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast-all filter\n", mc_all_on ? "enabling" : "disabling"); ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, mc_all_on); if (ret) { ath6kl_warn("Failed to %s multicast-all receive\n", mc_all_on ? "enable" : "disable"); return; } if (test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) return; /* Keep the driver and firmware mcast list in sync. */ list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) { found = false; netdev_for_each_mc_addr(ha, ndev) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (memcmp(ha->addr, mc_filter->hw_addr, #else if (memcmp(ha->dmi_addr, mc_filter->hw_addr, #endif ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { found = true; break; } } if (!found) { /* * Delete the filter which was previously set * but not in the new request. */ ath6kl_dbg(ATH6KL_DBG_TRC, "Removing %pM from multicast filter\n", mc_filter->hw_addr); ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, mc_filter->hw_addr, false); if (ret) { ath6kl_warn("Failed to remove multicast filter:%pM\n", mc_filter->hw_addr); return; } list_del(&mc_filter->list); kfree(mc_filter); } } INIT_LIST_HEAD(&mc_filter_new); netdev_for_each_mc_addr(ha, ndev) { found = false; list_for_each_entry(mc_filter, &vif->mc_filter, list) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (memcmp(ha->addr, mc_filter->hw_addr, #else if (memcmp(ha->dmi_addr, mc_filter->hw_addr, #endif ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { found = true; break; } } if (!found) { mc_filter = kzalloc(sizeof(struct ath6kl_mc_filter), GFP_ATOMIC); if (!mc_filter) { WARN_ON(1); goto out; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(mc_filter->hw_addr, ha->addr, #else memcpy(mc_filter->hw_addr, ha->dmi_addr, #endif ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE); /* Set the multicast filter */ ath6kl_dbg(ATH6KL_DBG_TRC, "Adding %pM to multicast filter list\n", mc_filter->hw_addr); ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, mc_filter->hw_addr, true); if (ret) { ath6kl_warn("Failed to add multicast filter :%pM\n", mc_filter->hw_addr); kfree(mc_filter); goto out; } list_add_tail(&mc_filter->list, &mc_filter_new); } } out: list_splice_tail(&mc_filter_new, &vif->mc_filter); } static const struct net_device_ops ath6kl_netdev_ops = { .ndo_open = ath6kl_open, .ndo_stop = ath6kl_close, .ndo_start_xmit = ath6kl_data_tx, .ndo_get_stats = ath6kl_get_stats, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_set_features = ath6kl_set_features, #endif .ndo_set_rx_mode = ath6kl_set_multicast_list, }; void init_netdev(struct net_device *dev) { netdev_attach_ops(dev, &ath6kl_netdev_ops); dev->destructor = free_netdev; dev->watchdog_timeo = ATH6KL_TX_TIMEOUT; dev->needed_headroom = ETH_HLEN; dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) + sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; #else dev->features |= NETIF_F_IP_CSUM; #endif return; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/init.c0000644000175000017500000012320612026211315023541 0ustar mcgrofmcgrof /* * Copyright (c) 2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include "core.h" #include "cfg80211.h" #include "target.h" #include "debug.h" #include "hif-ops.h" #include "htc-ops.h" static const struct ath6kl_hw hw_list[] = { { .id = AR6003_HW_2_0_VERSION, .name = "ar6003 hw 2.0", .dataset_patch_addr = 0x57e884, .app_load_addr = 0x543180, .board_ext_data_addr = 0x57e500, .reserved_ram_size = 6912, .refclk_hz = 26000000, .uarttx_pin = 8, .flags = 0, /* hw2.0 needs override address hardcoded */ .app_start_override_addr = 0x944C00, .fw = { .dir = AR6003_HW_2_0_FW_DIR, .otp = AR6003_HW_2_0_OTP_FILE, .fw = AR6003_HW_2_0_FIRMWARE_FILE, .tcmd = AR6003_HW_2_0_TCMD_FIRMWARE_FILE, .patch = AR6003_HW_2_0_PATCH_FILE, }, .fw_board = AR6003_HW_2_0_BOARD_DATA_FILE, .fw_default_board = AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE, }, { .id = AR6003_HW_2_1_1_VERSION, .name = "ar6003 hw 2.1.1", .dataset_patch_addr = 0x57ff74, .app_load_addr = 0x1234, .board_ext_data_addr = 0x542330, .reserved_ram_size = 512, .refclk_hz = 26000000, .uarttx_pin = 8, .testscript_addr = 0x57ef74, .flags = 0, .fw = { .dir = AR6003_HW_2_1_1_FW_DIR, .otp = AR6003_HW_2_1_1_OTP_FILE, .fw = AR6003_HW_2_1_1_FIRMWARE_FILE, .tcmd = AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE, .patch = AR6003_HW_2_1_1_PATCH_FILE, .utf = AR6003_HW_2_1_1_UTF_FIRMWARE_FILE, .testscript = AR6003_HW_2_1_1_TESTSCRIPT_FILE, }, .fw_board = AR6003_HW_2_1_1_BOARD_DATA_FILE, .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, }, { .id = AR6004_HW_1_0_VERSION, .name = "ar6004 hw 1.0", .dataset_patch_addr = 0x57e884, .app_load_addr = 0x1234, .board_ext_data_addr = 0x437000, .reserved_ram_size = 19456, .board_addr = 0x433900, .refclk_hz = 26000000, .uarttx_pin = 11, .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_0_FW_DIR, .fw = AR6004_HW_1_0_FIRMWARE_FILE, }, .fw_board = AR6004_HW_1_0_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE, }, { .id = AR6004_HW_1_1_VERSION, .name = "ar6004 hw 1.1", .dataset_patch_addr = 0x57e884, .app_load_addr = 0x1234, .board_ext_data_addr = 0x437000, .reserved_ram_size = 11264, .board_addr = 0x43d400, .refclk_hz = 40000000, .uarttx_pin = 11, .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_1_FW_DIR, .fw = AR6004_HW_1_1_FIRMWARE_FILE, }, .fw_board = AR6004_HW_1_1_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE, }, { .id = AR6004_HW_1_2_VERSION, .name = "ar6004 hw 1.2", .dataset_patch_addr = 0x436ecc, .app_load_addr = 0x1234, .board_ext_data_addr = 0x437000, .reserved_ram_size = 9216, .board_addr = 0x435c00, .refclk_hz = 40000000, .uarttx_pin = 11, .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_2_FW_DIR, .fw = AR6004_HW_1_2_FIRMWARE_FILE, }, .fw_board = AR6004_HW_1_2_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE, }, }; /* * Include definitions here that can be used to tune the WLAN module * behavior. Different customers can tune the behavior as per their needs, * here. */ /* * This configuration item enable/disable keepalive support. * Keepalive support: In the absence of any data traffic to AP, null * frames will be sent to the AP at periodic interval, to keep the association * active. This configuration item defines the periodic interval. * Use value of zero to disable keepalive support * Default: 60 seconds */ #define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60 /* * This configuration item sets the value of disconnect timeout * Firmware delays sending the disconnec event to the host for this * timeout after is gets disconnected from the current AP. * If the firmware successly roams within the disconnect timeout * it sends a new connect event */ #define WLAN_CONFIG_DISCONNECT_TIMEOUT 10 #define ATH6KL_DATA_OFFSET 64 struct sk_buff *ath6kl_buf_alloc(int size) { struct sk_buff *skb; u16 reserved; /* Add chacheline space at front and back of buffer */ reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES; skb = dev_alloc_skb(size + reserved); if (skb) skb_reserve(skb, reserved - L1_CACHE_BYTES); return skb; } void ath6kl_init_profile_info(struct ath6kl_vif *vif) { vif->ssid_len = 0; memset(vif->ssid, 0, sizeof(vif->ssid)); vif->dot11_auth_mode = OPEN_AUTH; vif->auth_mode = NONE_AUTH; vif->prwise_crypto = NONE_CRYPT; vif->prwise_crypto_len = 0; vif->grp_crypto = NONE_CRYPT; vif->grp_crypto_len = 0; memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); memset(vif->bssid, 0, sizeof(vif->bssid)); vif->bss_ch = 0; } static int ath6kl_set_host_app_area(struct ath6kl *ar) { u32 address, data; struct host_app_area host_app_area; /* Fetch the address of the host_app_area_s * instance in the host interest area */ address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); address = TARG_VTOP(ar->target_type, address); if (ath6kl_diag_read32(ar, address, &data)) return -EIO; address = TARG_VTOP(ar->target_type, data); host_app_area.wmi_protocol_ver = cpu_to_le32(WMI_PROTOCOL_VERSION); if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area, sizeof(struct host_app_area))) return -EIO; return 0; } static inline void set_ac2_ep_map(struct ath6kl *ar, u8 ac, enum htc_endpoint_id ep) { ar->ac2ep_map[ac] = ep; ar->ep2ac_map[ep] = ac; } /* connect to a service */ static int ath6kl_connectservice(struct ath6kl *ar, struct htc_service_connect_req *con_req, char *desc) { int status; struct htc_service_connect_resp response; memset(&response, 0, sizeof(response)); status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response); if (status) { ath6kl_err("failed to connect to %s service status:%d\n", desc, status); return status; } switch (con_req->svc_id) { case WMI_CONTROL_SVC: if (test_bit(WMI_ENABLED, &ar->flag)) ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint); ar->ctrl_ep = response.endpoint; break; case WMI_DATA_BE_SVC: set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint); break; case WMI_DATA_BK_SVC: set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint); break; case WMI_DATA_VI_SVC: set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint); break; case WMI_DATA_VO_SVC: set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint); break; default: ath6kl_err("service id is not mapped %d\n", con_req->svc_id); return -EINVAL; } return 0; } static int ath6kl_init_service_ep(struct ath6kl *ar) { struct htc_service_connect_req connect; memset(&connect, 0, sizeof(connect)); /* these fields are the same for all service endpoints */ connect.ep_cb.tx_comp_multi = ath6kl_tx_complete; connect.ep_cb.rx = ath6kl_rx; connect.ep_cb.rx_refill = ath6kl_rx_refill; connect.ep_cb.tx_full = ath6kl_tx_queue_full; /* * Set the max queue depth so that our ath6kl_tx_queue_full handler * gets called. */ connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH; connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4; if (!connect.ep_cb.rx_refill_thresh) connect.ep_cb.rx_refill_thresh++; /* connect to control service */ connect.svc_id = WMI_CONTROL_SVC; if (ath6kl_connectservice(ar, &connect, "WMI CONTROL")) return -EIO; connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN; /* * Limit the HTC message size on the send path, although e can * receive A-MSDU frames of 4K, we will only send ethernet-sized * (802.3) frames on the send path. */ connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH; /* * To reduce the amount of committed memory for larger A_MSDU * frames, use the recv-alloc threshold mechanism for larger * packets. */ connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE; connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf; /* * For the remaining data services set the connection flag to * reduce dribbling, if configured to do so. */ connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB; connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK; connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF; connect.svc_id = WMI_DATA_BE_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA BE")) return -EIO; /* connect to back-ground map this to WMI LOW_PRI */ connect.svc_id = WMI_DATA_BK_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) return -EIO; /* connect to Video service, map this to to HI PRI */ connect.svc_id = WMI_DATA_VI_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) return -EIO; /* * Connect to VO service, this is currently not mapped to a WMI * priority stream due to historical reasons. WMI originally * defined 3 priorities over 3 mailboxes We can change this when * WMI is reworked so that priorities are not dependent on * mailboxes. */ connect.svc_id = WMI_DATA_VO_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA VO")) return -EIO; return 0; } void ath6kl_init_control_info(struct ath6kl_vif *vif) { ath6kl_init_profile_info(vif); vif->def_txkey_index = 0; memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); vif->ch_hint = 0; } /* * Set HTC/Mbox operational parameters, this can only be called when the * target is in the BMI phase. */ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, u8 htc_ctrl_buf) { int status; u32 blk_size; blk_size = ar->mbox_info.block_size; if (htc_ctrl_buf) blk_size |= ((u32)htc_ctrl_buf) << 16; /* set the host interest area for the block size */ status = ath6kl_bmi_write_hi32(ar, hi_mbox_io_block_sz, blk_size); if (status) { ath6kl_err("bmi_write_memory for IO block size failed\n"); goto out; } ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n", blk_size, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz))); if (mbox_isr_yield_val) { /* set the host interest area for the mbox ISR yield limit */ status = ath6kl_bmi_write_hi32(ar, hi_mbox_isr_yield_limit, mbox_isr_yield_val); if (status) { ath6kl_err("bmi_write_memory for yield limit failed\n"); goto out; } } out: return status; } static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) { int ret; /* * Configure the device for rx dot11 header rules. "0,0" are the * default values. Required if checksum offload is needed. Set * RxMetaVersion to 2. */ ret = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, ar->rx_meta_ver, 0, 0); if (ret) { ath6kl_err("unable to set the rx frame format: %d\n", ret); return ret; } if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) { ret = ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, IGNORE_PS_FAIL_DURING_SCAN); if (ret) { ath6kl_err("unable to set power save fail event policy: %d\n", ret); return ret; } } if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) { ret = ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, WMI_FOLLOW_BARKER_IN_ERP); if (ret) { ath6kl_err("unable to set barker preamble policy: %d\n", ret); return ret; } } ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, WLAN_CONFIG_KEEP_ALIVE_INTERVAL); if (ret) { ath6kl_err("unable to set keep alive interval: %d\n", ret); return ret; } ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, WLAN_CONFIG_DISCONNECT_TIMEOUT); if (ret) { ath6kl_err("unable to set disconnect timeout: %d\n", ret); return ret; } if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) { ret = ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED); if (ret) { ath6kl_err("unable to set txop bursting: %d\n", ret); return ret; } } if (ar->p2p && (ar->vif_max == 1 || idx)) { ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx, P2P_FLAG_CAPABILITIES_REQ | P2P_FLAG_MACADDR_REQ | P2P_FLAG_HMODEL_REQ); if (ret) { ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P capabilities (%d) - assuming P2P not supported\n", ret); ar->p2p = false; } } if (ar->p2p && (ar->vif_max == 1 || idx)) { /* Enable Probe Request reporting for P2P */ ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true); if (ret) { ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe Request reporting (%d)\n", ret); } } return ret; } int ath6kl_configure_target(struct ath6kl *ar) { u32 param, ram_reserved_size; u8 fw_iftype, fw_mode = 0, fw_submode = 0; int i, status; param = !!(ar->conf_flags & ATH6KL_CONF_UART_DEBUG); if (ath6kl_bmi_write_hi32(ar, hi_serial_enable, param)) { ath6kl_err("bmi_write_memory for uart debug failed\n"); return -EIO; } /* * Note: Even though the firmware interface type is * chosen as BSS_STA for all three interfaces, can * be configured to IBSS/AP as long as the fw submode * remains normal mode (0 - AP, STA and IBSS). But * due to an target assert in firmware only one interface is * configured for now. */ fw_iftype = HI_OPTION_FW_MODE_BSS_STA; for (i = 0; i < ar->vif_max; i++) fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); /* * Submodes when fw does not support dynamic interface * switching: * vif[0] - AP/STA/IBSS * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" * Otherwise, All the interface are initialized to p2p dev. */ if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, ar->fw_capabilities)) { for (i = 0; i < ar->vif_max; i++) fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << (i * HI_OPTION_FW_SUBMODE_BITS); } else { for (i = 0; i < ar->max_norm_iface; i++) fw_submode |= HI_OPTION_FW_SUBMODE_NONE << (i * HI_OPTION_FW_SUBMODE_BITS); for (i = ar->max_norm_iface; i < ar->vif_max; i++) fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << (i * HI_OPTION_FW_SUBMODE_BITS); if (ar->p2p && ar->vif_max == 1) fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; } if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest, HTC_PROTOCOL_VERSION) != 0) { ath6kl_err("bmi_write_memory for htc version failed\n"); return -EIO; } /* set the firmware mode to STA/IBSS/AP */ param = 0; if (ath6kl_bmi_read_hi32(ar, hi_option_flag, ¶m) != 0) { ath6kl_err("bmi_read_memory for setting fwmode failed\n"); return -EIO; } param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT); param |= fw_mode << HI_OPTION_FW_MODE_SHIFT; param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT; param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); if (ath6kl_bmi_write_hi32(ar, hi_option_flag, param) != 0) { ath6kl_err("bmi_write_memory for setting fwmode failed\n"); return -EIO; } ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n"); /* * Hardcode the address use for the extended board data * Ideally this should be pre-allocate by the OS at boot time * But since it is a new feature and board data is loaded * at init time, we have to workaround this from host. * It is difficult to patch the firmware boot code, * but possible in theory. */ if (ar->target_type == TARGET_TYPE_AR6003) { param = ar->hw.board_ext_data_addr; ram_reserved_size = ar->hw.reserved_ram_size; if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); return -EIO; } if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, ram_reserved_size) != 0) { ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); return -EIO; } } /* set the block size for the target */ if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0)) /* use default number of control buffers */ return -EIO; /* Configure GPIO AR600x UART */ status = ath6kl_bmi_write_hi32(ar, hi_dbg_uart_txpin, ar->hw.uarttx_pin); if (status) return status; /* Configure target refclk_hz */ status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, ar->hw.refclk_hz); if (status) return status; return 0; } /* firmware upload */ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, u8 **fw, size_t *fw_len) { const struct firmware *fw_entry; int ret; ret = request_firmware(&fw_entry, filename, ar->dev); if (ret) return ret; *fw_len = fw_entry->size; *fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); if (*fw == NULL) ret = -ENOMEM; release_firmware(fw_entry); return ret; } #ifdef CONFIG_OF /* * Check the device tree for a board-id and use it to construct * the pathname to the firmware file. Used (for now) to find a * fallback to the "bdata.bin" file--typically a symlink to the * appropriate board-specific file. */ static bool check_device_tree(struct ath6kl *ar) { static const char *board_id_prop = "atheros,board-id"; struct device_node *node; char board_filename[64]; const char *board_id; int ret; for_each_compatible_node(node, NULL, "atheros,ath6kl") { board_id = of_get_property(node, board_id_prop, NULL); if (board_id == NULL) { ath6kl_warn("No \"%s\" property on %s node.\n", board_id_prop, node->name); continue; } snprintf(board_filename, sizeof(board_filename), "%s/bdata.%s.bin", ar->hw.fw.dir, board_id); ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board, &ar->fw_board_len); if (ret) { ath6kl_err("Failed to get DT board file %s: %d\n", board_filename, ret); continue; } return true; } return false; } #else static bool check_device_tree(struct ath6kl *ar) { return false; } #endif /* CONFIG_OF */ static int ath6kl_fetch_board_file(struct ath6kl *ar) { const char *filename; int ret; if (ar->fw_board != NULL) return 0; if (WARN_ON(ar->hw.fw_board == NULL)) return -EINVAL; filename = ar->hw.fw_board; ret = ath6kl_get_fw(ar, filename, &ar->fw_board, &ar->fw_board_len); if (ret == 0) { /* managed to get proper board file */ return 0; } if (check_device_tree(ar)) { /* got board file from device tree */ return 0; } /* there was no proper board file, try to use default instead */ ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", filename, ret); filename = ar->hw.fw_default_board; ret = ath6kl_get_fw(ar, filename, &ar->fw_board, &ar->fw_board_len); if (ret) { ath6kl_err("Failed to get default board file %s: %d\n", filename, ret); return ret; } ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n"); ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n"); return 0; } static int ath6kl_fetch_otp_file(struct ath6kl *ar) { char filename[100]; int ret; if (ar->fw_otp != NULL) return 0; if (ar->hw.fw.otp == NULL) { ath6kl_dbg(ATH6KL_DBG_BOOT, "no OTP file configured for this hw\n"); return 0; } snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, ar->hw.fw.otp); ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, &ar->fw_otp_len); if (ret) { ath6kl_err("Failed to get OTP file %s: %d\n", filename, ret); return ret; } return 0; } static int ath6kl_fetch_testmode_file(struct ath6kl *ar) { char filename[100]; int ret; if (ar->testmode == 0) return 0; ath6kl_dbg(ATH6KL_DBG_BOOT, "testmode %d\n", ar->testmode); if (ar->testmode == 2) { if (ar->hw.fw.utf == NULL) { ath6kl_warn("testmode 2 not supported\n"); return -EOPNOTSUPP; } snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, ar->hw.fw.utf); } else { if (ar->hw.fw.tcmd == NULL) { ath6kl_warn("testmode 1 not supported\n"); return -EOPNOTSUPP; } snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, ar->hw.fw.tcmd); } set_bit(TESTMODE, &ar->flag); ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); if (ret) { ath6kl_err("Failed to get testmode %d firmware file %s: %d\n", ar->testmode, filename, ret); return ret; } return 0; } static int ath6kl_fetch_fw_file(struct ath6kl *ar) { char filename[100]; int ret; if (ar->fw != NULL) return 0; /* FIXME: remove WARN_ON() as we won't support FW API 1 for long */ if (WARN_ON(ar->hw.fw.fw == NULL)) return -EINVAL; snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, ar->hw.fw.fw); ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); if (ret) { ath6kl_err("Failed to get firmware file %s: %d\n", filename, ret); return ret; } return 0; } static int ath6kl_fetch_patch_file(struct ath6kl *ar) { char filename[100]; int ret; if (ar->fw_patch != NULL) return 0; if (ar->hw.fw.patch == NULL) return 0; snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, ar->hw.fw.patch); ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, &ar->fw_patch_len); if (ret) { ath6kl_err("Failed to get patch file %s: %d\n", filename, ret); return ret; } return 0; } static int ath6kl_fetch_testscript_file(struct ath6kl *ar) { char filename[100]; int ret; if (ar->testmode != 2) return 0; if (ar->fw_testscript != NULL) return 0; if (ar->hw.fw.testscript == NULL) return 0; snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, ar->hw.fw.testscript); ret = ath6kl_get_fw(ar, filename, &ar->fw_testscript, &ar->fw_testscript_len); if (ret) { ath6kl_err("Failed to get testscript file %s: %d\n", filename, ret); return ret; } return 0; } static int ath6kl_fetch_fw_api1(struct ath6kl *ar) { int ret; ret = ath6kl_fetch_otp_file(ar); if (ret) return ret; ret = ath6kl_fetch_fw_file(ar); if (ret) return ret; ret = ath6kl_fetch_patch_file(ar); if (ret) return ret; ret = ath6kl_fetch_testscript_file(ar); if (ret) return ret; return 0; } static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) { size_t magic_len, len, ie_len; const struct firmware *fw; struct ath6kl_fw_ie *hdr; char filename[100]; const u8 *data; int ret, ie_id, i, index, bit; __le32 *val; snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name); ret = request_firmware(&fw, filename, ar->dev); if (ret) return ret; data = fw->data; len = fw->size; /* magic also includes the null byte, check that as well */ magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; if (len < magic_len) { ret = -EINVAL; goto out; } if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { ret = -EINVAL; goto out; } len -= magic_len; data += magic_len; /* loop elements */ while (len > sizeof(struct ath6kl_fw_ie)) { /* hdr is unaligned! */ hdr = (struct ath6kl_fw_ie *) data; ie_id = le32_to_cpup(&hdr->id); ie_len = le32_to_cpup(&hdr->len); len -= sizeof(*hdr); data += sizeof(*hdr); if (len < ie_len) { ret = -EINVAL; goto out; } switch (ie_id) { case ATH6KL_FW_IE_FW_VERSION: strlcpy(ar->wiphy->fw_version, data, sizeof(ar->wiphy->fw_version)); ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw version %s\n", ar->wiphy->fw_version); break; case ATH6KL_FW_IE_OTP_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", ie_len); ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_otp == NULL) { ret = -ENOMEM; goto out; } ar->fw_otp_len = ie_len; break; case ATH6KL_FW_IE_FW_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n", ie_len); /* in testmode we already might have a fw file */ if (ar->fw != NULL) break; ar->fw = vmalloc(ie_len); if (ar->fw == NULL) { ret = -ENOMEM; goto out; } memcpy(ar->fw, data, ie_len); ar->fw_len = ie_len; break; case ATH6KL_FW_IE_PATCH_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n", ie_len); ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_patch == NULL) { ret = -ENOMEM; goto out; } ar->fw_patch_len = ie_len; break; case ATH6KL_FW_IE_RESERVED_RAM_SIZE: val = (__le32 *) data; ar->hw.reserved_ram_size = le32_to_cpup(val); ath6kl_dbg(ATH6KL_DBG_BOOT, "found reserved ram size ie 0x%d\n", ar->hw.reserved_ram_size); break; case ATH6KL_FW_IE_CAPABILITIES: ath6kl_dbg(ATH6KL_DBG_BOOT, "found firmware capabilities ie (%zd B)\n", ie_len); for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { index = i / 8; bit = i % 8; if (index == ie_len) break; if (data[index] & (1 << bit)) __set_bit(i, ar->fw_capabilities); } ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "", ar->fw_capabilities, sizeof(ar->fw_capabilities)); break; case ATH6KL_FW_IE_PATCH_ADDR: if (ie_len != sizeof(*val)) break; val = (__le32 *) data; ar->hw.dataset_patch_addr = le32_to_cpup(val); ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch address ie 0x%x\n", ar->hw.dataset_patch_addr); break; case ATH6KL_FW_IE_BOARD_ADDR: if (ie_len != sizeof(*val)) break; val = (__le32 *) data; ar->hw.board_addr = le32_to_cpup(val); ath6kl_dbg(ATH6KL_DBG_BOOT, "found board address ie 0x%x\n", ar->hw.board_addr); break; case ATH6KL_FW_IE_VIF_MAX: if (ie_len != sizeof(*val)) break; val = (__le32 *) data; ar->vif_max = min_t(unsigned int, le32_to_cpup(val), ATH6KL_VIF_MAX); if (ar->vif_max > 1 && !ar->p2p) ar->max_norm_iface = 2; ath6kl_dbg(ATH6KL_DBG_BOOT, "found vif max ie %d\n", ar->vif_max); break; default: ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n", le32_to_cpup(&hdr->id)); break; } len -= ie_len; data += ie_len; }; ret = 0; out: release_firmware(fw); return ret; } int ath6kl_init_fetch_firmwares(struct ath6kl *ar) { int ret; ret = ath6kl_fetch_board_file(ar); if (ret) return ret; ret = ath6kl_fetch_testmode_file(ar); if (ret) return ret; ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API3_FILE); if (ret == 0) { ar->fw_api = 3; goto out; } ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API2_FILE); if (ret == 0) { ar->fw_api = 2; goto out; } ret = ath6kl_fetch_fw_api1(ar); if (ret) return ret; ar->fw_api = 1; out: ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api %d\n", ar->fw_api); return 0; } static int ath6kl_upload_board_file(struct ath6kl *ar) { u32 board_address, board_ext_address, param; u32 board_data_size, board_ext_data_size; int ret; if (WARN_ON(ar->fw_board == NULL)) return -ENOENT; /* * Determine where in Target RAM to write Board Data. * For AR6004, host determine Target RAM address for * writing board data. */ if (ar->hw.board_addr != 0) { board_address = ar->hw.board_addr; ath6kl_bmi_write_hi32(ar, hi_board_data, board_address); } else { ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address); } /* determine where in target ram to write extended board data */ ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address); if (ar->target_type == TARGET_TYPE_AR6003 && board_ext_address == 0) { ath6kl_err("Failed to get board file target address.\n"); return -EINVAL; } switch (ar->target_type) { case TARGET_TYPE_AR6003: board_data_size = AR6003_BOARD_DATA_SZ; board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ; if (ar->fw_board_len > (board_data_size + board_ext_data_size)) board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ_V2; break; case TARGET_TYPE_AR6004: board_data_size = AR6004_BOARD_DATA_SZ; board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ; break; default: WARN_ON(1); return -EINVAL; break; } if (board_ext_address && ar->fw_board_len == (board_data_size + board_ext_data_size)) { /* write extended board data */ ath6kl_dbg(ATH6KL_DBG_BOOT, "writing extended board data to 0x%x (%d B)\n", board_ext_address, board_ext_data_size); ret = ath6kl_bmi_write(ar, board_ext_address, ar->fw_board + board_data_size, board_ext_data_size); if (ret) { ath6kl_err("Failed to write extended board data: %d\n", ret); return ret; } /* record that extended board data is initialized */ param = (board_ext_data_size << 16) | 1; ath6kl_bmi_write_hi32(ar, hi_board_ext_data_config, param); } if (ar->fw_board_len < board_data_size) { ath6kl_err("Too small board file: %zu\n", ar->fw_board_len); ret = -EINVAL; return ret; } ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n", board_address, board_data_size); ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, board_data_size); if (ret) { ath6kl_err("Board file bmi write failed: %d\n", ret); return ret; } /* record the fact that Board Data IS initialized */ ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, 1); return ret; } static int ath6kl_upload_otp(struct ath6kl *ar) { u32 address, param; bool from_hw = false; int ret; if (ar->fw_otp == NULL) return 0; address = ar->hw.app_load_addr; ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address, ar->fw_otp_len); ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, ar->fw_otp_len); if (ret) { ath6kl_err("Failed to upload OTP file: %d\n", ret); return ret; } /* read firmware start address */ ret = ath6kl_bmi_read_hi32(ar, hi_app_start, &address); if (ret) { ath6kl_err("Failed to read hi_app_start: %d\n", ret); return ret; } if (ar->hw.app_start_override_addr == 0) { ar->hw.app_start_override_addr = address; from_hw = true; } ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr%s 0x%x\n", from_hw ? " (from hw)" : "", ar->hw.app_start_override_addr); /* execute the OTP code */ ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", ar->hw.app_start_override_addr); param = 0; ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, ¶m); return ret; } static int ath6kl_upload_firmware(struct ath6kl *ar) { u32 address; int ret; if (WARN_ON(ar->fw == NULL)) return 0; address = ar->hw.app_load_addr; ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n", address, ar->fw_len); ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); if (ret) { ath6kl_err("Failed to write firmware: %d\n", ret); return ret; } /* * Set starting address for firmware * Don't need to setup app_start override addr on AR6004 */ if (ar->target_type != TARGET_TYPE_AR6004) { address = ar->hw.app_start_override_addr; ath6kl_bmi_set_app_start(ar, address); } return ret; } static int ath6kl_upload_patch(struct ath6kl *ar) { u32 address; int ret; if (ar->fw_patch == NULL) return 0; address = ar->hw.dataset_patch_addr; ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n", address, ar->fw_patch_len); ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); if (ret) { ath6kl_err("Failed to write patch file: %d\n", ret); return ret; } ath6kl_bmi_write_hi32(ar, hi_dset_list_head, address); return 0; } static int ath6kl_upload_testscript(struct ath6kl *ar) { u32 address; int ret; if (ar->testmode != 2) return 0; if (ar->fw_testscript == NULL) return 0; address = ar->hw.testscript_addr; ath6kl_dbg(ATH6KL_DBG_BOOT, "writing testscript to 0x%x (%zd B)\n", address, ar->fw_testscript_len); ret = ath6kl_bmi_write(ar, address, ar->fw_testscript, ar->fw_testscript_len); if (ret) { ath6kl_err("Failed to write testscript file: %d\n", ret); return ret; } ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address); ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096); ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1); return 0; } static int ath6kl_init_upload(struct ath6kl *ar) { u32 param, options, sleep, address; int status = 0; if (ar->target_type != TARGET_TYPE_AR6003 && ar->target_type != TARGET_TYPE_AR6004) return -EINVAL; /* temporarily disable system sleep */ address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; status = ath6kl_bmi_reg_read(ar, address, ¶m); if (status) return status; options = param; param |= ATH6KL_OPTION_SLEEP_DISABLE; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; status = ath6kl_bmi_reg_read(ar, address, ¶m); if (status) return status; sleep = param; param |= SM(SYSTEM_SLEEP_DISABLE, 1); status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n", options, sleep); /* program analog PLL register */ /* no need to control 40/44MHz clock on AR6004 */ if (ar->target_type != TARGET_TYPE_AR6004) { status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, 0xF9104001); if (status) return status; /* Run at 80/88MHz by default */ param = SM(CPU_CLOCK_STANDARD, 1); address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; } param = 0; address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS; param = SM(LPO_CAL_ENABLE, 1); status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; /* WAR to avoid SDIO CRC err */ if (ar->version.target_ver == AR6003_HW_2_0_VERSION || ar->version.target_ver == AR6003_HW_2_1_1_VERSION) { ath6kl_err("temporary war to avoid sdio crc error\n"); param = 0x28; address = GPIO_BASE_ADDRESS + GPIO_PIN9_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; param = 0x20; address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; } /* write EEPROM data to Target RAM */ status = ath6kl_upload_board_file(ar); if (status) return status; /* transfer One time Programmable data */ status = ath6kl_upload_otp(ar); if (status) return status; /* Download Target firmware */ status = ath6kl_upload_firmware(ar); if (status) return status; status = ath6kl_upload_patch(ar); if (status) return status; /* Download the test script */ status = ath6kl_upload_testscript(ar); if (status) return status; /* Restore system sleep */ address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, sleep); if (status) return status; address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; param = options | 0x20; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; return status; } int ath6kl_init_hw_params(struct ath6kl *ar) { const struct ath6kl_hw *uninitialized_var(hw); int i; for (i = 0; i < ARRAY_SIZE(hw_list); i++) { hw = &hw_list[i]; if (hw->id == ar->version.target_ver) break; } if (i == ARRAY_SIZE(hw_list)) { ath6kl_err("Unsupported hardware version: 0x%x\n", ar->version.target_ver); return -EINVAL; } ar->hw = *hw; ath6kl_dbg(ATH6KL_DBG_BOOT, "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n", ar->version.target_ver, ar->target_type, ar->hw.dataset_patch_addr, ar->hw.app_load_addr); ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x", ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr, ar->hw.reserved_ram_size); ath6kl_dbg(ATH6KL_DBG_BOOT, "refclk_hz %d uarttx_pin %d", ar->hw.refclk_hz, ar->hw.uarttx_pin); return 0; } static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type) { switch (type) { case ATH6KL_HIF_TYPE_SDIO: return "sdio"; case ATH6KL_HIF_TYPE_USB: return "usb"; } return NULL; } int ath6kl_init_hw_start(struct ath6kl *ar) { long timeleft; int ret, i; ath6kl_dbg(ATH6KL_DBG_BOOT, "hw start\n"); ret = ath6kl_hif_power_on(ar); if (ret) return ret; ret = ath6kl_configure_target(ar); if (ret) goto err_power_off; ret = ath6kl_init_upload(ar); if (ret) goto err_power_off; /* Do we need to finish the BMI phase */ /* FIXME: return error from ath6kl_bmi_done() */ if (ath6kl_bmi_done(ar)) { ret = -EIO; goto err_power_off; } /* * The reason we have to wait for the target here is that the * driver layer has to init BMI in order to set the host block * size. */ if (ath6kl_htc_wait_target(ar->htc_target)) { ret = -EIO; goto err_power_off; } if (ath6kl_init_service_ep(ar)) { ret = -EIO; goto err_cleanup_scatter; } /* setup credit distribution */ ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info); /* start HTC */ ret = ath6kl_htc_start(ar->htc_target); if (ret) { /* FIXME: call this */ ath6kl_cookie_cleanup(ar); goto err_cleanup_scatter; } /* Wait for Wmi event to be ready */ timeleft = wait_event_interruptible_timeout(ar->event_wq, test_bit(WMI_READY, &ar->flag), WMI_TIMEOUT); ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { ath6kl_info("%s %s fw %s api %d%s\n", ar->hw.name, ath6kl_init_get_hif_name(ar->hif_type), ar->wiphy->fw_version, ar->fw_api, test_bit(TESTMODE, &ar->flag) ? " testmode" : ""); } if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", ATH6KL_ABI_VERSION, ar->version.abi_ver); ret = -EIO; goto err_htc_stop; } if (!timeleft || signal_pending(current)) { ath6kl_err("wmi is not ready or wait was interrupted\n"); ret = -EIO; goto err_htc_stop; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); /* communicate the wmi protocol verision to the target */ /* FIXME: return error */ if ((ath6kl_set_host_app_area(ar)) != 0) ath6kl_err("unable to set the host app area\n"); for (i = 0; i < ar->vif_max; i++) { ret = ath6kl_target_config_wlan_params(ar, i); if (ret) goto err_htc_stop; } ar->state = ATH6KL_STATE_ON; return 0; err_htc_stop: ath6kl_htc_stop(ar->htc_target); err_cleanup_scatter: ath6kl_hif_cleanup_scatter(ar); err_power_off: ath6kl_hif_power_off(ar); return ret; } int ath6kl_init_hw_stop(struct ath6kl *ar) { int ret; ath6kl_dbg(ATH6KL_DBG_BOOT, "hw stop\n"); ath6kl_htc_stop(ar->htc_target); ath6kl_hif_stop(ar); ath6kl_bmi_reset(ar); ret = ath6kl_hif_power_off(ar); if (ret) ath6kl_warn("failed to power off hif: %d\n", ret); ar->state = ATH6KL_STATE_OFF; return 0; } /* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) { static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; bool discon_issued; netif_stop_queue(vif->ndev); clear_bit(WLAN_ENABLED, &vif->flags); if (wmi_ready) { discon_issued = test_bit(CONNECTED, &vif->flags) || test_bit(CONNECT_PEND, &vif->flags); ath6kl_disconnect(vif); del_timer(&vif->disconnect_timer); if (discon_issued) ath6kl_disconnect_event(vif, DISCONNECT_CMD, (vif->nw_type & AP_NETWORK) ? bcast_mac : vif->bssid, 0, NULL, 0); } if (vif->scan_req) { cfg80211_scan_done(vif->scan_req, true); vif->scan_req = NULL; } /* need to clean up enhanced bmiss detection fw state */ ath6kl_cfg80211_sta_bmiss_enhance(vif, false); } void ath6kl_stop_txrx(struct ath6kl *ar) { struct ath6kl_vif *vif, *tmp_vif; int i; set_bit(DESTROY_IN_PROGRESS, &ar->flag); if (down_interruptible(&ar->sem)) { ath6kl_err("down_interruptible failed\n"); return; } for (i = 0; i < AP_MAX_NUM_STA; i++) aggr_reset_state(ar->sta_list[i].aggr_conn); spin_lock_bh(&ar->list_lock); list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) { list_del(&vif->list); spin_unlock_bh(&ar->list_lock); ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); rtnl_lock(); ath6kl_cfg80211_vif_cleanup(vif); rtnl_unlock(); spin_lock_bh(&ar->list_lock); } spin_unlock_bh(&ar->list_lock); clear_bit(WMI_READY, &ar->flag); /* * After wmi_shudown all WMI events will be dropped. We * need to cleanup the buffers allocated in AP mode and * give disconnect notification to stack, which usually * happens in the disconnect_event. Simulate the disconnect * event by calling the function directly. Sometimes * disconnect_event will be received when the debug logs * are collected. */ ath6kl_wmi_shutdown(ar->wmi); clear_bit(WMI_ENABLED, &ar->flag); if (ar->htc_target) { ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__); ath6kl_htc_stop(ar->htc_target); } /* * Try to reset the device if we can. The driver may have been * configure NOT to reset the target during a debug session. */ ath6kl_dbg(ATH6KL_DBG_TRC, "attempting to reset target on instance destroy\n"); ath6kl_reset_device(ar, ar->target_type, true, true); clear_bit(WLAN_ENABLED, &ar->flag); up(&ar->sem); } EXPORT_SYMBOL(ath6kl_stop_txrx); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/cfg80211.c0000644000175000017500000027042112026211315023733 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include "core.h" #include "cfg80211.h" #include "debug.h" #include "hif-ops.h" #include "testmode.h" #define RATETAB_ENT(_rate, _rateid, _flags) { \ .bitrate = (_rate), \ .flags = (_flags), \ .hw_value = (_rateid), \ } #define CHAN2G(_channel, _freq, _flags) { \ .band = IEEE80211_BAND_2GHZ, \ .hw_value = (_channel), \ .center_freq = (_freq), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } #define CHAN5G(_channel, _flags) { \ .band = IEEE80211_BAND_5GHZ, \ .hw_value = (_channel), \ .center_freq = 5000 + (5 * (_channel)), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } #define DEFAULT_BG_SCAN_PERIOD 60 struct ath6kl_cfg80211_match_probe_ssid { struct cfg80211_ssid ssid; u8 flag; }; static struct ieee80211_rate ath6kl_rates[] = { RATETAB_ENT(10, 0x1, 0), RATETAB_ENT(20, 0x2, 0), RATETAB_ENT(55, 0x4, 0), RATETAB_ENT(110, 0x8, 0), RATETAB_ENT(60, 0x10, 0), RATETAB_ENT(90, 0x20, 0), RATETAB_ENT(120, 0x40, 0), RATETAB_ENT(180, 0x80, 0), RATETAB_ENT(240, 0x100, 0), RATETAB_ENT(360, 0x200, 0), RATETAB_ENT(480, 0x400, 0), RATETAB_ENT(540, 0x800, 0), }; #define ath6kl_a_rates (ath6kl_rates + 4) #define ath6kl_a_rates_size 8 #define ath6kl_g_rates (ath6kl_rates + 0) #define ath6kl_g_rates_size 12 #define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20 #define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ IEEE80211_HT_CAP_SGI_20 | \ IEEE80211_HT_CAP_SGI_40) static struct ieee80211_channel ath6kl_2ghz_channels[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), CHAN2G(3, 2422, 0), CHAN2G(4, 2427, 0), CHAN2G(5, 2432, 0), CHAN2G(6, 2437, 0), CHAN2G(7, 2442, 0), CHAN2G(8, 2447, 0), CHAN2G(9, 2452, 0), CHAN2G(10, 2457, 0), CHAN2G(11, 2462, 0), CHAN2G(12, 2467, 0), CHAN2G(13, 2472, 0), CHAN2G(14, 2484, 0), }; static struct ieee80211_channel ath6kl_5ghz_a_channels[] = { CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0), CHAN5G(108, 0), CHAN5G(112, 0), CHAN5G(116, 0), CHAN5G(120, 0), CHAN5G(124, 0), CHAN5G(128, 0), CHAN5G(132, 0), CHAN5G(136, 0), CHAN5G(140, 0), CHAN5G(149, 0), CHAN5G(153, 0), CHAN5G(157, 0), CHAN5G(161, 0), CHAN5G(165, 0), CHAN5G(184, 0), CHAN5G(188, 0), CHAN5G(192, 0), CHAN5G(196, 0), CHAN5G(200, 0), CHAN5G(204, 0), CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; static struct ieee80211_supported_band ath6kl_band_2ghz = { .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels), .channels = ath6kl_2ghz_channels, .n_bitrates = ath6kl_g_rates_size, .bitrates = ath6kl_g_rates, .ht_cap.cap = ath6kl_g_htcap, .ht_cap.ht_supported = true, }; static struct ieee80211_supported_band ath6kl_band_5ghz = { .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels), .channels = ath6kl_5ghz_a_channels, .n_bitrates = ath6kl_a_rates_size, .bitrates = ath6kl_a_rates, .ht_cap.cap = ath6kl_a_htcap, .ht_cap.ht_supported = true, }; #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */ /* returns true if scheduled scan was stopped */ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; if (ar->state != ATH6KL_STATE_SCHED_SCAN) return false; del_timer_sync(&vif->sched_scan_timer); ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_AWAKE); ar->state = ATH6KL_STATE_ON; return true; } static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; bool stopped; stopped = __ath6kl_cfg80211_sscan_stop(vif); if (!stopped) return; cfg80211_sched_scan_stopped(ar->wiphy); } static int ath6kl_set_wpa_version(struct ath6kl_vif *vif, enum nl80211_wpa_versions wpa_version) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version); if (!wpa_version) { vif->auth_mode = NONE_AUTH; } else if (wpa_version & NL80211_WPA_VERSION_2) { vif->auth_mode = WPA2_AUTH; } else if (wpa_version & NL80211_WPA_VERSION_1) { vif->auth_mode = WPA_AUTH; } else { ath6kl_err("%s: %u not supported\n", __func__, wpa_version); return -ENOTSUPP; } return 0; } static int ath6kl_set_auth_type(struct ath6kl_vif *vif, enum nl80211_auth_type auth_type) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type); switch (auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: vif->dot11_auth_mode = OPEN_AUTH; break; case NL80211_AUTHTYPE_SHARED_KEY: vif->dot11_auth_mode = SHARED_AUTH; break; case NL80211_AUTHTYPE_NETWORK_EAP: vif->dot11_auth_mode = LEAP_AUTH; break; case NL80211_AUTHTYPE_AUTOMATIC: vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH; break; default: ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type); return -ENOTSUPP; } return 0; } static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast) { u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto; u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len : &vif->grp_crypto_len; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n", __func__, cipher, ucast); switch (cipher) { case 0: /* our own hack to use value 0 as no crypto used */ *ar_cipher = NONE_CRYPT; *ar_cipher_len = 0; break; case WLAN_CIPHER_SUITE_WEP40: *ar_cipher = WEP_CRYPT; *ar_cipher_len = 5; break; case WLAN_CIPHER_SUITE_WEP104: *ar_cipher = WEP_CRYPT; *ar_cipher_len = 13; break; case WLAN_CIPHER_SUITE_TKIP: *ar_cipher = TKIP_CRYPT; *ar_cipher_len = 0; break; case WLAN_CIPHER_SUITE_CCMP: *ar_cipher = AES_CRYPT; *ar_cipher_len = 0; break; case WLAN_CIPHER_SUITE_SMS4: *ar_cipher = WAPI_CRYPT; *ar_cipher_len = 0; break; default: ath6kl_err("cipher 0x%x not supported\n", cipher); return -ENOTSUPP; } return 0; } static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt); if (key_mgmt == WLAN_AKM_SUITE_PSK) { if (vif->auth_mode == WPA_AUTH) vif->auth_mode = WPA_PSK_AUTH; else if (vif->auth_mode == WPA2_AUTH) vif->auth_mode = WPA2_PSK_AUTH; } else if (key_mgmt == 0x00409600) { if (vif->auth_mode == WPA_AUTH) vif->auth_mode = WPA_AUTH_CCKM; else if (vif->auth_mode == WPA2_AUTH) vif->auth_mode = WPA2_AUTH_CCKM; } else if (key_mgmt != WLAN_AKM_SUITE_8021X) { vif->auth_mode = NONE_AUTH; } } static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; if (!test_bit(WMI_READY, &ar->flag)) { ath6kl_err("wmi is not ready\n"); return false; } if (!test_bit(WLAN_ENABLED, &vif->flags)) { ath6kl_err("wlan disabled\n"); return false; } return true; } static bool ath6kl_is_wpa_ie(const u8 *pos) { return pos[0] == WLAN_EID_WPA && pos[1] >= 4 && pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 && pos[5] == 0x01; } static bool ath6kl_is_rsn_ie(const u8 *pos) { return pos[0] == WLAN_EID_RSN; } static bool ath6kl_is_wps_ie(const u8 *pos) { return (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 && pos[5] == 0x04); } static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies, size_t ies_len) { struct ath6kl *ar = vif->ar; const u8 *pos; u8 *buf = NULL; size_t len = 0; int ret; /* * Clear previously set flag */ ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG; /* * Filter out RSN/WPA IE(s) */ if (ies && ies_len) { buf = kmalloc(ies_len, GFP_KERNEL); if (buf == NULL) return -ENOMEM; pos = ies; while (pos + 1 < ies + ies_len) { if (pos + 2 + pos[1] > ies + ies_len) break; if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) { memcpy(buf + len, pos, 2 + pos[1]); len += 2 + pos[1]; } if (ath6kl_is_wps_ie(pos)) ar->connect_ctrl_flags |= CONNECT_WPS_FLAG; pos += 2 + pos[1]; } } ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_ASSOC_REQ, buf, len); kfree(buf); return ret; } static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type) { switch (type) { case NL80211_IFTYPE_STATION: *nw_type = INFRA_NETWORK; break; case NL80211_IFTYPE_ADHOC: *nw_type = ADHOC_NETWORK; break; case NL80211_IFTYPE_AP: *nw_type = AP_NETWORK; break; case NL80211_IFTYPE_P2P_CLIENT: *nw_type = INFRA_NETWORK; break; case NL80211_IFTYPE_P2P_GO: *nw_type = AP_NETWORK; break; default: ath6kl_err("invalid interface type %u\n", type); return -ENOTSUPP; } return 0; } static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, u8 *if_idx, u8 *nw_type) { int i; if (ath6kl_nliftype_to_drv_iftype(type, nw_type)) return false; if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) && ar->num_vif)) return false; if (type == NL80211_IFTYPE_STATION || type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) { for (i = 0; i < ar->vif_max; i++) { if ((ar->avail_idx_map >> i) & BIT(0)) { *if_idx = i; return true; } } } if (type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO) { for (i = ar->max_norm_iface; i < ar->vif_max; i++) { if ((ar->avail_idx_map >> i) & BIT(0)) { *if_idx = i; return true; } } } return false; } static bool ath6kl_is_tx_pending(struct ath6kl *ar) { return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0; } static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); int status; u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE; u16 interval; ath6kl_cfg80211_sscan_disable(vif); vif->sme_state = SME_CONNECTING; if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { ath6kl_err("destroy in progress\n"); return -EBUSY; } if (test_bit(SKIP_SCAN, &ar->flag) && ((sme->channel && sme->channel->center_freq == 0) || (sme->bssid && is_zero_ether_addr(sme->bssid)))) { ath6kl_err("SkipScan: channel or bssid invalid\n"); return -EINVAL; } if (down_interruptible(&ar->sem)) { ath6kl_err("busy, couldn't get access\n"); return -ERESTARTSYS; } if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { ath6kl_err("busy, destroy in progress\n"); up(&ar->sem); return -EBUSY; } if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) { /* * sleep until the command queue drains */ wait_event_interruptible_timeout(ar->event_wq, ath6kl_is_tx_pending(ar), WMI_TIMEOUT); if (signal_pending(current)) { ath6kl_err("cmd queue drain timeout\n"); up(&ar->sem); return -EINTR; } } status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len); if (status) { up(&ar->sem); return status; } if (sme->ie == NULL || sme->ie_len == 0) ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG; if (test_bit(CONNECTED, &vif->flags) && vif->ssid_len == sme->ssid_len && !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) { vif->reconnect_flag = true; status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx, vif->req_bssid, vif->ch_hint); up(&ar->sem); if (status) { ath6kl_err("wmi_reconnect_cmd failed\n"); return -EIO; } return 0; } else if (vif->ssid_len == sme->ssid_len && !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) { ath6kl_disconnect(vif); } memset(vif->ssid, 0, sizeof(vif->ssid)); vif->ssid_len = sme->ssid_len; memcpy(vif->ssid, sme->ssid, sme->ssid_len); if (sme->channel) vif->ch_hint = sme->channel->center_freq; memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); if (sme->bssid && !is_broadcast_ether_addr(sme->bssid)) memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid)); ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions); status = ath6kl_set_auth_type(vif, sme->auth_type); if (status) { up(&ar->sem); return status; } if (sme->crypto.n_ciphers_pairwise) ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true); else ath6kl_set_cipher(vif, 0, true); ath6kl_set_cipher(vif, sme->crypto.cipher_group, false); if (sme->crypto.n_akm_suites) ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]); if ((sme->key_len) && (vif->auth_mode == NONE_AUTH) && (vif->prwise_crypto == WEP_CRYPT)) { struct ath6kl_key *key = NULL; if (sme->key_idx > WMI_MAX_KEY_INDEX) { ath6kl_err("key index %d out of bounds\n", sme->key_idx); up(&ar->sem); return -ENOENT; } key = &vif->keys[sme->key_idx]; key->key_len = sme->key_len; memcpy(key->key, sme->key, key->key_len); key->cipher = vif->prwise_crypto; vif->def_txkey_index = sme->key_idx; ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx, vif->prwise_crypto, GROUP_USAGE | TX_USAGE, key->key_len, NULL, 0, key->key, KEY_OP_INIT_VAL, NULL, NO_SYNC_WMIFLAG); } if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, ALL_BSS_FILTER, 0) != 0) { ath6kl_err("couldn't set bss filtering\n"); up(&ar->sem); return -EIO; } } vif->nw_type = vif->next_mode; /* enable enhanced bmiss detection if applicable */ ath6kl_cfg80211_sta_bmiss_enhance(vif, true); if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) nw_subtype = SUBTYPE_P2PCLIENT; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: connect called with authmode %d dot11 auth %d" " PW crypto %d PW crypto len %d GRP crypto %d" " GRP crypto len %d channel hint %u\n", __func__, vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto, vif->prwise_crypto_len, vif->grp_crypto, vif->grp_crypto_len, vif->ch_hint); vif->reconnect_flag = 0; if (vif->nw_type == INFRA_NETWORK) { interval = max_t(u16, vif->listen_intvl_t, ATH6KL_MAX_WOW_LISTEN_INTL); status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, interval, 0); if (status) { ath6kl_err("couldn't set listen intervel\n"); up(&ar->sem); return status; } } status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type, vif->dot11_auth_mode, vif->auth_mode, vif->prwise_crypto, vif->prwise_crypto_len, vif->grp_crypto, vif->grp_crypto_len, vif->ssid_len, vif->ssid, vif->req_bssid, vif->ch_hint, ar->connect_ctrl_flags, nw_subtype); /* disable background scan if period is 0 */ if (sme->bg_scan_period == 0) sme->bg_scan_period = 0xffff; /* configure default value if not specified */ if (sme->bg_scan_period == -1) sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); up(&ar->sem); if (status == -EINVAL) { memset(vif->ssid, 0, sizeof(vif->ssid)); vif->ssid_len = 0; ath6kl_err("invalid request\n"); return -ENOENT; } else if (status) { ath6kl_err("ath6kl_wmi_connect_cmd failed\n"); return -EIO; } if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) && ((vif->auth_mode == WPA_PSK_AUTH) || (vif->auth_mode == WPA2_PSK_AUTH))) { mod_timer(&vif->disconnect_timer, jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL)); } ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD; set_bit(CONNECT_PEND, &vif->flags); return 0; } static struct cfg80211_bss * ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, enum network_type nw_type, const u8 *bssid, struct ieee80211_channel *chan, const u8 *beacon_ie, size_t beacon_ie_len) { struct ath6kl *ar = vif->ar; struct cfg80211_bss *bss; u16 cap_mask, cap_val; u8 *ie; if (nw_type & ADHOC_NETWORK) { cap_mask = WLAN_CAPABILITY_IBSS; cap_val = WLAN_CAPABILITY_IBSS; } else { cap_mask = WLAN_CAPABILITY_ESS; cap_val = WLAN_CAPABILITY_ESS; } bss = cfg80211_get_bss(ar->wiphy, chan, bssid, vif->ssid, vif->ssid_len, cap_mask, cap_val); if (bss == NULL) { /* * Since cfg80211 may not yet know about the BSS, * generate a partial entry until the first BSS info * event becomes available. * * Prepend SSID element since it is not included in the Beacon * IEs from the target. */ ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL); if (ie == NULL) return NULL; ie[0] = WLAN_EID_SSID; ie[1] = vif->ssid_len; memcpy(ie + 2, vif->ssid, vif->ssid_len); memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len); bss = cfg80211_inform_bss(ar->wiphy, chan, bssid, 0, cap_val, 100, ie, 2 + vif->ssid_len + beacon_ie_len, 0, GFP_KERNEL); if (bss) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to cfg80211\n", bssid); kfree(ie); } else ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n"); return bss; } void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_intvl, u16 beacon_intvl, enum network_type nw_type, u8 beacon_ie_len, u8 assoc_req_len, u8 assoc_resp_len, u8 *assoc_info) { struct ieee80211_channel *chan; struct ath6kl *ar = vif->ar; struct cfg80211_bss *bss; /* capinfo + listen interval */ u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); /* capinfo + status code + associd */ u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16); u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset; u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len + assoc_resp_ie_offset; assoc_req_len -= assoc_req_ie_offset; assoc_resp_len -= assoc_resp_ie_offset; /* * Store Beacon interval here; DTIM period will be available only once * a Beacon frame from the AP is seen. */ vif->assoc_bss_beacon_int = beacon_intvl; clear_bit(DTIM_PERIOD_AVAIL, &vif->flags); if (nw_type & ADHOC_NETWORK) { if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in ibss mode\n", __func__); return; } } if (nw_type & INFRA_NETWORK) { if (vif->wdev.iftype != NL80211_IFTYPE_STATION && vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in station mode\n", __func__); return; } } chan = ieee80211_get_channel(ar->wiphy, (int) channel); bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info, beacon_ie_len); if (!bss) { ath6kl_err("could not add cfg80211 bss entry\n"); return; } if (nw_type & ADHOC_NETWORK) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n", nw_type & ADHOC_CREATOR ? "creator" : "joiner"); cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); cfg80211_put_bss(bss); return; } if (vif->sme_state == SME_CONNECTING) { /* inform connect result to cfg80211 */ vif->sme_state = SME_CONNECTED; cfg80211_connect_result(vif->ndev, bssid, assoc_req_ie, assoc_req_len, assoc_resp_ie, assoc_resp_len, WLAN_STATUS_SUCCESS, GFP_KERNEL); cfg80211_put_bss(bss); } else if (vif->sme_state == SME_CONNECTED) { /* inform roam event to cfg80211 */ cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len, assoc_resp_ie, assoc_resp_len, GFP_KERNEL); } } static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__, reason_code); ath6kl_cfg80211_sscan_disable(vif); if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { ath6kl_err("busy, destroy in progress\n"); return -EBUSY; } if (down_interruptible(&ar->sem)) { ath6kl_err("busy, couldn't get access\n"); return -ERESTARTSYS; } vif->reconnect_flag = 0; ath6kl_disconnect(vif); memset(vif->ssid, 0, sizeof(vif->ssid)); vif->ssid_len = 0; if (!test_bit(SKIP_SCAN, &ar->flag)) memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); up(&ar->sem); vif->sme_state = SME_DISCONNECTED; return 0; } void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 proto_reason) { struct ath6kl *ar = vif->ar; if (vif->scan_req) { cfg80211_scan_done(vif->scan_req, true); vif->scan_req = NULL; } if (vif->nw_type & ADHOC_NETWORK) { if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in ibss mode\n", __func__); return; } memset(bssid, 0, ETH_ALEN); cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); return; } if (vif->nw_type & INFRA_NETWORK) { if (vif->wdev.iftype != NL80211_IFTYPE_STATION && vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in station mode\n", __func__); return; } } clear_bit(CONNECT_PEND, &vif->flags); if (vif->sme_state == SME_CONNECTING) { cfg80211_connect_result(vif->ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); } else if (vif->sme_state == SME_CONNECTED) { cfg80211_disconnected(vif->ndev, proto_reason, NULL, 0, GFP_KERNEL); } vif->sme_state = SME_DISCONNECTED; /* * Send a disconnect command to target when a disconnect event is * received with reason code other than 3 (DISCONNECT_CMD - disconnect * request from host) to make the firmware stop trying to connect even * after giving disconnect event. There will be one more disconnect * event for this disconnect command with reason code DISCONNECT_CMD * which won't be notified to cfg80211. */ if (reason != DISCONNECT_CMD) ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); } static int ath6kl_set_probed_ssids(struct ath6kl *ar, struct ath6kl_vif *vif, struct cfg80211_ssid *ssids, int n_ssids, struct cfg80211_match_set *match_set, int n_match_ssid) { u8 i, j, index_to_add, ssid_found = false; struct ath6kl_cfg80211_match_probe_ssid ssid_list[MAX_PROBED_SSIDS]; memset(ssid_list, 0, sizeof(ssid_list)); if (n_ssids > MAX_PROBED_SSIDS || n_match_ssid > MAX_PROBED_SSIDS) return -EINVAL; for (i = 0; i < n_ssids; i++) { memcpy(ssid_list[i].ssid.ssid, ssids[i].ssid, ssids[i].ssid_len); ssid_list[i].ssid.ssid_len = ssids[i].ssid_len; if (ssids[i].ssid_len) ssid_list[i].flag = SPECIFIC_SSID_FLAG; else ssid_list[i].flag = ANY_SSID_FLAG; if (n_match_ssid == 0) ssid_list[i].flag |= MATCH_SSID_FLAG; } index_to_add = i; for (i = 0; i < n_match_ssid; i++) { ssid_found = false; for (j = 0; j < n_ssids; j++) { if ((match_set[i].ssid.ssid_len == ssid_list[j].ssid.ssid_len) && (!memcmp(ssid_list[j].ssid.ssid, match_set[i].ssid.ssid, match_set[i].ssid.ssid_len))) { ssid_list[j].flag |= MATCH_SSID_FLAG; ssid_found = true; break; } } if (ssid_found) continue; if (index_to_add >= MAX_PROBED_SSIDS) continue; ssid_list[index_to_add].ssid.ssid_len = match_set[i].ssid.ssid_len; memcpy(ssid_list[index_to_add].ssid.ssid, match_set[i].ssid.ssid, match_set[i].ssid.ssid_len); ssid_list[index_to_add].flag |= MATCH_SSID_FLAG; index_to_add++; } for (i = 0; i < index_to_add; i++) { ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, ssid_list[i].flag, ssid_list[i].ssid.ssid_len, ssid_list[i].ssid.ssid); } /* Make sure no old entries are left behind */ for (i = index_to_add; i < MAX_PROBED_SSIDS; i++) { ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, DISABLE_SSID_FLAG, 0, NULL); } return 0; } static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(request->wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); s8 n_channels = 0; u16 *channels = NULL; int ret = 0; u32 force_fg_scan = 0; if (!ath6kl_cfg80211_ready(vif)) return -EIO; ath6kl_cfg80211_sscan_disable(vif); if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, ALL_BSS_FILTER, 0); if (ret) { ath6kl_err("couldn't set bss filtering\n"); return ret; } } ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, request->n_ssids, NULL, 0); if (ret < 0) return ret; /* this also clears IE in fw if it's not set */ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_REQ, request->ie, request->ie_len); if (ret) { ath6kl_err("failed to set Probe Request appie for scan\n"); return ret; } /* * Scan only the requested channels if the request specifies a set of * channels. If the list is longer than the target supports, do not * configure the list and instead, scan all available channels. */ if (request->n_channels > 0 && request->n_channels <= WMI_MAX_CHANNELS) { u8 i; n_channels = request->n_channels; channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); if (channels == NULL) { ath6kl_warn("failed to set scan channels, scan all channels"); n_channels = 0; } for (i = 0; i < n_channels; i++) channels[i] = request->channels[i]->center_freq; } if (test_bit(CONNECTED, &vif->flags)) force_fg_scan = 1; vif->scan_req = request; if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, ar->fw_capabilities)) { /* * If capable of doing P2P mgmt operations using * station interface, send additional information like * supported rates to advertise and xmit rates for * probe requests */ ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN, force_fg_scan, false, 0, ATH6KL_FG_SCAN_INTERVAL, n_channels, channels, request->no_cck, request->rates); } else { ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN, force_fg_scan, false, 0, ATH6KL_FG_SCAN_INTERVAL, n_channels, channels); } if (ret) { ath6kl_err("wmi_startscan_cmd failed\n"); vif->scan_req = NULL; } kfree(channels); return ret; } void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted) { struct ath6kl *ar = vif->ar; int i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__, aborted ? " aborted" : ""); if (!vif->scan_req) return; if (aborted) goto out; if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) { for (i = 0; i < vif->scan_req->n_ssids; i++) { ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i + 1, DISABLE_SSID_FLAG, 0, NULL); } } out: cfg80211_scan_done(vif->scan_req, aborted); vif->scan_req = NULL; } void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, enum wmi_phy_mode mode) { enum nl80211_channel_type type; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "channel switch notify nw_type %d freq %d mode %d\n", vif->nw_type, freq, mode); type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; cfg80211_ch_switch_notify(vif->ndev, freq, type); } static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct ath6kl *ar = ath6kl_priv(ndev); struct ath6kl_vif *vif = netdev_priv(ndev); struct ath6kl_key *key = NULL; int seq_len; u8 key_usage; u8 key_type; if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (params->cipher == CCKM_KRK_CIPHER_SUITE) { if (params->key_len != WMI_KRK_LEN) return -EINVAL; return ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx, params->key); } if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } key = &vif->keys[key_index]; memset(key, 0, sizeof(struct ath6kl_key)); if (pairwise) key_usage = PAIRWISE_USAGE; else key_usage = GROUP_USAGE; seq_len = params->seq_len; if (params->cipher == WLAN_CIPHER_SUITE_SMS4 && seq_len > ATH6KL_KEY_SEQ_LEN) { /* Only first half of the WPI PN is configured */ seq_len = ATH6KL_KEY_SEQ_LEN; } if (params->key_len > WLAN_MAX_KEY_LEN || seq_len > sizeof(key->seq)) return -EINVAL; key->key_len = params->key_len; memcpy(key->key, params->key, key->key_len); key->seq_len = seq_len; memcpy(key->seq, params->seq, key->seq_len); key->cipher = params->cipher; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: key_type = WEP_CRYPT; break; case WLAN_CIPHER_SUITE_TKIP: key_type = TKIP_CRYPT; break; case WLAN_CIPHER_SUITE_CCMP: key_type = AES_CRYPT; break; case WLAN_CIPHER_SUITE_SMS4: key_type = WAPI_CRYPT; break; default: return -ENOTSUPP; } if (((vif->auth_mode == WPA_PSK_AUTH) || (vif->auth_mode == WPA2_PSK_AUTH)) && (key_usage & GROUP_USAGE)) del_timer(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n", __func__, key_index, key->key_len, key_type, key_usage, key->seq_len); if (vif->nw_type == AP_NETWORK && !pairwise && (key_type == TKIP_CRYPT || key_type == AES_CRYPT || key_type == WAPI_CRYPT)) { ar->ap_mode_bkey.valid = true; ar->ap_mode_bkey.key_index = key_index; ar->ap_mode_bkey.key_type = key_type; ar->ap_mode_bkey.key_len = key->key_len; memcpy(ar->ap_mode_bkey.key, key->key, key->key_len); if (!test_bit(CONNECTED, &vif->flags)) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group key configuration until AP mode has been started\n"); /* * The key will be set in ath6kl_connect_ap_mode() once * the connected event is received from the target. */ return 0; } } if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT && !test_bit(CONNECTED, &vif->flags)) { /* * Store the key locally so that it can be re-configured after * the AP mode has properly started * (ath6kl_install_statioc_wep_keys). */ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration until AP mode has been started\n"); vif->wep_key_list[key_index].key_len = key->key_len; memcpy(vif->wep_key_list[key_index].key, key->key, key->key_len); return 0; } return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index, key_type, key_usage, key->key_len, key->seq, key->seq_len, key->key, KEY_OP_INIT_VAL, (u8 *) mac_addr, SYNC_BOTH_WMIFLAG); } static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr) { struct ath6kl *ar = ath6kl_priv(ndev); struct ath6kl_vif *vif = netdev_priv(ndev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } if (!vif->keys[key_index].key_len) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d is empty\n", __func__, key_index); return 0; } vif->keys[key_index].key_len = 0; return ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index); } static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback) (void *cookie, struct key_params *)) { struct ath6kl_vif *vif = netdev_priv(ndev); struct ath6kl_key *key = NULL; struct key_params params; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } key = &vif->keys[key_index]; memset(¶ms, 0, sizeof(params)); params.cipher = key->cipher; params.key_len = key->key_len; params.seq_len = key->seq_len; params.seq = key->seq; params.key = key->key; callback(cookie, ¶ms); return key->key_len ? 0 : -ENOENT; } static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool unicast, bool multicast) { struct ath6kl *ar = ath6kl_priv(ndev); struct ath6kl_vif *vif = netdev_priv(ndev); struct ath6kl_key *key = NULL; u8 key_usage; enum crypto_type key_type = NONE_CRYPT; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } if (!vif->keys[key_index].key_len) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n", __func__, key_index); return -EINVAL; } vif->def_txkey_index = key_index; key = &vif->keys[vif->def_txkey_index]; key_usage = GROUP_USAGE; if (vif->prwise_crypto == WEP_CRYPT) key_usage |= TX_USAGE; if (unicast) key_type = vif->prwise_crypto; if (multicast) key_type = vif->grp_crypto; if (vif->next_mode == AP_NETWORK && !test_bit(CONNECTED, &vif->flags)) return 0; /* Delay until AP mode has been started */ return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, vif->def_txkey_index, key_type, key_usage, key->key_len, key->seq, key->seq_len, key->key, KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); } void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast); cfg80211_michael_mic_failure(vif->ndev, vif->bssid, (ismcast ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE), keyid, NULL, GFP_KERNEL); } static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl_vif *vif; int ret; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__, changed); vif = ath6kl_vif_first(ar); if (!vif) return -EIO; if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (changed & WIPHY_PARAM_RTS_THRESHOLD) { ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold); if (ret != 0) { ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n"); return -EIO; } } return 0; } /* * The type nl80211_tx_power_setting replaces the following * data type from 2.6.36 onwards */ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl_vif *vif; int dbm = MBM_TO_DBM(mbm); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__, type, dbm); vif = ath6kl_vif_first(ar); if (!vif) return -EIO; if (!ath6kl_cfg80211_ready(vif)) return -EIO; switch (type) { case NL80211_TX_POWER_AUTOMATIC: return 0; case NL80211_TX_POWER_LIMITED: ar->tx_pwr = dbm; break; default: ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n", __func__, type); return -EOPNOTSUPP; } ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm); return 0; } static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (test_bit(CONNECTED, &vif->flags)) { ar->tx_pwr = 0; if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) { ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n"); return -EIO; } wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0, 5 * HZ); if (signal_pending(current)) { ath6kl_err("target did not respond\n"); return -EINTR; } } *dbm = ar->tx_pwr; return 0; } static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool pmgmt, int timeout) { struct ath6kl *ar = ath6kl_priv(dev); struct wmi_power_mode_cmd mode; struct ath6kl_vif *vif = netdev_priv(dev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n", __func__, pmgmt, timeout); if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (pmgmt) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__); mode.pwr_mode = REC_POWER; } else { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__); mode.pwr_mode = MAX_PERF_POWER; } if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx, mode.pwr_mode) != 0) { ath6kl_err("wmi_powermode_cmd failed\n"); return -EIO; } return 0; } static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct ath6kl *ar = wiphy_priv(wiphy); struct wireless_dev *wdev; u8 if_idx, nw_type; if (ar->num_vif == ar->vif_max) { ath6kl_err("Reached maximum number of supported vif\n"); return ERR_PTR(-EINVAL); } if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) { ath6kl_err("Not a supported interface type\n"); return ERR_PTR(-EINVAL); } wdev = ath6kl_interface_add(ar, name, type, if_idx, nw_type); if (!wdev) return ERR_PTR(-ENOMEM); ar->num_vif++; return wdev; } static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) { struct ath6kl *ar = wiphy_priv(wiphy); struct ath6kl_vif *vif = netdev_priv(wdev->netdev); spin_lock_bh(&ar->list_lock); list_del(&vif->list); spin_unlock_bh(&ar->list_lock); ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); ath6kl_cfg80211_vif_cleanup(vif); return 0; } static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct ath6kl_vif *vif = netdev_priv(ndev); int i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); /* * Don't bring up p2p on an interface which is not initialized * for p2p operation where fw does not have capability to switch * dynamically between non-p2p and p2p type interface. */ if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, vif->ar->fw_capabilities) && (type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO)) { if (vif->ar->vif_max == 1) { if (vif->fw_vif_idx != 0) return -EINVAL; else goto set_iface_type; } for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) { if (i == vif->fw_vif_idx) break; } if (i == vif->ar->vif_max) { ath6kl_err("Invalid interface to bring up P2P\n"); return -EINVAL; } } /* need to clean up enhanced bmiss detection fw state */ ath6kl_cfg80211_sta_bmiss_enhance(vif, false); set_iface_type: switch (type) { case NL80211_IFTYPE_STATION: vif->next_mode = INFRA_NETWORK; break; case NL80211_IFTYPE_ADHOC: vif->next_mode = ADHOC_NETWORK; break; case NL80211_IFTYPE_AP: vif->next_mode = AP_NETWORK; break; case NL80211_IFTYPE_P2P_CLIENT: vif->next_mode = INFRA_NETWORK; break; case NL80211_IFTYPE_P2P_GO: vif->next_mode = AP_NETWORK; break; default: ath6kl_err("invalid interface type %u\n", type); return -EOPNOTSUPP; } vif->wdev.iftype = type; return 0; } static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *ibss_param) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); int status; if (!ath6kl_cfg80211_ready(vif)) return -EIO; vif->ssid_len = ibss_param->ssid_len; memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len); if (ibss_param->channel) vif->ch_hint = ibss_param->channel->center_freq; if (ibss_param->channel_fixed) { /* * TODO: channel_fixed: The channel should be fixed, do not * search for IBSSs to join on other channels. Target * firmware does not support this feature, needs to be * updated. */ return -EOPNOTSUPP; } memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid)) memcpy(vif->req_bssid, ibss_param->bssid, sizeof(vif->req_bssid)); ath6kl_set_wpa_version(vif, 0); status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM); if (status) return status; if (ibss_param->privacy) { ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true); ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false); } else { ath6kl_set_cipher(vif, 0, true); ath6kl_set_cipher(vif, 0, false); } vif->nw_type = vif->next_mode; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: connect called with authmode %d dot11 auth %d" " PW crypto %d PW crypto len %d GRP crypto %d" " GRP crypto len %d channel hint %u\n", __func__, vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto, vif->prwise_crypto_len, vif->grp_crypto, vif->grp_crypto_len, vif->ch_hint); status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type, vif->dot11_auth_mode, vif->auth_mode, vif->prwise_crypto, vif->prwise_crypto_len, vif->grp_crypto, vif->grp_crypto_len, vif->ssid_len, vif->ssid, vif->req_bssid, vif->ch_hint, ar->connect_ctrl_flags, SUBTYPE_NONE); set_bit(CONNECT_PEND, &vif->flags); return 0; } static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl_vif *vif = netdev_priv(dev); if (!ath6kl_cfg80211_ready(vif)) return -EIO; ath6kl_disconnect(vif); memset(vif->ssid, 0, sizeof(vif->ssid)); vif->ssid_len = 0; return 0; } static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, CCKM_KRK_CIPHER_SUITE, WLAN_CIPHER_SUITE_SMS4, }; static bool is_rate_legacy(s32 rate) { static const s32 legacy[] = { 1000, 2000, 5500, 11000, 6000, 9000, 12000, 18000, 24000, 36000, 48000, 54000 }; u8 i; for (i = 0; i < ARRAY_SIZE(legacy); i++) if (rate == legacy[i]) return true; return false; } static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi) { static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000, 52000, 58500, 65000, 72200 }; u8 i; for (i = 0; i < ARRAY_SIZE(ht20); i++) { if (rate == ht20[i]) { if (i == ARRAY_SIZE(ht20) - 1) /* last rate uses sgi */ *sgi = true; else *sgi = false; *mcs = i; return true; } } return false; } static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) { static const s32 ht40[] = { 13500, 27000, 40500, 54000, 81000, 108000, 121500, 135000, 150000 }; u8 i; for (i = 0; i < ARRAY_SIZE(ht40); i++) { if (rate == ht40[i]) { if (i == ARRAY_SIZE(ht40) - 1) /* last rate uses sgi */ *sgi = true; else *sgi = false; *mcs = i; return true; } } return false; } static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); long left; bool sgi; s32 rate; int ret; u8 mcs; if (memcmp(mac, vif->bssid, ETH_ALEN) != 0) return -ENOENT; if (down_interruptible(&ar->sem)) return -EBUSY; set_bit(STATS_UPDATE_PEND, &vif->flags); ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx); if (ret != 0) { up(&ar->sem); return -EIO; } left = wait_event_interruptible_timeout(ar->event_wq, !test_bit(STATS_UPDATE_PEND, &vif->flags), WMI_TIMEOUT); up(&ar->sem); if (left == 0) return -ETIMEDOUT; else if (left < 0) return left; if (vif->target_stats.rx_byte) { sinfo->rx_bytes = vif->target_stats.rx_byte; sinfo->filled |= STATION_INFO_RX_BYTES; sinfo->rx_packets = vif->target_stats.rx_pkt; sinfo->filled |= STATION_INFO_RX_PACKETS; } if (vif->target_stats.tx_byte) { sinfo->tx_bytes = vif->target_stats.tx_byte; sinfo->filled |= STATION_INFO_TX_BYTES; sinfo->tx_packets = vif->target_stats.tx_pkt; sinfo->filled |= STATION_INFO_TX_PACKETS; } sinfo->signal = vif->target_stats.cs_rssi; sinfo->filled |= STATION_INFO_SIGNAL; rate = vif->target_stats.tx_ucast_rate; if (is_rate_legacy(rate)) { sinfo->txrate.legacy = rate / 100; } else if (is_rate_ht20(rate, &mcs, &sgi)) { if (sgi) { sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; sinfo->txrate.mcs = mcs - 1; } else { sinfo->txrate.mcs = mcs; } sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; } else if (is_rate_ht40(rate, &mcs, &sgi)) { if (sgi) { sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; sinfo->txrate.mcs = mcs - 1; } else { sinfo->txrate.mcs = mcs; } sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; } else { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "invalid rate from stats: %d\n", rate); ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE); return 0; } sinfo->filled |= STATION_INFO_TX_BITRATE; if (test_bit(CONNECTED, &vif->flags) && test_bit(DTIM_PERIOD_AVAIL, &vif->flags) && vif->nw_type == INFRA_NETWORK) { sinfo->filled |= STATION_INFO_BSS_PARAM; sinfo->bss_param.flags = 0; sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period; sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int; } return 0; } static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa) { struct ath6kl *ar = ath6kl_priv(netdev); struct ath6kl_vif *vif = netdev_priv(netdev); return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid, pmksa->pmkid, true); } static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa) { struct ath6kl *ar = ath6kl_priv(netdev); struct ath6kl_vif *vif = netdev_priv(netdev); return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid, pmksa->pmkid, false); } static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) { struct ath6kl *ar = ath6kl_priv(netdev); struct ath6kl_vif *vif = netdev_priv(netdev); if (test_bit(CONNECTED, &vif->flags)) return ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, vif->bssid, NULL, false); return 0; } static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif, struct cfg80211_wowlan *wow, u32 *filter) { int ret, pos; u8 mask[WOW_MASK_SIZE]; u16 i; /* Configure the patterns that we received from the user. */ for (i = 0; i < wow->n_patterns; i++) { /* * Convert given nl80211 specific mask value to equivalent * driver specific mask value and send it to the chip along * with patterns. For example, If the mask value defined in * struct cfg80211_wowlan is 0xA (equivalent binary is 1010), * then equivalent driver specific mask value is * "0xFF 0x00 0xFF 0x00". */ memset(&mask, 0, sizeof(mask)); for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) { if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8))) mask[pos] = 0xFF; } /* * Note: Pattern's offset is not passed as part of wowlan * parameter from CFG layer. So it's always passed as ZERO * to the firmware. It means, given WOW patterns are always * matched from the first byte of received pkt in the firmware. */ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, wow->patterns[i].pattern_len, 0 /* pattern offset */, wow->patterns[i].pattern, mask); if (ret) return ret; } if (wow->disconnect) *filter |= WOW_FILTER_OPTION_NWK_DISASSOC; if (wow->magic_pkt) *filter |= WOW_FILTER_OPTION_MAGIC_PACKET; if (wow->gtk_rekey_failure) *filter |= WOW_FILTER_OPTION_GTK_ERROR; if (wow->eap_identity_req) *filter |= WOW_FILTER_OPTION_EAP_REQ; if (wow->four_way_handshake) *filter |= WOW_FILTER_OPTION_8021X_4WAYHS; return 0; } static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif) { static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; static const u8 unicst_mask[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f }; u8 unicst_offset = 0; static const u8 arp_pattern[] = { 0x08, 0x06 }; static const u8 arp_mask[] = { 0xff, 0xff }; u8 arp_offset = 20; static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 }; static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 }; u8 discvr_offset = 38; static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ }; static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ }; u8 dhcp_offset = 0; int ret; /* Setup unicast IP, EAPOL-like and ARP pkt pattern */ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, sizeof(unicst_pattern), unicst_offset, unicst_pattern, unicst_mask); if (ret) { ath6kl_err("failed to add WOW unicast IP pattern\n"); return ret; } /* Setup all ARP pkt pattern */ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, sizeof(arp_pattern), arp_offset, arp_pattern, arp_mask); if (ret) { ath6kl_err("failed to add WOW ARP pattern\n"); return ret; } /* * Setup multicast pattern for mDNS 224.0.0.251, * SSDP 239.255.255.250 and LLMNR 224.0.0.252 */ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, sizeof(discvr_pattern), discvr_offset, discvr_pattern, discvr_mask); if (ret) { ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n"); return ret; } /* Setup all DHCP broadcast pkt pattern */ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, sizeof(dhcp_pattern), dhcp_offset, dhcp_pattern, dhcp_mask); if (ret) { ath6kl_err("failed to add WOW DHCP broadcast pattern\n"); return ret; } return 0; } static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) { struct net_device *ndev = vif->ndev; static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 }; static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 }; u8 discvr_offset = 38; u8 mac_mask[ETH_ALEN]; int ret; /* Setup unicast pkt pattern */ memset(mac_mask, 0xff, ETH_ALEN); ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, ETH_ALEN, 0, ndev->dev_addr, mac_mask); if (ret) { ath6kl_err("failed to add WOW unicast pattern\n"); return ret; } /* * Setup multicast pattern for mDNS 224.0.0.251, * SSDP 239.255.255.250 and LLMNR 224.0.0.252 */ if ((ndev->flags & IFF_ALLMULTI) || (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) { ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, sizeof(discvr_pattern), discvr_offset, discvr_pattern, discvr_mask); if (ret) { ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n"); return ret; } } return 0; } static int is_hsleep_mode_procsed(struct ath6kl_vif *vif) { return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); } static bool is_ctrl_ep_empty(struct ath6kl *ar) { return !ar->tx_pending[ar->ctrl_ep]; } static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif) { int ret, left; clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_ASLEEP); if (ret) return ret; left = wait_event_interruptible_timeout(ar->event_wq, is_hsleep_mode_procsed(vif), WMI_TIMEOUT); if (left == 0) { ath6kl_warn("timeout, didn't get host sleep cmd processed event\n"); ret = -ETIMEDOUT; } else if (left < 0) { ath6kl_warn("error while waiting for host sleep cmd processed event %d\n", left); ret = left; } if (ar->tx_pending[ar->ctrl_ep]) { left = wait_event_interruptible_timeout(ar->event_wq, is_ctrl_ep_empty(ar), WMI_TIMEOUT); if (left == 0) { ath6kl_warn("clear wmi ctrl data timeout\n"); ret = -ETIMEDOUT; } else if (left < 0) { ath6kl_warn("clear wmi ctrl data failed: %d\n", left); ret = left; } } return ret; } static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) { struct in_device *in_dev; struct in_ifaddr *ifa; struct ath6kl_vif *vif; int ret; u32 filter = 0; u16 i, bmiss_time; u8 index = 0; __be32 ips[MAX_IP_ADDRS]; /* The FW currently can't support multi-vif WoW properly. */ if (ar->num_vif > 1) return -EIO; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (!test_bit(CONNECTED, &vif->flags)) return -ENOTCONN; if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) return -EINVAL; if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) && test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, ar->fw_capabilities)) { ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, false); if (ret) return ret; } /* Clear existing WOW patterns */ for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, i); /* * Skip the default WOW pattern configuration * if the driver receives any WOW patterns from * the user. */ if (wow) ret = ath6kl_wow_usr(ar, vif, wow, &filter); else if (vif->nw_type == AP_NETWORK) ret = ath6kl_wow_ap(ar, vif); else ret = ath6kl_wow_sta(ar, vif); if (ret) return ret; netif_stop_queue(vif->ndev); if (vif->nw_type != AP_NETWORK) { ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_MAX_WOW_LISTEN_INTL, 0); if (ret) return ret; /* Set listen interval x 15 times as bmiss time */ bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15; if (bmiss_time > ATH6KL_MAX_BMISS_TIME) bmiss_time = ATH6KL_MAX_BMISS_TIME; ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx, bmiss_time, 0); if (ret) return ret; ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0xFFFF, 0, 0, 0, 0, 0, 0, 0); if (ret) return ret; } ar->state = ATH6KL_STATE_SUSPENDING; /* Setup own IP addr for ARP agent. */ in_dev = __in_dev_get_rtnl(vif->ndev); if (!in_dev) goto skip_arp; ifa = in_dev->ifa_list; memset(&ips, 0, sizeof(ips)); /* Configure IP addr only if IP address count < MAX_IP_ADDRS */ while (index < MAX_IP_ADDRS && ifa) { ips[index] = ifa->ifa_local; ifa = ifa->ifa_next; index++; } if (ifa) { ath6kl_err("total IP addr count is exceeding fw limit\n"); return -EINVAL; } ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]); if (ret) { ath6kl_err("fail to setup ip for arp agent\n"); return ret; } skip_arp: ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_WOW_MODE_ENABLE, filter, WOW_HOST_REQ_DELAY); if (ret) return ret; ret = ath6kl_cfg80211_host_sleep(ar, vif); if (ret) return ret; return 0; } static int ath6kl_wow_resume(struct ath6kl *ar) { struct ath6kl_vif *vif; int ret; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; ar->state = ATH6KL_STATE_RESUMING; ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_AWAKE); if (ret) { ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n", ret); ar->state = ATH6KL_STATE_WOW; return ret; } if (vif->nw_type != AP_NETWORK) { ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); if (ret) return ret; ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); if (ret) return ret; ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx, vif->bmiss_time_t, 0); if (ret) return ret; } ar->state = ATH6KL_STATE_ON; if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) && test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, ar->fw_capabilities)) { ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, true); if (ret) return ret; } netif_wake_queue(vif->ndev); return 0; } static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar) { struct ath6kl_vif *vif; int ret; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; if (!test_bit(WMI_READY, &ar->flag)) { ath6kl_err("deepsleep failed as wmi is not ready\n"); return -EIO; } ath6kl_cfg80211_stop_all(ar); /* Save the current power mode before enabling power save */ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER); if (ret) return ret; /* Disable WOW mode */ ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_WOW_MODE_DISABLE, 0, 0); if (ret) return ret; /* Flush all non control pkts in TX path */ ath6kl_tx_data_cleanup(ar); ret = ath6kl_cfg80211_host_sleep(ar, vif); if (ret) return ret; return 0; } static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar) { struct ath6kl_vif *vif; int ret; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) { ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, ar->wmi->saved_pwr_mode); if (ret) return ret; } ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_AWAKE); if (ret) return ret; ar->state = ATH6KL_STATE_ON; /* Reset scan parameter to default values */ ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); if (ret) return ret; return 0; } int ath6kl_cfg80211_suspend(struct ath6kl *ar, enum ath6kl_cfg_suspend_mode mode, struct cfg80211_wowlan *wow) { struct ath6kl_vif *vif; enum ath6kl_state prev_state; int ret; switch (mode) { case ATH6KL_CFG_SUSPEND_WOW: ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n"); /* Flush all non control pkts in TX path */ ath6kl_tx_data_cleanup(ar); prev_state = ar->state; ret = ath6kl_wow_suspend(ar, wow); if (ret) { ar->state = prev_state; return ret; } ar->state = ATH6KL_STATE_WOW; break; case ATH6KL_CFG_SUSPEND_DEEPSLEEP: ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n"); ret = ath6kl_cfg80211_deepsleep_suspend(ar); if (ret) { ath6kl_err("deepsleep suspend failed: %d\n", ret); return ret; } ar->state = ATH6KL_STATE_DEEPSLEEP; break; case ATH6KL_CFG_SUSPEND_CUTPOWER: ath6kl_cfg80211_stop_all(ar); if (ar->state == ATH6KL_STATE_OFF) { ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend hw off, no action for cutpower\n"); break; } ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n"); ret = ath6kl_init_hw_stop(ar); if (ret) { ath6kl_warn("failed to stop hw during suspend: %d\n", ret); } ar->state = ATH6KL_STATE_CUTPOWER; break; case ATH6KL_CFG_SUSPEND_SCHED_SCAN: /* * Nothing needed for schedule scan, firmware is already in * wow mode and sleeping most of the time. */ break; default: break; } list_for_each_entry(vif, &ar->vif_list, list) ath6kl_cfg80211_scan_complete_event(vif, true); return 0; } EXPORT_SYMBOL(ath6kl_cfg80211_suspend); int ath6kl_cfg80211_resume(struct ath6kl *ar) { int ret; switch (ar->state) { case ATH6KL_STATE_WOW: ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n"); ret = ath6kl_wow_resume(ar); if (ret) { ath6kl_warn("wow mode resume failed: %d\n", ret); return ret; } break; case ATH6KL_STATE_DEEPSLEEP: ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n"); ret = ath6kl_cfg80211_deepsleep_resume(ar); if (ret) { ath6kl_warn("deep sleep resume failed: %d\n", ret); return ret; } break; case ATH6KL_STATE_CUTPOWER: ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n"); ret = ath6kl_init_hw_start(ar); if (ret) { ath6kl_warn("Failed to boot hw in resume: %d\n", ret); return ret; } break; case ATH6KL_STATE_SCHED_SCAN: break; default: break; } return 0; } EXPORT_SYMBOL(ath6kl_cfg80211_resume); #ifdef CONFIG_PM /* hif layer decides what suspend mode to use */ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) { struct ath6kl *ar = wiphy_priv(wiphy); return ath6kl_hif_suspend(ar, wow); } static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) { struct ath6kl *ar = wiphy_priv(wiphy); return ath6kl_hif_resume(ar); } /* * FIXME: WOW suspend mode is selected if the host sdio controller supports * both sdio irq wake up and keep power. The target pulls sdio data line to * wake up the host when WOW pattern matches. This causes sdio irq handler * is being called in the host side which internally hits ath6kl's RX path. * * Since sdio interrupt is not disabled, RX path executes even before * the host executes the actual resume operation from PM module. * * In the current scenario, WOW resume should happen before start processing * any data from the target. So It's required to perform WOW resume in RX path. * Ideally we should perform WOW resume only in the actual platform * resume path. This area needs bit rework to avoid WOW resume in RX path. * * ath6kl_check_wow_status() is called from ath6kl_rx(). */ void ath6kl_check_wow_status(struct ath6kl *ar) { if (ar->state == ATH6KL_STATE_SUSPENDING) return; if (ar->state == ATH6KL_STATE_WOW) ath6kl_cfg80211_resume(ar); } #else void ath6kl_check_wow_status(struct ath6kl *ar) { } #endif static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band, bool ht_enable) { struct ath6kl_htcap *htcap = &vif->htcap[band]; if (htcap->ht_enable == ht_enable) return 0; if (ht_enable) { /* Set default ht capabilities */ htcap->ht_enable = true; htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ? ath6kl_g_htcap : ath6kl_a_htcap; htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; } else /* Disable ht */ memset(htcap, 0, sizeof(*htcap)); return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx, band, htcap); } static int ath6kl_restore_htcap(struct ath6kl_vif *vif) { struct wiphy *wiphy = vif->ar->wiphy; int band, ret = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; ret = ath6kl_set_htcap(vif, band, wiphy->bands[band]->ht_cap.ht_supported); if (ret) return ret; } return ret; } static bool ath6kl_is_p2p_ie(const u8 *pos) { return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && pos[2] == 0x50 && pos[3] == 0x6f && pos[4] == 0x9a && pos[5] == 0x09; } static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif, const u8 *ies, size_t ies_len) { struct ath6kl *ar = vif->ar; const u8 *pos; u8 *buf = NULL; size_t len = 0; int ret; /* * Filter out P2P IE(s) since they will be included depending on * the Probe Request frame in ath6kl_send_go_probe_resp(). */ if (ies && ies_len) { buf = kmalloc(ies_len, GFP_KERNEL); if (buf == NULL) return -ENOMEM; pos = ies; while (pos + 1 < ies + ies_len) { if (pos + 2 + pos[1] > ies + ies_len) break; if (!ath6kl_is_p2p_ie(pos)) { memcpy(buf + len, pos, 2 + pos[1]); len += 2 + pos[1]; } pos += 2 + pos[1]; } } ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_RESP, buf, len); kfree(buf); return ret; } static int ath6kl_set_ies(struct ath6kl_vif *vif, struct cfg80211_beacon_data *info) { struct ath6kl *ar = vif->ar; int res; /* this also clears IE in fw if it's not set */ res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_BEACON, info->beacon_ies, info->beacon_ies_len); if (res) return res; /* this also clears IE in fw if it's not set */ res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies, info->proberesp_ies_len); if (res) return res; /* this also clears IE in fw if it's not set */ res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_ASSOC_RESP, info->assocresp_ies, info->assocresp_ies_len); if (res) return res; return 0; } void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable) { int err; if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag))) return; if (vif->nw_type != INFRA_NETWORK) return; if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, vif->ar->fw_capabilities)) return; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n", enable ? "enable" : "disable"); err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi, vif->fw_vif_idx, enable); if (err) ath6kl_err("failed to %s enhanced bmiss detection: %d\n", enable ? "enable" : "disable", err); } static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon, u8 *rsn_capab) { const u8 *rsn_ie; size_t rsn_ie_len; u16 cnt; if (!beacon->tail) return -EINVAL; rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len); if (!rsn_ie) return -EINVAL; rsn_ie_len = *(rsn_ie + 1); /* skip element id and length */ rsn_ie += 2; /* skip version */ if (rsn_ie_len < 2) return -EINVAL; rsn_ie += 2; rsn_ie_len -= 2; /* skip group cipher suite */ if (rsn_ie_len < 4) return 0; rsn_ie += 4; rsn_ie_len -= 4; /* skip pairwise cipher suite */ if (rsn_ie_len < 2) return 0; cnt = get_unaligned_le16(rsn_ie); rsn_ie += (2 + cnt * 4); rsn_ie_len -= (2 + cnt * 4); /* skip akm suite */ if (rsn_ie_len < 2) return 0; cnt = get_unaligned_le16(rsn_ie); rsn_ie += (2 + cnt * 4); rsn_ie_len -= (2 + cnt * 4); if (rsn_ie_len < 2) return 0; memcpy(rsn_capab, rsn_ie, 2); return 0; } static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *info) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); struct ieee80211_mgmt *mgmt; bool hidden = false; u8 *ies; int ies_len; struct wmi_connect_cmd p; int res; int i, ret; u16 rsn_capab = 0; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (vif->next_mode != AP_NETWORK) return -EOPNOTSUPP; res = ath6kl_set_ies(vif, &info->beacon); ar->ap_mode_bkey.valid = false; /* TODO: * info->interval */ ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx, info->dtim_period); /* ignore error, just print a warning and continue normally */ if (ret) ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret); if (info->beacon.head == NULL) return -EINVAL; mgmt = (struct ieee80211_mgmt *) info->beacon.head; ies = mgmt->u.beacon.variable; if (ies > info->beacon.head + info->beacon.head_len) return -EINVAL; ies_len = info->beacon.head + info->beacon.head_len - ies; if (info->ssid == NULL) return -EINVAL; memcpy(vif->ssid, info->ssid, info->ssid_len); vif->ssid_len = info->ssid_len; if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) hidden = true; res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden); if (res) return res; ret = ath6kl_set_auth_type(vif, info->auth_type); if (ret) return ret; memset(&p, 0, sizeof(p)); for (i = 0; i < info->crypto.n_akm_suites; i++) { switch (info->crypto.akm_suites[i]) { case WLAN_AKM_SUITE_8021X: if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) p.auth_mode |= WPA_AUTH; if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) p.auth_mode |= WPA2_AUTH; break; case WLAN_AKM_SUITE_PSK: if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) p.auth_mode |= WPA_PSK_AUTH; if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) p.auth_mode |= WPA2_PSK_AUTH; break; } } if (p.auth_mode == 0) p.auth_mode = NONE_AUTH; vif->auth_mode = p.auth_mode; for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) { switch (info->crypto.ciphers_pairwise[i]) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: p.prwise_crypto_type |= WEP_CRYPT; break; case WLAN_CIPHER_SUITE_TKIP: p.prwise_crypto_type |= TKIP_CRYPT; break; case WLAN_CIPHER_SUITE_CCMP: p.prwise_crypto_type |= AES_CRYPT; break; case WLAN_CIPHER_SUITE_SMS4: p.prwise_crypto_type |= WAPI_CRYPT; break; } } if (p.prwise_crypto_type == 0) { p.prwise_crypto_type = NONE_CRYPT; ath6kl_set_cipher(vif, 0, true); } else if (info->crypto.n_ciphers_pairwise == 1) ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true); switch (info->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: p.grp_crypto_type = WEP_CRYPT; break; case WLAN_CIPHER_SUITE_TKIP: p.grp_crypto_type = TKIP_CRYPT; break; case WLAN_CIPHER_SUITE_CCMP: p.grp_crypto_type = AES_CRYPT; break; case WLAN_CIPHER_SUITE_SMS4: p.grp_crypto_type = WAPI_CRYPT; break; default: p.grp_crypto_type = NONE_CRYPT; break; } ath6kl_set_cipher(vif, info->crypto.cipher_group, false); p.nw_type = AP_NETWORK; vif->nw_type = vif->next_mode; p.ssid_len = vif->ssid_len; memcpy(p.ssid, vif->ssid, vif->ssid_len); p.dot11_auth_mode = vif->dot11_auth_mode; p.ch = cpu_to_le16(info->channel->center_freq); /* Enable uAPSD support by default */ res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true); if (res < 0) return res; if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { p.nw_subtype = SUBTYPE_P2PGO; } else { /* * Due to firmware limitation, it is not possible to * do P2P mgmt operations in AP mode */ p.nw_subtype = SUBTYPE_NONE; } if (info->inactivity_timeout) { res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx, info->inactivity_timeout); if (res < 0) return res; } if (ath6kl_set_htcap(vif, info->channel->band, info->channel_type != NL80211_CHAN_NO_HT)) return -EIO; /* * Get the PTKSA replay counter in the RSN IE. Supplicant * will use the RSN IE in M3 message and firmware has to * advertise the same in beacon/probe response. Send * the complete RSN IE capability field to firmware */ if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) && test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, ar->fw_capabilities)) { res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, WLAN_EID_RSN, WMI_RSN_IE_CAPB, (const u8 *) &rsn_capab, sizeof(rsn_capab)); if (res < 0) return res; } memcpy(&vif->profile, &p, sizeof(p)); res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); if (res < 0) return res; return 0; } static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_beacon_data *beacon) { struct ath6kl_vif *vif = netdev_priv(dev); if (!ath6kl_cfg80211_ready(vif)) return -EIO; if (vif->next_mode != AP_NETWORK) return -EOPNOTSUPP; return ath6kl_set_ies(vif, beacon); } static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); if (vif->nw_type != AP_NETWORK) return -EOPNOTSUPP; if (!test_bit(CONNECTED, &vif->flags)) return -ENOTCONN; ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); clear_bit(CONNECTED, &vif->flags); /* Restore ht setting in firmware */ return ath6kl_restore_htcap(vif); } static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); const u8 *addr = mac ? mac : bcast_addr; return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); if (vif->nw_type != AP_NETWORK) return -EOPNOTSUPP; /* Use this only for authorizing/unauthorizing a station */ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) return -EOPNOTSUPP; if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_MLME_AUTHORIZE, mac, 0); return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_MLME_UNAUTHORIZE, mac, 0); } static int ath6kl_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); u32 id; /* TODO: if already pending or ongoing remain-on-channel, * return -EBUSY */ id = ++vif->last_roc_id; if (id == 0) { /* Do not use 0 as the cookie value */ id = ++vif->last_roc_id; } *cookie = id; return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx, chan->center_freq, duration); } static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); if (cookie != vif->last_roc_id) return -ENOENT; vif->last_cancel_roc_id = cookie; return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx); } static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif, const u8 *buf, size_t len, unsigned int freq) { struct ath6kl *ar = vif->ar; const u8 *pos; u8 *p2p; int p2p_len; int ret; const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; /* Include P2P IE(s) from the frame generated in user space. */ p2p = kmalloc(len, GFP_KERNEL); if (p2p == NULL) return -ENOMEM; p2p_len = 0; pos = mgmt->u.probe_resp.variable; while (pos + 1 < buf + len) { if (pos + 2 + pos[1] > buf + len) break; if (ath6kl_is_p2p_ie(pos)) { memcpy(p2p + p2p_len, pos, 2 + pos[1]); p2p_len += 2 + pos[1]; } pos += 2 + pos[1]; } ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, vif->fw_vif_idx, freq, mgmt->da, p2p, p2p_len); kfree(p2p); return ret; } static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, u32 id, u32 freq, u32 wait, const u8 *buf, size_t len, bool *more_data, bool no_cck) { struct ieee80211_mgmt *mgmt; struct ath6kl_sta *conn; bool is_psq_empty = false; struct ath6kl_mgmt_buff *mgmt_buf; size_t mgmt_buf_size; struct ath6kl *ar = vif->ar; mgmt = (struct ieee80211_mgmt *) buf; if (is_multicast_ether_addr(mgmt->da)) return false; conn = ath6kl_find_sta(vif, mgmt->da); if (!conn) return false; if (conn->sta_flags & STA_PS_SLEEP) { if (!(conn->sta_flags & STA_PS_POLLED)) { /* Queue the frames if the STA is sleeping */ mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff); mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL); if (!mgmt_buf) return false; INIT_LIST_HEAD(&mgmt_buf->list); mgmt_buf->id = id; mgmt_buf->freq = freq; mgmt_buf->wait = wait; mgmt_buf->len = len; mgmt_buf->no_cck = no_cck; memcpy(mgmt_buf->buf, buf, len); spin_lock_bh(&conn->psq_lock); is_psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0); list_add_tail(&mgmt_buf->list, &conn->mgmt_psq); conn->mgmt_psq_len++; spin_unlock_bh(&conn->psq_lock); /* * If this is the first pkt getting queued * for this STA, update the PVB for this * STA. */ if (is_psq_empty) ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 1); return true; } /* * This tx is because of a PsPoll. * Determine if MoreData bit has to be set. */ spin_lock_bh(&conn->psq_lock); if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0)) *more_data = true; spin_unlock_bh(&conn->psq_lock); } return false; } /* Check if SSID length is greater than DIRECT- */ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) { const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; /* variable[1] contains the SSID tag length */ if (buf + len >= &mgmt->u.probe_resp.variable[1] && (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) { return true; } return false; } static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); u32 id; const struct ieee80211_mgmt *mgmt; bool more_data, queued; mgmt = (const struct ieee80211_mgmt *) buf; if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && ieee80211_is_probe_resp(mgmt->frame_control) && ath6kl_is_p2p_go_ssid(buf, len)) { /* * Send Probe Response frame in GO mode using a separate WMI * command to allow the target to fill in the generic IEs. */ *cookie = 0; /* TX status not supported */ return ath6kl_send_go_probe_resp(vif, buf, len, chan->center_freq); } id = vif->send_action_id++; if (id == 0) { /* * 0 is a reserved value in the WMI command and shall not be * used for the command. */ id = vif->send_action_id++; } *cookie = id; /* AP mode Power saving processing */ if (vif->nw_type == AP_NETWORK) { queued = ath6kl_mgmt_powersave_ap(vif, id, chan->center_freq, wait, buf, len, &more_data, no_cck); if (queued) return 0; } return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, chan->center_freq, wait, buf, len, no_cck); } static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n", __func__, frame_type, reg); if (frame_type == IEEE80211_STYPE_PROBE_REQ) { /* * Note: This notification callback is not allowed to sleep, so * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we * hardcode target to report Probe Request frames all the time. */ vif->probe_req_report = reg; } } static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); u16 interval; int ret; if (ar->state != ATH6KL_STATE_ON) return -EIO; if (vif->sme_state != SME_DISCONNECTED) return -EBUSY; /* The FW currently can't support multi-vif WoW properly. */ if (ar->num_vif > 1) return -EIO; ath6kl_cfg80211_scan_complete_event(vif, true); ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, request->n_ssids, request->match_sets, request->n_match_sets); if (ret < 0) return ret; if (!request->n_match_sets) { ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, ALL_BSS_FILTER, 0); if (ret < 0) return ret; } else { ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, MATCHED_SSID_FILTER, 0); if (ret < 0) return ret; } /* fw uses seconds, also make sure that it's >0 */ interval = max_t(u16, 1, request->interval / 1000); ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, interval, interval, vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_WOW_MODE_ENABLE, WOW_FILTER_SSID, WOW_HOST_REQ_DELAY); if (ret) { ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret); return ret; } /* this also clears IE in fw if it's not set */ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_REQ, request->ie, request->ie_len); if (ret) { ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n", ret); return ret; } ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_ASLEEP); if (ret) { ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n", ret); return ret; } ar->state = ATH6KL_STATE_SCHED_SCAN; return ret; } static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl_vif *vif = netdev_priv(dev); bool stopped; stopped = __ath6kl_cfg80211_sscan_stop(vif); if (!stopped) return -EIO; return 0; } static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy, struct net_device *dev, const u8 *addr, const struct cfg80211_bitrate_mask *mask) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx, mask); } static const struct ieee80211_txrx_stypes ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_AP] = { .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_CLIENT] = { .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_GO] = { .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, }; static struct cfg80211_ops ath6kl_cfg80211_ops = { .add_virtual_intf = ath6kl_cfg80211_add_iface, .del_virtual_intf = ath6kl_cfg80211_del_iface, .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, .connect = ath6kl_cfg80211_connect, .disconnect = ath6kl_cfg80211_disconnect, .add_key = ath6kl_cfg80211_add_key, .get_key = ath6kl_cfg80211_get_key, .del_key = ath6kl_cfg80211_del_key, .set_default_key = ath6kl_cfg80211_set_default_key, .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params, .set_tx_power = ath6kl_cfg80211_set_txpower, .get_tx_power = ath6kl_cfg80211_get_txpower, .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt, .join_ibss = ath6kl_cfg80211_join_ibss, .leave_ibss = ath6kl_cfg80211_leave_ibss, .get_station = ath6kl_get_station, .set_pmksa = ath6kl_set_pmksa, .del_pmksa = ath6kl_del_pmksa, .flush_pmksa = ath6kl_flush_pmksa, CFG80211_TESTMODE_CMD(ath6kl_tm_cmd) #ifdef CONFIG_PM .suspend = __ath6kl_cfg80211_suspend, .resume = __ath6kl_cfg80211_resume, #endif .start_ap = ath6kl_start_ap, .change_beacon = ath6kl_change_beacon, .stop_ap = ath6kl_stop_ap, .del_station = ath6kl_del_station, .change_station = ath6kl_change_station, .remain_on_channel = ath6kl_remain_on_channel, .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, .mgmt_tx = ath6kl_mgmt_tx, .mgmt_frame_register = ath6kl_mgmt_frame_register, .sched_scan_start = ath6kl_cfg80211_sscan_start, .sched_scan_stop = ath6kl_cfg80211_sscan_stop, .set_bitrate_mask = ath6kl_cfg80211_set_bitrate, }; void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) { ath6kl_cfg80211_sscan_disable(vif); switch (vif->sme_state) { case SME_DISCONNECTED: break; case SME_CONNECTING: cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); break; case SME_CONNECTED: cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL); break; } if (test_bit(CONNECTED, &vif->flags) || test_bit(CONNECT_PEND, &vif->flags)) ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx); vif->sme_state = SME_DISCONNECTED; clear_bit(CONNECTED, &vif->flags); clear_bit(CONNECT_PEND, &vif->flags); /* disable scanning */ if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) ath6kl_warn("failed to disable scan during stop\n"); ath6kl_cfg80211_scan_complete_event(vif, true); } void ath6kl_cfg80211_stop_all(struct ath6kl *ar) { struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); if (!vif) { /* save the current power mode before enabling power save */ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0) ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n"); return; } /* * FIXME: we should take ar->list_lock to protect changes in the * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop() * sleeps. */ list_for_each_entry(vif, &ar->vif_list, list) ath6kl_cfg80211_stop(vif); } static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) { vif->aggr_cntxt = aggr_init(vif); if (!vif->aggr_cntxt) { ath6kl_err("failed to initialize aggr\n"); return -ENOMEM; } setup_timer(&vif->disconnect_timer, disconnect_timer_handler, (unsigned long) vif->ndev); setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer, (unsigned long) vif); set_bit(WMM_ENABLED, &vif->flags); spin_lock_init(&vif->if_lock); INIT_LIST_HEAD(&vif->mc_filter); return 0; } void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; struct ath6kl_mc_filter *mc_filter, *tmp; aggr_module_destroy(vif->aggr_cntxt); ar->avail_idx_map |= BIT(vif->fw_vif_idx); if (vif->nw_type == ADHOC_NETWORK) ar->ibss_if_active = false; list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) { list_del(&mc_filter->list); kfree(mc_filter); } unregister_netdevice(vif->ndev); ar->num_vif--; } struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type) { struct net_device *ndev; struct ath6kl_vif *vif; ndev = alloc_netdev(sizeof(*vif), name, ether_setup); if (!ndev) return NULL; vif = netdev_priv(ndev); ndev->ieee80211_ptr = &vif->wdev; vif->wdev.wiphy = ar->wiphy; vif->ar = ar; vif->ndev = ndev; SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy)); vif->wdev.netdev = ndev; vif->wdev.iftype = type; vif->fw_vif_idx = fw_vif_idx; vif->nw_type = nw_type; vif->next_mode = nw_type; vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL; vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME; vif->bg_scan_period = 0; vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true; vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true; memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); if (fw_vif_idx != 0) ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) | 0x2; init_netdev(ndev); ath6kl_init_control_info(vif); if (ath6kl_cfg80211_vif_init(vif)) goto err; if (register_netdevice(ndev)) goto err; ar->avail_idx_map &= ~BIT(fw_vif_idx); vif->sme_state = SME_DISCONNECTED; set_bit(WLAN_ENABLED, &vif->flags); ar->wlan_pwr_state = WLAN_POWER_STATE_ON; set_bit(NETDEV_REGISTERED, &vif->flags); if (type == NL80211_IFTYPE_ADHOC) ar->ibss_if_active = true; spin_lock_bh(&ar->list_lock); list_add_tail(&vif->list, &ar->vif_list); spin_unlock_bh(&ar->list_lock); return &vif->wdev; err: aggr_module_destroy(vif->aggr_cntxt); free_netdev(ndev); return NULL; } int ath6kl_cfg80211_init(struct ath6kl *ar) { struct wiphy *wiphy = ar->wiphy; bool band_2gig = false, band_5gig = false, ht = false; int ret; wiphy->mgmt_stypes = ath6kl_mgmt_stypes; wiphy->max_remain_on_channel_duration = 5000; /* set device pointer for wiphy */ set_wiphy_dev(wiphy, ar->dev); wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); if (ar->p2p) { wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT); } /* max num of ssids that can be probed during scanning */ wiphy->max_scan_ssids = MAX_PROBED_SSIDS; /* max num of ssids that can be matched after scan */ if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, ar->fw_capabilities)) wiphy->max_match_sets = MAX_PROBED_SSIDS; wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ switch (ar->hw.cap) { case WMI_11AN_CAP: ht = true; case WMI_11A_CAP: band_5gig = true; break; case WMI_11GN_CAP: ht = true; case WMI_11G_CAP: band_2gig = true; break; case WMI_11AGN_CAP: ht = true; case WMI_11AG_CAP: band_2gig = true; band_5gig = true; break; default: ath6kl_err("invalid phy capability!\n"); return -EINVAL; } /* * Even if the fw has HT support, advertise HT cap only when * the firmware has support to override RSN capability, otherwise * 4-way handshake would fail. */ if (!(ht && test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, ar->fw_capabilities))) { ath6kl_band_2ghz.ht_cap.cap = 0; ath6kl_band_2ghz.ht_cap.ht_supported = false; ath6kl_band_5ghz.ht_cap.cap = 0; ath6kl_band_5ghz.ht_cap.ht_supported = false; } if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff; } else { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; } if (band_2gig) wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; if (band_5gig) wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->cipher_suites = cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); #ifdef CONFIG_PM wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_GTK_REKEY_FAILURE | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_EAP_IDENTITY_REQ | WIPHY_WOWLAN_4WAY_HANDSHAKE; wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; wiphy->wowlan.pattern_min_len = 1; wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; #endif wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS; ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, ar->fw_capabilities)) ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER; ar->wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; ret = wiphy_register(wiphy); if (ret < 0) { ath6kl_err("couldn't register wiphy device\n"); return ret; } ar->wiphy_registered = true; return 0; } void ath6kl_cfg80211_cleanup(struct ath6kl *ar) { wiphy_unregister(ar->wiphy); ar->wiphy_registered = false; } struct ath6kl *ath6kl_cfg80211_create(void) { struct ath6kl *ar; struct wiphy *wiphy; /* create a new wiphy for use with cfg80211 */ wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl)); if (!wiphy) { ath6kl_err("couldn't allocate wiphy device\n"); return NULL; } ar = wiphy_priv(wiphy); ar->wiphy = wiphy; return ar; } /* Note: ar variable must not be accessed after calling this! */ void ath6kl_cfg80211_destroy(struct ath6kl *ar) { int i; for (i = 0; i < AP_MAX_NUM_STA; i++) kfree(ar->sta_list[i].aggr_conn); wiphy_free(ar->wiphy); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/sdio.c0000644000175000017500000011243312026211315023534 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "hif.h" #include "hif-ops.h" #include "target.h" #include "debug.h" #include "cfg80211.h" struct ath6kl_sdio { struct sdio_func *func; /* protects access to bus_req_freeq */ spinlock_t lock; /* free list */ struct list_head bus_req_freeq; /* available bus requests */ struct bus_request bus_req[BUS_REQUEST_MAX_NUM]; struct ath6kl *ar; u8 *dma_buffer; /* protects access to dma_buffer */ struct mutex dma_buffer_mutex; /* scatter request list head */ struct list_head scat_req; atomic_t irq_handling; wait_queue_head_t irq_wq; /* protects access to scat_req */ spinlock_t scat_lock; bool scatter_enabled; bool is_disabled; const struct sdio_device_id *id; struct work_struct wr_async_work; struct list_head wr_asyncq; /* protects access to wr_asyncq */ spinlock_t wr_async_lock; }; #define CMD53_ARG_READ 0 #define CMD53_ARG_WRITE 1 #define CMD53_ARG_BLOCK_BASIS 1 #define CMD53_ARG_FIXED_ADDRESS 0 #define CMD53_ARG_INCR_ADDRESS 1 static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar) { return ar->hif_priv; } /* * Macro to check if DMA buffer is WORD-aligned and DMA-able. * Most host controllers assume the buffer is DMA'able and will * bug-check otherwise (i.e. buffers on the stack). virt_addr_valid * check fails on stack memory. */ static inline bool buf_needs_bounce(u8 *buf) { return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf); } static void ath6kl_sdio_set_mbox_info(struct ath6kl *ar) { struct ath6kl_mbox_info *mbox_info = &ar->mbox_info; /* EP1 has an extended range */ mbox_info->htc_addr = HIF_MBOX_BASE_ADDR; mbox_info->htc_ext_addr = HIF_MBOX0_EXT_BASE_ADDR; mbox_info->htc_ext_sz = HIF_MBOX0_EXT_WIDTH; mbox_info->block_size = HIF_MBOX_BLOCK_SIZE; mbox_info->gmbox_addr = HIF_GMBOX_BASE_ADDR; mbox_info->gmbox_sz = HIF_GMBOX_WIDTH; } static inline void ath6kl_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func, u8 mode, u8 opcode, u32 addr, u16 blksz) { *arg = (((rw & 1) << 31) | ((func & 0x7) << 28) | ((mode & 1) << 27) | ((opcode & 1) << 26) | ((addr & 0x1FFFF) << 9) | (blksz & 0x1FF)); } static inline void ath6kl_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw, unsigned int address, unsigned char val) { const u8 func = 0; *arg = ((write & 1) << 31) | ((func & 0x7) << 28) | ((raw & 1) << 27) | (1 << 26) | ((address & 0x1FFFF) << 9) | (1 << 8) | (val & 0xFF); } static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card, unsigned int address, unsigned char byte) { struct mmc_command io_cmd; memset(&io_cmd, 0, sizeof(io_cmd)); ath6kl_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte); io_cmd.opcode = SD_IO_RW_DIRECT; io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; return mmc_wait_for_cmd(card->host, &io_cmd, 0); } static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, u8 *buf, u32 len) { int ret = 0; sdio_claim_host(func); if (request & HIF_WRITE) { /* FIXME: looks like ugly workaround for something */ if (addr >= HIF_MBOX_BASE_ADDR && addr <= HIF_MBOX_END_ADDR) addr += (HIF_MBOX_WIDTH - len); /* FIXME: this also looks like ugly workaround */ if (addr == HIF_MBOX0_EXT_BASE_ADDR) addr += HIF_MBOX0_EXT_WIDTH - len; if (request & HIF_FIXED_ADDRESS) ret = sdio_writesb(func, addr, buf, len); else ret = sdio_memcpy_toio(func, addr, buf, len); } else { if (request & HIF_FIXED_ADDRESS) ret = sdio_readsb(func, buf, addr, len); else ret = sdio_memcpy_fromio(func, buf, addr, len); } sdio_release_host(func); ath6kl_dbg(ATH6KL_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n", request & HIF_WRITE ? "wr" : "rd", addr, request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len); ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len); return ret; } static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio) { struct bus_request *bus_req; spin_lock_bh(&ar_sdio->lock); if (list_empty(&ar_sdio->bus_req_freeq)) { spin_unlock_bh(&ar_sdio->lock); return NULL; } bus_req = list_first_entry(&ar_sdio->bus_req_freeq, struct bus_request, list); list_del(&bus_req->list); spin_unlock_bh(&ar_sdio->lock); ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n", __func__, bus_req); return bus_req; } static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio, struct bus_request *bus_req) { ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n", __func__, bus_req); spin_lock_bh(&ar_sdio->lock); list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq); spin_unlock_bh(&ar_sdio->lock); } static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req, struct mmc_data *data) { struct scatterlist *sg; int i; data->blksz = HIF_MBOX_BLOCK_SIZE; data->blocks = scat_req->len / HIF_MBOX_BLOCK_SIZE; ath6kl_dbg(ATH6KL_DBG_SCATTER, "hif-scatter: (%s) addr: 0x%X, (block len: %d, block count: %d) , (tot:%d,sg:%d)\n", (scat_req->req & HIF_WRITE) ? "WR" : "RD", scat_req->addr, data->blksz, data->blocks, scat_req->len, scat_req->scat_entries); data->flags = (scat_req->req & HIF_WRITE) ? MMC_DATA_WRITE : MMC_DATA_READ; /* fill SG entries */ sg = scat_req->sgentries; sg_init_table(sg, scat_req->scat_entries); /* assemble SG list */ for (i = 0; i < scat_req->scat_entries; i++, sg++) { ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n", i, scat_req->scat_list[i].buf, scat_req->scat_list[i].len); sg_set_buf(sg, scat_req->scat_list[i].buf, scat_req->scat_list[i].len); } /* set scatter-gather table for request */ data->sg = scat_req->sgentries; data->sg_len = scat_req->scat_entries; } static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio, struct bus_request *req) { struct mmc_request mmc_req; struct mmc_command cmd; struct mmc_data data; struct hif_scatter_req *scat_req; u8 opcode, rw; int status, len; scat_req = req->scat_req; if (scat_req->virt_scat) { len = scat_req->len; if (scat_req->req & HIF_BLOCK_BASIS) len = round_down(len, HIF_MBOX_BLOCK_SIZE); status = ath6kl_sdio_io(ar_sdio->func, scat_req->req, scat_req->addr, scat_req->virt_dma_buf, len); goto scat_complete; } memset(&mmc_req, 0, sizeof(struct mmc_request)); memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); ath6kl_sdio_setup_scat_data(scat_req, &data); opcode = (scat_req->req & HIF_FIXED_ADDRESS) ? CMD53_ARG_FIXED_ADDRESS : CMD53_ARG_INCR_ADDRESS; rw = (scat_req->req & HIF_WRITE) ? CMD53_ARG_WRITE : CMD53_ARG_READ; /* Fixup the address so that the last byte will fall on MBOX EOM */ if (scat_req->req & HIF_WRITE) { if (scat_req->addr == HIF_MBOX_BASE_ADDR) scat_req->addr += HIF_MBOX_WIDTH - scat_req->len; else /* Uses extended address range */ scat_req->addr += HIF_MBOX0_EXT_WIDTH - scat_req->len; } /* set command argument */ ath6kl_sdio_set_cmd53_arg(&cmd.arg, rw, ar_sdio->func->num, CMD53_ARG_BLOCK_BASIS, opcode, scat_req->addr, data.blocks); cmd.opcode = SD_IO_RW_EXTENDED; cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; mmc_req.cmd = &cmd; mmc_req.data = &data; sdio_claim_host(ar_sdio->func); mmc_set_data_timeout(&data, ar_sdio->func->card); /* synchronous call to process request */ mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req); sdio_release_host(ar_sdio->func); status = cmd.error ? cmd.error : data.error; scat_complete: scat_req->status = status; if (scat_req->status) ath6kl_err("Scatter write request failed:%d\n", scat_req->status); if (scat_req->req & HIF_ASYNCHRONOUS) scat_req->complete(ar_sdio->ar->htc_target, scat_req); return status; } static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, int n_scat_entry, int n_scat_req, bool virt_scat) { struct hif_scatter_req *s_req; struct bus_request *bus_req; int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz; u8 *virt_buf; scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item); scat_req_sz = sizeof(*s_req) + scat_list_sz; if (!virt_scat) sg_sz = sizeof(struct scatterlist) * n_scat_entry; else buf_sz = 2 * L1_CACHE_BYTES + ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER; for (i = 0; i < n_scat_req; i++) { /* allocate the scatter request */ s_req = kzalloc(scat_req_sz, GFP_KERNEL); if (!s_req) return -ENOMEM; if (virt_scat) { virt_buf = kzalloc(buf_sz, GFP_KERNEL); if (!virt_buf) { kfree(s_req); return -ENOMEM; } s_req->virt_dma_buf = (u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf); } else { /* allocate sglist */ s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL); if (!s_req->sgentries) { kfree(s_req); return -ENOMEM; } } /* allocate a bus request for this scatter request */ bus_req = ath6kl_sdio_alloc_busreq(ar_sdio); if (!bus_req) { kfree(s_req->sgentries); kfree(s_req->virt_dma_buf); kfree(s_req); return -ENOMEM; } /* assign the scatter request to this bus request */ bus_req->scat_req = s_req; s_req->busrequest = bus_req; s_req->virt_scat = virt_scat; /* add it to the scatter pool */ hif_scatter_req_add(ar_sdio->ar, s_req); } return 0; } static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, u32 len, u32 request) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); u8 *tbuf = NULL; int ret; bool bounced = false; if (request & HIF_BLOCK_BASIS) len = round_down(len, HIF_MBOX_BLOCK_SIZE); if (buf_needs_bounce(buf)) { if (!ar_sdio->dma_buffer) return -ENOMEM; mutex_lock(&ar_sdio->dma_buffer_mutex); tbuf = ar_sdio->dma_buffer; if (request & HIF_WRITE) memcpy(tbuf, buf, len); bounced = true; } else tbuf = buf; ret = ath6kl_sdio_io(ar_sdio->func, request, addr, tbuf, len); if ((request & HIF_READ) && bounced) memcpy(buf, tbuf, len); if (bounced) mutex_unlock(&ar_sdio->dma_buffer_mutex); return ret; } static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio, struct bus_request *req) { if (req->scat_req) ath6kl_sdio_scat_rw(ar_sdio, req); else { void *context; int status; status = ath6kl_sdio_read_write_sync(ar_sdio->ar, req->address, req->buffer, req->length, req->request); context = req->packet; ath6kl_sdio_free_bus_req(ar_sdio, req); ath6kl_hif_rw_comp_handler(context, status); } } static void ath6kl_sdio_write_async_work(struct work_struct *work) { struct ath6kl_sdio *ar_sdio; struct bus_request *req, *tmp_req; ar_sdio = container_of(work, struct ath6kl_sdio, wr_async_work); spin_lock_bh(&ar_sdio->wr_async_lock); list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) { list_del(&req->list); spin_unlock_bh(&ar_sdio->wr_async_lock); __ath6kl_sdio_write_async(ar_sdio, req); spin_lock_bh(&ar_sdio->wr_async_lock); } spin_unlock_bh(&ar_sdio->wr_async_lock); } static void ath6kl_sdio_irq_handler(struct sdio_func *func) { int status; struct ath6kl_sdio *ar_sdio; ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n"); ar_sdio = sdio_get_drvdata(func); atomic_set(&ar_sdio->irq_handling, 1); /* * Release the host during interrups so we can pick it back up when * we process commands. */ sdio_release_host(ar_sdio->func); status = ath6kl_hif_intr_bh_handler(ar_sdio->ar); sdio_claim_host(ar_sdio->func); atomic_set(&ar_sdio->irq_handling, 0); wake_up(&ar_sdio->irq_wq); WARN_ON(status && status != -ECANCELED); } static int ath6kl_sdio_power_on(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct sdio_func *func = ar_sdio->func; int ret = 0; if (!ar_sdio->is_disabled) return 0; ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio power on\n"); sdio_claim_host(func); ret = sdio_enable_func(func); if (ret) { ath6kl_err("Unable to enable sdio func: %d)\n", ret); sdio_release_host(func); return ret; } sdio_release_host(func); /* * Wait for hardware to initialise. It should take a lot less than * 10 ms but let's be conservative here. */ msleep(10); ar_sdio->is_disabled = false; return ret; } static int ath6kl_sdio_power_off(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); int ret; if (ar_sdio->is_disabled) return 0; ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio power off\n"); /* Disable the card */ sdio_claim_host(ar_sdio->func); ret = sdio_disable_func(ar_sdio->func); sdio_release_host(ar_sdio->func); if (ret) return ret; ar_sdio->is_disabled = true; return ret; } static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer, u32 length, u32 request, struct htc_packet *packet) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct bus_request *bus_req; bus_req = ath6kl_sdio_alloc_busreq(ar_sdio); if (WARN_ON_ONCE(!bus_req)) return -ENOMEM; bus_req->address = address; bus_req->buffer = buffer; bus_req->length = length; bus_req->request = request; bus_req->packet = packet; spin_lock_bh(&ar_sdio->wr_async_lock); list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq); spin_unlock_bh(&ar_sdio->wr_async_lock); queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work); return 0; } static void ath6kl_sdio_irq_enable(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); int ret; sdio_claim_host(ar_sdio->func); /* Register the isr */ ret = sdio_claim_irq(ar_sdio->func, ath6kl_sdio_irq_handler); if (ret) ath6kl_err("Failed to claim sdio irq: %d\n", ret); sdio_release_host(ar_sdio->func); } static bool ath6kl_sdio_is_on_irq(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); return !atomic_read(&ar_sdio->irq_handling); } static void ath6kl_sdio_irq_disable(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); int ret; sdio_claim_host(ar_sdio->func); if (atomic_read(&ar_sdio->irq_handling)) { sdio_release_host(ar_sdio->func); ret = wait_event_interruptible(ar_sdio->irq_wq, ath6kl_sdio_is_on_irq(ar)); if (ret) return; sdio_claim_host(ar_sdio->func); } ret = sdio_release_irq(ar_sdio->func); if (ret) ath6kl_err("Failed to release sdio irq: %d\n", ret); sdio_release_host(ar_sdio->func); } static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct hif_scatter_req *node = NULL; spin_lock_bh(&ar_sdio->scat_lock); if (!list_empty(&ar_sdio->scat_req)) { node = list_first_entry(&ar_sdio->scat_req, struct hif_scatter_req, list); list_del(&node->list); node->scat_q_depth = get_queue_depth(&ar_sdio->scat_req); } spin_unlock_bh(&ar_sdio->scat_lock); return node; } static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar, struct hif_scatter_req *s_req) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); spin_lock_bh(&ar_sdio->scat_lock); list_add_tail(&s_req->list, &ar_sdio->scat_req); spin_unlock_bh(&ar_sdio->scat_lock); } /* scatter gather read write request */ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar, struct hif_scatter_req *scat_req) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); u32 request = scat_req->req; int status = 0; if (!scat_req->len) return -EINVAL; ath6kl_dbg(ATH6KL_DBG_SCATTER, "hif-scatter: total len: %d scatter entries: %d\n", scat_req->len, scat_req->scat_entries); if (request & HIF_SYNCHRONOUS) status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest); else { spin_lock_bh(&ar_sdio->wr_async_lock); list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq); spin_unlock_bh(&ar_sdio->wr_async_lock); queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work); } return status; } /* clean up scatter support */ static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct hif_scatter_req *s_req, *tmp_req; /* empty the free list */ spin_lock_bh(&ar_sdio->scat_lock); list_for_each_entry_safe(s_req, tmp_req, &ar_sdio->scat_req, list) { list_del(&s_req->list); spin_unlock_bh(&ar_sdio->scat_lock); /* * FIXME: should we also call completion handler with * ath6kl_hif_rw_comp_handler() with status -ECANCELED so * that the packet is properly freed? */ if (s_req->busrequest) ath6kl_sdio_free_bus_req(ar_sdio, s_req->busrequest); kfree(s_req->virt_dma_buf); kfree(s_req->sgentries); kfree(s_req); spin_lock_bh(&ar_sdio->scat_lock); } spin_unlock_bh(&ar_sdio->scat_lock); } /* setup of HIF scatter resources */ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct htc_target *target = ar->htc_target; int ret; bool virt_scat = false; if (ar_sdio->scatter_enabled) return 0; ar_sdio->scatter_enabled = true; /* check if host supports scatter and it meets our requirements */ if (ar_sdio->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) { ath6kl_err("host only supports scatter of :%d entries, need: %d\n", ar_sdio->func->card->host->max_segs, MAX_SCATTER_ENTRIES_PER_REQ); virt_scat = true; } if (!virt_scat) { ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio, MAX_SCATTER_ENTRIES_PER_REQ, MAX_SCATTER_REQUESTS, virt_scat); if (!ret) { ath6kl_dbg(ATH6KL_DBG_BOOT, "hif-scatter enabled requests %d entries %d\n", MAX_SCATTER_REQUESTS, MAX_SCATTER_ENTRIES_PER_REQ); target->max_scat_entries = MAX_SCATTER_ENTRIES_PER_REQ; target->max_xfer_szper_scatreq = MAX_SCATTER_REQ_TRANSFER_SIZE; } else { ath6kl_sdio_cleanup_scatter(ar); ath6kl_warn("hif scatter resource setup failed, trying virtual scatter method\n"); } } if (virt_scat || ret) { ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio, ATH6KL_SCATTER_ENTRIES_PER_REQ, ATH6KL_SCATTER_REQS, virt_scat); if (ret) { ath6kl_err("failed to alloc virtual scatter resources !\n"); ath6kl_sdio_cleanup_scatter(ar); return ret; } ath6kl_dbg(ATH6KL_DBG_BOOT, "virtual scatter enabled requests %d entries %d\n", ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ); target->max_scat_entries = ATH6KL_SCATTER_ENTRIES_PER_REQ; target->max_xfer_szper_scatreq = ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER; } return 0; } static int ath6kl_sdio_config(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct sdio_func *func = ar_sdio->func; int ret; sdio_claim_host(func); if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >= MANUFACTURER_ID_AR6003_BASE) { /* enable 4-bit ASYNC interrupt on AR6003 or later */ ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card, CCCR_SDIO_IRQ_MODE_REG, SDIO_IRQ_MODE_ASYNC_4BIT_IRQ); if (ret) { ath6kl_err("Failed to enable 4-bit async irq mode %d\n", ret); goto out; } ath6kl_dbg(ATH6KL_DBG_BOOT, "4-bit async irq mode enabled\n"); } /* give us some time to enable, in ms */ func->enable_timeout = 100; ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE); if (ret) { ath6kl_err("Set sdio block size %d failed: %d)\n", HIF_MBOX_BLOCK_SIZE, ret); goto out; } out: sdio_release_host(func); return ret; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct sdio_func *func = ar_sdio->func; mmc_pm_flag_t flags; int ret; flags = sdio_get_host_pm_caps(func); ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags); if (!(flags & MMC_PM_WAKE_SDIO_IRQ) || !(flags & MMC_PM_KEEP_POWER)) return -EINVAL; ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) { ath6kl_err("set sdio keep pwr flag failed: %d\n", ret); return ret; } /* sdio irq wakes up host */ ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); if (ret) ath6kl_err("set sdio wake irq flag failed: %d\n", ret); return ret; } static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct sdio_func *func = ar_sdio->func; mmc_pm_flag_t flags; bool try_deepsleep = false; int ret; if (ar->state == ATH6KL_STATE_SCHED_SCAN) { ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n"); ret = ath6kl_set_sdio_pm_caps(ar); if (ret) goto cut_pwr; ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_SCHED_SCAN, NULL); if (ret) goto cut_pwr; return 0; } if (ar->suspend_mode == WLAN_POWER_STATE_WOW || (!ar->suspend_mode && wow)) { ret = ath6kl_set_sdio_pm_caps(ar); if (ret) goto cut_pwr; ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); if (ret && ret != -ENOTCONN) ath6kl_err("wow suspend failed: %d\n", ret); if (ret && (!ar->wow_suspend_mode || ar->wow_suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP)) try_deepsleep = true; else if (ret && ar->wow_suspend_mode == WLAN_POWER_STATE_CUT_PWR) goto cut_pwr; if (!ret) return 0; } if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP || !ar->suspend_mode || try_deepsleep) { flags = sdio_get_host_pm_caps(func); if (!(flags & MMC_PM_KEEP_POWER)) goto cut_pwr; ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) goto cut_pwr; /* * Workaround to support Deep Sleep with MSM, set the host pm * flag as MMC_PM_WAKE_SDIO_IRQ to allow SDCC deiver to disable * the sdc2_clock and internally allows MSM to enter * TCXO shutdown properly. */ if ((flags & MMC_PM_WAKE_SDIO_IRQ)) { ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); if (ret) goto cut_pwr; } ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL); if (ret) goto cut_pwr; return 0; } cut_pwr: if (func->card && func->card->host) func->card->host->pm_flags &= ~MMC_PM_KEEP_POWER; return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL); } static int ath6kl_sdio_resume(struct ath6kl *ar) { switch (ar->state) { case ATH6KL_STATE_OFF: case ATH6KL_STATE_CUTPOWER: ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio resume configuring sdio\n"); /* need to set sdio settings after power is cut from sdio */ ath6kl_sdio_config(ar); break; case ATH6KL_STATE_ON: break; case ATH6KL_STATE_DEEPSLEEP: break; case ATH6KL_STATE_WOW: break; case ATH6KL_STATE_SCHED_SCAN: break; case ATH6KL_STATE_SUSPENDING: break; case ATH6KL_STATE_RESUMING: break; } ath6kl_cfg80211_resume(ar); return 0; } #else static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) { return 0; } static int ath6kl_sdio_resume(struct ath6kl *ar) { return 0; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ /* set the window address register (using 4-byte register access ). */ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) { int status; u8 addr_val[4]; s32 i; /* * Write bytes 1,2,3 of the register to set the upper address bytes, * the LSB is written last to initiate the access cycle */ for (i = 1; i <= 3; i++) { /* * Fill the buffer with the address byte value we want to * hit 4 times. */ memset(addr_val, ((u8 *)&addr)[i], 4); /* * Hit each byte of the register address with a 4-byte * write operation to the same address, this is a harmless * operation. */ status = ath6kl_sdio_read_write_sync(ar, reg_addr + i, addr_val, 4, HIF_WR_SYNC_BYTE_FIX); if (status) break; } if (status) { ath6kl_err("%s: failed to write initial bytes of 0x%x to window reg: 0x%X\n", __func__, addr, reg_addr); return status; } /* * Write the address register again, this time write the whole * 4-byte value. The effect here is that the LSB write causes the * cycle to start, the extra 3 byte write to bytes 1,2,3 has no * effect since we are writing the same values again */ status = ath6kl_sdio_read_write_sync(ar, reg_addr, (u8 *)(&addr), 4, HIF_WR_SYNC_BYTE_INC); if (status) { ath6kl_err("%s: failed to write 0x%x to window reg: 0x%X\n", __func__, addr, reg_addr); return status; } return 0; } static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data) { int status; /* set window register to start read cycle */ status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address); if (status) return status; /* read the data */ status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC); if (status) { ath6kl_err("%s: failed to read from window data addr\n", __func__); return status; } return status; } static int ath6kl_sdio_diag_write32(struct ath6kl *ar, u32 address, __le32 data) { int status; u32 val = (__force u32) data; /* set write data */ status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &val, sizeof(u32), HIF_WR_SYNC_BYTE_INC); if (status) { ath6kl_err("%s: failed to write 0x%x to window data addr\n", __func__, data); return status; } /* set window register, which starts the write cycle */ return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS, address); } static int ath6kl_sdio_bmi_credits(struct ath6kl *ar) { u32 addr; unsigned long timeout; int ret; ar->bmi.cmd_credits = 0; /* Read the counter register to get the command credits */ addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4; timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) { /* * Hit the credit counter with a 4-byte access, the first byte * read will hit the counter and cause a decrement, while the * remaining 3 bytes has no effect. The rationale behind this * is to make all HIF accesses 4-byte aligned. */ ret = ath6kl_sdio_read_write_sync(ar, addr, (u8 *)&ar->bmi.cmd_credits, 4, HIF_RD_SYNC_BYTE_INC); if (ret) { ath6kl_err("Unable to decrement the command credit count register: %d\n", ret); return ret; } /* The counter is only 8 bits. * Ignore anything in the upper 3 bytes */ ar->bmi.cmd_credits &= 0xFF; } if (!ar->bmi.cmd_credits) { ath6kl_err("bmi communication timeout\n"); return -ETIMEDOUT; } return 0; } static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar) { unsigned long timeout; u32 rx_word = 0; int ret = 0; timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); while ((time_before(jiffies, timeout)) && !rx_word) { ret = ath6kl_sdio_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS, (u8 *)&rx_word, sizeof(rx_word), HIF_RD_SYNC_BYTE_INC); if (ret) { ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n"); return ret; } /* all we really want is one bit */ rx_word &= (1 << ENDPOINT1); } if (!rx_word) { ath6kl_err("bmi_recv_buf FIFO empty\n"); return -EINVAL; } return ret; } static int ath6kl_sdio_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) { int ret; u32 addr; ret = ath6kl_sdio_bmi_credits(ar); if (ret) return ret; addr = ar->mbox_info.htc_addr; ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len, HIF_WR_SYNC_BYTE_INC); if (ret) ath6kl_err("unable to send the bmi data to the device\n"); return ret; } static int ath6kl_sdio_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) { int ret; u32 addr; /* * During normal bootup, small reads may be required. * Rather than issue an HIF Read and then wait as the Target * adds successive bytes to the FIFO, we wait here until * we know that response data is available. * * This allows us to cleanly timeout on an unexpected * Target failure rather than risk problems at the HIF level. * In particular, this avoids SDIO timeouts and possibly garbage * data on some host controllers. And on an interconnect * such as Compact Flash (as well as some SDIO masters) which * does not provide any indication on data timeout, it avoids * a potential hang or garbage response. * * Synchronization is more difficult for reads larger than the * size of the MBOX FIFO (128B), because the Target is unable * to push the 129th byte of data until AFTER the Host posts an * HIF Read and removes some FIFO data. So for large reads the * Host proceeds to post an HIF Read BEFORE all the data is * actually available to read. Fortunately, large BMI reads do * not occur in practice -- they're supported for debug/development. * * So Host/Target BMI synchronization is divided into these cases: * CASE 1: length < 4 * Should not happen * * CASE 2: 4 <= length <= 128 * Wait for first 4 bytes to be in FIFO * If CONSERVATIVE_BMI_READ is enabled, also wait for * a BMI command credit, which indicates that the ENTIRE * response is available in the the FIFO * * CASE 3: length > 128 * Wait for the first 4 bytes to be in FIFO * * For most uses, a small timeout should be sufficient and we will * usually see a response quickly; but there may be some unusual * (debug) cases of BMI_EXECUTE where we want an larger timeout. * For now, we use an unbounded busy loop while waiting for * BMI_EXECUTE. * * If BMI_EXECUTE ever needs to support longer-latency execution, * especially in production, this code needs to be enhanced to sleep * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently * a function of Host processor speed. */ if (len >= 4) { /* NB: Currently, always true */ ret = ath6kl_bmi_get_rx_lkahd(ar); if (ret) return ret; } addr = ar->mbox_info.htc_addr; ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len, HIF_RD_SYNC_BYTE_INC); if (ret) { ath6kl_err("Unable to read the bmi data from the device: %d\n", ret); return ret; } return 0; } static void ath6kl_sdio_stop(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct bus_request *req, *tmp_req; void *context; /* FIXME: make sure that wq is not queued again */ cancel_work_sync(&ar_sdio->wr_async_work); spin_lock_bh(&ar_sdio->wr_async_lock); list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) { list_del(&req->list); if (req->scat_req) { /* this is a scatter gather request */ req->scat_req->status = -ECANCELED; req->scat_req->complete(ar_sdio->ar->htc_target, req->scat_req); } else { context = req->packet; ath6kl_sdio_free_bus_req(ar_sdio, req); ath6kl_hif_rw_comp_handler(context, -ECANCELED); } } spin_unlock_bh(&ar_sdio->wr_async_lock); WARN_ON(get_queue_depth(&ar_sdio->scat_req) != 4); } static const struct ath6kl_hif_ops ath6kl_sdio_ops = { .read_write_sync = ath6kl_sdio_read_write_sync, .write_async = ath6kl_sdio_write_async, .irq_enable = ath6kl_sdio_irq_enable, .irq_disable = ath6kl_sdio_irq_disable, .scatter_req_get = ath6kl_sdio_scatter_req_get, .scatter_req_add = ath6kl_sdio_scatter_req_add, .enable_scatter = ath6kl_sdio_enable_scatter, .scat_req_rw = ath6kl_sdio_async_rw_scatter, .cleanup_scatter = ath6kl_sdio_cleanup_scatter, .suspend = ath6kl_sdio_suspend, .resume = ath6kl_sdio_resume, .diag_read32 = ath6kl_sdio_diag_read32, .diag_write32 = ath6kl_sdio_diag_write32, .bmi_read = ath6kl_sdio_bmi_read, .bmi_write = ath6kl_sdio_bmi_write, .power_on = ath6kl_sdio_power_on, .power_off = ath6kl_sdio_power_off, .stop = ath6kl_sdio_stop, }; #if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* * Empty handlers so that mmc subsystem doesn't remove us entirely during * suspend. We instead follow cfg80211 suspend/resume handlers. */ static int ath6kl_sdio_pm_suspend(struct device *device) { ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio pm suspend\n"); return 0; } static int ath6kl_sdio_pm_resume(struct device *device) { ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio pm resume\n"); return 0; } static SIMPLE_DEV_PM_OPS(ath6kl_sdio_pm_ops, ath6kl_sdio_pm_suspend, ath6kl_sdio_pm_resume); #define ATH6KL_SDIO_PM_OPS (&ath6kl_sdio_pm_ops) #else #define ATH6KL_SDIO_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ static int ath6kl_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int ret; struct ath6kl_sdio *ar_sdio; struct ath6kl *ar; int count; ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", func->num, func->vendor, func->device, func->max_blksize, func->cur_blksize); ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL); if (!ar_sdio) return -ENOMEM; ar_sdio->dma_buffer = kzalloc(HIF_DMA_BUFFER_SIZE, GFP_KERNEL); if (!ar_sdio->dma_buffer) { ret = -ENOMEM; goto err_hif; } ar_sdio->func = func; sdio_set_drvdata(func, ar_sdio); ar_sdio->id = id; ar_sdio->is_disabled = true; spin_lock_init(&ar_sdio->lock); spin_lock_init(&ar_sdio->scat_lock); spin_lock_init(&ar_sdio->wr_async_lock); mutex_init(&ar_sdio->dma_buffer_mutex); INIT_LIST_HEAD(&ar_sdio->scat_req); INIT_LIST_HEAD(&ar_sdio->bus_req_freeq); INIT_LIST_HEAD(&ar_sdio->wr_asyncq); INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work); init_waitqueue_head(&ar_sdio->irq_wq); for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]); ar = ath6kl_core_create(&ar_sdio->func->dev); if (!ar) { ath6kl_err("Failed to alloc ath6kl core\n"); ret = -ENOMEM; goto err_dma; } ar_sdio->ar = ar; ar->hif_type = ATH6KL_HIF_TYPE_SDIO; ar->hif_priv = ar_sdio; ar->hif_ops = &ath6kl_sdio_ops; ar->bmi.max_data_size = 256; ath6kl_sdio_set_mbox_info(ar); ret = ath6kl_sdio_config(ar); if (ret) { ath6kl_err("Failed to config sdio: %d\n", ret); goto err_core_alloc; } ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_MBOX); if (ret) { ath6kl_err("Failed to init ath6kl core\n"); goto err_core_alloc; } return ret; err_core_alloc: ath6kl_core_destroy(ar_sdio->ar); err_dma: kfree(ar_sdio->dma_buffer); err_hif: kfree(ar_sdio); return ret; } static void ath6kl_sdio_remove(struct sdio_func *func) { struct ath6kl_sdio *ar_sdio; ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio removed func %d vendor 0x%x device 0x%x\n", func->num, func->vendor, func->device); ar_sdio = sdio_get_drvdata(func); ath6kl_stop_txrx(ar_sdio->ar); cancel_work_sync(&ar_sdio->wr_async_work); ath6kl_core_cleanup(ar_sdio->ar); ath6kl_core_destroy(ar_sdio->ar); kfree(ar_sdio->dma_buffer); kfree(ar_sdio); } static const struct sdio_device_id ath6kl_sdio_devices[] = { {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))}, {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))}, {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))}, {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))}, {}, }; MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices); static struct sdio_driver ath6kl_sdio_driver = { .name = "ath6kl_sdio", .id_table = ath6kl_sdio_devices, .probe = ath6kl_sdio_probe, .remove = ath6kl_sdio_remove, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .drv.pm = ATH6KL_SDIO_PM_OPS, #endif }; static int __init ath6kl_sdio_init(void) { int ret; ret = sdio_register_driver(&ath6kl_sdio_driver); if (ret) ath6kl_err("sdio driver registration failed: %d\n", ret); return ret; } static void __exit ath6kl_sdio_exit(void) { sdio_unregister_driver(&ath6kl_sdio_driver); } module_init(ath6kl_sdio_init); module_exit(ath6kl_sdio_exit); MODULE_AUTHOR("Atheros Communications, Inc."); MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_OTP_FILE); MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_FIRMWARE_FILE); MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_PATCH_FILE); MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_OTP_FILE); MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_FIRMWARE_FILE); MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_PATCH_FILE); MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/htc_pipe.c0000644000175000017500000012455412026211315024400 0ustar mcgrofmcgrof/* * Copyright (c) 2007-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core.h" #include "debug.h" #include "hif-ops.h" #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) #include #endif #define HTC_PACKET_CONTAINER_ALLOCATION 32 #define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH) static int ath6kl_htc_pipe_tx(struct htc_target *handle, struct htc_packet *packet); static void ath6kl_htc_pipe_cleanup(struct htc_target *handle); /* htc pipe tx path */ static inline void restore_tx_packet(struct htc_packet *packet) { if (packet->info.tx.flags & HTC_FLAGS_TX_FIXUP_NETBUF) { skb_pull(packet->skb, sizeof(struct htc_frame_hdr)); packet->info.tx.flags &= ~HTC_FLAGS_TX_FIXUP_NETBUF; } } static void do_send_completion(struct htc_endpoint *ep, struct list_head *queue_to_indicate) { struct htc_packet *packet; if (list_empty(queue_to_indicate)) { /* nothing to indicate */ return; } if (ep->ep_cb.tx_comp_multi != NULL) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: calling ep %d, send complete multiple callback (%d pkts)\n", __func__, ep->eid, get_queue_depth(queue_to_indicate)); /* * a multiple send complete handler is being used, * pass the queue to the handler */ ep->ep_cb.tx_comp_multi(ep->target, queue_to_indicate); /* * all packets are now owned by the callback, * reset queue to be safe */ INIT_LIST_HEAD(queue_to_indicate); } else { /* using legacy EpTxComplete */ do { packet = list_first_entry(queue_to_indicate, struct htc_packet, list); list_del(&packet->list); ath6kl_dbg(ATH6KL_DBG_HTC, "%s: calling ep %d send complete callback on packet 0x%p\n", __func__, ep->eid, packet); ep->ep_cb.tx_complete(ep->target, packet); } while (!list_empty(queue_to_indicate)); } } static void send_packet_completion(struct htc_target *target, struct htc_packet *packet) { struct htc_endpoint *ep = &target->endpoint[packet->endpoint]; struct list_head container; restore_tx_packet(packet); INIT_LIST_HEAD(&container); list_add_tail(&packet->list, &container); /* do completion */ do_send_completion(ep, &container); } static void get_htc_packet_credit_based(struct htc_target *target, struct htc_endpoint *ep, struct list_head *queue) { int credits_required; int remainder; u8 send_flags; struct htc_packet *packet; unsigned int transfer_len; /* NOTE : the TX lock is held when this function is called */ /* loop until we can grab as many packets out of the queue as we can */ while (true) { send_flags = 0; if (list_empty(&ep->txq)) break; /* get packet at head, but don't remove it */ packet = list_first_entry(&ep->txq, struct htc_packet, list); ath6kl_dbg(ATH6KL_DBG_HTC, "%s: got head packet:0x%p , queue depth: %d\n", __func__, packet, get_queue_depth(&ep->txq)); transfer_len = packet->act_len + HTC_HDR_LENGTH; if (transfer_len <= target->tgt_cred_sz) { credits_required = 1; } else { /* figure out how many credits this message requires */ credits_required = transfer_len / target->tgt_cred_sz; remainder = transfer_len % target->tgt_cred_sz; if (remainder) credits_required++; } ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d got:%d\n", __func__, credits_required, ep->cred_dist.credits); if (ep->eid == ENDPOINT_0) { /* * endpoint 0 is special, it always has a credit and * does not require credit based flow control */ credits_required = 0; } else { if (ep->cred_dist.credits < credits_required) break; ep->cred_dist.credits -= credits_required; ep->ep_st.cred_cosumd += credits_required; /* check if we need credits back from the target */ if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { /* tell the target we need credits ASAP! */ send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; ep->ep_st.cred_low_indicate += 1; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: host needs credits\n", __func__); } } /* now we can fully dequeue */ packet = list_first_entry(&ep->txq, struct htc_packet, list); list_del(&packet->list); /* save the number of credits this packet consumed */ packet->info.tx.cred_used = credits_required; /* save send flags */ packet->info.tx.flags = send_flags; packet->info.tx.seqno = ep->seqno; ep->seqno++; /* queue this packet into the caller's queue */ list_add_tail(&packet->list, queue); } } static void get_htc_packet(struct htc_target *target, struct htc_endpoint *ep, struct list_head *queue, int resources) { struct htc_packet *packet; /* NOTE : the TX lock is held when this function is called */ /* loop until we can grab as many packets out of the queue as we can */ while (resources) { if (list_empty(&ep->txq)) break; packet = list_first_entry(&ep->txq, struct htc_packet, list); list_del(&packet->list); ath6kl_dbg(ATH6KL_DBG_HTC, "%s: got packet:0x%p , new queue depth: %d\n", __func__, packet, get_queue_depth(&ep->txq)); packet->info.tx.seqno = ep->seqno; packet->info.tx.flags = 0; packet->info.tx.cred_used = 0; ep->seqno++; /* queue this packet into the caller's queue */ list_add_tail(&packet->list, queue); resources--; } } static int htc_issue_packets(struct htc_target *target, struct htc_endpoint *ep, struct list_head *pkt_queue) { int status = 0; u16 payload_len; struct sk_buff *skb; struct htc_frame_hdr *htc_hdr; struct htc_packet *packet; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: queue: 0x%p, pkts %d\n", __func__, pkt_queue, get_queue_depth(pkt_queue)); while (!list_empty(pkt_queue)) { packet = list_first_entry(pkt_queue, struct htc_packet, list); list_del(&packet->list); skb = packet->skb; if (!skb) { WARN_ON_ONCE(1); status = -EINVAL; break; } payload_len = packet->act_len; /* setup HTC frame header */ htc_hdr = (struct htc_frame_hdr *) skb_push(skb, sizeof(*htc_hdr)); if (!htc_hdr) { WARN_ON_ONCE(1); status = -EINVAL; break; } packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF; /* Endianess? */ put_unaligned((u16) payload_len, &htc_hdr->payld_len); htc_hdr->flags = packet->info.tx.flags; htc_hdr->eid = (u8) packet->endpoint; htc_hdr->ctrl[0] = 0; htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno; spin_lock_bh(&target->tx_lock); /* store in look up queue to match completions */ list_add_tail(&packet->list, &ep->pipe.tx_lookup_queue); ep->ep_st.tx_issued += 1; spin_unlock_bh(&target->tx_lock); status = ath6kl_hif_pipe_send(target->dev->ar, ep->pipe.pipeid_ul, NULL, skb); if (status != 0) { if (status != -ENOMEM) { /* TODO: if more than 1 endpoint maps to the * same PipeID, it is possible to run out of * resources in the HIF layer. * Don't emit the error */ ath6kl_dbg(ATH6KL_DBG_HTC, "%s: failed status:%d\n", __func__, status); } spin_lock_bh(&target->tx_lock); list_del(&packet->list); /* reclaim credits */ ep->cred_dist.credits += packet->info.tx.cred_used; spin_unlock_bh(&target->tx_lock); /* put it back into the callers queue */ list_add(&packet->list, pkt_queue); break; } } if (status != 0) { while (!list_empty(pkt_queue)) { if (status != -ENOMEM) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: failed pkt:0x%p status:%d\n", __func__, packet, status); } packet = list_first_entry(pkt_queue, struct htc_packet, list); list_del(&packet->list); packet->status = status; send_packet_completion(target, packet); } } return status; } static enum htc_send_queue_result htc_try_send(struct htc_target *target, struct htc_endpoint *ep, struct list_head *txq) { struct list_head send_queue; /* temp queue to hold packets */ struct htc_packet *packet, *tmp_pkt; struct ath6kl *ar = target->dev->ar; enum htc_send_full_action action; int tx_resources, overflow, txqueue_depth, i, good_pkts; u8 pipeid; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: (queue:0x%p depth:%d)\n", __func__, txq, (txq == NULL) ? 0 : get_queue_depth(txq)); /* init the local send queue */ INIT_LIST_HEAD(&send_queue); /* * txq equals to NULL means * caller didn't provide a queue, just wants us to * check queues and send */ if (txq != NULL) { if (list_empty(txq)) { /* empty queue */ return HTC_SEND_QUEUE_DROP; } spin_lock_bh(&target->tx_lock); txqueue_depth = get_queue_depth(&ep->txq); spin_unlock_bh(&target->tx_lock); if (txqueue_depth >= ep->max_txq_depth) { /* we've already overflowed */ overflow = get_queue_depth(txq); } else { /* get how much we will overflow by */ overflow = txqueue_depth; overflow += get_queue_depth(txq); /* get how much we will overflow the TX queue by */ overflow -= ep->max_txq_depth; } /* if overflow is negative or zero, we are okay */ if (overflow > 0) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Endpoint %d, TX queue will overflow :%d, Tx Depth:%d, Max:%d\n", __func__, ep->eid, overflow, txqueue_depth, ep->max_txq_depth); } if ((overflow <= 0) || (ep->ep_cb.tx_full == NULL)) { /* * all packets will fit or caller did not provide send * full indication handler -- just move all of them * to the local send_queue object */ list_splice_tail_init(txq, &send_queue); } else { good_pkts = get_queue_depth(txq) - overflow; if (good_pkts < 0) { WARN_ON_ONCE(1); return HTC_SEND_QUEUE_DROP; } /* we have overflowed, and a callback is provided */ /* dequeue all non-overflow packets to the sendqueue */ for (i = 0; i < good_pkts; i++) { /* pop off caller's queue */ packet = list_first_entry(txq, struct htc_packet, list); list_del(&packet->list); /* insert into local queue */ list_add_tail(&packet->list, &send_queue); } /* * the caller's queue has all the packets that won't fit * walk through the caller's queue and indicate each to * the send full handler */ list_for_each_entry_safe(packet, tmp_pkt, txq, list) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Indicat overflowed TX pkts: %p\n", __func__, packet); action = ep->ep_cb.tx_full(ep->target, packet); if (action == HTC_SEND_FULL_DROP) { /* callback wants the packet dropped */ ep->ep_st.tx_dropped += 1; /* leave this one in the caller's queue * for cleanup */ } else { /* callback wants to keep this packet, * remove from caller's queue */ list_del(&packet->list); /* put it in the send queue */ list_add_tail(&packet->list, &send_queue); } } if (list_empty(&send_queue)) { /* no packets made it in, caller will cleanup */ return HTC_SEND_QUEUE_DROP; } } } if (!ep->pipe.tx_credit_flow_enabled) { tx_resources = ath6kl_hif_pipe_get_free_queue_number(ar, ep->pipe.pipeid_ul); } else { tx_resources = 0; } spin_lock_bh(&target->tx_lock); if (!list_empty(&send_queue)) { /* transfer packets to tail */ list_splice_tail_init(&send_queue, &ep->txq); if (!list_empty(&send_queue)) { WARN_ON_ONCE(1); spin_unlock_bh(&target->tx_lock); return HTC_SEND_QUEUE_DROP; } INIT_LIST_HEAD(&send_queue); } /* increment tx processing count on entry */ ep->tx_proc_cnt++; if (ep->tx_proc_cnt > 1) { /* * Another thread or task is draining the TX queues on this * endpoint that thread will reset the tx processing count * when the queue is drained. */ ep->tx_proc_cnt--; spin_unlock_bh(&target->tx_lock); return HTC_SEND_QUEUE_OK; } /***** beyond this point only 1 thread may enter ******/ /* * Now drain the endpoint TX queue for transmission as long as we have * enough transmit resources. */ while (true) { if (get_queue_depth(&ep->txq) == 0) break; if (ep->pipe.tx_credit_flow_enabled) { /* * Credit based mechanism provides flow control * based on target transmit resource availability, * we assume that the HIF layer will always have * bus resources greater than target transmit * resources. */ get_htc_packet_credit_based(target, ep, &send_queue); } else { /* * Get all packets for this endpoint that we can * for this pass. */ get_htc_packet(target, ep, &send_queue, tx_resources); } if (get_queue_depth(&send_queue) == 0) { /* * Didn't get packets due to out of resources or TX * queue was drained. */ break; } spin_unlock_bh(&target->tx_lock); /* send what we can */ htc_issue_packets(target, ep, &send_queue); if (!ep->pipe.tx_credit_flow_enabled) { pipeid = ep->pipe.pipeid_ul; tx_resources = ath6kl_hif_pipe_get_free_queue_number(ar, pipeid); } spin_lock_bh(&target->tx_lock); } /* done with this endpoint, we can clear the count */ ep->tx_proc_cnt = 0; spin_unlock_bh(&target->tx_lock); return HTC_SEND_QUEUE_OK; } /* htc control packet manipulation */ static void destroy_htc_txctrl_packet(struct htc_packet *packet) { struct sk_buff *skb; skb = packet->skb; if (skb != NULL) dev_kfree_skb(skb); kfree(packet); } static struct htc_packet *build_htc_txctrl_packet(void) { struct htc_packet *packet = NULL; struct sk_buff *skb; packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL); if (packet == NULL) return NULL; skb = __dev_alloc_skb(HTC_CONTROL_BUFFER_SIZE, GFP_KERNEL); if (skb == NULL) { kfree(packet); return NULL; } packet->skb = skb; return packet; } static void htc_free_txctrl_packet(struct htc_target *target, struct htc_packet *packet) { destroy_htc_txctrl_packet(packet); } static struct htc_packet *htc_alloc_txctrl_packet(struct htc_target *target) { return build_htc_txctrl_packet(); } static void htc_txctrl_complete(struct htc_target *target, struct htc_packet *packet) { htc_free_txctrl_packet(target, packet); } #define MAX_MESSAGE_SIZE 1536 static int htc_setup_target_buffer_assignments(struct htc_target *target) { int status, credits, credit_per_maxmsg, i; struct htc_pipe_txcredit_alloc *entry; unsigned int hif_usbaudioclass = 0; credit_per_maxmsg = MAX_MESSAGE_SIZE / target->tgt_cred_sz; if (MAX_MESSAGE_SIZE % target->tgt_cred_sz) credit_per_maxmsg++; /* TODO, this should be configured by the caller! */ credits = target->tgt_creds; entry = &target->pipe.txcredit_alloc[0]; status = -ENOMEM; /* FIXME: hif_usbaudioclass is always zero */ if (hif_usbaudioclass) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: For USB Audio Class- Total:%d\n", __func__, credits); entry++; entry++; /* Setup VO Service To have Max Credits */ entry->service_id = WMI_DATA_VO_SVC; entry->credit_alloc = (credits - 6); if (entry->credit_alloc == 0) entry->credit_alloc++; credits -= (int) entry->credit_alloc; if (credits <= 0) return status; entry++; entry->service_id = WMI_CONTROL_SVC; entry->credit_alloc = credit_per_maxmsg; credits -= (int) entry->credit_alloc; if (credits <= 0) return status; /* leftovers go to best effort */ entry++; entry++; entry->service_id = WMI_DATA_BE_SVC; entry->credit_alloc = (u8) credits; status = 0; } else { entry++; entry->service_id = WMI_DATA_VI_SVC; entry->credit_alloc = credits / 4; if (entry->credit_alloc == 0) entry->credit_alloc++; credits -= (int) entry->credit_alloc; if (credits <= 0) return status; entry++; entry->service_id = WMI_DATA_VO_SVC; entry->credit_alloc = credits / 4; if (entry->credit_alloc == 0) entry->credit_alloc++; credits -= (int) entry->credit_alloc; if (credits <= 0) return status; entry++; entry->service_id = WMI_CONTROL_SVC; entry->credit_alloc = credit_per_maxmsg; credits -= (int) entry->credit_alloc; if (credits <= 0) return status; entry++; entry->service_id = WMI_DATA_BK_SVC; entry->credit_alloc = credit_per_maxmsg; credits -= (int) entry->credit_alloc; if (credits <= 0) return status; /* leftovers go to best effort */ entry++; entry->service_id = WMI_DATA_BE_SVC; entry->credit_alloc = (u8) credits; status = 0; } if (status == 0) { for (i = 0; i < ENDPOINT_MAX; i++) { if (target->pipe.txcredit_alloc[i].service_id != 0) { ath6kl_dbg(ATH6KL_DBG_HTC, "HTC Service Index : %d TX : 0x%2.2X : alloc:%d\n", i, target->pipe.txcredit_alloc[i]. service_id, target->pipe.txcredit_alloc[i]. credit_alloc); } } } return status; } /* process credit reports and call distribution function */ static void htc_process_credit_report(struct htc_target *target, struct htc_credit_report *rpt, int num_entries, enum htc_endpoint_id from_ep) { int total_credits = 0, i; struct htc_endpoint *ep; /* lock out TX while we update credits */ spin_lock_bh(&target->tx_lock); for (i = 0; i < num_entries; i++, rpt++) { if (rpt->eid >= ENDPOINT_MAX) { WARN_ON_ONCE(1); spin_unlock_bh(&target->tx_lock); return; } ep = &target->endpoint[rpt->eid]; ep->cred_dist.credits += rpt->credits; if (ep->cred_dist.credits && get_queue_depth(&ep->txq)) { spin_unlock_bh(&target->tx_lock); htc_try_send(target, ep, NULL); spin_lock_bh(&target->tx_lock); } total_credits += rpt->credits; } ath6kl_dbg(ATH6KL_DBG_HTC, "Report indicated %d credits to distribute\n", total_credits); spin_unlock_bh(&target->tx_lock); } /* flush endpoint TX queue */ static void htc_flush_tx_endpoint(struct htc_target *target, struct htc_endpoint *ep, u16 tag) { struct htc_packet *packet; spin_lock_bh(&target->tx_lock); while (get_queue_depth(&ep->txq)) { packet = list_first_entry(&ep->txq, struct htc_packet, list); list_del(&packet->list); packet->status = 0; send_packet_completion(target, packet); } spin_unlock_bh(&target->tx_lock); } /* * In the adapted HIF layer, struct sk_buff * are passed between HIF and HTC, * since upper layers expects struct htc_packet containers we use the completed * skb and lookup it's corresponding HTC packet buffer from a lookup list. * This is extra overhead that can be fixed by re-aligning HIF interfaces with * HTC. */ static struct htc_packet *htc_lookup_tx_packet(struct htc_target *target, struct htc_endpoint *ep, struct sk_buff *skb) { struct htc_packet *packet, *tmp_pkt, *found_packet = NULL; spin_lock_bh(&target->tx_lock); /* * interate from the front of tx lookup queue * this lookup should be fast since lower layers completes in-order and * so the completed packet should be at the head of the list generally */ list_for_each_entry_safe(packet, tmp_pkt, &ep->pipe.tx_lookup_queue, list) { /* check for removal */ if (skb == packet->skb) { /* found it */ list_del(&packet->list); found_packet = packet; break; } } spin_unlock_bh(&target->tx_lock); return found_packet; } static int ath6kl_htc_pipe_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { struct htc_target *target = ar->htc_target; struct htc_frame_hdr *htc_hdr; struct htc_endpoint *ep; struct htc_packet *packet; u8 ep_id, *netdata; u32 netlen; netdata = skb->data; netlen = skb->len; htc_hdr = (struct htc_frame_hdr *) netdata; ep_id = htc_hdr->eid; ep = &target->endpoint[ep_id]; packet = htc_lookup_tx_packet(target, ep, skb); if (packet == NULL) { /* may have already been flushed and freed */ ath6kl_err("HTC TX lookup failed!\n"); } else { /* will be giving this buffer back to upper layers */ packet->status = 0; send_packet_completion(target, packet); } skb = NULL; if (!ep->pipe.tx_credit_flow_enabled) { /* * note: when using TX credit flow, the re-checking of queues * happens when credits flow back from the target. in the * non-TX credit case, we recheck after the packet completes */ htc_try_send(target, ep, NULL); } return 0; } static int htc_send_packets_multiple(struct htc_target *target, struct list_head *pkt_queue) { struct htc_endpoint *ep; struct htc_packet *packet, *tmp_pkt; if (list_empty(pkt_queue)) return -EINVAL; /* get first packet to find out which ep the packets will go into */ packet = list_first_entry(pkt_queue, struct htc_packet, list); if (packet->endpoint >= ENDPOINT_MAX) { WARN_ON_ONCE(1); return -EINVAL; } ep = &target->endpoint[packet->endpoint]; htc_try_send(target, ep, pkt_queue); /* do completion on any packets that couldn't get in */ if (!list_empty(pkt_queue)) { list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { packet->status = -ENOMEM; } do_send_completion(ep, pkt_queue); } return 0; } /* htc pipe rx path */ static struct htc_packet *alloc_htc_packet_container(struct htc_target *target) { struct htc_packet *packet; spin_lock_bh(&target->rx_lock); if (target->pipe.htc_packet_pool == NULL) { spin_unlock_bh(&target->rx_lock); return NULL; } packet = target->pipe.htc_packet_pool; target->pipe.htc_packet_pool = (struct htc_packet *) packet->list.next; spin_unlock_bh(&target->rx_lock); packet->list.next = NULL; return packet; } static void free_htc_packet_container(struct htc_target *target, struct htc_packet *packet) { struct list_head *lh; spin_lock_bh(&target->rx_lock); if (target->pipe.htc_packet_pool == NULL) { target->pipe.htc_packet_pool = packet; packet->list.next = NULL; } else { lh = (struct list_head *) target->pipe.htc_packet_pool; packet->list.next = lh; target->pipe.htc_packet_pool = packet; } spin_unlock_bh(&target->rx_lock); } static int htc_process_trailer(struct htc_target *target, u8 *buffer, int len, enum htc_endpoint_id from_ep) { struct htc_credit_report *report; struct htc_record_hdr *record; u8 *record_buf, *orig_buf; int orig_len, status; orig_buf = buffer; orig_len = len; status = 0; while (len > 0) { if (len < sizeof(struct htc_record_hdr)) { status = -EINVAL; break; } /* these are byte aligned structs */ record = (struct htc_record_hdr *) buffer; len -= sizeof(struct htc_record_hdr); buffer += sizeof(struct htc_record_hdr); if (record->len > len) { /* no room left in buffer for record */ ath6kl_dbg(ATH6KL_DBG_HTC, "invalid length: %d (id:%d) buffer has: %d bytes left\n", record->len, record->rec_id, len); status = -EINVAL; break; } /* start of record follows the header */ record_buf = buffer; switch (record->rec_id) { case HTC_RECORD_CREDITS: if (record->len < sizeof(struct htc_credit_report)) { WARN_ON_ONCE(1); return -EINVAL; } report = (struct htc_credit_report *) record_buf; htc_process_credit_report(target, report, record->len / sizeof(*report), from_ep); break; default: ath6kl_dbg(ATH6KL_DBG_HTC, "unhandled record: id:%d length:%d\n", record->rec_id, record->len); break; } if (status != 0) break; /* advance buffer past this record for next time around */ buffer += record->len; len -= record->len; } return status; } static void do_recv_completion(struct htc_endpoint *ep, struct list_head *queue_to_indicate) { struct htc_packet *packet; if (list_empty(queue_to_indicate)) { /* nothing to indicate */ return; } /* using legacy EpRecv */ while (!list_empty(queue_to_indicate)) { packet = list_first_entry(queue_to_indicate, struct htc_packet, list); list_del(&packet->list); ep->ep_cb.rx(ep->target, packet); } return; } static void recv_packet_completion(struct htc_target *target, struct htc_endpoint *ep, struct htc_packet *packet) { struct list_head container; INIT_LIST_HEAD(&container); list_add_tail(&packet->list, &container); /* do completion */ do_recv_completion(ep, &container); } static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipeid) { struct htc_target *target = ar->htc_target; u8 *netdata, *trailer, hdr_info; struct htc_frame_hdr *htc_hdr; u32 netlen, trailerlen = 0; struct htc_packet *packet; struct htc_endpoint *ep; u16 payload_len; int status = 0; netdata = skb->data; netlen = skb->len; htc_hdr = (struct htc_frame_hdr *) netdata; ep = &target->endpoint[htc_hdr->eid]; if (htc_hdr->eid >= ENDPOINT_MAX) { ath6kl_dbg(ATH6KL_DBG_HTC, "HTC Rx: invalid EndpointID=%d\n", htc_hdr->eid); status = -EINVAL; goto free_skb; } payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len)); if (netlen < (payload_len + HTC_HDR_LENGTH)) { ath6kl_dbg(ATH6KL_DBG_HTC, "HTC Rx: insufficient length, got:%d expected =%u\n", netlen, payload_len + HTC_HDR_LENGTH); status = -EINVAL; goto free_skb; } /* get flags to check for trailer */ hdr_info = htc_hdr->flags; if (hdr_info & HTC_FLG_RX_TRAILER) { /* extract the trailer length */ hdr_info = htc_hdr->ctrl[0]; if ((hdr_info < sizeof(struct htc_record_hdr)) || (hdr_info > payload_len)) { ath6kl_dbg(ATH6KL_DBG_HTC, "invalid header: payloadlen should be %d, CB[0]: %d\n", payload_len, hdr_info); status = -EINVAL; goto free_skb; } trailerlen = hdr_info; /* process trailer after hdr/apps payload */ trailer = (u8 *) htc_hdr + HTC_HDR_LENGTH + payload_len - hdr_info; status = htc_process_trailer(target, trailer, hdr_info, htc_hdr->eid); if (status != 0) goto free_skb; } if (((int) payload_len - (int) trailerlen) <= 0) { /* zero length packet with trailer, just drop these */ goto free_skb; } if (htc_hdr->eid == ENDPOINT_0) { /* handle HTC control message */ if (target->htc_flags & HTC_OP_STATE_SETUP_COMPLETE) { /* * fatal: target should not send unsolicited * messageson the endpoint 0 */ ath6kl_dbg(ATH6KL_DBG_HTC, "HTC ignores Rx Ctrl after setup complete\n"); status = -EINVAL; goto free_skb; } /* remove HTC header */ skb_pull(skb, HTC_HDR_LENGTH); netdata = skb->data; netlen = skb->len; spin_lock_bh(&target->rx_lock); target->pipe.ctrl_response_valid = true; target->pipe.ctrl_response_len = min_t(int, netlen, HTC_MAX_CTRL_MSG_LEN); memcpy(target->pipe.ctrl_response_buf, netdata, target->pipe.ctrl_response_len); spin_unlock_bh(&target->rx_lock); dev_kfree_skb(skb); skb = NULL; goto free_skb; } /* * TODO: the message based HIF architecture allocates net bufs * for recv packets since it bridges that HIF to upper layers, * which expects HTC packets, we form the packets here */ packet = alloc_htc_packet_container(target); if (packet == NULL) { status = -ENOMEM; goto free_skb; } packet->status = 0; packet->endpoint = htc_hdr->eid; packet->pkt_cntxt = skb; /* TODO: for backwards compatibility */ packet->buf = skb_push(skb, 0) + HTC_HDR_LENGTH; packet->act_len = netlen - HTC_HDR_LENGTH - trailerlen; /* * TODO: this is a hack because the driver layer will set the * actual len of the skb again which will just double the len */ skb_trim(skb, 0); recv_packet_completion(target, ep, packet); /* recover the packet container */ free_htc_packet_container(target, packet); skb = NULL; free_skb: if (skb != NULL) dev_kfree_skb(skb); return status; } static void htc_flush_rx_queue(struct htc_target *target, struct htc_endpoint *ep) { struct list_head container; struct htc_packet *packet; spin_lock_bh(&target->rx_lock); while (1) { if (list_empty(&ep->rx_bufq)) break; packet = list_first_entry(&ep->rx_bufq, struct htc_packet, list); list_del(&packet->list); spin_unlock_bh(&target->rx_lock); packet->status = -ECANCELED; packet->act_len = 0; ath6kl_dbg(ATH6KL_DBG_HTC, "Flushing RX packet:0x%p, length:%d, ep:%d\n", packet, packet->buf_len, packet->endpoint); INIT_LIST_HEAD(&container); list_add_tail(&packet->list, &container); /* give the packet back */ do_recv_completion(ep, &container); spin_lock_bh(&target->rx_lock); } spin_unlock_bh(&target->rx_lock); } /* polling routine to wait for a control packet to be received */ static int htc_wait_recv_ctrl_message(struct htc_target *target) { int count = HTC_TARGET_RESPONSE_POLL_COUNT; while (count > 0) { spin_lock_bh(&target->rx_lock); if (target->pipe.ctrl_response_valid) { target->pipe.ctrl_response_valid = false; spin_unlock_bh(&target->rx_lock); break; } spin_unlock_bh(&target->rx_lock); count--; msleep_interruptible(HTC_TARGET_RESPONSE_POLL_WAIT); } if (count <= 0) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Timeout!\n", __func__); return -ECOMM; } return 0; } static void htc_rxctrl_complete(struct htc_target *context, struct htc_packet *packet) { /* TODO, can't really receive HTC control messages yet.... */ ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__); } /* htc pipe initialization */ static void reset_endpoint_states(struct htc_target *target) { struct htc_endpoint *ep; int i; for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { ep = &target->endpoint[i]; ep->svc_id = 0; ep->len_max = 0; ep->max_txq_depth = 0; ep->eid = i; INIT_LIST_HEAD(&ep->txq); INIT_LIST_HEAD(&ep->pipe.tx_lookup_queue); INIT_LIST_HEAD(&ep->rx_bufq); ep->target = target; ep->pipe.tx_credit_flow_enabled = (bool) 1; /* FIXME */ } } /* start HTC, this is called after all services are connected */ static int htc_config_target_hif_pipe(struct htc_target *target) { return 0; } /* htc service functions */ static u8 htc_get_credit_alloc(struct htc_target *target, u16 service_id) { u8 allocation = 0; int i; for (i = 0; i < ENDPOINT_MAX; i++) { if (target->pipe.txcredit_alloc[i].service_id == service_id) allocation = target->pipe.txcredit_alloc[i].credit_alloc; } if (allocation == 0) { ath6kl_dbg(ATH6KL_DBG_HTC, "HTC Service TX : 0x%2.2X : allocation is zero!\n", service_id); } return allocation; } static int ath6kl_htc_pipe_conn_service(struct htc_target *target, struct htc_service_connect_req *conn_req, struct htc_service_connect_resp *conn_resp) { struct ath6kl *ar = target->dev->ar; struct htc_packet *packet = NULL; struct htc_conn_service_resp *resp_msg; struct htc_conn_service_msg *conn_msg; enum htc_endpoint_id assigned_epid = ENDPOINT_MAX; bool disable_credit_flowctrl = false; unsigned int max_msg_size = 0; struct htc_endpoint *ep; int length, status = 0; struct sk_buff *skb; u8 tx_alloc; u16 flags; if (conn_req->svc_id == 0) { WARN_ON_ONCE(1); status = -EINVAL; goto free_packet; } if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) { /* special case for pseudo control service */ assigned_epid = ENDPOINT_0; max_msg_size = HTC_MAX_CTRL_MSG_LEN; tx_alloc = 0; } else { tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id); if (tx_alloc == 0) { status = -ENOMEM; goto free_packet; } /* allocate a packet to send to the target */ packet = htc_alloc_txctrl_packet(target); if (packet == NULL) { WARN_ON_ONCE(1); status = -ENOMEM; goto free_packet; } skb = packet->skb; length = sizeof(struct htc_conn_service_msg); /* assemble connect service message */ conn_msg = (struct htc_conn_service_msg *) skb_put(skb, length); if (conn_msg == NULL) { WARN_ON_ONCE(1); status = -EINVAL; goto free_packet; } memset(conn_msg, 0, sizeof(struct htc_conn_service_msg)); conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID); conn_msg->svc_id = cpu_to_le16(conn_req->svc_id); conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags & ~HTC_CONN_FLGS_SET_RECV_ALLOC_MASK); /* tell target desired recv alloc for this ep */ flags = tx_alloc << HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT; conn_msg->conn_flags |= cpu_to_le16(flags); if (conn_req->conn_flags & HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL) { disable_credit_flowctrl = true; } set_htc_pkt_info(packet, NULL, (u8 *) conn_msg, length, ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); status = ath6kl_htc_pipe_tx(target, packet); /* we don't own it anymore */ packet = NULL; if (status != 0) goto free_packet; /* wait for response */ status = htc_wait_recv_ctrl_message(target); if (status != 0) goto free_packet; /* we controlled the buffer creation so it has to be * properly aligned */ resp_msg = (struct htc_conn_service_resp *) target->pipe.ctrl_response_buf; if (resp_msg->msg_id != cpu_to_le16(HTC_MSG_CONN_SVC_RESP_ID) || (target->pipe.ctrl_response_len < sizeof(*resp_msg))) { /* this message is not valid */ WARN_ON_ONCE(1); status = -EINVAL; goto free_packet; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: service 0x%X conn resp: status: %d ep: %d\n", __func__, resp_msg->svc_id, resp_msg->status, resp_msg->eid); conn_resp->resp_code = resp_msg->status; /* check response status */ if (resp_msg->status != HTC_SERVICE_SUCCESS) { ath6kl_dbg(ATH6KL_DBG_HTC, "Target failed service 0x%X connect request (status:%d)\n", resp_msg->svc_id, resp_msg->status); status = -EINVAL; goto free_packet; } assigned_epid = (enum htc_endpoint_id) resp_msg->eid; max_msg_size = le16_to_cpu(resp_msg->max_msg_sz); } /* the rest are parameter checks so set the error status */ status = -EINVAL; if (assigned_epid >= ENDPOINT_MAX) { WARN_ON_ONCE(1); goto free_packet; } if (max_msg_size == 0) { WARN_ON_ONCE(1); goto free_packet; } ep = &target->endpoint[assigned_epid]; ep->eid = assigned_epid; if (ep->svc_id != 0) { /* endpoint already in use! */ WARN_ON_ONCE(1); goto free_packet; } /* return assigned endpoint to caller */ conn_resp->endpoint = assigned_epid; conn_resp->len_max = max_msg_size; /* setup the endpoint */ ep->svc_id = conn_req->svc_id; /* this marks ep in use */ ep->max_txq_depth = conn_req->max_txq_depth; ep->len_max = max_msg_size; ep->cred_dist.credits = tx_alloc; ep->cred_dist.cred_sz = target->tgt_cred_sz; ep->cred_dist.cred_per_msg = max_msg_size / target->tgt_cred_sz; if (max_msg_size % target->tgt_cred_sz) ep->cred_dist.cred_per_msg++; /* copy all the callbacks */ ep->ep_cb = conn_req->ep_cb; /* initialize tx_drop_packet_threshold */ ep->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; status = ath6kl_hif_pipe_map_service(ar, ep->svc_id, &ep->pipe.pipeid_ul, &ep->pipe.pipeid_dl); if (status != 0) goto free_packet; ath6kl_dbg(ATH6KL_DBG_HTC, "SVC Ready: 0x%4.4X: ULpipe:%d DLpipe:%d id:%d\n", ep->svc_id, ep->pipe.pipeid_ul, ep->pipe.pipeid_dl, ep->eid); if (disable_credit_flowctrl && ep->pipe.tx_credit_flow_enabled) { ep->pipe.tx_credit_flow_enabled = false; ath6kl_dbg(ATH6KL_DBG_HTC, "SVC: 0x%4.4X ep:%d TX flow control off\n", ep->svc_id, assigned_epid); } free_packet: if (packet != NULL) htc_free_txctrl_packet(target, packet); return status; } /* htc export functions */ static void *ath6kl_htc_pipe_create(struct ath6kl *ar) { int status = 0; struct htc_endpoint *ep = NULL; struct htc_target *target = NULL; struct htc_packet *packet; int i; target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); if (target == NULL) { ath6kl_err("htc create unable to allocate memory\n"); status = -ENOMEM; goto fail_htc_create; } spin_lock_init(&target->htc_lock); spin_lock_init(&target->rx_lock); spin_lock_init(&target->tx_lock); reset_endpoint_states(target); for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) { packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL); if (packet != NULL) free_htc_packet_container(target, packet); } target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL); if (!target->dev) { ath6kl_err("unable to allocate memory\n"); status = -ENOMEM; goto fail_htc_create; } target->dev->ar = ar; target->dev->htc_cnxt = target; /* Get HIF default pipe for HTC message exchange */ ep = &target->endpoint[ENDPOINT_0]; ath6kl_hif_pipe_get_default(ar, &ep->pipe.pipeid_ul, &ep->pipe.pipeid_dl); return target; fail_htc_create: if (status != 0) { if (target != NULL) ath6kl_htc_pipe_cleanup(target); target = NULL; } return target; } /* cleanup the HTC instance */ static void ath6kl_htc_pipe_cleanup(struct htc_target *target) { struct htc_packet *packet; while (true) { packet = alloc_htc_packet_container(target); if (packet == NULL) break; kfree(packet); } kfree(target->dev); /* kfree our instance */ kfree(target); } static int ath6kl_htc_pipe_start(struct htc_target *target) { struct sk_buff *skb; struct htc_setup_comp_ext_msg *setup; struct htc_packet *packet; htc_config_target_hif_pipe(target); /* allocate a buffer to send */ packet = htc_alloc_txctrl_packet(target); if (packet == NULL) { WARN_ON_ONCE(1); return -ENOMEM; } skb = packet->skb; /* assemble setup complete message */ setup = (struct htc_setup_comp_ext_msg *) skb_put(skb, sizeof(*setup)); memset(setup, 0, sizeof(struct htc_setup_comp_ext_msg)); setup->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID); ath6kl_dbg(ATH6KL_DBG_HTC, "HTC using TX credit flow control\n"); set_htc_pkt_info(packet, NULL, (u8 *) setup, sizeof(struct htc_setup_comp_ext_msg), ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); target->htc_flags |= HTC_OP_STATE_SETUP_COMPLETE; return ath6kl_htc_pipe_tx(target, packet); } static void ath6kl_htc_pipe_stop(struct htc_target *target) { int i; struct htc_endpoint *ep; /* cleanup endpoints */ for (i = 0; i < ENDPOINT_MAX; i++) { ep = &target->endpoint[i]; htc_flush_rx_queue(target, ep); htc_flush_tx_endpoint(target, ep, HTC_TX_PACKET_TAG_ALL); } reset_endpoint_states(target); target->htc_flags &= ~HTC_OP_STATE_SETUP_COMPLETE; } static int ath6kl_htc_pipe_get_rxbuf_num(struct htc_target *target, enum htc_endpoint_id endpoint) { int num; spin_lock_bh(&target->rx_lock); num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq)); spin_unlock_bh(&target->rx_lock); return num; } static int ath6kl_htc_pipe_tx(struct htc_target *target, struct htc_packet *packet) { struct list_head queue; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: endPointId: %d, buffer: 0x%p, length: %d\n", __func__, packet->endpoint, packet->buf, packet->act_len); INIT_LIST_HEAD(&queue); list_add_tail(&packet->list, &queue); return htc_send_packets_multiple(target, &queue); } static int ath6kl_htc_pipe_wait_target(struct htc_target *target) { struct htc_ready_ext_msg *ready_msg; struct htc_service_connect_req connect; struct htc_service_connect_resp resp; int status = 0; status = htc_wait_recv_ctrl_message(target); if (status != 0) return status; if (target->pipe.ctrl_response_len < sizeof(*ready_msg)) { ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg len:%d!\n", target->pipe.ctrl_response_len); return -ECOMM; } ready_msg = (struct htc_ready_ext_msg *) target->pipe.ctrl_response_buf; if (ready_msg->ver2_0_info.msg_id != cpu_to_le16(HTC_MSG_READY_ID)) { ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg : 0x%X !\n", ready_msg->ver2_0_info.msg_id); return -ECOMM; } ath6kl_dbg(ATH6KL_DBG_HTC, "Target Ready! : transmit resources : %d size:%d\n", ready_msg->ver2_0_info.cred_cnt, ready_msg->ver2_0_info.cred_sz); target->tgt_creds = le16_to_cpu(ready_msg->ver2_0_info.cred_cnt); target->tgt_cred_sz = le16_to_cpu(ready_msg->ver2_0_info.cred_sz); if ((target->tgt_creds == 0) || (target->tgt_cred_sz == 0)) return -ECOMM; htc_setup_target_buffer_assignments(target); /* setup our pseudo HTC control endpoint connection */ memset(&connect, 0, sizeof(connect)); memset(&resp, 0, sizeof(resp)); connect.ep_cb.tx_complete = htc_txctrl_complete; connect.ep_cb.rx = htc_rxctrl_complete; connect.max_txq_depth = NUM_CONTROL_TX_BUFFERS; connect.svc_id = HTC_CTRL_RSVD_SVC; /* connect fake service */ status = ath6kl_htc_pipe_conn_service(target, &connect, &resp); return status; } static void ath6kl_htc_pipe_flush_txep(struct htc_target *target, enum htc_endpoint_id endpoint, u16 tag) { struct htc_endpoint *ep = &target->endpoint[endpoint]; if (ep->svc_id == 0) { WARN_ON_ONCE(1); /* not in use.. */ return; } htc_flush_tx_endpoint(target, ep, tag); } static int ath6kl_htc_pipe_add_rxbuf_multiple(struct htc_target *target, struct list_head *pkt_queue) { struct htc_packet *packet, *tmp_pkt, *first; struct htc_endpoint *ep; int status = 0; if (list_empty(pkt_queue)) return -EINVAL; first = list_first_entry(pkt_queue, struct htc_packet, list); if (first->endpoint >= ENDPOINT_MAX) { WARN_ON_ONCE(1); return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_HTC, "%s: epid: %d, cnt:%d, len: %d\n", __func__, first->endpoint, get_queue_depth(pkt_queue), first->buf_len); ep = &target->endpoint[first->endpoint]; spin_lock_bh(&target->rx_lock); /* store receive packets */ list_splice_tail_init(pkt_queue, &ep->rx_bufq); spin_unlock_bh(&target->rx_lock); if (status != 0) { /* walk through queue and mark each one canceled */ list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { packet->status = -ECANCELED; } do_recv_completion(ep, pkt_queue); } return status; } static void ath6kl_htc_pipe_activity_changed(struct htc_target *target, enum htc_endpoint_id ep, bool active) { /* TODO */ } static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target) { /* TODO */ } static int ath6kl_htc_pipe_credit_setup(struct htc_target *target, struct ath6kl_htc_credit_info *info) { return 0; } static const struct ath6kl_htc_ops ath6kl_htc_pipe_ops = { .create = ath6kl_htc_pipe_create, .wait_target = ath6kl_htc_pipe_wait_target, .start = ath6kl_htc_pipe_start, .conn_service = ath6kl_htc_pipe_conn_service, .tx = ath6kl_htc_pipe_tx, .stop = ath6kl_htc_pipe_stop, .cleanup = ath6kl_htc_pipe_cleanup, .flush_txep = ath6kl_htc_pipe_flush_txep, .flush_rx_buf = ath6kl_htc_pipe_flush_rx_buf, .activity_changed = ath6kl_htc_pipe_activity_changed, .get_rxbuf_num = ath6kl_htc_pipe_get_rxbuf_num, .add_rxbuf_multiple = ath6kl_htc_pipe_add_rxbuf_multiple, .credit_setup = ath6kl_htc_pipe_credit_setup, .tx_complete = ath6kl_htc_pipe_tx_complete, .rx_complete = ath6kl_htc_pipe_rx_complete, }; void ath6kl_htc_pipe_attach(struct ath6kl *ar) { ar->htc_ops = &ath6kl_htc_pipe_ops; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/wmi.h0000644000175000017500000016520212026211315023401 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This file contains the definitions of the WMI protocol specified in the * Wireless Module Interface (WMI). It includes definitions of all the * commands and events. Commands are messages from the host to the WM. * Events and Replies are messages from the WM to the host. */ #ifndef WMI_H #define WMI_H #include #include "htc.h" #define HTC_PROTOCOL_VERSION 0x0002 #define WMI_PROTOCOL_VERSION 0x0002 #define WMI_CONTROL_MSG_MAX_LEN 256 #define is_ethertype(type_or_len) ((type_or_len) >= 0x0600) #define IP_ETHERTYPE 0x0800 #define WMI_IMPLICIT_PSTREAM 0xFF #define WMI_MAX_THINSTREAM 15 #define SSID_IE_LEN_INDEX 13 /* Host side link management data structures */ #define SIG_QUALITY_THRESH_LVLS 6 #define SIG_QUALITY_UPPER_THRESH_LVLS SIG_QUALITY_THRESH_LVLS #define SIG_QUALITY_LOWER_THRESH_LVLS SIG_QUALITY_THRESH_LVLS #define A_BAND_24GHZ 0 #define A_BAND_5GHZ 1 #define A_NUM_BANDS 2 /* in ms */ #define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000 /* * There are no signed versions of __le16 and __le32, so for a temporary * solution come up with our own version. The idea is from fs/ntfs/types.h. * * Use a_ prefix so that it doesn't conflict if we get proper support to * linux/types.h. */ typedef __s16 __bitwise a_sle16; typedef __s32 __bitwise a_sle32; static inline a_sle32 a_cpu_to_sle32(s32 val) { return (__force a_sle32) cpu_to_le32(val); } static inline s32 a_sle32_to_cpu(a_sle32 val) { return le32_to_cpu((__force __le32) val); } static inline a_sle16 a_cpu_to_sle16(s16 val) { return (__force a_sle16) cpu_to_le16(val); } static inline s16 a_sle16_to_cpu(a_sle16 val) { return le16_to_cpu((__force __le16) val); } struct sq_threshold_params { s16 upper_threshold[SIG_QUALITY_UPPER_THRESH_LVLS]; s16 lower_threshold[SIG_QUALITY_LOWER_THRESH_LVLS]; u32 upper_threshold_valid_count; u32 lower_threshold_valid_count; u32 polling_interval; u8 weight; u8 last_rssi; u8 last_rssi_poll_event; }; struct wmi_data_sync_bufs { u8 traffic_class; struct sk_buff *skb; }; /* WMM stream classes */ #define WMM_NUM_AC 4 #define WMM_AC_BE 0 /* best effort */ #define WMM_AC_BK 1 /* background */ #define WMM_AC_VI 2 /* video */ #define WMM_AC_VO 3 /* voice */ #define WMI_VOICE_USER_PRIORITY 0x7 struct wmi { u16 stream_exist_for_ac[WMM_NUM_AC]; u8 fat_pipe_exist; struct ath6kl *parent_dev; u8 pwr_mode; /* protects fat_pipe_exist and stream_exist_for_ac */ spinlock_t lock; enum htc_endpoint_id ep_id; struct sq_threshold_params sq_threshld[SIGNAL_QUALITY_METRICS_NUM_MAX]; bool is_wmm_enabled; u8 traffic_class; bool is_probe_ssid; u8 *last_mgmt_tx_frame; size_t last_mgmt_tx_frame_len; u8 saved_pwr_mode; }; struct host_app_area { __le32 wmi_protocol_ver; } __packed; enum wmi_msg_type { DATA_MSGTYPE = 0x0, CNTL_MSGTYPE, SYNC_MSGTYPE, OPT_MSGTYPE, }; /* * Macros for operating on WMI_DATA_HDR (info) field */ #define WMI_DATA_HDR_MSG_TYPE_MASK 0x03 #define WMI_DATA_HDR_MSG_TYPE_SHIFT 0 #define WMI_DATA_HDR_UP_MASK 0x07 #define WMI_DATA_HDR_UP_SHIFT 2 /* In AP mode, the same bit (b5) is used to indicate Power save state in * the Rx dir and More data bit state in the tx direction. */ #define WMI_DATA_HDR_PS_MASK 0x1 #define WMI_DATA_HDR_PS_SHIFT 5 #define WMI_DATA_HDR_MORE 0x20 enum wmi_data_hdr_data_type { WMI_DATA_HDR_DATA_TYPE_802_3 = 0, WMI_DATA_HDR_DATA_TYPE_802_11, /* used to be used for the PAL */ WMI_DATA_HDR_DATA_TYPE_ACL, }; /* Bitmap of data header flags */ enum wmi_data_hdr_flags { WMI_DATA_HDR_FLAGS_MORE = 0x1, WMI_DATA_HDR_FLAGS_EOSP = 0x2, WMI_DATA_HDR_FLAGS_UAPSD = 0x4, }; #define WMI_DATA_HDR_DATA_TYPE_MASK 0x3 #define WMI_DATA_HDR_DATA_TYPE_SHIFT 6 /* Macros for operating on WMI_DATA_HDR (info2) field */ #define WMI_DATA_HDR_SEQNO_MASK 0xFFF #define WMI_DATA_HDR_SEQNO_SHIFT 0 #define WMI_DATA_HDR_AMSDU_MASK 0x1 #define WMI_DATA_HDR_AMSDU_SHIFT 12 #define WMI_DATA_HDR_META_MASK 0x7 #define WMI_DATA_HDR_META_SHIFT 13 #define WMI_DATA_HDR_PAD_BEFORE_DATA_MASK 0xFF #define WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT 0x8 /* Macros for operating on WMI_DATA_HDR (info3) field */ #define WMI_DATA_HDR_IF_IDX_MASK 0xF #define WMI_DATA_HDR_TRIG 0x10 #define WMI_DATA_HDR_EOSP 0x10 struct wmi_data_hdr { s8 rssi; /* * usage of 'info' field(8-bit): * * b1:b0 - WMI_MSG_TYPE * b4:b3:b2 - UP(tid) * b5 - Used in AP mode. * More-data in tx dir, PS in rx. * b7:b6 - Dot3 header(0), * Dot11 Header(1), * ACL data(2) */ u8 info; /* * usage of 'info2' field(16-bit): * * b11:b0 - seq_no * b12 - A-MSDU? * b15:b13 - META_DATA_VERSION 0 - 7 */ __le16 info2; /* * usage of info3, 16-bit: * b3:b0 - Interface index * b4 - uAPSD trigger in rx & EOSP in tx * b15:b5 - Reserved */ __le16 info3; } __packed; static inline u8 wmi_data_hdr_get_up(struct wmi_data_hdr *dhdr) { return (dhdr->info >> WMI_DATA_HDR_UP_SHIFT) & WMI_DATA_HDR_UP_MASK; } static inline void wmi_data_hdr_set_up(struct wmi_data_hdr *dhdr, u8 usr_pri) { dhdr->info &= ~(WMI_DATA_HDR_UP_MASK << WMI_DATA_HDR_UP_SHIFT); dhdr->info |= usr_pri << WMI_DATA_HDR_UP_SHIFT; } static inline u8 wmi_data_hdr_get_dot11(struct wmi_data_hdr *dhdr) { u8 data_type; data_type = (dhdr->info >> WMI_DATA_HDR_DATA_TYPE_SHIFT) & WMI_DATA_HDR_DATA_TYPE_MASK; return (data_type == WMI_DATA_HDR_DATA_TYPE_802_11); } static inline u16 wmi_data_hdr_get_seqno(struct wmi_data_hdr *dhdr) { return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_SEQNO_SHIFT) & WMI_DATA_HDR_SEQNO_MASK; } static inline u8 wmi_data_hdr_is_amsdu(struct wmi_data_hdr *dhdr) { return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_AMSDU_SHIFT) & WMI_DATA_HDR_AMSDU_MASK; } static inline u8 wmi_data_hdr_get_meta(struct wmi_data_hdr *dhdr) { return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_META_SHIFT) & WMI_DATA_HDR_META_MASK; } static inline u8 wmi_data_hdr_get_if_idx(struct wmi_data_hdr *dhdr) { return le16_to_cpu(dhdr->info3) & WMI_DATA_HDR_IF_IDX_MASK; } /* Tx meta version definitions */ #define WMI_MAX_TX_META_SZ 12 #define WMI_META_VERSION_1 0x01 #define WMI_META_VERSION_2 0x02 /* Flag to signal to FW to calculate TCP checksum */ #define WMI_META_V2_FLAG_CSUM_OFFLOAD 0x01 struct wmi_tx_meta_v1 { /* packet ID to identify the tx request */ u8 pkt_id; /* rate policy to be used for the tx of this frame */ u8 rate_plcy_id; } __packed; struct wmi_tx_meta_v2 { /* * Offset from start of the WMI header for csum calculation to * begin. */ u8 csum_start; /* offset from start of WMI header where final csum goes */ u8 csum_dest; /* no of bytes over which csum is calculated */ u8 csum_flags; } __packed; struct wmi_rx_meta_v1 { u8 status; /* rate index mapped to rate at which this packet was received. */ u8 rix; /* rssi of packet */ u8 rssi; /* rf channel during packet reception */ u8 channel; __le16 flags; } __packed; struct wmi_rx_meta_v2 { __le16 csum; /* bit 0 set -partial csum valid bit 1 set -test mode */ u8 csum_flags; } __packed; #define WMI_CMD_HDR_IF_ID_MASK 0xF /* Control Path */ struct wmi_cmd_hdr { __le16 cmd_id; /* info1 - 16 bits * b03:b00 - id * b15:b04 - unused */ __le16 info1; /* for alignment */ __le16 reserved; } __packed; static inline u8 wmi_cmd_hdr_get_if_idx(struct wmi_cmd_hdr *chdr) { return le16_to_cpu(chdr->info1) & WMI_CMD_HDR_IF_ID_MASK; } /* List of WMI commands */ enum wmi_cmd_id { WMI_CONNECT_CMDID = 0x0001, WMI_RECONNECT_CMDID, WMI_DISCONNECT_CMDID, WMI_SYNCHRONIZE_CMDID, WMI_CREATE_PSTREAM_CMDID, WMI_DELETE_PSTREAM_CMDID, /* WMI_START_SCAN_CMDID is to be deprecated. Use * WMI_BEGIN_SCAN_CMDID instead. The new cmd supports P2P mgmt * operations using station interface. */ WMI_START_SCAN_CMDID, WMI_SET_SCAN_PARAMS_CMDID, WMI_SET_BSS_FILTER_CMDID, WMI_SET_PROBED_SSID_CMDID, /* 10 */ WMI_SET_LISTEN_INT_CMDID, WMI_SET_BMISS_TIME_CMDID, WMI_SET_DISC_TIMEOUT_CMDID, WMI_GET_CHANNEL_LIST_CMDID, WMI_SET_BEACON_INT_CMDID, WMI_GET_STATISTICS_CMDID, WMI_SET_CHANNEL_PARAMS_CMDID, WMI_SET_POWER_MODE_CMDID, WMI_SET_IBSS_PM_CAPS_CMDID, WMI_SET_POWER_PARAMS_CMDID, /* 20 */ WMI_SET_POWERSAVE_TIMERS_POLICY_CMDID, WMI_ADD_CIPHER_KEY_CMDID, WMI_DELETE_CIPHER_KEY_CMDID, WMI_ADD_KRK_CMDID, WMI_DELETE_KRK_CMDID, WMI_SET_PMKID_CMDID, WMI_SET_TX_PWR_CMDID, WMI_GET_TX_PWR_CMDID, WMI_SET_ASSOC_INFO_CMDID, WMI_ADD_BAD_AP_CMDID, /* 30 */ WMI_DELETE_BAD_AP_CMDID, WMI_SET_TKIP_COUNTERMEASURES_CMDID, WMI_RSSI_THRESHOLD_PARAMS_CMDID, WMI_TARGET_ERROR_REPORT_BITMASK_CMDID, WMI_SET_ACCESS_PARAMS_CMDID, WMI_SET_RETRY_LIMITS_CMDID, WMI_SET_OPT_MODE_CMDID, WMI_OPT_TX_FRAME_CMDID, WMI_SET_VOICE_PKT_SIZE_CMDID, WMI_SET_MAX_SP_LEN_CMDID, /* 40 */ WMI_SET_ROAM_CTRL_CMDID, WMI_GET_ROAM_TBL_CMDID, WMI_GET_ROAM_DATA_CMDID, WMI_ENABLE_RM_CMDID, WMI_SET_MAX_OFFHOME_DURATION_CMDID, WMI_EXTENSION_CMDID, /* Non-wireless extensions */ WMI_SNR_THRESHOLD_PARAMS_CMDID, WMI_LQ_THRESHOLD_PARAMS_CMDID, WMI_SET_LPREAMBLE_CMDID, WMI_SET_RTS_CMDID, /* 50 */ WMI_CLR_RSSI_SNR_CMDID, WMI_SET_FIXRATES_CMDID, WMI_GET_FIXRATES_CMDID, WMI_SET_AUTH_MODE_CMDID, WMI_SET_REASSOC_MODE_CMDID, WMI_SET_WMM_CMDID, WMI_SET_WMM_TXOP_CMDID, WMI_TEST_CMDID, /* COEX AR6002 only */ WMI_SET_BT_STATUS_CMDID, WMI_SET_BT_PARAMS_CMDID, /* 60 */ WMI_SET_KEEPALIVE_CMDID, WMI_GET_KEEPALIVE_CMDID, WMI_SET_APPIE_CMDID, WMI_GET_APPIE_CMDID, WMI_SET_WSC_STATUS_CMDID, /* Wake on Wireless */ WMI_SET_HOST_SLEEP_MODE_CMDID, WMI_SET_WOW_MODE_CMDID, WMI_GET_WOW_LIST_CMDID, WMI_ADD_WOW_PATTERN_CMDID, WMI_DEL_WOW_PATTERN_CMDID, /* 70 */ WMI_SET_FRAMERATES_CMDID, WMI_SET_AP_PS_CMDID, WMI_SET_QOS_SUPP_CMDID, WMI_SET_IE_CMDID, /* WMI_THIN_RESERVED_... mark the start and end * values for WMI_THIN_RESERVED command IDs. These * command IDs can be found in wmi_thin.h */ WMI_THIN_RESERVED_START = 0x8000, WMI_THIN_RESERVED_END = 0x8fff, /* Developer commands starts at 0xF000 */ WMI_SET_BITRATE_CMDID = 0xF000, WMI_GET_BITRATE_CMDID, WMI_SET_WHALPARAM_CMDID, WMI_SET_MAC_ADDRESS_CMDID, WMI_SET_AKMP_PARAMS_CMDID, WMI_SET_PMKID_LIST_CMDID, WMI_GET_PMKID_LIST_CMDID, WMI_ABORT_SCAN_CMDID, WMI_SET_TARGET_EVENT_REPORT_CMDID, /* Unused */ WMI_UNUSED1, WMI_UNUSED2, /* AP mode commands */ WMI_AP_HIDDEN_SSID_CMDID, WMI_AP_SET_NUM_STA_CMDID, WMI_AP_ACL_POLICY_CMDID, WMI_AP_ACL_MAC_LIST_CMDID, WMI_AP_CONFIG_COMMIT_CMDID, WMI_AP_SET_MLME_CMDID, WMI_AP_SET_PVB_CMDID, WMI_AP_CONN_INACT_CMDID, WMI_AP_PROT_SCAN_TIME_CMDID, WMI_AP_SET_COUNTRY_CMDID, WMI_AP_SET_DTIM_CMDID, WMI_AP_MODE_STAT_CMDID, WMI_SET_IP_CMDID, WMI_SET_PARAMS_CMDID, WMI_SET_MCAST_FILTER_CMDID, WMI_DEL_MCAST_FILTER_CMDID, WMI_ALLOW_AGGR_CMDID, WMI_ADDBA_REQ_CMDID, WMI_DELBA_REQ_CMDID, WMI_SET_HT_CAP_CMDID, WMI_SET_HT_OP_CMDID, WMI_SET_TX_SELECT_RATES_CMDID, WMI_SET_TX_SGI_PARAM_CMDID, WMI_SET_RATE_POLICY_CMDID, WMI_HCI_CMD_CMDID, WMI_RX_FRAME_FORMAT_CMDID, WMI_SET_THIN_MODE_CMDID, WMI_SET_BT_WLAN_CONN_PRECEDENCE_CMDID, WMI_AP_SET_11BG_RATESET_CMDID, WMI_SET_PMK_CMDID, WMI_MCAST_FILTER_CMDID, /* COEX CMDID AR6003 */ WMI_SET_BTCOEX_FE_ANT_CMDID, WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID, WMI_SET_BTCOEX_SCO_CONFIG_CMDID, WMI_SET_BTCOEX_A2DP_CONFIG_CMDID, WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID, WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID, WMI_SET_BTCOEX_DEBUG_CMDID, WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID, WMI_GET_BTCOEX_STATS_CMDID, WMI_GET_BTCOEX_CONFIG_CMDID, WMI_SET_DFS_ENABLE_CMDID, /* F034 */ WMI_SET_DFS_MINRSSITHRESH_CMDID, WMI_SET_DFS_MAXPULSEDUR_CMDID, WMI_DFS_RADAR_DETECTED_CMDID, /* P2P commands */ WMI_P2P_SET_CONFIG_CMDID, /* F038 */ WMI_WPS_SET_CONFIG_CMDID, WMI_SET_REQ_DEV_ATTR_CMDID, WMI_P2P_FIND_CMDID, WMI_P2P_STOP_FIND_CMDID, WMI_P2P_GO_NEG_START_CMDID, WMI_P2P_LISTEN_CMDID, WMI_CONFIG_TX_MAC_RULES_CMDID, /* F040 */ WMI_SET_PROMISCUOUS_MODE_CMDID, WMI_RX_FRAME_FILTER_CMDID, WMI_SET_CHANNEL_CMDID, /* WAC commands */ WMI_ENABLE_WAC_CMDID, WMI_WAC_SCAN_REPLY_CMDID, WMI_WAC_CTRL_REQ_CMDID, WMI_SET_DIV_PARAMS_CMDID, WMI_GET_PMK_CMDID, WMI_SET_PASSPHRASE_CMDID, WMI_SEND_ASSOC_RES_CMDID, WMI_SET_ASSOC_REQ_RELAY_CMDID, /* ACS command, consists of sub-commands */ WMI_ACS_CTRL_CMDID, WMI_SET_EXCESS_TX_RETRY_THRES_CMDID, WMI_SET_TBD_TIME_CMDID, /*added for wmiconfig command for TBD */ /* Pktlog cmds */ WMI_PKTLOG_ENABLE_CMDID, WMI_PKTLOG_DISABLE_CMDID, /* More P2P Cmds */ WMI_P2P_GO_NEG_REQ_RSP_CMDID, WMI_P2P_GRP_INIT_CMDID, WMI_P2P_GRP_FORMATION_DONE_CMDID, WMI_P2P_INVITE_CMDID, WMI_P2P_INVITE_REQ_RSP_CMDID, WMI_P2P_PROV_DISC_REQ_CMDID, WMI_P2P_SET_CMDID, WMI_GET_RFKILL_MODE_CMDID, WMI_SET_RFKILL_MODE_CMDID, WMI_AP_SET_APSD_CMDID, WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID, WMI_P2P_SDPD_TX_CMDID, /* F05C */ WMI_P2P_STOP_SDPD_CMDID, WMI_P2P_CANCEL_CMDID, /* Ultra low power store / recall commands */ WMI_STORERECALL_CONFIGURE_CMDID, WMI_STORERECALL_RECALL_CMDID, WMI_STORERECALL_HOST_READY_CMDID, WMI_FORCE_TARGET_ASSERT_CMDID, WMI_SET_PROBED_SSID_EX_CMDID, WMI_SET_NETWORK_LIST_OFFLOAD_CMDID, WMI_SET_ARP_NS_OFFLOAD_CMDID, WMI_ADD_WOW_EXT_PATTERN_CMDID, WMI_GTK_OFFLOAD_OP_CMDID, WMI_REMAIN_ON_CHNL_CMDID, WMI_CANCEL_REMAIN_ON_CHNL_CMDID, /* WMI_SEND_ACTION_CMDID is to be deprecated. Use * WMI_SEND_MGMT_CMDID instead. The new cmd supports P2P mgmt * operations using station interface. */ WMI_SEND_ACTION_CMDID, WMI_PROBE_REQ_REPORT_CMDID, WMI_DISABLE_11B_RATES_CMDID, WMI_SEND_PROBE_RESPONSE_CMDID, WMI_GET_P2P_INFO_CMDID, WMI_AP_JOIN_BSS_CMDID, WMI_SMPS_ENABLE_CMDID, WMI_SMPS_CONFIG_CMDID, WMI_SET_RATECTRL_PARM_CMDID, /* LPL specific commands*/ WMI_LPL_FORCE_ENABLE_CMDID, WMI_LPL_SET_POLICY_CMDID, WMI_LPL_GET_POLICY_CMDID, WMI_LPL_GET_HWSTATE_CMDID, WMI_LPL_SET_PARAMS_CMDID, WMI_LPL_GET_PARAMS_CMDID, WMI_SET_BUNDLE_PARAM_CMDID, /*GreenTx specific commands*/ WMI_GREENTX_PARAMS_CMDID, WMI_RTT_MEASREQ_CMDID, WMI_RTT_CAPREQ_CMDID, WMI_RTT_STATUSREQ_CMDID, /* WPS Commands */ WMI_WPS_START_CMDID, WMI_GET_WPS_STATUS_CMDID, /* More P2P commands */ WMI_SET_NOA_CMDID, WMI_GET_NOA_CMDID, WMI_SET_OPPPS_CMDID, WMI_GET_OPPPS_CMDID, WMI_ADD_PORT_CMDID, WMI_DEL_PORT_CMDID, /* 802.11w cmd */ WMI_SET_RSN_CAP_CMDID, WMI_GET_RSN_CAP_CMDID, WMI_SET_IGTK_CMDID, WMI_RX_FILTER_COALESCE_FILTER_OP_CMDID, WMI_RX_FILTER_SET_FRAME_TEST_LIST_CMDID, WMI_SEND_MGMT_CMDID, WMI_BEGIN_SCAN_CMDID, WMI_SET_BLACK_LIST, WMI_SET_MCASTRATE, WMI_STA_BMISS_ENHANCE_CMDID, }; enum wmi_mgmt_frame_type { WMI_FRAME_BEACON = 0, WMI_FRAME_PROBE_REQ, WMI_FRAME_PROBE_RESP, WMI_FRAME_ASSOC_REQ, WMI_FRAME_ASSOC_RESP, WMI_NUM_MGMT_FRAME }; enum wmi_ie_field_type { WMI_RSN_IE_CAPB = 0x1, WMI_IE_FULL = 0xFF, /* indicats full IE */ }; /* WMI_CONNECT_CMDID */ enum network_type { INFRA_NETWORK = 0x01, ADHOC_NETWORK = 0x02, ADHOC_CREATOR = 0x04, AP_NETWORK = 0x10, }; enum network_subtype { SUBTYPE_NONE, SUBTYPE_BT, SUBTYPE_P2PDEV, SUBTYPE_P2PCLIENT, SUBTYPE_P2PGO, }; enum dot11_auth_mode { OPEN_AUTH = 0x01, SHARED_AUTH = 0x02, /* different from IEEE_AUTH_MODE definitions */ LEAP_AUTH = 0x04, }; enum auth_mode { NONE_AUTH = 0x01, WPA_AUTH = 0x02, WPA2_AUTH = 0x04, WPA_PSK_AUTH = 0x08, WPA2_PSK_AUTH = 0x10, WPA_AUTH_CCKM = 0x20, WPA2_AUTH_CCKM = 0x40, }; #define WMI_MAX_KEY_INDEX 3 #define WMI_MAX_KEY_LEN 32 /* * NB: these values are ordered carefully; there are lots of * of implications in any reordering. In particular beware * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY. */ #define ATH6KL_CIPHER_WEP 0 #define ATH6KL_CIPHER_TKIP 1 #define ATH6KL_CIPHER_AES_OCB 2 #define ATH6KL_CIPHER_AES_CCM 3 #define ATH6KL_CIPHER_CKIP 5 #define ATH6KL_CIPHER_CCKM_KRK 6 #define ATH6KL_CIPHER_NONE 7 /* pseudo value */ /* * 802.11 rate set. */ #define ATH6KL_RATE_MAXSIZE 15 /* max rates we'll handle */ #define ATH_OUI_TYPE 0x01 #define WPA_OUI_TYPE 0x01 #define WMM_PARAM_OUI_SUBTYPE 0x01 #define WMM_OUI_TYPE 0x02 #define WSC_OUT_TYPE 0x04 enum wmi_connect_ctrl_flags_bits { CONNECT_ASSOC_POLICY_USER = 0x0001, CONNECT_SEND_REASSOC = 0x0002, CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, CONNECT_PROFILE_MATCH_DONE = 0x0008, CONNECT_IGNORE_AAC_BEACON = 0x0010, CONNECT_CSA_FOLLOW_BSS = 0x0020, CONNECT_DO_WPA_OFFLOAD = 0x0040, CONNECT_DO_NOT_DEAUTH = 0x0080, CONNECT_WPS_FLAG = 0x0100, }; struct wmi_connect_cmd { u8 nw_type; u8 dot11_auth_mode; u8 auth_mode; u8 prwise_crypto_type; u8 prwise_crypto_len; u8 grp_crypto_type; u8 grp_crypto_len; u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; __le16 ch; u8 bssid[ETH_ALEN]; __le32 ctrl_flags; u8 nw_subtype; } __packed; /* WMI_RECONNECT_CMDID */ struct wmi_reconnect_cmd { /* channel hint */ __le16 channel; /* mandatory if set */ u8 bssid[ETH_ALEN]; } __packed; /* WMI_ADD_CIPHER_KEY_CMDID */ enum key_usage { PAIRWISE_USAGE = 0x00, GROUP_USAGE = 0x01, /* default Tx Key - static WEP only */ TX_USAGE = 0x02, }; /* * Bit Flag * Bit 0 - Initialise TSC - default is Initialize */ #define KEY_OP_INIT_TSC 0x01 #define KEY_OP_INIT_RSC 0x02 /* default initialise the TSC & RSC */ #define KEY_OP_INIT_VAL 0x03 #define KEY_OP_VALID_MASK 0x03 struct wmi_add_cipher_key_cmd { u8 key_index; u8 key_type; /* enum key_usage */ u8 key_usage; u8 key_len; /* key replay sequence counter */ u8 key_rsc[8]; u8 key[WLAN_MAX_KEY_LEN]; /* additional key control info */ u8 key_op_ctrl; u8 key_mac_addr[ETH_ALEN]; } __packed; /* WMI_DELETE_CIPHER_KEY_CMDID */ struct wmi_delete_cipher_key_cmd { u8 key_index; } __packed; #define WMI_KRK_LEN 16 /* WMI_ADD_KRK_CMDID */ struct wmi_add_krk_cmd { u8 krk[WMI_KRK_LEN]; } __packed; /* WMI_SETPMKID_CMDID */ #define WMI_PMKID_LEN 16 enum pmkid_enable_flg { PMKID_DISABLE = 0, PMKID_ENABLE = 1, }; struct wmi_setpmkid_cmd { u8 bssid[ETH_ALEN]; /* enum pmkid_enable_flg */ u8 enable; u8 pmkid[WMI_PMKID_LEN]; } __packed; /* WMI_START_SCAN_CMD */ enum wmi_scan_type { WMI_LONG_SCAN = 0, WMI_SHORT_SCAN = 1, }; struct wmi_supp_rates { u8 nrates; u8 rates[ATH6KL_RATE_MAXSIZE]; }; struct wmi_begin_scan_cmd { __le32 force_fg_scan; /* for legacy cisco AP compatibility */ __le32 is_legacy; /* max duration in the home channel(msec) */ __le32 home_dwell_time; /* time interval between scans (msec) */ __le32 force_scan_intvl; /* no CCK rates */ __le32 no_cck; /* enum wmi_scan_type */ u8 scan_type; /* Supported rates to advertise in the probe request frames */ struct wmi_supp_rates supp_rates[IEEE80211_NUM_BANDS]; /* how many channels follow */ u8 num_ch; /* channels in Mhz */ __le16 ch_list[1]; } __packed; /* wmi_start_scan_cmd is to be deprecated. Use * wmi_begin_scan_cmd instead. The new structure supports P2P mgmt * operations using station interface. */ struct wmi_start_scan_cmd { __le32 force_fg_scan; /* for legacy cisco AP compatibility */ __le32 is_legacy; /* max duration in the home channel(msec) */ __le32 home_dwell_time; /* time interval between scans (msec) */ __le32 force_scan_intvl; /* enum wmi_scan_type */ u8 scan_type; /* how many channels follow */ u8 num_ch; /* channels in Mhz */ __le16 ch_list[1]; } __packed; /* * Warning: scan control flag value of 0xFF is used to disable * all flags in WMI_SCAN_PARAMS_CMD. Do not add any more * flags here */ enum wmi_scan_ctrl_flags_bits { /* set if can scan in the connect cmd */ CONNECT_SCAN_CTRL_FLAGS = 0x01, /* set if scan for the SSID it is already connected to */ SCAN_CONNECTED_CTRL_FLAGS = 0x02, /* set if enable active scan */ ACTIVE_SCAN_CTRL_FLAGS = 0x04, /* set if enable roam scan when bmiss and lowrssi */ ROAM_SCAN_CTRL_FLAGS = 0x08, /* set if follows customer BSSINFO reporting rule */ REPORT_BSSINFO_CTRL_FLAGS = 0x10, /* if disabled, target doesn't scan after a disconnect event */ ENABLE_AUTO_CTRL_FLAGS = 0x20, /* * Scan complete event with canceled status will be generated when * a scan is prempted before it gets completed. */ ENABLE_SCAN_ABORT_EVENT = 0x40 }; struct wmi_scan_params_cmd { /* sec */ __le16 fg_start_period; /* sec */ __le16 fg_end_period; /* sec */ __le16 bg_period; /* msec */ __le16 maxact_chdwell_time; /* msec */ __le16 pas_chdwell_time; /* how many shorts scan for one long */ u8 short_scan_ratio; u8 scan_ctrl_flags; /* msec */ __le16 minact_chdwell_time; /* max active scans per ssid */ __le16 maxact_scan_per_ssid; /* msecs */ __le32 max_dfsch_act_time; } __packed; /* WMI_SET_BSS_FILTER_CMDID */ enum wmi_bss_filter { /* no beacons forwarded */ NONE_BSS_FILTER = 0x0, /* all beacons forwarded */ ALL_BSS_FILTER, /* only beacons matching profile */ PROFILE_FILTER, /* all but beacons matching profile */ ALL_BUT_PROFILE_FILTER, /* only beacons matching current BSS */ CURRENT_BSS_FILTER, /* all but beacons matching BSS */ ALL_BUT_BSS_FILTER, /* beacons matching probed ssid */ PROBED_SSID_FILTER, /* beacons matching matched ssid */ MATCHED_SSID_FILTER, /* marker only */ LAST_BSS_FILTER, }; struct wmi_bss_filter_cmd { /* see, enum wmi_bss_filter */ u8 bss_filter; /* for alignment */ u8 reserved1; /* for alignment */ __le16 reserved2; __le32 ie_mask; } __packed; /* WMI_SET_PROBED_SSID_CMDID */ #define MAX_PROBED_SSIDS 16 enum wmi_ssid_flag { /* disables entry */ DISABLE_SSID_FLAG = 0, /* probes specified ssid */ SPECIFIC_SSID_FLAG = 0x01, /* probes for any ssid */ ANY_SSID_FLAG = 0x02, /* match for ssid */ MATCH_SSID_FLAG = 0x08, }; struct wmi_probed_ssid_cmd { /* 0 to MAX_PROBED_SSIDS - 1 */ u8 entry_index; /* see, enum wmi_ssid_flg */ u8 flag; u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; } __packed; /* * WMI_SET_LISTEN_INT_CMDID * The Listen interval is between 15 and 3000 TUs */ struct wmi_listen_int_cmd { __le16 listen_intvl; __le16 num_beacons; } __packed; /* WMI_SET_BMISS_TIME_CMDID */ struct wmi_bmiss_time_cmd { __le16 bmiss_time; __le16 num_beacons; }; /* WMI_STA_ENHANCE_BMISS_CMDID */ struct wmi_sta_bmiss_enhance_cmd { u8 enable; } __packed; /* WMI_SET_POWER_MODE_CMDID */ enum wmi_power_mode { REC_POWER = 0x01, MAX_PERF_POWER, }; struct wmi_power_mode_cmd { /* see, enum wmi_power_mode */ u8 pwr_mode; } __packed; /* * Policy to determnine whether power save failure event should be sent to * host during scanning */ enum power_save_fail_event_policy { SEND_POWER_SAVE_FAIL_EVENT_ALWAYS = 1, IGNORE_PS_FAIL_DURING_SCAN = 2, }; struct wmi_power_params_cmd { /* msec */ __le16 idle_period; __le16 pspoll_number; __le16 dtim_policy; __le16 tx_wakeup_policy; __le16 num_tx_to_wakeup; __le16 ps_fail_event_policy; } __packed; /* * Ratemask for below modes should be passed * to WMI_SET_TX_SELECT_RATES_CMDID. * AR6003 has 32 bit mask for each modes. * First 12 bits for legacy rates, 13 to 20 * bits for HT 20 rates and 21 to 28 bits for * HT 40 rates */ enum wmi_mode_phy { WMI_RATES_MODE_11A = 0, WMI_RATES_MODE_11G, WMI_RATES_MODE_11B, WMI_RATES_MODE_11GONLY, WMI_RATES_MODE_11A_HT20, WMI_RATES_MODE_11G_HT20, WMI_RATES_MODE_11A_HT40, WMI_RATES_MODE_11G_HT40, WMI_RATES_MODE_MAX }; /* WMI_SET_TX_SELECT_RATES_CMDID */ struct wmi_set_tx_select_rates32_cmd { __le32 ratemask[WMI_RATES_MODE_MAX]; } __packed; /* WMI_SET_TX_SELECT_RATES_CMDID */ struct wmi_set_tx_select_rates64_cmd { __le64 ratemask[WMI_RATES_MODE_MAX]; } __packed; /* WMI_SET_DISC_TIMEOUT_CMDID */ struct wmi_disc_timeout_cmd { /* seconds */ u8 discon_timeout; } __packed; enum dir_type { UPLINK_TRAFFIC = 0, DNLINK_TRAFFIC = 1, BIDIR_TRAFFIC = 2, }; enum voiceps_cap_type { DISABLE_FOR_THIS_AC = 0, ENABLE_FOR_THIS_AC = 1, ENABLE_FOR_ALL_AC = 2, }; enum traffic_type { TRAFFIC_TYPE_APERIODIC = 0, TRAFFIC_TYPE_PERIODIC = 1, }; /* WMI_SYNCHRONIZE_CMDID */ struct wmi_sync_cmd { u8 data_sync_map; } __packed; /* WMI_CREATE_PSTREAM_CMDID */ struct wmi_create_pstream_cmd { /* msec */ __le32 min_service_int; /* msec */ __le32 max_service_int; /* msec */ __le32 inactivity_int; /* msec */ __le32 suspension_int; __le32 service_start_time; /* in bps */ __le32 min_data_rate; /* in bps */ __le32 mean_data_rate; /* in bps */ __le32 peak_data_rate; __le32 max_burst_size; __le32 delay_bound; /* in bps */ __le32 min_phy_rate; __le32 sba; __le32 medium_time; /* in octects */ __le16 nominal_msdu; /* in octects */ __le16 max_msdu; u8 traffic_class; /* see, enum dir_type */ u8 traffic_direc; u8 rx_queue_num; /* see, enum traffic_type */ u8 traffic_type; /* see, enum voiceps_cap_type */ u8 voice_psc_cap; u8 tsid; /* 802.1D user priority */ u8 user_pri; /* nominal phy rate */ u8 nominal_phy; } __packed; /* WMI_DELETE_PSTREAM_CMDID */ struct wmi_delete_pstream_cmd { u8 tx_queue_num; u8 rx_queue_num; u8 traffic_direc; u8 traffic_class; u8 tsid; } __packed; /* WMI_SET_CHANNEL_PARAMS_CMDID */ enum wmi_phy_mode { WMI_11A_MODE = 0x1, WMI_11G_MODE = 0x2, WMI_11AG_MODE = 0x3, WMI_11B_MODE = 0x4, WMI_11GONLY_MODE = 0x5, WMI_11G_HT20 = 0x6, }; #define WMI_MAX_CHANNELS 32 /* * WMI_RSSI_THRESHOLD_PARAMS_CMDID * Setting the polltime to 0 would disable polling. Threshold values are * in the ascending order, and should agree to: * (lowThreshold_lowerVal < lowThreshold_upperVal < highThreshold_lowerVal * < highThreshold_upperVal) */ struct wmi_rssi_threshold_params_cmd { /* polling time as a factor of LI */ __le32 poll_time; /* lowest of upper */ a_sle16 thresh_above1_val; a_sle16 thresh_above2_val; a_sle16 thresh_above3_val; a_sle16 thresh_above4_val; a_sle16 thresh_above5_val; /* highest of upper */ a_sle16 thresh_above6_val; /* lowest of bellow */ a_sle16 thresh_below1_val; a_sle16 thresh_below2_val; a_sle16 thresh_below3_val; a_sle16 thresh_below4_val; a_sle16 thresh_below5_val; /* highest of bellow */ a_sle16 thresh_below6_val; /* "alpha" */ u8 weight; u8 reserved[3]; } __packed; /* * WMI_SNR_THRESHOLD_PARAMS_CMDID * Setting the polltime to 0 would disable polling. */ struct wmi_snr_threshold_params_cmd { /* polling time as a factor of LI */ __le32 poll_time; /* "alpha" */ u8 weight; /* lowest of uppper */ u8 thresh_above1_val; u8 thresh_above2_val; u8 thresh_above3_val; /* highest of upper */ u8 thresh_above4_val; /* lowest of bellow */ u8 thresh_below1_val; u8 thresh_below2_val; u8 thresh_below3_val; /* highest of bellow */ u8 thresh_below4_val; u8 reserved[3]; } __packed; enum wmi_preamble_policy { WMI_IGNORE_BARKER_IN_ERP = 0, WMI_FOLLOW_BARKER_IN_ERP, }; struct wmi_set_lpreamble_cmd { u8 status; u8 preamble_policy; } __packed; struct wmi_set_rts_cmd { __le16 threshold; } __packed; /* WMI_SET_TX_PWR_CMDID */ struct wmi_set_tx_pwr_cmd { /* in dbM units */ u8 dbM; } __packed; struct wmi_tx_pwr_reply { /* in dbM units */ u8 dbM; } __packed; struct wmi_report_sleep_state_event { __le32 sleep_state; }; enum wmi_report_sleep_status { WMI_REPORT_SLEEP_STATUS_IS_DEEP_SLEEP = 0, WMI_REPORT_SLEEP_STATUS_IS_AWAKE }; enum target_event_report_config { /* default */ DISCONN_EVT_IN_RECONN = 0, NO_DISCONN_EVT_IN_RECONN }; struct wmi_mcast_filter_cmd { u8 mcast_all_enable; } __packed; #define ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE 6 struct wmi_mcast_filter_add_del_cmd { u8 mcast_mac[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; } __packed; struct wmi_set_htcap_cmd { u8 band; u8 ht_enable; u8 ht40_supported; u8 ht20_sgi; u8 ht40_sgi; u8 intolerant_40mhz; u8 max_ampdu_len_exp; } __packed; /* Command Replies */ /* WMI_GET_CHANNEL_LIST_CMDID reply */ struct wmi_channel_list_reply { u8 reserved; /* number of channels in reply */ u8 num_ch; /* channel in Mhz */ __le16 ch_list[1]; } __packed; /* List of Events (target to host) */ enum wmi_event_id { WMI_READY_EVENTID = 0x1001, WMI_CONNECT_EVENTID, WMI_DISCONNECT_EVENTID, WMI_BSSINFO_EVENTID, WMI_CMDERROR_EVENTID, WMI_REGDOMAIN_EVENTID, WMI_PSTREAM_TIMEOUT_EVENTID, WMI_NEIGHBOR_REPORT_EVENTID, WMI_TKIP_MICERR_EVENTID, WMI_SCAN_COMPLETE_EVENTID, /* 0x100a */ WMI_REPORT_STATISTICS_EVENTID, WMI_RSSI_THRESHOLD_EVENTID, WMI_ERROR_REPORT_EVENTID, WMI_OPT_RX_FRAME_EVENTID, WMI_REPORT_ROAM_TBL_EVENTID, WMI_EXTENSION_EVENTID, WMI_CAC_EVENTID, WMI_SNR_THRESHOLD_EVENTID, WMI_LQ_THRESHOLD_EVENTID, WMI_TX_RETRY_ERR_EVENTID, /* 0x1014 */ WMI_REPORT_ROAM_DATA_EVENTID, WMI_TEST_EVENTID, WMI_APLIST_EVENTID, WMI_GET_WOW_LIST_EVENTID, WMI_GET_PMKID_LIST_EVENTID, WMI_CHANNEL_CHANGE_EVENTID, WMI_PEER_NODE_EVENTID, WMI_PSPOLL_EVENTID, WMI_DTIMEXPIRY_EVENTID, WMI_WLAN_VERSION_EVENTID, WMI_SET_PARAMS_REPLY_EVENTID, WMI_ADDBA_REQ_EVENTID, /*0x1020 */ WMI_ADDBA_RESP_EVENTID, WMI_DELBA_REQ_EVENTID, WMI_TX_COMPLETE_EVENTID, WMI_HCI_EVENT_EVENTID, WMI_ACL_DATA_EVENTID, WMI_REPORT_SLEEP_STATE_EVENTID, WMI_REPORT_BTCOEX_STATS_EVENTID, WMI_REPORT_BTCOEX_CONFIG_EVENTID, WMI_GET_PMK_EVENTID, /* DFS Events */ WMI_DFS_HOST_ATTACH_EVENTID, WMI_DFS_HOST_INIT_EVENTID, WMI_DFS_RESET_DELAYLINES_EVENTID, WMI_DFS_RESET_RADARQ_EVENTID, WMI_DFS_RESET_AR_EVENTID, WMI_DFS_RESET_ARQ_EVENTID, WMI_DFS_SET_DUR_MULTIPLIER_EVENTID, WMI_DFS_SET_BANGRADAR_EVENTID, WMI_DFS_SET_DEBUGLEVEL_EVENTID, WMI_DFS_PHYERR_EVENTID, /* CCX Evants */ WMI_CCX_RM_STATUS_EVENTID, /* P2P Events */ WMI_P2P_GO_NEG_RESULT_EVENTID, WMI_WAC_SCAN_DONE_EVENTID, WMI_WAC_REPORT_BSS_EVENTID, WMI_WAC_START_WPS_EVENTID, WMI_WAC_CTRL_REQ_REPLY_EVENTID, WMI_REPORT_WMM_PARAMS_EVENTID, WMI_WAC_REJECT_WPS_EVENTID, /* More P2P Events */ WMI_P2P_GO_NEG_REQ_EVENTID, WMI_P2P_INVITE_REQ_EVENTID, WMI_P2P_INVITE_RCVD_RESULT_EVENTID, WMI_P2P_INVITE_SENT_RESULT_EVENTID, WMI_P2P_PROV_DISC_RESP_EVENTID, WMI_P2P_PROV_DISC_REQ_EVENTID, /* RFKILL Events */ WMI_RFKILL_STATE_CHANGE_EVENTID, WMI_RFKILL_GET_MODE_CMD_EVENTID, WMI_P2P_START_SDPD_EVENTID, WMI_P2P_SDPD_RX_EVENTID, WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID = 0x1047, WMI_THIN_RESERVED_START_EVENTID = 0x8000, /* Events in this range are reserved for thinmode */ WMI_THIN_RESERVED_END_EVENTID = 0x8fff, WMI_SET_CHANNEL_EVENTID, WMI_ASSOC_REQ_EVENTID, /* Generic ACS event */ WMI_ACS_EVENTID, WMI_STORERECALL_STORE_EVENTID, WMI_WOW_EXT_WAKE_EVENTID, WMI_GTK_OFFLOAD_STATUS_EVENTID, WMI_NETWORK_LIST_OFFLOAD_EVENTID, WMI_REMAIN_ON_CHNL_EVENTID, WMI_CANCEL_REMAIN_ON_CHNL_EVENTID, WMI_TX_STATUS_EVENTID, WMI_RX_PROBE_REQ_EVENTID, WMI_P2P_CAPABILITIES_EVENTID, WMI_RX_ACTION_EVENTID, WMI_P2P_INFO_EVENTID, }; struct wmi_ready_event_2 { __le32 sw_version; __le32 abi_version; u8 mac_addr[ETH_ALEN]; u8 phy_cap; } __packed; /* WMI_PHY_CAPABILITY */ enum wmi_phy_cap { WMI_11A_CAP = 0x01, WMI_11G_CAP = 0x02, WMI_11AG_CAP = 0x03, WMI_11AN_CAP = 0x04, WMI_11GN_CAP = 0x05, WMI_11AGN_CAP = 0x06, }; /* Connect Event */ struct wmi_connect_event { union { struct { __le16 ch; u8 bssid[ETH_ALEN]; __le16 listen_intvl; __le16 beacon_intvl; __le32 nw_type; } sta; struct { u8 phymode; u8 aid; u8 mac_addr[ETH_ALEN]; u8 auth; u8 keymgmt; __le16 cipher; u8 apsd_info; u8 unused[3]; } ap_sta; struct { __le16 ch; u8 bssid[ETH_ALEN]; u8 unused[8]; } ap_bss; } u; u8 beacon_ie_len; u8 assoc_req_len; u8 assoc_resp_len; u8 assoc_info[1]; } __packed; /* Disconnect Event */ enum wmi_disconnect_reason { NO_NETWORK_AVAIL = 0x01, /* bmiss */ LOST_LINK = 0x02, DISCONNECT_CMD = 0x03, BSS_DISCONNECTED = 0x04, AUTH_FAILED = 0x05, ASSOC_FAILED = 0x06, NO_RESOURCES_AVAIL = 0x07, CSERV_DISCONNECT = 0x08, INVALID_PROFILE = 0x0a, DOT11H_CHANNEL_SWITCH = 0x0b, PROFILE_MISMATCH = 0x0c, CONNECTION_EVICTED = 0x0d, IBSS_MERGE = 0xe, }; /* AP mode disconnect proto_reasons */ enum ap_disconnect_reason { WMI_AP_REASON_STA_LEFT = 101, WMI_AP_REASON_FROM_HOST = 102, WMI_AP_REASON_COMM_TIMEOUT = 103, WMI_AP_REASON_MAX_STA = 104, WMI_AP_REASON_ACL = 105, WMI_AP_REASON_STA_ROAM = 106, WMI_AP_REASON_DFS_CHANNEL = 107, }; #define ATH6KL_COUNTRY_RD_SHIFT 16 struct ath6kl_wmi_regdomain { __le32 reg_code; }; struct wmi_disconnect_event { /* reason code, see 802.11 spec. */ __le16 proto_reason_status; /* set if known */ u8 bssid[ETH_ALEN]; /* see WMI_DISCONNECT_REASON */ u8 disconn_reason; u8 assoc_resp_len; u8 assoc_info[1]; } __packed; /* * BSS Info Event. * Mechanism used to inform host of the presence and characteristic of * wireless networks present. Consists of bss info header followed by * the beacon or probe-response frame body. The 802.11 header is no included. */ enum wmi_bi_ftype { BEACON_FTYPE = 0x1, PROBERESP_FTYPE, ACTION_MGMT_FTYPE, PROBEREQ_FTYPE, }; #define DEF_LRSSI_SCAN_PERIOD 5 #define DEF_LRSSI_ROAM_THRESHOLD 20 #define DEF_LRSSI_ROAM_FLOOR 60 #define DEF_SCAN_FOR_ROAM_INTVL 2 enum wmi_roam_ctrl { WMI_FORCE_ROAM = 1, WMI_SET_ROAM_MODE, WMI_SET_HOST_BIAS, WMI_SET_LRSSI_SCAN_PARAMS, }; enum wmi_roam_mode { WMI_DEFAULT_ROAM_MODE = 1, /* RSSI based roam */ WMI_HOST_BIAS_ROAM_MODE = 2, /* Host bias based roam */ WMI_LOCK_BSS_MODE = 3, /* Lock to the current BSS */ }; struct bss_bias { u8 bssid[ETH_ALEN]; s8 bias; } __packed; struct bss_bias_info { u8 num_bss; struct bss_bias bss_bias[0]; } __packed; struct low_rssi_scan_params { __le16 lrssi_scan_period; a_sle16 lrssi_scan_threshold; a_sle16 lrssi_roam_threshold; u8 roam_rssi_floor; u8 reserved[1]; } __packed; struct roam_ctrl_cmd { union { u8 bssid[ETH_ALEN]; /* WMI_FORCE_ROAM */ u8 roam_mode; /* WMI_SET_ROAM_MODE */ struct bss_bias_info bss; /* WMI_SET_HOST_BIAS */ struct low_rssi_scan_params params; /* WMI_SET_LRSSI_SCAN_PARAMS */ } __packed info; u8 roam_ctrl; } __packed; struct set_dtim_cmd { __le32 dtim_period; } __packed; /* BSS INFO HDR version 2.0 */ struct wmi_bss_info_hdr2 { __le16 ch; /* frequency in MHz */ /* see, enum wmi_bi_ftype */ u8 frame_type; u8 snr; /* note: rssi = snr - 95 dBm */ u8 bssid[ETH_ALEN]; __le16 ie_mask; } __packed; /* Command Error Event */ enum wmi_error_code { INVALID_PARAM = 0x01, ILLEGAL_STATE = 0x02, INTERNAL_ERROR = 0x03, }; struct wmi_cmd_error_event { __le16 cmd_id; u8 err_code; } __packed; struct wmi_pstream_timeout_event { u8 tx_queue_num; u8 rx_queue_num; u8 traffic_direc; u8 traffic_class; } __packed; /* * The WMI_NEIGHBOR_REPORT Event is generated by the target to inform * the host of BSS's it has found that matches the current profile. * It can be used by the host to cache PMKs and/to initiate pre-authentication * if the BSS supports it. The first bssid is always the current associated * BSS. * The bssid and bssFlags information repeats according to the number * or APs reported. */ enum wmi_bss_flags { WMI_DEFAULT_BSS_FLAGS = 0x00, WMI_PREAUTH_CAPABLE_BSS = 0x01, WMI_PMKID_VALID_BSS = 0x02, }; struct wmi_neighbor_info { u8 bssid[ETH_ALEN]; u8 bss_flags; /* enum wmi_bss_flags */ } __packed; struct wmi_neighbor_report_event { u8 num_neighbors; struct wmi_neighbor_info neighbor[0]; } __packed; /* TKIP MIC Error Event */ struct wmi_tkip_micerr_event { u8 key_id; u8 is_mcast; } __packed; enum wmi_scan_status { WMI_SCAN_STATUS_SUCCESS = 0, }; /* WMI_SCAN_COMPLETE_EVENTID */ struct wmi_scan_complete_event { a_sle32 status; } __packed; #define MAX_OPT_DATA_LEN 1400 /* * Special frame receive Event. * Mechanism used to inform host of the receiption of the special frames. * Consists of special frame info header followed by special frame body. * The 802.11 header is not included. */ struct wmi_opt_rx_info_hdr { __le16 ch; u8 frame_type; s8 snr; u8 src_addr[ETH_ALEN]; u8 bssid[ETH_ALEN]; } __packed; /* Reporting statistic */ struct tx_stats { __le32 pkt; __le32 byte; __le32 ucast_pkt; __le32 ucast_byte; __le32 mcast_pkt; __le32 mcast_byte; __le32 bcast_pkt; __le32 bcast_byte; __le32 rts_success_cnt; __le32 pkt_per_ac[4]; __le32 err_per_ac[4]; __le32 err; __le32 fail_cnt; __le32 retry_cnt; __le32 mult_retry_cnt; __le32 rts_fail_cnt; a_sle32 ucast_rate; } __packed; struct rx_stats { __le32 pkt; __le32 byte; __le32 ucast_pkt; __le32 ucast_byte; __le32 mcast_pkt; __le32 mcast_byte; __le32 bcast_pkt; __le32 bcast_byte; __le32 frgment_pkt; __le32 err; __le32 crc_err; __le32 key_cache_miss; __le32 decrypt_err; __le32 dupl_frame; a_sle32 ucast_rate; } __packed; struct tkip_ccmp_stats { __le32 tkip_local_mic_fail; __le32 tkip_cnter_measures_invoked; __le32 tkip_replays; __le32 tkip_fmt_err; __le32 ccmp_fmt_err; __le32 ccmp_replays; } __packed; struct pm_stats { __le32 pwr_save_failure_cnt; __le16 stop_tx_failure_cnt; __le16 atim_tx_failure_cnt; __le16 atim_rx_failure_cnt; __le16 bcn_rx_failure_cnt; } __packed; struct cserv_stats { __le32 cs_bmiss_cnt; __le32 cs_low_rssi_cnt; __le16 cs_connect_cnt; __le16 cs_discon_cnt; a_sle16 cs_ave_beacon_rssi; __le16 cs_roam_count; a_sle16 cs_rssi; u8 cs_snr; u8 cs_ave_beacon_snr; u8 cs_last_roam_msec; } __packed; struct wlan_net_stats { struct tx_stats tx; struct rx_stats rx; struct tkip_ccmp_stats tkip_ccmp_stats; } __packed; struct arp_stats { __le32 arp_received; __le32 arp_matched; __le32 arp_replied; } __packed; struct wlan_wow_stats { __le32 wow_pkt_dropped; __le16 wow_evt_discarded; u8 wow_host_pkt_wakeups; u8 wow_host_evt_wakeups; } __packed; struct wmi_target_stats { __le32 lq_val; a_sle32 noise_floor_calib; struct pm_stats pm_stats; struct wlan_net_stats stats; struct wlan_wow_stats wow_stats; struct arp_stats arp_stats; struct cserv_stats cserv_stats; } __packed; /* * WMI_RSSI_THRESHOLD_EVENTID. * Indicate the RSSI events to host. Events are indicated when we breach a * thresold value. */ enum wmi_rssi_threshold_val { WMI_RSSI_THRESHOLD1_ABOVE = 0, WMI_RSSI_THRESHOLD2_ABOVE, WMI_RSSI_THRESHOLD3_ABOVE, WMI_RSSI_THRESHOLD4_ABOVE, WMI_RSSI_THRESHOLD5_ABOVE, WMI_RSSI_THRESHOLD6_ABOVE, WMI_RSSI_THRESHOLD1_BELOW, WMI_RSSI_THRESHOLD2_BELOW, WMI_RSSI_THRESHOLD3_BELOW, WMI_RSSI_THRESHOLD4_BELOW, WMI_RSSI_THRESHOLD5_BELOW, WMI_RSSI_THRESHOLD6_BELOW }; struct wmi_rssi_threshold_event { a_sle16 rssi; u8 range; } __packed; enum wmi_snr_threshold_val { WMI_SNR_THRESHOLD1_ABOVE = 1, WMI_SNR_THRESHOLD1_BELOW, WMI_SNR_THRESHOLD2_ABOVE, WMI_SNR_THRESHOLD2_BELOW, WMI_SNR_THRESHOLD3_ABOVE, WMI_SNR_THRESHOLD3_BELOW, WMI_SNR_THRESHOLD4_ABOVE, WMI_SNR_THRESHOLD4_BELOW }; struct wmi_snr_threshold_event { /* see, enum wmi_snr_threshold_val */ u8 range; u8 snr; } __packed; /* WMI_REPORT_ROAM_TBL_EVENTID */ #define MAX_ROAM_TBL_CAND 5 struct wmi_bss_roam_info { a_sle32 roam_util; u8 bssid[ETH_ALEN]; s8 rssi; s8 rssidt; s8 last_rssi; s8 util; s8 bias; /* for alignment */ u8 reserved; } __packed; struct wmi_target_roam_tbl { __le16 roam_mode; __le16 num_entries; struct wmi_bss_roam_info info[]; } __packed; /* WMI_CAC_EVENTID */ enum cac_indication { CAC_INDICATION_ADMISSION = 0x00, CAC_INDICATION_ADMISSION_RESP = 0x01, CAC_INDICATION_DELETE = 0x02, CAC_INDICATION_NO_RESP = 0x03, }; #define WMM_TSPEC_IE_LEN 63 struct wmi_cac_event { u8 ac; u8 cac_indication; u8 status_code; u8 tspec_suggestion[WMM_TSPEC_IE_LEN]; } __packed; /* WMI_APLIST_EVENTID */ enum aplist_ver { APLIST_VER1 = 1, }; struct wmi_ap_info_v1 { u8 bssid[ETH_ALEN]; __le16 channel; } __packed; union wmi_ap_info { struct wmi_ap_info_v1 ap_info_v1; } __packed; struct wmi_aplist_event { u8 ap_list_ver; u8 num_ap; union wmi_ap_info ap_list[1]; } __packed; /* Developer Commands */ /* * WMI_SET_BITRATE_CMDID * * Get bit rate cmd uses same definition as set bit rate cmd */ enum wmi_bit_rate { RATE_AUTO = -1, RATE_1Mb = 0, RATE_2Mb = 1, RATE_5_5Mb = 2, RATE_11Mb = 3, RATE_6Mb = 4, RATE_9Mb = 5, RATE_12Mb = 6, RATE_18Mb = 7, RATE_24Mb = 8, RATE_36Mb = 9, RATE_48Mb = 10, RATE_54Mb = 11, RATE_MCS_0_20 = 12, RATE_MCS_1_20 = 13, RATE_MCS_2_20 = 14, RATE_MCS_3_20 = 15, RATE_MCS_4_20 = 16, RATE_MCS_5_20 = 17, RATE_MCS_6_20 = 18, RATE_MCS_7_20 = 19, RATE_MCS_0_40 = 20, RATE_MCS_1_40 = 21, RATE_MCS_2_40 = 22, RATE_MCS_3_40 = 23, RATE_MCS_4_40 = 24, RATE_MCS_5_40 = 25, RATE_MCS_6_40 = 26, RATE_MCS_7_40 = 27, }; struct wmi_bit_rate_reply { /* see, enum wmi_bit_rate */ s8 rate_index; } __packed; /* * WMI_SET_FIXRATES_CMDID * * Get fix rates cmd uses same definition as set fix rates cmd */ struct wmi_fix_rates_reply { /* see wmi_bit_rate */ __le32 fix_rate_mask; } __packed; enum roam_data_type { /* get the roam time data */ ROAM_DATA_TIME = 1, }; struct wmi_target_roam_time { __le32 disassoc_time; __le32 no_txrx_time; __le32 assoc_time; __le32 allow_txrx_time; u8 disassoc_bssid[ETH_ALEN]; s8 disassoc_bss_rssi; u8 assoc_bssid[ETH_ALEN]; s8 assoc_bss_rssi; } __packed; enum wmi_txop_cfg { WMI_TXOP_DISABLED = 0, WMI_TXOP_ENABLED }; struct wmi_set_wmm_txop_cmd { u8 txop_enable; } __packed; struct wmi_set_keepalive_cmd { u8 keep_alive_intvl; } __packed; struct wmi_get_keepalive_cmd { __le32 configured; u8 keep_alive_intvl; } __packed; struct wmi_set_appie_cmd { u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ u8 ie_len; u8 ie_info[0]; } __packed; struct wmi_set_ie_cmd { u8 ie_id; u8 ie_field; /* enum wmi_ie_field_type */ u8 ie_len; u8 reserved; u8 ie_info[0]; } __packed; /* Notify the WSC registration status to the target */ #define WSC_REG_ACTIVE 1 #define WSC_REG_INACTIVE 0 #define WOW_MAX_FILTERS_PER_LIST 4 #define WOW_PATTERN_SIZE 64 #define WOW_MASK_SIZE 64 #define MAC_MAX_FILTERS_PER_LIST 4 struct wow_filter { u8 wow_valid_filter; u8 wow_filter_id; u8 wow_filter_size; u8 wow_filter_offset; u8 wow_filter_mask[WOW_MASK_SIZE]; u8 wow_filter_pattern[WOW_PATTERN_SIZE]; } __packed; #define MAX_IP_ADDRS 2 struct wmi_set_ip_cmd { /* IP in network byte order */ __be32 ips[MAX_IP_ADDRS]; } __packed; enum ath6kl_wow_filters { WOW_FILTER_SSID = BIT(1), WOW_FILTER_OPTION_MAGIC_PACKET = BIT(2), WOW_FILTER_OPTION_EAP_REQ = BIT(3), WOW_FILTER_OPTION_PATTERNS = BIT(4), WOW_FILTER_OPTION_OFFLOAD_ARP = BIT(5), WOW_FILTER_OPTION_OFFLOAD_NS = BIT(6), WOW_FILTER_OPTION_OFFLOAD_GTK = BIT(7), WOW_FILTER_OPTION_8021X_4WAYHS = BIT(8), WOW_FILTER_OPTION_NLO_DISCVRY = BIT(9), WOW_FILTER_OPTION_NWK_DISASSOC = BIT(10), WOW_FILTER_OPTION_GTK_ERROR = BIT(11), WOW_FILTER_OPTION_TEST_MODE = BIT(15), }; enum ath6kl_host_mode { ATH6KL_HOST_MODE_AWAKE, ATH6KL_HOST_MODE_ASLEEP, }; struct wmi_set_host_sleep_mode_cmd { __le32 awake; __le32 asleep; } __packed; enum ath6kl_wow_mode { ATH6KL_WOW_MODE_DISABLE, ATH6KL_WOW_MODE_ENABLE, }; struct wmi_set_wow_mode_cmd { __le32 enable_wow; __le32 filter; __le16 host_req_delay; } __packed; struct wmi_add_wow_pattern_cmd { u8 filter_list_id; u8 filter_size; u8 filter_offset; u8 filter[0]; } __packed; struct wmi_del_wow_pattern_cmd { __le16 filter_list_id; __le16 filter_id; } __packed; /* WMI_SET_AKMP_PARAMS_CMD */ struct wmi_pmkid { u8 pmkid[WMI_PMKID_LEN]; } __packed; /* WMI_GET_PMKID_LIST_CMD Reply */ struct wmi_pmkid_list_reply { __le32 num_pmkid; u8 bssid_list[ETH_ALEN][1]; struct wmi_pmkid pmkid_list[1]; } __packed; /* WMI_ADDBA_REQ_EVENTID */ struct wmi_addba_req_event { u8 tid; u8 win_sz; __le16 st_seq_no; /* f/w response for ADDBA Req; OK (0) or failure (!=0) */ u8 status; } __packed; /* WMI_ADDBA_RESP_EVENTID */ struct wmi_addba_resp_event { u8 tid; /* OK (0), failure (!=0) */ u8 status; /* three values: not supported(0), 3839, 8k */ __le16 amsdu_sz; } __packed; /* WMI_DELBA_EVENTID * f/w received a DELBA for peer and processed it. * Host is notified of this */ struct wmi_delba_event { u8 tid; u8 is_peer_initiator; __le16 reason_code; } __packed; #define PEER_NODE_JOIN_EVENT 0x00 #define PEER_NODE_LEAVE_EVENT 0x01 #define PEER_FIRST_NODE_JOIN_EVENT 0x10 #define PEER_LAST_NODE_LEAVE_EVENT 0x11 struct wmi_peer_node_event { u8 event_code; u8 peer_mac_addr[ETH_ALEN]; } __packed; /* Transmit complete event data structure(s) */ /* version 1 of tx complete msg */ struct tx_complete_msg_v1 { #define TX_COMPLETE_STATUS_SUCCESS 0 #define TX_COMPLETE_STATUS_RETRIES 1 #define TX_COMPLETE_STATUS_NOLINK 2 #define TX_COMPLETE_STATUS_TIMEOUT 3 #define TX_COMPLETE_STATUS_OTHER 4 u8 status; /* packet ID to identify parent packet */ u8 pkt_id; /* rate index on successful transmission */ u8 rate_idx; /* number of ACK failures in tx attempt */ u8 ack_failures; } __packed; struct wmi_tx_complete_event { /* no of tx comp msgs following this struct */ u8 num_msg; /* length in bytes for each individual msg following this struct */ u8 msg_len; /* version of tx complete msg data following this struct */ u8 msg_type; /* individual messages follow this header */ u8 reserved; } __packed; /* * ------- AP Mode definitions -------------- */ /* * !!! Warning !!! * -Changing the following values needs compilation of both driver and firmware */ #define AP_MAX_NUM_STA 10 /* Spl. AID used to set DTIM flag in the beacons */ #define MCAST_AID 0xFF #define DEF_AP_COUNTRY_CODE "US " /* Used with WMI_AP_SET_NUM_STA_CMDID */ /* * Used with WMI_AP_SET_MLME_CMDID */ /* MLME Commands */ #define WMI_AP_MLME_ASSOC 1 /* associate station */ #define WMI_AP_DISASSOC 2 /* disassociate station */ #define WMI_AP_DEAUTH 3 /* deauthenticate station */ #define WMI_AP_MLME_AUTHORIZE 4 /* authorize station */ #define WMI_AP_MLME_UNAUTHORIZE 5 /* unauthorize station */ struct wmi_ap_set_mlme_cmd { u8 mac[ETH_ALEN]; __le16 reason; /* 802.11 reason code */ u8 cmd; /* operation to perform (WMI_AP_*) */ } __packed; struct wmi_ap_set_pvb_cmd { __le32 flag; __le16 rsvd; __le16 aid; } __packed; struct wmi_rx_frame_format_cmd { /* version of meta data for rx packets <0 = default> (0-7 = valid) */ u8 meta_ver; /* * 1 == leave .11 header intact, * 0 == replace .11 header with .3 */ u8 dot11_hdr; /* * 1 == defragmentation is performed by host, * 0 == performed by target */ u8 defrag_on_host; /* for alignment */ u8 reserved[1]; } __packed; struct wmi_ap_hidden_ssid_cmd { u8 hidden_ssid; } __packed; struct wmi_set_inact_period_cmd { __le32 inact_period; u8 num_null_func; } __packed; /* AP mode events */ struct wmi_ap_set_apsd_cmd { u8 enable; } __packed; enum wmi_ap_apsd_buffered_traffic_flags { WMI_AP_APSD_NO_DELIVERY_FRAMES = 0x1, }; struct wmi_ap_apsd_buffered_traffic_cmd { __le16 aid; __le16 bitmap; __le32 flags; } __packed; /* WMI_PS_POLL_EVENT */ struct wmi_pspoll_event { __le16 aid; } __packed; struct wmi_per_sta_stat { __le32 tx_bytes; __le32 tx_pkts; __le32 tx_error; __le32 tx_discard; __le32 rx_bytes; __le32 rx_pkts; __le32 rx_error; __le32 rx_discard; __le32 aid; } __packed; struct wmi_ap_mode_stat { __le32 action; struct wmi_per_sta_stat sta[AP_MAX_NUM_STA + 1]; } __packed; /* End of AP mode definitions */ struct wmi_remain_on_chnl_cmd { __le32 freq; __le32 duration; } __packed; /* wmi_send_action_cmd is to be deprecated. Use * wmi_send_mgmt_cmd instead. The new structure supports P2P mgmt * operations using station interface. */ struct wmi_send_action_cmd { __le32 id; __le32 freq; __le32 wait; __le16 len; u8 data[0]; } __packed; struct wmi_send_mgmt_cmd { __le32 id; __le32 freq; __le32 wait; __le32 no_cck; __le16 len; u8 data[0]; } __packed; struct wmi_tx_status_event { __le32 id; u8 ack_status; } __packed; struct wmi_probe_req_report_cmd { u8 enable; } __packed; struct wmi_disable_11b_rates_cmd { u8 disable; } __packed; struct wmi_set_appie_extended_cmd { u8 role_id; u8 mgmt_frm_type; u8 ie_len; u8 ie_info[0]; } __packed; struct wmi_remain_on_chnl_event { __le32 freq; __le32 duration; } __packed; struct wmi_cancel_remain_on_chnl_event { __le32 freq; __le32 duration; u8 status; } __packed; struct wmi_rx_action_event { __le32 freq; __le16 len; u8 data[0]; } __packed; struct wmi_p2p_capabilities_event { __le16 len; u8 data[0]; } __packed; struct wmi_p2p_rx_probe_req_event { __le32 freq; __le16 len; u8 data[0]; } __packed; #define P2P_FLAG_CAPABILITIES_REQ (0x00000001) #define P2P_FLAG_MACADDR_REQ (0x00000002) #define P2P_FLAG_HMODEL_REQ (0x00000002) struct wmi_get_p2p_info { __le32 info_req_flags; } __packed; struct wmi_p2p_info_event { __le32 info_req_flags; __le16 len; u8 data[0]; } __packed; struct wmi_p2p_capabilities { u8 go_power_save; } __packed; struct wmi_p2p_macaddr { u8 mac_addr[ETH_ALEN]; } __packed; struct wmi_p2p_hmodel { u8 p2p_model; } __packed; struct wmi_p2p_probe_response_cmd { __le32 freq; u8 destination_addr[ETH_ALEN]; __le16 len; u8 data[0]; } __packed; /* Extended WMI (WMIX) * * Extended WMIX commands are encapsulated in a WMI message with * cmd=WMI_EXTENSION_CMD. * * Extended WMI commands are those that are needed during wireless * operation, but which are not really wireless commands. This allows, * for instance, platform-specific commands. Extended WMI commands are * embedded in a WMI command message with WMI_COMMAND_ID=WMI_EXTENSION_CMDID. * Extended WMI events are similarly embedded in a WMI event message with * WMI_EVENT_ID=WMI_EXTENSION_EVENTID. */ struct wmix_cmd_hdr { __le32 cmd_id; } __packed; enum wmix_command_id { WMIX_DSETOPEN_REPLY_CMDID = 0x2001, WMIX_DSETDATA_REPLY_CMDID, WMIX_GPIO_OUTPUT_SET_CMDID, WMIX_GPIO_INPUT_GET_CMDID, WMIX_GPIO_REGISTER_SET_CMDID, WMIX_GPIO_REGISTER_GET_CMDID, WMIX_GPIO_INTR_ACK_CMDID, WMIX_HB_CHALLENGE_RESP_CMDID, WMIX_DBGLOG_CFG_MODULE_CMDID, WMIX_PROF_CFG_CMDID, /* 0x200a */ WMIX_PROF_ADDR_SET_CMDID, WMIX_PROF_START_CMDID, WMIX_PROF_STOP_CMDID, WMIX_PROF_COUNT_GET_CMDID, }; enum wmix_event_id { WMIX_DSETOPENREQ_EVENTID = 0x3001, WMIX_DSETCLOSE_EVENTID, WMIX_DSETDATAREQ_EVENTID, WMIX_GPIO_INTR_EVENTID, WMIX_GPIO_DATA_EVENTID, WMIX_GPIO_ACK_EVENTID, WMIX_HB_CHALLENGE_RESP_EVENTID, WMIX_DBGLOG_EVENTID, WMIX_PROF_COUNT_EVENTID, }; /* * ------Error Detection support------- */ /* * WMIX_HB_CHALLENGE_RESP_CMDID * Heartbeat Challenge Response command */ struct wmix_hb_challenge_resp_cmd { __le32 cookie; __le32 source; } __packed; struct ath6kl_wmix_dbglog_cfg_module_cmd { __le32 valid; __le32 config; } __packed; /* End of Extended WMI (WMIX) */ enum wmi_sync_flag { NO_SYNC_WMIFLAG = 0, /* transmit all queued data before cmd */ SYNC_BEFORE_WMIFLAG, /* any new data waits until cmd execs */ SYNC_AFTER_WMIFLAG, SYNC_BOTH_WMIFLAG, /* end marker */ END_WMIFLAG }; enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi); void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id); int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, u8 msg_type, u32 flags, enum wmi_data_hdr_data_type data_type, u8 meta_ver, void *tx_meta_info, u8 if_idx); int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb); int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, u32 layer2_priority, bool wmm_enabled, u8 *ac); int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag); int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, enum network_type nw_type, enum dot11_auth_mode dot11_auth_mode, enum auth_mode auth_mode, enum crypto_type pairwise_crypto, u8 pairwise_crypto_len, enum crypto_type group_crypto, u8 group_crypto_len, int ssid_len, u8 *ssid, u8 *bssid, u16 channel, u32 ctrl_flags, u8 nw_subtype); int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid, u16 channel); int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list); int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates); int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, u16 fg_end_sec, u16 bg_sec, u16 minact_chdw_msec, u16 maxact_chdw_msec, u16 pas_chdw_msec, u8 short_scan_ratio, u8 scan_ctrl_flag, u32 max_dfsch_act_time, u16 maxact_scan_per_ssid); int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 if_idx, u8 filter, u32 ie_mask); int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag, u8 ssid_len, u8 *ssid); int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx, u16 listen_interval, u16 listen_beacons); int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx, u16 bmiss_time, u16 num_beacons); int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode); int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u8 if_idx, u16 idle_period, u16 ps_poll_num, u16 dtim_policy, u16 tx_wakup_policy, u16 num_tx_to_wakeup, u16 ps_fail_event_policy); int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi, u8 if_idx, struct wmi_create_pstream_cmd *pstream); int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class, u8 tsid); int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 if_idx, u8 timeout); int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold); int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 if_idx, u8 status, u8 preamble_policy); int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source); int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config); int ath6kl_wmi_get_stats_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, enum crypto_type key_type, u8 key_usage, u8 key_len, u8 *key_rsc, unsigned int key_rsc_len, u8 *key_material, u8 key_op_ctrl, u8 *mac_addr, enum wmi_sync_flag sync_flag); int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk); int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index); int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid, const u8 *pmkid, bool set); int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 if_idx, u8 dbM); int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi); int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg); int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, u8 keep_alive_intvl); int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, enum ieee80211_band band, struct ath6kl_htcap *htcap); int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); s32 ath6kl_wmi_get_rate(s8 rate_index); int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx, __be32 ips0, __be32 ips1); int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_host_mode host_mode); int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, const struct cfg80211_bitrate_mask *mask); int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_wow_mode wow_mode, u32 filter, u16 host_req_delay); int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u8 list_id, u8 filter_size, u8 filter_offset, const u8 *filter, const u8 *mask); int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u16 list_id, u16 filter_id); int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period); int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid); int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode); int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, u8 *filter, bool add_filter); int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable); /* AP mode uAPSD */ int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); int ath6kl_wmi_set_apsd_bfrd_traf(struct wmi *wmi, u8 if_idx, u16 aid, u16 bitmap, u32 flags); u8 ath6kl_wmi_get_traffic_class(u8 user_priority); u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri); /* AP mode */ int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx, struct wmi_connect_cmd *p); int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac, u16 reason); int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u8 if_idx, u16 aid, bool flag); int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, u8 rx_meta_version, bool rx_dot11_hdr, bool defrag_on_host); int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, const u8 *ie_info, u8 ie_len); /* P2P */ int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable); int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur); int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, u32 wait, const u8 *data, u16 data_len, u32 no_cck); int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq, const u8 *dst, const u8 *data, u16 data_len); int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u8 if_idx, u32 info_req_flags); int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout); void ath6kl_wmi_sscan_timer(unsigned long ptr); struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); void *ath6kl_wmi_init(struct ath6kl *devt); void ath6kl_wmi_shutdown(struct wmi *wmi); void ath6kl_wmi_reset(struct wmi *wmi); #endif /* WMI_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/wmi.c0000644000175000017500000031630212026211315023373 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "core.h" #include "debug.h" #include "testmode.h" #include "../regd.h" #include "../regd_common.h" static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx); static const s32 wmi_rate_tbl[][2] = { /* {W/O SGI, with SGI} */ {1000, 1000}, {2000, 2000}, {5500, 5500}, {11000, 11000}, {6000, 6000}, {9000, 9000}, {12000, 12000}, {18000, 18000}, {24000, 24000}, {36000, 36000}, {48000, 48000}, {54000, 54000}, {6500, 7200}, {13000, 14400}, {19500, 21700}, {26000, 28900}, {39000, 43300}, {52000, 57800}, {58500, 65000}, {65000, 72200}, {13500, 15000}, {27000, 30000}, {40500, 45000}, {54000, 60000}, {81000, 90000}, {108000, 120000}, {121500, 135000}, {135000, 150000}, {0, 0} }; /* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */ static const u8 up_to_ac[] = { WMM_AC_BE, WMM_AC_BK, WMM_AC_BK, WMM_AC_BE, WMM_AC_VI, WMM_AC_VI, WMM_AC_VO, WMM_AC_VO, }; void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id) { if (WARN_ON(ep_id == ENDPOINT_UNUSED || ep_id >= ENDPOINT_MAX)) return; wmi->ep_id = ep_id; } enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi) { return wmi->ep_id; } struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx) { struct ath6kl_vif *vif, *found = NULL; if (WARN_ON(if_idx > (ar->vif_max - 1))) return NULL; /* FIXME: Locking */ spin_lock_bh(&ar->list_lock); list_for_each_entry(vif, &ar->vif_list, list) { if (vif->fw_vif_idx == if_idx) { found = vif; break; } } spin_unlock_bh(&ar->list_lock); return found; } /* Performs DIX to 802.3 encapsulation for transmit packets. * Assumes the entire DIX header is contigous and that there is * enough room in the buffer for a 802.3 mac header and LLC+SNAP headers. */ int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb) { struct ath6kl_llc_snap_hdr *llc_hdr; struct ethhdr *eth_hdr; size_t new_len; __be16 type; u8 *datap; u16 size; if (WARN_ON(skb == NULL)) return -EINVAL; size = sizeof(struct ath6kl_llc_snap_hdr) + sizeof(struct wmi_data_hdr); if (skb_headroom(skb) < size) return -ENOMEM; eth_hdr = (struct ethhdr *) skb->data; type = eth_hdr->h_proto; if (!is_ethertype(be16_to_cpu(type))) { ath6kl_dbg(ATH6KL_DBG_WMI, "%s: pkt is already in 802.3 format\n", __func__); return 0; } new_len = skb->len - sizeof(*eth_hdr) + sizeof(*llc_hdr); skb_push(skb, sizeof(struct ath6kl_llc_snap_hdr)); datap = skb->data; eth_hdr->h_proto = cpu_to_be16(new_len); memcpy(datap, eth_hdr, sizeof(*eth_hdr)); llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap + sizeof(*eth_hdr)); llc_hdr->dsap = 0xAA; llc_hdr->ssap = 0xAA; llc_hdr->cntl = 0x03; llc_hdr->org_code[0] = 0x0; llc_hdr->org_code[1] = 0x0; llc_hdr->org_code[2] = 0x0; llc_hdr->eth_type = type; return 0; } static int ath6kl_wmi_meta_add(struct wmi *wmi, struct sk_buff *skb, u8 *version, void *tx_meta_info) { struct wmi_tx_meta_v1 *v1; struct wmi_tx_meta_v2 *v2; if (WARN_ON(skb == NULL || version == NULL)) return -EINVAL; switch (*version) { case WMI_META_VERSION_1: skb_push(skb, WMI_MAX_TX_META_SZ); v1 = (struct wmi_tx_meta_v1 *) skb->data; v1->pkt_id = 0; v1->rate_plcy_id = 0; *version = WMI_META_VERSION_1; break; case WMI_META_VERSION_2: skb_push(skb, WMI_MAX_TX_META_SZ); v2 = (struct wmi_tx_meta_v2 *) skb->data; memcpy(v2, (struct wmi_tx_meta_v2 *) tx_meta_info, sizeof(struct wmi_tx_meta_v2)); break; } return 0; } int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, u8 msg_type, u32 flags, enum wmi_data_hdr_data_type data_type, u8 meta_ver, void *tx_meta_info, u8 if_idx) { struct wmi_data_hdr *data_hdr; int ret; if (WARN_ON(skb == NULL || (if_idx > wmi->parent_dev->vif_max - 1))) return -EINVAL; if (tx_meta_info) { ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info); if (ret) return ret; } skb_push(skb, sizeof(struct wmi_data_hdr)); data_hdr = (struct wmi_data_hdr *)skb->data; memset(data_hdr, 0, sizeof(struct wmi_data_hdr)); data_hdr->info = msg_type << WMI_DATA_HDR_MSG_TYPE_SHIFT; data_hdr->info |= data_type << WMI_DATA_HDR_DATA_TYPE_SHIFT; if (flags & WMI_DATA_HDR_FLAGS_MORE) data_hdr->info |= WMI_DATA_HDR_MORE; if (flags & WMI_DATA_HDR_FLAGS_EOSP) data_hdr->info3 |= cpu_to_le16(WMI_DATA_HDR_EOSP); data_hdr->info2 |= cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT); data_hdr->info3 |= cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK); return 0; } u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri) { struct iphdr *ip_hdr = (struct iphdr *) pkt; u8 ip_pri; /* * Determine IPTOS priority * * IP-TOS - 8bits * : DSCP(6-bits) ECN(2-bits) * : DSCP - P2 P1 P0 X X X * where (P2 P1 P0) form 802.1D */ ip_pri = ip_hdr->tos >> 5; ip_pri &= 0x7; if ((layer2_pri & 0x7) > ip_pri) return (u8) layer2_pri & 0x7; else return ip_pri; } u8 ath6kl_wmi_get_traffic_class(u8 user_priority) { return up_to_ac[user_priority & 0x7]; } int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, u32 layer2_priority, bool wmm_enabled, u8 *ac) { struct wmi_data_hdr *data_hdr; struct ath6kl_llc_snap_hdr *llc_hdr; struct wmi_create_pstream_cmd cmd; u32 meta_size, hdr_size; u16 ip_type = IP_ETHERTYPE; u8 stream_exist, usr_pri; u8 traffic_class = WMM_AC_BE; u8 *datap; if (WARN_ON(skb == NULL)) return -EINVAL; datap = skb->data; data_hdr = (struct wmi_data_hdr *) datap; meta_size = ((le16_to_cpu(data_hdr->info2) >> WMI_DATA_HDR_META_SHIFT) & WMI_DATA_HDR_META_MASK) ? WMI_MAX_TX_META_SZ : 0; if (!wmm_enabled) { /* If WMM is disabled all traffic goes as BE traffic */ usr_pri = 0; } else { hdr_size = sizeof(struct ethhdr); llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap + sizeof(struct wmi_data_hdr) + meta_size + hdr_size); if (llc_hdr->eth_type == htons(ip_type)) { /* * Extract the endpoint info from the TOS field * in the IP header. */ usr_pri = ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) + sizeof(struct ath6kl_llc_snap_hdr), layer2_priority); } else usr_pri = layer2_priority & 0x7; /* * Queue the EAPOL frames in the same WMM_AC_VO queue * as that of management frames. */ if (skb->protocol == cpu_to_be16(ETH_P_PAE)) usr_pri = WMI_VOICE_USER_PRIORITY; } /* * workaround for WMM S5 * * FIXME: wmi->traffic_class is always 100 so this test doesn't * make sense */ if ((wmi->traffic_class == WMM_AC_VI) && ((usr_pri == 5) || (usr_pri == 4))) usr_pri = 1; /* Convert user priority to traffic class */ traffic_class = up_to_ac[usr_pri & 0x7]; wmi_data_hdr_set_up(data_hdr, usr_pri); spin_lock_bh(&wmi->lock); stream_exist = wmi->fat_pipe_exist; spin_unlock_bh(&wmi->lock); if (!(stream_exist & (1 << traffic_class))) { memset(&cmd, 0, sizeof(cmd)); cmd.traffic_class = traffic_class; cmd.user_pri = usr_pri; cmd.inactivity_int = cpu_to_le32(WMI_IMPLICIT_PSTREAM_INACTIVITY_INT); /* Implicit streams are created with TSID 0xFF */ cmd.tsid = WMI_IMPLICIT_PSTREAM; ath6kl_wmi_create_pstream_cmd(wmi, if_idx, &cmd); } *ac = traffic_class; return 0; } int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb) { struct ieee80211_hdr_3addr *pwh, wh; struct ath6kl_llc_snap_hdr *llc_hdr; struct ethhdr eth_hdr; u32 hdr_size; u8 *datap; __le16 sub_type; if (WARN_ON(skb == NULL)) return -EINVAL; datap = skb->data; pwh = (struct ieee80211_hdr_3addr *) datap; sub_type = pwh->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE); memcpy((u8 *) &wh, datap, sizeof(struct ieee80211_hdr_3addr)); /* Strip off the 802.11 header */ if (sub_type == cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { hdr_size = roundup(sizeof(struct ieee80211_qos_hdr), sizeof(u32)); skb_pull(skb, hdr_size); } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA)) skb_pull(skb, sizeof(struct ieee80211_hdr_3addr)); datap = skb->data; llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap); memset(ð_hdr, 0, sizeof(eth_hdr)); eth_hdr.h_proto = llc_hdr->eth_type; switch ((le16_to_cpu(wh.frame_control)) & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { case 0: memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN); memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN); break; case IEEE80211_FCTL_TODS: memcpy(eth_hdr.h_dest, wh.addr3, ETH_ALEN); memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN); break; case IEEE80211_FCTL_FROMDS: memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN); memcpy(eth_hdr.h_source, wh.addr3, ETH_ALEN); break; case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: break; } skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr)); skb_push(skb, sizeof(eth_hdr)); datap = skb->data; memcpy(datap, ð_hdr, sizeof(eth_hdr)); return 0; } /* * Performs 802.3 to DIX encapsulation for received packets. * Assumes the entire 802.3 header is contigous. */ int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb) { struct ath6kl_llc_snap_hdr *llc_hdr; struct ethhdr eth_hdr; u8 *datap; if (WARN_ON(skb == NULL)) return -EINVAL; datap = skb->data; memcpy(ð_hdr, datap, sizeof(eth_hdr)); llc_hdr = (struct ath6kl_llc_snap_hdr *) (datap + sizeof(eth_hdr)); eth_hdr.h_proto = llc_hdr->eth_type; skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr)); datap = skb->data; memcpy(datap, ð_hdr, sizeof(eth_hdr)); return 0; } static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) { struct tx_complete_msg_v1 *msg_v1; struct wmi_tx_complete_event *evt; int index; u16 size; evt = (struct wmi_tx_complete_event *) datap; ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n", evt->num_msg, evt->msg_len, evt->msg_type); for (index = 0; index < evt->num_msg; index++) { size = sizeof(struct wmi_tx_complete_event) + (index * sizeof(struct tx_complete_msg_v1)); msg_v1 = (struct tx_complete_msg_v1 *)(datap + size); ath6kl_dbg(ATH6KL_DBG_WMI, "msg: %d %d %d %d\n", msg_v1->status, msg_v1->pkt_id, msg_v1->rate_idx, msg_v1->ack_failures); } return 0; } static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_remain_on_chnl_event *ev; u32 freq; u32 dur; struct ieee80211_channel *chan; struct ath6kl *ar = wmi->parent_dev; u32 id; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_remain_on_chnl_event *) datap; freq = le32_to_cpu(ev->freq); dur = le32_to_cpu(ev->duration); ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n", freq, dur); chan = ieee80211_get_channel(ar->wiphy, freq); if (!chan) { ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: Unknown channel (freq=%u)\n", freq); return -EINVAL; } id = vif->last_roc_id; cfg80211_ready_on_channel(&vif->wdev, id, chan, NL80211_CHAN_NO_HT, dur, GFP_ATOMIC); return 0; } static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_cancel_remain_on_chnl_event *ev; u32 freq; u32 dur; struct ieee80211_channel *chan; struct ath6kl *ar = wmi->parent_dev; u32 id; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_cancel_remain_on_chnl_event *) datap; freq = le32_to_cpu(ev->freq); dur = le32_to_cpu(ev->duration); ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u status=%u\n", freq, dur, ev->status); chan = ieee80211_get_channel(ar->wiphy, freq); if (!chan) { ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: Unknown channel (freq=%u)\n", freq); return -EINVAL; } if (vif->last_cancel_roc_id && vif->last_cancel_roc_id + 1 == vif->last_roc_id) id = vif->last_cancel_roc_id; /* event for cancel command */ else id = vif->last_roc_id; /* timeout on uncanceled r-o-c */ vif->last_cancel_roc_id = 0; cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, NL80211_CHAN_NO_HT, GFP_ATOMIC); return 0; } static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_tx_status_event *ev; u32 id; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_tx_status_event *) datap; id = le32_to_cpu(ev->id); ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n", id, ev->ack_status); if (wmi->last_mgmt_tx_frame) { cfg80211_mgmt_tx_status(&vif->wdev, id, wmi->last_mgmt_tx_frame, wmi->last_mgmt_tx_frame_len, !!ev->ack_status, GFP_ATOMIC); kfree(wmi->last_mgmt_tx_frame); wmi->last_mgmt_tx_frame = NULL; wmi->last_mgmt_tx_frame_len = 0; } return 0; } static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_p2p_rx_probe_req_event *ev; u32 freq; u16 dlen; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_p2p_rx_probe_req_event *) datap; freq = le32_to_cpu(ev->freq); dlen = le16_to_cpu(ev->len); if (datap + len < ev->data + dlen) { ath6kl_err("invalid wmi_p2p_rx_probe_req_event: len=%d dlen=%u\n", len, dlen); return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u probe_req_report=%d\n", dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, GFP_ATOMIC); return 0; } static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len) { struct wmi_p2p_capabilities_event *ev; u16 dlen; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_p2p_capabilities_event *) datap; dlen = le16_to_cpu(ev->len); ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_capab: len=%u\n", dlen); return 0; } static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_rx_action_event *ev; u32 freq; u16 dlen; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_rx_action_event *) datap; freq = le32_to_cpu(ev->freq); dlen = le16_to_cpu(ev->len); if (datap + len < ev->data + dlen) { ath6kl_err("invalid wmi_rx_action_event: len=%d dlen=%u\n", len, dlen); return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, GFP_ATOMIC); return 0; } static int ath6kl_wmi_p2p_info_event_rx(u8 *datap, int len) { struct wmi_p2p_info_event *ev; u32 flags; u16 dlen; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_p2p_info_event *) datap; flags = le32_to_cpu(ev->info_req_flags); dlen = le16_to_cpu(ev->len); ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: flags=%x len=%d\n", flags, dlen); if (flags & P2P_FLAG_CAPABILITIES_REQ) { struct wmi_p2p_capabilities *cap; if (dlen < sizeof(*cap)) return -EINVAL; cap = (struct wmi_p2p_capabilities *) ev->data; ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: GO Power Save = %d\n", cap->go_power_save); } if (flags & P2P_FLAG_MACADDR_REQ) { struct wmi_p2p_macaddr *mac; if (dlen < sizeof(*mac)) return -EINVAL; mac = (struct wmi_p2p_macaddr *) ev->data; ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: MAC Address = %pM\n", mac->mac_addr); } if (flags & P2P_FLAG_HMODEL_REQ) { struct wmi_p2p_hmodel *mod; if (dlen < sizeof(*mod)) return -EINVAL; mod = (struct wmi_p2p_hmodel *) ev->data; ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: P2P Model = %d (%s)\n", mod->p2p_model, mod->p2p_model ? "host" : "firmware"); } return 0; } static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size) { struct sk_buff *skb; skb = ath6kl_buf_alloc(size); if (!skb) return NULL; skb_put(skb, size); if (size) memset(skb->data, 0, size); return skb; } /* Send a "simple" wmi command -- one with no arguments */ static int ath6kl_wmi_simple_cmd(struct wmi *wmi, u8 if_idx, enum wmi_cmd_id cmd_id) { struct sk_buff *skb; int ret; skb = ath6kl_wmi_get_new_buf(0); if (!skb) return -ENOMEM; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, cmd_id, NO_SYNC_WMIFLAG); return ret; } static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_ready_event_2 *ev = (struct wmi_ready_event_2 *) datap; if (len < sizeof(struct wmi_ready_event_2)) return -EINVAL; ath6kl_ready_event(wmi->parent_dev, ev->mac_addr, le32_to_cpu(ev->sw_version), le32_to_cpu(ev->abi_version), ev->phy_cap); return 0; } /* * Mechanism to modify the roaming behavior in the firmware. The lower rssi * at which the station has to roam can be passed with * WMI_SET_LRSSI_SCAN_PARAMS. Subtract 96 from RSSI to get the signal level * in dBm. */ int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi) { struct sk_buff *skb; struct roam_ctrl_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct roam_ctrl_cmd *) skb->data; cmd->info.params.lrssi_scan_period = cpu_to_le16(DEF_LRSSI_SCAN_PERIOD); cmd->info.params.lrssi_scan_threshold = a_cpu_to_sle16(lrssi + DEF_SCAN_FOR_ROAM_INTVL); cmd->info.params.lrssi_roam_threshold = a_cpu_to_sle16(lrssi); cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR; cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS; ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG); return 0; } int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid) { struct sk_buff *skb; struct roam_ctrl_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct roam_ctrl_cmd *) skb->data; memcpy(cmd->info.bssid, bssid, ETH_ALEN); cmd->roam_ctrl = WMI_FORCE_ROAM; ath6kl_dbg(ATH6KL_DBG_WMI, "force roam to %pM\n", bssid); return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period) { struct sk_buff *skb; struct set_dtim_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct set_dtim_cmd *) skb->data; cmd->dtim_period = cpu_to_le32(dtim_period); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_DTIM_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode) { struct sk_buff *skb; struct roam_ctrl_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct roam_ctrl_cmd *) skb->data; cmd->info.roam_mode = mode; cmd->roam_ctrl = WMI_SET_ROAM_MODE; ath6kl_dbg(ATH6KL_DBG_WMI, "set roam mode %d\n", mode); return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG); } static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_connect_event *ev; u8 *pie, *peie; if (len < sizeof(struct wmi_connect_event)) return -EINVAL; ev = (struct wmi_connect_event *) datap; if (vif->nw_type == AP_NETWORK) { /* AP mode start/STA connected event */ struct net_device *dev = vif->ndev; if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) { ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM (AP started)\n", __func__, le16_to_cpu(ev->u.ap_bss.ch), ev->u.ap_bss.bssid); ath6kl_connect_ap_mode_bss( vif, le16_to_cpu(ev->u.ap_bss.ch)); } else { ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM auth=%u keymgmt=%u cipher=%u apsd_info=%u (STA connected)\n", __func__, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr, ev->u.ap_sta.auth, ev->u.ap_sta.keymgmt, le16_to_cpu(ev->u.ap_sta.cipher), ev->u.ap_sta.apsd_info); ath6kl_connect_ap_mode_sta( vif, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr, ev->u.ap_sta.keymgmt, le16_to_cpu(ev->u.ap_sta.cipher), ev->u.ap_sta.auth, ev->assoc_req_len, ev->assoc_info + ev->beacon_ie_len, ev->u.ap_sta.apsd_info); } return 0; } /* STA/IBSS mode connection event */ ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event connect freq %d bssid %pM listen_intvl %d beacon_intvl %d type %d\n", le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid, le16_to_cpu(ev->u.sta.listen_intvl), le16_to_cpu(ev->u.sta.beacon_intvl), le32_to_cpu(ev->u.sta.nw_type)); /* Start of assoc rsp IEs */ pie = ev->assoc_info + ev->beacon_ie_len + ev->assoc_req_len + (sizeof(u16) * 3); /* capinfo, status, aid */ /* End of assoc rsp IEs */ peie = ev->assoc_info + ev->beacon_ie_len + ev->assoc_req_len + ev->assoc_resp_len; while (pie < peie) { switch (*pie) { case WLAN_EID_VENDOR_SPECIFIC: if (pie[1] > 3 && pie[2] == 0x00 && pie[3] == 0x50 && pie[4] == 0xf2 && pie[5] == WMM_OUI_TYPE) { /* WMM OUT (00:50:F2) */ if (pie[1] > 5 && pie[6] == WMM_PARAM_OUI_SUBTYPE) wmi->is_wmm_enabled = true; } break; } if (wmi->is_wmm_enabled) break; pie += pie[1] + 2; } ath6kl_connect_event(vif, le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid, le16_to_cpu(ev->u.sta.listen_intvl), le16_to_cpu(ev->u.sta.beacon_intvl), le32_to_cpu(ev->u.sta.nw_type), ev->beacon_ie_len, ev->assoc_req_len, ev->assoc_resp_len, ev->assoc_info); return 0; } static struct country_code_to_enum_rd * ath6kl_regd_find_country(u16 countryCode) { int i; for (i = 0; i < ARRAY_SIZE(allCountries); i++) { if (allCountries[i].countryCode == countryCode) return &allCountries[i]; } return NULL; } static struct reg_dmn_pair_mapping * ath6kl_get_regpair(u16 regdmn) { int i; if (regdmn == NO_ENUMRD) return NULL; for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { if (regDomainPairs[i].regDmnEnum == regdmn) return ®DomainPairs[i]; } return NULL; } static struct country_code_to_enum_rd * ath6kl_regd_find_country_by_rd(u16 regdmn) { int i; for (i = 0; i < ARRAY_SIZE(allCountries); i++) { if (allCountries[i].regDmnEnum == regdmn) return &allCountries[i]; } return NULL; } static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) { struct ath6kl_wmi_regdomain *ev; struct country_code_to_enum_rd *country = NULL; struct reg_dmn_pair_mapping *regpair = NULL; char alpha2[2]; u32 reg_code; ev = (struct ath6kl_wmi_regdomain *) datap; reg_code = le32_to_cpu(ev->reg_code); if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) country = ath6kl_regd_find_country((u16) reg_code); else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) { regpair = ath6kl_get_regpair((u16) reg_code); country = ath6kl_regd_find_country_by_rd((u16) reg_code); ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", regpair->regDmnEnum); } if (country && wmi->parent_dev->wiphy_registered) { alpha2[0] = country->isoName[0]; alpha2[1] = country->isoName[1]; regulatory_hint(wmi->parent_dev->wiphy, alpha2); ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n", alpha2[0], alpha2[1]); } } static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_disconnect_event *ev; wmi->traffic_class = 100; if (len < sizeof(struct wmi_disconnect_event)) return -EINVAL; ev = (struct wmi_disconnect_event *) datap; ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event disconnect proto_reason %d bssid %pM wmi_reason %d assoc_resp_len %d\n", le16_to_cpu(ev->proto_reason_status), ev->bssid, ev->disconn_reason, ev->assoc_resp_len); wmi->is_wmm_enabled = false; ath6kl_disconnect_event(vif, ev->disconn_reason, ev->bssid, ev->assoc_resp_len, ev->assoc_info, le16_to_cpu(ev->proto_reason_status)); return 0; } static int ath6kl_wmi_peer_node_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_peer_node_event *ev; if (len < sizeof(struct wmi_peer_node_event)) return -EINVAL; ev = (struct wmi_peer_node_event *) datap; if (ev->event_code == PEER_NODE_JOIN_EVENT) ath6kl_dbg(ATH6KL_DBG_WMI, "joined node with mac addr: %pM\n", ev->peer_mac_addr); else if (ev->event_code == PEER_NODE_LEAVE_EVENT) ath6kl_dbg(ATH6KL_DBG_WMI, "left node with mac addr: %pM\n", ev->peer_mac_addr); return 0; } static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_tkip_micerr_event *ev; if (len < sizeof(struct wmi_tkip_micerr_event)) return -EINVAL; ev = (struct wmi_tkip_micerr_event *) datap; ath6kl_tkip_micerr_event(vif, ev->key_id, ev->is_mcast); return 0; } void ath6kl_wmi_sscan_timer(unsigned long ptr) { struct ath6kl_vif *vif = (struct ath6kl_vif *) ptr; cfg80211_sched_scan_results(vif->ar->wiphy); } static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_bss_info_hdr2 *bih; u8 *buf; struct ieee80211_channel *channel; struct ath6kl *ar = wmi->parent_dev; struct ieee80211_mgmt *mgmt; struct cfg80211_bss *bss; if (len <= sizeof(struct wmi_bss_info_hdr2)) return -EINVAL; bih = (struct wmi_bss_info_hdr2 *) datap; buf = datap + sizeof(struct wmi_bss_info_hdr2); len -= sizeof(struct wmi_bss_info_hdr2); ath6kl_dbg(ATH6KL_DBG_WMI, "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" " "frame_type=%d\n", bih->ch, bih->snr, bih->snr - 95, bih->bssid, bih->frame_type); if (bih->frame_type != BEACON_FTYPE && bih->frame_type != PROBERESP_FTYPE) return 0; /* Only update BSS table for now */ if (bih->frame_type == BEACON_FTYPE && test_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags)) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); } channel = ieee80211_get_channel(ar->wiphy, le16_to_cpu(bih->ch)); if (channel == NULL) return -EINVAL; if (len < 8 + 2 + 2) return -EINVAL; if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &vif->flags) && memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) { const u8 *tim; tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2, len - 8 - 2 - 2); if (tim && tim[1] >= 2) { vif->assoc_bss_dtim_period = tim[3]; set_bit(DTIM_PERIOD_AVAIL, &vif->flags); } } /* * In theory, use of cfg80211_inform_bss() would be more natural here * since we do not have the full frame. However, at least for now, * cfg80211 can only distinguish Beacon and Probe Response frames from * each other when using cfg80211_inform_bss_frame(), so let's build a * fake IEEE 802.11 header to be able to take benefit of this. */ mgmt = kmalloc(24 + len, GFP_ATOMIC); if (mgmt == NULL) return -EINVAL; if (bih->frame_type == BEACON_FTYPE) { mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); memset(mgmt->da, 0xff, ETH_ALEN); } else { struct net_device *dev = vif->ndev; mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); memcpy(mgmt->da, dev->dev_addr, ETH_ALEN); } mgmt->duration = cpu_to_le16(0); memcpy(mgmt->sa, bih->bssid, ETH_ALEN); memcpy(mgmt->bssid, bih->bssid, ETH_ALEN); mgmt->seq_ctrl = cpu_to_le16(0); memcpy(&mgmt->u.beacon, buf, len); bss = cfg80211_inform_bss_frame(ar->wiphy, channel, mgmt, 24 + len, (bih->snr - 95) * 100, GFP_ATOMIC); kfree(mgmt); if (bss == NULL) return -ENOMEM; cfg80211_put_bss(bss); /* * Firmware doesn't return any event when scheduled scan has * finished, so we need to use a timer to find out when there are * no more results. * * The timer is started from the first bss info received, otherwise * the timer would not ever fire if the scan interval is short * enough. */ if (ar->state == ATH6KL_STATE_SCHED_SCAN && !timer_pending(&vif->sched_scan_timer)) { mod_timer(&vif->sched_scan_timer, jiffies + msecs_to_jiffies(ATH6KL_SCHED_SCAN_RESULT_DELAY)); } return 0; } /* Inactivity timeout of a fatpipe(pstream) at the target */ static int ath6kl_wmi_pstream_timeout_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_pstream_timeout_event *ev; if (len < sizeof(struct wmi_pstream_timeout_event)) return -EINVAL; ev = (struct wmi_pstream_timeout_event *) datap; /* * When the pstream (fat pipe == AC) timesout, it means there were * no thinStreams within this pstream & it got implicitly created * due to data flow on this AC. We start the inactivity timer only * for implicitly created pstream. Just reset the host state. */ spin_lock_bh(&wmi->lock); wmi->stream_exist_for_ac[ev->traffic_class] = 0; wmi->fat_pipe_exist &= ~(1 << ev->traffic_class); spin_unlock_bh(&wmi->lock); /* Indicate inactivity to driver layer for this fatpipe (pstream) */ ath6kl_indicate_tx_activity(wmi->parent_dev, ev->traffic_class, false); return 0; } static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_bit_rate_reply *reply; s32 rate; u32 sgi, index; if (len < sizeof(struct wmi_bit_rate_reply)) return -EINVAL; reply = (struct wmi_bit_rate_reply *) datap; ath6kl_dbg(ATH6KL_DBG_WMI, "rateindex %d\n", reply->rate_index); if (reply->rate_index == (s8) RATE_AUTO) { rate = RATE_AUTO; } else { index = reply->rate_index & 0x7f; sgi = (reply->rate_index & 0x80) ? 1 : 0; rate = wmi_rate_tbl[index][sgi]; } ath6kl_wakeup_event(wmi->parent_dev); return 0; } static int ath6kl_wmi_test_rx(struct wmi *wmi, u8 *datap, int len) { ath6kl_tm_rx_event(wmi->parent_dev, datap, len); return 0; } static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len) { if (len < sizeof(struct wmi_fix_rates_reply)) return -EINVAL; ath6kl_wakeup_event(wmi->parent_dev); return 0; } static int ath6kl_wmi_ch_list_reply_rx(struct wmi *wmi, u8 *datap, int len) { if (len < sizeof(struct wmi_channel_list_reply)) return -EINVAL; ath6kl_wakeup_event(wmi->parent_dev); return 0; } static int ath6kl_wmi_tx_pwr_reply_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_tx_pwr_reply *reply; if (len < sizeof(struct wmi_tx_pwr_reply)) return -EINVAL; reply = (struct wmi_tx_pwr_reply *) datap; ath6kl_txpwr_rx_evt(wmi->parent_dev, reply->dbM); return 0; } static int ath6kl_wmi_keepalive_reply_rx(struct wmi *wmi, u8 *datap, int len) { if (len < sizeof(struct wmi_get_keepalive_cmd)) return -EINVAL; ath6kl_wakeup_event(wmi->parent_dev); return 0; } static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_scan_complete_event *ev; ev = (struct wmi_scan_complete_event *) datap; ath6kl_scan_complete_evt(vif, a_sle32_to_cpu(ev->status)); wmi->is_probe_ssid = false; return 0; } static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_neighbor_report_event *ev; u8 i; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_neighbor_report_event *) datap; if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info) > len) { ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event (num=%d len=%d)\n", ev->num_neighbors, len); return -EINVAL; } for (i = 0; i < ev->num_neighbors; i++) { ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n", i + 1, ev->num_neighbors, ev->neighbor[i].bssid, ev->neighbor[i].bss_flags); cfg80211_pmksa_candidate_notify(vif->ndev, i, ev->neighbor[i].bssid, !!(ev->neighbor[i].bss_flags & WMI_PREAUTH_CAPABLE_BSS), GFP_ATOMIC); } return 0; } /* * Target is reporting a programming error. This is for * developer aid only. Target only checks a few common violations * and it is responsibility of host to do all error checking. * Behavior of target after wmi error event is undefined. * A reset is recommended. */ static int ath6kl_wmi_error_event_rx(struct wmi *wmi, u8 *datap, int len) { const char *type = "unknown error"; struct wmi_cmd_error_event *ev; ev = (struct wmi_cmd_error_event *) datap; switch (ev->err_code) { case INVALID_PARAM: type = "invalid parameter"; break; case ILLEGAL_STATE: type = "invalid state"; break; case INTERNAL_ERROR: type = "internal error"; break; } ath6kl_dbg(ATH6KL_DBG_WMI, "programming error, cmd=%d %s\n", ev->cmd_id, type); return 0; } static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { ath6kl_tgt_stats_event(vif, datap, len); return 0; } static u8 ath6kl_wmi_get_upper_threshold(s16 rssi, struct sq_threshold_params *sq_thresh, u32 size) { u32 index; u8 threshold = (u8) sq_thresh->upper_threshold[size - 1]; /* The list is already in sorted order. Get the next lower value */ for (index = 0; index < size; index++) { if (rssi < sq_thresh->upper_threshold[index]) { threshold = (u8) sq_thresh->upper_threshold[index]; break; } } return threshold; } static u8 ath6kl_wmi_get_lower_threshold(s16 rssi, struct sq_threshold_params *sq_thresh, u32 size) { u32 index; u8 threshold = (u8) sq_thresh->lower_threshold[size - 1]; /* The list is already in sorted order. Get the next lower value */ for (index = 0; index < size; index++) { if (rssi > sq_thresh->lower_threshold[index]) { threshold = (u8) sq_thresh->lower_threshold[index]; break; } } return threshold; } static int ath6kl_wmi_send_rssi_threshold_params(struct wmi *wmi, struct wmi_rssi_threshold_params_cmd *rssi_cmd) { struct sk_buff *skb; struct wmi_rssi_threshold_params_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_rssi_threshold_params_cmd *) skb->data; memcpy(cmd, rssi_cmd, sizeof(struct wmi_rssi_threshold_params_cmd)); return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_RSSI_THRESHOLD_PARAMS_CMDID, NO_SYNC_WMIFLAG); } static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_rssi_threshold_event *reply; struct wmi_rssi_threshold_params_cmd cmd; struct sq_threshold_params *sq_thresh; enum wmi_rssi_threshold_val new_threshold; u8 upper_rssi_threshold, lower_rssi_threshold; s16 rssi; int ret; if (len < sizeof(struct wmi_rssi_threshold_event)) return -EINVAL; reply = (struct wmi_rssi_threshold_event *) datap; new_threshold = (enum wmi_rssi_threshold_val) reply->range; rssi = a_sle16_to_cpu(reply->rssi); sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_RSSI]; /* * Identify the threshold breached and communicate that to the app. * After that install a new set of thresholds based on the signal * quality reported by the target */ if (new_threshold) { /* Upper threshold breached */ if (rssi < sq_thresh->upper_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, "spurious upper rssi threshold event: %d\n", rssi); } else if ((rssi < sq_thresh->upper_threshold[1]) && (rssi >= sq_thresh->upper_threshold[0])) { new_threshold = WMI_RSSI_THRESHOLD1_ABOVE; } else if ((rssi < sq_thresh->upper_threshold[2]) && (rssi >= sq_thresh->upper_threshold[1])) { new_threshold = WMI_RSSI_THRESHOLD2_ABOVE; } else if ((rssi < sq_thresh->upper_threshold[3]) && (rssi >= sq_thresh->upper_threshold[2])) { new_threshold = WMI_RSSI_THRESHOLD3_ABOVE; } else if ((rssi < sq_thresh->upper_threshold[4]) && (rssi >= sq_thresh->upper_threshold[3])) { new_threshold = WMI_RSSI_THRESHOLD4_ABOVE; } else if ((rssi < sq_thresh->upper_threshold[5]) && (rssi >= sq_thresh->upper_threshold[4])) { new_threshold = WMI_RSSI_THRESHOLD5_ABOVE; } else if (rssi >= sq_thresh->upper_threshold[5]) { new_threshold = WMI_RSSI_THRESHOLD6_ABOVE; } } else { /* Lower threshold breached */ if (rssi > sq_thresh->lower_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, "spurious lower rssi threshold event: %d %d\n", rssi, sq_thresh->lower_threshold[0]); } else if ((rssi > sq_thresh->lower_threshold[1]) && (rssi <= sq_thresh->lower_threshold[0])) { new_threshold = WMI_RSSI_THRESHOLD6_BELOW; } else if ((rssi > sq_thresh->lower_threshold[2]) && (rssi <= sq_thresh->lower_threshold[1])) { new_threshold = WMI_RSSI_THRESHOLD5_BELOW; } else if ((rssi > sq_thresh->lower_threshold[3]) && (rssi <= sq_thresh->lower_threshold[2])) { new_threshold = WMI_RSSI_THRESHOLD4_BELOW; } else if ((rssi > sq_thresh->lower_threshold[4]) && (rssi <= sq_thresh->lower_threshold[3])) { new_threshold = WMI_RSSI_THRESHOLD3_BELOW; } else if ((rssi > sq_thresh->lower_threshold[5]) && (rssi <= sq_thresh->lower_threshold[4])) { new_threshold = WMI_RSSI_THRESHOLD2_BELOW; } else if (rssi <= sq_thresh->lower_threshold[5]) { new_threshold = WMI_RSSI_THRESHOLD1_BELOW; } } /* Calculate and install the next set of thresholds */ lower_rssi_threshold = ath6kl_wmi_get_lower_threshold(rssi, sq_thresh, sq_thresh->lower_threshold_valid_count); upper_rssi_threshold = ath6kl_wmi_get_upper_threshold(rssi, sq_thresh, sq_thresh->upper_threshold_valid_count); /* Issue a wmi command to install the thresholds */ cmd.thresh_above1_val = a_cpu_to_sle16(upper_rssi_threshold); cmd.thresh_below1_val = a_cpu_to_sle16(lower_rssi_threshold); cmd.weight = sq_thresh->weight; cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval); ret = ath6kl_wmi_send_rssi_threshold_params(wmi, &cmd); if (ret) { ath6kl_err("unable to configure rssi thresholds\n"); return -EIO; } return 0; } static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_cac_event *reply; struct ieee80211_tspec_ie *ts; u16 active_tsids, tsinfo; u8 tsid, index; u8 ts_id; if (len < sizeof(struct wmi_cac_event)) return -EINVAL; reply = (struct wmi_cac_event *) datap; if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) && (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) { ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion); tsinfo = le16_to_cpu(ts->tsinfo); tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) & IEEE80211_WMM_IE_TSPEC_TID_MASK; ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx, reply->ac, tsid); } else if (reply->cac_indication == CAC_INDICATION_NO_RESP) { /* * Following assumes that there is only one outstanding * ADDTS request when this event is received */ spin_lock_bh(&wmi->lock); active_tsids = wmi->stream_exist_for_ac[reply->ac]; spin_unlock_bh(&wmi->lock); for (index = 0; index < sizeof(active_tsids) * 8; index++) { if ((active_tsids >> index) & 1) break; } if (index < (sizeof(active_tsids) * 8)) ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx, reply->ac, index); } /* * Clear active tsids and Add missing handling * for delete qos stream from AP */ else if (reply->cac_indication == CAC_INDICATION_DELETE) { ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion); tsinfo = le16_to_cpu(ts->tsinfo); ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) & IEEE80211_WMM_IE_TSPEC_TID_MASK); spin_lock_bh(&wmi->lock); wmi->stream_exist_for_ac[reply->ac] &= ~(1 << ts_id); active_tsids = wmi->stream_exist_for_ac[reply->ac]; spin_unlock_bh(&wmi->lock); /* Indicate stream inactivity to driver layer only if all tsids * within this AC are deleted. */ if (!active_tsids) { ath6kl_indicate_tx_activity(wmi->parent_dev, reply->ac, false); wmi->fat_pipe_exist &= ~(1 << reply->ac); } } return 0; } static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi, struct wmi_snr_threshold_params_cmd *snr_cmd) { struct sk_buff *skb; struct wmi_snr_threshold_params_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_snr_threshold_params_cmd *) skb->data; memcpy(cmd, snr_cmd, sizeof(struct wmi_snr_threshold_params_cmd)); return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SNR_THRESHOLD_PARAMS_CMDID, NO_SYNC_WMIFLAG); } static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_snr_threshold_event *reply; struct sq_threshold_params *sq_thresh; struct wmi_snr_threshold_params_cmd cmd; enum wmi_snr_threshold_val new_threshold; u8 upper_snr_threshold, lower_snr_threshold; s16 snr; int ret; if (len < sizeof(struct wmi_snr_threshold_event)) return -EINVAL; reply = (struct wmi_snr_threshold_event *) datap; new_threshold = (enum wmi_snr_threshold_val) reply->range; snr = reply->snr; sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_SNR]; /* * Identify the threshold breached and communicate that to the app. * After that install a new set of thresholds based on the signal * quality reported by the target. */ if (new_threshold) { /* Upper threshold breached */ if (snr < sq_thresh->upper_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, "spurious upper snr threshold event: %d\n", snr); } else if ((snr < sq_thresh->upper_threshold[1]) && (snr >= sq_thresh->upper_threshold[0])) { new_threshold = WMI_SNR_THRESHOLD1_ABOVE; } else if ((snr < sq_thresh->upper_threshold[2]) && (snr >= sq_thresh->upper_threshold[1])) { new_threshold = WMI_SNR_THRESHOLD2_ABOVE; } else if ((snr < sq_thresh->upper_threshold[3]) && (snr >= sq_thresh->upper_threshold[2])) { new_threshold = WMI_SNR_THRESHOLD3_ABOVE; } else if (snr >= sq_thresh->upper_threshold[3]) { new_threshold = WMI_SNR_THRESHOLD4_ABOVE; } } else { /* Lower threshold breached */ if (snr > sq_thresh->lower_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, "spurious lower snr threshold event: %d\n", sq_thresh->lower_threshold[0]); } else if ((snr > sq_thresh->lower_threshold[1]) && (snr <= sq_thresh->lower_threshold[0])) { new_threshold = WMI_SNR_THRESHOLD4_BELOW; } else if ((snr > sq_thresh->lower_threshold[2]) && (snr <= sq_thresh->lower_threshold[1])) { new_threshold = WMI_SNR_THRESHOLD3_BELOW; } else if ((snr > sq_thresh->lower_threshold[3]) && (snr <= sq_thresh->lower_threshold[2])) { new_threshold = WMI_SNR_THRESHOLD2_BELOW; } else if (snr <= sq_thresh->lower_threshold[3]) { new_threshold = WMI_SNR_THRESHOLD1_BELOW; } } /* Calculate and install the next set of thresholds */ lower_snr_threshold = ath6kl_wmi_get_lower_threshold(snr, sq_thresh, sq_thresh->lower_threshold_valid_count); upper_snr_threshold = ath6kl_wmi_get_upper_threshold(snr, sq_thresh, sq_thresh->upper_threshold_valid_count); /* Issue a wmi command to install the thresholds */ cmd.thresh_above1_val = upper_snr_threshold; cmd.thresh_below1_val = lower_snr_threshold; cmd.weight = sq_thresh->weight; cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval); ath6kl_dbg(ATH6KL_DBG_WMI, "snr: %d, threshold: %d, lower: %d, upper: %d\n", snr, new_threshold, lower_snr_threshold, upper_snr_threshold); ret = ath6kl_wmi_send_snr_threshold_params(wmi, &cmd); if (ret) { ath6kl_err("unable to configure snr threshold\n"); return -EIO; } return 0; } static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len) { u16 ap_info_entry_size; struct wmi_aplist_event *ev = (struct wmi_aplist_event *) datap; struct wmi_ap_info_v1 *ap_info_v1; u8 index; if (len < sizeof(struct wmi_aplist_event) || ev->ap_list_ver != APLIST_VER1) return -EINVAL; ap_info_entry_size = sizeof(struct wmi_ap_info_v1); ap_info_v1 = (struct wmi_ap_info_v1 *) ev->ap_list; ath6kl_dbg(ATH6KL_DBG_WMI, "number of APs in aplist event: %d\n", ev->num_ap); if (len < (int) (sizeof(struct wmi_aplist_event) + (ev->num_ap - 1) * ap_info_entry_size)) return -EINVAL; /* AP list version 1 contents */ for (index = 0; index < ev->num_ap; index++) { ath6kl_dbg(ATH6KL_DBG_WMI, "AP#%d BSSID %pM Channel %d\n", index, ap_info_v1->bssid, ap_info_v1->channel); ap_info_v1++; } return 0; } int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag) { struct wmi_cmd_hdr *cmd_hdr; enum htc_endpoint_id ep_id = wmi->ep_id; int ret; u16 info1; if (WARN_ON(skb == NULL || (if_idx > (wmi->parent_dev->vif_max - 1)))) return -EINVAL; ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n", cmd_id, skb->len, sync_flag); ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi tx ", skb->data, skb->len); if (sync_flag >= END_WMIFLAG) { dev_kfree_skb(skb); return -EINVAL; } if ((sync_flag == SYNC_BEFORE_WMIFLAG) || (sync_flag == SYNC_BOTH_WMIFLAG)) { /* * Make sure all data currently queued is transmitted before * the cmd execution. Establish a new sync point. */ ath6kl_wmi_sync_point(wmi, if_idx); } skb_push(skb, sizeof(struct wmi_cmd_hdr)); cmd_hdr = (struct wmi_cmd_hdr *) skb->data; cmd_hdr->cmd_id = cpu_to_le16(cmd_id); info1 = if_idx & WMI_CMD_HDR_IF_ID_MASK; cmd_hdr->info1 = cpu_to_le16(info1); /* Only for OPT_TX_CMD, use BE endpoint. */ if (cmd_id == WMI_OPT_TX_FRAME_CMDID) { ret = ath6kl_wmi_data_hdr_add(wmi, skb, OPT_MSGTYPE, false, false, 0, NULL, if_idx); if (ret) { dev_kfree_skb(skb); return ret; } ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, WMM_AC_BE); } ath6kl_control_tx(wmi->parent_dev, skb, ep_id); if ((sync_flag == SYNC_AFTER_WMIFLAG) || (sync_flag == SYNC_BOTH_WMIFLAG)) { /* * Make sure all new data queued waits for the command to * execute. Establish a new sync point. */ ath6kl_wmi_sync_point(wmi, if_idx); } return 0; } int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, enum network_type nw_type, enum dot11_auth_mode dot11_auth_mode, enum auth_mode auth_mode, enum crypto_type pairwise_crypto, u8 pairwise_crypto_len, enum crypto_type group_crypto, u8 group_crypto_len, int ssid_len, u8 *ssid, u8 *bssid, u16 channel, u32 ctrl_flags, u8 nw_subtype) { struct sk_buff *skb; struct wmi_connect_cmd *cc; int ret; ath6kl_dbg(ATH6KL_DBG_WMI, "wmi connect bssid %pM freq %d flags 0x%x ssid_len %d " "type %d dot11_auth %d auth %d pairwise %d group %d\n", bssid, channel, ctrl_flags, ssid_len, nw_type, dot11_auth_mode, auth_mode, pairwise_crypto, group_crypto); ath6kl_dbg_dump(ATH6KL_DBG_WMI, NULL, "ssid ", ssid, ssid_len); wmi->traffic_class = 100; if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT)) return -EINVAL; if ((pairwise_crypto != NONE_CRYPT) && (group_crypto == NONE_CRYPT)) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_connect_cmd)); if (!skb) return -ENOMEM; cc = (struct wmi_connect_cmd *) skb->data; if (ssid_len) memcpy(cc->ssid, ssid, ssid_len); cc->ssid_len = ssid_len; cc->nw_type = nw_type; cc->dot11_auth_mode = dot11_auth_mode; cc->auth_mode = auth_mode; cc->prwise_crypto_type = pairwise_crypto; cc->prwise_crypto_len = pairwise_crypto_len; cc->grp_crypto_type = group_crypto; cc->grp_crypto_len = group_crypto_len; cc->ch = cpu_to_le16(channel); cc->ctrl_flags = cpu_to_le32(ctrl_flags); cc->nw_subtype = nw_subtype; if (bssid != NULL) memcpy(cc->bssid, bssid, ETH_ALEN); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_CONNECT_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid, u16 channel) { struct sk_buff *skb; struct wmi_reconnect_cmd *cc; int ret; ath6kl_dbg(ATH6KL_DBG_WMI, "wmi reconnect bssid %pM freq %d\n", bssid, channel); wmi->traffic_class = 100; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd)); if (!skb) return -ENOMEM; cc = (struct wmi_reconnect_cmd *) skb->data; cc->channel = cpu_to_le16(channel); if (bssid != NULL) memcpy(cc->bssid, bssid, ETH_ALEN); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_RECONNECT_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx) { int ret; ath6kl_dbg(ATH6KL_DBG_WMI, "wmi disconnect\n"); wmi->traffic_class = 100; /* Disconnect command does not need to do a SYNC before. */ ret = ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_DISCONNECT_CMDID); return ret; } int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates) { struct ieee80211_supported_band *sband; struct sk_buff *skb; struct wmi_begin_scan_cmd *sc; s8 size, *supp_rates; int i, band, ret; struct ath6kl *ar = wmi->parent_dev; int num_rates; u32 ratemask; size = sizeof(struct wmi_begin_scan_cmd); if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) return -EINVAL; if (num_chan > WMI_MAX_CHANNELS) return -EINVAL; if (num_chan) size += sizeof(u16) * (num_chan - 1); skb = ath6kl_wmi_get_new_buf(size); if (!skb) return -ENOMEM; sc = (struct wmi_begin_scan_cmd *) skb->data; sc->scan_type = scan_type; sc->force_fg_scan = cpu_to_le32(force_fgscan); sc->is_legacy = cpu_to_le32(is_legacy); sc->home_dwell_time = cpu_to_le32(home_dwell_time); sc->force_scan_intvl = cpu_to_le32(force_scan_interval); sc->no_cck = cpu_to_le32(no_cck); sc->num_ch = num_chan; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = ar->wiphy->bands[band]; if (!sband) continue; ratemask = rates[band]; supp_rates = sc->supp_rates[band].rates; num_rates = 0; for (i = 0; i < sband->n_bitrates; i++) { if ((BIT(i) & ratemask) == 0) continue; /* skip rate */ supp_rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5); } sc->supp_rates[band].nrates = num_rates; } for (i = 0; i < num_chan; i++) sc->ch_list[i] = cpu_to_le16(ch_list[i]); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_BEGIN_SCAN_CMDID, NO_SYNC_WMIFLAG); return ret; } /* ath6kl_wmi_start_scan_cmd is to be deprecated. Use * ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P * mgmt operations using station interface. */ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list) { struct sk_buff *skb; struct wmi_start_scan_cmd *sc; s8 size; int i, ret; size = sizeof(struct wmi_start_scan_cmd); if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) return -EINVAL; if (num_chan > WMI_MAX_CHANNELS) return -EINVAL; if (num_chan) size += sizeof(u16) * (num_chan - 1); skb = ath6kl_wmi_get_new_buf(size); if (!skb) return -ENOMEM; sc = (struct wmi_start_scan_cmd *) skb->data; sc->scan_type = scan_type; sc->force_fg_scan = cpu_to_le32(force_fgscan); sc->is_legacy = cpu_to_le32(is_legacy); sc->home_dwell_time = cpu_to_le32(home_dwell_time); sc->force_scan_intvl = cpu_to_le32(force_scan_interval); sc->num_ch = num_chan; for (i = 0; i < num_chan; i++) sc->ch_list[i] = cpu_to_le16(ch_list[i]); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_START_SCAN_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, u16 fg_end_sec, u16 bg_sec, u16 minact_chdw_msec, u16 maxact_chdw_msec, u16 pas_chdw_msec, u8 short_scan_ratio, u8 scan_ctrl_flag, u32 max_dfsch_act_time, u16 maxact_scan_per_ssid) { struct sk_buff *skb; struct wmi_scan_params_cmd *sc; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*sc)); if (!skb) return -ENOMEM; sc = (struct wmi_scan_params_cmd *) skb->data; sc->fg_start_period = cpu_to_le16(fg_start_sec); sc->fg_end_period = cpu_to_le16(fg_end_sec); sc->bg_period = cpu_to_le16(bg_sec); sc->minact_chdwell_time = cpu_to_le16(minact_chdw_msec); sc->maxact_chdwell_time = cpu_to_le16(maxact_chdw_msec); sc->pas_chdwell_time = cpu_to_le16(pas_chdw_msec); sc->short_scan_ratio = short_scan_ratio; sc->scan_ctrl_flags = scan_ctrl_flag; sc->max_dfsch_act_time = cpu_to_le32(max_dfsch_act_time); sc->maxact_scan_per_ssid = cpu_to_le16(maxact_scan_per_ssid); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_SCAN_PARAMS_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 if_idx, u8 filter, u32 ie_mask) { struct sk_buff *skb; struct wmi_bss_filter_cmd *cmd; int ret; if (filter >= LAST_BSS_FILTER) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_bss_filter_cmd *) skb->data; cmd->bss_filter = filter; cmd->ie_mask = cpu_to_le32(ie_mask); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_BSS_FILTER_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag, u8 ssid_len, u8 *ssid) { struct sk_buff *skb; struct wmi_probed_ssid_cmd *cmd; int ret; if (index >= MAX_PROBED_SSIDS) return -EINVAL; if (ssid_len > sizeof(cmd->ssid)) return -EINVAL; if ((flag & (DISABLE_SSID_FLAG | ANY_SSID_FLAG)) && (ssid_len > 0)) return -EINVAL; if ((flag & SPECIFIC_SSID_FLAG) && !ssid_len) return -EINVAL; if (flag & SPECIFIC_SSID_FLAG) wmi->is_probe_ssid = true; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_probed_ssid_cmd *) skb->data; cmd->entry_index = index; cmd->flag = flag; cmd->ssid_len = ssid_len; memcpy(cmd->ssid, ssid, ssid_len); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_PROBED_SSID_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx, u16 listen_interval, u16 listen_beacons) { struct sk_buff *skb; struct wmi_listen_int_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_listen_int_cmd *) skb->data; cmd->listen_intvl = cpu_to_le16(listen_interval); cmd->num_beacons = cpu_to_le16(listen_beacons); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_LISTEN_INT_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx, u16 bmiss_time, u16 num_beacons) { struct sk_buff *skb; struct wmi_bmiss_time_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_bmiss_time_cmd *) skb->data; cmd->bmiss_time = cpu_to_le16(bmiss_time); cmd->num_beacons = cpu_to_le16(num_beacons); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_BMISS_TIME_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode) { struct sk_buff *skb; struct wmi_power_mode_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_power_mode_cmd *) skb->data; cmd->pwr_mode = pwr_mode; wmi->pwr_mode = pwr_mode; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_POWER_MODE_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u8 if_idx, u16 idle_period, u16 ps_poll_num, u16 dtim_policy, u16 tx_wakeup_policy, u16 num_tx_to_wakeup, u16 ps_fail_event_policy) { struct sk_buff *skb; struct wmi_power_params_cmd *pm; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*pm)); if (!skb) return -ENOMEM; pm = (struct wmi_power_params_cmd *)skb->data; pm->idle_period = cpu_to_le16(idle_period); pm->pspoll_number = cpu_to_le16(ps_poll_num); pm->dtim_policy = cpu_to_le16(dtim_policy); pm->tx_wakeup_policy = cpu_to_le16(tx_wakeup_policy); pm->num_tx_to_wakeup = cpu_to_le16(num_tx_to_wakeup); pm->ps_fail_event_policy = cpu_to_le16(ps_fail_event_policy); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_POWER_PARAMS_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 if_idx, u8 timeout) { struct sk_buff *skb; struct wmi_disc_timeout_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_disc_timeout_cmd *) skb->data; cmd->discon_timeout = timeout; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_DISC_TIMEOUT_CMDID, NO_SYNC_WMIFLAG); if (ret == 0) ath6kl_debug_set_disconnect_timeout(wmi->parent_dev, timeout); return ret; } int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, enum crypto_type key_type, u8 key_usage, u8 key_len, u8 *key_rsc, unsigned int key_rsc_len, u8 *key_material, u8 key_op_ctrl, u8 *mac_addr, enum wmi_sync_flag sync_flag) { struct sk_buff *skb; struct wmi_add_cipher_key_cmd *cmd; int ret; ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d key_usage=%d key_len=%d key_op_ctrl=%d\n", key_index, key_type, key_usage, key_len, key_op_ctrl); if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) || (key_material == NULL) || key_rsc_len > 8) return -EINVAL; if ((WEP_CRYPT != key_type) && (NULL == key_rsc)) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_add_cipher_key_cmd *) skb->data; cmd->key_index = key_index; cmd->key_type = key_type; cmd->key_usage = key_usage; cmd->key_len = key_len; memcpy(cmd->key, key_material, key_len); if (key_rsc != NULL) memcpy(cmd->key_rsc, key_rsc, key_rsc_len); cmd->key_op_ctrl = key_op_ctrl; if (mac_addr) memcpy(cmd->key_mac_addr, mac_addr, ETH_ALEN); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_CIPHER_KEY_CMDID, sync_flag); return ret; } int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk) { struct sk_buff *skb; struct wmi_add_krk_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_add_krk_cmd *) skb->data; memcpy(cmd->krk, krk, WMI_KRK_LEN); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_KRK_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index) { struct sk_buff *skb; struct wmi_delete_cipher_key_cmd *cmd; int ret; if (key_index > WMI_MAX_KEY_INDEX) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_delete_cipher_key_cmd *) skb->data; cmd->key_index = key_index; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DELETE_CIPHER_KEY_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid, const u8 *pmkid, bool set) { struct sk_buff *skb; struct wmi_setpmkid_cmd *cmd; int ret; if (bssid == NULL) return -EINVAL; if (set && pmkid == NULL) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_setpmkid_cmd *) skb->data; memcpy(cmd->bssid, bssid, ETH_ALEN); if (set) { memcpy(cmd->pmkid, pmkid, sizeof(cmd->pmkid)); cmd->enable = PMKID_ENABLE; } else { memset(cmd->pmkid, 0, sizeof(cmd->pmkid)); cmd->enable = PMKID_DISABLE; } ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_PMKID_CMDID, NO_SYNC_WMIFLAG); return ret; } static int ath6kl_wmi_data_sync_send(struct wmi *wmi, struct sk_buff *skb, enum htc_endpoint_id ep_id, u8 if_idx) { struct wmi_data_hdr *data_hdr; int ret; if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) return -EINVAL; skb_push(skb, sizeof(struct wmi_data_hdr)); data_hdr = (struct wmi_data_hdr *) skb->data; data_hdr->info = SYNC_MSGTYPE << WMI_DATA_HDR_MSG_TYPE_SHIFT; data_hdr->info3 = cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK); ret = ath6kl_control_tx(wmi->parent_dev, skb, ep_id); return ret; } static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) { struct sk_buff *skb; struct wmi_sync_cmd *cmd; struct wmi_data_sync_bufs data_sync_bufs[WMM_NUM_AC]; enum htc_endpoint_id ep_id; u8 index, num_pri_streams = 0; int ret = 0; memset(data_sync_bufs, 0, sizeof(data_sync_bufs)); spin_lock_bh(&wmi->lock); for (index = 0; index < WMM_NUM_AC; index++) { if (wmi->fat_pipe_exist & (1 << index)) { num_pri_streams++; data_sync_bufs[num_pri_streams - 1].traffic_class = index; } } spin_unlock_bh(&wmi->lock); skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) { ret = -ENOMEM; goto free_skb; } cmd = (struct wmi_sync_cmd *) skb->data; /* * In the SYNC cmd sent on the control Ep, send a bitmap * of the data eps on which the Data Sync will be sent */ cmd->data_sync_map = wmi->fat_pipe_exist; for (index = 0; index < num_pri_streams; index++) { data_sync_bufs[index].skb = ath6kl_buf_alloc(0); if (data_sync_bufs[index].skb == NULL) { ret = -ENOMEM; break; } } /* * If buffer allocation for any of the dataSync fails, * then do not send the Synchronize cmd on the control ep */ if (ret) goto free_skb; /* * Send sync cmd followed by sync data messages on all * endpoints being used */ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SYNCHRONIZE_CMDID, NO_SYNC_WMIFLAG); if (ret) goto free_skb; /* cmd buffer sent, we no longer own it */ skb = NULL; for (index = 0; index < num_pri_streams; index++) { if (WARN_ON(!data_sync_bufs[index].skb)) break; ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, data_sync_bufs[index]. traffic_class); ret = ath6kl_wmi_data_sync_send(wmi, data_sync_bufs[index].skb, ep_id, if_idx); if (ret) break; data_sync_bufs[index].skb = NULL; } free_skb: /* free up any resources left over (possibly due to an error) */ if (skb) dev_kfree_skb(skb); for (index = 0; index < num_pri_streams; index++) { if (data_sync_bufs[index].skb != NULL) { dev_kfree_skb((struct sk_buff *)data_sync_bufs[index]. skb); } } return ret; } int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi, u8 if_idx, struct wmi_create_pstream_cmd *params) { struct sk_buff *skb; struct wmi_create_pstream_cmd *cmd; u8 fatpipe_exist_for_ac = 0; s32 min_phy = 0; s32 nominal_phy = 0; int ret; if (!((params->user_pri < 8) && (params->user_pri <= 0x7) && (up_to_ac[params->user_pri & 0x7] == params->traffic_class) && (params->traffic_direc == UPLINK_TRAFFIC || params->traffic_direc == DNLINK_TRAFFIC || params->traffic_direc == BIDIR_TRAFFIC) && (params->traffic_type == TRAFFIC_TYPE_APERIODIC || params->traffic_type == TRAFFIC_TYPE_PERIODIC) && (params->voice_psc_cap == DISABLE_FOR_THIS_AC || params->voice_psc_cap == ENABLE_FOR_THIS_AC || params->voice_psc_cap == ENABLE_FOR_ALL_AC) && (params->tsid == WMI_IMPLICIT_PSTREAM || params->tsid <= WMI_MAX_THINSTREAM))) { return -EINVAL; } /* * Check nominal PHY rate is >= minimalPHY, * so that DUT can allow TSRS IE */ /* Get the physical rate (units of bps) */ min_phy = ((le32_to_cpu(params->min_phy_rate) / 1000) / 1000); /* Check minimal phy < nominal phy rate */ if (params->nominal_phy >= min_phy) { /* unit of 500 kbps */ nominal_phy = (params->nominal_phy * 1000) / 500; ath6kl_dbg(ATH6KL_DBG_WMI, "TSRS IE enabled::MinPhy %x->NominalPhy ===> %x\n", min_phy, nominal_phy); params->nominal_phy = nominal_phy; } else { params->nominal_phy = 0; } skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "sending create_pstream_cmd: ac=%d tsid:%d\n", params->traffic_class, params->tsid); cmd = (struct wmi_create_pstream_cmd *) skb->data; memcpy(cmd, params, sizeof(*cmd)); /* This is an implicitly created Fat pipe */ if ((u32) params->tsid == (u32) WMI_IMPLICIT_PSTREAM) { spin_lock_bh(&wmi->lock); fatpipe_exist_for_ac = (wmi->fat_pipe_exist & (1 << params->traffic_class)); wmi->fat_pipe_exist |= (1 << params->traffic_class); spin_unlock_bh(&wmi->lock); } else { /* explicitly created thin stream within a fat pipe */ spin_lock_bh(&wmi->lock); fatpipe_exist_for_ac = (wmi->fat_pipe_exist & (1 << params->traffic_class)); wmi->stream_exist_for_ac[params->traffic_class] |= (1 << params->tsid); /* * If a thinstream becomes active, the fat pipe automatically * becomes active */ wmi->fat_pipe_exist |= (1 << params->traffic_class); spin_unlock_bh(&wmi->lock); } /* * Indicate activty change to driver layer only if this is the * first TSID to get created in this AC explicitly or an implicit * fat pipe is getting created. */ if (!fatpipe_exist_for_ac) ath6kl_indicate_tx_activity(wmi->parent_dev, params->traffic_class, true); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_CREATE_PSTREAM_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class, u8 tsid) { struct sk_buff *skb; struct wmi_delete_pstream_cmd *cmd; u16 active_tsids = 0; int ret; if (traffic_class > 3) { ath6kl_err("invalid traffic class: %d\n", traffic_class); return -EINVAL; } skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_delete_pstream_cmd *) skb->data; cmd->traffic_class = traffic_class; cmd->tsid = tsid; spin_lock_bh(&wmi->lock); active_tsids = wmi->stream_exist_for_ac[traffic_class]; spin_unlock_bh(&wmi->lock); if (!(active_tsids & (1 << tsid))) { dev_kfree_skb(skb); ath6kl_dbg(ATH6KL_DBG_WMI, "TSID %d doesn't exist for traffic class: %d\n", tsid, traffic_class); return -ENODATA; } ath6kl_dbg(ATH6KL_DBG_WMI, "sending delete_pstream_cmd: traffic class: %d tsid=%d\n", traffic_class, tsid); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DELETE_PSTREAM_CMDID, SYNC_BEFORE_WMIFLAG); spin_lock_bh(&wmi->lock); wmi->stream_exist_for_ac[traffic_class] &= ~(1 << tsid); active_tsids = wmi->stream_exist_for_ac[traffic_class]; spin_unlock_bh(&wmi->lock); /* * Indicate stream inactivity to driver layer only if all tsids * within this AC are deleted. */ if (!active_tsids) { ath6kl_indicate_tx_activity(wmi->parent_dev, traffic_class, false); wmi->fat_pipe_exist &= ~(1 << traffic_class); } return ret; } int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx, __be32 ips0, __be32 ips1) { struct sk_buff *skb; struct wmi_set_ip_cmd *cmd; int ret; /* Multicast address are not valid */ if (ipv4_is_multicast(ips0) || ipv4_is_multicast(ips1)) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_ip_cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_ip_cmd *) skb->data; cmd->ips[0] = ips0; cmd->ips[1] = ips1; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IP_CMDID, NO_SYNC_WMIFLAG); return ret; } static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi) { u16 active_tsids; u8 stream_exist; int i; /* * Relinquish credits from all implicitly created pstreams * since when we go to sleep. If user created explicit * thinstreams exists with in a fatpipe leave them intact * for the user to delete. */ spin_lock_bh(&wmi->lock); stream_exist = wmi->fat_pipe_exist; spin_unlock_bh(&wmi->lock); for (i = 0; i < WMM_NUM_AC; i++) { if (stream_exist & (1 << i)) { /* * FIXME: Is this lock & unlock inside * for loop correct? may need rework. */ spin_lock_bh(&wmi->lock); active_tsids = wmi->stream_exist_for_ac[i]; spin_unlock_bh(&wmi->lock); /* * If there are no user created thin streams * delete the fatpipe */ if (!active_tsids) { stream_exist &= ~(1 << i); /* * Indicate inactivity to driver layer for * this fatpipe (pstream) */ ath6kl_indicate_tx_activity(wmi->parent_dev, i, false); } } } /* FIXME: Can we do this assignment without locking ? */ spin_lock_bh(&wmi->lock); wmi->fat_pipe_exist = stream_exist; spin_unlock_bh(&wmi->lock); } static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, const struct cfg80211_bitrate_mask *mask) { struct sk_buff *skb; int ret, mode, band; u64 mcsrate, ratemask[IEEE80211_NUM_BANDS]; struct wmi_set_tx_select_rates64_cmd *cmd; memset(&ratemask, 0, sizeof(ratemask)); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { /* copy legacy rate mask */ ratemask[band] = mask->control[band].legacy; if (band == IEEE80211_BAND_5GHZ) ratemask[band] = mask->control[band].legacy << 4; /* copy mcs rate mask */ mcsrate = mask->control[band].mcs[1]; mcsrate <<= 8; mcsrate |= mask->control[band].mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 28; } ath6kl_dbg(ATH6KL_DBG_WMI, "Ratemask 64 bit: 2.4:%llx 5:%llx\n", ratemask[0], ratemask[1]); skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX); if (!skb) return -ENOMEM; cmd = (struct wmi_set_tx_select_rates64_cmd *) skb->data; for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) { /* A mode operate in 5GHZ band */ if (mode == WMI_RATES_MODE_11A || mode == WMI_RATES_MODE_11A_HT20 || mode == WMI_RATES_MODE_11A_HT40) band = IEEE80211_BAND_5GHZ; else band = IEEE80211_BAND_2GHZ; cmd->ratemask[mode] = cpu_to_le64(ratemask[band]); } ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_TX_SELECT_RATES_CMDID, NO_SYNC_WMIFLAG); return ret; } static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, const struct cfg80211_bitrate_mask *mask) { struct sk_buff *skb; int ret, mode, band; u32 mcsrate, ratemask[IEEE80211_NUM_BANDS]; struct wmi_set_tx_select_rates32_cmd *cmd; memset(&ratemask, 0, sizeof(ratemask)); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { /* copy legacy rate mask */ ratemask[band] = mask->control[band].legacy; if (band == IEEE80211_BAND_5GHZ) ratemask[band] = mask->control[band].legacy << 4; /* copy mcs rate mask */ mcsrate = mask->control[band].mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 20; } ath6kl_dbg(ATH6KL_DBG_WMI, "Ratemask 32 bit: 2.4:%x 5:%x\n", ratemask[0], ratemask[1]); skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX); if (!skb) return -ENOMEM; cmd = (struct wmi_set_tx_select_rates32_cmd *) skb->data; for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) { /* A mode operate in 5GHZ band */ if (mode == WMI_RATES_MODE_11A || mode == WMI_RATES_MODE_11A_HT20 || mode == WMI_RATES_MODE_11A_HT40) band = IEEE80211_BAND_5GHZ; else band = IEEE80211_BAND_2GHZ; cmd->ratemask[mode] = cpu_to_le32(ratemask[band]); } ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_TX_SELECT_RATES_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, const struct cfg80211_bitrate_mask *mask) { struct ath6kl *ar = wmi->parent_dev; if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) return ath6kl_set_bitrate_mask64(wmi, if_idx, mask); else return ath6kl_set_bitrate_mask32(wmi, if_idx, mask); } int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_host_mode host_mode) { struct sk_buff *skb; struct wmi_set_host_sleep_mode_cmd *cmd; int ret; if ((host_mode != ATH6KL_HOST_MODE_ASLEEP) && (host_mode != ATH6KL_HOST_MODE_AWAKE)) { ath6kl_err("invalid host sleep mode: %d\n", host_mode); return -EINVAL; } skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_host_sleep_mode_cmd *) skb->data; if (host_mode == ATH6KL_HOST_MODE_ASLEEP) { ath6kl_wmi_relinquish_implicit_pstream_credits(wmi); cmd->asleep = cpu_to_le32(1); } else cmd->awake = cpu_to_le32(1); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HOST_SLEEP_MODE_CMDID, NO_SYNC_WMIFLAG); return ret; } /* This command has zero length payload */ static int ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(struct wmi *wmi, struct ath6kl_vif *vif) { struct ath6kl *ar = wmi->parent_dev; set_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); wake_up(&ar->event_wq); return 0; } int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_wow_mode wow_mode, u32 filter, u16 host_req_delay) { struct sk_buff *skb; struct wmi_set_wow_mode_cmd *cmd; int ret; if ((wow_mode != ATH6KL_WOW_MODE_ENABLE) && wow_mode != ATH6KL_WOW_MODE_DISABLE) { ath6kl_err("invalid wow mode: %d\n", wow_mode); return -EINVAL; } skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_wow_mode_cmd *) skb->data; cmd->enable_wow = cpu_to_le32(wow_mode); cmd->filter = cpu_to_le32(filter); cmd->host_req_delay = cpu_to_le16(host_req_delay); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_WOW_MODE_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u8 list_id, u8 filter_size, u8 filter_offset, const u8 *filter, const u8 *mask) { struct sk_buff *skb; struct wmi_add_wow_pattern_cmd *cmd; u16 size; u8 *filter_mask; int ret; /* * Allocate additional memory in the buffer to hold * filter and mask value, which is twice of filter_size. */ size = sizeof(*cmd) + (2 * filter_size); skb = ath6kl_wmi_get_new_buf(size); if (!skb) return -ENOMEM; cmd = (struct wmi_add_wow_pattern_cmd *) skb->data; cmd->filter_list_id = list_id; cmd->filter_size = filter_size; cmd->filter_offset = filter_offset; memcpy(cmd->filter, filter, filter_size); filter_mask = (u8 *) (cmd->filter + filter_size); memcpy(filter_mask, mask, filter_size); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_WOW_PATTERN_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u16 list_id, u16 filter_id) { struct sk_buff *skb; struct wmi_del_wow_pattern_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_del_wow_pattern_cmd *) skb->data; cmd->filter_list_id = cpu_to_le16(list_id); cmd->filter_id = cpu_to_le16(filter_id); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DEL_WOW_PATTERN_CMDID, NO_SYNC_WMIFLAG); return ret; } static int ath6kl_wmi_cmd_send_xtnd(struct wmi *wmi, struct sk_buff *skb, enum wmix_command_id cmd_id, enum wmi_sync_flag sync_flag) { struct wmix_cmd_hdr *cmd_hdr; int ret; skb_push(skb, sizeof(struct wmix_cmd_hdr)); cmd_hdr = (struct wmix_cmd_hdr *) skb->data; cmd_hdr->cmd_id = cpu_to_le32(cmd_id); ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_EXTENSION_CMDID, sync_flag); return ret; } int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source) { struct sk_buff *skb; struct wmix_hb_challenge_resp_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmix_hb_challenge_resp_cmd *) skb->data; cmd->cookie = cpu_to_le32(cookie); cmd->source = cpu_to_le32(source); ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_HB_CHALLENGE_RESP_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config) { struct ath6kl_wmix_dbglog_cfg_module_cmd *cmd; struct sk_buff *skb; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct ath6kl_wmix_dbglog_cfg_module_cmd *) skb->data; cmd->valid = cpu_to_le32(valid); cmd->config = cpu_to_le32(config); ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_DBGLOG_CFG_MODULE_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_get_stats_cmd(struct wmi *wmi, u8 if_idx) { return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_STATISTICS_CMDID); } int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 if_idx, u8 dbM) { struct sk_buff *skb; struct wmi_set_tx_pwr_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_tx_pwr_cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_tx_pwr_cmd *) skb->data; cmd->dbM = dbM; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_TX_PWR_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx) { return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_TX_PWR_CMDID); } int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi) { return ath6kl_wmi_simple_cmd(wmi, 0, WMI_GET_ROAM_TBL_CMDID); } int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 if_idx, u8 status, u8 preamble_policy) { struct sk_buff *skb; struct wmi_set_lpreamble_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_lpreamble_cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_lpreamble_cmd *) skb->data; cmd->status = status; cmd->preamble_policy = preamble_policy; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_LPREAMBLE_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u16 threshold) { struct sk_buff *skb; struct wmi_set_rts_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_rts_cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_rts_cmd *) skb->data; cmd->threshold = cpu_to_le16(threshold); ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_RTS_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg) { struct sk_buff *skb; struct wmi_set_wmm_txop_cmd *cmd; int ret; if (!((cfg == WMI_TXOP_DISABLED) || (cfg == WMI_TXOP_ENABLED))) return -EINVAL; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_wmm_txop_cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_wmm_txop_cmd *) skb->data; cmd->txop_enable = cfg; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_WMM_TXOP_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, u8 keep_alive_intvl) { struct sk_buff *skb; struct wmi_set_keepalive_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_keepalive_cmd *) skb->data; cmd->keep_alive_intvl = keep_alive_intvl; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_KEEPALIVE_CMDID, NO_SYNC_WMIFLAG); if (ret == 0) ath6kl_debug_set_keepalive(wmi->parent_dev, keep_alive_intvl); return ret; } int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, enum ieee80211_band band, struct ath6kl_htcap *htcap) { struct sk_buff *skb; struct wmi_set_htcap_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_htcap_cmd *) skb->data; /* * NOTE: Band in firmware matches enum ieee80211_band, it is unlikely * this will be changed in firmware. If at all there is any change in * band value, the host needs to be fixed. */ cmd->band = band; cmd->ht_enable = !!htcap->ht_enable; cmd->ht20_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_20); cmd->ht40_supported = !!(htcap->cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40); cmd->ht40_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_40); cmd->intolerant_40mhz = !!(htcap->cap_info & IEEE80211_HT_CAP_40MHZ_INTOLERANT); cmd->max_ampdu_len_exp = htcap->ampdu_factor; ath6kl_dbg(ATH6KL_DBG_WMI, "Set htcap: band:%d ht_enable:%d 40mhz:%d sgi_20mhz:%d sgi_40mhz:%d 40mhz_intolerant:%d ampdu_len_exp:%d\n", cmd->band, cmd->ht_enable, cmd->ht40_supported, cmd->ht20_sgi, cmd->ht40_sgi, cmd->intolerant_40mhz, cmd->max_ampdu_len_exp); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HT_CAP_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) { struct sk_buff *skb; int ret; skb = ath6kl_wmi_get_new_buf(len); if (!skb) return -ENOMEM; memcpy(skb->data, buf, len); ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on) { struct sk_buff *skb; struct wmi_mcast_filter_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_mcast_filter_cmd *) skb->data; cmd->mcast_all_enable = mc_all_on; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_MCAST_FILTER_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, u8 *filter, bool add_filter) { struct sk_buff *skb; struct wmi_mcast_filter_add_del_cmd *cmd; int ret; if ((filter[0] != 0x33 || filter[1] != 0x33) && (filter[0] != 0x01 || filter[1] != 0x00 || filter[2] != 0x5e || filter[3] > 0x7f)) { ath6kl_warn("invalid multicast filter address\n"); return -EINVAL; } skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_mcast_filter_add_del_cmd *) skb->data; memcpy(cmd->mcast_mac, filter, ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, add_filter ? WMI_SET_MCAST_FILTER_CMDID : WMI_DEL_MCAST_FILTER_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enhance) { struct sk_buff *skb; struct wmi_sta_bmiss_enhance_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_sta_bmiss_enhance_cmd *) skb->data; cmd->enable = enhance ? 1 : 0; ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_STA_BMISS_ENHANCE_CMDID, NO_SYNC_WMIFLAG); return ret; } s32 ath6kl_wmi_get_rate(s8 rate_index) { if (rate_index == RATE_AUTO) return 0; return wmi_rate_tbl[(u32) rate_index][0]; } static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap, u32 len) { struct wmi_pmkid_list_reply *reply; u32 expected_len; if (len < sizeof(struct wmi_pmkid_list_reply)) return -EINVAL; reply = (struct wmi_pmkid_list_reply *)datap; expected_len = sizeof(reply->num_pmkid) + le32_to_cpu(reply->num_pmkid) * WMI_PMKID_LEN; if (len < expected_len) return -EINVAL; return 0; } static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_addba_req_event *cmd = (struct wmi_addba_req_event *) datap; aggr_recv_addba_req_evt(vif, cmd->tid, le16_to_cpu(cmd->st_seq_no), cmd->win_sz); return 0; } static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_delba_event *cmd = (struct wmi_delba_event *) datap; aggr_recv_delba_req_evt(vif, cmd->tid); return 0; } /* AP mode functions */ int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx, struct wmi_connect_cmd *p) { struct sk_buff *skb; struct wmi_connect_cmd *cm; int res; skb = ath6kl_wmi_get_new_buf(sizeof(*cm)); if (!skb) return -ENOMEM; cm = (struct wmi_connect_cmd *) skb->data; memcpy(cm, p, sizeof(*cm)); res = ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_CONFIG_COMMIT_CMDID, NO_SYNC_WMIFLAG); ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u ctrl_flags=0x%x-> res=%d\n", __func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch), le32_to_cpu(p->ctrl_flags), res); return res; } int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac, u16 reason) { struct sk_buff *skb; struct wmi_ap_set_mlme_cmd *cm; skb = ath6kl_wmi_get_new_buf(sizeof(*cm)); if (!skb) return -ENOMEM; cm = (struct wmi_ap_set_mlme_cmd *) skb->data; memcpy(cm->mac, mac, ETH_ALEN); cm->reason = cpu_to_le16(reason); cm->cmd = cmd; ath6kl_dbg(ATH6KL_DBG_WMI, "ap_set_mlme: cmd=%d reason=%d\n", cm->cmd, cm->reason); return ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_SET_MLME_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable) { struct sk_buff *skb; struct wmi_ap_hidden_ssid_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_ap_hidden_ssid_cmd *) skb->data; cmd->hidden_ssid = enable ? 1 : 0; return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_HIDDEN_SSID_CMDID, NO_SYNC_WMIFLAG); } /* This command will be used to enable/disable AP uAPSD feature */ int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable) { struct wmi_ap_set_apsd_cmd *cmd; struct sk_buff *skb; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_ap_set_apsd_cmd *)skb->data; cmd->enable = enable; return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_APSD_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_set_apsd_bfrd_traf(struct wmi *wmi, u8 if_idx, u16 aid, u16 bitmap, u32 flags) { struct wmi_ap_apsd_buffered_traffic_cmd *cmd; struct sk_buff *skb; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_ap_apsd_buffered_traffic_cmd *)skb->data; cmd->aid = cpu_to_le16(aid); cmd->bitmap = cpu_to_le16(bitmap); cmd->flags = cpu_to_le32(flags); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID, NO_SYNC_WMIFLAG); } static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { struct wmi_pspoll_event *ev; if (len < sizeof(struct wmi_pspoll_event)) return -EINVAL; ev = (struct wmi_pspoll_event *) datap; ath6kl_pspoll_event(vif, le16_to_cpu(ev->aid)); return 0; } static int ath6kl_wmi_dtimexpiry_event_rx(struct wmi *wmi, u8 *datap, int len, struct ath6kl_vif *vif) { ath6kl_dtimexpiry_event(vif); return 0; } int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u8 if_idx, u16 aid, bool flag) { struct sk_buff *skb; struct wmi_ap_set_pvb_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_ap_set_pvb_cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_ap_set_pvb_cmd *) skb->data; cmd->aid = cpu_to_le16(aid); cmd->rsvd = cpu_to_le16(0); cmd->flag = cpu_to_le32(flag); ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_PVB_CMDID, NO_SYNC_WMIFLAG); return 0; } int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, u8 rx_meta_ver, bool rx_dot11_hdr, bool defrag_on_host) { struct sk_buff *skb; struct wmi_rx_frame_format_cmd *cmd; int ret; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_rx_frame_format_cmd *) skb->data; cmd->dot11_hdr = rx_dot11_hdr ? 1 : 0; cmd->defrag_on_host = defrag_on_host ? 1 : 0; cmd->meta_ver = rx_meta_ver; /* Delete the local aggr state, on host */ ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_RX_FRAME_FORMAT_CMDID, NO_SYNC_WMIFLAG); return ret; } int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len) { struct sk_buff *skb; struct wmi_set_appie_cmd *p; skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u ie_len=%u\n", mgmt_frm_type, ie_len); p = (struct wmi_set_appie_cmd *) skb->data; p->mgmt_frm_type = mgmt_frm_type; p->ie_len = ie_len; if (ie != NULL && ie_len > 0) memcpy(p->ie_info, ie, ie_len); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_APPIE_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, const u8 *ie_info, u8 ie_len) { struct sk_buff *skb; struct wmi_set_ie_cmd *p; skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "set_ie_cmd: ie_id=%u ie_ie_field=%u ie_len=%u\n", ie_id, ie_field, ie_len); p = (struct wmi_set_ie_cmd *) skb->data; p->ie_id = ie_id; p->ie_field = ie_field; p->ie_len = ie_len; if (ie_info && ie_len > 0) memcpy(p->ie_info, ie_info, ie_len); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IE_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable) { struct sk_buff *skb; struct wmi_disable_11b_rates_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "disable_11b_rates_cmd: disable=%u\n", disable); cmd = (struct wmi_disable_11b_rates_cmd *) skb->data; cmd->disable = disable ? 1 : 0; return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_DISABLE_11B_RATES_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur) { struct sk_buff *skb; struct wmi_remain_on_chnl_cmd *p; skb = ath6kl_wmi_get_new_buf(sizeof(*p)); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl_cmd: freq=%u dur=%u\n", freq, dur); p = (struct wmi_remain_on_chnl_cmd *) skb->data; p->freq = cpu_to_le32(freq); p->duration = cpu_to_le32(dur); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_REMAIN_ON_CHNL_CMDID, NO_SYNC_WMIFLAG); } /* ath6kl_wmi_send_action_cmd is to be deprecated. Use * ath6kl_wmi_send_mgmt_cmd instead. The new function supports P2P * mgmt operations using station interface. */ static int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, u32 wait, const u8 *data, u16 data_len) { struct sk_buff *skb; struct wmi_send_action_cmd *p; u8 *buf; if (wait) return -EINVAL; /* Offload for wait not supported */ buf = kmalloc(data_len, GFP_KERNEL); if (!buf) return -ENOMEM; skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); if (!skb) { kfree(buf); return -ENOMEM; } kfree(wmi->last_mgmt_tx_frame); memcpy(buf, data, data_len); wmi->last_mgmt_tx_frame = buf; wmi->last_mgmt_tx_frame_len = data_len; ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u len=%u\n", id, freq, wait, data_len); p = (struct wmi_send_action_cmd *) skb->data; p->id = cpu_to_le32(id); p->freq = cpu_to_le32(freq); p->wait = cpu_to_le32(wait); p->len = cpu_to_le16(data_len); memcpy(p->data, data, data_len); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_ACTION_CMDID, NO_SYNC_WMIFLAG); } static int __ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, u32 wait, const u8 *data, u16 data_len, u32 no_cck) { struct sk_buff *skb; struct wmi_send_mgmt_cmd *p; u8 *buf; if (wait) return -EINVAL; /* Offload for wait not supported */ buf = kmalloc(data_len, GFP_KERNEL); if (!buf) return -ENOMEM; skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); if (!skb) { kfree(buf); return -ENOMEM; } kfree(wmi->last_mgmt_tx_frame); memcpy(buf, data, data_len); wmi->last_mgmt_tx_frame = buf; wmi->last_mgmt_tx_frame_len = data_len; ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u len=%u\n", id, freq, wait, data_len); p = (struct wmi_send_mgmt_cmd *) skb->data; p->id = cpu_to_le32(id); p->freq = cpu_to_le32(freq); p->wait = cpu_to_le32(wait); p->no_cck = cpu_to_le32(no_cck); p->len = cpu_to_le16(data_len); memcpy(p->data, data, data_len); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_MGMT_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, u32 wait, const u8 *data, u16 data_len, u32 no_cck) { int status; struct ath6kl *ar = wmi->parent_dev; if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, ar->fw_capabilities)) { /* * If capable of doing P2P mgmt operations using * station interface, send additional information like * supported rates to advertise and xmit rates for * probe requests */ status = __ath6kl_wmi_send_mgmt_cmd(ar->wmi, if_idx, id, freq, wait, data, data_len, no_cck); } else { status = ath6kl_wmi_send_action_cmd(ar->wmi, if_idx, id, freq, wait, data, data_len); } return status; } int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq, const u8 *dst, const u8 *data, u16 data_len) { struct sk_buff *skb; struct wmi_p2p_probe_response_cmd *p; size_t cmd_len = sizeof(*p) + data_len; if (data_len == 0) cmd_len++; /* work around target minimum length requirement */ skb = ath6kl_wmi_get_new_buf(cmd_len); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM len=%u\n", freq, dst, data_len); p = (struct wmi_p2p_probe_response_cmd *) skb->data; p->freq = cpu_to_le32(freq); memcpy(p->destination_addr, dst, ETH_ALEN); p->len = cpu_to_le16(data_len); memcpy(p->data, data, data_len); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_PROBE_RESPONSE_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, u8 if_idx, bool enable) { struct sk_buff *skb; struct wmi_probe_req_report_cmd *p; skb = ath6kl_wmi_get_new_buf(sizeof(*p)); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "probe_report_req_cmd: enable=%u\n", enable); p = (struct wmi_probe_req_report_cmd *) skb->data; p->enable = enable ? 1 : 0; return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_PROBE_REQ_REPORT_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u8 if_idx, u32 info_req_flags) { struct sk_buff *skb; struct wmi_get_p2p_info *p; skb = ath6kl_wmi_get_new_buf(sizeof(*p)); if (!skb) return -ENOMEM; ath6kl_dbg(ATH6KL_DBG_WMI, "info_req_cmd: flags=%x\n", info_req_flags); p = (struct wmi_get_p2p_info *) skb->data; p->info_req_flags = cpu_to_le32(info_req_flags); return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_GET_P2P_INFO_CMDID, NO_SYNC_WMIFLAG); } int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx) { ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl_cmd\n"); return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_CANCEL_REMAIN_ON_CHNL_CMDID); } int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout) { struct sk_buff *skb; struct wmi_set_inact_period_cmd *cmd; skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_set_inact_period_cmd *) skb->data; cmd->inact_period = cpu_to_le32(inact_timeout); cmd->num_null_func = 0; return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_CONN_INACT_CMDID, NO_SYNC_WMIFLAG); } static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) { struct wmix_cmd_hdr *cmd; u32 len; u16 id; u8 *datap; int ret = 0; if (skb->len < sizeof(struct wmix_cmd_hdr)) { ath6kl_err("bad packet 1\n"); return -EINVAL; } cmd = (struct wmix_cmd_hdr *) skb->data; id = le32_to_cpu(cmd->cmd_id); skb_pull(skb, sizeof(struct wmix_cmd_hdr)); datap = skb->data; len = skb->len; switch (id) { case WMIX_HB_CHALLENGE_RESP_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n"); break; case WMIX_DBGLOG_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len); ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len); break; default: ath6kl_warn("unknown cmd id 0x%x\n", id); ret = -EINVAL; break; } return ret; } static int ath6kl_wmi_roam_tbl_event_rx(struct wmi *wmi, u8 *datap, int len) { return ath6kl_debug_roam_tbl_event(wmi->parent_dev, datap, len); } /* Process interface specific wmi events, caller would free the datap */ static int ath6kl_wmi_proc_events_vif(struct wmi *wmi, u16 if_idx, u16 cmd_id, u8 *datap, u32 len) { struct ath6kl_vif *vif; vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); if (!vif) { ath6kl_dbg(ATH6KL_DBG_WMI, "Wmi event for unavailable vif, vif_index:%d\n", if_idx); return -EINVAL; } switch (cmd_id) { case WMI_CONNECT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n"); return ath6kl_wmi_connect_event_rx(wmi, datap, len, vif); case WMI_DISCONNECT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n"); return ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif); case WMI_TKIP_MICERR_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n"); return ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif); case WMI_BSSINFO_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); return ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif); case WMI_NEIGHBOR_REPORT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); return ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len, vif); case WMI_SCAN_COMPLETE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); return ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif); case WMI_REPORT_STATISTICS_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n"); return ath6kl_wmi_stats_event_rx(wmi, datap, len, vif); case WMI_CAC_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n"); return ath6kl_wmi_cac_event_rx(wmi, datap, len, vif); case WMI_PSPOLL_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n"); return ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif); case WMI_DTIMEXPIRY_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n"); return ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif); case WMI_ADDBA_REQ_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n"); return ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif); case WMI_DELBA_REQ_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n"); return ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif); case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID"); return ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif); case WMI_REMAIN_ON_CHNL_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); return ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif); case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); return ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap, len, vif); case WMI_TX_STATUS_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); return ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif); case WMI_RX_PROBE_REQ_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); return ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif); case WMI_RX_ACTION_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); return ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); default: ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", cmd_id); return -EINVAL; } return 0; } static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd; int ret = 0; u32 len; u16 id; u8 if_idx; u8 *datap; cmd = (struct wmi_cmd_hdr *) skb->data; id = le16_to_cpu(cmd->cmd_id); if_idx = le16_to_cpu(cmd->info1) & WMI_CMD_HDR_IF_ID_MASK; skb_pull(skb, sizeof(struct wmi_cmd_hdr)); datap = skb->data; len = skb->len; ath6kl_dbg(ATH6KL_DBG_WMI, "wmi rx id %d len %d\n", id, len); ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ", datap, len); switch (id) { case WMI_GET_BITRATE_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_BITRATE_CMDID\n"); ret = ath6kl_wmi_bitrate_reply_rx(wmi, datap, len); break; case WMI_GET_CHANNEL_LIST_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_CHANNEL_LIST_CMDID\n"); ret = ath6kl_wmi_ch_list_reply_rx(wmi, datap, len); break; case WMI_GET_TX_PWR_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_TX_PWR_CMDID\n"); ret = ath6kl_wmi_tx_pwr_reply_rx(wmi, datap, len); break; case WMI_READY_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_READY_EVENTID\n"); ret = ath6kl_wmi_ready_event_rx(wmi, datap, len); break; case WMI_PEER_NODE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PEER_NODE_EVENTID\n"); ret = ath6kl_wmi_peer_node_event_rx(wmi, datap, len); break; case WMI_REGDOMAIN_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n"); ath6kl_wmi_regdomain_event(wmi, datap, len); break; case WMI_PSTREAM_TIMEOUT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n"); ret = ath6kl_wmi_pstream_timeout_event_rx(wmi, datap, len); break; case WMI_CMDERROR_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CMDERROR_EVENTID\n"); ret = ath6kl_wmi_error_event_rx(wmi, datap, len); break; case WMI_RSSI_THRESHOLD_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RSSI_THRESHOLD_EVENTID\n"); ret = ath6kl_wmi_rssi_threshold_event_rx(wmi, datap, len); break; case WMI_ERROR_REPORT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ERROR_REPORT_EVENTID\n"); break; case WMI_OPT_RX_FRAME_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n"); /* this event has been deprecated */ break; case WMI_REPORT_ROAM_TBL_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n"); ret = ath6kl_wmi_roam_tbl_event_rx(wmi, datap, len); break; case WMI_EXTENSION_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_EXTENSION_EVENTID\n"); ret = ath6kl_wmi_control_rx_xtnd(wmi, skb); break; case WMI_CHANNEL_CHANGE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CHANNEL_CHANGE_EVENTID\n"); break; case WMI_REPORT_ROAM_DATA_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n"); break; case WMI_TEST_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n"); ret = ath6kl_wmi_test_rx(wmi, datap, len); break; case WMI_GET_FIXRATES_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n"); ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len); break; case WMI_TX_RETRY_ERR_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_RETRY_ERR_EVENTID\n"); break; case WMI_SNR_THRESHOLD_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SNR_THRESHOLD_EVENTID\n"); ret = ath6kl_wmi_snr_threshold_event_rx(wmi, datap, len); break; case WMI_LQ_THRESHOLD_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_LQ_THRESHOLD_EVENTID\n"); break; case WMI_APLIST_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_APLIST_EVENTID\n"); ret = ath6kl_wmi_aplist_event_rx(wmi, datap, len); break; case WMI_GET_KEEPALIVE_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_KEEPALIVE_CMDID\n"); ret = ath6kl_wmi_keepalive_reply_rx(wmi, datap, len); break; case WMI_GET_WOW_LIST_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_WOW_LIST_EVENTID\n"); break; case WMI_GET_PMKID_LIST_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_PMKID_LIST_EVENTID\n"); ret = ath6kl_wmi_get_pmkid_list_event_rx(wmi, datap, len); break; case WMI_SET_PARAMS_REPLY_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_PARAMS_REPLY_EVENTID\n"); break; case WMI_ADDBA_RESP_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_RESP_EVENTID\n"); break; case WMI_REPORT_BTCOEX_CONFIG_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_BTCOEX_CONFIG_EVENTID\n"); break; case WMI_REPORT_BTCOEX_STATS_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_BTCOEX_STATS_EVENTID\n"); break; case WMI_TX_COMPLETE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n"); ret = ath6kl_wmi_tx_complete_event_rx(datap, len); break; case WMI_P2P_CAPABILITIES_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n"); ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len); break; case WMI_P2P_INFO_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n"); ret = ath6kl_wmi_p2p_info_event_rx(datap, len); break; default: /* may be the event is interface specific */ ret = ath6kl_wmi_proc_events_vif(wmi, if_idx, id, datap, len); break; } dev_kfree_skb(skb); return ret; } /* Control Path */ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) { if (WARN_ON(skb == NULL)) return -EINVAL; if (skb->len < sizeof(struct wmi_cmd_hdr)) { ath6kl_err("bad packet 1\n"); dev_kfree_skb(skb); return -EINVAL; } return ath6kl_wmi_proc_events(wmi, skb); } void ath6kl_wmi_reset(struct wmi *wmi) { spin_lock_bh(&wmi->lock); wmi->fat_pipe_exist = 0; memset(wmi->stream_exist_for_ac, 0, sizeof(wmi->stream_exist_for_ac)); spin_unlock_bh(&wmi->lock); } void *ath6kl_wmi_init(struct ath6kl *dev) { struct wmi *wmi; wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL); if (!wmi) return NULL; spin_lock_init(&wmi->lock); wmi->parent_dev = dev; wmi->pwr_mode = REC_POWER; ath6kl_wmi_reset(wmi); return wmi; } void ath6kl_wmi_shutdown(struct wmi *wmi) { if (!wmi) return; kfree(wmi->last_mgmt_tx_frame); kfree(wmi); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/testmode.h0000644000175000017500000000231312026211315024422 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core.h" #ifdef CONFIG_NL80211_TESTMODE void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len); int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len); #else static inline void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len) { } static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) { return 0; } #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/testmode.c0000644000175000017500000000526012026211315024421 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "testmode.h" #include "debug.h" #include enum ath6kl_tm_attr { __ATH6KL_TM_ATTR_INVALID = 0, ATH6KL_TM_ATTR_CMD = 1, ATH6KL_TM_ATTR_DATA = 2, /* keep last */ __ATH6KL_TM_ATTR_AFTER_LAST, ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1, }; enum ath6kl_tm_cmd { ATH6KL_TM_CMD_TCMD = 0, ATH6KL_TM_CMD_RX_REPORT = 1, /* not used anymore */ }; #define ATH6KL_TM_DATA_MAX_LEN 5000 static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = { [ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 }, [ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY, .len = ATH6KL_TM_DATA_MAX_LEN }, }; void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len) { struct sk_buff *skb; if (!buf || buf_len == 0) return; skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_KERNEL); if (!skb) { ath6kl_warn("failed to allocate testmode rx skb!\n"); return; } if (nla_put_u32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD) || nla_put(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf)) goto nla_put_failure; cfg80211_testmode_event(skb, GFP_KERNEL); return; nla_put_failure: kfree_skb(skb); ath6kl_warn("nla_put failed on testmode rx skb!\n"); } int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) { struct ath6kl *ar = wiphy_priv(wiphy); struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; int err, buf_len; void *buf; err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, ath6kl_tm_policy); if (err) return err; if (!tb[ATH6KL_TM_ATTR_CMD]) return -EINVAL; switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) { case ATH6KL_TM_CMD_TCMD: if (!tb[ATH6KL_TM_ATTR_DATA]) return -EINVAL; buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len); return 0; break; case ATH6KL_TM_CMD_RX_REPORT: default: return -EOPNOTSUPP; } } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/target.h0000644000175000017500000002747112026211315024100 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2010 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef TARGET_H #define TARGET_H #define AR6003_BOARD_DATA_SZ 1024 #define AR6003_BOARD_EXT_DATA_SZ 768 #define AR6003_BOARD_EXT_DATA_SZ_V2 1024 #define AR6004_BOARD_DATA_SZ 6144 #define AR6004_BOARD_EXT_DATA_SZ 0 #define RESET_CONTROL_ADDRESS 0x00000000 #define RESET_CONTROL_COLD_RST 0x00000100 #define RESET_CONTROL_MBOX_RST 0x00000004 #define CPU_CLOCK_STANDARD_S 0 #define CPU_CLOCK_STANDARD 0x00000003 #define CPU_CLOCK_ADDRESS 0x00000020 #define CLOCK_CONTROL_ADDRESS 0x00000028 #define CLOCK_CONTROL_LF_CLK32_S 2 #define CLOCK_CONTROL_LF_CLK32 0x00000004 #define SYSTEM_SLEEP_ADDRESS 0x000000c4 #define SYSTEM_SLEEP_DISABLE_S 0 #define SYSTEM_SLEEP_DISABLE 0x00000001 #define LPO_CAL_ADDRESS 0x000000e0 #define LPO_CAL_ENABLE_S 20 #define LPO_CAL_ENABLE 0x00100000 #define GPIO_PIN9_ADDRESS 0x0000004c #define GPIO_PIN10_ADDRESS 0x00000050 #define GPIO_PIN11_ADDRESS 0x00000054 #define GPIO_PIN12_ADDRESS 0x00000058 #define GPIO_PIN13_ADDRESS 0x0000005c #define HOST_INT_STATUS_ADDRESS 0x00000400 #define HOST_INT_STATUS_ERROR_S 7 #define HOST_INT_STATUS_ERROR 0x00000080 #define HOST_INT_STATUS_CPU_S 6 #define HOST_INT_STATUS_CPU 0x00000040 #define HOST_INT_STATUS_COUNTER_S 4 #define HOST_INT_STATUS_COUNTER 0x00000010 #define CPU_INT_STATUS_ADDRESS 0x00000401 #define ERROR_INT_STATUS_ADDRESS 0x00000402 #define ERROR_INT_STATUS_WAKEUP_S 2 #define ERROR_INT_STATUS_WAKEUP 0x00000004 #define ERROR_INT_STATUS_RX_UNDERFLOW_S 1 #define ERROR_INT_STATUS_RX_UNDERFLOW 0x00000002 #define ERROR_INT_STATUS_TX_OVERFLOW_S 0 #define ERROR_INT_STATUS_TX_OVERFLOW 0x00000001 #define COUNTER_INT_STATUS_ADDRESS 0x00000403 #define COUNTER_INT_STATUS_COUNTER_S 0 #define COUNTER_INT_STATUS_COUNTER 0x000000ff #define RX_LOOKAHEAD_VALID_ADDRESS 0x00000405 #define INT_STATUS_ENABLE_ADDRESS 0x00000418 #define INT_STATUS_ENABLE_ERROR_S 7 #define INT_STATUS_ENABLE_ERROR 0x00000080 #define INT_STATUS_ENABLE_CPU_S 6 #define INT_STATUS_ENABLE_CPU 0x00000040 #define INT_STATUS_ENABLE_INT_S 5 #define INT_STATUS_ENABLE_INT 0x00000020 #define INT_STATUS_ENABLE_COUNTER_S 4 #define INT_STATUS_ENABLE_COUNTER 0x00000010 #define INT_STATUS_ENABLE_MBOX_DATA_S 0 #define INT_STATUS_ENABLE_MBOX_DATA 0x0000000f #define CPU_INT_STATUS_ENABLE_ADDRESS 0x00000419 #define CPU_INT_STATUS_ENABLE_BIT_S 0 #define CPU_INT_STATUS_ENABLE_BIT 0x000000ff #define ERROR_STATUS_ENABLE_ADDRESS 0x0000041a #define ERROR_STATUS_ENABLE_RX_UNDERFLOW_S 1 #define ERROR_STATUS_ENABLE_RX_UNDERFLOW 0x00000002 #define ERROR_STATUS_ENABLE_TX_OVERFLOW_S 0 #define ERROR_STATUS_ENABLE_TX_OVERFLOW 0x00000001 #define COUNTER_INT_STATUS_ENABLE_ADDRESS 0x0000041b #define COUNTER_INT_STATUS_ENABLE_BIT_S 0 #define COUNTER_INT_STATUS_ENABLE_BIT 0x000000ff #define COUNT_ADDRESS 0x00000420 #define COUNT_DEC_ADDRESS 0x00000440 #define WINDOW_DATA_ADDRESS 0x00000474 #define WINDOW_WRITE_ADDR_ADDRESS 0x00000478 #define WINDOW_READ_ADDR_ADDRESS 0x0000047c #define CPU_DBG_SEL_ADDRESS 0x00000483 #define CPU_DBG_ADDRESS 0x00000484 #define LOCAL_SCRATCH_ADDRESS 0x000000c0 #define ATH6KL_OPTION_SLEEP_DISABLE 0x08 #define RTC_BASE_ADDRESS 0x00004000 #define GPIO_BASE_ADDRESS 0x00014000 #define MBOX_BASE_ADDRESS 0x00018000 #define ANALOG_INTF_BASE_ADDRESS 0x0001c000 /* real name of the register is unknown */ #define ATH6KL_ANALOG_PLL_REGISTER (ANALOG_INTF_BASE_ADDRESS + 0x284) #define SM(f, v) (((v) << f##_S) & f) #define MS(f, v) (((v) & f) >> f##_S) /* * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the * host_interest structure. * * Host Interest is shared between Host and Target in order to coordinate * between the two, and is intended to remain constant (with additions only * at the end). */ #define ATH6KL_AR6003_HI_START_ADDR 0x00540600 #define ATH6KL_AR6004_HI_START_ADDR 0x00400800 /* * These are items that the Host may need to access * via BMI or via the Diagnostic Window. The position * of items in this structure must remain constant. * across firmware revisions! * * Types for each item must be fixed size across target and host platforms. * The structure is used only to calculate offset for each register with * HI_ITEM() macro, no values are stored to it. * * More items may be added at the end. */ struct host_interest { /* * Pointer to application-defined area, if any. * Set by Target application during startup. */ u32 hi_app_host_interest; /* 0x00 */ /* Pointer to register dump area, valid after Target crash. */ u32 hi_failure_state; /* 0x04 */ /* Pointer to debug logging header */ u32 hi_dbglog_hdr; /* 0x08 */ u32 hi_unused1; /* 0x0c */ /* * General-purpose flag bits, similar to ATH6KL_OPTION_* flags. * Can be used by application rather than by OS. */ u32 hi_option_flag; /* 0x10 */ /* * Boolean that determines whether or not to * display messages on the serial port. */ u32 hi_serial_enable; /* 0x14 */ /* Start address of DataSet index, if any */ u32 hi_dset_list_head; /* 0x18 */ /* Override Target application start address */ u32 hi_app_start; /* 0x1c */ /* Clock and voltage tuning */ u32 hi_skip_clock_init; /* 0x20 */ u32 hi_core_clock_setting; /* 0x24 */ u32 hi_cpu_clock_setting; /* 0x28 */ u32 hi_system_sleep_setting; /* 0x2c */ u32 hi_xtal_control_setting; /* 0x30 */ u32 hi_pll_ctrl_setting_24ghz; /* 0x34 */ u32 hi_pll_ctrl_setting_5ghz; /* 0x38 */ u32 hi_ref_voltage_trim_setting; /* 0x3c */ u32 hi_clock_info; /* 0x40 */ /* * Flash configuration overrides, used only * when firmware is not executing from flash. * (When using flash, modify the global variables * with equivalent names.) */ u32 hi_bank0_addr_value; /* 0x44 */ u32 hi_bank0_read_value; /* 0x48 */ u32 hi_bank0_write_value; /* 0x4c */ u32 hi_bank0_config_value; /* 0x50 */ /* Pointer to Board Data */ u32 hi_board_data; /* 0x54 */ u32 hi_board_data_initialized; /* 0x58 */ u32 hi_dset_ram_index_tbl; /* 0x5c */ u32 hi_desired_baud_rate; /* 0x60 */ u32 hi_dbglog_config; /* 0x64 */ u32 hi_end_ram_reserve_sz; /* 0x68 */ u32 hi_mbox_io_block_sz; /* 0x6c */ u32 hi_num_bpatch_streams; /* 0x70 -- unused */ u32 hi_mbox_isr_yield_limit; /* 0x74 */ u32 hi_refclk_hz; /* 0x78 */ u32 hi_ext_clk_detected; /* 0x7c */ u32 hi_dbg_uart_txpin; /* 0x80 */ u32 hi_dbg_uart_rxpin; /* 0x84 */ u32 hi_hci_uart_baud; /* 0x88 */ u32 hi_hci_uart_pin_assignments; /* 0x8C */ /* * NOTE: byte [0] = tx pin, [1] = rx pin, [2] = rts pin, [3] = cts * pin */ u32 hi_hci_uart_baud_scale_val; /* 0x90 */ u32 hi_hci_uart_baud_step_val; /* 0x94 */ u32 hi_allocram_start; /* 0x98 */ u32 hi_allocram_sz; /* 0x9c */ u32 hi_hci_bridge_flags; /* 0xa0 */ u32 hi_hci_uart_support_pins; /* 0xa4 */ /* * NOTE: byte [0] = RESET pin (bit 7 is polarity), * bytes[1]..bytes[3] are for future use */ u32 hi_hci_uart_pwr_mgmt_params; /* 0xa8 */ /* * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high * [31:16]: wakeup timeout in ms */ /* Pointer to extended board data */ u32 hi_board_ext_data; /* 0xac */ u32 hi_board_ext_data_config; /* 0xb0 */ /* * Bit [0] : valid * Bit[31:16: size */ /* * hi_reset_flag is used to do some stuff when target reset. * such as restore app_start after warm reset or * preserve host Interest area, or preserve ROM data, literals etc. */ u32 hi_reset_flag; /* 0xb4 */ /* indicate hi_reset_flag is valid */ u32 hi_reset_flag_valid; /* 0xb8 */ u32 hi_hci_uart_pwr_mgmt_params_ext; /* 0xbc */ /* * 0xbc - [31:0]: idle timeout in ms */ /* ACS flags */ u32 hi_acs_flags; /* 0xc0 */ u32 hi_console_flags; /* 0xc4 */ u32 hi_nvram_state; /* 0xc8 */ u32 hi_option_flag2; /* 0xcc */ /* If non-zero, override values sent to Host in WMI_READY event. */ u32 hi_sw_version_override; /* 0xd0 */ u32 hi_abi_version_override; /* 0xd4 */ /* * Percentage of high priority RX traffic to total expected RX traffic - * applicable only to ar6004 */ u32 hi_hp_rx_traffic_ratio; /* 0xd8 */ /* test applications flags */ u32 hi_test_apps_related ; /* 0xdc */ /* location of test script */ u32 hi_ota_testscript; /* 0xe0 */ /* location of CAL data */ u32 hi_cal_data; /* 0xe4 */ /* Number of packet log buffers */ u32 hi_pktlog_num_buffers; /* 0xe8 */ } __packed; #define HI_ITEM(item) offsetof(struct host_interest, item) #define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3 #define HI_OPTION_FW_MODE_IBSS 0x0 #define HI_OPTION_FW_MODE_BSS_STA 0x1 #define HI_OPTION_FW_MODE_AP 0x2 #define HI_OPTION_FW_SUBMODE_NONE 0x0 #define HI_OPTION_FW_SUBMODE_P2PDEV 0x1 #define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 #define HI_OPTION_FW_SUBMODE_P2PGO 0x3 #define HI_OPTION_NUM_DEV_SHIFT 0x9 #define HI_OPTION_FW_BRIDGE_SHIFT 0x04 /* Fw Mode/SubMode Mask |------------------------------------------------------------------------------| | SUB | SUB | SUB | SUB | | | | | MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0| | (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2) |------------------------------------------------------------------------------| */ #define HI_OPTION_FW_MODE_BITS 0x2 #define HI_OPTION_FW_MODE_SHIFT 0xC #define HI_OPTION_FW_SUBMODE_BITS 0x2 #define HI_OPTION_FW_SUBMODE_SHIFT 0x14 /* Convert a Target virtual address into a Target physical address */ #define AR6003_VTOP(vaddr) ((vaddr) & 0x001fffff) #define AR6004_VTOP(vaddr) (vaddr) #define TARG_VTOP(target_type, vaddr) \ (((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \ (((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0)) #define ATH6KL_FWLOG_PAYLOAD_SIZE 1500 struct ath6kl_dbglog_buf { __le32 next; __le32 buffer_addr; __le32 bufsize; __le32 length; __le32 count; __le32 free; } __packed; struct ath6kl_dbglog_hdr { __le32 dbuf_addr; __le32 dropped; } __packed; #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/htc-ops.h0000644000175000017500000000620612026211315024160 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HTC_OPS_H #define HTC_OPS_H #include "htc.h" #include "debug.h" static inline void *ath6kl_htc_create(struct ath6kl *ar) { return ar->htc_ops->create(ar); } static inline int ath6kl_htc_wait_target(struct htc_target *target) { return target->dev->ar->htc_ops->wait_target(target); } static inline int ath6kl_htc_start(struct htc_target *target) { return target->dev->ar->htc_ops->start(target); } static inline int ath6kl_htc_conn_service(struct htc_target *target, struct htc_service_connect_req *req, struct htc_service_connect_resp *resp) { return target->dev->ar->htc_ops->conn_service(target, req, resp); } static inline int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet) { return target->dev->ar->htc_ops->tx(target, packet); } static inline void ath6kl_htc_stop(struct htc_target *target) { return target->dev->ar->htc_ops->stop(target); } static inline void ath6kl_htc_cleanup(struct htc_target *target) { return target->dev->ar->htc_ops->cleanup(target); } static inline void ath6kl_htc_flush_txep(struct htc_target *target, enum htc_endpoint_id endpoint, u16 tag) { return target->dev->ar->htc_ops->flush_txep(target, endpoint, tag); } static inline void ath6kl_htc_flush_rx_buf(struct htc_target *target) { return target->dev->ar->htc_ops->flush_rx_buf(target); } static inline void ath6kl_htc_activity_changed(struct htc_target *target, enum htc_endpoint_id endpoint, bool active) { return target->dev->ar->htc_ops->activity_changed(target, endpoint, active); } static inline int ath6kl_htc_get_rxbuf_num(struct htc_target *target, enum htc_endpoint_id endpoint) { return target->dev->ar->htc_ops->get_rxbuf_num(target, endpoint); } static inline int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, struct list_head *pktq) { return target->dev->ar->htc_ops->add_rxbuf_multiple(target, pktq); } static inline int ath6kl_htc_credit_setup(struct htc_target *target, struct ath6kl_htc_credit_info *info) { return target->dev->ar->htc_ops->credit_setup(target, info); } static inline void ath6kl_htc_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { ar->htc_ops->tx_complete(ar, skb); } static inline void ath6kl_htc_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe) { ar->htc_ops->rx_complete(ar, skb, pipe); } #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/htc_mbox.c0000644000175000017500000022450212026211315024402 0ustar mcgrofmcgrof/* * Copyright (c) 2007-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core.h" #include "hif.h" #include "debug.h" #include "hif-ops.h" #include #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) static void ath6kl_htc_mbox_cleanup(struct htc_target *target); static void ath6kl_htc_mbox_stop(struct htc_target *target); static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, struct list_head *pkt_queue); static void ath6kl_htc_set_credit_dist(struct htc_target *target, struct ath6kl_htc_credit_info *cred_info, u16 svc_pri_order[], int len); /* threshold to re-enable Tx bundling for an AC*/ #define TX_RESUME_BUNDLE_THRESHOLD 1500 /* Functions for Tx credit handling */ static void ath6kl_credit_deposit(struct ath6kl_htc_credit_info *cred_info, struct htc_endpoint_credit_dist *ep_dist, int credits) { ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit deposit ep %d credits %d\n", ep_dist->endpoint, credits); ep_dist->credits += credits; ep_dist->cred_assngd += credits; cred_info->cur_free_credits -= credits; } static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, struct list_head *ep_list, int tot_credits) { struct htc_endpoint_credit_dist *cur_ep_dist; int count; ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit init total %d\n", tot_credits); cred_info->cur_free_credits = tot_credits; cred_info->total_avail_credits = tot_credits; list_for_each_entry(cur_ep_dist, ep_list, list) { if (cur_ep_dist->endpoint == ENDPOINT_0) continue; cur_ep_dist->cred_min = cur_ep_dist->cred_per_msg; if (tot_credits > 4) { if ((cur_ep_dist->svc_id == WMI_DATA_BK_SVC) || (cur_ep_dist->svc_id == WMI_DATA_BE_SVC)) { ath6kl_credit_deposit(cred_info, cur_ep_dist, cur_ep_dist->cred_min); cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; } } if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) { ath6kl_credit_deposit(cred_info, cur_ep_dist, cur_ep_dist->cred_min); /* * Control service is always marked active, it * never goes inactive EVER. */ cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; } /* * Streams have to be created (explicit | implicit) for all * kinds of traffic. BE endpoints are also inactive in the * beginning. When BE traffic starts it creates implicit * streams that redistributes credits. * * Note: all other endpoints have minimums set but are * initially given NO credits. credits will be distributed * as traffic activity demands */ } /* * For ath6kl_credit_seek function, * it use list_for_each_entry_reverse to walk around the whole ep list. * Therefore assign this lowestpri_ep_dist after walk around the ep_list */ cred_info->lowestpri_ep_dist = cur_ep_dist->list; WARN_ON(cred_info->cur_free_credits <= 0); list_for_each_entry(cur_ep_dist, ep_list, list) { if (cur_ep_dist->endpoint == ENDPOINT_0) continue; if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg; else { /* * For the remaining data endpoints, we assume that * each cred_per_msg are the same. We use a simple * calculation here, we take the remaining credits * and determine how many max messages this can * cover and then set each endpoint's normal value * equal to 3/4 this amount. */ count = (cred_info->cur_free_credits / cur_ep_dist->cred_per_msg) * cur_ep_dist->cred_per_msg; count = (count * 3) >> 2; count = max(count, cur_ep_dist->cred_per_msg); cur_ep_dist->cred_norm = count; } ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit ep %d svc_id %d credits %d per_msg %d norm %d min %d\n", cur_ep_dist->endpoint, cur_ep_dist->svc_id, cur_ep_dist->credits, cur_ep_dist->cred_per_msg, cur_ep_dist->cred_norm, cur_ep_dist->cred_min); } } /* initialize and setup credit distribution */ static int ath6kl_htc_mbox_credit_setup(struct htc_target *htc_target, struct ath6kl_htc_credit_info *cred_info) { u16 servicepriority[5]; memset(cred_info, 0, sizeof(struct ath6kl_htc_credit_info)); servicepriority[0] = WMI_CONTROL_SVC; /* highest */ servicepriority[1] = WMI_DATA_VO_SVC; servicepriority[2] = WMI_DATA_VI_SVC; servicepriority[3] = WMI_DATA_BE_SVC; servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ /* set priority list */ ath6kl_htc_set_credit_dist(htc_target, cred_info, servicepriority, 5); return 0; } /* reduce an ep's credits back to a set limit */ static void ath6kl_credit_reduce(struct ath6kl_htc_credit_info *cred_info, struct htc_endpoint_credit_dist *ep_dist, int limit) { int credits; ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit reduce ep %d limit %d\n", ep_dist->endpoint, limit); ep_dist->cred_assngd = limit; if (ep_dist->credits <= limit) return; credits = ep_dist->credits - limit; ep_dist->credits -= credits; cred_info->cur_free_credits += credits; } static void ath6kl_credit_update(struct ath6kl_htc_credit_info *cred_info, struct list_head *epdist_list) { struct htc_endpoint_credit_dist *cur_list; list_for_each_entry(cur_list, epdist_list, list) { if (cur_list->endpoint == ENDPOINT_0) continue; if (cur_list->cred_to_dist > 0) { cur_list->credits += cur_list->cred_to_dist; cur_list->cred_to_dist = 0; if (cur_list->credits > cur_list->cred_assngd) ath6kl_credit_reduce(cred_info, cur_list, cur_list->cred_assngd); if (cur_list->credits > cur_list->cred_norm) ath6kl_credit_reduce(cred_info, cur_list, cur_list->cred_norm); if (!(cur_list->dist_flags & HTC_EP_ACTIVE)) { if (cur_list->txq_depth == 0) ath6kl_credit_reduce(cred_info, cur_list, 0); } } } } /* * HTC has an endpoint that needs credits, ep_dist is the endpoint in * question. */ static void ath6kl_credit_seek(struct ath6kl_htc_credit_info *cred_info, struct htc_endpoint_credit_dist *ep_dist) { struct htc_endpoint_credit_dist *curdist_list; int credits = 0; int need; if (ep_dist->svc_id == WMI_CONTROL_SVC) goto out; if ((ep_dist->svc_id == WMI_DATA_VI_SVC) || (ep_dist->svc_id == WMI_DATA_VO_SVC)) if ((ep_dist->cred_assngd >= ep_dist->cred_norm)) goto out; /* * For all other services, we follow a simple algorithm of: * * 1. checking the free pool for credits * 2. checking lower priority endpoints for credits to take */ credits = min(cred_info->cur_free_credits, ep_dist->seek_cred); if (credits >= ep_dist->seek_cred) goto out; /* * We don't have enough in the free pool, try taking away from * lower priority services The rule for taking away credits: * * 1. Only take from lower priority endpoints * 2. Only take what is allocated above the minimum (never * starve an endpoint completely) * 3. Only take what you need. */ list_for_each_entry_reverse(curdist_list, &cred_info->lowestpri_ep_dist, list) { if (curdist_list == ep_dist) break; need = ep_dist->seek_cred - cred_info->cur_free_credits; if ((curdist_list->cred_assngd - need) >= curdist_list->cred_min) { /* * The current one has been allocated more than * it's minimum and it has enough credits assigned * above it's minimum to fulfill our need try to * take away just enough to fulfill our need. */ ath6kl_credit_reduce(cred_info, curdist_list, curdist_list->cred_assngd - need); if (cred_info->cur_free_credits >= ep_dist->seek_cred) break; } if (curdist_list->endpoint == ENDPOINT_0) break; } credits = min(cred_info->cur_free_credits, ep_dist->seek_cred); out: /* did we find some credits? */ if (credits) ath6kl_credit_deposit(cred_info, ep_dist, credits); ep_dist->seek_cred = 0; } /* redistribute credits based on activity change */ static void ath6kl_credit_redistribute(struct ath6kl_htc_credit_info *info, struct list_head *ep_dist_list) { struct htc_endpoint_credit_dist *curdist_list; list_for_each_entry(curdist_list, ep_dist_list, list) { if (curdist_list->endpoint == ENDPOINT_0) continue; if ((curdist_list->svc_id == WMI_DATA_BK_SVC) || (curdist_list->svc_id == WMI_DATA_BE_SVC)) curdist_list->dist_flags |= HTC_EP_ACTIVE; if ((curdist_list->svc_id != WMI_CONTROL_SVC) && !(curdist_list->dist_flags & HTC_EP_ACTIVE)) { if (curdist_list->txq_depth == 0) ath6kl_credit_reduce(info, curdist_list, 0); else ath6kl_credit_reduce(info, curdist_list, curdist_list->cred_min); } } } /* * * This function is invoked whenever endpoints require credit * distributions. A lock is held while this function is invoked, this * function shall NOT block. The ep_dist_list is a list of distribution * structures in prioritized order as defined by the call to the * htc_set_credit_dist() api. */ static void ath6kl_credit_distribute(struct ath6kl_htc_credit_info *cred_info, struct list_head *ep_dist_list, enum htc_credit_dist_reason reason) { switch (reason) { case HTC_CREDIT_DIST_SEND_COMPLETE: ath6kl_credit_update(cred_info, ep_dist_list); break; case HTC_CREDIT_DIST_ACTIVITY_CHANGE: ath6kl_credit_redistribute(cred_info, ep_dist_list); break; default: break; } WARN_ON(cred_info->cur_free_credits > cred_info->total_avail_credits); WARN_ON(cred_info->cur_free_credits < 0); } static void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len) { u8 *align_addr; if (!IS_ALIGNED((unsigned long) *buf, 4)) { align_addr = PTR_ALIGN(*buf - 4, 4); memmove(align_addr, *buf, len); *buf = align_addr; } } static void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags, int ctrl0, int ctrl1) { struct htc_frame_hdr *hdr; packet->buf -= HTC_HDR_LENGTH; hdr = (struct htc_frame_hdr *)packet->buf; /* Endianess? */ put_unaligned((u16)packet->act_len, &hdr->payld_len); hdr->flags = flags; hdr->eid = packet->endpoint; hdr->ctrl[0] = ctrl0; hdr->ctrl[1] = ctrl1; } static void htc_reclaim_txctrl_buf(struct htc_target *target, struct htc_packet *pkt) { spin_lock_bh(&target->htc_lock); list_add_tail(&pkt->list, &target->free_ctrl_txbuf); spin_unlock_bh(&target->htc_lock); } static struct htc_packet *htc_get_control_buf(struct htc_target *target, bool tx) { struct htc_packet *packet = NULL; struct list_head *buf_list; buf_list = tx ? &target->free_ctrl_txbuf : &target->free_ctrl_rxbuf; spin_lock_bh(&target->htc_lock); if (list_empty(buf_list)) { spin_unlock_bh(&target->htc_lock); return NULL; } packet = list_first_entry(buf_list, struct htc_packet, list); list_del(&packet->list); spin_unlock_bh(&target->htc_lock); if (tx) packet->buf = packet->buf_start + HTC_HDR_LENGTH; return packet; } static void htc_tx_comp_update(struct htc_target *target, struct htc_endpoint *endpoint, struct htc_packet *packet) { packet->completion = NULL; packet->buf += HTC_HDR_LENGTH; if (!packet->status) return; ath6kl_err("req failed (status:%d, ep:%d, len:%d creds:%d)\n", packet->status, packet->endpoint, packet->act_len, packet->info.tx.cred_used); /* on failure to submit, reclaim credits for this packet */ spin_lock_bh(&target->tx_lock); endpoint->cred_dist.cred_to_dist += packet->info.tx.cred_used; endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq); ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx ctxt 0x%p dist 0x%p\n", target->credit_info, &target->cred_dist_list); ath6kl_credit_distribute(target->credit_info, &target->cred_dist_list, HTC_CREDIT_DIST_SEND_COMPLETE); spin_unlock_bh(&target->tx_lock); } static void htc_tx_complete(struct htc_endpoint *endpoint, struct list_head *txq) { if (list_empty(txq)) return; ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx complete ep %d pkts %d\n", endpoint->eid, get_queue_depth(txq)); ath6kl_tx_complete(endpoint->target, txq); } static void htc_tx_comp_handler(struct htc_target *target, struct htc_packet *packet) { struct htc_endpoint *endpoint = &target->endpoint[packet->endpoint]; struct list_head container; ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx complete seqno %d\n", packet->info.tx.seqno); htc_tx_comp_update(target, endpoint, packet); INIT_LIST_HEAD(&container); list_add_tail(&packet->list, &container); /* do completion */ htc_tx_complete(endpoint, &container); } static void htc_async_tx_scat_complete(struct htc_target *target, struct hif_scatter_req *scat_req) { struct htc_endpoint *endpoint; struct htc_packet *packet; struct list_head tx_compq; int i; INIT_LIST_HEAD(&tx_compq); ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx scat complete len %d entries %d\n", scat_req->len, scat_req->scat_entries); if (scat_req->status) ath6kl_err("send scatter req failed: %d\n", scat_req->status); packet = scat_req->scat_list[0].packet; endpoint = &target->endpoint[packet->endpoint]; /* walk through the scatter list and process */ for (i = 0; i < scat_req->scat_entries; i++) { packet = scat_req->scat_list[i].packet; if (!packet) { WARN_ON(1); return; } packet->status = scat_req->status; htc_tx_comp_update(target, endpoint, packet); list_add_tail(&packet->list, &tx_compq); } /* free scatter request */ hif_scatter_req_add(target->dev->ar, scat_req); /* complete all packets */ htc_tx_complete(endpoint, &tx_compq); } static int ath6kl_htc_tx_issue(struct htc_target *target, struct htc_packet *packet) { int status; bool sync = false; u32 padded_len, send_len; if (!packet->completion) sync = true; send_len = packet->act_len + HTC_HDR_LENGTH; padded_len = CALC_TXRX_PADDED_LEN(target, send_len); ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx issue len %d seqno %d padded_len %d mbox 0x%X %s\n", send_len, packet->info.tx.seqno, padded_len, target->dev->ar->mbox_info.htc_addr, sync ? "sync" : "async"); if (sync) { status = hif_read_write_sync(target->dev->ar, target->dev->ar->mbox_info.htc_addr, packet->buf, padded_len, HIF_WR_SYNC_BLOCK_INC); packet->status = status; packet->buf += HTC_HDR_LENGTH; } else status = hif_write_async(target->dev->ar, target->dev->ar->mbox_info.htc_addr, packet->buf, padded_len, HIF_WR_ASYNC_BLOCK_INC, packet); return status; } static int htc_check_credits(struct htc_target *target, struct htc_endpoint *ep, u8 *flags, enum htc_endpoint_id eid, unsigned int len, int *req_cred) { *req_cred = (len > target->tgt_cred_sz) ? DIV_ROUND_UP(len, target->tgt_cred_sz) : 1; ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit check need %d got %d\n", *req_cred, ep->cred_dist.credits); if (ep->cred_dist.credits < *req_cred) { if (eid == ENDPOINT_0) return -EINVAL; /* Seek more credits */ ep->cred_dist.seek_cred = *req_cred - ep->cred_dist.credits; ath6kl_credit_seek(target->credit_info, &ep->cred_dist); ep->cred_dist.seek_cred = 0; if (ep->cred_dist.credits < *req_cred) { ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit not found for ep %d\n", eid); return -EINVAL; } } ep->cred_dist.credits -= *req_cred; ep->ep_st.cred_cosumd += *req_cred; /* When we are getting low on credits, ask for more */ if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { ep->cred_dist.seek_cred = ep->cred_dist.cred_per_msg - ep->cred_dist.credits; ath6kl_credit_seek(target->credit_info, &ep->cred_dist); /* see if we were successful in getting more */ if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { /* tell the target we need credits ASAP! */ *flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; ep->ep_st.cred_low_indicate += 1; ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit we need credits asap\n"); } } return 0; } static void ath6kl_htc_tx_pkts_get(struct htc_target *target, struct htc_endpoint *endpoint, struct list_head *queue) { int req_cred; u8 flags; struct htc_packet *packet; unsigned int len; while (true) { flags = 0; if (list_empty(&endpoint->txq)) break; packet = list_first_entry(&endpoint->txq, struct htc_packet, list); ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx got packet 0x%p queue depth %d\n", packet, get_queue_depth(&endpoint->txq)); len = CALC_TXRX_PADDED_LEN(target, packet->act_len + HTC_HDR_LENGTH); if (htc_check_credits(target, endpoint, &flags, packet->endpoint, len, &req_cred)) break; /* now we can fully move onto caller's queue */ packet = list_first_entry(&endpoint->txq, struct htc_packet, list); list_move_tail(&packet->list, queue); /* save the number of credits this packet consumed */ packet->info.tx.cred_used = req_cred; /* all TX packets are handled asynchronously */ packet->completion = htc_tx_comp_handler; packet->context = target; endpoint->ep_st.tx_issued += 1; /* save send flags */ packet->info.tx.flags = flags; packet->info.tx.seqno = endpoint->seqno; endpoint->seqno++; } } /* See if the padded tx length falls on a credit boundary */ static int htc_get_credit_padding(unsigned int cred_sz, int *len, struct htc_endpoint *ep) { int rem_cred, cred_pad; rem_cred = *len % cred_sz; /* No padding needed */ if (!rem_cred) return 0; if (!(ep->conn_flags & HTC_FLGS_TX_BNDL_PAD_EN)) return -1; /* * The transfer consumes a "partial" credit, this * packet cannot be bundled unless we add * additional "dummy" padding (max 255 bytes) to * consume the entire credit. */ cred_pad = *len < cred_sz ? (cred_sz - *len) : rem_cred; if ((cred_pad > 0) && (cred_pad <= 255)) *len += cred_pad; else /* The amount of padding is too large, send as non-bundled */ return -1; return cred_pad; } static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, struct htc_endpoint *endpoint, struct hif_scatter_req *scat_req, int n_scat, struct list_head *queue) { struct htc_packet *packet; int i, len, rem_scat, cred_pad; int status = 0; u8 flags; rem_scat = target->max_tx_bndl_sz; for (i = 0; i < n_scat; i++) { scat_req->scat_list[i].packet = NULL; if (list_empty(queue)) break; packet = list_first_entry(queue, struct htc_packet, list); len = CALC_TXRX_PADDED_LEN(target, packet->act_len + HTC_HDR_LENGTH); cred_pad = htc_get_credit_padding(target->tgt_cred_sz, &len, endpoint); if (cred_pad < 0 || rem_scat < len) { status = -ENOSPC; break; } rem_scat -= len; /* now remove it from the queue */ list_del(&packet->list); scat_req->scat_list[i].packet = packet; /* prepare packet and flag message as part of a send bundle */ flags = packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE; ath6kl_htc_tx_prep_pkt(packet, flags, cred_pad, packet->info.tx.seqno); /* Make sure the buffer is 4-byte aligned */ ath6kl_htc_tx_buf_align(&packet->buf, packet->act_len + HTC_HDR_LENGTH); scat_req->scat_list[i].buf = packet->buf; scat_req->scat_list[i].len = len; scat_req->len += len; scat_req->scat_entries++; ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx adding (%d) pkt 0x%p seqno %d len %d remaining %d\n", i, packet, packet->info.tx.seqno, len, rem_scat); } /* Roll back scatter setup in case of any failure */ if (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE) { for (i = scat_req->scat_entries - 1; i >= 0; i--) { packet = scat_req->scat_list[i].packet; if (packet) { packet->buf += HTC_HDR_LENGTH; list_add(&packet->list, queue); } } return -EAGAIN; } return status; } /* * Drain a queue and send as bundles this function may return without fully * draining the queue when * * 1. scatter resources are exhausted * 2. a message that will consume a partial credit will stop the * bundling process early * 3. we drop below the minimum number of messages for a bundle */ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, struct list_head *queue, int *sent_bundle, int *n_bundle_pkts) { struct htc_target *target = endpoint->target; struct hif_scatter_req *scat_req = NULL; int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0; int status; u32 txb_mask; u8 ac = WMM_NUM_AC; if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && (WMI_CONTROL_SVC != endpoint->svc_id)) ac = target->dev->ar->ep2ac_map[endpoint->eid]; while (true) { status = 0; n_scat = get_queue_depth(queue); n_scat = min(n_scat, target->msg_per_bndl_max); if (n_scat < HTC_MIN_HTC_MSGS_TO_BUNDLE) /* not enough to bundle */ break; scat_req = hif_scatter_req_get(target->dev->ar); if (!scat_req) { /* no scatter resources */ ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx no more scatter resources\n"); break; } if ((ac < WMM_NUM_AC) && (ac != WMM_AC_BK)) { if (WMM_AC_BE == ac) /* * BE, BK have priorities and bit * positions reversed */ txb_mask = (1 << WMM_AC_BK); else /* * any AC with priority lower than * itself */ txb_mask = ((1 << ac) - 1); /* * when the scatter request resources drop below a * certain threshold, disable Tx bundling for all * AC's with priority lower than the current requesting * AC. Otherwise re-enable Tx bundling for them */ if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS) target->tx_bndl_mask &= ~txb_mask; else target->tx_bndl_mask |= txb_mask; } ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx pkts to scatter: %d\n", n_scat); scat_req->len = 0; scat_req->scat_entries = 0; status = ath6kl_htc_tx_setup_scat_list(target, endpoint, scat_req, n_scat, queue); if (status == -EAGAIN) { hif_scatter_req_add(target->dev->ar, scat_req); break; } /* send path is always asynchronous */ scat_req->complete = htc_async_tx_scat_complete; n_sent_bundle++; tot_pkts_bundle += scat_req->scat_entries; ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx scatter bytes %d entries %d\n", scat_req->len, scat_req->scat_entries); ath6kl_hif_submit_scat_req(target->dev, scat_req, false); if (status) break; } *sent_bundle = n_sent_bundle; *n_bundle_pkts = tot_pkts_bundle; ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx bundle sent %d pkts\n", n_sent_bundle); return; } static void ath6kl_htc_tx_from_queue(struct htc_target *target, struct htc_endpoint *endpoint) { struct list_head txq; struct htc_packet *packet; int bundle_sent; int n_pkts_bundle; u8 ac = WMM_NUM_AC; int status; spin_lock_bh(&target->tx_lock); endpoint->tx_proc_cnt++; if (endpoint->tx_proc_cnt > 1) { endpoint->tx_proc_cnt--; spin_unlock_bh(&target->tx_lock); ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx busy\n"); return; } /* * drain the endpoint TX queue for transmission as long * as we have enough credits. */ INIT_LIST_HEAD(&txq); if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && (WMI_CONTROL_SVC != endpoint->svc_id)) ac = target->dev->ar->ep2ac_map[endpoint->eid]; while (true) { if (list_empty(&endpoint->txq)) break; ath6kl_htc_tx_pkts_get(target, endpoint, &txq); if (list_empty(&txq)) break; spin_unlock_bh(&target->tx_lock); bundle_sent = 0; n_pkts_bundle = 0; while (true) { /* try to send a bundle on each pass */ if ((target->tx_bndl_mask) && (get_queue_depth(&txq) >= HTC_MIN_HTC_MSGS_TO_BUNDLE)) { int temp1 = 0, temp2 = 0; /* check if bundling is enabled for an AC */ if (target->tx_bndl_mask & (1 << ac)) { ath6kl_htc_tx_bundle(endpoint, &txq, &temp1, &temp2); bundle_sent += temp1; n_pkts_bundle += temp2; } } if (list_empty(&txq)) break; packet = list_first_entry(&txq, struct htc_packet, list); list_del(&packet->list); ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags, 0, packet->info.tx.seqno); status = ath6kl_htc_tx_issue(target, packet); if (status) { packet->status = status; packet->completion(packet->context, packet); } } spin_lock_bh(&target->tx_lock); endpoint->ep_st.tx_bundles += bundle_sent; endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle; /* * if an AC has bundling disabled and no tx bundling * has occured continously for a certain number of TX, * enable tx bundling for this AC */ if (!bundle_sent) { if (!(target->tx_bndl_mask & (1 << ac)) && (ac < WMM_NUM_AC)) { if (++target->ac_tx_count[ac] >= TX_RESUME_BUNDLE_THRESHOLD) { target->ac_tx_count[ac] = 0; target->tx_bndl_mask |= (1 << ac); } } } else { /* tx bundling will reset the counter */ if (ac < WMM_NUM_AC) target->ac_tx_count[ac] = 0; } } endpoint->tx_proc_cnt = 0; spin_unlock_bh(&target->tx_lock); } static bool ath6kl_htc_tx_try(struct htc_target *target, struct htc_endpoint *endpoint, struct htc_packet *tx_pkt) { struct htc_ep_callbacks ep_cb; int txq_depth; bool overflow = false; ep_cb = endpoint->ep_cb; spin_lock_bh(&target->tx_lock); txq_depth = get_queue_depth(&endpoint->txq); spin_unlock_bh(&target->tx_lock); if (txq_depth >= endpoint->max_txq_depth) overflow = true; if (overflow) ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx overflow ep %d depth %d max %d\n", endpoint->eid, txq_depth, endpoint->max_txq_depth); if (overflow && ep_cb.tx_full) { if (ep_cb.tx_full(endpoint->target, tx_pkt) == HTC_SEND_FULL_DROP) { endpoint->ep_st.tx_dropped += 1; return false; } } spin_lock_bh(&target->tx_lock); list_add_tail(&tx_pkt->list, &endpoint->txq); spin_unlock_bh(&target->tx_lock); ath6kl_htc_tx_from_queue(target, endpoint); return true; } static void htc_chk_ep_txq(struct htc_target *target) { struct htc_endpoint *endpoint; struct htc_endpoint_credit_dist *cred_dist; /* * Run through the credit distribution list to see if there are * packets queued. NOTE: no locks need to be taken since the * distribution list is not dynamic (cannot be re-ordered) and we * are not modifying any state. */ list_for_each_entry(cred_dist, &target->cred_dist_list, list) { endpoint = cred_dist->htc_ep; spin_lock_bh(&target->tx_lock); if (!list_empty(&endpoint->txq)) { ath6kl_dbg(ATH6KL_DBG_HTC, "htc creds ep %d credits %d pkts %d\n", cred_dist->endpoint, endpoint->cred_dist.credits, get_queue_depth(&endpoint->txq)); spin_unlock_bh(&target->tx_lock); /* * Try to start the stalled queue, this list is * ordered by priority. If there are credits * available the highest priority queue will get a * chance to reclaim credits from lower priority * ones. */ ath6kl_htc_tx_from_queue(target, endpoint); spin_lock_bh(&target->tx_lock); } spin_unlock_bh(&target->tx_lock); } } static int htc_setup_tx_complete(struct htc_target *target) { struct htc_packet *send_pkt = NULL; int status; send_pkt = htc_get_control_buf(target, true); if (!send_pkt) return -ENOMEM; if (target->htc_tgt_ver >= HTC_VERSION_2P1) { struct htc_setup_comp_ext_msg *setup_comp_ext; u32 flags = 0; setup_comp_ext = (struct htc_setup_comp_ext_msg *)send_pkt->buf; memset(setup_comp_ext, 0, sizeof(*setup_comp_ext)); setup_comp_ext->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID); if (target->msg_per_bndl_max > 0) { /* Indicate HTC bundling to the target */ flags |= HTC_SETUP_COMP_FLG_RX_BNDL_EN; setup_comp_ext->msg_per_rxbndl = target->msg_per_bndl_max; } memcpy(&setup_comp_ext->flags, &flags, sizeof(setup_comp_ext->flags)); set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp_ext, sizeof(struct htc_setup_comp_ext_msg), ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } else { struct htc_setup_comp_msg *setup_comp; setup_comp = (struct htc_setup_comp_msg *)send_pkt->buf; memset(setup_comp, 0, sizeof(struct htc_setup_comp_msg)); setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_ID); set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp, sizeof(struct htc_setup_comp_msg), ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } /* we want synchronous operation */ send_pkt->completion = NULL; ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0); status = ath6kl_htc_tx_issue(target, send_pkt); if (send_pkt != NULL) htc_reclaim_txctrl_buf(target, send_pkt); return status; } static void ath6kl_htc_set_credit_dist(struct htc_target *target, struct ath6kl_htc_credit_info *credit_info, u16 srvc_pri_order[], int list_len) { struct htc_endpoint *endpoint; int i, ep; target->credit_info = credit_info; list_add_tail(&target->endpoint[ENDPOINT_0].cred_dist.list, &target->cred_dist_list); for (i = 0; i < list_len; i++) { for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) { endpoint = &target->endpoint[ep]; if (endpoint->svc_id == srvc_pri_order[i]) { list_add_tail(&endpoint->cred_dist.list, &target->cred_dist_list); break; } } if (ep >= ENDPOINT_MAX) { WARN_ON(1); return; } } } static int ath6kl_htc_mbox_tx(struct htc_target *target, struct htc_packet *packet) { struct htc_endpoint *endpoint; struct list_head queue; ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx ep id %d buf 0x%p len %d\n", packet->endpoint, packet->buf, packet->act_len); if (packet->endpoint >= ENDPOINT_MAX) { WARN_ON(1); return -EINVAL; } endpoint = &target->endpoint[packet->endpoint]; if (!ath6kl_htc_tx_try(target, endpoint, packet)) { packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ? -ECANCELED : -ENOSPC; INIT_LIST_HEAD(&queue); list_add(&packet->list, &queue); htc_tx_complete(endpoint, &queue); } return 0; } /* flush endpoint TX queue */ static void ath6kl_htc_mbox_flush_txep(struct htc_target *target, enum htc_endpoint_id eid, u16 tag) { struct htc_packet *packet, *tmp_pkt; struct list_head discard_q, container; struct htc_endpoint *endpoint = &target->endpoint[eid]; if (!endpoint->svc_id) { WARN_ON(1); return; } /* initialize the discard queue */ INIT_LIST_HEAD(&discard_q); spin_lock_bh(&target->tx_lock); list_for_each_entry_safe(packet, tmp_pkt, &endpoint->txq, list) { if ((tag == HTC_TX_PACKET_TAG_ALL) || (tag == packet->info.tx.tag)) list_move_tail(&packet->list, &discard_q); } spin_unlock_bh(&target->tx_lock); list_for_each_entry_safe(packet, tmp_pkt, &discard_q, list) { packet->status = -ECANCELED; list_del(&packet->list); ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx flushing pkt 0x%p len %d ep %d tag 0x%x\n", packet, packet->act_len, packet->endpoint, packet->info.tx.tag); INIT_LIST_HEAD(&container); list_add_tail(&packet->list, &container); htc_tx_complete(endpoint, &container); } } static void ath6kl_htc_flush_txep_all(struct htc_target *target) { struct htc_endpoint *endpoint; int i; dump_cred_dist_stats(target); for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { endpoint = &target->endpoint[i]; if (endpoint->svc_id == 0) /* not in use.. */ continue; ath6kl_htc_mbox_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL); } } static void ath6kl_htc_mbox_activity_changed(struct htc_target *target, enum htc_endpoint_id eid, bool active) { struct htc_endpoint *endpoint = &target->endpoint[eid]; bool dist = false; if (endpoint->svc_id == 0) { WARN_ON(1); return; } spin_lock_bh(&target->tx_lock); if (active) { if (!(endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE)) { endpoint->cred_dist.dist_flags |= HTC_EP_ACTIVE; dist = true; } } else { if (endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE) { endpoint->cred_dist.dist_flags &= ~HTC_EP_ACTIVE; dist = true; } } if (dist) { endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq); ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx activity ctxt 0x%p dist 0x%p\n", target->credit_info, &target->cred_dist_list); ath6kl_credit_distribute(target->credit_info, &target->cred_dist_list, HTC_CREDIT_DIST_ACTIVITY_CHANGE); } spin_unlock_bh(&target->tx_lock); if (dist && !active) htc_chk_ep_txq(target); } /* HTC Rx */ static inline void ath6kl_htc_rx_update_stats(struct htc_endpoint *endpoint, int n_look_ahds) { endpoint->ep_st.rx_pkts++; if (n_look_ahds == 1) endpoint->ep_st.rx_lkahds++; else if (n_look_ahds > 1) endpoint->ep_st.rx_bundle_lkahd++; } static inline bool htc_valid_rx_frame_len(struct htc_target *target, enum htc_endpoint_id eid, int len) { return (eid == target->dev->ar->ctrl_ep) ? len <= ATH6KL_BUFFER_SIZE : len <= ATH6KL_AMSDU_BUFFER_SIZE; } static int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet) { struct list_head queue; INIT_LIST_HEAD(&queue); list_add_tail(&packet->list, &queue); return ath6kl_htc_mbox_add_rxbuf_multiple(target, &queue); } static void htc_reclaim_rxbuf(struct htc_target *target, struct htc_packet *packet, struct htc_endpoint *ep) { if (packet->info.rx.rx_flags & HTC_RX_PKT_NO_RECYCLE) { htc_rxpkt_reset(packet); packet->status = -ECANCELED; ep->ep_cb.rx(ep->target, packet); } else { htc_rxpkt_reset(packet); htc_add_rxbuf((void *)(target), packet); } } static void reclaim_rx_ctrl_buf(struct htc_target *target, struct htc_packet *packet) { spin_lock_bh(&target->htc_lock); list_add_tail(&packet->list, &target->free_ctrl_rxbuf); spin_unlock_bh(&target->htc_lock); } static int ath6kl_htc_rx_packet(struct htc_target *target, struct htc_packet *packet, u32 rx_len) { struct ath6kl_device *dev = target->dev; u32 padded_len; int status; padded_len = CALC_TXRX_PADDED_LEN(target, rx_len); if (padded_len > packet->buf_len) { ath6kl_err("not enough receive space for packet - padlen %d recvlen %d bufferlen %d\n", padded_len, rx_len, packet->buf_len); return -ENOMEM; } ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx 0x%p hdr 0x%x len %d mbox 0x%x\n", packet, packet->info.rx.exp_hdr, padded_len, dev->ar->mbox_info.htc_addr); status = hif_read_write_sync(dev->ar, dev->ar->mbox_info.htc_addr, packet->buf, padded_len, HIF_RD_SYNC_BLOCK_FIX); packet->status = status; return status; } /* * optimization for recv packets, we can indicate a * "hint" that there are more single-packets to fetch * on this endpoint. */ static void ath6kl_htc_rx_set_indicate(u32 lk_ahd, struct htc_endpoint *endpoint, struct htc_packet *packet) { struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)&lk_ahd; if (htc_hdr->eid == packet->endpoint) { if (!list_empty(&endpoint->rx_bufq)) packet->info.rx.indicat_flags |= HTC_RX_FLAGS_INDICATE_MORE_PKTS; } } static void ath6kl_htc_rx_chk_water_mark(struct htc_endpoint *endpoint) { struct htc_ep_callbacks ep_cb = endpoint->ep_cb; if (ep_cb.rx_refill_thresh > 0) { spin_lock_bh(&endpoint->target->rx_lock); if (get_queue_depth(&endpoint->rx_bufq) < ep_cb.rx_refill_thresh) { spin_unlock_bh(&endpoint->target->rx_lock); ep_cb.rx_refill(endpoint->target, endpoint->eid); return; } spin_unlock_bh(&endpoint->target->rx_lock); } } /* This function is called with rx_lock held */ static int ath6kl_htc_rx_setup(struct htc_target *target, struct htc_endpoint *ep, u32 *lk_ahds, struct list_head *queue, int n_msg) { struct htc_packet *packet; /* FIXME: type of lk_ahds can't be right */ struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)lk_ahds; struct htc_ep_callbacks ep_cb; int status = 0, j, full_len; bool no_recycle; full_len = CALC_TXRX_PADDED_LEN(target, le16_to_cpu(htc_hdr->payld_len) + sizeof(*htc_hdr)); if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) { ath6kl_warn("Rx buffer requested with invalid length htc_hdr:eid %d, flags 0x%x, len %d\n", htc_hdr->eid, htc_hdr->flags, le16_to_cpu(htc_hdr->payld_len)); return -EINVAL; } ep_cb = ep->ep_cb; for (j = 0; j < n_msg; j++) { /* * Reset flag, any packets allocated using the * rx_alloc() API cannot be recycled on * cleanup,they must be explicitly returned. */ no_recycle = false; if (ep_cb.rx_allocthresh && (full_len > ep_cb.rx_alloc_thresh)) { ep->ep_st.rx_alloc_thresh_hit += 1; ep->ep_st.rxalloc_thresh_byte += le16_to_cpu(htc_hdr->payld_len); spin_unlock_bh(&target->rx_lock); no_recycle = true; packet = ep_cb.rx_allocthresh(ep->target, ep->eid, full_len); spin_lock_bh(&target->rx_lock); } else { /* refill handler is being used */ if (list_empty(&ep->rx_bufq)) { if (ep_cb.rx_refill) { spin_unlock_bh(&target->rx_lock); ep_cb.rx_refill(ep->target, ep->eid); spin_lock_bh(&target->rx_lock); } } if (list_empty(&ep->rx_bufq)) packet = NULL; else { packet = list_first_entry(&ep->rx_bufq, struct htc_packet, list); list_del(&packet->list); } } if (!packet) { target->rx_st_flags |= HTC_RECV_WAIT_BUFFERS; target->ep_waiting = ep->eid; return -ENOSPC; } /* clear flags */ packet->info.rx.rx_flags = 0; packet->info.rx.indicat_flags = 0; packet->status = 0; if (no_recycle) /* * flag that these packets cannot be * recycled, they have to be returned to * the user */ packet->info.rx.rx_flags |= HTC_RX_PKT_NO_RECYCLE; /* Caller needs to free this upon any failure */ list_add_tail(&packet->list, queue); if (target->htc_flags & HTC_OP_STATE_STOPPING) { status = -ECANCELED; break; } if (j) { packet->info.rx.rx_flags |= HTC_RX_PKT_REFRESH_HDR; packet->info.rx.exp_hdr = 0xFFFFFFFF; } else /* set expected look ahead */ packet->info.rx.exp_hdr = *lk_ahds; packet->act_len = le16_to_cpu(htc_hdr->payld_len) + HTC_HDR_LENGTH; } return status; } static int ath6kl_htc_rx_alloc(struct htc_target *target, u32 lk_ahds[], int msg, struct htc_endpoint *endpoint, struct list_head *queue) { int status = 0; struct htc_packet *packet, *tmp_pkt; struct htc_frame_hdr *htc_hdr; int i, n_msg; spin_lock_bh(&target->rx_lock); for (i = 0; i < msg; i++) { htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i]; if (htc_hdr->eid >= ENDPOINT_MAX) { ath6kl_err("invalid ep in look-ahead: %d\n", htc_hdr->eid); status = -ENOMEM; break; } if (htc_hdr->eid != endpoint->eid) { ath6kl_err("invalid ep in look-ahead: %d should be : %d (index:%d)\n", htc_hdr->eid, endpoint->eid, i); status = -ENOMEM; break; } if (le16_to_cpu(htc_hdr->payld_len) > HTC_MAX_PAYLOAD_LENGTH) { ath6kl_err("payload len %d exceeds max htc : %d !\n", htc_hdr->payld_len, (u32) HTC_MAX_PAYLOAD_LENGTH); status = -ENOMEM; break; } if (endpoint->svc_id == 0) { ath6kl_err("ep %d is not connected !\n", htc_hdr->eid); status = -ENOMEM; break; } if (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) { /* * HTC header indicates that every packet to follow * has the same padded length so that it can be * optimally fetched as a full bundle. */ n_msg = (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) >> HTC_FLG_RX_BNDL_CNT_S; /* the count doesn't include the starter frame */ n_msg++; if (n_msg > target->msg_per_bndl_max) { status = -ENOMEM; break; } endpoint->ep_st.rx_bundle_from_hdr += 1; ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx bundle pkts %d\n", n_msg); } else /* HTC header only indicates 1 message to fetch */ n_msg = 1; /* Setup packet buffers for each message */ status = ath6kl_htc_rx_setup(target, endpoint, &lk_ahds[i], queue, n_msg); /* * This is due to unavailabilty of buffers to rx entire data. * Return no error so that free buffers from queue can be used * to receive partial data. */ if (status == -ENOSPC) { spin_unlock_bh(&target->rx_lock); return 0; } if (status) break; } spin_unlock_bh(&target->rx_lock); if (status) { list_for_each_entry_safe(packet, tmp_pkt, queue, list) { list_del(&packet->list); htc_reclaim_rxbuf(target, packet, &target->endpoint[packet->endpoint]); } } return status; } static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets) { if (packets->endpoint != ENDPOINT_0) { WARN_ON(1); return; } if (packets->status == -ECANCELED) { reclaim_rx_ctrl_buf(context, packets); return; } if (packets->act_len > 0) { ath6kl_err("htc_ctrl_rx, got message with len:%zu\n", packets->act_len + HTC_HDR_LENGTH); ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx unexpected endpoint 0 message", "", packets->buf - HTC_HDR_LENGTH, packets->act_len + HTC_HDR_LENGTH); } htc_reclaim_rxbuf(context, packets, &context->endpoint[0]); } static void htc_proc_cred_rpt(struct htc_target *target, struct htc_credit_report *rpt, int n_entries, enum htc_endpoint_id from_ep) { struct htc_endpoint *endpoint; int tot_credits = 0, i; bool dist = false; spin_lock_bh(&target->tx_lock); for (i = 0; i < n_entries; i++, rpt++) { if (rpt->eid >= ENDPOINT_MAX) { WARN_ON(1); spin_unlock_bh(&target->tx_lock); return; } endpoint = &target->endpoint[rpt->eid]; ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit report ep %d credits %d\n", rpt->eid, rpt->credits); endpoint->ep_st.tx_cred_rpt += 1; endpoint->ep_st.cred_retnd += rpt->credits; if (from_ep == rpt->eid) { /* * This credit report arrived on the same endpoint * indicating it arrived in an RX packet. */ endpoint->ep_st.cred_from_rx += rpt->credits; endpoint->ep_st.cred_rpt_from_rx += 1; } else if (from_ep == ENDPOINT_0) { /* credit arrived on endpoint 0 as a NULL message */ endpoint->ep_st.cred_from_ep0 += rpt->credits; endpoint->ep_st.cred_rpt_ep0 += 1; } else { endpoint->ep_st.cred_from_other += rpt->credits; endpoint->ep_st.cred_rpt_from_other += 1; } if (rpt->eid == ENDPOINT_0) /* always give endpoint 0 credits back */ endpoint->cred_dist.credits += rpt->credits; else { endpoint->cred_dist.cred_to_dist += rpt->credits; dist = true; } /* * Refresh tx depth for distribution function that will * recover these credits NOTE: this is only valid when * there are credits to recover! */ endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq); tot_credits += rpt->credits; } if (dist) { /* * This was a credit return based on a completed send * operations note, this is done with the lock held */ ath6kl_credit_distribute(target->credit_info, &target->cred_dist_list, HTC_CREDIT_DIST_SEND_COMPLETE); } spin_unlock_bh(&target->tx_lock); if (tot_credits) htc_chk_ep_txq(target); } static int htc_parse_trailer(struct htc_target *target, struct htc_record_hdr *record, u8 *record_buf, u32 *next_lk_ahds, enum htc_endpoint_id endpoint, int *n_lk_ahds) { struct htc_bundle_lkahd_rpt *bundle_lkahd_rpt; struct htc_lookahead_report *lk_ahd; int len; switch (record->rec_id) { case HTC_RECORD_CREDITS: len = record->len / sizeof(struct htc_credit_report); if (!len) { WARN_ON(1); return -EINVAL; } htc_proc_cred_rpt(target, (struct htc_credit_report *) record_buf, len, endpoint); break; case HTC_RECORD_LOOKAHEAD: len = record->len / sizeof(*lk_ahd); if (!len) { WARN_ON(1); return -EINVAL; } lk_ahd = (struct htc_lookahead_report *) record_buf; if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) && next_lk_ahds) { ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n", lk_ahd->pre_valid, lk_ahd->post_valid); /* look ahead bytes are valid, copy them over */ memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4); ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx next look ahead", "", next_lk_ahds, 4); *n_lk_ahds = 1; } break; case HTC_RECORD_LOOKAHEAD_BUNDLE: len = record->len / sizeof(*bundle_lkahd_rpt); if (!len || (len > HTC_HOST_MAX_MSG_PER_BUNDLE)) { WARN_ON(1); return -EINVAL; } if (next_lk_ahds) { int i; bundle_lkahd_rpt = (struct htc_bundle_lkahd_rpt *) record_buf; ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bundle lk_ahd", "", record_buf, record->len); for (i = 0; i < len; i++) { memcpy((u8 *)&next_lk_ahds[i], bundle_lkahd_rpt->lk_ahd, 4); bundle_lkahd_rpt++; } *n_lk_ahds = i; } break; default: ath6kl_err("unhandled record: id:%d len:%d\n", record->rec_id, record->len); break; } return 0; } static int htc_proc_trailer(struct htc_target *target, u8 *buf, int len, u32 *next_lk_ahds, int *n_lk_ahds, enum htc_endpoint_id endpoint) { struct htc_record_hdr *record; int orig_len; int status; u8 *record_buf; u8 *orig_buf; ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx trailer len %d\n", len); ath6kl_dbg_dump(ATH6KL_DBG_HTC, NULL, "", buf, len); orig_buf = buf; orig_len = len; status = 0; while (len > 0) { if (len < sizeof(struct htc_record_hdr)) { status = -ENOMEM; break; } /* these are byte aligned structs */ record = (struct htc_record_hdr *) buf; len -= sizeof(struct htc_record_hdr); buf += sizeof(struct htc_record_hdr); if (record->len > len) { ath6kl_err("invalid record len: %d (id:%d) buf has: %d bytes left\n", record->len, record->rec_id, len); status = -ENOMEM; break; } record_buf = buf; status = htc_parse_trailer(target, record, record_buf, next_lk_ahds, endpoint, n_lk_ahds); if (status) break; /* advance buffer past this record for next time around */ buf += record->len; len -= record->len; } if (status) ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad trailer", "", orig_buf, orig_len); return status; } static int ath6kl_htc_rx_process_hdr(struct htc_target *target, struct htc_packet *packet, u32 *next_lkahds, int *n_lkahds) { int status = 0; u16 payload_len; u32 lk_ahd; struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)packet->buf; if (n_lkahds != NULL) *n_lkahds = 0; /* * NOTE: we cannot assume the alignment of buf, so we use the safe * macros to retrieve 16 bit fields. */ payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len)); memcpy((u8 *)&lk_ahd, packet->buf, sizeof(lk_ahd)); if (packet->info.rx.rx_flags & HTC_RX_PKT_REFRESH_HDR) { /* * Refresh the expected header and the actual length as it * was unknown when this packet was grabbed as part of the * bundle. */ packet->info.rx.exp_hdr = lk_ahd; packet->act_len = payload_len + HTC_HDR_LENGTH; /* validate the actual header that was refreshed */ if (packet->act_len > packet->buf_len) { ath6kl_err("refreshed hdr payload len (%d) in bundled recv is invalid (hdr: 0x%X)\n", payload_len, lk_ahd); /* * Limit this to max buffer just to print out some * of the buffer. */ packet->act_len = min(packet->act_len, packet->buf_len); status = -ENOMEM; goto fail_rx; } if (packet->endpoint != htc_hdr->eid) { ath6kl_err("refreshed hdr ep (%d) does not match expected ep (%d)\n", htc_hdr->eid, packet->endpoint); status = -ENOMEM; goto fail_rx; } } if (lk_ahd != packet->info.rx.exp_hdr) { ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n", __func__, packet, packet->info.rx.rx_flags); ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx expected lk_ahd", "", &packet->info.rx.exp_hdr, 4); ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx current header", "", (u8 *)&lk_ahd, sizeof(lk_ahd)); status = -ENOMEM; goto fail_rx; } if (htc_hdr->flags & HTC_FLG_RX_TRAILER) { if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) || htc_hdr->ctrl[0] > payload_len) { ath6kl_err("%s(): invalid hdr (payload len should be :%d, CB[0] is:%d)\n", __func__, payload_len, htc_hdr->ctrl[0]); status = -ENOMEM; goto fail_rx; } if (packet->info.rx.rx_flags & HTC_RX_PKT_IGNORE_LOOKAHEAD) { next_lkahds = NULL; n_lkahds = NULL; } status = htc_proc_trailer(target, packet->buf + HTC_HDR_LENGTH + payload_len - htc_hdr->ctrl[0], htc_hdr->ctrl[0], next_lkahds, n_lkahds, packet->endpoint); if (status) goto fail_rx; packet->act_len -= htc_hdr->ctrl[0]; } packet->buf += HTC_HDR_LENGTH; packet->act_len -= HTC_HDR_LENGTH; fail_rx: if (status) ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad packet", "", packet->buf, packet->act_len); return status; } static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint, struct htc_packet *packet) { ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx complete ep %d packet 0x%p\n", endpoint->eid, packet); endpoint->ep_cb.rx(endpoint->target, packet); } static int ath6kl_htc_rx_bundle(struct htc_target *target, struct list_head *rxq, struct list_head *sync_compq, int *n_pkt_fetched, bool part_bundle) { struct hif_scatter_req *scat_req; struct htc_packet *packet; int rem_space = target->max_rx_bndl_sz; int n_scat_pkt, status = 0, i, len; n_scat_pkt = get_queue_depth(rxq); n_scat_pkt = min(n_scat_pkt, target->msg_per_bndl_max); if ((get_queue_depth(rxq) - n_scat_pkt) > 0) { /* * We were forced to split this bundle receive operation * all packets in this partial bundle must have their * lookaheads ignored. */ part_bundle = true; /* * This would only happen if the target ignored our max * bundle limit. */ ath6kl_warn("%s(): partial bundle detected num:%d , %d\n", __func__, get_queue_depth(rxq), n_scat_pkt); } len = 0; ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx bundle depth %d pkts %d\n", get_queue_depth(rxq), n_scat_pkt); scat_req = hif_scatter_req_get(target->dev->ar); if (scat_req == NULL) goto fail_rx_pkt; for (i = 0; i < n_scat_pkt; i++) { int pad_len; packet = list_first_entry(rxq, struct htc_packet, list); list_del(&packet->list); pad_len = CALC_TXRX_PADDED_LEN(target, packet->act_len); if ((rem_space - pad_len) < 0) { list_add(&packet->list, rxq); break; } rem_space -= pad_len; if (part_bundle || (i < (n_scat_pkt - 1))) /* * Packet 0..n-1 cannot be checked for look-aheads * since we are fetching a bundle the last packet * however can have it's lookahead used */ packet->info.rx.rx_flags |= HTC_RX_PKT_IGNORE_LOOKAHEAD; /* NOTE: 1 HTC packet per scatter entry */ scat_req->scat_list[i].buf = packet->buf; scat_req->scat_list[i].len = pad_len; packet->info.rx.rx_flags |= HTC_RX_PKT_PART_OF_BUNDLE; list_add_tail(&packet->list, sync_compq); WARN_ON(!scat_req->scat_list[i].len); len += scat_req->scat_list[i].len; } scat_req->len = len; scat_req->scat_entries = i; status = ath6kl_hif_submit_scat_req(target->dev, scat_req, true); if (!status) *n_pkt_fetched = i; /* free scatter request */ hif_scatter_req_add(target->dev->ar, scat_req); fail_rx_pkt: return status; } static int ath6kl_htc_rx_process_packets(struct htc_target *target, struct list_head *comp_pktq, u32 lk_ahds[], int *n_lk_ahd) { struct htc_packet *packet, *tmp_pkt; struct htc_endpoint *ep; int status = 0; list_for_each_entry_safe(packet, tmp_pkt, comp_pktq, list) { ep = &target->endpoint[packet->endpoint]; /* process header for each of the recv packet */ status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds, n_lk_ahd); if (status) return status; list_del(&packet->list); if (list_empty(comp_pktq)) { /* * Last packet's more packet flag is set * based on the lookahead. */ if (*n_lk_ahd > 0) ath6kl_htc_rx_set_indicate(lk_ahds[0], ep, packet); } else /* * Packets in a bundle automatically have * this flag set. */ packet->info.rx.indicat_flags |= HTC_RX_FLAGS_INDICATE_MORE_PKTS; ath6kl_htc_rx_update_stats(ep, *n_lk_ahd); if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE) ep->ep_st.rx_bundl += 1; ath6kl_htc_rx_complete(ep, packet); } return status; } static int ath6kl_htc_rx_fetch(struct htc_target *target, struct list_head *rx_pktq, struct list_head *comp_pktq) { int fetched_pkts; bool part_bundle = false; int status = 0; struct list_head tmp_rxq; struct htc_packet *packet, *tmp_pkt; /* now go fetch the list of HTC packets */ while (!list_empty(rx_pktq)) { fetched_pkts = 0; INIT_LIST_HEAD(&tmp_rxq); if (target->rx_bndl_enable && (get_queue_depth(rx_pktq) > 1)) { /* * There are enough packets to attempt a * bundle transfer and recv bundling is * allowed. */ status = ath6kl_htc_rx_bundle(target, rx_pktq, &tmp_rxq, &fetched_pkts, part_bundle); if (status) goto fail_rx; if (!list_empty(rx_pktq)) part_bundle = true; list_splice_tail_init(&tmp_rxq, comp_pktq); } if (!fetched_pkts) { packet = list_first_entry(rx_pktq, struct htc_packet, list); /* fully synchronous */ packet->completion = NULL; if (!list_is_singular(rx_pktq)) /* * look_aheads in all packet * except the last one in the * bundle must be ignored */ packet->info.rx.rx_flags |= HTC_RX_PKT_IGNORE_LOOKAHEAD; /* go fetch the packet */ status = ath6kl_htc_rx_packet(target, packet, packet->act_len); list_move_tail(&packet->list, &tmp_rxq); if (status) goto fail_rx; list_splice_tail_init(&tmp_rxq, comp_pktq); } } return 0; fail_rx: /* * Cleanup any packets we allocated but didn't use to * actually fetch any packets. */ list_for_each_entry_safe(packet, tmp_pkt, rx_pktq, list) { list_del(&packet->list); htc_reclaim_rxbuf(target, packet, &target->endpoint[packet->endpoint]); } list_for_each_entry_safe(packet, tmp_pkt, &tmp_rxq, list) { list_del(&packet->list); htc_reclaim_rxbuf(target, packet, &target->endpoint[packet->endpoint]); } return status; } int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, u32 msg_look_ahead, int *num_pkts) { struct htc_packet *packets, *tmp_pkt; struct htc_endpoint *endpoint; struct list_head rx_pktq, comp_pktq; int status = 0; u32 look_aheads[HTC_HOST_MAX_MSG_PER_BUNDLE]; int num_look_ahead = 1; enum htc_endpoint_id id; int n_fetched = 0; INIT_LIST_HEAD(&comp_pktq); *num_pkts = 0; /* * On first entry copy the look_aheads into our temp array for * processing */ look_aheads[0] = msg_look_ahead; while (true) { /* * First lookahead sets the expected endpoint IDs for all * packets in a bundle. */ id = ((struct htc_frame_hdr *)&look_aheads[0])->eid; endpoint = &target->endpoint[id]; if (id >= ENDPOINT_MAX) { ath6kl_err("MsgPend, invalid endpoint in look-ahead: %d\n", id); status = -ENOMEM; break; } INIT_LIST_HEAD(&rx_pktq); INIT_LIST_HEAD(&comp_pktq); /* * Try to allocate as many HTC RX packets indicated by the * look_aheads. */ status = ath6kl_htc_rx_alloc(target, look_aheads, num_look_ahead, endpoint, &rx_pktq); if (status) break; if (get_queue_depth(&rx_pktq) >= 2) /* * A recv bundle was detected, force IRQ status * re-check again */ target->chk_irq_status_cnt = 1; n_fetched += get_queue_depth(&rx_pktq); num_look_ahead = 0; status = ath6kl_htc_rx_fetch(target, &rx_pktq, &comp_pktq); if (!status) ath6kl_htc_rx_chk_water_mark(endpoint); /* Process fetched packets */ status = ath6kl_htc_rx_process_packets(target, &comp_pktq, look_aheads, &num_look_ahead); if (!num_look_ahead || status) break; /* * For SYNCH processing, if we get here, we are running * through the loop again due to a detected lookahead. Set * flag that we should re-check IRQ status registers again * before leaving IRQ processing, this can net better * performance in high throughput situations. */ target->chk_irq_status_cnt = 1; } if (status) { ath6kl_err("failed to get pending recv messages: %d\n", status); /* cleanup any packets in sync completion queue */ list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) { list_del(&packets->list); htc_reclaim_rxbuf(target, packets, &target->endpoint[packets->endpoint]); } if (target->htc_flags & HTC_OP_STATE_STOPPING) { ath6kl_warn("host is going to stop blocking receiver for htc_stop\n"); ath6kl_hif_rx_control(target->dev, false); } } /* * Before leaving, check to see if host ran out of buffers and * needs to stop the receiver. */ if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { ath6kl_warn("host has no rx buffers blocking receiver to prevent overrun\n"); ath6kl_hif_rx_control(target->dev, false); } *num_pkts = n_fetched; return status; } /* * Synchronously wait for a control message from the target, * This function is used at initialization time ONLY. At init messages * on ENDPOINT 0 are expected. */ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target) { struct htc_packet *packet = NULL; struct htc_frame_hdr *htc_hdr; u32 look_ahead; if (ath6kl_hif_poll_mboxmsg_rx(target->dev, &look_ahead, HTC_TARGET_RESPONSE_TIMEOUT)) return NULL; ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx wait ctrl look_ahead 0x%X\n", look_ahead); htc_hdr = (struct htc_frame_hdr *)&look_ahead; if (htc_hdr->eid != ENDPOINT_0) return NULL; packet = htc_get_control_buf(target, false); if (!packet) return NULL; packet->info.rx.rx_flags = 0; packet->info.rx.exp_hdr = look_ahead; packet->act_len = le16_to_cpu(htc_hdr->payld_len) + HTC_HDR_LENGTH; if (packet->act_len > packet->buf_len) goto fail_ctrl_rx; /* we want synchronous operation */ packet->completion = NULL; /* get the message from the device, this will block */ if (ath6kl_htc_rx_packet(target, packet, packet->act_len)) goto fail_ctrl_rx; /* process receive header */ packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL); if (packet->status) { ath6kl_err("htc_wait_for_ctrl_msg, ath6kl_htc_rx_process_hdr failed (status = %d)\n", packet->status); goto fail_ctrl_rx; } return packet; fail_ctrl_rx: if (packet != NULL) { htc_rxpkt_reset(packet); reclaim_rx_ctrl_buf(target, packet); } return NULL; } static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, struct list_head *pkt_queue) { struct htc_endpoint *endpoint; struct htc_packet *first_pkt; bool rx_unblock = false; int status = 0, depth; if (list_empty(pkt_queue)) return -ENOMEM; first_pkt = list_first_entry(pkt_queue, struct htc_packet, list); if (first_pkt->endpoint >= ENDPOINT_MAX) return status; depth = get_queue_depth(pkt_queue); ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx add multiple ep id %d cnt %d len %d\n", first_pkt->endpoint, depth, first_pkt->buf_len); endpoint = &target->endpoint[first_pkt->endpoint]; if (target->htc_flags & HTC_OP_STATE_STOPPING) { struct htc_packet *packet, *tmp_pkt; /* walk through queue and mark each one canceled */ list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { packet->status = -ECANCELED; list_del(&packet->list); ath6kl_htc_rx_complete(endpoint, packet); } return status; } spin_lock_bh(&target->rx_lock); list_splice_tail_init(pkt_queue, &endpoint->rx_bufq); /* check if we are blocked waiting for a new buffer */ if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { if (target->ep_waiting == first_pkt->endpoint) { ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx blocked on ep %d, unblocking\n", target->ep_waiting); target->rx_st_flags &= ~HTC_RECV_WAIT_BUFFERS; target->ep_waiting = ENDPOINT_MAX; rx_unblock = true; } } spin_unlock_bh(&target->rx_lock); if (rx_unblock && !(target->htc_flags & HTC_OP_STATE_STOPPING)) /* TODO : implement a buffer threshold count? */ ath6kl_hif_rx_control(target->dev, true); return status; } static void ath6kl_htc_mbox_flush_rx_buf(struct htc_target *target) { struct htc_endpoint *endpoint; struct htc_packet *packet, *tmp_pkt; int i; for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { endpoint = &target->endpoint[i]; if (!endpoint->svc_id) /* not in use.. */ continue; spin_lock_bh(&target->rx_lock); list_for_each_entry_safe(packet, tmp_pkt, &endpoint->rx_bufq, list) { list_del(&packet->list); spin_unlock_bh(&target->rx_lock); ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx flush pkt 0x%p len %d ep %d\n", packet, packet->buf_len, packet->endpoint); /* * packets in rx_bufq of endpoint 0 have originally * been queued from target->free_ctrl_rxbuf where * packet and packet->buf_start are allocated * separately using kmalloc(). For other endpoint * rx_bufq, it is allocated as skb where packet is * skb->head. Take care of this difference while freeing * the memory. */ if (packet->endpoint == ENDPOINT_0) { kfree(packet->buf_start); kfree(packet); } else { dev_kfree_skb(packet->pkt_cntxt); } spin_lock_bh(&target->rx_lock); } spin_unlock_bh(&target->rx_lock); } } static int ath6kl_htc_mbox_conn_service(struct htc_target *target, struct htc_service_connect_req *conn_req, struct htc_service_connect_resp *conn_resp) { struct htc_packet *rx_pkt = NULL; struct htc_packet *tx_pkt = NULL; struct htc_conn_service_resp *resp_msg; struct htc_conn_service_msg *conn_msg; struct htc_endpoint *endpoint; enum htc_endpoint_id assigned_ep = ENDPOINT_MAX; unsigned int max_msg_sz = 0; int status = 0; u16 msg_id; ath6kl_dbg(ATH6KL_DBG_HTC, "htc connect service target 0x%p service id 0x%x\n", target, conn_req->svc_id); if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) { /* special case for pseudo control service */ assigned_ep = ENDPOINT_0; max_msg_sz = HTC_MAX_CTRL_MSG_LEN; } else { /* allocate a packet to send to the target */ tx_pkt = htc_get_control_buf(target, true); if (!tx_pkt) return -ENOMEM; conn_msg = (struct htc_conn_service_msg *)tx_pkt->buf; memset(conn_msg, 0, sizeof(*conn_msg)); conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID); conn_msg->svc_id = cpu_to_le16(conn_req->svc_id); conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags); set_htc_pkt_info(tx_pkt, NULL, (u8 *) conn_msg, sizeof(*conn_msg) + conn_msg->svc_meta_len, ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); /* we want synchronous operation */ tx_pkt->completion = NULL; ath6kl_htc_tx_prep_pkt(tx_pkt, 0, 0, 0); status = ath6kl_htc_tx_issue(target, tx_pkt); if (status) goto fail_tx; /* wait for response */ rx_pkt = htc_wait_for_ctrl_msg(target); if (!rx_pkt) { status = -ENOMEM; goto fail_tx; } resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf; msg_id = le16_to_cpu(resp_msg->msg_id); if ((msg_id != HTC_MSG_CONN_SVC_RESP_ID) || (rx_pkt->act_len < sizeof(*resp_msg))) { status = -ENOMEM; goto fail_tx; } conn_resp->resp_code = resp_msg->status; /* check response status */ if (resp_msg->status != HTC_SERVICE_SUCCESS) { ath6kl_err("target failed service 0x%X connect request (status:%d)\n", resp_msg->svc_id, resp_msg->status); status = -ENOMEM; goto fail_tx; } assigned_ep = (enum htc_endpoint_id)resp_msg->eid; max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz); } if (assigned_ep >= ENDPOINT_MAX || !max_msg_sz) { status = -ENOMEM; goto fail_tx; } endpoint = &target->endpoint[assigned_ep]; endpoint->eid = assigned_ep; if (endpoint->svc_id) { status = -ENOMEM; goto fail_tx; } /* return assigned endpoint to caller */ conn_resp->endpoint = assigned_ep; conn_resp->len_max = max_msg_sz; /* setup the endpoint */ /* this marks the endpoint in use */ endpoint->svc_id = conn_req->svc_id; endpoint->max_txq_depth = conn_req->max_txq_depth; endpoint->len_max = max_msg_sz; endpoint->ep_cb = conn_req->ep_cb; endpoint->cred_dist.svc_id = conn_req->svc_id; endpoint->cred_dist.htc_ep = endpoint; endpoint->cred_dist.endpoint = assigned_ep; endpoint->cred_dist.cred_sz = target->tgt_cred_sz; switch (endpoint->svc_id) { case WMI_DATA_BK_SVC: endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 3; break; default: endpoint->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; break; } if (conn_req->max_rxmsg_sz) { /* * Override cred_per_msg calculation, this optimizes * the credit-low indications since the host will actually * issue smaller messages in the Send path. */ if (conn_req->max_rxmsg_sz > max_msg_sz) { status = -ENOMEM; goto fail_tx; } endpoint->cred_dist.cred_per_msg = conn_req->max_rxmsg_sz / target->tgt_cred_sz; } else endpoint->cred_dist.cred_per_msg = max_msg_sz / target->tgt_cred_sz; if (!endpoint->cred_dist.cred_per_msg) endpoint->cred_dist.cred_per_msg = 1; /* save local connection flags */ endpoint->conn_flags = conn_req->flags; fail_tx: if (tx_pkt) htc_reclaim_txctrl_buf(target, tx_pkt); if (rx_pkt) { htc_rxpkt_reset(rx_pkt); reclaim_rx_ctrl_buf(target, rx_pkt); } return status; } static void reset_ep_state(struct htc_target *target) { struct htc_endpoint *endpoint; int i; for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { endpoint = &target->endpoint[i]; memset(&endpoint->cred_dist, 0, sizeof(endpoint->cred_dist)); endpoint->svc_id = 0; endpoint->len_max = 0; endpoint->max_txq_depth = 0; memset(&endpoint->ep_st, 0, sizeof(endpoint->ep_st)); INIT_LIST_HEAD(&endpoint->rx_bufq); INIT_LIST_HEAD(&endpoint->txq); endpoint->target = target; } /* reset distribution list */ /* FIXME: free existing entries */ INIT_LIST_HEAD(&target->cred_dist_list); } static int ath6kl_htc_mbox_get_rxbuf_num(struct htc_target *target, enum htc_endpoint_id endpoint) { int num; spin_lock_bh(&target->rx_lock); num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq)); spin_unlock_bh(&target->rx_lock); return num; } static void htc_setup_msg_bndl(struct htc_target *target) { /* limit what HTC can handle */ target->msg_per_bndl_max = min(HTC_HOST_MAX_MSG_PER_BUNDLE, target->msg_per_bndl_max); if (ath6kl_hif_enable_scatter(target->dev->ar)) { target->msg_per_bndl_max = 0; return; } /* limit bundle what the device layer can handle */ target->msg_per_bndl_max = min(target->max_scat_entries, target->msg_per_bndl_max); ath6kl_dbg(ATH6KL_DBG_BOOT, "htc bundling allowed msg_per_bndl_max %d\n", target->msg_per_bndl_max); /* Max rx bundle size is limited by the max tx bundle size */ target->max_rx_bndl_sz = target->max_xfer_szper_scatreq; /* Max tx bundle size if limited by the extended mbox address range */ target->max_tx_bndl_sz = min(HIF_MBOX0_EXT_WIDTH, target->max_xfer_szper_scatreq); ath6kl_dbg(ATH6KL_DBG_BOOT, "htc max_rx_bndl_sz %d max_tx_bndl_sz %d\n", target->max_rx_bndl_sz, target->max_tx_bndl_sz); if (target->max_tx_bndl_sz) /* tx_bndl_mask is enabled per AC, each has 1 bit */ target->tx_bndl_mask = (1 << WMM_NUM_AC) - 1; if (target->max_rx_bndl_sz) target->rx_bndl_enable = true; if ((target->tgt_cred_sz % target->block_sz) != 0) { ath6kl_warn("credit size: %d is not block aligned! Disabling send bundling\n", target->tgt_cred_sz); /* * Disallow send bundling since the credit size is * not aligned to a block size the I/O block * padding will spill into the next credit buffer * which is fatal. */ target->tx_bndl_mask = 0; } } static int ath6kl_htc_mbox_wait_target(struct htc_target *target) { struct htc_packet *packet = NULL; struct htc_ready_ext_msg *rdy_msg; struct htc_service_connect_req connect; struct htc_service_connect_resp resp; int status; /* FIXME: remove once USB support is implemented */ if (target->dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) { ath6kl_err("HTC doesn't support USB yet. Patience!\n"); return -EOPNOTSUPP; } /* we should be getting 1 control message that the target is ready */ packet = htc_wait_for_ctrl_msg(target); if (!packet) return -ENOMEM; /* we controlled the buffer creation so it's properly aligned */ rdy_msg = (struct htc_ready_ext_msg *)packet->buf; if ((le16_to_cpu(rdy_msg->ver2_0_info.msg_id) != HTC_MSG_READY_ID) || (packet->act_len < sizeof(struct htc_ready_msg))) { status = -ENOMEM; goto fail_wait_target; } if (!rdy_msg->ver2_0_info.cred_cnt || !rdy_msg->ver2_0_info.cred_sz) { status = -ENOMEM; goto fail_wait_target; } target->tgt_creds = le16_to_cpu(rdy_msg->ver2_0_info.cred_cnt); target->tgt_cred_sz = le16_to_cpu(rdy_msg->ver2_0_info.cred_sz); ath6kl_dbg(ATH6KL_DBG_BOOT, "htc target ready credits %d size %d\n", target->tgt_creds, target->tgt_cred_sz); /* check if this is an extended ready message */ if (packet->act_len >= sizeof(struct htc_ready_ext_msg)) { /* this is an extended message */ target->htc_tgt_ver = rdy_msg->htc_ver; target->msg_per_bndl_max = rdy_msg->msg_per_htc_bndl; } else { /* legacy */ target->htc_tgt_ver = HTC_VERSION_2P0; target->msg_per_bndl_max = 0; } ath6kl_dbg(ATH6KL_DBG_BOOT, "htc using protocol %s (%d)\n", (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1", target->htc_tgt_ver); if (target->msg_per_bndl_max > 0) htc_setup_msg_bndl(target); /* setup our pseudo HTC control endpoint connection */ memset(&connect, 0, sizeof(connect)); memset(&resp, 0, sizeof(resp)); connect.ep_cb.rx = htc_ctrl_rx; connect.ep_cb.rx_refill = NULL; connect.ep_cb.tx_full = NULL; connect.max_txq_depth = NUM_CONTROL_BUFFERS; connect.svc_id = HTC_CTRL_RSVD_SVC; /* connect fake service */ status = ath6kl_htc_mbox_conn_service((void *)target, &connect, &resp); if (status) /* * FIXME: this call doesn't make sense, the caller should * call ath6kl_htc_mbox_cleanup() when it wants remove htc */ ath6kl_hif_cleanup_scatter(target->dev->ar); fail_wait_target: if (packet) { htc_rxpkt_reset(packet); reclaim_rx_ctrl_buf(target, packet); } return status; } /* * Start HTC, enable interrupts and let the target know * host has finished setup. */ static int ath6kl_htc_mbox_start(struct htc_target *target) { struct htc_packet *packet; int status; memset(&target->dev->irq_proc_reg, 0, sizeof(target->dev->irq_proc_reg)); /* Disable interrupts at the chip level */ ath6kl_hif_disable_intrs(target->dev); target->htc_flags = 0; target->rx_st_flags = 0; /* Push control receive buffers into htc control endpoint */ while ((packet = htc_get_control_buf(target, false)) != NULL) { status = htc_add_rxbuf(target, packet); if (status) return status; } /* NOTE: the first entry in the distribution list is ENDPOINT_0 */ ath6kl_credit_init(target->credit_info, &target->cred_dist_list, target->tgt_creds); dump_cred_dist_stats(target); /* Indicate to the target of the setup completion */ status = htc_setup_tx_complete(target); if (status) return status; /* unmask interrupts */ status = ath6kl_hif_unmask_intrs(target->dev); if (status) ath6kl_htc_mbox_stop(target); return status; } static int ath6kl_htc_reset(struct htc_target *target) { u32 block_size, ctrl_bufsz; struct htc_packet *packet; int i; reset_ep_state(target); block_size = target->dev->ar->mbox_info.block_size; ctrl_bufsz = (block_size > HTC_MAX_CTRL_MSG_LEN) ? (block_size + HTC_HDR_LENGTH) : (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH); for (i = 0; i < NUM_CONTROL_BUFFERS; i++) { packet = kzalloc(sizeof(*packet), GFP_KERNEL); if (!packet) return -ENOMEM; packet->buf_start = kzalloc(ctrl_bufsz, GFP_KERNEL); if (!packet->buf_start) { kfree(packet); return -ENOMEM; } packet->buf_len = ctrl_bufsz; if (i < NUM_CONTROL_RX_BUFFERS) { packet->act_len = 0; packet->buf = packet->buf_start; packet->endpoint = ENDPOINT_0; list_add_tail(&packet->list, &target->free_ctrl_rxbuf); } else list_add_tail(&packet->list, &target->free_ctrl_txbuf); } return 0; } /* htc_stop: stop interrupt reception, and flush all queued buffers */ static void ath6kl_htc_mbox_stop(struct htc_target *target) { spin_lock_bh(&target->htc_lock); target->htc_flags |= HTC_OP_STATE_STOPPING; spin_unlock_bh(&target->htc_lock); /* * Masking interrupts is a synchronous operation, when this * function returns all pending HIF I/O has completed, we can * safely flush the queues. */ ath6kl_hif_mask_intrs(target->dev); ath6kl_htc_flush_txep_all(target); ath6kl_htc_mbox_flush_rx_buf(target); ath6kl_htc_reset(target); } static void *ath6kl_htc_mbox_create(struct ath6kl *ar) { struct htc_target *target = NULL; int status = 0; target = kzalloc(sizeof(*target), GFP_KERNEL); if (!target) { ath6kl_err("unable to allocate memory\n"); return NULL; } target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL); if (!target->dev) { ath6kl_err("unable to allocate memory\n"); status = -ENOMEM; goto err_htc_cleanup; } spin_lock_init(&target->htc_lock); spin_lock_init(&target->rx_lock); spin_lock_init(&target->tx_lock); INIT_LIST_HEAD(&target->free_ctrl_txbuf); INIT_LIST_HEAD(&target->free_ctrl_rxbuf); INIT_LIST_HEAD(&target->cred_dist_list); target->dev->ar = ar; target->dev->htc_cnxt = target; target->ep_waiting = ENDPOINT_MAX; status = ath6kl_hif_setup(target->dev); if (status) goto err_htc_cleanup; status = ath6kl_htc_reset(target); if (status) goto err_htc_cleanup; return target; err_htc_cleanup: ath6kl_htc_mbox_cleanup(target); return NULL; } /* cleanup the HTC instance */ static void ath6kl_htc_mbox_cleanup(struct htc_target *target) { struct htc_packet *packet, *tmp_packet; /* FIXME: remove check once USB support is implemented */ if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB) ath6kl_hif_cleanup_scatter(target->dev->ar); list_for_each_entry_safe(packet, tmp_packet, &target->free_ctrl_txbuf, list) { list_del(&packet->list); kfree(packet->buf_start); kfree(packet); } list_for_each_entry_safe(packet, tmp_packet, &target->free_ctrl_rxbuf, list) { list_del(&packet->list); kfree(packet->buf_start); kfree(packet); } kfree(target->dev); kfree(target); } static const struct ath6kl_htc_ops ath6kl_htc_mbox_ops = { .create = ath6kl_htc_mbox_create, .wait_target = ath6kl_htc_mbox_wait_target, .start = ath6kl_htc_mbox_start, .conn_service = ath6kl_htc_mbox_conn_service, .tx = ath6kl_htc_mbox_tx, .stop = ath6kl_htc_mbox_stop, .cleanup = ath6kl_htc_mbox_cleanup, .flush_txep = ath6kl_htc_mbox_flush_txep, .flush_rx_buf = ath6kl_htc_mbox_flush_rx_buf, .activity_changed = ath6kl_htc_mbox_activity_changed, .get_rxbuf_num = ath6kl_htc_mbox_get_rxbuf_num, .add_rxbuf_multiple = ath6kl_htc_mbox_add_rxbuf_multiple, .credit_setup = ath6kl_htc_mbox_credit_setup, }; void ath6kl_htc_mbox_attach(struct ath6kl *ar) { ar->htc_ops = &ath6kl_htc_mbox_ops; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/htc.h0000644000175000017500000004145412026211315023365 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HTC_H #define HTC_H #include "common.h" /* frame header flags */ /* send direction */ #define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0) #define HTC_FLAGS_SEND_BUNDLE (1 << 1) #define HTC_FLAGS_TX_FIXUP_NETBUF (1 << 2) /* receive direction */ #define HTC_FLG_RX_UNUSED (1 << 0) #define HTC_FLG_RX_TRAILER (1 << 1) /* Bundle count maske and shift */ #define HTC_FLG_RX_BNDL_CNT (0xF0) #define HTC_FLG_RX_BNDL_CNT_S 4 #define HTC_HDR_LENGTH (sizeof(struct htc_frame_hdr)) #define HTC_MAX_PAYLOAD_LENGTH (4096 - sizeof(struct htc_frame_hdr)) /* HTC control message IDs */ #define HTC_MSG_READY_ID 1 #define HTC_MSG_CONN_SVC_ID 2 #define HTC_MSG_CONN_SVC_RESP_ID 3 #define HTC_MSG_SETUP_COMPLETE_ID 4 #define HTC_MSG_SETUP_COMPLETE_EX_ID 5 #define HTC_MAX_CTRL_MSG_LEN 256 #define HTC_VERSION_2P0 0x00 #define HTC_VERSION_2P1 0x01 #define HTC_SERVICE_META_DATA_MAX_LENGTH 128 #define HTC_CONN_FLGS_THRESH_LVL_QUAT 0x0 #define HTC_CONN_FLGS_THRESH_LVL_HALF 0x1 #define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2 #define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4 #define HTC_CONN_FLGS_THRESH_MASK 0x3 /* disable credit flow control on a specific service */ #define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3) #define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8 #define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00 /* connect response status codes */ #define HTC_SERVICE_SUCCESS 0 #define HTC_SERVICE_NOT_FOUND 1 #define HTC_SERVICE_FAILED 2 /* no resources (i.e. no more endpoints) */ #define HTC_SERVICE_NO_RESOURCES 3 /* specific service is not allowing any more endpoints */ #define HTC_SERVICE_NO_MORE_EP 4 /* report record IDs */ #define HTC_RECORD_NULL 0 #define HTC_RECORD_CREDITS 1 #define HTC_RECORD_LOOKAHEAD 2 #define HTC_RECORD_LOOKAHEAD_BUNDLE 3 #define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0) #define HTC_SETUP_COMP_FLG_DISABLE_TX_CREDIT_FLOW (1 << 1) #define MAKE_SERVICE_ID(group, index) \ (int)(((int)group << 8) | (int)(index)) /* NOTE: service ID of 0x0000 is reserved and should never be used */ #define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1) #define WMI_CONTROL_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0) #define WMI_DATA_BE_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1) #define WMI_DATA_BK_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2) #define WMI_DATA_VI_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3) #define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4) #define WMI_MAX_SERVICES 5 #define WMM_NUM_AC 4 /* reserved and used to flush ALL packets */ #define HTC_TX_PACKET_TAG_ALL 0 #define HTC_SERVICE_TX_PACKET_TAG 1 #define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_SERVICE_TX_PACKET_TAG + 9) /* more packets on this endpoint are being fetched */ #define HTC_RX_FLAGS_INDICATE_MORE_PKTS (1 << 0) /* TODO.. for BMI */ #define ENDPOINT1 0 /* TODO -remove me, but we have to fix BMI first */ #define HTC_MAILBOX_NUM_MAX 4 /* enable send bundle padding for this endpoint */ #define HTC_FLGS_TX_BNDL_PAD_EN (1 << 0) #define HTC_EP_ACTIVE ((u32) (1u << 31)) /* HTC operational parameters */ #define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */ #define HTC_TARGET_RESPONSE_POLL_WAIT 10 #define HTC_TARGET_RESPONSE_POLL_COUNT 200 #define HTC_TARGET_DEBUG_INTR_MASK 0x01 #define HTC_TARGET_CREDIT_INTR_MASK 0xF0 #define HTC_HOST_MAX_MSG_PER_BUNDLE 8 #define HTC_MIN_HTC_MSGS_TO_BUNDLE 2 /* packet flags */ #define HTC_RX_PKT_IGNORE_LOOKAHEAD (1 << 0) #define HTC_RX_PKT_REFRESH_HDR (1 << 1) #define HTC_RX_PKT_PART_OF_BUNDLE (1 << 2) #define HTC_RX_PKT_NO_RECYCLE (1 << 3) #define NUM_CONTROL_BUFFERS 8 #define NUM_CONTROL_TX_BUFFERS 2 #define NUM_CONTROL_RX_BUFFERS (NUM_CONTROL_BUFFERS - NUM_CONTROL_TX_BUFFERS) #define HTC_RECV_WAIT_BUFFERS (1 << 0) #define HTC_OP_STATE_STOPPING (1 << 0) #define HTC_OP_STATE_SETUP_COMPLETE (1 << 1) /* * The frame header length and message formats defined herein were selected * to accommodate optimal alignment for target processing. This reduces * code size and improves performance. Any changes to the header length may * alter the alignment and cause exceptions on the target. When adding to * the messagestructures insure that fields are properly aligned. */ /* HTC frame header * * NOTE: do not remove or re-arrange the fields, these are minimally * required to take advantage of 4-byte lookaheads in some hardware * implementations. */ struct htc_frame_hdr { u8 eid; u8 flags; /* length of data (including trailer) that follows the header */ __le16 payld_len; /* end of 4-byte lookahead */ u8 ctrl[2]; } __packed; /* HTC ready message */ struct htc_ready_msg { __le16 msg_id; __le16 cred_cnt; __le16 cred_sz; u8 max_ep; u8 pad; } __packed; /* extended HTC ready message */ struct htc_ready_ext_msg { struct htc_ready_msg ver2_0_info; u8 htc_ver; u8 msg_per_htc_bndl; } __packed; /* connect service */ struct htc_conn_service_msg { __le16 msg_id; __le16 svc_id; __le16 conn_flags; u8 svc_meta_len; u8 pad; } __packed; /* connect response */ struct htc_conn_service_resp { __le16 msg_id; __le16 svc_id; u8 status; u8 eid; __le16 max_msg_sz; u8 svc_meta_len; u8 pad; } __packed; struct htc_setup_comp_msg { __le16 msg_id; } __packed; /* extended setup completion message */ struct htc_setup_comp_ext_msg { __le16 msg_id; __le32 flags; u8 msg_per_rxbndl; u8 Rsvd[3]; } __packed; struct htc_record_hdr { u8 rec_id; u8 len; } __packed; struct htc_credit_report { u8 eid; u8 credits; } __packed; /* * NOTE: The lk_ahd array is guarded by a pre_valid * and Post Valid guard bytes. The pre_valid bytes must * equal the inverse of the post_valid byte. */ struct htc_lookahead_report { u8 pre_valid; u8 lk_ahd[4]; u8 post_valid; } __packed; struct htc_bundle_lkahd_rpt { u8 lk_ahd[4]; } __packed; /* Current service IDs */ enum htc_service_grp_ids { RSVD_SERVICE_GROUP = 0, WMI_SERVICE_GROUP = 1, HTC_TEST_GROUP = 254, HTC_SERVICE_GROUP_LAST = 255 }; /* ------ endpoint IDS ------ */ enum htc_endpoint_id { ENDPOINT_UNUSED = -1, ENDPOINT_0 = 0, ENDPOINT_1 = 1, ENDPOINT_2 = 2, ENDPOINT_3, ENDPOINT_4, ENDPOINT_5, ENDPOINT_6, ENDPOINT_7, ENDPOINT_8, ENDPOINT_MAX, }; struct htc_tx_packet_info { u16 tag; int cred_used; u8 flags; int seqno; }; struct htc_rx_packet_info { u32 exp_hdr; u32 rx_flags; u32 indicat_flags; }; struct htc_target; /* wrapper around endpoint-specific packets */ struct htc_packet { struct list_head list; /* caller's per packet specific context */ void *pkt_cntxt; /* * the true buffer start , the caller can store the real * buffer start here. In receive callbacks, the HTC layer * sets buf to the start of the payload past the header. * This field allows the caller to reset buf when it recycles * receive packets back to HTC. */ u8 *buf_start; /* * Pointer to the start of the buffer. In the transmit * direction this points to the start of the payload. In the * receive direction, however, the buffer when queued up * points to the start of the HTC header but when returned * to the caller points to the start of the payload */ u8 *buf; u32 buf_len; /* actual length of payload */ u32 act_len; /* endpoint that this packet was sent/recv'd from */ enum htc_endpoint_id endpoint; /* completion status */ int status; union { struct htc_tx_packet_info tx; struct htc_rx_packet_info rx; } info; void (*completion) (struct htc_target *, struct htc_packet *); struct htc_target *context; /* * optimization for network-oriented data, the HTC packet * can pass the network buffer corresponding to the HTC packet * lower layers may optimized the transfer knowing this is * a network buffer */ struct sk_buff *skb; }; enum htc_send_full_action { HTC_SEND_FULL_KEEP = 0, HTC_SEND_FULL_DROP = 1, }; struct htc_ep_callbacks { void (*tx_complete) (struct htc_target *, struct htc_packet *); void (*rx) (struct htc_target *, struct htc_packet *); void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint); enum htc_send_full_action (*tx_full) (struct htc_target *, struct htc_packet *); struct htc_packet *(*rx_allocthresh) (struct htc_target *, enum htc_endpoint_id, int); void (*tx_comp_multi) (struct htc_target *, struct list_head *); int rx_alloc_thresh; int rx_refill_thresh; }; /* service connection information */ struct htc_service_connect_req { u16 svc_id; u16 conn_flags; struct htc_ep_callbacks ep_cb; int max_txq_depth; u32 flags; unsigned int max_rxmsg_sz; }; /* service connection response information */ struct htc_service_connect_resp { u8 buf_len; u8 act_len; enum htc_endpoint_id endpoint; unsigned int len_max; u8 resp_code; }; /* endpoint distributionstructure */ struct htc_endpoint_credit_dist { struct list_head list; /* Service ID (set by HTC) */ u16 svc_id; /* endpoint for this distributionstruct (set by HTC) */ enum htc_endpoint_id endpoint; u32 dist_flags; /* * credits for normal operation, anything above this * indicates the endpoint is over-subscribed. */ int cred_norm; /* floor for credit distribution */ int cred_min; int cred_assngd; /* current credits available */ int credits; /* * pending credits to distribute on this endpoint, this * is set by HTC when credit reports arrive. The credit * distribution functions sets this to zero when it distributes * the credits. */ int cred_to_dist; /* * the number of credits that the current pending TX packet needs * to transmit. This is set by HTC when endpoint needs credits in * order to transmit. */ int seek_cred; /* size in bytes of each credit */ int cred_sz; /* credits required for a maximum sized messages */ int cred_per_msg; /* reserved for HTC use */ struct htc_endpoint *htc_ep; /* * current depth of TX queue , i.e. messages waiting for credits * This field is valid only when HTC_CREDIT_DIST_ACTIVITY_CHANGE * or HTC_CREDIT_DIST_SEND_COMPLETE is indicated on an endpoint * that has non-zero credits to recover. */ int txq_depth; }; /* * credit distibution code that is passed into the distrbution function, * there are mandatory and optional codes that must be handled */ enum htc_credit_dist_reason { HTC_CREDIT_DIST_SEND_COMPLETE = 0, HTC_CREDIT_DIST_ACTIVITY_CHANGE = 1, HTC_CREDIT_DIST_SEEK_CREDITS, }; struct ath6kl_htc_credit_info { int total_avail_credits; int cur_free_credits; /* list of lowest priority endpoints */ struct list_head lowestpri_ep_dist; }; /* endpoint statistics */ struct htc_endpoint_stats { /* * number of times the host set the credit-low flag in a send * message on this endpoint */ u32 cred_low_indicate; u32 tx_issued; u32 tx_pkt_bundled; u32 tx_bundles; u32 tx_dropped; /* running count of total credit reports received for this endpoint */ u32 tx_cred_rpt; /* credit reports received from this endpoint's RX packets */ u32 cred_rpt_from_rx; /* credit reports received from RX packets of other endpoints */ u32 cred_rpt_from_other; /* credit reports received from endpoint 0 RX packets */ u32 cred_rpt_ep0; /* count of credits received via Rx packets on this endpoint */ u32 cred_from_rx; /* count of credits received via another endpoint */ u32 cred_from_other; /* count of credits received via another endpoint */ u32 cred_from_ep0; /* count of consummed credits */ u32 cred_cosumd; /* count of credits returned */ u32 cred_retnd; u32 rx_pkts; /* count of lookahead records found in Rx msg */ u32 rx_lkahds; /* count of recv packets received in a bundle */ u32 rx_bundl; /* count of number of bundled lookaheads */ u32 rx_bundle_lkahd; /* count of the number of bundle indications from the HTC header */ u32 rx_bundle_from_hdr; /* the number of times the recv allocation threshold was hit */ u32 rx_alloc_thresh_hit; /* total number of bytes */ u32 rxalloc_thresh_byte; }; struct htc_endpoint { enum htc_endpoint_id eid; u16 svc_id; struct list_head txq; struct list_head rx_bufq; struct htc_endpoint_credit_dist cred_dist; struct htc_ep_callbacks ep_cb; int max_txq_depth; int len_max; int tx_proc_cnt; int rx_proc_cnt; struct htc_target *target; u8 seqno; u32 conn_flags; struct htc_endpoint_stats ep_st; u16 tx_drop_packet_threshold; struct { u8 pipeid_ul; u8 pipeid_dl; struct list_head tx_lookup_queue; bool tx_credit_flow_enabled; } pipe; }; struct htc_control_buffer { struct htc_packet packet; u8 *buf; }; struct htc_pipe_txcredit_alloc { u16 service_id; u8 credit_alloc; }; enum htc_send_queue_result { HTC_SEND_QUEUE_OK = 0, /* packet was queued */ HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */ }; struct ath6kl_htc_ops { void* (*create)(struct ath6kl *ar); int (*wait_target)(struct htc_target *target); int (*start)(struct htc_target *target); int (*conn_service)(struct htc_target *target, struct htc_service_connect_req *req, struct htc_service_connect_resp *resp); int (*tx)(struct htc_target *target, struct htc_packet *packet); void (*stop)(struct htc_target *target); void (*cleanup)(struct htc_target *target); void (*flush_txep)(struct htc_target *target, enum htc_endpoint_id endpoint, u16 tag); void (*flush_rx_buf)(struct htc_target *target); void (*activity_changed)(struct htc_target *target, enum htc_endpoint_id endpoint, bool active); int (*get_rxbuf_num)(struct htc_target *target, enum htc_endpoint_id endpoint); int (*add_rxbuf_multiple)(struct htc_target *target, struct list_head *pktq); int (*credit_setup)(struct htc_target *target, struct ath6kl_htc_credit_info *cred_info); int (*tx_complete)(struct ath6kl *ar, struct sk_buff *skb); int (*rx_complete)(struct ath6kl *ar, struct sk_buff *skb, u8 pipe); }; struct ath6kl_device; /* our HTC target state */ struct htc_target { struct htc_endpoint endpoint[ENDPOINT_MAX]; /* contains struct htc_endpoint_credit_dist */ struct list_head cred_dist_list; struct list_head free_ctrl_txbuf; struct list_head free_ctrl_rxbuf; struct ath6kl_htc_credit_info *credit_info; int tgt_creds; unsigned int tgt_cred_sz; /* protects free_ctrl_txbuf and free_ctrl_rxbuf */ spinlock_t htc_lock; /* FIXME: does this protext rx_bufq and endpoint structures or what? */ spinlock_t rx_lock; /* protects endpoint->txq */ spinlock_t tx_lock; struct ath6kl_device *dev; u32 htc_flags; u32 rx_st_flags; enum htc_endpoint_id ep_waiting; u8 htc_tgt_ver; /* max messages per bundle for HTC */ int msg_per_bndl_max; u32 tx_bndl_mask; int rx_bndl_enable; int max_rx_bndl_sz; int max_tx_bndl_sz; u32 block_sz; u32 block_mask; int max_scat_entries; int max_xfer_szper_scatreq; int chk_irq_status_cnt; /* counts the number of Tx without bundling continously per AC */ u32 ac_tx_count[WMM_NUM_AC]; struct { struct htc_packet *htc_packet_pool; u8 ctrl_response_buf[HTC_MAX_CTRL_MSG_LEN]; int ctrl_response_len; bool ctrl_response_valid; struct htc_pipe_txcredit_alloc txcredit_alloc[ENDPOINT_MAX]; } pipe; }; int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, u32 msg_look_ahead, int *n_pkts); static inline void set_htc_pkt_info(struct htc_packet *packet, void *context, u8 *buf, unsigned int len, enum htc_endpoint_id eid, u16 tag) { packet->pkt_cntxt = context; packet->buf = buf; packet->act_len = len; packet->endpoint = eid; packet->info.tx.tag = tag; } static inline void htc_rxpkt_reset(struct htc_packet *packet) { packet->buf = packet->buf_start; packet->act_len = 0; } static inline void set_htc_rxpkt_info(struct htc_packet *packet, void *context, u8 *buf, unsigned long len, enum htc_endpoint_id eid) { packet->pkt_cntxt = context; packet->buf = buf; packet->buf_start = buf; packet->buf_len = len; packet->endpoint = eid; } static inline int get_queue_depth(struct list_head *queue) { struct list_head *tmp_list; int depth = 0; list_for_each(tmp_list, queue) depth++; return depth; } void ath6kl_htc_pipe_attach(struct ath6kl *ar); void ath6kl_htc_mbox_attach(struct ath6kl *ar); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/hif-ops.h0000644000175000017500000001172512026211315024152 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HIF_OPS_H #define HIF_OPS_H #include "hif.h" #include "debug.h" static inline int hif_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, u32 len, u32 request) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif %s sync addr 0x%x buf 0x%p len %d request 0x%x\n", (request & HIF_WRITE) ? "write" : "read", addr, buf, len, request); return ar->hif_ops->read_write_sync(ar, addr, buf, len, request); } static inline int hif_write_async(struct ath6kl *ar, u32 address, u8 *buffer, u32 length, u32 request, struct htc_packet *packet) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif write async addr 0x%x buf 0x%p len %d request 0x%x\n", address, buffer, length, request); return ar->hif_ops->write_async(ar, address, buffer, length, request, packet); } static inline void ath6kl_hif_irq_enable(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif irq enable\n"); return ar->hif_ops->irq_enable(ar); } static inline void ath6kl_hif_irq_disable(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif irq disable\n"); return ar->hif_ops->irq_disable(ar); } static inline struct hif_scatter_req *hif_scatter_req_get(struct ath6kl *ar) { return ar->hif_ops->scatter_req_get(ar); } static inline void hif_scatter_req_add(struct ath6kl *ar, struct hif_scatter_req *s_req) { return ar->hif_ops->scatter_req_add(ar, s_req); } static inline int ath6kl_hif_enable_scatter(struct ath6kl *ar) { return ar->hif_ops->enable_scatter(ar); } static inline int ath6kl_hif_scat_req_rw(struct ath6kl *ar, struct hif_scatter_req *scat_req) { return ar->hif_ops->scat_req_rw(ar, scat_req); } static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar) { return ar->hif_ops->cleanup_scatter(ar); } static inline int ath6kl_hif_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif suspend\n"); return ar->hif_ops->suspend(ar, wow); } /* * Read from the ATH6KL through its diagnostic window. No cooperation from * the Target is required for this. */ static inline int ath6kl_hif_diag_read32(struct ath6kl *ar, u32 address, u32 *value) { return ar->hif_ops->diag_read32(ar, address, value); } /* * Write to the ATH6KL through its diagnostic window. No cooperation from * the Target is required for this. */ static inline int ath6kl_hif_diag_write32(struct ath6kl *ar, u32 address, __le32 value) { return ar->hif_ops->diag_write32(ar, address, value); } static inline int ath6kl_hif_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) { return ar->hif_ops->bmi_read(ar, buf, len); } static inline int ath6kl_hif_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) { return ar->hif_ops->bmi_write(ar, buf, len); } static inline int ath6kl_hif_resume(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif resume\n"); return ar->hif_ops->resume(ar); } static inline int ath6kl_hif_power_on(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif power on\n"); return ar->hif_ops->power_on(ar); } static inline int ath6kl_hif_power_off(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif power off\n"); return ar->hif_ops->power_off(ar); } static inline void ath6kl_hif_stop(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif stop\n"); ar->hif_ops->stop(ar); } static inline int ath6kl_hif_pipe_send(struct ath6kl *ar, u8 pipe, struct sk_buff *hdr_buf, struct sk_buff *buf) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe send\n"); return ar->hif_ops->pipe_send(ar, pipe, hdr_buf, buf); } static inline void ath6kl_hif_pipe_get_default(struct ath6kl *ar, u8 *ul_pipe, u8 *dl_pipe) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); ar->hif_ops->pipe_get_default(ar, ul_pipe, dl_pipe); } static inline int ath6kl_hif_pipe_map_service(struct ath6kl *ar, u16 service_id, u8 *ul_pipe, u8 *dl_pipe) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); return ar->hif_ops->pipe_map_service(ar, service_id, ul_pipe, dl_pipe); } static inline u16 ath6kl_hif_pipe_get_free_queue_number(struct ath6kl *ar, u8 pipe) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get free queue number\n"); return ar->hif_ops->pipe_get_free_queue_number(ar, pipe); } #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/hif.h0000644000175000017500000002221512026211315023347 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HIF_H #define HIF_H #include "common.h" #include "core.h" #include #define BUS_REQUEST_MAX_NUM 64 #define HIF_MBOX_BLOCK_SIZE 128 #define HIF_MBOX0_BLOCK_SIZE 1 #define HIF_DMA_BUFFER_SIZE (32 * 1024) #define CMD53_FIXED_ADDRESS 1 #define CMD53_INCR_ADDRESS 2 #define MAX_SCATTER_REQUESTS 4 #define MAX_SCATTER_ENTRIES_PER_REQ 16 #define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024) #define MANUFACTURER_ID_AR6003_BASE 0x300 #define MANUFACTURER_ID_AR6004_BASE 0x400 /* SDIO manufacturer ID and Codes */ #define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00 #define MANUFACTURER_CODE 0x271 /* Atheros */ /* Mailbox address in SDIO address space */ #define HIF_MBOX_BASE_ADDR 0x800 #define HIF_MBOX_WIDTH 0x800 #define HIF_MBOX_END_ADDR (HTC_MAILBOX_NUM_MAX * HIF_MBOX_WIDTH - 1) /* version 1 of the chip has only a 12K extended mbox range */ #define HIF_MBOX0_EXT_BASE_ADDR 0x4000 #define HIF_MBOX0_EXT_WIDTH (12*1024) /* GMBOX addresses */ #define HIF_GMBOX_BASE_ADDR 0x7000 #define HIF_GMBOX_WIDTH 0x4000 /* interrupt mode register */ #define CCCR_SDIO_IRQ_MODE_REG 0xF0 /* mode to enable special 4-bit interrupt assertion without clock */ #define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ (1 << 0) /* HTC runs over mailbox 0 */ #define HTC_MAILBOX 0 #define ATH6KL_TARGET_DEBUG_INTR_MASK 0x01 /* FIXME: are these duplicates with MAX_SCATTER_ values in hif.h? */ #define ATH6KL_SCATTER_ENTRIES_PER_REQ 16 #define ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER (16 * 1024) #define ATH6KL_SCATTER_REQS 4 #define ATH6KL_HIF_COMMUNICATION_TIMEOUT 1000 struct bus_request { struct list_head list; /* request data */ u32 address; u8 *buffer; u32 length; u32 request; struct htc_packet *packet; int status; /* this is a scatter request */ struct hif_scatter_req *scat_req; }; /* direction of transfer (read/write) */ #define HIF_READ 0x00000001 #define HIF_WRITE 0x00000002 #define HIF_DIR_MASK (HIF_READ | HIF_WRITE) /* * emode - This indicates the whether the command is to be executed in a * blocking or non-blocking fashion (HIF_SYNCHRONOUS/ * HIF_ASYNCHRONOUS). The read/write data paths in HTC have been * implemented using the asynchronous mode allowing the the bus * driver to indicate the completion of operation through the * registered callback routine. The requirement primarily comes * from the contexts these operations get called from (a driver's * transmit context or the ISR context in case of receive). * Support for both of these modes is essential. */ #define HIF_SYNCHRONOUS 0x00000010 #define HIF_ASYNCHRONOUS 0x00000020 #define HIF_EMODE_MASK (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS) /* * dmode - An interface may support different kinds of commands based on * the tradeoff between the amount of data it can carry and the * setup time. Byte and Block modes are supported (HIF_BYTE_BASIS/ * HIF_BLOCK_BASIS). In case of latter, the data is rounded off * to the nearest block size by padding. The size of the block is * configurable at compile time using the HIF_BLOCK_SIZE and is * negotiated with the target during initialization after the * ATH6KL interrupts are enabled. */ #define HIF_BYTE_BASIS 0x00000040 #define HIF_BLOCK_BASIS 0x00000080 #define HIF_DMODE_MASK (HIF_BYTE_BASIS | HIF_BLOCK_BASIS) /* * amode - This indicates if the address has to be incremented on ATH6KL * after every read/write operation (HIF?FIXED_ADDRESS/ * HIF_INCREMENTAL_ADDRESS). */ #define HIF_FIXED_ADDRESS 0x00000100 #define HIF_INCREMENTAL_ADDRESS 0x00000200 #define HIF_AMODE_MASK (HIF_FIXED_ADDRESS | HIF_INCREMENTAL_ADDRESS) #define HIF_WR_ASYNC_BYTE_INC \ (HIF_WRITE | HIF_ASYNCHRONOUS | \ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS) #define HIF_WR_ASYNC_BLOCK_INC \ (HIF_WRITE | HIF_ASYNCHRONOUS | \ HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS) #define HIF_WR_SYNC_BYTE_FIX \ (HIF_WRITE | HIF_SYNCHRONOUS | \ HIF_BYTE_BASIS | HIF_FIXED_ADDRESS) #define HIF_WR_SYNC_BYTE_INC \ (HIF_WRITE | HIF_SYNCHRONOUS | \ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS) #define HIF_WR_SYNC_BLOCK_INC \ (HIF_WRITE | HIF_SYNCHRONOUS | \ HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS) #define HIF_RD_SYNC_BYTE_INC \ (HIF_READ | HIF_SYNCHRONOUS | \ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS) #define HIF_RD_SYNC_BYTE_FIX \ (HIF_READ | HIF_SYNCHRONOUS | \ HIF_BYTE_BASIS | HIF_FIXED_ADDRESS) #define HIF_RD_ASYNC_BLOCK_FIX \ (HIF_READ | HIF_ASYNCHRONOUS | \ HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS) #define HIF_RD_SYNC_BLOCK_FIX \ (HIF_READ | HIF_SYNCHRONOUS | \ HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS) struct hif_scatter_item { u8 *buf; int len; struct htc_packet *packet; }; struct hif_scatter_req { struct list_head list; /* address for the read/write operation */ u32 addr; /* request flags */ u32 req; /* total length of entire transfer */ u32 len; bool virt_scat; void (*complete) (struct htc_target *, struct hif_scatter_req *); int status; int scat_entries; struct bus_request *busrequest; struct scatterlist *sgentries; /* bounce buffer for upper layers to copy to/from */ u8 *virt_dma_buf; struct hif_scatter_item scat_list[1]; u32 scat_q_depth; }; struct ath6kl_irq_proc_registers { u8 host_int_status; u8 cpu_int_status; u8 error_int_status; u8 counter_int_status; u8 mbox_frame; u8 rx_lkahd_valid; u8 host_int_status2; u8 gmbox_rx_avail; __le32 rx_lkahd[2]; __le32 rx_gmbox_lkahd_alias[2]; } __packed; struct ath6kl_irq_enable_reg { u8 int_status_en; u8 cpu_int_status_en; u8 err_int_status_en; u8 cntr_int_status_en; } __packed; struct ath6kl_device { /* protects irq_proc_reg and irq_en_reg below */ spinlock_t lock; struct ath6kl_irq_proc_registers irq_proc_reg; struct ath6kl_irq_enable_reg irq_en_reg; struct htc_target *htc_cnxt; struct ath6kl *ar; }; struct ath6kl_hif_ops { int (*read_write_sync)(struct ath6kl *ar, u32 addr, u8 *buf, u32 len, u32 request); int (*write_async)(struct ath6kl *ar, u32 address, u8 *buffer, u32 length, u32 request, struct htc_packet *packet); void (*irq_enable)(struct ath6kl *ar); void (*irq_disable)(struct ath6kl *ar); struct hif_scatter_req *(*scatter_req_get)(struct ath6kl *ar); void (*scatter_req_add)(struct ath6kl *ar, struct hif_scatter_req *s_req); int (*enable_scatter)(struct ath6kl *ar); int (*scat_req_rw) (struct ath6kl *ar, struct hif_scatter_req *scat_req); void (*cleanup_scatter)(struct ath6kl *ar); int (*suspend)(struct ath6kl *ar, struct cfg80211_wowlan *wow); int (*resume)(struct ath6kl *ar); int (*diag_read32)(struct ath6kl *ar, u32 address, u32 *value); int (*diag_write32)(struct ath6kl *ar, u32 address, __le32 value); int (*bmi_read)(struct ath6kl *ar, u8 *buf, u32 len); int (*bmi_write)(struct ath6kl *ar, u8 *buf, u32 len); int (*power_on)(struct ath6kl *ar); int (*power_off)(struct ath6kl *ar); void (*stop)(struct ath6kl *ar); int (*pipe_send)(struct ath6kl *ar, u8 pipe, struct sk_buff *hdr_buf, struct sk_buff *buf); void (*pipe_get_default)(struct ath6kl *ar, u8 *pipe_ul, u8 *pipe_dl); int (*pipe_map_service)(struct ath6kl *ar, u16 service_id, u8 *pipe_ul, u8 *pipe_dl); u16 (*pipe_get_free_queue_number)(struct ath6kl *ar, u8 pipe); }; int ath6kl_hif_setup(struct ath6kl_device *dev); int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev); int ath6kl_hif_mask_intrs(struct ath6kl_device *dev); int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd, int timeout); int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx); int ath6kl_hif_disable_intrs(struct ath6kl_device *dev); int ath6kl_hif_rw_comp_handler(void *context, int status); int ath6kl_hif_intr_bh_handler(struct ath6kl *ar); /* Scatter Function and Definitions */ int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev, struct hif_scatter_req *scat_req, bool read); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/hif.c0000644000175000017500000004515012026211315023345 0ustar mcgrofmcgrof/* * Copyright (c) 2007-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "hif.h" #include #include "core.h" #include "target.h" #include "hif-ops.h" #include "debug.h" #define MAILBOX_FOR_BLOCK_SIZE 1 #define ATH6KL_TIME_QUANTUM 10 /* in ms */ static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req, bool from_dma) { u8 *buf; int i; buf = req->virt_dma_buf; for (i = 0; i < req->scat_entries; i++) { if (from_dma) memcpy(req->scat_list[i].buf, buf, req->scat_list[i].len); else memcpy(buf, req->scat_list[i].buf, req->scat_list[i].len); buf += req->scat_list[i].len; } return 0; } int ath6kl_hif_rw_comp_handler(void *context, int status) { struct htc_packet *packet = context; ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n", packet, status); packet->status = status; packet->completion(packet->context, packet); return 0; } EXPORT_SYMBOL(ath6kl_hif_rw_comp_handler); #define REG_DUMP_COUNT_AR6003 60 #define REGISTER_DUMP_LEN_MAX 60 static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) { __le32 regdump_val[REGISTER_DUMP_LEN_MAX]; u32 i, address, regdump_addr = 0; int ret; if (ar->target_type != TARGET_TYPE_AR6003) return; /* the reg dump pointer is copied to the host interest area */ address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); address = TARG_VTOP(ar->target_type, address); /* read RAM location through diagnostic window */ ret = ath6kl_diag_read32(ar, address, ®dump_addr); if (ret || !regdump_addr) { ath6kl_warn("failed to get ptr to register dump area: %d\n", ret); return; } ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n", regdump_addr); regdump_addr = TARG_VTOP(ar->target_type, regdump_addr); /* fetch register dump data */ ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)®dump_val[0], REG_DUMP_COUNT_AR6003 * (sizeof(u32))); if (ret) { ath6kl_warn("failed to get register dump: %d\n", ret); return; } ath6kl_info("crash dump:\n"); ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version, ar->wiphy->fw_version); BUILD_BUG_ON(REG_DUMP_COUNT_AR6003 % 4); for (i = 0; i < REG_DUMP_COUNT_AR6003; i += 4) { ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", i, le32_to_cpu(regdump_val[i]), le32_to_cpu(regdump_val[i + 1]), le32_to_cpu(regdump_val[i + 2]), le32_to_cpu(regdump_val[i + 3])); } } static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) { u32 dummy; int ret; ath6kl_warn("firmware crashed\n"); /* * read counter to clear the interrupt, the debug error interrupt is * counter 0. */ ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS, (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC); if (ret) ath6kl_warn("Failed to clear debug interrupt: %d\n", ret); ath6kl_hif_dump_fw_crash(dev->ar); ath6kl_read_fwlogs(dev->ar); return ret; } /* mailbox recv message polling */ int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd, int timeout) { struct ath6kl_irq_proc_registers *rg; int status = 0, i; u8 htc_mbox = 1 << HTC_MAILBOX; for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) { /* this is the standard HIF way, load the reg table */ status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, (u8 *) &dev->irq_proc_reg, sizeof(dev->irq_proc_reg), HIF_RD_SYNC_BYTE_INC); if (status) { ath6kl_err("failed to read reg table\n"); return status; } /* check for MBOX data and valid lookahead */ if (dev->irq_proc_reg.host_int_status & htc_mbox) { if (dev->irq_proc_reg.rx_lkahd_valid & htc_mbox) { /* * Mailbox has a message and the look ahead * is valid. */ rg = &dev->irq_proc_reg; *lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); break; } } /* delay a little */ mdelay(ATH6KL_TIME_QUANTUM); ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i); } if (i == 0) { ath6kl_err("timeout waiting for recv message\n"); status = -ETIME; /* check if the target asserted */ if (dev->irq_proc_reg.counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK) /* * Target failure handler will be called in case of * an assert. */ ath6kl_hif_proc_dbg_intr(dev); } return status; } /* * Disable packet reception (used in case the host runs out of buffers) * using the interrupt enable registers through the host I/F */ int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx) { struct ath6kl_irq_enable_reg regs; int status = 0; ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n", enable_rx ? "enable" : "disable"); /* take the lock to protect interrupt enable shadows */ spin_lock_bh(&dev->lock); if (enable_rx) dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); else dev->irq_en_reg.int_status_en &= ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); memcpy(®s, &dev->irq_en_reg, sizeof(regs)); spin_unlock_bh(&dev->lock); status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, ®s.int_status_en, sizeof(struct ath6kl_irq_enable_reg), HIF_WR_SYNC_BYTE_INC); return status; } int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev, struct hif_scatter_req *scat_req, bool read) { int status = 0; if (read) { scat_req->req = HIF_RD_SYNC_BLOCK_FIX; scat_req->addr = dev->ar->mbox_info.htc_addr; } else { scat_req->req = HIF_WR_ASYNC_BLOCK_INC; scat_req->addr = (scat_req->len > HIF_MBOX_WIDTH) ? dev->ar->mbox_info.htc_ext_addr : dev->ar->mbox_info.htc_addr; } ath6kl_dbg(ATH6KL_DBG_HIF, "hif submit scatter request entries %d len %d mbox 0x%x %s %s\n", scat_req->scat_entries, scat_req->len, scat_req->addr, !read ? "async" : "sync", (read) ? "rd" : "wr"); if (!read && scat_req->virt_scat) { status = ath6kl_hif_cp_scat_dma_buf(scat_req, false); if (status) { scat_req->status = status; scat_req->complete(dev->ar->htc_target, scat_req); return 0; } } status = ath6kl_hif_scat_req_rw(dev->ar, scat_req); if (read) { /* in sync mode, we can touch the scatter request */ scat_req->status = status; if (!status && scat_req->virt_scat) scat_req->status = ath6kl_hif_cp_scat_dma_buf(scat_req, true); } return status; } static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev) { u8 counter_int_status; ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n"); counter_int_status = dev->irq_proc_reg.counter_int_status & dev->irq_en_reg.cntr_int_status_en; ath6kl_dbg(ATH6KL_DBG_IRQ, "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", counter_int_status); /* * NOTE: other modules like GMBOX may use the counter interrupt for * credit flow control on other counters, we only need to check for * the debug assertion counter interrupt. */ if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK) return ath6kl_hif_proc_dbg_intr(dev); return 0; } static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev) { int status; u8 error_int_status; u8 reg_buf[4]; ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n"); error_int_status = dev->irq_proc_reg.error_int_status & 0x0F; if (!error_int_status) { WARN_ON(1); return -EIO; } ath6kl_dbg(ATH6KL_DBG_IRQ, "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n", error_int_status); if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status)) ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n"); if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status)) ath6kl_err("rx underflow\n"); if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status)) ath6kl_err("tx overflow\n"); /* Clear the interrupt */ dev->irq_proc_reg.error_int_status &= ~error_int_status; /* set W1C value to clear the interrupt, this hits the register first */ reg_buf[0] = error_int_status; reg_buf[1] = 0; reg_buf[2] = 0; reg_buf[3] = 0; status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS, reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); if (status) WARN_ON(1); return status; } static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) { int status; u8 cpu_int_status; u8 reg_buf[4]; ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n"); cpu_int_status = dev->irq_proc_reg.cpu_int_status & dev->irq_en_reg.cpu_int_status_en; if (!cpu_int_status) { WARN_ON(1); return -EIO; } ath6kl_dbg(ATH6KL_DBG_IRQ, "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", cpu_int_status); /* Clear the interrupt */ dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status; /* * Set up the register transfer buffer to hit the register 4 times , * this is done to make the access 4-byte aligned to mitigate issues * with host bus interconnects that restrict bus transfer lengths to * be a multiple of 4-bytes. */ /* set W1C value to clear the interrupt, this hits the register first */ reg_buf[0] = cpu_int_status; /* the remaining are set to zero which have no-effect */ reg_buf[1] = 0; reg_buf[2] = 0; reg_buf[3] = 0; status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS, reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); if (status) WARN_ON(1); return status; } /* process pending interrupts synchronously */ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done) { struct ath6kl_irq_proc_registers *rg; int status = 0; u8 host_int_status = 0; u32 lk_ahd = 0; u8 htc_mbox = 1 << HTC_MAILBOX; ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev); /* * NOTE: HIF implementation guarantees that the context of this * call allows us to perform SYNCHRONOUS I/O, that is we can block, * sleep or call any API that can block or switch thread/task * contexts. This is a fully schedulable context. */ /* * Process pending intr only when int_status_en is clear, it may * result in unnecessary bus transaction otherwise. Target may be * unresponsive at the time. */ if (dev->irq_en_reg.int_status_en) { /* * Read the first 28 bytes of the HTC register table. This * will yield us the value of different int status * registers and the lookahead registers. * * length = sizeof(int_status) + sizeof(cpu_int_status) * + sizeof(error_int_status) + * sizeof(counter_int_status) + * sizeof(mbox_frame) + sizeof(rx_lkahd_valid) * + sizeof(hole) + sizeof(rx_lkahd) + * sizeof(int_status_en) + * sizeof(cpu_int_status_en) + * sizeof(err_int_status_en) + * sizeof(cntr_int_status_en); */ status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, (u8 *) &dev->irq_proc_reg, sizeof(dev->irq_proc_reg), HIF_RD_SYNC_BYTE_INC); if (status) goto out; ath6kl_dump_registers(dev, &dev->irq_proc_reg, &dev->irq_en_reg); /* Update only those registers that are enabled */ host_int_status = dev->irq_proc_reg.host_int_status & dev->irq_en_reg.int_status_en; /* Look at mbox status */ if (host_int_status & htc_mbox) { /* * Mask out pending mbox value, we use "lookAhead as * the real flag for mbox processing. */ host_int_status &= ~htc_mbox; if (dev->irq_proc_reg.rx_lkahd_valid & htc_mbox) { rg = &dev->irq_proc_reg; lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); if (!lk_ahd) ath6kl_err("lookAhead is zero!\n"); } } } if (!host_int_status && !lk_ahd) { *done = true; goto out; } if (lk_ahd) { int fetched = 0; ath6kl_dbg(ATH6KL_DBG_IRQ, "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd); /* * Mailbox Interrupt, the HTC layer may issue async * requests to empty the mailbox. When emptying the recv * mailbox we use the async handler above called from the * completion routine of the callers read request. This can * improve performance by reducing context switching when * we rapidly pull packets. */ status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt, lk_ahd, &fetched); if (status) goto out; if (!fetched) /* * HTC could not pull any messages out due to lack * of resources. */ dev->htc_cnxt->chk_irq_status_cnt = 0; } /* now handle the rest of them */ ath6kl_dbg(ATH6KL_DBG_IRQ, "valid interrupt source(s) for other interrupts: 0x%x\n", host_int_status); if (MS(HOST_INT_STATUS_CPU, host_int_status)) { /* CPU Interrupt */ status = ath6kl_hif_proc_cpu_intr(dev); if (status) goto out; } if (MS(HOST_INT_STATUS_ERROR, host_int_status)) { /* Error Interrupt */ status = ath6kl_hif_proc_err_intr(dev); if (status) goto out; } if (MS(HOST_INT_STATUS_COUNTER, host_int_status)) /* Counter Interrupt */ status = ath6kl_hif_proc_counter_intr(dev); out: /* * An optimization to bypass reading the IRQ status registers * unecessarily which can re-wake the target, if upper layers * determine that we are in a low-throughput mode, we can rely on * taking another interrupt rather than re-checking the status * registers which can re-wake the target. * * NOTE : for host interfaces that makes use of detecting pending * mbox messages at hif can not use this optimization due to * possible side effects, SPI requires the host to drain all * messages from the mailbox before exiting the ISR routine. */ ath6kl_dbg(ATH6KL_DBG_IRQ, "bypassing irq status re-check, forcing done\n"); if (!dev->htc_cnxt->chk_irq_status_cnt) *done = true; ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (done:%d, status=%d\n", *done, status); return status; } /* interrupt handler, kicks off all interrupt processing */ int ath6kl_hif_intr_bh_handler(struct ath6kl *ar) { struct ath6kl_device *dev = ar->htc_target->dev; unsigned long timeout; int status = 0; bool done = false; /* * Reset counter used to flag a re-scan of IRQ status registers on * the target. */ dev->htc_cnxt->chk_irq_status_cnt = 0; /* * IRQ processing is synchronous, interrupt status registers can be * re-read. */ timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT); while (time_before(jiffies, timeout) && !done) { status = proc_pending_irqs(dev, &done); if (status) break; } return status; } EXPORT_SYMBOL(ath6kl_hif_intr_bh_handler); static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev) { struct ath6kl_irq_enable_reg regs; int status; spin_lock_bh(&dev->lock); /* Enable all but ATH6KL CPU interrupts */ dev->irq_en_reg.int_status_en = SM(INT_STATUS_ENABLE_ERROR, 0x01) | SM(INT_STATUS_ENABLE_CPU, 0x01) | SM(INT_STATUS_ENABLE_COUNTER, 0x01); /* * NOTE: There are some cases where HIF can do detection of * pending mbox messages which is disabled now. */ dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); /* Set up the CPU Interrupt status Register */ dev->irq_en_reg.cpu_int_status_en = 0; /* Set up the Error Interrupt status Register */ dev->irq_en_reg.err_int_status_en = SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) | SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1); /* * Enable Counter interrupt status register to get fatal errors for * debugging. */ dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT, ATH6KL_TARGET_DEBUG_INTR_MASK); memcpy(®s, &dev->irq_en_reg, sizeof(regs)); spin_unlock_bh(&dev->lock); status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, ®s.int_status_en, sizeof(regs), HIF_WR_SYNC_BYTE_INC); if (status) ath6kl_err("failed to update interrupt ctl reg err: %d\n", status); return status; } int ath6kl_hif_disable_intrs(struct ath6kl_device *dev) { struct ath6kl_irq_enable_reg regs; spin_lock_bh(&dev->lock); /* Disable all interrupts */ dev->irq_en_reg.int_status_en = 0; dev->irq_en_reg.cpu_int_status_en = 0; dev->irq_en_reg.err_int_status_en = 0; dev->irq_en_reg.cntr_int_status_en = 0; memcpy(®s, &dev->irq_en_reg, sizeof(regs)); spin_unlock_bh(&dev->lock); return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, ®s.int_status_en, sizeof(regs), HIF_WR_SYNC_BYTE_INC); } /* enable device interrupts */ int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev) { int status = 0; /* * Make sure interrupt are disabled before unmasking at the HIF * layer. The rationale here is that between device insertion * (where we clear the interrupts the first time) and when HTC * is finally ready to handle interrupts, other software can perform * target "soft" resets. The ATH6KL interrupt enables reset back to an * "enabled" state when this happens. */ ath6kl_hif_disable_intrs(dev); /* unmask the host controller interrupts */ ath6kl_hif_irq_enable(dev->ar); status = ath6kl_hif_enable_intrs(dev); return status; } /* disable all device interrupts */ int ath6kl_hif_mask_intrs(struct ath6kl_device *dev) { /* * Mask the interrupt at the HIF layer to avoid any stray interrupt * taken while we zero out our shadow registers in * ath6kl_hif_disable_intrs(). */ ath6kl_hif_irq_disable(dev->ar); return ath6kl_hif_disable_intrs(dev); } int ath6kl_hif_setup(struct ath6kl_device *dev) { int status = 0; spin_lock_init(&dev->lock); /* * NOTE: we actually get the block size of a mailbox other than 0, * for SDIO the block size on mailbox 0 is artificially set to 1. * So we use the block size that is set for the other 3 mailboxes. */ dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size; /* must be a power of 2 */ if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) { WARN_ON(1); status = -EINVAL; goto fail_setup; } /* assemble mask, used for padding to a block */ dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1; ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); /* usb doesn't support enabling interrupts */ /* FIXME: remove check once USB support is implemented */ if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) return 0; status = ath6kl_hif_disable_intrs(dev); fail_setup: return status; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/debug.h0000644000175000017500000001060112026211315023663 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef DEBUG_H #define DEBUG_H #include "hif.h" enum ATH6K_DEBUG_MASK { ATH6KL_DBG_CREDIT = BIT(0), /* hole */ ATH6KL_DBG_WLAN_TX = BIT(2), /* wlan tx */ ATH6KL_DBG_WLAN_RX = BIT(3), /* wlan rx */ ATH6KL_DBG_BMI = BIT(4), /* bmi tracing */ ATH6KL_DBG_HTC = BIT(5), ATH6KL_DBG_HIF = BIT(6), ATH6KL_DBG_IRQ = BIT(7), /* interrupt processing */ /* hole */ /* hole */ ATH6KL_DBG_WMI = BIT(10), /* wmi tracing */ ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */ ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */ ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */ ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx frames */ ATH6KL_DBG_AGGR = BIT(15), /* aggregation */ ATH6KL_DBG_SDIO = BIT(16), ATH6KL_DBG_SDIO_DUMP = BIT(17), ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */ ATH6KL_DBG_WMI_DUMP = BIT(19), ATH6KL_DBG_SUSPEND = BIT(20), ATH6KL_DBG_USB = BIT(21), ATH6KL_DBG_USB_BULK = BIT(22), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; extern unsigned int debug_mask; extern __printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...); #define ath6kl_info(fmt, ...) \ ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__) #define ath6kl_err(fmt, ...) \ ath6kl_printk(KERN_ERR, fmt, ##__VA_ARGS__) #define ath6kl_warn(fmt, ...) \ ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__) enum ath6kl_war { ATH6KL_WAR_INVALID_RATE, }; #ifdef CONFIG_ATH6KL_DEBUG void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...); void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, const char *msg, const char *prefix, const void *buf, size_t len); void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_en_reg); void dump_cred_dist_stats(struct htc_target *target); void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len); void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war); int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, size_t len); void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive); void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout); void ath6kl_debug_init(struct ath6kl *ar); int ath6kl_debug_init_fs(struct ath6kl *ar); void ath6kl_debug_cleanup(struct ath6kl *ar); #else static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, const char *fmt, ...) { return 0; } static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, const char *msg, const char *prefix, const void *buf, size_t len) { } static inline void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_en_reg) { } static inline void dump_cred_dist_stats(struct htc_target *target) { } static inline void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) { } static inline void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) { } static inline int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, size_t len) { return 0; } static inline void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive) { } static inline void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout) { } static inline void ath6kl_debug_init(struct ath6kl *ar) { } static inline int ath6kl_debug_init_fs(struct ath6kl *ar) { return 0; } static inline void ath6kl_debug_cleanup(struct ath6kl *ar) { } #endif #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/debug.c0000644000175000017500000013124312026211315023664 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core.h" #include #include #include #include #include "debug.h" #include "target.h" struct ath6kl_fwlog_slot { __le32 timestamp; __le32 length; /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */ u8 payload[0]; }; #define ATH6KL_FWLOG_MAX_ENTRIES 20 #define ATH6KL_FWLOG_VALID_MASK 0x1ffff int ath6kl_printk(const char *level, const char *fmt, ...) { struct va_format vaf; va_list args; int rtn; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; rtn = printk("%sath6kl: %pV", level, &vaf); va_end(args); return rtn; } EXPORT_SYMBOL(ath6kl_printk); #ifdef CONFIG_ATH6KL_DEBUG void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...) { struct va_format vaf; va_list args; if (!(debug_mask & mask)) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; ath6kl_printk(KERN_DEBUG, "%pV", &vaf); va_end(args); } EXPORT_SYMBOL(ath6kl_dbg); void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, const char *msg, const char *prefix, const void *buf, size_t len) { if (debug_mask & mask) { if (msg) ath6kl_dbg(mask, "%s\n", msg); print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); } } EXPORT_SYMBOL(ath6kl_dbg_dump); #define REG_OUTPUT_LEN_PER_LINE 25 #define REGTYPE_STR_LEN 100 struct ath6kl_diag_reg_info { u32 reg_start; u32 reg_end; const char *reg_info; }; static const struct ath6kl_diag_reg_info diag_reg[] = { { 0x20000, 0x200fc, "General DMA and Rx registers" }, { 0x28000, 0x28900, "MAC PCU register & keycache" }, { 0x20800, 0x20a40, "QCU" }, { 0x21000, 0x212f0, "DCU" }, { 0x4000, 0x42e4, "RTC" }, { 0x540000, 0x540000 + (256 * 1024), "RAM" }, { 0x29800, 0x2B210, "Base Band" }, { 0x1C000, 0x1C748, "Analog" }, }; void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_enable_reg) { ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n")); if (irq_proc_reg != NULL) { ath6kl_dbg(ATH6KL_DBG_IRQ, "Host Int status: 0x%x\n", irq_proc_reg->host_int_status); ath6kl_dbg(ATH6KL_DBG_IRQ, "CPU Int status: 0x%x\n", irq_proc_reg->cpu_int_status); ath6kl_dbg(ATH6KL_DBG_IRQ, "Error Int status: 0x%x\n", irq_proc_reg->error_int_status); ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status: 0x%x\n", irq_proc_reg->counter_int_status); ath6kl_dbg(ATH6KL_DBG_IRQ, "Mbox Frame: 0x%x\n", irq_proc_reg->mbox_frame); ath6kl_dbg(ATH6KL_DBG_IRQ, "Rx Lookahead Valid: 0x%x\n", irq_proc_reg->rx_lkahd_valid); ath6kl_dbg(ATH6KL_DBG_IRQ, "Rx Lookahead 0: 0x%x\n", irq_proc_reg->rx_lkahd[0]); ath6kl_dbg(ATH6KL_DBG_IRQ, "Rx Lookahead 1: 0x%x\n", irq_proc_reg->rx_lkahd[1]); if (dev->ar->mbox_info.gmbox_addr != 0) { /* * If the target supports GMBOX hardware, dump some * additional state. */ ath6kl_dbg(ATH6KL_DBG_IRQ, "GMBOX Host Int status 2: 0x%x\n", irq_proc_reg->host_int_status2); ath6kl_dbg(ATH6KL_DBG_IRQ, "GMBOX RX Avail: 0x%x\n", irq_proc_reg->gmbox_rx_avail); ath6kl_dbg(ATH6KL_DBG_IRQ, "GMBOX lookahead alias 0: 0x%x\n", irq_proc_reg->rx_gmbox_lkahd_alias[0]); ath6kl_dbg(ATH6KL_DBG_IRQ, "GMBOX lookahead alias 1: 0x%x\n", irq_proc_reg->rx_gmbox_lkahd_alias[1]); } } if (irq_enable_reg != NULL) { ath6kl_dbg(ATH6KL_DBG_IRQ, "Int status Enable: 0x%x\n", irq_enable_reg->int_status_en); ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status Enable: 0x%x\n", irq_enable_reg->cntr_int_status_en); } ath6kl_dbg(ATH6KL_DBG_IRQ, "<------------------------------->\n"); } static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist) { ath6kl_dbg(ATH6KL_DBG_CREDIT, "--- endpoint: %d svc_id: 0x%X ---\n", ep_dist->endpoint, ep_dist->svc_id); ath6kl_dbg(ATH6KL_DBG_CREDIT, " dist_flags : 0x%X\n", ep_dist->dist_flags); ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_norm : %d\n", ep_dist->cred_norm); ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_min : %d\n", ep_dist->cred_min); ath6kl_dbg(ATH6KL_DBG_CREDIT, " credits : %d\n", ep_dist->credits); ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_assngd : %d\n", ep_dist->cred_assngd); ath6kl_dbg(ATH6KL_DBG_CREDIT, " seek_cred : %d\n", ep_dist->seek_cred); ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_sz : %d\n", ep_dist->cred_sz); ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_per_msg : %d\n", ep_dist->cred_per_msg); ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_to_dist : %d\n", ep_dist->cred_to_dist); ath6kl_dbg(ATH6KL_DBG_CREDIT, " txq_depth : %d\n", get_queue_depth(&ep_dist->htc_ep->txq)); ath6kl_dbg(ATH6KL_DBG_CREDIT, "----------------------------------\n"); } /* FIXME: move to htc.c */ void dump_cred_dist_stats(struct htc_target *target) { struct htc_endpoint_credit_dist *ep_list; list_for_each_entry(ep_list, &target->cred_dist_list, list) dump_cred_dist(ep_list); ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit distribution total %d free %d\n", target->credit_info->total_avail_credits, target->credit_info->cur_free_credits); } void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) { switch (war) { case ATH6KL_WAR_INVALID_RATE: ar->debug.war_stats.invalid_rate++; break; } } static ssize_t read_file_war_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; char *buf; unsigned int len = 0, buf_len = 1500; ssize_t ret_cnt; buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "%25s\n", "Workaround stats"); len += scnprintf(buf + len, buf_len - len, "%25s\n\n", "================="); len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", "Invalid rates", ar->debug.war_stats.invalid_rate); if (WARN_ON(len > buf_len)) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret_cnt; } static const struct file_operations fops_war_stats = { .read = read_file_war_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) { struct ath6kl_fwlog_slot *slot; struct sk_buff *skb; size_t slot_len; if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) return; slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE; skb = alloc_skb(slot_len, GFP_KERNEL); if (!skb) return; slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len); slot->timestamp = cpu_to_le32(jiffies); slot->length = cpu_to_le32(len); memcpy(slot->payload, buf, len); /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */ memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len); spin_lock(&ar->debug.fwlog_queue.lock); __skb_queue_tail(&ar->debug.fwlog_queue, skb); complete(&ar->debug.fwlog_completion); /* drop oldest entries */ while (skb_queue_len(&ar->debug.fwlog_queue) > ATH6KL_FWLOG_MAX_ENTRIES) { skb = __skb_dequeue(&ar->debug.fwlog_queue); kfree_skb(skb); } spin_unlock(&ar->debug.fwlog_queue.lock); return; } static int ath6kl_fwlog_open(struct inode *inode, struct file *file) { struct ath6kl *ar = inode->i_private; if (ar->debug.fwlog_open) return -EBUSY; ar->debug.fwlog_open = true; file->private_data = inode->i_private; return 0; } static int ath6kl_fwlog_release(struct inode *inode, struct file *file) { struct ath6kl *ar = inode->i_private; ar->debug.fwlog_open = false; return 0; } static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct sk_buff *skb; ssize_t ret_cnt; size_t len = 0; char *buf; buf = vmalloc(count); if (!buf) return -ENOMEM; /* read undelivered logs from firmware */ ath6kl_read_fwlogs(ar); spin_lock(&ar->debug.fwlog_queue.lock); while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { if (skb->len > count - len) { /* not enough space, put skb back and leave */ __skb_queue_head(&ar->debug.fwlog_queue, skb); break; } memcpy(buf + len, skb->data, skb->len); len += skb->len; kfree_skb(skb); } spin_unlock(&ar->debug.fwlog_queue.lock); /* FIXME: what to do if len == 0? */ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); vfree(buf); return ret_cnt; } static const struct file_operations fops_fwlog = { .open = ath6kl_fwlog_open, .release = ath6kl_fwlog_release, .read = ath6kl_fwlog_read, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_fwlog_block_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct sk_buff *skb; ssize_t ret_cnt; size_t len = 0, not_copied; char *buf; int ret; buf = vmalloc(count); if (!buf) return -ENOMEM; spin_lock(&ar->debug.fwlog_queue.lock); if (skb_queue_len(&ar->debug.fwlog_queue) == 0) { /* we must init under queue lock */ init_completion(&ar->debug.fwlog_completion); spin_unlock(&ar->debug.fwlog_queue.lock); ret = wait_for_completion_interruptible( &ar->debug.fwlog_completion); if (ret == -ERESTARTSYS) { vfree(buf); return ret; } spin_lock(&ar->debug.fwlog_queue.lock); } while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { if (skb->len > count - len) { /* not enough space, put skb back and leave */ __skb_queue_head(&ar->debug.fwlog_queue, skb); break; } memcpy(buf + len, skb->data, skb->len); len += skb->len; kfree_skb(skb); } spin_unlock(&ar->debug.fwlog_queue.lock); /* FIXME: what to do if len == 0? */ not_copied = copy_to_user(user_buf, buf, len); if (not_copied != 0) { ret_cnt = -EFAULT; goto out; } *ppos = *ppos + len; ret_cnt = len; out: vfree(buf); return ret_cnt; } static const struct file_operations fops_fwlog_block = { .open = ath6kl_fwlog_open, .release = ath6kl_fwlog_release, .read = ath6kl_fwlog_block_read, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; char buf[16]; int len; len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t ath6kl_fwlog_mask_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; int ret; ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask); if (ret) return ret; ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi, ATH6KL_FWLOG_VALID_MASK, ar->debug.fwlog_mask); if (ret) return ret; return count; } static const struct file_operations fops_fwlog_mask = { .open = simple_open, .read = ath6kl_fwlog_mask_read, .write = ath6kl_fwlog_mask_write, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; struct target_stats *tgt_stats; char *buf; unsigned int len = 0, buf_len = 1500; int i; long left; ssize_t ret_cnt; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; tgt_stats = &vif->target_stats; buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; if (down_interruptible(&ar->sem)) { kfree(buf); return -EBUSY; } set_bit(STATS_UPDATE_PEND, &vif->flags); if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) { up(&ar->sem); kfree(buf); return -EIO; } left = wait_event_interruptible_timeout(ar->event_wq, !test_bit(STATS_UPDATE_PEND, &vif->flags), WMI_TIMEOUT); up(&ar->sem); if (left <= 0) { kfree(buf); return -ETIMEDOUT; } len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "%25s\n", "Target Tx stats"); len += scnprintf(buf + len, buf_len - len, "%25s\n\n", "================="); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Ucast packets", tgt_stats->tx_ucast_pkt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Bcast packets", tgt_stats->tx_bcast_pkt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Ucast byte", tgt_stats->tx_ucast_byte); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Bcast byte", tgt_stats->tx_bcast_byte); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Rts success cnt", tgt_stats->tx_rts_success_cnt); for (i = 0; i < 4; i++) len += scnprintf(buf + len, buf_len - len, "%18s %d %10llu\n", "PER on ac", i, tgt_stats->tx_pkt_per_ac[i]); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Error", tgt_stats->tx_err); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Fail count", tgt_stats->tx_fail_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Retry count", tgt_stats->tx_retry_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Multi retry cnt", tgt_stats->tx_mult_retry_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Rts fail cnt", tgt_stats->tx_rts_fail_cnt); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n", "TKIP counter measure used", tgt_stats->tkip_cnter_measures_invoked); len += scnprintf(buf + len, buf_len - len, "%25s\n", "Target Rx stats"); len += scnprintf(buf + len, buf_len - len, "%25s\n", "================="); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Ucast packets", tgt_stats->rx_ucast_pkt); len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", "Ucast Rate", tgt_stats->rx_ucast_rate); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Bcast packets", tgt_stats->rx_bcast_pkt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Ucast byte", tgt_stats->rx_ucast_byte); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Bcast byte", tgt_stats->rx_bcast_byte); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Fragmented pkt", tgt_stats->rx_frgment_pkt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Error", tgt_stats->rx_err); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "CRC Err", tgt_stats->rx_crc_err); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Key chache miss", tgt_stats->rx_key_cache_miss); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Decrypt Err", tgt_stats->rx_decrypt_err); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Duplicate frame", tgt_stats->rx_dupl_frame); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Tkip Mic failure", tgt_stats->tkip_local_mic_fail); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "TKIP format err", tgt_stats->tkip_fmt_err); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "CCMP format Err", tgt_stats->ccmp_fmt_err); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n", "CCMP Replay Err", tgt_stats->ccmp_replays); len += scnprintf(buf + len, buf_len - len, "%25s\n", "Misc Target stats"); len += scnprintf(buf + len, buf_len - len, "%25s\n", "================="); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Beacon Miss count", tgt_stats->cs_bmiss_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Num Connects", tgt_stats->cs_connect_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", "Num disconnects", tgt_stats->cs_discon_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", "ARP pkt received", tgt_stats->arp_received); len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", "ARP pkt matched", tgt_stats->arp_matched); len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", "ARP pkt replied", tgt_stats->arp_replied); if (len > buf_len) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret_cnt; } static const struct file_operations fops_tgt_stats = { .read = read_file_tgt_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; #define print_credit_info(fmt_str, ep_list_field) \ (len += scnprintf(buf + len, buf_len - len, fmt_str, \ ep_list->ep_list_field)) #define CREDIT_INFO_DISPLAY_STRING_LEN 200 #define CREDIT_INFO_LEN 128 static ssize_t read_file_credit_dist_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct htc_target *target = ar->htc_target; struct htc_endpoint_credit_dist *ep_list; char *buf; unsigned int buf_len, len = 0; ssize_t ret_cnt; buf_len = CREDIT_INFO_DISPLAY_STRING_LEN + get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN; buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", "Total Avail Credits: ", target->credit_info->total_avail_credits); len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", "Free credits :", target->credit_info->cur_free_credits); len += scnprintf(buf + len, buf_len - len, " Epid Flags Cred_norm Cred_min Credits Cred_assngd" " Seek_cred Cred_sz Cred_per_msg Cred_to_dist" " qdepth\n"); list_for_each_entry(ep_list, &target->cred_dist_list, list) { print_credit_info(" %2d", endpoint); print_credit_info("%10x", dist_flags); print_credit_info("%8d", cred_norm); print_credit_info("%9d", cred_min); print_credit_info("%9d", credits); print_credit_info("%10d", cred_assngd); print_credit_info("%13d", seek_cred); print_credit_info("%12d", cred_sz); print_credit_info("%9d", cred_per_msg); print_credit_info("%14d", cred_to_dist); len += scnprintf(buf + len, buf_len - len, "%12d\n", get_queue_depth(&ep_list->htc_ep->txq)); } if (len > buf_len) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret_cnt; } static const struct file_operations fops_credit_dist_stats = { .read = read_file_credit_dist_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static unsigned int print_endpoint_stat(struct htc_target *target, char *buf, unsigned int buf_len, unsigned int len, int offset, const char *name) { int i; struct htc_endpoint_stats *ep_st; u32 *counter; len += scnprintf(buf + len, buf_len - len, "%s:", name); for (i = 0; i < ENDPOINT_MAX; i++) { ep_st = &target->endpoint[i].ep_st; counter = ((u32 *) ep_st) + (offset / 4); len += scnprintf(buf + len, buf_len - len, " %u", *counter); } len += scnprintf(buf + len, buf_len - len, "\n"); return len; } static ssize_t ath6kl_endpoint_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct htc_target *target = ar->htc_target; char *buf; unsigned int buf_len, len = 0; ssize_t ret_cnt; buf_len = sizeof(struct htc_endpoint_stats) / sizeof(u32) * (25 + ENDPOINT_MAX * 11); buf = kmalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; #define EPSTAT(name) \ do { \ len = print_endpoint_stat(target, buf, buf_len, len, \ offsetof(struct htc_endpoint_stats, \ name), \ #name); \ } while (0) EPSTAT(cred_low_indicate); EPSTAT(tx_issued); EPSTAT(tx_pkt_bundled); EPSTAT(tx_bundles); EPSTAT(tx_dropped); EPSTAT(tx_cred_rpt); EPSTAT(cred_rpt_from_rx); EPSTAT(cred_rpt_from_other); EPSTAT(cred_rpt_ep0); EPSTAT(cred_from_rx); EPSTAT(cred_from_other); EPSTAT(cred_from_ep0); EPSTAT(cred_cosumd); EPSTAT(cred_retnd); EPSTAT(rx_pkts); EPSTAT(rx_lkahds); EPSTAT(rx_bundl); EPSTAT(rx_bundle_lkahd); EPSTAT(rx_bundle_from_hdr); EPSTAT(rx_alloc_thresh_hit); EPSTAT(rxalloc_thresh_byte); #undef EPSTAT if (len > buf_len) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret_cnt; } static ssize_t ath6kl_endpoint_stats_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct htc_target *target = ar->htc_target; int ret, i; u32 val; struct htc_endpoint_stats *ep_st; ret = kstrtou32_from_user(user_buf, count, 0, &val); if (ret) return ret; if (val == 0) { for (i = 0; i < ENDPOINT_MAX; i++) { ep_st = &target->endpoint[i].ep_st; memset(ep_st, 0, sizeof(*ep_st)); } } return count; } static const struct file_operations fops_endpoint_stats = { .open = simple_open, .read = ath6kl_endpoint_stats_read, .write = ath6kl_endpoint_stats_write, .owner = THIS_MODULE, .llseek = default_llseek, }; static unsigned long ath6kl_get_num_reg(void) { int i; unsigned long n_reg = 0; for (i = 0; i < ARRAY_SIZE(diag_reg); i++) n_reg = n_reg + (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1; return n_reg; } static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr) { int i; for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { if (reg_addr >= diag_reg[i].reg_start && reg_addr <= diag_reg[i].reg_end) return true; } return false; } static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; u8 buf[50]; unsigned int len = 0; if (ar->debug.dbgfs_diag_reg) len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", ar->debug.dbgfs_diag_reg); else len += scnprintf(buf + len, sizeof(buf) - len, "All diag registers\n"); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t ath6kl_regread_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; unsigned long reg_addr; if (kstrtoul_from_user(user_buf, count, 0, ®_addr)) return -EINVAL; if ((reg_addr % 4) != 0) return -EINVAL; if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr)) return -EINVAL; ar->debug.dbgfs_diag_reg = reg_addr; return count; } static const struct file_operations fops_diag_reg_read = { .read = ath6kl_regread_read, .write = ath6kl_regread_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static int ath6kl_regdump_open(struct inode *inode, struct file *file) { struct ath6kl *ar = inode->i_private; u8 *buf; unsigned long int reg_len; unsigned int len = 0, n_reg; u32 addr; __le32 reg_val; int i, status; /* Dump all the registers if no register is specified */ if (!ar->debug.dbgfs_diag_reg) n_reg = ath6kl_get_num_reg(); else n_reg = 1; reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE; if (n_reg > 1) reg_len += REGTYPE_STR_LEN; buf = vmalloc(reg_len); if (!buf) return -ENOMEM; if (n_reg == 1) { addr = ar->debug.dbgfs_diag_reg; status = ath6kl_diag_read32(ar, TARG_VTOP(ar->target_type, addr), (u32 *)®_val); if (status) goto fail_reg_read; len += scnprintf(buf + len, reg_len - len, "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val)); goto done; } for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { len += scnprintf(buf + len, reg_len - len, "%s\n", diag_reg[i].reg_info); for (addr = diag_reg[i].reg_start; addr <= diag_reg[i].reg_end; addr += 4) { status = ath6kl_diag_read32(ar, TARG_VTOP(ar->target_type, addr), (u32 *)®_val); if (status) goto fail_reg_read; len += scnprintf(buf + len, reg_len - len, "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val)); } } done: file->private_data = buf; return 0; fail_reg_read: ath6kl_warn("Unable to read memory:%u\n", addr); vfree(buf); return -EIO; } static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { u8 *buf = file->private_data; return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); } static int ath6kl_regdump_release(struct inode *inode, struct file *file) { vfree(file->private_data); return 0; } static const struct file_operations fops_reg_dump = { .open = ath6kl_regdump_open, .read = ath6kl_regdump_read, .release = ath6kl_regdump_release, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_lrssi_roam_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; unsigned long lrssi_roam_threshold; if (kstrtoul_from_user(user_buf, count, 0, &lrssi_roam_threshold)) return -EINVAL; ar->lrssi_roam_threshold = lrssi_roam_threshold; ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold); return count; } static ssize_t ath6kl_lrssi_roam_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; char buf[32]; unsigned int len; len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_lrssi_roam_threshold = { .read = ath6kl_lrssi_roam_read, .write = ath6kl_lrssi_roam_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_regwrite_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; u8 buf[32]; unsigned int len = 0; len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n", ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t ath6kl_regwrite_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; char buf[32]; char *sptr, *token; unsigned int len = 0; u32 reg_addr, reg_val; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; sptr = buf; token = strsep(&sptr, "="); if (!token) return -EINVAL; if (kstrtou32(token, 0, ®_addr)) return -EINVAL; if (!ath6kl_dbg_is_diag_reg_valid(reg_addr)) return -EINVAL; if (kstrtou32(sptr, 0, ®_val)) return -EINVAL; ar->debug.diag_reg_addr_wr = reg_addr; ar->debug.diag_reg_val_wr = reg_val; if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr, cpu_to_le32(ar->debug.diag_reg_val_wr))) return -EIO; return count; } static const struct file_operations fops_diag_reg_write = { .read = ath6kl_regwrite_read, .write = ath6kl_regwrite_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, size_t len) { const struct wmi_target_roam_tbl *tbl; u16 num_entries; if (len < sizeof(*tbl)) return -EINVAL; tbl = (const struct wmi_target_roam_tbl *) buf; num_entries = le16_to_cpu(tbl->num_entries); if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) > len) return -EINVAL; if (ar->debug.roam_tbl == NULL || ar->debug.roam_tbl_len < (unsigned int) len) { kfree(ar->debug.roam_tbl); ar->debug.roam_tbl = kmalloc(len, GFP_ATOMIC); if (ar->debug.roam_tbl == NULL) return -ENOMEM; } memcpy(ar->debug.roam_tbl, buf, len); ar->debug.roam_tbl_len = len; if (test_bit(ROAM_TBL_PEND, &ar->flag)) { clear_bit(ROAM_TBL_PEND, &ar->flag); wake_up(&ar->event_wq); } return 0; } static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; int ret; long left; struct wmi_target_roam_tbl *tbl; u16 num_entries, i; char *buf; unsigned int len, buf_len; ssize_t ret_cnt; if (down_interruptible(&ar->sem)) return -EBUSY; set_bit(ROAM_TBL_PEND, &ar->flag); ret = ath6kl_wmi_get_roam_tbl_cmd(ar->wmi); if (ret) { up(&ar->sem); return ret; } left = wait_event_interruptible_timeout( ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT); up(&ar->sem); if (left <= 0) return -ETIMEDOUT; if (ar->debug.roam_tbl == NULL) return -ENOMEM; tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl; num_entries = le16_to_cpu(tbl->num_entries); buf_len = 100 + num_entries * 100; buf = kzalloc(buf_len, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len = 0; len += scnprintf(buf + len, buf_len - len, "roam_mode=%u\n\n" "# roam_util bssid rssi rssidt last_rssi util bias\n", le16_to_cpu(tbl->roam_mode)); for (i = 0; i < num_entries; i++) { struct wmi_bss_roam_info *info = &tbl->info[i]; len += scnprintf(buf + len, buf_len - len, "%d %pM %d %d %d %d %d\n", a_sle32_to_cpu(info->roam_util), info->bssid, info->rssi, info->rssidt, info->last_rssi, info->util, info->bias); } if (len > buf_len) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret_cnt; } static const struct file_operations fops_roam_table = { .read = ath6kl_roam_table_read, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_force_roam_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; int ret; char buf[20]; size_t len; u8 bssid[ETH_ALEN]; int i; int addr[ETH_ALEN]; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) != ETH_ALEN) return -EINVAL; for (i = 0; i < ETH_ALEN; i++) bssid[i] = addr[i]; ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid); if (ret) return ret; return count; } static const struct file_operations fops_force_roam = { .write = ath6kl_force_roam_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_roam_mode_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; int ret; char buf[20]; size_t len; enum wmi_roam_mode mode; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; if (strcasecmp(buf, "default") == 0) mode = WMI_DEFAULT_ROAM_MODE; else if (strcasecmp(buf, "bssbias") == 0) mode = WMI_HOST_BIAS_ROAM_MODE; else if (strcasecmp(buf, "lock") == 0) mode = WMI_LOCK_BSS_MODE; else return -EINVAL; ret = ath6kl_wmi_set_roam_mode_cmd(ar->wmi, mode); if (ret) return ret; return count; } static const struct file_operations fops_roam_mode = { .write = ath6kl_roam_mode_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive) { ar->debug.keepalive = keepalive; } static ssize_t ath6kl_keepalive_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; char buf[16]; int len; len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.keepalive); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t ath6kl_keepalive_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; int ret; u8 val; ret = kstrtou8_from_user(user_buf, count, 0, &val); if (ret) return ret; ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, 0, val); if (ret) return ret; return count; } static const struct file_operations fops_keepalive = { .open = simple_open, .read = ath6kl_keepalive_read, .write = ath6kl_keepalive_write, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout) { ar->debug.disc_timeout = timeout; } static ssize_t ath6kl_disconnect_timeout_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; char buf[16]; int len; len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.disc_timeout); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t ath6kl_disconnect_timeout_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; int ret; u8 val; ret = kstrtou8_from_user(user_buf, count, 0, &val); if (ret) return ret; ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, 0, val); if (ret) return ret; return count; } static const struct file_operations fops_disconnect_timeout = { .open = simple_open, .read = ath6kl_disconnect_timeout_read, .write = ath6kl_disconnect_timeout_write, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_create_qos_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; char buf[200]; ssize_t len; char *sptr, *token; struct wmi_create_pstream_cmd pstream; u32 val32; u16 val16; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; sptr = buf; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &pstream.user_pri)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &pstream.traffic_direc)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &pstream.traffic_class)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &pstream.traffic_type)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &pstream.voice_psc_cap)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.min_service_int = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.max_service_int = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.inactivity_int = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.suspension_int = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.service_start_time = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &pstream.tsid)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou16(token, 0, &val16)) return -EINVAL; pstream.nominal_msdu = cpu_to_le16(val16); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou16(token, 0, &val16)) return -EINVAL; pstream.max_msdu = cpu_to_le16(val16); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.min_data_rate = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.mean_data_rate = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.peak_data_rate = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.max_burst_size = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.delay_bound = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.min_phy_rate = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.sba = cpu_to_le32(val32); token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou32(token, 0, &val32)) return -EINVAL; pstream.medium_time = cpu_to_le32(val32); pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000; ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream); return count; } static const struct file_operations fops_create_qos = { .write = ath6kl_create_qos_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_delete_qos_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; char buf[100]; ssize_t len; char *sptr, *token; u8 traffic_class; u8 tsid; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; sptr = buf; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &traffic_class)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou8(token, 0, &tsid)) return -EINVAL; ath6kl_wmi_delete_pstream_cmd(ar->wmi, vif->fw_vif_idx, traffic_class, tsid); return count; } static const struct file_operations fops_delete_qos = { .write = ath6kl_delete_qos_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_bgscan_int_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; u16 bgscan_int; char buf[32]; ssize_t len; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (kstrtou16(buf, 0, &bgscan_int)) return -EINVAL; if (bgscan_int == 0) bgscan_int = 0xffff; vif->bg_scan_period = bgscan_int; ath6kl_wmi_scanparams_cmd(ar->wmi, 0, 0, 0, bgscan_int, 0, 0, 0, 3, 0, 0, 0); return count; } static const struct file_operations fops_bgscan_int = { .write = ath6kl_bgscan_int_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_listen_int_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; u16 listen_interval; char buf[32]; ssize_t len; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (kstrtou16(buf, 0, &listen_interval)) return -EINVAL; if ((listen_interval < 15) || (listen_interval > 3000)) return -EINVAL; vif->listen_intvl_t = listen_interval; ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); return count; } static ssize_t ath6kl_listen_int_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; char buf[32]; int len; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; len = scnprintf(buf, sizeof(buf), "%u\n", vif->listen_intvl_t); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_listen_int = { .read = ath6kl_listen_int_read, .write = ath6kl_listen_int_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static ssize_t ath6kl_power_params_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; u8 buf[100]; unsigned int len = 0; char *sptr, *token; u16 idle_period, ps_poll_num, dtim, tx_wakeup, num_tx; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; sptr = buf; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou16(token, 0, &idle_period)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou16(token, 0, &ps_poll_num)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou16(token, 0, &dtim)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou16(token, 0, &tx_wakeup)) return -EINVAL; token = strsep(&sptr, " "); if (!token) return -EINVAL; if (kstrtou16(token, 0, &num_tx)) return -EINVAL; ath6kl_wmi_pmparams_cmd(ar->wmi, 0, idle_period, ps_poll_num, dtim, tx_wakeup, num_tx, 0); return count; } static const struct file_operations fops_power_params = { .write = ath6kl_power_params_write, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath6kl_debug_init(struct ath6kl *ar) { skb_queue_head_init(&ar->debug.fwlog_queue); init_completion(&ar->debug.fwlog_completion); /* * Actually we are lying here but don't know how to read the mask * value from the firmware. */ ar->debug.fwlog_mask = 0; } /* * Initialisation needs to happen in two stages as fwlog events can come * before cfg80211 is initialised, and debugfs depends on cfg80211 * initialisation. */ int ath6kl_debug_init_fs(struct ath6kl *ar) { ar->debugfs_phy = debugfs_create_dir("ath6kl", ar->wiphy->debugfsdir); if (!ar->debugfs_phy) return -ENOMEM; debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, &fops_tgt_stats); debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar, &fops_credit_dist_stats); debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_endpoint_stats); debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, &fops_fwlog); debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar, &fops_fwlog_block); debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_fwlog_mask); debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_diag_reg_read); debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar, &fops_reg_dump); debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_lrssi_roam_threshold); debugfs_create_file("reg_write", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_diag_reg_write); debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar, &fops_war_stats); debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar, &fops_roam_table); debugfs_create_file("force_roam", S_IWUSR, ar->debugfs_phy, ar, &fops_force_roam); debugfs_create_file("roam_mode", S_IWUSR, ar->debugfs_phy, ar, &fops_roam_mode); debugfs_create_file("keepalive", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_keepalive); debugfs_create_file("disconnect_timeout", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_disconnect_timeout); debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar, &fops_create_qos); debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar, &fops_delete_qos); debugfs_create_file("bgscan_interval", S_IWUSR, ar->debugfs_phy, ar, &fops_bgscan_int); debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_listen_int); debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar, &fops_power_params); return 0; } void ath6kl_debug_cleanup(struct ath6kl *ar) { skb_queue_purge(&ar->debug.fwlog_queue); complete(&ar->debug.fwlog_completion); kfree(ar->debug.roam_tbl); } #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/core.h0000644000175000017500000005644412026211315023544 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef CORE_H #define CORE_H #include #include #include #include #include #include #include "htc.h" #include "wmi.h" #include "bmi.h" #include "target.h" #define MAX_ATH6KL 1 #define ATH6KL_MAX_RX_BUFFERS 16 #define ATH6KL_BUFFER_SIZE 1664 #define ATH6KL_MAX_AMSDU_RX_BUFFERS 4 #define ATH6KL_AMSDU_REFILL_THRESHOLD 3 #define ATH6KL_AMSDU_BUFFER_SIZE (WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH + 128) #define MAX_MSDU_SUBFRAME_PAYLOAD_LEN 1508 #define MIN_MSDU_SUBFRAME_PAYLOAD_LEN 46 #define USER_SAVEDKEYS_STAT_INIT 0 #define USER_SAVEDKEYS_STAT_RUN 1 #define ATH6KL_TX_TIMEOUT 10 #define ATH6KL_MAX_ENDPOINTS 4 #define MAX_NODE_NUM 15 #define ATH6KL_APSD_ALL_FRAME 0xFFFF #define ATH6KL_APSD_NUM_OF_AC 0x4 #define ATH6KL_APSD_FRAME_MASK 0xF /* Extra bytes for htc header alignment */ #define ATH6KL_HTC_ALIGN_BYTES 3 /* MAX_HI_COOKIE_NUM are reserved for high priority traffic */ #define MAX_DEF_COOKIE_NUM 180 #define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */ #define MAX_COOKIE_NUM (MAX_DEF_COOKIE_NUM + MAX_HI_COOKIE_NUM) #define MAX_DEFAULT_SEND_QUEUE_DEPTH (MAX_DEF_COOKIE_NUM / WMM_NUM_AC) #define DISCON_TIMER_INTVAL 10000 /* in msec */ /* Channel dwell time in fg scan */ #define ATH6KL_FG_SCAN_INTERVAL 50 /* in ms */ /* includes also the null byte */ #define ATH6KL_FIRMWARE_MAGIC "QCA-ATH6KL" enum ath6kl_fw_ie_type { ATH6KL_FW_IE_FW_VERSION = 0, ATH6KL_FW_IE_TIMESTAMP = 1, ATH6KL_FW_IE_OTP_IMAGE = 2, ATH6KL_FW_IE_FW_IMAGE = 3, ATH6KL_FW_IE_PATCH_IMAGE = 4, ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5, ATH6KL_FW_IE_CAPABILITIES = 6, ATH6KL_FW_IE_PATCH_ADDR = 7, ATH6KL_FW_IE_BOARD_ADDR = 8, ATH6KL_FW_IE_VIF_MAX = 9, }; enum ath6kl_fw_capability { ATH6KL_FW_CAPABILITY_HOST_P2P = 0, ATH6KL_FW_CAPABILITY_SCHED_SCAN = 1, /* * Firmware is capable of supporting P2P mgmt operations on a * station interface. After group formation, the station * interface will become a P2P client/GO interface as the case may be */ ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, /* * Firmware has support to cleanup inactive stations * in AP mode. */ ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, /* Firmware has support to override rsn cap of rsn ie */ ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, /* * Multicast support in WOW and host awake mode. * Allow all multicast in host awake mode. * Apply multicast filter in WOW mode. */ ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, /* Firmware supports enhanced bmiss detection */ ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, /* * FW supports matching of ssid in schedule scan */ ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; #define ATH6KL_CAPABILITY_LEN (ALIGN(ATH6KL_FW_CAPABILITY_MAX, 32) / 32) struct ath6kl_fw_ie { __le32 id; __le32 len; u8 data[0]; }; enum ath6kl_hw_flags { ATH6KL_HW_FLAG_64BIT_RATES = BIT(0), }; #define ATH6KL_FW_API2_FILE "fw-2.bin" #define ATH6KL_FW_API3_FILE "fw-3.bin" /* AR6003 1.0 definitions */ #define AR6003_HW_1_0_VERSION 0x300002ba /* AR6003 2.0 definitions */ #define AR6003_HW_2_0_VERSION 0x30000384 #define AR6003_HW_2_0_PATCH_DOWNLOAD_ADDRESS 0x57e910 #define AR6003_HW_2_0_FW_DIR "ath6k/AR6003/hw2.0" #define AR6003_HW_2_0_OTP_FILE "otp.bin.z77" #define AR6003_HW_2_0_FIRMWARE_FILE "athwlan.bin.z77" #define AR6003_HW_2_0_TCMD_FIRMWARE_FILE "athtcmd_ram.bin" #define AR6003_HW_2_0_PATCH_FILE "data.patch.bin" #define AR6003_HW_2_0_BOARD_DATA_FILE AR6003_HW_2_0_FW_DIR "/bdata.bin" #define AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE \ AR6003_HW_2_0_FW_DIR "/bdata.SD31.bin" /* AR6003 3.0 definitions */ #define AR6003_HW_2_1_1_VERSION 0x30000582 #define AR6003_HW_2_1_1_FW_DIR "ath6k/AR6003/hw2.1.1" #define AR6003_HW_2_1_1_OTP_FILE "otp.bin" #define AR6003_HW_2_1_1_FIRMWARE_FILE "athwlan.bin" #define AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE "athtcmd_ram.bin" #define AR6003_HW_2_1_1_UTF_FIRMWARE_FILE "utf.bin" #define AR6003_HW_2_1_1_TESTSCRIPT_FILE "nullTestFlow.bin" #define AR6003_HW_2_1_1_PATCH_FILE "data.patch.bin" #define AR6003_HW_2_1_1_BOARD_DATA_FILE AR6003_HW_2_1_1_FW_DIR "/bdata.bin" #define AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE \ AR6003_HW_2_1_1_FW_DIR "/bdata.SD31.bin" /* AR6004 1.0 definitions */ #define AR6004_HW_1_0_VERSION 0x30000623 #define AR6004_HW_1_0_FW_DIR "ath6k/AR6004/hw1.0" #define AR6004_HW_1_0_FIRMWARE_FILE "fw.ram.bin" #define AR6004_HW_1_0_BOARD_DATA_FILE AR6004_HW_1_0_FW_DIR "/bdata.bin" #define AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE \ AR6004_HW_1_0_FW_DIR "/bdata.DB132.bin" /* AR6004 1.1 definitions */ #define AR6004_HW_1_1_VERSION 0x30000001 #define AR6004_HW_1_1_FW_DIR "ath6k/AR6004/hw1.1" #define AR6004_HW_1_1_FIRMWARE_FILE "fw.ram.bin" #define AR6004_HW_1_1_BOARD_DATA_FILE AR6004_HW_1_1_FW_DIR "/bdata.bin" #define AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE \ AR6004_HW_1_1_FW_DIR "/bdata.DB132.bin" /* AR6004 1.2 definitions */ #define AR6004_HW_1_2_VERSION 0x300007e8 #define AR6004_HW_1_2_FW_DIR "ath6k/AR6004/hw1.2" #define AR6004_HW_1_2_FIRMWARE_FILE "fw.ram.bin" #define AR6004_HW_1_2_BOARD_DATA_FILE AR6004_HW_1_2_FW_DIR "/bdata.bin" #define AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE \ AR6004_HW_1_2_FW_DIR "/bdata.bin" /* Per STA data, used in AP mode */ #define STA_PS_AWAKE BIT(0) #define STA_PS_SLEEP BIT(1) #define STA_PS_POLLED BIT(2) #define STA_PS_APSD_TRIGGER BIT(3) #define STA_PS_APSD_EOSP BIT(4) /* HTC TX packet tagging definitions */ #define ATH6KL_CONTROL_PKT_TAG HTC_TX_PACKET_TAG_USER_DEFINED #define ATH6KL_DATA_PKT_TAG (ATH6KL_CONTROL_PKT_TAG + 1) #define AR6003_CUST_DATA_SIZE 16 #define AGGR_WIN_IDX(x, y) ((x) % (y)) #define AGGR_INCR_IDX(x, y) AGGR_WIN_IDX(((x) + 1), (y)) #define AGGR_DCRM_IDX(x, y) AGGR_WIN_IDX(((x) - 1), (y)) #define ATH6KL_MAX_SEQ_NO 0xFFF #define ATH6KL_NEXT_SEQ_NO(x) (((x) + 1) & ATH6KL_MAX_SEQ_NO) #define NUM_OF_TIDS 8 #define AGGR_SZ_DEFAULT 8 #define AGGR_WIN_SZ_MIN 2 #define AGGR_WIN_SZ_MAX 8 #define TID_WINDOW_SZ(_x) ((_x) << 1) #define AGGR_NUM_OF_FREE_NETBUFS 16 #define AGGR_RX_TIMEOUT 100 /* in ms */ #define WMI_TIMEOUT (2 * HZ) #define MBOX_YIELD_LIMIT 99 #define ATH6KL_DEFAULT_LISTEN_INTVAL 100 /* in TUs */ #define ATH6KL_DEFAULT_BMISS_TIME 1500 #define ATH6KL_MAX_WOW_LISTEN_INTL 300 /* in TUs */ #define ATH6KL_MAX_BMISS_TIME 5000 /* configuration lags */ /* * ATH6KL_CONF_IGNORE_ERP_BARKER: Ignore the barker premable in * ERP IE of beacon to determine the short premable support when * sending (Re)Assoc req. * ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN: Don't send the power * module state transition failure events which happen during * scan, to the host. */ #define ATH6KL_CONF_IGNORE_ERP_BARKER BIT(0) #define ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN BIT(1) #define ATH6KL_CONF_ENABLE_11N BIT(2) #define ATH6KL_CONF_ENABLE_TX_BURST BIT(3) #define ATH6KL_CONF_UART_DEBUG BIT(4) #define P2P_WILDCARD_SSID_LEN 7 /* DIRECT- */ enum wlan_low_pwr_state { WLAN_POWER_STATE_ON, WLAN_POWER_STATE_CUT_PWR, WLAN_POWER_STATE_DEEP_SLEEP, WLAN_POWER_STATE_WOW }; enum sme_state { SME_DISCONNECTED, SME_CONNECTING, SME_CONNECTED }; struct skb_hold_q { struct sk_buff *skb; bool is_amsdu; u16 seq_no; }; struct rxtid { bool aggr; bool timer_mon; u16 win_sz; u16 seq_next; u32 hold_q_sz; struct skb_hold_q *hold_q; struct sk_buff_head q; /* * lock mainly protects seq_next and hold_q. Movement of seq_next * needs to be protected between aggr_timeout() and * aggr_process_recv_frm(). hold_q will be holding the pending * reorder frames and it's access should also be protected. * Some of the other fields like hold_q_sz, win_sz and aggr are * initialized/reset when receiving addba/delba req, also while * deleting aggr state all the pending buffers are flushed before * resetting these fields, so there should not be any race in accessing * these fields. */ spinlock_t lock; }; struct rxtid_stats { u32 num_into_aggr; u32 num_dups; u32 num_oow; u32 num_mpdu; u32 num_amsdu; u32 num_delivered; u32 num_timeouts; u32 num_hole; u32 num_bar; }; struct aggr_info_conn { u8 aggr_sz; u8 timer_scheduled; struct timer_list timer; struct net_device *dev; struct rxtid rx_tid[NUM_OF_TIDS]; struct rxtid_stats stat[NUM_OF_TIDS]; struct aggr_info *aggr_info; }; struct aggr_info { struct aggr_info_conn *aggr_conn; struct sk_buff_head rx_amsdu_freeq; }; struct ath6kl_wep_key { u8 key_index; u8 key_len; u8 key[64]; }; #define ATH6KL_KEY_SEQ_LEN 8 struct ath6kl_key { u8 key[WLAN_MAX_KEY_LEN]; u8 key_len; u8 seq[ATH6KL_KEY_SEQ_LEN]; u8 seq_len; u32 cipher; }; struct ath6kl_node_mapping { u8 mac_addr[ETH_ALEN]; u8 ep_id; u8 tx_pend; }; struct ath6kl_cookie { struct sk_buff *skb; u32 map_no; struct htc_packet htc_pkt; struct ath6kl_cookie *arc_list_next; }; struct ath6kl_mgmt_buff { struct list_head list; u32 freq; u32 wait; u32 id; bool no_cck; size_t len; u8 buf[0]; }; struct ath6kl_sta { u16 sta_flags; u8 mac[ETH_ALEN]; u8 aid; u8 keymgmt; u8 ucipher; u8 auth; u8 wpa_ie[ATH6KL_MAX_IE]; struct sk_buff_head psq; /* protects psq, mgmt_psq, apsdq, and mgmt_psq_len fields */ spinlock_t psq_lock; struct list_head mgmt_psq; size_t mgmt_psq_len; u8 apsd_info; struct sk_buff_head apsdq; struct aggr_info_conn *aggr_conn; }; struct ath6kl_version { u32 target_ver; u32 wlan_ver; u32 abi_ver; }; struct ath6kl_bmi { u32 cmd_credits; bool done_sent; u8 *cmd_buf; u32 max_data_size; u32 max_cmd_size; }; struct target_stats { u64 tx_pkt; u64 tx_byte; u64 tx_ucast_pkt; u64 tx_ucast_byte; u64 tx_mcast_pkt; u64 tx_mcast_byte; u64 tx_bcast_pkt; u64 tx_bcast_byte; u64 tx_rts_success_cnt; u64 tx_pkt_per_ac[4]; u64 tx_err; u64 tx_fail_cnt; u64 tx_retry_cnt; u64 tx_mult_retry_cnt; u64 tx_rts_fail_cnt; u64 rx_pkt; u64 rx_byte; u64 rx_ucast_pkt; u64 rx_ucast_byte; u64 rx_mcast_pkt; u64 rx_mcast_byte; u64 rx_bcast_pkt; u64 rx_bcast_byte; u64 rx_frgment_pkt; u64 rx_err; u64 rx_crc_err; u64 rx_key_cache_miss; u64 rx_decrypt_err; u64 rx_dupl_frame; u64 tkip_local_mic_fail; u64 tkip_cnter_measures_invoked; u64 tkip_replays; u64 tkip_fmt_err; u64 ccmp_fmt_err; u64 ccmp_replays; u64 pwr_save_fail_cnt; u64 cs_bmiss_cnt; u64 cs_low_rssi_cnt; u64 cs_connect_cnt; u64 cs_discon_cnt; s32 tx_ucast_rate; s32 rx_ucast_rate; u32 lq_val; u32 wow_pkt_dropped; u16 wow_evt_discarded; s16 noise_floor_calib; s16 cs_rssi; s16 cs_ave_beacon_rssi; u8 cs_ave_beacon_snr; u8 cs_last_roam_msec; u8 cs_snr; u8 wow_host_pkt_wakeups; u8 wow_host_evt_wakeups; u32 arp_received; u32 arp_matched; u32 arp_replied; }; struct ath6kl_mbox_info { u32 htc_addr; u32 htc_ext_addr; u32 htc_ext_sz; u32 block_size; u32 gmbox_addr; u32 gmbox_sz; }; /* * 802.11i defines an extended IV for use with non-WEP ciphers. * When the EXTIV bit is set in the key id byte an additional * 4 bytes immediately follow the IV for TKIP. For CCMP the * EXTIV bit is likewise set but the 8 bytes represent the * CCMP header rather than IV+extended-IV. */ #define ATH6KL_KEYBUF_SIZE 16 #define ATH6KL_MICBUF_SIZE (8+8) /* space for both tx and rx */ #define ATH6KL_KEY_XMIT 0x01 #define ATH6KL_KEY_RECV 0x02 #define ATH6KL_KEY_DEFAULT 0x80 /* default xmit key */ /* Initial group key for AP mode */ struct ath6kl_req_key { bool valid; u8 key_index; int key_type; u8 key[WLAN_MAX_KEY_LEN]; u8 key_len; }; enum ath6kl_hif_type { ATH6KL_HIF_TYPE_SDIO, ATH6KL_HIF_TYPE_USB, }; enum ath6kl_htc_type { ATH6KL_HTC_TYPE_MBOX, ATH6KL_HTC_TYPE_PIPE, }; /* Max number of filters that hw supports */ #define ATH6K_MAX_MC_FILTERS_PER_LIST 7 struct ath6kl_mc_filter { struct list_head list; char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; }; struct ath6kl_htcap { bool ht_enable; u8 ampdu_factor; unsigned short cap_info; }; /* * Driver's maximum limit, note that some firmwares support only one vif * and the runtime (current) limit must be checked from ar->vif_max. */ #define ATH6KL_VIF_MAX 3 /* vif flags info */ enum ath6kl_vif_state { CONNECTED, CONNECT_PEND, WMM_ENABLED, NETQ_STOPPED, DTIM_EXPIRED, NETDEV_REGISTERED, CLEAR_BSSFILTER_ON_BEACON, DTIM_PERIOD_AVAIL, WLAN_ENABLED, STATS_UPDATE_PEND, HOST_SLEEP_MODE_CMD_PROCESSED, NETDEV_MCAST_ALL_ON, NETDEV_MCAST_ALL_OFF, }; struct ath6kl_vif { struct list_head list; struct wireless_dev wdev; struct net_device *ndev; struct ath6kl *ar; /* Lock to protect vif specific net_stats and flags */ spinlock_t if_lock; u8 fw_vif_idx; unsigned long flags; int ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 dot11_auth_mode; u8 auth_mode; u8 prwise_crypto; u8 prwise_crypto_len; u8 grp_crypto; u8 grp_crypto_len; u8 def_txkey_index; u8 next_mode; u8 nw_type; u8 bssid[ETH_ALEN]; u8 req_bssid[ETH_ALEN]; u16 ch_hint; u16 bss_ch; struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1]; struct aggr_info *aggr_cntxt; struct ath6kl_htcap htcap[IEEE80211_NUM_BANDS]; struct timer_list disconnect_timer; struct timer_list sched_scan_timer; struct cfg80211_scan_request *scan_req; enum sme_state sme_state; int reconnect_flag; u32 last_roc_id; u32 last_cancel_roc_id; u32 send_action_id; bool probe_req_report; u16 assoc_bss_beacon_int; u16 listen_intvl_t; u16 bmiss_time_t; u16 bg_scan_period; u8 assoc_bss_dtim_period; struct net_device_stats net_stats; struct target_stats target_stats; struct wmi_connect_cmd profile; struct list_head mc_filter; }; static inline struct ath6kl_vif *ath6kl_vif_from_wdev(struct wireless_dev *wdev) { return container_of(wdev, struct ath6kl_vif, wdev); } #define WOW_LIST_ID 0 #define WOW_HOST_REQ_DELAY 500 /* ms */ #define ATH6KL_SCHED_SCAN_RESULT_DELAY 5000 /* ms */ /* Flag info */ enum ath6kl_dev_state { WMI_ENABLED, WMI_READY, WMI_CTRL_EP_FULL, TESTMODE, DESTROY_IN_PROGRESS, SKIP_SCAN, ROAM_TBL_PEND, FIRST_BOOT, }; enum ath6kl_state { ATH6KL_STATE_OFF, ATH6KL_STATE_ON, ATH6KL_STATE_SUSPENDING, ATH6KL_STATE_RESUMING, ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, ATH6KL_STATE_SCHED_SCAN, }; struct ath6kl { struct device *dev; struct wiphy *wiphy; enum ath6kl_state state; unsigned int testmode; struct ath6kl_bmi bmi; const struct ath6kl_hif_ops *hif_ops; const struct ath6kl_htc_ops *htc_ops; struct wmi *wmi; int tx_pending[ENDPOINT_MAX]; int total_tx_data_pend; struct htc_target *htc_target; enum ath6kl_hif_type hif_type; void *hif_priv; struct list_head vif_list; /* Lock to avoid race in vif_list entries among add/del/traverse */ spinlock_t list_lock; u8 num_vif; unsigned int vif_max; u8 max_norm_iface; u8 avail_idx_map; /* * Protects at least amsdu_rx_buffer_queue, ath6kl_alloc_cookie() * calls, tx_pending and total_tx_data_pend. */ spinlock_t lock; struct semaphore sem; u8 lrssi_roam_threshold; struct ath6kl_version version; u32 target_type; u8 tx_pwr; struct ath6kl_node_mapping node_map[MAX_NODE_NUM]; u8 ibss_ps_enable; bool ibss_if_active; u8 node_num; u8 next_ep_id; struct ath6kl_cookie *cookie_list; u32 cookie_count; enum htc_endpoint_id ac2ep_map[WMM_NUM_AC]; bool ac_stream_active[WMM_NUM_AC]; u8 ac_stream_pri_map[WMM_NUM_AC]; u8 hiac_stream_active_pri; u8 ep2ac_map[ENDPOINT_MAX]; enum htc_endpoint_id ctrl_ep; struct ath6kl_htc_credit_info credit_state_info; u32 connect_ctrl_flags; u32 user_key_ctrl; u8 usr_bss_filter; struct ath6kl_sta sta_list[AP_MAX_NUM_STA]; u8 sta_list_index; struct ath6kl_req_key ap_mode_bkey; struct sk_buff_head mcastpsq; u32 want_ch_switch; /* * FIXME: protects access to mcastpsq but is actually useless as * all skbe_queue_*() functions provide serialisation themselves */ spinlock_t mcastpsq_lock; u8 intra_bss; struct wmi_ap_mode_stat ap_stats; u8 ap_country_code[3]; struct list_head amsdu_rx_buffer_queue; u8 rx_meta_ver; enum wlan_low_pwr_state wlan_pwr_state; u8 mac_addr[ETH_ALEN]; #define AR_MCAST_FILTER_MAC_ADDR_SIZE 4 struct { void *rx_report; size_t rx_report_len; } tm; struct ath6kl_hw { u32 id; const char *name; u32 dataset_patch_addr; u32 app_load_addr; u32 app_start_override_addr; u32 board_ext_data_addr; u32 reserved_ram_size; u32 board_addr; u32 refclk_hz; u32 uarttx_pin; u32 testscript_addr; enum wmi_phy_cap cap; u32 flags; struct ath6kl_hw_fw { const char *dir; const char *otp; const char *fw; const char *tcmd; const char *patch; const char *utf; const char *testscript; } fw; const char *fw_board; const char *fw_default_board; } hw; u16 conf_flags; u16 suspend_mode; u16 wow_suspend_mode; wait_queue_head_t event_wq; struct ath6kl_mbox_info mbox_info; struct ath6kl_cookie cookie_mem[MAX_COOKIE_NUM]; unsigned long flag; u8 *fw_board; size_t fw_board_len; u8 *fw_otp; size_t fw_otp_len; u8 *fw; size_t fw_len; u8 *fw_patch; size_t fw_patch_len; u8 *fw_testscript; size_t fw_testscript_len; unsigned int fw_api; unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN]; struct workqueue_struct *ath6kl_wq; struct dentry *debugfs_phy; bool p2p; bool wiphy_registered; #ifdef CONFIG_ATH6KL_DEBUG struct { struct sk_buff_head fwlog_queue; struct completion fwlog_completion; bool fwlog_open; u32 fwlog_mask; unsigned int dbgfs_diag_reg; u32 diag_reg_addr_wr; u32 diag_reg_val_wr; struct { unsigned int invalid_rate; } war_stats; u8 *roam_tbl; unsigned int roam_tbl_len; u8 keepalive; u8 disc_timeout; } debug; #endif /* CONFIG_ATH6KL_DEBUG */ }; static inline struct ath6kl *ath6kl_priv(struct net_device *dev) { return ((struct ath6kl_vif *) netdev_priv(dev))->ar; } static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar, u32 item_offset) { u32 addr = 0; if (ar->target_type == TARGET_TYPE_AR6003) addr = ATH6KL_AR6003_HI_START_ADDR + item_offset; else if (ar->target_type == TARGET_TYPE_AR6004) addr = ATH6KL_AR6004_HI_START_ADDR + item_offset; return addr; } int ath6kl_configure_target(struct ath6kl *ar); void ath6kl_detect_error(unsigned long ptr); void disconnect_timer_handler(unsigned long ptr); void init_netdev(struct net_device *dev); void ath6kl_cookie_init(struct ath6kl *ar); void ath6kl_cookie_cleanup(struct ath6kl *ar); void ath6kl_rx(struct htc_target *target, struct htc_packet *packet); void ath6kl_tx_complete(struct htc_target *context, struct list_head *packet_queue); enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, struct htc_packet *packet); void ath6kl_stop_txrx(struct ath6kl *ar); void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar); int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value); int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length); int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value); int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length); int ath6kl_read_fwlogs(struct ath6kl *ar); void ath6kl_init_profile_info(struct ath6kl_vif *vif); void ath6kl_tx_data_cleanup(struct ath6kl *ar); struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar); void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie); int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev); struct aggr_info *aggr_init(struct ath6kl_vif *vif); void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info, struct aggr_info_conn *aggr_conn); void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint); void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count); struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target, enum htc_endpoint_id endpoint, int len); void aggr_module_destroy(struct aggr_info *aggr_info); void aggr_reset_state(struct aggr_info_conn *aggr_conn); struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr); struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid); void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver, enum wmi_phy_cap cap); int ath6kl_control_tx(void *devt, struct sk_buff *skb, enum htc_endpoint_id eid); void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_int, u16 beacon_int, enum network_type net_type, u8 beacon_ie_len, u8 assoc_req_len, u8 assoc_resp_len, u8 *assoc_info); void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel); void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, u8 keymgmt, u8 ucipher, u8 auth, u8 assoc_req_len, u8 *assoc_info, u8 apsd_info); void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 prot_reason_status); void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast); void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr); void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status); void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len); void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active); enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac); void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid); void ath6kl_dtimexpiry_event(struct ath6kl_vif *vif); void ath6kl_disconnect(struct ath6kl_vif *vif); void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid); void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid, u16 seq_no, u8 win_sz); void ath6kl_wakeup_event(void *dev); void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, bool wait_fot_compltn, bool cold_reset); void ath6kl_init_control_info(struct ath6kl_vif *vif); struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar); void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready); int ath6kl_init_hw_start(struct ath6kl *ar); int ath6kl_init_hw_stop(struct ath6kl *ar); int ath6kl_init_fetch_firmwares(struct ath6kl *ar); int ath6kl_init_hw_params(struct ath6kl *ar); void ath6kl_check_wow_status(struct ath6kl *ar); void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb); void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe); struct ath6kl *ath6kl_core_create(struct device *dev); int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type); void ath6kl_core_cleanup(struct ath6kl *ar); void ath6kl_core_destroy(struct ath6kl *ar); #endif /* CORE_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/core.c0000644000175000017500000001754512026211315023536 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core.h" #include #include #include #include #include "debug.h" #include "hif-ops.h" #include "htc-ops.h" #include "cfg80211.h" unsigned int debug_mask; static unsigned int suspend_mode; static unsigned int wow_mode; static unsigned int uart_debug; static unsigned int ath6kl_p2p; static unsigned int testmode; module_param(debug_mask, uint, 0644); module_param(suspend_mode, uint, 0644); module_param(wow_mode, uint, 0644); module_param(uart_debug, uint, 0644); module_param(ath6kl_p2p, uint, 0644); module_param(testmode, uint, 0644); void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { ath6kl_htc_tx_complete(ar, skb); } EXPORT_SYMBOL(ath6kl_core_tx_complete); void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe) { ath6kl_htc_rx_complete(ar, skb, pipe); } EXPORT_SYMBOL(ath6kl_core_rx_complete); int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) { struct ath6kl_bmi_target_info targ_info; struct wireless_dev *wdev; int ret = 0, i; switch (htc_type) { case ATH6KL_HTC_TYPE_MBOX: ath6kl_htc_mbox_attach(ar); break; case ATH6KL_HTC_TYPE_PIPE: ath6kl_htc_pipe_attach(ar); break; default: WARN_ON(1); return -ENOMEM; } ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); if (!ar->ath6kl_wq) return -ENOMEM; ret = ath6kl_bmi_init(ar); if (ret) goto err_wq; /* * Turn on power to get hardware (target) version and leave power * on delibrately as we will boot the hardware anyway within few * seconds. */ ret = ath6kl_hif_power_on(ar); if (ret) goto err_bmi_cleanup; ret = ath6kl_bmi_get_target_info(ar, &targ_info); if (ret) goto err_power_off; ar->version.target_ver = le32_to_cpu(targ_info.version); ar->target_type = le32_to_cpu(targ_info.type); ar->wiphy->hw_version = le32_to_cpu(targ_info.version); ret = ath6kl_init_hw_params(ar); if (ret) goto err_power_off; ar->htc_target = ath6kl_htc_create(ar); if (!ar->htc_target) { ret = -ENOMEM; goto err_power_off; } ar->testmode = testmode; ret = ath6kl_init_fetch_firmwares(ar); if (ret) goto err_htc_cleanup; /* FIXME: we should free all firmwares in the error cases below */ /* Indicate that WMI is enabled (although not ready yet) */ set_bit(WMI_ENABLED, &ar->flag); ar->wmi = ath6kl_wmi_init(ar); if (!ar->wmi) { ath6kl_err("failed to initialize wmi\n"); ret = -EIO; goto err_htc_cleanup; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); /* setup access class priority mappings */ ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ ar->ac_stream_pri_map[WMM_AC_BE] = 1; ar->ac_stream_pri_map[WMM_AC_VI] = 2; ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ /* allocate some buffers that handle larger AMSDU frames */ ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); ath6kl_cookie_init(ar); ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; if (suspend_mode && suspend_mode >= WLAN_POWER_STATE_CUT_PWR && suspend_mode <= WLAN_POWER_STATE_WOW) ar->suspend_mode = suspend_mode; else ar->suspend_mode = 0; if (suspend_mode == WLAN_POWER_STATE_WOW && (wow_mode == WLAN_POWER_STATE_CUT_PWR || wow_mode == WLAN_POWER_STATE_DEEP_SLEEP)) ar->wow_suspend_mode = wow_mode; else ar->wow_suspend_mode = 0; if (uart_debug) ar->conf_flags |= ATH6KL_CONF_UART_DEBUG; set_bit(FIRST_BOOT, &ar->flag); ath6kl_debug_init(ar); ret = ath6kl_init_hw_start(ar); if (ret) { ath6kl_err("Failed to start hardware: %d\n", ret); goto err_rxbuf_cleanup; } /* give our connected endpoints some buffers */ ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); ret = ath6kl_cfg80211_init(ar); if (ret) goto err_rxbuf_cleanup; ret = ath6kl_debug_init_fs(ar); if (ret) { wiphy_unregister(ar->wiphy); goto err_rxbuf_cleanup; } for (i = 0; i < ar->vif_max; i++) ar->avail_idx_map |= BIT(i); rtnl_lock(); /* Add an initial station interface */ wdev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, INFRA_NETWORK); rtnl_unlock(); if (!wdev) { ath6kl_err("Failed to instantiate a network device\n"); ret = -ENOMEM; wiphy_unregister(ar->wiphy); goto err_rxbuf_cleanup; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", __func__, wdev->netdev->name, wdev->netdev, ar); return ret; err_rxbuf_cleanup: ath6kl_debug_cleanup(ar); ath6kl_htc_flush_rx_buf(ar->htc_target); ath6kl_cleanup_amsdu_rxbufs(ar); ath6kl_wmi_shutdown(ar->wmi); clear_bit(WMI_ENABLED, &ar->flag); ar->wmi = NULL; err_htc_cleanup: ath6kl_htc_cleanup(ar->htc_target); err_power_off: ath6kl_hif_power_off(ar); err_bmi_cleanup: ath6kl_bmi_cleanup(ar); err_wq: destroy_workqueue(ar->ath6kl_wq); return ret; } EXPORT_SYMBOL(ath6kl_core_init); struct ath6kl *ath6kl_core_create(struct device *dev) { struct ath6kl *ar; u8 ctr; ar = ath6kl_cfg80211_create(); if (!ar) return NULL; ar->p2p = !!ath6kl_p2p; ar->dev = dev; ar->vif_max = 1; ar->max_norm_iface = 1; spin_lock_init(&ar->lock); spin_lock_init(&ar->mcastpsq_lock); spin_lock_init(&ar->list_lock); init_waitqueue_head(&ar->event_wq); sema_init(&ar->sem, 1); INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); INIT_LIST_HEAD(&ar->vif_list); clear_bit(WMI_ENABLED, &ar->flag); clear_bit(SKIP_SCAN, &ar->flag); clear_bit(DESTROY_IN_PROGRESS, &ar->flag); ar->tx_pwr = 0; ar->intra_bss = 1; ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; ar->state = ATH6KL_STATE_OFF; memset((u8 *)ar->sta_list, 0, AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); /* Init the PS queues */ for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { spin_lock_init(&ar->sta_list[ctr].psq_lock); skb_queue_head_init(&ar->sta_list[ctr].psq); skb_queue_head_init(&ar->sta_list[ctr].apsdq); ar->sta_list[ctr].mgmt_psq_len = 0; INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq); ar->sta_list[ctr].aggr_conn = kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL); if (!ar->sta_list[ctr].aggr_conn) { ath6kl_err("Failed to allocate memory for sta aggregation information\n"); ath6kl_core_destroy(ar); return NULL; } } skb_queue_head_init(&ar->mcastpsq); memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); return ar; } EXPORT_SYMBOL(ath6kl_core_create); void ath6kl_core_cleanup(struct ath6kl *ar) { ath6kl_hif_power_off(ar); destroy_workqueue(ar->ath6kl_wq); if (ar->htc_target) ath6kl_htc_cleanup(ar->htc_target); ath6kl_cookie_cleanup(ar); ath6kl_cleanup_amsdu_rxbufs(ar); ath6kl_bmi_cleanup(ar); ath6kl_debug_cleanup(ar); kfree(ar->fw_board); kfree(ar->fw_otp); vfree(ar->fw); kfree(ar->fw_patch); kfree(ar->fw_testscript); ath6kl_cfg80211_cleanup(ar); } EXPORT_SYMBOL(ath6kl_core_cleanup); void ath6kl_core_destroy(struct ath6kl *ar) { ath6kl_cfg80211_destroy(ar); } EXPORT_SYMBOL(ath6kl_core_destroy); MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Core module for AR600x SDIO and USB devices."); MODULE_LICENSE("Dual BSD/GPL"); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/common.h0000644000175000017500000000462212026211315024073 0ustar mcgrofmcgrof/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef COMMON_H #define COMMON_H #include #define ATH6KL_MAX_IE 256 extern __printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...); /* * Reflects the version of binary interface exposed by ATH6KL target * firmware. Needs to be incremented by 1 for any change in the firmware * that requires upgrade of the driver on the host side for the change to * work correctly */ #define ATH6KL_ABI_VERSION 1 #define SIGNAL_QUALITY_METRICS_NUM_MAX 2 enum { SIGNAL_QUALITY_METRICS_SNR = 0, SIGNAL_QUALITY_METRICS_RSSI, SIGNAL_QUALITY_METRICS_ALL, }; /* * Data Path */ #define WMI_MAX_TX_DATA_FRAME_LENGTH \ (1500 + sizeof(struct wmi_data_hdr) + \ sizeof(struct ethhdr) + \ sizeof(struct ath6kl_llc_snap_hdr)) /* An AMSDU frame */ /* The MAX AMSDU length of AR6003 is 3839 */ #define WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH \ (3840 + sizeof(struct wmi_data_hdr) + \ sizeof(struct ethhdr) + \ sizeof(struct ath6kl_llc_snap_hdr)) #define EPPING_ALIGNMENT_PAD \ (((sizeof(struct htc_frame_hdr) + 3) & (~0x3)) \ - sizeof(struct htc_frame_hdr)) struct ath6kl_llc_snap_hdr { u8 dsap; u8 ssap; u8 cntl; u8 org_code[3]; __be16 eth_type; } __packed; enum crypto_type { NONE_CRYPT = 0x01, WEP_CRYPT = 0x02, TKIP_CRYPT = 0x04, AES_CRYPT = 0x08, WAPI_CRYPT = 0x10, }; struct htc_endpoint_credit_dist; struct ath6kl; struct ath6kl_htcap; enum htc_credit_dist_reason; struct ath6kl_htc_credit_info; struct sk_buff *ath6kl_buf_alloc(int size); #endif /* COMMON_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/cfg80211.h0000644000175000017500000000504112026211315023732 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ATH6KL_CFG80211_H #define ATH6KL_CFG80211_H enum ath6kl_cfg_suspend_mode { ATH6KL_CFG_SUSPEND_DEEPSLEEP, ATH6KL_CFG_SUSPEND_CUTPOWER, ATH6KL_CFG_SUSPEND_WOW, ATH6KL_CFG_SUSPEND_SCHED_SCAN, }; struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type); void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, enum wmi_phy_mode mode); void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_intvl, u16 beacon_intvl, enum network_type nw_type, u8 beacon_ie_len, u8 assoc_req_len, u8 assoc_resp_len, u8 *assoc_info); void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 proto_reason); void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast); int ath6kl_cfg80211_suspend(struct ath6kl *ar, enum ath6kl_cfg_suspend_mode mode, struct cfg80211_wowlan *wow); int ath6kl_cfg80211_resume(struct ath6kl *ar); void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif); void ath6kl_cfg80211_stop(struct ath6kl_vif *vif); void ath6kl_cfg80211_stop_all(struct ath6kl *ar); int ath6kl_cfg80211_init(struct ath6kl *ar); void ath6kl_cfg80211_cleanup(struct ath6kl *ar); struct ath6kl *ath6kl_cfg80211_create(void); void ath6kl_cfg80211_destroy(struct ath6kl *ar); /* TODO: remove this once ath6kl_vif_cleanup() is moved to cfg80211.c */ void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable); #endif /* ATH6KL_CFG80211_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/bmi.h0000644000175000017500000001763612026211315023363 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef BMI_H #define BMI_H /* * Bootloader Messaging Interface (BMI) * * BMI is a very simple messaging interface used during initialization * to read memory, write memory, execute code, and to define an * application entry PC. * * It is used to download an application to ATH6KL, to provide * patches to code that is already resident on ATH6KL, and generally * to examine and modify state. The Host has an opportunity to use * BMI only once during bootup. Once the Host issues a BMI_DONE * command, this opportunity ends. * * The Host writes BMI requests to mailbox0, and reads BMI responses * from mailbox0. BMI requests all begin with a command * (see below for specific commands), and are followed by * command-specific data. * * Flow control: * The Host can only issue a command once the Target gives it a * "BMI Command Credit", using ATH6KL Counter #4. As soon as the * Target has completed a command, it issues another BMI Command * Credit (so the Host can issue the next command). * * BMI handles all required Target-side cache flushing. */ /* BMI Commands */ #define BMI_NO_COMMAND 0 #define BMI_DONE 1 /* * Semantics: Host is done using BMI * Request format: * u32 command (BMI_DONE) * Response format: none */ #define BMI_READ_MEMORY 2 /* * Semantics: Host reads ATH6KL memory * Request format: * u32 command (BMI_READ_MEMORY) * u32 address * u32 length, at most BMI_DATASZ_MAX * Response format: * u8 data[length] */ #define BMI_WRITE_MEMORY 3 /* * Semantics: Host writes ATH6KL memory * Request format: * u32 command (BMI_WRITE_MEMORY) * u32 address * u32 length, at most BMI_DATASZ_MAX * u8 data[length] * Response format: none */ #define BMI_EXECUTE 4 /* * Semantics: Causes ATH6KL to execute code * Request format: * u32 command (BMI_EXECUTE) * u32 address * u32 parameter * Response format: * u32 return value */ #define BMI_SET_APP_START 5 /* * Semantics: Set Target application starting address * Request format: * u32 command (BMI_SET_APP_START) * u32 address * Response format: none */ #define BMI_READ_SOC_REGISTER 6 /* * Semantics: Read a 32-bit Target SOC register. * Request format: * u32 command (BMI_READ_REGISTER) * u32 address * Response format: * u32 value */ #define BMI_WRITE_SOC_REGISTER 7 /* * Semantics: Write a 32-bit Target SOC register. * Request format: * u32 command (BMI_WRITE_REGISTER) * u32 address * u32 value * * Response format: none */ #define BMI_GET_TARGET_ID 8 #define BMI_GET_TARGET_INFO 8 /* * Semantics: Fetch the 4-byte Target information * Request format: * u32 command (BMI_GET_TARGET_ID/INFO) * Response format1 (old firmware): * u32 TargetVersionID * Response format2 (newer firmware): * u32 TARGET_VERSION_SENTINAL * struct bmi_target_info; */ #define TARGET_VERSION_SENTINAL 0xffffffff #define TARGET_TYPE_AR6003 3 #define TARGET_TYPE_AR6004 5 #define BMI_ROMPATCH_INSTALL 9 /* * Semantics: Install a ROM Patch. * Request format: * u32 command (BMI_ROMPATCH_INSTALL) * u32 Target ROM Address * u32 Target RAM Address or Value (depending on Target Type) * u32 Size, in bytes * u32 Activate? 1-->activate; * 0-->install but do not activate * Response format: * u32 PatchID */ #define BMI_ROMPATCH_UNINSTALL 10 /* * Semantics: Uninstall a previously-installed ROM Patch, * automatically deactivating, if necessary. * Request format: * u32 command (BMI_ROMPATCH_UNINSTALL) * u32 PatchID * * Response format: none */ #define BMI_ROMPATCH_ACTIVATE 11 /* * Semantics: Activate a list of previously-installed ROM Patches. * Request format: * u32 command (BMI_ROMPATCH_ACTIVATE) * u32 rompatch_count * u32 PatchID[rompatch_count] * * Response format: none */ #define BMI_ROMPATCH_DEACTIVATE 12 /* * Semantics: Deactivate a list of active ROM Patches. * Request format: * u32 command (BMI_ROMPATCH_DEACTIVATE) * u32 rompatch_count * u32 PatchID[rompatch_count] * * Response format: none */ #define BMI_LZ_STREAM_START 13 /* * Semantics: Begin an LZ-compressed stream of input * which is to be uncompressed by the Target to an * output buffer at address. The output buffer must * be sufficiently large to hold the uncompressed * output from the compressed input stream. This BMI * command should be followed by a series of 1 or more * BMI_LZ_DATA commands. * u32 command (BMI_LZ_STREAM_START) * u32 address * Note: Not supported on all versions of ROM firmware. */ #define BMI_LZ_DATA 14 /* * Semantics: Host writes ATH6KL memory with LZ-compressed * data which is uncompressed by the Target. This command * must be preceded by a BMI_LZ_STREAM_START command. A series * of BMI_LZ_DATA commands are considered part of a single * input stream until another BMI_LZ_STREAM_START is issued. * Request format: * u32 command (BMI_LZ_DATA) * u32 length (of compressed data), * at most BMI_DATASZ_MAX * u8 CompressedData[length] * Response format: none * Note: Not supported on all versions of ROM firmware. */ #define BMI_COMMUNICATION_TIMEOUT 1000 /* in msec */ struct ath6kl; struct ath6kl_bmi_target_info { __le32 byte_count; /* size of this structure */ __le32 version; /* target version id */ __le32 type; /* target type */ } __packed; #define ath6kl_bmi_write_hi32(ar, item, val) \ ({ \ u32 addr; \ __le32 v; \ \ addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \ v = cpu_to_le32(val); \ ath6kl_bmi_write(ar, addr, (u8 *) &v, sizeof(v)); \ }) #define ath6kl_bmi_read_hi32(ar, item, val) \ ({ \ u32 addr, *check_type = val; \ __le32 tmp; \ int ret; \ \ (void) (check_type == val); \ addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \ ret = ath6kl_bmi_read(ar, addr, (u8 *) &tmp, 4); \ *val = le32_to_cpu(tmp); \ ret; \ }) int ath6kl_bmi_init(struct ath6kl *ar); void ath6kl_bmi_cleanup(struct ath6kl *ar); void ath6kl_bmi_reset(struct ath6kl *ar); int ath6kl_bmi_done(struct ath6kl *ar); int ath6kl_bmi_get_target_info(struct ath6kl *ar, struct ath6kl_bmi_target_info *targ_info); int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len); int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len); int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param); int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr); int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param); int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param); int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len); int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr); int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len); #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/bmi.c0000644000175000017500000003152612026211315023350 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core.h" #include "hif-ops.h" #include "target.h" #include "debug.h" int ath6kl_bmi_done(struct ath6kl *ar) { int ret; u32 cid = BMI_DONE; if (ar->bmi.done_sent) { ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n"); return 0; } ar->bmi.done_sent = true; ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid)); if (ret) { ath6kl_err("Unable to send bmi done: %d\n", ret); return ret; } return 0; } int ath6kl_bmi_get_target_info(struct ath6kl *ar, struct ath6kl_bmi_target_info *targ_info) { int ret; u32 cid = BMI_GET_TARGET_INFO; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid)); if (ret) { ath6kl_err("Unable to send get target info: %d\n", ret); return ret; } if (ar->hif_type == ATH6KL_HIF_TYPE_USB) { ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info, sizeof(*targ_info)); } else { ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version, sizeof(targ_info->version)); } if (ret) { ath6kl_err("Unable to recv target info: %d\n", ret); return ret; } if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { /* Determine how many bytes are in the Target's targ_info */ ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->byte_count, sizeof(targ_info->byte_count)); if (ret) { ath6kl_err("unable to read target info byte count: %d\n", ret); return ret; } /* * The target's targ_info doesn't match the host's targ_info. * We need to do some backwards compatibility to make this work. */ if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { WARN_ON(1); return -EINVAL; } /* Read the remainder of the targ_info */ ret = ath6kl_hif_bmi_read(ar, ((u8 *)targ_info) + sizeof(targ_info->byte_count), sizeof(*targ_info) - sizeof(targ_info->byte_count)); if (ret) { ath6kl_err("Unable to read target info (%d bytes): %d\n", targ_info->byte_count, ret); return ret; } } ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", targ_info->version, targ_info->type); return 0; } int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) { u32 cid = BMI_READ_MEMORY; int ret; u32 offset; u32 len_remain, rx_len; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read memory: device: addr: 0x%x, len: %d\n", addr, len); len_remain = len; while (len_remain) { rx_len = (len_remain < ar->bmi.max_data_size) ? len_remain : ar->bmi.max_data_size; offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len)); offset += sizeof(len); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len); if (ret) { ath6kl_err("Unable to read from the device: %d\n", ret); return ret; } memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); len_remain -= rx_len; addr += rx_len; } return 0; } int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) { u32 cid = BMI_WRITE_MEMORY; int ret; u32 offset; u32 len_remain, tx_len; const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); u8 aligned_buf[400]; u8 *src; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf))) return -E2BIG; memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi write memory: addr: 0x%x, len: %d\n", addr, len); len_remain = len; while (len_remain) { src = &buf[len - len_remain]; if (len_remain < (ar->bmi.max_data_size - header)) { if (len_remain & 3) { /* align it with 4 bytes */ len_remain = len_remain + (4 - (len_remain & 3)); memcpy(aligned_buf, src, len_remain); src = aligned_buf; } tx_len = len_remain; } else { tx_len = (ar->bmi.max_data_size - header); } offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); offset += sizeof(tx_len); memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); offset += tx_len; ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } len_remain -= tx_len; addr += tx_len; } return 0; } int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) { u32 cid = BMI_EXECUTE; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr) + sizeof(param); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", addr, *param); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); offset += sizeof(*param); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); if (ret) { ath6kl_err("Unable to read from the device: %d\n", ret); return ret; } memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); return 0; } int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) { u32 cid = BMI_SET_APP_START; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } return 0; } int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) { u32 cid = BMI_READ_SOC_REGISTER; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); if (ret) { ath6kl_err("Unable to read from the device: %d\n", ret); return ret; } memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); return 0; } int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) { u32 cid = BMI_WRITE_SOC_REGISTER; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr) + sizeof(param); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi write SOC reg: addr: 0x%x, param: %d\n", addr, param); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); offset += sizeof(param); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } return 0; } int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) { u32 cid = BMI_LZ_DATA; int ret; u32 offset; u32 len_remain, tx_len; const u32 header = sizeof(cid) + sizeof(len); u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = ar->bmi.max_data_size + header; if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", len); len_remain = len; while (len_remain) { tx_len = (len_remain < (ar->bmi.max_data_size - header)) ? len_remain : (ar->bmi.max_data_size - header); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); offset += sizeof(tx_len); memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], tx_len); offset += tx_len; ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } len_remain -= tx_len; } return 0; } int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) { u32 cid = BMI_LZ_STREAM_START; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi LZ stream start: addr: 0x%x)\n", addr); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to start LZ stream to the device: %d\n", ret); return ret; } return 0; } int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) { int ret; u32 last_word = 0; u32 last_word_offset = len & ~0x3; u32 unaligned_bytes = len & 0x3; ret = ath6kl_bmi_lz_stream_start(ar, addr); if (ret) return ret; if (unaligned_bytes) { /* copy the last word into a zero padded buffer */ memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); } ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); if (ret) return ret; if (unaligned_bytes) ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); if (!ret) { /* Close compressed stream and open a new (fake) one. * This serves mainly to flush Target caches. */ ret = ath6kl_bmi_lz_stream_start(ar, 0x00); } return ret; } void ath6kl_bmi_reset(struct ath6kl *ar) { ar->bmi.done_sent = false; } int ath6kl_bmi_init(struct ath6kl *ar) { if (WARN_ON(ar->bmi.max_data_size == 0)) return -EINVAL; /* cmd + addr + len + data_size */ ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3); ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_ATOMIC); if (!ar->bmi.cmd_buf) return -ENOMEM; return 0; } void ath6kl_bmi_cleanup(struct ath6kl *ar) { kfree(ar->bmi.cmd_buf); ar->bmi.cmd_buf = NULL; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/Makefile0000644000175000017500000000300212026211315024061 0ustar mcgrofmcgrof#------------------------------------------------------------------------------ # Copyright (c) 2004-2011 Atheros Communications Inc. # Copyright (c) 2011-2012 Qualcomm Atheros, Inc. # All rights reserved. # # # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # # # Author(s): ="Atheros" #------------------------------------------------------------------------------ obj-$(CONFIG_ATH6KL) += ath6kl_core.o ath6kl_core-y += debug.o ath6kl_core-y += hif.o ath6kl_core-y += htc_mbox.o ath6kl_core-y += htc_pipe.o ath6kl_core-y += bmi.o ath6kl_core-y += cfg80211.o ath6kl_core-y += init.o ath6kl_core-y += main.o ath6kl_core-y += txrx.o ath6kl_core-y += wmi.o ath6kl_core-y += core.o ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o ath6kl_sdio-y += sdio.o obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o ath6kl_usb-y += usb.o compat-drivers-2012-09-18/drivers/net/wireless/ath/ath6kl/Kconfig0000644000175000017500000000166512026211315023741 0ustar mcgrofmcgrofconfig ATH6KL tristate "Atheros mobile chipsets support" config ATH6KL_SDIO tristate "Atheros ath6kl SDIO support" depends on ATH6KL depends on MMC depends on CFG80211 ---help--- This module adds support for wireless adapters based on Atheros AR6003 and AR6004 chipsets running over SDIO. If you choose to build it as a module, it will be called ath6kl_sdio. Please note that AR6002 and AR6001 are not supported by this driver. config ATH6KL_USB tristate "Atheros ath6kl USB support" depends on ATH6KL depends on USB depends on CFG80211 depends on EXPERIMENTAL ---help--- This module adds support for wireless adapters based on Atheros AR6004 chipset running over USB. This is still under implementation and it isn't functional. If you choose to build it as a module, it will be called ath6kl_usb. config ATH6KL_DEBUG bool "Atheros ath6kl debugging" depends on ATH6KL ---help--- Enables debug support compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/0000755000175000017500000000000012026211315022251 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/debug.c0000644000175000017500000007555112026211315023520 0ustar mcgrofmcgrof/* * Copyright (c) 2007-2008 Bruno Randolf * * This file is free software: you may copy, redistribute and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 2 of the License, or (at your * option) any later version. * * This file 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * Copyright (c) 2004-2005 Atheros Communications, Inc. * Copyright (c) 2006 Devicescape Software, Inc. * Copyright (c) 2007 Jiri Slaby * Copyright (c) 2007 Luis R. Rodriguez * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include "debug.h" #include "ath5k.h" #include "reg.h" #include "base.h" static unsigned int ath5k_debug; module_param_named(debug, ath5k_debug, uint, 0); /* debugfs: registers */ struct reg { const char *name; int addr; }; #define REG_STRUCT_INIT(r) { #r, r } /* just a few random registers, might want to add more */ static const struct reg regs[] = { REG_STRUCT_INIT(AR5K_CR), REG_STRUCT_INIT(AR5K_RXDP), REG_STRUCT_INIT(AR5K_CFG), REG_STRUCT_INIT(AR5K_IER), REG_STRUCT_INIT(AR5K_BCR), REG_STRUCT_INIT(AR5K_RTSD0), REG_STRUCT_INIT(AR5K_RTSD1), REG_STRUCT_INIT(AR5K_TXCFG), REG_STRUCT_INIT(AR5K_RXCFG), REG_STRUCT_INIT(AR5K_RXJLA), REG_STRUCT_INIT(AR5K_MIBC), REG_STRUCT_INIT(AR5K_TOPS), REG_STRUCT_INIT(AR5K_RXNOFRM), REG_STRUCT_INIT(AR5K_TXNOFRM), REG_STRUCT_INIT(AR5K_RPGTO), REG_STRUCT_INIT(AR5K_RFCNT), REG_STRUCT_INIT(AR5K_MISC), REG_STRUCT_INIT(AR5K_QCUDCU_CLKGT), REG_STRUCT_INIT(AR5K_ISR), REG_STRUCT_INIT(AR5K_PISR), REG_STRUCT_INIT(AR5K_SISR0), REG_STRUCT_INIT(AR5K_SISR1), REG_STRUCT_INIT(AR5K_SISR2), REG_STRUCT_INIT(AR5K_SISR3), REG_STRUCT_INIT(AR5K_SISR4), REG_STRUCT_INIT(AR5K_IMR), REG_STRUCT_INIT(AR5K_PIMR), REG_STRUCT_INIT(AR5K_SIMR0), REG_STRUCT_INIT(AR5K_SIMR1), REG_STRUCT_INIT(AR5K_SIMR2), REG_STRUCT_INIT(AR5K_SIMR3), REG_STRUCT_INIT(AR5K_SIMR4), REG_STRUCT_INIT(AR5K_DCM_ADDR), REG_STRUCT_INIT(AR5K_DCCFG), REG_STRUCT_INIT(AR5K_CCFG), REG_STRUCT_INIT(AR5K_CPC0), REG_STRUCT_INIT(AR5K_CPC1), REG_STRUCT_INIT(AR5K_CPC2), REG_STRUCT_INIT(AR5K_CPC3), REG_STRUCT_INIT(AR5K_CPCOVF), REG_STRUCT_INIT(AR5K_RESET_CTL), REG_STRUCT_INIT(AR5K_SLEEP_CTL), REG_STRUCT_INIT(AR5K_INTPEND), REG_STRUCT_INIT(AR5K_SFR), REG_STRUCT_INIT(AR5K_PCICFG), REG_STRUCT_INIT(AR5K_GPIOCR), REG_STRUCT_INIT(AR5K_GPIODO), REG_STRUCT_INIT(AR5K_SREV), }; static void *reg_start(struct seq_file *seq, loff_t *pos) { return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL; } static void reg_stop(struct seq_file *seq, void *p) { /* nothing to do */ } static void *reg_next(struct seq_file *seq, void *p, loff_t *pos) { ++*pos; return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL; } static int reg_show(struct seq_file *seq, void *p) { struct ath5k_hw *ah = seq->private; struct reg *r = p; seq_printf(seq, "%-25s0x%08x\n", r->name, ath5k_hw_reg_read(ah, r->addr)); return 0; } static const struct seq_operations register_seq_ops = { .start = reg_start, .next = reg_next, .stop = reg_stop, .show = reg_show }; static int open_file_registers(struct inode *inode, struct file *file) { struct seq_file *s; int res; res = seq_open(file, ®ister_seq_ops); if (res == 0) { s = file->private_data; s->private = inode->i_private; } return res; } static const struct file_operations fops_registers = { .open = open_file_registers, .read = seq_read, .llseek = seq_lseek, .release = seq_release, .owner = THIS_MODULE, }; /* debugfs: beacons */ static ssize_t read_file_beacon(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[500]; unsigned int len = 0; unsigned int v; u64 tsf; v = ath5k_hw_reg_read(ah, AR5K_BEACON); len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tintval: %d\tTIM: 0x%x\n", "AR5K_BEACON", v, v & AR5K_BEACON_PERIOD, (v & AR5K_BEACON_TIM) >> AR5K_BEACON_TIM_S); len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\n", "AR5K_LAST_TSTP", ath5k_hw_reg_read(ah, AR5K_LAST_TSTP)); len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\n\n", "AR5K_BEACON_CNT", ath5k_hw_reg_read(ah, AR5K_BEACON_CNT)); v = ath5k_hw_reg_read(ah, AR5K_TIMER0); len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n", "AR5K_TIMER0 (TBTT)", v, v); v = ath5k_hw_reg_read(ah, AR5K_TIMER1); len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n", "AR5K_TIMER1 (DMA)", v, v >> 3); v = ath5k_hw_reg_read(ah, AR5K_TIMER2); len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n", "AR5K_TIMER2 (SWBA)", v, v >> 3); v = ath5k_hw_reg_read(ah, AR5K_TIMER3); len += snprintf(buf + len, sizeof(buf) - len, "%-24s0x%08x\tTU: %08x\n", "AR5K_TIMER3 (ATIM)", v, v); tsf = ath5k_hw_get_tsf64(ah); len += snprintf(buf + len, sizeof(buf) - len, "TSF\t\t0x%016llx\tTU: %08x\n", (unsigned long long)tsf, TSF_TO_TU(tsf)); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_beacon(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[20]; if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) return -EFAULT; if (strncmp(buf, "disable", 7) == 0) { AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); pr_info("debugfs disable beacons\n"); } else if (strncmp(buf, "enable", 6) == 0) { AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); pr_info("debugfs enable beacons\n"); } return count; } static const struct file_operations fops_beacon = { .read = read_file_beacon, .write = write_file_beacon, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; /* debugfs: reset */ static ssize_t write_file_reset(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "debug file triggered reset\n"); ieee80211_queue_work(ah->hw, &ah->reset_work); return count; } static const struct file_operations fops_reset = { .write = write_file_reset, .open = simple_open, .owner = THIS_MODULE, .llseek = noop_llseek, }; /* debugfs: debug level */ static const struct { enum ath5k_debug_level level; const char *name; const char *desc; } dbg_info[] = { { ATH5K_DEBUG_RESET, "reset", "reset and initialization" }, { ATH5K_DEBUG_INTR, "intr", "interrupt handling" }, { ATH5K_DEBUG_MODE, "mode", "mode init/setup" }, { ATH5K_DEBUG_XMIT, "xmit", "basic xmit operation" }, { ATH5K_DEBUG_BEACON, "beacon", "beacon handling" }, { ATH5K_DEBUG_CALIBRATE, "calib", "periodic calibration" }, { ATH5K_DEBUG_TXPOWER, "txpower", "transmit power setting" }, { ATH5K_DEBUG_LED, "led", "LED management" }, { ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" }, { ATH5K_DEBUG_DMA, "dma", "dma start/stop" }, { ATH5K_DEBUG_ANI, "ani", "adaptive noise immunity" }, { ATH5K_DEBUG_DESC, "desc", "descriptor chains" }, { ATH5K_DEBUG_ANY, "all", "show all debug levels" }, }; static ssize_t read_file_debug(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[700]; unsigned int len = 0; unsigned int i; len += snprintf(buf + len, sizeof(buf) - len, "DEBUG LEVEL: 0x%08x\n\n", ah->debug.level); for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) { len += snprintf(buf + len, sizeof(buf) - len, "%10s %c 0x%08x - %s\n", dbg_info[i].name, ah->debug.level & dbg_info[i].level ? '+' : ' ', dbg_info[i].level, dbg_info[i].desc); } len += snprintf(buf + len, sizeof(buf) - len, "%10s %c 0x%08x - %s\n", dbg_info[i].name, ah->debug.level == dbg_info[i].level ? '+' : ' ', dbg_info[i].level, dbg_info[i].desc); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_debug(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; unsigned int i; char buf[20]; if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) return -EFAULT; for (i = 0; i < ARRAY_SIZE(dbg_info); i++) { if (strncmp(buf, dbg_info[i].name, strlen(dbg_info[i].name)) == 0) { ah->debug.level ^= dbg_info[i].level; /* toggle bit */ break; } } return count; } static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; /* debugfs: antenna */ static ssize_t read_file_antenna(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[700]; unsigned int len = 0; unsigned int i; unsigned int v; len += snprintf(buf + len, sizeof(buf) - len, "antenna mode\t%d\n", ah->ah_ant_mode); len += snprintf(buf + len, sizeof(buf) - len, "default antenna\t%d\n", ah->ah_def_ant); len += snprintf(buf + len, sizeof(buf) - len, "tx antenna\t%d\n", ah->ah_tx_ant); len += snprintf(buf + len, sizeof(buf) - len, "\nANTENNA\t\tRX\tTX\n"); for (i = 1; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) { len += snprintf(buf + len, sizeof(buf) - len, "[antenna %d]\t%d\t%d\n", i, ah->stats.antenna_rx[i], ah->stats.antenna_tx[i]); } len += snprintf(buf + len, sizeof(buf) - len, "[invalid]\t%d\t%d\n", ah->stats.antenna_rx[0], ah->stats.antenna_tx[0]); v = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA); len += snprintf(buf + len, sizeof(buf) - len, "\nAR5K_DEFAULT_ANTENNA\t0x%08x\n", v); v = ath5k_hw_reg_read(ah, AR5K_STA_ID1); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_STA_ID1_DEFAULT_ANTENNA\t%d\n", (v & AR5K_STA_ID1_DEFAULT_ANTENNA) != 0); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_STA_ID1_DESC_ANTENNA\t%d\n", (v & AR5K_STA_ID1_DESC_ANTENNA) != 0); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_STA_ID1_RTS_DEF_ANTENNA\t%d\n", (v & AR5K_STA_ID1_RTS_DEF_ANTENNA) != 0); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_STA_ID1_SELFGEN_DEF_ANT\t%d\n", (v & AR5K_STA_ID1_SELFGEN_DEF_ANT) != 0); v = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL); len += snprintf(buf + len, sizeof(buf) - len, "\nAR5K_PHY_AGCCTL_OFDM_DIV_DIS\t%d\n", (v & AR5K_PHY_AGCCTL_OFDM_DIV_DIS) != 0); v = ath5k_hw_reg_read(ah, AR5K_PHY_RESTART); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_PHY_RESTART_DIV_GC\t\t%x\n", (v & AR5K_PHY_RESTART_DIV_GC) >> AR5K_PHY_RESTART_DIV_GC_S); v = ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ANT_DIV); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_PHY_FAST_ANT_DIV_EN\t%d\n", (v & AR5K_PHY_FAST_ANT_DIV_EN) != 0); v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_0); len += snprintf(buf + len, sizeof(buf) - len, "\nAR5K_PHY_ANT_SWITCH_TABLE_0\t0x%08x\n", v); v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_1); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_PHY_ANT_SWITCH_TABLE_1\t0x%08x\n", v); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_antenna(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; unsigned int i; char buf[20]; if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) return -EFAULT; if (strncmp(buf, "diversity", 9) == 0) { ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); pr_info("debug: enable diversity\n"); } else if (strncmp(buf, "fixed-a", 7) == 0) { ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A); pr_info("debug: fixed antenna A\n"); } else if (strncmp(buf, "fixed-b", 7) == 0) { ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B); pr_info("debug: fixed antenna B\n"); } else if (strncmp(buf, "clear", 5) == 0) { for (i = 0; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) { ah->stats.antenna_rx[i] = 0; ah->stats.antenna_tx[i] = 0; } pr_info("debug: cleared antenna stats\n"); } return count; } static const struct file_operations fops_antenna = { .read = read_file_antenna, .write = write_file_antenna, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; /* debugfs: misc */ static ssize_t read_file_misc(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[700]; unsigned int len = 0; u32 filt = ath5k_hw_get_rx_filter(ah); len += snprintf(buf + len, sizeof(buf) - len, "bssid-mask: %pM\n", ah->bssidmask); len += snprintf(buf + len, sizeof(buf) - len, "filter-flags: 0x%x ", filt); if (filt & AR5K_RX_FILTER_UCAST) len += snprintf(buf + len, sizeof(buf) - len, " UCAST"); if (filt & AR5K_RX_FILTER_MCAST) len += snprintf(buf + len, sizeof(buf) - len, " MCAST"); if (filt & AR5K_RX_FILTER_BCAST) len += snprintf(buf + len, sizeof(buf) - len, " BCAST"); if (filt & AR5K_RX_FILTER_CONTROL) len += snprintf(buf + len, sizeof(buf) - len, " CONTROL"); if (filt & AR5K_RX_FILTER_BEACON) len += snprintf(buf + len, sizeof(buf) - len, " BEACON"); if (filt & AR5K_RX_FILTER_PROM) len += snprintf(buf + len, sizeof(buf) - len, " PROM"); if (filt & AR5K_RX_FILTER_XRPOLL) len += snprintf(buf + len, sizeof(buf) - len, " XRPOLL"); if (filt & AR5K_RX_FILTER_PROBEREQ) len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); if (filt & AR5K_RX_FILTER_PHYERR_5212) len += snprintf(buf + len, sizeof(buf) - len, " PHYERR-5212"); if (filt & AR5K_RX_FILTER_RADARERR_5212) len += snprintf(buf + len, sizeof(buf) - len, " RADARERR-5212"); if (filt & AR5K_RX_FILTER_PHYERR_5211) snprintf(buf + len, sizeof(buf) - len, " PHYERR-5211"); if (filt & AR5K_RX_FILTER_RADARERR_5211) len += snprintf(buf + len, sizeof(buf) - len, " RADARERR-5211"); len += snprintf(buf + len, sizeof(buf) - len, "\nopmode: %s (%d)\n", ath_opmode_to_string(ah->opmode), ah->opmode); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static const struct file_operations fops_misc = { .read = read_file_misc, .open = simple_open, .owner = THIS_MODULE, }; /* debugfs: frameerrors */ static ssize_t read_file_frameerrors(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; struct ath5k_statistics *st = &ah->stats; char buf[700]; unsigned int len = 0; int i; len += snprintf(buf + len, sizeof(buf) - len, "RX\n---------------------\n"); len += snprintf(buf + len, sizeof(buf) - len, "CRC\t%u\t(%u%%)\n", st->rxerr_crc, st->rx_all_count > 0 ? st->rxerr_crc * 100 / st->rx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "PHY\t%u\t(%u%%)\n", st->rxerr_phy, st->rx_all_count > 0 ? st->rxerr_phy * 100 / st->rx_all_count : 0); for (i = 0; i < 32; i++) { if (st->rxerr_phy_code[i]) len += snprintf(buf + len, sizeof(buf) - len, " phy_err[%u]\t%u\n", i, st->rxerr_phy_code[i]); } len += snprintf(buf + len, sizeof(buf) - len, "FIFO\t%u\t(%u%%)\n", st->rxerr_fifo, st->rx_all_count > 0 ? st->rxerr_fifo * 100 / st->rx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "decrypt\t%u\t(%u%%)\n", st->rxerr_decrypt, st->rx_all_count > 0 ? st->rxerr_decrypt * 100 / st->rx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "MIC\t%u\t(%u%%)\n", st->rxerr_mic, st->rx_all_count > 0 ? st->rxerr_mic * 100 / st->rx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "process\t%u\t(%u%%)\n", st->rxerr_proc, st->rx_all_count > 0 ? st->rxerr_proc * 100 / st->rx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "jumbo\t%u\t(%u%%)\n", st->rxerr_jumbo, st->rx_all_count > 0 ? st->rxerr_jumbo * 100 / st->rx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "[RX all\t%u]\n", st->rx_all_count); len += snprintf(buf + len, sizeof(buf) - len, "RX-all-bytes\t%u\n", st->rx_bytes_count); len += snprintf(buf + len, sizeof(buf) - len, "\nTX\n---------------------\n"); len += snprintf(buf + len, sizeof(buf) - len, "retry\t%u\t(%u%%)\n", st->txerr_retry, st->tx_all_count > 0 ? st->txerr_retry * 100 / st->tx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "FIFO\t%u\t(%u%%)\n", st->txerr_fifo, st->tx_all_count > 0 ? st->txerr_fifo * 100 / st->tx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "filter\t%u\t(%u%%)\n", st->txerr_filt, st->tx_all_count > 0 ? st->txerr_filt * 100 / st->tx_all_count : 0); len += snprintf(buf + len, sizeof(buf) - len, "[TX all\t%u]\n", st->tx_all_count); len += snprintf(buf + len, sizeof(buf) - len, "TX-all-bytes\t%u\n", st->tx_bytes_count); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_frameerrors(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; struct ath5k_statistics *st = &ah->stats; char buf[20]; if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) return -EFAULT; if (strncmp(buf, "clear", 5) == 0) { st->rxerr_crc = 0; st->rxerr_phy = 0; st->rxerr_fifo = 0; st->rxerr_decrypt = 0; st->rxerr_mic = 0; st->rxerr_proc = 0; st->rxerr_jumbo = 0; st->rx_all_count = 0; st->txerr_retry = 0; st->txerr_fifo = 0; st->txerr_filt = 0; st->tx_all_count = 0; pr_info("debug: cleared frameerrors stats\n"); } return count; } static const struct file_operations fops_frameerrors = { .read = read_file_frameerrors, .write = write_file_frameerrors, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; /* debugfs: ani */ static ssize_t read_file_ani(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; struct ath5k_statistics *st = &ah->stats; struct ath5k_ani_state *as = &ah->ani_state; char buf[700]; unsigned int len = 0; len += snprintf(buf + len, sizeof(buf) - len, "HW has PHY error counters:\t%s\n", ah->ah_capabilities.cap_has_phyerr_counters ? "yes" : "no"); len += snprintf(buf + len, sizeof(buf) - len, "HW max spur immunity level:\t%d\n", as->max_spur_level); len += snprintf(buf + len, sizeof(buf) - len, "\nANI state\n--------------------------------------------\n"); len += snprintf(buf + len, sizeof(buf) - len, "operating mode:\t\t\t"); switch (as->ani_mode) { case ATH5K_ANI_MODE_OFF: len += snprintf(buf + len, sizeof(buf) - len, "OFF\n"); break; case ATH5K_ANI_MODE_MANUAL_LOW: len += snprintf(buf + len, sizeof(buf) - len, "MANUAL LOW\n"); break; case ATH5K_ANI_MODE_MANUAL_HIGH: len += snprintf(buf + len, sizeof(buf) - len, "MANUAL HIGH\n"); break; case ATH5K_ANI_MODE_AUTO: len += snprintf(buf + len, sizeof(buf) - len, "AUTO\n"); break; default: len += snprintf(buf + len, sizeof(buf) - len, "??? (not good)\n"); break; } len += snprintf(buf + len, sizeof(buf) - len, "noise immunity level:\t\t%d\n", as->noise_imm_level); len += snprintf(buf + len, sizeof(buf) - len, "spur immunity level:\t\t%d\n", as->spur_level); len += snprintf(buf + len, sizeof(buf) - len, "firstep level:\t\t\t%d\n", as->firstep_level); len += snprintf(buf + len, sizeof(buf) - len, "OFDM weak signal detection:\t%s\n", as->ofdm_weak_sig ? "on" : "off"); len += snprintf(buf + len, sizeof(buf) - len, "CCK weak signal detection:\t%s\n", as->cck_weak_sig ? "on" : "off"); len += snprintf(buf + len, sizeof(buf) - len, "\nMIB INTERRUPTS:\t\t%u\n", st->mib_intr); len += snprintf(buf + len, sizeof(buf) - len, "beacon RSSI average:\t%d\n", (int)ewma_read(&ah->ah_beacon_rssi_avg)); #define CC_PRINT(_struct, _field) \ _struct._field, \ _struct.cycles > 0 ? \ _struct._field * 100 / _struct.cycles : 0 len += snprintf(buf + len, sizeof(buf) - len, "profcnt tx\t\t%u\t(%d%%)\n", CC_PRINT(as->last_cc, tx_frame)); len += snprintf(buf + len, sizeof(buf) - len, "profcnt rx\t\t%u\t(%d%%)\n", CC_PRINT(as->last_cc, rx_frame)); len += snprintf(buf + len, sizeof(buf) - len, "profcnt busy\t\t%u\t(%d%%)\n", CC_PRINT(as->last_cc, rx_busy)); #undef CC_PRINT len += snprintf(buf + len, sizeof(buf) - len, "profcnt cycles\t\t%u\n", as->last_cc.cycles); len += snprintf(buf + len, sizeof(buf) - len, "listen time\t\t%d\tlast: %d\n", as->listen_time, as->last_listen); len += snprintf(buf + len, sizeof(buf) - len, "OFDM errors\t\t%u\tlast: %u\tsum: %u\n", as->ofdm_errors, as->last_ofdm_errors, as->sum_ofdm_errors); len += snprintf(buf + len, sizeof(buf) - len, "CCK errors\t\t%u\tlast: %u\tsum: %u\n", as->cck_errors, as->last_cck_errors, as->sum_cck_errors); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_PHYERR_CNT1\t%x\t(=%d)\n", ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1), ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1))); len += snprintf(buf + len, sizeof(buf) - len, "AR5K_PHYERR_CNT2\t%x\t(=%d)\n", ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2), ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2))); if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_ani(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[20]; if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) return -EFAULT; if (strncmp(buf, "sens-low", 8) == 0) { ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_HIGH); } else if (strncmp(buf, "sens-high", 9) == 0) { ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_LOW); } else if (strncmp(buf, "ani-off", 7) == 0) { ath5k_ani_init(ah, ATH5K_ANI_MODE_OFF); } else if (strncmp(buf, "ani-on", 6) == 0) { ath5k_ani_init(ah, ATH5K_ANI_MODE_AUTO); } else if (strncmp(buf, "noise-low", 9) == 0) { ath5k_ani_set_noise_immunity_level(ah, 0); } else if (strncmp(buf, "noise-high", 10) == 0) { ath5k_ani_set_noise_immunity_level(ah, ATH5K_ANI_MAX_NOISE_IMM_LVL); } else if (strncmp(buf, "spur-low", 8) == 0) { ath5k_ani_set_spur_immunity_level(ah, 0); } else if (strncmp(buf, "spur-high", 9) == 0) { ath5k_ani_set_spur_immunity_level(ah, ah->ani_state.max_spur_level); } else if (strncmp(buf, "fir-low", 7) == 0) { ath5k_ani_set_firstep_level(ah, 0); } else if (strncmp(buf, "fir-high", 8) == 0) { ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL); } else if (strncmp(buf, "ofdm-off", 8) == 0) { ath5k_ani_set_ofdm_weak_signal_detection(ah, false); } else if (strncmp(buf, "ofdm-on", 7) == 0) { ath5k_ani_set_ofdm_weak_signal_detection(ah, true); } else if (strncmp(buf, "cck-off", 7) == 0) { ath5k_ani_set_cck_weak_signal_detection(ah, false); } else if (strncmp(buf, "cck-on", 6) == 0) { ath5k_ani_set_cck_weak_signal_detection(ah, true); } return count; } static const struct file_operations fops_ani = { .read = read_file_ani, .write = write_file_ani, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; /* debugfs: queues etc */ static ssize_t read_file_queue(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[700]; unsigned int len = 0; struct ath5k_txq *txq; struct ath5k_buf *bf, *bf0; int i, n; len += snprintf(buf + len, sizeof(buf) - len, "available txbuffers: %d\n", ah->txbuf_len); for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) { txq = &ah->txqs[i]; len += snprintf(buf + len, sizeof(buf) - len, "%02d: %ssetup\n", i, txq->setup ? "" : "not "); if (!txq->setup) continue; n = 0; spin_lock_bh(&txq->lock); list_for_each_entry_safe(bf, bf0, &txq->q, list) n++; spin_unlock_bh(&txq->lock); len += snprintf(buf + len, sizeof(buf) - len, " len: %d bufs: %d\n", txq->txq_len, n); len += snprintf(buf + len, sizeof(buf) - len, " stuck: %d\n", txq->txq_stuck); } if (len > sizeof(buf)) len = sizeof(buf); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t write_file_queue(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ath5k_hw *ah = file->private_data; char buf[20]; if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) return -EFAULT; if (strncmp(buf, "start", 5) == 0) ieee80211_wake_queues(ah->hw); else if (strncmp(buf, "stop", 4) == 0) ieee80211_stop_queues(ah->hw); return count; } static const struct file_operations fops_queue = { .read = read_file_queue, .write = write_file_queue, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; void ath5k_debug_init_device(struct ath5k_hw *ah) { struct dentry *phydir; ah->debug.level = ath5k_debug; phydir = debugfs_create_dir("ath5k", ah->hw->wiphy->debugfsdir); if (!phydir) return; debugfs_create_file("debug", S_IWUSR | S_IRUSR, phydir, ah, &fops_debug); debugfs_create_file("registers", S_IRUSR, phydir, ah, &fops_registers); debugfs_create_file("beacon", S_IWUSR | S_IRUSR, phydir, ah, &fops_beacon); debugfs_create_file("reset", S_IWUSR, phydir, ah, &fops_reset); debugfs_create_file("antenna", S_IWUSR | S_IRUSR, phydir, ah, &fops_antenna); debugfs_create_file("misc", S_IRUSR, phydir, ah, &fops_misc); debugfs_create_file("frameerrors", S_IWUSR | S_IRUSR, phydir, ah, &fops_frameerrors); debugfs_create_file("ani", S_IWUSR | S_IRUSR, phydir, ah, &fops_ani); debugfs_create_file("queue", S_IWUSR | S_IRUSR, phydir, ah, &fops_queue); debugfs_create_bool("32khz_clock", S_IWUSR | S_IRUSR, phydir, &ah->ah_use_32khz_clock); } /* functions used in other places */ void ath5k_debug_dump_bands(struct ath5k_hw *ah) { unsigned int b, i; if (likely(!(ah->debug.level & ATH5K_DEBUG_DUMPBANDS))) return; BUG_ON(!ah->sbands); for (b = 0; b < IEEE80211_NUM_BANDS; b++) { struct ieee80211_supported_band *band = &ah->sbands[b]; char bname[6]; switch (band->band) { case IEEE80211_BAND_2GHZ: strcpy(bname, "2 GHz"); break; case IEEE80211_BAND_5GHZ: strcpy(bname, "5 GHz"); break; default: printk(KERN_DEBUG "Band not supported: %d\n", band->band); return; } printk(KERN_DEBUG "Band %s: channels %d, rates %d\n", bname, band->n_channels, band->n_bitrates); printk(KERN_DEBUG " channels:\n"); for (i = 0; i < band->n_channels; i++) printk(KERN_DEBUG " %3d %d %.4x %.4x\n", ieee80211_frequency_to_channel( band->channels[i].center_freq), band->channels[i].center_freq, band->channels[i].hw_value, band->channels[i].flags); printk(KERN_DEBUG " rates:\n"); for (i = 0; i < band->n_bitrates; i++) printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n", band->bitrates[i].bitrate, band->bitrates[i].hw_value, band->bitrates[i].flags, band->bitrates[i].hw_value_short); } } static inline void ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done, struct ath5k_rx_status *rs) { struct ath5k_desc *ds = bf->desc; struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx; printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link, ds->ds_data, rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1, rd->rx_stat.rx_status_0, rd->rx_stat.rx_status_1, !done ? ' ' : (rs->rs_status == 0) ? '*' : '!'); } void ath5k_debug_printrxbuffs(struct ath5k_hw *ah) { struct ath5k_desc *ds; struct ath5k_buf *bf; struct ath5k_rx_status rs = {}; int status; if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC))) return; printk(KERN_DEBUG "rxdp %x, rxlink %p\n", ath5k_hw_get_rxdp(ah), ah->rxlink); spin_lock_bh(&ah->rxbuflock); list_for_each_entry(bf, &ah->rxbuf, list) { ds = bf->desc; status = ah->ah_proc_rx_desc(ah, ds, &rs); if (!status) ath5k_debug_printrxbuf(bf, status == 0, &rs); } spin_unlock_bh(&ah->rxbuflock); } void ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf) { struct ath5k_desc *ds = bf->desc; struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212; struct ath5k_tx_status ts = {}; int done; if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC))) return; done = ah->ah_proc_tx_desc(ah, bf->desc, &ts); printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x " "%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link, ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1, td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3, td->tx_stat.tx_status_0, td->tx_stat.tx_status_1, done ? ' ' : (ts.ts_status == 0) ? '*' : '!'); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/eeprom.c0000644000175000017500000014001112026211315023701 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2009 Nick Kossifidis * Copyright (c) 2008-2009 Felix Fietkau * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /*************************************\ * EEPROM access functions and helpers * \*************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include "ath5k.h" #include "reg.h" #include "debug.h" /******************\ * Helper functions * \******************/ /* * Translate binary channel representation in EEPROM to frequency */ static u16 ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee, u16 bin, unsigned int mode) { u16 val; if (bin == AR5K_EEPROM_CHANNEL_DIS) return bin; if (mode == AR5K_EEPROM_MODE_11A) { if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) val = (5 * bin) + 4800; else val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 : (bin * 10) + 5100; } else { if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) val = bin + 2300; else val = bin + 2400; } return val; } /*********\ * Parsers * \*********/ /* * Initialize eeprom & capabilities structs */ static int ath5k_eeprom_init_header(struct ath5k_hw *ah) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u16 val; u32 cksum, offset, eep_max = AR5K_EEPROM_INFO_MAX; /* * Read values from EEPROM and store them in the capability structure */ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); /* Return if we have an old EEPROM */ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) return 0; /* * Validate the checksum of the EEPROM date. There are some * devices with invalid EEPROMs. */ AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_UPPER, val); if (val) { eep_max = (val & AR5K_EEPROM_SIZE_UPPER_MASK) << AR5K_EEPROM_SIZE_ENDLOC_SHIFT; AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_LOWER, val); eep_max = (eep_max | val) - AR5K_EEPROM_INFO_BASE; /* * Fail safe check to prevent stupid loops due * to busted EEPROMs. XXX: This value is likely too * big still, waiting on a better value. */ if (eep_max > (3 * AR5K_EEPROM_INFO_MAX)) { ATH5K_ERR(ah, "Invalid max custom EEPROM size: " "%d (0x%04x) max expected: %d (0x%04x)\n", eep_max, eep_max, 3 * AR5K_EEPROM_INFO_MAX, 3 * AR5K_EEPROM_INFO_MAX); return -EIO; } } for (cksum = 0, offset = 0; offset < eep_max; offset++) { AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); cksum ^= val; } if (cksum != AR5K_EEPROM_INFO_CKSUM) { ATH5K_ERR(ah, "Invalid EEPROM " "checksum: 0x%04x eep_max: 0x%04x (%s)\n", cksum, eep_max, eep_max == AR5K_EEPROM_INFO_MAX ? "default size" : "custom size"); return -EIO; } AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), ee_ant_gain); if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); /* XXX: Don't know which versions include these two */ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC2, ee_misc2); if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC3, ee_misc3); if (ee->ee_version >= AR5K_EEPROM_VERSION_5_0) { AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC4, ee_misc4); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC5, ee_misc5); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC6, ee_misc6); } } if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; } AR5K_EEPROM_READ(AR5K_EEPROM_IS_HB63, val); if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && val) ee->ee_is_hb63 = true; else ee->ee_is_hb63 = false; AR5K_EEPROM_READ(AR5K_EEPROM_RFKILL, val); ee->ee_rfkill_pin = (u8) AR5K_REG_MS(val, AR5K_EEPROM_RFKILL_GPIO_SEL); ee->ee_rfkill_pol = val & AR5K_EEPROM_RFKILL_POLARITY ? true : false; /* Check if PCIE_OFFSET points to PCIE_SERDES_SECTION * and enable serdes programming if needed. * * XXX: Serdes values seem to be fixed so * no need to read them here, we write them * during ath5k_hw_init */ AR5K_EEPROM_READ(AR5K_EEPROM_PCIE_OFFSET, val); ee->ee_serdes = (val == AR5K_EEPROM_PCIE_SERDES_SECTION) ? true : false; return 0; } /* * Read antenna infos from eeprom */ static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, unsigned int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 o = *offset; u16 val; int i = 0; AR5K_EEPROM_READ(o++, val); ee->ee_switch_settling[mode] = (val >> 8) & 0x7f; ee->ee_atn_tx_rx[mode] = (val >> 2) & 0x3f; ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; AR5K_EEPROM_READ(o++, val); ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; ee->ee_ant_control[mode][i++] = val & 0x3f; AR5K_EEPROM_READ(o++, val); ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f; ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f; ee->ee_ant_control[mode][i] = (val << 2) & 0x3f; AR5K_EEPROM_READ(o++, val); ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3; ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f; ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f; ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; AR5K_EEPROM_READ(o++, val); ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; ee->ee_ant_control[mode][i++] = val & 0x3f; /* Get antenna switch tables */ ah->ah_ant_ctl[mode][AR5K_ANT_CTL] = (ee->ee_ant_control[mode][0] << 4); ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_A] = ee->ee_ant_control[mode][1] | (ee->ee_ant_control[mode][2] << 6) | (ee->ee_ant_control[mode][3] << 12) | (ee->ee_ant_control[mode][4] << 18) | (ee->ee_ant_control[mode][5] << 24); ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_B] = ee->ee_ant_control[mode][6] | (ee->ee_ant_control[mode][7] << 6) | (ee->ee_ant_control[mode][8] << 12) | (ee->ee_ant_control[mode][9] << 18) | (ee->ee_ant_control[mode][10] << 24); /* return new offset */ *offset = o; return 0; } /* * Read supported modes and some mode-specific calibration data * from eeprom */ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, unsigned int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 o = *offset; u16 val; ee->ee_n_piers[mode] = 0; AR5K_EEPROM_READ(o++, val); ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); switch (mode) { case AR5K_EEPROM_MODE_11A: ee->ee_ob[mode][3] = (val >> 5) & 0x7; ee->ee_db[mode][3] = (val >> 2) & 0x7; ee->ee_ob[mode][2] = (val << 1) & 0x7; AR5K_EEPROM_READ(o++, val); ee->ee_ob[mode][2] |= (val >> 15) & 0x1; ee->ee_db[mode][2] = (val >> 12) & 0x7; ee->ee_ob[mode][1] = (val >> 9) & 0x7; ee->ee_db[mode][1] = (val >> 6) & 0x7; ee->ee_ob[mode][0] = (val >> 3) & 0x7; ee->ee_db[mode][0] = val & 0x7; break; case AR5K_EEPROM_MODE_11G: case AR5K_EEPROM_MODE_11B: ee->ee_ob[mode][1] = (val >> 4) & 0x7; ee->ee_db[mode][1] = val & 0x7; break; } AR5K_EEPROM_READ(o++, val); ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff; ee->ee_thr_62[mode] = val & 0xff; if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28; AR5K_EEPROM_READ(o++, val); ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff; ee->ee_tx_frm2xpa_enable[mode] = val & 0xff; AR5K_EEPROM_READ(o++, val); ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff; if ((val & 0xff) & 0x80) ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1); else ee->ee_noise_floor_thr[mode] = val & 0xff; if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) ee->ee_noise_floor_thr[mode] = mode == AR5K_EEPROM_MODE_11A ? -54 : -1; AR5K_EEPROM_READ(o++, val); ee->ee_xlna_gain[mode] = (val >> 5) & 0xff; ee->ee_x_gain[mode] = (val >> 1) & 0xf; ee->ee_xpd[mode] = val & 0x1; if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && mode != AR5K_EEPROM_MODE_11B) ee->ee_fixed_bias[mode] = (val >> 13) & 0x1; if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) { AR5K_EEPROM_READ(o++, val); ee->ee_false_detect[mode] = (val >> 6) & 0x7f; if (mode == AR5K_EEPROM_MODE_11A) ee->ee_xr_power[mode] = val & 0x3f; else { /* b_DB_11[bg] and b_OB_11[bg] */ ee->ee_ob[mode][0] = val & 0x7; ee->ee_db[mode][0] = (val >> 3) & 0x7; } } if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) { ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN; ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA; } else { ee->ee_i_gain[mode] = (val >> 13) & 0x7; AR5K_EEPROM_READ(o++, val); ee->ee_i_gain[mode] |= (val << 3) & 0x38; if (mode == AR5K_EEPROM_MODE_11G) { ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6) ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; } } if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && mode == AR5K_EEPROM_MODE_11A) { ee->ee_i_cal[mode] = (val >> 8) & 0x3f; ee->ee_q_cal[mode] = (val >> 3) & 0x1f; } if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0) goto done; /* Note: >= v5 have bg freq piers on another location * so these freq piers are ignored for >= v5 (should be 0xff * anyway) */ switch (mode) { case AR5K_EEPROM_MODE_11A: if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1) break; AR5K_EEPROM_READ(o++, val); ee->ee_margin_tx_rx[mode] = val & 0x3f; break; case AR5K_EEPROM_MODE_11B: AR5K_EEPROM_READ(o++, val); ee->ee_pwr_cal_b[0].freq = ath5k_eeprom_bin2freq(ee, val & 0xff, mode); if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS) ee->ee_n_piers[mode]++; ee->ee_pwr_cal_b[1].freq = ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS) ee->ee_n_piers[mode]++; AR5K_EEPROM_READ(o++, val); ee->ee_pwr_cal_b[2].freq = ath5k_eeprom_bin2freq(ee, val & 0xff, mode); if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS) ee->ee_n_piers[mode]++; if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; break; case AR5K_EEPROM_MODE_11G: AR5K_EEPROM_READ(o++, val); ee->ee_pwr_cal_g[0].freq = ath5k_eeprom_bin2freq(ee, val & 0xff, mode); if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS) ee->ee_n_piers[mode]++; ee->ee_pwr_cal_g[1].freq = ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS) ee->ee_n_piers[mode]++; AR5K_EEPROM_READ(o++, val); ee->ee_turbo_max_power[mode] = val & 0x7f; ee->ee_xr_power[mode] = (val >> 7) & 0x3f; AR5K_EEPROM_READ(o++, val); ee->ee_pwr_cal_g[2].freq = ath5k_eeprom_bin2freq(ee, val & 0xff, mode); if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS) ee->ee_n_piers[mode]++; if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; AR5K_EEPROM_READ(o++, val); ee->ee_i_cal[mode] = (val >> 5) & 0x3f; ee->ee_q_cal[mode] = val & 0x1f; if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { AR5K_EEPROM_READ(o++, val); ee->ee_cck_ofdm_gain_delta = val & 0xff; } break; } /* * Read turbo mode information on newer EEPROM versions */ if (ee->ee_version < AR5K_EEPROM_VERSION_5_0) goto done; switch (mode) { case AR5K_EEPROM_MODE_11A: ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f; ee->ee_atn_tx_rx_turbo[mode] = (val >> 13) & 0x7; AR5K_EEPROM_READ(o++, val); ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x7) << 3; ee->ee_margin_tx_rx_turbo[mode] = (val >> 3) & 0x3f; ee->ee_adc_desired_size_turbo[mode] = (val >> 9) & 0x7f; AR5K_EEPROM_READ(o++, val); ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7; ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff; if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >= 2) ee->ee_pd_gain_overlap = (val >> 9) & 0xf; break; case AR5K_EEPROM_MODE_11G: ee->ee_switch_settling_turbo[mode] = (val >> 8) & 0x7f; ee->ee_atn_tx_rx_turbo[mode] = (val >> 15) & 0x7; AR5K_EEPROM_READ(o++, val); ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x1f) << 1; ee->ee_margin_tx_rx_turbo[mode] = (val >> 5) & 0x3f; ee->ee_adc_desired_size_turbo[mode] = (val >> 11) & 0x7f; AR5K_EEPROM_READ(o++, val); ee->ee_adc_desired_size_turbo[mode] |= (val & 0x7) << 5; ee->ee_pga_desired_size_turbo[mode] = (val >> 3) & 0xff; break; } done: /* return new offset */ *offset = o; return 0; } /* Read mode-specific data (except power calibration data) */ static int ath5k_eeprom_init_modes(struct ath5k_hw *ah) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 mode_offset[3]; unsigned int mode; u32 offset; int ret; /* * Get values for all modes */ mode_offset[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_MODES_11A(ah->ah_ee_version); mode_offset[AR5K_EEPROM_MODE_11B] = AR5K_EEPROM_MODES_11B(ah->ah_ee_version); mode_offset[AR5K_EEPROM_MODE_11G] = AR5K_EEPROM_MODES_11G(ah->ah_ee_version); ee->ee_turbo_max_power[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) { offset = mode_offset[mode]; ret = ath5k_eeprom_read_ants(ah, &offset, mode); if (ret) return ret; ret = ath5k_eeprom_read_modes(ah, &offset, mode); if (ret) return ret; } /* override for older eeprom versions for better performance */ if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) { ee->ee_thr_62[AR5K_EEPROM_MODE_11A] = 15; ee->ee_thr_62[AR5K_EEPROM_MODE_11B] = 28; ee->ee_thr_62[AR5K_EEPROM_MODE_11G] = 28; } return 0; } /* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff * frequency mask) */ static inline int ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, struct ath5k_chan_pcal_info *pc, unsigned int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; int o = *offset; int i = 0; u8 freq1, freq2; u16 val; ee->ee_n_piers[mode] = 0; while (i < max) { AR5K_EEPROM_READ(o++, val); freq1 = val & 0xff; if (!freq1) break; pc[i++].freq = ath5k_eeprom_bin2freq(ee, freq1, mode); ee->ee_n_piers[mode]++; freq2 = (val >> 8) & 0xff; if (!freq2) break; pc[i++].freq = ath5k_eeprom_bin2freq(ee, freq2, mode); ee->ee_n_piers[mode]++; } /* return new offset */ *offset = o; return 0; } /* Read frequency piers for 802.11a */ static int ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info *pcal = ee->ee_pwr_cal_a; int i; u16 val; u8 mask; if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { ath5k_eeprom_read_freq_list(ah, &offset, AR5K_EEPROM_N_5GHZ_CHAN, pcal, AR5K_EEPROM_MODE_11A); } else { mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version); AR5K_EEPROM_READ(offset++, val); pcal[0].freq = (val >> 9) & mask; pcal[1].freq = (val >> 2) & mask; pcal[2].freq = (val << 5) & mask; AR5K_EEPROM_READ(offset++, val); pcal[2].freq |= (val >> 11) & 0x1f; pcal[3].freq = (val >> 4) & mask; pcal[4].freq = (val << 3) & mask; AR5K_EEPROM_READ(offset++, val); pcal[4].freq |= (val >> 13) & 0x7; pcal[5].freq = (val >> 6) & mask; pcal[6].freq = (val << 1) & mask; AR5K_EEPROM_READ(offset++, val); pcal[6].freq |= (val >> 15) & 0x1; pcal[7].freq = (val >> 8) & mask; pcal[8].freq = (val >> 1) & mask; pcal[9].freq = (val << 6) & mask; AR5K_EEPROM_READ(offset++, val); pcal[9].freq |= (val >> 10) & 0x3f; /* Fixed number of piers */ ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10; for (i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i++) { pcal[i].freq = ath5k_eeprom_bin2freq(ee, pcal[i].freq, AR5K_EEPROM_MODE_11A); } } return 0; } /* Read frequency piers for 802.11bg on eeprom versions >= 5 and eemap >= 2 */ static inline int ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info *pcal; switch (mode) { case AR5K_EEPROM_MODE_11B: pcal = ee->ee_pwr_cal_b; break; case AR5K_EEPROM_MODE_11G: pcal = ee->ee_pwr_cal_g; break; default: return -EINVAL; } ath5k_eeprom_read_freq_list(ah, &offset, AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal, mode); return 0; } /* * Read power calibration for RF5111 chips * * For RF5111 we have an XPD -eXternal Power Detector- curve * for each calibrated channel. Each curve has 0,5dB Power steps * on x axis and PCDAC steps (offsets) on y axis and looks like an * exponential function. To recreate the curve we read 11 points * here and interpolate later. */ /* Used to match PCDAC steps with power values on RF5111 chips * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC * steps that match with the power values we read from eeprom. On * older eeprom versions (< 3.2) these steps are equally spaced at * 10% of the pcdac curve -until the curve reaches its maximum- * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2) * these 11 steps are spaced in a different way. This function returns * the pcdac steps based on eeprom version and curve min/max so that we * can have pcdac/pwr points. */ static inline void ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) { static const u16 intercepts3[] = { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 }; static const u16 intercepts3_2[] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; const u16 *ip; int i; if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2) ip = intercepts3_2; else ip = intercepts3; for (i = 0; i < ARRAY_SIZE(intercepts3); i++) vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100; } static int ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info *chinfo; u8 pier, pdg; switch (mode) { case AR5K_EEPROM_MODE_11A: if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) return 0; chinfo = ee->ee_pwr_cal_a; break; case AR5K_EEPROM_MODE_11B: if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) return 0; chinfo = ee->ee_pwr_cal_b; break; case AR5K_EEPROM_MODE_11G: if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) return 0; chinfo = ee->ee_pwr_cal_g; break; default: return -EINVAL; } for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { if (!chinfo[pier].pd_curves) continue; for (pdg = 0; pdg < AR5K_EEPROM_N_PD_CURVES; pdg++) { struct ath5k_pdgain_info *pd = &chinfo[pier].pd_curves[pdg]; kfree(pd->pd_step); kfree(pd->pd_pwr); } kfree(chinfo[pier].pd_curves); } return 0; } /* Convert RF5111 specific data to generic raw data * used by interpolation code */ static int ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode, struct ath5k_chan_pcal_info *chinfo) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info_rf5111 *pcinfo; struct ath5k_pdgain_info *pd; u8 pier, point, idx; u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; /* Fill raw data for each calibration pier */ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { pcinfo = &chinfo[pier].rf5111_info; /* Allocate pd_curves for this cal pier */ chinfo[pier].pd_curves = kcalloc(AR5K_EEPROM_N_PD_CURVES, sizeof(struct ath5k_pdgain_info), GFP_KERNEL); if (!chinfo[pier].pd_curves) goto err_out; /* Only one curve for RF5111 * find out which one and place * in pd_curves. * Note: ee_x_gain is reversed here */ for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) { if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) { pdgain_idx[0] = idx; break; } } ee->ee_pd_gains[mode] = 1; pd = &chinfo[pier].pd_curves[idx]; pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111; /* Allocate pd points for this curve */ pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, sizeof(u8), GFP_KERNEL); if (!pd->pd_step) goto err_out; pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, sizeof(s16), GFP_KERNEL); if (!pd->pd_pwr) goto err_out; /* Fill raw dataset * (convert power to 0.25dB units * for RF5112 compatibility) */ for (point = 0; point < pd->pd_points; point++) { /* Absolute values */ pd->pd_pwr[point] = 2 * pcinfo->pwr[point]; /* Already sorted */ pd->pd_step[point] = pcinfo->pcdac[point]; } /* Set min/max pwr */ chinfo[pier].min_pwr = pd->pd_pwr[0]; chinfo[pier].max_pwr = pd->pd_pwr[10]; } return 0; err_out: ath5k_eeprom_free_pcal_info(ah, mode); return -ENOMEM; } /* Parse EEPROM data */ static int ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info *pcal; int offset, ret; int i; u16 val; offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); switch (mode) { case AR5K_EEPROM_MODE_11A: if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) return 0; ret = ath5k_eeprom_init_11a_pcal_freq(ah, offset + AR5K_EEPROM_GROUP1_OFFSET); if (ret < 0) return ret; offset += AR5K_EEPROM_GROUP2_OFFSET; pcal = ee->ee_pwr_cal_a; break; case AR5K_EEPROM_MODE_11B: if (!AR5K_EEPROM_HDR_11B(ee->ee_header) && !AR5K_EEPROM_HDR_11G(ee->ee_header)) return 0; pcal = ee->ee_pwr_cal_b; offset += AR5K_EEPROM_GROUP3_OFFSET; /* fixed piers */ pcal[0].freq = 2412; pcal[1].freq = 2447; pcal[2].freq = 2484; ee->ee_n_piers[mode] = 3; break; case AR5K_EEPROM_MODE_11G: if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) return 0; pcal = ee->ee_pwr_cal_g; offset += AR5K_EEPROM_GROUP4_OFFSET; /* fixed piers */ pcal[0].freq = 2312; pcal[1].freq = 2412; pcal[2].freq = 2484; ee->ee_n_piers[mode] = 3; break; default: return -EINVAL; } for (i = 0; i < ee->ee_n_piers[mode]; i++) { struct ath5k_chan_pcal_info_rf5111 *cdata = &pcal[i].rf5111_info; AR5K_EEPROM_READ(offset++, val); cdata->pcdac_max = ((val >> 10) & AR5K_EEPROM_PCDAC_M); cdata->pcdac_min = ((val >> 4) & AR5K_EEPROM_PCDAC_M); cdata->pwr[0] = ((val << 2) & AR5K_EEPROM_POWER_M); AR5K_EEPROM_READ(offset++, val); cdata->pwr[0] |= ((val >> 14) & 0x3); cdata->pwr[1] = ((val >> 8) & AR5K_EEPROM_POWER_M); cdata->pwr[2] = ((val >> 2) & AR5K_EEPROM_POWER_M); cdata->pwr[3] = ((val << 4) & AR5K_EEPROM_POWER_M); AR5K_EEPROM_READ(offset++, val); cdata->pwr[3] |= ((val >> 12) & 0xf); cdata->pwr[4] = ((val >> 6) & AR5K_EEPROM_POWER_M); cdata->pwr[5] = (val & AR5K_EEPROM_POWER_M); AR5K_EEPROM_READ(offset++, val); cdata->pwr[6] = ((val >> 10) & AR5K_EEPROM_POWER_M); cdata->pwr[7] = ((val >> 4) & AR5K_EEPROM_POWER_M); cdata->pwr[8] = ((val << 2) & AR5K_EEPROM_POWER_M); AR5K_EEPROM_READ(offset++, val); cdata->pwr[8] |= ((val >> 14) & 0x3); cdata->pwr[9] = ((val >> 8) & AR5K_EEPROM_POWER_M); cdata->pwr[10] = ((val >> 2) & AR5K_EEPROM_POWER_M); ath5k_get_pcdac_intercepts(ah, cdata->pcdac_min, cdata->pcdac_max, cdata->pcdac); } return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal); } /* * Read power calibration for RF5112 chips * * For RF5112 we have 4 XPD -eXternal Power Detector- curves * for each calibrated channel on 0, -6, -12 and -18dBm but we only * use the higher (3) and the lower (0) curves. Each curve has 0.5dB * power steps on x axis and PCDAC steps on y axis and looks like a * linear function. To recreate the curve and pass the power values * on hw, we read 4 points for xpd 0 (lower gain -> max power) * and 3 points for xpd 3 (higher gain -> lower power) here and * interpolate later. * * Note: Many vendors just use xpd 0 so xpd 3 is zeroed. */ /* Convert RF5112 specific data to generic raw data * used by interpolation code */ static int ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode, struct ath5k_chan_pcal_info *chinfo) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info_rf5112 *pcinfo; u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; unsigned int pier, pdg, point; /* Fill raw data for each calibration pier */ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { pcinfo = &chinfo[pier].rf5112_info; /* Allocate pd_curves for this cal pier */ chinfo[pier].pd_curves = kcalloc(AR5K_EEPROM_N_PD_CURVES, sizeof(struct ath5k_pdgain_info), GFP_KERNEL); if (!chinfo[pier].pd_curves) goto err_out; /* Fill pd_curves */ for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { u8 idx = pdgain_idx[pdg]; struct ath5k_pdgain_info *pd = &chinfo[pier].pd_curves[idx]; /* Lowest gain curve (max power) */ if (pdg == 0) { /* One more point for better accuracy */ pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS; /* Allocate pd points for this curve */ pd->pd_step = kcalloc(pd->pd_points, sizeof(u8), GFP_KERNEL); if (!pd->pd_step) goto err_out; pd->pd_pwr = kcalloc(pd->pd_points, sizeof(s16), GFP_KERNEL); if (!pd->pd_pwr) goto err_out; /* Fill raw dataset * (all power levels are in 0.25dB units) */ pd->pd_step[0] = pcinfo->pcdac_x0[0]; pd->pd_pwr[0] = pcinfo->pwr_x0[0]; for (point = 1; point < pd->pd_points; point++) { /* Absolute values */ pd->pd_pwr[point] = pcinfo->pwr_x0[point]; /* Deltas */ pd->pd_step[point] = pd->pd_step[point - 1] + pcinfo->pcdac_x0[point]; } /* Set min power for this frequency */ chinfo[pier].min_pwr = pd->pd_pwr[0]; /* Highest gain curve (min power) */ } else if (pdg == 1) { pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS; /* Allocate pd points for this curve */ pd->pd_step = kcalloc(pd->pd_points, sizeof(u8), GFP_KERNEL); if (!pd->pd_step) goto err_out; pd->pd_pwr = kcalloc(pd->pd_points, sizeof(s16), GFP_KERNEL); if (!pd->pd_pwr) goto err_out; /* Fill raw dataset * (all power levels are in 0.25dB units) */ for (point = 0; point < pd->pd_points; point++) { /* Absolute values */ pd->pd_pwr[point] = pcinfo->pwr_x3[point]; /* Fixed points */ pd->pd_step[point] = pcinfo->pcdac_x3[point]; } /* Since we have a higher gain curve * override min power */ chinfo[pier].min_pwr = pd->pd_pwr[0]; } } } return 0; err_out: ath5k_eeprom_free_pcal_info(ah, mode); return -ENOMEM; } /* Parse EEPROM data */ static int ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info; struct ath5k_chan_pcal_info *gen_chan_info; u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; u32 offset; u8 i, c; u16 val; u8 pd_gains = 0; /* Count how many curves we have and * identify them (which one of the 4 * available curves we have on each count). * Curves are stored from lower (x0) to * higher (x3) gain */ for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) { /* ee_x_gain[mode] is x gain mask */ if ((ee->ee_x_gain[mode] >> i) & 0x1) pdgain_idx[pd_gains++] = i; } ee->ee_pd_gains[mode] = pd_gains; if (pd_gains == 0 || pd_gains > 2) return -EINVAL; switch (mode) { case AR5K_EEPROM_MODE_11A: /* * Read 5GHz EEPROM channels */ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); ath5k_eeprom_init_11a_pcal_freq(ah, offset); offset += AR5K_EEPROM_GROUP2_OFFSET; gen_chan_info = ee->ee_pwr_cal_a; break; case AR5K_EEPROM_MODE_11B: offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); if (AR5K_EEPROM_HDR_11A(ee->ee_header)) offset += AR5K_EEPROM_GROUP3_OFFSET; /* NB: frequency piers parsed during mode init */ gen_chan_info = ee->ee_pwr_cal_b; break; case AR5K_EEPROM_MODE_11G: offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); if (AR5K_EEPROM_HDR_11A(ee->ee_header)) offset += AR5K_EEPROM_GROUP4_OFFSET; else if (AR5K_EEPROM_HDR_11B(ee->ee_header)) offset += AR5K_EEPROM_GROUP2_OFFSET; /* NB: frequency piers parsed during mode init */ gen_chan_info = ee->ee_pwr_cal_g; break; default: return -EINVAL; } for (i = 0; i < ee->ee_n_piers[mode]; i++) { chan_pcal_info = &gen_chan_info[i].rf5112_info; /* Power values in quarter dB * for the lower xpd gain curve * (0 dBm -> higher output power) */ for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) { AR5K_EEPROM_READ(offset++, val); chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff); chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff); } /* PCDAC steps * corresponding to the above power * measurements */ AR5K_EEPROM_READ(offset++, val); chan_pcal_info->pcdac_x0[1] = (val & 0x1f); chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f); chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f); /* Power values in quarter dB * for the higher xpd gain curve * (18 dBm -> lower output power) */ AR5K_EEPROM_READ(offset++, val); chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff); chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff); AR5K_EEPROM_READ(offset++, val); chan_pcal_info->pwr_x3[2] = (val & 0xff); /* PCDAC steps * corresponding to the above power * measurements (fixed) */ chan_pcal_info->pcdac_x3[0] = 20; chan_pcal_info->pcdac_x3[1] = 35; chan_pcal_info->pcdac_x3[2] = 63; if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) { chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f); /* Last xpd0 power level is also channel maximum */ gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3]; } else { chan_pcal_info->pcdac_x0[0] = 1; gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff); } } return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info); } /* * Read power calibration for RF2413 chips * * For RF2413 we have a Power to PDDAC table (Power Detector) * instead of a PCDAC and 4 pd gain curves for each calibrated channel. * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y * axis and looks like an exponential function like the RF5111 curve. * * To recreate the curves we read here the points and interpolate * later. Note that in most cases only 2 (higher and lower) curves are * used (like RF5112) but vendors have the opportunity to include all * 4 curves on eeprom. The final curve (higher power) has an extra * point for better accuracy like RF5112. */ /* For RF2413 power calibration data doesn't start on a fixed location and * if a mode is not supported, its section is missing -not zeroed-. * So we need to calculate the starting offset for each section by using * these two functions */ /* Return the size of each section based on the mode and the number of pd * gains available (maximum 4). */ static inline unsigned int ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode) { static const unsigned int pdgains_size[] = { 4, 6, 9, 12 }; unsigned int sz; sz = pdgains_size[ee->ee_pd_gains[mode] - 1]; sz *= ee->ee_n_piers[mode]; return sz; } /* Return the starting offset for a section based on the modes supported * and each section's size. */ static unsigned int ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) { u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4); switch (mode) { case AR5K_EEPROM_MODE_11G: if (AR5K_EEPROM_HDR_11B(ee->ee_header)) offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) + AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; /* fall through */ case AR5K_EEPROM_MODE_11B: if (AR5K_EEPROM_HDR_11A(ee->ee_header)) offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) + AR5K_EEPROM_N_5GHZ_CHAN / 2; /* fall through */ case AR5K_EEPROM_MODE_11A: break; default: break; } return offset; } /* Convert RF2413 specific data to generic raw data * used by interpolation code */ static int ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode, struct ath5k_chan_pcal_info *chinfo) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info_rf2413 *pcinfo; u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; unsigned int pier, pdg, point; /* Fill raw data for each calibration pier */ for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { pcinfo = &chinfo[pier].rf2413_info; /* Allocate pd_curves for this cal pier */ chinfo[pier].pd_curves = kcalloc(AR5K_EEPROM_N_PD_CURVES, sizeof(struct ath5k_pdgain_info), GFP_KERNEL); if (!chinfo[pier].pd_curves) goto err_out; /* Fill pd_curves */ for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { u8 idx = pdgain_idx[pdg]; struct ath5k_pdgain_info *pd = &chinfo[pier].pd_curves[idx]; /* One more point for the highest power * curve (lowest gain) */ if (pdg == ee->ee_pd_gains[mode] - 1) pd->pd_points = AR5K_EEPROM_N_PD_POINTS; else pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1; /* Allocate pd points for this curve */ pd->pd_step = kcalloc(pd->pd_points, sizeof(u8), GFP_KERNEL); if (!pd->pd_step) goto err_out; pd->pd_pwr = kcalloc(pd->pd_points, sizeof(s16), GFP_KERNEL); if (!pd->pd_pwr) goto err_out; /* Fill raw dataset * convert all pwr levels to * quarter dB for RF5112 compatibility */ pd->pd_step[0] = pcinfo->pddac_i[pdg]; pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg]; for (point = 1; point < pd->pd_points; point++) { pd->pd_pwr[point] = pd->pd_pwr[point - 1] + 2 * pcinfo->pwr[pdg][point - 1]; pd->pd_step[point] = pd->pd_step[point - 1] + pcinfo->pddac[pdg][point - 1]; } /* Highest gain curve -> min power */ if (pdg == 0) chinfo[pier].min_pwr = pd->pd_pwr[0]; /* Lowest gain curve -> max power */ if (pdg == ee->ee_pd_gains[mode] - 1) chinfo[pier].max_pwr = pd->pd_pwr[pd->pd_points - 1]; } } return 0; err_out: ath5k_eeprom_free_pcal_info(ah, mode); return -ENOMEM; } /* Parse EEPROM data */ static int ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info_rf2413 *pcinfo; struct ath5k_chan_pcal_info *chinfo; u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; u32 offset; int idx, i; u16 val; u8 pd_gains = 0; /* Count how many curves we have and * identify them (which one of the 4 * available curves we have on each count). * Curves are stored from higher to * lower gain so we go backwards */ for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) { /* ee_x_gain[mode] is x gain mask */ if ((ee->ee_x_gain[mode] >> idx) & 0x1) pdgain_idx[pd_gains++] = idx; } ee->ee_pd_gains[mode] = pd_gains; if (pd_gains == 0) return -EINVAL; offset = ath5k_cal_data_offset_2413(ee, mode); switch (mode) { case AR5K_EEPROM_MODE_11A: if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) return 0; ath5k_eeprom_init_11a_pcal_freq(ah, offset); offset += AR5K_EEPROM_N_5GHZ_CHAN / 2; chinfo = ee->ee_pwr_cal_a; break; case AR5K_EEPROM_MODE_11B: if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) return 0; ath5k_eeprom_init_11bg_2413(ah, mode, offset); offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; chinfo = ee->ee_pwr_cal_b; break; case AR5K_EEPROM_MODE_11G: if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) return 0; ath5k_eeprom_init_11bg_2413(ah, mode, offset); offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; chinfo = ee->ee_pwr_cal_g; break; default: return -EINVAL; } for (i = 0; i < ee->ee_n_piers[mode]; i++) { pcinfo = &chinfo[i].rf2413_info; /* * Read pwr_i, pddac_i and the first * 2 pd points (pwr, pddac) */ AR5K_EEPROM_READ(offset++, val); pcinfo->pwr_i[0] = val & 0x1f; pcinfo->pddac_i[0] = (val >> 5) & 0x7f; pcinfo->pwr[0][0] = (val >> 12) & 0xf; AR5K_EEPROM_READ(offset++, val); pcinfo->pddac[0][0] = val & 0x3f; pcinfo->pwr[0][1] = (val >> 6) & 0xf; pcinfo->pddac[0][1] = (val >> 10) & 0x3f; AR5K_EEPROM_READ(offset++, val); pcinfo->pwr[0][2] = val & 0xf; pcinfo->pddac[0][2] = (val >> 4) & 0x3f; pcinfo->pwr[0][3] = 0; pcinfo->pddac[0][3] = 0; if (pd_gains > 1) { /* * Pd gain 0 is not the last pd gain * so it only has 2 pd points. * Continue with pd gain 1. */ pcinfo->pwr_i[1] = (val >> 10) & 0x1f; pcinfo->pddac_i[1] = (val >> 15) & 0x1; AR5K_EEPROM_READ(offset++, val); pcinfo->pddac_i[1] |= (val & 0x3F) << 1; pcinfo->pwr[1][0] = (val >> 6) & 0xf; pcinfo->pddac[1][0] = (val >> 10) & 0x3f; AR5K_EEPROM_READ(offset++, val); pcinfo->pwr[1][1] = val & 0xf; pcinfo->pddac[1][1] = (val >> 4) & 0x3f; pcinfo->pwr[1][2] = (val >> 10) & 0xf; pcinfo->pddac[1][2] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); pcinfo->pddac[1][2] |= (val & 0xF) << 2; pcinfo->pwr[1][3] = 0; pcinfo->pddac[1][3] = 0; } else if (pd_gains == 1) { /* * Pd gain 0 is the last one so * read the extra point. */ pcinfo->pwr[0][3] = (val >> 10) & 0xf; pcinfo->pddac[0][3] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); pcinfo->pddac[0][3] |= (val & 0xF) << 2; } /* * Proceed with the other pd_gains * as above. */ if (pd_gains > 2) { pcinfo->pwr_i[2] = (val >> 4) & 0x1f; pcinfo->pddac_i[2] = (val >> 9) & 0x7f; AR5K_EEPROM_READ(offset++, val); pcinfo->pwr[2][0] = (val >> 0) & 0xf; pcinfo->pddac[2][0] = (val >> 4) & 0x3f; pcinfo->pwr[2][1] = (val >> 10) & 0xf; pcinfo->pddac[2][1] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); pcinfo->pddac[2][1] |= (val & 0xF) << 2; pcinfo->pwr[2][2] = (val >> 4) & 0xf; pcinfo->pddac[2][2] = (val >> 8) & 0x3f; pcinfo->pwr[2][3] = 0; pcinfo->pddac[2][3] = 0; } else if (pd_gains == 2) { pcinfo->pwr[1][3] = (val >> 4) & 0xf; pcinfo->pddac[1][3] = (val >> 8) & 0x3f; } if (pd_gains > 3) { pcinfo->pwr_i[3] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2; pcinfo->pddac_i[3] = (val >> 3) & 0x7f; pcinfo->pwr[3][0] = (val >> 10) & 0xf; pcinfo->pddac[3][0] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); pcinfo->pddac[3][0] |= (val & 0xF) << 2; pcinfo->pwr[3][1] = (val >> 4) & 0xf; pcinfo->pddac[3][1] = (val >> 8) & 0x3f; pcinfo->pwr[3][2] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2; pcinfo->pddac[3][2] = (val >> 2) & 0x3f; pcinfo->pwr[3][3] = (val >> 8) & 0xf; pcinfo->pddac[3][3] = (val >> 12) & 0xF; AR5K_EEPROM_READ(offset++, val); pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4; } else if (pd_gains == 3) { pcinfo->pwr[2][3] = (val >> 14) & 0x3; AR5K_EEPROM_READ(offset++, val); pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2; pcinfo->pddac[2][3] = (val >> 2) & 0x3f; } } return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo); } /* * Read per rate target power (this is the maximum tx power * supported by the card). This info is used when setting * tx power, no matter the channel. * * This also works for v5 EEPROMs. */ static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_rate_pcal_info *rate_pcal_info; u8 *rate_target_pwr_num; u32 offset; u16 val; int i; offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1); rate_target_pwr_num = &ee->ee_rate_target_pwr_num[mode]; switch (mode) { case AR5K_EEPROM_MODE_11A: offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version); rate_pcal_info = ee->ee_rate_tpwr_a; ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_RATE_CHAN; break; case AR5K_EEPROM_MODE_11B: offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version); rate_pcal_info = ee->ee_rate_tpwr_b; ee->ee_rate_target_pwr_num[mode] = 2; /* 3rd is g mode's 1st */ break; case AR5K_EEPROM_MODE_11G: offset += AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version); rate_pcal_info = ee->ee_rate_tpwr_g; ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_2GHZ_CHAN; break; default: return -EINVAL; } /* Different freq mask for older eeproms (<= v3.2) */ if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2) { for (i = 0; i < (*rate_target_pwr_num); i++) { AR5K_EEPROM_READ(offset++, val); rate_pcal_info[i].freq = ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode); rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f); rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f; AR5K_EEPROM_READ(offset++, val); if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || val == 0) { (*rate_target_pwr_num) = i; break; } rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7); rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f); rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f); } } else { for (i = 0; i < (*rate_target_pwr_num); i++) { AR5K_EEPROM_READ(offset++, val); rate_pcal_info[i].freq = ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f); rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f; AR5K_EEPROM_READ(offset++, val); if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || val == 0) { (*rate_target_pwr_num) = i; break; } rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf; rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f); rate_pcal_info[i].target_power_54 = (val & 0x3f); } } return 0; } /* * Read per channel calibration info from EEPROM * * This info is used to calibrate the baseband power table. Imagine * that for each channel there is a power curve that's hw specific * (depends on amplifier etc) and we try to "correct" this curve using * offsets we pass on to phy chip (baseband -> before amplifier) so that * it can use accurate power values when setting tx power (takes amplifier's * performance on each channel into account). * * EEPROM provides us with the offsets for some pre-calibrated channels * and we have to interpolate to create the full table for these channels and * also the table for any channel. */ static int ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; int (*read_pcal)(struct ath5k_hw *hw, int mode); int mode; int err; if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) && (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 1)) read_pcal = ath5k_eeprom_read_pcal_info_5112; else if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0) && (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 2)) read_pcal = ath5k_eeprom_read_pcal_info_2413; else read_pcal = ath5k_eeprom_read_pcal_info_5111; for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) { err = read_pcal(ah, mode); if (err) return err; err = ath5k_eeprom_read_target_rate_pwr_info(ah, mode); if (err < 0) return err; } return 0; } /* Read conformance test limits used for regulatory control */ static int ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_edge_power *rep; unsigned int fmask, pmask; unsigned int ctl_mode; int i, j; u32 offset; u16 val; pmask = AR5K_EEPROM_POWER_M; fmask = AR5K_EEPROM_FREQ_M(ee->ee_version); offset = AR5K_EEPROM_CTL(ee->ee_version); ee->ee_ctls = AR5K_EEPROM_N_CTLS(ee->ee_version); for (i = 0; i < ee->ee_ctls; i += 2) { AR5K_EEPROM_READ(offset++, val); ee->ee_ctl[i] = (val >> 8) & 0xff; ee->ee_ctl[i + 1] = val & 0xff; } offset = AR5K_EEPROM_GROUP8_OFFSET; if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0) offset += AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) - AR5K_EEPROM_GROUP5_OFFSET; else offset += AR5K_EEPROM_GROUPS_START(ee->ee_version); rep = ee->ee_ctl_pwr; for (i = 0; i < ee->ee_ctls; i++) { switch (ee->ee_ctl[i] & AR5K_CTL_MODE_M) { case AR5K_CTL_11A: case AR5K_CTL_TURBO: ctl_mode = AR5K_EEPROM_MODE_11A; break; default: ctl_mode = AR5K_EEPROM_MODE_11G; break; } if (ee->ee_ctl[i] == 0) { if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) offset += 8; else offset += 7; rep += AR5K_EEPROM_N_EDGES; continue; } if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { AR5K_EEPROM_READ(offset++, val); rep[j].freq = (val >> 8) & fmask; rep[j + 1].freq = val & fmask; } for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { AR5K_EEPROM_READ(offset++, val); rep[j].edge = (val >> 8) & pmask; rep[j].flag = (val >> 14) & 1; rep[j + 1].edge = val & pmask; rep[j + 1].flag = (val >> 6) & 1; } } else { AR5K_EEPROM_READ(offset++, val); rep[0].freq = (val >> 9) & fmask; rep[1].freq = (val >> 2) & fmask; rep[2].freq = (val << 5) & fmask; AR5K_EEPROM_READ(offset++, val); rep[2].freq |= (val >> 11) & 0x1f; rep[3].freq = (val >> 4) & fmask; rep[4].freq = (val << 3) & fmask; AR5K_EEPROM_READ(offset++, val); rep[4].freq |= (val >> 13) & 0x7; rep[5].freq = (val >> 6) & fmask; rep[6].freq = (val << 1) & fmask; AR5K_EEPROM_READ(offset++, val); rep[6].freq |= (val >> 15) & 0x1; rep[7].freq = (val >> 8) & fmask; rep[0].edge = (val >> 2) & pmask; rep[1].edge = (val << 4) & pmask; AR5K_EEPROM_READ(offset++, val); rep[1].edge |= (val >> 12) & 0xf; rep[2].edge = (val >> 6) & pmask; rep[3].edge = val & pmask; AR5K_EEPROM_READ(offset++, val); rep[4].edge = (val >> 10) & pmask; rep[5].edge = (val >> 4) & pmask; rep[6].edge = (val << 2) & pmask; AR5K_EEPROM_READ(offset++, val); rep[6].edge |= (val >> 14) & 0x3; rep[7].edge = (val >> 8) & pmask; } for (j = 0; j < AR5K_EEPROM_N_EDGES; j++) { rep[j].freq = ath5k_eeprom_bin2freq(ee, rep[j].freq, ctl_mode); } rep += AR5K_EEPROM_N_EDGES; } return 0; } static int ath5k_eeprom_read_spur_chans(struct ath5k_hw *ah) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 offset; u16 val; int ret = 0, i; offset = AR5K_EEPROM_CTL(ee->ee_version) + AR5K_EEPROM_N_CTLS(ee->ee_version); if (ee->ee_version < AR5K_EEPROM_VERSION_5_3) { /* No spur info for 5GHz */ ee->ee_spur_chans[0][0] = AR5K_EEPROM_NO_SPUR; /* 2 channels for 2GHz (2464/2420) */ ee->ee_spur_chans[0][1] = AR5K_EEPROM_5413_SPUR_CHAN_1; ee->ee_spur_chans[1][1] = AR5K_EEPROM_5413_SPUR_CHAN_2; ee->ee_spur_chans[2][1] = AR5K_EEPROM_NO_SPUR; } else if (ee->ee_version >= AR5K_EEPROM_VERSION_5_3) { for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { AR5K_EEPROM_READ(offset, val); ee->ee_spur_chans[i][0] = val; AR5K_EEPROM_READ(offset + AR5K_EEPROM_N_SPUR_CHANS, val); ee->ee_spur_chans[i][1] = val; offset++; } } return ret; } /***********************\ * Init/Detach functions * \***********************/ /* * Initialize eeprom data structure */ int ath5k_eeprom_init(struct ath5k_hw *ah) { int err; err = ath5k_eeprom_init_header(ah); if (err < 0) return err; err = ath5k_eeprom_init_modes(ah); if (err < 0) return err; err = ath5k_eeprom_read_pcal_info(ah); if (err < 0) return err; err = ath5k_eeprom_read_ctl_info(ah); if (err < 0) return err; err = ath5k_eeprom_read_spur_chans(ah); if (err < 0) return err; return 0; } void ath5k_eeprom_detach(struct ath5k_hw *ah) { u8 mode; for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) ath5k_eeprom_free_pcal_info(ah, mode); } int ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel) { switch (channel->hw_value) { case AR5K_MODE_11A: return AR5K_EEPROM_MODE_11A; case AR5K_MODE_11G: return AR5K_EEPROM_MODE_11G; case AR5K_MODE_11B: return AR5K_EEPROM_MODE_11B; default: return -1; } } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/pci.c0000644000175000017500000002311412026211315023171 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include "../ath.h" #include "ath5k.h" #include "debug.h" #include "base.h" #include "reg.h" /* Known PCI ids */ static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = { { PCI_VDEVICE(ATHEROS, 0x0207) }, /* 5210 early */ { PCI_VDEVICE(ATHEROS, 0x0007) }, /* 5210 */ { PCI_VDEVICE(ATHEROS, 0x0011) }, /* 5311 - this is on AHB bus !*/ { PCI_VDEVICE(ATHEROS, 0x0012) }, /* 5211 */ { PCI_VDEVICE(ATHEROS, 0x0013) }, /* 5212 */ { PCI_VDEVICE(3COM_2, 0x0013) }, /* 3com 5212 */ { PCI_VDEVICE(3COM, 0x0013) }, /* 3com 3CRDAG675 5212 */ { PCI_VDEVICE(ATHEROS, 0x1014) }, /* IBM minipci 5212 */ { PCI_VDEVICE(ATHEROS, 0x0014) }, /* 5212 compatible */ { PCI_VDEVICE(ATHEROS, 0x0015) }, /* 5212 compatible */ { PCI_VDEVICE(ATHEROS, 0x0016) }, /* 5212 compatible */ { PCI_VDEVICE(ATHEROS, 0x0017) }, /* 5212 compatible */ { PCI_VDEVICE(ATHEROS, 0x0018) }, /* 5212 compatible */ { PCI_VDEVICE(ATHEROS, 0x0019) }, /* 5212 compatible */ { PCI_VDEVICE(ATHEROS, 0x001a) }, /* 2413 Griffin-lite */ { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */ { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */ { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */ { PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */ { 0 } }; MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table); /* return bus cachesize in 4B word units */ static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz) { struct ath5k_hw *ah = (struct ath5k_hw *) common->priv; u8 u8tmp; pci_read_config_byte(ah->pdev, PCI_CACHE_LINE_SIZE, &u8tmp); *csz = (int)u8tmp; /* * This check was put in to avoid "unpleasant" consequences if * the bootrom has not fully initialized all PCI devices. * Sometimes the cache line size register is not set */ if (*csz == 0) *csz = L1_CACHE_BYTES >> 2; /* Use the default size */ } /* * Read from eeprom */ static bool ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data) { struct ath5k_hw *ah = (struct ath5k_hw *) common->ah; u32 status, timeout; /* * Initialize EEPROM access */ if (ah->ah_version == AR5K_AR5210) { AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE); (void)ath5k_hw_reg_read(ah, AR5K_EEPROM_BASE + (4 * offset)); } else { ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE); AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_READ); } for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) { status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS); if (status & AR5K_EEPROM_STAT_RDDONE) { if (status & AR5K_EEPROM_STAT_RDERR) return false; *data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) & 0xffff); return true; } usleep_range(15, 20); } return false; } int ath5k_hw_read_srev(struct ath5k_hw *ah) { ah->ah_mac_srev = ath5k_hw_reg_read(ah, AR5K_SREV); return 0; } /* * Read the MAC address from eeprom or platform_data */ static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) { u8 mac_d[ETH_ALEN] = {}; u32 total, offset; u16 data; int octet; AR5K_EEPROM_READ(0x20, data); for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { AR5K_EEPROM_READ(offset, data); total += data; mac_d[octet + 1] = data & 0xff; mac_d[octet] = data >> 8; octet += 2; } if (!total || total == 3 * 0xffff) return -EINVAL; memcpy(mac, mac_d, ETH_ALEN); return 0; } /* Common ath_bus_opts structure */ static const struct ath_bus_ops ath_pci_bus_ops = { .ath_bus_type = ATH_PCI, .read_cachesize = ath5k_pci_read_cachesize, .eeprom_read = ath5k_pci_eeprom_read, .eeprom_read_mac = ath5k_pci_eeprom_read_mac, }; /********************\ * PCI Initialization * \********************/ static int __devinit ath5k_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { void __iomem *mem; struct ath5k_hw *ah; struct ieee80211_hw *hw; int ret; u8 csz; /* * L0s needs to be disabled on all ath5k cards. * * For distributions shipping with CONFIG_PCIEASPM (this will be enabled * by default in the future in 2.6.36) this will also mean both L1 and * L0s will be disabled when a pre 1.1 PCIe device is detected. We do * know L1 works correctly even for all ath5k pre 1.1 PCIe devices * though but cannot currently undue the effect of a blacklist, for * details you can read pcie_aspm_sanity_check() and see how it adjusts * the device link capability. * * It may be possible in the future to implement some PCI API to allow * drivers to override blacklists for pre 1.1 PCIe but for now it is * best to accept that both L0s and L1 will be disabled completely for * distributions shipping with CONFIG_PCIEASPM rather than having this * issue present. Motivation for adding this new API will be to help * with power consumption for some of these devices. */ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S); ret = pci_enable_device(pdev); if (ret) { dev_err(&pdev->dev, "can't enable device\n"); goto err; } /* XXX 32-bit addressing only */ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { dev_err(&pdev->dev, "32-bit DMA not available\n"); goto err_dis; } /* * Cache line size is used to size and align various * structures used to communicate with the hardware. */ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); if (csz == 0) { /* * Linux 2.4.18 (at least) writes the cache line size * register as a 16-bit wide register which is wrong. * We must have this setup properly for rx buffer * DMA to work so force a reasonable value here if it * comes up zero. */ csz = L1_CACHE_BYTES >> 2; pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); } /* * The default setting of latency timer yields poor results, * set it to the value used by other systems. It may be worth * tweaking this setting more. */ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); /* Enable bus mastering */ pci_set_master(pdev); /* * Disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state. */ pci_write_config_byte(pdev, 0x41, 0); ret = pci_request_region(pdev, 0, "ath5k"); if (ret) { dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); goto err_dis; } mem = pci_iomap(pdev, 0, 0); if (!mem) { dev_err(&pdev->dev, "cannot remap PCI memory region\n"); ret = -EIO; goto err_reg; } /* * Allocate hw (mac80211 main struct) * and hw->priv (driver private data) */ hw = ieee80211_alloc_hw(sizeof(*ah), &ath5k_hw_ops); if (hw == NULL) { dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n"); ret = -ENOMEM; goto err_map; } dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy)); ah = hw->priv; ah->hw = hw; ah->pdev = pdev; ah->dev = &pdev->dev; ah->irq = pdev->irq; ah->devid = id->device; ah->iobase = mem; /* So we can unmap it on detach */ /* Initialize */ ret = ath5k_init_ah(ah, &ath_pci_bus_ops); if (ret) goto err_free; /* Set private data */ pci_set_drvdata(pdev, hw); return 0; err_free: ieee80211_free_hw(hw); err_map: pci_iounmap(pdev, mem); err_reg: pci_release_region(pdev, 0); err_dis: pci_disable_device(pdev); err: return ret; } static void __devexit ath5k_pci_remove(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath5k_hw *ah = hw->priv; ath5k_deinit_ah(ah); pci_iounmap(pdev, ah->iobase); pci_release_region(pdev, 0); pci_disable_device(pdev); ieee80211_free_hw(hw); } #ifdef CONFIG_PM_SLEEP static int ath5k_pci_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath5k_hw *ah = hw->priv; ath5k_led_off(ah); return 0; } static int ath5k_pci_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath5k_hw *ah = hw->priv; /* * Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, 0x41, 0); ath5k_led_enable(ah); return 0; } compat_pci_suspend(ath5k_pci_suspend) compat_pci_resume(ath5k_pci_resume) static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); #define ATH5K_PM_OPS (&ath5k_pm_ops) #else #define ATH5K_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ static struct pci_driver ath5k_pci_driver = { .name = KBUILD_MODNAME, .id_table = ath5k_pci_id_table, .probe = ath5k_pci_probe, .remove = __devexit_p(ath5k_pci_remove), #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATH5K_PM_OPS, #elif defined(CONFIG_PM_SLEEP) .suspend = ath5k_pci_suspend_compat, .resume = ath5k_pci_resume_compat, #endif }; module_pci_driver(ath5k_pci_driver); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/mac80211-ops.c0000644000175000017500000005522512026211315024361 0ustar mcgrofmcgrof/*- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * Copyright (c) 2004-2005 Atheros Communications, Inc. * Copyright (c) 2006 Devicescape Software, Inc. * Copyright (c) 2007 Jiri Slaby * Copyright (c) 2007 Luis R. Rodriguez * Copyright (c) 2010 Bruno Randolf * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include "ath5k.h" #include "base.h" #include "reg.h" /********************\ * Mac80211 functions * \********************/ static void ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct ath5k_hw *ah = hw->priv; u16 qnum = skb_get_queue_mapping(skb); if (WARN_ON(qnum >= ah->ah_capabilities.cap_queues.q_tx_num)) { dev_kfree_skb_any(skb); return; } ath5k_tx_queue(hw, skb, &ah->txqs[qnum]); } static int ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; int ret; struct ath5k_vif *avf = (void *)vif->drv_priv; mutex_lock(&ah->lock); if ((vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) && (ah->num_ap_vifs + ah->num_adhoc_vifs) >= ATH_BCBUF) { ret = -ELNRNG; goto end; } /* Don't allow other interfaces if one ad-hoc is configured. * TODO: Fix the problems with ad-hoc and multiple other interfaces. * We would need to operate the HW in ad-hoc mode to allow TSF updates * for the IBSS, but this breaks with additional AP or STA interfaces * at the moment. */ if (ah->num_adhoc_vifs || (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n"); ret = -ELNRNG; goto end; } switch (vif->type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: avf->opmode = vif->type; break; default: ret = -EOPNOTSUPP; goto end; } ah->nvifs++; ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode); /* Assign the vap/adhoc to a beacon xmit slot. */ if ((avf->opmode == NL80211_IFTYPE_AP) || (avf->opmode == NL80211_IFTYPE_ADHOC) || (avf->opmode == NL80211_IFTYPE_MESH_POINT)) { int slot; WARN_ON(list_empty(&ah->bcbuf)); avf->bbuf = list_first_entry(&ah->bcbuf, struct ath5k_buf, list); list_del(&avf->bbuf->list); avf->bslot = 0; for (slot = 0; slot < ATH_BCBUF; slot++) { if (!ah->bslot[slot]) { avf->bslot = slot; break; } } BUG_ON(ah->bslot[avf->bslot] != NULL); ah->bslot[avf->bslot] = vif; if (avf->opmode == NL80211_IFTYPE_AP) ah->num_ap_vifs++; else if (avf->opmode == NL80211_IFTYPE_ADHOC) ah->num_adhoc_vifs++; else if (avf->opmode == NL80211_IFTYPE_MESH_POINT) ah->num_mesh_vifs++; } /* Any MAC address is fine, all others are included through the * filter. */ ath5k_hw_set_lladdr(ah, vif->addr); ath5k_update_bssid_mask_and_opmode(ah, vif); ret = 0; end: mutex_unlock(&ah->lock); return ret; } static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; struct ath5k_vif *avf = (void *)vif->drv_priv; unsigned int i; mutex_lock(&ah->lock); ah->nvifs--; if (avf->bbuf) { ath5k_txbuf_free_skb(ah, avf->bbuf); list_add_tail(&avf->bbuf->list, &ah->bcbuf); for (i = 0; i < ATH_BCBUF; i++) { if (ah->bslot[i] == vif) { ah->bslot[i] = NULL; break; } } avf->bbuf = NULL; } if (avf->opmode == NL80211_IFTYPE_AP) ah->num_ap_vifs--; else if (avf->opmode == NL80211_IFTYPE_ADHOC) ah->num_adhoc_vifs--; else if (avf->opmode == NL80211_IFTYPE_MESH_POINT) ah->num_mesh_vifs--; ath5k_update_bssid_mask_and_opmode(ah, NULL); mutex_unlock(&ah->lock); } /* * TODO: Phy disable/diversity etc */ static int ath5k_config(struct ieee80211_hw *hw, u32 changed) { struct ath5k_hw *ah = hw->priv; struct ieee80211_conf *conf = &hw->conf; int ret = 0; int i; mutex_lock(&ah->lock); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { ret = ath5k_chan_set(ah, conf->channel); if (ret < 0) goto unlock; } if ((changed & IEEE80211_CONF_CHANGE_POWER) && (ah->ah_txpower.txp_requested != conf->power_level)) { ah->ah_txpower.txp_requested = conf->power_level; /* Half dB steps */ ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2)); } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { ah->ah_retry_long = conf->long_frame_max_tx_count; ah->ah_retry_short = conf->short_frame_max_tx_count; for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) ath5k_hw_set_tx_retry_limits(ah, i); } /* TODO: * 1) Move this on config_interface and handle each case * separately eg. when we have only one STA vif, use * AR5K_ANTMODE_SINGLE_AP * * 2) Allow the user to change antenna mode eg. when only * one antenna is present * * 3) Allow the user to set default/tx antenna when possible * * 4) Default mode should handle 90% of the cases, together * with fixed a/b and single AP modes we should be able to * handle 99%. Sectored modes are extreme cases and i still * haven't found a usage for them. If we decide to support them, * then we must allow the user to set how many tx antennas we * have available */ ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); unlock: mutex_unlock(&ah->lock); return ret; } static void ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct ath5k_vif *avf = (void *)vif->drv_priv; struct ath5k_hw *ah = hw->priv; struct ath_common *common = ath5k_hw_common(ah); mutex_lock(&ah->lock); if (changes & BSS_CHANGED_BSSID) { /* Cache for later use during resets */ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = 0; ath5k_hw_set_bssid(ah); mmiowb(); } if (changes & BSS_CHANGED_BEACON_INT) ah->bintval = bss_conf->beacon_int; if (changes & BSS_CHANGED_ERP_SLOT) { int slot_time; ah->ah_short_slot = bss_conf->use_short_slot; slot_time = ath5k_hw_get_default_slottime(ah) + 3 * ah->ah_coverage_class; ath5k_hw_set_ifs_intervals(ah, slot_time); } if (changes & BSS_CHANGED_ASSOC) { avf->assoc = bss_conf->assoc; if (bss_conf->assoc) ah->assoc = bss_conf->assoc; else ah->assoc = ath5k_any_vif_assoc(ah); if (ah->opmode == NL80211_IFTYPE_STATION) ath5k_set_beacon_filter(hw, ah->assoc); ath5k_hw_set_ledstate(ah, ah->assoc ? AR5K_LED_ASSOC : AR5K_LED_INIT); if (bss_conf->assoc) { ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "Bss Info ASSOC %d, bssid: %pM\n", bss_conf->aid, common->curbssid); common->curaid = bss_conf->aid; ath5k_hw_set_bssid(ah); /* Once ANI is available you would start it here */ } } if (changes & BSS_CHANGED_BEACON) { spin_lock_bh(&ah->block); ath5k_beacon_update(hw, vif); spin_unlock_bh(&ah->block); } if (changes & BSS_CHANGED_BEACON_ENABLED) ah->enable_beacon = bss_conf->enable_beacon; if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON_INT)) ath5k_beacon_config(ah); mutex_unlock(&ah->lock); } static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *ha) #endif { u32 mfilt[2], val; u8 pos; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; #else int i; #endif mfilt[0] = 0; mfilt[1] = 1; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { #else for (i = 0; i < mc_count; i++) { if (!ha) break; #endif /* calculate XOR of eight 6-bit values */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) val = get_unaligned_le32(ha->addr + 0); #else val = get_unaligned_le32(ha->dmi_addr + 0); #endif pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) val = get_unaligned_le32(ha->addr + 3); #else val = get_unaligned_le32(ha->dmi_addr + 3); #endif pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; pos &= 0x3f; mfilt[pos / 32] |= (1 << (pos % 32)); /* XXX: we might be able to just do this instead, * but not sure, needs testing, if we do use this we'd * need to inform below not to reset the mcast */ /* ath5k_hw_set_mcast_filterindex(ah, * ha->addr[5]); */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) ha = ha->next; #endif } return ((u64)(mfilt[1]) << 32) | mfilt[0]; } /* * o always accept unicast, broadcast, and multicast traffic * o multicast traffic for all BSSIDs will be enabled if mac80211 * says it should be * o maintain current state of phy ofdm or phy cck error reception. * If the hardware detects any of these type of errors then * ath5k_hw_get_rx_filter() will pass to us the respective * hardware filters to be able to receive these type of frames. * o probe request frames are accepted only when operating in * hostap, adhoc, or monitor modes * o enable promiscuous mode according to the interface state * o accept beacons: * - when operating in adhoc mode so the 802.11 layer creates * node table entries for peers, * - when operating in station mode for collecting rssi data when * the station is otherwise quiet, or * - when scanning */ static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 multicast) { #define SUPPORTED_FIF_FLAGS \ (FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC) struct ath5k_hw *ah = hw->priv; u32 mfilt[2], rfilt; struct ath5k_vif_iter_data iter_data; /* to count STA interfaces */ mutex_lock(&ah->lock); mfilt[0] = multicast; mfilt[1] = multicast >> 32; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; /* If HW detects any phy or radar errors, leave those filters on. * Also, always enable Unicast, Broadcasts and Multicast * XXX: move unicast, bssid broadcasts and multicast to mac80211 */ rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) | (AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST | AR5K_RX_FILTER_MCAST); if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) { if (*new_flags & FIF_PROMISC_IN_BSS) __set_bit(ATH_STAT_PROMISC, ah->status); else __clear_bit(ATH_STAT_PROMISC, ah->status); } if (test_bit(ATH_STAT_PROMISC, ah->status)) rfilt |= AR5K_RX_FILTER_PROM; /* Note, AR5K_RX_FILTER_MCAST is already enabled */ if (*new_flags & FIF_ALLMULTI) { mfilt[0] = ~0; mfilt[1] = ~0; } /* This is the best we can do */ if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL)) rfilt |= AR5K_RX_FILTER_PHYERR; /* FIF_BCN_PRBRESP_PROMISC really means to enable beacons * and probes for any BSSID */ if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (ah->nvifs > 1)) rfilt |= AR5K_RX_FILTER_BEACON; /* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not * set we should only pass on control frames for this * station. This needs testing. I believe right now this * enables *all* control frames, which is OK.. but * but we should see if we can improve on granularity */ if (*new_flags & FIF_CONTROL) rfilt |= AR5K_RX_FILTER_CONTROL; /* Additional settings per mode -- this is per ath5k */ /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */ switch (ah->opmode) { case NL80211_IFTYPE_MESH_POINT: rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: rfilt |= AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_BEACON; break; case NL80211_IFTYPE_STATION: if (ah->assoc) rfilt |= AR5K_RX_FILTER_BEACON; default: break; } iter_data.hw_macaddr = NULL; iter_data.n_stas = 0; iter_data.need_set_hw_addr = false; ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, &iter_data); /* Set up RX Filter */ if (iter_data.n_stas > 1) { /* If you have multiple STA interfaces connected to * different APs, ARPs are not received (most of the time?) * Enabling PROMISC appears to fix that problem. */ rfilt |= AR5K_RX_FILTER_PROM; } /* Set filters */ ath5k_hw_set_rx_filter(ah, rfilt); /* Set multicast bits */ ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); /* Set the cached hw filter flags, this will later actually * be set in HW */ ah->filter_flags = rfilt; mutex_unlock(&ah->lock); } static int ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath5k_hw *ah = hw->priv; struct ath_common *common = ath5k_hw_common(ah); int ret = 0; if (ath5k_modparam_nohwcrypt) return -EOPNOTSUPP; if (vif->type == NL80211_IFTYPE_ADHOC && (key->cipher == WLAN_CIPHER_SUITE_TKIP || key->cipher == WLAN_CIPHER_SUITE_CCMP) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { /* don't program group keys when using IBSS_RSN */ return -EOPNOTSUPP; } switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: break; case WLAN_CIPHER_SUITE_CCMP: if (common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM) break; return -EOPNOTSUPP; default: WARN_ON(1); return -EINVAL; } mutex_lock(&ah->lock); switch (cmd) { case SET_KEY: ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (key->cipher == WLAN_CIPHER_SUITE_CCMP) key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; ret = 0; } break; case DISABLE_KEY: ath_key_delete(common, key); break; default: ret = -EINVAL; } mmiowb(); mutex_unlock(&ah->lock); return ret; } static void ath5k_sw_scan_start(struct ieee80211_hw *hw) { struct ath5k_hw *ah = hw->priv; if (!ah->assoc) ath5k_hw_set_ledstate(ah, AR5K_LED_SCAN); } static void ath5k_sw_scan_complete(struct ieee80211_hw *hw) { struct ath5k_hw *ah = hw->priv; ath5k_hw_set_ledstate(ah, ah->assoc ? AR5K_LED_ASSOC : AR5K_LED_INIT); } static int ath5k_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct ath5k_hw *ah = hw->priv; /* Force update */ ath5k_hw_update_mib_counters(ah); stats->dot11ACKFailureCount = ah->stats.ack_fail; stats->dot11RTSFailureCount = ah->stats.rts_fail; stats->dot11RTSSuccessCount = ah->stats.rts_ok; stats->dot11FCSErrorCount = ah->stats.fcs_error; return 0; } static int ath5k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath5k_hw *ah = hw->priv; struct ath5k_txq_info qi; int ret = 0; if (queue >= ah->ah_capabilities.cap_queues.q_tx_num) return 0; mutex_lock(&ah->lock); ath5k_hw_get_tx_queueprops(ah, queue, &qi); qi.tqi_aifs = params->aifs; qi.tqi_cw_min = params->cw_min; qi.tqi_cw_max = params->cw_max; qi.tqi_burst_time = params->txop * 32; ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "Configure tx [queue %d], " "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", queue, params->aifs, params->cw_min, params->cw_max, params->txop); if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) { ATH5K_ERR(ah, "Unable to update hardware queue %u!\n", queue); ret = -EIO; } else ath5k_hw_reset_tx_queue(ah, queue); mutex_unlock(&ah->lock); return ret; } static u64 ath5k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; return ath5k_hw_get_tsf64(ah); } static void ath5k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct ath5k_hw *ah = hw->priv; ath5k_hw_set_tsf64(ah, tsf); } static void ath5k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; /* * in IBSS mode we need to update the beacon timers too. * this will also reset the TSF if we call it with 0 */ if (ah->opmode == NL80211_IFTYPE_ADHOC) ath5k_beacon_update_timers(ah, 0); else ath5k_hw_reset_tsf(ah); } static int ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct ath5k_hw *ah = hw->priv; struct ieee80211_conf *conf = &hw->conf; struct ath_common *common = ath5k_hw_common(ah); struct ath_cycle_counters *cc = &common->cc_survey; unsigned int div = common->clockrate * 1000; if (idx != 0) return -ENOENT; spin_lock_bh(&common->cc_lock); ath_hw_cycle_counters_update(common); if (cc->cycles > 0) { ah->survey.channel_time += cc->cycles / div; ah->survey.channel_time_busy += cc->rx_busy / div; ah->survey.channel_time_rx += cc->rx_frame / div; ah->survey.channel_time_tx += cc->tx_frame / div; } memset(cc, 0, sizeof(*cc)); spin_unlock_bh(&common->cc_lock); memcpy(survey, &ah->survey, sizeof(*survey)); survey->channel = conf->channel; survey->noise = ah->ah_noise_floor; survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_RX | SURVEY_INFO_CHANNEL_TIME_TX; return 0; } /** * ath5k_set_coverage_class - Set IEEE 802.11 coverage class * * @hw: struct ieee80211_hw pointer * @coverage_class: IEEE 802.11 coverage class number * * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given * coverage class. The values are persistent, they are restored after device * reset. */ static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) { struct ath5k_hw *ah = hw->priv; mutex_lock(&ah->lock); ath5k_hw_set_coverage_class(ah, coverage_class); mutex_unlock(&ah->lock); } static int ath5k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) { struct ath5k_hw *ah = hw->priv; if (tx_ant == 1 && rx_ant == 1) ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A); else if (tx_ant == 2 && rx_ant == 2) ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B); else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3) ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); else return -EINVAL; return 0; } static int ath5k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) { struct ath5k_hw *ah = hw->priv; switch (ah->ah_ant_mode) { case AR5K_ANTMODE_FIXED_A: *tx_ant = 1; *rx_ant = 1; break; case AR5K_ANTMODE_FIXED_B: *tx_ant = 2; *rx_ant = 2; break; case AR5K_ANTMODE_DEFAULT: *tx_ant = 3; *rx_ant = 3; break; } return 0; } static void ath5k_get_ringparam(struct ieee80211_hw *hw, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) { struct ath5k_hw *ah = hw->priv; *tx = ah->txqs[AR5K_TX_QUEUE_ID_DATA_MIN].txq_max; *tx_max = ATH5K_TXQ_LEN_MAX; *rx = *rx_max = ATH_RXBUF; } static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx) { struct ath5k_hw *ah = hw->priv; u16 qnum; /* only support setting tx ring size for now */ if (rx != ATH_RXBUF) return -EINVAL; /* restrict tx ring size min/max */ if (!tx || tx > ATH5K_TXQ_LEN_MAX) return -EINVAL; for (qnum = 0; qnum < ARRAY_SIZE(ah->txqs); qnum++) { if (!ah->txqs[qnum].setup) continue; if (ah->txqs[qnum].qnum < AR5K_TX_QUEUE_ID_DATA_MIN || ah->txqs[qnum].qnum > AR5K_TX_QUEUE_ID_DATA_MAX) continue; ah->txqs[qnum].txq_max = tx; if (ah->txqs[qnum].txq_len >= ah->txqs[qnum].txq_max) ieee80211_stop_queue(hw, ah->txqs[qnum].qnum); } return 0; } const struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, .start = ath5k_start, .stop = ath5k_stop, .add_interface = ath5k_add_interface, /* .change_interface = not implemented */ .remove_interface = ath5k_remove_interface, .config = ath5k_config, .bss_info_changed = ath5k_bss_info_changed, .prepare_multicast = ath5k_prepare_multicast, .configure_filter = ath5k_configure_filter, /* .set_tim = not implemented */ .set_key = ath5k_set_key, /* .update_tkip_key = not implemented */ /* .hw_scan = not implemented */ .sw_scan_start = ath5k_sw_scan_start, .sw_scan_complete = ath5k_sw_scan_complete, .get_stats = ath5k_get_stats, /* .get_tkip_seq = not implemented */ /* .set_frag_threshold = not implemented */ /* .set_rts_threshold = not implemented */ /* .sta_add = not implemented */ /* .sta_remove = not implemented */ /* .sta_notify = not implemented */ .conf_tx = ath5k_conf_tx, .get_tsf = ath5k_get_tsf, .set_tsf = ath5k_set_tsf, .reset_tsf = ath5k_reset_tsf, /* .tx_last_beacon = not implemented */ /* .ampdu_action = not needed */ .get_survey = ath5k_get_survey, .set_coverage_class = ath5k_set_coverage_class, /* .rfkill_poll = not implemented */ /* .flush = not implemented */ /* .channel_switch = not implemented */ /* .napi_poll = not implemented */ .set_antenna = ath5k_set_antenna, .get_antenna = ath5k_get_antenna, .set_ringparam = ath5k_set_ringparam, .get_ringparam = ath5k_get_ringparam, }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/sysfs.c0000644000175000017500000000705512026211315023573 0ustar mcgrofmcgrof#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include "ath5k.h" #include "reg.h" #define SIMPLE_SHOW_STORE(name, get, set) \ static ssize_t ath5k_attr_show_##name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct ieee80211_hw *hw = dev_get_drvdata(dev); \ struct ath5k_hw *ah = hw->priv; \ return snprintf(buf, PAGE_SIZE, "%d\n", get); \ } \ \ static ssize_t ath5k_attr_store_##name(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ struct ieee80211_hw *hw = dev_get_drvdata(dev); \ struct ath5k_hw *ah = hw->priv; \ int val, ret; \ \ ret = kstrtoint(buf, 10, &val); \ if (ret < 0) \ return ret; \ set(ah, val); \ return count; \ } \ static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \ ath5k_attr_show_##name, ath5k_attr_store_##name) #define SIMPLE_SHOW(name, get) \ static ssize_t ath5k_attr_show_##name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct ieee80211_hw *hw = dev_get_drvdata(dev); \ struct ath5k_hw *ah = hw->priv; \ return snprintf(buf, PAGE_SIZE, "%d\n", get); \ } \ static DEVICE_ATTR(name, S_IRUGO, ath5k_attr_show_##name, NULL) /*** ANI ***/ SIMPLE_SHOW_STORE(ani_mode, ah->ani_state.ani_mode, ath5k_ani_init); SIMPLE_SHOW_STORE(noise_immunity_level, ah->ani_state.noise_imm_level, ath5k_ani_set_noise_immunity_level); SIMPLE_SHOW_STORE(spur_level, ah->ani_state.spur_level, ath5k_ani_set_spur_immunity_level); SIMPLE_SHOW_STORE(firstep_level, ah->ani_state.firstep_level, ath5k_ani_set_firstep_level); SIMPLE_SHOW_STORE(ofdm_weak_signal_detection, ah->ani_state.ofdm_weak_sig, ath5k_ani_set_ofdm_weak_signal_detection); SIMPLE_SHOW_STORE(cck_weak_signal_detection, ah->ani_state.cck_weak_sig, ath5k_ani_set_cck_weak_signal_detection); SIMPLE_SHOW(spur_level_max, ah->ani_state.max_spur_level); static ssize_t ath5k_attr_show_noise_immunity_level_max(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", ATH5K_ANI_MAX_NOISE_IMM_LVL); } static DEVICE_ATTR(noise_immunity_level_max, S_IRUGO, ath5k_attr_show_noise_immunity_level_max, NULL); static ssize_t ath5k_attr_show_firstep_level_max(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", ATH5K_ANI_MAX_FIRSTEP_LVL); } static DEVICE_ATTR(firstep_level_max, S_IRUGO, ath5k_attr_show_firstep_level_max, NULL); static struct attribute *ath5k_sysfs_entries_ani[] = { &dev_attr_ani_mode.attr, &dev_attr_noise_immunity_level.attr, &dev_attr_spur_level.attr, &dev_attr_firstep_level.attr, &dev_attr_ofdm_weak_signal_detection.attr, &dev_attr_cck_weak_signal_detection.attr, &dev_attr_noise_immunity_level_max.attr, &dev_attr_spur_level_max.attr, &dev_attr_firstep_level_max.attr, NULL }; static struct attribute_group ath5k_attribute_group_ani = { .name = "ani", .attrs = ath5k_sysfs_entries_ani, }; /*** register / unregister ***/ int ath5k_sysfs_register(struct ath5k_hw *ah) { struct device *dev = ah->dev; int err; err = sysfs_create_group(&dev->kobj, &ath5k_attribute_group_ani); if (err) { ATH5K_ERR(ah, "failed to create sysfs group\n"); return err; } return 0; } void ath5k_sysfs_unregister(struct ath5k_hw *ah) { struct device *dev = ah->dev; sysfs_remove_group(&dev->kobj, &ath5k_attribute_group_ani); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/ani.c0000644000175000017500000005620612026211315023175 0ustar mcgrofmcgrof/* * Copyright (C) 2010 Bruno Randolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "ath5k.h" #include "reg.h" #include "debug.h" #include "ani.h" /** * DOC: Basic ANI Operation * * Adaptive Noise Immunity (ANI) controls five noise immunity parameters * depending on the amount of interference in the environment, increasing * or reducing sensitivity as necessary. * * The parameters are: * * - "noise immunity" * * - "spur immunity" * * - "firstep level" * * - "OFDM weak signal detection" * * - "CCK weak signal detection" * * Basically we look at the amount of ODFM and CCK timing errors we get and then * raise or lower immunity accordingly by setting one or more of these * parameters. * * Newer chipsets have PHY error counters in hardware which will generate a MIB * interrupt when they overflow. Older hardware has too enable PHY error frames * by setting a RX flag and then count every single PHY error. When a specified * threshold of errors has been reached we will raise immunity. * Also we regularly check the amount of errors and lower or raise immunity as * necessary. */ /***********************\ * ANI parameter control * \***********************/ /** * ath5k_ani_set_noise_immunity_level() - Set noise immunity level * @ah: The &struct ath5k_hw * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL */ void ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level) { /* TODO: * ANI documents suggest the following five levels to use, but the HAL * and ath9k use only the last two levels, making this * essentially an on/off option. There *may* be a reason for this (???), * so i stick with the HAL version for now... */ #if 0 static const s8 lo[] = { -52, -56, -60, -64, -70 }; static const s8 hi[] = { -18, -18, -16, -14, -12 }; static const s8 sz[] = { -34, -41, -48, -55, -62 }; static const s8 fr[] = { -70, -72, -75, -78, -80 }; #else static const s8 lo[] = { -64, -70 }; static const s8 hi[] = { -14, -12 }; static const s8 sz[] = { -55, -62 }; static const s8 fr[] = { -78, -80 }; #endif if (level < 0 || level >= ARRAY_SIZE(sz)) { ATH5K_ERR(ah, "noise immunity level %d out of range", level); return; } AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, AR5K_PHY_DESIRED_SIZE_TOT, sz[level]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, AR5K_PHY_AGCCOARSE_LO, lo[level]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, AR5K_PHY_AGCCOARSE_HI, hi[level]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, AR5K_PHY_SIG_FIRPWR, fr[level]); ah->ani_state.noise_imm_level = level; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); } /** * ath5k_ani_set_spur_immunity_level() - Set spur immunity level * @ah: The &struct ath5k_hw * @level: level between 0 and @max_spur_level (the maximum level is dependent * on the chip revision). */ void ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level) { static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; if (level < 0 || level >= ARRAY_SIZE(val) || level > ah->ani_state.max_spur_level) { ATH5K_ERR(ah, "spur immunity level %d out of range", level); return; } AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]); ah->ani_state.spur_level = level; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); } /** * ath5k_ani_set_firstep_level() - Set "firstep" level * @ah: The &struct ath5k_hw * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL */ void ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level) { static const int val[] = { 0, 4, 8 }; if (level < 0 || level >= ARRAY_SIZE(val)) { ATH5K_ERR(ah, "firstep level %d out of range", level); return; } AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, AR5K_PHY_SIG_FIRSTEP, val[level]); ah->ani_state.firstep_level = level; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); } /** * ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection * @ah: The &struct ath5k_hw * @on: turn on or off */ void ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on) { static const int m1l[] = { 127, 50 }; static const int m2l[] = { 127, 40 }; static const int m1[] = { 127, 0x4d }; static const int m2[] = { 127, 0x40 }; static const int m2cnt[] = { 31, 16 }; static const int m2lcnt[] = { 63, 48 }; AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]); if (on) AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); else AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); ah->ani_state.ofdm_weak_sig = on; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", on ? "on" : "off"); } /** * ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection * @ah: The &struct ath5k_hw * @on: turn on or off */ void ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on) { static const int val[] = { 8, 6 }; AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR, AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]); ah->ani_state.cck_weak_sig = on; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", on ? "on" : "off"); } /***************\ * ANI algorithm * \***************/ /** * ath5k_ani_raise_immunity() - Increase noise immunity * @ah: The &struct ath5k_hw * @as: The &struct ath5k_ani_state * @ofdm_trigger: If this is true we are called because of too many OFDM errors, * the algorithm will tune more parameters then. * * Try to raise noise immunity (=decrease sensitivity) in several steps * depending on the average RSSI of the beacons we received. */ static void ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, bool ofdm_trigger) { int rssi = ewma_read(&ah->ah_beacon_rssi_avg); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)", ofdm_trigger ? "ODFM" : "CCK"); /* first: raise noise immunity */ if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) { ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1); return; } /* only OFDM: raise spur immunity level */ if (ofdm_trigger && as->spur_level < ah->ani_state.max_spur_level) { ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1); return; } /* AP mode */ if (ah->opmode == NL80211_IFTYPE_AP) { if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); return; } /* STA and IBSS mode */ /* TODO: for IBSS mode it would be better to keep a beacon RSSI average * per each neighbour node and use the minimum of these, to make sure we * don't shut out a remote node by raising immunity too high. */ if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "beacon RSSI high"); /* only OFDM: beacon RSSI is high, we can disable ODFM weak * signal detection */ if (ofdm_trigger && as->ofdm_weak_sig) { ath5k_ani_set_ofdm_weak_signal_detection(ah, false); ath5k_ani_set_spur_immunity_level(ah, 0); return; } /* as a last resort or CCK: raise firstep level */ if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) { ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); return; } } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { /* beacon RSSI in mid range, we need OFDM weak signal detect, * but can raise firstep level */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "beacon RSSI mid"); if (ofdm_trigger && !as->ofdm_weak_sig) ath5k_ani_set_ofdm_weak_signal_detection(ah, true); if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); return; } else if (ah->ah_current_channel->band == IEEE80211_BAND_2GHZ) { /* beacon RSSI is low. in B/G mode turn of OFDM weak signal * detect and zero firstep level to maximize CCK sensitivity */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "beacon RSSI low, 2GHz"); if (ofdm_trigger && as->ofdm_weak_sig) ath5k_ani_set_ofdm_weak_signal_detection(ah, false); if (as->firstep_level > 0) ath5k_ani_set_firstep_level(ah, 0); return; } /* TODO: why not?: if (as->cck_weak_sig == true) { ath5k_ani_set_cck_weak_signal_detection(ah, false); } */ } /** * ath5k_ani_lower_immunity() - Decrease noise immunity * @ah: The &struct ath5k_hw * @as: The &struct ath5k_ani_state * * Try to lower noise immunity (=increase sensitivity) in several steps * depending on the average RSSI of the beacons we received. */ static void ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as) { int rssi = ewma_read(&ah->ah_beacon_rssi_avg); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity"); if (ah->opmode == NL80211_IFTYPE_AP) { /* AP mode */ if (as->firstep_level > 0) { ath5k_ani_set_firstep_level(ah, as->firstep_level - 1); return; } } else { /* STA and IBSS mode (see TODO above) */ if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { /* beacon signal is high, leave OFDM weak signal * detection off or it may oscillate * TODO: who said it's off??? */ } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { /* beacon RSSI is mid-range: turn on ODFM weak signal * detection and next, lower firstep level */ if (!as->ofdm_weak_sig) { ath5k_ani_set_ofdm_weak_signal_detection(ah, true); return; } if (as->firstep_level > 0) { ath5k_ani_set_firstep_level(ah, as->firstep_level - 1); return; } } else { /* beacon signal is low: only reduce firstep level */ if (as->firstep_level > 0) { ath5k_ani_set_firstep_level(ah, as->firstep_level - 1); return; } } } /* all modes */ if (as->spur_level > 0) { ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1); return; } /* finally, reduce noise immunity */ if (as->noise_imm_level > 0) { ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1); return; } } /** * ath5k_hw_ani_get_listen_time() - Update counters and return listening time * @ah: The &struct ath5k_hw * @as: The &struct ath5k_ani_state * * Return an approximation of the time spent "listening" in milliseconds (ms) * since the last call of this function. * Save a snapshot of the counter values for debugging/statistics. */ static int ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as) { struct ath_common *common = ath5k_hw_common(ah); int listen; spin_lock_bh(&common->cc_lock); ath_hw_cycle_counters_update(common); memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc)); /* clears common->cc_ani */ listen = ath_hw_get_listen_time(common); spin_unlock_bh(&common->cc_lock); return listen; } /** * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters * @ah: The &struct ath5k_hw * @as: The &struct ath5k_ani_state * * Clear the PHY error counters as soon as possible, since this might be called * from a MIB interrupt and we want to make sure we don't get interrupted again. * Add the count of CCK and OFDM errors to our internal state, so it can be used * by the algorithm later. * * Will be called from interrupt and tasklet context. * Returns 0 if both counters are zero. */ static int ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah, struct ath5k_ani_state *as) { unsigned int ofdm_err, cck_err; if (!ah->ah_capabilities.cap_has_phyerr_counters) return 0; ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1); cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2); /* reset counters first, we might be in a hurry (interrupt) */ ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, AR5K_PHYERR_CNT1); ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, AR5K_PHYERR_CNT2); ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err); cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err); /* sometimes both can be zero, especially when there is a superfluous * second interrupt. detect that here and return an error. */ if (ofdm_err <= 0 && cck_err <= 0) return 0; /* avoid negative values should one of the registers overflow */ if (ofdm_err > 0) { as->ofdm_errors += ofdm_err; as->sum_ofdm_errors += ofdm_err; } if (cck_err > 0) { as->cck_errors += cck_err; as->sum_cck_errors += cck_err; } return 1; } /** * ath5k_ani_period_restart() - Restart ANI period * @as: The &struct ath5k_ani_state * * Just reset counters, so they are clear for the next "ani period". */ static void ath5k_ani_period_restart(struct ath5k_ani_state *as) { /* keep last values for debugging */ as->last_ofdm_errors = as->ofdm_errors; as->last_cck_errors = as->cck_errors; as->last_listen = as->listen_time; as->ofdm_errors = 0; as->cck_errors = 0; as->listen_time = 0; } /** * ath5k_ani_calibration() - The main ANI calibration function * @ah: The &struct ath5k_hw * * We count OFDM and CCK errors relative to the time where we did not send or * receive ("listen" time) and raise or lower immunity accordingly. * This is called regularly (every second) from the calibration timer, but also * when an error threshold has been reached. * * In order to synchronize access from different contexts, this should be * called only indirectly by scheduling the ANI tasklet! */ void ath5k_ani_calibration(struct ath5k_hw *ah) { struct ath5k_ani_state *as = &ah->ani_state; int listen, ofdm_high, ofdm_low, cck_high, cck_low; /* get listen time since last call and add it to the counter because we * might not have restarted the "ani period" last time. * always do this to calculate the busy time also in manual mode */ listen = ath5k_hw_ani_get_listen_time(ah, as); as->listen_time += listen; if (as->ani_mode != ATH5K_ANI_MODE_AUTO) return; ath5k_ani_save_and_clear_phy_errors(ah, as); ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000; cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000; ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000; cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "listen %d (now %d)", as->listen_time, listen); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "check high ofdm %d/%d cck %d/%d", as->ofdm_errors, ofdm_high, as->cck_errors, cck_high); if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) { /* too many PHY errors - we have to raise immunity */ bool ofdm_flag = as->ofdm_errors > ofdm_high ? true : false; ath5k_ani_raise_immunity(ah, as, ofdm_flag); ath5k_ani_period_restart(as); } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) { /* If more than 5 (TODO: why 5?) periods have passed and we got * relatively little errors we can try to lower immunity */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "check low ofdm %d/%d cck %d/%d", as->ofdm_errors, ofdm_low, as->cck_errors, cck_low); if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low) ath5k_ani_lower_immunity(ah, as); ath5k_ani_period_restart(as); } } /*******************\ * Interrupt handler * \*******************/ /** * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters * @ah: The &struct ath5k_hw * * Just read & reset the registers quickly, so they don't generate more * interrupts, save the counters and schedule the tasklet to decide whether * to raise immunity or not. * * We just need to handle PHY error counters, ath5k_hw_update_mib_counters() * should take care of all "normal" MIB interrupts. */ void ath5k_ani_mib_intr(struct ath5k_hw *ah) { struct ath5k_ani_state *as = &ah->ani_state; /* nothing to do here if HW does not have PHY error counters - they * can't be the reason for the MIB interrupt then */ if (!ah->ah_capabilities.cap_has_phyerr_counters) return; /* not in use but clear anyways */ ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) return; /* If one of the errors triggered, we can get a superfluous second * interrupt, even though we have already reset the register. The * function detects that so we can return early. */ if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) return; if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH || as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) tasklet_schedule(&ah->ani_tasklet); } /** * ath5k_ani_phy_error_report - Used by older HW to report PHY errors * * @ah: The &struct ath5k_hw * @phyerr: One of enum ath5k_phy_error_code * * This is used by hardware without PHY error counters to report PHY errors * on a frame-by-frame basis, instead of the interrupt. */ void ath5k_ani_phy_error_report(struct ath5k_hw *ah, enum ath5k_phy_error_code phyerr) { struct ath5k_ani_state *as = &ah->ani_state; if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) { as->ofdm_errors++; if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH) tasklet_schedule(&ah->ani_tasklet); } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) { as->cck_errors++; if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) tasklet_schedule(&ah->ani_tasklet); } } /****************\ * Initialization * \****************/ /** * ath5k_enable_phy_err_counters() - Enable PHY error counters * @ah: The &struct ath5k_hw * * Enable PHY error counters for OFDM and CCK timing errors. */ static void ath5k_enable_phy_err_counters(struct ath5k_hw *ah) { ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, AR5K_PHYERR_CNT1); ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, AR5K_PHYERR_CNT2); ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK); ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK); /* not in use */ ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); } /** * ath5k_disable_phy_err_counters() - Disable PHY error counters * @ah: The &struct ath5k_hw * * Disable PHY error counters for OFDM and CCK timing errors. */ static void ath5k_disable_phy_err_counters(struct ath5k_hw *ah) { ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1); ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2); ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK); ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK); /* not in use */ ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); } /** * ath5k_ani_init() - Initialize ANI * @ah: The &struct ath5k_hw * @mode: One of enum ath5k_ani_mode * * Initialize ANI according to mode. */ void ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode) { /* ANI is only possible on 5212 and newer */ if (ah->ah_version < AR5K_AR5212) return; if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) { ATH5K_ERR(ah, "ANI mode %d out of range", mode); return; } /* clear old state information */ memset(&ah->ani_state, 0, sizeof(ah->ani_state)); /* older hardware has more spur levels than newer */ if (ah->ah_mac_srev < AR5K_SREV_AR2414) ah->ani_state.max_spur_level = 7; else ah->ani_state.max_spur_level = 2; /* initial values for our ani parameters */ if (mode == ATH5K_ANI_MODE_OFF) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n"); } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI manual low -> high sensitivity\n"); ath5k_ani_set_noise_immunity_level(ah, 0); ath5k_ani_set_spur_immunity_level(ah, 0); ath5k_ani_set_firstep_level(ah, 0); ath5k_ani_set_ofdm_weak_signal_detection(ah, true); ath5k_ani_set_cck_weak_signal_detection(ah, true); } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI manual high -> low sensitivity\n"); ath5k_ani_set_noise_immunity_level(ah, ATH5K_ANI_MAX_NOISE_IMM_LVL); ath5k_ani_set_spur_immunity_level(ah, ah->ani_state.max_spur_level); ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL); ath5k_ani_set_ofdm_weak_signal_detection(ah, false); ath5k_ani_set_cck_weak_signal_detection(ah, false); } else if (mode == ATH5K_ANI_MODE_AUTO) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n"); ath5k_ani_set_noise_immunity_level(ah, 0); ath5k_ani_set_spur_immunity_level(ah, 0); ath5k_ani_set_firstep_level(ah, 0); ath5k_ani_set_ofdm_weak_signal_detection(ah, true); ath5k_ani_set_cck_weak_signal_detection(ah, false); } /* newer hardware has PHY error counter registers which we can use to * get OFDM and CCK error counts. older hardware has to set rxfilter and * report every single PHY error by calling ath5k_ani_phy_error_report() */ if (mode == ATH5K_ANI_MODE_AUTO) { if (ah->ah_capabilities.cap_has_phyerr_counters) ath5k_enable_phy_err_counters(ah); else ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) | AR5K_RX_FILTER_PHYERR); } else { if (ah->ah_capabilities.cap_has_phyerr_counters) ath5k_disable_phy_err_counters(ah); else ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) & ~AR5K_RX_FILTER_PHYERR); } ah->ani_state.ani_mode = mode; } /**************\ * Debug output * \**************/ #ifdef CONFIG_ATH5K_DEBUG /** * ath5k_ani_print_counters() - Print ANI counters * @ah: The &struct ath5k_hw * * Used for debugging ANI */ void ath5k_ani_print_counters(struct ath5k_hw *ah) { /* clears too */ pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL)); pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL)); pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK)); pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL)); /* no clear */ pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX)); pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX)); pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR)); pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE)); pr_notice("AR5K_PHYERR_CNT1\t%d\n", ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)); pr_notice("AR5K_PHYERR_CNT2\t%d\n", ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)); pr_notice("AR5K_OFDM_FIL_CNT\t%d\n", ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT)); pr_notice("AR5K_CCK_FIL_CNT\t%d\n", ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT)); } #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/led.c0000644000175000017500000001511312026211315023162 0ustar mcgrofmcgrof/* * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * Copyright (c) 2004-2005 Atheros Communications, Inc. * Copyright (c) 2007 Jiri Slaby * Copyright (c) 2009 Bob Copeland * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include "ath5k.h" #define ATH_SDEVICE(subv, subd) \ .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \ .subvendor = (subv), .subdevice = (subd) #define ATH_LED(pin, polarity) .driver_data = (((pin) << 8) | (polarity)) #define ATH_PIN(data) ((data) >> 8) #define ATH_POLARITY(data) ((data) & 0xff) /* Devices we match on for LED config info (typically laptops) */ static DEFINE_PCI_DEVICE_TABLE(ath5k_led_devices) = { /* AR5211 */ { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5211), ATH_LED(0, 0) }, /* HP Compaq nc6xx, nc4000, nx6000 */ { ATH_SDEVICE(PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID), ATH_LED(1, 1) }, /* Acer Aspire One A150 (maximlevitsky@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe008), ATH_LED(3, 0) }, /* Acer Aspire One AO531h AO751h (keng-yu.lin@canonical.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe00d), ATH_LED(3, 0) }, /* Acer Ferrari 5000 (russ.dill@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) }, /* E-machines E510 (tuliom@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) }, /* BenQ Joybook R55v (nowymarluk@wp.pl) */ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0100), ATH_LED(1, 0) }, /* Acer Extensa 5620z (nekoreeve@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) }, /* Fukato Datacask Jupiter 1014a (mrb74@gmx.at) */ { ATH_SDEVICE(PCI_VENDOR_ID_AZWAVE, 0x1026), ATH_LED(3, 0) }, /* IBM ThinkPad AR5BXB6 (legovini@spiro.fisica.unipd.it) */ { ATH_SDEVICE(PCI_VENDOR_ID_IBM, 0x058a), ATH_LED(1, 0) }, /* HP Compaq CQ60-206US (ddreggors@jumptv.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) }, /* HP Compaq C700 (nitrousnrg@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) }, /* LiteOn AR5BXB63 (magooz@salug.it) */ { ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) }, /* IBM-specific AR5212 (all others) */ { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) }, /* Dell Vostro A860 (shahar@shahar-or.co.il) */ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0112), ATH_LED(3, 0) }, { } }; void ath5k_led_enable(struct ath5k_hw *ah) { if (test_bit(ATH_STAT_LEDSOFT, ah->status)) { ath5k_hw_set_gpio_output(ah, ah->led_pin); ath5k_led_off(ah); } } static void ath5k_led_on(struct ath5k_hw *ah) { if (!test_bit(ATH_STAT_LEDSOFT, ah->status)) return; ath5k_hw_set_gpio(ah, ah->led_pin, ah->led_on); } void ath5k_led_off(struct ath5k_hw *ah) { if (!test_bit(ATH_STAT_LEDSOFT, ah->status)) return; ath5k_hw_set_gpio(ah, ah->led_pin, !ah->led_on); } static void ath5k_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct ath5k_led *led = container_of(led_dev, struct ath5k_led, led_dev); if (brightness == LED_OFF) ath5k_led_off(led->ah); else ath5k_led_on(led->ah); } static int ath5k_register_led(struct ath5k_hw *ah, struct ath5k_led *led, const char *name, char *trigger) { int err; led->ah = ah; strncpy(led->name, name, sizeof(led->name)); led->led_dev.name = led->name; led->led_dev.default_trigger = trigger; led->led_dev.brightness_set = ath5k_led_brightness_set; err = led_classdev_register(ah->dev, &led->led_dev); if (err) { ATH5K_WARN(ah, "could not register LED %s\n", name); led->ah = NULL; } return err; } static void ath5k_unregister_led(struct ath5k_led *led) { if (!led->ah) return; led_classdev_unregister(&led->led_dev); ath5k_led_off(led->ah); led->ah = NULL; } void ath5k_unregister_leds(struct ath5k_hw *ah) { ath5k_unregister_led(&ah->rx_led); ath5k_unregister_led(&ah->tx_led); } int __devinit ath5k_init_leds(struct ath5k_hw *ah) { int ret = 0; struct ieee80211_hw *hw = ah->hw; #ifndef CONFIG_ATHEROS_AR231X struct pci_dev *pdev = ah->pdev; #endif char name[ATH5K_LED_MAX_NAME_LEN + 1]; const struct pci_device_id *match; if (!ah->pdev) return 0; #ifdef CONFIG_ATHEROS_AR231X match = NULL; #else match = pci_match_id(&ath5k_led_devices[0], pdev); #endif if (match) { __set_bit(ATH_STAT_LEDSOFT, ah->status); ah->led_pin = ATH_PIN(match->driver_data); ah->led_on = ATH_POLARITY(match->driver_data); } if (!test_bit(ATH_STAT_LEDSOFT, ah->status)) goto out; ath5k_led_enable(ah); snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy)); ret = ath5k_register_led(ah, &ah->rx_led, name, ieee80211_get_rx_led_name(hw)); if (ret) goto out; snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy)); ret = ath5k_register_led(ah, &ah->tx_led, name, ieee80211_get_tx_led_name(hw)); out: return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/base.c0000644000175000017500000024266512026211315023346 0ustar mcgrofmcgrof/*- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * Copyright (c) 2004-2005 Atheros Communications, Inc. * Copyright (c) 2006 Devicescape Software, Inc. * Copyright (c) 2007 Jiri Slaby * Copyright (c) 2007 Luis R. Rodriguez * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base.h" #include "reg.h" #include "debug.h" #include "ani.h" #include "ath5k.h" #include "../regd.h" #define CREATE_TRACE_POINTS #include "trace.h" bool ath5k_modparam_nohwcrypt; module_param_named(nohwcrypt, ath5k_modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); static bool modparam_fastchanswitch; module_param_named(fastchanswitch, modparam_fastchanswitch, bool, S_IRUGO); MODULE_PARM_DESC(fastchanswitch, "Enable fast channel switching for AR2413/AR5413 radios."); static bool ath5k_modparam_no_hw_rfkill_switch; module_param_named(no_hw_rfkill_switch, ath5k_modparam_no_hw_rfkill_switch, bool, S_IRUGO); MODULE_PARM_DESC(no_hw_rfkill_switch, "Ignore the GPIO RFKill switch state"); /* Module info */ MODULE_AUTHOR("Jiri Slaby"); MODULE_AUTHOR("Nick Kossifidis"); MODULE_DESCRIPTION("Support for 5xxx series of Atheros 802.11 wireless LAN cards."); MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); static int ath5k_init(struct ieee80211_hw *hw); static int ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, bool skip_pcu); /* Known SREVs */ static const struct ath5k_srev_name srev_names[] = { #ifdef CONFIG_ATHEROS_AR231X { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R2 }, { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R7 }, { "2313", AR5K_VERSION_MAC, AR5K_SREV_AR2313_R8 }, { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R6 }, { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R7 }, { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R1 }, { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R2 }, #else { "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 }, { "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 }, { "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A }, { "5311B", AR5K_VERSION_MAC, AR5K_SREV_AR5311B }, { "5211", AR5K_VERSION_MAC, AR5K_SREV_AR5211 }, { "5212", AR5K_VERSION_MAC, AR5K_SREV_AR5212 }, { "5213", AR5K_VERSION_MAC, AR5K_SREV_AR5213 }, { "5213A", AR5K_VERSION_MAC, AR5K_SREV_AR5213A }, { "2413", AR5K_VERSION_MAC, AR5K_SREV_AR2413 }, { "2414", AR5K_VERSION_MAC, AR5K_SREV_AR2414 }, { "5424", AR5K_VERSION_MAC, AR5K_SREV_AR5424 }, { "5413", AR5K_VERSION_MAC, AR5K_SREV_AR5413 }, { "5414", AR5K_VERSION_MAC, AR5K_SREV_AR5414 }, { "2415", AR5K_VERSION_MAC, AR5K_SREV_AR2415 }, { "5416", AR5K_VERSION_MAC, AR5K_SREV_AR5416 }, { "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 }, { "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 }, { "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 }, #endif { "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN }, { "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 }, { "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 }, { "5111A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111A }, { "2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111 }, { "5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112 }, { "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A }, { "5112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112B }, { "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 }, { "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A }, { "2112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112B }, { "2413", AR5K_VERSION_RAD, AR5K_SREV_RAD_2413 }, { "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 }, { "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 }, { "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 }, #ifdef CONFIG_ATHEROS_AR231X { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 }, { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 }, #endif { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN }, }; static const struct ieee80211_rate ath5k_rates[] = { { .bitrate = 10, .hw_value = ATH5K_RATE_CODE_1M, }, { .bitrate = 20, .hw_value = ATH5K_RATE_CODE_2M, .hw_value_short = ATH5K_RATE_CODE_2M | AR5K_SET_SHORT_PREAMBLE, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = ATH5K_RATE_CODE_5_5M, .hw_value_short = ATH5K_RATE_CODE_5_5M | AR5K_SET_SHORT_PREAMBLE, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .hw_value = ATH5K_RATE_CODE_11M, .hw_value_short = ATH5K_RATE_CODE_11M | AR5K_SET_SHORT_PREAMBLE, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = ATH5K_RATE_CODE_6M, .flags = 0 }, { .bitrate = 90, .hw_value = ATH5K_RATE_CODE_9M, .flags = 0 }, { .bitrate = 120, .hw_value = ATH5K_RATE_CODE_12M, .flags = 0 }, { .bitrate = 180, .hw_value = ATH5K_RATE_CODE_18M, .flags = 0 }, { .bitrate = 240, .hw_value = ATH5K_RATE_CODE_24M, .flags = 0 }, { .bitrate = 360, .hw_value = ATH5K_RATE_CODE_36M, .flags = 0 }, { .bitrate = 480, .hw_value = ATH5K_RATE_CODE_48M, .flags = 0 }, { .bitrate = 540, .hw_value = ATH5K_RATE_CODE_54M, .flags = 0 }, }; static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) { u64 tsf = ath5k_hw_get_tsf64(ah); if ((tsf & 0x7fff) < rstamp) tsf -= 0x8000; return (tsf & ~0x7fff) | rstamp; } const char * ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val) { const char *name = "xxxxx"; unsigned int i; for (i = 0; i < ARRAY_SIZE(srev_names); i++) { if (srev_names[i].sr_type != type) continue; if ((val & 0xf0) == srev_names[i].sr_val) name = srev_names[i].sr_name; if ((val & 0xff) == srev_names[i].sr_val) { name = srev_names[i].sr_name; break; } } return name; } static unsigned int ath5k_ioread32(void *hw_priv, u32 reg_offset) { struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv; return ath5k_hw_reg_read(ah, reg_offset); } static void ath5k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) { struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv; ath5k_hw_reg_write(ah, val, reg_offset); } static const struct ath_ops ath5k_common_ops = { .read = ath5k_ioread32, .write = ath5k_iowrite32, }; /***********************\ * Driver Initialization * \***********************/ static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath5k_hw *ah = hw->priv; struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); return ath_reg_notifier_apply(wiphy, request, regulatory); } /********************\ * Channel/mode setup * \********************/ /* * Returns true for the channel numbers used. */ #ifdef CONFIG_ATH5K_TEST_CHANNELS static bool ath5k_is_standard_channel(short chan, enum ieee80211_band band) { return true; } #else static bool ath5k_is_standard_channel(short chan, enum ieee80211_band band) { if (band == IEEE80211_BAND_2GHZ && chan <= 14) return true; return /* UNII 1,2 */ (((chan & 3) == 0 && chan >= 36 && chan <= 64) || /* midband */ ((chan & 3) == 0 && chan >= 100 && chan <= 140) || /* UNII-3 */ ((chan & 3) == 1 && chan >= 149 && chan <= 165) || /* 802.11j 5.030-5.080 GHz (20MHz) */ (chan == 8 || chan == 12 || chan == 16) || /* 802.11j 4.9GHz (20MHz) */ (chan == 184 || chan == 188 || chan == 192 || chan == 196)); } #endif static unsigned int ath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels, unsigned int mode, unsigned int max) { unsigned int count, size, freq, ch; enum ieee80211_band band; switch (mode) { case AR5K_MODE_11A: /* 1..220, but 2GHz frequencies are filtered by check_channel */ size = 220; band = IEEE80211_BAND_5GHZ; break; case AR5K_MODE_11B: case AR5K_MODE_11G: size = 26; band = IEEE80211_BAND_2GHZ; break; default: ATH5K_WARN(ah, "bad mode, not copying channels\n"); return 0; } count = 0; for (ch = 1; ch <= size && count < max; ch++) { freq = ieee80211_channel_to_frequency(ch, band); if (freq == 0) /* mapping failed - not a standard channel */ continue; /* Write channel info, needed for ath5k_channel_ok() */ channels[count].center_freq = freq; channels[count].band = band; channels[count].hw_value = mode; /* Check if channel is supported by the chipset */ if (!ath5k_channel_ok(ah, &channels[count])) continue; if (!ath5k_is_standard_channel(ch, band)) continue; count++; } return count; } static void ath5k_setup_rate_idx(struct ath5k_hw *ah, struct ieee80211_supported_band *b) { u8 i; for (i = 0; i < AR5K_MAX_RATES; i++) ah->rate_idx[b->band][i] = -1; for (i = 0; i < b->n_bitrates; i++) { ah->rate_idx[b->band][b->bitrates[i].hw_value] = i; if (b->bitrates[i].hw_value_short) ah->rate_idx[b->band][b->bitrates[i].hw_value_short] = i; } } static int ath5k_setup_bands(struct ieee80211_hw *hw) { struct ath5k_hw *ah = hw->priv; struct ieee80211_supported_band *sband; int max_c, count_c = 0; int i; BUILD_BUG_ON(ARRAY_SIZE(ah->sbands) < IEEE80211_NUM_BANDS); max_c = ARRAY_SIZE(ah->channels); /* 2GHz band */ sband = &ah->sbands[IEEE80211_BAND_2GHZ]; sband->band = IEEE80211_BAND_2GHZ; sband->bitrates = &ah->rates[IEEE80211_BAND_2GHZ][0]; if (test_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode)) { /* G mode */ memcpy(sband->bitrates, &ath5k_rates[0], sizeof(struct ieee80211_rate) * 12); sband->n_bitrates = 12; sband->channels = ah->channels; sband->n_channels = ath5k_setup_channels(ah, sband->channels, AR5K_MODE_11G, max_c); hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; count_c = sband->n_channels; max_c -= count_c; } else if (test_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode)) { /* B mode */ memcpy(sband->bitrates, &ath5k_rates[0], sizeof(struct ieee80211_rate) * 4); sband->n_bitrates = 4; /* 5211 only supports B rates and uses 4bit rate codes * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B) * fix them up here: */ if (ah->ah_version == AR5K_AR5211) { for (i = 0; i < 4; i++) { sband->bitrates[i].hw_value = sband->bitrates[i].hw_value & 0xF; sband->bitrates[i].hw_value_short = sband->bitrates[i].hw_value_short & 0xF; } } sband->channels = ah->channels; sband->n_channels = ath5k_setup_channels(ah, sband->channels, AR5K_MODE_11B, max_c); hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; count_c = sband->n_channels; max_c -= count_c; } ath5k_setup_rate_idx(ah, sband); /* 5GHz band, A mode */ if (test_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode)) { sband = &ah->sbands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; sband->bitrates = &ah->rates[IEEE80211_BAND_5GHZ][0]; memcpy(sband->bitrates, &ath5k_rates[4], sizeof(struct ieee80211_rate) * 8); sband->n_bitrates = 8; sband->channels = &ah->channels[count_c]; sband->n_channels = ath5k_setup_channels(ah, sband->channels, AR5K_MODE_11A, max_c); hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; } ath5k_setup_rate_idx(ah, sband); ath5k_debug_dump_bands(ah); return 0; } /* * Set/change channels. We always reset the chip. * To accomplish this we must first cleanup any pending DMA, * then restart stuff after a la ath5k_init. * * Called with ah->lock. */ int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "channel set, resetting (%u -> %u MHz)\n", ah->curchan->center_freq, chan->center_freq); /* * To switch channels clear any pending DMA operations; * wait long enough for the RX fifo to drain, reset the * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ return ath5k_reset(ah, chan, true); } void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath5k_vif_iter_data *iter_data = data; int i; struct ath5k_vif *avf = (void *)vif->drv_priv; if (iter_data->hw_macaddr) for (i = 0; i < ETH_ALEN; i++) iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); if (!iter_data->found_active) { iter_data->found_active = true; memcpy(iter_data->active_mac, mac, ETH_ALEN); } if (iter_data->need_set_hw_addr && iter_data->hw_macaddr) if (ether_addr_equal(iter_data->hw_macaddr, mac)) iter_data->need_set_hw_addr = false; if (!iter_data->any_assoc) { if (avf->assoc) iter_data->any_assoc = true; } /* Calculate combined mode - when APs are active, operate in AP mode. * Otherwise use the mode of the new interface. This can currently * only deal with combinations of APs and STAs. Only one ad-hoc * interfaces is allowed. */ if (avf->opmode == NL80211_IFTYPE_AP) iter_data->opmode = NL80211_IFTYPE_AP; else { if (avf->opmode == NL80211_IFTYPE_STATION) iter_data->n_stas++; if (iter_data->opmode == NL80211_IFTYPE_UNSPECIFIED) iter_data->opmode = avf->opmode; } } void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah, struct ieee80211_vif *vif) { struct ath_common *common = ath5k_hw_common(ah); struct ath5k_vif_iter_data iter_data; u32 rfilt; /* * Use the hardware MAC address as reference, the hardware uses it * together with the BSSID mask when matching addresses. */ iter_data.hw_macaddr = common->macaddr; memset(&iter_data.mask, 0xff, ETH_ALEN); iter_data.found_active = false; iter_data.need_set_hw_addr = true; iter_data.opmode = NL80211_IFTYPE_UNSPECIFIED; iter_data.n_stas = 0; if (vif) ath5k_vif_iter(&iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, &iter_data); memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN); ah->opmode = iter_data.opmode; if (ah->opmode == NL80211_IFTYPE_UNSPECIFIED) /* Nothing active, default to station mode */ ah->opmode = NL80211_IFTYPE_STATION; ath5k_hw_set_opmode(ah, ah->opmode); ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n", ah->opmode, ath_opmode_to_string(ah->opmode)); if (iter_data.need_set_hw_addr && iter_data.found_active) ath5k_hw_set_lladdr(ah, iter_data.active_mac); if (ath5k_hw_hasbssidmask(ah)) ath5k_hw_set_bssid_mask(ah, ah->bssidmask); /* Set up RX Filter */ if (iter_data.n_stas > 1) { /* If you have multiple STA interfaces connected to * different APs, ARPs are not received (most of the time?) * Enabling PROMISC appears to fix that problem. */ ah->filter_flags |= AR5K_RX_FILTER_PROM; } rfilt = ah->filter_flags; ath5k_hw_set_rx_filter(ah, rfilt); ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); } static inline int ath5k_hw_to_driver_rix(struct ath5k_hw *ah, int hw_rix) { int rix; /* return base rate on errors */ if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES, "hw_rix out of bounds: %x\n", hw_rix)) return 0; rix = ah->rate_idx[ah->curchan->band][hw_rix]; if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix)) rix = 0; return rix; } /***************\ * Buffers setup * \***************/ static struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_hw *ah, dma_addr_t *skb_addr) { struct ath_common *common = ath5k_hw_common(ah); struct sk_buff *skb; /* * Allocate buffer with headroom_needed space for the * fake physical layer header at the start. */ skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); if (!skb) { ATH5K_ERR(ah, "can't alloc skbuff of size %u\n", common->rx_bufsize); return NULL; } *skb_addr = dma_map_single(ah->dev, skb->data, common->rx_bufsize, DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(ah->dev, *skb_addr))) { ATH5K_ERR(ah, "%s: DMA mapping failed\n", __func__); dev_kfree_skb(skb); return NULL; } return skb; } static int ath5k_rxbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf) { struct sk_buff *skb = bf->skb; struct ath5k_desc *ds; int ret; if (!skb) { skb = ath5k_rx_skb_alloc(ah, &bf->skbaddr); if (!skb) return -ENOMEM; bf->skb = skb; } /* * Setup descriptors. For receive we always terminate * the descriptor list with a self-linked entry so we'll * not get overrun under high load (as can happen with a * 5212 when ANI processing enables PHY error frames). * * To ensure the last descriptor is self-linked we create * each descriptor as self-linked and add it to the end. As * each additional descriptor is added the previous self-linked * entry is "fixed" naturally. This should be safe even * if DMA is happening. When processing RX interrupts we * never remove/process the last, self-linked, entry on the * descriptor list. This ensures the hardware always has * someplace to write a new frame. */ ds = bf->desc; ds->ds_link = bf->daddr; /* link to self */ ds->ds_data = bf->skbaddr; ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0); if (ret) { ATH5K_ERR(ah, "%s: could not setup RX desc\n", __func__); return ret; } if (ah->rxlink != NULL) *ah->rxlink = bf->daddr; ah->rxlink = &ds->ds_link; return 0; } static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) { struct ieee80211_hdr *hdr; enum ath5k_pkt_type htype; __le16 fc; hdr = (struct ieee80211_hdr *)skb->data; fc = hdr->frame_control; if (ieee80211_is_beacon(fc)) htype = AR5K_PKT_TYPE_BEACON; else if (ieee80211_is_probe_resp(fc)) htype = AR5K_PKT_TYPE_PROBE_RESP; else if (ieee80211_is_atim(fc)) htype = AR5K_PKT_TYPE_ATIM; else if (ieee80211_is_pspoll(fc)) htype = AR5K_PKT_TYPE_PSPOLL; else htype = AR5K_PKT_TYPE_NORMAL; return htype; } static int ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, struct ath5k_txq *txq, int padsize) { struct ath5k_desc *ds = bf->desc; struct sk_buff *skb = bf->skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; struct ieee80211_rate *rate; unsigned int mrr_rate[3], mrr_tries[3]; int i, ret; u16 hw_rate; u16 cts_rate = 0; u16 duration = 0; u8 rc_flags; flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; /* XXX endianness */ bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, DMA_TO_DEVICE); rate = ieee80211_get_tx_rate(ah->hw, info); if (!rate) { ret = -EINVAL; goto err_unmap; } if (info->flags & IEEE80211_TX_CTL_NO_ACK) flags |= AR5K_TXDESC_NOACK; rc_flags = info->control.rates[0].flags; hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? rate->hw_value_short : rate->hw_value; pktlen = skb->len; /* FIXME: If we are in g mode and rate is a CCK rate * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta * from tx power (value is in dB units already) */ if (info->control.hw_key) { keyidx = info->control.hw_key->hw_key_idx; pktlen += info->control.hw_key->icv_len; } if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { flags |= AR5K_TXDESC_RTSENA; cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value; duration = le16_to_cpu(ieee80211_rts_duration(ah->hw, info->control.vif, pktlen, info)); } if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { flags |= AR5K_TXDESC_CTSENA; cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value; duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw, info->control.vif, pktlen, info)); } ret = ah->ah_setup_tx_desc(ah, ds, pktlen, ieee80211_get_hdrlen_from_skb(skb), padsize, get_hw_packet_type(skb), (ah->ah_txpower.txp_requested * 2), hw_rate, info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, cts_rate, duration); if (ret) goto err_unmap; /* Set up MRR descriptor */ if (ah->ah_capabilities.cap_has_mrr_support) { memset(mrr_rate, 0, sizeof(mrr_rate)); memset(mrr_tries, 0, sizeof(mrr_tries)); for (i = 0; i < 3; i++) { rate = ieee80211_get_alt_retry_rate(ah->hw, info, i); if (!rate) break; mrr_rate[i] = rate->hw_value; mrr_tries[i] = info->control.rates[i + 1].count; } ath5k_hw_setup_mrr_tx_desc(ah, ds, mrr_rate[0], mrr_tries[0], mrr_rate[1], mrr_tries[1], mrr_rate[2], mrr_tries[2]); } ds->ds_link = 0; ds->ds_data = bf->skbaddr; spin_lock_bh(&txq->lock); list_add_tail(&bf->list, &txq->q); txq->txq_len++; if (txq->link == NULL) /* is this first packet? */ ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); else /* no, so only link it */ *txq->link = bf->daddr; txq->link = &ds->ds_link; ath5k_hw_start_tx_dma(ah, txq->qnum); mmiowb(); spin_unlock_bh(&txq->lock); return 0; err_unmap: dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE); return ret; } /*******************\ * Descriptors setup * \*******************/ static int ath5k_desc_alloc(struct ath5k_hw *ah) { struct ath5k_desc *ds; struct ath5k_buf *bf; dma_addr_t da; unsigned int i; int ret; /* allocate descriptors */ ah->desc_len = sizeof(struct ath5k_desc) * (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1); ah->desc = dma_alloc_coherent(ah->dev, ah->desc_len, &ah->desc_daddr, GFP_KERNEL); if (ah->desc == NULL) { ATH5K_ERR(ah, "can't allocate descriptors\n"); ret = -ENOMEM; goto err; } ds = ah->desc; da = ah->desc_daddr; ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n", ds, ah->desc_len, (unsigned long long)ah->desc_daddr); bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF, sizeof(struct ath5k_buf), GFP_KERNEL); if (bf == NULL) { ATH5K_ERR(ah, "can't allocate bufptr\n"); ret = -ENOMEM; goto err_free; } ah->bufptr = bf; INIT_LIST_HEAD(&ah->rxbuf); for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { bf->desc = ds; bf->daddr = da; list_add_tail(&bf->list, &ah->rxbuf); } INIT_LIST_HEAD(&ah->txbuf); ah->txbuf_len = ATH_TXBUF; for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, da += sizeof(*ds)) { bf->desc = ds; bf->daddr = da; list_add_tail(&bf->list, &ah->txbuf); } /* beacon buffers */ INIT_LIST_HEAD(&ah->bcbuf); for (i = 0; i < ATH_BCBUF; i++, bf++, ds++, da += sizeof(*ds)) { bf->desc = ds; bf->daddr = da; list_add_tail(&bf->list, &ah->bcbuf); } return 0; err_free: dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr); err: ah->desc = NULL; return ret; } void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf) { BUG_ON(!bf); if (!bf->skb) return; dma_unmap_single(ah->dev, bf->skbaddr, bf->skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(bf->skb); bf->skb = NULL; bf->skbaddr = 0; bf->desc->ds_data = 0; } void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf) { struct ath_common *common = ath5k_hw_common(ah); BUG_ON(!bf); if (!bf->skb) return; dma_unmap_single(ah->dev, bf->skbaddr, common->rx_bufsize, DMA_FROM_DEVICE); dev_kfree_skb_any(bf->skb); bf->skb = NULL; bf->skbaddr = 0; bf->desc->ds_data = 0; } static void ath5k_desc_free(struct ath5k_hw *ah) { struct ath5k_buf *bf; list_for_each_entry(bf, &ah->txbuf, list) ath5k_txbuf_free_skb(ah, bf); list_for_each_entry(bf, &ah->rxbuf, list) ath5k_rxbuf_free_skb(ah, bf); list_for_each_entry(bf, &ah->bcbuf, list) ath5k_txbuf_free_skb(ah, bf); /* Free memory associated with all descriptors */ dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr); ah->desc = NULL; ah->desc_daddr = 0; kfree(ah->bufptr); ah->bufptr = NULL; } /**************\ * Queues setup * \**************/ static struct ath5k_txq * ath5k_txq_setup(struct ath5k_hw *ah, int qtype, int subtype) { struct ath5k_txq *txq; struct ath5k_txq_info qi = { .tqi_subtype = subtype, /* XXX: default values not correct for B and XR channels, * but who cares? */ .tqi_aifs = AR5K_TUNE_AIFS, .tqi_cw_min = AR5K_TUNE_CWMIN, .tqi_cw_max = AR5K_TUNE_CWMAX }; int qnum; /* * Enable interrupts only for EOL and DESC conditions. * We mark tx descriptors to receive a DESC interrupt * when a tx queue gets deep; otherwise we wait for the * EOL to reap descriptors. Note that this is done to * reduce interrupt load and this only defers reaping * descriptors, never transmitting frames. Aside from * reducing interrupts this also permits more concurrency. * The only potential downside is if the tx queue backs * up in which case the top half of the kernel may backup * due to a lack of tx descriptors. */ qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | AR5K_TXQ_FLAG_TXDESCINT_ENABLE; qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); if (qnum < 0) { /* * NB: don't print a message, this happens * normally on parts with too few tx queues */ return ERR_PTR(qnum); } txq = &ah->txqs[qnum]; if (!txq->setup) { txq->qnum = qnum; txq->link = NULL; INIT_LIST_HEAD(&txq->q); spin_lock_init(&txq->lock); txq->setup = true; txq->txq_len = 0; txq->txq_max = ATH5K_TXQ_LEN_MAX; txq->txq_poll_mark = false; txq->txq_stuck = 0; } return &ah->txqs[qnum]; } static int ath5k_beaconq_setup(struct ath5k_hw *ah) { struct ath5k_txq_info qi = { /* XXX: default values not correct for B and XR channels, * but who cares? */ .tqi_aifs = AR5K_TUNE_AIFS, .tqi_cw_min = AR5K_TUNE_CWMIN, .tqi_cw_max = AR5K_TUNE_CWMAX, /* NB: for dynamic turbo, don't enable any other interrupts */ .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE }; return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi); } static int ath5k_beaconq_config(struct ath5k_hw *ah) { struct ath5k_txq_info qi; int ret; ret = ath5k_hw_get_tx_queueprops(ah, ah->bhalq, &qi); if (ret) goto err; if (ah->opmode == NL80211_IFTYPE_AP || ah->opmode == NL80211_IFTYPE_MESH_POINT) { /* * Always burst out beacon and CAB traffic * (aifs = cwmin = cwmax = 0) */ qi.tqi_aifs = 0; qi.tqi_cw_min = 0; qi.tqi_cw_max = 0; } else if (ah->opmode == NL80211_IFTYPE_ADHOC) { /* * Adhoc mode; backoff between 0 and (2 * cw_min). */ qi.tqi_aifs = 0; qi.tqi_cw_min = 0; qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN; } ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n", qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max); ret = ath5k_hw_set_tx_queueprops(ah, ah->bhalq, &qi); if (ret) { ATH5K_ERR(ah, "%s: unable to update parameters for beacon " "hardware queue!\n", __func__); goto err; } ret = ath5k_hw_reset_tx_queue(ah, ah->bhalq); /* push to h/w */ if (ret) goto err; /* reconfigure cabq with ready time to 80% of beacon_interval */ ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); if (ret) goto err; qi.tqi_ready_time = (ah->bintval * 80) / 100; ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); if (ret) goto err; ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB); err: return ret; } /** * ath5k_drain_tx_buffs - Empty tx buffers * * @ah The &struct ath5k_hw * * Empty tx buffers from all queues in preparation * of a reset or during shutdown. * * NB: this assumes output has been stopped and * we do not need to block ath5k_tx_tasklet */ static void ath5k_drain_tx_buffs(struct ath5k_hw *ah) { struct ath5k_txq *txq; struct ath5k_buf *bf, *bf0; int i; for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) { if (ah->txqs[i].setup) { txq = &ah->txqs[i]; spin_lock_bh(&txq->lock); list_for_each_entry_safe(bf, bf0, &txq->q, list) { ath5k_debug_printtxbuf(ah, bf); ath5k_txbuf_free_skb(ah, bf); spin_lock(&ah->txbuflock); list_move_tail(&bf->list, &ah->txbuf); ah->txbuf_len++; txq->txq_len--; spin_unlock(&ah->txbuflock); } txq->link = NULL; txq->txq_poll_mark = false; spin_unlock_bh(&txq->lock); } } } static void ath5k_txq_release(struct ath5k_hw *ah) { struct ath5k_txq *txq = ah->txqs; unsigned int i; for (i = 0; i < ARRAY_SIZE(ah->txqs); i++, txq++) if (txq->setup) { ath5k_hw_release_tx_queue(ah, txq->qnum); txq->setup = false; } } /*************\ * RX Handling * \*************/ /* * Enable the receive h/w following a reset. */ static int ath5k_rx_start(struct ath5k_hw *ah) { struct ath_common *common = ath5k_hw_common(ah); struct ath5k_buf *bf; int ret; common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz); ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n", common->cachelsz, common->rx_bufsize); spin_lock_bh(&ah->rxbuflock); ah->rxlink = NULL; list_for_each_entry(bf, &ah->rxbuf, list) { ret = ath5k_rxbuf_setup(ah, bf); if (ret != 0) { spin_unlock_bh(&ah->rxbuflock); goto err; } } bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list); ath5k_hw_set_rxdp(ah, bf->daddr); spin_unlock_bh(&ah->rxbuflock); ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */ ath5k_update_bssid_mask_and_opmode(ah, NULL); /* set filters, etc. */ ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ return 0; err: return ret; } /* * Disable the receive logic on PCU (DRU) * In preparation for a shutdown. * * Note: Doesn't stop rx DMA, ath5k_hw_dma_stop * does. */ static void ath5k_rx_stop(struct ath5k_hw *ah) { ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ ath5k_hw_stop_rx_pcu(ah); /* disable PCU */ ath5k_debug_printrxbuffs(ah); } static unsigned int ath5k_rx_decrypted(struct ath5k_hw *ah, struct sk_buff *skb, struct ath5k_rx_status *rs) { struct ath_common *common = ath5k_hw_common(ah); struct ieee80211_hdr *hdr = (void *)skb->data; unsigned int keyix, hlen; if (!(rs->rs_status & AR5K_RXERR_DECRYPT) && rs->rs_keyix != AR5K_RXKEYIX_INVALID) return RX_FLAG_DECRYPTED; /* Apparently when a default key is used to decrypt the packet the hw does not set the index used to decrypt. In such cases get the index from the packet. */ hlen = ieee80211_hdrlen(hdr->frame_control); if (ieee80211_has_protected(hdr->frame_control) && !(rs->rs_status & AR5K_RXERR_DECRYPT) && skb->len >= hlen + 4) { keyix = skb->data[hlen + 3] >> 6; if (test_bit(keyix, common->keymap)) return RX_FLAG_DECRYPTED; } return 0; } static void ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb, struct ieee80211_rx_status *rxs) { struct ath_common *common = ath5k_hw_common(ah); u64 tsf, bc_tstamp; u32 hw_tu; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; if (ieee80211_is_beacon(mgmt->frame_control) && le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && ether_addr_equal(mgmt->bssid, common->curbssid)) { /* * Received an IBSS beacon with the same BSSID. Hardware *must* * have updated the local TSF. We have to work around various * hardware bugs, though... */ tsf = ath5k_hw_get_tsf64(ah); bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp); hw_tu = TSF_TO_TU(tsf); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "beacon %llx mactime %llx (diff %lld) tsf now %llx\n", (unsigned long long)bc_tstamp, (unsigned long long)rxs->mactime, (unsigned long long)(rxs->mactime - bc_tstamp), (unsigned long long)tsf); /* * Sometimes the HW will give us a wrong tstamp in the rx * status, causing the timestamp extension to go wrong. * (This seems to happen especially with beacon frames bigger * than 78 byte (incl. FCS)) * But we know that the receive timestamp must be later than the * timestamp of the beacon since HW must have synced to that. * * NOTE: here we assume mactime to be after the frame was * received, not like mac80211 which defines it at the start. */ if (bc_tstamp > rxs->mactime) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "fixing mactime from %llx to %llx\n", (unsigned long long)rxs->mactime, (unsigned long long)tsf); rxs->mactime = tsf; } /* * Local TSF might have moved higher than our beacon timers, * in that case we have to update them to continue sending * beacons. This also takes care of synchronizing beacon sending * times with other stations. */ if (hw_tu >= ah->nexttbtt) ath5k_beacon_update_timers(ah, bc_tstamp); /* Check if the beacon timers are still correct, because a TSF * update might have created a window between them - for a * longer description see the comment of this function: */ if (!ath5k_hw_check_beacon_timers(ah, ah->bintval)) { ath5k_beacon_update_timers(ah, bc_tstamp); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "fixed beacon timers after beacon receive\n"); } } } static void ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; struct ath_common *common = ath5k_hw_common(ah); /* only beacons from our BSSID */ if (!ieee80211_is_beacon(mgmt->frame_control) || !ether_addr_equal(mgmt->bssid, common->curbssid)) return; ewma_add(&ah->ah_beacon_rssi_avg, rssi); /* in IBSS mode we should keep RSSI statistics per neighbour */ /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */ } /* * Compute padding position. skb must contain an IEEE 802.11 frame */ static int ath5k_common_padpos(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 frame_control = hdr->frame_control; int padpos = 24; if (ieee80211_has_a4(frame_control)) padpos += ETH_ALEN; if (ieee80211_is_data_qos(frame_control)) padpos += IEEE80211_QOS_CTL_LEN; return padpos; } /* * This function expects an 802.11 frame and returns the number of * bytes added, or -1 if we don't have enough header room. */ static int ath5k_add_padding(struct sk_buff *skb) { int padpos = ath5k_common_padpos(skb); int padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) return -1; skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); return padsize; } return 0; } /* * The MAC header is padded to have 32-bit boundary if the * packet payload is non-zero. The general calculation for * padsize would take into account odd header lengths: * padsize = 4 - (hdrlen & 3); however, since only * even-length headers are used, padding can only be 0 or 2 * bytes and we can optimize this a bit. We must not try to * remove padding from short control frames that do not have a * payload. * * This function expects an 802.11 frame and returns the number of * bytes removed. */ static int ath5k_remove_padding(struct sk_buff *skb) { int padpos = ath5k_common_padpos(skb); int padsize = padpos & 3; if (padsize && skb->len >= padpos + padsize) { memmove(skb->data + padsize, skb->data, padpos); skb_pull(skb, padsize); return padsize; } return 0; } static void ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, struct ath5k_rx_status *rs) { struct ieee80211_rx_status *rxs; ath5k_remove_padding(skb); rxs = IEEE80211_SKB_RXCB(skb); rxs->flag = 0; if (unlikely(rs->rs_status & AR5K_RXERR_MIC)) rxs->flag |= RX_FLAG_MMIC_ERROR; /* * always extend the mac timestamp, since this information is * also needed for proper IBSS merging. * * XXX: it might be too late to do it here, since rs_tstamp is * 15bit only. that means TSF extension has to be done within * 32768usec (about 32ms). it might be necessary to move this to * the interrupt handler, like it is done in madwifi. * * Unfortunately we don't know when the hardware takes the rx * timestamp (beginning of phy frame, data frame, end of rx?). * The only thing we know is that it is hardware specific... * On AR5213 it seems the rx timestamp is at the end of the * frame, but I'm not sure. * * NOTE: mac80211 defines mactime at the beginning of the first * data symbol. Since we don't have any time references it's * impossible to comply to that. This affects IBSS merge only * right now, so it's not too bad... */ rxs->mactime = ath5k_extend_tsf(ah, rs->rs_tstamp); rxs->flag |= RX_FLAG_MACTIME_MPDU; rxs->freq = ah->curchan->center_freq; rxs->band = ah->curchan->band; rxs->signal = ah->ah_noise_floor + rs->rs_rssi; rxs->antenna = rs->rs_antenna; if (rs->rs_antenna > 0 && rs->rs_antenna < 5) ah->stats.antenna_rx[rs->rs_antenna]++; else ah->stats.antenna_rx[0]++; /* invalid */ rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate); rxs->flag |= ath5k_rx_decrypted(ah, skb, rs); if (rxs->rate_idx >= 0 && rs->rs_rate == ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short) rxs->flag |= RX_FLAG_SHORTPRE; trace_ath5k_rx(ah, skb); ath5k_update_beacon_rssi(ah, skb, rs->rs_rssi); /* check beacons in IBSS mode */ if (ah->opmode == NL80211_IFTYPE_ADHOC) ath5k_check_ibss_tsf(ah, skb, rxs); ieee80211_rx(ah->hw, skb); } /** ath5k_frame_receive_ok() - Do we want to receive this frame or not? * * Check if we want to further process this frame or not. Also update * statistics. Return true if we want this frame, false if not. */ static bool ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs) { ah->stats.rx_all_count++; ah->stats.rx_bytes_count += rs->rs_datalen; if (unlikely(rs->rs_status)) { if (rs->rs_status & AR5K_RXERR_CRC) ah->stats.rxerr_crc++; if (rs->rs_status & AR5K_RXERR_FIFO) ah->stats.rxerr_fifo++; if (rs->rs_status & AR5K_RXERR_PHY) { ah->stats.rxerr_phy++; if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32) ah->stats.rxerr_phy_code[rs->rs_phyerr]++; return false; } if (rs->rs_status & AR5K_RXERR_DECRYPT) { /* * Decrypt error. If the error occurred * because there was no hardware key, then * let the frame through so the upper layers * can process it. This is necessary for 5210 * parts which have no way to setup a ``clear'' * key cache entry. * * XXX do key cache faulting */ ah->stats.rxerr_decrypt++; if (rs->rs_keyix == AR5K_RXKEYIX_INVALID && !(rs->rs_status & AR5K_RXERR_CRC)) return true; } if (rs->rs_status & AR5K_RXERR_MIC) { ah->stats.rxerr_mic++; return true; } /* reject any frames with non-crypto errors */ if (rs->rs_status & ~(AR5K_RXERR_DECRYPT)) return false; } if (unlikely(rs->rs_more)) { ah->stats.rxerr_jumbo++; return false; } return true; } static void ath5k_set_current_imask(struct ath5k_hw *ah) { enum ath5k_int imask; unsigned long flags; spin_lock_irqsave(&ah->irqlock, flags); imask = ah->imask; if (ah->rx_pending) imask &= ~AR5K_INT_RX_ALL; if (ah->tx_pending) imask &= ~AR5K_INT_TX_ALL; ath5k_hw_set_imr(ah, imask); spin_unlock_irqrestore(&ah->irqlock, flags); } static void ath5k_tasklet_rx(unsigned long data) { struct ath5k_rx_status rs = {}; struct sk_buff *skb, *next_skb; dma_addr_t next_skb_addr; struct ath5k_hw *ah = (void *)data; struct ath_common *common = ath5k_hw_common(ah); struct ath5k_buf *bf; struct ath5k_desc *ds; int ret; spin_lock(&ah->rxbuflock); if (list_empty(&ah->rxbuf)) { ATH5K_WARN(ah, "empty rx buf pool\n"); goto unlock; } do { bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list); BUG_ON(bf->skb == NULL); skb = bf->skb; ds = bf->desc; /* bail if HW is still using self-linked descriptor */ if (ath5k_hw_get_rxdp(ah) == bf->daddr) break; ret = ah->ah_proc_rx_desc(ah, ds, &rs); if (unlikely(ret == -EINPROGRESS)) break; else if (unlikely(ret)) { ATH5K_ERR(ah, "error in processing rx descriptor\n"); ah->stats.rxerr_proc++; break; } if (ath5k_receive_frame_ok(ah, &rs)) { next_skb = ath5k_rx_skb_alloc(ah, &next_skb_addr); /* * If we can't replace bf->skb with a new skb under * memory pressure, just skip this packet */ if (!next_skb) goto next; dma_unmap_single(ah->dev, bf->skbaddr, common->rx_bufsize, DMA_FROM_DEVICE); skb_put(skb, rs.rs_datalen); ath5k_receive_frame(ah, skb, &rs); bf->skb = next_skb; bf->skbaddr = next_skb_addr; } next: list_move_tail(&bf->list, &ah->rxbuf); } while (ath5k_rxbuf_setup(ah, bf) == 0); unlock: spin_unlock(&ah->rxbuflock); ah->rx_pending = false; ath5k_set_current_imask(ah); } /*************\ * TX Handling * \*************/ void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath5k_txq *txq) { struct ath5k_hw *ah = hw->priv; struct ath5k_buf *bf; unsigned long flags; int padsize; trace_ath5k_tx(ah, skb, txq); /* * The hardware expects the header padded to 4 byte boundaries. * If this is not the case, we add the padding after the header. */ padsize = ath5k_add_padding(skb); if (padsize < 0) { ATH5K_ERR(ah, "tx hdrlen not %%4: not enough" " headroom to pad"); goto drop_packet; } if (txq->txq_len >= txq->txq_max && txq->qnum <= AR5K_TX_QUEUE_ID_DATA_MAX) ieee80211_stop_queue(hw, txq->qnum); spin_lock_irqsave(&ah->txbuflock, flags); if (list_empty(&ah->txbuf)) { ATH5K_ERR(ah, "no further txbuf available, dropping packet\n"); spin_unlock_irqrestore(&ah->txbuflock, flags); ieee80211_stop_queues(hw); goto drop_packet; } bf = list_first_entry(&ah->txbuf, struct ath5k_buf, list); list_del(&bf->list); ah->txbuf_len--; if (list_empty(&ah->txbuf)) ieee80211_stop_queues(hw); spin_unlock_irqrestore(&ah->txbuflock, flags); bf->skb = skb; if (ath5k_txbuf_setup(ah, bf, txq, padsize)) { bf->skb = NULL; spin_lock_irqsave(&ah->txbuflock, flags); list_add_tail(&bf->list, &ah->txbuf); ah->txbuf_len++; spin_unlock_irqrestore(&ah->txbuflock, flags); goto drop_packet; } return; drop_packet: dev_kfree_skb_any(skb); } static void ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, struct ath5k_txq *txq, struct ath5k_tx_status *ts) { struct ieee80211_tx_info *info; u8 tries[3]; int i; ah->stats.tx_all_count++; ah->stats.tx_bytes_count += skb->len; info = IEEE80211_SKB_CB(skb); tries[0] = info->status.rates[0].count; tries[1] = info->status.rates[1].count; tries[2] = info->status.rates[2].count; ieee80211_tx_info_clear_status(info); for (i = 0; i < ts->ts_final_idx; i++) { struct ieee80211_tx_rate *r = &info->status.rates[i]; r->count = tries[i]; } info->status.rates[ts->ts_final_idx].count = ts->ts_final_retry; info->status.rates[ts->ts_final_idx + 1].idx = -1; if (unlikely(ts->ts_status)) { ah->stats.ack_fail++; if (ts->ts_status & AR5K_TXERR_FILT) { info->flags |= IEEE80211_TX_STAT_TX_FILTERED; ah->stats.txerr_filt++; } if (ts->ts_status & AR5K_TXERR_XRETRY) ah->stats.txerr_retry++; if (ts->ts_status & AR5K_TXERR_FIFO) ah->stats.txerr_fifo++; } else { info->flags |= IEEE80211_TX_STAT_ACK; info->status.ack_signal = ts->ts_rssi; /* count the successful attempt as well */ info->status.rates[ts->ts_final_idx].count++; } /* * Remove MAC header padding before giving the frame * back to mac80211. */ ath5k_remove_padding(skb); if (ts->ts_antenna > 0 && ts->ts_antenna < 5) ah->stats.antenna_tx[ts->ts_antenna]++; else ah->stats.antenna_tx[0]++; /* invalid */ trace_ath5k_tx_complete(ah, skb, txq, ts); ieee80211_tx_status(ah->hw, skb); } static void ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq) { struct ath5k_tx_status ts = {}; struct ath5k_buf *bf, *bf0; struct ath5k_desc *ds; struct sk_buff *skb; int ret; spin_lock(&txq->lock); list_for_each_entry_safe(bf, bf0, &txq->q, list) { txq->txq_poll_mark = false; /* skb might already have been processed last time. */ if (bf->skb != NULL) { ds = bf->desc; ret = ah->ah_proc_tx_desc(ah, ds, &ts); if (unlikely(ret == -EINPROGRESS)) break; else if (unlikely(ret)) { ATH5K_ERR(ah, "error %d while processing " "queue %u\n", ret, txq->qnum); break; } skb = bf->skb; bf->skb = NULL; dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE); ath5k_tx_frame_completed(ah, skb, txq, &ts); } /* * It's possible that the hardware can say the buffer is * completed when it hasn't yet loaded the ds_link from * host memory and moved on. * Always keep the last descriptor to avoid HW races... */ if (ath5k_hw_get_txdp(ah, txq->qnum) != bf->daddr) { spin_lock(&ah->txbuflock); list_move_tail(&bf->list, &ah->txbuf); ah->txbuf_len++; txq->txq_len--; spin_unlock(&ah->txbuflock); } } spin_unlock(&txq->lock); if (txq->txq_len < ATH5K_TXQ_LEN_LOW && txq->qnum < 4) ieee80211_wake_queue(ah->hw, txq->qnum); } static void ath5k_tasklet_tx(unsigned long data) { int i; struct ath5k_hw *ah = (void *)data; for (i = 0; i < AR5K_NUM_TX_QUEUES; i++) if (ah->txqs[i].setup && (ah->ah_txq_isr_txok_all & BIT(i))) ath5k_tx_processq(ah, &ah->txqs[i]); ah->tx_pending = false; ath5k_set_current_imask(ah); } /*****************\ * Beacon handling * \*****************/ /* * Setup the beacon frame for transmit. */ static int ath5k_beacon_setup(struct ath5k_hw *ah, struct ath5k_buf *bf) { struct sk_buff *skb = bf->skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ath5k_desc *ds; int ret = 0; u8 antenna; u32 flags; const int padsize = 0; bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, DMA_TO_DEVICE); ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] " "skbaddr %llx\n", skb, skb->data, skb->len, (unsigned long long)bf->skbaddr); if (dma_mapping_error(ah->dev, bf->skbaddr)) { ATH5K_ERR(ah, "beacon DMA mapping failed\n"); dev_kfree_skb_any(skb); bf->skb = NULL; return -EIO; } ds = bf->desc; antenna = ah->ah_tx_ant; flags = AR5K_TXDESC_NOACK; if (ah->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) { ds->ds_link = bf->daddr; /* self-linked */ flags |= AR5K_TXDESC_VEOL; } else ds->ds_link = 0; /* * If we use multiple antennas on AP and use * the Sectored AP scenario, switch antenna every * 4 beacons to make sure everybody hears our AP. * When a client tries to associate, hw will keep * track of the tx antenna to be used for this client * automatically, based on ACKed packets. * * Note: AP still listens and transmits RTS on the * default antenna which is supposed to be an omni. * * Note2: On sectored scenarios it's possible to have * multiple antennas (1 omni -- the default -- and 14 * sectors), so if we choose to actually support this * mode, we need to allow the user to set how many antennas * we have and tweak the code below to send beacons * on all of them. */ if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP) antenna = ah->bsent & 4 ? 2 : 1; /* FIXME: If we are in g mode and rate is a CCK rate * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta * from tx power (value is in dB units already) */ ds->ds_data = bf->skbaddr; ret = ah->ah_setup_tx_desc(ah, ds, skb->len, ieee80211_get_hdrlen_from_skb(skb), padsize, AR5K_PKT_TYPE_BEACON, (ah->ah_txpower.txp_requested * 2), ieee80211_get_tx_rate(ah->hw, info)->hw_value, 1, AR5K_TXKEYIX_INVALID, antenna, flags, 0, 0); if (ret) goto err_unmap; return 0; err_unmap: dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE); return ret; } /* * Updates the beacon that is sent by ath5k_beacon_send. For adhoc, * this is called only once at config_bss time, for AP we do it every * SWBA interrupt so that the TIM will reflect buffered frames. * * Called with the beacon lock. */ int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { int ret; struct ath5k_hw *ah = hw->priv; struct ath5k_vif *avf = (void *)vif->drv_priv; struct sk_buff *skb; if (WARN_ON(!vif)) { ret = -EINVAL; goto out; } skb = ieee80211_beacon_get(hw, vif); if (!skb) { ret = -ENOMEM; goto out; } ath5k_txbuf_free_skb(ah, avf->bbuf); avf->bbuf->skb = skb; ret = ath5k_beacon_setup(ah, avf->bbuf); out: return ret; } /* * Transmit a beacon frame at SWBA. Dynamic updates to the * frame contents are done as needed and the slot time is * also adjusted based on current state. * * This is called from software irq context (beacontq tasklets) * or user context from ath5k_beacon_config. */ static void ath5k_beacon_send(struct ath5k_hw *ah) { struct ieee80211_vif *vif; struct ath5k_vif *avf; struct ath5k_buf *bf; struct sk_buff *skb; int err; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "in beacon_send\n"); /* * Check if the previous beacon has gone out. If * not, don't don't try to post another: skip this * period and wait for the next. Missed beacons * indicate a problem and should not occur. If we * miss too many consecutive beacons reset the device. */ if (unlikely(ath5k_hw_num_tx_pending(ah, ah->bhalq) != 0)) { ah->bmisscount++; ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "missed %u consecutive beacons\n", ah->bmisscount); if (ah->bmisscount > 10) { /* NB: 10 is a guess */ ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "stuck beacon time (%u missed)\n", ah->bmisscount); ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "stuck beacon, resetting\n"); ieee80211_queue_work(ah->hw, &ah->reset_work); } return; } if (unlikely(ah->bmisscount != 0)) { ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "resume beacon xmit after %u misses\n", ah->bmisscount); ah->bmisscount = 0; } if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs + ah->num_mesh_vifs > 1) || ah->opmode == NL80211_IFTYPE_MESH_POINT) { u64 tsf = ath5k_hw_get_tsf64(ah); u32 tsftu = TSF_TO_TU(tsf); int slot = ((tsftu % ah->bintval) * ATH_BCBUF) / ah->bintval; vif = ah->bslot[(slot + 1) % ATH_BCBUF]; ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "tsf %llx tsftu %x intval %u slot %u vif %p\n", (unsigned long long)tsf, tsftu, ah->bintval, slot, vif); } else /* only one interface */ vif = ah->bslot[0]; if (!vif) return; avf = (void *)vif->drv_priv; bf = avf->bbuf; /* * Stop any current dma and put the new frame on the queue. * This should never fail since we check above that no frames * are still pending on the queue. */ if (unlikely(ath5k_hw_stop_beacon_queue(ah, ah->bhalq))) { ATH5K_WARN(ah, "beacon queue %u didn't start/stop ?\n", ah->bhalq); /* NB: hw still stops DMA, so proceed */ } /* refresh the beacon for AP or MESH mode */ if (ah->opmode == NL80211_IFTYPE_AP || ah->opmode == NL80211_IFTYPE_MESH_POINT) { err = ath5k_beacon_update(ah->hw, vif); if (err) return; } if (unlikely(bf->skb == NULL || ah->opmode == NL80211_IFTYPE_STATION || ah->opmode == NL80211_IFTYPE_MONITOR)) { ATH5K_WARN(ah, "bf=%p bf_skb=%p\n", bf, bf->skb); return; } trace_ath5k_tx(ah, bf->skb, &ah->txqs[ah->bhalq]); ath5k_hw_set_txdp(ah, ah->bhalq, bf->daddr); ath5k_hw_start_tx_dma(ah, ah->bhalq); ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", ah->bhalq, (unsigned long long)bf->daddr, bf->desc); skb = ieee80211_get_buffered_bc(ah->hw, vif); while (skb) { ath5k_tx_queue(ah->hw, skb, ah->cabq); if (ah->cabq->txq_len >= ah->cabq->txq_max) break; skb = ieee80211_get_buffered_bc(ah->hw, vif); } ah->bsent++; } /** * ath5k_beacon_update_timers - update beacon timers * * @ah: struct ath5k_hw pointer we are operating on * @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a * beacon timer update based on the current HW TSF. * * Calculate the next target beacon transmit time (TBTT) based on the timestamp * of a received beacon or the current local hardware TSF and write it to the * beacon timer registers. * * This is called in a variety of situations, e.g. when a beacon is received, * when a TSF update has been detected, but also when an new IBSS is created or * when we otherwise know we have to update the timers, but we keep it in this * function to have it all together in one place. */ void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf) { u32 nexttbtt, intval, hw_tu, bc_tu; u64 hw_tsf; intval = ah->bintval & AR5K_BEACON_PERIOD; if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs + ah->num_mesh_vifs > 1) { intval /= ATH_BCBUF; /* staggered multi-bss beacons */ if (intval < 15) ATH5K_WARN(ah, "intval %u is too low, min 15\n", intval); } if (WARN_ON(!intval)) return; /* beacon TSF converted to TU */ bc_tu = TSF_TO_TU(bc_tsf); /* current TSF converted to TU */ hw_tsf = ath5k_hw_get_tsf64(ah); hw_tu = TSF_TO_TU(hw_tsf); #define FUDGE (AR5K_TUNE_SW_BEACON_RESP + 3) /* We use FUDGE to make sure the next TBTT is ahead of the current TU. * Since we later subtract AR5K_TUNE_SW_BEACON_RESP (10) in the timer * configuration we need to make sure it is bigger than that. */ if (bc_tsf == -1) { /* * no beacons received, called internally. * just need to refresh timers based on HW TSF. */ nexttbtt = roundup(hw_tu + FUDGE, intval); } else if (bc_tsf == 0) { /* * no beacon received, probably called by ath5k_reset_tsf(). * reset TSF to start with 0. */ nexttbtt = intval; intval |= AR5K_BEACON_RESET_TSF; } else if (bc_tsf > hw_tsf) { /* * beacon received, SW merge happened but HW TSF not yet updated. * not possible to reconfigure timers yet, but next time we * receive a beacon with the same BSSID, the hardware will * automatically update the TSF and then we need to reconfigure * the timers. */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "need to wait for HW TSF sync\n"); return; } else { /* * most important case for beacon synchronization between STA. * * beacon received and HW TSF has been already updated by HW. * update next TBTT based on the TSF of the beacon, but make * sure it is ahead of our local TSF timer. */ nexttbtt = bc_tu + roundup(hw_tu + FUDGE - bc_tu, intval); } #undef FUDGE ah->nexttbtt = nexttbtt; intval |= AR5K_BEACON_ENA; ath5k_hw_init_beacon_timers(ah, nexttbtt, intval); /* * debugging output last in order to preserve the time critical aspect * of this function */ if (bc_tsf == -1) ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "reconfigured timers based on HW TSF\n"); else if (bc_tsf == 0) ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "reset HW TSF and timers\n"); else ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "updated timers based on beacon TSF\n"); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n", (unsigned long long) bc_tsf, (unsigned long long) hw_tsf, bc_tu, hw_tu, nexttbtt); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "intval %u %s %s\n", intval & AR5K_BEACON_PERIOD, intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "", intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : ""); } /** * ath5k_beacon_config - Configure the beacon queues and interrupts * * @ah: struct ath5k_hw pointer we are operating on * * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA * interrupts to detect TSF updates only. */ void ath5k_beacon_config(struct ath5k_hw *ah) { spin_lock_bh(&ah->block); ah->bmisscount = 0; ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA); if (ah->enable_beacon) { /* * In IBSS mode we use a self-linked tx descriptor and let the * hardware send the beacons automatically. We have to load it * only once here. * We use the SWBA interrupt only to keep track of the beacon * timers in order to detect automatic TSF updates. */ ath5k_beaconq_config(ah); ah->imask |= AR5K_INT_SWBA; if (ah->opmode == NL80211_IFTYPE_ADHOC) { if (ath5k_hw_hasveol(ah)) ath5k_beacon_send(ah); } else ath5k_beacon_update_timers(ah, -1); } else { ath5k_hw_stop_beacon_queue(ah, ah->bhalq); } ath5k_hw_set_imr(ah, ah->imask); mmiowb(); spin_unlock_bh(&ah->block); } static void ath5k_tasklet_beacon(unsigned long data) { struct ath5k_hw *ah = (struct ath5k_hw *) data; /* * Software beacon alert--time to send a beacon. * * In IBSS mode we use this interrupt just to * keep track of the next TBTT (target beacon * transmission time) in order to detect whether * automatic TSF updates happened. */ if (ah->opmode == NL80211_IFTYPE_ADHOC) { /* XXX: only if VEOL supported */ u64 tsf = ath5k_hw_get_tsf64(ah); ah->nexttbtt += ah->bintval; ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "SWBA nexttbtt: %x hw_tu: %x " "TSF: %llx\n", ah->nexttbtt, TSF_TO_TU(tsf), (unsigned long long) tsf); } else { spin_lock(&ah->block); ath5k_beacon_send(ah); spin_unlock(&ah->block); } } /********************\ * Interrupt handling * \********************/ static void ath5k_intr_calibration_poll(struct ath5k_hw *ah) { if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) && !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { /* Run ANI only when calibration is not active */ ah->ah_cal_next_ani = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); tasklet_schedule(&ah->ani_tasklet); } else if (time_is_before_eq_jiffies(ah->ah_cal_next_short) && !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { /* Run calibration only when another calibration * is not running. * * Note: This is for both full/short calibration, * if it's time for a full one, ath5k_calibrate_work will deal * with it. */ ah->ah_cal_next_short = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); ieee80211_queue_work(ah->hw, &ah->calib_work); } /* we could use SWI to generate enough interrupts to meet our * calibration interval requirements, if necessary: * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */ } static void ath5k_schedule_rx(struct ath5k_hw *ah) { ah->rx_pending = true; tasklet_schedule(&ah->rxtq); } static void ath5k_schedule_tx(struct ath5k_hw *ah) { ah->tx_pending = true; tasklet_schedule(&ah->txtq); } static irqreturn_t ath5k_intr(int irq, void *dev_id) { struct ath5k_hw *ah = dev_id; enum ath5k_int status; unsigned int counter = 1000; /* * If hw is not ready (or detached) and we get an * interrupt, or if we have no interrupts pending * (that means it's not for us) skip it. * * NOTE: Group 0/1 PCI interface registers are not * supported on WiSOCs, so we can't check for pending * interrupts (ISR belongs to another register group * so we are ok). */ if (unlikely(test_bit(ATH_STAT_INVALID, ah->status) || ((ath5k_get_bus_type(ah) != ATH_AHB) && !ath5k_hw_is_intr_pending(ah)))) return IRQ_NONE; /** Main loop **/ do { ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ ATH5K_DBG(ah, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n", status, ah->imask); /* * Fatal hw error -> Log and reset * * Fatal errors are unrecoverable so we have to * reset the card. These errors include bus and * dma errors. */ if (unlikely(status & AR5K_INT_FATAL)) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "fatal int, resetting\n"); ieee80211_queue_work(ah->hw, &ah->reset_work); /* * RX Overrun -> Count and reset if needed * * Receive buffers are full. Either the bus is busy or * the CPU is not fast enough to process all received * frames. */ } else if (unlikely(status & AR5K_INT_RXORN)) { /* * Older chipsets need a reset to come out of this * condition, but we treat it as RX for newer chips. * We don't know exactly which versions need a reset * this guess is copied from the HAL. */ ah->stats.rxorn_intr++; if (ah->ah_mac_srev < AR5K_SREV_AR5212) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "rx overrun, resetting\n"); ieee80211_queue_work(ah->hw, &ah->reset_work); } else ath5k_schedule_rx(ah); } else { /* Software Beacon Alert -> Schedule beacon tasklet */ if (status & AR5K_INT_SWBA) tasklet_hi_schedule(&ah->beacontq); /* * No more RX descriptors -> Just count * * NB: the hardware should re-read the link when * RXE bit is written, but it doesn't work at * least on older hardware revs. */ if (status & AR5K_INT_RXEOL) ah->stats.rxeol_intr++; /* TX Underrun -> Bump tx trigger level */ if (status & AR5K_INT_TXURN) ath5k_hw_update_tx_triglevel(ah, true); /* RX -> Schedule rx tasklet */ if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR)) ath5k_schedule_rx(ah); /* TX -> Schedule tx tasklet */ if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC | AR5K_INT_TXERR | AR5K_INT_TXEOL)) ath5k_schedule_tx(ah); /* Missed beacon -> TODO if (status & AR5K_INT_BMISS) */ /* MIB event -> Update counters and notify ANI */ if (status & AR5K_INT_MIB) { ah->stats.mib_intr++; ath5k_hw_update_mib_counters(ah); ath5k_ani_mib_intr(ah); } /* GPIO -> Notify RFKill layer */ if (status & AR5K_INT_GPIO) tasklet_schedule(&ah->rf_kill.toggleq); } if (ath5k_get_bus_type(ah) == ATH_AHB) break; } while (ath5k_hw_is_intr_pending(ah) && --counter > 0); /* * Until we handle rx/tx interrupts mask them on IMR * * NOTE: ah->(rx/tx)_pending are set when scheduling the tasklets * and unset after we 've handled the interrupts. */ if (ah->rx_pending || ah->tx_pending) ath5k_set_current_imask(ah); if (unlikely(!counter)) ATH5K_WARN(ah, "too many interrupts, giving up for now\n"); /* Fire up calibration poll */ ath5k_intr_calibration_poll(ah); return IRQ_HANDLED; } /* * Periodically recalibrate the PHY to account * for temperature/environment changes. */ static void ath5k_calibrate_work(struct work_struct *work) { struct ath5k_hw *ah = container_of(work, struct ath5k_hw, calib_work); /* Should we run a full calibration ? */ if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) { ah->ah_cal_next_full = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); ah->ah_cal_mask |= AR5K_CALIBRATION_FULL; ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "running full calibration\n"); if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { /* * Rfgain is out of bounds, reset the chip * to load new gain values. */ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "got new rfgain, resetting\n"); ieee80211_queue_work(ah->hw, &ah->reset_work); } } else ah->ah_cal_mask |= AR5K_CALIBRATION_SHORT; ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", ieee80211_frequency_to_channel(ah->curchan->center_freq), ah->curchan->hw_value); if (ath5k_hw_phy_calibrate(ah, ah->curchan)) ATH5K_ERR(ah, "calibration of channel %u failed\n", ieee80211_frequency_to_channel( ah->curchan->center_freq)); /* Clear calibration flags */ if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL) ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; else if (ah->ah_cal_mask & AR5K_CALIBRATION_SHORT) ah->ah_cal_mask &= ~AR5K_CALIBRATION_SHORT; } static void ath5k_tasklet_ani(unsigned long data) { struct ath5k_hw *ah = (void *)data; ah->ah_cal_mask |= AR5K_CALIBRATION_ANI; ath5k_ani_calibration(ah); ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI; } static void ath5k_tx_complete_poll_work(struct work_struct *work) { struct ath5k_hw *ah = container_of(work, struct ath5k_hw, tx_complete_work.work); struct ath5k_txq *txq; int i; bool needreset = false; mutex_lock(&ah->lock); for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) { if (ah->txqs[i].setup) { txq = &ah->txqs[i]; spin_lock_bh(&txq->lock); if (txq->txq_len > 1) { if (txq->txq_poll_mark) { ATH5K_DBG(ah, ATH5K_DEBUG_XMIT, "TX queue stuck %d\n", txq->qnum); needreset = true; txq->txq_stuck++; spin_unlock_bh(&txq->lock); break; } else { txq->txq_poll_mark = true; } } spin_unlock_bh(&txq->lock); } } if (needreset) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "TX queues stuck, resetting\n"); ath5k_reset(ah, NULL, true); } mutex_unlock(&ah->lock); ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work, msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); } /*************************\ * Initialization routines * \*************************/ static const struct ieee80211_iface_limit if_limits[] = { { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) }, { .max = 4, .types = #ifdef CONFIG_MAC80211_MESH BIT(NL80211_IFTYPE_MESH_POINT) | #endif BIT(NL80211_IFTYPE_AP) }, }; static const struct ieee80211_iface_combination if_comb = { .limits = if_limits, .n_limits = ARRAY_SIZE(if_limits), .max_interfaces = 2048, .num_different_channels = 1, }; int __devinit ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) { struct ieee80211_hw *hw = ah->hw; struct ath_common *common; int ret; int csz; /* Initialize driver private data */ SET_IEEE80211_DEV(hw, ah->dev); hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_REPORTS_TX_ACK_STATUS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); hw->wiphy->iface_combinations = &if_comb; hw->wiphy->n_iface_combinations = 1; /* SW support for IBSS_RSN is provided by mac80211 */ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; /* both antennas can be configured as RX or TX */ hw->wiphy->available_antennas_tx = 0x3; hw->wiphy->available_antennas_rx = 0x3; hw->extra_tx_headroom = 2; hw->channel_change_time = 5000; /* * Mark the device as detached to avoid processing * interrupts until setup is complete. */ __set_bit(ATH_STAT_INVALID, ah->status); ah->opmode = NL80211_IFTYPE_STATION; ah->bintval = 1000; mutex_init(&ah->lock); spin_lock_init(&ah->rxbuflock); spin_lock_init(&ah->txbuflock); spin_lock_init(&ah->block); spin_lock_init(&ah->irqlock); /* Setup interrupt handler */ ret = request_irq(ah->irq, ath5k_intr, IRQF_SHARED, "ath", ah); if (ret) { ATH5K_ERR(ah, "request_irq failed\n"); goto err; } common = ath5k_hw_common(ah); common->ops = &ath5k_common_ops; common->bus_ops = bus_ops; common->ah = ah; common->hw = hw; common->priv = ah; common->clockrate = 40; /* * Cache line size is used to size and align various * structures used to communicate with the hardware. */ ath5k_read_cachesize(common, &csz); common->cachelsz = csz << 2; /* convert to bytes */ spin_lock_init(&common->cc_lock); /* Initialize device */ ret = ath5k_hw_init(ah); if (ret) goto err_irq; /* Set up multi-rate retry capabilities */ if (ah->ah_capabilities.cap_has_mrr_support) { hw->max_rates = 4; hw->max_rate_tries = max(AR5K_INIT_RETRY_SHORT, AR5K_INIT_RETRY_LONG); } hw->vif_data_size = sizeof(struct ath5k_vif); /* Finish private driver data initialization */ ret = ath5k_init(hw); if (ret) goto err_ah; ATH5K_INFO(ah, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n", ath5k_chip_name(AR5K_VERSION_MAC, ah->ah_mac_srev), ah->ah_mac_srev, ah->ah_phy_revision); if (!ah->ah_single_chip) { /* Single chip radio (!RF5111) */ if (ah->ah_radio_5ghz_revision && !ah->ah_radio_2ghz_revision) { /* No 5GHz support -> report 2GHz radio */ if (!test_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode)) { ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n", ath5k_chip_name(AR5K_VERSION_RAD, ah->ah_radio_5ghz_revision), ah->ah_radio_5ghz_revision); /* No 2GHz support (5110 and some * 5GHz only cards) -> report 5GHz radio */ } else if (!test_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode)) { ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n", ath5k_chip_name(AR5K_VERSION_RAD, ah->ah_radio_5ghz_revision), ah->ah_radio_5ghz_revision); /* Multiband radio */ } else { ATH5K_INFO(ah, "RF%s multiband radio found" " (0x%x)\n", ath5k_chip_name(AR5K_VERSION_RAD, ah->ah_radio_5ghz_revision), ah->ah_radio_5ghz_revision); } } /* Multi chip radio (RF5111 - RF2111) -> * report both 2GHz/5GHz radios */ else if (ah->ah_radio_5ghz_revision && ah->ah_radio_2ghz_revision) { ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n", ath5k_chip_name(AR5K_VERSION_RAD, ah->ah_radio_5ghz_revision), ah->ah_radio_5ghz_revision); ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n", ath5k_chip_name(AR5K_VERSION_RAD, ah->ah_radio_2ghz_revision), ah->ah_radio_2ghz_revision); } } ath5k_debug_init_device(ah); /* ready to process interrupts */ __clear_bit(ATH_STAT_INVALID, ah->status); return 0; err_ah: ath5k_hw_deinit(ah); err_irq: free_irq(ah->irq, ah); err: return ret; } static int ath5k_stop_locked(struct ath5k_hw *ah) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "invalid %u\n", test_bit(ATH_STAT_INVALID, ah->status)); /* * Shutdown the hardware and driver: * stop output from above * disable interrupts * turn off timers * turn off the radio * clear transmit machinery * clear receive machinery * drain and release tx queues * reclaim beacon resources * power down hardware * * Note that some of this work is not possible if the * hardware is gone (invalid). */ ieee80211_stop_queues(ah->hw); if (!test_bit(ATH_STAT_INVALID, ah->status)) { ath5k_led_off(ah); ath5k_hw_set_imr(ah, 0); synchronize_irq(ah->irq); ath5k_rx_stop(ah); ath5k_hw_dma_stop(ah); ath5k_drain_tx_buffs(ah); ath5k_hw_phy_disable(ah); } return 0; } int ath5k_start(struct ieee80211_hw *hw) { struct ath5k_hw *ah = hw->priv; struct ath_common *common = ath5k_hw_common(ah); int ret, i; mutex_lock(&ah->lock); ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "mode %d\n", ah->opmode); /* * Stop anything previously setup. This is safe * no matter this is the first time through or not. */ ath5k_stop_locked(ah); /* * The basic interface to setting the hardware in a good * state is ``reset''. On return the hardware is known to * be powered up and with interrupts disabled. This must * be followed by initialization of the appropriate bits * and then setup of the interrupt mask. */ ah->curchan = ah->hw->conf.channel; ah->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; ret = ath5k_reset(ah, NULL, false); if (ret) goto done; if (!ath5k_modparam_no_hw_rfkill_switch) ath5k_rfkill_hw_start(ah); /* * Reset the key cache since some parts do not reset the * contents on initial power up or resume from suspend. */ for (i = 0; i < common->keymax; i++) ath_hw_keyreset(common, (u16) i); /* Use higher rates for acks instead of base * rate */ ah->ah_ack_bitrate_high = true; for (i = 0; i < ARRAY_SIZE(ah->bslot); i++) ah->bslot[i] = NULL; ret = 0; done: mmiowb(); mutex_unlock(&ah->lock); ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work, msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); return ret; } static void ath5k_stop_tasklets(struct ath5k_hw *ah) { ah->rx_pending = false; ah->tx_pending = false; tasklet_kill(&ah->rxtq); tasklet_kill(&ah->txtq); tasklet_kill(&ah->beacontq); tasklet_kill(&ah->ani_tasklet); } /* * Stop the device, grabbing the top-level lock to protect * against concurrent entry through ath5k_init (which can happen * if another thread does a system call and the thread doing the * stop is preempted). */ void ath5k_stop(struct ieee80211_hw *hw) { struct ath5k_hw *ah = hw->priv; int ret; mutex_lock(&ah->lock); ret = ath5k_stop_locked(ah); if (ret == 0 && !test_bit(ATH_STAT_INVALID, ah->status)) { /* * Don't set the card in full sleep mode! * * a) When the device is in this state it must be carefully * woken up or references to registers in the PCI clock * domain may freeze the bus (and system). This varies * by chip and is mostly an issue with newer parts * (madwifi sources mentioned srev >= 0x78) that go to * sleep more quickly. * * b) On older chips full sleep results a weird behaviour * during wakeup. I tested various cards with srev < 0x78 * and they don't wake up after module reload, a second * module reload is needed to bring the card up again. * * Until we figure out what's going on don't enable * full chip reset on any chip (this is what Legacy HAL * and Sam's HAL do anyway). Instead Perform a full reset * on the device (same as initial state after attach) and * leave it idle (keep MAC/BB on warm reset) */ ret = ath5k_hw_on_hold(ah); ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "putting device to sleep\n"); } mmiowb(); mutex_unlock(&ah->lock); ath5k_stop_tasklets(ah); cancel_delayed_work_sync(&ah->tx_complete_work); if (!ath5k_modparam_no_hw_rfkill_switch) ath5k_rfkill_hw_stop(ah); } /* * Reset the hardware. If chan is not NULL, then also pause rx/tx * and change to the given channel. * * This should be called with ah->lock. */ static int ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, bool skip_pcu) { struct ath_common *common = ath5k_hw_common(ah); int ret, ani_mode; bool fast; ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n"); ath5k_hw_set_imr(ah, 0); synchronize_irq(ah->irq); ath5k_stop_tasklets(ah); /* Save ani mode and disable ANI during * reset. If we don't we might get false * PHY error interrupts. */ ani_mode = ah->ani_state.ani_mode; ath5k_ani_init(ah, ATH5K_ANI_MODE_OFF); /* We are going to empty hw queues * so we should also free any remaining * tx buffers */ ath5k_drain_tx_buffs(ah); if (chan) ah->curchan = chan; fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0; ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu); if (ret) { ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret); goto err; } ret = ath5k_rx_start(ah); if (ret) { ATH5K_ERR(ah, "can't start recv logic\n"); goto err; } ath5k_ani_init(ah, ani_mode); /* * Set calibration intervals * * Note: We don't need to run calibration imediately * since some initial calibration is done on reset * even for fast channel switching. Also on scanning * this will get set again and again and it won't get * executed unless we connect somewhere and spend some * time on the channel (that's what calibration needs * anyway to be accurate). */ ah->ah_cal_next_full = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); ah->ah_cal_next_ani = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); ah->ah_cal_next_short = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); ewma_init(&ah->ah_beacon_rssi_avg, 1024, 8); /* clear survey data and cycle counters */ memset(&ah->survey, 0, sizeof(ah->survey)); spin_lock_bh(&common->cc_lock); ath_hw_cycle_counters_update(common); memset(&common->cc_survey, 0, sizeof(common->cc_survey)); memset(&common->cc_ani, 0, sizeof(common->cc_ani)); spin_unlock_bh(&common->cc_lock); /* * Change channels and update the h/w rate map if we're switching; * e.g. 11a to 11b/g. * * We may be doing a reset in response to an ioctl that changes the * channel so update any state that might change as a result. * * XXX needed? */ /* ath5k_chan_change(ah, c); */ ath5k_beacon_config(ah); /* intrs are enabled by ath5k_beacon_config */ ieee80211_wake_queues(ah->hw); return 0; err: return ret; } static void ath5k_reset_work(struct work_struct *work) { struct ath5k_hw *ah = container_of(work, struct ath5k_hw, reset_work); mutex_lock(&ah->lock); ath5k_reset(ah, NULL, true); mutex_unlock(&ah->lock); } static int __devinit ath5k_init(struct ieee80211_hw *hw) { struct ath5k_hw *ah = hw->priv; struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); struct ath5k_txq *txq; u8 mac[ETH_ALEN] = {}; int ret; /* * Collect the channel list. The 802.11 layer * is responsible for filtering this list based * on settings like the phy mode and regulatory * domain restrictions. */ ret = ath5k_setup_bands(hw); if (ret) { ATH5K_ERR(ah, "can't get channels\n"); goto err; } /* * Allocate tx+rx descriptors and populate the lists. */ ret = ath5k_desc_alloc(ah); if (ret) { ATH5K_ERR(ah, "can't allocate descriptors\n"); goto err; } /* * Allocate hardware transmit queues: one queue for * beacon frames and one data queue for each QoS * priority. Note that hw functions handle resetting * these queues at the needed time. */ ret = ath5k_beaconq_setup(ah); if (ret < 0) { ATH5K_ERR(ah, "can't setup a beacon xmit queue\n"); goto err_desc; } ah->bhalq = ret; ah->cabq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_CAB, 0); if (IS_ERR(ah->cabq)) { ATH5K_ERR(ah, "can't setup cab queue\n"); ret = PTR_ERR(ah->cabq); goto err_bhal; } /* 5211 and 5212 usually support 10 queues but we better rely on the * capability information */ if (ah->ah_capabilities.cap_queues.q_tx_num >= 6) { /* This order matches mac80211's queue priority, so we can * directly use the mac80211 queue number without any mapping */ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO); if (IS_ERR(txq)) { ATH5K_ERR(ah, "can't setup xmit queue\n"); ret = PTR_ERR(txq); goto err_queues; } txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI); if (IS_ERR(txq)) { ATH5K_ERR(ah, "can't setup xmit queue\n"); ret = PTR_ERR(txq); goto err_queues; } txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE); if (IS_ERR(txq)) { ATH5K_ERR(ah, "can't setup xmit queue\n"); ret = PTR_ERR(txq); goto err_queues; } txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); if (IS_ERR(txq)) { ATH5K_ERR(ah, "can't setup xmit queue\n"); ret = PTR_ERR(txq); goto err_queues; } hw->queues = 4; } else { /* older hardware (5210) can only support one data queue */ txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE); if (IS_ERR(txq)) { ATH5K_ERR(ah, "can't setup xmit queue\n"); ret = PTR_ERR(txq); goto err_queues; } hw->queues = 1; } tasklet_init(&ah->rxtq, ath5k_tasklet_rx, (unsigned long)ah); tasklet_init(&ah->txtq, ath5k_tasklet_tx, (unsigned long)ah); tasklet_init(&ah->beacontq, ath5k_tasklet_beacon, (unsigned long)ah); tasklet_init(&ah->ani_tasklet, ath5k_tasklet_ani, (unsigned long)ah); INIT_WORK(&ah->reset_work, ath5k_reset_work); INIT_WORK(&ah->calib_work, ath5k_calibrate_work); INIT_DELAYED_WORK(&ah->tx_complete_work, ath5k_tx_complete_poll_work); ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac); if (ret) { ATH5K_ERR(ah, "unable to read address from EEPROM\n"); goto err_queues; } SET_IEEE80211_PERM_ADDR(hw, mac); /* All MAC address bits matter for ACKs */ ath5k_update_bssid_mask_and_opmode(ah, NULL); regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier); if (ret) { ATH5K_ERR(ah, "can't initialize regulatory system\n"); goto err_queues; } ret = ieee80211_register_hw(hw); if (ret) { ATH5K_ERR(ah, "can't register ieee80211 hw\n"); goto err_queues; } if (!ath_is_world_regd(regulatory)) regulatory_hint(hw->wiphy, regulatory->alpha2); ath5k_init_leds(ah); ath5k_sysfs_register(ah); return 0; err_queues: ath5k_txq_release(ah); err_bhal: ath5k_hw_release_tx_queue(ah, ah->bhalq); err_desc: ath5k_desc_free(ah); err: return ret; } void ath5k_deinit_ah(struct ath5k_hw *ah) { struct ieee80211_hw *hw = ah->hw; /* * NB: the order of these is important: * o call the 802.11 layer before detaching ath5k_hw to * ensure callbacks into the driver to delete global * key cache entries can be handled * o reclaim the tx queue data structures after calling * the 802.11 layer as we'll get called back to reclaim * node state and potentially want to use them * o to cleanup the tx queues the hal is called, so detach * it last * XXX: ??? detach ath5k_hw ??? * Other than that, it's straightforward... */ ieee80211_unregister_hw(hw); ath5k_desc_free(ah); ath5k_txq_release(ah); ath5k_hw_release_tx_queue(ah, ah->bhalq); ath5k_unregister_leds(ah); ath5k_sysfs_unregister(ah); /* * NB: can't reclaim these until after ieee80211_ifdetach * returns because we'll get called back to reclaim node * state and potentially want to use them. */ ath5k_hw_deinit(ah); free_irq(ah->irq, ah); } bool ath5k_any_vif_assoc(struct ath5k_hw *ah) { struct ath5k_vif_iter_data iter_data; iter_data.hw_macaddr = NULL; iter_data.any_assoc = false; iter_data.need_set_hw_addr = false; iter_data.found_active = true; ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, &iter_data); return iter_data.any_assoc; } void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable) { struct ath5k_hw *ah = hw->priv; u32 rfilt; rfilt = ath5k_hw_get_rx_filter(ah); if (enable) rfilt |= AR5K_RX_FILTER_BEACON; else rfilt &= ~AR5K_RX_FILTER_BEACON; ath5k_hw_set_rx_filter(ah, rfilt); ah->filter_flags = rfilt; } void _ath5k_printk(const struct ath5k_hw *ah, const char *level, const char *fmt, ...) { struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; if (ah && ah->hw) printk("%s" pr_fmt("%s: %pV"), level, wiphy_name(ah->hw->wiphy), &vaf); else printk("%s" pr_fmt("%pV"), level, &vaf); va_end(args); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/attach.c0000644000175000017500000002373312026211315023671 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /*************************************\ * Attach/Detach Functions and helpers * \*************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include "ath5k.h" #include "reg.h" #include "debug.h" /** * ath5k_hw_post() - Power On Self Test helper function * @ah: The &struct ath5k_hw */ static int ath5k_hw_post(struct ath5k_hw *ah) { static const u32 static_pattern[4] = { 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999 }; static const u16 regs[2] = { AR5K_STA_ID0, AR5K_PHY(8) }; int i, c; u16 cur_reg; u32 var_pattern; u32 init_val; u32 cur_val; for (c = 0; c < 2; c++) { cur_reg = regs[c]; /* Save previous value */ init_val = ath5k_hw_reg_read(ah, cur_reg); for (i = 0; i < 256; i++) { var_pattern = i << 16 | i; ath5k_hw_reg_write(ah, var_pattern, cur_reg); cur_val = ath5k_hw_reg_read(ah, cur_reg); if (cur_val != var_pattern) { ATH5K_ERR(ah, "POST Failed !!!\n"); return -EAGAIN; } /* Found on ndiswrapper dumps */ var_pattern = 0x0039080f; ath5k_hw_reg_write(ah, var_pattern, cur_reg); } for (i = 0; i < 4; i++) { var_pattern = static_pattern[i]; ath5k_hw_reg_write(ah, var_pattern, cur_reg); cur_val = ath5k_hw_reg_read(ah, cur_reg); if (cur_val != var_pattern) { ATH5K_ERR(ah, "POST Failed !!!\n"); return -EAGAIN; } /* Found on ndiswrapper dumps */ var_pattern = 0x003b080f; ath5k_hw_reg_write(ah, var_pattern, cur_reg); } /* Restore previous value */ ath5k_hw_reg_write(ah, init_val, cur_reg); } return 0; } /** * ath5k_hw_init() - Check if hw is supported and init the needed structs * @ah: The &struct ath5k_hw associated with the device * * Check if the device is supported, perform a POST and initialize the needed * structs. Returns -ENOMEM if we don't have memory for the needed structs, * -ENODEV if the device is not supported or prints an error msg if something * else went wrong. */ int ath5k_hw_init(struct ath5k_hw *ah) { static const u8 zero_mac[ETH_ALEN] = { }; struct ath_common *common = ath5k_hw_common(ah); struct pci_dev *pdev = ah->pdev; struct ath5k_eeprom_info *ee; int ret; u32 srev; /* * HW information */ ah->ah_bwmode = AR5K_BWMODE_DEFAULT; ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; ah->ah_imr = 0; ah->ah_retry_short = AR5K_INIT_RETRY_SHORT; ah->ah_retry_long = AR5K_INIT_RETRY_LONG; ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT; ah->ah_noise_floor = -95; /* until first NF calibration is run */ ah->ani_state.ani_mode = ATH5K_ANI_MODE_AUTO; ah->ah_current_channel = &ah->channels[0]; /* * Find the mac version */ ath5k_hw_read_srev(ah); srev = ah->ah_mac_srev; if (srev < AR5K_SREV_AR5311) ah->ah_version = AR5K_AR5210; else if (srev < AR5K_SREV_AR5212) ah->ah_version = AR5K_AR5211; else ah->ah_version = AR5K_AR5212; /* Get the MAC version */ ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER); /* Fill the ath5k_hw struct with the needed functions */ ret = ath5k_hw_init_desc_functions(ah); if (ret) goto err; /* Bring device out of sleep and reset its units */ ret = ath5k_hw_nic_wakeup(ah, NULL); if (ret) goto err; /* Get PHY and RADIO revisions */ ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) & 0xffffffff; ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah, IEEE80211_BAND_5GHZ); /* Try to identify radio chip based on its srev */ switch (ah->ah_radio_5ghz_revision & 0xf0) { case AR5K_SREV_RAD_5111: ah->ah_radio = AR5K_RF5111; ah->ah_single_chip = false; ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, IEEE80211_BAND_2GHZ); break; case AR5K_SREV_RAD_5112: case AR5K_SREV_RAD_2112: ah->ah_radio = AR5K_RF5112; ah->ah_single_chip = false; ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, IEEE80211_BAND_2GHZ); break; case AR5K_SREV_RAD_2413: ah->ah_radio = AR5K_RF2413; ah->ah_single_chip = true; break; case AR5K_SREV_RAD_5413: ah->ah_radio = AR5K_RF5413; ah->ah_single_chip = true; break; case AR5K_SREV_RAD_2316: ah->ah_radio = AR5K_RF2316; ah->ah_single_chip = true; break; case AR5K_SREV_RAD_2317: ah->ah_radio = AR5K_RF2317; ah->ah_single_chip = true; break; case AR5K_SREV_RAD_5424: if (ah->ah_mac_version == AR5K_SREV_AR2425 || ah->ah_mac_version == AR5K_SREV_AR2417) { ah->ah_radio = AR5K_RF2425; ah->ah_single_chip = true; } else { ah->ah_radio = AR5K_RF5413; ah->ah_single_chip = true; } break; default: /* Identify radio based on mac/phy srev */ if (ah->ah_version == AR5K_AR5210) { ah->ah_radio = AR5K_RF5110; ah->ah_single_chip = false; } else if (ah->ah_version == AR5K_AR5211) { ah->ah_radio = AR5K_RF5111; ah->ah_single_chip = false; ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, IEEE80211_BAND_2GHZ); } else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) || ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) || ah->ah_phy_revision == AR5K_SREV_PHY_2425) { ah->ah_radio = AR5K_RF2425; ah->ah_single_chip = true; ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425; } else if (srev == AR5K_SREV_AR5213A && ah->ah_phy_revision == AR5K_SREV_PHY_5212B) { ah->ah_radio = AR5K_RF5112; ah->ah_single_chip = false; ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B; } else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4) || ah->ah_mac_version == (AR5K_SREV_AR2315_R6 >> 4)) { ah->ah_radio = AR5K_RF2316; ah->ah_single_chip = true; ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316; } else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) || ah->ah_phy_revision == AR5K_SREV_PHY_5413) { ah->ah_radio = AR5K_RF5413; ah->ah_single_chip = true; ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413; } else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) || ah->ah_phy_revision == AR5K_SREV_PHY_2413) { ah->ah_radio = AR5K_RF2413; ah->ah_single_chip = true; ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413; } else { ATH5K_ERR(ah, "Couldn't identify radio revision.\n"); ret = -ENODEV; goto err; } } /* Return on unsupported chips (unsupported eeprom etc) */ if ((srev >= AR5K_SREV_AR5416) && (srev < AR5K_SREV_AR2425)) { ATH5K_ERR(ah, "Device not yet supported.\n"); ret = -ENODEV; goto err; } /* * POST */ ret = ath5k_hw_post(ah); if (ret) goto err; /* Enable pci core retry fix on Hainan (5213A) and later chips */ if (srev >= AR5K_SREV_AR5213A) AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_RETRY_FIX); /* * Get card capabilities, calibration values etc * TODO: EEPROM work */ ret = ath5k_eeprom_init(ah); if (ret) { ATH5K_ERR(ah, "unable to init EEPROM\n"); goto err; } ee = &ah->ah_capabilities.cap_eeprom; /* * Write PCI-E power save settings */ if ((ah->ah_version == AR5K_AR5212) && pdev && (pci_is_pcie(pdev))) { ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES); ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES); /* Shut off RX when elecidle is asserted */ ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES); ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES); /* If serdes programming is enabled, increase PCI-E * tx power for systems with long trace from host * to minicard connector. */ if (ee->ee_serdes) ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES); else ath5k_hw_reg_write(ah, 0xf6800579, AR5K_PCIE_SERDES); /* Shut off PLL and CLKREQ active in L1 */ ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES); /* Preserve other settings */ ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES); ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES); ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES); /* Reset SERDES to load new settings */ ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET); usleep_range(1000, 1500); } /* Get misc capabilities */ ret = ath5k_hw_set_capabilities(ah); if (ret) { ATH5K_ERR(ah, "unable to get device capabilities\n"); goto err; } /* Crypto settings */ common->keymax = (ah->ah_version == AR5K_AR5210 ? AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211); if (srev >= AR5K_SREV_AR5212_V4 && (ee->ee_version < AR5K_EEPROM_VERSION_5_0 || !AR5K_EEPROM_AES_DIS(ee->ee_misc5))) common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM; if (srev >= AR5K_SREV_AR2414) { common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED; AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE, AR5K_MISC_MODE_COMBINED_MIC); } /* MAC address is cleared until add_interface */ ath5k_hw_set_lladdr(ah, zero_mac); /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */ memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN); ath5k_hw_set_bssid(ah); ath5k_hw_set_opmode(ah, ah->opmode); ath5k_hw_rfgain_opt_init(ah); ath5k_hw_init_nfcal_hist(ah); /* turn on HW LEDs */ ath5k_hw_set_ledstate(ah, AR5K_LED_INIT); return 0; err: return ret; } /** * ath5k_hw_deinit() - Free the &struct ath5k_hw * @ah: The &struct ath5k_hw */ void ath5k_hw_deinit(struct ath5k_hw *ah) { __set_bit(ATH_STAT_INVALID, ah->status); if (ah->ah_rf_banks != NULL) kfree(ah->ah_rf_banks); ath5k_eeprom_detach(ah); /* assume interrupts are down */ } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/reset.c0000644000175000017500000011204312026211315023540 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * Copyright (c) 2007-2008 Luis Rodriguez * Copyright (c) 2007-2008 Pavel Roskin * Copyright (c) 2007-2008 Jiri Slaby * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /****************************\ Reset function and helpers \****************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include /* To determine if a card is pci-e */ #include #include #include "ath5k.h" #include "reg.h" #include "debug.h" /** * DOC: Reset function and helpers * * Here we implement the main reset routine, used to bring the card * to a working state and ready to receive. We also handle routines * that don't fit on other places such as clock, sleep and power control */ /******************\ * Helper functions * \******************/ /** * ath5k_hw_register_timeout() - Poll a register for a flag/field change * @ah: The &struct ath5k_hw * @reg: The register to read * @flag: The flag/field to check on the register * @val: The field value we expect (if we check a field) * @is_set: Instead of checking if the flag got cleared, check if it got set * * Some registers contain flags that indicate that an operation is * running. We use this function to poll these registers and check * if these flags get cleared. We also use it to poll a register * field (containing multiple flags) until it gets a specific value. * * Returns -EAGAIN if we exceeded AR5K_TUNE_REGISTER_TIMEOUT * 15us or 0 */ int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val, bool is_set) { int i; u32 data; for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) { data = ath5k_hw_reg_read(ah, reg); if (is_set && (data & flag)) break; else if ((data & flag) == val) break; udelay(15); } return (i <= 0) ? -EAGAIN : 0; } /*************************\ * Clock related functions * \*************************/ /** * ath5k_hw_htoclock() - Translate usec to hw clock units * @ah: The &struct ath5k_hw * @usec: value in microseconds * * Translate usecs to hw clock units based on the current * hw clock rate. * * Returns number of clock units */ unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) { struct ath_common *common = ath5k_hw_common(ah); return usec * common->clockrate; } /** * ath5k_hw_clocktoh() - Translate hw clock units to usec * @ah: The &struct ath5k_hw * @clock: value in hw clock units * * Translate hw clock units to usecs based on the current * hw clock rate. * * Returns number of usecs */ unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock) { struct ath_common *common = ath5k_hw_common(ah); return clock / common->clockrate; } /** * ath5k_hw_init_core_clock() - Initialize core clock * @ah: The &struct ath5k_hw * * Initialize core clock parameters (usec, usec32, latencies etc), * based on current bwmode and chipset properties. */ static void ath5k_hw_init_core_clock(struct ath5k_hw *ah) { struct ieee80211_channel *channel = ah->ah_current_channel; struct ath_common *common = ath5k_hw_common(ah); u32 usec_reg, txlat, rxlat, usec, clock, sclock, txf2txs; /* * Set core clock frequency */ switch (channel->hw_value) { case AR5K_MODE_11A: clock = 40; break; case AR5K_MODE_11B: clock = 22; break; case AR5K_MODE_11G: default: clock = 44; break; } /* Use clock multiplier for non-default * bwmode */ switch (ah->ah_bwmode) { case AR5K_BWMODE_40MHZ: clock *= 2; break; case AR5K_BWMODE_10MHZ: clock /= 2; break; case AR5K_BWMODE_5MHZ: clock /= 4; break; default: break; } common->clockrate = clock; /* * Set USEC parameters */ /* Set USEC counter on PCU*/ usec = clock - 1; usec = AR5K_REG_SM(usec, AR5K_USEC_1); /* Set usec duration on DCU */ if (ah->ah_version != AR5K_AR5210) AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC, AR5K_DCU_GBL_IFS_MISC_USEC_DUR, clock); /* Set 32MHz USEC counter */ if ((ah->ah_radio == AR5K_RF5112) || (ah->ah_radio == AR5K_RF2413) || (ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF2316) || (ah->ah_radio == AR5K_RF2317)) /* Remain on 40MHz clock ? */ sclock = 40 - 1; else sclock = 32 - 1; sclock = AR5K_REG_SM(sclock, AR5K_USEC_32); /* * Set tx/rx latencies */ usec_reg = ath5k_hw_reg_read(ah, AR5K_USEC_5211); txlat = AR5K_REG_MS(usec_reg, AR5K_USEC_TX_LATENCY_5211); rxlat = AR5K_REG_MS(usec_reg, AR5K_USEC_RX_LATENCY_5211); /* * Set default Tx frame to Tx data start delay */ txf2txs = AR5K_INIT_TXF2TXD_START_DEFAULT; /* * 5210 initvals don't include usec settings * so we need to use magic values here for * tx/rx latencies */ if (ah->ah_version == AR5K_AR5210) { /* same for turbo */ txlat = AR5K_INIT_TX_LATENCY_5210; rxlat = AR5K_INIT_RX_LATENCY_5210; } if (ah->ah_mac_srev < AR5K_SREV_AR5211) { /* 5311 has different tx/rx latency masks * from 5211, since we deal 5311 the same * as 5211 when setting initvals, shift * values here to their proper locations * * Note: Initvals indicate tx/rx/ latencies * are the same for turbo mode */ txlat = AR5K_REG_SM(txlat, AR5K_USEC_TX_LATENCY_5210); rxlat = AR5K_REG_SM(rxlat, AR5K_USEC_RX_LATENCY_5210); } else switch (ah->ah_bwmode) { case AR5K_BWMODE_10MHZ: txlat = AR5K_REG_SM(txlat * 2, AR5K_USEC_TX_LATENCY_5211); rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX, AR5K_USEC_RX_LATENCY_5211); txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_10MHZ; break; case AR5K_BWMODE_5MHZ: txlat = AR5K_REG_SM(txlat * 4, AR5K_USEC_TX_LATENCY_5211); rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX, AR5K_USEC_RX_LATENCY_5211); txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_5MHZ; break; case AR5K_BWMODE_40MHZ: txlat = AR5K_INIT_TX_LAT_MIN; rxlat = AR5K_REG_SM(rxlat / 2, AR5K_USEC_RX_LATENCY_5211); txf2txs = AR5K_INIT_TXF2TXD_START_DEFAULT; break; default: break; } usec_reg = (usec | sclock | txlat | rxlat); ath5k_hw_reg_write(ah, usec_reg, AR5K_USEC); /* On 5112 set tx frame to tx data start delay */ if (ah->ah_radio == AR5K_RF5112) { AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL2, AR5K_PHY_RF_CTL2_TXF2TXD_START, txf2txs); } } /** * ath5k_hw_set_sleep_clock() - Setup sleep clock operation * @ah: The &struct ath5k_hw * @enable: Enable sleep clock operation (false to disable) * * If there is an external 32KHz crystal available, use it * as ref. clock instead of 32/40MHz clock and baseband clocks * to save power during sleep or restore normal 32/40MHz * operation. * * NOTE: When operating on 32KHz certain PHY registers (27 - 31, * 123 - 127) require delay on access. */ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 scal, spending, sclock; /* Only set 32KHz settings if we have an external * 32KHz crystal present */ if ((AR5K_EEPROM_HAS32KHZCRYSTAL(ee->ee_misc1) || AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(ee->ee_misc1)) && enable) { /* 1 usec/cycle */ AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, 1); /* Set up tsf increment on each cycle */ AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 61); /* Set baseband sleep control registers * and sleep control rate */ ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR); if ((ah->ah_radio == AR5K_RF5112) || (ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF2316) || (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) spending = 0x14; else spending = 0x18; ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING); if ((ah->ah_radio == AR5K_RF5112) || (ah->ah_radio == AR5K_RF5413) || (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) { ath5k_hw_reg_write(ah, 0x26, AR5K_PHY_SLMT); ath5k_hw_reg_write(ah, 0x0d, AR5K_PHY_SCAL); ath5k_hw_reg_write(ah, 0x07, AR5K_PHY_SCLOCK); ath5k_hw_reg_write(ah, 0x3f, AR5K_PHY_SDELAY); AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x02); } else { ath5k_hw_reg_write(ah, 0x0a, AR5K_PHY_SLMT); ath5k_hw_reg_write(ah, 0x0c, AR5K_PHY_SCAL); ath5k_hw_reg_write(ah, 0x03, AR5K_PHY_SCLOCK); ath5k_hw_reg_write(ah, 0x20, AR5K_PHY_SDELAY); AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x03); } /* Enable sleep clock operation */ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_SLEEP_CLOCK_EN); } else { /* Disable sleep clock operation and * restore default parameters */ AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_SLEEP_CLOCK_EN); AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_SLEEP_CLOCK_RATE, 0); /* Set DAC/ADC delays */ ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR); ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT); if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)) scal = AR5K_PHY_SCAL_32MHZ_2417; else if (ee->ee_is_hb63) scal = AR5K_PHY_SCAL_32MHZ_HB63; else scal = AR5K_PHY_SCAL_32MHZ; ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL); ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK); ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY); if ((ah->ah_radio == AR5K_RF5112) || (ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF2316) || (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) spending = 0x14; else spending = 0x18; ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING); /* Set up tsf increment on each cycle */ AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 1); if ((ah->ah_radio == AR5K_RF5112) || (ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF2316) || (ah->ah_radio == AR5K_RF2317)) sclock = 40 - 1; else sclock = 32 - 1; AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, sclock); } } /*********************\ * Reset/Sleep control * \*********************/ /** * ath5k_hw_nic_reset() - Reset the various chipset units * @ah: The &struct ath5k_hw * @val: Mask to indicate what units to reset * * To reset the various chipset units we need to write * the mask to AR5K_RESET_CTL and poll the register until * all flags are cleared. * * Returns 0 if we are O.K. or -EAGAIN (from athk5_hw_register_timeout) */ static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val) { int ret; u32 mask = val ? val : ~0U; /* Read-and-clear RX Descriptor Pointer*/ ath5k_hw_reg_read(ah, AR5K_RXDP); /* * Reset the device and wait until success */ ath5k_hw_reg_write(ah, val, AR5K_RESET_CTL); /* Wait at least 128 PCI clocks */ usleep_range(15, 20); if (ah->ah_version == AR5K_AR5210) { val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY; mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY; } else { val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; } ret = ath5k_hw_register_timeout(ah, AR5K_RESET_CTL, mask, val, false); /* * Reset configuration register (for hw byte-swap). Note that this * is only set for big endian. We do the necessary magic in * AR5K_INIT_CFG. */ if ((val & AR5K_RESET_CTL_PCU) == 0) ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG); return ret; } /** * ath5k_hw_wisoc_reset() - Reset AHB chipset * @ah: The &struct ath5k_hw * @flags: Mask to indicate what units to reset * * Same as ath5k_hw_nic_reset but for AHB based devices * * Returns 0 if we are O.K. or -EAGAIN (from athk5_hw_register_timeout) */ static int ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags) { u32 mask = flags ? flags : ~0U; u32 __iomem *reg; u32 regval; u32 val = 0; /* ah->ah_mac_srev is not available at this point yet */ if (ah->devid >= AR5K_SREV_AR2315_R6) { reg = (u32 __iomem *) AR5K_AR2315_RESET; if (mask & AR5K_RESET_CTL_PCU) val |= AR5K_AR2315_RESET_WMAC; if (mask & AR5K_RESET_CTL_BASEBAND) val |= AR5K_AR2315_RESET_BB_WARM; } else { reg = (u32 __iomem *) AR5K_AR5312_RESET; if (to_platform_device(ah->dev)->id == 0) { if (mask & AR5K_RESET_CTL_PCU) val |= AR5K_AR5312_RESET_WMAC0; if (mask & AR5K_RESET_CTL_BASEBAND) val |= AR5K_AR5312_RESET_BB0_COLD | AR5K_AR5312_RESET_BB0_WARM; } else { if (mask & AR5K_RESET_CTL_PCU) val |= AR5K_AR5312_RESET_WMAC1; if (mask & AR5K_RESET_CTL_BASEBAND) val |= AR5K_AR5312_RESET_BB1_COLD | AR5K_AR5312_RESET_BB1_WARM; } } /* Put BB/MAC into reset */ regval = ioread32(reg); iowrite32(regval | val, reg); regval = ioread32(reg); usleep_range(100, 150); /* Bring BB/MAC out of reset */ iowrite32(regval & ~val, reg); regval = ioread32(reg); /* * Reset configuration register (for hw byte-swap). Note that this * is only set for big endian. We do the necessary magic in * AR5K_INIT_CFG. */ if ((flags & AR5K_RESET_CTL_PCU) == 0) ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG); return 0; } /** * ath5k_hw_set_power_mode() - Set power mode * @ah: The &struct ath5k_hw * @mode: One of enum ath5k_power_mode * @set_chip: Set to true to write sleep control register * @sleep_duration: How much time the device is allowed to sleep * when sleep logic is enabled (in 128 microsecond increments). * * This function is used to configure sleep policy and allowed * sleep modes. For more information check out the sleep control * register on reg.h and STA_ID1. * * Returns 0 on success, -EIO if chip didn't wake up or -EINVAL if an invalid * mode is requested. */ static int ath5k_hw_set_power_mode(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration) { unsigned int i; u32 staid, data; staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1); switch (mode) { case AR5K_PM_AUTO: staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA; /* fallthrough */ case AR5K_PM_NETWORK_SLEEP: if (set_chip) ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_ALLOW | sleep_duration, AR5K_SLEEP_CTL); staid |= AR5K_STA_ID1_PWR_SV; break; case AR5K_PM_FULL_SLEEP: if (set_chip) ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_SLP, AR5K_SLEEP_CTL); staid |= AR5K_STA_ID1_PWR_SV; break; case AR5K_PM_AWAKE: staid &= ~AR5K_STA_ID1_PWR_SV; if (!set_chip) goto commit; data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); /* If card is down we 'll get 0xffff... so we * need to clean this up before we write the register */ if (data & 0xffc00000) data = 0; else /* Preserve sleep duration etc */ data = data & ~AR5K_SLEEP_CTL_SLE; ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, AR5K_SLEEP_CTL); usleep_range(15, 20); for (i = 200; i > 0; i--) { /* Check if the chip did wake up */ if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & AR5K_PCICFG_SPWR_DN) == 0) break; /* Wait a bit and retry */ usleep_range(50, 75); ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, AR5K_SLEEP_CTL); } /* Fail if the chip didn't wake up */ if (i == 0) return -EIO; break; default: return -EINVAL; } commit: ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1); return 0; } /** * ath5k_hw_on_hold() - Put device on hold * @ah: The &struct ath5k_hw * * Put MAC and Baseband on warm reset and keep that state * (don't clean sleep control register). After this MAC * and Baseband are disabled and a full reset is needed * to come back. This way we save as much power as possible * without putting the card on full sleep. * * Returns 0 on success or -EIO on error */ int ath5k_hw_on_hold(struct ath5k_hw *ah) { struct pci_dev *pdev = ah->pdev; u32 bus_flags; int ret; if (ath5k_get_bus_type(ah) == ATH_AHB) return 0; /* Make sure device is awake */ ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n"); return ret; } /* * Put chipset on warm reset... * * Note: putting PCI core on warm reset on PCI-E cards * results card to hang and always return 0xffff... so * we ignore that flag for PCI-E cards. On PCI cards * this flag gets cleared after 64 PCI clocks. */ bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI; if (ah->ah_version == AR5K_AR5210) { ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); usleep_range(2000, 2500); } else { ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND | bus_flags); } if (ret) { ATH5K_ERR(ah, "failed to put device on warm reset\n"); return -EIO; } /* ...wakeup again!*/ ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to put device on hold\n"); return ret; } return ret; } /** * ath5k_hw_nic_wakeup() - Force card out of sleep * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * Bring up MAC + PHY Chips and program PLL * NOTE: Channel is NULL for the initial wakeup. * * Returns 0 on success, -EIO on hw failure or -EINVAL for false channel infos */ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct pci_dev *pdev = ah->pdev; u32 turbo, mode, clock, bus_flags; int ret; turbo = 0; mode = 0; clock = 0; if ((ath5k_get_bus_type(ah) != ATH_AHB) || channel) { /* Wakeup the device */ ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n"); return ret; } } /* * Put chipset on warm reset... * * Note: putting PCI core on warm reset on PCI-E cards * results card to hang and always return 0xffff... so * we ignore that flag for PCI-E cards. On PCI cards * this flag gets cleared after 64 PCI clocks. */ bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI; if (ah->ah_version == AR5K_AR5210) { ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); usleep_range(2000, 2500); } else { if (ath5k_get_bus_type(ah) == ATH_AHB) ret = ath5k_hw_wisoc_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND); else ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND | bus_flags); } if (ret) { ATH5K_ERR(ah, "failed to reset the MAC Chip\n"); return -EIO; } /* ...wakeup again!...*/ ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to resume the MAC Chip\n"); return ret; } /* ...reset configuration register on Wisoc ... * ...clear reset control register and pull device out of * warm reset on others */ if (ath5k_get_bus_type(ah) == ATH_AHB) ret = ath5k_hw_wisoc_reset(ah, 0); else ret = ath5k_hw_nic_reset(ah, 0); if (ret) { ATH5K_ERR(ah, "failed to warm reset the MAC Chip\n"); return -EIO; } /* On initialization skip PLL programming since we don't have * a channel / mode set yet */ if (!channel) return 0; if (ah->ah_version != AR5K_AR5210) { /* * Get channel mode flags */ if (ah->ah_radio >= AR5K_RF5112) { mode = AR5K_PHY_MODE_RAD_RF5112; clock = AR5K_PHY_PLL_RF5112; } else { mode = AR5K_PHY_MODE_RAD_RF5111; /*Zero*/ clock = AR5K_PHY_PLL_RF5111; /*Zero*/ } if (channel->band == IEEE80211_BAND_2GHZ) { mode |= AR5K_PHY_MODE_FREQ_2GHZ; clock |= AR5K_PHY_PLL_44MHZ; if (channel->hw_value == AR5K_MODE_11B) { mode |= AR5K_PHY_MODE_MOD_CCK; } else { /* XXX Dynamic OFDM/CCK is not supported by the * AR5211 so we set MOD_OFDM for plain g (no * CCK headers) operation. We need to test * this, 5211 might support ofdm-only g after * all, there are also initial register values * in the code for g mode (see initvals.c). */ if (ah->ah_version == AR5K_AR5211) mode |= AR5K_PHY_MODE_MOD_OFDM; else mode |= AR5K_PHY_MODE_MOD_DYN; } } else if (channel->band == IEEE80211_BAND_5GHZ) { mode |= (AR5K_PHY_MODE_FREQ_5GHZ | AR5K_PHY_MODE_MOD_OFDM); /* Different PLL setting for 5413 */ if (ah->ah_radio == AR5K_RF5413) clock = AR5K_PHY_PLL_40MHZ_5413; else clock |= AR5K_PHY_PLL_40MHZ; } else { ATH5K_ERR(ah, "invalid radio frequency mode\n"); return -EINVAL; } /*XXX: Can bwmode be used with dynamic mode ? * (I don't think it supports 44MHz) */ /* On 2425 initvals TURBO_SHORT is not present */ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) { turbo = AR5K_PHY_TURBO_MODE | (ah->ah_radio == AR5K_RF2425) ? 0 : AR5K_PHY_TURBO_SHORT; } else if (ah->ah_bwmode != AR5K_BWMODE_DEFAULT) { if (ah->ah_radio == AR5K_RF5413) { mode |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ? AR5K_PHY_MODE_HALF_RATE : AR5K_PHY_MODE_QUARTER_RATE; } else if (ah->ah_version == AR5K_AR5212) { clock |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ? AR5K_PHY_PLL_HALF_RATE : AR5K_PHY_PLL_QUARTER_RATE; } } } else { /* Reset the device */ /* ...enable Atheros turbo mode if requested */ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE, AR5K_PHY_TURBO); } if (ah->ah_version != AR5K_AR5210) { /* ...update PLL if needed */ if (ath5k_hw_reg_read(ah, AR5K_PHY_PLL) != clock) { ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL); usleep_range(300, 350); } /* ...set the PHY operating mode */ ath5k_hw_reg_write(ah, mode, AR5K_PHY_MODE); ath5k_hw_reg_write(ah, turbo, AR5K_PHY_TURBO); } return 0; } /**************************************\ * Post-initvals register modifications * \**************************************/ /** * ath5k_hw_tweak_initval_settings() - Tweak initial settings * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * Some settings are not handled on initvals, e.g. bwmode * settings, some phy settings, workarounds etc that in general * don't fit anywhere else or are too small to introduce a separate * function for each one. So we have this function to handle * them all during reset and complete card's initialization. */ static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah, struct ieee80211_channel *channel) { if (ah->ah_version == AR5K_AR5212 && ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { /* Setup ADC control */ ath5k_hw_reg_write(ah, (AR5K_REG_SM(2, AR5K_PHY_ADC_CTL_INBUFGAIN_OFF) | AR5K_REG_SM(2, AR5K_PHY_ADC_CTL_INBUFGAIN_ON) | AR5K_PHY_ADC_CTL_PWD_DAC_OFF | AR5K_PHY_ADC_CTL_PWD_ADC_OFF), AR5K_PHY_ADC_CTL); /* Disable barker RSSI threshold */ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_DAG_CCK_CTL, AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DAG_CCK_CTL, AR5K_PHY_DAG_CCK_CTL_RSSI_THR, 2); /* Set the mute mask */ ath5k_hw_reg_write(ah, 0x0000000f, AR5K_SEQ_MASK); } /* Clear PHY_BLUETOOTH to allow RX_CLEAR line debug */ if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212B) ath5k_hw_reg_write(ah, 0, AR5K_PHY_BLUETOOTH); /* Enable DCU double buffering */ if (ah->ah_phy_revision > AR5K_SREV_PHY_5212B) AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_DCU_DBL_BUF_DIS); /* Set fast ADC */ if ((ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF2317) || (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) { u32 fast_adc = true; if (channel->center_freq == 2462 || channel->center_freq == 2467) fast_adc = 0; /* Only update if needed */ if (ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ADC) != fast_adc) ath5k_hw_reg_write(ah, fast_adc, AR5K_PHY_FAST_ADC); } /* Fix for first revision of the RF5112 RF chipset */ if (ah->ah_radio == AR5K_RF5112 && ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) { u32 data; ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD, AR5K_PHY_CCKTXCTL); if (channel->band == IEEE80211_BAND_5GHZ) data = 0xffb81020; else data = 0xffb80d20; ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL); } if (ah->ah_mac_srev < AR5K_SREV_AR5211) { /* Clear QCU/DCU clock gating register */ ath5k_hw_reg_write(ah, 0, AR5K_QCUDCU_CLKGT); /* Set DAC/ADC delays */ ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ_5311, AR5K_PHY_SCAL); /* Enable PCU FIFO corruption ECO */ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211, AR5K_DIAG_SW_ECO_ENABLE); } if (ah->ah_bwmode) { /* Increase PHY switch and AGC settling time * on turbo mode (ath5k_hw_commit_eeprom_settings * will override settling time if available) */ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) { AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING, AR5K_PHY_SETTLING_AGC, AR5K_AGC_SETTLING_TURBO); /* XXX: Initvals indicate we only increase * switch time on AR5212, 5211 and 5210 * only change agc time (bug?) */ if (ah->ah_version == AR5K_AR5212) AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING, AR5K_PHY_SETTLING_SWITCH, AR5K_SWITCH_SETTLING_TURBO); if (ah->ah_version == AR5K_AR5210) { /* Set Frame Control Register */ ath5k_hw_reg_write(ah, (AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT | 0x2020), AR5K_PHY_FRAME_CTL_5210); } /* On 5413 PHY force window length for half/quarter rate*/ } else if ((ah->ah_mac_srev >= AR5K_SREV_AR5424) && (ah->ah_mac_srev <= AR5K_SREV_AR5414)) { AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL_5211, AR5K_PHY_FRAME_CTL_WIN_LEN, 3); } } else if (ah->ah_version == AR5K_AR5210) { /* Set Frame Control Register for normal operation */ ath5k_hw_reg_write(ah, (AR5K_PHY_FRAME_CTL_INI | 0x1020), AR5K_PHY_FRAME_CTL_5210); } } /** * ath5k_hw_commit_eeprom_settings() - Commit settings from EEPROM * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * Use settings stored on EEPROM to properly initialize the card * based on various infos and per-mode calibration data. */ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; s16 cck_ofdm_pwr_delta; u8 ee_mode; /* TODO: Add support for AR5210 EEPROM */ if (ah->ah_version == AR5K_AR5210) return; ee_mode = ath5k_eeprom_mode_from_channel(channel); /* Adjust power delta for channel 14 */ if (channel->center_freq == 2484) cck_ofdm_pwr_delta = ((ee->ee_cck_ofdm_power_delta - ee->ee_scaled_cck_delta) * 2) / 10; else cck_ofdm_pwr_delta = (ee->ee_cck_ofdm_power_delta * 2) / 10; /* Set CCK to OFDM power delta on tx power * adjustment register */ if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { if (channel->hw_value == AR5K_MODE_11G) ath5k_hw_reg_write(ah, AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1), AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) | AR5K_REG_SM((cck_ofdm_pwr_delta * -1), AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX), AR5K_PHY_TX_PWR_ADJ); else ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ); } else { /* For older revs we scale power on sw during tx power * setup */ ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta; ah->ah_txpower.txp_cck_ofdm_gainf_delta = ee->ee_cck_ofdm_gain_delta; } /* XXX: necessary here? is called from ath5k_hw_set_antenna_mode() * too */ ath5k_hw_set_antenna_switch(ah, ee_mode); /* Noise floor threshold */ ath5k_hw_reg_write(ah, AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]), AR5K_PHY_NFTHRES); if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) && (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0)) { /* Switch settling time (Turbo) */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING, AR5K_PHY_SETTLING_SWITCH, ee->ee_switch_settling_turbo[ee_mode]); /* Tx/Rx attenuation (Turbo) */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN, AR5K_PHY_GAIN_TXRX_ATTEN, ee->ee_atn_tx_rx_turbo[ee_mode]); /* ADC/PGA desired size (Turbo) */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, AR5K_PHY_DESIRED_SIZE_ADC, ee->ee_adc_desired_size_turbo[ee_mode]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, AR5K_PHY_DESIRED_SIZE_PGA, ee->ee_pga_desired_size_turbo[ee_mode]); /* Tx/Rx margin (Turbo) */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ, AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX, ee->ee_margin_tx_rx_turbo[ee_mode]); } else { /* Switch settling time */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING, AR5K_PHY_SETTLING_SWITCH, ee->ee_switch_settling[ee_mode]); /* Tx/Rx attenuation */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN, AR5K_PHY_GAIN_TXRX_ATTEN, ee->ee_atn_tx_rx[ee_mode]); /* ADC/PGA desired size */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, AR5K_PHY_DESIRED_SIZE_ADC, ee->ee_adc_desired_size[ee_mode]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, AR5K_PHY_DESIRED_SIZE_PGA, ee->ee_pga_desired_size[ee_mode]); /* Tx/Rx margin */ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ, AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX, ee->ee_margin_tx_rx[ee_mode]); } /* XPA delays */ ath5k_hw_reg_write(ah, (ee->ee_tx_end2xpa_disable[ee_mode] << 24) | (ee->ee_tx_end2xpa_disable[ee_mode] << 16) | (ee->ee_tx_frm2xpa_enable[ee_mode] << 8) | (ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY_RF_CTL4); /* XLNA delay */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL3, AR5K_PHY_RF_CTL3_TXE2XLNA_ON, ee->ee_tx_end2xlna_enable[ee_mode]); /* Thresh64 (ANI) */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_NF, AR5K_PHY_NF_THRESH62, ee->ee_thr_62[ee_mode]); /* False detect backoff for channels * that have spur noise. Write the new * cyclic power RSSI threshold. */ if (ath5k_hw_chan_has_spur_noise(ah, channel)) AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, AR5K_INIT_CYCRSSI_THR1 + ee->ee_false_detect[ee_mode]); else AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, AR5K_INIT_CYCRSSI_THR1); /* I/Q correction (set enable bit last to match HAL sources) */ /* TODO: Per channel i/q infos ? */ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, ee->ee_i_cal[ee_mode]); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, ee->ee_q_cal[ee_mode]); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE); } /* Heavy clipping -disable for now */ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_1) ath5k_hw_reg_write(ah, 0, AR5K_PHY_HEAVY_CLIP_ENABLE); } /*********************\ * Main reset function * \*********************/ /** * ath5k_hw_reset() - The main reset function * @ah: The &struct ath5k_hw * @op_mode: One of enum nl80211_iftype * @channel: The &struct ieee80211_channel * @fast: Enable fast channel switching * @skip_pcu: Skip pcu initialization * * This is the function we call each time we want to (re)initialize the * card and pass new settings to hw. We also call it when hw runs into * trouble to make it come back to a working state. * * Returns 0 on success, -EINVAL on false op_mode or channel infos, or -EIO * on failure. */ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool fast, bool skip_pcu) { u32 s_seq[10], s_led[3], tsf_up, tsf_lo; u8 mode; int i, ret; tsf_up = 0; tsf_lo = 0; mode = 0; /* * Sanity check for fast flag * Fast channel change only available * on AR2413/AR5413. */ if (fast && (ah->ah_radio != AR5K_RF2413) && (ah->ah_radio != AR5K_RF5413)) fast = false; /* Disable sleep clock operation * to avoid register access delay on certain * PHY registers */ if (ah->ah_version == AR5K_AR5212) ath5k_hw_set_sleep_clock(ah, false); /* * Stop PCU */ ath5k_hw_stop_rx_pcu(ah); /* * Stop DMA * * Note: If DMA didn't stop continue * since only a reset will fix it. */ ret = ath5k_hw_dma_stop(ah); /* RF Bus grant won't work if we have pending * frames */ if (ret && fast) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "DMA didn't stop, falling back to normal reset\n"); fast = false; /* Non fatal, just continue with * normal reset */ ret = 0; } mode = channel->hw_value; switch (mode) { case AR5K_MODE_11A: break; case AR5K_MODE_11G: if (ah->ah_version <= AR5K_AR5211) { ATH5K_ERR(ah, "G mode not available on 5210/5211"); return -EINVAL; } break; case AR5K_MODE_11B: if (ah->ah_version < AR5K_AR5211) { ATH5K_ERR(ah, "B mode not available on 5210"); return -EINVAL; } break; default: ATH5K_ERR(ah, "invalid channel: %d\n", channel->center_freq); return -EINVAL; } /* * If driver requested fast channel change and DMA has stopped * go on. If it fails continue with a normal reset. */ if (fast) { ret = ath5k_hw_phy_init(ah, channel, mode, true); if (ret) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "fast chan change failed, falling back to normal reset\n"); /* Non fatal, can happen eg. * on mode change */ ret = 0; } else { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "fast chan change successful\n"); return 0; } } /* * Save some registers before a reset */ if (ah->ah_version != AR5K_AR5210) { /* * Save frame sequence count * For revs. after Oahu, only save * seq num for DCU 0 (Global seq num) */ if (ah->ah_mac_srev < AR5K_SREV_AR5211) { for (i = 0; i < 10; i++) s_seq[i] = ath5k_hw_reg_read(ah, AR5K_QUEUE_DCU_SEQNUM(i)); } else { s_seq[0] = ath5k_hw_reg_read(ah, AR5K_QUEUE_DCU_SEQNUM(0)); } /* TSF accelerates on AR5211 during reset * As a workaround save it here and restore * it later so that it's back in time after * reset. This way it'll get re-synced on the * next beacon without breaking ad-hoc. * * On AR5212 TSF is almost preserved across a * reset so it stays back in time anyway and * we don't have to save/restore it. * * XXX: Since this breaks power saving we have * to disable power saving until we receive the * next beacon, so we can resync beacon timers */ if (ah->ah_version == AR5K_AR5211) { tsf_up = ath5k_hw_reg_read(ah, AR5K_TSF_U32); tsf_lo = ath5k_hw_reg_read(ah, AR5K_TSF_L32); } } /*GPIOs*/ s_led[0] = ath5k_hw_reg_read(ah, AR5K_PCICFG) & AR5K_PCICFG_LEDSTATE; s_led[1] = ath5k_hw_reg_read(ah, AR5K_GPIOCR); s_led[2] = ath5k_hw_reg_read(ah, AR5K_GPIODO); /* * Since we are going to write rf buffer * check if we have any pending gain_F * optimization settings */ if (ah->ah_version == AR5K_AR5212 && (ah->ah_radio <= AR5K_RF5112)) { if (!fast && ah->ah_rf_banks != NULL) ath5k_hw_gainf_calibrate(ah); } /* Wakeup the device */ ret = ath5k_hw_nic_wakeup(ah, channel); if (ret) return ret; /* PHY access enable */ if (ah->ah_mac_srev >= AR5K_SREV_AR5211) ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); else ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ | 0x40, AR5K_PHY(0)); /* Write initial settings */ ret = ath5k_hw_write_initvals(ah, mode, skip_pcu); if (ret) return ret; /* Initialize core clock settings */ ath5k_hw_init_core_clock(ah); /* * Tweak initval settings for revised * chipsets and add some more config * bits */ ath5k_hw_tweak_initval_settings(ah, channel); /* Commit values from EEPROM */ ath5k_hw_commit_eeprom_settings(ah, channel); /* * Restore saved values */ /* Seqnum, TSF */ if (ah->ah_version != AR5K_AR5210) { if (ah->ah_mac_srev < AR5K_SREV_AR5211) { for (i = 0; i < 10; i++) ath5k_hw_reg_write(ah, s_seq[i], AR5K_QUEUE_DCU_SEQNUM(i)); } else { ath5k_hw_reg_write(ah, s_seq[0], AR5K_QUEUE_DCU_SEQNUM(0)); } if (ah->ah_version == AR5K_AR5211) { ath5k_hw_reg_write(ah, tsf_up, AR5K_TSF_U32); ath5k_hw_reg_write(ah, tsf_lo, AR5K_TSF_L32); } } /* Ledstate */ AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, s_led[0]); /* Gpio settings */ ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR); ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO); /* * Initialize PCU */ ath5k_hw_pcu_init(ah, op_mode); /* * Initialize PHY */ ret = ath5k_hw_phy_init(ah, channel, mode, false); if (ret) { ATH5K_ERR(ah, "failed to initialize PHY (%i) !\n", ret); return ret; } /* * Configure QCUs/DCUs */ ret = ath5k_hw_init_queues(ah); if (ret) return ret; /* * Initialize DMA/Interrupts */ ath5k_hw_dma_init(ah); /* * Enable 32KHz clock function for AR5212+ chips * Set clocks to 32KHz operation and use an * external 32KHz crystal when sleeping if one * exists. * Disabled by default because it is also disabled in * other drivers and it is known to cause stability * issues on some devices */ if (ah->ah_use_32khz_clock && ah->ah_version == AR5K_AR5212 && op_mode != NL80211_IFTYPE_AP) ath5k_hw_set_sleep_clock(ah, true); /* * Disable beacons and reset the TSF */ AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); ath5k_hw_reset_tsf(ah); return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/phy.c0000644000175000017500000033036412026211315023226 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2007 Reyk Floeter * Copyright (c) 2006-2009 Nick Kossifidis * Copyright (c) 2007-2008 Jiri Slaby * Copyright (c) 2008-2009 Felix Fietkau * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /***********************\ * PHY related functions * \***********************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include "ath5k.h" #include "reg.h" #include "rfbuffer.h" #include "rfgain.h" #include "../regd.h" /** * DOC: PHY related functions * * Here we handle the low-level functions related to baseband * and analog frontend (RF) parts. This is by far the most complex * part of the hw code so make sure you know what you are doing. * * Here is a list of what this is all about: * * - Channel setting/switching * * - Automatic Gain Control (AGC) calibration * * - Noise Floor calibration * * - I/Q imbalance calibration (QAM correction) * * - Calibration due to thermal changes (gain_F) * * - Spur noise mitigation * * - RF/PHY initialization for the various operating modes and bwmodes * * - Antenna control * * - TX power control per channel/rate/packet type * * Also have in mind we never got documentation for most of these * functions, what we have comes mostly from Atheros's code, reverse * engineering and patent docs/presentations etc. */ /******************\ * Helper functions * \******************/ /** * ath5k_hw_radio_revision() - Get the PHY Chip revision * @ah: The &struct ath5k_hw * @band: One of enum ieee80211_band * * Returns the revision number of a 2GHz, 5GHz or single chip * radio. */ u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band) { unsigned int i; u32 srev; u16 ret; /* * Set the radio chip access register */ switch (band) { case IEEE80211_BAND_2GHZ: ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0)); break; case IEEE80211_BAND_5GHZ: ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); break; default: return 0; } usleep_range(2000, 2500); /* ...wait until PHY is ready and read the selected radio revision */ ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34)); for (i = 0; i < 8; i++) ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20)); if (ah->ah_version == AR5K_AR5210) { srev = ath5k_hw_reg_read(ah, AR5K_PHY(256) >> 28) & 0xf; ret = (u16)ath5k_hw_bitswap(srev, 4) + 1; } else { srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff; ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) | ((srev & 0x0f) << 4), 8); } /* Reset to the 5GHz mode */ ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); return ret; } /** * ath5k_channel_ok() - Check if a channel is supported by the hw * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * Note: We don't do any regulatory domain checks here, it's just * a sanity check. */ bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u16 freq = channel->center_freq; /* Check if the channel is in our supported range */ if (channel->band == IEEE80211_BAND_2GHZ) { if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) && (freq <= ah->ah_capabilities.cap_range.range_2ghz_max)) return true; } else if (channel->band == IEEE80211_BAND_5GHZ) if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) && (freq <= ah->ah_capabilities.cap_range.range_5ghz_max)) return true; return false; } /** * ath5k_hw_chan_has_spur_noise() - Check if channel is sensitive to spur noise * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel */ bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u8 refclk_freq; if ((ah->ah_radio == AR5K_RF5112) || (ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF2413) || (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) refclk_freq = 40; else refclk_freq = 32; if ((channel->center_freq % refclk_freq != 0) && ((channel->center_freq % refclk_freq < 10) || (channel->center_freq % refclk_freq > 22))) return true; else return false; } /** * ath5k_hw_rfb_op() - Perform an operation on the given RF Buffer * @ah: The &struct ath5k_hw * @rf_regs: The struct ath5k_rf_reg * @val: New value * @reg_id: RF register ID * @set: Indicate we need to swap data * * This is an internal function used to modify RF Banks before * writing them to AR5K_RF_BUFFER. Check out rfbuffer.h for more * infos. */ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs, u32 val, u8 reg_id, bool set) { const struct ath5k_rf_reg *rfreg = NULL; u8 offset, bank, num_bits, col, position; u16 entry; u32 mask, data, last_bit, bits_shifted, first_bit; u32 *rfb; s32 bits_left; int i; data = 0; rfb = ah->ah_rf_banks; for (i = 0; i < ah->ah_rf_regs_count; i++) { if (rf_regs[i].index == reg_id) { rfreg = &rf_regs[i]; break; } } if (rfb == NULL || rfreg == NULL) { ATH5K_PRINTF("Rf register not found!\n"); /* should not happen */ return 0; } bank = rfreg->bank; num_bits = rfreg->field.len; first_bit = rfreg->field.pos; col = rfreg->field.col; /* first_bit is an offset from bank's * start. Since we have all banks on * the same array, we use this offset * to mark each bank's start */ offset = ah->ah_offset[bank]; /* Boundary check */ if (!(col <= 3 && num_bits <= 32 && first_bit + num_bits <= 319)) { ATH5K_PRINTF("invalid values at offset %u\n", offset); return 0; } entry = ((first_bit - 1) / 8) + offset; position = (first_bit - 1) % 8; if (set) data = ath5k_hw_bitswap(val, num_bits); for (bits_shifted = 0, bits_left = num_bits; bits_left > 0; position = 0, entry++) { last_bit = (position + bits_left > 8) ? 8 : position + bits_left; mask = (((1 << last_bit) - 1) ^ ((1 << position) - 1)) << (col * 8); if (set) { rfb[entry] &= ~mask; rfb[entry] |= ((data << position) << (col * 8)) & mask; data >>= (8 - position); } else { data |= (((rfb[entry] & mask) >> (col * 8)) >> position) << bits_shifted; bits_shifted += last_bit - position; } bits_left -= 8 - position; } data = set ? 1 : ath5k_hw_bitswap(data, num_bits); return data; } /** * ath5k_hw_write_ofdm_timings() - set OFDM timings on AR5212 * @ah: the &struct ath5k_hw * @channel: the currently set channel upon reset * * Write the delta slope coefficient (used on pilot tracking ?) for OFDM * operation on the AR5212 upon reset. This is a helper for ath5k_hw_phy_init. * * Since delta slope is floating point we split it on its exponent and * mantissa and provide these values on hw. * * For more infos i think this patent is related * "http://www.freepatentsonline.com/7184495.html" */ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, struct ieee80211_channel *channel) { /* Get exponent and mantissa and set it */ u32 coef_scaled, coef_exp, coef_man, ds_coef_exp, ds_coef_man, clock; BUG_ON(!(ah->ah_version == AR5K_AR5212) || (channel->hw_value == AR5K_MODE_11B)); /* Get coefficient * ALGO: coef = (5 * clock / carrier_freq) / 2 * we scale coef by shifting clock value by 24 for * better precision since we use integers */ switch (ah->ah_bwmode) { case AR5K_BWMODE_40MHZ: clock = 40 * 2; break; case AR5K_BWMODE_10MHZ: clock = 40 / 2; break; case AR5K_BWMODE_5MHZ: clock = 40 / 4; break; default: clock = 40; break; } coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq; /* Get exponent * ALGO: coef_exp = 14 - highest set bit position */ coef_exp = ilog2(coef_scaled); /* Doesn't make sense if it's zero*/ if (!coef_scaled || !coef_exp) return -EINVAL; /* Note: we've shifted coef_scaled by 24 */ coef_exp = 14 - (coef_exp - 24); /* Get mantissa (significant digits) * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */ coef_man = coef_scaled + (1 << (24 - coef_exp - 1)); /* Calculate delta slope coefficient exponent * and mantissa (remove scaling) and set them on hw */ ds_coef_man = coef_man >> (24 - coef_exp); ds_coef_exp = coef_exp - 16; AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp); return 0; } /** * ath5k_hw_phy_disable() - Disable PHY * @ah: The &struct ath5k_hw */ int ath5k_hw_phy_disable(struct ath5k_hw *ah) { /*Just a try M.F.*/ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); return 0; } /** * ath5k_hw_wait_for_synth() - Wait for synth to settle * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel */ static void ath5k_hw_wait_for_synth(struct ath5k_hw *ah, struct ieee80211_channel *channel) { /* * On 5211+ read activation -> rx delay * and use it (100ns steps). */ if (ah->ah_version != AR5K_AR5210) { u32 delay; delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) & AR5K_PHY_RX_DELAY_M; delay = (channel->hw_value == AR5K_MODE_11B) ? ((delay << 2) / 22) : (delay / 10); if (ah->ah_bwmode == AR5K_BWMODE_10MHZ) delay = delay << 1; if (ah->ah_bwmode == AR5K_BWMODE_5MHZ) delay = delay << 2; /* XXX: /2 on turbo ? Let's be safe * for now */ usleep_range(100 + delay, 100 + (2 * delay)); } else { usleep_range(1000, 1500); } } /**********************\ * RF Gain optimization * \**********************/ /** * DOC: RF Gain optimization * * This code is used to optimize RF gain on different environments * (temperature mostly) based on feedback from a power detector. * * It's only used on RF5111 and RF5112, later RF chips seem to have * auto adjustment on hw -notice they have a much smaller BANK 7 and * no gain optimization ladder-. * * For more infos check out this patent doc * "http://www.freepatentsonline.com/7400691.html" * * This paper describes power drops as seen on the receiver due to * probe packets * "http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues * %20of%20Power%20Control.pdf" * * And this is the MadWiFi bug entry related to the above * "http://madwifi-project.org/ticket/1659" * with various measurements and diagrams */ /** * ath5k_hw_rfgain_opt_init() - Initialize ah_gain during attach * @ah: The &struct ath5k_hw */ int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah) { /* Initialize the gain optimization values */ switch (ah->ah_radio) { case AR5K_RF5111: ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default; ah->ah_gain.g_low = 20; ah->ah_gain.g_high = 35; ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; break; case AR5K_RF5112: ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default; ah->ah_gain.g_low = 20; ah->ah_gain.g_high = 85; ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; break; default: return -EINVAL; } return 0; } /** * ath5k_hw_request_rfgain_probe() - Request a PAPD probe packet * @ah: The &struct ath5k_hw * * Schedules a gain probe check on the next transmitted packet. * That means our next packet is going to be sent with lower * tx power and a Peak to Average Power Detector (PAPD) will try * to measure the gain. * * TODO: Force a tx packet (bypassing PCU arbitrator etc) * just after we enable the probe so that we don't mess with * standard traffic. */ static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) { /* Skip if gain calibration is inactive or * we already handle a probe request */ if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE) return; /* Send the packet with 2dB below max power as * patent doc suggest */ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4, AR5K_PHY_PAPD_PROBE_TXPOWER) | AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE); ah->ah_gain.g_state = AR5K_RFGAIN_READ_REQUESTED; } /** * ath5k_hw_rf_gainf_corr() - Calculate Gain_F measurement correction * @ah: The &struct ath5k_hw * * Calculate Gain_F measurement correction * based on the current step for RF5112 rev. 2 */ static u32 ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah) { u32 mix, step; u32 *rf; const struct ath5k_gain_opt *go; const struct ath5k_gain_opt_step *g_step; const struct ath5k_rf_reg *rf_regs; /* Only RF5112 Rev. 2 supports it */ if ((ah->ah_radio != AR5K_RF5112) || (ah->ah_radio_5ghz_revision <= AR5K_SREV_RAD_5112A)) return 0; go = &rfgain_opt_5112; rf_regs = rf_regs_5112a; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a); g_step = &go->go_step[ah->ah_gain.g_step_idx]; if (ah->ah_rf_banks == NULL) return 0; rf = ah->ah_rf_banks; ah->ah_gain.g_f_corr = 0; /* No VGA (Variable Gain Amplifier) override, skip */ if (ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, false) != 1) return 0; /* Mix gain stepping */ step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXGAIN_STEP, false); /* Mix gain override */ mix = g_step->gos_param[0]; switch (mix) { case 3: ah->ah_gain.g_f_corr = step * 2; break; case 2: ah->ah_gain.g_f_corr = (step - 5) * 2; break; case 1: ah->ah_gain.g_f_corr = step; break; default: ah->ah_gain.g_f_corr = 0; break; } return ah->ah_gain.g_f_corr; } /** * ath5k_hw_rf_check_gainf_readback() - Validate Gain_F feedback from detector * @ah: The &struct ath5k_hw * * Check if current gain_F measurement is in the range of our * power detector windows. If we get a measurement outside range * we know it's not accurate (detectors can't measure anything outside * their detection window) so we must ignore it. * * Returns true if readback was O.K. or false on failure */ static bool ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah) { const struct ath5k_rf_reg *rf_regs; u32 step, mix_ovr, level[4]; u32 *rf; if (ah->ah_rf_banks == NULL) return false; rf = ah->ah_rf_banks; if (ah->ah_radio == AR5K_RF5111) { rf_regs = rf_regs_5111; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111); step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_RFGAIN_STEP, false); level[0] = 0; level[1] = (step == 63) ? 50 : step + 4; level[2] = (step != 63) ? 64 : level[0]; level[3] = level[2] + 50; ah->ah_gain.g_high = level[3] - (step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5); ah->ah_gain.g_low = level[0] + (step == 63 ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0); } else { rf_regs = rf_regs_5112; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112); mix_ovr = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, false); level[0] = level[2] = 0; if (mix_ovr == 1) { level[1] = level[3] = 83; } else { level[1] = level[3] = 107; ah->ah_gain.g_high = 55; } } return (ah->ah_gain.g_current >= level[0] && ah->ah_gain.g_current <= level[1]) || (ah->ah_gain.g_current >= level[2] && ah->ah_gain.g_current <= level[3]); } /** * ath5k_hw_rf_gainf_adjust() - Perform Gain_F adjustment * @ah: The &struct ath5k_hw * * Choose the right target gain based on current gain * and RF gain optimization ladder */ static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah) { const struct ath5k_gain_opt *go; const struct ath5k_gain_opt_step *g_step; int ret = 0; switch (ah->ah_radio) { case AR5K_RF5111: go = &rfgain_opt_5111; break; case AR5K_RF5112: go = &rfgain_opt_5112; break; default: return 0; } g_step = &go->go_step[ah->ah_gain.g_step_idx]; if (ah->ah_gain.g_current >= ah->ah_gain.g_high) { /* Reached maximum */ if (ah->ah_gain.g_step_idx == 0) return -1; for (ah->ah_gain.g_target = ah->ah_gain.g_current; ah->ah_gain.g_target >= ah->ah_gain.g_high && ah->ah_gain.g_step_idx > 0; g_step = &go->go_step[ah->ah_gain.g_step_idx]) ah->ah_gain.g_target -= 2 * (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain - g_step->gos_gain); ret = 1; goto done; } if (ah->ah_gain.g_current <= ah->ah_gain.g_low) { /* Reached minimum */ if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1)) return -2; for (ah->ah_gain.g_target = ah->ah_gain.g_current; ah->ah_gain.g_target <= ah->ah_gain.g_low && ah->ah_gain.g_step_idx < go->go_steps_count - 1; g_step = &go->go_step[ah->ah_gain.g_step_idx]) ah->ah_gain.g_target -= 2 * (go->go_step[++ah->ah_gain.g_step_idx].gos_gain - g_step->gos_gain); ret = 2; goto done; } done: ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "ret %d, gain step %u, current gain %u, target gain %u\n", ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current, ah->ah_gain.g_target); return ret; } /** * ath5k_hw_gainf_calibrate() - Do a gain_F calibration * @ah: The &struct ath5k_hw * * Main callback for thermal RF gain calibration engine * Check for a new gain reading and schedule an adjustment * if needed. * * Returns one of enum ath5k_rfgain codes */ enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah) { u32 data, type; struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; if (ah->ah_rf_banks == NULL || ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE) return AR5K_RFGAIN_INACTIVE; /* No check requested, either engine is inactive * or an adjustment is already requested */ if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED) goto done; /* Read the PAPD (Peak to Average Power Detector) * register */ data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE); /* No probe is scheduled, read gain_F measurement */ if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) { ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S; type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE); /* If tx packet is CCK correct the gain_F measurement * by cck ofdm gain delta */ if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) { if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) ah->ah_gain.g_current += ee->ee_cck_ofdm_gain_delta; else ah->ah_gain.g_current += AR5K_GAIN_CCK_PROBE_CORR; } /* Further correct gain_F measurement for * RF5112A radios */ if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { ath5k_hw_rf_gainf_corr(ah); ah->ah_gain.g_current = ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ? (ah->ah_gain.g_current - ah->ah_gain.g_f_corr) : 0; } /* Check if measurement is ok and if we need * to adjust gain, schedule a gain adjustment, * else switch back to the active state */ if (ath5k_hw_rf_check_gainf_readback(ah) && AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) && ath5k_hw_rf_gainf_adjust(ah)) { ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE; } else { ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; } } done: return ah->ah_gain.g_state; } /** * ath5k_hw_rfgain_init() - Write initial RF gain settings to hw * @ah: The &struct ath5k_hw * @band: One of enum ieee80211_band * * Write initial RF gain table to set the RF sensitivity. * * NOTE: This one works on all RF chips and has nothing to do * with Gain_F calibration */ static int ath5k_hw_rfgain_init(struct ath5k_hw *ah, enum ieee80211_band band) { const struct ath5k_ini_rfgain *ath5k_rfg; unsigned int i, size, index; switch (ah->ah_radio) { case AR5K_RF5111: ath5k_rfg = rfgain_5111; size = ARRAY_SIZE(rfgain_5111); break; case AR5K_RF5112: ath5k_rfg = rfgain_5112; size = ARRAY_SIZE(rfgain_5112); break; case AR5K_RF2413: ath5k_rfg = rfgain_2413; size = ARRAY_SIZE(rfgain_2413); break; case AR5K_RF2316: ath5k_rfg = rfgain_2316; size = ARRAY_SIZE(rfgain_2316); break; case AR5K_RF5413: ath5k_rfg = rfgain_5413; size = ARRAY_SIZE(rfgain_5413); break; case AR5K_RF2317: case AR5K_RF2425: ath5k_rfg = rfgain_2425; size = ARRAY_SIZE(rfgain_2425); break; default: return -EINVAL; } index = (band == IEEE80211_BAND_2GHZ) ? 1 : 0; for (i = 0; i < size; i++) { AR5K_REG_WAIT(i); ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[index], (u32)ath5k_rfg[i].rfg_register); } return 0; } /********************\ * RF Registers setup * \********************/ /** * ath5k_hw_rfregs_init() - Initialize RF register settings * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * @mode: One of enum ath5k_driver_mode * * Setup RF registers by writing RF buffer on hw. For * more infos on this, check out rfbuffer.h */ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, unsigned int mode) { const struct ath5k_rf_reg *rf_regs; const struct ath5k_ini_rfbuffer *ini_rfb; const struct ath5k_gain_opt *go = NULL; const struct ath5k_gain_opt_step *g_step; struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u8 ee_mode = 0; u32 *rfb; int i, obdb = -1, bank = -1; switch (ah->ah_radio) { case AR5K_RF5111: rf_regs = rf_regs_5111; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111); ini_rfb = rfb_5111; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5111); go = &rfgain_opt_5111; break; case AR5K_RF5112: if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { rf_regs = rf_regs_5112a; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a); ini_rfb = rfb_5112a; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112a); } else { rf_regs = rf_regs_5112; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112); ini_rfb = rfb_5112; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112); } go = &rfgain_opt_5112; break; case AR5K_RF2413: rf_regs = rf_regs_2413; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2413); ini_rfb = rfb_2413; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2413); break; case AR5K_RF2316: rf_regs = rf_regs_2316; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2316); ini_rfb = rfb_2316; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2316); break; case AR5K_RF5413: rf_regs = rf_regs_5413; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5413); ini_rfb = rfb_5413; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5413); break; case AR5K_RF2317: rf_regs = rf_regs_2425; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425); ini_rfb = rfb_2317; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2317); break; case AR5K_RF2425: rf_regs = rf_regs_2425; ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425); if (ah->ah_mac_srev < AR5K_SREV_AR2417) { ini_rfb = rfb_2425; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2425); } else { ini_rfb = rfb_2417; ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2417); } break; default: return -EINVAL; } /* If it's the first time we set RF buffer, allocate * ah->ah_rf_banks based on ah->ah_rf_banks_size * we set above */ if (ah->ah_rf_banks == NULL) { ah->ah_rf_banks = kmalloc(sizeof(u32) * ah->ah_rf_banks_size, GFP_KERNEL); if (ah->ah_rf_banks == NULL) { ATH5K_ERR(ah, "out of memory\n"); return -ENOMEM; } } /* Copy values to modify them */ rfb = ah->ah_rf_banks; for (i = 0; i < ah->ah_rf_banks_size; i++) { if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) { ATH5K_ERR(ah, "invalid bank\n"); return -EINVAL; } /* Bank changed, write down the offset */ if (bank != ini_rfb[i].rfb_bank) { bank = ini_rfb[i].rfb_bank; ah->ah_offset[bank] = i; } rfb[i] = ini_rfb[i].rfb_mode_data[mode]; } /* Set Output and Driver bias current (OB/DB) */ if (channel->band == IEEE80211_BAND_2GHZ) { if (channel->hw_value == AR5K_MODE_11B) ee_mode = AR5K_EEPROM_MODE_11B; else ee_mode = AR5K_EEPROM_MODE_11G; /* For RF511X/RF211X combination we * use b_OB and b_DB parameters stored * in eeprom on ee->ee_ob[ee_mode][0] * * For all other chips we use OB/DB for 2GHz * stored in the b/g modal section just like * 802.11a on ee->ee_ob[ee_mode][1] */ if ((ah->ah_radio == AR5K_RF5111) || (ah->ah_radio == AR5K_RF5112)) obdb = 0; else obdb = 1; ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb], AR5K_RF_OB_2GHZ, true); ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb], AR5K_RF_DB_2GHZ, true); /* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */ } else if ((channel->band == IEEE80211_BAND_5GHZ) || (ah->ah_radio == AR5K_RF5111)) { /* For 11a, Turbo and XR we need to choose * OB/DB based on frequency range */ ee_mode = AR5K_EEPROM_MODE_11A; obdb = channel->center_freq >= 5725 ? 3 : (channel->center_freq >= 5500 ? 2 : (channel->center_freq >= 5260 ? 1 : (channel->center_freq > 4000 ? 0 : -1))); if (obdb < 0) return -EINVAL; ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb], AR5K_RF_OB_5GHZ, true); ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb], AR5K_RF_DB_5GHZ, true); } g_step = &go->go_step[ah->ah_gain.g_step_idx]; /* Set turbo mode (N/A on RF5413) */ if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) && (ah->ah_radio != AR5K_RF5413)) ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_TURBO, false); /* Bank Modifications (chip-specific) */ if (ah->ah_radio == AR5K_RF5111) { /* Set gain_F settings according to current step */ if (channel->hw_value != AR5K_MODE_11B) { AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL, AR5K_PHY_FRAME_CTL_TX_CLIP, g_step->gos_param[0]); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1], AR5K_RF_PWD_90, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2], AR5K_RF_PWD_84, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3], AR5K_RF_RFGAIN_SEL, true); /* We programmed gain_F parameters, switch back * to active state */ ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; } /* Bank 6/7 setup */ ath5k_hw_rfb_op(ah, rf_regs, !ee->ee_xpd[ee_mode], AR5K_RF_PWD_XPD, true); ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode], AR5K_RF_XPD_GAIN, true); ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode], AR5K_RF_GAIN_I, true); ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode], AR5K_RF_PLO_SEL, true); /* Tweak power detectors for half/quarter rate support */ if (ah->ah_bwmode == AR5K_BWMODE_5MHZ || ah->ah_bwmode == AR5K_BWMODE_10MHZ) { u8 wait_i; ath5k_hw_rfb_op(ah, rf_regs, 0x1f, AR5K_RF_WAIT_S, true); wait_i = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ? 0x1f : 0x10; ath5k_hw_rfb_op(ah, rf_regs, wait_i, AR5K_RF_WAIT_I, true); ath5k_hw_rfb_op(ah, rf_regs, 3, AR5K_RF_MAX_TIME, true); } } if (ah->ah_radio == AR5K_RF5112) { /* Set gain_F settings according to current step */ if (channel->hw_value != AR5K_MODE_11B) { ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0], AR5K_RF_MIXGAIN_OVR, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1], AR5K_RF_PWD_138, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2], AR5K_RF_PWD_137, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3], AR5K_RF_PWD_136, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[4], AR5K_RF_PWD_132, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[5], AR5K_RF_PWD_131, true); ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[6], AR5K_RF_PWD_130, true); /* We programmed gain_F parameters, switch back * to active state */ ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; } /* Bank 6/7 setup */ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode], AR5K_RF_XPD_SEL, true); if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) { /* Rev. 1 supports only one xpd */ ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode], AR5K_RF_XPD_GAIN, true); } else { u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode]; if (ee->ee_pd_gains[ee_mode] > 1) { ath5k_hw_rfb_op(ah, rf_regs, pdg_curve_to_idx[0], AR5K_RF_PD_GAIN_LO, true); ath5k_hw_rfb_op(ah, rf_regs, pdg_curve_to_idx[1], AR5K_RF_PD_GAIN_HI, true); } else { ath5k_hw_rfb_op(ah, rf_regs, pdg_curve_to_idx[0], AR5K_RF_PD_GAIN_LO, true); ath5k_hw_rfb_op(ah, rf_regs, pdg_curve_to_idx[0], AR5K_RF_PD_GAIN_HI, true); } /* Lower synth voltage on Rev 2 */ if (ah->ah_radio == AR5K_RF5112 && (ah->ah_radio_5ghz_revision & AR5K_SREV_REV) > 0) { ath5k_hw_rfb_op(ah, rf_regs, 2, AR5K_RF_HIGH_VC_CP, true); ath5k_hw_rfb_op(ah, rf_regs, 2, AR5K_RF_MID_VC_CP, true); ath5k_hw_rfb_op(ah, rf_regs, 2, AR5K_RF_LOW_VC_CP, true); ath5k_hw_rfb_op(ah, rf_regs, 2, AR5K_RF_PUSH_UP, true); } /* Decrease power consumption on 5213+ BaseBand */ if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_PAD2GND, true); ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_XB2_LVL, true); ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_XB5_LVL, true); ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_PWD_167, true); ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_PWD_166, true); } } ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode], AR5K_RF_GAIN_I, true); /* Tweak power detector for half/quarter rates */ if (ah->ah_bwmode == AR5K_BWMODE_5MHZ || ah->ah_bwmode == AR5K_BWMODE_10MHZ) { u8 pd_delay; pd_delay = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ? 0xf : 0x8; ath5k_hw_rfb_op(ah, rf_regs, pd_delay, AR5K_RF_PD_PERIOD_A, true); ath5k_hw_rfb_op(ah, rf_regs, 0xf, AR5K_RF_PD_DELAY_A, true); } } if (ah->ah_radio == AR5K_RF5413 && channel->band == IEEE80211_BAND_2GHZ) { ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE, true); /* Set optimum value for early revisions (on pci-e chips) */ if (ah->ah_mac_srev >= AR5K_SREV_AR5424 && ah->ah_mac_srev < AR5K_SREV_AR5413) ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3), AR5K_RF_PWD_ICLOBUF_2G, true); } /* Write RF banks on hw */ for (i = 0; i < ah->ah_rf_banks_size; i++) { AR5K_REG_WAIT(i); ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register); } return 0; } /**************************\ PHY/RF channel functions \**************************/ /** * ath5k_hw_rf5110_chan2athchan() - Convert channel freq on RF5110 * @channel: The &struct ieee80211_channel * * Map channel frequency to IEEE channel number and convert it * to an internal channel value used by the RF5110 chipset. */ static u32 ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) { u32 athchan; athchan = (ath5k_hw_bitswap( (ieee80211_frequency_to_channel( channel->center_freq) - 24) / 2, 5) << 1) | (1 << 6) | 0x1; return athchan; } /** * ath5k_hw_rf5110_channel() - Set channel frequency on RF5110 * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel */ static int ath5k_hw_rf5110_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 data; /* * Set the channel and wait */ data = ath5k_hw_rf5110_chan2athchan(channel); ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER); ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0); usleep_range(1000, 1500); return 0; } /** * ath5k_hw_rf5111_chan2athchan() - Handle 2GHz channels on RF5111/2111 * @ieee: IEEE channel number * @athchan: The &struct ath5k_athchan_2ghz * * In order to enable the RF2111 frequency converter on RF5111/2111 setups * we need to add some offsets and extra flags to the data values we pass * on to the PHY. So for every 2GHz channel this function gets called * to do the conversion. */ static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee, struct ath5k_athchan_2ghz *athchan) { int channel; /* Cast this value to catch negative channel numbers (>= -19) */ channel = (int)ieee; /* * Map 2GHz IEEE channel to 5GHz Atheros channel */ if (channel <= 13) { athchan->a2_athchan = 115 + channel; athchan->a2_flags = 0x46; } else if (channel == 14) { athchan->a2_athchan = 124; athchan->a2_flags = 0x44; } else if (channel >= 15 && channel <= 26) { athchan->a2_athchan = ((channel - 14) * 4) + 132; athchan->a2_flags = 0x46; } else return -EINVAL; return 0; } /** * ath5k_hw_rf5111_channel() - Set channel frequency on RF5111/2111 * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel */ static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct ath5k_athchan_2ghz ath5k_channel_2ghz; unsigned int ath5k_channel = ieee80211_frequency_to_channel(channel->center_freq); u32 data0, data1, clock; int ret; /* * Set the channel on the RF5111 radio */ data0 = data1 = 0; if (channel->band == IEEE80211_BAND_2GHZ) { /* Map 2GHz channel to 5GHz Atheros channel ID */ ret = ath5k_hw_rf5111_chan2athchan( ieee80211_frequency_to_channel(channel->center_freq), &ath5k_channel_2ghz); if (ret) return ret; ath5k_channel = ath5k_channel_2ghz.a2_athchan; data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff) << 5) | (1 << 4); } if (ath5k_channel < 145 || !(ath5k_channel & 1)) { clock = 1; data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) | (clock << 1) | (1 << 10) | 1; } else { clock = 0; data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff) << 2) | (clock << 1) | (1 << 10) | 1; } ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8), AR5K_RF_BUFFER); ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00), AR5K_RF_BUFFER_CONTROL_3); return 0; } /** * ath5k_hw_rf5112_channel() - Set channel frequency on 5112 and newer * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * On RF5112/2112 and newer we don't need to do any conversion. * We pass the frequency value after a few modifications to the * chip directly. * * NOTE: Make sure channel frequency given is within our range or else * we might damage the chip ! Use ath5k_channel_ok before calling this one. */ static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 data, data0, data1, data2; u16 c; data = data0 = data1 = data2 = 0; c = channel->center_freq; /* My guess based on code: * 2GHz RF has 2 synth modes, one with a Local Oscillator * at 2224Hz and one with a LO at 2192Hz. IF is 1520Hz * (3040/2). data0 is used to set the PLL divider and data1 * selects synth mode. */ if (c < 4800) { /* Channel 14 and all frequencies with 2Hz spacing * below/above (non-standard channels) */ if (!((c - 2224) % 5)) { /* Same as (c - 2224) / 5 */ data0 = ((2 * (c - 704)) - 3040) / 10; data1 = 1; /* Channel 1 and all frequencies with 5Hz spacing * below/above (standard channels without channel 14) */ } else if (!((c - 2192) % 5)) { /* Same as (c - 2192) / 5 */ data0 = ((2 * (c - 672)) - 3040) / 10; data1 = 0; } else return -EINVAL; data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8); /* This is more complex, we have a single synthesizer with * 4 reference clock settings (?) based on frequency spacing * and set using data2. LO is at 4800Hz and data0 is again used * to set some divider. * * NOTE: There is an old atheros presentation at Stanford * that mentions a method called dual direct conversion * with 1GHz sliding IF for RF5110. Maybe that's what we * have here, or an updated version. */ } else if ((c % 5) != 2 || c > 5435) { if (!(c % 20) && c >= 5120) { data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); data2 = ath5k_hw_bitswap(3, 2); } else if (!(c % 10)) { data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); data2 = ath5k_hw_bitswap(2, 2); } else if (!(c % 5)) { data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); data2 = ath5k_hw_bitswap(1, 2); } else return -EINVAL; } else { data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8); data2 = ath5k_hw_bitswap(0, 2); } data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001; ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); return 0; } /** * ath5k_hw_rf2425_channel() - Set channel frequency on RF2425 * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * AR2425/2417 have a different 2GHz RF so code changes * a little bit from RF5112. */ static int ath5k_hw_rf2425_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 data, data0, data2; u16 c; data = data0 = data2 = 0; c = channel->center_freq; if (c < 4800) { data0 = ath5k_hw_bitswap((c - 2272), 8); data2 = 0; /* ? 5GHz ? */ } else if ((c % 5) != 2 || c > 5435) { if (!(c % 20) && c < 5120) data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); else if (!(c % 10)) data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); else if (!(c % 5)) data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); else return -EINVAL; data2 = ath5k_hw_bitswap(1, 2); } else { data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8); data2 = ath5k_hw_bitswap(0, 2); } data = (data0 << 4) | data2 << 2 | 0x1001; ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); return 0; } /** * ath5k_hw_channel() - Set a channel on the radio chip * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * This is the main function called to set a channel on the * radio chip based on the radio chip version. */ static int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { int ret; /* * Check bounds supported by the PHY (we don't care about regulatory * restrictions at this point). */ if (!ath5k_channel_ok(ah, channel)) { ATH5K_ERR(ah, "channel frequency (%u MHz) out of supported " "band range\n", channel->center_freq); return -EINVAL; } /* * Set the channel and wait */ switch (ah->ah_radio) { case AR5K_RF5110: ret = ath5k_hw_rf5110_channel(ah, channel); break; case AR5K_RF5111: ret = ath5k_hw_rf5111_channel(ah, channel); break; case AR5K_RF2317: case AR5K_RF2425: ret = ath5k_hw_rf2425_channel(ah, channel); break; default: ret = ath5k_hw_rf5112_channel(ah, channel); break; } if (ret) return ret; /* Set JAPAN setting for channel 14 */ if (channel->center_freq == 2484) { AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, AR5K_PHY_CCKTXCTL_JAPAN); } else { AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, AR5K_PHY_CCKTXCTL_WORLD); } ah->ah_current_channel = channel; return 0; } /*****************\ PHY calibration \*****************/ /** * DOC: PHY Calibration routines * * Noise floor calibration: When we tell the hardware to * perform a noise floor calibration by setting the * AR5K_PHY_AGCCTL_NF bit on AR5K_PHY_AGCCTL, it will periodically * sample-and-hold the minimum noise level seen at the antennas. * This value is then stored in a ring buffer of recently measured * noise floor values so we have a moving window of the last few * samples. The median of the values in the history is then loaded * into the hardware for its own use for RSSI and CCA measurements. * This type of calibration doesn't interfere with traffic. * * AGC calibration: When we tell the hardware to perform * an AGC (Automatic Gain Control) calibration by setting the * AR5K_PHY_AGCCTL_CAL, hw disconnects the antennas and does * a calibration on the DC offsets of ADCs. During this period * rx/tx gets disabled so we have to deal with it on the driver * part. * * I/Q calibration: When we tell the hardware to perform * an I/Q calibration, it tries to correct I/Q imbalance and * fix QAM constellation by sampling data from rxed frames. * It doesn't interfere with traffic. * * For more infos on AGC and I/Q calibration check out patent doc * #03/094463. */ /** * ath5k_hw_read_measured_noise_floor() - Read measured NF from hw * @ah: The &struct ath5k_hw */ static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) { s32 val; val = ath5k_hw_reg_read(ah, AR5K_PHY_NF); return sign_extend32(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 8); } /** * ath5k_hw_init_nfcal_hist() - Initialize NF calibration history buffer * @ah: The &struct ath5k_hw */ void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) { int i; ah->ah_nfcal_hist.index = 0; for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE; } /** * ath5k_hw_update_nfcal_hist() - Update NF calibration history buffer * @ah: The &struct ath5k_hw * @noise_floor: The NF we got from hw */ static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor) { struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist; hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX - 1); hist->nfval[hist->index] = noise_floor; } /** * ath5k_hw_get_median_noise_floor() - Get median NF from history buffer * @ah: The &struct ath5k_hw */ static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) { s16 sort[ATH5K_NF_CAL_HIST_MAX]; s16 tmp; int i, j; memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort)); for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) { for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) { if (sort[j] > sort[j - 1]) { tmp = sort[j]; sort[j] = sort[j - 1]; sort[j - 1] = tmp; } } } for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) { ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "cal %d:%d\n", i, sort[i]); } return sort[(ATH5K_NF_CAL_HIST_MAX - 1) / 2]; } /** * ath5k_hw_update_noise_floor() - Update NF on hardware * @ah: The &struct ath5k_hw * * This is the main function we call to perform a NF calibration, * it reads NF from hardware, calculates the median and updates * NF on hw. */ void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 val; s16 nf, threshold; u8 ee_mode; /* keep last value if calibration hasn't completed */ if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) { ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "NF did not complete in calibration window\n"); return; } ah->ah_cal_mask |= AR5K_CALIBRATION_NF; ee_mode = ath5k_eeprom_mode_from_channel(ah->ah_current_channel); /* completed NF calibration, test threshold */ nf = ath5k_hw_read_measured_noise_floor(ah); threshold = ee->ee_noise_floor_thr[ee_mode]; if (nf > threshold) { ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "noise floor failure detected; " "read %d, threshold %d\n", nf, threshold); nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE; } ath5k_hw_update_nfcal_hist(ah, nf); nf = ath5k_hw_get_median_noise_floor(ah); /* load noise floor (in .5 dBm) so the hardware will use it */ val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M; val |= (nf * 2) & AR5K_PHY_NF_M; ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE)); ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, 0, false); /* * Load a high max CCA Power value (-50 dBm in .5 dBm units) * so that we're not capped by the median we just loaded. * This will be used as the initial value for the next noise * floor calibration. */ val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M); ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE | AR5K_PHY_AGCCTL_NF); ah->ah_noise_floor = nf; ah->ah_cal_mask &= ~AR5K_CALIBRATION_NF; ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "noise floor calibrated: %d\n", nf); } /** * ath5k_hw_rf5110_calibrate() - Perform a PHY calibration on RF5110 * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * Do a complete PHY calibration (AGC + NF + I/Q) on RF5110 */ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 phy_sig, phy_agc, phy_sat, beacon; int ret; if (!(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) return 0; /* * Disable beacons and RX/TX queues, wait */ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210, AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210); beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210); ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210); usleep_range(2000, 2500); /* * Set the channel (with AGC turned off) */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); udelay(10); ret = ath5k_hw_channel(ah, channel); /* * Activate PHY and wait */ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); usleep_range(1000, 1500); AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); if (ret) return ret; /* * Calibrate the radio chip */ /* Remember normal state */ phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG); phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE); phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT); /* Update radio registers */ ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) | AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI | AR5K_PHY_AGCCOARSE_LO)) | AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) | AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT | AR5K_PHY_ADCSAT_THR)) | AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) | AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT); udelay(20); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); udelay(10); ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG); AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); usleep_range(1000, 1500); /* * Enable calibration and wait until completion */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL); ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL, 0, false); /* Reset to normal state */ ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG); ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE); ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT); if (ret) { ATH5K_ERR(ah, "calibration timeout (%uMHz)\n", channel->center_freq); return ret; } /* * Re-enable RX/TX and beacons */ AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210, AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210); ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210); return 0; } /** * ath5k_hw_rf511x_iq_calibrate() - Perform I/Q calibration on RF5111 and newer * @ah: The &struct ath5k_hw */ static int ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah) { u32 i_pwr, q_pwr; s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd; int i; /* Skip if I/Q calibration is not needed or if it's still running */ if (!ah->ah_iq_cal_needed) return -EINVAL; else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, "I/Q calibration still running"); return -EBUSY; } /* Calibration has finished, get the results and re-run */ /* Work around for empty results which can apparently happen on 5212: * Read registers up to 10 times until we get both i_pr and q_pwr */ for (i = 0; i <= 10; i++) { iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr); if (i_pwr && q_pwr) break; } i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; if (ah->ah_version == AR5K_AR5211) q_coffd = q_pwr >> 6; else q_coffd = q_pwr >> 7; /* In case i_coffd became zero, cancel calibration * not only it's too small, it'll also result a divide * by zero later on. */ if (i_coffd == 0 || q_coffd < 2) return -ECANCELED; /* Protect against loss of sign bits */ i_coff = (-iq_corr) / i_coffd; i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */ if (ah->ah_version == AR5K_AR5211) q_coff = (i_pwr / q_coffd) - 64; else q_coff = (i_pwr / q_coffd) - 128; q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, "new I:%d Q:%d (i_coffd:%x q_coffd:%x)", i_coff, q_coff, i_coffd, q_coffd); /* Commit new I/Q values (set enable bit last to match HAL sources) */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE); /* Re-enable calibration -if we don't we'll commit * the same values again and again */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN); return 0; } /** * ath5k_hw_phy_calibrate() - Perform a PHY calibration * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * The main function we call from above to perform * a short or full PHY calibration based on RF chip * and current channel */ int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel) { int ret; if (ah->ah_radio == AR5K_RF5110) return ath5k_hw_rf5110_calibrate(ah, channel); ret = ath5k_hw_rf511x_iq_calibrate(ah); if (ret) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, "No I/Q correction performed (%uMHz)\n", channel->center_freq); /* Happens all the time if there is not much * traffic, consider it normal behaviour. */ ret = 0; } /* On full calibration request a PAPD probe for * gainf calibration if needed */ if ((ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && (ah->ah_radio == AR5K_RF5111 || ah->ah_radio == AR5K_RF5112) && channel->hw_value != AR5K_MODE_11B) ath5k_hw_request_rfgain_probe(ah); /* Update noise floor */ if (!(ah->ah_cal_mask & AR5K_CALIBRATION_NF)) ath5k_hw_update_noise_floor(ah); return ret; } /***************************\ * Spur mitigation functions * \***************************/ /** * ath5k_hw_set_spur_mitigation_filter() - Configure SPUR filter * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * * This function gets called during PHY initialization to * configure the spur filter for the given channel. Spur is noise * generated due to "reflection" effects, for more information on this * method check out patent US7643810 */ static void ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 mag_mask[4] = {0, 0, 0, 0}; u32 pilot_mask[2] = {0, 0}; /* Note: fbin values are scaled up by 2 */ u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window; s32 spur_delta_phase, spur_freq_sigma_delta; s32 spur_offset, num_symbols_x16; u8 num_symbol_offsets, i, freq_band; /* Convert current frequency to fbin value (the same way channels * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale * up by 2 so we can compare it later */ if (channel->band == IEEE80211_BAND_2GHZ) { chan_fbin = (channel->center_freq - 2300) * 10; freq_band = AR5K_EEPROM_BAND_2GHZ; } else { chan_fbin = (channel->center_freq - 4900) * 10; freq_band = AR5K_EEPROM_BAND_5GHZ; } /* Check if any spur_chan_fbin from EEPROM is * within our current channel's spur detection range */ spur_chan_fbin = AR5K_EEPROM_NO_SPUR; spur_detection_window = AR5K_SPUR_CHAN_WIDTH; /* XXX: Half/Quarter channels ?*/ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) spur_detection_window *= 2; for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { spur_chan_fbin = ee->ee_spur_chans[i][freq_band]; /* Note: mask cleans AR5K_EEPROM_NO_SPUR flag * so it's zero if we got nothing from EEPROM */ if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) { spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; break; } if ((chan_fbin - spur_detection_window <= (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) && (chan_fbin + spur_detection_window >= (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) { spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; break; } } /* We need to enable spur filter for this channel */ if (spur_chan_fbin) { spur_offset = spur_chan_fbin - chan_fbin; /* * Calculate deltas: * spur_freq_sigma_delta -> spur_offset / sample_freq << 21 * spur_delta_phase -> spur_offset / chip_freq << 11 * Note: Both values have 100Hz resolution */ switch (ah->ah_bwmode) { case AR5K_BWMODE_40MHZ: /* Both sample_freq and chip_freq are 80MHz */ spur_delta_phase = (spur_offset << 16) / 25; spur_freq_sigma_delta = (spur_delta_phase >> 10); symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz * 2; break; case AR5K_BWMODE_10MHZ: /* Both sample_freq and chip_freq are 20MHz (?) */ spur_delta_phase = (spur_offset << 18) / 25; spur_freq_sigma_delta = (spur_delta_phase >> 10); symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2; case AR5K_BWMODE_5MHZ: /* Both sample_freq and chip_freq are 10MHz (?) */ spur_delta_phase = (spur_offset << 19) / 25; spur_freq_sigma_delta = (spur_delta_phase >> 10); symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4; default: if (channel->band == IEEE80211_BAND_5GHZ) { /* Both sample_freq and chip_freq are 40MHz */ spur_delta_phase = (spur_offset << 17) / 25; spur_freq_sigma_delta = (spur_delta_phase >> 10); symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; } else { /* sample_freq -> 40MHz chip_freq -> 44MHz * (for b compatibility) */ spur_delta_phase = (spur_offset << 17) / 25; spur_freq_sigma_delta = (spur_offset << 8) / 55; symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; } break; } /* Calculate pilot and magnitude masks */ /* Scale up spur_offset by 1000 to switch to 100HZ resolution * and divide by symbol_width to find how many symbols we have * Note: number of symbols is scaled up by 16 */ num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width; /* Spur is on a symbol if num_symbols_x16 % 16 is zero */ if (!(num_symbols_x16 & 0xF)) /* _X_ */ num_symbol_offsets = 3; else /* _xx_ */ num_symbol_offsets = 4; for (i = 0; i < num_symbol_offsets; i++) { /* Calculate pilot mask */ s32 curr_sym_off = (num_symbols_x16 / 16) + i + 25; /* Pilot magnitude mask seems to be a way to * declare the boundaries for our detection * window or something, it's 2 for the middle * value(s) where the symbol is expected to be * and 1 on the boundary values */ u8 plt_mag_map = (i == 0 || i == (num_symbol_offsets - 1)) ? 1 : 2; if (curr_sym_off >= 0 && curr_sym_off <= 32) { if (curr_sym_off <= 25) pilot_mask[0] |= 1 << curr_sym_off; else if (curr_sym_off >= 27) pilot_mask[0] |= 1 << (curr_sym_off - 1); } else if (curr_sym_off >= 33 && curr_sym_off <= 52) pilot_mask[1] |= 1 << (curr_sym_off - 33); /* Calculate magnitude mask (for viterbi decoder) */ if (curr_sym_off >= -1 && curr_sym_off <= 14) mag_mask[0] |= plt_mag_map << (curr_sym_off + 1) * 2; else if (curr_sym_off >= 15 && curr_sym_off <= 30) mag_mask[1] |= plt_mag_map << (curr_sym_off - 15) * 2; else if (curr_sym_off >= 31 && curr_sym_off <= 46) mag_mask[2] |= plt_mag_map << (curr_sym_off - 31) * 2; else if (curr_sym_off >= 47 && curr_sym_off <= 53) mag_mask[3] |= plt_mag_map << (curr_sym_off - 47) * 2; } /* Write settings on hw to enable spur filter */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, AR5K_PHY_BIN_MASK_CTL_RATE, 0xff); /* XXX: Self correlator also ? */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_PILOT_MASK_EN | AR5K_PHY_IQ_CHAN_MASK_EN | AR5K_PHY_IQ_SPUR_FILT_EN); /* Set delta phase and freq sigma delta */ ath5k_hw_reg_write(ah, AR5K_REG_SM(spur_delta_phase, AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) | AR5K_REG_SM(spur_freq_sigma_delta, AR5K_PHY_TIMING_11_SPUR_FREQ_SD) | AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC, AR5K_PHY_TIMING_11); /* Write pilot masks */ ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, AR5K_PHY_TIMING_8_PILOT_MASK_2, pilot_mask[1]); ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, AR5K_PHY_TIMING_10_PILOT_MASK_2, pilot_mask[1]); /* Write magnitude masks */ ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1); ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2); ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, AR5K_PHY_BIN_MASK_CTL_MASK_4, mag_mask[3]); ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1); ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2); ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, AR5K_PHY_BIN_MASK2_4_MASK_4, mag_mask[3]); } else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_SPUR_FILT_EN) { /* Clean up spur mitigation settings and disable filter */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, AR5K_PHY_BIN_MASK_CTL_RATE, 0); AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_PILOT_MASK_EN | AR5K_PHY_IQ_CHAN_MASK_EN | AR5K_PHY_IQ_SPUR_FILT_EN); ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11); /* Clear pilot masks */ ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, AR5K_PHY_TIMING_8_PILOT_MASK_2, 0); ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, AR5K_PHY_TIMING_10_PILOT_MASK_2, 0); /* Clear magnitude masks */ ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1); ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2); ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, AR5K_PHY_BIN_MASK_CTL_MASK_4, 0); ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1); ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2); ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, AR5K_PHY_BIN_MASK2_4_MASK_4, 0); } } /*****************\ * Antenna control * \*****************/ /** * DOC: Antenna control * * Hw supports up to 14 antennas ! I haven't found any card that implements * that. The maximum number of antennas I've seen is up to 4 (2 for 2GHz and 2 * for 5GHz). Antenna 1 (MAIN) should be omnidirectional, 2 (AUX) * omnidirectional or sectorial and antennas 3-14 sectorial (or directional). * * We can have a single antenna for RX and multiple antennas for TX. * RX antenna is our "default" antenna (usually antenna 1) set on * DEFAULT_ANTENNA register and TX antenna is set on each TX control descriptor * (0 for automatic selection, 1 - 14 antenna number). * * We can let hw do all the work doing fast antenna diversity for both * tx and rx or we can do things manually. Here are the options we have * (all are bits of STA_ID1 register): * * AR5K_STA_ID1_DEFAULT_ANTENNA -> When 0 is set as the TX antenna on TX * control descriptor, use the default antenna to transmit or else use the last * antenna on which we received an ACK. * * AR5K_STA_ID1_DESC_ANTENNA -> Update default antenna after each TX frame to * the antenna on which we got the ACK for that frame. * * AR5K_STA_ID1_RTS_DEF_ANTENNA -> Use default antenna for RTS or else use the * one on the TX descriptor. * * AR5K_STA_ID1_SELFGEN_DEF_ANT -> Use default antenna for self generated frames * (ACKs etc), or else use current antenna (the one we just used for TX). * * Using the above we support the following scenarios: * * AR5K_ANTMODE_DEFAULT -> Hw handles antenna diversity etc automatically * * AR5K_ANTMODE_FIXED_A -> Only antenna A (MAIN) is present * * AR5K_ANTMODE_FIXED_B -> Only antenna B (AUX) is present * * AR5K_ANTMODE_SINGLE_AP -> Sta locked on a single ap * * AR5K_ANTMODE_SECTOR_AP -> AP with tx antenna set on tx desc * * AR5K_ANTMODE_SECTOR_STA -> STA with tx antenna set on tx desc * * AR5K_ANTMODE_DEBUG Debug mode -A -> Rx, B-> Tx- * * Also note that when setting antenna to F on tx descriptor card inverts * current tx antenna. */ /** * ath5k_hw_set_def_antenna() - Set default rx antenna on AR5211/5212 and newer * @ah: The &struct ath5k_hw * @ant: Antenna number */ static void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant) { if (ah->ah_version != AR5K_AR5210) ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA); } /** * ath5k_hw_set_fast_div() - Enable/disable fast rx antenna diversity * @ah: The &struct ath5k_hw * @ee_mode: One of enum ath5k_driver_mode * @enable: True to enable, false to disable */ static void ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable) { switch (ee_mode) { case AR5K_EEPROM_MODE_11G: /* XXX: This is set to * disabled on initvals !!! */ case AR5K_EEPROM_MODE_11A: if (enable) AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_OFDM_DIV_DIS); else AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_OFDM_DIV_DIS); break; case AR5K_EEPROM_MODE_11B: AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_OFDM_DIV_DIS); break; default: return; } if (enable) { AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, AR5K_PHY_RESTART_DIV_GC, 4); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, AR5K_PHY_FAST_ANT_DIV_EN); } else { AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, AR5K_PHY_RESTART_DIV_GC, 0); AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, AR5K_PHY_FAST_ANT_DIV_EN); } } /** * ath5k_hw_set_antenna_switch() - Set up antenna switch table * @ah: The &struct ath5k_hw * @ee_mode: One of enum ath5k_driver_mode * * Switch table comes from EEPROM and includes information on controlling * the 2 antenna RX attenuators */ void ath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode) { u8 ant0, ant1; /* * In case a fixed antenna was set as default * use the same switch table twice. */ if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_A) ant0 = ant1 = AR5K_ANT_SWTABLE_A; else if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_B) ant0 = ant1 = AR5K_ANT_SWTABLE_B; else { ant0 = AR5K_ANT_SWTABLE_A; ant1 = AR5K_ANT_SWTABLE_B; } /* Set antenna idle switch table */ AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL, AR5K_PHY_ANT_CTL_SWTABLE_IDLE, (ah->ah_ant_ctl[ee_mode][AR5K_ANT_CTL] | AR5K_PHY_ANT_CTL_TXRX_EN)); /* Set antenna switch tables */ ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant0], AR5K_PHY_ANT_SWITCH_TABLE_0); ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant1], AR5K_PHY_ANT_SWITCH_TABLE_1); } /** * ath5k_hw_set_antenna_mode() - Set antenna operating mode * @ah: The &struct ath5k_hw * @ant_mode: One of enum ath5k_ant_mode */ void ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) { struct ieee80211_channel *channel = ah->ah_current_channel; bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div; bool use_def_for_sg; int ee_mode; u8 def_ant, tx_ant; u32 sta_id1 = 0; /* if channel is not initialized yet we can't set the antennas * so just store the mode. it will be set on the next reset */ if (channel == NULL) { ah->ah_ant_mode = ant_mode; return; } def_ant = ah->ah_def_ant; ee_mode = ath5k_eeprom_mode_from_channel(channel); if (ee_mode < 0) { ATH5K_ERR(ah, "invalid channel: %d\n", channel->center_freq); return; } switch (ant_mode) { case AR5K_ANTMODE_DEFAULT: tx_ant = 0; use_def_for_tx = false; update_def_on_tx = false; use_def_for_rts = false; use_def_for_sg = false; fast_div = true; break; case AR5K_ANTMODE_FIXED_A: def_ant = 1; tx_ant = 1; use_def_for_tx = true; update_def_on_tx = false; use_def_for_rts = true; use_def_for_sg = true; fast_div = false; break; case AR5K_ANTMODE_FIXED_B: def_ant = 2; tx_ant = 2; use_def_for_tx = true; update_def_on_tx = false; use_def_for_rts = true; use_def_for_sg = true; fast_div = false; break; case AR5K_ANTMODE_SINGLE_AP: def_ant = 1; /* updated on tx */ tx_ant = 0; use_def_for_tx = true; update_def_on_tx = true; use_def_for_rts = true; use_def_for_sg = true; fast_div = true; break; case AR5K_ANTMODE_SECTOR_AP: tx_ant = 1; /* variable */ use_def_for_tx = false; update_def_on_tx = false; use_def_for_rts = true; use_def_for_sg = false; fast_div = false; break; case AR5K_ANTMODE_SECTOR_STA: tx_ant = 1; /* variable */ use_def_for_tx = true; update_def_on_tx = false; use_def_for_rts = true; use_def_for_sg = false; fast_div = true; break; case AR5K_ANTMODE_DEBUG: def_ant = 1; tx_ant = 2; use_def_for_tx = false; update_def_on_tx = false; use_def_for_rts = false; use_def_for_sg = false; fast_div = false; break; default: return; } ah->ah_tx_ant = tx_ant; ah->ah_ant_mode = ant_mode; ah->ah_def_ant = def_ant; sta_id1 |= use_def_for_tx ? AR5K_STA_ID1_DEFAULT_ANTENNA : 0; sta_id1 |= update_def_on_tx ? AR5K_STA_ID1_DESC_ANTENNA : 0; sta_id1 |= use_def_for_rts ? AR5K_STA_ID1_RTS_DEF_ANTENNA : 0; sta_id1 |= use_def_for_sg ? AR5K_STA_ID1_SELFGEN_DEF_ANT : 0; AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_ANTENNA_SETTINGS); if (sta_id1) AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, sta_id1); ath5k_hw_set_antenna_switch(ah, ee_mode); /* Note: set diversity before default antenna * because it won't work correctly */ ath5k_hw_set_fast_div(ah, ee_mode, fast_div); ath5k_hw_set_def_antenna(ah, def_ant); } /****************\ * TX power setup * \****************/ /* * Helper functions */ /** * ath5k_get_interpolated_value() - Get interpolated Y val between two points * @target: X value of the middle point * @x_left: X value of the left point * @x_right: X value of the right point * @y_left: Y value of the left point * @y_right: Y value of the right point */ static s16 ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right, s16 y_left, s16 y_right) { s16 ratio, result; /* Avoid divide by zero and skip interpolation * if we have the same point */ if ((x_left == x_right) || (y_left == y_right)) return y_left; /* * Since we use ints and not fps, we need to scale up in * order to get a sane ratio value (or else we 'll eg. get * always 1 instead of 1.25, 1.75 etc). We scale up by 100 * to have some accuracy both for 0.5 and 0.25 steps. */ ratio = ((100 * y_right - 100 * y_left) / (x_right - x_left)); /* Now scale down to be in range */ result = y_left + (ratio * (target - x_left) / 100); return result; } /** * ath5k_get_linear_pcdac_min() - Find vertical boundary (min pwr) for the * linear PCDAC curve * @stepL: Left array with y values (pcdac steps) * @stepR: Right array with y values (pcdac steps) * @pwrL: Left array with x values (power steps) * @pwrR: Right array with x values (power steps) * * Since we have the top of the curve and we draw the line below * until we reach 1 (1 pcdac step) we need to know which point * (x value) that is so that we don't go below x axis and have negative * pcdac values when creating the curve, or fill the table with zeros. */ static s16 ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, const s16 *pwrL, const s16 *pwrR) { s8 tmp; s16 min_pwrL, min_pwrR; s16 pwr_i; /* Some vendors write the same pcdac value twice !!! */ if (stepL[0] == stepL[1] || stepR[0] == stepR[1]) return max(pwrL[0], pwrR[0]); if (pwrL[0] == pwrL[1]) min_pwrL = pwrL[0]; else { pwr_i = pwrL[0]; do { pwr_i--; tmp = (s8) ath5k_get_interpolated_value(pwr_i, pwrL[0], pwrL[1], stepL[0], stepL[1]); } while (tmp > 1); min_pwrL = pwr_i; } if (pwrR[0] == pwrR[1]) min_pwrR = pwrR[0]; else { pwr_i = pwrR[0]; do { pwr_i--; tmp = (s8) ath5k_get_interpolated_value(pwr_i, pwrR[0], pwrR[1], stepR[0], stepR[1]); } while (tmp > 1); min_pwrR = pwr_i; } /* Keep the right boundary so that it works for both curves */ return max(min_pwrL, min_pwrR); } /** * ath5k_create_power_curve() - Create a Power to PDADC or PCDAC curve * @pmin: Minimum power value (xmin) * @pmax: Maximum power value (xmax) * @pwr: Array of power steps (x values) * @vpd: Array of matching PCDAC/PDADC steps (y values) * @num_points: Number of provided points * @vpd_table: Array to fill with the full PCDAC/PDADC values (y values) * @type: One of enum ath5k_powertable_type (eeprom.h) * * Interpolate (pwr,vpd) points to create a Power to PDADC or a * Power to PCDAC curve. * * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC * steps (offsets) on y axis. Power can go up to 31.5dB and max * PCDAC/PDADC step for each curve is 64 but we can write more than * one curves on hw so we can go up to 128 (which is the max step we * can write on the final table). * * We write y values (PCDAC/PDADC steps) on hw. */ static void ath5k_create_power_curve(s16 pmin, s16 pmax, const s16 *pwr, const u8 *vpd, u8 num_points, u8 *vpd_table, u8 type) { u8 idx[2] = { 0, 1 }; s16 pwr_i = 2 * pmin; int i; if (num_points < 2) return; /* We want the whole line, so adjust boundaries * to cover the entire power range. Note that * power values are already 0.25dB so no need * to multiply pwr_i by 2 */ if (type == AR5K_PWRTABLE_LINEAR_PCDAC) { pwr_i = pmin; pmin = 0; pmax = 63; } /* Find surrounding turning points (TPs) * and interpolate between them */ for (i = 0; (i <= (u16) (pmax - pmin)) && (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { /* We passed the right TP, move to the next set of TPs * if we pass the last TP, extrapolate above using the last * two TPs for ratio */ if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) { idx[0]++; idx[1]++; } vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i, pwr[idx[0]], pwr[idx[1]], vpd[idx[0]], vpd[idx[1]]); /* Increase by 0.5dB * (0.25 dB units) */ pwr_i += 2; } } /** * ath5k_get_chan_pcal_surrounding_piers() - Get surrounding calibration piers * for a given channel. * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * @pcinfo_l: The &struct ath5k_chan_pcal_info to put the left cal. pier * @pcinfo_r: The &struct ath5k_chan_pcal_info to put the right cal. pier * * Get the surrounding per-channel power calibration piers * for a given frequency so that we can interpolate between * them and come up with an appropriate dataset for our current * channel. */ static void ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah, struct ieee80211_channel *channel, struct ath5k_chan_pcal_info **pcinfo_l, struct ath5k_chan_pcal_info **pcinfo_r) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_chan_pcal_info *pcinfo; u8 idx_l, idx_r; u8 mode, max, i; u32 target = channel->center_freq; idx_l = 0; idx_r = 0; switch (channel->hw_value) { case AR5K_EEPROM_MODE_11A: pcinfo = ee->ee_pwr_cal_a; mode = AR5K_EEPROM_MODE_11A; break; case AR5K_EEPROM_MODE_11B: pcinfo = ee->ee_pwr_cal_b; mode = AR5K_EEPROM_MODE_11B; break; case AR5K_EEPROM_MODE_11G: default: pcinfo = ee->ee_pwr_cal_g; mode = AR5K_EEPROM_MODE_11G; break; } max = ee->ee_n_piers[mode] - 1; /* Frequency is below our calibrated * range. Use the lowest power curve * we have */ if (target < pcinfo[0].freq) { idx_l = idx_r = 0; goto done; } /* Frequency is above our calibrated * range. Use the highest power curve * we have */ if (target > pcinfo[max].freq) { idx_l = idx_r = max; goto done; } /* Frequency is inside our calibrated * channel range. Pick the surrounding * calibration piers so that we can * interpolate */ for (i = 0; i <= max; i++) { /* Frequency matches one of our calibration * piers, no need to interpolate, just use * that calibration pier */ if (pcinfo[i].freq == target) { idx_l = idx_r = i; goto done; } /* We found a calibration pier that's above * frequency, use this pier and the previous * one to interpolate */ if (target < pcinfo[i].freq) { idx_r = i; idx_l = idx_r - 1; goto done; } } done: *pcinfo_l = &pcinfo[idx_l]; *pcinfo_r = &pcinfo[idx_r]; } /** * ath5k_get_rate_pcal_data() - Get the interpolated per-rate power * calibration data * @ah: The &struct ath5k_hw *ah, * @channel: The &struct ieee80211_channel * @rates: The &struct ath5k_rate_pcal_info to fill * * Get the surrounding per-rate power calibration data * for a given frequency and interpolate between power * values to set max target power supported by hw for * each rate on this frequency. */ static void ath5k_get_rate_pcal_data(struct ath5k_hw *ah, struct ieee80211_channel *channel, struct ath5k_rate_pcal_info *rates) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_rate_pcal_info *rpinfo; u8 idx_l, idx_r; u8 mode, max, i; u32 target = channel->center_freq; idx_l = 0; idx_r = 0; switch (channel->hw_value) { case AR5K_MODE_11A: rpinfo = ee->ee_rate_tpwr_a; mode = AR5K_EEPROM_MODE_11A; break; case AR5K_MODE_11B: rpinfo = ee->ee_rate_tpwr_b; mode = AR5K_EEPROM_MODE_11B; break; case AR5K_MODE_11G: default: rpinfo = ee->ee_rate_tpwr_g; mode = AR5K_EEPROM_MODE_11G; break; } max = ee->ee_rate_target_pwr_num[mode] - 1; /* Get the surrounding calibration * piers - same as above */ if (target < rpinfo[0].freq) { idx_l = idx_r = 0; goto done; } if (target > rpinfo[max].freq) { idx_l = idx_r = max; goto done; } for (i = 0; i <= max; i++) { if (rpinfo[i].freq == target) { idx_l = idx_r = i; goto done; } if (target < rpinfo[i].freq) { idx_r = i; idx_l = idx_r - 1; goto done; } } done: /* Now interpolate power value, based on the frequency */ rates->freq = target; rates->target_power_6to24 = ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, rpinfo[idx_r].freq, rpinfo[idx_l].target_power_6to24, rpinfo[idx_r].target_power_6to24); rates->target_power_36 = ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, rpinfo[idx_r].freq, rpinfo[idx_l].target_power_36, rpinfo[idx_r].target_power_36); rates->target_power_48 = ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, rpinfo[idx_r].freq, rpinfo[idx_l].target_power_48, rpinfo[idx_r].target_power_48); rates->target_power_54 = ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, rpinfo[idx_r].freq, rpinfo[idx_l].target_power_54, rpinfo[idx_r].target_power_54); } /** * ath5k_get_max_ctl_power() - Get max edge power for a given frequency * @ah: the &struct ath5k_hw * @channel: The &struct ieee80211_channel * * Get the max edge power for this channel if * we have such data from EEPROM's Conformance Test * Limits (CTL), and limit max power if needed. */ static void ath5k_get_max_ctl_power(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_edge_power *rep = ee->ee_ctl_pwr; u8 *ctl_val = ee->ee_ctl; s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4; s16 edge_pwr = 0; u8 rep_idx; u8 i, ctl_mode; u8 ctl_idx = 0xFF; u32 target = channel->center_freq; ctl_mode = ath_regd_get_band_ctl(regulatory, channel->band); switch (channel->hw_value) { case AR5K_MODE_11A: if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) ctl_mode |= AR5K_CTL_TURBO; else ctl_mode |= AR5K_CTL_11A; break; case AR5K_MODE_11G: if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) ctl_mode |= AR5K_CTL_TURBOG; else ctl_mode |= AR5K_CTL_11G; break; case AR5K_MODE_11B: ctl_mode |= AR5K_CTL_11B; break; default: return; } for (i = 0; i < ee->ee_ctls; i++) { if (ctl_val[i] == ctl_mode) { ctl_idx = i; break; } } /* If we have a CTL dataset available grab it and find the * edge power for our frequency */ if (ctl_idx == 0xFF) return; /* Edge powers are sorted by frequency from lower * to higher. Each CTL corresponds to 8 edge power * measurements. */ rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES; /* Don't do boundaries check because we * might have more that one bands defined * for this mode */ /* Get the edge power that's closer to our * frequency */ for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) { rep_idx += i; if (target <= rep[rep_idx].freq) edge_pwr = (s16) rep[rep_idx].edge; } if (edge_pwr) ah->ah_txpower.txp_max_pwr = 4 * min(edge_pwr, max_chan_pwr); } /* * Power to PCDAC table functions */ /** * DOC: Power to PCDAC table functions * * For RF5111 we have an XPD -eXternal Power Detector- curve * for each calibrated channel. Each curve has 0,5dB Power steps * on x axis and PCDAC steps (offsets) on y axis and looks like an * exponential function. To recreate the curve we read 11 points * from eeprom (eeprom.c) and interpolate here. * * For RF5112 we have 4 XPD -eXternal Power Detector- curves * for each calibrated channel on 0, -6, -12 and -18dBm but we only * use the higher (3) and the lower (0) curves. Each curve again has 0.5dB * power steps on x axis and PCDAC steps on y axis and looks like a * linear function. To recreate the curve and pass the power values * on hw, we get 4 points for xpd 0 (lower gain -> max power) * and 3 points for xpd 3 (higher gain -> lower power) from eeprom (eeprom.c) * and interpolate here. * * For a given channel we get the calibrated points (piers) for it or * -if we don't have calibration data for this specific channel- from the * available surrounding channels we have calibration data for, after we do a * linear interpolation between them. Then since we have our calibrated points * for this channel, we do again a linear interpolation between them to get the * whole curve. * * We finally write the Y values of the curve(s) (the PCDAC values) on hw */ /** * ath5k_fill_pwr_to_pcdac_table() - Fill Power to PCDAC table on RF5111 * @ah: The &struct ath5k_hw * @table_min: Minimum power (x min) * @table_max: Maximum power (x max) * * No further processing is needed for RF5111, the only thing we have to * do is fill the values below and above calibration range since eeprom data * may not cover the entire PCDAC table. */ static void ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min, s16 *table_max) { u8 *pcdac_out = ah->ah_txpower.txp_pd_table; u8 *pcdac_tmp = ah->ah_txpower.tmpL[0]; u8 pcdac_0, pcdac_n, pcdac_i, pwr_idx, i; s16 min_pwr, max_pwr; /* Get table boundaries */ min_pwr = table_min[0]; pcdac_0 = pcdac_tmp[0]; max_pwr = table_max[0]; pcdac_n = pcdac_tmp[table_max[0] - table_min[0]]; /* Extrapolate below minimum using pcdac_0 */ pcdac_i = 0; for (i = 0; i < min_pwr; i++) pcdac_out[pcdac_i++] = pcdac_0; /* Copy values from pcdac_tmp */ pwr_idx = min_pwr; for (i = 0; pwr_idx <= max_pwr && pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) { pcdac_out[pcdac_i++] = pcdac_tmp[i]; pwr_idx++; } /* Extrapolate above maximum */ while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE) pcdac_out[pcdac_i++] = pcdac_n; } /** * ath5k_combine_linear_pcdac_curves() - Combine available PCDAC Curves * @ah: The &struct ath5k_hw * @table_min: Minimum power (x min) * @table_max: Maximum power (x max) * @pdcurves: Number of pd curves * * Combine available XPD Curves and fill Linear Power to PCDAC table on RF5112 * RFX112 can have up to 2 curves (one for low txpower range and one for * higher txpower range). We need to put them both on pcdac_out and place * them in the correct location. In case we only have one curve available * just fit it on pcdac_out (it's supposed to cover the entire range of * available pwr levels since it's always the higher power curve). Extrapolate * below and above final table if needed. */ static void ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min, s16 *table_max, u8 pdcurves) { u8 *pcdac_out = ah->ah_txpower.txp_pd_table; u8 *pcdac_low_pwr; u8 *pcdac_high_pwr; u8 *pcdac_tmp; u8 pwr; s16 max_pwr_idx; s16 min_pwr_idx; s16 mid_pwr_idx = 0; /* Edge flag turns on the 7nth bit on the PCDAC * to declare the higher power curve (force values * to be greater than 64). If we only have one curve * we don't need to set this, if we have 2 curves and * fill the table backwards this can also be used to * switch from higher power curve to lower power curve */ u8 edge_flag; int i; /* When we have only one curve available * that's the higher power curve. If we have * two curves the first is the high power curve * and the next is the low power curve. */ if (pdcurves > 1) { pcdac_low_pwr = ah->ah_txpower.tmpL[1]; pcdac_high_pwr = ah->ah_txpower.tmpL[0]; mid_pwr_idx = table_max[1] - table_min[1] - 1; max_pwr_idx = (table_max[0] - table_min[0]) / 2; /* If table size goes beyond 31.5dB, keep the * upper 31.5dB range when setting tx power. * Note: 126 = 31.5 dB in quarter dB steps */ if (table_max[0] - table_min[1] > 126) min_pwr_idx = table_max[0] - 126; else min_pwr_idx = table_min[1]; /* Since we fill table backwards * start from high power curve */ pcdac_tmp = pcdac_high_pwr; edge_flag = 0x40; } else { pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */ pcdac_high_pwr = ah->ah_txpower.tmpL[0]; min_pwr_idx = table_min[0]; max_pwr_idx = (table_max[0] - table_min[0]) / 2; pcdac_tmp = pcdac_high_pwr; edge_flag = 0; } /* This is used when setting tx power*/ ah->ah_txpower.txp_min_idx = min_pwr_idx / 2; /* Fill Power to PCDAC table backwards */ pwr = max_pwr_idx; for (i = 63; i >= 0; i--) { /* Entering lower power range, reset * edge flag and set pcdac_tmp to lower * power curve.*/ if (edge_flag == 0x40 && (2 * pwr <= (table_max[1] - table_min[0]) || pwr == 0)) { edge_flag = 0x00; pcdac_tmp = pcdac_low_pwr; pwr = mid_pwr_idx / 2; } /* Don't go below 1, extrapolate below if we have * already switched to the lower power curve -or * we only have one curve and edge_flag is zero * anyway */ if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) { while (i >= 0) { pcdac_out[i] = pcdac_out[i + 1]; i--; } break; } pcdac_out[i] = pcdac_tmp[pwr] | edge_flag; /* Extrapolate above if pcdac is greater than * 126 -this can happen because we OR pcdac_out * value with edge_flag on high power curve */ if (pcdac_out[i] > 126) pcdac_out[i] = 126; /* Decrease by a 0.5dB step */ pwr--; } } /** * ath5k_write_pcdac_table() - Write the PCDAC values on hw * @ah: The &struct ath5k_hw */ static void ath5k_write_pcdac_table(struct ath5k_hw *ah) { u8 *pcdac_out = ah->ah_txpower.txp_pd_table; int i; /* * Write TX power values */ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { ath5k_hw_reg_write(ah, (((pcdac_out[2 * i + 0] << 8 | 0xff) & 0xffff) << 0) | (((pcdac_out[2 * i + 1] << 8 | 0xff) & 0xffff) << 16), AR5K_PHY_PCDAC_TXPOWER(i)); } } /* * Power to PDADC table functions */ /** * DOC: Power to PDADC table functions * * For RF2413 and later we have a Power to PDADC table (Power Detector) * instead of a PCDAC (Power Control) and 4 pd gain curves for each * calibrated channel. Each curve has power on x axis in 0.5 db steps and * PDADC steps on y axis and looks like an exponential function like the * RF5111 curve. * * To recreate the curves we read the points from eeprom (eeprom.c) * and interpolate here. Note that in most cases only 2 (higher and lower) * curves are used (like RF5112) but vendors have the opportunity to include * all 4 curves on eeprom. The final curve (higher power) has an extra * point for better accuracy like RF5112. * * The process is similar to what we do above for RF5111/5112 */ /** * ath5k_combine_pwr_to_pdadc_curves() - Combine the various PDADC curves * @ah: The &struct ath5k_hw * @pwr_min: Minimum power (x min) * @pwr_max: Maximum power (x max) * @pdcurves: Number of available curves * * Combine the various pd curves and create the final Power to PDADC table * We can have up to 4 pd curves, we need to do a similar process * as we do for RF5112. This time we don't have an edge_flag but we * set the gain boundaries on a separate register. */ static void ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah, s16 *pwr_min, s16 *pwr_max, u8 pdcurves) { u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS]; u8 *pdadc_out = ah->ah_txpower.txp_pd_table; u8 *pdadc_tmp; s16 pdadc_0; u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size; u8 pd_gain_overlap; /* Note: Register value is initialized on initvals * there is no feedback from hw. * XXX: What about pd_gain_overlap from EEPROM ? */ pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) & AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP; /* Create final PDADC table */ for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) { pdadc_tmp = ah->ah_txpower.tmpL[pdg]; if (pdg == pdcurves - 1) /* 2 dB boundary stretch for last * (higher power) curve */ gain_boundaries[pdg] = pwr_max[pdg] + 4; else /* Set gain boundary in the middle * between this curve and the next one */ gain_boundaries[pdg] = (pwr_max[pdg] + pwr_min[pdg + 1]) / 2; /* Sanity check in case our 2 db stretch got out of * range. */ if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER) gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER; /* For the first curve (lower power) * start from 0 dB */ if (pdg == 0) pdadc_0 = 0; else /* For the other curves use the gain overlap */ pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) - pd_gain_overlap; /* Force each power step to be at least 0.5 dB */ if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1) pwr_step = pdadc_tmp[1] - pdadc_tmp[0]; else pwr_step = 1; /* If pdadc_0 is negative, we need to extrapolate * below this pdgain by a number of pwr_steps */ while ((pdadc_0 < 0) && (pdadc_i < 128)) { s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step; pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp; pdadc_0++; } /* Set last pwr level, using gain boundaries */ pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg]; /* Limit it to be inside pwr range */ table_size = pwr_max[pdg] - pwr_min[pdg]; max_idx = (pdadc_n < table_size) ? pdadc_n : table_size; /* Fill pdadc_out table */ while (pdadc_0 < max_idx && pdadc_i < 128) pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++]; /* Need to extrapolate above this pdgain? */ if (pdadc_n <= max_idx) continue; /* Force each power step to be at least 0.5 dB */ if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1) pwr_step = pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]; else pwr_step = 1; /* Extrapolate above */ while ((pdadc_0 < (s16) pdadc_n) && (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) { s16 tmp = pdadc_tmp[table_size - 1] + (pdadc_0 - max_idx) * pwr_step; pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp; pdadc_0++; } } while (pdg < AR5K_EEPROM_N_PD_GAINS) { gain_boundaries[pdg] = gain_boundaries[pdg - 1]; pdg++; } while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) { pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1]; pdadc_i++; } /* Set gain boundaries */ ath5k_hw_reg_write(ah, AR5K_REG_SM(pd_gain_overlap, AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) | AR5K_REG_SM(gain_boundaries[0], AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) | AR5K_REG_SM(gain_boundaries[1], AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) | AR5K_REG_SM(gain_boundaries[2], AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) | AR5K_REG_SM(gain_boundaries[3], AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4), AR5K_PHY_TPC_RG5); /* Used for setting rate power table */ ah->ah_txpower.txp_min_idx = pwr_min[0]; } /** * ath5k_write_pwr_to_pdadc_table() - Write the PDADC values on hw * @ah: The &struct ath5k_hw * @ee_mode: One of enum ath5k_driver_mode */ static void ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u8 *pdadc_out = ah->ah_txpower.txp_pd_table; u8 *pdg_to_idx = ee->ee_pdc_to_idx[ee_mode]; u8 pdcurves = ee->ee_pd_gains[ee_mode]; u32 reg; u8 i; /* Select the right pdgain curves */ /* Clear current settings */ reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1); reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 | AR5K_PHY_TPC_RG1_PDGAIN_2 | AR5K_PHY_TPC_RG1_PDGAIN_3 | AR5K_PHY_TPC_RG1_NUM_PD_GAIN); /* * Use pd_gains curve from eeprom * * This overrides the default setting from initvals * in case some vendors (e.g. Zcomax) don't use the default * curves. If we don't honor their settings we 'll get a * 5dB (1 * gain overlap ?) drop. */ reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN); switch (pdcurves) { case 3: reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3); /* Fall through */ case 2: reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2); /* Fall through */ case 1: reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1); break; } ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1); /* * Write TX power values */ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { u32 val = get_unaligned_le32(&pdadc_out[4 * i]); ath5k_hw_reg_write(ah, val, AR5K_PHY_PDADC_TXPOWER(i)); } } /* * Common code for PCDAC/PDADC tables */ /** * ath5k_setup_channel_powertable() - Set up power table for this channel * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * @ee_mode: One of enum ath5k_driver_mode * @type: One of enum ath5k_powertable_type (eeprom.h) * * This is the main function that uses all of the above * to set PCDAC/PDADC table on hw for the current channel. * This table is used for tx power calibration on the baseband, * without it we get weird tx power levels and in some cases * distorted spectral mask */ static int ath5k_setup_channel_powertable(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 ee_mode, u8 type) { struct ath5k_pdgain_info *pdg_L, *pdg_R; struct ath5k_chan_pcal_info *pcinfo_L; struct ath5k_chan_pcal_info *pcinfo_R; struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode]; s16 table_min[AR5K_EEPROM_N_PD_GAINS]; s16 table_max[AR5K_EEPROM_N_PD_GAINS]; u8 *tmpL; u8 *tmpR; u32 target = channel->center_freq; int pdg, i; /* Get surrounding freq piers for this channel */ ath5k_get_chan_pcal_surrounding_piers(ah, channel, &pcinfo_L, &pcinfo_R); /* Loop over pd gain curves on * surrounding freq piers by index */ for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) { /* Fill curves in reverse order * from lower power (max gain) * to higher power. Use curve -> idx * backmapping we did on eeprom init */ u8 idx = pdg_curve_to_idx[pdg]; /* Grab the needed curves by index */ pdg_L = &pcinfo_L->pd_curves[idx]; pdg_R = &pcinfo_R->pd_curves[idx]; /* Initialize the temp tables */ tmpL = ah->ah_txpower.tmpL[pdg]; tmpR = ah->ah_txpower.tmpR[pdg]; /* Set curve's x boundaries and create * curves so that they cover the same * range (if we don't do that one table * will have values on some range and the * other one won't have any so interpolation * will fail) */ table_min[pdg] = min(pdg_L->pd_pwr[0], pdg_R->pd_pwr[0]) / 2; table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1], pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2; /* Now create the curves on surrounding channels * and interpolate if needed to get the final * curve for this gain on this channel */ switch (type) { case AR5K_PWRTABLE_LINEAR_PCDAC: /* Override min/max so that we don't loose * accuracy (don't divide by 2) */ table_min[pdg] = min(pdg_L->pd_pwr[0], pdg_R->pd_pwr[0]); table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1], pdg_R->pd_pwr[pdg_R->pd_points - 1]); /* Override minimum so that we don't get * out of bounds while extrapolating * below. Don't do this when we have 2 * curves and we are on the high power curve * because table_min is ok in this case */ if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) { table_min[pdg] = ath5k_get_linear_pcdac_min(pdg_L->pd_step, pdg_R->pd_step, pdg_L->pd_pwr, pdg_R->pd_pwr); /* Don't go too low because we will * miss the upper part of the curve. * Note: 126 = 31.5dB (max power supported) * in 0.25dB units */ if (table_max[pdg] - table_min[pdg] > 126) table_min[pdg] = table_max[pdg] - 126; } /* Fall through */ case AR5K_PWRTABLE_PWR_TO_PCDAC: case AR5K_PWRTABLE_PWR_TO_PDADC: ath5k_create_power_curve(table_min[pdg], table_max[pdg], pdg_L->pd_pwr, pdg_L->pd_step, pdg_L->pd_points, tmpL, type); /* We are in a calibration * pier, no need to interpolate * between freq piers */ if (pcinfo_L == pcinfo_R) continue; ath5k_create_power_curve(table_min[pdg], table_max[pdg], pdg_R->pd_pwr, pdg_R->pd_step, pdg_R->pd_points, tmpR, type); break; default: return -EINVAL; } /* Interpolate between curves * of surrounding freq piers to * get the final curve for this * pd gain. Re-use tmpL for interpolation * output */ for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) && (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { tmpL[i] = (u8) ath5k_get_interpolated_value(target, (s16) pcinfo_L->freq, (s16) pcinfo_R->freq, (s16) tmpL[i], (s16) tmpR[i]); } } /* Now we have a set of curves for this * channel on tmpL (x range is table_max - table_min * and y values are tmpL[pdg][]) sorted in the same * order as EEPROM (because we've used the backmapping). * So for RF5112 it's from higher power to lower power * and for RF2413 it's from lower power to higher power. * For RF5111 we only have one curve. */ /* Fill min and max power levels for this * channel by interpolating the values on * surrounding channels to complete the dataset */ ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target, (s16) pcinfo_L->freq, (s16) pcinfo_R->freq, pcinfo_L->min_pwr, pcinfo_R->min_pwr); ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target, (s16) pcinfo_L->freq, (s16) pcinfo_R->freq, pcinfo_L->max_pwr, pcinfo_R->max_pwr); /* Fill PCDAC/PDADC table */ switch (type) { case AR5K_PWRTABLE_LINEAR_PCDAC: /* For RF5112 we can have one or two curves * and each curve covers a certain power lvl * range so we need to do some more processing */ ath5k_combine_linear_pcdac_curves(ah, table_min, table_max, ee->ee_pd_gains[ee_mode]); /* Set txp.offset so that we can * match max power value with max * table index */ ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2); break; case AR5K_PWRTABLE_PWR_TO_PCDAC: /* We are done for RF5111 since it has only * one curve, just fit the curve on the table */ ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max); /* No rate powertable adjustment for RF5111 */ ah->ah_txpower.txp_min_idx = 0; ah->ah_txpower.txp_offset = 0; break; case AR5K_PWRTABLE_PWR_TO_PDADC: /* Set PDADC boundaries and fill * final PDADC table */ ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max, ee->ee_pd_gains[ee_mode]); /* Set txp.offset, note that table_min * can be negative */ ah->ah_txpower.txp_offset = table_min[0]; break; default: return -EINVAL; } ah->ah_txpower.txp_setup = true; return 0; } /** * ath5k_write_channel_powertable() - Set power table for current channel on hw * @ah: The &struct ath5k_hw * @ee_mode: One of enum ath5k_driver_mode * @type: One of enum ath5k_powertable_type (eeprom.h) */ static void ath5k_write_channel_powertable(struct ath5k_hw *ah, u8 ee_mode, u8 type) { if (type == AR5K_PWRTABLE_PWR_TO_PDADC) ath5k_write_pwr_to_pdadc_table(ah, ee_mode); else ath5k_write_pcdac_table(ah); } /** * DOC: Per-rate tx power setting * * This is the code that sets the desired tx power limit (below * maximum) on hw for each rate (we also have TPC that sets * power per packet type). We do that by providing an index on the * PCDAC/PDADC table we set up above, for each rate. * * For now we only limit txpower based on maximum tx power * supported by hw (what's inside rate_info) + conformance test * limits. We need to limit this even more, based on regulatory domain * etc to be safe. Normally this is done from above so we don't care * here, all we care is that the tx power we set will be O.K. * for the hw (e.g. won't create noise on PA etc). * * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps - * x values) and is indexed as follows: * rates[0] - rates[7] -> OFDM rates * rates[8] - rates[14] -> CCK rates * rates[15] -> XR rates (they all have the same power) */ /** * ath5k_setup_rate_powertable() - Set up rate power table for a given tx power * @ah: The &struct ath5k_hw * @max_pwr: The maximum tx power requested in 0.5dB steps * @rate_info: The &struct ath5k_rate_pcal_info to fill * @ee_mode: One of enum ath5k_driver_mode */ static void ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, struct ath5k_rate_pcal_info *rate_info, u8 ee_mode) { unsigned int i; u16 *rates; s16 rate_idx_scaled = 0; /* max_pwr is power level we got from driver/user in 0.5dB * units, switch to 0.25dB units so we can compare */ max_pwr *= 2; max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2; /* apply rate limits */ rates = ah->ah_txpower.txp_rates_power_table; /* OFDM rates 6 to 24Mb/s */ for (i = 0; i < 5; i++) rates[i] = min(max_pwr, rate_info->target_power_6to24); /* Rest OFDM rates */ rates[5] = min(rates[0], rate_info->target_power_36); rates[6] = min(rates[0], rate_info->target_power_48); rates[7] = min(rates[0], rate_info->target_power_54); /* CCK rates */ /* 1L */ rates[8] = min(rates[0], rate_info->target_power_6to24); /* 2L */ rates[9] = min(rates[0], rate_info->target_power_36); /* 2S */ rates[10] = min(rates[0], rate_info->target_power_36); /* 5L */ rates[11] = min(rates[0], rate_info->target_power_48); /* 5S */ rates[12] = min(rates[0], rate_info->target_power_48); /* 11L */ rates[13] = min(rates[0], rate_info->target_power_54); /* 11S */ rates[14] = min(rates[0], rate_info->target_power_54); /* XR rates */ rates[15] = min(rates[0], rate_info->target_power_6to24); /* CCK rates have different peak to average ratio * so we have to tweak their power so that gainf * correction works ok. For this we use OFDM to * CCK delta from eeprom */ if ((ee_mode == AR5K_EEPROM_MODE_11G) && (ah->ah_phy_revision < AR5K_SREV_PHY_5212A)) for (i = 8; i <= 15; i++) rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta; /* Save min/max and current tx power for this channel * in 0.25dB units. * * Note: We use rates[0] for current tx power because * it covers most of the rates, in most cases. It's our * tx power limit and what the user expects to see. */ ah->ah_txpower.txp_min_pwr = 2 * rates[7]; ah->ah_txpower.txp_cur_pwr = 2 * rates[0]; /* Set max txpower for correct OFDM operation on all rates * -that is the txpower for 54Mbit-, it's used for the PAPD * gain probe and it's in 0.5dB units */ ah->ah_txpower.txp_ofdm = rates[7]; /* Now that we have all rates setup use table offset to * match the power range set by user with the power indices * on PCDAC/PDADC table */ for (i = 0; i < 16; i++) { rate_idx_scaled = rates[i] + ah->ah_txpower.txp_offset; /* Don't get out of bounds */ if (rate_idx_scaled > 63) rate_idx_scaled = 63; if (rate_idx_scaled < 0) rate_idx_scaled = 0; rates[i] = rate_idx_scaled; } } /** * ath5k_hw_txpower() - Set transmission power limit for a given channel * @ah: The &struct ath5k_hw * @channel: The &struct ieee80211_channel * @txpower: Requested tx power in 0.5dB steps * * Combines all of the above to set the requested tx power limit * on hw. */ static int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 txpower) { struct ath5k_rate_pcal_info rate_info; struct ieee80211_channel *curr_channel = ah->ah_current_channel; int ee_mode; u8 type; int ret; if (txpower > AR5K_TUNE_MAX_TXPOWER) { ATH5K_ERR(ah, "invalid tx power: %u\n", txpower); return -EINVAL; } ee_mode = ath5k_eeprom_mode_from_channel(channel); if (ee_mode < 0) { ATH5K_ERR(ah, "invalid channel: %d\n", channel->center_freq); return -EINVAL; } /* Initialize TX power table */ switch (ah->ah_radio) { case AR5K_RF5110: /* TODO */ return 0; case AR5K_RF5111: type = AR5K_PWRTABLE_PWR_TO_PCDAC; break; case AR5K_RF5112: type = AR5K_PWRTABLE_LINEAR_PCDAC; break; case AR5K_RF2413: case AR5K_RF5413: case AR5K_RF2316: case AR5K_RF2317: case AR5K_RF2425: type = AR5K_PWRTABLE_PWR_TO_PDADC; break; default: return -EINVAL; } /* * If we don't change channel/mode skip tx powertable calculation * and use the cached one. */ if (!ah->ah_txpower.txp_setup || (channel->hw_value != curr_channel->hw_value) || (channel->center_freq != curr_channel->center_freq)) { /* Reset TX power values but preserve requested * tx power from above */ int requested_txpower = ah->ah_txpower.txp_requested; memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); /* Restore TPC setting and requested tx power */ ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; ah->ah_txpower.txp_requested = requested_txpower; /* Calculate the powertable */ ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type); if (ret) return ret; } /* Write table on hw */ ath5k_write_channel_powertable(ah, ee_mode, type); /* Limit max power if we have a CTL available */ ath5k_get_max_ctl_power(ah, channel); /* FIXME: Antenna reduction stuff */ /* FIXME: Limit power on turbo modes */ /* FIXME: TPC scale reduction */ /* Get surrounding channels for per-rate power table * calibration */ ath5k_get_rate_pcal_data(ah, channel, &rate_info); /* Setup rate power table */ ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode); /* Write rate power table on hw */ ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) | AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) | AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1); ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) | AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) | AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2); ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) | AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) | AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3); ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) | AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) | AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4); /* FIXME: TPC support */ if (ah->ah_txpower.txp_tpc) { ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE | AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); ath5k_hw_reg_write(ah, AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) | AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) | AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP), AR5K_TPC); } else { ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX | AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); } return 0; } /** * ath5k_hw_set_txpower_limit() - Set txpower limit for the current channel * @ah: The &struct ath5k_hw * @txpower: The requested tx power limit in 0.5dB steps * * This function provides access to ath5k_hw_txpower to the driver in * case user or an application changes it while PHY is running. */ int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) { ATH5K_DBG(ah, ATH5K_DEBUG_TXPOWER, "changing txpower to %d\n", txpower); return ath5k_hw_txpower(ah, ah->ah_current_channel, txpower); } /*************\ Init function \*************/ /** * ath5k_hw_phy_init() - Initialize PHY * @ah: The &struct ath5k_hw * @channel: The @struct ieee80211_channel * @mode: One of enum ath5k_driver_mode * @fast: Try a fast channel switch instead * * This is the main function used during reset to initialize PHY * or do a fast channel change if possible. * * NOTE: Do not call this one from the driver, it assumes PHY is in a * warm reset state ! */ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 mode, bool fast) { struct ieee80211_channel *curr_channel; int ret, i; u32 phy_tst1; ret = 0; /* * Sanity check for fast flag * Don't try fast channel change when changing modulation * mode/band. We check for chip compatibility on * ath5k_hw_reset. */ curr_channel = ah->ah_current_channel; if (fast && (channel->hw_value != curr_channel->hw_value)) return -EINVAL; /* * On fast channel change we only set the synth parameters * while PHY is running, enable calibration and skip the rest. */ if (fast) { AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_RFBUS_REQ, AR5K_PHY_RFBUS_REQ_REQUEST); for (i = 0; i < 100; i++) { if (ath5k_hw_reg_read(ah, AR5K_PHY_RFBUS_GRANT)) break; udelay(5); } /* Failed */ if (i >= 100) return -EIO; /* Set channel and wait for synth */ ret = ath5k_hw_channel(ah, channel); if (ret) return ret; ath5k_hw_wait_for_synth(ah, channel); } /* * Set TX power * * Note: We need to do that before we set * RF buffer settings on 5211/5212+ so that we * properly set curve indices. */ ret = ath5k_hw_txpower(ah, channel, ah->ah_txpower.txp_requested ? ah->ah_txpower.txp_requested * 2 : AR5K_TUNE_MAX_TXPOWER); if (ret) return ret; /* Write OFDM timings on 5212*/ if (ah->ah_version == AR5K_AR5212 && channel->hw_value != AR5K_MODE_11B) { ret = ath5k_hw_write_ofdm_timings(ah, channel); if (ret) return ret; /* Spur info is available only from EEPROM versions * greater than 5.3, but the EEPROM routines will use * static values for older versions */ if (ah->ah_mac_srev >= AR5K_SREV_AR5424) ath5k_hw_set_spur_mitigation_filter(ah, channel); } /* If we used fast channel switching * we are done, release RF bus and * fire up NF calibration. * * Note: Only NF calibration due to * channel change, not AGC calibration * since AGC is still running ! */ if (fast) { /* * Release RF Bus grant */ AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_RFBUS_REQ, AR5K_PHY_RFBUS_REQ_REQUEST); /* * Start NF calibration */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF); return ret; } /* * For 5210 we do all initialization using * initvals, so we don't have to modify * any settings (5210 also only supports * a/aturbo modes) */ if (ah->ah_version != AR5K_AR5210) { /* * Write initial RF gain settings * This should work for both 5111/5112 */ ret = ath5k_hw_rfgain_init(ah, channel->band); if (ret) return ret; usleep_range(1000, 1500); /* * Write RF buffer */ ret = ath5k_hw_rfregs_init(ah, channel, mode); if (ret) return ret; /*Enable/disable 802.11b mode on 5111 (enable 2111 frequency converter + CCK)*/ if (ah->ah_radio == AR5K_RF5111) { if (mode == AR5K_MODE_11B) AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_B_MODE); else AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_B_MODE); } } else if (ah->ah_version == AR5K_AR5210) { usleep_range(1000, 1500); /* Disable phy and wait */ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); usleep_range(1000, 1500); } /* Set channel on PHY */ ret = ath5k_hw_channel(ah, channel); if (ret) return ret; /* * Enable the PHY and wait until completion * This includes BaseBand and Synthesizer * activation. */ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); ath5k_hw_wait_for_synth(ah, channel); /* * Perform ADC test to see if baseband is ready * Set tx hold and check adc test register */ phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1); ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1); for (i = 0; i <= 20; i++) { if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10)) break; usleep_range(200, 250); } ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1); /* * Start automatic gain control calibration * * During AGC calibration RX path is re-routed to * a power detector so we don't receive anything. * * This method is used to calibrate some static offsets * used together with on-the fly I/Q calibration (the * one performed via ath5k_hw_phy_calibrate), which doesn't * interrupt rx path. * * While rx path is re-routed to the power detector we also * start a noise floor calibration to measure the * card's noise floor (the noise we measure when we are not * transmitting or receiving anything). * * If we are in a noisy environment, AGC calibration may time * out and/or noise floor calibration might timeout. */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF); /* At the same time start I/Q calibration for QAM constellation * -no need for CCK- */ ah->ah_iq_cal_needed = false; if (!(mode == AR5K_MODE_11B)) { ah->ah_iq_cal_needed = true; AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN); } /* Wait for gain calibration to finish (we check for I/Q calibration * during ath5k_phy_calibrate) */ if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL, 0, false)) { ATH5K_ERR(ah, "gain calibration timeout (%uMHz)\n", channel->center_freq); } /* Restore antenna mode */ ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); return ret; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/qcu.c0000644000175000017500000005074012026211315023213 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /********************************************\ Queue Control Unit, DCF Control Unit Functions \********************************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "ath5k.h" #include "reg.h" #include "debug.h" #include /** * DOC: Queue Control Unit (QCU)/DCF Control Unit (DCU) functions * * Here we setup parameters for the 12 available TX queues. Note that * on the various registers we can usually only map the first 10 of them so * basically we have 10 queues to play with. Each queue has a matching * QCU that controls when the queue will get triggered and multiple QCUs * can be mapped to a single DCU that controls the various DFS parameters * for the various queues. In our setup we have a 1:1 mapping between QCUs * and DCUs allowing us to have different DFS settings for each queue. * * When a frame goes into a TX queue, QCU decides when it'll trigger a * transmission based on various criteria (such as how many data we have inside * it's buffer or -if it's a beacon queue- if it's time to fire up the queue * based on TSF etc), DCU adds backoff, IFSes etc and then a scheduler * (arbitrator) decides the priority of each QCU based on it's configuration * (e.g. beacons are always transmitted when they leave DCU bypassing all other * frames from other queues waiting to be transmitted). After a frame leaves * the DCU it goes to PCU for further processing and then to PHY for * the actual transmission. */ /******************\ * Helper functions * \******************/ /** * ath5k_hw_num_tx_pending() - Get number of pending frames for a given queue * @ah: The &struct ath5k_hw * @queue: One of enum ath5k_tx_queue_id */ u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) { u32 pending; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); /* Return if queue is declared inactive */ if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) return false; /* XXX: How about AR5K_CFG_TXCNT ? */ if (ah->ah_version == AR5K_AR5210) return false; pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue)); pending &= AR5K_QCU_STS_FRMPENDCNT; /* It's possible to have no frames pending even if TXE * is set. To indicate that q has not stopped return * true */ if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) return true; return pending; } /** * ath5k_hw_release_tx_queue() - Set a transmit queue inactive * @ah: The &struct ath5k_hw * @queue: One of enum ath5k_tx_queue_id */ void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) { if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num)) return; /* This queue will be skipped in further operations */ ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE; /*For SIMR setup*/ AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue); } /** * ath5k_cw_validate() - Make sure the given cw is valid * @cw_req: The contention window value to check * * Make sure cw is a power of 2 minus 1 and smaller than 1024 */ static u16 ath5k_cw_validate(u16 cw_req) { cw_req = min(cw_req, (u16)1023); /* Check if cw_req + 1 a power of 2 */ if (is_power_of_2(cw_req + 1)) return cw_req; /* Check if cw_req is a power of 2 */ if (is_power_of_2(cw_req)) return cw_req - 1; /* If none of the above is correct * find the closest power of 2 */ cw_req = (u16) roundup_pow_of_two(cw_req) - 1; return cw_req; } /** * ath5k_hw_get_tx_queueprops() - Get properties for a transmit queue * @ah: The &struct ath5k_hw * @queue: One of enum ath5k_tx_queue_id * @queue_info: The &struct ath5k_txq_info to fill */ int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, struct ath5k_txq_info *queue_info) { memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info)); return 0; } /** * ath5k_hw_set_tx_queueprops() - Set properties for a transmit queue * @ah: The &struct ath5k_hw * @queue: One of enum ath5k_tx_queue_id * @qinfo: The &struct ath5k_txq_info to use * * Returns 0 on success or -EIO if queue is inactive */ int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, const struct ath5k_txq_info *qinfo) { struct ath5k_txq_info *qi; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); qi = &ah->ah_txq[queue]; if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE) return -EIO; /* copy and validate values */ qi->tqi_type = qinfo->tqi_type; qi->tqi_subtype = qinfo->tqi_subtype; qi->tqi_flags = qinfo->tqi_flags; /* * According to the docs: Although the AIFS field is 8 bit wide, * the maximum supported value is 0xFC. Setting it higher than that * will cause the DCU to hang. */ qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC); qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min); qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max); qi->tqi_cbr_period = qinfo->tqi_cbr_period; qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit; qi->tqi_burst_time = qinfo->tqi_burst_time; qi->tqi_ready_time = qinfo->tqi_ready_time; /*XXX: Is this supported on 5210 ?*/ /*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/ if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA && ((qinfo->tqi_subtype == AR5K_WME_AC_VI) || (qinfo->tqi_subtype == AR5K_WME_AC_VO))) || qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD) qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; return 0; } /** * ath5k_hw_setup_tx_queue() - Initialize a transmit queue * @ah: The &struct ath5k_hw * @queue_type: One of enum ath5k_tx_queue * @queue_info: The &struct ath5k_txq_info to use * * Returns 0 on success, -EINVAL on invalid arguments */ int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, struct ath5k_txq_info *queue_info) { unsigned int queue; int ret; /* * Get queue by type */ /* 5210 only has 2 queues */ if (ah->ah_capabilities.cap_queues.q_tx_num == 2) { switch (queue_type) { case AR5K_TX_QUEUE_DATA: queue = AR5K_TX_QUEUE_ID_NOQCU_DATA; break; case AR5K_TX_QUEUE_BEACON: case AR5K_TX_QUEUE_CAB: queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON; break; default: return -EINVAL; } } else { switch (queue_type) { case AR5K_TX_QUEUE_DATA: for (queue = AR5K_TX_QUEUE_ID_DATA_MIN; ah->ah_txq[queue].tqi_type != AR5K_TX_QUEUE_INACTIVE; queue++) { if (queue > AR5K_TX_QUEUE_ID_DATA_MAX) return -EINVAL; } break; case AR5K_TX_QUEUE_UAPSD: queue = AR5K_TX_QUEUE_ID_UAPSD; break; case AR5K_TX_QUEUE_BEACON: queue = AR5K_TX_QUEUE_ID_BEACON; break; case AR5K_TX_QUEUE_CAB: queue = AR5K_TX_QUEUE_ID_CAB; break; default: return -EINVAL; } } /* * Setup internal queue structure */ memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info)); ah->ah_txq[queue].tqi_type = queue_type; if (queue_info != NULL) { queue_info->tqi_type = queue_type; ret = ath5k_hw_set_tx_queueprops(ah, queue, queue_info); if (ret) return ret; } /* * We use ah_txq_status to hold a temp value for * the Secondary interrupt mask registers on 5211+ * check out ath5k_hw_reset_tx_queue */ AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue); return queue; } /*******************************\ * Single QCU/DCU initialization * \*******************************/ /** * ath5k_hw_set_tx_retry_limits() - Set tx retry limits on DCU * @ah: The &struct ath5k_hw * @queue: One of enum ath5k_tx_queue_id * * This function is used when initializing a queue, to set * retry limits based on ah->ah_retry_* and the chipset used. */ void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, unsigned int queue) { /* Single data queue on AR5210 */ if (ah->ah_version == AR5K_AR5210) { struct ath5k_txq_info *tq = &ah->ah_txq[queue]; if (queue > 0) return; ath5k_hw_reg_write(ah, (tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) | AR5K_REG_SM(ah->ah_retry_long, AR5K_NODCU_RETRY_LMT_SLG_RETRY) | AR5K_REG_SM(ah->ah_retry_short, AR5K_NODCU_RETRY_LMT_SSH_RETRY) | AR5K_REG_SM(ah->ah_retry_long, AR5K_NODCU_RETRY_LMT_LG_RETRY) | AR5K_REG_SM(ah->ah_retry_short, AR5K_NODCU_RETRY_LMT_SH_RETRY), AR5K_NODCU_RETRY_LMT); /* DCU on AR5211+ */ } else { ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_retry_long, AR5K_DCU_RETRY_LMT_RTS) | AR5K_REG_SM(ah->ah_retry_long, AR5K_DCU_RETRY_LMT_STA_RTS) | AR5K_REG_SM(max(ah->ah_retry_long, ah->ah_retry_short), AR5K_DCU_RETRY_LMT_STA_DATA), AR5K_QUEUE_DFS_RETRY_LIMIT(queue)); } } /** * ath5k_hw_reset_tx_queue() - Initialize a single hw queue * @ah: The &struct ath5k_hw * @queue: One of enum ath5k_tx_queue_id * * Set DCF properties for the given transmit queue on DCU * and configures all queue-specific parameters. */ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) { struct ath5k_txq_info *tq = &ah->ah_txq[queue]; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); tq = &ah->ah_txq[queue]; /* Skip if queue inactive or if we are on AR5210 * that doesn't have QCU/DCU */ if ((ah->ah_version == AR5K_AR5210) || (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)) return 0; /* * Set contention window (cw_min/cw_max) * and arbitrated interframe space (aifs)... */ ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS), AR5K_QUEUE_DFS_LOCAL_IFS(queue)); /* * Set tx retry limits for this queue */ ath5k_hw_set_tx_retry_limits(ah, queue); /* * Set misc registers */ /* Enable DCU to wait for next fragment from QCU */ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), AR5K_DCU_MISC_FRAG_WAIT); /* On Maui and Spirit use the global seqnum on DCU */ if (ah->ah_mac_version < AR5K_SREV_AR5211) AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), AR5K_DCU_MISC_SEQNUM_CTL); /* Constant bit rate period */ if (tq->tqi_cbr_period) { ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period, AR5K_QCU_CBRCFG_INTVAL) | AR5K_REG_SM(tq->tqi_cbr_overflow_limit, AR5K_QCU_CBRCFG_ORN_THRES), AR5K_QUEUE_CBRCFG(queue)); AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_FRSHED_CBR); if (tq->tqi_cbr_overflow_limit) AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_CBR_THRES_ENABLE); } /* Ready time interval */ if (tq->tqi_ready_time && (tq->tqi_type != AR5K_TX_QUEUE_CAB)) ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time, AR5K_QCU_RDYTIMECFG_INTVAL) | AR5K_QCU_RDYTIMECFG_ENABLE, AR5K_QUEUE_RDYTIMECFG(queue)); if (tq->tqi_burst_time) { ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time, AR5K_DCU_CHAN_TIME_DUR) | AR5K_DCU_CHAN_TIME_ENABLE, AR5K_QUEUE_DFS_CHANNEL_TIME(queue)); if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_RDY_VEOL_POLICY); } /* Enable/disable Post frame backoff */ if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS, AR5K_QUEUE_DFS_MISC(queue)); /* Enable/disable fragmentation burst backoff */ if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG, AR5K_QUEUE_DFS_MISC(queue)); /* * Set registers by queue type */ switch (tq->tqi_type) { case AR5K_TX_QUEUE_BEACON: AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_FRSHED_DBA_GT | AR5K_QCU_MISC_CBREXP_BCN_DIS | AR5K_QCU_MISC_BCN_ENABLE); AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << AR5K_DCU_MISC_ARBLOCK_CTL_S) | AR5K_DCU_MISC_ARBLOCK_IGNORE | AR5K_DCU_MISC_POST_FR_BKOFF_DIS | AR5K_DCU_MISC_BCN_ENABLE); break; case AR5K_TX_QUEUE_CAB: /* XXX: use BCN_SENT_GT, if we can figure out how */ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_FRSHED_DBA_GT | AR5K_QCU_MISC_CBREXP_DIS | AR5K_QCU_MISC_CBREXP_BCN_DIS); ath5k_hw_reg_write(ah, ((tq->tqi_ready_time - (AR5K_TUNE_SW_BEACON_RESP - AR5K_TUNE_DMA_BEACON_RESP) - AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | AR5K_QCU_RDYTIMECFG_ENABLE, AR5K_QUEUE_RDYTIMECFG(queue)); AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << AR5K_DCU_MISC_ARBLOCK_CTL_S)); break; case AR5K_TX_QUEUE_UAPSD: AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_CBREXP_DIS); break; case AR5K_TX_QUEUE_DATA: default: break; } /* TODO: Handle frame compression */ /* * Enable interrupts for this tx queue * in the secondary interrupt mask registers */ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue); if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE) AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue); /* Update secondary interrupt mask registers */ /* Filter out inactive queues */ ah->ah_txq_imr_txok &= ah->ah_txq_status; ah->ah_txq_imr_txerr &= ah->ah_txq_status; ah->ah_txq_imr_txurn &= ah->ah_txq_status; ah->ah_txq_imr_txdesc &= ah->ah_txq_status; ah->ah_txq_imr_txeol &= ah->ah_txq_status; ah->ah_txq_imr_cbrorn &= ah->ah_txq_status; ah->ah_txq_imr_cbrurn &= ah->ah_txq_status; ah->ah_txq_imr_qtrig &= ah->ah_txq_status; ah->ah_txq_imr_nofrm &= ah->ah_txq_status; ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok, AR5K_SIMR0_QCU_TXOK) | AR5K_REG_SM(ah->ah_txq_imr_txdesc, AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0); ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr, AR5K_SIMR1_QCU_TXERR) | AR5K_REG_SM(ah->ah_txq_imr_txeol, AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1); /* Update SIMR2 but don't overwrite rest simr2 settings */ AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN); AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, AR5K_REG_SM(ah->ah_txq_imr_txurn, AR5K_SIMR2_QCU_TXURN)); ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn, AR5K_SIMR3_QCBRORN) | AR5K_REG_SM(ah->ah_txq_imr_cbrurn, AR5K_SIMR3_QCBRURN), AR5K_SIMR3); ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig, AR5K_SIMR4_QTRIG), AR5K_SIMR4); /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm, AR5K_TXNOFRM_QCU), AR5K_TXNOFRM); /* No queue has TXNOFRM enabled, disable the interrupt * by setting AR5K_TXNOFRM to zero */ if (ah->ah_txq_imr_nofrm == 0) ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM); /* Set QCU mask for this DCU to save power */ AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue); return 0; } /**************************\ * Global QCU/DCU functions * \**************************/ /** * ath5k_hw_set_ifs_intervals() - Set global inter-frame spaces on DCU * @ah: The &struct ath5k_hw * @slot_time: Slot time in us * * Sets the global IFS intervals on DCU (also works on AR5210) for * the given slot time and the current bwmode. */ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) { struct ieee80211_channel *channel = ah->ah_current_channel; enum ieee80211_band band; struct ieee80211_rate *rate; u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock; u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time); if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX) return -EINVAL; sifs = ath5k_hw_get_default_sifs(ah); sifs_clock = ath5k_hw_htoclock(ah, sifs - 2); /* EIFS * Txtime of ack at lowest rate + SIFS + DIFS * (DIFS = SIFS + 2 * Slot time) * * Note: HAL has some predefined values for EIFS * Turbo: (37 + 2 * 6) * Default: (74 + 2 * 9) * Half: (149 + 2 * 13) * Quarter: (298 + 2 * 21) * * (74 + 2 * 6) for AR5210 default and turbo ! * * According to the formula we have * ack_tx_time = 25 for turbo and * ack_tx_time = 42.5 * clock multiplier * for default/half/quarter. * * This can't be right, 42 is what we would get * from ath5k_hw_get_frame_dur_for_bwmode or * ieee80211_generic_frame_duration for zero frame * length and without SIFS ! * * Also we have different lowest rate for 802.11a */ if (channel->band == IEEE80211_BAND_5GHZ) band = IEEE80211_BAND_5GHZ; else band = IEEE80211_BAND_2GHZ; rate = &ah->sbands[band].bitrates[0]; ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false); /* ack_tx_time includes an SIFS already */ eifs = ack_tx_time + sifs + 2 * slot_time; eifs_clock = ath5k_hw_htoclock(ah, eifs); /* Set IFS settings on AR5210 */ if (ah->ah_version == AR5K_AR5210) { u32 pifs, pifs_clock, difs, difs_clock; /* Set slot time */ ath5k_hw_reg_write(ah, slot_time_clock, AR5K_SLOT_TIME); /* Set EIFS */ eifs_clock = AR5K_REG_SM(eifs_clock, AR5K_IFS1_EIFS); /* PIFS = Slot time + SIFS */ pifs = slot_time + sifs; pifs_clock = ath5k_hw_htoclock(ah, pifs); pifs_clock = AR5K_REG_SM(pifs_clock, AR5K_IFS1_PIFS); /* DIFS = SIFS + 2 * Slot time */ difs = sifs + 2 * slot_time; difs_clock = ath5k_hw_htoclock(ah, difs); /* Set SIFS/DIFS */ ath5k_hw_reg_write(ah, (difs_clock << AR5K_IFS0_DIFS_S) | sifs_clock, AR5K_IFS0); /* Set PIFS/EIFS and preserve AR5K_INIT_CARR_SENSE_EN */ ath5k_hw_reg_write(ah, pifs_clock | eifs_clock | (AR5K_INIT_CARR_SENSE_EN << AR5K_IFS1_CS_EN_S), AR5K_IFS1); return 0; } /* Set IFS slot time */ ath5k_hw_reg_write(ah, slot_time_clock, AR5K_DCU_GBL_IFS_SLOT); /* Set EIFS interval */ ath5k_hw_reg_write(ah, eifs_clock, AR5K_DCU_GBL_IFS_EIFS); /* Set SIFS interval in usecs */ AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC, AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC, sifs); /* Set SIFS interval in clock cycles */ ath5k_hw_reg_write(ah, sifs_clock, AR5K_DCU_GBL_IFS_SIFS); return 0; } /** * ath5k_hw_init_queues() - Initialize tx queues * @ah: The &struct ath5k_hw * * Initializes all tx queues based on information on * ah->ah_txq* set by the driver */ int ath5k_hw_init_queues(struct ath5k_hw *ah) { int i, ret; /* TODO: HW Compression support for data queues */ /* TODO: Burst prefetch for data queues */ /* * Reset queues and start beacon timers at the end of the reset routine * This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping * Note: If we want we can assign multiple qcus on one dcu. */ if (ah->ah_version != AR5K_AR5210) for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) { ret = ath5k_hw_reset_tx_queue(ah, i); if (ret) { ATH5K_ERR(ah, "failed to reset TX queue #%d\n", i); return ret; } } else /* No QCU/DCU on AR5210, just set tx * retry limits. We set IFS parameters * on ath5k_hw_set_ifs_intervals */ ath5k_hw_set_tx_retry_limits(ah, 0); /* Set the turbo flag when operating on 40MHz */ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) AR5K_REG_ENABLE_BITS(ah, AR5K_DCU_GBL_IFS_MISC, AR5K_DCU_GBL_IFS_MISC_TURBO_MODE); /* If we didn't set IFS timings through * ath5k_hw_set_coverage_class make sure * we set them here */ if (!ah->ah_coverage_class) { unsigned int slot_time = ath5k_hw_get_default_slottime(ah); ath5k_hw_set_ifs_intervals(ah, slot_time); } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/dma.c0000644000175000017500000005755512026211315023177 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /*************************************\ * DMA and interrupt masking functions * \*************************************/ /** * DOC: DMA and interrupt masking functions * * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and * handle queue setup for 5210 chipset (rest are handled on qcu.c). * Also we setup interrupt mask register (IMR) and read the various interrupt * status registers (ISR). */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "ath5k.h" #include "reg.h" #include "debug.h" /*********\ * Receive * \*********/ /** * ath5k_hw_start_rx_dma() - Start DMA receive * @ah: The &struct ath5k_hw */ void ath5k_hw_start_rx_dma(struct ath5k_hw *ah) { ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); ath5k_hw_reg_read(ah, AR5K_CR); } /** * ath5k_hw_stop_rx_dma() - Stop DMA receive * @ah: The &struct ath5k_hw */ static int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah) { unsigned int i; ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR); /* * It may take some time to disable the DMA receive unit */ for (i = 1000; i > 0 && (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0; i--) udelay(100); if (!i) ATH5K_DBG(ah, ATH5K_DEBUG_DMA, "failed to stop RX DMA !\n"); return i ? 0 : -EBUSY; } /** * ath5k_hw_get_rxdp() - Get RX Descriptor's address * @ah: The &struct ath5k_hw */ u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah) { return ath5k_hw_reg_read(ah, AR5K_RXDP); } /** * ath5k_hw_set_rxdp() - Set RX Descriptor's address * @ah: The &struct ath5k_hw * @phys_addr: RX descriptor address * * Returns -EIO if rx is active */ int ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) { if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) { ATH5K_DBG(ah, ATH5K_DEBUG_DMA, "tried to set RXDP while rx was active !\n"); return -EIO; } ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP); return 0; } /**********\ * Transmit * \**********/ /** * ath5k_hw_start_tx_dma() - Start DMA transmit for a specific queue * @ah: The &struct ath5k_hw * @queue: The hw queue number * * Start DMA transmit for a specific queue and since 5210 doesn't have * QCU/DCU, set up queue parameters for 5210 here based on queue type (one * queue for normal data and one queue for beacons). For queue setup * on newer chips check out qcu.c. Returns -EINVAL if queue number is out * of range or if queue is already disabled. * * NOTE: Must be called after setting up tx control descriptor for that * queue (see below). */ int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue) { u32 tx_queue; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); /* Return if queue is declared inactive */ if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) return -EINVAL; if (ah->ah_version == AR5K_AR5210) { tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); /* * Set the queue by type on 5210 */ switch (ah->ah_txq[queue].tqi_type) { case AR5K_TX_QUEUE_DATA: tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0; break; case AR5K_TX_QUEUE_BEACON: tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, AR5K_BSR); break; case AR5K_TX_QUEUE_CAB: tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, AR5K_BSR); break; default: return -EINVAL; } /* Start queue */ ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); ath5k_hw_reg_read(ah, AR5K_CR); } else { /* Return if queue is disabled */ if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) return -EIO; /* Start queue */ AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue); } return 0; } /** * ath5k_hw_stop_tx_dma() - Stop DMA transmit on a specific queue * @ah: The &struct ath5k_hw * @queue: The hw queue number * * Stop DMA transmit on a specific hw queue and drain queue so we don't * have any pending frames. Returns -EBUSY if we still have pending frames, * -EINVAL if queue number is out of range or inactive. */ static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) { unsigned int i = 40; u32 tx_queue, pending; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); /* Return if queue is declared inactive */ if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) return -EINVAL; if (ah->ah_version == AR5K_AR5210) { tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); /* * Set by queue type */ switch (ah->ah_txq[queue].tqi_type) { case AR5K_TX_QUEUE_DATA: tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0; break; case AR5K_TX_QUEUE_BEACON: case AR5K_TX_QUEUE_CAB: /* XXX Fix me... */ tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1; ath5k_hw_reg_write(ah, 0, AR5K_BSR); break; default: return -EINVAL; } /* Stop queue */ ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); ath5k_hw_reg_read(ah, AR5K_CR); } else { /* * Enable DCU early termination to quickly * flush any pending frames from QCU */ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_DCU_EARLY); /* * Schedule TX disable and wait until queue is empty */ AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue); /* Wait for queue to stop */ for (i = 1000; i > 0 && (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue) != 0); i--) udelay(100); if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) ATH5K_DBG(ah, ATH5K_DEBUG_DMA, "queue %i didn't stop !\n", queue); /* Check for pending frames */ i = 1000; do { pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue)) & AR5K_QCU_STS_FRMPENDCNT; udelay(100); } while (--i && pending); /* For 2413+ order PCU to drop packets using * QUIET mechanism */ if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) && pending) { /* Set periodicity and duration */ ath5k_hw_reg_write(ah, AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)| AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR), AR5K_QUIET_CTL2); /* Enable quiet period for current TSF */ ath5k_hw_reg_write(ah, AR5K_QUIET_CTL1_QT_EN | AR5K_REG_SM(ath5k_hw_reg_read(ah, AR5K_TSF_L32_5211) >> 10, AR5K_QUIET_CTL1_NEXT_QT_TSF), AR5K_QUIET_CTL1); /* Force channel idle high */ AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211, AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); /* Wait a while and disable mechanism */ udelay(400); AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1, AR5K_QUIET_CTL1_QT_EN); /* Re-check for pending frames */ i = 100; do { pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue)) & AR5K_QCU_STS_FRMPENDCNT; udelay(100); } while (--i && pending); AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211, AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); if (pending) ATH5K_DBG(ah, ATH5K_DEBUG_DMA, "quiet mechanism didn't work q:%i !\n", queue); } /* * Disable DCU early termination */ AR5K_REG_DISABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_DCU_EARLY); /* Clear register */ ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD); if (pending) { ATH5K_DBG(ah, ATH5K_DEBUG_DMA, "tx dma didn't stop (q:%i, frm:%i) !\n", queue, pending); return -EBUSY; } } /* TODO: Check for success on 5210 else return error */ return 0; } /** * ath5k_hw_stop_beacon_queue() - Stop beacon queue * @ah: The &struct ath5k_hw * @queue: The queue number * * Returns -EIO if queue didn't stop */ int ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue) { int ret; ret = ath5k_hw_stop_tx_dma(ah, queue); if (ret) { ATH5K_DBG(ah, ATH5K_DEBUG_DMA, "beacon queue didn't stop !\n"); return -EIO; } return 0; } /** * ath5k_hw_get_txdp() - Get TX Descriptor's address for a specific queue * @ah: The &struct ath5k_hw * @queue: The hw queue number * * Get TX descriptor's address for a specific queue. For 5210 we ignore * the queue number and use tx queue type since we only have 2 queues. * We use TXDP0 for normal data queue and TXDP1 for beacon queue. * For newer chips with QCU/DCU we just read the corresponding TXDP register. * * XXX: Is TXDP read and clear ? */ u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) { u16 tx_reg; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); /* * Get the transmit queue descriptor pointer from the selected queue */ /*5210 doesn't have QCU*/ if (ah->ah_version == AR5K_AR5210) { switch (ah->ah_txq[queue].tqi_type) { case AR5K_TX_QUEUE_DATA: tx_reg = AR5K_NOQCU_TXDP0; break; case AR5K_TX_QUEUE_BEACON: case AR5K_TX_QUEUE_CAB: tx_reg = AR5K_NOQCU_TXDP1; break; default: return 0xffffffff; } } else { tx_reg = AR5K_QUEUE_TXDP(queue); } return ath5k_hw_reg_read(ah, tx_reg); } /** * ath5k_hw_set_txdp() - Set TX Descriptor's address for a specific queue * @ah: The &struct ath5k_hw * @queue: The hw queue number * @phys_addr: The physical address * * Set TX descriptor's address for a specific queue. For 5210 we ignore * the queue number and we use tx queue type since we only have 2 queues * so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue. * For newer chips with QCU/DCU we just set the corresponding TXDP register. * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still * active. */ int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) { u16 tx_reg; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); /* * Set the transmit queue descriptor pointer register by type * on 5210 */ if (ah->ah_version == AR5K_AR5210) { switch (ah->ah_txq[queue].tqi_type) { case AR5K_TX_QUEUE_DATA: tx_reg = AR5K_NOQCU_TXDP0; break; case AR5K_TX_QUEUE_BEACON: case AR5K_TX_QUEUE_CAB: tx_reg = AR5K_NOQCU_TXDP1; break; default: return -EINVAL; } } else { /* * Set the transmit queue descriptor pointer for * the selected queue on QCU for 5211+ * (this won't work if the queue is still active) */ if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) return -EIO; tx_reg = AR5K_QUEUE_TXDP(queue); } /* Set descriptor pointer */ ath5k_hw_reg_write(ah, phys_addr, tx_reg); return 0; } /** * ath5k_hw_update_tx_triglevel() - Update tx trigger level * @ah: The &struct ath5k_hw * @increase: Flag to force increase of trigger level * * This function increases/decreases the tx trigger level for the tx fifo * buffer (aka FIFO threshold) that is used to indicate when PCU flushes * the buffer and transmits its data. Lowering this results sending small * frames more quickly but can lead to tx underruns, raising it a lot can * result other problems. Right now we start with the lowest possible * (64Bytes) and if we get tx underrun we increase it using the increase * flag. Returns -EIO if we have reached maximum/minimum. * * XXX: Link this with tx DMA size ? * XXX2: Use it to save interrupts ? */ int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) { u32 trigger_level, imr; int ret = -EIO; /* * Disable interrupts by setting the mask */ imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL); trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG), AR5K_TXCFG_TXFULL); if (!increase) { if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES) goto done; } else trigger_level += ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2); /* * Update trigger level on success */ if (ah->ah_version == AR5K_AR5210) ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL); else AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_TXFULL, trigger_level); ret = 0; done: /* * Restore interrupt mask */ ath5k_hw_set_imr(ah, imr); return ret; } /*******************\ * Interrupt masking * \*******************/ /** * ath5k_hw_is_intr_pending() - Check if we have pending interrupts * @ah: The &struct ath5k_hw * * Check if we have pending interrupts to process. Returns 1 if we * have pending interrupts and 0 if we haven't. */ bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah) { return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0; } /** * ath5k_hw_get_isr() - Get interrupt status * @ah: The @struct ath5k_hw * @interrupt_mask: Driver's interrupt mask used to filter out * interrupts in sw. * * This function is used inside our interrupt handler to determine the reason * for the interrupt by reading Primary Interrupt Status Register. Returns an * abstract interrupt status mask which is mostly ISR with some uncommon bits * being mapped on some standard non hw-specific positions * (check out &ath5k_int). * * NOTE: We do write-to-clear, so the active PISR/SISR bits at the time this * function gets called are cleared on return. */ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) { u32 data = 0; /* * Read interrupt status from Primary Interrupt * Register. * * Note: PISR/SISR Not available on 5210 */ if (ah->ah_version == AR5K_AR5210) { u32 isr = 0; isr = ath5k_hw_reg_read(ah, AR5K_ISR); if (unlikely(isr == AR5K_INT_NOCARD)) { *interrupt_mask = isr; return -ENODEV; } /* * Filter out the non-common bits from the interrupt * status. */ *interrupt_mask = (isr & AR5K_INT_COMMON) & ah->ah_imr; /* Hanlde INT_FATAL */ if (unlikely(isr & (AR5K_ISR_SSERR | AR5K_ISR_MCABT | AR5K_ISR_DPERR))) *interrupt_mask |= AR5K_INT_FATAL; /* * XXX: BMISS interrupts may occur after association. * I found this on 5210 code but it needs testing. If this is * true we should disable them before assoc and re-enable them * after a successful assoc + some jiffies. interrupt_mask &= ~AR5K_INT_BMISS; */ data = isr; } else { u32 pisr = 0; u32 pisr_clear = 0; u32 sisr0 = 0; u32 sisr1 = 0; u32 sisr2 = 0; u32 sisr3 = 0; u32 sisr4 = 0; /* Read PISR and SISRs... */ pisr = ath5k_hw_reg_read(ah, AR5K_PISR); if (unlikely(pisr == AR5K_INT_NOCARD)) { *interrupt_mask = pisr; return -ENODEV; } sisr0 = ath5k_hw_reg_read(ah, AR5K_SISR0); sisr1 = ath5k_hw_reg_read(ah, AR5K_SISR1); sisr2 = ath5k_hw_reg_read(ah, AR5K_SISR2); sisr3 = ath5k_hw_reg_read(ah, AR5K_SISR3); sisr4 = ath5k_hw_reg_read(ah, AR5K_SISR4); /* * PISR holds the logical OR of interrupt bits * from SISR registers: * * TXOK and TXDESC -> Logical OR of TXOK and TXDESC * per-queue bits on SISR0 * * TXERR and TXEOL -> Logical OR of TXERR and TXEOL * per-queue bits on SISR1 * * TXURN -> Logical OR of TXURN per-queue bits on SISR2 * * HIUERR -> Logical OR of MCABT, SSERR and DPER bits on SISR2 * * BCNMISC -> Logical OR of TIM, CAB_END, DTIM_SYNC * BCN_TIMEOUT, CAB_TIMEOUT and DTIM * (and TSFOOR ?) bits on SISR2 * * QCBRORN and QCBRURN -> Logical OR of QCBRORN and * QCBRURN per-queue bits on SISR3 * QTRIG -> Logical OR of QTRIG per-queue bits on SISR4 * * If we clean these bits on PISR we 'll also clear all * related bits from SISRs, e.g. if we write the TXOK bit on * PISR we 'll clean all TXOK bits from SISR0 so if a new TXOK * interrupt got fired for another queue while we were reading * the interrupt registers and we write back the TXOK bit on * PISR we 'll lose it. So make sure that we don't write back * on PISR any bits that come from SISRs. Clearing them from * SISRs will also clear PISR so no need to worry here. */ pisr_clear = pisr & ~AR5K_ISR_BITS_FROM_SISRS; /* * Write to clear them... * Note: This means that each bit we write back * to the registers will get cleared, leaving the * rest unaffected. So this won't affect new interrupts * we didn't catch while reading/processing, we 'll get * them next time get_isr gets called. */ ath5k_hw_reg_write(ah, sisr0, AR5K_SISR0); ath5k_hw_reg_write(ah, sisr1, AR5K_SISR1); ath5k_hw_reg_write(ah, sisr2, AR5K_SISR2); ath5k_hw_reg_write(ah, sisr3, AR5K_SISR3); ath5k_hw_reg_write(ah, sisr4, AR5K_SISR4); ath5k_hw_reg_write(ah, pisr_clear, AR5K_PISR); /* Flush previous write */ ath5k_hw_reg_read(ah, AR5K_PISR); /* * Filter out the non-common bits from the interrupt * status. */ *interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr; /* We treat TXOK,TXDESC, TXERR and TXEOL * the same way (schedule the tx tasklet) * so we track them all together per queue */ if (pisr & AR5K_ISR_TXOK) ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, AR5K_SISR0_QCU_TXOK); if (pisr & AR5K_ISR_TXDESC) ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, AR5K_SISR0_QCU_TXDESC); if (pisr & AR5K_ISR_TXERR) ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, AR5K_SISR1_QCU_TXERR); if (pisr & AR5K_ISR_TXEOL) ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, AR5K_SISR1_QCU_TXEOL); /* Currently this is not much usefull since we treat * all queues the same way if we get a TXURN (update * tx trigger level) but we might need it later on*/ if (pisr & AR5K_ISR_TXURN) ah->ah_txq_isr_txurn |= AR5K_REG_MS(sisr2, AR5K_SISR2_QCU_TXURN); /* Misc Beacon related interrupts */ /* For AR5211 */ if (pisr & AR5K_ISR_TIM) *interrupt_mask |= AR5K_INT_TIM; /* For AR5212+ */ if (pisr & AR5K_ISR_BCNMISC) { if (sisr2 & AR5K_SISR2_TIM) *interrupt_mask |= AR5K_INT_TIM; if (sisr2 & AR5K_SISR2_DTIM) *interrupt_mask |= AR5K_INT_DTIM; if (sisr2 & AR5K_SISR2_DTIM_SYNC) *interrupt_mask |= AR5K_INT_DTIM_SYNC; if (sisr2 & AR5K_SISR2_BCN_TIMEOUT) *interrupt_mask |= AR5K_INT_BCN_TIMEOUT; if (sisr2 & AR5K_SISR2_CAB_TIMEOUT) *interrupt_mask |= AR5K_INT_CAB_TIMEOUT; } /* Below interrupts are unlikely to happen */ /* HIU = Host Interface Unit (PCI etc) * Can be one of MCABT, SSERR, DPERR from SISR2 */ if (unlikely(pisr & (AR5K_ISR_HIUERR))) *interrupt_mask |= AR5K_INT_FATAL; /*Beacon Not Ready*/ if (unlikely(pisr & (AR5K_ISR_BNR))) *interrupt_mask |= AR5K_INT_BNR; /* A queue got CBR overrun */ if (unlikely(pisr & (AR5K_ISR_QCBRORN))) { *interrupt_mask |= AR5K_INT_QCBRORN; ah->ah_txq_isr_qcborn |= AR5K_REG_MS(sisr3, AR5K_SISR3_QCBRORN); } /* A queue got CBR underrun */ if (unlikely(pisr & (AR5K_ISR_QCBRURN))) { *interrupt_mask |= AR5K_INT_QCBRURN; ah->ah_txq_isr_qcburn |= AR5K_REG_MS(sisr3, AR5K_SISR3_QCBRURN); } /* A queue got triggered */ if (unlikely(pisr & (AR5K_ISR_QTRIG))) { *interrupt_mask |= AR5K_INT_QTRIG; ah->ah_txq_isr_qtrig |= AR5K_REG_MS(sisr4, AR5K_SISR4_QTRIG); } data = pisr; } /* * In case we didn't handle anything, * print the register value. */ if (unlikely(*interrupt_mask == 0 && net_ratelimit())) ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr); return 0; } /** * ath5k_hw_set_imr() - Set interrupt mask * @ah: The &struct ath5k_hw * @new_mask: The new interrupt mask to be set * * Set the interrupt mask in hw to save interrupts. We do that by mapping * ath5k_int bits to hw-specific bits to remove abstraction and writing * Interrupt Mask Register. */ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) { enum ath5k_int old_mask, int_mask; old_mask = ah->ah_imr; /* * Disable card interrupts to prevent any race conditions * (they will be re-enabled afterwards if AR5K_INT GLOBAL * is set again on the new mask). */ if (old_mask & AR5K_INT_GLOBAL) { ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); ath5k_hw_reg_read(ah, AR5K_IER); } /* * Add additional, chipset-dependent interrupt mask flags * and write them to the IMR (interrupt mask register). */ int_mask = new_mask & AR5K_INT_COMMON; if (ah->ah_version != AR5K_AR5210) { /* Preserve per queue TXURN interrupt mask */ u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2) & AR5K_SIMR2_QCU_TXURN; /* Fatal interrupt abstraction for 5211+ */ if (new_mask & AR5K_INT_FATAL) { int_mask |= AR5K_IMR_HIUERR; simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR); } /* Misc beacon related interrupts */ if (new_mask & AR5K_INT_TIM) int_mask |= AR5K_IMR_TIM; if (new_mask & AR5K_INT_TIM) simr2 |= AR5K_SISR2_TIM; if (new_mask & AR5K_INT_DTIM) simr2 |= AR5K_SISR2_DTIM; if (new_mask & AR5K_INT_DTIM_SYNC) simr2 |= AR5K_SISR2_DTIM_SYNC; if (new_mask & AR5K_INT_BCN_TIMEOUT) simr2 |= AR5K_SISR2_BCN_TIMEOUT; if (new_mask & AR5K_INT_CAB_TIMEOUT) simr2 |= AR5K_SISR2_CAB_TIMEOUT; /*Beacon Not Ready*/ if (new_mask & AR5K_INT_BNR) int_mask |= AR5K_INT_BNR; /* Note: Per queue interrupt masks * are set via ath5k_hw_reset_tx_queue() (qcu.c) */ ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2); } else { /* Fatal interrupt abstraction for 5210 */ if (new_mask & AR5K_INT_FATAL) int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT | AR5K_IMR_HIUERR | AR5K_IMR_DPERR); /* Only common interrupts left for 5210 (no SIMRs) */ ath5k_hw_reg_write(ah, int_mask, AR5K_IMR); } /* If RXNOFRM interrupt is masked disable it * by setting AR5K_RXNOFRM to zero */ if (!(new_mask & AR5K_INT_RXNOFRM)) ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM); /* Store new interrupt mask */ ah->ah_imr = new_mask; /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */ if (new_mask & AR5K_INT_GLOBAL) { ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); ath5k_hw_reg_read(ah, AR5K_IER); } return old_mask; } /********************\ Init/Stop functions \********************/ /** * ath5k_hw_dma_init() - Initialize DMA unit * @ah: The &struct ath5k_hw * * Set DMA size and pre-enable interrupts * (driver handles tx/rx buffer setup and * dma start/stop) * * XXX: Save/restore RXDP/TXDP registers ? */ void ath5k_hw_dma_init(struct ath5k_hw *ah) { /* * Set Rx/Tx DMA Configuration * * Set standard DMA size (128). Note that * a DMA size of 512 causes rx overruns and tx errors * on pci-e cards (tested on 5424 but since rx overruns * also occur on 5416/5418 with madwifi we set 128 * for all PCI-E cards to be safe). * * XXX: need to check 5210 for this * TODO: Check out tx trigger level, it's always 64 on dumps but I * guess we can tweak it and see how it goes ;-) */ if (ah->ah_version != AR5K_AR5210) { AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B); AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B); } /* Pre-enable interrupts on 5211/5212*/ if (ah->ah_version != AR5K_AR5210) ath5k_hw_set_imr(ah, ah->ah_imr); } /** * ath5k_hw_dma_stop() - stop DMA unit * @ah: The &struct ath5k_hw * * Stop tx/rx DMA and interrupts. Returns * -EBUSY if tx or rx dma failed to stop. * * XXX: Sometimes DMA unit hangs and we have * stuck frames on tx queues, only a reset * can fix that. */ int ath5k_hw_dma_stop(struct ath5k_hw *ah) { int i, qmax, err; err = 0; /* Disable interrupts */ ath5k_hw_set_imr(ah, 0); /* Stop rx dma */ err = ath5k_hw_stop_rx_dma(ah); if (err) return err; /* Clear any pending interrupts * and disable tx dma */ if (ah->ah_version != AR5K_AR5210) { ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR); qmax = AR5K_NUM_TX_QUEUES; } else { /* PISR/SISR Not available on 5210 */ ath5k_hw_reg_read(ah, AR5K_ISR); qmax = AR5K_NUM_TX_QUEUES_NOQCU; } for (i = 0; i < qmax; i++) { err = ath5k_hw_stop_tx_dma(ah, i); /* -EINVAL -> queue inactive */ if (err && err != -EINVAL) return err; } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/desc.c0000644000175000017500000005403412026211315023341 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * Copyright (c) 2007-2008 Pavel Roskin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /******************************\ Hardware Descriptor Functions \******************************/ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "ath5k.h" #include "reg.h" #include "debug.h" /** * DOC: Hardware descriptor functions * * Here we handle the processing of the low-level hw descriptors * that hw reads and writes via DMA for each TX and RX attempt (that means * we can also have descriptors for failed TX/RX tries). We have two kind of * descriptors for RX and TX, control descriptors tell the hw how to send or * receive a packet where to read/write it from/to etc and status descriptors * that contain information about how the packet was sent or received (errors * included). * * Descriptor format is not exactly the same for each MAC chip version so we * have function pointers on &struct ath5k_hw we initialize at runtime based on * the chip used. */ /************************\ * TX Control descriptors * \************************/ /** * ath5k_hw_setup_2word_tx_desc() - Initialize a 2-word tx control descriptor * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @pkt_len: Frame length in bytes * @hdr_len: Header length in bytes (only used on AR5210) * @padsize: Any padding we've added to the frame length * @type: One of enum ath5k_pkt_type * @tx_power: Tx power in 0.5dB steps * @tx_rate0: HW idx for transmission rate * @tx_tries0: Max number of retransmissions * @key_index: Index on key table to use for encryption * @antenna_mode: Which antenna to use (0 for auto) * @flags: One of AR5K_TXDESC_* flags (desc.h) * @rtscts_rate: HW idx for RTS/CTS transmission rate * @rtscts_duration: What to put on duration field on the header of RTS/CTS * * Internal function to initialize a 2-Word TX control descriptor * found on AR5210 and AR5211 MACs chips. * * Returns 0 on success or -EINVAL on false input */ static int ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len, int padsize, enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0, unsigned int key_index, unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate, unsigned int rtscts_duration) { u32 frame_type; struct ath5k_hw_2w_tx_ctl *tx_ctl; unsigned int frame_len; tx_ctl = &desc->ud.ds_tx5210.tx_ctl; /* * Validate input * - Zero retries don't make sense. * - A zero rate will put the HW into a mode where it continuously sends * noise on the channel, so it is important to avoid this. */ if (unlikely(tx_tries0 == 0)) { ATH5K_ERR(ah, "zero retries\n"); WARN_ON(1); return -EINVAL; } if (unlikely(tx_rate0 == 0)) { ATH5K_ERR(ah, "zero rate\n"); WARN_ON(1); return -EINVAL; } /* Clear descriptor */ memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc)); /* Setup control descriptor */ /* Verify and set frame length */ /* remove padding we might have added before */ frame_len = pkt_len - padsize + FCS_LEN; if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN) return -EINVAL; tx_ctl->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN; /* Verify and set buffer length */ /* NB: beacon's BufLen must be a multiple of 4 bytes */ if (type == AR5K_PKT_TYPE_BEACON) pkt_len = roundup(pkt_len, 4); if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN) return -EINVAL; tx_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN; /* * Verify and set header length (only 5210) */ if (ah->ah_version == AR5K_AR5210) { if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210) return -EINVAL; tx_ctl->tx_control_0 |= AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210); } /*Differences between 5210-5211*/ if (ah->ah_version == AR5K_AR5210) { switch (type) { case AR5K_PKT_TYPE_BEACON: case AR5K_PKT_TYPE_PROBE_RESP: frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY; break; case AR5K_PKT_TYPE_PIFS: frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS; break; default: frame_type = type; break; } tx_ctl->tx_control_0 |= AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210) | AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE); } else { tx_ctl->tx_control_0 |= AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) | AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT); tx_ctl->tx_control_1 |= AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211); } #define _TX_FLAGS(_c, _flag) \ if (flags & AR5K_TXDESC_##_flag) { \ tx_ctl->tx_control_##_c |= \ AR5K_2W_TX_DESC_CTL##_c##_##_flag; \ } #define _TX_FLAGS_5211(_c, _flag) \ if (flags & AR5K_TXDESC_##_flag) { \ tx_ctl->tx_control_##_c |= \ AR5K_2W_TX_DESC_CTL##_c##_##_flag##_5211; \ } _TX_FLAGS(0, CLRDMASK); _TX_FLAGS(0, INTREQ); _TX_FLAGS(0, RTSENA); if (ah->ah_version == AR5K_AR5211) { _TX_FLAGS_5211(0, VEOL); _TX_FLAGS_5211(1, NOACK); } #undef _TX_FLAGS #undef _TX_FLAGS_5211 /* * WEP crap */ if (key_index != AR5K_TXKEYIX_INVALID) { tx_ctl->tx_control_0 |= AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; tx_ctl->tx_control_1 |= AR5K_REG_SM(key_index, AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX); } /* * RTS/CTS Duration [5210 ?] */ if ((ah->ah_version == AR5K_AR5210) && (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA))) tx_ctl->tx_control_1 |= rtscts_duration & AR5K_2W_TX_DESC_CTL1_RTS_DURATION_5210; return 0; } /** * ath5k_hw_setup_4word_tx_desc() - Initialize a 4-word tx control descriptor * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @pkt_len: Frame length in bytes * @hdr_len: Header length in bytes (only used on AR5210) * @padsize: Any padding we've added to the frame length * @type: One of enum ath5k_pkt_type * @tx_power: Tx power in 0.5dB steps * @tx_rate0: HW idx for transmission rate * @tx_tries0: Max number of retransmissions * @key_index: Index on key table to use for encryption * @antenna_mode: Which antenna to use (0 for auto) * @flags: One of AR5K_TXDESC_* flags (desc.h) * @rtscts_rate: HW idx for RTS/CTS transmission rate * @rtscts_duration: What to put on duration field on the header of RTS/CTS * * Internal function to initialize a 4-Word TX control descriptor * found on AR5212 and later MACs chips. * * Returns 0 on success or -EINVAL on false input */ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len, int padsize, enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0, unsigned int key_index, unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate, unsigned int rtscts_duration) { struct ath5k_hw_4w_tx_ctl *tx_ctl; unsigned int frame_len; /* * Use local variables for these to reduce load/store access on * uncached memory */ u32 txctl0 = 0, txctl1 = 0, txctl2 = 0, txctl3 = 0; tx_ctl = &desc->ud.ds_tx5212.tx_ctl; /* * Validate input * - Zero retries don't make sense. * - A zero rate will put the HW into a mode where it continuously sends * noise on the channel, so it is important to avoid this. */ if (unlikely(tx_tries0 == 0)) { ATH5K_ERR(ah, "zero retries\n"); WARN_ON(1); return -EINVAL; } if (unlikely(tx_rate0 == 0)) { ATH5K_ERR(ah, "zero rate\n"); WARN_ON(1); return -EINVAL; } tx_power += ah->ah_txpower.txp_offset; if (tx_power > AR5K_TUNE_MAX_TXPOWER) tx_power = AR5K_TUNE_MAX_TXPOWER; /* Clear descriptor status area */ memset(&desc->ud.ds_tx5212.tx_stat, 0, sizeof(desc->ud.ds_tx5212.tx_stat)); /* Setup control descriptor */ /* Verify and set frame length */ /* remove padding we might have added before */ frame_len = pkt_len - padsize + FCS_LEN; if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN) return -EINVAL; txctl0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN; /* Verify and set buffer length */ /* NB: beacon's BufLen must be a multiple of 4 bytes */ if (type == AR5K_PKT_TYPE_BEACON) pkt_len = roundup(pkt_len, 4); if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN) return -EINVAL; txctl1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN; txctl0 |= AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) | AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT); txctl1 |= AR5K_REG_SM(type, AR5K_4W_TX_DESC_CTL1_FRAME_TYPE); txctl2 = AR5K_REG_SM(tx_tries0, AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0); txctl3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; #define _TX_FLAGS(_c, _flag) \ if (flags & AR5K_TXDESC_##_flag) { \ txctl##_c |= AR5K_4W_TX_DESC_CTL##_c##_##_flag; \ } _TX_FLAGS(0, CLRDMASK); _TX_FLAGS(0, VEOL); _TX_FLAGS(0, INTREQ); _TX_FLAGS(0, RTSENA); _TX_FLAGS(0, CTSENA); _TX_FLAGS(1, NOACK); #undef _TX_FLAGS /* * WEP crap */ if (key_index != AR5K_TXKEYIX_INVALID) { txctl0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; txctl1 |= AR5K_REG_SM(key_index, AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX); } /* * RTS/CTS */ if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) { if ((flags & AR5K_TXDESC_RTSENA) && (flags & AR5K_TXDESC_CTSENA)) return -EINVAL; txctl2 |= rtscts_duration & AR5K_4W_TX_DESC_CTL2_RTS_DURATION; txctl3 |= AR5K_REG_SM(rtscts_rate, AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE); } tx_ctl->tx_control_0 = txctl0; tx_ctl->tx_control_1 = txctl1; tx_ctl->tx_control_2 = txctl2; tx_ctl->tx_control_3 = txctl3; return 0; } /** * ath5k_hw_setup_mrr_tx_desc() - Initialize an MRR tx control descriptor * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @tx_rate1: HW idx for rate used on transmission series 1 * @tx_tries1: Max number of retransmissions for transmission series 1 * @tx_rate2: HW idx for rate used on transmission series 2 * @tx_tries2: Max number of retransmissions for transmission series 2 * @tx_rate3: HW idx for rate used on transmission series 3 * @tx_tries3: Max number of retransmissions for transmission series 3 * * Multi rate retry (MRR) tx control descriptors are available only on AR5212 * MACs, they are part of the normal 4-word tx control descriptor (see above) * but we handle them through a separate function for better abstraction. * * Returns 0 on success or -EINVAL on invalid input */ int ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, u_int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2, u_int tx_rate3, u_int tx_tries3) { struct ath5k_hw_4w_tx_ctl *tx_ctl; /* no mrr support for cards older than 5212 */ if (ah->ah_version < AR5K_AR5212) return 0; /* * Rates can be 0 as long as the retry count is 0 too. * A zero rate and nonzero retry count will put the HW into a mode where * it continuously sends noise on the channel, so it is important to * avoid this. */ if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) || (tx_rate2 == 0 && tx_tries2 != 0) || (tx_rate3 == 0 && tx_tries3 != 0))) { ATH5K_ERR(ah, "zero rate\n"); WARN_ON(1); return -EINVAL; } if (ah->ah_version == AR5K_AR5212) { tx_ctl = &desc->ud.ds_tx5212.tx_ctl; #define _XTX_TRIES(_n) \ if (tx_tries##_n) { \ tx_ctl->tx_control_2 |= \ AR5K_REG_SM(tx_tries##_n, \ AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \ tx_ctl->tx_control_3 |= \ AR5K_REG_SM(tx_rate##_n, \ AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n); \ } _XTX_TRIES(1); _XTX_TRIES(2); _XTX_TRIES(3); #undef _XTX_TRIES return 1; } return 0; } /***********************\ * TX Status descriptors * \***********************/ /** * ath5k_hw_proc_2word_tx_status() - Process a tx status descriptor on 5210/1 * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @ts: The &struct ath5k_tx_status */ static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah, struct ath5k_desc *desc, struct ath5k_tx_status *ts) { struct ath5k_hw_tx_status *tx_status; tx_status = &desc->ud.ds_tx5210.tx_stat; /* No frame has been send or error */ if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0)) return -EINPROGRESS; /* * Get descriptor status */ ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0, AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0, AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); ts->ts_final_retry = AR5K_REG_MS(tx_status->tx_status_0, AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); /*TODO: ts->ts_virtcol + test*/ ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1, AR5K_DESC_TX_STATUS1_SEQ_NUM); ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1, AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); ts->ts_antenna = 1; ts->ts_status = 0; ts->ts_final_idx = 0; if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) { if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) ts->ts_status |= AR5K_TXERR_XRETRY; if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) ts->ts_status |= AR5K_TXERR_FIFO; if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED) ts->ts_status |= AR5K_TXERR_FILT; } return 0; } /** * ath5k_hw_proc_4word_tx_status() - Process a tx status descriptor on 5212 * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @ts: The &struct ath5k_tx_status */ static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah, struct ath5k_desc *desc, struct ath5k_tx_status *ts) { struct ath5k_hw_tx_status *tx_status; u32 txstat0, txstat1; tx_status = &desc->ud.ds_tx5212.tx_stat; txstat1 = ACCESS_ONCE(tx_status->tx_status_1); /* No frame has been send or error */ if (unlikely(!(txstat1 & AR5K_DESC_TX_STATUS1_DONE))) return -EINPROGRESS; txstat0 = ACCESS_ONCE(tx_status->tx_status_0); /* * Get descriptor status */ ts->ts_tstamp = AR5K_REG_MS(txstat0, AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); ts->ts_shortretry = AR5K_REG_MS(txstat0, AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); ts->ts_final_retry = AR5K_REG_MS(txstat0, AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); ts->ts_seqnum = AR5K_REG_MS(txstat1, AR5K_DESC_TX_STATUS1_SEQ_NUM); ts->ts_rssi = AR5K_REG_MS(txstat1, AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); ts->ts_antenna = (txstat1 & AR5K_DESC_TX_STATUS1_XMIT_ANTENNA_5212) ? 2 : 1; ts->ts_status = 0; ts->ts_final_idx = AR5K_REG_MS(txstat1, AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212); /* TX error */ if (!(txstat0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) { if (txstat0 & AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) ts->ts_status |= AR5K_TXERR_XRETRY; if (txstat0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) ts->ts_status |= AR5K_TXERR_FIFO; if (txstat0 & AR5K_DESC_TX_STATUS0_FILTERED) ts->ts_status |= AR5K_TXERR_FILT; } return 0; } /****************\ * RX Descriptors * \****************/ /** * ath5k_hw_setup_rx_desc() - Initialize an rx control descriptor * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @size: RX buffer length in bytes * @flags: One of AR5K_RXDESC_* flags */ int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, u32 size, unsigned int flags) { struct ath5k_hw_rx_ctl *rx_ctl; rx_ctl = &desc->ud.ds_rx.rx_ctl; /* * Clear the descriptor * If we don't clean the status descriptor, * while scanning we get too many results, * most of them virtual, after some secs * of scanning system hangs. M.F. */ memset(&desc->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc)); if (unlikely(size & ~AR5K_DESC_RX_CTL1_BUF_LEN)) return -EINVAL; /* Setup descriptor */ rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN; if (flags & AR5K_RXDESC_INTREQ) rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ; return 0; } /** * ath5k_hw_proc_5210_rx_status() - Process the rx status descriptor on 5210/1 * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @rs: The &struct ath5k_rx_status * * Internal function used to process an RX status descriptor * on AR5210/5211 MAC. * * Returns 0 on success or -EINPROGRESS in case we haven't received the who;e * frame yet. */ static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah, struct ath5k_desc *desc, struct ath5k_rx_status *rs) { struct ath5k_hw_rx_status *rx_status; rx_status = &desc->ud.ds_rx.rx_stat; /* No frame received / not ready */ if (unlikely(!(rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_DONE))) return -EINPROGRESS; memset(rs, 0, sizeof(struct ath5k_rx_status)); /* * Frame receive status */ rs->rs_datalen = rx_status->rx_status_0 & AR5K_5210_RX_DESC_STATUS0_DATA_LEN; rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0, AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL); rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0, AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE); rs->rs_more = !!(rx_status->rx_status_0 & AR5K_5210_RX_DESC_STATUS0_MORE); /* TODO: this timestamp is 13 bit, later on we assume 15 bit! * also the HAL code for 5210 says the timestamp is bits [10..22] of the * TSF, and extends the timestamp here to 15 bit. * we need to check on 5210... */ rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); if (ah->ah_version == AR5K_AR5211) rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0, AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211); else rs->rs_antenna = (rx_status->rx_status_0 & AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5210) ? 2 : 1; /* * Key table status */ if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID) rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1, AR5K_5210_RX_DESC_STATUS1_KEY_INDEX); else rs->rs_keyix = AR5K_RXKEYIX_INVALID; /* * Receive/descriptor errors */ if (!(rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) { if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_CRC_ERROR) rs->rs_status |= AR5K_RXERR_CRC; /* only on 5210 */ if ((ah->ah_version == AR5K_AR5210) && (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN_5210)) rs->rs_status |= AR5K_RXERR_FIFO; if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) { rs->rs_status |= AR5K_RXERR_PHY; rs->rs_phyerr = AR5K_REG_MS(rx_status->rx_status_1, AR5K_5210_RX_DESC_STATUS1_PHY_ERROR); } if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) rs->rs_status |= AR5K_RXERR_DECRYPT; } return 0; } /** * ath5k_hw_proc_5212_rx_status() - Process the rx status descriptor on 5212 * @ah: The &struct ath5k_hw * @desc: The &struct ath5k_desc * @rs: The &struct ath5k_rx_status * * Internal function used to process an RX status descriptor * on AR5212 and later MAC. * * Returns 0 on success or -EINPROGRESS in case we haven't received the who;e * frame yet. */ static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah, struct ath5k_desc *desc, struct ath5k_rx_status *rs) { struct ath5k_hw_rx_status *rx_status; u32 rxstat0, rxstat1; rx_status = &desc->ud.ds_rx.rx_stat; rxstat1 = ACCESS_ONCE(rx_status->rx_status_1); /* No frame received / not ready */ if (unlikely(!(rxstat1 & AR5K_5212_RX_DESC_STATUS1_DONE))) return -EINPROGRESS; memset(rs, 0, sizeof(struct ath5k_rx_status)); rxstat0 = ACCESS_ONCE(rx_status->rx_status_0); /* * Frame receive status */ rs->rs_datalen = rxstat0 & AR5K_5212_RX_DESC_STATUS0_DATA_LEN; rs->rs_rssi = AR5K_REG_MS(rxstat0, AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL); rs->rs_rate = AR5K_REG_MS(rxstat0, AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE); rs->rs_antenna = AR5K_REG_MS(rxstat0, AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA); rs->rs_more = !!(rxstat0 & AR5K_5212_RX_DESC_STATUS0_MORE); rs->rs_tstamp = AR5K_REG_MS(rxstat1, AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); /* * Key table status */ if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID) rs->rs_keyix = AR5K_REG_MS(rxstat1, AR5K_5212_RX_DESC_STATUS1_KEY_INDEX); else rs->rs_keyix = AR5K_RXKEYIX_INVALID; /* * Receive/descriptor errors */ if (!(rxstat1 & AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) { if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_CRC_ERROR) rs->rs_status |= AR5K_RXERR_CRC; if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) { rs->rs_status |= AR5K_RXERR_PHY; rs->rs_phyerr = AR5K_REG_MS(rxstat1, AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE); if (!ah->ah_capabilities.cap_has_phyerr_counters) ath5k_ani_phy_error_report(ah, rs->rs_phyerr); } if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) rs->rs_status |= AR5K_RXERR_DECRYPT; if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_MIC_ERROR) rs->rs_status |= AR5K_RXERR_MIC; } return 0; } /********\ * Attach * \********/ /** * ath5k_hw_init_desc_functions() - Init function pointers inside ah * @ah: The &struct ath5k_hw * * Maps the internal descriptor functions to the function pointers on ah, used * from above. This is used as an abstraction layer to handle the various chips * the same way. */ int ath5k_hw_init_desc_functions(struct ath5k_hw *ah) { if (ah->ah_version == AR5K_AR5212) { ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc; ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status; ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status; } else if (ah->ah_version <= AR5K_AR5211) { ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc; ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status; ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status; } else return -ENOTSUPP; return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/initvals.c0000644000175000017500000014464312026211315024262 0ustar mcgrofmcgrof/* * Initial register settings functions * * Copyright (c) 2004-2007 Reyk Floeter * Copyright (c) 2006-2009 Nick Kossifidis * Copyright (c) 2007-2008 Jiri Slaby * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "ath5k.h" #include "reg.h" #include "debug.h" /** * struct ath5k_ini - Mode-independent initial register writes * @ini_register: Register address * @ini_value: Default value * @ini_mode: 0 to write 1 to read (and clear) */ struct ath5k_ini { u16 ini_register; u32 ini_value; enum { AR5K_INI_WRITE = 0, /* Default */ AR5K_INI_READ = 1, } ini_mode; }; /** * struct ath5k_ini_mode - Mode specific initial register values * @mode_register: Register address * @mode_value: Set of values for each enum ath5k_driver_mode */ struct ath5k_ini_mode { u16 mode_register; u32 mode_value[3]; }; /* Initial register settings for AR5210 */ static const struct ath5k_ini ar5210_ini[] = { /* PCU and MAC registers */ { AR5K_NOQCU_TXDP0, 0 }, { AR5K_NOQCU_TXDP1, 0 }, { AR5K_RXDP, 0 }, { AR5K_CR, 0 }, { AR5K_ISR, 0, AR5K_INI_READ }, { AR5K_IMR, 0 }, { AR5K_IER, AR5K_IER_DISABLE }, { AR5K_BSR, 0, AR5K_INI_READ }, { AR5K_TXCFG, AR5K_DMASIZE_128B }, { AR5K_RXCFG, AR5K_DMASIZE_128B }, { AR5K_CFG, AR5K_INIT_CFG }, { AR5K_TOPS, 8 }, { AR5K_RXNOFRM, 8 }, { AR5K_RPGTO, 0 }, { AR5K_TXNOFRM, 0 }, { AR5K_SFR, 0 }, { AR5K_MIBC, 0 }, { AR5K_MISC, 0 }, { AR5K_RX_FILTER_5210, 0 }, { AR5K_MCAST_FILTER0_5210, 0 }, { AR5K_MCAST_FILTER1_5210, 0 }, { AR5K_TX_MASK0, 0 }, { AR5K_TX_MASK1, 0 }, { AR5K_CLR_TMASK, 0 }, { AR5K_TRIG_LVL, AR5K_TUNE_MIN_TX_FIFO_THRES }, { AR5K_DIAG_SW_5210, 0 }, { AR5K_RSSI_THR, AR5K_TUNE_RSSI_THRES }, { AR5K_TSF_L32_5210, 0 }, { AR5K_TIMER0_5210, 0 }, { AR5K_TIMER1_5210, 0xffffffff }, { AR5K_TIMER2_5210, 0xffffffff }, { AR5K_TIMER3_5210, 1 }, { AR5K_CFP_DUR_5210, 0 }, { AR5K_CFP_PERIOD_5210, 0 }, /* PHY registers */ { AR5K_PHY(0), 0x00000047 }, { AR5K_PHY_AGC, 0x00000000 }, { AR5K_PHY(3), 0x09848ea6 }, { AR5K_PHY(4), 0x3d32e000 }, { AR5K_PHY(5), 0x0000076b }, { AR5K_PHY_ACT, AR5K_PHY_ACT_DISABLE }, { AR5K_PHY(8), 0x02020200 }, { AR5K_PHY(9), 0x00000e0e }, { AR5K_PHY(10), 0x0a020201 }, { AR5K_PHY(11), 0x00036ffc }, { AR5K_PHY(12), 0x00000000 }, { AR5K_PHY(13), 0x00000e0e }, { AR5K_PHY(14), 0x00000007 }, { AR5K_PHY(15), 0x00020100 }, { AR5K_PHY(16), 0x89630000 }, { AR5K_PHY(17), 0x1372169c }, { AR5K_PHY(18), 0x0018b633 }, { AR5K_PHY(19), 0x1284613c }, { AR5K_PHY(20), 0x0de8b8e0 }, { AR5K_PHY(21), 0x00074859 }, { AR5K_PHY(22), 0x7e80beba }, { AR5K_PHY(23), 0x313a665e }, { AR5K_PHY_AGCCTL, 0x00001d08 }, { AR5K_PHY(25), 0x0001ce00 }, { AR5K_PHY(26), 0x409a4190 }, { AR5K_PHY(28), 0x0000000f }, { AR5K_PHY(29), 0x00000080 }, { AR5K_PHY(30), 0x00000004 }, { AR5K_PHY(31), 0x00000018 }, /* 0x987c */ { AR5K_PHY(64), 0x00000000 }, /* 0x9900 */ { AR5K_PHY(65), 0x00000000 }, { AR5K_PHY(66), 0x00000000 }, { AR5K_PHY(67), 0x00800000 }, { AR5K_PHY(68), 0x00000003 }, /* BB gain table (64bytes) */ { AR5K_BB_GAIN(0), 0x00000000 }, { AR5K_BB_GAIN(1), 0x00000020 }, { AR5K_BB_GAIN(2), 0x00000010 }, { AR5K_BB_GAIN(3), 0x00000030 }, { AR5K_BB_GAIN(4), 0x00000008 }, { AR5K_BB_GAIN(5), 0x00000028 }, { AR5K_BB_GAIN(6), 0x00000028 }, { AR5K_BB_GAIN(7), 0x00000004 }, { AR5K_BB_GAIN(8), 0x00000024 }, { AR5K_BB_GAIN(9), 0x00000014 }, { AR5K_BB_GAIN(10), 0x00000034 }, { AR5K_BB_GAIN(11), 0x0000000c }, { AR5K_BB_GAIN(12), 0x0000002c }, { AR5K_BB_GAIN(13), 0x00000002 }, { AR5K_BB_GAIN(14), 0x00000022 }, { AR5K_BB_GAIN(15), 0x00000012 }, { AR5K_BB_GAIN(16), 0x00000032 }, { AR5K_BB_GAIN(17), 0x0000000a }, { AR5K_BB_GAIN(18), 0x0000002a }, { AR5K_BB_GAIN(19), 0x00000001 }, { AR5K_BB_GAIN(20), 0x00000021 }, { AR5K_BB_GAIN(21), 0x00000011 }, { AR5K_BB_GAIN(22), 0x00000031 }, { AR5K_BB_GAIN(23), 0x00000009 }, { AR5K_BB_GAIN(24), 0x00000029 }, { AR5K_BB_GAIN(25), 0x00000005 }, { AR5K_BB_GAIN(26), 0x00000025 }, { AR5K_BB_GAIN(27), 0x00000015 }, { AR5K_BB_GAIN(28), 0x00000035 }, { AR5K_BB_GAIN(29), 0x0000000d }, { AR5K_BB_GAIN(30), 0x0000002d }, { AR5K_BB_GAIN(31), 0x00000003 }, { AR5K_BB_GAIN(32), 0x00000023 }, { AR5K_BB_GAIN(33), 0x00000013 }, { AR5K_BB_GAIN(34), 0x00000033 }, { AR5K_BB_GAIN(35), 0x0000000b }, { AR5K_BB_GAIN(36), 0x0000002b }, { AR5K_BB_GAIN(37), 0x00000007 }, { AR5K_BB_GAIN(38), 0x00000027 }, { AR5K_BB_GAIN(39), 0x00000017 }, { AR5K_BB_GAIN(40), 0x00000037 }, { AR5K_BB_GAIN(41), 0x0000000f }, { AR5K_BB_GAIN(42), 0x0000002f }, { AR5K_BB_GAIN(43), 0x0000002f }, { AR5K_BB_GAIN(44), 0x0000002f }, { AR5K_BB_GAIN(45), 0x0000002f }, { AR5K_BB_GAIN(46), 0x0000002f }, { AR5K_BB_GAIN(47), 0x0000002f }, { AR5K_BB_GAIN(48), 0x0000002f }, { AR5K_BB_GAIN(49), 0x0000002f }, { AR5K_BB_GAIN(50), 0x0000002f }, { AR5K_BB_GAIN(51), 0x0000002f }, { AR5K_BB_GAIN(52), 0x0000002f }, { AR5K_BB_GAIN(53), 0x0000002f }, { AR5K_BB_GAIN(54), 0x0000002f }, { AR5K_BB_GAIN(55), 0x0000002f }, { AR5K_BB_GAIN(56), 0x0000002f }, { AR5K_BB_GAIN(57), 0x0000002f }, { AR5K_BB_GAIN(58), 0x0000002f }, { AR5K_BB_GAIN(59), 0x0000002f }, { AR5K_BB_GAIN(60), 0x0000002f }, { AR5K_BB_GAIN(61), 0x0000002f }, { AR5K_BB_GAIN(62), 0x0000002f }, { AR5K_BB_GAIN(63), 0x0000002f }, /* 5110 RF gain table (64btes) */ { AR5K_RF_GAIN(0), 0x0000001d }, { AR5K_RF_GAIN(1), 0x0000005d }, { AR5K_RF_GAIN(2), 0x0000009d }, { AR5K_RF_GAIN(3), 0x000000dd }, { AR5K_RF_GAIN(4), 0x0000011d }, { AR5K_RF_GAIN(5), 0x00000021 }, { AR5K_RF_GAIN(6), 0x00000061 }, { AR5K_RF_GAIN(7), 0x000000a1 }, { AR5K_RF_GAIN(8), 0x000000e1 }, { AR5K_RF_GAIN(9), 0x00000031 }, { AR5K_RF_GAIN(10), 0x00000071 }, { AR5K_RF_GAIN(11), 0x000000b1 }, { AR5K_RF_GAIN(12), 0x0000001c }, { AR5K_RF_GAIN(13), 0x0000005c }, { AR5K_RF_GAIN(14), 0x00000029 }, { AR5K_RF_GAIN(15), 0x00000069 }, { AR5K_RF_GAIN(16), 0x000000a9 }, { AR5K_RF_GAIN(17), 0x00000020 }, { AR5K_RF_GAIN(18), 0x00000019 }, { AR5K_RF_GAIN(19), 0x00000059 }, { AR5K_RF_GAIN(20), 0x00000099 }, { AR5K_RF_GAIN(21), 0x00000030 }, { AR5K_RF_GAIN(22), 0x00000005 }, { AR5K_RF_GAIN(23), 0x00000025 }, { AR5K_RF_GAIN(24), 0x00000065 }, { AR5K_RF_GAIN(25), 0x000000a5 }, { AR5K_RF_GAIN(26), 0x00000028 }, { AR5K_RF_GAIN(27), 0x00000068 }, { AR5K_RF_GAIN(28), 0x0000001f }, { AR5K_RF_GAIN(29), 0x0000001e }, { AR5K_RF_GAIN(30), 0x00000018 }, { AR5K_RF_GAIN(31), 0x00000058 }, { AR5K_RF_GAIN(32), 0x00000098 }, { AR5K_RF_GAIN(33), 0x00000003 }, { AR5K_RF_GAIN(34), 0x00000004 }, { AR5K_RF_GAIN(35), 0x00000044 }, { AR5K_RF_GAIN(36), 0x00000084 }, { AR5K_RF_GAIN(37), 0x00000013 }, { AR5K_RF_GAIN(38), 0x00000012 }, { AR5K_RF_GAIN(39), 0x00000052 }, { AR5K_RF_GAIN(40), 0x00000092 }, { AR5K_RF_GAIN(41), 0x000000d2 }, { AR5K_RF_GAIN(42), 0x0000002b }, { AR5K_RF_GAIN(43), 0x0000002a }, { AR5K_RF_GAIN(44), 0x0000006a }, { AR5K_RF_GAIN(45), 0x000000aa }, { AR5K_RF_GAIN(46), 0x0000001b }, { AR5K_RF_GAIN(47), 0x0000001a }, { AR5K_RF_GAIN(48), 0x0000005a }, { AR5K_RF_GAIN(49), 0x0000009a }, { AR5K_RF_GAIN(50), 0x000000da }, { AR5K_RF_GAIN(51), 0x00000006 }, { AR5K_RF_GAIN(52), 0x00000006 }, { AR5K_RF_GAIN(53), 0x00000006 }, { AR5K_RF_GAIN(54), 0x00000006 }, { AR5K_RF_GAIN(55), 0x00000006 }, { AR5K_RF_GAIN(56), 0x00000006 }, { AR5K_RF_GAIN(57), 0x00000006 }, { AR5K_RF_GAIN(58), 0x00000006 }, { AR5K_RF_GAIN(59), 0x00000006 }, { AR5K_RF_GAIN(60), 0x00000006 }, { AR5K_RF_GAIN(61), 0x00000006 }, { AR5K_RF_GAIN(62), 0x00000006 }, { AR5K_RF_GAIN(63), 0x00000006 }, /* PHY activation */ { AR5K_PHY(53), 0x00000020 }, { AR5K_PHY(51), 0x00000004 }, { AR5K_PHY(50), 0x00060106 }, { AR5K_PHY(39), 0x0000006d }, { AR5K_PHY(48), 0x00000000 }, { AR5K_PHY(52), 0x00000014 }, { AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE }, }; /* Initial register settings for AR5211 */ static const struct ath5k_ini ar5211_ini[] = { { AR5K_RXDP, 0x00000000 }, { AR5K_RTSD0, 0x84849c9c }, { AR5K_RTSD1, 0x7c7c7c7c }, { AR5K_RXCFG, 0x00000005 }, { AR5K_MIBC, 0x00000000 }, { AR5K_TOPS, 0x00000008 }, { AR5K_RXNOFRM, 0x00000008 }, { AR5K_TXNOFRM, 0x00000010 }, { AR5K_RPGTO, 0x00000000 }, { AR5K_RFCNT, 0x0000001f }, { AR5K_QUEUE_TXDP(0), 0x00000000 }, { AR5K_QUEUE_TXDP(1), 0x00000000 }, { AR5K_QUEUE_TXDP(2), 0x00000000 }, { AR5K_QUEUE_TXDP(3), 0x00000000 }, { AR5K_QUEUE_TXDP(4), 0x00000000 }, { AR5K_QUEUE_TXDP(5), 0x00000000 }, { AR5K_QUEUE_TXDP(6), 0x00000000 }, { AR5K_QUEUE_TXDP(7), 0x00000000 }, { AR5K_QUEUE_TXDP(8), 0x00000000 }, { AR5K_QUEUE_TXDP(9), 0x00000000 }, { AR5K_DCU_FP, 0x00000000 }, { AR5K_STA_ID1, 0x00000000 }, { AR5K_BSS_ID0, 0x00000000 }, { AR5K_BSS_ID1, 0x00000000 }, { AR5K_RSSI_THR, 0x00000000 }, { AR5K_CFP_PERIOD_5211, 0x00000000 }, { AR5K_TIMER0_5211, 0x00000030 }, { AR5K_TIMER1_5211, 0x0007ffff }, { AR5K_TIMER2_5211, 0x01ffffff }, { AR5K_TIMER3_5211, 0x00000031 }, { AR5K_CFP_DUR_5211, 0x00000000 }, { AR5K_RX_FILTER_5211, 0x00000000 }, { AR5K_MCAST_FILTER0_5211, 0x00000000 }, { AR5K_MCAST_FILTER1_5211, 0x00000002 }, { AR5K_DIAG_SW_5211, 0x00000000 }, { AR5K_ADDAC_TEST, 0x00000000 }, { AR5K_DEFAULT_ANTENNA, 0x00000000 }, /* PHY registers */ { AR5K_PHY_AGC, 0x00000000 }, { AR5K_PHY(3), 0x2d849093 }, { AR5K_PHY(4), 0x7d32e000 }, { AR5K_PHY(5), 0x00000f6b }, { AR5K_PHY_ACT, 0x00000000 }, { AR5K_PHY(11), 0x00026ffe }, { AR5K_PHY(12), 0x00000000 }, { AR5K_PHY(15), 0x00020100 }, { AR5K_PHY(16), 0x206a017a }, { AR5K_PHY(19), 0x1284613c }, { AR5K_PHY(21), 0x00000859 }, { AR5K_PHY(26), 0x409a4190 }, /* 0x9868 */ { AR5K_PHY(27), 0x050cb081 }, { AR5K_PHY(28), 0x0000000f }, { AR5K_PHY(29), 0x00000080 }, { AR5K_PHY(30), 0x0000000c }, { AR5K_PHY(64), 0x00000000 }, { AR5K_PHY(65), 0x00000000 }, { AR5K_PHY(66), 0x00000000 }, { AR5K_PHY(67), 0x00800000 }, { AR5K_PHY(68), 0x00000001 }, { AR5K_PHY(71), 0x0000092a }, { AR5K_PHY_IQ, 0x00000000 }, { AR5K_PHY(73), 0x00058a05 }, { AR5K_PHY(74), 0x00000001 }, { AR5K_PHY(75), 0x00000000 }, { AR5K_PHY_PAPD_PROBE, 0x00000000 }, { AR5K_PHY(77), 0x00000000 }, /* 0x9934 */ { AR5K_PHY(78), 0x00000000 }, /* 0x9938 */ { AR5K_PHY(79), 0x0000003f }, /* 0x993c */ { AR5K_PHY(80), 0x00000004 }, { AR5K_PHY(82), 0x00000000 }, { AR5K_PHY(83), 0x00000000 }, { AR5K_PHY(84), 0x00000000 }, { AR5K_PHY_RADAR, 0x5d50f14c }, { AR5K_PHY(86), 0x00000018 }, { AR5K_PHY(87), 0x004b6a8e }, /* Initial Power table (32bytes) * common on all cards/modes. * Note: Table is rewritten during * txpower setup later using calibration * data etc. so next write is non-common */ { AR5K_PHY_PCDAC_TXPOWER(1), 0x06ff05ff }, { AR5K_PHY_PCDAC_TXPOWER(2), 0x07ff07ff }, { AR5K_PHY_PCDAC_TXPOWER(3), 0x08ff08ff }, { AR5K_PHY_PCDAC_TXPOWER(4), 0x09ff09ff }, { AR5K_PHY_PCDAC_TXPOWER(5), 0x0aff0aff }, { AR5K_PHY_PCDAC_TXPOWER(6), 0x0bff0bff }, { AR5K_PHY_PCDAC_TXPOWER(7), 0x0cff0cff }, { AR5K_PHY_PCDAC_TXPOWER(8), 0x0dff0dff }, { AR5K_PHY_PCDAC_TXPOWER(9), 0x0fff0eff }, { AR5K_PHY_PCDAC_TXPOWER(10), 0x12ff12ff }, { AR5K_PHY_PCDAC_TXPOWER(11), 0x14ff13ff }, { AR5K_PHY_PCDAC_TXPOWER(12), 0x16ff15ff }, { AR5K_PHY_PCDAC_TXPOWER(13), 0x19ff17ff }, { AR5K_PHY_PCDAC_TXPOWER(14), 0x1bff1aff }, { AR5K_PHY_PCDAC_TXPOWER(15), 0x1eff1dff }, { AR5K_PHY_PCDAC_TXPOWER(16), 0x23ff20ff }, { AR5K_PHY_PCDAC_TXPOWER(17), 0x27ff25ff }, { AR5K_PHY_PCDAC_TXPOWER(18), 0x2cff29ff }, { AR5K_PHY_PCDAC_TXPOWER(19), 0x31ff2fff }, { AR5K_PHY_PCDAC_TXPOWER(20), 0x37ff34ff }, { AR5K_PHY_PCDAC_TXPOWER(21), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(22), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(23), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(24), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(25), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(26), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(27), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(28), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(29), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(30), 0x3aff3aff }, { AR5K_PHY_PCDAC_TXPOWER(31), 0x3aff3aff }, { AR5K_PHY_CCKTXCTL, 0x00000000 }, { AR5K_PHY(642), 0x503e4646 }, { AR5K_PHY_GAIN_2GHZ, 0x6480416c }, { AR5K_PHY(644), 0x0199a003 }, { AR5K_PHY(645), 0x044cd610 }, { AR5K_PHY(646), 0x13800040 }, { AR5K_PHY(647), 0x1be00060 }, { AR5K_PHY(648), 0x0c53800a }, { AR5K_PHY(649), 0x0014df3b }, { AR5K_PHY(650), 0x000001b5 }, { AR5K_PHY(651), 0x00000020 }, }; /* Initial mode-specific settings for AR5211 * 5211 supports OFDM-only g (draft g) but we * need to test it ! */ static const struct ath5k_ini_mode ar5211_ini_mode[] = { { AR5K_TXCFG, /* A B G */ { 0x00000015, 0x0000001d, 0x00000015 } }, { AR5K_QUEUE_DFS_LOCAL_IFS(0), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(1), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(2), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(3), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(4), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(5), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(6), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(7), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(8), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(9), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_DCU_GBL_IFS_SLOT, { 0x00000168, 0x000001b8, 0x00000168 } }, { AR5K_DCU_GBL_IFS_SIFS, { 0x00000230, 0x000000b0, 0x00000230 } }, { AR5K_DCU_GBL_IFS_EIFS, { 0x00000d98, 0x00001f48, 0x00000d98 } }, { AR5K_DCU_GBL_IFS_MISC, { 0x0000a0e0, 0x00005880, 0x0000a0e0 } }, { AR5K_TIME_OUT, { 0x04000400, 0x20003000, 0x04000400 } }, { AR5K_USEC_5211, { 0x0e8d8fa7, 0x01608f95, 0x0e8d8fa7 } }, { AR5K_PHY(8), { 0x02020200, 0x02010200, 0x02020200 } }, { AR5K_PHY_RF_CTL2, { 0x00000e0e, 0x00000707, 0x00000e0e } }, { AR5K_PHY_RF_CTL3, { 0x0a020001, 0x05010000, 0x0a020001 } }, { AR5K_PHY_RF_CTL4, { 0x00000e0e, 0x00000e0e, 0x00000e0e } }, { AR5K_PHY_PA_CTL, { 0x00000007, 0x0000000b, 0x0000000b } }, { AR5K_PHY_SETTLING, { 0x1372169c, 0x137216a8, 0x1372169c } }, { AR5K_PHY_GAIN, { 0x0018ba67, 0x0018ba69, 0x0018ba69 } }, { AR5K_PHY_DESIRED_SIZE, { 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0 } }, { AR5K_PHY_SIG, { 0x7e800d2e, 0x7ec00d2e, 0x7e800d2e } }, { AR5K_PHY_AGCCOARSE, { 0x31375d5e, 0x313a5d5e, 0x31375d5e } }, { AR5K_PHY_AGCCTL, { 0x0000bd10, 0x0000bd38, 0x0000bd10 } }, { AR5K_PHY_NF, { 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, { AR5K_PHY_RX_DELAY, { 0x00002710, 0x0000157c, 0x00002710 } }, { AR5K_PHY(70), { 0x00000190, 0x00000084, 0x00000190 } }, { AR5K_PHY_FRAME_CTL_5211, { 0x6fe01020, 0x6fe00920, 0x6fe01020 } }, { AR5K_PHY_PCDAC_TXPOWER_BASE, { 0x05ff14ff, 0x05ff14ff, 0x05ff19ff } }, { AR5K_RF_BUFFER_CONTROL_4, { 0x00000010, 0x00000010, 0x00000010 } }, }; /* Initial register settings for AR5212 and newer chips */ static const struct ath5k_ini ar5212_ini_common_start[] = { { AR5K_RXDP, 0x00000000 }, { AR5K_RXCFG, 0x00000005 }, { AR5K_MIBC, 0x00000000 }, { AR5K_TOPS, 0x00000008 }, { AR5K_RXNOFRM, 0x00000008 }, { AR5K_TXNOFRM, 0x00000010 }, { AR5K_RPGTO, 0x00000000 }, { AR5K_RFCNT, 0x0000001f }, { AR5K_QUEUE_TXDP(0), 0x00000000 }, { AR5K_QUEUE_TXDP(1), 0x00000000 }, { AR5K_QUEUE_TXDP(2), 0x00000000 }, { AR5K_QUEUE_TXDP(3), 0x00000000 }, { AR5K_QUEUE_TXDP(4), 0x00000000 }, { AR5K_QUEUE_TXDP(5), 0x00000000 }, { AR5K_QUEUE_TXDP(6), 0x00000000 }, { AR5K_QUEUE_TXDP(7), 0x00000000 }, { AR5K_QUEUE_TXDP(8), 0x00000000 }, { AR5K_QUEUE_TXDP(9), 0x00000000 }, { AR5K_DCU_FP, 0x00000000 }, { AR5K_DCU_TXP, 0x00000000 }, /* Tx filter table 0 (32 entries) */ { AR5K_DCU_TX_FILTER_0(0), 0x00000000 }, /* DCU 0 */ { AR5K_DCU_TX_FILTER_0(1), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(2), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(3), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(4), 0x00000000 }, /* DCU 1 */ { AR5K_DCU_TX_FILTER_0(5), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(6), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(7), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(8), 0x00000000 }, /* DCU 2 */ { AR5K_DCU_TX_FILTER_0(9), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(10), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(11), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(12), 0x00000000 }, /* DCU 3 */ { AR5K_DCU_TX_FILTER_0(13), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(14), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(15), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(16), 0x00000000 }, /* DCU 4 */ { AR5K_DCU_TX_FILTER_0(17), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(18), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(19), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(20), 0x00000000 }, /* DCU 5 */ { AR5K_DCU_TX_FILTER_0(21), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(22), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(23), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(24), 0x00000000 }, /* DCU 6 */ { AR5K_DCU_TX_FILTER_0(25), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(26), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(27), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(28), 0x00000000 }, /* DCU 7 */ { AR5K_DCU_TX_FILTER_0(29), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(30), 0x00000000 }, { AR5K_DCU_TX_FILTER_0(31), 0x00000000 }, /* Tx filter table 1 (16 entries) */ { AR5K_DCU_TX_FILTER_1(0), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(1), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(2), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(3), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(4), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(5), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(6), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(7), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(8), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(9), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(10), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(11), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(12), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(13), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(14), 0x00000000 }, { AR5K_DCU_TX_FILTER_1(15), 0x00000000 }, { AR5K_DCU_TX_FILTER_CLR, 0x00000000 }, { AR5K_DCU_TX_FILTER_SET, 0x00000000 }, { AR5K_STA_ID1, 0x00000000 }, { AR5K_BSS_ID0, 0x00000000 }, { AR5K_BSS_ID1, 0x00000000 }, { AR5K_BEACON_5211, 0x00000000 }, { AR5K_CFP_PERIOD_5211, 0x00000000 }, { AR5K_TIMER0_5211, 0x00000030 }, { AR5K_TIMER1_5211, 0x0007ffff }, { AR5K_TIMER2_5211, 0x01ffffff }, { AR5K_TIMER3_5211, 0x00000031 }, { AR5K_CFP_DUR_5211, 0x00000000 }, { AR5K_RX_FILTER_5211, 0x00000000 }, { AR5K_DIAG_SW_5211, 0x00000000 }, { AR5K_ADDAC_TEST, 0x00000000 }, { AR5K_DEFAULT_ANTENNA, 0x00000000 }, { AR5K_FRAME_CTL_QOSM, 0x000fc78f }, { AR5K_XRMODE, 0x2a82301a }, { AR5K_XRDELAY, 0x05dc01e0 }, { AR5K_XRTIMEOUT, 0x1f402710 }, { AR5K_XRCHIRP, 0x01f40000 }, { AR5K_XRSTOMP, 0x00001e1c }, { AR5K_SLEEP0, 0x0002aaaa }, { AR5K_SLEEP1, 0x02005555 }, { AR5K_SLEEP2, 0x00000000 }, { AR_BSSMSKL, 0xffffffff }, { AR_BSSMSKU, 0x0000ffff }, { AR5K_TXPC, 0x00000000 }, { AR5K_PROFCNT_TX, 0x00000000 }, { AR5K_PROFCNT_RX, 0x00000000 }, { AR5K_PROFCNT_RXCLR, 0x00000000 }, { AR5K_PROFCNT_CYCLE, 0x00000000 }, { AR5K_QUIET_CTL1, 0x00000088 }, /* Initial rate duration table (32 entries )*/ { AR5K_RATE_DUR(0), 0x00000000 }, { AR5K_RATE_DUR(1), 0x0000008c }, { AR5K_RATE_DUR(2), 0x000000e4 }, { AR5K_RATE_DUR(3), 0x000002d5 }, { AR5K_RATE_DUR(4), 0x00000000 }, { AR5K_RATE_DUR(5), 0x00000000 }, { AR5K_RATE_DUR(6), 0x000000a0 }, { AR5K_RATE_DUR(7), 0x000001c9 }, { AR5K_RATE_DUR(8), 0x0000002c }, { AR5K_RATE_DUR(9), 0x0000002c }, { AR5K_RATE_DUR(10), 0x00000030 }, { AR5K_RATE_DUR(11), 0x0000003c }, { AR5K_RATE_DUR(12), 0x0000002c }, { AR5K_RATE_DUR(13), 0x0000002c }, { AR5K_RATE_DUR(14), 0x00000030 }, { AR5K_RATE_DUR(15), 0x0000003c }, { AR5K_RATE_DUR(16), 0x00000000 }, { AR5K_RATE_DUR(17), 0x00000000 }, { AR5K_RATE_DUR(18), 0x00000000 }, { AR5K_RATE_DUR(19), 0x00000000 }, { AR5K_RATE_DUR(20), 0x00000000 }, { AR5K_RATE_DUR(21), 0x00000000 }, { AR5K_RATE_DUR(22), 0x00000000 }, { AR5K_RATE_DUR(23), 0x00000000 }, { AR5K_RATE_DUR(24), 0x000000d5 }, { AR5K_RATE_DUR(25), 0x000000df }, { AR5K_RATE_DUR(26), 0x00000102 }, { AR5K_RATE_DUR(27), 0x0000013a }, { AR5K_RATE_DUR(28), 0x00000075 }, { AR5K_RATE_DUR(29), 0x0000007f }, { AR5K_RATE_DUR(30), 0x000000a2 }, { AR5K_RATE_DUR(31), 0x00000000 }, { AR5K_QUIET_CTL2, 0x00010002 }, { AR5K_TSF_PARM, 0x00000001 }, { AR5K_QOS_NOACK, 0x000000c0 }, { AR5K_PHY_ERR_FIL, 0x00000000 }, { AR5K_XRLAT_TX, 0x00000168 }, { AR5K_ACKSIFS, 0x00000000 }, /* Rate -> db table * notice ...03<-02<-01<-00 ! */ { AR5K_RATE2DB(0), 0x03020100 }, { AR5K_RATE2DB(1), 0x07060504 }, { AR5K_RATE2DB(2), 0x0b0a0908 }, { AR5K_RATE2DB(3), 0x0f0e0d0c }, { AR5K_RATE2DB(4), 0x13121110 }, { AR5K_RATE2DB(5), 0x17161514 }, { AR5K_RATE2DB(6), 0x1b1a1918 }, { AR5K_RATE2DB(7), 0x1f1e1d1c }, /* Db -> Rate table */ { AR5K_DB2RATE(0), 0x03020100 }, { AR5K_DB2RATE(1), 0x07060504 }, { AR5K_DB2RATE(2), 0x0b0a0908 }, { AR5K_DB2RATE(3), 0x0f0e0d0c }, { AR5K_DB2RATE(4), 0x13121110 }, { AR5K_DB2RATE(5), 0x17161514 }, { AR5K_DB2RATE(6), 0x1b1a1918 }, { AR5K_DB2RATE(7), 0x1f1e1d1c }, /* PHY registers (Common settings * for all chips/modes) */ { AR5K_PHY(3), 0xad848e19 }, { AR5K_PHY(4), 0x7d28e000 }, { AR5K_PHY_TIMING_3, 0x9c0a9f6b }, { AR5K_PHY_ACT, 0x00000000 }, { AR5K_PHY(16), 0x206a017a }, { AR5K_PHY(21), 0x00000859 }, { AR5K_PHY_BIN_MASK_1, 0x00000000 }, { AR5K_PHY_BIN_MASK_2, 0x00000000 }, { AR5K_PHY_BIN_MASK_3, 0x00000000 }, { AR5K_PHY_BIN_MASK_CTL, 0x00800000 }, { AR5K_PHY_ANT_CTL, 0x00000001 }, /*{ AR5K_PHY(71), 0x0000092a },*/ /* Old value */ { AR5K_PHY_MAX_RX_LEN, 0x00000c80 }, { AR5K_PHY_IQ, 0x05100000 }, { AR5K_PHY_WARM_RESET, 0x00000001 }, { AR5K_PHY_CTL, 0x00000004 }, { AR5K_PHY_TXPOWER_RATE1, 0x1e1f2022 }, { AR5K_PHY_TXPOWER_RATE2, 0x0a0b0c0d }, { AR5K_PHY_TXPOWER_RATE_MAX, 0x0000003f }, { AR5K_PHY(82), 0x9280b212 }, { AR5K_PHY_RADAR, 0x5d50e188 }, /*{ AR5K_PHY(86), 0x000000ff },*/ { AR5K_PHY(87), 0x004b6a8e }, { AR5K_PHY_NFTHRES, 0x000003ce }, { AR5K_PHY_RESTART, 0x192fb515 }, { AR5K_PHY(94), 0x00000001 }, { AR5K_PHY_RFBUS_REQ, 0x00000000 }, /*{ AR5K_PHY(644), 0x0080a333 },*/ /* Old value */ /*{ AR5K_PHY(645), 0x00206c10 },*/ /* Old value */ { AR5K_PHY(644), 0x00806333 }, { AR5K_PHY(645), 0x00106c10 }, { AR5K_PHY(646), 0x009c4060 }, /* { AR5K_PHY(647), 0x1483800a }, */ /* { AR5K_PHY(648), 0x01831061 }, */ /* Old value */ { AR5K_PHY(648), 0x018830c6 }, { AR5K_PHY(649), 0x00000400 }, /*{ AR5K_PHY(650), 0x000001b5 },*/ { AR5K_PHY(651), 0x00000000 }, { AR5K_PHY_TXPOWER_RATE3, 0x20202020 }, { AR5K_PHY_TXPOWER_RATE4, 0x20202020 }, /*{ AR5K_PHY(655), 0x13c889af },*/ { AR5K_PHY(656), 0x38490a20 }, { AR5K_PHY(657), 0x00007bb6 }, { AR5K_PHY(658), 0x0fff3ffc }, }; /* Initial mode-specific settings for AR5212 (Written before ar5212_ini) */ static const struct ath5k_ini_mode ar5212_ini_mode_start[] = { { AR5K_QUEUE_DFS_LOCAL_IFS(0), /* A/XR B G */ { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(1), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(2), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(3), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(4), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(5), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(6), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(7), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(8), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_QUEUE_DFS_LOCAL_IFS(9), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, { AR5K_DCU_GBL_IFS_SIFS, { 0x00000230, 0x000000b0, 0x00000160 } }, { AR5K_DCU_GBL_IFS_SLOT, { 0x00000168, 0x000001b8, 0x0000018c } }, { AR5K_DCU_GBL_IFS_EIFS, { 0x00000e60, 0x00001f1c, 0x00003e38 } }, { AR5K_DCU_GBL_IFS_MISC, { 0x0000a0e0, 0x00005880, 0x0000b0e0 } }, { AR5K_TIME_OUT, { 0x03e803e8, 0x04200420, 0x08400840 } }, { AR5K_PHY(8), { 0x02020200, 0x02010200, 0x02020200 } }, { AR5K_PHY_RF_CTL2, { 0x00000e0e, 0x00000707, 0x00000e0e } }, { AR5K_PHY_SETTLING, { 0x1372161c, 0x13721722, 0x137216a2 } }, { AR5K_PHY_AGCCTL, { 0x00009d10, 0x00009d18, 0x00009d18 } }, { AR5K_PHY_NF, { 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, { AR5K_PHY_WEAK_OFDM_HIGH_THR, { 0x409a4190, 0x409a4190, 0x409a4190 } }, { AR5K_PHY(70), { 0x000001b8, 0x00000084, 0x00000108 } }, { AR5K_PHY_OFDM_SELFCORR, { 0x10058a05, 0x10058a05, 0x10058a05 } }, { 0xa230, { 0x00000000, 0x00000000, 0x00000108 } }, }; /* Initial mode-specific settings for AR5212 + RF5111 * (Written after ar5212_ini) */ static const struct ath5k_ini_mode rf5111_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ { 0x00008015, 0x00008015, 0x00008015 } }, { AR5K_USEC_5211, { 0x128d8fa7, 0x04e00f95, 0x12e00fab } }, { AR5K_PHY_RF_CTL3, { 0x0a020001, 0x05010100, 0x0a020001 } }, { AR5K_PHY_RF_CTL4, { 0x00000e0e, 0x00000e0e, 0x00000e0e } }, { AR5K_PHY_PA_CTL, { 0x00000007, 0x0000000b, 0x0000000b } }, { AR5K_PHY_GAIN, { 0x0018da5a, 0x0018ca69, 0x0018ca69 } }, { AR5K_PHY_DESIRED_SIZE, { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, { AR5K_PHY_SIG, { 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e } }, { AR5K_PHY_AGCCOARSE, { 0x3137665e, 0x3137665e, 0x3137665e } }, { AR5K_PHY_WEAK_OFDM_LOW_THR, { 0x050cb081, 0x050cb081, 0x050cb080 } }, { AR5K_PHY_RX_DELAY, { 0x00002710, 0x0000157c, 0x00002af8 } }, { AR5K_PHY_FRAME_CTL_5211, { 0xf7b81020, 0xf7b80d20, 0xf7b81020 } }, { AR5K_PHY_GAIN_2GHZ, { 0x642c416a, 0x6440416a, 0x6440416a } }, { AR5K_PHY_CCK_RX_CTL_4, { 0x1883800a, 0x1873800a, 0x1883800a } }, }; /* Common for all modes */ static const struct ath5k_ini rf5111_ini_common_end[] = { { AR5K_DCU_FP, 0x00000000 }, { AR5K_PHY_AGC, 0x00000000 }, { AR5K_PHY_ADC_CTL, 0x00022ffe }, { 0x983c, 0x00020100 }, { AR5K_PHY_GAIN_OFFSET, 0x1284613c }, { AR5K_PHY_PAPD_PROBE, 0x00004883 }, { 0x9940, 0x00000004 }, { 0x9958, 0x000000ff }, { 0x9974, 0x00000000 }, { AR5K_PHY_SPENDING, 0x00000018 }, { AR5K_PHY_CCKTXCTL, 0x00000000 }, { AR5K_PHY_CCK_CROSSCORR, 0xd03e6788 }, { AR5K_PHY_DAG_CCK_CTL, 0x000001b5 }, { 0xa23c, 0x13c889af }, }; /* Initial mode-specific settings for AR5212 + RF5112 * (Written after ar5212_ini) */ static const struct ath5k_ini_mode rf5112_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ { 0x00008015, 0x00008015, 0x00008015 } }, { AR5K_USEC_5211, { 0x128d93a7, 0x04e01395, 0x12e013ab } }, { AR5K_PHY_RF_CTL3, { 0x0a020001, 0x05020100, 0x0a020001 } }, { AR5K_PHY_RF_CTL4, { 0x00000e0e, 0x00000e0e, 0x00000e0e } }, { AR5K_PHY_PA_CTL, { 0x00000007, 0x0000000b, 0x0000000b } }, { AR5K_PHY_GAIN, { 0x0018da6d, 0x0018ca75, 0x0018ca75 } }, { AR5K_PHY_DESIRED_SIZE, { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, { AR5K_PHY_SIG, { 0x7e800d2e, 0x7ee80d2e, 0x7ee80d2e } }, { AR5K_PHY_AGCCOARSE, { 0x3137665e, 0x3137665e, 0x3137665e } }, { AR5K_PHY_WEAK_OFDM_LOW_THR, { 0x050cb081, 0x050cb081, 0x050cb081 } }, { AR5K_PHY_RX_DELAY, { 0x000007d0, 0x0000044c, 0x00000898 } }, { AR5K_PHY_FRAME_CTL_5211, { 0xf7b81020, 0xf7b80d10, 0xf7b81010 } }, { AR5K_PHY_CCKTXCTL, { 0x00000000, 0x00000008, 0x00000008 } }, { AR5K_PHY_CCK_CROSSCORR, { 0xd6be6788, 0xd03e6788, 0xd03e6788 } }, { AR5K_PHY_GAIN_2GHZ, { 0x642c0140, 0x6442c160, 0x6442c160 } }, { AR5K_PHY_CCK_RX_CTL_4, { 0x1883800a, 0x1873800a, 0x1883800a } }, }; static const struct ath5k_ini rf5112_ini_common_end[] = { { AR5K_DCU_FP, 0x00000000 }, { AR5K_PHY_AGC, 0x00000000 }, { AR5K_PHY_ADC_CTL, 0x00022ffe }, { 0x983c, 0x00020100 }, { AR5K_PHY_GAIN_OFFSET, 0x1284613c }, { AR5K_PHY_PAPD_PROBE, 0x00004882 }, { 0x9940, 0x00000004 }, { 0x9958, 0x000000ff }, { 0x9974, 0x00000000 }, { AR5K_PHY_DAG_CCK_CTL, 0x000001b5 }, { 0xa23c, 0x13c889af }, }; /* Initial mode-specific settings for RF5413/5414 * (Written after ar5212_ini) */ static const struct ath5k_ini_mode rf5413_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ { 0x00000015, 0x00000015, 0x00000015 } }, { AR5K_USEC_5211, { 0x128d93a7, 0x04e01395, 0x12e013ab } }, { AR5K_PHY_RF_CTL3, { 0x0a020001, 0x05020100, 0x0a020001 } }, { AR5K_PHY_RF_CTL4, { 0x00000e0e, 0x00000e0e, 0x00000e0e } }, { AR5K_PHY_PA_CTL, { 0x00000007, 0x0000000b, 0x0000000b } }, { AR5K_PHY_GAIN, { 0x0018fa61, 0x001a1a63, 0x001a1a63 } }, { AR5K_PHY_DESIRED_SIZE, { 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da } }, { AR5K_PHY_SIG, { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } }, { AR5K_PHY_AGCCOARSE, { 0x3139605e, 0x3139605e, 0x3139605e } }, { AR5K_PHY_WEAK_OFDM_LOW_THR, { 0x050cb081, 0x050cb081, 0x050cb081 } }, { AR5K_PHY_RX_DELAY, { 0x000007d0, 0x0000044c, 0x00000898 } }, { AR5K_PHY_FRAME_CTL_5211, { 0xf7b81000, 0xf7b80d00, 0xf7b81000 } }, { AR5K_PHY_CCKTXCTL, { 0x00000000, 0x00000000, 0x00000000 } }, { AR5K_PHY_CCK_CROSSCORR, { 0xd6be6788, 0xd03e6788, 0xd03e6788 } }, { AR5K_PHY_GAIN_2GHZ, { 0x002ec1e0, 0x002ac120, 0x002ac120 } }, { AR5K_PHY_CCK_RX_CTL_4, { 0x1883800a, 0x1863800a, 0x1883800a } }, { 0xa300, { 0x18010000, 0x18010000, 0x18010000 } }, { 0xa304, { 0x30032602, 0x30032602, 0x30032602 } }, { 0xa308, { 0x48073e06, 0x48073e06, 0x48073e06 } }, { 0xa30c, { 0x560b4c0a, 0x560b4c0a, 0x560b4c0a } }, { 0xa310, { 0x641a600f, 0x641a600f, 0x641a600f } }, { 0xa314, { 0x784f6e1b, 0x784f6e1b, 0x784f6e1b } }, { 0xa318, { 0x868f7c5a, 0x868f7c5a, 0x868f7c5a } }, { 0xa31c, { 0x90cf865b, 0x8ecf865b, 0x8ecf865b } }, { 0xa320, { 0x9d4f970f, 0x9b4f970f, 0x9b4f970f } }, { 0xa324, { 0xa7cfa38f, 0xa3cf9f8f, 0xa3cf9f8f } }, { 0xa328, { 0xb55faf1f, 0xb35faf1f, 0xb35faf1f } }, { 0xa32c, { 0xbddfb99f, 0xbbdfb99f, 0xbbdfb99f } }, { 0xa330, { 0xcb7fc53f, 0xcb7fc73f, 0xcb7fc73f } }, { 0xa334, { 0xd5ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf } }, }; static const struct ath5k_ini rf5413_ini_common_end[] = { { AR5K_DCU_FP, 0x000003e0 }, { AR5K_5414_CBCFG, 0x00000010 }, { AR5K_SEQ_MASK, 0x0000000f }, { 0x809c, 0x00000000 }, { 0x80a0, 0x00000000 }, { AR5K_MIC_QOS_CTL, 0x00000000 }, { AR5K_MIC_QOS_SEL, 0x00000000 }, { AR5K_MISC_MODE, 0x00000000 }, { AR5K_OFDM_FIL_CNT, 0x00000000 }, { AR5K_CCK_FIL_CNT, 0x00000000 }, { AR5K_PHYERR_CNT1, 0x00000000 }, { AR5K_PHYERR_CNT1_MASK, 0x00000000 }, { AR5K_PHYERR_CNT2, 0x00000000 }, { AR5K_PHYERR_CNT2_MASK, 0x00000000 }, { AR5K_TSF_THRES, 0x00000000 }, { 0x8140, 0x800003f9 }, { 0x8144, 0x00000000 }, { AR5K_PHY_AGC, 0x00000000 }, { AR5K_PHY_ADC_CTL, 0x0000a000 }, { 0x983c, 0x00200400 }, { AR5K_PHY_GAIN_OFFSET, 0x1284233c }, { AR5K_PHY_SCR, 0x0000001f }, { AR5K_PHY_SLMT, 0x00000080 }, { AR5K_PHY_SCAL, 0x0000000e }, { 0x9958, 0x00081fff }, { AR5K_PHY_TIMING_7, 0x00000000 }, { AR5K_PHY_TIMING_8, 0x02800000 }, { AR5K_PHY_TIMING_11, 0x00000000 }, { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 }, { 0x99e4, 0xaaaaaaaa }, { 0x99e8, 0x3c466478 }, { 0x99ec, 0x000000aa }, { AR5K_PHY_SCLOCK, 0x0000000c }, { AR5K_PHY_SDELAY, 0x000000ff }, { AR5K_PHY_SPENDING, 0x00000014 }, { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 }, { 0xa23c, 0x93c889af }, { AR5K_PHY_FAST_ADC, 0x00000001 }, { 0xa250, 0x0000a000 }, { AR5K_PHY_BLUETOOTH, 0x00000000 }, { AR5K_PHY_TPC_RG1, 0x0cc75380 }, { 0xa25c, 0x0f0f0f01 }, { 0xa260, 0x5f690f01 }, { 0xa264, 0x00418a11 }, { 0xa268, 0x00000000 }, { AR5K_PHY_TPC_RG5, 0x0c30c16a }, { 0xa270, 0x00820820 }, { 0xa274, 0x081b7caa }, { 0xa278, 0x1ce739ce }, { 0xa27c, 0x051701ce }, { 0xa338, 0x00000000 }, { 0xa33c, 0x00000000 }, { 0xa340, 0x00000000 }, { 0xa344, 0x00000000 }, { 0xa348, 0x3fffffff }, { 0xa34c, 0x3fffffff }, { 0xa350, 0x3fffffff }, { 0xa354, 0x0003ffff }, { 0xa358, 0x79a8aa1f }, { 0xa35c, 0x066c420f }, { 0xa360, 0x0f282207 }, { 0xa364, 0x17601685 }, { 0xa368, 0x1f801104 }, { 0xa36c, 0x37a00c03 }, { 0xa370, 0x3fc40883 }, { 0xa374, 0x57c00803 }, { 0xa378, 0x5fd80682 }, { 0xa37c, 0x7fe00482 }, { 0xa380, 0x7f3c7bba }, { 0xa384, 0xf3307ff0 }, }; /* Initial mode-specific settings for RF2413/2414 * (Written after ar5212_ini) */ /* XXX: a mode ? */ static const struct ath5k_ini_mode rf2413_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ { 0x00000015, 0x00000015, 0x00000015 } }, { AR5K_USEC_5211, { 0x128d93a7, 0x04e01395, 0x12e013ab } }, { AR5K_PHY_RF_CTL3, { 0x0a020001, 0x05020000, 0x0a020001 } }, { AR5K_PHY_RF_CTL4, { 0x00000e00, 0x00000e00, 0x00000e00 } }, { AR5K_PHY_PA_CTL, { 0x00000002, 0x0000000a, 0x0000000a } }, { AR5K_PHY_GAIN, { 0x0018da6d, 0x001a6a64, 0x001a6a64 } }, { AR5K_PHY_DESIRED_SIZE, { 0x0de8b4e0, 0x0de8b0da, 0x0c98b0da } }, { AR5K_PHY_SIG, { 0x7e800d2e, 0x7ee80d2e, 0x7ec80d2e } }, { AR5K_PHY_AGCCOARSE, { 0x3137665e, 0x3137665e, 0x3139605e } }, { AR5K_PHY_WEAK_OFDM_LOW_THR, { 0x050cb081, 0x050cb081, 0x050cb081 } }, { AR5K_PHY_RX_DELAY, { 0x000007d0, 0x0000044c, 0x00000898 } }, { AR5K_PHY_FRAME_CTL_5211, { 0xf7b81000, 0xf7b80d00, 0xf7b81000 } }, { AR5K_PHY_CCKTXCTL, { 0x00000000, 0x00000000, 0x00000000 } }, { AR5K_PHY_CCK_CROSSCORR, { 0xd6be6788, 0xd03e6788, 0xd03e6788 } }, { AR5K_PHY_GAIN_2GHZ, { 0x002c0140, 0x0042c140, 0x0042c140 } }, { AR5K_PHY_CCK_RX_CTL_4, { 0x1883800a, 0x1863800a, 0x1883800a } }, }; static const struct ath5k_ini rf2413_ini_common_end[] = { { AR5K_DCU_FP, 0x000003e0 }, { AR5K_SEQ_MASK, 0x0000000f }, { AR5K_MIC_QOS_CTL, 0x00000000 }, { AR5K_MIC_QOS_SEL, 0x00000000 }, { AR5K_MISC_MODE, 0x00000000 }, { AR5K_OFDM_FIL_CNT, 0x00000000 }, { AR5K_CCK_FIL_CNT, 0x00000000 }, { AR5K_PHYERR_CNT1, 0x00000000 }, { AR5K_PHYERR_CNT1_MASK, 0x00000000 }, { AR5K_PHYERR_CNT2, 0x00000000 }, { AR5K_PHYERR_CNT2_MASK, 0x00000000 }, { AR5K_TSF_THRES, 0x00000000 }, { 0x8140, 0x800000a8 }, { 0x8144, 0x00000000 }, { AR5K_PHY_AGC, 0x00000000 }, { AR5K_PHY_ADC_CTL, 0x0000a000 }, { 0x983c, 0x00200400 }, { AR5K_PHY_GAIN_OFFSET, 0x1284233c }, { AR5K_PHY_SCR, 0x0000001f }, { AR5K_PHY_SLMT, 0x00000080 }, { AR5K_PHY_SCAL, 0x0000000e }, { 0x9958, 0x000000ff }, { AR5K_PHY_TIMING_7, 0x00000000 }, { AR5K_PHY_TIMING_8, 0x02800000 }, { AR5K_PHY_TIMING_11, 0x00000000 }, { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 }, { 0x99e4, 0xaaaaaaaa }, { 0x99e8, 0x3c466478 }, { 0x99ec, 0x000000aa }, { AR5K_PHY_SCLOCK, 0x0000000c }, { AR5K_PHY_SDELAY, 0x000000ff }, { AR5K_PHY_SPENDING, 0x00000014 }, { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 }, { 0xa23c, 0x93c889af }, { AR5K_PHY_FAST_ADC, 0x00000001 }, { 0xa250, 0x0000a000 }, { AR5K_PHY_BLUETOOTH, 0x00000000 }, { AR5K_PHY_TPC_RG1, 0x0cc75380 }, { 0xa25c, 0x0f0f0f01 }, { 0xa260, 0x5f690f01 }, { 0xa264, 0x00418a11 }, { 0xa268, 0x00000000 }, { AR5K_PHY_TPC_RG5, 0x0c30c16a }, { 0xa270, 0x00820820 }, { 0xa274, 0x001b7caa }, { 0xa278, 0x1ce739ce }, { 0xa27c, 0x051701ce }, { 0xa300, 0x18010000 }, { 0xa304, 0x30032602 }, { 0xa308, 0x48073e06 }, { 0xa30c, 0x560b4c0a }, { 0xa310, 0x641a600f }, { 0xa314, 0x784f6e1b }, { 0xa318, 0x868f7c5a }, { 0xa31c, 0x8ecf865b }, { 0xa320, 0x9d4f970f }, { 0xa324, 0xa5cfa18f }, { 0xa328, 0xb55faf1f }, { 0xa32c, 0xbddfb99f }, { 0xa330, 0xcd7fc73f }, { 0xa334, 0xd5ffd1bf }, { 0xa338, 0x00000000 }, { 0xa33c, 0x00000000 }, { 0xa340, 0x00000000 }, { 0xa344, 0x00000000 }, { 0xa348, 0x3fffffff }, { 0xa34c, 0x3fffffff }, { 0xa350, 0x3fffffff }, { 0xa354, 0x0003ffff }, { 0xa358, 0x79a8aa1f }, { 0xa35c, 0x066c420f }, { 0xa360, 0x0f282207 }, { 0xa364, 0x17601685 }, { 0xa368, 0x1f801104 }, { 0xa36c, 0x37a00c03 }, { 0xa370, 0x3fc40883 }, { 0xa374, 0x57c00803 }, { 0xa378, 0x5fd80682 }, { 0xa37c, 0x7fe00482 }, { 0xa380, 0x7f3c7bba }, { 0xa384, 0xf3307ff0 }, }; /* Initial mode-specific settings for RF2425 * (Written after ar5212_ini) */ /* XXX: a mode ? */ static const struct ath5k_ini_mode rf2425_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ { 0x00000015, 0x00000015, 0x00000015 } }, { AR5K_USEC_5211, { 0x128d93a7, 0x04e01395, 0x12e013ab } }, { AR5K_PHY_RF_CTL3, { 0x0a020001, 0x05020100, 0x0a020001 } }, { AR5K_PHY_RF_CTL4, { 0x00000e0e, 0x00000e0e, 0x00000e0e } }, { AR5K_PHY_PA_CTL, { 0x00000003, 0x0000000b, 0x0000000b } }, { AR5K_PHY_SETTLING, { 0x1372161c, 0x13721722, 0x13721422 } }, { AR5K_PHY_GAIN, { 0x0018fa61, 0x00199a65, 0x00199a65 } }, { AR5K_PHY_DESIRED_SIZE, { 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da } }, { AR5K_PHY_SIG, { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } }, { AR5K_PHY_AGCCOARSE, { 0x3139605e, 0x3139605e, 0x3139605e } }, { AR5K_PHY_WEAK_OFDM_LOW_THR, { 0x050cb081, 0x050cb081, 0x050cb081 } }, { AR5K_PHY_RX_DELAY, { 0x000007d0, 0x0000044c, 0x00000898 } }, { AR5K_PHY_FRAME_CTL_5211, { 0xf7b81000, 0xf7b80d00, 0xf7b81000 } }, { AR5K_PHY_CCKTXCTL, { 0x00000000, 0x00000000, 0x00000000 } }, { AR5K_PHY_CCK_CROSSCORR, { 0xd6be6788, 0xd03e6788, 0xd03e6788 } }, { AR5K_PHY_GAIN_2GHZ, { 0x00000140, 0x0052c140, 0x0052c140 } }, { AR5K_PHY_CCK_RX_CTL_4, { 0x1883800a, 0x1863800a, 0x1883800a } }, { 0xa324, { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, { 0xa328, { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, { 0xa32c, { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, { 0xa330, { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, { 0xa334, { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } }, }; static const struct ath5k_ini rf2425_ini_common_end[] = { { AR5K_DCU_FP, 0x000003e0 }, { AR5K_SEQ_MASK, 0x0000000f }, { 0x809c, 0x00000000 }, { 0x80a0, 0x00000000 }, { AR5K_MIC_QOS_CTL, 0x00000000 }, { AR5K_MIC_QOS_SEL, 0x00000000 }, { AR5K_MISC_MODE, 0x00000000 }, { AR5K_OFDM_FIL_CNT, 0x00000000 }, { AR5K_CCK_FIL_CNT, 0x00000000 }, { AR5K_PHYERR_CNT1, 0x00000000 }, { AR5K_PHYERR_CNT1_MASK, 0x00000000 }, { AR5K_PHYERR_CNT2, 0x00000000 }, { AR5K_PHYERR_CNT2_MASK, 0x00000000 }, { AR5K_TSF_THRES, 0x00000000 }, { 0x8140, 0x800003f9 }, { 0x8144, 0x00000000 }, { AR5K_PHY_AGC, 0x00000000 }, { AR5K_PHY_ADC_CTL, 0x0000a000 }, { 0x983c, 0x00200400 }, { AR5K_PHY_GAIN_OFFSET, 0x1284233c }, { AR5K_PHY_SCR, 0x0000001f }, { AR5K_PHY_SLMT, 0x00000080 }, { AR5K_PHY_SCAL, 0x0000000e }, { 0x9958, 0x00081fff }, { AR5K_PHY_TIMING_7, 0x00000000 }, { AR5K_PHY_TIMING_8, 0x02800000 }, { AR5K_PHY_TIMING_11, 0x00000000 }, { 0x99dc, 0xfebadbe8 }, { AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000 }, { 0x99e4, 0xaaaaaaaa }, { 0x99e8, 0x3c466478 }, { 0x99ec, 0x000000aa }, { AR5K_PHY_SCLOCK, 0x0000000c }, { AR5K_PHY_SDELAY, 0x000000ff }, { AR5K_PHY_SPENDING, 0x00000014 }, { AR5K_PHY_DAG_CCK_CTL, 0x000009b5 }, { AR5K_PHY_TXPOWER_RATE3, 0x20202020 }, { AR5K_PHY_TXPOWER_RATE4, 0x20202020 }, { 0xa23c, 0x93c889af }, { AR5K_PHY_FAST_ADC, 0x00000001 }, { 0xa250, 0x0000a000 }, { AR5K_PHY_BLUETOOTH, 0x00000000 }, { AR5K_PHY_TPC_RG1, 0x0cc75380 }, { 0xa25c, 0x0f0f0f01 }, { 0xa260, 0x5f690f01 }, { 0xa264, 0x00418a11 }, { 0xa268, 0x00000000 }, { AR5K_PHY_TPC_RG5, 0x0c30c166 }, { 0xa270, 0x00820820 }, { 0xa274, 0x081a3caa }, { 0xa278, 0x1ce739ce }, { 0xa27c, 0x051701ce }, { 0xa300, 0x16010000 }, { 0xa304, 0x2c032402 }, { 0xa308, 0x48433e42 }, { 0xa30c, 0x5a0f500b }, { 0xa310, 0x6c4b624a }, { 0xa314, 0x7e8b748a }, { 0xa318, 0x96cf8ccb }, { 0xa31c, 0xa34f9d0f }, { 0xa320, 0xa7cfa58f }, { 0xa348, 0x3fffffff }, { 0xa34c, 0x3fffffff }, { 0xa350, 0x3fffffff }, { 0xa354, 0x0003ffff }, { 0xa358, 0x79a8aa1f }, { 0xa35c, 0x066c420f }, { 0xa360, 0x0f282207 }, { 0xa364, 0x17601685 }, { 0xa368, 0x1f801104 }, { 0xa36c, 0x37a00c03 }, { 0xa370, 0x3fc40883 }, { 0xa374, 0x57c00803 }, { 0xa378, 0x5fd80682 }, { 0xa37c, 0x7fe00482 }, { 0xa380, 0x7f3c7bba }, { 0xa384, 0xf3307ff0 }, }; /* * Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with * RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI) */ /* RF5111 Initial BaseBand Gain settings */ static const struct ath5k_ini rf5111_ini_bbgain[] = { { AR5K_BB_GAIN(0), 0x00000000 }, { AR5K_BB_GAIN(1), 0x00000020 }, { AR5K_BB_GAIN(2), 0x00000010 }, { AR5K_BB_GAIN(3), 0x00000030 }, { AR5K_BB_GAIN(4), 0x00000008 }, { AR5K_BB_GAIN(5), 0x00000028 }, { AR5K_BB_GAIN(6), 0x00000004 }, { AR5K_BB_GAIN(7), 0x00000024 }, { AR5K_BB_GAIN(8), 0x00000014 }, { AR5K_BB_GAIN(9), 0x00000034 }, { AR5K_BB_GAIN(10), 0x0000000c }, { AR5K_BB_GAIN(11), 0x0000002c }, { AR5K_BB_GAIN(12), 0x00000002 }, { AR5K_BB_GAIN(13), 0x00000022 }, { AR5K_BB_GAIN(14), 0x00000012 }, { AR5K_BB_GAIN(15), 0x00000032 }, { AR5K_BB_GAIN(16), 0x0000000a }, { AR5K_BB_GAIN(17), 0x0000002a }, { AR5K_BB_GAIN(18), 0x00000006 }, { AR5K_BB_GAIN(19), 0x00000026 }, { AR5K_BB_GAIN(20), 0x00000016 }, { AR5K_BB_GAIN(21), 0x00000036 }, { AR5K_BB_GAIN(22), 0x0000000e }, { AR5K_BB_GAIN(23), 0x0000002e }, { AR5K_BB_GAIN(24), 0x00000001 }, { AR5K_BB_GAIN(25), 0x00000021 }, { AR5K_BB_GAIN(26), 0x00000011 }, { AR5K_BB_GAIN(27), 0x00000031 }, { AR5K_BB_GAIN(28), 0x00000009 }, { AR5K_BB_GAIN(29), 0x00000029 }, { AR5K_BB_GAIN(30), 0x00000005 }, { AR5K_BB_GAIN(31), 0x00000025 }, { AR5K_BB_GAIN(32), 0x00000015 }, { AR5K_BB_GAIN(33), 0x00000035 }, { AR5K_BB_GAIN(34), 0x0000000d }, { AR5K_BB_GAIN(35), 0x0000002d }, { AR5K_BB_GAIN(36), 0x00000003 }, { AR5K_BB_GAIN(37), 0x00000023 }, { AR5K_BB_GAIN(38), 0x00000013 }, { AR5K_BB_GAIN(39), 0x00000033 }, { AR5K_BB_GAIN(40), 0x0000000b }, { AR5K_BB_GAIN(41), 0x0000002b }, { AR5K_BB_GAIN(42), 0x0000002b }, { AR5K_BB_GAIN(43), 0x0000002b }, { AR5K_BB_GAIN(44), 0x0000002b }, { AR5K_BB_GAIN(45), 0x0000002b }, { AR5K_BB_GAIN(46), 0x0000002b }, { AR5K_BB_GAIN(47), 0x0000002b }, { AR5K_BB_GAIN(48), 0x0000002b }, { AR5K_BB_GAIN(49), 0x0000002b }, { AR5K_BB_GAIN(50), 0x0000002b }, { AR5K_BB_GAIN(51), 0x0000002b }, { AR5K_BB_GAIN(52), 0x0000002b }, { AR5K_BB_GAIN(53), 0x0000002b }, { AR5K_BB_GAIN(54), 0x0000002b }, { AR5K_BB_GAIN(55), 0x0000002b }, { AR5K_BB_GAIN(56), 0x0000002b }, { AR5K_BB_GAIN(57), 0x0000002b }, { AR5K_BB_GAIN(58), 0x0000002b }, { AR5K_BB_GAIN(59), 0x0000002b }, { AR5K_BB_GAIN(60), 0x0000002b }, { AR5K_BB_GAIN(61), 0x0000002b }, { AR5K_BB_GAIN(62), 0x00000002 }, { AR5K_BB_GAIN(63), 0x00000016 }, }; /* RF5112 Initial BaseBand Gain settings (Same for RF5413/5414+) */ static const struct ath5k_ini rf5112_ini_bbgain[] = { { AR5K_BB_GAIN(0), 0x00000000 }, { AR5K_BB_GAIN(1), 0x00000001 }, { AR5K_BB_GAIN(2), 0x00000002 }, { AR5K_BB_GAIN(3), 0x00000003 }, { AR5K_BB_GAIN(4), 0x00000004 }, { AR5K_BB_GAIN(5), 0x00000005 }, { AR5K_BB_GAIN(6), 0x00000008 }, { AR5K_BB_GAIN(7), 0x00000009 }, { AR5K_BB_GAIN(8), 0x0000000a }, { AR5K_BB_GAIN(9), 0x0000000b }, { AR5K_BB_GAIN(10), 0x0000000c }, { AR5K_BB_GAIN(11), 0x0000000d }, { AR5K_BB_GAIN(12), 0x00000010 }, { AR5K_BB_GAIN(13), 0x00000011 }, { AR5K_BB_GAIN(14), 0x00000012 }, { AR5K_BB_GAIN(15), 0x00000013 }, { AR5K_BB_GAIN(16), 0x00000014 }, { AR5K_BB_GAIN(17), 0x00000015 }, { AR5K_BB_GAIN(18), 0x00000018 }, { AR5K_BB_GAIN(19), 0x00000019 }, { AR5K_BB_GAIN(20), 0x0000001a }, { AR5K_BB_GAIN(21), 0x0000001b }, { AR5K_BB_GAIN(22), 0x0000001c }, { AR5K_BB_GAIN(23), 0x0000001d }, { AR5K_BB_GAIN(24), 0x00000020 }, { AR5K_BB_GAIN(25), 0x00000021 }, { AR5K_BB_GAIN(26), 0x00000022 }, { AR5K_BB_GAIN(27), 0x00000023 }, { AR5K_BB_GAIN(28), 0x00000024 }, { AR5K_BB_GAIN(29), 0x00000025 }, { AR5K_BB_GAIN(30), 0x00000028 }, { AR5K_BB_GAIN(31), 0x00000029 }, { AR5K_BB_GAIN(32), 0x0000002a }, { AR5K_BB_GAIN(33), 0x0000002b }, { AR5K_BB_GAIN(34), 0x0000002c }, { AR5K_BB_GAIN(35), 0x0000002d }, { AR5K_BB_GAIN(36), 0x00000030 }, { AR5K_BB_GAIN(37), 0x00000031 }, { AR5K_BB_GAIN(38), 0x00000032 }, { AR5K_BB_GAIN(39), 0x00000033 }, { AR5K_BB_GAIN(40), 0x00000034 }, { AR5K_BB_GAIN(41), 0x00000035 }, { AR5K_BB_GAIN(42), 0x00000035 }, { AR5K_BB_GAIN(43), 0x00000035 }, { AR5K_BB_GAIN(44), 0x00000035 }, { AR5K_BB_GAIN(45), 0x00000035 }, { AR5K_BB_GAIN(46), 0x00000035 }, { AR5K_BB_GAIN(47), 0x00000035 }, { AR5K_BB_GAIN(48), 0x00000035 }, { AR5K_BB_GAIN(49), 0x00000035 }, { AR5K_BB_GAIN(50), 0x00000035 }, { AR5K_BB_GAIN(51), 0x00000035 }, { AR5K_BB_GAIN(52), 0x00000035 }, { AR5K_BB_GAIN(53), 0x00000035 }, { AR5K_BB_GAIN(54), 0x00000035 }, { AR5K_BB_GAIN(55), 0x00000035 }, { AR5K_BB_GAIN(56), 0x00000035 }, { AR5K_BB_GAIN(57), 0x00000035 }, { AR5K_BB_GAIN(58), 0x00000035 }, { AR5K_BB_GAIN(59), 0x00000035 }, { AR5K_BB_GAIN(60), 0x00000035 }, { AR5K_BB_GAIN(61), 0x00000035 }, { AR5K_BB_GAIN(62), 0x00000010 }, { AR5K_BB_GAIN(63), 0x0000001a }, }; /** * ath5k_hw_ini_registers() - Write initial register dump common for all modes * @ah: The &struct ath5k_hw * @size: Dump size * @ini_regs: The array of &struct ath5k_ini * @skip_pcu: Skip PCU registers */ static void ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size, const struct ath5k_ini *ini_regs, bool skip_pcu) { unsigned int i; /* Write initial registers */ for (i = 0; i < size; i++) { /* Skip PCU registers if * requested */ if (skip_pcu && ini_regs[i].ini_register >= AR5K_PCU_MIN && ini_regs[i].ini_register <= AR5K_PCU_MAX) continue; switch (ini_regs[i].ini_mode) { case AR5K_INI_READ: /* Cleared on read */ ath5k_hw_reg_read(ah, ini_regs[i].ini_register); break; case AR5K_INI_WRITE: default: AR5K_REG_WAIT(i); ath5k_hw_reg_write(ah, ini_regs[i].ini_value, ini_regs[i].ini_register); } } } /** * ath5k_hw_ini_mode_registers() - Write initial mode-specific register dump * @ah: The &struct ath5k_hw * @size: Dump size * @ini_mode: The array of &struct ath5k_ini_mode * @mode: One of enum ath5k_driver_mode */ static void ath5k_hw_ini_mode_registers(struct ath5k_hw *ah, unsigned int size, const struct ath5k_ini_mode *ini_mode, u8 mode) { unsigned int i; for (i = 0; i < size; i++) { AR5K_REG_WAIT(i); ath5k_hw_reg_write(ah, ini_mode[i].mode_value[mode], (u32)ini_mode[i].mode_register); } } /** * ath5k_hw_write_initvals() - Write initial chip-specific register dump * @ah: The &struct ath5k_hw * @mode: One of enum ath5k_driver_mode * @skip_pcu: Skip PCU registers * * Write initial chip-specific register dump, to get the chipset on a * clean and ready-to-work state after warm reset. */ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu) { /* * Write initial register settings */ /* For AR5212 and compatible */ if (ah->ah_version == AR5K_AR5212) { /* First set of mode-specific settings */ ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5212_ini_mode_start), ar5212_ini_mode_start, mode); /* * Write initial settings common for all modes */ ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5212_ini_common_start), ar5212_ini_common_start, skip_pcu); /* Second set of mode-specific settings */ switch (ah->ah_radio) { case AR5K_RF5111: ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(rf5111_ini_mode_end), rf5111_ini_mode_end, mode); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5111_ini_common_end), rf5111_ini_common_end, skip_pcu); /* Baseband gain table */ ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5111_ini_bbgain), rf5111_ini_bbgain, skip_pcu); break; case AR5K_RF5112: ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(rf5112_ini_mode_end), rf5112_ini_mode_end, mode); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5112_ini_common_end), rf5112_ini_common_end, skip_pcu); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5112_ini_bbgain), rf5112_ini_bbgain, skip_pcu); break; case AR5K_RF5413: ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(rf5413_ini_mode_end), rf5413_ini_mode_end, mode); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5413_ini_common_end), rf5413_ini_common_end, skip_pcu); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5112_ini_bbgain), rf5112_ini_bbgain, skip_pcu); break; case AR5K_RF2316: case AR5K_RF2413: ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(rf2413_ini_mode_end), rf2413_ini_mode_end, mode); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf2413_ini_common_end), rf2413_ini_common_end, skip_pcu); /* Override settings from rf2413_ini_common_end */ if (ah->ah_radio == AR5K_RF2316) { ath5k_hw_reg_write(ah, 0x00004000, AR5K_PHY_AGC); ath5k_hw_reg_write(ah, 0x081b7caa, 0xa274); } ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5112_ini_bbgain), rf5112_ini_bbgain, skip_pcu); break; case AR5K_RF2317: ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(rf2413_ini_mode_end), rf2413_ini_mode_end, mode); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf2425_ini_common_end), rf2425_ini_common_end, skip_pcu); /* Override settings from rf2413_ini_mode_end */ ath5k_hw_reg_write(ah, 0x00180a65, AR5K_PHY_GAIN); /* Override settings from rf2413_ini_common_end */ ath5k_hw_reg_write(ah, 0x00004000, AR5K_PHY_AGC); AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TPC_RG5, AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP, 0xa); ath5k_hw_reg_write(ah, 0x800000a8, 0x8140); ath5k_hw_reg_write(ah, 0x000000ff, 0x9958); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5112_ini_bbgain), rf5112_ini_bbgain, skip_pcu); break; case AR5K_RF2425: ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(rf2425_ini_mode_end), rf2425_ini_mode_end, mode); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf2425_ini_common_end), rf2425_ini_common_end, skip_pcu); ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5112_ini_bbgain), rf5112_ini_bbgain, skip_pcu); break; default: return -EINVAL; } /* For AR5211 */ } else if (ah->ah_version == AR5K_AR5211) { /* AR5K_MODE_11B */ if (mode > 2) { ATH5K_ERR(ah, "unsupported channel mode: %d\n", mode); return -EINVAL; } /* Mode-specific settings */ ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5211_ini_mode), ar5211_ini_mode, mode); /* * Write initial settings common for all modes */ ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5211_ini), ar5211_ini, skip_pcu); /* AR5211 only comes with 5111 */ /* Baseband gain table */ ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5111_ini_bbgain), rf5111_ini_bbgain, skip_pcu); /* For AR5210 (for mode settings check out ath5k_hw_reset_tx_queue) */ } else if (ah->ah_version == AR5K_AR5210) { ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5210_ini), ar5210_ini, skip_pcu); } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/trace.h0000644000175000017500000000461712026211315023530 0ustar mcgrofmcgrof#if !defined(__TRACE_ATH5K_H) || defined(TRACE_HEADER_MULTI_READ) #define __TRACE_ATH5K_H #include #if !defined(CONFIG_ATH5K_TRACER) || defined(__CHECKER__) #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ static inline void trace_ ## name(proto) {} #endif struct sk_buff; struct ath5k_txq; struct ath5k_tx_status; #undef TRACE_SYSTEM #define TRACE_SYSTEM ath5k TRACE_EVENT(ath5k_rx, TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb), TP_ARGS(priv, skb), TP_STRUCT__entry( __field(struct ath5k_hw *, priv) __field(unsigned long, skbaddr) __dynamic_array(u8, frame, skb->len) ), TP_fast_assign( __entry->priv = priv; __entry->skbaddr = (unsigned long) skb; memcpy(__get_dynamic_array(frame), skb->data, skb->len); ), TP_printk( "[%p] RX skb=%lx", __entry->priv, __entry->skbaddr ) ); TRACE_EVENT(ath5k_tx, TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb, struct ath5k_txq *q), TP_ARGS(priv, skb, q), TP_STRUCT__entry( __field(struct ath5k_hw *, priv) __field(unsigned long, skbaddr) __field(u8, qnum) __dynamic_array(u8, frame, skb->len) ), TP_fast_assign( __entry->priv = priv; __entry->skbaddr = (unsigned long) skb; __entry->qnum = (u8) q->qnum; memcpy(__get_dynamic_array(frame), skb->data, skb->len); ), TP_printk( "[%p] TX skb=%lx q=%d", __entry->priv, __entry->skbaddr, __entry->qnum ) ); TRACE_EVENT(ath5k_tx_complete, TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb, struct ath5k_txq *q, struct ath5k_tx_status *ts), TP_ARGS(priv, skb, q, ts), TP_STRUCT__entry( __field(struct ath5k_hw *, priv) __field(unsigned long, skbaddr) __field(u8, qnum) __field(u8, ts_status) __field(s8, ts_rssi) __field(u8, ts_antenna) ), TP_fast_assign( __entry->priv = priv; __entry->skbaddr = (unsigned long) skb; __entry->qnum = (u8) q->qnum; __entry->ts_status = ts->ts_status; __entry->ts_rssi = ts->ts_rssi; __entry->ts_antenna = ts->ts_antenna; ), TP_printk( "[%p] TX end skb=%lx q=%d stat=%x rssi=%d ant=%x", __entry->priv, __entry->skbaddr, __entry->qnum, __entry->ts_status, __entry->ts_rssi, __entry->ts_antenna ) ); #endif /* __TRACE_ATH5K_H */ #if defined(CONFIG_ATH5K_TRACER) && !defined(__CHECKER__) #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH ../../drivers/net/wireless/ath/ath5k #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace #include #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/rfkill.c0000644000175000017500000000756312026211315023713 0ustar mcgrofmcgrof/* * RFKILL support for ath5k * * Copyright (c) 2009 Tobias Doerffel * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include "ath5k.h" static inline void ath5k_rfkill_disable(struct ath5k_hw *ah) { ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "rfkill disable (gpio:%d polarity:%d)\n", ah->rf_kill.gpio, ah->rf_kill.polarity); ath5k_hw_set_gpio_output(ah, ah->rf_kill.gpio); ath5k_hw_set_gpio(ah, ah->rf_kill.gpio, !ah->rf_kill.polarity); } static inline void ath5k_rfkill_enable(struct ath5k_hw *ah) { ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "rfkill enable (gpio:%d polarity:%d)\n", ah->rf_kill.gpio, ah->rf_kill.polarity); ath5k_hw_set_gpio_output(ah, ah->rf_kill.gpio); ath5k_hw_set_gpio(ah, ah->rf_kill.gpio, ah->rf_kill.polarity); } static inline void ath5k_rfkill_set_intr(struct ath5k_hw *ah, bool enable) { u32 curval; ath5k_hw_set_gpio_input(ah, ah->rf_kill.gpio); curval = ath5k_hw_get_gpio(ah, ah->rf_kill.gpio); ath5k_hw_set_gpio_intr(ah, ah->rf_kill.gpio, enable ? !!curval : !curval); } static bool ath5k_is_rfkill_set(struct ath5k_hw *ah) { /* configuring GPIO for input for some reason disables rfkill */ /*ath5k_hw_set_gpio_input(ah, ah->rf_kill.gpio);*/ return ath5k_hw_get_gpio(ah, ah->rf_kill.gpio) == ah->rf_kill.polarity; } static void ath5k_tasklet_rfkill_toggle(unsigned long data) { struct ath5k_hw *ah = (void *)data; bool blocked; blocked = ath5k_is_rfkill_set(ah); wiphy_rfkill_set_hw_state(ah->hw->wiphy, blocked); } void ath5k_rfkill_hw_start(struct ath5k_hw *ah) { /* read rfkill GPIO configuration from EEPROM header */ ah->rf_kill.gpio = ah->ah_capabilities.cap_eeprom.ee_rfkill_pin; ah->rf_kill.polarity = ah->ah_capabilities.cap_eeprom.ee_rfkill_pol; tasklet_init(&ah->rf_kill.toggleq, ath5k_tasklet_rfkill_toggle, (unsigned long)ah); ath5k_rfkill_disable(ah); /* enable interrupt for rfkill switch */ if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) ath5k_rfkill_set_intr(ah, true); } void ath5k_rfkill_hw_stop(struct ath5k_hw *ah) { /* disable interrupt for rfkill switch */ if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) ath5k_rfkill_set_intr(ah, false); tasklet_kill(&ah->rf_kill.toggleq); /* enable RFKILL when stopping HW so Wifi LED is turned off */ ath5k_rfkill_enable(ah); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/rfgain.h0000644000175000017500000005613312026211315023700 0ustar mcgrofmcgrof/* * RF Gain optimization * * Copyright (c) 2004-2009 Reyk Floeter * Copyright (c) 2006-2009 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /** * struct ath5k_ini_rfgain - RF Gain table * @rfg_register: RF Gain register address * @rfg_value: Register value for 5 and 2GHz * * Mode-specific RF Gain table (64bytes) for RF5111/5112 * (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial * RF Gain values are included in AR5K_AR5210_INI) */ struct ath5k_ini_rfgain { u16 rfg_register; u32 rfg_value[2]; /* [freq (see below)] */ }; /* Initial RF Gain settings for RF5111 */ static const struct ath5k_ini_rfgain rfgain_5111[] = { /* 5GHz 2GHz */ { AR5K_RF_GAIN(0), { 0x000001a9, 0x00000000 } }, { AR5K_RF_GAIN(1), { 0x000001e9, 0x00000040 } }, { AR5K_RF_GAIN(2), { 0x00000029, 0x00000080 } }, { AR5K_RF_GAIN(3), { 0x00000069, 0x00000150 } }, { AR5K_RF_GAIN(4), { 0x00000199, 0x00000190 } }, { AR5K_RF_GAIN(5), { 0x000001d9, 0x000001d0 } }, { AR5K_RF_GAIN(6), { 0x00000019, 0x00000010 } }, { AR5K_RF_GAIN(7), { 0x00000059, 0x00000044 } }, { AR5K_RF_GAIN(8), { 0x00000099, 0x00000084 } }, { AR5K_RF_GAIN(9), { 0x000001a5, 0x00000148 } }, { AR5K_RF_GAIN(10), { 0x000001e5, 0x00000188 } }, { AR5K_RF_GAIN(11), { 0x00000025, 0x000001c8 } }, { AR5K_RF_GAIN(12), { 0x000001c8, 0x00000014 } }, { AR5K_RF_GAIN(13), { 0x00000008, 0x00000042 } }, { AR5K_RF_GAIN(14), { 0x00000048, 0x00000082 } }, { AR5K_RF_GAIN(15), { 0x00000088, 0x00000178 } }, { AR5K_RF_GAIN(16), { 0x00000198, 0x000001b8 } }, { AR5K_RF_GAIN(17), { 0x000001d8, 0x000001f8 } }, { AR5K_RF_GAIN(18), { 0x00000018, 0x00000012 } }, { AR5K_RF_GAIN(19), { 0x00000058, 0x00000052 } }, { AR5K_RF_GAIN(20), { 0x00000098, 0x00000092 } }, { AR5K_RF_GAIN(21), { 0x000001a4, 0x0000017c } }, { AR5K_RF_GAIN(22), { 0x000001e4, 0x000001bc } }, { AR5K_RF_GAIN(23), { 0x00000024, 0x000001fc } }, { AR5K_RF_GAIN(24), { 0x00000064, 0x0000000a } }, { AR5K_RF_GAIN(25), { 0x000000a4, 0x0000004a } }, { AR5K_RF_GAIN(26), { 0x000000e4, 0x0000008a } }, { AR5K_RF_GAIN(27), { 0x0000010a, 0x0000015a } }, { AR5K_RF_GAIN(28), { 0x0000014a, 0x0000019a } }, { AR5K_RF_GAIN(29), { 0x0000018a, 0x000001da } }, { AR5K_RF_GAIN(30), { 0x000001ca, 0x0000000e } }, { AR5K_RF_GAIN(31), { 0x0000000a, 0x0000004e } }, { AR5K_RF_GAIN(32), { 0x0000004a, 0x0000008e } }, { AR5K_RF_GAIN(33), { 0x0000008a, 0x0000015e } }, { AR5K_RF_GAIN(34), { 0x000001ba, 0x0000019e } }, { AR5K_RF_GAIN(35), { 0x000001fa, 0x000001de } }, { AR5K_RF_GAIN(36), { 0x0000003a, 0x00000009 } }, { AR5K_RF_GAIN(37), { 0x0000007a, 0x00000049 } }, { AR5K_RF_GAIN(38), { 0x00000186, 0x00000089 } }, { AR5K_RF_GAIN(39), { 0x000001c6, 0x00000179 } }, { AR5K_RF_GAIN(40), { 0x00000006, 0x000001b9 } }, { AR5K_RF_GAIN(41), { 0x00000046, 0x000001f9 } }, { AR5K_RF_GAIN(42), { 0x00000086, 0x00000039 } }, { AR5K_RF_GAIN(43), { 0x000000c6, 0x00000079 } }, { AR5K_RF_GAIN(44), { 0x000000c6, 0x000000b9 } }, { AR5K_RF_GAIN(45), { 0x000000c6, 0x000001bd } }, { AR5K_RF_GAIN(46), { 0x000000c6, 0x000001fd } }, { AR5K_RF_GAIN(47), { 0x000000c6, 0x0000003d } }, { AR5K_RF_GAIN(48), { 0x000000c6, 0x0000007d } }, { AR5K_RF_GAIN(49), { 0x000000c6, 0x000000bd } }, { AR5K_RF_GAIN(50), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(51), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(52), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(53), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(54), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(55), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(56), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(57), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(58), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(59), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(60), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(61), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(62), { 0x000000c6, 0x000000fd } }, { AR5K_RF_GAIN(63), { 0x000000c6, 0x000000fd } }, }; /* Initial RF Gain settings for RF5112 */ static const struct ath5k_ini_rfgain rfgain_5112[] = { /* 5GHz 2GHz */ { AR5K_RF_GAIN(0), { 0x00000007, 0x00000007 } }, { AR5K_RF_GAIN(1), { 0x00000047, 0x00000047 } }, { AR5K_RF_GAIN(2), { 0x00000087, 0x00000087 } }, { AR5K_RF_GAIN(3), { 0x000001a0, 0x000001a0 } }, { AR5K_RF_GAIN(4), { 0x000001e0, 0x000001e0 } }, { AR5K_RF_GAIN(5), { 0x00000020, 0x00000020 } }, { AR5K_RF_GAIN(6), { 0x00000060, 0x00000060 } }, { AR5K_RF_GAIN(7), { 0x000001a1, 0x000001a1 } }, { AR5K_RF_GAIN(8), { 0x000001e1, 0x000001e1 } }, { AR5K_RF_GAIN(9), { 0x00000021, 0x00000021 } }, { AR5K_RF_GAIN(10), { 0x00000061, 0x00000061 } }, { AR5K_RF_GAIN(11), { 0x00000162, 0x00000162 } }, { AR5K_RF_GAIN(12), { 0x000001a2, 0x000001a2 } }, { AR5K_RF_GAIN(13), { 0x000001e2, 0x000001e2 } }, { AR5K_RF_GAIN(14), { 0x00000022, 0x00000022 } }, { AR5K_RF_GAIN(15), { 0x00000062, 0x00000062 } }, { AR5K_RF_GAIN(16), { 0x00000163, 0x00000163 } }, { AR5K_RF_GAIN(17), { 0x000001a3, 0x000001a3 } }, { AR5K_RF_GAIN(18), { 0x000001e3, 0x000001e3 } }, { AR5K_RF_GAIN(19), { 0x00000023, 0x00000023 } }, { AR5K_RF_GAIN(20), { 0x00000063, 0x00000063 } }, { AR5K_RF_GAIN(21), { 0x00000184, 0x00000184 } }, { AR5K_RF_GAIN(22), { 0x000001c4, 0x000001c4 } }, { AR5K_RF_GAIN(23), { 0x00000004, 0x00000004 } }, { AR5K_RF_GAIN(24), { 0x000001ea, 0x0000000b } }, { AR5K_RF_GAIN(25), { 0x0000002a, 0x0000004b } }, { AR5K_RF_GAIN(26), { 0x0000006a, 0x0000008b } }, { AR5K_RF_GAIN(27), { 0x000000aa, 0x000001ac } }, { AR5K_RF_GAIN(28), { 0x000001ab, 0x000001ec } }, { AR5K_RF_GAIN(29), { 0x000001eb, 0x0000002c } }, { AR5K_RF_GAIN(30), { 0x0000002b, 0x00000012 } }, { AR5K_RF_GAIN(31), { 0x0000006b, 0x00000052 } }, { AR5K_RF_GAIN(32), { 0x000000ab, 0x00000092 } }, { AR5K_RF_GAIN(33), { 0x000001ac, 0x00000193 } }, { AR5K_RF_GAIN(34), { 0x000001ec, 0x000001d3 } }, { AR5K_RF_GAIN(35), { 0x0000002c, 0x00000013 } }, { AR5K_RF_GAIN(36), { 0x0000003a, 0x00000053 } }, { AR5K_RF_GAIN(37), { 0x0000007a, 0x00000093 } }, { AR5K_RF_GAIN(38), { 0x000000ba, 0x00000194 } }, { AR5K_RF_GAIN(39), { 0x000001bb, 0x000001d4 } }, { AR5K_RF_GAIN(40), { 0x000001fb, 0x00000014 } }, { AR5K_RF_GAIN(41), { 0x0000003b, 0x0000003a } }, { AR5K_RF_GAIN(42), { 0x0000007b, 0x0000007a } }, { AR5K_RF_GAIN(43), { 0x000000bb, 0x000000ba } }, { AR5K_RF_GAIN(44), { 0x000001bc, 0x000001bb } }, { AR5K_RF_GAIN(45), { 0x000001fc, 0x000001fb } }, { AR5K_RF_GAIN(46), { 0x0000003c, 0x0000003b } }, { AR5K_RF_GAIN(47), { 0x0000007c, 0x0000007b } }, { AR5K_RF_GAIN(48), { 0x000000bc, 0x000000bb } }, { AR5K_RF_GAIN(49), { 0x000000fc, 0x000001bc } }, { AR5K_RF_GAIN(50), { 0x000000fc, 0x000001fc } }, { AR5K_RF_GAIN(51), { 0x000000fc, 0x0000003c } }, { AR5K_RF_GAIN(52), { 0x000000fc, 0x0000007c } }, { AR5K_RF_GAIN(53), { 0x000000fc, 0x000000bc } }, { AR5K_RF_GAIN(54), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(55), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(56), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(57), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(58), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(59), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(60), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(61), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(62), { 0x000000fc, 0x000000fc } }, { AR5K_RF_GAIN(63), { 0x000000fc, 0x000000fc } }, }; /* Initial RF Gain settings for RF2413 */ static const struct ath5k_ini_rfgain rfgain_2413[] = { { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, { AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } }, { AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } }, { AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } }, { AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } }, { AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } }, { AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } }, { AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } }, { AR5K_RF_GAIN(8), { 0x00000000, 0x00000168 } }, { AR5K_RF_GAIN(9), { 0x00000000, 0x000001a8 } }, { AR5K_RF_GAIN(10), { 0x00000000, 0x000001e8 } }, { AR5K_RF_GAIN(11), { 0x00000000, 0x00000028 } }, { AR5K_RF_GAIN(12), { 0x00000000, 0x00000068 } }, { AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } }, { AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } }, { AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } }, { AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } }, { AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } }, { AR5K_RF_GAIN(18), { 0x00000000, 0x00000190 } }, { AR5K_RF_GAIN(19), { 0x00000000, 0x000001d0 } }, { AR5K_RF_GAIN(20), { 0x00000000, 0x00000010 } }, { AR5K_RF_GAIN(21), { 0x00000000, 0x00000050 } }, { AR5K_RF_GAIN(22), { 0x00000000, 0x00000090 } }, { AR5K_RF_GAIN(23), { 0x00000000, 0x00000191 } }, { AR5K_RF_GAIN(24), { 0x00000000, 0x000001d1 } }, { AR5K_RF_GAIN(25), { 0x00000000, 0x00000011 } }, { AR5K_RF_GAIN(26), { 0x00000000, 0x00000051 } }, { AR5K_RF_GAIN(27), { 0x00000000, 0x00000091 } }, { AR5K_RF_GAIN(28), { 0x00000000, 0x00000178 } }, { AR5K_RF_GAIN(29), { 0x00000000, 0x000001b8 } }, { AR5K_RF_GAIN(30), { 0x00000000, 0x000001f8 } }, { AR5K_RF_GAIN(31), { 0x00000000, 0x00000038 } }, { AR5K_RF_GAIN(32), { 0x00000000, 0x00000078 } }, { AR5K_RF_GAIN(33), { 0x00000000, 0x00000199 } }, { AR5K_RF_GAIN(34), { 0x00000000, 0x000001d9 } }, { AR5K_RF_GAIN(35), { 0x00000000, 0x00000019 } }, { AR5K_RF_GAIN(36), { 0x00000000, 0x00000059 } }, { AR5K_RF_GAIN(37), { 0x00000000, 0x00000099 } }, { AR5K_RF_GAIN(38), { 0x00000000, 0x000000d9 } }, { AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } }, }; /* Initial RF Gain settings for AR2316 */ static const struct ath5k_ini_rfgain rfgain_2316[] = { { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, { AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } }, { AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } }, { AR5K_RF_GAIN(3), { 0x00000000, 0x000000c0 } }, { AR5K_RF_GAIN(4), { 0x00000000, 0x000000e0 } }, { AR5K_RF_GAIN(5), { 0x00000000, 0x000000e0 } }, { AR5K_RF_GAIN(6), { 0x00000000, 0x00000128 } }, { AR5K_RF_GAIN(7), { 0x00000000, 0x00000128 } }, { AR5K_RF_GAIN(8), { 0x00000000, 0x00000128 } }, { AR5K_RF_GAIN(9), { 0x00000000, 0x00000168 } }, { AR5K_RF_GAIN(10), { 0x00000000, 0x000001a8 } }, { AR5K_RF_GAIN(11), { 0x00000000, 0x000001e8 } }, { AR5K_RF_GAIN(12), { 0x00000000, 0x00000028 } }, { AR5K_RF_GAIN(13), { 0x00000000, 0x00000068 } }, { AR5K_RF_GAIN(14), { 0x00000000, 0x000000a8 } }, { AR5K_RF_GAIN(15), { 0x00000000, 0x000000e8 } }, { AR5K_RF_GAIN(16), { 0x00000000, 0x000000e8 } }, { AR5K_RF_GAIN(17), { 0x00000000, 0x00000130 } }, { AR5K_RF_GAIN(18), { 0x00000000, 0x00000130 } }, { AR5K_RF_GAIN(19), { 0x00000000, 0x00000170 } }, { AR5K_RF_GAIN(20), { 0x00000000, 0x000001b0 } }, { AR5K_RF_GAIN(21), { 0x00000000, 0x000001f0 } }, { AR5K_RF_GAIN(22), { 0x00000000, 0x00000030 } }, { AR5K_RF_GAIN(23), { 0x00000000, 0x00000070 } }, { AR5K_RF_GAIN(24), { 0x00000000, 0x000000b0 } }, { AR5K_RF_GAIN(25), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(26), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(27), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(28), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(29), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(30), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(31), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(32), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(33), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(34), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(35), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(36), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(37), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(38), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(39), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(40), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(41), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(42), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(43), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(44), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(45), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(46), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(47), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(48), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(49), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(50), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(51), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(52), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(53), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(54), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(55), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(56), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(57), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(58), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(59), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(60), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(61), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(62), { 0x00000000, 0x000000f0 } }, { AR5K_RF_GAIN(63), { 0x00000000, 0x000000f0 } }, }; /* Initial RF Gain settings for RF5413 */ static const struct ath5k_ini_rfgain rfgain_5413[] = { /* 5GHz 2GHz */ { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, { AR5K_RF_GAIN(1), { 0x00000040, 0x00000040 } }, { AR5K_RF_GAIN(2), { 0x00000080, 0x00000080 } }, { AR5K_RF_GAIN(3), { 0x000001a1, 0x00000161 } }, { AR5K_RF_GAIN(4), { 0x000001e1, 0x000001a1 } }, { AR5K_RF_GAIN(5), { 0x00000021, 0x000001e1 } }, { AR5K_RF_GAIN(6), { 0x00000061, 0x00000021 } }, { AR5K_RF_GAIN(7), { 0x00000188, 0x00000061 } }, { AR5K_RF_GAIN(8), { 0x000001c8, 0x00000188 } }, { AR5K_RF_GAIN(9), { 0x00000008, 0x000001c8 } }, { AR5K_RF_GAIN(10), { 0x00000048, 0x00000008 } }, { AR5K_RF_GAIN(11), { 0x00000088, 0x00000048 } }, { AR5K_RF_GAIN(12), { 0x000001a9, 0x00000088 } }, { AR5K_RF_GAIN(13), { 0x000001e9, 0x00000169 } }, { AR5K_RF_GAIN(14), { 0x00000029, 0x000001a9 } }, { AR5K_RF_GAIN(15), { 0x00000069, 0x000001e9 } }, { AR5K_RF_GAIN(16), { 0x000001d0, 0x00000029 } }, { AR5K_RF_GAIN(17), { 0x00000010, 0x00000069 } }, { AR5K_RF_GAIN(18), { 0x00000050, 0x00000190 } }, { AR5K_RF_GAIN(19), { 0x00000090, 0x000001d0 } }, { AR5K_RF_GAIN(20), { 0x000001b1, 0x00000010 } }, { AR5K_RF_GAIN(21), { 0x000001f1, 0x00000050 } }, { AR5K_RF_GAIN(22), { 0x00000031, 0x00000090 } }, { AR5K_RF_GAIN(23), { 0x00000071, 0x00000171 } }, { AR5K_RF_GAIN(24), { 0x000001b8, 0x000001b1 } }, { AR5K_RF_GAIN(25), { 0x000001f8, 0x000001f1 } }, { AR5K_RF_GAIN(26), { 0x00000038, 0x00000031 } }, { AR5K_RF_GAIN(27), { 0x00000078, 0x00000071 } }, { AR5K_RF_GAIN(28), { 0x00000199, 0x00000198 } }, { AR5K_RF_GAIN(29), { 0x000001d9, 0x000001d8 } }, { AR5K_RF_GAIN(30), { 0x00000019, 0x00000018 } }, { AR5K_RF_GAIN(31), { 0x00000059, 0x00000058 } }, { AR5K_RF_GAIN(32), { 0x00000099, 0x00000098 } }, { AR5K_RF_GAIN(33), { 0x000000d9, 0x00000179 } }, { AR5K_RF_GAIN(34), { 0x000000f9, 0x000001b9 } }, { AR5K_RF_GAIN(35), { 0x000000f9, 0x000001f9 } }, { AR5K_RF_GAIN(36), { 0x000000f9, 0x00000039 } }, { AR5K_RF_GAIN(37), { 0x000000f9, 0x00000079 } }, { AR5K_RF_GAIN(38), { 0x000000f9, 0x000000b9 } }, { AR5K_RF_GAIN(39), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(40), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(41), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(42), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(43), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(44), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(45), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(46), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(47), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(48), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(49), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(50), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(51), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(52), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(53), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(54), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(55), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(56), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(57), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(58), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(59), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(60), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(61), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(62), { 0x000000f9, 0x000000f9 } }, { AR5K_RF_GAIN(63), { 0x000000f9, 0x000000f9 } }, }; /* Initial RF Gain settings for RF2425 */ static const struct ath5k_ini_rfgain rfgain_2425[] = { { AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } }, { AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } }, { AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } }, { AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } }, { AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } }, { AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } }, { AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } }, { AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } }, { AR5K_RF_GAIN(8), { 0x00000000, 0x00000188 } }, { AR5K_RF_GAIN(9), { 0x00000000, 0x000001c8 } }, { AR5K_RF_GAIN(10), { 0x00000000, 0x00000008 } }, { AR5K_RF_GAIN(11), { 0x00000000, 0x00000048 } }, { AR5K_RF_GAIN(12), { 0x00000000, 0x00000088 } }, { AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } }, { AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } }, { AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } }, { AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } }, { AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } }, { AR5K_RF_GAIN(18), { 0x00000000, 0x000001b0 } }, { AR5K_RF_GAIN(19), { 0x00000000, 0x000001f0 } }, { AR5K_RF_GAIN(20), { 0x00000000, 0x00000030 } }, { AR5K_RF_GAIN(21), { 0x00000000, 0x00000070 } }, { AR5K_RF_GAIN(22), { 0x00000000, 0x00000171 } }, { AR5K_RF_GAIN(23), { 0x00000000, 0x000001b1 } }, { AR5K_RF_GAIN(24), { 0x00000000, 0x000001f1 } }, { AR5K_RF_GAIN(25), { 0x00000000, 0x00000031 } }, { AR5K_RF_GAIN(26), { 0x00000000, 0x00000071 } }, { AR5K_RF_GAIN(27), { 0x00000000, 0x000001b8 } }, { AR5K_RF_GAIN(28), { 0x00000000, 0x000001f8 } }, { AR5K_RF_GAIN(29), { 0x00000000, 0x00000038 } }, { AR5K_RF_GAIN(30), { 0x00000000, 0x00000078 } }, { AR5K_RF_GAIN(31), { 0x00000000, 0x000000b8 } }, { AR5K_RF_GAIN(32), { 0x00000000, 0x000001b9 } }, { AR5K_RF_GAIN(33), { 0x00000000, 0x000001f9 } }, { AR5K_RF_GAIN(34), { 0x00000000, 0x00000039 } }, { AR5K_RF_GAIN(35), { 0x00000000, 0x00000079 } }, { AR5K_RF_GAIN(36), { 0x00000000, 0x000000b9 } }, { AR5K_RF_GAIN(37), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(38), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } }, { AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } }, }; #define AR5K_GAIN_CRN_FIX_BITS_5111 4 #define AR5K_GAIN_CRN_FIX_BITS_5112 7 #define AR5K_GAIN_CRN_MAX_FIX_BITS AR5K_GAIN_CRN_FIX_BITS_5112 #define AR5K_GAIN_DYN_ADJUST_HI_MARGIN 15 #define AR5K_GAIN_DYN_ADJUST_LO_MARGIN 20 #define AR5K_GAIN_CCK_PROBE_CORR 5 #define AR5K_GAIN_CCK_OFDM_GAIN_DELTA 15 #define AR5K_GAIN_STEP_COUNT 10 /* Check if our current measurement is inside our * current variable attenuation window */ #define AR5K_GAIN_CHECK_ADJUST(_g) \ ((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high) /** * struct ath5k_gain_opt_step - An RF gain optimization step * @gos_param: Set of parameters * @gos_gain: Gain */ struct ath5k_gain_opt_step { s8 gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS]; s8 gos_gain; }; /** * struct ath5k_gain_opt - RF Gain optimization ladder * @go_default: The default step * @go_steps_count: How many optimization steps * @go_step: Array of &struct ath5k_gain_opt_step */ struct ath5k_gain_opt { u8 go_default; u8 go_steps_count; const struct ath5k_gain_opt_step go_step[AR5K_GAIN_STEP_COUNT]; }; /* * RF5111 * Parameters on gos_param: * 1) Tx clip PHY register * 2) PWD 90 RF register * 3) PWD 84 RF register * 4) RFGainSel RF register */ static const struct ath5k_gain_opt rfgain_opt_5111 = { 4, 9, { { { 4, 1, 1, 1 }, 6 }, { { 4, 0, 1, 1 }, 4 }, { { 3, 1, 1, 1 }, 3 }, { { 4, 0, 0, 1 }, 1 }, { { 4, 1, 1, 0 }, 0 }, { { 4, 0, 1, 0 }, -2 }, { { 3, 1, 1, 0 }, -3 }, { { 4, 0, 0, 0 }, -4 }, { { 2, 1, 1, 0 }, -6 } } }; /* * RF5112 * Parameters on gos_param: * 1) Mixgain ovr RF register * 2) PWD 138 RF register * 3) PWD 137 RF register * 4) PWD 136 RF register * 5) PWD 132 RF register * 6) PWD 131 RF register * 7) PWD 130 RF register */ static const struct ath5k_gain_opt rfgain_opt_5112 = { 1, 8, { { { 3, 0, 0, 0, 0, 0, 0 }, 6 }, { { 2, 0, 0, 0, 0, 0, 0 }, 0 }, { { 1, 0, 0, 0, 0, 0, 0 }, -3 }, { { 0, 0, 0, 0, 0, 0, 0 }, -6 }, { { 0, 1, 1, 0, 0, 0, 0 }, -8 }, { { 0, 1, 1, 0, 1, 1, 0 }, -10 }, { { 0, 1, 0, 1, 1, 1, 0 }, -13 }, { { 0, 1, 0, 1, 1, 0, 1 }, -16 }, } }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/rfbuffer.h0000644000175000017500000010600412026211315024224 0ustar mcgrofmcgrof/* * RF Buffer handling functions * * Copyright (c) 2009 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /** * DOC: RF Buffer registers * * There are some special registers on the RF chip * that control various operation settings related mostly to * the analog parts (channel, gain adjustment etc). * * We don't write on those registers directly but * we send a data packet on the chip, using a special register, * that holds all the settings we need. After we've sent the * data packet, we write on another special register to notify hw * to apply the settings. This is done so that control registers * can be dynamically programmed during operation and the settings * are applied faster on the hw. * * We call each data packet an "RF Bank" and all the data we write * (all RF Banks) "RF Buffer". This file holds initial RF Buffer * data for the different RF chips, and various info to match RF * Buffer offsets with specific RF registers so that we can access * them. We tweak these settings on rfregs_init function. * * Also check out reg.h and U.S. Patent 6677779 B1 (about buffer * registers and control registers): * * http://www.google.com/patents?id=qNURAAAAEBAJ */ /** * struct ath5k_ini_rfbuffer - Initial RF Buffer settings * @rfb_bank: RF Bank number * @rfb_ctrl_register: RF Buffer control register * @rfb_mode_data: RF Buffer data for each mode * * Struct to hold default mode specific RF * register values (RF Banks) for each chip. */ struct ath5k_ini_rfbuffer { u8 rfb_bank; u16 rfb_ctrl_register; u32 rfb_mode_data[3]; }; /** * struct ath5k_rfb_field - An RF Buffer field (register/value) * @len: Field length * @pos: Offset on the raw packet * @col: Used for shifting * * Struct to hold RF Buffer field * infos used to access certain RF * analog registers */ struct ath5k_rfb_field { u8 len; u16 pos; u8 col; }; /** * struct ath5k_rf_reg - RF analog register definition * @bank: RF Buffer Bank number * @index: Register's index on ath5k_rf_regx_idx * @field: The &struct ath5k_rfb_field * * We use this struct to define the set of RF registers * on each chip that we want to tweak. Some RF registers * are common between different chip versions so this saves * us space and complexity because we can refer to an rf * register by it's index no matter what chip we work with * as long as it has that register. */ struct ath5k_rf_reg { u8 bank; u8 index; struct ath5k_rfb_field field; }; /** * enum ath5k_rf_regs_idx - Map RF registers to indexes * * We do this to handle common bits and make our * life easier by using an index for each register * instead of a full rfb_field */ enum ath5k_rf_regs_idx { /* BANK 2 */ AR5K_RF_TURBO = 0, /* BANK 6 */ AR5K_RF_OB_2GHZ, AR5K_RF_OB_5GHZ, AR5K_RF_DB_2GHZ, AR5K_RF_DB_5GHZ, AR5K_RF_FIXED_BIAS_A, AR5K_RF_FIXED_BIAS_B, AR5K_RF_PWD_XPD, AR5K_RF_XPD_SEL, AR5K_RF_XPD_GAIN, AR5K_RF_PD_GAIN_LO, AR5K_RF_PD_GAIN_HI, AR5K_RF_HIGH_VC_CP, AR5K_RF_MID_VC_CP, AR5K_RF_LOW_VC_CP, AR5K_RF_PUSH_UP, AR5K_RF_PAD2GND, AR5K_RF_XB2_LVL, AR5K_RF_XB5_LVL, AR5K_RF_PWD_ICLOBUF_2G, AR5K_RF_PWD_84, AR5K_RF_PWD_90, AR5K_RF_PWD_130, AR5K_RF_PWD_131, AR5K_RF_PWD_132, AR5K_RF_PWD_136, AR5K_RF_PWD_137, AR5K_RF_PWD_138, AR5K_RF_PWD_166, AR5K_RF_PWD_167, AR5K_RF_DERBY_CHAN_SEL_MODE, /* BANK 7 */ AR5K_RF_GAIN_I, AR5K_RF_PLO_SEL, AR5K_RF_RFGAIN_SEL, AR5K_RF_RFGAIN_STEP, AR5K_RF_WAIT_S, AR5K_RF_WAIT_I, AR5K_RF_MAX_TIME, AR5K_RF_MIXVGA_OVR, AR5K_RF_MIXGAIN_OVR, AR5K_RF_MIXGAIN_STEP, AR5K_RF_PD_DELAY_A, AR5K_RF_PD_DELAY_B, AR5K_RF_PD_DELAY_XR, AR5K_RF_PD_PERIOD_A, AR5K_RF_PD_PERIOD_B, AR5K_RF_PD_PERIOD_XR, }; /*******************\ * RF5111 (Sombrero) * \*******************/ /* BANK 2 len pos col */ #define AR5K_RF5111_RF_TURBO { 1, 3, 0 } /* BANK 6 len pos col */ #define AR5K_RF5111_OB_2GHZ { 3, 119, 0 } #define AR5K_RF5111_DB_2GHZ { 3, 122, 0 } #define AR5K_RF5111_OB_5GHZ { 3, 104, 0 } #define AR5K_RF5111_DB_5GHZ { 3, 107, 0 } #define AR5K_RF5111_PWD_XPD { 1, 95, 0 } #define AR5K_RF5111_XPD_GAIN { 4, 96, 0 } /* Access to PWD registers */ #define AR5K_RF5111_PWD(_n) { 1, (135 - _n), 3 } /* BANK 7 len pos col */ #define AR5K_RF5111_GAIN_I { 6, 29, 0 } #define AR5K_RF5111_PLO_SEL { 1, 4, 0 } #define AR5K_RF5111_RFGAIN_SEL { 1, 36, 0 } #define AR5K_RF5111_RFGAIN_STEP { 6, 37, 0 } /* Only on AR5212 BaseBand and up */ #define AR5K_RF5111_WAIT_S { 5, 19, 0 } #define AR5K_RF5111_WAIT_I { 5, 24, 0 } #define AR5K_RF5111_MAX_TIME { 2, 49, 0 } static const struct ath5k_rf_reg rf_regs_5111[] = { {2, AR5K_RF_TURBO, AR5K_RF5111_RF_TURBO}, {6, AR5K_RF_OB_2GHZ, AR5K_RF5111_OB_2GHZ}, {6, AR5K_RF_DB_2GHZ, AR5K_RF5111_DB_2GHZ}, {6, AR5K_RF_OB_5GHZ, AR5K_RF5111_OB_5GHZ}, {6, AR5K_RF_DB_5GHZ, AR5K_RF5111_DB_5GHZ}, {6, AR5K_RF_PWD_XPD, AR5K_RF5111_PWD_XPD}, {6, AR5K_RF_XPD_GAIN, AR5K_RF5111_XPD_GAIN}, {6, AR5K_RF_PWD_84, AR5K_RF5111_PWD(84)}, {6, AR5K_RF_PWD_90, AR5K_RF5111_PWD(90)}, {7, AR5K_RF_GAIN_I, AR5K_RF5111_GAIN_I}, {7, AR5K_RF_PLO_SEL, AR5K_RF5111_PLO_SEL}, {7, AR5K_RF_RFGAIN_SEL, AR5K_RF5111_RFGAIN_SEL}, {7, AR5K_RF_RFGAIN_STEP, AR5K_RF5111_RFGAIN_STEP}, {7, AR5K_RF_WAIT_S, AR5K_RF5111_WAIT_S}, {7, AR5K_RF_WAIT_I, AR5K_RF5111_WAIT_I}, {7, AR5K_RF_MAX_TIME, AR5K_RF5111_MAX_TIME} }; /* Default mode specific settings */ static const struct ath5k_ini_rfbuffer rfb_5111[] = { /* BANK / C.R. A/XR B G */ { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00380000, 0x00380000, 0x00380000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 0, 0x989c, { 0x00000000, 0x000000c0, 0x00000080 } }, { 0, 0x989c, { 0x000400f9, 0x000400ff, 0x000400fd } }, { 0, 0x98d4, { 0x00000000, 0x00000004, 0x00000004 } }, { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d4, { 0x00000010, 0x00000010, 0x00000010 } }, { 3, 0x98d8, { 0x00601068, 0x00601068, 0x00601068 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } }, { 6, 0x989c, { 0x04000000, 0x04000000, 0x04000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x0a000000, 0x00000000 } }, { 6, 0x989c, { 0x003800c0, 0x023800c0, 0x003800c0 } }, { 6, 0x989c, { 0x00020006, 0x00000006, 0x00020006 } }, { 6, 0x989c, { 0x00000089, 0x00000089, 0x00000089 } }, { 6, 0x989c, { 0x000000a0, 0x000000a0, 0x000000a0 } }, { 6, 0x989c, { 0x00040007, 0x00040007, 0x00040007 } }, { 6, 0x98d4, { 0x0000001a, 0x0000001a, 0x0000001a } }, { 7, 0x989c, { 0x00000040, 0x00000040, 0x00000040 } }, { 7, 0x989c, { 0x00000010, 0x00000010, 0x00000010 } }, { 7, 0x989c, { 0x00000008, 0x00000008, 0x00000008 } }, { 7, 0x989c, { 0x0000004f, 0x0000004f, 0x0000004f } }, { 7, 0x989c, { 0x000000f1, 0x00000061, 0x000000f1 } }, { 7, 0x989c, { 0x0000904f, 0x0000904c, 0x0000904f } }, { 7, 0x989c, { 0x0000125a, 0x0000129a, 0x0000125a } }, { 7, 0x98cc, { 0x0000000e, 0x0000000f, 0x0000000e } }, }; /***********************\ * RF5112/RF2112 (Derby) * \***********************/ /* BANK 2 (Common) len pos col */ #define AR5K_RF5112X_RF_TURBO { 1, 1, 2 } /* BANK 7 (Common) len pos col */ #define AR5K_RF5112X_GAIN_I { 6, 14, 0 } #define AR5K_RF5112X_MIXVGA_OVR { 1, 36, 0 } #define AR5K_RF5112X_MIXGAIN_OVR { 2, 37, 0 } #define AR5K_RF5112X_MIXGAIN_STEP { 4, 32, 0 } #define AR5K_RF5112X_PD_DELAY_A { 4, 58, 0 } #define AR5K_RF5112X_PD_DELAY_B { 4, 62, 0 } #define AR5K_RF5112X_PD_DELAY_XR { 4, 66, 0 } #define AR5K_RF5112X_PD_PERIOD_A { 4, 70, 0 } #define AR5K_RF5112X_PD_PERIOD_B { 4, 74, 0 } #define AR5K_RF5112X_PD_PERIOD_XR { 4, 78, 0 } /* RFX112 (Derby 1) */ /* BANK 6 len pos col */ #define AR5K_RF5112_OB_2GHZ { 3, 269, 0 } #define AR5K_RF5112_DB_2GHZ { 3, 272, 0 } #define AR5K_RF5112_OB_5GHZ { 3, 261, 0 } #define AR5K_RF5112_DB_5GHZ { 3, 264, 0 } #define AR5K_RF5112_FIXED_BIAS_A { 1, 260, 0 } #define AR5K_RF5112_FIXED_BIAS_B { 1, 259, 0 } #define AR5K_RF5112_XPD_SEL { 1, 284, 0 } #define AR5K_RF5112_XPD_GAIN { 2, 252, 0 } /* Access to PWD registers */ #define AR5K_RF5112_PWD(_n) { 1, (302 - _n), 3 } static const struct ath5k_rf_reg rf_regs_5112[] = { {2, AR5K_RF_TURBO, AR5K_RF5112X_RF_TURBO}, {6, AR5K_RF_OB_2GHZ, AR5K_RF5112_OB_2GHZ}, {6, AR5K_RF_DB_2GHZ, AR5K_RF5112_DB_2GHZ}, {6, AR5K_RF_OB_5GHZ, AR5K_RF5112_OB_5GHZ}, {6, AR5K_RF_DB_5GHZ, AR5K_RF5112_DB_5GHZ}, {6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112_FIXED_BIAS_A}, {6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112_FIXED_BIAS_B}, {6, AR5K_RF_XPD_SEL, AR5K_RF5112_XPD_SEL}, {6, AR5K_RF_XPD_GAIN, AR5K_RF5112_XPD_GAIN}, {6, AR5K_RF_PWD_130, AR5K_RF5112_PWD(130)}, {6, AR5K_RF_PWD_131, AR5K_RF5112_PWD(131)}, {6, AR5K_RF_PWD_132, AR5K_RF5112_PWD(132)}, {6, AR5K_RF_PWD_136, AR5K_RF5112_PWD(136)}, {6, AR5K_RF_PWD_137, AR5K_RF5112_PWD(137)}, {6, AR5K_RF_PWD_138, AR5K_RF5112_PWD(138)}, {7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I}, {7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR}, {7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR}, {7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP}, {7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A}, {7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B}, {7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR}, {7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A}, {7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B}, {7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR}, }; /* Default mode specific settings */ static const struct ath5k_ini_rfbuffer rfb_5112[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x03060408, 0x03060408, 0x03060408 } }, { 3, 0x98dc, { 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0 } }, { 6, 0x989c, { 0x00a00000, 0x00a00000, 0x00a00000 } }, { 6, 0x989c, { 0x000a0000, 0x000a0000, 0x000a0000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00660000, 0x00660000, 0x00660000 } }, { 6, 0x989c, { 0x00db0000, 0x00db0000, 0x00db0000 } }, { 6, 0x989c, { 0x00f10000, 0x00f10000, 0x00f10000 } }, { 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } }, { 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } }, { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } }, { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, { 6, 0x989c, { 0x008b0000, 0x008b0000, 0x008b0000 } }, { 6, 0x989c, { 0x00600000, 0x00600000, 0x00600000 } }, { 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } }, { 6, 0x989c, { 0x00840000, 0x00840000, 0x00840000 } }, { 6, 0x989c, { 0x00640000, 0x00640000, 0x00640000 } }, { 6, 0x989c, { 0x00200000, 0x00200000, 0x00200000 } }, { 6, 0x989c, { 0x00240000, 0x00240000, 0x00240000 } }, { 6, 0x989c, { 0x00250000, 0x00250000, 0x00250000 } }, { 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } }, { 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } }, { 6, 0x989c, { 0x00510000, 0x00510000, 0x00510000 } }, { 6, 0x989c, { 0x1c040000, 0x1c040000, 0x1c040000 } }, { 6, 0x989c, { 0x000a0000, 0x000a0000, 0x000a0000 } }, { 6, 0x989c, { 0x00a10000, 0x00a10000, 0x00a10000 } }, { 6, 0x989c, { 0x00400000, 0x00400000, 0x00400000 } }, { 6, 0x989c, { 0x03090000, 0x03090000, 0x03090000 } }, { 6, 0x989c, { 0x06000000, 0x06000000, 0x06000000 } }, { 6, 0x989c, { 0x000000b0, 0x000000a8, 0x000000a8 } }, { 6, 0x989c, { 0x0000002e, 0x0000002e, 0x0000002e } }, { 6, 0x989c, { 0x006c4a41, 0x006c4af1, 0x006c4a61 } }, { 6, 0x989c, { 0x0050892a, 0x0050892b, 0x0050892b } }, { 6, 0x989c, { 0x00842400, 0x00842400, 0x00842400 } }, { 6, 0x989c, { 0x00c69200, 0x00c69200, 0x00c69200 } }, { 6, 0x98d0, { 0x0002000c, 0x0002000c, 0x0002000c } }, { 7, 0x989c, { 0x00000094, 0x00000094, 0x00000094 } }, { 7, 0x989c, { 0x00000091, 0x00000091, 0x00000091 } }, { 7, 0x989c, { 0x0000000a, 0x00000012, 0x00000012 } }, { 7, 0x989c, { 0x00000080, 0x00000080, 0x00000080 } }, { 7, 0x989c, { 0x000000c1, 0x000000c1, 0x000000c1 } }, { 7, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } }, { 7, 0x989c, { 0x000000f0, 0x000000f0, 0x000000f0 } }, { 7, 0x989c, { 0x00000022, 0x00000022, 0x00000022 } }, { 7, 0x989c, { 0x00000092, 0x00000092, 0x00000092 } }, { 7, 0x989c, { 0x000000d4, 0x000000d4, 0x000000d4 } }, { 7, 0x989c, { 0x000014cc, 0x000014cc, 0x000014cc } }, { 7, 0x989c, { 0x0000048c, 0x0000048c, 0x0000048c } }, { 7, 0x98c4, { 0x00000003, 0x00000003, 0x00000003 } }, }; /* RFX112A (Derby 2) */ /* BANK 6 len pos col */ #define AR5K_RF5112A_OB_2GHZ { 3, 287, 0 } #define AR5K_RF5112A_DB_2GHZ { 3, 290, 0 } #define AR5K_RF5112A_OB_5GHZ { 3, 279, 0 } #define AR5K_RF5112A_DB_5GHZ { 3, 282, 0 } #define AR5K_RF5112A_FIXED_BIAS_A { 1, 278, 0 } #define AR5K_RF5112A_FIXED_BIAS_B { 1, 277, 0 } #define AR5K_RF5112A_XPD_SEL { 1, 302, 0 } #define AR5K_RF5112A_PDGAINLO { 2, 270, 0 } #define AR5K_RF5112A_PDGAINHI { 2, 257, 0 } /* Access to PWD registers */ #define AR5K_RF5112A_PWD(_n) { 1, (306 - _n), 3 } /* Voltage regulators */ #define AR5K_RF5112A_HIGH_VC_CP { 2, 90, 2 } #define AR5K_RF5112A_MID_VC_CP { 2, 92, 2 } #define AR5K_RF5112A_LOW_VC_CP { 2, 94, 2 } #define AR5K_RF5112A_PUSH_UP { 1, 254, 2 } /* Power consumption */ #define AR5K_RF5112A_PAD2GND { 1, 281, 1 } #define AR5K_RF5112A_XB2_LVL { 2, 1, 3 } #define AR5K_RF5112A_XB5_LVL { 2, 3, 3 } static const struct ath5k_rf_reg rf_regs_5112a[] = { {2, AR5K_RF_TURBO, AR5K_RF5112X_RF_TURBO}, {6, AR5K_RF_OB_2GHZ, AR5K_RF5112A_OB_2GHZ}, {6, AR5K_RF_DB_2GHZ, AR5K_RF5112A_DB_2GHZ}, {6, AR5K_RF_OB_5GHZ, AR5K_RF5112A_OB_5GHZ}, {6, AR5K_RF_DB_5GHZ, AR5K_RF5112A_DB_5GHZ}, {6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112A_FIXED_BIAS_A}, {6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112A_FIXED_BIAS_B}, {6, AR5K_RF_XPD_SEL, AR5K_RF5112A_XPD_SEL}, {6, AR5K_RF_PD_GAIN_LO, AR5K_RF5112A_PDGAINLO}, {6, AR5K_RF_PD_GAIN_HI, AR5K_RF5112A_PDGAINHI}, {6, AR5K_RF_PWD_130, AR5K_RF5112A_PWD(130)}, {6, AR5K_RF_PWD_131, AR5K_RF5112A_PWD(131)}, {6, AR5K_RF_PWD_132, AR5K_RF5112A_PWD(132)}, {6, AR5K_RF_PWD_136, AR5K_RF5112A_PWD(136)}, {6, AR5K_RF_PWD_137, AR5K_RF5112A_PWD(137)}, {6, AR5K_RF_PWD_138, AR5K_RF5112A_PWD(138)}, {6, AR5K_RF_PWD_166, AR5K_RF5112A_PWD(166)}, {6, AR5K_RF_PWD_167, AR5K_RF5112A_PWD(167)}, {6, AR5K_RF_HIGH_VC_CP, AR5K_RF5112A_HIGH_VC_CP}, {6, AR5K_RF_MID_VC_CP, AR5K_RF5112A_MID_VC_CP}, {6, AR5K_RF_LOW_VC_CP, AR5K_RF5112A_LOW_VC_CP}, {6, AR5K_RF_PUSH_UP, AR5K_RF5112A_PUSH_UP}, {6, AR5K_RF_PAD2GND, AR5K_RF5112A_PAD2GND}, {6, AR5K_RF_XB2_LVL, AR5K_RF5112A_XB2_LVL}, {6, AR5K_RF_XB5_LVL, AR5K_RF5112A_XB5_LVL}, {7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I}, {7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR}, {7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR}, {7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP}, {7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A}, {7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B}, {7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR}, {7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A}, {7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B}, {7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR}, }; /* Default mode specific settings */ static const struct ath5k_ini_rfbuffer rfb_5112a[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x03060408, 0x03060408, 0x03060408 } }, { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } }, { 6, 0x989c, { 0x0f000000, 0x0f000000, 0x0f000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00800000, 0x00800000, 0x00800000 } }, { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } }, { 6, 0x989c, { 0x00010000, 0x00010000, 0x00010000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00180000, 0x00180000, 0x00180000 } }, { 6, 0x989c, { 0x00600000, 0x006e0000, 0x006e0000 } }, { 6, 0x989c, { 0x00c70000, 0x00c70000, 0x00c70000 } }, { 6, 0x989c, { 0x004b0000, 0x004b0000, 0x004b0000 } }, { 6, 0x989c, { 0x04480000, 0x04480000, 0x04480000 } }, { 6, 0x989c, { 0x004c0000, 0x004c0000, 0x004c0000 } }, { 6, 0x989c, { 0x00e40000, 0x00e40000, 0x00e40000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00fc0000, 0x00fc0000, 0x00fc0000 } }, { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, { 6, 0x989c, { 0x043f0000, 0x043f0000, 0x043f0000 } }, { 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } }, { 6, 0x989c, { 0x02190000, 0x02190000, 0x02190000 } }, { 6, 0x989c, { 0x00240000, 0x00240000, 0x00240000 } }, { 6, 0x989c, { 0x00b40000, 0x00b40000, 0x00b40000 } }, { 6, 0x989c, { 0x00990000, 0x00990000, 0x00990000 } }, { 6, 0x989c, { 0x00500000, 0x00500000, 0x00500000 } }, { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } }, { 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } }, { 6, 0x989c, { 0xc0320000, 0xc0320000, 0xc0320000 } }, { 6, 0x989c, { 0x01740000, 0x01740000, 0x01740000 } }, { 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } }, { 6, 0x989c, { 0x86280000, 0x86280000, 0x86280000 } }, { 6, 0x989c, { 0x31840000, 0x31840000, 0x31840000 } }, { 6, 0x989c, { 0x00f20080, 0x00f20080, 0x00f20080 } }, { 6, 0x989c, { 0x00270019, 0x00270019, 0x00270019 } }, { 6, 0x989c, { 0x00000003, 0x00000003, 0x00000003 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x000000b2, 0x000000b2, 0x000000b2 } }, { 6, 0x989c, { 0x00b02084, 0x00b02084, 0x00b02084 } }, { 6, 0x989c, { 0x004125a4, 0x004125a4, 0x004125a4 } }, { 6, 0x989c, { 0x00119220, 0x00119220, 0x00119220 } }, { 6, 0x989c, { 0x001a4800, 0x001a4800, 0x001a4800 } }, { 6, 0x98d8, { 0x000b0230, 0x000b0230, 0x000b0230 } }, { 7, 0x989c, { 0x00000094, 0x00000094, 0x00000094 } }, { 7, 0x989c, { 0x00000091, 0x00000091, 0x00000091 } }, { 7, 0x989c, { 0x00000012, 0x00000012, 0x00000012 } }, { 7, 0x989c, { 0x00000080, 0x00000080, 0x00000080 } }, { 7, 0x989c, { 0x000000d9, 0x000000d9, 0x000000d9 } }, { 7, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } }, { 7, 0x989c, { 0x000000f0, 0x000000f0, 0x000000f0 } }, { 7, 0x989c, { 0x000000a2, 0x000000a2, 0x000000a2 } }, { 7, 0x989c, { 0x00000052, 0x00000052, 0x00000052 } }, { 7, 0x989c, { 0x000000d4, 0x000000d4, 0x000000d4 } }, { 7, 0x989c, { 0x000014cc, 0x000014cc, 0x000014cc } }, { 7, 0x989c, { 0x0000048c, 0x0000048c, 0x0000048c } }, { 7, 0x98c4, { 0x00000003, 0x00000003, 0x00000003 } }, }; /******************\ * RF2413 (Griffin) * \******************/ /* BANK 2 len pos col */ #define AR5K_RF2413_RF_TURBO { 1, 1, 2 } /* BANK 6 len pos col */ #define AR5K_RF2413_OB_2GHZ { 3, 168, 0 } #define AR5K_RF2413_DB_2GHZ { 3, 165, 0 } static const struct ath5k_rf_reg rf_regs_2413[] = { {2, AR5K_RF_TURBO, AR5K_RF2413_RF_TURBO}, {6, AR5K_RF_OB_2GHZ, AR5K_RF2413_OB_2GHZ}, {6, AR5K_RF_DB_2GHZ, AR5K_RF2413_DB_2GHZ}, }; /* Default mode specific settings * XXX: a/aTurbo ??? */ static const struct ath5k_ini_rfbuffer rfb_2413[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } }, { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } }, { 6, 0x989c, { 0xf0000000, 0xf0000000, 0xf0000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x03000000, 0x03000000, 0x03000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x40400000, 0x40400000, 0x40400000 } }, { 6, 0x989c, { 0x65050000, 0x65050000, 0x65050000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00420000, 0x00420000, 0x00420000 } }, { 6, 0x989c, { 0x00b50000, 0x00b50000, 0x00b50000 } }, { 6, 0x989c, { 0x00030000, 0x00030000, 0x00030000 } }, { 6, 0x989c, { 0x00f70000, 0x00f70000, 0x00f70000 } }, { 6, 0x989c, { 0x009d0000, 0x009d0000, 0x009d0000 } }, { 6, 0x989c, { 0x00220000, 0x00220000, 0x00220000 } }, { 6, 0x989c, { 0x04220000, 0x04220000, 0x04220000 } }, { 6, 0x989c, { 0x00230018, 0x00230018, 0x00230018 } }, { 6, 0x989c, { 0x00280000, 0x00280060, 0x00280060 } }, { 6, 0x989c, { 0x005000c0, 0x005000c3, 0x005000c3 } }, { 6, 0x989c, { 0x0004007f, 0x0004007f, 0x0004007f } }, { 6, 0x989c, { 0x00000458, 0x00000458, 0x00000458 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x0000c000, 0x0000c000, 0x0000c000 } }, { 6, 0x98d8, { 0x00400230, 0x00400230, 0x00400230 } }, { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } }, { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } }, { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } }, }; /***************************\ * RF2315/RF2316 (Cobra SoC) * \***************************/ /* BANK 2 len pos col */ #define AR5K_RF2316_RF_TURBO { 1, 1, 2 } /* BANK 6 len pos col */ #define AR5K_RF2316_OB_2GHZ { 3, 178, 0 } #define AR5K_RF2316_DB_2GHZ { 3, 175, 0 } static const struct ath5k_rf_reg rf_regs_2316[] = { {2, AR5K_RF_TURBO, AR5K_RF2316_RF_TURBO}, {6, AR5K_RF_OB_2GHZ, AR5K_RF2316_OB_2GHZ}, {6, AR5K_RF_DB_2GHZ, AR5K_RF2316_DB_2GHZ}, }; /* Default mode specific settings */ static const struct ath5k_ini_rfbuffer rfb_2316[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } }, { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0xc0000000, 0xc0000000, 0xc0000000 } }, { 6, 0x989c, { 0x0f000000, 0x0f000000, 0x0f000000 } }, { 6, 0x989c, { 0x02000000, 0x02000000, 0x02000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0xf8000000, 0xf8000000, 0xf8000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x95150000, 0x95150000, 0x95150000 } }, { 6, 0x989c, { 0xc1000000, 0xc1000000, 0xc1000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00080000, 0x00080000, 0x00080000 } }, { 6, 0x989c, { 0x00d50000, 0x00d50000, 0x00d50000 } }, { 6, 0x989c, { 0x000e0000, 0x000e0000, 0x000e0000 } }, { 6, 0x989c, { 0x00dc0000, 0x00dc0000, 0x00dc0000 } }, { 6, 0x989c, { 0x00770000, 0x00770000, 0x00770000 } }, { 6, 0x989c, { 0x008a0000, 0x008a0000, 0x008a0000 } }, { 6, 0x989c, { 0x10880000, 0x10880000, 0x10880000 } }, { 6, 0x989c, { 0x008c0060, 0x008c0060, 0x008c0060 } }, { 6, 0x989c, { 0x00a00000, 0x00a00080, 0x00a00080 } }, { 6, 0x989c, { 0x00400000, 0x0040000d, 0x0040000d } }, { 6, 0x989c, { 0x00110400, 0x00110400, 0x00110400 } }, { 6, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } }, { 6, 0x989c, { 0x00000001, 0x00000001, 0x00000001 } }, { 6, 0x989c, { 0x00000b00, 0x00000b00, 0x00000b00 } }, { 6, 0x989c, { 0x00000be8, 0x00000be8, 0x00000be8 } }, { 6, 0x98c0, { 0x00010000, 0x00010000, 0x00010000 } }, { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } }, { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } }, { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } }, }; /******************************\ * RF5413/RF5424 (Eagle/Condor) * \******************************/ /* BANK 6 len pos col */ #define AR5K_RF5413_OB_2GHZ { 3, 241, 0 } #define AR5K_RF5413_DB_2GHZ { 3, 238, 0 } #define AR5K_RF5413_OB_5GHZ { 3, 247, 0 } #define AR5K_RF5413_DB_5GHZ { 3, 244, 0 } #define AR5K_RF5413_PWD_ICLOBUF2G { 3, 131, 3 } #define AR5K_RF5413_DERBY_CHAN_SEL_MODE { 1, 291, 2 } static const struct ath5k_rf_reg rf_regs_5413[] = { {6, AR5K_RF_OB_2GHZ, AR5K_RF5413_OB_2GHZ}, {6, AR5K_RF_DB_2GHZ, AR5K_RF5413_DB_2GHZ}, {6, AR5K_RF_OB_5GHZ, AR5K_RF5413_OB_5GHZ}, {6, AR5K_RF_DB_5GHZ, AR5K_RF5413_DB_5GHZ}, {6, AR5K_RF_PWD_ICLOBUF_2G, AR5K_RF5413_PWD_ICLOBUF2G}, {6, AR5K_RF_DERBY_CHAN_SEL_MODE, AR5K_RF5413_DERBY_CHAN_SEL_MODE}, }; /* Default mode specific settings */ static const struct ath5k_ini_rfbuffer rfb_5413[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x00000008, 0x00000008, 0x00000008 } }, { 3, 0x98dc, { 0x00a000c0, 0x00e000c0, 0x00e000c0 } }, { 6, 0x989c, { 0x33000000, 0x33000000, 0x33000000 } }, { 6, 0x989c, { 0x01000000, 0x01000000, 0x01000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x1f000000, 0x1f000000, 0x1f000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00b80000, 0x00b80000, 0x00b80000 } }, { 6, 0x989c, { 0x00b70000, 0x00b70000, 0x00b70000 } }, { 6, 0x989c, { 0x00840000, 0x00840000, 0x00840000 } }, { 6, 0x989c, { 0x00980000, 0x00980000, 0x00980000 } }, { 6, 0x989c, { 0x00c00000, 0x00c00000, 0x00c00000 } }, { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, { 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, { 6, 0x989c, { 0x00d70000, 0x00d70000, 0x00d70000 } }, { 6, 0x989c, { 0x00610000, 0x00610000, 0x00610000 } }, { 6, 0x989c, { 0x00fe0000, 0x00fe0000, 0x00fe0000 } }, { 6, 0x989c, { 0x00de0000, 0x00de0000, 0x00de0000 } }, { 6, 0x989c, { 0x007f0000, 0x007f0000, 0x007f0000 } }, { 6, 0x989c, { 0x043d0000, 0x043d0000, 0x043d0000 } }, { 6, 0x989c, { 0x00770000, 0x00770000, 0x00770000 } }, { 6, 0x989c, { 0x00440000, 0x00440000, 0x00440000 } }, { 6, 0x989c, { 0x00980000, 0x00980000, 0x00980000 } }, { 6, 0x989c, { 0x00100080, 0x00100080, 0x00100080 } }, { 6, 0x989c, { 0x0005c034, 0x0005c034, 0x0005c034 } }, { 6, 0x989c, { 0x003100f0, 0x003100f0, 0x003100f0 } }, { 6, 0x989c, { 0x000c011f, 0x000c011f, 0x000c011f } }, { 6, 0x989c, { 0x00510040, 0x00510040, 0x00510040 } }, { 6, 0x989c, { 0x005000da, 0x005000da, 0x005000da } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00004044, 0x00004044, 0x00004044 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x000060c0, 0x000060c0, 0x000060c0 } }, { 6, 0x989c, { 0x00002c00, 0x00003600, 0x00003600 } }, { 6, 0x98c8, { 0x00000403, 0x00040403, 0x00040403 } }, { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } }, { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } }, { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } }, }; /***************************\ * RF2425/RF2417 (Swan/Nala) * * AR2317 (Spider SoC) * \***************************/ /* BANK 2 len pos col */ #define AR5K_RF2425_RF_TURBO { 1, 1, 2 } /* BANK 6 len pos col */ #define AR5K_RF2425_OB_2GHZ { 3, 193, 0 } #define AR5K_RF2425_DB_2GHZ { 3, 190, 0 } static const struct ath5k_rf_reg rf_regs_2425[] = { {2, AR5K_RF_TURBO, AR5K_RF2425_RF_TURBO}, {6, AR5K_RF_OB_2GHZ, AR5K_RF2425_OB_2GHZ}, {6, AR5K_RF_DB_2GHZ, AR5K_RF2425_DB_2GHZ}, }; /* Default mode specific settings */ static const struct ath5k_ini_rfbuffer rfb_2425[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } }, { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } }, { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } }, { 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } }, { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } }, { 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } }, { 6, 0x989c, { 0x00e70000, 0x00e70000, 0x00e70000 } }, { 6, 0x989c, { 0x00140000, 0x00140000, 0x00140000 } }, { 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } }, { 6, 0x989c, { 0x0007001a, 0x0007001a, 0x0007001a } }, { 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } }, { 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } }, { 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } }, { 6, 0x989c, { 0x00001688, 0x00001688, 0x00001688 } }, { 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } }, { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } }, { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } }, { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } }, }; /* * TODO: Handle the few differences with swan during * bank modification and get rid of this */ static const struct ath5k_ini_rfbuffer rfb_2317[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } }, { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } }, { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } }, { 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } }, { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } }, { 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } }, { 6, 0x989c, { 0x00e70000, 0x00e70000, 0x00e70000 } }, { 6, 0x989c, { 0x00140100, 0x00140100, 0x00140100 } }, { 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } }, { 6, 0x989c, { 0x0007001a, 0x0007001a, 0x0007001a } }, { 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } }, { 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } }, { 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } }, { 6, 0x989c, { 0x00009688, 0x00009688, 0x00009688 } }, { 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } }, { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } }, { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } }, { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } }, }; /* * TODO: Handle the few differences with swan during * bank modification and get rid of this */ static const struct ath5k_ini_rfbuffer rfb_2417[] = { /* BANK / C.R. A/XR B G */ { 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } }, { 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } }, { 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } }, { 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } }, { 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } }, { 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } }, { 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } }, { 6, 0x989c, { 0x00e70000, 0x80e70000, 0x80e70000 } }, { 6, 0x989c, { 0x00140000, 0x00140000, 0x00140000 } }, { 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } }, { 6, 0x989c, { 0x0007001a, 0x0207001a, 0x0207001a } }, { 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } }, { 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } }, { 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } }, { 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } }, { 6, 0x989c, { 0x00001688, 0x00001688, 0x00001688 } }, { 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } }, { 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } }, { 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } }, { 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } }, }; compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/reg.h0000644000175000017500000027643712026211315023222 0ustar mcgrofmcgrof/* * Copyright (c) 2006-2008 Nick Kossifidis * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2007-2008 Michael Taylor * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * Register values for Atheros 5210/5211/5212 cards from OpenBSD's ar5k * maintained by Reyk Floeter * * I tried to document those registers by looking at ar5k code, some * 802.11 (802.11e mostly) papers and by reading various public available * Atheros presentations and papers like these: * * 5210 - http://nova.stanford.edu/~bbaas/ps/isscc2002_slides.pdf * * 5211 - http://www.hotchips.org/archives/hc14/3_Tue/16_mcfarland.pdf * * This file also contains register values found on a memory dump of * Atheros's ART program (Atheros Radio Test), on ath9k, on legacy-hal * released by Atheros and on various debug messages found on the net. */ #include "../reg.h" /*====MAC DMA REGISTERS====*/ /* * AR5210-Specific TXDP registers * 5210 has only 2 transmit queues so no DCU/QCU, just * 2 transmit descriptor pointers... */ #define AR5K_NOQCU_TXDP0 0x0000 /* Queue 0 - data */ #define AR5K_NOQCU_TXDP1 0x0004 /* Queue 1 - beacons */ /* * Mac Control Register */ #define AR5K_CR 0x0008 /* Register Address */ #define AR5K_CR_TXE0 0x00000001 /* TX Enable for queue 0 on 5210 */ #define AR5K_CR_TXE1 0x00000002 /* TX Enable for queue 1 on 5210 */ #define AR5K_CR_RXE 0x00000004 /* RX Enable */ #define AR5K_CR_TXD0 0x00000008 /* TX Disable for queue 0 on 5210 */ #define AR5K_CR_TXD1 0x00000010 /* TX Disable for queue 1 on 5210 */ #define AR5K_CR_RXD 0x00000020 /* RX Disable */ #define AR5K_CR_SWI 0x00000040 /* Software Interrupt */ /* * RX Descriptor Pointer register */ #define AR5K_RXDP 0x000c /* * Configuration and status register */ #define AR5K_CFG 0x0014 /* Register Address */ #define AR5K_CFG_SWTD 0x00000001 /* Byte-swap TX descriptor (for big endian archs) */ #define AR5K_CFG_SWTB 0x00000002 /* Byte-swap TX buffer */ #define AR5K_CFG_SWRD 0x00000004 /* Byte-swap RX descriptor */ #define AR5K_CFG_SWRB 0x00000008 /* Byte-swap RX buffer */ #define AR5K_CFG_SWRG 0x00000010 /* Byte-swap Register access */ #define AR5K_CFG_IBSS 0x00000020 /* 0-BSS, 1-IBSS [5211+] */ #define AR5K_CFG_PHY_OK 0x00000100 /* [5211+] */ #define AR5K_CFG_EEBS 0x00000200 /* EEPROM is busy */ #define AR5K_CFG_CLKGD 0x00000400 /* Clock gated (Disable dynamic clock) */ #define AR5K_CFG_TXCNT 0x00007800 /* Tx frame count (?) [5210] */ #define AR5K_CFG_TXCNT_S 11 #define AR5K_CFG_TXFSTAT 0x00008000 /* Tx frame status (?) [5210] */ #define AR5K_CFG_TXFSTRT 0x00010000 /* [5210] */ #define AR5K_CFG_PCI_THRES 0x00060000 /* PCI Master req q threshold [5211+] */ #define AR5K_CFG_PCI_THRES_S 17 /* * Interrupt enable register */ #define AR5K_IER 0x0024 /* Register Address */ #define AR5K_IER_DISABLE 0x00000000 /* Disable card interrupts */ #define AR5K_IER_ENABLE 0x00000001 /* Enable card interrupts */ /* * 0x0028 is Beacon Control Register on 5210 * and first RTS duration register on 5211 */ /* * Beacon control register [5210] */ #define AR5K_BCR 0x0028 /* Register Address */ #define AR5K_BCR_AP 0x00000000 /* AP mode */ #define AR5K_BCR_ADHOC 0x00000001 /* Ad-Hoc mode */ #define AR5K_BCR_BDMAE 0x00000002 /* DMA enable */ #define AR5K_BCR_TQ1FV 0x00000004 /* Use Queue1 for CAB traffic */ #define AR5K_BCR_TQ1V 0x00000008 /* Use Queue1 for Beacon traffic */ #define AR5K_BCR_BCGET 0x00000010 /* * First RTS duration register [5211] */ #define AR5K_RTSD0 0x0028 /* Register Address */ #define AR5K_RTSD0_6 0x000000ff /* 6Mb RTS duration mask (?) */ #define AR5K_RTSD0_6_S 0 /* 6Mb RTS duration shift (?) */ #define AR5K_RTSD0_9 0x0000ff00 /* 9Mb*/ #define AR5K_RTSD0_9_S 8 #define AR5K_RTSD0_12 0x00ff0000 /* 12Mb*/ #define AR5K_RTSD0_12_S 16 #define AR5K_RTSD0_18 0xff000000 /* 16Mb*/ #define AR5K_RTSD0_18_S 24 /* * 0x002c is Beacon Status Register on 5210 * and second RTS duration register on 5211 */ /* * Beacon status register [5210] * * As i can see in ar5k_ar5210_tx_start Reyk uses some of the values of BCR * for this register, so i guess TQ1V,TQ1FV and BDMAE have the same meaning * here and SNP/SNAP means "snapshot" (so this register gets synced with BCR). * So SNAPPEDBCRVALID should also stand for "snapped BCR -values- valid", so i * renamed it to SNAPSHOTSVALID to make more sense. I really have no idea what * else can it be. I also renamed SNPBCMD to SNPADHOC to match BCR. */ #define AR5K_BSR 0x002c /* Register Address */ #define AR5K_BSR_BDLYSW 0x00000001 /* SW Beacon delay (?) */ #define AR5K_BSR_BDLYDMA 0x00000002 /* DMA Beacon delay (?) */ #define AR5K_BSR_TXQ1F 0x00000004 /* Beacon queue (1) finished */ #define AR5K_BSR_ATIMDLY 0x00000008 /* ATIM delay (?) */ #define AR5K_BSR_SNPADHOC 0x00000100 /* Ad-hoc mode set (?) */ #define AR5K_BSR_SNPBDMAE 0x00000200 /* Beacon DMA enabled (?) */ #define AR5K_BSR_SNPTQ1FV 0x00000400 /* Queue1 is used for CAB traffic (?) */ #define AR5K_BSR_SNPTQ1V 0x00000800 /* Queue1 is used for Beacon traffic (?) */ #define AR5K_BSR_SNAPSHOTSVALID 0x00001000 /* BCR snapshots are valid (?) */ #define AR5K_BSR_SWBA_CNT 0x00ff0000 /* * Second RTS duration register [5211] */ #define AR5K_RTSD1 0x002c /* Register Address */ #define AR5K_RTSD1_24 0x000000ff /* 24Mb */ #define AR5K_RTSD1_24_S 0 #define AR5K_RTSD1_36 0x0000ff00 /* 36Mb */ #define AR5K_RTSD1_36_S 8 #define AR5K_RTSD1_48 0x00ff0000 /* 48Mb */ #define AR5K_RTSD1_48_S 16 #define AR5K_RTSD1_54 0xff000000 /* 54Mb */ #define AR5K_RTSD1_54_S 24 /* * Transmit configuration register */ #define AR5K_TXCFG 0x0030 /* Register Address */ #define AR5K_TXCFG_SDMAMR 0x00000007 /* DMA size (read) */ #define AR5K_TXCFG_SDMAMR_S 0 #define AR5K_TXCFG_B_MODE 0x00000008 /* Set b mode for 5111 (enable 2111) */ #define AR5K_TXCFG_TXFSTP 0x00000008 /* TX DMA full Stop [5210] */ #define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Trigger level mask */ #define AR5K_TXCFG_TXFULL_S 4 #define AR5K_TXCFG_TXFULL_0B 0x00000000 #define AR5K_TXCFG_TXFULL_64B 0x00000010 #define AR5K_TXCFG_TXFULL_128B 0x00000020 #define AR5K_TXCFG_TXFULL_192B 0x00000030 #define AR5K_TXCFG_TXFULL_256B 0x00000040 #define AR5K_TXCFG_TXCONT_EN 0x00000080 #define AR5K_TXCFG_DMASIZE 0x00000100 /* Flag for passing DMA size [5210] */ #define AR5K_TXCFG_JUMBO_DESC_EN 0x00000400 /* Enable jumbo tx descriptors [5211+] */ #define AR5K_TXCFG_ADHOC_BCN_ATIM 0x00000800 /* Adhoc Beacon ATIM Policy */ #define AR5K_TXCFG_ATIM_WINDOW_DEF_DIS 0x00001000 /* Disable ATIM window defer [5211+] */ #define AR5K_TXCFG_RTSRND 0x00001000 /* [5211+] */ #define AR5K_TXCFG_FRMPAD_DIS 0x00002000 /* [5211+] */ #define AR5K_TXCFG_RDY_CBR_DIS 0x00004000 /* Ready time CBR disable [5211+] */ #define AR5K_TXCFG_JUMBO_FRM_MODE 0x00008000 /* Jumbo frame mode [5211+] */ #define AR5K_TXCFG_DCU_DBL_BUF_DIS 0x00008000 /* Disable double buffering on DCU */ #define AR5K_TXCFG_DCU_CACHING_DIS 0x00010000 /* Disable DCU caching */ /* * Receive configuration register */ #define AR5K_RXCFG 0x0034 /* Register Address */ #define AR5K_RXCFG_SDMAMW 0x00000007 /* DMA size (write) */ #define AR5K_RXCFG_SDMAMW_S 0 #define AR5K_RXCFG_ZLFDMA 0x00000008 /* Enable Zero-length frame DMA */ #define AR5K_RXCFG_DEF_ANTENNA 0x00000010 /* Default antenna (?) */ #define AR5K_RXCFG_JUMBO_RXE 0x00000020 /* Enable jumbo rx descriptors [5211+] */ #define AR5K_RXCFG_JUMBO_WRAP 0x00000040 /* Wrap jumbo frames [5211+] */ #define AR5K_RXCFG_SLE_ENTRY 0x00000080 /* Sleep entry policy */ /* * Receive jumbo descriptor last address register * Only found in 5211 (?) */ #define AR5K_RXJLA 0x0038 /* * MIB control register */ #define AR5K_MIBC 0x0040 /* Register Address */ #define AR5K_MIBC_COW 0x00000001 /* Counter Overflow Warning */ #define AR5K_MIBC_FMC 0x00000002 /* Freeze MIB Counters */ #define AR5K_MIBC_CMC 0x00000004 /* Clear MIB Counters */ #define AR5K_MIBC_MCS 0x00000008 /* MIB counter strobe, increment all */ /* * Timeout prescale register */ #define AR5K_TOPS 0x0044 #define AR5K_TOPS_M 0x0000ffff /* * Receive timeout register (no frame received) */ #define AR5K_RXNOFRM 0x0048 #define AR5K_RXNOFRM_M 0x000003ff /* * Transmit timeout register (no frame sent) */ #define AR5K_TXNOFRM 0x004c #define AR5K_TXNOFRM_M 0x000003ff #define AR5K_TXNOFRM_QCU 0x000ffc00 #define AR5K_TXNOFRM_QCU_S 10 /* * Receive frame gap timeout register */ #define AR5K_RPGTO 0x0050 #define AR5K_RPGTO_M 0x000003ff /* * Receive frame count limit register */ #define AR5K_RFCNT 0x0054 #define AR5K_RFCNT_M 0x0000001f /* [5211+] (?) */ #define AR5K_RFCNT_RFCL 0x0000000f /* [5210] */ /* * Misc settings register * (reserved0-3) */ #define AR5K_MISC 0x0058 /* Register Address */ #define AR5K_MISC_DMA_OBS_M 0x000001e0 #define AR5K_MISC_DMA_OBS_S 5 #define AR5K_MISC_MISC_OBS_M 0x00000e00 #define AR5K_MISC_MISC_OBS_S 9 #define AR5K_MISC_MAC_OBS_LSB_M 0x00007000 #define AR5K_MISC_MAC_OBS_LSB_S 12 #define AR5K_MISC_MAC_OBS_MSB_M 0x00038000 #define AR5K_MISC_MAC_OBS_MSB_S 15 #define AR5K_MISC_LED_DECAY 0x001c0000 /* [5210] */ #define AR5K_MISC_LED_BLINK 0x00e00000 /* [5210] */ /* * QCU/DCU clock gating register (5311) * (reserved4-5) */ #define AR5K_QCUDCU_CLKGT 0x005c /* Register Address (?) */ #define AR5K_QCUDCU_CLKGT_QCU 0x0000ffff /* Mask for QCU clock */ #define AR5K_QCUDCU_CLKGT_DCU 0x07ff0000 /* Mask for DCU clock */ /* * Interrupt Status Registers * * For 5210 there is only one status register but for * 5211/5212 we have one primary and 4 secondary registers. * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212. * Most of these bits are common for all chipsets. * * NOTE: On 5211+ TXOK, TXDESC, TXERR, TXEOL and TXURN contain * the logical OR from per-queue interrupt bits found on SISR registers * (see below). */ #define AR5K_ISR 0x001c /* Register Address [5210] */ #define AR5K_PISR 0x0080 /* Register Address [5211+] */ #define AR5K_ISR_RXOK 0x00000001 /* Frame successfully received */ #define AR5K_ISR_RXDESC 0x00000002 /* RX descriptor request */ #define AR5K_ISR_RXERR 0x00000004 /* Receive error */ #define AR5K_ISR_RXNOFRM 0x00000008 /* No frame received (receive timeout) */ #define AR5K_ISR_RXEOL 0x00000010 /* Empty RX descriptor */ #define AR5K_ISR_RXORN 0x00000020 /* Receive FIFO overrun */ #define AR5K_ISR_TXOK 0x00000040 /* Frame successfully transmitted */ #define AR5K_ISR_TXDESC 0x00000080 /* TX descriptor request */ #define AR5K_ISR_TXERR 0x00000100 /* Transmit error */ #define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout) * NOTE: We don't have per-queue info for this * one, but we can enable it per-queue through * TXNOFRM_QCU field on TXNOFRM register */ #define AR5K_ISR_TXEOL 0x00000400 /* Empty TX descriptor */ #define AR5K_ISR_TXURN 0x00000800 /* Transmit FIFO underrun */ #define AR5K_ISR_MIB 0x00001000 /* Update MIB counters */ #define AR5K_ISR_SWI 0x00002000 /* Software interrupt */ #define AR5K_ISR_RXPHY 0x00004000 /* PHY error */ #define AR5K_ISR_RXKCM 0x00008000 /* RX Key cache miss */ #define AR5K_ISR_SWBA 0x00010000 /* Software beacon alert */ #define AR5K_ISR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */ #define AR5K_ISR_BMISS 0x00040000 /* Beacon missed */ #define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] * 'or' of MCABT, SSERR, DPERR from SISR2 */ #define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */ #define AR5K_ISR_MCABT 0x00100000 /* Master Cycle Abort [5210] */ #define AR5K_ISR_RXCHIRP 0x00200000 /* CHIRP Received [5212+] */ #define AR5K_ISR_SSERR 0x00200000 /* Signaled System Error [5210] */ #define AR5K_ISR_DPERR 0x00400000 /* Bus parity error [5210] */ #define AR5K_ISR_RXDOPPLER 0x00400000 /* Doppler chirp received [5212+] */ #define AR5K_ISR_TIM 0x00800000 /* [5211+] */ #define AR5K_ISR_BCNMISC 0x00800000 /* Misc beacon related interrupt * 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT, * CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */ #define AR5K_ISR_GPIO 0x01000000 /* GPIO (rf kill) */ #define AR5K_ISR_QCBRORN 0x02000000 /* QCU CBR overrun [5211+] */ #define AR5K_ISR_QCBRURN 0x04000000 /* QCU CBR underrun [5211+] */ #define AR5K_ISR_QTRIG 0x08000000 /* QCU scheduling trigger [5211+] */ #define AR5K_ISR_BITS_FROM_SISRS (AR5K_ISR_TXOK | AR5K_ISR_TXDESC |\ AR5K_ISR_TXERR | AR5K_ISR_TXEOL |\ AR5K_ISR_TXURN | AR5K_ISR_HIUERR |\ AR5K_ISR_BCNMISC | AR5K_ISR_QCBRORN |\ AR5K_ISR_QCBRURN | AR5K_ISR_QTRIG) /* * Secondary status registers [5211+] (0 - 4) * * These give the status for each QCU, only QCUs 0-9 are * represented. */ #define AR5K_SISR0 0x0084 /* Register Address [5211+] */ #define AR5K_SISR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */ #define AR5K_SISR0_QCU_TXOK_S 0 #define AR5K_SISR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */ #define AR5K_SISR0_QCU_TXDESC_S 16 #define AR5K_SISR1 0x0088 /* Register Address [5211+] */ #define AR5K_SISR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */ #define AR5K_SISR1_QCU_TXERR_S 0 #define AR5K_SISR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */ #define AR5K_SISR1_QCU_TXEOL_S 16 #define AR5K_SISR2 0x008c /* Register Address [5211+] */ #define AR5K_SISR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ #define AR5K_SISR2_QCU_TXURN_S 0 #define AR5K_SISR2_MCABT 0x00010000 /* Master Cycle Abort */ #define AR5K_SISR2_SSERR 0x00020000 /* Signaled System Error */ #define AR5K_SISR2_DPERR 0x00040000 /* Bus parity error */ #define AR5K_SISR2_TIM 0x01000000 /* [5212+] */ #define AR5K_SISR2_CAB_END 0x02000000 /* [5212+] */ #define AR5K_SISR2_DTIM_SYNC 0x04000000 /* DTIM sync lost [5212+] */ #define AR5K_SISR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */ #define AR5K_SISR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */ #define AR5K_SISR2_DTIM 0x20000000 /* [5212+] */ #define AR5K_SISR2_TSFOOR 0x80000000 /* TSF Out of range */ #define AR5K_SISR3 0x0090 /* Register Address [5211+] */ #define AR5K_SISR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ #define AR5K_SISR3_QCBRORN_S 0 #define AR5K_SISR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */ #define AR5K_SISR3_QCBRURN_S 16 #define AR5K_SISR4 0x0094 /* Register Address [5211+] */ #define AR5K_SISR4_QTRIG 0x000003ff /* Mask for QTRIG */ #define AR5K_SISR4_QTRIG_S 0 /* * Shadow read-and-clear interrupt status registers [5211+] */ #define AR5K_RAC_PISR 0x00c0 /* Read and clear PISR */ #define AR5K_RAC_SISR0 0x00c4 /* Read and clear SISR0 */ #define AR5K_RAC_SISR1 0x00c8 /* Read and clear SISR1 */ #define AR5K_RAC_SISR2 0x00cc /* Read and clear SISR2 */ #define AR5K_RAC_SISR3 0x00d0 /* Read and clear SISR3 */ #define AR5K_RAC_SISR4 0x00d4 /* Read and clear SISR4 */ /* * Interrupt Mask Registers * * As with ISRs 5210 has one IMR (AR5K_IMR) and 5211/5212 has one primary * (AR5K_PIMR) and 4 secondary IMRs (AR5K_SIMRx). Note that ISR/IMR flags match. */ #define AR5K_IMR 0x0020 /* Register Address [5210] */ #define AR5K_PIMR 0x00a0 /* Register Address [5211+] */ #define AR5K_IMR_RXOK 0x00000001 /* Frame successfully received*/ #define AR5K_IMR_RXDESC 0x00000002 /* RX descriptor request*/ #define AR5K_IMR_RXERR 0x00000004 /* Receive error*/ #define AR5K_IMR_RXNOFRM 0x00000008 /* No frame received (receive timeout)*/ #define AR5K_IMR_RXEOL 0x00000010 /* Empty RX descriptor*/ #define AR5K_IMR_RXORN 0x00000020 /* Receive FIFO overrun*/ #define AR5K_IMR_TXOK 0x00000040 /* Frame successfully transmitted*/ #define AR5K_IMR_TXDESC 0x00000080 /* TX descriptor request*/ #define AR5K_IMR_TXERR 0x00000100 /* Transmit error*/ #define AR5K_IMR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout)*/ #define AR5K_IMR_TXEOL 0x00000400 /* Empty TX descriptor*/ #define AR5K_IMR_TXURN 0x00000800 /* Transmit FIFO underrun*/ #define AR5K_IMR_MIB 0x00001000 /* Update MIB counters*/ #define AR5K_IMR_SWI 0x00002000 /* Software interrupt */ #define AR5K_IMR_RXPHY 0x00004000 /* PHY error*/ #define AR5K_IMR_RXKCM 0x00008000 /* RX Key cache miss */ #define AR5K_IMR_SWBA 0x00010000 /* Software beacon alert*/ #define AR5K_IMR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */ #define AR5K_IMR_BMISS 0x00040000 /* Beacon missed*/ #define AR5K_IMR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ #define AR5K_IMR_BNR 0x00100000 /* Beacon not ready [5211+] */ #define AR5K_IMR_MCABT 0x00100000 /* Master Cycle Abort [5210] */ #define AR5K_IMR_RXCHIRP 0x00200000 /* CHIRP Received [5212+]*/ #define AR5K_IMR_SSERR 0x00200000 /* Signaled System Error [5210] */ #define AR5K_IMR_DPERR 0x00400000 /* Det par Error (?) [5210] */ #define AR5K_IMR_RXDOPPLER 0x00400000 /* Doppler chirp received [5212+] */ #define AR5K_IMR_TIM 0x00800000 /* [5211+] */ #define AR5K_IMR_BCNMISC 0x00800000 /* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT, CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */ #define AR5K_IMR_GPIO 0x01000000 /* GPIO (rf kill)*/ #define AR5K_IMR_QCBRORN 0x02000000 /* QCU CBR overrun (?) [5211+] */ #define AR5K_IMR_QCBRURN 0x04000000 /* QCU CBR underrun (?) [5211+] */ #define AR5K_IMR_QTRIG 0x08000000 /* QCU scheduling trigger [5211+] */ /* * Secondary interrupt mask registers [5211+] (0 - 4) */ #define AR5K_SIMR0 0x00a4 /* Register Address [5211+] */ #define AR5K_SIMR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */ #define AR5K_SIMR0_QCU_TXOK_S 0 #define AR5K_SIMR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */ #define AR5K_SIMR0_QCU_TXDESC_S 16 #define AR5K_SIMR1 0x00a8 /* Register Address [5211+] */ #define AR5K_SIMR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */ #define AR5K_SIMR1_QCU_TXERR_S 0 #define AR5K_SIMR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */ #define AR5K_SIMR1_QCU_TXEOL_S 16 #define AR5K_SIMR2 0x00ac /* Register Address [5211+] */ #define AR5K_SIMR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ #define AR5K_SIMR2_QCU_TXURN_S 0 #define AR5K_SIMR2_MCABT 0x00010000 /* Master Cycle Abort */ #define AR5K_SIMR2_SSERR 0x00020000 /* Signaled System Error */ #define AR5K_SIMR2_DPERR 0x00040000 /* Bus parity error */ #define AR5K_SIMR2_TIM 0x01000000 /* [5212+] */ #define AR5K_SIMR2_CAB_END 0x02000000 /* [5212+] */ #define AR5K_SIMR2_DTIM_SYNC 0x04000000 /* DTIM Sync lost [5212+] */ #define AR5K_SIMR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */ #define AR5K_SIMR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */ #define AR5K_SIMR2_DTIM 0x20000000 /* [5212+] */ #define AR5K_SIMR2_TSFOOR 0x80000000 /* TSF OOR (?) */ #define AR5K_SIMR3 0x00b0 /* Register Address [5211+] */ #define AR5K_SIMR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ #define AR5K_SIMR3_QCBRORN_S 0 #define AR5K_SIMR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */ #define AR5K_SIMR3_QCBRURN_S 16 #define AR5K_SIMR4 0x00b4 /* Register Address [5211+] */ #define AR5K_SIMR4_QTRIG 0x000003ff /* Mask for QTRIG */ #define AR5K_SIMR4_QTRIG_S 0 /* * DMA Debug registers 0-7 * 0xe0 - 0xfc */ /* * Decompression mask registers [5212+] */ #define AR5K_DCM_ADDR 0x0400 /*Decompression mask address (index) */ #define AR5K_DCM_DATA 0x0404 /*Decompression mask data */ /* * Wake On Wireless pattern control register [5212+] */ #define AR5K_WOW_PCFG 0x0410 /* Register Address */ #define AR5K_WOW_PCFG_PAT_MATCH_EN 0x00000001 /* Pattern match enable */ #define AR5K_WOW_PCFG_LONG_FRAME_POL 0x00000002 /* Long frame policy */ #define AR5K_WOW_PCFG_WOBMISS 0x00000004 /* Wake on bea(con) miss (?) */ #define AR5K_WOW_PCFG_PAT_0_EN 0x00000100 /* Enable pattern 0 */ #define AR5K_WOW_PCFG_PAT_1_EN 0x00000200 /* Enable pattern 1 */ #define AR5K_WOW_PCFG_PAT_2_EN 0x00000400 /* Enable pattern 2 */ #define AR5K_WOW_PCFG_PAT_3_EN 0x00000800 /* Enable pattern 3 */ #define AR5K_WOW_PCFG_PAT_4_EN 0x00001000 /* Enable pattern 4 */ #define AR5K_WOW_PCFG_PAT_5_EN 0x00002000 /* Enable pattern 5 */ /* * Wake On Wireless pattern index register (?) [5212+] */ #define AR5K_WOW_PAT_IDX 0x0414 /* * Wake On Wireless pattern data register [5212+] */ #define AR5K_WOW_PAT_DATA 0x0418 /* Register Address */ #define AR5K_WOW_PAT_DATA_0_3_V 0x00000001 /* Pattern 0, 3 value */ #define AR5K_WOW_PAT_DATA_1_4_V 0x00000100 /* Pattern 1, 4 value */ #define AR5K_WOW_PAT_DATA_2_5_V 0x00010000 /* Pattern 2, 5 value */ #define AR5K_WOW_PAT_DATA_0_3_M 0x01000000 /* Pattern 0, 3 mask */ #define AR5K_WOW_PAT_DATA_1_4_M 0x04000000 /* Pattern 1, 4 mask */ #define AR5K_WOW_PAT_DATA_2_5_M 0x10000000 /* Pattern 2, 5 mask */ /* * Decompression configuration registers [5212+] */ #define AR5K_DCCFG 0x0420 /* Register Address */ #define AR5K_DCCFG_GLOBAL_EN 0x00000001 /* Enable decompression on all queues */ #define AR5K_DCCFG_BYPASS_EN 0x00000002 /* Bypass decompression */ #define AR5K_DCCFG_BCAST_EN 0x00000004 /* Enable decompression for bcast frames */ #define AR5K_DCCFG_MCAST_EN 0x00000008 /* Enable decompression for mcast frames */ /* * Compression configuration registers [5212+] */ #define AR5K_CCFG 0x0600 /* Register Address */ #define AR5K_CCFG_WINDOW_SIZE 0x00000007 /* Compression window size */ #define AR5K_CCFG_CPC_EN 0x00000008 /* Enable performance counters */ #define AR5K_CCFG_CCU 0x0604 /* Register Address */ #define AR5K_CCFG_CCU_CUP_EN 0x00000001 /* CCU Catchup enable */ #define AR5K_CCFG_CCU_CREDIT 0x00000002 /* CCU Credit (field) */ #define AR5K_CCFG_CCU_CD_THRES 0x00000080 /* CCU Cyc(lic?) debt threshold (field) */ #define AR5K_CCFG_CCU_CUP_LCNT 0x00010000 /* CCU Catchup lit(?) count */ #define AR5K_CCFG_CCU_INIT 0x00100200 /* Initial value during reset */ /* * Compression performance counter registers [5212+] */ #define AR5K_CPC0 0x0610 /* Compression performance counter 0 */ #define AR5K_CPC1 0x0614 /* Compression performance counter 1*/ #define AR5K_CPC2 0x0618 /* Compression performance counter 2 */ #define AR5K_CPC3 0x061c /* Compression performance counter 3 */ #define AR5K_CPCOVF 0x0620 /* Compression performance overflow */ /* * Queue control unit (QCU) registers [5211+] * * Card has 12 TX Queues but i see that only 0-9 are used (?) * both in binary HAL (see ah.h) and ar5k. Each queue has it's own * TXDP at addresses 0x0800 - 0x082c, a CBR (Constant Bit Rate) * configuration register (0x08c0 - 0x08ec), a ready time configuration * register (0x0900 - 0x092c), a misc configuration register (0x09c0 - * 0x09ec) and a status register (0x0a00 - 0x0a2c). We also have some * global registers, QCU transmit enable/disable and "one shot arm (?)" * set/clear, which contain status for all queues (we shift by 1 for each * queue). To access these registers easily we define some macros here * that are used inside HAL. For more infos check out *_tx_queue functs. */ /* * Generic QCU Register access macros */ #define AR5K_QUEUE_REG(_r, _q) (((_q) << 2) + _r) #define AR5K_QCU_GLOBAL_READ(_r, _q) (AR5K_REG_READ(_r) & (1 << _q)) #define AR5K_QCU_GLOBAL_WRITE(_r, _q) AR5K_REG_WRITE(_r, (1 << _q)) /* * QCU Transmit descriptor pointer registers */ #define AR5K_QCU_TXDP_BASE 0x0800 /* Register Address - Queue0 TXDP */ #define AR5K_QUEUE_TXDP(_q) AR5K_QUEUE_REG(AR5K_QCU_TXDP_BASE, _q) /* * QCU Transmit enable register */ #define AR5K_QCU_TXE 0x0840 #define AR5K_ENABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXE, _q) #define AR5K_QUEUE_ENABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXE, _q) /* * QCU Transmit disable register */ #define AR5K_QCU_TXD 0x0880 #define AR5K_DISABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXD, _q) #define AR5K_QUEUE_DISABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXD, _q) /* * QCU Constant Bit Rate configuration registers */ #define AR5K_QCU_CBRCFG_BASE 0x08c0 /* Register Address - Queue0 CBRCFG */ #define AR5K_QCU_CBRCFG_INTVAL 0x00ffffff /* CBR Interval mask */ #define AR5K_QCU_CBRCFG_INTVAL_S 0 #define AR5K_QCU_CBRCFG_ORN_THRES 0xff000000 /* CBR overrun threshold mask */ #define AR5K_QCU_CBRCFG_ORN_THRES_S 24 #define AR5K_QUEUE_CBRCFG(_q) AR5K_QUEUE_REG(AR5K_QCU_CBRCFG_BASE, _q) /* * QCU Ready time configuration registers */ #define AR5K_QCU_RDYTIMECFG_BASE 0x0900 /* Register Address - Queue0 RDYTIMECFG */ #define AR5K_QCU_RDYTIMECFG_INTVAL 0x00ffffff /* Ready time interval mask */ #define AR5K_QCU_RDYTIMECFG_INTVAL_S 0 #define AR5K_QCU_RDYTIMECFG_ENABLE 0x01000000 /* Ready time enable mask */ #define AR5K_QUEUE_RDYTIMECFG(_q) AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q) /* * QCU one shot arm set registers */ #define AR5K_QCU_ONESHOTARM_SET 0x0940 /* Register Address -QCU "one shot arm set (?)" */ #define AR5K_QCU_ONESHOTARM_SET_M 0x0000ffff /* * QCU one shot arm clear registers */ #define AR5K_QCU_ONESHOTARM_CLEAR 0x0980 /* Register Address -QCU "one shot arm clear (?)" */ #define AR5K_QCU_ONESHOTARM_CLEAR_M 0x0000ffff /* * QCU misc registers */ #define AR5K_QCU_MISC_BASE 0x09c0 /* Register Address -Queue0 MISC */ #define AR5K_QCU_MISC_FRSHED_M 0x0000000f /* Frame scheduling mask */ #define AR5K_QCU_MISC_FRSHED_ASAP 0 /* ASAP */ #define AR5K_QCU_MISC_FRSHED_CBR 1 /* Constant Bit Rate */ #define AR5K_QCU_MISC_FRSHED_DBA_GT 2 /* DMA Beacon alert gated */ #define AR5K_QCU_MISC_FRSHED_TIM_GT 3 /* TIMT gated */ #define AR5K_QCU_MISC_FRSHED_BCN_SENT_GT 4 /* Beacon sent gated */ #define AR5K_QCU_MISC_ONESHOT_ENABLE 0x00000010 /* Oneshot enable */ #define AR5K_QCU_MISC_CBREXP_DIS 0x00000020 /* Disable CBR expired counter (normal queue) */ #define AR5K_QCU_MISC_CBREXP_BCN_DIS 0x00000040 /* Disable CBR expired counter (beacon queue) */ #define AR5K_QCU_MISC_BCN_ENABLE 0x00000080 /* Enable Beacon use */ #define AR5K_QCU_MISC_CBR_THRES_ENABLE 0x00000100 /* CBR expired threshold enabled */ #define AR5K_QCU_MISC_RDY_VEOL_POLICY 0x00000200 /* TXE reset when RDYTIME expired or VEOL */ #define AR5K_QCU_MISC_CBR_RESET_CNT 0x00000400 /* CBR threshold (counter) reset */ #define AR5K_QCU_MISC_DCU_EARLY 0x00000800 /* DCU early termination */ #define AR5K_QCU_MISC_DCU_CMP_EN 0x00001000 /* Enable frame compression */ #define AR5K_QUEUE_MISC(_q) AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q) /* * QCU status registers */ #define AR5K_QCU_STS_BASE 0x0a00 /* Register Address - Queue0 STS */ #define AR5K_QCU_STS_FRMPENDCNT 0x00000003 /* Frames pending counter */ #define AR5K_QCU_STS_CBREXPCNT 0x0000ff00 /* CBR expired counter */ #define AR5K_QUEUE_STATUS(_q) AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q) /* * QCU ready time shutdown register */ #define AR5K_QCU_RDYTIMESHDN 0x0a40 #define AR5K_QCU_RDYTIMESHDN_M 0x000003ff /* * QCU compression buffer base registers [5212+] */ #define AR5K_QCU_CBB_SELECT 0x0b00 #define AR5K_QCU_CBB_ADDR 0x0b04 #define AR5K_QCU_CBB_ADDR_S 9 /* * QCU compression buffer configuration register [5212+] * (buffer size) */ #define AR5K_QCU_CBCFG 0x0b08 /* * Distributed Coordination Function (DCF) control unit (DCU) * registers [5211+] * * These registers control the various characteristics of each queue * for 802.11e (WME) compatibility so they go together with * QCU registers in pairs. For each queue we have a QCU mask register, * (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c), * a retry limit register (0x1080 - 0x10ac), a channel time register * (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and * a sequence number register (0x1140 - 0x116c). It seems that "global" * registers here affect all queues (see use of DCU_GBL_IFS_SLOT in ar5k). * We use the same macros here for easier register access. * */ /* * DCU QCU mask registers */ #define AR5K_DCU_QCUMASK_BASE 0x1000 /* Register Address -Queue0 DCU_QCUMASK */ #define AR5K_DCU_QCUMASK_M 0x000003ff #define AR5K_QUEUE_QCUMASK(_q) AR5K_QUEUE_REG(AR5K_DCU_QCUMASK_BASE, _q) /* * DCU local Inter Frame Space settings register */ #define AR5K_DCU_LCL_IFS_BASE 0x1040 /* Register Address -Queue0 DCU_LCL_IFS */ #define AR5K_DCU_LCL_IFS_CW_MIN 0x000003ff /* Minimum Contention Window */ #define AR5K_DCU_LCL_IFS_CW_MIN_S 0 #define AR5K_DCU_LCL_IFS_CW_MAX 0x000ffc00 /* Maximum Contention Window */ #define AR5K_DCU_LCL_IFS_CW_MAX_S 10 #define AR5K_DCU_LCL_IFS_AIFS 0x0ff00000 /* Arbitrated Interframe Space */ #define AR5K_DCU_LCL_IFS_AIFS_S 20 #define AR5K_DCU_LCL_IFS_AIFS_MAX 0xfc /* Anything above that can cause DCU to hang */ #define AR5K_QUEUE_DFS_LOCAL_IFS(_q) AR5K_QUEUE_REG(AR5K_DCU_LCL_IFS_BASE, _q) /* * DCU retry limit registers * all these fields don't allow zero values */ #define AR5K_DCU_RETRY_LMT_BASE 0x1080 /* Register Address -Queue0 DCU_RETRY_LMT */ #define AR5K_DCU_RETRY_LMT_RTS 0x0000000f /* RTS failure limit. Transmission fails if no CTS is received for this number of times */ #define AR5K_DCU_RETRY_LMT_RTS_S 0 #define AR5K_DCU_RETRY_LMT_STA_RTS 0x00003f00 /* STA RTS failure limit. If exceeded CW reset */ #define AR5K_DCU_RETRY_LMT_STA_RTS_S 8 #define AR5K_DCU_RETRY_LMT_STA_DATA 0x000fc000 /* STA data failure limit. If exceeded CW reset. */ #define AR5K_DCU_RETRY_LMT_STA_DATA_S 14 #define AR5K_QUEUE_DFS_RETRY_LIMIT(_q) AR5K_QUEUE_REG(AR5K_DCU_RETRY_LMT_BASE, _q) /* * DCU channel time registers */ #define AR5K_DCU_CHAN_TIME_BASE 0x10c0 /* Register Address -Queue0 DCU_CHAN_TIME */ #define AR5K_DCU_CHAN_TIME_DUR 0x000fffff /* Channel time duration */ #define AR5K_DCU_CHAN_TIME_DUR_S 0 #define AR5K_DCU_CHAN_TIME_ENABLE 0x00100000 /* Enable channel time */ #define AR5K_QUEUE_DFS_CHANNEL_TIME(_q) AR5K_QUEUE_REG(AR5K_DCU_CHAN_TIME_BASE, _q) /* * DCU misc registers [5211+] * * Note: Arbiter lockout control controls the * behaviour on low priority queues when we have multiple queues * with pending frames. Intra-frame lockout means we wait until * the queue's current frame transmits (with post frame backoff and bursting) * before we transmit anything else and global lockout means we * wait for the whole queue to finish before higher priority queues * can transmit (this is used on beacon and CAB queues). * No lockout means there is no special handling. */ #define AR5K_DCU_MISC_BASE 0x1100 /* Register Address -Queue0 DCU_MISC */ #define AR5K_DCU_MISC_BACKOFF 0x0000003f /* Mask for backoff threshold */ #define AR5K_DCU_MISC_ETS_RTS_POL 0x00000040 /* End of transmission series station RTS/data failure count reset policy (?) */ #define AR5K_DCU_MISC_ETS_CW_POL 0x00000080 /* End of transmission series CW reset policy */ #define AR5K_DCU_MISC_FRAG_WAIT 0x00000100 /* Wait for next fragment */ #define AR5K_DCU_MISC_BACKOFF_FRAG 0x00000200 /* Enable backoff while bursting */ #define AR5K_DCU_MISC_HCFPOLL_ENABLE 0x00000800 /* CF - Poll enable */ #define AR5K_DCU_MISC_BACKOFF_PERSIST 0x00001000 /* Persistent backoff */ #define AR5K_DCU_MISC_FRMPRFTCH_ENABLE 0x00002000 /* Enable frame pre-fetch */ #define AR5K_DCU_MISC_VIRTCOL 0x0000c000 /* Mask for Virtual Collision (?) */ #define AR5K_DCU_MISC_VIRTCOL_NORMAL 0 #define AR5K_DCU_MISC_VIRTCOL_IGNORE 1 #define AR5K_DCU_MISC_BCN_ENABLE 0x00010000 /* Enable Beacon use */ #define AR5K_DCU_MISC_ARBLOCK_CTL 0x00060000 /* Arbiter lockout control mask */ #define AR5K_DCU_MISC_ARBLOCK_CTL_S 17 #define AR5K_DCU_MISC_ARBLOCK_CTL_NONE 0 /* No arbiter lockout */ #define AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM 1 /* Intra-frame lockout */ #define AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL 2 /* Global lockout */ #define AR5K_DCU_MISC_ARBLOCK_IGNORE 0x00080000 /* Ignore Arbiter lockout */ #define AR5K_DCU_MISC_SEQ_NUM_INCR_DIS 0x00100000 /* Disable sequence number increment */ #define AR5K_DCU_MISC_POST_FR_BKOFF_DIS 0x00200000 /* Disable post-frame backoff */ #define AR5K_DCU_MISC_VIRT_COLL_POLICY 0x00400000 /* Virtual Collision cw policy */ #define AR5K_DCU_MISC_BLOWN_IFS_POLICY 0x00800000 /* Blown IFS policy (?) */ #define AR5K_DCU_MISC_SEQNUM_CTL 0x01000000 /* Sequence number control (?) */ #define AR5K_QUEUE_DFS_MISC(_q) AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q) /* * DCU frame sequence number registers */ #define AR5K_DCU_SEQNUM_BASE 0x1140 #define AR5K_DCU_SEQNUM_M 0x00000fff #define AR5K_QUEUE_DCU_SEQNUM(_q) AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q) /* * DCU global IFS SIFS register */ #define AR5K_DCU_GBL_IFS_SIFS 0x1030 #define AR5K_DCU_GBL_IFS_SIFS_M 0x0000ffff /* * DCU global IFS slot interval register */ #define AR5K_DCU_GBL_IFS_SLOT 0x1070 #define AR5K_DCU_GBL_IFS_SLOT_M 0x0000ffff /* * DCU global IFS EIFS register */ #define AR5K_DCU_GBL_IFS_EIFS 0x10b0 #define AR5K_DCU_GBL_IFS_EIFS_M 0x0000ffff /* * DCU global IFS misc register * * LFSR stands for Linear Feedback Shift Register * and it's used for generating pseudo-random * number sequences. * * (If i understand correctly, random numbers are * used for idle sensing -multiplied with cwmin/max etc-) */ #define AR5K_DCU_GBL_IFS_MISC 0x10f0 /* Register Address */ #define AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE 0x00000007 /* LFSR Slice Select */ #define AR5K_DCU_GBL_IFS_MISC_TURBO_MODE 0x00000008 /* Turbo mode */ #define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC 0x000003f0 /* SIFS Duration mask */ #define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC_S 4 #define AR5K_DCU_GBL_IFS_MISC_USEC_DUR 0x000ffc00 /* USEC Duration mask */ #define AR5K_DCU_GBL_IFS_MISC_USEC_DUR_S 10 #define AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY 0x00300000 /* DCU Arbiter delay mask */ #define AR5K_DCU_GBL_IFS_MISC_SIFS_CNT_RST 0x00400000 /* SIFS cnt reset policy (?) */ #define AR5K_DCU_GBL_IFS_MISC_AIFS_CNT_RST 0x00800000 /* AIFS cnt reset policy (?) */ #define AR5K_DCU_GBL_IFS_MISC_RND_LFSR_SL_DIS 0x01000000 /* Disable random LFSR slice */ /* * DCU frame prefetch control register */ #define AR5K_DCU_FP 0x1230 /* Register Address */ #define AR5K_DCU_FP_NOBURST_DCU_EN 0x00000001 /* Enable non-burst prefetch on DCU (?) */ #define AR5K_DCU_FP_NOBURST_EN 0x00000010 /* Enable non-burst prefetch (?) */ #define AR5K_DCU_FP_BURST_DCU_EN 0x00000020 /* Enable burst prefetch on DCU (?) */ /* * DCU transmit pause control/status register */ #define AR5K_DCU_TXP 0x1270 /* Register Address */ #define AR5K_DCU_TXP_M 0x000003ff /* Tx pause mask */ #define AR5K_DCU_TXP_STATUS 0x00010000 /* Tx pause status */ /* * DCU transmit filter table 0 (32 entries) * each entry contains a 32bit slice of the * 128bit tx filter for each DCU (4 slices per DCU) */ #define AR5K_DCU_TX_FILTER_0_BASE 0x1038 #define AR5K_DCU_TX_FILTER_0(_n) (AR5K_DCU_TX_FILTER_0_BASE + (_n * 64)) /* * DCU transmit filter table 1 (16 entries) */ #define AR5K_DCU_TX_FILTER_1_BASE 0x103c #define AR5K_DCU_TX_FILTER_1(_n) (AR5K_DCU_TX_FILTER_1_BASE + (_n * 64)) /* * DCU clear transmit filter register */ #define AR5K_DCU_TX_FILTER_CLR 0x143c /* * DCU set transmit filter register */ #define AR5K_DCU_TX_FILTER_SET 0x147c /* * Reset control register */ #define AR5K_RESET_CTL 0x4000 /* Register Address */ #define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */ #define AR5K_RESET_CTL_DMA 0x00000002 /* DMA (Rx/Tx) reset [5210] */ #define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset [5211+] */ #define AR5K_RESET_CTL_MAC 0x00000004 /* MAC reset (PCU+Baseband ?) [5210] */ #define AR5K_RESET_CTL_PHY 0x00000008 /* PHY reset [5210] */ #define AR5K_RESET_CTL_PCI 0x00000010 /* PCI Core reset (interrupts etc) */ /* * Sleep control register */ #define AR5K_SLEEP_CTL 0x4004 /* Register Address */ #define AR5K_SLEEP_CTL_SLDUR 0x0000ffff /* Sleep duration mask */ #define AR5K_SLEEP_CTL_SLDUR_S 0 #define AR5K_SLEEP_CTL_SLE 0x00030000 /* Sleep enable mask */ #define AR5K_SLEEP_CTL_SLE_S 16 #define AR5K_SLEEP_CTL_SLE_WAKE 0x00000000 /* Force chip awake */ #define AR5K_SLEEP_CTL_SLE_SLP 0x00010000 /* Force chip sleep */ #define AR5K_SLEEP_CTL_SLE_ALLOW 0x00020000 /* Normal sleep policy */ #define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* [5211+] */ #define AR5K_SLEEP_CTL_DUR_TIM_POL 0x00040000 /* Sleep duration timing policy */ #define AR5K_SLEEP_CTL_DUR_WRITE_POL 0x00080000 /* Sleep duration write policy */ #define AR5K_SLEEP_CTL_SLE_POL 0x00100000 /* Sleep policy mode */ /* * Interrupt pending register */ #define AR5K_INTPEND 0x4008 #define AR5K_INTPEND_M 0x00000001 /* * Sleep force register */ #define AR5K_SFR 0x400c #define AR5K_SFR_EN 0x00000001 /* * PCI configuration register * TODO: Fix LED stuff */ #define AR5K_PCICFG 0x4010 /* Register Address */ #define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */ #define AR5K_PCICFG_SLEEP_CLOCK_EN 0x00000002 /* Enable sleep clock */ #define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */ #define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */ #define AR5K_PCICFG_EESIZE_S 3 #define AR5K_PCICFG_EESIZE_4K 0 /* 4K */ #define AR5K_PCICFG_EESIZE_8K 1 /* 8K */ #define AR5K_PCICFG_EESIZE_16K 2 /* 16K */ #define AR5K_PCICFG_EESIZE_FAIL 3 /* Failed to get size [5211+] */ #define AR5K_PCICFG_LED 0x00000060 /* Led status [5211+] */ #define AR5K_PCICFG_LED_NONE 0x00000000 /* Default [5211+] */ #define AR5K_PCICFG_LED_PEND 0x00000020 /* Scan / Auth pending */ #define AR5K_PCICFG_LED_ASSOC 0x00000040 /* Associated */ #define AR5K_PCICFG_BUS_SEL 0x00000380 /* Mask for "bus select" [5211+] (?) */ #define AR5K_PCICFG_CBEFIX_DIS 0x00000400 /* Disable CBE fix */ #define AR5K_PCICFG_SL_INTEN 0x00000800 /* Enable interrupts when asleep */ #define AR5K_PCICFG_LED_BCTL 0x00001000 /* Led blink (?) [5210] */ #define AR5K_PCICFG_RETRY_FIX 0x00001000 /* Enable pci core retry fix */ #define AR5K_PCICFG_SL_INPEN 0x00002000 /* Sleep even with pending interrupts*/ #define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status */ #define AR5K_PCICFG_LEDMODE 0x000e0000 /* Ledmode [5211+] */ #define AR5K_PCICFG_LEDMODE_PROP 0x00000000 /* Blink on standard traffic [5211+] */ #define AR5K_PCICFG_LEDMODE_PROM 0x00020000 /* Default mode (blink on any traffic) [5211+] */ #define AR5K_PCICFG_LEDMODE_PWR 0x00040000 /* Some other blinking mode (?) [5211+] */ #define AR5K_PCICFG_LEDMODE_RAND 0x00060000 /* Random blinking (?) [5211+] */ #define AR5K_PCICFG_LEDBLINK 0x00700000 /* Led blink rate */ #define AR5K_PCICFG_LEDBLINK_S 20 #define AR5K_PCICFG_LEDSLOW 0x00800000 /* Slowest led blink rate [5211+] */ #define AR5K_PCICFG_LEDSTATE \ (AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE | \ AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW) #define AR5K_PCICFG_SLEEP_CLOCK_RATE 0x03000000 /* Sleep clock rate */ #define AR5K_PCICFG_SLEEP_CLOCK_RATE_S 24 /* * "General Purpose Input/Output" (GPIO) control register * * I'm not sure about this but after looking at the code * for all chipsets here is what i got. * * We have 6 GPIOs (pins), each GPIO has 4 modes (2 bits) * Mode 0 -> always input * Mode 1 -> output when GPIODO for this GPIO is set to 0 * Mode 2 -> output when GPIODO for this GPIO is set to 1 * Mode 3 -> always output * * For more infos check out get_gpio/set_gpio and * set_gpio_input/set_gpio_output functs. * For more infos on gpio interrupt check out set_gpio_intr. */ #define AR5K_NUM_GPIO 6 #define AR5K_GPIOCR 0x4014 /* Register Address */ #define AR5K_GPIOCR_INT_ENA 0x00008000 /* Enable GPIO interrupt */ #define AR5K_GPIOCR_INT_SELL 0x00000000 /* Generate interrupt when pin is low */ #define AR5K_GPIOCR_INT_SELH 0x00010000 /* Generate interrupt when pin is high */ #define AR5K_GPIOCR_IN(n) (0 << ((n) * 2)) /* Mode 0 for pin n */ #define AR5K_GPIOCR_OUT0(n) (1 << ((n) * 2)) /* Mode 1 for pin n */ #define AR5K_GPIOCR_OUT1(n) (2 << ((n) * 2)) /* Mode 2 for pin n */ #define AR5K_GPIOCR_OUT(n) (3 << ((n) * 2)) /* Mode 3 for pin n */ #define AR5K_GPIOCR_INT_SEL(n) ((n) << 12) /* Interrupt for GPIO pin n */ /* * "General Purpose Input/Output" (GPIO) data output register */ #define AR5K_GPIODO 0x4018 /* * "General Purpose Input/Output" (GPIO) data input register */ #define AR5K_GPIODI 0x401c #define AR5K_GPIODI_M 0x0000002f /* * Silicon revision register */ #define AR5K_SREV 0x4020 /* Register Address */ #define AR5K_SREV_REV 0x0000000f /* Mask for revision */ #define AR5K_SREV_REV_S 0 #define AR5K_SREV_VER 0x000000ff /* Mask for version */ #define AR5K_SREV_VER_S 4 /* * TXE write posting register */ #define AR5K_TXEPOST 0x4028 /* * QCU sleep mask */ #define AR5K_QCU_SLEEP_MASK 0x402c /* 0x4068 is compression buffer configuration * register on 5414 and pm configuration register * on 5424 and newer pci-e chips. */ /* * Compression buffer configuration * register (enable/disable) [5414] */ #define AR5K_5414_CBCFG 0x4068 #define AR5K_5414_CBCFG_BUF_DIS 0x10 /* Disable buffer */ /* * PCI-E Power management configuration * and status register [5424+] */ #define AR5K_PCIE_PM_CTL 0x4068 /* Register address */ /* Only 5424 */ #define AR5K_PCIE_PM_CTL_L1_WHEN_D2 0x00000001 /* enable PCIe core enter L1 when d2_sleep_en is asserted */ #define AR5K_PCIE_PM_CTL_L0_L0S_CLEAR 0x00000002 /* Clear L0 and L0S counters */ #define AR5K_PCIE_PM_CTL_L0_L0S_EN 0x00000004 /* Start L0 nd L0S counters */ #define AR5K_PCIE_PM_CTL_LDRESET_EN 0x00000008 /* Enable reset when link goes down */ /* Wake On Wireless */ #define AR5K_PCIE_PM_CTL_PME_EN 0x00000010 /* PME Enable */ #define AR5K_PCIE_PM_CTL_AUX_PWR_DET 0x00000020 /* Aux power detect */ #define AR5K_PCIE_PM_CTL_PME_CLEAR 0x00000040 /* Clear PME */ #define AR5K_PCIE_PM_CTL_PSM_D0 0x00000080 #define AR5K_PCIE_PM_CTL_PSM_D1 0x00000100 #define AR5K_PCIE_PM_CTL_PSM_D2 0x00000200 #define AR5K_PCIE_PM_CTL_PSM_D3 0x00000400 /* * PCI-E Workaround enable register */ #define AR5K_PCIE_WAEN 0x407c /* * PCI-E Serializer/Deserializer * registers */ #define AR5K_PCIE_SERDES 0x4080 #define AR5K_PCIE_SERDES_RESET 0x4084 /*====EEPROM REGISTERS====*/ /* * EEPROM access registers * * Here we got a difference between 5210/5211-12 * read data register for 5210 is at 0x6800 and * status register is at 0x6c00. There is also * no eeprom command register on 5210 and the * offsets are different. * * To read eeprom data for a specific offset: * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) * read AR5K_EEPROM_BASE +(4 * offset) * check the eeprom status register * and read eeprom data register. * * 5211 - write offset to AR5K_EEPROM_BASE * 5212 write AR5K_EEPROM_CMD_READ on AR5K_EEPROM_CMD * check the eeprom status register * and read eeprom data register. * * To write eeprom data for a specific offset: * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) * write data to AR5K_EEPROM_BASE +(4 * offset) * check the eeprom status register * 5211 - write AR5K_EEPROM_CMD_RESET on AR5K_EEPROM_CMD * 5212 write offset to AR5K_EEPROM_BASE * write data to data register * write AR5K_EEPROM_CMD_WRITE on AR5K_EEPROM_CMD * check the eeprom status register * * For more infos check eeprom_* functs and the ar5k.c * file posted in madwifi-devel mailing list. * http://sourceforge.net/mailarchive/message.php?msg_id=8966525 * */ #define AR5K_EEPROM_BASE 0x6000 /* * EEPROM data register */ #define AR5K_EEPROM_DATA_5211 0x6004 #define AR5K_EEPROM_DATA_5210 0x6800 #define AR5K_EEPROM_DATA (ah->ah_version == AR5K_AR5210 ? \ AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211) /* * EEPROM command register */ #define AR5K_EEPROM_CMD 0x6008 /* Register Address */ #define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */ #define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */ #define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */ /* * EEPROM status register */ #define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */ #define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */ #define AR5K_EEPROM_STATUS (ah->ah_version == AR5K_AR5210 ? \ AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211) #define AR5K_EEPROM_STAT_RDERR 0x00000001 /* EEPROM read failed */ #define AR5K_EEPROM_STAT_RDDONE 0x00000002 /* EEPROM read successful */ #define AR5K_EEPROM_STAT_WRERR 0x00000004 /* EEPROM write failed */ #define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */ /* * EEPROM config register */ #define AR5K_EEPROM_CFG 0x6010 /* Register Address */ #define AR5K_EEPROM_CFG_SIZE 0x00000003 /* Size determination override */ #define AR5K_EEPROM_CFG_SIZE_AUTO 0 #define AR5K_EEPROM_CFG_SIZE_4KBIT 1 #define AR5K_EEPROM_CFG_SIZE_8KBIT 2 #define AR5K_EEPROM_CFG_SIZE_16KBIT 3 #define AR5K_EEPROM_CFG_WR_WAIT_DIS 0x00000004 /* Disable write wait */ #define AR5K_EEPROM_CFG_CLK_RATE 0x00000018 /* Clock rate */ #define AR5K_EEPROM_CFG_CLK_RATE_S 3 #define AR5K_EEPROM_CFG_CLK_RATE_156KHZ 0 #define AR5K_EEPROM_CFG_CLK_RATE_312KHZ 1 #define AR5K_EEPROM_CFG_CLK_RATE_625KHZ 2 #define AR5K_EEPROM_CFG_PROT_KEY 0x00ffff00 /* Protection key */ #define AR5K_EEPROM_CFG_PROT_KEY_S 8 #define AR5K_EEPROM_CFG_LIND_EN 0x01000000 /* Enable length indicator (?) */ /* * TODO: Wake On Wireless registers * Range 0x7000 - 0x7ce0 */ /* * Protocol Control Unit (PCU) registers */ /* * Used for checking initial register writes * during channel reset (see reset func) */ #define AR5K_PCU_MIN 0x8000 #define AR5K_PCU_MAX 0x8fff /* * First station id register (Lower 32 bits of MAC address) */ #define AR5K_STA_ID0 0x8000 #define AR5K_STA_ID0_ARRD_L32 0xffffffff /* * Second station id register (Upper 16 bits of MAC address + PCU settings) */ #define AR5K_STA_ID1 0x8004 /* Register Address */ #define AR5K_STA_ID1_ADDR_U16 0x0000ffff /* Upper 16 bits of MAC address */ #define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */ #define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */ #define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting */ #define AR5K_STA_ID1_NO_KEYSRCH 0x00080000 /* No key search */ #define AR5K_STA_ID1_NO_PSPOLL 0x00100000 /* No power save polling [5210] */ #define AR5K_STA_ID1_PCF_5211 0x00100000 /* Enable PCF on [5211+] */ #define AR5K_STA_ID1_PCF_5210 0x00200000 /* Enable PCF on [5210]*/ #define AR5K_STA_ID1_PCF (ah->ah_version == AR5K_AR5210 ? \ AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211) #define AR5K_STA_ID1_DEFAULT_ANTENNA 0x00200000 /* Use default antenna */ #define AR5K_STA_ID1_DESC_ANTENNA 0x00400000 /* Update antenna from descriptor */ #define AR5K_STA_ID1_RTS_DEF_ANTENNA 0x00800000 /* Use default antenna for RTS */ #define AR5K_STA_ID1_ACKCTS_6MB 0x01000000 /* Rate to use for ACK/CTS. 0: highest mandatory rate <= RX rate; 1: 1Mbps in B mode */ #define AR5K_STA_ID1_BASE_RATE_11B 0x02000000 /* 802.11b base rate. 0: 1, 2, 5.5 and 11Mbps; 1: 1 and 2Mbps. [5211+] */ #define AR5K_STA_ID1_SELFGEN_DEF_ANT 0x04000000 /* Use def. antenna for self generated frames */ #define AR5K_STA_ID1_CRYPT_MIC_EN 0x08000000 /* Enable MIC */ #define AR5K_STA_ID1_KEYSRCH_MODE 0x10000000 /* Look up key when key id != 0 */ #define AR5K_STA_ID1_PRESERVE_SEQ_NUM 0x20000000 /* Preserve sequence number */ #define AR5K_STA_ID1_CBCIV_ENDIAN 0x40000000 /* ??? */ #define AR5K_STA_ID1_KEYSRCH_MCAST 0x80000000 /* Do key cache search for mcast frames */ #define AR5K_STA_ID1_ANTENNA_SETTINGS (AR5K_STA_ID1_DEFAULT_ANTENNA | \ AR5K_STA_ID1_DESC_ANTENNA | \ AR5K_STA_ID1_RTS_DEF_ANTENNA | \ AR5K_STA_ID1_SELFGEN_DEF_ANT) /* * First BSSID register (MAC address, lower 32bits) */ #define AR5K_BSS_ID0 0x8008 /* * Second BSSID register (MAC address in upper 16 bits) * * AID: Association ID */ #define AR5K_BSS_ID1 0x800c #define AR5K_BSS_ID1_AID 0xffff0000 #define AR5K_BSS_ID1_AID_S 16 /* * Backoff slot time register */ #define AR5K_SLOT_TIME 0x8010 /* * ACK/CTS timeout register */ #define AR5K_TIME_OUT 0x8014 /* Register Address */ #define AR5K_TIME_OUT_ACK 0x00001fff /* ACK timeout mask */ #define AR5K_TIME_OUT_ACK_S 0 #define AR5K_TIME_OUT_CTS 0x1fff0000 /* CTS timeout mask */ #define AR5K_TIME_OUT_CTS_S 16 /* * RSSI threshold register */ #define AR5K_RSSI_THR 0x8018 /* Register Address */ #define AR5K_RSSI_THR_M 0x000000ff /* Mask for RSSI threshold [5211+] */ #define AR5K_RSSI_THR_BMISS_5210 0x00000700 /* Mask for Beacon Missed threshold [5210] */ #define AR5K_RSSI_THR_BMISS_5210_S 8 #define AR5K_RSSI_THR_BMISS_5211 0x0000ff00 /* Mask for Beacon Missed threshold [5211+] */ #define AR5K_RSSI_THR_BMISS_5211_S 8 #define AR5K_RSSI_THR_BMISS (ah->ah_version == AR5K_AR5210 ? \ AR5K_RSSI_THR_BMISS_5210 : AR5K_RSSI_THR_BMISS_5211) #define AR5K_RSSI_THR_BMISS_S 8 /* * 5210 has more PCU registers because there is no QCU/DCU * so queue parameters are set here, this way a lot common * registers have different address for 5210. To make things * easier we define a macro based on ah->ah_version for common * registers with different addresses and common flags. */ /* * Retry limit register * * Retry limit register for 5210 (no QCU/DCU so it's done in PCU) */ #define AR5K_NODCU_RETRY_LMT 0x801c /* Register Address */ #define AR5K_NODCU_RETRY_LMT_SH_RETRY 0x0000000f /* Short retry limit mask */ #define AR5K_NODCU_RETRY_LMT_SH_RETRY_S 0 #define AR5K_NODCU_RETRY_LMT_LG_RETRY 0x000000f0 /* Long retry mask */ #define AR5K_NODCU_RETRY_LMT_LG_RETRY_S 4 #define AR5K_NODCU_RETRY_LMT_SSH_RETRY 0x00003f00 /* Station short retry limit mask */ #define AR5K_NODCU_RETRY_LMT_SSH_RETRY_S 8 #define AR5K_NODCU_RETRY_LMT_SLG_RETRY 0x000fc000 /* Station long retry limit mask */ #define AR5K_NODCU_RETRY_LMT_SLG_RETRY_S 14 #define AR5K_NODCU_RETRY_LMT_CW_MIN 0x3ff00000 /* Minimum contention window mask */ #define AR5K_NODCU_RETRY_LMT_CW_MIN_S 20 /* * Transmit latency register */ #define AR5K_USEC_5210 0x8020 /* Register Address [5210] */ #define AR5K_USEC_5211 0x801c /* Register Address [5211+] */ #define AR5K_USEC (ah->ah_version == AR5K_AR5210 ? \ AR5K_USEC_5210 : AR5K_USEC_5211) #define AR5K_USEC_1 0x0000007f /* clock cycles for 1us */ #define AR5K_USEC_1_S 0 #define AR5K_USEC_32 0x00003f80 /* clock cycles for 1us while on 32MHz clock */ #define AR5K_USEC_32_S 7 #define AR5K_USEC_TX_LATENCY_5211 0x007fc000 #define AR5K_USEC_TX_LATENCY_5211_S 14 #define AR5K_USEC_RX_LATENCY_5211 0x1f800000 #define AR5K_USEC_RX_LATENCY_5211_S 23 #define AR5K_USEC_TX_LATENCY_5210 0x000fc000 /* also for 5311 */ #define AR5K_USEC_TX_LATENCY_5210_S 14 #define AR5K_USEC_RX_LATENCY_5210 0x03f00000 /* also for 5311 */ #define AR5K_USEC_RX_LATENCY_5210_S 20 /* * PCU beacon control register */ #define AR5K_BEACON_5210 0x8024 /*Register Address [5210] */ #define AR5K_BEACON_5211 0x8020 /*Register Address [5211+] */ #define AR5K_BEACON (ah->ah_version == AR5K_AR5210 ? \ AR5K_BEACON_5210 : AR5K_BEACON_5211) #define AR5K_BEACON_PERIOD 0x0000ffff /* Mask for beacon period */ #define AR5K_BEACON_PERIOD_S 0 #define AR5K_BEACON_TIM 0x007f0000 /* Mask for TIM offset */ #define AR5K_BEACON_TIM_S 16 #define AR5K_BEACON_ENABLE 0x00800000 /* Enable beacons */ #define AR5K_BEACON_RESET_TSF 0x01000000 /* Force TSF reset */ /* * CFP period register */ #define AR5K_CFP_PERIOD_5210 0x8028 #define AR5K_CFP_PERIOD_5211 0x8024 #define AR5K_CFP_PERIOD (ah->ah_version == AR5K_AR5210 ? \ AR5K_CFP_PERIOD_5210 : AR5K_CFP_PERIOD_5211) /* * Next beacon time register */ #define AR5K_TIMER0_5210 0x802c #define AR5K_TIMER0_5211 0x8028 #define AR5K_TIMER0 (ah->ah_version == AR5K_AR5210 ? \ AR5K_TIMER0_5210 : AR5K_TIMER0_5211) /* * Next DMA beacon alert register */ #define AR5K_TIMER1_5210 0x8030 #define AR5K_TIMER1_5211 0x802c #define AR5K_TIMER1 (ah->ah_version == AR5K_AR5210 ? \ AR5K_TIMER1_5210 : AR5K_TIMER1_5211) /* * Next software beacon alert register */ #define AR5K_TIMER2_5210 0x8034 #define AR5K_TIMER2_5211 0x8030 #define AR5K_TIMER2 (ah->ah_version == AR5K_AR5210 ? \ AR5K_TIMER2_5210 : AR5K_TIMER2_5211) /* * Next ATIM window time register */ #define AR5K_TIMER3_5210 0x8038 #define AR5K_TIMER3_5211 0x8034 #define AR5K_TIMER3 (ah->ah_version == AR5K_AR5210 ? \ AR5K_TIMER3_5210 : AR5K_TIMER3_5211) /* * 5210 First inter frame spacing register (IFS) */ #define AR5K_IFS0 0x8040 #define AR5K_IFS0_SIFS 0x000007ff #define AR5K_IFS0_SIFS_S 0 #define AR5K_IFS0_DIFS 0x007ff800 #define AR5K_IFS0_DIFS_S 11 /* * 5210 Second inter frame spacing register (IFS) */ #define AR5K_IFS1 0x8044 #define AR5K_IFS1_PIFS 0x00000fff #define AR5K_IFS1_PIFS_S 0 #define AR5K_IFS1_EIFS 0x03fff000 #define AR5K_IFS1_EIFS_S 12 #define AR5K_IFS1_CS_EN 0x04000000 #define AR5K_IFS1_CS_EN_S 26 /* * CFP duration register */ #define AR5K_CFP_DUR_5210 0x8048 #define AR5K_CFP_DUR_5211 0x8038 #define AR5K_CFP_DUR (ah->ah_version == AR5K_AR5210 ? \ AR5K_CFP_DUR_5210 : AR5K_CFP_DUR_5211) /* * Receive filter register */ #define AR5K_RX_FILTER_5210 0x804c /* Register Address [5210] */ #define AR5K_RX_FILTER_5211 0x803c /* Register Address [5211+] */ #define AR5K_RX_FILTER (ah->ah_version == AR5K_AR5210 ? \ AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211) #define AR5K_RX_FILTER_UCAST 0x00000001 /* Don't filter unicast frames */ #define AR5K_RX_FILTER_MCAST 0x00000002 /* Don't filter multicast frames */ #define AR5K_RX_FILTER_BCAST 0x00000004 /* Don't filter broadcast frames */ #define AR5K_RX_FILTER_CONTROL 0x00000008 /* Don't filter control frames */ #define AR5K_RX_FILTER_BEACON 0x00000010 /* Don't filter beacon frames */ #define AR5K_RX_FILTER_PROM 0x00000020 /* Set promiscuous mode */ #define AR5K_RX_FILTER_XRPOLL 0x00000040 /* Don't filter XR poll frame [5212+] */ #define AR5K_RX_FILTER_PROBEREQ 0x00000080 /* Don't filter probe requests [5212+] */ #define AR5K_RX_FILTER_PHYERR_5212 0x00000100 /* Don't filter phy errors [5212+] */ #define AR5K_RX_FILTER_RADARERR_5212 0x00000200 /* Don't filter phy radar errors [5212+] */ #define AR5K_RX_FILTER_PHYERR_5211 0x00000040 /* [5211] */ #define AR5K_RX_FILTER_RADARERR_5211 0x00000080 /* [5211] */ #define AR5K_RX_FILTER_PHYERR \ ((ah->ah_version == AR5K_AR5211 ? \ AR5K_RX_FILTER_PHYERR_5211 : AR5K_RX_FILTER_PHYERR_5212)) #define AR5K_RX_FILTER_RADARERR \ ((ah->ah_version == AR5K_AR5211 ? \ AR5K_RX_FILTER_RADARERR_5211 : AR5K_RX_FILTER_RADARERR_5212)) /* * Multicast filter register (lower 32 bits) */ #define AR5K_MCAST_FILTER0_5210 0x8050 #define AR5K_MCAST_FILTER0_5211 0x8040 #define AR5K_MCAST_FILTER0 (ah->ah_version == AR5K_AR5210 ? \ AR5K_MCAST_FILTER0_5210 : AR5K_MCAST_FILTER0_5211) /* * Multicast filter register (higher 16 bits) */ #define AR5K_MCAST_FILTER1_5210 0x8054 #define AR5K_MCAST_FILTER1_5211 0x8044 #define AR5K_MCAST_FILTER1 (ah->ah_version == AR5K_AR5210 ? \ AR5K_MCAST_FILTER1_5210 : AR5K_MCAST_FILTER1_5211) /* * Transmit mask register (lower 32 bits) [5210] */ #define AR5K_TX_MASK0 0x8058 /* * Transmit mask register (higher 16 bits) [5210] */ #define AR5K_TX_MASK1 0x805c /* * Clear transmit mask [5210] */ #define AR5K_CLR_TMASK 0x8060 /* * Trigger level register (before transmission) [5210] */ #define AR5K_TRIG_LVL 0x8064 /* * PCU Diagnostic register * * Used for tweaking/diagnostics. */ #define AR5K_DIAG_SW_5210 0x8068 /* Register Address [5210] */ #define AR5K_DIAG_SW_5211 0x8048 /* Register Address [5211+] */ #define AR5K_DIAG_SW (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211) #define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 /* Disable ACKs if WEP key is invalid */ #define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs */ #define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs */ #define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable HW encryption */ #define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable HW decryption */ #define AR5K_DIAG_SW_DIS_TX_5210 0x00000020 /* Disable transmit [5210] */ #define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable receive */ #define AR5K_DIAG_SW_DIS_RX_5211 0x00000020 #define AR5K_DIAG_SW_DIS_RX (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211) #define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* TX Data Loopback (i guess it goes with DIS_TX) [5210] */ #define AR5K_DIAG_SW_LOOP_BACK_5211 0x00000040 #define AR5K_DIAG_SW_LOOP_BACK (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211) #define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 /* Generate invalid TX FCS */ #define AR5K_DIAG_SW_CORR_FCS_5211 0x00000080 #define AR5K_DIAG_SW_CORR_FCS (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211) #define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 /* Add 56 bytes of channel info before the frame data in the RX buffer */ #define AR5K_DIAG_SW_CHAN_INFO_5211 0x00000100 #define AR5K_DIAG_SW_CHAN_INFO (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211) #define AR5K_DIAG_SW_EN_SCRAM_SEED_5210 0x00000400 /* Enable fixed scrambler seed */ #define AR5K_DIAG_SW_EN_SCRAM_SEED_5211 0x00000200 #define AR5K_DIAG_SW_EN_SCRAM_SEED (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211) #define AR5K_DIAG_SW_ECO_ENABLE 0x00000400 /* [5211+] */ #define AR5K_DIAG_SW_SCVRAM_SEED 0x0003f800 /* [5210] */ #define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask */ #define AR5K_DIAG_SW_SCRAM_SEED_S 10 #define AR5K_DIAG_SW_DIS_SEQ_INC_5210 0x00040000 /* Disable seqnum increment (?)[5210] */ #define AR5K_DIAG_SW_FRAME_NV0_5210 0x00080000 #define AR5K_DIAG_SW_FRAME_NV0_5211 0x00020000 /* Accept frames of non-zero protocol number */ #define AR5K_DIAG_SW_FRAME_NV0 (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211) #define AR5K_DIAG_SW_OBSPT_M 0x000c0000 /* Observation point select (?) */ #define AR5K_DIAG_SW_OBSPT_S 18 #define AR5K_DIAG_SW_RX_CLEAR_HIGH 0x00100000 /* Ignore carrier sense */ #define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x00200000 /* Ignore virtual carrier sense */ #define AR5K_DIAG_SW_CHANNEL_IDLE_HIGH 0x00400000 /* Force channel idle high */ #define AR5K_DIAG_SW_PHEAR_ME 0x00800000 /* ??? */ /* * TSF (clock) register (lower 32 bits) */ #define AR5K_TSF_L32_5210 0x806c #define AR5K_TSF_L32_5211 0x804c #define AR5K_TSF_L32 (ah->ah_version == AR5K_AR5210 ? \ AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211) /* * TSF (clock) register (higher 32 bits) */ #define AR5K_TSF_U32_5210 0x8070 #define AR5K_TSF_U32_5211 0x8050 #define AR5K_TSF_U32 (ah->ah_version == AR5K_AR5210 ? \ AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211) /* * Last beacon timestamp register (Read Only) */ #define AR5K_LAST_TSTP 0x8080 /* * ADDAC test register [5211+] */ #define AR5K_ADDAC_TEST 0x8054 /* Register Address */ #define AR5K_ADDAC_TEST_TXCONT 0x00000001 /* Test continuous tx */ #define AR5K_ADDAC_TEST_TST_MODE 0x00000002 /* Test mode */ #define AR5K_ADDAC_TEST_LOOP_EN 0x00000004 /* Enable loop */ #define AR5K_ADDAC_TEST_LOOP_LEN 0x00000008 /* Loop length (field) */ #define AR5K_ADDAC_TEST_USE_U8 0x00004000 /* Use upper 8 bits */ #define AR5K_ADDAC_TEST_MSB 0x00008000 /* State of MSB */ #define AR5K_ADDAC_TEST_TRIG_SEL 0x00010000 /* Trigger select */ #define AR5K_ADDAC_TEST_TRIG_PTY 0x00020000 /* Trigger polarity */ #define AR5K_ADDAC_TEST_RXCONT 0x00040000 /* Continuous capture */ #define AR5K_ADDAC_TEST_CAPTURE 0x00080000 /* Begin capture */ #define AR5K_ADDAC_TEST_TST_ARM 0x00100000 /* ARM rx buffer for capture */ /* * Default antenna register [5211+] */ #define AR5K_DEFAULT_ANTENNA 0x8058 /* * Frame control QoS mask register (?) [5211+] * (FC_QOS_MASK) */ #define AR5K_FRAME_CTL_QOSM 0x805c /* * Seq mask register (?) [5211+] */ #define AR5K_SEQ_MASK 0x8060 /* * Retry count register [5210] */ #define AR5K_RETRY_CNT 0x8084 /* Register Address [5210] */ #define AR5K_RETRY_CNT_SSH 0x0000003f /* Station short retry count (?) */ #define AR5K_RETRY_CNT_SLG 0x00000fc0 /* Station long retry count (?) */ /* * Back-off status register [5210] */ #define AR5K_BACKOFF 0x8088 /* Register Address [5210] */ #define AR5K_BACKOFF_CW 0x000003ff /* Backoff Contention Window (?) */ #define AR5K_BACKOFF_CNT 0x03ff0000 /* Backoff count (?) */ /* * NAV register (current) */ #define AR5K_NAV_5210 0x808c #define AR5K_NAV_5211 0x8084 #define AR5K_NAV (ah->ah_version == AR5K_AR5210 ? \ AR5K_NAV_5210 : AR5K_NAV_5211) /* * MIB counters: * * max value is 0xc000, if this is reached we get a MIB interrupt. * they can be controlled via AR5K_MIBC and are cleared on read. */ /* * RTS success (MIB counter) */ #define AR5K_RTS_OK_5210 0x8090 #define AR5K_RTS_OK_5211 0x8088 #define AR5K_RTS_OK (ah->ah_version == AR5K_AR5210 ? \ AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211) /* * RTS failure (MIB counter) */ #define AR5K_RTS_FAIL_5210 0x8094 #define AR5K_RTS_FAIL_5211 0x808c #define AR5K_RTS_FAIL (ah->ah_version == AR5K_AR5210 ? \ AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211) /* * ACK failure (MIB counter) */ #define AR5K_ACK_FAIL_5210 0x8098 #define AR5K_ACK_FAIL_5211 0x8090 #define AR5K_ACK_FAIL (ah->ah_version == AR5K_AR5210 ? \ AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211) /* * FCS failure (MIB counter) */ #define AR5K_FCS_FAIL_5210 0x809c #define AR5K_FCS_FAIL_5211 0x8094 #define AR5K_FCS_FAIL (ah->ah_version == AR5K_AR5210 ? \ AR5K_FCS_FAIL_5210 : AR5K_FCS_FAIL_5211) /* * Beacon count register */ #define AR5K_BEACON_CNT_5210 0x80a0 #define AR5K_BEACON_CNT_5211 0x8098 #define AR5K_BEACON_CNT (ah->ah_version == AR5K_AR5210 ? \ AR5K_BEACON_CNT_5210 : AR5K_BEACON_CNT_5211) /*===5212 Specific PCU registers===*/ /* * Transmit power control register */ #define AR5K_TPC 0x80e8 #define AR5K_TPC_ACK 0x0000003f /* ack frames */ #define AR5K_TPC_ACK_S 0 #define AR5K_TPC_CTS 0x00003f00 /* cts frames */ #define AR5K_TPC_CTS_S 8 #define AR5K_TPC_CHIRP 0x003f0000 /* chirp frames */ #define AR5K_TPC_CHIRP_S 16 #define AR5K_TPC_DOPPLER 0x0f000000 /* doppler chirp span */ #define AR5K_TPC_DOPPLER_S 24 /* * XR (eXtended Range) mode register */ #define AR5K_XRMODE 0x80c0 /* Register Address */ #define AR5K_XRMODE_POLL_TYPE_M 0x0000003f /* Mask for Poll type (?) */ #define AR5K_XRMODE_POLL_TYPE_S 0 #define AR5K_XRMODE_POLL_SUBTYPE_M 0x0000003c /* Mask for Poll subtype (?) */ #define AR5K_XRMODE_POLL_SUBTYPE_S 2 #define AR5K_XRMODE_POLL_WAIT_ALL 0x00000080 /* Wait for poll */ #define AR5K_XRMODE_SIFS_DELAY 0x000fff00 /* Mask for SIFS delay */ #define AR5K_XRMODE_FRAME_HOLD_M 0xfff00000 /* Mask for frame hold (?) */ #define AR5K_XRMODE_FRAME_HOLD_S 20 /* * XR delay register */ #define AR5K_XRDELAY 0x80c4 /* Register Address */ #define AR5K_XRDELAY_SLOT_DELAY_M 0x0000ffff /* Mask for slot delay */ #define AR5K_XRDELAY_SLOT_DELAY_S 0 #define AR5K_XRDELAY_CHIRP_DELAY_M 0xffff0000 /* Mask for CHIRP data delay */ #define AR5K_XRDELAY_CHIRP_DELAY_S 16 /* * XR timeout register */ #define AR5K_XRTIMEOUT 0x80c8 /* Register Address */ #define AR5K_XRTIMEOUT_CHIRP_M 0x0000ffff /* Mask for CHIRP timeout */ #define AR5K_XRTIMEOUT_CHIRP_S 0 #define AR5K_XRTIMEOUT_POLL_M 0xffff0000 /* Mask for Poll timeout */ #define AR5K_XRTIMEOUT_POLL_S 16 /* * XR chirp register */ #define AR5K_XRCHIRP 0x80cc /* Register Address */ #define AR5K_XRCHIRP_SEND 0x00000001 /* Send CHIRP */ #define AR5K_XRCHIRP_GAP 0xffff0000 /* Mask for CHIRP gap (?) */ /* * XR stomp register */ #define AR5K_XRSTOMP 0x80d0 /* Register Address */ #define AR5K_XRSTOMP_TX 0x00000001 /* Stomp Tx (?) */ #define AR5K_XRSTOMP_RX 0x00000002 /* Stomp Rx (?) */ #define AR5K_XRSTOMP_TX_RSSI 0x00000004 /* Stomp Tx RSSI (?) */ #define AR5K_XRSTOMP_TX_BSSID 0x00000008 /* Stomp Tx BSSID (?) */ #define AR5K_XRSTOMP_DATA 0x00000010 /* Stomp data (?)*/ #define AR5K_XRSTOMP_RSSI_THRES 0x0000ff00 /* Mask for XR RSSI threshold */ /* * First enhanced sleep register */ #define AR5K_SLEEP0 0x80d4 /* Register Address */ #define AR5K_SLEEP0_NEXT_DTIM 0x0007ffff /* Mask for next DTIM (?) */ #define AR5K_SLEEP0_NEXT_DTIM_S 0 #define AR5K_SLEEP0_ASSUME_DTIM 0x00080000 /* Assume DTIM */ #define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 /* Enable enhanced sleep control */ #define AR5K_SLEEP0_CABTO 0xff000000 /* Mask for CAB Time Out */ #define AR5K_SLEEP0_CABTO_S 24 /* * Second enhanced sleep register */ #define AR5K_SLEEP1 0x80d8 /* Register Address */ #define AR5K_SLEEP1_NEXT_TIM 0x0007ffff /* Mask for next TIM (?) */ #define AR5K_SLEEP1_NEXT_TIM_S 0 #define AR5K_SLEEP1_BEACON_TO 0xff000000 /* Mask for Beacon Time Out */ #define AR5K_SLEEP1_BEACON_TO_S 24 /* * Third enhanced sleep register */ #define AR5K_SLEEP2 0x80dc /* Register Address */ #define AR5K_SLEEP2_TIM_PER 0x0000ffff /* Mask for TIM period (?) */ #define AR5K_SLEEP2_TIM_PER_S 0 #define AR5K_SLEEP2_DTIM_PER 0xffff0000 /* Mask for DTIM period (?) */ #define AR5K_SLEEP2_DTIM_PER_S 16 /* * TX power control (TPC) register * * XXX: PCDAC steps (0.5dBm) or dBm ? * */ #define AR5K_TXPC 0x80e8 /* Register Address */ #define AR5K_TXPC_ACK_M 0x0000003f /* ACK tx power */ #define AR5K_TXPC_ACK_S 0 #define AR5K_TXPC_CTS_M 0x00003f00 /* CTS tx power */ #define AR5K_TXPC_CTS_S 8 #define AR5K_TXPC_CHIRP_M 0x003f0000 /* CHIRP tx power */ #define AR5K_TXPC_CHIRP_S 16 #define AR5K_TXPC_DOPPLER 0x0f000000 /* Doppler chirp span (?) */ #define AR5K_TXPC_DOPPLER_S 24 /* * Profile count registers * * These registers can be cleared and frozen with ATH5K_MIBC, but they do not * generate a MIB interrupt. * Instead of overflowing, they shift by one bit to the right. All registers * shift together, i.e. when one reaches the max, all shift at the same time by * one bit to the right. This way we should always get consistent values. */ #define AR5K_PROFCNT_TX 0x80ec /* Tx count */ #define AR5K_PROFCNT_RX 0x80f0 /* Rx count */ #define AR5K_PROFCNT_RXCLR 0x80f4 /* Busy count */ #define AR5K_PROFCNT_CYCLE 0x80f8 /* Cycle counter */ /* * Quiet period control registers */ #define AR5K_QUIET_CTL1 0x80fc /* Register Address */ #define AR5K_QUIET_CTL1_NEXT_QT_TSF 0x0000ffff /* Next quiet period TSF (TU) */ #define AR5K_QUIET_CTL1_NEXT_QT_TSF_S 0 #define AR5K_QUIET_CTL1_QT_EN 0x00010000 /* Enable quiet period */ #define AR5K_QUIET_CTL1_ACK_CTS_EN 0x00020000 /* Send ACK/CTS during quiet period */ #define AR5K_QUIET_CTL2 0x8100 /* Register Address */ #define AR5K_QUIET_CTL2_QT_PER 0x0000ffff /* Mask for quiet period periodicity */ #define AR5K_QUIET_CTL2_QT_PER_S 0 #define AR5K_QUIET_CTL2_QT_DUR 0xffff0000 /* Mask for quiet period duration */ #define AR5K_QUIET_CTL2_QT_DUR_S 16 /* * TSF parameter register */ #define AR5K_TSF_PARM 0x8104 /* Register Address */ #define AR5K_TSF_PARM_INC 0x000000ff /* Mask for TSF increment */ #define AR5K_TSF_PARM_INC_S 0 /* * QoS NOACK policy */ #define AR5K_QOS_NOACK 0x8108 /* Register Address */ #define AR5K_QOS_NOACK_2BIT_VALUES 0x0000000f /* ??? */ #define AR5K_QOS_NOACK_2BIT_VALUES_S 0 #define AR5K_QOS_NOACK_BIT_OFFSET 0x00000070 /* ??? */ #define AR5K_QOS_NOACK_BIT_OFFSET_S 4 #define AR5K_QOS_NOACK_BYTE_OFFSET 0x00000180 /* ??? */ #define AR5K_QOS_NOACK_BYTE_OFFSET_S 7 /* * PHY error filter register */ #define AR5K_PHY_ERR_FIL 0x810c #define AR5K_PHY_ERR_FIL_RADAR 0x00000020 /* Radar signal */ #define AR5K_PHY_ERR_FIL_OFDM 0x00020000 /* OFDM false detect (ANI) */ #define AR5K_PHY_ERR_FIL_CCK 0x02000000 /* CCK false detect (ANI) */ /* * XR latency register */ #define AR5K_XRLAT_TX 0x8110 /* * ACK SIFS register */ #define AR5K_ACKSIFS 0x8114 /* Register Address */ #define AR5K_ACKSIFS_INC 0x00000000 /* ACK SIFS Increment (field) */ /* * MIC QoS control register (?) */ #define AR5K_MIC_QOS_CTL 0x8118 /* Register Address */ #define AR5K_MIC_QOS_CTL_OFF(_n) (1 << (_n * 2)) #define AR5K_MIC_QOS_CTL_MQ_EN 0x00010000 /* Enable MIC QoS */ /* * MIC QoS select register (?) */ #define AR5K_MIC_QOS_SEL 0x811c #define AR5K_MIC_QOS_SEL_OFF(_n) (1 << (_n * 4)) /* * Misc mode control register (?) */ #define AR5K_MISC_MODE 0x8120 /* Register Address */ #define AR5K_MISC_MODE_FBSSID_MATCH 0x00000001 /* Force BSSID match */ #define AR5K_MISC_MODE_ACKSIFS_MEM 0x00000002 /* ACK SIFS memory (?) */ #define AR5K_MISC_MODE_COMBINED_MIC 0x00000004 /* use rx/tx MIC key */ /* more bits */ /* * OFDM Filter counter */ #define AR5K_OFDM_FIL_CNT 0x8124 /* * CCK Filter counter */ #define AR5K_CCK_FIL_CNT 0x8128 /* * PHY Error Counters (same masks as AR5K_PHY_ERR_FIL) */ #define AR5K_PHYERR_CNT1 0x812c #define AR5K_PHYERR_CNT1_MASK 0x8130 #define AR5K_PHYERR_CNT2 0x8134 #define AR5K_PHYERR_CNT2_MASK 0x8138 /* if the PHY Error Counters reach this maximum, we get MIB interrupts */ #define ATH5K_PHYERR_CNT_MAX 0x00c00000 /* * TSF Threshold register (?) */ #define AR5K_TSF_THRES 0x813c /* * TODO: Wake On Wireless registers * Range: 0x8147 - 0x818c */ /* * Rate -> ACK SIFS mapping table (32 entries) */ #define AR5K_RATE_ACKSIFS_BASE 0x8680 /* Register Address */ #define AR5K_RATE_ACKSIFS(_n) (AR5K_RATE_ACKSIFS_BSE + ((_n) << 2)) #define AR5K_RATE_ACKSIFS_NORMAL 0x00000001 /* Normal SIFS (field) */ #define AR5K_RATE_ACKSIFS_TURBO 0x00000400 /* Turbo SIFS (field) */ /* * Rate -> duration mapping table (32 entries) */ #define AR5K_RATE_DUR_BASE 0x8700 #define AR5K_RATE_DUR(_n) (AR5K_RATE_DUR_BASE + ((_n) << 2)) /* * Rate -> db mapping table * (8 entries, each one has 4 8bit fields) */ #define AR5K_RATE2DB_BASE 0x87c0 #define AR5K_RATE2DB(_n) (AR5K_RATE2DB_BASE + ((_n) << 2)) /* * db -> Rate mapping table * (8 entries, each one has 4 8bit fields) */ #define AR5K_DB2RATE_BASE 0x87e0 #define AR5K_DB2RATE(_n) (AR5K_DB2RATE_BASE + ((_n) << 2)) /*===5212 end===*/ #define AR5K_KEYTABLE_SIZE_5210 64 #define AR5K_KEYTABLE_SIZE_5211 128 /*===PHY REGISTERS===*/ /* * PHY registers start */ #define AR5K_PHY_BASE 0x9800 #define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2)) /* * TST_2 (Misc config parameters) */ #define AR5K_PHY_TST2 0x9800 /* Register Address */ #define AR5K_PHY_TST2_TRIG_SEL 0x00000007 /* Trigger select (?)*/ #define AR5K_PHY_TST2_TRIG 0x00000010 /* Trigger (?) */ #define AR5K_PHY_TST2_CBUS_MODE 0x00000060 /* Cardbus mode (?) */ #define AR5K_PHY_TST2_CLK32 0x00000400 /* CLK_OUT is CLK32 (32kHz external) */ #define AR5K_PHY_TST2_CHANCOR_DUMP_EN 0x00000800 /* Enable Chancor dump (?) */ #define AR5K_PHY_TST2_EVEN_CHANCOR_DUMP 0x00001000 /* Even Chancor dump (?) */ #define AR5K_PHY_TST2_RFSILENT_EN 0x00002000 /* Enable RFSILENT */ #define AR5K_PHY_TST2_ALT_RFDATA 0x00004000 /* Alternate RFDATA (5-2GHz switch ?) */ #define AR5K_PHY_TST2_MINI_OBS_EN 0x00008000 /* Enable mini OBS (?) */ #define AR5K_PHY_TST2_RX2_IS_RX5_INV 0x00010000 /* 2GHz rx path is the 5GHz path inverted (?) */ #define AR5K_PHY_TST2_SLOW_CLK160 0x00020000 /* Slow CLK160 (?) */ #define AR5K_PHY_TST2_AGC_OBS_SEL_3 0x00040000 /* AGC OBS Select 3 (?) */ #define AR5K_PHY_TST2_BBB_OBS_SEL 0x00080000 /* BB OBS Select (field ?) */ #define AR5K_PHY_TST2_ADC_OBS_SEL 0x00800000 /* ADC OBS Select (field ?) */ #define AR5K_PHY_TST2_RX_CLR_SEL 0x08000000 /* RX Clear Select (?) */ #define AR5K_PHY_TST2_FORCE_AGC_CLR 0x10000000 /* Force AGC clear (?) */ #define AR5K_PHY_SHIFT_2GHZ 0x00004007 /* Used to access 2GHz radios */ #define AR5K_PHY_SHIFT_5GHZ 0x00000007 /* Used to access 5GHz radios (default) */ /* * PHY frame control register [5110] /turbo mode register [5111+] * * There is another frame control register for [5111+] * at address 0x9944 (see below) but the 2 first flags * are common here between 5110 frame control register * and [5111+] turbo mode register, so this also works as * a "turbo mode register" for 5110. We treat this one as * a frame control register for 5110 below. */ #define AR5K_PHY_TURBO 0x9804 /* Register Address */ #define AR5K_PHY_TURBO_MODE 0x00000001 /* Enable turbo mode */ #define AR5K_PHY_TURBO_SHORT 0x00000002 /* Set short symbols to turbo mode */ #define AR5K_PHY_TURBO_MIMO 0x00000004 /* Set turbo for mimo */ /* * PHY agility command register * (aka TST_1) */ #define AR5K_PHY_AGC 0x9808 /* Register Address */ #define AR5K_PHY_TST1 0x9808 #define AR5K_PHY_AGC_DISABLE 0x08000000 /* Disable AGC to A2 (?)*/ #define AR5K_PHY_TST1_TXHOLD 0x00003800 /* Set tx hold (?) */ #define AR5K_PHY_TST1_TXSRC_SRC 0x00000002 /* Used with bit 7 (?) */ #define AR5K_PHY_TST1_TXSRC_SRC_S 1 #define AR5K_PHY_TST1_TXSRC_ALT 0x00000080 /* Set input to tsdac (?) */ #define AR5K_PHY_TST1_TXSRC_ALT_S 7 /* * PHY timing register 3 [5112+] */ #define AR5K_PHY_TIMING_3 0x9814 #define AR5K_PHY_TIMING_3_DSC_MAN 0xfffe0000 #define AR5K_PHY_TIMING_3_DSC_MAN_S 17 #define AR5K_PHY_TIMING_3_DSC_EXP 0x0001e000 #define AR5K_PHY_TIMING_3_DSC_EXP_S 13 /* * PHY chip revision register */ #define AR5K_PHY_CHIP_ID 0x9818 /* * PHY activation register */ #define AR5K_PHY_ACT 0x981c /* Register Address */ #define AR5K_PHY_ACT_ENABLE 0x00000001 /* Activate PHY */ #define AR5K_PHY_ACT_DISABLE 0x00000002 /* Deactivate PHY */ /* * PHY RF control registers */ #define AR5K_PHY_RF_CTL2 0x9824 /* Register Address */ #define AR5K_PHY_RF_CTL2_TXF2TXD_START 0x0000000f /* TX frame to TX data start */ #define AR5K_PHY_RF_CTL2_TXF2TXD_START_S 0 #define AR5K_PHY_RF_CTL3 0x9828 /* Register Address */ #define AR5K_PHY_RF_CTL3_TXE2XLNA_ON 0x0000ff00 /* TX end to XLNA on */ #define AR5K_PHY_RF_CTL3_TXE2XLNA_ON_S 8 #define AR5K_PHY_ADC_CTL 0x982c #define AR5K_PHY_ADC_CTL_INBUFGAIN_OFF 0x00000003 #define AR5K_PHY_ADC_CTL_INBUFGAIN_OFF_S 0 #define AR5K_PHY_ADC_CTL_PWD_DAC_OFF 0x00002000 #define AR5K_PHY_ADC_CTL_PWD_BAND_GAP_OFF 0x00004000 #define AR5K_PHY_ADC_CTL_PWD_ADC_OFF 0x00008000 #define AR5K_PHY_ADC_CTL_INBUFGAIN_ON 0x00030000 #define AR5K_PHY_ADC_CTL_INBUFGAIN_ON_S 16 #define AR5K_PHY_RF_CTL4 0x9834 /* Register Address */ #define AR5K_PHY_RF_CTL4_TXF2XPA_A_ON 0x00000001 /* TX frame to XPA A on (field) */ #define AR5K_PHY_RF_CTL4_TXF2XPA_B_ON 0x00000100 /* TX frame to XPA B on (field) */ #define AR5K_PHY_RF_CTL4_TXE2XPA_A_OFF 0x00010000 /* TX end to XPA A off (field) */ #define AR5K_PHY_RF_CTL4_TXE2XPA_B_OFF 0x01000000 /* TX end to XPA B off (field) */ /* * Pre-Amplifier control register * (XPA -> external pre-amplifier) */ #define AR5K_PHY_PA_CTL 0x9838 /* Register Address */ #define AR5K_PHY_PA_CTL_XPA_A_HI 0x00000001 /* XPA A high (?) */ #define AR5K_PHY_PA_CTL_XPA_B_HI 0x00000002 /* XPA B high (?) */ #define AR5K_PHY_PA_CTL_XPA_A_EN 0x00000004 /* Enable XPA A */ #define AR5K_PHY_PA_CTL_XPA_B_EN 0x00000008 /* Enable XPA B */ /* * PHY settling register */ #define AR5K_PHY_SETTLING 0x9844 /* Register Address */ #define AR5K_PHY_SETTLING_AGC 0x0000007f /* AGC settling time */ #define AR5K_PHY_SETTLING_AGC_S 0 #define AR5K_PHY_SETTLING_SWITCH 0x00003f80 /* Switch settling time */ #define AR5K_PHY_SETTLING_SWITCH_S 7 /* * PHY Gain registers */ #define AR5K_PHY_GAIN 0x9848 /* Register Address */ #define AR5K_PHY_GAIN_TXRX_ATTEN 0x0003f000 /* TX-RX Attenuation */ #define AR5K_PHY_GAIN_TXRX_ATTEN_S 12 #define AR5K_PHY_GAIN_TXRX_RF_MAX 0x007c0000 #define AR5K_PHY_GAIN_TXRX_RF_MAX_S 18 #define AR5K_PHY_GAIN_OFFSET 0x984c /* Register Address */ #define AR5K_PHY_GAIN_OFFSET_RXTX_FLAG 0x00020000 /* RX-TX flag (?) */ /* * Desired ADC/PGA size register * (for more infos read ANI patent) */ #define AR5K_PHY_DESIRED_SIZE 0x9850 /* Register Address */ #define AR5K_PHY_DESIRED_SIZE_ADC 0x000000ff /* ADC desired size */ #define AR5K_PHY_DESIRED_SIZE_ADC_S 0 #define AR5K_PHY_DESIRED_SIZE_PGA 0x0000ff00 /* PGA desired size */ #define AR5K_PHY_DESIRED_SIZE_PGA_S 8 #define AR5K_PHY_DESIRED_SIZE_TOT 0x0ff00000 /* Total desired size */ #define AR5K_PHY_DESIRED_SIZE_TOT_S 20 /* * PHY signal register * (for more infos read ANI patent) */ #define AR5K_PHY_SIG 0x9858 /* Register Address */ #define AR5K_PHY_SIG_FIRSTEP 0x0003f000 /* FIRSTEP */ #define AR5K_PHY_SIG_FIRSTEP_S 12 #define AR5K_PHY_SIG_FIRPWR 0x03fc0000 /* FIPWR */ #define AR5K_PHY_SIG_FIRPWR_S 18 /* * PHY coarse agility control register * (for more infos read ANI patent) */ #define AR5K_PHY_AGCCOARSE 0x985c /* Register Address */ #define AR5K_PHY_AGCCOARSE_LO 0x00007f80 /* AGC Coarse low */ #define AR5K_PHY_AGCCOARSE_LO_S 7 #define AR5K_PHY_AGCCOARSE_HI 0x003f8000 /* AGC Coarse high */ #define AR5K_PHY_AGCCOARSE_HI_S 15 /* * PHY agility control register */ #define AR5K_PHY_AGCCTL 0x9860 /* Register address */ #define AR5K_PHY_AGCCTL_CAL 0x00000001 /* Enable PHY calibration */ #define AR5K_PHY_AGCCTL_NF 0x00000002 /* Enable Noise Floor calibration */ #define AR5K_PHY_AGCCTL_OFDM_DIV_DIS 0x00000008 /* Disable antenna diversity on OFDM modes */ #define AR5K_PHY_AGCCTL_NF_EN 0x00008000 /* Enable nf calibration to happen (?) */ #define AR5K_PHY_AGCTL_FLTR_CAL 0x00010000 /* Allow filter calibration (?) */ #define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automatically */ /* * PHY noise floor status register (CCA = Clear Channel Assessment) */ #define AR5K_PHY_NF 0x9864 /* Register address */ #define AR5K_PHY_NF_M 0x000001ff /* Noise floor, written to hardware in 1/2 dBm units */ #define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) #define AR5K_PHY_NF_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */ #define AR5K_PHY_NF_THRESH62_S 12 #define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* Minimum measured noise level, read from hardware in 1 dBm units */ #define AR5K_PHY_NF_MINCCA_PWR_S 19 /* * PHY ADC saturation register [5110] */ #define AR5K_PHY_ADCSAT 0x9868 #define AR5K_PHY_ADCSAT_ICNT 0x0001f800 #define AR5K_PHY_ADCSAT_ICNT_S 11 #define AR5K_PHY_ADCSAT_THR 0x000007e0 #define AR5K_PHY_ADCSAT_THR_S 5 /* * PHY Weak ofdm signal detection threshold registers (ANI) [5212+] */ /* High thresholds */ #define AR5K_PHY_WEAK_OFDM_HIGH_THR 0x9868 #define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT 0x0000001f #define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT_S 0 #define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1 0x00fe0000 #define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1_S 17 #define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2 0x7f000000 #define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_S 24 /* Low thresholds */ #define AR5K_PHY_WEAK_OFDM_LOW_THR 0x986c #define AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN 0x00000001 #define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT 0x00003f00 #define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT_S 8 #define AR5K_PHY_WEAK_OFDM_LOW_THR_M1 0x001fc000 #define AR5K_PHY_WEAK_OFDM_LOW_THR_M1_S 14 #define AR5K_PHY_WEAK_OFDM_LOW_THR_M2 0x0fe00000 #define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_S 21 /* * PHY sleep registers [5112+] */ #define AR5K_PHY_SCR 0x9870 #define AR5K_PHY_SLMT 0x9874 #define AR5K_PHY_SLMT_32MHZ 0x0000007f #define AR5K_PHY_SCAL 0x9878 #define AR5K_PHY_SCAL_32MHZ 0x0000000e #define AR5K_PHY_SCAL_32MHZ_5311 0x00000008 #define AR5K_PHY_SCAL_32MHZ_2417 0x0000000a #define AR5K_PHY_SCAL_32MHZ_HB63 0x00000032 /* * PHY PLL (Phase Locked Loop) control register */ #define AR5K_PHY_PLL 0x987c #define AR5K_PHY_PLL_20MHZ 0x00000013 /* For half rate (?) */ /* 40MHz -> 5GHz band */ #define AR5K_PHY_PLL_40MHZ_5211 0x00000018 #define AR5K_PHY_PLL_40MHZ_5212 0x000000aa #define AR5K_PHY_PLL_40MHZ_5413 0x00000004 #define AR5K_PHY_PLL_40MHZ (ah->ah_version == AR5K_AR5211 ? \ AR5K_PHY_PLL_40MHZ_5211 : AR5K_PHY_PLL_40MHZ_5212) /* 44MHz -> 2.4GHz band */ #define AR5K_PHY_PLL_44MHZ_5211 0x00000019 #define AR5K_PHY_PLL_44MHZ_5212 0x000000ab #define AR5K_PHY_PLL_44MHZ (ah->ah_version == AR5K_AR5211 ? \ AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212) #define AR5K_PHY_PLL_RF5111 0x00000000 #define AR5K_PHY_PLL_RF5112 0x00000040 #define AR5K_PHY_PLL_HALF_RATE 0x00000100 #define AR5K_PHY_PLL_QUARTER_RATE 0x00000200 /* * RF Buffer register * * It's obvious from the code that 0x989c is the buffer register but * for the other special registers that we write to after sending each * packet, i have no idea. So I'll name them BUFFER_CONTROL_X registers * for now. It's interesting that they are also used for some other operations. */ #define AR5K_RF_BUFFER 0x989c #define AR5K_RF_BUFFER_CONTROL_0 0x98c0 /* Channel on 5110 */ #define AR5K_RF_BUFFER_CONTROL_1 0x98c4 /* Bank 7 on 5112 */ #define AR5K_RF_BUFFER_CONTROL_2 0x98cc /* Bank 7 on 5111 */ #define AR5K_RF_BUFFER_CONTROL_3 0x98d0 /* Bank 2 on 5112 */ /* Channel set on 5111 */ /* Used to read radio revision*/ #define AR5K_RF_BUFFER_CONTROL_4 0x98d4 /* RF Stage register on 5110 */ /* Bank 0,1,2,6 on 5111 */ /* Bank 1 on 5112 */ /* Used during activation on 5111 */ #define AR5K_RF_BUFFER_CONTROL_5 0x98d8 /* Bank 3 on 5111 */ /* Used during activation on 5111 */ /* Channel on 5112 */ /* Bank 6 on 5112 */ #define AR5K_RF_BUFFER_CONTROL_6 0x98dc /* Bank 3 on 5112 */ /* * PHY RF stage register [5210] */ #define AR5K_PHY_RFSTG 0x98d4 #define AR5K_PHY_RFSTG_DISABLE 0x00000021 /* * BIN masks (?) */ #define AR5K_PHY_BIN_MASK_1 0x9900 #define AR5K_PHY_BIN_MASK_2 0x9904 #define AR5K_PHY_BIN_MASK_3 0x9908 #define AR5K_PHY_BIN_MASK_CTL 0x990c #define AR5K_PHY_BIN_MASK_CTL_MASK_4 0x00003fff #define AR5K_PHY_BIN_MASK_CTL_MASK_4_S 0 #define AR5K_PHY_BIN_MASK_CTL_RATE 0xff000000 #define AR5K_PHY_BIN_MASK_CTL_RATE_S 24 /* * PHY Antenna control register */ #define AR5K_PHY_ANT_CTL 0x9910 /* Register Address */ #define AR5K_PHY_ANT_CTL_TXRX_EN 0x00000001 /* Enable TX/RX (?) */ #define AR5K_PHY_ANT_CTL_SECTORED_ANT 0x00000004 /* Sectored Antenna */ #define AR5K_PHY_ANT_CTL_HITUNE5 0x00000008 /* Hitune5 (?) */ #define AR5K_PHY_ANT_CTL_SWTABLE_IDLE 0x000003f0 /* Switch table idle (?) */ #define AR5K_PHY_ANT_CTL_SWTABLE_IDLE_S 4 /* * PHY receiver delay register [5111+] */ #define AR5K_PHY_RX_DELAY 0x9914 /* Register Address */ #define AR5K_PHY_RX_DELAY_M 0x00003fff /* Mask for RX activate to receive delay (/100ns) */ /* * PHY max rx length register (?) [5111] */ #define AR5K_PHY_MAX_RX_LEN 0x991c /* * PHY timing register 4 * I(nphase)/Q(adrature) calibration register [5111+] */ #define AR5K_PHY_IQ 0x9920 /* Register Address */ #define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */ #define AR5K_PHY_IQ_CORR_Q_Q_COFF_S 0 #define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */ #define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5 #define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */ #define AR5K_PHY_IQ_CAL_NUM_LOG_MAX 0x0000f000 /* Mask for max number of samples in log scale */ #define AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S 12 #define AR5K_PHY_IQ_RUN 0x00010000 /* Run i/q calibration */ #define AR5K_PHY_IQ_USE_PT_DF 0x00020000 /* Use pilot track df (?) */ #define AR5K_PHY_IQ_EARLY_TRIG_THR 0x00200000 /* Early trigger threshold (?) (field) */ #define AR5K_PHY_IQ_PILOT_MASK_EN 0x10000000 /* Enable pilot mask (?) */ #define AR5K_PHY_IQ_CHAN_MASK_EN 0x20000000 /* Enable channel mask (?) */ #define AR5K_PHY_IQ_SPUR_FILT_EN 0x40000000 /* Enable spur filter */ #define AR5K_PHY_IQ_SPUR_RSSI_EN 0x80000000 /* Enable spur rssi */ /* * PHY timing register 5 * OFDM Self-correlator Cyclic RSSI threshold params * (Check out bb_cycpwr_thr1 on ANI patent) */ #define AR5K_PHY_OFDM_SELFCORR 0x9924 /* Register Address */ #define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_EN 0x00000001 /* Enable cyclic RSSI thr 1 */ #define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1 0x000000fe /* Mask for Cyclic RSSI threshold 1 */ #define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_S 1 #define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR3 0x00000100 /* Cyclic RSSI threshold 3 (field) (?) */ #define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR_EN 0x00008000 /* Enable 1A RSSI threshold (?) */ #define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR 0x00010000 /* 1A RSSI threshold (field) (?) */ #define AR5K_PHY_OFDM_SELFCORR_LSCTHR_HIRSSI 0x00800000 /* Long sc threshold hi rssi (?) */ /* * PHY-only warm reset register */ #define AR5K_PHY_WARM_RESET 0x9928 /* * PHY-only control register */ #define AR5K_PHY_CTL 0x992c /* Register Address */ #define AR5K_PHY_CTL_RX_DRAIN_RATE 0x00000001 /* RX drain rate (?) */ #define AR5K_PHY_CTL_LATE_TX_SIG_SYM 0x00000002 /* Late tx signal symbol (?) */ #define AR5K_PHY_CTL_GEN_SCRAMBLER 0x00000004 /* Generate scrambler */ #define AR5K_PHY_CTL_TX_ANT_SEL 0x00000008 /* TX antenna select */ #define AR5K_PHY_CTL_TX_ANT_STATIC 0x00000010 /* Static TX antenna */ #define AR5K_PHY_CTL_RX_ANT_SEL 0x00000020 /* RX antenna select */ #define AR5K_PHY_CTL_RX_ANT_STATIC 0x00000040 /* Static RX antenna */ #define AR5K_PHY_CTL_LOW_FREQ_SLE_EN 0x00000080 /* Enable low freq sleep */ /* * PHY PAPD probe register [5111+] */ #define AR5K_PHY_PAPD_PROBE 0x9930 #define AR5K_PHY_PAPD_PROBE_SH_HI_PAR 0x00000001 #define AR5K_PHY_PAPD_PROBE_PCDAC_BIAS 0x00000002 #define AR5K_PHY_PAPD_PROBE_COMP_GAIN 0x00000040 #define AR5K_PHY_PAPD_PROBE_TXPOWER 0x00007e00 #define AR5K_PHY_PAPD_PROBE_TXPOWER_S 9 #define AR5K_PHY_PAPD_PROBE_TX_NEXT 0x00008000 #define AR5K_PHY_PAPD_PROBE_PREDIST_EN 0x00010000 #define AR5K_PHY_PAPD_PROBE_TYPE 0x01800000 /* [5112+] */ #define AR5K_PHY_PAPD_PROBE_TYPE_S 23 #define AR5K_PHY_PAPD_PROBE_TYPE_OFDM 0 #define AR5K_PHY_PAPD_PROBE_TYPE_XR 1 #define AR5K_PHY_PAPD_PROBE_TYPE_CCK 2 #define AR5K_PHY_PAPD_PROBE_GAINF 0xfe000000 #define AR5K_PHY_PAPD_PROBE_GAINF_S 25 #define AR5K_PHY_PAPD_PROBE_INI_5111 0x00004883 /* [5212+] */ #define AR5K_PHY_PAPD_PROBE_INI_5112 0x00004882 /* [5212+] */ /* * PHY TX rate power registers [5112+] */ #define AR5K_PHY_TXPOWER_RATE1 0x9934 #define AR5K_PHY_TXPOWER_RATE2 0x9938 #define AR5K_PHY_TXPOWER_RATE_MAX 0x993c #define AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE 0x00000040 #define AR5K_PHY_TXPOWER_RATE3 0xa234 #define AR5K_PHY_TXPOWER_RATE4 0xa238 /* * PHY frame control register [5111+] */ #define AR5K_PHY_FRAME_CTL_5210 0x9804 #define AR5K_PHY_FRAME_CTL_5211 0x9944 #define AR5K_PHY_FRAME_CTL (ah->ah_version == AR5K_AR5210 ? \ AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211) /*---[5111+]---*/ #define AR5K_PHY_FRAME_CTL_WIN_LEN 0x00000003 /* Force window length (?) */ #define AR5K_PHY_FRAME_CTL_WIN_LEN_S 0 #define AR5K_PHY_FRAME_CTL_TX_CLIP 0x00000038 /* Mask for tx clip (?) */ #define AR5K_PHY_FRAME_CTL_TX_CLIP_S 3 #define AR5K_PHY_FRAME_CTL_PREP_CHINFO 0x00010000 /* Prepend chan info */ #define AR5K_PHY_FRAME_CTL_EMU 0x80000000 #define AR5K_PHY_FRAME_CTL_EMU_S 31 /*---[5110/5111]---*/ #define AR5K_PHY_FRAME_CTL_TIMING_ERR 0x01000000 /* PHY timing error */ #define AR5K_PHY_FRAME_CTL_PARITY_ERR 0x02000000 /* Parity error */ #define AR5K_PHY_FRAME_CTL_ILLRATE_ERR 0x04000000 /* Illegal rate */ #define AR5K_PHY_FRAME_CTL_ILLLEN_ERR 0x08000000 /* Illegal length */ #define AR5K_PHY_FRAME_CTL_SERVICE_ERR 0x20000000 #define AR5K_PHY_FRAME_CTL_TXURN_ERR 0x40000000 /* TX underrun */ #define AR5K_PHY_FRAME_CTL_INI \ (AR5K_PHY_FRAME_CTL_SERVICE_ERR | \ AR5K_PHY_FRAME_CTL_TXURN_ERR | \ AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \ AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \ AR5K_PHY_FRAME_CTL_PARITY_ERR | \ AR5K_PHY_FRAME_CTL_TIMING_ERR) /* * PHY Tx Power adjustment register [5212A+] */ #define AR5K_PHY_TX_PWR_ADJ 0x994c #define AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA 0x00000fc0 #define AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA_S 6 #define AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX 0x00fc0000 #define AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX_S 18 /* * PHY radar detection register [5111+] */ #define AR5K_PHY_RADAR 0x9954 #define AR5K_PHY_RADAR_ENABLE 0x00000001 #define AR5K_PHY_RADAR_DISABLE 0x00000000 #define AR5K_PHY_RADAR_INBANDTHR 0x0000003e /* Inband threshold 5-bits, units unknown {0..31} (? MHz ?) */ #define AR5K_PHY_RADAR_INBANDTHR_S 1 #define AR5K_PHY_RADAR_PRSSI_THR 0x00000fc0 /* Pulse RSSI/SNR threshold 6-bits, dBm range {0..63} in dBm units. */ #define AR5K_PHY_RADAR_PRSSI_THR_S 6 #define AR5K_PHY_RADAR_PHEIGHT_THR 0x0003f000 /* Pulse height threshold 6-bits, dBm range {0..63} in dBm units. */ #define AR5K_PHY_RADAR_PHEIGHT_THR_S 12 #define AR5K_PHY_RADAR_RSSI_THR 0x00fc0000 /* Radar RSSI/SNR threshold. 6-bits, dBm range {0..63} in dBm units. */ #define AR5K_PHY_RADAR_RSSI_THR_S 18 #define AR5K_PHY_RADAR_FIRPWR_THR 0x7f000000 /* Finite Impulse Response filter power out threshold. 7-bits, standard power range {0..127} in 1/2 dBm units. */ #define AR5K_PHY_RADAR_FIRPWR_THRS 24 /* * PHY antenna switch table registers */ #define AR5K_PHY_ANT_SWITCH_TABLE_0 0x9960 #define AR5K_PHY_ANT_SWITCH_TABLE_1 0x9964 /* * PHY Noise floor threshold */ #define AR5K_PHY_NFTHRES 0x9968 /* * Sigma Delta register (?) [5213] */ #define AR5K_PHY_SIGMA_DELTA 0x996C #define AR5K_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 #define AR5K_PHY_SIGMA_DELTA_ADC_SEL_S 0 #define AR5K_PHY_SIGMA_DELTA_FILT2 0x000000f8 #define AR5K_PHY_SIGMA_DELTA_FILT2_S 3 #define AR5K_PHY_SIGMA_DELTA_FILT1 0x00001f00 #define AR5K_PHY_SIGMA_DELTA_FILT1_S 8 #define AR5K_PHY_SIGMA_DELTA_ADC_CLIP 0x01ffe000 #define AR5K_PHY_SIGMA_DELTA_ADC_CLIP_S 13 /* * RF restart register [5112+] (?) */ #define AR5K_PHY_RESTART 0x9970 /* restart */ #define AR5K_PHY_RESTART_DIV_GC 0x001c0000 /* Fast diversity gc_limit (?) */ #define AR5K_PHY_RESTART_DIV_GC_S 18 /* * RF Bus access request register (for synth-only channel switching) */ #define AR5K_PHY_RFBUS_REQ 0x997C #define AR5K_PHY_RFBUS_REQ_REQUEST 0x00000001 /* * Spur mitigation masks (?) */ #define AR5K_PHY_TIMING_7 0x9980 #define AR5K_PHY_TIMING_8 0x9984 #define AR5K_PHY_TIMING_8_PILOT_MASK_2 0x000fffff #define AR5K_PHY_TIMING_8_PILOT_MASK_2_S 0 #define AR5K_PHY_BIN_MASK2_1 0x9988 #define AR5K_PHY_BIN_MASK2_2 0x998c #define AR5K_PHY_BIN_MASK2_3 0x9990 #define AR5K_PHY_BIN_MASK2_4 0x9994 #define AR5K_PHY_BIN_MASK2_4_MASK_4 0x00003fff #define AR5K_PHY_BIN_MASK2_4_MASK_4_S 0 #define AR5K_PHY_TIMING_9 0x9998 #define AR5K_PHY_TIMING_10 0x999c #define AR5K_PHY_TIMING_10_PILOT_MASK_2 0x000fffff #define AR5K_PHY_TIMING_10_PILOT_MASK_2_S 0 /* * Spur mitigation control */ #define AR5K_PHY_TIMING_11 0x99a0 /* Register address */ #define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE 0x000fffff /* Spur delta phase */ #define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE_S 0 #define AR5K_PHY_TIMING_11_SPUR_FREQ_SD 0x3ff00000 /* Freq sigma delta */ #define AR5K_PHY_TIMING_11_SPUR_FREQ_SD_S 20 #define AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC 0x40000000 /* Spur filter in AGC detector */ #define AR5K_PHY_TIMING_11_USE_SPUR_IN_SELFCOR 0x80000000 /* Spur filter in OFDM self correlator */ /* * Gain tables */ #define AR5K_BB_GAIN_BASE 0x9b00 /* BaseBand Amplifier Gain table base address */ #define AR5K_BB_GAIN(_n) (AR5K_BB_GAIN_BASE + ((_n) << 2)) #define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplifier Gain table base address */ #define AR5K_RF_GAIN(_n) (AR5K_RF_GAIN_BASE + ((_n) << 2)) /* * PHY timing IQ calibration result register [5111+] */ #define AR5K_PHY_IQRES_CAL_PWR_I 0x9c10 /* I (Inphase) power value */ #define AR5K_PHY_IQRES_CAL_PWR_Q 0x9c14 /* Q (Quadrature) power value */ #define AR5K_PHY_IQRES_CAL_CORR 0x9c18 /* I/Q Correlation */ /* * PHY current RSSI register [5111+] */ #define AR5K_PHY_CURRENT_RSSI 0x9c1c /* * PHY RF Bus grant register */ #define AR5K_PHY_RFBUS_GRANT 0x9c20 #define AR5K_PHY_RFBUS_GRANT_OK 0x00000001 /* * PHY ADC test register */ #define AR5K_PHY_ADC_TEST 0x9c24 #define AR5K_PHY_ADC_TEST_I 0x00000001 #define AR5K_PHY_ADC_TEST_Q 0x00000200 /* * PHY DAC test register */ #define AR5K_PHY_DAC_TEST 0x9c28 #define AR5K_PHY_DAC_TEST_I 0x00000001 #define AR5K_PHY_DAC_TEST_Q 0x00000200 /* * PHY PTAT register (?) */ #define AR5K_PHY_PTAT 0x9c2c /* * PHY Illegal TX rate register [5112+] */ #define AR5K_PHY_BAD_TX_RATE 0x9c30 /* * PHY SPUR Power register [5112+] */ #define AR5K_PHY_SPUR_PWR 0x9c34 /* Register Address */ #define AR5K_PHY_SPUR_PWR_I 0x00000001 /* SPUR Power estimate for I (field) */ #define AR5K_PHY_SPUR_PWR_Q 0x00000100 /* SPUR Power estimate for Q (field) */ #define AR5K_PHY_SPUR_PWR_FILT 0x00010000 /* Power with SPUR removed (field) */ /* * PHY Channel status register [5112+] (?) */ #define AR5K_PHY_CHAN_STATUS 0x9c38 #define AR5K_PHY_CHAN_STATUS_BT_ACT 0x00000001 #define AR5K_PHY_CHAN_STATUS_RX_CLR_RAW 0x00000002 #define AR5K_PHY_CHAN_STATUS_RX_CLR_MAC 0x00000004 #define AR5K_PHY_CHAN_STATUS_RX_CLR_PAP 0x00000008 /* * Heavy clip enable register */ #define AR5K_PHY_HEAVY_CLIP_ENABLE 0x99e0 /* * PHY clock sleep registers [5112+] */ #define AR5K_PHY_SCLOCK 0x99f0 #define AR5K_PHY_SCLOCK_32MHZ 0x0000000c #define AR5K_PHY_SDELAY 0x99f4 #define AR5K_PHY_SDELAY_32MHZ 0x000000ff #define AR5K_PHY_SPENDING 0x99f8 /* * PHY PAPD I (power?) table (?) * (92! entries) */ #define AR5K_PHY_PAPD_I_BASE 0xa000 #define AR5K_PHY_PAPD_I(_n) (AR5K_PHY_PAPD_I_BASE + ((_n) << 2)) /* * PHY PCDAC TX power table */ #define AR5K_PHY_PCDAC_TXPOWER_BASE 0xa180 #define AR5K_PHY_PCDAC_TXPOWER(_n) (AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2)) /* * PHY mode register [5111+] */ #define AR5K_PHY_MODE 0x0a200 /* Register Address */ #define AR5K_PHY_MODE_MOD 0x00000001 /* PHY Modulation bit */ #define AR5K_PHY_MODE_MOD_OFDM 0 #define AR5K_PHY_MODE_MOD_CCK 1 #define AR5K_PHY_MODE_FREQ 0x00000002 /* Freq mode bit */ #define AR5K_PHY_MODE_FREQ_5GHZ 0 #define AR5K_PHY_MODE_FREQ_2GHZ 2 #define AR5K_PHY_MODE_MOD_DYN 0x00000004 /* Enable Dynamic OFDM/CCK mode [5112+] */ #define AR5K_PHY_MODE_RAD 0x00000008 /* [5212+] */ #define AR5K_PHY_MODE_RAD_RF5111 0 #define AR5K_PHY_MODE_RAD_RF5112 8 #define AR5K_PHY_MODE_XR 0x00000010 /* Enable XR mode [5112+] */ #define AR5K_PHY_MODE_HALF_RATE 0x00000020 /* Enable Half rate (test) */ #define AR5K_PHY_MODE_QUARTER_RATE 0x00000040 /* Enable Quarter rat (test) */ /* * PHY CCK transmit control register [5111+ (?)] */ #define AR5K_PHY_CCKTXCTL 0xa204 #define AR5K_PHY_CCKTXCTL_WORLD 0x00000000 #define AR5K_PHY_CCKTXCTL_JAPAN 0x00000010 #define AR5K_PHY_CCKTXCTL_SCRAMBLER_DIS 0x00000001 #define AR5K_PHY_CCKTXCTK_DAC_SCALE 0x00000004 /* * PHY CCK Cross-correlator Barker RSSI threshold register [5212+] */ #define AR5K_PHY_CCK_CROSSCORR 0xa208 #define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR 0x0000003f #define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR_S 0 /* Same address is used for antenna diversity activation */ #define AR5K_PHY_FAST_ANT_DIV 0xa208 #define AR5K_PHY_FAST_ANT_DIV_EN 0x00002000 /* * PHY 2GHz gain register [5111+] */ #define AR5K_PHY_GAIN_2GHZ 0xa20c #define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX 0x00fc0000 #define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX_S 18 #define AR5K_PHY_GAIN_2GHZ_INI_5111 0x6480416c #define AR5K_PHY_CCK_RX_CTL_4 0xa21c #define AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT 0x01f80000 #define AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT_S 19 #define AR5K_PHY_DAG_CCK_CTL 0xa228 #define AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR 0x00000200 #define AR5K_PHY_DAG_CCK_CTL_RSSI_THR 0x0001fc00 #define AR5K_PHY_DAG_CCK_CTL_RSSI_THR_S 10 #define AR5K_PHY_FAST_ADC 0xa24c #define AR5K_PHY_BLUETOOTH 0xa254 /* * Transmit Power Control register * [2413+] */ #define AR5K_PHY_TPC_RG1 0xa258 #define AR5K_PHY_TPC_RG1_NUM_PD_GAIN 0x0000c000 #define AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S 14 #define AR5K_PHY_TPC_RG1_PDGAIN_1 0x00030000 #define AR5K_PHY_TPC_RG1_PDGAIN_1_S 16 #define AR5K_PHY_TPC_RG1_PDGAIN_2 0x000c0000 #define AR5K_PHY_TPC_RG1_PDGAIN_2_S 18 #define AR5K_PHY_TPC_RG1_PDGAIN_3 0x00300000 #define AR5K_PHY_TPC_RG1_PDGAIN_3_S 20 #define AR5K_PHY_TPC_RG5 0xa26C #define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP 0x0000000F #define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP_S 0 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1 0x000003F0 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1_S 4 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2 0x0000FC00 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2_S 10 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3 0x003F0000 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3_S 16 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4 0x0FC00000 #define AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4_S 22 /* * PHY PDADC Tx power table */ #define AR5K_PHY_PDADC_TXPOWER_BASE 0xa280 #define AR5K_PHY_PDADC_TXPOWER(_n) (AR5K_PHY_PDADC_TXPOWER_BASE + ((_n) << 2)) /* * Platform registers for WiSoC */ #define AR5K_AR5312_RESET 0xbc003020 #define AR5K_AR5312_RESET_BB0_COLD 0x00000004 #define AR5K_AR5312_RESET_BB1_COLD 0x00000200 #define AR5K_AR5312_RESET_WMAC0 0x00002000 #define AR5K_AR5312_RESET_BB0_WARM 0x00004000 #define AR5K_AR5312_RESET_WMAC1 0x00020000 #define AR5K_AR5312_RESET_BB1_WARM 0x00040000 #define AR5K_AR5312_ENABLE 0xbc003080 #define AR5K_AR5312_ENABLE_WLAN0 0x00000001 #define AR5K_AR5312_ENABLE_WLAN1 0x00000008 #define AR5K_AR2315_RESET 0xb1000004 #define AR5K_AR2315_RESET_WMAC 0x00000001 #define AR5K_AR2315_RESET_BB_WARM 0x00000002 #define AR5K_AR2315_AHB_ARB_CTL 0xb1000008 #define AR5K_AR2315_AHB_ARB_CTL_WLAN 0x00000002 #define AR5K_AR2315_BYTESWAP 0xb100000c #define AR5K_AR2315_BYTESWAP_WMAC 0x00000002 compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/pcu.c0000644000175000017500000006763512026211315023225 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * Copyright (c) 2007-2008 Matthew W. S. Bell * Copyright (c) 2007-2008 Luis Rodriguez * Copyright (c) 2007-2008 Pavel Roskin * Copyright (c) 2007-2008 Jiri Slaby * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /*********************************\ * Protocol Control Unit Functions * \*********************************/ #include #include "ath5k.h" #include "reg.h" #include "debug.h" /** * DOC: Protocol Control Unit (PCU) functions * * Protocol control unit is responsible to maintain various protocol * properties before a frame is send and after a frame is received to/from * baseband. To be more specific, PCU handles: * * - Buffering of RX and TX frames (after QCU/DCUs) * * - Encrypting and decrypting (using the built-in engine) * * - Generating ACKs, RTS/CTS frames * * - Maintaining TSF * * - FCS * * - Updating beacon data (with TSF etc) * * - Generating virtual CCA * * - RX/Multicast filtering * * - BSSID filtering * * - Various statistics * * -Different operating modes: AP, STA, IBSS * * Note: Most of these functions can be tweaked/bypassed so you can do * them on sw above for debugging or research. For more infos check out PCU * registers on reg.h. */ /** * DOC: ACK rates * * AR5212+ can use higher rates for ack transmission * based on current tx rate instead of the base rate. * It does this to better utilize channel usage. * There is a mapping between G rates (that cover both * CCK and OFDM) and ack rates that we use when setting * rate -> duration table. This mapping is hw-based so * don't change anything. * * To enable this functionality we must set * ah->ah_ack_bitrate_high to true else base rate is * used (1Mb for CCK, 6Mb for OFDM). */ static const unsigned int ack_rates_high[] = /* Tx -> ACK */ /* 1Mb -> 1Mb */ { 0, /* 2MB -> 2Mb */ 1, /* 5.5Mb -> 2Mb */ 1, /* 11Mb -> 2Mb */ 1, /* 6Mb -> 6Mb */ 4, /* 9Mb -> 6Mb */ 4, /* 12Mb -> 12Mb */ 6, /* 18Mb -> 12Mb */ 6, /* 24Mb -> 24Mb */ 8, /* 36Mb -> 24Mb */ 8, /* 48Mb -> 24Mb */ 8, /* 54Mb -> 24Mb */ 8 }; /*******************\ * Helper functions * \*******************/ /** * ath5k_hw_get_frame_duration() - Get tx time of a frame * @ah: The &struct ath5k_hw * @len: Frame's length in bytes * @rate: The @struct ieee80211_rate * @shortpre: Indicate short preample * * Calculate tx duration of a frame given it's rate and length * It extends ieee80211_generic_frame_duration for non standard * bwmodes. */ int ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band, int len, struct ieee80211_rate *rate, bool shortpre) { int sifs, preamble, plcp_bits, sym_time; int bitrate, bits, symbols, symbol_bits; int dur; /* Fallback */ if (!ah->ah_bwmode) { __le16 raw_dur = ieee80211_generic_frame_duration(ah->hw, NULL, band, len, rate); /* subtract difference between long and short preamble */ dur = le16_to_cpu(raw_dur); if (shortpre) dur -= 96; return dur; } bitrate = rate->bitrate; preamble = AR5K_INIT_OFDM_PREAMPLE_TIME; plcp_bits = AR5K_INIT_OFDM_PLCP_BITS; sym_time = AR5K_INIT_OFDM_SYMBOL_TIME; switch (ah->ah_bwmode) { case AR5K_BWMODE_40MHZ: sifs = AR5K_INIT_SIFS_TURBO; preamble = AR5K_INIT_OFDM_PREAMBLE_TIME_MIN; break; case AR5K_BWMODE_10MHZ: sifs = AR5K_INIT_SIFS_HALF_RATE; preamble *= 2; sym_time *= 2; break; case AR5K_BWMODE_5MHZ: sifs = AR5K_INIT_SIFS_QUARTER_RATE; preamble *= 4; sym_time *= 4; break; default: sifs = AR5K_INIT_SIFS_DEFAULT_BG; break; } bits = plcp_bits + (len << 3); /* Bit rate is in 100Kbits */ symbol_bits = bitrate * sym_time; symbols = DIV_ROUND_UP(bits * 10, symbol_bits); dur = sifs + preamble + (sym_time * symbols); return dur; } /** * ath5k_hw_get_default_slottime() - Get the default slot time for current mode * @ah: The &struct ath5k_hw */ unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah) { struct ieee80211_channel *channel = ah->ah_current_channel; unsigned int slot_time; switch (ah->ah_bwmode) { case AR5K_BWMODE_40MHZ: slot_time = AR5K_INIT_SLOT_TIME_TURBO; break; case AR5K_BWMODE_10MHZ: slot_time = AR5K_INIT_SLOT_TIME_HALF_RATE; break; case AR5K_BWMODE_5MHZ: slot_time = AR5K_INIT_SLOT_TIME_QUARTER_RATE; break; case AR5K_BWMODE_DEFAULT: default: slot_time = AR5K_INIT_SLOT_TIME_DEFAULT; if ((channel->hw_value == AR5K_MODE_11B) && !ah->ah_short_slot) slot_time = AR5K_INIT_SLOT_TIME_B; break; } return slot_time; } /** * ath5k_hw_get_default_sifs() - Get the default SIFS for current mode * @ah: The &struct ath5k_hw */ unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah) { struct ieee80211_channel *channel = ah->ah_current_channel; unsigned int sifs; switch (ah->ah_bwmode) { case AR5K_BWMODE_40MHZ: sifs = AR5K_INIT_SIFS_TURBO; break; case AR5K_BWMODE_10MHZ: sifs = AR5K_INIT_SIFS_HALF_RATE; break; case AR5K_BWMODE_5MHZ: sifs = AR5K_INIT_SIFS_QUARTER_RATE; break; case AR5K_BWMODE_DEFAULT: sifs = AR5K_INIT_SIFS_DEFAULT_BG; default: if (channel->band == IEEE80211_BAND_5GHZ) sifs = AR5K_INIT_SIFS_DEFAULT_A; break; } return sifs; } /** * ath5k_hw_update_mib_counters() - Update MIB counters (mac layer statistics) * @ah: The &struct ath5k_hw * * Reads MIB counters from PCU and updates sw statistics. Is called after a * MIB interrupt, because one of these counters might have reached their maximum * and triggered the MIB interrupt, to let us read and clear the counter. * * NOTE: Is called in interrupt context! */ void ath5k_hw_update_mib_counters(struct ath5k_hw *ah) { struct ath5k_statistics *stats = &ah->stats; /* Read-And-Clear */ stats->ack_fail += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL); stats->rts_fail += ath5k_hw_reg_read(ah, AR5K_RTS_FAIL); stats->rts_ok += ath5k_hw_reg_read(ah, AR5K_RTS_OK); stats->fcs_error += ath5k_hw_reg_read(ah, AR5K_FCS_FAIL); stats->beacons += ath5k_hw_reg_read(ah, AR5K_BEACON_CNT); } /******************\ * ACK/CTS Timeouts * \******************/ /** * ath5k_hw_write_rate_duration() - Fill rate code to duration table * @ah: The &struct ath5k_hw * * Write the rate code to duration table upon hw reset. This is a helper for * ath5k_hw_pcu_init(). It seems all this is doing is setting an ACK timeout on * the hardware, based on current mode, for each rate. The rates which are * capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, and 11Mbps) have * different rate code so we write their value twice (one for long preamble * and one for short). * * Note: Band doesn't matter here, if we set the values for OFDM it works * on both a and g modes. So all we have to do is set values for all g rates * that include all OFDM and CCK rates. * */ static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah) { struct ieee80211_rate *rate; unsigned int i; /* 802.11g covers both OFDM and CCK */ u8 band = IEEE80211_BAND_2GHZ; /* Write rate duration table */ for (i = 0; i < ah->sbands[band].n_bitrates; i++) { u32 reg; u16 tx_time; if (ah->ah_ack_bitrate_high) rate = &ah->sbands[band].bitrates[ack_rates_high[i]]; /* CCK -> 1Mb */ else if (i < 4) rate = &ah->sbands[band].bitrates[0]; /* OFDM -> 6Mb */ else rate = &ah->sbands[band].bitrates[4]; /* Set ACK timeout */ reg = AR5K_RATE_DUR(rate->hw_value); /* An ACK frame consists of 10 bytes. If you add the FCS, * which ieee80211_generic_frame_duration() adds, * its 14 bytes. Note we use the control rate and not the * actual rate for this rate. See mac80211 tx.c * ieee80211_duration() for a brief description of * what rate we should choose to TX ACKs. */ tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false); ath5k_hw_reg_write(ah, tx_time, reg); if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)) continue; tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, true); ath5k_hw_reg_write(ah, tx_time, reg + (AR5K_SET_SHORT_PREAMBLE << 2)); } } /** * ath5k_hw_set_ack_timeout() - Set ACK timeout on PCU * @ah: The &struct ath5k_hw * @timeout: Timeout in usec */ static int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) { if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK)) <= timeout) return -EINVAL; AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, ath5k_hw_htoclock(ah, timeout)); return 0; } /** * ath5k_hw_set_cts_timeout() - Set CTS timeout on PCU * @ah: The &struct ath5k_hw * @timeout: Timeout in usec */ static int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) { if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS)) <= timeout) return -EINVAL; AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, ath5k_hw_htoclock(ah, timeout)); return 0; } /*******************\ * RX filter Control * \*******************/ /** * ath5k_hw_set_lladdr() - Set station id * @ah: The &struct ath5k_hw * @mac: The card's mac address (array of octets) * * Set station id on hw using the provided mac address */ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) { struct ath_common *common = ath5k_hw_common(ah); u32 low_id, high_id; u32 pcu_reg; /* Set new station ID */ memcpy(common->macaddr, mac, ETH_ALEN); pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; low_id = get_unaligned_le32(mac); high_id = get_unaligned_le16(mac + 4); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); return 0; } /** * ath5k_hw_set_bssid() - Set current BSSID on hw * @ah: The &struct ath5k_hw * * Sets the current BSSID and BSSID mask we have from the * common struct into the hardware */ void ath5k_hw_set_bssid(struct ath5k_hw *ah) { struct ath_common *common = ath5k_hw_common(ah); u16 tim_offset = 0; /* * Set BSSID mask on 5212 */ if (ah->ah_version == AR5K_AR5212) ath_hw_setbssidmask(common); /* * Set BSSID */ ath5k_hw_reg_write(ah, get_unaligned_le32(common->curbssid), AR5K_BSS_ID0); ath5k_hw_reg_write(ah, get_unaligned_le16(common->curbssid + 4) | ((common->curaid & 0x3fff) << AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); if (common->curaid == 0) { ath5k_hw_disable_pspoll(ah); return; } AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM, tim_offset ? tim_offset + 4 : 0); ath5k_hw_enable_pspoll(ah, NULL, 0); } /** * ath5k_hw_set_bssid_mask() - Filter out bssids we listen * @ah: The &struct ath5k_hw * @mask: The BSSID mask to set (array of octets) * * BSSID masking is a method used by AR5212 and newer hardware to inform PCU * which bits of the interface's MAC address should be looked at when trying * to decide which packets to ACK. In station mode and AP mode with a single * BSS every bit matters since we lock to only one BSS. In AP mode with * multiple BSSes (virtual interfaces) not every bit matters because hw must * accept frames for all BSSes and so we tweak some bits of our mac address * in order to have multiple BSSes. * * For more information check out ../hw.c of the common ath module. */ void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) { struct ath_common *common = ath5k_hw_common(ah); /* Cache bssid mask so that we can restore it * on reset */ memcpy(common->bssidmask, mask, ETH_ALEN); if (ah->ah_version == AR5K_AR5212) ath_hw_setbssidmask(common); } /** * ath5k_hw_set_mcast_filter() - Set multicast filter * @ah: The &struct ath5k_hw * @filter0: Lower 32bits of muticast filter * @filter1: Higher 16bits of multicast filter */ void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) { ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0); ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1); } /** * ath5k_hw_get_rx_filter() - Get current rx filter * @ah: The &struct ath5k_hw * * Returns the RX filter by reading rx filter and * phy error filter registers. RX filter is used * to set the allowed frame types that PCU will accept * and pass to the driver. For a list of frame types * check out reg.h. */ u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah) { u32 data, filter = 0; filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER); /*Radar detection for 5212*/ if (ah->ah_version == AR5K_AR5212) { data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL); if (data & AR5K_PHY_ERR_FIL_RADAR) filter |= AR5K_RX_FILTER_RADARERR; if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK)) filter |= AR5K_RX_FILTER_PHYERR; } return filter; } /** * ath5k_hw_set_rx_filter() - Set rx filter * @ah: The &struct ath5k_hw * @filter: RX filter mask (see reg.h) * * Sets RX filter register and also handles PHY error filter * register on 5212 and newer chips so that we have proper PHY * error reporting. */ void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) { u32 data = 0; /* Set PHY error filter register on 5212*/ if (ah->ah_version == AR5K_AR5212) { if (filter & AR5K_RX_FILTER_RADARERR) data |= AR5K_PHY_ERR_FIL_RADAR; if (filter & AR5K_RX_FILTER_PHYERR) data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK; } /* * The AR5210 uses promiscuous mode to detect radar activity */ if (ah->ah_version == AR5K_AR5210 && (filter & AR5K_RX_FILTER_RADARERR)) { filter &= ~AR5K_RX_FILTER_RADARERR; filter |= AR5K_RX_FILTER_PROM; } /*Zero length DMA (phy error reporting) */ if (data) AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); else AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); /*Write RX Filter register*/ ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER); /*Write PHY error filter register on 5212*/ if (ah->ah_version == AR5K_AR5212) ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL); } /****************\ * Beacon control * \****************/ #define ATH5K_MAX_TSF_READ 10 /** * ath5k_hw_get_tsf64() - Get the full 64bit TSF * @ah: The &struct ath5k_hw * * Returns the current TSF */ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) { u32 tsf_lower, tsf_upper1, tsf_upper2; int i; unsigned long flags; /* This code is time critical - we don't want to be interrupted here */ local_irq_save(flags); /* * While reading TSF upper and then lower part, the clock is still * counting (or jumping in case of IBSS merge) so we might get * inconsistent values. To avoid this, we read the upper part again * and check it has not been changed. We make the hypothesis that a * maximum of 3 changes can happens in a row (we use 10 as a safe * value). * * Impact on performance is pretty small, since in most cases, only * 3 register reads are needed. */ tsf_upper1 = ath5k_hw_reg_read(ah, AR5K_TSF_U32); for (i = 0; i < ATH5K_MAX_TSF_READ; i++) { tsf_lower = ath5k_hw_reg_read(ah, AR5K_TSF_L32); tsf_upper2 = ath5k_hw_reg_read(ah, AR5K_TSF_U32); if (tsf_upper2 == tsf_upper1) break; tsf_upper1 = tsf_upper2; } local_irq_restore(flags); WARN_ON(i == ATH5K_MAX_TSF_READ); return ((u64)tsf_upper1 << 32) | tsf_lower; } #undef ATH5K_MAX_TSF_READ /** * ath5k_hw_set_tsf64() - Set a new 64bit TSF * @ah: The &struct ath5k_hw * @tsf64: The new 64bit TSF * * Sets the new TSF */ void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64) { ath5k_hw_reg_write(ah, tsf64 & 0xffffffff, AR5K_TSF_L32); ath5k_hw_reg_write(ah, (tsf64 >> 32) & 0xffffffff, AR5K_TSF_U32); } /** * ath5k_hw_reset_tsf() - Force a TSF reset * @ah: The &struct ath5k_hw * * Forces a TSF reset on PCU */ void ath5k_hw_reset_tsf(struct ath5k_hw *ah) { u32 val; val = ath5k_hw_reg_read(ah, AR5K_BEACON) | AR5K_BEACON_RESET_TSF; /* * Each write to the RESET_TSF bit toggles a hardware internal * signal to reset TSF, but if left high it will cause a TSF reset * on the next chip reset as well. Thus we always write the value * twice to clear the signal. */ ath5k_hw_reg_write(ah, val, AR5K_BEACON); ath5k_hw_reg_write(ah, val, AR5K_BEACON); } /** * ath5k_hw_init_beacon_timers() - Initialize beacon timers * @ah: The &struct ath5k_hw * @next_beacon: Next TBTT * @interval: Current beacon interval * * This function is used to initialize beacon timers based on current * operation mode and settings. */ void ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, u32 interval) { u32 timer1, timer2, timer3; /* * Set the additional timers by mode */ switch (ah->opmode) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_STATION: /* In STA mode timer1 is used as next wakeup * timer and timer2 as next CFP duration start * timer. Both in 1/8TUs. */ /* TODO: PCF handling */ if (ah->ah_version == AR5K_AR5210) { timer1 = 0xffffffff; timer2 = 0xffffffff; } else { timer1 = 0x0000ffff; timer2 = 0x0007ffff; } /* Mark associated AP as PCF incapable for now */ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF); break; case NL80211_IFTYPE_ADHOC: AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM); default: /* On non-STA modes timer1 is used as next DMA * beacon alert (DBA) timer and timer2 as next * software beacon alert. Both in 1/8TUs. */ timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3; timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3; break; } /* Timer3 marks the end of our ATIM window * a zero length window is not allowed because * we 'll get no beacons */ timer3 = next_beacon + 1; /* * Set the beacon register and enable all timers. */ /* When in AP or Mesh Point mode zero timer0 to start TSF */ if (ah->opmode == NL80211_IFTYPE_AP || ah->opmode == NL80211_IFTYPE_MESH_POINT) ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); /* Force a TSF reset if requested and enable beacons */ if (interval & AR5K_BEACON_RESET_TSF) ath5k_hw_reset_tsf(ah); ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | AR5K_BEACON_ENABLE), AR5K_BEACON); /* Flush any pending BMISS interrupts on ISR by * performing a clear-on-write operation on PISR * register for the BMISS bit (writing a bit on * ISR toggles a reset for that bit and leaves * the remaining bits intact) */ if (ah->ah_version == AR5K_AR5210) ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR); else ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR); /* TODO: Set enhanced sleep registers on AR5212 * based on vif->bss_conf params, until then * disable power save reporting.*/ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV); } /** * ath5k_check_timer_win() - Check if timer B is timer A + window * @a: timer a (before b) * @b: timer b (after a) * @window: difference between a and b * @intval: timers are increased by this interval * * This helper function checks if timer B is timer A + window and covers * cases where timer A or B might have already been updated or wrapped * around (Timers are 16 bit). * * Returns true if O.K. */ static inline bool ath5k_check_timer_win(int a, int b, int window, int intval) { /* * 1.) usually B should be A + window * 2.) A already updated, B not updated yet * 3.) A already updated and has wrapped around * 4.) B has wrapped around */ if ((b - a == window) || /* 1.) */ (a - b == intval - window) || /* 2.) */ ((a | 0x10000) - b == intval - window) || /* 3.) */ ((b | 0x10000) - a == window)) /* 4.) */ return true; /* O.K. */ return false; } /** * ath5k_hw_check_beacon_timers() - Check if the beacon timers are correct * @ah: The &struct ath5k_hw * @intval: beacon interval * * This is a workaround for IBSS mode * * The need for this function arises from the fact that we have 4 separate * HW timer registers (TIMER0 - TIMER3), which are closely related to the * next beacon target time (NBTT), and that the HW updates these timers * separately based on the current TSF value. The hardware increments each * timer by the beacon interval, when the local TSF converted to TU is equal * to the value stored in the timer. * * The reception of a beacon with the same BSSID can update the local HW TSF * at any time - this is something we can't avoid. If the TSF jumps to a * time which is later than the time stored in a timer, this timer will not * be updated until the TSF in TU wraps around at 16 bit (the size of the * timers) and reaches the time which is stored in the timer. * * The problem is that these timers are closely related to TIMER0 (NBTT) and * that they define a time "window". When the TSF jumps between two timers * (e.g. ATIM and NBTT), the one in the past will be left behind (not * updated), while the one in the future will be updated every beacon * interval. This causes the window to get larger, until the TSF wraps * around as described above and the timer which was left behind gets * updated again. But - because the beacon interval is usually not an exact * divisor of the size of the timers (16 bit), an unwanted "window" between * these timers has developed! * * This is especially important with the ATIM window, because during * the ATIM window only ATIM frames and no data frames are allowed to be * sent, which creates transmission pauses after each beacon. This symptom * has been described as "ramping ping" because ping times increase linearly * for some time and then drop down again. A wrong window on the DMA beacon * timer has the same effect, so we check for these two conditions. * * Returns true if O.K. */ bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval) { unsigned int nbtt, atim, dma; nbtt = ath5k_hw_reg_read(ah, AR5K_TIMER0); atim = ath5k_hw_reg_read(ah, AR5K_TIMER3); dma = ath5k_hw_reg_read(ah, AR5K_TIMER1) >> 3; /* NOTE: SWBA is different. Having a wrong window there does not * stop us from sending data and this condition is caught by * other means (SWBA interrupt) */ if (ath5k_check_timer_win(nbtt, atim, 1, intval) && ath5k_check_timer_win(dma, nbtt, AR5K_TUNE_DMA_BEACON_RESP, intval)) return true; /* O.K. */ return false; } /** * ath5k_hw_set_coverage_class() - Set IEEE 802.11 coverage class * @ah: The &struct ath5k_hw * @coverage_class: IEEE 802.11 coverage class number * * Sets IFS intervals and ACK/CTS timeouts for given coverage class. */ void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class) { /* As defined by IEEE 802.11-2007 17.3.8.6 */ int slot_time = ath5k_hw_get_default_slottime(ah) + 3 * coverage_class; int ack_timeout = ath5k_hw_get_default_sifs(ah) + slot_time; int cts_timeout = ack_timeout; ath5k_hw_set_ifs_intervals(ah, slot_time); ath5k_hw_set_ack_timeout(ah, ack_timeout); ath5k_hw_set_cts_timeout(ah, cts_timeout); ah->ah_coverage_class = coverage_class; } /***************************\ * Init/Start/Stop functions * \***************************/ /** * ath5k_hw_start_rx_pcu() - Start RX engine * @ah: The &struct ath5k_hw * * Starts RX engine on PCU so that hw can process RXed frames * (ACK etc). * * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma */ void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) { AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); } /** * at5k_hw_stop_rx_pcu() - Stop RX engine * @ah: The &struct ath5k_hw * * Stops RX engine on PCU */ void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah) { AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); } /** * ath5k_hw_set_opmode() - Set PCU operating mode * @ah: The &struct ath5k_hw * @op_mode: One of enum nl80211_iftype * * Configure PCU for the various operating modes (AP/STA etc) */ int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode) { struct ath_common *common = ath5k_hw_common(ah); u32 pcu_reg, beacon_reg, low_id, high_id; ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "mode %d\n", op_mode); /* Preserve rest settings */ pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0)); beacon_reg = 0; switch (op_mode) { case NL80211_IFTYPE_ADHOC: pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE; beacon_reg |= AR5K_BCR_ADHOC; if (ah->ah_version == AR5K_AR5210) pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; else AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE; beacon_reg |= AR5K_BCR_AP; if (ah->ah_version == AR5K_AR5210) pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; else AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); break; case NL80211_IFTYPE_STATION: pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_PWR_SV : 0); case NL80211_IFTYPE_MONITOR: pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_NO_PSPOLL : 0); break; default: return -EINVAL; } /* * Set PCU registers */ low_id = get_unaligned_le32(common->macaddr); high_id = get_unaligned_le16(common->macaddr + 4); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); /* * Set Beacon Control Register on 5210 */ if (ah->ah_version == AR5K_AR5210) ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); return 0; } /** * ath5k_hw_pcu_init() - Initialize PCU * @ah: The &struct ath5k_hw * @op_mode: One of enum nl80211_iftype * @mode: One of enum ath5k_driver_mode * * This function is used to initialize PCU by setting current * operation mode and various other settings. */ void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode) { /* Set bssid and bssid mask */ ath5k_hw_set_bssid(ah); /* Set PCU config */ ath5k_hw_set_opmode(ah, op_mode); /* Write rate duration table only on AR5212 and if * virtual interface has already been brought up * XXX: rethink this after new mode changes to * mac80211 are integrated */ if (ah->ah_version == AR5K_AR5212 && ah->nvifs) ath5k_hw_write_rate_duration(ah); /* Set RSSI/BRSSI thresholds * * Note: If we decide to set this value * dynamically, have in mind that when AR5K_RSSI_THR * register is read it might return 0x40 if we haven't * wrote anything to it plus BMISS RSSI threshold is zeroed. * So doing a save/restore procedure here isn't the right * choice. Instead store it on ath5k_hw */ ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES | AR5K_TUNE_BMISS_THRES << AR5K_RSSI_THR_BMISS_S), AR5K_RSSI_THR); /* MIC QoS support */ if (ah->ah_mac_srev >= AR5K_SREV_AR2413) { ath5k_hw_reg_write(ah, 0x000100aa, AR5K_MIC_QOS_CTL); ath5k_hw_reg_write(ah, 0x00003210, AR5K_MIC_QOS_SEL); } /* QoS NOACK Policy */ if (ah->ah_version == AR5K_AR5212) { ath5k_hw_reg_write(ah, AR5K_REG_SM(2, AR5K_QOS_NOACK_2BIT_VALUES) | AR5K_REG_SM(5, AR5K_QOS_NOACK_BIT_OFFSET) | AR5K_REG_SM(0, AR5K_QOS_NOACK_BYTE_OFFSET), AR5K_QOS_NOACK); } /* Restore slot time and ACK timeouts */ if (ah->ah_coverage_class > 0) ath5k_hw_set_coverage_class(ah, ah->ah_coverage_class); /* Set ACK bitrate mode (see ack_rates_high) */ if (ah->ah_version == AR5K_AR5212) { u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB; if (ah->ah_ack_bitrate_high) AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val); else AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val); } return; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/gpio.c0000644000175000017500000001265212026211315023361 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /****************\ GPIO Functions \****************/ #include "ath5k.h" #include "reg.h" #include "debug.h" /** * DOC: GPIO/LED functions * * Here we control the 6 bidirectional GPIO pins provided by the hw. * We can set a GPIO pin to be an input or an output pin on GPIO control * register and then read or set its status from GPIO data input/output * registers. * * We also control the two LED pins provided by the hw, LED_0 is our * "power" LED and LED_1 is our "network activity" LED but many scenarios * are available from hw. Vendors might also provide LEDs connected to the * GPIO pins, we handle them through the LED subsystem on led.c */ /** * ath5k_hw_set_ledstate() - Set led state * @ah: The &struct ath5k_hw * @state: One of AR5K_LED_* * * Used to set the LED blinking state. This only * works for the LED connected to the LED_0, LED_1 pins, * not the GPIO based. */ void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state) { u32 led; /*5210 has different led mode handling*/ u32 led_5210; /*Reset led status*/ if (ah->ah_version != AR5K_AR5210) AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LEDMODE | AR5K_PCICFG_LED); else AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED); /* * Some blinking values, define at your wish */ switch (state) { case AR5K_LED_SCAN: case AR5K_LED_AUTH: led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND; led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL; break; case AR5K_LED_INIT: led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE; led_5210 = AR5K_PCICFG_LED_PEND; break; case AR5K_LED_ASSOC: case AR5K_LED_RUN: led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC; led_5210 = AR5K_PCICFG_LED_ASSOC; break; default: led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE; led_5210 = AR5K_PCICFG_LED_PEND; break; } /*Write new status to the register*/ if (ah->ah_version != AR5K_AR5210) AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led); else AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210); } /** * ath5k_hw_set_gpio_input() - Set GPIO inputs * @ah: The &struct ath5k_hw * @gpio: GPIO pin to set as input */ int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio) { if (gpio >= AR5K_NUM_GPIO) return -EINVAL; ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR); return 0; } /** * ath5k_hw_set_gpio_output() - Set GPIO outputs * @ah: The &struct ath5k_hw * @gpio: The GPIO pin to set as output */ int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio) { if (gpio >= AR5K_NUM_GPIO) return -EINVAL; ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR); return 0; } /** * ath5k_hw_get_gpio() - Get GPIO state * @ah: The &struct ath5k_hw * @gpio: The GPIO pin to read */ u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio) { if (gpio >= AR5K_NUM_GPIO) return 0xffffffff; /* GPIO input magic */ return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) & 0x1; } /** * ath5k_hw_set_gpio() - Set GPIO state * @ah: The &struct ath5k_hw * @gpio: The GPIO pin to set * @val: Value to set (boolean) */ int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val) { u32 data; if (gpio >= AR5K_NUM_GPIO) return -EINVAL; /* GPIO output magic */ data = ath5k_hw_reg_read(ah, AR5K_GPIODO); data &= ~(1 << gpio); data |= (val & 1) << gpio; ath5k_hw_reg_write(ah, data, AR5K_GPIODO); return 0; } /** * ath5k_hw_set_gpio_intr() - Initialize the GPIO interrupt (RFKill switch) * @ah: The &struct ath5k_hw * @gpio: The GPIO pin to use * @interrupt_level: True to generate interrupt on active pin (high) * * This function is used to set up the GPIO interrupt for the hw RFKill switch. * That switch is connected to a GPIO pin and it's number is stored on EEPROM. * It can either open or close the circuit to indicate that we should disable * RF/Wireless to save power (we also get that from EEPROM). */ void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, u32 interrupt_level) { u32 data; if (gpio >= AR5K_NUM_GPIO) return; /* * Set the GPIO interrupt */ data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH | AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) | (AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA); ath5k_hw_reg_write(ah, interrupt_level ? data : (data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR); ah->ah_imr |= AR5K_IMR_GPIO; /* Enable GPIO interrupts */ AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO); } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/eeprom.h0000644000175000017500000005002412026211315023712 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE) */ #define AR5K_EEPROM_PCIE_OFFSET 0x02 /* Contains offset to PCI-E infos */ #define AR5K_EEPROM_PCIE_SERDES_SECTION 0x40 /* PCIE_OFFSET points here when * SERDES infos are present */ #define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */ #define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */ #define AR5K_EEPROM_IS_HB63 0x000b /* Talon detect */ #define AR5K_EEPROM_RFKILL 0x0f #define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c #define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2 #define AR5K_EEPROM_RFKILL_POLARITY 0x00000002 #define AR5K_EEPROM_RFKILL_POLARITY_S 1 #define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */ /* FLASH(EEPROM) Defines for AR531X chips */ #define AR5K_EEPROM_SIZE_LOWER 0x1b /* size info -- lower */ #define AR5K_EEPROM_SIZE_UPPER 0x1c /* size info -- upper */ #define AR5K_EEPROM_SIZE_UPPER_MASK 0xfff0 #define AR5K_EEPROM_SIZE_UPPER_SHIFT 4 #define AR5K_EEPROM_SIZE_ENDLOC_SHIFT 12 #define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */ #define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */ #define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE) #define AR5K_EEPROM_INFO_CKSUM 0xffff #define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n)) #define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */ #define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */ #define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2GHz (ar5211_rfregs) */ #define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */ #define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */ #define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */ #define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */ #define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */ #define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */ #define AR5K_EEPROM_VERSION_4_3 0x4003 /* power calibration changes */ #define AR5K_EEPROM_VERSION_4_4 0x4004 #define AR5K_EEPROM_VERSION_4_5 0x4005 #define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */ #define AR5K_EEPROM_VERSION_4_7 0x3007 /* 4007 ? */ #define AR5K_EEPROM_VERSION_4_9 0x4009 /* EAR futureproofing */ #define AR5K_EEPROM_VERSION_5_0 0x5000 /* Has 2413 PDADC calibration etc */ #define AR5K_EEPROM_VERSION_5_1 0x5001 /* Has capability values */ #define AR5K_EEPROM_VERSION_5_3 0x5003 /* Has spur mitigation tables */ #define AR5K_EEPROM_MODE_11A 0 #define AR5K_EEPROM_MODE_11B 1 #define AR5K_EEPROM_MODE_11G 2 #define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */ #define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1) #define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1) #define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1) #define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2GHz */ #define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for < 2W power consumption */ #define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7) /* Device type (1 Cardbus, 2 PCI, 3 MiniPCI, 4 AP) */ #define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */ #define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5GHz */ /* Newer EEPROMs are using a different offset */ #define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \ (((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0) #define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3) #define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((s8)(((_v) >> 8) & 0xff)) #define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((s8)((_v) & 0xff)) /* Misc values available since EEPROM 4.0 */ #define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4) #define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff) #define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1) #define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1) #define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3) #define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5) #define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff) #define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1) /* has 32KHz crystal for sleep mode */ #define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v) (((_v) >> 15) & 0x1) #define AR5K_EEPROM_MISC2 AR5K_EEPROM_INFO(6) #define AR5K_EEPROM_EEP_FILE_VERSION(_v) (((_v) >> 8) & 0xff) #define AR5K_EEPROM_EAR_FILE_VERSION(_v) ((_v) & 0xff) #define AR5K_EEPROM_MISC3 AR5K_EEPROM_INFO(7) #define AR5K_EEPROM_ART_BUILD_NUM(_v) (((_v) >> 10) & 0x3f) #define AR5K_EEPROM_EAR_FILE_ID(_v) ((_v) & 0xff) #define AR5K_EEPROM_MISC4 AR5K_EEPROM_INFO(8) #define AR5K_EEPROM_CAL_DATA_START(_v) (((_v) >> 4) & 0xfff) #define AR5K_EEPROM_MASK_R0(_v) (((_v) >> 2) & 0x3) /* modes supported by radio 0 (bit 1: G, bit 2: A) */ #define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3) /* modes supported by radio 1 (bit 1: G, bit 2: A) */ #define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9) #define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1) /* disable compression */ #define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1) /* disable AES */ #define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1) /* disable fast frames */ #define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1) /* disable bursting */ #define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf) /* max number of QCUs. defaults to 10 */ #define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1) /* enable heavy clipping */ #define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf) /* key cache size. defaults to 128 */ #define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10) #define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x7) /* MIMO chains disabled for TX bitmask */ #define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x7) /* MIMO chains disabled for RX bitmask */ #define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1) /* 5.47-5.7GHz supported */ #define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1) /* Japan UNII1 band (5.15-5.25GHz) on even channels (5180, 5200, 5220, 5240) supported */ #define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1) /* Japan UNII2 band (5.25-5.35GHz) supported */ #define AR5K_EEPROM_JAP_MID_EN (((_v) >> 9) & 0x1) /* Japan band from 5.47-5.7GHz supported */ #define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 10) & 0x1) /* Japan UNII2 band (5.15-5.25GHz) on odd channels (5170, 5190, 5210, 5230) supported */ #define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 11) & 0x1) /* Japan A mode enabled (using even channels) */ /* calibration settings */ #define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4) #define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2) #define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d) #define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */ #define AR5K_EEPROM_GROUPS_START(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* Start of Groups */ #define AR5K_EEPROM_GROUP1_OFFSET 0x0 #define AR5K_EEPROM_GROUP2_OFFSET 0x5 #define AR5K_EEPROM_GROUP3_OFFSET 0x37 #define AR5K_EEPROM_GROUP4_OFFSET 0x46 #define AR5K_EEPROM_GROUP5_OFFSET 0x55 #define AR5K_EEPROM_GROUP6_OFFSET 0x65 #define AR5K_EEPROM_GROUP7_OFFSET 0x69 #define AR5K_EEPROM_GROUP8_OFFSET 0x6f #define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ AR5K_EEPROM_GROUP5_OFFSET, 0x0000) #define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ AR5K_EEPROM_GROUP6_OFFSET, 0x0010) #define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \ AR5K_EEPROM_GROUP7_OFFSET, 0x0014) /* [3.1 - 3.3] */ #define AR5K_EEPROM_OBDB0_2GHZ 0x00ec #define AR5K_EEPROM_OBDB1_2GHZ 0x00ed #define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */ #define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */ #define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */ #define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */ #define AR5K_EEPROM_PROTECT_WR_32_63 0x0008 #define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */ #define AR5K_EEPROM_PROTECT_WR_64_127 0x0020 #define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */ #define AR5K_EEPROM_PROTECT_WR_128_191 0x0080 #define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */ #define AR5K_EEPROM_PROTECT_WR_192_207 0x0200 #define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */ #define AR5K_EEPROM_PROTECT_WR_208_223 0x0800 #define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */ #define AR5K_EEPROM_PROTECT_WR_224_239 0x2000 #define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */ #define AR5K_EEPROM_PROTECT_WR_240_255 0x8000 /* Some EEPROM defines */ #define AR5K_EEPROM_EEP_SCALE 100 #define AR5K_EEPROM_EEP_DELTA 10 #define AR5K_EEPROM_N_MODES 3 #define AR5K_EEPROM_N_5GHZ_CHAN 10 #define AR5K_EEPROM_N_5GHZ_RATE_CHAN 8 #define AR5K_EEPROM_N_2GHZ_CHAN 3 #define AR5K_EEPROM_N_2GHZ_CHAN_2413 4 #define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4 #define AR5K_EEPROM_MAX_CHAN 10 #define AR5K_EEPROM_N_PWR_POINTS_5111 11 #define AR5K_EEPROM_N_PCDAC 11 #define AR5K_EEPROM_N_PHASE_CAL 5 #define AR5K_EEPROM_N_TEST_FREQ 8 #define AR5K_EEPROM_N_EDGES 8 #define AR5K_EEPROM_N_INTERCEPTS 11 #define AR5K_EEPROM_FREQ_M(_v) AR5K_EEPROM_OFF(_v, 0x7f, 0xff) #define AR5K_EEPROM_PCDAC_M 0x3f #define AR5K_EEPROM_PCDAC_START 1 #define AR5K_EEPROM_PCDAC_STOP 63 #define AR5K_EEPROM_PCDAC_STEP 1 #define AR5K_EEPROM_NON_EDGE_M 0x40 #define AR5K_EEPROM_CHANNEL_POWER 8 #define AR5K_EEPROM_N_OBDB 4 #define AR5K_EEPROM_OBDB_DIS 0xffff #define AR5K_EEPROM_CHANNEL_DIS 0xff #define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10) #define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32) #define AR5K_EEPROM_MAX_CTLS 32 #define AR5K_EEPROM_N_PD_CURVES 4 #define AR5K_EEPROM_N_XPD0_POINTS 4 #define AR5K_EEPROM_N_XPD3_POINTS 3 #define AR5K_EEPROM_N_PD_GAINS 4 #define AR5K_EEPROM_N_PD_POINTS 5 #define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35 #define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55 #define AR5K_EEPROM_POWER_M 0x3f #define AR5K_EEPROM_POWER_MIN 0 #define AR5K_EEPROM_POWER_MAX 3150 #define AR5K_EEPROM_POWER_STEP 50 #define AR5K_EEPROM_POWER_TABLE_SIZE 64 #define AR5K_EEPROM_N_POWER_LOC_11B 4 #define AR5K_EEPROM_N_POWER_LOC_11G 6 #define AR5K_EEPROM_I_GAIN 10 #define AR5K_EEPROM_CCK_OFDM_DELTA 15 #define AR5K_EEPROM_N_IQ_CAL 2 /* 5GHz/2GHz */ enum ath5k_eeprom_freq_bands { AR5K_EEPROM_BAND_5GHZ = 0, AR5K_EEPROM_BAND_2GHZ = 1, AR5K_EEPROM_N_FREQ_BANDS, }; /* Spur chans per freq band */ #define AR5K_EEPROM_N_SPUR_CHANS 5 /* fbin value for chan 2464 x2 */ #define AR5K_EEPROM_5413_SPUR_CHAN_1 1640 /* fbin value for chan 2420 x2 */ #define AR5K_EEPROM_5413_SPUR_CHAN_2 1200 #define AR5K_EEPROM_SPUR_CHAN_MASK 0x3FFF #define AR5K_EEPROM_NO_SPUR 0x8000 #define AR5K_SPUR_CHAN_WIDTH 87 #define AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz 3125 #define AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz 6250 #define AR5K_EEPROM_READ(_o, _v) do { \ if (!ath5k_hw_nvram_read(ah, (_o), &(_v))) \ return -EIO; \ } while (0) #define AR5K_EEPROM_READ_HDR(_o, _v) \ AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v); \ enum ath5k_ant_table { AR5K_ANT_CTL = 0, /* Idle switch table settings */ AR5K_ANT_SWTABLE_A = 1, /* Switch table for antenna A */ AR5K_ANT_SWTABLE_B = 2, /* Switch table for antenna B */ AR5K_ANT_MAX, }; enum ath5k_ctl_mode { AR5K_CTL_11A = 0, AR5K_CTL_11B = 1, AR5K_CTL_11G = 2, AR5K_CTL_TURBO = 3, AR5K_CTL_TURBOG = 4, AR5K_CTL_2GHT20 = 5, AR5K_CTL_5GHT20 = 6, AR5K_CTL_2GHT40 = 7, AR5K_CTL_5GHT40 = 8, AR5K_CTL_MODE_M = 15, }; /* Per channel calibration data, used for power table setup */ struct ath5k_chan_pcal_info_rf5111 { /* Power levels in half dBm units * for one power curve. */ u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111]; /* PCDAC table steps * for the above values */ u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111]; /* Starting PCDAC step */ u8 pcdac_min; /* Final PCDAC step */ u8 pcdac_max; }; struct ath5k_chan_pcal_info_rf5112 { /* Power levels in quarter dBm units * for lower (0) and higher (3) * level curves in 0.25dB units */ s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS]; s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS]; /* PCDAC table steps * for the above values */ u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS]; u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS]; }; struct ath5k_chan_pcal_info_rf2413 { /* Starting pwr/pddac values */ s8 pwr_i[AR5K_EEPROM_N_PD_GAINS]; u8 pddac_i[AR5K_EEPROM_N_PD_GAINS]; /* (pwr,pddac) points * power levels in 0.5dB units */ s8 pwr[AR5K_EEPROM_N_PD_GAINS] [AR5K_EEPROM_N_PD_POINTS]; u8 pddac[AR5K_EEPROM_N_PD_GAINS] [AR5K_EEPROM_N_PD_POINTS]; }; enum ath5k_powertable_type { AR5K_PWRTABLE_PWR_TO_PCDAC = 0, AR5K_PWRTABLE_LINEAR_PCDAC = 1, AR5K_PWRTABLE_PWR_TO_PDADC = 2, }; struct ath5k_pdgain_info { u8 pd_points; u8 *pd_step; /* Power values are in * 0.25dB units */ s16 *pd_pwr; }; struct ath5k_chan_pcal_info { /* Frequency */ u16 freq; /* Tx power boundaries */ s16 max_pwr; s16 min_pwr; union { struct ath5k_chan_pcal_info_rf5111 rf5111_info; struct ath5k_chan_pcal_info_rf5112 rf5112_info; struct ath5k_chan_pcal_info_rf2413 rf2413_info; }; /* Raw values used by phy code * Curves are stored in order from lower * gain to higher gain (max txpower -> min txpower) */ struct ath5k_pdgain_info *pd_curves; }; /* Per rate calibration data for each mode, * used for rate power table setup. * Note: Values in 0.5dB units */ struct ath5k_rate_pcal_info { u16 freq; /* Frequency */ /* Power level for 6-24Mbit/s rates or * 1Mb rate */ u16 target_power_6to24; /* Power level for 36Mbit rate or * 2Mb rate */ u16 target_power_36; /* Power level for 48Mbit rate or * 5.5Mbit rate */ u16 target_power_48; /* Power level for 54Mbit rate or * 11Mbit rate */ u16 target_power_54; }; /* Power edges for conformance test limits */ struct ath5k_edge_power { u16 freq; u16 edge; /* in half dBm */ bool flag; }; /** * struct ath5k_eeprom_info - EEPROM calibration data * * @ee_regdomain: ath/regd.c takes care of COUNTRY_ERD and WORLDWIDE_ROAMING * flags * @ee_ant_gain: Antenna gain in 0.5dB steps signed [5211 only?] * @ee_cck_ofdm_gain_delta: difference in gainF to output the same power for * OFDM and CCK packets * @ee_cck_ofdm_power_delta: power difference between OFDM (6Mbps) and CCK * (11Mbps) rate in G mode. 0.1dB steps * @ee_scaled_cck_delta: for Japan Channel 14: 0.1dB resolution * * @ee_i_cal: Initial I coefficient to correct I/Q mismatch in the receive path * @ee_q_cal: Initial Q coefficient to correct I/Q mismatch in the receive path * @ee_fixed_bias: use ee_ob and ee_db settings or use automatic control * @ee_switch_settling: RX/TX Switch settling time * @ee_atn_tx_rx: Difference in attenuation between TX and RX in 1dB steps * @ee_ant_control: Antenna Control Settings * @ee_ob: Bias current for Output stage of PA * B/G mode: Index [0] is used for AR2112/5112, otherwise [1] * A mode: [0] 5.15-5.25 [1] 5.25-5.50 [2] 5.50-5.70 [3] 5.70-5.85 GHz * @ee_db: Bias current for Output stage of PA. see @ee_ob * @ee_tx_end2xlna_enable: Time difference from when BB finishes sending a frame * to when the external LNA is activated * @ee_tx_end2xpa_disable: Time difference from when BB finishes sending a frame * to when the external PA switch is deactivated * @ee_tx_frm2xpa_enable: Time difference from when MAC sends frame to when * external PA switch is activated * @ee_thr_62: Clear Channel Assessment (CCA) sensitivity * (IEEE802.11a section 17.3.10.5 ) * @ee_xlna_gain: Total gain of the LNA (information only) * @ee_xpd: Use external (1) or internal power detector * @ee_x_gain: Gain for external power detector output (differences in EEMAP * versions!) * @ee_i_gain: Initial gain value after reset * @ee_margin_tx_rx: Margin in dB when final attenuation stage should be used * * @ee_false_detect: Backoff in Sensitivity (dB) on channels with spur signals * @ee_noise_floor_thr: Noise floor threshold in 1dB steps * @ee_adc_desired_size: Desired amplitude for ADC, used by AGC; in 0.5 dB steps * @ee_pga_desired_size: Desired output of PGA (for BB gain) in 0.5 dB steps * @ee_pd_gain_overlap: PD ADC curves need to overlap in 0.5dB steps (ee_map>=2) */ struct ath5k_eeprom_info { /* Header information */ u16 ee_magic; u16 ee_protect; u16 ee_regdomain; u16 ee_version; u16 ee_header; u16 ee_ant_gain; u8 ee_rfkill_pin; bool ee_rfkill_pol; bool ee_is_hb63; bool ee_serdes; u16 ee_misc0; u16 ee_misc1; u16 ee_misc2; u16 ee_misc3; u16 ee_misc4; u16 ee_misc5; u16 ee_misc6; u16 ee_cck_ofdm_gain_delta; u16 ee_cck_ofdm_power_delta; u16 ee_scaled_cck_delta; /* RF Calibration settings (reset, rfregs) */ u16 ee_i_cal[AR5K_EEPROM_N_MODES]; u16 ee_q_cal[AR5K_EEPROM_N_MODES]; u16 ee_fixed_bias[AR5K_EEPROM_N_MODES]; u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES]; u16 ee_xr_power[AR5K_EEPROM_N_MODES]; u16 ee_switch_settling[AR5K_EEPROM_N_MODES]; u16 ee_atn_tx_rx[AR5K_EEPROM_N_MODES]; u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC]; u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; u16 ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES]; u16 ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES]; u16 ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES]; u16 ee_thr_62[AR5K_EEPROM_N_MODES]; u16 ee_xlna_gain[AR5K_EEPROM_N_MODES]; u16 ee_xpd[AR5K_EEPROM_N_MODES]; u16 ee_x_gain[AR5K_EEPROM_N_MODES]; u16 ee_i_gain[AR5K_EEPROM_N_MODES]; u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES]; u16 ee_switch_settling_turbo[AR5K_EEPROM_N_MODES]; u16 ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES]; u16 ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES]; /* Power calibration data */ u16 ee_false_detect[AR5K_EEPROM_N_MODES]; /* Number of pd gain curves per mode */ u8 ee_pd_gains[AR5K_EEPROM_N_MODES]; /* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */ u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS]; u8 ee_n_piers[AR5K_EEPROM_N_MODES]; struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN]; struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; /* Per rate target power levels */ u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES]; struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN]; struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX]; /* Conformance test limits (Unused) */ u8 ee_ctls; u8 ee_ctl[AR5K_EEPROM_MAX_CTLS]; struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS]; /* Noise Floor Calibration settings */ s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES]; s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES]; s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES]; s8 ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES]; s8 ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES]; s8 ee_pd_gain_overlap; /* Spur mitigation data (fbin values for spur channels) */ u16 ee_spur_chans[AR5K_EEPROM_N_SPUR_CHANS][AR5K_EEPROM_N_FREQ_BANDS]; /* Antenna raw switch tables */ u32 ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; }; int ath5k_eeprom_mode_from_channel(struct ieee80211_channel *channel); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/desc.h0000644000175000017500000004132012026211315023340 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * RX/TX descriptor structures */ /** * struct ath5k_hw_rx_ctl - Common hardware RX control descriptor * @rx_control_0: RX control word 0 * @rx_control_1: RX control word 1 */ struct ath5k_hw_rx_ctl { u32 rx_control_0; u32 rx_control_1; } __packed __aligned(4); /* RX control word 1 fields/flags */ #define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff /* data buffer length */ #define AR5K_DESC_RX_CTL1_INTREQ 0x00002000 /* RX interrupt request */ /** * struct ath5k_hw_rx_status - Common hardware RX status descriptor * @rx_status_0: RX status word 0 * @rx_status_1: RX status word 1 * * 5210, 5211 and 5212 differ only in the fields and flags defined below */ struct ath5k_hw_rx_status { u32 rx_status_0; u32 rx_status_1; } __packed __aligned(4); /* 5210/5211 */ /* RX status word 0 fields/flags */ #define AR5K_5210_RX_DESC_STATUS0_DATA_LEN 0x00000fff /* RX data length */ #define AR5K_5210_RX_DESC_STATUS0_MORE 0x00001000 /* more desc for this frame */ #define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5210 0x00004000 /* [5210] receive on ant 1 */ #define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000 /* reception rate */ #define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S 15 #define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000 /* rssi */ #define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19 #define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211 0x38000000 /* [5211] receive antenna */ #define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211_S 27 /* RX status word 1 fields/flags */ #define AR5K_5210_RX_DESC_STATUS1_DONE 0x00000001 /* descriptor complete */ #define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 /* reception success */ #define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004 /* CRC error */ #define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN_5210 0x00000008 /* [5210] FIFO overrun */ #define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010 /* decryption CRC failure */ #define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0 /* PHY error */ #define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5 #define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 /* key index valid */ #define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00 /* decryption key index */ #define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9 #define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000 /* 13 bit of TSF */ #define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15 #define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000 /* key cache miss */ /* 5212 */ /* RX status word 0 fields/flags */ #define AR5K_5212_RX_DESC_STATUS0_DATA_LEN 0x00000fff /* RX data length */ #define AR5K_5212_RX_DESC_STATUS0_MORE 0x00001000 /* more desc for this frame */ #define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000 /* decompression CRC error */ #define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000 /* reception rate */ #define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S 15 #define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000 /* rssi */ #define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20 #define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000 /* receive antenna */ #define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28 /* RX status word 1 fields/flags */ #define AR5K_5212_RX_DESC_STATUS1_DONE 0x00000001 /* descriptor complete */ #define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 /* frame reception success */ #define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR 0x00000004 /* CRC error */ #define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008 /* decryption CRC failure */ #define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR 0x00000010 /* PHY error */ #define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR 0x00000020 /* MIC decrypt error */ #define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 /* key index valid */ #define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00 /* decryption key index */ #define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S 9 #define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000 /* first 15bit of the TSF */ #define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16 #define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000 /* key cache miss */ #define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE 0x0000ff00 /* phy error code overlays key index and valid fields */ #define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE_S 8 /** * enum ath5k_phy_error_code - PHY Error codes * @AR5K_RX_PHY_ERROR_UNDERRUN: Transmit underrun, [5210] No error * @AR5K_RX_PHY_ERROR_TIMING: Timing error * @AR5K_RX_PHY_ERROR_PARITY: Illegal parity * @AR5K_RX_PHY_ERROR_RATE: Illegal rate * @AR5K_RX_PHY_ERROR_LENGTH: Illegal length * @AR5K_RX_PHY_ERROR_RADAR: Radar detect, [5210] 64 QAM rate * @AR5K_RX_PHY_ERROR_SERVICE: Illegal service * @AR5K_RX_PHY_ERROR_TOR: Transmit override receive * @AR5K_RX_PHY_ERROR_OFDM_TIMING: OFDM Timing error [5212+] * @AR5K_RX_PHY_ERROR_OFDM_SIGNAL_PARITY: OFDM Signal parity error [5212+] * @AR5K_RX_PHY_ERROR_OFDM_RATE_ILLEGAL: OFDM Illegal rate [5212+] * @AR5K_RX_PHY_ERROR_OFDM_LENGTH_ILLEGAL: OFDM Illegal length [5212+] * @AR5K_RX_PHY_ERROR_OFDM_POWER_DROP: OFDM Power drop [5212+] * @AR5K_RX_PHY_ERROR_OFDM_SERVICE: OFDM Service (?) [5212+] * @AR5K_RX_PHY_ERROR_OFDM_RESTART: OFDM Restart (?) [5212+] * @AR5K_RX_PHY_ERROR_CCK_TIMING: CCK Timing error [5212+] * @AR5K_RX_PHY_ERROR_CCK_HEADER_CRC: Header CRC error [5212+] * @AR5K_RX_PHY_ERROR_CCK_RATE_ILLEGAL: Illegal rate [5212+] * @AR5K_RX_PHY_ERROR_CCK_SERVICE: CCK Service (?) [5212+] * @AR5K_RX_PHY_ERROR_CCK_RESTART: CCK Restart (?) [5212+] */ enum ath5k_phy_error_code { AR5K_RX_PHY_ERROR_UNDERRUN = 0, AR5K_RX_PHY_ERROR_TIMING = 1, AR5K_RX_PHY_ERROR_PARITY = 2, AR5K_RX_PHY_ERROR_RATE = 3, AR5K_RX_PHY_ERROR_LENGTH = 4, AR5K_RX_PHY_ERROR_RADAR = 5, AR5K_RX_PHY_ERROR_SERVICE = 6, AR5K_RX_PHY_ERROR_TOR = 7, AR5K_RX_PHY_ERROR_OFDM_TIMING = 17, AR5K_RX_PHY_ERROR_OFDM_SIGNAL_PARITY = 18, AR5K_RX_PHY_ERROR_OFDM_RATE_ILLEGAL = 19, AR5K_RX_PHY_ERROR_OFDM_LENGTH_ILLEGAL = 20, AR5K_RX_PHY_ERROR_OFDM_POWER_DROP = 21, AR5K_RX_PHY_ERROR_OFDM_SERVICE = 22, AR5K_RX_PHY_ERROR_OFDM_RESTART = 23, AR5K_RX_PHY_ERROR_CCK_TIMING = 25, AR5K_RX_PHY_ERROR_CCK_HEADER_CRC = 26, AR5K_RX_PHY_ERROR_CCK_RATE_ILLEGAL = 27, AR5K_RX_PHY_ERROR_CCK_SERVICE = 30, AR5K_RX_PHY_ERROR_CCK_RESTART = 31, }; /** * struct ath5k_hw_2w_tx_ctl - 5210/5211 hardware 2-word TX control descriptor * @tx_control_0: TX control word 0 * @tx_control_1: TX control word 1 */ struct ath5k_hw_2w_tx_ctl { u32 tx_control_0; u32 tx_control_1; } __packed __aligned(4); /* TX control word 0 fields/flags */ #define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff /* frame length */ #define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210 0x0003f000 /* [5210] header length */ #define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210_S 12 #define AR5K_2W_TX_DESC_CTL0_XMIT_RATE 0x003c0000 /* tx rate */ #define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S 18 #define AR5K_2W_TX_DESC_CTL0_RTSENA 0x00400000 /* RTS/CTS enable */ #define AR5K_2W_TX_DESC_CTL0_LONG_PACKET_5210 0x00800000 /* [5210] long packet */ #define AR5K_2W_TX_DESC_CTL0_VEOL_5211 0x00800000 /* [5211] virtual end-of-list */ #define AR5K_2W_TX_DESC_CTL0_CLRDMASK 0x01000000 /* clear destination mask */ #define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 0x02000000 /* [5210] antenna selection */ #define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211 0x1e000000 /* [5211] antenna selection */ #define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT \ (ah->ah_version == AR5K_AR5210 ? \ AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 : \ AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211) #define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25 #define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210 0x1c000000 /* [5210] frame type */ #define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210_S 26 #define AR5K_2W_TX_DESC_CTL0_INTREQ 0x20000000 /* TX interrupt request */ #define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 /* key is valid */ /* TX control word 1 fields/flags */ #define AR5K_2W_TX_DESC_CTL1_BUF_LEN 0x00000fff /* data buffer length */ #define AR5K_2W_TX_DESC_CTL1_MORE 0x00001000 /* more desc for this frame */ #define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5210 0x0007e000 /* [5210] key table index */ #define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5211 0x000fe000 /* [5211] key table index */ #define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX \ (ah->ah_version == AR5K_AR5210 ? \ AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5210 : \ AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5211) #define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_S 13 #define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211 0x00700000 /* [5211] frame type */ #define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211_S 20 #define AR5K_2W_TX_DESC_CTL1_NOACK_5211 0x00800000 /* [5211] no ACK */ #define AR5K_2W_TX_DESC_CTL1_RTS_DURATION_5210 0xfff80000 /* [5210] lower 13 bit of duration */ /* Frame types */ #define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL 0 #define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM 1 #define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL 2 #define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 3 #define AR5K_AR5211_TX_DESC_FRAME_TYPE_BEACON 3 #define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 4 #define AR5K_AR5211_TX_DESC_FRAME_TYPE_PRESP 4 /** * struct ath5k_hw_4w_tx_ctl - 5212 hardware 4-word TX control descriptor * @tx_control_0: TX control word 0 * @tx_control_1: TX control word 1 * @tx_control_2: TX control word 2 * @tx_control_3: TX control word 3 */ struct ath5k_hw_4w_tx_ctl { u32 tx_control_0; u32 tx_control_1; u32 tx_control_2; u32 tx_control_3; } __packed __aligned(4); /* TX control word 0 fields/flags */ #define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff /* frame length */ #define AR5K_4W_TX_DESC_CTL0_XMIT_POWER 0x003f0000 /* transmit power */ #define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S 16 #define AR5K_4W_TX_DESC_CTL0_RTSENA 0x00400000 /* RTS/CTS enable */ #define AR5K_4W_TX_DESC_CTL0_VEOL 0x00800000 /* virtual end-of-list */ #define AR5K_4W_TX_DESC_CTL0_CLRDMASK 0x01000000 /* clear destination mask */ #define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT 0x1e000000 /* TX antenna selection */ #define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25 #define AR5K_4W_TX_DESC_CTL0_INTREQ 0x20000000 /* TX interrupt request */ #define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 /* destination index valid */ #define AR5K_4W_TX_DESC_CTL0_CTSENA 0x80000000 /* precede frame with CTS */ /* TX control word 1 fields/flags */ #define AR5K_4W_TX_DESC_CTL1_BUF_LEN 0x00000fff /* data buffer length */ #define AR5K_4W_TX_DESC_CTL1_MORE 0x00001000 /* more desc for this frame */ #define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX 0x000fe000 /* destination table index */ #define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX_S 13 #define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE 0x00f00000 /* frame type */ #define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S 20 #define AR5K_4W_TX_DESC_CTL1_NOACK 0x01000000 /* no ACK */ #define AR5K_4W_TX_DESC_CTL1_COMP_PROC 0x06000000 /* compression processing */ #define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S 25 #define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN 0x18000000 /* length of frame IV */ #define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S 27 #define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN 0x60000000 /* length of frame ICV */ #define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S 29 /* TX control word 2 fields/flags */ #define AR5K_4W_TX_DESC_CTL2_RTS_DURATION 0x00007fff /* RTS/CTS duration */ #define AR5K_4W_TX_DESC_CTL2_DURATION_UPD_EN 0x00008000 /* frame duration update */ #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0 0x000f0000 /* series 0 max attempts */ #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S 16 #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1 0x00f00000 /* series 1 max attempts */ #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S 20 #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2 0x0f000000 /* series 2 max attempts */ #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S 24 #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3 0xf0000000 /* series 3 max attempts */ #define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S 28 /* TX control word 3 fields/flags */ #define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0 0x0000001f /* series 0 tx rate */ #define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1 0x000003e0 /* series 1 tx rate */ #define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S 5 #define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2 0x00007c00 /* series 2 tx rate */ #define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S 10 #define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3 0x000f8000 /* series 3 tx rate */ #define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S 15 #define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000 /* RTS or CTS rate */ #define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20 /** * struct ath5k_hw_tx_status - Common TX status descriptor * @tx_status_0: TX status word 0 * @tx_status_1: TX status word 1 */ struct ath5k_hw_tx_status { u32 tx_status_0; u32 tx_status_1; } __packed __aligned(4); /* TX status word 0 fields/flags */ #define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK 0x00000001 /* TX success */ #define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES 0x00000002 /* excessive retries */ #define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN 0x00000004 /* FIFO underrun */ #define AR5K_DESC_TX_STATUS0_FILTERED 0x00000008 /* TX filter indication */ /* according to the HAL sources the spec has short/long retry counts reversed. * we have it reversed to the HAL sources as well, for 5210 and 5211. * For 5212 these fields are defined as RTS_FAIL_COUNT and DATA_FAIL_COUNT, * but used respectively as SHORT and LONG retry count in the code later. This * is consistent with the definitions here... TODO: check */ #define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT 0x000000f0 /* short retry count */ #define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S 4 #define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT 0x00000f00 /* long retry count */ #define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S 8 #define AR5K_DESC_TX_STATUS0_VIRTCOLL_CT_5211 0x0000f000 /* [5211+] virtual collision count */ #define AR5K_DESC_TX_STATUS0_VIRTCOLL_CT_5212_S 12 #define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP 0xffff0000 /* TX timestamp */ #define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S 16 /* TX status word 1 fields/flags */ #define AR5K_DESC_TX_STATUS1_DONE 0x00000001 /* descriptor complete */ #define AR5K_DESC_TX_STATUS1_SEQ_NUM 0x00001ffe /* TX sequence number */ #define AR5K_DESC_TX_STATUS1_SEQ_NUM_S 1 #define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH 0x001fe000 /* signal strength of ACK */ #define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S 13 #define AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212 0x00600000 /* [5212] final TX attempt series ix */ #define AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212_S 21 #define AR5K_DESC_TX_STATUS1_COMP_SUCCESS_5212 0x00800000 /* [5212] compression status */ #define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA_5212 0x01000000 /* [5212] transmit antenna */ /** * struct ath5k_hw_5210_tx_desc - 5210/5211 hardware TX descriptor * @tx_ctl: The &struct ath5k_hw_2w_tx_ctl * @tx_stat: The &struct ath5k_hw_tx_status */ struct ath5k_hw_5210_tx_desc { struct ath5k_hw_2w_tx_ctl tx_ctl; struct ath5k_hw_tx_status tx_stat; } __packed __aligned(4); /** * struct ath5k_hw_5212_tx_desc - 5212 hardware TX descriptor * @tx_ctl: The &struct ath5k_hw_4w_tx_ctl * @tx_stat: The &struct ath5k_hw_tx_status */ struct ath5k_hw_5212_tx_desc { struct ath5k_hw_4w_tx_ctl tx_ctl; struct ath5k_hw_tx_status tx_stat; } __packed __aligned(4); /** * struct ath5k_hw_all_rx_desc - Common hardware RX descriptor * @rx_ctl: The &struct ath5k_hw_rx_ctl * @rx_stat: The &struct ath5k_hw_rx_status */ struct ath5k_hw_all_rx_desc { struct ath5k_hw_rx_ctl rx_ctl; struct ath5k_hw_rx_status rx_stat; } __packed __aligned(4); /** * struct ath5k_desc - Atheros hardware DMA descriptor * @ds_link: Physical address of the next descriptor * @ds_data: Physical address of data buffer (skb) * @ud: Union containing hw_5xxx_tx_desc structs and hw_all_rx_desc * * This is read and written to by the hardware */ struct ath5k_desc { u32 ds_link; u32 ds_data; union { struct ath5k_hw_5210_tx_desc ds_tx5210; struct ath5k_hw_5212_tx_desc ds_tx5212; struct ath5k_hw_all_rx_desc ds_rx; } ud; } __packed __aligned(4); #define AR5K_RXDESC_INTREQ 0x0020 #define AR5K_TXDESC_CLRDMASK 0x0001 #define AR5K_TXDESC_NOACK 0x0002 /*[5211+]*/ #define AR5K_TXDESC_RTSENA 0x0004 #define AR5K_TXDESC_CTSENA 0x0008 #define AR5K_TXDESC_INTREQ 0x0010 #define AR5K_TXDESC_VEOL 0x0020 /*[5211+]*/ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/debug.h0000644000175000017500000001360112026211315023511 0ustar mcgrofmcgrof/* * Copyright (c) 2007 Bruno Randolf * * This file is free software: you may copy, redistribute and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 2 of the License, or (at your * option) any later version. * * This file 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * Copyright (c) 2004-2005 Atheros Communications, Inc. * Copyright (c) 2006 Devicescape Software, Inc. * Copyright (c) 2007 Jiri Slaby * Copyright (c) 2007 Luis R. Rodriguez * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef _ATH5K_DEBUG_H #define _ATH5K_DEBUG_H struct ath5k_hw; struct sk_buff; struct ath5k_buf; struct ath5k_dbg_info { unsigned int level; /* debug level */ }; /** * enum ath5k_debug_level - ath5k debug level * * @ATH5K_DEBUG_RESET: reset processing * @ATH5K_DEBUG_INTR: interrupt handling * @ATH5K_DEBUG_MODE: mode init/setup * @ATH5K_DEBUG_XMIT: basic xmit operation * @ATH5K_DEBUG_BEACON: beacon handling * @ATH5K_DEBUG_CALIBRATE: periodic calibration * @ATH5K_DEBUG_TXPOWER: transmit power setting * @ATH5K_DEBUG_LED: led management * @ATH5K_DEBUG_DUMP_RX: print received skb content * @ATH5K_DEBUG_DUMP_TX: print transmit skb content * @ATH5K_DEBUG_DUMPBANDS: dump bands * @ATH5K_DEBUG_DMA: debug dma start/stop * @ATH5K_DEBUG_TRACE: trace function calls * @ATH5K_DEBUG_DESC: descriptor setup * @ATH5K_DEBUG_ANY: show at any debug level * * The debug level is used to control the amount and type of debugging output * we want to see. The debug level is given in calls to ATH5K_DBG to specify * where the message should appear, and the user can control the debugging * messages he wants to see, either by the module parameter 'debug' on module * load, or dynamically by using debugfs 'ath5k/phyX/debug'. these levels can * be combined together by bitwise OR. */ enum ath5k_debug_level { ATH5K_DEBUG_RESET = 0x00000001, ATH5K_DEBUG_INTR = 0x00000002, ATH5K_DEBUG_MODE = 0x00000004, ATH5K_DEBUG_XMIT = 0x00000008, ATH5K_DEBUG_BEACON = 0x00000010, ATH5K_DEBUG_CALIBRATE = 0x00000020, ATH5K_DEBUG_TXPOWER = 0x00000040, ATH5K_DEBUG_LED = 0x00000080, ATH5K_DEBUG_DUMPBANDS = 0x00000400, ATH5K_DEBUG_DMA = 0x00000800, ATH5K_DEBUG_ANI = 0x00002000, ATH5K_DEBUG_DESC = 0x00004000, ATH5K_DEBUG_ANY = 0xffffffff }; #ifdef CONFIG_ATH5K_DEBUG #define ATH5K_DBG(_sc, _m, _fmt, ...) do { \ if (unlikely((_sc)->debug.level & (_m) && net_ratelimit())) \ ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \ __func__, __LINE__, ##__VA_ARGS__); \ } while (0) #define ATH5K_DBG_UNLIMIT(_sc, _m, _fmt, ...) do { \ if (unlikely((_sc)->debug.level & (_m))) \ ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \ __func__, __LINE__, ##__VA_ARGS__); \ } while (0) void ath5k_debug_init_device(struct ath5k_hw *ah); void ath5k_debug_printrxbuffs(struct ath5k_hw *ah); void ath5k_debug_dump_bands(struct ath5k_hw *ah); void ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf); #else /* no debugging */ #include static inline __printf(3, 4) void ATH5K_DBG(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...) {} static inline __printf(3, 4) void ATH5K_DBG_UNLIMIT(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...) {} static inline void ath5k_debug_init_device(struct ath5k_hw *ah) {} static inline void ath5k_debug_printrxbuffs(struct ath5k_hw *ah) {} static inline void ath5k_debug_dump_bands(struct ath5k_hw *ah) {} static inline void ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf) {} #endif /* ifdef CONFIG_ATH5K_DEBUG */ #endif /* ifndef _ATH5K_DEBUG_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/caps.c0000644000175000017500000001044612026211315023350 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2008 Reyk Floeter * Copyright (c) 2006-2008 Nick Kossifidis * Copyright (c) 2007-2008 Jiri Slaby * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /**************\ * Capabilities * \**************/ #include "ath5k.h" #include "reg.h" #include "debug.h" #include "../regd.h" /* * Fill the capabilities struct * TODO: Merge this with EEPROM code when we are done with it */ int ath5k_hw_set_capabilities(struct ath5k_hw *ah) { struct ath5k_capabilities *caps = &ah->ah_capabilities; u16 ee_header; /* Capabilities stored in the EEPROM */ ee_header = caps->cap_eeprom.ee_header; if (ah->ah_version == AR5K_AR5210) { /* * Set radio capabilities * (The AR5110 only supports the middle 5GHz band) */ caps->cap_range.range_5ghz_min = 5120; caps->cap_range.range_5ghz_max = 5430; caps->cap_range.range_2ghz_min = 0; caps->cap_range.range_2ghz_max = 0; /* Set supported modes */ __set_bit(AR5K_MODE_11A, caps->cap_mode); } else { /* * XXX The transceiver supports frequencies from 4920 to 6100MHz * XXX and from 2312 to 2732MHz. There are problems with the * XXX current ieee80211 implementation because the IEEE * XXX channel mapping does not support negative channel * XXX numbers (2312MHz is channel -19). Of course, this * XXX doesn't matter because these channels are out of the * XXX legal range. */ /* * Set radio capabilities */ if (AR5K_EEPROM_HDR_11A(ee_header)) { if (ath_is_49ghz_allowed(caps->cap_eeprom.ee_regdomain)) caps->cap_range.range_5ghz_min = 4920; else caps->cap_range.range_5ghz_min = 5005; caps->cap_range.range_5ghz_max = 6100; /* Set supported modes */ __set_bit(AR5K_MODE_11A, caps->cap_mode); } /* Enable 802.11b if a 2GHz capable radio (2111/5112) is * connected */ if (AR5K_EEPROM_HDR_11B(ee_header) || (AR5K_EEPROM_HDR_11G(ee_header) && ah->ah_version != AR5K_AR5211)) { /* 2312 */ caps->cap_range.range_2ghz_min = 2412; caps->cap_range.range_2ghz_max = 2732; /* Override 2GHz modes on SoCs that need it * NOTE: cap_needs_2GHz_ovr gets set from * ath_ahb_probe */ if (!caps->cap_needs_2GHz_ovr) { if (AR5K_EEPROM_HDR_11B(ee_header)) __set_bit(AR5K_MODE_11B, caps->cap_mode); if (AR5K_EEPROM_HDR_11G(ee_header) && ah->ah_version != AR5K_AR5211) __set_bit(AR5K_MODE_11G, caps->cap_mode); } } } if ((ah->ah_radio_5ghz_revision & 0xf0) == AR5K_SREV_RAD_2112) __clear_bit(AR5K_MODE_11A, caps->cap_mode); /* Set number of supported TX queues */ if (ah->ah_version == AR5K_AR5210) caps->cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES_NOQCU; else caps->cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES; /* Newer hardware has PHY error counters */ if (ah->ah_mac_srev >= AR5K_SREV_AR5213A) caps->cap_has_phyerr_counters = true; else caps->cap_has_phyerr_counters = false; /* MACs since AR5212 have MRR support */ if (ah->ah_version == AR5K_AR5212) caps->cap_has_mrr_support = true; else caps->cap_has_mrr_support = false; return 0; } /* * TODO: Following functions should be part of a new function * set_capability */ int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, u16 assoc_id) { if (ah->ah_version == AR5K_AR5210) { AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); return 0; } return -EIO; } int ath5k_hw_disable_pspoll(struct ath5k_hw *ah) { if (ah->ah_version == AR5K_AR5210) { AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); return 0; } return -EIO; } compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/base.h0000644000175000017500000001026112026211315023334 0ustar mcgrofmcgrof/*- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * */ /* * Definitions for the Atheros Wireless LAN controller driver. */ #ifndef _DEV_ATH5K_BASE_H #define _DEV_ATH5K_BASE_H struct ieee80211_vif; struct ieee80211_hw; struct ath5k_hw; struct ath5k_txq; struct ieee80211_channel; struct ath_bus_ops; enum nl80211_iftype; enum ath5k_srev_type { AR5K_VERSION_MAC, AR5K_VERSION_RAD, }; struct ath5k_srev_name { const char *sr_name; enum ath5k_srev_type sr_type; u_int sr_val; }; struct ath5k_buf { struct list_head list; struct ath5k_desc *desc; /* virtual addr of desc */ dma_addr_t daddr; /* physical addr of desc */ struct sk_buff *skb; /* skbuff for buf */ dma_addr_t skbaddr;/* physical addr of skb data */ }; struct ath5k_vif { bool assoc; /* are we associated or not */ enum nl80211_iftype opmode; int bslot; struct ath5k_buf *bbuf; /* beacon buffer */ }; struct ath5k_vif_iter_data { const u8 *hw_macaddr; u8 mask[ETH_ALEN]; u8 active_mac[ETH_ALEN]; /* first active MAC */ bool need_set_hw_addr; bool found_active; bool any_assoc; enum nl80211_iftype opmode; int n_stas; }; void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif); bool ath5k_any_vif_assoc(struct ath5k_hw *ah); int ath5k_start(struct ieee80211_hw *hw); void ath5k_stop(struct ieee80211_hw *hw); void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf); int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void ath5k_beacon_config(struct ath5k_hw *ah); void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable); void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah, struct ieee80211_vif *vif); int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan); void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath5k_txq *txq); const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val); int ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops); void ath5k_deinit_ah(struct ath5k_hw *ah); /* Check whether BSSID mask is supported */ #define ath5k_hw_hasbssidmask(_ah) (ah->ah_version == AR5K_AR5212) /* Check whether virtual EOL is supported */ #define ath5k_hw_hasveol(_ah) (ah->ah_version != AR5K_AR5210) #endif /* _DEV_ATH5K_BASE_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/ath5k.h0000644000175000017500000015222412026211315023444 0ustar mcgrofmcgrof/* * Copyright (c) 2004-2007 Reyk Floeter * Copyright (c) 2006-2007 Nick Kossifidis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _ATH5K_H #define _ATH5K_H /* TODO: Clean up channel debugging (doesn't work anyway) and start * working on reg. control code using all available eeprom information * (rev. engineering needed) */ #define CHAN_DEBUG 0 #include #include #include #include #include #include /* RX/TX descriptor hw structs * TODO: Driver part should only see sw structs */ #include "desc.h" /* EEPROM structs/offsets * TODO: Make a more generic struct (eg. add more stuff to ath5k_capabilities) * and clean up common bits, then introduce set/get functions in eeprom.c */ #include "eeprom.h" #include "debug.h" #include "../ath.h" #include "ani.h" /* PCI IDs */ #define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */ #define PCI_DEVICE_ID_ATHEROS_AR5311 0x0011 /* AR5311 */ #define PCI_DEVICE_ID_ATHEROS_AR5211 0x0012 /* AR5211 */ #define PCI_DEVICE_ID_ATHEROS_AR5212 0x0013 /* AR5212 */ #define PCI_DEVICE_ID_3COM_3CRDAG675 0x0013 /* 3CRDAG675 (Atheros AR5212) */ #define PCI_DEVICE_ID_3COM_2_3CRPAG175 0x0013 /* 3CRPAG175 (Atheros AR5212) */ #define PCI_DEVICE_ID_ATHEROS_AR5210_AP 0x0207 /* AR5210 (Early) */ #define PCI_DEVICE_ID_ATHEROS_AR5212_IBM 0x1014 /* AR5212 (IBM MiniPCI) */ #define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 0x1107 /* AR5210 (no eeprom) */ #define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 0x1113 /* AR5212 (no eeprom) */ #define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 0x1112 /* AR5211 (no eeprom) */ #define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 0xf013 /* AR5212 (emulation board) */ #define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 0xff12 /* AR5211 (emulation board) */ #define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 0xf11b /* AR5211 (emulation board) */ #define PCI_DEVICE_ID_ATHEROS_AR5312_REV2 0x0052 /* AR5312 WMAC (AP31) */ #define PCI_DEVICE_ID_ATHEROS_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */ #define PCI_DEVICE_ID_ATHEROS_AR5312_REV8 0x0058 /* AR5312 WMAC (AP43-030) */ #define PCI_DEVICE_ID_ATHEROS_AR5212_0014 0x0014 /* AR5212 compatible */ #define PCI_DEVICE_ID_ATHEROS_AR5212_0015 0x0015 /* AR5212 compatible */ #define PCI_DEVICE_ID_ATHEROS_AR5212_0016 0x0016 /* AR5212 compatible */ #define PCI_DEVICE_ID_ATHEROS_AR5212_0017 0x0017 /* AR5212 compatible */ #define PCI_DEVICE_ID_ATHEROS_AR5212_0018 0x0018 /* AR5212 compatible */ #define PCI_DEVICE_ID_ATHEROS_AR5212_0019 0x0019 /* AR5212 compatible */ #define PCI_DEVICE_ID_ATHEROS_AR2413 0x001a /* AR2413 (Griffin-lite) */ #define PCI_DEVICE_ID_ATHEROS_AR5413 0x001b /* AR5413 (Eagle) */ #define PCI_DEVICE_ID_ATHEROS_AR5424 0x001c /* AR5424 (Condor PCI-E) */ #define PCI_DEVICE_ID_ATHEROS_AR5416 0x0023 /* AR5416 */ #define PCI_DEVICE_ID_ATHEROS_AR5418 0x0024 /* AR5418 */ /****************************\ GENERIC DRIVER DEFINITIONS \****************************/ #define ATH5K_PRINTF(fmt, ...) \ pr_warn("%s: " fmt, __func__, ##__VA_ARGS__) void __printf(3, 4) _ath5k_printk(const struct ath5k_hw *ah, const char *level, const char *fmt, ...); #define ATH5K_PRINTK(_sc, _level, _fmt, ...) \ _ath5k_printk(_sc, _level, _fmt, ##__VA_ARGS__) #define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) \ do { \ if (net_ratelimit()) \ ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \ } while (0) #define ATH5K_INFO(_sc, _fmt, ...) \ ATH5K_PRINTK(_sc, KERN_INFO, _fmt, ##__VA_ARGS__) #define ATH5K_WARN(_sc, _fmt, ...) \ ATH5K_PRINTK_LIMIT(_sc, KERN_WARNING, _fmt, ##__VA_ARGS__) #define ATH5K_ERR(_sc, _fmt, ...) \ ATH5K_PRINTK_LIMIT(_sc, KERN_ERR, _fmt, ##__VA_ARGS__) /* * AR5K REGISTER ACCESS */ /* Some macros to read/write fields */ /* First shift, then mask */ #define AR5K_REG_SM(_val, _flags) \ (((_val) << _flags##_S) & (_flags)) /* First mask, then shift */ #define AR5K_REG_MS(_val, _flags) \ (((_val) & (_flags)) >> _flags##_S) /* Some registers can hold multiple values of interest. For this * reason when we want to write to these registers we must first * retrieve the values which we do not want to clear (lets call this * old_data) and then set the register with this and our new_value: * ( old_data | new_value) */ #define AR5K_REG_WRITE_BITS(ah, _reg, _flags, _val) \ ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & ~(_flags)) | \ (((_val) << _flags##_S) & (_flags)), _reg) #define AR5K_REG_MASKED_BITS(ah, _reg, _flags, _mask) \ ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & \ (_mask)) | (_flags), _reg) #define AR5K_REG_ENABLE_BITS(ah, _reg, _flags) \ ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) | (_flags), _reg) #define AR5K_REG_DISABLE_BITS(ah, _reg, _flags) \ ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) & ~(_flags), _reg) /* Access QCU registers per queue */ #define AR5K_REG_READ_Q(ah, _reg, _queue) \ (ath5k_hw_reg_read(ah, _reg) & (1 << _queue)) \ #define AR5K_REG_WRITE_Q(ah, _reg, _queue) \ ath5k_hw_reg_write(ah, (1 << _queue), _reg) #define AR5K_Q_ENABLE_BITS(_reg, _queue) do { \ _reg |= 1 << _queue; \ } while (0) #define AR5K_Q_DISABLE_BITS(_reg, _queue) do { \ _reg &= ~(1 << _queue); \ } while (0) /* Used while writing initvals */ #define AR5K_REG_WAIT(_i) do { \ if (_i % 64) \ udelay(1); \ } while (0) /* * Some tunable values (these should be changeable by the user) * TODO: Make use of them and add more options OR use debug/configfs */ #define AR5K_TUNE_DMA_BEACON_RESP 2 #define AR5K_TUNE_SW_BEACON_RESP 10 #define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0 #define AR5K_TUNE_MIN_TX_FIFO_THRES 1 #define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_FRAME_LEN / 64) + 1) #define AR5K_TUNE_REGISTER_TIMEOUT 20000 /* Register for RSSI threshold has a mask of 0xff, so 255 seems to * be the max value. */ #define AR5K_TUNE_RSSI_THRES 129 /* This must be set when setting the RSSI threshold otherwise it can * prevent a reset. If AR5K_RSSI_THR is read after writing to it * the BMISS_THRES will be seen as 0, seems hardware doesn't keep * track of it. Max value depends on hardware. For AR5210 this is just 7. * For AR5211+ this seems to be up to 255. */ #define AR5K_TUNE_BMISS_THRES 7 #define AR5K_TUNE_REGISTER_DWELL_TIME 20000 #define AR5K_TUNE_BEACON_INTERVAL 100 #define AR5K_TUNE_AIFS 2 #define AR5K_TUNE_AIFS_11B 2 #define AR5K_TUNE_AIFS_XR 0 #define AR5K_TUNE_CWMIN 15 #define AR5K_TUNE_CWMIN_11B 31 #define AR5K_TUNE_CWMIN_XR 3 #define AR5K_TUNE_CWMAX 1023 #define AR5K_TUNE_CWMAX_11B 1023 #define AR5K_TUNE_CWMAX_XR 7 #define AR5K_TUNE_NOISE_FLOOR -72 #define AR5K_TUNE_CCA_MAX_GOOD_VALUE -95 #define AR5K_TUNE_MAX_TXPOWER 63 #define AR5K_TUNE_DEFAULT_TXPOWER 25 #define AR5K_TUNE_TPC_TXPOWER false #define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 60000 /* 60 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT 10000 /* 10 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */ #define ATH5K_TX_COMPLETE_POLL_INT 3000 /* 3 sec */ #define AR5K_INIT_CARR_SENSE_EN 1 /*Swap RX/TX Descriptor for big endian archs*/ #if defined(__BIG_ENDIAN) #define AR5K_INIT_CFG ( \ AR5K_CFG_SWTD | AR5K_CFG_SWRD \ ) #else #define AR5K_INIT_CFG 0x00000000 #endif /* Initial values */ #define AR5K_INIT_CYCRSSI_THR1 2 /* Tx retry limit defaults from standard */ #define AR5K_INIT_RETRY_SHORT 7 #define AR5K_INIT_RETRY_LONG 4 /* Slot time */ #define AR5K_INIT_SLOT_TIME_TURBO 6 #define AR5K_INIT_SLOT_TIME_DEFAULT 9 #define AR5K_INIT_SLOT_TIME_HALF_RATE 13 #define AR5K_INIT_SLOT_TIME_QUARTER_RATE 21 #define AR5K_INIT_SLOT_TIME_B 20 #define AR5K_SLOT_TIME_MAX 0xffff /* SIFS */ #define AR5K_INIT_SIFS_TURBO 6 #define AR5K_INIT_SIFS_DEFAULT_BG 10 #define AR5K_INIT_SIFS_DEFAULT_A 16 #define AR5K_INIT_SIFS_HALF_RATE 32 #define AR5K_INIT_SIFS_QUARTER_RATE 64 /* Used to calculate tx time for non 5/10/40MHz * operation */ /* It's preamble time + signal time (16 + 4) */ #define AR5K_INIT_OFDM_PREAMPLE_TIME 20 /* Preamble time for 40MHz (turbo) operation (min ?) */ #define AR5K_INIT_OFDM_PREAMBLE_TIME_MIN 14 #define AR5K_INIT_OFDM_SYMBOL_TIME 4 #define AR5K_INIT_OFDM_PLCP_BITS 22 /* Rx latency for 5 and 10MHz operation (max ?) */ #define AR5K_INIT_RX_LAT_MAX 63 /* Tx latencies from initvals (5212 only but no problem * because we only tweak them on 5212) */ #define AR5K_INIT_TX_LAT_A 54 #define AR5K_INIT_TX_LAT_BG 384 /* Tx latency for 40MHz (turbo) operation (min ?) */ #define AR5K_INIT_TX_LAT_MIN 32 /* Default Tx/Rx latencies (same for 5211)*/ #define AR5K_INIT_TX_LATENCY_5210 54 #define AR5K_INIT_RX_LATENCY_5210 29 /* Tx frame to Tx data start delay */ #define AR5K_INIT_TXF2TXD_START_DEFAULT 14 #define AR5K_INIT_TXF2TXD_START_DELAY_10MHZ 12 #define AR5K_INIT_TXF2TXD_START_DELAY_5MHZ 13 /* We need to increase PHY switch and agc settling time * on turbo mode */ #define AR5K_SWITCH_SETTLING 5760 #define AR5K_SWITCH_SETTLING_TURBO 7168 #define AR5K_AGC_SETTLING 28 /* 38 on 5210 but shouldn't matter */ #define AR5K_AGC_SETTLING_TURBO 37 /*****************************\ * GENERIC CHIPSET DEFINITIONS * \*****************************/ /** * enum ath5k_version - MAC Chips * @AR5K_AR5210: AR5210 (Crete) * @AR5K_AR5211: AR5211 (Oahu/Maui) * @AR5K_AR5212: AR5212 (Venice) and newer */ enum ath5k_version { AR5K_AR5210 = 0, AR5K_AR5211 = 1, AR5K_AR5212 = 2, }; /** * enum ath5k_radio - PHY Chips * @AR5K_RF5110: RF5110 (Fez) * @AR5K_RF5111: RF5111 (Sombrero) * @AR5K_RF5112: RF2112/5112(A) (Derby/Derby2) * @AR5K_RF2413: RF2413/2414 (Griffin/Griffin-Lite) * @AR5K_RF5413: RF5413/5414/5424 (Eagle/Condor) * @AR5K_RF2316: RF2315/2316 (Cobra SoC) * @AR5K_RF2317: RF2317 (Spider SoC) * @AR5K_RF2425: RF2425/2417 (Swan/Nalla) */ enum ath5k_radio { AR5K_RF5110 = 0, AR5K_RF5111 = 1, AR5K_RF5112 = 2, AR5K_RF2413 = 3, AR5K_RF5413 = 4, AR5K_RF2316 = 5, AR5K_RF2317 = 6, AR5K_RF2425 = 7, }; /* * Common silicon revision/version values */ #define AR5K_SREV_UNKNOWN 0xffff #define AR5K_SREV_AR5210 0x00 /* Crete */ #define AR5K_SREV_AR5311 0x10 /* Maui 1 */ #define AR5K_SREV_AR5311A 0x20 /* Maui 2 */ #define AR5K_SREV_AR5311B 0x30 /* Spirit */ #define AR5K_SREV_AR5211 0x40 /* Oahu */ #define AR5K_SREV_AR5212 0x50 /* Venice */ #define AR5K_SREV_AR5312_R2 0x52 /* AP31 */ #define AR5K_SREV_AR5212_V4 0x54 /* ??? */ #define AR5K_SREV_AR5213 0x55 /* ??? */ #define AR5K_SREV_AR5312_R7 0x57 /* AP30 */ #define AR5K_SREV_AR2313_R8 0x58 /* AP43 */ #define AR5K_SREV_AR5213A 0x59 /* Hainan */ #define AR5K_SREV_AR2413 0x78 /* Griffin lite */ #define AR5K_SREV_AR2414 0x70 /* Griffin */ #define AR5K_SREV_AR2315_R6 0x86 /* AP51-Light */ #define AR5K_SREV_AR2315_R7 0x87 /* AP51-Full */ #define AR5K_SREV_AR5424 0x90 /* Condor */ #define AR5K_SREV_AR2317_R1 0x90 /* AP61-Light */ #define AR5K_SREV_AR2317_R2 0x91 /* AP61-Full */ #define AR5K_SREV_AR5413 0xa4 /* Eagle lite */ #define AR5K_SREV_AR5414 0xa0 /* Eagle */ #define AR5K_SREV_AR2415 0xb0 /* Talon */ #define AR5K_SREV_AR5416 0xc0 /* PCI-E */ #define AR5K_SREV_AR5418 0xca /* PCI-E */ #define AR5K_SREV_AR2425 0xe0 /* Swan */ #define AR5K_SREV_AR2417 0xf0 /* Nala */ #define AR5K_SREV_RAD_5110 0x00 #define AR5K_SREV_RAD_5111 0x10 #define AR5K_SREV_RAD_5111A 0x15 #define AR5K_SREV_RAD_2111 0x20 #define AR5K_SREV_RAD_5112 0x30 #define AR5K_SREV_RAD_5112A 0x35 #define AR5K_SREV_RAD_5112B 0x36 #define AR5K_SREV_RAD_2112 0x40 #define AR5K_SREV_RAD_2112A 0x45 #define AR5K_SREV_RAD_2112B 0x46 #define AR5K_SREV_RAD_2413 0x50 #define AR5K_SREV_RAD_5413 0x60 #define AR5K_SREV_RAD_2316 0x70 /* Cobra SoC */ #define AR5K_SREV_RAD_2317 0x80 #define AR5K_SREV_RAD_5424 0xa0 /* Mostly same as 5413 */ #define AR5K_SREV_RAD_2425 0xa2 #define AR5K_SREV_RAD_5133 0xc0 #define AR5K_SREV_PHY_5211 0x30 #define AR5K_SREV_PHY_5212 0x41 #define AR5K_SREV_PHY_5212A 0x42 #define AR5K_SREV_PHY_5212B 0x43 #define AR5K_SREV_PHY_2413 0x45 #define AR5K_SREV_PHY_5413 0x61 #define AR5K_SREV_PHY_2425 0x70 /* TODO add support to mac80211 for vendor-specific rates and modes */ /** * DOC: Atheros XR * * Some of this information is based on Documentation from: * * http://madwifi-project.org/wiki/ChipsetFeatures/SuperAG * * Atheros' eXtended Range - range enhancing extension is a modulation scheme * that is supposed to double the link distance between an Atheros XR-enabled * client device with an Atheros XR-enabled access point. This is achieved * by increasing the receiver sensitivity up to, -105dBm, which is about 20dB * above what the 802.11 specifications demand. In addition, new (proprietary) * data rates are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s. * * Please note that can you either use XR or TURBO but you cannot use both, * they are exclusive. * * Also note that we do not plan to support XR mode at least for now. You can * get a mode similar to XR by using 5MHz bwmode. */ /** * DOC: Atheros SuperAG * * In addition to XR we have another modulation scheme called TURBO mode * that is supposed to provide a throughput transmission speed up to 40Mbit/s * -60Mbit/s at a 108Mbit/s signaling rate achieved through the bonding of two * 54Mbit/s 802.11g channels. To use this feature both ends must support it. * There is also a distinction between "static" and "dynamic" turbo modes: * * - Static: is the dumb version: devices set to this mode stick to it until * the mode is turned off. * * - Dynamic: is the intelligent version, the network decides itself if it * is ok to use turbo. As soon as traffic is detected on adjacent channels * (which would get used in turbo mode), or when a non-turbo station joins * the network, turbo mode won't be used until the situation changes again. * Dynamic mode is achieved by Atheros' Adaptive Radio (AR) feature which * monitors the used radio band in order to decide whether turbo mode may * be used or not. * * This article claims Super G sticks to bonding of channels 5 and 6 for * USA: * * http://www.pcworld.com/article/id,113428-page,1/article.html * * The channel bonding seems to be driver specific though. * * In addition to TURBO modes we also have the following features for even * greater speed-up: * * - Bursting: allows multiple frames to be sent at once, rather than pausing * after each frame. Bursting is a standards-compliant feature that can be * used with any Access Point. * * - Fast frames: increases the amount of information that can be sent per * frame, also resulting in a reduction of transmission overhead. It is a * proprietary feature that needs to be supported by the Access Point. * * - Compression: data frames are compressed in real time using a Lempel Ziv * algorithm. This is done transparently. Once this feature is enabled, * compression and decompression takes place inside the chipset, without * putting additional load on the host CPU. * * As with XR we also don't plan to support SuperAG features for now. You can * get a mode similar to TURBO by using 40MHz bwmode. */ /** * enum ath5k_driver_mode - PHY operation mode * @AR5K_MODE_11A: 802.11a * @AR5K_MODE_11B: 802.11b * @AR5K_MODE_11G: 801.11g * @AR5K_MODE_MAX: Used for boundary checks * * Do not change the order here, we use these as * array indices and it also maps EEPROM structures. */ enum ath5k_driver_mode { AR5K_MODE_11A = 0, AR5K_MODE_11B = 1, AR5K_MODE_11G = 2, AR5K_MODE_MAX = 3 }; /** * enum ath5k_ant_mode - Antenna operation mode * @AR5K_ANTMODE_DEFAULT: Default antenna setup * @AR5K_ANTMODE_FIXED_A: Only antenna A is present * @AR5K_ANTMODE_FIXED_B: Only antenna B is present * @AR5K_ANTMODE_SINGLE_AP: STA locked on a single ap * @AR5K_ANTMODE_SECTOR_AP: AP with tx antenna set on tx desc * @AR5K_ANTMODE_SECTOR_STA: STA with tx antenna set on tx desc * @AR5K_ANTMODE_DEBUG: Debug mode -A -> Rx, B-> Tx- * @AR5K_ANTMODE_MAX: Used for boundary checks * * For more infos on antenna control check out phy.c */ enum ath5k_ant_mode { AR5K_ANTMODE_DEFAULT = 0, AR5K_ANTMODE_FIXED_A = 1, AR5K_ANTMODE_FIXED_B = 2, AR5K_ANTMODE_SINGLE_AP = 3, AR5K_ANTMODE_SECTOR_AP = 4, AR5K_ANTMODE_SECTOR_STA = 5, AR5K_ANTMODE_DEBUG = 6, AR5K_ANTMODE_MAX, }; /** * enum ath5k_bw_mode - Bandwidth operation mode * @AR5K_BWMODE_DEFAULT: 20MHz, default operation * @AR5K_BWMODE_5MHZ: Quarter rate * @AR5K_BWMODE_10MHZ: Half rate * @AR5K_BWMODE_40MHZ: Turbo */ enum ath5k_bw_mode { AR5K_BWMODE_DEFAULT = 0, AR5K_BWMODE_5MHZ = 1, AR5K_BWMODE_10MHZ = 2, AR5K_BWMODE_40MHZ = 3 }; /****************\ TX DEFINITIONS \****************/ /** * struct ath5k_tx_status - TX Status descriptor * @ts_seqnum: Sequence number * @ts_tstamp: Timestamp * @ts_status: Status code * @ts_final_idx: Final transmission series index * @ts_final_retry: Final retry count * @ts_rssi: RSSI for received ACK * @ts_shortretry: Short retry count * @ts_virtcol: Virtual collision count * @ts_antenna: Antenna used * * TX status descriptor gets filled by the hw * on each transmission attempt. */ struct ath5k_tx_status { u16 ts_seqnum; u16 ts_tstamp; u8 ts_status; u8 ts_final_idx; u8 ts_final_retry; s8 ts_rssi; u8 ts_shortretry; u8 ts_virtcol; u8 ts_antenna; }; #define AR5K_TXSTAT_ALTRATE 0x80 #define AR5K_TXERR_XRETRY 0x01 #define AR5K_TXERR_FILT 0x02 #define AR5K_TXERR_FIFO 0x04 /** * enum ath5k_tx_queue - Queue types used to classify tx queues. * @AR5K_TX_QUEUE_INACTIVE: q is unused -- see ath5k_hw_release_tx_queue * @AR5K_TX_QUEUE_DATA: A normal data queue * @AR5K_TX_QUEUE_BEACON: The beacon queue * @AR5K_TX_QUEUE_CAB: The after-beacon queue * @AR5K_TX_QUEUE_UAPSD: Unscheduled Automatic Power Save Delivery queue */ enum ath5k_tx_queue { AR5K_TX_QUEUE_INACTIVE = 0, AR5K_TX_QUEUE_DATA, AR5K_TX_QUEUE_BEACON, AR5K_TX_QUEUE_CAB, AR5K_TX_QUEUE_UAPSD, }; #define AR5K_NUM_TX_QUEUES 10 #define AR5K_NUM_TX_QUEUES_NOQCU 2 /** * enum ath5k_tx_queue_subtype - Queue sub-types to classify normal data queues * @AR5K_WME_AC_BK: Background traffic * @AR5K_WME_AC_BE: Best-effort (normal) traffic * @AR5K_WME_AC_VI: Video traffic * @AR5K_WME_AC_VO: Voice traffic * * These are the 4 Access Categories as defined in * WME spec. 0 is the lowest priority and 4 is the * highest. Normal data that hasn't been classified * goes to the Best Effort AC. */ enum ath5k_tx_queue_subtype { AR5K_WME_AC_BK = 0, AR5K_WME_AC_BE, AR5K_WME_AC_VI, AR5K_WME_AC_VO, }; /** * enum ath5k_tx_queue_id - Queue ID numbers as returned by the hw functions * @AR5K_TX_QUEUE_ID_NOQCU_DATA: Data queue on AR5210 (no QCU available) * @AR5K_TX_QUEUE_ID_NOQCU_BEACON: Beacon queue on AR5210 (no QCU available) * @AR5K_TX_QUEUE_ID_DATA_MIN: Data queue min index * @AR5K_TX_QUEUE_ID_DATA_MAX: Data queue max index * @AR5K_TX_QUEUE_ID_CAB: Content after beacon queue * @AR5K_TX_QUEUE_ID_BEACON: Beacon queue * @AR5K_TX_QUEUE_ID_UAPSD: Urgent Automatic Power Save Delivery, * * Each number represents a hw queue. If hw does not support hw queues * (eg 5210) all data goes in one queue. */ enum ath5k_tx_queue_id { AR5K_TX_QUEUE_ID_NOQCU_DATA = 0, AR5K_TX_QUEUE_ID_NOQCU_BEACON = 1, AR5K_TX_QUEUE_ID_DATA_MIN = 0, AR5K_TX_QUEUE_ID_DATA_MAX = 3, AR5K_TX_QUEUE_ID_UAPSD = 7, AR5K_TX_QUEUE_ID_CAB = 8, AR5K_TX_QUEUE_ID_BEACON = 9, }; /* * Flags to set hw queue's parameters... */ #define AR5K_TXQ_FLAG_TXOKINT_ENABLE 0x0001 /* Enable TXOK interrupt */ #define AR5K_TXQ_FLAG_TXERRINT_ENABLE 0x0002 /* Enable TXERR interrupt */ #define AR5K_TXQ_FLAG_TXEOLINT_ENABLE 0x0004 /* Enable TXEOL interrupt -not used- */ #define AR5K_TXQ_FLAG_TXDESCINT_ENABLE 0x0008 /* Enable TXDESC interrupt -not used- */ #define AR5K_TXQ_FLAG_TXURNINT_ENABLE 0x0010 /* Enable TXURN interrupt */ #define AR5K_TXQ_FLAG_CBRORNINT_ENABLE 0x0020 /* Enable CBRORN interrupt */ #define AR5K_TXQ_FLAG_CBRURNINT_ENABLE 0x0040 /* Enable CBRURN interrupt */ #define AR5K_TXQ_FLAG_QTRIGINT_ENABLE 0x0080 /* Enable QTRIG interrupt */ #define AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE 0x0100 /* Enable TXNOFRM interrupt */ #define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0200 /* Disable random post-backoff */ #define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0300 /* Enable ready time expiry policy (?)*/ #define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0800 /* Enable backoff while bursting */ #define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x1000 /* Disable backoff while bursting */ #define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x2000 /* Enable hw compression -not implemented-*/ /** * struct ath5k_txq - Transmit queue state * @qnum: Hardware q number * @link: Link ptr in last TX desc * @q: Transmit queue (&struct list_head) * @lock: Lock on q and link * @setup: Is the queue configured * @txq_len:Number of queued buffers * @txq_max: Max allowed num of queued buffers * @txq_poll_mark: Used to check if queue got stuck * @txq_stuck: Queue stuck counter * * One of these exists for each hardware transmit queue. * Packets sent to us from above are assigned to queues based * on their priority. Not all devices support a complete set * of hardware transmit queues. For those devices the array * sc_ac2q will map multiple priorities to fewer hardware queues * (typically all to one hardware queue). */ struct ath5k_txq { unsigned int qnum; u32 *link; struct list_head q; spinlock_t lock; bool setup; int txq_len; int txq_max; bool txq_poll_mark; unsigned int txq_stuck; }; /** * struct ath5k_txq_info - A struct to hold TX queue's parameters * @tqi_type: One of enum ath5k_tx_queue * @tqi_subtype: One of enum ath5k_tx_queue_subtype * @tqi_flags: TX queue flags (see above) * @tqi_aifs: Arbitrated Inter-frame Space * @tqi_cw_min: Minimum Contention Window * @tqi_cw_max: Maximum Contention Window * @tqi_cbr_period: Constant bit rate period * @tqi_ready_time: Time queue waits after an event when RDYTIME is enabled */ struct ath5k_txq_info { enum ath5k_tx_queue tqi_type; enum ath5k_tx_queue_subtype tqi_subtype; u16 tqi_flags; u8 tqi_aifs; u16 tqi_cw_min; u16 tqi_cw_max; u32 tqi_cbr_period; u32 tqi_cbr_overflow_limit; u32 tqi_burst_time; u32 tqi_ready_time; }; /** * enum ath5k_pkt_type - Transmit packet types * @AR5K_PKT_TYPE_NORMAL: Normal data * @AR5K_PKT_TYPE_ATIM: ATIM * @AR5K_PKT_TYPE_PSPOLL: PS-Poll * @AR5K_PKT_TYPE_BEACON: Beacon * @AR5K_PKT_TYPE_PROBE_RESP: Probe response * @AR5K_PKT_TYPE_PIFS: PIFS * Used on tx control descriptor */ enum ath5k_pkt_type { AR5K_PKT_TYPE_NORMAL = 0, AR5K_PKT_TYPE_ATIM = 1, AR5K_PKT_TYPE_PSPOLL = 2, AR5K_PKT_TYPE_BEACON = 3, AR5K_PKT_TYPE_PROBE_RESP = 4, AR5K_PKT_TYPE_PIFS = 5, }; /* * TX power and TPC settings */ #define AR5K_TXPOWER_OFDM(_r, _v) ( \ ((0 & 1) << ((_v) + 6)) | \ (((ah->ah_txpower.txp_rates_power_table[(_r)]) & 0x3f) << (_v)) \ ) #define AR5K_TXPOWER_CCK(_r, _v) ( \ (ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v) \ ) /****************\ RX DEFINITIONS \****************/ /** * struct ath5k_rx_status - RX Status descriptor * @rs_datalen: Data length * @rs_tstamp: Timestamp * @rs_status: Status code * @rs_phyerr: PHY error mask * @rs_rssi: RSSI in 0.5dbm units * @rs_keyix: Index to the key used for decrypting * @rs_rate: Rate used to decode the frame * @rs_antenna: Antenna used to receive the frame * @rs_more: Indicates this is a frame fragment (Fast frames) */ struct ath5k_rx_status { u16 rs_datalen; u16 rs_tstamp; u8 rs_status; u8 rs_phyerr; s8 rs_rssi; u8 rs_keyix; u8 rs_rate; u8 rs_antenna; u8 rs_more; }; #define AR5K_RXERR_CRC 0x01 #define AR5K_RXERR_PHY 0x02 #define AR5K_RXERR_FIFO 0x04 #define AR5K_RXERR_DECRYPT 0x08 #define AR5K_RXERR_MIC 0x10 #define AR5K_RXKEYIX_INVALID ((u8) -1) #define AR5K_TXKEYIX_INVALID ((u32) -1) /**************************\ BEACON TIMERS DEFINITIONS \**************************/ #define AR5K_BEACON_PERIOD 0x0000ffff #define AR5K_BEACON_ENA 0x00800000 /*enable beacon xmit*/ #define AR5K_BEACON_RESET_TSF 0x01000000 /*force a TSF reset*/ /* * TSF to TU conversion: * * TSF is a 64bit value in usec (microseconds). * TU is a 32bit value and defined by IEEE802.11 (page 6) as "A measurement of * time equal to 1024 usec", so it's roughly milliseconds (usec / 1024). */ #define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10) /*******************************\ GAIN OPTIMIZATION DEFINITIONS \*******************************/ /** * enum ath5k_rfgain - RF Gain optimization engine state * @AR5K_RFGAIN_INACTIVE: Engine disabled * @AR5K_RFGAIN_ACTIVE: Probe active * @AR5K_RFGAIN_READ_REQUESTED: Probe requested * @AR5K_RFGAIN_NEED_CHANGE: Gain_F needs change */ enum ath5k_rfgain { AR5K_RFGAIN_INACTIVE = 0, AR5K_RFGAIN_ACTIVE, AR5K_RFGAIN_READ_REQUESTED, AR5K_RFGAIN_NEED_CHANGE, }; /** * struct ath5k_gain - RF Gain optimization engine state data * @g_step_idx: Current step index * @g_current: Current gain * @g_target: Target gain * @g_low: Low gain boundary * @g_high: High gain boundary * @g_f_corr: Gain_F correction * @g_state: One of enum ath5k_rfgain */ struct ath5k_gain { u8 g_step_idx; u8 g_current; u8 g_target; u8 g_low; u8 g_high; u8 g_f_corr; u8 g_state; }; /********************\ COMMON DEFINITIONS \********************/ #define AR5K_SLOT_TIME_9 396 #define AR5K_SLOT_TIME_20 880 #define AR5K_SLOT_TIME_MAX 0xffff /** * struct ath5k_athchan_2ghz - 2GHz to 5GHZ map for RF5111 * @a2_flags: Channel flags (internal) * @a2_athchan: HW channel number (internal) * * This structure is used to map 2GHz channels to * 5GHz Atheros channels on 2111 frequency converter * that comes together with RF5111 * TODO: Clean up */ struct ath5k_athchan_2ghz { u32 a2_flags; u16 a2_athchan; }; /** * enum ath5k_dmasize - DMA size definitions (2^(n+2)) * @AR5K_DMASIZE_4B: 4Bytes * @AR5K_DMASIZE_8B: 8Bytes * @AR5K_DMASIZE_16B: 16Bytes * @AR5K_DMASIZE_32B: 32Bytes * @AR5K_DMASIZE_64B: 64Bytes (Default) * @AR5K_DMASIZE_128B: 128Bytes * @AR5K_DMASIZE_256B: 256Bytes * @AR5K_DMASIZE_512B: 512Bytes * * These are used to set DMA burst size on hw * * Note: Some platforms can't handle more than 4Bytes * be careful on embedded boards. */ enum ath5k_dmasize { AR5K_DMASIZE_4B = 0, AR5K_DMASIZE_8B, AR5K_DMASIZE_16B, AR5K_DMASIZE_32B, AR5K_DMASIZE_64B, AR5K_DMASIZE_128B, AR5K_DMASIZE_256B, AR5K_DMASIZE_512B }; /******************\ RATE DEFINITIONS \******************/ /** * DOC: Rate codes * * Seems the ar5xxx hardware supports up to 32 rates, indexed by 1-32. * * The rate code is used to get the RX rate or set the TX rate on the * hardware descriptors. It is also used for internal modulation control * and settings. * * This is the hardware rate map we are aware of (html unfriendly): * * Rate code Rate (Kbps) * --------- ----------- * 0x01 3000 (XR) * 0x02 1000 (XR) * 0x03 250 (XR) * 0x04 - 05 -Reserved- * 0x06 2000 (XR) * 0x07 500 (XR) * 0x08 48000 (OFDM) * 0x09 24000 (OFDM) * 0x0A 12000 (OFDM) * 0x0B 6000 (OFDM) * 0x0C 54000 (OFDM) * 0x0D 36000 (OFDM) * 0x0E 18000 (OFDM) * 0x0F 9000 (OFDM) * 0x10 - 17 -Reserved- * 0x18 11000L (CCK) * 0x19 5500L (CCK) * 0x1A 2000L (CCK) * 0x1B 1000L (CCK) * 0x1C 11000S (CCK) * 0x1D 5500S (CCK) * 0x1E 2000S (CCK) * 0x1F -Reserved- * * "S" indicates CCK rates with short preamble and "L" with long preamble. * * AR5211 has different rate codes for CCK (802.11B) rates. It only uses the * lowest 4 bits, so they are the same as above with a 0xF mask. * (0xB, 0xA, 0x9 and 0x8 for 1M, 2M, 5.5M and 11M). * We handle this in ath5k_setup_bands(). */ #define AR5K_MAX_RATES 32 /* B */ #define ATH5K_RATE_CODE_1M 0x1B #define ATH5K_RATE_CODE_2M 0x1A #define ATH5K_RATE_CODE_5_5M 0x19 #define ATH5K_RATE_CODE_11M 0x18 /* A and G */ #define ATH5K_RATE_CODE_6M 0x0B #define ATH5K_RATE_CODE_9M 0x0F #define ATH5K_RATE_CODE_12M 0x0A #define ATH5K_RATE_CODE_18M 0x0E #define ATH5K_RATE_CODE_24M 0x09 #define ATH5K_RATE_CODE_36M 0x0D #define ATH5K_RATE_CODE_48M 0x08 #define ATH5K_RATE_CODE_54M 0x0C /* Adding this flag to rate_code on B rates * enables short preamble */ #define AR5K_SET_SHORT_PREAMBLE 0x04 /* * Crypto definitions */ #define AR5K_KEYCACHE_SIZE 8 extern bool ath5k_modparam_nohwcrypt; /***********************\ HW RELATED DEFINITIONS \***********************/ /* * Misc definitions */ #define AR5K_RSSI_EP_MULTIPLIER (1 << 7) #define AR5K_ASSERT_ENTRY(_e, _s) do { \ if (_e >= _s) \ return false; \ } while (0) /* * Hardware interrupt abstraction */ /** * enum ath5k_int - Hardware interrupt masks helpers * @AR5K_INT_RXOK: Frame successfully received * @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor * @AR5K_INT_RXERR: Frame reception failed * @AR5K_INT_RXNOFRM: No frame received within a specified time period * @AR5K_INT_RXEOL: Reached "End Of List", means we need more RX descriptors * @AR5K_INT_RXORN: Indicates we got RX FIFO overrun. Note that Rx overrun is * not always fatal, on some chips we can continue operation * without resetting the card, that's why %AR5K_INT_FATAL is not * common for all chips. * @AR5K_INT_RX_ALL: Mask to identify all RX related interrupts * * @AR5K_INT_TXOK: Frame transmission success * @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor * @AR5K_INT_TXERR: Frame transmission failure * @AR5K_INT_TXEOL: Received End Of List for VEOL (Virtual End Of List). The * Queue Control Unit (QCU) signals an EOL interrupt only if a * descriptor's LinkPtr is NULL. For more details, refer to: * "http://www.freepatentsonline.com/20030225739.html" * @AR5K_INT_TXNOFRM: No frame was transmitted within a specified time period * @AR5K_INT_TXURN: Indicates we got TX FIFO underrun. In such case we should * increase the TX trigger threshold. * @AR5K_INT_TX_ALL: Mask to identify all TX related interrupts * * @AR5K_INT_MIB: Indicates the either Management Information Base counters or * one of the PHY error counters reached the maximum value and * should be read and cleared. * @AR5K_INT_SWI: Software triggered interrupt. * @AR5K_INT_RXPHY: RX PHY Error * @AR5K_INT_RXKCM: RX Key cache miss * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a * beacon that must be handled in software. The alternative is if * you have VEOL support, in that case you let the hardware deal * with things. * @AR5K_INT_BRSSI: Beacon received with an RSSI value below our threshold * @AR5K_INT_BMISS: If in STA mode this indicates we have stopped seeing * beacons from the AP have associated with, we should probably * try to reassociate. When in IBSS mode this might mean we have * not received any beacons from any local stations. Note that * every station in an IBSS schedules to send beacons at the * Target Beacon Transmission Time (TBTT) with a random backoff. * @AR5K_INT_BNR: Beacon queue got triggered (DMA beacon alert) while empty. * @AR5K_INT_TIM: Beacon with local station's TIM bit set * @AR5K_INT_DTIM: Beacon with DTIM bit and zero DTIM count received * @AR5K_INT_DTIM_SYNC: DTIM sync lost * @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill switches connected to * our GPIO pins. * @AR5K_INT_BCN_TIMEOUT: Beacon timeout, we waited after TBTT but got noting * @AR5K_INT_CAB_TIMEOUT: We waited for CAB traffic after the beacon but got * nothing or an incomplete CAB frame sequence. * @AR5K_INT_QCBRORN: A queue got it's CBR counter expired * @AR5K_INT_QCBRURN: A queue got triggered wile empty * @AR5K_INT_QTRIG: A queue got triggered * * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by bus/DMA * errors. Indicates we need to reset the card. * @AR5K_INT_GLOBAL: Used to clear and set the IER * @AR5K_INT_NOCARD: Signals the card has been removed * @AR5K_INT_COMMON: Common interrupts shared among MACs with the same * bit value * * These are mapped to take advantage of some common bits * between the MACs, to be able to set intr properties * easier. Some of them are not used yet inside hw.c. Most map * to the respective hw interrupt value as they are common among different * MACs. */ enum ath5k_int { AR5K_INT_RXOK = 0x00000001, AR5K_INT_RXDESC = 0x00000002, AR5K_INT_RXERR = 0x00000004, AR5K_INT_RXNOFRM = 0x00000008, AR5K_INT_RXEOL = 0x00000010, AR5K_INT_RXORN = 0x00000020, AR5K_INT_TXOK = 0x00000040, AR5K_INT_TXDESC = 0x00000080, AR5K_INT_TXERR = 0x00000100, AR5K_INT_TXNOFRM = 0x00000200, AR5K_INT_TXEOL = 0x00000400, AR5K_INT_TXURN = 0x00000800, AR5K_INT_MIB = 0x00001000, AR5K_INT_SWI = 0x00002000, AR5K_INT_RXPHY = 0x00004000, AR5K_INT_RXKCM = 0x00008000, AR5K_INT_SWBA = 0x00010000, AR5K_INT_BRSSI = 0x00020000, AR5K_INT_BMISS = 0x00040000, AR5K_INT_FATAL = 0x00080000, /* Non common */ AR5K_INT_BNR = 0x00100000, /* Non common */ AR5K_INT_TIM = 0x00200000, /* Non common */ AR5K_INT_DTIM = 0x00400000, /* Non common */ AR5K_INT_DTIM_SYNC = 0x00800000, /* Non common */ AR5K_INT_GPIO = 0x01000000, AR5K_INT_BCN_TIMEOUT = 0x02000000, /* Non common */ AR5K_INT_CAB_TIMEOUT = 0x04000000, /* Non common */ AR5K_INT_QCBRORN = 0x08000000, /* Non common */ AR5K_INT_QCBRURN = 0x10000000, /* Non common */ AR5K_INT_QTRIG = 0x20000000, /* Non common */ AR5K_INT_GLOBAL = 0x80000000, AR5K_INT_TX_ALL = AR5K_INT_TXOK | AR5K_INT_TXDESC | AR5K_INT_TXERR | AR5K_INT_TXNOFRM | AR5K_INT_TXEOL | AR5K_INT_TXURN, AR5K_INT_RX_ALL = AR5K_INT_RXOK | AR5K_INT_RXDESC | AR5K_INT_RXERR | AR5K_INT_RXNOFRM | AR5K_INT_RXEOL | AR5K_INT_RXORN, AR5K_INT_COMMON = AR5K_INT_RXOK | AR5K_INT_RXDESC | AR5K_INT_RXERR | AR5K_INT_RXNOFRM | AR5K_INT_RXEOL | AR5K_INT_RXORN | AR5K_INT_TXOK | AR5K_INT_TXDESC | AR5K_INT_TXERR | AR5K_INT_TXNOFRM | AR5K_INT_TXEOL | AR5K_INT_TXURN | AR5K_INT_MIB | AR5K_INT_SWI | AR5K_INT_RXPHY | AR5K_INT_RXKCM | AR5K_INT_SWBA | AR5K_INT_BRSSI | AR5K_INT_BMISS | AR5K_INT_GPIO | AR5K_INT_GLOBAL, AR5K_INT_NOCARD = 0xffffffff }; /** * enum ath5k_calibration_mask - Mask which calibration is active at the moment * @AR5K_CALIBRATION_FULL: Full calibration (AGC + SHORT) * @AR5K_CALIBRATION_SHORT: Short calibration (NF + I/Q) * @AR5K_CALIBRATION_NF: Noise Floor calibration * @AR5K_CALIBRATION_ANI: Adaptive Noise Immunity */ enum ath5k_calibration_mask { AR5K_CALIBRATION_FULL = 0x01, AR5K_CALIBRATION_SHORT = 0x02, AR5K_CALIBRATION_NF = 0x04, AR5K_CALIBRATION_ANI = 0x08, }; /** * enum ath5k_power_mode - Power management modes * @AR5K_PM_UNDEFINED: Undefined * @AR5K_PM_AUTO: Allow card to sleep if possible * @AR5K_PM_AWAKE: Force card to wake up * @AR5K_PM_FULL_SLEEP: Force card to full sleep (DANGEROUS) * @AR5K_PM_NETWORK_SLEEP: Allow to sleep for a specified duration * * Currently only PM_AWAKE is used, FULL_SLEEP and NETWORK_SLEEP/AUTO * are also known to have problems on some cards. This is not a big * problem though because we can have almost the same effect as * FULL_SLEEP by putting card on warm reset (it's almost powered down). */ enum ath5k_power_mode { AR5K_PM_UNDEFINED = 0, AR5K_PM_AUTO, AR5K_PM_AWAKE, AR5K_PM_FULL_SLEEP, AR5K_PM_NETWORK_SLEEP, }; /* * These match net80211 definitions (not used in * mac80211). * TODO: Clean this up */ #define AR5K_LED_INIT 0 /*IEEE80211_S_INIT*/ #define AR5K_LED_SCAN 1 /*IEEE80211_S_SCAN*/ #define AR5K_LED_AUTH 2 /*IEEE80211_S_AUTH*/ #define AR5K_LED_ASSOC 3 /*IEEE80211_S_ASSOC*/ #define AR5K_LED_RUN 4 /*IEEE80211_S_RUN*/ /* GPIO-controlled software LED */ #define AR5K_SOFTLED_PIN 0 #define AR5K_SOFTLED_ON 0 #define AR5K_SOFTLED_OFF 1 /* XXX: we *may* move cap_range stuff to struct wiphy */ struct ath5k_capabilities { /* * Supported PHY modes * (ie. AR5K_MODE_11A, AR5K_MODE_11B, ...) */ DECLARE_BITMAP(cap_mode, AR5K_MODE_MAX); /* * Frequency range (without regulation restrictions) */ struct { u16 range_2ghz_min; u16 range_2ghz_max; u16 range_5ghz_min; u16 range_5ghz_max; } cap_range; /* * Values stored in the EEPROM (some of them...) */ struct ath5k_eeprom_info cap_eeprom; /* * Queue information */ struct { u8 q_tx_num; } cap_queues; bool cap_has_phyerr_counters; bool cap_has_mrr_support; bool cap_needs_2GHz_ovr; }; /* size of noise floor history (keep it a power of two) */ #define ATH5K_NF_CAL_HIST_MAX 8 struct ath5k_nfcal_hist { s16 index; /* current index into nfval */ s16 nfval[ATH5K_NF_CAL_HIST_MAX]; /* last few noise floors */ }; #define ATH5K_LED_MAX_NAME_LEN 31 /* * State for LED triggers */ struct ath5k_led { char name[ATH5K_LED_MAX_NAME_LEN + 1]; /* name of the LED in sysfs */ struct ath5k_hw *ah; /* driver state */ struct led_classdev led_dev; /* led classdev */ }; /* Rfkill */ struct ath5k_rfkill { /* GPIO PIN for rfkill */ u16 gpio; /* polarity of rfkill GPIO PIN */ bool polarity; /* RFKILL toggle tasklet */ struct tasklet_struct toggleq; }; /* statistics */ struct ath5k_statistics { /* antenna use */ unsigned int antenna_rx[5]; /* frames count per antenna RX */ unsigned int antenna_tx[5]; /* frames count per antenna TX */ /* frame errors */ unsigned int rx_all_count; /* all RX frames, including errors */ unsigned int tx_all_count; /* all TX frames, including errors */ unsigned int rx_bytes_count; /* all RX bytes, including errored pkts * and the MAC headers for each packet */ unsigned int tx_bytes_count; /* all TX bytes, including errored pkts * and the MAC headers and padding for * each packet. */ unsigned int rxerr_crc; unsigned int rxerr_phy; unsigned int rxerr_phy_code[32]; unsigned int rxerr_fifo; unsigned int rxerr_decrypt; unsigned int rxerr_mic; unsigned int rxerr_proc; unsigned int rxerr_jumbo; unsigned int txerr_retry; unsigned int txerr_fifo; unsigned int txerr_filt; /* MIB counters */ unsigned int ack_fail; unsigned int rts_fail; unsigned int rts_ok; unsigned int fcs_error; unsigned int beacons; unsigned int mib_intr; unsigned int rxorn_intr; unsigned int rxeol_intr; }; /* * Misc defines */ #define AR5K_MAX_GPIO 10 #define AR5K_MAX_RF_BANKS 8 #if CHAN_DEBUG #define ATH_CHAN_MAX (26 + 26 + 26 + 200 + 200) #else #define ATH_CHAN_MAX (14 + 14 + 14 + 252 + 20) #endif #define ATH_RXBUF 40 /* number of RX buffers */ #define ATH_TXBUF 200 /* number of TX buffers */ #define ATH_BCBUF 4 /* number of beacon buffers */ #define ATH5K_TXQ_LEN_MAX (ATH_TXBUF / 4) /* bufs per queue */ #define ATH5K_TXQ_LEN_LOW (ATH5K_TXQ_LEN_MAX / 2) /* low mark */ /* Driver state associated with an instance of a device */ struct ath5k_hw { struct ath_common common; struct pci_dev *pdev; struct device *dev; /* for dma mapping */ int irq; u16 devid; void __iomem *iobase; /* address of the device */ struct mutex lock; /* dev-level lock */ struct ieee80211_hw *hw; /* IEEE 802.11 common */ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; struct ieee80211_channel channels[ATH_CHAN_MAX]; struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; enum nl80211_iftype opmode; #ifdef CONFIG_ATH5K_DEBUG struct ath5k_dbg_info debug; /* debug info */ #endif /* CONFIG_ATH5K_DEBUG */ struct ath5k_buf *bufptr; /* allocated buffer ptr */ struct ath5k_desc *desc; /* TX/RX descriptors */ dma_addr_t desc_daddr; /* DMA (physical) address */ size_t desc_len; /* size of TX/RX descriptors */ DECLARE_BITMAP(status, 4); #define ATH_STAT_INVALID 0 /* disable hardware accesses */ #define ATH_STAT_PROMISC 1 #define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */ #define ATH_STAT_STARTED 3 /* opened & irqs enabled */ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ struct ieee80211_channel *curchan; /* current h/w channel */ u16 nvifs; enum ath5k_int imask; /* interrupt mask copy */ spinlock_t irqlock; bool rx_pending; /* rx tasklet pending */ bool tx_pending; /* tx tasklet pending */ u8 bssidmask[ETH_ALEN]; unsigned int led_pin, /* GPIO pin for driving LED */ led_on; /* pin setting for LED on */ struct work_struct reset_work; /* deferred chip reset */ struct work_struct calib_work; /* deferred phy calibration */ struct list_head rxbuf; /* receive buffer */ spinlock_t rxbuflock; u32 *rxlink; /* link ptr in last RX desc */ struct tasklet_struct rxtq; /* rx intr tasklet */ struct ath5k_led rx_led; /* rx led */ struct list_head txbuf; /* transmit buffer */ spinlock_t txbuflock; unsigned int txbuf_len; /* buf count in txbuf list */ struct ath5k_txq txqs[AR5K_NUM_TX_QUEUES]; /* tx queues */ struct tasklet_struct txtq; /* tx intr tasklet */ struct ath5k_led tx_led; /* tx led */ struct ath5k_rfkill rf_kill; spinlock_t block; /* protects beacon */ struct tasklet_struct beacontq; /* beacon intr tasklet */ struct list_head bcbuf; /* beacon buffer */ struct ieee80211_vif *bslot[ATH_BCBUF]; u16 num_ap_vifs; u16 num_adhoc_vifs; u16 num_mesh_vifs; unsigned int bhalq, /* SW q for outgoing beacons */ bmisscount, /* missed beacon transmits */ bintval, /* beacon interval in TU */ bsent; unsigned int nexttbtt; /* next beacon time in TU */ struct ath5k_txq *cabq; /* content after beacon */ bool assoc; /* associate state */ bool enable_beacon; /* true if beacons are on */ struct ath5k_statistics stats; struct ath5k_ani_state ani_state; struct tasklet_struct ani_tasklet; /* ANI calibration */ struct delayed_work tx_complete_work; struct survey_info survey; /* collected survey info */ enum ath5k_int ah_imr; struct ieee80211_channel *ah_current_channel; bool ah_iq_cal_needed; bool ah_single_chip; enum ath5k_version ah_version; enum ath5k_radio ah_radio; u32 ah_mac_srev; u16 ah_mac_version; u16 ah_phy_revision; u16 ah_radio_5ghz_revision; u16 ah_radio_2ghz_revision; #define ah_modes ah_capabilities.cap_mode #define ah_ee_version ah_capabilities.cap_eeprom.ee_version u8 ah_retry_long; u8 ah_retry_short; u32 ah_use_32khz_clock; u8 ah_coverage_class; bool ah_ack_bitrate_high; u8 ah_bwmode; bool ah_short_slot; /* Antenna Control */ u32 ah_ant_ctl[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; u8 ah_ant_mode; u8 ah_tx_ant; u8 ah_def_ant; struct ath5k_capabilities ah_capabilities; struct ath5k_txq_info ah_txq[AR5K_NUM_TX_QUEUES]; u32 ah_txq_status; u32 ah_txq_imr_txok; u32 ah_txq_imr_txerr; u32 ah_txq_imr_txurn; u32 ah_txq_imr_txdesc; u32 ah_txq_imr_txeol; u32 ah_txq_imr_cbrorn; u32 ah_txq_imr_cbrurn; u32 ah_txq_imr_qtrig; u32 ah_txq_imr_nofrm; u32 ah_txq_isr_txok_all; u32 ah_txq_isr_txurn; u32 ah_txq_isr_qcborn; u32 ah_txq_isr_qcburn; u32 ah_txq_isr_qtrig; u32 *ah_rf_banks; size_t ah_rf_banks_size; size_t ah_rf_regs_count; struct ath5k_gain ah_gain; u8 ah_offset[AR5K_MAX_RF_BANKS]; struct { /* Temporary tables used for interpolation */ u8 tmpL[AR5K_EEPROM_N_PD_GAINS] [AR5K_EEPROM_POWER_TABLE_SIZE]; u8 tmpR[AR5K_EEPROM_N_PD_GAINS] [AR5K_EEPROM_POWER_TABLE_SIZE]; u8 txp_pd_table[AR5K_EEPROM_POWER_TABLE_SIZE * 2]; u16 txp_rates_power_table[AR5K_MAX_RATES]; u8 txp_min_idx; bool txp_tpc; /* Values in 0.25dB units */ s16 txp_min_pwr; s16 txp_max_pwr; s16 txp_cur_pwr; /* Values in 0.5dB units */ s16 txp_offset; s16 txp_ofdm; s16 txp_cck_ofdm_gainf_delta; /* Value in dB units */ s16 txp_cck_ofdm_pwr_delta; bool txp_setup; int txp_requested; /* Requested tx power in dBm */ } ah_txpower; struct ath5k_nfcal_hist ah_nfcal_hist; /* average beacon RSSI in our BSS (used by ANI) */ struct ewma ah_beacon_rssi_avg; /* noise floor from last periodic calibration */ s32 ah_noise_floor; /* Calibration timestamp */ unsigned long ah_cal_next_full; unsigned long ah_cal_next_short; unsigned long ah_cal_next_ani; /* Calibration mask */ u8 ah_cal_mask; /* * Function pointers */ int (*ah_setup_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, unsigned int, unsigned int, int, enum ath5k_pkt_type, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, struct ath5k_tx_status *); int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *, struct ath5k_rx_status *); }; struct ath_bus_ops { enum ath_bus_type ath_bus_type; void (*read_cachesize)(struct ath_common *common, int *csz); bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data); int (*eeprom_read_mac)(struct ath5k_hw *ah, u8 *mac); }; /* * Prototypes */ extern const struct ieee80211_ops ath5k_hw_ops; /* Initialization and detach functions */ int ath5k_hw_init(struct ath5k_hw *ah); void ath5k_hw_deinit(struct ath5k_hw *ah); int ath5k_sysfs_register(struct ath5k_hw *ah); void ath5k_sysfs_unregister(struct ath5k_hw *ah); /*Chip id helper functions */ int ath5k_hw_read_srev(struct ath5k_hw *ah); /* LED functions */ int ath5k_init_leds(struct ath5k_hw *ah); void ath5k_led_enable(struct ath5k_hw *ah); void ath5k_led_off(struct ath5k_hw *ah); void ath5k_unregister_leds(struct ath5k_hw *ah); /* Reset Functions */ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel); int ath5k_hw_on_hold(struct ath5k_hw *ah); int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool fast, bool skip_pcu); int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val, bool is_set); /* Power management functions */ /* Clock rate related functions */ unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec); unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock); void ath5k_hw_set_clockrate(struct ath5k_hw *ah); /* DMA Related Functions */ void ath5k_hw_start_rx_dma(struct ath5k_hw *ah); u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah); int ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr); int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue); int ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue); u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue); int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr); int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase); /* Interrupt handling */ bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah); int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask); enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask); void ath5k_hw_update_mib_counters(struct ath5k_hw *ah); /* Init/Stop functions */ void ath5k_hw_dma_init(struct ath5k_hw *ah); int ath5k_hw_dma_stop(struct ath5k_hw *ah); /* EEPROM access functions */ int ath5k_eeprom_init(struct ath5k_hw *ah); void ath5k_eeprom_detach(struct ath5k_hw *ah); /* Protocol Control Unit Functions */ /* Helpers */ int ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band, int len, struct ieee80211_rate *rate, bool shortpre); unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah); unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah); int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype opmode); void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class); /* RX filter control*/ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); void ath5k_hw_set_bssid(struct ath5k_hw *ah); void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask); void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1); u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah); void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter); /* Receive (DRU) start/stop functions */ void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah); void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah); /* Beacon control functions */ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah); void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64); void ath5k_hw_reset_tsf(struct ath5k_hw *ah); void ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, u32 interval); bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval); /* Init function */ void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode); /* Queue Control Unit, DFS Control Unit Functions */ int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, struct ath5k_txq_info *queue_info); int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, const struct ath5k_txq_info *queue_info); int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, struct ath5k_txq_info *queue_info); void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, unsigned int queue); u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue); void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue); int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue); int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time); /* Init function */ int ath5k_hw_init_queues(struct ath5k_hw *ah); /* Hardware Descriptor Functions */ int ath5k_hw_init_desc_functions(struct ath5k_hw *ah); int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, u32 size, unsigned int flags); int ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3); /* GPIO Functions */ void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state); int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio); int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio); u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio); int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val); void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, u32 interrupt_level); /* RFkill Functions */ void ath5k_rfkill_hw_start(struct ath5k_hw *ah); void ath5k_rfkill_hw_stop(struct ath5k_hw *ah); /* Misc functions TODO: Cleanup */ int ath5k_hw_set_capabilities(struct ath5k_hw *ah); int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, u16 assoc_id); int ath5k_hw_disable_pspoll(struct ath5k_hw *ah); /* Initial register settings functions */ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel); /* PHY functions */ /* Misc PHY functions */ u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band); int ath5k_hw_phy_disable(struct ath5k_hw *ah); /* Gain_F optimization */ enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah); int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah); /* PHY/RF channel functions */ bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel); /* PHY calibration */ void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah); int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); void ath5k_hw_update_noise_floor(struct ath5k_hw *ah); /* Spur mitigation */ bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, struct ieee80211_channel *channel); /* Antenna control */ void ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode); void ath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode); /* TX power setup */ int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower); /* Init function */ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 mode, bool fast); /* * Functions used internally */ static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah) { return &ah->common; } static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah) { return &(ath5k_hw_common(ah)->regulatory); } #ifdef CONFIG_ATHEROS_AR231X #define AR5K_AR2315_PCI_BASE ((void __iomem *)0xb0100000) static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 reg) { /* On AR2315 and AR2317 the PCI clock domain registers * are outside of the WMAC register space */ if (unlikely((reg >= 0x4000) && (reg < 0x5000) && (ah->ah_mac_srev >= AR5K_SREV_AR2315_R6))) return AR5K_AR2315_PCI_BASE + reg; return ah->iobase + reg; } static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg) { return ioread32(ath5k_ahb_reg(ah, reg)); } static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg) { iowrite32(val, ath5k_ahb_reg(ah, reg)); } #else static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg) { return ioread32(ah->iobase + reg); } static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg) { iowrite32(val, ah->iobase + reg); } #endif static inline enum ath_bus_type ath5k_get_bus_type(struct ath5k_hw *ah) { return ath5k_hw_common(ah)->bus_ops->ath_bus_type; } static inline void ath5k_read_cachesize(struct ath_common *common, int *csz) { common->bus_ops->read_cachesize(common, csz); } static inline bool ath5k_hw_nvram_read(struct ath5k_hw *ah, u32 off, u16 *data) { struct ath_common *common = ath5k_hw_common(ah); return common->bus_ops->eeprom_read(common, off, data); } static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits) { u32 retval = 0, bit, i; for (i = 0; i < bits; i++) { bit = (val >> i) & 1; retval = (retval << 1) | bit; } return retval; } #endif compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/ani.h0000644000175000017500000001022712026211315023173 0ustar mcgrofmcgrof/* * Copyright (C) 2010 Bruno Randolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ANI_H #define ANI_H #include "../ath.h" enum ath5k_phy_error_code; /* these thresholds are relative to the ATH5K_ANI_LISTEN_PERIOD */ #define ATH5K_ANI_LISTEN_PERIOD 100 #define ATH5K_ANI_OFDM_TRIG_HIGH 500 #define ATH5K_ANI_OFDM_TRIG_LOW 200 #define ATH5K_ANI_CCK_TRIG_HIGH 200 #define ATH5K_ANI_CCK_TRIG_LOW 100 /* average beacon RSSI thresholds */ #define ATH5K_ANI_RSSI_THR_HIGH 40 #define ATH5K_ANI_RSSI_THR_LOW 7 /* maximum available levels */ #define ATH5K_ANI_MAX_FIRSTEP_LVL 2 #define ATH5K_ANI_MAX_NOISE_IMM_LVL 1 /** * enum ath5k_ani_mode - mode for ANI / noise sensitivity * * @ATH5K_ANI_MODE_OFF: Turn ANI off. This can be useful to just stop the ANI * algorithm after it has been on auto mode. * @ATH5K_ANI_MODE_MANUAL_LOW: Manually set all immunity parameters to low, * maximizing sensitivity. ANI will not run. * @ATH5K_ANI_MODE_MANUAL_HIGH: Manually set all immunity parameters to high, * minimizing sensitivity. ANI will not run. * @ATH5K_ANI_MODE_AUTO: Automatically control immunity parameters based on the * amount of OFDM and CCK frame errors (default). */ enum ath5k_ani_mode { ATH5K_ANI_MODE_OFF = 0, ATH5K_ANI_MODE_MANUAL_LOW = 1, ATH5K_ANI_MODE_MANUAL_HIGH = 2, ATH5K_ANI_MODE_AUTO = 3 }; /** * struct ath5k_ani_state - ANI state and associated counters * @ani_mode: One of enum ath5k_ani_mode * @noise_imm_level: Noise immunity level * @spur_level: Spur immunity level * @firstep_level: FIRstep level * @ofdm_weak_sig: OFDM weak signal detection state (on/off) * @cck_weak_sig: CCK weak signal detection state (on/off) * @max_spur_level: Max spur immunity level (chip specific) * @listen_time: Listen time * @ofdm_errors: OFDM timing error count * @cck_errors: CCK timing error count * @last_cc: The &struct ath_cycle_counters (for stats) * @last_listen: Listen time from previous run (for stats) * @last_ofdm_errors: OFDM timing error count from previous run (for tats) * @last_cck_errors: CCK timing error count from previous run (for stats) * @sum_ofdm_errors: Sum of OFDM timing errors (for stats) * @sum_cck_errors: Sum of all CCK timing errors (for stats) */ struct ath5k_ani_state { enum ath5k_ani_mode ani_mode; /* state */ int noise_imm_level; int spur_level; int firstep_level; bool ofdm_weak_sig; bool cck_weak_sig; int max_spur_level; /* used by the algorithm */ unsigned int listen_time; unsigned int ofdm_errors; unsigned int cck_errors; /* debug/statistics only: numbers from last ANI calibration */ struct ath_cycle_counters last_cc; unsigned int last_listen; unsigned int last_ofdm_errors; unsigned int last_cck_errors; unsigned int sum_ofdm_errors; unsigned int sum_cck_errors; }; void ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode); void ath5k_ani_mib_intr(struct ath5k_hw *ah); void ath5k_ani_calibration(struct ath5k_hw *ah); void ath5k_ani_phy_error_report(struct ath5k_hw *ah, enum ath5k_phy_error_code phyerr); /* for manual control */ void ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level); void ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level); void ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level); void ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on); void ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on); void ath5k_ani_print_counters(struct ath5k_hw *ah); #endif /* ANI_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/ahb.c0000644000175000017500000001463212026211315023155 0ustar mcgrofmcgrof/* * Copyright (c) 2008-2009 Atheros Communications Inc. * Copyright (c) 2009 Gabor Juhos * Copyright (c) 2009 Imre Kaloz * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "ath5k.h" #include "debug.h" #include "base.h" #include "reg.h" /* return bus cachesize in 4B word units */ static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz) { *csz = L1_CACHE_BYTES >> 2; } static bool ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath5k_hw *ah = common->priv; struct platform_device *pdev = to_platform_device(ah->dev); struct ar231x_board_config *bcfg = pdev->dev.platform_data; u16 *eeprom, *eeprom_end; bcfg = pdev->dev.platform_data; eeprom = (u16 *) bcfg->radio; eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ; eeprom += off; if (eeprom > eeprom_end) return false; *data = *eeprom; return true; } int ath5k_hw_read_srev(struct ath5k_hw *ah) { struct platform_device *pdev = to_platform_device(ah->dev); struct ar231x_board_config *bcfg = pdev->dev.platform_data; ah->ah_mac_srev = bcfg->devid; return 0; } static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) { struct platform_device *pdev = to_platform_device(ah->dev); struct ar231x_board_config *bcfg = pdev->dev.platform_data; u8 *cfg_mac; if (to_platform_device(ah->dev)->id == 0) cfg_mac = bcfg->config->wlan0_mac; else cfg_mac = bcfg->config->wlan1_mac; memcpy(mac, cfg_mac, ETH_ALEN); return 0; } static const struct ath_bus_ops ath_ahb_bus_ops = { .ath_bus_type = ATH_AHB, .read_cachesize = ath5k_ahb_read_cachesize, .eeprom_read = ath5k_ahb_eeprom_read, .eeprom_read_mac = ath5k_ahb_eeprom_read_mac, }; /*Initialization*/ static int ath_ahb_probe(struct platform_device *pdev) { struct ar231x_board_config *bcfg = pdev->dev.platform_data; struct ath5k_hw *ah; struct ieee80211_hw *hw; struct resource *res; void __iomem *mem; int irq; int ret = 0; u32 reg; if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data specified\n"); ret = -EINVAL; goto err_out; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "no memory resource found\n"); ret = -ENXIO; goto err_out; } mem = ioremap_nocache(res->start, resource_size(res)); if (mem == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -ENOMEM; goto err_out; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(&pdev->dev, "no IRQ resource found\n"); ret = -ENXIO; goto err_iounmap; } irq = res->start; hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops); if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); ret = -ENOMEM; goto err_iounmap; } ah = hw->priv; ah->hw = hw; ah->dev = &pdev->dev; ah->iobase = mem; ah->irq = irq; ah->devid = bcfg->devid; if (bcfg->devid >= AR5K_SREV_AR2315_R6) { /* Enable WMAC AHB arbitration */ reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN; iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); /* Enable global WMAC swapping */ reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP); reg |= AR5K_AR2315_BYTESWAP_WMAC; iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP); } else { /* Enable WMAC DMA access (assuming 5312 or 231x*/ /* TODO: check other platforms */ reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); if (to_platform_device(ah->dev)->id == 0) reg |= AR5K_AR5312_ENABLE_WLAN0; else reg |= AR5K_AR5312_ENABLE_WLAN1; iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); /* * On a dual-band AR5312, the multiband radio is only * used as pass-through. Disable 2 GHz support in the * driver for it */ if (to_platform_device(ah->dev)->id == 0 && (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) == (BD_WLAN1 | BD_WLAN0)) ah->ah_capabilities.cap_needs_2GHz_ovr = true; else ah->ah_capabilities.cap_needs_2GHz_ovr = false; } ret = ath5k_init_ah(ah, &ath_ahb_bus_ops); if (ret != 0) { dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret); ret = -ENODEV; goto err_free_hw; } platform_set_drvdata(pdev, hw); return 0; err_free_hw: ieee80211_free_hw(hw); platform_set_drvdata(pdev, NULL); err_iounmap: iounmap(mem); err_out: return ret; } static int ath_ahb_remove(struct platform_device *pdev) { struct ar231x_board_config *bcfg = pdev->dev.platform_data; struct ieee80211_hw *hw = platform_get_drvdata(pdev); struct ath5k_hw *ah; u32 reg; if (!hw) return 0; ah = hw->priv; if (bcfg->devid >= AR5K_SREV_AR2315_R6) { /* Disable WMAC AHB arbitration */ reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN; iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); } else { /*Stop DMA access */ reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); if (to_platform_device(ah->dev)->id == 0) reg &= ~AR5K_AR5312_ENABLE_WLAN0; else reg &= ~AR5K_AR5312_ENABLE_WLAN1; iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); } ath5k_deinit_ah(ah); iounmap(ah->iobase); platform_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); return 0; } static struct platform_driver ath_ahb_driver = { .probe = ath_ahb_probe, .remove = ath_ahb_remove, .driver = { .name = "ar231x-wmac", .owner = THIS_MODULE, }, }; static int __init ath5k_ahb_init(void) { return platform_driver_register(&ath_ahb_driver); } static void __exit ath5k_ahb_exit(void) { platform_driver_unregister(&ath_ahb_driver); } module_init(ath5k_ahb_init); module_exit(ath5k_ahb_exit); compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/Makefile0000644000175000017500000000100012026211315023700 0ustar mcgrofmcgrofath5k-y += caps.o ath5k-y += initvals.o ath5k-y += eeprom.o ath5k-y += gpio.o ath5k-y += desc.o ath5k-y += dma.o ath5k-y += qcu.o ath5k-y += pcu.o ath5k-y += phy.o ath5k-y += reset.o ath5k-y += attach.o ath5k-y += base.o ath5k-y += led.o ath5k-y += rfkill.o ath5k-y += ani.o ath5k-y += sysfs.o ath5k-y += mac80211-ops.o ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o ath5k-$(CONFIG_ATH5K_AHB) += ahb.o ath5k-$(CONFIG_ATH5K_PCI) += pci.o obj-$(CONFIG_ATH5K) += ath5k.o compat-drivers-2012-09-18/drivers/net/wireless/ath/ath5k/Kconfig0000644000175000017500000000376412026211315023566 0ustar mcgrofmcgrofconfig ATH5K tristate "Atheros 5xxx wireless cards support" depends on (PCI || ATHEROS_AR231X) && MAC80211 select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS select AVERAGE select ATH5K_AHB if (ATHEROS_AR231X && !PCI) select ATH5K_PCI if (!ATHEROS_AR231X && PCI) ---help--- This module adds support for wireless adapters based on Atheros 5xxx chipset. Currently the following chip versions are supported: MAC: AR5211 AR5212 PHY: RF5111/2111 RF5112/2112 RF5413/2413 This driver uses the kernel's mac80211 subsystem. If you choose to build a module, it'll be called ath5k. Say M if unsure. config ATH5K_DEBUG bool "Atheros 5xxx debugging" depends on ATH5K ---help--- Atheros 5xxx debugging messages. Say Y, if and you will get debug options for ath5k. To use this, you need to mount debugfs: mount -t debugfs debug /sys/kernel/debug You will get access to files under: /sys/kernel/debug/ath5k/phy0/ To enable debug, pass the debug level to the debug module parameter. For example: modprobe ath5k debug=0x00000400 config ATH5K_TRACER bool "Atheros 5xxx tracer" depends on ATH5K depends on EVENT_TRACING ---help--- Say Y here to enable tracepoints for the ath5k driver using the kernel tracing infrastructure. Select this option if you are interested in debugging the driver. If unsure, say N. config ATH5K_AHB bool "Atheros 5xxx AHB bus support" depends on (ATHEROS_AR231X && !PCI) ---help--- This adds support for WiSoC type chipsets of the 5xxx Atheros family. config ATH5K_PCI bool "Atheros 5xxx PCI bus support" depends on (!ATHEROS_AR231X && PCI) ---help--- This adds support for PCI type chipsets of the 5xxx Atheros family. config ATH5K_TEST_CHANNELS bool "Enables testing channels on ath5k" depends on ATH5K && CFG80211_CERTIFICATION_ONUS ---help--- This enables non-standard IEEE 802.11 channels on ath5k, which can be used for research purposes. This option should be disabled unless doing research. compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/0000755000175000017500000000000012026211315022477 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/usb.c0000644000175000017500000006217212026211315023444 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * USB - frontend * * Copyright 2008, Johannes Berg * Copyright 2009, 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "carl9170.h" #include "cmd.h" #include "hw.h" #include "fwcmd.h" MODULE_AUTHOR("Johannes Berg "); MODULE_AUTHOR("Christian Lamparter "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless"); MODULE_FIRMWARE(CARL9170FW_NAME); MODULE_ALIAS("ar9170usb"); MODULE_ALIAS("arusb_lnx"); /* * Note: * * Always update our wiki's device list (located at: * http://wireless.kernel.org/en/users/Drivers/ar9170/devices ), * whenever you add a new device. */ static struct usb_device_id carl9170_usb_ids[] = { /* Atheros 9170 */ { USB_DEVICE(0x0cf3, 0x9170) }, /* Atheros TG121N */ { USB_DEVICE(0x0cf3, 0x1001) }, /* TP-Link TL-WN821N v2 */ { USB_DEVICE(0x0cf3, 0x1002), .driver_info = CARL9170_WPS_BUTTON | CARL9170_ONE_LED }, /* 3Com Dual Band 802.11n USB Adapter */ { USB_DEVICE(0x0cf3, 0x1010) }, /* H3C Dual Band 802.11n USB Adapter */ { USB_DEVICE(0x0cf3, 0x1011) }, /* Cace Airpcap NX */ { USB_DEVICE(0xcace, 0x0300) }, /* D-Link DWA 160 A1 */ { USB_DEVICE(0x07d1, 0x3c10) }, /* D-Link DWA 160 A2 */ { USB_DEVICE(0x07d1, 0x3a09) }, /* D-Link DWA 130 D */ { USB_DEVICE(0x07d1, 0x3a0f) }, /* Netgear WNA1000 */ { USB_DEVICE(0x0846, 0x9040) }, /* Netgear WNDA3100 (v1) */ { USB_DEVICE(0x0846, 0x9010) }, /* Netgear WN111 v2 */ { USB_DEVICE(0x0846, 0x9001), .driver_info = CARL9170_ONE_LED }, /* Zydas ZD1221 */ { USB_DEVICE(0x0ace, 0x1221) }, /* Proxim ORiNOCO 802.11n USB */ { USB_DEVICE(0x1435, 0x0804) }, /* WNC Generic 11n USB Dongle */ { USB_DEVICE(0x1435, 0x0326) }, /* ZyXEL NWD271N */ { USB_DEVICE(0x0586, 0x3417) }, /* Z-Com UB81 BG */ { USB_DEVICE(0x0cde, 0x0023) }, /* Z-Com UB82 ABG */ { USB_DEVICE(0x0cde, 0x0026) }, /* Sphairon Homelink 1202 */ { USB_DEVICE(0x0cde, 0x0027) }, /* Arcadyan WN7512 */ { USB_DEVICE(0x083a, 0xf522) }, /* Planex GWUS300 */ { USB_DEVICE(0x2019, 0x5304) }, /* IO-Data WNGDNUS2 */ { USB_DEVICE(0x04bb, 0x093f) }, /* NEC WL300NU-G */ { USB_DEVICE(0x0409, 0x0249) }, /* NEC WL300NU-AG */ { USB_DEVICE(0x0409, 0x02b4) }, /* AVM FRITZ!WLAN USB Stick N */ { USB_DEVICE(0x057c, 0x8401) }, /* AVM FRITZ!WLAN USB Stick N 2.4 */ { USB_DEVICE(0x057c, 0x8402) }, /* Qwest/Actiontec 802AIN Wireless N USB Network Adapter */ { USB_DEVICE(0x1668, 0x1200) }, /* Airlive X.USB a/b/g/n */ { USB_DEVICE(0x1b75, 0x9170) }, /* terminate */ {} }; MODULE_DEVICE_TABLE(usb, carl9170_usb_ids); static void carl9170_usb_submit_data_urb(struct ar9170 *ar) { struct urb *urb; int err; if (atomic_inc_return(&ar->tx_anch_urbs) > AR9170_NUM_TX_URBS) goto err_acc; urb = usb_get_from_anchor(&ar->tx_wait); if (!urb) goto err_acc; usb_anchor_urb(urb, &ar->tx_anch); err = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(err)) { if (net_ratelimit()) { dev_err(&ar->udev->dev, "tx submit failed (%d)\n", urb->status); } usb_unanchor_urb(urb); usb_anchor_urb(urb, &ar->tx_err); } usb_free_urb(urb); if (likely(err == 0)) return; err_acc: atomic_dec(&ar->tx_anch_urbs); } static void carl9170_usb_tx_data_complete(struct urb *urb) { struct ar9170 *ar = usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); if (WARN_ON_ONCE(!ar)) { dev_kfree_skb_irq(urb->context); return; } atomic_dec(&ar->tx_anch_urbs); switch (urb->status) { /* everything is fine */ case 0: carl9170_tx_callback(ar, (void *)urb->context); break; /* disconnect */ case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: /* * Defer the frame clean-up to the tasklet worker. * This is necessary, because carl9170_tx_drop * does not work in an irqsave context. */ usb_anchor_urb(urb, &ar->tx_err); return; /* a random transmission error has occurred? */ default: if (net_ratelimit()) { dev_err(&ar->udev->dev, "tx failed (%d)\n", urb->status); } usb_anchor_urb(urb, &ar->tx_err); break; } if (likely(IS_STARTED(ar))) carl9170_usb_submit_data_urb(ar); } static int carl9170_usb_submit_cmd_urb(struct ar9170 *ar) { struct urb *urb; int err; if (atomic_inc_return(&ar->tx_cmd_urbs) != 1) { atomic_dec(&ar->tx_cmd_urbs); return 0; } urb = usb_get_from_anchor(&ar->tx_cmd); if (!urb) { atomic_dec(&ar->tx_cmd_urbs); return 0; } usb_anchor_urb(urb, &ar->tx_anch); err = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(err)) { usb_unanchor_urb(urb); atomic_dec(&ar->tx_cmd_urbs); } usb_free_urb(urb); return err; } static void carl9170_usb_cmd_complete(struct urb *urb) { struct ar9170 *ar = urb->context; int err = 0; if (WARN_ON_ONCE(!ar)) return; atomic_dec(&ar->tx_cmd_urbs); switch (urb->status) { /* everything is fine */ case 0: break; /* disconnect */ case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: return; default: err = urb->status; break; } if (!IS_INITIALIZED(ar)) return; if (err) dev_err(&ar->udev->dev, "submit cmd cb failed (%d).\n", err); err = carl9170_usb_submit_cmd_urb(ar); if (err) dev_err(&ar->udev->dev, "submit cmd failed (%d).\n", err); } static void carl9170_usb_rx_irq_complete(struct urb *urb) { struct ar9170 *ar = urb->context; if (WARN_ON_ONCE(!ar)) return; switch (urb->status) { /* everything is fine */ case 0: break; /* disconnect */ case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: return; default: goto resubmit; } carl9170_handle_command_response(ar, urb->transfer_buffer, urb->actual_length); resubmit: usb_anchor_urb(urb, &ar->rx_anch); if (unlikely(usb_submit_urb(urb, GFP_ATOMIC))) usb_unanchor_urb(urb); } static int carl9170_usb_submit_rx_urb(struct ar9170 *ar, gfp_t gfp) { struct urb *urb; int err = 0, runs = 0; while ((atomic_read(&ar->rx_anch_urbs) < AR9170_NUM_RX_URBS) && (runs++ < AR9170_NUM_RX_URBS)) { err = -ENOSPC; urb = usb_get_from_anchor(&ar->rx_pool); if (urb) { usb_anchor_urb(urb, &ar->rx_anch); err = usb_submit_urb(urb, gfp); if (unlikely(err)) { usb_unanchor_urb(urb); usb_anchor_urb(urb, &ar->rx_pool); } else { atomic_dec(&ar->rx_pool_urbs); atomic_inc(&ar->rx_anch_urbs); } usb_free_urb(urb); } } return err; } static void carl9170_usb_rx_work(struct ar9170 *ar) { struct urb *urb; int i; for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) { urb = usb_get_from_anchor(&ar->rx_work); if (!urb) break; atomic_dec(&ar->rx_work_urbs); if (IS_INITIALIZED(ar)) { carl9170_rx(ar, urb->transfer_buffer, urb->actual_length); } usb_anchor_urb(urb, &ar->rx_pool); atomic_inc(&ar->rx_pool_urbs); usb_free_urb(urb); carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC); } } void carl9170_usb_handle_tx_err(struct ar9170 *ar) { struct urb *urb; while ((urb = usb_get_from_anchor(&ar->tx_err))) { struct sk_buff *skb = (void *)urb->context; carl9170_tx_drop(ar, skb); carl9170_tx_callback(ar, skb); usb_free_urb(urb); } } static void carl9170_usb_tasklet(unsigned long data) { struct ar9170 *ar = (struct ar9170 *) data; if (!IS_INITIALIZED(ar)) return; carl9170_usb_rx_work(ar); /* * Strictly speaking: The tx scheduler is not part of the USB system. * But the rx worker returns frames back to the mac80211-stack and * this is the _perfect_ place to generate the next transmissions. */ if (IS_STARTED(ar)) carl9170_tx_scheduler(ar); } static void carl9170_usb_rx_complete(struct urb *urb) { struct ar9170 *ar = (struct ar9170 *)urb->context; int err; if (WARN_ON_ONCE(!ar)) return; atomic_dec(&ar->rx_anch_urbs); switch (urb->status) { case 0: /* rx path */ usb_anchor_urb(urb, &ar->rx_work); atomic_inc(&ar->rx_work_urbs); break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: /* handle disconnect events*/ return; default: /* handle all other errors */ usb_anchor_urb(urb, &ar->rx_pool); atomic_inc(&ar->rx_pool_urbs); break; } err = carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC); if (unlikely(err)) { /* * usb_submit_rx_urb reported a problem. * In case this is due to a rx buffer shortage, * elevate the tasklet worker priority to * the highest available level. */ tasklet_hi_schedule(&ar->usb_tasklet); if (atomic_read(&ar->rx_anch_urbs) == 0) { /* * The system is too slow to cope with * the enormous workload. We have simply * run out of active rx urbs and this * unfortunately leads to an unpredictable * device. */ ieee80211_queue_work(ar->hw, &ar->ping_work); } } else { /* * Using anything less than _high_ priority absolutely * kills the rx performance my UP-System... */ tasklet_hi_schedule(&ar->usb_tasklet); } } static struct urb *carl9170_usb_alloc_rx_urb(struct ar9170 *ar, gfp_t gfp) { struct urb *urb; void *buf; buf = kmalloc(ar->fw.rx_size, gfp); if (!buf) return NULL; urb = usb_alloc_urb(0, gfp); if (!urb) { kfree(buf); return NULL; } usb_fill_bulk_urb(urb, ar->udev, usb_rcvbulkpipe(ar->udev, AR9170_USB_EP_RX), buf, ar->fw.rx_size, carl9170_usb_rx_complete, ar); urb->transfer_flags |= URB_FREE_BUFFER; return urb; } static int carl9170_usb_send_rx_irq_urb(struct ar9170 *ar) { struct urb *urb = NULL; void *ibuf; int err = -ENOMEM; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) goto out; ibuf = kmalloc(AR9170_USB_EP_CTRL_MAX, GFP_KERNEL); if (!ibuf) goto out; usb_fill_int_urb(urb, ar->udev, usb_rcvintpipe(ar->udev, AR9170_USB_EP_IRQ), ibuf, AR9170_USB_EP_CTRL_MAX, carl9170_usb_rx_irq_complete, ar, 1); urb->transfer_flags |= URB_FREE_BUFFER; usb_anchor_urb(urb, &ar->rx_anch); err = usb_submit_urb(urb, GFP_KERNEL); if (err) usb_unanchor_urb(urb); out: usb_free_urb(urb); return err; } static int carl9170_usb_init_rx_bulk_urbs(struct ar9170 *ar) { struct urb *urb; int i, err = -EINVAL; /* * The driver actively maintains a second shadow * pool for inactive, but fully-prepared rx urbs. * * The pool should help the driver to master huge * workload spikes without running the risk of * undersupplying the hardware or wasting time by * processing rx data (streams) inside the urb * completion (hardirq context). */ for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) { urb = carl9170_usb_alloc_rx_urb(ar, GFP_KERNEL); if (!urb) { err = -ENOMEM; goto err_out; } usb_anchor_urb(urb, &ar->rx_pool); atomic_inc(&ar->rx_pool_urbs); usb_free_urb(urb); } err = carl9170_usb_submit_rx_urb(ar, GFP_KERNEL); if (err) goto err_out; /* the device now waiting for the firmware. */ carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE); return 0; err_out: usb_scuttle_anchored_urbs(&ar->rx_pool); usb_scuttle_anchored_urbs(&ar->rx_work); usb_kill_anchored_urbs(&ar->rx_anch); return err; } static int carl9170_usb_flush(struct ar9170 *ar) { struct urb *urb; int ret, err = 0; while ((urb = usb_get_from_anchor(&ar->tx_wait))) { struct sk_buff *skb = (void *)urb->context; carl9170_tx_drop(ar, skb); carl9170_tx_callback(ar, skb); usb_free_urb(urb); } ret = usb_wait_anchor_empty_timeout(&ar->tx_cmd, 1000); if (ret == 0) err = -ETIMEDOUT; /* lets wait a while until the tx - queues are dried out */ ret = usb_wait_anchor_empty_timeout(&ar->tx_anch, 1000); if (ret == 0) err = -ETIMEDOUT; usb_kill_anchored_urbs(&ar->tx_anch); carl9170_usb_handle_tx_err(ar); return err; } static void carl9170_usb_cancel_urbs(struct ar9170 *ar) { int err; carl9170_set_state(ar, CARL9170_UNKNOWN_STATE); err = carl9170_usb_flush(ar); if (err) dev_err(&ar->udev->dev, "stuck tx urbs!\n"); usb_poison_anchored_urbs(&ar->tx_anch); carl9170_usb_handle_tx_err(ar); usb_poison_anchored_urbs(&ar->rx_anch); tasklet_kill(&ar->usb_tasklet); usb_scuttle_anchored_urbs(&ar->rx_work); usb_scuttle_anchored_urbs(&ar->rx_pool); usb_scuttle_anchored_urbs(&ar->tx_cmd); } int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd, const bool free_buf) { struct urb *urb; int err = 0; if (!IS_INITIALIZED(ar)) { err = -EPERM; goto err_free; } if (WARN_ON(cmd->hdr.len > CARL9170_MAX_CMD_LEN - 4)) { err = -EINVAL; goto err_free; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { err = -ENOMEM; goto err_free; } usb_fill_int_urb(urb, ar->udev, usb_sndintpipe(ar->udev, AR9170_USB_EP_CMD), cmd, cmd->hdr.len + 4, carl9170_usb_cmd_complete, ar, 1); if (free_buf) urb->transfer_flags |= URB_FREE_BUFFER; usb_anchor_urb(urb, &ar->tx_cmd); usb_free_urb(urb); return carl9170_usb_submit_cmd_urb(ar); err_free: if (free_buf) kfree(cmd); return err; } int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids cmd, unsigned int plen, void *payload, unsigned int outlen, void *out) { int err = -ENOMEM; if (!IS_ACCEPTING_CMD(ar)) return -EIO; if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) might_sleep(); ar->cmd.hdr.len = plen; ar->cmd.hdr.cmd = cmd; /* writing multiple regs fills this buffer already */ if (plen && payload != (u8 *)(ar->cmd.data)) memcpy(ar->cmd.data, payload, plen); spin_lock_bh(&ar->cmd_lock); ar->readbuf = (u8 *)out; ar->readlen = outlen; spin_unlock_bh(&ar->cmd_lock); err = __carl9170_exec_cmd(ar, &ar->cmd, false); if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) { err = wait_for_completion_timeout(&ar->cmd_wait, HZ); if (err == 0) { err = -ETIMEDOUT; goto err_unbuf; } if (ar->readlen != outlen) { err = -EMSGSIZE; goto err_unbuf; } } return 0; err_unbuf: /* Maybe the device was removed in the moment we were waiting? */ if (IS_STARTED(ar)) { dev_err(&ar->udev->dev, "no command feedback " "received (%d).\n", err); /* provide some maybe useful debug information */ print_hex_dump_bytes("carl9170 cmd: ", DUMP_PREFIX_NONE, &ar->cmd, plen + 4); carl9170_restart(ar, CARL9170_RR_COMMAND_TIMEOUT); } /* invalidate to avoid completing the next command prematurely */ spin_lock_bh(&ar->cmd_lock); ar->readbuf = NULL; ar->readlen = 0; spin_unlock_bh(&ar->cmd_lock); return err; } void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb) { struct urb *urb; struct ar9170_stream *tx_stream; void *data; unsigned int len; if (!IS_STARTED(ar)) goto err_drop; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) goto err_drop; if (ar->fw.tx_stream) { tx_stream = (void *) (skb->data - sizeof(*tx_stream)); len = skb->len + sizeof(*tx_stream); tx_stream->length = cpu_to_le16(len); tx_stream->tag = cpu_to_le16(AR9170_TX_STREAM_TAG); data = tx_stream; } else { data = skb->data; len = skb->len; } usb_fill_bulk_urb(urb, ar->udev, usb_sndbulkpipe(ar->udev, AR9170_USB_EP_TX), data, len, carl9170_usb_tx_data_complete, skb); urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(urb, &ar->tx_wait); usb_free_urb(urb); carl9170_usb_submit_data_urb(ar); return; err_drop: carl9170_tx_drop(ar, skb); carl9170_tx_callback(ar, skb); } static void carl9170_release_firmware(struct ar9170 *ar) { if (ar->fw.fw) { release_firmware(ar->fw.fw); memset(&ar->fw, 0, sizeof(ar->fw)); } } void carl9170_usb_stop(struct ar9170 *ar) { int ret; carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STOPPED); ret = carl9170_usb_flush(ar); if (ret) dev_err(&ar->udev->dev, "kill pending tx urbs.\n"); usb_poison_anchored_urbs(&ar->tx_anch); carl9170_usb_handle_tx_err(ar); /* kill any pending command */ spin_lock_bh(&ar->cmd_lock); ar->readlen = 0; spin_unlock_bh(&ar->cmd_lock); complete_all(&ar->cmd_wait); /* This is required to prevent an early completion on _start */ INIT_COMPLETION(ar->cmd_wait); /* * Note: * So far we freed all tx urbs, but we won't dare to touch any rx urbs. * Else we would end up with a unresponsive device... */ } int carl9170_usb_open(struct ar9170 *ar) { usb_unpoison_anchored_urbs(&ar->tx_anch); carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE); return 0; } static int carl9170_usb_load_firmware(struct ar9170 *ar) { const u8 *data; u8 *buf; unsigned int transfer; size_t len; u32 addr; int err = 0; buf = kmalloc(4096, GFP_KERNEL); if (!buf) { err = -ENOMEM; goto err_out; } data = ar->fw.fw->data; len = ar->fw.fw->size; addr = ar->fw.address; /* this removes the miniboot image */ data += ar->fw.offset; len -= ar->fw.offset; while (len) { transfer = min_t(unsigned int, len, 4096u); memcpy(buf, data, transfer); err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0), 0x30 /* FW DL */, 0x40 | USB_DIR_OUT, addr >> 8, 0, buf, transfer, 100); if (err < 0) { kfree(buf); goto err_out; } len -= transfer; data += transfer; addr += transfer; } kfree(buf); err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0), 0x31 /* FW DL COMPLETE */, 0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 200); if (wait_for_completion_timeout(&ar->fw_boot_wait, HZ) == 0) { err = -ETIMEDOUT; goto err_out; } err = carl9170_echo_test(ar, 0x4a110123); if (err) goto err_out; /* now, start the command response counter */ ar->cmd_seq = -1; return 0; err_out: dev_err(&ar->udev->dev, "firmware upload failed (%d).\n", err); return err; } int carl9170_usb_restart(struct ar9170 *ar) { int err = 0; if (ar->intf->condition != USB_INTERFACE_BOUND) return 0; /* * Disable the command response sequence counter check. * We already know that the device/firmware is in a bad state. * So, no extra points are awarded to anyone who reminds the * driver about that. */ ar->cmd_seq = -2; err = carl9170_reboot(ar); carl9170_usb_stop(ar); if (err) goto err_out; tasklet_schedule(&ar->usb_tasklet); /* The reboot procedure can take quite a while to complete. */ msleep(1100); err = carl9170_usb_open(ar); if (err) goto err_out; err = carl9170_usb_load_firmware(ar); if (err) goto err_out; return 0; err_out: carl9170_usb_cancel_urbs(ar); return err; } void carl9170_usb_reset(struct ar9170 *ar) { /* * This is the last resort to get the device going again * without any *user replugging action*. * * But there is a catch: usb_reset really is like a physical * *reconnect*. The mac80211 state will be lost in the process. * Therefore a userspace application, which is monitoring * the link must step in. */ carl9170_usb_cancel_urbs(ar); carl9170_usb_stop(ar); usb_queue_reset_device(ar->intf); } static int carl9170_usb_init_device(struct ar9170 *ar) { int err; /* * The carl9170 firmware let's the driver know when it's * ready for action. But we have to be prepared to gracefully * handle all spurious [flushed] messages after each (re-)boot. * Thus the command response counter remains disabled until it * can be safely synchronized. */ ar->cmd_seq = -2; err = carl9170_usb_send_rx_irq_urb(ar); if (err) goto err_out; err = carl9170_usb_init_rx_bulk_urbs(ar); if (err) goto err_unrx; err = carl9170_usb_open(ar); if (err) goto err_unrx; mutex_lock(&ar->mutex); err = carl9170_usb_load_firmware(ar); mutex_unlock(&ar->mutex); if (err) goto err_stop; return 0; err_stop: carl9170_usb_stop(ar); err_unrx: carl9170_usb_cancel_urbs(ar); err_out: return err; } static void carl9170_usb_firmware_failed(struct ar9170 *ar) { struct device *parent = ar->udev->dev.parent; struct usb_device *udev; /* * Store a copy of the usb_device pointer locally. * This is because device_release_driver initiates * carl9170_usb_disconnect, which in turn frees our * driver context (ar). */ udev = ar->udev; complete(&ar->fw_load_wait); /* unbind anything failed */ if (parent) device_lock(parent); device_release_driver(&udev->dev); if (parent) device_unlock(parent); usb_put_dev(udev); } static void carl9170_usb_firmware_finish(struct ar9170 *ar) { int err; err = carl9170_parse_firmware(ar); if (err) goto err_freefw; err = carl9170_usb_init_device(ar); if (err) goto err_freefw; err = carl9170_register(ar); carl9170_usb_stop(ar); if (err) goto err_unrx; complete(&ar->fw_load_wait); usb_put_dev(ar->udev); return; err_unrx: carl9170_usb_cancel_urbs(ar); err_freefw: carl9170_release_firmware(ar); carl9170_usb_firmware_failed(ar); } static void carl9170_usb_firmware_step2(const struct firmware *fw, void *context) { struct ar9170 *ar = context; if (fw) { ar->fw.fw = fw; carl9170_usb_firmware_finish(ar); return; } dev_err(&ar->udev->dev, "firmware not found.\n"); carl9170_usb_firmware_failed(ar); } static int carl9170_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct ar9170 *ar; struct usb_device *udev; int err; err = usb_reset_device(interface_to_usbdev(intf)); if (err) return err; ar = carl9170_alloc(sizeof(*ar)); if (IS_ERR(ar)) return PTR_ERR(ar); udev = interface_to_usbdev(intf); usb_get_dev(udev); ar->udev = udev; ar->intf = intf; ar->features = id->driver_info; usb_set_intfdata(intf, ar); SET_IEEE80211_DEV(ar->hw, &intf->dev); init_usb_anchor(&ar->rx_anch); init_usb_anchor(&ar->rx_pool); init_usb_anchor(&ar->rx_work); init_usb_anchor(&ar->tx_wait); init_usb_anchor(&ar->tx_anch); init_usb_anchor(&ar->tx_cmd); init_usb_anchor(&ar->tx_err); init_completion(&ar->cmd_wait); init_completion(&ar->fw_boot_wait); init_completion(&ar->fw_load_wait); tasklet_init(&ar->usb_tasklet, carl9170_usb_tasklet, (unsigned long)ar); atomic_set(&ar->tx_cmd_urbs, 0); atomic_set(&ar->tx_anch_urbs, 0); atomic_set(&ar->rx_work_urbs, 0); atomic_set(&ar->rx_anch_urbs, 0); atomic_set(&ar->rx_pool_urbs, 0); usb_get_dev(ar->udev); carl9170_set_state(ar, CARL9170_STOPPED); return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2); } static void carl9170_usb_disconnect(struct usb_interface *intf) { struct ar9170 *ar = usb_get_intfdata(intf); struct usb_device *udev; if (WARN_ON(!ar)) return; udev = ar->udev; wait_for_completion(&ar->fw_load_wait); if (IS_INITIALIZED(ar)) { carl9170_reboot(ar); carl9170_usb_stop(ar); } carl9170_usb_cancel_urbs(ar); carl9170_unregister(ar); usb_set_intfdata(intf, NULL); carl9170_release_firmware(ar); carl9170_free(ar); usb_put_dev(udev); } #ifdef CONFIG_PM static int carl9170_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct ar9170 *ar = usb_get_intfdata(intf); if (!ar) return -ENODEV; carl9170_usb_cancel_urbs(ar); return 0; } static int carl9170_usb_resume(struct usb_interface *intf) { struct ar9170 *ar = usb_get_intfdata(intf); int err; if (!ar) return -ENODEV; usb_unpoison_anchored_urbs(&ar->rx_anch); carl9170_set_state(ar, CARL9170_STOPPED); /* * The USB documentation demands that [for suspend] all traffic * to and from the device has to stop. This would be fine, but * there's a catch: the device[usb phy] does not come back. * * Upon resume the firmware will "kill" itself and the * boot-code sorts out the magic voodoo. * Not very nice, but there's not much what could go wrong. */ msleep(1100); err = carl9170_usb_init_device(ar); if (err) goto err_unrx; return 0; err_unrx: carl9170_usb_cancel_urbs(ar); return err; } #endif /* CONFIG_PM */ static struct usb_driver carl9170_driver = { .name = KBUILD_MODNAME, .probe = carl9170_usb_probe, .disconnect = carl9170_usb_disconnect, .id_table = carl9170_usb_ids, .soft_unbind = 1, #ifdef CONFIG_PM .suspend = carl9170_usb_suspend, .resume = carl9170_usb_resume, .reset_resume = carl9170_usb_resume, #endif /* CONFIG_PM */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(carl9170_driver); compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/main.c0000644000175000017500000013634512026211315023603 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * mac80211 interaction code * * Copyright 2008, Johannes Berg * Copyright 2009, 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "hw.h" #include "carl9170.h" #include "cmd.h" static bool modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware crypto offload."); int modparam_noht; module_param_named(noht, modparam_noht, int, S_IRUGO); MODULE_PARM_DESC(noht, "Disable MPDU aggregation."); #define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \ .bitrate = (_bitrate), \ .flags = (_flags), \ .hw_value = (_hw_rate) | (_txpidx) << 4, \ } struct ieee80211_rate __carl9170_ratetable[] = { RATE(10, 0, 0, 0), RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE), RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE), RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE), RATE(60, 0xb, 0, 0), RATE(90, 0xf, 0, 0), RATE(120, 0xa, 0, 0), RATE(180, 0xe, 0, 0), RATE(240, 0x9, 0, 0), RATE(360, 0xd, 1, 0), RATE(480, 0x8, 2, 0), RATE(540, 0xc, 3, 0), }; #undef RATE #define carl9170_g_ratetable (__carl9170_ratetable + 0) #define carl9170_g_ratetable_size 12 #define carl9170_a_ratetable (__carl9170_ratetable + 4) #define carl9170_a_ratetable_size 8 /* * NB: The hw_value is used as an index into the carl9170_phy_freq_params * array in phy.c so that we don't have to do frequency lookups! */ #define CHAN(_freq, _idx) { \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 18, /* XXX */ \ } static struct ieee80211_channel carl9170_2ghz_chantable[] = { CHAN(2412, 0), CHAN(2417, 1), CHAN(2422, 2), CHAN(2427, 3), CHAN(2432, 4), CHAN(2437, 5), CHAN(2442, 6), CHAN(2447, 7), CHAN(2452, 8), CHAN(2457, 9), CHAN(2462, 10), CHAN(2467, 11), CHAN(2472, 12), CHAN(2484, 13), }; static struct ieee80211_channel carl9170_5ghz_chantable[] = { CHAN(4920, 14), CHAN(4940, 15), CHAN(4960, 16), CHAN(4980, 17), CHAN(5040, 18), CHAN(5060, 19), CHAN(5080, 20), CHAN(5180, 21), CHAN(5200, 22), CHAN(5220, 23), CHAN(5240, 24), CHAN(5260, 25), CHAN(5280, 26), CHAN(5300, 27), CHAN(5320, 28), CHAN(5500, 29), CHAN(5520, 30), CHAN(5540, 31), CHAN(5560, 32), CHAN(5580, 33), CHAN(5600, 34), CHAN(5620, 35), CHAN(5640, 36), CHAN(5660, 37), CHAN(5680, 38), CHAN(5700, 39), CHAN(5745, 40), CHAN(5765, 41), CHAN(5785, 42), CHAN(5805, 43), CHAN(5825, 44), CHAN(5170, 45), CHAN(5190, 46), CHAN(5210, 47), CHAN(5230, 48), }; #undef CHAN #define CARL9170_HT_CAP \ { \ .ht_supported = true, \ .cap = IEEE80211_HT_CAP_MAX_AMSDU | \ IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ IEEE80211_HT_CAP_SGI_40 | \ IEEE80211_HT_CAP_DSSSCCK40 | \ IEEE80211_HT_CAP_SM_PS, \ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \ .mcs = { \ .rx_mask = { 0xff, 0xff, 0, 0, 0x1, 0, 0, 0, 0, 0, }, \ .rx_highest = cpu_to_le16(300), \ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \ }, \ } static struct ieee80211_supported_band carl9170_band_2GHz = { .channels = carl9170_2ghz_chantable, .n_channels = ARRAY_SIZE(carl9170_2ghz_chantable), .bitrates = carl9170_g_ratetable, .n_bitrates = carl9170_g_ratetable_size, .ht_cap = CARL9170_HT_CAP, }; static struct ieee80211_supported_band carl9170_band_5GHz = { .channels = carl9170_5ghz_chantable, .n_channels = ARRAY_SIZE(carl9170_5ghz_chantable), .bitrates = carl9170_a_ratetable, .n_bitrates = carl9170_a_ratetable_size, .ht_cap = CARL9170_HT_CAP, }; static void carl9170_ampdu_gc(struct ar9170 *ar) { struct carl9170_sta_tid *tid_info; LIST_HEAD(tid_gc); rcu_read_lock(); list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) { spin_lock_bh(&ar->tx_ampdu_list_lock); if (tid_info->state == CARL9170_TID_STATE_SHUTDOWN) { tid_info->state = CARL9170_TID_STATE_KILLED; list_del_rcu(&tid_info->list); ar->tx_ampdu_list_len--; list_add_tail(&tid_info->tmp_list, &tid_gc); } spin_unlock_bh(&ar->tx_ampdu_list_lock); } rcu_assign_pointer(ar->tx_ampdu_iter, tid_info); rcu_read_unlock(); synchronize_rcu(); while (!list_empty(&tid_gc)) { struct sk_buff *skb; tid_info = list_first_entry(&tid_gc, struct carl9170_sta_tid, tmp_list); while ((skb = __skb_dequeue(&tid_info->queue))) carl9170_tx_status(ar, skb, false); list_del_init(&tid_info->tmp_list); kfree(tid_info); } } static void carl9170_flush(struct ar9170 *ar, bool drop_queued) { if (drop_queued) { int i; /* * We can only drop frames which have not been uploaded * to the device yet. */ for (i = 0; i < ar->hw->queues; i++) { struct sk_buff *skb; while ((skb = skb_dequeue(&ar->tx_pending[i]))) { struct ieee80211_tx_info *info; info = IEEE80211_SKB_CB(skb); if (info->flags & IEEE80211_TX_CTL_AMPDU) atomic_dec(&ar->tx_ampdu_upload); carl9170_tx_status(ar, skb, false); } } } /* Wait for all other outstanding frames to timeout. */ if (atomic_read(&ar->tx_total_queued)) WARN_ON(wait_for_completion_timeout(&ar->tx_flush, HZ) == 0); } static void carl9170_flush_ba(struct ar9170 *ar) { struct sk_buff_head free; struct carl9170_sta_tid *tid_info; struct sk_buff *skb; __skb_queue_head_init(&free); rcu_read_lock(); spin_lock_bh(&ar->tx_ampdu_list_lock); list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) { if (tid_info->state > CARL9170_TID_STATE_SUSPEND) { tid_info->state = CARL9170_TID_STATE_SUSPEND; spin_lock(&tid_info->lock); while ((skb = __skb_dequeue(&tid_info->queue))) __skb_queue_tail(&free, skb); spin_unlock(&tid_info->lock); } } spin_unlock_bh(&ar->tx_ampdu_list_lock); rcu_read_unlock(); while ((skb = __skb_dequeue(&free))) carl9170_tx_status(ar, skb, false); } static void carl9170_zap_queues(struct ar9170 *ar) { struct carl9170_vif_info *cvif; unsigned int i; carl9170_ampdu_gc(ar); carl9170_flush_ba(ar); carl9170_flush(ar, true); for (i = 0; i < ar->hw->queues; i++) { spin_lock_bh(&ar->tx_status[i].lock); while (!skb_queue_empty(&ar->tx_status[i])) { struct sk_buff *skb; skb = skb_peek(&ar->tx_status[i]); carl9170_tx_get_skb(skb); spin_unlock_bh(&ar->tx_status[i].lock); carl9170_tx_drop(ar, skb); spin_lock_bh(&ar->tx_status[i].lock); carl9170_tx_put_skb(skb); } spin_unlock_bh(&ar->tx_status[i].lock); } BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_SOFT < 1); BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD < CARL9170_NUM_TX_LIMIT_SOFT); BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD >= CARL9170_BAW_BITS); /* reinitialize queues statistics */ memset(&ar->tx_stats, 0, sizeof(ar->tx_stats)); for (i = 0; i < ar->hw->queues; i++) ar->tx_stats[i].limit = CARL9170_NUM_TX_LIMIT_HARD; for (i = 0; i < DIV_ROUND_UP(ar->fw.mem_blocks, BITS_PER_LONG); i++) ar->mem_bitmap[i] = 0; rcu_read_lock(); list_for_each_entry_rcu(cvif, &ar->vif_list, list) { spin_lock_bh(&ar->beacon_lock); dev_kfree_skb_any(cvif->beacon); cvif->beacon = NULL; spin_unlock_bh(&ar->beacon_lock); } rcu_read_unlock(); atomic_set(&ar->tx_ampdu_upload, 0); atomic_set(&ar->tx_ampdu_scheduler, 0); atomic_set(&ar->tx_total_pending, 0); atomic_set(&ar->tx_total_queued, 0); atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks); } #define CARL9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop) \ do { \ queue.aifs = ai_fs; \ queue.cw_min = cwmin; \ queue.cw_max = cwmax; \ queue.txop = _txop; \ } while (0) static int carl9170_op_start(struct ieee80211_hw *hw) { struct ar9170 *ar = hw->priv; int err, i; mutex_lock(&ar->mutex); carl9170_zap_queues(ar); /* reset QoS defaults */ CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_VO], 2, 3, 7, 47); CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_VI], 2, 7, 15, 94); CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_BE], 3, 15, 1023, 0); CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_BK], 7, 15, 1023, 0); CARL9170_FILL_QUEUE(ar->edcf[AR9170_TXQ_SPECIAL], 2, 3, 7, 0); ar->current_factor = ar->current_density = -1; /* "The first key is unique." */ ar->usedkeys = 1; ar->filter_state = 0; ar->ps.last_action = jiffies; ar->ps.last_slept = jiffies; ar->erp_mode = CARL9170_ERP_AUTO; ar->rx_software_decryption = false; ar->disable_offload = false; for (i = 0; i < ar->hw->queues; i++) { ar->queue_stop_timeout[i] = jiffies; ar->max_queue_stop_timeout[i] = 0; } atomic_set(&ar->mem_allocs, 0); err = carl9170_usb_open(ar); if (err) goto out; err = carl9170_init_mac(ar); if (err) goto out; err = carl9170_set_qos(ar); if (err) goto out; if (ar->fw.rx_filter) { err = carl9170_rx_filter(ar, CARL9170_RX_FILTER_OTHER_RA | CARL9170_RX_FILTER_CTL_OTHER | CARL9170_RX_FILTER_BAD); if (err) goto out; } err = carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER, AR9170_DMA_TRIGGER_RXQ); if (err) goto out; /* Clear key-cache */ for (i = 0; i < AR9170_CAM_MAX_USER + 4; i++) { err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE, 0, NULL, 0); if (err) goto out; err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE, 1, NULL, 0); if (err) goto out; if (i < AR9170_CAM_MAX_USER) { err = carl9170_disable_key(ar, i); if (err) goto out; } } carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STARTED); ieee80211_queue_delayed_work(ar->hw, &ar->stat_work, round_jiffies(msecs_to_jiffies(CARL9170_STAT_WORK))); ieee80211_wake_queues(ar->hw); err = 0; out: mutex_unlock(&ar->mutex); return err; } static void carl9170_cancel_worker(struct ar9170 *ar) { cancel_delayed_work_sync(&ar->stat_work); cancel_delayed_work_sync(&ar->tx_janitor); #ifdef CONFIG_CARL9170_LEDS cancel_delayed_work_sync(&ar->led_work); #endif /* CONFIG_CARL9170_LEDS */ cancel_work_sync(&ar->ps_work); cancel_work_sync(&ar->ping_work); cancel_work_sync(&ar->ampdu_work); } static void carl9170_op_stop(struct ieee80211_hw *hw) { struct ar9170 *ar = hw->priv; carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE); ieee80211_stop_queues(ar->hw); mutex_lock(&ar->mutex); if (IS_ACCEPTING_CMD(ar)) { RCU_INIT_POINTER(ar->beacon_iter, NULL); carl9170_led_set_state(ar, 0); /* stop DMA */ carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER, 0); carl9170_usb_stop(ar); } carl9170_zap_queues(ar); mutex_unlock(&ar->mutex); carl9170_cancel_worker(ar); } static void carl9170_restart_work(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, restart_work); int err; ar->usedkeys = 0; ar->filter_state = 0; carl9170_cancel_worker(ar); mutex_lock(&ar->mutex); err = carl9170_usb_restart(ar); if (net_ratelimit()) { if (err) { dev_err(&ar->udev->dev, "Failed to restart device " " (%d).\n", err); } else { dev_info(&ar->udev->dev, "device restarted " "successfully.\n"); } } carl9170_zap_queues(ar); mutex_unlock(&ar->mutex); if (!err) { ar->restart_counter++; atomic_set(&ar->pending_restarts, 0); ieee80211_restart_hw(ar->hw); } else { /* * The reset was unsuccessful and the device seems to * be dead. But there's still one option: a low-level * usb subsystem reset... */ carl9170_usb_reset(ar); } } void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r) { carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE); /* * Sometimes, an error can trigger several different reset events. * By ignoring these *surplus* reset events, the device won't be * killed again, right after it has recovered. */ if (atomic_inc_return(&ar->pending_restarts) > 1) { dev_dbg(&ar->udev->dev, "ignoring restart (%d)\n", r); return; } ieee80211_stop_queues(ar->hw); dev_err(&ar->udev->dev, "restart device (%d)\n", r); if (!WARN_ON(r == CARL9170_RR_NO_REASON) || !WARN_ON(r >= __CARL9170_RR_LAST)) ar->last_reason = r; if (!ar->registered) return; if (IS_ACCEPTING_CMD(ar) && !ar->needs_full_reset) ieee80211_queue_work(ar->hw, &ar->restart_work); else carl9170_usb_reset(ar); /* * At this point, the device instance might have vanished/disabled. * So, don't put any code which access the ar9170 struct * without proper protection. */ } static void carl9170_ping_work(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, ping_work); int err; if (!IS_STARTED(ar)) return; mutex_lock(&ar->mutex); err = carl9170_echo_test(ar, 0xdeadbeef); if (err) carl9170_restart(ar, CARL9170_RR_UNRESPONSIVE_DEVICE); mutex_unlock(&ar->mutex); } static int carl9170_init_interface(struct ar9170 *ar, struct ieee80211_vif *vif) { struct ath_common *common = &ar->common; int err; if (!vif) { WARN_ON_ONCE(IS_STARTED(ar)); return 0; } memcpy(common->macaddr, vif->addr, ETH_ALEN); if (modparam_nohwcrypt || ((vif->type != NL80211_IFTYPE_STATION) && (vif->type != NL80211_IFTYPE_AP))) { ar->rx_software_decryption = true; ar->disable_offload = true; } err = carl9170_set_operating_mode(ar); return err; } static int carl9170_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv; struct ieee80211_vif *main_vif; struct ar9170 *ar = hw->priv; int vif_id = -1, err = 0; mutex_lock(&ar->mutex); rcu_read_lock(); if (vif_priv->active) { /* * Skip the interface structure initialization, * if the vif survived the _restart call. */ vif_id = vif_priv->id; vif_priv->enable_beacon = false; spin_lock_bh(&ar->beacon_lock); dev_kfree_skb_any(vif_priv->beacon); vif_priv->beacon = NULL; spin_unlock_bh(&ar->beacon_lock); goto init; } main_vif = carl9170_get_main_vif(ar); if (main_vif) { switch (main_vif->type) { case NL80211_IFTYPE_STATION: if (vif->type == NL80211_IFTYPE_STATION) break; err = -EBUSY; rcu_read_unlock(); goto unlock; case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: if ((vif->type == NL80211_IFTYPE_STATION) || (vif->type == NL80211_IFTYPE_WDS) || (vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_MESH_POINT)) break; err = -EBUSY; rcu_read_unlock(); goto unlock; default: rcu_read_unlock(); goto unlock; } } vif_id = bitmap_find_free_region(&ar->vif_bitmap, ar->fw.vif_num, 0); if (vif_id < 0) { rcu_read_unlock(); err = -ENOSPC; goto unlock; } BUG_ON(ar->vif_priv[vif_id].id != vif_id); vif_priv->active = true; vif_priv->id = vif_id; vif_priv->enable_beacon = false; ar->vifs++; list_add_tail_rcu(&vif_priv->list, &ar->vif_list); rcu_assign_pointer(ar->vif_priv[vif_id].vif, vif); init: if (carl9170_get_main_vif(ar) == vif) { rcu_assign_pointer(ar->beacon_iter, vif_priv); rcu_read_unlock(); err = carl9170_init_interface(ar, vif); if (err) goto unlock; } else { rcu_read_unlock(); err = carl9170_mod_virtual_mac(ar, vif_id, vif->addr); if (err) goto unlock; } if (ar->fw.tx_seq_table) { err = carl9170_write_reg(ar, ar->fw.tx_seq_table + vif_id * 4, 0); if (err) goto unlock; } unlock: if (err && (vif_id >= 0)) { vif_priv->active = false; bitmap_release_region(&ar->vif_bitmap, vif_id, 0); ar->vifs--; RCU_INIT_POINTER(ar->vif_priv[vif_id].vif, NULL); list_del_rcu(&vif_priv->list); mutex_unlock(&ar->mutex); synchronize_rcu(); } else { if (ar->vifs > 1) ar->ps.off_override |= PS_OFF_VIF; mutex_unlock(&ar->mutex); } return err; } static void carl9170_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv; struct ieee80211_vif *main_vif; struct ar9170 *ar = hw->priv; unsigned int id; mutex_lock(&ar->mutex); if (WARN_ON_ONCE(!vif_priv->active)) goto unlock; ar->vifs--; rcu_read_lock(); main_vif = carl9170_get_main_vif(ar); id = vif_priv->id; vif_priv->active = false; WARN_ON(vif_priv->enable_beacon); vif_priv->enable_beacon = false; list_del_rcu(&vif_priv->list); RCU_INIT_POINTER(ar->vif_priv[id].vif, NULL); if (vif == main_vif) { rcu_read_unlock(); if (ar->vifs) { WARN_ON(carl9170_init_interface(ar, carl9170_get_main_vif(ar))); } else { carl9170_set_operating_mode(ar); } } else { rcu_read_unlock(); WARN_ON(carl9170_mod_virtual_mac(ar, id, NULL)); } carl9170_update_beacon(ar, false); carl9170_flush_cab(ar, id); spin_lock_bh(&ar->beacon_lock); dev_kfree_skb_any(vif_priv->beacon); vif_priv->beacon = NULL; spin_unlock_bh(&ar->beacon_lock); bitmap_release_region(&ar->vif_bitmap, id, 0); carl9170_set_beacon_timers(ar); if (ar->vifs == 1) ar->ps.off_override &= ~PS_OFF_VIF; unlock: mutex_unlock(&ar->mutex); synchronize_rcu(); } void carl9170_ps_check(struct ar9170 *ar) { ieee80211_queue_work(ar->hw, &ar->ps_work); } /* caller must hold ar->mutex */ static int carl9170_ps_update(struct ar9170 *ar) { bool ps = false; int err = 0; if (!ar->ps.off_override) ps = (ar->hw->conf.flags & IEEE80211_CONF_PS); if (ps != ar->ps.state) { err = carl9170_powersave(ar, ps); if (err) return err; if (ar->ps.state && !ps) { ar->ps.sleep_ms = jiffies_to_msecs(jiffies - ar->ps.last_action); } if (ps) ar->ps.last_slept = jiffies; ar->ps.last_action = jiffies; ar->ps.state = ps; } return 0; } static void carl9170_ps_work(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, ps_work); mutex_lock(&ar->mutex); if (IS_STARTED(ar)) WARN_ON_ONCE(carl9170_ps_update(ar) != 0); mutex_unlock(&ar->mutex); } static int carl9170_update_survey(struct ar9170 *ar, bool flush, bool noise) { int err; if (noise) { err = carl9170_get_noisefloor(ar); if (err) return err; } if (ar->fw.hw_counters) { err = carl9170_collect_tally(ar); if (err) return err; } if (flush) memset(&ar->tally, 0, sizeof(ar->tally)); return 0; } static void carl9170_stat_work(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, stat_work.work); int err; mutex_lock(&ar->mutex); err = carl9170_update_survey(ar, false, true); mutex_unlock(&ar->mutex); if (err) return; ieee80211_queue_delayed_work(ar->hw, &ar->stat_work, round_jiffies(msecs_to_jiffies(CARL9170_STAT_WORK))); } static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed) { struct ar9170 *ar = hw->priv; int err = 0; mutex_lock(&ar->mutex); if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { /* TODO */ err = 0; } if (changed & IEEE80211_CONF_CHANGE_PS) { err = carl9170_ps_update(ar); if (err) goto out; } if (changed & IEEE80211_CONF_CHANGE_SMPS) { /* TODO */ err = 0; } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { /* adjust slot time for 5 GHz */ err = carl9170_set_slot_time(ar); if (err) goto out; err = carl9170_update_survey(ar, true, false); if (err) goto out; err = carl9170_set_channel(ar, hw->conf.channel, hw->conf.channel_type, CARL9170_RFI_NONE); if (err) goto out; err = carl9170_update_survey(ar, false, true); if (err) goto out; err = carl9170_set_dyn_sifs_ack(ar); if (err) goto out; err = carl9170_set_rts_cts_rate(ar); if (err) goto out; } if (changed & IEEE80211_CONF_CHANGE_POWER) { err = carl9170_set_mac_tpc(ar, ar->hw->conf.channel); if (err) goto out; } out: mutex_unlock(&ar->mutex); return err; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) #else static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *ha) #endif { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; #else int i; #endif u64 mchash; /* always get broadcast frames */ mchash = 1ULL << (0xff >> 2); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) mchash |= 1ULL << (ha->addr[5] >> 2); #else for (i = 0; i < mc_count; i++) { if (WARN_ON(!ha)) break; mchash |= 1ULL << (ha->dmi_addr[5] >> 2); ha = ha->next; } #endif return mchash; } static void carl9170_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 multicast) { struct ar9170 *ar = hw->priv; /* mask supported flags */ *new_flags &= FIF_ALLMULTI | ar->rx_filter_caps; if (!IS_ACCEPTING_CMD(ar)) return; mutex_lock(&ar->mutex); ar->filter_state = *new_flags; /* * We can support more by setting the sniffer bit and * then checking the error flags, later. */ if (*new_flags & FIF_ALLMULTI) multicast = ~0ULL; if (multicast != ar->cur_mc_hash) WARN_ON(carl9170_update_multicast(ar, multicast)); if (changed_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)) { ar->sniffer_enabled = !!(*new_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)); WARN_ON(carl9170_set_operating_mode(ar)); } if (ar->fw.rx_filter && changed_flags & ar->rx_filter_caps) { u32 rx_filter = 0; if (!ar->fw.ba_filter) rx_filter |= CARL9170_RX_FILTER_CTL_OTHER; if (!(*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL))) rx_filter |= CARL9170_RX_FILTER_BAD; if (!(*new_flags & FIF_CONTROL)) rx_filter |= CARL9170_RX_FILTER_CTL_OTHER; if (!(*new_flags & FIF_PSPOLL)) rx_filter |= CARL9170_RX_FILTER_CTL_PSPOLL; if (!(*new_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS))) { rx_filter |= CARL9170_RX_FILTER_OTHER_RA; rx_filter |= CARL9170_RX_FILTER_DECRY_FAIL; } WARN_ON(carl9170_rx_filter(ar, rx_filter)); } mutex_unlock(&ar->mutex); } static void carl9170_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { struct ar9170 *ar = hw->priv; struct ath_common *common = &ar->common; int err = 0; struct carl9170_vif_info *vif_priv; struct ieee80211_vif *main_vif; mutex_lock(&ar->mutex); vif_priv = (void *) vif->drv_priv; main_vif = carl9170_get_main_vif(ar); if (WARN_ON(!main_vif)) goto out; if (changed & BSS_CHANGED_BEACON_ENABLED) { struct carl9170_vif_info *iter; int i = 0; vif_priv->enable_beacon = bss_conf->enable_beacon; rcu_read_lock(); list_for_each_entry_rcu(iter, &ar->vif_list, list) { if (iter->active && iter->enable_beacon) i++; } rcu_read_unlock(); ar->beacon_enabled = i; } if (changed & BSS_CHANGED_BEACON) { err = carl9170_update_beacon(ar, false); if (err) goto out; } if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { if (main_vif != vif) { bss_conf->beacon_int = main_vif->bss_conf.beacon_int; bss_conf->dtim_period = main_vif->bss_conf.dtim_period; } /* * Therefore a hard limit for the broadcast traffic should * prevent false alarms. */ if (vif->type != NL80211_IFTYPE_STATION && (bss_conf->beacon_int * bss_conf->dtim_period >= (CARL9170_QUEUE_STUCK_TIMEOUT / 2))) { err = -EINVAL; goto out; } err = carl9170_set_beacon_timers(ar); if (err) goto out; } if (changed & BSS_CHANGED_HT) { /* TODO */ err = 0; if (err) goto out; } if (main_vif != vif) goto out; /* * The following settings can only be changed by the * master interface. */ if (changed & BSS_CHANGED_BSSID) { memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); err = carl9170_set_operating_mode(ar); if (err) goto out; } if (changed & BSS_CHANGED_ASSOC) { ar->common.curaid = bss_conf->aid; err = carl9170_set_beacon_timers(ar); if (err) goto out; } if (changed & BSS_CHANGED_ERP_SLOT) { err = carl9170_set_slot_time(ar); if (err) goto out; } if (changed & BSS_CHANGED_BASIC_RATES) { err = carl9170_set_mac_rates(ar); if (err) goto out; } out: WARN_ON_ONCE(err && IS_STARTED(ar)); mutex_unlock(&ar->mutex); } static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ar9170 *ar = hw->priv; struct carl9170_tsf_rsp tsf; int err; mutex_lock(&ar->mutex); err = carl9170_exec_cmd(ar, CARL9170_CMD_READ_TSF, 0, NULL, sizeof(tsf), &tsf); mutex_unlock(&ar->mutex); if (WARN_ON(err)) return 0; return le64_to_cpu(tsf.tsf_64); } static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ar9170 *ar = hw->priv; int err = 0, i; u8 ktype; if (ar->disable_offload || !vif) return -EOPNOTSUPP; /* * We have to fall back to software encryption, whenever * the user choose to participates in an IBSS or is connected * to more than one network. * * This is very unfortunate, because some machines cannot handle * the high througput speed in 802.11n networks. */ if (!is_main_vif(ar, vif)) { mutex_lock(&ar->mutex); goto err_softw; } /* * While the hardware supports *catch-all* key, for offloading * group-key en-/de-cryption. The way of how the hardware * decides which keyId maps to which key, remains a mystery... */ if ((vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_ADHOC) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) return -EOPNOTSUPP; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: ktype = AR9170_ENC_ALG_WEP64; break; case WLAN_CIPHER_SUITE_WEP104: ktype = AR9170_ENC_ALG_WEP128; break; case WLAN_CIPHER_SUITE_TKIP: ktype = AR9170_ENC_ALG_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: ktype = AR9170_ENC_ALG_AESCCMP; break; default: return -EOPNOTSUPP; } mutex_lock(&ar->mutex); if (cmd == SET_KEY) { if (!IS_STARTED(ar)) { err = -EOPNOTSUPP; goto out; } if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { sta = NULL; i = 64 + key->keyidx; } else { for (i = 0; i < 64; i++) if (!(ar->usedkeys & BIT(i))) break; if (i == 64) goto err_softw; } key->hw_key_idx = i; err = carl9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 0, key->key, min_t(u8, 16, key->keylen)); if (err) goto out; if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { err = carl9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 1, key->key + 16, 16); if (err) goto out; /* * hardware is not capable generating MMIC * of fragmented frames! */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; } if (i < 64) ar->usedkeys |= BIT(i); key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; } else { if (!IS_STARTED(ar)) { /* The device is gone... together with the key ;-) */ err = 0; goto out; } if (key->hw_key_idx < 64) { ar->usedkeys &= ~BIT(key->hw_key_idx); } else { err = carl9170_upload_key(ar, key->hw_key_idx, NULL, AR9170_ENC_ALG_NONE, 0, NULL, 0); if (err) goto out; if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { err = carl9170_upload_key(ar, key->hw_key_idx, NULL, AR9170_ENC_ALG_NONE, 1, NULL, 0); if (err) goto out; } } err = carl9170_disable_key(ar, key->hw_key_idx); if (err) goto out; } out: mutex_unlock(&ar->mutex); return err; err_softw: if (!ar->rx_software_decryption) { ar->rx_software_decryption = true; carl9170_set_operating_mode(ar); } mutex_unlock(&ar->mutex); return -ENOSPC; } static int carl9170_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; unsigned int i; atomic_set(&sta_info->pending_frames, 0); if (sta->ht_cap.ht_supported) { if (sta->ht_cap.ampdu_density > 6) { /* * HW does support 16us AMPDU density. * No HT-Xmit for station. */ return 0; } for (i = 0; i < CARL9170_NUM_TID; i++) RCU_INIT_POINTER(sta_info->agg[i], NULL); sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor); sta_info->ht_sta = true; } return 0; } static int carl9170_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; unsigned int i; bool cleanup = false; if (sta->ht_cap.ht_supported) { sta_info->ht_sta = false; rcu_read_lock(); for (i = 0; i < CARL9170_NUM_TID; i++) { struct carl9170_sta_tid *tid_info; tid_info = rcu_dereference(sta_info->agg[i]); RCU_INIT_POINTER(sta_info->agg[i], NULL); if (!tid_info) continue; spin_lock_bh(&ar->tx_ampdu_list_lock); if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN) tid_info->state = CARL9170_TID_STATE_SHUTDOWN; spin_unlock_bh(&ar->tx_ampdu_list_lock); cleanup = true; } rcu_read_unlock(); if (cleanup) carl9170_ampdu_gc(ar); } return 0; } static int carl9170_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *param) { struct ar9170 *ar = hw->priv; int ret; mutex_lock(&ar->mutex); if (queue < ar->hw->queues) { memcpy(&ar->edcf[ar9170_qmap[queue]], param, sizeof(*param)); ret = carl9170_set_qos(ar); } else { ret = -EINVAL; } mutex_unlock(&ar->mutex); return ret; } static void carl9170_ampdu_work(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, ampdu_work); if (!IS_STARTED(ar)) return; mutex_lock(&ar->mutex); carl9170_ampdu_gc(ar); mutex_unlock(&ar->mutex); } static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; struct carl9170_sta_tid *tid_info; if (modparam_noht) return -EOPNOTSUPP; switch (action) { case IEEE80211_AMPDU_TX_START: if (!sta_info->ht_sta) return -EOPNOTSUPP; rcu_read_lock(); if (rcu_dereference(sta_info->agg[tid])) { rcu_read_unlock(); return -EBUSY; } tid_info = kzalloc(sizeof(struct carl9170_sta_tid), GFP_ATOMIC); if (!tid_info) { rcu_read_unlock(); return -ENOMEM; } tid_info->hsn = tid_info->bsn = tid_info->snx = (*ssn); tid_info->state = CARL9170_TID_STATE_PROGRESS; tid_info->tid = tid; tid_info->max = sta_info->ampdu_max_len; INIT_LIST_HEAD(&tid_info->list); INIT_LIST_HEAD(&tid_info->tmp_list); skb_queue_head_init(&tid_info->queue); spin_lock_init(&tid_info->lock); spin_lock_bh(&ar->tx_ampdu_list_lock); ar->tx_ampdu_list_len++; list_add_tail_rcu(&tid_info->list, &ar->tx_ampdu_list); rcu_assign_pointer(sta_info->agg[tid], tid_info); spin_unlock_bh(&ar->tx_ampdu_list_lock); rcu_read_unlock(); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: rcu_read_lock(); tid_info = rcu_dereference(sta_info->agg[tid]); if (tid_info) { spin_lock_bh(&ar->tx_ampdu_list_lock); if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN) tid_info->state = CARL9170_TID_STATE_SHUTDOWN; spin_unlock_bh(&ar->tx_ampdu_list_lock); } RCU_INIT_POINTER(sta_info->agg[tid], NULL); rcu_read_unlock(); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_queue_work(ar->hw, &ar->ampdu_work); break; case IEEE80211_AMPDU_TX_OPERATIONAL: rcu_read_lock(); tid_info = rcu_dereference(sta_info->agg[tid]); sta_info->stats[tid].clear = true; sta_info->stats[tid].req = false; if (tid_info) { bitmap_zero(tid_info->bitmap, CARL9170_BAW_SIZE); tid_info->state = CARL9170_TID_STATE_IDLE; } rcu_read_unlock(); if (WARN_ON_ONCE(!tid_info)) return -EFAULT; break; case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: /* Handled by hardware */ break; default: return -EOPNOTSUPP; } return 0; } #ifdef CONFIG_CARL9170_WPC static int carl9170_register_wps_button(struct ar9170 *ar) { struct input_dev *input; int err; if (!(ar->features & CARL9170_WPS_BUTTON)) return 0; input = input_allocate_device(); if (!input) return -ENOMEM; snprintf(ar->wps.name, sizeof(ar->wps.name), "%s WPS Button", wiphy_name(ar->hw->wiphy)); snprintf(ar->wps.phys, sizeof(ar->wps.phys), "ieee80211/%s/input0", wiphy_name(ar->hw->wiphy)); input->name = ar->wps.name; input->phys = ar->wps.phys; input->id.bustype = BUS_USB; input->dev.parent = &ar->hw->wiphy->dev; input_set_capability(input, EV_KEY, KEY_WPS_BUTTON); err = input_register_device(input); if (err) { input_free_device(input); return err; } ar->wps.pbc = input; return 0; } #endif /* CONFIG_CARL9170_WPC */ #ifdef CONFIG_CARL9170_HWRNG static int carl9170_rng_get(struct ar9170 *ar) { #define RW (CARL9170_MAX_CMD_PAYLOAD_LEN / sizeof(u32)) #define RB (CARL9170_MAX_CMD_PAYLOAD_LEN) static const __le32 rng_load[RW] = { [0 ... (RW - 1)] = cpu_to_le32(AR9170_RAND_REG_NUM)}; u32 buf[RW]; unsigned int i, off = 0, transfer, count; int err; BUILD_BUG_ON(RB > CARL9170_MAX_CMD_PAYLOAD_LEN); if (!IS_ACCEPTING_CMD(ar) || !ar->rng.initialized) return -EAGAIN; count = ARRAY_SIZE(ar->rng.cache); while (count) { err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG, RB, (u8 *) rng_load, RB, (u8 *) buf); if (err) return err; transfer = min_t(unsigned int, count, RW); for (i = 0; i < transfer; i++) ar->rng.cache[off + i] = buf[i]; off += transfer; count -= transfer; } ar->rng.cache_idx = 0; #undef RW #undef RB return 0; } static int carl9170_rng_read(struct hwrng *rng, u32 *data) { struct ar9170 *ar = (struct ar9170 *)rng->priv; int ret = -EIO; mutex_lock(&ar->mutex); if (ar->rng.cache_idx >= ARRAY_SIZE(ar->rng.cache)) { ret = carl9170_rng_get(ar); if (ret) { mutex_unlock(&ar->mutex); return ret; } } *data = ar->rng.cache[ar->rng.cache_idx++]; mutex_unlock(&ar->mutex); return sizeof(u16); } static void carl9170_unregister_hwrng(struct ar9170 *ar) { if (ar->rng.initialized) { hwrng_unregister(&ar->rng.rng); ar->rng.initialized = false; } } static int carl9170_register_hwrng(struct ar9170 *ar) { int err; snprintf(ar->rng.name, ARRAY_SIZE(ar->rng.name), "%s_%s", KBUILD_MODNAME, wiphy_name(ar->hw->wiphy)); ar->rng.rng.name = ar->rng.name; ar->rng.rng.data_read = carl9170_rng_read; ar->rng.rng.priv = (unsigned long)ar; if (WARN_ON(ar->rng.initialized)) return -EALREADY; err = hwrng_register(&ar->rng.rng); if (err) { dev_err(&ar->udev->dev, "Failed to register the random " "number generator (%d)\n", err); return err; } ar->rng.initialized = true; err = carl9170_rng_get(ar); if (err) { carl9170_unregister_hwrng(ar); return err; } return 0; } #endif /* CONFIG_CARL9170_HWRNG */ static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { struct ar9170 *ar = hw->priv; struct ieee80211_channel *chan; struct ieee80211_supported_band *band; int err, b, i; chan = ar->channel; if (!chan) return -ENODEV; if (idx == chan->hw_value) { mutex_lock(&ar->mutex); err = carl9170_update_survey(ar, false, true); mutex_unlock(&ar->mutex); if (err) return err; } for (b = 0; b < IEEE80211_NUM_BANDS; b++) { band = ar->hw->wiphy->bands[b]; if (!band) continue; for (i = 0; i < band->n_channels; i++) { if (band->channels[i].hw_value == idx) { chan = &band->channels[i]; goto found; } } } return -ENOENT; found: memcpy(survey, &ar->survey[idx], sizeof(*survey)); survey->channel = chan; survey->filled = SURVEY_INFO_NOISE_DBM; if (ar->channel == chan) survey->filled |= SURVEY_INFO_IN_USE; if (ar->fw.hw_counters) { survey->filled |= SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_TX; } return 0; } static void carl9170_op_flush(struct ieee80211_hw *hw, bool drop) { struct ar9170 *ar = hw->priv; unsigned int vid; mutex_lock(&ar->mutex); for_each_set_bit(vid, &ar->vif_bitmap, ar->fw.vif_num) carl9170_flush_cab(ar, vid); carl9170_flush(ar, drop); mutex_unlock(&ar->mutex); } static int carl9170_op_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { struct ar9170 *ar = hw->priv; memset(stats, 0, sizeof(*stats)); stats->dot11ACKFailureCount = ar->tx_ack_failures; stats->dot11FCSErrorCount = ar->tx_fcs_errors; return 0; } static void carl9170_op_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; switch (cmd) { case STA_NOTIFY_SLEEP: sta_info->sleeping = true; if (atomic_read(&sta_info->pending_frames)) ieee80211_sta_block_awake(hw, sta, true); break; case STA_NOTIFY_AWAKE: sta_info->sleeping = false; break; } } static bool carl9170_tx_frames_pending(struct ieee80211_hw *hw) { struct ar9170 *ar = hw->priv; return !!atomic_read(&ar->tx_total_queued); } static const struct ieee80211_ops carl9170_ops = { .start = carl9170_op_start, .stop = carl9170_op_stop, .tx = carl9170_op_tx, .flush = carl9170_op_flush, .add_interface = carl9170_op_add_interface, .remove_interface = carl9170_op_remove_interface, .config = carl9170_op_config, .prepare_multicast = carl9170_op_prepare_multicast, .configure_filter = carl9170_op_configure_filter, .conf_tx = carl9170_op_conf_tx, .bss_info_changed = carl9170_op_bss_info_changed, .get_tsf = carl9170_op_get_tsf, .set_key = carl9170_op_set_key, .sta_add = carl9170_op_sta_add, .sta_remove = carl9170_op_sta_remove, .sta_notify = carl9170_op_sta_notify, .get_survey = carl9170_op_get_survey, .get_stats = carl9170_op_get_stats, .ampdu_action = carl9170_op_ampdu_action, .tx_frames_pending = carl9170_tx_frames_pending, }; void *carl9170_alloc(size_t priv_size) { struct ieee80211_hw *hw; struct ar9170 *ar; struct sk_buff *skb; int i; /* * this buffer is used for rx stream reconstruction. * Under heavy load this device (or the transport layer?) * tends to split the streams into separate rx descriptors. */ skb = __dev_alloc_skb(AR9170_RX_STREAM_MAX_SIZE, GFP_KERNEL); if (!skb) goto err_nomem; hw = ieee80211_alloc_hw(priv_size, &carl9170_ops); if (!hw) goto err_nomem; ar = hw->priv; ar->hw = hw; ar->rx_failover = skb; memset(&ar->rx_plcp, 0, sizeof(struct ar9170_rx_head)); ar->rx_has_plcp = false; /* * Here's a hidden pitfall! * * All 4 AC queues work perfectly well under _legacy_ operation. * However as soon as aggregation is enabled, the traffic flow * gets very bumpy. Therefore we have to _switch_ to a * software AC with a single HW queue. */ hw->queues = __AR9170_NUM_TXQ; mutex_init(&ar->mutex); spin_lock_init(&ar->beacon_lock); spin_lock_init(&ar->cmd_lock); spin_lock_init(&ar->tx_stats_lock); spin_lock_init(&ar->tx_ampdu_list_lock); spin_lock_init(&ar->mem_lock); spin_lock_init(&ar->state_lock); atomic_set(&ar->pending_restarts, 0); ar->vifs = 0; for (i = 0; i < ar->hw->queues; i++) { skb_queue_head_init(&ar->tx_status[i]); skb_queue_head_init(&ar->tx_pending[i]); INIT_LIST_HEAD(&ar->bar_list[i]); spin_lock_init(&ar->bar_list_lock[i]); } INIT_WORK(&ar->ps_work, carl9170_ps_work); INIT_WORK(&ar->ping_work, carl9170_ping_work); INIT_WORK(&ar->restart_work, carl9170_restart_work); INIT_WORK(&ar->ampdu_work, carl9170_ampdu_work); INIT_DELAYED_WORK(&ar->stat_work, carl9170_stat_work); INIT_DELAYED_WORK(&ar->tx_janitor, carl9170_tx_janitor); INIT_LIST_HEAD(&ar->tx_ampdu_list); rcu_assign_pointer(ar->tx_ampdu_iter, (struct carl9170_sta_tid *) &ar->tx_ampdu_list); bitmap_zero(&ar->vif_bitmap, ar->fw.vif_num); INIT_LIST_HEAD(&ar->vif_list); init_completion(&ar->tx_flush); /* firmware decides which modes we support */ hw->wiphy->interface_modes = 0; hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_NEED_DTIM_PERIOD | IEEE80211_HW_SIGNAL_DBM; if (!modparam_noht) { /* * see the comment above, why we allow the user * to disable HT by a module parameter. */ hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; } hw->extra_tx_headroom = sizeof(struct _carl9170_tx_superframe); hw->sta_data_size = sizeof(struct carl9170_sta_info); hw->vif_data_size = sizeof(struct carl9170_vif_info); hw->max_rates = CARL9170_TX_MAX_RATES; hw->max_rate_tries = CARL9170_TX_USER_RATE_TRIES; for (i = 0; i < ARRAY_SIZE(ar->noise); i++) ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; /* As IBSS Encryption is software-based, IBSS RSN is supported. */ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; return ar; err_nomem: kfree_skb(skb); return ERR_PTR(-ENOMEM); } static int carl9170_read_eeprom(struct ar9170 *ar) { #define RW 8 /* number of words to read at once */ #define RB (sizeof(u32) * RW) u8 *eeprom = (void *)&ar->eeprom; __le32 offsets[RW]; int i, j, err; BUILD_BUG_ON(sizeof(ar->eeprom) & 3); BUILD_BUG_ON(RB > CARL9170_MAX_CMD_LEN - 4); #ifndef __CHECKER__ /* don't want to handle trailing remains */ BUILD_BUG_ON(sizeof(ar->eeprom) % RB); #endif for (i = 0; i < sizeof(ar->eeprom) / RB; i++) { for (j = 0; j < RW; j++) offsets[j] = cpu_to_le32(AR9170_EEPROM_START + RB * i + 4 * j); err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG, RB, (u8 *) &offsets, RB, eeprom + RB * i); if (err) return err; } #undef RW #undef RB return 0; } static int carl9170_parse_eeprom(struct ar9170 *ar) { struct ath_regulatory *regulatory = &ar->common.regulatory; unsigned int rx_streams, tx_streams, tx_params = 0; int bands = 0; int chans = 0; if (ar->eeprom.length == cpu_to_le16(0xffff)) return -ENODATA; rx_streams = hweight8(ar->eeprom.rx_mask); tx_streams = hweight8(ar->eeprom.tx_mask); if (rx_streams != tx_streams) { tx_params = IEEE80211_HT_MCS_TX_RX_DIFF; WARN_ON(!(tx_streams >= 1 && tx_streams <= IEEE80211_HT_MCS_TX_MAX_STREAMS)); tx_params = (tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; carl9170_band_2GHz.ht_cap.mcs.tx_params |= tx_params; carl9170_band_5GHz.ht_cap.mcs.tx_params |= tx_params; } if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) { ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &carl9170_band_2GHz; chans += carl9170_band_2GHz.n_channels; bands++; } if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) { ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &carl9170_band_5GHz; chans += carl9170_band_5GHz.n_channels; bands++; } if (!bands) return -EINVAL; ar->survey = kzalloc(sizeof(struct survey_info) * chans, GFP_KERNEL); if (!ar->survey) return -ENOMEM; ar->num_channels = chans; /* * I measured this, a bandswitch takes roughly * 135 ms and a frequency switch about 80. * * FIXME: measure these values again once EEPROM settings * are used, that will influence them! */ if (bands == 2) ar->hw->channel_change_time = 135 * 1000; else ar->hw->channel_change_time = 80 * 1000; regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); /* second part of wiphy init */ SET_IEEE80211_PERM_ADDR(ar->hw, ar->eeprom.mac_address); return 0; } static int carl9170_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ar9170 *ar = hw->priv; return ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory); } int carl9170_register(struct ar9170 *ar) { struct ath_regulatory *regulatory = &ar->common.regulatory; int err = 0, i; if (WARN_ON(ar->mem_bitmap)) return -EINVAL; ar->mem_bitmap = kzalloc(roundup(ar->fw.mem_blocks, BITS_PER_LONG) * sizeof(unsigned long), GFP_KERNEL); if (!ar->mem_bitmap) return -ENOMEM; /* try to read EEPROM, init MAC addr */ err = carl9170_read_eeprom(ar); if (err) return err; err = carl9170_parse_eeprom(ar); if (err) return err; err = ath_regd_init(regulatory, ar->hw->wiphy, carl9170_reg_notifier); if (err) return err; if (modparam_noht) { carl9170_band_2GHz.ht_cap.ht_supported = false; carl9170_band_5GHz.ht_cap.ht_supported = false; } for (i = 0; i < ar->fw.vif_num; i++) { ar->vif_priv[i].id = i; ar->vif_priv[i].vif = NULL; } err = ieee80211_register_hw(ar->hw); if (err) return err; /* mac80211 interface is now registered */ ar->registered = true; if (!ath_is_world_regd(regulatory)) regulatory_hint(ar->hw->wiphy, regulatory->alpha2); #ifdef CONFIG_CARL9170_DEBUGFS carl9170_debugfs_register(ar); #endif /* CONFIG_CARL9170_DEBUGFS */ err = carl9170_led_init(ar); if (err) goto err_unreg; #ifdef CONFIG_CARL9170_LEDS err = carl9170_led_register(ar); if (err) goto err_unreg; #endif /* CONFIG_CARL9170_LEDS */ #ifdef CONFIG_CARL9170_WPC err = carl9170_register_wps_button(ar); if (err) goto err_unreg; #endif /* CONFIG_CARL9170_WPC */ #ifdef CONFIG_CARL9170_HWRNG err = carl9170_register_hwrng(ar); if (err) goto err_unreg; #endif /* CONFIG_CARL9170_HWRNG */ dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n", wiphy_name(ar->hw->wiphy)); return 0; err_unreg: carl9170_unregister(ar); return err; } void carl9170_unregister(struct ar9170 *ar) { if (!ar->registered) return; ar->registered = false; #ifdef CONFIG_CARL9170_LEDS carl9170_led_unregister(ar); #endif /* CONFIG_CARL9170_LEDS */ #ifdef CONFIG_CARL9170_DEBUGFS carl9170_debugfs_unregister(ar); #endif /* CONFIG_CARL9170_DEBUGFS */ #ifdef CONFIG_CARL9170_WPC if (ar->wps.pbc) { input_unregister_device(ar->wps.pbc); ar->wps.pbc = NULL; } #endif /* CONFIG_CARL9170_WPC */ #ifdef CONFIG_CARL9170_HWRNG carl9170_unregister_hwrng(ar); #endif /* CONFIG_CARL9170_HWRNG */ carl9170_cancel_worker(ar); cancel_work_sync(&ar->restart_work); ieee80211_unregister_hw(ar->hw); } void carl9170_free(struct ar9170 *ar) { WARN_ON(ar->registered); WARN_ON(IS_INITIALIZED(ar)); kfree_skb(ar->rx_failover); ar->rx_failover = NULL; kfree(ar->mem_bitmap); ar->mem_bitmap = NULL; kfree(ar->survey); ar->survey = NULL; mutex_destroy(&ar->mutex); ieee80211_free_hw(ar->hw); } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/wlan.h0000644000175000017500000002726312026211315023623 0ustar mcgrofmcgrof/* * Shared Atheros AR9170 Header * * RX/TX meta descriptor format * * Copyright 2008, Johannes Berg * Copyright 2009-2011 Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CARL9170_SHARED_WLAN_H #define __CARL9170_SHARED_WLAN_H #include "fwcmd.h" #define AR9170_RX_PHY_RATE_CCK_1M 0x0a #define AR9170_RX_PHY_RATE_CCK_2M 0x14 #define AR9170_RX_PHY_RATE_CCK_5M 0x37 #define AR9170_RX_PHY_RATE_CCK_11M 0x6e #define AR9170_ENC_ALG_NONE 0x0 #define AR9170_ENC_ALG_WEP64 0x1 #define AR9170_ENC_ALG_TKIP 0x2 #define AR9170_ENC_ALG_AESCCMP 0x4 #define AR9170_ENC_ALG_WEP128 0x5 #define AR9170_ENC_ALG_WEP256 0x6 #define AR9170_ENC_ALG_CENC 0x7 #define AR9170_RX_ENC_SOFTWARE 0x8 #define AR9170_RX_STATUS_MODULATION 0x03 #define AR9170_RX_STATUS_MODULATION_S 0 #define AR9170_RX_STATUS_MODULATION_CCK 0x00 #define AR9170_RX_STATUS_MODULATION_OFDM 0x01 #define AR9170_RX_STATUS_MODULATION_HT 0x02 #define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03 /* depends on modulation */ #define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08 #define AR9170_RX_STATUS_GREENFIELD 0x08 #define AR9170_RX_STATUS_MPDU 0x30 #define AR9170_RX_STATUS_MPDU_S 4 #define AR9170_RX_STATUS_MPDU_SINGLE 0x00 #define AR9170_RX_STATUS_MPDU_FIRST 0x20 #define AR9170_RX_STATUS_MPDU_MIDDLE 0x30 #define AR9170_RX_STATUS_MPDU_LAST 0x10 #define AR9170_RX_STATUS_CONT_AGGR 0x40 #define AR9170_RX_STATUS_TOTAL_ERROR 0x80 #define AR9170_RX_ERROR_RXTO 0x01 #define AR9170_RX_ERROR_OVERRUN 0x02 #define AR9170_RX_ERROR_DECRYPT 0x04 #define AR9170_RX_ERROR_FCS 0x08 #define AR9170_RX_ERROR_WRONG_RA 0x10 #define AR9170_RX_ERROR_PLCP 0x20 #define AR9170_RX_ERROR_MMIC 0x40 /* these are either-or */ #define AR9170_TX_MAC_PROT_RTS 0x0001 #define AR9170_TX_MAC_PROT_CTS 0x0002 #define AR9170_TX_MAC_PROT 0x0003 #define AR9170_TX_MAC_NO_ACK 0x0004 /* if unset, MAC will only do SIFS space before frame */ #define AR9170_TX_MAC_BACKOFF 0x0008 #define AR9170_TX_MAC_BURST 0x0010 #define AR9170_TX_MAC_AGGR 0x0020 /* encryption is a two-bit field */ #define AR9170_TX_MAC_ENCR_NONE 0x0000 #define AR9170_TX_MAC_ENCR_RC4 0x0040 #define AR9170_TX_MAC_ENCR_CENC 0x0080 #define AR9170_TX_MAC_ENCR_AES 0x00c0 #define AR9170_TX_MAC_MMIC 0x0100 #define AR9170_TX_MAC_HW_DURATION 0x0200 #define AR9170_TX_MAC_QOS_S 10 #define AR9170_TX_MAC_QOS 0x0c00 #define AR9170_TX_MAC_DISABLE_TXOP 0x1000 #define AR9170_TX_MAC_TXOP_RIFS 0x2000 #define AR9170_TX_MAC_IMM_BA 0x4000 /* either-or */ #define AR9170_TX_PHY_MOD_CCK 0x00000000 #define AR9170_TX_PHY_MOD_OFDM 0x00000001 #define AR9170_TX_PHY_MOD_HT 0x00000002 /* depends on modulation */ #define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004 #define AR9170_TX_PHY_GREENFIELD 0x00000004 #define AR9170_TX_PHY_BW_S 3 #define AR9170_TX_PHY_BW (3 << AR9170_TX_PHY_BW_SHIFT) #define AR9170_TX_PHY_BW_20MHZ 0 #define AR9170_TX_PHY_BW_40MHZ 2 #define AR9170_TX_PHY_BW_40MHZ_DUP 3 #define AR9170_TX_PHY_TX_HEAVY_CLIP_S 6 #define AR9170_TX_PHY_TX_HEAVY_CLIP (7 << \ AR9170_TX_PHY_TX_HEAVY_CLIP_S) #define AR9170_TX_PHY_TX_PWR_S 9 #define AR9170_TX_PHY_TX_PWR (0x3f << \ AR9170_TX_PHY_TX_PWR_S) #define AR9170_TX_PHY_TXCHAIN_S 15 #define AR9170_TX_PHY_TXCHAIN (7 << \ AR9170_TX_PHY_TXCHAIN_S) #define AR9170_TX_PHY_TXCHAIN_1 1 /* use for cck, ofdm 6/9/12/18/24 and HT if capable */ #define AR9170_TX_PHY_TXCHAIN_2 5 #define AR9170_TX_PHY_MCS_S 18 #define AR9170_TX_PHY_MCS (0x7f << \ AR9170_TX_PHY_MCS_S) #define AR9170_TX_PHY_RATE_CCK_1M 0x0 #define AR9170_TX_PHY_RATE_CCK_2M 0x1 #define AR9170_TX_PHY_RATE_CCK_5M 0x2 #define AR9170_TX_PHY_RATE_CCK_11M 0x3 /* same as AR9170_RX_PHY_RATE */ #define AR9170_TXRX_PHY_RATE_OFDM_6M 0xb #define AR9170_TXRX_PHY_RATE_OFDM_9M 0xf #define AR9170_TXRX_PHY_RATE_OFDM_12M 0xa #define AR9170_TXRX_PHY_RATE_OFDM_18M 0xe #define AR9170_TXRX_PHY_RATE_OFDM_24M 0x9 #define AR9170_TXRX_PHY_RATE_OFDM_36M 0xd #define AR9170_TXRX_PHY_RATE_OFDM_48M 0x8 #define AR9170_TXRX_PHY_RATE_OFDM_54M 0xc #define AR9170_TXRX_PHY_RATE_HT_MCS0 0x0 #define AR9170_TXRX_PHY_RATE_HT_MCS1 0x1 #define AR9170_TXRX_PHY_RATE_HT_MCS2 0x2 #define AR9170_TXRX_PHY_RATE_HT_MCS3 0x3 #define AR9170_TXRX_PHY_RATE_HT_MCS4 0x4 #define AR9170_TXRX_PHY_RATE_HT_MCS5 0x5 #define AR9170_TXRX_PHY_RATE_HT_MCS6 0x6 #define AR9170_TXRX_PHY_RATE_HT_MCS7 0x7 #define AR9170_TXRX_PHY_RATE_HT_MCS8 0x8 #define AR9170_TXRX_PHY_RATE_HT_MCS9 0x9 #define AR9170_TXRX_PHY_RATE_HT_MCS10 0xa #define AR9170_TXRX_PHY_RATE_HT_MCS11 0xb #define AR9170_TXRX_PHY_RATE_HT_MCS12 0xc #define AR9170_TXRX_PHY_RATE_HT_MCS13 0xd #define AR9170_TXRX_PHY_RATE_HT_MCS14 0xe #define AR9170_TXRX_PHY_RATE_HT_MCS15 0xf #define AR9170_TX_PHY_SHORT_GI 0x80000000 #ifdef __CARL9170FW__ struct ar9170_tx_hw_mac_control { union { struct { /* * Beware of compiler bugs in all gcc pre 4.4! */ u8 erp_prot:2; u8 no_ack:1; u8 backoff:1; u8 burst:1; u8 ampdu:1; u8 enc_mode:2; u8 hw_mmic:1; u8 hw_duration:1; u8 qos_queue:2; u8 disable_txop:1; u8 txop_rifs:1; u8 ba_end:1; u8 probe:1; } __packed; __le16 set; } __packed; } __packed; struct ar9170_tx_hw_phy_control { union { struct { /* * Beware of compiler bugs in all gcc pre 4.4! */ u8 modulation:2; u8 preamble:1; u8 bandwidth:2; u8:1; u8 heavy_clip:3; u8 tx_power:6; u8 chains:3; u8 mcs:7; u8:6; u8 short_gi:1; } __packed; __le32 set; } __packed; } __packed; struct ar9170_tx_rate_info { u8 tries:3; u8 erp_prot:2; u8 ampdu:1; u8 free:2; /* free for use (e.g.:RIFS/TXOP/AMPDU) */ } __packed; struct carl9170_tx_superdesc { __le16 len; u8 rix; u8 cnt; u8 cookie; u8 ampdu_density:3; u8 ampdu_factor:2; u8 ampdu_commit_density:1; u8 ampdu_commit_factor:1; u8 ampdu_unused_bit:1; u8 queue:2; u8 assign_seq:1; u8 vif_id:3; u8 fill_in_tsf:1; u8 cab:1; u8 padding2; struct ar9170_tx_rate_info ri[CARL9170_TX_MAX_RATES]; struct ar9170_tx_hw_phy_control rr[CARL9170_TX_MAX_RETRY_RATES]; } __packed; struct ar9170_tx_hwdesc { __le16 length; struct ar9170_tx_hw_mac_control mac; struct ar9170_tx_hw_phy_control phy; } __packed; struct ar9170_tx_frame { struct ar9170_tx_hwdesc hdr; union { struct ieee80211_hdr i3e; u8 payload[0]; } data; } __packed; struct carl9170_tx_superframe { struct carl9170_tx_superdesc s; struct ar9170_tx_frame f; } __packed __aligned(4); #endif /* __CARL9170FW__ */ struct _ar9170_tx_hwdesc { __le16 length; __le16 mac_control; __le32 phy_control; } __packed; #define CARL9170_TX_SUPER_AMPDU_DENSITY_S 0 #define CARL9170_TX_SUPER_AMPDU_DENSITY 0x7 #define CARL9170_TX_SUPER_AMPDU_FACTOR 0x18 #define CARL9170_TX_SUPER_AMPDU_FACTOR_S 3 #define CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY 0x20 #define CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY_S 5 #define CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR 0x40 #define CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR_S 6 #define CARL9170_TX_SUPER_MISC_QUEUE 0x3 #define CARL9170_TX_SUPER_MISC_QUEUE_S 0 #define CARL9170_TX_SUPER_MISC_ASSIGN_SEQ 0x4 #define CARL9170_TX_SUPER_MISC_VIF_ID 0x38 #define CARL9170_TX_SUPER_MISC_VIF_ID_S 3 #define CARL9170_TX_SUPER_MISC_FILL_IN_TSF 0x40 #define CARL9170_TX_SUPER_MISC_CAB 0x80 #define CARL9170_TX_SUPER_RI_TRIES 0x7 #define CARL9170_TX_SUPER_RI_TRIES_S 0 #define CARL9170_TX_SUPER_RI_ERP_PROT 0x18 #define CARL9170_TX_SUPER_RI_ERP_PROT_S 3 #define CARL9170_TX_SUPER_RI_AMPDU 0x20 #define CARL9170_TX_SUPER_RI_AMPDU_S 5 struct _carl9170_tx_superdesc { __le16 len; u8 rix; u8 cnt; u8 cookie; u8 ampdu_settings; u8 misc; u8 padding; u8 ri[CARL9170_TX_MAX_RATES]; __le32 rr[CARL9170_TX_MAX_RETRY_RATES]; } __packed; struct _carl9170_tx_superframe { struct _carl9170_tx_superdesc s; struct _ar9170_tx_hwdesc f; u8 frame_data[0]; } __packed __aligned(4); #define CARL9170_TX_SUPERDESC_LEN 24 #define AR9170_TX_HWDESC_LEN 8 #define CARL9170_TX_SUPERFRAME_LEN (CARL9170_TX_SUPERDESC_LEN + \ AR9170_TX_HWDESC_LEN) struct ar9170_rx_head { u8 plcp[12]; } __packed; #define AR9170_RX_HEAD_LEN 12 struct ar9170_rx_phystatus { union { struct { u8 rssi_ant0, rssi_ant1, rssi_ant2, rssi_ant0x, rssi_ant1x, rssi_ant2x, rssi_combined; } __packed; u8 rssi[7]; } __packed; u8 evm_stream0[6], evm_stream1[6]; u8 phy_err; } __packed; #define AR9170_RX_PHYSTATUS_LEN 20 struct ar9170_rx_macstatus { u8 SAidx, DAidx; u8 error; u8 status; } __packed; #define AR9170_RX_MACSTATUS_LEN 4 struct ar9170_rx_frame_single { struct ar9170_rx_head phy_head; struct ieee80211_hdr i3e; struct ar9170_rx_phystatus phy_tail; struct ar9170_rx_macstatus macstatus; } __packed; struct ar9170_rx_frame_head { struct ar9170_rx_head phy_head; struct ieee80211_hdr i3e; struct ar9170_rx_macstatus macstatus; } __packed; struct ar9170_rx_frame_middle { struct ieee80211_hdr i3e; struct ar9170_rx_macstatus macstatus; } __packed; struct ar9170_rx_frame_tail { struct ieee80211_hdr i3e; struct ar9170_rx_phystatus phy_tail; struct ar9170_rx_macstatus macstatus; } __packed; struct ar9170_rx_frame { union { struct ar9170_rx_frame_single single; struct ar9170_rx_frame_head head; struct ar9170_rx_frame_middle middle; struct ar9170_rx_frame_tail tail; } __packed; } __packed; static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t) { return (t->SAidx & 0xc0) >> 4 | (t->DAidx & 0xc0) >> 6; } /* * This is an workaround for several undocumented bugs. * Don't mess with the QoS/AC <-> HW Queue map, if you don't * know what you are doing. * * Known problems [hardware]: * * The MAC does not aggregate frames on anything other * than the first HW queue. * * when an AMPDU is placed [in the first hw queue] and * additional frames are already queued on a different * hw queue, the MAC will ALWAYS freeze. * * In a nutshell: The hardware can either do QoS or * Aggregation but not both at the same time. As a * result, this makes the device pretty much useless * for any serious 802.11n setup. */ enum ar9170_txq { AR9170_TXQ_BK = 0, /* TXQ0 */ AR9170_TXQ_BE, /* TXQ1 */ AR9170_TXQ_VI, /* TXQ2 */ AR9170_TXQ_VO, /* TXQ3 */ __AR9170_NUM_TXQ, }; #define AR9170_TXQ_DEPTH 32 #endif /* __CARL9170_SHARED_WLAN_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/version.h0000644000175000017500000000037712026211315024344 0ustar mcgrofmcgrof#ifndef __CARL9170_SHARED_VERSION_H #define __CARL9170_SHARED_VERSION_H #define CARL9170FW_VERSION_YEAR 12 #define CARL9170FW_VERSION_MONTH 7 #define CARL9170FW_VERSION_DAY 7 #define CARL9170FW_VERSION_GIT "1.9.6" #endif /* __CARL9170_SHARED_VERSION_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/tx.c0000644000175000017500000012232212026211315023300 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * 802.11 xmit & status routines * * Copyright 2008, Johannes Berg * Copyright 2009, 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "carl9170.h" #include "hw.h" #include "cmd.h" static inline unsigned int __carl9170_get_queue(struct ar9170 *ar, unsigned int queue) { if (unlikely(modparam_noht)) { return queue; } else { /* * This is just another workaround, until * someone figures out how to get QoS and * AMPDU to play nicely together. */ return 2; /* AC_BE */ } } static inline unsigned int carl9170_get_queue(struct ar9170 *ar, struct sk_buff *skb) { return __carl9170_get_queue(ar, skb_get_queue_mapping(skb)); } static bool is_mem_full(struct ar9170 *ar) { return (DIV_ROUND_UP(IEEE80211_MAX_FRAME_LEN, ar->fw.mem_block_size) > atomic_read(&ar->mem_free_blocks)); } static void carl9170_tx_accounting(struct ar9170 *ar, struct sk_buff *skb) { int queue, i; bool mem_full; atomic_inc(&ar->tx_total_queued); queue = skb_get_queue_mapping(skb); spin_lock_bh(&ar->tx_stats_lock); /* * The driver has to accept the frame, regardless if the queue is * full to the brim, or not. We have to do the queuing internally, * since mac80211 assumes that a driver which can operate with * aggregated frames does not reject frames for this reason. */ ar->tx_stats[queue].len++; ar->tx_stats[queue].count++; mem_full = is_mem_full(ar); for (i = 0; i < ar->hw->queues; i++) { if (mem_full || ar->tx_stats[i].len >= ar->tx_stats[i].limit) { ieee80211_stop_queue(ar->hw, i); ar->queue_stop_timeout[i] = jiffies; } } spin_unlock_bh(&ar->tx_stats_lock); } /* needs rcu_read_lock */ static struct ieee80211_sta *__carl9170_get_tx_sta(struct ar9170 *ar, struct sk_buff *skb) { struct _carl9170_tx_superframe *super = (void *) skb->data; struct ieee80211_hdr *hdr = (void *) super->frame_data; struct ieee80211_vif *vif; unsigned int vif_id; vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >> CARL9170_TX_SUPER_MISC_VIF_ID_S; if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC)) return NULL; vif = rcu_dereference(ar->vif_priv[vif_id].vif); if (unlikely(!vif)) return NULL; /* * Normally we should use wrappers like ieee80211_get_DA to get * the correct peer ieee80211_sta. * * But there is a problem with indirect traffic (broadcasts, or * data which is designated for other stations) in station mode. * The frame will be directed to the AP for distribution and not * to the actual destination. */ return ieee80211_find_sta(vif, hdr->addr1); } static void carl9170_tx_ps_unblock(struct ar9170 *ar, struct sk_buff *skb) { struct ieee80211_sta *sta; struct carl9170_sta_info *sta_info; rcu_read_lock(); sta = __carl9170_get_tx_sta(ar, skb); if (unlikely(!sta)) goto out_rcu; sta_info = (struct carl9170_sta_info *) sta->drv_priv; if (atomic_dec_return(&sta_info->pending_frames) == 0) ieee80211_sta_block_awake(ar->hw, sta, false); out_rcu: rcu_read_unlock(); } static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb) { int queue; queue = skb_get_queue_mapping(skb); spin_lock_bh(&ar->tx_stats_lock); ar->tx_stats[queue].len--; if (!is_mem_full(ar)) { unsigned int i; for (i = 0; i < ar->hw->queues; i++) { if (ar->tx_stats[i].len >= CARL9170_NUM_TX_LIMIT_SOFT) continue; if (ieee80211_queue_stopped(ar->hw, i)) { unsigned long tmp; tmp = jiffies - ar->queue_stop_timeout[i]; if (tmp > ar->max_queue_stop_timeout[i]) ar->max_queue_stop_timeout[i] = tmp; } ieee80211_wake_queue(ar->hw, i); } } spin_unlock_bh(&ar->tx_stats_lock); if (atomic_dec_and_test(&ar->tx_total_queued)) complete(&ar->tx_flush); } static int carl9170_alloc_dev_space(struct ar9170 *ar, struct sk_buff *skb) { struct _carl9170_tx_superframe *super = (void *) skb->data; unsigned int chunks; int cookie = -1; atomic_inc(&ar->mem_allocs); chunks = DIV_ROUND_UP(skb->len, ar->fw.mem_block_size); if (unlikely(atomic_sub_return(chunks, &ar->mem_free_blocks) < 0)) { atomic_add(chunks, &ar->mem_free_blocks); return -ENOSPC; } spin_lock_bh(&ar->mem_lock); cookie = bitmap_find_free_region(ar->mem_bitmap, ar->fw.mem_blocks, 0); spin_unlock_bh(&ar->mem_lock); if (unlikely(cookie < 0)) { atomic_add(chunks, &ar->mem_free_blocks); return -ENOSPC; } super = (void *) skb->data; /* * Cookie #0 serves two special purposes: * 1. The firmware might use it generate BlockACK frames * in responds of an incoming BlockAckReqs. * * 2. Prevent double-free bugs. */ super->s.cookie = (u8) cookie + 1; return 0; } static void carl9170_release_dev_space(struct ar9170 *ar, struct sk_buff *skb) { struct _carl9170_tx_superframe *super = (void *) skb->data; int cookie; /* make a local copy of the cookie */ cookie = super->s.cookie; /* invalidate cookie */ super->s.cookie = 0; /* * Do a out-of-bounds check on the cookie: * * * cookie "0" is reserved and won't be assigned to any * out-going frame. Internally however, it is used to * mark no longer/un-accounted frames and serves as a * cheap way of preventing frames from being freed * twice by _accident_. NB: There is a tiny race... * * * obviously, cookie number is limited by the amount * of available memory blocks, so the number can * never execeed the mem_blocks count. */ if (unlikely(WARN_ON_ONCE(cookie == 0) || WARN_ON_ONCE(cookie > ar->fw.mem_blocks))) return; atomic_add(DIV_ROUND_UP(skb->len, ar->fw.mem_block_size), &ar->mem_free_blocks); spin_lock_bh(&ar->mem_lock); bitmap_release_region(ar->mem_bitmap, cookie - 1, 0); spin_unlock_bh(&ar->mem_lock); } /* Called from any context */ static void carl9170_tx_release(struct kref *ref) { struct ar9170 *ar; struct carl9170_tx_info *arinfo; struct ieee80211_tx_info *txinfo; struct sk_buff *skb; arinfo = container_of(ref, struct carl9170_tx_info, ref); txinfo = container_of((void *) arinfo, struct ieee80211_tx_info, rate_driver_data); skb = container_of((void *) txinfo, struct sk_buff, cb); ar = arinfo->ar; if (WARN_ON_ONCE(!ar)) return; BUILD_BUG_ON( offsetof(struct ieee80211_tx_info, status.ack_signal) != 20); memset(&txinfo->status.ack_signal, 0, sizeof(struct ieee80211_tx_info) - offsetof(struct ieee80211_tx_info, status.ack_signal)); if (atomic_read(&ar->tx_total_queued)) ar->tx_schedule = true; if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) { if (!atomic_read(&ar->tx_ampdu_upload)) ar->tx_ampdu_schedule = true; if (txinfo->flags & IEEE80211_TX_STAT_AMPDU) { struct _carl9170_tx_superframe *super; super = (void *)skb->data; txinfo->status.ampdu_len = super->s.rix; txinfo->status.ampdu_ack_len = super->s.cnt; } else if ((txinfo->flags & IEEE80211_TX_STAT_ACK) && !(txinfo->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { /* * drop redundant tx_status reports: * * 1. ampdu_ack_len of the final tx_status does * include the feedback of this particular frame. * * 2. tx_status_irqsafe only queues up to 128 * tx feedback reports and discards the rest. * * 3. minstrel_ht is picky, it only accepts * reports of frames with the TX_STATUS_AMPDU flag. * * 4. mac80211 is not particularly interested in * feedback either [CTL_REQ_TX_STATUS not set] */ ieee80211_free_txskb(ar->hw, skb); return; } else { /* * Either the frame transmission has failed or * mac80211 requested tx status. */ } } skb_pull(skb, sizeof(struct _carl9170_tx_superframe)); ieee80211_tx_status_irqsafe(ar->hw, skb); } void carl9170_tx_get_skb(struct sk_buff *skb) { struct carl9170_tx_info *arinfo = (void *) (IEEE80211_SKB_CB(skb))->rate_driver_data; kref_get(&arinfo->ref); } int carl9170_tx_put_skb(struct sk_buff *skb) { struct carl9170_tx_info *arinfo = (void *) (IEEE80211_SKB_CB(skb))->rate_driver_data; return kref_put(&arinfo->ref, carl9170_tx_release); } /* Caller must hold the tid_info->lock & rcu_read_lock */ static void carl9170_tx_shift_bm(struct ar9170 *ar, struct carl9170_sta_tid *tid_info, u16 seq) { u16 off; off = SEQ_DIFF(seq, tid_info->bsn); if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS)) return; /* * Sanity check. For each MPDU we set the bit in bitmap and * clear it once we received the tx_status. * But if the bit is already cleared then we've been bitten * by a bug. */ WARN_ON_ONCE(!test_and_clear_bit(off, tid_info->bitmap)); off = SEQ_DIFF(tid_info->snx, tid_info->bsn); if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS)) return; if (!bitmap_empty(tid_info->bitmap, off)) off = find_first_bit(tid_info->bitmap, off); tid_info->bsn += off; tid_info->bsn &= 0x0fff; bitmap_shift_right(tid_info->bitmap, tid_info->bitmap, off, CARL9170_BAW_BITS); } static void carl9170_tx_status_process_ampdu(struct ar9170 *ar, struct sk_buff *skb, struct ieee80211_tx_info *txinfo) { struct _carl9170_tx_superframe *super = (void *) skb->data; struct ieee80211_hdr *hdr = (void *) super->frame_data; struct ieee80211_sta *sta; struct carl9170_sta_info *sta_info; struct carl9170_sta_tid *tid_info; u8 tid; if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) || txinfo->flags & IEEE80211_TX_CTL_INJECTED || (!(super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_AGGR)))) return; rcu_read_lock(); sta = __carl9170_get_tx_sta(ar, skb); if (unlikely(!sta)) goto out_rcu; tid = get_tid_h(hdr); sta_info = (void *) sta->drv_priv; tid_info = rcu_dereference(sta_info->agg[tid]); if (!tid_info) goto out_rcu; spin_lock_bh(&tid_info->lock); if (likely(tid_info->state >= CARL9170_TID_STATE_IDLE)) carl9170_tx_shift_bm(ar, tid_info, get_seq_h(hdr)); if (sta_info->stats[tid].clear) { sta_info->stats[tid].clear = false; sta_info->stats[tid].req = false; sta_info->stats[tid].ampdu_len = 0; sta_info->stats[tid].ampdu_ack_len = 0; } sta_info->stats[tid].ampdu_len++; if (txinfo->status.rates[0].count == 1) sta_info->stats[tid].ampdu_ack_len++; if (!(txinfo->flags & IEEE80211_TX_STAT_ACK)) sta_info->stats[tid].req = true; if (super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_IMM_BA)) { super->s.rix = sta_info->stats[tid].ampdu_len; super->s.cnt = sta_info->stats[tid].ampdu_ack_len; txinfo->flags |= IEEE80211_TX_STAT_AMPDU; if (sta_info->stats[tid].req) txinfo->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; sta_info->stats[tid].clear = true; } spin_unlock_bh(&tid_info->lock); out_rcu: rcu_read_unlock(); } static void carl9170_tx_bar_status(struct ar9170 *ar, struct sk_buff *skb, struct ieee80211_tx_info *tx_info) { struct _carl9170_tx_superframe *super = (void *) skb->data; struct ieee80211_bar *bar = (void *) super->frame_data; /* * Unlike all other frames, the status report for BARs does * not directly come from the hardware as it is incapable of * matching a BA to a previously send BAR. * Instead the RX-path will scan for incoming BAs and set the * IEEE80211_TX_STAT_ACK if it sees one that was likely * caused by a BAR from us. */ if (unlikely(ieee80211_is_back_req(bar->frame_control)) && !(tx_info->flags & IEEE80211_TX_STAT_ACK)) { struct carl9170_bar_list_entry *entry; int queue = skb_get_queue_mapping(skb); rcu_read_lock(); list_for_each_entry_rcu(entry, &ar->bar_list[queue], list) { if (entry->skb == skb) { spin_lock_bh(&ar->bar_list_lock[queue]); list_del_rcu(&entry->list); spin_unlock_bh(&ar->bar_list_lock[queue]); kfree_rcu(entry, head); goto out; } } WARN(1, "bar not found in %d - ra:%pM ta:%pM c:%x ssn:%x\n", queue, bar->ra, bar->ta, bar->control, bar->start_seq_num); out: rcu_read_unlock(); } } void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb, const bool success) { struct ieee80211_tx_info *txinfo; carl9170_tx_accounting_free(ar, skb); txinfo = IEEE80211_SKB_CB(skb); carl9170_tx_bar_status(ar, skb, txinfo); if (success) txinfo->flags |= IEEE80211_TX_STAT_ACK; else ar->tx_ack_failures++; if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) carl9170_tx_status_process_ampdu(ar, skb, txinfo); carl9170_tx_ps_unblock(ar, skb); carl9170_tx_put_skb(skb); } /* This function may be called form any context */ void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb) { struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); atomic_dec(&ar->tx_total_pending); if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) atomic_dec(&ar->tx_ampdu_upload); if (carl9170_tx_put_skb(skb)) tasklet_hi_schedule(&ar->usb_tasklet); } static struct sk_buff *carl9170_get_queued_skb(struct ar9170 *ar, u8 cookie, struct sk_buff_head *queue) { struct sk_buff *skb; spin_lock_bh(&queue->lock); skb_queue_walk(queue, skb) { struct _carl9170_tx_superframe *txc = (void *) skb->data; if (txc->s.cookie != cookie) continue; __skb_unlink(skb, queue); spin_unlock_bh(&queue->lock); carl9170_release_dev_space(ar, skb); return skb; } spin_unlock_bh(&queue->lock); return NULL; } static void carl9170_tx_fill_rateinfo(struct ar9170 *ar, unsigned int rix, unsigned int tries, struct ieee80211_tx_info *txinfo) { unsigned int i; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if (txinfo->status.rates[i].idx < 0) break; if (i == rix) { txinfo->status.rates[i].count = tries; i++; break; } } for (; i < IEEE80211_TX_MAX_RATES; i++) { txinfo->status.rates[i].idx = -1; txinfo->status.rates[i].count = 0; } } static void carl9170_check_queue_stop_timeout(struct ar9170 *ar) { int i; struct sk_buff *skb; struct ieee80211_tx_info *txinfo; struct carl9170_tx_info *arinfo; bool restart = false; for (i = 0; i < ar->hw->queues; i++) { spin_lock_bh(&ar->tx_status[i].lock); skb = skb_peek(&ar->tx_status[i]); if (!skb) goto next; txinfo = IEEE80211_SKB_CB(skb); arinfo = (void *) txinfo->rate_driver_data; if (time_is_before_jiffies(arinfo->timeout + msecs_to_jiffies(CARL9170_QUEUE_STUCK_TIMEOUT)) == true) restart = true; next: spin_unlock_bh(&ar->tx_status[i].lock); } if (restart) { /* * At least one queue has been stuck for long enough. * Give the device a kick and hope it gets back to * work. * * possible reasons may include: * - frames got lost/corrupted (bad connection to the device) * - stalled rx processing/usb controller hiccups * - firmware errors/bugs * - every bug you can think of. * - all bugs you can't... * - ... */ carl9170_restart(ar, CARL9170_RR_STUCK_TX); } } static void carl9170_tx_ampdu_timeout(struct ar9170 *ar) { struct carl9170_sta_tid *iter; struct sk_buff *skb; struct ieee80211_tx_info *txinfo; struct carl9170_tx_info *arinfo; struct ieee80211_sta *sta; rcu_read_lock(); list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) { if (iter->state < CARL9170_TID_STATE_IDLE) continue; spin_lock_bh(&iter->lock); skb = skb_peek(&iter->queue); if (!skb) goto unlock; txinfo = IEEE80211_SKB_CB(skb); arinfo = (void *)txinfo->rate_driver_data; if (time_is_after_jiffies(arinfo->timeout + msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT))) goto unlock; sta = __carl9170_get_tx_sta(ar, skb); if (WARN_ON(!sta)) goto unlock; ieee80211_stop_tx_ba_session(sta, iter->tid); unlock: spin_unlock_bh(&iter->lock); } rcu_read_unlock(); } void carl9170_tx_janitor(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, tx_janitor.work); if (!IS_STARTED(ar)) return; ar->tx_janitor_last_run = jiffies; carl9170_check_queue_stop_timeout(ar); carl9170_tx_ampdu_timeout(ar); if (!atomic_read(&ar->tx_total_queued)) return; ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor, msecs_to_jiffies(CARL9170_TX_TIMEOUT)); } static void __carl9170_tx_process_status(struct ar9170 *ar, const uint8_t cookie, const uint8_t info) { struct sk_buff *skb; struct ieee80211_tx_info *txinfo; unsigned int r, t, q; bool success = true; q = ar9170_qmap[info & CARL9170_TX_STATUS_QUEUE]; skb = carl9170_get_queued_skb(ar, cookie, &ar->tx_status[q]); if (!skb) { /* * We have lost the race to another thread. */ return ; } txinfo = IEEE80211_SKB_CB(skb); if (!(info & CARL9170_TX_STATUS_SUCCESS)) success = false; r = (info & CARL9170_TX_STATUS_RIX) >> CARL9170_TX_STATUS_RIX_S; t = (info & CARL9170_TX_STATUS_TRIES) >> CARL9170_TX_STATUS_TRIES_S; carl9170_tx_fill_rateinfo(ar, r, t, txinfo); carl9170_tx_status(ar, skb, success); } void carl9170_tx_process_status(struct ar9170 *ar, const struct carl9170_rsp *cmd) { unsigned int i; for (i = 0; i < cmd->hdr.ext; i++) { if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) { print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE, (void *) cmd, cmd->hdr.len + 4); break; } __carl9170_tx_process_status(ar, cmd->_tx_status[i].cookie, cmd->_tx_status[i].info); } } static void carl9170_tx_rate_tpc_chains(struct ar9170 *ar, struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate, unsigned int *phyrate, unsigned int *tpc, unsigned int *chains) { struct ieee80211_rate *rate = NULL; u8 *txpower; unsigned int idx; idx = txrate->idx; *tpc = 0; *phyrate = 0; if (txrate->flags & IEEE80211_TX_RC_MCS) { if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { /* +1 dBm for HT40 */ *tpc += 2; if (info->band == IEEE80211_BAND_2GHZ) txpower = ar->power_2G_ht40; else txpower = ar->power_5G_ht40; } else { if (info->band == IEEE80211_BAND_2GHZ) txpower = ar->power_2G_ht20; else txpower = ar->power_5G_ht20; } *phyrate = txrate->idx; *tpc += txpower[idx & 7]; } else { if (info->band == IEEE80211_BAND_2GHZ) { if (idx < 4) txpower = ar->power_2G_cck; else txpower = ar->power_2G_ofdm; } else { txpower = ar->power_5G_leg; idx += 4; } rate = &__carl9170_ratetable[idx]; *tpc += txpower[(rate->hw_value & 0x30) >> 4]; *phyrate = rate->hw_value & 0xf; } if (ar->eeprom.tx_mask == 1) { *chains = AR9170_TX_PHY_TXCHAIN_1; } else { if (!(txrate->flags & IEEE80211_TX_RC_MCS) && rate && rate->bitrate >= 360) *chains = AR9170_TX_PHY_TXCHAIN_1; else *chains = AR9170_TX_PHY_TXCHAIN_2; } *tpc = min_t(unsigned int, *tpc, ar->hw->conf.power_level * 2); } static __le32 carl9170_tx_physet(struct ar9170 *ar, struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate) { unsigned int power = 0, chains = 0, phyrate = 0; __le32 tmp; tmp = cpu_to_le32(0); if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ << AR9170_TX_PHY_BW_S); /* this works because 40 MHz is 2 and dup is 3 */ if (txrate->flags & IEEE80211_TX_RC_DUP_DATA) tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP << AR9170_TX_PHY_BW_S); if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI); if (txrate->flags & IEEE80211_TX_RC_MCS) { SET_VAL(AR9170_TX_PHY_MCS, phyrate, txrate->idx); /* heavy clip control */ tmp |= cpu_to_le32((txrate->idx & 0x7) << AR9170_TX_PHY_TX_HEAVY_CLIP_S); tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT); /* * green field preamble does not work. * * if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) * tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD); */ } else { if (info->band == IEEE80211_BAND_2GHZ) { if (txrate->idx <= AR9170_TX_PHY_RATE_CCK_11M) tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_CCK); else tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM); } else { tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM); } /* * short preamble seems to be broken too. * * if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) * tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE); */ } carl9170_tx_rate_tpc_chains(ar, info, txrate, &phyrate, &power, &chains); tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_MCS, phyrate)); tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TX_PWR, power)); tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TXCHAIN, chains)); return tmp; } static bool carl9170_tx_rts_check(struct ar9170 *ar, struct ieee80211_tx_rate *rate, bool ampdu, bool multi) { switch (ar->erp_mode) { case CARL9170_ERP_AUTO: if (ampdu) break; case CARL9170_ERP_MAC80211: if (!(rate->flags & IEEE80211_TX_RC_USE_RTS_CTS)) break; case CARL9170_ERP_RTS: if (likely(!multi)) return true; default: break; } return false; } static bool carl9170_tx_cts_check(struct ar9170 *ar, struct ieee80211_tx_rate *rate) { switch (ar->erp_mode) { case CARL9170_ERP_AUTO: case CARL9170_ERP_MAC80211: if (!(rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) break; case CARL9170_ERP_CTS: return true; default: break; } return false; } static int carl9170_tx_prepare(struct ar9170 *ar, struct ieee80211_sta *sta, struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct _carl9170_tx_superframe *txc; struct carl9170_vif_info *cvif; struct ieee80211_tx_info *info; struct ieee80211_tx_rate *txrate; struct carl9170_tx_info *arinfo; unsigned int hw_queue; int i; __le16 mac_tmp; u16 len; bool ampdu, no_ack; BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN); BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN); BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES); BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC > ((CARL9170_TX_SUPER_MISC_VIF_ID >> CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1)); hw_queue = ar9170_qmap[carl9170_get_queue(ar, skb)]; hdr = (void *)skb->data; info = IEEE80211_SKB_CB(skb); len = skb->len; /* * Note: If the frame was sent through a monitor interface, * the ieee80211_vif pointer can be NULL. */ if (likely(info->control.vif)) cvif = (void *) info->control.vif->drv_priv; else cvif = NULL; txc = (void *)skb_push(skb, sizeof(*txc)); memset(txc, 0, sizeof(*txc)); SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txc->s.misc, hw_queue); if (likely(cvif)) SET_VAL(CARL9170_TX_SUPER_MISC_VIF_ID, txc->s.misc, cvif->id); if (unlikely(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)) txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB; if (unlikely(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) txc->s.misc |= CARL9170_TX_SUPER_MISC_ASSIGN_SEQ; if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) txc->s.misc |= CARL9170_TX_SUPER_MISC_FILL_IN_TSF; mac_tmp = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | AR9170_TX_MAC_BACKOFF); mac_tmp |= cpu_to_le16((hw_queue << AR9170_TX_MAC_QOS_S) & AR9170_TX_MAC_QOS); no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK); if (unlikely(no_ack)) mac_tmp |= cpu_to_le16(AR9170_TX_MAC_NO_ACK); if (info->control.hw_key) { len += info->control.hw_key->icv_len; switch (info->control.hw_key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: mac_tmp |= cpu_to_le16(AR9170_TX_MAC_ENCR_RC4); break; case WLAN_CIPHER_SUITE_CCMP: mac_tmp |= cpu_to_le16(AR9170_TX_MAC_ENCR_AES); break; default: WARN_ON(1); goto err_out; } } ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU); if (ampdu) { unsigned int density, factor; if (unlikely(!sta || !cvif)) goto err_out; factor = min_t(unsigned int, 1u, sta->ht_cap.ampdu_factor); density = sta->ht_cap.ampdu_density; if (density) { /* * Watch out! * * Otus uses slightly different density values than * those from the 802.11n spec. */ density = max_t(unsigned int, density + 1, 7u); } SET_VAL(CARL9170_TX_SUPER_AMPDU_DENSITY, txc->s.ampdu_settings, density); SET_VAL(CARL9170_TX_SUPER_AMPDU_FACTOR, txc->s.ampdu_settings, factor); for (i = 0; i < CARL9170_TX_MAX_RATES; i++) { txrate = &info->control.rates[i]; if (txrate->idx >= 0) { txc->s.ri[i] = CARL9170_TX_SUPER_RI_AMPDU; if (WARN_ON(!(txrate->flags & IEEE80211_TX_RC_MCS))) { /* * Not sure if it's even possible * to aggregate non-ht rates with * this HW. */ goto err_out; } continue; } txrate->idx = 0; txrate->count = ar->hw->max_rate_tries; } mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR); } /* * NOTE: For the first rate, the ERP & AMPDU flags are directly * taken from mac_control. For all fallback rate, the firmware * updates the mac_control flags from the rate info field. */ for (i = 1; i < CARL9170_TX_MAX_RATES; i++) { txrate = &info->control.rates[i]; if (txrate->idx < 0) break; SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i], txrate->count); if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS << CARL9170_TX_SUPER_RI_ERP_PROT_S); else if (carl9170_tx_cts_check(ar, txrate)) txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS << CARL9170_TX_SUPER_RI_ERP_PROT_S); txc->s.rr[i - 1] = carl9170_tx_physet(ar, info, txrate); } txrate = &info->control.rates[0]; SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[0], txrate->count); if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); else if (carl9170_tx_cts_check(ar, txrate)) mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); txc->s.len = cpu_to_le16(skb->len); txc->f.length = cpu_to_le16(len + FCS_LEN); txc->f.mac_control = mac_tmp; txc->f.phy_control = carl9170_tx_physet(ar, info, txrate); arinfo = (void *)info->rate_driver_data; arinfo->timeout = jiffies; arinfo->ar = ar; kref_init(&arinfo->ref); return 0; err_out: skb_pull(skb, sizeof(*txc)); return -EINVAL; } static void carl9170_set_immba(struct ar9170 *ar, struct sk_buff *skb) { struct _carl9170_tx_superframe *super; super = (void *) skb->data; super->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_IMM_BA); } static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb) { struct _carl9170_tx_superframe *super; int tmp; super = (void *) skb->data; tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_DENSITY) << CARL9170_TX_SUPER_AMPDU_DENSITY_S; /* * If you haven't noticed carl9170_tx_prepare has already filled * in all ampdu spacing & factor parameters. * Now it's the time to check whenever the settings have to be * updated by the firmware, or if everything is still the same. * * There's no sane way to handle different density values with * this hardware, so we may as well just do the compare in the * driver. */ if (tmp != ar->current_density) { ar->current_density = tmp; super->s.ampdu_settings |= CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY; } tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_FACTOR) << CARL9170_TX_SUPER_AMPDU_FACTOR_S; if (tmp != ar->current_factor) { ar->current_factor = tmp; super->s.ampdu_settings |= CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR; } } static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest, struct sk_buff *_src) { struct _carl9170_tx_superframe *dest, *src; dest = (void *) _dest->data; src = (void *) _src->data; /* * The mac80211 rate control algorithm expects that all MPDUs in * an AMPDU share the same tx vectors. * This is not really obvious right now, because the hardware * does the AMPDU setup according to its own rulebook. * Our nicely assembled, strictly monotonic increasing mpdu * chains will be broken up, mashed back together... */ return (dest->f.phy_control == src->f.phy_control); } static void carl9170_tx_ampdu(struct ar9170 *ar) { struct sk_buff_head agg; struct carl9170_sta_tid *tid_info; struct sk_buff *skb, *first; unsigned int i = 0, done_ampdus = 0; u16 seq, queue, tmpssn; atomic_inc(&ar->tx_ampdu_scheduler); ar->tx_ampdu_schedule = false; if (atomic_read(&ar->tx_ampdu_upload)) return; if (!ar->tx_ampdu_list_len) return; __skb_queue_head_init(&agg); rcu_read_lock(); tid_info = rcu_dereference(ar->tx_ampdu_iter); if (WARN_ON_ONCE(!tid_info)) { rcu_read_unlock(); return; } retry: list_for_each_entry_continue_rcu(tid_info, &ar->tx_ampdu_list, list) { i++; if (tid_info->state < CARL9170_TID_STATE_PROGRESS) continue; queue = TID_TO_WME_AC(tid_info->tid); spin_lock_bh(&tid_info->lock); if (tid_info->state != CARL9170_TID_STATE_XMIT) goto processed; tid_info->counter++; first = skb_peek(&tid_info->queue); tmpssn = carl9170_get_seq(first); seq = tid_info->snx; if (unlikely(tmpssn != seq)) { tid_info->state = CARL9170_TID_STATE_IDLE; goto processed; } while ((skb = skb_peek(&tid_info->queue))) { /* strict 0, 1, ..., n - 1, n frame sequence order */ if (unlikely(carl9170_get_seq(skb) != seq)) break; /* don't upload more than AMPDU FACTOR allows. */ if (unlikely(SEQ_DIFF(tid_info->snx, tid_info->bsn) >= (tid_info->max - 1))) break; if (!carl9170_tx_rate_check(ar, skb, first)) break; atomic_inc(&ar->tx_ampdu_upload); tid_info->snx = seq = SEQ_NEXT(seq); __skb_unlink(skb, &tid_info->queue); __skb_queue_tail(&agg, skb); if (skb_queue_len(&agg) >= CARL9170_NUM_TX_AGG_MAX) break; } if (skb_queue_empty(&tid_info->queue) || carl9170_get_seq(skb_peek(&tid_info->queue)) != tid_info->snx) { /* * stop TID, if A-MPDU frames are still missing, * or whenever the queue is empty. */ tid_info->state = CARL9170_TID_STATE_IDLE; } done_ampdus++; processed: spin_unlock_bh(&tid_info->lock); if (skb_queue_empty(&agg)) continue; /* apply ampdu spacing & factor settings */ carl9170_set_ampdu_params(ar, skb_peek(&agg)); /* set aggregation push bit */ carl9170_set_immba(ar, skb_peek_tail(&agg)); spin_lock_bh(&ar->tx_pending[queue].lock); skb_queue_splice_tail_init(&agg, &ar->tx_pending[queue]); spin_unlock_bh(&ar->tx_pending[queue].lock); ar->tx_schedule = true; } if ((done_ampdus++ == 0) && (i++ == 0)) goto retry; rcu_assign_pointer(ar->tx_ampdu_iter, tid_info); rcu_read_unlock(); } static struct sk_buff *carl9170_tx_pick_skb(struct ar9170 *ar, struct sk_buff_head *queue) { struct sk_buff *skb; struct ieee80211_tx_info *info; struct carl9170_tx_info *arinfo; BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); spin_lock_bh(&queue->lock); skb = skb_peek(queue); if (unlikely(!skb)) goto err_unlock; if (carl9170_alloc_dev_space(ar, skb)) goto err_unlock; __skb_unlink(skb, queue); spin_unlock_bh(&queue->lock); info = IEEE80211_SKB_CB(skb); arinfo = (void *) info->rate_driver_data; arinfo->timeout = jiffies; return skb; err_unlock: spin_unlock_bh(&queue->lock); return NULL; } void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb) { struct _carl9170_tx_superframe *super; uint8_t q = 0; ar->tx_dropped++; super = (void *)skb->data; SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, q, ar9170_qmap[carl9170_get_queue(ar, skb)]); __carl9170_tx_process_status(ar, super->s.cookie, q); } static bool carl9170_tx_ps_drop(struct ar9170 *ar, struct sk_buff *skb) { struct ieee80211_sta *sta; struct carl9170_sta_info *sta_info; struct ieee80211_tx_info *tx_info; rcu_read_lock(); sta = __carl9170_get_tx_sta(ar, skb); if (!sta) goto out_rcu; sta_info = (void *) sta->drv_priv; tx_info = IEEE80211_SKB_CB(skb); if (unlikely(sta_info->sleeping) && !(tx_info->flags & (IEEE80211_TX_CTL_NO_PS_BUFFER | IEEE80211_TX_CTL_CLEAR_PS_FILT))) { rcu_read_unlock(); if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) atomic_dec(&ar->tx_ampdu_upload); tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; carl9170_release_dev_space(ar, skb); carl9170_tx_status(ar, skb, false); return true; } out_rcu: rcu_read_unlock(); return false; } static void carl9170_bar_check(struct ar9170 *ar, struct sk_buff *skb) { struct _carl9170_tx_superframe *super = (void *) skb->data; struct ieee80211_bar *bar = (void *) super->frame_data; if (unlikely(ieee80211_is_back_req(bar->frame_control)) && skb->len >= sizeof(struct ieee80211_bar)) { struct carl9170_bar_list_entry *entry; unsigned int queue = skb_get_queue_mapping(skb); entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!WARN_ON_ONCE(!entry)) { entry->skb = skb; spin_lock_bh(&ar->bar_list_lock[queue]); list_add_tail_rcu(&entry->list, &ar->bar_list[queue]); spin_unlock_bh(&ar->bar_list_lock[queue]); } } } static void carl9170_tx(struct ar9170 *ar) { struct sk_buff *skb; unsigned int i, q; bool schedule_garbagecollector = false; ar->tx_schedule = false; if (unlikely(!IS_STARTED(ar))) return; carl9170_usb_handle_tx_err(ar); for (i = 0; i < ar->hw->queues; i++) { while (!skb_queue_empty(&ar->tx_pending[i])) { skb = carl9170_tx_pick_skb(ar, &ar->tx_pending[i]); if (unlikely(!skb)) break; if (unlikely(carl9170_tx_ps_drop(ar, skb))) continue; carl9170_bar_check(ar, skb); atomic_inc(&ar->tx_total_pending); q = __carl9170_get_queue(ar, i); /* * NB: tx_status[i] vs. tx_status[q], * TODO: Move into pick_skb or alloc_dev_space. */ skb_queue_tail(&ar->tx_status[q], skb); /* * increase ref count to "2". * Ref counting is the easiest way to solve the * race between the urb's completion routine: * carl9170_tx_callback * and wlan tx status functions: * carl9170_tx_status/janitor. */ carl9170_tx_get_skb(skb); carl9170_usb_tx(ar, skb); schedule_garbagecollector = true; } } if (!schedule_garbagecollector) return; ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor, msecs_to_jiffies(CARL9170_TX_TIMEOUT)); } static bool carl9170_tx_ampdu_queue(struct ar9170 *ar, struct ieee80211_sta *sta, struct sk_buff *skb) { struct _carl9170_tx_superframe *super = (void *) skb->data; struct carl9170_sta_info *sta_info; struct carl9170_sta_tid *agg; struct sk_buff *iter; u16 tid, seq, qseq, off; bool run = false; tid = carl9170_get_tid(skb); seq = carl9170_get_seq(skb); sta_info = (void *) sta->drv_priv; rcu_read_lock(); agg = rcu_dereference(sta_info->agg[tid]); if (!agg) goto err_unlock_rcu; spin_lock_bh(&agg->lock); if (unlikely(agg->state < CARL9170_TID_STATE_IDLE)) goto err_unlock; /* check if sequence is within the BA window */ if (unlikely(!BAW_WITHIN(agg->bsn, CARL9170_BAW_BITS, seq))) goto err_unlock; if (WARN_ON_ONCE(!BAW_WITHIN(agg->snx, CARL9170_BAW_BITS, seq))) goto err_unlock; off = SEQ_DIFF(seq, agg->bsn); if (WARN_ON_ONCE(test_and_set_bit(off, agg->bitmap))) goto err_unlock; if (likely(BAW_WITHIN(agg->hsn, CARL9170_BAW_BITS, seq))) { __skb_queue_tail(&agg->queue, skb); agg->hsn = seq; goto queued; } skb_queue_reverse_walk(&agg->queue, iter) { qseq = carl9170_get_seq(iter); if (BAW_WITHIN(qseq, CARL9170_BAW_BITS, seq)) { __skb_queue_after(&agg->queue, iter, skb); goto queued; } } __skb_queue_head(&agg->queue, skb); queued: if (unlikely(agg->state != CARL9170_TID_STATE_XMIT)) { if (agg->snx == carl9170_get_seq(skb_peek(&agg->queue))) { agg->state = CARL9170_TID_STATE_XMIT; run = true; } } spin_unlock_bh(&agg->lock); rcu_read_unlock(); return run; err_unlock: spin_unlock_bh(&agg->lock); err_unlock_rcu: rcu_read_unlock(); super->f.mac_control &= ~cpu_to_le16(AR9170_TX_MAC_AGGR); carl9170_tx_status(ar, skb, false); ar->tx_dropped++; return false; } void carl9170_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct ar9170 *ar = hw->priv; struct ieee80211_tx_info *info; struct ieee80211_sta *sta = control->sta; bool run; if (unlikely(!IS_STARTED(ar))) goto err_free; info = IEEE80211_SKB_CB(skb); if (unlikely(carl9170_tx_prepare(ar, sta, skb))) goto err_free; carl9170_tx_accounting(ar, skb); /* * from now on, one has to use carl9170_tx_status to free * all ressouces which are associated with the frame. */ if (sta) { struct carl9170_sta_info *stai = (void *) sta->drv_priv; atomic_inc(&stai->pending_frames); } if (info->flags & IEEE80211_TX_CTL_AMPDU) { run = carl9170_tx_ampdu_queue(ar, sta, skb); if (run) carl9170_tx_ampdu(ar); } else { unsigned int queue = skb_get_queue_mapping(skb); skb_queue_tail(&ar->tx_pending[queue], skb); } carl9170_tx(ar); return; err_free: ar->tx_dropped++; ieee80211_free_txskb(ar->hw, skb); } void carl9170_tx_scheduler(struct ar9170 *ar) { if (ar->tx_ampdu_schedule) carl9170_tx_ampdu(ar); if (ar->tx_schedule) carl9170_tx(ar); } int carl9170_update_beacon(struct ar9170 *ar, const bool submit) { struct sk_buff *skb = NULL; struct carl9170_vif_info *cvif; struct ieee80211_tx_info *txinfo; struct ieee80211_tx_rate *rate; __le32 *data, *old = NULL; unsigned int plcp, power, chains; u32 word, ht1, off, addr, len; int i = 0, err = 0; rcu_read_lock(); cvif = rcu_dereference(ar->beacon_iter); retry: if (ar->vifs == 0 || !cvif) goto out_unlock; list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) { if (cvif->active && cvif->enable_beacon) goto found; } if (!ar->beacon_enabled || i++) goto out_unlock; goto retry; found: rcu_assign_pointer(ar->beacon_iter, cvif); skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif), NULL, NULL); if (!skb) { err = -ENOMEM; goto err_free; } txinfo = IEEE80211_SKB_CB(skb); spin_lock_bh(&ar->beacon_lock); data = (__le32 *)skb->data; if (cvif->beacon) old = (__le32 *)cvif->beacon->data; off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX; addr = ar->fw.beacon_addr + off; len = roundup(skb->len + FCS_LEN, 4); if ((off + len) > ar->fw.beacon_max_len) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "beacon does not " "fit into device memory!\n"); } err = -EINVAL; goto err_unlock; } if (len > AR9170_MAC_BCN_LENGTH_MAX) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "no support for beacons " "bigger than %d (yours:%d).\n", AR9170_MAC_BCN_LENGTH_MAX, len); } err = -EMSGSIZE; goto err_unlock; } ht1 = AR9170_MAC_BCN_HT1_TX_ANT0; rate = &txinfo->control.rates[0]; carl9170_tx_rate_tpc_chains(ar, txinfo, rate, &plcp, &power, &chains); if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) { if (plcp <= AR9170_TX_PHY_RATE_CCK_11M) plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400; else plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010; } else { ht1 |= AR9170_MAC_BCN_HT1_HT_EN; if (rate->flags & IEEE80211_TX_RC_SHORT_GI) plcp |= AR9170_MAC_BCN_HT2_SGI; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED; plcp |= AR9170_MAC_BCN_HT2_BW40; } if (rate->flags & IEEE80211_TX_RC_DUP_DATA) { ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP; plcp |= AR9170_MAC_BCN_HT2_BW40; } SET_VAL(AR9170_MAC_BCN_HT2_LEN, plcp, skb->len + FCS_LEN); } SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, ht1, 7); SET_VAL(AR9170_MAC_BCN_HT1_TPC, ht1, power); SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, ht1, chains); if (chains == AR9170_TX_PHY_TXCHAIN_2) ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1; carl9170_async_regwrite_begin(ar); carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT1, ht1); if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp); else carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT2, plcp); for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) { /* * XXX: This accesses beyond skb data for up * to the last 3 bytes!! */ if (old && (data[i] == old[i])) continue; word = le32_to_cpu(data[i]); carl9170_async_regwrite(addr + 4 * i, word); } carl9170_async_regwrite_finish(); dev_kfree_skb_any(cvif->beacon); cvif->beacon = NULL; err = carl9170_async_regwrite_result(); if (!err) cvif->beacon = skb; spin_unlock_bh(&ar->beacon_lock); if (err) goto err_free; if (submit) { err = carl9170_bcn_ctrl(ar, cvif->id, CARL9170_BCN_CTRL_CAB_TRIGGER, addr, skb->len + FCS_LEN); if (err) goto err_free; } out_unlock: rcu_read_unlock(); return 0; err_unlock: spin_unlock_bh(&ar->beacon_lock); err_free: rcu_read_unlock(); dev_kfree_skb_any(skb); return err; } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/rx.c0000644000175000017500000005701012026211315023277 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * 802.11 & command trap routines * * Copyright 2008, Johannes Berg * Copyright 2009, 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "carl9170.h" #include "hw.h" #include "cmd.h" static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len) { bool restart = false; enum carl9170_restart_reasons reason = CARL9170_RR_NO_REASON; if (len > 3) { if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) { ar->fw.err_counter++; if (ar->fw.err_counter > 3) { restart = true; reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS; } } if (memcmp(buf, CARL9170_BUG_MAGIC, 3) == 0) { ar->fw.bug_counter++; restart = true; reason = CARL9170_RR_FATAL_FIRMWARE_ERROR; } } wiphy_info(ar->hw->wiphy, "FW: %.*s\n", len, buf); if (restart) carl9170_restart(ar, reason); } static void carl9170_handle_ps(struct ar9170 *ar, struct carl9170_rsp *rsp) { u32 ps; bool new_ps; ps = le32_to_cpu(rsp->psm.state); new_ps = (ps & CARL9170_PSM_COUNTER) != CARL9170_PSM_WAKE; if (ar->ps.state != new_ps) { if (!new_ps) { ar->ps.sleep_ms = jiffies_to_msecs(jiffies - ar->ps.last_action); } ar->ps.last_action = jiffies; ar->ps.state = new_ps; } } static int carl9170_check_sequence(struct ar9170 *ar, unsigned int seq) { if (ar->cmd_seq < -1) return 0; /* * Initialize Counter */ if (ar->cmd_seq < 0) ar->cmd_seq = seq; /* * The sequence is strictly monotonic increasing and it never skips! * * Therefore we can safely assume that whenever we received an * unexpected sequence we have lost some valuable data. */ if (seq != ar->cmd_seq) { int count; count = (seq - ar->cmd_seq) % ar->fw.cmd_bufs; wiphy_err(ar->hw->wiphy, "lost %d command responses/traps! " "w:%d g:%d\n", count, ar->cmd_seq, seq); carl9170_restart(ar, CARL9170_RR_LOST_RSP); return -EIO; } ar->cmd_seq = (ar->cmd_seq + 1) % ar->fw.cmd_bufs; return 0; } static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) { /* * Some commands may have a variable response length * and we cannot predict the correct length in advance. * So we only check if we provided enough space for the data. */ if (unlikely(ar->readlen != (len - 4))) { dev_warn(&ar->udev->dev, "received invalid command response:" "got %d, instead of %d\n", len - 4, ar->readlen); print_hex_dump_bytes("carl9170 cmd:", DUMP_PREFIX_OFFSET, ar->cmd_buf, (ar->cmd.hdr.len + 4) & 0x3f); print_hex_dump_bytes("carl9170 rsp:", DUMP_PREFIX_OFFSET, buffer, len); /* * Do not complete. The command times out, * and we get a stack trace from there. */ carl9170_restart(ar, CARL9170_RR_INVALID_RSP); } spin_lock(&ar->cmd_lock); if (ar->readbuf) { if (len >= 4) memcpy(ar->readbuf, buffer + 4, len - 4); ar->readbuf = NULL; } complete(&ar->cmd_wait); spin_unlock(&ar->cmd_lock); } void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) { struct carl9170_rsp *cmd = buf; struct ieee80211_vif *vif; if (carl9170_check_sequence(ar, cmd->hdr.seq)) return; if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) { if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG)) carl9170_cmd_callback(ar, len, buf); return; } if (unlikely(cmd->hdr.len != (len - 4))) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "FW: received over-/under" "sized event %x (%d, but should be %d).\n", cmd->hdr.cmd, cmd->hdr.len, len - 4); print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); } return; } /* hardware event handlers */ switch (cmd->hdr.cmd) { case CARL9170_RSP_PRETBTT: /* pre-TBTT event */ rcu_read_lock(); vif = carl9170_get_main_vif(ar); if (!vif) { rcu_read_unlock(); break; } switch (vif->type) { case NL80211_IFTYPE_STATION: carl9170_handle_ps(ar, cmd); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: carl9170_update_beacon(ar, true); break; default: break; } rcu_read_unlock(); break; case CARL9170_RSP_TXCOMP: /* TX status notification */ carl9170_tx_process_status(ar, cmd); break; case CARL9170_RSP_BEACON_CONFIG: /* * (IBSS) beacon send notification * bytes: 04 c2 XX YY B4 B3 B2 B1 * * XX always 80 * YY always 00 * B1-B4 "should" be the number of send out beacons. */ break; case CARL9170_RSP_ATIM: /* End of Atim Window */ break; case CARL9170_RSP_WATCHDOG: /* Watchdog Interrupt */ carl9170_restart(ar, CARL9170_RR_WATCHDOG); break; case CARL9170_RSP_TEXT: /* firmware debug */ carl9170_dbg_message(ar, (char *)buf + 4, len - 4); break; case CARL9170_RSP_HEXDUMP: wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4); print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE, (char *)buf + 4, len - 4); break; case CARL9170_RSP_RADAR: if (!net_ratelimit()) break; wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this " "incident to linux-wireless@vger.kernel.org !\n"); break; case CARL9170_RSP_GPIO: #ifdef CONFIG_CARL9170_WPC if (ar->wps.pbc) { bool state = !!(cmd->gpio.gpio & cpu_to_le32( AR9170_GPIO_PORT_WPS_BUTTON_PRESSED)); if (state != ar->wps.pbc_state) { ar->wps.pbc_state = state; input_report_key(ar->wps.pbc, KEY_WPS_BUTTON, state); input_sync(ar->wps.pbc); } } #endif /* CONFIG_CARL9170_WPC */ break; case CARL9170_RSP_BOOT: complete(&ar->fw_boot_wait); break; default: wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n", cmd->hdr.cmd); print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); break; } } static int carl9170_rx_mac_status(struct ar9170 *ar, struct ar9170_rx_head *head, struct ar9170_rx_macstatus *mac, struct ieee80211_rx_status *status) { struct ieee80211_channel *chan; u8 error, decrypt; BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12); BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4); error = mac->error; if (error & AR9170_RX_ERROR_WRONG_RA) { if (!ar->sniffer_enabled) return -EINVAL; } if (error & AR9170_RX_ERROR_PLCP) { if (!(ar->filter_state & FIF_PLCPFAIL)) return -EINVAL; status->flag |= RX_FLAG_FAILED_PLCP_CRC; } if (error & AR9170_RX_ERROR_FCS) { ar->tx_fcs_errors++; if (!(ar->filter_state & FIF_FCSFAIL)) return -EINVAL; status->flag |= RX_FLAG_FAILED_FCS_CRC; } decrypt = ar9170_get_decrypt_type(mac); if (!(decrypt & AR9170_RX_ENC_SOFTWARE) && decrypt != AR9170_ENC_ALG_NONE) { if ((decrypt == AR9170_ENC_ALG_TKIP) && (error & AR9170_RX_ERROR_MMIC)) status->flag |= RX_FLAG_MMIC_ERROR; status->flag |= RX_FLAG_DECRYPTED; } if (error & AR9170_RX_ERROR_DECRYPT && !ar->sniffer_enabled) return -ENODATA; error &= ~(AR9170_RX_ERROR_MMIC | AR9170_RX_ERROR_FCS | AR9170_RX_ERROR_WRONG_RA | AR9170_RX_ERROR_DECRYPT | AR9170_RX_ERROR_PLCP); /* drop any other error frames */ if (unlikely(error)) { /* TODO: update netdevice's RX dropped/errors statistics */ if (net_ratelimit()) wiphy_dbg(ar->hw->wiphy, "received frame with " "suspicious error code (%#x).\n", error); return -EINVAL; } chan = ar->channel; if (chan) { status->band = chan->band; status->freq = chan->center_freq; } switch (mac->status & AR9170_RX_STATUS_MODULATION) { case AR9170_RX_STATUS_MODULATION_CCK: if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE) status->flag |= RX_FLAG_SHORTPRE; switch (head->plcp[0]) { case AR9170_RX_PHY_RATE_CCK_1M: status->rate_idx = 0; break; case AR9170_RX_PHY_RATE_CCK_2M: status->rate_idx = 1; break; case AR9170_RX_PHY_RATE_CCK_5M: status->rate_idx = 2; break; case AR9170_RX_PHY_RATE_CCK_11M: status->rate_idx = 3; break; default: if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "invalid plcp cck " "rate (%x).\n", head->plcp[0]); } return -EINVAL; } break; case AR9170_RX_STATUS_MODULATION_DUPOFDM: case AR9170_RX_STATUS_MODULATION_OFDM: switch (head->plcp[0] & 0xf) { case AR9170_TXRX_PHY_RATE_OFDM_6M: status->rate_idx = 0; break; case AR9170_TXRX_PHY_RATE_OFDM_9M: status->rate_idx = 1; break; case AR9170_TXRX_PHY_RATE_OFDM_12M: status->rate_idx = 2; break; case AR9170_TXRX_PHY_RATE_OFDM_18M: status->rate_idx = 3; break; case AR9170_TXRX_PHY_RATE_OFDM_24M: status->rate_idx = 4; break; case AR9170_TXRX_PHY_RATE_OFDM_36M: status->rate_idx = 5; break; case AR9170_TXRX_PHY_RATE_OFDM_48M: status->rate_idx = 6; break; case AR9170_TXRX_PHY_RATE_OFDM_54M: status->rate_idx = 7; break; default: if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "invalid plcp ofdm " "rate (%x).\n", head->plcp[0]); } return -EINVAL; } if (status->band == IEEE80211_BAND_2GHZ) status->rate_idx += 4; break; case AR9170_RX_STATUS_MODULATION_HT: if (head->plcp[3] & 0x80) status->flag |= RX_FLAG_40MHZ; if (head->plcp[6] & 0x80) status->flag |= RX_FLAG_SHORT_GI; status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f); status->flag |= RX_FLAG_HT; break; default: BUG(); return -ENOSYS; } return 0; } static void carl9170_rx_phy_status(struct ar9170 *ar, struct ar9170_rx_phystatus *phy, struct ieee80211_rx_status *status) { int i; BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20); for (i = 0; i < 3; i++) if (phy->rssi[i] != 0x80) status->antenna |= BIT(i); /* post-process RSSI */ for (i = 0; i < 7; i++) if (phy->rssi[i] & 0x80) phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f; /* TODO: we could do something with phy_errors */ status->signal = ar->noise[0] + phy->rssi_combined; } static struct sk_buff *carl9170_rx_copy_data(u8 *buf, int len) { struct sk_buff *skb; int reserved = 0; struct ieee80211_hdr *hdr = (void *) buf; if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *qc = ieee80211_get_qos_ctl(hdr); reserved += NET_IP_ALIGN; if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) reserved += NET_IP_ALIGN; } if (ieee80211_has_a4(hdr->frame_control)) reserved += NET_IP_ALIGN; reserved = 32 + (reserved & NET_IP_ALIGN); skb = dev_alloc_skb(len + reserved); if (likely(skb)) { skb_reserve(skb, reserved); memcpy(skb_put(skb, len), buf, len); } return skb; } static u8 *carl9170_find_ie(u8 *data, unsigned int len, u8 ie) { struct ieee80211_mgmt *mgmt = (void *)data; u8 *pos, *end; pos = (u8 *)mgmt->u.beacon.variable; end = data + len; while (pos < end) { if (pos + 2 + pos[1] > end) return NULL; if (pos[0] == ie) return pos; pos += 2 + pos[1]; } return NULL; } /* * NOTE: * * The firmware is in charge of waking up the device just before * the AP is expected to transmit the next beacon. * * This leaves the driver with the important task of deciding when * to set the PHY back to bed again. */ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) { struct ieee80211_hdr *hdr = data; struct ieee80211_tim_ie *tim_ie; u8 *tim; u8 tim_len; bool cam; if (likely(!(ar->hw->conf.flags & IEEE80211_CONF_PS))) return; /* check if this really is a beacon */ if (!ieee80211_is_beacon(hdr->frame_control)) return; /* min. beacon length + FCS_LEN */ if (len <= 40 + FCS_LEN) return; /* and only beacons from the associated BSSID, please */ if (!ether_addr_equal(hdr->addr3, ar->common.curbssid) || !ar->common.curaid) return; ar->ps.last_beacon = jiffies; tim = carl9170_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); if (!tim) return; if (tim[1] < sizeof(*tim_ie)) return; tim_len = tim[1]; tim_ie = (struct ieee80211_tim_ie *) &tim[2]; if (!WARN_ON_ONCE(!ar->hw->conf.ps_dtim_period)) ar->ps.dtim_counter = (tim_ie->dtim_count - 1) % ar->hw->conf.ps_dtim_period; /* Check whenever the PHY can be turned off again. */ /* 1. What about buffered unicast traffic for our AID? */ cam = ieee80211_check_tim(tim_ie, tim_len, ar->common.curaid); /* 2. Maybe the AP wants to send multicast/broadcast data? */ cam |= !!(tim_ie->bitmap_ctrl & 0x01); if (!cam) { /* back to low-power land. */ ar->ps.off_override &= ~PS_OFF_BCN; carl9170_ps_check(ar); } else { /* force CAM */ ar->ps.off_override |= PS_OFF_BCN; } } static void carl9170_ba_check(struct ar9170 *ar, void *data, unsigned int len) { struct ieee80211_bar *bar = (void *) data; struct carl9170_bar_list_entry *entry; unsigned int queue; if (likely(!ieee80211_is_back(bar->frame_control))) return; if (len <= sizeof(*bar) + FCS_LEN) return; queue = TID_TO_WME_AC(((le16_to_cpu(bar->control) & IEEE80211_BAR_CTRL_TID_INFO_MASK) >> IEEE80211_BAR_CTRL_TID_INFO_SHIFT) & 7); rcu_read_lock(); list_for_each_entry_rcu(entry, &ar->bar_list[queue], list) { struct sk_buff *entry_skb = entry->skb; struct _carl9170_tx_superframe *super = (void *)entry_skb->data; struct ieee80211_bar *entry_bar = (void *)super->frame_data; #define TID_CHECK(a, b) ( \ ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \ ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \ if (bar->start_seq_num == entry_bar->start_seq_num && TID_CHECK(bar->control, entry_bar->control) && compare_ether_addr(bar->ra, entry_bar->ta) == 0 && compare_ether_addr(bar->ta, entry_bar->ra) == 0) { struct ieee80211_tx_info *tx_info; tx_info = IEEE80211_SKB_CB(entry_skb); tx_info->flags |= IEEE80211_TX_STAT_ACK; spin_lock_bh(&ar->bar_list_lock[queue]); list_del_rcu(&entry->list); spin_unlock_bh(&ar->bar_list_lock[queue]); kfree_rcu(entry, head); break; } } rcu_read_unlock(); #undef TID_CHECK } static bool carl9170_ampdu_check(struct ar9170 *ar, u8 *buf, u8 ms, struct ieee80211_rx_status *rx_status) { __le16 fc; if ((ms & AR9170_RX_STATUS_MPDU) == AR9170_RX_STATUS_MPDU_SINGLE) { /* * This frame is not part of an aMPDU. * Therefore it is not subjected to any * of the following content restrictions. */ return true; } rx_status->flag |= RX_FLAG_AMPDU_DETAILS | RX_FLAG_AMPDU_LAST_KNOWN; rx_status->ampdu_reference = ar->ampdu_ref; /* * "802.11n - 7.4a.3 A-MPDU contents" describes in which contexts * certain frame types can be part of an aMPDU. * * In order to keep the processing cost down, I opted for a * stateless filter solely based on the frame control field. */ fc = ((struct ieee80211_hdr *)buf)->frame_control; if (ieee80211_is_data_qos(fc) && ieee80211_is_data_present(fc)) return true; if (ieee80211_is_ack(fc) || ieee80211_is_back(fc) || ieee80211_is_back_req(fc)) return true; if (ieee80211_is_action(fc)) return true; return false; } /* * If the frame alignment is right (or the kernel has * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there * is only a single MPDU in the USB frame, then we could * submit to mac80211 the SKB directly. However, since * there may be multiple packets in one SKB in stream * mode, and we need to observe the proper ordering, * this is non-trivial. */ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) { struct ar9170_rx_head *head; struct ar9170_rx_macstatus *mac; struct ar9170_rx_phystatus *phy = NULL; struct ieee80211_rx_status status; struct sk_buff *skb; int mpdu_len; u8 mac_status; if (!IS_STARTED(ar)) return; if (unlikely(len < sizeof(*mac))) goto drop; memset(&status, 0, sizeof(status)); mpdu_len = len - sizeof(*mac); mac = (void *)(buf + mpdu_len); mac_status = mac->status; switch (mac_status & AR9170_RX_STATUS_MPDU) { case AR9170_RX_STATUS_MPDU_FIRST: ar->ampdu_ref++; /* Aggregated MPDUs start with an PLCP header */ if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) { head = (void *) buf; /* * The PLCP header needs to be cached for the * following MIDDLE + LAST A-MPDU packets. * * So, if you are wondering why all frames seem * to share a common RX status information, * then you have the answer right here... */ memcpy(&ar->rx_plcp, (void *) buf, sizeof(struct ar9170_rx_head)); mpdu_len -= sizeof(struct ar9170_rx_head); buf += sizeof(struct ar9170_rx_head); ar->rx_has_plcp = true; } else { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "plcp info " "is clipped.\n"); } goto drop; } break; case AR9170_RX_STATUS_MPDU_LAST: status.flag |= RX_FLAG_AMPDU_IS_LAST; /* * The last frame of an A-MPDU has an extra tail * which does contain the phy status of the whole * aggregate. */ if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) { mpdu_len -= sizeof(struct ar9170_rx_phystatus); phy = (void *)(buf + mpdu_len); } else { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "frame tail " "is clipped.\n"); } goto drop; } case AR9170_RX_STATUS_MPDU_MIDDLE: /* These are just data + mac status */ if (unlikely(!ar->rx_has_plcp)) { if (!net_ratelimit()) return; wiphy_err(ar->hw->wiphy, "rx stream does not start " "with a first_mpdu frame tag.\n"); goto drop; } head = &ar->rx_plcp; break; case AR9170_RX_STATUS_MPDU_SINGLE: /* single mpdu has both: plcp (head) and phy status (tail) */ head = (void *) buf; mpdu_len -= sizeof(struct ar9170_rx_head); mpdu_len -= sizeof(struct ar9170_rx_phystatus); buf += sizeof(struct ar9170_rx_head); phy = (void *)(buf + mpdu_len); break; default: BUG_ON(1); break; } /* FC + DU + RA + FCS */ if (unlikely(mpdu_len < (2 + 2 + ETH_ALEN + FCS_LEN))) goto drop; if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) goto drop; if (!carl9170_ampdu_check(ar, buf, mac_status, &status)) goto drop; if (phy) carl9170_rx_phy_status(ar, phy, &status); carl9170_ps_beacon(ar, buf, mpdu_len); carl9170_ba_check(ar, buf, mpdu_len); skb = carl9170_rx_copy_data(buf, mpdu_len); if (!skb) goto drop; memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); ieee80211_rx(ar->hw, skb); return; drop: ar->rx_dropped++; } static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf, const unsigned int resplen) { struct carl9170_rsp *cmd; int i = 0; while (i < resplen) { cmd = (void *) &respbuf[i]; i += cmd->hdr.len + 4; if (unlikely(i > resplen)) break; carl9170_handle_command_response(ar, cmd, cmd->hdr.len + 4); } if (unlikely(i != resplen)) { if (!net_ratelimit()) return; wiphy_err(ar->hw->wiphy, "malformed firmware trap:\n"); print_hex_dump_bytes("rxcmd:", DUMP_PREFIX_OFFSET, respbuf, resplen); } } static void __carl9170_rx(struct ar9170 *ar, u8 *buf, unsigned int len) { unsigned int i = 0; /* weird thing, but this is the same in the original driver */ while (len > 2 && i < 12 && buf[0] == 0xff && buf[1] == 0xff) { i += 2; len -= 2; buf += 2; } if (unlikely(len < 4)) return; /* found the 6 * 0xffff marker? */ if (i == 12) carl9170_rx_untie_cmds(ar, buf, len); else carl9170_handle_mpdu(ar, buf, len); } static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len) { unsigned int tlen, wlen = 0, clen = 0; struct ar9170_stream *rx_stream; u8 *tbuf; tbuf = buf; tlen = len; while (tlen >= 4) { rx_stream = (void *) tbuf; clen = le16_to_cpu(rx_stream->length); wlen = ALIGN(clen, 4); /* check if this is stream has a valid tag.*/ if (rx_stream->tag != cpu_to_le16(AR9170_RX_STREAM_TAG)) { /* * TODO: handle the highly unlikely event that the * corrupted stream has the TAG at the right position. */ /* check if the frame can be repaired. */ if (!ar->rx_failover_missing) { /* this is not "short read". */ if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "missing tag!\n"); } __carl9170_rx(ar, tbuf, tlen); return; } if (ar->rx_failover_missing > tlen) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "possible multi " "stream corruption!\n"); goto err_telluser; } else { goto err_silent; } } memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); ar->rx_failover_missing -= tlen; if (ar->rx_failover_missing <= 0) { /* * nested carl9170_rx_stream call! * * termination is guaranteed, even when the * combined frame also have an element with * a bad tag. */ ar->rx_failover_missing = 0; carl9170_rx_stream(ar, ar->rx_failover->data, ar->rx_failover->len); skb_reset_tail_pointer(ar->rx_failover); skb_trim(ar->rx_failover, 0); } return; } /* check if stream is clipped */ if (wlen > tlen - 4) { if (ar->rx_failover_missing) { /* TODO: handle double stream corruption. */ if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "double rx " "stream corruption!\n"); goto err_telluser; } else { goto err_silent; } } /* * save incomplete data set. * the firmware will resend the missing bits when * the rx - descriptor comes round again. */ memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen); ar->rx_failover_missing = clen - tlen; return; } __carl9170_rx(ar, rx_stream->payload, clen); tbuf += wlen + 4; tlen -= wlen + 4; } if (tlen) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "%d bytes of unprocessed " "data left in rx stream!\n", tlen); } goto err_telluser; } return; err_telluser: wiphy_err(ar->hw->wiphy, "damaged RX stream data [want:%d, " "data:%d, rx:%d, pending:%d ]\n", clen, wlen, tlen, ar->rx_failover_missing); if (ar->rx_failover_missing) print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET, ar->rx_failover->data, ar->rx_failover->len); print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET, buf, len); wiphy_err(ar->hw->wiphy, "please check your hardware and cables, if " "you see this message frequently.\n"); err_silent: if (ar->rx_failover_missing) { skb_reset_tail_pointer(ar->rx_failover); skb_trim(ar->rx_failover, 0); ar->rx_failover_missing = 0; } } void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len) { if (ar->fw.rx_stream) carl9170_rx_stream(ar, buf, len); else __carl9170_rx(ar, buf, len); } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/phy.h0000644000175000017500000006164312026211315023462 0ustar mcgrofmcgrof/* * Shared Atheros AR9170 Header * * PHY register map * * Copyright (c) 2008-2009 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CARL9170_SHARED_PHY_H #define __CARL9170_SHARED_PHY_H #define AR9170_PHY_REG_BASE (0x1bc000 + 0x9800) #define AR9170_PHY_REG(_n) (AR9170_PHY_REG_BASE + \ ((_n) << 2)) #define AR9170_PHY_REG_TEST (AR9170_PHY_REG_BASE + 0x0000) #define AR9170_PHY_TEST_AGC_CLR 0x10000000 #define AR9170_PHY_TEST_RFSILENT_BB 0x00002000 #define AR9170_PHY_REG_TURBO (AR9170_PHY_REG_BASE + 0x0004) #define AR9170_PHY_TURBO_FC_TURBO_MODE 0x00000001 #define AR9170_PHY_TURBO_FC_TURBO_SHORT 0x00000002 #define AR9170_PHY_TURBO_FC_DYN2040_EN 0x00000004 #define AR9170_PHY_TURBO_FC_DYN2040_PRI_ONLY 0x00000008 #define AR9170_PHY_TURBO_FC_DYN2040_PRI_CH 0x00000010 /* For 25 MHz channel spacing -- not used but supported by hw */ #define AR9170_PHY_TURBO_FC_DYN2040_EXT_CH 0x00000020 #define AR9170_PHY_TURBO_FC_HT_EN 0x00000040 #define AR9170_PHY_TURBO_FC_SHORT_GI_40 0x00000080 #define AR9170_PHY_TURBO_FC_WALSH 0x00000100 #define AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 0x00000200 #define AR9170_PHY_TURBO_FC_ENABLE_DAC_FIFO 0x00000800 #define AR9170_PHY_REG_TEST2 (AR9170_PHY_REG_BASE + 0x0008) #define AR9170_PHY_REG_TIMING2 (AR9170_PHY_REG_BASE + 0x0010) #define AR9170_PHY_TIMING2_USE_FORCE 0x00001000 #define AR9170_PHY_TIMING2_FORCE 0x00000fff #define AR9170_PHY_TIMING2_FORCE_S 0 #define AR9170_PHY_REG_TIMING3 (AR9170_PHY_REG_BASE + 0x0014) #define AR9170_PHY_TIMING3_DSC_EXP 0x0001e000 #define AR9170_PHY_TIMING3_DSC_EXP_S 13 #define AR9170_PHY_TIMING3_DSC_MAN 0xfffe0000 #define AR9170_PHY_TIMING3_DSC_MAN_S 17 #define AR9170_PHY_REG_CHIP_ID (AR9170_PHY_REG_BASE + 0x0018) #define AR9170_PHY_CHIP_ID_REV_0 0x80 #define AR9170_PHY_CHIP_ID_REV_1 0x81 #define AR9170_PHY_CHIP_ID_9160_REV_0 0xb0 #define AR9170_PHY_REG_ACTIVE (AR9170_PHY_REG_BASE + 0x001c) #define AR9170_PHY_ACTIVE_EN 0x00000001 #define AR9170_PHY_ACTIVE_DIS 0x00000000 #define AR9170_PHY_REG_RF_CTL2 (AR9170_PHY_REG_BASE + 0x0024) #define AR9170_PHY_RF_CTL2_TX_END_DATA_START 0x000000ff #define AR9170_PHY_RF_CTL2_TX_END_DATA_START_S 0 #define AR9170_PHY_RF_CTL2_TX_END_PA_ON 0x0000ff00 #define AR9170_PHY_RF_CTL2_TX_END_PA_ON_S 8 #define AR9170_PHY_REG_RF_CTL3 (AR9170_PHY_REG_BASE + 0x0028) #define AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON 0x00ff0000 #define AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON_S 16 #define AR9170_PHY_REG_ADC_CTL (AR9170_PHY_REG_BASE + 0x002c) #define AR9170_PHY_ADC_CTL_OFF_INBUFGAIN 0x00000003 #define AR9170_PHY_ADC_CTL_OFF_INBUFGAIN_S 0 #define AR9170_PHY_ADC_CTL_OFF_PWDDAC 0x00002000 #define AR9170_PHY_ADC_CTL_OFF_PWDBANDGAP 0x00004000 #define AR9170_PHY_ADC_CTL_OFF_PWDADC 0x00008000 #define AR9170_PHY_ADC_CTL_ON_INBUFGAIN 0x00030000 #define AR9170_PHY_ADC_CTL_ON_INBUFGAIN_S 16 #define AR9170_PHY_REG_ADC_SERIAL_CTL (AR9170_PHY_REG_BASE + 0x0030) #define AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC 0x00000000 #define AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO 0x00000001 #define AR9170_PHY_REG_RF_CTL4 (AR9170_PHY_REG_BASE + 0x0034) #define AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF 0xff000000 #define AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF_S 24 #define AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF 0x00ff0000 #define AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF_S 16 #define AR9170_PHY_RF_CTL4_FRAME_XPAB_ON 0x0000ff00 #define AR9170_PHY_RF_CTL4_FRAME_XPAB_ON_S 8 #define AR9170_PHY_RF_CTL4_FRAME_XPAA_ON 0x000000ff #define AR9170_PHY_RF_CTL4_FRAME_XPAA_ON_S 0 #define AR9170_PHY_REG_TSTDAC_CONST (AR9170_PHY_REG_BASE + 0x003c) #define AR9170_PHY_REG_SETTLING (AR9170_PHY_REG_BASE + 0x0044) #define AR9170_PHY_SETTLING_SWITCH 0x00003f80 #define AR9170_PHY_SETTLING_SWITCH_S 7 #define AR9170_PHY_REG_RXGAIN (AR9170_PHY_REG_BASE + 0x0048) #define AR9170_PHY_REG_RXGAIN_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2048) #define AR9170_PHY_RXGAIN_TXRX_ATTEN 0x0003f000 #define AR9170_PHY_RXGAIN_TXRX_ATTEN_S 12 #define AR9170_PHY_RXGAIN_TXRX_RF_MAX 0x007c0000 #define AR9170_PHY_RXGAIN_TXRX_RF_MAX_S 18 #define AR9170_PHY_REG_DESIRED_SZ (AR9170_PHY_REG_BASE + 0x0050) #define AR9170_PHY_DESIRED_SZ_ADC 0x000000ff #define AR9170_PHY_DESIRED_SZ_ADC_S 0 #define AR9170_PHY_DESIRED_SZ_PGA 0x0000ff00 #define AR9170_PHY_DESIRED_SZ_PGA_S 8 #define AR9170_PHY_DESIRED_SZ_TOT_DES 0x0ff00000 #define AR9170_PHY_DESIRED_SZ_TOT_DES_S 20 #define AR9170_PHY_REG_FIND_SIG (AR9170_PHY_REG_BASE + 0x0058) #define AR9170_PHY_FIND_SIG_FIRSTEP 0x0003f000 #define AR9170_PHY_FIND_SIG_FIRSTEP_S 12 #define AR9170_PHY_FIND_SIG_FIRPWR 0x03fc0000 #define AR9170_PHY_FIND_SIG_FIRPWR_S 18 #define AR9170_PHY_REG_AGC_CTL1 (AR9170_PHY_REG_BASE + 0x005c) #define AR9170_PHY_AGC_CTL1_COARSE_LOW 0x00007f80 #define AR9170_PHY_AGC_CTL1_COARSE_LOW_S 7 #define AR9170_PHY_AGC_CTL1_COARSE_HIGH 0x003f8000 #define AR9170_PHY_AGC_CTL1_COARSE_HIGH_S 15 #define AR9170_PHY_REG_AGC_CONTROL (AR9170_PHY_REG_BASE + 0x0060) #define AR9170_PHY_AGC_CONTROL_CAL 0x00000001 #define AR9170_PHY_AGC_CONTROL_NF 0x00000002 #define AR9170_PHY_AGC_CONTROL_ENABLE_NF 0x00008000 #define AR9170_PHY_AGC_CONTROL_FLTR_CAL 0x00010000 #define AR9170_PHY_AGC_CONTROL_NO_UPDATE_NF 0x00020000 #define AR9170_PHY_REG_CCA (AR9170_PHY_REG_BASE + 0x0064) #define AR9170_PHY_CCA_MIN_PWR 0x0ff80000 #define AR9170_PHY_CCA_MIN_PWR_S 19 #define AR9170_PHY_CCA_THRESH62 0x0007f000 #define AR9170_PHY_CCA_THRESH62_S 12 #define AR9170_PHY_REG_SFCORR (AR9170_PHY_REG_BASE + 0x0068) #define AR9170_PHY_SFCORR_M2COUNT_THR 0x0000001f #define AR9170_PHY_SFCORR_M2COUNT_THR_S 0 #define AR9170_PHY_SFCORR_M1_THRESH 0x00fe0000 #define AR9170_PHY_SFCORR_M1_THRESH_S 17 #define AR9170_PHY_SFCORR_M2_THRESH 0x7f000000 #define AR9170_PHY_SFCORR_M2_THRESH_S 24 #define AR9170_PHY_REG_SFCORR_LOW (AR9170_PHY_REG_BASE + 0x006c) #define AR9170_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001 #define AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW 0x00003f00 #define AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8 #define AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW 0x001fc000 #define AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW_S 14 #define AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW 0x0fe00000 #define AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW_S 21 #define AR9170_PHY_REG_SLEEP_CTR_CONTROL (AR9170_PHY_REG_BASE + 0x0070) #define AR9170_PHY_REG_SLEEP_CTR_LIMIT (AR9170_PHY_REG_BASE + 0x0074) #define AR9170_PHY_REG_SLEEP_SCAL (AR9170_PHY_REG_BASE + 0x0078) #define AR9170_PHY_REG_PLL_CTL (AR9170_PHY_REG_BASE + 0x007c) #define AR9170_PHY_PLL_CTL_40 0xaa #define AR9170_PHY_PLL_CTL_40_5413 0x04 #define AR9170_PHY_PLL_CTL_44 0xab #define AR9170_PHY_PLL_CTL_44_2133 0xeb #define AR9170_PHY_PLL_CTL_40_2133 0xea #define AR9170_PHY_REG_BIN_MASK_1 (AR9170_PHY_REG_BASE + 0x0100) #define AR9170_PHY_REG_BIN_MASK_2 (AR9170_PHY_REG_BASE + 0x0104) #define AR9170_PHY_REG_BIN_MASK_3 (AR9170_PHY_REG_BASE + 0x0108) #define AR9170_PHY_REG_MASK_CTL (AR9170_PHY_REG_BASE + 0x010c) /* analogue power on time (100ns) */ #define AR9170_PHY_REG_RX_DELAY (AR9170_PHY_REG_BASE + 0x0114) #define AR9170_PHY_REG_SEARCH_START_DELAY (AR9170_PHY_REG_BASE + 0x0118) #define AR9170_PHY_RX_DELAY_DELAY 0x00003fff #define AR9170_PHY_REG_TIMING_CTRL4(_i) (AR9170_PHY_REG_BASE + \ (0x0120 + ((_i) << 12))) #define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01f #define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0 #define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7e0 #define AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5 #define AR9170_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800 #define AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xf000 #define AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12 #define AR9170_PHY_TIMING_CTRL4_DO_IQCAL 0x10000 #define AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI 0x80000000 #define AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER 0x40000000 #define AR9170_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK 0x20000000 #define AR9170_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK 0x10000000 #define AR9170_PHY_REG_TIMING5 (AR9170_PHY_REG_BASE + 0x0124) #define AR9170_PHY_TIMING5_CYCPWR_THR1 0x000000fe #define AR9170_PHY_TIMING5_CYCPWR_THR1_S 1 #define AR9170_PHY_REG_POWER_TX_RATE1 (AR9170_PHY_REG_BASE + 0x0134) #define AR9170_PHY_REG_POWER_TX_RATE2 (AR9170_PHY_REG_BASE + 0x0138) #define AR9170_PHY_REG_POWER_TX_RATE_MAX (AR9170_PHY_REG_BASE + 0x013c) #define AR9170_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040 #define AR9170_PHY_REG_FRAME_CTL (AR9170_PHY_REG_BASE + 0x0144) #define AR9170_PHY_FRAME_CTL_TX_CLIP 0x00000038 #define AR9170_PHY_FRAME_CTL_TX_CLIP_S 3 #define AR9170_PHY_REG_SPUR_REG (AR9170_PHY_REG_BASE + 0x014c) #define AR9170_PHY_SPUR_REG_MASK_RATE_CNTL (0xff << 18) #define AR9170_PHY_SPUR_REG_MASK_RATE_CNTL_S 18 #define AR9170_PHY_SPUR_REG_ENABLE_MASK_PPM 0x20000 #define AR9170_PHY_SPUR_REG_MASK_RATE_SELECT (0xff << 9) #define AR9170_PHY_SPUR_REG_MASK_RATE_SELECT_S 9 #define AR9170_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI 0x100 #define AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH 0x7f #define AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH_S 0 #define AR9170_PHY_REG_RADAR_EXT (AR9170_PHY_REG_BASE + 0x0140) #define AR9170_PHY_RADAR_EXT_ENA 0x00004000 #define AR9170_PHY_REG_RADAR_0 (AR9170_PHY_REG_BASE + 0x0154) #define AR9170_PHY_RADAR_0_ENA 0x00000001 #define AR9170_PHY_RADAR_0_FFT_ENA 0x80000000 /* inband pulse threshold */ #define AR9170_PHY_RADAR_0_INBAND 0x0000003e #define AR9170_PHY_RADAR_0_INBAND_S 1 /* pulse RSSI threshold */ #define AR9170_PHY_RADAR_0_PRSSI 0x00000fc0 #define AR9170_PHY_RADAR_0_PRSSI_S 6 /* pulse height threshold */ #define AR9170_PHY_RADAR_0_HEIGHT 0x0003f000 #define AR9170_PHY_RADAR_0_HEIGHT_S 12 /* radar RSSI threshold */ #define AR9170_PHY_RADAR_0_RRSSI 0x00fc0000 #define AR9170_PHY_RADAR_0_RRSSI_S 18 /* radar firepower threshold */ #define AR9170_PHY_RADAR_0_FIRPWR 0x7f000000 #define AR9170_PHY_RADAR_0_FIRPWR_S 24 #define AR9170_PHY_REG_RADAR_1 (AR9170_PHY_REG_BASE + 0x0158) #define AR9170_PHY_RADAR_1_RELPWR_ENA 0x00800000 #define AR9170_PHY_RADAR_1_USE_FIR128 0x00400000 #define AR9170_PHY_RADAR_1_RELPWR_THRESH 0x003f0000 #define AR9170_PHY_RADAR_1_RELPWR_THRESH_S 16 #define AR9170_PHY_RADAR_1_BLOCK_CHECK 0x00008000 #define AR9170_PHY_RADAR_1_MAX_RRSSI 0x00004000 #define AR9170_PHY_RADAR_1_RELSTEP_CHECK 0x00002000 #define AR9170_PHY_RADAR_1_RELSTEP_THRESH 0x00001f00 #define AR9170_PHY_RADAR_1_RELSTEP_THRESH_S 8 #define AR9170_PHY_RADAR_1_MAXLEN 0x000000ff #define AR9170_PHY_RADAR_1_MAXLEN_S 0 #define AR9170_PHY_REG_SWITCH_CHAIN_0 (AR9170_PHY_REG_BASE + 0x0160) #define AR9170_PHY_REG_SWITCH_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2160) #define AR9170_PHY_REG_SWITCH_COM (AR9170_PHY_REG_BASE + 0x0164) #define AR9170_PHY_REG_CCA_THRESHOLD (AR9170_PHY_REG_BASE + 0x0168) #define AR9170_PHY_REG_SIGMA_DELTA (AR9170_PHY_REG_BASE + 0x016c) #define AR9170_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 #define AR9170_PHY_SIGMA_DELTA_ADC_SEL_S 0 #define AR9170_PHY_SIGMA_DELTA_FILT2 0x000000f8 #define AR9170_PHY_SIGMA_DELTA_FILT2_S 3 #define AR9170_PHY_SIGMA_DELTA_FILT1 0x00001f00 #define AR9170_PHY_SIGMA_DELTA_FILT1_S 8 #define AR9170_PHY_SIGMA_DELTA_ADC_CLIP 0x01ffe000 #define AR9170_PHY_SIGMA_DELTA_ADC_CLIP_S 13 #define AR9170_PHY_REG_RESTART (AR9170_PHY_REG_BASE + 0x0170) #define AR9170_PHY_RESTART_DIV_GC 0x001c0000 #define AR9170_PHY_RESTART_DIV_GC_S 18 #define AR9170_PHY_REG_RFBUS_REQ (AR9170_PHY_REG_BASE + 0x017c) #define AR9170_PHY_RFBUS_REQ_EN 0x00000001 #define AR9170_PHY_REG_TIMING7 (AR9170_PHY_REG_BASE + 0x0180) #define AR9170_PHY_REG_TIMING8 (AR9170_PHY_REG_BASE + 0x0184) #define AR9170_PHY_TIMING8_PILOT_MASK_2 0x000fffff #define AR9170_PHY_TIMING8_PILOT_MASK_2_S 0 #define AR9170_PHY_REG_BIN_MASK2_1 (AR9170_PHY_REG_BASE + 0x0188) #define AR9170_PHY_REG_BIN_MASK2_2 (AR9170_PHY_REG_BASE + 0x018c) #define AR9170_PHY_REG_BIN_MASK2_3 (AR9170_PHY_REG_BASE + 0x0190) #define AR9170_PHY_REG_BIN_MASK2_4 (AR9170_PHY_REG_BASE + 0x0194) #define AR9170_PHY_BIN_MASK2_4_MASK_4 0x00003fff #define AR9170_PHY_BIN_MASK2_4_MASK_4_S 0 #define AR9170_PHY_REG_TIMING9 (AR9170_PHY_REG_BASE + 0x0198) #define AR9170_PHY_REG_TIMING10 (AR9170_PHY_REG_BASE + 0x019c) #define AR9170_PHY_TIMING10_PILOT_MASK_2 0x000fffff #define AR9170_PHY_TIMING10_PILOT_MASK_2_S 0 #define AR9170_PHY_REG_TIMING11 (AR9170_PHY_REG_BASE + 0x01a0) #define AR9170_PHY_TIMING11_SPUR_DELTA_PHASE 0x000fffff #define AR9170_PHY_TIMING11_SPUR_DELTA_PHASE_S 0 #define AR9170_PHY_TIMING11_SPUR_FREQ_SD 0x3ff00000 #define AR9170_PHY_TIMING11_SPUR_FREQ_SD_S 20 #define AR9170_PHY_TIMING11_USE_SPUR_IN_AGC 0x40000000 #define AR9170_PHY_TIMING11_USE_SPUR_IN_SELFCOR 0x80000000 #define AR9170_PHY_REG_RX_CHAINMASK (AR9170_PHY_REG_BASE + 0x01a4) #define AR9170_PHY_REG_NEW_ADC_DC_GAIN_CORR(_i) (AR9170_PHY_REG_BASE + \ 0x01b4 + ((_i) << 12)) #define AR9170_PHY_NEW_ADC_GAIN_CORR_ENABLE 0x40000000 #define AR9170_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000 #define AR9170_PHY_REG_MULTICHAIN_GAIN_CTL (AR9170_PHY_REG_BASE + 0x01ac) #define AR9170_PHY_9285_ANT_DIV_CTL_ALL 0x7f000000 #define AR9170_PHY_9285_ANT_DIV_CTL 0x01000000 #define AR9170_PHY_9285_ANT_DIV_CTL_S 24 #define AR9170_PHY_9285_ANT_DIV_ALT_LNACONF 0x06000000 #define AR9170_PHY_9285_ANT_DIV_ALT_LNACONF_S 25 #define AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF 0x18000000 #define AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF_S 27 #define AR9170_PHY_9285_ANT_DIV_ALT_GAINTB 0x20000000 #define AR9170_PHY_9285_ANT_DIV_ALT_GAINTB_S 29 #define AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB 0x40000000 #define AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB_S 30 #define AR9170_PHY_9285_ANT_DIV_LNA1 2 #define AR9170_PHY_9285_ANT_DIV_LNA2 1 #define AR9170_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2 3 #define AR9170_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0 #define AR9170_PHY_9285_ANT_DIV_GAINTB_0 0 #define AR9170_PHY_9285_ANT_DIV_GAINTB_1 1 #define AR9170_PHY_REG_EXT_CCA0 (AR9170_PHY_REG_BASE + 0x01b8) #define AR9170_PHY_REG_EXT_CCA0_THRESH62 0x000000ff #define AR9170_PHY_REG_EXT_CCA0_THRESH62_S 0 #define AR9170_PHY_REG_EXT_CCA (AR9170_PHY_REG_BASE + 0x01bc) #define AR9170_PHY_EXT_CCA_CYCPWR_THR1 0x0000fe00 #define AR9170_PHY_EXT_CCA_CYCPWR_THR1_S 9 #define AR9170_PHY_EXT_CCA_THRESH62 0x007f0000 #define AR9170_PHY_EXT_CCA_THRESH62_S 16 #define AR9170_PHY_EXT_CCA_MIN_PWR 0xff800000 #define AR9170_PHY_EXT_CCA_MIN_PWR_S 23 #define AR9170_PHY_REG_SFCORR_EXT (AR9170_PHY_REG_BASE + 0x01c0) #define AR9170_PHY_SFCORR_EXT_M1_THRESH 0x0000007f #define AR9170_PHY_SFCORR_EXT_M1_THRESH_S 0 #define AR9170_PHY_SFCORR_EXT_M2_THRESH 0x00003f80 #define AR9170_PHY_SFCORR_EXT_M2_THRESH_S 7 #define AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW 0x001fc000 #define AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW_S 14 #define AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW 0x0fe00000 #define AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW_S 21 #define AR9170_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28 #define AR9170_PHY_REG_HALFGI (AR9170_PHY_REG_BASE + 0x01d0) #define AR9170_PHY_HALFGI_DSC_MAN 0x0007fff0 #define AR9170_PHY_HALFGI_DSC_MAN_S 4 #define AR9170_PHY_HALFGI_DSC_EXP 0x0000000f #define AR9170_PHY_HALFGI_DSC_EXP_S 0 #define AR9170_PHY_REG_CHANNEL_MASK_01_30 (AR9170_PHY_REG_BASE + 0x01d4) #define AR9170_PHY_REG_CHANNEL_MASK_31_60 (AR9170_PHY_REG_BASE + 0x01d8) #define AR9170_PHY_REG_CHAN_INFO_MEMORY (AR9170_PHY_REG_BASE + 0x01dc) #define AR9170_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001 #define AR9170_PHY_REG_HEAVY_CLIP_ENABLE (AR9170_PHY_REG_BASE + 0x01e0) #define AR9170_PHY_REG_HEAVY_CLIP_FACTOR_RIFS (AR9170_PHY_REG_BASE + 0x01ec) #define AR9170_PHY_RIFS_INIT_DELAY 0x03ff0000 #define AR9170_PHY_REG_CALMODE (AR9170_PHY_REG_BASE + 0x01f0) #define AR9170_PHY_CALMODE_IQ 0x00000000 #define AR9170_PHY_CALMODE_ADC_GAIN 0x00000001 #define AR9170_PHY_CALMODE_ADC_DC_PER 0x00000002 #define AR9170_PHY_CALMODE_ADC_DC_INIT 0x00000003 #define AR9170_PHY_REG_REFCLKDLY (AR9170_PHY_REG_BASE + 0x01f4) #define AR9170_PHY_REG_REFCLKPD (AR9170_PHY_REG_BASE + 0x01f8) #define AR9170_PHY_REG_CAL_MEAS_0(_i) (AR9170_PHY_REG_BASE + \ 0x0410 + ((_i) << 12)) #define AR9170_PHY_REG_CAL_MEAS_1(_i) (AR9170_PHY_REG_BASE + \ 0x0414 \ + ((_i) << 12)) #define AR9170_PHY_REG_CAL_MEAS_2(_i) (AR9170_PHY_REG_BASE + \ 0x0418 + ((_i) << 12)) #define AR9170_PHY_REG_CAL_MEAS_3(_i) (AR9170_PHY_REG_BASE + \ 0x041c + ((_i) << 12)) #define AR9170_PHY_REG_CURRENT_RSSI (AR9170_PHY_REG_BASE + 0x041c) #define AR9170_PHY_REG_RFBUS_GRANT (AR9170_PHY_REG_BASE + 0x0420) #define AR9170_PHY_RFBUS_GRANT_EN 0x00000001 #define AR9170_PHY_REG_CHAN_INFO_GAIN_DIFF (AR9170_PHY_REG_BASE + 0x04f4) #define AR9170_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320 #define AR9170_PHY_REG_CHAN_INFO_GAIN (AR9170_PHY_REG_BASE + 0x04fc) #define AR9170_PHY_REG_MODE (AR9170_PHY_REG_BASE + 0x0a00) #define AR9170_PHY_MODE_ASYNCFIFO 0x80 #define AR9170_PHY_MODE_AR2133 0x08 #define AR9170_PHY_MODE_AR5111 0x00 #define AR9170_PHY_MODE_AR5112 0x08 #define AR9170_PHY_MODE_DYNAMIC 0x04 #define AR9170_PHY_MODE_RF2GHZ 0x02 #define AR9170_PHY_MODE_RF5GHZ 0x00 #define AR9170_PHY_MODE_CCK 0x01 #define AR9170_PHY_MODE_OFDM 0x00 #define AR9170_PHY_MODE_DYN_CCK_DISABLE 0x100 #define AR9170_PHY_REG_CCK_TX_CTRL (AR9170_PHY_REG_BASE + 0x0a04) #define AR9170_PHY_CCK_TX_CTRL_JAPAN 0x00000010 #define AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK 0x0000000c #define AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK_S 2 #define AR9170_PHY_REG_CCK_DETECT (AR9170_PHY_REG_BASE + 0x0a08) #define AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003f #define AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 /* [12:6] settling time for antenna switch */ #define AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001fc0 #define AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME_S 6 #define AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000 #define AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV_S 13 #define AR9170_PHY_REG_GAIN_2GHZ (AR9170_PHY_REG_BASE + 0x0a0c) #define AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2 (AR9170_PHY_REG_BASE + 0x2a0c) #define AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00fc0000 #define AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN_S 18 #define AR9170_PHY_GAIN_2GHZ_BSW_MARGIN 0x00003c00 #define AR9170_PHY_GAIN_2GHZ_BSW_MARGIN_S 10 #define AR9170_PHY_GAIN_2GHZ_BSW_ATTEN 0x0000001f #define AR9170_PHY_GAIN_2GHZ_BSW_ATTEN_S 0 #define AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN 0x003e0000 #define AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S 17 #define AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN 0x0001f000 #define AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S 12 #define AR9170_PHY_GAIN_2GHZ_XATTEN2_DB 0x00000fc0 #define AR9170_PHY_GAIN_2GHZ_XATTEN2_DB_S 6 #define AR9170_PHY_GAIN_2GHZ_XATTEN1_DB 0x0000003f #define AR9170_PHY_GAIN_2GHZ_XATTEN1_DB_S 0 #define AR9170_PHY_REG_CCK_RXCTRL4 (AR9170_PHY_REG_BASE + 0x0a1c) #define AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT 0x01f80000 #define AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S 19 #define AR9170_PHY_REG_DAG_CTRLCCK (AR9170_PHY_REG_BASE + 0x0a28) #define AR9170_REG_DAG_CTRLCCK_EN_RSSI_THR 0x00000200 #define AR9170_REG_DAG_CTRLCCK_RSSI_THR 0x0001fc00 #define AR9170_REG_DAG_CTRLCCK_RSSI_THR_S 10 #define AR9170_PHY_REG_FORCE_CLKEN_CCK (AR9170_PHY_REG_BASE + 0x0a2c) #define AR9170_FORCE_CLKEN_CCK_MRC_MUX 0x00000040 #define AR9170_PHY_REG_POWER_TX_RATE3 (AR9170_PHY_REG_BASE + 0x0a34) #define AR9170_PHY_REG_POWER_TX_RATE4 (AR9170_PHY_REG_BASE + 0x0a38) #define AR9170_PHY_REG_SCRM_SEQ_XR (AR9170_PHY_REG_BASE + 0x0a3c) #define AR9170_PHY_REG_HEADER_DETECT_XR (AR9170_PHY_REG_BASE + 0x0a40) #define AR9170_PHY_REG_CHIRP_DETECTED_XR (AR9170_PHY_REG_BASE + 0x0a44) #define AR9170_PHY_REG_BLUETOOTH (AR9170_PHY_REG_BASE + 0x0a54) #define AR9170_PHY_REG_TPCRG1 (AR9170_PHY_REG_BASE + 0x0a58) #define AR9170_PHY_TPCRG1_NUM_PD_GAIN 0x0000c000 #define AR9170_PHY_TPCRG1_NUM_PD_GAIN_S 14 #define AR9170_PHY_TPCRG1_PD_GAIN_1 0x00030000 #define AR9170_PHY_TPCRG1_PD_GAIN_1_S 16 #define AR9170_PHY_TPCRG1_PD_GAIN_2 0x000c0000 #define AR9170_PHY_TPCRG1_PD_GAIN_2_S 18 #define AR9170_PHY_TPCRG1_PD_GAIN_3 0x00300000 #define AR9170_PHY_TPCRG1_PD_GAIN_3_S 20 #define AR9170_PHY_TPCRG1_PD_CAL_ENABLE 0x00400000 #define AR9170_PHY_TPCRG1_PD_CAL_ENABLE_S 22 #define AR9170_PHY_REG_TX_PWRCTRL4 (AR9170_PHY_REG_BASE + 0x0a64) #define AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID 0x00000001 #define AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID_S 0 #define AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT 0x000001fe #define AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT_S 1 #define AR9170_PHY_REG_ANALOG_SWAP (AR9170_PHY_REG_BASE + 0x0a68) #define AR9170_PHY_ANALOG_SWAP_AB 0x0001 #define AR9170_PHY_ANALOG_SWAP_ALT_CHAIN 0x00000040 #define AR9170_PHY_REG_TPCRG5 (AR9170_PHY_REG_BASE + 0x0a6c) #define AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP 0x0000000f #define AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP_S 0 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1 0x000003f0 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S 4 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2 0x0000fc00 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S 10 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3 0x003f0000 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S 16 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4 0x0fc00000 #define AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S 22 #define AR9170_PHY_REG_TX_PWRCTRL6_0 (AR9170_PHY_REG_BASE + 0x0a70) #define AR9170_PHY_REG_TX_PWRCTRL6_1 (AR9170_PHY_REG_BASE + 0x1a70) #define AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE 0x03000000 #define AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE_S 24 #define AR9170_PHY_REG_TX_PWRCTRL7 (AR9170_PHY_REG_BASE + 0x0a74) #define AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN 0x01f80000 #define AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN_S 19 #define AR9170_PHY_REG_TX_PWRCTRL9 (AR9170_PHY_REG_BASE + 0x0a7c) #define AR9170_PHY_TX_DESIRED_SCALE_CCK 0x00007c00 #define AR9170_PHY_TX_DESIRED_SCALE_CCK_S 10 #define AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL 0x80000000 #define AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL_S 31 #define AR9170_PHY_REG_TX_GAIN_TBL1 (AR9170_PHY_REG_BASE + 0x0b00) #define AR9170_PHY_TX_GAIN 0x0007f000 #define AR9170_PHY_TX_GAIN_S 12 /* Carrier leak calibration control, do it after AGC calibration */ #define AR9170_PHY_REG_CL_CAL_CTL (AR9170_PHY_REG_BASE + 0x0b58) #define AR9170_PHY_CL_CAL_ENABLE 0x00000002 #define AR9170_PHY_CL_CAL_PARALLEL_CAL_ENABLE 0x00000001 #define AR9170_PHY_REG_POWER_TX_RATE5 (AR9170_PHY_REG_BASE + 0x0b8c) #define AR9170_PHY_REG_POWER_TX_RATE6 (AR9170_PHY_REG_BASE + 0x0b90) #define AR9170_PHY_REG_CH0_TX_PWRCTRL11 (AR9170_PHY_REG_BASE + 0x0b98) #define AR9170_PHY_REG_CH1_TX_PWRCTRL11 (AR9170_PHY_REG_BASE + 0x1b98) #define AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP 0x0000fc00 #define AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP_S 10 #define AR9170_PHY_REG_CAL_CHAINMASK (AR9170_PHY_REG_BASE + 0x0b9c) #define AR9170_PHY_REG_VIT_MASK2_M_46_61 (AR9170_PHY_REG_BASE + 0x0ba0) #define AR9170_PHY_REG_MASK2_M_31_45 (AR9170_PHY_REG_BASE + 0x0ba4) #define AR9170_PHY_REG_MASK2_M_16_30 (AR9170_PHY_REG_BASE + 0x0ba8) #define AR9170_PHY_REG_MASK2_M_00_15 (AR9170_PHY_REG_BASE + 0x0bac) #define AR9170_PHY_REG_PILOT_MASK_01_30 (AR9170_PHY_REG_BASE + 0x0bb0) #define AR9170_PHY_REG_PILOT_MASK_31_60 (AR9170_PHY_REG_BASE + 0x0bb4) #define AR9170_PHY_REG_MASK2_P_15_01 (AR9170_PHY_REG_BASE + 0x0bb8) #define AR9170_PHY_REG_MASK2_P_30_16 (AR9170_PHY_REG_BASE + 0x0bbc) #define AR9170_PHY_REG_MASK2_P_45_31 (AR9170_PHY_REG_BASE + 0x0bc0) #define AR9170_PHY_REG_MASK2_P_61_45 (AR9170_PHY_REG_BASE + 0x0bc4) #define AR9170_PHY_REG_POWER_TX_SUB (AR9170_PHY_REG_BASE + 0x0bc8) #define AR9170_PHY_REG_POWER_TX_RATE7 (AR9170_PHY_REG_BASE + 0x0bcc) #define AR9170_PHY_REG_POWER_TX_RATE8 (AR9170_PHY_REG_BASE + 0x0bd0) #define AR9170_PHY_REG_POWER_TX_RATE9 (AR9170_PHY_REG_BASE + 0x0bd4) #define AR9170_PHY_REG_XPA_CFG (AR9170_PHY_REG_BASE + 0x0bd8) #define AR9170_PHY_FORCE_XPA_CFG 0x000000001 #define AR9170_PHY_FORCE_XPA_CFG_S 0 #define AR9170_PHY_REG_CH1_CCA (AR9170_PHY_REG_BASE + 0x1064) #define AR9170_PHY_CH1_CCA_MIN_PWR 0x0ff80000 #define AR9170_PHY_CH1_CCA_MIN_PWR_S 19 #define AR9170_PHY_REG_CH2_CCA (AR9170_PHY_REG_BASE + 0x2064) #define AR9170_PHY_CH2_CCA_MIN_PWR 0x0ff80000 #define AR9170_PHY_CH2_CCA_MIN_PWR_S 19 #define AR9170_PHY_REG_CH1_EXT_CCA (AR9170_PHY_REG_BASE + 0x11bc) #define AR9170_PHY_CH1_EXT_CCA_MIN_PWR 0xff800000 #define AR9170_PHY_CH1_EXT_CCA_MIN_PWR_S 23 #define AR9170_PHY_REG_CH2_EXT_CCA (AR9170_PHY_REG_BASE + 0x21bc) #define AR9170_PHY_CH2_EXT_CCA_MIN_PWR 0xff800000 #define AR9170_PHY_CH2_EXT_CCA_MIN_PWR_S 23 #endif /* __CARL9170_SHARED_PHY_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/phy.c0000644000175000017500000015731312026211315023455 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * PHY and RF code * * Copyright 2008, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "carl9170.h" #include "cmd.h" #include "phy.h" static int carl9170_init_power_cal(struct ar9170 *ar) { carl9170_regwrite_begin(ar); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE_MAX, 0x7f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE1, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE2, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE3, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE4, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE5, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE6, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE7, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE8, 0x3f3f3f3f); carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE9, 0x3f3f3f3f); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } struct carl9170_phy_init { u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20; }; static struct carl9170_phy_init ar5416_phy_init[] = { { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, }, { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, }, { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, }, { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, }, { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, }, { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, }, { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, }, { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, }, { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, }, { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, }, { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, }, { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, }, { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, }, { 0x1c5850, 0x6c48b4e4, 0x6d48b4e4, 0x6d48b0e4, 0x6c48b0e4, }, { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, }, { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, }, { 0x1c585c, 0x31395c5e, 0x3139605e, 0x3139605e, 0x31395c5e, }, { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, }, { 0x1c5864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, }, { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, }, { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, }, { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, }, { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, }, { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, }, { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, }, { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, }, { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, }, { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, }, { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, }, { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, }, { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, }, { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, }, { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, }, { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, }, { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, }, { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, }, { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, }, { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, }, { 0x1c59bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, }, { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, }, { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, }, { 0x1c59c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c, }, { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, }, { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, }, { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, }, { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, }, { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, }, { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, }, { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, }, { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, }, { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, }, { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, }, { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, }, { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, }, { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, }, { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, }, { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, }, { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, }, { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, }, { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, }, { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, }, { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, }, { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, }, { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, }, { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, }, { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, }, { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, }, { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, }, { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, }, { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, }, { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, }, { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, }, { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, }, { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, }, { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, }, { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, }, { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, }, { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, }, { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, }, { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, }, { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, }, { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, }, { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, }, { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, }, { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, }, { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, }, { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, }, { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, }, { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, }, { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, }, { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, }, { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, }, { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, }, { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, }, { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, }, { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, }, { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, }, { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, }, { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, }, { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, }, { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, }, { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, }, { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, }, { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, }, { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, }, { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, }, { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, }, { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, }, { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, }, { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, }, { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, }, { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, }, { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, }, { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, }, { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, }, { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, }, { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, }, { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, }, { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, }, { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, }, { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, }, { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, }, { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, }, { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, }, { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, }, { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, }, { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, }, { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, }, { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, }, { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, }, { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, }, { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, }, { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, }, { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, }, { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, }, { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, }, { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, }, { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, }, { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, }, { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, }, { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, }, { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, }, { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, }, { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, }, { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, }, { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, }, { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, }, { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, }, { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, }, { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, }, { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, }, { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, }, { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, }, { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, }, { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, }, { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, }, { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, }, { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, }, { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, }, { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, }, { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, }, { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, }, { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, }, { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, }, { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, }, { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, }, { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, }, { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, }, { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, }, { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, }, { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, }, { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, }, { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, }, { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, }, { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, }, { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, }, /* { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */ { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, }, { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, }, { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, }, { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, }, { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, }, { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, }, { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, }, { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, }, { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, }, { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, }, { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, }, { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, }, { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, }, { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, }, { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, }, { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, } }; /* * look up a certain register in ar5416_phy_init[] and return the init. value * for the band and bandwidth given. Return 0 if register address not found. */ static u32 carl9170_def_val(u32 reg, bool is_2ghz, bool is_40mhz) { unsigned int i; for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) { if (ar5416_phy_init[i].reg != reg) continue; if (is_2ghz) { if (is_40mhz) return ar5416_phy_init[i]._2ghz_40; else return ar5416_phy_init[i]._2ghz_20; } else { if (is_40mhz) return ar5416_phy_init[i]._5ghz_40; else return ar5416_phy_init[i]._5ghz_20; } } return 0; } /* * initialize some phy regs from eeprom values in modal_header[] * acc. to band and bandwidth */ static int carl9170_init_phy_from_eeprom(struct ar9170 *ar, bool is_2ghz, bool is_40mhz) { static const u8 xpd2pd[16] = { 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x6, 0x2, 0x2, 0x3, 0x7, 0x2, 0xb, 0x2, 0x2, 0x2 }; /* pointer to the modal_header acc. to band */ struct ar9170_eeprom_modal *m = &ar->eeprom.modal_header[is_2ghz]; u32 val; carl9170_regwrite_begin(ar); /* ant common control (index 0) */ carl9170_regwrite(AR9170_PHY_REG_SWITCH_COM, le32_to_cpu(m->antCtrlCommon)); /* ant control chain 0 (index 1) */ carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_0, le32_to_cpu(m->antCtrlChain[0])); /* ant control chain 2 (index 2) */ carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_2, le32_to_cpu(m->antCtrlChain[1])); /* SwSettle (index 3) */ if (!is_40mhz) { val = carl9170_def_val(AR9170_PHY_REG_SETTLING, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_SETTLING_SWITCH, val, m->switchSettling); carl9170_regwrite(AR9170_PHY_REG_SETTLING, val); } /* adcDesired, pdaDesired (index 4) */ val = carl9170_def_val(AR9170_PHY_REG_DESIRED_SZ, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_DESIRED_SZ_PGA, val, m->pgaDesiredSize); SET_VAL(AR9170_PHY_DESIRED_SZ_ADC, val, m->adcDesiredSize); carl9170_regwrite(AR9170_PHY_REG_DESIRED_SZ, val); /* TxEndToXpaOff, TxFrameToXpaOn (index 5) */ val = carl9170_def_val(AR9170_PHY_REG_RF_CTL4, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF, val, m->txEndToXpaOff); SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF, val, m->txEndToXpaOff); SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAB_ON, val, m->txFrameToXpaOn); SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAA_ON, val, m->txFrameToXpaOn); carl9170_regwrite(AR9170_PHY_REG_RF_CTL4, val); /* TxEndToRxOn (index 6) */ val = carl9170_def_val(AR9170_PHY_REG_RF_CTL3, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON, val, m->txEndToRxOn); carl9170_regwrite(AR9170_PHY_REG_RF_CTL3, val); /* thresh62 (index 7) */ val = carl9170_def_val(0x1c8864, is_2ghz, is_40mhz); val = (val & ~0x7f000) | (m->thresh62 << 12); carl9170_regwrite(0x1c8864, val); /* tx/rx attenuation chain 0 (index 8) */ val = carl9170_def_val(AR9170_PHY_REG_RXGAIN, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[0]); carl9170_regwrite(AR9170_PHY_REG_RXGAIN, val); /* tx/rx attenuation chain 2 (index 9) */ val = carl9170_def_val(AR9170_PHY_REG_RXGAIN_CHAIN_2, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[1]); carl9170_regwrite(AR9170_PHY_REG_RXGAIN_CHAIN_2, val); /* tx/rx margin chain 0 (index 10) */ val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[0]); /* bsw margin chain 0 for 5GHz only */ if (!is_2ghz) SET_VAL(AR9170_PHY_GAIN_2GHZ_BSW_MARGIN, val, m->bswMargin[0]); carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ, val); /* tx/rx margin chain 2 (index 11) */ val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[1]); carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2, val); /* iqCall, iqCallq chain 0 (index 12) */ val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(0), is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[0]); SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[0]); carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(0), val); /* iqCall, iqCallq chain 2 (index 13) */ val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(2), is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[1]); SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[1]); carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(2), val); /* xpd gain mask (index 14) */ val = carl9170_def_val(AR9170_PHY_REG_TPCRG1, is_2ghz, is_40mhz); SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_1, val, xpd2pd[m->xpdGain & 0xf] & 3); SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_2, val, xpd2pd[m->xpdGain & 0xf] >> 2); carl9170_regwrite(AR9170_PHY_REG_TPCRG1, val); carl9170_regwrite(AR9170_PHY_REG_RX_CHAINMASK, ar->eeprom.rx_mask); carl9170_regwrite(AR9170_PHY_REG_CAL_CHAINMASK, ar->eeprom.rx_mask); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } static int carl9170_init_phy(struct ar9170 *ar, enum ieee80211_band band) { int i, err; u32 val; bool is_2ghz = band == IEEE80211_BAND_2GHZ; bool is_40mhz = conf_is_ht40(&ar->hw->conf); carl9170_regwrite_begin(ar); for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) { if (is_40mhz) { if (is_2ghz) val = ar5416_phy_init[i]._2ghz_40; else val = ar5416_phy_init[i]._5ghz_40; } else { if (is_2ghz) val = ar5416_phy_init[i]._2ghz_20; else val = ar5416_phy_init[i]._5ghz_20; } carl9170_regwrite(ar5416_phy_init[i].reg, val); } carl9170_regwrite_finish(); err = carl9170_regwrite_result(); if (err) return err; err = carl9170_init_phy_from_eeprom(ar, is_2ghz, is_40mhz); if (err) return err; err = carl9170_init_power_cal(ar); if (err) return err; if (!ar->fw.hw_counters) { err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, is_2ghz ? 0x5163 : 0x5143); } return err; } struct carl9170_rf_initvals { u32 reg, _5ghz, _2ghz; }; static struct carl9170_rf_initvals carl9170_rf_initval[] = { /* bank 0 */ { 0x1c58b0, 0x1e5795e5, 0x1e5795e5}, { 0x1c58e0, 0x02008020, 0x02008020}, /* bank 1 */ { 0x1c58b0, 0x02108421, 0x02108421}, { 0x1c58ec, 0x00000008, 0x00000008}, /* bank 2 */ { 0x1c58b0, 0x0e73ff17, 0x0e73ff17}, { 0x1c58e0, 0x00000420, 0x00000420}, /* bank 3 */ { 0x1c58f0, 0x01400018, 0x01c00018}, /* bank 4 */ { 0x1c58b0, 0x000001a1, 0x000001a1}, { 0x1c58e8, 0x00000001, 0x00000001}, /* bank 5 */ { 0x1c58b0, 0x00000013, 0x00000013}, { 0x1c58e4, 0x00000002, 0x00000002}, /* bank 6 */ { 0x1c58b0, 0x00000000, 0x00000000}, { 0x1c58b0, 0x00000000, 0x00000000}, { 0x1c58b0, 0x00000000, 0x00000000}, { 0x1c58b0, 0x00000000, 0x00000000}, { 0x1c58b0, 0x00000000, 0x00000000}, { 0x1c58b0, 0x00004000, 0x00004000}, { 0x1c58b0, 0x00006c00, 0x00006c00}, { 0x1c58b0, 0x00002c00, 0x00002c00}, { 0x1c58b0, 0x00004800, 0x00004800}, { 0x1c58b0, 0x00004000, 0x00004000}, { 0x1c58b0, 0x00006000, 0x00006000}, { 0x1c58b0, 0x00001000, 0x00001000}, { 0x1c58b0, 0x00004000, 0x00004000}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00087c00, 0x00087c00}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00005400, 0x00005400}, { 0x1c58b0, 0x00000c00, 0x00000c00}, { 0x1c58b0, 0x00001800, 0x00001800}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00006c00, 0x00006c00}, { 0x1c58b0, 0x00006c00, 0x00006c00}, { 0x1c58b0, 0x00007c00, 0x00007c00}, { 0x1c58b0, 0x00002c00, 0x00002c00}, { 0x1c58b0, 0x00003c00, 0x00003c00}, { 0x1c58b0, 0x00003800, 0x00003800}, { 0x1c58b0, 0x00001c00, 0x00001c00}, { 0x1c58b0, 0x00000800, 0x00000800}, { 0x1c58b0, 0x00000408, 0x00000408}, { 0x1c58b0, 0x00004c15, 0x00004c15}, { 0x1c58b0, 0x00004188, 0x00004188}, { 0x1c58b0, 0x0000201e, 0x0000201e}, { 0x1c58b0, 0x00010408, 0x00010408}, { 0x1c58b0, 0x00000801, 0x00000801}, { 0x1c58b0, 0x00000c08, 0x00000c08}, { 0x1c58b0, 0x0000181e, 0x0000181e}, { 0x1c58b0, 0x00001016, 0x00001016}, { 0x1c58b0, 0x00002800, 0x00002800}, { 0x1c58b0, 0x00004010, 0x00004010}, { 0x1c58b0, 0x0000081c, 0x0000081c}, { 0x1c58b0, 0x00000115, 0x00000115}, { 0x1c58b0, 0x00000015, 0x00000015}, { 0x1c58b0, 0x00000066, 0x00000066}, { 0x1c58b0, 0x0000001c, 0x0000001c}, { 0x1c58b0, 0x00000000, 0x00000000}, { 0x1c58b0, 0x00000004, 0x00000004}, { 0x1c58b0, 0x00000015, 0x00000015}, { 0x1c58b0, 0x0000001f, 0x0000001f}, { 0x1c58e0, 0x00000000, 0x00000400}, /* bank 7 */ { 0x1c58b0, 0x000000a0, 0x000000a0}, { 0x1c58b0, 0x00000000, 0x00000000}, { 0x1c58b0, 0x00000040, 0x00000040}, { 0x1c58f0, 0x0000001c, 0x0000001c}, }; static int carl9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz) { int err, i; carl9170_regwrite_begin(ar); for (i = 0; i < ARRAY_SIZE(carl9170_rf_initval); i++) carl9170_regwrite(carl9170_rf_initval[i].reg, band5ghz ? carl9170_rf_initval[i]._5ghz : carl9170_rf_initval[i]._2ghz); carl9170_regwrite_finish(); err = carl9170_regwrite_result(); if (err) wiphy_err(ar->hw->wiphy, "rf init failed\n"); return err; } struct carl9170_phy_freq_params { u8 coeff_exp; u16 coeff_man; u8 coeff_exp_shgi; u16 coeff_man_shgi; }; enum carl9170_bw { CARL9170_BW_20, CARL9170_BW_40_BELOW, CARL9170_BW_40_ABOVE, __CARL9170_NUM_BW, }; struct carl9170_phy_freq_entry { u16 freq; struct carl9170_phy_freq_params params[__CARL9170_NUM_BW]; }; /* NB: must be in sync with channel tables in main! */ static const struct carl9170_phy_freq_entry carl9170_phy_freq_params[] = { /* * freq, * 20MHz, * 40MHz (below), * 40Mhz (above), */ { 2412, { { 3, 21737, 3, 19563, }, { 3, 21827, 3, 19644, }, { 3, 21647, 3, 19482, }, } }, { 2417, { { 3, 21692, 3, 19523, }, { 3, 21782, 3, 19604, }, { 3, 21602, 3, 19442, }, } }, { 2422, { { 3, 21647, 3, 19482, }, { 3, 21737, 3, 19563, }, { 3, 21558, 3, 19402, }, } }, { 2427, { { 3, 21602, 3, 19442, }, { 3, 21692, 3, 19523, }, { 3, 21514, 3, 19362, }, } }, { 2432, { { 3, 21558, 3, 19402, }, { 3, 21647, 3, 19482, }, { 3, 21470, 3, 19323, }, } }, { 2437, { { 3, 21514, 3, 19362, }, { 3, 21602, 3, 19442, }, { 3, 21426, 3, 19283, }, } }, { 2442, { { 3, 21470, 3, 19323, }, { 3, 21558, 3, 19402, }, { 3, 21382, 3, 19244, }, } }, { 2447, { { 3, 21426, 3, 19283, }, { 3, 21514, 3, 19362, }, { 3, 21339, 3, 19205, }, } }, { 2452, { { 3, 21382, 3, 19244, }, { 3, 21470, 3, 19323, }, { 3, 21295, 3, 19166, }, } }, { 2457, { { 3, 21339, 3, 19205, }, { 3, 21426, 3, 19283, }, { 3, 21252, 3, 19127, }, } }, { 2462, { { 3, 21295, 3, 19166, }, { 3, 21382, 3, 19244, }, { 3, 21209, 3, 19088, }, } }, { 2467, { { 3, 21252, 3, 19127, }, { 3, 21339, 3, 19205, }, { 3, 21166, 3, 19050, }, } }, { 2472, { { 3, 21209, 3, 19088, }, { 3, 21295, 3, 19166, }, { 3, 21124, 3, 19011, }, } }, { 2484, { { 3, 21107, 3, 18996, }, { 3, 21192, 3, 19073, }, { 3, 21022, 3, 18920, }, } }, { 4920, { { 4, 21313, 4, 19181, }, { 4, 21356, 4, 19220, }, { 4, 21269, 4, 19142, }, } }, { 4940, { { 4, 21226, 4, 19104, }, { 4, 21269, 4, 19142, }, { 4, 21183, 4, 19065, }, } }, { 4960, { { 4, 21141, 4, 19027, }, { 4, 21183, 4, 19065, }, { 4, 21098, 4, 18988, }, } }, { 4980, { { 4, 21056, 4, 18950, }, { 4, 21098, 4, 18988, }, { 4, 21014, 4, 18912, }, } }, { 5040, { { 4, 20805, 4, 18725, }, { 4, 20846, 4, 18762, }, { 4, 20764, 4, 18687, }, } }, { 5060, { { 4, 20723, 4, 18651, }, { 4, 20764, 4, 18687, }, { 4, 20682, 4, 18614, }, } }, { 5080, { { 4, 20641, 4, 18577, }, { 4, 20682, 4, 18614, }, { 4, 20601, 4, 18541, }, } }, { 5180, { { 4, 20243, 4, 18219, }, { 4, 20282, 4, 18254, }, { 4, 20204, 4, 18183, }, } }, { 5200, { { 4, 20165, 4, 18148, }, { 4, 20204, 4, 18183, }, { 4, 20126, 4, 18114, }, } }, { 5220, { { 4, 20088, 4, 18079, }, { 4, 20126, 4, 18114, }, { 4, 20049, 4, 18044, }, } }, { 5240, { { 4, 20011, 4, 18010, }, { 4, 20049, 4, 18044, }, { 4, 19973, 4, 17976, }, } }, { 5260, { { 4, 19935, 4, 17941, }, { 4, 19973, 4, 17976, }, { 4, 19897, 4, 17907, }, } }, { 5280, { { 4, 19859, 4, 17873, }, { 4, 19897, 4, 17907, }, { 4, 19822, 4, 17840, }, } }, { 5300, { { 4, 19784, 4, 17806, }, { 4, 19822, 4, 17840, }, { 4, 19747, 4, 17772, }, } }, { 5320, { { 4, 19710, 4, 17739, }, { 4, 19747, 4, 17772, }, { 4, 19673, 4, 17706, }, } }, { 5500, { { 4, 19065, 4, 17159, }, { 4, 19100, 4, 17190, }, { 4, 19030, 4, 17127, }, } }, { 5520, { { 4, 18996, 4, 17096, }, { 4, 19030, 4, 17127, }, { 4, 18962, 4, 17065, }, } }, { 5540, { { 4, 18927, 4, 17035, }, { 4, 18962, 4, 17065, }, { 4, 18893, 4, 17004, }, } }, { 5560, { { 4, 18859, 4, 16973, }, { 4, 18893, 4, 17004, }, { 4, 18825, 4, 16943, }, } }, { 5580, { { 4, 18792, 4, 16913, }, { 4, 18825, 4, 16943, }, { 4, 18758, 4, 16882, }, } }, { 5600, { { 4, 18725, 4, 16852, }, { 4, 18758, 4, 16882, }, { 4, 18691, 4, 16822, }, } }, { 5620, { { 4, 18658, 4, 16792, }, { 4, 18691, 4, 16822, }, { 4, 18625, 4, 16762, }, } }, { 5640, { { 4, 18592, 4, 16733, }, { 4, 18625, 4, 16762, }, { 4, 18559, 4, 16703, }, } }, { 5660, { { 4, 18526, 4, 16673, }, { 4, 18559, 4, 16703, }, { 4, 18493, 4, 16644, }, } }, { 5680, { { 4, 18461, 4, 16615, }, { 4, 18493, 4, 16644, }, { 4, 18428, 4, 16586, }, } }, { 5700, { { 4, 18396, 4, 16556, }, { 4, 18428, 4, 16586, }, { 4, 18364, 4, 16527, }, } }, { 5745, { { 4, 18252, 4, 16427, }, { 4, 18284, 4, 16455, }, { 4, 18220, 4, 16398, }, } }, { 5765, { { 4, 18189, 5, 32740, }, { 4, 18220, 4, 16398, }, { 4, 18157, 5, 32683, }, } }, { 5785, { { 4, 18126, 5, 32626, }, { 4, 18157, 5, 32683, }, { 4, 18094, 5, 32570, }, } }, { 5805, { { 4, 18063, 5, 32514, }, { 4, 18094, 5, 32570, }, { 4, 18032, 5, 32458, }, } }, { 5825, { { 4, 18001, 5, 32402, }, { 4, 18032, 5, 32458, }, { 4, 17970, 5, 32347, }, } }, { 5170, { { 4, 20282, 4, 18254, }, { 4, 20321, 4, 18289, }, { 4, 20243, 4, 18219, }, } }, { 5190, { { 4, 20204, 4, 18183, }, { 4, 20243, 4, 18219, }, { 4, 20165, 4, 18148, }, } }, { 5210, { { 4, 20126, 4, 18114, }, { 4, 20165, 4, 18148, }, { 4, 20088, 4, 18079, }, } }, { 5230, { { 4, 20049, 4, 18044, }, { 4, 20088, 4, 18079, }, { 4, 20011, 4, 18010, }, } }, }; static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz, u32 freq, enum carl9170_bw bw) { int err; u32 d0, d1, td0, td1, fd0, fd1; u8 chansel; u8 refsel0 = 1, refsel1 = 0; u8 lf_synth = 0; switch (bw) { case CARL9170_BW_40_ABOVE: freq += 10; break; case CARL9170_BW_40_BELOW: freq -= 10; break; case CARL9170_BW_20: break; default: BUG(); return -ENOSYS; } if (band5ghz) { if (freq % 10) { chansel = (freq - 4800) / 5; } else { chansel = ((freq - 4800) / 10) * 2; refsel0 = 0; refsel1 = 1; } chansel = byte_rev_table[chansel]; } else { if (freq == 2484) { chansel = 10 + (freq - 2274) / 5; lf_synth = 1; } else chansel = 16 + (freq - 2272) / 5; chansel *= 4; chansel = byte_rev_table[chansel]; } d1 = chansel; d0 = 0x21 | refsel0 << 3 | refsel1 << 2 | lf_synth << 1; td0 = d0 & 0x1f; td1 = d1 & 0x1f; fd0 = td1 << 5 | td0; td0 = (d0 >> 5) & 0x7; td1 = (d1 >> 5) & 0x7; fd1 = td1 << 5 | td0; carl9170_regwrite_begin(ar); carl9170_regwrite(0x1c58b0, fd0); carl9170_regwrite(0x1c58e8, fd1); carl9170_regwrite_finish(); err = carl9170_regwrite_result(); if (err) return err; return 0; } static const struct carl9170_phy_freq_params * carl9170_get_hw_dyn_params(struct ieee80211_channel *channel, enum carl9170_bw bw) { unsigned int chanidx = 0; u16 freq = 2412; if (channel) { chanidx = channel->hw_value; freq = channel->center_freq; } BUG_ON(chanidx >= ARRAY_SIZE(carl9170_phy_freq_params)); BUILD_BUG_ON(__CARL9170_NUM_BW != 3); WARN_ON(carl9170_phy_freq_params[chanidx].freq != freq); return &carl9170_phy_freq_params[chanidx].params[bw]; } static int carl9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f) { int idx = nfreqs - 2; while (idx >= 0) { if (f >= freqs[idx]) return idx; idx--; } return 0; } static s32 carl9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) { /* nothing to interpolate, it's horizontal */ if (y2 == y1) return y1; /* check if we hit one of the edges */ if (x == x1) return y1; if (x == x2) return y2; /* x1 == x2 is bad, hopefully == x */ if (x2 == x1) return y1; return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1)); } static u8 carl9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2) { #define SHIFT 8 s32 y; y = carl9170_interpolate_s32(x << SHIFT, x1 << SHIFT, y1 << SHIFT, x2 << SHIFT, y2 << SHIFT); /* * XXX: unwrap this expression * Isn't it just DIV_ROUND_UP(y, 1<> SHIFT) + ((y & (1 << (SHIFT - 1))) >> (SHIFT - 1)); #undef SHIFT } static u8 carl9170_interpolate_val(u8 x, u8 *x_array, u8 *y_array) { int i; for (i = 0; i < 3; i++) { if (x <= x_array[i + 1]) break; } return carl9170_interpolate_u8(x, x_array[i], y_array[i], x_array[i + 1], y_array[i + 1]); } static int carl9170_set_freq_cal_data(struct ar9170 *ar, struct ieee80211_channel *channel) { u8 *cal_freq_pier; u8 vpds[2][AR5416_PD_GAIN_ICEPTS]; u8 pwrs[2][AR5416_PD_GAIN_ICEPTS]; int chain, idx, i; u32 phy_data = 0; u8 f, tmp; switch (channel->band) { case IEEE80211_BAND_2GHZ: f = channel->center_freq - 2300; cal_freq_pier = ar->eeprom.cal_freq_pier_2G; i = AR5416_NUM_2G_CAL_PIERS - 1; break; case IEEE80211_BAND_5GHZ: f = (channel->center_freq - 4800) / 5; cal_freq_pier = ar->eeprom.cal_freq_pier_5G; i = AR5416_NUM_5G_CAL_PIERS - 1; break; default: return -EINVAL; break; } for (; i >= 0; i--) { if (cal_freq_pier[i] != 0xff) break; } if (i < 0) return -EINVAL; idx = carl9170_find_freq_idx(i, cal_freq_pier, f); carl9170_regwrite_begin(ar); for (chain = 0; chain < AR5416_MAX_CHAINS; chain++) { for (i = 0; i < AR5416_PD_GAIN_ICEPTS; i++) { struct ar9170_calibration_data_per_freq *cal_pier_data; int j; switch (channel->band) { case IEEE80211_BAND_2GHZ: cal_pier_data = &ar->eeprom. cal_pier_data_2G[chain][idx]; break; case IEEE80211_BAND_5GHZ: cal_pier_data = &ar->eeprom. cal_pier_data_5G[chain][idx]; break; default: return -EINVAL; } for (j = 0; j < 2; j++) { vpds[j][i] = carl9170_interpolate_u8(f, cal_freq_pier[idx], cal_pier_data->vpd_pdg[j][i], cal_freq_pier[idx + 1], cal_pier_data[1].vpd_pdg[j][i]); pwrs[j][i] = carl9170_interpolate_u8(f, cal_freq_pier[idx], cal_pier_data->pwr_pdg[j][i], cal_freq_pier[idx + 1], cal_pier_data[1].pwr_pdg[j][i]) / 2; } } for (i = 0; i < 76; i++) { if (i < 25) { tmp = carl9170_interpolate_val(i, &pwrs[0][0], &vpds[0][0]); } else { tmp = carl9170_interpolate_val(i - 12, &pwrs[1][0], &vpds[1][0]); } phy_data |= tmp << ((i & 3) << 3); if ((i & 3) == 3) { carl9170_regwrite(0x1c6280 + chain * 0x1000 + (i & ~3), phy_data); phy_data = 0; } } for (i = 19; i < 32; i++) carl9170_regwrite(0x1c6280 + chain * 0x1000 + (i << 2), 0x0); } carl9170_regwrite_finish(); return carl9170_regwrite_result(); } static u8 carl9170_get_max_edge_power(struct ar9170 *ar, u32 freq, struct ar9170_calctl_edges edges[]) { int i; u8 rc = AR5416_MAX_RATE_POWER; u8 f; if (freq < 3000) f = freq - 2300; else f = (freq - 4800) / 5; for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) { if (edges[i].channel == 0xff) break; if (f == edges[i].channel) { /* exact freq match */ rc = edges[i].power_flags & ~AR9170_CALCTL_EDGE_FLAGS; break; } if (i > 0 && f < edges[i].channel) { if (f > edges[i - 1].channel && edges[i - 1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { /* lower channel has the inband flag set */ rc = edges[i - 1].power_flags & ~AR9170_CALCTL_EDGE_FLAGS; } break; } } if (i == AR5416_NUM_BAND_EDGES) { if (f > edges[i - 1].channel && edges[i - 1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { /* lower channel has the inband flag set */ rc = edges[i - 1].power_flags & ~AR9170_CALCTL_EDGE_FLAGS; } } return rc; } static u8 carl9170_get_heavy_clip(struct ar9170 *ar, u32 freq, enum carl9170_bw bw, struct ar9170_calctl_edges edges[]) { u8 f; int i; u8 rc = 0; if (freq < 3000) f = freq - 2300; else f = (freq - 4800) / 5; if (bw == CARL9170_BW_40_BELOW || bw == CARL9170_BW_40_ABOVE) rc |= 0xf0; for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) { if (edges[i].channel == 0xff) break; if (f == edges[i].channel) { if (!(edges[i].power_flags & AR9170_CALCTL_EDGE_FLAGS)) rc |= 0x0f; break; } } return rc; } /* * calculate the conformance test limits and the heavy clip parameter * and apply them to ar->power* (derived from otus hal/hpmain.c, line 3706) */ static void carl9170_calc_ctl(struct ar9170 *ar, u32 freq, enum carl9170_bw bw) { u8 ctl_grp; /* CTL group */ u8 ctl_idx; /* CTL index */ int i, j; struct ctl_modes { u8 ctl_mode; u8 max_power; u8 *pwr_cal_data; int pwr_cal_len; } *modes; /* * order is relevant in the mode_list_*: we fall back to the * lower indices if any mode is missed in the EEPROM. */ struct ctl_modes mode_list_2ghz[] = { { CTL_11B, 0, ar->power_2G_cck, 4 }, { CTL_11G, 0, ar->power_2G_ofdm, 4 }, { CTL_2GHT20, 0, ar->power_2G_ht20, 8 }, { CTL_2GHT40, 0, ar->power_2G_ht40, 8 }, }; struct ctl_modes mode_list_5ghz[] = { { CTL_11A, 0, ar->power_5G_leg, 4 }, { CTL_5GHT20, 0, ar->power_5G_ht20, 8 }, { CTL_5GHT40, 0, ar->power_5G_ht40, 8 }, }; int nr_modes; #define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n]) ar->heavy_clip = 0; /* * TODO: investigate the differences between OTUS' * hpreg.c::zfHpGetRegulatoryDomain() and * ath/regd.c::ath_regd_get_band_ctl() - * e.g. for FCC3_WORLD the OTUS procedure * always returns CTL_FCC, while the one in ath/ delivers * CTL_ETSI for 2GHz and CTL_FCC for 5GHz. */ ctl_grp = ath_regd_get_band_ctl(&ar->common.regulatory, ar->hw->conf.channel->band); /* ctl group not found - either invalid band (NO_CTL) or ww roaming */ if (ctl_grp == NO_CTL || ctl_grp == SD_NO_CTL) ctl_grp = CTL_FCC; if (ctl_grp != CTL_FCC) /* skip CTL and heavy clip for CTL_MKK and CTL_ETSI */ return; if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) { modes = mode_list_2ghz; nr_modes = ARRAY_SIZE(mode_list_2ghz); } else { modes = mode_list_5ghz; nr_modes = ARRAY_SIZE(mode_list_5ghz); } for (i = 0; i < nr_modes; i++) { u8 c = ctl_grp | modes[i].ctl_mode; for (ctl_idx = 0; ctl_idx < AR5416_NUM_CTLS; ctl_idx++) if (c == ar->eeprom.ctl_index[ctl_idx]) break; if (ctl_idx < AR5416_NUM_CTLS) { int f_off = 0; /* * determine heavy clip parameter * from the 11G edges array */ if (modes[i].ctl_mode == CTL_11G) { ar->heavy_clip = carl9170_get_heavy_clip(ar, freq, bw, EDGES(ctl_idx, 1)); } /* adjust freq for 40MHz */ if (modes[i].ctl_mode == CTL_2GHT40 || modes[i].ctl_mode == CTL_5GHT40) { if (bw == CARL9170_BW_40_BELOW) f_off = -10; else f_off = 10; } modes[i].max_power = carl9170_get_max_edge_power(ar, freq + f_off, EDGES(ctl_idx, 1)); /* * TODO: check if the regulatory max. power is * controlled by cfg80211 for DFS. * (hpmain applies it to max_power itself for DFS freq) */ } else { /* * Workaround in otus driver, hpmain.c, line 3906: * if no data for 5GHT20 are found, take the * legacy 5G value. We extend this here to fallback * from any other HT* or 11G, too. */ int k = i; modes[i].max_power = AR5416_MAX_RATE_POWER; while (k-- > 0) { if (modes[k].max_power != AR5416_MAX_RATE_POWER) { modes[i].max_power = modes[k].max_power; break; } } } /* apply max power to pwr_cal_data (ar->power_*) */ for (j = 0; j < modes[i].pwr_cal_len; j++) { modes[i].pwr_cal_data[j] = min(modes[i].pwr_cal_data[j], modes[i].max_power); } } if (ar->heavy_clip & 0xf0) { ar->power_2G_ht40[0]--; ar->power_2G_ht40[1]--; ar->power_2G_ht40[2]--; } if (ar->heavy_clip & 0xf) { ar->power_2G_ht20[0]++; ar->power_2G_ht20[1]++; ar->power_2G_ht20[2]++; } #undef EDGES } static void carl9170_set_power_cal(struct ar9170 *ar, u32 freq, enum carl9170_bw bw) { struct ar9170_calibration_target_power_legacy *ctpl; struct ar9170_calibration_target_power_ht *ctph; u8 *ctpres; int ntargets; int idx, i, n; u8 f; u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS]; if (freq < 3000) f = freq - 2300; else f = (freq - 4800) / 5; /* * cycle through the various modes * * legacy modes first: 5G, 2G CCK, 2G OFDM */ for (i = 0; i < 3; i++) { switch (i) { case 0: /* 5 GHz legacy */ ctpl = &ar->eeprom.cal_tgt_pwr_5G[0]; ntargets = AR5416_NUM_5G_TARGET_PWRS; ctpres = ar->power_5G_leg; break; case 1: /* 2.4 GHz CCK */ ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0]; ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS; ctpres = ar->power_2G_cck; break; case 2: /* 2.4 GHz OFDM */ ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0]; ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; ctpres = ar->power_2G_ofdm; break; default: BUG(); } for (n = 0; n < ntargets; n++) { if (ctpl[n].freq == 0xff) break; pwr_freqs[n] = ctpl[n].freq; } ntargets = n; idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f); for (n = 0; n < 4; n++) ctpres[n] = carl9170_interpolate_u8(f, ctpl[idx + 0].freq, ctpl[idx + 0].power[n], ctpl[idx + 1].freq, ctpl[idx + 1].power[n]); } /* HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40 */ for (i = 0; i < 4; i++) { switch (i) { case 0: /* 5 GHz HT 20 */ ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0]; ntargets = AR5416_NUM_5G_TARGET_PWRS; ctpres = ar->power_5G_ht20; break; case 1: /* 5 GHz HT 40 */ ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0]; ntargets = AR5416_NUM_5G_TARGET_PWRS; ctpres = ar->power_5G_ht40; break; case 2: /* 2.4 GHz HT 20 */ ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0]; ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; ctpres = ar->power_2G_ht20; break; case 3: /* 2.4 GHz HT 40 */ ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0]; ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS; ctpres = ar->power_2G_ht40; break; default: BUG(); } for (n = 0; n < ntargets; n++) { if (ctph[n].freq == 0xff) break; pwr_freqs[n] = ctph[n].freq; } ntargets = n; idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f); for (n = 0; n < 8; n++) ctpres[n] = carl9170_interpolate_u8(f, ctph[idx + 0].freq, ctph[idx + 0].power[n], ctph[idx + 1].freq, ctph[idx + 1].power[n]); } /* calc. conformance test limits and apply to ar->power*[] */ carl9170_calc_ctl(ar, freq, bw); } int carl9170_get_noisefloor(struct ar9170 *ar) { static const u32 phy_regs[] = { AR9170_PHY_REG_CCA, AR9170_PHY_REG_CH2_CCA, AR9170_PHY_REG_EXT_CCA, AR9170_PHY_REG_CH2_EXT_CCA }; u32 phy_res[ARRAY_SIZE(phy_regs)]; int err, i; BUILD_BUG_ON(ARRAY_SIZE(phy_regs) != ARRAY_SIZE(ar->noise)); err = carl9170_read_mreg(ar, ARRAY_SIZE(phy_regs), phy_regs, phy_res); if (err) return err; for (i = 0; i < 2; i++) { ar->noise[i] = sign_extend32(GET_VAL( AR9170_PHY_CCA_MIN_PWR, phy_res[i]), 8); ar->noise[i + 2] = sign_extend32(GET_VAL( AR9170_PHY_EXT_CCA_MIN_PWR, phy_res[i + 2]), 8); } if (ar->channel) ar->survey[ar->channel->hw_value].noise = ar->noise[0]; return 0; } static enum carl9170_bw nl80211_to_carl(enum nl80211_channel_type type) { switch (type) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: return CARL9170_BW_20; case NL80211_CHAN_HT40MINUS: return CARL9170_BW_40_BELOW; case NL80211_CHAN_HT40PLUS: return CARL9170_BW_40_ABOVE; default: BUG(); } } int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, enum nl80211_channel_type _bw, enum carl9170_rf_init_mode rfi) { const struct carl9170_phy_freq_params *freqpar; struct carl9170_rf_init_result rf_res; struct carl9170_rf_init rf; u32 cmd, tmp, offs = 0, new_ht = 0; int err; enum carl9170_bw bw; bool warm_reset; struct ieee80211_channel *old_channel = NULL; bw = nl80211_to_carl(_bw); if (conf_is_ht(&ar->hw->conf)) new_ht |= CARL9170FW_PHY_HT_ENABLE; if (conf_is_ht40(&ar->hw->conf)) new_ht |= CARL9170FW_PHY_HT_DYN2040; /* may be NULL at first setup */ if (ar->channel) { old_channel = ar->channel; warm_reset = (old_channel->band != channel->band) || (old_channel->center_freq == channel->center_freq) || (ar->ht_settings != new_ht); ar->channel = NULL; } else { warm_reset = true; } /* HW workaround */ if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] && channel->center_freq <= 2417) warm_reset = true; if (rfi != CARL9170_RFI_NONE || warm_reset) { u32 val; if (rfi == CARL9170_RFI_COLD) val = AR9170_PWR_RESET_BB_COLD_RESET; else val = AR9170_PWR_RESET_BB_WARM_RESET; /* warm/cold reset BB/ADDA */ err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, val); if (err) return err; err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, 0x0); if (err) return err; err = carl9170_init_phy(ar, channel->band); if (err) return err; err = carl9170_init_rf_banks_0_7(ar, channel->band == IEEE80211_BAND_5GHZ); if (err) return err; cmd = CARL9170_CMD_RF_INIT; } else { cmd = CARL9170_CMD_FREQUENCY; } err = carl9170_exec_cmd(ar, CARL9170_CMD_FREQ_START, 0, NULL, 0, NULL); if (err) return err; err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE, 0x200); if (err) return err; err = carl9170_init_rf_bank4_pwr(ar, channel->band == IEEE80211_BAND_5GHZ, channel->center_freq, bw); if (err) return err; tmp = AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 | AR9170_PHY_TURBO_FC_HT_EN; switch (bw) { case CARL9170_BW_20: break; case CARL9170_BW_40_BELOW: tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN | AR9170_PHY_TURBO_FC_SHORT_GI_40; offs = 3; break; case CARL9170_BW_40_ABOVE: tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN | AR9170_PHY_TURBO_FC_SHORT_GI_40 | AR9170_PHY_TURBO_FC_DYN2040_PRI_CH; offs = 1; break; default: BUG(); return -ENOSYS; } if (ar->eeprom.tx_mask != 1) tmp |= AR9170_PHY_TURBO_FC_WALSH; err = carl9170_write_reg(ar, AR9170_PHY_REG_TURBO, tmp); if (err) return err; err = carl9170_set_freq_cal_data(ar, channel); if (err) return err; carl9170_set_power_cal(ar, channel->center_freq, bw); err = carl9170_set_mac_tpc(ar, channel); if (err) return err; freqpar = carl9170_get_hw_dyn_params(channel, bw); rf.ht_settings = new_ht; if (conf_is_ht40(&ar->hw->conf)) SET_VAL(CARL9170FW_PHY_HT_EXT_CHAN_OFF, rf.ht_settings, offs); rf.freq = cpu_to_le32(channel->center_freq * 1000); rf.delta_slope_coeff_exp = cpu_to_le32(freqpar->coeff_exp); rf.delta_slope_coeff_man = cpu_to_le32(freqpar->coeff_man); rf.delta_slope_coeff_exp_shgi = cpu_to_le32(freqpar->coeff_exp_shgi); rf.delta_slope_coeff_man_shgi = cpu_to_le32(freqpar->coeff_man_shgi); if (rfi != CARL9170_RFI_NONE) rf.finiteLoopCount = cpu_to_le32(2000); else rf.finiteLoopCount = cpu_to_le32(1000); err = carl9170_exec_cmd(ar, cmd, sizeof(rf), &rf, sizeof(rf_res), &rf_res); if (err) return err; err = le32_to_cpu(rf_res.ret); if (err != 0) { ar->chan_fail++; ar->total_chan_fail++; wiphy_err(ar->hw->wiphy, "channel change: %d -> %d " "failed (%d).\n", old_channel ? old_channel->center_freq : -1, channel->center_freq, err); if ((rfi == CARL9170_RFI_COLD) || (ar->chan_fail > 3)) { /* * We have tried very hard to change to _another_ * channel and we've failed to do so! * Chances are that the PHY/RF is no longer * operable (due to corruptions/fatal events/bugs?) * and we need to reset at a higher level. */ carl9170_restart(ar, CARL9170_RR_TOO_MANY_PHY_ERRORS); return 0; } err = carl9170_set_channel(ar, channel, _bw, CARL9170_RFI_COLD); if (err) return err; } else { ar->chan_fail = 0; } if (ar->heavy_clip) { err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE, 0x200 | ar->heavy_clip); if (err) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "failed to set " "heavy clip\n"); } return err; } } ar->channel = channel; ar->ht_settings = new_ht; return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/mac.c0000644000175000017500000003335312026211315023412 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * MAC programming * * Copyright 2008, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "carl9170.h" #include "cmd.h" int carl9170_set_dyn_sifs_ack(struct ar9170 *ar) { u32 val; if (conf_is_ht40(&ar->hw->conf)) val = 0x010a; else { if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) val = 0x105; else val = 0x104; } return carl9170_write_reg(ar, AR9170_MAC_REG_DYNAMIC_SIFS_ACK, val); } int carl9170_set_rts_cts_rate(struct ar9170 *ar) { u32 rts_rate, cts_rate; if (conf_is_ht(&ar->hw->conf)) { /* 12 mbit OFDM */ rts_rate = 0x1da; cts_rate = 0x10a; } else { if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) { /* 11 mbit CCK */ rts_rate = 033; cts_rate = 003; } else { /* 6 mbit OFDM */ rts_rate = 0x1bb; cts_rate = 0x10b; } } return carl9170_write_reg(ar, AR9170_MAC_REG_RTS_CTS_RATE, rts_rate | (cts_rate) << 16); } int carl9170_set_slot_time(struct ar9170 *ar) { struct ieee80211_vif *vif; u32 slottime = 20; rcu_read_lock(); vif = carl9170_get_main_vif(ar); if (!vif) { rcu_read_unlock(); return 0; } if ((ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) || vif->bss_conf.use_short_slot) slottime = 9; rcu_read_unlock(); return carl9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME, slottime << 10); } int carl9170_set_mac_rates(struct ar9170 *ar) { struct ieee80211_vif *vif; u32 basic, mandatory; rcu_read_lock(); vif = carl9170_get_main_vif(ar); if (!vif) { rcu_read_unlock(); return 0; } basic = (vif->bss_conf.basic_rates & 0xf); basic |= (vif->bss_conf.basic_rates & 0xff0) << 4; rcu_read_unlock(); if (ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) mandatory = 0xff00; /* OFDM 6/9/12/18/24/36/48/54 */ else mandatory = 0xff0f; /* OFDM (6/9../54) + CCK (1/2/5.5/11) */ carl9170_regwrite_begin(ar); carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, basic); carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, mandatory); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } int carl9170_set_qos(struct ar9170 *ar) { carl9170_regwrite_begin(ar); carl9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min | (ar->edcf[0].cw_max << 16)); carl9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min | (ar->edcf[1].cw_max << 16)); carl9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min | (ar->edcf[2].cw_max << 16)); carl9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min | (ar->edcf[3].cw_max << 16)); carl9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min | (ar->edcf[4].cw_max << 16)); carl9170_regwrite(AR9170_MAC_REG_AC2_AC1_AC0_AIFS, ((ar->edcf[0].aifs * 9 + 10)) | ((ar->edcf[1].aifs * 9 + 10) << 12) | ((ar->edcf[2].aifs * 9 + 10) << 24)); carl9170_regwrite(AR9170_MAC_REG_AC4_AC3_AC2_AIFS, ((ar->edcf[2].aifs * 9 + 10) >> 8) | ((ar->edcf[3].aifs * 9 + 10) << 4) | ((ar->edcf[4].aifs * 9 + 10) << 16)); carl9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP, ar->edcf[0].txop | ar->edcf[1].txop << 16); carl9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP, ar->edcf[2].txop | ar->edcf[3].txop << 16 | ar->edcf[4].txop << 24); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } int carl9170_init_mac(struct ar9170 *ar) { carl9170_regwrite_begin(ar); /* switch MAC to OTUS interface */ carl9170_regwrite(0x1c3600, 0x3); carl9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40); carl9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0x0); carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER, AR9170_MAC_FTF_MONITOR); /* enable MMIC */ carl9170_regwrite(AR9170_MAC_REG_SNIFFER, AR9170_MAC_SNIFFER_DEFAULTS); carl9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80); carl9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70); carl9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000); carl9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10); /* CF-END & CF-ACK rate => 24M OFDM */ carl9170_regwrite(AR9170_MAC_REG_TID_CFACK_CFEND_RATE, 0x59900000); /* NAV protects ACK only (in TXOP) */ carl9170_regwrite(AR9170_MAC_REG_TXOP_DURATION, 0x201); /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */ /* OTUS set AM to 0x1 */ carl9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170); carl9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105); /* Aggregation MAX number and timeout */ carl9170_regwrite(AR9170_MAC_REG_AMPDU_FACTOR, 0x8000a); carl9170_regwrite(AR9170_MAC_REG_AMPDU_DENSITY, 0x140a07); carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER, AR9170_MAC_FTF_DEFAULTS); carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL, AR9170_MAC_RX_CTRL_DEAGG | AR9170_MAC_RX_CTRL_SHORT_FILTER); /* rate sets */ carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f); carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f); carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x0030033); /* MIMO response control */ carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, 0x4003c1e); carl9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff); /* set PHY register read timeout (??) */ carl9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008); /* Disable Rx TimeOut, workaround for BB. */ carl9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0); /* Set WLAN DMA interrupt mode: generate int per packet */ carl9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011); carl9170_regwrite(AR9170_MAC_REG_FCS_SELECT, AR9170_MAC_FCS_FIFO_PROT); /* Disables the CF_END frame, undocumented register */ carl9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND, 0x141e0f48); /* reset group hash table */ carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, 0xffffffff); carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, 0xffffffff); /* disable PRETBTT interrupt */ carl9170_regwrite(AR9170_MAC_REG_PRETBTT, 0x0); carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, 0x0); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } static int carl9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac) { static const u8 zero[ETH_ALEN] = { 0 }; if (!mac) mac = zero; carl9170_regwrite_begin(ar); carl9170_regwrite(reg, get_unaligned_le32(mac)); carl9170_regwrite(reg + 4, get_unaligned_le16(mac + 4)); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id, const u8 *mac) { if (WARN_ON(id >= ar->fw.vif_num)) return -EINVAL; return carl9170_set_mac_reg(ar, AR9170_MAC_REG_ACK_TABLE + (id - 1) * 8, mac); } int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hash) { int err; carl9170_regwrite_begin(ar); carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, mc_hash >> 32); carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, mc_hash); carl9170_regwrite_finish(); err = carl9170_regwrite_result(); if (err) return err; ar->cur_mc_hash = mc_hash; return 0; } int carl9170_set_operating_mode(struct ar9170 *ar) { struct ieee80211_vif *vif; struct ath_common *common = &ar->common; u8 *mac_addr, *bssid; u32 cam_mode = AR9170_MAC_CAM_DEFAULTS; u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS; u32 rx_ctrl = AR9170_MAC_RX_CTRL_DEAGG | AR9170_MAC_RX_CTRL_SHORT_FILTER; u32 sniffer = AR9170_MAC_SNIFFER_DEFAULTS; int err = 0; rcu_read_lock(); vif = carl9170_get_main_vif(ar); if (vif) { mac_addr = common->macaddr; bssid = common->curbssid; switch (vif->type) { case NL80211_IFTYPE_ADHOC: cam_mode |= AR9170_MAC_CAM_IBSS; break; case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: cam_mode |= AR9170_MAC_CAM_AP; /* iwlagn 802.11n STA Workaround */ rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST; break; case NL80211_IFTYPE_WDS: cam_mode |= AR9170_MAC_CAM_AP_WDS; rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST; break; case NL80211_IFTYPE_STATION: cam_mode |= AR9170_MAC_CAM_STA; rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST; break; default: WARN(1, "Unsupported operation mode %x\n", vif->type); err = -EOPNOTSUPP; break; } } else { mac_addr = NULL; bssid = NULL; } rcu_read_unlock(); if (err) return err; if (ar->rx_software_decryption) enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE; if (ar->sniffer_enabled) { rx_ctrl |= AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER; sniffer |= AR9170_MAC_SNIFFER_ENABLE_PROMISC; enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE; } err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr); if (err) return err; err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid); if (err) return err; carl9170_regwrite_begin(ar); carl9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer); carl9170_regwrite(AR9170_MAC_REG_CAM_MODE, cam_mode); carl9170_regwrite(AR9170_MAC_REG_ENCRYPTION, enc_mode); carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL, rx_ctrl); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } int carl9170_set_hwretry_limit(struct ar9170 *ar, const unsigned int max_retry) { u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111); return carl9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp); } int carl9170_set_beacon_timers(struct ar9170 *ar) { struct ieee80211_vif *vif; u32 v = 0; u32 pretbtt = 0; rcu_read_lock(); vif = carl9170_get_main_vif(ar); if (vif) { struct carl9170_vif_info *mvif; mvif = (void *) vif->drv_priv; if (mvif->enable_beacon && !WARN_ON(!ar->beacon_enabled)) { ar->global_beacon_int = vif->bss_conf.beacon_int / ar->beacon_enabled; SET_VAL(AR9170_MAC_BCN_DTIM, v, vif->bss_conf.dtim_period); switch (vif->type) { case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_ADHOC: v |= AR9170_MAC_BCN_IBSS_MODE; break; case NL80211_IFTYPE_AP: v |= AR9170_MAC_BCN_AP_MODE; break; default: WARN_ON_ONCE(1); break; } } else if (vif->type == NL80211_IFTYPE_STATION) { ar->global_beacon_int = vif->bss_conf.beacon_int; SET_VAL(AR9170_MAC_BCN_DTIM, v, ar->hw->conf.ps_dtim_period); v |= AR9170_MAC_BCN_STA_PS | AR9170_MAC_BCN_PWR_MGT; } if (ar->global_beacon_int) { if (ar->global_beacon_int < 15) { rcu_read_unlock(); return -ERANGE; } ar->global_pretbtt = ar->global_beacon_int - CARL9170_PRETBTT_KUS; } else { ar->global_pretbtt = 0; } } else { ar->global_beacon_int = 0; ar->global_pretbtt = 0; } rcu_read_unlock(); SET_VAL(AR9170_MAC_BCN_PERIOD, v, ar->global_beacon_int); SET_VAL(AR9170_MAC_PRETBTT, pretbtt, ar->global_pretbtt); SET_VAL(AR9170_MAC_PRETBTT2, pretbtt, ar->global_pretbtt); carl9170_regwrite_begin(ar); carl9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt); carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac, const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen) { struct carl9170_set_key_cmd key = { }; static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; mac = mac ? : bcast; key.user = cpu_to_le16(id); key.keyId = cpu_to_le16(keyidx); key.type = cpu_to_le16(ktype); memcpy(&key.macAddr, mac, ETH_ALEN); if (keydata) memcpy(&key.key, keydata, keylen); return carl9170_exec_cmd(ar, CARL9170_CMD_EKEY, sizeof(key), (u8 *)&key, 0, NULL); } int carl9170_disable_key(struct ar9170 *ar, const u8 id) { struct carl9170_disable_key_cmd key = { }; key.user = cpu_to_le16(id); return carl9170_exec_cmd(ar, CARL9170_CMD_DKEY, sizeof(key), (u8 *)&key, 0, NULL); } int carl9170_set_mac_tpc(struct ar9170 *ar, struct ieee80211_channel *channel) { unsigned int power, chains; if (ar->eeprom.tx_mask != 1) chains = AR9170_TX_PHY_TXCHAIN_2; else chains = AR9170_TX_PHY_TXCHAIN_1; switch (channel->band) { case IEEE80211_BAND_2GHZ: power = ar->power_2G_ofdm[0] & 0x3f; break; case IEEE80211_BAND_5GHZ: power = ar->power_5G_leg[0] & 0x3f; break; default: BUG_ON(1); } power = min_t(unsigned int, power, ar->hw->conf.power_level * 2); carl9170_regwrite_begin(ar); carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, 0x3c1e | power << 20 | chains << 26); carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_TPC, power << 5 | chains << 11 | power << 21 | chains << 27); carl9170_regwrite(AR9170_MAC_REG_CFEND_QOSNULL_TPC, power << 5 | chains << 11 | power << 21 | chains << 27); carl9170_regwrite_finish(); return carl9170_regwrite_result(); } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/led.c0000644000175000017500000001157712026211315023422 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * LED handling * * Copyright 2008, Johannes Berg * Copyright 2009, 2010, Christian Lamparer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "carl9170.h" #include "cmd.h" int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state) { return carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_DATA, led_state); } int carl9170_led_init(struct ar9170 *ar) { int err; /* disable LEDs */ /* GPIO [0/1 mode: output, 2/3: input] */ err = carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3); if (err) goto out; /* GPIO 0/1 value: off */ err = carl9170_led_set_state(ar, 0); out: return err; } #ifdef CONFIG_CARL9170_LEDS static void carl9170_led_update(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, led_work.work); int i, tmp = 300, blink_delay = 1000; u32 led_val = 0; bool rerun = false; if (!IS_ACCEPTING_CMD(ar)) return; mutex_lock(&ar->mutex); for (i = 0; i < AR9170_NUM_LEDS; i++) { if (ar->leds[i].registered) { if (ar->leds[i].last_state || ar->leds[i].toggled) { if (ar->leds[i].toggled) tmp = 70 + 200 / (ar->leds[i].toggled); if (tmp < blink_delay) blink_delay = tmp; led_val |= 1 << i; ar->leds[i].toggled = 0; rerun = true; } } } carl9170_led_set_state(ar, led_val); mutex_unlock(&ar->mutex); if (!rerun) return; ieee80211_queue_delayed_work(ar->hw, &ar->led_work, msecs_to_jiffies(blink_delay)); } static void carl9170_led_set_brightness(struct led_classdev *led, enum led_brightness brightness) { struct carl9170_led *arl = container_of(led, struct carl9170_led, l); struct ar9170 *ar = arl->ar; if (!arl->registered) return; if (arl->last_state != !!brightness) { arl->toggled++; arl->last_state = !!brightness; } if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled)) ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ / 10); } static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name, char *trigger) { int err; snprintf(ar->leds[i].name, sizeof(ar->leds[i].name), "carl9170-%s::%s", wiphy_name(ar->hw->wiphy), name); ar->leds[i].ar = ar; ar->leds[i].l.name = ar->leds[i].name; ar->leds[i].l.brightness_set = carl9170_led_set_brightness; ar->leds[i].l.brightness = 0; ar->leds[i].l.default_trigger = trigger; err = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds[i].l); if (err) { wiphy_err(ar->hw->wiphy, "failed to register %s LED (%d).\n", ar->leds[i].name, err); } else { ar->leds[i].registered = true; } return err; } void carl9170_led_unregister(struct ar9170 *ar) { int i; for (i = 0; i < AR9170_NUM_LEDS; i++) if (ar->leds[i].registered) { led_classdev_unregister(&ar->leds[i].l); ar->leds[i].registered = false; ar->leds[i].toggled = 0; } cancel_delayed_work_sync(&ar->led_work); } int carl9170_led_register(struct ar9170 *ar) { int err; INIT_DELAYED_WORK(&ar->led_work, carl9170_led_update); err = carl9170_led_register_led(ar, 0, "tx", ieee80211_get_tx_led_name(ar->hw)); if (err) goto fail; if (ar->features & CARL9170_ONE_LED) return 0; err = carl9170_led_register_led(ar, 1, "assoc", ieee80211_get_assoc_led_name(ar->hw)); if (err) goto fail; return 0; fail: carl9170_led_unregister(ar); return err; } #endif /* CONFIG_CARL9170_LEDS */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/hw.h0000644000175000017500000010610212026211315023266 0ustar mcgrofmcgrof/* * Shared Atheros AR9170 Header * * Register map, hardware-specific definitions * * Copyright 2008, Johannes Berg * Copyright 2009-2011 Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CARL9170_SHARED_HW_H #define __CARL9170_SHARED_HW_H /* High Speed UART */ #define AR9170_UART_REG_BASE 0x1c0000 /* Definitions of interrupt registers */ #define AR9170_UART_REG_RX_BUFFER (AR9170_UART_REG_BASE + 0x000) #define AR9170_UART_REG_TX_HOLDING (AR9170_UART_REG_BASE + 0x004) #define AR9170_UART_REG_FIFO_CONTROL (AR9170_UART_REG_BASE + 0x010) #define AR9170_UART_FIFO_CTRL_RESET_RX_FIFO 0x02 #define AR9170_UART_FIFO_CTRL_RESET_TX_FIFO 0x04 #define AR9170_UART_REG_LINE_CONTROL (AR9170_UART_REG_BASE + 0x014) #define AR9170_UART_REG_MODEM_CONTROL (AR9170_UART_REG_BASE + 0x018) #define AR9170_UART_MODEM_CTRL_DTR_BIT 0x01 #define AR9170_UART_MODEM_CTRL_RTS_BIT 0x02 #define AR9170_UART_MODEM_CTRL_INTERNAL_LOOP_BACK 0x10 #define AR9170_UART_MODEM_CTRL_AUTO_RTS 0x20 #define AR9170_UART_MODEM_CTRL_AUTO_CTR 0x40 #define AR9170_UART_REG_LINE_STATUS (AR9170_UART_REG_BASE + 0x01c) #define AR9170_UART_LINE_STS_RX_DATA_READY 0x01 #define AR9170_UART_LINE_STS_RX_BUFFER_OVERRUN 0x02 #define AR9170_UART_LINE_STS_RX_BREAK_IND 0x10 #define AR9170_UART_LINE_STS_TX_FIFO_NEAR_EMPTY 0x20 #define AR9170_UART_LINE_STS_TRANSMITTER_EMPTY 0x40 #define AR9170_UART_REG_MODEM_STATUS (AR9170_UART_REG_BASE + 0x020) #define AR9170_UART_MODEM_STS_CTS_CHANGE 0x01 #define AR9170_UART_MODEM_STS_DSR_CHANGE 0x02 #define AR9170_UART_MODEM_STS_DCD_CHANGE 0x08 #define AR9170_UART_MODEM_STS_CTS_COMPL 0x10 #define AR9170_UART_MODEM_STS_DSR_COMPL 0x20 #define AR9170_UART_MODEM_STS_DCD_COMPL 0x80 #define AR9170_UART_REG_SCRATCH (AR9170_UART_REG_BASE + 0x024) #define AR9170_UART_REG_DIVISOR_LSB (AR9170_UART_REG_BASE + 0x028) #define AR9170_UART_REG_DIVISOR_MSB (AR9170_UART_REG_BASE + 0x02c) #define AR9170_UART_REG_WORD_RX_BUFFER (AR9170_UART_REG_BASE + 0x034) #define AR9170_UART_REG_WORD_TX_HOLDING (AR9170_UART_REG_BASE + 0x038) #define AR9170_UART_REG_FIFO_COUNT (AR9170_UART_REG_BASE + 0x03c) #define AR9170_UART_REG_REMAINDER (AR9170_UART_REG_BASE + 0x04c) /* Timer */ #define AR9170_TIMER_REG_BASE 0x1c1000 #define AR9170_TIMER_REG_WATCH_DOG (AR9170_TIMER_REG_BASE + 0x000) #define AR9170_TIMER_REG_TIMER0 (AR9170_TIMER_REG_BASE + 0x010) #define AR9170_TIMER_REG_TIMER1 (AR9170_TIMER_REG_BASE + 0x014) #define AR9170_TIMER_REG_TIMER2 (AR9170_TIMER_REG_BASE + 0x018) #define AR9170_TIMER_REG_TIMER3 (AR9170_TIMER_REG_BASE + 0x01c) #define AR9170_TIMER_REG_TIMER4 (AR9170_TIMER_REG_BASE + 0x020) #define AR9170_TIMER_REG_CONTROL (AR9170_TIMER_REG_BASE + 0x024) #define AR9170_TIMER_CTRL_DISABLE_CLOCK 0x100 #define AR9170_TIMER_REG_INTERRUPT (AR9170_TIMER_REG_BASE + 0x028) #define AR9170_TIMER_INT_TIMER0 0x001 #define AR9170_TIMER_INT_TIMER1 0x002 #define AR9170_TIMER_INT_TIMER2 0x004 #define AR9170_TIMER_INT_TIMER3 0x008 #define AR9170_TIMER_INT_TIMER4 0x010 #define AR9170_TIMER_INT_TICK_TIMER 0x100 #define AR9170_TIMER_REG_TICK_TIMER (AR9170_TIMER_REG_BASE + 0x030) #define AR9170_TIMER_REG_CLOCK_LOW (AR9170_TIMER_REG_BASE + 0x040) #define AR9170_TIMER_REG_CLOCK_HIGH (AR9170_TIMER_REG_BASE + 0x044) #define AR9170_MAC_REG_BASE 0x1c3000 #define AR9170_MAC_REG_POWER_STATE_CTRL (AR9170_MAC_REG_BASE + 0x500) #define AR9170_MAC_POWER_STATE_CTRL_RESET 0x20 #define AR9170_MAC_REG_MAC_POWER_STATE_CTRL (AR9170_MAC_REG_BASE + 0x50c) #define AR9170_MAC_REG_INT_CTRL (AR9170_MAC_REG_BASE + 0x510) #define AR9170_MAC_INT_TXC BIT(0) #define AR9170_MAC_INT_RXC BIT(1) #define AR9170_MAC_INT_RETRY_FAIL BIT(2) #define AR9170_MAC_INT_WAKEUP BIT(3) #define AR9170_MAC_INT_ATIM BIT(4) #define AR9170_MAC_INT_DTIM BIT(5) #define AR9170_MAC_INT_CFG_BCN BIT(6) #define AR9170_MAC_INT_ABORT BIT(7) #define AR9170_MAC_INT_QOS BIT(8) #define AR9170_MAC_INT_MIMO_PS BIT(9) #define AR9170_MAC_INT_KEY_GEN BIT(10) #define AR9170_MAC_INT_DECRY_NOUSER BIT(11) #define AR9170_MAC_INT_RADAR BIT(12) #define AR9170_MAC_INT_QUIET_FRAME BIT(13) #define AR9170_MAC_INT_PRETBTT BIT(14) #define AR9170_MAC_REG_TSF_L (AR9170_MAC_REG_BASE + 0x514) #define AR9170_MAC_REG_TSF_H (AR9170_MAC_REG_BASE + 0x518) #define AR9170_MAC_REG_ATIM_WINDOW (AR9170_MAC_REG_BASE + 0x51c) #define AR9170_MAC_ATIM_PERIOD_S 0 #define AR9170_MAC_ATIM_PERIOD 0x0000ffff #define AR9170_MAC_REG_BCN_PERIOD (AR9170_MAC_REG_BASE + 0x520) #define AR9170_MAC_BCN_PERIOD_S 0 #define AR9170_MAC_BCN_PERIOD 0x0000ffff #define AR9170_MAC_BCN_DTIM_S 16 #define AR9170_MAC_BCN_DTIM 0x00ff0000 #define AR9170_MAC_BCN_AP_MODE BIT(24) #define AR9170_MAC_BCN_IBSS_MODE BIT(25) #define AR9170_MAC_BCN_PWR_MGT BIT(26) #define AR9170_MAC_BCN_STA_PS BIT(27) #define AR9170_MAC_REG_PRETBTT (AR9170_MAC_REG_BASE + 0x524) #define AR9170_MAC_PRETBTT_S 0 #define AR9170_MAC_PRETBTT 0x0000ffff #define AR9170_MAC_PRETBTT2_S 16 #define AR9170_MAC_PRETBTT2 0xffff0000 #define AR9170_MAC_REG_MAC_ADDR_L (AR9170_MAC_REG_BASE + 0x610) #define AR9170_MAC_REG_MAC_ADDR_H (AR9170_MAC_REG_BASE + 0x614) #define AR9170_MAC_REG_BSSID_L (AR9170_MAC_REG_BASE + 0x618) #define AR9170_MAC_REG_BSSID_H (AR9170_MAC_REG_BASE + 0x61c) #define AR9170_MAC_REG_GROUP_HASH_TBL_L (AR9170_MAC_REG_BASE + 0x624) #define AR9170_MAC_REG_GROUP_HASH_TBL_H (AR9170_MAC_REG_BASE + 0x628) #define AR9170_MAC_REG_RX_TIMEOUT (AR9170_MAC_REG_BASE + 0x62c) #define AR9170_MAC_REG_BASIC_RATE (AR9170_MAC_REG_BASE + 0x630) #define AR9170_MAC_REG_MANDATORY_RATE (AR9170_MAC_REG_BASE + 0x634) #define AR9170_MAC_REG_RTS_CTS_RATE (AR9170_MAC_REG_BASE + 0x638) #define AR9170_MAC_REG_BACKOFF_PROTECT (AR9170_MAC_REG_BASE + 0x63c) #define AR9170_MAC_REG_RX_THRESHOLD (AR9170_MAC_REG_BASE + 0x640) #define AR9170_MAC_REG_AFTER_PNP (AR9170_MAC_REG_BASE + 0x648) #define AR9170_MAC_REG_RX_PE_DELAY (AR9170_MAC_REG_BASE + 0x64c) #define AR9170_MAC_REG_DYNAMIC_SIFS_ACK (AR9170_MAC_REG_BASE + 0x658) #define AR9170_MAC_REG_SNIFFER (AR9170_MAC_REG_BASE + 0x674) #define AR9170_MAC_SNIFFER_ENABLE_PROMISC BIT(0) #define AR9170_MAC_SNIFFER_DEFAULTS 0x02000000 #define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678) #define AR9170_MAC_ENCRYPTION_MGMT_RX_SOFTWARE BIT(2) #define AR9170_MAC_ENCRYPTION_RX_SOFTWARE BIT(3) #define AR9170_MAC_ENCRYPTION_DEFAULTS 0x70 #define AR9170_MAC_REG_MISC_680 (AR9170_MAC_REG_BASE + 0x680) #define AR9170_MAC_REG_MISC_684 (AR9170_MAC_REG_BASE + 0x684) #define AR9170_MAC_REG_TX_UNDERRUN (AR9170_MAC_REG_BASE + 0x688) #define AR9170_MAC_REG_FRAMETYPE_FILTER (AR9170_MAC_REG_BASE + 0x68c) #define AR9170_MAC_FTF_ASSOC_REQ BIT(0) #define AR9170_MAC_FTF_ASSOC_RESP BIT(1) #define AR9170_MAC_FTF_REASSOC_REQ BIT(2) #define AR9170_MAC_FTF_REASSOC_RESP BIT(3) #define AR9170_MAC_FTF_PRB_REQ BIT(4) #define AR9170_MAC_FTF_PRB_RESP BIT(5) #define AR9170_MAC_FTF_BIT6 BIT(6) #define AR9170_MAC_FTF_BIT7 BIT(7) #define AR9170_MAC_FTF_BEACON BIT(8) #define AR9170_MAC_FTF_ATIM BIT(9) #define AR9170_MAC_FTF_DEASSOC BIT(10) #define AR9170_MAC_FTF_AUTH BIT(11) #define AR9170_MAC_FTF_DEAUTH BIT(12) #define AR9170_MAC_FTF_BIT13 BIT(13) #define AR9170_MAC_FTF_BIT14 BIT(14) #define AR9170_MAC_FTF_BIT15 BIT(15) #define AR9170_MAC_FTF_BAR BIT(24) #define AR9170_MAC_FTF_BA BIT(25) #define AR9170_MAC_FTF_PSPOLL BIT(26) #define AR9170_MAC_FTF_RTS BIT(27) #define AR9170_MAC_FTF_CTS BIT(28) #define AR9170_MAC_FTF_ACK BIT(29) #define AR9170_MAC_FTF_CFE BIT(30) #define AR9170_MAC_FTF_CFE_ACK BIT(31) #define AR9170_MAC_FTF_DEFAULTS 0x0500ffff #define AR9170_MAC_FTF_MONITOR 0xff00ffff #define AR9170_MAC_REG_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0x690) #define AR9170_MAC_REG_ACK_TPC (AR9170_MAC_REG_BASE + 0x694) #define AR9170_MAC_REG_EIFS_AND_SIFS (AR9170_MAC_REG_BASE + 0x698) #define AR9170_MAC_REG_RX_TIMEOUT_COUNT (AR9170_MAC_REG_BASE + 0x69c) #define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6a0) #define AR9170_MAC_REG_RX_CRC32 (AR9170_MAC_REG_BASE + 0x6a4) #define AR9170_MAC_REG_RX_CRC16 (AR9170_MAC_REG_BASE + 0x6a8) #define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI (AR9170_MAC_REG_BASE + 0x6ac) #define AR9170_MAC_REG_RX_OVERRUN (AR9170_MAC_REG_BASE + 0x6b0) #define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL (AR9170_MAC_REG_BASE + 0x6bc) #define AR9170_MAC_REG_TX_BLOCKACKS (AR9170_MAC_REG_BASE + 0x6c0) #define AR9170_MAC_REG_NAV_COUNT (AR9170_MAC_REG_BASE + 0x6c4) #define AR9170_MAC_REG_BACKOFF_STATUS (AR9170_MAC_REG_BASE + 0x6c8) #define AR9170_MAC_BACKOFF_CCA BIT(24) #define AR9170_MAC_BACKOFF_TX_PEX BIT(25) #define AR9170_MAC_BACKOFF_RX_PE BIT(26) #define AR9170_MAC_BACKOFF_MD_READY BIT(27) #define AR9170_MAC_BACKOFF_TX_PE BIT(28) #define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6cc) #define AR9170_MAC_REG_TX_COMPLETE (AR9170_MAC_REG_BASE + 0x6d4) #define AR9170_MAC_REG_CHANNEL_BUSY (AR9170_MAC_REG_BASE + 0x6e8) #define AR9170_MAC_REG_EXT_BUSY (AR9170_MAC_REG_BASE + 0x6ec) #define AR9170_MAC_REG_SLOT_TIME (AR9170_MAC_REG_BASE + 0x6f0) #define AR9170_MAC_REG_TX_TOTAL (AR9170_MAC_REG_BASE + 0x6f4) #define AR9170_MAC_REG_ACK_FC (AR9170_MAC_REG_BASE + 0x6f8) #define AR9170_MAC_REG_CAM_MODE (AR9170_MAC_REG_BASE + 0x700) #define AR9170_MAC_CAM_IBSS 0xe0 #define AR9170_MAC_CAM_AP 0xa1 #define AR9170_MAC_CAM_STA 0x2 #define AR9170_MAC_CAM_AP_WDS 0x3 #define AR9170_MAC_CAM_DEFAULTS (0xf << 24) #define AR9170_MAC_CAM_HOST_PENDING 0x80000000 #define AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L (AR9170_MAC_REG_BASE + 0x704) #define AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H (AR9170_MAC_REG_BASE + 0x708) #define AR9170_MAC_REG_CAM_ADDR (AR9170_MAC_REG_BASE + 0x70c) #define AR9170_MAC_CAM_ADDR_WRITE 0x80000000 #define AR9170_MAC_REG_CAM_DATA0 (AR9170_MAC_REG_BASE + 0x720) #define AR9170_MAC_REG_CAM_DATA1 (AR9170_MAC_REG_BASE + 0x724) #define AR9170_MAC_REG_CAM_DATA2 (AR9170_MAC_REG_BASE + 0x728) #define AR9170_MAC_REG_CAM_DATA3 (AR9170_MAC_REG_BASE + 0x72c) #define AR9170_MAC_REG_CAM_DBG0 (AR9170_MAC_REG_BASE + 0x730) #define AR9170_MAC_REG_CAM_DBG1 (AR9170_MAC_REG_BASE + 0x734) #define AR9170_MAC_REG_CAM_DBG2 (AR9170_MAC_REG_BASE + 0x738) #define AR9170_MAC_REG_CAM_STATE (AR9170_MAC_REG_BASE + 0x73c) #define AR9170_MAC_CAM_STATE_READ_PENDING 0x40000000 #define AR9170_MAC_CAM_STATE_WRITE_PENDING 0x80000000 #define AR9170_MAC_REG_CAM_TXKEY (AR9170_MAC_REG_BASE + 0x740) #define AR9170_MAC_REG_CAM_RXKEY (AR9170_MAC_REG_BASE + 0x750) #define AR9170_MAC_REG_CAM_TX_ENC_TYPE (AR9170_MAC_REG_BASE + 0x760) #define AR9170_MAC_REG_CAM_RX_ENC_TYPE (AR9170_MAC_REG_BASE + 0x770) #define AR9170_MAC_REG_CAM_TX_SERACH_HIT (AR9170_MAC_REG_BASE + 0x780) #define AR9170_MAC_REG_CAM_RX_SERACH_HIT (AR9170_MAC_REG_BASE + 0x790) #define AR9170_MAC_REG_AC0_CW (AR9170_MAC_REG_BASE + 0xb00) #define AR9170_MAC_REG_AC1_CW (AR9170_MAC_REG_BASE + 0xb04) #define AR9170_MAC_REG_AC2_CW (AR9170_MAC_REG_BASE + 0xb08) #define AR9170_MAC_REG_AC3_CW (AR9170_MAC_REG_BASE + 0xb0c) #define AR9170_MAC_REG_AC4_CW (AR9170_MAC_REG_BASE + 0xb10) #define AR9170_MAC_REG_AC2_AC1_AC0_AIFS (AR9170_MAC_REG_BASE + 0xb14) #define AR9170_MAC_REG_AC4_AC3_AC2_AIFS (AR9170_MAC_REG_BASE + 0xb18) #define AR9170_MAC_REG_TXOP_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0xb1c) #define AR9170_MAC_REG_TXOP_ACK_INTERVAL (AR9170_MAC_REG_BASE + 0xb20) #define AR9170_MAC_REG_CONTENTION_POINT (AR9170_MAC_REG_BASE + 0xb24) #define AR9170_MAC_REG_RETRY_MAX (AR9170_MAC_REG_BASE + 0xb28) #define AR9170_MAC_REG_TID_CFACK_CFEND_RATE (AR9170_MAC_REG_BASE + 0xb2c) #define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND (AR9170_MAC_REG_BASE + 0xb30) #define AR9170_MAC_REG_TKIP_TSC (AR9170_MAC_REG_BASE + 0xb34) #define AR9170_MAC_REG_TXOP_DURATION (AR9170_MAC_REG_BASE + 0xb38) #define AR9170_MAC_REG_TX_QOS_THRESHOLD (AR9170_MAC_REG_BASE + 0xb3c) #define AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA (AR9170_MAC_REG_BASE + 0xb40) #define AR9170_MAC_VIRTUAL_CCA_Q0 BIT(15) #define AR9170_MAC_VIRTUAL_CCA_Q1 BIT(16) #define AR9170_MAC_VIRTUAL_CCA_Q2 BIT(17) #define AR9170_MAC_VIRTUAL_CCA_Q3 BIT(18) #define AR9170_MAC_VIRTUAL_CCA_Q4 BIT(19) #define AR9170_MAC_VIRTUAL_CCA_ALL (0xf8000) #define AR9170_MAC_REG_AC1_AC0_TXOP (AR9170_MAC_REG_BASE + 0xb44) #define AR9170_MAC_REG_AC3_AC2_TXOP (AR9170_MAC_REG_BASE + 0xb48) #define AR9170_MAC_REG_AMPDU_COUNT (AR9170_MAC_REG_BASE + 0xb88) #define AR9170_MAC_REG_MPDU_COUNT (AR9170_MAC_REG_BASE + 0xb8c) #define AR9170_MAC_REG_AMPDU_FACTOR (AR9170_MAC_REG_BASE + 0xb9c) #define AR9170_MAC_AMPDU_FACTOR 0x7f0000 #define AR9170_MAC_AMPDU_FACTOR_S 16 #define AR9170_MAC_REG_AMPDU_DENSITY (AR9170_MAC_REG_BASE + 0xba0) #define AR9170_MAC_AMPDU_DENSITY 0x7 #define AR9170_MAC_AMPDU_DENSITY_S 0 #define AR9170_MAC_REG_FCS_SELECT (AR9170_MAC_REG_BASE + 0xbb0) #define AR9170_MAC_FCS_SWFCS 0x1 #define AR9170_MAC_FCS_FIFO_PROT 0x4 #define AR9170_MAC_REG_RTS_CTS_TPC (AR9170_MAC_REG_BASE + 0xbb4) #define AR9170_MAC_REG_CFEND_QOSNULL_TPC (AR9170_MAC_REG_BASE + 0xbb8) #define AR9170_MAC_REG_ACK_TABLE (AR9170_MAC_REG_BASE + 0xc00) #define AR9170_MAC_REG_RX_CONTROL (AR9170_MAC_REG_BASE + 0xc40) #define AR9170_MAC_RX_CTRL_DEAGG 0x1 #define AR9170_MAC_RX_CTRL_SHORT_FILTER 0x2 #define AR9170_MAC_RX_CTRL_SA_DA_SEARCH 0x20 #define AR9170_MAC_RX_CTRL_PASS_TO_HOST BIT(28) #define AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER BIT(30) #define AR9170_MAC_REG_RX_CONTROL_1 (AR9170_MAC_REG_BASE + 0xc44) #define AR9170_MAC_REG_AMPDU_RX_THRESH (AR9170_MAC_REG_BASE + 0xc50) #define AR9170_MAC_REG_RX_MPDU (AR9170_MAC_REG_BASE + 0xca0) #define AR9170_MAC_REG_RX_DROPPED_MPDU (AR9170_MAC_REG_BASE + 0xca4) #define AR9170_MAC_REG_RX_DEL_MPDU (AR9170_MAC_REG_BASE + 0xca8) #define AR9170_MAC_REG_RX_PHY_MISC_ERROR (AR9170_MAC_REG_BASE + 0xcac) #define AR9170_MAC_REG_RX_PHY_XR_ERROR (AR9170_MAC_REG_BASE + 0xcb0) #define AR9170_MAC_REG_RX_PHY_OFDM_ERROR (AR9170_MAC_REG_BASE + 0xcb4) #define AR9170_MAC_REG_RX_PHY_CCK_ERROR (AR9170_MAC_REG_BASE + 0xcb8) #define AR9170_MAC_REG_RX_PHY_HT_ERROR (AR9170_MAC_REG_BASE + 0xcbc) #define AR9170_MAC_REG_RX_PHY_TOTAL (AR9170_MAC_REG_BASE + 0xcc0) #define AR9170_MAC_REG_DMA_TXQ_ADDR (AR9170_MAC_REG_BASE + 0xd00) #define AR9170_MAC_REG_DMA_TXQ_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd04) #define AR9170_MAC_REG_DMA_TXQ0_ADDR (AR9170_MAC_REG_BASE + 0xd00) #define AR9170_MAC_REG_DMA_TXQ0_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd04) #define AR9170_MAC_REG_DMA_TXQ1_ADDR (AR9170_MAC_REG_BASE + 0xd08) #define AR9170_MAC_REG_DMA_TXQ1_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd0c) #define AR9170_MAC_REG_DMA_TXQ2_ADDR (AR9170_MAC_REG_BASE + 0xd10) #define AR9170_MAC_REG_DMA_TXQ2_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd14) #define AR9170_MAC_REG_DMA_TXQ3_ADDR (AR9170_MAC_REG_BASE + 0xd18) #define AR9170_MAC_REG_DMA_TXQ3_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd1c) #define AR9170_MAC_REG_DMA_TXQ4_ADDR (AR9170_MAC_REG_BASE + 0xd20) #define AR9170_MAC_REG_DMA_TXQ4_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd24) #define AR9170_MAC_REG_DMA_RXQ_ADDR (AR9170_MAC_REG_BASE + 0xd28) #define AR9170_MAC_REG_DMA_RXQ_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd2c) #define AR9170_MAC_REG_DMA_TRIGGER (AR9170_MAC_REG_BASE + 0xd30) #define AR9170_DMA_TRIGGER_TXQ0 BIT(0) #define AR9170_DMA_TRIGGER_TXQ1 BIT(1) #define AR9170_DMA_TRIGGER_TXQ2 BIT(2) #define AR9170_DMA_TRIGGER_TXQ3 BIT(3) #define AR9170_DMA_TRIGGER_TXQ4 BIT(4) #define AR9170_DMA_TRIGGER_RXQ BIT(8) #define AR9170_MAC_REG_DMA_WLAN_STATUS (AR9170_MAC_REG_BASE + 0xd38) #define AR9170_MAC_REG_DMA_STATUS (AR9170_MAC_REG_BASE + 0xd3c) #define AR9170_MAC_REG_DMA_TXQ_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd40) #define AR9170_MAC_REG_DMA_TXQ0_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd40) #define AR9170_MAC_REG_DMA_TXQ1_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd44) #define AR9170_MAC_REG_DMA_TXQ2_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd48) #define AR9170_MAC_REG_DMA_TXQ3_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd4c) #define AR9170_MAC_REG_DMA_TXQ4_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd50) #define AR9170_MAC_REG_DMA_TXQ0Q1_LEN (AR9170_MAC_REG_BASE + 0xd54) #define AR9170_MAC_REG_DMA_TXQ2Q3_LEN (AR9170_MAC_REG_BASE + 0xd58) #define AR9170_MAC_REG_DMA_TXQ4_LEN (AR9170_MAC_REG_BASE + 0xd5c) #define AR9170_MAC_REG_DMA_TXQX_LAST_ADDR (AR9170_MAC_REG_BASE + 0xd74) #define AR9170_MAC_REG_DMA_TXQX_FAIL_ADDR (AR9170_MAC_REG_BASE + 0xd78) #define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xd7c) #define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f #define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0 #define AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000 #define AR9170_MAC_TXRX_MPI_RX_TO_MASK 0xfff00000 #define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xd84) #define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xd88) #define AR9170_MAC_BCN_LENGTH_MAX 256 #define AR9170_MAC_REG_BCN_STATUS (AR9170_MAC_REG_BASE + 0xd8c) #define AR9170_MAC_REG_BCN_PLCP (AR9170_MAC_REG_BASE + 0xd90) #define AR9170_MAC_REG_BCN_CTRL (AR9170_MAC_REG_BASE + 0xd94) #define AR9170_BCN_CTRL_READY 0x01 #define AR9170_BCN_CTRL_LOCK 0x02 #define AR9170_MAC_REG_BCN_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd98) #define AR9170_MAC_REG_BCN_COUNT (AR9170_MAC_REG_BASE + 0xd9c) #define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xda0) #define AR9170_MAC_BCN_HT1_HT_EN BIT(0) #define AR9170_MAC_BCN_HT1_GF_PMB BIT(1) #define AR9170_MAC_BCN_HT1_SP_EXP BIT(2) #define AR9170_MAC_BCN_HT1_TX_BF BIT(3) #define AR9170_MAC_BCN_HT1_PWR_CTRL_S 4 #define AR9170_MAC_BCN_HT1_PWR_CTRL 0x70 #define AR9170_MAC_BCN_HT1_TX_ANT1 BIT(7) #define AR9170_MAC_BCN_HT1_TX_ANT0 BIT(8) #define AR9170_MAC_BCN_HT1_NUM_LFT_S 9 #define AR9170_MAC_BCN_HT1_NUM_LFT 0x600 #define AR9170_MAC_BCN_HT1_BWC_20M_EXT BIT(16) #define AR9170_MAC_BCN_HT1_BWC_40M_SHARED BIT(17) #define AR9170_MAC_BCN_HT1_BWC_40M_DUP (BIT(16) | BIT(17)) #define AR9170_MAC_BCN_HT1_BF_MCS_S 18 #define AR9170_MAC_BCN_HT1_BF_MCS 0x1c0000 #define AR9170_MAC_BCN_HT1_TPC_S 21 #define AR9170_MAC_BCN_HT1_TPC 0x7e00000 #define AR9170_MAC_BCN_HT1_CHAIN_MASK_S 27 #define AR9170_MAC_BCN_HT1_CHAIN_MASK 0x38000000 #define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xda4) #define AR9170_MAC_BCN_HT2_MCS_S 0 #define AR9170_MAC_BCN_HT2_MCS 0x7f #define AR9170_MAC_BCN_HT2_BW40 BIT(8) #define AR9170_MAC_BCN_HT2_SMOOTHING BIT(9) #define AR9170_MAC_BCN_HT2_SS BIT(10) #define AR9170_MAC_BCN_HT2_NSS BIT(11) #define AR9170_MAC_BCN_HT2_STBC_S 12 #define AR9170_MAC_BCN_HT2_STBC 0x3000 #define AR9170_MAC_BCN_HT2_ADV_COD BIT(14) #define AR9170_MAC_BCN_HT2_SGI BIT(15) #define AR9170_MAC_BCN_HT2_LEN_S 16 #define AR9170_MAC_BCN_HT2_LEN 0xffff0000 #define AR9170_MAC_REG_DMA_TXQX_ADDR_CURR (AR9170_MAC_REG_BASE + 0xdc0) /* Random number generator */ #define AR9170_RAND_REG_BASE 0x1d0000 #define AR9170_RAND_REG_NUM (AR9170_RAND_REG_BASE + 0x000) #define AR9170_RAND_REG_MODE (AR9170_RAND_REG_BASE + 0x004) #define AR9170_RAND_MODE_MANUAL 0x000 #define AR9170_RAND_MODE_FREE 0x001 /* GPIO */ #define AR9170_GPIO_REG_BASE 0x1d0100 #define AR9170_GPIO_REG_PORT_TYPE (AR9170_GPIO_REG_BASE + 0x000) #define AR9170_GPIO_REG_PORT_DATA (AR9170_GPIO_REG_BASE + 0x004) #define AR9170_GPIO_PORT_LED_0 1 #define AR9170_GPIO_PORT_LED_1 2 /* WPS Button GPIO for TP-Link TL-WN821N */ #define AR9170_GPIO_PORT_WPS_BUTTON_PRESSED 4 /* Memory Controller */ #define AR9170_MC_REG_BASE 0x1d1000 #define AR9170_MC_REG_FLASH_WAIT_STATE (AR9170_MC_REG_BASE + 0x000) #define AR9170_MC_REG_SEEPROM_WP0 (AR9170_MC_REG_BASE + 0x400) #define AR9170_MC_REG_SEEPROM_WP1 (AR9170_MC_REG_BASE + 0x404) #define AR9170_MC_REG_SEEPROM_WP2 (AR9170_MC_REG_BASE + 0x408) /* Interrupt Controller */ #define AR9170_MAX_INT_SRC 9 #define AR9170_INT_REG_BASE 0x1d2000 #define AR9170_INT_REG_FLAG (AR9170_INT_REG_BASE + 0x000) #define AR9170_INT_REG_FIQ_MASK (AR9170_INT_REG_BASE + 0x004) #define AR9170_INT_REG_IRQ_MASK (AR9170_INT_REG_BASE + 0x008) /* INT_REG_FLAG, INT_REG_FIQ_MASK and INT_REG_IRQ_MASK */ #define AR9170_INT_FLAG_WLAN 0x001 #define AR9170_INT_FLAG_PTAB_BIT 0x002 #define AR9170_INT_FLAG_SE_BIT 0x004 #define AR9170_INT_FLAG_UART_BIT 0x008 #define AR9170_INT_FLAG_TIMER_BIT 0x010 #define AR9170_INT_FLAG_EXT_BIT 0x020 #define AR9170_INT_FLAG_SW_BIT 0x040 #define AR9170_INT_FLAG_USB_BIT 0x080 #define AR9170_INT_FLAG_ETHERNET_BIT 0x100 #define AR9170_INT_REG_PRIORITY1 (AR9170_INT_REG_BASE + 0x00c) #define AR9170_INT_REG_PRIORITY2 (AR9170_INT_REG_BASE + 0x010) #define AR9170_INT_REG_PRIORITY3 (AR9170_INT_REG_BASE + 0x014) #define AR9170_INT_REG_EXT_INT_CONTROL (AR9170_INT_REG_BASE + 0x018) #define AR9170_INT_REG_SW_INT_CONTROL (AR9170_INT_REG_BASE + 0x01c) #define AR9170_INT_SW_INT_ENABLE 0x1 #define AR9170_INT_REG_FIQ_ENCODE (AR9170_INT_REG_BASE + 0x020) #define AR9170_INT_INT_IRQ_ENCODE (AR9170_INT_REG_BASE + 0x024) /* Power Management */ #define AR9170_PWR_REG_BASE 0x1d4000 #define AR9170_PWR_REG_POWER_STATE (AR9170_PWR_REG_BASE + 0x000) #define AR9170_PWR_REG_RESET (AR9170_PWR_REG_BASE + 0x004) #define AR9170_PWR_RESET_COMMIT_RESET_MASK BIT(0) #define AR9170_PWR_RESET_WLAN_MASK BIT(1) #define AR9170_PWR_RESET_DMA_MASK BIT(2) #define AR9170_PWR_RESET_BRIDGE_MASK BIT(3) #define AR9170_PWR_RESET_AHB_MASK BIT(9) #define AR9170_PWR_RESET_BB_WARM_RESET BIT(10) #define AR9170_PWR_RESET_BB_COLD_RESET BIT(11) #define AR9170_PWR_RESET_ADDA_CLK_COLD_RESET BIT(12) #define AR9170_PWR_RESET_PLL BIT(13) #define AR9170_PWR_RESET_USB_PLL BIT(14) #define AR9170_PWR_REG_CLOCK_SEL (AR9170_PWR_REG_BASE + 0x008) #define AR9170_PWR_CLK_AHB_40MHZ 0 #define AR9170_PWR_CLK_AHB_20_22MHZ 1 #define AR9170_PWR_CLK_AHB_40_44MHZ 2 #define AR9170_PWR_CLK_AHB_80_88MHZ 3 #define AR9170_PWR_CLK_DAC_160_INV_DLY 0x70 #define AR9170_PWR_REG_CHIP_REVISION (AR9170_PWR_REG_BASE + 0x010) #define AR9170_PWR_REG_PLL_ADDAC (AR9170_PWR_REG_BASE + 0x014) #define AR9170_PWR_PLL_ADDAC_DIV_S 2 #define AR9170_PWR_PLL_ADDAC_DIV 0xffc #define AR9170_PWR_REG_WATCH_DOG_MAGIC (AR9170_PWR_REG_BASE + 0x020) /* Faraday USB Controller */ #define AR9170_USB_REG_BASE 0x1e1000 #define AR9170_USB_REG_MAIN_CTRL (AR9170_USB_REG_BASE + 0x000) #define AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP BIT(0) #define AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT BIT(2) #define AR9170_USB_MAIN_CTRL_GO_TO_SUSPEND BIT(3) #define AR9170_USB_MAIN_CTRL_RESET BIT(4) #define AR9170_USB_MAIN_CTRL_CHIP_ENABLE BIT(5) #define AR9170_USB_MAIN_CTRL_HIGHSPEED BIT(6) #define AR9170_USB_REG_DEVICE_ADDRESS (AR9170_USB_REG_BASE + 0x001) #define AR9170_USB_DEVICE_ADDRESS_CONFIGURE BIT(7) #define AR9170_USB_REG_TEST (AR9170_USB_REG_BASE + 0x002) #define AR9170_USB_REG_PHY_TEST_SELECT (AR9170_USB_REG_BASE + 0x008) #define AR9170_USB_REG_CX_CONFIG_STATUS (AR9170_USB_REG_BASE + 0x00b) #define AR9170_USB_REG_EP0_DATA (AR9170_USB_REG_BASE + 0x00c) #define AR9170_USB_REG_EP0_DATA1 (AR9170_USB_REG_BASE + 0x00c) #define AR9170_USB_REG_EP0_DATA2 (AR9170_USB_REG_BASE + 0x00d) #define AR9170_USB_REG_INTR_MASK_BYTE_0 (AR9170_USB_REG_BASE + 0x011) #define AR9170_USB_REG_INTR_MASK_BYTE_1 (AR9170_USB_REG_BASE + 0x012) #define AR9170_USB_REG_INTR_MASK_BYTE_2 (AR9170_USB_REG_BASE + 0x013) #define AR9170_USB_REG_INTR_MASK_BYTE_3 (AR9170_USB_REG_BASE + 0x014) #define AR9170_USB_REG_INTR_MASK_BYTE_4 (AR9170_USB_REG_BASE + 0x015) #define AR9170_USB_INTR_DISABLE_OUT_INT (BIT(7) | BIT(6)) #define AR9170_USB_REG_INTR_MASK_BYTE_5 (AR9170_USB_REG_BASE + 0x016) #define AR9170_USB_REG_INTR_MASK_BYTE_6 (AR9170_USB_REG_BASE + 0x017) #define AR9170_USB_INTR_DISABLE_IN_INT BIT(6) #define AR9170_USB_REG_INTR_MASK_BYTE_7 (AR9170_USB_REG_BASE + 0x018) #define AR9170_USB_REG_INTR_GROUP (AR9170_USB_REG_BASE + 0x020) #define AR9170_USB_REG_INTR_SOURCE_0 (AR9170_USB_REG_BASE + 0x021) #define AR9170_USB_INTR_SRC0_SETUP BIT(0) #define AR9170_USB_INTR_SRC0_IN BIT(1) #define AR9170_USB_INTR_SRC0_OUT BIT(2) #define AR9170_USB_INTR_SRC0_FAIL BIT(3) /* ??? */ #define AR9170_USB_INTR_SRC0_END BIT(4) /* ??? */ #define AR9170_USB_INTR_SRC0_ABORT BIT(7) #define AR9170_USB_REG_INTR_SOURCE_1 (AR9170_USB_REG_BASE + 0x022) #define AR9170_USB_REG_INTR_SOURCE_2 (AR9170_USB_REG_BASE + 0x023) #define AR9170_USB_REG_INTR_SOURCE_3 (AR9170_USB_REG_BASE + 0x024) #define AR9170_USB_REG_INTR_SOURCE_4 (AR9170_USB_REG_BASE + 0x025) #define AR9170_USB_REG_INTR_SOURCE_5 (AR9170_USB_REG_BASE + 0x026) #define AR9170_USB_REG_INTR_SOURCE_6 (AR9170_USB_REG_BASE + 0x027) #define AR9170_USB_REG_INTR_SOURCE_7 (AR9170_USB_REG_BASE + 0x028) #define AR9170_USB_INTR_SRC7_USB_RESET BIT(1) #define AR9170_USB_INTR_SRC7_USB_SUSPEND BIT(2) #define AR9170_USB_INTR_SRC7_USB_RESUME BIT(3) #define AR9170_USB_INTR_SRC7_ISO_SEQ_ERR BIT(4) #define AR9170_USB_INTR_SRC7_ISO_SEQ_ABORT BIT(5) #define AR9170_USB_INTR_SRC7_TX0BYTE BIT(6) #define AR9170_USB_INTR_SRC7_RX0BYTE BIT(7) #define AR9170_USB_REG_IDLE_COUNT (AR9170_USB_REG_BASE + 0x02f) #define AR9170_USB_REG_EP_MAP (AR9170_USB_REG_BASE + 0x030) #define AR9170_USB_REG_EP1_MAP (AR9170_USB_REG_BASE + 0x030) #define AR9170_USB_REG_EP2_MAP (AR9170_USB_REG_BASE + 0x031) #define AR9170_USB_REG_EP3_MAP (AR9170_USB_REG_BASE + 0x032) #define AR9170_USB_REG_EP4_MAP (AR9170_USB_REG_BASE + 0x033) #define AR9170_USB_REG_EP5_MAP (AR9170_USB_REG_BASE + 0x034) #define AR9170_USB_REG_EP6_MAP (AR9170_USB_REG_BASE + 0x035) #define AR9170_USB_REG_EP7_MAP (AR9170_USB_REG_BASE + 0x036) #define AR9170_USB_REG_EP8_MAP (AR9170_USB_REG_BASE + 0x037) #define AR9170_USB_REG_EP9_MAP (AR9170_USB_REG_BASE + 0x038) #define AR9170_USB_REG_EP10_MAP (AR9170_USB_REG_BASE + 0x039) #define AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x03f) #define AR9170_USB_EP_IN_TOGGLE 0x10 #define AR9170_USB_REG_EP_IN_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x03e) #define AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x05f) #define AR9170_USB_EP_OUT_TOGGLE 0x10 #define AR9170_USB_REG_EP_OUT_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x05e) #define AR9170_USB_REG_EP3_BYTE_COUNT_HIGH (AR9170_USB_REG_BASE + 0x0ae) #define AR9170_USB_REG_EP3_BYTE_COUNT_LOW (AR9170_USB_REG_BASE + 0x0be) #define AR9170_USB_REG_EP4_BYTE_COUNT_HIGH (AR9170_USB_REG_BASE + 0x0af) #define AR9170_USB_REG_EP4_BYTE_COUNT_LOW (AR9170_USB_REG_BASE + 0x0bf) #define AR9170_USB_REG_FIFO_MAP (AR9170_USB_REG_BASE + 0x080) #define AR9170_USB_REG_FIFO0_MAP (AR9170_USB_REG_BASE + 0x080) #define AR9170_USB_REG_FIFO1_MAP (AR9170_USB_REG_BASE + 0x081) #define AR9170_USB_REG_FIFO2_MAP (AR9170_USB_REG_BASE + 0x082) #define AR9170_USB_REG_FIFO3_MAP (AR9170_USB_REG_BASE + 0x083) #define AR9170_USB_REG_FIFO4_MAP (AR9170_USB_REG_BASE + 0x084) #define AR9170_USB_REG_FIFO5_MAP (AR9170_USB_REG_BASE + 0x085) #define AR9170_USB_REG_FIFO6_MAP (AR9170_USB_REG_BASE + 0x086) #define AR9170_USB_REG_FIFO7_MAP (AR9170_USB_REG_BASE + 0x087) #define AR9170_USB_REG_FIFO8_MAP (AR9170_USB_REG_BASE + 0x088) #define AR9170_USB_REG_FIFO9_MAP (AR9170_USB_REG_BASE + 0x089) #define AR9170_USB_REG_FIFO_CONFIG (AR9170_USB_REG_BASE + 0x090) #define AR9170_USB_REG_FIFO0_CONFIG (AR9170_USB_REG_BASE + 0x090) #define AR9170_USB_REG_FIFO1_CONFIG (AR9170_USB_REG_BASE + 0x091) #define AR9170_USB_REG_FIFO2_CONFIG (AR9170_USB_REG_BASE + 0x092) #define AR9170_USB_REG_FIFO3_CONFIG (AR9170_USB_REG_BASE + 0x093) #define AR9170_USB_REG_FIFO4_CONFIG (AR9170_USB_REG_BASE + 0x094) #define AR9170_USB_REG_FIFO5_CONFIG (AR9170_USB_REG_BASE + 0x095) #define AR9170_USB_REG_FIFO6_CONFIG (AR9170_USB_REG_BASE + 0x096) #define AR9170_USB_REG_FIFO7_CONFIG (AR9170_USB_REG_BASE + 0x097) #define AR9170_USB_REG_FIFO8_CONFIG (AR9170_USB_REG_BASE + 0x098) #define AR9170_USB_REG_FIFO9_CONFIG (AR9170_USB_REG_BASE + 0x099) #define AR9170_USB_REG_EP3_DATA (AR9170_USB_REG_BASE + 0x0f8) #define AR9170_USB_REG_EP4_DATA (AR9170_USB_REG_BASE + 0x0fc) #define AR9170_USB_REG_FIFO_SIZE (AR9170_USB_REG_BASE + 0x100) #define AR9170_USB_REG_DMA_CTL (AR9170_USB_REG_BASE + 0x108) #define AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE BIT(0) #define AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE BIT(1) #define AR9170_USB_DMA_CTL_HIGH_SPEED BIT(2) #define AR9170_USB_DMA_CTL_UP_PACKET_MODE BIT(3) #define AR9170_USB_DMA_CTL_UP_STREAM_S 4 #define AR9170_USB_DMA_CTL_UP_STREAM (BIT(4) | BIT(5)) #define AR9170_USB_DMA_CTL_UP_STREAM_4K (0) #define AR9170_USB_DMA_CTL_UP_STREAM_8K BIT(4) #define AR9170_USB_DMA_CTL_UP_STREAM_16K BIT(5) #define AR9170_USB_DMA_CTL_UP_STREAM_32K (BIT(4) | BIT(5)) #define AR9170_USB_DMA_CTL_DOWN_STREAM BIT(6) #define AR9170_USB_REG_DMA_STATUS (AR9170_USB_REG_BASE + 0x10c) #define AR9170_USB_DMA_STATUS_UP_IDLE BIT(8) #define AR9170_USB_DMA_STATUS_DN_IDLE BIT(16) #define AR9170_USB_REG_MAX_AGG_UPLOAD (AR9170_USB_REG_BASE + 0x110) #define AR9170_USB_REG_UPLOAD_TIME_CTL (AR9170_USB_REG_BASE + 0x114) #define AR9170_USB_REG_WAKE_UP (AR9170_USB_REG_BASE + 0x120) #define AR9170_USB_WAKE_UP_WAKE BIT(0) #define AR9170_USB_REG_CBUS_CTRL (AR9170_USB_REG_BASE + 0x1f0) #define AR9170_USB_CBUS_CTRL_BUFFER_END (BIT(1)) /* PCI/USB to AHB Bridge */ #define AR9170_PTA_REG_BASE 0x1e2000 #define AR9170_PTA_REG_CMD (AR9170_PTA_REG_BASE + 0x000) #define AR9170_PTA_REG_PARAM1 (AR9170_PTA_REG_BASE + 0x004) #define AR9170_PTA_REG_PARAM2 (AR9170_PTA_REG_BASE + 0x008) #define AR9170_PTA_REG_PARAM3 (AR9170_PTA_REG_BASE + 0x00c) #define AR9170_PTA_REG_RSP (AR9170_PTA_REG_BASE + 0x010) #define AR9170_PTA_REG_STATUS1 (AR9170_PTA_REG_BASE + 0x014) #define AR9170_PTA_REG_STATUS2 (AR9170_PTA_REG_BASE + 0x018) #define AR9170_PTA_REG_STATUS3 (AR9170_PTA_REG_BASE + 0x01c) #define AR9170_PTA_REG_AHB_INT_FLAG (AR9170_PTA_REG_BASE + 0x020) #define AR9170_PTA_REG_AHB_INT_MASK (AR9170_PTA_REG_BASE + 0x024) #define AR9170_PTA_REG_AHB_INT_ACK (AR9170_PTA_REG_BASE + 0x028) #define AR9170_PTA_REG_AHB_SCRATCH1 (AR9170_PTA_REG_BASE + 0x030) #define AR9170_PTA_REG_AHB_SCRATCH2 (AR9170_PTA_REG_BASE + 0x034) #define AR9170_PTA_REG_AHB_SCRATCH3 (AR9170_PTA_REG_BASE + 0x038) #define AR9170_PTA_REG_AHB_SCRATCH4 (AR9170_PTA_REG_BASE + 0x03c) #define AR9170_PTA_REG_SHARE_MEM_CTRL (AR9170_PTA_REG_BASE + 0x124) /* * PCI to AHB Bridge */ #define AR9170_PTA_REG_INT_FLAG (AR9170_PTA_REG_BASE + 0x100) #define AR9170_PTA_INT_FLAG_DN 0x01 #define AR9170_PTA_INT_FLAG_UP 0x02 #define AR9170_PTA_INT_FLAG_CMD 0x04 #define AR9170_PTA_REG_INT_MASK (AR9170_PTA_REG_BASE + 0x104) #define AR9170_PTA_REG_DN_DMA_ADDRL (AR9170_PTA_REG_BASE + 0x108) #define AR9170_PTA_REG_DN_DMA_ADDRH (AR9170_PTA_REG_BASE + 0x10c) #define AR9170_PTA_REG_UP_DMA_ADDRL (AR9170_PTA_REG_BASE + 0x110) #define AR9170_PTA_REG_UP_DMA_ADDRH (AR9170_PTA_REG_BASE + 0x114) #define AR9170_PTA_REG_DN_PEND_TIME (AR9170_PTA_REG_BASE + 0x118) #define AR9170_PTA_REG_UP_PEND_TIME (AR9170_PTA_REG_BASE + 0x11c) #define AR9170_PTA_REG_CONTROL (AR9170_PTA_REG_BASE + 0x120) #define AR9170_PTA_CTRL_4_BEAT_BURST 0x00 #define AR9170_PTA_CTRL_8_BEAT_BURST 0x01 #define AR9170_PTA_CTRL_16_BEAT_BURST 0x02 #define AR9170_PTA_CTRL_LOOPBACK_MODE 0x10 #define AR9170_PTA_REG_MEM_CTRL (AR9170_PTA_REG_BASE + 0x124) #define AR9170_PTA_REG_MEM_ADDR (AR9170_PTA_REG_BASE + 0x128) #define AR9170_PTA_REG_DN_DMA_TRIGGER (AR9170_PTA_REG_BASE + 0x12c) #define AR9170_PTA_REG_UP_DMA_TRIGGER (AR9170_PTA_REG_BASE + 0x130) #define AR9170_PTA_REG_DMA_STATUS (AR9170_PTA_REG_BASE + 0x134) #define AR9170_PTA_REG_DN_CURR_ADDRL (AR9170_PTA_REG_BASE + 0x138) #define AR9170_PTA_REG_DN_CURR_ADDRH (AR9170_PTA_REG_BASE + 0x13c) #define AR9170_PTA_REG_UP_CURR_ADDRL (AR9170_PTA_REG_BASE + 0x140) #define AR9170_PTA_REG_UP_CURR_ADDRH (AR9170_PTA_REG_BASE + 0x144) #define AR9170_PTA_REG_DMA_MODE_CTRL (AR9170_PTA_REG_BASE + 0x148) #define AR9170_PTA_DMA_MODE_CTRL_RESET BIT(0) #define AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB BIT(1) /* Protocol Controller Module */ #define AR9170_MAC_REG_PC_REG_BASE (AR9170_MAC_REG_BASE + 0xe00) #define AR9170_NUM_LEDS 2 /* CAM */ #define AR9170_CAM_MAX_USER 64 #define AR9170_CAM_MAX_KEY_LENGTH 16 #define AR9170_SRAM_OFFSET 0x100000 #define AR9170_SRAM_SIZE 0x18000 #define AR9170_PRAM_OFFSET 0x200000 #define AR9170_PRAM_SIZE 0x8000 enum cpu_clock { AHB_STATIC_40MHZ = 0, AHB_GMODE_22MHZ = 1, AHB_AMODE_20MHZ = 1, AHB_GMODE_44MHZ = 2, AHB_AMODE_40MHZ = 2, AHB_GMODE_88MHZ = 3, AHB_AMODE_80MHZ = 3 }; /* USB endpoints */ enum ar9170_usb_ep { /* * Control EP is always EP 0 (USB SPEC) * * The weird thing is: the original firmware has a few * comments that suggest that the actual EP numbers * are in the 1 to 10 range?! */ AR9170_USB_EP_CTRL = 0, AR9170_USB_EP_TX, AR9170_USB_EP_RX, AR9170_USB_EP_IRQ, AR9170_USB_EP_CMD, AR9170_USB_NUM_EXTRA_EP = 4, __AR9170_USB_NUM_EP, __AR9170_USB_NUM_MAX_EP = 10 }; enum ar9170_usb_fifo { __AR9170_USB_NUM_MAX_FIFO = 10 }; enum ar9170_tx_queues { AR9170_TXQ0 = 0, AR9170_TXQ1, AR9170_TXQ2, AR9170_TXQ3, AR9170_TXQ_SPECIAL, /* keep last */ __AR9170_NUM_TX_QUEUES = 5 }; #define AR9170_TX_STREAM_TAG 0x697e #define AR9170_RX_STREAM_TAG 0x4e00 #define AR9170_RX_STREAM_MAX_SIZE 0xffff struct ar9170_stream { __le16 length; __le16 tag; u8 payload[0]; } __packed __aligned(4); #define AR9170_STREAM_LEN 4 #define AR9170_MAX_ACKTABLE_ENTRIES 8 #define AR9170_MAX_VIRTUAL_MAC 7 #define AR9170_USB_EP_CTRL_MAX 64 #define AR9170_USB_EP_TX_MAX 512 #define AR9170_USB_EP_RX_MAX 512 #define AR9170_USB_EP_IRQ_MAX 64 #define AR9170_USB_EP_CMD_MAX 64 /* Trigger PRETBTT interrupt 6 Kus earlier */ #define CARL9170_PRETBTT_KUS 6 #define AR5416_MAX_RATE_POWER 63 #define SET_VAL(reg, value, newvalue) \ (value = ((value) & ~reg) | (((newvalue) << reg##_S) & reg)) #define SET_CONSTVAL(reg, newvalue) \ (((newvalue) << reg##_S) & reg) #define MOD_VAL(reg, value, newvalue) \ (((value) & ~reg) | (((newvalue) << reg##_S) & reg)) #define GET_VAL(reg, value) \ (((value) & reg) >> reg##_S) #endif /* __CARL9170_SHARED_HW_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/fwdesc.h0000644000175000017500000001675012026211315024134 0ustar mcgrofmcgrof/* * Shared CARL9170 Header * * Firmware descriptor format * * Copyright 2009-2011 Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. */ #ifndef __CARL9170_SHARED_FWDESC_H #define __CARL9170_SHARED_FWDESC_H /* NOTE: Don't mess with the order of the flags! */ enum carl9170fw_feature_list { /* Always set */ CARL9170FW_DUMMY_FEATURE, /* * Indicates that this image has special boot block which prevents * legacy drivers to drive the firmware. */ CARL9170FW_MINIBOOT, /* usb registers are initialized by the firmware */ CARL9170FW_USB_INIT_FIRMWARE, /* command traps & notifications are send through EP2 */ CARL9170FW_USB_RESP_EP2, /* usb download (app -> fw) stream */ CARL9170FW_USB_DOWN_STREAM, /* usb upload (fw -> app) stream */ CARL9170FW_USB_UP_STREAM, /* unusable - reserved to flag non-functional debug firmwares */ CARL9170FW_UNUSABLE, /* AR9170_CMD_RF_INIT, AR9170_CMD_FREQ_START, AR9170_CMD_FREQUENCY */ CARL9170FW_COMMAND_PHY, /* AR9170_CMD_EKEY, AR9170_CMD_DKEY */ CARL9170FW_COMMAND_CAM, /* Firmware has a software Content After Beacon Queueing mechanism */ CARL9170FW_WLANTX_CAB, /* The firmware is capable of responding to incoming BAR frames */ CARL9170FW_HANDLE_BACK_REQ, /* GPIO Interrupt | CARL9170_RSP_GPIO */ CARL9170FW_GPIO_INTERRUPT, /* Firmware PSM support | CARL9170_CMD_PSM */ CARL9170FW_PSM, /* Firmware RX filter | CARL9170_CMD_RX_FILTER */ CARL9170FW_RX_FILTER, /* Wake up on WLAN */ CARL9170FW_WOL, /* Firmware supports PSM in the 5GHZ Band */ CARL9170FW_FIXED_5GHZ_PSM, /* HW (ANI, CCA, MIB) tally counters */ CARL9170FW_HW_COUNTERS, /* Firmware will pass BA when BARs are queued */ CARL9170FW_RX_BA_FILTER, /* KEEP LAST */ __CARL9170FW_FEATURE_NUM }; #define OTUS_MAGIC "OTAR" #define MOTD_MAGIC "MOTD" #define FIX_MAGIC "FIX\0" #define DBG_MAGIC "DBG\0" #define CHK_MAGIC "CHK\0" #define TXSQ_MAGIC "TXSQ" #define WOL_MAGIC "WOL\0" #define LAST_MAGIC "LAST" #define CARL9170FW_SET_DAY(d) (((d) - 1) % 31) #define CARL9170FW_SET_MONTH(m) ((((m) - 1) % 12) * 31) #define CARL9170FW_SET_YEAR(y) (((y) - 10) * 372) #define CARL9170FW_GET_DAY(d) (((d) % 31) + 1) #define CARL9170FW_GET_MONTH(m) ((((m) / 31) % 12) + 1) #define CARL9170FW_GET_YEAR(y) ((y) / 372 + 10) #define CARL9170FW_MAGIC_SIZE 4 struct carl9170fw_desc_head { u8 magic[CARL9170FW_MAGIC_SIZE]; __le16 length; u8 min_ver; u8 cur_ver; } __packed; #define CARL9170FW_DESC_HEAD_SIZE \ (sizeof(struct carl9170fw_desc_head)) #define CARL9170FW_OTUS_DESC_MIN_VER 6 #define CARL9170FW_OTUS_DESC_CUR_VER 7 struct carl9170fw_otus_desc { struct carl9170fw_desc_head head; __le32 feature_set; __le32 fw_address; __le32 bcn_addr; __le16 bcn_len; __le16 miniboot_size; __le16 tx_frag_len; __le16 rx_max_frame_len; u8 tx_descs; u8 cmd_bufs; u8 api_ver; u8 vif_num; } __packed; #define CARL9170FW_OTUS_DESC_SIZE \ (sizeof(struct carl9170fw_otus_desc)) #define CARL9170FW_MOTD_STRING_LEN 24 #define CARL9170FW_MOTD_RELEASE_LEN 20 #define CARL9170FW_MOTD_DESC_MIN_VER 1 #define CARL9170FW_MOTD_DESC_CUR_VER 2 struct carl9170fw_motd_desc { struct carl9170fw_desc_head head; __le32 fw_year_month_day; char desc[CARL9170FW_MOTD_STRING_LEN]; char release[CARL9170FW_MOTD_RELEASE_LEN]; } __packed; #define CARL9170FW_MOTD_DESC_SIZE \ (sizeof(struct carl9170fw_motd_desc)) #define CARL9170FW_FIX_DESC_MIN_VER 1 #define CARL9170FW_FIX_DESC_CUR_VER 2 struct carl9170fw_fix_entry { __le32 address; __le32 mask; __le32 value; } __packed; struct carl9170fw_fix_desc { struct carl9170fw_desc_head head; struct carl9170fw_fix_entry data[0]; } __packed; #define CARL9170FW_FIX_DESC_SIZE \ (sizeof(struct carl9170fw_fix_desc)) #define CARL9170FW_DBG_DESC_MIN_VER 1 #define CARL9170FW_DBG_DESC_CUR_VER 3 struct carl9170fw_dbg_desc { struct carl9170fw_desc_head head; __le32 bogoclock_addr; __le32 counter_addr; __le32 rx_total_addr; __le32 rx_overrun_addr; __le32 rx_filter; /* Put your debugging definitions here */ } __packed; #define CARL9170FW_DBG_DESC_SIZE \ (sizeof(struct carl9170fw_dbg_desc)) #define CARL9170FW_CHK_DESC_MIN_VER 1 #define CARL9170FW_CHK_DESC_CUR_VER 2 struct carl9170fw_chk_desc { struct carl9170fw_desc_head head; __le32 fw_crc32; __le32 hdr_crc32; } __packed; #define CARL9170FW_CHK_DESC_SIZE \ (sizeof(struct carl9170fw_chk_desc)) #define CARL9170FW_TXSQ_DESC_MIN_VER 1 #define CARL9170FW_TXSQ_DESC_CUR_VER 1 struct carl9170fw_txsq_desc { struct carl9170fw_desc_head head; __le32 seq_table_addr; } __packed; #define CARL9170FW_TXSQ_DESC_SIZE \ (sizeof(struct carl9170fw_txsq_desc)) #define CARL9170FW_WOL_DESC_MIN_VER 1 #define CARL9170FW_WOL_DESC_CUR_VER 1 struct carl9170fw_wol_desc { struct carl9170fw_desc_head head; __le32 supported_triggers; /* CARL9170_WOL_ */ } __packed; #define CARL9170FW_WOL_DESC_SIZE \ (sizeof(struct carl9170fw_wol_desc)) #define CARL9170FW_LAST_DESC_MIN_VER 1 #define CARL9170FW_LAST_DESC_CUR_VER 2 struct carl9170fw_last_desc { struct carl9170fw_desc_head head; } __packed; #define CARL9170FW_LAST_DESC_SIZE \ (sizeof(struct carl9170fw_fix_desc)) #define CARL9170FW_DESC_MAX_LENGTH 8192 #define CARL9170FW_FILL_DESC(_magic, _length, _min_ver, _cur_ver) \ .head = { \ .magic = _magic, \ .length = cpu_to_le16(_length), \ .min_ver = _min_ver, \ .cur_ver = _cur_ver, \ } static inline void carl9170fw_fill_desc(struct carl9170fw_desc_head *head, u8 magic[CARL9170FW_MAGIC_SIZE], __le16 length, u8 min_ver, u8 cur_ver) { head->magic[0] = magic[0]; head->magic[1] = magic[1]; head->magic[2] = magic[2]; head->magic[3] = magic[3]; head->length = length; head->min_ver = min_ver; head->cur_ver = cur_ver; } #define carl9170fw_for_each_hdr(desc, fw_desc) \ for (desc = fw_desc; \ memcmp(desc->magic, LAST_MAGIC, CARL9170FW_MAGIC_SIZE) && \ le16_to_cpu(desc->length) >= CARL9170FW_DESC_HEAD_SIZE && \ le16_to_cpu(desc->length) < CARL9170FW_DESC_MAX_LENGTH; \ desc = (void *)((unsigned long)desc + le16_to_cpu(desc->length))) #define CHECK_HDR_VERSION(head, _min_ver) \ (((head)->cur_ver < _min_ver) || ((head)->min_ver > _min_ver)) \ static inline bool carl9170fw_supports(__le32 list, u8 feature) { return le32_to_cpu(list) & BIT(feature); } static inline bool carl9170fw_desc_cmp(const struct carl9170fw_desc_head *head, const u8 descid[CARL9170FW_MAGIC_SIZE], u16 min_len, u8 compatible_revision) { if (descid[0] == head->magic[0] && descid[1] == head->magic[1] && descid[2] == head->magic[2] && descid[3] == head->magic[3] && !CHECK_HDR_VERSION(head, compatible_revision) && (le16_to_cpu(head->length) >= min_len)) return true; return false; } #define CARL9170FW_MIN_SIZE 32 #define CARL9170FW_MAX_SIZE 16384 static inline bool carl9170fw_size_check(unsigned int len) { return (len <= CARL9170FW_MAX_SIZE && len >= CARL9170FW_MIN_SIZE); } #endif /* __CARL9170_SHARED_FWDESC_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/fwcmd.h0000644000175000017500000002021612026211315023751 0ustar mcgrofmcgrof/* * Shared Atheros AR9170 Header * * Firmware command interface definitions * * Copyright 2008, Johannes Berg * Copyright 2009-2011 Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CARL9170_SHARED_FWCMD_H #define __CARL9170_SHARED_FWCMD_H #define CARL9170_MAX_CMD_LEN 64 #define CARL9170_MAX_CMD_PAYLOAD_LEN 60 #define CARL9170FW_API_MIN_VER 1 #define CARL9170FW_API_MAX_VER 1 enum carl9170_cmd_oids { CARL9170_CMD_RREG = 0x00, CARL9170_CMD_WREG = 0x01, CARL9170_CMD_ECHO = 0x02, CARL9170_CMD_SWRST = 0x03, CARL9170_CMD_REBOOT = 0x04, CARL9170_CMD_BCN_CTRL = 0x05, CARL9170_CMD_READ_TSF = 0x06, CARL9170_CMD_RX_FILTER = 0x07, CARL9170_CMD_WOL = 0x08, CARL9170_CMD_TALLY = 0x09, /* CAM */ CARL9170_CMD_EKEY = 0x10, CARL9170_CMD_DKEY = 0x11, /* RF / PHY */ CARL9170_CMD_FREQUENCY = 0x20, CARL9170_CMD_RF_INIT = 0x21, CARL9170_CMD_SYNTH = 0x22, CARL9170_CMD_FREQ_START = 0x23, CARL9170_CMD_PSM = 0x24, /* Asychronous command flag */ CARL9170_CMD_ASYNC_FLAG = 0x40, CARL9170_CMD_WREG_ASYNC = (CARL9170_CMD_WREG | CARL9170_CMD_ASYNC_FLAG), CARL9170_CMD_REBOOT_ASYNC = (CARL9170_CMD_REBOOT | CARL9170_CMD_ASYNC_FLAG), CARL9170_CMD_BCN_CTRL_ASYNC = (CARL9170_CMD_BCN_CTRL | CARL9170_CMD_ASYNC_FLAG), CARL9170_CMD_PSM_ASYNC = (CARL9170_CMD_PSM | CARL9170_CMD_ASYNC_FLAG), /* responses and traps */ CARL9170_RSP_FLAG = 0xc0, CARL9170_RSP_PRETBTT = 0xc0, CARL9170_RSP_TXCOMP = 0xc1, CARL9170_RSP_BEACON_CONFIG = 0xc2, CARL9170_RSP_ATIM = 0xc3, CARL9170_RSP_WATCHDOG = 0xc6, CARL9170_RSP_TEXT = 0xca, CARL9170_RSP_HEXDUMP = 0xcc, CARL9170_RSP_RADAR = 0xcd, CARL9170_RSP_GPIO = 0xce, CARL9170_RSP_BOOT = 0xcf, }; struct carl9170_set_key_cmd { __le16 user; __le16 keyId; __le16 type; u8 macAddr[6]; u32 key[4]; } __packed __aligned(4); #define CARL9170_SET_KEY_CMD_SIZE 28 struct carl9170_disable_key_cmd { __le16 user; __le16 padding; } __packed __aligned(4); #define CARL9170_DISABLE_KEY_CMD_SIZE 4 struct carl9170_u32_list { u32 vals[0]; } __packed; struct carl9170_reg_list { __le32 regs[0]; } __packed; struct carl9170_write_reg { struct { __le32 addr; __le32 val; } regs[0] __packed; } __packed; #define CARL9170FW_PHY_HT_ENABLE 0x4 #define CARL9170FW_PHY_HT_DYN2040 0x8 #define CARL9170FW_PHY_HT_EXT_CHAN_OFF 0x3 #define CARL9170FW_PHY_HT_EXT_CHAN_OFF_S 2 struct carl9170_rf_init { __le32 freq; u8 ht_settings; u8 padding2[3]; __le32 delta_slope_coeff_exp; __le32 delta_slope_coeff_man; __le32 delta_slope_coeff_exp_shgi; __le32 delta_slope_coeff_man_shgi; __le32 finiteLoopCount; } __packed; #define CARL9170_RF_INIT_SIZE 28 struct carl9170_rf_init_result { __le32 ret; /* AR9170_PHY_REG_AGC_CONTROL */ } __packed; #define CARL9170_RF_INIT_RESULT_SIZE 4 #define CARL9170_PSM_SLEEP 0x1000 #define CARL9170_PSM_SOFTWARE 0 #define CARL9170_PSM_WAKE 0 /* internally used. */ #define CARL9170_PSM_COUNTER 0xfff #define CARL9170_PSM_COUNTER_S 0 struct carl9170_psm { __le32 state; } __packed; #define CARL9170_PSM_SIZE 4 struct carl9170_rx_filter_cmd { __le32 rx_filter; } __packed; #define CARL9170_RX_FILTER_CMD_SIZE 4 #define CARL9170_RX_FILTER_BAD 0x01 #define CARL9170_RX_FILTER_OTHER_RA 0x02 #define CARL9170_RX_FILTER_DECRY_FAIL 0x04 #define CARL9170_RX_FILTER_CTL_OTHER 0x08 #define CARL9170_RX_FILTER_CTL_PSPOLL 0x10 #define CARL9170_RX_FILTER_CTL_BACKR 0x20 #define CARL9170_RX_FILTER_MGMT 0x40 #define CARL9170_RX_FILTER_DATA 0x80 #define CARL9170_RX_FILTER_EVERYTHING (~0) struct carl9170_bcn_ctrl_cmd { __le32 vif_id; __le32 mode; __le32 bcn_addr; __le32 bcn_len; } __packed; #define CARL9170_BCN_CTRL_CMD_SIZE 16 #define CARL9170_BCN_CTRL_DRAIN 0 #define CARL9170_BCN_CTRL_CAB_TRIGGER 1 struct carl9170_wol_cmd { __le32 flags; u8 mac[6]; u8 bssid[6]; __le32 null_interval; __le32 free_for_use2; __le32 mask; u8 pattern[32]; } __packed; #define CARL9170_WOL_CMD_SIZE 60 #define CARL9170_WOL_DISCONNECT 1 #define CARL9170_WOL_MAGIC_PKT 2 struct carl9170_cmd_head { union { struct { u8 len; u8 cmd; u8 seq; u8 ext; } __packed; u32 hdr_data; } __packed; } __packed; struct carl9170_cmd { struct carl9170_cmd_head hdr; union { struct carl9170_set_key_cmd setkey; struct carl9170_disable_key_cmd disablekey; struct carl9170_u32_list echo; struct carl9170_reg_list rreg; struct carl9170_write_reg wreg; struct carl9170_rf_init rf_init; struct carl9170_psm psm; struct carl9170_wol_cmd wol; struct carl9170_bcn_ctrl_cmd bcn_ctrl; struct carl9170_rx_filter_cmd rx_filter; u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN]; } __packed; } __packed __aligned(4); #define CARL9170_TX_STATUS_QUEUE 3 #define CARL9170_TX_STATUS_QUEUE_S 0 #define CARL9170_TX_STATUS_RIX_S 2 #define CARL9170_TX_STATUS_RIX (3 << CARL9170_TX_STATUS_RIX_S) #define CARL9170_TX_STATUS_TRIES_S 4 #define CARL9170_TX_STATUS_TRIES (7 << CARL9170_TX_STATUS_TRIES_S) #define CARL9170_TX_STATUS_SUCCESS 0x80 #ifdef __CARL9170FW__ /* * NOTE: * Both structs [carl9170_tx_status and _carl9170_tx_status] * need to be "bit for bit" in sync. */ struct carl9170_tx_status { /* * Beware of compiler bugs in all gcc pre 4.4! */ u8 cookie; u8 queue:2; u8 rix:2; u8 tries:3; u8 success:1; } __packed; #endif /* __CARL9170FW__ */ struct _carl9170_tx_status { /* * This version should be immune to all alignment bugs. */ u8 cookie; u8 info; } __packed; #define CARL9170_TX_STATUS_SIZE 2 #define CARL9170_RSP_TX_STATUS_NUM (CARL9170_MAX_CMD_PAYLOAD_LEN / \ sizeof(struct _carl9170_tx_status)) #define CARL9170_TX_MAX_RATE_TRIES 7 #define CARL9170_TX_MAX_RATES 4 #define CARL9170_TX_MAX_RETRY_RATES (CARL9170_TX_MAX_RATES - 1) #define CARL9170_ERR_MAGIC "ERR:" #define CARL9170_BUG_MAGIC "BUG:" struct carl9170_gpio { __le32 gpio; } __packed; #define CARL9170_GPIO_SIZE 4 struct carl9170_tsf_rsp { union { __le32 tsf[2]; __le64 tsf_64; } __packed; } __packed; #define CARL9170_TSF_RSP_SIZE 8 struct carl9170_tally_rsp { __le32 active; __le32 cca; __le32 tx_time; __le32 rx_total; __le32 rx_overrun; __le32 tick; } __packed; struct carl9170_rsp { struct carl9170_cmd_head hdr; union { struct carl9170_rf_init_result rf_init_res; struct carl9170_u32_list rreg_res; struct carl9170_u32_list echo; #ifdef __CARL9170FW__ struct carl9170_tx_status tx_status[0]; #endif /* __CARL9170FW__ */ struct _carl9170_tx_status _tx_status[0]; struct carl9170_gpio gpio; struct carl9170_tsf_rsp tsf; struct carl9170_psm psm; struct carl9170_tally_rsp tally; u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN]; } __packed; } __packed __aligned(4); #endif /* __CARL9170_SHARED_FWCMD_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/fw.c0000644000175000017500000002626612026211315023273 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * firmware parser * * Copyright 2009, 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. */ #include #include #include #include #include "carl9170.h" #include "fwcmd.h" #include "version.h" #define MAKE_STR(symbol) #symbol #define TO_STR(symbol) MAKE_STR(symbol) #define CARL9170FW_API_VER_STR TO_STR(CARL9170FW_API_MAX_VER) MODULE_VERSION(CARL9170FW_API_VER_STR ":" CARL9170FW_VERSION_GIT); static const u8 otus_magic[4] = { OTUS_MAGIC }; static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4], const unsigned int len, const u8 compatible_revision) { const struct carl9170fw_desc_head *iter; carl9170fw_for_each_hdr(iter, ar->fw.desc) { if (carl9170fw_desc_cmp(iter, descid, len, compatible_revision)) return (void *)iter; } /* needed to find the LAST desc */ if (carl9170fw_desc_cmp(iter, descid, len, compatible_revision)) return (void *)iter; return NULL; } static int carl9170_fw_verify_descs(struct ar9170 *ar, const struct carl9170fw_desc_head *head, unsigned int max_len) { const struct carl9170fw_desc_head *pos; unsigned long pos_addr, end_addr; unsigned int pos_length; if (max_len < sizeof(*pos)) return -ENODATA; max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len); pos = head; pos_addr = (unsigned long) pos; end_addr = pos_addr + max_len; while (pos_addr < end_addr) { if (pos_addr + sizeof(*head) > end_addr) return -E2BIG; pos_length = le16_to_cpu(pos->length); if (pos_length < sizeof(*head)) return -EBADMSG; if (pos_length > max_len) return -EOVERFLOW; if (pos_addr + pos_length > end_addr) return -EMSGSIZE; if (carl9170fw_desc_cmp(pos, LAST_MAGIC, CARL9170FW_LAST_DESC_SIZE, CARL9170FW_LAST_DESC_CUR_VER)) return 0; pos_addr += pos_length; pos = (void *)pos_addr; max_len -= pos_length; } return -EINVAL; } static void carl9170_fw_info(struct ar9170 *ar) { const struct carl9170fw_motd_desc *motd_desc; unsigned int str_ver_len; u32 fw_date; dev_info(&ar->udev->dev, "driver API: %s 2%03d-%02d-%02d [%d-%d]\n", CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR, CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY, CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER); motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC, sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER); if (motd_desc) { str_ver_len = strnlen(motd_desc->release, CARL9170FW_MOTD_RELEASE_LEN); fw_date = le32_to_cpu(motd_desc->fw_year_month_day); dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n", str_ver_len, motd_desc->release, CARL9170FW_GET_YEAR(fw_date), CARL9170FW_GET_MONTH(fw_date), CARL9170FW_GET_DAY(fw_date)); strlcpy(ar->hw->wiphy->fw_version, motd_desc->release, sizeof(ar->hw->wiphy->fw_version)); } } static bool valid_dma_addr(const u32 address) { if (address >= AR9170_SRAM_OFFSET && address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE)) return true; return false; } static bool valid_cpu_addr(const u32 address) { if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET && address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE))) return true; return false; } static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data, size_t len) { const struct carl9170fw_otus_desc *otus_desc; const struct carl9170fw_last_desc *last_desc; const struct carl9170fw_chk_desc *chk_desc; unsigned long fin, diff; unsigned int dsc_len; u32 crc32; last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC, sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER); if (!last_desc) return -EINVAL; otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC, sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER); if (!otus_desc) { dev_err(&ar->udev->dev, "failed to find compatible firmware " "descriptor.\n"); return -ENODATA; } chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC, sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER); if (!chk_desc) { dev_warn(&ar->udev->dev, "Unprotected firmware image.\n"); return 0; } dsc_len = min_t(unsigned int, len, (unsigned long)chk_desc - (unsigned long)otus_desc); fin = (unsigned long) last_desc + sizeof(*last_desc); diff = fin - (unsigned long) otus_desc; if (diff < len) len -= diff; if (len < 256) return -EIO; crc32 = crc32_le(~0, data, len); if (cpu_to_le32(crc32) != chk_desc->fw_crc32) { dev_err(&ar->udev->dev, "fw checksum test failed.\n"); return -ENOEXEC; } crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len); if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) { dev_err(&ar->udev->dev, "descriptor check failed.\n"); return -EINVAL; } return 0; } static int carl9170_fw_tx_sequence(struct ar9170 *ar) { const struct carl9170fw_txsq_desc *txsq_desc; txsq_desc = carl9170_fw_find_desc(ar, TXSQ_MAGIC, sizeof(*txsq_desc), CARL9170FW_TXSQ_DESC_CUR_VER); if (txsq_desc) { ar->fw.tx_seq_table = le32_to_cpu(txsq_desc->seq_table_addr); if (!valid_cpu_addr(ar->fw.tx_seq_table)) return -EINVAL; } else { ar->fw.tx_seq_table = 0; } return 0; } static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) { const struct carl9170fw_otus_desc *otus_desc; int err; u16 if_comb_types; err = carl9170_fw_checksum(ar, data, len); if (err) return err; otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC, sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER); if (!otus_desc) { return -ENODATA; } #define SUPP(feat) \ (carl9170fw_supports(otus_desc->feature_set, feat)) if (!SUPP(CARL9170FW_DUMMY_FEATURE)) { dev_err(&ar->udev->dev, "invalid firmware descriptor " "format detected.\n"); return -EINVAL; } ar->fw.api_version = otus_desc->api_ver; if (ar->fw.api_version < CARL9170FW_API_MIN_VER || ar->fw.api_version > CARL9170FW_API_MAX_VER) { dev_err(&ar->udev->dev, "unsupported firmware api version.\n"); return -EINVAL; } if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) || !SUPP(CARL9170FW_HANDLE_BACK_REQ)) { dev_err(&ar->udev->dev, "firmware does support " "mandatory features.\n"); return -ECANCELED; } if (ilog2(le32_to_cpu(otus_desc->feature_set)) >= __CARL9170FW_FEATURE_NUM) { dev_warn(&ar->udev->dev, "driver does not support all " "firmware features.\n"); } if (!SUPP(CARL9170FW_COMMAND_CAM)) { dev_info(&ar->udev->dev, "crypto offloading is disabled " "by firmware.\n"); ar->disable_offload = true; } if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM)) ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS; if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) { dev_err(&ar->udev->dev, "firmware does not provide " "mandatory interfaces.\n"); return -EINVAL; } if (SUPP(CARL9170FW_MINIBOOT)) ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size); else ar->fw.offset = 0; if (SUPP(CARL9170FW_USB_DOWN_STREAM)) { ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream); ar->fw.tx_stream = true; } if (SUPP(CARL9170FW_USB_UP_STREAM)) ar->fw.rx_stream = true; if (SUPP(CARL9170FW_RX_FILTER)) { ar->fw.rx_filter = true; ar->rx_filter_caps = FIF_FCSFAIL | FIF_PLCPFAIL | FIF_CONTROL | FIF_PSPOLL | FIF_OTHER_BSS | FIF_PROMISC_IN_BSS; } if (SUPP(CARL9170FW_HW_COUNTERS)) ar->fw.hw_counters = true; if (SUPP(CARL9170FW_WOL)) device_set_wakeup_enable(&ar->udev->dev, true); if (SUPP(CARL9170FW_RX_BA_FILTER)) ar->fw.ba_filter = true; if_comb_types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT); ar->fw.vif_num = otus_desc->vif_num; ar->fw.cmd_bufs = otus_desc->cmd_bufs; ar->fw.address = le32_to_cpu(otus_desc->fw_address); ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len); ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe); atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks); ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len); if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num || ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs || ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 || ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 || !valid_cpu_addr(ar->fw.address)) { dev_err(&ar->udev->dev, "firmware shows obvious signs of " "malicious tampering.\n"); return -EINVAL; } ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr); ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len); if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >= AR9170_MAC_BCN_LENGTH_MAX) { ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); if (SUPP(CARL9170FW_WLANTX_CAB)) { if_comb_types |= BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_P2P_GO); } } ar->if_comb_limits[0].max = ar->fw.vif_num; ar->if_comb_limits[0].types = if_comb_types; ar->if_combs[0].num_different_channels = 1; ar->if_combs[0].max_interfaces = ar->fw.vif_num; ar->if_combs[0].limits = ar->if_comb_limits; ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits); ar->hw->wiphy->iface_combinations = ar->if_combs; ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs); ar->hw->wiphy->interface_modes |= if_comb_types; ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; #undef SUPPORTED return carl9170_fw_tx_sequence(ar); } static struct carl9170fw_desc_head * carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len) { int scan = 0, found = 0; if (!carl9170fw_size_check(len)) { dev_err(&ar->udev->dev, "firmware size is out of bound.\n"); return NULL; } while (scan < len - sizeof(struct carl9170fw_desc_head)) { if (fw_data[scan++] == otus_magic[found]) found++; else found = 0; if (scan >= len) break; if (found == sizeof(otus_magic)) break; } if (found != sizeof(otus_magic)) return NULL; return (void *)&fw_data[scan - found]; } int carl9170_parse_firmware(struct ar9170 *ar) { const struct carl9170fw_desc_head *fw_desc = NULL; const struct firmware *fw = ar->fw.fw; unsigned long header_offset = 0; int err; if (WARN_ON(!fw)) return -EINVAL; fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size); if (!fw_desc) { dev_err(&ar->udev->dev, "unsupported firmware.\n"); return -ENODATA; } header_offset = (unsigned long)fw_desc - (unsigned long)fw->data; err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset); if (err) { dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err); return err; } ar->fw.desc = fw_desc; carl9170_fw_info(ar); err = carl9170_fw(ar, fw->data, fw->size); if (err) { dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n", err); return err; } return 0; } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/eeprom.h0000644000175000017500000001431612026211315024144 0ustar mcgrofmcgrof/* * Shared Atheros AR9170 Header * * EEPROM layout * * Copyright 2008, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CARL9170_SHARED_EEPROM_H #define __CARL9170_SHARED_EEPROM_H #define AR9170_EEPROM_START 0x1600 #define AR5416_MAX_CHAINS 2 #define AR5416_MODAL_SPURS 5 struct ar9170_eeprom_modal { __le32 antCtrlChain[AR5416_MAX_CHAINS]; __le32 antCtrlCommon; s8 antennaGainCh[AR5416_MAX_CHAINS]; u8 switchSettling; u8 txRxAttenCh[AR5416_MAX_CHAINS]; u8 rxTxMarginCh[AR5416_MAX_CHAINS]; s8 adcDesiredSize; s8 pgaDesiredSize; u8 xlnaGainCh[AR5416_MAX_CHAINS]; u8 txEndToXpaOff; u8 txEndToRxOn; u8 txFrameToXpaOn; u8 thresh62; s8 noiseFloorThreshCh[AR5416_MAX_CHAINS]; u8 xpdGain; u8 xpd; s8 iqCalICh[AR5416_MAX_CHAINS]; s8 iqCalQCh[AR5416_MAX_CHAINS]; u8 pdGainOverlap; u8 ob; u8 db; u8 xpaBiasLvl; u8 pwrDecreaseFor2Chain; u8 pwrDecreaseFor3Chain; u8 txFrameToDataStart; u8 txFrameToPaOn; u8 ht40PowerIncForPdadc; u8 bswAtten[AR5416_MAX_CHAINS]; u8 bswMargin[AR5416_MAX_CHAINS]; u8 swSettleHt40; u8 reserved[22]; struct spur_channel { __le16 spurChan; u8 spurRangeLow; u8 spurRangeHigh; } __packed spur_channels[AR5416_MODAL_SPURS]; } __packed; #define AR5416_NUM_PD_GAINS 4 #define AR5416_PD_GAIN_ICEPTS 5 struct ar9170_calibration_data_per_freq { u8 pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; u8 vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; } __packed; #define AR5416_NUM_5G_CAL_PIERS 8 #define AR5416_NUM_2G_CAL_PIERS 4 #define AR5416_NUM_5G_TARGET_PWRS 8 #define AR5416_NUM_2G_CCK_TARGET_PWRS 3 #define AR5416_NUM_2G_OFDM_TARGET_PWRS 4 #define AR5416_MAX_NUM_TGT_PWRS 8 struct ar9170_calibration_target_power_legacy { u8 freq; u8 power[4]; } __packed; struct ar9170_calibration_target_power_ht { u8 freq; u8 power[8]; } __packed; #define AR5416_NUM_CTLS 24 struct ar9170_calctl_edges { u8 channel; #define AR9170_CALCTL_EDGE_FLAGS 0xC0 u8 power_flags; } __packed; #define AR5416_NUM_BAND_EDGES 8 struct ar9170_calctl_data { struct ar9170_calctl_edges control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; } __packed; struct ar9170_eeprom { __le16 length; __le16 checksum; __le16 version; u8 operating_flags; #define AR9170_OPFLAG_5GHZ 1 #define AR9170_OPFLAG_2GHZ 2 u8 misc; __le16 reg_domain[2]; u8 mac_address[6]; u8 rx_mask; u8 tx_mask; __le16 rf_silent; __le16 bluetooth_options; __le16 device_capabilities; __le32 build_number; u8 deviceType; u8 reserved[33]; u8 customer_data[64]; struct ar9170_eeprom_modal modal_header[2]; u8 cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS]; u8 cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS]; struct ar9170_calibration_data_per_freq cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS], cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]; /* power calibration data */ struct ar9170_calibration_target_power_legacy cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS]; struct ar9170_calibration_target_power_ht cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS], cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS]; struct ar9170_calibration_target_power_legacy cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS], cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS]; struct ar9170_calibration_target_power_ht cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS], cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS]; /* conformance testing limits */ u8 ctl_index[AR5416_NUM_CTLS]; struct ar9170_calctl_data ctl_data[AR5416_NUM_CTLS]; u8 pad; __le16 subsystem_id; } __packed; #define AR9170_LED_MODE_POWER_ON 0x0001 #define AR9170_LED_MODE_RESERVED 0x0002 #define AR9170_LED_MODE_DISABLE_STATE 0x0004 #define AR9170_LED_MODE_OFF_IN_PSM 0x0008 /* AR9170_LED_MODE BIT is set */ #define AR9170_LED_MODE_FREQUENCY_S 4 #define AR9170_LED_MODE_FREQUENCY 0x0030 #define AR9170_LED_MODE_FREQUENCY_1HZ 0x0000 #define AR9170_LED_MODE_FREQUENCY_0_5HZ 0x0010 #define AR9170_LED_MODE_FREQUENCY_0_25HZ 0x0020 #define AR9170_LED_MODE_FREQUENCY_0_125HZ 0x0030 /* AR9170_LED_MODE BIT is not set */ #define AR9170_LED_MODE_CONN_STATE_S 4 #define AR9170_LED_MODE_CONN_STATE 0x0030 #define AR9170_LED_MODE_CONN_STATE_FORCE_OFF 0x0000 #define AR9170_LED_MODE_CONN_STATE_FORCE_ON 0x0010 /* Idle off / Active on */ #define AR9170_LED_MODE_CONN_STATE_IOFF_AON 0x0020 /* Idle on / Active off */ #define AR9170_LED_MODE_CONN_STATE_ION_AOFF 0x0010 #define AR9170_LED_MODE_MODE 0x0040 #define AR9170_LED_MODE_RESERVED2 0x0080 #define AR9170_LED_MODE_TON_SCAN_S 8 #define AR9170_LED_MODE_TON_SCAN 0x0f00 #define AR9170_LED_MODE_TOFF_SCAN_S 12 #define AR9170_LED_MODE_TOFF_SCAN 0xf000 struct ar9170_led_mode { __le16 led; }; #endif /* __CARL9170_SHARED_EEPROM_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/debug.h0000644000175000017500000001057012026211315023741 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * debug header * * Copyright 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __DEBUG_H #define __DEBUG_H #include "eeprom.h" #include "wlan.h" #include "hw.h" #include "fwdesc.h" #include "fwcmd.h" #include "../regd.h" struct hw_stat_reg_entry { u32 reg; char nreg[32]; }; #define STAT_MAC_REG(reg) \ { (AR9170_MAC_REG_##reg), #reg } #define STAT_PTA_REG(reg) \ { (AR9170_PTA_REG_##reg), #reg } #define STAT_USB_REG(reg) \ { (AR9170_USB_REG_##reg), #reg } static const struct hw_stat_reg_entry hw_rx_tally_regs[] = { STAT_MAC_REG(RX_CRC32), STAT_MAC_REG(RX_CRC16), STAT_MAC_REG(RX_TIMEOUT_COUNT), STAT_MAC_REG(RX_ERR_DECRYPTION_UNI), STAT_MAC_REG(RX_ERR_DECRYPTION_MUL), STAT_MAC_REG(RX_MPDU), STAT_MAC_REG(RX_DROPPED_MPDU), STAT_MAC_REG(RX_DEL_MPDU), }; static const struct hw_stat_reg_entry hw_phy_errors_regs[] = { STAT_MAC_REG(RX_PHY_MISC_ERROR), STAT_MAC_REG(RX_PHY_XR_ERROR), STAT_MAC_REG(RX_PHY_OFDM_ERROR), STAT_MAC_REG(RX_PHY_CCK_ERROR), STAT_MAC_REG(RX_PHY_HT_ERROR), STAT_MAC_REG(RX_PHY_TOTAL), }; static const struct hw_stat_reg_entry hw_tx_tally_regs[] = { STAT_MAC_REG(TX_TOTAL), STAT_MAC_REG(TX_UNDERRUN), STAT_MAC_REG(TX_RETRY), }; static const struct hw_stat_reg_entry hw_wlan_queue_regs[] = { STAT_MAC_REG(DMA_STATUS), STAT_MAC_REG(DMA_TRIGGER), STAT_MAC_REG(DMA_TXQ0_ADDR), STAT_MAC_REG(DMA_TXQ0_CURR_ADDR), STAT_MAC_REG(DMA_TXQ1_ADDR), STAT_MAC_REG(DMA_TXQ1_CURR_ADDR), STAT_MAC_REG(DMA_TXQ2_ADDR), STAT_MAC_REG(DMA_TXQ2_CURR_ADDR), STAT_MAC_REG(DMA_TXQ3_ADDR), STAT_MAC_REG(DMA_TXQ3_CURR_ADDR), STAT_MAC_REG(DMA_RXQ_ADDR), STAT_MAC_REG(DMA_RXQ_CURR_ADDR), }; static const struct hw_stat_reg_entry hw_ampdu_info_regs[] = { STAT_MAC_REG(AMPDU_DENSITY), STAT_MAC_REG(AMPDU_FACTOR), }; static const struct hw_stat_reg_entry hw_pta_queue_regs[] = { STAT_PTA_REG(DN_CURR_ADDRH), STAT_PTA_REG(DN_CURR_ADDRL), STAT_PTA_REG(UP_CURR_ADDRH), STAT_PTA_REG(UP_CURR_ADDRL), STAT_PTA_REG(DMA_STATUS), STAT_PTA_REG(DMA_MODE_CTRL), }; #define DEFINE_TALLY(name) \ u32 name##_sum[ARRAY_SIZE(name##_regs)], \ name##_counter[ARRAY_SIZE(name##_regs)] \ #define DEFINE_STAT(name) \ u32 name##_counter[ARRAY_SIZE(name##_regs)] \ struct ath_stats { DEFINE_TALLY(hw_tx_tally); DEFINE_TALLY(hw_rx_tally); DEFINE_TALLY(hw_phy_errors); DEFINE_STAT(hw_wlan_queue); DEFINE_STAT(hw_pta_queue); DEFINE_STAT(hw_ampdu_info); }; struct carl9170_debug_mem_rbe { u32 reg; u32 value; }; #define CARL9170_DEBUG_RING_SIZE 64 struct carl9170_debug { struct ath_stats stats; struct carl9170_debug_mem_rbe ring[CARL9170_DEBUG_RING_SIZE]; struct mutex ring_lock; unsigned int ring_head, ring_tail; struct delayed_work update_tally; }; struct ar9170; void carl9170_debugfs_register(struct ar9170 *ar); void carl9170_debugfs_unregister(struct ar9170 *ar); #endif /* __DEBUG_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/debug.c0000644000175000017500000005713712026211315023746 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * debug(fs) probing * * Copyright 2008, Johannes Berg * Copyright 2009, 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2008-2009 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "carl9170.h" #include "cmd.h" #define ADD(buf, off, max, fmt, args...) \ off += snprintf(&buf[off], max - off, fmt, ##args); struct carl9170_debugfs_fops { unsigned int read_bufsize; umode_t attr; char *(*read)(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len); ssize_t (*write)(struct ar9170 *aru, const char *buf, size_t size); const struct file_operations fops; enum carl9170_device_state req_dev_state; }; static ssize_t carl9170_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct carl9170_debugfs_fops *dfops; struct ar9170 *ar; char *buf = NULL, *res_buf = NULL; ssize_t ret = 0; int err = 0; if (!count) return 0; ar = file->private_data; if (!ar) return -ENODEV; dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops); if (!dfops->read) return -ENOSYS; if (dfops->read_bufsize) { buf = vmalloc(dfops->read_bufsize); if (!buf) return -ENOMEM; } mutex_lock(&ar->mutex); if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) { err = -ENODEV; res_buf = buf; goto out_free; } res_buf = dfops->read(ar, buf, dfops->read_bufsize, &ret); if (ret > 0) err = simple_read_from_buffer(userbuf, count, ppos, res_buf, ret); else err = ret; WARN_ON_ONCE(dfops->read_bufsize && (res_buf != buf)); out_free: vfree(res_buf); mutex_unlock(&ar->mutex); return err; } static ssize_t carl9170_debugfs_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct carl9170_debugfs_fops *dfops; struct ar9170 *ar; char *buf = NULL; int err = 0; if (!count) return 0; if (count > PAGE_SIZE) return -E2BIG; ar = file->private_data; if (!ar) return -ENODEV; dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops); if (!dfops->write) return -ENOSYS; buf = vmalloc(count); if (!buf) return -ENOMEM; if (copy_from_user(buf, userbuf, count)) { err = -EFAULT; goto out_free; } if (mutex_trylock(&ar->mutex) == 0) { err = -EAGAIN; goto out_free; } if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) { err = -ENODEV; goto out_unlock; } err = dfops->write(ar, buf, count); if (err) goto out_unlock; out_unlock: mutex_unlock(&ar->mutex); out_free: vfree(buf); return err; } #define __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, \ _attr, _dstate) \ static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\ .read_bufsize = _read_bufsize, \ .read = _read, \ .write = _write, \ .attr = _attr, \ .req_dev_state = _dstate, \ .fops = { \ .open = simple_open, \ .read = carl9170_debugfs_read, \ .write = carl9170_debugfs_write, \ .owner = THIS_MODULE \ }, \ } #define DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, _attr) \ __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, \ _attr, CARL9170_STARTED) \ #define DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize) \ DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \ NULL, _read_bufsize, S_IRUSR) #define DEBUGFS_DECLARE_WO_FILE(name) \ DEBUGFS_DECLARE_FILE(name, NULL, carl9170_debugfs_##name ##_write,\ 0, S_IWUSR) #define DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize) \ DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \ carl9170_debugfs_##name ##_write, \ _read_bufsize, S_IRUSR | S_IWUSR) #define __DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize, _dstate) \ __DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \ carl9170_debugfs_##name ##_write, \ _read_bufsize, S_IRUSR | S_IWUSR, _dstate) #define DEBUGFS_READONLY_FILE(name, _read_bufsize, fmt, value...) \ static char *carl9170_debugfs_ ##name ## _read(struct ar9170 *ar, \ char *buf, size_t buf_size,\ ssize_t *len) \ { \ ADD(buf, *len, buf_size, fmt "\n", ##value); \ return buf; \ } \ DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize) static char *carl9170_debugfs_mem_usage_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len) { ADD(buf, *len, bufsize, "jar: ["); spin_lock_bh(&ar->mem_lock); *len += bitmap_scnprintf(&buf[*len], bufsize - *len, ar->mem_bitmap, ar->fw.mem_blocks); ADD(buf, *len, bufsize, "]\n"); ADD(buf, *len, bufsize, "cookies: used:%3d / total:%3d, allocs:%d\n", bitmap_weight(ar->mem_bitmap, ar->fw.mem_blocks), ar->fw.mem_blocks, atomic_read(&ar->mem_allocs)); ADD(buf, *len, bufsize, "memory: free:%3d (%3d KiB) / total:%3d KiB)\n", atomic_read(&ar->mem_free_blocks), (atomic_read(&ar->mem_free_blocks) * ar->fw.mem_block_size) / 1024, (ar->fw.mem_blocks * ar->fw.mem_block_size) / 1024); spin_unlock_bh(&ar->mem_lock); return buf; } DEBUGFS_DECLARE_RO_FILE(mem_usage, 512); static char *carl9170_debugfs_qos_stat_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len) { ADD(buf, *len, bufsize, "%s QoS AC\n", modparam_noht ? "Hardware" : "Software"); ADD(buf, *len, bufsize, "[ VO VI " " BE BK ]\n"); spin_lock_bh(&ar->tx_stats_lock); ADD(buf, *len, bufsize, "[length/limit length/limit " "length/limit length/limit ]\n" "[ %3d/%3d %3d/%3d " " %3d/%3d %3d/%3d ]\n\n", ar->tx_stats[0].len, ar->tx_stats[0].limit, ar->tx_stats[1].len, ar->tx_stats[1].limit, ar->tx_stats[2].len, ar->tx_stats[2].limit, ar->tx_stats[3].len, ar->tx_stats[3].limit); ADD(buf, *len, bufsize, "[ total total " " total total ]\n" "[%10d %10d %10d %10d ]\n\n", ar->tx_stats[0].count, ar->tx_stats[1].count, ar->tx_stats[2].count, ar->tx_stats[3].count); spin_unlock_bh(&ar->tx_stats_lock); ADD(buf, *len, bufsize, "[ pend/waittx pend/waittx " " pend/waittx pend/waittx]\n" "[ %3d/%3d %3d/%3d " " %3d/%3d %3d/%3d ]\n\n", skb_queue_len(&ar->tx_pending[0]), skb_queue_len(&ar->tx_status[0]), skb_queue_len(&ar->tx_pending[1]), skb_queue_len(&ar->tx_status[1]), skb_queue_len(&ar->tx_pending[2]), skb_queue_len(&ar->tx_status[2]), skb_queue_len(&ar->tx_pending[3]), skb_queue_len(&ar->tx_status[3])); return buf; } DEBUGFS_DECLARE_RO_FILE(qos_stat, 512); static void carl9170_debugfs_format_frame(struct ar9170 *ar, struct sk_buff *skb, const char *prefix, char *buf, ssize_t *off, ssize_t bufsize) { struct _carl9170_tx_superframe *txc = (void *) skb->data; struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); struct carl9170_tx_info *arinfo = (void *) txinfo->rate_driver_data; struct ieee80211_hdr *hdr = (void *) txc->frame_data; ADD(buf, *off, bufsize, "%s %p, c:%2x, DA:%pM, sq:%4d, mc:%.4x, " "pc:%.8x, to:%d ms\n", prefix, skb, txc->s.cookie, ieee80211_get_DA(hdr), get_seq_h(hdr), le16_to_cpu(txc->f.mac_control), le32_to_cpu(txc->f.phy_control), jiffies_to_msecs(jiffies - arinfo->timeout)); } static char *carl9170_debugfs_ampdu_state_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len) { struct carl9170_sta_tid *iter; struct sk_buff *skb; int cnt = 0, fc; int offset; rcu_read_lock(); list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) { spin_lock_bh(&iter->lock); ADD(buf, *len, bufsize, "Entry: #%2d TID:%1d, BSN:%4d, " "SNX:%4d, HSN:%4d, BAW:%2d, state:%1d, toggles:%d\n", cnt, iter->tid, iter->bsn, iter->snx, iter->hsn, iter->max, iter->state, iter->counter); ADD(buf, *len, bufsize, "\tWindow: ["); *len += bitmap_scnprintf(&buf[*len], bufsize - *len, iter->bitmap, CARL9170_BAW_BITS); #define BM_STR_OFF(offset) \ ((CARL9170_BAW_BITS - (offset) - 1) / 4 + \ (CARL9170_BAW_BITS - (offset) - 1) / 32 + 1) ADD(buf, *len, bufsize, ",W]\n"); offset = BM_STR_OFF(0); ADD(buf, *len, bufsize, "\tBase Seq: %*s\n", offset, "T"); offset = BM_STR_OFF(SEQ_DIFF(iter->snx, iter->bsn)); ADD(buf, *len, bufsize, "\tNext Seq: %*s\n", offset, "W"); offset = BM_STR_OFF(((int)iter->hsn - (int)iter->bsn) % CARL9170_BAW_BITS); ADD(buf, *len, bufsize, "\tLast Seq: %*s\n", offset, "N"); ADD(buf, *len, bufsize, "\tPre-Aggregation reorder buffer: " " currently queued:%d\n", skb_queue_len(&iter->queue)); fc = 0; skb_queue_walk(&iter->queue, skb) { char prefix[32]; snprintf(prefix, sizeof(prefix), "\t\t%3d :", fc); carl9170_debugfs_format_frame(ar, skb, prefix, buf, len, bufsize); fc++; } spin_unlock_bh(&iter->lock); cnt++; } rcu_read_unlock(); return buf; } DEBUGFS_DECLARE_RO_FILE(ampdu_state, 8000); static void carl9170_debugfs_queue_dump(struct ar9170 *ar, char *buf, ssize_t *len, size_t bufsize, struct sk_buff_head *queue) { struct sk_buff *skb; char prefix[16]; int fc = 0; spin_lock_bh(&queue->lock); skb_queue_walk(queue, skb) { snprintf(prefix, sizeof(prefix), "%3d :", fc); carl9170_debugfs_format_frame(ar, skb, prefix, buf, len, bufsize); fc++; } spin_unlock_bh(&queue->lock); } #define DEBUGFS_QUEUE_DUMP(q, qi) \ static char *carl9170_debugfs_##q ##_##qi ##_read(struct ar9170 *ar, \ char *buf, size_t bufsize, ssize_t *len) \ { \ carl9170_debugfs_queue_dump(ar, buf, len, bufsize, &ar->q[qi]); \ return buf; \ } \ DEBUGFS_DECLARE_RO_FILE(q##_##qi, 8000); static char *carl9170_debugfs_sta_psm_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len) { ADD(buf, *len, bufsize, "psm state: %s\n", (ar->ps.off_override ? "FORCE CAM" : (ar->ps.state ? "PSM" : "CAM"))); ADD(buf, *len, bufsize, "sleep duration: %d ms.\n", ar->ps.sleep_ms); ADD(buf, *len, bufsize, "last power-state transition: %d ms ago.\n", jiffies_to_msecs(jiffies - ar->ps.last_action)); ADD(buf, *len, bufsize, "last CAM->PSM transition: %d ms ago.\n", jiffies_to_msecs(jiffies - ar->ps.last_slept)); return buf; } DEBUGFS_DECLARE_RO_FILE(sta_psm, 160); static char *carl9170_debugfs_tx_stuck_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len) { int i; for (i = 0; i < ar->hw->queues; i++) { ADD(buf, *len, bufsize, "TX queue [%d]: %10d max:%10d ms.\n", i, ieee80211_queue_stopped(ar->hw, i) ? jiffies_to_msecs(jiffies - ar->queue_stop_timeout[i]) : 0, jiffies_to_msecs(ar->max_queue_stop_timeout[i])); ar->max_queue_stop_timeout[i] = 0; } return buf; } DEBUGFS_DECLARE_RO_FILE(tx_stuck, 180); static char *carl9170_debugfs_phy_noise_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len) { int err; err = carl9170_get_noisefloor(ar); if (err) { *len = err; return buf; } ADD(buf, *len, bufsize, "Chain 0: %10d dBm, ext. chan.:%10d dBm\n", ar->noise[0], ar->noise[2]); ADD(buf, *len, bufsize, "Chain 2: %10d dBm, ext. chan.:%10d dBm\n", ar->noise[1], ar->noise[3]); return buf; } DEBUGFS_DECLARE_RO_FILE(phy_noise, 180); static char *carl9170_debugfs_vif_dump_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len) { struct carl9170_vif_info *iter; int i = 0; ADD(buf, *len, bufsize, "registered VIFs:%d \\ %d\n", ar->vifs, ar->fw.vif_num); ADD(buf, *len, bufsize, "VIF bitmap: ["); *len += bitmap_scnprintf(&buf[*len], bufsize - *len, &ar->vif_bitmap, ar->fw.vif_num); ADD(buf, *len, bufsize, "]\n"); rcu_read_lock(); list_for_each_entry_rcu(iter, &ar->vif_list, list) { struct ieee80211_vif *vif = carl9170_get_vif(iter); ADD(buf, *len, bufsize, "\t%d = [%s VIF, id:%d, type:%x " " mac:%pM %s]\n", i, (carl9170_get_main_vif(ar) == vif ? "Master" : " Slave"), iter->id, vif->type, vif->addr, iter->enable_beacon ? "beaconing " : ""); i++; } rcu_read_unlock(); return buf; } DEBUGFS_DECLARE_RO_FILE(vif_dump, 8000); #define UPDATE_COUNTER(ar, name) ({ \ u32 __tmp[ARRAY_SIZE(name##_regs)]; \ unsigned int __i, __err = -ENODEV; \ \ for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) { \ __tmp[__i] = name##_regs[__i].reg; \ ar->debug.stats.name##_counter[__i] = 0; \ } \ \ if (IS_STARTED(ar)) \ __err = carl9170_read_mreg(ar, ARRAY_SIZE(name##_regs), \ __tmp, ar->debug.stats.name##_counter); \ (__err); }) #define TALLY_SUM_UP(ar, name) do { \ unsigned int __i; \ \ for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) { \ ar->debug.stats.name##_sum[__i] += \ ar->debug.stats.name##_counter[__i]; \ } \ } while (0) #define DEBUGFS_HW_TALLY_FILE(name, f) \ static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar, \ char *dum, size_t bufsize, ssize_t *ret) \ { \ char *buf; \ int i, max_len, err; \ \ max_len = ARRAY_SIZE(name##_regs) * 80; \ buf = vmalloc(max_len); \ if (!buf) \ return NULL; \ \ err = UPDATE_COUNTER(ar, name); \ if (err) { \ *ret = err; \ return buf; \ } \ \ TALLY_SUM_UP(ar, name); \ \ for (i = 0; i < ARRAY_SIZE(name##_regs); i++) { \ ADD(buf, *ret, max_len, "%22s = %" f "[+%" f "]\n", \ name##_regs[i].nreg, ar->debug.stats.name ##_sum[i],\ ar->debug.stats.name ##_counter[i]); \ } \ \ return buf; \ } \ DEBUGFS_DECLARE_RO_FILE(name, 0); #define DEBUGFS_HW_REG_FILE(name, f) \ static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar, \ char *dum, size_t bufsize, ssize_t *ret) \ { \ char *buf; \ int i, max_len, err; \ \ max_len = ARRAY_SIZE(name##_regs) * 80; \ buf = vmalloc(max_len); \ if (!buf) \ return NULL; \ \ err = UPDATE_COUNTER(ar, name); \ if (err) { \ *ret = err; \ return buf; \ } \ \ for (i = 0; i < ARRAY_SIZE(name##_regs); i++) { \ ADD(buf, *ret, max_len, "%22s = %" f "\n", \ name##_regs[i].nreg, \ ar->debug.stats.name##_counter[i]); \ } \ \ return buf; \ } \ DEBUGFS_DECLARE_RO_FILE(name, 0); static ssize_t carl9170_debugfs_hw_ioread32_write(struct ar9170 *ar, const char *buf, size_t count) { int err = 0, i, n = 0, max_len = 32, res; unsigned int reg, tmp; if (!count) return 0; if (count > max_len) return -E2BIG; res = sscanf(buf, "0x%X %d", ®, &n); if (res < 1) { err = -EINVAL; goto out; } if (res == 1) n = 1; if (n > 15) { err = -EMSGSIZE; goto out; } if ((reg >= 0x280000) || ((reg + (n << 2)) >= 0x280000)) { err = -EADDRNOTAVAIL; goto out; } if (reg & 3) { err = -EINVAL; goto out; } for (i = 0; i < n; i++) { err = carl9170_read_reg(ar, reg + (i << 2), &tmp); if (err) goto out; ar->debug.ring[ar->debug.ring_tail].reg = reg + (i << 2); ar->debug.ring[ar->debug.ring_tail].value = tmp; ar->debug.ring_tail++; ar->debug.ring_tail %= CARL9170_DEBUG_RING_SIZE; } out: return err ? err : count; } static char *carl9170_debugfs_hw_ioread32_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *ret) { int i = 0; while (ar->debug.ring_head != ar->debug.ring_tail) { ADD(buf, *ret, bufsize, "%.8x = %.8x\n", ar->debug.ring[ar->debug.ring_head].reg, ar->debug.ring[ar->debug.ring_head].value); ar->debug.ring_head++; ar->debug.ring_head %= CARL9170_DEBUG_RING_SIZE; if (i++ == 64) break; } ar->debug.ring_head = ar->debug.ring_tail; return buf; } DEBUGFS_DECLARE_RW_FILE(hw_ioread32, CARL9170_DEBUG_RING_SIZE * 40); static ssize_t carl9170_debugfs_bug_write(struct ar9170 *ar, const char *buf, size_t count) { int err; if (count < 1) return -EINVAL; switch (buf[0]) { case 'F': ar->needs_full_reset = true; break; case 'R': if (!IS_STARTED(ar)) { err = -EAGAIN; goto out; } ar->needs_full_reset = false; break; case 'M': err = carl9170_mac_reset(ar); if (err < 0) count = err; goto out; case 'P': err = carl9170_set_channel(ar, ar->hw->conf.channel, ar->hw->conf.channel_type, CARL9170_RFI_COLD); if (err < 0) count = err; goto out; default: return -EINVAL; } carl9170_restart(ar, CARL9170_RR_USER_REQUEST); out: return count; } static char *carl9170_debugfs_bug_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *ret) { ADD(buf, *ret, bufsize, "[P]hy reinit, [R]estart, [F]ull usb reset, " "[M]ac reset\n"); ADD(buf, *ret, bufsize, "firmware restarts:%d, last reason:%d\n", ar->restart_counter, ar->last_reason); ADD(buf, *ret, bufsize, "phy reinit errors:%d (%d)\n", ar->total_chan_fail, ar->chan_fail); ADD(buf, *ret, bufsize, "reported firmware errors:%d\n", ar->fw.err_counter); ADD(buf, *ret, bufsize, "reported firmware BUGs:%d\n", ar->fw.bug_counter); ADD(buf, *ret, bufsize, "pending restart requests:%d\n", atomic_read(&ar->pending_restarts)); return buf; } __DEBUGFS_DECLARE_RW_FILE(bug, 400, CARL9170_STOPPED); static const char *const erp_modes[] = { [CARL9170_ERP_INVALID] = "INVALID", [CARL9170_ERP_AUTO] = "Automatic", [CARL9170_ERP_MAC80211] = "Set by MAC80211", [CARL9170_ERP_OFF] = "Force Off", [CARL9170_ERP_RTS] = "Force RTS", [CARL9170_ERP_CTS] = "Force CTS" }; static char *carl9170_debugfs_erp_read(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *ret) { ADD(buf, *ret, bufsize, "ERP Setting: (%d) -> %s\n", ar->erp_mode, erp_modes[ar->erp_mode]); return buf; } static ssize_t carl9170_debugfs_erp_write(struct ar9170 *ar, const char *buf, size_t count) { int res, val; if (count < 1) return -EINVAL; res = sscanf(buf, "%d", &val); if (res != 1) return -EINVAL; if (!((val > CARL9170_ERP_INVALID) && (val < __CARL9170_ERP_NUM))) return -EINVAL; ar->erp_mode = val; return count; } DEBUGFS_DECLARE_RW_FILE(erp, 80); static ssize_t carl9170_debugfs_hw_iowrite32_write(struct ar9170 *ar, const char *buf, size_t count) { int err = 0, max_len = 22, res; u32 reg, val; if (!count) return 0; if (count > max_len) return -E2BIG; res = sscanf(buf, "0x%X 0x%X", ®, &val); if (res != 2) { err = -EINVAL; goto out; } if (reg <= 0x100000 || reg >= 0x280000) { err = -EADDRNOTAVAIL; goto out; } if (reg & 3) { err = -EINVAL; goto out; } err = carl9170_write_reg(ar, reg, val); if (err) goto out; out: return err ? err : count; } DEBUGFS_DECLARE_WO_FILE(hw_iowrite32); DEBUGFS_HW_TALLY_FILE(hw_tx_tally, "u"); DEBUGFS_HW_TALLY_FILE(hw_rx_tally, "u"); DEBUGFS_HW_TALLY_FILE(hw_phy_errors, "u"); DEBUGFS_HW_REG_FILE(hw_wlan_queue, ".8x"); DEBUGFS_HW_REG_FILE(hw_pta_queue, ".8x"); DEBUGFS_HW_REG_FILE(hw_ampdu_info, ".8x"); DEBUGFS_QUEUE_DUMP(tx_status, 0); DEBUGFS_QUEUE_DUMP(tx_status, 1); DEBUGFS_QUEUE_DUMP(tx_status, 2); DEBUGFS_QUEUE_DUMP(tx_status, 3); DEBUGFS_QUEUE_DUMP(tx_pending, 0); DEBUGFS_QUEUE_DUMP(tx_pending, 1); DEBUGFS_QUEUE_DUMP(tx_pending, 2); DEBUGFS_QUEUE_DUMP(tx_pending, 3); DEBUGFS_READONLY_FILE(usb_tx_anch_urbs, 20, "%d", atomic_read(&ar->tx_anch_urbs)); DEBUGFS_READONLY_FILE(usb_rx_anch_urbs, 20, "%d", atomic_read(&ar->rx_anch_urbs)); DEBUGFS_READONLY_FILE(usb_rx_work_urbs, 20, "%d", atomic_read(&ar->rx_work_urbs)); DEBUGFS_READONLY_FILE(usb_rx_pool_urbs, 20, "%d", atomic_read(&ar->rx_pool_urbs)); DEBUGFS_READONLY_FILE(tx_total_queued, 20, "%d", atomic_read(&ar->tx_total_queued)); DEBUGFS_READONLY_FILE(tx_ampdu_scheduler, 20, "%d", atomic_read(&ar->tx_ampdu_scheduler)); DEBUGFS_READONLY_FILE(tx_total_pending, 20, "%d", atomic_read(&ar->tx_total_pending)); DEBUGFS_READONLY_FILE(tx_ampdu_list_len, 20, "%d", ar->tx_ampdu_list_len); DEBUGFS_READONLY_FILE(tx_ampdu_upload, 20, "%d", atomic_read(&ar->tx_ampdu_upload)); DEBUGFS_READONLY_FILE(tx_janitor_last_run, 64, "last run:%d ms ago", jiffies_to_msecs(jiffies - ar->tx_janitor_last_run)); DEBUGFS_READONLY_FILE(tx_dropped, 20, "%d", ar->tx_dropped); DEBUGFS_READONLY_FILE(rx_dropped, 20, "%d", ar->rx_dropped); DEBUGFS_READONLY_FILE(sniffer_enabled, 20, "%d", ar->sniffer_enabled); DEBUGFS_READONLY_FILE(rx_software_decryption, 20, "%d", ar->rx_software_decryption); DEBUGFS_READONLY_FILE(ampdu_factor, 20, "%d", ar->current_factor); DEBUGFS_READONLY_FILE(ampdu_density, 20, "%d", ar->current_density); DEBUGFS_READONLY_FILE(beacon_int, 20, "%d TU", ar->global_beacon_int); DEBUGFS_READONLY_FILE(pretbtt, 20, "%d TU", ar->global_pretbtt); void carl9170_debugfs_register(struct ar9170 *ar) { ar->debug_dir = debugfs_create_dir(KBUILD_MODNAME, ar->hw->wiphy->debugfsdir); #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, carl_debugfs_##name ##_ops.attr, \ ar->debug_dir, ar, \ &carl_debugfs_##name ## _ops.fops); DEBUGFS_ADD(usb_tx_anch_urbs); DEBUGFS_ADD(usb_rx_pool_urbs); DEBUGFS_ADD(usb_rx_anch_urbs); DEBUGFS_ADD(usb_rx_work_urbs); DEBUGFS_ADD(tx_total_queued); DEBUGFS_ADD(tx_total_pending); DEBUGFS_ADD(tx_dropped); DEBUGFS_ADD(tx_stuck); DEBUGFS_ADD(tx_ampdu_upload); DEBUGFS_ADD(tx_ampdu_scheduler); DEBUGFS_ADD(tx_ampdu_list_len); DEBUGFS_ADD(rx_dropped); DEBUGFS_ADD(sniffer_enabled); DEBUGFS_ADD(rx_software_decryption); DEBUGFS_ADD(mem_usage); DEBUGFS_ADD(qos_stat); DEBUGFS_ADD(sta_psm); DEBUGFS_ADD(ampdu_state); DEBUGFS_ADD(hw_tx_tally); DEBUGFS_ADD(hw_rx_tally); DEBUGFS_ADD(hw_phy_errors); DEBUGFS_ADD(phy_noise); DEBUGFS_ADD(hw_wlan_queue); DEBUGFS_ADD(hw_pta_queue); DEBUGFS_ADD(hw_ampdu_info); DEBUGFS_ADD(ampdu_density); DEBUGFS_ADD(ampdu_factor); DEBUGFS_ADD(tx_janitor_last_run); DEBUGFS_ADD(tx_status_0); DEBUGFS_ADD(tx_status_1); DEBUGFS_ADD(tx_status_2); DEBUGFS_ADD(tx_status_3); DEBUGFS_ADD(tx_pending_0); DEBUGFS_ADD(tx_pending_1); DEBUGFS_ADD(tx_pending_2); DEBUGFS_ADD(tx_pending_3); DEBUGFS_ADD(hw_ioread32); DEBUGFS_ADD(hw_iowrite32); DEBUGFS_ADD(bug); DEBUGFS_ADD(erp); DEBUGFS_ADD(vif_dump); DEBUGFS_ADD(beacon_int); DEBUGFS_ADD(pretbtt); #undef DEBUGFS_ADD } void carl9170_debugfs_unregister(struct ar9170 *ar) { debugfs_remove_recursive(ar->debug_dir); } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/cmd.h0000644000175000017500000001324212026211315023415 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * Basic HW register/memory/command access functions * * Copyright 2008, Johannes Berg * Copyright 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CMD_H #define __CMD_H #include "carl9170.h" /* basic HW access */ int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val); int carl9170_read_reg(struct ar9170 *ar, const u32 reg, u32 *val); int carl9170_read_mreg(struct ar9170 *ar, const int nregs, const u32 *regs, u32 *out); int carl9170_echo_test(struct ar9170 *ar, u32 v); int carl9170_reboot(struct ar9170 *ar); int carl9170_mac_reset(struct ar9170 *ar); int carl9170_powersave(struct ar9170 *ar, const bool power_on); int carl9170_collect_tally(struct ar9170 *ar); int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id, const u32 mode, const u32 addr, const u32 len); static inline int carl9170_flush_cab(struct ar9170 *ar, const unsigned int vif_id) { return carl9170_bcn_ctrl(ar, vif_id, CARL9170_BCN_CTRL_DRAIN, 0, 0); } static inline int carl9170_rx_filter(struct ar9170 *ar, const unsigned int _rx_filter) { __le32 rx_filter = cpu_to_le32(_rx_filter); return carl9170_exec_cmd(ar, CARL9170_CMD_RX_FILTER, sizeof(rx_filter), (u8 *)&rx_filter, 0, NULL); } struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar, const enum carl9170_cmd_oids cmd, const unsigned int len); /* * Macros to facilitate writing multiple registers in a single * write-combining USB command. Note that when the first group * fails the whole thing will fail without any others attempted, * but you won't know which write in the group failed. */ #define carl9170_regwrite_begin(ar) \ do { \ int __nreg = 0, __err = 0; \ struct ar9170 *__ar = ar; #define carl9170_regwrite(r, v) do { \ __ar->cmd_buf[2 * __nreg + 1] = cpu_to_le32(r); \ __ar->cmd_buf[2 * __nreg + 2] = cpu_to_le32(v); \ __nreg++; \ if ((__nreg >= PAYLOAD_MAX / 2)) { \ if (IS_ACCEPTING_CMD(__ar)) \ __err = carl9170_exec_cmd(__ar, \ CARL9170_CMD_WREG, 8 * __nreg, \ (u8 *) &__ar->cmd_buf[1], 0, NULL); \ else \ goto __regwrite_out; \ \ __nreg = 0; \ if (__err) \ goto __regwrite_out; \ } \ } while (0) #define carl9170_regwrite_finish() \ __regwrite_out : \ if (__err == 0 && __nreg) { \ if (IS_ACCEPTING_CMD(__ar)) \ __err = carl9170_exec_cmd(__ar, \ CARL9170_CMD_WREG, 8 * __nreg, \ (u8 *) &__ar->cmd_buf[1], 0, NULL); \ __nreg = 0; \ } #define carl9170_regwrite_result() \ __err; \ } while (0) #define carl9170_async_regwrite_get_buf() \ do { \ __nreg = 0; \ __cmd = carl9170_cmd_buf(__carl, CARL9170_CMD_WREG_ASYNC, \ CARL9170_MAX_CMD_PAYLOAD_LEN); \ if (__cmd == NULL) { \ __err = -ENOMEM; \ goto __async_regwrite_out; \ } \ } while (0) #define carl9170_async_regwrite_begin(carl) \ do { \ struct ar9170 *__carl = carl; \ struct carl9170_cmd *__cmd; \ unsigned int __nreg; \ int __err = 0; \ carl9170_async_regwrite_get_buf(); \ #define carl9170_async_regwrite_flush() \ do { \ if (__cmd == NULL || __nreg == 0) \ break; \ \ if (IS_ACCEPTING_CMD(__carl) && __nreg) { \ __cmd->hdr.len = 8 * __nreg; \ __err = __carl9170_exec_cmd(__carl, __cmd, true); \ __cmd = NULL; \ break; \ } \ goto __async_regwrite_out; \ } while (0) #define carl9170_async_regwrite(r, v) do { \ if (__cmd == NULL) \ carl9170_async_regwrite_get_buf(); \ __cmd->wreg.regs[__nreg].addr = cpu_to_le32(r); \ __cmd->wreg.regs[__nreg].val = cpu_to_le32(v); \ __nreg++; \ if ((__nreg >= PAYLOAD_MAX / 2)) \ carl9170_async_regwrite_flush(); \ } while (0) #define carl9170_async_regwrite_finish() do { \ __async_regwrite_out: \ if (__cmd != NULL && __err == 0) \ carl9170_async_regwrite_flush(); \ kfree(__cmd); \ } while (0) \ #define carl9170_async_regwrite_result() \ __err; \ } while (0) #endif /* __CMD_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/cmd.c0000644000175000017500000001330312026211315023406 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * Basic HW register/memory/command access functions * * Copyright 2008, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "carl9170.h" #include "cmd.h" int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val) { const __le32 buf[2] = { cpu_to_le32(reg), cpu_to_le32(val), }; int err; err = carl9170_exec_cmd(ar, CARL9170_CMD_WREG, sizeof(buf), (u8 *) buf, 0, NULL); if (err) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "writing reg %#x " "(val %#x) failed (%d)\n", reg, val, err); } } return err; } int carl9170_read_mreg(struct ar9170 *ar, const int nregs, const u32 *regs, u32 *out) { int i, err; __le32 *offs, *res; /* abuse "out" for the register offsets, must be same length */ offs = (__le32 *)out; for (i = 0; i < nregs; i++) offs[i] = cpu_to_le32(regs[i]); /* also use the same buffer for the input */ res = (__le32 *)out; err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG, 4 * nregs, (u8 *)offs, 4 * nregs, (u8 *)res); if (err) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "reading regs failed (%d)\n", err); } return err; } /* convert result to cpu endian */ for (i = 0; i < nregs; i++) out[i] = le32_to_cpu(res[i]); return 0; } int carl9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val) { return carl9170_read_mreg(ar, 1, ®, val); } int carl9170_echo_test(struct ar9170 *ar, const u32 v) { u32 echores; int err; err = carl9170_exec_cmd(ar, CARL9170_CMD_ECHO, 4, (u8 *)&v, 4, (u8 *)&echores); if (err) return err; if (v != echores) { wiphy_info(ar->hw->wiphy, "wrong echo %x != %x", v, echores); return -EINVAL; } return 0; } struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar, const enum carl9170_cmd_oids cmd, const unsigned int len) { struct carl9170_cmd *tmp; tmp = kzalloc(sizeof(struct carl9170_cmd_head) + len, GFP_ATOMIC); if (tmp) { tmp->hdr.cmd = cmd; tmp->hdr.len = len; } return tmp; } int carl9170_reboot(struct ar9170 *ar) { struct carl9170_cmd *cmd; int err; cmd = carl9170_cmd_buf(ar, CARL9170_CMD_REBOOT_ASYNC, 0); if (!cmd) return -ENOMEM; err = __carl9170_exec_cmd(ar, cmd, true); return err; } int carl9170_mac_reset(struct ar9170 *ar) { return carl9170_exec_cmd(ar, CARL9170_CMD_SWRST, 0, NULL, 0, NULL); } int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id, const u32 mode, const u32 addr, const u32 len) { struct carl9170_cmd *cmd; cmd = carl9170_cmd_buf(ar, CARL9170_CMD_BCN_CTRL_ASYNC, sizeof(struct carl9170_bcn_ctrl_cmd)); if (!cmd) return -ENOMEM; cmd->bcn_ctrl.vif_id = cpu_to_le32(vif_id); cmd->bcn_ctrl.mode = cpu_to_le32(mode); cmd->bcn_ctrl.bcn_addr = cpu_to_le32(addr); cmd->bcn_ctrl.bcn_len = cpu_to_le32(len); return __carl9170_exec_cmd(ar, cmd, true); } int carl9170_collect_tally(struct ar9170 *ar) { struct carl9170_tally_rsp tally; struct survey_info *info; unsigned int tick; int err; err = carl9170_exec_cmd(ar, CARL9170_CMD_TALLY, 0, NULL, sizeof(tally), (u8 *)&tally); if (err) return err; tick = le32_to_cpu(tally.tick); if (tick) { ar->tally.active += le32_to_cpu(tally.active) / tick; ar->tally.cca += le32_to_cpu(tally.cca) / tick; ar->tally.tx_time += le32_to_cpu(tally.tx_time) / tick; ar->tally.rx_total += le32_to_cpu(tally.rx_total); ar->tally.rx_overrun += le32_to_cpu(tally.rx_overrun); if (ar->channel) { info = &ar->survey[ar->channel->hw_value]; info->channel_time = ar->tally.active; info->channel_time_busy = ar->tally.cca; info->channel_time_tx = ar->tally.tx_time; do_div(info->channel_time, 1000); do_div(info->channel_time_busy, 1000); do_div(info->channel_time_tx, 1000); } } return 0; } int carl9170_powersave(struct ar9170 *ar, const bool ps) { struct carl9170_cmd *cmd; u32 state; cmd = carl9170_cmd_buf(ar, CARL9170_CMD_PSM_ASYNC, sizeof(struct carl9170_psm)); if (!cmd) return -ENOMEM; if (ps) { /* Sleep until next TBTT */ state = CARL9170_PSM_SLEEP | 1; } else { /* wake up immediately */ state = 1; } cmd->psm.state = cpu_to_le32(state); return __carl9170_exec_cmd(ar, cmd, true); } compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/carl9170.h0000644000175000017500000004374012026211315024122 0ustar mcgrofmcgrof/* * Atheros CARL9170 driver * * Driver specific definitions * * Copyright 2008, Johannes Berg * Copyright 2009, 2010, Christian Lamparter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, see * http://www.gnu.org/licenses/. * * This file incorporates work covered by the following copyright and * permission notice: * Copyright (c) 2007-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CARL9170_H #define __CARL9170_H #include #include #include #include #include #include #include #include #ifdef CONFIG_CARL9170_LEDS #include #endif /* CONFIG_CARL9170_LEDS */ #ifdef CONFIG_CARL9170_WPC #include #endif /* CONFIG_CARL9170_WPC */ #include "eeprom.h" #include "wlan.h" #include "hw.h" #include "fwdesc.h" #include "fwcmd.h" #include "../regd.h" #ifdef CONFIG_CARL9170_DEBUGFS #include "debug.h" #endif /* CONFIG_CARL9170_DEBUGFS */ #define CARL9170FW_NAME "carl9170-1.fw" #define PAYLOAD_MAX (CARL9170_MAX_CMD_LEN / 4 - 1) static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 3, 2, 1, 0 }; enum carl9170_rf_init_mode { CARL9170_RFI_NONE, CARL9170_RFI_WARM, CARL9170_RFI_COLD, }; #define CARL9170_MAX_RX_BUFFER_SIZE 8192 enum carl9170_device_state { CARL9170_UNKNOWN_STATE, CARL9170_STOPPED, CARL9170_IDLE, CARL9170_STARTED, }; #define CARL9170_NUM_TID 16 #define WME_BA_BMP_SIZE 64 #define CARL9170_TX_USER_RATE_TRIES 3 #define WME_AC_BE 2 #define WME_AC_BK 3 #define WME_AC_VI 1 #define WME_AC_VO 0 #define TID_TO_WME_AC(_tid) \ ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ WME_AC_VO) #define SEQ_DIFF(_start, _seq) \ (((_start) - (_seq)) & 0x0fff) #define SEQ_PREV(_seq) \ (((_seq) - 1) & 0x0fff) #define SEQ_NEXT(_seq) \ (((_seq) + 1) & 0x0fff) #define BAW_WITHIN(_start, _bawsz, _seqno) \ ((((_seqno) - (_start)) & 0xfff) < (_bawsz)) enum carl9170_tid_state { CARL9170_TID_STATE_INVALID, CARL9170_TID_STATE_KILLED, CARL9170_TID_STATE_SHUTDOWN, CARL9170_TID_STATE_SUSPEND, CARL9170_TID_STATE_PROGRESS, CARL9170_TID_STATE_IDLE, CARL9170_TID_STATE_XMIT, }; #define CARL9170_BAW_BITS (2 * WME_BA_BMP_SIZE) #define CARL9170_BAW_SIZE (BITS_TO_LONGS(CARL9170_BAW_BITS)) #define CARL9170_BAW_LEN (DIV_ROUND_UP(CARL9170_BAW_BITS, BITS_PER_BYTE)) struct carl9170_sta_tid { /* must be the first entry! */ struct list_head list; /* temporary list for RCU unlink procedure */ struct list_head tmp_list; /* lock for the following data structures */ spinlock_t lock; unsigned int counter; enum carl9170_tid_state state; u8 tid; /* TID number ( 0 - 15 ) */ u16 max; /* max. AMPDU size */ u16 snx; /* awaiting _next_ frame */ u16 hsn; /* highest _queued_ sequence */ u16 bsn; /* base of the tx/agg bitmap */ unsigned long bitmap[CARL9170_BAW_SIZE]; /* Preaggregation reorder queue */ struct sk_buff_head queue; }; #define CARL9170_QUEUE_TIMEOUT 256 #define CARL9170_BUMP_QUEUE 1000 #define CARL9170_TX_TIMEOUT 2500 #define CARL9170_JANITOR_DELAY 128 #define CARL9170_QUEUE_STUCK_TIMEOUT 5500 #define CARL9170_STAT_WORK 30000 #define CARL9170_NUM_TX_AGG_MAX 30 /* * Tradeoff between stability/latency and speed. * * AR9170_TXQ_DEPTH is devised by dividing the amount of available * tx buffers with the size of a full ethernet frame + overhead. * * Naturally: The higher the limit, the faster the device CAN send. * However, even a slight over-commitment at the wrong time and the * hardware is doomed to send all already-queued frames at suboptimal * rates. This in turn leads to an enormous amount of unsuccessful * retries => Latency goes up, whereas the throughput goes down. CRASH! */ #define CARL9170_NUM_TX_LIMIT_HARD ((AR9170_TXQ_DEPTH * 3) / 2) #define CARL9170_NUM_TX_LIMIT_SOFT (AR9170_TXQ_DEPTH) struct carl9170_tx_queue_stats { unsigned int count; unsigned int limit; unsigned int len; }; struct carl9170_vif { unsigned int id; struct ieee80211_vif __rcu *vif; }; struct carl9170_vif_info { struct list_head list; bool active; unsigned int id; struct sk_buff *beacon; bool enable_beacon; }; #define AR9170_NUM_RX_URBS 16 #define AR9170_NUM_RX_URBS_MUL 2 #define AR9170_NUM_TX_URBS 8 #define AR9170_NUM_RX_URBS_POOL (AR9170_NUM_RX_URBS_MUL * AR9170_NUM_RX_URBS) enum carl9170_device_features { CARL9170_WPS_BUTTON = BIT(0), CARL9170_ONE_LED = BIT(1), }; #ifdef CONFIG_CARL9170_LEDS struct ar9170; struct carl9170_led { struct ar9170 *ar; struct led_classdev l; char name[32]; unsigned int toggled; bool last_state; bool registered; }; #endif /* CONFIG_CARL9170_LEDS */ enum carl9170_restart_reasons { CARL9170_RR_NO_REASON = 0, CARL9170_RR_FATAL_FIRMWARE_ERROR, CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS, CARL9170_RR_WATCHDOG, CARL9170_RR_STUCK_TX, CARL9170_RR_UNRESPONSIVE_DEVICE, CARL9170_RR_COMMAND_TIMEOUT, CARL9170_RR_TOO_MANY_PHY_ERRORS, CARL9170_RR_LOST_RSP, CARL9170_RR_INVALID_RSP, CARL9170_RR_USER_REQUEST, __CARL9170_RR_LAST, }; enum carl9170_erp_modes { CARL9170_ERP_INVALID, CARL9170_ERP_AUTO, CARL9170_ERP_MAC80211, CARL9170_ERP_OFF, CARL9170_ERP_CTS, CARL9170_ERP_RTS, __CARL9170_ERP_NUM, }; struct ar9170 { struct ath_common common; struct ieee80211_hw *hw; struct mutex mutex; enum carl9170_device_state state; spinlock_t state_lock; enum carl9170_restart_reasons last_reason; bool registered; /* USB */ struct usb_device *udev; struct usb_interface *intf; struct usb_anchor rx_anch; struct usb_anchor rx_work; struct usb_anchor rx_pool; struct usb_anchor tx_wait; struct usb_anchor tx_anch; struct usb_anchor tx_cmd; struct usb_anchor tx_err; struct tasklet_struct usb_tasklet; atomic_t tx_cmd_urbs; atomic_t tx_anch_urbs; atomic_t rx_anch_urbs; atomic_t rx_work_urbs; atomic_t rx_pool_urbs; kernel_ulong_t features; /* firmware settings */ struct completion fw_load_wait; struct completion fw_boot_wait; struct { const struct carl9170fw_desc_head *desc; const struct firmware *fw; unsigned int offset; unsigned int address; unsigned int cmd_bufs; unsigned int api_version; unsigned int vif_num; unsigned int err_counter; unsigned int bug_counter; u32 beacon_addr; unsigned int beacon_max_len; bool rx_stream; bool tx_stream; bool rx_filter; bool hw_counters; unsigned int mem_blocks; unsigned int mem_block_size; unsigned int rx_size; unsigned int tx_seq_table; bool ba_filter; } fw; /* interface configuration combinations */ struct ieee80211_iface_limit if_comb_limits[1]; struct ieee80211_iface_combination if_combs[1]; /* reset / stuck frames/queue detection */ struct work_struct restart_work; struct work_struct ping_work; unsigned int restart_counter; unsigned long queue_stop_timeout[__AR9170_NUM_TXQ]; unsigned long max_queue_stop_timeout[__AR9170_NUM_TXQ]; bool needs_full_reset; atomic_t pending_restarts; /* interface mode settings */ struct list_head vif_list; unsigned long vif_bitmap; unsigned int vifs; struct carl9170_vif vif_priv[AR9170_MAX_VIRTUAL_MAC]; /* beaconing */ spinlock_t beacon_lock; unsigned int global_pretbtt; unsigned int global_beacon_int; struct carl9170_vif_info __rcu *beacon_iter; unsigned int beacon_enabled; /* cryptographic engine */ u64 usedkeys; bool rx_software_decryption; bool disable_offload; /* filter settings */ u64 cur_mc_hash; u32 cur_filter; unsigned int filter_state; unsigned int rx_filter_caps; bool sniffer_enabled; /* MAC */ enum carl9170_erp_modes erp_mode; /* PHY */ struct ieee80211_channel *channel; unsigned int num_channels; int noise[4]; unsigned int chan_fail; unsigned int total_chan_fail; u8 heavy_clip; u8 ht_settings; struct { u64 active; /* usec */ u64 cca; /* usec */ u64 tx_time; /* usec */ u64 rx_total; u64 rx_overrun; } tally; struct delayed_work stat_work; struct survey_info *survey; /* power calibration data */ u8 power_5G_leg[4]; u8 power_2G_cck[4]; u8 power_2G_ofdm[4]; u8 power_5G_ht20[8]; u8 power_5G_ht40[8]; u8 power_2G_ht20[8]; u8 power_2G_ht40[8]; #ifdef CONFIG_CARL9170_LEDS /* LED */ struct delayed_work led_work; struct carl9170_led leds[AR9170_NUM_LEDS]; #endif /* CONFIG_CARL9170_LEDS */ /* qos queue settings */ spinlock_t tx_stats_lock; struct carl9170_tx_queue_stats tx_stats[__AR9170_NUM_TXQ]; struct ieee80211_tx_queue_params edcf[5]; struct completion tx_flush; /* CMD */ int cmd_seq; int readlen; u8 *readbuf; spinlock_t cmd_lock; struct completion cmd_wait; union { __le32 cmd_buf[PAYLOAD_MAX + 1]; struct carl9170_cmd cmd; struct carl9170_rsp rsp; }; /* statistics */ unsigned int tx_dropped; unsigned int tx_ack_failures; unsigned int tx_fcs_errors; unsigned int rx_dropped; /* EEPROM */ struct ar9170_eeprom eeprom; /* tx queuing */ struct sk_buff_head tx_pending[__AR9170_NUM_TXQ]; struct sk_buff_head tx_status[__AR9170_NUM_TXQ]; struct delayed_work tx_janitor; unsigned long tx_janitor_last_run; bool tx_schedule; /* tx ampdu */ struct work_struct ampdu_work; spinlock_t tx_ampdu_list_lock; struct carl9170_sta_tid __rcu *tx_ampdu_iter; struct list_head tx_ampdu_list; atomic_t tx_ampdu_upload; atomic_t tx_ampdu_scheduler; atomic_t tx_total_pending; atomic_t tx_total_queued; unsigned int tx_ampdu_list_len; int current_density; int current_factor; bool tx_ampdu_schedule; /* internal memory management */ spinlock_t mem_lock; unsigned long *mem_bitmap; atomic_t mem_free_blocks; atomic_t mem_allocs; /* rxstream mpdu merge */ struct ar9170_rx_head rx_plcp; bool rx_has_plcp; struct sk_buff *rx_failover; int rx_failover_missing; u32 ampdu_ref; /* FIFO for collecting outstanding BlockAckRequest */ struct list_head bar_list[__AR9170_NUM_TXQ]; spinlock_t bar_list_lock[__AR9170_NUM_TXQ]; #ifdef CONFIG_CARL9170_WPC struct { bool pbc_state; struct input_dev *pbc; char name[32]; char phys[32]; } wps; #endif /* CONFIG_CARL9170_WPC */ #ifdef CONFIG_CARL9170_DEBUGFS struct carl9170_debug debug; struct dentry *debug_dir; #endif /* CONFIG_CARL9170_DEBUGFS */ /* PSM */ struct work_struct ps_work; struct { unsigned int dtim_counter; unsigned long last_beacon; unsigned long last_action; unsigned long last_slept; unsigned int sleep_ms; unsigned int off_override; bool state; } ps; #ifdef CONFIG_CARL9170_HWRNG # define CARL9170_HWRNG_CACHE_SIZE CARL9170_MAX_CMD_PAYLOAD_LEN struct { struct hwrng rng; bool initialized; char name[30 + 1]; u16 cache[CARL9170_HWRNG_CACHE_SIZE / sizeof(u16)]; unsigned int cache_idx; } rng; #endif /* CONFIG_CARL9170_HWRNG */ }; enum carl9170_ps_off_override_reasons { PS_OFF_VIF = BIT(0), PS_OFF_BCN = BIT(1), }; struct carl9170_bar_list_entry { struct list_head list; struct rcu_head head; struct sk_buff *skb; }; struct carl9170_ba_stats { u8 ampdu_len; u8 ampdu_ack_len; bool clear; bool req; }; struct carl9170_sta_info { bool ht_sta; bool sleeping; atomic_t pending_frames; unsigned int ampdu_max_len; struct carl9170_sta_tid __rcu *agg[CARL9170_NUM_TID]; struct carl9170_ba_stats stats[CARL9170_NUM_TID]; }; struct carl9170_tx_info { unsigned long timeout; struct ar9170 *ar; struct kref ref; }; #define CHK_DEV_STATE(a, s) (((struct ar9170 *)a)->state >= (s)) #define IS_INITIALIZED(a) (CHK_DEV_STATE(a, CARL9170_STOPPED)) #define IS_ACCEPTING_CMD(a) (CHK_DEV_STATE(a, CARL9170_IDLE)) #define IS_STARTED(a) (CHK_DEV_STATE(a, CARL9170_STARTED)) static inline void __carl9170_set_state(struct ar9170 *ar, enum carl9170_device_state newstate) { ar->state = newstate; } static inline void carl9170_set_state(struct ar9170 *ar, enum carl9170_device_state newstate) { unsigned long flags; spin_lock_irqsave(&ar->state_lock, flags); __carl9170_set_state(ar, newstate); spin_unlock_irqrestore(&ar->state_lock, flags); } static inline void carl9170_set_state_when(struct ar9170 *ar, enum carl9170_device_state min, enum carl9170_device_state newstate) { unsigned long flags; spin_lock_irqsave(&ar->state_lock, flags); if (CHK_DEV_STATE(ar, min)) __carl9170_set_state(ar, newstate); spin_unlock_irqrestore(&ar->state_lock, flags); } /* exported interface */ void *carl9170_alloc(size_t priv_size); int carl9170_register(struct ar9170 *ar); void carl9170_unregister(struct ar9170 *ar); void carl9170_free(struct ar9170 *ar); void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r); void carl9170_ps_check(struct ar9170 *ar); /* USB back-end */ int carl9170_usb_open(struct ar9170 *ar); void carl9170_usb_stop(struct ar9170 *ar); void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb); void carl9170_usb_handle_tx_err(struct ar9170 *ar); int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids, u32 plen, void *payload, u32 rlen, void *resp); int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd, const bool free_buf); int carl9170_usb_restart(struct ar9170 *ar); void carl9170_usb_reset(struct ar9170 *ar); /* MAC */ int carl9170_init_mac(struct ar9170 *ar); int carl9170_set_qos(struct ar9170 *ar); int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hast); int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id, const u8 *mac); int carl9170_set_operating_mode(struct ar9170 *ar); int carl9170_set_beacon_timers(struct ar9170 *ar); int carl9170_set_dyn_sifs_ack(struct ar9170 *ar); int carl9170_set_rts_cts_rate(struct ar9170 *ar); int carl9170_set_ampdu_settings(struct ar9170 *ar); int carl9170_set_slot_time(struct ar9170 *ar); int carl9170_set_mac_rates(struct ar9170 *ar); int carl9170_set_hwretry_limit(struct ar9170 *ar, const u32 max_retry); int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac, const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen); int carl9170_disable_key(struct ar9170 *ar, const u8 id); int carl9170_set_mac_tpc(struct ar9170 *ar, struct ieee80211_channel *channel); /* RX */ void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len); void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); /* TX */ void carl9170_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); void carl9170_tx_janitor(struct work_struct *work); void carl9170_tx_process_status(struct ar9170 *ar, const struct carl9170_rsp *cmd); void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb, const bool success); void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb); void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb); void carl9170_tx_scheduler(struct ar9170 *ar); void carl9170_tx_get_skb(struct sk_buff *skb); int carl9170_tx_put_skb(struct sk_buff *skb); int carl9170_update_beacon(struct ar9170 *ar, const bool submit); /* LEDs */ #ifdef CONFIG_CARL9170_LEDS int carl9170_led_register(struct ar9170 *ar); void carl9170_led_unregister(struct ar9170 *ar); #endif /* CONFIG_CARL9170_LEDS */ int carl9170_led_init(struct ar9170 *ar); int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state); /* PHY / RF */ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, enum nl80211_channel_type bw, enum carl9170_rf_init_mode rfi); int carl9170_get_noisefloor(struct ar9170 *ar); /* FW */ int carl9170_parse_firmware(struct ar9170 *ar); extern struct ieee80211_rate __carl9170_ratetable[]; extern int modparam_noht; static inline struct ar9170 *carl9170_get_priv(struct carl9170_vif *carl_vif) { return container_of(carl_vif, struct ar9170, vif_priv[carl_vif->id]); } static inline struct ieee80211_hdr *carl9170_get_hdr(struct sk_buff *skb) { return (void *)((struct _carl9170_tx_superframe *) skb->data)->frame_data; } static inline u16 get_seq_h(struct ieee80211_hdr *hdr) { return le16_to_cpu(hdr->seq_ctrl) >> 4; } static inline u16 carl9170_get_seq(struct sk_buff *skb) { return get_seq_h(carl9170_get_hdr(skb)); } static inline u16 get_tid_h(struct ieee80211_hdr *hdr) { return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; } static inline u16 carl9170_get_tid(struct sk_buff *skb) { return get_tid_h(carl9170_get_hdr(skb)); } static inline struct ieee80211_vif * carl9170_get_vif(struct carl9170_vif_info *priv) { return container_of((void *)priv, struct ieee80211_vif, drv_priv); } /* Protected by ar->mutex or RCU */ static inline struct ieee80211_vif *carl9170_get_main_vif(struct ar9170 *ar) { struct carl9170_vif_info *cvif; list_for_each_entry_rcu(cvif, &ar->vif_list, list) { if (cvif->active) return carl9170_get_vif(cvif); } return NULL; } static inline bool is_main_vif(struct ar9170 *ar, struct ieee80211_vif *vif) { bool ret; rcu_read_lock(); ret = (carl9170_get_main_vif(ar) == vif); rcu_read_unlock(); return ret; } #endif /* __CARL9170_H */ compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/Makefile0000644000175000017500000000023212026211315024134 0ustar mcgrofmcgrofcarl9170-objs := main.o usb.o cmd.o mac.o phy.o led.o fw.o tx.o rx.o carl9170-$(CONFIG_CARL9170_DEBUGFS) += debug.o obj-$(CONFIG_CARL9170) += carl9170.o compat-drivers-2012-09-18/drivers/net/wireless/ath/carl9170/Kconfig0000644000175000017500000000305712026211315024007 0ustar mcgrofmcgrofconfig CARL9170 tristate "Linux Community AR9170 802.11n USB support" depends on USB && MAC80211 && EXPERIMENTAL select FW_LOADER select CRC32 help This is another driver for the Atheros "otus" 802.11n USB devices. This driver provides more features than the original, but it needs a special firmware (carl9170-1.fw) to do that. The firmware can be downloaded from our wiki here: If you choose to build a module, it'll be called carl9170. config CARL9170_LEDS bool "SoftLED Support" depends on CARL9170 select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS default y help This option is necessary, if you want your device' LEDs to blink Say Y, unless you need the LEDs for firmware debugging. config CARL9170_DEBUGFS bool "DebugFS Support" depends on CARL9170 && DEBUG_FS && MAC80211_DEBUGFS default n help Export several driver and device internals to user space. Say N. config CARL9170_WPC bool depends on CARL9170 && (INPUT = y || INPUT = CARL9170) default y config CARL9170_HWRNG bool "Random number generator" depends on CARL9170 && (HW_RANDOM = y || HW_RANDOM = CARL9170) default n help Provides a hardware random number generator to the kernel. SECURITY WARNING: It's relatively easy to eavesdrop all generated random numbers from the transport stream with usbmon [software] or special usb sniffer hardware. Say N, unless your setup[i.e.: embedded system] has no other rng source and you can afford to take the risk. compat-drivers-2012-09-18/drivers/net/ethernet/0000755000175000017500000000000012026211315020442 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/ethernet/atheros/0000755000175000017500000000000012026211315022107 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/ethernet/atheros/Makefile0000644000175000017500000000025412026211315023550 0ustar mcgrofmcgrof# # Makefile for the Atheros network device drivers. # obj-$(CONFIG_ATL1) += atlx/ obj-$(CONFIG_ATL2) += atlx/ obj-$(CONFIG_ATL1E) += atl1e/ obj-$(CONFIG_ATL1C) += atl1c/ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/Kconfig0000644000175000017500000000345312026211315023417 0ustar mcgrofmcgrof# # Atheros device configuration # config NET_VENDOR_ATHEROS bool "Atheros devices" default y depends on PCI ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from . Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all the questions about Atheros devices. If you say Y, you will be asked for your specific card in the following questions. if NET_VENDOR_ATHEROS config ATL2 tristate "Atheros L2 Fast Ethernet support" depends on PCI select CRC32 select NET_CORE select MII ---help--- This driver supports the Atheros L2 fast ethernet adapter. To compile this driver as a module, choose M here. The module will be called atl2. config ATL1 tristate "Atheros/Attansic L1 Gigabit Ethernet support" depends on PCI select CRC32 select NET_CORE select MII ---help--- This driver supports the Atheros/Attansic L1 gigabit ethernet adapter. To compile this driver as a module, choose M here. The module will be called atl1. config ATL1E tristate "Atheros L1E Gigabit Ethernet support (EXPERIMENTAL)" depends on PCI && EXPERIMENTAL select CRC32 select NET_CORE select MII ---help--- This driver supports the Atheros L1E gigabit ethernet adapter. To compile this driver as a module, choose M here. The module will be called atl1e. config ATL1C tristate "Atheros L1C Gigabit Ethernet support (EXPERIMENTAL)" depends on PCI && EXPERIMENTAL select CRC32 select NET_CORE select MII ---help--- This driver supports the Atheros L1C gigabit ethernet adapter. To compile this driver as a module, choose M here. The module will be called atl1c. endif # NET_VENDOR_ATHEROS compat-drivers-2012-09-18/drivers/net/ethernet/atheros/alx/0000755000175000017500000000000012026211315022673 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/0000755000175000017500000000000012026211315023057 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/atl1.c0000644000175000017500000031133512026211315024072 0ustar mcgrofmcgrof/* * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2006 - 2007 Chris Snook * Copyright(c) 2006 - 2008 Jay Cliburn * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The full GNU General Public License is included in this distribution in the * file called COPYING. * * Contact Information: * Xiong Huang * Jie Yang * Chris Snook * Jay Cliburn * * This version is adapted from the Attansic reference driver. * * TODO: * Add more ethtool functions. * Fix abstruse irq enable/disable condition described here: * http://marc.theaimsgroup.com/?l=linux-netdev&m=116398508500553&w=2 * * NEEDS TESTING: * VLAN * multicast * promiscuous mode * interrupt coalescing * SMP torture testing */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atl1.h" #define ATLX_DRIVER_VERSION "2.1.3" MODULE_AUTHOR("Xiong Huang , " "Chris Snook , " "Jay Cliburn "); MODULE_LICENSE("GPL"); MODULE_VERSION(ATLX_DRIVER_VERSION); /* Temporary hack for merging atl1 and atl2 */ #include "atlx.c" static const struct ethtool_ops atl1_ethtool_ops; /* * This is the only thing that needs to be changed to adjust the * maximum number of ports that the driver can manage. */ #define ATL1_MAX_NIC 4 #define OPTION_UNSET -1 #define OPTION_DISABLED 0 #define OPTION_ENABLED 1 #define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET } /* * Interrupt Moderate Timer in units of 2 us * * Valid Range: 10-65535 * * Default Value: 100 (200us) */ static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT; static unsigned int num_int_mod_timer; module_param_array_named(int_mod_timer, int_mod_timer, int, &num_int_mod_timer, 0); MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer"); #define DEFAULT_INT_MOD_CNT 100 /* 200us */ #define MAX_INT_MOD_CNT 65000 #define MIN_INT_MOD_CNT 50 struct atl1_option { enum { enable_option, range_option, list_option } type; char *name; char *err; int def; union { struct { /* range_option info */ int min; int max; } r; struct { /* list_option info */ int nr; struct atl1_opt_list { int i; char *str; } *p; } l; } arg; }; static int __devinit atl1_validate_option(int *value, struct atl1_option *opt, struct pci_dev *pdev) { if (*value == OPTION_UNSET) { *value = opt->def; return 0; } switch (opt->type) { case enable_option: switch (*value) { case OPTION_ENABLED: dev_info(&pdev->dev, "%s enabled\n", opt->name); return 0; case OPTION_DISABLED: dev_info(&pdev->dev, "%s disabled\n", opt->name); return 0; } break; case range_option: if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { dev_info(&pdev->dev, "%s set to %i\n", opt->name, *value); return 0; } break; case list_option:{ int i; struct atl1_opt_list *ent; for (i = 0; i < opt->arg.l.nr; i++) { ent = &opt->arg.l.p[i]; if (*value == ent->i) { if (ent->str[0] != '\0') dev_info(&pdev->dev, "%s\n", ent->str); return 0; } } } break; default: break; } dev_info(&pdev->dev, "invalid %s specified (%i) %s\n", opt->name, *value, opt->err); *value = opt->def; return -1; } /** * atl1_check_options - Range Checking for Command Line Parameters * @adapter: board private structure * * This routine checks all command line parameters for valid user * input. If an invalid value is given, or if no user specified * value exists, a default value is used. The final value is stored * in a variable in the adapter structure. */ static void __devinit atl1_check_options(struct atl1_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; int bd = adapter->bd_number; if (bd >= ATL1_MAX_NIC) { dev_notice(&pdev->dev, "no configuration for board#%i\n", bd); dev_notice(&pdev->dev, "using defaults for all values\n"); } { /* Interrupt Moderate Timer */ struct atl1_option opt = { .type = range_option, .name = "Interrupt Moderator Timer", .err = "using default of " __MODULE_STRING(DEFAULT_INT_MOD_CNT), .def = DEFAULT_INT_MOD_CNT, .arg = {.r = {.min = MIN_INT_MOD_CNT, .max = MAX_INT_MOD_CNT} } }; int val; if (num_int_mod_timer > bd) { val = int_mod_timer[bd]; atl1_validate_option(&val, &opt, pdev); adapter->imt = (u16) val; } else adapter->imt = (u16) (opt.def); } } /* * atl1_pci_tbl - PCI Device ID Table */ static DEFINE_PCI_DEVICE_TABLE(atl1_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1)}, /* required last entry */ {0,} }; MODULE_DEVICE_TABLE(pci, atl1_pci_tbl); static const u32 atl1_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP; static int debug = -1; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Message level (0=none,...,16=all)"); /* * Reset the transmit and receive units; mask and clear all interrupts. * hw - Struct containing variables accessed by shared code * return : 0 or idle status (if error) */ static s32 atl1_reset_hw(struct atl1_hw *hw) { struct pci_dev *pdev = hw->back->pdev; struct atl1_adapter *adapter = hw->back; u32 icr; int i; /* * Clear Interrupt mask to stop board from generating * interrupts & Clear any pending interrupt events */ /* * atlx_irq_disable(adapter); * iowrite32(0xffffffff, hw->hw_addr + REG_ISR); */ /* * Issue Soft Reset to the MAC. This will reset the chip's * transmit, receive, DMA. It will not effect * the current PCI configuration. The global reset bit is self- * clearing, and should clear within a microsecond. */ iowrite32(MASTER_CTRL_SOFT_RST, hw->hw_addr + REG_MASTER_CTRL); ioread32(hw->hw_addr + REG_MASTER_CTRL); iowrite16(1, hw->hw_addr + REG_PHY_ENABLE); ioread16(hw->hw_addr + REG_PHY_ENABLE); /* delay about 1ms */ msleep(1); /* Wait at least 10ms for All module to be Idle */ for (i = 0; i < 10; i++) { icr = ioread32(hw->hw_addr + REG_IDLE_STATUS); if (!icr) break; /* delay 1 ms */ msleep(1); /* FIXME: still the right way to do this? */ cpu_relax(); } if (icr) { if (netif_msg_hw(adapter)) dev_dbg(&pdev->dev, "ICR = 0x%x\n", icr); return icr; } return 0; } /* function about EEPROM * * check_eeprom_exist * return 0 if eeprom exist */ static int atl1_check_eeprom_exist(struct atl1_hw *hw) { u32 value; value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); if (value & SPI_FLASH_CTRL_EN_VPD) { value &= ~SPI_FLASH_CTRL_EN_VPD; iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); } value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST); return ((value & 0xFF00) == 0x6C00) ? 0 : 1; } static bool atl1_read_eeprom(struct atl1_hw *hw, u32 offset, u32 *p_value) { int i; u32 control; if (offset & 3) /* address do not align */ return false; iowrite32(0, hw->hw_addr + REG_VPD_DATA); control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; iowrite32(control, hw->hw_addr + REG_VPD_CAP); ioread32(hw->hw_addr + REG_VPD_CAP); for (i = 0; i < 10; i++) { msleep(2); control = ioread32(hw->hw_addr + REG_VPD_CAP); if (control & VPD_CAP_VPD_FLAG) break; } if (control & VPD_CAP_VPD_FLAG) { *p_value = ioread32(hw->hw_addr + REG_VPD_DATA); return true; } /* timeout */ return false; } /* * Reads the value from a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to read */ static s32 atl1_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data) { u32 val; int i; val = ((u32) (reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; iowrite32(val, hw->hw_addr + REG_MDIO_CTRL); ioread32(hw->hw_addr + REG_MDIO_CTRL); for (i = 0; i < MDIO_WAIT_TIMES; i++) { udelay(2); val = ioread32(hw->hw_addr + REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; } if (!(val & (MDIO_START | MDIO_BUSY))) { *phy_data = (u16) val; return 0; } return ATLX_ERR_PHY; } #define CUSTOM_SPI_CS_SETUP 2 #define CUSTOM_SPI_CLK_HI 2 #define CUSTOM_SPI_CLK_LO 2 #define CUSTOM_SPI_CS_HOLD 2 #define CUSTOM_SPI_CS_HI 3 static bool atl1_spi_read(struct atl1_hw *hw, u32 addr, u32 *buf) { int i; u32 value; iowrite32(0, hw->hw_addr + REG_SPI_DATA); iowrite32(addr, hw->hw_addr + REG_SPI_ADDR); value = SPI_FLASH_CTRL_WAIT_READY | (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) << SPI_FLASH_CTRL_CS_SETUP_SHIFT | (CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) << SPI_FLASH_CTRL_CLK_HI_SHIFT | (CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) << SPI_FLASH_CTRL_CLK_LO_SHIFT | (CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) << SPI_FLASH_CTRL_CS_HOLD_SHIFT | (CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) << SPI_FLASH_CTRL_CS_HI_SHIFT | (1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT; iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); value |= SPI_FLASH_CTRL_START; iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); for (i = 0; i < 10; i++) { msleep(1); value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); if (!(value & SPI_FLASH_CTRL_START)) break; } if (value & SPI_FLASH_CTRL_START) return false; *buf = ioread32(hw->hw_addr + REG_SPI_DATA); return true; } /* * get_permanent_address * return 0 if get valid mac address, */ static int atl1_get_permanent_address(struct atl1_hw *hw) { u32 addr[2]; u32 i, control; u16 reg; u8 eth_addr[ETH_ALEN]; bool key_valid; if (is_valid_ether_addr(hw->perm_mac_addr)) return 0; /* init */ addr[0] = addr[1] = 0; if (!atl1_check_eeprom_exist(hw)) { reg = 0; key_valid = false; /* Read out all EEPROM content */ i = 0; while (1) { if (atl1_read_eeprom(hw, i + 0x100, &control)) { if (key_valid) { if (reg == REG_MAC_STA_ADDR) addr[0] = control; else if (reg == (REG_MAC_STA_ADDR + 4)) addr[1] = control; key_valid = false; } else if ((control & 0xff) == 0x5A) { key_valid = true; reg = (u16) (control >> 16); } else break; } else /* read error */ break; i += 4; } *(u32 *) ð_addr[2] = swab32(addr[0]); *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); if (is_valid_ether_addr(eth_addr)) { memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); return 0; } } /* see if SPI FLAGS exist ? */ addr[0] = addr[1] = 0; reg = 0; key_valid = false; i = 0; while (1) { if (atl1_spi_read(hw, i + 0x1f000, &control)) { if (key_valid) { if (reg == REG_MAC_STA_ADDR) addr[0] = control; else if (reg == (REG_MAC_STA_ADDR + 4)) addr[1] = control; key_valid = false; } else if ((control & 0xff) == 0x5A) { key_valid = true; reg = (u16) (control >> 16); } else /* data end */ break; } else /* read error */ break; i += 4; } *(u32 *) ð_addr[2] = swab32(addr[0]); *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); if (is_valid_ether_addr(eth_addr)) { memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); return 0; } /* * On some motherboards, the MAC address is written by the * BIOS directly to the MAC register during POST, and is * not stored in eeprom. If all else thus far has failed * to fetch the permanent MAC address, try reading it directly. */ addr[0] = ioread32(hw->hw_addr + REG_MAC_STA_ADDR); addr[1] = ioread16(hw->hw_addr + (REG_MAC_STA_ADDR + 4)); *(u32 *) ð_addr[2] = swab32(addr[0]); *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); if (is_valid_ether_addr(eth_addr)) { memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); return 0; } return 1; } /* * Reads the adapter's MAC address from the EEPROM * hw - Struct containing variables accessed by shared code */ static s32 atl1_read_mac_addr(struct atl1_hw *hw) { s32 ret = 0; u16 i; if (atl1_get_permanent_address(hw)) { eth_random_addr(hw->perm_mac_addr); ret = 1; } for (i = 0; i < ETH_ALEN; i++) hw->mac_addr[i] = hw->perm_mac_addr[i]; return ret; } /* * Hashes an address to determine its location in the multicast table * hw - Struct containing variables accessed by shared code * mc_addr - the multicast address to hash * * atl1_hash_mc_addr * purpose * set hash value for a multicast address * hash calcu processing : * 1. calcu 32bit CRC for multicast address * 2. reverse crc with MSB to LSB */ static u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr) { u32 crc32, value = 0; int i; crc32 = ether_crc_le(6, mc_addr); for (i = 0; i < 32; i++) value |= (((crc32 >> i) & 1) << (31 - i)); return value; } /* * Sets the bit in the multicast table corresponding to the hash value. * hw - Struct containing variables accessed by shared code * hash_value - Multicast address hash value */ static void atl1_hash_set(struct atl1_hw *hw, u32 hash_value) { u32 hash_bit, hash_reg; u32 mta; /* * The HASH Table is a register array of 2 32-bit registers. * It is treated like an array of 64 bits. We want to set * bit BitArray[hash_value]. So we figure out what register * the bit is in, read it, OR in the new bit, then write * back the new value. The register is determined by the * upper 7 bits of the hash value and the bit within that * register are determined by the lower 5 bits of the value. */ hash_reg = (hash_value >> 31) & 0x1; hash_bit = (hash_value >> 26) & 0x1F; mta = ioread32((hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2)); mta |= (1 << hash_bit); iowrite32(mta, (hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2)); } /* * Writes a value to a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to write * data - data to write to the PHY */ static s32 atl1_write_phy_reg(struct atl1_hw *hw, u32 reg_addr, u16 phy_data) { int i; u32 val; val = ((u32) (phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | MDIO_SUP_PREAMBLE | MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; iowrite32(val, hw->hw_addr + REG_MDIO_CTRL); ioread32(hw->hw_addr + REG_MDIO_CTRL); for (i = 0; i < MDIO_WAIT_TIMES; i++) { udelay(2); val = ioread32(hw->hw_addr + REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; } if (!(val & (MDIO_START | MDIO_BUSY))) return 0; return ATLX_ERR_PHY; } /* * Make L001's PHY out of Power Saving State (bug) * hw - Struct containing variables accessed by shared code * when power on, L001's PHY always on Power saving State * (Gigabit Link forbidden) */ static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw) { s32 ret; ret = atl1_write_phy_reg(hw, 29, 0x0029); if (ret) return ret; return atl1_write_phy_reg(hw, 30, 0); } /* * Resets the PHY and make all config validate * hw - Struct containing variables accessed by shared code * * Sets bit 15 and 12 of the MII Control regiser (for F001 bug) */ static s32 atl1_phy_reset(struct atl1_hw *hw) { struct pci_dev *pdev = hw->back->pdev; struct atl1_adapter *adapter = hw->back; s32 ret_val; u16 phy_data; if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; else { switch (hw->media_type) { case MEDIA_TYPE_100M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_100M_HALF: phy_data = MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_10M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; break; default: /* MEDIA_TYPE_10M_HALF: */ phy_data = MII_CR_SPEED_10 | MII_CR_RESET; break; } } ret_val = atl1_write_phy_reg(hw, MII_BMCR, phy_data); if (ret_val) { u32 val; int i; /* pcie serdes link may be down! */ if (netif_msg_hw(adapter)) dev_dbg(&pdev->dev, "pcie phy link down\n"); for (i = 0; i < 25; i++) { msleep(1); val = ioread32(hw->hw_addr + REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; } if ((val & (MDIO_START | MDIO_BUSY)) != 0) { if (netif_msg_hw(adapter)) dev_warn(&pdev->dev, "pcie link down at least 25ms\n"); return ret_val; } } return 0; } /* * Configures PHY autoneg and flow control advertisement settings * hw - Struct containing variables accessed by shared code */ static s32 atl1_phy_setup_autoneg_adv(struct atl1_hw *hw) { s32 ret_val; s16 mii_autoneg_adv_reg; s16 mii_1000t_ctrl_reg; /* Read the MII Auto-Neg Advertisement Register (Address 4). */ mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; /* Read the MII 1000Base-T Control Register (Address 9). */ mii_1000t_ctrl_reg = MII_ATLX_CR_1000T_DEFAULT_CAP_MASK; /* * First we clear all the 10/100 mb speed bits in the Auto-Neg * Advertisement Register (Address 4) and the 1000 mb speed bits in * the 1000Base-T Control Register (Address 9). */ mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK; mii_1000t_ctrl_reg &= ~MII_ATLX_CR_1000T_SPEED_MASK; /* * Need to parse media_type and set up * the appropriate PHY registers. */ switch (hw->media_type) { case MEDIA_TYPE_AUTO_SENSOR: mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS | MII_AR_10T_FD_CAPS | MII_AR_100TX_HD_CAPS | MII_AR_100TX_FD_CAPS); mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS; break; case MEDIA_TYPE_1000M_FULL: mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS; break; case MEDIA_TYPE_100M_FULL: mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; break; case MEDIA_TYPE_100M_HALF: mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; break; case MEDIA_TYPE_10M_FULL: mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; break; default: mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; break; } /* flow control fixed to enable all */ mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg; ret_val = atl1_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); if (ret_val) return ret_val; ret_val = atl1_write_phy_reg(hw, MII_ATLX_CR, mii_1000t_ctrl_reg); if (ret_val) return ret_val; return 0; } /* * Configures link settings. * hw - Struct containing variables accessed by shared code * Assumes the hardware has previously been reset and the * transmitter and receiver are not enabled. */ static s32 atl1_setup_link(struct atl1_hw *hw) { struct pci_dev *pdev = hw->back->pdev; struct atl1_adapter *adapter = hw->back; s32 ret_val; /* * Options: * PHY will advertise value(s) parsed from * autoneg_advertised and fc * no matter what autoneg is , We will not wait link result. */ ret_val = atl1_phy_setup_autoneg_adv(hw); if (ret_val) { if (netif_msg_link(adapter)) dev_dbg(&pdev->dev, "error setting up autonegotiation\n"); return ret_val; } /* SW.Reset , En-Auto-Neg if needed */ ret_val = atl1_phy_reset(hw); if (ret_val) { if (netif_msg_link(adapter)) dev_dbg(&pdev->dev, "error resetting phy\n"); return ret_val; } hw->phy_configured = true; return ret_val; } static void atl1_init_flash_opcode(struct atl1_hw *hw) { if (hw->flash_vendor >= ARRAY_SIZE(flash_table)) /* Atmel */ hw->flash_vendor = 0; /* Init OP table */ iowrite8(flash_table[hw->flash_vendor].cmd_program, hw->hw_addr + REG_SPI_FLASH_OP_PROGRAM); iowrite8(flash_table[hw->flash_vendor].cmd_sector_erase, hw->hw_addr + REG_SPI_FLASH_OP_SC_ERASE); iowrite8(flash_table[hw->flash_vendor].cmd_chip_erase, hw->hw_addr + REG_SPI_FLASH_OP_CHIP_ERASE); iowrite8(flash_table[hw->flash_vendor].cmd_rdid, hw->hw_addr + REG_SPI_FLASH_OP_RDID); iowrite8(flash_table[hw->flash_vendor].cmd_wren, hw->hw_addr + REG_SPI_FLASH_OP_WREN); iowrite8(flash_table[hw->flash_vendor].cmd_rdsr, hw->hw_addr + REG_SPI_FLASH_OP_RDSR); iowrite8(flash_table[hw->flash_vendor].cmd_wrsr, hw->hw_addr + REG_SPI_FLASH_OP_WRSR); iowrite8(flash_table[hw->flash_vendor].cmd_read, hw->hw_addr + REG_SPI_FLASH_OP_READ); } /* * Performs basic configuration of the adapter. * hw - Struct containing variables accessed by shared code * Assumes that the controller has previously been reset and is in a * post-reset uninitialized state. Initializes multicast table, * and Calls routines to setup link * Leaves the transmit and receive units disabled and uninitialized. */ static s32 atl1_init_hw(struct atl1_hw *hw) { u32 ret_val = 0; /* Zero out the Multicast HASH table */ iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE); /* clear the old settings from the multicast hash table */ iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2)); atl1_init_flash_opcode(hw); if (!hw->phy_configured) { /* enable GPHY LinkChange Interrupt */ ret_val = atl1_write_phy_reg(hw, 18, 0xC00); if (ret_val) return ret_val; /* make PHY out of power-saving state */ ret_val = atl1_phy_leave_power_saving(hw); if (ret_val) return ret_val; /* Call a subroutine to configure the link */ ret_val = atl1_setup_link(hw); } return ret_val; } /* * Detects the current speed and duplex settings of the hardware. * hw - Struct containing variables accessed by shared code * speed - Speed of the connection * duplex - Duplex setting of the connection */ static s32 atl1_get_speed_and_duplex(struct atl1_hw *hw, u16 *speed, u16 *duplex) { struct pci_dev *pdev = hw->back->pdev; struct atl1_adapter *adapter = hw->back; s32 ret_val; u16 phy_data; /* ; --- Read PHY Specific Status Register (17) */ ret_val = atl1_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data); if (ret_val) return ret_val; if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED)) return ATLX_ERR_PHY_RES; switch (phy_data & MII_ATLX_PSSR_SPEED) { case MII_ATLX_PSSR_1000MBS: *speed = SPEED_1000; break; case MII_ATLX_PSSR_100MBS: *speed = SPEED_100; break; case MII_ATLX_PSSR_10MBS: *speed = SPEED_10; break; default: if (netif_msg_hw(adapter)) dev_dbg(&pdev->dev, "error getting speed\n"); return ATLX_ERR_PHY_SPEED; break; } if (phy_data & MII_ATLX_PSSR_DPLX) *duplex = FULL_DUPLEX; else *duplex = HALF_DUPLEX; return 0; } static void atl1_set_mac_addr(struct atl1_hw *hw) { u32 value; /* * 00-0B-6A-F6-00-DC * 0: 6AF600DC 1: 000B * low dword */ value = (((u32) hw->mac_addr[2]) << 24) | (((u32) hw->mac_addr[3]) << 16) | (((u32) hw->mac_addr[4]) << 8) | (((u32) hw->mac_addr[5])); iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR); /* high dword */ value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1])); iowrite32(value, (hw->hw_addr + REG_MAC_STA_ADDR) + (1 << 2)); } /** * atl1_sw_init - Initialize general software structures (struct atl1_adapter) * @adapter: board private structure to initialize * * atl1_sw_init initializes the Adapter private data structure. * Fields are initialized based on PCI device information and * OS network device settings (MTU size). */ static int __devinit atl1_sw_init(struct atl1_adapter *adapter) { struct atl1_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; hw->min_frame_size = ETH_ZLEN + ETH_FCS_LEN; adapter->wol = 0; device_set_wakeup_enable(&adapter->pdev->dev, false); adapter->rx_buffer_len = (hw->max_frame_size + 7) & ~7; adapter->ict = 50000; /* 100ms */ adapter->link_speed = SPEED_0; /* hardware init */ adapter->link_duplex = FULL_DUPLEX; hw->phy_configured = false; hw->preamble_len = 7; hw->ipgt = 0x60; hw->min_ifg = 0x50; hw->ipgr1 = 0x40; hw->ipgr2 = 0x60; hw->max_retry = 0xf; hw->lcol = 0x37; hw->jam_ipg = 7; hw->rfd_burst = 8; hw->rrd_burst = 8; hw->rfd_fetch_gap = 1; hw->rx_jumbo_th = adapter->rx_buffer_len / 8; hw->rx_jumbo_lkah = 1; hw->rrd_ret_timer = 16; hw->tpd_burst = 4; hw->tpd_fetch_th = 16; hw->txf_burst = 0x100; hw->tx_jumbo_task_th = (hw->max_frame_size + 7) >> 3; hw->tpd_fetch_gap = 1; hw->rcb_value = atl1_rcb_64; hw->dma_ord = atl1_dma_ord_enh; hw->dmar_block = atl1_dma_req_256; hw->dmaw_block = atl1_dma_req_256; hw->cmb_rrd = 4; hw->cmb_tpd = 4; hw->cmb_rx_timer = 1; /* about 2us */ hw->cmb_tx_timer = 1; /* about 2us */ hw->smb_timer = 100000; /* about 200ms */ spin_lock_init(&adapter->lock); spin_lock_init(&adapter->mb_lock); return 0; } static int mdio_read(struct net_device *netdev, int phy_id, int reg_num) { struct atl1_adapter *adapter = netdev_priv(netdev); u16 result; atl1_read_phy_reg(&adapter->hw, reg_num & 0x1f, &result); return result; } static void mdio_write(struct net_device *netdev, int phy_id, int reg_num, int val) { struct atl1_adapter *adapter = netdev_priv(netdev); atl1_write_phy_reg(&adapter->hw, reg_num, val); } static int atl1_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct atl1_adapter *adapter = netdev_priv(netdev); unsigned long flags; int retval; if (!netif_running(netdev)) return -EINVAL; spin_lock_irqsave(&adapter->lock, flags); retval = generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL); spin_unlock_irqrestore(&adapter->lock, flags); return retval; } /** * atl1_setup_mem_resources - allocate Tx / RX descriptor resources * @adapter: board private structure * * Return 0 on success, negative on failure */ static s32 atl1_setup_ring_resources(struct atl1_adapter *adapter) { struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1_ring_header *ring_header = &adapter->ring_header; struct pci_dev *pdev = adapter->pdev; int size; u8 offset = 0; size = sizeof(struct atl1_buffer) * (tpd_ring->count + rfd_ring->count); tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL); if (unlikely(!tpd_ring->buffer_info)) { if (netif_msg_drv(adapter)) dev_err(&pdev->dev, "kzalloc failed , size = D%d\n", size); goto err_nomem; } rfd_ring->buffer_info = (tpd_ring->buffer_info + tpd_ring->count); /* * real ring DMA buffer * each ring/block may need up to 8 bytes for alignment, hence the * additional 40 bytes tacked onto the end. */ ring_header->size = size = sizeof(struct tx_packet_desc) * tpd_ring->count + sizeof(struct rx_free_desc) * rfd_ring->count + sizeof(struct rx_return_desc) * rrd_ring->count + sizeof(struct coals_msg_block) + sizeof(struct stats_msg_block) + 40; ring_header->desc = pci_alloc_consistent(pdev, ring_header->size, &ring_header->dma); if (unlikely(!ring_header->desc)) { if (netif_msg_drv(adapter)) dev_err(&pdev->dev, "pci_alloc_consistent failed\n"); goto err_nomem; } memset(ring_header->desc, 0, ring_header->size); /* init TPD ring */ tpd_ring->dma = ring_header->dma; offset = (tpd_ring->dma & 0x7) ? (8 - (ring_header->dma & 0x7)) : 0; tpd_ring->dma += offset; tpd_ring->desc = (u8 *) ring_header->desc + offset; tpd_ring->size = sizeof(struct tx_packet_desc) * tpd_ring->count; /* init RFD ring */ rfd_ring->dma = tpd_ring->dma + tpd_ring->size; offset = (rfd_ring->dma & 0x7) ? (8 - (rfd_ring->dma & 0x7)) : 0; rfd_ring->dma += offset; rfd_ring->desc = (u8 *) tpd_ring->desc + (tpd_ring->size + offset); rfd_ring->size = sizeof(struct rx_free_desc) * rfd_ring->count; /* init RRD ring */ rrd_ring->dma = rfd_ring->dma + rfd_ring->size; offset = (rrd_ring->dma & 0x7) ? (8 - (rrd_ring->dma & 0x7)) : 0; rrd_ring->dma += offset; rrd_ring->desc = (u8 *) rfd_ring->desc + (rfd_ring->size + offset); rrd_ring->size = sizeof(struct rx_return_desc) * rrd_ring->count; /* init CMB */ adapter->cmb.dma = rrd_ring->dma + rrd_ring->size; offset = (adapter->cmb.dma & 0x7) ? (8 - (adapter->cmb.dma & 0x7)) : 0; adapter->cmb.dma += offset; adapter->cmb.cmb = (struct coals_msg_block *) ((u8 *) rrd_ring->desc + (rrd_ring->size + offset)); /* init SMB */ adapter->smb.dma = adapter->cmb.dma + sizeof(struct coals_msg_block); offset = (adapter->smb.dma & 0x7) ? (8 - (adapter->smb.dma & 0x7)) : 0; adapter->smb.dma += offset; adapter->smb.smb = (struct stats_msg_block *) ((u8 *) adapter->cmb.cmb + (sizeof(struct coals_msg_block) + offset)); return 0; err_nomem: kfree(tpd_ring->buffer_info); return -ENOMEM; } static void atl1_init_ring_ptrs(struct atl1_adapter *adapter) { struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; atomic_set(&tpd_ring->next_to_use, 0); atomic_set(&tpd_ring->next_to_clean, 0); rfd_ring->next_to_clean = 0; atomic_set(&rfd_ring->next_to_use, 0); rrd_ring->next_to_use = 0; atomic_set(&rrd_ring->next_to_clean, 0); } /** * atl1_clean_rx_ring - Free RFD Buffers * @adapter: board private structure */ static void atl1_clean_rx_ring(struct atl1_adapter *adapter) { struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1_buffer *buffer_info; struct pci_dev *pdev = adapter->pdev; unsigned long size; unsigned int i; /* Free all the Rx ring sk_buffs */ for (i = 0; i < rfd_ring->count; i++) { buffer_info = &rfd_ring->buffer_info[i]; if (buffer_info->dma) { pci_unmap_page(pdev, buffer_info->dma, buffer_info->length, PCI_DMA_FROMDEVICE); buffer_info->dma = 0; } if (buffer_info->skb) { dev_kfree_skb(buffer_info->skb); buffer_info->skb = NULL; } } size = sizeof(struct atl1_buffer) * rfd_ring->count; memset(rfd_ring->buffer_info, 0, size); /* Zero out the descriptor ring */ memset(rfd_ring->desc, 0, rfd_ring->size); rfd_ring->next_to_clean = 0; atomic_set(&rfd_ring->next_to_use, 0); rrd_ring->next_to_use = 0; atomic_set(&rrd_ring->next_to_clean, 0); } /** * atl1_clean_tx_ring - Free Tx Buffers * @adapter: board private structure */ static void atl1_clean_tx_ring(struct atl1_adapter *adapter) { struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; struct atl1_buffer *buffer_info; struct pci_dev *pdev = adapter->pdev; unsigned long size; unsigned int i; /* Free all the Tx ring sk_buffs */ for (i = 0; i < tpd_ring->count; i++) { buffer_info = &tpd_ring->buffer_info[i]; if (buffer_info->dma) { pci_unmap_page(pdev, buffer_info->dma, buffer_info->length, PCI_DMA_TODEVICE); buffer_info->dma = 0; } } for (i = 0; i < tpd_ring->count; i++) { buffer_info = &tpd_ring->buffer_info[i]; if (buffer_info->skb) { dev_kfree_skb_any(buffer_info->skb); buffer_info->skb = NULL; } } size = sizeof(struct atl1_buffer) * tpd_ring->count; memset(tpd_ring->buffer_info, 0, size); /* Zero out the descriptor ring */ memset(tpd_ring->desc, 0, tpd_ring->size); atomic_set(&tpd_ring->next_to_use, 0); atomic_set(&tpd_ring->next_to_clean, 0); } /** * atl1_free_ring_resources - Free Tx / RX descriptor Resources * @adapter: board private structure * * Free all transmit software resources */ static void atl1_free_ring_resources(struct atl1_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1_ring_header *ring_header = &adapter->ring_header; atl1_clean_tx_ring(adapter); atl1_clean_rx_ring(adapter); kfree(tpd_ring->buffer_info); pci_free_consistent(pdev, ring_header->size, ring_header->desc, ring_header->dma); tpd_ring->buffer_info = NULL; tpd_ring->desc = NULL; tpd_ring->dma = 0; rfd_ring->buffer_info = NULL; rfd_ring->desc = NULL; rfd_ring->dma = 0; rrd_ring->desc = NULL; rrd_ring->dma = 0; adapter->cmb.dma = 0; adapter->cmb.cmb = NULL; adapter->smb.dma = 0; adapter->smb.smb = NULL; } static void atl1_setup_mac_ctrl(struct atl1_adapter *adapter) { u32 value; struct atl1_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; /* Config MAC CTRL Register */ value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN; /* duplex */ if (FULL_DUPLEX == adapter->link_duplex) value |= MAC_CTRL_DUPLX; /* speed */ value |= ((u32) ((SPEED_1000 == adapter->link_speed) ? MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) << MAC_CTRL_SPEED_SHIFT); /* flow control */ value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); /* PAD & CRC */ value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); /* preamble length */ value |= (((u32) adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); /* vlan */ __atlx_vlan_mode(netdev->features, &value); /* rx checksum if (adapter->rx_csum) value |= MAC_CTRL_RX_CHKSUM_EN; */ /* filter mode */ value |= MAC_CTRL_BC_EN; if (netdev->flags & IFF_PROMISC) value |= MAC_CTRL_PROMIS_EN; else if (netdev->flags & IFF_ALLMULTI) value |= MAC_CTRL_MC_ALL_EN; /* value |= MAC_CTRL_LOOPBACK; */ iowrite32(value, hw->hw_addr + REG_MAC_CTRL); } static u32 atl1_check_link(struct atl1_adapter *adapter) { struct atl1_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; u32 ret_val; u16 speed, duplex, phy_data; int reconfig = 0; /* MII_BMSR must read twice */ atl1_read_phy_reg(hw, MII_BMSR, &phy_data); atl1_read_phy_reg(hw, MII_BMSR, &phy_data); if (!(phy_data & BMSR_LSTATUS)) { /* link down */ if (netif_carrier_ok(netdev)) { /* old link state: Up */ if (netif_msg_link(adapter)) dev_info(&adapter->pdev->dev, "link is down\n"); adapter->link_speed = SPEED_0; netif_carrier_off(netdev); } return 0; } /* Link Up */ ret_val = atl1_get_speed_and_duplex(hw, &speed, &duplex); if (ret_val) return ret_val; switch (hw->media_type) { case MEDIA_TYPE_1000M_FULL: if (speed != SPEED_1000 || duplex != FULL_DUPLEX) reconfig = 1; break; case MEDIA_TYPE_100M_FULL: if (speed != SPEED_100 || duplex != FULL_DUPLEX) reconfig = 1; break; case MEDIA_TYPE_100M_HALF: if (speed != SPEED_100 || duplex != HALF_DUPLEX) reconfig = 1; break; case MEDIA_TYPE_10M_FULL: if (speed != SPEED_10 || duplex != FULL_DUPLEX) reconfig = 1; break; case MEDIA_TYPE_10M_HALF: if (speed != SPEED_10 || duplex != HALF_DUPLEX) reconfig = 1; break; } /* link result is our setting */ if (!reconfig) { if (adapter->link_speed != speed || adapter->link_duplex != duplex) { adapter->link_speed = speed; adapter->link_duplex = duplex; atl1_setup_mac_ctrl(adapter); if (netif_msg_link(adapter)) dev_info(&adapter->pdev->dev, "%s link is up %d Mbps %s\n", netdev->name, adapter->link_speed, adapter->link_duplex == FULL_DUPLEX ? "full duplex" : "half duplex"); } if (!netif_carrier_ok(netdev)) { /* Link down -> Up */ netif_carrier_on(netdev); } return 0; } /* change original link status */ if (netif_carrier_ok(netdev)) { adapter->link_speed = SPEED_0; netif_carrier_off(netdev); netif_stop_queue(netdev); } if (hw->media_type != MEDIA_TYPE_AUTO_SENSOR && hw->media_type != MEDIA_TYPE_1000M_FULL) { switch (hw->media_type) { case MEDIA_TYPE_100M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_100M_HALF: phy_data = MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_10M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; break; default: /* MEDIA_TYPE_10M_HALF: */ phy_data = MII_CR_SPEED_10 | MII_CR_RESET; break; } atl1_write_phy_reg(hw, MII_BMCR, phy_data); return 0; } /* auto-neg, insert timer to re-config phy */ if (!adapter->phy_timer_pending) { adapter->phy_timer_pending = true; mod_timer(&adapter->phy_config_timer, round_jiffies(jiffies + 3 * HZ)); } return 0; } static void set_flow_ctrl_old(struct atl1_adapter *adapter) { u32 hi, lo, value; /* RFD Flow Control */ value = adapter->rfd_ring.count; hi = value / 16; if (hi < 2) hi = 2; lo = value * 7 / 8; value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) | ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT); iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RXF_PAUSE_THRESH); /* RRD Flow Control */ value = adapter->rrd_ring.count; lo = value / 16; hi = value * 7 / 8; if (lo < 2) lo = 2; value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) | ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT); iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RRD_PAUSE_THRESH); } static void set_flow_ctrl_new(struct atl1_hw *hw) { u32 hi, lo, value; /* RXF Flow Control */ value = ioread32(hw->hw_addr + REG_SRAM_RXF_LEN); lo = value / 16; if (lo < 192) lo = 192; hi = value * 7 / 8; if (hi < lo) hi = lo + 16; value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) | ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT); iowrite32(value, hw->hw_addr + REG_RXQ_RXF_PAUSE_THRESH); /* RRD Flow Control */ value = ioread32(hw->hw_addr + REG_SRAM_RRD_LEN); lo = value / 8; hi = value * 7 / 8; if (lo < 2) lo = 2; if (hi < lo) hi = lo + 3; value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) | ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT); iowrite32(value, hw->hw_addr + REG_RXQ_RRD_PAUSE_THRESH); } /** * atl1_configure - Configure Transmit&Receive Unit after Reset * @adapter: board private structure * * Configure the Tx /Rx unit of the MAC after a reset. */ static u32 atl1_configure(struct atl1_adapter *adapter) { struct atl1_hw *hw = &adapter->hw; u32 value; /* clear interrupt status */ iowrite32(0xffffffff, adapter->hw.hw_addr + REG_ISR); /* set MAC Address */ value = (((u32) hw->mac_addr[2]) << 24) | (((u32) hw->mac_addr[3]) << 16) | (((u32) hw->mac_addr[4]) << 8) | (((u32) hw->mac_addr[5])); iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR); value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1])); iowrite32(value, hw->hw_addr + (REG_MAC_STA_ADDR + 4)); /* tx / rx ring */ /* HI base address */ iowrite32((u32) ((adapter->tpd_ring.dma & 0xffffffff00000000ULL) >> 32), hw->hw_addr + REG_DESC_BASE_ADDR_HI); /* LO base address */ iowrite32((u32) (adapter->rfd_ring.dma & 0x00000000ffffffffULL), hw->hw_addr + REG_DESC_RFD_ADDR_LO); iowrite32((u32) (adapter->rrd_ring.dma & 0x00000000ffffffffULL), hw->hw_addr + REG_DESC_RRD_ADDR_LO); iowrite32((u32) (adapter->tpd_ring.dma & 0x00000000ffffffffULL), hw->hw_addr + REG_DESC_TPD_ADDR_LO); iowrite32((u32) (adapter->cmb.dma & 0x00000000ffffffffULL), hw->hw_addr + REG_DESC_CMB_ADDR_LO); iowrite32((u32) (adapter->smb.dma & 0x00000000ffffffffULL), hw->hw_addr + REG_DESC_SMB_ADDR_LO); /* element count */ value = adapter->rrd_ring.count; value <<= 16; value += adapter->rfd_ring.count; iowrite32(value, hw->hw_addr + REG_DESC_RFD_RRD_RING_SIZE); iowrite32(adapter->tpd_ring.count, hw->hw_addr + REG_DESC_TPD_RING_SIZE); /* Load Ptr */ iowrite32(1, hw->hw_addr + REG_LOAD_PTR); /* config Mailbox */ value = ((atomic_read(&adapter->tpd_ring.next_to_use) & MB_TPD_PROD_INDX_MASK) << MB_TPD_PROD_INDX_SHIFT) | ((atomic_read(&adapter->rrd_ring.next_to_clean) & MB_RRD_CONS_INDX_MASK) << MB_RRD_CONS_INDX_SHIFT) | ((atomic_read(&adapter->rfd_ring.next_to_use) & MB_RFD_PROD_INDX_MASK) << MB_RFD_PROD_INDX_SHIFT); iowrite32(value, hw->hw_addr + REG_MAILBOX); /* config IPG/IFG */ value = (((u32) hw->ipgt & MAC_IPG_IFG_IPGT_MASK) << MAC_IPG_IFG_IPGT_SHIFT) | (((u32) hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) << MAC_IPG_IFG_MIFG_SHIFT) | (((u32) hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) << MAC_IPG_IFG_IPGR1_SHIFT) | (((u32) hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) << MAC_IPG_IFG_IPGR2_SHIFT); iowrite32(value, hw->hw_addr + REG_MAC_IPG_IFG); /* config Half-Duplex Control */ value = ((u32) hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) | (((u32) hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) << MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) | MAC_HALF_DUPLX_CTRL_EXC_DEF_EN | (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) | (((u32) hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) << MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT); iowrite32(value, hw->hw_addr + REG_MAC_HALF_DUPLX_CTRL); /* set Interrupt Moderator Timer */ iowrite16(adapter->imt, hw->hw_addr + REG_IRQ_MODU_TIMER_INIT); iowrite32(MASTER_CTRL_ITIMER_EN, hw->hw_addr + REG_MASTER_CTRL); /* set Interrupt Clear Timer */ iowrite16(adapter->ict, hw->hw_addr + REG_CMBDISDMA_TIMER); /* set max frame size hw will accept */ iowrite32(hw->max_frame_size, hw->hw_addr + REG_MTU); /* jumbo size & rrd retirement timer */ value = (((u32) hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) << RXQ_JMBOSZ_TH_SHIFT) | (((u32) hw->rx_jumbo_lkah & RXQ_JMBO_LKAH_MASK) << RXQ_JMBO_LKAH_SHIFT) | (((u32) hw->rrd_ret_timer & RXQ_RRD_TIMER_MASK) << RXQ_RRD_TIMER_SHIFT); iowrite32(value, hw->hw_addr + REG_RXQ_JMBOSZ_RRDTIM); /* Flow Control */ switch (hw->dev_rev) { case 0x8001: case 0x9001: case 0x9002: case 0x9003: set_flow_ctrl_old(adapter); break; default: set_flow_ctrl_new(hw); break; } /* config TXQ */ value = (((u32) hw->tpd_burst & TXQ_CTRL_TPD_BURST_NUM_MASK) << TXQ_CTRL_TPD_BURST_NUM_SHIFT) | (((u32) hw->txf_burst & TXQ_CTRL_TXF_BURST_NUM_MASK) << TXQ_CTRL_TXF_BURST_NUM_SHIFT) | (((u32) hw->tpd_fetch_th & TXQ_CTRL_TPD_FETCH_TH_MASK) << TXQ_CTRL_TPD_FETCH_TH_SHIFT) | TXQ_CTRL_ENH_MODE | TXQ_CTRL_EN; iowrite32(value, hw->hw_addr + REG_TXQ_CTRL); /* min tpd fetch gap & tx jumbo packet size threshold for taskoffload */ value = (((u32) hw->tx_jumbo_task_th & TX_JUMBO_TASK_TH_MASK) << TX_JUMBO_TASK_TH_SHIFT) | (((u32) hw->tpd_fetch_gap & TX_TPD_MIN_IPG_MASK) << TX_TPD_MIN_IPG_SHIFT); iowrite32(value, hw->hw_addr + REG_TX_JUMBO_TASK_TH_TPD_IPG); /* config RXQ */ value = (((u32) hw->rfd_burst & RXQ_CTRL_RFD_BURST_NUM_MASK) << RXQ_CTRL_RFD_BURST_NUM_SHIFT) | (((u32) hw->rrd_burst & RXQ_CTRL_RRD_BURST_THRESH_MASK) << RXQ_CTRL_RRD_BURST_THRESH_SHIFT) | (((u32) hw->rfd_fetch_gap & RXQ_CTRL_RFD_PREF_MIN_IPG_MASK) << RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT) | RXQ_CTRL_CUT_THRU_EN | RXQ_CTRL_EN; iowrite32(value, hw->hw_addr + REG_RXQ_CTRL); /* config DMA Engine */ value = ((((u32) hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) << DMA_CTRL_DMAR_BURST_LEN_SHIFT) | ((((u32) hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) << DMA_CTRL_DMAW_BURST_LEN_SHIFT) | DMA_CTRL_DMAR_EN | DMA_CTRL_DMAW_EN; value |= (u32) hw->dma_ord; if (atl1_rcb_128 == hw->rcb_value) value |= DMA_CTRL_RCB_VALUE; iowrite32(value, hw->hw_addr + REG_DMA_CTRL); /* config CMB / SMB */ value = (hw->cmb_tpd > adapter->tpd_ring.count) ? hw->cmb_tpd : adapter->tpd_ring.count; value <<= 16; value |= hw->cmb_rrd; iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TH); value = hw->cmb_rx_timer | ((u32) hw->cmb_tx_timer << 16); iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TIMER); iowrite32(hw->smb_timer, hw->hw_addr + REG_SMB_TIMER); /* --- enable CMB / SMB */ value = CSMB_CTRL_CMB_EN | CSMB_CTRL_SMB_EN; iowrite32(value, hw->hw_addr + REG_CSMB_CTRL); value = ioread32(adapter->hw.hw_addr + REG_ISR); if (unlikely((value & ISR_PHY_LINKDOWN) != 0)) value = 1; /* config failed */ else value = 0; /* clear all interrupt status */ iowrite32(0x3fffffff, adapter->hw.hw_addr + REG_ISR); iowrite32(0, adapter->hw.hw_addr + REG_ISR); return value; } /* * atl1_pcie_patch - Patch for PCIE module */ static void atl1_pcie_patch(struct atl1_adapter *adapter) { u32 value; /* much vendor magic here */ value = 0x6500; iowrite32(value, adapter->hw.hw_addr + 0x12FC); /* pcie flow control mode change */ value = ioread32(adapter->hw.hw_addr + 0x1008); value |= 0x8000; iowrite32(value, adapter->hw.hw_addr + 0x1008); } /* * When ACPI resume on some VIA MotherBoard, the Interrupt Disable bit/0x400 * on PCI Command register is disable. * The function enable this bit. * Brackett, 2006/03/15 */ static void atl1_via_workaround(struct atl1_adapter *adapter) { unsigned long value; value = ioread16(adapter->hw.hw_addr + PCI_COMMAND); if (value & PCI_COMMAND_INTX_DISABLE) value &= ~PCI_COMMAND_INTX_DISABLE; iowrite32(value, adapter->hw.hw_addr + PCI_COMMAND); } static void atl1_inc_smb(struct atl1_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct stats_msg_block *smb = adapter->smb.smb; /* Fill out the OS statistics structure */ adapter->soft_stats.rx_packets += smb->rx_ok; adapter->soft_stats.tx_packets += smb->tx_ok; adapter->soft_stats.rx_bytes += smb->rx_byte_cnt; adapter->soft_stats.tx_bytes += smb->tx_byte_cnt; adapter->soft_stats.multicast += smb->rx_mcast; adapter->soft_stats.collisions += (smb->tx_1_col + smb->tx_2_col * 2 + smb->tx_late_col + smb->tx_abort_col * adapter->hw.max_retry); /* Rx Errors */ adapter->soft_stats.rx_errors += (smb->rx_frag + smb->rx_fcs_err + smb->rx_len_err + smb->rx_sz_ov + smb->rx_rxf_ov + smb->rx_rrd_ov + smb->rx_align_err); adapter->soft_stats.rx_fifo_errors += smb->rx_rxf_ov; adapter->soft_stats.rx_length_errors += smb->rx_len_err; adapter->soft_stats.rx_crc_errors += smb->rx_fcs_err; adapter->soft_stats.rx_frame_errors += smb->rx_align_err; adapter->soft_stats.rx_missed_errors += (smb->rx_rrd_ov + smb->rx_rxf_ov); adapter->soft_stats.rx_pause += smb->rx_pause; adapter->soft_stats.rx_rrd_ov += smb->rx_rrd_ov; adapter->soft_stats.rx_trunc += smb->rx_sz_ov; /* Tx Errors */ adapter->soft_stats.tx_errors += (smb->tx_late_col + smb->tx_abort_col + smb->tx_underrun + smb->tx_trunc); adapter->soft_stats.tx_fifo_errors += smb->tx_underrun; adapter->soft_stats.tx_aborted_errors += smb->tx_abort_col; adapter->soft_stats.tx_window_errors += smb->tx_late_col; adapter->soft_stats.excecol += smb->tx_abort_col; adapter->soft_stats.deffer += smb->tx_defer; adapter->soft_stats.scc += smb->tx_1_col; adapter->soft_stats.mcc += smb->tx_2_col; adapter->soft_stats.latecol += smb->tx_late_col; adapter->soft_stats.tx_underun += smb->tx_underrun; adapter->soft_stats.tx_trunc += smb->tx_trunc; adapter->soft_stats.tx_pause += smb->tx_pause; netdev->stats.rx_packets = adapter->soft_stats.rx_packets; netdev->stats.tx_packets = adapter->soft_stats.tx_packets; netdev->stats.rx_bytes = adapter->soft_stats.rx_bytes; netdev->stats.tx_bytes = adapter->soft_stats.tx_bytes; netdev->stats.multicast = adapter->soft_stats.multicast; netdev->stats.collisions = adapter->soft_stats.collisions; netdev->stats.rx_errors = adapter->soft_stats.rx_errors; netdev->stats.rx_over_errors = adapter->soft_stats.rx_missed_errors; netdev->stats.rx_length_errors = adapter->soft_stats.rx_length_errors; netdev->stats.rx_crc_errors = adapter->soft_stats.rx_crc_errors; netdev->stats.rx_frame_errors = adapter->soft_stats.rx_frame_errors; netdev->stats.rx_fifo_errors = adapter->soft_stats.rx_fifo_errors; netdev->stats.rx_missed_errors = adapter->soft_stats.rx_missed_errors; netdev->stats.tx_errors = adapter->soft_stats.tx_errors; netdev->stats.tx_fifo_errors = adapter->soft_stats.tx_fifo_errors; netdev->stats.tx_aborted_errors = adapter->soft_stats.tx_aborted_errors; netdev->stats.tx_window_errors = adapter->soft_stats.tx_window_errors; netdev->stats.tx_carrier_errors = adapter->soft_stats.tx_carrier_errors; } static void atl1_update_mailbox(struct atl1_adapter *adapter) { unsigned long flags; u32 tpd_next_to_use; u32 rfd_next_to_use; u32 rrd_next_to_clean; u32 value; spin_lock_irqsave(&adapter->mb_lock, flags); tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use); rfd_next_to_use = atomic_read(&adapter->rfd_ring.next_to_use); rrd_next_to_clean = atomic_read(&adapter->rrd_ring.next_to_clean); value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) << MB_RFD_PROD_INDX_SHIFT) | ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) << MB_RRD_CONS_INDX_SHIFT) | ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) << MB_TPD_PROD_INDX_SHIFT); iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX); spin_unlock_irqrestore(&adapter->mb_lock, flags); } static void atl1_clean_alloc_flag(struct atl1_adapter *adapter, struct rx_return_desc *rrd, u16 offset) { struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; while (rfd_ring->next_to_clean != (rrd->buf_indx + offset)) { rfd_ring->buffer_info[rfd_ring->next_to_clean].alloced = 0; if (++rfd_ring->next_to_clean == rfd_ring->count) { rfd_ring->next_to_clean = 0; } } } static void atl1_update_rfd_index(struct atl1_adapter *adapter, struct rx_return_desc *rrd) { u16 num_buf; num_buf = (rrd->xsz.xsum_sz.pkt_size + adapter->rx_buffer_len - 1) / adapter->rx_buffer_len; if (rrd->num_buf == num_buf) /* clean alloc flag for bad rrd */ atl1_clean_alloc_flag(adapter, rrd, num_buf); } static void atl1_rx_checksum(struct atl1_adapter *adapter, struct rx_return_desc *rrd, struct sk_buff *skb) { struct pci_dev *pdev = adapter->pdev; /* * The L1 hardware contains a bug that erroneously sets the * PACKET_FLAG_ERR and ERR_FLAG_L4_CHKSUM bits whenever a * fragmented IP packet is received, even though the packet * is perfectly valid and its checksum is correct. There's * no way to distinguish between one of these good packets * and a packet that actually contains a TCP/UDP checksum * error, so all we can do is allow it to be handed up to * the higher layers and let it be sorted out there. */ skb_checksum_none_assert(skb); if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) { if (rrd->err_flg & (ERR_FLAG_CRC | ERR_FLAG_TRUNC | ERR_FLAG_CODE | ERR_FLAG_OV)) { adapter->hw_csum_err++; if (netif_msg_rx_err(adapter)) dev_printk(KERN_DEBUG, &pdev->dev, "rx checksum error\n"); return; } } /* not IPv4 */ if (!(rrd->pkt_flg & PACKET_FLAG_IPV4)) /* checksum is invalid, but it's not an IPv4 pkt, so ok */ return; /* IPv4 packet */ if (likely(!(rrd->err_flg & (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM)))) { skb->ip_summed = CHECKSUM_UNNECESSARY; adapter->hw_csum_good++; return; } } /** * atl1_alloc_rx_buffers - Replace used receive buffers * @adapter: address of board private structure */ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) { struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; struct pci_dev *pdev = adapter->pdev; struct page *page; unsigned long offset; struct atl1_buffer *buffer_info, *next_info; struct sk_buff *skb; u16 num_alloc = 0; u16 rfd_next_to_use, next_next; struct rx_free_desc *rfd_desc; next_next = rfd_next_to_use = atomic_read(&rfd_ring->next_to_use); if (++next_next == rfd_ring->count) next_next = 0; buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; next_info = &rfd_ring->buffer_info[next_next]; while (!buffer_info->alloced && !next_info->alloced) { if (buffer_info->skb) { buffer_info->alloced = 1; goto next; } rfd_desc = ATL1_RFD_DESC(rfd_ring, rfd_next_to_use); skb = netdev_alloc_skb_ip_align(adapter->netdev, adapter->rx_buffer_len); if (unlikely(!skb)) { /* Better luck next round */ adapter->netdev->stats.rx_dropped++; break; } buffer_info->alloced = 1; buffer_info->skb = skb; buffer_info->length = (u16) adapter->rx_buffer_len; page = virt_to_page(skb->data); offset = (unsigned long)skb->data & ~PAGE_MASK; buffer_info->dma = pci_map_page(pdev, page, offset, adapter->rx_buffer_len, PCI_DMA_FROMDEVICE); rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); rfd_desc->buf_len = cpu_to_le16(adapter->rx_buffer_len); rfd_desc->coalese = 0; next: rfd_next_to_use = next_next; if (unlikely(++next_next == rfd_ring->count)) next_next = 0; buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; next_info = &rfd_ring->buffer_info[next_next]; num_alloc++; } if (num_alloc) { /* * Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, * such as IA-64). */ wmb(); atomic_set(&rfd_ring->next_to_use, (int)rfd_next_to_use); } return num_alloc; } static int atl1_intr_rx(struct atl1_adapter *adapter, int budget) { int i, count; u16 length; u16 rrd_next_to_clean; u32 value; struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1_buffer *buffer_info; struct rx_return_desc *rrd; struct sk_buff *skb; count = 0; rrd_next_to_clean = atomic_read(&rrd_ring->next_to_clean); while (count < budget) { rrd = ATL1_RRD_DESC(rrd_ring, rrd_next_to_clean); i = 1; if (likely(rrd->xsz.valid)) { /* packet valid */ chk_rrd: /* check rrd status */ if (likely(rrd->num_buf == 1)) goto rrd_ok; else if (netif_msg_rx_err(adapter)) { dev_printk(KERN_DEBUG, &adapter->pdev->dev, "unexpected RRD buffer count\n"); dev_printk(KERN_DEBUG, &adapter->pdev->dev, "rx_buf_len = %d\n", adapter->rx_buffer_len); dev_printk(KERN_DEBUG, &adapter->pdev->dev, "RRD num_buf = %d\n", rrd->num_buf); dev_printk(KERN_DEBUG, &adapter->pdev->dev, "RRD pkt_len = %d\n", rrd->xsz.xsum_sz.pkt_size); dev_printk(KERN_DEBUG, &adapter->pdev->dev, "RRD pkt_flg = 0x%08X\n", rrd->pkt_flg); dev_printk(KERN_DEBUG, &adapter->pdev->dev, "RRD err_flg = 0x%08X\n", rrd->err_flg); dev_printk(KERN_DEBUG, &adapter->pdev->dev, "RRD vlan_tag = 0x%08X\n", rrd->vlan_tag); } /* rrd seems to be bad */ if (unlikely(i-- > 0)) { /* rrd may not be DMAed completely */ udelay(1); goto chk_rrd; } /* bad rrd */ if (netif_msg_rx_err(adapter)) dev_printk(KERN_DEBUG, &adapter->pdev->dev, "bad RRD\n"); /* see if update RFD index */ if (rrd->num_buf > 1) atl1_update_rfd_index(adapter, rrd); /* update rrd */ rrd->xsz.valid = 0; if (++rrd_next_to_clean == rrd_ring->count) rrd_next_to_clean = 0; count++; continue; } else { /* current rrd still not be updated */ break; } rrd_ok: /* clean alloc flag for bad rrd */ atl1_clean_alloc_flag(adapter, rrd, 0); buffer_info = &rfd_ring->buffer_info[rrd->buf_indx]; if (++rfd_ring->next_to_clean == rfd_ring->count) rfd_ring->next_to_clean = 0; /* update rrd next to clean */ if (++rrd_next_to_clean == rrd_ring->count) rrd_next_to_clean = 0; count++; if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) { if (!(rrd->err_flg & (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM | ERR_FLAG_LEN))) { /* packet error, don't need upstream */ buffer_info->alloced = 0; rrd->xsz.valid = 0; continue; } } /* Good Receive */ pci_unmap_page(adapter->pdev, buffer_info->dma, buffer_info->length, PCI_DMA_FROMDEVICE); buffer_info->dma = 0; skb = buffer_info->skb; length = le16_to_cpu(rrd->xsz.xsum_sz.pkt_size); skb_put(skb, length - ETH_FCS_LEN); /* Receive Checksum Offload */ atl1_rx_checksum(adapter, rrd, skb); skb->protocol = eth_type_trans(skb, adapter->netdev); if (rrd->pkt_flg & PACKET_FLAG_VLAN_INS) { u16 vlan_tag = (rrd->vlan_tag >> 4) | ((rrd->vlan_tag & 7) << 13) | ((rrd->vlan_tag & 8) << 9); __vlan_hwaccel_put_tag(skb, vlan_tag); } netif_receive_skb(skb); /* let protocol layer free skb */ buffer_info->skb = NULL; buffer_info->alloced = 0; rrd->xsz.valid = 0; } atomic_set(&rrd_ring->next_to_clean, rrd_next_to_clean); atl1_alloc_rx_buffers(adapter); /* update mailbox ? */ if (count) { u32 tpd_next_to_use; u32 rfd_next_to_use; spin_lock(&adapter->mb_lock); tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use); rfd_next_to_use = atomic_read(&adapter->rfd_ring.next_to_use); rrd_next_to_clean = atomic_read(&adapter->rrd_ring.next_to_clean); value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) << MB_RFD_PROD_INDX_SHIFT) | ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) << MB_RRD_CONS_INDX_SHIFT) | ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) << MB_TPD_PROD_INDX_SHIFT); iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX); spin_unlock(&adapter->mb_lock); } return count; } static int atl1_intr_tx(struct atl1_adapter *adapter) { struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; struct atl1_buffer *buffer_info; u16 sw_tpd_next_to_clean; u16 cmb_tpd_next_to_clean; int count = 0; sw_tpd_next_to_clean = atomic_read(&tpd_ring->next_to_clean); cmb_tpd_next_to_clean = le16_to_cpu(adapter->cmb.cmb->tpd_cons_idx); while (cmb_tpd_next_to_clean != sw_tpd_next_to_clean) { buffer_info = &tpd_ring->buffer_info[sw_tpd_next_to_clean]; if (buffer_info->dma) { pci_unmap_page(adapter->pdev, buffer_info->dma, buffer_info->length, PCI_DMA_TODEVICE); buffer_info->dma = 0; } if (buffer_info->skb) { dev_kfree_skb_irq(buffer_info->skb); buffer_info->skb = NULL; } if (++sw_tpd_next_to_clean == tpd_ring->count) sw_tpd_next_to_clean = 0; count++; } atomic_set(&tpd_ring->next_to_clean, sw_tpd_next_to_clean); if (netif_queue_stopped(adapter->netdev) && netif_carrier_ok(adapter->netdev)) netif_wake_queue(adapter->netdev); return count; } static u16 atl1_tpd_avail(struct atl1_tpd_ring *tpd_ring) { u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); u16 next_to_use = atomic_read(&tpd_ring->next_to_use); return (next_to_clean > next_to_use) ? next_to_clean - next_to_use - 1 : tpd_ring->count + next_to_clean - next_to_use - 1; } static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb, struct tx_packet_desc *ptpd) { u8 hdr_len, ip_off; u32 real_len; int err; if (skb_shinfo(skb)->gso_size) { if (skb_header_cloned(skb)) { err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (unlikely(err)) return -1; } if (skb->protocol == htons(ETH_P_IP)) { struct iphdr *iph = ip_hdr(skb); real_len = (((unsigned char *)iph - skb->data) + ntohs(iph->tot_len)); if (real_len < skb->len) pskb_trim(skb, real_len); hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); if (skb->len == hdr_len) { iph->check = 0; tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, tcp_hdrlen(skb), IPPROTO_TCP, 0); ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) << TPD_IPHL_SHIFT; ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) & TPD_TCPHDRLEN_MASK) << TPD_TCPHDRLEN_SHIFT; ptpd->word3 |= 1 << TPD_IP_CSUM_SHIFT; ptpd->word3 |= 1 << TPD_TCP_CSUM_SHIFT; return 1; } iph->check = 0; tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 0, IPPROTO_TCP, 0); ip_off = (unsigned char *)iph - (unsigned char *) skb_network_header(skb); if (ip_off == 8) /* 802.3-SNAP frame */ ptpd->word3 |= 1 << TPD_ETHTYPE_SHIFT; else if (ip_off != 0) return -2; ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) << TPD_IPHL_SHIFT; ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) & TPD_TCPHDRLEN_MASK) << TPD_TCPHDRLEN_SHIFT; ptpd->word3 |= (skb_shinfo(skb)->gso_size & TPD_MSS_MASK) << TPD_MSS_SHIFT; ptpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT; return 3; } } return false; } static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb, struct tx_packet_desc *ptpd) { u8 css, cso; if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { css = skb_checksum_start_offset(skb); cso = css + (u8) skb->csum_offset; if (unlikely(css & 0x1)) { /* L1 hardware requires an even number here */ if (netif_msg_tx_err(adapter)) dev_printk(KERN_DEBUG, &adapter->pdev->dev, "payload offset not an even number\n"); return -1; } ptpd->word3 |= (css & TPD_PLOADOFFSET_MASK) << TPD_PLOADOFFSET_SHIFT; ptpd->word3 |= (cso & TPD_CCSUMOFFSET_MASK) << TPD_CCSUMOFFSET_SHIFT; ptpd->word3 |= 1 << TPD_CUST_CSUM_EN_SHIFT; return true; } return 0; } static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, struct tx_packet_desc *ptpd) { struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; struct atl1_buffer *buffer_info; u16 buf_len = skb->len; struct page *page; unsigned long offset; unsigned int nr_frags; unsigned int f; int retval; u16 next_to_use; u16 data_len; u8 hdr_len; buf_len -= skb->data_len; nr_frags = skb_shinfo(skb)->nr_frags; next_to_use = atomic_read(&tpd_ring->next_to_use); buffer_info = &tpd_ring->buffer_info[next_to_use]; BUG_ON(buffer_info->skb); /* put skb in last TPD */ buffer_info->skb = NULL; retval = (ptpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK; if (retval) { /* TSO */ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); buffer_info->length = hdr_len; page = virt_to_page(skb->data); offset = (unsigned long)skb->data & ~PAGE_MASK; buffer_info->dma = pci_map_page(adapter->pdev, page, offset, hdr_len, PCI_DMA_TODEVICE); if (++next_to_use == tpd_ring->count) next_to_use = 0; if (buf_len > hdr_len) { int i, nseg; data_len = buf_len - hdr_len; nseg = (data_len + ATL1_MAX_TX_BUF_LEN - 1) / ATL1_MAX_TX_BUF_LEN; for (i = 0; i < nseg; i++) { buffer_info = &tpd_ring->buffer_info[next_to_use]; buffer_info->skb = NULL; buffer_info->length = (ATL1_MAX_TX_BUF_LEN >= data_len) ? ATL1_MAX_TX_BUF_LEN : data_len; data_len -= buffer_info->length; page = virt_to_page(skb->data + (hdr_len + i * ATL1_MAX_TX_BUF_LEN)); offset = (unsigned long)(skb->data + (hdr_len + i * ATL1_MAX_TX_BUF_LEN)) & ~PAGE_MASK; buffer_info->dma = pci_map_page(adapter->pdev, page, offset, buffer_info->length, PCI_DMA_TODEVICE); if (++next_to_use == tpd_ring->count) next_to_use = 0; } } } else { /* not TSO */ buffer_info->length = buf_len; page = virt_to_page(skb->data); offset = (unsigned long)skb->data & ~PAGE_MASK; buffer_info->dma = pci_map_page(adapter->pdev, page, offset, buf_len, PCI_DMA_TODEVICE); if (++next_to_use == tpd_ring->count) next_to_use = 0; } for (f = 0; f < nr_frags; f++) { const struct skb_frag_struct *frag; u16 i, nseg; frag = &skb_shinfo(skb)->frags[f]; buf_len = skb_frag_size(frag); nseg = (buf_len + ATL1_MAX_TX_BUF_LEN - 1) / ATL1_MAX_TX_BUF_LEN; for (i = 0; i < nseg; i++) { buffer_info = &tpd_ring->buffer_info[next_to_use]; BUG_ON(buffer_info->skb); buffer_info->skb = NULL; buffer_info->length = (buf_len > ATL1_MAX_TX_BUF_LEN) ? ATL1_MAX_TX_BUF_LEN : buf_len; buf_len -= buffer_info->length; buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev, frag, i * ATL1_MAX_TX_BUF_LEN, buffer_info->length, DMA_TO_DEVICE); if (++next_to_use == tpd_ring->count) next_to_use = 0; } } /* last tpd's buffer-info */ buffer_info->skb = skb; } static void atl1_tx_queue(struct atl1_adapter *adapter, u16 count, struct tx_packet_desc *ptpd) { struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; struct atl1_buffer *buffer_info; struct tx_packet_desc *tpd; u16 j; u32 val; u16 next_to_use = (u16) atomic_read(&tpd_ring->next_to_use); for (j = 0; j < count; j++) { buffer_info = &tpd_ring->buffer_info[next_to_use]; tpd = ATL1_TPD_DESC(&adapter->tpd_ring, next_to_use); if (tpd != ptpd) memcpy(tpd, ptpd, sizeof(struct tx_packet_desc)); tpd->buffer_addr = cpu_to_le64(buffer_info->dma); tpd->word2 &= ~(TPD_BUFLEN_MASK << TPD_BUFLEN_SHIFT); tpd->word2 |= (cpu_to_le16(buffer_info->length) & TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT; /* * if this is the first packet in a TSO chain, set * TPD_HDRFLAG, otherwise, clear it. */ val = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK; if (val) { if (!j) tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT; else tpd->word3 &= ~(1 << TPD_HDRFLAG_SHIFT); } if (j == (count - 1)) tpd->word3 |= 1 << TPD_EOP_SHIFT; if (++next_to_use == tpd_ring->count) next_to_use = 0; } /* * Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, * such as IA-64). */ wmb(); atomic_set(&tpd_ring->next_to_use, next_to_use); } static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; int len; int tso; int count = 1; int ret_val; struct tx_packet_desc *ptpd; u16 vlan_tag; unsigned int nr_frags = 0; unsigned int mss = 0; unsigned int f; unsigned int proto_hdr_len; len = skb_headlen(skb); if (unlikely(skb->len <= 0)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } nr_frags = skb_shinfo(skb)->nr_frags; for (f = 0; f < nr_frags; f++) { unsigned int f_size = skb_frag_size(&skb_shinfo(skb)->frags[f]); count += (f_size + ATL1_MAX_TX_BUF_LEN - 1) / ATL1_MAX_TX_BUF_LEN; } mss = skb_shinfo(skb)->gso_size; if (mss) { if (skb->protocol == htons(ETH_P_IP)) { proto_hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); if (unlikely(proto_hdr_len > len)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } /* need additional TPD ? */ if (proto_hdr_len != len) count += (len - proto_hdr_len + ATL1_MAX_TX_BUF_LEN - 1) / ATL1_MAX_TX_BUF_LEN; } } if (atl1_tpd_avail(&adapter->tpd_ring) < count) { /* not enough descriptors */ netif_stop_queue(netdev); if (netif_msg_tx_queued(adapter)) dev_printk(KERN_DEBUG, &adapter->pdev->dev, "tx busy\n"); return NETDEV_TX_BUSY; } ptpd = ATL1_TPD_DESC(tpd_ring, (u16) atomic_read(&tpd_ring->next_to_use)); memset(ptpd, 0, sizeof(struct tx_packet_desc)); if (vlan_tx_tag_present(skb)) { vlan_tag = vlan_tx_tag_get(skb); vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) | ((vlan_tag >> 9) & 0x8); ptpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT; ptpd->word2 |= (vlan_tag & TPD_VLANTAG_MASK) << TPD_VLANTAG_SHIFT; } tso = atl1_tso(adapter, skb, ptpd); if (tso < 0) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } if (!tso) { ret_val = atl1_tx_csum(adapter, skb, ptpd); if (ret_val < 0) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } } atl1_tx_map(adapter, skb, ptpd); atl1_tx_queue(adapter, count, ptpd); atl1_update_mailbox(adapter); mmiowb(); return NETDEV_TX_OK; } static int atl1_rings_clean(struct napi_struct *napi, int budget) { struct atl1_adapter *adapter = container_of(napi, struct atl1_adapter, napi); int work_done = atl1_intr_rx(adapter, budget); if (atl1_intr_tx(adapter)) work_done = budget; /* Let's come again to process some more packets */ if (work_done >= budget) return work_done; napi_complete(napi); /* re-enable Interrupt */ if (likely(adapter->int_enabled)) atlx_imr_set(adapter, IMR_NORMAL_MASK); return work_done; } static inline int atl1_sched_rings_clean(struct atl1_adapter* adapter) { if (!napi_schedule_prep(&adapter->napi)) /* It is possible in case even the RX/TX ints are disabled via IMR * register the ISR bits are set anyway (but do not produce IRQ). * To handle such situation the napi functions used to check is * something scheduled or not. */ return 0; __napi_schedule(&adapter->napi); /* * Disable RX/TX ints via IMR register if it is * allowed. NAPI handler must reenable them in same * way. */ if (!adapter->int_enabled) return 1; atlx_imr_set(adapter, IMR_NORXTX_MASK); return 1; } /** * atl1_intr - Interrupt Handler * @irq: interrupt number * @data: pointer to a network interface device structure */ static irqreturn_t atl1_intr(int irq, void *data) { struct atl1_adapter *adapter = netdev_priv(data); u32 status; status = adapter->cmb.cmb->int_stats; if (!status) return IRQ_NONE; /* clear CMB interrupt status at once, * but leave rx/tx interrupt status in case it should be dropped * only if rx/tx processing queued. In other case interrupt * can be lost. */ adapter->cmb.cmb->int_stats = status & (ISR_CMB_TX | ISR_CMB_RX); if (status & ISR_GPHY) /* clear phy status */ atlx_clear_phy_int(adapter); /* clear ISR status, and Enable CMB DMA/Disable Interrupt */ iowrite32(status | ISR_DIS_INT, adapter->hw.hw_addr + REG_ISR); /* check if SMB intr */ if (status & ISR_SMB) atl1_inc_smb(adapter); /* check if PCIE PHY Link down */ if (status & ISR_PHY_LINKDOWN) { if (netif_msg_intr(adapter)) dev_printk(KERN_DEBUG, &adapter->pdev->dev, "pcie phy link down %x\n", status); if (netif_running(adapter->netdev)) { /* reset MAC */ atlx_irq_disable(adapter); schedule_work(&adapter->reset_dev_task); return IRQ_HANDLED; } } /* check if DMA read/write error ? */ if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { if (netif_msg_intr(adapter)) dev_printk(KERN_DEBUG, &adapter->pdev->dev, "pcie DMA r/w error (status = 0x%x)\n", status); atlx_irq_disable(adapter); schedule_work(&adapter->reset_dev_task); return IRQ_HANDLED; } /* link event */ if (status & ISR_GPHY) { adapter->soft_stats.tx_carrier_errors++; atl1_check_for_link(adapter); } /* transmit or receive event */ if (status & (ISR_CMB_TX | ISR_CMB_RX) && atl1_sched_rings_clean(adapter)) adapter->cmb.cmb->int_stats = adapter->cmb.cmb->int_stats & ~(ISR_CMB_TX | ISR_CMB_RX); /* rx exception */ if (unlikely(status & (ISR_RXF_OV | ISR_RFD_UNRUN | ISR_RRD_OV | ISR_HOST_RFD_UNRUN | ISR_HOST_RRD_OV))) { if (netif_msg_intr(adapter)) dev_printk(KERN_DEBUG, &adapter->pdev->dev, "rx exception, ISR = 0x%x\n", status); atl1_sched_rings_clean(adapter); } /* re-enable Interrupt */ iowrite32(ISR_DIS_SMB | ISR_DIS_DMA, adapter->hw.hw_addr + REG_ISR); return IRQ_HANDLED; } /** * atl1_phy_config - Timer Call-back * @data: pointer to netdev cast into an unsigned long */ static void atl1_phy_config(unsigned long data) { struct atl1_adapter *adapter = (struct atl1_adapter *)data; struct atl1_hw *hw = &adapter->hw; unsigned long flags; spin_lock_irqsave(&adapter->lock, flags); adapter->phy_timer_pending = false; atl1_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); atl1_write_phy_reg(hw, MII_ATLX_CR, hw->mii_1000t_ctrl_reg); atl1_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN); spin_unlock_irqrestore(&adapter->lock, flags); } /* * Orphaned vendor comment left intact here: * * If TPD Buffer size equal to 0, PCIE DMAR_TO_INT * will assert. We do soft reset <0x1400=1> according * with the SPEC. BUT, it seemes that PCIE or DMA * state-machine will not be reset. DMAR_TO_INT will * assert again and again. * */ static int atl1_reset(struct atl1_adapter *adapter) { int ret; ret = atl1_reset_hw(&adapter->hw); if (ret) return ret; return atl1_init_hw(&adapter->hw); } static s32 atl1_up(struct atl1_adapter *adapter) { struct net_device *netdev = adapter->netdev; int err; int irq_flags = 0; /* hardware has been reset, we need to reload some things */ atlx_set_multi(netdev); atl1_init_ring_ptrs(adapter); atlx_restore_vlan(adapter); err = atl1_alloc_rx_buffers(adapter); if (unlikely(!err)) /* no RX BUFFER allocated */ return -ENOMEM; if (unlikely(atl1_configure(adapter))) { err = -EIO; goto err_up; } err = pci_enable_msi(adapter->pdev); if (err) { if (netif_msg_ifup(adapter)) dev_info(&adapter->pdev->dev, "Unable to enable MSI: %d\n", err); irq_flags |= IRQF_SHARED; } err = request_irq(adapter->pdev->irq, atl1_intr, irq_flags, netdev->name, netdev); if (unlikely(err)) goto err_up; napi_enable(&adapter->napi); atlx_irq_enable(adapter); atl1_check_link(adapter); netif_start_queue(netdev); return 0; err_up: pci_disable_msi(adapter->pdev); /* free rx_buffers */ atl1_clean_rx_ring(adapter); return err; } static void atl1_down(struct atl1_adapter *adapter) { struct net_device *netdev = adapter->netdev; napi_disable(&adapter->napi); netif_stop_queue(netdev); del_timer_sync(&adapter->phy_config_timer); adapter->phy_timer_pending = false; atlx_irq_disable(adapter); free_irq(adapter->pdev->irq, netdev); pci_disable_msi(adapter->pdev); atl1_reset_hw(&adapter->hw); adapter->cmb.cmb->int_stats = 0; adapter->link_speed = SPEED_0; adapter->link_duplex = -1; netif_carrier_off(netdev); atl1_clean_tx_ring(adapter); atl1_clean_rx_ring(adapter); } static void atl1_reset_dev_task(struct work_struct *work) { struct atl1_adapter *adapter = container_of(work, struct atl1_adapter, reset_dev_task); struct net_device *netdev = adapter->netdev; netif_device_detach(netdev); atl1_down(adapter); atl1_up(adapter); netif_device_attach(netdev); } /** * atl1_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure * @new_mtu: new value for maximum frame size * * Returns 0 on success, negative on failure */ static int atl1_change_mtu(struct net_device *netdev, int new_mtu) { struct atl1_adapter *adapter = netdev_priv(netdev); int old_mtu = netdev->mtu; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); return -EINVAL; } adapter->hw.max_frame_size = max_frame; adapter->hw.tx_jumbo_task_th = (max_frame + 7) >> 3; adapter->rx_buffer_len = (max_frame + 7) & ~7; adapter->hw.rx_jumbo_th = adapter->rx_buffer_len / 8; netdev->mtu = new_mtu; if ((old_mtu != new_mtu) && netif_running(netdev)) { atl1_down(adapter); atl1_up(adapter); } return 0; } /** * atl1_open - Called when a network interface is made active * @netdev: network interface device structure * * Returns 0 on success, negative value on failure * * The open entry point is called when a network interface is made * active by the system (IFF_UP). At this point all resources needed * for transmit and receive operations are allocated, the interrupt * handler is registered with the OS, the watchdog timer is started, * and the stack is notified that the interface is ready. */ static int atl1_open(struct net_device *netdev) { struct atl1_adapter *adapter = netdev_priv(netdev); int err; netif_carrier_off(netdev); /* allocate transmit descriptors */ err = atl1_setup_ring_resources(adapter); if (err) return err; err = atl1_up(adapter); if (err) goto err_up; return 0; err_up: atl1_reset(adapter); return err; } /** * atl1_close - Disables a network interface * @netdev: network interface device structure * * Returns 0, this is not allowed to fail * * The close entry point is called when an interface is de-activated * by the OS. The hardware is still under the drivers control, but * needs to be disabled. A global MAC reset is issued to stop the * hardware, and all transmit and receive resources are freed. */ static int atl1_close(struct net_device *netdev) { struct atl1_adapter *adapter = netdev_priv(netdev); atl1_down(adapter); atl1_free_ring_resources(adapter); return 0; } #ifdef CONFIG_PM static int atl1_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; u32 ctrl = 0; u32 wufc = adapter->wol; u32 val; u16 speed; u16 duplex; netif_device_detach(netdev); if (netif_running(netdev)) atl1_down(adapter); atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); val = ctrl & BMSR_LSTATUS; if (val) wufc &= ~ATLX_WUFC_LNKC; if (!wufc) goto disable_wol; if (val) { val = atl1_get_speed_and_duplex(hw, &speed, &duplex); if (val) { if (netif_msg_ifdown(adapter)) dev_printk(KERN_DEBUG, &pdev->dev, "error getting speed/duplex\n"); goto disable_wol; } ctrl = 0; /* enable magic packet WOL */ if (wufc & ATLX_WUFC_MAG) ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN); iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL); ioread32(hw->hw_addr + REG_WOL_CTRL); /* configure the mac */ ctrl = MAC_CTRL_RX_EN; ctrl |= ((u32)((speed == SPEED_1000) ? MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) << MAC_CTRL_SPEED_SHIFT); if (duplex == FULL_DUPLEX) ctrl |= MAC_CTRL_DUPLX; ctrl |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); __atlx_vlan_mode(netdev->features, &ctrl); if (wufc & ATLX_WUFC_MAG) ctrl |= MAC_CTRL_BC_EN; iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL); ioread32(hw->hw_addr + REG_MAC_CTRL); /* poke the PHY */ ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC); ioread32(hw->hw_addr + REG_PCIE_PHYMISC); } else { ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN); iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL); ioread32(hw->hw_addr + REG_WOL_CTRL); iowrite32(0, hw->hw_addr + REG_MAC_CTRL); ioread32(hw->hw_addr + REG_MAC_CTRL); hw->phy_configured = false; } return 0; disable_wol: iowrite32(0, hw->hw_addr + REG_WOL_CTRL); ioread32(hw->hw_addr + REG_WOL_CTRL); ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC); ioread32(hw->hw_addr + REG_PCIE_PHYMISC); hw->phy_configured = false; return 0; } static int atl1_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct atl1_adapter *adapter = netdev_priv(netdev); iowrite32(0, adapter->hw.hw_addr + REG_WOL_CTRL); atl1_reset_hw(&adapter->hw); if (netif_running(netdev)) { adapter->cmb.cmb->int_stats = 0; atl1_up(adapter); } netif_device_attach(netdev); return 0; } compat_pci_suspend(atl1_suspend) compat_pci_resume(atl1_resume) static SIMPLE_DEV_PM_OPS(atl1_pm_ops, atl1_suspend, atl1_resume); #define ATL1_PM_OPS (&atl1_pm_ops) #else static int atl1_suspend(struct device *dev) { return 0; } #define ATL1_PM_OPS NULL #endif static void atl1_shutdown(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1_adapter *adapter = netdev_priv(netdev); atl1_suspend(&pdev->dev); pci_wake_from_d3(pdev, adapter->wol); pci_set_power_state(pdev, PCI_D3hot); } #ifdef CONFIG_NET_POLL_CONTROLLER static void atl1_poll_controller(struct net_device *netdev) { disable_irq(netdev->irq); atl1_intr(netdev->irq, netdev); enable_irq(netdev->irq); } #endif static const struct net_device_ops atl1_netdev_ops = { .ndo_open = atl1_open, .ndo_stop = atl1_close, .ndo_start_xmit = atl1_xmit_frame, .ndo_set_rx_mode = atlx_set_multi, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = atl1_set_mac, .ndo_change_mtu = atl1_change_mtu, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atlx_fix_features, .ndo_set_features = atlx_set_features, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_do_ioctl = atlx_ioctl, .ndo_tx_timeout = atlx_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = atl1_poll_controller, #endif }; /** * atl1_probe - Device Initialization Routine * @pdev: PCI device information struct * @ent: entry in atl1_pci_tbl * * Returns 0 on success, negative on failure * * atl1_probe initializes an adapter identified by a pci_dev structure. * The OS initialization, configuring of the adapter private structure, * and a hardware reset occur. */ static int __devinit atl1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct atl1_adapter *adapter; static int cards_found = 0; int err; err = pci_enable_device(pdev); if (err) return err; /* * The atl1 chip can DMA to 64-bit addresses, but it uses a single * shared register for the high 32 bits, so only a single, aligned, * 4 GB physical address range can be used at a time. * * Supporting 64-bit DMA on this hardware is more trouble than it's * worth. It is far easier to limit to 32-bit DMA than update * various kernel subsystems to support the mechanics required by a * fixed-high-32-bit system. */ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { dev_err(&pdev->dev, "no usable DMA configuration\n"); goto err_dma; } /* * Mark all PCI regions associated with PCI device * pdev as being reserved by owner atl1_driver_name */ err = pci_request_regions(pdev, ATLX_DRIVER_NAME); if (err) goto err_request_regions; /* * Enables bus-mastering on the device and calls * pcibios_set_master to do the needed arch specific settings */ pci_set_master(pdev); netdev = alloc_etherdev(sizeof(struct atl1_adapter)); if (!netdev) { err = -ENOMEM; goto err_alloc_etherdev; } SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); adapter = netdev_priv(netdev); adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; adapter->msg_enable = netif_msg_init(debug, atl1_default_msg); adapter->hw.hw_addr = pci_iomap(pdev, 0, 0); if (!adapter->hw.hw_addr) { err = -EIO; goto err_pci_iomap; } /* get device revision number */ adapter->hw.dev_rev = ioread16(adapter->hw.hw_addr + (REG_MASTER_CTRL + 2)); if (netif_msg_probe(adapter)) dev_info(&pdev->dev, "version %s\n", ATLX_DRIVER_VERSION); /* set default ring resource counts */ adapter->rfd_ring.count = adapter->rrd_ring.count = ATL1_DEFAULT_RFD; adapter->tpd_ring.count = ATL1_DEFAULT_TPD; adapter->mii.dev = netdev; adapter->mii.mdio_read = mdio_read; adapter->mii.mdio_write = mdio_write; adapter->mii.phy_id_mask = 0x1f; adapter->mii.reg_num_mask = 0x1f; netdev_attach_ops(netdev, &atl1_netdev_ops); netdev->watchdog_timeo = 5 * HZ; netif_napi_add(netdev, &adapter->napi, atl1_rings_clean, 64); netdev->ethtool_ops = &atl1_ethtool_ops; adapter->bd_number = cards_found; /* setup the private structure */ err = atl1_sw_init(adapter); if (err) goto err_common; netdev->features = NETIF_F_HW_CSUM; netdev->features |= NETIF_F_SG; netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_VLAN_RX; /* is this valid? see atl1_setup_mac_ctrl() */ netdev->features |= NETIF_F_RXCSUM; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /* * patch for some L1 of old version, * the final version of L1 may not need these * patches */ /* atl1_pcie_patch(adapter); */ /* really reset GPHY core */ iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE); /* * reset the controller to * put the device in a known good starting state */ if (atl1_reset_hw(&adapter->hw)) { err = -EIO; goto err_common; } /* copy the MAC address out of the EEPROM */ if (atl1_read_mac_addr(&adapter->hw)) { /* mark random mac */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type |= NET_ADDR_RANDOM; #endif } memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); if (!is_valid_ether_addr(netdev->dev_addr)) { err = -EIO; goto err_common; } atl1_check_options(adapter); /* pre-init the MAC, and setup link */ err = atl1_init_hw(&adapter->hw); if (err) { err = -EIO; goto err_common; } atl1_pcie_patch(adapter); /* assume we have no link for now */ netif_carrier_off(netdev); setup_timer(&adapter->phy_config_timer, atl1_phy_config, (unsigned long)adapter); adapter->phy_timer_pending = false; INIT_WORK(&adapter->reset_dev_task, atl1_reset_dev_task); INIT_WORK(&adapter->link_chg_task, atlx_link_chg_task); err = register_netdev(netdev); if (err) goto err_common; cards_found++; atl1_via_workaround(adapter); return 0; err_common: pci_iounmap(pdev, adapter->hw.hw_addr); err_pci_iomap: free_netdev(netdev); err_alloc_etherdev: pci_release_regions(pdev); err_dma: err_request_regions: pci_disable_device(pdev); return err; } /** * atl1_remove - Device Removal Routine * @pdev: PCI device information struct * * atl1_remove is called by the PCI subsystem to alert the driver * that it should release a PCI device. The could be caused by a * Hot-Plug event, or because the driver is going to be removed from * memory. */ static void __devexit atl1_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1_adapter *adapter; /* Device not available. Return. */ if (!netdev) return; adapter = netdev_priv(netdev); /* * Some atl1 boards lack persistent storage for their MAC, and get it * from the BIOS during POST. If we've been messing with the MAC * address, we need to save the permanent one. */ if (memcmp(adapter->hw.mac_addr, adapter->hw.perm_mac_addr, ETH_ALEN)) { memcpy(adapter->hw.mac_addr, adapter->hw.perm_mac_addr, ETH_ALEN); atl1_set_mac_addr(&adapter->hw); } iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE); unregister_netdev(netdev); pci_iounmap(pdev, adapter->hw.hw_addr); pci_release_regions(pdev); free_netdev(netdev); pci_disable_device(pdev); } static struct pci_driver atl1_driver = { .name = ATLX_DRIVER_NAME, .id_table = atl1_pci_tbl, .probe = atl1_probe, .remove = __devexit_p(atl1_remove), .shutdown = atl1_shutdown, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATL1_PM_OPS, #elif defined(CONFIG_PM_SLEEP) .suspend = atl1_suspend_compat, .resume = atl1_resume_compat, #endif }; /** * atl1_exit_module - Driver Exit Cleanup Routine * * atl1_exit_module is called just before the driver is removed * from memory. */ static void __exit atl1_exit_module(void) { pci_unregister_driver(&atl1_driver); } /** * atl1_init_module - Driver Registration Routine * * atl1_init_module is the first routine called when the driver is * loaded. All it does is register with the PCI subsystem. */ static int __init atl1_init_module(void) { return pci_register_driver(&atl1_driver); } module_init(atl1_init_module); module_exit(atl1_exit_module); struct atl1_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; int stat_offset; }; #define ATL1_STAT(m) \ sizeof(((struct atl1_adapter *)0)->m), offsetof(struct atl1_adapter, m) static struct atl1_stats atl1_gstrings_stats[] = { {"rx_packets", ATL1_STAT(soft_stats.rx_packets)}, {"tx_packets", ATL1_STAT(soft_stats.tx_packets)}, {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)}, {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)}, {"rx_errors", ATL1_STAT(soft_stats.rx_errors)}, {"tx_errors", ATL1_STAT(soft_stats.tx_errors)}, {"multicast", ATL1_STAT(soft_stats.multicast)}, {"collisions", ATL1_STAT(soft_stats.collisions)}, {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)}, {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)}, {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)}, {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)}, {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)}, {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)}, {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)}, {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)}, {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)}, {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)}, {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)}, {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)}, {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)}, {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)}, {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)}, {"tx_underun", ATL1_STAT(soft_stats.tx_underun)}, {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)}, {"tx_pause", ATL1_STAT(soft_stats.tx_pause)}, {"rx_pause", ATL1_STAT(soft_stats.rx_pause)}, {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)}, {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)} }; static void atl1_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct atl1_adapter *adapter = netdev_priv(netdev); int i; char *p; for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) { p = (char *)adapter+atl1_gstrings_stats[i].stat_offset; data[i] = (atl1_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } } static int atl1_get_sset_count(struct net_device *netdev, int sset) { switch (sset) { case ETH_SS_STATS: return ARRAY_SIZE(atl1_gstrings_stats); default: return -EOPNOTSUPP; } } static int atl1_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); ecmd->advertising = ADVERTISED_TP; if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) { ecmd->advertising |= ADVERTISED_Autoneg; if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) { ecmd->advertising |= ADVERTISED_Autoneg; ecmd->advertising |= (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Full); } else ecmd->advertising |= (ADVERTISED_1000baseT_Full); } ecmd->port = PORT_TP; ecmd->phy_address = 0; ecmd->transceiver = XCVR_INTERNAL; if (netif_carrier_ok(adapter->netdev)) { u16 link_speed, link_duplex; atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex); ethtool_cmd_speed_set(ecmd, link_speed); if (link_duplex == FULL_DUPLEX) ecmd->duplex = DUPLEX_FULL; else ecmd->duplex = DUPLEX_HALF; } else { ethtool_cmd_speed_set(ecmd, -1); ecmd->duplex = -1; } if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) ecmd->autoneg = AUTONEG_ENABLE; else ecmd->autoneg = AUTONEG_DISABLE; return 0; } static int atl1_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; u16 phy_data; int ret_val = 0; u16 old_media_type = hw->media_type; if (netif_running(adapter->netdev)) { if (netif_msg_link(adapter)) dev_dbg(&adapter->pdev->dev, "ethtool shutting down adapter\n"); atl1_down(adapter); } if (ecmd->autoneg == AUTONEG_ENABLE) hw->media_type = MEDIA_TYPE_AUTO_SENSOR; else { u32 speed = ethtool_cmd_speed(ecmd); if (speed == SPEED_1000) { if (ecmd->duplex != DUPLEX_FULL) { if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "1000M half is invalid\n"); ret_val = -EINVAL; goto exit_sset; } hw->media_type = MEDIA_TYPE_1000M_FULL; } else if (speed == SPEED_100) { if (ecmd->duplex == DUPLEX_FULL) hw->media_type = MEDIA_TYPE_100M_FULL; else hw->media_type = MEDIA_TYPE_100M_HALF; } else { if (ecmd->duplex == DUPLEX_FULL) hw->media_type = MEDIA_TYPE_10M_FULL; else hw->media_type = MEDIA_TYPE_10M_HALF; } } switch (hw->media_type) { case MEDIA_TYPE_AUTO_SENSOR: ecmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_TP; break; case MEDIA_TYPE_1000M_FULL: ecmd->advertising = ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_TP; break; default: ecmd->advertising = 0; break; } if (atl1_phy_setup_autoneg_adv(hw)) { ret_val = -EINVAL; if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "invalid ethtool speed/duplex setting\n"); goto exit_sset; } if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; else { switch (hw->media_type) { case MEDIA_TYPE_100M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_100M_HALF: phy_data = MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_10M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; break; default: /* MEDIA_TYPE_10M_HALF: */ phy_data = MII_CR_SPEED_10 | MII_CR_RESET; break; } } atl1_write_phy_reg(hw, MII_BMCR, phy_data); exit_sset: if (ret_val) hw->media_type = old_media_type; if (netif_running(adapter->netdev)) { if (netif_msg_link(adapter)) dev_dbg(&adapter->pdev->dev, "ethtool starting adapter\n"); atl1_up(adapter); } else if (!ret_val) { if (netif_msg_link(adapter)) dev_dbg(&adapter->pdev->dev, "ethtool resetting adapter\n"); atl1_reset(adapter); } return ret_val; } static void atl1_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct atl1_adapter *adapter = netdev_priv(netdev); strlcpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, ATLX_DRIVER_VERSION, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->eedump_len = ATL1_EEDUMP_LEN; } static void atl1_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl1_adapter *adapter = netdev_priv(netdev); wol->supported = WAKE_MAGIC; wol->wolopts = 0; if (adapter->wol & ATLX_WUFC_MAG) wol->wolopts |= WAKE_MAGIC; } static int atl1_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl1_adapter *adapter = netdev_priv(netdev); if (wol->wolopts & (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP | WAKE_MAGICSECURE)) return -EOPNOTSUPP; adapter->wol = 0; if (wol->wolopts & WAKE_MAGIC) adapter->wol |= ATLX_WUFC_MAG; device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); return 0; } static u32 atl1_get_msglevel(struct net_device *netdev) { struct atl1_adapter *adapter = netdev_priv(netdev); return adapter->msg_enable; } static void atl1_set_msglevel(struct net_device *netdev, u32 value) { struct atl1_adapter *adapter = netdev_priv(netdev); adapter->msg_enable = value; } static int atl1_get_regs_len(struct net_device *netdev) { return ATL1_REG_COUNT * sizeof(u32); } static void atl1_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; unsigned int i; u32 *regbuf = p; for (i = 0; i < ATL1_REG_COUNT; i++) { /* * This switch statement avoids reserved regions * of register space. */ switch (i) { case 6 ... 9: case 14: case 29 ... 31: case 34 ... 63: case 75 ... 127: case 136 ... 1023: case 1027 ... 1087: case 1091 ... 1151: case 1194 ... 1195: case 1200 ... 1201: case 1206 ... 1213: case 1216 ... 1279: case 1290 ... 1311: case 1323 ... 1343: case 1358 ... 1359: case 1368 ... 1375: case 1378 ... 1383: case 1388 ... 1391: case 1393 ... 1395: case 1402 ... 1403: case 1410 ... 1471: case 1522 ... 1535: /* reserved region; don't read it */ regbuf[i] = 0; break; default: /* unreserved region */ regbuf[i] = ioread32(hw->hw_addr + (i * sizeof(u32))); } } } static void atl1_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_tpd_ring *txdr = &adapter->tpd_ring; struct atl1_rfd_ring *rxdr = &adapter->rfd_ring; ring->rx_max_pending = ATL1_MAX_RFD; ring->tx_max_pending = ATL1_MAX_TPD; ring->rx_pending = rxdr->count; ring->tx_pending = txdr->count; } static int atl1_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_tpd_ring *tpdr = &adapter->tpd_ring; struct atl1_rrd_ring *rrdr = &adapter->rrd_ring; struct atl1_rfd_ring *rfdr = &adapter->rfd_ring; struct atl1_tpd_ring tpd_old, tpd_new; struct atl1_rfd_ring rfd_old, rfd_new; struct atl1_rrd_ring rrd_old, rrd_new; struct atl1_ring_header rhdr_old, rhdr_new; struct atl1_smb smb; struct atl1_cmb cmb; int err; tpd_old = adapter->tpd_ring; rfd_old = adapter->rfd_ring; rrd_old = adapter->rrd_ring; rhdr_old = adapter->ring_header; if (netif_running(adapter->netdev)) atl1_down(adapter); rfdr->count = (u16) max(ring->rx_pending, (u32) ATL1_MIN_RFD); rfdr->count = rfdr->count > ATL1_MAX_RFD ? ATL1_MAX_RFD : rfdr->count; rfdr->count = (rfdr->count + 3) & ~3; rrdr->count = rfdr->count; tpdr->count = (u16) max(ring->tx_pending, (u32) ATL1_MIN_TPD); tpdr->count = tpdr->count > ATL1_MAX_TPD ? ATL1_MAX_TPD : tpdr->count; tpdr->count = (tpdr->count + 3) & ~3; if (netif_running(adapter->netdev)) { /* try to get new resources before deleting old */ err = atl1_setup_ring_resources(adapter); if (err) goto err_setup_ring; /* * save the new, restore the old in order to free it, * then restore the new back again */ rfd_new = adapter->rfd_ring; rrd_new = adapter->rrd_ring; tpd_new = adapter->tpd_ring; rhdr_new = adapter->ring_header; adapter->rfd_ring = rfd_old; adapter->rrd_ring = rrd_old; adapter->tpd_ring = tpd_old; adapter->ring_header = rhdr_old; /* * Save SMB and CMB, since atl1_free_ring_resources * will clear them. */ smb = adapter->smb; cmb = adapter->cmb; atl1_free_ring_resources(adapter); adapter->rfd_ring = rfd_new; adapter->rrd_ring = rrd_new; adapter->tpd_ring = tpd_new; adapter->ring_header = rhdr_new; adapter->smb = smb; adapter->cmb = cmb; err = atl1_up(adapter); if (err) return err; } return 0; err_setup_ring: adapter->rfd_ring = rfd_old; adapter->rrd_ring = rrd_old; adapter->tpd_ring = tpd_old; adapter->ring_header = rhdr_old; atl1_up(adapter); return err; } static void atl1_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *epause) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) { epause->autoneg = AUTONEG_ENABLE; } else { epause->autoneg = AUTONEG_DISABLE; } epause->rx_pause = 1; epause->tx_pause = 1; } static int atl1_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *epause) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) { epause->autoneg = AUTONEG_ENABLE; } else { epause->autoneg = AUTONEG_DISABLE; } epause->rx_pause = 1; epause->tx_pause = 1; return 0; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) /* FIXME: is this right? -- CHS */ static u32 atl1_get_rx_csum(struct net_device *netdev) { return 1; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ static void atl1_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { u8 *p = data; int i; switch (stringset) { case ETH_SS_STATS: for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) { memcpy(p, atl1_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } break; } } static int atl1_nway_reset(struct net_device *netdev) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; if (netif_running(netdev)) { u16 phy_data; atl1_down(adapter); if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) { phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; } else { switch (hw->media_type) { case MEDIA_TYPE_100M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_100M_HALF: phy_data = MII_CR_SPEED_100 | MII_CR_RESET; break; case MEDIA_TYPE_10M_FULL: phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; break; default: /* MEDIA_TYPE_10M_HALF */ phy_data = MII_CR_SPEED_10 | MII_CR_RESET; } } atl1_write_phy_reg(hw, MII_BMCR, phy_data); atl1_up(adapter); } return 0; } static const struct ethtool_ops atl1_ethtool_ops = { .get_settings = atl1_get_settings, .set_settings = atl1_set_settings, .get_drvinfo = atl1_get_drvinfo, .get_wol = atl1_get_wol, .set_wol = atl1_set_wol, .get_msglevel = atl1_get_msglevel, .set_msglevel = atl1_set_msglevel, .get_regs_len = atl1_get_regs_len, .get_regs = atl1_get_regs, .get_ringparam = atl1_get_ringparam, .set_ringparam = atl1_set_ringparam, .get_pauseparam = atl1_get_pauseparam, .set_pauseparam = atl1_set_pauseparam, .get_link = ethtool_op_get_link, .get_strings = atl1_get_strings, .nway_reset = atl1_nway_reset, .get_ethtool_stats = atl1_get_ethtool_stats, .get_sset_count = atl1_get_sset_count, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) .get_rx_csum = atl1_get_rx_csum, .set_tx_csum = ethtool_op_set_tx_hw_csum, .set_sg = ethtool_op_set_sg, .set_tso = ethtool_op_set_tso, #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/atlx.c0000644000175000017500000002033712026211315024200 0ustar mcgrofmcgrof/* atlx.c -- common functions for Attansic network drivers * * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2006 - 2007 Chris Snook * Copyright(c) 2006 - 2008 Jay Cliburn * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Including this file like a header is a temporary hack, I promise. -- CHS */ #ifndef ATLX_C #define ATLX_C #include #include #include #include #include #include #include #include #include #include #include #include "atlx.h" static s32 atlx_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data); static u32 atlx_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr); static void atlx_set_mac_addr(struct atl1_hw *hw); static struct atlx_spi_flash_dev flash_table[] = { /* MFR_NAME WRSR READ PRGM WREN WRDI RDSR RDID SEC_ERS CHIP_ERS */ {"Atmel", 0x00, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62}, {"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60}, {"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7}, }; static int atlx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { switch (cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: return atlx_mii_ioctl(netdev, ifr, cmd); default: return -EOPNOTSUPP; } } /** * atlx_set_mac - Change the Ethernet Address of the NIC * @netdev: network interface device structure * @p: pointer to an address structure * * Returns 0 on success, negative on failure */ static int atlx_set_mac(struct net_device *netdev, void *p) { struct atlx_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; if (netif_running(netdev)) return -EBUSY; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type &= ~NET_ADDR_RANDOM; #endif atlx_set_mac_addr(&adapter->hw); return 0; } static void atlx_check_for_link(struct atlx_adapter *adapter) { struct net_device *netdev = adapter->netdev; u16 phy_data = 0; spin_lock(&adapter->lock); adapter->phy_timer_pending = false; atlx_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); atlx_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); spin_unlock(&adapter->lock); /* notify upper layer link down ASAP */ if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */ if (netif_carrier_ok(netdev)) { /* old link state: Up */ dev_info(&adapter->pdev->dev, "%s link is down\n", netdev->name); adapter->link_speed = SPEED_0; netif_carrier_off(netdev); } } schedule_work(&adapter->link_chg_task); } /** * atlx_set_multi - Multicast and Promiscuous mode set * @netdev: network interface device structure * * The set_multi entry point is called whenever the multicast address * list or the network interface flags are updated. This routine is * responsible for configuring the hardware for proper multicast, * promiscuous mode, and all-multi behavior. */ static void atlx_set_multi(struct net_device *netdev) { struct atlx_adapter *adapter = netdev_priv(netdev); struct atlx_hw *hw = &adapter->hw; struct netdev_hw_addr *ha; u32 rctl; u32 hash_value; /* Check for Promiscuous and All Multicast modes */ rctl = ioread32(hw->hw_addr + REG_MAC_CTRL); if (netdev->flags & IFF_PROMISC) rctl |= MAC_CTRL_PROMIS_EN; else if (netdev->flags & IFF_ALLMULTI) { rctl |= MAC_CTRL_MC_ALL_EN; rctl &= ~MAC_CTRL_PROMIS_EN; } else rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); iowrite32(rctl, hw->hw_addr + REG_MAC_CTRL); /* clear the old settings from the multicast hash table */ iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE); iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2)); /* compute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atlx_hash_mc_addr(hw, ha->addr); #else hash_value = atlx_hash_mc_addr(hw, ha->dmi_addr); #endif atlx_hash_set(hw, hash_value); } } static inline void atlx_imr_set(struct atlx_adapter *adapter, unsigned int imr) { iowrite32(imr, adapter->hw.hw_addr + REG_IMR); ioread32(adapter->hw.hw_addr + REG_IMR); } /** * atlx_irq_enable - Enable default interrupt generation settings * @adapter: board private structure */ static void atlx_irq_enable(struct atlx_adapter *adapter) { atlx_imr_set(adapter, IMR_NORMAL_MASK); adapter->int_enabled = true; } /** * atlx_irq_disable - Mask off interrupt generation on the NIC * @adapter: board private structure */ static void atlx_irq_disable(struct atlx_adapter *adapter) { adapter->int_enabled = false; atlx_imr_set(adapter, 0); synchronize_irq(adapter->pdev->irq); } static void atlx_clear_phy_int(struct atlx_adapter *adapter) { u16 phy_data; unsigned long flags; spin_lock_irqsave(&adapter->lock, flags); atlx_read_phy_reg(&adapter->hw, 19, &phy_data); spin_unlock_irqrestore(&adapter->lock, flags); } /** * atlx_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure */ static void atlx_tx_timeout(struct net_device *netdev) { struct atlx_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ schedule_work(&adapter->reset_dev_task); } /* * atlx_link_chg_task - deal with link change event Out of interrupt context */ static void atlx_link_chg_task(struct work_struct *work) { struct atlx_adapter *adapter; unsigned long flags; adapter = container_of(work, struct atlx_adapter, link_chg_task); spin_lock_irqsave(&adapter->lock, flags); atlx_check_link(adapter); spin_unlock_irqrestore(&adapter->lock, flags); } static void __atlx_vlan_mode(netdev_features_t features, u32 *ctrl) { if (features & NETIF_F_HW_VLAN_RX) { /* enable VLAN tag insert/strip */ *ctrl |= MAC_CTRL_RMV_VLAN; } else { /* disable VLAN tag insert/strip */ *ctrl &= ~MAC_CTRL_RMV_VLAN; } } static void atlx_vlan_mode(struct net_device *netdev, netdev_features_t features) { struct atlx_adapter *adapter = netdev_priv(netdev); unsigned long flags; u32 ctrl; spin_lock_irqsave(&adapter->lock, flags); /* atlx_irq_disable(adapter); FIXME: confirm/remove */ ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL); __atlx_vlan_mode(features, &ctrl); iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL); /* atlx_irq_enable(adapter); FIXME */ spin_unlock_irqrestore(&adapter->lock, flags); } static void atlx_restore_vlan(struct atlx_adapter *adapter) { atlx_vlan_mode(adapter->netdev, adapter->netdev->features); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atlx_fix_features(struct net_device *netdev, netdev_features_t features) { /* * Since there is no support for separate rx/tx vlan accel * enable/disable make sure tx flag is always in same state as rx. */ if (features & NETIF_F_HW_VLAN_RX) features |= NETIF_F_HW_VLAN_TX; else features &= ~NETIF_F_HW_VLAN_TX; return features; } static int atlx_set_features(struct net_device *netdev, netdev_features_t features) { netdev_features_t changed = netdev->features ^ features; if (changed & NETIF_F_HW_VLAN_RX) atlx_vlan_mode(netdev, features); return 0; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ #endif /* ATLX_C */ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/atl2.c0000644000175000017500000024276312026211315024103 0ustar mcgrofmcgrof/* * Copyright(c) 2006 - 2007 Atheros Corporation. All rights reserved. * Copyright(c) 2007 - 2008 Chris Snook * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atl2.h" #define ATL2_DRV_VERSION "2.2.3" static const char atl2_driver_name[] = "atl2"; static const char atl2_driver_string[] = "Atheros(R) L2 Ethernet Driver"; static const char atl2_copyright[] = "Copyright (c) 2007 Atheros Corporation."; static const char atl2_driver_version[] = ATL2_DRV_VERSION; MODULE_AUTHOR("Atheros Corporation , Chris Snook "); MODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(ATL2_DRV_VERSION); /* * atl2_pci_tbl - PCI Device ID Table */ static DEFINE_PCI_DEVICE_TABLE(atl2_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2)}, /* required last entry */ {0,} }; MODULE_DEVICE_TABLE(pci, atl2_pci_tbl); static void atl2_set_ethtool_ops(struct net_device *netdev); static void atl2_check_options(struct atl2_adapter *adapter); /** * atl2_sw_init - Initialize general software structures (struct atl2_adapter) * @adapter: board private structure to initialize * * atl2_sw_init initializes the Adapter private data structure. * Fields are initialized based on PCI device information and * OS network device settings (MTU size). */ static int __devinit atl2_sw_init(struct atl2_adapter *adapter) { struct atl2_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; /* PCI config space info */ hw->vendor_id = pdev->vendor; hw->device_id = pdev->device; hw->subsystem_vendor_id = pdev->subsystem_vendor; hw->subsystem_id = pdev->subsystem_device; hw->revision_id = pdev->revision; pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); adapter->wol = 0; adapter->ict = 50000; /* ~100ms */ adapter->link_speed = SPEED_0; /* hardware init */ adapter->link_duplex = FULL_DUPLEX; hw->phy_configured = false; hw->preamble_len = 7; hw->ipgt = 0x60; hw->min_ifg = 0x50; hw->ipgr1 = 0x40; hw->ipgr2 = 0x60; hw->retry_buf = 2; hw->max_retry = 0xf; hw->lcol = 0x37; hw->jam_ipg = 7; hw->fc_rxd_hi = 0; hw->fc_rxd_lo = 0; hw->max_frame_size = adapter->netdev->mtu; spin_lock_init(&adapter->stats_lock); set_bit(__ATL2_DOWN, &adapter->flags); return 0; } /** * atl2_set_multi - Multicast and Promiscuous mode set * @netdev: network interface device structure * * The set_multi entry point is called whenever the multicast address * list or the network interface flags are updated. This routine is * responsible for configuring the hardware for proper multicast, * promiscuous mode, and all-multi behavior. */ static void atl2_set_multi(struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; struct netdev_hw_addr *ha; u32 rctl; u32 hash_value; /* Check for Promiscuous and All Multicast modes */ rctl = ATL2_READ_REG(hw, REG_MAC_CTRL); if (netdev->flags & IFF_PROMISC) { rctl |= MAC_CTRL_PROMIS_EN; } else if (netdev->flags & IFF_ALLMULTI) { rctl |= MAC_CTRL_MC_ALL_EN; rctl &= ~MAC_CTRL_PROMIS_EN; } else rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); ATL2_WRITE_REG(hw, REG_MAC_CTRL, rctl); /* clear the old settings from the multicast hash table */ ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atl2_hash_mc_addr(hw, ha->addr); #else hash_value = atl2_hash_mc_addr(hw, ha->dmi_addr); #endif atl2_hash_set(hw, hash_value); } } static void init_ring_ptrs(struct atl2_adapter *adapter) { /* Read / Write Ptr Initialize: */ adapter->txd_write_ptr = 0; atomic_set(&adapter->txd_read_ptr, 0); adapter->rxd_read_ptr = 0; adapter->rxd_write_ptr = 0; atomic_set(&adapter->txs_write_ptr, 0); adapter->txs_next_clear = 0; } /** * atl2_configure - Configure Transmit&Receive Unit after Reset * @adapter: board private structure * * Configure the Tx /Rx unit of the MAC after a reset. */ static int atl2_configure(struct atl2_adapter *adapter) { struct atl2_hw *hw = &adapter->hw; u32 value; /* clear interrupt status */ ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0xffffffff); /* set MAC Address */ value = (((u32)hw->mac_addr[2]) << 24) | (((u32)hw->mac_addr[3]) << 16) | (((u32)hw->mac_addr[4]) << 8) | (((u32)hw->mac_addr[5])); ATL2_WRITE_REG(hw, REG_MAC_STA_ADDR, value); value = (((u32)hw->mac_addr[0]) << 8) | (((u32)hw->mac_addr[1])); ATL2_WRITE_REG(hw, (REG_MAC_STA_ADDR+4), value); /* HI base address */ ATL2_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, (u32)((adapter->ring_dma & 0xffffffff00000000ULL) >> 32)); /* LO base address */ ATL2_WRITE_REG(hw, REG_TXD_BASE_ADDR_LO, (u32)(adapter->txd_dma & 0x00000000ffffffffULL)); ATL2_WRITE_REG(hw, REG_TXS_BASE_ADDR_LO, (u32)(adapter->txs_dma & 0x00000000ffffffffULL)); ATL2_WRITE_REG(hw, REG_RXD_BASE_ADDR_LO, (u32)(adapter->rxd_dma & 0x00000000ffffffffULL)); /* element count */ ATL2_WRITE_REGW(hw, REG_TXD_MEM_SIZE, (u16)(adapter->txd_ring_size/4)); ATL2_WRITE_REGW(hw, REG_TXS_MEM_SIZE, (u16)adapter->txs_ring_size); ATL2_WRITE_REGW(hw, REG_RXD_BUF_NUM, (u16)adapter->rxd_ring_size); /* config Internal SRAM */ /* ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_tx_end); ATL2_WRITE_REGW(hw, REG_SRAM_TXRAM_END, sram_rx_end); */ /* config IPG/IFG */ value = (((u32)hw->ipgt & MAC_IPG_IFG_IPGT_MASK) << MAC_IPG_IFG_IPGT_SHIFT) | (((u32)hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) << MAC_IPG_IFG_MIFG_SHIFT) | (((u32)hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) << MAC_IPG_IFG_IPGR1_SHIFT)| (((u32)hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) << MAC_IPG_IFG_IPGR2_SHIFT); ATL2_WRITE_REG(hw, REG_MAC_IPG_IFG, value); /* config Half-Duplex Control */ value = ((u32)hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) | (((u32)hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) << MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) | MAC_HALF_DUPLX_CTRL_EXC_DEF_EN | (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) | (((u32)hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) << MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT); ATL2_WRITE_REG(hw, REG_MAC_HALF_DUPLX_CTRL, value); /* set Interrupt Moderator Timer */ ATL2_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, adapter->imt); ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_ITIMER_EN); /* set Interrupt Clear Timer */ ATL2_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, adapter->ict); /* set MTU */ ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu + ENET_HEADER_SIZE + VLAN_SIZE + ETHERNET_FCS_SIZE); /* 1590 */ ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177); /* flow control */ ATL2_WRITE_REGW(hw, REG_PAUSE_ON_TH, hw->fc_rxd_hi); ATL2_WRITE_REGW(hw, REG_PAUSE_OFF_TH, hw->fc_rxd_lo); /* Init mailbox */ ATL2_WRITE_REGW(hw, REG_MB_TXD_WR_IDX, (u16)adapter->txd_write_ptr); ATL2_WRITE_REGW(hw, REG_MB_RXD_RD_IDX, (u16)adapter->rxd_read_ptr); /* enable DMA read/write */ ATL2_WRITE_REGB(hw, REG_DMAR, DMAR_EN); ATL2_WRITE_REGB(hw, REG_DMAW, DMAW_EN); value = ATL2_READ_REG(&adapter->hw, REG_ISR); if ((value & ISR_PHY_LINKDOWN) != 0) value = 1; /* config failed */ else value = 0; /* clear all interrupt status */ ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0x3fffffff); ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); return value; } /** * atl2_setup_ring_resources - allocate Tx / RX descriptor resources * @adapter: board private structure * * Return 0 on success, negative on failure */ static s32 atl2_setup_ring_resources(struct atl2_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; int size; u8 offset = 0; /* real ring DMA buffer */ adapter->ring_size = size = adapter->txd_ring_size * 1 + 7 + /* dword align */ adapter->txs_ring_size * 4 + 7 + /* dword align */ adapter->rxd_ring_size * 1536 + 127; /* 128bytes align */ adapter->ring_vir_addr = pci_alloc_consistent(pdev, size, &adapter->ring_dma); if (!adapter->ring_vir_addr) return -ENOMEM; memset(adapter->ring_vir_addr, 0, adapter->ring_size); /* Init TXD Ring */ adapter->txd_dma = adapter->ring_dma ; offset = (adapter->txd_dma & 0x7) ? (8 - (adapter->txd_dma & 0x7)) : 0; adapter->txd_dma += offset; adapter->txd_ring = adapter->ring_vir_addr + offset; /* Init TXS Ring */ adapter->txs_dma = adapter->txd_dma + adapter->txd_ring_size; offset = (adapter->txs_dma & 0x7) ? (8 - (adapter->txs_dma & 0x7)) : 0; adapter->txs_dma += offset; adapter->txs_ring = (struct tx_pkt_status *) (((u8 *)adapter->txd_ring) + (adapter->txd_ring_size + offset)); /* Init RXD Ring */ adapter->rxd_dma = adapter->txs_dma + adapter->txs_ring_size * 4; offset = (adapter->rxd_dma & 127) ? (128 - (adapter->rxd_dma & 127)) : 0; if (offset > 7) offset -= 8; else offset += (128 - 8); adapter->rxd_dma += offset; adapter->rxd_ring = (struct rx_desc *) (((u8 *)adapter->txs_ring) + (adapter->txs_ring_size * 4 + offset)); /* * Read / Write Ptr Initialize: * init_ring_ptrs(adapter); */ return 0; } /** * atl2_irq_enable - Enable default interrupt generation settings * @adapter: board private structure */ static inline void atl2_irq_enable(struct atl2_adapter *adapter) { ATL2_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK); ATL2_WRITE_FLUSH(&adapter->hw); } /** * atl2_irq_disable - Mask off interrupt generation on the NIC * @adapter: board private structure */ static inline void atl2_irq_disable(struct atl2_adapter *adapter) { ATL2_WRITE_REG(&adapter->hw, REG_IMR, 0); ATL2_WRITE_FLUSH(&adapter->hw); synchronize_irq(adapter->pdev->irq); } static void __atl2_vlan_mode(netdev_features_t features, u32 *ctrl) { if (features & NETIF_F_HW_VLAN_RX) { /* enable VLAN tag insert/strip */ *ctrl |= MAC_CTRL_RMV_VLAN; } else { /* disable VLAN tag insert/strip */ *ctrl &= ~MAC_CTRL_RMV_VLAN; } } static void atl2_vlan_mode(struct net_device *netdev, netdev_features_t features) { struct atl2_adapter *adapter = netdev_priv(netdev); u32 ctrl; atl2_irq_disable(adapter); ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL); __atl2_vlan_mode(features, &ctrl); ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl); atl2_irq_enable(adapter); } static void atl2_restore_vlan(struct atl2_adapter *adapter) { atl2_vlan_mode(adapter->netdev, adapter->netdev->features); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atl2_fix_features(struct net_device *netdev, netdev_features_t features) { /* * Since there is no support for separate rx/tx vlan accel * enable/disable make sure tx flag is always in same state as rx. */ if (features & NETIF_F_HW_VLAN_RX) features |= NETIF_F_HW_VLAN_TX; else features &= ~NETIF_F_HW_VLAN_TX; return features; } static int atl2_set_features(struct net_device *netdev, netdev_features_t features) { netdev_features_t changed = netdev->features ^ features; if (changed & NETIF_F_HW_VLAN_RX) atl2_vlan_mode(netdev, features); return 0; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ static void atl2_intr_rx(struct atl2_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct rx_desc *rxd; struct sk_buff *skb; do { rxd = adapter->rxd_ring+adapter->rxd_write_ptr; if (!rxd->status.update) break; /* end of tx */ /* clear this flag at once */ rxd->status.update = 0; if (rxd->status.ok && rxd->status.pkt_size >= 60) { int rx_size = (int)(rxd->status.pkt_size - 4); /* alloc new buffer */ skb = netdev_alloc_skb_ip_align(netdev, rx_size); if (NULL == skb) { printk(KERN_WARNING "%s: Mem squeeze, deferring packet.\n", netdev->name); /* * Check that some rx space is free. If not, * free one and mark stats->rx_dropped++. */ netdev->stats.rx_dropped++; break; } memcpy(skb->data, rxd->packet, rx_size); skb_put(skb, rx_size); skb->protocol = eth_type_trans(skb, netdev); if (rxd->status.vlan) { u16 vlan_tag = (rxd->status.vtag>>4) | ((rxd->status.vtag&7) << 13) | ((rxd->status.vtag&8) << 9); __vlan_hwaccel_put_tag(skb, vlan_tag); } netif_rx(skb); netdev->stats.rx_bytes += rx_size; netdev->stats.rx_packets++; } else { netdev->stats.rx_errors++; if (rxd->status.ok && rxd->status.pkt_size <= 60) netdev->stats.rx_length_errors++; if (rxd->status.mcast) netdev->stats.multicast++; if (rxd->status.crc) netdev->stats.rx_crc_errors++; if (rxd->status.align) netdev->stats.rx_frame_errors++; } /* advance write ptr */ if (++adapter->rxd_write_ptr == adapter->rxd_ring_size) adapter->rxd_write_ptr = 0; } while (1); /* update mailbox? */ adapter->rxd_read_ptr = adapter->rxd_write_ptr; ATL2_WRITE_REGW(&adapter->hw, REG_MB_RXD_RD_IDX, adapter->rxd_read_ptr); } static void atl2_intr_tx(struct atl2_adapter *adapter) { struct net_device *netdev = adapter->netdev; u32 txd_read_ptr; u32 txs_write_ptr; struct tx_pkt_status *txs; struct tx_pkt_header *txph; int free_hole = 0; do { txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); txs = adapter->txs_ring + txs_write_ptr; if (!txs->update) break; /* tx stop here */ free_hole = 1; txs->update = 0; if (++txs_write_ptr == adapter->txs_ring_size) txs_write_ptr = 0; atomic_set(&adapter->txs_write_ptr, (int)txs_write_ptr); txd_read_ptr = (u32) atomic_read(&adapter->txd_read_ptr); txph = (struct tx_pkt_header *) (((u8 *)adapter->txd_ring) + txd_read_ptr); if (txph->pkt_size != txs->pkt_size) { struct tx_pkt_status *old_txs = txs; printk(KERN_WARNING "%s: txs packet size not consistent with txd" " txd_:0x%08x, txs_:0x%08x!\n", adapter->netdev->name, *(u32 *)txph, *(u32 *)txs); printk(KERN_WARNING "txd read ptr: 0x%x\n", txd_read_ptr); txs = adapter->txs_ring + txs_write_ptr; printk(KERN_WARNING "txs-behind:0x%08x\n", *(u32 *)txs); if (txs_write_ptr < 2) { txs = adapter->txs_ring + (adapter->txs_ring_size + txs_write_ptr - 2); } else { txs = adapter->txs_ring + (txs_write_ptr - 2); } printk(KERN_WARNING "txs-before:0x%08x\n", *(u32 *)txs); txs = old_txs; } /* 4for TPH */ txd_read_ptr += (((u32)(txph->pkt_size) + 7) & ~3); if (txd_read_ptr >= adapter->txd_ring_size) txd_read_ptr -= adapter->txd_ring_size; atomic_set(&adapter->txd_read_ptr, (int)txd_read_ptr); /* tx statistics: */ if (txs->ok) { netdev->stats.tx_bytes += txs->pkt_size; netdev->stats.tx_packets++; } else netdev->stats.tx_errors++; if (txs->defer) netdev->stats.collisions++; if (txs->abort_col) netdev->stats.tx_aborted_errors++; if (txs->late_col) netdev->stats.tx_window_errors++; if (txs->underun) netdev->stats.tx_fifo_errors++; } while (1); if (free_hole) { if (netif_queue_stopped(adapter->netdev) && netif_carrier_ok(adapter->netdev)) netif_wake_queue(adapter->netdev); } } static void atl2_check_for_link(struct atl2_adapter *adapter) { struct net_device *netdev = adapter->netdev; u16 phy_data = 0; spin_lock(&adapter->stats_lock); atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); atl2_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); spin_unlock(&adapter->stats_lock); /* notify upper layer link down ASAP */ if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */ if (netif_carrier_ok(netdev)) { /* old link state: Up */ printk(KERN_INFO "%s: %s NIC Link is Down\n", atl2_driver_name, netdev->name); adapter->link_speed = SPEED_0; netif_carrier_off(netdev); netif_stop_queue(netdev); } } schedule_work(&adapter->link_chg_task); } static inline void atl2_clear_phy_int(struct atl2_adapter *adapter) { u16 phy_data; spin_lock(&adapter->stats_lock); atl2_read_phy_reg(&adapter->hw, 19, &phy_data); spin_unlock(&adapter->stats_lock); } /** * atl2_intr - Interrupt Handler * @irq: interrupt number * @data: pointer to a network interface device structure */ static irqreturn_t atl2_intr(int irq, void *data) { struct atl2_adapter *adapter = netdev_priv(data); struct atl2_hw *hw = &adapter->hw; u32 status; status = ATL2_READ_REG(hw, REG_ISR); if (0 == status) return IRQ_NONE; /* link event */ if (status & ISR_PHY) atl2_clear_phy_int(adapter); /* clear ISR status, and Enable CMB DMA/Disable Interrupt */ ATL2_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); /* check if PCIE PHY Link down */ if (status & ISR_PHY_LINKDOWN) { if (netif_running(adapter->netdev)) { /* reset MAC */ ATL2_WRITE_REG(hw, REG_ISR, 0); ATL2_WRITE_REG(hw, REG_IMR, 0); ATL2_WRITE_FLUSH(hw); schedule_work(&adapter->reset_task); return IRQ_HANDLED; } } /* check if DMA read/write error? */ if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { ATL2_WRITE_REG(hw, REG_ISR, 0); ATL2_WRITE_REG(hw, REG_IMR, 0); ATL2_WRITE_FLUSH(hw); schedule_work(&adapter->reset_task); return IRQ_HANDLED; } /* link event */ if (status & (ISR_PHY | ISR_MANUAL)) { adapter->netdev->stats.tx_carrier_errors++; atl2_check_for_link(adapter); } /* transmit event */ if (status & ISR_TX_EVENT) atl2_intr_tx(adapter); /* rx exception */ if (status & ISR_RX_EVENT) atl2_intr_rx(adapter); /* re-enable Interrupt */ ATL2_WRITE_REG(&adapter->hw, REG_ISR, 0); return IRQ_HANDLED; } static int atl2_request_irq(struct atl2_adapter *adapter) { struct net_device *netdev = adapter->netdev; int flags, err = 0; flags = IRQF_SHARED; adapter->have_msi = true; err = pci_enable_msi(adapter->pdev); if (err) adapter->have_msi = false; if (adapter->have_msi) flags &= ~IRQF_SHARED; return request_irq(adapter->pdev->irq, atl2_intr, flags, netdev->name, netdev); } /** * atl2_free_ring_resources - Free Tx / RX descriptor Resources * @adapter: board private structure * * Free all transmit software resources */ static void atl2_free_ring_resources(struct atl2_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; pci_free_consistent(pdev, adapter->ring_size, adapter->ring_vir_addr, adapter->ring_dma); } /** * atl2_open - Called when a network interface is made active * @netdev: network interface device structure * * Returns 0 on success, negative value on failure * * The open entry point is called when a network interface is made * active by the system (IFF_UP). At this point all resources needed * for transmit and receive operations are allocated, the interrupt * handler is registered with the OS, the watchdog timer is started, * and the stack is notified that the interface is ready. */ static int atl2_open(struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); int err; u32 val; /* disallow open during test */ if (test_bit(__ATL2_TESTING, &adapter->flags)) return -EBUSY; /* allocate transmit descriptors */ err = atl2_setup_ring_resources(adapter); if (err) return err; err = atl2_init_hw(&adapter->hw); if (err) { err = -EIO; goto err_init_hw; } /* hardware has been reset, we need to reload some things */ atl2_set_multi(netdev); init_ring_ptrs(adapter); atl2_restore_vlan(adapter); if (atl2_configure(adapter)) { err = -EIO; goto err_config; } err = atl2_request_irq(adapter); if (err) goto err_req_irq; clear_bit(__ATL2_DOWN, &adapter->flags); mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 4*HZ)); val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val | MASTER_CTRL_MANUAL_INT); atl2_irq_enable(adapter); return 0; err_init_hw: err_req_irq: err_config: atl2_free_ring_resources(adapter); atl2_reset_hw(&adapter->hw); return err; } static void atl2_down(struct atl2_adapter *adapter) { struct net_device *netdev = adapter->netdev; /* signal that we're down so the interrupt handler does not * reschedule our watchdog timer */ set_bit(__ATL2_DOWN, &adapter->flags); netif_tx_disable(netdev); /* reset MAC to disable all RX/TX */ atl2_reset_hw(&adapter->hw); msleep(1); atl2_irq_disable(adapter); del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_config_timer); clear_bit(0, &adapter->cfg_phy); netif_carrier_off(netdev); adapter->link_speed = SPEED_0; adapter->link_duplex = -1; } static void atl2_free_irq(struct atl2_adapter *adapter) { struct net_device *netdev = adapter->netdev; free_irq(adapter->pdev->irq, netdev); #ifdef CONFIG_PCI_MSI if (adapter->have_msi) pci_disable_msi(adapter->pdev); #endif } /** * atl2_close - Disables a network interface * @netdev: network interface device structure * * Returns 0, this is not allowed to fail * * The close entry point is called when an interface is de-activated * by the OS. The hardware is still under the drivers control, but * needs to be disabled. A global MAC reset is issued to stop the * hardware, and all transmit and receive resources are freed. */ static int atl2_close(struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); atl2_down(adapter); atl2_free_irq(adapter); atl2_free_ring_resources(adapter); return 0; } static inline int TxsFreeUnit(struct atl2_adapter *adapter) { u32 txs_write_ptr = (u32) atomic_read(&adapter->txs_write_ptr); return (adapter->txs_next_clear >= txs_write_ptr) ? (int) (adapter->txs_ring_size - adapter->txs_next_clear + txs_write_ptr - 1) : (int) (txs_write_ptr - adapter->txs_next_clear - 1); } static inline int TxdFreeBytes(struct atl2_adapter *adapter) { u32 txd_read_ptr = (u32)atomic_read(&adapter->txd_read_ptr); return (adapter->txd_write_ptr >= txd_read_ptr) ? (int) (adapter->txd_ring_size - adapter->txd_write_ptr + txd_read_ptr - 1) : (int) (txd_read_ptr - adapter->txd_write_ptr - 1); } static netdev_tx_t atl2_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); struct tx_pkt_header *txph; u32 offset, copy_len; int txs_unused; int txbuf_unused; if (test_bit(__ATL2_DOWN, &adapter->flags)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } if (unlikely(skb->len <= 0)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } txs_unused = TxsFreeUnit(adapter); txbuf_unused = TxdFreeBytes(adapter); if (skb->len + sizeof(struct tx_pkt_header) + 4 > txbuf_unused || txs_unused < 1) { /* not enough resources */ netif_stop_queue(netdev); return NETDEV_TX_BUSY; } offset = adapter->txd_write_ptr; txph = (struct tx_pkt_header *) (((u8 *)adapter->txd_ring) + offset); *(u32 *)txph = 0; txph->pkt_size = skb->len; offset += 4; if (offset >= adapter->txd_ring_size) offset -= adapter->txd_ring_size; copy_len = adapter->txd_ring_size - offset; if (copy_len >= skb->len) { memcpy(((u8 *)adapter->txd_ring) + offset, skb->data, skb->len); offset += ((u32)(skb->len + 3) & ~3); } else { memcpy(((u8 *)adapter->txd_ring)+offset, skb->data, copy_len); memcpy((u8 *)adapter->txd_ring, skb->data+copy_len, skb->len-copy_len); offset = ((u32)(skb->len-copy_len + 3) & ~3); } #ifdef NETIF_F_HW_VLAN_TX if (vlan_tx_tag_present(skb)) { u16 vlan_tag = vlan_tx_tag_get(skb); vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) | ((vlan_tag >> 9) & 0x8); txph->ins_vlan = 1; txph->vlan = vlan_tag; } #endif if (offset >= adapter->txd_ring_size) offset -= adapter->txd_ring_size; adapter->txd_write_ptr = offset; /* clear txs before send */ adapter->txs_ring[adapter->txs_next_clear].update = 0; if (++adapter->txs_next_clear == adapter->txs_ring_size) adapter->txs_next_clear = 0; ATL2_WRITE_REGW(&adapter->hw, REG_MB_TXD_WR_IDX, (adapter->txd_write_ptr >> 2)); mmiowb(); dev_kfree_skb_any(skb); return NETDEV_TX_OK; } /** * atl2_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure * @new_mtu: new value for maximum frame size * * Returns 0 on success, negative on failure */ static int atl2_change_mtu(struct net_device *netdev, int new_mtu) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; if ((new_mtu < 40) || (new_mtu > (ETH_DATA_LEN + VLAN_SIZE))) return -EINVAL; /* set MTU */ if (hw->max_frame_size != new_mtu) { netdev->mtu = new_mtu; ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ENET_HEADER_SIZE + VLAN_SIZE + ETHERNET_FCS_SIZE); } return 0; } /** * atl2_set_mac - Change the Ethernet Address of the NIC * @netdev: network interface device structure * @p: pointer to an address structure * * Returns 0 on success, negative on failure */ static int atl2_set_mac(struct net_device *netdev, void *p) { struct atl2_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; if (netif_running(netdev)) return -EBUSY; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); atl2_set_mac_addr(&adapter->hw); return 0; } static int atl2_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct atl2_adapter *adapter = netdev_priv(netdev); struct mii_ioctl_data *data = if_mii(ifr); unsigned long flags; switch (cmd) { case SIOCGMIIPHY: data->phy_id = 0; break; case SIOCGMIIREG: spin_lock_irqsave(&adapter->stats_lock, flags); if (atl2_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, &data->val_out)) { spin_unlock_irqrestore(&adapter->stats_lock, flags); return -EIO; } spin_unlock_irqrestore(&adapter->stats_lock, flags); break; case SIOCSMIIREG: if (data->reg_num & ~(0x1F)) return -EFAULT; spin_lock_irqsave(&adapter->stats_lock, flags); if (atl2_write_phy_reg(&adapter->hw, data->reg_num, data->val_in)) { spin_unlock_irqrestore(&adapter->stats_lock, flags); return -EIO; } spin_unlock_irqrestore(&adapter->stats_lock, flags); break; default: return -EOPNOTSUPP; } return 0; } static int atl2_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { switch (cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: return atl2_mii_ioctl(netdev, ifr, cmd); #ifdef ETHTOOL_OPS_COMPAT case SIOCETHTOOL: return ethtool_ioctl(ifr); #endif default: return -EOPNOTSUPP; } } /** * atl2_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure */ static void atl2_tx_timeout(struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ schedule_work(&adapter->reset_task); } /** * atl2_watchdog - Timer Call-back * @data: pointer to netdev cast into an unsigned long */ static void atl2_watchdog(unsigned long data) { struct atl2_adapter *adapter = (struct atl2_adapter *) data; if (!test_bit(__ATL2_DOWN, &adapter->flags)) { u32 drop_rxd, drop_rxs; unsigned long flags; spin_lock_irqsave(&adapter->stats_lock, flags); drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV); drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV); spin_unlock_irqrestore(&adapter->stats_lock, flags); adapter->netdev->stats.rx_over_errors += drop_rxd + drop_rxs; /* Reset the timer */ mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 4 * HZ)); } } /** * atl2_phy_config - Timer Call-back * @data: pointer to netdev cast into an unsigned long */ static void atl2_phy_config(unsigned long data) { struct atl2_adapter *adapter = (struct atl2_adapter *) data; struct atl2_hw *hw = &adapter->hw; unsigned long flags; spin_lock_irqsave(&adapter->stats_lock, flags); atl2_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); atl2_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); spin_unlock_irqrestore(&adapter->stats_lock, flags); clear_bit(0, &adapter->cfg_phy); } static int atl2_up(struct atl2_adapter *adapter) { struct net_device *netdev = adapter->netdev; int err = 0; u32 val; /* hardware has been reset, we need to reload some things */ err = atl2_init_hw(&adapter->hw); if (err) { err = -EIO; return err; } atl2_set_multi(netdev); init_ring_ptrs(adapter); atl2_restore_vlan(adapter); if (atl2_configure(adapter)) { err = -EIO; goto err_up; } clear_bit(__ATL2_DOWN, &adapter->flags); val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL); ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val | MASTER_CTRL_MANUAL_INT); atl2_irq_enable(adapter); err_up: return err; } static void atl2_reinit_locked(struct atl2_adapter *adapter) { WARN_ON(in_interrupt()); while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) msleep(1); atl2_down(adapter); atl2_up(adapter); clear_bit(__ATL2_RESETTING, &adapter->flags); } static void atl2_reset_task(struct work_struct *work) { struct atl2_adapter *adapter; adapter = container_of(work, struct atl2_adapter, reset_task); atl2_reinit_locked(adapter); } static void atl2_setup_mac_ctrl(struct atl2_adapter *adapter) { u32 value; struct atl2_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; /* Config MAC CTRL Register */ value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; /* duplex */ if (FULL_DUPLEX == adapter->link_duplex) value |= MAC_CTRL_DUPLX; /* flow control */ value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); /* PAD & CRC */ value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); /* preamble length */ value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); /* vlan */ __atl2_vlan_mode(netdev->features, &value); /* filter mode */ value |= MAC_CTRL_BC_EN; if (netdev->flags & IFF_PROMISC) value |= MAC_CTRL_PROMIS_EN; else if (netdev->flags & IFF_ALLMULTI) value |= MAC_CTRL_MC_ALL_EN; /* half retry buffer */ value |= (((u32)(adapter->hw.retry_buf & MAC_CTRL_HALF_LEFT_BUF_MASK)) << MAC_CTRL_HALF_LEFT_BUF_SHIFT); ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); } static int atl2_check_link(struct atl2_adapter *adapter) { struct atl2_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; int ret_val; u16 speed, duplex, phy_data; int reconfig = 0; /* MII_BMSR must read twise */ atl2_read_phy_reg(hw, MII_BMSR, &phy_data); atl2_read_phy_reg(hw, MII_BMSR, &phy_data); if (!(phy_data&BMSR_LSTATUS)) { /* link down */ if (netif_carrier_ok(netdev)) { /* old link state: Up */ u32 value; /* disable rx */ value = ATL2_READ_REG(hw, REG_MAC_CTRL); value &= ~MAC_CTRL_RX_EN; ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); adapter->link_speed = SPEED_0; netif_carrier_off(netdev); netif_stop_queue(netdev); } return 0; } /* Link Up */ ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); if (ret_val) return ret_val; switch (hw->MediaType) { case MEDIA_TYPE_100M_FULL: if (speed != SPEED_100 || duplex != FULL_DUPLEX) reconfig = 1; break; case MEDIA_TYPE_100M_HALF: if (speed != SPEED_100 || duplex != HALF_DUPLEX) reconfig = 1; break; case MEDIA_TYPE_10M_FULL: if (speed != SPEED_10 || duplex != FULL_DUPLEX) reconfig = 1; break; case MEDIA_TYPE_10M_HALF: if (speed != SPEED_10 || duplex != HALF_DUPLEX) reconfig = 1; break; } /* link result is our setting */ if (reconfig == 0) { if (adapter->link_speed != speed || adapter->link_duplex != duplex) { adapter->link_speed = speed; adapter->link_duplex = duplex; atl2_setup_mac_ctrl(adapter); printk(KERN_INFO "%s: %s NIC Link is Up<%d Mbps %s>\n", atl2_driver_name, netdev->name, adapter->link_speed, adapter->link_duplex == FULL_DUPLEX ? "Full Duplex" : "Half Duplex"); } if (!netif_carrier_ok(netdev)) { /* Link down -> Up */ netif_carrier_on(netdev); netif_wake_queue(netdev); } return 0; } /* change original link status */ if (netif_carrier_ok(netdev)) { u32 value; /* disable rx */ value = ATL2_READ_REG(hw, REG_MAC_CTRL); value &= ~MAC_CTRL_RX_EN; ATL2_WRITE_REG(hw, REG_MAC_CTRL, value); adapter->link_speed = SPEED_0; netif_carrier_off(netdev); netif_stop_queue(netdev); } /* auto-neg, insert timer to re-config phy * (if interval smaller than 5 seconds, something strange) */ if (!test_bit(__ATL2_DOWN, &adapter->flags)) { if (!test_and_set_bit(0, &adapter->cfg_phy)) mod_timer(&adapter->phy_config_timer, round_jiffies(jiffies + 5 * HZ)); } return 0; } /** * atl2_link_chg_task - deal with link change event Out of interrupt context */ static void atl2_link_chg_task(struct work_struct *work) { struct atl2_adapter *adapter; unsigned long flags; adapter = container_of(work, struct atl2_adapter, link_chg_task); spin_lock_irqsave(&adapter->stats_lock, flags); atl2_check_link(adapter); spin_unlock_irqrestore(&adapter->stats_lock, flags); } static void atl2_setup_pcicmd(struct pci_dev *pdev) { u16 cmd; pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (cmd & PCI_COMMAND_INTX_DISABLE) cmd &= ~PCI_COMMAND_INTX_DISABLE; if (cmd & PCI_COMMAND_IO) cmd &= ~PCI_COMMAND_IO; if (0 == (cmd & PCI_COMMAND_MEMORY)) cmd |= PCI_COMMAND_MEMORY; if (0 == (cmd & PCI_COMMAND_MASTER)) cmd |= PCI_COMMAND_MASTER; pci_write_config_word(pdev, PCI_COMMAND, cmd); /* * some motherboards BIOS(PXE/EFI) driver may set PME * while they transfer control to OS (Windows/Linux) * so we should clear this bit before NIC work normally */ pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0); } #ifdef CONFIG_NET_POLL_CONTROLLER static void atl2_poll_controller(struct net_device *netdev) { disable_irq(netdev->irq); atl2_intr(netdev->irq, netdev); enable_irq(netdev->irq); } #endif static const struct net_device_ops atl2_netdev_ops = { .ndo_open = atl2_open, .ndo_stop = atl2_close, .ndo_start_xmit = atl2_xmit_frame, .ndo_set_rx_mode = atl2_set_multi, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = atl2_set_mac, .ndo_change_mtu = atl2_change_mtu, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atl2_fix_features, .ndo_set_features = atl2_set_features, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_do_ioctl = atl2_ioctl, .ndo_tx_timeout = atl2_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = atl2_poll_controller, #endif }; /** * atl2_probe - Device Initialization Routine * @pdev: PCI device information struct * @ent: entry in atl2_pci_tbl * * Returns 0 on success, negative on failure * * atl2_probe initializes an adapter identified by a pci_dev structure. * The OS initialization, configuring of the adapter private structure, * and a hardware reset occur. */ static int __devinit atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct atl2_adapter *adapter; static int cards_found; unsigned long mmio_start; int mmio_len; int err; cards_found = 0; err = pci_enable_device(pdev); if (err) return err; /* * atl2 is a shared-high-32-bit device, so we're stuck with 32-bit DMA * until the kernel has the proper infrastructure to support 64-bit DMA * on these devices. */ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) && pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { printk(KERN_ERR "atl2: No usable DMA configuration, aborting\n"); goto err_dma; } /* Mark all PCI regions associated with PCI device * pdev as being reserved by owner atl2_driver_name */ err = pci_request_regions(pdev, atl2_driver_name); if (err) goto err_pci_reg; /* Enables bus-mastering on the device and calls * pcibios_set_master to do the needed arch specific settings */ pci_set_master(pdev); err = -ENOMEM; netdev = alloc_etherdev(sizeof(struct atl2_adapter)); if (!netdev) goto err_alloc_etherdev; SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); adapter = netdev_priv(netdev); adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; mmio_start = pci_resource_start(pdev, 0x0); mmio_len = pci_resource_len(pdev, 0x0); adapter->hw.mem_rang = (u32)mmio_len; adapter->hw.hw_addr = ioremap(mmio_start, mmio_len); if (!adapter->hw.hw_addr) { err = -EIO; goto err_ioremap; } atl2_setup_pcicmd(pdev); netdev_attach_ops(netdev, &atl2_netdev_ops); atl2_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); netdev->mem_start = mmio_start; netdev->mem_end = mmio_start + mmio_len; adapter->bd_number = cards_found; adapter->pci_using_64 = false; /* setup the private structure */ err = atl2_sw_init(adapter); if (err) goto err_sw_init; err = -EIO; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_RX; #endif #if defined(NETIF_F_HW_VLAN_TX) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); #endif /* Init PHY as early as possible due to power saving issue */ atl2_phy_init(&adapter->hw); /* reset the controller to * put the device in a known good starting state */ if (atl2_reset_hw(&adapter->hw)) { err = -EIO; goto err_reset; } /* copy the MAC address out of the EEPROM */ atl2_read_mac_addr(&adapter->hw); memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); /* FIXME: do we still need this? */ #ifdef ETHTOOL_GPERMADDR memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); if (!is_valid_ether_addr(netdev->perm_addr)) { #else if (!is_valid_ether_addr(netdev->dev_addr)) { #endif err = -EIO; goto err_eeprom; } atl2_check_options(adapter); init_timer(&adapter->watchdog_timer); adapter->watchdog_timer.function = atl2_watchdog; adapter->watchdog_timer.data = (unsigned long) adapter; init_timer(&adapter->phy_config_timer); adapter->phy_config_timer.function = atl2_phy_config; adapter->phy_config_timer.data = (unsigned long) adapter; INIT_WORK(&adapter->reset_task, atl2_reset_task); INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task); strcpy(netdev->name, "eth%d"); /* ?? */ err = register_netdev(netdev); if (err) goto err_register; /* assume we have no link for now */ netif_carrier_off(netdev); netif_stop_queue(netdev); cards_found++; return 0; err_reset: err_register: err_sw_init: err_eeprom: iounmap(adapter->hw.hw_addr); err_ioremap: free_netdev(netdev); err_alloc_etherdev: pci_release_regions(pdev); err_pci_reg: err_dma: pci_disable_device(pdev); return err; } /** * atl2_remove - Device Removal Routine * @pdev: PCI device information struct * * atl2_remove is called by the PCI subsystem to alert the driver * that it should release a PCI device. The could be caused by a * Hot-Plug event, or because the driver is going to be removed from * memory. */ /* FIXME: write the original MAC address back in case it was changed from a * BIOS-set value, as in atl1 -- CHS */ static void __devexit atl2_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl2_adapter *adapter = netdev_priv(netdev); /* flush_scheduled work may reschedule our watchdog task, so * explicitly disable watchdog tasks from being rescheduled */ set_bit(__ATL2_DOWN, &adapter->flags); del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_config_timer); cancel_work_sync(&adapter->reset_task); cancel_work_sync(&adapter->link_chg_task); unregister_netdev(netdev); atl2_force_ps(&adapter->hw); iounmap(adapter->hw.hw_addr); pci_release_regions(pdev); free_netdev(netdev); pci_disable_device(pdev); } static int atl2_suspend(struct pci_dev *pdev, pm_message_t state) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; u16 speed, duplex; u32 ctrl = 0; u32 wufc = adapter->wol; #ifdef CONFIG_PM int retval = 0; #endif netif_device_detach(netdev); if (netif_running(netdev)) { WARN_ON(test_bit(__ATL2_RESETTING, &adapter->flags)); atl2_down(adapter); } #ifdef CONFIG_PM retval = pci_save_state(pdev); if (retval) return retval; #endif atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl); atl2_read_phy_reg(hw, MII_BMSR, (u16 *)&ctrl); if (ctrl & BMSR_LSTATUS) wufc &= ~ATLX_WUFC_LNKC; if (0 != (ctrl & BMSR_LSTATUS) && 0 != wufc) { u32 ret_val; /* get current link speed & duplex */ ret_val = atl2_get_speed_and_duplex(hw, &speed, &duplex); if (ret_val) { printk(KERN_DEBUG "%s: get speed&duplex error while suspend\n", atl2_driver_name); goto wol_dis; } ctrl = 0; /* turn on magic packet wol */ if (wufc & ATLX_WUFC_MAG) ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN); /* ignore Link Chg event when Link is up */ ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); /* Config MAC CTRL Register */ ctrl = MAC_CTRL_RX_EN | MAC_CTRL_MACLP_CLK_PHY; if (FULL_DUPLEX == adapter->link_duplex) ctrl |= MAC_CTRL_DUPLX; ctrl |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); ctrl |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); ctrl |= (((u32)(adapter->hw.retry_buf & MAC_CTRL_HALF_LEFT_BUF_MASK)) << MAC_CTRL_HALF_LEFT_BUF_SHIFT); if (wufc & ATLX_WUFC_MAG) { /* magic packet maybe Broadcast&multicast&Unicast */ ctrl |= MAC_CTRL_BC_EN; } ATL2_WRITE_REG(hw, REG_MAC_CTRL, ctrl); /* pcie patch */ ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); goto suspend_exit; } if (0 == (ctrl&BMSR_LSTATUS) && 0 != (wufc&ATLX_WUFC_LNKC)) { /* link is down, so only LINK CHG WOL event enable */ ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN); ATL2_WRITE_REG(hw, REG_WOL_CTRL, ctrl); ATL2_WRITE_REG(hw, REG_MAC_CTRL, 0); /* pcie patch */ ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); hw->phy_configured = false; /* re-init PHY when resume */ pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); goto suspend_exit; } wol_dis: /* WOL disabled */ ATL2_WRITE_REG(hw, REG_WOL_CTRL, 0); /* pcie patch */ ctrl = ATL2_READ_REG(hw, REG_PCIE_PHYMISC); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; ATL2_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); ctrl = ATL2_READ_REG(hw, REG_PCIE_DLL_TX_CTRL1); ctrl |= PCIE_DLL_TX_CTRL1_SEL_NOR_CLK; ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, ctrl); atl2_force_ps(hw); hw->phy_configured = false; /* re-init PHY when resume */ pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); suspend_exit: if (netif_running(netdev)) atl2_free_irq(adapter); pci_disable_device(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); return 0; } #ifdef CONFIG_PM static int atl2_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl2_adapter *adapter = netdev_priv(netdev); u32 err; pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "atl2: Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); ATL2_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */ pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); ATL2_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); if (netif_running(netdev)) { err = atl2_request_irq(adapter); if (err) return err; } atl2_reset_hw(&adapter->hw); if (netif_running(netdev)) atl2_up(adapter); netif_device_attach(netdev); return 0; } #endif static void atl2_shutdown(struct pci_dev *pdev) { atl2_suspend(pdev, PMSG_SUSPEND); } static struct pci_driver atl2_driver = { .name = atl2_driver_name, .id_table = atl2_pci_tbl, .probe = atl2_probe, .remove = __devexit_p(atl2_remove), /* Power Management Hooks */ .suspend = atl2_suspend, #ifdef CONFIG_PM .resume = atl2_resume, #endif .shutdown = atl2_shutdown, }; /** * atl2_init_module - Driver Registration Routine * * atl2_init_module is the first routine called when the driver is * loaded. All it does is register with the PCI subsystem. */ static int __init atl2_init_module(void) { printk(KERN_INFO "%s - version %s\n", atl2_driver_string, atl2_driver_version); printk(KERN_INFO "%s\n", atl2_copyright); return pci_register_driver(&atl2_driver); } module_init(atl2_init_module); /** * atl2_exit_module - Driver Exit Cleanup Routine * * atl2_exit_module is called just before the driver is removed * from memory. */ static void __exit atl2_exit_module(void) { pci_unregister_driver(&atl2_driver); } module_exit(atl2_exit_module); static void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) { struct atl2_adapter *adapter = hw->back; pci_read_config_word(adapter->pdev, reg, value); } static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) { struct atl2_adapter *adapter = hw->back; pci_write_config_word(adapter->pdev, reg, *value); } static int atl2_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); ecmd->advertising = ADVERTISED_TP; ecmd->advertising |= ADVERTISED_Autoneg; ecmd->advertising |= hw->autoneg_advertised; ecmd->port = PORT_TP; ecmd->phy_address = 0; ecmd->transceiver = XCVR_INTERNAL; if (adapter->link_speed != SPEED_0) { ethtool_cmd_speed_set(ecmd, adapter->link_speed); if (adapter->link_duplex == FULL_DUPLEX) ecmd->duplex = DUPLEX_FULL; else ecmd->duplex = DUPLEX_HALF; } else { ethtool_cmd_speed_set(ecmd, -1); ecmd->duplex = -1; } ecmd->autoneg = AUTONEG_ENABLE; return 0; } static int atl2_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) msleep(1); if (ecmd->autoneg == AUTONEG_ENABLE) { #define MY_ADV_MASK (ADVERTISE_10_HALF | \ ADVERTISE_10_FULL | \ ADVERTISE_100_HALF| \ ADVERTISE_100_FULL) if ((ecmd->advertising & MY_ADV_MASK) == MY_ADV_MASK) { hw->MediaType = MEDIA_TYPE_AUTO_SENSOR; hw->autoneg_advertised = MY_ADV_MASK; } else if ((ecmd->advertising & MY_ADV_MASK) == ADVERTISE_100_FULL) { hw->MediaType = MEDIA_TYPE_100M_FULL; hw->autoneg_advertised = ADVERTISE_100_FULL; } else if ((ecmd->advertising & MY_ADV_MASK) == ADVERTISE_100_HALF) { hw->MediaType = MEDIA_TYPE_100M_HALF; hw->autoneg_advertised = ADVERTISE_100_HALF; } else if ((ecmd->advertising & MY_ADV_MASK) == ADVERTISE_10_FULL) { hw->MediaType = MEDIA_TYPE_10M_FULL; hw->autoneg_advertised = ADVERTISE_10_FULL; } else if ((ecmd->advertising & MY_ADV_MASK) == ADVERTISE_10_HALF) { hw->MediaType = MEDIA_TYPE_10M_HALF; hw->autoneg_advertised = ADVERTISE_10_HALF; } else { clear_bit(__ATL2_RESETTING, &adapter->flags); return -EINVAL; } ecmd->advertising = hw->autoneg_advertised | ADVERTISED_TP | ADVERTISED_Autoneg; } else { clear_bit(__ATL2_RESETTING, &adapter->flags); return -EINVAL; } /* reset the link */ if (netif_running(adapter->netdev)) { atl2_down(adapter); atl2_up(adapter); } else atl2_reset_hw(&adapter->hw); clear_bit(__ATL2_RESETTING, &adapter->flags); return 0; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) static u32 atl2_get_tx_csum(struct net_device *netdev) { return (netdev->features & NETIF_F_HW_CSUM) != 0; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ static u32 atl2_get_msglevel(struct net_device *netdev) { return 0; } /* * It's sane for this to be empty, but we might want to take advantage of this. */ static void atl2_set_msglevel(struct net_device *netdev, u32 data) { } static int atl2_get_regs_len(struct net_device *netdev) { #define ATL2_REGS_LEN 42 return sizeof(u32) * ATL2_REGS_LEN; } static void atl2_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; u32 *regs_buff = p; u16 phy_data; memset(p, 0, sizeof(u32) * ATL2_REGS_LEN); regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; regs_buff[0] = ATL2_READ_REG(hw, REG_VPD_CAP); regs_buff[1] = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); regs_buff[2] = ATL2_READ_REG(hw, REG_SPI_FLASH_CONFIG); regs_buff[3] = ATL2_READ_REG(hw, REG_TWSI_CTRL); regs_buff[4] = ATL2_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); regs_buff[5] = ATL2_READ_REG(hw, REG_MASTER_CTRL); regs_buff[6] = ATL2_READ_REG(hw, REG_MANUAL_TIMER_INIT); regs_buff[7] = ATL2_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); regs_buff[8] = ATL2_READ_REG(hw, REG_PHY_ENABLE); regs_buff[9] = ATL2_READ_REG(hw, REG_CMBDISDMA_TIMER); regs_buff[10] = ATL2_READ_REG(hw, REG_IDLE_STATUS); regs_buff[11] = ATL2_READ_REG(hw, REG_MDIO_CTRL); regs_buff[12] = ATL2_READ_REG(hw, REG_SERDES_LOCK); regs_buff[13] = ATL2_READ_REG(hw, REG_MAC_CTRL); regs_buff[14] = ATL2_READ_REG(hw, REG_MAC_IPG_IFG); regs_buff[15] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR); regs_buff[16] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR+4); regs_buff[17] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE); regs_buff[18] = ATL2_READ_REG(hw, REG_RX_HASH_TABLE+4); regs_buff[19] = ATL2_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); regs_buff[20] = ATL2_READ_REG(hw, REG_MTU); regs_buff[21] = ATL2_READ_REG(hw, REG_WOL_CTRL); regs_buff[22] = ATL2_READ_REG(hw, REG_SRAM_TXRAM_END); regs_buff[23] = ATL2_READ_REG(hw, REG_DESC_BASE_ADDR_HI); regs_buff[24] = ATL2_READ_REG(hw, REG_TXD_BASE_ADDR_LO); regs_buff[25] = ATL2_READ_REG(hw, REG_TXD_MEM_SIZE); regs_buff[26] = ATL2_READ_REG(hw, REG_TXS_BASE_ADDR_LO); regs_buff[27] = ATL2_READ_REG(hw, REG_TXS_MEM_SIZE); regs_buff[28] = ATL2_READ_REG(hw, REG_RXD_BASE_ADDR_LO); regs_buff[29] = ATL2_READ_REG(hw, REG_RXD_BUF_NUM); regs_buff[30] = ATL2_READ_REG(hw, REG_DMAR); regs_buff[31] = ATL2_READ_REG(hw, REG_TX_CUT_THRESH); regs_buff[32] = ATL2_READ_REG(hw, REG_DMAW); regs_buff[33] = ATL2_READ_REG(hw, REG_PAUSE_ON_TH); regs_buff[34] = ATL2_READ_REG(hw, REG_PAUSE_OFF_TH); regs_buff[35] = ATL2_READ_REG(hw, REG_MB_TXD_WR_IDX); regs_buff[36] = ATL2_READ_REG(hw, REG_MB_RXD_RD_IDX); regs_buff[38] = ATL2_READ_REG(hw, REG_ISR); regs_buff[39] = ATL2_READ_REG(hw, REG_IMR); atl2_read_phy_reg(hw, MII_BMCR, &phy_data); regs_buff[40] = (u32)phy_data; atl2_read_phy_reg(hw, MII_BMSR, &phy_data); regs_buff[41] = (u32)phy_data; } static int atl2_get_eeprom_len(struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); if (!atl2_check_eeprom_exist(&adapter->hw)) return 512; else return 0; } static int atl2_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; u32 *eeprom_buff; int first_dword, last_dword; int ret_val = 0; int i; if (eeprom->len == 0) return -EINVAL; if (atl2_check_eeprom_exist(hw)) return -EINVAL; eeprom->magic = hw->vendor_id | (hw->device_id << 16); first_dword = eeprom->offset >> 2; last_dword = (eeprom->offset + eeprom->len - 1) >> 2; eeprom_buff = kmalloc(sizeof(u32) * (last_dword - first_dword + 1), GFP_KERNEL); if (!eeprom_buff) return -ENOMEM; for (i = first_dword; i < last_dword; i++) { if (!atl2_read_eeprom(hw, i*4, &(eeprom_buff[i-first_dword]))) { ret_val = -EIO; goto free; } } memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), eeprom->len); free: kfree(eeprom_buff); return ret_val; } static int atl2_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; u32 *eeprom_buff; u32 *ptr; int max_len, first_dword, last_dword, ret_val = 0; int i; if (eeprom->len == 0) return -EOPNOTSUPP; if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) return -EFAULT; max_len = 512; first_dword = eeprom->offset >> 2; last_dword = (eeprom->offset + eeprom->len - 1) >> 2; eeprom_buff = kmalloc(max_len, GFP_KERNEL); if (!eeprom_buff) return -ENOMEM; ptr = eeprom_buff; if (eeprom->offset & 3) { /* need read/modify/write of first changed EEPROM word */ /* only the second byte of the word is being modified */ if (!atl2_read_eeprom(hw, first_dword*4, &(eeprom_buff[0]))) { ret_val = -EIO; goto out; } ptr++; } if (((eeprom->offset + eeprom->len) & 3)) { /* * need read/modify/write of last changed EEPROM word * only the first byte of the word is being modified */ if (!atl2_read_eeprom(hw, last_dword * 4, &(eeprom_buff[last_dword - first_dword]))) { ret_val = -EIO; goto out; } } /* Device's eeprom is always little-endian, word addressable */ memcpy(ptr, bytes, eeprom->len); for (i = 0; i < last_dword - first_dword + 1; i++) { if (!atl2_write_eeprom(hw, ((first_dword+i)*4), eeprom_buff[i])) { ret_val = -EIO; goto out; } } out: kfree(eeprom_buff); return ret_val; } static void atl2_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct atl2_adapter *adapter = netdev_priv(netdev); strlcpy(drvinfo->driver, atl2_driver_name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, atl2_driver_version, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = 0; drvinfo->testinfo_len = 0; drvinfo->regdump_len = atl2_get_regs_len(netdev); drvinfo->eedump_len = atl2_get_eeprom_len(netdev); } static void atl2_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl2_adapter *adapter = netdev_priv(netdev); wol->supported = WAKE_MAGIC; wol->wolopts = 0; if (adapter->wol & ATLX_WUFC_EX) wol->wolopts |= WAKE_UCAST; if (adapter->wol & ATLX_WUFC_MC) wol->wolopts |= WAKE_MCAST; if (adapter->wol & ATLX_WUFC_BC) wol->wolopts |= WAKE_BCAST; if (adapter->wol & ATLX_WUFC_MAG) wol->wolopts |= WAKE_MAGIC; if (adapter->wol & ATLX_WUFC_LNKC) wol->wolopts |= WAKE_PHY; } static int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl2_adapter *adapter = netdev_priv(netdev); if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)) return -EOPNOTSUPP; if (wol->wolopts & (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) return -EOPNOTSUPP; /* these settings will always override what we currently have */ adapter->wol = 0; if (wol->wolopts & WAKE_MAGIC) adapter->wol |= ATLX_WUFC_MAG; if (wol->wolopts & WAKE_PHY) adapter->wol |= ATLX_WUFC_LNKC; return 0; } static int atl2_nway_reset(struct net_device *netdev) { struct atl2_adapter *adapter = netdev_priv(netdev); if (netif_running(netdev)) atl2_reinit_locked(adapter); return 0; } static const struct ethtool_ops atl2_ethtool_ops = { .get_settings = atl2_get_settings, .set_settings = atl2_set_settings, .get_drvinfo = atl2_get_drvinfo, .get_regs_len = atl2_get_regs_len, .get_regs = atl2_get_regs, .get_wol = atl2_get_wol, .set_wol = atl2_set_wol, .get_msglevel = atl2_get_msglevel, .set_msglevel = atl2_set_msglevel, .nway_reset = atl2_nway_reset, .get_link = ethtool_op_get_link, .get_eeprom_len = atl2_get_eeprom_len, .get_eeprom = atl2_get_eeprom, .set_eeprom = atl2_set_eeprom, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) .get_tx_csum = atl2_get_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, #ifdef NETIF_F_TSO .get_tso = ethtool_op_get_tso, #endif #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; static void atl2_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops); } #define LBYTESWAP(a) ((((a) & 0x00ff00ff) << 8) | \ (((a) & 0xff00ff00) >> 8)) #define LONGSWAP(a) ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16)) #define SHORTSWAP(a) (((a) << 8) | ((a) >> 8)) /* * Reset the transmit and receive units; mask and clear all interrupts. * * hw - Struct containing variables accessed by shared code * return : 0 or idle status (if error) */ static s32 atl2_reset_hw(struct atl2_hw *hw) { u32 icr; u16 pci_cfg_cmd_word; int i; /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ atl2_read_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); if ((pci_cfg_cmd_word & (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) != (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER)) { pci_cfg_cmd_word |= (CMD_IO_SPACE|CMD_MEMORY_SPACE|CMD_BUS_MASTER); atl2_write_pci_cfg(hw, PCI_REG_COMMAND, &pci_cfg_cmd_word); } /* Clear Interrupt mask to stop board from generating * interrupts & Clear any pending interrupt events */ /* FIXME */ /* ATL2_WRITE_REG(hw, REG_IMR, 0); */ /* ATL2_WRITE_REG(hw, REG_ISR, 0xffffffff); */ /* Issue Soft Reset to the MAC. This will reset the chip's * transmit, receive, DMA. It will not effect * the current PCI configuration. The global reset bit is self- * clearing, and should clear within a microsecond. */ ATL2_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST); wmb(); msleep(1); /* delay about 1ms */ /* Wait at least 10ms for All module to be Idle */ for (i = 0; i < 10; i++) { icr = ATL2_READ_REG(hw, REG_IDLE_STATUS); if (!icr) break; msleep(1); /* delay 1 ms */ cpu_relax(); } if (icr) return icr; return 0; } #define CUSTOM_SPI_CS_SETUP 2 #define CUSTOM_SPI_CLK_HI 2 #define CUSTOM_SPI_CLK_LO 2 #define CUSTOM_SPI_CS_HOLD 2 #define CUSTOM_SPI_CS_HI 3 static struct atl2_spi_flash_dev flash_table[] = { /* MFR WRSR READ PROGRAM WREN WRDI RDSR RDID SECTOR_ERASE CHIP_ERASE */ {"Atmel", 0x0, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62 }, {"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60 }, {"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7 }, }; static bool atl2_spi_read(struct atl2_hw *hw, u32 addr, u32 *buf) { int i; u32 value; ATL2_WRITE_REG(hw, REG_SPI_DATA, 0); ATL2_WRITE_REG(hw, REG_SPI_ADDR, addr); value = SPI_FLASH_CTRL_WAIT_READY | (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) << SPI_FLASH_CTRL_CS_SETUP_SHIFT | (CUSTOM_SPI_CLK_HI & SPI_FLASH_CTRL_CLK_HI_MASK) << SPI_FLASH_CTRL_CLK_HI_SHIFT | (CUSTOM_SPI_CLK_LO & SPI_FLASH_CTRL_CLK_LO_MASK) << SPI_FLASH_CTRL_CLK_LO_SHIFT | (CUSTOM_SPI_CS_HOLD & SPI_FLASH_CTRL_CS_HOLD_MASK) << SPI_FLASH_CTRL_CS_HOLD_SHIFT | (CUSTOM_SPI_CS_HI & SPI_FLASH_CTRL_CS_HI_MASK) << SPI_FLASH_CTRL_CS_HI_SHIFT | (0x1 & SPI_FLASH_CTRL_INS_MASK) << SPI_FLASH_CTRL_INS_SHIFT; ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); value |= SPI_FLASH_CTRL_START; ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); for (i = 0; i < 10; i++) { msleep(1); value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); if (!(value & SPI_FLASH_CTRL_START)) break; } if (value & SPI_FLASH_CTRL_START) return false; *buf = ATL2_READ_REG(hw, REG_SPI_DATA); return true; } /* * get_permanent_address * return 0 if get valid mac address, */ static int get_permanent_address(struct atl2_hw *hw) { u32 Addr[2]; u32 i, Control; u16 Register; u8 EthAddr[ETH_ALEN]; bool KeyValid; if (is_valid_ether_addr(hw->perm_mac_addr)) return 0; Addr[0] = 0; Addr[1] = 0; if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */ Register = 0; KeyValid = false; /* Read out all EEPROM content */ i = 0; while (1) { if (atl2_read_eeprom(hw, i + 0x100, &Control)) { if (KeyValid) { if (Register == REG_MAC_STA_ADDR) Addr[0] = Control; else if (Register == (REG_MAC_STA_ADDR + 4)) Addr[1] = Control; KeyValid = false; } else if ((Control & 0xff) == 0x5A) { KeyValid = true; Register = (u16) (Control >> 16); } else { /* assume data end while encount an invalid KEYWORD */ break; } } else { break; /* read error */ } i += 4; } *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]); if (is_valid_ether_addr(EthAddr)) { memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN); return 0; } return 1; } /* see if SPI flash exists? */ Addr[0] = 0; Addr[1] = 0; Register = 0; KeyValid = false; i = 0; while (1) { if (atl2_spi_read(hw, i + 0x1f000, &Control)) { if (KeyValid) { if (Register == REG_MAC_STA_ADDR) Addr[0] = Control; else if (Register == (REG_MAC_STA_ADDR + 4)) Addr[1] = Control; KeyValid = false; } else if ((Control & 0xff) == 0x5A) { KeyValid = true; Register = (u16) (Control >> 16); } else { break; /* data end */ } } else { break; /* read error */ } i += 4; } *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]); if (is_valid_ether_addr(EthAddr)) { memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN); return 0; } /* maybe MAC-address is from BIOS */ Addr[0] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR); Addr[1] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR + 4); *(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]); *(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]); if (is_valid_ether_addr(EthAddr)) { memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN); return 0; } return 1; } /* * Reads the adapter's MAC address from the EEPROM * * hw - Struct containing variables accessed by shared code */ static s32 atl2_read_mac_addr(struct atl2_hw *hw) { if (get_permanent_address(hw)) { /* for test */ /* FIXME: shouldn't we use eth_random_addr() here? */ hw->perm_mac_addr[0] = 0x00; hw->perm_mac_addr[1] = 0x13; hw->perm_mac_addr[2] = 0x74; hw->perm_mac_addr[3] = 0x00; hw->perm_mac_addr[4] = 0x5c; hw->perm_mac_addr[5] = 0x38; } memcpy(hw->mac_addr, hw->perm_mac_addr, ETH_ALEN); return 0; } /* * Hashes an address to determine its location in the multicast table * * hw - Struct containing variables accessed by shared code * mc_addr - the multicast address to hash * * atl2_hash_mc_addr * purpose * set hash value for a multicast address * hash calcu processing : * 1. calcu 32bit CRC for multicast address * 2. reverse crc with MSB to LSB */ static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr) { u32 crc32, value; int i; value = 0; crc32 = ether_crc_le(6, mc_addr); for (i = 0; i < 32; i++) value |= (((crc32 >> i) & 1) << (31 - i)); return value; } /* * Sets the bit in the multicast table corresponding to the hash value. * * hw - Struct containing variables accessed by shared code * hash_value - Multicast address hash value */ static void atl2_hash_set(struct atl2_hw *hw, u32 hash_value) { u32 hash_bit, hash_reg; u32 mta; /* The HASH Table is a register array of 2 32-bit registers. * It is treated like an array of 64 bits. We want to set * bit BitArray[hash_value]. So we figure out what register * the bit is in, read it, OR in the new bit, then write * back the new value. The register is determined by the * upper 7 bits of the hash value and the bit within that * register are determined by the lower 5 bits of the value. */ hash_reg = (hash_value >> 31) & 0x1; hash_bit = (hash_value >> 26) & 0x1F; mta = ATL2_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); mta |= (1 << hash_bit); ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); } /* * atl2_init_pcie - init PCIE module */ static void atl2_init_pcie(struct atl2_hw *hw) { u32 value; value = LTSSM_TEST_MODE_DEF; ATL2_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value); value = PCIE_DLL_TX_CTRL1_DEF; ATL2_WRITE_REG(hw, REG_PCIE_DLL_TX_CTRL1, value); } static void atl2_init_flash_opcode(struct atl2_hw *hw) { if (hw->flash_vendor >= ARRAY_SIZE(flash_table)) hw->flash_vendor = 0; /* ATMEL */ /* Init OP table */ ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_PROGRAM, flash_table[hw->flash_vendor].cmdPROGRAM); ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_SC_ERASE, flash_table[hw->flash_vendor].cmdSECTOR_ERASE); ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_CHIP_ERASE, flash_table[hw->flash_vendor].cmdCHIP_ERASE); ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDID, flash_table[hw->flash_vendor].cmdRDID); ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WREN, flash_table[hw->flash_vendor].cmdWREN); ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_RDSR, flash_table[hw->flash_vendor].cmdRDSR); ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_WRSR, flash_table[hw->flash_vendor].cmdWRSR); ATL2_WRITE_REGB(hw, REG_SPI_FLASH_OP_READ, flash_table[hw->flash_vendor].cmdREAD); } /******************************************************************** * Performs basic configuration of the adapter. * * hw - Struct containing variables accessed by shared code * Assumes that the controller has previously been reset and is in a * post-reset uninitialized state. Initializes multicast table, * and Calls routines to setup link * Leaves the transmit and receive units disabled and uninitialized. ********************************************************************/ static s32 atl2_init_hw(struct atl2_hw *hw) { u32 ret_val = 0; atl2_init_pcie(hw); /* Zero out the Multicast HASH table */ /* clear the old settings from the multicast hash table */ ATL2_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); ATL2_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); atl2_init_flash_opcode(hw); ret_val = atl2_phy_init(hw); return ret_val; } /* * Detects the current speed and duplex settings of the hardware. * * hw - Struct containing variables accessed by shared code * speed - Speed of the connection * duplex - Duplex setting of the connection */ static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed, u16 *duplex) { s32 ret_val; u16 phy_data; /* Read PHY Specific Status Register (17) */ ret_val = atl2_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data); if (ret_val) return ret_val; if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED)) return ATLX_ERR_PHY_RES; switch (phy_data & MII_ATLX_PSSR_SPEED) { case MII_ATLX_PSSR_100MBS: *speed = SPEED_100; break; case MII_ATLX_PSSR_10MBS: *speed = SPEED_10; break; default: return ATLX_ERR_PHY_SPEED; break; } if (phy_data & MII_ATLX_PSSR_DPLX) *duplex = FULL_DUPLEX; else *duplex = HALF_DUPLEX; return 0; } /* * Reads the value from a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to read */ static s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data) { u32 val; int i; val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val); wmb(); for (i = 0; i < MDIO_WAIT_TIMES; i++) { udelay(2); val = ATL2_READ_REG(hw, REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; wmb(); } if (!(val & (MDIO_START | MDIO_BUSY))) { *phy_data = (u16)val; return 0; } return ATLX_ERR_PHY; } /* * Writes a value to a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to write * data - data to write to the PHY */ static s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data) { int i; u32 val; val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | MDIO_SUP_PREAMBLE | MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; ATL2_WRITE_REG(hw, REG_MDIO_CTRL, val); wmb(); for (i = 0; i < MDIO_WAIT_TIMES; i++) { udelay(2); val = ATL2_READ_REG(hw, REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; wmb(); } if (!(val & (MDIO_START | MDIO_BUSY))) return 0; return ATLX_ERR_PHY; } /* * Configures PHY autoneg and flow control advertisement settings * * hw - Struct containing variables accessed by shared code */ static s32 atl2_phy_setup_autoneg_adv(struct atl2_hw *hw) { s32 ret_val; s16 mii_autoneg_adv_reg; /* Read the MII Auto-Neg Advertisement Register (Address 4). */ mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; /* Need to parse autoneg_advertised and set up * the appropriate PHY registers. First we will parse for * autoneg_advertised software override. Since we can advertise * a plethora of combinations, we need to check each bit * individually. */ /* First we clear all the 10/100 mb speed bits in the Auto-Neg * Advertisement Register (Address 4) and the 1000 mb speed bits in * the 1000Base-T Control Register (Address 9). */ mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK; /* Need to parse MediaType and setup the * appropriate PHY registers. */ switch (hw->MediaType) { case MEDIA_TYPE_AUTO_SENSOR: mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS | MII_AR_10T_FD_CAPS | MII_AR_100TX_HD_CAPS| MII_AR_100TX_FD_CAPS); hw->autoneg_advertised = ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF| ADVERTISE_100_FULL; break; case MEDIA_TYPE_100M_FULL: mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; hw->autoneg_advertised = ADVERTISE_100_FULL; break; case MEDIA_TYPE_100M_HALF: mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; hw->autoneg_advertised = ADVERTISE_100_HALF; break; case MEDIA_TYPE_10M_FULL: mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; hw->autoneg_advertised = ADVERTISE_10_FULL; break; default: mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; hw->autoneg_advertised = ADVERTISE_10_HALF; break; } /* flow control fixed to enable all */ mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; ret_val = atl2_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); if (ret_val) return ret_val; return 0; } /* * Resets the PHY and make all config validate * * hw - Struct containing variables accessed by shared code * * Sets bit 15 and 12 of the MII Control regiser (for F001 bug) */ static s32 atl2_phy_commit(struct atl2_hw *hw) { s32 ret_val; u16 phy_data; phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG; ret_val = atl2_write_phy_reg(hw, MII_BMCR, phy_data); if (ret_val) { u32 val; int i; /* pcie serdes link may be down ! */ for (i = 0; i < 25; i++) { msleep(1); val = ATL2_READ_REG(hw, REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; } if (0 != (val & (MDIO_START | MDIO_BUSY))) { printk(KERN_ERR "atl2: PCIe link down for at least 25ms !\n"); return ret_val; } } return 0; } static s32 atl2_phy_init(struct atl2_hw *hw) { s32 ret_val; u16 phy_val; if (hw->phy_configured) return 0; /* Enable PHY */ ATL2_WRITE_REGW(hw, REG_PHY_ENABLE, 1); ATL2_WRITE_FLUSH(hw); msleep(1); /* check if the PHY is in powersaving mode */ atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); /* 024E / 124E 0r 0274 / 1274 ? */ if (phy_val & 0x1000) { phy_val &= ~0x1000; atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val); } msleep(1); /*Enable PHY LinkChange Interrupt */ ret_val = atl2_write_phy_reg(hw, 18, 0xC00); if (ret_val) return ret_val; /* setup AutoNeg parameters */ ret_val = atl2_phy_setup_autoneg_adv(hw); if (ret_val) return ret_val; /* SW.Reset & En-Auto-Neg to restart Auto-Neg */ ret_val = atl2_phy_commit(hw); if (ret_val) return ret_val; hw->phy_configured = true; return ret_val; } static void atl2_set_mac_addr(struct atl2_hw *hw) { u32 value; /* 00-0B-6A-F6-00-DC * 0: 6AF600DC 1: 000B * low dword */ value = (((u32)hw->mac_addr[2]) << 24) | (((u32)hw->mac_addr[3]) << 16) | (((u32)hw->mac_addr[4]) << 8) | (((u32)hw->mac_addr[5])); ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); /* hight dword */ value = (((u32)hw->mac_addr[0]) << 8) | (((u32)hw->mac_addr[1])); ATL2_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); } /* * check_eeprom_exist * return 0 if eeprom exist */ static int atl2_check_eeprom_exist(struct atl2_hw *hw) { u32 value; value = ATL2_READ_REG(hw, REG_SPI_FLASH_CTRL); if (value & SPI_FLASH_CTRL_EN_VPD) { value &= ~SPI_FLASH_CTRL_EN_VPD; ATL2_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); } value = ATL2_READ_REGW(hw, REG_PCIE_CAP_LIST); return ((value & 0xFF00) == 0x6C00) ? 0 : 1; } /* FIXME: This doesn't look right. -- CHS */ static bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value) { return true; } static bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue) { int i; u32 Control; if (Offset & 0x3) return false; /* address do not align */ ATL2_WRITE_REG(hw, REG_VPD_DATA, 0); Control = (Offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; ATL2_WRITE_REG(hw, REG_VPD_CAP, Control); for (i = 0; i < 10; i++) { msleep(2); Control = ATL2_READ_REG(hw, REG_VPD_CAP); if (Control & VPD_CAP_VPD_FLAG) break; } if (Control & VPD_CAP_VPD_FLAG) { *pValue = ATL2_READ_REG(hw, REG_VPD_DATA); return true; } return false; /* timeout */ } static void atl2_force_ps(struct atl2_hw *hw) { u16 phy_val; atl2_write_phy_reg(hw, MII_DBG_ADDR, 0); atl2_read_phy_reg(hw, MII_DBG_DATA, &phy_val); atl2_write_phy_reg(hw, MII_DBG_DATA, phy_val | 0x1000); atl2_write_phy_reg(hw, MII_DBG_ADDR, 2); atl2_write_phy_reg(hw, MII_DBG_DATA, 0x3000); atl2_write_phy_reg(hw, MII_DBG_ADDR, 3); atl2_write_phy_reg(hw, MII_DBG_DATA, 0); } /* This is the only thing that needs to be changed to adjust the * maximum number of ports that the driver can manage. */ #define ATL2_MAX_NIC 4 #define OPTION_UNSET -1 #define OPTION_DISABLED 0 #define OPTION_ENABLED 1 /* All parameters are treated the same, as an integer array of values. * This macro just reduces the need to repeat the same declaration code * over and over (plus this helps to avoid typo bugs). */ #define ATL2_PARAM_INIT {[0 ... ATL2_MAX_NIC] = OPTION_UNSET} #ifndef module_param_array /* Module Parameters are always initialized to -1, so that the driver * can tell the difference between no user specified value or the * user asking for the default value. * The true default values are loaded in when atl2_check_options is called. * * This is a GCC extension to ANSI C. * See the item "Labeled Elements in Initializers" in the section * "Extensions to the C Language Family" of the GCC documentation. */ #define ATL2_PARAM(X, desc) \ static const int __devinitdata X[ATL2_MAX_NIC + 1] = ATL2_PARAM_INIT; \ MODULE_PARM(X, "1-" __MODULE_STRING(ATL2_MAX_NIC) "i"); \ MODULE_PARM_DESC(X, desc); #else #define ATL2_PARAM(X, desc) \ static int __devinitdata X[ATL2_MAX_NIC+1] = ATL2_PARAM_INIT; \ static unsigned int num_##X; \ module_param_array_named(X, X, int, &num_##X, 0); \ MODULE_PARM_DESC(X, desc); #endif /* * Transmit Memory Size * Valid Range: 64-2048 * Default Value: 128 */ #define ATL2_MIN_TX_MEMSIZE 4 /* 4KB */ #define ATL2_MAX_TX_MEMSIZE 64 /* 64KB */ #define ATL2_DEFAULT_TX_MEMSIZE 8 /* 8KB */ ATL2_PARAM(TxMemSize, "Bytes of Transmit Memory"); /* * Receive Memory Block Count * Valid Range: 16-512 * Default Value: 128 */ #define ATL2_MIN_RXD_COUNT 16 #define ATL2_MAX_RXD_COUNT 512 #define ATL2_DEFAULT_RXD_COUNT 64 ATL2_PARAM(RxMemBlock, "Number of receive memory block"); /* * User Specified MediaType Override * * Valid Range: 0-5 * - 0 - auto-negotiate at all supported speeds * - 1 - only link at 1000Mbps Full Duplex * - 2 - only link at 100Mbps Full Duplex * - 3 - only link at 100Mbps Half Duplex * - 4 - only link at 10Mbps Full Duplex * - 5 - only link at 10Mbps Half Duplex * Default Value: 0 */ ATL2_PARAM(MediaType, "MediaType Select"); /* * Interrupt Moderate Timer in units of 2048 ns (~2 us) * Valid Range: 10-65535 * Default Value: 45000(90ms) */ #define INT_MOD_DEFAULT_CNT 100 /* 200us */ #define INT_MOD_MAX_CNT 65000 #define INT_MOD_MIN_CNT 50 ATL2_PARAM(IntModTimer, "Interrupt Moderator Timer"); /* * FlashVendor * Valid Range: 0-2 * 0 - Atmel * 1 - SST * 2 - ST */ ATL2_PARAM(FlashVendor, "SPI Flash Vendor"); #define AUTONEG_ADV_DEFAULT 0x2F #define AUTONEG_ADV_MASK 0x2F #define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL #define FLASH_VENDOR_DEFAULT 0 #define FLASH_VENDOR_MIN 0 #define FLASH_VENDOR_MAX 2 struct atl2_option { enum { enable_option, range_option, list_option } type; char *name; char *err; int def; union { struct { /* range_option info */ int min; int max; } r; struct { /* list_option info */ int nr; struct atl2_opt_list { int i; char *str; } *p; } l; } arg; }; static int __devinit atl2_validate_option(int *value, struct atl2_option *opt) { int i; struct atl2_opt_list *ent; if (*value == OPTION_UNSET) { *value = opt->def; return 0; } switch (opt->type) { case enable_option: switch (*value) { case OPTION_ENABLED: printk(KERN_INFO "%s Enabled\n", opt->name); return 0; break; case OPTION_DISABLED: printk(KERN_INFO "%s Disabled\n", opt->name); return 0; break; } break; case range_option: if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { printk(KERN_INFO "%s set to %i\n", opt->name, *value); return 0; } break; case list_option: for (i = 0; i < opt->arg.l.nr; i++) { ent = &opt->arg.l.p[i]; if (*value == ent->i) { if (ent->str[0] != '\0') printk(KERN_INFO "%s\n", ent->str); return 0; } } break; default: BUG(); } printk(KERN_INFO "Invalid %s specified (%i) %s\n", opt->name, *value, opt->err); *value = opt->def; return -1; } /** * atl2_check_options - Range Checking for Command Line Parameters * @adapter: board private structure * * This routine checks all command line parameters for valid user * input. If an invalid value is given, or if no user specified * value exists, a default value is used. The final value is stored * in a variable in the adapter structure. */ static void __devinit atl2_check_options(struct atl2_adapter *adapter) { int val; struct atl2_option opt; int bd = adapter->bd_number; if (bd >= ATL2_MAX_NIC) { printk(KERN_NOTICE "Warning: no configuration for board #%i\n", bd); printk(KERN_NOTICE "Using defaults for all values\n"); #ifndef module_param_array bd = ATL2_MAX_NIC; #endif } /* Bytes of Transmit Memory */ opt.type = range_option; opt.name = "Bytes of Transmit Memory"; opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_TX_MEMSIZE); opt.def = ATL2_DEFAULT_TX_MEMSIZE; opt.arg.r.min = ATL2_MIN_TX_MEMSIZE; opt.arg.r.max = ATL2_MAX_TX_MEMSIZE; #ifdef module_param_array if (num_TxMemSize > bd) { #endif val = TxMemSize[bd]; atl2_validate_option(&val, &opt); adapter->txd_ring_size = ((u32) val) * 1024; #ifdef module_param_array } else adapter->txd_ring_size = ((u32)opt.def) * 1024; #endif /* txs ring size: */ adapter->txs_ring_size = adapter->txd_ring_size / 128; if (adapter->txs_ring_size > 160) adapter->txs_ring_size = 160; /* Receive Memory Block Count */ opt.type = range_option; opt.name = "Number of receive memory block"; opt.err = "using default of " __MODULE_STRING(ATL2_DEFAULT_RXD_COUNT); opt.def = ATL2_DEFAULT_RXD_COUNT; opt.arg.r.min = ATL2_MIN_RXD_COUNT; opt.arg.r.max = ATL2_MAX_RXD_COUNT; #ifdef module_param_array if (num_RxMemBlock > bd) { #endif val = RxMemBlock[bd]; atl2_validate_option(&val, &opt); adapter->rxd_ring_size = (u32)val; /* FIXME */ /* ((u16)val)&~1; */ /* even number */ #ifdef module_param_array } else adapter->rxd_ring_size = (u32)opt.def; #endif /* init RXD Flow control value */ adapter->hw.fc_rxd_hi = (adapter->rxd_ring_size / 8) * 7; adapter->hw.fc_rxd_lo = (ATL2_MIN_RXD_COUNT / 8) > (adapter->rxd_ring_size / 12) ? (ATL2_MIN_RXD_COUNT / 8) : (adapter->rxd_ring_size / 12); /* Interrupt Moderate Timer */ opt.type = range_option; opt.name = "Interrupt Moderate Timer"; opt.err = "using default of " __MODULE_STRING(INT_MOD_DEFAULT_CNT); opt.def = INT_MOD_DEFAULT_CNT; opt.arg.r.min = INT_MOD_MIN_CNT; opt.arg.r.max = INT_MOD_MAX_CNT; #ifdef module_param_array if (num_IntModTimer > bd) { #endif val = IntModTimer[bd]; atl2_validate_option(&val, &opt); adapter->imt = (u16) val; #ifdef module_param_array } else adapter->imt = (u16)(opt.def); #endif /* Flash Vendor */ opt.type = range_option; opt.name = "SPI Flash Vendor"; opt.err = "using default of " __MODULE_STRING(FLASH_VENDOR_DEFAULT); opt.def = FLASH_VENDOR_DEFAULT; opt.arg.r.min = FLASH_VENDOR_MIN; opt.arg.r.max = FLASH_VENDOR_MAX; #ifdef module_param_array if (num_FlashVendor > bd) { #endif val = FlashVendor[bd]; atl2_validate_option(&val, &opt); adapter->hw.flash_vendor = (u8) val; #ifdef module_param_array } else adapter->hw.flash_vendor = (u8)(opt.def); #endif /* MediaType */ opt.type = range_option; opt.name = "Speed/Duplex Selection"; opt.err = "using default of " __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR); opt.def = MEDIA_TYPE_AUTO_SENSOR; opt.arg.r.min = MEDIA_TYPE_AUTO_SENSOR; opt.arg.r.max = MEDIA_TYPE_10M_HALF; #ifdef module_param_array if (num_MediaType > bd) { #endif val = MediaType[bd]; atl2_validate_option(&val, &opt); adapter->hw.MediaType = (u16) val; #ifdef module_param_array } else adapter->hw.MediaType = (u16)(opt.def); #endif } compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/atlx.h0000644000175000017500000004334312026211315024207 0ustar mcgrofmcgrof/* atlx_hw.h -- common hardware definitions for Attansic network drivers * * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2006 - 2007 Chris Snook * Copyright(c) 2006 - 2008 Jay Cliburn * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ATLX_H #define ATLX_H #include #include #define ATLX_ERR_PHY 2 #define ATLX_ERR_PHY_SPEED 7 #define ATLX_ERR_PHY_RES 8 #define SPEED_0 0xffff #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 #define HALF_DUPLEX 1 #define FULL_DUPLEX 2 #define MEDIA_TYPE_AUTO_SENSOR 0 /* register definitions */ #define REG_PM_CTRLSTAT 0x44 #define REG_PCIE_CAP_LIST 0x58 #define REG_VPD_CAP 0x6C #define VPD_CAP_ID_MASK 0xFF #define VPD_CAP_ID_SHIFT 0 #define VPD_CAP_NEXT_PTR_MASK 0xFF #define VPD_CAP_NEXT_PTR_SHIFT 8 #define VPD_CAP_VPD_ADDR_MASK 0x7FFF #define VPD_CAP_VPD_ADDR_SHIFT 16 #define VPD_CAP_VPD_FLAG 0x80000000 #define REG_VPD_DATA 0x70 #define REG_SPI_FLASH_CTRL 0x200 #define SPI_FLASH_CTRL_STS_NON_RDY 0x1 #define SPI_FLASH_CTRL_STS_WEN 0x2 #define SPI_FLASH_CTRL_STS_WPEN 0x80 #define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF #define SPI_FLASH_CTRL_DEV_STS_SHIFT 0 #define SPI_FLASH_CTRL_INS_MASK 0x7 #define SPI_FLASH_CTRL_INS_SHIFT 8 #define SPI_FLASH_CTRL_START 0x800 #define SPI_FLASH_CTRL_EN_VPD 0x2000 #define SPI_FLASH_CTRL_LDSTART 0x8000 #define SPI_FLASH_CTRL_CS_HI_MASK 0x3 #define SPI_FLASH_CTRL_CS_HI_SHIFT 16 #define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3 #define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18 #define SPI_FLASH_CTRL_CLK_LO_MASK 0x3 #define SPI_FLASH_CTRL_CLK_LO_SHIFT 20 #define SPI_FLASH_CTRL_CLK_HI_MASK 0x3 #define SPI_FLASH_CTRL_CLK_HI_SHIFT 22 #define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3 #define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24 #define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3 #define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26 #define SPI_FLASH_CTRL_WAIT_READY 0x10000000 #define REG_SPI_ADDR 0x204 #define REG_SPI_DATA 0x208 #define REG_SPI_FLASH_CONFIG 0x20C #define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF #define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0 #define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3 #define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24 #define SPI_FLASH_CONFIG_LD_EXIST 0x4000000 #define REG_SPI_FLASH_OP_PROGRAM 0x210 #define REG_SPI_FLASH_OP_SC_ERASE 0x211 #define REG_SPI_FLASH_OP_CHIP_ERASE 0x212 #define REG_SPI_FLASH_OP_RDID 0x213 #define REG_SPI_FLASH_OP_WREN 0x214 #define REG_SPI_FLASH_OP_RDSR 0x215 #define REG_SPI_FLASH_OP_WRSR 0x216 #define REG_SPI_FLASH_OP_READ 0x217 #define REG_TWSI_CTRL 0x218 #define TWSI_CTRL_LD_OFFSET_MASK 0xFF #define TWSI_CTRL_LD_OFFSET_SHIFT 0 #define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 #define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 #define TWSI_CTRL_SW_LDSTART 0x800 #define TWSI_CTRL_HW_LDSTART 0x1000 #define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F #define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 #define TWSI_CTRL_LD_EXIST 0x400000 #define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 #define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 #define TWSI_CTRL_FREQ_SEL_100K 0 #define TWSI_CTRL_FREQ_SEL_200K 1 #define TWSI_CTRL_FREQ_SEL_300K 2 #define TWSI_CTRL_FREQ_SEL_400K 3 #define TWSI_CTRL_SMB_SLV_ADDR /* FIXME: define or remove */ #define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 #define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 #define REG_PCIE_DEV_MISC_CTRL 0x21C #define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2 #define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1 #define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4 #define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8 #define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10 #define REG_PCIE_PHYMISC 0x1000 #define PCIE_PHYMISC_FORCE_RCV_DET 0x4 #define REG_PCIE_DLL_TX_CTRL1 0x1104 #define PCIE_DLL_TX_CTRL1_SEL_NOR_CLK 0x400 #define PCIE_DLL_TX_CTRL1_DEF 0x568 #define REG_LTSSM_TEST_MODE 0x12FC #define LTSSM_TEST_MODE_DEF 0x6500 /* Master Control Register */ #define REG_MASTER_CTRL 0x1400 #define MASTER_CTRL_SOFT_RST 0x1 #define MASTER_CTRL_MTIMER_EN 0x2 #define MASTER_CTRL_ITIMER_EN 0x4 #define MASTER_CTRL_MANUAL_INT 0x8 #define MASTER_CTRL_REV_NUM_SHIFT 16 #define MASTER_CTRL_REV_NUM_MASK 0xFF #define MASTER_CTRL_DEV_ID_SHIFT 24 #define MASTER_CTRL_DEV_ID_MASK 0xFF /* Timer Initial Value Register */ #define REG_MANUAL_TIMER_INIT 0x1404 /* IRQ Moderator Timer Initial Value Register */ #define REG_IRQ_MODU_TIMER_INIT 0x1408 #define REG_PHY_ENABLE 0x140C /* IRQ Anti-Lost Timer Initial Value Register */ #define REG_CMBDISDMA_TIMER 0x140E /* Block IDLE Status Register */ #define REG_IDLE_STATUS 0x1410 /* MDIO Control Register */ #define REG_MDIO_CTRL 0x1414 #define MDIO_DATA_MASK 0xFFFF #define MDIO_DATA_SHIFT 0 #define MDIO_REG_ADDR_MASK 0x1F #define MDIO_REG_ADDR_SHIFT 16 #define MDIO_RW 0x200000 #define MDIO_SUP_PREAMBLE 0x400000 #define MDIO_START 0x800000 #define MDIO_CLK_SEL_SHIFT 24 #define MDIO_CLK_25_4 0 #define MDIO_CLK_25_6 2 #define MDIO_CLK_25_8 3 #define MDIO_CLK_25_10 4 #define MDIO_CLK_25_14 5 #define MDIO_CLK_25_20 6 #define MDIO_CLK_25_28 7 #define MDIO_BUSY 0x8000000 /* MII PHY Status Register */ #define REG_PHY_STATUS 0x1418 /* BIST Control and Status Register0 (for the Packet Memory) */ #define REG_BIST0_CTRL 0x141C #define BIST0_NOW 0x1 #define BIST0_SRAM_FAIL 0x2 #define BIST0_FUSE_FLAG 0x4 #define REG_BIST1_CTRL 0x1420 #define BIST1_NOW 0x1 #define BIST1_SRAM_FAIL 0x2 #define BIST1_FUSE_FLAG 0x4 /* SerDes Lock Detect Control and Status Register */ #define REG_SERDES_LOCK 0x1424 #define SERDES_LOCK_DETECT 1 #define SERDES_LOCK_DETECT_EN 2 /* MAC Control Register */ #define REG_MAC_CTRL 0x1480 #define MAC_CTRL_TX_EN 1 #define MAC_CTRL_RX_EN 2 #define MAC_CTRL_TX_FLOW 4 #define MAC_CTRL_RX_FLOW 8 #define MAC_CTRL_LOOPBACK 0x10 #define MAC_CTRL_DUPLX 0x20 #define MAC_CTRL_ADD_CRC 0x40 #define MAC_CTRL_PAD 0x80 #define MAC_CTRL_LENCHK 0x100 #define MAC_CTRL_HUGE_EN 0x200 #define MAC_CTRL_PRMLEN_SHIFT 10 #define MAC_CTRL_PRMLEN_MASK 0xF #define MAC_CTRL_RMV_VLAN 0x4000 #define MAC_CTRL_PROMIS_EN 0x8000 #define MAC_CTRL_MC_ALL_EN 0x2000000 #define MAC_CTRL_BC_EN 0x4000000 /* MAC IPG/IFG Control Register */ #define REG_MAC_IPG_IFG 0x1484 #define MAC_IPG_IFG_IPGT_SHIFT 0 #define MAC_IPG_IFG_IPGT_MASK 0x7F #define MAC_IPG_IFG_MIFG_SHIFT 8 #define MAC_IPG_IFG_MIFG_MASK 0xFF #define MAC_IPG_IFG_IPGR1_SHIFT 16 #define MAC_IPG_IFG_IPGR1_MASK 0x7F #define MAC_IPG_IFG_IPGR2_SHIFT 24 #define MAC_IPG_IFG_IPGR2_MASK 0x7F /* MAC STATION ADDRESS */ #define REG_MAC_STA_ADDR 0x1488 /* Hash table for multicast address */ #define REG_RX_HASH_TABLE 0x1490 /* MAC Half-Duplex Control Register */ #define REG_MAC_HALF_DUPLX_CTRL 0x1498 #define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 #define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3FF #define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 #define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xF #define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 #define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 #define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 #define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 #define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 #define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xF #define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 #define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xF /* Maximum Frame Length Control Register */ #define REG_MTU 0x149C /* Wake-On-Lan control register */ #define REG_WOL_CTRL 0x14A0 #define WOL_PATTERN_EN 0x1 #define WOL_PATTERN_PME_EN 0x2 #define WOL_MAGIC_EN 0x4 #define WOL_MAGIC_PME_EN 0x8 #define WOL_LINK_CHG_EN 0x10 #define WOL_LINK_CHG_PME_EN 0x20 #define WOL_PATTERN_ST 0x100 #define WOL_MAGIC_ST 0x200 #define WOL_LINKCHG_ST 0x400 #define WOL_PT0_EN 0x10000 #define WOL_PT1_EN 0x20000 #define WOL_PT2_EN 0x40000 #define WOL_PT3_EN 0x80000 #define WOL_PT4_EN 0x100000 #define WOL_PT0_MATCH 0x1000000 #define WOL_PT1_MATCH 0x2000000 #define WOL_PT2_MATCH 0x4000000 #define WOL_PT3_MATCH 0x8000000 #define WOL_PT4_MATCH 0x10000000 /* Internal SRAM Partition Register, high 32 bits */ #define REG_SRAM_RFD_ADDR 0x1500 /* Descriptor Control register, high 32 bits */ #define REG_DESC_BASE_ADDR_HI 0x1540 /* Interrupt Status Register */ #define REG_ISR 0x1600 #define ISR_UR_DETECTED 0x1000000 #define ISR_FERR_DETECTED 0x2000000 #define ISR_NFERR_DETECTED 0x4000000 #define ISR_CERR_DETECTED 0x8000000 #define ISR_PHY_LINKDOWN 0x10000000 #define ISR_DIS_INT 0x80000000 /* Interrupt Mask Register */ #define REG_IMR 0x1604 #define REG_RFD_RRD_IDX 0x1800 #define REG_TPD_IDX 0x1804 /* MII definitions */ /* PHY Common Register */ #define MII_ATLX_CR 0x09 #define MII_ATLX_SR 0x0A #define MII_ATLX_ESR 0x0F #define MII_ATLX_PSCR 0x10 #define MII_ATLX_PSSR 0x11 /* PHY Control Register */ #define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, * 00=10 */ #define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ #define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ #define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ #define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ #define MII_CR_POWER_DOWN 0x0800 /* Power down */ #define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ #define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, * 00=10 */ #define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ #define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ #define MII_CR_SPEED_MASK 0x2040 #define MII_CR_SPEED_1000 0x0040 #define MII_CR_SPEED_100 0x2000 #define MII_CR_SPEED_10 0x0000 /* PHY Status Register */ #define MII_SR_EXTENDED_CAPS 0x0001 /* Ext register capabilities */ #define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ #define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ #define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ #define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ #define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ #define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ #define MII_SR_EXTENDED_STATUS 0x0100 /* Ext stat info in Reg 0x0F */ #define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ #define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ #define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ #define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ #define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ #define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ #define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ /* Link partner ability register */ #define MII_LPA_SLCT 0x001f /* Same as advertise selector */ #define MII_LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ #define MII_LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ #define MII_LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ #define MII_LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ #define MII_LPA_100BASE4 0x0200 /* 100BASE-T4 */ #define MII_LPA_PAUSE 0x0400 /* PAUSE */ #define MII_LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */ #define MII_LPA_RFAULT 0x2000 /* Link partner faulted */ #define MII_LPA_LPACK 0x4000 /* Link partner acked us */ #define MII_LPA_NPAGE 0x8000 /* Next page bit */ /* Autoneg Advertisement Register */ #define MII_AR_SELECTOR_FIELD 0x0001 /* IEEE 802.3 CSMA/CD */ #define MII_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ #define MII_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ #define MII_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ #define MII_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ #define MII_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ #define MII_AR_PAUSE 0x0400 /* Pause operation desired */ #define MII_AR_ASM_DIR 0x0800 /* Asymmetric Pause Dir bit */ #define MII_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ #define MII_AR_NEXT_PAGE 0x8000 /* Next Page ability support */ #define MII_AR_SPEED_MASK 0x01E0 #define MII_AR_DEFAULT_CAP_MASK 0x0DE0 /* 1000BASE-T Control Register */ #define MII_ATLX_CR_1000T_HD_CAPS 0x0100 /* Adv 1000T HD cap */ #define MII_ATLX_CR_1000T_FD_CAPS 0x0200 /* Adv 1000T FD cap */ #define MII_ATLX_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device, * 0=DTE device */ #define MII_ATLX_CR_1000T_MS_VALUE 0x0800 /* 1=Config PHY as Master, * 0=Configure PHY as Slave */ #define MII_ATLX_CR_1000T_MS_ENABLE 0x1000 /* 1=Man Master/Slave config, * 0=Auto Master/Slave config */ #define MII_ATLX_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ #define MII_ATLX_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ #define MII_ATLX_CR_1000T_TEST_MODE_2 0x4000 /* Master Xmit Jitter test */ #define MII_ATLX_CR_1000T_TEST_MODE_3 0x6000 /* Slave Xmit Jitter test */ #define MII_ATLX_CR_1000T_TEST_MODE_4 0x8000 /* Xmitter Distortion test */ #define MII_ATLX_CR_1000T_SPEED_MASK 0x0300 #define MII_ATLX_CR_1000T_DEFAULT_CAP_MASK 0x0300 /* 1000BASE-T Status Register */ #define MII_ATLX_SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ #define MII_ATLX_SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ #define MII_ATLX_SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ #define MII_ATLX_SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ #define MII_ATLX_SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master * 0=Slave */ #define MII_ATLX_SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config * fault */ #define MII_ATLX_SR_1000T_REMOTE_RX_STATUS_SHIFT 12 #define MII_ATLX_SR_1000T_LOCAL_RX_STATUS_SHIFT 13 /* Extended Status Register */ #define MII_ATLX_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ #define MII_ATLX_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ #define MII_ATLX_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ #define MII_ATLX_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ /* ATLX PHY Specific Control Register */ #define MII_ATLX_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Func disabled */ #define MII_ATLX_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enbld */ #define MII_ATLX_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ #define MII_ATLX_PSCR_MAC_POWERDOWN 0x0008 #define MII_ATLX_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low * 0=CLK125 toggling */ #define MII_ATLX_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5, * Manual MDI configuration */ #define MII_ATLX_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ #define MII_ATLX_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover * 100BASE-TX/10BASE-T: MDI * Mode */ #define MII_ATLX_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled * all speeds. */ #define MII_ATLX_PSCR_10BT_EXT_DIST_ENABLE 0x0080 /* 1=Enable Extended * 10BASE-T distance * (Lower 10BASE-T RX * Threshold) * 0=Normal 10BASE-T RX * Threshold */ #define MII_ATLX_PSCR_MII_5BIT_ENABLE 0x0100 /* 1=5-Bit interface in * 100BASE-TX * 0=MII interface in * 100BASE-TX */ #define MII_ATLX_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler dsbl */ #define MII_ATLX_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ #define MII_ATLX_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ #define MII_ATLX_PSCR_POLARITY_REVERSAL_SHIFT 1 #define MII_ATLX_PSCR_AUTO_X_MODE_SHIFT 5 #define MII_ATLX_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 /* ATLX PHY Specific Status Register */ #define MII_ATLX_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ #define MII_ATLX_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ #define MII_ATLX_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ #define MII_ATLX_PSSR_10MBS 0x0000 /* 00=10Mbs */ #define MII_ATLX_PSSR_100MBS 0x4000 /* 01=100Mbs */ #define MII_ATLX_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ #define MII_DBG_ADDR 0x1D #define MII_DBG_DATA 0x1E /* PCI Command Register Bit Definitions */ #define PCI_REG_COMMAND 0x04 /* PCI Command Register */ #define CMD_IO_SPACE 0x0001 #define CMD_MEMORY_SPACE 0x0002 #define CMD_BUS_MASTER 0x0004 /* Wake Up Filter Control */ #define ATLX_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ #define ATLX_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ #define ATLX_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ #define ATLX_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ #define ATLX_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ #define ADVERTISE_10_HALF 0x0001 #define ADVERTISE_10_FULL 0x0002 #define ADVERTISE_100_HALF 0x0004 #define ADVERTISE_100_FULL 0x0008 #define ADVERTISE_1000_HALF 0x0010 #define ADVERTISE_1000_FULL 0x0020 #define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds */ #define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds */ #define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ #define PHY_FORCE_TIME 20 /* 2.0 Seconds */ /* For checksumming, the sum of all words in the EEPROM should equal 0xBABA */ #define EEPROM_SUM 0xBABA struct atlx_spi_flash_dev { const char *manu_name; /* manufacturer id */ /* op-code */ u8 cmd_wrsr; u8 cmd_read; u8 cmd_program; u8 cmd_wren; u8 cmd_wrdi; u8 cmd_rdsr; u8 cmd_rdid; u8 cmd_sector_erase; u8 cmd_chip_erase; }; #endif /* ATLX_H */ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/atl2.h0000644000175000017500000003756312026211315024110 0ustar mcgrofmcgrof/* atl2.h -- atl2 driver definitions * * Copyright(c) 2007 Atheros Corporation. All rights reserved. * Copyright(c) 2006 xiong huang * Copyright(c) 2007 Chris Snook * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _ATL2_H_ #define _ATL2_H_ #include #include #ifndef _ATL2_HW_H_ #define _ATL2_HW_H_ #ifndef _ATL2_OSDEP_H_ #define _ATL2_OSDEP_H_ #include #include #include #include #include "atlx.h" #ifdef ETHTOOL_OPS_COMPAT extern int ethtool_ioctl(struct ifreq *ifr); #endif #define PCI_COMMAND_REGISTER PCI_COMMAND #define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE #define ATL2_WRITE_REG(a, reg, value) (iowrite32((value), \ ((a)->hw_addr + (reg)))) #define ATL2_WRITE_FLUSH(a) (ioread32((a)->hw_addr)) #define ATL2_READ_REG(a, reg) (ioread32((a)->hw_addr + (reg))) #define ATL2_WRITE_REGB(a, reg, value) (iowrite8((value), \ ((a)->hw_addr + (reg)))) #define ATL2_READ_REGB(a, reg) (ioread8((a)->hw_addr + (reg))) #define ATL2_WRITE_REGW(a, reg, value) (iowrite16((value), \ ((a)->hw_addr + (reg)))) #define ATL2_READ_REGW(a, reg) (ioread16((a)->hw_addr + (reg))) #define ATL2_WRITE_REG_ARRAY(a, reg, offset, value) \ (iowrite32((value), (((a)->hw_addr + (reg)) + ((offset) << 2)))) #define ATL2_READ_REG_ARRAY(a, reg, offset) \ (ioread32(((a)->hw_addr + (reg)) + ((offset) << 2))) #endif /* _ATL2_OSDEP_H_ */ struct atl2_adapter; struct atl2_hw; /* function prototype */ static s32 atl2_reset_hw(struct atl2_hw *hw); static s32 atl2_read_mac_addr(struct atl2_hw *hw); static s32 atl2_init_hw(struct atl2_hw *hw); static s32 atl2_get_speed_and_duplex(struct atl2_hw *hw, u16 *speed, u16 *duplex); static u32 atl2_hash_mc_addr(struct atl2_hw *hw, u8 *mc_addr); static void atl2_hash_set(struct atl2_hw *hw, u32 hash_value); static s32 atl2_read_phy_reg(struct atl2_hw *hw, u16 reg_addr, u16 *phy_data); static s32 atl2_write_phy_reg(struct atl2_hw *hw, u32 reg_addr, u16 phy_data); static void atl2_read_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value); static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value); static void atl2_set_mac_addr(struct atl2_hw *hw); static bool atl2_read_eeprom(struct atl2_hw *hw, u32 Offset, u32 *pValue); static bool atl2_write_eeprom(struct atl2_hw *hw, u32 offset, u32 value); static s32 atl2_phy_init(struct atl2_hw *hw); static int atl2_check_eeprom_exist(struct atl2_hw *hw); static void atl2_force_ps(struct atl2_hw *hw); /* register definition */ /* Block IDLE Status Register */ #define IDLE_STATUS_RXMAC 1 /* 1: RXMAC is non-IDLE */ #define IDLE_STATUS_TXMAC 2 /* 1: TXMAC is non-IDLE */ #define IDLE_STATUS_DMAR 8 /* 1: DMAR is non-IDLE */ #define IDLE_STATUS_DMAW 4 /* 1: DMAW is non-IDLE */ /* MDIO Control Register */ #define MDIO_WAIT_TIMES 10 /* MAC Control Register */ #define MAC_CTRL_DBG_TX_BKPRESURE 0x100000 /* 1: TX max backoff */ #define MAC_CTRL_MACLP_CLK_PHY 0x8000000 /* 1: 25MHz from phy */ #define MAC_CTRL_HALF_LEFT_BUF_SHIFT 28 #define MAC_CTRL_HALF_LEFT_BUF_MASK 0xF /* MAC retry buf x32B */ /* Internal SRAM Partition Register */ #define REG_SRAM_TXRAM_END 0x1500 /* Internal tail address of TXRAM * default: 2byte*1024 */ #define REG_SRAM_RXRAM_END 0x1502 /* Internal tail address of RXRAM * default: 2byte*1024 */ /* Descriptor Control register */ #define REG_TXD_BASE_ADDR_LO 0x1544 /* The base address of the Transmit * Data Mem low 32-bit(dword align) */ #define REG_TXD_MEM_SIZE 0x1548 /* Transmit Data Memory size(by * double word , max 256KB) */ #define REG_TXS_BASE_ADDR_LO 0x154C /* The base address of the Transmit * Status Memory low 32-bit(dword word * align) */ #define REG_TXS_MEM_SIZE 0x1550 /* double word unit, max 4*2047 * bytes. */ #define REG_RXD_BASE_ADDR_LO 0x1554 /* The base address of the Transmit * Status Memory low 32-bit(unit 8 * bytes) */ #define REG_RXD_BUF_NUM 0x1558 /* Receive Data & Status Memory buffer * number (unit 1536bytes, max * 1536*2047) */ /* DMAR Control Register */ #define REG_DMAR 0x1580 #define DMAR_EN 0x1 /* 1: Enable DMAR */ /* TX Cur-Through (early tx threshold) Control Register */ #define REG_TX_CUT_THRESH 0x1590 /* TxMac begin transmit packet * threshold(unit word) */ /* DMAW Control Register */ #define REG_DMAW 0x15A0 #define DMAW_EN 0x1 /* Flow control register */ #define REG_PAUSE_ON_TH 0x15A8 /* RXD high watermark of overflow * threshold configuration register */ #define REG_PAUSE_OFF_TH 0x15AA /* RXD lower watermark of overflow * threshold configuration register */ /* Mailbox Register */ #define REG_MB_TXD_WR_IDX 0x15f0 /* double word align */ #define REG_MB_RXD_RD_IDX 0x15F4 /* RXD Read index (unit: 1536byets) */ /* Interrupt Status Register */ #define ISR_TIMER 1 /* Interrupt when Timer counts down to zero */ #define ISR_MANUAL 2 /* Software manual interrupt, for debug. Set * when SW_MAN_INT_EN is set in Table 51 * Selene Master Control Register * (Offset 0x1400). */ #define ISR_RXF_OV 4 /* RXF overflow interrupt */ #define ISR_TXF_UR 8 /* TXF underrun interrupt */ #define ISR_TXS_OV 0x10 /* Internal transmit status buffer full * interrupt */ #define ISR_RXS_OV 0x20 /* Internal receive status buffer full * interrupt */ #define ISR_LINK_CHG 0x40 /* Link Status Change Interrupt */ #define ISR_HOST_TXD_UR 0x80 #define ISR_HOST_RXD_OV 0x100 /* Host rx data memory full , one pulse */ #define ISR_DMAR_TO_RST 0x200 /* DMAR op timeout interrupt. SW should * do Reset */ #define ISR_DMAW_TO_RST 0x400 #define ISR_PHY 0x800 /* phy interrupt */ #define ISR_TS_UPDATE 0x10000 /* interrupt after new tx pkt status written * to host */ #define ISR_RS_UPDATE 0x20000 /* interrupt ater new rx pkt status written * to host. */ #define ISR_TX_EARLY 0x40000 /* interrupt when txmac begin transmit one * packet */ #define ISR_TX_EVENT (ISR_TXF_UR | ISR_TXS_OV | ISR_HOST_TXD_UR |\ ISR_TS_UPDATE | ISR_TX_EARLY) #define ISR_RX_EVENT (ISR_RXF_OV | ISR_RXS_OV | ISR_HOST_RXD_OV |\ ISR_RS_UPDATE) #define IMR_NORMAL_MASK (\ /*ISR_LINK_CHG |*/\ ISR_MANUAL |\ ISR_DMAR_TO_RST |\ ISR_DMAW_TO_RST |\ ISR_PHY |\ ISR_PHY_LINKDOWN |\ ISR_TS_UPDATE |\ ISR_RS_UPDATE) /* Receive MAC Statistics Registers */ #define REG_STS_RX_PAUSE 0x1700 /* Num pause packets received */ #define REG_STS_RXD_OV 0x1704 /* Num frames dropped due to RX * FIFO overflow */ #define REG_STS_RXS_OV 0x1708 /* Num frames dropped due to RX * Status Buffer Overflow */ #define REG_STS_RX_FILTER 0x170C /* Num packets dropped due to * address filtering */ /* MII definitions */ /* PHY Common Register */ #define MII_SMARTSPEED 0x14 #define MII_DBG_ADDR 0x1D #define MII_DBG_DATA 0x1E /* PCI Command Register Bit Definitions */ #define PCI_REG_COMMAND 0x04 #define CMD_IO_SPACE 0x0001 #define CMD_MEMORY_SPACE 0x0002 #define CMD_BUS_MASTER 0x0004 #define MEDIA_TYPE_100M_FULL 1 #define MEDIA_TYPE_100M_HALF 2 #define MEDIA_TYPE_10M_FULL 3 #define MEDIA_TYPE_10M_HALF 4 #define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x000F /* Everything */ /* The size (in bytes) of a ethernet packet */ #define ENET_HEADER_SIZE 14 #define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* with FCS */ #define MINIMUM_ETHERNET_FRAME_SIZE 64 /* with FCS */ #define ETHERNET_FCS_SIZE 4 #define MAX_JUMBO_FRAME_SIZE 0x2000 #define VLAN_SIZE 4 struct tx_pkt_header { unsigned pkt_size:11; unsigned:4; /* reserved */ unsigned ins_vlan:1; /* txmac should insert vlan */ unsigned short vlan; /* vlan tag */ }; /* FIXME: replace above bitfields with MASK/SHIFT defines below */ #define TX_PKT_HEADER_SIZE_MASK 0x7FF #define TX_PKT_HEADER_SIZE_SHIFT 0 #define TX_PKT_HEADER_INS_VLAN_MASK 0x1 #define TX_PKT_HEADER_INS_VLAN_SHIFT 15 #define TX_PKT_HEADER_VLAN_TAG_MASK 0xFFFF #define TX_PKT_HEADER_VLAN_TAG_SHIFT 16 struct tx_pkt_status { unsigned pkt_size:11; unsigned:5; /* reserved */ unsigned ok:1; /* current packet transmitted without error */ unsigned bcast:1; /* broadcast packet */ unsigned mcast:1; /* multicast packet */ unsigned pause:1; /* transmiited a pause frame */ unsigned ctrl:1; unsigned defer:1; /* current packet is xmitted with defer */ unsigned exc_defer:1; unsigned single_col:1; unsigned multi_col:1; unsigned late_col:1; unsigned abort_col:1; unsigned underun:1; /* current packet is aborted * due to txram underrun */ unsigned:3; /* reserved */ unsigned update:1; /* always 1'b1 in tx_status_buf */ }; /* FIXME: replace above bitfields with MASK/SHIFT defines below */ #define TX_PKT_STATUS_SIZE_MASK 0x7FF #define TX_PKT_STATUS_SIZE_SHIFT 0 #define TX_PKT_STATUS_OK_MASK 0x1 #define TX_PKT_STATUS_OK_SHIFT 16 #define TX_PKT_STATUS_BCAST_MASK 0x1 #define TX_PKT_STATUS_BCAST_SHIFT 17 #define TX_PKT_STATUS_MCAST_MASK 0x1 #define TX_PKT_STATUS_MCAST_SHIFT 18 #define TX_PKT_STATUS_PAUSE_MASK 0x1 #define TX_PKT_STATUS_PAUSE_SHIFT 19 #define TX_PKT_STATUS_CTRL_MASK 0x1 #define TX_PKT_STATUS_CTRL_SHIFT 20 #define TX_PKT_STATUS_DEFER_MASK 0x1 #define TX_PKT_STATUS_DEFER_SHIFT 21 #define TX_PKT_STATUS_EXC_DEFER_MASK 0x1 #define TX_PKT_STATUS_EXC_DEFER_SHIFT 22 #define TX_PKT_STATUS_SINGLE_COL_MASK 0x1 #define TX_PKT_STATUS_SINGLE_COL_SHIFT 23 #define TX_PKT_STATUS_MULTI_COL_MASK 0x1 #define TX_PKT_STATUS_MULTI_COL_SHIFT 24 #define TX_PKT_STATUS_LATE_COL_MASK 0x1 #define TX_PKT_STATUS_LATE_COL_SHIFT 25 #define TX_PKT_STATUS_ABORT_COL_MASK 0x1 #define TX_PKT_STATUS_ABORT_COL_SHIFT 26 #define TX_PKT_STATUS_UNDERRUN_MASK 0x1 #define TX_PKT_STATUS_UNDERRUN_SHIFT 27 #define TX_PKT_STATUS_UPDATE_MASK 0x1 #define TX_PKT_STATUS_UPDATE_SHIFT 31 struct rx_pkt_status { unsigned pkt_size:11; /* packet size, max 2047 bytes */ unsigned:5; /* reserved */ unsigned ok:1; /* current packet received ok without error */ unsigned bcast:1; /* current packet is broadcast */ unsigned mcast:1; /* current packet is multicast */ unsigned pause:1; unsigned ctrl:1; unsigned crc:1; /* received a packet with crc error */ unsigned code:1; /* received a packet with code error */ unsigned runt:1; /* received a packet less than 64 bytes * with good crc */ unsigned frag:1; /* received a packet less than 64 bytes * with bad crc */ unsigned trunc:1; /* current frame truncated due to rxram full */ unsigned align:1; /* this packet is alignment error */ unsigned vlan:1; /* this packet has vlan */ unsigned:3; /* reserved */ unsigned update:1; unsigned short vtag; /* vlan tag */ unsigned:16; }; /* FIXME: replace above bitfields with MASK/SHIFT defines below */ #define RX_PKT_STATUS_SIZE_MASK 0x7FF #define RX_PKT_STATUS_SIZE_SHIFT 0 #define RX_PKT_STATUS_OK_MASK 0x1 #define RX_PKT_STATUS_OK_SHIFT 16 #define RX_PKT_STATUS_BCAST_MASK 0x1 #define RX_PKT_STATUS_BCAST_SHIFT 17 #define RX_PKT_STATUS_MCAST_MASK 0x1 #define RX_PKT_STATUS_MCAST_SHIFT 18 #define RX_PKT_STATUS_PAUSE_MASK 0x1 #define RX_PKT_STATUS_PAUSE_SHIFT 19 #define RX_PKT_STATUS_CTRL_MASK 0x1 #define RX_PKT_STATUS_CTRL_SHIFT 20 #define RX_PKT_STATUS_CRC_MASK 0x1 #define RX_PKT_STATUS_CRC_SHIFT 21 #define RX_PKT_STATUS_CODE_MASK 0x1 #define RX_PKT_STATUS_CODE_SHIFT 22 #define RX_PKT_STATUS_RUNT_MASK 0x1 #define RX_PKT_STATUS_RUNT_SHIFT 23 #define RX_PKT_STATUS_FRAG_MASK 0x1 #define RX_PKT_STATUS_FRAG_SHIFT 24 #define RX_PKT_STATUS_TRUNK_MASK 0x1 #define RX_PKT_STATUS_TRUNK_SHIFT 25 #define RX_PKT_STATUS_ALIGN_MASK 0x1 #define RX_PKT_STATUS_ALIGN_SHIFT 26 #define RX_PKT_STATUS_VLAN_MASK 0x1 #define RX_PKT_STATUS_VLAN_SHIFT 27 #define RX_PKT_STATUS_UPDATE_MASK 0x1 #define RX_PKT_STATUS_UPDATE_SHIFT 31 #define RX_PKT_STATUS_VLAN_TAG_MASK 0xFFFF #define RX_PKT_STATUS_VLAN_TAG_SHIFT 32 struct rx_desc { struct rx_pkt_status status; unsigned char packet[1536-sizeof(struct rx_pkt_status)]; }; enum atl2_speed_duplex { atl2_10_half = 0, atl2_10_full = 1, atl2_100_half = 2, atl2_100_full = 3 }; struct atl2_spi_flash_dev { const char *manu_name; /* manufacturer id */ /* op-code */ u8 cmdWRSR; u8 cmdREAD; u8 cmdPROGRAM; u8 cmdWREN; u8 cmdWRDI; u8 cmdRDSR; u8 cmdRDID; u8 cmdSECTOR_ERASE; u8 cmdCHIP_ERASE; }; /* Structure containing variables used by the shared code (atl2_hw.c) */ struct atl2_hw { u8 __iomem *hw_addr; void *back; u8 preamble_len; u8 max_retry; /* Retransmission maximum, afterwards the * packet will be discarded. */ u8 jam_ipg; /* IPG to start JAM for collision based flow * control in half-duplex mode. In unit of * 8-bit time. */ u8 ipgt; /* Desired back to back inter-packet gap. The * default is 96-bit time. */ u8 min_ifg; /* Minimum number of IFG to enforce in between * RX frames. Frame gap below such IFP is * dropped. */ u8 ipgr1; /* 64bit Carrier-Sense window */ u8 ipgr2; /* 96-bit IPG window */ u8 retry_buf; /* When half-duplex mode, should hold some * bytes for mac retry . (8*4bytes unit) */ u16 fc_rxd_hi; u16 fc_rxd_lo; u16 lcol; /* Collision Window */ u16 max_frame_size; u16 MediaType; u16 autoneg_advertised; u16 pci_cmd_word; u16 mii_autoneg_adv_reg; u32 mem_rang; u32 txcw; u32 mc_filter_type; u32 num_mc_addrs; u32 collision_delta; u32 tx_packet_delta; u16 phy_spd_default; u16 device_id; u16 vendor_id; u16 subsystem_id; u16 subsystem_vendor_id; u8 revision_id; /* spi flash */ u8 flash_vendor; u8 dma_fairness; u8 mac_addr[ETH_ALEN]; u8 perm_mac_addr[ETH_ALEN]; /* FIXME */ /* bool phy_preamble_sup; */ bool phy_configured; }; #endif /* _ATL2_HW_H_ */ struct atl2_ring_header { /* pointer to the descriptor ring memory */ void *desc; /* physical address of the descriptor ring */ dma_addr_t dma; /* length of descriptor ring in bytes */ unsigned int size; }; /* board specific private data structure */ struct atl2_adapter { /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; u32 wol; u16 link_speed; u16 link_duplex; spinlock_t stats_lock; struct work_struct reset_task; struct work_struct link_chg_task; struct timer_list watchdog_timer; struct timer_list phy_config_timer; unsigned long cfg_phy; bool mac_disabled; /* All Descriptor memory */ dma_addr_t ring_dma; void *ring_vir_addr; int ring_size; struct tx_pkt_header *txd_ring; dma_addr_t txd_dma; struct tx_pkt_status *txs_ring; dma_addr_t txs_dma; struct rx_desc *rxd_ring; dma_addr_t rxd_dma; u32 txd_ring_size; /* bytes per unit */ u32 txs_ring_size; /* dwords per unit */ u32 rxd_ring_size; /* 1536 bytes per unit */ /* read /write ptr: */ /* host */ u32 txd_write_ptr; u32 txs_next_clear; u32 rxd_read_ptr; /* nic */ atomic_t txd_read_ptr; atomic_t txs_write_ptr; u32 rxd_write_ptr; /* Interrupt Moderator timer ( 2us resolution) */ u16 imt; /* Interrupt Clear timer (2us resolution) */ u16 ict; unsigned long flags; /* structs defined in atl2_hw.h */ u32 bd_number; /* board number */ bool pci_using_64; bool have_msi; struct atl2_hw hw; u32 usr_cmd; /* FIXME */ /* u32 regs_buff[ATL2_REGS_LEN]; */ u32 pci_state[16]; u32 *config_space; }; enum atl2_state_t { __ATL2_TESTING, __ATL2_RESETTING, __ATL2_DOWN }; #endif /* _ATL2_H_ */ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/atl1.h0000644000175000017500000005676012026211315024107 0ustar mcgrofmcgrof/* * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. * Copyright(c) 2006 - 2007 Chris Snook * Copyright(c) 2006 - 2008 Jay Cliburn * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ATL1_H #define ATL1_H #include #include #include #include #include #include #include #include #include #include #include "atlx.h" #define ATLX_DRIVER_NAME "atl1" MODULE_DESCRIPTION("Atheros L1 Gigabit Ethernet Driver"); #define atlx_adapter atl1_adapter #define atlx_check_for_link atl1_check_for_link #define atlx_check_link atl1_check_link #define atlx_hash_mc_addr atl1_hash_mc_addr #define atlx_hash_set atl1_hash_set #define atlx_hw atl1_hw #define atlx_mii_ioctl atl1_mii_ioctl #define atlx_read_phy_reg atl1_read_phy_reg #define atlx_set_mac atl1_set_mac #define atlx_set_mac_addr atl1_set_mac_addr struct atl1_adapter; struct atl1_hw; /* function prototypes needed by multiple files */ static u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr); static void atl1_hash_set(struct atl1_hw *hw, u32 hash_value); static void atl1_set_mac_addr(struct atl1_hw *hw); static int atl1_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); static u32 atl1_check_link(struct atl1_adapter *adapter); /* hardware definitions specific to L1 */ /* Block IDLE Status Register */ #define IDLE_STATUS_RXMAC 0x1 #define IDLE_STATUS_TXMAC 0x2 #define IDLE_STATUS_RXQ 0x4 #define IDLE_STATUS_TXQ 0x8 #define IDLE_STATUS_DMAR 0x10 #define IDLE_STATUS_DMAW 0x20 #define IDLE_STATUS_SMB 0x40 #define IDLE_STATUS_CMB 0x80 /* MDIO Control Register */ #define MDIO_WAIT_TIMES 30 /* MAC Control Register */ #define MAC_CTRL_TX_PAUSE 0x10000 #define MAC_CTRL_SCNT 0x20000 #define MAC_CTRL_SRST_TX 0x40000 #define MAC_CTRL_TX_SIMURST 0x80000 #define MAC_CTRL_SPEED_SHIFT 20 #define MAC_CTRL_SPEED_MASK 0x300000 #define MAC_CTRL_SPEED_1000 0x2 #define MAC_CTRL_SPEED_10_100 0x1 #define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 #define MAC_CTRL_TX_HUGE 0x800000 #define MAC_CTRL_RX_CHKSUM_EN 0x1000000 #define MAC_CTRL_DBG 0x8000000 /* Wake-On-Lan control register */ #define WOL_CLK_SWITCH_EN 0x8000 #define WOL_PT5_EN 0x200000 #define WOL_PT6_EN 0x400000 #define WOL_PT5_MATCH 0x8000000 #define WOL_PT6_MATCH 0x10000000 /* WOL Length ( 2 DWORD ) */ #define REG_WOL_PATTERN_LEN 0x14A4 #define WOL_PT_LEN_MASK 0x7F #define WOL_PT0_LEN_SHIFT 0 #define WOL_PT1_LEN_SHIFT 8 #define WOL_PT2_LEN_SHIFT 16 #define WOL_PT3_LEN_SHIFT 24 #define WOL_PT4_LEN_SHIFT 0 #define WOL_PT5_LEN_SHIFT 8 #define WOL_PT6_LEN_SHIFT 16 /* Internal SRAM Partition Registers, low 32 bits */ #define REG_SRAM_RFD_LEN 0x1504 #define REG_SRAM_RRD_ADDR 0x1508 #define REG_SRAM_RRD_LEN 0x150C #define REG_SRAM_TPD_ADDR 0x1510 #define REG_SRAM_TPD_LEN 0x1514 #define REG_SRAM_TRD_ADDR 0x1518 #define REG_SRAM_TRD_LEN 0x151C #define REG_SRAM_RXF_ADDR 0x1520 #define REG_SRAM_RXF_LEN 0x1524 #define REG_SRAM_TXF_ADDR 0x1528 #define REG_SRAM_TXF_LEN 0x152C #define REG_SRAM_TCPH_PATH_ADDR 0x1530 #define SRAM_TCPH_ADDR_MASK 0xFFF #define SRAM_TCPH_ADDR_SHIFT 0 #define SRAM_PATH_ADDR_MASK 0xFFF #define SRAM_PATH_ADDR_SHIFT 16 /* Load Ptr Register */ #define REG_LOAD_PTR 0x1534 /* Descriptor Control registers, low 32 bits */ #define REG_DESC_RFD_ADDR_LO 0x1544 #define REG_DESC_RRD_ADDR_LO 0x1548 #define REG_DESC_TPD_ADDR_LO 0x154C #define REG_DESC_CMB_ADDR_LO 0x1550 #define REG_DESC_SMB_ADDR_LO 0x1554 #define REG_DESC_RFD_RRD_RING_SIZE 0x1558 #define DESC_RFD_RING_SIZE_MASK 0x7FF #define DESC_RFD_RING_SIZE_SHIFT 0 #define DESC_RRD_RING_SIZE_MASK 0x7FF #define DESC_RRD_RING_SIZE_SHIFT 16 #define REG_DESC_TPD_RING_SIZE 0x155C #define DESC_TPD_RING_SIZE_MASK 0x3FF #define DESC_TPD_RING_SIZE_SHIFT 0 /* TXQ Control Register */ #define REG_TXQ_CTRL 0x1580 #define TXQ_CTRL_TPD_BURST_NUM_SHIFT 0 #define TXQ_CTRL_TPD_BURST_NUM_MASK 0x1F #define TXQ_CTRL_EN 0x20 #define TXQ_CTRL_ENH_MODE 0x40 #define TXQ_CTRL_TPD_FETCH_TH_SHIFT 8 #define TXQ_CTRL_TPD_FETCH_TH_MASK 0x3F #define TXQ_CTRL_TXF_BURST_NUM_SHIFT 16 #define TXQ_CTRL_TXF_BURST_NUM_MASK 0xFFFF /* Jumbo packet Threshold for task offload */ #define REG_TX_JUMBO_TASK_TH_TPD_IPG 0x1584 #define TX_JUMBO_TASK_TH_MASK 0x7FF #define TX_JUMBO_TASK_TH_SHIFT 0 #define TX_TPD_MIN_IPG_MASK 0x1F #define TX_TPD_MIN_IPG_SHIFT 16 /* RXQ Control Register */ #define REG_RXQ_CTRL 0x15A0 #define RXQ_CTRL_RFD_BURST_NUM_SHIFT 0 #define RXQ_CTRL_RFD_BURST_NUM_MASK 0xFF #define RXQ_CTRL_RRD_BURST_THRESH_SHIFT 8 #define RXQ_CTRL_RRD_BURST_THRESH_MASK 0xFF #define RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT 16 #define RXQ_CTRL_RFD_PREF_MIN_IPG_MASK 0x1F #define RXQ_CTRL_CUT_THRU_EN 0x40000000 #define RXQ_CTRL_EN 0x80000000 /* Rx jumbo packet threshold and rrd retirement timer */ #define REG_RXQ_JMBOSZ_RRDTIM 0x15A4 #define RXQ_JMBOSZ_TH_MASK 0x7FF #define RXQ_JMBOSZ_TH_SHIFT 0 #define RXQ_JMBO_LKAH_MASK 0xF #define RXQ_JMBO_LKAH_SHIFT 11 #define RXQ_RRD_TIMER_MASK 0xFFFF #define RXQ_RRD_TIMER_SHIFT 16 /* RFD flow control register */ #define REG_RXQ_RXF_PAUSE_THRESH 0x15A8 #define RXQ_RXF_PAUSE_TH_HI_SHIFT 16 #define RXQ_RXF_PAUSE_TH_HI_MASK 0xFFF #define RXQ_RXF_PAUSE_TH_LO_SHIFT 0 #define RXQ_RXF_PAUSE_TH_LO_MASK 0xFFF /* RRD flow control register */ #define REG_RXQ_RRD_PAUSE_THRESH 0x15AC #define RXQ_RRD_PAUSE_TH_HI_SHIFT 0 #define RXQ_RRD_PAUSE_TH_HI_MASK 0xFFF #define RXQ_RRD_PAUSE_TH_LO_SHIFT 16 #define RXQ_RRD_PAUSE_TH_LO_MASK 0xFFF /* DMA Engine Control Register */ #define REG_DMA_CTRL 0x15C0 #define DMA_CTRL_DMAR_IN_ORDER 0x1 #define DMA_CTRL_DMAR_ENH_ORDER 0x2 #define DMA_CTRL_DMAR_OUT_ORDER 0x4 #define DMA_CTRL_RCB_VALUE 0x8 #define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4 #define DMA_CTRL_DMAR_BURST_LEN_MASK 7 #define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7 #define DMA_CTRL_DMAW_BURST_LEN_MASK 7 #define DMA_CTRL_DMAR_EN 0x400 #define DMA_CTRL_DMAW_EN 0x800 /* CMB/SMB Control Register */ #define REG_CSMB_CTRL 0x15D0 #define CSMB_CTRL_CMB_NOW 1 #define CSMB_CTRL_SMB_NOW 2 #define CSMB_CTRL_CMB_EN 4 #define CSMB_CTRL_SMB_EN 8 /* CMB DMA Write Threshold Register */ #define REG_CMB_WRITE_TH 0x15D4 #define CMB_RRD_TH_SHIFT 0 #define CMB_RRD_TH_MASK 0x7FF #define CMB_TPD_TH_SHIFT 16 #define CMB_TPD_TH_MASK 0x7FF /* RX/TX count-down timer to trigger CMB-write. 2us resolution. */ #define REG_CMB_WRITE_TIMER 0x15D8 #define CMB_RX_TM_SHIFT 0 #define CMB_RX_TM_MASK 0xFFFF #define CMB_TX_TM_SHIFT 16 #define CMB_TX_TM_MASK 0xFFFF /* Number of packet received since last CMB write */ #define REG_CMB_RX_PKT_CNT 0x15DC /* Number of packet transmitted since last CMB write */ #define REG_CMB_TX_PKT_CNT 0x15E0 /* SMB auto DMA timer register */ #define REG_SMB_TIMER 0x15E4 /* Mailbox Register */ #define REG_MAILBOX 0x15F0 #define MB_RFD_PROD_INDX_SHIFT 0 #define MB_RFD_PROD_INDX_MASK 0x7FF #define MB_RRD_CONS_INDX_SHIFT 11 #define MB_RRD_CONS_INDX_MASK 0x7FF #define MB_TPD_PROD_INDX_SHIFT 22 #define MB_TPD_PROD_INDX_MASK 0x3FF /* Interrupt Status Register */ #define ISR_SMB 0x1 #define ISR_TIMER 0x2 #define ISR_MANUAL 0x4 #define ISR_RXF_OV 0x8 #define ISR_RFD_UNRUN 0x10 #define ISR_RRD_OV 0x20 #define ISR_TXF_UNRUN 0x40 #define ISR_LINK 0x80 #define ISR_HOST_RFD_UNRUN 0x100 #define ISR_HOST_RRD_OV 0x200 #define ISR_DMAR_TO_RST 0x400 #define ISR_DMAW_TO_RST 0x800 #define ISR_GPHY 0x1000 #define ISR_RX_PKT 0x10000 #define ISR_TX_PKT 0x20000 #define ISR_TX_DMA 0x40000 #define ISR_RX_DMA 0x80000 #define ISR_CMB_RX 0x100000 #define ISR_CMB_TX 0x200000 #define ISR_MAC_RX 0x400000 #define ISR_MAC_TX 0x800000 #define ISR_DIS_SMB 0x20000000 #define ISR_DIS_DMA 0x40000000 /* Normal Interrupt mask without RX/TX enabled */ #define IMR_NORXTX_MASK (\ ISR_SMB |\ ISR_GPHY |\ ISR_PHY_LINKDOWN|\ ISR_DMAR_TO_RST |\ ISR_DMAW_TO_RST) /* Normal Interrupt mask */ #define IMR_NORMAL_MASK (\ IMR_NORXTX_MASK |\ ISR_CMB_TX |\ ISR_CMB_RX) /* Debug Interrupt Mask (enable all interrupt) */ #define IMR_DEBUG_MASK (\ ISR_SMB |\ ISR_TIMER |\ ISR_MANUAL |\ ISR_RXF_OV |\ ISR_RFD_UNRUN |\ ISR_RRD_OV |\ ISR_TXF_UNRUN |\ ISR_LINK |\ ISR_CMB_TX |\ ISR_CMB_RX |\ ISR_RX_PKT |\ ISR_TX_PKT |\ ISR_MAC_RX |\ ISR_MAC_TX) #define MEDIA_TYPE_1000M_FULL 1 #define MEDIA_TYPE_100M_FULL 2 #define MEDIA_TYPE_100M_HALF 3 #define MEDIA_TYPE_10M_FULL 4 #define MEDIA_TYPE_10M_HALF 5 #define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* All but 1000-Half */ #define MAX_JUMBO_FRAME_SIZE 10240 #define ATL1_EEDUMP_LEN 48 /* Statistics counters collected by the MAC */ struct stats_msg_block { /* rx */ u32 rx_ok; /* good RX packets */ u32 rx_bcast; /* good RX broadcast packets */ u32 rx_mcast; /* good RX multicast packets */ u32 rx_pause; /* RX pause frames */ u32 rx_ctrl; /* RX control packets other than pause frames */ u32 rx_fcs_err; /* RX packets with bad FCS */ u32 rx_len_err; /* RX packets with length != actual size */ u32 rx_byte_cnt; /* good bytes received. FCS is NOT included */ u32 rx_runt; /* RX packets < 64 bytes with good FCS */ u32 rx_frag; /* RX packets < 64 bytes with bad FCS */ u32 rx_sz_64; /* 64 byte RX packets */ u32 rx_sz_65_127; u32 rx_sz_128_255; u32 rx_sz_256_511; u32 rx_sz_512_1023; u32 rx_sz_1024_1518; u32 rx_sz_1519_max; /* 1519 byte to MTU RX packets */ u32 rx_sz_ov; /* truncated RX packets > MTU */ u32 rx_rxf_ov; /* frames dropped due to RX FIFO overflow */ u32 rx_rrd_ov; /* frames dropped due to RRD overflow */ u32 rx_align_err; /* alignment errors */ u32 rx_bcast_byte_cnt; /* RX broadcast bytes, excluding FCS */ u32 rx_mcast_byte_cnt; /* RX multicast bytes, excluding FCS */ u32 rx_err_addr; /* packets dropped due to address filtering */ /* tx */ u32 tx_ok; /* good TX packets */ u32 tx_bcast; /* good TX broadcast packets */ u32 tx_mcast; /* good TX multicast packets */ u32 tx_pause; /* TX pause frames */ u32 tx_exc_defer; /* TX packets deferred excessively */ u32 tx_ctrl; /* TX control frames, excluding pause frames */ u32 tx_defer; /* TX packets deferred */ u32 tx_byte_cnt; /* bytes transmitted, FCS is NOT included */ u32 tx_sz_64; /* 64 byte TX packets */ u32 tx_sz_65_127; u32 tx_sz_128_255; u32 tx_sz_256_511; u32 tx_sz_512_1023; u32 tx_sz_1024_1518; u32 tx_sz_1519_max; /* 1519 byte to MTU TX packets */ u32 tx_1_col; /* packets TX after a single collision */ u32 tx_2_col; /* packets TX after multiple collisions */ u32 tx_late_col; /* TX packets with late collisions */ u32 tx_abort_col; /* TX packets aborted w/excessive collisions */ u32 tx_underrun; /* TX packets aborted due to TX FIFO underrun * or TRD FIFO underrun */ u32 tx_rd_eop; /* reads beyond the EOP into the next frame * when TRD was not written timely */ u32 tx_len_err; /* TX packets where length != actual size */ u32 tx_trunc; /* TX packets truncated due to size > MTU */ u32 tx_bcast_byte; /* broadcast bytes transmitted, excluding FCS */ u32 tx_mcast_byte; /* multicast bytes transmitted, excluding FCS */ u32 smb_updated; /* 1: SMB Updated. This is used by software to * indicate the statistics update. Software * should clear this bit after retrieving the * statistics information. */ }; /* Coalescing Message Block */ struct coals_msg_block { u32 int_stats; /* interrupt status */ u16 rrd_prod_idx; /* TRD Producer Index. */ u16 rfd_cons_idx; /* RFD Consumer Index. */ u16 update; /* Selene sets this bit every time it DMAs the * CMB to host memory. Software should clear * this bit when CMB info is processed. */ u16 tpd_cons_idx; /* TPD Consumer Index. */ }; /* RRD descriptor */ struct rx_return_desc { u8 num_buf; /* Number of RFD buffers used by the received packet */ u8 resved; u16 buf_indx; /* RFD Index of the first buffer */ union { u32 valid; struct { u16 rx_chksum; u16 pkt_size; } xsum_sz; } xsz; u16 pkt_flg; /* Packet flags */ u16 err_flg; /* Error flags */ u16 resved2; u16 vlan_tag; /* VLAN TAG */ }; #define PACKET_FLAG_ETH_TYPE 0x0080 #define PACKET_FLAG_VLAN_INS 0x0100 #define PACKET_FLAG_ERR 0x0200 #define PACKET_FLAG_IPV4 0x0400 #define PACKET_FLAG_UDP 0x0800 #define PACKET_FLAG_TCP 0x1000 #define PACKET_FLAG_BCAST 0x2000 #define PACKET_FLAG_MCAST 0x4000 #define PACKET_FLAG_PAUSE 0x8000 #define ERR_FLAG_CRC 0x0001 #define ERR_FLAG_CODE 0x0002 #define ERR_FLAG_DRIBBLE 0x0004 #define ERR_FLAG_RUNT 0x0008 #define ERR_FLAG_OV 0x0010 #define ERR_FLAG_TRUNC 0x0020 #define ERR_FLAG_IP_CHKSUM 0x0040 #define ERR_FLAG_L4_CHKSUM 0x0080 #define ERR_FLAG_LEN 0x0100 #define ERR_FLAG_DES_ADDR 0x0200 /* RFD descriptor */ struct rx_free_desc { __le64 buffer_addr; /* Address of the descriptor's data buffer */ __le16 buf_len; /* Size of the receive buffer in host memory */ u16 coalese; /* Update consumer index to host after the * reception of this frame */ /* __packed is required */ } __packed; /* * The L1 transmit packet descriptor is comprised of four 32-bit words. * * 31 0 * +---------------------------------------+ * | Word 0: Buffer addr lo | * +---------------------------------------+ * | Word 1: Buffer addr hi | * +---------------------------------------+ * | Word 2 | * +---------------------------------------+ * | Word 3 | * +---------------------------------------+ * * Words 0 and 1 combine to form a 64-bit buffer address. * * Word 2 is self explanatory in the #define block below. * * Word 3 has two forms, depending upon the state of bits 3 and 4. * If bits 3 and 4 are both zero, then bits 14:31 are unused by the * hardware. Otherwise, if either bit 3 or 4 is set, the definition * of bits 14:31 vary according to the following depiction. * * 0 End of packet 0 End of packet * 1 Coalesce 1 Coalesce * 2 Insert VLAN tag 2 Insert VLAN tag * 3 Custom csum enable = 0 3 Custom csum enable = 1 * 4 Segment enable = 1 4 Segment enable = 0 * 5 Generate IP checksum 5 Generate IP checksum * 6 Generate TCP checksum 6 Generate TCP checksum * 7 Generate UDP checksum 7 Generate UDP checksum * 8 VLAN tagged 8 VLAN tagged * 9 Ethernet frame type 9 Ethernet frame type * 10-+ 10-+ * 11 | IP hdr length (10:13) 11 | IP hdr length (10:13) * 12 | (num 32-bit words) 12 | (num 32-bit words) * 13-+ 13-+ * 14-+ 14 Unused * 15 | TCP hdr length (14:17) 15 Unused * 16 | (num 32-bit words) 16-+ * 17-+ 17 | * 18 Header TPD flag 18 | * 19-+ 19 | Payload offset * 20 | 20 | (16:23) * 21 | 21 | * 22 | 22 | * 23 | 23-+ * 24 | 24-+ * 25 | MSS (19:31) 25 | * 26 | 26 | * 27 | 27 | Custom csum offset * 28 | 28 | (24:31) * 29 | 29 | * 30 | 30 | * 31-+ 31-+ */ /* tpd word 2 */ #define TPD_BUFLEN_MASK 0x3FFF #define TPD_BUFLEN_SHIFT 0 #define TPD_DMAINT_MASK 0x0001 #define TPD_DMAINT_SHIFT 14 #define TPD_PKTNT_MASK 0x0001 #define TPD_PKTINT_SHIFT 15 #define TPD_VLANTAG_MASK 0xFFFF #define TPD_VLANTAG_SHIFT 16 /* tpd word 3 bits 0:13 */ #define TPD_EOP_MASK 0x0001 #define TPD_EOP_SHIFT 0 #define TPD_COALESCE_MASK 0x0001 #define TPD_COALESCE_SHIFT 1 #define TPD_INS_VL_TAG_MASK 0x0001 #define TPD_INS_VL_TAG_SHIFT 2 #define TPD_CUST_CSUM_EN_MASK 0x0001 #define TPD_CUST_CSUM_EN_SHIFT 3 #define TPD_SEGMENT_EN_MASK 0x0001 #define TPD_SEGMENT_EN_SHIFT 4 #define TPD_IP_CSUM_MASK 0x0001 #define TPD_IP_CSUM_SHIFT 5 #define TPD_TCP_CSUM_MASK 0x0001 #define TPD_TCP_CSUM_SHIFT 6 #define TPD_UDP_CSUM_MASK 0x0001 #define TPD_UDP_CSUM_SHIFT 7 #define TPD_VL_TAGGED_MASK 0x0001 #define TPD_VL_TAGGED_SHIFT 8 #define TPD_ETHTYPE_MASK 0x0001 #define TPD_ETHTYPE_SHIFT 9 #define TPD_IPHL_MASK 0x000F #define TPD_IPHL_SHIFT 10 /* tpd word 3 bits 14:31 if segment enabled */ #define TPD_TCPHDRLEN_MASK 0x000F #define TPD_TCPHDRLEN_SHIFT 14 #define TPD_HDRFLAG_MASK 0x0001 #define TPD_HDRFLAG_SHIFT 18 #define TPD_MSS_MASK 0x1FFF #define TPD_MSS_SHIFT 19 /* tpd word 3 bits 16:31 if custom csum enabled */ #define TPD_PLOADOFFSET_MASK 0x00FF #define TPD_PLOADOFFSET_SHIFT 16 #define TPD_CCSUMOFFSET_MASK 0x00FF #define TPD_CCSUMOFFSET_SHIFT 24 struct tx_packet_desc { __le64 buffer_addr; __le32 word2; __le32 word3; }; /* DMA Order Settings */ enum atl1_dma_order { atl1_dma_ord_in = 1, atl1_dma_ord_enh = 2, atl1_dma_ord_out = 4 }; enum atl1_dma_rcb { atl1_rcb_64 = 0, atl1_rcb_128 = 1 }; enum atl1_dma_req_block { atl1_dma_req_128 = 0, atl1_dma_req_256 = 1, atl1_dma_req_512 = 2, atl1_dma_req_1024 = 3, atl1_dma_req_2048 = 4, atl1_dma_req_4096 = 5 }; #define ATL1_MAX_INTR 3 #define ATL1_MAX_TX_BUF_LEN 0x3000 /* 12288 bytes */ #define ATL1_DEFAULT_TPD 256 #define ATL1_MAX_TPD 1024 #define ATL1_MIN_TPD 64 #define ATL1_DEFAULT_RFD 512 #define ATL1_MIN_RFD 128 #define ATL1_MAX_RFD 2048 #define ATL1_REG_COUNT 1538 #define ATL1_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i])) #define ATL1_RFD_DESC(R, i) ATL1_GET_DESC(R, i, struct rx_free_desc) #define ATL1_TPD_DESC(R, i) ATL1_GET_DESC(R, i, struct tx_packet_desc) #define ATL1_RRD_DESC(R, i) ATL1_GET_DESC(R, i, struct rx_return_desc) /* * atl1_ring_header represents a single, contiguous block of DMA space * mapped for the three descriptor rings (tpd, rfd, rrd) and the two * message blocks (cmb, smb) described below */ struct atl1_ring_header { void *desc; /* virtual address */ dma_addr_t dma; /* physical address*/ unsigned int size; /* length in bytes */ }; /* * atl1_buffer is wrapper around a pointer to a socket buffer * so a DMA handle can be stored along with the skb */ struct atl1_buffer { struct sk_buff *skb; /* socket buffer */ u16 length; /* rx buffer length */ u16 alloced; /* 1 if skb allocated */ dma_addr_t dma; }; /* transmit packet descriptor (tpd) ring */ struct atl1_tpd_ring { void *desc; /* descriptor ring virtual address */ dma_addr_t dma; /* descriptor ring physical address */ u16 size; /* descriptor ring length in bytes */ u16 count; /* number of descriptors in the ring */ u16 hw_idx; /* hardware index */ atomic_t next_to_clean; atomic_t next_to_use; struct atl1_buffer *buffer_info; }; /* receive free descriptor (rfd) ring */ struct atl1_rfd_ring { void *desc; /* descriptor ring virtual address */ dma_addr_t dma; /* descriptor ring physical address */ u16 size; /* descriptor ring length in bytes */ u16 count; /* number of descriptors in the ring */ atomic_t next_to_use; u16 next_to_clean; struct atl1_buffer *buffer_info; }; /* receive return descriptor (rrd) ring */ struct atl1_rrd_ring { void *desc; /* descriptor ring virtual address */ dma_addr_t dma; /* descriptor ring physical address */ unsigned int size; /* descriptor ring length in bytes */ u16 count; /* number of descriptors in the ring */ u16 next_to_use; atomic_t next_to_clean; }; /* coalescing message block (cmb) */ struct atl1_cmb { struct coals_msg_block *cmb; dma_addr_t dma; }; /* statistics message block (smb) */ struct atl1_smb { struct stats_msg_block *smb; dma_addr_t dma; }; /* Statistics counters */ struct atl1_sft_stats { u64 rx_packets; u64 tx_packets; u64 rx_bytes; u64 tx_bytes; u64 multicast; u64 collisions; u64 rx_errors; u64 rx_length_errors; u64 rx_crc_errors; u64 rx_frame_errors; u64 rx_fifo_errors; u64 rx_missed_errors; u64 tx_errors; u64 tx_fifo_errors; u64 tx_aborted_errors; u64 tx_window_errors; u64 tx_carrier_errors; u64 tx_pause; /* TX pause frames */ u64 excecol; /* TX packets w/ excessive collisions */ u64 deffer; /* TX packets deferred */ u64 scc; /* packets TX after a single collision */ u64 mcc; /* packets TX after multiple collisions */ u64 latecol; /* TX packets w/ late collisions */ u64 tx_underun; /* TX packets aborted due to TX FIFO underrun * or TRD FIFO underrun */ u64 tx_trunc; /* TX packets truncated due to size > MTU */ u64 rx_pause; /* num Pause packets received. */ u64 rx_rrd_ov; u64 rx_trunc; }; /* hardware structure */ struct atl1_hw { u8 __iomem *hw_addr; struct atl1_adapter *back; enum atl1_dma_order dma_ord; enum atl1_dma_rcb rcb_value; enum atl1_dma_req_block dmar_block; enum atl1_dma_req_block dmaw_block; u8 preamble_len; u8 max_retry; u8 jam_ipg; /* IPG to start JAM for collision based flow * control in half-duplex mode. In units of * 8-bit time */ u8 ipgt; /* Desired back to back inter-packet gap. * The default is 96-bit time */ u8 min_ifg; /* Minimum number of IFG to enforce in between * receive frames. Frame gap below such IFP * is dropped */ u8 ipgr1; /* 64bit Carrier-Sense window */ u8 ipgr2; /* 96-bit IPG window */ u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned * burst. Each TPD is 16 bytes long */ u8 rfd_burst; /* Number of RFD to prefetch in cache-aligned * burst. Each RFD is 12 bytes long */ u8 rfd_fetch_gap; u8 rrd_burst; /* Threshold number of RRDs that can be retired * in a burst. Each RRD is 16 bytes long */ u8 tpd_fetch_th; u8 tpd_fetch_gap; u16 tx_jumbo_task_th; u16 txf_burst; /* Number of data bytes to read in a cache- * aligned burst. Each SRAM entry is 8 bytes */ u16 rx_jumbo_th; /* Jumbo packet size for non-VLAN packet. VLAN * packets should add 4 bytes */ u16 rx_jumbo_lkah; u16 rrd_ret_timer; /* RRD retirement timer. Decrement by 1 after * every 512ns passes. */ u16 lcol; /* Collision Window */ u16 cmb_tpd; u16 cmb_rrd; u16 cmb_rx_timer; u16 cmb_tx_timer; u32 smb_timer; u16 media_type; u16 autoneg_advertised; u16 mii_autoneg_adv_reg; u16 mii_1000t_ctrl_reg; u32 max_frame_size; u32 min_frame_size; u16 dev_rev; /* spi flash */ u8 flash_vendor; u8 mac_addr[ETH_ALEN]; u8 perm_mac_addr[ETH_ALEN]; bool phy_configured; }; struct atl1_adapter { struct net_device *netdev; struct pci_dev *pdev; struct atl1_sft_stats soft_stats; u32 rx_buffer_len; u32 wol; u16 link_speed; u16 link_duplex; spinlock_t lock; struct napi_struct napi; struct work_struct reset_dev_task; struct work_struct link_chg_task; struct timer_list phy_config_timer; bool phy_timer_pending; /* all descriptor rings' memory */ struct atl1_ring_header ring_header; /* TX */ struct atl1_tpd_ring tpd_ring; spinlock_t mb_lock; /* RX */ struct atl1_rfd_ring rfd_ring; struct atl1_rrd_ring rrd_ring; u64 hw_csum_err; u64 hw_csum_good; u32 msg_enable; u16 imt; /* interrupt moderator timer (2us resolution) */ u16 ict; /* interrupt clear timer (2us resolution */ struct mii_if_info mii; /* MII interface info */ /* * Use this value to check is napi handler allowed to * enable ints or not */ bool int_enabled; u32 bd_number; /* board number */ bool pci_using_64; struct atl1_hw hw; struct atl1_smb smb; struct atl1_cmb cmb; }; #endif /* ATL1_H */ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atlx/Makefile0000644000175000017500000000007312026211315024517 0ustar mcgrofmcgrofobj-$(CONFIG_ATL1) += atl1.o obj-$(CONFIG_ATL2) += atl2.o compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/0000755000175000017500000000000012026211315023115 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/atl1e_main.c0000644000175000017500000021133312026211315025276 0ustar mcgrofmcgrof/* * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "atl1e.h" #define DRV_VERSION "1.0.0.7-NAPI" char atl1e_driver_name[] = "ATL1E"; char atl1e_driver_version[] = DRV_VERSION; #define PCI_DEVICE_ID_ATTANSIC_L1E 0x1026 /* * atl1e_pci_tbl - PCI Device ID Table * * Wildcard entries (PCI_ANY_ID) should come last * Last entry must be all 0s * * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, * Class, Class Mask, private data (not used) } */ static DEFINE_PCI_DEVICE_TABLE(atl1e_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1E)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, 0x1066)}, /* required last entry */ { 0 } }; MODULE_DEVICE_TABLE(pci, atl1e_pci_tbl); MODULE_AUTHOR("Atheros Corporation, , Jie Yang "); MODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter); static const u16 atl1e_rx_page_vld_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = { {REG_HOST_RXF0_PAGE0_VLD, REG_HOST_RXF0_PAGE1_VLD}, {REG_HOST_RXF1_PAGE0_VLD, REG_HOST_RXF1_PAGE1_VLD}, {REG_HOST_RXF2_PAGE0_VLD, REG_HOST_RXF2_PAGE1_VLD}, {REG_HOST_RXF3_PAGE0_VLD, REG_HOST_RXF3_PAGE1_VLD} }; static const u16 atl1e_rx_page_hi_addr_regs[AT_MAX_RECEIVE_QUEUE] = { REG_RXF0_BASE_ADDR_HI, REG_RXF1_BASE_ADDR_HI, REG_RXF2_BASE_ADDR_HI, REG_RXF3_BASE_ADDR_HI }; static const u16 atl1e_rx_page_lo_addr_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = { {REG_HOST_RXF0_PAGE0_LO, REG_HOST_RXF0_PAGE1_LO}, {REG_HOST_RXF1_PAGE0_LO, REG_HOST_RXF1_PAGE1_LO}, {REG_HOST_RXF2_PAGE0_LO, REG_HOST_RXF2_PAGE1_LO}, {REG_HOST_RXF3_PAGE0_LO, REG_HOST_RXF3_PAGE1_LO} }; static const u16 atl1e_rx_page_write_offset_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = { {REG_HOST_RXF0_MB0_LO, REG_HOST_RXF0_MB1_LO}, {REG_HOST_RXF1_MB0_LO, REG_HOST_RXF1_MB1_LO}, {REG_HOST_RXF2_MB0_LO, REG_HOST_RXF2_MB1_LO}, {REG_HOST_RXF3_MB0_LO, REG_HOST_RXF3_MB1_LO} }; static const u16 atl1e_pay_load_size[] = { 128, 256, 512, 1024, 2048, 4096, }; /** * atl1e_irq_enable - Enable default interrupt generation settings * @adapter: board private structure */ static inline void atl1e_irq_enable(struct atl1e_adapter *adapter) { if (likely(atomic_dec_and_test(&adapter->irq_sem))) { AT_WRITE_REG(&adapter->hw, REG_ISR, 0); AT_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK); AT_WRITE_FLUSH(&adapter->hw); } } /** * atl1e_irq_disable - Mask off interrupt generation on the NIC * @adapter: board private structure */ static inline void atl1e_irq_disable(struct atl1e_adapter *adapter) { atomic_inc(&adapter->irq_sem); AT_WRITE_REG(&adapter->hw, REG_IMR, 0); AT_WRITE_FLUSH(&adapter->hw); synchronize_irq(adapter->pdev->irq); } /** * atl1e_irq_reset - reset interrupt confiure on the NIC * @adapter: board private structure */ static inline void atl1e_irq_reset(struct atl1e_adapter *adapter) { atomic_set(&adapter->irq_sem, 0); AT_WRITE_REG(&adapter->hw, REG_ISR, 0); AT_WRITE_REG(&adapter->hw, REG_IMR, 0); AT_WRITE_FLUSH(&adapter->hw); } /** * atl1e_phy_config - Timer Call-back * @data: pointer to netdev cast into an unsigned long */ static void atl1e_phy_config(unsigned long data) { struct atl1e_adapter *adapter = (struct atl1e_adapter *) data; struct atl1e_hw *hw = &adapter->hw; unsigned long flags; spin_lock_irqsave(&adapter->mdio_lock, flags); atl1e_restart_autoneg(hw); spin_unlock_irqrestore(&adapter->mdio_lock, flags); } void atl1e_reinit_locked(struct atl1e_adapter *adapter) { WARN_ON(in_interrupt()); while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); atl1e_down(adapter); atl1e_up(adapter); clear_bit(__AT_RESETTING, &adapter->flags); } static void atl1e_reset_task(struct work_struct *work) { struct atl1e_adapter *adapter; adapter = container_of(work, struct atl1e_adapter, reset_task); atl1e_reinit_locked(adapter); } static int atl1e_check_link(struct atl1e_adapter *adapter) { struct atl1e_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; int err = 0; u16 speed, duplex, phy_data; /* MII_BMSR must read twice */ atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); if ((phy_data & BMSR_LSTATUS) == 0) { /* link down */ if (netif_carrier_ok(netdev)) { /* old link state: Up */ u32 value; /* disable rx */ value = AT_READ_REG(hw, REG_MAC_CTRL); value &= ~MAC_CTRL_RX_EN; AT_WRITE_REG(hw, REG_MAC_CTRL, value); adapter->link_speed = SPEED_0; netif_carrier_off(netdev); netif_stop_queue(netdev); } } else { /* Link Up */ err = atl1e_get_speed_and_duplex(hw, &speed, &duplex); if (unlikely(err)) return err; /* link result is our setting */ if (adapter->link_speed != speed || adapter->link_duplex != duplex) { adapter->link_speed = speed; adapter->link_duplex = duplex; atl1e_setup_mac_ctrl(adapter); netdev_info(netdev, "NIC Link is Up <%d Mbps %s Duplex>\n", adapter->link_speed, adapter->link_duplex == FULL_DUPLEX ? "Full" : "Half"); } if (!netif_carrier_ok(netdev)) { /* Link down -> Up */ netif_carrier_on(netdev); netif_wake_queue(netdev); } } return 0; } /** * atl1e_link_chg_task - deal with link change event Out of interrupt context * @netdev: network interface device structure */ static void atl1e_link_chg_task(struct work_struct *work) { struct atl1e_adapter *adapter; unsigned long flags; adapter = container_of(work, struct atl1e_adapter, link_chg_task); spin_lock_irqsave(&adapter->mdio_lock, flags); atl1e_check_link(adapter); spin_unlock_irqrestore(&adapter->mdio_lock, flags); } static void atl1e_link_chg_event(struct atl1e_adapter *adapter) { struct net_device *netdev = adapter->netdev; u16 phy_data = 0; u16 link_up = 0; spin_lock(&adapter->mdio_lock); atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); spin_unlock(&adapter->mdio_lock); link_up = phy_data & BMSR_LSTATUS; /* notify upper layer link down ASAP */ if (!link_up) { if (netif_carrier_ok(netdev)) { /* old link state: Up */ netdev_info(netdev, "NIC Link is Down\n"); adapter->link_speed = SPEED_0; netif_stop_queue(netdev); } } schedule_work(&adapter->link_chg_task); } static void atl1e_del_timer(struct atl1e_adapter *adapter) { del_timer_sync(&adapter->phy_config_timer); } static void atl1e_cancel_work(struct atl1e_adapter *adapter) { cancel_work_sync(&adapter->reset_task); cancel_work_sync(&adapter->link_chg_task); } /** * atl1e_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure */ static void atl1e_tx_timeout(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ schedule_work(&adapter->reset_task); } /** * atl1e_set_multi - Multicast and Promiscuous mode set * @netdev: network interface device structure * * The set_multi entry point is called whenever the multicast address * list or the network interface flags are updated. This routine is * responsible for configuring the hardware for proper multicast, * promiscuous mode, and all-multi behavior. */ static void atl1e_set_multi(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; struct netdev_hw_addr *ha; u32 mac_ctrl_data = 0; u32 hash_value; /* Check for Promiscuous and All Multicast modes */ mac_ctrl_data = AT_READ_REG(hw, REG_MAC_CTRL); if (netdev->flags & IFF_PROMISC) { mac_ctrl_data |= MAC_CTRL_PROMIS_EN; } else if (netdev->flags & IFF_ALLMULTI) { mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN; } else { mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); } AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); /* clear the old settings from the multicast hash table */ AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atl1e_hash_mc_addr(hw, ha->addr); #else hash_value = atl1e_hash_mc_addr(hw, ha->dmi_addr); #endif atl1e_hash_set(hw, hash_value); } } static void __atl1e_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data) { if (features & NETIF_F_HW_VLAN_RX) { /* enable VLAN tag insert/strip */ *mac_ctrl_data |= MAC_CTRL_RMV_VLAN; } else { /* disable VLAN tag insert/strip */ *mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN; } } static void atl1e_vlan_mode(struct net_device *netdev, netdev_features_t features) { struct atl1e_adapter *adapter = netdev_priv(netdev); u32 mac_ctrl_data = 0; netdev_dbg(adapter->netdev, "%s\n", __func__); atl1e_irq_disable(adapter); mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL); __atl1e_vlan_mode(features, &mac_ctrl_data); AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); atl1e_irq_enable(adapter); } static void atl1e_restore_vlan(struct atl1e_adapter *adapter) { netdev_dbg(adapter->netdev, "%s\n", __func__); atl1e_vlan_mode(adapter->netdev, adapter->netdev->features); } /** * atl1e_set_mac - Change the Ethernet Address of the NIC * @netdev: network interface device structure * @p: pointer to an address structure * * Returns 0 on success, negative on failure */ static int atl1e_set_mac_addr(struct net_device *netdev, void *p) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; if (netif_running(netdev)) return -EBUSY; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); atl1e_hw_set_mac_addr(&adapter->hw); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atl1e_fix_features(struct net_device *netdev, netdev_features_t features) { /* * Since there is no support for separate rx/tx vlan accel * enable/disable make sure tx flag is always in same state as rx. */ if (features & NETIF_F_HW_VLAN_RX) features |= NETIF_F_HW_VLAN_TX; else features &= ~NETIF_F_HW_VLAN_TX; return features; } static int atl1e_set_features(struct net_device *netdev, netdev_features_t features) { netdev_features_t changed = netdev->features ^ features; if (changed & NETIF_F_HW_VLAN_RX) atl1e_vlan_mode(netdev, features); return 0; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /** * atl1e_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure * @new_mtu: new value for maximum frame size * * Returns 0 on success, negative on failure */ static int atl1e_change_mtu(struct net_device *netdev, int new_mtu) { struct atl1e_adapter *adapter = netdev_priv(netdev); int old_mtu = netdev->mtu; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { netdev_warn(adapter->netdev, "invalid MTU setting\n"); return -EINVAL; } /* set MTU */ if (old_mtu != new_mtu && netif_running(netdev)) { while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); netdev->mtu = new_mtu; adapter->hw.max_frame_size = new_mtu; adapter->hw.rx_jumbo_th = (max_frame + 7) >> 3; atl1e_down(adapter); atl1e_up(adapter); clear_bit(__AT_RESETTING, &adapter->flags); } return 0; } /* * caller should hold mdio_lock */ static int atl1e_mdio_read(struct net_device *netdev, int phy_id, int reg_num) { struct atl1e_adapter *adapter = netdev_priv(netdev); u16 result; atl1e_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result); return result; } static void atl1e_mdio_write(struct net_device *netdev, int phy_id, int reg_num, int val) { struct atl1e_adapter *adapter = netdev_priv(netdev); atl1e_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val); } static int atl1e_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct mii_ioctl_data *data = if_mii(ifr); unsigned long flags; int retval = 0; if (!netif_running(netdev)) return -EINVAL; spin_lock_irqsave(&adapter->mdio_lock, flags); switch (cmd) { case SIOCGMIIPHY: data->phy_id = 0; break; case SIOCGMIIREG: if (atl1e_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, &data->val_out)) { retval = -EIO; goto out; } break; case SIOCSMIIREG: if (data->reg_num & ~(0x1F)) { retval = -EFAULT; goto out; } netdev_dbg(adapter->netdev, " write %x %x\n", data->reg_num, data->val_in); if (atl1e_write_phy_reg(&adapter->hw, data->reg_num, data->val_in)) { retval = -EIO; goto out; } break; default: retval = -EOPNOTSUPP; break; } out: spin_unlock_irqrestore(&adapter->mdio_lock, flags); return retval; } static int atl1e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { switch (cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: return atl1e_mii_ioctl(netdev, ifr, cmd); default: return -EOPNOTSUPP; } } static void atl1e_setup_pcicmd(struct pci_dev *pdev) { u16 cmd; pci_read_config_word(pdev, PCI_COMMAND, &cmd); cmd &= ~(PCI_COMMAND_INTX_DISABLE | PCI_COMMAND_IO); cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); pci_write_config_word(pdev, PCI_COMMAND, cmd); /* * some motherboards BIOS(PXE/EFI) driver may set PME * while they transfer control to OS (Windows/Linux) * so we should clear this bit before NIC work normally */ pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0); msleep(1); } /** * atl1e_alloc_queues - Allocate memory for all rings * @adapter: board private structure to initialize * */ static int __devinit atl1e_alloc_queues(struct atl1e_adapter *adapter) { return 0; } /** * atl1e_sw_init - Initialize general software structures (struct atl1e_adapter) * @adapter: board private structure to initialize * * atl1e_sw_init initializes the Adapter private data structure. * Fields are initialized based on PCI device information and * OS network device settings (MTU size). */ static int __devinit atl1e_sw_init(struct atl1e_adapter *adapter) { struct atl1e_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; u32 phy_status_data = 0; adapter->wol = 0; adapter->link_speed = SPEED_0; /* hardware init */ adapter->link_duplex = FULL_DUPLEX; adapter->num_rx_queues = 1; /* PCI config space info */ hw->vendor_id = pdev->vendor; hw->device_id = pdev->device; hw->subsystem_vendor_id = pdev->subsystem_vendor; hw->subsystem_id = pdev->subsystem_device; hw->revision_id = pdev->revision; pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); /* nic type */ if (hw->revision_id >= 0xF0) { hw->nic_type = athr_l2e_revB; } else { if (phy_status_data & PHY_STATUS_100M) hw->nic_type = athr_l1e; else hw->nic_type = athr_l2e_revA; } phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); if (phy_status_data & PHY_STATUS_EMI_CA) hw->emi_ca = true; else hw->emi_ca = false; hw->phy_configured = false; hw->preamble_len = 7; hw->max_frame_size = adapter->netdev->mtu; hw->rx_jumbo_th = (hw->max_frame_size + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN + 7) >> 3; hw->rrs_type = atl1e_rrs_disable; hw->indirect_tab = 0; hw->base_cpu = 0; /* need confirm */ hw->ict = 50000; /* 100ms */ hw->smb_timer = 200000; /* 200ms */ hw->tpd_burst = 5; hw->rrd_thresh = 1; hw->tpd_thresh = adapter->tx_ring.count / 2; hw->rx_count_down = 4; /* 2us resolution */ hw->tx_count_down = hw->imt * 4 / 3; hw->dmar_block = atl1e_dma_req_1024; hw->dmaw_block = atl1e_dma_req_1024; hw->dmar_dly_cnt = 15; hw->dmaw_dly_cnt = 4; if (atl1e_alloc_queues(adapter)) { netdev_err(adapter->netdev, "Unable to allocate memory for queues\n"); return -ENOMEM; } atomic_set(&adapter->irq_sem, 1); spin_lock_init(&adapter->mdio_lock); spin_lock_init(&adapter->tx_lock); set_bit(__AT_DOWN, &adapter->flags); return 0; } /** * atl1e_clean_tx_ring - Free Tx-skb * @adapter: board private structure */ static void atl1e_clean_tx_ring(struct atl1e_adapter *adapter) { struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; struct atl1e_tx_buffer *tx_buffer = NULL; struct pci_dev *pdev = adapter->pdev; u16 index, ring_count; if (tx_ring->desc == NULL || tx_ring->tx_buffer == NULL) return; ring_count = tx_ring->count; /* first unmmap dma */ for (index = 0; index < ring_count; index++) { tx_buffer = &tx_ring->tx_buffer[index]; if (tx_buffer->dma) { if (tx_buffer->flags & ATL1E_TX_PCIMAP_SINGLE) pci_unmap_single(pdev, tx_buffer->dma, tx_buffer->length, PCI_DMA_TODEVICE); else if (tx_buffer->flags & ATL1E_TX_PCIMAP_PAGE) pci_unmap_page(pdev, tx_buffer->dma, tx_buffer->length, PCI_DMA_TODEVICE); tx_buffer->dma = 0; } } /* second free skb */ for (index = 0; index < ring_count; index++) { tx_buffer = &tx_ring->tx_buffer[index]; if (tx_buffer->skb) { dev_kfree_skb_any(tx_buffer->skb); tx_buffer->skb = NULL; } } /* Zero out Tx-buffers */ memset(tx_ring->desc, 0, sizeof(struct atl1e_tpd_desc) * ring_count); memset(tx_ring->tx_buffer, 0, sizeof(struct atl1e_tx_buffer) * ring_count); } /** * atl1e_clean_rx_ring - Free rx-reservation skbs * @adapter: board private structure */ static void atl1e_clean_rx_ring(struct atl1e_adapter *adapter) { struct atl1e_rx_ring *rx_ring = &adapter->rx_ring; struct atl1e_rx_page_desc *rx_page_desc = rx_ring->rx_page_desc; u16 i, j; if (adapter->ring_vir_addr == NULL) return; /* Zero out the descriptor ring */ for (i = 0; i < adapter->num_rx_queues; i++) { for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { if (rx_page_desc[i].rx_page[j].addr != NULL) { memset(rx_page_desc[i].rx_page[j].addr, 0, rx_ring->real_page_size); } } } } static void atl1e_cal_ring_size(struct atl1e_adapter *adapter, u32 *ring_size) { *ring_size = ((u32)(adapter->tx_ring.count * sizeof(struct atl1e_tpd_desc) + 7 /* tx ring, qword align */ + adapter->rx_ring.real_page_size * AT_PAGE_NUM_PER_QUEUE * adapter->num_rx_queues + 31 /* rx ring, 32 bytes align */ + (1 + AT_PAGE_NUM_PER_QUEUE * adapter->num_rx_queues) * sizeof(u32) + 3)); /* tx, rx cmd, dword align */ } static void atl1e_init_ring_resources(struct atl1e_adapter *adapter) { struct atl1e_rx_ring *rx_ring = NULL; rx_ring = &adapter->rx_ring; rx_ring->real_page_size = adapter->rx_ring.page_size + adapter->hw.max_frame_size + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; rx_ring->real_page_size = roundup(rx_ring->real_page_size, 32); atl1e_cal_ring_size(adapter, &adapter->ring_size); adapter->ring_vir_addr = NULL; adapter->rx_ring.desc = NULL; rwlock_init(&adapter->tx_ring.tx_lock); } /* * Read / Write Ptr Initialize: */ static void atl1e_init_ring_ptrs(struct atl1e_adapter *adapter) { struct atl1e_tx_ring *tx_ring = NULL; struct atl1e_rx_ring *rx_ring = NULL; struct atl1e_rx_page_desc *rx_page_desc = NULL; int i, j; tx_ring = &adapter->tx_ring; rx_ring = &adapter->rx_ring; rx_page_desc = rx_ring->rx_page_desc; tx_ring->next_to_use = 0; atomic_set(&tx_ring->next_to_clean, 0); for (i = 0; i < adapter->num_rx_queues; i++) { rx_page_desc[i].rx_using = 0; rx_page_desc[i].rx_nxseq = 0; for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { *rx_page_desc[i].rx_page[j].write_offset_addr = 0; rx_page_desc[i].rx_page[j].read_offset = 0; } } } /** * atl1e_free_ring_resources - Free Tx / RX descriptor Resources * @adapter: board private structure * * Free all transmit software resources */ static void atl1e_free_ring_resources(struct atl1e_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; atl1e_clean_tx_ring(adapter); atl1e_clean_rx_ring(adapter); if (adapter->ring_vir_addr) { pci_free_consistent(pdev, adapter->ring_size, adapter->ring_vir_addr, adapter->ring_dma); adapter->ring_vir_addr = NULL; } if (adapter->tx_ring.tx_buffer) { kfree(adapter->tx_ring.tx_buffer); adapter->tx_ring.tx_buffer = NULL; } } /** * atl1e_setup_mem_resources - allocate Tx / RX descriptor resources * @adapter: board private structure * * Return 0 on success, negative on failure */ static int atl1e_setup_ring_resources(struct atl1e_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; struct atl1e_tx_ring *tx_ring; struct atl1e_rx_ring *rx_ring; struct atl1e_rx_page_desc *rx_page_desc; int size, i, j; u32 offset = 0; int err = 0; if (adapter->ring_vir_addr != NULL) return 0; /* alloced already */ tx_ring = &adapter->tx_ring; rx_ring = &adapter->rx_ring; /* real ring DMA buffer */ size = adapter->ring_size; adapter->ring_vir_addr = pci_alloc_consistent(pdev, adapter->ring_size, &adapter->ring_dma); if (adapter->ring_vir_addr == NULL) { netdev_err(adapter->netdev, "pci_alloc_consistent failed, size = D%d\n", size); return -ENOMEM; } memset(adapter->ring_vir_addr, 0, adapter->ring_size); rx_page_desc = rx_ring->rx_page_desc; /* Init TPD Ring */ tx_ring->dma = roundup(adapter->ring_dma, 8); offset = tx_ring->dma - adapter->ring_dma; tx_ring->desc = adapter->ring_vir_addr + offset; size = sizeof(struct atl1e_tx_buffer) * (tx_ring->count); tx_ring->tx_buffer = kzalloc(size, GFP_KERNEL); if (tx_ring->tx_buffer == NULL) { netdev_err(adapter->netdev, "kzalloc failed, size = D%d\n", size); err = -ENOMEM; goto failed; } /* Init RXF-Pages */ offset += (sizeof(struct atl1e_tpd_desc) * tx_ring->count); offset = roundup(offset, 32); for (i = 0; i < adapter->num_rx_queues; i++) { for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { rx_page_desc[i].rx_page[j].dma = adapter->ring_dma + offset; rx_page_desc[i].rx_page[j].addr = adapter->ring_vir_addr + offset; offset += rx_ring->real_page_size; } } /* Init CMB dma address */ tx_ring->cmb_dma = adapter->ring_dma + offset; tx_ring->cmb = adapter->ring_vir_addr + offset; offset += sizeof(u32); for (i = 0; i < adapter->num_rx_queues; i++) { for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { rx_page_desc[i].rx_page[j].write_offset_dma = adapter->ring_dma + offset; rx_page_desc[i].rx_page[j].write_offset_addr = adapter->ring_vir_addr + offset; offset += sizeof(u32); } } if (unlikely(offset > adapter->ring_size)) { netdev_err(adapter->netdev, "offset(%d) > ring size(%d) !!\n", offset, adapter->ring_size); err = -1; goto failed; } return 0; failed: if (adapter->ring_vir_addr != NULL) { pci_free_consistent(pdev, adapter->ring_size, adapter->ring_vir_addr, adapter->ring_dma); adapter->ring_vir_addr = NULL; } return err; } static inline void atl1e_configure_des_ring(struct atl1e_adapter *adapter) { struct atl1e_hw *hw = &adapter->hw; struct atl1e_rx_ring *rx_ring = &adapter->rx_ring; struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; struct atl1e_rx_page_desc *rx_page_desc = NULL; int i, j; AT_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, (u32)((adapter->ring_dma & AT_DMA_HI_ADDR_MASK) >> 32)); AT_WRITE_REG(hw, REG_TPD_BASE_ADDR_LO, (u32)((tx_ring->dma) & AT_DMA_LO_ADDR_MASK)); AT_WRITE_REG(hw, REG_TPD_RING_SIZE, (u16)(tx_ring->count)); AT_WRITE_REG(hw, REG_HOST_TX_CMB_LO, (u32)((tx_ring->cmb_dma) & AT_DMA_LO_ADDR_MASK)); rx_page_desc = rx_ring->rx_page_desc; /* RXF Page Physical address / Page Length */ for (i = 0; i < AT_MAX_RECEIVE_QUEUE; i++) { AT_WRITE_REG(hw, atl1e_rx_page_hi_addr_regs[i], (u32)((adapter->ring_dma & AT_DMA_HI_ADDR_MASK) >> 32)); for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { u32 page_phy_addr; u32 offset_phy_addr; page_phy_addr = rx_page_desc[i].rx_page[j].dma; offset_phy_addr = rx_page_desc[i].rx_page[j].write_offset_dma; AT_WRITE_REG(hw, atl1e_rx_page_lo_addr_regs[i][j], page_phy_addr & AT_DMA_LO_ADDR_MASK); AT_WRITE_REG(hw, atl1e_rx_page_write_offset_regs[i][j], offset_phy_addr & AT_DMA_LO_ADDR_MASK); AT_WRITE_REGB(hw, atl1e_rx_page_vld_regs[i][j], 1); } } /* Page Length */ AT_WRITE_REG(hw, REG_HOST_RXFPAGE_SIZE, rx_ring->page_size); /* Load all of base address above */ AT_WRITE_REG(hw, REG_LOAD_PTR, 1); } static inline void atl1e_configure_tx(struct atl1e_adapter *adapter) { struct atl1e_hw *hw = &adapter->hw; u32 dev_ctrl_data = 0; u32 max_pay_load = 0; u32 jumbo_thresh = 0; u32 extra_size = 0; /* Jumbo frame threshold in QWORD unit */ /* configure TXQ param */ if (hw->nic_type != athr_l2e_revB) { extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; if (hw->max_frame_size <= 1500) { jumbo_thresh = hw->max_frame_size + extra_size; } else if (hw->max_frame_size < 6*1024) { jumbo_thresh = (hw->max_frame_size + extra_size) * 2 / 3; } else { jumbo_thresh = (hw->max_frame_size + extra_size) / 2; } AT_WRITE_REG(hw, REG_TX_EARLY_TH, (jumbo_thresh + 7) >> 3); } dev_ctrl_data = AT_READ_REG(hw, REG_DEVICE_CTRL); max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT)) & DEVICE_CTRL_MAX_PAYLOAD_MASK; hw->dmaw_block = min_t(u32, max_pay_load, hw->dmaw_block); max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT)) & DEVICE_CTRL_MAX_RREQ_SZ_MASK; hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block); if (hw->nic_type != athr_l2e_revB) AT_WRITE_REGW(hw, REG_TXQ_CTRL + 2, atl1e_pay_load_size[hw->dmar_block]); /* enable TXQ */ AT_WRITE_REGW(hw, REG_TXQ_CTRL, (((u16)hw->tpd_burst & TXQ_CTRL_NUM_TPD_BURST_MASK) << TXQ_CTRL_NUM_TPD_BURST_SHIFT) | TXQ_CTRL_ENH_MODE | TXQ_CTRL_EN); } static inline void atl1e_configure_rx(struct atl1e_adapter *adapter) { struct atl1e_hw *hw = &adapter->hw; u32 rxf_len = 0; u32 rxf_low = 0; u32 rxf_high = 0; u32 rxf_thresh_data = 0; u32 rxq_ctrl_data = 0; if (hw->nic_type != athr_l2e_revB) { AT_WRITE_REGW(hw, REG_RXQ_JMBOSZ_RRDTIM, (u16)((hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) << RXQ_JMBOSZ_TH_SHIFT | (1 & RXQ_JMBO_LKAH_MASK) << RXQ_JMBO_LKAH_SHIFT)); rxf_len = AT_READ_REG(hw, REG_SRAM_RXF_LEN); rxf_high = rxf_len * 4 / 5; rxf_low = rxf_len / 5; rxf_thresh_data = ((rxf_high & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) | ((rxf_low & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT); AT_WRITE_REG(hw, REG_RXQ_RXF_PAUSE_THRESH, rxf_thresh_data); } /* RRS */ AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab); AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu); if (hw->rrs_type & atl1e_rrs_ipv4) rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4; if (hw->rrs_type & atl1e_rrs_ipv4_tcp) rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4_TCP; if (hw->rrs_type & atl1e_rrs_ipv6) rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6; if (hw->rrs_type & atl1e_rrs_ipv6_tcp) rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6_TCP; if (hw->rrs_type != atl1e_rrs_disable) rxq_ctrl_data |= (RXQ_CTRL_HASH_ENABLE | RXQ_CTRL_RSS_MODE_MQUESINT); rxq_ctrl_data |= RXQ_CTRL_IPV6_XSUM_VERIFY_EN | RXQ_CTRL_PBA_ALIGN_32 | RXQ_CTRL_CUT_THRU_EN | RXQ_CTRL_EN; AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); } static inline void atl1e_configure_dma(struct atl1e_adapter *adapter) { struct atl1e_hw *hw = &adapter->hw; u32 dma_ctrl_data = 0; dma_ctrl_data = DMA_CTRL_RXCMB_EN; dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) << DMA_CTRL_DMAR_BURST_LEN_SHIFT; dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) << DMA_CTRL_DMAW_BURST_LEN_SHIFT; dma_ctrl_data |= DMA_CTRL_DMAR_REQ_PRI | DMA_CTRL_DMAR_OUT_ORDER; dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK) << DMA_CTRL_DMAR_DLY_CNT_SHIFT; dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK) << DMA_CTRL_DMAW_DLY_CNT_SHIFT; AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data); } static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter) { u32 value; struct atl1e_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; /* Config MAC CTRL Register */ value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN ; if (FULL_DUPLEX == adapter->link_duplex) value |= MAC_CTRL_DUPLX; value |= ((u32)((SPEED_1000 == adapter->link_speed) ? MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) << MAC_CTRL_SPEED_SHIFT); value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); value |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); __atl1e_vlan_mode(netdev->features, &value); value |= MAC_CTRL_BC_EN; if (netdev->flags & IFF_PROMISC) value |= MAC_CTRL_PROMIS_EN; if (netdev->flags & IFF_ALLMULTI) value |= MAC_CTRL_MC_ALL_EN; AT_WRITE_REG(hw, REG_MAC_CTRL, value); } /** * atl1e_configure - Configure Transmit&Receive Unit after Reset * @adapter: board private structure * * Configure the Tx /Rx unit of the MAC after a reset. */ static int atl1e_configure(struct atl1e_adapter *adapter) { struct atl1e_hw *hw = &adapter->hw; u32 intr_status_data = 0; /* clear interrupt status */ AT_WRITE_REG(hw, REG_ISR, ~0); /* 1. set MAC Address */ atl1e_hw_set_mac_addr(hw); /* 2. Init the Multicast HASH table done by set_muti */ /* 3. Clear any WOL status */ AT_WRITE_REG(hw, REG_WOL_CTRL, 0); /* 4. Descripter Ring BaseMem/Length/Read ptr/Write ptr * TPD Ring/SMB/RXF0 Page CMBs, they use the same * High 32bits memory */ atl1e_configure_des_ring(adapter); /* 5. set Interrupt Moderator Timer */ AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, hw->imt); AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER2_INIT, hw->imt); AT_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_LED_MODE | MASTER_CTRL_ITIMER_EN | MASTER_CTRL_ITIMER2_EN); /* 6. rx/tx threshold to trig interrupt */ AT_WRITE_REGW(hw, REG_TRIG_RRD_THRESH, hw->rrd_thresh); AT_WRITE_REGW(hw, REG_TRIG_TPD_THRESH, hw->tpd_thresh); AT_WRITE_REGW(hw, REG_TRIG_RXTIMER, hw->rx_count_down); AT_WRITE_REGW(hw, REG_TRIG_TXTIMER, hw->tx_count_down); /* 7. set Interrupt Clear Timer */ AT_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, hw->ict); /* 8. set MTU */ AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN); /* 9. config TXQ early tx threshold */ atl1e_configure_tx(adapter); /* 10. config RXQ */ atl1e_configure_rx(adapter); /* 11. config DMA Engine */ atl1e_configure_dma(adapter); /* 12. smb timer to trig interrupt */ AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, hw->smb_timer); intr_status_data = AT_READ_REG(hw, REG_ISR); if (unlikely((intr_status_data & ISR_PHY_LINKDOWN) != 0)) { netdev_err(adapter->netdev, "atl1e_configure failed, PCIE phy link down\n"); return -1; } AT_WRITE_REG(hw, REG_ISR, 0x7fffffff); return 0; } /** * atl1e_get_stats - Get System Network Statistics * @netdev: network interface device structure * * Returns the address of the device statistics structure. * The statistics are actually updated from the timer callback. */ static struct net_device_stats *atl1e_get_stats(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw_stats *hw_stats = &adapter->hw_stats; struct net_device_stats *net_stats = &netdev->stats; net_stats->rx_packets = hw_stats->rx_ok; net_stats->tx_packets = hw_stats->tx_ok; net_stats->rx_bytes = hw_stats->rx_byte_cnt; net_stats->tx_bytes = hw_stats->tx_byte_cnt; net_stats->multicast = hw_stats->rx_mcast; net_stats->collisions = hw_stats->tx_1_col + hw_stats->tx_2_col * 2 + hw_stats->tx_late_col + hw_stats->tx_abort_col; net_stats->rx_errors = hw_stats->rx_frag + hw_stats->rx_fcs_err + hw_stats->rx_len_err + hw_stats->rx_sz_ov + hw_stats->rx_rrd_ov + hw_stats->rx_align_err; net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov; net_stats->rx_length_errors = hw_stats->rx_len_err; net_stats->rx_crc_errors = hw_stats->rx_fcs_err; net_stats->rx_frame_errors = hw_stats->rx_align_err; net_stats->rx_over_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col + hw_stats->tx_underrun + hw_stats->tx_trunc; net_stats->tx_fifo_errors = hw_stats->tx_underrun; net_stats->tx_aborted_errors = hw_stats->tx_abort_col; net_stats->tx_window_errors = hw_stats->tx_late_col; return net_stats; } static void atl1e_update_hw_stats(struct atl1e_adapter *adapter) { u16 hw_reg_addr = 0; unsigned long *stats_item = NULL; /* update rx status */ hw_reg_addr = REG_MAC_RX_STATUS_BIN; stats_item = &adapter->hw_stats.rx_ok; while (hw_reg_addr <= REG_MAC_RX_STATUS_END) { *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr); stats_item++; hw_reg_addr += 4; } /* update tx status */ hw_reg_addr = REG_MAC_TX_STATUS_BIN; stats_item = &adapter->hw_stats.tx_ok; while (hw_reg_addr <= REG_MAC_TX_STATUS_END) { *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr); stats_item++; hw_reg_addr += 4; } } static inline void atl1e_clear_phy_int(struct atl1e_adapter *adapter) { u16 phy_data; spin_lock(&adapter->mdio_lock); atl1e_read_phy_reg(&adapter->hw, MII_INT_STATUS, &phy_data); spin_unlock(&adapter->mdio_lock); } static bool atl1e_clean_tx_irq(struct atl1e_adapter *adapter) { struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; struct atl1e_tx_buffer *tx_buffer = NULL; u16 hw_next_to_clean = AT_READ_REGW(&adapter->hw, REG_TPD_CONS_IDX); u16 next_to_clean = atomic_read(&tx_ring->next_to_clean); while (next_to_clean != hw_next_to_clean) { tx_buffer = &tx_ring->tx_buffer[next_to_clean]; if (tx_buffer->dma) { if (tx_buffer->flags & ATL1E_TX_PCIMAP_SINGLE) pci_unmap_single(adapter->pdev, tx_buffer->dma, tx_buffer->length, PCI_DMA_TODEVICE); else if (tx_buffer->flags & ATL1E_TX_PCIMAP_PAGE) pci_unmap_page(adapter->pdev, tx_buffer->dma, tx_buffer->length, PCI_DMA_TODEVICE); tx_buffer->dma = 0; } if (tx_buffer->skb) { dev_kfree_skb_irq(tx_buffer->skb); tx_buffer->skb = NULL; } if (++next_to_clean == tx_ring->count) next_to_clean = 0; } atomic_set(&tx_ring->next_to_clean, next_to_clean); if (netif_queue_stopped(adapter->netdev) && netif_carrier_ok(adapter->netdev)) { netif_wake_queue(adapter->netdev); } return true; } /** * atl1e_intr - Interrupt Handler * @irq: interrupt number * @data: pointer to a network interface device structure */ static irqreturn_t atl1e_intr(int irq, void *data) { struct net_device *netdev = data; struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; int max_ints = AT_MAX_INT_WORK; int handled = IRQ_NONE; u32 status; do { status = AT_READ_REG(hw, REG_ISR); if ((status & IMR_NORMAL_MASK) == 0 || (status & ISR_DIS_INT) != 0) { if (max_ints != AT_MAX_INT_WORK) handled = IRQ_HANDLED; break; } /* link event */ if (status & ISR_GPHY) atl1e_clear_phy_int(adapter); /* Ack ISR */ AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); handled = IRQ_HANDLED; /* check if PCIE PHY Link down */ if (status & ISR_PHY_LINKDOWN) { netdev_err(adapter->netdev, "pcie phy linkdown %x\n", status); if (netif_running(adapter->netdev)) { /* reset MAC */ atl1e_irq_reset(adapter); schedule_work(&adapter->reset_task); break; } } /* check if DMA read/write error */ if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { netdev_err(adapter->netdev, "PCIE DMA RW error (status = 0x%x)\n", status); atl1e_irq_reset(adapter); schedule_work(&adapter->reset_task); break; } if (status & ISR_SMB) atl1e_update_hw_stats(adapter); /* link event */ if (status & (ISR_GPHY | ISR_MANUAL)) { netdev->stats.tx_carrier_errors++; atl1e_link_chg_event(adapter); break; } /* transmit event */ if (status & ISR_TX_EVENT) atl1e_clean_tx_irq(adapter); if (status & ISR_RX_EVENT) { /* * disable rx interrupts, without * the synchronize_irq bit */ AT_WRITE_REG(hw, REG_IMR, IMR_NORMAL_MASK & ~ISR_RX_EVENT); AT_WRITE_FLUSH(hw); if (likely(napi_schedule_prep( &adapter->napi))) __napi_schedule(&adapter->napi); } } while (--max_ints > 0); /* re-enable Interrupt*/ AT_WRITE_REG(&adapter->hw, REG_ISR, 0); return handled; } static inline void atl1e_rx_checksum(struct atl1e_adapter *adapter, struct sk_buff *skb, struct atl1e_recv_ret_status *prrs) { u8 *packet = (u8 *)(prrs + 1); struct iphdr *iph; u16 head_len = ETH_HLEN; u16 pkt_flags; u16 err_flags; skb_checksum_none_assert(skb); pkt_flags = prrs->pkt_flag; err_flags = prrs->err_flag; if (((pkt_flags & RRS_IS_IPV4) || (pkt_flags & RRS_IS_IPV6)) && ((pkt_flags & RRS_IS_TCP) || (pkt_flags & RRS_IS_UDP))) { if (pkt_flags & RRS_IS_IPV4) { if (pkt_flags & RRS_IS_802_3) head_len += 8; iph = (struct iphdr *) (packet + head_len); if (iph->frag_off != 0 && !(pkt_flags & RRS_IS_IP_DF)) goto hw_xsum; } if (!(err_flags & (RRS_ERR_IP_CSUM | RRS_ERR_L4_CSUM))) { skb->ip_summed = CHECKSUM_UNNECESSARY; return; } } hw_xsum : return; } static struct atl1e_rx_page *atl1e_get_rx_page(struct atl1e_adapter *adapter, u8 que) { struct atl1e_rx_page_desc *rx_page_desc = (struct atl1e_rx_page_desc *) adapter->rx_ring.rx_page_desc; u8 rx_using = rx_page_desc[que].rx_using; return &(rx_page_desc[que].rx_page[rx_using]); } static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que, int *work_done, int work_to_do) { struct net_device *netdev = adapter->netdev; struct atl1e_rx_ring *rx_ring = &adapter->rx_ring; struct atl1e_rx_page_desc *rx_page_desc = (struct atl1e_rx_page_desc *) rx_ring->rx_page_desc; struct sk_buff *skb = NULL; struct atl1e_rx_page *rx_page = atl1e_get_rx_page(adapter, que); u32 packet_size, write_offset; struct atl1e_recv_ret_status *prrs; write_offset = *(rx_page->write_offset_addr); if (likely(rx_page->read_offset < write_offset)) { do { if (*work_done >= work_to_do) break; (*work_done)++; /* get new packet's rrs */ prrs = (struct atl1e_recv_ret_status *) (rx_page->addr + rx_page->read_offset); /* check sequence number */ if (prrs->seq_num != rx_page_desc[que].rx_nxseq) { netdev_err(netdev, "rx sequence number error (rx=%d) (expect=%d)\n", prrs->seq_num, rx_page_desc[que].rx_nxseq); rx_page_desc[que].rx_nxseq++; /* just for debug use */ AT_WRITE_REG(&adapter->hw, REG_DEBUG_DATA0, (((u32)prrs->seq_num) << 16) | rx_page_desc[que].rx_nxseq); goto fatal_err; } rx_page_desc[que].rx_nxseq++; /* error packet */ if (prrs->pkt_flag & RRS_IS_ERR_FRAME) { if (prrs->err_flag & (RRS_ERR_BAD_CRC | RRS_ERR_DRIBBLE | RRS_ERR_CODE | RRS_ERR_TRUNC)) { /* hardware error, discard this packet*/ netdev_err(netdev, "rx packet desc error %x\n", *((u32 *)prrs + 1)); goto skip_pkt; } } packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & RRS_PKT_SIZE_MASK) - 4; /* CRC */ skb = netdev_alloc_skb_ip_align(netdev, packet_size); if (skb == NULL) { netdev_warn(netdev, "Memory squeeze, deferring packet\n"); goto skip_pkt; } memcpy(skb->data, (u8 *)(prrs + 1), packet_size); skb_put(skb, packet_size); skb->protocol = eth_type_trans(skb, netdev); atl1e_rx_checksum(adapter, skb, prrs); if (prrs->pkt_flag & RRS_IS_VLAN_TAG) { u16 vlan_tag = (prrs->vtag >> 4) | ((prrs->vtag & 7) << 13) | ((prrs->vtag & 8) << 9); netdev_dbg(netdev, "RXD VLAN TAG=0x%04x\n", prrs->vtag); __vlan_hwaccel_put_tag(skb, vlan_tag); } netif_receive_skb(skb); skip_pkt: /* skip current packet whether it's ok or not. */ rx_page->read_offset += (((u32)((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & RRS_PKT_SIZE_MASK) + sizeof(struct atl1e_recv_ret_status) + 31) & 0xFFFFFFE0); if (rx_page->read_offset >= rx_ring->page_size) { /* mark this page clean */ u16 reg_addr; u8 rx_using; rx_page->read_offset = *(rx_page->write_offset_addr) = 0; rx_using = rx_page_desc[que].rx_using; reg_addr = atl1e_rx_page_vld_regs[que][rx_using]; AT_WRITE_REGB(&adapter->hw, reg_addr, 1); rx_page_desc[que].rx_using ^= 1; rx_page = atl1e_get_rx_page(adapter, que); } write_offset = *(rx_page->write_offset_addr); } while (rx_page->read_offset < write_offset); } return; fatal_err: if (!test_bit(__AT_DOWN, &adapter->flags)) schedule_work(&adapter->reset_task); } /** * atl1e_clean - NAPI Rx polling callback */ static int atl1e_clean(struct napi_struct *napi, int budget) { struct atl1e_adapter *adapter = container_of(napi, struct atl1e_adapter, napi); u32 imr_data; int work_done = 0; /* Keep link state information with original netdev */ if (!netif_carrier_ok(adapter->netdev)) goto quit_polling; atl1e_clean_rx_irq(adapter, 0, &work_done, budget); /* If no Tx and not enough Rx work done, exit the polling mode */ if (work_done < budget) { quit_polling: napi_complete(napi); imr_data = AT_READ_REG(&adapter->hw, REG_IMR); AT_WRITE_REG(&adapter->hw, REG_IMR, imr_data | ISR_RX_EVENT); /* test debug */ if (test_bit(__AT_DOWN, &adapter->flags)) { atomic_dec(&adapter->irq_sem); netdev_err(adapter->netdev, "atl1e_clean is called when AT_DOWN\n"); } /* reenable RX intr */ /*atl1e_irq_enable(adapter); */ } return work_done; } #ifdef CONFIG_NET_POLL_CONTROLLER /* * Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing. */ static void atl1e_netpoll(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); disable_irq(adapter->pdev->irq); atl1e_intr(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); } #endif static inline u16 atl1e_tpd_avail(struct atl1e_adapter *adapter) { struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; u16 next_to_use = 0; u16 next_to_clean = 0; next_to_clean = atomic_read(&tx_ring->next_to_clean); next_to_use = tx_ring->next_to_use; return (u16)(next_to_clean > next_to_use) ? (next_to_clean - next_to_use - 1) : (tx_ring->count + next_to_clean - next_to_use - 1); } /* * get next usable tpd * Note: should call atl1e_tdp_avail to make sure * there is enough tpd to use */ static struct atl1e_tpd_desc *atl1e_get_tpd(struct atl1e_adapter *adapter) { struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; u16 next_to_use = 0; next_to_use = tx_ring->next_to_use; if (++tx_ring->next_to_use == tx_ring->count) tx_ring->next_to_use = 0; memset(&tx_ring->desc[next_to_use], 0, sizeof(struct atl1e_tpd_desc)); return &tx_ring->desc[next_to_use]; } static struct atl1e_tx_buffer * atl1e_get_tx_buffer(struct atl1e_adapter *adapter, struct atl1e_tpd_desc *tpd) { struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; return &tx_ring->tx_buffer[tpd - tx_ring->desc]; } /* Calculate the transmit packet descript needed*/ static u16 atl1e_cal_tdp_req(const struct sk_buff *skb) { int i = 0; u16 tpd_req = 1; u16 fg_size = 0; u16 proto_hdr_len = 0; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { fg_size = skb_frag_size(&skb_shinfo(skb)->frags[i]); tpd_req += ((fg_size + MAX_TX_BUF_LEN - 1) >> MAX_TX_BUF_SHIFT); } if (skb_is_gso(skb)) { if (skb->protocol == htons(ETH_P_IP) || (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6)) { proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (proto_hdr_len < skb_headlen(skb)) { tpd_req += ((skb_headlen(skb) - proto_hdr_len + MAX_TX_BUF_LEN - 1) >> MAX_TX_BUF_SHIFT); } } } return tpd_req; } static int atl1e_tso_csum(struct atl1e_adapter *adapter, struct sk_buff *skb, struct atl1e_tpd_desc *tpd) { u8 hdr_len; u32 real_len; unsigned short offload_type; int err; if (skb_is_gso(skb)) { if (skb_header_cloned(skb)) { err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (unlikely(err)) return -1; } offload_type = skb_shinfo(skb)->gso_type; if (offload_type & SKB_GSO_TCPV4) { real_len = (((unsigned char *)ip_hdr(skb) - skb->data) + ntohs(ip_hdr(skb)->tot_len)); if (real_len < skb->len) pskb_trim(skb, real_len); hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); if (unlikely(skb->len == hdr_len)) { /* only xsum need */ netdev_warn(adapter->netdev, "IPV4 tso with zero data??\n"); goto check_sum; } else { ip_hdr(skb)->check = 0; ip_hdr(skb)->tot_len = 0; tcp_hdr(skb)->check = ~csum_tcpudp_magic( ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); tpd->word3 |= (ip_hdr(skb)->ihl & TDP_V4_IPHL_MASK) << TPD_V4_IPHL_SHIFT; tpd->word3 |= ((tcp_hdrlen(skb) >> 2) & TPD_TCPHDRLEN_MASK) << TPD_TCPHDRLEN_SHIFT; tpd->word3 |= ((skb_shinfo(skb)->gso_size) & TPD_MSS_MASK) << TPD_MSS_SHIFT; tpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT; } return 0; } } check_sum: if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { u8 css, cso; cso = skb_checksum_start_offset(skb); if (unlikely(cso & 0x1)) { netdev_err(adapter->netdev, "payload offset should not ant event number\n"); return -1; } else { css = cso + skb->csum_offset; tpd->word3 |= (cso & TPD_PLOADOFFSET_MASK) << TPD_PLOADOFFSET_SHIFT; tpd->word3 |= (css & TPD_CCSUMOFFSET_MASK) << TPD_CCSUMOFFSET_SHIFT; tpd->word3 |= 1 << TPD_CC_SEGMENT_EN_SHIFT; } } return 0; } static void atl1e_tx_map(struct atl1e_adapter *adapter, struct sk_buff *skb, struct atl1e_tpd_desc *tpd) { struct atl1e_tpd_desc *use_tpd = NULL; struct atl1e_tx_buffer *tx_buffer = NULL; u16 buf_len = skb_headlen(skb); u16 map_len = 0; u16 mapped_len = 0; u16 hdr_len = 0; u16 nr_frags; u16 f; int segment; nr_frags = skb_shinfo(skb)->nr_frags; segment = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK; if (segment) { /* TSO */ map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); use_tpd = tpd; tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); tx_buffer->length = map_len; tx_buffer->dma = pci_map_single(adapter->pdev, skb->data, hdr_len, PCI_DMA_TODEVICE); ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_SINGLE); mapped_len += map_len; use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | ((cpu_to_le32(tx_buffer->length) & TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); } while (mapped_len < buf_len) { /* mapped_len == 0, means we should use the first tpd, which is given by caller */ if (mapped_len == 0) { use_tpd = tpd; } else { use_tpd = atl1e_get_tpd(adapter); memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc)); } tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); tx_buffer->skb = NULL; tx_buffer->length = map_len = ((buf_len - mapped_len) >= MAX_TX_BUF_LEN) ? MAX_TX_BUF_LEN : (buf_len - mapped_len); tx_buffer->dma = pci_map_single(adapter->pdev, skb->data + mapped_len, map_len, PCI_DMA_TODEVICE); ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_SINGLE); mapped_len += map_len; use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | ((cpu_to_le32(tx_buffer->length) & TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); } for (f = 0; f < nr_frags; f++) { const struct skb_frag_struct *frag; u16 i; u16 seg_num; frag = &skb_shinfo(skb)->frags[f]; buf_len = skb_frag_size(frag); seg_num = (buf_len + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN; for (i = 0; i < seg_num; i++) { use_tpd = atl1e_get_tpd(adapter); memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc)); tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); BUG_ON(tx_buffer->skb); tx_buffer->skb = NULL; tx_buffer->length = (buf_len > MAX_TX_BUF_LEN) ? MAX_TX_BUF_LEN : buf_len; buf_len -= tx_buffer->length; tx_buffer->dma = skb_frag_dma_map(&adapter->pdev->dev, frag, (i * MAX_TX_BUF_LEN), tx_buffer->length, DMA_TO_DEVICE); ATL1E_SET_PCIMAP_TYPE(tx_buffer, ATL1E_TX_PCIMAP_PAGE); use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | ((cpu_to_le32(tx_buffer->length) & TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); } } if ((tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK) /* note this one is a tcp header */ tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT; /* The last tpd */ use_tpd->word3 |= 1 << TPD_EOP_SHIFT; /* The last buffer info contain the skb address, so it will be free after unmap */ tx_buffer->skb = skb; } static void atl1e_tx_queue(struct atl1e_adapter *adapter, u16 count, struct atl1e_tpd_desc *tpd) { struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, * such as IA-64). */ wmb(); AT_WRITE_REG(&adapter->hw, REG_MB_TPD_PROD_IDX, tx_ring->next_to_use); } static netdev_tx_t atl1e_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); unsigned long flags; u16 tpd_req = 1; struct atl1e_tpd_desc *tpd; if (test_bit(__AT_DOWN, &adapter->flags)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } if (unlikely(skb->len <= 0)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } tpd_req = atl1e_cal_tdp_req(skb); if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) return NETDEV_TX_LOCKED; if (atl1e_tpd_avail(adapter) < tpd_req) { /* no enough descriptor, just stop queue */ netif_stop_queue(netdev); spin_unlock_irqrestore(&adapter->tx_lock, flags); return NETDEV_TX_BUSY; } tpd = atl1e_get_tpd(adapter); if (vlan_tx_tag_present(skb)) { u16 vlan_tag = vlan_tx_tag_get(skb); u16 atl1e_vlan_tag; tpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT; AT_VLAN_TAG_TO_TPD_TAG(vlan_tag, atl1e_vlan_tag); tpd->word2 |= (atl1e_vlan_tag & TPD_VLANTAG_MASK) << TPD_VLAN_SHIFT; } if (skb->protocol == htons(ETH_P_8021Q)) tpd->word3 |= 1 << TPD_VL_TAGGED_SHIFT; if (skb_network_offset(skb) != ETH_HLEN) tpd->word3 |= 1 << TPD_ETHTYPE_SHIFT; /* 802.3 frame */ /* do TSO and check sum */ if (atl1e_tso_csum(adapter, skb, tpd) != 0) { spin_unlock_irqrestore(&adapter->tx_lock, flags); dev_kfree_skb_any(skb); return NETDEV_TX_OK; } atl1e_tx_map(adapter, skb, tpd); atl1e_tx_queue(adapter, tpd_req, tpd); netdev->trans_start = jiffies; /* NETIF_F_LLTX driver :( */ spin_unlock_irqrestore(&adapter->tx_lock, flags); return NETDEV_TX_OK; } static void atl1e_free_irq(struct atl1e_adapter *adapter) { struct net_device *netdev = adapter->netdev; free_irq(adapter->pdev->irq, netdev); if (adapter->have_msi) pci_disable_msi(adapter->pdev); } static int atl1e_request_irq(struct atl1e_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; struct net_device *netdev = adapter->netdev; int flags = 0; int err = 0; adapter->have_msi = true; err = pci_enable_msi(pdev); if (err) { netdev_dbg(netdev, "Unable to allocate MSI interrupt Error: %d\n", err); adapter->have_msi = false; } if (!adapter->have_msi) flags |= IRQF_SHARED; err = request_irq(pdev->irq, atl1e_intr, flags, netdev->name, netdev); if (err) { netdev_dbg(adapter->netdev, "Unable to allocate interrupt Error: %d\n", err); if (adapter->have_msi) pci_disable_msi(pdev); return err; } netdev_dbg(netdev, "atl1e_request_irq OK\n"); return err; } int atl1e_up(struct atl1e_adapter *adapter) { struct net_device *netdev = adapter->netdev; int err = 0; u32 val; /* hardware has been reset, we need to reload some things */ err = atl1e_init_hw(&adapter->hw); if (err) { err = -EIO; return err; } atl1e_init_ring_ptrs(adapter); atl1e_set_multi(netdev); atl1e_restore_vlan(adapter); if (atl1e_configure(adapter)) { err = -EIO; goto err_up; } clear_bit(__AT_DOWN, &adapter->flags); napi_enable(&adapter->napi); atl1e_irq_enable(adapter); val = AT_READ_REG(&adapter->hw, REG_MASTER_CTRL); AT_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, val | MASTER_CTRL_MANUAL_INT); err_up: return err; } void atl1e_down(struct atl1e_adapter *adapter) { struct net_device *netdev = adapter->netdev; /* signal that we're down so the interrupt handler does not * reschedule our watchdog timer */ set_bit(__AT_DOWN, &adapter->flags); #if defined(NETIF_F_LLTX) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netif_stop_queue(netdev); #else netif_tx_disable(netdev); #endif /* reset MAC to disable all RX/TX */ atl1e_reset_hw(&adapter->hw); msleep(1); napi_disable(&adapter->napi); atl1e_del_timer(adapter); atl1e_irq_disable(adapter); netif_carrier_off(netdev); adapter->link_speed = SPEED_0; adapter->link_duplex = -1; atl1e_clean_tx_ring(adapter); atl1e_clean_rx_ring(adapter); } /** * atl1e_open - Called when a network interface is made active * @netdev: network interface device structure * * Returns 0 on success, negative value on failure * * The open entry point is called when a network interface is made * active by the system (IFF_UP). At this point all resources needed * for transmit and receive operations are allocated, the interrupt * handler is registered with the OS, the watchdog timer is started, * and the stack is notified that the interface is ready. */ static int atl1e_open(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); int err; /* disallow open during test */ if (test_bit(__AT_TESTING, &adapter->flags)) return -EBUSY; /* allocate rx/tx dma buffer & descriptors */ atl1e_init_ring_resources(adapter); err = atl1e_setup_ring_resources(adapter); if (unlikely(err)) return err; err = atl1e_request_irq(adapter); if (unlikely(err)) goto err_req_irq; err = atl1e_up(adapter); if (unlikely(err)) goto err_up; return 0; err_up: atl1e_free_irq(adapter); err_req_irq: atl1e_free_ring_resources(adapter); atl1e_reset_hw(&adapter->hw); return err; } /** * atl1e_close - Disables a network interface * @netdev: network interface device structure * * Returns 0, this is not allowed to fail * * The close entry point is called when an interface is de-activated * by the OS. The hardware is still under the drivers control, but * needs to be disabled. A global MAC reset is issued to stop the * hardware, and all transmit and receive resources are freed. */ static int atl1e_close(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); atl1e_down(adapter); atl1e_free_irq(adapter); atl1e_free_ring_resources(adapter); return 0; } static int atl1e_suspend(struct pci_dev *pdev, pm_message_t state) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; u32 ctrl = 0; u32 mac_ctrl_data = 0; u32 wol_ctrl_data = 0; u16 mii_advertise_data = 0; u16 mii_bmsr_data = 0; u16 mii_intr_status_data = 0; u32 wufc = adapter->wol; u32 i; #ifdef CONFIG_PM int retval = 0; #endif if (netif_running(netdev)) { WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); atl1e_down(adapter); } netif_device_detach(netdev); #ifdef CONFIG_PM retval = pci_save_state(pdev); if (retval) return retval; #endif if (wufc) { /* get link status */ atl1e_read_phy_reg(hw, MII_BMSR, &mii_bmsr_data); atl1e_read_phy_reg(hw, MII_BMSR, &mii_bmsr_data); mii_advertise_data = ADVERTISE_10HALF; if ((atl1e_write_phy_reg(hw, MII_CTRL1000, 0) != 0) || (atl1e_write_phy_reg(hw, MII_ADVERTISE, mii_advertise_data) != 0) || (atl1e_phy_commit(hw)) != 0) { netdev_dbg(adapter->netdev, "set phy register failed\n"); goto wol_dis; } hw->phy_configured = false; /* re-init PHY when resume */ /* turn on magic packet wol */ if (wufc & AT_WUFC_MAG) wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN; if (wufc & AT_WUFC_LNKC) { /* if orignal link status is link, just wait for retrive link */ if (mii_bmsr_data & BMSR_LSTATUS) { for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { msleep(100); atl1e_read_phy_reg(hw, MII_BMSR, &mii_bmsr_data); if (mii_bmsr_data & BMSR_LSTATUS) break; } if ((mii_bmsr_data & BMSR_LSTATUS) == 0) netdev_dbg(adapter->netdev, "Link may change when suspend\n"); } wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; /* only link up can wake up */ if (atl1e_write_phy_reg(hw, MII_INT_CTRL, 0x400) != 0) { netdev_dbg(adapter->netdev, "read write phy register failed\n"); goto wol_dis; } } /* clear phy interrupt */ atl1e_read_phy_reg(hw, MII_INT_STATUS, &mii_intr_status_data); /* Config MAC Ctrl register */ mac_ctrl_data = MAC_CTRL_RX_EN; /* set to 10/100M halt duplex */ mac_ctrl_data |= MAC_CTRL_SPEED_10_100 << MAC_CTRL_SPEED_SHIFT; mac_ctrl_data |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); __atl1e_vlan_mode(netdev->features, &mac_ctrl_data); /* magic packet maybe Broadcast&multicast&Unicast frame */ if (wufc & AT_WUFC_MAG) mac_ctrl_data |= MAC_CTRL_BC_EN; netdev_dbg(adapter->netdev, "suspend MAC=0x%x\n", mac_ctrl_data); AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data); AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); /* pcie patch */ ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); goto suspend_exit; } wol_dis: /* WOL disabled */ AT_WRITE_REG(hw, REG_WOL_CTRL, 0); /* pcie patch */ ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC); ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); atl1e_force_ps(hw); hw->phy_configured = false; /* re-init PHY when resume */ pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); suspend_exit: if (netif_running(netdev)) atl1e_free_irq(adapter); pci_disable_device(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); return 0; } #ifdef CONFIG_PM static int atl1e_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1e_adapter *adapter = netdev_priv(netdev); u32 err; pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); err = pci_enable_device(pdev); if (err) { netdev_err(adapter->netdev, "Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); AT_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */ pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); if (netif_running(netdev)) { err = atl1e_request_irq(adapter); if (err) return err; } atl1e_reset_hw(&adapter->hw); if (netif_running(netdev)) atl1e_up(adapter); netif_device_attach(netdev); return 0; } #endif static void atl1e_shutdown(struct pci_dev *pdev) { atl1e_suspend(pdev, PMSG_SUSPEND); } static const struct net_device_ops atl1e_netdev_ops = { .ndo_open = atl1e_open, .ndo_stop = atl1e_close, .ndo_start_xmit = atl1e_xmit_frame, .ndo_get_stats = atl1e_get_stats, .ndo_set_rx_mode = atl1e_set_multi, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = atl1e_set_mac_addr, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atl1e_fix_features, .ndo_set_features = atl1e_set_features, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_change_mtu = atl1e_change_mtu, .ndo_do_ioctl = atl1e_ioctl, .ndo_tx_timeout = atl1e_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = atl1e_netpoll, #endif }; static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev) { SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); netdev_attach_ops(netdev, &atl1e_netdev_ops); netdev->watchdog_timeo = AT_TX_WATCHDOG; atl1e_set_ethtool_ops(netdev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_HW_VLAN_RX; netdev->features = netdev->hw_features | NETIF_F_LLTX | NETIF_F_HW_VLAN_TX; #else netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_HW_VLAN_RX | NETIF_F_LLTX | NETIF_F_HW_VLAN_TX; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ return 0; } /** * atl1e_probe - Device Initialization Routine * @pdev: PCI device information struct * @ent: entry in atl1e_pci_tbl * * Returns 0 on success, negative on failure * * atl1e_probe initializes an adapter identified by a pci_dev structure. * The OS initialization, configuring of the adapter private structure, * and a hardware reset occur. */ static int __devinit atl1e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct atl1e_adapter *adapter = NULL; static int cards_found; int err = 0; err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "cannot enable PCI device\n"); return err; } /* * The atl1e chip can DMA to 64-bit addresses, but it uses a single * shared register for the high 32 bits, so only a single, aligned, * 4 GB physical address range can be used at a time. * * Supporting 64-bit DMA on this hardware is more trouble than it's * worth. It is far easier to limit to 32-bit DMA than update * various kernel subsystems to support the mechanics required by a * fixed-high-32-bit system. */ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) || (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) { dev_err(&pdev->dev, "No usable DMA configuration,aborting\n"); goto err_dma; } err = pci_request_regions(pdev, atl1e_driver_name); if (err) { dev_err(&pdev->dev, "cannot obtain PCI resources\n"); goto err_pci_reg; } pci_set_master(pdev); netdev = alloc_etherdev(sizeof(struct atl1e_adapter)); if (netdev == NULL) { err = -ENOMEM; goto err_alloc_etherdev; } err = atl1e_init_netdev(netdev, pdev); if (err) { netdev_err(netdev, "init netdevice failed\n"); goto err_init_netdev; } adapter = netdev_priv(netdev); adapter->bd_number = cards_found; adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.adapter = adapter; adapter->hw.hw_addr = pci_iomap(pdev, BAR_0, 0); if (!adapter->hw.hw_addr) { err = -EIO; netdev_err(netdev, "cannot map device registers\n"); goto err_ioremap; } /* init mii data */ adapter->mii.dev = netdev; adapter->mii.mdio_read = atl1e_mdio_read; adapter->mii.mdio_write = atl1e_mdio_write; adapter->mii.phy_id_mask = 0x1f; adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK; netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64); init_timer(&adapter->phy_config_timer); adapter->phy_config_timer.function = atl1e_phy_config; adapter->phy_config_timer.data = (unsigned long) adapter; /* get user settings */ atl1e_check_options(adapter); /* * Mark all PCI regions associated with PCI device * pdev as being reserved by owner atl1e_driver_name * Enables bus-mastering on the device and calls * pcibios_set_master to do the needed arch specific settings */ atl1e_setup_pcicmd(pdev); /* setup the private structure */ err = atl1e_sw_init(adapter); if (err) { netdev_err(netdev, "net device private data init failed\n"); goto err_sw_init; } /* Init GPHY as early as possible due to power saving issue */ atl1e_phy_init(&adapter->hw); /* reset the controller to * put the device in a known good starting state */ err = atl1e_reset_hw(&adapter->hw); if (err) { err = -EIO; goto err_reset; } if (atl1e_read_mac_addr(&adapter->hw) != 0) { err = -EIO; netdev_err(netdev, "get mac address failed\n"); goto err_eeprom; } memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); netdev_dbg(netdev, "mac address : %pM\n", adapter->hw.mac_addr); INIT_WORK(&adapter->reset_task, atl1e_reset_task); INIT_WORK(&adapter->link_chg_task, atl1e_link_chg_task); err = register_netdev(netdev); if (err) { netdev_err(netdev, "register netdevice failed\n"); goto err_register; } /* assume we have no link for now */ netif_stop_queue(netdev); netif_carrier_off(netdev); cards_found++; return 0; err_reset: err_register: err_sw_init: err_eeprom: iounmap(adapter->hw.hw_addr); err_init_netdev: err_ioremap: free_netdev(netdev); err_alloc_etherdev: pci_release_regions(pdev); err_pci_reg: err_dma: pci_disable_device(pdev); return err; } /** * atl1e_remove - Device Removal Routine * @pdev: PCI device information struct * * atl1e_remove is called by the PCI subsystem to alert the driver * that it should release a PCI device. The could be caused by a * Hot-Plug event, or because the driver is going to be removed from * memory. */ static void __devexit atl1e_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1e_adapter *adapter = netdev_priv(netdev); /* * flush_scheduled work may reschedule our watchdog task, so * explicitly disable watchdog tasks from being rescheduled */ set_bit(__AT_DOWN, &adapter->flags); atl1e_del_timer(adapter); atl1e_cancel_work(adapter); unregister_netdev(netdev); atl1e_free_ring_resources(adapter); atl1e_force_ps(&adapter->hw); iounmap(adapter->hw.hw_addr); pci_release_regions(pdev); free_netdev(netdev); pci_disable_device(pdev); } /** * atl1e_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device * @state: The current pci connection state * * This function is called after a PCI bus error affecting * this device has been detected. */ static pci_ers_result_t atl1e_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1e_adapter *adapter = netdev_priv(netdev); netif_device_detach(netdev); if (state == pci_channel_io_perm_failure) return PCI_ERS_RESULT_DISCONNECT; if (netif_running(netdev)) atl1e_down(adapter); pci_disable_device(pdev); /* Request a slot slot reset. */ return PCI_ERS_RESULT_NEED_RESET; } /** * atl1e_io_slot_reset - called after the pci bus has been reset. * @pdev: Pointer to PCI device * * Restart the card from scratch, as if from a cold-boot. Implementation * resembles the first-half of the e1000_resume routine. */ static pci_ers_result_t atl1e_io_slot_reset(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1e_adapter *adapter = netdev_priv(netdev); if (pci_enable_device(pdev)) { netdev_err(adapter->netdev, "Cannot re-enable PCI device after reset\n"); return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); atl1e_reset_hw(&adapter->hw); return PCI_ERS_RESULT_RECOVERED; } /** * atl1e_io_resume - called when traffic can start flowing again. * @pdev: Pointer to PCI device * * This callback is called when the error recovery driver tells us that * its OK to resume normal operation. Implementation resembles the * second-half of the atl1e_resume routine. */ static void atl1e_io_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1e_adapter *adapter = netdev_priv(netdev); if (netif_running(netdev)) { if (atl1e_up(adapter)) { netdev_err(adapter->netdev, "can't bring device back up after reset\n"); return; } } netif_device_attach(netdev); } static const struct pci_error_handlers atl1e_err_handler = { .error_detected = atl1e_io_error_detected, .slot_reset = atl1e_io_slot_reset, .resume = atl1e_io_resume, }; static struct pci_driver atl1e_driver = { .name = atl1e_driver_name, .id_table = atl1e_pci_tbl, .probe = atl1e_probe, .remove = __devexit_p(atl1e_remove), /* Power Management Hooks */ #ifdef CONFIG_PM .suspend = atl1e_suspend, .resume = atl1e_resume, #endif .shutdown = atl1e_shutdown, .err_handler = &atl1e_err_handler }; /** * atl1e_init_module - Driver Registration Routine * * atl1e_init_module is the first routine called when the driver is * loaded. All it does is register with the PCI subsystem. */ static int __init atl1e_init_module(void) { return pci_register_driver(&atl1e_driver); } /** * atl1e_exit_module - Driver Exit Cleanup Routine * * atl1e_exit_module is called just before the driver is removed * from memory. */ static void __exit atl1e_exit_module(void) { pci_unregister_driver(&atl1e_driver); } module_init(atl1e_init_module); module_exit(atl1e_exit_module); compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c0000644000175000017500000002653012026211315026033 0ustar mcgrofmcgrof/* * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include "atl1e.h" static int atl1e_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); if (hw->nic_type == athr_l1e) ecmd->supported |= SUPPORTED_1000baseT_Full; ecmd->advertising = ADVERTISED_TP; ecmd->advertising |= ADVERTISED_Autoneg; ecmd->advertising |= hw->autoneg_advertised; ecmd->port = PORT_TP; ecmd->phy_address = 0; ecmd->transceiver = XCVR_INTERNAL; if (adapter->link_speed != SPEED_0) { ethtool_cmd_speed_set(ecmd, adapter->link_speed); if (adapter->link_duplex == FULL_DUPLEX) ecmd->duplex = DUPLEX_FULL; else ecmd->duplex = DUPLEX_HALF; } else { ethtool_cmd_speed_set(ecmd, -1); ecmd->duplex = -1; } ecmd->autoneg = AUTONEG_ENABLE; return 0; } static int atl1e_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); if (ecmd->autoneg == AUTONEG_ENABLE) { u16 adv4, adv9; if ((ecmd->advertising&ADVERTISE_1000_FULL)) { if (hw->nic_type == athr_l1e) { hw->autoneg_advertised = ecmd->advertising & AT_ADV_MASK; } else { clear_bit(__AT_RESETTING, &adapter->flags); return -EINVAL; } } else if (ecmd->advertising&ADVERTISE_1000_HALF) { clear_bit(__AT_RESETTING, &adapter->flags); return -EINVAL; } else { hw->autoneg_advertised = ecmd->advertising & AT_ADV_MASK; } ecmd->advertising = hw->autoneg_advertised | ADVERTISED_TP | ADVERTISED_Autoneg; adv4 = hw->mii_autoneg_adv_reg & ~ADVERTISE_ALL; adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK; if (hw->autoneg_advertised & ADVERTISE_10_HALF) adv4 |= ADVERTISE_10HALF; if (hw->autoneg_advertised & ADVERTISE_10_FULL) adv4 |= ADVERTISE_10FULL; if (hw->autoneg_advertised & ADVERTISE_100_HALF) adv4 |= ADVERTISE_100HALF; if (hw->autoneg_advertised & ADVERTISE_100_FULL) adv4 |= ADVERTISE_100FULL; if (hw->autoneg_advertised & ADVERTISE_1000_FULL) adv9 |= ADVERTISE_1000FULL; if (adv4 != hw->mii_autoneg_adv_reg || adv9 != hw->mii_1000t_ctrl_reg) { hw->mii_autoneg_adv_reg = adv4; hw->mii_1000t_ctrl_reg = adv9; hw->re_autoneg = true; } } else { clear_bit(__AT_RESETTING, &adapter->flags); return -EINVAL; } /* reset the link */ if (netif_running(adapter->netdev)) { atl1e_down(adapter); atl1e_up(adapter); } else atl1e_reset_hw(&adapter->hw); clear_bit(__AT_RESETTING, &adapter->flags); return 0; } static u32 atl1e_get_msglevel(struct net_device *netdev) { #ifdef DBG return 1; #else return 0; #endif } static int atl1e_get_regs_len(struct net_device *netdev) { return AT_REGS_LEN * sizeof(u32); } static void atl1e_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; u32 *regs_buff = p; u16 phy_data; memset(p, 0, AT_REGS_LEN * sizeof(u32)); regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; regs_buff[0] = AT_READ_REG(hw, REG_VPD_CAP); regs_buff[1] = AT_READ_REG(hw, REG_SPI_FLASH_CTRL); regs_buff[2] = AT_READ_REG(hw, REG_SPI_FLASH_CONFIG); regs_buff[3] = AT_READ_REG(hw, REG_TWSI_CTRL); regs_buff[4] = AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); regs_buff[5] = AT_READ_REG(hw, REG_MASTER_CTRL); regs_buff[6] = AT_READ_REG(hw, REG_MANUAL_TIMER_INIT); regs_buff[7] = AT_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); regs_buff[8] = AT_READ_REG(hw, REG_GPHY_CTRL); regs_buff[9] = AT_READ_REG(hw, REG_CMBDISDMA_TIMER); regs_buff[10] = AT_READ_REG(hw, REG_IDLE_STATUS); regs_buff[11] = AT_READ_REG(hw, REG_MDIO_CTRL); regs_buff[12] = AT_READ_REG(hw, REG_SERDES_LOCK); regs_buff[13] = AT_READ_REG(hw, REG_MAC_CTRL); regs_buff[14] = AT_READ_REG(hw, REG_MAC_IPG_IFG); regs_buff[15] = AT_READ_REG(hw, REG_MAC_STA_ADDR); regs_buff[16] = AT_READ_REG(hw, REG_MAC_STA_ADDR+4); regs_buff[17] = AT_READ_REG(hw, REG_RX_HASH_TABLE); regs_buff[18] = AT_READ_REG(hw, REG_RX_HASH_TABLE+4); regs_buff[19] = AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); regs_buff[20] = AT_READ_REG(hw, REG_MTU); regs_buff[21] = AT_READ_REG(hw, REG_WOL_CTRL); regs_buff[22] = AT_READ_REG(hw, REG_SRAM_TRD_ADDR); regs_buff[23] = AT_READ_REG(hw, REG_SRAM_TRD_LEN); regs_buff[24] = AT_READ_REG(hw, REG_SRAM_RXF_ADDR); regs_buff[25] = AT_READ_REG(hw, REG_SRAM_RXF_LEN); regs_buff[26] = AT_READ_REG(hw, REG_SRAM_TXF_ADDR); regs_buff[27] = AT_READ_REG(hw, REG_SRAM_TXF_LEN); regs_buff[28] = AT_READ_REG(hw, REG_SRAM_TCPH_ADDR); regs_buff[29] = AT_READ_REG(hw, REG_SRAM_PKTH_ADDR); atl1e_read_phy_reg(hw, MII_BMCR, &phy_data); regs_buff[73] = (u32)phy_data; atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); regs_buff[74] = (u32)phy_data; } static int atl1e_get_eeprom_len(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); if (!atl1e_check_eeprom_exist(&adapter->hw)) return AT_EEPROM_LEN; else return 0; } static int atl1e_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; u32 *eeprom_buff; int first_dword, last_dword; int ret_val = 0; int i; if (eeprom->len == 0) return -EINVAL; if (atl1e_check_eeprom_exist(hw)) /* not exist */ return -EINVAL; eeprom->magic = hw->vendor_id | (hw->device_id << 16); first_dword = eeprom->offset >> 2; last_dword = (eeprom->offset + eeprom->len - 1) >> 2; eeprom_buff = kmalloc(sizeof(u32) * (last_dword - first_dword + 1), GFP_KERNEL); if (eeprom_buff == NULL) return -ENOMEM; for (i = first_dword; i < last_dword; i++) { if (!atl1e_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { kfree(eeprom_buff); return -EIO; } } memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), eeprom->len); kfree(eeprom_buff); return ret_val; } static int atl1e_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; u32 *eeprom_buff; u32 *ptr; int first_dword, last_dword; int ret_val = 0; int i; if (eeprom->len == 0) return -EOPNOTSUPP; if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) return -EINVAL; first_dword = eeprom->offset >> 2; last_dword = (eeprom->offset + eeprom->len - 1) >> 2; eeprom_buff = kmalloc(AT_EEPROM_LEN, GFP_KERNEL); if (eeprom_buff == NULL) return -ENOMEM; ptr = eeprom_buff; if (eeprom->offset & 3) { /* need read/modify/write of first changed EEPROM word */ /* only the second byte of the word is being modified */ if (!atl1e_read_eeprom(hw, first_dword * 4, &(eeprom_buff[0]))) { ret_val = -EIO; goto out; } ptr++; } if (((eeprom->offset + eeprom->len) & 3)) { /* need read/modify/write of last changed EEPROM word */ /* only the first byte of the word is being modified */ if (!atl1e_read_eeprom(hw, last_dword * 4, &(eeprom_buff[last_dword - first_dword]))) { ret_val = -EIO; goto out; } } /* Device's eeprom is always little-endian, word addressable */ memcpy(ptr, bytes, eeprom->len); for (i = 0; i < last_dword - first_dword + 1; i++) { if (!atl1e_write_eeprom(hw, ((first_dword + i) * 4), eeprom_buff[i])) { ret_val = -EIO; goto out; } } out: kfree(eeprom_buff); return ret_val; } static void atl1e_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct atl1e_adapter *adapter = netdev_priv(netdev); strlcpy(drvinfo->driver, atl1e_driver_name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, atl1e_driver_version, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "L1e", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = 0; drvinfo->testinfo_len = 0; drvinfo->regdump_len = atl1e_get_regs_len(netdev); drvinfo->eedump_len = atl1e_get_eeprom_len(netdev); } static void atl1e_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl1e_adapter *adapter = netdev_priv(netdev); wol->supported = WAKE_MAGIC | WAKE_PHY; wol->wolopts = 0; if (adapter->wol & AT_WUFC_EX) wol->wolopts |= WAKE_UCAST; if (adapter->wol & AT_WUFC_MC) wol->wolopts |= WAKE_MCAST; if (adapter->wol & AT_WUFC_BC) wol->wolopts |= WAKE_BCAST; if (adapter->wol & AT_WUFC_MAG) wol->wolopts |= WAKE_MAGIC; if (adapter->wol & AT_WUFC_LNKC) wol->wolopts |= WAKE_PHY; } static int atl1e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl1e_adapter *adapter = netdev_priv(netdev); if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)) return -EOPNOTSUPP; /* these settings will always override what we currently have */ adapter->wol = 0; if (wol->wolopts & WAKE_MAGIC) adapter->wol |= AT_WUFC_MAG; if (wol->wolopts & WAKE_PHY) adapter->wol |= AT_WUFC_LNKC; device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); return 0; } static int atl1e_nway_reset(struct net_device *netdev) { struct atl1e_adapter *adapter = netdev_priv(netdev); if (netif_running(netdev)) atl1e_reinit_locked(adapter); return 0; } static const struct ethtool_ops atl1e_ethtool_ops = { .get_settings = atl1e_get_settings, .set_settings = atl1e_set_settings, .get_drvinfo = atl1e_get_drvinfo, .get_regs_len = atl1e_get_regs_len, .get_regs = atl1e_get_regs, .get_wol = atl1e_get_wol, .set_wol = atl1e_set_wol, .get_msglevel = atl1e_get_msglevel, .nway_reset = atl1e_nway_reset, .get_link = ethtool_op_get_link, .get_eeprom_len = atl1e_get_eeprom_len, .get_eeprom = atl1e_get_eeprom, .set_eeprom = atl1e_set_eeprom, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) .set_tx_csum = ethtool_op_set_tx_hw_csum, .set_sg = ethtool_op_set_sg, .set_tso = ethtool_op_set_tso, #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; void atl1e_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops); } compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/atl1e_param.c0000644000175000017500000001624612026211315025460 0ustar mcgrofmcgrof/* * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "atl1e.h" /* This is the only thing that needs to be changed to adjust the * maximum number of ports that the driver can manage. */ #define ATL1E_MAX_NIC 32 #define OPTION_UNSET -1 #define OPTION_DISABLED 0 #define OPTION_ENABLED 1 /* All parameters are treated the same, as an integer array of values. * This macro just reduces the need to repeat the same declaration code * over and over (plus this helps to avoid typo bugs). */ #define ATL1E_PARAM_INIT { [0 ... ATL1E_MAX_NIC] = OPTION_UNSET } #define ATL1E_PARAM(x, desc) \ static int __devinitdata x[ATL1E_MAX_NIC + 1] = ATL1E_PARAM_INIT; \ static unsigned int num_##x; \ module_param_array_named(x, x, int, &num_##x, 0); \ MODULE_PARM_DESC(x, desc); /* Transmit Memory count * * Valid Range: 64-2048 * * Default Value: 128 */ #define ATL1E_MIN_TX_DESC_CNT 32 #define ATL1E_MAX_TX_DESC_CNT 1020 #define ATL1E_DEFAULT_TX_DESC_CNT 128 ATL1E_PARAM(tx_desc_cnt, "Transmit description count"); /* Receive Memory Block Count * * Valid Range: 16-512 * * Default Value: 128 */ #define ATL1E_MIN_RX_MEM_SIZE 8 /* 8KB */ #define ATL1E_MAX_RX_MEM_SIZE 1024 /* 1MB */ #define ATL1E_DEFAULT_RX_MEM_SIZE 256 /* 128KB */ ATL1E_PARAM(rx_mem_size, "memory size of rx buffer(KB)"); /* User Specified MediaType Override * * Valid Range: 0-5 * - 0 - auto-negotiate at all supported speeds * - 1 - only link at 100Mbps Full Duplex * - 2 - only link at 100Mbps Half Duplex * - 3 - only link at 10Mbps Full Duplex * - 4 - only link at 10Mbps Half Duplex * Default Value: 0 */ ATL1E_PARAM(media_type, "MediaType Select"); /* Interrupt Moderate Timer in units of 2 us * * Valid Range: 10-65535 * * Default Value: 45000(90ms) */ #define INT_MOD_DEFAULT_CNT 100 /* 200us */ #define INT_MOD_MAX_CNT 65000 #define INT_MOD_MIN_CNT 50 ATL1E_PARAM(int_mod_timer, "Interrupt Moderator Timer"); #define AUTONEG_ADV_DEFAULT 0x2F #define AUTONEG_ADV_MASK 0x2F #define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL #define FLASH_VENDOR_DEFAULT 0 #define FLASH_VENDOR_MIN 0 #define FLASH_VENDOR_MAX 2 struct atl1e_option { enum { enable_option, range_option, list_option } type; char *name; char *err; int def; union { struct { /* range_option info */ int min; int max; } r; struct { /* list_option info */ int nr; struct atl1e_opt_list { int i; char *str; } *p; } l; } arg; }; static int __devinit atl1e_validate_option(int *value, struct atl1e_option *opt, struct atl1e_adapter *adapter) { if (*value == OPTION_UNSET) { *value = opt->def; return 0; } switch (opt->type) { case enable_option: switch (*value) { case OPTION_ENABLED: netdev_info(adapter->netdev, "%s Enabled\n", opt->name); return 0; case OPTION_DISABLED: netdev_info(adapter->netdev, "%s Disabled\n", opt->name); return 0; } break; case range_option: if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { netdev_info(adapter->netdev, "%s set to %i\n", opt->name, *value); return 0; } break; case list_option:{ int i; struct atl1e_opt_list *ent; for (i = 0; i < opt->arg.l.nr; i++) { ent = &opt->arg.l.p[i]; if (*value == ent->i) { if (ent->str[0] != '\0') netdev_info(adapter->netdev, "%s\n", ent->str); return 0; } } break; } default: BUG(); } netdev_info(adapter->netdev, "Invalid %s specified (%i) %s\n", opt->name, *value, opt->err); *value = opt->def; return -1; } /** * atl1e_check_options - Range Checking for Command Line Parameters * @adapter: board private structure * * This routine checks all command line parameters for valid user * input. If an invalid value is given, or if no user specified * value exists, a default value is used. The final value is stored * in a variable in the adapter structure. */ void __devinit atl1e_check_options(struct atl1e_adapter *adapter) { int bd = adapter->bd_number; if (bd >= ATL1E_MAX_NIC) { netdev_notice(adapter->netdev, "no configuration for board #%i\n", bd); netdev_notice(adapter->netdev, "Using defaults for all values\n"); } { /* Transmit Ring Size */ struct atl1e_option opt = { .type = range_option, .name = "Transmit Ddescription Count", .err = "using default of " __MODULE_STRING(ATL1E_DEFAULT_TX_DESC_CNT), .def = ATL1E_DEFAULT_TX_DESC_CNT, .arg = { .r = { .min = ATL1E_MIN_TX_DESC_CNT, .max = ATL1E_MAX_TX_DESC_CNT} } }; int val; if (num_tx_desc_cnt > bd) { val = tx_desc_cnt[bd]; atl1e_validate_option(&val, &opt, adapter); adapter->tx_ring.count = (u16) val & 0xFFFC; } else adapter->tx_ring.count = (u16)opt.def; } { /* Receive Memory Block Count */ struct atl1e_option opt = { .type = range_option, .name = "Memory size of rx buffer(KB)", .err = "using default of " __MODULE_STRING(ATL1E_DEFAULT_RX_MEM_SIZE), .def = ATL1E_DEFAULT_RX_MEM_SIZE, .arg = { .r = { .min = ATL1E_MIN_RX_MEM_SIZE, .max = ATL1E_MAX_RX_MEM_SIZE} } }; int val; if (num_rx_mem_size > bd) { val = rx_mem_size[bd]; atl1e_validate_option(&val, &opt, adapter); adapter->rx_ring.page_size = (u32)val * 1024; } else { adapter->rx_ring.page_size = (u32)opt.def * 1024; } } { /* Interrupt Moderate Timer */ struct atl1e_option opt = { .type = range_option, .name = "Interrupt Moderate Timer", .err = "using default of " __MODULE_STRING(INT_MOD_DEFAULT_CNT), .def = INT_MOD_DEFAULT_CNT, .arg = { .r = { .min = INT_MOD_MIN_CNT, .max = INT_MOD_MAX_CNT} } } ; int val; if (num_int_mod_timer > bd) { val = int_mod_timer[bd]; atl1e_validate_option(&val, &opt, adapter); adapter->hw.imt = (u16) val; } else adapter->hw.imt = (u16)(opt.def); } { /* MediaType */ struct atl1e_option opt = { .type = range_option, .name = "Speed/Duplex Selection", .err = "using default of " __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR), .def = MEDIA_TYPE_AUTO_SENSOR, .arg = { .r = { .min = MEDIA_TYPE_AUTO_SENSOR, .max = MEDIA_TYPE_10M_HALF} } } ; int val; if (num_media_type > bd) { val = media_type[bd]; atl1e_validate_option(&val, &opt, adapter); adapter->hw.media_type = (u16) val; } else adapter->hw.media_type = (u16)(opt.def); } } compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h0000644000175000017500000007550412026211315025005 0ustar mcgrofmcgrof/* * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _ATHL1E_HW_H_ #define _ATHL1E_HW_H_ #include #include struct atl1e_adapter; struct atl1e_hw; /* function prototype */ s32 atl1e_reset_hw(struct atl1e_hw *hw); s32 atl1e_read_mac_addr(struct atl1e_hw *hw); s32 atl1e_init_hw(struct atl1e_hw *hw); s32 atl1e_phy_commit(struct atl1e_hw *hw); s32 atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex); u32 atl1e_auto_get_fc(struct atl1e_adapter *adapter, u16 duplex); u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr); void atl1e_hash_set(struct atl1e_hw *hw, u32 hash_value); s32 atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data); s32 atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data); s32 atl1e_validate_mdi_setting(struct atl1e_hw *hw); void atl1e_hw_set_mac_addr(struct atl1e_hw *hw); bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value); bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value); s32 atl1e_phy_enter_power_saving(struct atl1e_hw *hw); s32 atl1e_phy_leave_power_saving(struct atl1e_hw *hw); s32 atl1e_phy_init(struct atl1e_hw *hw); int atl1e_check_eeprom_exist(struct atl1e_hw *hw); void atl1e_force_ps(struct atl1e_hw *hw); s32 atl1e_restart_autoneg(struct atl1e_hw *hw); /* register definition */ #define REG_PM_CTRLSTAT 0x44 #define REG_PCIE_CAP_LIST 0x58 #define REG_DEVICE_CAP 0x5C #define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7 #define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0 #define REG_DEVICE_CTRL 0x60 #define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7 #define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5 #define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7 #define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12 #define REG_VPD_CAP 0x6C #define VPD_CAP_ID_MASK 0xff #define VPD_CAP_ID_SHIFT 0 #define VPD_CAP_NEXT_PTR_MASK 0xFF #define VPD_CAP_NEXT_PTR_SHIFT 8 #define VPD_CAP_VPD_ADDR_MASK 0x7FFF #define VPD_CAP_VPD_ADDR_SHIFT 16 #define VPD_CAP_VPD_FLAG 0x80000000 #define REG_VPD_DATA 0x70 #define REG_SPI_FLASH_CTRL 0x200 #define SPI_FLASH_CTRL_STS_NON_RDY 0x1 #define SPI_FLASH_CTRL_STS_WEN 0x2 #define SPI_FLASH_CTRL_STS_WPEN 0x80 #define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF #define SPI_FLASH_CTRL_DEV_STS_SHIFT 0 #define SPI_FLASH_CTRL_INS_MASK 0x7 #define SPI_FLASH_CTRL_INS_SHIFT 8 #define SPI_FLASH_CTRL_START 0x800 #define SPI_FLASH_CTRL_EN_VPD 0x2000 #define SPI_FLASH_CTRL_LDSTART 0x8000 #define SPI_FLASH_CTRL_CS_HI_MASK 0x3 #define SPI_FLASH_CTRL_CS_HI_SHIFT 16 #define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3 #define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18 #define SPI_FLASH_CTRL_CLK_LO_MASK 0x3 #define SPI_FLASH_CTRL_CLK_LO_SHIFT 20 #define SPI_FLASH_CTRL_CLK_HI_MASK 0x3 #define SPI_FLASH_CTRL_CLK_HI_SHIFT 22 #define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3 #define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24 #define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3 #define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26 #define SPI_FLASH_CTRL_WAIT_READY 0x10000000 #define REG_SPI_ADDR 0x204 #define REG_SPI_DATA 0x208 #define REG_SPI_FLASH_CONFIG 0x20C #define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF #define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0 #define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3 #define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24 #define SPI_FLASH_CONFIG_LD_EXIST 0x4000000 #define REG_SPI_FLASH_OP_PROGRAM 0x210 #define REG_SPI_FLASH_OP_SC_ERASE 0x211 #define REG_SPI_FLASH_OP_CHIP_ERASE 0x212 #define REG_SPI_FLASH_OP_RDID 0x213 #define REG_SPI_FLASH_OP_WREN 0x214 #define REG_SPI_FLASH_OP_RDSR 0x215 #define REG_SPI_FLASH_OP_WRSR 0x216 #define REG_SPI_FLASH_OP_READ 0x217 #define REG_TWSI_CTRL 0x218 #define TWSI_CTRL_LD_OFFSET_MASK 0xFF #define TWSI_CTRL_LD_OFFSET_SHIFT 0 #define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 #define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 #define TWSI_CTRL_SW_LDSTART 0x800 #define TWSI_CTRL_HW_LDSTART 0x1000 #define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x0x7F #define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 #define TWSI_CTRL_LD_EXIST 0x400000 #define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 #define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 #define TWSI_CTRL_FREQ_SEL_100K 0 #define TWSI_CTRL_FREQ_SEL_200K 1 #define TWSI_CTRL_FREQ_SEL_300K 2 #define TWSI_CTRL_FREQ_SEL_400K 3 #define TWSI_CTRL_SMB_SLV_ADDR #define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 #define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 #define REG_PCIE_DEV_MISC_CTRL 0x21C #define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2 #define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1 #define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4 #define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8 #define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10 #define REG_PCIE_PHYMISC 0x1000 #define PCIE_PHYMISC_FORCE_RCV_DET 0x4 #define REG_LTSSM_TEST_MODE 0x12FC #define LTSSM_TEST_MODE_DEF 0xE000 /* Selene Master Control Register */ #define REG_MASTER_CTRL 0x1400 #define MASTER_CTRL_SOFT_RST 0x1 #define MASTER_CTRL_MTIMER_EN 0x2 #define MASTER_CTRL_ITIMER_EN 0x4 #define MASTER_CTRL_MANUAL_INT 0x8 #define MASTER_CTRL_ITIMER2_EN 0x20 #define MASTER_CTRL_INT_RDCLR 0x40 #define MASTER_CTRL_LED_MODE 0x200 #define MASTER_CTRL_REV_NUM_SHIFT 16 #define MASTER_CTRL_REV_NUM_MASK 0xff #define MASTER_CTRL_DEV_ID_SHIFT 24 #define MASTER_CTRL_DEV_ID_MASK 0xff /* Timer Initial Value Register */ #define REG_MANUAL_TIMER_INIT 0x1404 /* IRQ ModeratorTimer Initial Value Register */ #define REG_IRQ_MODU_TIMER_INIT 0x1408 /* w */ #define REG_IRQ_MODU_TIMER2_INIT 0x140A /* w */ #define REG_GPHY_CTRL 0x140C #define GPHY_CTRL_EXT_RESET 1 #define GPHY_CTRL_PIPE_MOD 2 #define GPHY_CTRL_TEST_MODE_MASK 3 #define GPHY_CTRL_TEST_MODE_SHIFT 2 #define GPHY_CTRL_BERT_START 0x10 #define GPHY_CTRL_GATE_25M_EN 0x20 #define GPHY_CTRL_LPW_EXIT 0x40 #define GPHY_CTRL_PHY_IDDQ 0x80 #define GPHY_CTRL_PHY_IDDQ_DIS 0x100 #define GPHY_CTRL_PCLK_SEL_DIS 0x200 #define GPHY_CTRL_HIB_EN 0x400 #define GPHY_CTRL_HIB_PULSE 0x800 #define GPHY_CTRL_SEL_ANA_RST 0x1000 #define GPHY_CTRL_PHY_PLL_ON 0x2000 #define GPHY_CTRL_PWDOWN_HW 0x4000 #define GPHY_CTRL_DEFAULT (\ GPHY_CTRL_PHY_PLL_ON |\ GPHY_CTRL_SEL_ANA_RST |\ GPHY_CTRL_HIB_PULSE |\ GPHY_CTRL_HIB_EN) #define GPHY_CTRL_PW_WOL_DIS (\ GPHY_CTRL_PHY_PLL_ON |\ GPHY_CTRL_SEL_ANA_RST |\ GPHY_CTRL_HIB_PULSE |\ GPHY_CTRL_HIB_EN |\ GPHY_CTRL_PWDOWN_HW |\ GPHY_CTRL_PCLK_SEL_DIS |\ GPHY_CTRL_PHY_IDDQ) /* IRQ Anti-Lost Timer Initial Value Register */ #define REG_CMBDISDMA_TIMER 0x140E /* Block IDLE Status Register */ #define REG_IDLE_STATUS 0x1410 #define IDLE_STATUS_RXMAC 1 /* 1: RXMAC state machine is in non-IDLE state. 0: RXMAC is idling */ #define IDLE_STATUS_TXMAC 2 /* 1: TXMAC state machine is in non-IDLE state. 0: TXMAC is idling */ #define IDLE_STATUS_RXQ 4 /* 1: RXQ state machine is in non-IDLE state. 0: RXQ is idling */ #define IDLE_STATUS_TXQ 8 /* 1: TXQ state machine is in non-IDLE state. 0: TXQ is idling */ #define IDLE_STATUS_DMAR 0x10 /* 1: DMAR state machine is in non-IDLE state. 0: DMAR is idling */ #define IDLE_STATUS_DMAW 0x20 /* 1: DMAW state machine is in non-IDLE state. 0: DMAW is idling */ #define IDLE_STATUS_SMB 0x40 /* 1: SMB state machine is in non-IDLE state. 0: SMB is idling */ #define IDLE_STATUS_CMB 0x80 /* 1: CMB state machine is in non-IDLE state. 0: CMB is idling */ /* MDIO Control Register */ #define REG_MDIO_CTRL 0x1414 #define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit control data to write to PHY MII management register */ #define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit status data that was read from the PHY MII management register*/ #define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */ #define MDIO_REG_ADDR_SHIFT 16 #define MDIO_RW 0x200000 /* 1: read, 0: write */ #define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */ #define MDIO_START 0x800000 /* Write 1 to initiate the MDIO master. And this bit is self cleared after one cycle*/ #define MDIO_CLK_SEL_SHIFT 24 #define MDIO_CLK_25_4 0 #define MDIO_CLK_25_6 2 #define MDIO_CLK_25_8 3 #define MDIO_CLK_25_10 4 #define MDIO_CLK_25_14 5 #define MDIO_CLK_25_20 6 #define MDIO_CLK_25_28 7 #define MDIO_BUSY 0x8000000 #define MDIO_AP_EN 0x10000000 #define MDIO_WAIT_TIMES 10 /* MII PHY Status Register */ #define REG_PHY_STATUS 0x1418 #define PHY_STATUS_100M 0x20000 #define PHY_STATUS_EMI_CA 0x40000 /* BIST Control and Status Register0 (for the Packet Memory) */ #define REG_BIST0_CTRL 0x141c #define BIST0_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */ /* BIST process and reset to zero when BIST is done */ #define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */ /* decoder failure or more than 1 cell stuck-to-x failure */ #define BIST0_FUSE_FLAG 0x4 /* 1: Indicating one cell has been fixed */ /* BIST Control and Status Register1(for the retry buffer of PCI Express) */ #define REG_BIST1_CTRL 0x1420 #define BIST1_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */ /* BIST process and reset to zero when BIST is done */ #define BIST1_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */ /* decoder failure or more than 1 cell stuck-to-x failure.*/ #define BIST1_FUSE_FLAG 0x4 /* SerDes Lock Detect Control and Status Register */ #define REG_SERDES_LOCK 0x1424 #define SERDES_LOCK_DETECT 1 /* 1: SerDes lock detected . This signal comes from Analog SerDes */ #define SERDES_LOCK_DETECT_EN 2 /* 1: Enable SerDes Lock detect function */ /* MAC Control Register */ #define REG_MAC_CTRL 0x1480 #define MAC_CTRL_TX_EN 1 /* 1: Transmit Enable */ #define MAC_CTRL_RX_EN 2 /* 1: Receive Enable */ #define MAC_CTRL_TX_FLOW 4 /* 1: Transmit Flow Control Enable */ #define MAC_CTRL_RX_FLOW 8 /* 1: Receive Flow Control Enable */ #define MAC_CTRL_LOOPBACK 0x10 /* 1: Loop back at G/MII Interface */ #define MAC_CTRL_DUPLX 0x20 /* 1: Full-duplex mode 0: Half-duplex mode */ #define MAC_CTRL_ADD_CRC 0x40 /* 1: Instruct MAC to attach CRC on all egress Ethernet frames */ #define MAC_CTRL_PAD 0x80 /* 1: Instruct MAC to pad short frames to 60-bytes, and then attach CRC. This bit has higher priority over CRC_EN */ #define MAC_CTRL_LENCHK 0x100 /* 1: Instruct MAC to check if length field matches the real packet length */ #define MAC_CTRL_HUGE_EN 0x200 /* 1: receive Jumbo frame enable */ #define MAC_CTRL_PRMLEN_SHIFT 10 /* Preamble length */ #define MAC_CTRL_PRMLEN_MASK 0xf #define MAC_CTRL_RMV_VLAN 0x4000 /* 1: to remove VLAN Tag automatically from all receive packets */ #define MAC_CTRL_PROMIS_EN 0x8000 /* 1: Promiscuous Mode Enable */ #define MAC_CTRL_TX_PAUSE 0x10000 /* 1: transmit test pause */ #define MAC_CTRL_SCNT 0x20000 /* 1: shortcut slot time counter */ #define MAC_CTRL_SRST_TX 0x40000 /* 1: synchronized reset Transmit MAC module */ #define MAC_CTRL_TX_SIMURST 0x80000 /* 1: transmit simulation reset */ #define MAC_CTRL_SPEED_SHIFT 20 /* 10: gigabit 01:10M/100M */ #define MAC_CTRL_SPEED_MASK 0x300000 #define MAC_CTRL_SPEED_1000 2 #define MAC_CTRL_SPEED_10_100 1 #define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 /* 1: transmit maximum backoff (half-duplex test bit) */ #define MAC_CTRL_TX_HUGE 0x800000 /* 1: transmit huge enable */ #define MAC_CTRL_RX_CHKSUM_EN 0x1000000 /* 1: RX checksum enable */ #define MAC_CTRL_MC_ALL_EN 0x2000000 /* 1: upload all multicast frame without error to system */ #define MAC_CTRL_BC_EN 0x4000000 /* 1: upload all broadcast frame without error to system */ #define MAC_CTRL_DBG 0x8000000 /* 1: upload all received frame to system (Debug Mode) */ /* MAC IPG/IFG Control Register */ #define REG_MAC_IPG_IFG 0x1484 #define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back inter-packet gap. The default is 96-bit time */ #define MAC_IPG_IFG_IPGT_MASK 0x7f #define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to enforce in between RX frames */ #define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */ #define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */ #define MAC_IPG_IFG_IPGR1_MASK 0x7f #define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */ #define MAC_IPG_IFG_IPGR2_MASK 0x7f /* MAC STATION ADDRESS */ #define REG_MAC_STA_ADDR 0x1488 /* Hash table for multicast address */ #define REG_RX_HASH_TABLE 0x1490 /* MAC Half-Duplex Control Register */ #define REG_MAC_HALF_DUPLX_CTRL 0x1498 #define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */ #define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff #define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 /* Retransmission maximum, afterwards the packet will be discarded */ #define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf #define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 /* 1: Allow the transmission of a packet which has been excessively deferred */ #define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 /* 1: No back-off on collision, immediately start the retransmission */ #define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* 1: No back-off on backpressure, immediately start the transmission after back pressure */ #define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */ #define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */ #define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf #define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */ #define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */ /* Maximum Frame Length Control Register */ #define REG_MTU 0x149c /* Wake-On-Lan control register */ #define REG_WOL_CTRL 0x14a0 #define WOL_PATTERN_EN 0x00000001 #define WOL_PATTERN_PME_EN 0x00000002 #define WOL_MAGIC_EN 0x00000004 #define WOL_MAGIC_PME_EN 0x00000008 #define WOL_LINK_CHG_EN 0x00000010 #define WOL_LINK_CHG_PME_EN 0x00000020 #define WOL_PATTERN_ST 0x00000100 #define WOL_MAGIC_ST 0x00000200 #define WOL_LINKCHG_ST 0x00000400 #define WOL_CLK_SWITCH_EN 0x00008000 #define WOL_PT0_EN 0x00010000 #define WOL_PT1_EN 0x00020000 #define WOL_PT2_EN 0x00040000 #define WOL_PT3_EN 0x00080000 #define WOL_PT4_EN 0x00100000 #define WOL_PT5_EN 0x00200000 #define WOL_PT6_EN 0x00400000 /* WOL Length ( 2 DWORD ) */ #define REG_WOL_PATTERN_LEN 0x14a4 #define WOL_PT_LEN_MASK 0x7f #define WOL_PT0_LEN_SHIFT 0 #define WOL_PT1_LEN_SHIFT 8 #define WOL_PT2_LEN_SHIFT 16 #define WOL_PT3_LEN_SHIFT 24 #define WOL_PT4_LEN_SHIFT 0 #define WOL_PT5_LEN_SHIFT 8 #define WOL_PT6_LEN_SHIFT 16 /* Internal SRAM Partition Register */ #define REG_SRAM_TRD_ADDR 0x1518 #define REG_SRAM_TRD_LEN 0x151C #define REG_SRAM_RXF_ADDR 0x1520 #define REG_SRAM_RXF_LEN 0x1524 #define REG_SRAM_TXF_ADDR 0x1528 #define REG_SRAM_TXF_LEN 0x152C #define REG_SRAM_TCPH_ADDR 0x1530 #define REG_SRAM_PKTH_ADDR 0x1532 /* Load Ptr Register */ #define REG_LOAD_PTR 0x1534 /* Software sets this bit after the initialization of the head and tail */ /* * addresses of all descriptors, as well as the following descriptor * control register, which triggers each function block to load the head * pointer to prepare for the operation. This bit is then self-cleared * after one cycle. */ /* Descriptor Control register */ #define REG_RXF3_BASE_ADDR_HI 0x153C #define REG_DESC_BASE_ADDR_HI 0x1540 #define REG_RXF0_BASE_ADDR_HI 0x1540 /* share with DESC BASE ADDR HI */ #define REG_HOST_RXF0_PAGE0_LO 0x1544 #define REG_HOST_RXF0_PAGE1_LO 0x1548 #define REG_TPD_BASE_ADDR_LO 0x154C #define REG_RXF1_BASE_ADDR_HI 0x1550 #define REG_RXF2_BASE_ADDR_HI 0x1554 #define REG_HOST_RXFPAGE_SIZE 0x1558 #define REG_TPD_RING_SIZE 0x155C /* RSS about */ #define REG_RSS_KEY0 0x14B0 #define REG_RSS_KEY1 0x14B4 #define REG_RSS_KEY2 0x14B8 #define REG_RSS_KEY3 0x14BC #define REG_RSS_KEY4 0x14C0 #define REG_RSS_KEY5 0x14C4 #define REG_RSS_KEY6 0x14C8 #define REG_RSS_KEY7 0x14CC #define REG_RSS_KEY8 0x14D0 #define REG_RSS_KEY9 0x14D4 #define REG_IDT_TABLE4 0x14E0 #define REG_IDT_TABLE5 0x14E4 #define REG_IDT_TABLE6 0x14E8 #define REG_IDT_TABLE7 0x14EC #define REG_IDT_TABLE0 0x1560 #define REG_IDT_TABLE1 0x1564 #define REG_IDT_TABLE2 0x1568 #define REG_IDT_TABLE3 0x156C #define REG_IDT_TABLE REG_IDT_TABLE0 #define REG_RSS_HASH_VALUE 0x1570 #define REG_RSS_HASH_FLAG 0x1574 #define REG_BASE_CPU_NUMBER 0x157C /* TXQ Control Register */ #define REG_TXQ_CTRL 0x1580 #define TXQ_CTRL_NUM_TPD_BURST_MASK 0xF #define TXQ_CTRL_NUM_TPD_BURST_SHIFT 0 #define TXQ_CTRL_EN 0x20 /* 1: Enable TXQ */ #define TXQ_CTRL_ENH_MODE 0x40 /* Performance enhancement mode, in which up to two back-to-back DMA read commands might be dispatched. */ #define TXQ_CTRL_TXF_BURST_NUM_SHIFT 16 /* Number of data byte to read in a cache-aligned burst. Each SRAM entry is 8-byte in length. */ #define TXQ_CTRL_TXF_BURST_NUM_MASK 0xffff /* Jumbo packet Threshold for task offload */ #define REG_TX_EARLY_TH 0x1584 /* Jumbo frame threshold in QWORD unit. Packet greater than */ /* JUMBO_TASK_OFFLOAD_THRESHOLD will not be task offloaded. */ #define TX_TX_EARLY_TH_MASK 0x7ff #define TX_TX_EARLY_TH_SHIFT 0 /* RXQ Control Register */ #define REG_RXQ_CTRL 0x15A0 #define RXQ_CTRL_PBA_ALIGN_32 0 /* rx-packet alignment */ #define RXQ_CTRL_PBA_ALIGN_64 1 #define RXQ_CTRL_PBA_ALIGN_128 2 #define RXQ_CTRL_PBA_ALIGN_256 3 #define RXQ_CTRL_Q1_EN 0x10 #define RXQ_CTRL_Q2_EN 0x20 #define RXQ_CTRL_Q3_EN 0x40 #define RXQ_CTRL_IPV6_XSUM_VERIFY_EN 0x80 #define RXQ_CTRL_HASH_TLEN_SHIFT 8 #define RXQ_CTRL_HASH_TLEN_MASK 0xFF #define RXQ_CTRL_HASH_TYPE_IPV4 0x10000 #define RXQ_CTRL_HASH_TYPE_IPV4_TCP 0x20000 #define RXQ_CTRL_HASH_TYPE_IPV6 0x40000 #define RXQ_CTRL_HASH_TYPE_IPV6_TCP 0x80000 #define RXQ_CTRL_RSS_MODE_DISABLE 0 #define RXQ_CTRL_RSS_MODE_SQSINT 0x4000000 #define RXQ_CTRL_RSS_MODE_MQUESINT 0x8000000 #define RXQ_CTRL_RSS_MODE_MQUEMINT 0xC000000 #define RXQ_CTRL_NIP_QUEUE_SEL_TBL 0x10000000 #define RXQ_CTRL_HASH_ENABLE 0x20000000 #define RXQ_CTRL_CUT_THRU_EN 0x40000000 #define RXQ_CTRL_EN 0x80000000 /* Rx jumbo packet threshold and rrd retirement timer */ #define REG_RXQ_JMBOSZ_RRDTIM 0x15A4 /* * Jumbo packet threshold for non-VLAN packet, in QWORD (64-bit) unit. * When the packet length greater than or equal to this value, RXQ * shall start cut-through forwarding of the received packet. */ #define RXQ_JMBOSZ_TH_MASK 0x7ff #define RXQ_JMBOSZ_TH_SHIFT 0 /* RRD retirement timer. Decrement by 1 after every 512ns passes*/ #define RXQ_JMBO_LKAH_MASK 0xf #define RXQ_JMBO_LKAH_SHIFT 11 /* RXF flow control register */ #define REG_RXQ_RXF_PAUSE_THRESH 0x15A8 #define RXQ_RXF_PAUSE_TH_HI_SHIFT 0 #define RXQ_RXF_PAUSE_TH_HI_MASK 0xfff #define RXQ_RXF_PAUSE_TH_LO_SHIFT 16 #define RXQ_RXF_PAUSE_TH_LO_MASK 0xfff /* DMA Engine Control Register */ #define REG_DMA_CTRL 0x15C0 #define DMA_CTRL_DMAR_IN_ORDER 0x1 #define DMA_CTRL_DMAR_ENH_ORDER 0x2 #define DMA_CTRL_DMAR_OUT_ORDER 0x4 #define DMA_CTRL_RCB_VALUE 0x8 #define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4 #define DMA_CTRL_DMAR_BURST_LEN_MASK 7 #define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7 #define DMA_CTRL_DMAW_BURST_LEN_MASK 7 #define DMA_CTRL_DMAR_REQ_PRI 0x400 #define DMA_CTRL_DMAR_DLY_CNT_MASK 0x1F #define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11 #define DMA_CTRL_DMAW_DLY_CNT_MASK 0xF #define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16 #define DMA_CTRL_TXCMB_EN 0x100000 #define DMA_CTRL_RXCMB_EN 0x200000 /* CMB/SMB Control Register */ #define REG_SMB_STAT_TIMER 0x15C4 #define REG_TRIG_RRD_THRESH 0x15CA #define REG_TRIG_TPD_THRESH 0x15C8 #define REG_TRIG_TXTIMER 0x15CC #define REG_TRIG_RXTIMER 0x15CE /* HOST RXF Page 1,2,3 address */ #define REG_HOST_RXF1_PAGE0_LO 0x15D0 #define REG_HOST_RXF1_PAGE1_LO 0x15D4 #define REG_HOST_RXF2_PAGE0_LO 0x15D8 #define REG_HOST_RXF2_PAGE1_LO 0x15DC #define REG_HOST_RXF3_PAGE0_LO 0x15E0 #define REG_HOST_RXF3_PAGE1_LO 0x15E4 /* Mail box */ #define REG_MB_RXF1_RADDR 0x15B4 #define REG_MB_RXF2_RADDR 0x15B8 #define REG_MB_RXF3_RADDR 0x15BC #define REG_MB_TPD_PROD_IDX 0x15F0 /* RXF-Page 0-3 PageNo & Valid bit */ #define REG_HOST_RXF0_PAGE0_VLD 0x15F4 #define HOST_RXF_VALID 1 #define HOST_RXF_PAGENO_SHIFT 1 #define HOST_RXF_PAGENO_MASK 0x7F #define REG_HOST_RXF0_PAGE1_VLD 0x15F5 #define REG_HOST_RXF1_PAGE0_VLD 0x15F6 #define REG_HOST_RXF1_PAGE1_VLD 0x15F7 #define REG_HOST_RXF2_PAGE0_VLD 0x15F8 #define REG_HOST_RXF2_PAGE1_VLD 0x15F9 #define REG_HOST_RXF3_PAGE0_VLD 0x15FA #define REG_HOST_RXF3_PAGE1_VLD 0x15FB /* Interrupt Status Register */ #define REG_ISR 0x1600 #define ISR_SMB 1 #define ISR_TIMER 2 /* Interrupt when Timer is counted down to zero */ /* * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set * in Table 51 Selene Master Control Register (Offset 0x1400). */ #define ISR_MANUAL 4 #define ISR_HW_RXF_OV 8 /* RXF overflow interrupt */ #define ISR_HOST_RXF0_OV 0x10 #define ISR_HOST_RXF1_OV 0x20 #define ISR_HOST_RXF2_OV 0x40 #define ISR_HOST_RXF3_OV 0x80 #define ISR_TXF_UN 0x100 #define ISR_RX0_PAGE_FULL 0x200 #define ISR_DMAR_TO_RST 0x400 #define ISR_DMAW_TO_RST 0x800 #define ISR_GPHY 0x1000 #define ISR_TX_CREDIT 0x2000 #define ISR_GPHY_LPW 0x4000 /* GPHY low power state interrupt */ #define ISR_RX_PKT 0x10000 /* One packet received, triggered by RFD */ #define ISR_TX_PKT 0x20000 /* One packet transmitted, triggered by TPD */ #define ISR_TX_DMA 0x40000 #define ISR_RX_PKT_1 0x80000 #define ISR_RX_PKT_2 0x100000 #define ISR_RX_PKT_3 0x200000 #define ISR_MAC_RX 0x400000 #define ISR_MAC_TX 0x800000 #define ISR_UR_DETECTED 0x1000000 #define ISR_FERR_DETECTED 0x2000000 #define ISR_NFERR_DETECTED 0x4000000 #define ISR_CERR_DETECTED 0x8000000 #define ISR_PHY_LINKDOWN 0x10000000 #define ISR_DIS_INT 0x80000000 /* Interrupt Mask Register */ #define REG_IMR 0x1604 #define IMR_NORMAL_MASK (\ ISR_SMB |\ ISR_TXF_UN |\ ISR_HW_RXF_OV |\ ISR_HOST_RXF0_OV|\ ISR_MANUAL |\ ISR_GPHY |\ ISR_GPHY_LPW |\ ISR_DMAR_TO_RST |\ ISR_DMAW_TO_RST |\ ISR_PHY_LINKDOWN|\ ISR_RX_PKT |\ ISR_TX_PKT) #define ISR_TX_EVENT (ISR_TXF_UN | ISR_TX_PKT) #define ISR_RX_EVENT (ISR_HOST_RXF0_OV | ISR_HW_RXF_OV | ISR_RX_PKT) #define REG_MAC_RX_STATUS_BIN 0x1700 #define REG_MAC_RX_STATUS_END 0x175c #define REG_MAC_TX_STATUS_BIN 0x1760 #define REG_MAC_TX_STATUS_END 0x17c0 /* Hardware Offset Register */ #define REG_HOST_RXF0_PAGEOFF 0x1800 #define REG_TPD_CONS_IDX 0x1804 #define REG_HOST_RXF1_PAGEOFF 0x1808 #define REG_HOST_RXF2_PAGEOFF 0x180C #define REG_HOST_RXF3_PAGEOFF 0x1810 /* RXF-Page 0-3 Offset DMA Address */ #define REG_HOST_RXF0_MB0_LO 0x1820 #define REG_HOST_RXF0_MB1_LO 0x1824 #define REG_HOST_RXF1_MB0_LO 0x1828 #define REG_HOST_RXF1_MB1_LO 0x182C #define REG_HOST_RXF2_MB0_LO 0x1830 #define REG_HOST_RXF2_MB1_LO 0x1834 #define REG_HOST_RXF3_MB0_LO 0x1838 #define REG_HOST_RXF3_MB1_LO 0x183C /* Tpd CMB DMA Address */ #define REG_HOST_TX_CMB_LO 0x1840 #define REG_HOST_SMB_ADDR_LO 0x1844 /* DEBUG ADDR */ #define REG_DEBUG_DATA0 0x1900 #define REG_DEBUG_DATA1 0x1904 /***************************** MII definition ***************************************/ /* PHY Common Register */ #define MII_AT001_PSCR 0x10 #define MII_AT001_PSSR 0x11 #define MII_INT_CTRL 0x12 #define MII_INT_STATUS 0x13 #define MII_SMARTSPEED 0x14 #define MII_LBRERROR 0x18 #define MII_RESV2 0x1a #define MII_DBG_ADDR 0x1D #define MII_DBG_DATA 0x1E /* Autoneg Advertisement Register */ #define MII_AR_DEFAULT_CAP_MASK 0 /* 1000BASE-T Control Register */ #define MII_AT001_CR_1000T_SPEED_MASK \ (ADVERTISE_1000FULL | ADVERTISE_1000HALF) #define MII_AT001_CR_1000T_DEFAULT_CAP_MASK MII_AT001_CR_1000T_SPEED_MASK /* AT001 PHY Specific Control Register */ #define MII_AT001_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ #define MII_AT001_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ #define MII_AT001_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ #define MII_AT001_PSCR_MAC_POWERDOWN 0x0008 #define MII_AT001_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, * 0=CLK125 toggling */ #define MII_AT001_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ /* Manual MDI configuration */ #define MII_AT001_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ #define MII_AT001_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, * 100BASE-TX/10BASE-T: * MDI Mode */ #define MII_AT001_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled * all speeds. */ #define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE 0x0080 /* 1=Enable Extended 10BASE-T distance * (Lower 10BASE-T RX Threshold) * 0=Normal 10BASE-T RX Threshold */ #define MII_AT001_PSCR_MII_5BIT_ENABLE 0x0100 /* 1=5-Bit interface in 100BASE-TX * 0=MII interface in 100BASE-TX */ #define MII_AT001_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ #define MII_AT001_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ #define MII_AT001_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ #define MII_AT001_PSCR_POLARITY_REVERSAL_SHIFT 1 #define MII_AT001_PSCR_AUTO_X_MODE_SHIFT 5 #define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 /* AT001 PHY Specific Status Register */ #define MII_AT001_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ #define MII_AT001_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ #define MII_AT001_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ #define MII_AT001_PSSR_10MBS 0x0000 /* 00=10Mbs */ #define MII_AT001_PSSR_100MBS 0x4000 /* 01=100Mbs */ #define MII_AT001_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ #endif /*_ATHL1E_HW_H_*/ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/atl1e_hw.c0000644000175000017500000004011112026211315024762 0ustar mcgrofmcgrof/* * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "atl1e.h" /* * check_eeprom_exist * return 0 if eeprom exist */ int atl1e_check_eeprom_exist(struct atl1e_hw *hw) { u32 value; value = AT_READ_REG(hw, REG_SPI_FLASH_CTRL); if (value & SPI_FLASH_CTRL_EN_VPD) { value &= ~SPI_FLASH_CTRL_EN_VPD; AT_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); } value = AT_READ_REGW(hw, REG_PCIE_CAP_LIST); return ((value & 0xFF00) == 0x6C00) ? 0 : 1; } void atl1e_hw_set_mac_addr(struct atl1e_hw *hw) { u32 value; /* * 00-0B-6A-F6-00-DC * 0: 6AF600DC 1: 000B * low dword */ value = (((u32)hw->mac_addr[2]) << 24) | (((u32)hw->mac_addr[3]) << 16) | (((u32)hw->mac_addr[4]) << 8) | (((u32)hw->mac_addr[5])) ; AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); /* hight dword */ value = (((u32)hw->mac_addr[0]) << 8) | (((u32)hw->mac_addr[1])) ; AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); } /* * atl1e_get_permanent_address * return 0 if get valid mac address, */ static int atl1e_get_permanent_address(struct atl1e_hw *hw) { u32 addr[2]; u32 i; u32 twsi_ctrl_data; u8 eth_addr[ETH_ALEN]; if (is_valid_ether_addr(hw->perm_mac_addr)) return 0; /* init */ addr[0] = addr[1] = 0; if (!atl1e_check_eeprom_exist(hw)) { /* eeprom exist */ twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL); twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data); for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) { msleep(10); twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL); if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0) break; } if (i >= AT_TWSI_EEPROM_TIMEOUT) return AT_ERR_TIMEOUT; } /* maybe MAC-address is from BIOS */ addr[0] = AT_READ_REG(hw, REG_MAC_STA_ADDR); addr[1] = AT_READ_REG(hw, REG_MAC_STA_ADDR + 4); *(u32 *) ð_addr[2] = swab32(addr[0]); *(u16 *) ð_addr[0] = swab16(*(u16 *)&addr[1]); if (is_valid_ether_addr(eth_addr)) { memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); return 0; } return AT_ERR_EEPROM; } bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value) { return true; } bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value) { int i; u32 control; if (offset & 3) return false; /* address do not align */ AT_WRITE_REG(hw, REG_VPD_DATA, 0); control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; AT_WRITE_REG(hw, REG_VPD_CAP, control); for (i = 0; i < 10; i++) { msleep(2); control = AT_READ_REG(hw, REG_VPD_CAP); if (control & VPD_CAP_VPD_FLAG) break; } if (control & VPD_CAP_VPD_FLAG) { *p_value = AT_READ_REG(hw, REG_VPD_DATA); return true; } return false; /* timeout */ } void atl1e_force_ps(struct atl1e_hw *hw) { AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET); } /* * Reads the adapter's MAC address from the EEPROM * * hw - Struct containing variables accessed by shared code */ int atl1e_read_mac_addr(struct atl1e_hw *hw) { int err = 0; err = atl1e_get_permanent_address(hw); if (err) return AT_ERR_EEPROM; memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr)); return 0; } /* * atl1e_hash_mc_addr * purpose * set hash value for a multicast address */ u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr) { u32 crc32; u32 value = 0; int i; crc32 = ether_crc_le(6, mc_addr); for (i = 0; i < 32; i++) value |= (((crc32 >> i) & 1) << (31 - i)); return value; } /* * Sets the bit in the multicast table corresponding to the hash value. * hw - Struct containing variables accessed by shared code * hash_value - Multicast address hash value */ void atl1e_hash_set(struct atl1e_hw *hw, u32 hash_value) { u32 hash_bit, hash_reg; u32 mta; /* * The HASH Table is a register array of 2 32-bit registers. * It is treated like an array of 64 bits. We want to set * bit BitArray[hash_value]. So we figure out what register * the bit is in, read it, OR in the new bit, then write * back the new value. The register is determined by the * upper 7 bits of the hash value and the bit within that * register are determined by the lower 5 bits of the value. */ hash_reg = (hash_value >> 31) & 0x1; hash_bit = (hash_value >> 26) & 0x1F; mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); mta |= (1 << hash_bit); AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); } /* * Reads the value from a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to read */ int atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data) { u32 val; int i; val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; AT_WRITE_REG(hw, REG_MDIO_CTRL, val); wmb(); for (i = 0; i < MDIO_WAIT_TIMES; i++) { udelay(2); val = AT_READ_REG(hw, REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; wmb(); } if (!(val & (MDIO_START | MDIO_BUSY))) { *phy_data = (u16)val; return 0; } return AT_ERR_PHY; } /* * Writes a value to a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to write * data - data to write to the PHY */ int atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data) { int i; u32 val; val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | (reg_addr&MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | MDIO_SUP_PREAMBLE | MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; AT_WRITE_REG(hw, REG_MDIO_CTRL, val); wmb(); for (i = 0; i < MDIO_WAIT_TIMES; i++) { udelay(2); val = AT_READ_REG(hw, REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; wmb(); } if (!(val & (MDIO_START | MDIO_BUSY))) return 0; return AT_ERR_PHY; } /* * atl1e_init_pcie - init PCIE module */ static void atl1e_init_pcie(struct atl1e_hw *hw) { u32 value; /* comment 2lines below to save more power when sususpend value = LTSSM_TEST_MODE_DEF; AT_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value); */ /* pcie flow control mode change */ value = AT_READ_REG(hw, 0x1008); value |= 0x8000; AT_WRITE_REG(hw, 0x1008, value); } /* * Configures PHY autoneg and flow control advertisement settings * * hw - Struct containing variables accessed by shared code */ static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw) { s32 ret_val; u16 mii_autoneg_adv_reg; u16 mii_1000t_ctrl_reg; if (0 != hw->mii_autoneg_adv_reg) return 0; /* Read the MII Auto-Neg Advertisement Register (Address 4/9). */ mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; mii_1000t_ctrl_reg = MII_AT001_CR_1000T_DEFAULT_CAP_MASK; /* * Need to parse autoneg_advertised and set up * the appropriate PHY registers. First we will parse for * autoneg_advertised software override. Since we can advertise * a plethora of combinations, we need to check each bit * individually. */ /* * First we clear all the 10/100 mb speed bits in the Auto-Neg * Advertisement Register (Address 4) and the 1000 mb speed bits in * the 1000Base-T control Register (Address 9). */ mii_autoneg_adv_reg &= ~ADVERTISE_ALL; mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK; /* * Need to parse MediaType and setup the * appropriate PHY registers. */ switch (hw->media_type) { case MEDIA_TYPE_AUTO_SENSOR: mii_autoneg_adv_reg |= ADVERTISE_ALL; hw->autoneg_advertised = ADVERTISE_ALL; if (hw->nic_type == athr_l1e) { mii_1000t_ctrl_reg |= ADVERTISE_1000FULL; hw->autoneg_advertised |= ADVERTISE_1000_FULL; } break; case MEDIA_TYPE_100M_FULL: mii_autoneg_adv_reg |= ADVERTISE_100FULL; hw->autoneg_advertised = ADVERTISE_100_FULL; break; case MEDIA_TYPE_100M_HALF: mii_autoneg_adv_reg |= ADVERTISE_100_HALF; hw->autoneg_advertised = ADVERTISE_100_HALF; break; case MEDIA_TYPE_10M_FULL: mii_autoneg_adv_reg |= ADVERTISE_10_FULL; hw->autoneg_advertised = ADVERTISE_10_FULL; break; default: mii_autoneg_adv_reg |= ADVERTISE_10_HALF; hw->autoneg_advertised = ADVERTISE_10_HALF; break; } /* flow control fixed to enable all */ mii_autoneg_adv_reg |= (ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP); hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg; ret_val = atl1e_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); if (ret_val) return ret_val; if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) { ret_val = atl1e_write_phy_reg(hw, MII_CTRL1000, mii_1000t_ctrl_reg); if (ret_val) return ret_val; } return 0; } /* * Resets the PHY and make all config validate * * hw - Struct containing variables accessed by shared code * * Sets bit 15 and 12 of the MII control regiser (for F001 bug) */ int atl1e_phy_commit(struct atl1e_hw *hw) { struct atl1e_adapter *adapter = hw->adapter; int ret_val; u16 phy_data; phy_data = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART; ret_val = atl1e_write_phy_reg(hw, MII_BMCR, phy_data); if (ret_val) { u32 val; int i; /************************************** * pcie serdes link may be down ! **************************************/ for (i = 0; i < 25; i++) { msleep(1); val = AT_READ_REG(hw, REG_MDIO_CTRL); if (!(val & (MDIO_START | MDIO_BUSY))) break; } if (0 != (val & (MDIO_START | MDIO_BUSY))) { netdev_err(adapter->netdev, "pcie linkdown at least for 25ms\n"); return ret_val; } netdev_err(adapter->netdev, "pcie linkup after %d ms\n", i); } return 0; } int atl1e_phy_init(struct atl1e_hw *hw) { struct atl1e_adapter *adapter = hw->adapter; s32 ret_val; u16 phy_val; if (hw->phy_configured) { if (hw->re_autoneg) { hw->re_autoneg = false; return atl1e_restart_autoneg(hw); } return 0; } /* RESET GPHY Core */ AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT); msleep(2); AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT | GPHY_CTRL_EXT_RESET); msleep(2); /* patches */ /* p1. eable hibernation mode */ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0xB); if (ret_val) return ret_val; ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0xBC00); if (ret_val) return ret_val; /* p2. set Class A/B for all modes */ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0); if (ret_val) return ret_val; phy_val = 0x02ef; /* remove Class AB */ /* phy_val = hw->emi_ca ? 0x02ef : 0x02df; */ ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, phy_val); if (ret_val) return ret_val; /* p3. 10B ??? */ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x12); if (ret_val) return ret_val; ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x4C04); if (ret_val) return ret_val; /* p4. 1000T power */ ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x4); if (ret_val) return ret_val; ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x8BBB); if (ret_val) return ret_val; ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x5); if (ret_val) return ret_val; ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x2C46); if (ret_val) return ret_val; msleep(1); /*Enable PHY LinkChange Interrupt */ ret_val = atl1e_write_phy_reg(hw, MII_INT_CTRL, 0xC00); if (ret_val) { netdev_err(adapter->netdev, "Error enable PHY linkChange Interrupt\n"); return ret_val; } /* setup AutoNeg parameters */ ret_val = atl1e_phy_setup_autoneg_adv(hw); if (ret_val) { netdev_err(adapter->netdev, "Error Setting up Auto-Negotiation\n"); return ret_val; } /* SW.Reset & En-Auto-Neg to restart Auto-Neg*/ netdev_dbg(adapter->netdev, "Restarting Auto-Negotiation\n"); ret_val = atl1e_phy_commit(hw); if (ret_val) { netdev_err(adapter->netdev, "Error resetting the phy\n"); return ret_val; } hw->phy_configured = true; return 0; } /* * Reset the transmit and receive units; mask and clear all interrupts. * hw - Struct containing variables accessed by shared code * return : 0 or idle status (if error) */ int atl1e_reset_hw(struct atl1e_hw *hw) { struct atl1e_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; u32 idle_status_data = 0; u16 pci_cfg_cmd_word = 0; int timeout = 0; /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ pci_read_config_word(pdev, PCI_REG_COMMAND, &pci_cfg_cmd_word); if ((pci_cfg_cmd_word & (CMD_IO_SPACE | CMD_MEMORY_SPACE | CMD_BUS_MASTER)) != (CMD_IO_SPACE | CMD_MEMORY_SPACE | CMD_BUS_MASTER)) { pci_cfg_cmd_word |= (CMD_IO_SPACE | CMD_MEMORY_SPACE | CMD_BUS_MASTER); pci_write_config_word(pdev, PCI_REG_COMMAND, pci_cfg_cmd_word); } /* * Issue Soft Reset to the MAC. This will reset the chip's * transmit, receive, DMA. It will not effect * the current PCI configuration. The global reset bit is self- * clearing, and should clear within a microsecond. */ AT_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_LED_MODE | MASTER_CTRL_SOFT_RST); wmb(); msleep(1); /* Wait at least 10ms for All module to be Idle */ for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { idle_status_data = AT_READ_REG(hw, REG_IDLE_STATUS); if (idle_status_data == 0) break; msleep(1); cpu_relax(); } if (timeout >= AT_HW_MAX_IDLE_DELAY) { netdev_err(adapter->netdev, "MAC state machine can't be idle since disabled for 10ms second\n"); return AT_ERR_TIMEOUT; } return 0; } /* * Performs basic configuration of the adapter. * * hw - Struct containing variables accessed by shared code * Assumes that the controller has previously been reset and is in a * post-reset uninitialized state. Initializes multicast table, * and Calls routines to setup link * Leaves the transmit and receive units disabled and uninitialized. */ int atl1e_init_hw(struct atl1e_hw *hw) { s32 ret_val = 0; atl1e_init_pcie(hw); /* Zero out the Multicast HASH table */ /* clear the old settings from the multicast hash table */ AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); ret_val = atl1e_phy_init(hw); return ret_val; } /* * Detects the current speed and duplex settings of the hardware. * * hw - Struct containing variables accessed by shared code * speed - Speed of the connection * duplex - Duplex setting of the connection */ int atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex) { int err; u16 phy_data; /* Read PHY Specific Status Register (17) */ err = atl1e_read_phy_reg(hw, MII_AT001_PSSR, &phy_data); if (err) return err; if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED)) return AT_ERR_PHY_RES; switch (phy_data & MII_AT001_PSSR_SPEED) { case MII_AT001_PSSR_1000MBS: *speed = SPEED_1000; break; case MII_AT001_PSSR_100MBS: *speed = SPEED_100; break; case MII_AT001_PSSR_10MBS: *speed = SPEED_10; break; default: return AT_ERR_PHY_SPEED; break; } if (phy_data & MII_AT001_PSSR_DPLX) *duplex = FULL_DUPLEX; else *duplex = HALF_DUPLEX; return 0; } int atl1e_restart_autoneg(struct atl1e_hw *hw) { int err = 0; err = atl1e_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); if (err) return err; if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) { err = atl1e_write_phy_reg(hw, MII_CTRL1000, hw->mii_1000t_ctrl_reg); if (err) return err; } err = atl1e_write_phy_reg(hw, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART); return err; } compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/atl1e.h0000644000175000017500000004263412026211315024305 0ustar mcgrofmcgrof/* * Copyright(c) 2007 Atheros Corporation. All rights reserved. * Copyright(c) 2007 xiong huang * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _ATL1E_H_ #define _ATL1E_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atl1e_hw.h" #define PCI_REG_COMMAND 0x04 /* PCI Command Register */ #define CMD_IO_SPACE 0x0001 #define CMD_MEMORY_SPACE 0x0002 #define CMD_BUS_MASTER 0x0004 #define BAR_0 0 #define BAR_1 1 #define BAR_5 5 /* Wake Up Filter Control */ #define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ #define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ #define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ #define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ #define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ #define SPEED_0 0xffff #define HALF_DUPLEX 1 #define FULL_DUPLEX 2 /* Error Codes */ #define AT_ERR_EEPROM 1 #define AT_ERR_PHY 2 #define AT_ERR_CONFIG 3 #define AT_ERR_PARAM 4 #define AT_ERR_MAC_TYPE 5 #define AT_ERR_PHY_TYPE 6 #define AT_ERR_PHY_SPEED 7 #define AT_ERR_PHY_RES 8 #define AT_ERR_TIMEOUT 9 #define MAX_JUMBO_FRAME_SIZE 0x2000 #define AT_VLAN_TAG_TO_TPD_TAG(_vlan, _tpd) \ _tpd = (((_vlan) << (4)) | (((_vlan) >> 13) & 7) |\ (((_vlan) >> 9) & 8)) #define AT_TPD_TAG_TO_VLAN_TAG(_tpd, _vlan) \ _vlan = (((_tpd) >> 8) | (((_tpd) & 0x77) << 9) |\ (((_tdp) & 0x88) << 5)) #define AT_MAX_RECEIVE_QUEUE 4 #define AT_PAGE_NUM_PER_QUEUE 2 #define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL #define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL #define AT_TX_WATCHDOG (5 * HZ) #define AT_MAX_INT_WORK 10 #define AT_TWSI_EEPROM_TIMEOUT 100 #define AT_HW_MAX_IDLE_DELAY 10 #define AT_SUSPEND_LINK_TIMEOUT 28 #define AT_REGS_LEN 75 #define AT_EEPROM_LEN 512 #define AT_ADV_MASK (ADVERTISE_10_HALF |\ ADVERTISE_10_FULL |\ ADVERTISE_100_HALF |\ ADVERTISE_100_FULL |\ ADVERTISE_1000_FULL) /* tpd word 2 */ #define TPD_BUFLEN_MASK 0x3FFF #define TPD_BUFLEN_SHIFT 0 #define TPD_DMAINT_MASK 0x0001 #define TPD_DMAINT_SHIFT 14 #define TPD_PKTNT_MASK 0x0001 #define TPD_PKTINT_SHIFT 15 #define TPD_VLANTAG_MASK 0xFFFF #define TPD_VLAN_SHIFT 16 /* tpd word 3 bits 0:4 */ #define TPD_EOP_MASK 0x0001 #define TPD_EOP_SHIFT 0 #define TPD_IP_VERSION_MASK 0x0001 #define TPD_IP_VERSION_SHIFT 1 /* 0 : IPV4, 1 : IPV6 */ #define TPD_INS_VL_TAG_MASK 0x0001 #define TPD_INS_VL_TAG_SHIFT 2 #define TPD_CC_SEGMENT_EN_MASK 0x0001 #define TPD_CC_SEGMENT_EN_SHIFT 3 #define TPD_SEGMENT_EN_MASK 0x0001 #define TPD_SEGMENT_EN_SHIFT 4 /* tdp word 3 bits 5:7 if ip version is 0 */ #define TPD_IP_CSUM_MASK 0x0001 #define TPD_IP_CSUM_SHIFT 5 #define TPD_TCP_CSUM_MASK 0x0001 #define TPD_TCP_CSUM_SHIFT 6 #define TPD_UDP_CSUM_MASK 0x0001 #define TPD_UDP_CSUM_SHIFT 7 /* tdp word 3 bits 5:7 if ip version is 1 */ #define TPD_V6_IPHLLO_MASK 0x0007 #define TPD_V6_IPHLLO_SHIFT 7 /* tpd word 3 bits 8:9 bit */ #define TPD_VL_TAGGED_MASK 0x0001 #define TPD_VL_TAGGED_SHIFT 8 #define TPD_ETHTYPE_MASK 0x0001 #define TPD_ETHTYPE_SHIFT 9 /* tdp word 3 bits 10:13 if ip version is 0 */ #define TDP_V4_IPHL_MASK 0x000F #define TPD_V4_IPHL_SHIFT 10 /* tdp word 3 bits 10:13 if ip version is 1 */ #define TPD_V6_IPHLHI_MASK 0x000F #define TPD_V6_IPHLHI_SHIFT 10 /* tpd word 3 bit 14:31 if segment enabled */ #define TPD_TCPHDRLEN_MASK 0x000F #define TPD_TCPHDRLEN_SHIFT 14 #define TPD_HDRFLAG_MASK 0x0001 #define TPD_HDRFLAG_SHIFT 18 #define TPD_MSS_MASK 0x1FFF #define TPD_MSS_SHIFT 19 /* tdp word 3 bit 16:31 if custom csum enabled */ #define TPD_PLOADOFFSET_MASK 0x00FF #define TPD_PLOADOFFSET_SHIFT 16 #define TPD_CCSUMOFFSET_MASK 0x00FF #define TPD_CCSUMOFFSET_SHIFT 24 struct atl1e_tpd_desc { __le64 buffer_addr; __le32 word2; __le32 word3; }; /* how about 0x2000 */ #define MAX_TX_BUF_LEN 0x2000 #define MAX_TX_BUF_SHIFT 13 /*#define MAX_TX_BUF_LEN 0x3000 */ /* rrs word 1 bit 0:31 */ #define RRS_RX_CSUM_MASK 0xFFFF #define RRS_RX_CSUM_SHIFT 0 #define RRS_PKT_SIZE_MASK 0x3FFF #define RRS_PKT_SIZE_SHIFT 16 #define RRS_CPU_NUM_MASK 0x0003 #define RRS_CPU_NUM_SHIFT 30 #define RRS_IS_RSS_IPV4 0x0001 #define RRS_IS_RSS_IPV4_TCP 0x0002 #define RRS_IS_RSS_IPV6 0x0004 #define RRS_IS_RSS_IPV6_TCP 0x0008 #define RRS_IS_IPV6 0x0010 #define RRS_IS_IP_FRAG 0x0020 #define RRS_IS_IP_DF 0x0040 #define RRS_IS_802_3 0x0080 #define RRS_IS_VLAN_TAG 0x0100 #define RRS_IS_ERR_FRAME 0x0200 #define RRS_IS_IPV4 0x0400 #define RRS_IS_UDP 0x0800 #define RRS_IS_TCP 0x1000 #define RRS_IS_BCAST 0x2000 #define RRS_IS_MCAST 0x4000 #define RRS_IS_PAUSE 0x8000 #define RRS_ERR_BAD_CRC 0x0001 #define RRS_ERR_CODE 0x0002 #define RRS_ERR_DRIBBLE 0x0004 #define RRS_ERR_RUNT 0x0008 #define RRS_ERR_RX_OVERFLOW 0x0010 #define RRS_ERR_TRUNC 0x0020 #define RRS_ERR_IP_CSUM 0x0040 #define RRS_ERR_L4_CSUM 0x0080 #define RRS_ERR_LENGTH 0x0100 #define RRS_ERR_DES_ADDR 0x0200 struct atl1e_recv_ret_status { u16 seq_num; u16 hash_lo; __le32 word1; u16 pkt_flag; u16 err_flag; u16 hash_hi; u16 vtag; }; enum atl1e_dma_req_block { atl1e_dma_req_128 = 0, atl1e_dma_req_256 = 1, atl1e_dma_req_512 = 2, atl1e_dma_req_1024 = 3, atl1e_dma_req_2048 = 4, atl1e_dma_req_4096 = 5 }; enum atl1e_rrs_type { atl1e_rrs_disable = 0, atl1e_rrs_ipv4 = 1, atl1e_rrs_ipv4_tcp = 2, atl1e_rrs_ipv6 = 4, atl1e_rrs_ipv6_tcp = 8 }; enum atl1e_nic_type { athr_l1e = 0, athr_l2e_revA = 1, athr_l2e_revB = 2 }; struct atl1e_hw_stats { /* rx */ unsigned long rx_ok; /* The number of good packet received. */ unsigned long rx_bcast; /* The number of good broadcast packet received. */ unsigned long rx_mcast; /* The number of good multicast packet received. */ unsigned long rx_pause; /* The number of Pause packet received. */ unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */ unsigned long rx_fcs_err; /* The number of packets with bad FCS. */ unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */ unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */ unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */ unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */ unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */ unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */ unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */ unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */ unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */ unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */ unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */ unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */ unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */ unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */ unsigned long rx_align_err; /* Alignment Error */ unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */ unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */ unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */ /* tx */ unsigned long tx_ok; /* The number of good packet transmitted. */ unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */ unsigned long tx_mcast; /* The number of good multicast packet transmitted. */ unsigned long tx_pause; /* The number of Pause packet transmitted. */ unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */ unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */ unsigned long tx_defer; /* The number of packets transmitted that is deferred. */ unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */ unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */ unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */ unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */ unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */ unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */ unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */ unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */ unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */ unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */ unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */ unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */ unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */ unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */ unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */ unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */ unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */ unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */ }; struct atl1e_hw { u8 __iomem *hw_addr; /* inner register address */ resource_size_t mem_rang; struct atl1e_adapter *adapter; enum atl1e_nic_type nic_type; u16 device_id; u16 vendor_id; u16 subsystem_id; u16 subsystem_vendor_id; u8 revision_id; u16 pci_cmd_word; u8 mac_addr[ETH_ALEN]; u8 perm_mac_addr[ETH_ALEN]; u8 preamble_len; u16 max_frame_size; u16 rx_jumbo_th; u16 tx_jumbo_th; u16 media_type; #define MEDIA_TYPE_AUTO_SENSOR 0 #define MEDIA_TYPE_100M_FULL 1 #define MEDIA_TYPE_100M_HALF 2 #define MEDIA_TYPE_10M_FULL 3 #define MEDIA_TYPE_10M_HALF 4 u16 autoneg_advertised; #define ADVERTISE_10_HALF 0x0001 #define ADVERTISE_10_FULL 0x0002 #define ADVERTISE_100_HALF 0x0004 #define ADVERTISE_100_FULL 0x0008 #define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */ #define ADVERTISE_1000_FULL 0x0020 u16 mii_autoneg_adv_reg; u16 mii_1000t_ctrl_reg; u16 imt; /* Interrupt Moderator timer ( 2us resolution) */ u16 ict; /* Interrupt Clear timer (2us resolution) */ u32 smb_timer; u16 rrd_thresh; /* Threshold of number of RRD produced to trigger interrupt request */ u16 tpd_thresh; u16 rx_count_down; /* 2us resolution */ u16 tx_count_down; u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */ enum atl1e_rrs_type rrs_type; u32 base_cpu; u32 indirect_tab; enum atl1e_dma_req_block dmar_block; enum atl1e_dma_req_block dmaw_block; u8 dmaw_dly_cnt; u8 dmar_dly_cnt; bool phy_configured; bool re_autoneg; bool emi_ca; }; /* * wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct atl1e_tx_buffer { struct sk_buff *skb; u16 flags; #define ATL1E_TX_PCIMAP_SINGLE 0x0001 #define ATL1E_TX_PCIMAP_PAGE 0x0002 #define ATL1E_TX_PCIMAP_TYPE_MASK 0x0003 u16 length; dma_addr_t dma; }; #define ATL1E_SET_PCIMAP_TYPE(tx_buff, type) do { \ ((tx_buff)->flags) &= ~ATL1E_TX_PCIMAP_TYPE_MASK; \ ((tx_buff)->flags) |= (type); \ } while (0) struct atl1e_rx_page { dma_addr_t dma; /* receive rage DMA address */ u8 *addr; /* receive rage virtual address */ dma_addr_t write_offset_dma; /* the DMA address which contain the receive data offset in the page */ u32 *write_offset_addr; /* the virtaul address which contain the receive data offset in the page */ u32 read_offset; /* the offset where we have read */ }; struct atl1e_rx_page_desc { struct atl1e_rx_page rx_page[AT_PAGE_NUM_PER_QUEUE]; u8 rx_using; u16 rx_nxseq; }; /* transmit packet descriptor (tpd) ring */ struct atl1e_tx_ring { struct atl1e_tpd_desc *desc; /* descriptor ring virtual address */ dma_addr_t dma; /* descriptor ring physical address */ u16 count; /* the count of transmit rings */ rwlock_t tx_lock; u16 next_to_use; atomic_t next_to_clean; struct atl1e_tx_buffer *tx_buffer; dma_addr_t cmb_dma; u32 *cmb; }; /* receive packet descriptor ring */ struct atl1e_rx_ring { void *desc; dma_addr_t dma; int size; u32 page_size; /* bytes length of rxf page */ u32 real_page_size; /* real_page_size = page_size + jumbo + aliagn */ struct atl1e_rx_page_desc rx_page_desc[AT_MAX_RECEIVE_QUEUE]; }; /* board specific private data structure */ struct atl1e_adapter { struct net_device *netdev; struct pci_dev *pdev; struct napi_struct napi; struct mii_if_info mii; /* MII interface info */ struct atl1e_hw hw; struct atl1e_hw_stats hw_stats; bool have_msi; u32 wol; u16 link_speed; u16 link_duplex; spinlock_t mdio_lock; spinlock_t tx_lock; atomic_t irq_sem; struct work_struct reset_task; struct work_struct link_chg_task; struct timer_list watchdog_timer; struct timer_list phy_config_timer; /* All Descriptor memory */ dma_addr_t ring_dma; void *ring_vir_addr; u32 ring_size; struct atl1e_tx_ring tx_ring; struct atl1e_rx_ring rx_ring; int num_rx_queues; unsigned long flags; #define __AT_TESTING 0x0001 #define __AT_RESETTING 0x0002 #define __AT_DOWN 0x0003 u32 bd_number; /* board number;*/ u32 pci_state[16]; u32 *config_space; }; #define AT_WRITE_REG(a, reg, value) ( \ writel((value), ((a)->hw_addr + reg))) #define AT_WRITE_FLUSH(a) (\ readl((a)->hw_addr)) #define AT_READ_REG(a, reg) ( \ readl((a)->hw_addr + reg)) #define AT_WRITE_REGB(a, reg, value) (\ writeb((value), ((a)->hw_addr + reg))) #define AT_READ_REGB(a, reg) (\ readb((a)->hw_addr + reg)) #define AT_WRITE_REGW(a, reg, value) (\ writew((value), ((a)->hw_addr + reg))) #define AT_READ_REGW(a, reg) (\ readw((a)->hw_addr + reg)) #define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \ writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))) #define AT_READ_REG_ARRAY(a, reg, offset) ( \ readl(((a)->hw_addr + reg) + ((offset) << 2))) extern char atl1e_driver_name[]; extern char atl1e_driver_version[]; extern void atl1e_check_options(struct atl1e_adapter *adapter); extern int atl1e_up(struct atl1e_adapter *adapter); extern void atl1e_down(struct atl1e_adapter *adapter); extern void atl1e_reinit_locked(struct atl1e_adapter *adapter); extern s32 atl1e_reset_hw(struct atl1e_hw *hw); extern void atl1e_set_ethtool_ops(struct net_device *netdev); #endif /* _ATL1_E_H_ */ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1e/Makefile0000644000175000017500000000014412026211315024554 0ustar mcgrofmcgrofobj-$(CONFIG_ATL1E) += atl1e.o atl1e-objs += atl1e_main.o atl1e_hw.o atl1e_ethtool.o atl1e_param.o compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1c/0000755000175000017500000000000012026211315023113 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1c/atl1c_main.c0000644000175000017500000023220412026211315025272 0ustar mcgrofmcgrof/* * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "atl1c.h" #define ATL1C_DRV_VERSION "1.0.1.0-NAPI" char atl1c_driver_name[] = "atl1c"; char atl1c_driver_version[] = ATL1C_DRV_VERSION; /* * atl1c_pci_tbl - PCI Device ID Table * * Wildcard entries (PCI_ANY_ID) should come last * Last entry must be all 0s * * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, * Class, Class Mask, private data (not used) } */ static DEFINE_PCI_DEVICE_TABLE(atl1c_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1C)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L2C)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B2)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D_2_0)}, /* required last entry */ { 0 } }; MODULE_DEVICE_TABLE(pci, atl1c_pci_tbl); MODULE_AUTHOR("Jie Yang"); MODULE_AUTHOR("Qualcomm Atheros Inc., "); MODULE_DESCRIPTION("Qualcom Atheros 100/1000M Ethernet Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(ATL1C_DRV_VERSION); static int atl1c_stop_mac(struct atl1c_hw *hw); static void atl1c_disable_l0s_l1(struct atl1c_hw *hw); static void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed); static void atl1c_start_mac(struct atl1c_adapter *adapter); static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, int *work_done, int work_to_do); static int atl1c_up(struct atl1c_adapter *adapter); static void atl1c_down(struct atl1c_adapter *adapter); static int atl1c_reset_mac(struct atl1c_hw *hw); static void atl1c_reset_dma_ring(struct atl1c_adapter *adapter); static int atl1c_configure(struct atl1c_adapter *adapter); static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter); static const u16 atl1c_pay_load_size[] = { 128, 256, 512, 1024, 2048, 4096, }; static const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP; static void atl1c_pcie_patch(struct atl1c_hw *hw) { u32 mst_data, data; /* pclk sel could switch to 25M */ AT_READ_REG(hw, REG_MASTER_CTRL, &mst_data); mst_data &= ~MASTER_CTRL_CLK_SEL_DIS; AT_WRITE_REG(hw, REG_MASTER_CTRL, mst_data); /* WoL/PCIE related settings */ if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) { AT_READ_REG(hw, REG_PCIE_PHYMISC, &data); data |= PCIE_PHYMISC_FORCE_RCV_DET; AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data); } else { /* new dev set bit5 of MASTER */ if (!(mst_data & MASTER_CTRL_WAKEN_25M)) AT_WRITE_REG(hw, REG_MASTER_CTRL, mst_data | MASTER_CTRL_WAKEN_25M); } /* aspm/PCIE setting only for l2cb 1.0 */ if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10) { AT_READ_REG(hw, REG_PCIE_PHYMISC2, &data); data = FIELD_SETX(data, PCIE_PHYMISC2_CDR_BW, L2CB1_PCIE_PHYMISC2_CDR_BW); data = FIELD_SETX(data, PCIE_PHYMISC2_L0S_TH, L2CB1_PCIE_PHYMISC2_L0S_TH); AT_WRITE_REG(hw, REG_PCIE_PHYMISC2, data); /* extend L1 sync timer */ AT_READ_REG(hw, REG_LINK_CTRL, &data); data |= LINK_CTRL_EXT_SYNC; AT_WRITE_REG(hw, REG_LINK_CTRL, data); } /* l2cb 1.x & l1d 1.x */ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d) { AT_READ_REG(hw, REG_PM_CTRL, &data); data |= PM_CTRL_L0S_BUFSRX_EN; AT_WRITE_REG(hw, REG_PM_CTRL, data); /* clear vendor msg */ AT_READ_REG(hw, REG_DMA_DBG, &data); AT_WRITE_REG(hw, REG_DMA_DBG, data & ~DMA_DBG_VENDOR_MSG); } } /* FIXME: no need any more ? */ /* * atl1c_init_pcie - init PCIE module */ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag) { u32 data; u32 pci_cmd; struct pci_dev *pdev = hw->adapter->pdev; int pos; AT_READ_REG(hw, PCI_COMMAND, &pci_cmd); pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; pci_cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_IO); AT_WRITE_REG(hw, PCI_COMMAND, pci_cmd); /* * Clear any PowerSaveing Settings */ pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); /* wol sts read-clear */ AT_READ_REG(hw, REG_WOL_CTRL, &data); AT_WRITE_REG(hw, REG_WOL_CTRL, 0); /* * Mask some pcie error bits */ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, &data); data &= ~(PCI_ERR_UNC_DLP | PCI_ERR_UNC_FCP); pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, data); /* clear error status */ pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_NFED | PCI_EXP_DEVSTA_FED | PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_URD); AT_READ_REG(hw, REG_LTSSM_ID_CTRL, &data); data &= ~LTSSM_ID_EN_WRO; AT_WRITE_REG(hw, REG_LTSSM_ID_CTRL, data); atl1c_pcie_patch(hw); if (flag & ATL1C_PCIE_L0S_L1_DISABLE) atl1c_disable_l0s_l1(hw); msleep(5); } /** * atl1c_irq_enable - Enable default interrupt generation settings * @adapter: board private structure */ static inline void atl1c_irq_enable(struct atl1c_adapter *adapter) { if (likely(atomic_dec_and_test(&adapter->irq_sem))) { AT_WRITE_REG(&adapter->hw, REG_ISR, 0x7FFFFFFF); AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask); AT_WRITE_FLUSH(&adapter->hw); } } /** * atl1c_irq_disable - Mask off interrupt generation on the NIC * @adapter: board private structure */ static inline void atl1c_irq_disable(struct atl1c_adapter *adapter) { atomic_inc(&adapter->irq_sem); AT_WRITE_REG(&adapter->hw, REG_IMR, 0); AT_WRITE_REG(&adapter->hw, REG_ISR, ISR_DIS_INT); AT_WRITE_FLUSH(&adapter->hw); synchronize_irq(adapter->pdev->irq); } /** * atl1c_irq_reset - reset interrupt confiure on the NIC * @adapter: board private structure */ static inline void atl1c_irq_reset(struct atl1c_adapter *adapter) { atomic_set(&adapter->irq_sem, 1); atl1c_irq_enable(adapter); } /* * atl1c_wait_until_idle - wait up to AT_HW_MAX_IDLE_DELAY reads * of the idle status register until the device is actually idle */ static u32 atl1c_wait_until_idle(struct atl1c_hw *hw, u32 modu_ctrl) { int timeout; u32 data; for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { AT_READ_REG(hw, REG_IDLE_STATUS, &data); if ((data & modu_ctrl) == 0) return 0; msleep(1); } return data; } /** * atl1c_phy_config - Timer Call-back * @data: pointer to netdev cast into an unsigned long */ static void atl1c_phy_config(unsigned long data) { struct atl1c_adapter *adapter = (struct atl1c_adapter *) data; struct atl1c_hw *hw = &adapter->hw; unsigned long flags; spin_lock_irqsave(&adapter->mdio_lock, flags); atl1c_restart_autoneg(hw); spin_unlock_irqrestore(&adapter->mdio_lock, flags); } void atl1c_reinit_locked(struct atl1c_adapter *adapter) { WARN_ON(in_interrupt()); atl1c_down(adapter); atl1c_up(adapter); clear_bit(__AT_RESETTING, &adapter->flags); } static void atl1c_check_link_status(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; int err; unsigned long flags; u16 speed, duplex, phy_data; spin_lock_irqsave(&adapter->mdio_lock, flags); /* MII_BMSR must read twise */ atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); spin_unlock_irqrestore(&adapter->mdio_lock, flags); if ((phy_data & BMSR_LSTATUS) == 0) { /* link down */ netif_carrier_off(netdev); hw->hibernate = true; if (atl1c_reset_mac(hw) != 0) if (netif_msg_hw(adapter)) dev_warn(&pdev->dev, "reset mac failed\n"); atl1c_set_aspm(hw, SPEED_0); atl1c_post_phy_linkchg(hw, SPEED_0); atl1c_reset_dma_ring(adapter); atl1c_configure(adapter); } else { /* Link Up */ hw->hibernate = false; spin_lock_irqsave(&adapter->mdio_lock, flags); err = atl1c_get_speed_and_duplex(hw, &speed, &duplex); spin_unlock_irqrestore(&adapter->mdio_lock, flags); if (unlikely(err)) return; /* link result is our setting */ if (adapter->link_speed != speed || adapter->link_duplex != duplex) { adapter->link_speed = speed; adapter->link_duplex = duplex; atl1c_set_aspm(hw, speed); atl1c_post_phy_linkchg(hw, speed); atl1c_start_mac(adapter); if (netif_msg_link(adapter)) dev_info(&pdev->dev, "%s: %s NIC Link is Up<%d Mbps %s>\n", atl1c_driver_name, netdev->name, adapter->link_speed, adapter->link_duplex == FULL_DUPLEX ? "Full Duplex" : "Half Duplex"); } if (!netif_carrier_ok(netdev)) netif_carrier_on(netdev); } } static void atl1c_link_chg_event(struct atl1c_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; u16 phy_data; u16 link_up; spin_lock(&adapter->mdio_lock); atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); spin_unlock(&adapter->mdio_lock); link_up = phy_data & BMSR_LSTATUS; /* notify upper layer link down ASAP */ if (!link_up) { if (netif_carrier_ok(netdev)) { /* old link state: Up */ netif_carrier_off(netdev); if (netif_msg_link(adapter)) dev_info(&pdev->dev, "%s: %s NIC Link is Down\n", atl1c_driver_name, netdev->name); adapter->link_speed = SPEED_0; } } set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event); schedule_work(&adapter->common_task); } static void atl1c_common_task(struct work_struct *work) { struct atl1c_adapter *adapter; struct net_device *netdev; adapter = container_of(work, struct atl1c_adapter, common_task); netdev = adapter->netdev; if (test_bit(__AT_DOWN, &adapter->flags)) return; if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) { netif_device_detach(netdev); atl1c_down(adapter); atl1c_up(adapter); netif_device_attach(netdev); } if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event)) { atl1c_irq_disable(adapter); atl1c_check_link_status(adapter); atl1c_irq_enable(adapter); } } static void atl1c_del_timer(struct atl1c_adapter *adapter) { del_timer_sync(&adapter->phy_config_timer); } /** * atl1c_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure */ static void atl1c_tx_timeout(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event); schedule_work(&adapter->common_task); } /** * atl1c_set_multi - Multicast and Promiscuous mode set * @netdev: network interface device structure * * The set_multi entry point is called whenever the multicast address * list or the network interface flags are updated. This routine is * responsible for configuring the hardware for proper multicast, * promiscuous mode, and all-multi behavior. */ static void atl1c_set_multi(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; struct netdev_hw_addr *ha; u32 mac_ctrl_data; u32 hash_value; /* Check for Promiscuous and All Multicast modes */ AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data); if (netdev->flags & IFF_PROMISC) { mac_ctrl_data |= MAC_CTRL_PROMIS_EN; } else if (netdev->flags & IFF_ALLMULTI) { mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN; } else { mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); } AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); /* clear the old settings from the multicast hash table */ AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atl1c_hash_mc_addr(hw, ha->addr); #else hash_value = atl1c_hash_mc_addr(hw, ha->dmi_addr); #endif atl1c_hash_set(hw, hash_value); } } static void __atl1c_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data) { if (features & NETIF_F_HW_VLAN_RX) { /* enable VLAN tag insert/strip */ *mac_ctrl_data |= MAC_CTRL_RMV_VLAN; } else { /* disable VLAN tag insert/strip */ *mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN; } } static void atl1c_vlan_mode(struct net_device *netdev, netdev_features_t features) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct pci_dev *pdev = adapter->pdev; u32 mac_ctrl_data = 0; if (netif_msg_pktdata(adapter)) dev_dbg(&pdev->dev, "atl1c_vlan_mode\n"); atl1c_irq_disable(adapter); AT_READ_REG(&adapter->hw, REG_MAC_CTRL, &mac_ctrl_data); __atl1c_vlan_mode(features, &mac_ctrl_data); AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); atl1c_irq_enable(adapter); } static void atl1c_restore_vlan(struct atl1c_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; if (netif_msg_pktdata(adapter)) dev_dbg(&pdev->dev, "atl1c_restore_vlan\n"); atl1c_vlan_mode(adapter->netdev, adapter->netdev->features); } /** * atl1c_set_mac - Change the Ethernet Address of the NIC * @netdev: network interface device structure * @p: pointer to an address structure * * Returns 0 on success, negative on failure */ static int atl1c_set_mac_addr(struct net_device *netdev, void *p) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; if (netif_running(netdev)) return -EBUSY; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type &= ~NET_ADDR_RANDOM; #endif atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr); return 0; } static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter, struct net_device *dev) { int mtu = dev->mtu; adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ? roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atl1c_fix_features(struct net_device *netdev, netdev_features_t features) { /* * Since there is no support for separate rx/tx vlan accel * enable/disable make sure tx flag is always in same state as rx. */ if (features & NETIF_F_HW_VLAN_RX) features |= NETIF_F_HW_VLAN_TX; else features &= ~NETIF_F_HW_VLAN_TX; if (netdev->mtu > MAX_TSO_FRAME_SIZE) features &= ~(NETIF_F_TSO | NETIF_F_TSO6); return features; } static int atl1c_set_features(struct net_device *netdev, netdev_features_t features) { netdev_features_t changed = netdev->features ^ features; if (changed & NETIF_F_HW_VLAN_RX) atl1c_vlan_mode(netdev, features); return 0; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /** * atl1c_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure * @new_mtu: new value for maximum frame size * * Returns 0 on success, negative on failure */ static int atl1c_change_mtu(struct net_device *netdev, int new_mtu) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; int old_mtu = netdev->mtu; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; /* Fast Ethernet controller doesn't support jumbo packet */ if (((hw->nic_type == athr_l2c || hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) && new_mtu > ETH_DATA_LEN) || max_frame < ETH_ZLEN + ETH_FCS_LEN || max_frame > MAX_JUMBO_FRAME_SIZE) { if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); return -EINVAL; } /* set MTU */ if (old_mtu != new_mtu && netif_running(netdev)) { while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); netdev->mtu = new_mtu; adapter->hw.max_frame_size = new_mtu; atl1c_set_rxbufsize(adapter, netdev); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) if (new_mtu > MAX_TSO_FRAME_SIZE) { adapter->netdev->features &= ~NETIF_F_TSO; adapter->netdev->features &= ~NETIF_F_TSO6; } else { adapter->netdev->features |= NETIF_F_TSO; adapter->netdev->features |= NETIF_F_TSO6; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ atl1c_down(adapter); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev_update_features(netdev); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ atl1c_up(adapter); clear_bit(__AT_RESETTING, &adapter->flags); } return 0; } /* * caller should hold mdio_lock */ static int atl1c_mdio_read(struct net_device *netdev, int phy_id, int reg_num) { struct atl1c_adapter *adapter = netdev_priv(netdev); u16 result; atl1c_read_phy_reg(&adapter->hw, reg_num, &result); return result; } static void atl1c_mdio_write(struct net_device *netdev, int phy_id, int reg_num, int val) { struct atl1c_adapter *adapter = netdev_priv(netdev); atl1c_write_phy_reg(&adapter->hw, reg_num, val); } static int atl1c_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct pci_dev *pdev = adapter->pdev; struct mii_ioctl_data *data = if_mii(ifr); unsigned long flags; int retval = 0; if (!netif_running(netdev)) return -EINVAL; spin_lock_irqsave(&adapter->mdio_lock, flags); switch (cmd) { case SIOCGMIIPHY: data->phy_id = 0; break; case SIOCGMIIREG: if (atl1c_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, &data->val_out)) { retval = -EIO; goto out; } break; case SIOCSMIIREG: if (data->reg_num & ~(0x1F)) { retval = -EFAULT; goto out; } dev_dbg(&pdev->dev, " write %x %x", data->reg_num, data->val_in); if (atl1c_write_phy_reg(&adapter->hw, data->reg_num, data->val_in)) { retval = -EIO; goto out; } break; default: retval = -EOPNOTSUPP; break; } out: spin_unlock_irqrestore(&adapter->mdio_lock, flags); return retval; } static int atl1c_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { switch (cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: return atl1c_mii_ioctl(netdev, ifr, cmd); default: return -EOPNOTSUPP; } } /** * atl1c_alloc_queues - Allocate memory for all rings * @adapter: board private structure to initialize * */ static int __devinit atl1c_alloc_queues(struct atl1c_adapter *adapter) { return 0; } static void atl1c_set_mac_type(struct atl1c_hw *hw) { switch (hw->device_id) { case PCI_DEVICE_ID_ATTANSIC_L2C: hw->nic_type = athr_l2c; break; case PCI_DEVICE_ID_ATTANSIC_L1C: hw->nic_type = athr_l1c; break; case PCI_DEVICE_ID_ATHEROS_L2C_B: hw->nic_type = athr_l2c_b; break; case PCI_DEVICE_ID_ATHEROS_L2C_B2: hw->nic_type = athr_l2c_b2; break; case PCI_DEVICE_ID_ATHEROS_L1D: hw->nic_type = athr_l1d; break; case PCI_DEVICE_ID_ATHEROS_L1D_2_0: hw->nic_type = athr_l1d_2; break; default: break; } } static int atl1c_setup_mac_funcs(struct atl1c_hw *hw) { u32 link_ctrl_data; atl1c_set_mac_type(hw); AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data); hw->ctrl_flags = ATL1C_INTR_MODRT_ENABLE | ATL1C_TXQ_MODE_ENHANCE; hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT | ATL1C_ASPM_L1_SUPPORT; hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON; if (hw->nic_type == athr_l1c || hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2) hw->link_cap_flags |= ATL1C_LINK_CAP_1000M; return 0; } struct atl1c_platform_patch { u16 pci_did; u8 pci_revid; u16 subsystem_vid; u16 subsystem_did; u32 patch_flag; #define ATL1C_LINK_PATCH 0x1 }; static const struct atl1c_platform_patch plats[] __devinitdata = { {0x2060, 0xC1, 0x1019, 0x8152, 0x1}, {0x2060, 0xC1, 0x1019, 0x2060, 0x1}, {0x2060, 0xC1, 0x1019, 0xE000, 0x1}, {0x2062, 0xC0, 0x1019, 0x8152, 0x1}, {0x2062, 0xC0, 0x1019, 0x2062, 0x1}, {0x2062, 0xC0, 0x1458, 0xE000, 0x1}, {0x2062, 0xC1, 0x1019, 0x8152, 0x1}, {0x2062, 0xC1, 0x1019, 0x2062, 0x1}, {0x2062, 0xC1, 0x1458, 0xE000, 0x1}, {0x2062, 0xC1, 0x1565, 0x2802, 0x1}, {0x2062, 0xC1, 0x1565, 0x2801, 0x1}, {0x1073, 0xC0, 0x1019, 0x8151, 0x1}, {0x1073, 0xC0, 0x1019, 0x1073, 0x1}, {0x1073, 0xC0, 0x1458, 0xE000, 0x1}, {0x1083, 0xC0, 0x1458, 0xE000, 0x1}, {0x1083, 0xC0, 0x1019, 0x8151, 0x1}, {0x1083, 0xC0, 0x1019, 0x1083, 0x1}, {0x1083, 0xC0, 0x1462, 0x7680, 0x1}, {0x1083, 0xC0, 0x1565, 0x2803, 0x1}, {0}, }; static void __devinit atl1c_patch_assign(struct atl1c_hw *hw) { struct pci_dev *pdev = hw->adapter->pdev; u32 misc_ctrl; int i = 0; hw->msi_lnkpatch = false; while (plats[i].pci_did != 0) { if (plats[i].pci_did == hw->device_id && plats[i].pci_revid == hw->revision_id && plats[i].subsystem_vid == hw->subsystem_vendor_id && plats[i].subsystem_did == hw->subsystem_id) { if (plats[i].patch_flag & ATL1C_LINK_PATCH) hw->msi_lnkpatch = true; } i++; } if (hw->device_id == PCI_DEVICE_ID_ATHEROS_L2C_B2 && hw->revision_id == L2CB_V21) { /* config acess mode */ pci_write_config_dword(pdev, REG_PCIE_IND_ACC_ADDR, REG_PCIE_DEV_MISC_CTRL); pci_read_config_dword(pdev, REG_PCIE_IND_ACC_DATA, &misc_ctrl); misc_ctrl &= ~0x100; pci_write_config_dword(pdev, REG_PCIE_IND_ACC_ADDR, REG_PCIE_DEV_MISC_CTRL); pci_write_config_dword(pdev, REG_PCIE_IND_ACC_DATA, misc_ctrl); } } /** * atl1c_sw_init - Initialize general software structures (struct atl1c_adapter) * @adapter: board private structure to initialize * * atl1c_sw_init initializes the Adapter private data structure. * Fields are initialized based on PCI device information and * OS network device settings (MTU size). */ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; u32 revision; adapter->wol = 0; device_set_wakeup_enable(&pdev->dev, false); adapter->link_speed = SPEED_0; adapter->link_duplex = FULL_DUPLEX; adapter->tpd_ring[0].count = 1024; adapter->rfd_ring.count = 512; hw->vendor_id = pdev->vendor; hw->device_id = pdev->device; hw->subsystem_vendor_id = pdev->subsystem_vendor; hw->subsystem_id = pdev->subsystem_device; pci_read_config_dword(pdev, PCI_CLASS_REVISION, &revision); hw->revision_id = revision & 0xFF; /* before link up, we assume hibernate is true */ hw->hibernate = true; hw->media_type = MEDIA_TYPE_AUTO_SENSOR; if (atl1c_setup_mac_funcs(hw) != 0) { dev_err(&pdev->dev, "set mac function pointers failed\n"); return -1; } atl1c_patch_assign(hw); hw->intr_mask = IMR_NORMAL_MASK; hw->phy_configured = false; hw->preamble_len = 7; hw->max_frame_size = adapter->netdev->mtu; hw->autoneg_advertised = ADVERTISED_Autoneg; hw->indirect_tab = 0xE4E4E4E4; hw->base_cpu = 0; hw->ict = 50000; /* 100ms */ hw->smb_timer = 200000; /* 400ms */ hw->rx_imt = 200; hw->tx_imt = 1000; hw->tpd_burst = 5; hw->rfd_burst = 8; hw->dma_order = atl1c_dma_ord_out; hw->dmar_block = atl1c_dma_req_1024; if (atl1c_alloc_queues(adapter)) { dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); return -ENOMEM; } /* TODO */ atl1c_set_rxbufsize(adapter, adapter->netdev); atomic_set(&adapter->irq_sem, 1); spin_lock_init(&adapter->mdio_lock); spin_lock_init(&adapter->tx_lock); set_bit(__AT_DOWN, &adapter->flags); return 0; } static inline void atl1c_clean_buffer(struct pci_dev *pdev, struct atl1c_buffer *buffer_info, int in_irq) { u16 pci_driection; if (buffer_info->flags & ATL1C_BUFFER_FREE) return; if (buffer_info->dma) { if (buffer_info->flags & ATL1C_PCIMAP_FROMDEVICE) pci_driection = PCI_DMA_FROMDEVICE; else pci_driection = PCI_DMA_TODEVICE; if (buffer_info->flags & ATL1C_PCIMAP_SINGLE) pci_unmap_single(pdev, buffer_info->dma, buffer_info->length, pci_driection); else if (buffer_info->flags & ATL1C_PCIMAP_PAGE) pci_unmap_page(pdev, buffer_info->dma, buffer_info->length, pci_driection); } if (buffer_info->skb) { if (in_irq) dev_kfree_skb_irq(buffer_info->skb); else dev_kfree_skb(buffer_info->skb); } buffer_info->dma = 0; buffer_info->skb = NULL; ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE); } /** * atl1c_clean_tx_ring - Free Tx-skb * @adapter: board private structure */ static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter, enum atl1c_trans_queue type) { struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; struct atl1c_buffer *buffer_info; struct pci_dev *pdev = adapter->pdev; u16 index, ring_count; ring_count = tpd_ring->count; for (index = 0; index < ring_count; index++) { buffer_info = &tpd_ring->buffer_info[index]; atl1c_clean_buffer(pdev, buffer_info, 0); } /* Zero out Tx-buffers */ memset(tpd_ring->desc, 0, sizeof(struct atl1c_tpd_desc) * ring_count); atomic_set(&tpd_ring->next_to_clean, 0); tpd_ring->next_to_use = 0; } /** * atl1c_clean_rx_ring - Free rx-reservation skbs * @adapter: board private structure */ static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter) { struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1c_buffer *buffer_info; struct pci_dev *pdev = adapter->pdev; int j; for (j = 0; j < rfd_ring->count; j++) { buffer_info = &rfd_ring->buffer_info[j]; atl1c_clean_buffer(pdev, buffer_info, 0); } /* zero out the descriptor ring */ memset(rfd_ring->desc, 0, rfd_ring->size); rfd_ring->next_to_clean = 0; rfd_ring->next_to_use = 0; rrd_ring->next_to_use = 0; rrd_ring->next_to_clean = 0; } /* * Read / Write Ptr Initialize: */ static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter) { struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1c_buffer *buffer_info; int i, j; for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { tpd_ring[i].next_to_use = 0; atomic_set(&tpd_ring[i].next_to_clean, 0); buffer_info = tpd_ring[i].buffer_info; for (j = 0; j < tpd_ring->count; j++) ATL1C_SET_BUFFER_STATE(&buffer_info[i], ATL1C_BUFFER_FREE); } rfd_ring->next_to_use = 0; rfd_ring->next_to_clean = 0; rrd_ring->next_to_use = 0; rrd_ring->next_to_clean = 0; for (j = 0; j < rfd_ring->count; j++) { buffer_info = &rfd_ring->buffer_info[j]; ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE); } } /** * atl1c_free_ring_resources - Free Tx / RX descriptor Resources * @adapter: board private structure * * Free all transmit software resources */ static void atl1c_free_ring_resources(struct atl1c_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; pci_free_consistent(pdev, adapter->ring_header.size, adapter->ring_header.desc, adapter->ring_header.dma); adapter->ring_header.desc = NULL; /* Note: just free tdp_ring.buffer_info, * it contain rfd_ring.buffer_info, do not double free */ if (adapter->tpd_ring[0].buffer_info) { kfree(adapter->tpd_ring[0].buffer_info); adapter->tpd_ring[0].buffer_info = NULL; } } /** * atl1c_setup_mem_resources - allocate Tx / RX descriptor resources * @adapter: board private structure * * Return 0 on success, negative on failure */ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1c_ring_header *ring_header = &adapter->ring_header; int size; int i; int count = 0; int rx_desc_count = 0; u32 offset = 0; rrd_ring->count = rfd_ring->count; for (i = 1; i < AT_MAX_TRANSMIT_QUEUE; i++) tpd_ring[i].count = tpd_ring[0].count; /* 2 tpd queue, one high priority queue, * another normal priority queue */ size = sizeof(struct atl1c_buffer) * (tpd_ring->count * 2 + rfd_ring->count); tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL); if (unlikely(!tpd_ring->buffer_info)) { dev_err(&pdev->dev, "kzalloc failed, size = %d\n", size); goto err_nomem; } for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { tpd_ring[i].buffer_info = (tpd_ring->buffer_info + count); count += tpd_ring[i].count; } rfd_ring->buffer_info = (tpd_ring->buffer_info + count); count += rfd_ring->count; rx_desc_count += rfd_ring->count; /* * real ring DMA buffer * each ring/block may need up to 8 bytes for alignment, hence the * additional bytes tacked onto the end. */ ring_header->size = size = sizeof(struct atl1c_tpd_desc) * tpd_ring->count * 2 + sizeof(struct atl1c_rx_free_desc) * rx_desc_count + sizeof(struct atl1c_recv_ret_status) * rx_desc_count + 8 * 4; ring_header->desc = pci_alloc_consistent(pdev, ring_header->size, &ring_header->dma); if (unlikely(!ring_header->desc)) { dev_err(&pdev->dev, "pci_alloc_consistend failed\n"); goto err_nomem; } memset(ring_header->desc, 0, ring_header->size); /* init TPD ring */ tpd_ring[0].dma = roundup(ring_header->dma, 8); offset = tpd_ring[0].dma - ring_header->dma; for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { tpd_ring[i].dma = ring_header->dma + offset; tpd_ring[i].desc = (u8 *) ring_header->desc + offset; tpd_ring[i].size = sizeof(struct atl1c_tpd_desc) * tpd_ring[i].count; offset += roundup(tpd_ring[i].size, 8); } /* init RFD ring */ rfd_ring->dma = ring_header->dma + offset; rfd_ring->desc = (u8 *) ring_header->desc + offset; rfd_ring->size = sizeof(struct atl1c_rx_free_desc) * rfd_ring->count; offset += roundup(rfd_ring->size, 8); /* init RRD ring */ rrd_ring->dma = ring_header->dma + offset; rrd_ring->desc = (u8 *) ring_header->desc + offset; rrd_ring->size = sizeof(struct atl1c_recv_ret_status) * rrd_ring->count; offset += roundup(rrd_ring->size, 8); return 0; err_nomem: kfree(tpd_ring->buffer_info); return -ENOMEM; } static void atl1c_configure_des_ring(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) adapter->tpd_ring; /* TPD */ AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI, (u32)((tpd_ring[atl1c_trans_normal].dma & AT_DMA_HI_ADDR_MASK) >> 32)); /* just enable normal priority TX queue */ AT_WRITE_REG(hw, REG_TPD_PRI0_ADDR_LO, (u32)(tpd_ring[atl1c_trans_normal].dma & AT_DMA_LO_ADDR_MASK)); AT_WRITE_REG(hw, REG_TPD_PRI1_ADDR_LO, (u32)(tpd_ring[atl1c_trans_high].dma & AT_DMA_LO_ADDR_MASK)); AT_WRITE_REG(hw, REG_TPD_RING_SIZE, (u32)(tpd_ring[0].count & TPD_RING_SIZE_MASK)); /* RFD */ AT_WRITE_REG(hw, REG_RX_BASE_ADDR_HI, (u32)((rfd_ring->dma & AT_DMA_HI_ADDR_MASK) >> 32)); AT_WRITE_REG(hw, REG_RFD0_HEAD_ADDR_LO, (u32)(rfd_ring->dma & AT_DMA_LO_ADDR_MASK)); AT_WRITE_REG(hw, REG_RFD_RING_SIZE, rfd_ring->count & RFD_RING_SIZE_MASK); AT_WRITE_REG(hw, REG_RX_BUF_SIZE, adapter->rx_buffer_len & RX_BUF_SIZE_MASK); /* RRD */ AT_WRITE_REG(hw, REG_RRD0_HEAD_ADDR_LO, (u32)(rrd_ring->dma & AT_DMA_LO_ADDR_MASK)); AT_WRITE_REG(hw, REG_RRD_RING_SIZE, (rrd_ring->count & RRD_RING_SIZE_MASK)); if (hw->nic_type == athr_l2c_b) { AT_WRITE_REG(hw, REG_SRAM_RXF_LEN, 0x02a0L); AT_WRITE_REG(hw, REG_SRAM_TXF_LEN, 0x0100L); AT_WRITE_REG(hw, REG_SRAM_RXF_ADDR, 0x029f0000L); AT_WRITE_REG(hw, REG_SRAM_RFD0_INFO, 0x02bf02a0L); AT_WRITE_REG(hw, REG_SRAM_TXF_ADDR, 0x03bf02c0L); AT_WRITE_REG(hw, REG_SRAM_TRD_ADDR, 0x03df03c0L); AT_WRITE_REG(hw, REG_TXF_WATER_MARK, 0); /* TX watermark, to enter l1 state.*/ AT_WRITE_REG(hw, REG_RXD_DMA_CTRL, 0); /* RXD threshold.*/ } /* Load all of base address above */ AT_WRITE_REG(hw, REG_LOAD_PTR, 1); } static void atl1c_configure_tx(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; int max_pay_load; u16 tx_offload_thresh; u32 txq_ctrl_data; tx_offload_thresh = MAX_TSO_FRAME_SIZE; AT_WRITE_REG(hw, REG_TX_TSO_OFFLOAD_THRESH, (tx_offload_thresh >> 3) & TX_TSO_OFFLOAD_THRESH_MASK); max_pay_load = pcie_get_readrq(adapter->pdev) >> 8; hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block); /* * if BIOS had changed the dam-read-max-length to an invalid value, * restore it to default value */ if (hw->dmar_block < DEVICE_CTRL_MAXRRS_MIN) { pcie_set_readrq(adapter->pdev, 128 << DEVICE_CTRL_MAXRRS_MIN); hw->dmar_block = DEVICE_CTRL_MAXRRS_MIN; } txq_ctrl_data = hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 ? L2CB_TXQ_CFGV : L1C_TXQ_CFGV; AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data); } static void atl1c_configure_rx(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; u32 rxq_ctrl_data; rxq_ctrl_data = (hw->rfd_burst & RXQ_RFD_BURST_NUM_MASK) << RXQ_RFD_BURST_NUM_SHIFT; if (hw->ctrl_flags & ATL1C_RX_IPV6_CHKSUM) rxq_ctrl_data |= IPV6_CHKSUM_CTRL_EN; /* aspm for gigabit */ if (hw->nic_type != athr_l1d_2 && (hw->device_id & 1) != 0) rxq_ctrl_data = FIELD_SETX(rxq_ctrl_data, ASPM_THRUPUT_LIMIT, ASPM_THRUPUT_LIMIT_100M); AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); } static void atl1c_configure_dma(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; u32 dma_ctrl_data; dma_ctrl_data = FIELDX(DMA_CTRL_RORDER_MODE, DMA_CTRL_RORDER_MODE_OUT) | DMA_CTRL_RREQ_PRI_DATA | FIELDX(DMA_CTRL_RREQ_BLEN, hw->dmar_block) | FIELDX(DMA_CTRL_WDLY_CNT, DMA_CTRL_WDLY_CNT_DEF) | FIELDX(DMA_CTRL_RDLY_CNT, DMA_CTRL_RDLY_CNT_DEF); AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data); } /* * Stop the mac, transmit and receive units * hw - Struct containing variables accessed by shared code * return : 0 or idle status (if error) */ static int atl1c_stop_mac(struct atl1c_hw *hw) { u32 data; AT_READ_REG(hw, REG_RXQ_CTRL, &data); data &= ~RXQ_CTRL_EN; AT_WRITE_REG(hw, REG_RXQ_CTRL, data); AT_READ_REG(hw, REG_TXQ_CTRL, &data); data &= ~TXQ_CTRL_EN; AT_WRITE_REG(hw, REG_TXQ_CTRL, data); atl1c_wait_until_idle(hw, IDLE_STATUS_RXQ_BUSY | IDLE_STATUS_TXQ_BUSY); AT_READ_REG(hw, REG_MAC_CTRL, &data); data &= ~(MAC_CTRL_TX_EN | MAC_CTRL_RX_EN); AT_WRITE_REG(hw, REG_MAC_CTRL, data); return (int)atl1c_wait_until_idle(hw, IDLE_STATUS_TXMAC_BUSY | IDLE_STATUS_RXMAC_BUSY); } static void atl1c_start_mac(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; u32 mac, txq, rxq; hw->mac_duplex = adapter->link_duplex == FULL_DUPLEX ? true : false; hw->mac_speed = adapter->link_speed == SPEED_1000 ? atl1c_mac_speed_1000 : atl1c_mac_speed_10_100; AT_READ_REG(hw, REG_TXQ_CTRL, &txq); AT_READ_REG(hw, REG_RXQ_CTRL, &rxq); AT_READ_REG(hw, REG_MAC_CTRL, &mac); txq |= TXQ_CTRL_EN; rxq |= RXQ_CTRL_EN; mac |= MAC_CTRL_TX_EN | MAC_CTRL_TX_FLOW | MAC_CTRL_RX_EN | MAC_CTRL_RX_FLOW | MAC_CTRL_ADD_CRC | MAC_CTRL_PAD | MAC_CTRL_BC_EN | MAC_CTRL_SINGLE_PAUSE_EN | MAC_CTRL_HASH_ALG_CRC32; if (hw->mac_duplex) mac |= MAC_CTRL_DUPLX; else mac &= ~MAC_CTRL_DUPLX; mac = FIELD_SETX(mac, MAC_CTRL_SPEED, hw->mac_speed); mac = FIELD_SETX(mac, MAC_CTRL_PRMLEN, hw->preamble_len); AT_WRITE_REG(hw, REG_TXQ_CTRL, txq); AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq); AT_WRITE_REG(hw, REG_MAC_CTRL, mac); } /* * Reset the transmit and receive units; mask and clear all interrupts. * hw - Struct containing variables accessed by shared code * return : 0 or idle status (if error) */ static int atl1c_reset_mac(struct atl1c_hw *hw) { struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; u32 ctrl_data = 0; atl1c_stop_mac(hw); /* * Issue Soft Reset to the MAC. This will reset the chip's * transmit, receive, DMA. It will not effect * the current PCI configuration. The global reset bit is self- * clearing, and should clear within a microsecond. */ AT_READ_REG(hw, REG_MASTER_CTRL, &ctrl_data); ctrl_data |= MASTER_CTRL_OOB_DIS; AT_WRITE_REG(hw, REG_MASTER_CTRL, ctrl_data | MASTER_CTRL_SOFT_RST); AT_WRITE_FLUSH(hw); msleep(10); /* Wait at least 10ms for All module to be Idle */ if (atl1c_wait_until_idle(hw, IDLE_STATUS_MASK)) { dev_err(&pdev->dev, "MAC state machine can't be idle since" " disabled for 10ms second\n"); return -1; } AT_WRITE_REG(hw, REG_MASTER_CTRL, ctrl_data); /* driver control speed/duplex */ AT_READ_REG(hw, REG_MAC_CTRL, &ctrl_data); AT_WRITE_REG(hw, REG_MAC_CTRL, ctrl_data | MAC_CTRL_SPEED_MODE_SW); /* clk switch setting */ AT_READ_REG(hw, REG_SERDES, &ctrl_data); switch (hw->nic_type) { case athr_l2c_b: ctrl_data &= ~(SERDES_PHY_CLK_SLOWDOWN | SERDES_MAC_CLK_SLOWDOWN); AT_WRITE_REG(hw, REG_SERDES, ctrl_data); break; case athr_l2c_b2: case athr_l1d_2: ctrl_data |= SERDES_PHY_CLK_SLOWDOWN | SERDES_MAC_CLK_SLOWDOWN; AT_WRITE_REG(hw, REG_SERDES, ctrl_data); break; default: break; } return 0; } static void atl1c_disable_l0s_l1(struct atl1c_hw *hw) { u16 ctrl_flags = hw->ctrl_flags; hw->ctrl_flags &= ~(ATL1C_ASPM_L0S_SUPPORT | ATL1C_ASPM_L1_SUPPORT); atl1c_set_aspm(hw, SPEED_0); hw->ctrl_flags = ctrl_flags; } /* * Set ASPM state. * Enable/disable L0s/L1 depend on link state. */ static void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed) { u32 pm_ctrl_data; u32 link_l1_timer; AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data); pm_ctrl_data &= ~(PM_CTRL_ASPM_L1_EN | PM_CTRL_ASPM_L0S_EN | PM_CTRL_MAC_ASPM_CHK); /* L1 timer */ if (hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) { pm_ctrl_data &= ~PMCTRL_TXL1_AFTER_L0S; link_l1_timer = link_speed == SPEED_1000 || link_speed == SPEED_100 ? L1D_PMCTRL_L1_ENTRY_TM_16US : 1; pm_ctrl_data = FIELD_SETX(pm_ctrl_data, L1D_PMCTRL_L1_ENTRY_TM, link_l1_timer); } else { link_l1_timer = hw->nic_type == athr_l2c_b ? L2CB1_PM_CTRL_L1_ENTRY_TM : L1C_PM_CTRL_L1_ENTRY_TM; if (link_speed != SPEED_1000 && link_speed != SPEED_100) link_l1_timer = 1; pm_ctrl_data = FIELD_SETX(pm_ctrl_data, PM_CTRL_L1_ENTRY_TIMER, link_l1_timer); } /* L0S/L1 enable */ if ((hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT) && link_speed != SPEED_0) pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN | PM_CTRL_MAC_ASPM_CHK; if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) pm_ctrl_data |= PM_CTRL_ASPM_L1_EN | PM_CTRL_MAC_ASPM_CHK; /* l2cb & l1d & l2cb2 & l1d2 */ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d || hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) { pm_ctrl_data = FIELD_SETX(pm_ctrl_data, PM_CTRL_PM_REQ_TIMER, PM_CTRL_PM_REQ_TO_DEF); pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER | PM_CTRL_SERDES_PD_EX_L1 | PM_CTRL_CLK_SWH_L1; pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN | PM_CTRL_SERDES_PLL_L1_EN | PM_CTRL_SERDES_BUFS_RX_L1_EN | PM_CTRL_SA_DLY_EN | PM_CTRL_HOTRST); /* disable l0s if link down or l2cb */ if (link_speed == SPEED_0 || hw->nic_type == athr_l2c_b) pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; } else { /* l1c */ pm_ctrl_data = FIELD_SETX(pm_ctrl_data, PM_CTRL_L1_ENTRY_TIMER, 0); if (link_speed != SPEED_0) { pm_ctrl_data |= PM_CTRL_SERDES_L1_EN | PM_CTRL_SERDES_PLL_L1_EN | PM_CTRL_SERDES_BUFS_RX_L1_EN; pm_ctrl_data &= ~(PM_CTRL_SERDES_PD_EX_L1 | PM_CTRL_CLK_SWH_L1 | PM_CTRL_ASPM_L0S_EN | PM_CTRL_ASPM_L1_EN); } else { /* link down */ pm_ctrl_data |= PM_CTRL_CLK_SWH_L1; pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN | PM_CTRL_SERDES_PLL_L1_EN | PM_CTRL_SERDES_BUFS_RX_L1_EN | PM_CTRL_ASPM_L0S_EN); } } AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data); return; } /** * atl1c_configure - Configure Transmit&Receive Unit after Reset * @adapter: board private structure * * Configure the Tx /Rx unit of the MAC after a reset. */ static int atl1c_configure_mac(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; u32 master_ctrl_data = 0; u32 intr_modrt_data; u32 data; AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data); master_ctrl_data &= ~(MASTER_CTRL_TX_ITIMER_EN | MASTER_CTRL_RX_ITIMER_EN | MASTER_CTRL_INT_RDCLR); /* clear interrupt status */ AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF); /* Clear any WOL status */ AT_WRITE_REG(hw, REG_WOL_CTRL, 0); /* set Interrupt Clear Timer * HW will enable self to assert interrupt event to system after * waiting x-time for software to notify it accept interrupt. */ data = CLK_GATING_EN_ALL; if (hw->ctrl_flags & ATL1C_CLK_GATING_EN) { if (hw->nic_type == athr_l2c_b) data &= ~CLK_GATING_RXMAC_EN; } else data = 0; AT_WRITE_REG(hw, REG_CLK_GATING_CTRL, data); AT_WRITE_REG(hw, REG_INT_RETRIG_TIMER, hw->ict & INT_RETRIG_TIMER_MASK); atl1c_configure_des_ring(adapter); if (hw->ctrl_flags & ATL1C_INTR_MODRT_ENABLE) { intr_modrt_data = (hw->tx_imt & IRQ_MODRT_TIMER_MASK) << IRQ_MODRT_TX_TIMER_SHIFT; intr_modrt_data |= (hw->rx_imt & IRQ_MODRT_TIMER_MASK) << IRQ_MODRT_RX_TIMER_SHIFT; AT_WRITE_REG(hw, REG_IRQ_MODRT_TIMER_INIT, intr_modrt_data); master_ctrl_data |= MASTER_CTRL_TX_ITIMER_EN | MASTER_CTRL_RX_ITIMER_EN; } if (hw->ctrl_flags & ATL1C_INTR_CLEAR_ON_READ) master_ctrl_data |= MASTER_CTRL_INT_RDCLR; master_ctrl_data |= MASTER_CTRL_SA_TIMER_EN; AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, hw->smb_timer & SMB_STAT_TIMER_MASK); /* set MTU */ AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN); atl1c_configure_tx(adapter); atl1c_configure_rx(adapter); atl1c_configure_dma(adapter); return 0; } static int atl1c_configure(struct atl1c_adapter *adapter) { struct net_device *netdev = adapter->netdev; int num; atl1c_init_ring_ptrs(adapter); atl1c_set_multi(netdev); atl1c_restore_vlan(adapter); num = atl1c_alloc_rx_buffer(adapter); if (unlikely(num == 0)) return -ENOMEM; if (atl1c_configure_mac(adapter)) return -EIO; return 0; } static void atl1c_update_hw_stats(struct atl1c_adapter *adapter) { u16 hw_reg_addr = 0; unsigned long *stats_item = NULL; u32 data; /* update rx status */ hw_reg_addr = REG_MAC_RX_STATUS_BIN; stats_item = &adapter->hw_stats.rx_ok; while (hw_reg_addr <= REG_MAC_RX_STATUS_END) { AT_READ_REG(&adapter->hw, hw_reg_addr, &data); *stats_item += data; stats_item++; hw_reg_addr += 4; } /* update tx status */ hw_reg_addr = REG_MAC_TX_STATUS_BIN; stats_item = &adapter->hw_stats.tx_ok; while (hw_reg_addr <= REG_MAC_TX_STATUS_END) { AT_READ_REG(&adapter->hw, hw_reg_addr, &data); *stats_item += data; stats_item++; hw_reg_addr += 4; } } /** * atl1c_get_stats - Get System Network Statistics * @netdev: network interface device structure * * Returns the address of the device statistics structure. * The statistics are actually updated from the timer callback. */ static struct net_device_stats *atl1c_get_stats(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw_stats *hw_stats = &adapter->hw_stats; struct net_device_stats *net_stats = &netdev->stats; atl1c_update_hw_stats(adapter); net_stats->rx_packets = hw_stats->rx_ok; net_stats->tx_packets = hw_stats->tx_ok; net_stats->rx_bytes = hw_stats->rx_byte_cnt; net_stats->tx_bytes = hw_stats->tx_byte_cnt; net_stats->multicast = hw_stats->rx_mcast; net_stats->collisions = hw_stats->tx_1_col + hw_stats->tx_2_col * 2 + hw_stats->tx_late_col + hw_stats->tx_abort_col; net_stats->rx_errors = hw_stats->rx_frag + hw_stats->rx_fcs_err + hw_stats->rx_len_err + hw_stats->rx_sz_ov + hw_stats->rx_rrd_ov + hw_stats->rx_align_err; net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov; net_stats->rx_length_errors = hw_stats->rx_len_err; net_stats->rx_crc_errors = hw_stats->rx_fcs_err; net_stats->rx_frame_errors = hw_stats->rx_align_err; net_stats->rx_over_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col + hw_stats->tx_underrun + hw_stats->tx_trunc; net_stats->tx_fifo_errors = hw_stats->tx_underrun; net_stats->tx_aborted_errors = hw_stats->tx_abort_col; net_stats->tx_window_errors = hw_stats->tx_late_col; return net_stats; } static inline void atl1c_clear_phy_int(struct atl1c_adapter *adapter) { u16 phy_data; spin_lock(&adapter->mdio_lock); atl1c_read_phy_reg(&adapter->hw, MII_ISR, &phy_data); spin_unlock(&adapter->mdio_lock); } static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter, enum atl1c_trans_queue type) { struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; struct atl1c_buffer *buffer_info; struct pci_dev *pdev = adapter->pdev; u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); u16 hw_next_to_clean; u16 reg; reg = type == atl1c_trans_high ? REG_TPD_PRI1_CIDX : REG_TPD_PRI0_CIDX; AT_READ_REGW(&adapter->hw, reg, &hw_next_to_clean); while (next_to_clean != hw_next_to_clean) { buffer_info = &tpd_ring->buffer_info[next_to_clean]; atl1c_clean_buffer(pdev, buffer_info, 1); if (++next_to_clean == tpd_ring->count) next_to_clean = 0; atomic_set(&tpd_ring->next_to_clean, next_to_clean); } if (netif_queue_stopped(adapter->netdev) && netif_carrier_ok(adapter->netdev)) { netif_wake_queue(adapter->netdev); } return true; } /** * atl1c_intr - Interrupt Handler * @irq: interrupt number * @data: pointer to a network interface device structure */ static irqreturn_t atl1c_intr(int irq, void *data) { struct net_device *netdev = data; struct atl1c_adapter *adapter = netdev_priv(netdev); struct pci_dev *pdev = adapter->pdev; struct atl1c_hw *hw = &adapter->hw; int max_ints = AT_MAX_INT_WORK; int handled = IRQ_NONE; u32 status; u32 reg_data; do { AT_READ_REG(hw, REG_ISR, ®_data); status = reg_data & hw->intr_mask; if (status == 0 || (status & ISR_DIS_INT) != 0) { if (max_ints != AT_MAX_INT_WORK) handled = IRQ_HANDLED; break; } /* link event */ if (status & ISR_GPHY) atl1c_clear_phy_int(adapter); /* Ack ISR */ AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); if (status & ISR_RX_PKT) { if (likely(napi_schedule_prep(&adapter->napi))) { hw->intr_mask &= ~ISR_RX_PKT; AT_WRITE_REG(hw, REG_IMR, hw->intr_mask); __napi_schedule(&adapter->napi); } } if (status & ISR_TX_PKT) atl1c_clean_tx_irq(adapter, atl1c_trans_normal); handled = IRQ_HANDLED; /* check if PCIE PHY Link down */ if (status & ISR_ERROR) { if (netif_msg_hw(adapter)) dev_err(&pdev->dev, "atl1c hardware error (status = 0x%x)\n", status & ISR_ERROR); /* reset MAC */ set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event); schedule_work(&adapter->common_task); return IRQ_HANDLED; } if (status & ISR_OVER) if (netif_msg_intr(adapter)) dev_warn(&pdev->dev, "TX/RX overflow (status = 0x%x)\n", status & ISR_OVER); /* link event */ if (status & (ISR_GPHY | ISR_MANUAL)) { netdev->stats.tx_carrier_errors++; atl1c_link_chg_event(adapter); break; } } while (--max_ints > 0); /* re-enable Interrupt*/ AT_WRITE_REG(&adapter->hw, REG_ISR, 0); return handled; } static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter, struct sk_buff *skb, struct atl1c_recv_ret_status *prrs) { /* * The pid field in RRS in not correct sometimes, so we * cannot figure out if the packet is fragmented or not, * so we tell the KERNEL CHECKSUM_NONE */ skb_checksum_none_assert(skb); } static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter) { struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; struct pci_dev *pdev = adapter->pdev; struct atl1c_buffer *buffer_info, *next_info; struct sk_buff *skb; void *vir_addr = NULL; u16 num_alloc = 0; u16 rfd_next_to_use, next_next; struct atl1c_rx_free_desc *rfd_desc; next_next = rfd_next_to_use = rfd_ring->next_to_use; if (++next_next == rfd_ring->count) next_next = 0; buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; next_info = &rfd_ring->buffer_info[next_next]; while (next_info->flags & ATL1C_BUFFER_FREE) { rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use); skb = netdev_alloc_skb(adapter->netdev, adapter->rx_buffer_len); if (unlikely(!skb)) { if (netif_msg_rx_err(adapter)) dev_warn(&pdev->dev, "alloc rx buffer failed\n"); break; } /* * Make buffer alignment 2 beyond a 16 byte boundary * this will result in a 16 byte aligned IP header after * the 14 byte MAC header is removed */ vir_addr = skb->data; ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); buffer_info->skb = skb; buffer_info->length = adapter->rx_buffer_len; buffer_info->dma = pci_map_single(pdev, vir_addr, buffer_info->length, PCI_DMA_FROMDEVICE); ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE, ATL1C_PCIMAP_FROMDEVICE); rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); rfd_next_to_use = next_next; if (++next_next == rfd_ring->count) next_next = 0; buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; next_info = &rfd_ring->buffer_info[next_next]; num_alloc++; } if (num_alloc) { /* TODO: update mailbox here */ wmb(); rfd_ring->next_to_use = rfd_next_to_use; AT_WRITE_REG(&adapter->hw, REG_MB_RFD0_PROD_IDX, rfd_ring->next_to_use & MB_RFDX_PROD_IDX_MASK); } return num_alloc; } static void atl1c_clean_rrd(struct atl1c_rrd_ring *rrd_ring, struct atl1c_recv_ret_status *rrs, u16 num) { u16 i; /* the relationship between rrd and rfd is one map one */ for (i = 0; i < num; i++, rrs = ATL1C_RRD_DESC(rrd_ring, rrd_ring->next_to_clean)) { rrs->word3 &= ~RRS_RXD_UPDATED; if (++rrd_ring->next_to_clean == rrd_ring->count) rrd_ring->next_to_clean = 0; } } static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring, struct atl1c_recv_ret_status *rrs, u16 num) { u16 i; u16 rfd_index; struct atl1c_buffer *buffer_info = rfd_ring->buffer_info; rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) & RRS_RX_RFD_INDEX_MASK; for (i = 0; i < num; i++) { buffer_info[rfd_index].skb = NULL; ATL1C_SET_BUFFER_STATE(&buffer_info[rfd_index], ATL1C_BUFFER_FREE); if (++rfd_index == rfd_ring->count) rfd_index = 0; } rfd_ring->next_to_clean = rfd_index; } static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, int *work_done, int work_to_do) { u16 rfd_num, rfd_index; u16 count = 0; u16 length; struct pci_dev *pdev = adapter->pdev; struct net_device *netdev = adapter->netdev; struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; struct sk_buff *skb; struct atl1c_recv_ret_status *rrs; struct atl1c_buffer *buffer_info; while (1) { if (*work_done >= work_to_do) break; rrs = ATL1C_RRD_DESC(rrd_ring, rrd_ring->next_to_clean); if (likely(RRS_RXD_IS_VALID(rrs->word3))) { rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) & RRS_RX_RFD_CNT_MASK; if (unlikely(rfd_num != 1)) /* TODO support mul rfd*/ if (netif_msg_rx_err(adapter)) dev_warn(&pdev->dev, "Multi rfd not support yet!\n"); goto rrs_checked; } else { break; } rrs_checked: atl1c_clean_rrd(rrd_ring, rrs, rfd_num); if (rrs->word3 & (RRS_RX_ERR_SUM | RRS_802_3_LEN_ERR)) { atl1c_clean_rfd(rfd_ring, rrs, rfd_num); if (netif_msg_rx_err(adapter)) dev_warn(&pdev->dev, "wrong packet! rrs word3 is %x\n", rrs->word3); continue; } length = le16_to_cpu((rrs->word3 >> RRS_PKT_SIZE_SHIFT) & RRS_PKT_SIZE_MASK); /* Good Receive */ if (likely(rfd_num == 1)) { rfd_index = (rrs->word0 >> RRS_RX_RFD_INDEX_SHIFT) & RRS_RX_RFD_INDEX_MASK; buffer_info = &rfd_ring->buffer_info[rfd_index]; pci_unmap_single(pdev, buffer_info->dma, buffer_info->length, PCI_DMA_FROMDEVICE); skb = buffer_info->skb; } else { /* TODO */ if (netif_msg_rx_err(adapter)) dev_warn(&pdev->dev, "Multi rfd not support yet!\n"); break; } atl1c_clean_rfd(rfd_ring, rrs, rfd_num); skb_put(skb, length - ETH_FCS_LEN); skb->protocol = eth_type_trans(skb, netdev); atl1c_rx_checksum(adapter, skb, rrs); if (rrs->word3 & RRS_VLAN_INS) { u16 vlan; AT_TAG_TO_VLAN(rrs->vlan_tag, vlan); vlan = le16_to_cpu(vlan); __vlan_hwaccel_put_tag(skb, vlan); } netif_receive_skb(skb); (*work_done)++; count++; } if (count) atl1c_alloc_rx_buffer(adapter); } /** * atl1c_clean - NAPI Rx polling callback */ static int atl1c_clean(struct napi_struct *napi, int budget) { struct atl1c_adapter *adapter = container_of(napi, struct atl1c_adapter, napi); int work_done = 0; /* Keep link state information with original netdev */ if (!netif_carrier_ok(adapter->netdev)) goto quit_polling; /* just enable one RXQ */ atl1c_clean_rx_irq(adapter, &work_done, budget); if (work_done < budget) { quit_polling: napi_complete(napi); adapter->hw.intr_mask |= ISR_RX_PKT; AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask); } return work_done; } #ifdef CONFIG_NET_POLL_CONTROLLER /* * Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing. */ static void atl1c_netpoll(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); disable_irq(adapter->pdev->irq); atl1c_intr(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); } #endif static inline u16 atl1c_tpd_avail(struct atl1c_adapter *adapter, enum atl1c_trans_queue type) { struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; u16 next_to_use = 0; u16 next_to_clean = 0; next_to_clean = atomic_read(&tpd_ring->next_to_clean); next_to_use = tpd_ring->next_to_use; return (u16)(next_to_clean > next_to_use) ? (next_to_clean - next_to_use - 1) : (tpd_ring->count + next_to_clean - next_to_use - 1); } /* * get next usable tpd * Note: should call atl1c_tdp_avail to make sure * there is enough tpd to use */ static struct atl1c_tpd_desc *atl1c_get_tpd(struct atl1c_adapter *adapter, enum atl1c_trans_queue type) { struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; struct atl1c_tpd_desc *tpd_desc; u16 next_to_use = 0; next_to_use = tpd_ring->next_to_use; if (++tpd_ring->next_to_use == tpd_ring->count) tpd_ring->next_to_use = 0; tpd_desc = ATL1C_TPD_DESC(tpd_ring, next_to_use); memset(tpd_desc, 0, sizeof(struct atl1c_tpd_desc)); return tpd_desc; } static struct atl1c_buffer * atl1c_get_tx_buffer(struct atl1c_adapter *adapter, struct atl1c_tpd_desc *tpd) { struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; return &tpd_ring->buffer_info[tpd - (struct atl1c_tpd_desc *)tpd_ring->desc]; } /* Calculate the transmit packet descript needed*/ static u16 atl1c_cal_tpd_req(const struct sk_buff *skb) { u16 tpd_req; u16 proto_hdr_len = 0; tpd_req = skb_shinfo(skb)->nr_frags + 1; if (skb_is_gso(skb)) { proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (proto_hdr_len < skb_headlen(skb)) tpd_req++; if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) tpd_req++; } return tpd_req; } static int atl1c_tso_csum(struct atl1c_adapter *adapter, struct sk_buff *skb, struct atl1c_tpd_desc **tpd, enum atl1c_trans_queue type) { struct pci_dev *pdev = adapter->pdev; u8 hdr_len; u32 real_len; unsigned short offload_type; int err; if (skb_is_gso(skb)) { if (skb_header_cloned(skb)) { err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (unlikely(err)) return -1; } offload_type = skb_shinfo(skb)->gso_type; if (offload_type & SKB_GSO_TCPV4) { real_len = (((unsigned char *)ip_hdr(skb) - skb->data) + ntohs(ip_hdr(skb)->tot_len)); if (real_len < skb->len) pskb_trim(skb, real_len); hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); if (unlikely(skb->len == hdr_len)) { /* only xsum need */ if (netif_msg_tx_queued(adapter)) dev_warn(&pdev->dev, "IPV4 tso with zero data??\n"); goto check_sum; } else { ip_hdr(skb)->check = 0; tcp_hdr(skb)->check = ~csum_tcpudp_magic( ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); (*tpd)->word1 |= 1 << TPD_IPV4_PACKET_SHIFT; } } if (offload_type & SKB_GSO_TCPV6) { struct atl1c_tpd_ext_desc *etpd = *(struct atl1c_tpd_ext_desc **)(tpd); memset(etpd, 0, sizeof(struct atl1c_tpd_ext_desc)); *tpd = atl1c_get_tpd(adapter, type); ipv6_hdr(skb)->payload_len = 0; /* check payload == 0 byte ? */ hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); if (unlikely(skb->len == hdr_len)) { /* only xsum need */ if (netif_msg_tx_queued(adapter)) dev_warn(&pdev->dev, "IPV6 tso with zero data??\n"); goto check_sum; } else tcp_hdr(skb)->check = ~csum_ipv6_magic( &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); etpd->word1 |= 1 << TPD_LSO_EN_SHIFT; etpd->word1 |= 1 << TPD_LSO_VER_SHIFT; etpd->pkt_len = cpu_to_le32(skb->len); (*tpd)->word1 |= 1 << TPD_LSO_VER_SHIFT; } (*tpd)->word1 |= 1 << TPD_LSO_EN_SHIFT; (*tpd)->word1 |= (skb_transport_offset(skb) & TPD_TCPHDR_OFFSET_MASK) << TPD_TCPHDR_OFFSET_SHIFT; (*tpd)->word1 |= (skb_shinfo(skb)->gso_size & TPD_MSS_MASK) << TPD_MSS_SHIFT; return 0; } check_sum: if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { u8 css, cso; cso = skb_checksum_start_offset(skb); if (unlikely(cso & 0x1)) { if (netif_msg_tx_err(adapter)) dev_err(&adapter->pdev->dev, "payload offset should not an event number\n"); return -1; } else { css = cso + skb->csum_offset; (*tpd)->word1 |= ((cso >> 1) & TPD_PLOADOFFSET_MASK) << TPD_PLOADOFFSET_SHIFT; (*tpd)->word1 |= ((css >> 1) & TPD_CCSUM_OFFSET_MASK) << TPD_CCSUM_OFFSET_SHIFT; (*tpd)->word1 |= 1 << TPD_CCSUM_EN_SHIFT; } } return 0; } static void atl1c_tx_map(struct atl1c_adapter *adapter, struct sk_buff *skb, struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type) { struct atl1c_tpd_desc *use_tpd = NULL; struct atl1c_buffer *buffer_info = NULL; u16 buf_len = skb_headlen(skb); u16 map_len = 0; u16 mapped_len = 0; u16 hdr_len = 0; u16 nr_frags; u16 f; int tso; nr_frags = skb_shinfo(skb)->nr_frags; tso = (tpd->word1 >> TPD_LSO_EN_SHIFT) & TPD_LSO_EN_MASK; if (tso) { /* TSO */ map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); use_tpd = tpd; buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); buffer_info->length = map_len; buffer_info->dma = pci_map_single(adapter->pdev, skb->data, hdr_len, PCI_DMA_TODEVICE); ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE, ATL1C_PCIMAP_TODEVICE); mapped_len += map_len; use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); } if (mapped_len < buf_len) { /* mapped_len == 0, means we should use the first tpd, which is given by caller */ if (mapped_len == 0) use_tpd = tpd; else { use_tpd = atl1c_get_tpd(adapter, type); memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); } buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); buffer_info->length = buf_len - mapped_len; buffer_info->dma = pci_map_single(adapter->pdev, skb->data + mapped_len, buffer_info->length, PCI_DMA_TODEVICE); ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE, ATL1C_PCIMAP_TODEVICE); use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); } for (f = 0; f < nr_frags; f++) { struct skb_frag_struct *frag; frag = &skb_shinfo(skb)->frags[f]; use_tpd = atl1c_get_tpd(adapter, type); memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc)); buffer_info = atl1c_get_tx_buffer(adapter, use_tpd); buffer_info->length = skb_frag_size(frag); buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev, frag, 0, buffer_info->length, DMA_TO_DEVICE); ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE, ATL1C_PCIMAP_TODEVICE); use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); } /* The last tpd */ use_tpd->word1 |= 1 << TPD_EOP_SHIFT; /* The last buffer info contain the skb address, so it will be free after unmap */ buffer_info->skb = skb; } static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb, struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type) { struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; u16 reg; reg = type == atl1c_trans_high ? REG_TPD_PRI1_PIDX : REG_TPD_PRI0_PIDX; AT_WRITE_REGW(&adapter->hw, reg, tpd_ring->next_to_use); } static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); unsigned long flags; u16 tpd_req = 1; struct atl1c_tpd_desc *tpd; enum atl1c_trans_queue type = atl1c_trans_normal; if (test_bit(__AT_DOWN, &adapter->flags)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } tpd_req = atl1c_cal_tpd_req(skb); if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) { if (netif_msg_pktdata(adapter)) dev_info(&adapter->pdev->dev, "tx locked\n"); return NETDEV_TX_LOCKED; } if (atl1c_tpd_avail(adapter, type) < tpd_req) { /* no enough descriptor, just stop queue */ netif_stop_queue(netdev); spin_unlock_irqrestore(&adapter->tx_lock, flags); return NETDEV_TX_BUSY; } tpd = atl1c_get_tpd(adapter, type); /* do TSO and check sum */ if (atl1c_tso_csum(adapter, skb, &tpd, type) != 0) { spin_unlock_irqrestore(&adapter->tx_lock, flags); dev_kfree_skb_any(skb); return NETDEV_TX_OK; } if (unlikely(vlan_tx_tag_present(skb))) { u16 vlan = vlan_tx_tag_get(skb); __le16 tag; vlan = cpu_to_le16(vlan); AT_VLAN_TO_TAG(vlan, tag); tpd->word1 |= 1 << TPD_INS_VTAG_SHIFT; tpd->vlan_tag = tag; } if (skb_network_offset(skb) != ETH_HLEN) tpd->word1 |= 1 << TPD_ETH_TYPE_SHIFT; /* Ethernet frame */ atl1c_tx_map(adapter, skb, tpd, type); atl1c_tx_queue(adapter, skb, tpd, type); spin_unlock_irqrestore(&adapter->tx_lock, flags); return NETDEV_TX_OK; } static void atl1c_free_irq(struct atl1c_adapter *adapter) { struct net_device *netdev = adapter->netdev; free_irq(adapter->pdev->irq, netdev); if (adapter->have_msi) pci_disable_msi(adapter->pdev); } static int atl1c_request_irq(struct atl1c_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; struct net_device *netdev = adapter->netdev; int flags = 0; int err = 0; adapter->have_msi = true; err = pci_enable_msi(adapter->pdev); if (err) { if (netif_msg_ifup(adapter)) dev_err(&pdev->dev, "Unable to allocate MSI interrupt Error: %d\n", err); adapter->have_msi = false; } if (!adapter->have_msi) flags |= IRQF_SHARED; err = request_irq(adapter->pdev->irq, atl1c_intr, flags, netdev->name, netdev); if (err) { if (netif_msg_ifup(adapter)) dev_err(&pdev->dev, "Unable to allocate interrupt Error: %d\n", err); if (adapter->have_msi) pci_disable_msi(adapter->pdev); return err; } if (netif_msg_ifup(adapter)) dev_dbg(&pdev->dev, "atl1c_request_irq OK\n"); return err; } static void atl1c_reset_dma_ring(struct atl1c_adapter *adapter) { /* release tx-pending skbs and reset tx/rx ring index */ atl1c_clean_tx_ring(adapter, atl1c_trans_normal); atl1c_clean_tx_ring(adapter, atl1c_trans_high); atl1c_clean_rx_ring(adapter); } static int atl1c_up(struct atl1c_adapter *adapter) { struct net_device *netdev = adapter->netdev; int err; netif_carrier_off(netdev); err = atl1c_configure(adapter); if (unlikely(err)) goto err_up; err = atl1c_request_irq(adapter); if (unlikely(err)) goto err_up; atl1c_check_link_status(adapter); clear_bit(__AT_DOWN, &adapter->flags); napi_enable(&adapter->napi); atl1c_irq_enable(adapter); netif_start_queue(netdev); return err; err_up: atl1c_clean_rx_ring(adapter); return err; } static void atl1c_down(struct atl1c_adapter *adapter) { struct net_device *netdev = adapter->netdev; atl1c_del_timer(adapter); adapter->work_event = 0; /* clear all event */ /* signal that we're down so the interrupt handler does not * reschedule our watchdog timer */ set_bit(__AT_DOWN, &adapter->flags); netif_carrier_off(netdev); napi_disable(&adapter->napi); atl1c_irq_disable(adapter); atl1c_free_irq(adapter); /* disable ASPM if device inactive */ atl1c_disable_l0s_l1(&adapter->hw); /* reset MAC to disable all RX/TX */ atl1c_reset_mac(&adapter->hw); msleep(1); adapter->link_speed = SPEED_0; adapter->link_duplex = -1; atl1c_reset_dma_ring(adapter); } /** * atl1c_open - Called when a network interface is made active * @netdev: network interface device structure * * Returns 0 on success, negative value on failure * * The open entry point is called when a network interface is made * active by the system (IFF_UP). At this point all resources needed * for transmit and receive operations are allocated, the interrupt * handler is registered with the OS, the watchdog timer is started, * and the stack is notified that the interface is ready. */ static int atl1c_open(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); int err; /* disallow open during test */ if (test_bit(__AT_TESTING, &adapter->flags)) return -EBUSY; /* allocate rx/tx dma buffer & descriptors */ err = atl1c_setup_ring_resources(adapter); if (unlikely(err)) return err; err = atl1c_up(adapter); if (unlikely(err)) goto err_up; return 0; err_up: atl1c_free_irq(adapter); atl1c_free_ring_resources(adapter); atl1c_reset_mac(&adapter->hw); return err; } /** * atl1c_close - Disables a network interface * @netdev: network interface device structure * * Returns 0, this is not allowed to fail * * The close entry point is called when an interface is de-activated * by the OS. The hardware is still under the drivers control, but * needs to be disabled. A global MAC reset is issued to stop the * hardware, and all transmit and receive resources are freed. */ static int atl1c_close(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); set_bit(__AT_DOWN, &adapter->flags); cancel_work_sync(&adapter->common_task); atl1c_down(adapter); atl1c_free_ring_resources(adapter); return 0; } static int atl1c_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; u32 wufc = adapter->wol; atl1c_disable_l0s_l1(hw); if (netif_running(netdev)) { WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); atl1c_down(adapter); } netif_device_detach(netdev); if (wufc) if (atl1c_phy_to_ps_link(hw) != 0) dev_dbg(&pdev->dev, "phy power saving failed"); atl1c_power_saving(hw, wufc); return 0; } #ifdef CONFIG_PM_SLEEP static int atl1c_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct atl1c_adapter *adapter = netdev_priv(netdev); AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE); atl1c_phy_reset(&adapter->hw); atl1c_reset_mac(&adapter->hw); atl1c_phy_init(&adapter->hw); #if 0 AT_READ_REG(&adapter->hw, REG_PM_CTRLSTAT, &pm_data); pm_data &= ~PM_CTRLSTAT_PME_EN; AT_WRITE_REG(&adapter->hw, REG_PM_CTRLSTAT, pm_data); #endif netif_device_attach(netdev); if (netif_running(netdev)) atl1c_up(adapter); return 0; } #endif static void atl1c_shutdown(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1c_adapter *adapter = netdev_priv(netdev); atl1c_suspend(&pdev->dev); pci_wake_from_d3(pdev, adapter->wol); pci_set_power_state(pdev, PCI_D3hot); } static const struct net_device_ops atl1c_netdev_ops = { .ndo_open = atl1c_open, .ndo_stop = atl1c_close, .ndo_validate_addr = eth_validate_addr, .ndo_start_xmit = atl1c_xmit_frame, .ndo_set_mac_address = atl1c_set_mac_addr, .ndo_set_rx_mode = atl1c_set_multi, .ndo_change_mtu = atl1c_change_mtu, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atl1c_fix_features, .ndo_set_features = atl1c_set_features, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_do_ioctl = atl1c_ioctl, .ndo_tx_timeout = atl1c_tx_timeout, .ndo_get_stats = atl1c_get_stats, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = atl1c_netpoll, #endif }; static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev) { SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); netdev_attach_ops(netdev, &atl1c_netdev_ops); netdev->watchdog_timeo = AT_TX_WATCHDOG; atl1c_set_ethtool_ops(netdev); /* TODO: add when ready */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_RX | NETIF_F_TSO | NETIF_F_TSO6; netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_TX; #else netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | NETIF_F_TSO | NETIF_F_TSO6; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ return 0; } /** * atl1c_probe - Device Initialization Routine * @pdev: PCI device information struct * @ent: entry in atl1c_pci_tbl * * Returns 0 on success, negative on failure * * atl1c_probe initializes an adapter identified by a pci_dev structure. * The OS initialization, configuring of the adapter private structure, * and a hardware reset occur. */ static int __devinit atl1c_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct atl1c_adapter *adapter; static int cards_found; int err = 0; /* enable device (incl. PCI PM wakeup and hotplug setup) */ err = pci_enable_device_mem(pdev); if (err) { dev_err(&pdev->dev, "cannot enable PCI device\n"); return err; } /* * The atl1c chip can DMA to 64-bit addresses, but it uses a single * shared register for the high 32 bits, so only a single, aligned, * 4 GB physical address range can be used at a time. * * Supporting 64-bit DMA on this hardware is more trouble than it's * worth. It is far easier to limit to 32-bit DMA than update * various kernel subsystems to support the mechanics required by a * fixed-high-32-bit system. */ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) || (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) { dev_err(&pdev->dev, "No usable DMA configuration,aborting\n"); goto err_dma; } err = pci_request_regions(pdev, atl1c_driver_name); if (err) { dev_err(&pdev->dev, "cannot obtain PCI resources\n"); goto err_pci_reg; } pci_set_master(pdev); netdev = alloc_etherdev(sizeof(struct atl1c_adapter)); if (netdev == NULL) { err = -ENOMEM; goto err_alloc_etherdev; } err = atl1c_init_netdev(netdev, pdev); if (err) { dev_err(&pdev->dev, "init netdevice failed\n"); goto err_init_netdev; } adapter = netdev_priv(netdev); adapter->bd_number = cards_found; adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.adapter = adapter; adapter->msg_enable = netif_msg_init(-1, atl1c_default_msg); adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); if (!adapter->hw.hw_addr) { err = -EIO; dev_err(&pdev->dev, "cannot map device registers\n"); goto err_ioremap; } /* init mii data */ adapter->mii.dev = netdev; adapter->mii.mdio_read = atl1c_mdio_read; adapter->mii.mdio_write = atl1c_mdio_write; adapter->mii.phy_id_mask = 0x1f; adapter->mii.reg_num_mask = MDIO_CTRL_REG_MASK; netif_napi_add(netdev, &adapter->napi, atl1c_clean, 64); setup_timer(&adapter->phy_config_timer, atl1c_phy_config, (unsigned long)adapter); /* setup the private structure */ err = atl1c_sw_init(adapter); if (err) { dev_err(&pdev->dev, "net device private data init failed\n"); goto err_sw_init; } atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE); /* Init GPHY as early as possible due to power saving issue */ atl1c_phy_reset(&adapter->hw); err = atl1c_reset_mac(&adapter->hw); if (err) { err = -EIO; goto err_reset; } /* reset the controller to * put the device in a known good starting state */ err = atl1c_phy_init(&adapter->hw); if (err) { err = -EIO; goto err_reset; } if (atl1c_read_mac_addr(&adapter->hw)) { /* got a random MAC address, set NET_ADDR_RANDOM to netdev */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type |= NET_ADDR_RANDOM; #endif } memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); if (netif_msg_probe(adapter)) dev_dbg(&pdev->dev, "mac address : %pM\n", adapter->hw.mac_addr); atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr); INIT_WORK(&adapter->common_task, atl1c_common_task); adapter->work_event = 0; err = register_netdev(netdev); if (err) { dev_err(&pdev->dev, "register netdevice failed\n"); goto err_register; } if (netif_msg_probe(adapter)) dev_info(&pdev->dev, "version %s\n", ATL1C_DRV_VERSION); cards_found++; return 0; err_reset: err_register: err_sw_init: iounmap(adapter->hw.hw_addr); err_init_netdev: err_ioremap: free_netdev(netdev); err_alloc_etherdev: pci_release_regions(pdev); err_pci_reg: err_dma: pci_disable_device(pdev); return err; } /** * atl1c_remove - Device Removal Routine * @pdev: PCI device information struct * * atl1c_remove is called by the PCI subsystem to alert the driver * that it should release a PCI device. The could be caused by a * Hot-Plug event, or because the driver is going to be removed from * memory. */ static void __devexit atl1c_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1c_adapter *adapter = netdev_priv(netdev); unregister_netdev(netdev); /* restore permanent address */ atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.perm_mac_addr); atl1c_phy_disable(&adapter->hw); iounmap(adapter->hw.hw_addr); pci_release_regions(pdev); pci_disable_device(pdev); free_netdev(netdev); } /** * atl1c_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device * @state: The current pci connection state * * This function is called after a PCI bus error affecting * this device has been detected. */ static pci_ers_result_t atl1c_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1c_adapter *adapter = netdev_priv(netdev); netif_device_detach(netdev); if (state == pci_channel_io_perm_failure) return PCI_ERS_RESULT_DISCONNECT; if (netif_running(netdev)) atl1c_down(adapter); pci_disable_device(pdev); /* Request a slot slot reset. */ return PCI_ERS_RESULT_NEED_RESET; } /** * atl1c_io_slot_reset - called after the pci bus has been reset. * @pdev: Pointer to PCI device * * Restart the card from scratch, as if from a cold-boot. Implementation * resembles the first-half of the e1000_resume routine. */ static pci_ers_result_t atl1c_io_slot_reset(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1c_adapter *adapter = netdev_priv(netdev); if (pci_enable_device(pdev)) { if (netif_msg_hw(adapter)) dev_err(&pdev->dev, "Cannot re-enable PCI device after reset\n"); return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); atl1c_reset_mac(&adapter->hw); return PCI_ERS_RESULT_RECOVERED; } /** * atl1c_io_resume - called when traffic can start flowing again. * @pdev: Pointer to PCI device * * This callback is called when the error recovery driver tells us that * its OK to resume normal operation. Implementation resembles the * second-half of the atl1c_resume routine. */ static void atl1c_io_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct atl1c_adapter *adapter = netdev_priv(netdev); if (netif_running(netdev)) { if (atl1c_up(adapter)) { if (netif_msg_hw(adapter)) dev_err(&pdev->dev, "Cannot bring device back up after reset\n"); return; } } netif_device_attach(netdev); } static const struct pci_error_handlers atl1c_err_handler = { .error_detected = atl1c_io_error_detected, .slot_reset = atl1c_io_slot_reset, .resume = atl1c_io_resume, }; compat_pci_suspend(atl1c_suspend) compat_pci_resume(atl1c_resume) static SIMPLE_DEV_PM_OPS(atl1c_pm_ops, atl1c_suspend, atl1c_resume); static struct pci_driver atl1c_driver = { .name = atl1c_driver_name, .id_table = atl1c_pci_tbl, .probe = atl1c_probe, .remove = __devexit_p(atl1c_remove), .shutdown = atl1c_shutdown, .err_handler = &atl1c_err_handler, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &atl1c_pm_ops, #elif defined(CONFIG_PM_SLEEP) .suspend = atl1c_suspend_compat, .resume = atl1c_resume_compat, #endif }; /** * atl1c_init_module - Driver Registration Routine * * atl1c_init_module is the first routine called when the driver is * loaded. All it does is register with the PCI subsystem. */ static int __init atl1c_init_module(void) { return pci_register_driver(&atl1c_driver); } /** * atl1c_exit_module - Driver Exit Cleanup Routine * * atl1c_exit_module is called just before the driver is removed * from memory. */ static void __exit atl1c_exit_module(void) { pci_unregister_driver(&atl1c_driver); } module_init(atl1c_init_module); module_exit(atl1c_exit_module); compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c0000644000175000017500000002232112026211315026021 0ustar mcgrofmcgrof/* * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include "atl1c.h" static int atl1c_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) ecmd->supported |= SUPPORTED_1000baseT_Full; ecmd->advertising = ADVERTISED_TP; ecmd->advertising |= hw->autoneg_advertised; ecmd->port = PORT_TP; ecmd->phy_address = 0; ecmd->transceiver = XCVR_INTERNAL; if (adapter->link_speed != SPEED_0) { ethtool_cmd_speed_set(ecmd, adapter->link_speed); if (adapter->link_duplex == FULL_DUPLEX) ecmd->duplex = DUPLEX_FULL; else ecmd->duplex = DUPLEX_HALF; } else { ethtool_cmd_speed_set(ecmd, -1); ecmd->duplex = -1; } ecmd->autoneg = AUTONEG_ENABLE; return 0; } static int atl1c_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; u16 autoneg_advertised; while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); if (ecmd->autoneg == AUTONEG_ENABLE) { autoneg_advertised = ADVERTISED_Autoneg; } else { u32 speed = ethtool_cmd_speed(ecmd); if (speed == SPEED_1000) { if (ecmd->duplex != DUPLEX_FULL) { if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "1000M half is invalid\n"); clear_bit(__AT_RESETTING, &adapter->flags); return -EINVAL; } autoneg_advertised = ADVERTISED_1000baseT_Full; } else if (speed == SPEED_100) { if (ecmd->duplex == DUPLEX_FULL) autoneg_advertised = ADVERTISED_100baseT_Full; else autoneg_advertised = ADVERTISED_100baseT_Half; } else { if (ecmd->duplex == DUPLEX_FULL) autoneg_advertised = ADVERTISED_10baseT_Full; else autoneg_advertised = ADVERTISED_10baseT_Half; } } if (hw->autoneg_advertised != autoneg_advertised) { hw->autoneg_advertised = autoneg_advertised; if (atl1c_restart_autoneg(hw) != 0) { if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "ethtool speed/duplex setting failed\n"); clear_bit(__AT_RESETTING, &adapter->flags); return -EINVAL; } } clear_bit(__AT_RESETTING, &adapter->flags); return 0; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) static u32 atl1c_get_tx_csum(struct net_device *netdev) { return (netdev->features & NETIF_F_HW_CSUM) != 0; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ static u32 atl1c_get_msglevel(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); return adapter->msg_enable; } static void atl1c_set_msglevel(struct net_device *netdev, u32 data) { struct atl1c_adapter *adapter = netdev_priv(netdev); adapter->msg_enable = data; } static int atl1c_get_regs_len(struct net_device *netdev) { return AT_REGS_LEN; } static void atl1c_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; u32 *regs_buff = p; u16 phy_data; memset(p, 0, AT_REGS_LEN); regs->version = 1; AT_READ_REG(hw, REG_PM_CTRL, p++); AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++); AT_READ_REG(hw, REG_TWSI_CTRL, p++); AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++); AT_READ_REG(hw, REG_MASTER_CTRL, p++); AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++); AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++); AT_READ_REG(hw, REG_GPHY_CTRL, p++); AT_READ_REG(hw, REG_LINK_CTRL, p++); AT_READ_REG(hw, REG_IDLE_STATUS, p++); AT_READ_REG(hw, REG_MDIO_CTRL, p++); AT_READ_REG(hw, REG_SERDES, p++); AT_READ_REG(hw, REG_MAC_CTRL, p++); AT_READ_REG(hw, REG_MAC_IPG_IFG, p++); AT_READ_REG(hw, REG_MAC_STA_ADDR, p++); AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++); AT_READ_REG(hw, REG_RX_HASH_TABLE, p++); AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++); AT_READ_REG(hw, REG_RXQ_CTRL, p++); AT_READ_REG(hw, REG_TXQ_CTRL, p++); AT_READ_REG(hw, REG_MTU, p++); AT_READ_REG(hw, REG_WOL_CTRL, p++); atl1c_read_phy_reg(hw, MII_BMCR, &phy_data); regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data; atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data; } static int atl1c_get_eeprom_len(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); if (atl1c_check_eeprom_exist(&adapter->hw)) return AT_EEPROM_LEN; else return 0; } static int atl1c_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, u8 *bytes) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; u32 *eeprom_buff; int first_dword, last_dword; int ret_val = 0; int i; if (eeprom->len == 0) return -EINVAL; if (!atl1c_check_eeprom_exist(hw)) /* not exist */ return -EINVAL; eeprom->magic = adapter->pdev->vendor | (adapter->pdev->device << 16); first_dword = eeprom->offset >> 2; last_dword = (eeprom->offset + eeprom->len - 1) >> 2; eeprom_buff = kmalloc(sizeof(u32) * (last_dword - first_dword + 1), GFP_KERNEL); if (eeprom_buff == NULL) return -ENOMEM; for (i = first_dword; i < last_dword; i++) { if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { kfree(eeprom_buff); return -EIO; } } memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), eeprom->len); kfree(eeprom_buff); return ret_val; return 0; } static void atl1c_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct atl1c_adapter *adapter = netdev_priv(netdev); strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, atl1c_driver_version, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = 0; drvinfo->testinfo_len = 0; drvinfo->regdump_len = atl1c_get_regs_len(netdev); drvinfo->eedump_len = atl1c_get_eeprom_len(netdev); } static void atl1c_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl1c_adapter *adapter = netdev_priv(netdev); wol->supported = WAKE_MAGIC | WAKE_PHY; wol->wolopts = 0; if (adapter->wol & AT_WUFC_EX) wol->wolopts |= WAKE_UCAST; if (adapter->wol & AT_WUFC_MC) wol->wolopts |= WAKE_MCAST; if (adapter->wol & AT_WUFC_BC) wol->wolopts |= WAKE_BCAST; if (adapter->wol & AT_WUFC_MAG) wol->wolopts |= WAKE_MAGIC; if (adapter->wol & AT_WUFC_LNKC) wol->wolopts |= WAKE_PHY; } static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct atl1c_adapter *adapter = netdev_priv(netdev); if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) return -EOPNOTSUPP; /* these settings will always override what we currently have */ adapter->wol = 0; if (wol->wolopts & WAKE_MAGIC) adapter->wol |= AT_WUFC_MAG; if (wol->wolopts & WAKE_PHY) adapter->wol |= AT_WUFC_LNKC; device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); return 0; } static int atl1c_nway_reset(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); if (netif_running(netdev)) atl1c_reinit_locked(adapter); return 0; } static const struct ethtool_ops atl1c_ethtool_ops = { .get_settings = atl1c_get_settings, .set_settings = atl1c_set_settings, .get_drvinfo = atl1c_get_drvinfo, .get_regs_len = atl1c_get_regs_len, .get_regs = atl1c_get_regs, .get_wol = atl1c_get_wol, .set_wol = atl1c_set_wol, .get_msglevel = atl1c_get_msglevel, .set_msglevel = atl1c_set_msglevel, .nway_reset = atl1c_nway_reset, .get_link = ethtool_op_get_link, .get_eeprom_len = atl1c_get_eeprom_len, .get_eeprom = atl1c_get_eeprom, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) .get_tx_csum = atl1c_get_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; void atl1c_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops); } compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h0000644000175000017500000011010112026211315024760 0ustar mcgrofmcgrof/* * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _ATL1C_HW_H_ #define _ATL1C_HW_H_ #include #include #define FIELD_GETX(_x, _name) ((_x) >> (_name##_SHIFT) & (_name##_MASK)) #define FIELD_SETX(_x, _name, _v) \ (((_x) & ~((_name##_MASK) << (_name##_SHIFT))) |\ (((_v) & (_name##_MASK)) << (_name##_SHIFT))) #define FIELDX(_name, _v) (((_v) & (_name##_MASK)) << (_name##_SHIFT)) struct atl1c_adapter; struct atl1c_hw; /* function prototype */ void atl1c_phy_disable(struct atl1c_hw *hw); void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr); int atl1c_phy_reset(struct atl1c_hw *hw); int atl1c_read_mac_addr(struct atl1c_hw *hw); int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex); u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr); void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value); int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data); int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data); bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value); int atl1c_phy_init(struct atl1c_hw *hw); int atl1c_check_eeprom_exist(struct atl1c_hw *hw); int atl1c_restart_autoneg(struct atl1c_hw *hw); int atl1c_phy_to_ps_link(struct atl1c_hw *hw); int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc); bool atl1c_wait_mdio_idle(struct atl1c_hw *hw); void atl1c_stop_phy_polling(struct atl1c_hw *hw); void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel); int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, u16 reg, u16 *phy_data); int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, u16 reg, u16 phy_data); int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr, u16 reg_addr, u16 *phy_data); int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr, u16 reg_addr, u16 phy_data); int atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data); int atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data); void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed); /* hw-ids */ #define PCI_DEVICE_ID_ATTANSIC_L2C 0x1062 #define PCI_DEVICE_ID_ATTANSIC_L1C 0x1063 #define PCI_DEVICE_ID_ATHEROS_L2C_B 0x2060 /* AR8152 v1.1 Fast 10/100 */ #define PCI_DEVICE_ID_ATHEROS_L2C_B2 0x2062 /* AR8152 v2.0 Fast 10/100 */ #define PCI_DEVICE_ID_ATHEROS_L1D 0x1073 /* AR8151 v1.0 Gigabit 1000 */ #define PCI_DEVICE_ID_ATHEROS_L1D_2_0 0x1083 /* AR8151 v2.0 Gigabit 1000 */ #define L2CB_V10 0xc0 #define L2CB_V11 0xc1 #define L2CB_V20 0xc0 #define L2CB_V21 0xc1 /* register definition */ #define REG_DEVICE_CAP 0x5C #define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7 #define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0 #define DEVICE_CTRL_MAXRRS_MIN 2 #define REG_LINK_CTRL 0x68 #define LINK_CTRL_L0S_EN 0x01 #define LINK_CTRL_L1_EN 0x02 #define LINK_CTRL_EXT_SYNC 0x80 #define REG_PCIE_IND_ACC_ADDR 0x80 #define REG_PCIE_IND_ACC_DATA 0x84 #define REG_DEV_SERIALNUM_CTRL 0x200 #define REG_DEV_MAC_SEL_MASK 0x0 /* 0:EUI; 1:MAC */ #define REG_DEV_MAC_SEL_SHIFT 0 #define REG_DEV_SERIAL_NUM_EN_MASK 0x1 #define REG_DEV_SERIAL_NUM_EN_SHIFT 1 #define REG_TWSI_CTRL 0x218 #define TWSI_CTLR_FREQ_MASK 0x3UL #define TWSI_CTRL_FREQ_SHIFT 24 #define TWSI_CTRL_FREQ_100K 0 #define TWSI_CTRL_FREQ_200K 1 #define TWSI_CTRL_FREQ_300K 2 #define TWSI_CTRL_FREQ_400K 3 #define TWSI_CTRL_LD_EXIST BIT(23) #define TWSI_CTRL_HW_LDSTAT BIT(12) /* 0:finish,1:in progress */ #define TWSI_CTRL_SW_LDSTART BIT(11) #define TWSI_CTRL_LD_OFFSET_MASK 0xFF #define TWSI_CTRL_LD_OFFSET_SHIFT 0 #define REG_PCIE_DEV_MISC_CTRL 0x21C #define PCIE_DEV_MISC_EXT_PIPE 0x2 #define PCIE_DEV_MISC_RETRY_BUFDIS 0x1 #define PCIE_DEV_MISC_SPIROM_EXIST 0x4 #define PCIE_DEV_MISC_SERDES_ENDIAN 0x8 #define PCIE_DEV_MISC_SERDES_SEL_DIN 0x10 #define REG_PCIE_PHYMISC 0x1000 #define PCIE_PHYMISC_FORCE_RCV_DET BIT(2) #define PCIE_PHYMISC_NFTS_MASK 0xFFUL #define PCIE_PHYMISC_NFTS_SHIFT 16 #define REG_PCIE_PHYMISC2 0x1004 #define PCIE_PHYMISC2_L0S_TH_MASK 0x3UL #define PCIE_PHYMISC2_L0S_TH_SHIFT 18 #define L2CB1_PCIE_PHYMISC2_L0S_TH 3 #define PCIE_PHYMISC2_CDR_BW_MASK 0x3UL #define PCIE_PHYMISC2_CDR_BW_SHIFT 16 #define L2CB1_PCIE_PHYMISC2_CDR_BW 3 #define REG_TWSI_DEBUG 0x1108 #define TWSI_DEBUG_DEV_EXIST BIT(29) #define REG_DMA_DBG 0x1114 #define DMA_DBG_VENDOR_MSG BIT(0) #define REG_EEPROM_CTRL 0x12C0 #define EEPROM_CTRL_DATA_HI_MASK 0xFFFF #define EEPROM_CTRL_DATA_HI_SHIFT 0 #define EEPROM_CTRL_ADDR_MASK 0x3FF #define EEPROM_CTRL_ADDR_SHIFT 16 #define EEPROM_CTRL_ACK 0x40000000 #define EEPROM_CTRL_RW 0x80000000 #define REG_EEPROM_DATA_LO 0x12C4 #define REG_OTP_CTRL 0x12F0 #define OTP_CTRL_CLK_EN BIT(1) #define REG_PM_CTRL 0x12F8 #define PM_CTRL_HOTRST BIT(31) #define PM_CTRL_MAC_ASPM_CHK BIT(30) /* L0s/L1 dis by MAC based on * thrghput(setting in 15A0) */ #define PM_CTRL_SA_DLY_EN BIT(29) #define PM_CTRL_L0S_BUFSRX_EN BIT(28) #define PM_CTRL_LCKDET_TIMER_MASK 0xFUL #define PM_CTRL_LCKDET_TIMER_SHIFT 24 #define PM_CTRL_LCKDET_TIMER_DEF 0xC #define PM_CTRL_PM_REQ_TIMER_MASK 0xFUL #define PM_CTRL_PM_REQ_TIMER_SHIFT 20 /* pm_request_l1 time > @ * ->L0s not L1 */ #define PM_CTRL_PM_REQ_TO_DEF 0xF #define PMCTRL_TXL1_AFTER_L0S BIT(19) /* l1dv2.0+ */ #define L1D_PMCTRL_L1_ENTRY_TM_MASK 7UL /* l1dv2.0+, 3bits */ #define L1D_PMCTRL_L1_ENTRY_TM_SHIFT 16 #define L1D_PMCTRL_L1_ENTRY_TM_DIS 0 #define L1D_PMCTRL_L1_ENTRY_TM_2US 1 #define L1D_PMCTRL_L1_ENTRY_TM_4US 2 #define L1D_PMCTRL_L1_ENTRY_TM_8US 3 #define L1D_PMCTRL_L1_ENTRY_TM_16US 4 #define L1D_PMCTRL_L1_ENTRY_TM_24US 5 #define L1D_PMCTRL_L1_ENTRY_TM_32US 6 #define L1D_PMCTRL_L1_ENTRY_TM_63US 7 #define PM_CTRL_L1_ENTRY_TIMER_MASK 0xFUL /* l1C 4bits */ #define PM_CTRL_L1_ENTRY_TIMER_SHIFT 16 #define L2CB1_PM_CTRL_L1_ENTRY_TM 7 #define L1C_PM_CTRL_L1_ENTRY_TM 0xF #define PM_CTRL_RCVR_WT_TIMER BIT(15) /* 1:1us, 0:2ms */ #define PM_CTRL_CLK_PWM_VER1_1 BIT(14) /* 0:1.0a,1:1.1 */ #define PM_CTRL_CLK_SWH_L1 BIT(13) /* en pcie clk sw in L1 */ #define PM_CTRL_ASPM_L0S_EN BIT(12) #define PM_CTRL_RXL1_AFTER_L0S BIT(11) /* l1dv2.0+ */ #define L1D_PMCTRL_L0S_TIMER_MASK 7UL /* l1d2.0+, 3bits*/ #define L1D_PMCTRL_L0S_TIMER_SHIFT 8 #define PM_CTRL_L0S_ENTRY_TIMER_MASK 0xFUL /* l1c, 4bits */ #define PM_CTRL_L0S_ENTRY_TIMER_SHIFT 8 #define PM_CTRL_SERDES_BUFS_RX_L1_EN BIT(7) #define PM_CTRL_SERDES_PD_EX_L1 BIT(6) /* power down serdes rx */ #define PM_CTRL_SERDES_PLL_L1_EN BIT(5) #define PM_CTRL_SERDES_L1_EN BIT(4) #define PM_CTRL_ASPM_L1_EN BIT(3) #define PM_CTRL_CLK_REQ_EN BIT(2) #define PM_CTRL_RBER_EN BIT(1) #define PM_CTRL_SPRSDWER_EN BIT(0) #define REG_LTSSM_ID_CTRL 0x12FC #define LTSSM_ID_EN_WRO 0x1000 /* Selene Master Control Register */ #define REG_MASTER_CTRL 0x1400 #define MASTER_CTRL_OTP_SEL BIT(31) #define MASTER_DEV_NUM_MASK 0x7FUL #define MASTER_DEV_NUM_SHIFT 24 #define MASTER_REV_NUM_MASK 0xFFUL #define MASTER_REV_NUM_SHIFT 16 #define MASTER_CTRL_INT_RDCLR BIT(14) #define MASTER_CTRL_CLK_SEL_DIS BIT(12) /* 1:alwys sel pclk from * serdes, not sw to 25M */ #define MASTER_CTRL_RX_ITIMER_EN BIT(11) /* IRQ MODURATION FOR RX */ #define MASTER_CTRL_TX_ITIMER_EN BIT(10) /* MODURATION FOR TX/RX */ #define MASTER_CTRL_MANU_INT BIT(9) /* SOFT MANUAL INT */ #define MASTER_CTRL_MANUTIMER_EN BIT(8) #define MASTER_CTRL_SA_TIMER_EN BIT(7) /* SYS ALIVE TIMER EN */ #define MASTER_CTRL_OOB_DIS BIT(6) /* OUT OF BOX DIS */ #define MASTER_CTRL_WAKEN_25M BIT(5) /* WAKE WO. PCIE CLK */ #define MASTER_CTRL_BERT_START BIT(4) #define MASTER_PCIE_TSTMOD_MASK 3UL #define MASTER_PCIE_TSTMOD_SHIFT 2 #define MASTER_PCIE_RST BIT(1) #define MASTER_CTRL_SOFT_RST BIT(0) /* RST MAC & DMA */ #define DMA_MAC_RST_TO 50 /* Timer Initial Value Register */ #define REG_MANUAL_TIMER_INIT 0x1404 /* IRQ ModeratorTimer Initial Value Register */ #define REG_IRQ_MODRT_TIMER_INIT 0x1408 #define IRQ_MODRT_TIMER_MASK 0xffff #define IRQ_MODRT_TX_TIMER_SHIFT 0 #define IRQ_MODRT_RX_TIMER_SHIFT 16 #define REG_GPHY_CTRL 0x140C #define GPHY_CTRL_ADDR_MASK 0x1FUL #define GPHY_CTRL_ADDR_SHIFT 19 #define GPHY_CTRL_BP_VLTGSW BIT(18) #define GPHY_CTRL_100AB_EN BIT(17) #define GPHY_CTRL_10AB_EN BIT(16) #define GPHY_CTRL_PHY_PLL_BYPASS BIT(15) #define GPHY_CTRL_PWDOWN_HW BIT(14) /* affect MAC&PHY, to low pw */ #define GPHY_CTRL_PHY_PLL_ON BIT(13) /* 1:pll always on, 0:can sw */ #define GPHY_CTRL_SEL_ANA_RST BIT(12) #define GPHY_CTRL_HIB_PULSE BIT(11) #define GPHY_CTRL_HIB_EN BIT(10) #define GPHY_CTRL_GIGA_DIS BIT(9) #define GPHY_CTRL_PHY_IDDQ_DIS BIT(8) /* pw on RST */ #define GPHY_CTRL_PHY_IDDQ BIT(7) /* bit8 affect bit7 while rb */ #define GPHY_CTRL_LPW_EXIT BIT(6) #define GPHY_CTRL_GATE_25M_EN BIT(5) #define GPHY_CTRL_REV_ANEG BIT(4) #define GPHY_CTRL_ANEG_NOW BIT(3) #define GPHY_CTRL_LED_MODE BIT(2) #define GPHY_CTRL_RTL_MODE BIT(1) #define GPHY_CTRL_EXT_RESET BIT(0) /* 1:out of DSP RST status */ #define GPHY_CTRL_EXT_RST_TO 80 /* 800us atmost */ #define GPHY_CTRL_CLS (\ GPHY_CTRL_LED_MODE |\ GPHY_CTRL_100AB_EN |\ GPHY_CTRL_PHY_PLL_ON) /* Block IDLE Status Register */ #define REG_IDLE_STATUS 0x1410 #define IDLE_STATUS_SFORCE_MASK 0xFUL #define IDLE_STATUS_SFORCE_SHIFT 14 #define IDLE_STATUS_CALIB_DONE BIT(13) #define IDLE_STATUS_CALIB_RES_MASK 0x1FUL #define IDLE_STATUS_CALIB_RES_SHIFT 8 #define IDLE_STATUS_CALIBERR_MASK 0xFUL #define IDLE_STATUS_CALIBERR_SHIFT 4 #define IDLE_STATUS_TXQ_BUSY BIT(3) #define IDLE_STATUS_RXQ_BUSY BIT(2) #define IDLE_STATUS_TXMAC_BUSY BIT(1) #define IDLE_STATUS_RXMAC_BUSY BIT(0) #define IDLE_STATUS_MASK (\ IDLE_STATUS_TXQ_BUSY |\ IDLE_STATUS_RXQ_BUSY |\ IDLE_STATUS_TXMAC_BUSY |\ IDLE_STATUS_RXMAC_BUSY) /* MDIO Control Register */ #define REG_MDIO_CTRL 0x1414 #define MDIO_CTRL_MODE_EXT BIT(30) #define MDIO_CTRL_POST_READ BIT(29) #define MDIO_CTRL_AP_EN BIT(28) #define MDIO_CTRL_BUSY BIT(27) #define MDIO_CTRL_CLK_SEL_MASK 0x7UL #define MDIO_CTRL_CLK_SEL_SHIFT 24 #define MDIO_CTRL_CLK_25_4 0 /* 25MHz divide 4 */ #define MDIO_CTRL_CLK_25_6 2 #define MDIO_CTRL_CLK_25_8 3 #define MDIO_CTRL_CLK_25_10 4 #define MDIO_CTRL_CLK_25_32 5 #define MDIO_CTRL_CLK_25_64 6 #define MDIO_CTRL_CLK_25_128 7 #define MDIO_CTRL_START BIT(23) #define MDIO_CTRL_SPRES_PRMBL BIT(22) #define MDIO_CTRL_OP_READ BIT(21) /* 1:read, 0:write */ #define MDIO_CTRL_REG_MASK 0x1FUL #define MDIO_CTRL_REG_SHIFT 16 #define MDIO_CTRL_DATA_MASK 0xFFFFUL #define MDIO_CTRL_DATA_SHIFT 0 #define MDIO_MAX_AC_TO 120 /* 1.2ms timeout for slow clk */ /* for extension reg access */ #define REG_MDIO_EXTN 0x1448 #define MDIO_EXTN_PORTAD_MASK 0x1FUL #define MDIO_EXTN_PORTAD_SHIFT 21 #define MDIO_EXTN_DEVAD_MASK 0x1FUL #define MDIO_EXTN_DEVAD_SHIFT 16 #define MDIO_EXTN_REG_MASK 0xFFFFUL #define MDIO_EXTN_REG_SHIFT 0 /* BIST Control and Status Register0 (for the Packet Memory) */ #define REG_BIST0_CTRL 0x141c #define BIST0_NOW 0x1 #define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is * un-repairable because * it has address decoder * failure or more than 1 cell * stuck-to-x failure */ #define BIST0_FUSE_FLAG 0x4 /* BIST Control and Status Register1(for the retry buffer of PCI Express) */ #define REG_BIST1_CTRL 0x1420 #define BIST1_NOW 0x1 #define BIST1_SRAM_FAIL 0x2 #define BIST1_FUSE_FLAG 0x4 /* SerDes Lock Detect Control and Status Register */ #define REG_SERDES 0x1424 #define SERDES_PHY_CLK_SLOWDOWN BIT(18) #define SERDES_MAC_CLK_SLOWDOWN BIT(17) #define SERDES_SELFB_PLL_MASK 0x3UL #define SERDES_SELFB_PLL_SHIFT 14 #define SERDES_PHYCLK_SEL_GTX BIT(13) /* 1:gtx_clk, 0:25M */ #define SERDES_PCIECLK_SEL_SRDS BIT(12) /* 1:serdes,0:25M */ #define SERDES_BUFS_RX_EN BIT(11) #define SERDES_PD_RX BIT(10) #define SERDES_PLL_EN BIT(9) #define SERDES_EN BIT(8) #define SERDES_SELFB_PLL_SEL_CSR BIT(6) /* 0:state-machine,1:csr */ #define SERDES_SELFB_PLL_CSR_MASK 0x3UL #define SERDES_SELFB_PLL_CSR_SHIFT 4 #define SERDES_SELFB_PLL_CSR_4 3 /* 4-12% OV-CLK */ #define SERDES_SELFB_PLL_CSR_0 2 /* 0-4% OV-CLK */ #define SERDES_SELFB_PLL_CSR_12 1 /* 12-18% OV-CLK */ #define SERDES_SELFB_PLL_CSR_18 0 /* 18-25% OV-CLK */ #define SERDES_VCO_SLOW BIT(3) #define SERDES_VCO_FAST BIT(2) #define SERDES_LOCK_DETECT_EN BIT(1) #define SERDES_LOCK_DETECT BIT(0) #define REG_LPI_DECISN_TIMER 0x143C #define L2CB_LPI_DESISN_TIMER 0x7D00 #define REG_LPI_CTRL 0x1440 #define LPI_CTRL_CHK_DA BIT(31) #define LPI_CTRL_ENH_TO_MASK 0x1FFFUL #define LPI_CTRL_ENH_TO_SHIFT 12 #define LPI_CTRL_ENH_TH_MASK 0x1FUL #define LPI_CTRL_ENH_TH_SHIFT 6 #define LPI_CTRL_ENH_EN BIT(5) #define LPI_CTRL_CHK_RX BIT(4) #define LPI_CTRL_CHK_STATE BIT(3) #define LPI_CTRL_GMII BIT(2) #define LPI_CTRL_TO_PHY BIT(1) #define LPI_CTRL_EN BIT(0) #define REG_LPI_WAIT 0x1444 #define LPI_WAIT_TIMER_MASK 0xFFFFUL #define LPI_WAIT_TIMER_SHIFT 0 /* MAC Control Register */ #define REG_MAC_CTRL 0x1480 #define MAC_CTRL_SPEED_MODE_SW BIT(30) /* 0:phy,1:sw */ #define MAC_CTRL_HASH_ALG_CRC32 BIT(29) /* 1:legacy,0:lw_5b */ #define MAC_CTRL_SINGLE_PAUSE_EN BIT(28) #define MAC_CTRL_DBG BIT(27) #define MAC_CTRL_BC_EN BIT(26) #define MAC_CTRL_MC_ALL_EN BIT(25) #define MAC_CTRL_RX_CHKSUM_EN BIT(24) #define MAC_CTRL_TX_HUGE BIT(23) #define MAC_CTRL_DBG_TX_BKPRESURE BIT(22) #define MAC_CTRL_SPEED_MASK 3UL #define MAC_CTRL_SPEED_SHIFT 20 #define MAC_CTRL_SPEED_10_100 1 #define MAC_CTRL_SPEED_1000 2 #define MAC_CTRL_TX_SIMURST BIT(19) #define MAC_CTRL_SCNT BIT(17) #define MAC_CTRL_TX_PAUSE BIT(16) #define MAC_CTRL_PROMIS_EN BIT(15) #define MAC_CTRL_RMV_VLAN BIT(14) #define MAC_CTRL_PRMLEN_MASK 0xFUL #define MAC_CTRL_PRMLEN_SHIFT 10 #define MAC_CTRL_HUGE_EN BIT(9) #define MAC_CTRL_LENCHK BIT(8) #define MAC_CTRL_PAD BIT(7) #define MAC_CTRL_ADD_CRC BIT(6) #define MAC_CTRL_DUPLX BIT(5) #define MAC_CTRL_LOOPBACK BIT(4) #define MAC_CTRL_RX_FLOW BIT(3) #define MAC_CTRL_TX_FLOW BIT(2) #define MAC_CTRL_RX_EN BIT(1) #define MAC_CTRL_TX_EN BIT(0) /* MAC IPG/IFG Control Register */ #define REG_MAC_IPG_IFG 0x1484 #define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back * inter-packet gap. The * default is 96-bit time */ #define MAC_IPG_IFG_IPGT_MASK 0x7f #define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to * enforce in between RX frames */ #define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */ #define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */ #define MAC_IPG_IFG_IPGR1_MASK 0x7f #define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */ #define MAC_IPG_IFG_IPGR2_MASK 0x7f /* MAC STATION ADDRESS */ #define REG_MAC_STA_ADDR 0x1488 /* Hash table for multicast address */ #define REG_RX_HASH_TABLE 0x1490 /* MAC Half-Duplex Control Register */ #define REG_MAC_HALF_DUPLX_CTRL 0x1498 #define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */ #define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff #define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 #define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf #define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 #define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 #define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* No back-off on backpressure, * immediately start the * transmission after back pressure */ #define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */ #define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */ #define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf #define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */ #define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */ /* Maximum Frame Length Control Register */ #define REG_MTU 0x149c /* Wake-On-Lan control register */ #define REG_WOL_CTRL 0x14a0 #define WOL_PT7_MATCH BIT(31) #define WOL_PT6_MATCH BIT(30) #define WOL_PT5_MATCH BIT(29) #define WOL_PT4_MATCH BIT(28) #define WOL_PT3_MATCH BIT(27) #define WOL_PT2_MATCH BIT(26) #define WOL_PT1_MATCH BIT(25) #define WOL_PT0_MATCH BIT(24) #define WOL_PT7_EN BIT(23) #define WOL_PT6_EN BIT(22) #define WOL_PT5_EN BIT(21) #define WOL_PT4_EN BIT(20) #define WOL_PT3_EN BIT(19) #define WOL_PT2_EN BIT(18) #define WOL_PT1_EN BIT(17) #define WOL_PT0_EN BIT(16) #define WOL_LNKCHG_ST BIT(10) #define WOL_MAGIC_ST BIT(9) #define WOL_PATTERN_ST BIT(8) #define WOL_OOB_EN BIT(6) #define WOL_LINK_CHG_PME_EN BIT(5) #define WOL_LINK_CHG_EN BIT(4) #define WOL_MAGIC_PME_EN BIT(3) #define WOL_MAGIC_EN BIT(2) #define WOL_PATTERN_PME_EN BIT(1) #define WOL_PATTERN_EN BIT(0) /* WOL Length ( 2 DWORD ) */ #define REG_WOL_PTLEN1 0x14A4 #define WOL_PTLEN1_3_MASK 0xFFUL #define WOL_PTLEN1_3_SHIFT 24 #define WOL_PTLEN1_2_MASK 0xFFUL #define WOL_PTLEN1_2_SHIFT 16 #define WOL_PTLEN1_1_MASK 0xFFUL #define WOL_PTLEN1_1_SHIFT 8 #define WOL_PTLEN1_0_MASK 0xFFUL #define WOL_PTLEN1_0_SHIFT 0 #define REG_WOL_PTLEN2 0x14A8 #define WOL_PTLEN2_7_MASK 0xFFUL #define WOL_PTLEN2_7_SHIFT 24 #define WOL_PTLEN2_6_MASK 0xFFUL #define WOL_PTLEN2_6_SHIFT 16 #define WOL_PTLEN2_5_MASK 0xFFUL #define WOL_PTLEN2_5_SHIFT 8 #define WOL_PTLEN2_4_MASK 0xFFUL #define WOL_PTLEN2_4_SHIFT 0 /* Internal SRAM Partition Register */ #define RFDX_HEAD_ADDR_MASK 0x03FF #define RFDX_HARD_ADDR_SHIFT 0 #define RFDX_TAIL_ADDR_MASK 0x03FF #define RFDX_TAIL_ADDR_SHIFT 16 #define REG_SRAM_RFD0_INFO 0x1500 #define REG_SRAM_RFD1_INFO 0x1504 #define REG_SRAM_RFD2_INFO 0x1508 #define REG_SRAM_RFD3_INFO 0x150C #define REG_RFD_NIC_LEN 0x1510 /* In 8-bytes */ #define RFD_NIC_LEN_MASK 0x03FF #define REG_SRAM_TRD_ADDR 0x1518 #define TPD_HEAD_ADDR_MASK 0x03FF #define TPD_HEAD_ADDR_SHIFT 0 #define TPD_TAIL_ADDR_MASK 0x03FF #define TPD_TAIL_ADDR_SHIFT 16 #define REG_SRAM_TRD_LEN 0x151C /* In 8-bytes */ #define TPD_NIC_LEN_MASK 0x03FF #define REG_SRAM_RXF_ADDR 0x1520 #define REG_SRAM_RXF_LEN 0x1524 #define REG_SRAM_TXF_ADDR 0x1528 #define REG_SRAM_TXF_LEN 0x152C #define REG_SRAM_TCPH_ADDR 0x1530 #define REG_SRAM_PKTH_ADDR 0x1532 /* * Load Ptr Register * Software sets this bit after the initialization of the head and tail */ #define REG_LOAD_PTR 0x1534 /* * addresses of all descriptors, as well as the following descriptor * control register, which triggers each function block to load the head * pointer to prepare for the operation. This bit is then self-cleared * after one cycle. */ #define REG_RX_BASE_ADDR_HI 0x1540 #define REG_TX_BASE_ADDR_HI 0x1544 #define REG_RFD0_HEAD_ADDR_LO 0x1550 #define REG_RFD_RING_SIZE 0x1560 #define RFD_RING_SIZE_MASK 0x0FFF #define REG_RX_BUF_SIZE 0x1564 #define RX_BUF_SIZE_MASK 0xFFFF #define REG_RRD0_HEAD_ADDR_LO 0x1568 #define REG_RRD_RING_SIZE 0x1578 #define RRD_RING_SIZE_MASK 0x0FFF #define REG_TPD_PRI1_ADDR_LO 0x157C #define REG_TPD_PRI0_ADDR_LO 0x1580 #define REG_TPD_RING_SIZE 0x1584 #define TPD_RING_SIZE_MASK 0xFFFF /* TXQ Control Register */ #define REG_TXQ_CTRL 0x1590 #define TXQ_TXF_BURST_NUM_MASK 0xFFFFUL #define TXQ_TXF_BURST_NUM_SHIFT 16 #define L1C_TXQ_TXF_BURST_PREF 0x200 #define L2CB_TXQ_TXF_BURST_PREF 0x40 #define TXQ_CTRL_PEDING_CLR BIT(8) #define TXQ_CTRL_LS_8023_EN BIT(7) #define TXQ_CTRL_ENH_MODE BIT(6) #define TXQ_CTRL_EN BIT(5) #define TXQ_CTRL_IP_OPTION_EN BIT(4) #define TXQ_NUM_TPD_BURST_MASK 0xFUL #define TXQ_NUM_TPD_BURST_SHIFT 0 #define TXQ_NUM_TPD_BURST_DEF 5 #define TXQ_CFGV (\ FIELDX(TXQ_NUM_TPD_BURST, TXQ_NUM_TPD_BURST_DEF) |\ TXQ_CTRL_ENH_MODE |\ TXQ_CTRL_LS_8023_EN |\ TXQ_CTRL_IP_OPTION_EN) #define L1C_TXQ_CFGV (\ TXQ_CFGV |\ FIELDX(TXQ_TXF_BURST_NUM, L1C_TXQ_TXF_BURST_PREF)) #define L2CB_TXQ_CFGV (\ TXQ_CFGV |\ FIELDX(TXQ_TXF_BURST_NUM, L2CB_TXQ_TXF_BURST_PREF)) /* Jumbo packet Threshold for task offload */ #define REG_TX_TSO_OFFLOAD_THRESH 0x1594 /* In 8-bytes */ #define TX_TSO_OFFLOAD_THRESH_MASK 0x07FF #define MAX_TSO_FRAME_SIZE (7*1024) #define REG_TXF_WATER_MARK 0x1598 /* In 8-bytes */ #define TXF_WATER_MARK_MASK 0x0FFF #define TXF_LOW_WATER_MARK_SHIFT 0 #define TXF_HIGH_WATER_MARK_SHIFT 16 #define TXQ_CTRL_BURST_MODE_EN 0x80000000 #define REG_THRUPUT_MON_CTRL 0x159C #define THRUPUT_MON_RATE_MASK 0x3 #define THRUPUT_MON_RATE_SHIFT 0 #define THRUPUT_MON_EN 0x80 /* RXQ Control Register */ #define REG_RXQ_CTRL 0x15A0 #define ASPM_THRUPUT_LIMIT_MASK 0x3 #define ASPM_THRUPUT_LIMIT_SHIFT 0 #define ASPM_THRUPUT_LIMIT_NO 0x00 #define ASPM_THRUPUT_LIMIT_1M 0x01 #define ASPM_THRUPUT_LIMIT_10M 0x02 #define ASPM_THRUPUT_LIMIT_100M 0x03 #define IPV6_CHKSUM_CTRL_EN BIT(7) #define RXQ_RFD_BURST_NUM_MASK 0x003F #define RXQ_RFD_BURST_NUM_SHIFT 20 #define RXQ_NUM_RFD_PREF_DEF 8 #define RSS_MODE_MASK 3UL #define RSS_MODE_SHIFT 26 #define RSS_MODE_DIS 0 #define RSS_MODE_SQSI 1 #define RSS_MODE_MQSI 2 #define RSS_MODE_MQMI 3 #define RSS_NIP_QUEUE_SEL BIT(28) /* 0:q0, 1:table */ #define RRS_HASH_CTRL_EN BIT(29) #define RX_CUT_THRU_EN BIT(30) #define RXQ_CTRL_EN BIT(31) #define REG_RFD_FREE_THRESH 0x15A4 #define RFD_FREE_THRESH_MASK 0x003F #define RFD_FREE_HI_THRESH_SHIFT 0 #define RFD_FREE_LO_THRESH_SHIFT 6 /* RXF flow control register */ #define REG_RXQ_RXF_PAUSE_THRESH 0x15A8 #define RXQ_RXF_PAUSE_TH_HI_SHIFT 0 #define RXQ_RXF_PAUSE_TH_HI_MASK 0x0FFF #define RXQ_RXF_PAUSE_TH_LO_SHIFT 16 #define RXQ_RXF_PAUSE_TH_LO_MASK 0x0FFF #define REG_RXD_DMA_CTRL 0x15AC #define RXD_DMA_THRESH_MASK 0x0FFF /* In 8-bytes */ #define RXD_DMA_THRESH_SHIFT 0 #define RXD_DMA_DOWN_TIMER_MASK 0xFFFF #define RXD_DMA_DOWN_TIMER_SHIFT 16 /* DMA Engine Control Register */ #define REG_DMA_CTRL 0x15C0 #define DMA_CTRL_SMB_NOW BIT(31) #define DMA_CTRL_WPEND_CLR BIT(30) #define DMA_CTRL_RPEND_CLR BIT(29) #define DMA_CTRL_WDLY_CNT_MASK 0xFUL #define DMA_CTRL_WDLY_CNT_SHIFT 16 #define DMA_CTRL_WDLY_CNT_DEF 4 #define DMA_CTRL_RDLY_CNT_MASK 0x1FUL #define DMA_CTRL_RDLY_CNT_SHIFT 11 #define DMA_CTRL_RDLY_CNT_DEF 15 #define DMA_CTRL_RREQ_PRI_DATA BIT(10) /* 0:tpd, 1:data */ #define DMA_CTRL_WREQ_BLEN_MASK 7UL #define DMA_CTRL_WREQ_BLEN_SHIFT 7 #define DMA_CTRL_RREQ_BLEN_MASK 7UL #define DMA_CTRL_RREQ_BLEN_SHIFT 4 #define L1C_CTRL_DMA_RCB_LEN128 BIT(3) /* 0:64bytes,1:128bytes */ #define DMA_CTRL_RORDER_MODE_MASK 7UL #define DMA_CTRL_RORDER_MODE_SHIFT 0 #define DMA_CTRL_RORDER_MODE_OUT 4 #define DMA_CTRL_RORDER_MODE_ENHANCE 2 #define DMA_CTRL_RORDER_MODE_IN 1 /* INT-triggle/SMB Control Register */ #define REG_SMB_STAT_TIMER 0x15C4 /* 2us resolution */ #define SMB_STAT_TIMER_MASK 0xFFFFFF #define REG_TINT_TPD_THRESH 0x15C8 /* tpd th to trig intrrupt */ /* Mail box */ #define MB_RFDX_PROD_IDX_MASK 0xFFFF #define REG_MB_RFD0_PROD_IDX 0x15E0 #define REG_TPD_PRI1_PIDX 0x15F0 /* 16bit,hi-tpd producer idx */ #define REG_TPD_PRI0_PIDX 0x15F2 /* 16bit,lo-tpd producer idx */ #define REG_TPD_PRI1_CIDX 0x15F4 /* 16bit,hi-tpd consumer idx */ #define REG_TPD_PRI0_CIDX 0x15F6 /* 16bit,lo-tpd consumer idx */ #define REG_MB_RFD01_CONS_IDX 0x15F8 #define MB_RFD0_CONS_IDX_MASK 0x0000FFFF #define MB_RFD1_CONS_IDX_MASK 0xFFFF0000 /* Interrupt Status Register */ #define REG_ISR 0x1600 #define ISR_SMB 0x00000001 #define ISR_TIMER 0x00000002 /* * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set * in Table 51 Selene Master Control Register (Offset 0x1400). */ #define ISR_MANUAL 0x00000004 #define ISR_HW_RXF_OV 0x00000008 /* RXF overflow interrupt */ #define ISR_RFD0_UR 0x00000010 /* RFD0 under run */ #define ISR_RFD1_UR 0x00000020 #define ISR_RFD2_UR 0x00000040 #define ISR_RFD3_UR 0x00000080 #define ISR_TXF_UR 0x00000100 #define ISR_DMAR_TO_RST 0x00000200 #define ISR_DMAW_TO_RST 0x00000400 #define ISR_TX_CREDIT 0x00000800 #define ISR_GPHY 0x00001000 /* GPHY low power state interrupt */ #define ISR_GPHY_LPW 0x00002000 #define ISR_TXQ_TO_RST 0x00004000 #define ISR_TX_PKT 0x00008000 #define ISR_RX_PKT_0 0x00010000 #define ISR_RX_PKT_1 0x00020000 #define ISR_RX_PKT_2 0x00040000 #define ISR_RX_PKT_3 0x00080000 #define ISR_MAC_RX 0x00100000 #define ISR_MAC_TX 0x00200000 #define ISR_UR_DETECTED 0x00400000 #define ISR_FERR_DETECTED 0x00800000 #define ISR_NFERR_DETECTED 0x01000000 #define ISR_CERR_DETECTED 0x02000000 #define ISR_PHY_LINKDOWN 0x04000000 #define ISR_DIS_INT 0x80000000 /* Interrupt Mask Register */ #define REG_IMR 0x1604 #define IMR_NORMAL_MASK (\ ISR_MANUAL |\ ISR_HW_RXF_OV |\ ISR_RFD0_UR |\ ISR_TXF_UR |\ ISR_DMAR_TO_RST |\ ISR_TXQ_TO_RST |\ ISR_DMAW_TO_RST |\ ISR_GPHY |\ ISR_TX_PKT |\ ISR_RX_PKT_0 |\ ISR_GPHY_LPW |\ ISR_PHY_LINKDOWN) #define ISR_RX_PKT (\ ISR_RX_PKT_0 |\ ISR_RX_PKT_1 |\ ISR_RX_PKT_2 |\ ISR_RX_PKT_3) #define ISR_OVER (\ ISR_RFD0_UR |\ ISR_RFD1_UR |\ ISR_RFD2_UR |\ ISR_RFD3_UR |\ ISR_HW_RXF_OV |\ ISR_TXF_UR) #define ISR_ERROR (\ ISR_DMAR_TO_RST |\ ISR_TXQ_TO_RST |\ ISR_DMAW_TO_RST |\ ISR_PHY_LINKDOWN) #define REG_INT_RETRIG_TIMER 0x1608 #define INT_RETRIG_TIMER_MASK 0xFFFF #define REG_MAC_RX_STATUS_BIN 0x1700 #define REG_MAC_RX_STATUS_END 0x175c #define REG_MAC_TX_STATUS_BIN 0x1760 #define REG_MAC_TX_STATUS_END 0x17c0 #define REG_CLK_GATING_CTRL 0x1814 #define CLK_GATING_DMAW_EN 0x0001 #define CLK_GATING_DMAR_EN 0x0002 #define CLK_GATING_TXQ_EN 0x0004 #define CLK_GATING_RXQ_EN 0x0008 #define CLK_GATING_TXMAC_EN 0x0010 #define CLK_GATING_RXMAC_EN 0x0020 #define CLK_GATING_EN_ALL (CLK_GATING_DMAW_EN |\ CLK_GATING_DMAR_EN |\ CLK_GATING_TXQ_EN |\ CLK_GATING_RXQ_EN |\ CLK_GATING_TXMAC_EN|\ CLK_GATING_RXMAC_EN) /* DEBUG ADDR */ #define REG_DEBUG_DATA0 0x1900 #define REG_DEBUG_DATA1 0x1904 #define L1D_MPW_PHYID1 0xD01C /* V7 */ #define L1D_MPW_PHYID2 0xD01D /* V1-V6 */ #define L1D_MPW_PHYID3 0xD01E /* V8 */ /* Autoneg Advertisement Register */ #define ADVERTISE_DEFAULT_CAP \ (ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM) /* 1000BASE-T Control Register */ #define GIGA_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port 0=DTE device */ #define GIGA_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master 0=Configure PHY as Slave */ #define GIGA_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value 0=Automatic Master/Slave config */ #define GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ #define GIGA_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ #define GIGA_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ #define GIGA_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ #define GIGA_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ #define GIGA_CR_1000T_SPEED_MASK 0x0300 #define GIGA_CR_1000T_DEFAULT_CAP 0x0300 /* PHY Specific Status Register */ #define MII_GIGA_PSSR 0x11 #define GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ #define GIGA_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ #define GIGA_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ #define GIGA_PSSR_10MBS 0x0000 /* 00=10Mbs */ #define GIGA_PSSR_100MBS 0x4000 /* 01=100Mbs */ #define GIGA_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ /* PHY Interrupt Enable Register */ #define MII_IER 0x12 #define IER_LINK_UP 0x0400 #define IER_LINK_DOWN 0x0800 /* PHY Interrupt Status Register */ #define MII_ISR 0x13 #define ISR_LINK_UP 0x0400 #define ISR_LINK_DOWN 0x0800 /* Cable-Detect-Test Control Register */ #define MII_CDTC 0x16 #define CDTC_EN_OFF 0 /* sc */ #define CDTC_EN_BITS 1 #define CDTC_PAIR_OFF 8 #define CDTC_PAIR_BIT 2 /* Cable-Detect-Test Status Register */ #define MII_CDTS 0x1C #define CDTS_STATUS_OFF 8 #define CDTS_STATUS_BITS 2 #define CDTS_STATUS_NORMAL 0 #define CDTS_STATUS_SHORT 1 #define CDTS_STATUS_OPEN 2 #define CDTS_STATUS_INVALID 3 #define MII_DBG_ADDR 0x1D #define MII_DBG_DATA 0x1E /***************************** debug port *************************************/ #define MIIDBG_ANACTRL 0x00 #define ANACTRL_CLK125M_DELAY_EN 0x8000 #define ANACTRL_VCO_FAST 0x4000 #define ANACTRL_VCO_SLOW 0x2000 #define ANACTRL_AFE_MODE_EN 0x1000 #define ANACTRL_LCKDET_PHY 0x800 #define ANACTRL_LCKDET_EN 0x400 #define ANACTRL_OEN_125M 0x200 #define ANACTRL_HBIAS_EN 0x100 #define ANACTRL_HB_EN 0x80 #define ANACTRL_SEL_HSP 0x40 #define ANACTRL_CLASSA_EN 0x20 #define ANACTRL_MANUSWON_SWR_MASK 3U #define ANACTRL_MANUSWON_SWR_SHIFT 2 #define ANACTRL_MANUSWON_SWR_2V 0 #define ANACTRL_MANUSWON_SWR_1P9V 1 #define ANACTRL_MANUSWON_SWR_1P8V 2 #define ANACTRL_MANUSWON_SWR_1P7V 3 #define ANACTRL_MANUSWON_BW3_4M 0x2 #define ANACTRL_RESTART_CAL 0x1 #define ANACTRL_DEF 0x02EF #define MIIDBG_SYSMODCTRL 0x04 #define SYSMODCTRL_IECHOADJ_PFMH_PHY 0x8000 #define SYSMODCTRL_IECHOADJ_BIASGEN 0x4000 #define SYSMODCTRL_IECHOADJ_PFML_PHY 0x2000 #define SYSMODCTRL_IECHOADJ_PS_MASK 3U #define SYSMODCTRL_IECHOADJ_PS_SHIFT 10 #define SYSMODCTRL_IECHOADJ_PS_40 3 #define SYSMODCTRL_IECHOADJ_PS_20 2 #define SYSMODCTRL_IECHOADJ_PS_0 1 #define SYSMODCTRL_IECHOADJ_10BT_100MV 0x40 /* 1:100mv, 0:200mv */ #define SYSMODCTRL_IECHOADJ_HLFAP_MASK 3U #define SYSMODCTRL_IECHOADJ_HLFAP_SHIFT 4 #define SYSMODCTRL_IECHOADJ_VDFULBW 0x8 #define SYSMODCTRL_IECHOADJ_VDBIASHLF 0x4 #define SYSMODCTRL_IECHOADJ_VDAMPHLF 0x2 #define SYSMODCTRL_IECHOADJ_VDLANSW 0x1 #define SYSMODCTRL_IECHOADJ_DEF 0x88BB /* ???? */ /* for l1d & l2cb */ #define SYSMODCTRL_IECHOADJ_CUR_ADD 0x8000 #define SYSMODCTRL_IECHOADJ_CUR_MASK 7U #define SYSMODCTRL_IECHOADJ_CUR_SHIFT 12 #define SYSMODCTRL_IECHOADJ_VOL_MASK 0xFU #define SYSMODCTRL_IECHOADJ_VOL_SHIFT 8 #define SYSMODCTRL_IECHOADJ_VOL_17ALL 3 #define SYSMODCTRL_IECHOADJ_VOL_100M15 1 #define SYSMODCTRL_IECHOADJ_VOL_10M17 0 #define SYSMODCTRL_IECHOADJ_BIAS1_MASK 0xFU #define SYSMODCTRL_IECHOADJ_BIAS1_SHIFT 4 #define SYSMODCTRL_IECHOADJ_BIAS2_MASK 0xFU #define SYSMODCTRL_IECHOADJ_BIAS2_SHIFT 0 #define L1D_SYSMODCTRL_IECHOADJ_DEF 0x4FBB #define MIIDBG_SRDSYSMOD 0x05 #define SRDSYSMOD_LCKDET_EN 0x2000 #define SRDSYSMOD_PLL_EN 0x800 #define SRDSYSMOD_SEL_HSP 0x400 #define SRDSYSMOD_HLFTXDR 0x200 #define SRDSYSMOD_TXCLK_DELAY_EN 0x100 #define SRDSYSMOD_TXELECIDLE 0x80 #define SRDSYSMOD_DEEMP_EN 0x40 #define SRDSYSMOD_MS_PAD 0x4 #define SRDSYSMOD_CDR_ADC_VLTG 0x2 #define SRDSYSMOD_CDR_DAC_1MA 0x1 #define SRDSYSMOD_DEF 0x2C46 #define MIIDBG_CFGLPSPD 0x0A #define CFGLPSPD_RSTCNT_MASK 3U #define CFGLPSPD_RSTCNT_SHIFT 14 #define CFGLPSPD_RSTCNT_CLK125SW 0x2000 #define MIIDBG_HIBNEG 0x0B #define HIBNEG_PSHIB_EN 0x8000 #define HIBNEG_WAKE_BOTH 0x4000 #define HIBNEG_ONOFF_ANACHG_SUDEN 0x2000 #define HIBNEG_HIB_PULSE 0x1000 #define HIBNEG_GATE_25M_EN 0x800 #define HIBNEG_RST_80U 0x400 #define HIBNEG_RST_TIMER_MASK 3U #define HIBNEG_RST_TIMER_SHIFT 8 #define HIBNEG_GTX_CLK_DELAY_MASK 3U #define HIBNEG_GTX_CLK_DELAY_SHIFT 5 #define HIBNEG_BYPSS_BRKTIMER 0x10 #define HIBNEG_DEF 0xBC40 #define MIIDBG_TST10BTCFG 0x12 #define TST10BTCFG_INTV_TIMER_MASK 3U #define TST10BTCFG_INTV_TIMER_SHIFT 14 #define TST10BTCFG_TRIGER_TIMER_MASK 3U #define TST10BTCFG_TRIGER_TIMER_SHIFT 12 #define TST10BTCFG_DIV_MAN_MLT3_EN 0x800 #define TST10BTCFG_OFF_DAC_IDLE 0x400 #define TST10BTCFG_LPBK_DEEP 0x4 /* 1:deep,0:shallow */ #define TST10BTCFG_DEF 0x4C04 #define MIIDBG_AZ_ANADECT 0x15 #define AZ_ANADECT_10BTRX_TH 0x8000 #define AZ_ANADECT_BOTH_01CHNL 0x4000 #define AZ_ANADECT_INTV_MASK 0x3FU #define AZ_ANADECT_INTV_SHIFT 8 #define AZ_ANADECT_THRESH_MASK 0xFU #define AZ_ANADECT_THRESH_SHIFT 4 #define AZ_ANADECT_CHNL_MASK 0xFU #define AZ_ANADECT_CHNL_SHIFT 0 #define AZ_ANADECT_DEF 0x3220 #define AZ_ANADECT_LONG 0xb210 #define MIIDBG_MSE16DB 0x18 /* l1d */ #define L1D_MSE16DB_UP 0x05EA #define L1D_MSE16DB_DOWN 0x02EA #define MIIDBG_LEGCYPS 0x29 #define LEGCYPS_EN 0x8000 #define LEGCYPS_DAC_AMP1000_MASK 7U #define LEGCYPS_DAC_AMP1000_SHIFT 12 #define LEGCYPS_DAC_AMP100_MASK 7U #define LEGCYPS_DAC_AMP100_SHIFT 9 #define LEGCYPS_DAC_AMP10_MASK 7U #define LEGCYPS_DAC_AMP10_SHIFT 6 #define LEGCYPS_UNPLUG_TIMER_MASK 7U #define LEGCYPS_UNPLUG_TIMER_SHIFT 3 #define LEGCYPS_UNPLUG_DECT_EN 0x4 #define LEGCYPS_ECNC_PS_EN 0x1 #define L1D_LEGCYPS_DEF 0x129D #define L1C_LEGCYPS_DEF 0x36DD #define MIIDBG_TST100BTCFG 0x36 #define TST100BTCFG_NORMAL_BW_EN 0x8000 #define TST100BTCFG_BADLNK_BYPASS 0x4000 #define TST100BTCFG_SHORTCABL_TH_MASK 0x3FU #define TST100BTCFG_SHORTCABL_TH_SHIFT 8 #define TST100BTCFG_LITCH_EN 0x80 #define TST100BTCFG_VLT_SW 0x40 #define TST100BTCFG_LONGCABL_TH_MASK 0x3FU #define TST100BTCFG_LONGCABL_TH_SHIFT 0 #define TST100BTCFG_DEF 0xE12C #define MIIDBG_VOLT_CTRL 0x3B /* only for l2cb 1 & 2 */ #define VOLT_CTRL_CABLE1TH_MASK 0x1FFU #define VOLT_CTRL_CABLE1TH_SHIFT 7 #define VOLT_CTRL_AMPCTRL_MASK 3U #define VOLT_CTRL_AMPCTRL_SHIFT 5 #define VOLT_CTRL_SW_BYPASS 0x10 #define VOLT_CTRL_SWLOWEST 0x8 #define VOLT_CTRL_DACAMP10_MASK 7U #define VOLT_CTRL_DACAMP10_SHIFT 0 #define MIIDBG_CABLE1TH_DET 0x3E #define CABLE1TH_DET_EN 0x8000 /******* dev 3 *********/ #define MIIEXT_PCS 3 #define MIIEXT_CLDCTRL3 0x8003 #define CLDCTRL3_BP_CABLE1TH_DET_GT 0x8000 #define CLDCTRL3_AZ_DISAMP 0x1000 #define L2CB_CLDCTRL3 0x4D19 #define L1D_CLDCTRL3 0xDD19 #define MIIEXT_CLDCTRL6 0x8006 #define CLDCTRL6_CAB_LEN_MASK 0x1FFU #define CLDCTRL6_CAB_LEN_SHIFT 0 #define CLDCTRL6_CAB_LEN_SHORT 0x50 /********* dev 7 **********/ #define MIIEXT_ANEG 7 #define MIIEXT_LOCAL_EEEADV 0x3C #define LOCAL_EEEADV_1000BT 0x4 #define LOCAL_EEEADV_100BT 0x2 #define MIIEXT_REMOTE_EEEADV 0x3D #define REMOTE_EEEADV_1000BT 0x4 #define REMOTE_EEEADV_100BT 0x2 #define MIIEXT_EEE_ANEG 0x8000 #define EEE_ANEG_1000M 0x4 #define EEE_ANEG_100M 0x2 #endif /*_ATL1C_HW_H_*/ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c0000644000175000017500000005623412026211315024773 0ustar mcgrofmcgrof/* * Copyright(c) 2007 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "atl1c.h" /* * check_eeprom_exist * return 1 if eeprom exist */ int atl1c_check_eeprom_exist(struct atl1c_hw *hw) { u32 data; AT_READ_REG(hw, REG_TWSI_DEBUG, &data); if (data & TWSI_DEBUG_DEV_EXIST) return 1; AT_READ_REG(hw, REG_MASTER_CTRL, &data); if (data & MASTER_CTRL_OTP_SEL) return 1; return 0; } void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr) { u32 value; /* * 00-0B-6A-F6-00-DC * 0: 6AF600DC 1: 000B * low dword */ value = mac_addr[2] << 24 | mac_addr[3] << 16 | mac_addr[4] << 8 | mac_addr[5]; AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); /* hight dword */ value = mac_addr[0] << 8 | mac_addr[1]; AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); } /* read mac address from hardware register */ static bool atl1c_read_current_addr(struct atl1c_hw *hw, u8 *eth_addr) { u32 addr[2]; AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]); AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]); *(u32 *) ð_addr[2] = htonl(addr[0]); *(u16 *) ð_addr[0] = htons((u16)addr[1]); return is_valid_ether_addr(eth_addr); } /* * atl1c_get_permanent_address * return 0 if get valid mac address, */ static int atl1c_get_permanent_address(struct atl1c_hw *hw) { u32 i; u32 otp_ctrl_data; u32 twsi_ctrl_data; u16 phy_data; bool raise_vol = false; /* MAC-address from BIOS is the 1st priority */ if (atl1c_read_current_addr(hw, hw->perm_mac_addr)) return 0; /* init */ AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); if (atl1c_check_eeprom_exist(hw)) { if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) { /* Enable OTP CLK */ if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) { otp_ctrl_data |= OTP_CTRL_CLK_EN; AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); AT_WRITE_FLUSH(hw); msleep(1); } } /* raise voltage temporally for l2cb */ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data); phy_data &= ~ANACTRL_HB_EN; atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data); atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); phy_data |= VOLT_CTRL_SWLOWEST; atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); udelay(20); raise_vol = true; } AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data); for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) { msleep(10); AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0) break; } if (i >= AT_TWSI_EEPROM_TIMEOUT) return -1; } /* Disable OTP_CLK */ if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) { otp_ctrl_data &= ~OTP_CTRL_CLK_EN; AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); msleep(1); } if (raise_vol) { atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data); phy_data |= ANACTRL_HB_EN; atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data); atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); phy_data &= ~VOLT_CTRL_SWLOWEST; atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); udelay(20); } if (atl1c_read_current_addr(hw, hw->perm_mac_addr)) return 0; return -1; } bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value) { int i; int ret = false; u32 otp_ctrl_data; u32 control; u32 data; if (offset & 3) return ret; /* address do not align */ AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) AT_WRITE_REG(hw, REG_OTP_CTRL, (otp_ctrl_data | OTP_CTRL_CLK_EN)); AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0); control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT; AT_WRITE_REG(hw, REG_EEPROM_CTRL, control); for (i = 0; i < 10; i++) { udelay(100); AT_READ_REG(hw, REG_EEPROM_CTRL, &control); if (control & EEPROM_CTRL_RW) break; } if (control & EEPROM_CTRL_RW) { AT_READ_REG(hw, REG_EEPROM_CTRL, &data); AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value); data = data & 0xFFFF; *p_value = swab32((data << 16) | (*p_value >> 16)); ret = true; } if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); return ret; } /* * Reads the adapter's MAC address from the EEPROM * * hw - Struct containing variables accessed by shared code */ int atl1c_read_mac_addr(struct atl1c_hw *hw) { int err = 0; err = atl1c_get_permanent_address(hw); if (err) eth_random_addr(hw->perm_mac_addr); memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr)); return err; } /* * atl1c_hash_mc_addr * purpose * set hash value for a multicast address * hash calcu processing : * 1. calcu 32bit CRC for multicast address * 2. reverse crc with MSB to LSB */ u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr) { u32 crc32; u32 value = 0; int i; crc32 = ether_crc_le(6, mc_addr); for (i = 0; i < 32; i++) value |= (((crc32 >> i) & 1) << (31 - i)); return value; } /* * Sets the bit in the multicast table corresponding to the hash value. * hw - Struct containing variables accessed by shared code * hash_value - Multicast address hash value */ void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value) { u32 hash_bit, hash_reg; u32 mta; /* * The HASH Table is a register array of 2 32-bit registers. * It is treated like an array of 64 bits. We want to set * bit BitArray[hash_value]. So we figure out what register * the bit is in, read it, OR in the new bit, then write * back the new value. The register is determined by the * upper bit of the hash value and the bit within that * register are determined by the lower 5 bits of the value. */ hash_reg = (hash_value >> 31) & 0x1; hash_bit = (hash_value >> 26) & 0x1F; mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); mta |= (1 << hash_bit); AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); } /* * wait mdio module be idle * return true: idle * false: still busy */ bool atl1c_wait_mdio_idle(struct atl1c_hw *hw) { u32 val; int i; for (i = 0; i < MDIO_MAX_AC_TO; i++) { AT_READ_REG(hw, REG_MDIO_CTRL, &val); if (!(val & (MDIO_CTRL_BUSY | MDIO_CTRL_START))) break; udelay(10); } return i != MDIO_MAX_AC_TO; } void atl1c_stop_phy_polling(struct atl1c_hw *hw) { if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) return; AT_WRITE_REG(hw, REG_MDIO_CTRL, 0); atl1c_wait_mdio_idle(hw); } void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel) { u32 val; if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) return; val = MDIO_CTRL_SPRES_PRMBL | FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | FIELDX(MDIO_CTRL_REG, 1) | MDIO_CTRL_START | MDIO_CTRL_OP_READ; AT_WRITE_REG(hw, REG_MDIO_CTRL, val); atl1c_wait_mdio_idle(hw); val |= MDIO_CTRL_AP_EN; val &= ~MDIO_CTRL_START; AT_WRITE_REG(hw, REG_MDIO_CTRL, val); udelay(30); } /* * atl1c_read_phy_core * core funtion to read register in PHY via MDIO control regsiter. * ext: extension register (see IEEE 802.3) * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0) * reg: reg to read */ int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, u16 reg, u16 *phy_data) { u32 val; u16 clk_sel = MDIO_CTRL_CLK_25_4; atl1c_stop_phy_polling(hw); *phy_data = 0; /* only l2c_b2 & l1d_2 could use slow clock */ if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) && hw->hibernate) clk_sel = MDIO_CTRL_CLK_25_128; if (ext) { val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg); AT_WRITE_REG(hw, REG_MDIO_EXTN, val); val = MDIO_CTRL_SPRES_PRMBL | FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | MDIO_CTRL_START | MDIO_CTRL_MODE_EXT | MDIO_CTRL_OP_READ; } else { val = MDIO_CTRL_SPRES_PRMBL | FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | FIELDX(MDIO_CTRL_REG, reg) | MDIO_CTRL_START | MDIO_CTRL_OP_READ; } AT_WRITE_REG(hw, REG_MDIO_CTRL, val); if (!atl1c_wait_mdio_idle(hw)) return -1; AT_READ_REG(hw, REG_MDIO_CTRL, &val); *phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA); atl1c_start_phy_polling(hw, clk_sel); return 0; } /* * atl1c_write_phy_core * core funtion to write to register in PHY via MDIO control regsiter. * ext: extension register (see IEEE 802.3) * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0) * reg: reg to write */ int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, u16 reg, u16 phy_data) { u32 val; u16 clk_sel = MDIO_CTRL_CLK_25_4; atl1c_stop_phy_polling(hw); /* only l2c_b2 & l1d_2 could use slow clock */ if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) && hw->hibernate) clk_sel = MDIO_CTRL_CLK_25_128; if (ext) { val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg); AT_WRITE_REG(hw, REG_MDIO_EXTN, val); val = MDIO_CTRL_SPRES_PRMBL | FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | FIELDX(MDIO_CTRL_DATA, phy_data) | MDIO_CTRL_START | MDIO_CTRL_MODE_EXT; } else { val = MDIO_CTRL_SPRES_PRMBL | FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | FIELDX(MDIO_CTRL_DATA, phy_data) | FIELDX(MDIO_CTRL_REG, reg) | MDIO_CTRL_START; } AT_WRITE_REG(hw, REG_MDIO_CTRL, val); if (!atl1c_wait_mdio_idle(hw)) return -1; atl1c_start_phy_polling(hw, clk_sel); return 0; } /* * Reads the value from a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to read */ int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) { return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data); } /* * Writes a value to a PHY register * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to write * data - data to write to the PHY */ int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data) { return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data); } /* read from PHY extension register */ int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr, u16 reg_addr, u16 *phy_data) { return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data); } /* write to PHY extension register */ int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr, u16 reg_addr, u16 phy_data) { return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data); } int atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) { int err; err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr); if (unlikely(err)) return err; else err = atl1c_read_phy_reg(hw, MII_DBG_DATA, phy_data); return err; } int atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data) { int err; err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr); if (unlikely(err)) return err; else err = atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data); return err; } /* * Configures PHY autoneg and flow control advertisement settings * * hw - Struct containing variables accessed by shared code */ static int atl1c_phy_setup_adv(struct atl1c_hw *hw) { u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL; u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP & ~GIGA_CR_1000T_SPEED_MASK; if (hw->autoneg_advertised & ADVERTISED_10baseT_Half) mii_adv_data |= ADVERTISE_10HALF; if (hw->autoneg_advertised & ADVERTISED_10baseT_Full) mii_adv_data |= ADVERTISE_10FULL; if (hw->autoneg_advertised & ADVERTISED_100baseT_Half) mii_adv_data |= ADVERTISE_100HALF; if (hw->autoneg_advertised & ADVERTISED_100baseT_Full) mii_adv_data |= ADVERTISE_100FULL; if (hw->autoneg_advertised & ADVERTISED_Autoneg) mii_adv_data |= ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL; if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) { if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half) mii_giga_ctrl_data |= ADVERTISE_1000HALF; if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full) mii_giga_ctrl_data |= ADVERTISE_1000FULL; if (hw->autoneg_advertised & ADVERTISED_Autoneg) mii_giga_ctrl_data |= ADVERTISE_1000HALF | ADVERTISE_1000FULL; } if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 || atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0) return -1; return 0; } void atl1c_phy_disable(struct atl1c_hw *hw) { atl1c_power_saving(hw, 0); } int atl1c_phy_reset(struct atl1c_hw *hw) { struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; u16 phy_data; u32 phy_ctrl_data, lpi_ctrl; int err; /* reset PHY core */ AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl_data); phy_ctrl_data &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_GATE_25M_EN | GPHY_CTRL_PWDOWN_HW | GPHY_CTRL_CLS); phy_ctrl_data |= GPHY_CTRL_SEL_ANA_RST; if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) phy_ctrl_data |= (GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE); else phy_ctrl_data &= ~(GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE); AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); AT_WRITE_FLUSH(hw); udelay(10); AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data | GPHY_CTRL_EXT_RESET); AT_WRITE_FLUSH(hw); udelay(10 * GPHY_CTRL_EXT_RST_TO); /* delay 800us */ /* switch clock */ if (hw->nic_type == athr_l2c_b) { atl1c_read_phy_dbg(hw, MIIDBG_CFGLPSPD, &phy_data); atl1c_write_phy_dbg(hw, MIIDBG_CFGLPSPD, phy_data & ~CFGLPSPD_RSTCNT_CLK125SW); } /* tx-half amplitude issue fix */ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { atl1c_read_phy_dbg(hw, MIIDBG_CABLE1TH_DET, &phy_data); phy_data |= CABLE1TH_DET_EN; atl1c_write_phy_dbg(hw, MIIDBG_CABLE1TH_DET, phy_data); } /* clear bit3 of dbgport 3B to lower voltage */ if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) { if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); phy_data &= ~VOLT_CTRL_SWLOWEST; atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); } /* power saving config */ phy_data = hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ? L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF; atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data); /* hib */ atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, SYSMODCTRL_IECHOADJ_DEF); } else { /* disable pws */ atl1c_read_phy_dbg(hw, MIIDBG_LEGCYPS, &phy_data); atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data & ~LEGCYPS_EN); /* disable hibernate */ atl1c_read_phy_dbg(hw, MIIDBG_HIBNEG, &phy_data); atl1c_write_phy_dbg(hw, MIIDBG_HIBNEG, phy_data & HIBNEG_PSHIB_EN); } /* disable AZ(EEE) by default */ if (hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 || hw->nic_type == athr_l2c_b2) { AT_READ_REG(hw, REG_LPI_CTRL, &lpi_ctrl); AT_WRITE_REG(hw, REG_LPI_CTRL, lpi_ctrl & ~LPI_CTRL_EN); atl1c_write_phy_ext(hw, MIIEXT_ANEG, MIIEXT_LOCAL_EEEADV, 0); atl1c_write_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL3, L2CB_CLDCTRL3); } /* other debug port to set */ atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, ANACTRL_DEF); atl1c_write_phy_dbg(hw, MIIDBG_SRDSYSMOD, SRDSYSMOD_DEF); atl1c_write_phy_dbg(hw, MIIDBG_TST10BTCFG, TST10BTCFG_DEF); /* UNH-IOL test issue, set bit7 */ atl1c_write_phy_dbg(hw, MIIDBG_TST100BTCFG, TST100BTCFG_DEF | TST100BTCFG_LITCH_EN); /* set phy interrupt mask */ phy_data = IER_LINK_UP | IER_LINK_DOWN; err = atl1c_write_phy_reg(hw, MII_IER, phy_data); if (err) { if (netif_msg_hw(adapter)) dev_err(&pdev->dev, "Error enable PHY linkChange Interrupt\n"); return err; } return 0; } int atl1c_phy_init(struct atl1c_hw *hw) { struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; int ret_val; u16 mii_bmcr_data = BMCR_RESET; if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) || (atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) { dev_err(&pdev->dev, "Error get phy ID\n"); return -1; } switch (hw->media_type) { case MEDIA_TYPE_AUTO_SENSOR: ret_val = atl1c_phy_setup_adv(hw); if (ret_val) { if (netif_msg_link(adapter)) dev_err(&pdev->dev, "Error Setting up Auto-Negotiation\n"); return ret_val; } mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART; break; case MEDIA_TYPE_100M_FULL: mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX; break; case MEDIA_TYPE_100M_HALF: mii_bmcr_data |= BMCR_SPEED100; break; case MEDIA_TYPE_10M_FULL: mii_bmcr_data |= BMCR_FULLDPLX; break; case MEDIA_TYPE_10M_HALF: break; default: if (netif_msg_link(adapter)) dev_err(&pdev->dev, "Wrong Media type %d\n", hw->media_type); return -1; break; } ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); if (ret_val) return ret_val; hw->phy_configured = true; return 0; } /* * Detects the current speed and duplex settings of the hardware. * * hw - Struct containing variables accessed by shared code * speed - Speed of the connection * duplex - Duplex setting of the connection */ int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex) { int err; u16 phy_data; /* Read PHY Specific Status Register (17) */ err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data); if (err) return err; if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED)) return -1; switch (phy_data & GIGA_PSSR_SPEED) { case GIGA_PSSR_1000MBS: *speed = SPEED_1000; break; case GIGA_PSSR_100MBS: *speed = SPEED_100; break; case GIGA_PSSR_10MBS: *speed = SPEED_10; break; default: return -1; break; } if (phy_data & GIGA_PSSR_DPLX) *duplex = FULL_DUPLEX; else *duplex = HALF_DUPLEX; return 0; } /* select one link mode to get lower power consumption */ int atl1c_phy_to_ps_link(struct atl1c_hw *hw) { struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; int ret = 0; u16 autoneg_advertised = ADVERTISED_10baseT_Half; u16 save_autoneg_advertised; u16 phy_data; u16 mii_lpa_data; u16 speed = SPEED_0; u16 duplex = FULL_DUPLEX; int i; atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); if (phy_data & BMSR_LSTATUS) { atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data); if (mii_lpa_data & LPA_10FULL) autoneg_advertised = ADVERTISED_10baseT_Full; else if (mii_lpa_data & LPA_10HALF) autoneg_advertised = ADVERTISED_10baseT_Half; else if (mii_lpa_data & LPA_100HALF) autoneg_advertised = ADVERTISED_100baseT_Half; else if (mii_lpa_data & LPA_100FULL) autoneg_advertised = ADVERTISED_100baseT_Full; save_autoneg_advertised = hw->autoneg_advertised; hw->phy_configured = false; hw->autoneg_advertised = autoneg_advertised; if (atl1c_restart_autoneg(hw) != 0) { dev_dbg(&pdev->dev, "phy autoneg failed\n"); ret = -1; } hw->autoneg_advertised = save_autoneg_advertised; if (mii_lpa_data) { for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { mdelay(100); atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); if (phy_data & BMSR_LSTATUS) { if (atl1c_get_speed_and_duplex(hw, &speed, &duplex) != 0) dev_dbg(&pdev->dev, "get speed and duplex failed\n"); break; } } } } else { speed = SPEED_10; duplex = HALF_DUPLEX; } adapter->link_speed = speed; adapter->link_duplex = duplex; return ret; } int atl1c_restart_autoneg(struct atl1c_hw *hw) { int err = 0; u16 mii_bmcr_data = BMCR_RESET; err = atl1c_phy_setup_adv(hw); if (err) return err; mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART; return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); } int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc) { struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; u32 master_ctrl, mac_ctrl, phy_ctrl; u32 wol_ctrl, speed; u16 phy_data; wol_ctrl = 0; speed = adapter->link_speed == SPEED_1000 ? MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100; AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl); AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl); AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl); master_ctrl &= ~MASTER_CTRL_CLK_SEL_DIS; mac_ctrl = FIELD_SETX(mac_ctrl, MAC_CTRL_SPEED, speed); mac_ctrl &= ~(MAC_CTRL_DUPLX | MAC_CTRL_RX_EN | MAC_CTRL_TX_EN); if (adapter->link_duplex == FULL_DUPLEX) mac_ctrl |= MAC_CTRL_DUPLX; phy_ctrl &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_CLS); phy_ctrl |= GPHY_CTRL_SEL_ANA_RST | GPHY_CTRL_HIB_PULSE | GPHY_CTRL_HIB_EN; if (!wufc) { /* without WoL */ master_ctrl |= MASTER_CTRL_CLK_SEL_DIS; phy_ctrl |= GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_PWDOWN_HW; AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl); AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl); AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl); AT_WRITE_REG(hw, REG_WOL_CTRL, 0); hw->phy_configured = false; /* re-init PHY when resume */ return 0; } phy_ctrl |= GPHY_CTRL_EXT_RESET; if (wufc & AT_WUFC_MAG) { mac_ctrl |= MAC_CTRL_RX_EN | MAC_CTRL_BC_EN; wol_ctrl |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN; if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V11) wol_ctrl |= WOL_PATTERN_EN | WOL_PATTERN_PME_EN; } if (wufc & AT_WUFC_LNKC) { wol_ctrl |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) { dev_dbg(&pdev->dev, "%s: write phy MII_IER faild.\n", atl1c_driver_name); } } /* clear PHY interrupt */ atl1c_read_phy_reg(hw, MII_ISR, &phy_data); dev_dbg(&pdev->dev, "%s: suspend MAC=%x,MASTER=%x,PHY=0x%x,WOL=%x\n", atl1c_driver_name, mac_ctrl, master_ctrl, phy_ctrl, wol_ctrl); AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl); AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl); AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl); AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl); return 0; } /* configure phy after Link change Event */ void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed) { u16 phy_val; bool adj_thresh = false; if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2) adj_thresh = true; if (link_speed != SPEED_0) { /* link up */ /* az with brcm, half-amp */ if (hw->nic_type == athr_l1d_2) { atl1c_read_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL6, &phy_val); phy_val = FIELD_GETX(phy_val, CLDCTRL6_CAB_LEN); phy_val = phy_val > CLDCTRL6_CAB_LEN_SHORT ? AZ_ANADECT_LONG : AZ_ANADECT_DEF; atl1c_write_phy_dbg(hw, MIIDBG_AZ_ANADECT, phy_val); } /* threshold adjust */ if (adj_thresh && link_speed == SPEED_100 && hw->msi_lnkpatch) { atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_UP); atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, L1D_SYSMODCTRL_IECHOADJ_DEF); } } else { /* link down */ if (adj_thresh && hw->msi_lnkpatch) { atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, SYSMODCTRL_IECHOADJ_DEF); atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_DOWN); } } } compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1c/atl1c.h0000644000175000017500000005033712026211315024300 0ustar mcgrofmcgrof/* * Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved. * * Derived from Intel e1000 driver * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _ATL1C_H_ #define _ATL1C_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atl1c_hw.h" /* Wake Up Filter Control */ #define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ #define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ #define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ #define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ #define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ #define AT_VLAN_TO_TAG(_vlan, _tag) \ _tag = ((((_vlan) >> 8) & 0xFF) |\ (((_vlan) & 0xFF) << 8)) #define AT_TAG_TO_VLAN(_tag, _vlan) \ _vlan = ((((_tag) >> 8) & 0xFF) |\ (((_tag) & 0xFF) << 8)) #define SPEED_0 0xffff #define HALF_DUPLEX 1 #define FULL_DUPLEX 2 #define AT_RX_BUF_SIZE (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN) #define MAX_JUMBO_FRAME_SIZE (6*1024) #define AT_MAX_RECEIVE_QUEUE 4 #define AT_DEF_RECEIVE_QUEUE 1 #define AT_MAX_TRANSMIT_QUEUE 2 #define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL #define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL #define AT_TX_WATCHDOG (5 * HZ) #define AT_MAX_INT_WORK 5 #define AT_TWSI_EEPROM_TIMEOUT 100 #define AT_HW_MAX_IDLE_DELAY 10 #define AT_SUSPEND_LINK_TIMEOUT 100 #define AT_ASPM_L0S_TIMER 6 #define AT_ASPM_L1_TIMER 12 #define AT_LCKDET_TIMER 12 #define ATL1C_PCIE_L0S_L1_DISABLE 0x01 #define ATL1C_PCIE_PHY_RESET 0x02 #define ATL1C_ASPM_L0s_ENABLE 0x0001 #define ATL1C_ASPM_L1_ENABLE 0x0002 #define AT_REGS_LEN (74 * sizeof(u32)) #define AT_EEPROM_LEN 512 #define ATL1C_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i])) #define ATL1C_RFD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_rx_free_desc) #define ATL1C_TPD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_tpd_desc) #define ATL1C_RRD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_recv_ret_status) /* tpd word 1 bit 0:7 General Checksum task offload */ #define TPD_L4HDR_OFFSET_MASK 0x00FF #define TPD_L4HDR_OFFSET_SHIFT 0 /* tpd word 1 bit 0:7 Large Send task offload (IPv4/IPV6) */ #define TPD_TCPHDR_OFFSET_MASK 0x00FF #define TPD_TCPHDR_OFFSET_SHIFT 0 /* tpd word 1 bit 0:7 Custom Checksum task offload */ #define TPD_PLOADOFFSET_MASK 0x00FF #define TPD_PLOADOFFSET_SHIFT 0 /* tpd word 1 bit 8:17 */ #define TPD_CCSUM_EN_MASK 0x0001 #define TPD_CCSUM_EN_SHIFT 8 #define TPD_IP_CSUM_MASK 0x0001 #define TPD_IP_CSUM_SHIFT 9 #define TPD_TCP_CSUM_MASK 0x0001 #define TPD_TCP_CSUM_SHIFT 10 #define TPD_UDP_CSUM_MASK 0x0001 #define TPD_UDP_CSUM_SHIFT 11 #define TPD_LSO_EN_MASK 0x0001 /* TCP Large Send Offload */ #define TPD_LSO_EN_SHIFT 12 #define TPD_LSO_VER_MASK 0x0001 #define TPD_LSO_VER_SHIFT 13 /* 0 : ipv4; 1 : ipv4/ipv6 */ #define TPD_CON_VTAG_MASK 0x0001 #define TPD_CON_VTAG_SHIFT 14 #define TPD_INS_VTAG_MASK 0x0001 #define TPD_INS_VTAG_SHIFT 15 #define TPD_IPV4_PACKET_MASK 0x0001 /* valid when LSO VER is 1 */ #define TPD_IPV4_PACKET_SHIFT 16 #define TPD_ETH_TYPE_MASK 0x0001 #define TPD_ETH_TYPE_SHIFT 17 /* 0 : 802.3 frame; 1 : Ethernet */ /* tpd word 18:25 Custom Checksum task offload */ #define TPD_CCSUM_OFFSET_MASK 0x00FF #define TPD_CCSUM_OFFSET_SHIFT 18 #define TPD_CCSUM_EPAD_MASK 0x0001 #define TPD_CCSUM_EPAD_SHIFT 30 /* tpd word 18:30 Large Send task offload (IPv4/IPV6) */ #define TPD_MSS_MASK 0x1FFF #define TPD_MSS_SHIFT 18 #define TPD_EOP_MASK 0x0001 #define TPD_EOP_SHIFT 31 struct atl1c_tpd_desc { __le16 buffer_len; /* include 4-byte CRC */ __le16 vlan_tag; __le32 word1; __le64 buffer_addr; }; struct atl1c_tpd_ext_desc { u32 reservd_0; __le32 word1; __le32 pkt_len; u32 reservd_1; }; /* rrs word 0 bit 0:31 */ #define RRS_RX_CSUM_MASK 0xFFFF #define RRS_RX_CSUM_SHIFT 0 #define RRS_RX_RFD_CNT_MASK 0x000F #define RRS_RX_RFD_CNT_SHIFT 16 #define RRS_RX_RFD_INDEX_MASK 0x0FFF #define RRS_RX_RFD_INDEX_SHIFT 20 /* rrs flag bit 0:16 */ #define RRS_HEAD_LEN_MASK 0x00FF #define RRS_HEAD_LEN_SHIFT 0 #define RRS_HDS_TYPE_MASK 0x0003 #define RRS_HDS_TYPE_SHIFT 8 #define RRS_CPU_NUM_MASK 0x0003 #define RRS_CPU_NUM_SHIFT 10 #define RRS_HASH_FLG_MASK 0x000F #define RRS_HASH_FLG_SHIFT 12 #define RRS_HDS_TYPE_HEAD 1 #define RRS_HDS_TYPE_DATA 2 #define RRS_IS_NO_HDS_TYPE(flag) \ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == 0) #define RRS_IS_HDS_HEAD(flag) \ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \ RRS_HDS_TYPE_HEAD) #define RRS_IS_HDS_DATA(flag) \ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \ RRS_HDS_TYPE_DATA) /* rrs word 3 bit 0:31 */ #define RRS_PKT_SIZE_MASK 0x3FFF #define RRS_PKT_SIZE_SHIFT 0 #define RRS_ERR_L4_CSUM_MASK 0x0001 #define RRS_ERR_L4_CSUM_SHIFT 14 #define RRS_ERR_IP_CSUM_MASK 0x0001 #define RRS_ERR_IP_CSUM_SHIFT 15 #define RRS_VLAN_INS_MASK 0x0001 #define RRS_VLAN_INS_SHIFT 16 #define RRS_PROT_ID_MASK 0x0007 #define RRS_PROT_ID_SHIFT 17 #define RRS_RX_ERR_SUM_MASK 0x0001 #define RRS_RX_ERR_SUM_SHIFT 20 #define RRS_RX_ERR_CRC_MASK 0x0001 #define RRS_RX_ERR_CRC_SHIFT 21 #define RRS_RX_ERR_FAE_MASK 0x0001 #define RRS_RX_ERR_FAE_SHIFT 22 #define RRS_RX_ERR_TRUNC_MASK 0x0001 #define RRS_RX_ERR_TRUNC_SHIFT 23 #define RRS_RX_ERR_RUNC_MASK 0x0001 #define RRS_RX_ERR_RUNC_SHIFT 24 #define RRS_RX_ERR_ICMP_MASK 0x0001 #define RRS_RX_ERR_ICMP_SHIFT 25 #define RRS_PACKET_BCAST_MASK 0x0001 #define RRS_PACKET_BCAST_SHIFT 26 #define RRS_PACKET_MCAST_MASK 0x0001 #define RRS_PACKET_MCAST_SHIFT 27 #define RRS_PACKET_TYPE_MASK 0x0001 #define RRS_PACKET_TYPE_SHIFT 28 #define RRS_FIFO_FULL_MASK 0x0001 #define RRS_FIFO_FULL_SHIFT 29 #define RRS_802_3_LEN_ERR_MASK 0x0001 #define RRS_802_3_LEN_ERR_SHIFT 30 #define RRS_RXD_UPDATED_MASK 0x0001 #define RRS_RXD_UPDATED_SHIFT 31 #define RRS_ERR_L4_CSUM 0x00004000 #define RRS_ERR_IP_CSUM 0x00008000 #define RRS_VLAN_INS 0x00010000 #define RRS_RX_ERR_SUM 0x00100000 #define RRS_RX_ERR_CRC 0x00200000 #define RRS_802_3_LEN_ERR 0x40000000 #define RRS_RXD_UPDATED 0x80000000 #define RRS_PACKET_TYPE_802_3 1 #define RRS_PACKET_TYPE_ETH 0 #define RRS_PACKET_IS_ETH(word) \ ((((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK) == \ RRS_PACKET_TYPE_ETH) #define RRS_RXD_IS_VALID(word) \ ((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1) #define RRS_PACKET_PROT_IS_IPV4_ONLY(word) \ ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 1) #define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \ ((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6) struct atl1c_recv_ret_status { __le32 word0; __le32 rss_hash; __le16 vlan_tag; __le16 flag; __le32 word3; }; /* RFD descriptor */ struct atl1c_rx_free_desc { __le64 buffer_addr; }; /* DMA Order Settings */ enum atl1c_dma_order { atl1c_dma_ord_in = 1, atl1c_dma_ord_enh = 2, atl1c_dma_ord_out = 4 }; enum atl1c_dma_rcb { atl1c_rcb_64 = 0, atl1c_rcb_128 = 1 }; enum atl1c_mac_speed { atl1c_mac_speed_0 = 0, atl1c_mac_speed_10_100 = 1, atl1c_mac_speed_1000 = 2 }; enum atl1c_dma_req_block { atl1c_dma_req_128 = 0, atl1c_dma_req_256 = 1, atl1c_dma_req_512 = 2, atl1c_dma_req_1024 = 3, atl1c_dma_req_2048 = 4, atl1c_dma_req_4096 = 5 }; enum atl1c_nic_type { athr_l1c = 0, athr_l2c = 1, athr_l2c_b, athr_l2c_b2, athr_l1d, athr_l1d_2, }; enum atl1c_trans_queue { atl1c_trans_normal = 0, atl1c_trans_high = 1 }; struct atl1c_hw_stats { /* rx */ unsigned long rx_ok; /* The number of good packet received. */ unsigned long rx_bcast; /* The number of good broadcast packet received. */ unsigned long rx_mcast; /* The number of good multicast packet received. */ unsigned long rx_pause; /* The number of Pause packet received. */ unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */ unsigned long rx_fcs_err; /* The number of packets with bad FCS. */ unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */ unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */ unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */ unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */ unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */ unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */ unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */ unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */ unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */ unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */ unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */ unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */ unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */ unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */ unsigned long rx_align_err; /* Alignment Error */ unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */ unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */ unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */ /* tx */ unsigned long tx_ok; /* The number of good packet transmitted. */ unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */ unsigned long tx_mcast; /* The number of good multicast packet transmitted. */ unsigned long tx_pause; /* The number of Pause packet transmitted. */ unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */ unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */ unsigned long tx_defer; /* The number of packets transmitted that is deferred. */ unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */ unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */ unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */ unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */ unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */ unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */ unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */ unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */ unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */ unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */ unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */ unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */ unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */ unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */ unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */ unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */ unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */ unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */ }; struct atl1c_hw { u8 __iomem *hw_addr; /* inner register address */ struct atl1c_adapter *adapter; enum atl1c_nic_type nic_type; enum atl1c_dma_order dma_order; enum atl1c_dma_rcb rcb_value; enum atl1c_dma_req_block dmar_block; u16 device_id; u16 vendor_id; u16 subsystem_id; u16 subsystem_vendor_id; u8 revision_id; u16 phy_id1; u16 phy_id2; u32 intr_mask; u8 preamble_len; u16 max_frame_size; u16 min_frame_size; enum atl1c_mac_speed mac_speed; bool mac_duplex; bool hibernate; u16 media_type; #define MEDIA_TYPE_AUTO_SENSOR 0 #define MEDIA_TYPE_100M_FULL 1 #define MEDIA_TYPE_100M_HALF 2 #define MEDIA_TYPE_10M_FULL 3 #define MEDIA_TYPE_10M_HALF 4 u16 autoneg_advertised; u16 mii_autoneg_adv_reg; u16 mii_1000t_ctrl_reg; u16 tx_imt; /* TX Interrupt Moderator timer ( 2us resolution) */ u16 rx_imt; /* RX Interrupt Moderator timer ( 2us resolution) */ u16 ict; /* Interrupt Clear timer (2us resolution) */ u16 ctrl_flags; #define ATL1C_INTR_CLEAR_ON_READ 0x0001 #define ATL1C_INTR_MODRT_ENABLE 0x0002 #define ATL1C_CMB_ENABLE 0x0004 #define ATL1C_SMB_ENABLE 0x0010 #define ATL1C_TXQ_MODE_ENHANCE 0x0020 #define ATL1C_RX_IPV6_CHKSUM 0x0040 #define ATL1C_ASPM_L0S_SUPPORT 0x0080 #define ATL1C_ASPM_L1_SUPPORT 0x0100 #define ATL1C_ASPM_CTRL_MON 0x0200 #define ATL1C_HIB_DISABLE 0x0400 #define ATL1C_APS_MODE_ENABLE 0x0800 #define ATL1C_LINK_EXT_SYNC 0x1000 #define ATL1C_CLK_GATING_EN 0x2000 #define ATL1C_FPGA_VERSION 0x8000 u16 link_cap_flags; #define ATL1C_LINK_CAP_1000M 0x0001 u32 smb_timer; u16 rrd_thresh; /* Threshold of number of RRD produced to trigger interrupt request */ u16 tpd_thresh; u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */ u8 rfd_burst; u32 base_cpu; u32 indirect_tab; u8 mac_addr[ETH_ALEN]; u8 perm_mac_addr[ETH_ALEN]; bool phy_configured; bool re_autoneg; bool emi_ca; bool msi_lnkpatch; /* link patch for specific platforms */ }; /* * atl1c_ring_header represents a single, contiguous block of DMA space * mapped for the three descriptor rings (tpd, rfd, rrd) described below */ struct atl1c_ring_header { void *desc; /* virtual address */ dma_addr_t dma; /* physical address*/ unsigned int size; /* length in bytes */ }; /* * atl1c_buffer is wrapper around a pointer to a socket buffer * so a DMA handle can be stored along with the skb */ struct atl1c_buffer { struct sk_buff *skb; /* socket buffer */ u16 length; /* rx buffer length */ u16 flags; /* information of buffer */ #define ATL1C_BUFFER_FREE 0x0001 #define ATL1C_BUFFER_BUSY 0x0002 #define ATL1C_BUFFER_STATE_MASK 0x0003 #define ATL1C_PCIMAP_SINGLE 0x0004 #define ATL1C_PCIMAP_PAGE 0x0008 #define ATL1C_PCIMAP_TYPE_MASK 0x000C #define ATL1C_PCIMAP_TODEVICE 0x0010 #define ATL1C_PCIMAP_FROMDEVICE 0x0020 #define ATL1C_PCIMAP_DIRECTION_MASK 0x0030 dma_addr_t dma; }; #define ATL1C_SET_BUFFER_STATE(buff, state) do { \ ((buff)->flags) &= ~ATL1C_BUFFER_STATE_MASK; \ ((buff)->flags) |= (state); \ } while (0) #define ATL1C_SET_PCIMAP_TYPE(buff, type, direction) do { \ ((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \ ((buff)->flags) |= (type); \ ((buff)->flags) &= ~ATL1C_PCIMAP_DIRECTION_MASK; \ ((buff)->flags) |= (direction); \ } while (0) /* transimit packet descriptor (tpd) ring */ struct atl1c_tpd_ring { void *desc; /* descriptor ring virtual address */ dma_addr_t dma; /* descriptor ring physical address */ u16 size; /* descriptor ring length in bytes */ u16 count; /* number of descriptors in the ring */ u16 next_to_use; /* this is protectd by adapter->tx_lock */ atomic_t next_to_clean; struct atl1c_buffer *buffer_info; }; /* receive free descriptor (rfd) ring */ struct atl1c_rfd_ring { void *desc; /* descriptor ring virtual address */ dma_addr_t dma; /* descriptor ring physical address */ u16 size; /* descriptor ring length in bytes */ u16 count; /* number of descriptors in the ring */ u16 next_to_use; u16 next_to_clean; struct atl1c_buffer *buffer_info; }; /* receive return descriptor (rrd) ring */ struct atl1c_rrd_ring { void *desc; /* descriptor ring virtual address */ dma_addr_t dma; /* descriptor ring physical address */ u16 size; /* descriptor ring length in bytes */ u16 count; /* number of descriptors in the ring */ u16 next_to_use; u16 next_to_clean; }; /* board specific private data structure */ struct atl1c_adapter { struct net_device *netdev; struct pci_dev *pdev; struct napi_struct napi; struct atl1c_hw hw; struct atl1c_hw_stats hw_stats; struct mii_if_info mii; /* MII interface info */ u16 rx_buffer_len; unsigned long flags; #define __AT_TESTING 0x0001 #define __AT_RESETTING 0x0002 #define __AT_DOWN 0x0003 unsigned long work_event; #define ATL1C_WORK_EVENT_RESET 0 #define ATL1C_WORK_EVENT_LINK_CHANGE 1 u32 msg_enable; bool have_msi; u32 wol; u16 link_speed; u16 link_duplex; spinlock_t mdio_lock; spinlock_t tx_lock; atomic_t irq_sem; struct work_struct common_task; struct timer_list watchdog_timer; struct timer_list phy_config_timer; /* All Descriptor memory */ struct atl1c_ring_header ring_header; struct atl1c_tpd_ring tpd_ring[AT_MAX_TRANSMIT_QUEUE]; struct atl1c_rfd_ring rfd_ring; struct atl1c_rrd_ring rrd_ring; u32 bd_number; /* board number;*/ }; #define AT_WRITE_REG(a, reg, value) ( \ writel((value), ((a)->hw_addr + reg))) #define AT_WRITE_FLUSH(a) (\ readl((a)->hw_addr)) #define AT_READ_REG(a, reg, pdata) do { \ if (unlikely((a)->hibernate)) { \ readl((a)->hw_addr + reg); \ *(u32 *)pdata = readl((a)->hw_addr + reg); \ } else { \ *(u32 *)pdata = readl((a)->hw_addr + reg); \ } \ } while (0) #define AT_WRITE_REGB(a, reg, value) (\ writeb((value), ((a)->hw_addr + reg))) #define AT_READ_REGB(a, reg) (\ readb((a)->hw_addr + reg)) #define AT_WRITE_REGW(a, reg, value) (\ writew((value), ((a)->hw_addr + reg))) #define AT_READ_REGW(a, reg, pdata) do { \ if (unlikely((a)->hibernate)) { \ readw((a)->hw_addr + reg); \ *(u16 *)pdata = readw((a)->hw_addr + reg); \ } else { \ *(u16 *)pdata = readw((a)->hw_addr + reg); \ } \ } while (0) #define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \ writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))) #define AT_READ_REG_ARRAY(a, reg, offset) ( \ readl(((a)->hw_addr + reg) + ((offset) << 2))) extern char atl1c_driver_name[]; extern char atl1c_driver_version[]; extern void atl1c_reinit_locked(struct atl1c_adapter *adapter); extern s32 atl1c_reset_hw(struct atl1c_hw *hw); extern void atl1c_set_ethtool_ops(struct net_device *netdev); #endif /* _ATL1C_H_ */ compat-drivers-2012-09-18/drivers/net/ethernet/atheros/atl1c/Makefile0000644000175000017500000000012512026211315024551 0ustar mcgrofmcgrofobj-$(CONFIG_ATL1C) += atl1c.o atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o compat-drivers-2012-09-18/drivers/net/ethernet/broadcom/0000755000175000017500000000000012026211315022230 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/ethernet/broadcom/b44.c0000644000175000017500000016545712026211315023007 0ustar mcgrofmcgrof/* b44.c: Broadcom 44xx/47xx Fast Ethernet device driver. * * Copyright (C) 2002 David S. Miller (davem@redhat.com) * Copyright (C) 2004 Pekka Pietikainen (pp@ee.oulu.fi) * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) * Copyright (C) 2006 Felix Fietkau (nbd@openwrt.org) * Copyright (C) 2006 Broadcom Corporation. * Copyright (C) 2007 Michael Buesch * * Distribute under GPL. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "b44.h" #define DRV_MODULE_NAME "b44" #define DRV_MODULE_VERSION "2.0" #define DRV_DESCRIPTION "Broadcom 44xx/47xx 10/100 PCI ethernet driver" #define B44_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK | \ NETIF_MSG_TIMER | \ NETIF_MSG_IFDOWN | \ NETIF_MSG_IFUP | \ NETIF_MSG_RX_ERR | \ NETIF_MSG_TX_ERR) /* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */ #define B44_TX_TIMEOUT (5 * HZ) /* hardware minimum and maximum for a single frame's data payload */ #define B44_MIN_MTU 60 #define B44_MAX_MTU 1500 #define B44_RX_RING_SIZE 512 #define B44_DEF_RX_RING_PENDING 200 #define B44_RX_RING_BYTES (sizeof(struct dma_desc) * \ B44_RX_RING_SIZE) #define B44_TX_RING_SIZE 512 #define B44_DEF_TX_RING_PENDING (B44_TX_RING_SIZE - 1) #define B44_TX_RING_BYTES (sizeof(struct dma_desc) * \ B44_TX_RING_SIZE) #define TX_RING_GAP(BP) \ (B44_TX_RING_SIZE - (BP)->tx_pending) #define TX_BUFFS_AVAIL(BP) \ (((BP)->tx_cons <= (BP)->tx_prod) ? \ (BP)->tx_cons + (BP)->tx_pending - (BP)->tx_prod : \ (BP)->tx_cons - (BP)->tx_prod - TX_RING_GAP(BP)) #define NEXT_TX(N) (((N) + 1) & (B44_TX_RING_SIZE - 1)) #define RX_PKT_OFFSET (RX_HEADER_LEN + 2) #define RX_PKT_BUF_SZ (1536 + RX_PKT_OFFSET) /* minimum number of free TX descriptors required to wake up TX process */ #define B44_TX_WAKEUP_THRESH (B44_TX_RING_SIZE / 4) /* b44 internal pattern match filter info */ #define B44_PATTERN_BASE 0x400 #define B44_PATTERN_SIZE 0x80 #define B44_PMASK_BASE 0x600 #define B44_PMASK_SIZE 0x10 #define B44_MAX_PATTERNS 16 #define B44_ETHIPV6UDP_HLEN 62 #define B44_ETHIPV4UDP_HLEN 42 MODULE_AUTHOR("Felix Fietkau, Florian Schirmer, Pekka Pietikainen, David S. Miller"); MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); static int b44_debug = -1; /* -1 == use B44_DEF_MSG_ENABLE as value */ module_param(b44_debug, int, 0); MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value"); #ifdef CONFIG_B44_PCI static DEFINE_PCI_DEVICE_TABLE(b44_pci_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1) }, { 0 } /* terminate list with empty entry */ }; MODULE_DEVICE_TABLE(pci, b44_pci_tbl); static struct pci_driver b44_pci_driver = { .name = DRV_MODULE_NAME, .id_table = b44_pci_tbl, }; #endif /* CONFIG_B44_PCI */ static const struct ssb_device_id b44_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET, SSB_ANY_REV), SSB_DEVTABLE_END }; MODULE_DEVICE_TABLE(ssb, b44_ssb_tbl); static void b44_halt(struct b44 *); static void b44_init_rings(struct b44 *); #define B44_FULL_RESET 1 #define B44_FULL_RESET_SKIP_PHY 2 #define B44_PARTIAL_RESET 3 #define B44_CHIP_RESET_FULL 4 #define B44_CHIP_RESET_PARTIAL 5 static void b44_init_hw(struct b44 *, int); static int dma_desc_sync_size; static int instance; static const char b44_gstrings[][ETH_GSTRING_LEN] = { #define _B44(x...) # x, B44_STAT_REG_DECLARE #undef _B44 }; static inline void b44_sync_dma_desc_for_device(struct ssb_device *sdev, dma_addr_t dma_base, unsigned long offset, enum dma_data_direction dir) { dma_sync_single_for_device(sdev->dma_dev, dma_base + offset, dma_desc_sync_size, dir); } static inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev, dma_addr_t dma_base, unsigned long offset, enum dma_data_direction dir) { dma_sync_single_for_cpu(sdev->dma_dev, dma_base + offset, dma_desc_sync_size, dir); } static inline unsigned long br32(const struct b44 *bp, unsigned long reg) { return ssb_read32(bp->sdev, reg); } static inline void bw32(const struct b44 *bp, unsigned long reg, unsigned long val) { ssb_write32(bp->sdev, reg, val); } static int b44_wait_bit(struct b44 *bp, unsigned long reg, u32 bit, unsigned long timeout, const int clear) { unsigned long i; for (i = 0; i < timeout; i++) { u32 val = br32(bp, reg); if (clear && !(val & bit)) break; if (!clear && (val & bit)) break; udelay(10); } if (i == timeout) { if (net_ratelimit()) netdev_err(bp->dev, "BUG! Timeout waiting for bit %08x of register %lx to %s\n", bit, reg, clear ? "clear" : "set"); return -ENODEV; } return 0; } static inline void __b44_cam_read(struct b44 *bp, unsigned char *data, int index) { u32 val; bw32(bp, B44_CAM_CTRL, (CAM_CTRL_READ | (index << CAM_CTRL_INDEX_SHIFT))); b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1); val = br32(bp, B44_CAM_DATA_LO); data[2] = (val >> 24) & 0xFF; data[3] = (val >> 16) & 0xFF; data[4] = (val >> 8) & 0xFF; data[5] = (val >> 0) & 0xFF; val = br32(bp, B44_CAM_DATA_HI); data[0] = (val >> 8) & 0xFF; data[1] = (val >> 0) & 0xFF; } static inline void __b44_cam_write(struct b44 *bp, unsigned char *data, int index) { u32 val; val = ((u32) data[2]) << 24; val |= ((u32) data[3]) << 16; val |= ((u32) data[4]) << 8; val |= ((u32) data[5]) << 0; bw32(bp, B44_CAM_DATA_LO, val); val = (CAM_DATA_HI_VALID | (((u32) data[0]) << 8) | (((u32) data[1]) << 0)); bw32(bp, B44_CAM_DATA_HI, val); bw32(bp, B44_CAM_CTRL, (CAM_CTRL_WRITE | (index << CAM_CTRL_INDEX_SHIFT))); b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1); } static inline void __b44_disable_ints(struct b44 *bp) { bw32(bp, B44_IMASK, 0); } static void b44_disable_ints(struct b44 *bp) { __b44_disable_ints(bp); /* Flush posted writes. */ br32(bp, B44_IMASK); } static void b44_enable_ints(struct b44 *bp) { bw32(bp, B44_IMASK, bp->imask); } static int __b44_readphy(struct b44 *bp, int phy_addr, int reg, u32 *val) { int err; bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) | (phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT))); err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); *val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA; return err; } static int __b44_writephy(struct b44 *bp, int phy_addr, int reg, u32 val) { bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) | (phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT) | (val & MDIO_DATA_DATA))); return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); } static inline int b44_readphy(struct b44 *bp, int reg, u32 *val) { if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) return 0; return __b44_readphy(bp, bp->phy_addr, reg, val); } static inline int b44_writephy(struct b44 *bp, int reg, u32 val) { if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) return 0; return __b44_writephy(bp, bp->phy_addr, reg, val); } /* miilib interface */ static int b44_mii_read(struct net_device *dev, int phy_id, int location) { u32 val; struct b44 *bp = netdev_priv(dev); int rc = __b44_readphy(bp, phy_id, location, &val); if (rc) return 0xffffffff; return val; } static void b44_mii_write(struct net_device *dev, int phy_id, int location, int val) { struct b44 *bp = netdev_priv(dev); __b44_writephy(bp, phy_id, location, val); } static int b44_phy_reset(struct b44 *bp) { u32 val; int err; if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) return 0; err = b44_writephy(bp, MII_BMCR, BMCR_RESET); if (err) return err; udelay(100); err = b44_readphy(bp, MII_BMCR, &val); if (!err) { if (val & BMCR_RESET) { netdev_err(bp->dev, "PHY Reset would not complete\n"); err = -ENODEV; } } return err; } static void __b44_set_flow_ctrl(struct b44 *bp, u32 pause_flags) { u32 val; bp->flags &= ~(B44_FLAG_TX_PAUSE | B44_FLAG_RX_PAUSE); bp->flags |= pause_flags; val = br32(bp, B44_RXCONFIG); if (pause_flags & B44_FLAG_RX_PAUSE) val |= RXCONFIG_FLOW; else val &= ~RXCONFIG_FLOW; bw32(bp, B44_RXCONFIG, val); val = br32(bp, B44_MAC_FLOW); if (pause_flags & B44_FLAG_TX_PAUSE) val |= (MAC_FLOW_PAUSE_ENAB | (0xc0 & MAC_FLOW_RX_HI_WATER)); else val &= ~MAC_FLOW_PAUSE_ENAB; bw32(bp, B44_MAC_FLOW, val); } static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote) { u32 pause_enab = 0; /* The driver supports only rx pause by default because the b44 mac tx pause mechanism generates excessive pause frames. Use ethtool to turn on b44 tx pause if necessary. */ if ((local & ADVERTISE_PAUSE_CAP) && (local & ADVERTISE_PAUSE_ASYM)){ if ((remote & LPA_PAUSE_ASYM) && !(remote & LPA_PAUSE_CAP)) pause_enab |= B44_FLAG_RX_PAUSE; } __b44_set_flow_ctrl(bp, pause_enab); } #ifdef CONFIG_BCM47XX #include static void b44_wap54g10_workaround(struct b44 *bp) { char buf[20]; u32 val; int err; /* * workaround for bad hardware design in Linksys WAP54G v1.0 * see https://dev.openwrt.org/ticket/146 * check and reset bit "isolate" */ if (nvram_getenv("boardnum", buf, sizeof(buf)) < 0) return; if (simple_strtoul(buf, NULL, 0) == 2) { err = __b44_readphy(bp, 0, MII_BMCR, &val); if (err) goto error; if (!(val & BMCR_ISOLATE)) return; val &= ~BMCR_ISOLATE; err = __b44_writephy(bp, 0, MII_BMCR, val); if (err) goto error; } return; error: pr_warning("PHY: cannot reset MII transceiver isolate bit\n"); } #else static inline void b44_wap54g10_workaround(struct b44 *bp) { } #endif static int b44_setup_phy(struct b44 *bp) { u32 val; int err; b44_wap54g10_workaround(bp); if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) return 0; if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_ALEDCTRL, val & MII_ALEDCTRL_ALLMSK)) != 0) goto out; if ((err = b44_readphy(bp, B44_MII_TLEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_TLEDCTRL, val | MII_TLEDCTRL_ENABLE)) != 0) goto out; if (!(bp->flags & B44_FLAG_FORCE_LINK)) { u32 adv = ADVERTISE_CSMA; if (bp->flags & B44_FLAG_ADV_10HALF) adv |= ADVERTISE_10HALF; if (bp->flags & B44_FLAG_ADV_10FULL) adv |= ADVERTISE_10FULL; if (bp->flags & B44_FLAG_ADV_100HALF) adv |= ADVERTISE_100HALF; if (bp->flags & B44_FLAG_ADV_100FULL) adv |= ADVERTISE_100FULL; if (bp->flags & B44_FLAG_PAUSE_AUTO) adv |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; if ((err = b44_writephy(bp, MII_ADVERTISE, adv)) != 0) goto out; if ((err = b44_writephy(bp, MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART))) != 0) goto out; } else { u32 bmcr; if ((err = b44_readphy(bp, MII_BMCR, &bmcr)) != 0) goto out; bmcr &= ~(BMCR_FULLDPLX | BMCR_ANENABLE | BMCR_SPEED100); if (bp->flags & B44_FLAG_100_BASE_T) bmcr |= BMCR_SPEED100; if (bp->flags & B44_FLAG_FULL_DUPLEX) bmcr |= BMCR_FULLDPLX; if ((err = b44_writephy(bp, MII_BMCR, bmcr)) != 0) goto out; /* Since we will not be negotiating there is no safe way * to determine if the link partner supports flow control * or not. So just disable it completely in this case. */ b44_set_flow_ctrl(bp, 0, 0); } out: return err; } static void b44_stats_update(struct b44 *bp) { unsigned long reg; u64 *val; val = &bp->hw_stats.tx_good_octets; u64_stats_update_begin(&bp->hw_stats.syncp); for (reg = B44_TX_GOOD_O; reg <= B44_TX_PAUSE; reg += 4UL) { *val++ += br32(bp, reg); } /* Pad */ reg += 8*4UL; for (reg = B44_RX_GOOD_O; reg <= B44_RX_NPAUSE; reg += 4UL) { *val++ += br32(bp, reg); } u64_stats_update_end(&bp->hw_stats.syncp); } static void b44_link_report(struct b44 *bp) { if (!netif_carrier_ok(bp->dev)) { netdev_info(bp->dev, "Link is down\n"); } else { netdev_info(bp->dev, "Link is up at %d Mbps, %s duplex\n", (bp->flags & B44_FLAG_100_BASE_T) ? 100 : 10, (bp->flags & B44_FLAG_FULL_DUPLEX) ? "full" : "half"); netdev_info(bp->dev, "Flow control is %s for TX and %s for RX\n", (bp->flags & B44_FLAG_TX_PAUSE) ? "on" : "off", (bp->flags & B44_FLAG_RX_PAUSE) ? "on" : "off"); } } static void b44_check_phy(struct b44 *bp) { u32 bmsr, aux; if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) { bp->flags |= B44_FLAG_100_BASE_T; bp->flags |= B44_FLAG_FULL_DUPLEX; if (!netif_carrier_ok(bp->dev)) { u32 val = br32(bp, B44_TX_CTRL); val |= TX_CTRL_DUPLEX; bw32(bp, B44_TX_CTRL, val); netif_carrier_on(bp->dev); b44_link_report(bp); } return; } if (!b44_readphy(bp, MII_BMSR, &bmsr) && !b44_readphy(bp, B44_MII_AUXCTRL, &aux) && (bmsr != 0xffff)) { if (aux & MII_AUXCTRL_SPEED) bp->flags |= B44_FLAG_100_BASE_T; else bp->flags &= ~B44_FLAG_100_BASE_T; if (aux & MII_AUXCTRL_DUPLEX) bp->flags |= B44_FLAG_FULL_DUPLEX; else bp->flags &= ~B44_FLAG_FULL_DUPLEX; if (!netif_carrier_ok(bp->dev) && (bmsr & BMSR_LSTATUS)) { u32 val = br32(bp, B44_TX_CTRL); u32 local_adv, remote_adv; if (bp->flags & B44_FLAG_FULL_DUPLEX) val |= TX_CTRL_DUPLEX; else val &= ~TX_CTRL_DUPLEX; bw32(bp, B44_TX_CTRL, val); if (!(bp->flags & B44_FLAG_FORCE_LINK) && !b44_readphy(bp, MII_ADVERTISE, &local_adv) && !b44_readphy(bp, MII_LPA, &remote_adv)) b44_set_flow_ctrl(bp, local_adv, remote_adv); /* Link now up */ netif_carrier_on(bp->dev); b44_link_report(bp); } else if (netif_carrier_ok(bp->dev) && !(bmsr & BMSR_LSTATUS)) { /* Link now down */ netif_carrier_off(bp->dev); b44_link_report(bp); } if (bmsr & BMSR_RFAULT) netdev_warn(bp->dev, "Remote fault detected in PHY\n"); if (bmsr & BMSR_JCD) netdev_warn(bp->dev, "Jabber detected in PHY\n"); } } static void b44_timer(unsigned long __opaque) { struct b44 *bp = (struct b44 *) __opaque; spin_lock_irq(&bp->lock); b44_check_phy(bp); b44_stats_update(bp); spin_unlock_irq(&bp->lock); mod_timer(&bp->timer, round_jiffies(jiffies + HZ)); } static void b44_tx(struct b44 *bp) { u32 cur, cons; cur = br32(bp, B44_DMATX_STAT) & DMATX_STAT_CDMASK; cur /= sizeof(struct dma_desc); /* XXX needs updating when NETIF_F_SG is supported */ for (cons = bp->tx_cons; cons != cur; cons = NEXT_TX(cons)) { struct ring_info *rp = &bp->tx_buffers[cons]; struct sk_buff *skb = rp->skb; BUG_ON(skb == NULL); dma_unmap_single(bp->sdev->dma_dev, rp->mapping, skb->len, DMA_TO_DEVICE); rp->skb = NULL; dev_kfree_skb_irq(skb); } bp->tx_cons = cons; if (netif_queue_stopped(bp->dev) && TX_BUFFS_AVAIL(bp) > B44_TX_WAKEUP_THRESH) netif_wake_queue(bp->dev); bw32(bp, B44_GPTIMER, 0); } /* Works like this. This chip writes a 'struct rx_header" 30 bytes * before the DMA address you give it. So we allocate 30 more bytes * for the RX buffer, DMA map all of it, skb_reserve the 30 bytes, then * point the chip at 30 bytes past where the rx_header will go. */ static int b44_alloc_rx_skb(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) { struct dma_desc *dp; struct ring_info *src_map, *map; struct rx_header *rh; struct sk_buff *skb; dma_addr_t mapping; int dest_idx; u32 ctrl; src_map = NULL; if (src_idx >= 0) src_map = &bp->rx_buffers[src_idx]; dest_idx = dest_idx_unmasked & (B44_RX_RING_SIZE - 1); map = &bp->rx_buffers[dest_idx]; skb = netdev_alloc_skb(bp->dev, RX_PKT_BUF_SZ); if (skb == NULL) return -ENOMEM; mapping = dma_map_single(bp->sdev->dma_dev, skb->data, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); /* Hardware bug work-around, the chip is unable to do PCI DMA to/from anything above 1GB :-( */ if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + RX_PKT_BUF_SZ > DMA_BIT_MASK(30)) { /* Sigh... */ if (!dma_mapping_error(bp->sdev->dma_dev, mapping)) dma_unmap_single(bp->sdev->dma_dev, mapping, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); dev_kfree_skb_any(skb); skb = alloc_skb(RX_PKT_BUF_SZ, GFP_ATOMIC | GFP_DMA); if (skb == NULL) return -ENOMEM; mapping = dma_map_single(bp->sdev->dma_dev, skb->data, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + RX_PKT_BUF_SZ > DMA_BIT_MASK(30)) { if (!dma_mapping_error(bp->sdev->dma_dev, mapping)) dma_unmap_single(bp->sdev->dma_dev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE); dev_kfree_skb_any(skb); return -ENOMEM; } bp->force_copybreak = 1; } rh = (struct rx_header *) skb->data; rh->len = 0; rh->flags = 0; map->skb = skb; map->mapping = mapping; if (src_map != NULL) src_map->skb = NULL; ctrl = (DESC_CTRL_LEN & RX_PKT_BUF_SZ); if (dest_idx == (B44_RX_RING_SIZE - 1)) ctrl |= DESC_CTRL_EOT; dp = &bp->rx_ring[dest_idx]; dp->ctrl = cpu_to_le32(ctrl); dp->addr = cpu_to_le32((u32) mapping + bp->dma_offset); if (bp->flags & B44_FLAG_RX_RING_HACK) b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma, dest_idx * sizeof(*dp), DMA_BIDIRECTIONAL); return RX_PKT_BUF_SZ; } static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) { struct dma_desc *src_desc, *dest_desc; struct ring_info *src_map, *dest_map; struct rx_header *rh; int dest_idx; __le32 ctrl; dest_idx = dest_idx_unmasked & (B44_RX_RING_SIZE - 1); dest_desc = &bp->rx_ring[dest_idx]; dest_map = &bp->rx_buffers[dest_idx]; src_desc = &bp->rx_ring[src_idx]; src_map = &bp->rx_buffers[src_idx]; dest_map->skb = src_map->skb; rh = (struct rx_header *) src_map->skb->data; rh->len = 0; rh->flags = 0; dest_map->mapping = src_map->mapping; if (bp->flags & B44_FLAG_RX_RING_HACK) b44_sync_dma_desc_for_cpu(bp->sdev, bp->rx_ring_dma, src_idx * sizeof(*src_desc), DMA_BIDIRECTIONAL); ctrl = src_desc->ctrl; if (dest_idx == (B44_RX_RING_SIZE - 1)) ctrl |= cpu_to_le32(DESC_CTRL_EOT); else ctrl &= cpu_to_le32(~DESC_CTRL_EOT); dest_desc->ctrl = ctrl; dest_desc->addr = src_desc->addr; src_map->skb = NULL; if (bp->flags & B44_FLAG_RX_RING_HACK) b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma, dest_idx * sizeof(*dest_desc), DMA_BIDIRECTIONAL); dma_sync_single_for_device(bp->sdev->dma_dev, dest_map->mapping, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); } static int b44_rx(struct b44 *bp, int budget) { int received; u32 cons, prod; received = 0; prod = br32(bp, B44_DMARX_STAT) & DMARX_STAT_CDMASK; prod /= sizeof(struct dma_desc); cons = bp->rx_cons; while (cons != prod && budget > 0) { struct ring_info *rp = &bp->rx_buffers[cons]; struct sk_buff *skb = rp->skb; dma_addr_t map = rp->mapping; struct rx_header *rh; u16 len; dma_sync_single_for_cpu(bp->sdev->dma_dev, map, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); rh = (struct rx_header *) skb->data; len = le16_to_cpu(rh->len); if ((len > (RX_PKT_BUF_SZ - RX_PKT_OFFSET)) || (rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) { drop_it: b44_recycle_rx(bp, cons, bp->rx_prod); drop_it_no_recycle: bp->dev->stats.rx_dropped++; goto next_pkt; } if (len == 0) { int i = 0; do { udelay(2); barrier(); len = le16_to_cpu(rh->len); } while (len == 0 && i++ < 5); if (len == 0) goto drop_it; } /* Omit CRC. */ len -= 4; if (!bp->force_copybreak && len > RX_COPY_THRESHOLD) { int skb_size; skb_size = b44_alloc_rx_skb(bp, cons, bp->rx_prod); if (skb_size < 0) goto drop_it; dma_unmap_single(bp->sdev->dma_dev, map, skb_size, DMA_FROM_DEVICE); /* Leave out rx_header */ skb_put(skb, len + RX_PKT_OFFSET); skb_pull(skb, RX_PKT_OFFSET); } else { struct sk_buff *copy_skb; b44_recycle_rx(bp, cons, bp->rx_prod); copy_skb = netdev_alloc_skb(bp->dev, len + 2); if (copy_skb == NULL) goto drop_it_no_recycle; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); /* DMA sync done above, copy just the actual packet */ skb_copy_from_linear_data_offset(skb, RX_PKT_OFFSET, copy_skb->data, len); skb = copy_skb; } skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, bp->dev); netif_receive_skb(skb); received++; budget--; next_pkt: bp->rx_prod = (bp->rx_prod + 1) & (B44_RX_RING_SIZE - 1); cons = (cons + 1) & (B44_RX_RING_SIZE - 1); } bp->rx_cons = cons; bw32(bp, B44_DMARX_PTR, cons * sizeof(struct dma_desc)); return received; } static int b44_poll(struct napi_struct *napi, int budget) { struct b44 *bp = container_of(napi, struct b44, napi); int work_done; unsigned long flags; spin_lock_irqsave(&bp->lock, flags); if (bp->istat & (ISTAT_TX | ISTAT_TO)) { /* spin_lock(&bp->tx_lock); */ b44_tx(bp); /* spin_unlock(&bp->tx_lock); */ } if (bp->istat & ISTAT_RFO) { /* fast recovery, in ~20msec */ bp->istat &= ~ISTAT_RFO; b44_disable_ints(bp); ssb_device_enable(bp->sdev, 0); /* resets ISTAT_RFO */ b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY); netif_wake_queue(bp->dev); } spin_unlock_irqrestore(&bp->lock, flags); work_done = 0; if (bp->istat & ISTAT_RX) work_done += b44_rx(bp, budget); if (bp->istat & ISTAT_ERRORS) { spin_lock_irqsave(&bp->lock, flags); b44_halt(bp); b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY); netif_wake_queue(bp->dev); spin_unlock_irqrestore(&bp->lock, flags); work_done = 0; } if (work_done < budget) { napi_complete(napi); b44_enable_ints(bp); } return work_done; } static irqreturn_t b44_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct b44 *bp = netdev_priv(dev); u32 istat, imask; int handled = 0; spin_lock(&bp->lock); istat = br32(bp, B44_ISTAT); imask = br32(bp, B44_IMASK); /* The interrupt mask register controls which interrupt bits * will actually raise an interrupt to the CPU when set by hw/firmware, * but doesn't mask off the bits. */ istat &= imask; if (istat) { handled = 1; if (unlikely(!netif_running(dev))) { netdev_info(dev, "late interrupt\n"); goto irq_ack; } if (napi_schedule_prep(&bp->napi)) { /* NOTE: These writes are posted by the readback of * the ISTAT register below. */ bp->istat = istat; __b44_disable_ints(bp); __napi_schedule(&bp->napi); } irq_ack: bw32(bp, B44_ISTAT, istat); br32(bp, B44_ISTAT); } spin_unlock(&bp->lock); return IRQ_RETVAL(handled); } static void b44_tx_timeout(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); netdev_err(dev, "transmit timed out, resetting\n"); spin_lock_irq(&bp->lock); b44_halt(bp); b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET); spin_unlock_irq(&bp->lock); b44_enable_ints(bp); netif_wake_queue(dev); } static netdev_tx_t b44_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct b44 *bp = netdev_priv(dev); int rc = NETDEV_TX_OK; dma_addr_t mapping; u32 len, entry, ctrl; unsigned long flags; len = skb->len; spin_lock_irqsave(&bp->lock, flags); /* This is a hard error, log it. */ if (unlikely(TX_BUFFS_AVAIL(bp) < 1)) { netif_stop_queue(dev); netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); goto err_out; } mapping = dma_map_single(bp->sdev->dma_dev, skb->data, len, DMA_TO_DEVICE); if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + len > DMA_BIT_MASK(30)) { struct sk_buff *bounce_skb; /* Chip can't handle DMA to/from >1GB, use bounce buffer */ if (!dma_mapping_error(bp->sdev->dma_dev, mapping)) dma_unmap_single(bp->sdev->dma_dev, mapping, len, DMA_TO_DEVICE); bounce_skb = alloc_skb(len, GFP_ATOMIC | GFP_DMA); if (!bounce_skb) goto err_out; mapping = dma_map_single(bp->sdev->dma_dev, bounce_skb->data, len, DMA_TO_DEVICE); if (dma_mapping_error(bp->sdev->dma_dev, mapping) || mapping + len > DMA_BIT_MASK(30)) { if (!dma_mapping_error(bp->sdev->dma_dev, mapping)) dma_unmap_single(bp->sdev->dma_dev, mapping, len, DMA_TO_DEVICE); dev_kfree_skb_any(bounce_skb); goto err_out; } skb_copy_from_linear_data(skb, skb_put(bounce_skb, len), len); dev_kfree_skb_any(skb); skb = bounce_skb; } entry = bp->tx_prod; bp->tx_buffers[entry].skb = skb; bp->tx_buffers[entry].mapping = mapping; ctrl = (len & DESC_CTRL_LEN); ctrl |= DESC_CTRL_IOC | DESC_CTRL_SOF | DESC_CTRL_EOF; if (entry == (B44_TX_RING_SIZE - 1)) ctrl |= DESC_CTRL_EOT; bp->tx_ring[entry].ctrl = cpu_to_le32(ctrl); bp->tx_ring[entry].addr = cpu_to_le32((u32) mapping+bp->dma_offset); if (bp->flags & B44_FLAG_TX_RING_HACK) b44_sync_dma_desc_for_device(bp->sdev, bp->tx_ring_dma, entry * sizeof(bp->tx_ring[0]), DMA_TO_DEVICE); entry = NEXT_TX(entry); bp->tx_prod = entry; wmb(); bw32(bp, B44_DMATX_PTR, entry * sizeof(struct dma_desc)); if (bp->flags & B44_FLAG_BUGGY_TXPTR) bw32(bp, B44_DMATX_PTR, entry * sizeof(struct dma_desc)); if (bp->flags & B44_FLAG_REORDER_BUG) br32(bp, B44_DMATX_PTR); if (TX_BUFFS_AVAIL(bp) < 1) netif_stop_queue(dev); out_unlock: spin_unlock_irqrestore(&bp->lock, flags); return rc; err_out: rc = NETDEV_TX_BUSY; goto out_unlock; } static int b44_change_mtu(struct net_device *dev, int new_mtu) { struct b44 *bp = netdev_priv(dev); if (new_mtu < B44_MIN_MTU || new_mtu > B44_MAX_MTU) return -EINVAL; if (!netif_running(dev)) { /* We'll just catch it later when the * device is up'd. */ dev->mtu = new_mtu; return 0; } spin_lock_irq(&bp->lock); b44_halt(bp); dev->mtu = new_mtu; b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET); spin_unlock_irq(&bp->lock); b44_enable_ints(bp); return 0; } /* Free up pending packets in all rx/tx rings. * * The chip has been shut down and the driver detached from * the networking, so no interrupts or new tx packets will * end up in the driver. bp->lock is not held and we are not * in an interrupt context and thus may sleep. */ static void b44_free_rings(struct b44 *bp) { struct ring_info *rp; int i; for (i = 0; i < B44_RX_RING_SIZE; i++) { rp = &bp->rx_buffers[i]; if (rp->skb == NULL) continue; dma_unmap_single(bp->sdev->dma_dev, rp->mapping, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } /* XXX needs changes once NETIF_F_SG is set... */ for (i = 0; i < B44_TX_RING_SIZE; i++) { rp = &bp->tx_buffers[i]; if (rp->skb == NULL) continue; dma_unmap_single(bp->sdev->dma_dev, rp->mapping, rp->skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } } /* Initialize tx/rx rings for packet processing. * * The chip has been shut down and the driver detached from * the networking, so no interrupts or new tx packets will * end up in the driver. */ static void b44_init_rings(struct b44 *bp) { int i; b44_free_rings(bp); memset(bp->rx_ring, 0, B44_RX_RING_BYTES); memset(bp->tx_ring, 0, B44_TX_RING_BYTES); if (bp->flags & B44_FLAG_RX_RING_HACK) dma_sync_single_for_device(bp->sdev->dma_dev, bp->rx_ring_dma, DMA_TABLE_BYTES, DMA_BIDIRECTIONAL); if (bp->flags & B44_FLAG_TX_RING_HACK) dma_sync_single_for_device(bp->sdev->dma_dev, bp->tx_ring_dma, DMA_TABLE_BYTES, DMA_TO_DEVICE); for (i = 0; i < bp->rx_pending; i++) { if (b44_alloc_rx_skb(bp, -1, i) < 0) break; } } /* * Must not be invoked with interrupt sources disabled and * the hardware shutdown down. */ static void b44_free_consistent(struct b44 *bp) { kfree(bp->rx_buffers); bp->rx_buffers = NULL; kfree(bp->tx_buffers); bp->tx_buffers = NULL; if (bp->rx_ring) { if (bp->flags & B44_FLAG_RX_RING_HACK) { dma_unmap_single(bp->sdev->dma_dev, bp->rx_ring_dma, DMA_TABLE_BYTES, DMA_BIDIRECTIONAL); kfree(bp->rx_ring); } else dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES, bp->rx_ring, bp->rx_ring_dma); bp->rx_ring = NULL; bp->flags &= ~B44_FLAG_RX_RING_HACK; } if (bp->tx_ring) { if (bp->flags & B44_FLAG_TX_RING_HACK) { dma_unmap_single(bp->sdev->dma_dev, bp->tx_ring_dma, DMA_TABLE_BYTES, DMA_TO_DEVICE); kfree(bp->tx_ring); } else dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES, bp->tx_ring, bp->tx_ring_dma); bp->tx_ring = NULL; bp->flags &= ~B44_FLAG_TX_RING_HACK; } } /* * Must not be invoked with interrupt sources disabled and * the hardware shutdown down. Can sleep. */ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) { int size; size = B44_RX_RING_SIZE * sizeof(struct ring_info); bp->rx_buffers = kzalloc(size, gfp); if (!bp->rx_buffers) goto out_err; size = B44_TX_RING_SIZE * sizeof(struct ring_info); bp->tx_buffers = kzalloc(size, gfp); if (!bp->tx_buffers) goto out_err; size = DMA_TABLE_BYTES; bp->rx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size, &bp->rx_ring_dma, gfp); if (!bp->rx_ring) { /* Allocation may have failed due to pci_alloc_consistent insisting on use of GFP_DMA, which is more restrictive than necessary... */ struct dma_desc *rx_ring; dma_addr_t rx_ring_dma; rx_ring = kzalloc(size, gfp); if (!rx_ring) goto out_err; rx_ring_dma = dma_map_single(bp->sdev->dma_dev, rx_ring, DMA_TABLE_BYTES, DMA_BIDIRECTIONAL); if (dma_mapping_error(bp->sdev->dma_dev, rx_ring_dma) || rx_ring_dma + size > DMA_BIT_MASK(30)) { kfree(rx_ring); goto out_err; } bp->rx_ring = rx_ring; bp->rx_ring_dma = rx_ring_dma; bp->flags |= B44_FLAG_RX_RING_HACK; } bp->tx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size, &bp->tx_ring_dma, gfp); if (!bp->tx_ring) { /* Allocation may have failed due to ssb_dma_alloc_consistent insisting on use of GFP_DMA, which is more restrictive than necessary... */ struct dma_desc *tx_ring; dma_addr_t tx_ring_dma; tx_ring = kzalloc(size, gfp); if (!tx_ring) goto out_err; tx_ring_dma = dma_map_single(bp->sdev->dma_dev, tx_ring, DMA_TABLE_BYTES, DMA_TO_DEVICE); if (dma_mapping_error(bp->sdev->dma_dev, tx_ring_dma) || tx_ring_dma + size > DMA_BIT_MASK(30)) { kfree(tx_ring); goto out_err; } bp->tx_ring = tx_ring; bp->tx_ring_dma = tx_ring_dma; bp->flags |= B44_FLAG_TX_RING_HACK; } return 0; out_err: b44_free_consistent(bp); return -ENOMEM; } /* bp->lock is held. */ static void b44_clear_stats(struct b44 *bp) { unsigned long reg; bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ); for (reg = B44_TX_GOOD_O; reg <= B44_TX_PAUSE; reg += 4UL) br32(bp, reg); for (reg = B44_RX_GOOD_O; reg <= B44_RX_NPAUSE; reg += 4UL) br32(bp, reg); } /* bp->lock is held. */ static void b44_chip_reset(struct b44 *bp, int reset_kind) { struct ssb_device *sdev = bp->sdev; bool was_enabled; was_enabled = ssb_device_is_enabled(bp->sdev); ssb_device_enable(bp->sdev, 0); ssb_pcicore_dev_irqvecs_enable(&sdev->bus->pcicore, sdev); if (was_enabled) { bw32(bp, B44_RCV_LAZY, 0); bw32(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE); b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 200, 1); bw32(bp, B44_DMATX_CTRL, 0); bp->tx_prod = bp->tx_cons = 0; if (br32(bp, B44_DMARX_STAT) & DMARX_STAT_EMASK) { b44_wait_bit(bp, B44_DMARX_STAT, DMARX_STAT_SIDLE, 100, 0); } bw32(bp, B44_DMARX_CTRL, 0); bp->rx_prod = bp->rx_cons = 0; } b44_clear_stats(bp); /* * Don't enable PHY if we are doing a partial reset * we are probably going to power down */ if (reset_kind == B44_CHIP_RESET_PARTIAL) return; switch (sdev->bus->bustype) { case SSB_BUSTYPE_SSB: bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE | (DIV_ROUND_CLOSEST(ssb_clockspeed(sdev->bus), B44_MDC_RATIO) & MDIO_CTRL_MAXF_MASK))); break; case SSB_BUSTYPE_PCI: bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE | (0x0d & MDIO_CTRL_MAXF_MASK))); break; case SSB_BUSTYPE_PCMCIA: case SSB_BUSTYPE_SDIO: WARN_ON(1); /* A device with this bus does not exist. */ break; } br32(bp, B44_MDIO_CTRL); if (!(br32(bp, B44_DEVCTRL) & DEVCTRL_IPP)) { bw32(bp, B44_ENET_CTRL, ENET_CTRL_EPSEL); br32(bp, B44_ENET_CTRL); bp->flags &= ~B44_FLAG_INTERNAL_PHY; } else { u32 val = br32(bp, B44_DEVCTRL); if (val & DEVCTRL_EPR) { bw32(bp, B44_DEVCTRL, (val & ~DEVCTRL_EPR)); br32(bp, B44_DEVCTRL); udelay(100); } bp->flags |= B44_FLAG_INTERNAL_PHY; } } /* bp->lock is held. */ static void b44_halt(struct b44 *bp) { b44_disable_ints(bp); /* reset PHY */ b44_phy_reset(bp); /* power down PHY */ netdev_info(bp->dev, "powering down PHY\n"); bw32(bp, B44_MAC_CTRL, MAC_CTRL_PHY_PDOWN); /* now reset the chip, but without enabling the MAC&PHY * part of it. This has to be done _after_ we shut down the PHY */ b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL); } /* bp->lock is held. */ static void __b44_set_mac_addr(struct b44 *bp) { bw32(bp, B44_CAM_CTRL, 0); if (!(bp->dev->flags & IFF_PROMISC)) { u32 val; __b44_cam_write(bp, bp->dev->dev_addr, 0); val = br32(bp, B44_CAM_CTRL); bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE); } } static int b44_set_mac_addr(struct net_device *dev, void *p) { struct b44 *bp = netdev_priv(dev); struct sockaddr *addr = p; u32 val; if (netif_running(dev)) return -EBUSY; if (!is_valid_ether_addr(addr->sa_data)) return -EINVAL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); spin_lock_irq(&bp->lock); val = br32(bp, B44_RXCONFIG); if (!(val & RXCONFIG_CAM_ABSENT)) __b44_set_mac_addr(bp); spin_unlock_irq(&bp->lock); return 0; } /* Called at device open time to get the chip ready for * packet processing. Invoked with bp->lock held. */ static void __b44_set_rx_mode(struct net_device *); static void b44_init_hw(struct b44 *bp, int reset_kind) { u32 val; b44_chip_reset(bp, B44_CHIP_RESET_FULL); if (reset_kind == B44_FULL_RESET) { b44_phy_reset(bp); b44_setup_phy(bp); } /* Enable CRC32, set proper LED modes and power on PHY */ bw32(bp, B44_MAC_CTRL, MAC_CTRL_CRC32_ENAB | MAC_CTRL_PHY_LEDCTRL); bw32(bp, B44_RCV_LAZY, (1 << RCV_LAZY_FC_SHIFT)); /* This sets the MAC address too. */ __b44_set_rx_mode(bp->dev); /* MTU + eth header + possible VLAN tag + struct rx_header */ bw32(bp, B44_RXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN); bw32(bp, B44_TXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN); bw32(bp, B44_TX_WMARK, 56); /* XXX magic */ if (reset_kind == B44_PARTIAL_RESET) { bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE | (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT))); } else { bw32(bp, B44_DMATX_CTRL, DMATX_CTRL_ENABLE); bw32(bp, B44_DMATX_ADDR, bp->tx_ring_dma + bp->dma_offset); bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE | (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT))); bw32(bp, B44_DMARX_ADDR, bp->rx_ring_dma + bp->dma_offset); bw32(bp, B44_DMARX_PTR, bp->rx_pending); bp->rx_prod = bp->rx_pending; bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ); } val = br32(bp, B44_ENET_CTRL); bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE)); } static int b44_open(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); int err; err = b44_alloc_consistent(bp, GFP_KERNEL); if (err) goto out; napi_enable(&bp->napi); b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET); b44_check_phy(bp); err = request_irq(dev->irq, b44_interrupt, IRQF_SHARED, dev->name, dev); if (unlikely(err < 0)) { napi_disable(&bp->napi); b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL); b44_free_rings(bp); b44_free_consistent(bp); goto out; } init_timer(&bp->timer); bp->timer.expires = jiffies + HZ; bp->timer.data = (unsigned long) bp; bp->timer.function = b44_timer; add_timer(&bp->timer); b44_enable_ints(bp); netif_start_queue(dev); out: return err; } #ifdef CONFIG_NET_POLL_CONTROLLER /* * Polling receive - used by netconsole and other diagnostic tools * to allow network i/o with interrupts disabled. */ static void b44_poll_controller(struct net_device *dev) { disable_irq(dev->irq); b44_interrupt(dev->irq, dev); enable_irq(dev->irq); } #endif static void bwfilter_table(struct b44 *bp, u8 *pp, u32 bytes, u32 table_offset) { u32 i; u32 *pattern = (u32 *) pp; for (i = 0; i < bytes; i += sizeof(u32)) { bw32(bp, B44_FILT_ADDR, table_offset + i); bw32(bp, B44_FILT_DATA, pattern[i / sizeof(u32)]); } } static int b44_magic_pattern(u8 *macaddr, u8 *ppattern, u8 *pmask, int offset) { int magicsync = 6; int k, j, len = offset; int ethaddr_bytes = ETH_ALEN; memset(ppattern + offset, 0xff, magicsync); for (j = 0; j < magicsync; j++) set_bit(len++, (unsigned long *) pmask); for (j = 0; j < B44_MAX_PATTERNS; j++) { if ((B44_PATTERN_SIZE - len) >= ETH_ALEN) ethaddr_bytes = ETH_ALEN; else ethaddr_bytes = B44_PATTERN_SIZE - len; if (ethaddr_bytes <=0) break; for (k = 0; k< ethaddr_bytes; k++) { ppattern[offset + magicsync + (j * ETH_ALEN) + k] = macaddr[k]; set_bit(len++, (unsigned long *) pmask); } } return len - 1; } /* Setup magic packet patterns in the b44 WOL * pattern matching filter. */ static void b44_setup_pseudo_magicp(struct b44 *bp) { u32 val; int plen0, plen1, plen2; u8 *pwol_pattern; u8 pwol_mask[B44_PMASK_SIZE]; pwol_pattern = kzalloc(B44_PATTERN_SIZE, GFP_KERNEL); if (!pwol_pattern) { pr_err("Memory not available for WOL\n"); return; } /* Ipv4 magic packet pattern - pattern 0.*/ memset(pwol_mask, 0, B44_PMASK_SIZE); plen0 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask, B44_ETHIPV4UDP_HLEN); bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, B44_PATTERN_BASE); bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, B44_PMASK_BASE); /* Raw ethernet II magic packet pattern - pattern 1 */ memset(pwol_pattern, 0, B44_PATTERN_SIZE); memset(pwol_mask, 0, B44_PMASK_SIZE); plen1 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask, ETH_HLEN); bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, B44_PATTERN_BASE + B44_PATTERN_SIZE); bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, B44_PMASK_BASE + B44_PMASK_SIZE); /* Ipv6 magic packet pattern - pattern 2 */ memset(pwol_pattern, 0, B44_PATTERN_SIZE); memset(pwol_mask, 0, B44_PMASK_SIZE); plen2 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask, B44_ETHIPV6UDP_HLEN); bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, B44_PATTERN_BASE + B44_PATTERN_SIZE + B44_PATTERN_SIZE); bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, B44_PMASK_BASE + B44_PMASK_SIZE + B44_PMASK_SIZE); kfree(pwol_pattern); /* set these pattern's lengths: one less than each real length */ val = plen0 | (plen1 << 8) | (plen2 << 16) | WKUP_LEN_ENABLE_THREE; bw32(bp, B44_WKUP_LEN, val); /* enable wakeup pattern matching */ val = br32(bp, B44_DEVCTRL); bw32(bp, B44_DEVCTRL, val | DEVCTRL_PFE); } #ifdef CONFIG_B44_PCI static void b44_setup_wol_pci(struct b44 *bp) { u16 val; if (bp->sdev->bus->bustype != SSB_BUSTYPE_SSB) { bw32(bp, SSB_TMSLOW, br32(bp, SSB_TMSLOW) | SSB_TMSLOW_PE); pci_read_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, &val); pci_write_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, val | SSB_PE); } } #else static inline void b44_setup_wol_pci(struct b44 *bp) { } #endif /* CONFIG_B44_PCI */ static void b44_setup_wol(struct b44 *bp) { u32 val; bw32(bp, B44_RXCONFIG, RXCONFIG_ALLMULTI); if (bp->flags & B44_FLAG_B0_ANDLATER) { bw32(bp, B44_WKUP_LEN, WKUP_LEN_DISABLE); val = bp->dev->dev_addr[2] << 24 | bp->dev->dev_addr[3] << 16 | bp->dev->dev_addr[4] << 8 | bp->dev->dev_addr[5]; bw32(bp, B44_ADDR_LO, val); val = bp->dev->dev_addr[0] << 8 | bp->dev->dev_addr[1]; bw32(bp, B44_ADDR_HI, val); val = br32(bp, B44_DEVCTRL); bw32(bp, B44_DEVCTRL, val | DEVCTRL_MPM | DEVCTRL_PFE); } else { b44_setup_pseudo_magicp(bp); } b44_setup_wol_pci(bp); } static int b44_close(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); netif_stop_queue(dev); napi_disable(&bp->napi); del_timer_sync(&bp->timer); spin_lock_irq(&bp->lock); b44_halt(bp); b44_free_rings(bp); netif_carrier_off(dev); spin_unlock_irq(&bp->lock); free_irq(dev->irq, dev); if (bp->flags & B44_FLAG_WOL_ENABLE) { b44_init_hw(bp, B44_PARTIAL_RESET); b44_setup_wol(bp); } b44_free_consistent(bp); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) static struct rtnl_link_stats64 *b44_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *nstat) #else static struct net_device_stats *b44_get_stats(struct net_device *dev) #endif { struct b44 *bp = netdev_priv(dev); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) struct net_device_stats *nstat = &dev->stats; #endif struct b44_hw_stats *hwstat = &bp->hw_stats; unsigned int start; do { start = u64_stats_fetch_begin_bh(&hwstat->syncp); /* Convert HW stats into rtnl_link_stats64 stats. */ nstat->rx_packets = hwstat->rx_pkts; nstat->tx_packets = hwstat->tx_pkts; nstat->rx_bytes = hwstat->rx_octets; nstat->tx_bytes = hwstat->tx_octets; nstat->tx_errors = (hwstat->tx_jabber_pkts + hwstat->tx_oversize_pkts + hwstat->tx_underruns + hwstat->tx_excessive_cols + hwstat->tx_late_cols); nstat->multicast = hwstat->tx_multicast_pkts; nstat->collisions = hwstat->tx_total_cols; nstat->rx_length_errors = (hwstat->rx_oversize_pkts + hwstat->rx_undersize); nstat->rx_over_errors = hwstat->rx_missed_pkts; nstat->rx_frame_errors = hwstat->rx_align_errs; nstat->rx_crc_errors = hwstat->rx_crc_errs; nstat->rx_errors = (hwstat->rx_jabber_pkts + hwstat->rx_oversize_pkts + hwstat->rx_missed_pkts + hwstat->rx_crc_align_errs + hwstat->rx_undersize + hwstat->rx_crc_errs + hwstat->rx_align_errs + hwstat->rx_symbol_errs); nstat->tx_aborted_errors = hwstat->tx_underruns; #if 0 /* Carrier lost counter seems to be broken for some devices */ nstat->tx_carrier_errors = hwstat->tx_carrier_lost; #endif } while (u64_stats_fetch_retry_bh(&hwstat->syncp, start)); return nstat; } static int __b44_load_mcast(struct b44 *bp, struct net_device *dev) { struct netdev_hw_addr *ha; int i, num_ents; num_ents = min_t(int, netdev_mc_count(dev), B44_MCAST_TABLE_SIZE); i = 0; netdev_for_each_mc_addr(ha, dev) { if (i == num_ents) break; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __b44_cam_write(bp, ha->addr, i++ + 1); #else __b44_cam_write(bp, ha->dmi_addr, i++ + 1); #endif } return i+1; } static void __b44_set_rx_mode(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); u32 val; val = br32(bp, B44_RXCONFIG); val &= ~(RXCONFIG_PROMISC | RXCONFIG_ALLMULTI); if ((dev->flags & IFF_PROMISC) || (val & RXCONFIG_CAM_ABSENT)) { val |= RXCONFIG_PROMISC; bw32(bp, B44_RXCONFIG, val); } else { unsigned char zero[6] = {0, 0, 0, 0, 0, 0}; int i = 1; __b44_set_mac_addr(bp); if ((dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > B44_MCAST_TABLE_SIZE)) val |= RXCONFIG_ALLMULTI; else i = __b44_load_mcast(bp, dev); for (; i < 64; i++) __b44_cam_write(bp, zero, i); bw32(bp, B44_RXCONFIG, val); val = br32(bp, B44_CAM_CTRL); bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE); } } static void b44_set_rx_mode(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); spin_lock_irq(&bp->lock); __b44_set_rx_mode(dev); spin_unlock_irq(&bp->lock); } static u32 b44_get_msglevel(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); return bp->msg_enable; } static void b44_set_msglevel(struct net_device *dev, u32 value) { struct b44 *bp = netdev_priv(dev); bp->msg_enable = value; } static void b44_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) { struct b44 *bp = netdev_priv(dev); struct ssb_bus *bus = bp->sdev->bus; strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); switch (bus->bustype) { case SSB_BUSTYPE_PCI: strlcpy(info->bus_info, pci_name(bus->host_pci), sizeof(info->bus_info)); break; case SSB_BUSTYPE_SSB: strlcpy(info->bus_info, "SSB", sizeof(info->bus_info)); break; case SSB_BUSTYPE_PCMCIA: case SSB_BUSTYPE_SDIO: WARN_ON(1); /* A device with this bus does not exist. */ break; } } static int b44_nway_reset(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); u32 bmcr; int r; spin_lock_irq(&bp->lock); b44_readphy(bp, MII_BMCR, &bmcr); b44_readphy(bp, MII_BMCR, &bmcr); r = -EINVAL; if (bmcr & BMCR_ANENABLE) { b44_writephy(bp, MII_BMCR, bmcr | BMCR_ANRESTART); r = 0; } spin_unlock_irq(&bp->lock); return r; } static int b44_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct b44 *bp = netdev_priv(dev); cmd->supported = (SUPPORTED_Autoneg); cmd->supported |= (SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_MII); cmd->advertising = 0; if (bp->flags & B44_FLAG_ADV_10HALF) cmd->advertising |= ADVERTISED_10baseT_Half; if (bp->flags & B44_FLAG_ADV_10FULL) cmd->advertising |= ADVERTISED_10baseT_Full; if (bp->flags & B44_FLAG_ADV_100HALF) cmd->advertising |= ADVERTISED_100baseT_Half; if (bp->flags & B44_FLAG_ADV_100FULL) cmd->advertising |= ADVERTISED_100baseT_Full; cmd->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; ethtool_cmd_speed_set(cmd, ((bp->flags & B44_FLAG_100_BASE_T) ? SPEED_100 : SPEED_10)); cmd->duplex = (bp->flags & B44_FLAG_FULL_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; cmd->port = 0; cmd->phy_address = bp->phy_addr; cmd->transceiver = (bp->flags & B44_FLAG_INTERNAL_PHY) ? XCVR_INTERNAL : XCVR_EXTERNAL; cmd->autoneg = (bp->flags & B44_FLAG_FORCE_LINK) ? AUTONEG_DISABLE : AUTONEG_ENABLE; if (cmd->autoneg == AUTONEG_ENABLE) cmd->advertising |= ADVERTISED_Autoneg; if (!netif_running(dev)){ ethtool_cmd_speed_set(cmd, 0); cmd->duplex = 0xff; } cmd->maxtxpkt = 0; cmd->maxrxpkt = 0; return 0; } static int b44_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct b44 *bp = netdev_priv(dev); u32 speed = ethtool_cmd_speed(cmd); /* We do not support gigabit. */ if (cmd->autoneg == AUTONEG_ENABLE) { if (cmd->advertising & (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) return -EINVAL; } else if ((speed != SPEED_100 && speed != SPEED_10) || (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL)) { return -EINVAL; } spin_lock_irq(&bp->lock); if (cmd->autoneg == AUTONEG_ENABLE) { bp->flags &= ~(B44_FLAG_FORCE_LINK | B44_FLAG_100_BASE_T | B44_FLAG_FULL_DUPLEX | B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL | B44_FLAG_ADV_100HALF | B44_FLAG_ADV_100FULL); if (cmd->advertising == 0) { bp->flags |= (B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL | B44_FLAG_ADV_100HALF | B44_FLAG_ADV_100FULL); } else { if (cmd->advertising & ADVERTISED_10baseT_Half) bp->flags |= B44_FLAG_ADV_10HALF; if (cmd->advertising & ADVERTISED_10baseT_Full) bp->flags |= B44_FLAG_ADV_10FULL; if (cmd->advertising & ADVERTISED_100baseT_Half) bp->flags |= B44_FLAG_ADV_100HALF; if (cmd->advertising & ADVERTISED_100baseT_Full) bp->flags |= B44_FLAG_ADV_100FULL; } } else { bp->flags |= B44_FLAG_FORCE_LINK; bp->flags &= ~(B44_FLAG_100_BASE_T | B44_FLAG_FULL_DUPLEX); if (speed == SPEED_100) bp->flags |= B44_FLAG_100_BASE_T; if (cmd->duplex == DUPLEX_FULL) bp->flags |= B44_FLAG_FULL_DUPLEX; } if (netif_running(dev)) b44_setup_phy(bp); spin_unlock_irq(&bp->lock); return 0; } static void b44_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { struct b44 *bp = netdev_priv(dev); ering->rx_max_pending = B44_RX_RING_SIZE - 1; ering->rx_pending = bp->rx_pending; /* XXX ethtool lacks a tx_max_pending, oops... */ } static int b44_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { struct b44 *bp = netdev_priv(dev); if ((ering->rx_pending > B44_RX_RING_SIZE - 1) || (ering->rx_mini_pending != 0) || (ering->rx_jumbo_pending != 0) || (ering->tx_pending > B44_TX_RING_SIZE - 1)) return -EINVAL; spin_lock_irq(&bp->lock); bp->rx_pending = ering->rx_pending; bp->tx_pending = ering->tx_pending; b44_halt(bp); b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET); netif_wake_queue(bp->dev); spin_unlock_irq(&bp->lock); b44_enable_ints(bp); return 0; } static void b44_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) { struct b44 *bp = netdev_priv(dev); epause->autoneg = (bp->flags & B44_FLAG_PAUSE_AUTO) != 0; epause->rx_pause = (bp->flags & B44_FLAG_RX_PAUSE) != 0; epause->tx_pause = (bp->flags & B44_FLAG_TX_PAUSE) != 0; } static int b44_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) { struct b44 *bp = netdev_priv(dev); spin_lock_irq(&bp->lock); if (epause->autoneg) bp->flags |= B44_FLAG_PAUSE_AUTO; else bp->flags &= ~B44_FLAG_PAUSE_AUTO; if (epause->rx_pause) bp->flags |= B44_FLAG_RX_PAUSE; else bp->flags &= ~B44_FLAG_RX_PAUSE; if (epause->tx_pause) bp->flags |= B44_FLAG_TX_PAUSE; else bp->flags &= ~B44_FLAG_TX_PAUSE; if (bp->flags & B44_FLAG_PAUSE_AUTO) { b44_halt(bp); b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET); } else { __b44_set_flow_ctrl(bp, bp->flags); } spin_unlock_irq(&bp->lock); b44_enable_ints(bp); return 0; } static void b44_get_strings(struct net_device *dev, u32 stringset, u8 *data) { switch(stringset) { case ETH_SS_STATS: memcpy(data, *b44_gstrings, sizeof(b44_gstrings)); break; } } static int b44_get_sset_count(struct net_device *dev, int sset) { switch (sset) { case ETH_SS_STATS: return ARRAY_SIZE(b44_gstrings); default: return -EOPNOTSUPP; } } static void b44_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct b44 *bp = netdev_priv(dev); struct b44_hw_stats *hwstat = &bp->hw_stats; u64 *data_src, *data_dst; unsigned int start; u32 i; spin_lock_irq(&bp->lock); b44_stats_update(bp); spin_unlock_irq(&bp->lock); do { data_src = &hwstat->tx_good_octets; data_dst = data; start = u64_stats_fetch_begin_bh(&hwstat->syncp); for (i = 0; i < ARRAY_SIZE(b44_gstrings); i++) *data_dst++ = *data_src++; } while (u64_stats_fetch_retry_bh(&hwstat->syncp, start)); } static void b44_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct b44 *bp = netdev_priv(dev); wol->supported = WAKE_MAGIC; if (bp->flags & B44_FLAG_WOL_ENABLE) wol->wolopts = WAKE_MAGIC; else wol->wolopts = 0; memset(&wol->sopass, 0, sizeof(wol->sopass)); } static int b44_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct b44 *bp = netdev_priv(dev); spin_lock_irq(&bp->lock); if (wol->wolopts & WAKE_MAGIC) bp->flags |= B44_FLAG_WOL_ENABLE; else bp->flags &= ~B44_FLAG_WOL_ENABLE; spin_unlock_irq(&bp->lock); return 0; } static const struct ethtool_ops b44_ethtool_ops = { .get_drvinfo = b44_get_drvinfo, .get_settings = b44_get_settings, .set_settings = b44_set_settings, .nway_reset = b44_nway_reset, .get_link = ethtool_op_get_link, .get_wol = b44_get_wol, .set_wol = b44_set_wol, .get_ringparam = b44_get_ringparam, .set_ringparam = b44_set_ringparam, .get_pauseparam = b44_get_pauseparam, .set_pauseparam = b44_set_pauseparam, .get_msglevel = b44_get_msglevel, .set_msglevel = b44_set_msglevel, .get_strings = b44_get_strings, .get_sset_count = b44_get_sset_count, .get_ethtool_stats = b44_get_ethtool_stats, }; static int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mii_ioctl_data *data = if_mii(ifr); struct b44 *bp = netdev_priv(dev); int err = -EINVAL; if (!netif_running(dev)) goto out; spin_lock_irq(&bp->lock); err = generic_mii_ioctl(&bp->mii_if, data, cmd, NULL); spin_unlock_irq(&bp->lock); out: return err; } static int __devinit b44_get_invariants(struct b44 *bp) { struct ssb_device *sdev = bp->sdev; int err = 0; u8 *addr; bp->dma_offset = ssb_dma_translation(sdev); if (sdev->bus->bustype == SSB_BUSTYPE_SSB && instance > 1) { addr = sdev->bus->sprom.et1mac; bp->phy_addr = sdev->bus->sprom.et1phyaddr; } else { addr = sdev->bus->sprom.et0mac; bp->phy_addr = sdev->bus->sprom.et0phyaddr; } /* Some ROMs have buggy PHY addresses with the high * bits set (sign extension?). Truncate them to a * valid PHY address. */ bp->phy_addr &= 0x1F; memcpy(bp->dev->dev_addr, addr, 6); if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){ pr_err("Invalid MAC address found in EEPROM\n"); return -EINVAL; } memcpy(bp->dev->perm_addr, bp->dev->dev_addr, bp->dev->addr_len); bp->imask = IMASK_DEF; /* XXX - really required? bp->flags |= B44_FLAG_BUGGY_TXPTR; */ if (bp->sdev->id.revision >= 7) bp->flags |= B44_FLAG_B0_ANDLATER; return err; } static const struct net_device_ops b44_netdev_ops = { .ndo_open = b44_open, .ndo_stop = b44_close, .ndo_start_xmit = b44_start_xmit, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) .ndo_get_stats64 = b44_get_stats64, #else .ndo_get_stats = b44_get_stats, #endif .ndo_set_rx_mode = b44_set_rx_mode, .ndo_set_mac_address = b44_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = b44_ioctl, .ndo_tx_timeout = b44_tx_timeout, .ndo_change_mtu = b44_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = b44_poll_controller, #endif }; static int __devinit b44_init_one(struct ssb_device *sdev, const struct ssb_device_id *ent) { struct net_device *dev; struct b44 *bp; int err; instance++; pr_info_once("%s version %s\n", DRV_DESCRIPTION, DRV_MODULE_VERSION); dev = alloc_etherdev(sizeof(*bp)); if (!dev) { err = -ENOMEM; goto out; } SET_NETDEV_DEV(dev, sdev->dev); /* No interesting netdevice features in this card... */ dev->features |= 0; bp = netdev_priv(dev); bp->sdev = sdev; bp->dev = dev; bp->force_copybreak = 0; bp->msg_enable = netif_msg_init(b44_debug, B44_DEF_MSG_ENABLE); spin_lock_init(&bp->lock); bp->rx_pending = B44_DEF_RX_RING_PENDING; bp->tx_pending = B44_DEF_TX_RING_PENDING; netdev_attach_ops(dev, &b44_netdev_ops); netif_napi_add(dev, &bp->napi, b44_poll, 64); dev->watchdog_timeo = B44_TX_TIMEOUT; dev->irq = sdev->irq; SET_ETHTOOL_OPS(dev, &b44_ethtool_ops); err = ssb_bus_powerup(sdev->bus, 0); if (err) { dev_err(sdev->dev, "Failed to powerup the bus\n"); goto err_out_free_dev; } if (dma_set_mask(sdev->dma_dev, DMA_BIT_MASK(30)) || dma_set_coherent_mask(sdev->dma_dev, DMA_BIT_MASK(30))) { dev_err(sdev->dev, "Required 30BIT DMA mask unsupported by the system\n"); goto err_out_powerdown; } err = b44_get_invariants(bp); if (err) { dev_err(sdev->dev, "Problem fetching invariants of chip, aborting\n"); goto err_out_powerdown; } bp->mii_if.dev = dev; bp->mii_if.mdio_read = b44_mii_read; bp->mii_if.mdio_write = b44_mii_write; bp->mii_if.phy_id = bp->phy_addr; bp->mii_if.phy_id_mask = 0x1f; bp->mii_if.reg_num_mask = 0x1f; /* By default, advertise all speed/duplex settings. */ bp->flags |= (B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL | B44_FLAG_ADV_100HALF | B44_FLAG_ADV_100FULL); /* By default, auto-negotiate PAUSE. */ bp->flags |= B44_FLAG_PAUSE_AUTO; err = register_netdev(dev); if (err) { dev_err(sdev->dev, "Cannot register net device, aborting\n"); goto err_out_powerdown; } netif_carrier_off(dev); ssb_set_drvdata(sdev, dev); /* Chip reset provides power to the b44 MAC & PCI cores, which * is necessary for MAC register access. */ b44_chip_reset(bp, B44_CHIP_RESET_FULL); /* do a phy reset to test if there is an active phy */ if (b44_phy_reset(bp) < 0) bp->phy_addr = B44_PHY_ADDR_NO_PHY; netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr); return 0; err_out_powerdown: ssb_bus_may_powerdown(sdev->bus); err_out_free_dev: free_netdev(dev); out: return err; } static void __devexit b44_remove_one(struct ssb_device *sdev) { struct net_device *dev = ssb_get_drvdata(sdev); unregister_netdev(dev); ssb_device_disable(sdev, 0); ssb_bus_may_powerdown(sdev->bus); free_netdev(dev); ssb_pcihost_set_power_state(sdev, PCI_D3hot); ssb_set_drvdata(sdev, NULL); } static int b44_suspend(struct ssb_device *sdev, pm_message_t state) { struct net_device *dev = ssb_get_drvdata(sdev); struct b44 *bp = netdev_priv(dev); if (!netif_running(dev)) return 0; del_timer_sync(&bp->timer); spin_lock_irq(&bp->lock); b44_halt(bp); netif_carrier_off(bp->dev); netif_device_detach(bp->dev); b44_free_rings(bp); spin_unlock_irq(&bp->lock); free_irq(dev->irq, dev); if (bp->flags & B44_FLAG_WOL_ENABLE) { b44_init_hw(bp, B44_PARTIAL_RESET); b44_setup_wol(bp); } ssb_pcihost_set_power_state(sdev, PCI_D3hot); return 0; } static int b44_resume(struct ssb_device *sdev) { struct net_device *dev = ssb_get_drvdata(sdev); struct b44 *bp = netdev_priv(dev); int rc = 0; rc = ssb_bus_powerup(sdev->bus, 0); if (rc) { dev_err(sdev->dev, "Failed to powerup the bus\n"); return rc; } if (!netif_running(dev)) return 0; spin_lock_irq(&bp->lock); b44_init_rings(bp); b44_init_hw(bp, B44_FULL_RESET); spin_unlock_irq(&bp->lock); /* * As a shared interrupt, the handler can be called immediately. To be * able to check the interrupt status the hardware must already be * powered back on (b44_init_hw). */ rc = request_irq(dev->irq, b44_interrupt, IRQF_SHARED, dev->name, dev); if (rc) { netdev_err(dev, "request_irq failed\n"); spin_lock_irq(&bp->lock); b44_halt(bp); b44_free_rings(bp); spin_unlock_irq(&bp->lock); return rc; } netif_device_attach(bp->dev); b44_enable_ints(bp); netif_wake_queue(dev); mod_timer(&bp->timer, jiffies + 1); return 0; } static struct ssb_driver b44_ssb_driver = { .name = DRV_MODULE_NAME, .id_table = b44_ssb_tbl, .probe = b44_init_one, .remove = __devexit_p(b44_remove_one), .suspend = b44_suspend, .resume = b44_resume, }; static inline int __init b44_pci_init(void) { int err = 0; #ifdef CONFIG_B44_PCI err = ssb_pcihost_register(&b44_pci_driver); #endif return err; } static inline void b44_pci_exit(void) { #ifdef CONFIG_B44_PCI ssb_pcihost_unregister(&b44_pci_driver); #endif } static int __init b44_init(void) { unsigned int dma_desc_align_size = dma_get_cache_alignment(); int err; /* Setup paramaters for syncing RX/TX DMA descriptors */ dma_desc_sync_size = max_t(unsigned int, dma_desc_align_size, sizeof(struct dma_desc)); err = b44_pci_init(); if (err) return err; err = ssb_driver_register(&b44_ssb_driver); if (err) b44_pci_exit(); return err; } static void __exit b44_cleanup(void) { ssb_driver_unregister(&b44_ssb_driver); b44_pci_exit(); } module_init(b44_init); module_exit(b44_cleanup); compat-drivers-2012-09-18/drivers/net/ethernet/broadcom/Makefile0000644000175000017500000000003312026211315023664 0ustar mcgrofmcgrofobj-$(CONFIG_B44) += b44.o compat-drivers-2012-09-18/drivers/net/ethernet/broadcom/b44.h0000644000175000017500000004230712026211315023000 0ustar mcgrofmcgrof#ifndef _B44_H #define _B44_H /* Register layout. (These correspond to struct _bcmenettregs in bcm4400.) */ #define B44_DEVCTRL 0x0000UL /* Device Control */ #define DEVCTRL_MPM 0x00000040 /* Magic Packet PME Enable (B0 only) */ #define DEVCTRL_PFE 0x00000080 /* Pattern Filtering Enable */ #define DEVCTRL_IPP 0x00000400 /* Internal EPHY Present */ #define DEVCTRL_EPR 0x00008000 /* EPHY Reset */ #define DEVCTRL_PME 0x00001000 /* PHY Mode Enable */ #define DEVCTRL_PMCE 0x00002000 /* PHY Mode Clocks Enable */ #define DEVCTRL_PADDR 0x0007c000 /* PHY Address */ #define DEVCTRL_PADDR_SHIFT 18 #define B44_BIST_STAT 0x000CUL /* Built-In Self-Test Status */ #define B44_WKUP_LEN 0x0010UL /* Wakeup Length */ #define WKUP_LEN_P0_MASK 0x0000007f /* Pattern 0 */ #define WKUP_LEN_D0 0x00000080 #define WKUP_LEN_P1_MASK 0x00007f00 /* Pattern 1 */ #define WKUP_LEN_P1_SHIFT 8 #define WKUP_LEN_D1 0x00008000 #define WKUP_LEN_P2_MASK 0x007f0000 /* Pattern 2 */ #define WKUP_LEN_P2_SHIFT 16 #define WKUP_LEN_D2 0x00000000 #define WKUP_LEN_P3_MASK 0x7f000000 /* Pattern 3 */ #define WKUP_LEN_P3_SHIFT 24 #define WKUP_LEN_D3 0x80000000 #define WKUP_LEN_DISABLE 0x80808080 #define WKUP_LEN_ENABLE_TWO 0x80800000 #define WKUP_LEN_ENABLE_THREE 0x80000000 #define B44_ISTAT 0x0020UL /* Interrupt Status */ #define ISTAT_LS 0x00000020 /* Link Change (B0 only) */ #define ISTAT_PME 0x00000040 /* Power Management Event */ #define ISTAT_TO 0x00000080 /* General Purpose Timeout */ #define ISTAT_DSCE 0x00000400 /* Descriptor Error */ #define ISTAT_DATAE 0x00000800 /* Data Error */ #define ISTAT_DPE 0x00001000 /* Descr. Protocol Error */ #define ISTAT_RDU 0x00002000 /* Receive Descr. Underflow */ #define ISTAT_RFO 0x00004000 /* Receive FIFO Overflow */ #define ISTAT_TFU 0x00008000 /* Transmit FIFO Underflow */ #define ISTAT_RX 0x00010000 /* RX Interrupt */ #define ISTAT_TX 0x01000000 /* TX Interrupt */ #define ISTAT_EMAC 0x04000000 /* EMAC Interrupt */ #define ISTAT_MII_WRITE 0x08000000 /* MII Write Interrupt */ #define ISTAT_MII_READ 0x10000000 /* MII Read Interrupt */ #define ISTAT_ERRORS (ISTAT_DSCE|ISTAT_DATAE|ISTAT_DPE|ISTAT_RDU|ISTAT_RFO|ISTAT_TFU) #define B44_IMASK 0x0024UL /* Interrupt Mask */ #define IMASK_DEF (ISTAT_ERRORS | ISTAT_TO | ISTAT_RX | ISTAT_TX) #define B44_GPTIMER 0x0028UL /* General Purpose Timer */ #define B44_ADDR_LO 0x0088UL /* ENET Address Lo (B0 only) */ #define B44_ADDR_HI 0x008CUL /* ENET Address Hi (B0 only) */ #define B44_FILT_ADDR 0x0090UL /* ENET Filter Address */ #define B44_FILT_DATA 0x0094UL /* ENET Filter Data */ #define B44_TXBURST 0x00A0UL /* TX Max Burst Length */ #define B44_RXBURST 0x00A4UL /* RX Max Burst Length */ #define B44_MAC_CTRL 0x00A8UL /* MAC Control */ #define MAC_CTRL_CRC32_ENAB 0x00000001 /* CRC32 Generation Enable */ #define MAC_CTRL_PHY_PDOWN 0x00000004 /* Onchip EPHY Powerdown */ #define MAC_CTRL_PHY_EDET 0x00000008 /* Onchip EPHY Energy Detected */ #define MAC_CTRL_PHY_LEDCTRL 0x000000e0 /* Onchip EPHY LED Control */ #define MAC_CTRL_PHY_LEDCTRL_SHIFT 5 #define B44_MAC_FLOW 0x00ACUL /* MAC Flow Control */ #define MAC_FLOW_RX_HI_WATER 0x000000ff /* Receive FIFO HI Water Mark */ #define MAC_FLOW_PAUSE_ENAB 0x00008000 /* Enable Pause Frame Generation */ #define B44_RCV_LAZY 0x0100UL /* Lazy Interrupt Control */ #define RCV_LAZY_TO_MASK 0x00ffffff /* Timeout */ #define RCV_LAZY_FC_MASK 0xff000000 /* Frame Count */ #define RCV_LAZY_FC_SHIFT 24 #define B44_DMATX_CTRL 0x0200UL /* DMA TX Control */ #define DMATX_CTRL_ENABLE 0x00000001 /* Enable */ #define DMATX_CTRL_SUSPEND 0x00000002 /* Suepend Request */ #define DMATX_CTRL_LPBACK 0x00000004 /* Loopback Enable */ #define DMATX_CTRL_FAIRPRIOR 0x00000008 /* Fair Priority */ #define DMATX_CTRL_FLUSH 0x00000010 /* Flush Request */ #define B44_DMATX_ADDR 0x0204UL /* DMA TX Descriptor Ring Address */ #define B44_DMATX_PTR 0x0208UL /* DMA TX Last Posted Descriptor */ #define B44_DMATX_STAT 0x020CUL /* DMA TX Current Active Desc. + Status */ #define DMATX_STAT_CDMASK 0x00000fff /* Current Descriptor Mask */ #define DMATX_STAT_SMASK 0x0000f000 /* State Mask */ #define DMATX_STAT_SDISABLED 0x00000000 /* State Disabled */ #define DMATX_STAT_SACTIVE 0x00001000 /* State Active */ #define DMATX_STAT_SIDLE 0x00002000 /* State Idle Wait */ #define DMATX_STAT_SSTOPPED 0x00003000 /* State Stopped */ #define DMATX_STAT_SSUSP 0x00004000 /* State Suspend Pending */ #define DMATX_STAT_EMASK 0x000f0000 /* Error Mask */ #define DMATX_STAT_ENONE 0x00000000 /* Error None */ #define DMATX_STAT_EDPE 0x00010000 /* Error Desc. Protocol Error */ #define DMATX_STAT_EDFU 0x00020000 /* Error Data FIFO Underrun */ #define DMATX_STAT_EBEBR 0x00030000 /* Error Bus Error on Buffer Read */ #define DMATX_STAT_EBEDA 0x00040000 /* Error Bus Error on Desc. Access */ #define DMATX_STAT_FLUSHED 0x00100000 /* Flushed */ #define B44_DMARX_CTRL 0x0210UL /* DMA RX Control */ #define DMARX_CTRL_ENABLE 0x00000001 /* Enable */ #define DMARX_CTRL_ROMASK 0x000000fe /* Receive Offset Mask */ #define DMARX_CTRL_ROSHIFT 1 /* Receive Offset Shift */ #define B44_DMARX_ADDR 0x0214UL /* DMA RX Descriptor Ring Address */ #define B44_DMARX_PTR 0x0218UL /* DMA RX Last Posted Descriptor */ #define B44_DMARX_STAT 0x021CUL /* DMA RX Current Active Desc. + Status */ #define DMARX_STAT_CDMASK 0x00000fff /* Current Descriptor Mask */ #define DMARX_STAT_SMASK 0x0000f000 /* State Mask */ #define DMARX_STAT_SDISABLED 0x00000000 /* State Disabled */ #define DMARX_STAT_SACTIVE 0x00001000 /* State Active */ #define DMARX_STAT_SIDLE 0x00002000 /* State Idle Wait */ #define DMARX_STAT_SSTOPPED 0x00003000 /* State Stopped */ #define DMARX_STAT_EMASK 0x000f0000 /* Error Mask */ #define DMARX_STAT_ENONE 0x00000000 /* Error None */ #define DMARX_STAT_EDPE 0x00010000 /* Error Desc. Protocol Error */ #define DMARX_STAT_EDFO 0x00020000 /* Error Data FIFO Overflow */ #define DMARX_STAT_EBEBW 0x00030000 /* Error Bus Error on Buffer Write */ #define DMARX_STAT_EBEDA 0x00040000 /* Error Bus Error on Desc. Access */ #define B44_DMAFIFO_AD 0x0220UL /* DMA FIFO Diag Address */ #define DMAFIFO_AD_OMASK 0x0000ffff /* Offset Mask */ #define DMAFIFO_AD_SMASK 0x000f0000 /* Select Mask */ #define DMAFIFO_AD_SXDD 0x00000000 /* Select Transmit DMA Data */ #define DMAFIFO_AD_SXDP 0x00010000 /* Select Transmit DMA Pointers */ #define DMAFIFO_AD_SRDD 0x00040000 /* Select Receive DMA Data */ #define DMAFIFO_AD_SRDP 0x00050000 /* Select Receive DMA Pointers */ #define DMAFIFO_AD_SXFD 0x00080000 /* Select Transmit FIFO Data */ #define DMAFIFO_AD_SXFP 0x00090000 /* Select Transmit FIFO Pointers */ #define DMAFIFO_AD_SRFD 0x000c0000 /* Select Receive FIFO Data */ #define DMAFIFO_AD_SRFP 0x000c0000 /* Select Receive FIFO Pointers */ #define B44_DMAFIFO_LO 0x0224UL /* DMA FIFO Diag Low Data */ #define B44_DMAFIFO_HI 0x0228UL /* DMA FIFO Diag High Data */ #define B44_RXCONFIG 0x0400UL /* EMAC RX Config */ #define RXCONFIG_DBCAST 0x00000001 /* Disable Broadcast */ #define RXCONFIG_ALLMULTI 0x00000002 /* Accept All Multicast */ #define RXCONFIG_NORX_WHILE_TX 0x00000004 /* Receive Disable While Transmitting */ #define RXCONFIG_PROMISC 0x00000008 /* Promiscuous Enable */ #define RXCONFIG_LPBACK 0x00000010 /* Loopback Enable */ #define RXCONFIG_FLOW 0x00000020 /* Flow Control Enable */ #define RXCONFIG_FLOW_ACCEPT 0x00000040 /* Accept Unicast Flow Control Frame */ #define RXCONFIG_RFILT 0x00000080 /* Reject Filter */ #define RXCONFIG_CAM_ABSENT 0x00000100 /* CAM Absent */ #define B44_RXMAXLEN 0x0404UL /* EMAC RX Max Packet Length */ #define B44_TXMAXLEN 0x0408UL /* EMAC TX Max Packet Length */ #define B44_MDIO_CTRL 0x0410UL /* EMAC MDIO Control */ #define MDIO_CTRL_MAXF_MASK 0x0000007f /* MDC Frequency */ #define MDIO_CTRL_PREAMBLE 0x00000080 /* MII Preamble Enable */ #define B44_MDIO_DATA 0x0414UL /* EMAC MDIO Data */ #define MDIO_DATA_DATA 0x0000ffff /* R/W Data */ #define MDIO_DATA_TA_MASK 0x00030000 /* Turnaround Value */ #define MDIO_DATA_TA_SHIFT 16 #define MDIO_TA_VALID 2 #define MDIO_DATA_RA_MASK 0x007c0000 /* Register Address */ #define MDIO_DATA_RA_SHIFT 18 #define MDIO_DATA_PMD_MASK 0x0f800000 /* Physical Media Device */ #define MDIO_DATA_PMD_SHIFT 23 #define MDIO_DATA_OP_MASK 0x30000000 /* Opcode */ #define MDIO_DATA_OP_SHIFT 28 #define MDIO_OP_WRITE 1 #define MDIO_OP_READ 2 #define MDIO_DATA_SB_MASK 0xc0000000 /* Start Bits */ #define MDIO_DATA_SB_SHIFT 30 #define MDIO_DATA_SB_START 0x40000000 /* Start Of Frame */ #define B44_EMAC_IMASK 0x0418UL /* EMAC Interrupt Mask */ #define B44_EMAC_ISTAT 0x041CUL /* EMAC Interrupt Status */ #define EMAC_INT_MII 0x00000001 /* MII MDIO Interrupt */ #define EMAC_INT_MIB 0x00000002 /* MIB Interrupt */ #define EMAC_INT_FLOW 0x00000003 /* Flow Control Interrupt */ #define B44_CAM_DATA_LO 0x0420UL /* EMAC CAM Data Low */ #define B44_CAM_DATA_HI 0x0424UL /* EMAC CAM Data High */ #define CAM_DATA_HI_VALID 0x00010000 /* Valid Bit */ #define B44_CAM_CTRL 0x0428UL /* EMAC CAM Control */ #define CAM_CTRL_ENABLE 0x00000001 /* CAM Enable */ #define CAM_CTRL_MSEL 0x00000002 /* Mask Select */ #define CAM_CTRL_READ 0x00000004 /* Read */ #define CAM_CTRL_WRITE 0x00000008 /* Read */ #define CAM_CTRL_INDEX_MASK 0x003f0000 /* Index Mask */ #define CAM_CTRL_INDEX_SHIFT 16 #define CAM_CTRL_BUSY 0x80000000 /* CAM Busy */ #define B44_ENET_CTRL 0x042CUL /* EMAC ENET Control */ #define ENET_CTRL_ENABLE 0x00000001 /* EMAC Enable */ #define ENET_CTRL_DISABLE 0x00000002 /* EMAC Disable */ #define ENET_CTRL_SRST 0x00000004 /* EMAC Soft Reset */ #define ENET_CTRL_EPSEL 0x00000008 /* External PHY Select */ #define B44_TX_CTRL 0x0430UL /* EMAC TX Control */ #define TX_CTRL_DUPLEX 0x00000001 /* Full Duplex */ #define TX_CTRL_FMODE 0x00000002 /* Flow Mode */ #define TX_CTRL_SBENAB 0x00000004 /* Single Backoff Enable */ #define TX_CTRL_SMALL_SLOT 0x00000008 /* Small Slottime */ #define B44_TX_WMARK 0x0434UL /* EMAC TX Watermark */ #define B44_MIB_CTRL 0x0438UL /* EMAC MIB Control */ #define MIB_CTRL_CLR_ON_READ 0x00000001 /* Autoclear on Read */ #define B44_TX_GOOD_O 0x0500UL /* MIB TX Good Octets */ #define B44_TX_GOOD_P 0x0504UL /* MIB TX Good Packets */ #define B44_TX_O 0x0508UL /* MIB TX Octets */ #define B44_TX_P 0x050CUL /* MIB TX Packets */ #define B44_TX_BCAST 0x0510UL /* MIB TX Broadcast Packets */ #define B44_TX_MCAST 0x0514UL /* MIB TX Multicast Packets */ #define B44_TX_64 0x0518UL /* MIB TX <= 64 byte Packets */ #define B44_TX_65_127 0x051CUL /* MIB TX 65 to 127 byte Packets */ #define B44_TX_128_255 0x0520UL /* MIB TX 128 to 255 byte Packets */ #define B44_TX_256_511 0x0524UL /* MIB TX 256 to 511 byte Packets */ #define B44_TX_512_1023 0x0528UL /* MIB TX 512 to 1023 byte Packets */ #define B44_TX_1024_MAX 0x052CUL /* MIB TX 1024 to max byte Packets */ #define B44_TX_JABBER 0x0530UL /* MIB TX Jabber Packets */ #define B44_TX_OSIZE 0x0534UL /* MIB TX Oversize Packets */ #define B44_TX_FRAG 0x0538UL /* MIB TX Fragment Packets */ #define B44_TX_URUNS 0x053CUL /* MIB TX Underruns */ #define B44_TX_TCOLS 0x0540UL /* MIB TX Total Collisions */ #define B44_TX_SCOLS 0x0544UL /* MIB TX Single Collisions */ #define B44_TX_MCOLS 0x0548UL /* MIB TX Multiple Collisions */ #define B44_TX_ECOLS 0x054CUL /* MIB TX Excessive Collisions */ #define B44_TX_LCOLS 0x0550UL /* MIB TX Late Collisions */ #define B44_TX_DEFERED 0x0554UL /* MIB TX Defered Packets */ #define B44_TX_CLOST 0x0558UL /* MIB TX Carrier Lost */ #define B44_TX_PAUSE 0x055CUL /* MIB TX Pause Packets */ #define B44_RX_GOOD_O 0x0580UL /* MIB RX Good Octets */ #define B44_RX_GOOD_P 0x0584UL /* MIB RX Good Packets */ #define B44_RX_O 0x0588UL /* MIB RX Octets */ #define B44_RX_P 0x058CUL /* MIB RX Packets */ #define B44_RX_BCAST 0x0590UL /* MIB RX Broadcast Packets */ #define B44_RX_MCAST 0x0594UL /* MIB RX Multicast Packets */ #define B44_RX_64 0x0598UL /* MIB RX <= 64 byte Packets */ #define B44_RX_65_127 0x059CUL /* MIB RX 65 to 127 byte Packets */ #define B44_RX_128_255 0x05A0UL /* MIB RX 128 to 255 byte Packets */ #define B44_RX_256_511 0x05A4UL /* MIB RX 256 to 511 byte Packets */ #define B44_RX_512_1023 0x05A8UL /* MIB RX 512 to 1023 byte Packets */ #define B44_RX_1024_MAX 0x05ACUL /* MIB RX 1024 to max byte Packets */ #define B44_RX_JABBER 0x05B0UL /* MIB RX Jabber Packets */ #define B44_RX_OSIZE 0x05B4UL /* MIB RX Oversize Packets */ #define B44_RX_FRAG 0x05B8UL /* MIB RX Fragment Packets */ #define B44_RX_MISS 0x05BCUL /* MIB RX Missed Packets */ #define B44_RX_CRCA 0x05C0UL /* MIB RX CRC Align Errors */ #define B44_RX_USIZE 0x05C4UL /* MIB RX Undersize Packets */ #define B44_RX_CRC 0x05C8UL /* MIB RX CRC Errors */ #define B44_RX_ALIGN 0x05CCUL /* MIB RX Align Errors */ #define B44_RX_SYM 0x05D0UL /* MIB RX Symbol Errors */ #define B44_RX_PAUSE 0x05D4UL /* MIB RX Pause Packets */ #define B44_RX_NPAUSE 0x05D8UL /* MIB RX Non-Pause Packets */ /* 4400 PHY registers */ #define B44_MII_AUXCTRL 24 /* Auxiliary Control */ #define MII_AUXCTRL_DUPLEX 0x0001 /* Full Duplex */ #define MII_AUXCTRL_SPEED 0x0002 /* 1=100Mbps, 0=10Mbps */ #define MII_AUXCTRL_FORCED 0x0004 /* Forced 10/100 */ #define B44_MII_ALEDCTRL 26 /* Activity LED */ #define MII_ALEDCTRL_ALLMSK 0x7fff #define B44_MII_TLEDCTRL 27 /* Traffic Meter LED */ #define MII_TLEDCTRL_ENABLE 0x0040 struct dma_desc { __le32 ctrl; __le32 addr; }; /* There are only 12 bits in the DMA engine for descriptor offsetting * so the table must be aligned on a boundary of this. */ #define DMA_TABLE_BYTES 4096 #define DESC_CTRL_LEN 0x00001fff #define DESC_CTRL_CMASK 0x0ff00000 /* Core specific bits */ #define DESC_CTRL_EOT 0x10000000 /* End of Table */ #define DESC_CTRL_IOC 0x20000000 /* Interrupt On Completion */ #define DESC_CTRL_EOF 0x40000000 /* End of Frame */ #define DESC_CTRL_SOF 0x80000000 /* Start of Frame */ #define RX_COPY_THRESHOLD 256 struct rx_header { __le16 len; __le16 flags; __le16 pad[12]; }; #define RX_HEADER_LEN 28 #define RX_FLAG_OFIFO 0x00000001 /* FIFO Overflow */ #define RX_FLAG_CRCERR 0x00000002 /* CRC Error */ #define RX_FLAG_SERR 0x00000004 /* Receive Symbol Error */ #define RX_FLAG_ODD 0x00000008 /* Frame has odd number of nibbles */ #define RX_FLAG_LARGE 0x00000010 /* Frame is > RX MAX Length */ #define RX_FLAG_MCAST 0x00000020 /* Dest is Multicast Address */ #define RX_FLAG_BCAST 0x00000040 /* Dest is Broadcast Address */ #define RX_FLAG_MISS 0x00000080 /* Received due to promisc mode */ #define RX_FLAG_LAST 0x00000800 /* Last buffer in frame */ #define RX_FLAG_ERRORS (RX_FLAG_ODD | RX_FLAG_SERR | RX_FLAG_CRCERR | RX_FLAG_OFIFO) struct ring_info { struct sk_buff *skb; dma_addr_t mapping; }; #define B44_MCAST_TABLE_SIZE 32 #define B44_PHY_ADDR_NO_PHY 30 #define B44_MDC_RATIO 5000000 #define B44_STAT_REG_DECLARE \ _B44(tx_good_octets) \ _B44(tx_good_pkts) \ _B44(tx_octets) \ _B44(tx_pkts) \ _B44(tx_broadcast_pkts) \ _B44(tx_multicast_pkts) \ _B44(tx_len_64) \ _B44(tx_len_65_to_127) \ _B44(tx_len_128_to_255) \ _B44(tx_len_256_to_511) \ _B44(tx_len_512_to_1023) \ _B44(tx_len_1024_to_max) \ _B44(tx_jabber_pkts) \ _B44(tx_oversize_pkts) \ _B44(tx_fragment_pkts) \ _B44(tx_underruns) \ _B44(tx_total_cols) \ _B44(tx_single_cols) \ _B44(tx_multiple_cols) \ _B44(tx_excessive_cols) \ _B44(tx_late_cols) \ _B44(tx_defered) \ _B44(tx_carrier_lost) \ _B44(tx_pause_pkts) \ _B44(rx_good_octets) \ _B44(rx_good_pkts) \ _B44(rx_octets) \ _B44(rx_pkts) \ _B44(rx_broadcast_pkts) \ _B44(rx_multicast_pkts) \ _B44(rx_len_64) \ _B44(rx_len_65_to_127) \ _B44(rx_len_128_to_255) \ _B44(rx_len_256_to_511) \ _B44(rx_len_512_to_1023) \ _B44(rx_len_1024_to_max) \ _B44(rx_jabber_pkts) \ _B44(rx_oversize_pkts) \ _B44(rx_fragment_pkts) \ _B44(rx_missed_pkts) \ _B44(rx_crc_align_errs) \ _B44(rx_undersize) \ _B44(rx_crc_errs) \ _B44(rx_align_errs) \ _B44(rx_symbol_errs) \ _B44(rx_pause_pkts) \ _B44(rx_nonpause_pkts) /* SW copy of device statistics, kept up to date by periodic timer * which probes HW values. Check b44_stats_update if you mess with * the layout */ struct b44_hw_stats { #define _B44(x) u64 x; B44_STAT_REG_DECLARE #undef _B44 struct u64_stats_sync syncp; }; struct ssb_device; struct b44 { spinlock_t lock; u32 imask, istat; struct dma_desc *rx_ring, *tx_ring; u32 tx_prod, tx_cons; u32 rx_prod, rx_cons; struct ring_info *rx_buffers; struct ring_info *tx_buffers; struct napi_struct napi; u32 dma_offset; u32 flags; #define B44_FLAG_B0_ANDLATER 0x00000001 #define B44_FLAG_BUGGY_TXPTR 0x00000002 #define B44_FLAG_REORDER_BUG 0x00000004 #define B44_FLAG_PAUSE_AUTO 0x00008000 #define B44_FLAG_FULL_DUPLEX 0x00010000 #define B44_FLAG_100_BASE_T 0x00020000 #define B44_FLAG_TX_PAUSE 0x00040000 #define B44_FLAG_RX_PAUSE 0x00080000 #define B44_FLAG_FORCE_LINK 0x00100000 #define B44_FLAG_ADV_10HALF 0x01000000 #define B44_FLAG_ADV_10FULL 0x02000000 #define B44_FLAG_ADV_100HALF 0x04000000 #define B44_FLAG_ADV_100FULL 0x08000000 #define B44_FLAG_INTERNAL_PHY 0x10000000 #define B44_FLAG_RX_RING_HACK 0x20000000 #define B44_FLAG_TX_RING_HACK 0x40000000 #define B44_FLAG_WOL_ENABLE 0x80000000 u32 msg_enable; struct timer_list timer; struct b44_hw_stats hw_stats; struct ssb_device *sdev; struct net_device *dev; dma_addr_t rx_ring_dma, tx_ring_dma; u32 rx_pending; u32 tx_pending; u8 phy_addr; u8 force_copybreak; struct mii_if_info mii_if; }; #endif /* _B44_H */ compat-drivers-2012-09-18/drivers/net/usb/0000755000175000017500000000000012026211315017415 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/net/usb/Makefile0000644000175000017500000000030212026211315021050 0ustar mcgrofmcgrof# # Makefile for USB Network drivers # obj-$(CONFIG_USB_NET_COMPAT_CDCETHER) += cdc_ether.o obj-$(CONFIG_USB_NET_COMPAT_RNDIS_HOST) += rndis_host.o obj-$(CONFIG_USB_COMPAT_USBNET) += usbnet.o compat-drivers-2012-09-18/drivers/net/usb/rndis_host.c0000644000175000017500000004755712026211315021757 0ustar mcgrofmcgrof/* * Host Side support for RNDIS Networking Links * Copyright (C) 2005 by David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include /* * RNDIS is NDIS remoted over USB. It's a MSFT variant of CDC ACM ... of * course ACM was intended for modems, not Ethernet links! USB's standard * for Ethernet links is "CDC Ethernet", which is significantly simpler. * * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete. Issues * include: * - Power management in particular relies on information that's scattered * through other documentation, and which is incomplete or incorrect even * there. * - There are various undocumented protocol requirements, such as the * need to send unused garbage in control-OUT messages. * - In some cases, MS-Windows will emit undocumented requests; this * matters more to peripheral implementations than host ones. * * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync". * * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and * currently rare) "Ethernet Emulation Model" (EEM). */ /* * RNDIS notifications from device: command completion; "reverse" * keepalives; etc */ void rndis_status(struct usbnet *dev, struct urb *urb) { netdev_dbg(dev->net, "rndis status urb, len %d stat %d\n", urb->actual_length, urb->status); // FIXME for keepalives, respond immediately (asynchronously) // if not an RNDIS status, do like cdc_status(dev,urb) does } EXPORT_SYMBOL_GPL(rndis_status); /* * RNDIS indicate messages. */ static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg, int buflen) { struct cdc_state *info = (void *)&dev->data; struct device *udev = &info->control->dev; if (dev->driver_info->indication) { dev->driver_info->indication(dev, msg, buflen); } else { u32 status = le32_to_cpu(msg->status); switch (status) { case RNDIS_STATUS_MEDIA_CONNECT: dev_info(udev, "rndis media connect\n"); break; case RNDIS_STATUS_MEDIA_DISCONNECT: dev_info(udev, "rndis media disconnect\n"); break; default: dev_info(udev, "rndis indication: 0x%08x\n", status); } } } /* * RPC done RNDIS-style. Caller guarantees: * - message is properly byteswapped * - there's no other request pending * - buf can hold up to 1KB response (required by RNDIS spec) * On return, the first few entries are already byteswapped. * * Call context is likely probe(), before interface name is known, * which is why we won't try to use it in the diagnostics. */ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) { struct cdc_state *info = (void *) &dev->data; struct usb_cdc_notification notification; int master_ifnum; int retval; int partial; unsigned count; u32 xid = 0, msg_len, request_id, msg_type, rsp, status; /* REVISIT when this gets called from contexts other than probe() or * disconnect(): either serialize, or dispatch responses on xid */ msg_type = le32_to_cpu(buf->msg_type); /* Issue the request; xid is unique, don't bother byteswapping it */ if (likely(msg_type != RNDIS_MSG_HALT && msg_type != RNDIS_MSG_RESET)) { xid = dev->xid++; if (!xid) xid = dev->xid++; buf->request_id = (__force __le32) xid; } master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber; retval = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, master_ifnum, buf, le32_to_cpu(buf->msg_len), RNDIS_CONTROL_TIMEOUT_MS); if (unlikely(retval < 0 || xid == 0)) return retval; /* Some devices don't respond on the control channel until * polled on the status channel, so do that first. */ if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) { retval = usb_interrupt_msg( dev->udev, usb_rcvintpipe(dev->udev, dev->status->desc.bEndpointAddress), ¬ification, sizeof(notification), &partial, RNDIS_CONTROL_TIMEOUT_MS); if (unlikely(retval < 0)) return retval; } /* Poll the control channel; the request probably completed immediately */ rsp = le32_to_cpu(buf->msg_type) | RNDIS_MSG_COMPLETION; for (count = 0; count < 10; count++) { memset(buf, 0, CONTROL_BUFFER_SIZE); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), USB_CDC_GET_ENCAPSULATED_RESPONSE, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, master_ifnum, buf, buflen, RNDIS_CONTROL_TIMEOUT_MS); if (likely(retval >= 8)) { msg_type = le32_to_cpu(buf->msg_type); msg_len = le32_to_cpu(buf->msg_len); status = le32_to_cpu(buf->status); request_id = (__force u32) buf->request_id; if (likely(msg_type == rsp)) { if (likely(request_id == xid)) { if (unlikely(rsp == RNDIS_MSG_RESET_C)) return 0; if (likely(RNDIS_STATUS_SUCCESS == status)) return 0; dev_dbg(&info->control->dev, "rndis reply status %08x\n", status); return -EL3RST; } dev_dbg(&info->control->dev, "rndis reply id %d expected %d\n", request_id, xid); /* then likely retry */ } else switch (msg_type) { case RNDIS_MSG_INDICATE: /* fault/event */ rndis_msg_indicate(dev, (void *)buf, buflen); break; case RNDIS_MSG_KEEPALIVE: { /* ping */ struct rndis_keepalive_c *msg = (void *)buf; msg->msg_type = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C); msg->msg_len = cpu_to_le32(sizeof *msg); msg->status = cpu_to_le32(RNDIS_STATUS_SUCCESS); retval = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, master_ifnum, msg, sizeof *msg, RNDIS_CONTROL_TIMEOUT_MS); if (unlikely(retval < 0)) dev_dbg(&info->control->dev, "rndis keepalive err %d\n", retval); } break; default: dev_dbg(&info->control->dev, "unexpected rndis msg %08x len %d\n", le32_to_cpu(buf->msg_type), msg_len); } } else { /* device probably issued a protocol stall; ignore */ dev_dbg(&info->control->dev, "rndis response error, code %d\n", retval); } msleep(20); } dev_dbg(&info->control->dev, "rndis response timeout\n"); return -ETIMEDOUT; } EXPORT_SYMBOL_GPL(rndis_command); /* * rndis_query: * * Performs a query for @oid along with 0 or more bytes of payload as * specified by @in_len. If @reply_len is not set to -1 then the reply * length is checked against this value, resulting in an error if it * doesn't match. * * NOTE: Adding a payload exactly or greater than the size of the expected * response payload is an evident requirement MSFT added for ActiveSync. * * The only exception is for OIDs that return a variably sized response, * in which case no payload should be added. This undocumented (and * nonsensical!) issue was found by sniffing protocol requests from the * ActiveSync 4.1 Windows driver. */ static int rndis_query(struct usbnet *dev, struct usb_interface *intf, void *buf, u32 oid, u32 in_len, void **reply, int *reply_len) { int retval; union { void *buf; struct rndis_msg_hdr *header; struct rndis_query *get; struct rndis_query_c *get_c; } u; u32 off, len; u.buf = buf; memset(u.get, 0, sizeof *u.get + in_len); u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY); u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len); u.get->oid = cpu_to_le32(oid); u.get->len = cpu_to_le32(in_len); u.get->offset = cpu_to_le32(20); retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", oid, retval); return retval; } off = le32_to_cpu(u.get_c->offset); len = le32_to_cpu(u.get_c->len); if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE)) goto response_error; if (*reply_len != -1 && len != *reply_len) goto response_error; *reply = (unsigned char *) &u.get_c->request_id + off; *reply_len = len; return retval; response_error: dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) " "invalid response - off %d len %d\n", oid, off, len); return -EDOM; } /* same as usbnet_netdev_ops but MTU change not allowed */ static const struct net_device_ops rndis_netdev_ops = { .ndo_open = usbnet_open, .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) { int retval; struct net_device *net = dev->net; struct cdc_state *info = (void *) &dev->data; union { void *buf; struct rndis_msg_hdr *header; struct rndis_init *init; struct rndis_init_c *init_c; struct rndis_query *get; struct rndis_query_c *get_c; struct rndis_set *set; struct rndis_set_c *set_c; struct rndis_halt *halt; } u; u32 tmp; __le32 phym_unspec, *phym; int reply_len; unsigned char *bp; /* we can't rely on i/o from stack working, or stack allocation */ u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (!u.buf) return -ENOMEM; retval = usbnet_generic_cdc_bind(dev, intf); if (retval < 0) goto fail; u.init->msg_type = cpu_to_le32(RNDIS_MSG_INIT); u.init->msg_len = cpu_to_le32(sizeof *u.init); u.init->major_version = cpu_to_le32(1); u.init->minor_version = cpu_to_le32(0); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) /* can't we remove this? */ net->change_mtu = NULL; #endif /* max transfer (in spec) is 0x4000 at full speed, but for * TX we'll stick to one Ethernet packet plus RNDIS framing. * For RX we handle drivers that zero-pad to end-of-packet. * Don't let userspace change these settings. * * NOTE: there still seems to be wierdness here, as if we need * to do some more things to make sure WinCE targets accept this. * They default to jumbograms of 8KB or 16KB, which is absurd * for such low data rates and which is also more than Linux * can usually expect to allocate for SKB data... */ net->hard_header_len += sizeof (struct rndis_data_hdr); dev->hard_mtu = net->mtu + net->hard_header_len; dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1); if (dev->maxpacket == 0) { netif_dbg(dev, probe, dev->net, "dev->maxpacket can't be 0\n"); retval = -EINVAL; goto fail_and_release; } dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1); dev->rx_urb_size &= ~(dev->maxpacket - 1); u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); netdev_attach_ops(net, &rndis_netdev_ops); retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); goto fail_and_release; } tmp = le32_to_cpu(u.init_c->max_transfer_size); if (tmp < dev->hard_mtu) { if (tmp <= net->hard_header_len) { dev_err(&intf->dev, "dev can't take %u byte packets (max %u)\n", dev->hard_mtu, tmp); retval = -EINVAL; goto halt_fail_and_release; } dev_warn(&intf->dev, "dev can't take %u byte packets (max %u), " "adjusting MTU to %u\n", dev->hard_mtu, tmp, tmp - net->hard_header_len); dev->hard_mtu = tmp; net->mtu = dev->hard_mtu - net->hard_header_len; } /* REVISIT: peripheral "alignment" request is ignored ... */ dev_dbg(&intf->dev, "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n", dev->hard_mtu, tmp, dev->rx_urb_size, 1 << le32_to_cpu(u.init_c->packet_alignment)); /* module has some device initialization code needs to be done right * after RNDIS_INIT */ if (dev->driver_info->early_init && dev->driver_info->early_init(dev) != 0) goto halt_fail_and_release; /* Check physical medium */ phym = NULL; reply_len = sizeof *phym; retval = rndis_query(dev, intf, u.buf, RNDIS_OID_GEN_PHYSICAL_MEDIUM, 0, (void **) &phym, &reply_len); if (retval != 0 || !phym) { /* OID is optional so don't fail here. */ phym_unspec = cpu_to_le32(RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED); phym = &phym_unspec; } if ((flags & FLAG_RNDIS_PHYM_WIRELESS) && le32_to_cpup(phym) != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { netif_dbg(dev, probe, dev->net, "driver requires wireless physical medium, but device is not\n"); retval = -ENODEV; goto halt_fail_and_release; } if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) && le32_to_cpup(phym) == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { netif_dbg(dev, probe, dev->net, "driver requires non-wireless physical medium, but device is wireless.\n"); retval = -ENODEV; goto halt_fail_and_release; } /* Get designated host ethernet address */ reply_len = ETH_ALEN; retval = rndis_query(dev, intf, u.buf, RNDIS_OID_802_3_PERMANENT_ADDRESS, 48, (void **) &bp, &reply_len); if (unlikely(retval< 0)) { dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); goto halt_fail_and_release; } memcpy(net->dev_addr, bp, ETH_ALEN); memcpy(net->perm_addr, bp, ETH_ALEN); /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); u.set->msg_len = cpu_to_le32(4 + sizeof *u.set); u.set->oid = cpu_to_le32(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); u.set->len = cpu_to_le32(4); u.set->offset = cpu_to_le32((sizeof *u.set) - 8); *(__le32 *)(u.buf + sizeof *u.set) = cpu_to_le32(RNDIS_DEFAULT_FILTER); retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); goto halt_fail_and_release; } retval = 0; kfree(u.buf); return retval; halt_fail_and_release: memset(u.halt, 0, sizeof *u.halt); u.halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT); u.halt->msg_len = cpu_to_le32(sizeof *u.halt); (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE); fail_and_release: usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); info->data = NULL; fail: kfree(u.buf); return retval; } EXPORT_SYMBOL_GPL(generic_rndis_bind); static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) { return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS); } void rndis_unbind(struct usbnet *dev, struct usb_interface *intf) { struct rndis_halt *halt; /* try to clear any rndis state/activity (no i/o from stack!) */ halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (halt) { halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT); halt->msg_len = cpu_to_le32(sizeof *halt); (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE); kfree(halt); } usbnet_cdc_unbind(dev, intf); } EXPORT_SYMBOL_GPL(rndis_unbind); /* * DATA -- host must not write zlps */ int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb) { /* peripheral may have batched packets to us... */ while (likely(skb->len)) { struct rndis_data_hdr *hdr = (void *)skb->data; struct sk_buff *skb2; u32 msg_type, msg_len, data_offset, data_len; msg_type = le32_to_cpu(hdr->msg_type); msg_len = le32_to_cpu(hdr->msg_len); data_offset = le32_to_cpu(hdr->data_offset); data_len = le32_to_cpu(hdr->data_len); /* don't choke if we see oob, per-packet data, etc */ if (unlikely(msg_type != RNDIS_MSG_PACKET || skb->len < msg_len || (data_offset + data_len + 8) > msg_len)) { dev->net->stats.rx_frame_errors++; netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n", le32_to_cpu(hdr->msg_type), msg_len, data_offset, data_len, skb->len); return 0; } skb_pull(skb, 8 + data_offset); /* at most one packet left? */ if (likely((data_len - skb->len) <= sizeof *hdr)) { skb_trim(skb, data_len); break; } /* try to return all the packets in the batch */ skb2 = skb_clone(skb, GFP_ATOMIC); if (unlikely(!skb2)) break; skb_pull(skb, msg_len - sizeof *hdr); skb_trim(skb2, data_len); usbnet_skb_return(dev, skb2); } /* caller will usbnet_skb_return the remaining packet */ return 1; } EXPORT_SYMBOL_GPL(rndis_rx_fixup); struct sk_buff * rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { struct rndis_data_hdr *hdr; struct sk_buff *skb2; unsigned len = skb->len; if (likely(!skb_cloned(skb))) { int room = skb_headroom(skb); /* enough head room as-is? */ if (unlikely((sizeof *hdr) <= room)) goto fill; /* enough room, but needs to be readjusted? */ room += skb_tailroom(skb); if (likely((sizeof *hdr) <= room)) { skb->data = memmove(skb->head + sizeof *hdr, skb->data, len); skb_set_tail_pointer(skb, len); goto fill; } } /* create a new skb, with the correct size (and tailpad) */ skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags); dev_kfree_skb_any(skb); if (unlikely(!skb2)) return skb2; skb = skb2; /* fill out the RNDIS header. we won't bother trying to batch * packets; Linux minimizes wasted bandwidth through tx queues. */ fill: hdr = (void *) __skb_push(skb, sizeof *hdr); memset(hdr, 0, sizeof *hdr); hdr->msg_type = cpu_to_le32(RNDIS_MSG_PACKET); hdr->msg_len = cpu_to_le32(skb->len); hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8); hdr->data_len = cpu_to_le32(len); /* FIXME make the last packet always be short ... */ return skb; } EXPORT_SYMBOL_GPL(rndis_tx_fixup); static const struct driver_info rndis_info = { .description = "RNDIS device", .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, .bind = rndis_bind, .unbind = rndis_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, }; static const struct driver_info rndis_poll_status_info = { .description = "RNDIS device (poll status before control)", .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, .data = RNDIS_DRIVER_DATA_POLL_STATUS, .bind = rndis_bind, .unbind = rndis_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, }; /*-------------------------------------------------------------------------*/ static const struct usb_device_id products [] = { { /* 2Wire HomePortal 1000SW */ USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042, USB_CLASS_COMM, 2 /* ACM */, 0x0ff), .driver_info = (unsigned long) &rndis_poll_status_info, }, { /* RNDIS is MSFT's un-official variant of CDC ACM */ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), .driver_info = (unsigned long) &rndis_info, }, { /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), .driver_info = (unsigned long) &rndis_poll_status_info, }, { /* RNDIS for tethering */ USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), .driver_info = (unsigned long) &rndis_info, }, { }, // END }; MODULE_DEVICE_TABLE(usb, products); static struct usb_driver rndis_driver = { .name = "rndis_host", .id_table = products, .probe = usbnet_probe, .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(rndis_driver); MODULE_AUTHOR("David Brownell"); MODULE_DESCRIPTION("USB Host side RNDIS driver"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/usb/cdc_ether.c0000644000175000017500000005201612026211315021505 0ustar mcgrofmcgrof/* * CDC Ethernet based networking peripherals * Copyright (C) 2003-2005 by David Brownell * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // #define DEBUG // error path messages, extra info // #define VERBOSE // more; success messages #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_USB_NET_COMPAT_RNDIS_HOST) || defined(CONFIG_USB_NET_COMPAT_RNDIS_HOST_MODULE) static int is_rndis(struct usb_interface_descriptor *desc) { return (desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 && desc->bInterfaceProtocol == 0xff); } static int is_activesync(struct usb_interface_descriptor *desc) { return (desc->bInterfaceClass == USB_CLASS_MISC && desc->bInterfaceSubClass == 1 && desc->bInterfaceProtocol == 1); } static int is_wireless_rndis(struct usb_interface_descriptor *desc) { return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER && desc->bInterfaceSubClass == 1 && desc->bInterfaceProtocol == 3); } #else #define is_rndis(desc) 0 #define is_activesync(desc) 0 #define is_wireless_rndis(desc) 0 #endif static const u8 mbm_guid[16] = { 0xa3, 0x17, 0xa8, 0x8b, 0x04, 0x5e, 0x4f, 0x01, 0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a, }; /* * probes control interface, claims data interface, collects the bulk * endpoints, activates data interface (if needed), maybe sets MTU. * all pure cdc, except for certain firmware workarounds, and knowing * that rndis uses one different rule. */ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) { u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *d; struct cdc_state *info = (void *) &dev->data; int status; int rndis; bool android_rndis_quirk = false; struct usb_driver *driver = driver_of(intf); struct usb_cdc_mdlm_desc *desc = NULL; struct usb_cdc_mdlm_detail_desc *detail = NULL; if (sizeof dev->data < sizeof *info) return -EDOM; /* expect strict spec conformance for the descriptors, but * cope with firmware which stores them in the wrong place */ if (len == 0 && dev->udev->actconfig->extralen) { /* Motorola SB4100 (and others: Brad Hards says it's * from a Broadcom design) put CDC descriptors here */ buf = dev->udev->actconfig->extra; len = dev->udev->actconfig->extralen; dev_dbg(&intf->dev, "CDC descriptors on config\n"); } /* Maybe CDC descriptors are after the endpoint? This bug has * been seen on some 2Wire Inc RNDIS-ish products. */ if (len == 0) { struct usb_host_endpoint *hep; hep = intf->cur_altsetting->endpoint; if (hep) { buf = hep->extra; len = hep->extralen; } if (len) dev_dbg(&intf->dev, "CDC descriptors on endpoint\n"); } /* this assumes that if there's a non-RNDIS vendor variant * of cdc-acm, it'll fail RNDIS requests cleanly. */ rndis = (is_rndis(&intf->cur_altsetting->desc) || is_activesync(&intf->cur_altsetting->desc) || is_wireless_rndis(&intf->cur_altsetting->desc)); memset(info, 0, sizeof *info); info->control = intf; while (len > 3) { if (buf [1] != USB_DT_CS_INTERFACE) goto next_desc; /* use bDescriptorSubType to identify the CDC descriptors. * We expect devices with CDC header and union descriptors. * For CDC Ethernet we need the ethernet descriptor. * For RNDIS, ignore two (pointless) CDC modem descriptors * in favor of a complicated OID-based RPC scheme doing what * CDC Ethernet achieves with a simple descriptor. */ switch (buf [2]) { case USB_CDC_HEADER_TYPE: if (info->header) { dev_dbg(&intf->dev, "extra CDC header\n"); goto bad_desc; } info->header = (void *) buf; if (info->header->bLength != sizeof *info->header) { dev_dbg(&intf->dev, "CDC header len %u\n", info->header->bLength); goto bad_desc; } break; case USB_CDC_ACM_TYPE: /* paranoia: disambiguate a "real" vendor-specific * modem interface from an RNDIS non-modem. */ if (rndis) { struct usb_cdc_acm_descriptor *acm; acm = (void *) buf; if (acm->bmCapabilities) { dev_dbg(&intf->dev, "ACM capabilities %02x, " "not really RNDIS?\n", acm->bmCapabilities); goto bad_desc; } } break; case USB_CDC_UNION_TYPE: if (info->u) { dev_dbg(&intf->dev, "extra CDC union\n"); goto bad_desc; } info->u = (void *) buf; if (info->u->bLength != sizeof *info->u) { dev_dbg(&intf->dev, "CDC union len %u\n", info->u->bLength); goto bad_desc; } /* we need a master/control interface (what we're * probed with) and a slave/data interface; union * descriptors sort this all out. */ info->control = usb_ifnum_to_if(dev->udev, info->u->bMasterInterface0); info->data = usb_ifnum_to_if(dev->udev, info->u->bSlaveInterface0); if (!info->control || !info->data) { dev_dbg(&intf->dev, "master #%u/%p slave #%u/%p\n", info->u->bMasterInterface0, info->control, info->u->bSlaveInterface0, info->data); /* fall back to hard-wiring for RNDIS */ if (rndis) { android_rndis_quirk = true; goto next_desc; } goto bad_desc; } if (info->control != intf) { dev_dbg(&intf->dev, "bogus CDC Union\n"); /* Ambit USB Cable Modem (and maybe others) * interchanges master and slave interface. */ if (info->data == intf) { info->data = info->control; info->control = intf; } else goto bad_desc; } /* a data interface altsetting does the real i/o */ d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { dev_dbg(&intf->dev, "slave class %u\n", d->bInterfaceClass); goto bad_desc; } break; case USB_CDC_ETHERNET_TYPE: if (info->ether) { dev_dbg(&intf->dev, "extra CDC ether\n"); goto bad_desc; } info->ether = (void *) buf; if (info->ether->bLength != sizeof *info->ether) { dev_dbg(&intf->dev, "CDC ether len %u\n", info->ether->bLength); goto bad_desc; } dev->hard_mtu = le16_to_cpu( info->ether->wMaxSegmentSize); /* because of Zaurus, we may be ignoring the host * side link address we were given. */ break; case USB_CDC_MDLM_TYPE: if (desc) { dev_dbg(&intf->dev, "extra MDLM descriptor\n"); goto bad_desc; } desc = (void *)buf; if (desc->bLength != sizeof(*desc)) goto bad_desc; if (memcmp(&desc->bGUID, mbm_guid, 16)) goto bad_desc; break; case USB_CDC_MDLM_DETAIL_TYPE: if (detail) { dev_dbg(&intf->dev, "extra MDLM detail descriptor\n"); goto bad_desc; } detail = (void *)buf; if (detail->bGuidDescriptorType == 0) { if (detail->bLength < (sizeof(*detail) + 1)) goto bad_desc; } else goto bad_desc; break; } next_desc: len -= buf [0]; /* bLength */ buf += buf [0]; } /* Microsoft ActiveSync based and some regular RNDIS devices lack the * CDC descriptors, so we'll hard-wire the interfaces and not check * for descriptors. * * Some Android RNDIS devices have a CDC Union descriptor pointing * to non-existing interfaces. Ignore that and attempt the same * hard-wired 0 and 1 interfaces. */ if (rndis && (!info->u || android_rndis_quirk)) { info->control = usb_ifnum_to_if(dev->udev, 0); info->data = usb_ifnum_to_if(dev->udev, 1); if (!info->control || !info->data || info->control != intf) { dev_dbg(&intf->dev, "rndis: master #0/%p slave #1/%p\n", info->control, info->data); goto bad_desc; } } else if (!info->header || !info->u || (!rndis && !info->ether)) { dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", info->ether ? "" : "ether "); goto bad_desc; } /* claim data interface and set it up ... with side effects. * network traffic can't flow until an altsetting is enabled. */ status = usb_driver_claim_interface(driver, info->data, dev); if (status < 0) return status; status = usbnet_get_endpoints(dev, info->data); if (status < 0) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); return status; } /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ dev->status = NULL; if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { struct usb_endpoint_descriptor *desc; dev->status = &info->control->cur_altsetting->endpoint [0]; desc = &dev->status->desc; if (!usb_endpoint_is_int_in(desc) || (le16_to_cpu(desc->wMaxPacketSize) < sizeof(struct usb_cdc_notification)) || !desc->bInterval) { dev_dbg(&intf->dev, "bad notification endpoint\n"); dev->status = NULL; } } if (rndis && !dev->status) { dev_dbg(&intf->dev, "missing RNDIS status endpoint\n"); usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); return -ENODEV; } return 0; bad_desc: dev_info(&dev->udev->dev, "bad CDC descriptors\n"); return -ENODEV; } EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind); void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf) { struct cdc_state *info = (void *) &dev->data; struct usb_driver *driver = driver_of(intf); /* disconnect master --> disconnect slave */ if (intf == info->control && info->data) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver, info->data); info->data = NULL; } /* and vice versa (just in case) */ else if (intf == info->data && info->control) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->control, NULL); usb_driver_release_interface(driver, info->control); info->control = NULL; } } EXPORT_SYMBOL_GPL(usbnet_cdc_unbind); /*------------------------------------------------------------------------- * * Communications Device Class, Ethernet Control model * * Takes two interfaces. The DATA interface is inactive till an altsetting * is selected. Configuration data includes class descriptors. There's * an optional status endpoint on the control interface. * * This should interop with whatever the 2.4 "CDCEther.c" driver * (by Brad Hards) talked with, with more functionality. * *-------------------------------------------------------------------------*/ static void dumpspeed(struct usbnet *dev, __le32 *speeds) { netif_info(dev, timer, dev->net, "link speeds: %u kbps up, %u kbps down\n", __le32_to_cpu(speeds[0]) / 1000, __le32_to_cpu(speeds[1]) / 1000); } void usbnet_cdc_status(struct usbnet *dev, struct urb *urb) { struct usb_cdc_notification *event; if (urb->actual_length < sizeof *event) return; /* SPEED_CHANGE can get split into two 8-byte packets */ if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) { dumpspeed(dev, (__le32 *) urb->transfer_buffer); return; } event = urb->transfer_buffer; switch (event->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n", event->wValue ? "on" : "off"); if (event->wValue) netif_carrier_on(dev->net); else netif_carrier_off(dev->net); break; case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */ netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n", urb->actual_length); if (urb->actual_length != (sizeof *event + 8)) set_bit(EVENT_STS_SPLIT, &dev->flags); else dumpspeed(dev, (__le32 *) &event[1]); break; /* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS), * but there are no standard formats for the response data. */ default: netdev_err(dev->net, "CDC: unexpected notification %02x!\n", event->bNotificationType); break; } } EXPORT_SYMBOL_GPL(usbnet_cdc_status); int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf) { int status; struct cdc_state *info = (void *) &dev->data; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct cdc_state))); status = usbnet_generic_cdc_bind(dev, intf); if (status < 0) return status; status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress); if (status < 0) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); return status; } /* FIXME cdc-ether has some multicast code too, though it complains * in routine cases. info->ether describes the multicast support. * Implement that here, manipulating the cdc filter as needed. */ return 0; } EXPORT_SYMBOL_GPL(usbnet_cdc_bind); static int cdc_manage_power(struct usbnet *dev, int on) { dev->intf->needs_remote_wakeup = on; return 0; } static const struct driver_info cdc_info = { .description = "CDC Ethernet Device", .flags = FLAG_ETHER | FLAG_POINTTOPOINT, // .check_connect = cdc_check_connect, .bind = usbnet_cdc_bind, .unbind = usbnet_cdc_unbind, .status = usbnet_cdc_status, .manage_power = cdc_manage_power, }; static const struct driver_info wwan_info = { .description = "Mobile Broadband Network Device", .flags = FLAG_WWAN, .bind = usbnet_cdc_bind, .unbind = usbnet_cdc_unbind, .status = usbnet_cdc_status, .manage_power = cdc_manage_power, }; /*-------------------------------------------------------------------------*/ #define HUAWEI_VENDOR_ID 0x12D1 #define NOVATEL_VENDOR_ID 0x1410 #define ZTE_VENDOR_ID 0x19D2 static const struct usb_device_id products [] = { /* * BLACKLIST !! * * First blacklist any products that are egregiously nonconformant * with the CDC Ethernet specs. Minor braindamage we cope with; when * they're not even trying, needing a separate driver is only the first * of the differences to show up. */ #define ZAURUS_MASTER_INTERFACE \ .bInterfaceClass = USB_CLASS_COMM, \ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ .bInterfaceProtocol = USB_CDC_PROTO_NONE /* SA-1100 based Sharp Zaurus ("collie"), or compatible; * wire-incompatible with true CDC Ethernet implementations. * (And, it seems, needlessly so...) */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8004, ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, /* PXA-25x based Sharp Zaurii. Note that it seems some of these * (later models especially) may have shipped only with firmware * advertising false "CDC MDLM" compatibility ... but we're not * clear which models did that, so for now let's assume the worst. */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8005, /* A-300 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8006, /* B-500/SL-5600 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8007, /* C-700 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x9031, /* C-750 C-760 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x9032, /* SL-6000 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, /* reported with some C860 units */ .idProduct = 0x9050, /* C-860 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, /* Olympus has some models with a Zaurus-compatible option. * R-1000 uses a FreeScale i.MXL cpu (ARMv4T) */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x07B4, .idProduct = 0x0F02, /* R-1000 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, /* LG Electronics VL600 wants additional headers on every frame */ { USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = 0, }, /* Logitech Harmony 900 - uses the pseudo-MDLM (BLAN) driver */ { USB_DEVICE_AND_INTERFACE_INFO(0x046d, 0xc11f, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = 0, }, /* * WHITELIST!!! * * CDC Ether uses two interfaces, not necessarily consecutive. * We match the main interface, ignoring the optional device * class so we could handle devices that aren't exclusively * CDC ether. * * NOTE: this match must come AFTER entries blacklisting devices * because of bugs/quirks in a given product (like Zaurus, above). */ { /* Novatel USB551L */ /* This match must come *before* the generic CDC-ETHER match so that * we get FLAG_WWAN set on the device, since it's descriptors are * generic CDC-ETHER. */ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = NOVATEL_VENDOR_ID, .idProduct = 0xB001, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = USB_CDC_PROTO_NONE, .driver_info = (unsigned long)&wwan_info, }, { /* ZTE (Vodafone) K3805-Z */ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = ZTE_VENDOR_ID, .idProduct = 0x1003, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = USB_CDC_PROTO_NONE, .driver_info = (unsigned long)&wwan_info, }, { /* ZTE (Vodafone) K3806-Z */ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = ZTE_VENDOR_ID, .idProduct = 0x1015, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = USB_CDC_PROTO_NONE, .driver_info = (unsigned long)&wwan_info, }, { /* ZTE (Vodafone) K4510-Z */ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = ZTE_VENDOR_ID, .idProduct = 0x1173, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = USB_CDC_PROTO_NONE, .driver_info = (unsigned long)&wwan_info, }, { /* ZTE (Vodafone) K3770-Z */ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = ZTE_VENDOR_ID, .idProduct = 0x1177, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = USB_CDC_PROTO_NONE, .driver_info = (unsigned long)&wwan_info, }, { /* ZTE (Vodafone) K3772-Z */ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = ZTE_VENDOR_ID, .idProduct = 0x1181, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = USB_CDC_PROTO_NONE, .driver_info = (unsigned long)&wwan_info, }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&wwan_info, }, { /* Various Huawei modems with a network port like the UMG1831 */ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = HUAWEI_VENDOR_ID, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = 255, .driver_info = (unsigned long)&wwan_info, }, { }, // END }; MODULE_DEVICE_TABLE(usb, products); static struct usb_driver cdc_driver = { .name = "cdc_ether", .id_table = products, .probe = usbnet_probe, .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, .reset_resume = usbnet_resume, .supports_autosuspend = 1, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, #endif }; module_usb_driver(cdc_driver); MODULE_AUTHOR("David Brownell"); MODULE_DESCRIPTION("USB CDC Ethernet devices"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/net/usb/usbnet.c0000644000175000017500000012557312026211315021076 0ustar mcgrofmcgrof/* * USB Network driver infrastructure * Copyright (C) 2000-2005 by David Brownell * Copyright (C) 2003-2005 David Hollis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This is a generic "USB networking" framework that works with several * kinds of full and high speed networking devices: host-to-host cables, * smart usb peripherals, and actual Ethernet adapters. * * These devices usually differ in terms of control protocols (if they * even have one!) and sometimes they define new framing to wrap or batch * Ethernet packets. Otherwise, they talk to USB pretty much the same, * so interface (un)binding, endpoint I/O queues, fault handling, and other * issues can usefully be addressed by this framework. */ // #define DEBUG // error path messages, extra info // #define VERBOSE // more; success messages #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_VERSION "22-Aug-2005" /*-------------------------------------------------------------------------*/ /* * Nineteen USB 1.1 max size bulk transactions per frame (ms), max. * Several dozen bytes of IPv4 data can fit in two such transactions. * One maximum size Ethernet packet takes twenty four of them. * For high speed, each frame comfortably fits almost 36 max size * Ethernet packets (so queues should be bigger). * * REVISIT qlens should be members of 'struct usbnet'; the goal is to * let the USB host controller be busy for 5msec or more before an irq * is required, under load. Jumbograms change the equation. */ #define RX_MAX_QUEUE_MEMORY (60 * 1518) #define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4) #define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4) // reawaken network queue this soon after stopping; else watchdog barks #define TX_TIMEOUT_JIFFIES (5*HZ) // throttle rx/tx briefly after some faults, so khubd might disconnect() // us (it polls at HZ/4 usually) before we report too many false errors. #define THROTTLE_JIFFIES (HZ/8) // between wakeups #define UNLINK_TIMEOUT_MS 3 /*-------------------------------------------------------------------------*/ // randomly generated ethernet address static u8 node_id [ETH_ALEN]; static const char driver_name [] = "usbnet"; /* use ethtool to change the level for any given device */ static int msg_level = -1; module_param (msg_level, int, 0); MODULE_PARM_DESC (msg_level, "Override default message level"); /*-------------------------------------------------------------------------*/ /* handles CDC Ethernet and many other network "bulk data" interfaces */ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf) { int tmp; struct usb_host_interface *alt = NULL; struct usb_host_endpoint *in = NULL, *out = NULL; struct usb_host_endpoint *status = NULL; for (tmp = 0; tmp < intf->num_altsetting; tmp++) { unsigned ep; in = out = status = NULL; alt = intf->altsetting + tmp; /* take the first altsetting with in-bulk + out-bulk; * remember any status endpoint, just in case; * ignore other endpoints and altsettings. */ for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) { struct usb_host_endpoint *e; int intr = 0; e = alt->endpoint + ep; switch (e->desc.bmAttributes) { case USB_ENDPOINT_XFER_INT: if (!usb_endpoint_dir_in(&e->desc)) continue; intr = 1; /* FALLTHROUGH */ case USB_ENDPOINT_XFER_BULK: break; default: continue; } if (usb_endpoint_dir_in(&e->desc)) { if (!intr && !in) in = e; else if (intr && !status) status = e; } else { if (!out) out = e; } } if (in && out) break; } if (!alt || !in || !out) return -EINVAL; if (alt->desc.bAlternateSetting != 0 || !(dev->driver_info->flags & FLAG_NO_SETINT)) { tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber, alt->desc.bAlternateSetting); if (tmp < 0) return tmp; } dev->in = usb_rcvbulkpipe (dev->udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->out = usb_sndbulkpipe (dev->udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->status = status; return 0; } EXPORT_SYMBOL_GPL(usbnet_get_endpoints); int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress) { int tmp, i; unsigned char buf [13]; tmp = usb_string(dev->udev, iMACAddress, buf, sizeof buf); if (tmp != 12) { dev_dbg(&dev->udev->dev, "bad MAC string %d fetch, %d\n", iMACAddress, tmp); if (tmp >= 0) tmp = -EINVAL; return tmp; } for (i = tmp = 0; i < 6; i++, tmp += 2) dev->net->dev_addr [i] = (hex_to_bin(buf[tmp]) << 4) + hex_to_bin(buf[tmp + 1]); return 0; } EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr); static void intr_complete (struct urb *urb) { struct usbnet *dev = urb->context; int status = urb->status; switch (status) { /* success */ case 0: dev->driver_info->status(dev, urb); break; /* software-driven interface shutdown */ case -ENOENT: /* urb killed */ case -ESHUTDOWN: /* hardware gone */ netif_dbg(dev, ifdown, dev->net, "intr shutdown, code %d\n", status); return; /* NOTE: not throttling like RX/TX, since this endpoint * already polls infrequently */ default: netdev_dbg(dev->net, "intr status %d\n", status); break; } if (!netif_running (dev->net)) return; status = usb_submit_urb (urb, GFP_ATOMIC); if (status != 0) netif_err(dev, timer, dev->net, "intr resubmit --> %d\n", status); } static int init_status (struct usbnet *dev, struct usb_interface *intf) { char *buf = NULL; unsigned pipe = 0; unsigned maxp; unsigned period; if (!dev->driver_info->status) return 0; pipe = usb_rcvintpipe (dev->udev, dev->status->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); maxp = usb_maxpacket (dev->udev, pipe, 0); /* avoid 1 msec chatter: min 8 msec poll rate */ period = max ((int) dev->status->desc.bInterval, (dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3); buf = kmalloc (maxp, GFP_KERNEL); if (buf) { dev->interrupt = usb_alloc_urb (0, GFP_KERNEL); if (!dev->interrupt) { kfree (buf); return -ENOMEM; } else { usb_fill_int_urb(dev->interrupt, dev->udev, pipe, buf, maxp, intr_complete, dev, period); dev->interrupt->transfer_flags |= URB_FREE_BUFFER; dev_dbg(&intf->dev, "status ep%din, %d bytes period %d\n", usb_pipeendpoint(pipe), maxp, period); } } return 0; } /* Passes this packet up the stack, updating its accounting. * Some link protocols batch packets, so their rx_fixup paths * can return clones as well as just modify the original skb. */ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) { int status; if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { skb_queue_tail(&dev->rxq_pause, skb); return; } skb->protocol = eth_type_trans (skb, dev->net); dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len; netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n", skb->len + sizeof (struct ethhdr), skb->protocol); memset (skb->cb, 0, sizeof (struct skb_data)); if (skb_defer_rx_timestamp(skb)) return; status = netif_rx (skb); if (status != NET_RX_SUCCESS) netif_dbg(dev, rx_err, dev->net, "netif_rx status %d\n", status); } EXPORT_SYMBOL_GPL(usbnet_skb_return); /*------------------------------------------------------------------------- * * Network Device Driver (peer link to "Host Device", from USB host) * *-------------------------------------------------------------------------*/ int usbnet_change_mtu (struct net_device *net, int new_mtu) { struct usbnet *dev = netdev_priv(net); int ll_mtu = new_mtu + net->hard_header_len; int old_hard_mtu = dev->hard_mtu; int old_rx_urb_size = dev->rx_urb_size; if (new_mtu <= 0) return -EINVAL; // no second zero-length packet read wanted after mtu-sized packets if ((ll_mtu % dev->maxpacket) == 0) return -EDOM; net->mtu = new_mtu; dev->hard_mtu = net->mtu + net->hard_header_len; if (dev->rx_urb_size == old_hard_mtu) { dev->rx_urb_size = dev->hard_mtu; if (dev->rx_urb_size > old_rx_urb_size) usbnet_unlink_rx_urbs(dev); } return 0; } EXPORT_SYMBOL_GPL(usbnet_change_mtu); /* The caller must hold list->lock */ static void __usbnet_queue_skb(struct sk_buff_head *list, struct sk_buff *newsk, enum skb_state state) { struct skb_data *entry = (struct skb_data *) newsk->cb; __skb_queue_tail(list, newsk); entry->state = state; } /*-------------------------------------------------------------------------*/ /* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from * completion callbacks. 2.5 should have fixed those bugs... */ static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list, enum skb_state state) { unsigned long flags; enum skb_state old_state; struct skb_data *entry = (struct skb_data *) skb->cb; spin_lock_irqsave(&list->lock, flags); old_state = entry->state; entry->state = state; __skb_unlink(skb, list); spin_unlock(&list->lock); spin_lock(&dev->done.lock); __skb_queue_tail(&dev->done, skb); if (dev->done.qlen == 1) tasklet_schedule(&dev->bh); spin_unlock_irqrestore(&dev->done.lock, flags); return old_state; } /* some work can't be done in tasklets, so we use keventd * * NOTE: annoying asymmetry: if it's active, schedule_work() fails, * but tasklet_schedule() doesn't. hope the failure is rare. */ void usbnet_defer_kevent (struct usbnet *dev, int work) { set_bit (work, &dev->flags); if (!schedule_work (&dev->kevent)) netdev_err(dev->net, "kevent %d may have been dropped\n", work); else netdev_dbg(dev->net, "kevent %d scheduled\n", work); } EXPORT_SYMBOL_GPL(usbnet_defer_kevent); /*-------------------------------------------------------------------------*/ static void rx_complete (struct urb *urb); static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) { struct sk_buff *skb; struct skb_data *entry; int retval = 0; unsigned long lockflags; size_t size = dev->rx_urb_size; skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); if (!skb) { netif_dbg(dev, rx_err, dev->net, "no rx skb\n"); usbnet_defer_kevent (dev, EVENT_RX_MEMORY); usb_free_urb (urb); return -ENOMEM; } entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; entry->length = 0; usb_fill_bulk_urb (urb, dev->udev, dev->in, skb->data, size, rx_complete, skb); spin_lock_irqsave (&dev->rxq.lock, lockflags); if (netif_running (dev->net) && netif_device_present (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags) && !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) { switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) { case -EPIPE: usbnet_defer_kevent (dev, EVENT_RX_HALT); break; case -ENOMEM: usbnet_defer_kevent (dev, EVENT_RX_MEMORY); break; case -ENODEV: netif_dbg(dev, ifdown, dev->net, "device gone\n"); netif_device_detach (dev->net); break; case -EHOSTUNREACH: retval = -ENOLINK; break; default: netif_dbg(dev, rx_err, dev->net, "rx submit, %d\n", retval); tasklet_schedule (&dev->bh); break; case 0: __usbnet_queue_skb(&dev->rxq, skb, rx_start); } } else { netif_dbg(dev, ifdown, dev->net, "rx: stopped\n"); retval = -ENOLINK; } spin_unlock_irqrestore (&dev->rxq.lock, lockflags); if (retval) { dev_kfree_skb_any (skb); usb_free_urb (urb); } return retval; } /*-------------------------------------------------------------------------*/ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) { if (dev->driver_info->rx_fixup && !dev->driver_info->rx_fixup (dev, skb)) { /* With RX_ASSEMBLE, rx_fixup() must update counters */ if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE)) dev->net->stats.rx_errors++; goto done; } // else network stack removes extra byte if we forced a short packet if (skb->len) { /* all data was already cloned from skb inside the driver */ if (dev->driver_info->flags & FLAG_MULTI_PACKET) dev_kfree_skb_any(skb); else usbnet_skb_return(dev, skb); return; } netif_dbg(dev, rx_err, dev->net, "drop\n"); dev->net->stats.rx_errors++; done: skb_queue_tail(&dev->done, skb); } /*-------------------------------------------------------------------------*/ static void rx_complete (struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct skb_data *entry = (struct skb_data *) skb->cb; struct usbnet *dev = entry->dev; int urb_status = urb->status; enum skb_state state; skb_put (skb, urb->actual_length); state = rx_done; entry->urb = NULL; switch (urb_status) { /* success */ case 0: if (skb->len < dev->net->hard_header_len) { state = rx_cleanup; dev->net->stats.rx_errors++; dev->net->stats.rx_length_errors++; netif_dbg(dev, rx_err, dev->net, "rx length %d\n", skb->len); } break; /* stalls need manual reset. this is rare ... except that * when going through USB 2.0 TTs, unplug appears this way. * we avoid the highspeed version of the ETIMEDOUT/EILSEQ * storm, recovering as needed. */ case -EPIPE: dev->net->stats.rx_errors++; usbnet_defer_kevent (dev, EVENT_RX_HALT); // FALLTHROUGH /* software-driven interface shutdown */ case -ECONNRESET: /* async unlink */ case -ESHUTDOWN: /* hardware gone */ netif_dbg(dev, ifdown, dev->net, "rx shutdown, code %d\n", urb_status); goto block; /* we get controller i/o faults during khubd disconnect() delays. * throttle down resubmits, to avoid log floods; just temporarily, * so we still recover when the fault isn't a khubd delay. */ case -EPROTO: case -ETIME: case -EILSEQ: dev->net->stats.rx_errors++; if (!timer_pending (&dev->delay)) { mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES); netif_dbg(dev, link, dev->net, "rx throttle %d\n", urb_status); } block: state = rx_cleanup; entry->urb = urb; urb = NULL; break; /* data overrun ... flush fifo? */ case -EOVERFLOW: dev->net->stats.rx_over_errors++; // FALLTHROUGH default: state = rx_cleanup; dev->net->stats.rx_errors++; netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status); break; } state = defer_bh(dev, skb, &dev->rxq, state); if (urb) { if (netif_running (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags) && state != unlink_start) { rx_submit (dev, urb, GFP_ATOMIC); usb_mark_last_busy(dev->udev); return; } usb_free_urb (urb); } netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n"); } /*-------------------------------------------------------------------------*/ void usbnet_pause_rx(struct usbnet *dev) { set_bit(EVENT_RX_PAUSED, &dev->flags); netif_dbg(dev, rx_status, dev->net, "paused rx queue enabled\n"); } EXPORT_SYMBOL_GPL(usbnet_pause_rx); void usbnet_resume_rx(struct usbnet *dev) { struct sk_buff *skb; int num = 0; clear_bit(EVENT_RX_PAUSED, &dev->flags); while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) { usbnet_skb_return(dev, skb); num++; } tasklet_schedule(&dev->bh); netif_dbg(dev, rx_status, dev->net, "paused rx queue disabled, %d skbs requeued\n", num); } EXPORT_SYMBOL_GPL(usbnet_resume_rx); void usbnet_purge_paused_rxq(struct usbnet *dev) { skb_queue_purge(&dev->rxq_pause); } EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq); /*-------------------------------------------------------------------------*/ // unlink pending rx/tx; completion handlers do all other cleanup static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) { unsigned long flags; struct sk_buff *skb; int count = 0; spin_lock_irqsave (&q->lock, flags); while (!skb_queue_empty(q)) { struct skb_data *entry; struct urb *urb; int retval; skb_queue_walk(q, skb) { entry = (struct skb_data *) skb->cb; if (entry->state != unlink_start) goto found; } break; found: entry->state = unlink_start; urb = entry->urb; /* * Get reference count of the URB to avoid it to be * freed during usb_unlink_urb, which may trigger * use-after-free problem inside usb_unlink_urb since * usb_unlink_urb is always racing with .complete * handler(include defer_bh). */ usb_get_urb(urb); spin_unlock_irqrestore(&q->lock, flags); // during some PM-driven resume scenarios, // these (async) unlinks complete immediately retval = usb_unlink_urb (urb); if (retval != -EINPROGRESS && retval != 0) netdev_dbg(dev->net, "unlink urb err, %d\n", retval); else count++; usb_put_urb(urb); spin_lock_irqsave(&q->lock, flags); } spin_unlock_irqrestore (&q->lock, flags); return count; } // Flush all pending rx urbs // minidrivers may need to do this when the MTU changes void usbnet_unlink_rx_urbs(struct usbnet *dev) { if (netif_running(dev->net)) { (void) unlink_urbs (dev, &dev->rxq); tasklet_schedule(&dev->bh); } } EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs); /*-------------------------------------------------------------------------*/ // precondition: never called in_interrupt static void usbnet_terminate_urbs(struct usbnet *dev) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup); DECLARE_WAITQUEUE(wait, current); int temp; /* ensure there are no more active urbs */ add_wait_queue(&unlink_wakeup, &wait); set_current_state(TASK_UNINTERRUPTIBLE); dev->wait = &unlink_wakeup; temp = unlink_urbs(dev, &dev->txq) + unlink_urbs(dev, &dev->rxq); /* maybe wait for deletions to finish. */ while (!skb_queue_empty(&dev->rxq) && !skb_queue_empty(&dev->txq) && !skb_queue_empty(&dev->done)) { schedule_timeout(msecs_to_jiffies(UNLINK_TIMEOUT_MS)); set_current_state(TASK_UNINTERRUPTIBLE); netif_dbg(dev, ifdown, dev->net, "waited for %d urb completions\n", temp); } set_current_state(TASK_RUNNING); dev->wait = NULL; remove_wait_queue(&unlink_wakeup, &wait); } int usbnet_stop (struct net_device *net) { struct usbnet *dev = netdev_priv(net); struct driver_info *info = dev->driver_info; int retval; clear_bit(EVENT_DEV_OPEN, &dev->flags); netif_stop_queue (net); netif_info(dev, ifdown, dev->net, "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n", net->stats.rx_packets, net->stats.tx_packets, net->stats.rx_errors, net->stats.tx_errors); /* allow minidriver to stop correctly (wireless devices to turn off * radio etc) */ if (info->stop) { retval = info->stop(dev); if (retval < 0) netif_info(dev, ifdown, dev->net, "stop fail (%d) usbnet usb-%s-%s, %s\n", retval, dev->udev->bus->bus_name, dev->udev->devpath, info->description); } if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) usbnet_terminate_urbs(dev); usb_kill_urb(dev->interrupt); usbnet_purge_paused_rxq(dev); /* deferred work (task, timer, softirq) must also stop. * can't flush_scheduled_work() until we drop rtnl (later), * else workers could deadlock; so make workers a NOP. */ dev->flags = 0; del_timer_sync (&dev->delay); tasklet_kill (&dev->bh); if (info->manage_power) info->manage_power(dev, 0); else usb_autopm_put_interface(dev->intf); return 0; } EXPORT_SYMBOL_GPL(usbnet_stop); /*-------------------------------------------------------------------------*/ // posts reads, and enables write queuing // precondition: never called in_interrupt int usbnet_open (struct net_device *net) { struct usbnet *dev = netdev_priv(net); int retval; struct driver_info *info = dev->driver_info; if ((retval = usb_autopm_get_interface(dev->intf)) < 0) { netif_info(dev, ifup, dev->net, "resumption fail (%d) usbnet usb-%s-%s, %s\n", retval, dev->udev->bus->bus_name, dev->udev->devpath, info->description); goto done_nopm; } // put into "known safe" state if (info->reset && (retval = info->reset (dev)) < 0) { netif_info(dev, ifup, dev->net, "open reset fail (%d) usbnet usb-%s-%s, %s\n", retval, dev->udev->bus->bus_name, dev->udev->devpath, info->description); goto done; } // insist peer be connected if (info->check_connect && (retval = info->check_connect (dev)) < 0) { netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval); goto done; } /* start any status interrupt transfer */ if (dev->interrupt) { retval = usb_submit_urb (dev->interrupt, GFP_KERNEL); if (retval < 0) { netif_err(dev, ifup, dev->net, "intr submit %d\n", retval); goto done; } } set_bit(EVENT_DEV_OPEN, &dev->flags); netif_start_queue (net); netif_info(dev, ifup, dev->net, "open: enable queueing (rx %d, tx %d) mtu %d %s framing\n", (int)RX_QLEN(dev), (int)TX_QLEN(dev), dev->net->mtu, (dev->driver_info->flags & FLAG_FRAMING_NC) ? "NetChip" : (dev->driver_info->flags & FLAG_FRAMING_GL) ? "GeneSys" : (dev->driver_info->flags & FLAG_FRAMING_Z) ? "Zaurus" : (dev->driver_info->flags & FLAG_FRAMING_RN) ? "RNDIS" : (dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" : "simple"); // delay posting reads until we're fully open tasklet_schedule (&dev->bh); if (info->manage_power) { retval = info->manage_power(dev, 1); if (retval < 0) goto done_manage_power_error; usb_autopm_put_interface(dev->intf); } return retval; done_manage_power_error: clear_bit(EVENT_DEV_OPEN, &dev->flags); done: usb_autopm_put_interface(dev->intf); done_nopm: return retval; } EXPORT_SYMBOL_GPL(usbnet_open); /*-------------------------------------------------------------------------*/ /* ethtool methods; minidrivers may need to add some more, but * they'll probably want to use this base set. */ int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd) { struct usbnet *dev = netdev_priv(net); if (!dev->mii.mdio_read) return -EOPNOTSUPP; return mii_ethtool_gset(&dev->mii, cmd); } EXPORT_SYMBOL_GPL(usbnet_get_settings); int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd) { struct usbnet *dev = netdev_priv(net); int retval; if (!dev->mii.mdio_write) return -EOPNOTSUPP; retval = mii_ethtool_sset(&dev->mii, cmd); /* link speed/duplex might have changed */ if (dev->driver_info->link_reset) dev->driver_info->link_reset(dev); return retval; } EXPORT_SYMBOL_GPL(usbnet_set_settings); u32 usbnet_get_link (struct net_device *net) { struct usbnet *dev = netdev_priv(net); /* If a check_connect is defined, return its result */ if (dev->driver_info->check_connect) return dev->driver_info->check_connect (dev) == 0; /* if the device has mii operations, use those */ if (dev->mii.mdio_read) return mii_link_ok(&dev->mii); /* Otherwise, dtrt for drivers calling netif_carrier_{on,off} */ return ethtool_op_get_link(net); } EXPORT_SYMBOL_GPL(usbnet_get_link); int usbnet_nway_reset(struct net_device *net) { struct usbnet *dev = netdev_priv(net); if (!dev->mii.mdio_write) return -EOPNOTSUPP; return mii_nway_restart(&dev->mii); } EXPORT_SYMBOL_GPL(usbnet_nway_reset); void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info) { struct usbnet *dev = netdev_priv(net); strlcpy (info->driver, dev->driver_name, sizeof info->driver); strlcpy (info->version, DRIVER_VERSION, sizeof info->version); strlcpy (info->fw_version, dev->driver_info->description, sizeof info->fw_version); usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info); } EXPORT_SYMBOL_GPL(usbnet_get_drvinfo); u32 usbnet_get_msglevel (struct net_device *net) { struct usbnet *dev = netdev_priv(net); return dev->msg_enable; } EXPORT_SYMBOL_GPL(usbnet_get_msglevel); void usbnet_set_msglevel (struct net_device *net, u32 level) { struct usbnet *dev = netdev_priv(net); dev->msg_enable = level; } EXPORT_SYMBOL_GPL(usbnet_set_msglevel); /* drivers may override default ethtool_ops in their bind() routine */ static const struct ethtool_ops usbnet_ethtool_ops = { .get_settings = usbnet_get_settings, .set_settings = usbnet_set_settings, .get_link = usbnet_get_link, .nway_reset = usbnet_nway_reset, .get_drvinfo = usbnet_get_drvinfo, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .get_ts_info = ethtool_op_get_ts_info, #endif }; /*-------------------------------------------------------------------------*/ /* work that cannot be done in interrupt context uses keventd. * * NOTE: with 2.5 we could do more of this using completion callbacks, * especially now that control transfers can be queued. */ static void kevent (struct work_struct *work) { struct usbnet *dev = container_of(work, struct usbnet, kevent); int status; /* usb_clear_halt() needs a thread context */ if (test_bit (EVENT_TX_HALT, &dev->flags)) { unlink_urbs (dev, &dev->txq); status = usb_autopm_get_interface(dev->intf); if (status < 0) goto fail_pipe; status = usb_clear_halt (dev->udev, dev->out); usb_autopm_put_interface(dev->intf); if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) { if (netif_msg_tx_err (dev)) fail_pipe: netdev_err(dev->net, "can't clear tx halt, status %d\n", status); } else { clear_bit (EVENT_TX_HALT, &dev->flags); if (status != -ESHUTDOWN) netif_wake_queue (dev->net); } } if (test_bit (EVENT_RX_HALT, &dev->flags)) { unlink_urbs (dev, &dev->rxq); status = usb_autopm_get_interface(dev->intf); if (status < 0) goto fail_halt; status = usb_clear_halt (dev->udev, dev->in); usb_autopm_put_interface(dev->intf); if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) { if (netif_msg_rx_err (dev)) fail_halt: netdev_err(dev->net, "can't clear rx halt, status %d\n", status); } else { clear_bit (EVENT_RX_HALT, &dev->flags); tasklet_schedule (&dev->bh); } } /* tasklet could resubmit itself forever if memory is tight */ if (test_bit (EVENT_RX_MEMORY, &dev->flags)) { struct urb *urb = NULL; int resched = 1; if (netif_running (dev->net)) urb = usb_alloc_urb (0, GFP_KERNEL); else clear_bit (EVENT_RX_MEMORY, &dev->flags); if (urb != NULL) { clear_bit (EVENT_RX_MEMORY, &dev->flags); status = usb_autopm_get_interface(dev->intf); if (status < 0) { usb_free_urb(urb); goto fail_lowmem; } if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK) resched = 0; usb_autopm_put_interface(dev->intf); fail_lowmem: if (resched) tasklet_schedule (&dev->bh); } } if (test_bit (EVENT_LINK_RESET, &dev->flags)) { struct driver_info *info = dev->driver_info; int retval = 0; clear_bit (EVENT_LINK_RESET, &dev->flags); status = usb_autopm_get_interface(dev->intf); if (status < 0) goto skip_reset; if(info->link_reset && (retval = info->link_reset(dev)) < 0) { usb_autopm_put_interface(dev->intf); skip_reset: netdev_info(dev->net, "link reset failed (%d) usbnet usb-%s-%s, %s\n", retval, dev->udev->bus->bus_name, dev->udev->devpath, info->description); } else { usb_autopm_put_interface(dev->intf); } } if (dev->flags) netdev_dbg(dev->net, "kevent done, flags = 0x%lx\n", dev->flags); } /*-------------------------------------------------------------------------*/ static void tx_complete (struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct skb_data *entry = (struct skb_data *) skb->cb; struct usbnet *dev = entry->dev; if (urb->status == 0) { if (!(dev->driver_info->flags & FLAG_MULTI_PACKET)) dev->net->stats.tx_packets++; dev->net->stats.tx_bytes += entry->length; } else { dev->net->stats.tx_errors++; switch (urb->status) { case -EPIPE: usbnet_defer_kevent (dev, EVENT_TX_HALT); break; /* software-driven interface shutdown */ case -ECONNRESET: // async unlink case -ESHUTDOWN: // hardware gone break; // like rx, tx gets controller i/o faults during khubd delays // and so it uses the same throttling mechanism. case -EPROTO: case -ETIME: case -EILSEQ: usb_mark_last_busy(dev->udev); if (!timer_pending (&dev->delay)) { mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES); netif_dbg(dev, link, dev->net, "tx throttle %d\n", urb->status); } netif_stop_queue (dev->net); break; default: netif_dbg(dev, tx_err, dev->net, "tx err %d\n", entry->urb->status); break; } } usb_autopm_put_interface_async(dev->intf); (void) defer_bh(dev, skb, &dev->txq, tx_done); } /*-------------------------------------------------------------------------*/ void usbnet_tx_timeout (struct net_device *net) { struct usbnet *dev = netdev_priv(net); unlink_urbs (dev, &dev->txq); tasklet_schedule (&dev->bh); // FIXME: device recovery -- reset? } EXPORT_SYMBOL_GPL(usbnet_tx_timeout); /*-------------------------------------------------------------------------*/ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) { struct usbnet *dev = netdev_priv(net); int length; struct urb *urb = NULL; struct skb_data *entry; struct driver_info *info = dev->driver_info; unsigned long flags; int retval; if (skb) skb_tx_timestamp(skb); // some devices want funky USB-level framing, for // win32 driver (usually) and/or hardware quirks if (info->tx_fixup) { skb = info->tx_fixup (dev, skb, GFP_ATOMIC); if (!skb) { if (netif_msg_tx_err(dev)) { netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); goto drop; } else { /* cdc_ncm collected packet; waits for more */ goto not_drop; } } } length = skb->len; if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { netif_dbg(dev, tx_err, dev->net, "no urb\n"); goto drop; } entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; entry->length = length; usb_fill_bulk_urb (urb, dev->udev, dev->out, skb->data, skb->len, tx_complete, skb); /* don't assume the hardware handles USB_ZERO_PACKET * NOTE: strictly conforming cdc-ether devices should expect * the ZLP here, but ignore the one-byte packet. * NOTE2: CDC NCM specification is different from CDC ECM when * handling ZLP/short packets, so cdc_ncm driver will make short * packet itself if needed. */ if (length % dev->maxpacket == 0) { if (!(info->flags & FLAG_SEND_ZLP)) { if (!(info->flags & FLAG_MULTI_PACKET)) { urb->transfer_buffer_length++; if (skb_tailroom(skb)) { skb->data[skb->len] = 0; __skb_put(skb, 1); } } } else urb->transfer_flags |= URB_ZERO_PACKET; } spin_lock_irqsave(&dev->txq.lock, flags); retval = usb_autopm_get_interface_async(dev->intf); if (retval < 0) { spin_unlock_irqrestore(&dev->txq.lock, flags); goto drop; } #ifdef CONFIG_PM /* if this triggers the device is still a sleep */ if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { /* transmission will be done in resume */ usb_anchor_urb(urb, &dev->deferred); /* no use to process more packets */ netif_stop_queue(net); spin_unlock_irqrestore(&dev->txq.lock, flags); netdev_dbg(dev->net, "Delaying transmission for resumption\n"); goto deferred; } #endif switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) { case -EPIPE: netif_stop_queue (net); usbnet_defer_kevent (dev, EVENT_TX_HALT); usb_autopm_put_interface_async(dev->intf); break; default: usb_autopm_put_interface_async(dev->intf); netif_dbg(dev, tx_err, dev->net, "tx: submit urb err %d\n", retval); break; case 0: net->trans_start = jiffies; __usbnet_queue_skb(&dev->txq, skb, tx_start); if (dev->txq.qlen >= TX_QLEN (dev)) netif_stop_queue (net); } spin_unlock_irqrestore (&dev->txq.lock, flags); if (retval) { netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", retval); drop: dev->net->stats.tx_dropped++; not_drop: if (skb) dev_kfree_skb_any (skb); usb_free_urb (urb); } else netif_dbg(dev, tx_queued, dev->net, "> tx, len %d, type 0x%x\n", length, skb->protocol); #ifdef CONFIG_PM deferred: #endif return NETDEV_TX_OK; } EXPORT_SYMBOL_GPL(usbnet_start_xmit); static int rx_alloc_submit(struct usbnet *dev, gfp_t flags) { struct urb *urb; int i; int ret = 0; /* don't refill the queue all at once */ for (i = 0; i < 10 && dev->rxq.qlen < RX_QLEN(dev); i++) { urb = usb_alloc_urb(0, flags); if (urb != NULL) { ret = rx_submit(dev, urb, flags); if (ret) goto err; } else { ret = -ENOMEM; goto err; } } err: return ret; } /*-------------------------------------------------------------------------*/ // tasklet (work deferred from completions, in_irq) or timer static void usbnet_bh (unsigned long param) { struct usbnet *dev = (struct usbnet *) param; struct sk_buff *skb; struct skb_data *entry; while ((skb = skb_dequeue (&dev->done))) { entry = (struct skb_data *) skb->cb; switch (entry->state) { case rx_done: entry->state = rx_cleanup; rx_process (dev, skb); continue; case tx_done: case rx_cleanup: usb_free_urb (entry->urb); dev_kfree_skb (skb); continue; default: netdev_dbg(dev->net, "bogus skb state %d\n", entry->state); } } // waiting for all pending urbs to complete? if (dev->wait) { if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { wake_up (dev->wait); } // or are we maybe short a few urbs? } else if (netif_running (dev->net) && netif_device_present (dev->net) && !timer_pending (&dev->delay) && !test_bit (EVENT_RX_HALT, &dev->flags)) { int temp = dev->rxq.qlen; if (temp < RX_QLEN(dev)) { if (rx_alloc_submit(dev, GFP_ATOMIC) == -ENOLINK) return; if (temp != dev->rxq.qlen) netif_dbg(dev, link, dev->net, "rxqlen %d --> %d\n", temp, dev->rxq.qlen); if (dev->rxq.qlen < RX_QLEN(dev)) tasklet_schedule (&dev->bh); } if (dev->txq.qlen < TX_QLEN (dev)) netif_wake_queue (dev->net); } } /*------------------------------------------------------------------------- * * USB Device Driver support * *-------------------------------------------------------------------------*/ // precondition: never called in_interrupt void usbnet_disconnect (struct usb_interface *intf) { struct usbnet *dev; struct usb_device *xdev; struct net_device *net; dev = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); if (!dev) return; xdev = interface_to_usbdev (intf); netif_info(dev, probe, dev->net, "unregister '%s' usb-%s-%s, %s\n", intf->dev.driver->name, xdev->bus->bus_name, xdev->devpath, dev->driver_info->description); net = dev->net; unregister_netdev (net); cancel_work_sync(&dev->kevent); if (dev->driver_info->unbind) dev->driver_info->unbind (dev, intf); usb_kill_urb(dev->interrupt); usb_free_urb(dev->interrupt); free_netdev(net); } EXPORT_SYMBOL_GPL(usbnet_disconnect); static const struct net_device_ops usbnet_netdev_ops = { .ndo_open = usbnet_open, .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; /*-------------------------------------------------------------------------*/ // precondition: never called in_interrupt #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type wlan_type = { .name = "wlan", }; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type wwan_type = { .name = "wwan", }; #endif int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) { struct usbnet *dev; struct net_device *net; struct usb_host_interface *interface; struct driver_info *info; struct usb_device *xdev; int status; const char *name; struct usb_driver *driver = to_usb_driver(udev->dev.driver); /* usbnet already took usb runtime pm, so have to enable the feature * for usb interface, otherwise usb_autopm_get_interface may return * failure if USB_SUSPEND(RUNTIME_PM) is enabled. */ if (!driver->supports_autosuspend) { driver->supports_autosuspend = 1; pm_runtime_enable(&udev->dev); } name = udev->dev.driver->name; info = (struct driver_info *) prod->driver_info; if (!info) { dev_dbg (&udev->dev, "blacklisted by %s\n", name); return -ENODEV; } xdev = interface_to_usbdev (udev); interface = udev->cur_altsetting; status = -ENOMEM; // set up our own records net = alloc_etherdev(sizeof(*dev)); if (!net) goto out; /* netdev_printk() needs this so do it as early as possible */ SET_NETDEV_DEV(net, &udev->dev); dev = netdev_priv(net); dev->udev = xdev; dev->intf = udev; dev->driver_info = info; dev->driver_name = name; dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK); skb_queue_head_init (&dev->rxq); skb_queue_head_init (&dev->txq); skb_queue_head_init (&dev->done); skb_queue_head_init(&dev->rxq_pause); dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long) dev; INIT_WORK (&dev->kevent, kevent); init_usb_anchor(&dev->deferred); dev->delay.function = usbnet_bh; dev->delay.data = (unsigned long) dev; init_timer (&dev->delay); mutex_init (&dev->phy_mutex); dev->net = net; strcpy (net->name, "usb%d"); memcpy (net->dev_addr, node_id, sizeof node_id); /* rx and tx sides can use different message sizes; * bind() should set rx_urb_size in that case. */ dev->hard_mtu = net->mtu + net->hard_header_len; #if 0 // dma_supported() is deeply broken on almost all architectures // possible with some EHCI controllers if (dma_supported (&udev->dev, DMA_BIT_MASK(64))) net->features |= NETIF_F_HIGHDMA; #endif netdev_attach_ops(net, &usbnet_netdev_ops); net->watchdog_timeo = TX_TIMEOUT_JIFFIES; net->ethtool_ops = &usbnet_ethtool_ops; // allow device-specific bind/init procedures // NOTE net->name still not usable ... if (info->bind) { status = info->bind (dev, udev); if (status < 0) goto out1; // heuristic: "usb%d" for links we know are two-host, // else "eth%d" when there's reasonable doubt. userspace // can rename the link if it knows better. if ((dev->driver_info->flags & FLAG_ETHER) != 0 && ((dev->driver_info->flags & FLAG_POINTTOPOINT) == 0 || (net->dev_addr [0] & 0x02) == 0)) strcpy (net->name, "eth%d"); /* WLAN devices should always be named "wlan%d" */ if ((dev->driver_info->flags & FLAG_WLAN) != 0) strcpy(net->name, "wlan%d"); /* WWAN devices should always be named "wwan%d" */ if ((dev->driver_info->flags & FLAG_WWAN) != 0) strcpy(net->name, "wwan%d"); /* maybe the remote can't receive an Ethernet MTU */ if (net->mtu > (dev->hard_mtu - net->hard_header_len)) net->mtu = dev->hard_mtu - net->hard_header_len; } else if (!info->in || !info->out) status = usbnet_get_endpoints (dev, udev); else { dev->in = usb_rcvbulkpipe (xdev, info->in); dev->out = usb_sndbulkpipe (xdev, info->out); if (!(info->flags & FLAG_NO_SETINT)) status = usb_set_interface (xdev, interface->desc.bInterfaceNumber, interface->desc.bAlternateSetting); else status = 0; } if (status >= 0 && dev->status) status = init_status (dev, udev); if (status < 0) goto out3; if (!dev->rx_urb_size) dev->rx_urb_size = dev->hard_mtu; dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); if ((dev->driver_info->flags & FLAG_WLAN) != 0) SET_NETDEV_DEVTYPE(net, &wlan_type); if ((dev->driver_info->flags & FLAG_WWAN) != 0) SET_NETDEV_DEVTYPE(net, &wwan_type); status = register_netdev (net); if (status) goto out4; netif_info(dev, probe, dev->net, "register '%s' at usb-%s-%s, %s, %pM\n", udev->dev.driver->name, xdev->bus->bus_name, xdev->devpath, dev->driver_info->description, net->dev_addr); // ok, it's ready to go. usb_set_intfdata (udev, dev); netif_device_attach (net); if (dev->driver_info->flags & FLAG_LINK_INTR) netif_carrier_off(net); return 0; out4: usb_free_urb(dev->interrupt); out3: if (info->unbind) info->unbind (dev, udev); out1: free_netdev(net); out: return status; } EXPORT_SYMBOL_GPL(usbnet_probe); /*-------------------------------------------------------------------------*/ /* * suspend the whole driver as soon as the first interface is suspended * resume only when the last interface is resumed */ int usbnet_suspend (struct usb_interface *intf, pm_message_t message) { struct usbnet *dev = usb_get_intfdata(intf); if (!dev->suspend_count++) { spin_lock_irq(&dev->txq.lock); /* don't autosuspend while transmitting */ if (dev->txq.qlen && PMSG_IS_AUTO(message)) { dev->suspend_count--; spin_unlock_irq(&dev->txq.lock); return -EBUSY; } else { set_bit(EVENT_DEV_ASLEEP, &dev->flags); spin_unlock_irq(&dev->txq.lock); } /* * accelerate emptying of the rx and queues, to avoid * having everything error out. */ netif_device_detach (dev->net); usbnet_terminate_urbs(dev); usb_kill_urb(dev->interrupt); /* * reattach so runtime management can use and * wake the device */ netif_device_attach (dev->net); } return 0; } EXPORT_SYMBOL_GPL(usbnet_suspend); int usbnet_resume (struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); struct sk_buff *skb; struct urb *res; int retval; if (!--dev->suspend_count) { /* resume interrupt URBs */ if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags)) usb_submit_urb(dev->interrupt, GFP_NOIO); spin_lock_irq(&dev->txq.lock); while ((res = usb_get_from_anchor(&dev->deferred))) { skb = (struct sk_buff *)res->context; retval = usb_submit_urb(res, GFP_ATOMIC); if (retval < 0) { dev_kfree_skb_any(skb); usb_free_urb(res); usb_autopm_put_interface_async(dev->intf); } else { dev->net->trans_start = jiffies; __skb_queue_tail(&dev->txq, skb); } } smp_mb(); clear_bit(EVENT_DEV_ASLEEP, &dev->flags); spin_unlock_irq(&dev->txq.lock); if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { /* handle remote wakeup ASAP */ if (!dev->wait && netif_device_present(dev->net) && !timer_pending(&dev->delay) && !test_bit(EVENT_RX_HALT, &dev->flags)) rx_alloc_submit(dev, GFP_NOIO); if (!(dev->txq.qlen >= TX_QLEN(dev))) netif_tx_wake_all_queues(dev->net); tasklet_schedule (&dev->bh); } } return 0; } EXPORT_SYMBOL_GPL(usbnet_resume); /*-------------------------------------------------------------------------*/ static int __init usbnet_init(void) { /* Compiler should optimize this out. */ BUILD_BUG_ON( FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data)); eth_random_addr(node_id); return 0; } module_init(usbnet_init); static void __exit usbnet_exit(void) { } module_exit(usbnet_exit); MODULE_AUTHOR("David Brownell"); MODULE_DESCRIPTION("USB network driver framework"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/drivers/misc/0000755000175000017500000000000012026211315016771 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/misc/eeprom/0000755000175000017500000000000012026211315020260 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/misc/eeprom/Makefile0000644000175000017500000000005512026211315021720 0ustar mcgrofmcgrofobj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o compat-drivers-2012-09-18/drivers/misc/eeprom/eeprom_93cx6.c0000644000175000017500000001653612026211315022662 0ustar mcgrofmcgrof/* * Copyright (C) 2004 - 2006 rt2x00 SourceForge Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * Module: eeprom_93cx6 * Abstract: EEPROM reader routines for 93cx6 chipsets. * Supported chipsets: 93c46 & 93c66. */ #include #include #include #include MODULE_AUTHOR("http://rt2x00.serialmonkey.com"); MODULE_VERSION("1.0"); MODULE_DESCRIPTION("EEPROM 93cx6 chip driver"); MODULE_LICENSE("GPL"); static inline void eeprom_93cx6_pulse_high(struct eeprom_93cx6 *eeprom) { eeprom->reg_data_clock = 1; eeprom->register_write(eeprom); /* * Add a short delay for the pulse to work. * According to the specifications the "maximum minimum" * time should be 450ns. */ ndelay(450); } static inline void eeprom_93cx6_pulse_low(struct eeprom_93cx6 *eeprom) { eeprom->reg_data_clock = 0; eeprom->register_write(eeprom); /* * Add a short delay for the pulse to work. * According to the specifications the "maximum minimum" * time should be 450ns. */ ndelay(450); } static void eeprom_93cx6_startup(struct eeprom_93cx6 *eeprom) { /* * Clear all flags, and enable chip select. */ eeprom->register_read(eeprom); eeprom->reg_data_in = 0; eeprom->reg_data_out = 0; eeprom->reg_data_clock = 0; eeprom->reg_chip_select = 1; eeprom->drive_data = 1; eeprom->register_write(eeprom); /* * kick a pulse. */ eeprom_93cx6_pulse_high(eeprom); eeprom_93cx6_pulse_low(eeprom); } static void eeprom_93cx6_cleanup(struct eeprom_93cx6 *eeprom) { /* * Clear chip_select and data_in flags. */ eeprom->register_read(eeprom); eeprom->reg_data_in = 0; eeprom->reg_chip_select = 0; eeprom->register_write(eeprom); /* * kick a pulse. */ eeprom_93cx6_pulse_high(eeprom); eeprom_93cx6_pulse_low(eeprom); } static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom, const u16 data, const u16 count) { unsigned int i; eeprom->register_read(eeprom); /* * Clear data flags. */ eeprom->reg_data_in = 0; eeprom->reg_data_out = 0; eeprom->drive_data = 1; /* * Start writing all bits. */ for (i = count; i > 0; i--) { /* * Check if this bit needs to be set. */ eeprom->reg_data_in = !!(data & (1 << (i - 1))); /* * Write the bit to the eeprom register. */ eeprom->register_write(eeprom); /* * Kick a pulse. */ eeprom_93cx6_pulse_high(eeprom); eeprom_93cx6_pulse_low(eeprom); } eeprom->reg_data_in = 0; eeprom->register_write(eeprom); } static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom, u16 *data, const u16 count) { unsigned int i; u16 buf = 0; eeprom->register_read(eeprom); /* * Clear data flags. */ eeprom->reg_data_in = 0; eeprom->reg_data_out = 0; eeprom->drive_data = 0; /* * Start reading all bits. */ for (i = count; i > 0; i--) { eeprom_93cx6_pulse_high(eeprom); eeprom->register_read(eeprom); /* * Clear data_in flag. */ eeprom->reg_data_in = 0; /* * Read if the bit has been set. */ if (eeprom->reg_data_out) buf |= (1 << (i - 1)); eeprom_93cx6_pulse_low(eeprom); } *data = buf; } /** * eeprom_93cx6_read - Read multiple words from eeprom * @eeprom: Pointer to eeprom structure * @word: Word index from where we should start reading * @data: target pointer where the information will have to be stored * * This function will read the eeprom data as host-endian word * into the given data pointer. */ void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, const u8 word, u16 *data) { u16 command; /* * Initialize the eeprom register */ eeprom_93cx6_startup(eeprom); /* * Select the read opcode and the word to be read. */ command = (PCI_EEPROM_READ_OPCODE << eeprom->width) | word; eeprom_93cx6_write_bits(eeprom, command, PCI_EEPROM_WIDTH_OPCODE + eeprom->width); /* * Read the requested 16 bits. */ eeprom_93cx6_read_bits(eeprom, data, 16); /* * Cleanup eeprom register. */ eeprom_93cx6_cleanup(eeprom); } EXPORT_SYMBOL_GPL(eeprom_93cx6_read); /** * eeprom_93cx6_multiread - Read multiple words from eeprom * @eeprom: Pointer to eeprom structure * @word: Word index from where we should start reading * @data: target pointer where the information will have to be stored * @words: Number of words that should be read. * * This function will read all requested words from the eeprom, * this is done by calling eeprom_93cx6_read() multiple times. * But with the additional change that while the eeprom_93cx6_read * will return host ordered bytes, this method will return little * endian words. */ void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word, __le16 *data, const u16 words) { unsigned int i; u16 tmp; for (i = 0; i < words; i++) { tmp = 0; eeprom_93cx6_read(eeprom, word + i, &tmp); data[i] = cpu_to_le16(tmp); } } EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread); /** * eeprom_93cx6_wren - set the write enable state * @eeprom: Pointer to eeprom structure * @enable: true to enable writes, otherwise disable writes * * Set the EEPROM write enable state to either allow or deny * writes depending on the @enable value. */ void eeprom_93cx6_wren(struct eeprom_93cx6 *eeprom, bool enable) { u16 command; /* start the command */ eeprom_93cx6_startup(eeprom); /* create command to enable/disable */ command = enable ? PCI_EEPROM_EWEN_OPCODE : PCI_EEPROM_EWDS_OPCODE; command <<= (eeprom->width - 2); eeprom_93cx6_write_bits(eeprom, command, PCI_EEPROM_WIDTH_OPCODE + eeprom->width); eeprom_93cx6_cleanup(eeprom); } EXPORT_SYMBOL_GPL(eeprom_93cx6_wren); /** * eeprom_93cx6_write - write data to the EEPROM * @eeprom: Pointer to eeprom structure * @addr: Address to write data to. * @data: The data to write to address @addr. * * Write the @data to the specified @addr in the EEPROM and * waiting for the device to finish writing. * * Note, since we do not expect large number of write operations * we delay in between parts of the operation to avoid using excessive * amounts of CPU time busy waiting. */ void eeprom_93cx6_write(struct eeprom_93cx6 *eeprom, u8 addr, u16 data) { int timeout = 100; u16 command; /* start the command */ eeprom_93cx6_startup(eeprom); command = PCI_EEPROM_WRITE_OPCODE << eeprom->width; command |= addr; /* send write command */ eeprom_93cx6_write_bits(eeprom, command, PCI_EEPROM_WIDTH_OPCODE + eeprom->width); /* send data */ eeprom_93cx6_write_bits(eeprom, data, 16); /* get ready to check for busy */ eeprom->drive_data = 0; eeprom->reg_chip_select = 1; eeprom->register_write(eeprom); /* wait at-least 250ns to get DO to be the busy signal */ usleep_range(1000, 2000); /* wait for DO to go high to signify finish */ while (true) { eeprom->register_read(eeprom); if (eeprom->reg_data_out) break; usleep_range(1000, 2000); if (--timeout <= 0) { printk(KERN_ERR "%s: timeout\n", __func__); break; } } eeprom_93cx6_cleanup(eeprom); } EXPORT_SYMBOL_GPL(eeprom_93cx6_write); compat-drivers-2012-09-18/drivers/bcma/0000755000175000017500000000000012026211315016740 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/drivers/bcma/bcma_private.h0000644000175000017500000000512212026211315021545 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_PRIVATE_H_ #define LINUX_BCMA_PRIVATE_H_ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #define BCMA_CORE_SIZE 0x1000 #define bcma_err(bus, fmt, ...) \ pr_err("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) #define bcma_warn(bus, fmt, ...) \ pr_warn("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) #define bcma_info(bus, fmt, ...) \ pr_info("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) #define bcma_debug(bus, fmt, ...) \ pr_debug("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) struct bcma_bus; /* main.c */ int __devinit bcma_bus_register(struct bcma_bus *bus); void bcma_bus_unregister(struct bcma_bus *bus); int __init bcma_bus_early_register(struct bcma_bus *bus, struct bcma_device *core_cc, struct bcma_device *core_mips); #ifdef CONFIG_PM int bcma_bus_suspend(struct bcma_bus *bus); int bcma_bus_resume(struct bcma_bus *bus); #endif /* scan.c */ int bcma_bus_scan(struct bcma_bus *bus); int __init bcma_bus_scan_early(struct bcma_bus *bus, struct bcma_device_id *match, struct bcma_device *core); void bcma_init_bus(struct bcma_bus *bus); /* sprom.c */ int bcma_sprom_get(struct bcma_bus *bus); /* driver_chipcommon.c */ #ifdef CONFIG_BCMA_DRIVER_MIPS void bcma_chipco_serial_init(struct bcma_drv_cc *cc); #endif /* CONFIG_BCMA_DRIVER_MIPS */ /* driver_chipcommon_pmu.c */ u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc); u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc); #ifdef CONFIG_BCMA_SFLASH /* driver_chipcommon_sflash.c */ int bcma_sflash_init(struct bcma_drv_cc *cc); extern struct platform_device bcma_sflash_dev; #else static inline int bcma_sflash_init(struct bcma_drv_cc *cc) { bcma_err(cc->core->bus, "Serial flash not supported\n"); return 0; } #endif /* CONFIG_BCMA_SFLASH */ #ifdef CONFIG_BCMA_NFLASH /* driver_chipcommon_nflash.c */ int bcma_nflash_init(struct bcma_drv_cc *cc); extern struct platform_device bcma_nflash_dev; #else static inline int bcma_nflash_init(struct bcma_drv_cc *cc) { bcma_err(cc->core->bus, "NAND flash not supported\n"); return 0; } #endif /* CONFIG_BCMA_NFLASH */ #ifdef CONFIG_BCMA_HOST_PCI /* host_pci.c */ extern int __init bcma_host_pci_init(void); extern void __exit bcma_host_pci_exit(void); #endif /* CONFIG_BCMA_HOST_PCI */ /* driver_pci.c */ u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc); void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc); #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ #endif compat-drivers-2012-09-18/drivers/bcma/host_pci.c0000644000175000017500000001707312026211315020724 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * PCI Host * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include #include #include static void bcma_host_pci_switch_core(struct bcma_device *core) { pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN, core->addr); pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2, core->wrap); core->bus->mapped_core = core; bcma_debug(core->bus, "Switched to core: 0x%X\n", core->id.id); } /* Provides access to the requested core. Returns base offset that has to be * used. It makes use of fixed windows when possible. */ static u16 bcma_host_pci_provide_access_to_core(struct bcma_device *core) { switch (core->id.id) { case BCMA_CORE_CHIPCOMMON: return 3 * BCMA_CORE_SIZE; case BCMA_CORE_PCIE: return 2 * BCMA_CORE_SIZE; } if (core->bus->mapped_core != core) bcma_host_pci_switch_core(core); return 0; } static u8 bcma_host_pci_read8(struct bcma_device *core, u16 offset) { offset += bcma_host_pci_provide_access_to_core(core); return ioread8(core->bus->mmio + offset); } static u16 bcma_host_pci_read16(struct bcma_device *core, u16 offset) { offset += bcma_host_pci_provide_access_to_core(core); return ioread16(core->bus->mmio + offset); } static u32 bcma_host_pci_read32(struct bcma_device *core, u16 offset) { offset += bcma_host_pci_provide_access_to_core(core); return ioread32(core->bus->mmio + offset); } static void bcma_host_pci_write8(struct bcma_device *core, u16 offset, u8 value) { offset += bcma_host_pci_provide_access_to_core(core); iowrite8(value, core->bus->mmio + offset); } static void bcma_host_pci_write16(struct bcma_device *core, u16 offset, u16 value) { offset += bcma_host_pci_provide_access_to_core(core); iowrite16(value, core->bus->mmio + offset); } static void bcma_host_pci_write32(struct bcma_device *core, u16 offset, u32 value) { offset += bcma_host_pci_provide_access_to_core(core); iowrite32(value, core->bus->mmio + offset); } #ifdef CONFIG_BCMA_BLOCKIO static void bcma_host_pci_block_read(struct bcma_device *core, void *buffer, size_t count, u16 offset, u8 reg_width) { void __iomem *addr = core->bus->mmio + offset; if (core->bus->mapped_core != core) bcma_host_pci_switch_core(core); switch (reg_width) { case sizeof(u8): ioread8_rep(addr, buffer, count); break; case sizeof(u16): WARN_ON(count & 1); ioread16_rep(addr, buffer, count >> 1); break; case sizeof(u32): WARN_ON(count & 3); ioread32_rep(addr, buffer, count >> 2); break; default: WARN_ON(1); } } static void bcma_host_pci_block_write(struct bcma_device *core, const void *buffer, size_t count, u16 offset, u8 reg_width) { void __iomem *addr = core->bus->mmio + offset; if (core->bus->mapped_core != core) bcma_host_pci_switch_core(core); switch (reg_width) { case sizeof(u8): iowrite8_rep(addr, buffer, count); break; case sizeof(u16): WARN_ON(count & 1); iowrite16_rep(addr, buffer, count >> 1); break; case sizeof(u32): WARN_ON(count & 3); iowrite32_rep(addr, buffer, count >> 2); break; default: WARN_ON(1); } } #endif static u32 bcma_host_pci_aread32(struct bcma_device *core, u16 offset) { if (core->bus->mapped_core != core) bcma_host_pci_switch_core(core); return ioread32(core->bus->mmio + (1 * BCMA_CORE_SIZE) + offset); } static void bcma_host_pci_awrite32(struct bcma_device *core, u16 offset, u32 value) { if (core->bus->mapped_core != core) bcma_host_pci_switch_core(core); iowrite32(value, core->bus->mmio + (1 * BCMA_CORE_SIZE) + offset); } static const struct bcma_host_ops bcma_host_pci_ops = { .read8 = bcma_host_pci_read8, .read16 = bcma_host_pci_read16, .read32 = bcma_host_pci_read32, .write8 = bcma_host_pci_write8, .write16 = bcma_host_pci_write16, .write32 = bcma_host_pci_write32, #ifdef CONFIG_BCMA_BLOCKIO .block_read = bcma_host_pci_block_read, .block_write = bcma_host_pci_block_write, #endif .aread32 = bcma_host_pci_aread32, .awrite32 = bcma_host_pci_awrite32, }; static int __devinit bcma_host_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct bcma_bus *bus; int err = -ENOMEM; const char *name; u32 val; /* Alloc */ bus = kzalloc(sizeof(*bus), GFP_KERNEL); if (!bus) goto out; /* Basic PCI configuration */ err = pci_enable_device(dev); if (err) goto err_kfree_bus; name = dev_name(&dev->dev); if (dev->driver && dev->driver->name) name = dev->driver->name; err = pci_request_regions(dev, name); if (err) goto err_pci_disable; pci_set_master(dev); /* Disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ pci_read_config_dword(dev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(dev, 0x40, val & 0xffff00ff); /* SSB needed additional powering up, do we have any AMBA PCI cards? */ if (!pci_is_pcie(dev)) bcma_err(bus, "PCI card detected, report problems.\n"); /* Map MMIO */ err = -ENOMEM; bus->mmio = pci_iomap(dev, 0, ~0UL); if (!bus->mmio) goto err_pci_release_regions; /* Host specific */ bus->host_pci = dev; bus->hosttype = BCMA_HOSTTYPE_PCI; bus->ops = &bcma_host_pci_ops; bus->boardinfo.vendor = bus->host_pci->subsystem_vendor; bus->boardinfo.type = bus->host_pci->subsystem_device; /* Register */ err = bcma_bus_register(bus); if (err) goto err_pci_unmap_mmio; pci_set_drvdata(dev, bus); out: return err; err_pci_unmap_mmio: pci_iounmap(dev, bus->mmio); err_pci_release_regions: pci_release_regions(dev); err_pci_disable: pci_disable_device(dev); err_kfree_bus: kfree(bus); return err; } static void __devexit bcma_host_pci_remove(struct pci_dev *dev) { struct bcma_bus *bus = pci_get_drvdata(dev); bcma_bus_unregister(bus); pci_iounmap(dev, bus->mmio); pci_release_regions(dev); pci_disable_device(dev); kfree(bus); pci_set_drvdata(dev, NULL); } #ifdef CONFIG_PM static int bcma_host_pci_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct bcma_bus *bus = pci_get_drvdata(pdev); bus->mapped_core = NULL; return bcma_bus_suspend(bus); } static int bcma_host_pci_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct bcma_bus *bus = pci_get_drvdata(pdev); return bcma_bus_resume(bus); } compat_pci_suspend(bcma_host_pci_suspend) compat_pci_resume(bcma_host_pci_resume) static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend, bcma_host_pci_resume); #define BCMA_PM_OPS (&bcma_pm_ops) #else /* CONFIG_PM */ #define BCMA_PM_OPS NULL #endif /* CONFIG_PM */ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, { 0, }, }; MODULE_DEVICE_TABLE(pci, bcma_pci_bridge_tbl); static struct pci_driver bcma_pci_bridge_driver = { .name = "bcma-pci-bridge", .id_table = bcma_pci_bridge_tbl, .probe = bcma_host_pci_probe, .remove = __devexit_p(bcma_host_pci_remove), #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = BCMA_PM_OPS, #elif defined(CONFIG_PM) .suspend = bcma_host_pci_suspend_compat, .resume = bcma_host_pci_resume_compat, #endif }; int __init bcma_host_pci_init(void) { return pci_register_driver(&bcma_pci_bridge_driver); } void __exit bcma_host_pci_exit(void) { pci_unregister_driver(&bcma_pci_bridge_driver); } compat-drivers-2012-09-18/drivers/bcma/sprom.c0000644000175000017500000004734212026211315020256 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * SPROM reading * * Copyright 2011, 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include #include #include #include #include static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out); /** * bcma_arch_register_fallback_sprom - Registers a method providing a * fallback SPROM if no SPROM is found. * * @sprom_callback: The callback function. * * With this function the architecture implementation may register a * callback handler which fills the SPROM data structure. The fallback is * used for PCI based BCMA devices, where no valid SPROM can be found * in the shadow registers and to provide the SPROM for SoCs where BCMA is * to controll the system bus. * * This function is useful for weird architectures that have a half-assed * BCMA device hardwired to their PCI bus. * * This function is available for architecture code, only. So it is not * exported. */ int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus, struct ssb_sprom *out)) { if (get_fallback_sprom) return -EEXIST; get_fallback_sprom = sprom_callback; return 0; } static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus, struct ssb_sprom *out) { int err; if (!get_fallback_sprom) { err = -ENOENT; goto fail; } err = get_fallback_sprom(bus, out); if (err) goto fail; bcma_debug(bus, "Using SPROM revision %d provided by platform.\n", bus->sprom.revision); return 0; fail: bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err); return err; } /************************************************** * R/W ops. **************************************************/ static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom) { int i; for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++) sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2)); } /************************************************** * Validation. **************************************************/ static inline u8 bcma_crc8(u8 crc, u8 data) { /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */ static const u8 t[] = { 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, }; return t[crc ^ data]; } static u8 bcma_sprom_crc(const u16 *sprom) { int word; u8 crc = 0xFF; for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) { crc = bcma_crc8(crc, sprom[word] & 0x00FF); crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8); } crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF); crc ^= 0xFF; return crc; } static int bcma_sprom_check_crc(const u16 *sprom) { u8 crc; u8 expected_crc; u16 tmp; crc = bcma_sprom_crc(sprom); tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC; expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; if (crc != expected_crc) return -EPROTO; return 0; } static int bcma_sprom_valid(const u16 *sprom) { u16 revision; int err; err = bcma_sprom_check_crc(sprom); if (err) return err; revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV; if (revision != 8 && revision != 9) { pr_err("Unsupported SPROM revision: %d\n", revision); return -ENOENT; } return 0; } /************************************************** * SPROM extraction. **************************************************/ #define SPOFF(offset) ((offset) / sizeof(u16)) #define SPEX(_field, _offset, _mask, _shift) \ bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift)) #define SPEX32(_field, _offset, _mask, _shift) \ bus->sprom._field = ((((u32)sprom[SPOFF((_offset)+2)] << 16 | \ sprom[SPOFF(_offset)]) & (_mask)) >> (_shift)) #define SPEX_ARRAY8(_field, _offset, _mask, _shift) \ do { \ SPEX(_field[0], _offset + 0, _mask, _shift); \ SPEX(_field[1], _offset + 2, _mask, _shift); \ SPEX(_field[2], _offset + 4, _mask, _shift); \ SPEX(_field[3], _offset + 6, _mask, _shift); \ SPEX(_field[4], _offset + 8, _mask, _shift); \ SPEX(_field[5], _offset + 10, _mask, _shift); \ SPEX(_field[6], _offset + 12, _mask, _shift); \ SPEX(_field[7], _offset + 14, _mask, _shift); \ } while (0) static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) { u16 v, o; int i; u16 pwr_info_offset[] = { SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1, SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3 }; BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != ARRAY_SIZE(bus->sprom.core_pwr_info)); bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV; for (i = 0; i < 3; i++) { v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i]; *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v); } SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0); SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0, SSB_SPROM4_TXPID2G0_SHIFT); SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1, SSB_SPROM4_TXPID2G1_SHIFT); SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2, SSB_SPROM4_TXPID2G2_SHIFT); SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3, SSB_SPROM4_TXPID2G3_SHIFT); SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0, SSB_SPROM4_TXPID5GL0_SHIFT); SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1, SSB_SPROM4_TXPID5GL1_SHIFT); SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2, SSB_SPROM4_TXPID5GL2_SHIFT); SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3, SSB_SPROM4_TXPID5GL3_SHIFT); SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0, SSB_SPROM4_TXPID5G0_SHIFT); SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1, SSB_SPROM4_TXPID5G1_SHIFT); SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2, SSB_SPROM4_TXPID5G2_SHIFT); SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3, SSB_SPROM4_TXPID5G3_SHIFT); SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0, SSB_SPROM4_TXPID5GH0_SHIFT); SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1, SSB_SPROM4_TXPID5GH1_SHIFT); SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2, SSB_SPROM4_TXPID5GH2_SHIFT); SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3, SSB_SPROM4_TXPID5GH3_SHIFT); SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0); SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0); SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0); SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0); SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8); SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0); /* Extract cores power info info */ for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { o = pwr_info_offset[i]; SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI, SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT); SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI, SSB_SPROM8_2G_MAXP, 0); SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0); SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI, SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT); SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI, SSB_SPROM8_5G_MAXP, 0); SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP, SSB_SPROM8_5GH_MAXP, 0); SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP, SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT); SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0); SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0); SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0); SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0); SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0); } SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT); SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT); SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT); SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT); SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT); SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT); SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT); SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT); SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT); SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT); SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A, SSB_SPROM8_ANTAVAIL_A_SHIFT); SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG, SSB_SPROM8_ANTAVAIL_BG_SHIFT); SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0); SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG, SSB_SPROM8_ITSSI_BG_SHIFT); SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0); SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A, SSB_SPROM8_ITSSI_A_SHIFT); SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0); SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK, SSB_SPROM8_MAXP_AL_SHIFT); SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0); SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1, SSB_SPROM8_GPIOA_P1_SHIFT); SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0); SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3, SSB_SPROM8_GPIOB_P3_SHIFT); SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0); SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G, SSB_SPROM8_TRI5G_SHIFT); SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0); SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH, SSB_SPROM8_TRI5GH_SHIFT); SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, SSB_SPROM8_RXPO2G_SHIFT); SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G, SSB_SPROM8_RXPO5G_SHIFT); SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0); SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G, SSB_SPROM8_RSSISMC2G_SHIFT); SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G, SSB_SPROM8_RSSISAV2G_SHIFT); SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G, SSB_SPROM8_BXA2G_SHIFT); SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0); SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G, SSB_SPROM8_RSSISMC5G_SHIFT); SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G, SSB_SPROM8_RSSISAV5G_SHIFT); SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G, SSB_SPROM8_BXA5G_SHIFT); SPEX(pa0b0, SSB_SPROM8_PA0B0, ~0, 0); SPEX(pa0b1, SSB_SPROM8_PA0B1, ~0, 0); SPEX(pa0b2, SSB_SPROM8_PA0B2, ~0, 0); SPEX(pa1b0, SSB_SPROM8_PA1B0, ~0, 0); SPEX(pa1b1, SSB_SPROM8_PA1B1, ~0, 0); SPEX(pa1b2, SSB_SPROM8_PA1B2, ~0, 0); SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, ~0, 0); SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, ~0, 0); SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, ~0, 0); SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, ~0, 0); SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, ~0, 0); SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, ~0, 0); SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, ~0, 0); SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, ~0, 0); SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, ~0, 0); SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, ~0, 0); SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0); /* Extract the antenna gain values. */ SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01, SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT); SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01, SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT); SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23, SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT); SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23, SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT); SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON, SSB_SPROM8_LEDDC_ON_SHIFT); SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF, SSB_SPROM8_LEDDC_OFF_SHIFT); SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN, SSB_SPROM8_TXRXC_TXCHAIN_SHIFT); SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN, SSB_SPROM8_TXRXC_RXCHAIN_SHIFT); SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH, SSB_SPROM8_TXRXC_SWITCH_SHIFT); SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0); SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0); SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0); SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0); SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0); SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP, SSB_SPROM8_RAWTS_RAWTEMP_SHIFT); SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER, SSB_SPROM8_RAWTS_MEASPOWER_SHIFT); SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMP_SLOPE, SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT); SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT); SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMP_OPTION, SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT); SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR, SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT); SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP, SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT); SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT); SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0); SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0); SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0); SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0); SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH, SSB_SPROM8_THERMAL_TRESH_SHIFT); SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET, SSB_SPROM8_THERMAL_OFFSET_SHIFT); SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PHYCAL, SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT); SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD, SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT); SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_HYSTERESIS, SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT); } /* * Indicates the presence of external SPROM. */ static bool bcma_sprom_ext_available(struct bcma_bus *bus) { u32 chip_status; u32 srom_control; u32 present_mask; if (bus->drv_cc.core->id.rev >= 31) { if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) return false; srom_control = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); return srom_control & BCMA_CC_SROM_CONTROL_PRESENT; } /* older chipcommon revisions use chip status register */ chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4313: present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT; break; case BCMA_CHIP_ID_BCM4331: present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT; break; default: return true; } return chip_status & present_mask; } /* * Indicates that on-chip OTP memory is present and enabled. */ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) { u32 chip_status; u32 otpsize = 0; bool present; chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4313: present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT; break; case BCMA_CHIP_ID_BCM4331: present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; break; case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43225: /* for these chips OTP is always available */ present = true; break; case BCMA_CHIP_ID_BCM43228: present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT; break; default: present = false; break; } if (present) { otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS; otpsize >>= BCMA_CC_CAP_OTPS_SHIFT; } return otpsize != 0; } /* * Verify OTP is filled and determine the byte * offset where SPROM data is located. * * On error, returns 0; byte offset otherwise. */ static int bcma_sprom_onchip_offset(struct bcma_bus *bus) { struct bcma_device *cc = bus->drv_cc.core; u32 offset; /* verify OTP status */ if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0) return 0; /* obtain bit offset from otplayout register */ offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET); return BCMA_CC_SPROM + (offset >> 3); } int bcma_sprom_get(struct bcma_bus *bus) { u16 offset = BCMA_CC_SPROM; u16 *sprom; int err = 0; if (!bus->drv_cc.core) return -EOPNOTSUPP; if (!bcma_sprom_ext_available(bus)) { bool sprom_onchip; /* * External SPROM takes precedence so check * on-chip OTP only when no external SPROM * is present. */ sprom_onchip = bcma_sprom_onchip_available(bus); if (sprom_onchip) { /* determine offset */ offset = bcma_sprom_onchip_offset(bus); } if (!offset || !sprom_onchip) { /* * Maybe there is no SPROM on the device? * Now we ask the arch code if there is some sprom * available for this device in some other storage. */ err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); return err; } } sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), GFP_KERNEL); if (!sprom) return -ENOMEM; if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); bcma_debug(bus, "SPROM offset 0x%x\n", offset); bcma_sprom_read(bus, offset, sprom); if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); err = bcma_sprom_valid(sprom); if (err) goto out; bcma_sprom_extract_r8(bus, sprom); out: kfree(sprom); return err; } compat-drivers-2012-09-18/drivers/bcma/scan.h0000644000175000017500000000324012026211315020034 0ustar mcgrofmcgrof#ifndef BCMA_SCAN_H_ #define BCMA_SCAN_H_ #define BCMA_ADDR_BASE 0x18000000 #define BCMA_WRAP_BASE 0x18100000 #define SCAN_ER_VALID 0x00000001 #define SCAN_ER_TAGX 0x00000006 /* we have to ignore 0x8 bit when checking tag for SCAN_ER_TAG_ADDR */ #define SCAN_ER_TAG 0x0000000E #define SCAN_ER_TAG_CI 0x00000000 #define SCAN_ER_TAG_MP 0x00000002 #define SCAN_ER_TAG_ADDR 0x00000004 #define SCAN_ER_TAG_END 0x0000000E #define SCAN_ER_BAD 0xFFFFFFFF #define SCAN_CIA_CLASS 0x000000F0 #define SCAN_CIA_CLASS_SHIFT 4 #define SCAN_CIA_ID 0x000FFF00 #define SCAN_CIA_ID_SHIFT 8 #define SCAN_CIA_MANUF 0xFFF00000 #define SCAN_CIA_MANUF_SHIFT 20 #define SCAN_CIB_NMP 0x000001F0 #define SCAN_CIB_NMP_SHIFT 4 #define SCAN_CIB_NSP 0x00003E00 #define SCAN_CIB_NSP_SHIFT 9 #define SCAN_CIB_NMW 0x0007C000 #define SCAN_CIB_NMW_SHIFT 14 #define SCAN_CIB_NSW 0x00F80000 #define SCAN_CIB_NSW_SHIFT 19 #define SCAN_CIB_REV 0xFF000000 #define SCAN_CIB_REV_SHIFT 24 #define SCAN_ADDR_AG32 0x00000008 #define SCAN_ADDR_SZ 0x00000030 #define SCAN_ADDR_SZ_SHIFT 4 #define SCAN_ADDR_SZ_4K 0x00000000 #define SCAN_ADDR_SZ_8K 0x00000010 #define SCAN_ADDR_SZ_16K 0x00000020 #define SCAN_ADDR_SZ_SZD 0x00000030 #define SCAN_ADDR_TYPE 0x000000C0 #define SCAN_ADDR_TYPE_SLAVE 0x00000000 #define SCAN_ADDR_TYPE_BRIDGE 0x00000040 #define SCAN_ADDR_TYPE_SWRAP 0x00000080 #define SCAN_ADDR_TYPE_MWRAP 0x000000C0 #define SCAN_ADDR_PORT 0x00000F00 #define SCAN_ADDR_PORT_SHIFT 8 #define SCAN_ADDR_ADDR 0xFFFFF000 #define SCAN_ADDR_SZ_BASE 0x00001000 /* 4KB */ #define SCAN_SIZE_SZ_ALIGN 0x00000FFF #define SCAN_SIZE_SZ 0xFFFFF000 #define SCAN_SIZE_SG32 0x00000008 #endif /* BCMA_SCAN_H_ */ compat-drivers-2012-09-18/drivers/bcma/scan.c0000644000175000017500000003422512026211315020036 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * Bus scanning * * Licensed under the GNU/GPL. See COPYING for details. */ #include "scan.h" #include "bcma_private.h" #include #include #include #include #include #include struct bcma_device_id_name { u16 id; const char *name; }; static const struct bcma_device_id_name bcma_arm_device_names[] = { { BCMA_CORE_4706_MAC_GBIT_COMMON, "BCM4706 GBit MAC Common" }, { BCMA_CORE_ARM_1176, "ARM 1176" }, { BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" }, { BCMA_CORE_ARM_CM3, "ARM CM3" }, }; static const struct bcma_device_id_name bcma_bcm_device_names[] = { { BCMA_CORE_OOB_ROUTER, "OOB Router" }, { BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" }, { BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" }, { BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" }, { BCMA_CORE_AMEMC, "AMEMC (DDR)" }, { BCMA_CORE_ALTA, "ALTA (I2S)" }, { BCMA_CORE_INVALID, "Invalid" }, { BCMA_CORE_CHIPCOMMON, "ChipCommon" }, { BCMA_CORE_ILINE20, "ILine 20" }, { BCMA_CORE_SRAM, "SRAM" }, { BCMA_CORE_SDRAM, "SDRAM" }, { BCMA_CORE_PCI, "PCI" }, { BCMA_CORE_ETHERNET, "Fast Ethernet" }, { BCMA_CORE_V90, "V90" }, { BCMA_CORE_USB11_HOSTDEV, "USB 1.1 Hostdev" }, { BCMA_CORE_ADSL, "ADSL" }, { BCMA_CORE_ILINE100, "ILine 100" }, { BCMA_CORE_IPSEC, "IPSEC" }, { BCMA_CORE_UTOPIA, "UTOPIA" }, { BCMA_CORE_PCMCIA, "PCMCIA" }, { BCMA_CORE_INTERNAL_MEM, "Internal Memory" }, { BCMA_CORE_MEMC_SDRAM, "MEMC SDRAM" }, { BCMA_CORE_OFDM, "OFDM" }, { BCMA_CORE_EXTIF, "EXTIF" }, { BCMA_CORE_80211, "IEEE 802.11" }, { BCMA_CORE_PHY_A, "PHY A" }, { BCMA_CORE_PHY_B, "PHY B" }, { BCMA_CORE_PHY_G, "PHY G" }, { BCMA_CORE_USB11_HOST, "USB 1.1 Host" }, { BCMA_CORE_USB11_DEV, "USB 1.1 Device" }, { BCMA_CORE_USB20_HOST, "USB 2.0 Host" }, { BCMA_CORE_USB20_DEV, "USB 2.0 Device" }, { BCMA_CORE_SDIO_HOST, "SDIO Host" }, { BCMA_CORE_ROBOSWITCH, "Roboswitch" }, { BCMA_CORE_PARA_ATA, "PATA" }, { BCMA_CORE_SATA_XORDMA, "SATA XOR-DMA" }, { BCMA_CORE_ETHERNET_GBIT, "GBit Ethernet" }, { BCMA_CORE_PCIE, "PCIe" }, { BCMA_CORE_PHY_N, "PHY N" }, { BCMA_CORE_SRAM_CTL, "SRAM Controller" }, { BCMA_CORE_MINI_MACPHY, "Mini MACPHY" }, { BCMA_CORE_PHY_LP, "PHY LP" }, { BCMA_CORE_PMU, "PMU" }, { BCMA_CORE_PHY_SSN, "PHY SSN" }, { BCMA_CORE_SDIO_DEV, "SDIO Device" }, { BCMA_CORE_PHY_HT, "PHY HT" }, { BCMA_CORE_MAC_GBIT, "GBit MAC" }, { BCMA_CORE_DDR12_MEM_CTL, "DDR1/DDR2 Memory Controller" }, { BCMA_CORE_PCIE_RC, "PCIe Root Complex" }, { BCMA_CORE_OCP_OCP_BRIDGE, "OCP to OCP Bridge" }, { BCMA_CORE_SHARED_COMMON, "Common Shared" }, { BCMA_CORE_OCP_AHB_BRIDGE, "OCP to AHB Bridge" }, { BCMA_CORE_SPI_HOST, "SPI Host" }, { BCMA_CORE_I2S, "I2S" }, { BCMA_CORE_SDR_DDR1_MEM_CTL, "SDR/DDR1 Memory Controller" }, { BCMA_CORE_SHIM, "SHIM" }, { BCMA_CORE_DEFAULT, "Default" }, }; static const struct bcma_device_id_name bcma_mips_device_names[] = { { BCMA_CORE_MIPS, "MIPS" }, { BCMA_CORE_MIPS_3302, "MIPS 3302" }, { BCMA_CORE_MIPS_74K, "MIPS 74K" }, }; static const char *bcma_device_name(const struct bcma_device_id *id) { const struct bcma_device_id_name *names; int size, i; /* search manufacturer specific names */ switch (id->manuf) { case BCMA_MANUF_ARM: names = bcma_arm_device_names; size = ARRAY_SIZE(bcma_arm_device_names); break; case BCMA_MANUF_BCM: names = bcma_bcm_device_names; size = ARRAY_SIZE(bcma_bcm_device_names); break; case BCMA_MANUF_MIPS: names = bcma_mips_device_names; size = ARRAY_SIZE(bcma_mips_device_names); break; default: return "UNKNOWN"; } for (i = 0; i < size; i++) { if (names[i].id == id->id) return names[i].name; } return "UNKNOWN"; } static u32 bcma_scan_read32(struct bcma_bus *bus, u8 current_coreidx, u16 offset) { return readl(bus->mmio + offset); } static void bcma_scan_switch_core(struct bcma_bus *bus, u32 addr) { if (bus->hosttype == BCMA_HOSTTYPE_PCI) pci_write_config_dword(bus->host_pci, BCMA_PCI_BAR0_WIN, addr); } static u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 **eromptr) { u32 ent = readl(*eromptr); (*eromptr)++; return ent; } static void bcma_erom_push_ent(u32 **eromptr) { (*eromptr)--; } static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 **eromptr) { u32 ent = bcma_erom_get_ent(bus, eromptr); if (!(ent & SCAN_ER_VALID)) return -ENOENT; if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_CI) return -ENOENT; return ent; } static bool bcma_erom_is_end(struct bcma_bus *bus, u32 **eromptr) { u32 ent = bcma_erom_get_ent(bus, eromptr); bcma_erom_push_ent(eromptr); return (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID)); } static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 **eromptr) { u32 ent = bcma_erom_get_ent(bus, eromptr); bcma_erom_push_ent(eromptr); return (((ent & SCAN_ER_VALID)) && ((ent & SCAN_ER_TAGX) == SCAN_ER_TAG_ADDR) && ((ent & SCAN_ADDR_TYPE) == SCAN_ADDR_TYPE_BRIDGE)); } static void bcma_erom_skip_component(struct bcma_bus *bus, u32 **eromptr) { u32 ent; while (1) { ent = bcma_erom_get_ent(bus, eromptr); if ((ent & SCAN_ER_VALID) && ((ent & SCAN_ER_TAG) == SCAN_ER_TAG_CI)) break; if (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID)) break; } bcma_erom_push_ent(eromptr); } static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 **eromptr) { u32 ent = bcma_erom_get_ent(bus, eromptr); if (!(ent & SCAN_ER_VALID)) return -ENOENT; if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_MP) return -ENOENT; return ent; } static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 **eromptr, u32 type, u8 port) { u32 addrl, addrh, sizel, sizeh = 0; u32 size; u32 ent = bcma_erom_get_ent(bus, eromptr); if ((!(ent & SCAN_ER_VALID)) || ((ent & SCAN_ER_TAGX) != SCAN_ER_TAG_ADDR) || ((ent & SCAN_ADDR_TYPE) != type) || (((ent & SCAN_ADDR_PORT) >> SCAN_ADDR_PORT_SHIFT) != port)) { bcma_erom_push_ent(eromptr); return -EINVAL; } addrl = ent & SCAN_ADDR_ADDR; if (ent & SCAN_ADDR_AG32) addrh = bcma_erom_get_ent(bus, eromptr); else addrh = 0; if ((ent & SCAN_ADDR_SZ) == SCAN_ADDR_SZ_SZD) { size = bcma_erom_get_ent(bus, eromptr); sizel = size & SCAN_SIZE_SZ; if (size & SCAN_SIZE_SG32) sizeh = bcma_erom_get_ent(bus, eromptr); } else sizel = SCAN_ADDR_SZ_BASE << ((ent & SCAN_ADDR_SZ) >> SCAN_ADDR_SZ_SHIFT); return addrl; } static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus, u16 index) { struct bcma_device *core; list_for_each_entry(core, &bus->cores, list) { if (core->core_index == index) return core; } return NULL; } static struct bcma_device *bcma_find_core_reverse(struct bcma_bus *bus, u16 coreid) { struct bcma_device *core; list_for_each_entry_reverse(core, &bus->cores, list) { if (core->id.id == coreid) return core; } return NULL; } static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr, struct bcma_device_id *match, int core_num, struct bcma_device *core) { s32 tmp; u8 i, j; s32 cia, cib; u8 ports[2], wrappers[2]; /* get CIs */ cia = bcma_erom_get_ci(bus, eromptr); if (cia < 0) { bcma_erom_push_ent(eromptr); if (bcma_erom_is_end(bus, eromptr)) return -ESPIPE; return -EILSEQ; } cib = bcma_erom_get_ci(bus, eromptr); if (cib < 0) return -EILSEQ; /* parse CIs */ core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT; core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT; core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT; ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT; ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT; wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT; wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT; core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT; if (((core->id.manuf == BCMA_MANUF_ARM) && (core->id.id == 0xFFF)) || (ports[1] == 0)) { bcma_erom_skip_component(bus, eromptr); return -ENXIO; } /* check if component is a core at all */ if (wrappers[0] + wrappers[1] == 0) { /* Some specific cores don't need wrappers */ switch (core->id.id) { case BCMA_CORE_4706_MAC_GBIT_COMMON: /* Not used yet: case BCMA_CORE_OOB_ROUTER: */ break; default: bcma_erom_skip_component(bus, eromptr); return -ENXIO; } } if (bcma_erom_is_bridge(bus, eromptr)) { bcma_erom_skip_component(bus, eromptr); return -ENXIO; } if (bcma_find_core_by_index(bus, core_num)) { bcma_erom_skip_component(bus, eromptr); return -ENODEV; } if (match && ((match->manuf != BCMA_ANY_MANUF && match->manuf != core->id.manuf) || (match->id != BCMA_ANY_ID && match->id != core->id.id) || (match->rev != BCMA_ANY_REV && match->rev != core->id.rev) || (match->class != BCMA_ANY_CLASS && match->class != core->id.class) )) { bcma_erom_skip_component(bus, eromptr); return -ENODEV; } /* get & parse master ports */ for (i = 0; i < ports[0]; i++) { s32 mst_port_d = bcma_erom_get_mst_port(bus, eromptr); if (mst_port_d < 0) return -EILSEQ; } /* First Slave Address Descriptor should be port 0: * the main register space for the core */ tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0); if (tmp <= 0) { /* Try again to see if it is a bridge */ tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_BRIDGE, 0); if (tmp <= 0) { return -EILSEQ; } else { bcma_info(bus, "Bridge found\n"); return -ENXIO; } } core->addr = tmp; /* get & parse slave ports */ for (i = 0; i < ports[1]; i++) { for (j = 0; ; j++) { tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, i); if (tmp < 0) { /* no more entries for port _i_ */ /* pr_debug("erom: slave port %d " * "has %d descriptors\n", i, j); */ break; } else { if (i == 0 && j == 0) core->addr1 = tmp; } } } /* get & parse master wrappers */ for (i = 0; i < wrappers[0]; i++) { for (j = 0; ; j++) { tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_MWRAP, i); if (tmp < 0) { /* no more entries for port _i_ */ /* pr_debug("erom: master wrapper %d " * "has %d descriptors\n", i, j); */ break; } else { if (i == 0 && j == 0) core->wrap = tmp; } } } /* get & parse slave wrappers */ for (i = 0; i < wrappers[1]; i++) { u8 hack = (ports[1] == 1) ? 0 : 1; for (j = 0; ; j++) { tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SWRAP, i + hack); if (tmp < 0) { /* no more entries for port _i_ */ /* pr_debug("erom: master wrapper %d " * has %d descriptors\n", i, j); */ break; } else { if (wrappers[0] == 0 && !i && !j) core->wrap = tmp; } } } if (bus->hosttype == BCMA_HOSTTYPE_SOC) { core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE); if (!core->io_addr) return -ENOMEM; core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE); if (!core->io_wrap) { iounmap(core->io_addr); return -ENOMEM; } } return 0; } void bcma_init_bus(struct bcma_bus *bus) { s32 tmp; struct bcma_chipinfo *chipinfo = &(bus->chipinfo); if (bus->init_done) return; INIT_LIST_HEAD(&bus->cores); bus->nr_cores = 0; bcma_scan_switch_core(bus, BCMA_ADDR_BASE); tmp = bcma_scan_read32(bus, 0, BCMA_CC_ID); chipinfo->id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT; chipinfo->rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT; chipinfo->pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT; bcma_info(bus, "Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n", chipinfo->id, chipinfo->rev, chipinfo->pkg); bus->init_done = true; } int bcma_bus_scan(struct bcma_bus *bus) { u32 erombase; u32 __iomem *eromptr, *eromend; int err, core_num = 0; bcma_init_bus(bus); erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM); if (bus->hosttype == BCMA_HOSTTYPE_SOC) { eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE); if (!eromptr) return -ENOMEM; } else { eromptr = bus->mmio; } eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32); bcma_scan_switch_core(bus, erombase); while (eromptr < eromend) { struct bcma_device *other_core; struct bcma_device *core = kzalloc(sizeof(*core), GFP_KERNEL); if (!core) { err = -ENOMEM; goto out; } INIT_LIST_HEAD(&core->list); core->bus = bus; err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core); if (err < 0) { kfree(core); if (err == -ENODEV) { core_num++; continue; } else if (err == -ENXIO) { continue; } else if (err == -ESPIPE) { break; } goto out; } core->core_index = core_num++; bus->nr_cores++; other_core = bcma_find_core_reverse(bus, core->id.id); core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1; bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n", core->core_index, bcma_device_name(&core->id), core->id.manuf, core->id.id, core->id.rev, core->id.class); list_add_tail(&core->list, &bus->cores); } err = 0; out: if (bus->hosttype == BCMA_HOSTTYPE_SOC) iounmap(eromptr); return err; } int __init bcma_bus_scan_early(struct bcma_bus *bus, struct bcma_device_id *match, struct bcma_device *core) { u32 erombase; u32 __iomem *eromptr, *eromend; int err = -ENODEV; int core_num = 0; erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM); if (bus->hosttype == BCMA_HOSTTYPE_SOC) { eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE); if (!eromptr) return -ENOMEM; } else { eromptr = bus->mmio; } eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32); bcma_scan_switch_core(bus, erombase); while (eromptr < eromend) { memset(core, 0, sizeof(*core)); INIT_LIST_HEAD(&core->list); core->bus = bus; err = bcma_get_next_core(bus, &eromptr, match, core_num, core); if (err == -ENODEV) { core_num++; continue; } else if (err == -ENXIO) continue; else if (err == -ESPIPE) break; else if (err < 0) goto out; core->core_index = core_num++; bus->nr_cores++; bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n", core->core_index, bcma_device_name(&core->id), core->id.manuf, core->id.id, core->id.rev, core->id.class); list_add_tail(&core->list, &bus->cores); err = 0; break; } out: if (bus->hosttype == BCMA_HOSTTYPE_SOC) iounmap(eromptr); return err; } compat-drivers-2012-09-18/drivers/bcma/main.c0000644000175000017500000002400512026211315020031 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * Bus subsystem * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include #include #include MODULE_DESCRIPTION("Broadcom's specific AMBA driver"); MODULE_LICENSE("GPL"); /* contains the number the next bus should get. */ static unsigned int bcma_bus_next_num = 0; /* bcma_buses_mutex locks the bcma_bus_next_num */ static DEFINE_MUTEX(bcma_buses_mutex); static int bcma_bus_match(struct device *dev, struct device_driver *drv); static int bcma_device_probe(struct device *dev); static int bcma_device_remove(struct device *dev); static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env); static ssize_t manuf_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); return sprintf(buf, "0x%03X\n", core->id.manuf); } static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); return sprintf(buf, "0x%03X\n", core->id.id); } static ssize_t rev_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); return sprintf(buf, "0x%02X\n", core->id.rev); } static ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); return sprintf(buf, "0x%X\n", core->id.class); } static struct device_attribute bcma_device_attrs[] = { __ATTR_RO(manuf), __ATTR_RO(id), __ATTR_RO(rev), __ATTR_RO(class), __ATTR_NULL, }; static struct bus_type bcma_bus_type = { .name = "bcma", .match = bcma_bus_match, .probe = bcma_device_probe, .remove = bcma_device_remove, .uevent = bcma_device_uevent, .dev_attrs = bcma_device_attrs, }; static u16 bcma_cc_core_id(struct bcma_bus *bus) { if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) return BCMA_CORE_4706_CHIPCOMMON; return BCMA_CORE_CHIPCOMMON; } struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid) { struct bcma_device *core; list_for_each_entry(core, &bus->cores, list) { if (core->id.id == coreid) return core; } return NULL; } EXPORT_SYMBOL_GPL(bcma_find_core); static void bcma_release_core_dev(struct device *dev) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); if (core->io_addr) iounmap(core->io_addr); if (core->io_wrap) iounmap(core->io_wrap); kfree(core); } static int bcma_register_cores(struct bcma_bus *bus) { struct bcma_device *core; int err, dev_id = 0; list_for_each_entry(core, &bus->cores, list) { /* We support that cores ourself */ switch (core->id.id) { case BCMA_CORE_4706_CHIPCOMMON: case BCMA_CORE_CHIPCOMMON: case BCMA_CORE_PCI: case BCMA_CORE_PCIE: case BCMA_CORE_MIPS_74K: case BCMA_CORE_4706_MAC_GBIT_COMMON: continue; } core->dev.release = bcma_release_core_dev; core->dev.bus = &bcma_bus_type; dev_set_name(&core->dev, "bcma%d:%d", bus->num, dev_id); switch (bus->hosttype) { case BCMA_HOSTTYPE_PCI: core->dev.parent = &bus->host_pci->dev; core->dma_dev = &bus->host_pci->dev; core->irq = bus->host_pci->irq; break; case BCMA_HOSTTYPE_SOC: core->dev.dma_mask = &core->dev.coherent_dma_mask; core->dma_dev = &core->dev; break; case BCMA_HOSTTYPE_SDIO: break; } err = device_register(&core->dev); if (err) { bcma_err(bus, "Could not register dev for core 0x%03X\n", core->id.id); continue; } core->dev_registered = true; dev_id++; } #ifdef CONFIG_BCMA_SFLASH if (bus->drv_cc.sflash.present) { err = platform_device_register(&bcma_sflash_dev); if (err) bcma_err(bus, "Error registering serial flash\n"); } #endif #ifdef CONFIG_BCMA_NFLASH if (bus->drv_cc.nflash.present) { err = platform_device_register(&bcma_nflash_dev); if (err) bcma_err(bus, "Error registering NAND flash\n"); } #endif return 0; } static void bcma_unregister_cores(struct bcma_bus *bus) { struct bcma_device *core; list_for_each_entry(core, &bus->cores, list) { if (core->dev_registered) device_unregister(&core->dev); } } int __devinit bcma_bus_register(struct bcma_bus *bus) { int err; struct bcma_device *core; mutex_lock(&bcma_buses_mutex); bus->num = bcma_bus_next_num++; mutex_unlock(&bcma_buses_mutex); /* Scan for devices (cores) */ err = bcma_bus_scan(bus); if (err) { bcma_err(bus, "Failed to scan: %d\n", err); return -1; } /* Init CC core */ core = bcma_find_core(bus, bcma_cc_core_id(bus)); if (core) { bus->drv_cc.core = core; bcma_core_chipcommon_init(&bus->drv_cc); } /* Init MIPS core */ core = bcma_find_core(bus, BCMA_CORE_MIPS_74K); if (core) { bus->drv_mips.core = core; bcma_core_mips_init(&bus->drv_mips); } /* Init PCIE core */ core = bcma_find_core(bus, BCMA_CORE_PCIE); if (core) { bus->drv_pci.core = core; bcma_core_pci_init(&bus->drv_pci); } /* Init GBIT MAC COMMON core */ core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); if (core) { bus->drv_gmac_cmn.core = core; bcma_core_gmac_cmn_init(&bus->drv_gmac_cmn); } /* Try to get SPROM */ err = bcma_sprom_get(bus); if (err == -ENOENT) { bcma_err(bus, "No SPROM available\n"); } else if (err) bcma_err(bus, "Failed to get SPROM: %d\n", err); /* Register found cores */ bcma_register_cores(bus); bcma_info(bus, "Bus registered\n"); return 0; } void bcma_bus_unregister(struct bcma_bus *bus) { struct bcma_device *cores[3]; cores[0] = bcma_find_core(bus, BCMA_CORE_MIPS_74K); cores[1] = bcma_find_core(bus, BCMA_CORE_PCIE); cores[2] = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); bcma_unregister_cores(bus); kfree(cores[2]); kfree(cores[1]); kfree(cores[0]); } int __init bcma_bus_early_register(struct bcma_bus *bus, struct bcma_device *core_cc, struct bcma_device *core_mips) { int err; struct bcma_device *core; struct bcma_device_id match; bcma_init_bus(bus); match.manuf = BCMA_MANUF_BCM; match.id = bcma_cc_core_id(bus); match.class = BCMA_CL_SIM; match.rev = BCMA_ANY_REV; /* Scan for chip common core */ err = bcma_bus_scan_early(bus, &match, core_cc); if (err) { bcma_err(bus, "Failed to scan for common core: %d\n", err); return -1; } match.manuf = BCMA_MANUF_MIPS; match.id = BCMA_CORE_MIPS_74K; match.class = BCMA_CL_SIM; match.rev = BCMA_ANY_REV; /* Scan for mips core */ err = bcma_bus_scan_early(bus, &match, core_mips); if (err) { bcma_err(bus, "Failed to scan for mips core: %d\n", err); return -1; } /* Init CC core */ core = bcma_find_core(bus, bcma_cc_core_id(bus)); if (core) { bus->drv_cc.core = core; bcma_core_chipcommon_init(&bus->drv_cc); } /* Init MIPS core */ core = bcma_find_core(bus, BCMA_CORE_MIPS_74K); if (core) { bus->drv_mips.core = core; bcma_core_mips_init(&bus->drv_mips); } bcma_info(bus, "Early bus registered\n"); return 0; } #ifdef CONFIG_PM int bcma_bus_suspend(struct bcma_bus *bus) { struct bcma_device *core; list_for_each_entry(core, &bus->cores, list) { struct device_driver *drv = core->dev.driver; if (drv) { struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); if (adrv->suspend) adrv->suspend(core); } } return 0; } int bcma_bus_resume(struct bcma_bus *bus) { struct bcma_device *core; /* Init CC core */ if (bus->drv_cc.core) { bus->drv_cc.setup_done = false; bcma_core_chipcommon_init(&bus->drv_cc); } list_for_each_entry(core, &bus->cores, list) { struct device_driver *drv = core->dev.driver; if (drv) { struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); if (adrv->resume) adrv->resume(core); } } return 0; } #endif int __bcma_driver_register(struct bcma_driver *drv, struct module *owner) { drv->drv.name = drv->name; drv->drv.bus = &bcma_bus_type; drv->drv.owner = owner; return driver_register(&drv->drv); } EXPORT_SYMBOL_GPL(__bcma_driver_register); void bcma_driver_unregister(struct bcma_driver *drv) { driver_unregister(&drv->drv); } EXPORT_SYMBOL_GPL(bcma_driver_unregister); static int bcma_bus_match(struct device *dev, struct device_driver *drv) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); const struct bcma_device_id *cid = &core->id; const struct bcma_device_id *did; for (did = adrv->id_table; did->manuf || did->id || did->rev; did++) { if ((did->manuf == cid->manuf || did->manuf == BCMA_ANY_MANUF) && (did->id == cid->id || did->id == BCMA_ANY_ID) && (did->rev == cid->rev || did->rev == BCMA_ANY_REV) && (did->class == cid->class || did->class == BCMA_ANY_CLASS)) return 1; } return 0; } static int bcma_device_probe(struct device *dev) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver, drv); int err = 0; if (adrv->probe) err = adrv->probe(core); return err; } static int bcma_device_remove(struct device *dev) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver, drv); if (adrv->remove) adrv->remove(core); return 0; } static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); return add_uevent_var(env, "MODALIAS=bcma:m%04Xid%04Xrev%02Xcl%02X", core->id.manuf, core->id.id, core->id.rev, core->id.class); } static int __init bcma_modinit(void) { int err; err = bus_register(&bcma_bus_type); if (err) return err; #ifdef CONFIG_BCMA_HOST_PCI err = bcma_host_pci_init(); if (err) { pr_err("PCI host initialization failed\n"); err = 0; } #endif return err; } fs_initcall(bcma_modinit); static void __exit bcma_modexit(void) { #ifdef CONFIG_BCMA_HOST_PCI bcma_host_pci_exit(); #endif bus_unregister(&bcma_bus_type); } module_exit(bcma_modexit) compat-drivers-2012-09-18/drivers/bcma/host_soc.c0000644000175000017500000000714312026211315020732 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * System on Chip (SoC) Host * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include "scan.h" #include #include static u8 bcma_host_soc_read8(struct bcma_device *core, u16 offset) { return readb(core->io_addr + offset); } static u16 bcma_host_soc_read16(struct bcma_device *core, u16 offset) { return readw(core->io_addr + offset); } static u32 bcma_host_soc_read32(struct bcma_device *core, u16 offset) { return readl(core->io_addr + offset); } static void bcma_host_soc_write8(struct bcma_device *core, u16 offset, u8 value) { writeb(value, core->io_addr + offset); } static void bcma_host_soc_write16(struct bcma_device *core, u16 offset, u16 value) { writew(value, core->io_addr + offset); } static void bcma_host_soc_write32(struct bcma_device *core, u16 offset, u32 value) { writel(value, core->io_addr + offset); } #ifdef CONFIG_BCMA_BLOCKIO static void bcma_host_soc_block_read(struct bcma_device *core, void *buffer, size_t count, u16 offset, u8 reg_width) { void __iomem *addr = core->io_addr + offset; switch (reg_width) { case sizeof(u8): { u8 *buf = buffer; while (count) { *buf = __raw_readb(addr); buf++; count--; } break; } case sizeof(u16): { __le16 *buf = buffer; WARN_ON(count & 1); while (count) { *buf = (__force __le16)__raw_readw(addr); buf++; count -= 2; } break; } case sizeof(u32): { __le32 *buf = buffer; WARN_ON(count & 3); while (count) { *buf = (__force __le32)__raw_readl(addr); buf++; count -= 4; } break; } default: WARN_ON(1); } } static void bcma_host_soc_block_write(struct bcma_device *core, const void *buffer, size_t count, u16 offset, u8 reg_width) { void __iomem *addr = core->io_addr + offset; switch (reg_width) { case sizeof(u8): { const u8 *buf = buffer; while (count) { __raw_writeb(*buf, addr); buf++; count--; } break; } case sizeof(u16): { const __le16 *buf = buffer; WARN_ON(count & 1); while (count) { __raw_writew((__force u16)(*buf), addr); buf++; count -= 2; } break; } case sizeof(u32): { const __le32 *buf = buffer; WARN_ON(count & 3); while (count) { __raw_writel((__force u32)(*buf), addr); buf++; count -= 4; } break; } default: WARN_ON(1); } } #endif /* CONFIG_BCMA_BLOCKIO */ static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset) { return readl(core->io_wrap + offset); } static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset, u32 value) { writel(value, core->io_wrap + offset); } static const struct bcma_host_ops bcma_host_soc_ops = { .read8 = bcma_host_soc_read8, .read16 = bcma_host_soc_read16, .read32 = bcma_host_soc_read32, .write8 = bcma_host_soc_write8, .write16 = bcma_host_soc_write16, .write32 = bcma_host_soc_write32, #ifdef CONFIG_BCMA_BLOCKIO .block_read = bcma_host_soc_block_read, .block_write = bcma_host_soc_block_write, #endif .aread32 = bcma_host_soc_aread32, .awrite32 = bcma_host_soc_awrite32, }; int __init bcma_host_soc_register(struct bcma_soc *soc) { struct bcma_bus *bus = &soc->bus; int err; /* iomap only first core. We have to read some register on this core * to scan the bus. */ bus->mmio = ioremap_nocache(BCMA_ADDR_BASE, BCMA_CORE_SIZE * 1); if (!bus->mmio) return -ENOMEM; /* Host specific */ bus->hosttype = BCMA_HOSTTYPE_SOC; bus->ops = &bcma_host_soc_ops; /* Register */ err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips); if (err) iounmap(bus->mmio); return err; } compat-drivers-2012-09-18/drivers/bcma/driver_pci_host.c0000644000175000017500000004110612026211315022271 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * PCI Core in hostmode * * Copyright 2005 - 2011, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * Copyright 2011, 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include #include #include /* Probe a 32bit value on the bus and catch bus exceptions. * Returns nonzero on a bus exception. * This is MIPS specific */ #define mips_busprobe32(val, addr) get_dbe((val), ((u32 *)(addr))) /* Assume one-hot slot wiring */ #define BCMA_PCI_SLOT_MAX 16 #define PCI_CONFIG_SPACE_SIZE 256 bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) { struct bcma_bus *bus = pc->core->bus; u16 chipid_top; u32 tmp; chipid_top = (bus->chipinfo.id & 0xFF00); if (chipid_top != 0x4700 && chipid_top != 0x5300) return false; if (bus->sprom.boardflags_lo & BCMA_CORE_PCI_BFL_NOPCI) { bcma_info(bus, "This PCI core is disabled and not working\n"); return false; } bcma_core_enable(pc->core, 0); return !mips_busprobe32(tmp, pc->core->io_addr); } static u32 bcma_pcie_read_config(struct bcma_drv_pci *pc, u32 address) { pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_ADDR, address); pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_ADDR); return pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_DATA); } static void bcma_pcie_write_config(struct bcma_drv_pci *pc, u32 address, u32 data) { pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_ADDR, address); pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_ADDR); pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_DATA, data); } static u32 bcma_get_cfgspace_addr(struct bcma_drv_pci *pc, unsigned int dev, unsigned int func, unsigned int off) { u32 addr = 0; /* Issue config commands only when the data link is up (atleast * one external pcie device is present). */ if (dev >= 2 || !(bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_LSREG) & BCMA_CORE_PCI_DLLP_LSREG_LINKUP)) goto out; /* Type 0 transaction */ /* Slide the PCI window to the appropriate slot */ pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI1, BCMA_CORE_PCI_SBTOPCI_CFG0); /* Calculate the address */ addr = pc->host_controller->host_cfg_addr; addr |= (dev << BCMA_CORE_PCI_CFG_SLOT_SHIFT); addr |= (func << BCMA_CORE_PCI_CFG_FUN_SHIFT); addr |= (off & ~3); out: return addr; } static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev, unsigned int func, unsigned int off, void *buf, int len) { int err = -EINVAL; u32 addr, val; void __iomem *mmio = 0; WARN_ON(!pc->hostmode); if (unlikely(len != 1 && len != 2 && len != 4)) goto out; if (dev == 0) { /* we support only two functions on device 0 */ if (func > 1) return -EINVAL; /* accesses to config registers with offsets >= 256 * requires indirect access. */ if (off >= PCI_CONFIG_SPACE_SIZE) { addr = (func << 12); addr |= (off & 0x0FFF); val = bcma_pcie_read_config(pc, addr); } else { addr = BCMA_CORE_PCI_PCICFG0; addr |= (func << 8); addr |= (off & 0xfc); val = pcicore_read32(pc, addr); } } else { addr = bcma_get_cfgspace_addr(pc, dev, func, off); if (unlikely(!addr)) goto out; err = -ENOMEM; mmio = ioremap_nocache(addr, sizeof(val)); if (!mmio) goto out; if (mips_busprobe32(val, mmio)) { val = 0xffffffff; goto unmap; } val = readl(mmio); } val >>= (8 * (off & 3)); switch (len) { case 1: *((u8 *)buf) = (u8)val; break; case 2: *((u16 *)buf) = (u16)val; break; case 4: *((u32 *)buf) = (u32)val; break; } err = 0; unmap: if (mmio) iounmap(mmio); out: return err; } static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, unsigned int func, unsigned int off, const void *buf, int len) { int err = -EINVAL; u32 addr = 0, val = 0; void __iomem *mmio = 0; u16 chipid = pc->core->bus->chipinfo.id; WARN_ON(!pc->hostmode); if (unlikely(len != 1 && len != 2 && len != 4)) goto out; if (dev == 0) { /* accesses to config registers with offsets >= 256 * requires indirect access. */ if (off < PCI_CONFIG_SPACE_SIZE) { addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0; addr |= (func << 8); addr |= (off & 0xfc); mmio = ioremap_nocache(addr, sizeof(val)); if (!mmio) goto out; } } else { addr = bcma_get_cfgspace_addr(pc, dev, func, off); if (unlikely(!addr)) goto out; err = -ENOMEM; mmio = ioremap_nocache(addr, sizeof(val)); if (!mmio) goto out; if (mips_busprobe32(val, mmio)) { val = 0xffffffff; goto unmap; } } switch (len) { case 1: val = readl(mmio); val &= ~(0xFF << (8 * (off & 3))); val |= *((const u8 *)buf) << (8 * (off & 3)); break; case 2: val = readl(mmio); val &= ~(0xFFFF << (8 * (off & 3))); val |= *((const u16 *)buf) << (8 * (off & 3)); break; case 4: val = *((const u32 *)buf); break; } if (dev == 0 && !addr) { /* accesses to config registers with offsets >= 256 * requires indirect access. */ addr = (func << 12); addr |= (off & 0x0FFF); bcma_pcie_write_config(pc, addr, val); } else { writel(val, mmio); if (chipid == BCMA_CHIP_ID_BCM4716 || chipid == BCMA_CHIP_ID_BCM4748) readl(mmio); } err = 0; unmap: if (mmio) iounmap(mmio); out: return err; } static int bcma_core_pci_hostmode_read_config(struct pci_bus *bus, unsigned int devfn, int reg, int size, u32 *val) { unsigned long flags; int err; struct bcma_drv_pci *pc; struct bcma_drv_pci_host *pc_host; pc_host = container_of(bus->ops, struct bcma_drv_pci_host, pci_ops); pc = pc_host->pdev; spin_lock_irqsave(&pc_host->cfgspace_lock, flags); err = bcma_extpci_read_config(pc, PCI_SLOT(devfn), PCI_FUNC(devfn), reg, val, size); spin_unlock_irqrestore(&pc_host->cfgspace_lock, flags); return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; } static int bcma_core_pci_hostmode_write_config(struct pci_bus *bus, unsigned int devfn, int reg, int size, u32 val) { unsigned long flags; int err; struct bcma_drv_pci *pc; struct bcma_drv_pci_host *pc_host; pc_host = container_of(bus->ops, struct bcma_drv_pci_host, pci_ops); pc = pc_host->pdev; spin_lock_irqsave(&pc_host->cfgspace_lock, flags); err = bcma_extpci_write_config(pc, PCI_SLOT(devfn), PCI_FUNC(devfn), reg, &val, size); spin_unlock_irqrestore(&pc_host->cfgspace_lock, flags); return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; } /* return cap_offset if requested capability exists in the PCI config space */ static u8 __devinit bcma_find_pci_capability(struct bcma_drv_pci *pc, unsigned int dev, unsigned int func, u8 req_cap_id, unsigned char *buf, u32 *buflen) { u8 cap_id; u8 cap_ptr = 0; u32 bufsize; u8 byte_val; /* check for Header type 0 */ bcma_extpci_read_config(pc, dev, func, PCI_HEADER_TYPE, &byte_val, sizeof(u8)); if ((byte_val & 0x7f) != PCI_HEADER_TYPE_NORMAL) return cap_ptr; /* check if the capability pointer field exists */ bcma_extpci_read_config(pc, dev, func, PCI_STATUS, &byte_val, sizeof(u8)); if (!(byte_val & PCI_STATUS_CAP_LIST)) return cap_ptr; /* check if the capability pointer is 0x00 */ bcma_extpci_read_config(pc, dev, func, PCI_CAPABILITY_LIST, &cap_ptr, sizeof(u8)); if (cap_ptr == 0x00) return cap_ptr; /* loop thr'u the capability list and see if the requested capabilty * exists */ bcma_extpci_read_config(pc, dev, func, cap_ptr, &cap_id, sizeof(u8)); while (cap_id != req_cap_id) { bcma_extpci_read_config(pc, dev, func, cap_ptr + 1, &cap_ptr, sizeof(u8)); if (cap_ptr == 0x00) return cap_ptr; bcma_extpci_read_config(pc, dev, func, cap_ptr, &cap_id, sizeof(u8)); } /* found the caller requested capability */ if ((buf != NULL) && (buflen != NULL)) { u8 cap_data; bufsize = *buflen; if (!bufsize) return cap_ptr; *buflen = 0; /* copy the cpability data excluding cap ID and next ptr */ cap_data = cap_ptr + 2; if ((bufsize + cap_data) > PCI_CONFIG_SPACE_SIZE) bufsize = PCI_CONFIG_SPACE_SIZE - cap_data; *buflen = bufsize; while (bufsize--) { bcma_extpci_read_config(pc, dev, func, cap_data, buf, sizeof(u8)); cap_data++; buf++; } } return cap_ptr; } /* If the root port is capable of returning Config Request * Retry Status (CRS) Completion Status to software then * enable the feature. */ static void __devinit bcma_core_pci_enable_crs(struct bcma_drv_pci *pc) { struct bcma_bus *bus = pc->core->bus; u8 cap_ptr, root_ctrl, root_cap, dev; u16 val16; int i; cap_ptr = bcma_find_pci_capability(pc, 0, 0, PCI_CAP_ID_EXP, NULL, NULL); root_cap = cap_ptr + PCI_EXP_RTCAP; bcma_extpci_read_config(pc, 0, 0, root_cap, &val16, sizeof(u16)); if (val16 & BCMA_CORE_PCI_RC_CRS_VISIBILITY) { /* Enable CRS software visibility */ root_ctrl = cap_ptr + PCI_EXP_RTCTL; val16 = PCI_EXP_RTCTL_CRSSVE; bcma_extpci_read_config(pc, 0, 0, root_ctrl, &val16, sizeof(u16)); /* Initiate a configuration request to read the vendor id * field of the device function's config space header after * 100 ms wait time from the end of Reset. If the device is * not done with its internal initialization, it must at * least return a completion TLP, with a completion status * of "Configuration Request Retry Status (CRS)". The root * complex must complete the request to the host by returning * a read-data value of 0001h for the Vendor ID field and * all 1s for any additional bytes included in the request. * Poll using the config reads for max wait time of 1 sec or * until we receive the successful completion status. Repeat * the procedure for all the devices. */ for (dev = 1; dev < BCMA_PCI_SLOT_MAX; dev++) { for (i = 0; i < 100000; i++) { bcma_extpci_read_config(pc, dev, 0, PCI_VENDOR_ID, &val16, sizeof(val16)); if (val16 != 0x1) break; udelay(10); } if (val16 == 0x1) bcma_err(bus, "PCI: Broken device in slot %d\n", dev); } } } void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) { struct bcma_bus *bus = pc->core->bus; struct bcma_drv_pci_host *pc_host; u32 tmp; u32 pci_membase_1G; unsigned long io_map_base; bcma_info(bus, "PCIEcore in host mode found\n"); pc_host = kzalloc(sizeof(*pc_host), GFP_KERNEL); if (!pc_host) { bcma_err(bus, "can not allocate memory"); return; } pc->host_controller = pc_host; pc_host->pci_controller.io_resource = &pc_host->io_resource; pc_host->pci_controller.mem_resource = &pc_host->mem_resource; pc_host->pci_controller.pci_ops = &pc_host->pci_ops; pc_host->pdev = pc; pci_membase_1G = BCMA_SOC_PCI_DMA; pc_host->host_cfg_addr = BCMA_SOC_PCI_CFG; pc_host->pci_ops.read = bcma_core_pci_hostmode_read_config; pc_host->pci_ops.write = bcma_core_pci_hostmode_write_config; pc_host->mem_resource.name = "BCMA PCIcore external memory", pc_host->mem_resource.start = BCMA_SOC_PCI_DMA; pc_host->mem_resource.end = BCMA_SOC_PCI_DMA + BCMA_SOC_PCI_DMA_SZ - 1; pc_host->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; pc_host->io_resource.name = "BCMA PCIcore external I/O", pc_host->io_resource.start = 0x100; pc_host->io_resource.end = 0x7FF; pc_host->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED; /* Reset RC */ udelay(3000); pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST_OE); udelay(1000); pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST | BCMA_CORE_PCI_CTL_RST_OE); /* 64 MB I/O access window. On 4716, use * sbtopcie0 to access the device registers. We * can't use address match 2 (1 GB window) region * as mips can't generate 64-bit address on the * backplane. */ if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4716 || bus->chipinfo.id == BCMA_CHIP_ID_BCM4748) { pc_host->mem_resource.start = BCMA_SOC_PCI_MEM; pc_host->mem_resource.end = BCMA_SOC_PCI_MEM + BCMA_SOC_PCI_MEM_SZ - 1; pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, BCMA_CORE_PCI_SBTOPCI_MEM | BCMA_SOC_PCI_MEM); } else if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { tmp = BCMA_CORE_PCI_SBTOPCI_MEM; tmp |= BCMA_CORE_PCI_SBTOPCI_PREF; tmp |= BCMA_CORE_PCI_SBTOPCI_BURST; if (pc->core->core_unit == 0) { pc_host->mem_resource.start = BCMA_SOC_PCI_MEM; pc_host->mem_resource.end = BCMA_SOC_PCI_MEM + BCMA_SOC_PCI_MEM_SZ - 1; pci_membase_1G = BCMA_SOC_PCIE_DMA_H32; pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, tmp | BCMA_SOC_PCI_MEM); } else if (pc->core->core_unit == 1) { pc_host->mem_resource.start = BCMA_SOC_PCI1_MEM; pc_host->mem_resource.end = BCMA_SOC_PCI1_MEM + BCMA_SOC_PCI_MEM_SZ - 1; pci_membase_1G = BCMA_SOC_PCIE1_DMA_H32; pc_host->host_cfg_addr = BCMA_SOC_PCI1_CFG; pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, tmp | BCMA_SOC_PCI1_MEM); } } else pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, BCMA_CORE_PCI_SBTOPCI_IO); /* 64 MB configuration access window */ pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI1, BCMA_CORE_PCI_SBTOPCI_CFG0); /* 1 GB memory access window */ pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI2, BCMA_CORE_PCI_SBTOPCI_MEM | pci_membase_1G); /* As per PCI Express Base Spec 1.1 we need to wait for * at least 100 ms from the end of a reset (cold/warm/hot) * before issuing configuration requests to PCI Express * devices. */ udelay(100000); bcma_core_pci_enable_crs(pc); /* Enable PCI bridge BAR0 memory & master access */ tmp = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; bcma_extpci_write_config(pc, 0, 0, PCI_COMMAND, &tmp, sizeof(tmp)); /* Enable PCI interrupts */ pcicore_write32(pc, BCMA_CORE_PCI_IMASK, BCMA_CORE_PCI_IMASK_INTA); /* Ok, ready to run, register it to the system. * The following needs change, if we want to port hostmode * to non-MIPS platform. */ io_map_base = (unsigned long)ioremap_nocache(pc_host->mem_resource.start, resource_size(&pc_host->mem_resource)); pc_host->pci_controller.io_map_base = io_map_base; set_io_port_base(pc_host->pci_controller.io_map_base); /* Give some time to the PCI controller to configure itself with the new * values. Not waiting at this point causes crashes of the machine. */ mdelay(10); register_pci_controller(&pc_host->pci_controller); return; } /* Early PCI fixup for a device on the PCI-core bridge. */ static void bcma_core_pci_fixup_pcibridge(struct pci_dev *dev) { if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { /* This is not a device on the PCI-core bridge. */ return; } if (PCI_SLOT(dev->devfn) != 0) return; pr_info("PCI: Fixing up bridge %s\n", pci_name(dev)); /* Enable PCI bridge bus mastering and memory space */ pci_set_master(dev); if (pcibios_enable_device(dev, ~0) < 0) { pr_err("PCI: BCMA bridge enable failed\n"); return; } /* Enable PCI bridge BAR1 prefetch and burst */ pci_write_config_dword(dev, BCMA_PCI_BAR1_CONTROL, 3); } DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, bcma_core_pci_fixup_pcibridge); /* Early PCI fixup for all PCI-cores to set the correct memory address. */ static void bcma_core_pci_fixup_addresses(struct pci_dev *dev) { struct resource *res; int pos; if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { /* This is not a device on the PCI-core bridge. */ return; } if (PCI_SLOT(dev->devfn) == 0) return; pr_info("PCI: Fixing up addresses %s\n", pci_name(dev)); for (pos = 0; pos < 6; pos++) { res = &dev->resource[pos]; if (res->flags & (IORESOURCE_IO | IORESOURCE_MEM)) pci_assign_resource(dev, pos); } } DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, bcma_core_pci_fixup_addresses); /* This function is called when doing a pci_enable_device(). * We must first check if the device is a device on the PCI-core bridge. */ int bcma_core_pci_plat_dev_init(struct pci_dev *dev) { struct bcma_drv_pci_host *pc_host; if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { /* This is not a device on the PCI-core bridge. */ return -ENODEV; } pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host, pci_ops); pr_info("PCI: Fixing up device %s\n", pci_name(dev)); /* Fix up interrupt lines */ dev->irq = bcma_core_mips_irq(pc_host->pdev->core) + 2; pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); return 0; } EXPORT_SYMBOL(bcma_core_pci_plat_dev_init); /* PCI device IRQ mapping. */ int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev) { struct bcma_drv_pci_host *pc_host; if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { /* This is not a device on the PCI-core bridge. */ return -ENODEV; } pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host, pci_ops); return bcma_core_mips_irq(pc_host->pdev->core) + 2; } EXPORT_SYMBOL(bcma_core_pci_pcibios_map_irq); compat-drivers-2012-09-18/drivers/bcma/driver_pci.c0000644000175000017500000001676312026211315021247 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * PCI Core * * Copyright 2005, 2011, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * Copyright 2011, 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include /************************************************** * R/W ops. **************************************************/ u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address) { pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA); } static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data) { pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data); } static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) { u32 v; int i; v = BCMA_CORE_PCI_MDIODATA_START; v |= BCMA_CORE_PCI_MDIODATA_WRITE; v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); v |= BCMA_CORE_PCI_MDIODATA_TA; v |= (phy << 4); pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); udelay(10); for (i = 0; i < 200; i++) { v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) break; msleep(1); } } static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address) { int max_retries = 10; u16 ret = 0; u32 v; int i; /* enable mdio access to SERDES */ v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); if (pc->core->id.rev >= 10) { max_retries = 200; bcma_pcie_mdio_set_phy(pc, device); v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); } else { v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } v = BCMA_CORE_PCI_MDIODATA_START; v |= BCMA_CORE_PCI_MDIODATA_READ; v |= BCMA_CORE_PCI_MDIODATA_TA; pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) { udelay(10); ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA); break; } msleep(1); } pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); return ret; } static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, u8 address, u16 data) { int max_retries = 10; u32 v; int i; /* enable mdio access to SERDES */ v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); if (pc->core->id.rev >= 10) { max_retries = 200; bcma_pcie_mdio_set_phy(pc, device); v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); } else { v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } v = BCMA_CORE_PCI_MDIODATA_START; v |= BCMA_CORE_PCI_MDIODATA_WRITE; v |= BCMA_CORE_PCI_MDIODATA_TA; v |= data; pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) break; msleep(1); } pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); } /************************************************** * Workarounds. **************************************************/ static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc) { u32 tmp; tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG); if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT) return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE | BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY; else return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE; } static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc) { u16 tmp; bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX, BCMA_CORE_PCI_SERDES_RX_CTRL, bcma_pcicore_polarity_workaround(pc)); tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, BCMA_CORE_PCI_SERDES_PLL_CTRL); if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN) bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, BCMA_CORE_PCI_SERDES_PLL_CTRL, tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN); } static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc) { struct bcma_device *core = pc->core; u16 val16, core_index; uint regoff; regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET); core_index = (u16)core->core_index; val16 = pcicore_read16(pc, regoff); if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT) != core_index) { val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) | (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK); pcicore_write16(pc, regoff, val16); } } /* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */ /* Needs to happen when coming out of 'standby'/'hibernate' */ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc) { u16 val16; uint regoff; regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG); val16 = pcicore_read16(pc, regoff); if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) { val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST; pcicore_write16(pc, regoff, val16); } } /************************************************** * Init. **************************************************/ static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) { bcma_core_pci_fixcfg(pc); bcma_pcicore_serdes_workaround(pc); bcma_core_pci_config_fixup(pc); } void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc) { if (pc->setup_done) return; #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE pc->hostmode = bcma_core_pci_is_in_hostmode(pc); if (pc->hostmode) bcma_core_pci_hostmode_init(pc); #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ if (!pc->hostmode) bcma_core_pci_clientmode_init(pc); } int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable) { struct pci_dev *pdev; u32 coremask, tmp; int err = 0; if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) { /* This bcma device is not on a PCI host-bus. So the IRQs are * not routed through the PCI core. * So we must not enable routing through the PCI core. */ goto out; } pdev = pc->core->bus->host_pci; err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); if (err) goto out; coremask = BIT(core->core_index) << 8; if (enable) tmp |= coremask; else tmp &= ~coremask; err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp); out: return err; } EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl); void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) { u32 w; w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); if (extend) w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND; else w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND; bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w); bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); } EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer); compat-drivers-2012-09-18/drivers/bcma/driver_mips.c0000644000175000017500000001507412026211315021436 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * Broadcom MIPS32 74K core driver * * Copyright 2009, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * Copyright 2010, Bernhard Loos * Copyright 2011, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include #include #include #include /* The 47162a0 hangs when reading MIPS DMP registers registers */ static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev) { return dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM47162 && dev->bus->chipinfo.rev == 0 && dev->id.id == BCMA_CORE_MIPS_74K; } /* The 5357b0 hangs when reading USB20H DMP registers */ static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev) { return (dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) && dev->bus->chipinfo.pkg == 11 && dev->id.id == BCMA_CORE_USB20_HOST; } static inline u32 mips_read32(struct bcma_drv_mips *mcore, u16 offset) { return bcma_read32(mcore->core, offset); } static inline void mips_write32(struct bcma_drv_mips *mcore, u16 offset, u32 value) { bcma_write32(mcore->core, offset, value); } static const u32 ipsflag_irq_mask[] = { 0, BCMA_MIPS_IPSFLAG_IRQ1, BCMA_MIPS_IPSFLAG_IRQ2, BCMA_MIPS_IPSFLAG_IRQ3, BCMA_MIPS_IPSFLAG_IRQ4, }; static const u32 ipsflag_irq_shift[] = { 0, BCMA_MIPS_IPSFLAG_IRQ1_SHIFT, BCMA_MIPS_IPSFLAG_IRQ2_SHIFT, BCMA_MIPS_IPSFLAG_IRQ3_SHIFT, BCMA_MIPS_IPSFLAG_IRQ4_SHIFT, }; static u32 bcma_core_mips_irqflag(struct bcma_device *dev) { u32 flag; if (bcma_core_mips_bcm47162a0_quirk(dev)) return dev->core_index; if (bcma_core_mips_bcm5357b0_quirk(dev)) return dev->core_index; flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30); return flag & 0x1F; } /* Get the MIPS IRQ assignment for a specified device. * If unassigned, 0 is returned. */ unsigned int bcma_core_mips_irq(struct bcma_device *dev) { struct bcma_device *mdev = dev->bus->drv_mips.core; u32 irqflag; unsigned int irq; irqflag = bcma_core_mips_irqflag(dev); for (irq = 1; irq <= 4; irq++) if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) & (1 << irqflag)) return irq; return 0; } EXPORT_SYMBOL(bcma_core_mips_irq); static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) { unsigned int oldirq = bcma_core_mips_irq(dev); struct bcma_bus *bus = dev->bus; struct bcma_device *mdev = bus->drv_mips.core; u32 irqflag; irqflag = bcma_core_mips_irqflag(dev); BUG_ON(oldirq == 6); dev->irq = irq + 2; /* clear the old irq */ if (oldirq == 0) bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0), bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) & ~(1 << irqflag)); else bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), 0); /* assign the new one */ if (irq == 0) { bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0), bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) | (1 << irqflag)); } else { u32 oldirqflag = bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)); if (oldirqflag) { struct bcma_device *core; /* backplane irq line is in use, find out who uses * it and set user to irq 0 */ list_for_each_entry(core, &bus->cores, list) { if ((1 << bcma_core_mips_irqflag(core)) == oldirqflag) { bcma_core_mips_set_irq(core, 0); break; } } } bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), 1 << irqflag); } bcma_info(bus, "set_irq: core 0x%04x, irq %d => %d\n", dev->id.id, oldirq + 2, irq + 2); } static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq) { int i; static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"}; printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id); for (i = 0; i <= 6; i++) printk(" %s%s", irq_name[i], i == irq ? "*" : " "); printk("\n"); } static void bcma_core_mips_dump_irq(struct bcma_bus *bus) { struct bcma_device *core; list_for_each_entry(core, &bus->cores, list) { bcma_core_mips_print_irq(core, bcma_core_mips_irq(core)); } } u32 bcma_cpu_clock(struct bcma_drv_mips *mcore) { struct bcma_bus *bus = mcore->core->bus; if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU) return bcma_pmu_get_clockcpu(&bus->drv_cc); bcma_err(bus, "No PMU available, need this to get the cpu clock\n"); return 0; } EXPORT_SYMBOL(bcma_cpu_clock); static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) { struct bcma_bus *bus = mcore->core->bus; switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: case BCMA_CC_FLASHT_ATSER: bcma_debug(bus, "Found serial flash\n"); bcma_sflash_init(&bus->drv_cc); break; case BCMA_CC_FLASHT_PARA: bcma_debug(bus, "Found parallel flash\n"); bus->drv_cc.pflash.window = 0x1c000000; bus->drv_cc.pflash.window_size = 0x02000000; if ((bcma_read32(bus->drv_cc.core, BCMA_CC_FLASH_CFG) & BCMA_CC_FLASH_CFG_DS) == 0) bus->drv_cc.pflash.buswidth = 1; else bus->drv_cc.pflash.buswidth = 2; break; default: bcma_err(bus, "Flash type not supported\n"); } if (bus->drv_cc.core->id.rev == 38 || bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { if (bus->drv_cc.capabilities & BCMA_CC_CAP_NFLASH) { bcma_debug(bus, "Found NAND flash\n"); bcma_nflash_init(&bus->drv_cc); } } } void bcma_core_mips_init(struct bcma_drv_mips *mcore) { struct bcma_bus *bus; struct bcma_device *core; bus = mcore->core->bus; bcma_info(bus, "Initializing MIPS core...\n"); if (!mcore->setup_done) mcore->assigned_irqs = 1; /* Assign IRQs to all cores on the bus */ list_for_each_entry(core, &bus->cores, list) { int mips_irq; if (core->irq) continue; mips_irq = bcma_core_mips_irq(core); if (mips_irq > 4) core->irq = 0; else core->irq = mips_irq + 2; if (core->irq > 5) continue; switch (core->id.id) { case BCMA_CORE_PCI: case BCMA_CORE_PCIE: case BCMA_CORE_ETHERNET: case BCMA_CORE_ETHERNET_GBIT: case BCMA_CORE_MAC_GBIT: case BCMA_CORE_80211: case BCMA_CORE_USB20_HOST: /* These devices get their own IRQ line if available, * the rest goes on IRQ0 */ if (mcore->assigned_irqs <= 4) bcma_core_mips_set_irq(core, mcore->assigned_irqs++); break; } } bcma_info(bus, "IRQ reconfiguration done\n"); bcma_core_mips_dump_irq(bus); if (mcore->setup_done) return; bcma_chipco_serial_init(&bus->drv_cc); bcma_core_mips_flash_detect(mcore); mcore->setup_done = true; } compat-drivers-2012-09-18/drivers/bcma/driver_gmac_cmn.c0000644000175000017500000000042312026211315022222 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * GBIT MAC COMMON Core * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include void __devinit bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc) { mutex_init(&gc->phy_mutex); } compat-drivers-2012-09-18/drivers/bcma/driver_chipcommon_sflash.c0000644000175000017500000000625112026211315024157 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * ChipCommon serial flash interface * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include "bcma_private.h" static struct resource bcma_sflash_resource = { .name = "bcma_sflash", .start = BCMA_SFLASH, .end = 0, .flags = IORESOURCE_MEM | IORESOURCE_READONLY, }; struct platform_device bcma_sflash_dev = { .name = "bcma_sflash", .resource = &bcma_sflash_resource, .num_resources = 1, }; struct bcma_sflash_tbl_e { char *name; u32 id; u32 blocksize; u16 numblocks; }; static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = { { "", 0x14, 0x10000, 32, }, { 0 }, }; static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = { { 0 }, }; static struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = { { 0 }, }; static void bcma_sflash_cmd(struct bcma_drv_cc *cc, u32 opcode) { int i; bcma_cc_write32(cc, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode); for (i = 0; i < 1000; i++) { if (!(bcma_cc_read32(cc, BCMA_CC_FLASHCTL) & BCMA_CC_FLASHCTL_BUSY)) return; cpu_relax(); } bcma_err(cc->core->bus, "SFLASH control command failed (timeout)!\n"); } /* Initialize serial flash access */ int bcma_sflash_init(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; struct bcma_sflash *sflash = &cc->sflash; struct bcma_sflash_tbl_e *e; u32 id, id2; switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_DP); bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 0); bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 1); bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); id2 = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); switch (id) { case 0xbf: for (e = bcma_sflash_sst_tbl; e->name; e++) { if (e->id == id2) break; } break; default: for (e = bcma_sflash_st_tbl; e->name; e++) { if (e->id == id) break; } break; } if (!e->name) { bcma_err(bus, "Unsupported ST serial flash (id: 0x%X, id2: 0x%X)\n", id, id2); return -ENOTSUPP; } break; case BCMA_CC_FLASHT_ATSER: bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS); id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA) & 0x3c; for (e = bcma_sflash_at_tbl; e->name; e++) { if (e->id == id) break; } if (!e->name) { bcma_err(bus, "Unsupported Atmel serial flash (id: 0x%X)\n", id); return -ENOTSUPP; } break; default: bcma_err(bus, "Unsupported flash type\n"); return -ENOTSUPP; } sflash->window = BCMA_SFLASH; sflash->blocksize = e->blocksize; sflash->numblocks = e->numblocks; sflash->size = sflash->blocksize * sflash->numblocks; sflash->present = true; bcma_info(bus, "Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n", e->name, sflash->size / 1024, sflash->blocksize, sflash->numblocks); /* Prepare platform device, but don't register it yet. It's too early, * malloc (required by device_private_init) is not available yet. */ bcma_sflash_dev.resource[0].end = bcma_sflash_dev.resource[0].start + sflash->size; bcma_sflash_dev.dev.platform_data = sflash; return 0; } compat-drivers-2012-09-18/drivers/bcma/driver_chipcommon_pmu.c0000644000175000017500000003602112026211315023476 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * ChipCommon Power Management Unit driver * * Copyright 2009, Michael Buesch * Copyright 2007, 2011, Broadcom Corporation * Copyright 2011, 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset) { bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); } void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value) { bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); } EXPORT_SYMBOL_GPL(bcma_chipco_pll_write); void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set) { bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set); } EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset); void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set) { bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR); bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set); } EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset); void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set) { bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR); bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set); } EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset); static void bcma_pmu_resources_init(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; u32 min_msk = 0, max_msk = 0; switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4313: min_msk = 0x200D; max_msk = 0xFFFF; break; default: bcma_debug(bus, "PMU resource config unknown or not needed for device 0x%04X\n", bus->chipinfo.id); } /* Set the resource masks. */ if (min_msk) bcma_cc_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk); if (max_msk) bcma_cc_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk); /* Add some delay; allow resources to come up and settle. */ mdelay(2); } /* Disable to allow reading SPROM. Don't know the adventages of enabling it. */ void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable) { struct bcma_bus *bus = cc->core->bus; u32 val; val = bcma_cc_read32(cc, BCMA_CC_CHIPCTL); if (enable) { val |= BCMA_CHIPCTL_4331_EXTPA_EN; if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11) val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5; else if (bus->chipinfo.rev > 0) val |= BCMA_CHIPCTL_4331_EXTPA_EN2; } else { val &= ~BCMA_CHIPCTL_4331_EXTPA_EN; val &= ~BCMA_CHIPCTL_4331_EXTPA_EN2; val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5; } bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val); } static void bcma_pmu_workarounds(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4313: /* enable 12 mA drive strenth for 4313 and set chipControl register bit 1 */ bcma_chipco_chipctl_maskset(cc, 0, ~BCMA_CCTRL_4313_12MA_LED_DRIVE, BCMA_CCTRL_4313_12MA_LED_DRIVE); break; case BCMA_CHIP_ID_BCM4331: case BCMA_CHIP_ID_BCM43431: /* Ext PA lines must be enabled for tx on BCM4331 */ bcma_chipco_bcm4331_ext_pa_lines_ctl(cc, true); break; case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43421: /* enable 12 mA drive strenth for 43224 and set chipControl register bit 15 */ if (bus->chipinfo.rev == 0) { bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL, ~BCMA_CCTRL_43224_GPIO_TOGGLE, BCMA_CCTRL_43224_GPIO_TOGGLE); bcma_chipco_chipctl_maskset(cc, 0, ~BCMA_CCTRL_43224A0_12MA_LED_DRIVE, BCMA_CCTRL_43224A0_12MA_LED_DRIVE); } else { bcma_chipco_chipctl_maskset(cc, 0, ~BCMA_CCTRL_43224B0_12MA_LED_DRIVE, BCMA_CCTRL_43224B0_12MA_LED_DRIVE); } break; default: bcma_debug(bus, "Workarounds unknown or not needed for device 0x%04X\n", bus->chipinfo.id); } } void bcma_pmu_init(struct bcma_drv_cc *cc) { u32 pmucap; pmucap = bcma_cc_read32(cc, BCMA_CC_PMU_CAP); cc->pmu.rev = (pmucap & BCMA_CC_PMU_CAP_REVISION); bcma_debug(cc->core->bus, "Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev, pmucap); if (cc->pmu.rev == 1) bcma_cc_mask32(cc, BCMA_CC_PMU_CTL, ~BCMA_CC_PMU_CTL_NOILPONW); else bcma_cc_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_NOILPONW); bcma_pmu_resources_init(cc); bcma_pmu_workarounds(cc); } u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4716: case BCMA_CHIP_ID_BCM4748: case BCMA_CHIP_ID_BCM47162: case BCMA_CHIP_ID_BCM4313: case BCMA_CHIP_ID_BCM5357: case BCMA_CHIP_ID_BCM4749: case BCMA_CHIP_ID_BCM53572: /* always 20Mhz */ return 20000 * 1000; case BCMA_CHIP_ID_BCM5356: case BCMA_CHIP_ID_BCM4706: /* always 25Mhz */ return 25000 * 1000; default: bcma_warn(bus, "No ALP clock specified for %04X device, pmu rev. %d, using default %d Hz\n", bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK); } return BCMA_CC_PMU_ALP_CLOCK; } /* Find the output of the "m" pll divider given pll controls that start with * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc. */ static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m) { u32 tmp, div, ndiv, p1, p2, fc; struct bcma_bus *bus = cc->core->bus; BUG_ON((pll0 & 3) || (pll0 > BCMA_CC_PMU4716_MAINPLL_PLL0)); BUG_ON(!m || m > 4); if (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) { /* Detect failure in clock setting */ tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT); if (tmp & 0x40000) return 133 * 1000000; } tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_P1P2_OFF); p1 = (tmp & BCMA_CC_PPL_P1_MASK) >> BCMA_CC_PPL_P1_SHIFT; p2 = (tmp & BCMA_CC_PPL_P2_MASK) >> BCMA_CC_PPL_P2_SHIFT; tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_M14_OFF); div = (tmp >> ((m - 1) * BCMA_CC_PPL_MDIV_WIDTH)) & BCMA_CC_PPL_MDIV_MASK; tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_NM5_OFF); ndiv = (tmp & BCMA_CC_PPL_NDIV_MASK) >> BCMA_CC_PPL_NDIV_SHIFT; /* Do calculation in Mhz */ fc = bcma_pmu_alp_clock(cc) / 1000000; fc = (p1 * ndiv * fc) / p2; /* Return clock in Hertz */ return (fc / div) * 1000000; } static u32 bcma_pmu_clock_bcm4706(struct bcma_drv_cc *cc, u32 pll0, u32 m) { u32 tmp, ndiv, p1div, p2div; u32 clock; BUG_ON(!m || m > 4); /* Get N, P1 and P2 dividers to determine CPU clock */ tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PMU6_4706_PROCPLL_OFF); ndiv = (tmp & BCMA_CC_PMU6_4706_PROC_NDIV_INT_MASK) >> BCMA_CC_PMU6_4706_PROC_NDIV_INT_SHIFT; p1div = (tmp & BCMA_CC_PMU6_4706_PROC_P1DIV_MASK) >> BCMA_CC_PMU6_4706_PROC_P1DIV_SHIFT; p2div = (tmp & BCMA_CC_PMU6_4706_PROC_P2DIV_MASK) >> BCMA_CC_PMU6_4706_PROC_P2DIV_SHIFT; tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT); if (tmp & BCMA_CC_CHIPST_4706_PKG_OPTION) /* Low cost bonding: Fixed reference clock 25MHz and m = 4 */ clock = (25000000 / 4) * ndiv * p2div / p1div; else /* Fixed reference clock 25MHz and m = 2 */ clock = (25000000 / 2) * ndiv * p2div / p1div; if (m == BCMA_CC_PMU5_MAINPLL_SSB) clock = clock / 4; return clock; } /* query bus clock frequency for PMU-enabled chipcommon */ static u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4716: case BCMA_CHIP_ID_BCM4748: case BCMA_CHIP_ID_BCM47162: return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); case BCMA_CHIP_ID_BCM5356: return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); case BCMA_CHIP_ID_BCM5357: case BCMA_CHIP_ID_BCM4749: return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); case BCMA_CHIP_ID_BCM4706: return bcma_pmu_clock_bcm4706(cc, BCMA_CC_PMU4706_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); case BCMA_CHIP_ID_BCM53572: return 75000000; default: bcma_warn(bus, "No backplane clock specified for %04X device, pmu rev. %d, using default %d Hz\n", bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK); } return BCMA_CC_PMU_HT_CLOCK; } /* query cpu clock frequency for PMU-enabled chipcommon */ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; if (bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) return 300000000; if (cc->pmu.rev >= 5) { u32 pll; switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4706: return bcma_pmu_clock_bcm4706(cc, BCMA_CC_PMU4706_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_CPU); case BCMA_CHIP_ID_BCM5356: pll = BCMA_CC_PMU5356_MAINPLL_PLL0; break; case BCMA_CHIP_ID_BCM5357: case BCMA_CHIP_ID_BCM4749: pll = BCMA_CC_PMU5357_MAINPLL_PLL0; break; default: pll = BCMA_CC_PMU4716_MAINPLL_PLL0; break; } return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU); } return bcma_pmu_get_clockcontrol(cc); } static void bcma_pmu_spuravoid_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value) { bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); } void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid) { u32 tmp = 0; u8 phypll_offset = 0; u8 bcm5357_bcm43236_p1div[] = {0x1, 0x5, 0x5}; u8 bcm5357_bcm43236_ndiv[] = {0x30, 0xf6, 0xfc}; struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM5357: case BCMA_CHIP_ID_BCM4749: case BCMA_CHIP_ID_BCM53572: /* 5357[ab]0, 43236[ab]0, and 6362b0 */ /* BCM5357 needs to touch PLL1_PLLCTL[02], so offset PLL0_PLLCTL[02] by 6 */ phypll_offset = (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || bus->chipinfo.id == BCMA_CHIP_ID_BCM4749 || bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) ? 6 : 0; /* RMW only the P1 divider */ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, BCMA_CC_PMU_PLL_CTL0 + phypll_offset); tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); tmp &= (~(BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK)); tmp |= (bcm5357_bcm43236_p1div[spuravoid] << BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT); bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp); /* RMW only the int feedback divider */ bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, BCMA_CC_PMU_PLL_CTL2 + phypll_offset); tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); tmp &= ~(BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK); tmp |= (bcm5357_bcm43236_ndiv[spuravoid]) << BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT; bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp); tmp = 1 << 10; break; case BCMA_CHIP_ID_BCM4331: case BCMA_CHIP_ID_BCM43431: if (spuravoid == 2) { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11500014); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0FC00a08); } else if (spuravoid == 1) { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11500014); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0F600a08); } else { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11100014); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x03000a08); } tmp = 1 << 10; break; case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43225: case BCMA_CHIP_ID_BCM43421: if (spuravoid == 1) { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11500010); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, 0x000C0C06); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0F600a08); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x00000000); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, 0x2001E920); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, 0x88888815); } else { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11100010); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, 0x000c0c06); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x03000a08); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x00000000); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, 0x200005c0); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, 0x88888815); } tmp = 1 << 10; break; case BCMA_CHIP_ID_BCM4716: case BCMA_CHIP_ID_BCM4748: case BCMA_CHIP_ID_BCM47162: if (spuravoid == 1) { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11500060); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, 0x080C0C06); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x0F600000); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x00000000); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, 0x2001E924); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, 0x88888815); } else { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11100060); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, 0x080c0c06); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x03000000); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x00000000); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, 0x200005c0); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, 0x88888815); } tmp = 3 << 9; break; case BCMA_CHIP_ID_BCM43227: case BCMA_CHIP_ID_BCM43228: case BCMA_CHIP_ID_BCM43428: /* LCNXN */ /* PLL Settings for spur avoidance on/off mode, no on2 support for 43228A0 */ if (spuravoid == 1) { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x01100014); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, 0x040C0C06); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x03140A08); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x00333333); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, 0x202C2820); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, 0x88888815); } else { bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, 0x11100014); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, 0x040c0c06); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, 0x03000a08); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, 0x00000000); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, 0x200005c0); bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, 0x88888815); } tmp = 1 << 10; break; default: bcma_err(bus, "Unknown spuravoidance settings for chip 0x%04X, not changing PLL\n", bus->chipinfo.id); break; } tmp |= bcma_cc_read32(cc, BCMA_CC_PMU_CTL); bcma_cc_write32(cc, BCMA_CC_PMU_CTL, tmp); } EXPORT_SYMBOL_GPL(bcma_pmu_spuravoid_pllupdate); compat-drivers-2012-09-18/drivers/bcma/driver_chipcommon_nflash.c0000644000175000017500000000171312026211315024150 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * ChipCommon NAND flash interface * * Licensed under the GNU/GPL. See COPYING for details. */ #include #include #include "bcma_private.h" struct platform_device bcma_nflash_dev = { .name = "bcma_nflash", .num_resources = 0, }; /* Initialize NAND flash access */ int bcma_nflash_init(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4706 && cc->core->id.rev != 0x38) { bcma_err(bus, "NAND flash on unsupported board!\n"); return -ENOTSUPP; } if (!(cc->capabilities & BCMA_CC_CAP_NFLASH)) { bcma_err(bus, "NAND flash not present according to ChipCommon\n"); return -ENODEV; } cc->nflash.present = true; /* Prepare platform device, but don't register it yet. It's too early, * malloc (required by device_private_init) is not available yet. */ bcma_nflash_dev.dev.platform_data = &cc->nflash; return 0; } compat-drivers-2012-09-18/drivers/bcma/driver_chipcommon.c0000644000175000017500000001034112026211315022612 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * ChipCommon core driver * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset, u32 mask, u32 value) { value &= mask; value |= bcma_cc_read32(cc, offset) & ~mask; bcma_cc_write32(cc, offset, value); return value; } void bcma_core_chipcommon_init(struct bcma_drv_cc *cc) { u32 leddc_on = 10; u32 leddc_off = 90; if (cc->setup_done) return; if (cc->core->id.rev >= 11) cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT); cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP); if (cc->core->id.rev >= 35) cc->capabilities_ext = bcma_cc_read32(cc, BCMA_CC_CAP_EXT); if (cc->core->id.rev >= 20) { bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, 0); bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, 0); } if (cc->capabilities & BCMA_CC_CAP_PMU) bcma_pmu_init(cc); if (cc->capabilities & BCMA_CC_CAP_PCTL) bcma_err(cc->core->bus, "Power control not implemented!\n"); if (cc->core->id.rev >= 16) { if (cc->core->bus->sprom.leddc_on_time && cc->core->bus->sprom.leddc_off_time) { leddc_on = cc->core->bus->sprom.leddc_on_time; leddc_off = cc->core->bus->sprom.leddc_off_time; } bcma_cc_write32(cc, BCMA_CC_GPIOTIMER, ((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) | (leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT))); } cc->setup_done = true; } /* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */ void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks) { /* instant NMI */ bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks); } void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value) { bcma_cc_write32_masked(cc, BCMA_CC_IRQMASK, mask, value); } u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask) { return bcma_cc_read32(cc, BCMA_CC_IRQSTAT) & mask; } u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask) { return bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask; } u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value) { return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value); } u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value) { return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value); } u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value) { return bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value); } EXPORT_SYMBOL_GPL(bcma_chipco_gpio_control); u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value) { return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value); } u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value) { return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value); } #ifdef CONFIG_BCMA_DRIVER_MIPS void bcma_chipco_serial_init(struct bcma_drv_cc *cc) { unsigned int irq; u32 baud_base; u32 i; unsigned int ccrev = cc->core->id.rev; struct bcma_serial_port *ports = cc->serial_ports; if (ccrev >= 11 && ccrev != 15) { /* Fixed ALP clock */ baud_base = bcma_pmu_alp_clock(cc); if (ccrev >= 21) { /* Turn off UART clock before switching clocksource. */ bcma_cc_write32(cc, BCMA_CC_CORECTL, bcma_cc_read32(cc, BCMA_CC_CORECTL) & ~BCMA_CC_CORECTL_UARTCLKEN); } /* Set the override bit so we don't divide it */ bcma_cc_write32(cc, BCMA_CC_CORECTL, bcma_cc_read32(cc, BCMA_CC_CORECTL) | BCMA_CC_CORECTL_UARTCLK0); if (ccrev >= 21) { /* Re-enable the UART clock. */ bcma_cc_write32(cc, BCMA_CC_CORECTL, bcma_cc_read32(cc, BCMA_CC_CORECTL) | BCMA_CC_CORECTL_UARTCLKEN); } } else { bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n", ccrev); return; } irq = bcma_core_mips_irq(cc->core); /* Determine the registers of the UARTs */ cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART); for (i = 0; i < cc->nr_serial_ports; i++) { ports[i].regs = cc->core->io_addr + BCMA_CC_UART0_DATA + (i * 256); ports[i].irq = irq; ports[i].baud_base = baud_base; ports[i].reg_shift = 0; } } #endif /* CONFIG_BCMA_DRIVER_MIPS */ compat-drivers-2012-09-18/drivers/bcma/core.c0000644000175000017500000000570212026211315020040 0ustar mcgrofmcgrof/* * Broadcom specific AMBA * Core ops * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" #include #include bool bcma_core_is_enabled(struct bcma_device *core) { if ((bcma_aread32(core, BCMA_IOCTL) & (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC)) != BCMA_IOCTL_CLK) return false; if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET) return false; return true; } EXPORT_SYMBOL_GPL(bcma_core_is_enabled); void bcma_core_disable(struct bcma_device *core, u32 flags) { if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET) return; bcma_awrite32(core, BCMA_IOCTL, flags); bcma_aread32(core, BCMA_IOCTL); udelay(10); bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET); bcma_aread32(core, BCMA_RESET_CTL); udelay(1); } EXPORT_SYMBOL_GPL(bcma_core_disable); int bcma_core_enable(struct bcma_device *core, u32 flags) { bcma_core_disable(core, flags); bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC | flags)); bcma_aread32(core, BCMA_IOCTL); bcma_awrite32(core, BCMA_RESET_CTL, 0); udelay(1); bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | flags)); bcma_aread32(core, BCMA_IOCTL); udelay(1); return 0; } EXPORT_SYMBOL_GPL(bcma_core_enable); void bcma_core_set_clockmode(struct bcma_device *core, enum bcma_clkmode clkmode) { u16 i; WARN_ON(core->id.id != BCMA_CORE_CHIPCOMMON && core->id.id != BCMA_CORE_PCIE && core->id.id != BCMA_CORE_80211); switch (clkmode) { case BCMA_CLKMODE_FAST: bcma_set32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT); udelay(64); for (i = 0; i < 1500; i++) { if (bcma_read32(core, BCMA_CLKCTLST) & BCMA_CLKCTLST_HAVEHT) { i = 0; break; } udelay(10); } if (i) bcma_err(core->bus, "HT force timeout\n"); break; case BCMA_CLKMODE_DYNAMIC: bcma_set32(core, BCMA_CLKCTLST, ~BCMA_CLKCTLST_FORCEHT); break; } } EXPORT_SYMBOL_GPL(bcma_core_set_clockmode); void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on) { u16 i; WARN_ON(req & ~BCMA_CLKCTLST_EXTRESREQ); WARN_ON(status & ~BCMA_CLKCTLST_EXTRESST); if (on) { bcma_set32(core, BCMA_CLKCTLST, req); for (i = 0; i < 10000; i++) { if ((bcma_read32(core, BCMA_CLKCTLST) & status) == status) { i = 0; break; } udelay(10); } if (i) bcma_err(core->bus, "PLL enable timeout\n"); } else { bcma_warn(core->bus, "Disabling PLL not supported yet!\n"); } } EXPORT_SYMBOL_GPL(bcma_core_pll_ctl); u32 bcma_core_dma_translation(struct bcma_device *core) { switch (core->bus->hosttype) { case BCMA_HOSTTYPE_SOC: return 0; case BCMA_HOSTTYPE_PCI: if (bcma_aread32(core, BCMA_IOST) & BCMA_IOST_DMA64) return BCMA_DMA_TRANSLATION_DMA64_CMT; else return BCMA_DMA_TRANSLATION_DMA32_CMT; default: bcma_err(core->bus, "DMA translation unknown for host %d\n", core->bus->hosttype); } return BCMA_DMA_TRANSLATION_NONE; } EXPORT_SYMBOL(bcma_core_dma_translation); compat-drivers-2012-09-18/drivers/bcma/Makefile0000644000175000017500000000107512026211315020403 0ustar mcgrofmcgrofbcma-y += main.o scan.o core.o sprom.o bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o bcma-y += driver_pci.o bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o obj-$(CONFIG_BCMA) += bcma.o ccflags-$(CONFIG_BCMA_DEBUG) := -DDEBUG compat-drivers-2012-09-18/drivers/bcma/Kconfig0000644000175000017500000000277012026211315020251 0ustar mcgrofmcgrofconfig BCMA_POSSIBLE bool depends on HAS_IOMEM && HAS_DMA default y menu "Broadcom specific AMBA" depends on BCMA_POSSIBLE config BCMA tristate "BCMA support" depends on BCMA_POSSIBLE help Bus driver for Broadcom specific Advanced Microcontroller Bus Architecture. # Support for Block-I/O. SELECT this from the driver that needs it. config BCMA_BLOCKIO bool depends on BCMA config BCMA_HOST_PCI_POSSIBLE bool depends on BCMA && PCI = y default y config BCMA_HOST_PCI bool "Support for BCMA on PCI-host bus" depends on BCMA_HOST_PCI_POSSIBLE config BCMA_DRIVER_PCI_HOSTMODE bool "Driver for PCI core working in hostmode" depends on BCMA && MIPS && BCMA_HOST_PCI help PCI core hostmode operation (external PCI bus). config BCMA_HOST_SOC bool depends on BCMA_DRIVER_MIPS config BCMA_DRIVER_MIPS bool "BCMA Broadcom MIPS core driver" depends on BCMA && MIPS help Driver for the Broadcom MIPS core attached to Broadcom specific Advanced Microcontroller Bus. If unsure, say N config BCMA_SFLASH bool depends on BCMA_DRIVER_MIPS default y config BCMA_NFLASH bool depends on BCMA_DRIVER_MIPS default y config BCMA_DRIVER_GMAC_CMN bool "BCMA Broadcom GBIT MAC COMMON core driver" depends on BCMA help Driver for the Broadcom GBIT MAC COMMON core attached to Broadcom specific Advanced Microcontroller Bus. If unsure, say N config BCMA_DEBUG bool "BCMA debugging" depends on BCMA help This turns on additional debugging messages. If unsure, say N endmenu compat-drivers-2012-09-18/.gitignore0000644000175000017500000000044212007776563016374 0ustar mcgrofmcgrofdrivers include net compat udev *~ git-describe compat-release Module.symvers module.order .pc code-metrics.txt .compat_base .compat_base_tree .compat_base_tree_version .compat_version .compat_autoconf_compat-* .config .config.mk_md5sum.txt .tmp_versions/ MAINTAINERS modules modules.order compat-drivers-2012-09-18/compat/0000755000175000017500000000000012026176501015652 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/compat/cordic.c0000644000175000017500000000476612026211315017267 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #define CORDIC_ANGLE_GEN 39797 #define CORDIC_PRECISION_SHIFT 16 #define CORDIC_NUM_ITER (CORDIC_PRECISION_SHIFT + 2) #define FIXED(X) ((s32)((X) << CORDIC_PRECISION_SHIFT)) #define FLOAT(X) (((X) >= 0) \ ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \ : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1)) static const s32 arctan_table[] = { 2949120, 1740967, 919879, 466945, 234379, 117304, 58666, 29335, 14668, 7334, 3667, 1833, 917, 458, 229, 115, 57, 29 }; /* * cordic_calc_iq() - calculates the i/q coordinate for given angle * * theta: angle in degrees for which i/q coordinate is to be calculated * coord: function output parameter holding the i/q coordinate */ struct cordic_iq cordic_calc_iq(s32 theta) { struct cordic_iq coord; s32 angle, valtmp; unsigned iter; int signx = 1; int signtheta; coord.i = CORDIC_ANGLE_GEN; coord.q = 0; angle = 0; theta = FIXED(theta); signtheta = (theta < 0) ? -1 : 1; theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) - FIXED(180) * signtheta; if (FLOAT(theta) > 90) { theta -= FIXED(180); signx = -1; } else if (FLOAT(theta) < -90) { theta += FIXED(180); signx = -1; } for (iter = 0; iter < CORDIC_NUM_ITER; iter++) { if (theta > angle) { valtmp = coord.i - (coord.q >> iter); coord.q += (coord.i >> iter); angle += arctan_table[iter]; } else { valtmp = coord.i + (coord.q >> iter); coord.q -= (coord.i >> iter); angle -= arctan_table[iter]; } coord.i = valtmp; } coord.i *= signx; coord.q *= signx; return coord; } EXPORT_SYMBOL_GPL(cordic_calc_iq); MODULE_DESCRIPTION("Cordic functions"); MODULE_AUTHOR("Broadcom Corporation"); MODULE_LICENSE("Dual BSD/GPL"); compat-drivers-2012-09-18/compat/compat-2.6.37.c0000644000175000017500000002017212026211315020027 0ustar mcgrofmcgrof/* * Copyright 2010 Hauke Mehrtens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.37. */ #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) static const void *net_current_ns(void) { return current->nsproxy->net_ns; } static const void *net_initial_ns(void) { return &init_net; } static const void *net_netlink_ns(struct sock *sk) { return sock_net(sk); } struct kobj_ns_type_operations net_ns_type_operations = { .type = KOBJ_NS_TYPE_NET, .current_ns = net_current_ns, .netlink_ns = net_netlink_ns, .initial_ns = net_initial_ns, }; EXPORT_SYMBOL_GPL(net_ns_type_operations); #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)*/ #undef genl_info #undef genl_unregister_family static LIST_HEAD(compat_nl_fam); static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) { struct genl_ops *ops; list_for_each_entry(ops, &family->family.ops_list, ops.ops_list) if (ops->cmd == cmd) return ops; return NULL; } static int nl_doit_wrapper(struct sk_buff *skb, struct genl_info *info) { struct compat_genl_info compat_info; struct genl_family *family; struct genl_ops *ops; int err; list_for_each_entry(family, &compat_nl_fam, list) { if (family->id == info->nlhdr->nlmsg_type) goto found; } return -ENOENT; found: ops = genl_get_cmd(info->genlhdr->cmd, family); if (!ops) return -ENOENT; memset(&compat_info.user_ptr, 0, sizeof(compat_info.user_ptr)); compat_info.info = info; #define __copy(_field) compat_info._field = info->_field __copy(snd_seq); __copy(snd_pid); __copy(genlhdr); __copy(attrs); #undef __copy if (family->pre_doit) { err = family->pre_doit(ops, skb, &compat_info); if (err) return err; } err = ops->doit(skb, &compat_info); if (family->post_doit) family->post_doit(ops, skb, &compat_info); return err; } int compat_genl_register_family_with_ops(struct genl_family *family, struct genl_ops *ops, size_t n_ops) { int i, ret; #define __copy(_field) family->family._field = family->_field __copy(id); __copy(hdrsize); __copy(version); __copy(maxattr); strncpy(family->family.name, family->name, sizeof(family->family.name)); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) __copy(netnsok); #endif #undef __copy ret = genl_register_family(&family->family); if (ret < 0) return ret; family->attrbuf = family->family.attrbuf; family->id = family->family.id; for (i = 0; i < n_ops; i++) { #define __copy(_field) ops[i].ops._field = ops[i]._field __copy(cmd); __copy(flags); __copy(policy); __copy(dumpit); __copy(done); #undef __copy if (ops[i].doit) ops[i].ops.doit = nl_doit_wrapper; ret = genl_register_ops(&family->family, &ops[i].ops); if (ret < 0) goto error_ops; } list_add(&family->list, &compat_nl_fam); return ret; error_ops: compat_genl_unregister_family(family); return ret; } EXPORT_SYMBOL_GPL(compat_genl_register_family_with_ops); int compat_genl_unregister_family(struct genl_family *family) { int err; err = genl_unregister_family(&family->family); list_del(&family->list); return err; } EXPORT_SYMBOL_GPL(compat_genl_unregister_family); #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) #undef led_brightness_set #undef led_classdev_unregister static DEFINE_SPINLOCK(led_lock); static LIST_HEAD(led_timers); struct led_timer { struct list_head list; struct led_classdev *cdev; struct timer_list blink_timer; unsigned long blink_delay_on; unsigned long blink_delay_off; int blink_brightness; }; static void led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { led_cdev->brightness = brightness; led_cdev->brightness_set(led_cdev, brightness); } static struct led_timer *led_get_timer(struct led_classdev *led_cdev) { struct led_timer *p; unsigned long flags; spin_lock_irqsave(&led_lock, flags); list_for_each_entry(p, &led_timers, list) { if (p->cdev == led_cdev) goto found; } p = NULL; found: spin_unlock_irqrestore(&led_lock, flags); return p; } static void led_stop_software_blink(struct led_timer *led) { del_timer_sync(&led->blink_timer); led->blink_delay_on = 0; led->blink_delay_off = 0; } static void led_timer_function(unsigned long data) { struct led_timer *led = (struct led_timer *)data; unsigned long brightness; unsigned long delay; if (!led->blink_delay_on || !led->blink_delay_off) { led->cdev->brightness_set(led->cdev, LED_OFF); return; } brightness = led->cdev->brightness; if (!brightness) { /* Time to switch the LED on. */ brightness = led->blink_brightness; delay = led->blink_delay_on; } else { /* Store the current brightness value to be able * to restore it when the delay_off period is over. */ led->blink_brightness = brightness; brightness = LED_OFF; delay = led->blink_delay_off; } led_brightness_set(led->cdev, brightness); mod_timer(&led->blink_timer, jiffies + msecs_to_jiffies(delay)); } static struct led_timer *led_new_timer(struct led_classdev *led_cdev) { struct led_timer *led; unsigned long flags; led = kzalloc(sizeof(struct led_timer), GFP_ATOMIC); if (!led) return NULL; led->cdev = led_cdev; init_timer(&led->blink_timer); led->blink_timer.function = led_timer_function; led->blink_timer.data = (unsigned long) led; spin_lock_irqsave(&led_lock, flags); list_add(&led->list, &led_timers); spin_unlock_irqrestore(&led_lock, flags); return led; } void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct led_timer *led; int current_brightness; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) if (led_cdev->blink_set && !led_cdev->blink_set(led_cdev, delay_on, delay_off)) return; #endif led = led_get_timer(led_cdev); if (!led) { led = led_new_timer(led_cdev); if (!led) return; } /* blink with 1 Hz as default if nothing specified */ if (!*delay_on && !*delay_off) *delay_on = *delay_off = 500; if (led->blink_delay_on == *delay_on && led->blink_delay_off == *delay_off) return; current_brightness = led_cdev->brightness; if (current_brightness) led->blink_brightness = current_brightness; if (!led->blink_brightness) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) led->blink_brightness = led_cdev->max_brightness; #else led->blink_brightness = LED_FULL; #endif led_stop_software_blink(led); led->blink_delay_on = *delay_on; led->blink_delay_off = *delay_off; /* never on - don't blink */ if (!*delay_on) return; /* never off - just set to brightness */ if (!*delay_off) { led_brightness_set(led_cdev, led->blink_brightness); return; } mod_timer(&led->blink_timer, jiffies + 1); } EXPORT_SYMBOL_GPL(led_blink_set); void compat_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct led_timer *led = led_get_timer(led_cdev); if (led) led_stop_software_blink(led); return led_cdev->brightness_set(led_cdev, brightness); } EXPORT_SYMBOL_GPL(compat_led_brightness_set); void compat_led_classdev_unregister(struct led_classdev *led_cdev) { struct led_timer *led = led_get_timer(led_cdev); unsigned long flags; if (led) { del_timer_sync(&led->blink_timer); spin_lock_irqsave(&led_lock, flags); list_del(&led->list); spin_unlock_irqrestore(&led_lock, flags); kfree(led); } led_classdev_unregister(led_cdev); } EXPORT_SYMBOL_GPL(compat_led_classdev_unregister); /** * vzalloc - allocate virtually contiguous memory with zero fill * @size: allocation size * Allocate enough pages to cover @size from the page level * allocator and map them into contiguous kernel virtual space. * The memory allocated is set to zero. * * For tight control over page level allocator and protection flags * use __vmalloc() instead. */ void *compat_vzalloc(unsigned long size) { void *buf; buf = vmalloc(size); if (buf) memset(buf, 0, size); return buf; } EXPORT_SYMBOL_GPL(compat_vzalloc); #endif compat-drivers-2012-09-18/compat/compat_atomic.c0000644000175000017500000000147212026211315020632 0ustar mcgrofmcgrof#include #include #if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) && (defined(CONFIG_UML) || defined(CONFIG_X86))) && !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) && defined(CONFIG_ARM) && !defined(CONFIG_GENERIC_ATOMIC64)) static DEFINE_SPINLOCK(lock); long long atomic64_read(const atomic64_t *v) { unsigned long flags; long long val; spin_lock_irqsave(&lock, flags); val = v->counter; spin_unlock_irqrestore(&lock, flags); return val; } EXPORT_SYMBOL_GPL(atomic64_read); long long atomic64_add_return(long long a, atomic64_t *v) { unsigned long flags; long long val; spin_lock_irqsave(&lock, flags); val = v->counter += a; spin_unlock_irqrestore(&lock, flags); return val; } EXPORT_SYMBOL_GPL(atomic64_add_return); #endif compat-drivers-2012-09-18/compat/compat-2.6.24.c0000644000175000017500000001064012026211315020022 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.24. */ #include #include /* * We simply won't use it though, just declare it for our wrappers and * for usage with tons of code that makes mention to it. */ struct net init_net; EXPORT_SYMBOL_GPL(init_net); /* 2.6.22 and 2.6.23 have eth_header_cache_update defined as extern in include/linux/etherdevice.h * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */ /** * eth_header_cache_update - update cache entry * @hh: destination cache entry * @dev: network device * @haddr: new hardware address * * Called by Address Resolution module to notify changes in address. */ void eth_header_cache_update(struct hh_cache *hh, struct net_device *dev, unsigned char *haddr) { memcpy(((u8 *) hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)), haddr, ETH_ALEN); } EXPORT_SYMBOL_GPL(eth_header_cache_update); /* 2.6.22 and 2.6.23 have eth_header_cache defined as extern in include/linux/etherdevice.h * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */ /** * eth_header_cache - fill cache entry from neighbour * @neigh: source neighbour * @hh: destination cache entry * Create an Ethernet header template from the neighbour. */ int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh) { __be16 type = hh->hh_type; struct ethhdr *eth; const struct net_device *dev = neigh->dev; eth = (struct ethhdr *) (((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth)))); if (type == htons(ETH_P_802_3)) return -1; eth->h_proto = type; memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); memcpy(eth->h_dest, neigh->ha, ETH_ALEN); hh->hh_len = ETH_HLEN; return 0; } EXPORT_SYMBOL_GPL(eth_header_cache); /* 2.6.22 and 2.6.23 have eth_header() defined as extern in include/linux/etherdevice.h * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */ /** * eth_header - create the Ethernet header * @skb: buffer to alter * @dev: source device * @type: Ethernet type field * @daddr: destination address (NULL leave destination address) * @saddr: source address (NULL use device source address) * @len: packet length (<= skb->len) * * * Set the protocol type. For a packet of type ETH_P_802_3 we put the length * in here instead. It is up to the 802.2 layer to carry protocol information. */ int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN); if (type != ETH_P_802_3) eth->h_proto = htons(type); else eth->h_proto = htons(len); /* * Set the source hardware address. */ if (!saddr) saddr = dev->dev_addr; memcpy(eth->h_source, saddr, dev->addr_len); if (daddr) { memcpy(eth->h_dest, daddr, dev->addr_len); return ETH_HLEN; } /* * Anyway, the loopback-device should never use this function... */ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { memset(eth->h_dest, 0, dev->addr_len); return ETH_HLEN; } return -ETH_HLEN; } EXPORT_SYMBOL_GPL(eth_header); /* 2.6.22 and 2.6.23 have eth_rebuild_header defined as extern in include/linux/etherdevice.h * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */ /** * eth_rebuild_header- rebuild the Ethernet MAC header. * @skb: socket buffer to update * * This is called after an ARP or IPV6 ndisc it's resolution on this * sk_buff. We now let protocol (ARP) fill in the other fields. * * This routine CANNOT use cached dst->neigh! * Really, it is used only when dst->neigh is wrong. */ int eth_rebuild_header(struct sk_buff *skb) { struct ethhdr *eth = (struct ethhdr *)skb->data; struct net_device *dev = skb->dev; switch (eth->h_proto) { #ifdef CONFIG_INET case __constant_htons(ETH_P_IP): return arp_find(eth->h_dest, skb); #endif default: printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n", dev->name, (int)eth->h_proto); memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); break; } return 0; } EXPORT_SYMBOL_GPL(eth_rebuild_header); compat-drivers-2012-09-18/compat/compat-3.7.c0000644000175000017500000001376712026211315017615 0ustar mcgrofmcgrof/* * Copyright 2012 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 3.7. */ #include #include bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay) { cancel_delayed_work(dwork); queue_delayed_work(wq, dwork, delay); return false; } EXPORT_SYMBOL_GPL(mod_delayed_work); /* * Kernels >= 3.7 get their PCI-E Capabilities Register cached * via the pci_dev->pcie_flags_reg so for older kernels we have * no other option but to read this every single time we need * it accessed. If we really cared to improve the efficiency * of this we could try to find an unused u16 varible on the * pci_dev but if we found it we likely would remove it from * the kernel anyway right? Bite me. */ static inline u16 pcie_flags_reg(struct pci_dev *dev) { int pos; u16 reg16; pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (!pos) return 0; pci_read_config_word(dev, pos + PCI_EXP_FLAGS, ®16); return reg16; } static inline int pci_pcie_type(struct pci_dev *dev) { return (pcie_flags_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4; } static inline int pcie_cap_version(struct pci_dev *dev) { return pcie_flags_reg(dev) & PCI_EXP_FLAGS_VERS; } static inline bool pcie_cap_has_devctl(const struct pci_dev *dev) { return true; } static inline bool pcie_cap_has_lnkctl(struct pci_dev *dev) { int type = pci_pcie_type(dev); return pcie_cap_version(dev) > 1 || type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_ENDPOINT || type == PCI_EXP_TYPE_LEG_END; } static inline bool pcie_cap_has_sltctl(struct pci_dev *dev) { int type = pci_pcie_type(dev); return pcie_cap_version(dev) > 1 || type == PCI_EXP_TYPE_ROOT_PORT || (type == PCI_EXP_TYPE_DOWNSTREAM && pcie_flags_reg(dev) & PCI_EXP_FLAGS_SLOT); } static inline bool pcie_cap_has_rtctl(struct pci_dev *dev) { int type = pci_pcie_type(dev); return pcie_cap_version(dev) > 1 || type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_RC_EC; } static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos) { if (!pci_is_pcie(dev)) return false; switch (pos) { case PCI_EXP_FLAGS_TYPE: return true; case PCI_EXP_DEVCAP: case PCI_EXP_DEVCTL: case PCI_EXP_DEVSTA: return pcie_cap_has_devctl(dev); case PCI_EXP_LNKCAP: case PCI_EXP_LNKCTL: case PCI_EXP_LNKSTA: return pcie_cap_has_lnkctl(dev); case PCI_EXP_SLTCAP: case PCI_EXP_SLTCTL: case PCI_EXP_SLTSTA: return pcie_cap_has_sltctl(dev); case PCI_EXP_RTCTL: case PCI_EXP_RTCAP: case PCI_EXP_RTSTA: return pcie_cap_has_rtctl(dev); case PCI_EXP_DEVCAP2: case PCI_EXP_DEVCTL2: case PCI_EXP_LNKCAP2: case PCI_EXP_LNKCTL2: case PCI_EXP_LNKSTA2: return pcie_cap_version(dev) > 1; default: return false; } } /* * Note that these accessor functions are only for the "PCI Express * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.) */ int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val) { int ret; *val = 0; if (pos & 1) return -EINVAL; if (pcie_capability_reg_implemented(dev, pos)) { ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val); /* * Reset *val to 0 if pci_read_config_word() fails, it may * have been written as 0xFFFF if hardware error happens * during pci_read_config_word(). */ if (ret) *val = 0; return ret; } /* * For Functions that do not implement the Slot Capabilities, * Slot Status, and Slot Control registers, these spaces must * be hardwired to 0b, with the exception of the Presence Detect * State bit in the Slot Status register of Downstream Ports, * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8) */ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA && pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { *val = PCI_EXP_SLTSTA_PDS; } return 0; } EXPORT_SYMBOL(pcie_capability_read_word); int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val) { int ret; *val = 0; if (pos & 3) return -EINVAL; if (pcie_capability_reg_implemented(dev, pos)) { ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val); /* * Reset *val to 0 if pci_read_config_dword() fails, it may * have been written as 0xFFFFFFFF if hardware error happens * during pci_read_config_dword(). */ if (ret) *val = 0; return ret; } if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL && pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { *val = PCI_EXP_SLTSTA_PDS; } return 0; } EXPORT_SYMBOL(pcie_capability_read_dword); int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val) { if (pos & 1) return -EINVAL; if (!pcie_capability_reg_implemented(dev, pos)) return 0; return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val); } EXPORT_SYMBOL(pcie_capability_write_word); int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val) { if (pos & 3) return -EINVAL; if (!pcie_capability_reg_implemented(dev, pos)) return 0; return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val); } EXPORT_SYMBOL(pcie_capability_write_dword); int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos, u16 clear, u16 set) { int ret; u16 val; ret = pcie_capability_read_word(dev, pos, &val); if (!ret) { val &= ~clear; val |= set; ret = pcie_capability_write_word(dev, pos, val); } return ret; } EXPORT_SYMBOL(pcie_capability_clear_and_set_word); int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos, u32 clear, u32 set) { int ret; u32 val; ret = pcie_capability_read_dword(dev, pos, &val); if (!ret) { val &= ~clear; val |= set; ret = pcie_capability_write_dword(dev, pos, val); } return ret; } EXPORT_SYMBOL(pcie_capability_clear_and_set_dword); compat-drivers-2012-09-18/compat/kstrtox.c0000644000175000017500000001071312026211315017527 0ustar mcgrofmcgrof/* * Convert integer string representation to an integer. * If an integer doesn't fit into specified type, -E is returned. * * Integer starts with optional sign. * kstrtou*() functions do not accept sign "-". * * Radix 0 means autodetection: leading "0x" implies radix 16, * leading "0" implies radix 8, otherwise radix is 10. * Autodetection hints work after optional sign, but not before. * * If -E is returned, result is not touched. */ #include /* * kstrto* was included in kernel 2.6.38.4 and causes conflicts with the * version included in compat-drivers. We use strict_strtol to check if * kstrto* is already available. */ #ifndef strict_strtol #include #include #include #include #include #include static inline char _tolower(const char c) { return c | 0x20; } static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) { unsigned long long acc; int ok; if (base == 0) { if (s[0] == '0') { if (_tolower(s[1]) == 'x' && isxdigit(s[2])) base = 16; else base = 8; } else base = 10; } if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') s += 2; acc = 0; ok = 0; while (*s) { unsigned int val; if ('0' <= *s && *s <= '9') val = *s - '0'; else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') val = _tolower(*s) - 'a' + 10; else if (*s == '\n') { if (*(s + 1) == '\0') break; else return -EINVAL; } else return -EINVAL; if (val >= base) return -EINVAL; if (acc > div_u64(ULLONG_MAX - val, base)) return -ERANGE; acc = acc * base + val; ok = 1; s++; } if (!ok) return -EINVAL; *res = acc; return 0; } int kstrtoull(const char *s, unsigned int base, unsigned long long *res) { if (s[0] == '+') s++; return _kstrtoull(s, base, res); } EXPORT_SYMBOL_GPL(kstrtoull); int kstrtoll(const char *s, unsigned int base, long long *res) { unsigned long long tmp; int rv; if (s[0] == '-') { rv = _kstrtoull(s + 1, base, &tmp); if (rv < 0) return rv; if ((long long)(-tmp) >= 0) return -ERANGE; *res = -tmp; } else { rv = kstrtoull(s, base, &tmp); if (rv < 0) return rv; if ((long long)tmp < 0) return -ERANGE; *res = tmp; } return 0; } EXPORT_SYMBOL_GPL(kstrtoll); /* Internal, do not use. */ int _kstrtoul(const char *s, unsigned int base, unsigned long *res) { unsigned long long tmp; int rv; rv = kstrtoull(s, base, &tmp); if (rv < 0) return rv; if (tmp != (unsigned long long)(unsigned long)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(_kstrtoul); /* Internal, do not use. */ int _kstrtol(const char *s, unsigned int base, long *res) { long long tmp; int rv; rv = kstrtoll(s, base, &tmp); if (rv < 0) return rv; if (tmp != (long long)(long)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(_kstrtol); int kstrtouint(const char *s, unsigned int base, unsigned int *res) { unsigned long long tmp; int rv; rv = kstrtoull(s, base, &tmp); if (rv < 0) return rv; if (tmp != (unsigned long long)(unsigned int)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(kstrtouint); int kstrtoint(const char *s, unsigned int base, int *res) { long long tmp; int rv; rv = kstrtoll(s, base, &tmp); if (rv < 0) return rv; if (tmp != (long long)(int)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(kstrtoint); int kstrtou16(const char *s, unsigned int base, u16 *res) { unsigned long long tmp; int rv; rv = kstrtoull(s, base, &tmp); if (rv < 0) return rv; if (tmp != (unsigned long long)(u16)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(kstrtou16); int kstrtos16(const char *s, unsigned int base, s16 *res) { long long tmp; int rv; rv = kstrtoll(s, base, &tmp); if (rv < 0) return rv; if (tmp != (long long)(s16)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(kstrtos16); int kstrtou8(const char *s, unsigned int base, u8 *res) { unsigned long long tmp; int rv; rv = kstrtoull(s, base, &tmp); if (rv < 0) return rv; if (tmp != (unsigned long long)(u8)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(kstrtou8); int kstrtos8(const char *s, unsigned int base, s8 *res) { long long tmp; int rv; rv = kstrtoll(s, base, &tmp); if (rv < 0) return rv; if (tmp != (long long)(s8)tmp) return -ERANGE; *res = tmp; return 0; } EXPORT_SYMBOL_GPL(kstrtos8); #endif /* #ifndef strict_strtol */ compat-drivers-2012-09-18/compat/compat-3.4.c0000644000175000017500000002761312026211315017605 0ustar mcgrofmcgrof/* * Copyright 2012 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 3.4. */ #include #include #include /* __wake_up_common was declared as part of the wait.h until * 2.6.31 in which they made it private to the scheduler. Prefix it with * compat to avoid double declaration issues. */ static void compat_wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int wake_flags, void *key) { wait_queue_t *curr, *next; list_for_each_entry_safe(curr, next, &q->task_list, task_list) { unsigned flags = curr->flags; if (curr->func(curr, mode, wake_flags, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; } } /* The last 'nr' parameter was added to the __wake_up_locked() function * in 3.4 kernel. Define a new one prefixed with compat_ for the new API. */ void compat_wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr) { compat_wake_up_common(q, mode, nr, 0, NULL); } EXPORT_SYMBOL_GPL(compat_wake_up_locked); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) #include #include #include #define setsda(adap, val) adap->setsda(adap->data, val) #define setscl(adap, val) adap->setscl(adap->data, val) #define getsda(adap) adap->getsda(adap->data) #define getscl(adap) adap->getscl(adap->data) #define bit_dbg(level, dev, format, args...) \ do {} while (0) static inline void sdalo(struct i2c_algo_bit_data *adap) { setsda(adap, 0); udelay((adap->udelay + 1) / 2); } static inline void sdahi(struct i2c_algo_bit_data *adap) { setsda(adap, 1); udelay((adap->udelay + 1) / 2); } static inline void scllo(struct i2c_algo_bit_data *adap) { setscl(adap, 0); udelay(adap->udelay / 2); } static int sclhi(struct i2c_algo_bit_data *adap) { unsigned long start; setscl(adap, 1); /* Not all adapters have scl sense line... */ if (!adap->getscl) goto done; start = jiffies; while (!getscl(adap)) { /* This hw knows how to read the clock line, so we wait * until it actually gets high. This is safer as some * chips may hold it low ("clock stretching") while they * are processing data internally. */ if (time_after(jiffies, start + adap->timeout)) { /* Test one last time, as we may have been preempted * between last check and timeout test. */ if (getscl(adap)) break; return -ETIMEDOUT; } cpu_relax(); } #ifdef DEBUG if (jiffies != start && i2c_debug >= 3) pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go " "high\n", jiffies - start); #endif done: udelay(adap->udelay); return 0; } static void i2c_start(struct i2c_algo_bit_data *adap) { /* assert: scl, sda are high */ setsda(adap, 0); udelay(adap->udelay); scllo(adap); } static void i2c_repstart(struct i2c_algo_bit_data *adap) { /* assert: scl is low */ sdahi(adap); sclhi(adap); setsda(adap, 0); udelay(adap->udelay); scllo(adap); } static void i2c_stop(struct i2c_algo_bit_data *adap) { /* assert: scl is low */ sdalo(adap); sclhi(adap); setsda(adap, 1); udelay(adap->udelay); } static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) { int i; int sb; int ack; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; /* assert: scl is low */ for (i = 7; i >= 0; i--) { sb = (c >> i) & 1; setsda(adap, sb); udelay((adap->udelay + 1) / 2); if (sclhi(adap) < 0) { /* timed out */ bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at bit #%d\n", (int)c, i); return -ETIMEDOUT; } /* FIXME do arbitration here: * if (sb && !getsda(adap)) -> ouch! Get out of here. * * Report a unique code, so higher level code can retry * the whole (combined) message and *NOT* issue STOP. */ scllo(adap); } sdahi(adap); if (sclhi(adap) < 0) { /* timeout */ bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at ack\n", (int)c); return -ETIMEDOUT; } /* read ack: SDA should be pulled down by slave, or it may * NAK (usually to report problems with the data we wrote). */ ack = !getsda(adap); /* ack: sda is pulled low -> success */ bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c, ack ? "A" : "NA"); scllo(adap); return ack; /* assert: scl is low (sda undef) */ } static int i2c_inb(struct i2c_adapter *i2c_adap) { /* read byte via i2c port, without start/stop sequence */ /* acknowledge is sent in i2c_read. */ int i; unsigned char indata = 0; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; /* assert: scl is low */ sdahi(adap); for (i = 0; i < 8; i++) { if (sclhi(adap) < 0) { /* timeout */ bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit " "#%d\n", 7 - i); return -ETIMEDOUT; } indata *= 2; if (getsda(adap)) indata |= 0x01; setscl(adap, 0); udelay(i == 7 ? adap->udelay / 2 : adap->udelay); } /* assert: scl is low */ return indata; } static int try_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) { struct i2c_algo_bit_data *adap = i2c_adap->algo_data; int i, ret = 0; for (i = 0; i <= retries; i++) { ret = i2c_outb(i2c_adap, addr); if (ret == 1 || i == retries) break; bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); i2c_stop(adap); udelay(adap->udelay); yield(); bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); i2c_start(adap); } if (i && ret) bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at " "0x%02x: %s\n", i + 1, addr & 1 ? "read from" : "write to", addr >> 1, ret == 1 ? "success" : "failed, timeout?"); return ret; } static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { unsigned short flags = msg->flags; unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; unsigned char addr; int ret, retries; retries = nak_ok ? 0 : i2c_adap->retries; if (flags & I2C_M_TEN) { /* a ten bit address */ addr = 0xf0 | ((msg->addr >> 7) & 0x06); bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr); /* try extended address code...*/ ret = try_address(i2c_adap, addr, retries); if ((ret != 1) && !nak_ok) { dev_err(&i2c_adap->dev, "died at extended address code\n"); return -ENXIO; } /* the remaining 8 bit address */ ret = i2c_outb(i2c_adap, msg->addr & 0xff); if ((ret != 1) && !nak_ok) { /* the chip did not ack / xmission error occurred */ dev_err(&i2c_adap->dev, "died at 2nd address code\n"); return -ENXIO; } if (flags & I2C_M_RD) { bit_dbg(3, &i2c_adap->dev, "emitting repeated " "start condition\n"); i2c_repstart(adap); /* okay, now switch into reading mode */ addr |= 0x01; ret = try_address(i2c_adap, addr, retries); if ((ret != 1) && !nak_ok) { dev_err(&i2c_adap->dev, "died at repeated address code\n"); return -EIO; } } } else { /* normal 7bit address */ addr = msg->addr << 1; if (flags & I2C_M_RD) addr |= 1; if (flags & I2C_M_REV_DIR_ADDR) addr ^= 1; ret = try_address(i2c_adap, addr, retries); if ((ret != 1) && !nak_ok) return -ENXIO; } return 0; } static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { const unsigned char *temp = msg->buf; int count = msg->len; unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; int retval; int wrcount = 0; while (count > 0) { retval = i2c_outb(i2c_adap, *temp); /* OK/ACK; or ignored NAK */ if ((retval > 0) || (nak_ok && (retval == 0))) { count--; temp++; wrcount++; /* A slave NAKing the master means the slave didn't like * something about the data it saw. For example, maybe * the SMBus PEC was wrong. */ } else if (retval == 0) { dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n"); return -EIO; /* Timeout; or (someday) lost arbitration * * FIXME Lost ARB implies retrying the transaction from * the first message, after the "winning" master issues * its STOP. As a rule, upper layer code has no reason * to know or care about this ... it is *NOT* an error. */ } else { dev_err(&i2c_adap->dev, "sendbytes: error %d\n", retval); return retval; } } return wrcount; } static int acknak(struct i2c_adapter *i2c_adap, int is_ack) { struct i2c_algo_bit_data *adap = i2c_adap->algo_data; /* assert: sda is high */ if (is_ack) /* send ack */ setsda(adap, 0); udelay((adap->udelay + 1) / 2); if (sclhi(adap) < 0) { /* timeout */ dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n"); return -ETIMEDOUT; } scllo(adap); return 0; } static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) { int inval; int rdcount = 0; /* counts bytes read */ unsigned char *temp = msg->buf; int count = msg->len; const unsigned flags = msg->flags; while (count > 0) { inval = i2c_inb(i2c_adap); if (inval >= 0) { *temp = inval; rdcount++; } else { /* read timed out */ break; } temp++; count--; /* Some SMBus transactions require that we receive the transaction length as the first read byte. */ if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) { if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) { if (!(flags & I2C_M_NO_RD_ACK)) acknak(i2c_adap, 0); dev_err(&i2c_adap->dev, "readbytes: invalid " "block length (%d)\n", inval); return -EPROTO; } /* The original count value accounts for the extra bytes, that is, either 1 for a regular transaction, or 2 for a PEC transaction. */ count += inval; msg->len += inval; } bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n", inval, (flags & I2C_M_NO_RD_ACK) ? "(no ack/nak)" : (count ? "A" : "NA")); if (!(flags & I2C_M_NO_RD_ACK)) { inval = acknak(i2c_adap, count); if (inval < 0) return inval; } } return rdcount; } static u32 bit_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; } static int bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { struct i2c_msg *pmsg; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; int i, ret; unsigned short nak_ok; if (adap->pre_xfer) { ret = adap->pre_xfer(i2c_adap); if (ret < 0) return ret; } bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); i2c_start(adap); for (i = 0; i < num; i++) { pmsg = &msgs[i]; nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; if (!(pmsg->flags & I2C_M_NOSTART)) { if (i) { bit_dbg(3, &i2c_adap->dev, "emitting " "repeated start condition\n"); i2c_repstart(adap); } ret = bit_doAddress(i2c_adap, pmsg); if ((ret != 0) && !nak_ok) { bit_dbg(1, &i2c_adap->dev, "NAK from " "device addr 0x%02x msg #%d\n", msgs[i].addr, i); goto bailout; } } if (pmsg->flags & I2C_M_RD) { /* read bytes into buffer*/ ret = readbytes(i2c_adap, pmsg); if (ret >= 1) bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n", ret, ret == 1 ? "" : "s"); if (ret < pmsg->len) { if (ret >= 0) ret = -EIO; goto bailout; } } else { /* write bytes from buffer */ ret = sendbytes(i2c_adap, pmsg); if (ret >= 1) bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n", ret, ret == 1 ? "" : "s"); if (ret < pmsg->len) { if (ret >= 0) ret = -EIO; goto bailout; } } } ret = i; bailout: bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); i2c_stop(adap); if (adap->post_xfer) adap->post_xfer(i2c_adap); return ret; } const struct i2c_algorithm i2c_bit_algo = { .master_xfer = bit_xfer, .functionality = bit_func, }; EXPORT_SYMBOL(i2c_bit_algo); #endif int simple_open(struct inode *inode, struct file *file) { if (inode->i_private) file->private_data = inode->i_private; return 0; } EXPORT_SYMBOL_GPL(simple_open); compat-drivers-2012-09-18/compat/compat-2.6.18.c0000644000175000017500000000060312026211315020023 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.18. */ #include /* 2.6.18 compat code goes here */ compat-drivers-2012-09-18/compat/compat-2.6.28.c0000644000175000017500000003136412026211315020034 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.28. */ #include #include #include #include /* 2.6.28 compat code goes here */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) #if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE) /* * Compat-wireless notes for USB backport stuff: * * urb->reject exists on 2.6.27, the poison/unpoison helpers * did not though. The anchor poison does not exist so we cannot use them. * * USB anchor poising seems to exist to prevent future driver sumbissions * of usb_anchor_urb() to an anchor marked as poisoned. For older kernels * we cannot use that, so new usb_anchor_urb()s will be anchored. The down * side to this should be submission of URBs will continue being anchored * on an anchor instead of having them being rejected immediately when the * driver realized we needed to stop. For ar9170 we poison URBs upon the * ar9170 mac80211 stop callback(), don't think this should be so bad. * It mean there is period of time in older kernels for which we continue * to anchor new URBs to a known stopped anchor. We have two anchors * (TX, and RX) */ #if 0 /** * usb_poison_urb - reliably kill a transfer and prevent further use of an URB * @urb: pointer to URB describing a previously submitted request, * may be NULL * * This routine cancels an in-progress request. It is guaranteed that * upon return all completion handlers will have finished and the URB * will be totally idle and cannot be reused. These features make * this an ideal way to stop I/O in a disconnect() callback. * If the request has not already finished or been unlinked * the completion handler will see urb->status == -ENOENT. * * After and while the routine runs, attempts to resubmit the URB will fail * with error -EPERM. Thus even if the URB's completion handler always * tries to resubmit, it will not succeed and the URB will become idle. * * This routine may not be used in an interrupt context (such as a bottom * half or a completion handler), or when holding a spinlock, or in other * situations where the caller can't schedule(). * * This routine should not be called by a driver after its disconnect * method has returned. */ void usb_poison_urb(struct urb *urb) { might_sleep(); if (!(urb && urb->dev && urb->ep)) return; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) spin_lock_irq(&usb_reject_lock); #endif ++urb->reject; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) spin_unlock_irq(&usb_reject_lock); #endif /* * XXX: usb_hcd_unlink_urb() needs backporting... this is defined * on usb hcd.c but urb.c gets access to it. That is, older kernels * have usb_hcd_unlink_urb() but its not exported, nor can we * re-implement it exactly. This essentially dequeues the urb from * hw, we need to figure out a way to backport this. */ //usb_hcd_unlink_urb(urb, -ENOENT); wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); } EXPORT_SYMBOL_GPL(usb_poison_urb); #endif #endif /* CONFIG_USB */ #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) #include struct pcmcia_cfg_mem { tuple_t tuple; cisparse_t parse; u8 buf[256]; cistpl_cftable_entry_t dflt; }; /** * pcmcia_loop_config() - loop over configuration options * @p_dev: the struct pcmcia_device which we need to loop for. * @conf_check: function to call for each configuration option. * It gets passed the struct pcmcia_device, the CIS data * describing the configuration option, and private data * being passed to pcmcia_loop_config() * @priv_data: private data to be passed to the conf_check function. * * pcmcia_loop_config() loops over all configuration options, and calls * the driver-specific conf_check() for each one, checking whether * it is a valid one. Returns 0 on success or errorcode otherwise. */ int pcmcia_loop_config(struct pcmcia_device *p_dev, int (*conf_check) (struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cfg, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data), void *priv_data) { struct pcmcia_cfg_mem *cfg_mem; tuple_t *tuple; int ret; unsigned int vcc; cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL); if (cfg_mem == NULL) return -ENOMEM; /* get the current Vcc setting */ vcc = p_dev->socket->socket.Vcc; tuple = &cfg_mem->tuple; tuple->TupleData = cfg_mem->buf; tuple->TupleDataMax = 255; tuple->TupleOffset = 0; tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple->Attributes = 0; ret = pcmcia_get_first_tuple(p_dev, tuple); while (!ret) { cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry; if (pcmcia_get_tuple_data(p_dev, tuple)) goto next_entry; if (pcmcia_parse_tuple(tuple, &cfg_mem->parse)) goto next_entry; /* default values */ p_dev->conf.ConfigIndex = cfg->index; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) cfg_mem->dflt = *cfg; ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data); if (!ret) break; next_entry: ret = pcmcia_get_next_tuple(p_dev, tuple); } return ret; } EXPORT_SYMBOL_GPL(pcmcia_loop_config); #endif /* CONFIG_PCMCIA */ #if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE) void usb_unpoison_urb(struct urb *urb) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) unsigned long flags; #endif if (!urb) return; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) spin_lock_irqsave(&usb_reject_lock, flags); #endif --urb->reject; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) spin_unlock_irqrestore(&usb_reject_lock, flags); #endif } EXPORT_SYMBOL_GPL(usb_unpoison_urb); #if 0 /** * usb_poison_anchored_urbs - cease all traffic from an anchor * @anchor: anchor the requests are bound to * * this allows all outstanding URBs to be poisoned starting * from the back of the queue. Newly added URBs will also be * poisoned * * This routine should not be called by a driver after its disconnect * method has returned. */ void usb_poison_anchored_urbs(struct usb_anchor *anchor) { struct urb *victim; spin_lock_irq(&anchor->lock); // anchor->poisoned = 1; /* XXX: Cannot backport */ while (!list_empty(&anchor->urb_list)) { victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list); /* we must make sure the URB isn't freed before we kill it*/ usb_get_urb(victim); spin_unlock_irq(&anchor->lock); /* this will unanchor the URB */ usb_poison_urb(victim); usb_put_urb(victim); spin_lock_irq(&anchor->lock); } spin_unlock_irq(&anchor->lock); } EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs); #endif /** * usb_anchor_empty - is an anchor empty * @anchor: the anchor you want to query * * returns 1 if the anchor has no urbs associated with it */ int usb_anchor_empty(struct usb_anchor *anchor) { return list_empty(&anchor->urb_list); } EXPORT_SYMBOL_GPL(usb_anchor_empty); #endif /* CONFIG_USB */ #endif void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) { /* * Make sure the BAR is actually a memory resource, not an IO resource */ if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { WARN_ON(1); return NULL; } return ioremap_nocache(pci_resource_start(pdev, bar), pci_resource_len(pdev, bar)); } EXPORT_SYMBOL_GPL(pci_ioremap_bar); static unsigned long round_jiffies_common(unsigned long j, int cpu, bool force_up) { int rem; unsigned long original = j; /* * We don't want all cpus firing their timers at once hitting the * same lock or cachelines, so we skew each extra cpu with an extra * 3 jiffies. This 3 jiffies came originally from the mm/ code which * already did this. * The skew is done by adding 3*cpunr, then round, then subtract this * extra offset again. */ j += cpu * 3; rem = j % HZ; /* * If the target jiffie is just after a whole second (which can happen * due to delays of the timer irq, long irq off times etc etc) then * we should round down to the whole second, not up. Use 1/4th second * as cutoff for this rounding as an extreme upper bound for this. * But never round down if @force_up is set. */ if (rem < HZ/4 && !force_up) /* round down */ j = j - rem; else /* round up */ j = j - rem + HZ; /* now that we have rounded, subtract the extra skew again */ j -= cpu * 3; if (j <= jiffies) /* rounding ate our timeout entirely; */ return original; return j; } /** * round_jiffies_up - function to round jiffies up to a full second * @j: the time in (absolute) jiffies that should be rounded * * This is the same as round_jiffies() except that it will never * round down. This is useful for timeouts for which the exact time * of firing does not matter too much, as long as they don't fire too * early. */ unsigned long round_jiffies_up(unsigned long j) { return round_jiffies_common(j, raw_smp_processor_id(), true); } EXPORT_SYMBOL_GPL(round_jiffies_up); void v2_6_28_skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, int size) { skb_fill_page_desc(skb, i, page, off, size); skb->len += size; skb->data_len += size; skb->truesize += size; } EXPORT_SYMBOL_GPL(v2_6_28_skb_add_rx_frag); void tty_write_unlock(struct tty_struct *tty) { mutex_unlock(&tty->atomic_write_lock); wake_up_interruptible_poll(&tty->write_wait, POLLOUT); } int tty_write_lock(struct tty_struct *tty, int ndelay) { if (!mutex_trylock(&tty->atomic_write_lock)) { if (ndelay) return -EAGAIN; if (mutex_lock_interruptible(&tty->atomic_write_lock)) return -ERESTARTSYS; } return 0; } /** * send_prio_char - send priority character * * Send a high priority character to the tty even if stopped * * Locking: none for xchar method, write ordering for write method. */ static int send_prio_char(struct tty_struct *tty, char ch) { int was_stopped = tty->stopped; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) if (tty->ops->send_xchar) { tty->ops->send_xchar(tty, ch); #else if (tty->driver->send_xchar) { tty->driver->send_xchar(tty, ch); #endif return 0; } if (tty_write_lock(tty, 0) < 0) return -ERESTARTSYS; if (was_stopped) start_tty(tty); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) tty->ops->write(tty, &ch, 1); #else tty->driver->write(tty, &ch, 1); #endif if (was_stopped) stop_tty(tty); tty_write_unlock(tty); return 0; } int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) unsigned long flags; #endif int retval; switch (cmd) { case TCXONC: retval = tty_check_change(tty); if (retval) return retval; switch (arg) { case TCOOFF: if (!tty->flow_stopped) { tty->flow_stopped = 1; stop_tty(tty); } break; case TCOON: if (tty->flow_stopped) { tty->flow_stopped = 0; start_tty(tty); } break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) return send_prio_char(tty, STOP_CHAR(tty)); break; case TCION: if (START_CHAR(tty) != __DISABLED_CHAR) return send_prio_char(tty, START_CHAR(tty)); break; default: return -EINVAL; } return 0; case TCFLSH: return tty_perform_flush(tty, arg); case TIOCPKT: { int pktmode; if (tty->driver->type != TTY_DRIVER_TYPE_PTY || tty->driver->subtype != PTY_TYPE_MASTER) return -ENOTTY; if (get_user(pktmode, (int __user *) arg)) return -EFAULT; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) spin_lock_irqsave(&tty->ctrl_lock, flags); #endif if (pktmode) { if (!tty->packet) { tty->packet = 1; tty->link->ctrl_status = 0; } } else tty->packet = 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) spin_unlock_irqrestore(&tty->ctrl_lock, flags); #endif return 0; } default: /* Try the mode commands */ return tty_mode_ioctl(tty, file, cmd, arg); } } EXPORT_SYMBOL_GPL(n_tty_ioctl_helper); /** * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold * @dev: PCI device to prepare * @enable: True to enable wake-up event generation; false to disable * * Many drivers want the device to wake up the system from D3_hot or D3_cold * and this function allows them to set that up cleanly - pci_enable_wake() * should not be called twice in a row to enable wake-up due to PCI PM vs ACPI * ordering constraints. * * This function only returns error code if the device is not capable of * generating PME# from both D3_hot and D3_cold, and the platform is unable to * enable wake-up power for it. */ int pci_wake_from_d3(struct pci_dev *dev, bool enable) { return pci_pme_capable(dev, PCI_D3cold) ? pci_enable_wake(dev, PCI_D3cold, enable) : pci_enable_wake(dev, PCI_D3hot, enable); } EXPORT_SYMBOL_GPL(pci_wake_from_d3); compat-drivers-2012-09-18/compat/compat-2.6.32.c0000644000175000017500000001173112026211315020023 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.32. */ #include #include int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int glbl) { struct dev_addr_list *da; for (da = *list; da != NULL; da = da->next) { if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 && da->da_addrlen == alen) { if (glbl) { int old_glbl = da->da_gusers; da->da_gusers = 1; if (old_glbl) return 0; } da->da_users++; return 0; } } da = kzalloc(sizeof(*da), GFP_ATOMIC); if (da == NULL) return -ENOMEM; memcpy(da->da_addr, addr, alen); da->da_addrlen = alen; da->da_users = 1; da->da_gusers = glbl ? 1 : 0; da->next = *list; *list = da; (*count)++; return 0; } int __dev_addr_delete(struct dev_addr_list **list, int *count, void *addr, int alen, int glbl) { struct dev_addr_list *da; for (; (da = *list) != NULL; list = &da->next) { if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 && alen == da->da_addrlen) { if (glbl) { int old_glbl = da->da_gusers; da->da_gusers = 0; if (old_glbl == 0) break; } if (--da->da_users) return 0; *list = da->next; kfree(da); (*count)--; return 0; } } return -ENOENT; } int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count) { struct dev_addr_list *da, *next; int err = 0; da = *from; while (da != NULL) { next = da->next; if (!da->da_synced) { err = __dev_addr_add(to, to_count, da->da_addr, da->da_addrlen, 0); if (err < 0) break; da->da_synced = 1; da->da_users++; } else if (da->da_users == 1) { __dev_addr_delete(to, to_count, da->da_addr, da->da_addrlen, 0); __dev_addr_delete(from, from_count, da->da_addr, da->da_addrlen, 0); } da = next; } return err; } EXPORT_SYMBOL_GPL(__dev_addr_sync); void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count) { struct dev_addr_list *da, *next; da = *from; while (da != NULL) { next = da->next; if (da->da_synced) { __dev_addr_delete(to, to_count, da->da_addr, da->da_addrlen, 0); da->da_synced = 0; __dev_addr_delete(from, from_count, da->da_addr, da->da_addrlen, 0); } da = next; } } EXPORT_SYMBOL_GPL(__dev_addr_unsync); /* * Nonzero if YEAR is a leap year (every 4 years, * except every 100th isn't, and every 400th is). */ static int __isleap(long year) { return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0); } /* do a mathdiv for long type */ static long math_div(long a, long b) { return a / b - (a % b < 0); } /* How many leap years between y1 and y2, y1 must less or equal to y2 */ static long leaps_between(long y1, long y2) { long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100) + math_div(y1 - 1, 400); long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100) + math_div(y2 - 1, 400); return leaps2 - leaps1; } /* How many days come before each month (0-12). */ static const unsigned short __mon_yday[2][13] = { /* Normal years. */ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, /* Leap years. */ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} }; #define SECS_PER_HOUR (60 * 60) #define SECS_PER_DAY (SECS_PER_HOUR * 24) /** * time_to_tm - converts the calendar time to local broken-down time * * @totalsecs the number of seconds elapsed since 00:00:00 on January 1, 1970, * Coordinated Universal Time (UTC). * @offset offset seconds adding to totalsecs. * @result pointer to struct tm variable to receive broken-down time */ void time_to_tm(time_t totalsecs, int offset, struct tm *result) { long days, rem, y; const unsigned short *ip; days = totalsecs / SECS_PER_DAY; rem = totalsecs % SECS_PER_DAY; rem += offset; while (rem < 0) { rem += SECS_PER_DAY; --days; } while (rem >= SECS_PER_DAY) { rem -= SECS_PER_DAY; ++days; } result->tm_hour = rem / SECS_PER_HOUR; rem %= SECS_PER_HOUR; result->tm_min = rem / 60; result->tm_sec = rem % 60; /* January 1, 1970 was a Thursday. */ result->tm_wday = (4 + days) % 7; if (result->tm_wday < 0) result->tm_wday += 7; y = 1970; while (days < 0 || days >= (__isleap(y) ? 366 : 365)) { /* Guess a corrected year, assuming 365 days per year. */ long yg = y + math_div(days, 365); /* Adjust DAYS and Y to match the guessed year. */ days -= (yg - y) * 365 + leaps_between(y, yg); y = yg; } result->tm_year = y - 1900; result->tm_yday = days; ip = __mon_yday[__isleap(y)]; for (y = 11; days < ip[y]; y--) continue; days -= ip[y]; result->tm_mon = y; result->tm_mday = days + 1; } EXPORT_SYMBOL_GPL(time_to_tm); /* source: kernel/time/timeconv.c*/ compat-drivers-2012-09-18/compat/sch_codel.c0000644000175000017500000002015412026211315017734 0ustar mcgrofmcgrof/* * Codel - The Controlled-Delay Active Queue Management algorithm * * Copyright (C) 2011-2012 Kathleen Nichols * Copyright (C) 2011-2012 Van Jacobson * * Implemented on linux by : * Copyright (C) 2012 Michael D. Taht * Copyright (C) 2012 Eric Dumazet * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, provided that this notice is retained in full, this * software may be distributed under the terms of the GNU General * Public License ("GPL") version 2, in which case the provisions of the * GPL apply INSTEAD OF those given above. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * */ #include #include #include #include #include #include #include #include #include #define DEFAULT_CODEL_LIMIT 1000 struct codel_sched_data { struct codel_params params; struct codel_vars vars; struct codel_stats stats; u32 drop_overlimit; #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) u32 limit; #endif }; /* This is the specific function called from codel_dequeue() * to dequeue a packet from queue. Note: backlog is handled in * codel, we dont need to reduce it here. */ static struct sk_buff *dequeue(struct codel_vars *vars, struct Qdisc *sch) { struct sk_buff *skb = __skb_dequeue(&sch->q); prefetch(&skb->end); /* we'll need skb_shinfo() */ return skb; } static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) { struct codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; skb = codel_dequeue(sch, &q->params, &q->vars, &q->stats, dequeue); /* We cant call qdisc_tree_decrease_qlen() if our qlen is 0, * or HTB crashes. Defer it for next round. */ if (q->stats.drop_count && sch->q.qlen) { qdisc_tree_decrease_qlen(sch, q->stats.drop_count); q->stats.drop_count = 0; } if (skb) qdisc_bstats_update(sch, skb); return skb; } static int codel_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct codel_sched_data *q; q = qdisc_priv(sch); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) if (likely(qdisc_qlen(sch) < q->limit)) { #else if (likely(qdisc_qlen(sch) < sch->limit)) { #endif codel_set_enqueue_time(skb); return qdisc_enqueue_tail(skb, sch); } q->drop_overlimit++; return qdisc_drop(skb, sch); } static const struct nla_policy codel_policy[TCA_CODEL_MAX + 1] = { [TCA_CODEL_TARGET] = { .type = NLA_U32 }, [TCA_CODEL_LIMIT] = { .type = NLA_U32 }, [TCA_CODEL_INTERVAL] = { .type = NLA_U32 }, [TCA_CODEL_ECN] = { .type = NLA_U32 }, }; static int codel_change(struct Qdisc *sch, struct nlattr *opt) { struct codel_sched_data *q = qdisc_priv(sch); struct nlattr *tb[TCA_CODEL_MAX + 1]; unsigned int qlen; int err; if (!opt) return -EINVAL; err = nla_parse_nested(tb, TCA_CODEL_MAX, opt, codel_policy); if (err < 0) return err; sch_tree_lock(sch); if (tb[TCA_CODEL_TARGET]) { u32 target = nla_get_u32(tb[TCA_CODEL_TARGET]); q->params.target = ((u64)target * NSEC_PER_USEC) >> CODEL_SHIFT; } if (tb[TCA_CODEL_INTERVAL]) { u32 interval = nla_get_u32(tb[TCA_CODEL_INTERVAL]); q->params.interval = ((u64)interval * NSEC_PER_USEC) >> CODEL_SHIFT; } if (tb[TCA_CODEL_LIMIT]) #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) q->limit = nla_get_u32(tb[TCA_CODEL_LIMIT]); #else sch->limit = nla_get_u32(tb[TCA_CODEL_LIMIT]); #endif if (tb[TCA_CODEL_ECN]) q->params.ecn = !!nla_get_u32(tb[TCA_CODEL_ECN]); qlen = sch->q.qlen; #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) while (sch->q.qlen > q->limit) { #else while (sch->q.qlen > sch->limit) { #endif struct sk_buff *skb = __skb_dequeue(&sch->q); sch->qstats.backlog -= qdisc_pkt_len(skb); qdisc_drop(skb, sch); } qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen); sch_tree_unlock(sch); return 0; } static int codel_init(struct Qdisc *sch, struct nlattr *opt) { struct codel_sched_data *q = qdisc_priv(sch); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) q->limit = DEFAULT_CODEL_LIMIT; #else sch->limit = DEFAULT_CODEL_LIMIT; #endif codel_params_init(&q->params); codel_vars_init(&q->vars); codel_stats_init(&q->stats); if (opt) { int err = codel_change(sch, opt); if (err) return err; } #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) if (q->limit >= 1) #else if (sch->limit >= 1) #endif sch->flags |= TCQ_F_CAN_BYPASS; else sch->flags &= ~TCQ_F_CAN_BYPASS; return 0; } static int codel_dump(struct Qdisc *sch, struct sk_buff *skb) { struct codel_sched_data *q = qdisc_priv(sch); struct nlattr *opts; opts = nla_nest_start(skb, TCA_OPTIONS); if (opts == NULL) goto nla_put_failure; if (nla_put_u32(skb, TCA_CODEL_TARGET, codel_time_to_us(q->params.target)) || nla_put_u32(skb, TCA_CODEL_LIMIT, #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) q->limit) || #else sch->limit) || #endif nla_put_u32(skb, TCA_CODEL_INTERVAL, codel_time_to_us(q->params.interval)) || nla_put_u32(skb, TCA_CODEL_ECN, q->params.ecn)) goto nla_put_failure; return nla_nest_end(skb, opts); nla_put_failure: nla_nest_cancel(skb, opts); return -1; } static int codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { const struct codel_sched_data *q = qdisc_priv(sch); struct tc_codel_xstats st = { .maxpacket = q->stats.maxpacket, .count = q->vars.count, .lastcount = q->vars.lastcount, .drop_overlimit = q->drop_overlimit, .ldelay = codel_time_to_us(q->vars.ldelay), .dropping = q->vars.dropping, .ecn_mark = q->stats.ecn_mark, }; if (q->vars.dropping) { codel_tdiff_t delta = q->vars.drop_next - codel_get_time(); if (delta >= 0) st.drop_next = codel_time_to_us(delta); else st.drop_next = -codel_time_to_us(-delta); } return gnet_stats_copy_app(d, &st, sizeof(st)); } static void codel_reset(struct Qdisc *sch) { struct codel_sched_data *q = qdisc_priv(sch); qdisc_reset_queue(sch); codel_vars_init(&q->vars); } static struct Qdisc_ops codel_qdisc_ops __read_mostly = { .id = "codel", .priv_size = sizeof(struct codel_sched_data), .enqueue = codel_qdisc_enqueue, .dequeue = codel_qdisc_dequeue, #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)) .peek = qdisc_peek_dequeued, #endif .init = codel_init, .reset = codel_reset, .change = codel_change, .dump = codel_dump, .dump_stats = codel_dump_stats, .owner = THIS_MODULE, }; static int __init codel_module_init(void) { return register_qdisc(&codel_qdisc_ops); } static void __exit codel_module_exit(void) { unregister_qdisc(&codel_qdisc_ops); } module_init(codel_module_init) module_exit(codel_module_exit) MODULE_DESCRIPTION("Controlled Delay queue discipline"); MODULE_AUTHOR("Dave Taht"); MODULE_AUTHOR("Eric Dumazet"); MODULE_LICENSE("Dual BSD/GPL"); compat-drivers-2012-09-18/compat/compat-2.6.33.c0000644000175000017500000000753112026211315020027 0ustar mcgrofmcgrof/* * Copyright 2009 Hauke Mehrtens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.33. */ #include #if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE) /** * pccard_loop_tuple() - loop over tuples in the CIS * @s: the struct pcmcia_socket where the card is inserted * @function: the device function we loop for * @code: which CIS code shall we look for? * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) * @priv_data: private data to be passed to the loop_tuple function. * @loop_tuple: function to call for each CIS entry of type @function. IT * gets passed the raw tuple, the paresed tuple (if @parse is * set) and @priv_data. * * pccard_loop_tuple() loops over all CIS entries of type @function, and * calls the @loop_tuple function for each entry. If the call to @loop_tuple * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. */ int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, cisparse_t *parse, void *priv_data, int (*loop_tuple) (tuple_t *tuple, cisparse_t *parse, void *priv_data)) { tuple_t tuple; cisdata_t *buf; int ret; buf = kzalloc(256, GFP_KERNEL); if (buf == NULL) { dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); return -ENOMEM; } tuple.TupleData = buf; tuple.TupleDataMax = 255; tuple.TupleOffset = 0; tuple.DesiredTuple = code; tuple.Attributes = 0; ret = pccard_get_first_tuple(s, function, &tuple); while (!ret) { if (pccard_get_tuple_data(s, &tuple)) goto next_entry; if (parse) if (pcmcia_parse_tuple(&tuple, parse)) goto next_entry; ret = loop_tuple(&tuple, parse, priv_data); if (!ret) break; next_entry: ret = pccard_get_next_tuple(s, function, &tuple); } kfree(buf); return ret; } EXPORT_SYMBOL_GPL(pccard_loop_tuple); /* Source: drivers/pcmcia/cistpl.c */ #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) struct pcmcia_loop_mem { struct pcmcia_device *p_dev; void *priv_data; int (*loop_tuple) (struct pcmcia_device *p_dev, tuple_t *tuple, void *priv_data); }; /** * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config() * * pcmcia_do_loop_tuple() is the internal callback for the call from * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred * by a struct pcmcia_cfg_mem. */ static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv) { struct pcmcia_loop_mem *loop = priv; return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data); }; /** * pcmcia_loop_tuple() - loop over tuples in the CIS * @p_dev: the struct pcmcia_device which we need to loop for. * @code: which CIS code shall we look for? * @priv_data: private data to be passed to the loop_tuple function. * @loop_tuple: function to call for each CIS entry of type @function. IT * gets passed the raw tuple and @priv_data. * * pcmcia_loop_tuple() loops over all CIS entries of type @function, and * calls the @loop_tuple function for each entry. If the call to @loop_tuple * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. */ int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, int (*loop_tuple) (struct pcmcia_device *p_dev, tuple_t *tuple, void *priv_data), void *priv_data) { struct pcmcia_loop_mem loop = { .p_dev = p_dev, .loop_tuple = loop_tuple, .priv_data = priv_data}; return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL, &loop, pcmcia_do_loop_tuple); } EXPORT_SYMBOL_GPL(pcmcia_loop_tuple); /* Source: drivers/pcmcia/pcmcia_resource.c */ #endif /* CONFIG_PCMCIA */ #endif /* CONFIG_PCCARD */ compat-drivers-2012-09-18/compat/compat-2.6.34.h0000644000175000017500000000063612026211315020034 0ustar mcgrofmcgrof#ifndef LINUX_26_34_COMPAT_PRIVATE_H #define LINUX_26_34_COMPAT_PRIVATE_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) #include void init_compat_mmc_pm_flags(void); #else /* Kernels >= 2.6.34 */ static inline void init_compat_mmc_pm_flags(void) { } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) */ #endif /* LINUX_26_34_COMPAT_PRIVATE_H */ compat-drivers-2012-09-18/compat/compat-2.6.29.c0000644000175000017500000001124212026211315020026 0ustar mcgrofmcgrof/* * Copyright 2007-2010 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.29. */ #include #include #include /* * If you don't see your net_device_ops implemented on * netdev_attach_ops() then you are shit out of luck and * you must do the nasty ifdef magic, unless you figure * out a way to squeze your hacks into this routine :) */ void netdev_attach_ops(struct net_device *dev, const struct net_device_ops *ops) { dev->open = ops->ndo_open; dev->init = ops->ndo_init; dev->stop = ops->ndo_stop; dev->hard_start_xmit = ops->ndo_start_xmit; dev->change_rx_flags = ops->ndo_change_rx_flags; dev->set_multicast_list = ops->ndo_set_multicast_list; dev->validate_addr = ops->ndo_validate_addr; dev->do_ioctl = ops->ndo_do_ioctl; dev->set_config = ops->ndo_set_config; dev->change_mtu = ops->ndo_change_mtu; dev->set_mac_address = ops->ndo_set_mac_address; dev->tx_timeout = ops->ndo_tx_timeout; if (ops->ndo_get_stats) dev->get_stats = ops->ndo_get_stats; dev->vlan_rx_register = ops->ndo_vlan_rx_register; dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid; dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid; #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = ops->ndo_poll_controller; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) dev->select_queue = ops->ndo_select_queue; #endif } EXPORT_SYMBOL_GPL(netdev_attach_ops); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) #if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE) /** * usb_unpoison_anchored_urbs - let an anchor be used successfully again * @anchor: anchor the requests are bound to * * Reverses the effect of usb_poison_anchored_urbs * the anchor can be used normally after it returns */ void usb_unpoison_anchored_urbs(struct usb_anchor *anchor) { unsigned long flags; struct urb *lazarus; spin_lock_irqsave(&anchor->lock, flags); list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) { usb_unpoison_urb(lazarus); } //anchor->poisoned = 0; /* XXX: cannot backport */ spin_unlock_irqrestore(&anchor->lock, flags); } EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs); #endif /* CONFIG_USB */ #endif /** * eth_mac_addr - set new Ethernet hardware address * @dev: network device * @p: socket address * Change hardware address of device. * * This doesn't change hardware matching, so needs to be overridden * for most real devices. */ int eth_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; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); return 0; } EXPORT_SYMBOL_GPL(eth_mac_addr); /** * eth_change_mtu - set new MTU size * @dev: network device * @new_mtu: new Maximum Transfer Unit * * Allow changing MTU size. Needs to be overridden for devices * supporting jumbo frames. */ int eth_change_mtu(struct net_device *dev, int new_mtu) { if (new_mtu < 68 || new_mtu > ETH_DATA_LEN) return -EINVAL; dev->mtu = new_mtu; return 0; } EXPORT_SYMBOL_GPL(eth_change_mtu); int eth_validate_addr(struct net_device *dev) { if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; return 0; } EXPORT_SYMBOL_GPL(eth_validate_addr); /* Source: net/ethernet/eth.c */ #define NETREG_DUMMY 5 /** * init_dummy_netdev - init a dummy network device for NAPI * @dev: device to init * * This takes a network device structure and initialize the minimum * amount of fields so it can be used to schedule NAPI polls without * registering a full blown interface. This is to be used by drivers * that need to tie several hardware interfaces to a single NAPI * poll scheduler due to HW limitations. */ int init_dummy_netdev(struct net_device *dev) { /* Clear everything. Note we don't initialize spinlocks * are they aren't supposed to be taken by any of the * NAPI code and this dummy netdev is supposed to be * only ever used for NAPI polls */ memset(dev, 0, sizeof(struct net_device)); /* make sure we BUG if trying to hit standard * register/unregister code path */ dev->reg_state = NETREG_DUMMY; /* initialize the ref count */ atomic_set(&dev->refcnt, 1); #ifdef CONFIG_NETPOLL /* NAPI wants this */ INIT_LIST_HEAD(&dev->napi_list); #endif /* a dummy interface is started by default */ set_bit(__LINK_STATE_PRESENT, &dev->state); set_bit(__LINK_STATE_START, &dev->state); return 0; } EXPORT_SYMBOL_GPL(init_dummy_netdev); /* Source: net/core/dev.c */ compat-drivers-2012-09-18/compat/compat-2.6.27.c0000644000175000017500000001456212026211315020034 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.27 */ #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) #include #include #include #include #endif /* rfkill notification chain */ #define RFKILL_STATE_CHANGED 0x0001 /* state of a normal rfkill switch has changed */ /* * e5899e1b7d73e67de758a32174a859cc2586c0b9 made pci_pme_capable() external, * it was defined internally, some drivers want access to this information. * * Unfortunately the old kernels do not have ->pm_cap or ->pme_support so * we have to call the PCI routines directly. */ /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. * @state: PCI state from which device will issue PME#. * * This is the backport code for older kernels for compat-drivers, we read stuff * from the initialization stuff from pci_pm_init(). */ bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) { int pm; u16 pmc = 0; u16 pme_support; /* as from the pci dev */ /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); if (!pm) return false; if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", pmc & PCI_PM_CAP_VER_MASK); return false; } pmc &= PCI_PM_CAP_PME_MASK; if (!pmc) return false; pme_support = pmc >> PCI_PM_CAP_PME_SHIFT; /* Check device's ability to generate PME# */ return !!(pme_support & (1 << state)); } EXPORT_SYMBOL_GPL(pci_pme_capable); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) /** * mmc_align_data_size - pads a transfer size to a more optimal value * @card: the MMC card associated with the data transfer * @sz: original transfer size * * Pads the original data size with a number of extra bytes in * order to avoid controller bugs and/or performance hits * (e.g. some controllers revert to PIO for certain sizes). * * Returns the improved size, which might be unmodified. * * Note that this function is only relevant when issuing a * single scatter gather entry. */ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) { /* * FIXME: We don't have a system for the controller to tell * the core about its problems yet, so for now we just 32-bit * align the size. */ sz = ((sz + 3) / 4) * 4; return sz; } EXPORT_SYMBOL_GPL(mmc_align_data_size); /* * Calculate the maximum byte mode transfer size */ static inline unsigned int sdio_max_byte_size(struct sdio_func *func) { unsigned int mval = (unsigned int) min(func->card->host->max_seg_size, func->card->host->max_blk_size); mval = min(mval, func->max_blksize); return min(mval, 512u); /* maximum size for byte mode */ } /** * sdio_align_size - pads a transfer size to a more optimal value * @func: SDIO function * @sz: original transfer size * * Pads the original data size with a number of extra bytes in * order to avoid controller bugs and/or performance hits * (e.g. some controllers revert to PIO for certain sizes). * * If possible, it will also adjust the size so that it can be * handled in just a single request. * * Returns the improved size, which might be unmodified. */ unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz) { unsigned int orig_sz; unsigned int blk_sz, byte_sz; unsigned chunk_sz; orig_sz = sz; /* * Do a first check with the controller, in case it * wants to increase the size up to a point where it * might need more than one block. */ sz = mmc_align_data_size(func->card, sz); /* * If we can still do this with just a byte transfer, then * we're done. */ if (sz <= sdio_max_byte_size(func)) return sz; if (func->card->cccr.multi_block) { /* * Check if the transfer is already block aligned */ if ((sz % func->cur_blksize) == 0) return sz; /* * Realign it so that it can be done with one request, * and recheck if the controller still likes it. */ blk_sz = ((sz + func->cur_blksize - 1) / func->cur_blksize) * func->cur_blksize; blk_sz = mmc_align_data_size(func->card, blk_sz); /* * This value is only good if it is still just * one request. */ if ((blk_sz % func->cur_blksize) == 0) return blk_sz; /* * We failed to do one request, but at least try to * pad the remainder properly. */ byte_sz = mmc_align_data_size(func->card, sz % func->cur_blksize); if (byte_sz <= sdio_max_byte_size(func)) { blk_sz = sz / func->cur_blksize; return blk_sz * func->cur_blksize + byte_sz; } } else { /* * We need multiple requests, so first check that the * controller can handle the chunk size; */ chunk_sz = mmc_align_data_size(func->card, sdio_max_byte_size(func)); if (chunk_sz == sdio_max_byte_size(func)) { /* * Fix up the size of the remainder (if any) */ byte_sz = orig_sz % chunk_sz; if (byte_sz) { byte_sz = mmc_align_data_size(func->card, byte_sz); } return (orig_sz / chunk_sz) * chunk_sz + byte_sz; } } /* * The controller is simply incapable of transferring the size * we want in decent manner, so just return the original size. */ return orig_sz; } EXPORT_SYMBOL_GPL(sdio_align_size); #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */ #ifdef CONFIG_DEBUG_FS /* * Backport of debugfs_remove_recursive() without using the internals globals * which are used by the kernel's version with: * simple_release_fs(&debugfs_mount, &debugfs_mount_count); */ void debugfs_remove_recursive(struct dentry *dentry) { struct dentry *last = NULL; /* Sanity checks */ if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode) return; while (dentry != last) { struct dentry *child = dentry; /* Find a child without children */ while (!list_empty(&child->d_subdirs)) child = list_entry(child->d_subdirs.next, struct dentry, d_u.d_child); /* Bail out if we already tried to remove that entry */ if (child == last) return; last = child; debugfs_remove(child); } } EXPORT_SYMBOL_GPL(debugfs_remove_recursive); #endif /* CONFIG_DEBUG_FS */ compat-drivers-2012-09-18/compat/main.c0000644000175000017500000000347112026211315016740 0ustar mcgrofmcgrof#include MODULE_AUTHOR("Luis R. Rodriguez"); MODULE_DESCRIPTION("Kernel compatibility module"); MODULE_LICENSE("GPL"); #ifndef COMPAT_BASE #error "You need a COMPAT_BASE" #endif #ifndef COMPAT_BASE_TREE #error "You need a COMPAT_BASE_TREE" #endif #ifndef COMPAT_BASE_TREE_VERSION #error "You need a COMPAT_BASE_TREE_VERSION" #endif #ifndef COMPAT_VERSION #error "You need a COMPAT_VERSION" #endif static char *compat_base = COMPAT_BASE; static char *compat_base_tree = COMPAT_BASE_TREE; static char *compat_base_tree_version = COMPAT_BASE_TREE_VERSION; static char *compat_version = COMPAT_VERSION; module_param(compat_base, charp, 0400); MODULE_PARM_DESC(compat_base_tree, "The upstream verion of compat.git used"); module_param(compat_base_tree, charp, 0400); MODULE_PARM_DESC(compat_base_tree, "The upstream tree used as base for this backport"); module_param(compat_base_tree_version, charp, 0400); MODULE_PARM_DESC(compat_base_tree_version, "The git-describe of the upstream base tree"); module_param(compat_version, charp, 0400); MODULE_PARM_DESC(compat_version, "Version of the kernel compat backport work"); void compat_dependency_symbol(void) { } EXPORT_SYMBOL_GPL(compat_dependency_symbol); static int __init compat_init(void) { compat_pm_qos_power_init(); compat_system_workqueue_create(); init_compat_mmc_pm_flags(); printk(KERN_INFO COMPAT_PROJECT " backport release: " COMPAT_VERSION "\n"); printk(KERN_INFO "Backport based on " COMPAT_BASE_TREE " " COMPAT_BASE_TREE_VERSION "\n"); printk(KERN_INFO "compat.git: " COMPAT_BASE_TREE "\n"); return 0; } module_init(compat_init); static void __exit compat_exit(void) { compat_pm_qos_power_deinit(); compat_system_workqueue_destroy(); return; } module_exit(compat_exit); compat-drivers-2012-09-18/compat/scripts/0000755000175000017500000000000012025673354017350 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/compat/scripts/gen-compat-config.sh0000755000175000017500000000540512025673354023210 0ustar mcgrofmcgrof#!/bin/bash # Copyright 2012 Luis R. Rodriguez # Copyright 2012 Hauke Mehrtens # # This generates a bunch of CONFIG_COMPAT_KERNEL_2_6_22 # CONFIG_COMPAT_KERNEL_3_0 .. etc for each kernel release you need an object # for. # # Note: this is part of the compat.git project, not compat-drivers, # send patches against compat.git. if [[ ! -f ${KLIB_BUILD}/Makefile ]]; then exit fi # Actual kernel version KERNEL_VERSION=$(${MAKE} -C ${KLIB_BUILD} kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p') # 3.0 kernel stuff COMPAT_LATEST_VERSION="7" KERNEL_SUBLEVEL="-1" # Note that this script will export all variables explicitly, # trying to export all with a blanket "export" statement at # the top of the generated file causes the build to slow down # by an order of magnitude. if [[ ${KERNEL_VERSION} -eq "3" ]]; then KERNEL_SUBLEVEL=$(${MAKE} -C ${KLIB_BUILD} kernelversion | sed -n 's/^3\.\([0-9]\+\).*/\1/p') else COMPAT_26LATEST_VERSION="39" KERNEL_26SUBLEVEL=$(${MAKE} -C ${KLIB_BUILD} kernelversion | sed -n 's/^2\.6\.\([0-9]\+\).*/\1/p') let KERNEL_26SUBLEVEL=${KERNEL_26SUBLEVEL}+1 for i in $(seq ${KERNEL_26SUBLEVEL} ${COMPAT_26LATEST_VERSION}); do eval CONFIG_COMPAT_KERNEL_2_6_${i}=y echo "export CONFIG_COMPAT_KERNEL_2_6_${i}=y" done fi let KERNEL_SUBLEVEL=${KERNEL_SUBLEVEL}+1 for i in $(seq ${KERNEL_SUBLEVEL} ${COMPAT_LATEST_VERSION}); do eval CONFIG_COMPAT_KERNEL_3_${i}=y echo "export CONFIG_COMPAT_KERNEL_3_${i}=y" done # The purpose of these seem to be the inverse of the above other varibales. # The RHEL checks seem to annotate the existance of RHEL minor versions. RHEL_MAJOR=$(grep ^RHEL_MAJOR ${KLIB_BUILD}/Makefile | sed -n 's/.*= *\(.*\)/\1/p') if [[ ! -z ${RHEL_MAJOR} ]]; then RHEL_MINOR=$(grep ^RHEL_MINOR ${KLIB_BUILD}/Makefile | sed -n 's/.*= *\(.*\)/\1/p') for i in $(seq 0 ${RHEL_MINOR}); do eval CONFIG_COMPAT_RHEL_${RHEL_MAJOR}_${i}=y echo "export CONFIG_COMPAT_RHEL_${RHEL_MAJOR}_${i}=y" done fi if [[ ${CONFIG_COMPAT_KERNEL_2_6_33} = "y" ]]; then if [[ ! ${CONFIG_COMPAT_RHEL_6_0} = "y" ]]; then echo "export CONFIG_COMPAT_FIRMWARE_CLASS=m" fi fi if [[ ${CONFIG_COMPAT_KERNEL_2_6_36} = "y" ]]; then if [[ ! ${CONFIG_COMPAT_RHEL_6_1} = "y" ]]; then echo "export CONFIG_COMPAT_KFIFO=y" fi fi if [[ ${CONFIG_COMPAT_KERNEL_3_5} = "y" ]]; then # We don't have 2.6.24 backport support yet for Codel / FQ CoDel # For those who want to try this is what is required that I can tell # so far: # * struct Qdisc_ops # - init and change callback ops use a different argument dataype # - you need to parse data received from userspace differently if [[ ${CONFIG_COMPAT_KERNEL_2_6_25} != "y" ]]; then echo "export CONFIG_COMPAT_NET_SCH_CODEL=m" echo "export CONFIG_COMPAT_NET_SCH_FQ_CODEL=m" fi fi compat-drivers-2012-09-18/compat/scripts/compat_firmware_install0000755000175000017500000000070512025673354024205 0ustar mcgrofmcgrof#!/bin/sh if [ -f /usr/bin/lsb_release ]; then LSB_RED_ID=$(/usr/bin/lsb_release -i -s) else LSB_RED_ID="Unknown" fi case $LSB_RED_ID in "Ubuntu") mkdir -p /lib/udev/ /lib/udev/rules.d/ cp udev/ubuntu/compat_firmware.sh /lib/udev/ cp udev/50-compat_firmware.rules /lib/udev/rules.d/ ;; *) mkdir -p /lib/udev/ /lib/udev/rules.d/ cp udev/compat_firmware.sh /lib/udev/ cp udev/50-compat_firmware.rules /lib/udev/rules.d/ ;; esac compat-drivers-2012-09-18/compat/scripts/skip-colors0000755000175000017500000000006512025673354021544 0ustar mcgrofmcgrof#!/bin/bash perl -pe 's|(\e)\[(\d+)(;*)(\d*)(\w)||g' compat-drivers-2012-09-18/compat/scripts/gen-compat-autoconf.sh0000755000175000017500000000654312025673354023565 0ustar mcgrofmcgrof#!/bin/bash # # Copyright 2012 Luis R. Rodriguez # Copyright 2011 Hauke Mehrtens # Copyright 2011 John W. Linville # # Use this to parse a small .config equivalent looking file to generate # our own autoconf.h. This file has defines for each config option # just like the kernels include/linux/autoconf.h # # XXX: consider using scripts/kconfig/confdata.c instead. # On the downside this would require the user to have libc though. # This indicates which is the oldest kernel we support # Update this if you are adding support for older kernels. OLDEST_KERNEL_SUPPORTED="2.6.24" if [ $# -ne 1 ]; then echo "Usage $0 config-file" exit fi COMPAT_CONFIG="$1" if [ ! -f $COMPAT_CONFIG ]; then echo "File $1 is not a file" exit fi # Defines a CONFIG_ option if not defined yet, this helps respect # linux/autoconf.h function define_config { VAR=$1 VALUE=$2 case $VALUE in n) # Try to undefine it echo "#undef $VAR" ;; y) echo "#ifndef $VAR" echo "#define $VAR 1" echo "#endif /* $VAR */" ;; m) echo "#ifndef $VAR" echo "#define $VAR 1" echo "#endif /* $VAR */" ;; *) # Assume string # XXX: add better checks to make sure what was on # the right was indeed a string echo "#ifndef $VAR" echo "#define $VAR \"$VALUE\"" echo "#endif /* $VAR */" ;; esac } function kernel_version_req { VERSION=$(echo $1 | sed -e 's/\./,/g') echo "#if (LINUX_VERSION_CODE < KERNEL_VERSION($VERSION))" echo "#error compat requirement: Linux >= $VERSION" echo "#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION($VERSION) */" } cat <= \3)/' -e 's/\(#ifdef \)\(CONFIG_[^:space:]*\)/#if defined(\2) || defined(\2_MODULE)/' continue ;; 'ifndef+CONFIG_'* ) echo "#$i" | sed -e 's/+/ /' -e 's/\(ifndef CONFIG_COMPAT_KERNEL_3_\)\([0-9]*\)/if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,\2,0))/' -e 's/\(ifndef CONFIG_COMPAT_KERNEL_2_6_\)\([0-9]*\)/if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,\2))/' -e 's/\(ifndef CONFIG_COMPAT_RHEL_\)\([0-9]*\)_\([0-9]*\)/if (!defined(RHEL_MAJOR) || RHEL_MAJOR != \2 || RHEL_MINOR < \3)/' -e 's/\(#ifndef \)\(CONFIG_[^:space:]*\)/#if !defined(\2) \&\& !defined(\2_MODULE)/' continue ;; 'else+#CONFIG_'* | 'endif+#CONFIG_'* ) echo "#$i */" |sed -e 's/+#/ \/* /g' continue ;; CONFIG_* ) # Get the element on the left of the "=" VAR=$(echo $i | cut -d"=" -f 1) # Get the element on the right of the "=" VALUE=$(echo $i | cut -d"=" -f 2) # Any other module which can *definitely* be built as a module goes here define_config $VAR $VALUE continue ;; esac done echo "#endif /* COMPAT_AUTOCONF_INCLUDED */" compat-drivers-2012-09-18/compat/sch_fq_codel_core.c0000644000175000017500000004033412026211315021434 0ustar mcgrofmcgrof/* * Fair Queue CoDel discipline * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Copyright (C) 2012 Eric Dumazet */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Fair Queue CoDel. * * Principles : * Packets are classified (internal classifier or external) on flows. * This is a Stochastic model (as we use a hash, several flows * might be hashed on same slot) * Each flow has a CoDel managed queue. * Flows are linked onto two (Round Robin) lists, * so that new flows have priority on old ones. * * For a given flow, packets are not reordered (CoDel uses a FIFO) * head drops only. * ECN capability is on by default. * Low memory footprint (64 bytes per flow) */ struct fq_codel_flow { struct sk_buff *head; struct sk_buff *tail; struct list_head flowchain; int deficit; u32 dropped; /* number of drops (or ECN marks) on this flow */ struct codel_vars cvars; }; /* please try to keep this structure <= 64 bytes */ struct fq_codel_sched_data { struct tcf_proto *filter_list; /* optional external classifier */ struct fq_codel_flow *flows; /* Flows table [flows_cnt] */ u32 *backlogs; /* backlog table [flows_cnt] */ u32 flows_cnt; /* number of flows */ u32 perturbation; /* hash perturbation */ u32 quantum; /* psched_mtu(qdisc_dev(sch)); */ struct codel_params cparams; struct codel_stats cstats; u32 drop_overlimit; u32 new_flow_count; struct list_head new_flows; /* list of new flows */ struct list_head old_flows; /* list of old flows */ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) u32 limit; #endif }; static unsigned int fq_codel_hash(const struct fq_codel_sched_data *q, const struct sk_buff *skb) { struct flow_keys keys; unsigned int hash; skb_flow_dissect(skb, &keys); hash = jhash_3words((__force u32)keys.dst, (__force u32)keys.src ^ keys.ip_proto, (__force u32)keys.ports, q->perturbation); return ((u64)hash * q->flows_cnt) >> 32; } static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct tcf_result res; int result; if (TC_H_MAJ(skb->priority) == sch->handle && TC_H_MIN(skb->priority) > 0 && TC_H_MIN(skb->priority) <= q->flows_cnt) return TC_H_MIN(skb->priority); if (!q->filter_list) return fq_codel_hash(q, skb) + 1; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; result = tc_classify(skb, q->filter_list, &res); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { case TC_ACT_STOLEN: case TC_ACT_QUEUED: *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; case TC_ACT_SHOT: return 0; } #endif if (TC_H_MIN(res.classid) <= q->flows_cnt) return TC_H_MIN(res.classid); } return 0; } /* helper functions : might be changed when/if skb use a standard list_head */ /* remove one skb from head of slot queue */ static inline struct sk_buff *dequeue_head(struct fq_codel_flow *flow) { struct sk_buff *skb = flow->head; flow->head = skb->next; skb->next = NULL; return skb; } /* add skb to flow queue (tail add) */ static inline void flow_queue_add(struct fq_codel_flow *flow, struct sk_buff *skb) { if (flow->head == NULL) flow->head = skb; else flow->tail->next = skb; flow->tail = skb; skb->next = NULL; } static unsigned int fq_codel_drop(struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; unsigned int maxbacklog = 0, idx = 0, i, len; struct fq_codel_flow *flow; /* Queue is full! Find the fat flow and drop packet from it. * This might sound expensive, but with 1024 flows, we scan * 4KB of memory, and we dont need to handle a complex tree * in fast path (packet queue/enqueue) with many cache misses. */ for (i = 0; i < q->flows_cnt; i++) { if (q->backlogs[i] > maxbacklog) { maxbacklog = q->backlogs[i]; idx = i; } } flow = &q->flows[idx]; skb = dequeue_head(flow); len = qdisc_pkt_len(skb); q->backlogs[idx] -= len; kfree_skb(skb); sch->q.qlen--; sch->qstats.drops++; sch->qstats.backlog -= len; flow->dropped++; return idx; } static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); unsigned int idx; struct fq_codel_flow *flow; int uninitialized_var(ret); idx = fq_codel_classify(skb, sch, &ret); if (idx == 0) { if (ret & __NET_XMIT_BYPASS) sch->qstats.drops++; kfree_skb(skb); return ret; } idx--; codel_set_enqueue_time(skb); flow = &q->flows[idx]; flow_queue_add(flow, skb); q->backlogs[idx] += qdisc_pkt_len(skb); sch->qstats.backlog += qdisc_pkt_len(skb); if (list_empty(&flow->flowchain)) { list_add_tail(&flow->flowchain, &q->new_flows); codel_vars_init(&flow->cvars); q->new_flow_count++; flow->deficit = q->quantum; flow->dropped = 0; } #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) if (++sch->q.qlen < q->limit) #else if (++sch->q.qlen < sch->limit) #endif return NET_XMIT_SUCCESS; q->drop_overlimit++; /* Return Congestion Notification only if we dropped a packet * from this flow. */ if (fq_codel_drop(sch) == idx) return NET_XMIT_CN; /* As we dropped a packet, better let upper stack know this */ qdisc_tree_decrease_qlen(sch, 1); return NET_XMIT_SUCCESS; } /* This is the specific function called from codel_dequeue() * to dequeue a packet from queue. Note: backlog is handled in * codel, we dont need to reduce it here. */ static struct sk_buff *dequeue(struct codel_vars *vars, struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct fq_codel_flow *flow; struct sk_buff *skb = NULL; flow = container_of(vars, struct fq_codel_flow, cvars); if (flow->head) { skb = dequeue_head(flow); q->backlogs[flow - q->flows] -= qdisc_pkt_len(skb); sch->q.qlen--; } return skb; } static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; struct fq_codel_flow *flow; struct list_head *head; u32 prev_drop_count, prev_ecn_mark; begin: head = &q->new_flows; if (list_empty(head)) { head = &q->old_flows; if (list_empty(head)) return NULL; } flow = list_first_entry(head, struct fq_codel_flow, flowchain); if (flow->deficit <= 0) { flow->deficit += q->quantum; list_move_tail(&flow->flowchain, &q->old_flows); goto begin; } prev_drop_count = q->cstats.drop_count; prev_ecn_mark = q->cstats.ecn_mark; skb = codel_dequeue(sch, &q->cparams, &flow->cvars, &q->cstats, dequeue); flow->dropped += q->cstats.drop_count - prev_drop_count; flow->dropped += q->cstats.ecn_mark - prev_ecn_mark; if (!skb) { /* force a pass through old_flows to prevent starvation */ if ((head == &q->new_flows) && !list_empty(&q->old_flows)) list_move_tail(&flow->flowchain, &q->old_flows); else list_del_init(&flow->flowchain); goto begin; } qdisc_bstats_update(sch, skb); flow->deficit -= qdisc_pkt_len(skb); /* We cant call qdisc_tree_decrease_qlen() if our qlen is 0, * or HTB crashes. Defer it for next round. */ if (q->cstats.drop_count && sch->q.qlen) { qdisc_tree_decrease_qlen(sch, q->cstats.drop_count); q->cstats.drop_count = 0; } return skb; } static void fq_codel_reset(struct Qdisc *sch) { struct sk_buff *skb; while ((skb = fq_codel_dequeue(sch)) != NULL) kfree_skb(skb); } static const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = { [TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 }, [TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 }, [TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 }, [TCA_FQ_CODEL_ECN] = { .type = NLA_U32 }, [TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 }, [TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 }, }; static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct nlattr *tb[TCA_FQ_CODEL_MAX + 1]; int err; if (!opt) return -EINVAL; err = nla_parse_nested(tb, TCA_FQ_CODEL_MAX, opt, fq_codel_policy); if (err < 0) return err; if (tb[TCA_FQ_CODEL_FLOWS]) { if (q->flows) return -EINVAL; q->flows_cnt = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]); if (!q->flows_cnt || q->flows_cnt > 65536) return -EINVAL; } sch_tree_lock(sch); if (tb[TCA_FQ_CODEL_TARGET]) { u64 target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]); q->cparams.target = (target * NSEC_PER_USEC) >> CODEL_SHIFT; } if (tb[TCA_FQ_CODEL_INTERVAL]) { u64 interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]); q->cparams.interval = (interval * NSEC_PER_USEC) >> CODEL_SHIFT; } if (tb[TCA_FQ_CODEL_LIMIT]) #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) q->limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]); #else sch->limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]); #endif if (tb[TCA_FQ_CODEL_ECN]) q->cparams.ecn = !!nla_get_u32(tb[TCA_FQ_CODEL_ECN]); if (tb[TCA_FQ_CODEL_QUANTUM]) q->quantum = max(256U, nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM])); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) while (sch->q.qlen > q->limit) { #else while (sch->q.qlen > sch->limit) { #endif struct sk_buff *skb = fq_codel_dequeue(sch); kfree_skb(skb); q->cstats.drop_count++; } qdisc_tree_decrease_qlen(sch, q->cstats.drop_count); q->cstats.drop_count = 0; sch_tree_unlock(sch); return 0; } static void *fq_codel_zalloc(size_t sz) { void *ptr = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN); if (!ptr) ptr = vzalloc(sz); return ptr; } static void fq_codel_free(void *addr) { if (addr) { if (is_vmalloc_addr(addr)) vfree(addr); else kfree(addr); } } static void fq_codel_destroy(struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) tcf_destroy_chain(&q->filter_list); #else tcf_destroy_chain(q->filter_list); #endif fq_codel_free(q->backlogs); fq_codel_free(q->flows); } static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt) { struct fq_codel_sched_data *q = qdisc_priv(sch); int i; #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) q->limit = 10*1024; #else sch->limit = 10*1024; #endif q->flows_cnt = 1024; q->quantum = psched_mtu(qdisc_dev(sch)); q->perturbation = net_random(); INIT_LIST_HEAD(&q->new_flows); INIT_LIST_HEAD(&q->old_flows); codel_params_init(&q->cparams); codel_stats_init(&q->cstats); q->cparams.ecn = true; if (opt) { int err = fq_codel_change(sch, opt); if (err) return err; } if (!q->flows) { q->flows = fq_codel_zalloc(q->flows_cnt * sizeof(struct fq_codel_flow)); if (!q->flows) return -ENOMEM; q->backlogs = fq_codel_zalloc(q->flows_cnt * sizeof(u32)); if (!q->backlogs) { fq_codel_free(q->flows); return -ENOMEM; } for (i = 0; i < q->flows_cnt; i++) { struct fq_codel_flow *flow = q->flows + i; INIT_LIST_HEAD(&flow->flowchain); } } #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) if (q->limit >= 1) #else if (sch->limit >= 1) #endif sch->flags |= TCQ_F_CAN_BYPASS; else sch->flags &= ~TCQ_F_CAN_BYPASS; return 0; } static int fq_codel_dump(struct Qdisc *sch, struct sk_buff *skb) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct nlattr *opts; opts = nla_nest_start(skb, TCA_OPTIONS); if (opts == NULL) goto nla_put_failure; if (nla_put_u32(skb, TCA_FQ_CODEL_TARGET, codel_time_to_us(q->cparams.target)) || nla_put_u32(skb, TCA_FQ_CODEL_LIMIT, #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39)) q->limit) || #else sch->limit) || #endif nla_put_u32(skb, TCA_FQ_CODEL_INTERVAL, codel_time_to_us(q->cparams.interval)) || nla_put_u32(skb, TCA_FQ_CODEL_ECN, q->cparams.ecn) || nla_put_u32(skb, TCA_FQ_CODEL_QUANTUM, q->quantum) || nla_put_u32(skb, TCA_FQ_CODEL_FLOWS, q->flows_cnt)) goto nla_put_failure; nla_nest_end(skb, opts); return skb->len; nla_put_failure: return -1; } static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct tc_fq_codel_xstats st = { .type = TCA_FQ_CODEL_XSTATS_QDISC, }; struct list_head *pos; st.qdisc_stats.maxpacket = q->cstats.maxpacket; st.qdisc_stats.drop_overlimit = q->drop_overlimit; st.qdisc_stats.ecn_mark = q->cstats.ecn_mark; st.qdisc_stats.new_flow_count = q->new_flow_count; list_for_each(pos, &q->new_flows) st.qdisc_stats.new_flows_len++; list_for_each(pos, &q->old_flows) st.qdisc_stats.old_flows_len++; return gnet_stats_copy_app(d, &st, sizeof(st)); } static struct Qdisc *fq_codel_leaf(struct Qdisc *sch, unsigned long arg) { return NULL; } static unsigned long fq_codel_get(struct Qdisc *sch, u32 classid) { return 0; } static unsigned long fq_codel_bind(struct Qdisc *sch, unsigned long parent, u32 classid) { /* we cannot bypass queue discipline anymore */ sch->flags &= ~TCQ_F_CAN_BYPASS; return 0; } static void fq_codel_put(struct Qdisc *q, unsigned long cl) { } static struct tcf_proto **fq_codel_find_tcf(struct Qdisc *sch, unsigned long cl) { struct fq_codel_sched_data *q = qdisc_priv(sch); if (cl) return NULL; return &q->filter_list; } static int fq_codel_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { tcm->tcm_handle |= TC_H_MIN(cl); return 0; } static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl, struct gnet_dump *d) { struct fq_codel_sched_data *q = qdisc_priv(sch); u32 idx = cl - 1; struct gnet_stats_queue qs = { 0 }; struct tc_fq_codel_xstats xstats; if (idx < q->flows_cnt) { const struct fq_codel_flow *flow = &q->flows[idx]; const struct sk_buff *skb = flow->head; memset(&xstats, 0, sizeof(xstats)); xstats.type = TCA_FQ_CODEL_XSTATS_CLASS; xstats.class_stats.deficit = flow->deficit; xstats.class_stats.ldelay = codel_time_to_us(flow->cvars.ldelay); xstats.class_stats.count = flow->cvars.count; xstats.class_stats.lastcount = flow->cvars.lastcount; xstats.class_stats.dropping = flow->cvars.dropping; if (flow->cvars.dropping) { codel_tdiff_t delta = flow->cvars.drop_next - codel_get_time(); xstats.class_stats.drop_next = (delta >= 0) ? codel_time_to_us(delta) : -codel_time_to_us(-delta); } while (skb) { qs.qlen++; skb = skb->next; } qs.backlog = q->backlogs[idx]; qs.drops = flow->dropped; } if (gnet_stats_copy_queue(d, &qs) < 0) return -1; if (idx < q->flows_cnt) return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); return 0; } static void fq_codel_walk(struct Qdisc *sch, struct qdisc_walker *arg) { struct fq_codel_sched_data *q = qdisc_priv(sch); unsigned int i; if (arg->stop) return; for (i = 0; i < q->flows_cnt; i++) { if (list_empty(&q->flows[i].flowchain) || arg->count < arg->skip) { arg->count++; continue; } if (arg->fn(sch, i + 1, arg) < 0) { arg->stop = 1; break; } arg->count++; } } static const struct Qdisc_class_ops fq_codel_class_ops = { .leaf = fq_codel_leaf, .get = fq_codel_get, .put = fq_codel_put, .tcf_chain = fq_codel_find_tcf, .bind_tcf = fq_codel_bind, .unbind_tcf = fq_codel_put, .dump = fq_codel_dump_class, .dump_stats = fq_codel_dump_class_stats, .walk = fq_codel_walk, }; static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = { .cl_ops = &fq_codel_class_ops, .id = "fq_codel", .priv_size = sizeof(struct fq_codel_sched_data), .enqueue = fq_codel_enqueue, .dequeue = fq_codel_dequeue, #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)) .peek = qdisc_peek_dequeued, #endif .drop = fq_codel_drop, .init = fq_codel_init, .reset = fq_codel_reset, .destroy = fq_codel_destroy, .change = fq_codel_change, .dump = fq_codel_dump, .dump_stats = fq_codel_dump_stats, .owner = THIS_MODULE, }; static int __init fq_codel_module_init(void) { return register_qdisc(&fq_codel_qdisc_ops); } static void __exit fq_codel_module_exit(void) { unregister_qdisc(&fq_codel_qdisc_ops); } module_init(fq_codel_module_init) module_exit(fq_codel_module_exit) MODULE_AUTHOR("Eric Dumazet"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/compat/compat-3.3.c0000644000175000017500000001072312026211315017576 0ustar mcgrofmcgrof/* * Copyright 2012 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 3.3. */ #include #include #include #include #include #include static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) { new->tstamp = old->tstamp; new->dev = old->dev; new->transport_header = old->transport_header; new->network_header = old->network_header; new->mac_header = old->mac_header; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) skb_dst_copy(new, old); new->rxhash = old->rxhash; #else skb_dst_set(new, dst_clone(skb_dst(old))); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) new->ooo_okay = old->ooo_okay; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) new->l4_rxhash = old->l4_rxhash; #endif #ifdef CONFIG_XFRM new->sp = secpath_get(old->sp); #endif memcpy(new->cb, old->cb, sizeof(old->cb)); new->csum = old->csum; new->local_df = old->local_df; new->pkt_type = old->pkt_type; new->ip_summed = old->ip_summed; skb_copy_queue_mapping(new, old); new->priority = old->priority; #if IS_ENABLED(CONFIG_IP_VS) new->ipvs_property = old->ipvs_property; #endif new->protocol = old->protocol; new->mark = old->mark; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) new->skb_iif = old->skb_iif; #endif __nf_copy(new, old); #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) new->nf_trace = old->nf_trace; #endif #ifdef CONFIG_NET_SCHED new->tc_index = old->tc_index; #ifdef CONFIG_NET_CLS_ACT new->tc_verd = old->tc_verd; #endif #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) new->vlan_tci = old->vlan_tci; #endif skb_copy_secmark(new, old); } static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) { #ifndef NET_SKBUFF_DATA_USES_OFFSET /* * Shift between the two data areas in bytes */ unsigned long offset = new->data - old->data; #endif __copy_skb_header(new, old); #ifndef NET_SKBUFF_DATA_USES_OFFSET /* {transport,network,mac}_header are relative to skb->head */ new->transport_header += offset; new->network_header += offset; if (skb_mac_header_was_set(new)) new->mac_header += offset; #endif skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; } static void skb_clone_fraglist(struct sk_buff *skb) { struct sk_buff *list; skb_walk_frags(skb, list) skb_get(list); } /** * __pskb_copy - create copy of an sk_buff with private head. * @skb: buffer to copy * @headroom: headroom of new skb * @gfp_mask: allocation priority * * Make a copy of both an &sk_buff and part of its data, located * in header. Fragmented data remain shared. This is used when * the caller wishes to modify only header of &sk_buff and needs * private copy of the header to alter. Returns %NULL on failure * or the pointer to the buffer on success. * The returned buffer has a reference count of 1. */ struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask) { unsigned int size = skb_headlen(skb) + headroom; struct sk_buff *n = alloc_skb(size, gfp_mask); if (!n) goto out; /* Set the data pointer */ skb_reserve(n, headroom); /* Set the tail pointer and length */ skb_put(n, skb_headlen(skb)); /* Copy the bytes */ skb_copy_from_linear_data(skb, n->data, n->len); n->truesize += skb->data_len; n->data_len = skb->data_len; n->len = skb->len; if (skb_shinfo(skb)->nr_frags) { int i; /* * SKBTX_DEV_ZEROCOPY was added on 3.1 as well but requires ubuf * stuff added to the skb which we do not have */ #if 0 if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { if (skb_copy_ubufs(skb, gfp_mask)) { kfree_skb(n); n = NULL; goto out; } } #endif for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) skb_frag_ref(skb, i); #else get_page(skb_shinfo(skb)->frags[i].page); #endif } skb_shinfo(n)->nr_frags = i; } if (skb_has_frag_list(skb)) { skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list; skb_clone_fraglist(n); } copy_skb_header(n, skb); out: return n; } EXPORT_SYMBOL_GPL(__pskb_copy); compat-drivers-2012-09-18/compat/kfifo.c0000644000175000017500000003146712026211315017120 0ustar mcgrofmcgrof/* * A generic kernel FIFO implementation * * Copyright (C) 2009/2010 Stefani Seibold * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include /* * internal helper to calculate the unused elements in a fifo */ static inline unsigned int kfifo_unused(struct __kfifo *fifo) { return (fifo->mask + 1) - (fifo->in - fifo->out); } int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, size_t esize, gfp_t gfp_mask) { /* * round down to the next power of 2, since our 'let the indices * wrap' technique works only in this case. */ if (!is_power_of_2(size)) size = rounddown_pow_of_two(size); fifo->in = 0; fifo->out = 0; fifo->esize = esize; if (size < 2) { fifo->data = NULL; fifo->mask = 0; return -EINVAL; } fifo->data = kmalloc(size * esize, gfp_mask); if (!fifo->data) { fifo->mask = 0; return -ENOMEM; } fifo->mask = size - 1; return 0; } EXPORT_SYMBOL_GPL(__kfifo_alloc); void __kfifo_free(struct __kfifo *fifo) { kfree(fifo->data); fifo->in = 0; fifo->out = 0; fifo->esize = 0; fifo->data = NULL; fifo->mask = 0; } EXPORT_SYMBOL_GPL(__kfifo_free); int __kfifo_init(struct __kfifo *fifo, void *buffer, unsigned int size, size_t esize) { size /= esize; if (!is_power_of_2(size)) size = rounddown_pow_of_two(size); fifo->in = 0; fifo->out = 0; fifo->esize = esize; fifo->data = buffer; if (size < 2) { fifo->mask = 0; return -EINVAL; } fifo->mask = size - 1; return 0; } EXPORT_SYMBOL_GPL(__kfifo_init); static void kfifo_copy_in(struct __kfifo *fifo, const void *src, unsigned int len, unsigned int off) { unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; unsigned int l; off &= fifo->mask; if (esize != 1) { off *= esize; size *= esize; len *= esize; } l = min(len, size - off); memcpy(fifo->data + off, src, l); memcpy(fifo->data, src + l, len - l); /* * make sure that the data in the fifo is up to date before * incrementing the fifo->in index counter */ smp_wmb(); } unsigned int __kfifo_in(struct __kfifo *fifo, const void *buf, unsigned int len) { unsigned int l; l = kfifo_unused(fifo); if (len > l) len = l; kfifo_copy_in(fifo, buf, len, fifo->in); fifo->in += len; return len; } EXPORT_SYMBOL_GPL(__kfifo_in); static void kfifo_copy_out(struct __kfifo *fifo, void *dst, unsigned int len, unsigned int off) { unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; unsigned int l; off &= fifo->mask; if (esize != 1) { off *= esize; size *= esize; len *= esize; } l = min(len, size - off); memcpy(dst, fifo->data + off, l); memcpy(dst + l, fifo->data, len - l); /* * make sure that the data is copied before * incrementing the fifo->out index counter */ smp_wmb(); } unsigned int __kfifo_out_peek(struct __kfifo *fifo, void *buf, unsigned int len) { unsigned int l; l = fifo->in - fifo->out; if (len > l) len = l; kfifo_copy_out(fifo, buf, len, fifo->out); return len; } EXPORT_SYMBOL_GPL(__kfifo_out_peek); unsigned int __kfifo_out(struct __kfifo *fifo, void *buf, unsigned int len) { len = __kfifo_out_peek(fifo, buf, len); fifo->out += len; return len; } EXPORT_SYMBOL_GPL(__kfifo_out); static unsigned long kfifo_copy_from_user(struct __kfifo *fifo, const void __user *from, unsigned int len, unsigned int off, unsigned int *copied) { unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; unsigned int l; unsigned long ret; off &= fifo->mask; if (esize != 1) { off *= esize; size *= esize; len *= esize; } l = min(len, size - off); ret = copy_from_user(fifo->data + off, from, l); if (unlikely(ret)) ret = DIV_ROUND_UP(ret + len - l, esize); else { ret = copy_from_user(fifo->data, from + l, len - l); if (unlikely(ret)) ret = DIV_ROUND_UP(ret, esize); } /* * make sure that the data in the fifo is up to date before * incrementing the fifo->in index counter */ smp_wmb(); *copied = len - ret; /* return the number of elements which are not copied */ return ret; } int __kfifo_from_user(struct __kfifo *fifo, const void __user *from, unsigned long len, unsigned int *copied) { unsigned int l; unsigned long ret; unsigned int esize = fifo->esize; int err; if (esize != 1) len /= esize; l = kfifo_unused(fifo); if (len > l) len = l; ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied); if (unlikely(ret)) { len -= ret; err = -EFAULT; } else err = 0; fifo->in += len; return err; } EXPORT_SYMBOL_GPL(__kfifo_from_user); static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to, unsigned int len, unsigned int off, unsigned int *copied) { unsigned int l; unsigned long ret; unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; off &= fifo->mask; if (esize != 1) { off *= esize; size *= esize; len *= esize; } l = min(len, size - off); ret = copy_to_user(to, fifo->data + off, l); if (unlikely(ret)) ret = DIV_ROUND_UP(ret + len - l, esize); else { ret = copy_to_user(to + l, fifo->data, len - l); if (unlikely(ret)) ret = DIV_ROUND_UP(ret, esize); } /* * make sure that the data is copied before * incrementing the fifo->out index counter */ smp_wmb(); *copied = len - ret; /* return the number of elements which are not copied */ return ret; } int __kfifo_to_user(struct __kfifo *fifo, void __user *to, unsigned long len, unsigned int *copied) { unsigned int l; unsigned long ret; unsigned int esize = fifo->esize; int err; if (esize != 1) len /= esize; l = fifo->in - fifo->out; if (len > l) len = l; ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied); if (unlikely(ret)) { len -= ret; err = -EFAULT; } else err = 0; fifo->out += len; return err; } EXPORT_SYMBOL_GPL(__kfifo_to_user); static int setup_sgl_buf(struct scatterlist *sgl, void *buf, int nents, unsigned int len) { int n; unsigned int l; unsigned int off; struct page *page; if (!nents) return 0; if (!len) return 0; n = 0; page = virt_to_page(buf); off = offset_in_page(buf); l = 0; while (len >= l + PAGE_SIZE - off) { struct page *npage; l += PAGE_SIZE; buf += PAGE_SIZE; npage = virt_to_page(buf); if (page_to_phys(page) != page_to_phys(npage) - l) { sg_set_page(sgl, page, l - off, off); sgl = sg_next(sgl); if (++n == nents || sgl == NULL) return n; page = npage; len -= l - off; l = off = 0; } } sg_set_page(sgl, page, len, off); return n + 1; } static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len, unsigned int off) { unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; unsigned int l; unsigned int n; off &= fifo->mask; if (esize != 1) { off *= esize; size *= esize; len *= esize; } l = min(len, size - off); n = setup_sgl_buf(sgl, fifo->data + off, nents, l); n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l); return n; } unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len) { unsigned int l; l = kfifo_unused(fifo); if (len > l) len = l; return setup_sgl(fifo, sgl, nents, len, fifo->in); } EXPORT_SYMBOL_GPL(__kfifo_dma_in_prepare); unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len) { unsigned int l; l = fifo->in - fifo->out; if (len > l) len = l; return setup_sgl(fifo, sgl, nents, len, fifo->out); } EXPORT_SYMBOL_GPL(__kfifo_dma_out_prepare); unsigned int __kfifo_max_r(unsigned int len, size_t recsize) { unsigned int max = (1 << (recsize << 3)) - 1; if (len > max) return max; return len; } #define __KFIFO_PEEK(data, out, mask) \ ((data)[(out) & (mask)]) /* * __kfifo_peek_n internal helper function for determinate the length of * the next record in the fifo */ static unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize) { unsigned int l; unsigned int mask = fifo->mask; unsigned char *data = fifo->data; l = __KFIFO_PEEK(data, fifo->out, mask); if (--recsize) l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8; return l; } #define __KFIFO_POKE(data, in, mask, val) \ ( \ (data)[(in) & (mask)] = (unsigned char)(val) \ ) /* * __kfifo_poke_n internal helper function for storeing the length of * the record into the fifo */ static void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize) { unsigned int mask = fifo->mask; unsigned char *data = fifo->data; __KFIFO_POKE(data, fifo->in, mask, n); if (recsize > 1) __KFIFO_POKE(data, fifo->in + 1, mask, n >> 8); } unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize) { return __kfifo_peek_n(fifo, recsize); } EXPORT_SYMBOL_GPL(__kfifo_len_r); unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf, unsigned int len, size_t recsize) { if (len + recsize > kfifo_unused(fifo)) return 0; __kfifo_poke_n(fifo, len, recsize); kfifo_copy_in(fifo, buf, len, fifo->in + recsize); fifo->in += len + recsize; return len; } EXPORT_SYMBOL_GPL(__kfifo_in_r); static unsigned int kfifo_out_copy_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize, unsigned int *n) { *n = __kfifo_peek_n(fifo, recsize); if (len > *n) len = *n; kfifo_copy_out(fifo, buf, len, fifo->out + recsize); return len; } unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize) { unsigned int n; if (fifo->in == fifo->out) return 0; return kfifo_out_copy_r(fifo, buf, len, recsize, &n); } EXPORT_SYMBOL_GPL(__kfifo_out_peek_r); unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize) { unsigned int n; if (fifo->in == fifo->out) return 0; len = kfifo_out_copy_r(fifo, buf, len, recsize, &n); fifo->out += n + recsize; return len; } EXPORT_SYMBOL_GPL(__kfifo_out_r); void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize) { unsigned int n; n = __kfifo_peek_n(fifo, recsize); fifo->out += n + recsize; } EXPORT_SYMBOL_GPL(__kfifo_skip_r); int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from, unsigned long len, unsigned int *copied, size_t recsize) { unsigned long ret; len = __kfifo_max_r(len, recsize); if (len + recsize > kfifo_unused(fifo)) { *copied = 0; return 0; } __kfifo_poke_n(fifo, len, recsize); ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied); if (unlikely(ret)) { *copied = 0; return -EFAULT; } fifo->in += len + recsize; return 0; } EXPORT_SYMBOL_GPL(__kfifo_from_user_r); int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to, unsigned long len, unsigned int *copied, size_t recsize) { unsigned long ret; unsigned int n; if (fifo->in == fifo->out) { *copied = 0; return 0; } n = __kfifo_peek_n(fifo, recsize); if (len > n) len = n; ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied); if (unlikely(ret)) { *copied = 0; return -EFAULT; } fifo->out += n + recsize; return 0; } EXPORT_SYMBOL_GPL(__kfifo_to_user_r); unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) { if (!nents) BUG(); len = __kfifo_max_r(len, recsize); if (len + recsize > kfifo_unused(fifo)) return 0; return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize); } EXPORT_SYMBOL_GPL(__kfifo_dma_in_prepare_r); void __kfifo_dma_in_finish_r(struct __kfifo *fifo, unsigned int len, size_t recsize) { len = __kfifo_max_r(len, recsize); __kfifo_poke_n(fifo, len, recsize); fifo->in += len + recsize; } EXPORT_SYMBOL_GPL(__kfifo_dma_in_finish_r); unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) { if (!nents) BUG(); len = __kfifo_max_r(len, recsize); if (len + recsize > fifo->in - fifo->out) return 0; return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize); } EXPORT_SYMBOL_GPL(__kfifo_dma_out_prepare_r); void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize) { unsigned int len; len = __kfifo_peek_n(fifo, recsize); fifo->out += len + recsize; } EXPORT_SYMBOL_GPL(__kfifo_dma_out_finish_r); compat-drivers-2012-09-18/compat/pm_qos_params.c0000644000175000017500000003223012026211315020650 0ustar mcgrofmcgrof#include /* This is the backport of pm-qos params for kernels <= 2.6.25 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) /* * This module exposes the interface to kernel space for specifying * QoS dependencies. It provides infrastructure for registration of: * * Dependents on a QoS value : register requirements * Watchers of QoS value : get notified when target QoS value changes * * This QoS design is best effort based. Dependents register their QoS needs. * Watchers register to keep track of the current QoS needs of the system. * * There are 3 basic classes of QoS parameter: latency, timeout, throughput * each have defined units: * latency: usec * timeout: usec <-- currently not used. * throughput: kbs (kilo byte / sec) * * There are lists of pm_qos_objects each one wrapping requirements, notifiers * * User mode requirements on a QOS parameter register themselves to the * subsystem by opening the device node /dev/... and writing there request to * the node. As long as the process holds a file handle open to the node the * client continues to be accounted for. Upon file release the usermode * requirement is removed and a new qos target is computed. This way when the * requirement that the application has is cleaned up when closes the file * pointer or exits the pm_qos_object will get an opportunity to clean up. * * Mark Gross */ #include #include #include #include #include #include #include #include #include #include #include #include /* * locking rule: all changes to requirements or notifiers lists * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock * held, taken with _irqsave. One lock to rule them all */ struct requirement_list { struct list_head list; union { s32 value; s32 usec; s32 kbps; }; char *name; }; static s32 max_compare(s32 v1, s32 v2); static s32 min_compare(s32 v1, s32 v2); struct pm_qos_object { struct requirement_list requirements; struct blocking_notifier_head *notifiers; struct miscdevice pm_qos_power_miscdev; char *name; s32 default_value; atomic_t target_value; s32 (*comparitor)(s32, s32); }; static struct pm_qos_object null_pm_qos; static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); static struct pm_qos_object cpu_dma_pm_qos = { .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)}, .notifiers = &cpu_dma_lat_notifier, .name = "cpu_dma_latency", .default_value = 2000 * USEC_PER_SEC, .target_value = ATOMIC_INIT(2000 * USEC_PER_SEC), .comparitor = min_compare }; static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); static struct pm_qos_object network_lat_pm_qos = { .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)}, .notifiers = &network_lat_notifier, .name = "network_latency", .default_value = 2000 * USEC_PER_SEC, .target_value = ATOMIC_INIT(2000 * USEC_PER_SEC), .comparitor = min_compare }; static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); static struct pm_qos_object network_throughput_pm_qos = { .requirements = {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)}, .notifiers = &network_throughput_notifier, .name = "network_throughput", .default_value = 0, .target_value = ATOMIC_INIT(0), .comparitor = max_compare }; static BLOCKING_NOTIFIER_HEAD(system_bus_freq_notifier); static struct pm_qos_object system_bus_freq_pm_qos = { .requirements = {LIST_HEAD_INIT(system_bus_freq_pm_qos.requirements.list)}, .notifiers = &system_bus_freq_notifier, .name = "system_bus_freq", .default_value = 0, .target_value = ATOMIC_INIT(0), .comparitor = max_compare }; static struct pm_qos_object *pm_qos_array[PM_QOS_NUM_CLASSES] = { [PM_QOS_RESERVED] = &null_pm_qos, [PM_QOS_CPU_DMA_LATENCY] = &cpu_dma_pm_qos, [PM_QOS_NETWORK_LATENCY] = &network_lat_pm_qos, [PM_QOS_NETWORK_THROUGHPUT] = &network_throughput_pm_qos, [PM_QOS_SYSTEM_BUS_FREQ] = &system_bus_freq_pm_qos, }; static DEFINE_SPINLOCK(pm_qos_lock); static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); static int pm_qos_power_open(struct inode *inode, struct file *filp); static int pm_qos_power_release(struct inode *inode, struct file *filp); static const struct file_operations pm_qos_power_fops = { .write = pm_qos_power_write, .open = pm_qos_power_open, .release = pm_qos_power_release, }; /* static helper functions */ static s32 max_compare(s32 v1, s32 v2) { return max(v1, v2); } static s32 min_compare(s32 v1, s32 v2) { return min(v1, v2); } static void update_target(int target) { s32 extreme_value; struct requirement_list *node; unsigned long flags; int call_notifier = 0; spin_lock_irqsave(&pm_qos_lock, flags); extreme_value = pm_qos_array[target]->default_value; list_for_each_entry(node, &pm_qos_array[target]->requirements.list, list) { extreme_value = pm_qos_array[target]->comparitor( extreme_value, node->value); } if (atomic_read(&pm_qos_array[target]->target_value) != extreme_value) { call_notifier = 1; atomic_set(&pm_qos_array[target]->target_value, extreme_value); pr_debug(KERN_ERR "new target for qos %d is %d\n", target, atomic_read(&pm_qos_array[target]->target_value)); } spin_unlock_irqrestore(&pm_qos_lock, flags); if (call_notifier) blocking_notifier_call_chain(pm_qos_array[target]->notifiers, (unsigned long) extreme_value, NULL); } static int register_pm_qos_misc(struct pm_qos_object *qos) { qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; qos->pm_qos_power_miscdev.name = qos->name; qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; return misc_register(&qos->pm_qos_power_miscdev); } static int find_pm_qos_object_by_minor(int minor) { int pm_qos_class; for (pm_qos_class = 0; pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) { if (minor == pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor) return pm_qos_class; } return -1; } /** * pm_qos_requirement - returns current system wide qos expectation * @pm_qos_class: identification of which qos value is requested * * This function returns the current target value in an atomic manner. */ int pm_qos_requirement(int pm_qos_class) { return atomic_read(&pm_qos_array[pm_qos_class]->target_value); } EXPORT_SYMBOL_GPL(pm_qos_requirement); /** * pm_qos_add_requirement - inserts new qos request into the list * @pm_qos_class: identifies which list of qos request to us * @name: identifies the request * @value: defines the qos request * * This function inserts a new entry in the pm_qos_class list of requested qos * performance characteristics. It recomputes the aggregate QoS expectations * for the pm_qos_class of parameters. */ int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value) { struct requirement_list *dep; unsigned long flags; dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL); if (dep) { if (value == PM_QOS_DEFAULT_VALUE) dep->value = pm_qos_array[pm_qos_class]->default_value; else dep->value = value; dep->name = kstrdup(name, GFP_KERNEL); if (!dep->name) goto cleanup; spin_lock_irqsave(&pm_qos_lock, flags); list_add(&dep->list, &pm_qos_array[pm_qos_class]->requirements.list); spin_unlock_irqrestore(&pm_qos_lock, flags); update_target(pm_qos_class); return 0; } cleanup: kfree(dep); return -ENOMEM; } EXPORT_SYMBOL_GPL(pm_qos_add_requirement); /** * pm_qos_update_requirement - modifies an existing qos request * @pm_qos_class: identifies which list of qos request to us * @name: identifies the request * @value: defines the qos request * * Updates an existing qos requirement for the pm_qos_class of parameters along * with updating the target pm_qos_class value. * * If the named request isn't in the list then no change is made. */ int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value) { unsigned long flags; struct requirement_list *node; int pending_update = 0; spin_lock_irqsave(&pm_qos_lock, flags); list_for_each_entry(node, &pm_qos_array[pm_qos_class]->requirements.list, list) { if (strcmp(node->name, name) == 0) { if (new_value == PM_QOS_DEFAULT_VALUE) node->value = pm_qos_array[pm_qos_class]->default_value; else node->value = new_value; pending_update = 1; break; } } spin_unlock_irqrestore(&pm_qos_lock, flags); if (pending_update) update_target(pm_qos_class); return 0; } EXPORT_SYMBOL_GPL(pm_qos_update_requirement); /** * pm_qos_remove_requirement - modifies an existing qos request * @pm_qos_class: identifies which list of qos request to us * @name: identifies the request * * Will remove named qos request from pm_qos_class list of parameters and * recompute the current target value for the pm_qos_class. */ void pm_qos_remove_requirement(int pm_qos_class, char *name) { unsigned long flags; struct requirement_list *node; int pending_update = 0; spin_lock_irqsave(&pm_qos_lock, flags); list_for_each_entry(node, &pm_qos_array[pm_qos_class]->requirements.list, list) { if (strcmp(node->name, name) == 0) { kfree(node->name); list_del(&node->list); kfree(node); pending_update = 1; break; } } spin_unlock_irqrestore(&pm_qos_lock, flags); if (pending_update) update_target(pm_qos_class); } EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); /** * pm_qos_add_notifier - sets notification entry for changes to target value * @pm_qos_class: identifies which qos target changes should be notified. * @notifier: notifier block managed by caller. * * will register the notifier into a notification chain that gets called * upon changes to the pm_qos_class target value. */ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) { int retval; retval = blocking_notifier_chain_register( pm_qos_array[pm_qos_class]->notifiers, notifier); return retval; } EXPORT_SYMBOL_GPL(pm_qos_add_notifier); /** * pm_qos_remove_notifier - deletes notification entry from chain. * @pm_qos_class: identifies which qos target changes are notified. * @notifier: notifier block to be removed. * * will remove the notifier from the notification chain that gets called * upon changes to the pm_qos_class target value. */ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) { int retval; retval = blocking_notifier_chain_unregister( pm_qos_array[pm_qos_class]->notifiers, notifier); return retval; } EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); #define PID_NAME_LEN 32 static int pm_qos_power_open(struct inode *inode, struct file *filp) { int ret; long pm_qos_class; char name[PID_NAME_LEN]; pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); if (pm_qos_class >= 0) { filp->private_data = (void *)pm_qos_class; snprintf(name, PID_NAME_LEN, "process_%d", current->pid); ret = pm_qos_add_requirement(pm_qos_class, name, PM_QOS_DEFAULT_VALUE); if (ret >= 0) return 0; } return -EPERM; } static int pm_qos_power_release(struct inode *inode, struct file *filp) { int pm_qos_class; char name[PID_NAME_LEN]; pm_qos_class = (long)filp->private_data; snprintf(name, PID_NAME_LEN, "process_%d", current->pid); pm_qos_remove_requirement(pm_qos_class, name); return 0; } static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { s32 value; int pm_qos_class; char name[PID_NAME_LEN]; pm_qos_class = (long)filp->private_data; if (count != sizeof(s32)) return -EINVAL; if (copy_from_user(&value, buf, sizeof(s32))) return -EFAULT; snprintf(name, PID_NAME_LEN, "process_%d", current->pid); pm_qos_update_requirement(pm_qos_class, name, value); return sizeof(s32); } /* * This initializes pm-qos for older kernels. */ int compat_pm_qos_power_init(void) { int ret = 0; ret = register_pm_qos_misc(&cpu_dma_pm_qos); if (ret < 0) { printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n"); return ret; } ret = register_pm_qos_misc(&network_lat_pm_qos); if (ret < 0) { printk(KERN_ERR "pm_qos_param: network_latency setup failed\n"); return ret; } ret = register_pm_qos_misc(&network_throughput_pm_qos); if (ret < 0) { printk(KERN_ERR "pm_qos_param: network_throughput setup failed\n"); return ret; } ret = register_pm_qos_misc(&system_bus_freq_pm_qos); if (ret < 0) printk(KERN_ERR "pm_qos_param: system_bus_freq setup failed\n"); return ret; } int compat_pm_qos_power_deinit(void) { int ret = 0; ret = misc_deregister(&cpu_dma_pm_qos.pm_qos_power_miscdev); if (ret < 0) { printk(KERN_ERR "pm_qos_param: cpu_dma_latency deinit failed\n"); return ret; } ret = misc_deregister(&network_lat_pm_qos.pm_qos_power_miscdev); if (ret < 0) { printk(KERN_ERR "pm_qos_param: network_latency deinit failed\n"); return ret; } ret = misc_deregister(&network_throughput_pm_qos.pm_qos_power_miscdev); if (ret < 0) { printk(KERN_ERR "pm_qos_param: network_throughput deinit failed\n"); return ret; } ret = misc_deregister(&system_bus_freq_pm_qos.pm_qos_power_miscdev); if (ret < 0) { printk(KERN_ERR "pm_qos_param: system_bus_freq deinit failed\n"); return ret; } return ret; } #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) */ compat-drivers-2012-09-18/compat/flow_dissector.c0000644000175000017500000000602712026211315021042 0ustar mcgrofmcgrof#include #include #include #include #include #include #include #include #include #include /* copy saddr & daddr, possibly using 64bit load/store * Equivalent to : flow->src = iph->saddr; * flow->dst = iph->daddr; */ static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph) { BUILD_BUG_ON(offsetof(typeof(*flow), dst) != offsetof(typeof(*flow), src) + sizeof(flow->src)); memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst)); } bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow) { int poff, nhoff = skb_network_offset(skb); u8 ip_proto; __be16 proto = skb->protocol; memset(flow, 0, sizeof(*flow)); again: switch (proto) { case __constant_htons(ETH_P_IP): { const struct iphdr *iph; struct iphdr _iph; ip: iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); if (!iph) return false; if (ip_is_fragment(iph)) ip_proto = 0; else ip_proto = iph->protocol; iph_to_flow_copy_addrs(flow, iph); nhoff += iph->ihl * 4; break; } case __constant_htons(ETH_P_IPV6): { const struct ipv6hdr *iph; struct ipv6hdr _iph; ipv6: iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); if (!iph) return false; ip_proto = iph->nexthdr; flow->src = iph->saddr.s6_addr32[3]; flow->dst = iph->daddr.s6_addr32[3]; nhoff += sizeof(struct ipv6hdr); break; } case __constant_htons(ETH_P_8021Q): { const struct vlan_hdr *vlan; struct vlan_hdr _vlan; vlan = skb_header_pointer(skb, nhoff, sizeof(_vlan), &_vlan); if (!vlan) return false; proto = vlan->h_vlan_encapsulated_proto; nhoff += sizeof(*vlan); goto again; } case __constant_htons(ETH_P_PPP_SES): { struct { struct pppoe_hdr hdr; __be16 proto; } *hdr, _hdr; hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); if (!hdr) return false; proto = hdr->proto; nhoff += PPPOE_SES_HLEN; switch (proto) { case __constant_htons(PPP_IP): goto ip; case __constant_htons(PPP_IPV6): goto ipv6; default: return false; } } default: return false; } switch (ip_proto) { case IPPROTO_GRE: { struct gre_hdr { __be16 flags; __be16 proto; } *hdr, _hdr; hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); if (!hdr) return false; /* * Only look inside GRE if version zero and no * routing */ if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) { proto = hdr->proto; nhoff += 4; if (hdr->flags & GRE_CSUM) nhoff += 4; if (hdr->flags & GRE_KEY) nhoff += 4; if (hdr->flags & GRE_SEQ) nhoff += 4; goto again; } break; } case IPPROTO_IPIP: goto again; default: break; } flow->ip_proto = ip_proto; poff = proto_ports_offset(ip_proto); if (poff >= 0) { __be32 *ports, _ports; nhoff += poff; ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports); if (ports) flow->ports = *ports; } return true; } compat-drivers-2012-09-18/compat/Makefile0000644000175000017500000000375012026211315017310 0ustar mcgrofmcgrofobj-m += compat.o #compat-objs := obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o obj-$(CONFIG_COMPAT_NET_SCH_CODEL) += sch_codel.o sch_fq_codel-y = sch_fq_codel_core.o flow_dissector.o obj-$(CONFIG_COMPAT_NET_SCH_FQ_CODEL) += sch_fq_codel.o compat-y += main.o # Compat kernel compatibility code compat-$(CONFIG_COMPAT_KERNEL_2_6_14) += compat-2.6.14.o compat-$(CONFIG_COMPAT_KERNEL_2_6_18) += compat-2.6.18.o compat-$(CONFIG_COMPAT_KERNEL_2_6_19) += compat-2.6.19.o compat-$(CONFIG_COMPAT_KERNEL_2_6_21) += compat-2.6.21.o compat-$(CONFIG_COMPAT_KERNEL_2_6_22) += compat-2.6.22.o compat-$(CONFIG_COMPAT_KERNEL_2_6_23) += compat-2.6.23.o compat-$(CONFIG_COMPAT_KERNEL_2_6_24) += compat-2.6.24.o compat-$(CONFIG_COMPAT_KERNEL_2_6_25) += \ compat-2.6.25.o \ pm_qos_params.o compat-$(CONFIG_COMPAT_KERNEL_2_6_26) += compat-2.6.26.o compat-$(CONFIG_COMPAT_KERNEL_2_6_27) += compat-2.6.27.o compat-$(CONFIG_COMPAT_KERNEL_2_6_28) += compat-2.6.28.o compat-$(CONFIG_COMPAT_KERNEL_2_6_29) += compat-2.6.29.o compat-$(CONFIG_COMPAT_KERNEL_2_6_32) += compat-2.6.32.o compat-$(CONFIG_COMPAT_KERNEL_2_6_33) += compat-2.6.33.o compat-$(CONFIG_COMPAT_KERNEL_2_6_34) += compat-2.6.34.o compat-$(CONFIG_COMPAT_KERNEL_2_6_35) += compat-2.6.35.o compat-$(CONFIG_COMPAT_KERNEL_2_6_36) += compat-2.6.36.o compat-$(CONFIG_COMPAT_KFIFO) += kfifo.o compat-$(CONFIG_COMPAT_KERNEL_2_6_37) += compat-2.6.37.o compat-$(CONFIG_COMPAT_KERNEL_2_6_38) += compat-2.6.38.o compat-$(CONFIG_COMPAT_KERNEL_2_6_39) += \ compat-2.6.39.o \ kstrtox.o compat-$(CONFIG_COMPAT_KERNEL_3_0) += compat-3.0.o compat-$(CONFIG_COMPAT_KERNEL_3_1) += compat-3.1.o compat-$(CONFIG_COMPAT_KERNEL_3_2) += compat-3.2.o compat-$(CONFIG_COMPAT_KERNEL_3_3) += \ compat-3.3.o compat-$(CONFIG_COMPAT_KERNEL_3_4) += compat-3.4.o compat-$(CONFIG_COMPAT_KERNEL_3_7) += compat-3.7.o compat-$(CONFIG_COMPAT_CORDIC) += cordic.o compat-$(CONFIG_COMPAT_CRC8) += crc8.o ifndef CONFIG_64BIT ifndef CONFIG_GENERIC_ATOMIC64 compat-y += compat_atomic.o endif endif compat-drivers-2012-09-18/compat/compat-3.1.c0000644000175000017500000000464212026211315017577 0ustar mcgrofmcgrof/* * Copyright 2012 Hauke Mehrtens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 3.1. */ #include #include /* This backports: * commit 3d73710880afa3d61cf57b5d4eb192e812eb7e4f * Author: Jesse Barnes * Date: Tue Jun 28 10:59:12 2011 -0700 * * cpufreq: expose a cpufreq_quick_get_max routine */ unsigned int compat_cpufreq_quick_get_max(unsigned int cpu) { struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); unsigned int ret_freq = 0; if (policy) { ret_freq = policy->max; cpufreq_cpu_put(policy); } return ret_freq; } EXPORT_SYMBOL(compat_cpufreq_quick_get_max); static DEFINE_SPINLOCK(compat_simple_ida_lock); /** * ida_simple_get - get a new id. * @ida: the (initialized) ida. * @start: the minimum id (inclusive, < 0x8000000) * @end: the maximum id (exclusive, < 0x8000000 or 0) * @gfp_mask: memory allocation flags * * Allocates an id in the range start <= id < end, or returns -ENOSPC. * On memory allocation failure, returns -ENOMEM. * * Use ida_simple_remove() to get rid of an id. */ int compat_ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, gfp_t gfp_mask) { int ret, id; unsigned int max; unsigned long flags; BUG_ON((int)start < 0); BUG_ON((int)end < 0); if (end == 0) max = 0x80000000; else { BUG_ON(end < start); max = end - 1; } again: if (!ida_pre_get(ida, gfp_mask)) return -ENOMEM; spin_lock_irqsave(&compat_simple_ida_lock, flags); ret = ida_get_new_above(ida, start, &id); if (!ret) { if (id > max) { ida_remove(ida, id); ret = -ENOSPC; } else { ret = id; } } spin_unlock_irqrestore(&compat_simple_ida_lock, flags); if (unlikely(ret == -EAGAIN)) goto again; return ret; } EXPORT_SYMBOL(compat_ida_simple_get); /** * ida_simple_remove - remove an allocated id. * @ida: the (initialized) ida. * @id: the id returned by ida_simple_get. */ void compat_ida_simple_remove(struct ida *ida, unsigned int id) { unsigned long flags; BUG_ON((int)id < 0); spin_lock_irqsave(&compat_simple_ida_lock, flags); ida_remove(ida, id); spin_unlock_irqrestore(&compat_simple_ida_lock, flags); } EXPORT_SYMBOL(compat_ida_simple_remove); /* source lib/idr.c */ compat-drivers-2012-09-18/compat/compat-2.6.39.c0000644000175000017500000000640512026211315020034 0ustar mcgrofmcgrof/* * Copyright 2011 Hauke Mehrtens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.39. */ #include #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) /* * Termios Helper Methods */ static void unset_locked_termios(struct ktermios *termios, struct ktermios *old, struct ktermios *locked) { int i; #define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z))) if (!locked) { printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n"); return; } NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag); NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag); NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag); NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag); termios->c_line = locked->c_line ? old->c_line : termios->c_line; for (i = 0; i < NCCS; i++) termios->c_cc[i] = locked->c_cc[i] ? old->c_cc[i] : termios->c_cc[i]; /* FIXME: What should we do for i/ospeed */ } /** * tty_set_termios - update termios values * @tty: tty to update * @new_termios: desired new value * * Perform updates to the termios values set on this terminal. There * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * * Locking: termios_mutex */ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) { struct ktermios old_termios; struct tty_ldisc *ld; unsigned long flags; /* * Perform the actual termios internal changes under lock. */ /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ mutex_lock(&tty->termios_mutex); old_termios = *tty->termios; *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { int extproc = (old_termios.c_lflag & EXTPROC) | (tty->termios->c_lflag & EXTPROC); int old_flow = ((old_termios.c_iflag & IXON) && (old_termios.c_cc[VSTOP] == '\023') && (old_termios.c_cc[VSTART] == '\021')); int new_flow = (I_IXON(tty) && STOP_CHAR(tty) == '\023' && START_CHAR(tty) == '\021'); if ((old_flow != new_flow) || extproc) { spin_lock_irqsave(&tty->ctrl_lock, flags); if (old_flow != new_flow) { tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); if (new_flow) tty->ctrl_status |= TIOCPKT_DOSTOP; else tty->ctrl_status |= TIOCPKT_NOSTOP; } if (extproc) tty->ctrl_status |= TIOCPKT_IOCTL; spin_unlock_irqrestore(&tty->ctrl_lock, flags); wake_up_interruptible(&tty->link->read_wait); } } if (tty->ops->set_termios) (*tty->ops->set_termios)(tty, &old_termios); else tty_termios_copy_hw(tty->termios, &old_termios); ld = tty_ldisc_ref(tty); if (ld != NULL) { if (ld->ops->set_termios) (ld->ops->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } mutex_unlock(&tty->termios_mutex); return 0; } EXPORT_SYMBOL_GPL(tty_set_termios); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) */ compat-drivers-2012-09-18/compat/compat-2.6.25.c0000644000175000017500000000666212026211315020034 0ustar mcgrofmcgrof/* * Copyright 2007-2012 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.25. */ #include #include /* * To backport b718989d correctly pcibios_enable_device() * is required but we don't have access to it on modules * as its an architecture specific routine that is not * exported and as such only core kernel code has access * to it. We implement a sloppy work around for backporting * this. */ int pci_enable_device_mem(struct pci_dev *dev) { int bars = pci_select_bars(dev, IORESOURCE_MEM); return pci_enable_device_bars(dev, bars); } EXPORT_SYMBOL_GPL(pci_enable_device_mem); /** * The following things are out of ./lib/vsprintf.c * The new iwlwifi driver is using them. */ /** * strict_strtoul - convert a string to an unsigned long strictly * @cp: The string to be converted * @base: The number base to use * @res: The converted result value * * strict_strtoul converts a string to an unsigned long only if the * string is really an unsigned long string, any string containing * any invalid char at the tail will be rejected and -EINVAL is returned, * only a newline char at the tail is acceptible because people generally * change a module parameter in the following way: * * echo 1024 > /sys/module/e1000/parameters/copybreak * * echo will append a newline to the tail. * * It returns 0 if conversion is successful and *res is set to the converted * value, otherwise it returns -EINVAL and *res is set to 0. * * simple_strtoul just ignores the successive invalid characters and * return the converted value of prefix part of the string. */ int strict_strtoul(const char *cp, unsigned int base, unsigned long *res); /** * strict_strtol - convert a string to a long strictly * @cp: The string to be converted * @base: The number base to use * @res: The converted result value * * strict_strtol is similiar to strict_strtoul, but it allows the first * character of a string is '-'. * * It returns 0 if conversion is successful and *res is set to the converted * value, otherwise it returns -EINVAL and *res is set to 0. */ int strict_strtol(const char *cp, unsigned int base, long *res); #define define_strict_strtoux(type, valtype) \ int strict_strtou##type(const char *cp, unsigned int base, valtype *res)\ { \ char *tail; \ valtype val; \ size_t len; \ \ *res = 0; \ len = strlen(cp); \ if (len == 0) \ return -EINVAL; \ \ val = simple_strtou##type(cp, &tail, base); \ if ((*tail == '\0') || \ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {\ *res = val; \ return 0; \ } \ \ return -EINVAL; \ } \ #define define_strict_strtox(type, valtype) \ int strict_strto##type(const char *cp, unsigned int base, valtype *res) \ { \ int ret; \ if (*cp == '-') { \ ret = strict_strtou##type(cp+1, base, res); \ if (!ret) \ *res = -(*res); \ } else \ ret = strict_strtou##type(cp, base, res); \ \ return ret; \ } \ define_strict_strtoux(l, unsigned long) define_strict_strtox(l, long) EXPORT_SYMBOL_GPL(strict_strtoul); EXPORT_SYMBOL_GPL(strict_strtol); compat-drivers-2012-09-18/compat/compat-2.6.38.c0000644000175000017500000000265012026211315020031 0ustar mcgrofmcgrof/* * Copyright 2010 Hauke Mehrtens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.38. */ #include #include #include /** * ewma_init() - Initialize EWMA parameters * @avg: Average structure * @factor: Factor to use for the scaled up internal value. The maximum value * of averages can be ULONG_MAX/(factor*weight). * @weight: Exponential weight, or decay rate. This defines how fast the * influence of older values decreases. Has to be bigger than 1. * * Initialize the EWMA parameters for a given struct ewma @avg. */ void compat_ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight) { WARN_ON(weight <= 1 || factor == 0); avg->internal = 0; avg->weight = weight; avg->factor = factor; } EXPORT_SYMBOL_GPL(compat_ewma_init); /** * ewma_add() - Exponentially weighted moving average (EWMA) * @avg: Average structure * @val: Current value * * Add a sample to the average. */ struct ewma *compat_ewma_add(struct ewma *avg, unsigned long val) { avg->internal = avg->internal ? (((avg->internal * (avg->weight - 1)) + (val * avg->factor)) / avg->weight) : (val * avg->factor); return avg; } EXPORT_SYMBOL_GPL(compat_ewma_add); compat-drivers-2012-09-18/compat/compat-2.6.26.c0000644000175000017500000000373012026211315020026 0ustar mcgrofmcgrof/* * Copyright 2007-2010 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.26. * * Copyright holders from ported work: * * Copyright (c) 2002-2003 Patrick Mochel * Copyright (c) 2006-2007 Greg Kroah-Hartman * Copyright (c) 2006-2007 Novell Inc. */ #include /* 2.6.24 does not have the struct kobject with a name */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) /** * kobject_set_name_vargs - Set the name of an kobject * @kobj: struct kobject to set the name of * @fmt: format string used to build the name * @vargs: vargs to format the string. */ static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs) { const char *old_name = kobj->name; char *s; if (kobj->name && !fmt) return 0; kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs); if (!kobj->name) return -ENOMEM; /* ewww... some of these buggers have '/' in the name ... */ while ((s = strchr(kobj->name, '/'))) s[0] = '!'; kfree(old_name); return 0; } #else static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs) { struct device *dev; unsigned int len; va_list aq; dev = container_of(kobj, struct device, kobj); va_copy(aq, vargs); len = vsnprintf(NULL, 0, fmt, aq); va_end(aq); len = len < BUS_ID_SIZE ? (len + 1) : BUS_ID_SIZE; vsnprintf(dev->bus_id, len, fmt, vargs); return 0; } #endif /** * dev_set_name - set a device name * @dev: device * @fmt: format string for the device's name */ int dev_set_name(struct device *dev, const char *fmt, ...) { va_list vargs; int err; va_start(vargs, fmt); err = kobject_set_name_vargs(&dev->kobj, fmt, vargs); va_end(vargs); return err; } EXPORT_SYMBOL_GPL(dev_set_name); compat-drivers-2012-09-18/compat/compat-2.6.22.c0000644000175000017500000000060312026211315020016 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.22. */ #include /* 2.6.22 compat code goes here */ compat-drivers-2012-09-18/compat/compat-2.6.35.c0000644000175000017500000000254212026211315020026 0ustar mcgrofmcgrof/* * Copyright 2010 Kshitij Kulshreshtha * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.35. */ #include #include /** * hex_to_bin - convert a hex digit to its real value * @ch: ascii character represents hex digit * * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad * input. */ int compat_hex_to_bin(char ch) { if ((ch >= '0') && (ch <= '9')) return ch - '0'; ch = tolower(ch); if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10; return -1; } EXPORT_SYMBOL_GPL(compat_hex_to_bin); /** * noop_llseek - No Operation Performed llseek implementation * @file: file structure to seek on * @offset: file offset to seek to * @origin: type of seek * * This is an implementation of ->llseek useable for the rare special case when * userspace expects the seek to succeed but the (device) file is actually not * able to perform the seek. In this case you use noop_llseek() instead of * falling back to the default implementation of ->llseek. */ loff_t noop_llseek(struct file *file, loff_t offset, int origin) { return file->f_pos; } EXPORT_SYMBOL_GPL(noop_llseek); compat-drivers-2012-09-18/compat/compat-2.6.36.c0000644000175000017500000001211712026211315020026 0ustar mcgrofmcgrof/* * Copyright 2010 Hauke Mehrtens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.36. */ #include #include #ifdef CONFIG_COMPAT_USB_URB_THREAD_FIX /* Callers must hold anchor->lock */ static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor) { urb->anchor = NULL; list_del(&urb->anchor_list); usb_put_urb(urb); if (list_empty(&anchor->urb_list)) wake_up(&anchor->wait); } /** * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse * @anchor: anchor the requests are bound to * * this allows all outstanding URBs to be unlinked starting * from the back of the queue. This function is asynchronous. * The unlinking is just tiggered. It may happen after this * function has returned. * * This routine should not be called by a driver after its disconnect * method has returned. */ void compat_usb_unlink_anchored_urbs(struct usb_anchor *anchor) { struct urb *victim; while ((victim = usb_get_from_anchor(anchor)) != NULL) { usb_unlink_urb(victim); usb_put_urb(victim); } } EXPORT_SYMBOL_GPL(compat_usb_unlink_anchored_urbs); /** * usb_get_from_anchor - get an anchor's oldest urb * @anchor: the anchor whose urb you want * * this will take the oldest urb from an anchor, * unanchor and return it */ struct urb *compat_usb_get_from_anchor(struct usb_anchor *anchor) { struct urb *victim; unsigned long flags; spin_lock_irqsave(&anchor->lock, flags); if (!list_empty(&anchor->urb_list)) { victim = list_entry(anchor->urb_list.next, struct urb, anchor_list); usb_get_urb(victim); __usb_unanchor_urb(victim, anchor); } else { victim = NULL; } spin_unlock_irqrestore(&anchor->lock, flags); return victim; } EXPORT_SYMBOL_GPL(compat_usb_get_from_anchor); /** * usb_scuttle_anchored_urbs - unanchor all an anchor's urbs * @anchor: the anchor whose urbs you want to unanchor * * use this to get rid of all an anchor's urbs */ void compat_usb_scuttle_anchored_urbs(struct usb_anchor *anchor) { struct urb *victim; unsigned long flags; spin_lock_irqsave(&anchor->lock, flags); while (!list_empty(&anchor->urb_list)) { victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list); __usb_unanchor_urb(victim, anchor); } spin_unlock_irqrestore(&anchor->lock, flags); } EXPORT_SYMBOL_GPL(compat_usb_scuttle_anchored_urbs); #endif /* CONFIG_COMPAT_USB_URB_THREAD_FIX */ struct workqueue_struct *system_wq __read_mostly; struct workqueue_struct *system_long_wq __read_mostly; struct workqueue_struct *system_nrt_wq __read_mostly; EXPORT_SYMBOL_GPL(system_wq); EXPORT_SYMBOL_GPL(system_long_wq); EXPORT_SYMBOL_GPL(system_nrt_wq); int compat_schedule_work(struct work_struct *work) { return queue_work(system_wq, work); } EXPORT_SYMBOL_GPL(compat_schedule_work); int compat_schedule_work_on(int cpu, struct work_struct *work) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) return queue_work_on(cpu, system_wq, work); #else return queue_work(system_wq, work); #endif } EXPORT_SYMBOL_GPL(compat_schedule_work_on); int compat_schedule_delayed_work(struct delayed_work *dwork, unsigned long delay) { return queue_delayed_work(system_wq, dwork, delay); } EXPORT_SYMBOL_GPL(compat_schedule_delayed_work); int compat_schedule_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay) { return queue_delayed_work_on(cpu, system_wq, dwork, delay); } EXPORT_SYMBOL_GPL(compat_schedule_delayed_work_on); #undef flush_scheduled_work void compat_flush_scheduled_work(void) { /* * It is debatable which one we should prioritize first, lets * go with the old kernel's one first for now (keventd_wq) and * if think its reasonable later we can flip this around. */ flush_workqueue(system_wq); flush_scheduled_work(); } EXPORT_SYMBOL_GPL(compat_flush_scheduled_work); /** * work_busy - test whether a work is currently pending or running * @work: the work to be tested * * Test whether @work is currently pending or running. There is no * synchronization around this function and the test result is * unreliable and only useful as advisory hints or for debugging. * Especially for reentrant wqs, the pending state might hide the * running state. * * RETURNS: * OR'd bitmask of WORK_BUSY_* bits. */ unsigned int work_busy(struct work_struct *work) { unsigned int ret = 0; if (work_pending(work)) ret |= WORK_BUSY_PENDING; return ret; } EXPORT_SYMBOL_GPL(work_busy); void compat_system_workqueue_create() { system_wq = alloc_workqueue("events", 0, 0); system_long_wq = alloc_workqueue("events_long", 0, 0); system_nrt_wq = create_singlethread_workqueue("events_nrt"); BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq); } void compat_system_workqueue_destroy() { destroy_workqueue(system_wq); destroy_workqueue(system_long_wq); destroy_workqueue(system_nrt_wq); } compat-drivers-2012-09-18/compat/compat-3.0.c0000644000175000017500000000522612026211315017575 0ustar mcgrofmcgrof/* * Copyright 2011 Hauke Mehrtens * Copyright 2011 Alexey Dobriyan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 3.0. */ #include #include /* This pulls-in a lot of non-exported symbol backports * on kernels older than 2.6.32. There's no harm for not * making this available on kernels < 2.6.32. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) #include /* This backports: * * commit d9d90e5eb70e09903dadff42099b6c948f814050 * Author: Hugh Dickins * Date: Mon Jun 27 16:18:04 2011 -0700 * * tmpfs: add shmem_read_mapping_page_gfp */ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp) { return read_cache_page_gfp(mapping, index, gfp); } EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp); #endif int mac_pton(const char *s, u8 *mac) { int i; /* XX:XX:XX:XX:XX:XX */ if (strlen(s) < 3 * ETH_ALEN - 1) return 0; /* Don't dirty result unless string is valid MAC. */ for (i = 0; i < ETH_ALEN; i++) { if (!strchr("0123456789abcdefABCDEF", s[i * 3])) return 0; if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1])) return 0; if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':') return 0; } for (i = 0; i < ETH_ALEN; i++) { mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]); } return 1; } EXPORT_SYMBOL_GPL(mac_pton); #define kstrto_from_user(f, g, type) \ int f(const char __user *s, size_t count, unsigned int base, type *res) \ { \ /* sign, base 2 representation, newline, terminator */ \ char buf[1 + sizeof(type) * 8 + 1 + 1]; \ \ count = min(count, sizeof(buf) - 1); \ if (copy_from_user(buf, s, count)) \ return -EFAULT; \ buf[count] = '\0'; \ return g(buf, base, res); \ } \ EXPORT_SYMBOL_GPL(f) kstrto_from_user(kstrtoull_from_user, kstrtoull, unsigned long long); kstrto_from_user(kstrtoll_from_user, kstrtoll, long long); kstrto_from_user(kstrtoul_from_user, kstrtoul, unsigned long); kstrto_from_user(kstrtol_from_user, kstrtol, long); kstrto_from_user(kstrtouint_from_user, kstrtouint, unsigned int); kstrto_from_user(kstrtoint_from_user, kstrtoint, int); kstrto_from_user(kstrtou16_from_user, kstrtou16, u16); kstrto_from_user(kstrtos16_from_user, kstrtos16, s16); kstrto_from_user(kstrtou8_from_user, kstrtou8, u8); kstrto_from_user(kstrtos8_from_user, kstrtos8, s8); compat-drivers-2012-09-18/compat/compat-2.6.21.c0000644000175000017500000000060312026211315020015 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.21. */ #include /* 2.6.21 compat code goes here */ compat-drivers-2012-09-18/compat/compat_firmware_class.c0000644000175000017500000004436412026211315022366 0ustar mcgrofmcgrof/* * firmware_class.c - Multi purpose firmware loading support * * Copyright (c) 2003 Manuel Estrada Sainz * * Please see Documentation/firmware_class/ for more information. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define compat_firmware_to_dev(obj) container_of(obj, struct device, kobj) MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); MODULE_LICENSE("GPL"); /* Builtin firmware support */ //#ifdef CONFIG_FW_LOADER #if 0 extern struct builtin_fw __start_builtin_fw[]; extern struct builtin_fw __end_builtin_fw[]; static bool fw_get_builtin_firmware(struct firmware *fw, const char *name) { struct builtin_fw *b_fw; for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) { if (strcmp(name, b_fw->name) == 0) { fw->size = b_fw->size; fw->data = b_fw->data; return true; } } return false; } static bool fw_is_builtin_firmware(const struct firmware *fw) { struct builtin_fw *b_fw; for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) if (fw->data == b_fw->data) return true; return false; } #else /* Module case - no builtin firmware support */ static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name) { return false; } static inline bool fw_is_builtin_firmware(const struct firmware *fw) { return false; } #endif enum { FW_STATUS_LOADING, FW_STATUS_DONE, FW_STATUS_ABORT, }; static int loading_timeout = 60; /* In seconds */ /* fw_lock could be moved to 'struct firmware_priv' but since it is just * guarding for corner cases a global lock should be OK */ static DEFINE_MUTEX(fw_lock); struct firmware_priv { struct completion completion; struct firmware *fw; unsigned long status; struct page **pages; int nr_pages; int page_array_size; struct timer_list timeout; struct device dev; bool nowait; char fw_id[]; }; static struct firmware_priv *to_firmware_priv(struct device *dev) { return container_of(dev, struct firmware_priv, dev); } static void fw_load_abort(struct firmware_priv *fw_priv) { set_bit(FW_STATUS_ABORT, &fw_priv->status); wmb(); complete(&fw_priv->completion); } static ssize_t firmware_timeout_show(struct class *class, char *buf) { return sprintf(buf, "%d\n", loading_timeout); } /** * firmware_timeout_store - set number of seconds to wait for firmware * @class: device class pointer * @buf: buffer to scan for timeout value * @count: number of bytes in @buf * * Sets the number of seconds to wait for the firmware. Once * this expires an error will be returned to the driver and no * firmware will be provided. * * Note: zero means 'wait forever'. **/ static ssize_t firmware_timeout_store(struct class *class, const char *buf, size_t count) { loading_timeout = simple_strtol(buf, NULL, 10); if (loading_timeout < 0) loading_timeout = 0; return count; } static struct class_attribute firmware_class_attrs[] = { __ATTR(timeout, S_IWUSR | S_IRUGO, firmware_timeout_show, firmware_timeout_store), __ATTR_NULL }; static void fw_dev_release(struct device *dev) { struct firmware_priv *fw_priv = to_firmware_priv(dev); int i; for (i = 0; i < fw_priv->nr_pages; i++) __free_page(fw_priv->pages[i]); kfree(fw_priv->pages); kfree(fw_priv); module_put(THIS_MODULE); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) { struct firmware_priv *fw_priv = to_firmware_priv(dev); if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id)) return -ENOMEM; if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout)) return -ENOMEM; if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait)) return -ENOMEM; return 0; } #else static int firmware_uevent(struct device *dev, char **envp, int num_envp, char *buf, int size) { struct firmware_priv *fw_priv = to_firmware_priv(dev); int error, len = 0, i = 0; error = add_uevent_var(envp, num_envp, &i, buf, size, &len, "FIRMWARE=%s", fw_priv->fw_id); if (error) goto exit; error = add_uevent_var(envp, num_envp, &i, buf, size, &len, "TIMEOUT=%i", loading_timeout); if (error) goto exit; error = add_uevent_var(envp, num_envp, &i, buf, size, &len, "ASYNC=%i", fw_priv->nowait); if (error) goto exit; return 0; exit: envp[i] = NULL; return error; } #endif static struct class firmware_class = { .name = "compat_firmware", .class_attrs = firmware_class_attrs, .dev_uevent = firmware_uevent, .dev_release = fw_dev_release, }; static ssize_t firmware_loading_show(struct device *dev, struct device_attribute *attr, char *buf) { struct firmware_priv *fw_priv = to_firmware_priv(dev); int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status); return sprintf(buf, "%d\n", loading); } static void firmware_free_data(const struct firmware *fw) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) int i; vunmap(fw->data); if (fw->pages) { for (i = 0; i < PFN_UP(fw->size); i++) __free_page(fw->pages[i]); kfree(fw->pages); } #else vunmap(fw->data); #endif } /* Some architectures don't have PAGE_KERNEL_RO */ #ifndef PAGE_KERNEL_RO #define PAGE_KERNEL_RO PAGE_KERNEL #endif /** * firmware_loading_store - set value in the 'loading' control file * @dev: device pointer * @buf: buffer to scan for loading control value * @count: number of bytes in @buf * * The relevant values are: * * 1: Start a load, discarding any previous partial load. * 0: Conclude the load and hand the data to the driver code. * -1: Conclude the load with an error and discard any written data. **/ static ssize_t firmware_loading_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct firmware_priv *fw_priv = to_firmware_priv(dev); int loading = simple_strtol(buf, NULL, 10); int i; switch (loading) { case 1: mutex_lock(&fw_lock); if (!fw_priv->fw) { mutex_unlock(&fw_lock); break; } firmware_free_data(fw_priv->fw); memset(fw_priv->fw, 0, sizeof(struct firmware)); /* If the pages are not owned by 'struct firmware' */ for (i = 0; i < fw_priv->nr_pages; i++) __free_page(fw_priv->pages[i]); kfree(fw_priv->pages); fw_priv->pages = NULL; fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; set_bit(FW_STATUS_LOADING, &fw_priv->status); mutex_unlock(&fw_lock); break; case 0: if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { vunmap(fw_priv->fw->data); fw_priv->fw->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0, PAGE_KERNEL_RO); if (!fw_priv->fw->data) { dev_err(dev, "%s: vmap() failed\n", __func__); goto err; } /* Pages are now owned by 'struct firmware' */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) fw_priv->fw->pages = fw_priv->pages; fw_priv->pages = NULL; #endif fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; complete(&fw_priv->completion); clear_bit(FW_STATUS_LOADING, &fw_priv->status); break; } /* fallthrough */ default: dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); /* fallthrough */ case -1: err: fw_load_abort(fw_priv); break; } return count; } static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); #if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP) static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) #else static ssize_t firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) #endif { struct device *dev = compat_firmware_to_dev(kobj); struct firmware_priv *fw_priv = to_firmware_priv(dev); struct firmware *fw; ssize_t ret_count; mutex_lock(&fw_lock); fw = fw_priv->fw; if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) { ret_count = -ENODEV; goto out; } if (offset > fw->size) { ret_count = 0; goto out; } if (count > fw->size - offset) count = fw->size - offset; ret_count = count; while (count) { void *page_data; int page_nr = offset >> PAGE_SHIFT; int page_ofs = offset & (PAGE_SIZE-1); int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); page_data = kmap(fw_priv->pages[page_nr]); memcpy(buffer, page_data + page_ofs, page_cnt); kunmap(fw_priv->pages[page_nr]); buffer += page_cnt; offset += page_cnt; count -= page_cnt; } out: mutex_unlock(&fw_lock); return ret_count; } static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) { int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT; /* If the array of pages is too small, grow it... */ if (fw_priv->page_array_size < pages_needed) { int new_array_size = max(pages_needed, fw_priv->page_array_size * 2); struct page **new_pages; new_pages = kmalloc(new_array_size * sizeof(void *), GFP_KERNEL); if (!new_pages) { fw_load_abort(fw_priv); return -ENOMEM; } memcpy(new_pages, fw_priv->pages, fw_priv->page_array_size * sizeof(void *)); memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) * (new_array_size - fw_priv->page_array_size)); kfree(fw_priv->pages); fw_priv->pages = new_pages; fw_priv->page_array_size = new_array_size; } while (fw_priv->nr_pages < pages_needed) { fw_priv->pages[fw_priv->nr_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); if (!fw_priv->pages[fw_priv->nr_pages]) { fw_load_abort(fw_priv); return -ENOMEM; } fw_priv->nr_pages++; } return 0; } /** * firmware_data_write - write method for firmware * @kobj: kobject for the device * @bin_attr: bin_attr structure * @buffer: buffer being written * @offset: buffer offset for write in total data store area * @count: buffer size * * Data written to the 'data' attribute will be later handed to * the driver as a firmware image. **/ #if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP) static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) #else static ssize_t firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) #endif { struct device *dev = compat_firmware_to_dev(kobj); struct firmware_priv *fw_priv = to_firmware_priv(dev); struct firmware *fw; ssize_t retval; if (!capable(CAP_SYS_RAWIO)) return -EPERM; mutex_lock(&fw_lock); fw = fw_priv->fw; if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) { retval = -ENODEV; goto out; } retval = fw_realloc_buffer(fw_priv, offset + count); if (retval) goto out; retval = count; while (count) { void *page_data; int page_nr = offset >> PAGE_SHIFT; int page_ofs = offset & (PAGE_SIZE - 1); int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); page_data = kmap(fw_priv->pages[page_nr]); memcpy(page_data + page_ofs, buffer, page_cnt); kunmap(fw_priv->pages[page_nr]); buffer += page_cnt; offset += page_cnt; count -= page_cnt; } fw->size = max_t(size_t, offset, fw->size); out: mutex_unlock(&fw_lock); return retval; } static struct bin_attribute firmware_attr_data = { .attr = { .name = "data", .mode = 0644 }, .size = 0, .read = firmware_data_read, .write = firmware_data_write, }; static void firmware_class_timeout(u_long data) { struct firmware_priv *fw_priv = (struct firmware_priv *) data; fw_load_abort(fw_priv); } static struct firmware_priv * fw_create_instance(struct firmware *firmware, const char *fw_name, struct device *device, bool uevent, bool nowait) { struct firmware_priv *fw_priv; struct device *f_dev; int error; fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); if (!fw_priv) { dev_err(device, "%s: kmalloc failed\n", __func__); error = -ENOMEM; goto err_out; } fw_priv->fw = firmware; fw_priv->nowait = nowait; strcpy(fw_priv->fw_id, fw_name); init_completion(&fw_priv->completion); setup_timer(&fw_priv->timeout, firmware_class_timeout, (u_long) fw_priv); f_dev = &fw_priv->dev; device_initialize(f_dev); dev_set_name(f_dev, "%s", dev_name(device)); f_dev->parent = device; f_dev->class = &firmware_class; dev_set_uevent_suppress(f_dev, true); /* Need to pin this module until class device is destroyed */ __module_get(THIS_MODULE); error = device_add(f_dev); if (error) { dev_err(device, "%s: device_register failed\n", __func__); goto err_put_dev; } error = device_create_bin_file(f_dev, &firmware_attr_data); if (error) { dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__); goto err_del_dev; } error = device_create_file(f_dev, &dev_attr_loading); if (error) { dev_err(device, "%s: device_create_file failed\n", __func__); goto err_del_bin_attr; } if (uevent) dev_set_uevent_suppress(f_dev, false); return fw_priv; err_del_bin_attr: device_remove_bin_file(f_dev, &firmware_attr_data); err_del_dev: device_del(f_dev); err_put_dev: put_device(f_dev); err_out: return ERR_PTR(error); } static void fw_destroy_instance(struct firmware_priv *fw_priv) { struct device *f_dev = &fw_priv->dev; device_remove_file(f_dev, &dev_attr_loading); device_remove_bin_file(f_dev, &firmware_attr_data); device_unregister(f_dev); } static int _request_firmware(const struct firmware **firmware_p, const char *name, struct device *device, bool uevent, bool nowait) { struct firmware_priv *fw_priv; struct firmware *firmware; int retval = 0; if (!firmware_p) return -EINVAL; *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n", __func__); retval = -ENOMEM; goto out; } if (fw_get_builtin_firmware(firmware, name)) { dev_dbg(device, "firmware: using built-in firmware %s\n", name); return 0; } if (uevent) dev_dbg(device, "firmware: requesting %s\n", name); fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); if (IS_ERR(fw_priv)) { retval = PTR_ERR(fw_priv); goto out; } if (uevent) { if (loading_timeout > 0) mod_timer(&fw_priv->timeout, round_jiffies_up(jiffies + loading_timeout * HZ)); kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); } wait_for_completion(&fw_priv->completion); set_bit(FW_STATUS_DONE, &fw_priv->status); del_timer_sync(&fw_priv->timeout); mutex_lock(&fw_lock); if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) retval = -ENOENT; fw_priv->fw = NULL; mutex_unlock(&fw_lock); fw_destroy_instance(fw_priv); out: if (retval) { release_firmware(firmware); *firmware_p = NULL; } return retval; } /** * request_firmware: - send firmware request and wait for it * @firmware_p: pointer to firmware image * @name: name of firmware file * @device: device for which firmware is being loaded * * @firmware_p will be used to return a firmware image by the name * of @name for device @device. * * Should be called from user context where sleeping is allowed. * * @name will be used as $FIRMWARE in the uevent environment and * should be distinctive enough not to be confused with any other * firmware image for this or any other device. **/ int compat_request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { int uevent = 1; return _request_firmware(firmware_p, name, device, uevent, false); } /** * release_firmware: - release the resource associated with a firmware image * @fw: firmware resource to release **/ void compat_release_firmware(const struct firmware *fw) { if (fw) { if (!fw_is_builtin_firmware(fw)) firmware_free_data(fw); kfree(fw); } } /* Async support */ struct firmware_work { struct work_struct work; struct module *module; const char *name; struct device *device; void *context; void (*cont)(const struct firmware *fw, void *context); int uevent; }; static int request_firmware_work_func(void *arg) { struct firmware_work *fw_work = arg; const struct firmware *fw; int ret; if (!arg) { WARN_ON(1); return 0; } ret = _request_firmware(&fw, fw_work->name, fw_work->device, fw_work->uevent, true); fw_work->cont(fw, fw_work->context); module_put(fw_work->module); kfree(fw_work); return ret; } /** * request_firmware_nowait - asynchronous version of request_firmware * @module: module requesting the firmware * @uevent: sends uevent to copy the firmware image if this flag * is non-zero else the firmware copy must be done manually. * @name: name of firmware file * @device: device for which firmware is being loaded * @gfp: allocation flags * @context: will be passed over to @cont, and * @fw may be %NULL if firmware request fails. * @cont: function will be called asynchronously when the firmware * request is over. * * Asynchronous variant of request_firmware() for user contexts where * it is not possible to sleep for long time. It can't be called * in atomic contexts. **/ int compat_request_firmware_nowait( struct module *module, int uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)) { struct task_struct *task; struct firmware_work *fw_work; fw_work = kzalloc(sizeof (struct firmware_work), gfp); if (!fw_work) return -ENOMEM; fw_work->module = module; fw_work->name = name; fw_work->device = device; fw_work->context = context; fw_work->cont = cont; fw_work->uevent = uevent; if (!try_module_get(module)) { kfree(fw_work); return -EFAULT; } task = kthread_run(request_firmware_work_func, fw_work, "firmware/%s", name); if (IS_ERR(task)) { fw_work->cont(NULL, fw_work->context); module_put(fw_work->module); kfree(fw_work); return PTR_ERR(task); } return 0; } static int __init firmware_class_init(void) { return class_register(&firmware_class); } static void __exit firmware_class_exit(void) { class_unregister(&firmware_class); } fs_initcall(firmware_class_init); module_exit(firmware_class_exit); EXPORT_SYMBOL_GPL(release_firmware); EXPORT_SYMBOL_GPL(request_firmware); EXPORT_SYMBOL_GPL(request_firmware_nowait); compat-drivers-2012-09-18/compat/crc8.c0000644000175000017500000000466712026211315016663 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include /* * crc8_populate_msb - fill crc table for given polynomial in reverse bit order. * * table: table to be filled. * polynomial: polynomial for which table is to be filled. */ void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial) { int i, j; const u8 msbit = 0x80; u8 t = msbit; table[0] = 0; for (i = 1; i < CRC8_TABLE_SIZE; i *= 2) { t = (t << 1) ^ (t & msbit ? polynomial : 0); for (j = 0; j < i; j++) table[i+j] = table[j] ^ t; } } EXPORT_SYMBOL_GPL(crc8_populate_msb); /* * crc8_populate_lsb - fill crc table for given polynomial in regular bit order. * * table: table to be filled. * polynomial: polynomial for which table is to be filled. */ void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial) { int i, j; u8 t = 1; table[0] = 0; for (i = (CRC8_TABLE_SIZE >> 1); i; i >>= 1) { t = (t >> 1) ^ (t & 1 ? polynomial : 0); for (j = 0; j < CRC8_TABLE_SIZE; j += 2*i) table[i+j] = table[j] ^ t; } } EXPORT_SYMBOL_GPL(crc8_populate_lsb); /* * crc8 - calculate a crc8 over the given input data. * * table: crc table used for calculation. * pdata: pointer to data buffer. * nbytes: number of bytes in data buffer. * crc: previous returned crc8 value. */ u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc) { /* loop over the buffer data */ while (nbytes-- > 0) crc = table[(crc ^ *pdata++) & 0xff]; return crc; } EXPORT_SYMBOL_GPL(crc8); MODULE_DESCRIPTION("CRC8 (by Williams, Ross N.) function"); MODULE_AUTHOR("Broadcom Corporation"); MODULE_LICENSE("Dual BSD/GPL"); compat-drivers-2012-09-18/compat/compat-2.6.23.c0000644000175000017500000001411112026211315020016 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.23. */ #include /* On net/core/dev.c as of 2.6.24 */ int __dev_addr_delete(struct dev_addr_list **list, int *count, void *addr, int alen, int glbl) { struct dev_addr_list *da; for (; (da = *list) != NULL; list = &da->next) { if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 && alen == da->da_addrlen) { if (glbl) { int old_glbl = da->da_gusers; da->da_gusers = 0; if (old_glbl == 0) break; } if (--da->da_users) return 0; *list = da->next; kfree(da); (*count)--; return 0; } } return -ENOENT; } EXPORT_SYMBOL_GPL(__dev_addr_delete); /* On net/core/dev.c as of 2.6.24. This is not yet used by mac80211 but * might as well add it */ int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int glbl) { struct dev_addr_list *da; for (da = *list; da != NULL; da = da->next) { if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 && da->da_addrlen == alen) { if (glbl) { int old_glbl = da->da_gusers; da->da_gusers = 1; if (old_glbl) return 0; } da->da_users++; return 0; } } da = kmalloc(sizeof(*da), GFP_ATOMIC); if (da == NULL) return -ENOMEM; memcpy(da->da_addr, addr, alen); da->da_addrlen = alen; da->da_users = 1; da->da_gusers = glbl ? 1 : 0; da->next = *list; *list = da; (*count)++; return 0; } EXPORT_SYMBOL_GPL(__dev_addr_add); /* Part of net/core/dev_mcast.c as of 2.6.23. This is a slightly different version. * Since da->da_synced is not part of 2.6.22 we need to take longer route when * syncing */ /** * dev_mc_sync - Synchronize device's multicast list to another device * @to: destination device * @from: source device * * Add newly added addresses to the destination device and release * addresses that have no users left. The source device must be * locked by netif_tx_lock_bh. * * This function is intended to be called from the dev->set_multicast_list * function of layered software devices. */ int dev_mc_sync(struct net_device *to, struct net_device *from) { struct dev_addr_list *da, *next, *da_to; int err = 0; netif_tx_lock_bh(to); da = from->mc_list; while (da != NULL) { int synced = 0; next = da->next; da_to = to->mc_list; /* 2.6.22 does not have da->da_synced so lets take the long route */ while (da_to != NULL) { if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 && da->da_addrlen == da_to->da_addrlen) synced = 1; break; } if (!synced) { err = __dev_addr_add(&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen, 0); if (err < 0) break; da->da_users++; } else if (da->da_users == 1) { __dev_addr_delete(&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen, 0); __dev_addr_delete(&from->mc_list, &from->mc_count, da->da_addr, da->da_addrlen, 0); } da = next; } if (!err) __dev_set_rx_mode(to); netif_tx_unlock_bh(to); return err; } EXPORT_SYMBOL_GPL(dev_mc_sync); /* Part of net/core/dev_mcast.c as of 2.6.23. This is a slighty different version. * Since da->da_synced is not part of 2.6.22 we need to take longer route when * unsyncing */ /** * dev_mc_unsync - Remove synchronized addresses from the destination * device * @to: destination device * @from: source device * * Remove all addresses that were added to the destination device by * dev_mc_sync(). This function is intended to be called from the * dev->stop function of layered software devices. */ void dev_mc_unsync(struct net_device *to, struct net_device *from) { struct dev_addr_list *da, *next, *da_to; netif_tx_lock_bh(from); netif_tx_lock_bh(to); da = from->mc_list; while (da != NULL) { bool synced = false; next = da->next; da_to = to->mc_list; /* 2.6.22 does not have da->da_synced so lets take the long route */ while (da_to != NULL) { if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 && da->da_addrlen == da_to->da_addrlen) synced = true; break; } if (!synced) { da = next; continue; } __dev_addr_delete(&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen, 0); __dev_addr_delete(&from->mc_list, &from->mc_count, da->da_addr, da->da_addrlen, 0); da = next; } __dev_set_rx_mode(to); netif_tx_unlock_bh(to); netif_tx_unlock_bh(from); } EXPORT_SYMBOL_GPL(dev_mc_unsync); /* Added as of 2.6.23 on net/core/dev.c. Slightly modifed, no dev->set_rx_mode on * 2.6.22 so ignore that. */ /* * Upload unicast and multicast address lists to device and * configure RX filtering. When the device doesn't support unicast * filtering it is put in promiscous mode while unicast addresses * are present. */ void __dev_set_rx_mode(struct net_device *dev) { /* dev_open will call this function so the list will stay sane. */ if (!(dev->flags&IFF_UP)) return; if (!netif_device_present(dev)) return; /* This needs to be ported to 2.6.22 framework */ #if 0 /* Unicast addresses changes may only happen under the rtnl, * therefore calling __dev_set_promiscuity here is safe. */ if (dev->uc_count > 0 && !dev->uc_promisc) { __dev_set_promiscuity(dev, 1); dev->uc_promisc = 1; } else if (dev->uc_count == 0 && dev->uc_promisc) { __dev_set_promiscuity(dev, -1); dev->uc_promisc = 0; } #endif if (dev->set_multicast_list) dev->set_multicast_list(dev); } /** * pci_try_set_mwi - enables memory-write-invalidate PCI transaction * @dev: the PCI device for which MWI is enabled * * Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND. * Callers are not required to check the return value. * * RETURNS: An appropriate -ERRNO error value on error, or zero for success. */ int pci_try_set_mwi(struct pci_dev *dev) { int rc = 0; #ifdef HAVE_PCI_SET_MWI rc = pci_set_mwi(dev); #endif return rc; } EXPORT_SYMBOL_GPL(pci_try_set_mwi); #endif compat-drivers-2012-09-18/compat/compat-3.2.c0000644000175000017500000000167512026211315017603 0ustar mcgrofmcgrof/* * Copyright 2012 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 3.2. */ #include #include int __netdev_printk(const char *level, const struct net_device *dev, struct va_format *vaf) { int r; if (dev && dev->dev.parent) #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) r = dev_printk(level, dev->dev.parent, "%s: %pV", netdev_name(dev), vaf); #else /* XXX: this could likely be done better but I'm lazy */ r = printk("%s%s: %pV", level, netdev_name(dev), vaf); #endif else if (dev) r = printk("%s%s: %pV", level, netdev_name(dev), vaf); else r = printk("%s(NULL net_device): %pV", level, vaf); return r; } EXPORT_SYMBOL_GPL(__netdev_printk); compat-drivers-2012-09-18/compat/compat-2.6.14.c0000644000175000017500000000060312026211315020017 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.14. */ #include /* 2.6.14 compat code goes here */ compat-drivers-2012-09-18/compat/compat-2.6.19.c0000644000175000017500000000060312026211315020024 0ustar mcgrofmcgrof/* * Copyright 2007 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.19. */ #include /* 2.6.19 compat code goes here */ compat-drivers-2012-09-18/compat/ckmake0000755000175000017500000001415512026211315017032 0ustar mcgrofmcgrof#!/bin/bash # # Copyright (C) 2012, Luis R. Rodriguez # Copyright (C) 2012, Hauke Mehrtens # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # You can use this to compile a module accross all installed kernels # found. This relies on distribution specific kernels, but can handle # your own custom list of target kernels. Log is setnt to LOG variable. #export KCFLAGS="-Wno-unused-but-set-variable" KERNEL_DIR="/lib/modules" KLIBS="" LOG="ckmake.log" LOG_TMP="ckmake-tmp.log" REPORT="ckmake-report.log" TIME="0" DEBUG="0" NOCOLOR="0" ARGS="" QUIET="" RET_FILE="ret-tmp.txt" # First and last kernels to use FIRST="" LAST="" RANGE="" RET="" # If $HOME/compat-ksrc is found use that, otherwise use system-wide # sources in /usr/src. KSRC_PREFIX= if [[ -d "$HOME/compat-ksrc" ]]; then KSRC_PREFIX="$HOME/compat-ksrc" fi # Colorify output if NOCOLOR != 1 or -n is not given function prettify() { if [[ $NOCOLOR == "1" ]]; then echo -n "$2" else ANSI_CODE= NORMAL="\033[00m" case $1 in "green") ANSI_CODE="\033[01;32m" ;; "yellow") ANSI_CODE="\033[01;33m" ;; "blue") ANSI_CODE="\033[34m" ;; "red") ANSI_CODE="\033[31m" ;; "purple") ANSI_CODE="\033[35m" ;; "cyan") ANSI_CODE="\033[36m" ;; "underline") ANSI_CODE="\033[02m" ;; esac echo -e -n "${ANSI_CODE}$2${NORMAL}" fi } function tee_color_split() { while read; do echo -e $REPLY | perl -pe 's|(\e)\[(\d+)(;*)(\d*)(\w)||g' >> $1 echo -e $REPLY done } function log_try_kernel() { printf "Trying kernel %40s\t" "$(prettify blue $1)" } function usage() { echo -e "Usage: $0 [-t] " echo -e "-t will run: 'time ckmake; time ckmake' account for" echo -e " benchmark how long it takes to compile without ccache" echo -e " and a run after cache kicks in" echo -e "-n Do not use colors in the output" echo -e "-q will ask ckmake to run make with -s to only output errors" echo echo -e " is the arguments you want to pass to the" echo -e "child make call that ckmake will use. For example if you have" echo -e "a target 'linux' on your Makefile you can run 'cmake linux'" echo -e "" echo -e " are the kernels you want to test" echo -e "compile against. This consists of a range. The third extraversion" echo -e "number is ignored" } for i in $@ ; do case $1 in "-h") usage exit 1 ;; "--help") usage exit 1 ;; "-t") TIME="1" shift ;; "-n") NOCOLOR="1" shift ;; "-s") QUIET="-s" shift ;; "-d") DEBUG="1" shift ;; *) echo $i | grep "\.\." 2>&1 > /dev/null if [[ $? -eq 0 ]]; then FIRST=$(echo $i | sed 's|\.\.|-|' | awk -F"-" '{print $1}') LAST=$(echo $i | sed 's|\.\.|-|' | awk -F"-" '{print $2}') RANGE="${FIRST}..${LAST}" echo -e "Going to use kernel ranges: $(prettify blue $FIRST)..$(prettify blue $LAST)" shift fi ARGS="${ARGS} $1" shift esac done function run_ckmake() { for i in $KLIBS; do KERNEL=$(basename $i) DIR=${i}/build/ echo -e "--------------------------------------------" >> $LOG if [[ ! -d $DIR ]]; then continue fi NOCOLOR="1" log_try_kernel $KERNEL >> $LOG log_try_kernel $KERNEL #ionice -c 3 nice -n 20 make $QUIET KLIB=$DIR KLIB_BUILD=$DIR -j6 -Wunused-but-set-variable $ARGS &>> $LOG ionice -c 3 nice -n 20 make $QUIET KLIB=$DIR KLIB_BUILD=$DIR -j6 $ARGS &>> $LOG CUR_RET=$? if [[ $RET = "" ]]; then RET=$CUR_RET fi if [[ $CUR_RET -eq 0 ]]; then echo -e "$(prettify green [OK])" | tee_color_split $LOG else echo -e "$(prettify red [FAILED])" | tee_color_split $LOG RET=$CUR_RET fi nice make clean KLIB=$DIR KLIB_BUILD=$DIR 2>&1 >> $LOG done # Bash doesn't do what we expect with the variables... # and using return $RET here won't work here either given # that we end up piping the result anyway. echo $RET > $RET_FILE } # This mimic's the kernel's own algorithm: # # KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) function kernel_version_orig { echo "$@" | awk -F. '{ printf("%d\n", lshift($1,16) + lshift($2, 8) + $3); }' } function kernel_version_26 { kernel_version_orig $@ } # Ignores the last extraversion number function kernel_version_30 { echo "$@" | awk -F. '{ printf("%d\n", lshift($1,16) + lshift($2, 8) ); }' } # If we're using 3.0 kernels we do not require an extraversion, # although one could be supplied. For purposes of this script # though the 2.6.29..3.1 ranges are acceptable. If we forced usage # of kernel_version_orig() for 3.0 it means we'd have to require a user # to be very specific and specific 2.6.29..3.1.0 or whatever. Lets # instead be flexible. function kernel_version { if [[ $(kernel_version_orig $@ ) -lt $(kernel_version_orig "3.0") ]] ; then echo $(kernel_version_26 $@) else echo $(kernel_version_30 $@) fi } for i in $(find $KSRC_PREFIX/lib/modules/ -type d -name \*generic\* | sort -n -r | grep -v -E '\-[[:alnum:]]{1,2}\-'); do KERNEL=$(echo ${i} | awk -F"/" '{print $NF}' | awk -F"-" '{print $1}') if [[ ! -z $FIRST && ! -z $LAST ]]; then if [[ $(kernel_version $KERNEL ) -lt $(kernel_version $FIRST) ]] ; then continue; fi if [[ $(kernel_version $KERNEL ) -gt $(kernel_version $LAST) ]] ; then continue; fi if [[ ! -z $DEBUG ]]; then echo -e "$(prettify cyan $FIRST) $(kernel_version $FIRST) <= $(prettify green $KERNEL) $(kernel_version $KERNEL) <= $(prettify cyan $LAST) $(kernel_version $LAST)" fi fi KLIBS="$KLIBS $i" done for i in $LOG $LOG_TMP $REPORT; do echo > $i done nice make clean 2>&1 > $LOG if [[ $TIME != "1" ]]; then run_ckmake | tee_color_split $REPORT cat $LOG $REPORT > $LOG_TMP mv $LOG_TMP $LOG rm -f $LOG_TMP RET=$(cat $RET_FILE) exit $RET fi time $0 $QUIET ${RANGE} $ARGS | tee_color_split $REPORT time $0 $QUIET ${RANGE} $ARGS | egrep "real|user|sys" | tee_color_split $REPORT cat $LOG $REPORT > $LOG_TMP mv $LOG_TMP $LOG rm -f $LOG_TMP RET=$(cat $RET_FILE) rm -f $RET_FILE exit $RET compat-drivers-2012-09-18/compat/compat-2.6.34.c0000644000175000017500000000362012026211315020023 0ustar mcgrofmcgrof/* * Copyright 2012 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Compatibility file for Linux wireless for kernels 2.6.34. */ #include #include "compat-2.6.34.h" static mmc_pm_flag_t compat_mmc_pm_flags; void init_compat_mmc_pm_flags(void) { compat_mmc_pm_flags = 0; } mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func) { return compat_mmc_pm_flags; } int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags) { return -EINVAL; } /** * seq_hlist_start - start an iteration of a hlist * @head: the head of the hlist * @pos: the start position of the sequence * * Called at seq_file->op->start(). */ struct hlist_node *seq_hlist_start(struct hlist_head *head, loff_t pos) { struct hlist_node *node; hlist_for_each(node, head) if (pos-- == 0) return node; return NULL; } /** * seq_hlist_start_head - start an iteration of a hlist * @head: the head of the hlist * @pos: the start position of the sequence * * Called at seq_file->op->start(). Call this function if you want to * print a header at the top of the output. */ struct hlist_node *seq_hlist_start_head(struct hlist_head *head, loff_t pos) { if (!pos) return SEQ_START_TOKEN; return seq_hlist_start(head, pos - 1); } EXPORT_SYMBOL(seq_hlist_start_head); /** * seq_hlist_next - move to the next position of the hlist * @v: the current iterator * @head: the head of the hlist * @ppos: the current position * * Called at seq_file->op->next(). */ struct hlist_node *seq_hlist_next(void *v, struct hlist_head *head, loff_t *ppos) { struct hlist_node *node = v; ++*ppos; if (v == SEQ_START_TOKEN) return head->first; else return node->next; } EXPORT_SYMBOL(seq_hlist_next); compat-drivers-2012-09-18/COPYRIGHT0000644000175000017500000004440512025712017015666 0ustar mcgrofmcgrof NOTE! This copyright does *not* cover user programs that use kernel services by normal system calls - this is merely considered normal use of the kernel, and does *not* fall under the heading of "derived work". Also note that the GPL below is copyrighted by the Free Software Foundation, but the instance of code that it refers to (the Linux kernel) is copyrighted by me and others who actually wrote it. Also note that the only valid version of the GPL as far as the kernel is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. Linus Torvalds ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. compat-drivers-2012-09-18/udev/0000755000175000017500000000000012025673354015341 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/udev/compat_firmware.sh0000755000175000017500000000174212025673354021063 0ustar mcgrofmcgrof#!/bin/sh -e # This is ported from Ubuntu but ubuntu uses these directories which # other distributions don't care about: # FIRMWARE_DIRS="/lib/firmware/updates/$(uname -r) /lib/firmware/updates \ # /lib/firmware/$(uname -r) /lib/firmware" # If your distribution looks for firmware in other directories # feel free to extend this and add your own directory here. # FIRMWARE_DIRS="/lib/firmware" err() { echo "$@" >&2 logger -t "${0##*/}[$$]" "$@" 2>/dev/null || true } if [ ! -e /sys$DEVPATH/loading ]; then err "udev firmware loader misses sysfs directory" exit 1 fi for DIR in $FIRMWARE_DIRS; do [ -e "$DIR/$FIRMWARE" ] || continue echo 1 > /sys$DEVPATH/loading cat "$DIR/$FIRMWARE" > /sys$DEVPATH/data echo 0 > /sys$DEVPATH/loading exit 0 done echo -1 > /sys$DEVPATH/loading err "Cannot find firmware file '$FIRMWARE'" mkdir -p /dev/.udev/firmware-missing file=$(echo "$FIRMWARE" | sed 's:/:\\x2f:g') ln -s -f "$DEVPATH" /dev/.udev/firmware-missing/$file exit 1 compat-drivers-2012-09-18/udev/50-compat_firmware.rules0000644000175000017500000000030112025673354022010 0ustar mcgrofmcgrof# do not edit this file, it will be overwritten on update # compat_firmware-class requests, copies files into the kernel SUBSYSTEM=="compat_firmware", ACTION=="add", RUN+="compat_firmware.sh" compat-drivers-2012-09-18/udev/ubuntu/0000755000175000017500000000000012025673354016663 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/udev/ubuntu/compat_firmware.sh0000755000175000017500000000132712025673354022404 0ustar mcgrofmcgrof#!/bin/sh -e FIRMWARE_DIRS="/lib/firmware/updates/$(uname -r) /lib/firmware/updates \ /lib/firmware/$(uname -r) /lib/firmware" err() { echo "$@" >&2 logger -t "${0##*/}[$$]" "$@" 2>/dev/null || true } if [ ! -e /sys$DEVPATH/loading ]; then err "udev firmware loader misses sysfs directory" exit 1 fi for DIR in $FIRMWARE_DIRS; do [ -e "$DIR/$FIRMWARE" ] || continue echo 1 > /sys$DEVPATH/loading cat "$DIR/$FIRMWARE" > /sys$DEVPATH/data echo 0 > /sys$DEVPATH/loading exit 0 done echo -1 > /sys$DEVPATH/loading err "Cannot find firmware file '$FIRMWARE'" mkdir -p /dev/.udev/firmware-missing file=$(echo "$FIRMWARE" | sed 's:/:\\x2f:g') ln -s -f "$DEVPATH" /dev/.udev/firmware-missing/$file exit 1 compat-drivers-2012-09-18/scripts/0000755000175000017500000000000012026211313016045 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/scripts/gen-stable-release.sh0000755000175000017500000001441012026211313022043 0ustar mcgrofmcgrof#!/usr/bin/env bash # Copyright 2009 Luis R. Rodriguez # # You can use this to make stable compat-drivers releases # # The assumption is you have the linux-stable git tree on your $HOME # git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git # # Local branches will be created based on the remote linux-2.6.X.y branches. # If your branch already exists we will nuke it for you to avoid rebasing. # # If no kernel is specified we use the latest rc-release, which will be on the # remove master branch. Your master branch should be clean. # Pretty colors GREEN="\033[01;32m" YELLOW="\033[01;33m" NORMAL="\033[00m" BLUE="\033[34m" RED="\033[31m" PURPLE="\033[35m" CYAN="\033[36m" UNDERLINE="\033[02m" GIT_STABLE_URL="git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git" # Note that this tree may not have the latest RC stuff, so you should also add # Linus' tree as a remote and fetch those objects if you want to make an RC # release instead. ALL_STABLE_TREE="linux-stable" STAGING=/tmp/staging/compat-drivers/ function usage() { echo -e "Usage: ${GREEN}$1${NORMAL} ${BLUE}[ -n | -p | -c | -f | -i ]${NORMAL} ${CYAN}[ linux-2.6.X.y ]${NORMAL}" echo echo Examples usages: echo echo -e "${PURPLE}${1}${NORMAL}" echo -e "${PURPLE}${1} ${CYAN}linux-2.6.29.y${NORMAL}" echo -e "${PURPLE}${1} ${CYAN}linux-2.6.30.y${NORMAL}" echo echo -e "If no kernel is specified we try to make a release based on the latest RC kernel." echo -en "If a kernel release is specified ${CYAN}X${NORMAL} is the next stable release " echo -en "as ${CYAN}35${NORMAL} in ${CYAN}2.6.35.y${NORMAL}\n" exit } UPDATE_ARGS="" POSTFIX_RELEASE_TAG="-" if [ -z $GIT_TREE ]; then export GIT_TREE=$HOME/$ALL_STABLE_TREE if [ ! -d $GIT_TREE ]; then echo "Please tell me where your linux-stable git tree is." echo "You can do this by exporting its location as follows:" echo echo " export GIT_TREE=/home/$USER/linux-stable/" echo echo "If you do not have one you can clone the repository:" echo " git clone $GIT_STABLE_URL" exit 1 fi else echo "You said to use git tree at: $GIT_TREE for linux-stable" fi COMPAT_WIRELESS_DIR=$(pwd) COMPAT_WIRELESS_BRANCH=$(git branch | grep \* | awk '{print $2}') cd $GIT_TREE # --abbrev=0 on branch should work but I guess it doesn't on some releases EXISTING_BRANCH=$(git branch | grep \* | awk '{print $2}') # target branch we want to use from hpa's tree, by default # this respects the existing branch on the target kernel. # You can override the target branch by specifying an argument # to this script. TARGET_BRANCH="$EXISTING_BRANCH" # By default we will not do a git fetch and reset of the branch, # use -f if you want to force an update, this will delete all # of your local patches so be careful. FORCE_UPDATE="no" while [ $# -ne 0 ]; do if [[ "$1" = "-s" ]]; then UPDATE_ARGS="${UPDATE_ARGS} $1" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}s" shift; continue; fi if [[ "$1" = "-n" ]]; then UPDATE_ARGS="${UPDATE_ARGS} $1" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}n" shift; continue; fi if [[ "$1" = "-p" ]]; then UPDATE_ARGS="${UPDATE_ARGS} $1" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}p" shift; continue; fi if [[ "$1" = "-c" ]]; then UPDATE_ARGS="${UPDATE_ARGS} $1" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}c" shift; continue; fi if [[ "$1" = "-f" ]]; then FORCE_UPDATE="yes" shift; continue; fi if [[ $(expr "$1" : '^linux-') -eq 6 ]]; then TARGET_BRANCH="$1" shift; continue; fi echo -e "Unexpected argument passed: ${RED}${1}${NORMAL}" usage $0 exit done function check_for_updates() { case $TARGET_BRANCH in "master") # Preparing a new stable compat-drivers release based on an RC kernel git checkout -f git fetch git reset --hard origin ;; *) # Based on a stable {2.6.x.y, 3.x.y} release, lets just move to the target branch # we'll only ask for object updates if and only if you asked us to with -f, # otherwise we eat up whatever you already have on your existing branch. git checkout -f git fetch git branch -D $TARGET_BRANCH git checkout -b $TARGET_BRANCH origin/$TARGET_BRANCH ;; esac } # We will not update your linux-stable git tree by default. You can force # an update by two methods: # # a) Specifying a different target branch # b) Specifying the -f flag to this script if [[ "$FORCE_UPDATE" = "yes" || "$TARGET_BRANCH" != "$EXISTING_BRANCH" ]]; then check_for_updates else echo -e "Skipping $ALL_STABLE_TREE git tree update checks for branch: $TARGET_BRANCH" fi echo "On $ALL_STABLE_TREE: $TARGET_BRANCH" # At this point your linux-stable tree should be up to date # with the target kernel you want to use. Lets now make sure you are # on matching compat-drivers branch. # This is a super hack, but let me know if you figure out a cleaner way TARGET_KERNEL_RELEASE=$(make VERSION="linux-3" SUBLEVEL="" EXTRAVERSION=".y" kernelversion) BASE_TREE=$(basename $GIT_TREE) if [[ $COMPAT_WIRELESS_BRANCH != $TARGET_KERNEL_RELEASE && $BASE_TREE != "linux-next" ]]; then echo -e "You are on the compat-drivers ${GREEN}${COMPAT_WIRELESS_BRANCH}${NORMAL} but are " echo -en "on the ${RED}${TARGET_KERNEL_RELEASE}${NORMAL} branch... " echo -e "try changing to that first." read -p "Do you still want to continue (y/N)? " if [[ "${REPLY}" != "y" ]]; then echo -e "Bailing out !" exit fi fi cd $COMPAT_WIRELESS_DIR if [[ $COMPAT_WIRELESS_BRANCH != "master" ]]; then RELEASE=$(git describe --abbrev=0 | sed -e 's/v//g') else RELEASE=$(git describe --abbrev=0) fi if [[ $POSTFIX_RELEASE_TAG != "-" ]]; then RELEASE="${RELEASE}${POSTFIX_RELEASE_TAG}" fi RELEASE_TAR="$RELEASE.tar.bz2" rm -rf $STAGING mkdir -p $STAGING cp -a $COMPAT_WIRELESS_DIR $STAGING/$RELEASE cd $STAGING/$RELEASE ./scripts/admin-update.sh $UPDATE_ARGS rm -rf $STAGING/$RELEASE/.git # Remove any gunk echo echo "Cleaning up the release ..." make clean 2>&1 > /dev/null find ./ -type f -name *.orig | xargs rm -f find ./ -type f -name *.rej | xargs rm -f cd $STAGING/ echo "Creating $RELEASE_TAR ..." tar -cf ${RELEASE}.tar $RELEASE/ gpg --armor --detach-sign ${RELEASE}.tar echo echo "Compat-wireles release: $RELEASE" echo "Size: $(du -h ${RELEASE}.tar)" echo "sha1sum: $(sha1sum ${RELEASE}.tar)" echo echo "Release: ${STAGING}${RELEASE}.tar" echo "Release signature: ${STAGING}${RELEASE}.tar.asc" compat-drivers-2012-09-18/scripts/update-initramfs0000755000175000017500000000221612025712017021256 0ustar mcgrofmcgrof#!/bin/bash # Copyright 2009 Luis R. Rodriguez # # Since we provide ssb, the Ethernet module b44 some people may # rely on it to netboot, so update the initrafms for each # distribution. # # Note that in the future people may want to wireless-boot # so this will help with that as well. LSB_RED_ID=$(/usr/bin/lsb_release -i -s) KLIB=/lib/modules/$(uname -r)/build ver=$(echo $KLIB | awk -F "/lib/modules/" '{print $2}' | awk -F"/" '{print $1}') dir=/boot/ case $LSB_RED_ID in "Ubuntu") echo "Updating Ubuntu's initramfs for $ver under $dir ..." mkinitramfs -o $dir/initrd.img-$ver $ver echo "Will now run update-grub to ensure grub will find the new initramfs ..." update-grub ;; *) echo "Warning:" echo "You may or may not need to update your initframfs, you should if" echo "any of the modules installed are part of your initramfs. To add" echo "support for your distribution to do this automatically send a" echo "patch against $0. If your distribution does not require this" echo "send a patch against the '/usr/bin/lsb_release -i -s': $LSB_RED_ID" echo "tag for your distribution to avoid this warning." ;; esac compat-drivers-2012-09-18/scripts/modlib.sh0000755000175000017500000000474412025712017017671 0ustar mcgrofmcgrof#!/bin/bash # # Copyright 2007 Luis R. Rodriguez # # You can use these to enable/disable modules without blacklisting them # Make sure our imporant paths are included PATH=$PATH:/usr/sbin:/sbin # Appended to module file at the end when we want to ignore one IGNORE_SUFFIX=".ignore" VER=`uname -r` # If 'module' is found, its renamed to 'module.ignore' function module_disable { # Basic check to see if this is a module available for loading MODULE_CHECK=`modprobe -l $1` if [ -z $MODULE_CHECK ]; then echo "Module $1 not detected -- this is fine" return fi MODULE=$1 MODULE_KO=${MODULE}.ko # In case there are more than one of these modules. This can # happen, for example if your distribution provides one and you have # compiled one in yourself later. MODULE_COUNT=`find /lib/modules/$VER/ -name $MODULE_KO | wc -l` ALL_MODULES=`find /lib/modules/$VER/ -name $MODULE_KO` COUNT=1 CHECK=`modprobe -l $MODULE` for i in $ALL_MODULES; do if [[ $MODULE_COUNT -gt 1 ]]; then if [[ $COUNT -eq 1 ]]; then echo -en "$MODULE_COUNT $MODULE modules found " echo -e "we'll disable all of them" fi echo -en "Disabling $MODULE ($COUNT) ..." else echo -en "Disabling $MODULE ..." fi mv -f $i ${i}${IGNORE_SUFFIX} depmod -a CHECK_AGAIN=`modprobe -l $MODULE` if [ "$CHECK" != "$CHECK_AGAIN" ]; then echo -e "\t[OK]\tModule disabled:" echo "$CHECK" else echo -e "[ERROR]\tModule is still being detected:" echo "$CHECK" fi let COUNT=$COUNT+1 done } # If 'module.ignore' is found, rename it back to 'module' function module_enable { MODULE=$1 MODULE_KO=${MODULE}.ko IGNORED_MODULE=${MODULE_KO}${IGNORE_SUFFIX} # In case there are more than one of these modules. This can # happen, for example if your distribution provides one and you have # compiled one in yourself later. ALL_MODULES=`find /lib/modules/$VER/ -name $IGNORED_MODULE` for i in $ALL_MODULES; do echo -en "Enabling $MODULE ..." DIR=`dirname $i` mv $i $DIR/$MODULE_KO depmod -a CHECK=`modprobe -l $MODULE` if [ "$DIR/$MODULE_KO" != $CHECK ]; then if [ -z $CHECK ]; then echo -e "\t[ERROR]\tModule could not be enabled" else echo -en "\t[OK]\tModule renamed but another " echo "module file is being preferred" echo -e "Renamed module:\t\t$DIR/$MODULE_KO" echo -e "Preferred module:\t$CHECK" fi else echo -e "\t[OK]\tModule enabled: " echo "$DIR/$MODULE_KO" fi # Lets only do this for the first module found break done } compat-drivers-2012-09-18/scripts/iwl-enable0000755000175000017500000000207712026176477020045 0ustar mcgrofmcgrof#!/bin/bash # # Copyright 2007 Luis R. Rodriguez # # Makes sure either iwlagn (new) or iwl4965 (old) # is enabled to be used. This allows us to choose any driver without # blacklisting each other. . /usr/lib/compat-drivers/modlib.sh if [[ $UID -ne 0 ]]; then echo "Run with root privileges" exit fi IWL_NEW="iwlagn" IWL_OLD="iwl4965" # Appended to module file at the end when we want to ignore one USAGE="Usage: $0 [ $IWL_NEW | $IWL_OLD | iwlwifi ]" function enable_iwlwifi { for i in $IWL_OLD $IWL_NEW; do module_disable $i done module_enable iwlwifi } function enable_iwlagn { module_disable $IWL_OLD for i in $IWL_NEW; do module_enable $i done } # Default behavior: disables the old iwl4965 driver and enables iwlagn if [ $# -eq 0 ]; then enable_iwlagn exit elif [ $# -ne 1 ]; then echo "$USAGE" exit fi MODULE=$1 if [ "$MODULE" == "iwl4965" ]; then module_disable $IWL_NEW module_enable $IWL_OLD elif [ "$MODULE" == "iwlagn" ]; then enable_iwlagn elif [ "$MODULE" == "iwlwifi" ]; then enable_iwlwifi else echo "$USAGE" exit fi compat-drivers-2012-09-18/scripts/driver-select0000755000175000017500000002723012026176477020574 0ustar mcgrofmcgrof#!/usr/bin/env bash # Copyright 2009 Luis R. Rodriguez # # This script allows you to select your compat-drivers driver and # reduce compilation time. COMPAT_CONFIG_CW="config.mk" DRIVERS_MAKEFILE="drivers/net/wireless/Makefile" ATH_MAKEFILE="drivers/net/wireless/ath/Makefile" ATH9K_MAKEFILE="drivers/net/wireless/ath/ath9k/Makefile" BRCM80211_MAKEFILE="drivers/net/wireless/brcm80211/Makefile" RT2X00_MAKEFILE="drivers/net/wireless/rt2x00/Makefile" TI_MAKEFILE="drivers/net/wireless/ti/Makefile" NET_WIRELESS_MAKEFILE="net/wireless/Makefile" EEPROM_MAKEFILE="drivers/misc/eeprom/Makefile" DRIVERS_NET_ATHEROS="drivers/net/ethernet/atheros/Makefile" DRIVERS_NET_BROADCOM="drivers/net/ethernet/broadcom/Makefile" DRIVERS_NET_USB_MAKEFILE="drivers/net/usb/Makefile" SSB_MAKEFILE="drivers/ssb/Makefile" BCMA_MAKEFILE="drivers/bcma/Makefile" # used to backup files from foo to foo.${BACKUP_EXT} # If you change this also modify restore_compat() and # restore_file() below I couldn't find a way to use # the $BACKUP_EXT there. BACKUP_EXT="bk" # Pretty colors GREEN="\033[01;32m" YELLOW="\033[01;33m" NORMAL="\033[00m" BLUE="\033[34m" RED="\033[31m" PURPLE="\033[35m" CYAN="\033[36m" UNDERLINE="\033[02m" SUPPORTED_80211_DRIVERS="ath5k ath9k ath9k_ap ath9k_htc carl9170 ath6kl wil6210 b43 zd1211rw rt2x00 wl1251 wl12xx brcmsmac brcmfmac" # b43 needs some more work for driver-select, the SSB stuff, plus # what if you update b44 but not b43? It will bust. SUPPORTED_ETH_DRIVERS="atl1 atl2 atl1e atl1c alx" function usage { echo -e "${GREEN}Usage${NORMAL}: ${BOLD}$0${NORMAL} [ ${PURPLE}${NORMAL} | ${CYAN}${NORMAL} | ${GREEN}restore${NORMAL} ]" # These should match the switch below. echo -e "Supported 802.11 drivers:" for i in $SUPPORTED_80211_DRIVERS; do echo -e "\t${PURPLE}${i}${NORMAL}" done echo echo -e "Supported Ethernet drivers:" for i in $SUPPORTED_ETH_DRIVERS; do echo -e "\t${PURPLE}${i}${NORMAL}" done # These should match the switch below. echo -e "\nSupported group drivers:" echo -e "\t${CYAN}atheros${NORMAL} < ${PURPLE} ath5k ath9k carl9170 zd1211rw ath6kl wil6210${NORMAL}>" echo -e "\t${CYAN}ath${NORMAL} < ${PURPLE} ath5k ath9k carl9170 ath6kl wil6210${NORMAL}>" echo -e "\t${CYAN}brcm80211${NORMAL} < ${PURPLE} brcmsmac brcmfmac ${NORMAL}>" echo -e "\t${CYAN}intel${NORMAL} < ${PURPLE} iwlwifi, iwlegacy ${NORMAL}>" echo -e "\t${CYAN}rtl818x${NORMAL} < ${PURPLE} rtl8180 rtl8187 ${NORMAL}>" echo -e "\t${CYAN}rtlwifi${NORMAL} < ${PURPLE} rtl8192ce ${NORMAL}>" echo -e "\t${CYAN}ti${NORMAL} < ${PURPLE} wl1251 wl12xx (SPI and SDIO)${NORMAL}>" echo -e "\nSupported group drivers: Bluetooth & Ethernet:" echo -e "\t${CYAN}atlxx${NORMAL} < ${PURPLE} atl1 atl2 atl1e alx${NORMAL}>" echo -e "\t${CYAN}bt${NORMAL} < ${PURPLE} Linux bluetooth drivers ${NORMAL}>" echo -e "Restoring compat-drivers:" echo -e "\t${GREEN}restore${NORMAL}: you can use this option to restore compat-drivers to the original state" } function backup_file { if [ -f $1.${BACKUP_EXT} ]; then echo -e "Backup exists: ${CYAN}${1}.${BACKUP_EXT}${NORMAL}" return fi echo -e "Backing up makefile: ${CYAN}${1}.${BACKUP_EXT}${NORMAL}" cp $1 $1.bk } function disable_makefile { backup_file $1 echo > $1 } function select_drivers_from_makefile { MAKEFILE="$1" shift backup_file $MAKEFILE CONFIGS="" COUNT=0 for i in $@; do if [[ "$CONFIGS" = "" ]]; then CONFIGS="$i" else CONFIGS="${CONFIGS}|$i" fi done egrep "$CONFIGS" $MAKEFILE > ${MAKEFILE}.tmp mv ${MAKEFILE}.tmp ${MAKEFILE} } function select_drivers { select_drivers_from_makefile $DRIVERS_MAKEFILE $@ } function disable_lib80211 { backup_file $NET_WIRELESS_MAKEFILE perl -i -ne 'print if ! /LIB80211/ ' $NET_WIRELESS_MAKEFILE } function disable_b44 { backup_file $DRIVERS_NET_BROADCOM perl -i -ne 'print if ! /CONFIG_B44/ ' $DRIVERS_NET_BROADCOM } function disable_ssb { disable_makefile ${SSB_MAKEFILE} perl -i -ne 'print if ! /drivers\/ssb\//' Makefile } function disable_bcma { disable_makefile ${BCMA_MAKEFILE} perl -i -ne 'print if ! /drivers\/bcma\//' Makefile } function disable_rfkill { backup_file Makefile perl -i -ne 'print if ! /CONFIG_COMPAT_RFKILL/' Makefile } function disable_eeprom { disable_makefile ${EEPROM_MAKEFILE} perl -i -ne 'print if ! /drivers\/misc\/eeprom\//' Makefile } function disable_usbnet { disable_makefile ${DRIVERS_NET_USB_MAKEFILE} perl -i -ne 'print if ! /drivers\/net\/usb\//' Makefile } function disable_usbnet { perl -i -ne 'print if ! /CONFIG_COMPAT_NET_USB_MODULES/' Makefile } function disable_ethernet { perl -i -ne 'print if ! /CONFIG_COMPAT_NETWORK_MODULES/' Makefile } function disable_var_03 { perl -i -ne 'print if ! /CONFIG_COMPAT_VAR_MODULES/' Makefile } function disable_bt { perl -i -ne 'print if ! /CONFIG_COMPAT_BLUETOOTH/' Makefile } function disable_80211 { perl -i -ne 'print if ! /CONFIG_COMPAT_WIRELESS/' Makefile } function disable_bt_usb_ethernet { backup_file Makefile disable_usbnet disable_ethernet disable_bt disable_update-initramfs } function disable_bt_usb_ethernet_var { backup_file Makefile disable_bt_usb_ethernet disable_var_03 } function enable_only_ethernet { backup_file Makefile backup_file $DRIVERS_NET_BROADCOM backup_file $DRIVERS_NET_ATHEROS disable_staging disable_usbnet disable_var_03 disable_bt # rfkill may be needed if you enable b44 as you may have b43 disable_rfkill disable_80211 } function disable_var { disable_ssb disable_bcma disable_usbnet disable_eeprom disable_update-initramfs } function disable_var_01 { disable_lib80211 disable_var } function disable_var_02 { #var_01 with eeprom not disabled disable_lib80211 disable_ssb disable_bcma disable_usbnet disable_update-initramfs } function disable_staging { backup_file Makefile perl -i -ne 'print if ! /CONFIG_COMPAT_STAGING/ ' Makefile } function disable_update-initramfs { backup_file Makefile perl -i -ne 'print if ! /update-initramfs/' Makefile } function select_ath_driver { backup_file $ATH_MAKEFILE perl -i -ne 'print if /'$1'/ || /CONFIG_ATH_/ || /ath-objs/ || /regd.o/ || /hw.o/ || /key.o/' $ATH_MAKEFILE disable_var_01 } function select_ath9k_driver { select_ath_driver CONFIG_ATH9K_HW # In the future here we'll add stuff to disable ath9k_htc } function select_ath9k_driver_ap { select_ath9k_driver backup_file $COMPAT_CONFIG_CW perl -i -ne 'print if ! /CONFIG_COMPAT_ATH9K_RATE_CONTROL/ ' $COMPAT_CONFIG_CW } function select_ti_drivers { select_drivers CONFIG_WL_TI select_drivers_from_makefile $TI_MAKEFILE $@ } function select_brcm80211_driver { backup_file $BRCM80211_MAKEFILE perl -i -ne 'print if /'$1'/ || /CONFIG_BRCMUTIL/ ' $BRCM80211_MAKEFILE } function restore_file { #ORIG=$(shell ${1%%.${BACKUP_EXT}}) ORIG=${1%%.bk} cp $1 $ORIG rm -f $1 echo -e "Restored makefile: ${CYAN}${ORIG}${NORMAL} (and removed backup)" } function restore_compat { #FILES=$(find ./ -type f -name *.$BACKUP_EXT) FILES=$(find ./ -type f -name *.bk) for i in $FILES; do restore_file $i done } if [ $# -ne 1 ]; then usage exit fi if [ ! -f .compat_version ]; then echo "Must run $0 from the compat-drivers top level directory" exit fi if [[ ! -f built-in.o ]]; then if [[ "$1" != "restore" ]]; then echo -e "${PURPLE}Processing new driver-select request...${NORMAL}" fi fi # Always backup the top level Makefile, unless restoring if [[ "$1" != "restore" ]]; then backup_file Makefile fi # If a user selects a new driver make sure we clean up for them # first and also restore the backup makefiles then. Otherwise # we'll be trying to leave drivers on Makefiles which are not # already there from a previous run. if [ -f built-in.o ]; then echo -e "${PURPLE}Old build found, going to clean this up first...${NORMAL}" make clean echo -e "${PURPLE}Restoring Makefiles...${NORMAL}" ./$0 restore fi case $1 in restore) restore_compat ;; # Group drivers atheros) select_drivers CONFIG_ATH_COMMON \ CONFIG_COMPAT_ZD1211RW disable_staging disable_bt_usb_ethernet_var disable_var_01 ;; ath) disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON disable_var_01 ;; intel) select_drivers CONFIG_IWLWIFI \ CONFIG_IWLEGACY \ CONFIG_IPW disable_staging disable_var disable_bt disable_ethernet disable_usbnet ;; iwlwifi) select_drivers CONFIG_IWLWIFI disable_staging disable_var_01 disable_bt disable_ethernet disable_usbnet ;; iwlegacy) select_drivers CONFIG_IWLEGACY disable_staging disable_var_01 disable_bt disable_ethernet disable_usbnet ;; rtl818x) select_drivers CONFIG_RTL8180 CONFIG_RTL8187 disable_staging disable_bt_usb_ethernet disable_ssb disable_bcma disable_lib80211 ;; rtlwifi) select_drivers CONFIG_RTL8192CE CONFIG_RTLWIFI disable_staging disable_bt_usb_ethernet_var disable_lib80211 ;; ti) select_drivers CONFIG_WL_TI disable_staging disable_var_01 ;; brcm80211) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_BRCMUTIL \ CONFIG_BRCMFMAC \ CONFIG_BRCMSMAC ;; # Singular modules ath5k) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON select_ath_driver CONFIG_ATH5K #patch -p1 < enable-older-kernels/enable-2.6.23.patch ;; ath9k) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON select_ath9k_driver ;; ath9k_ap) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON select_ath9k_driver_ap ;; carl9170) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON select_ath_driver CONFIG_CARL9170 ;; ath9k_htc) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON select_ath9k_driver ;; ath6kl) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON select_ath_driver CONFIG_ATH6KL ;; wil6210) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_ATH_COMMON select_ath_driver CONFIG_WIL6210 ;; brcmsmac) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_BRCMSMAC select_brcm80211_driver CONFIG_BRCMSMAC CONFIG_BRCMUTIL ;; brcmfmac) disable_staging disable_bt_usb_ethernet_var select_drivers CONFIG_BRCMFMAC select_brcm80211_driver CONFIG_BRCMFMAC CONFIG_BRCMUTIL ;; zd1211rw) select_drivers CONFIG_COMPAT_ZD1211RW disable_staging disable_var_01 ;; b43) disable_staging disable_bt_usb_ethernet disable_eeprom disable_lib80211 select_drivers CONFIG_B43 ;; rt2x00) select_drivers CONFIG_RT2X00 disable_staging disable_bt_usb_ethernet disable_var_02 ;; wl1251) select_ti_drivers CONFIG_WL1251 disable_staging disable_var_01 ;; wl12xx) select_ti_drivers CONFIG_WL12XX disable_staging disable_var_01 ;; wl18xx) select_ti_drivers CONFIG_WL18XX disable_staging disable_var_01 ;; # Ethernet and Bluetooth drivers atl1) enable_only_ethernet disable_b44 echo -e "obj-\$(CONFIG_ATL1) += atlx/" > $DRIVERS_NET_ATHEROS ;; atl2) enable_only_ethernet disable_b44 echo -e "obj-\$(CONFIG_ATL2) += atlx/" > $DRIVERS_NET_ATHEROS ;; atl1e) enable_only_ethernet disable_b44 echo -e "obj-\$(CONFIG_ATL1E) += atl1e/" > $DRIVERS_NET_ATHEROS ;; atl1c) enable_only_ethernet disable_b44 echo -e "obj-\$(CONFIG_ATL1C) += atl1c/" > $DRIVERS_NET_ATHEROS ;; alx) enable_only_ethernet disable_b44 echo -e "obj-\$(CONFIG_ALX) += alx/" > $DRIVERS_NET_ATHEROS ;; atlxx) select_drivers CONFIG_ATL1 CONFIG_ATL2 CONFIG_ATL1E CONFIG_ALX enable_only_ethernet disable_b44 disable_update-initramfs ;; bt) select_drivers CONFIG_BT disable_var disable_ethernet disable_staging disable_80211 ;; *) echo "Unsupported driver" exit ;; esac compat-drivers-2012-09-18/scripts/refresh-compat-local0000755000175000017500000000237112026176477022032 0ustar mcgrofmcgrof#!/bin/bash # # Copyright 2012 Luis R. Rodriguez # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # You can keep this in synch with your git tree after running # This script just update your local copy. source .compat_copy UPDATE_SCRIPT="$COMPAT_SRC/scripts/refresh-compat" if [[ ! -d $COMPAT_SRC ]]; then echo -e "You should have compat-drivers.git cloned and checked out under $COMPAT_SRC" echo -e "The git tree: git://github.com/mcgrof/compat-drivers" exit 1 fi if [[ ! -f $UPDATE_SCRIPT ]]; then echo -e "$UPDATE_SCRIPT must exist" exit 1 fi exec $UPDATE_SCRIPT compat-drivers-2012-09-18/scripts/compress_modules0000755000175000017500000000153212025712017021365 0ustar mcgrofmcgrof#!/bin/bash # To be used by distributions using compressed modules COMPRESSION_FOUND="n" COUNT=0; for i in $(modprobe -l mac80211); do let COUNT=$COUNT+1 i=${i##*/} if [ "$i" = "mac80211.ko.gz" ]; then COMPRESSION_FOUND="y" continue fi done if [ $COUNT -gt 2 ]; then echo "More than two mac80211 modules are detected, please report this." exit fi if [ $COMPRESSION_FOUND = "n" ]; then exit fi DIRS="$KLIB/$KMODDIR/net/mac80211/" # This handles both drivers/net/ and drivers/net/wireless/ DIRS="$DIRS $KLIB/$KMODDIR/net/" DIRS="$DIRS $KLIB/$KMODDIR/drivers/ssb/" DIRS="$DIRS $KLIB/$KMODDIR/drivers/net/usb/" DIRS="$DIRS $KLIB/$KMODDIR/drivers/net/wireless/" DIRS="$DIRS $KLIB/$KMODDIR/drivers/misc/eeprom/" for i in $DIRS; do if [ ! -d $i ]; then continue; fi for driver in $(find $i -type f -name *.ko); do gzip -9 $driver done done compat-drivers-2012-09-18/scripts/unload.sh0000755000175000017500000000521012025712017017672 0ustar mcgrofmcgrof#!/bin/bash # The old stack drivers and the mac80211 rc80211_simple modules # which is no longer on recent kernels (its internal) OLD_MODULES="iwlwifi_mac80211 rc80211_simple zd1211rw-mac80211" OLD_MODULES="$OLD_MODULES ieee80211_crypt_tkip ieee80211_crypt_ccmp" OLD_MODULES="$OLD_MODULES ieee80211softmac ieee80211_crypt ieee80211" OLD_MODULES="$OLD_MODULES bcm43xx rndis_wext iwl4965" MODULES="$OLD_MODULES" MODULES="$MODULES ipw2100 ipw2200 libipw" MODULES="$MODULES wl1251 wl12xx" MODULES="$MODULES libertas_cs usb8xxx libertas libertas_sdio libertas_spi" MODULES="$MODULES libertas_tf libertas_tf_usb" MODULES="$MODULES adm8211 zd1211rw" MODULES="$MODULES orinoco_cs orinoco_nortel orinoco_pci orinoco_plx" MODULES="$MODULES orinoco_tld orinoco_usb spectrum_cs orinoco" MODULES="$MODULES b43 b44 b43legacy brcm80211 ssb" MODULES="$MODULES iwl3945 iwlwifi iwlagn iwlcore" MODULES="$MODULES ath9k ath9k_htc ath9k_common ath9k_hw " MODULES="$MODULES ath5k ath ath6kl ar9170usb carl9170" MODULES="$MODULES p54pci p54usb p54spi p54common" MODULES="$MODULES rt2400pci rt2500pci rt61pci" MODULES="$MODULES rt2500usb rt73usb" MODULES="$MODULES rt2800usb rt2800pci rt2800lib" MODULES="$MODULES rt2x00usb rt2x00pci rt2x00lib" MODULES="$MODULES rtl8180 rtl8187 rtl8192ce rtlwifi" MODULES="$MODULES mwl8k mac80211_hwsim" MODULES="$MODULES at76c50x_usb at76_usb" MODULES="$MODULES rndis_wlan rndis_host cdc_ether usbnet" # eeprom_93cx6 is used by rt2x00 (rt61pci, rt2500pci, rt2400pci) # and Realtek drivers ( rtl8187, rtl8180) MODULES="$MODULES eeprom_93cx6" MODULES="$MODULES lib80211_crypt_ccmp lib80211_crypt_tkip lib80211_crypt_wep" MODULES="$MODULES mac80211 cfg80211 lib80211" MODULES="$MODULES compat" # Bluetooth modules MODULES="$MODULES ath3k bcm203x bluecard_cs bnep bpa10x bt3c_cs btmrvl btmrvl_sdio btsdio" MODULES="$MODULES btusb btuart_cs cmtp dtl1_cs hidp hci_vhci hci_uart rfcomm sco bluetooth l2cap" MODULES="$MODULES atl1 atl2 atl1e atl1c alx" echo Stoping bluetooth service.. /etc/init.d/bluetooth stop /etc/init.d/bluetooth status MADWIFI_MODULES="ath_pci ath_rate_sample wlan_scan_sta wlan ath_hal" IPW3945D="/sbin/ipw3945d-`uname -r`" if [ -f $IPW3945D ]; then $IPW3945D --isrunning if [ ! $? ]; then echo -n "Detected ipw3945 daemon loaded we're going to " echo "shut the daemon down now and remove the module." modprobe -r --ignore-remove ipw3945 fi fi grep ath_pci /proc/modules 2>&1 > /dev/null if [ $? -eq 0 ]; then echo "MadWifi driver is loaded, going to try to unload it..." ./scripts/madwifi-unload fi for i in $MODULES; do grep ^$i /proc/modules 2>&1 > /dev/null if [ $? -eq 0 ]; then echo Unloading $i... modprobe -r --ignore-remove $i fi done compat-drivers-2012-09-18/scripts/check_config.sh0000755000175000017500000000172112026176477021034 0ustar mcgrofmcgrof#!/usr/bin/env bash # This script checks the compat-drivers configuration file and if changes were made # regenerates the compat_autoconf header. # These variables are expected to be exported: #COMPAT_CONFIG=".config" #COMPAT_CONFIG_CW="config" #CONFIG_CHECK=".${COMPAT_CONFIG}.md5" #COMPAT_AUTOCONF="include/linux/compat_autoconf.h" function gen_compat_autoconf { echo "./scripts/gen-compat-autoconf.sh $COMPAT_CONFIG $COMPAT_CONFIG_CW > $COMPAT_AUTOCONF" ./scripts/gen-compat-autoconf.sh $COMPAT_CONFIG $COMPAT_CONFIG_CW > $COMPAT_AUTOCONF md5sum $COMPAT_CONFIG $COMPAT_CONFIG_CW > $CONFIG_CHECK } which md5sum 2>&1 > /dev/null if [ $? -ne 0 ]; then echo "md5sum required to detect changes on config file" exit -1 fi if [ ! -f $CONFIG_CHECK ]; then gen_compat_autoconf exit fi md5sum -c $CONFIG_CHECK 2> /dev/null 1>/dev/null if [ $? -ne 0 ]; then echo "Changes to compat-drivers's configuration was detected, regenerating autoconf..." gen_compat_autoconf fi compat-drivers-2012-09-18/scripts/madwifi-unload0000755000175000017500000000217212025712017020703 0ustar mcgrofmcgrof#!/bin/sh # Copyright 2006 Kel Modderman # # Taken from madwifi scripts. This unloads madwifi : ${PATTERN='\(ath_.*\|wlan_.*\|wlan\)$'} : ${MAX_TRIES=10} test "`id -u`" = 0 || { echo "ERROR: You must be root to run this script" >&2 exit 1 } test -r /proc/modules || { echo "ERROR: Cannot read /proc/modules" >&2 exit 1 } tries="$MAX_TRIES" while test "$tries" != "0"; do skipped=0 IFS=' ' for line in `cat /proc/modules`; do IFS=' ' set x $line name="$2" size="$3" use_count="$4" use_name="$5" state="$6" expr "$name" : "$PATTERN" >/dev/null || continue # Compatibility for Linux 2.4.x test -z "$state" && { use_name="-"; state="Live"; } if test "$state" != "Live" || test "$use_count" != "0" || \ test "$use_name" != "-"; then # Don't skip unload in the last run if test "$tries" != "1"; then skipped=1 continue fi fi echo "Unloading \"$name\"" sync # to be safe /sbin/rmmod "$name" || { echo "ERROR: cannot unload module \"$name\"" >&2 exit 1 } sync # to be even safer done test "$skipped" = "0" && break tries=$(($tries - 1)) done exit 0 compat-drivers-2012-09-18/scripts/admin-clean.sh0000755000175000017500000000037612026176477020607 0ustar mcgrofmcgrof#!/usr/bin/env bash if [ -d net ] ; then make clean fi rm -rf net rm -rf drivers rm -rf include rm -rf compat rm -rf udev rm -f .compat_base_tree rm -f .compat_base_tree_version rm -f .compat_version rm -f code-metrics.txt echo "Cleaned compat-drivers" compat-drivers-2012-09-18/scripts/athload0000755000175000017500000000223112026176477017432 0ustar mcgrofmcgrof#!/bin/bash # Copyright 2007 Luis R. Rodriguez # # Loads ath5k or madwifi . /usr/lib/compat-drivers/modlib.sh if [[ $UID -ne 0 ]]; then echo "Run with root privileges" exit fi USAGE="Usage: $0 [ ath5k | madwifi ]" # Default behavior: unload MadWifi and load ath5k if [ $# -eq 0 ]; then athenable ath5k exit elif [ $# -ne 1 ]; then echo "$USAGE" exit fi MODULE=$1 if [ "$MODULE" == "ath5k" ]; then madwifi-unload athenable ath5k modprobe ath5k CHECK=`modprobe -l ath5k` if [ ! -z $CHECK ]; then echo "ath5k loaded successfully" fi modprobe ath9k CHECK=`modprobe -l ath9k` if [ ! -z $CHECK ]; then echo "ath9k loaded successfully" fi elif [ "$MODULE" == "madwifi" ]; then CHECK=`modprobe -l ath5k` if [ ! -z $CHECK ]; then echo "ath5k currently loaded, going to try to unload the module..." modprobe -r --ignore-remove ath5k fi athenable madwifi # MadWifi may be loaded, but it doesn't mean devices # currently available were picked up madwifi-unload 2>&1 > /dev/null modprobe ath_pci CHECK=`modprobe -l ath_pci` if [ ! -z $CHECK ]; then echo "MadWifi loaded successfully!" fi else echo "$USAGE" exit fi compat-drivers-2012-09-18/scripts/check_depmod0000755000175000017500000000465212026176477020434 0ustar mcgrofmcgrof#!/bin/bash # Copyright 2009 Luis R. Rodriguez # # Ensures your distribution likes to prefer updates/ over the kernel/ # search updates built-in # Seems Mandriva has an $DEPMOD_DIR but it doesn't have any files, # so lets deal with those distributions. DEPMOD_CONF="/etc/depmod.conf" DEPMOD_CONF_TMP="$DEPMOD_CONF.compat-drivers.old" DEPMOD_DIR="/etc/depmod.d/" COMPAT_DEPMOD_FILE=compat-drivers.conf GREP_REGEX_UPDATES="^[[:space:]]*search.*[[:space:]]updates\([[:space:]]\|$\)" GREP_REGEX_SEARCH="^[[:space:]]*search[[:space:]].\+$" DEPMOD_CMD="depmod" function add_compat_depmod_conf { echo "NOTE: Your distribution lacks an $DEPMOD_DIR directory with " echo "updates/ directory being prioritized for modules, we're adding " echo "one for you." mkdir -p $DEPMOD_DIR FIRST_FILE=$(ls $DEPMOD_DIR|head -1) [ -n "$FIRST_FILE" ] && while [[ $FIRST_FILE < $COMPAT_DEPMOD_FILE ]]; do COMPAT_DEPMOD_FILE="0$COMPAT_DEPMOD_FILE" done echo "search updates" > $DEPMOD_DIR/$COMPAT_DEPMOD_FILE } function add_global_depmod_conf { echo "NOTE: Your distribution lacks updates/ directory being" echo "prioritized for modules, we're adding it to $DEPMOD_CONF." rm -f $DEPMOD_CONF_TMP [ -f $DEPMOD_CONF ] && cp -f $DEPMOD_CONF $DEPMOD_CONF_TMP echo "search updates" > $DEPMOD_CONF [ -f $DEPMOD_CONF_TMP ] && cat $DEPMOD_CONF_TMP >> $DEPMOD_CONF } function depmod_updates_ok { echo "depmod will prefer updates/ over kernel/ -- OK!" } function add_depmod_conf { if [ -f "$DEPMOD_CONF" ]; then add_global_depmod_conf else DEPMOD_VERSION=$($DEPMOD_CMD --version | cut -d" " -f2 | sed "s/\.//") if [[ $DEPMOD_VERSION -gt 36 ]]; then add_compat_depmod_conf else add_global_depmod_conf fi fi } # ============================================================================= # === MAIN ==================================================================== # ============================================================================= GREP_FILES="" [ -f $DEPMOD_CONF ] && GREP_FILES="$DEPMOD_CONF" if [ -d $DEPMOD_DIR ]; then DEPMOD_FILE_COUNT=$(ls $DEPMOD_DIR | wc -l) [[ $DEPMOD_FILE_COUNT -gt 0 ]] && GREP_FILES="$GREP_FILES $DEPMOD_DIR/*" fi if [ -n "$GREP_FILES" ]; then grep -q "$GREP_REGEX_SEARCH" $GREP_FILES if [[ $? -eq 0 ]]; then grep -q "$GREP_REGEX_UPDATES" $GREP_FILES if [[ $? -eq 0 ]]; then depmod_updates_ok else add_depmod_conf fi else depmod_updates_ok fi else depmod_updates_ok fi exit 0 compat-drivers-2012-09-18/scripts/admin-refresh.sh0000755000175000017500000000011512025712017021133 0ustar mcgrofmcgrof#!/usr/bin/env bash ./scripts/admin-clean.sh $@ ./scripts/admin-update.sh $@ compat-drivers-2012-09-18/scripts/athenable0000755000175000017500000000171712026176477017751 0ustar mcgrofmcgrof#!/bin/bash # # Copyright 2007 Luis R. Rodriguez # # Makes sure either ath5k or MadWifi are ready to be used. This allows # us to choose any driver without blacklisting each other. . /usr/lib/compat-drivers/modlib.sh if [[ $UID -ne 0 ]]; then echo "Run with root privileges" exit fi ATH5K="ath5k" ATH9K="ath9k" MADWIFI="ath_pci" # Appended to module file at the end when we want to ignore one IGNORE_SUFFIX=".ignore" USAGE="Usage: $0 [ ath5k | madwifi ]" # Default behavior: disables any MadWifi driver present and makes sure # ath5k is enabled if [ $# -eq 0 ]; then module_disable $MADWIFI module_enable $ATH5K module_enable $ATH9K exit elif [ $# -ne 1 ]; then echo "$USAGE" exit fi MODULE=$1 if [ "$MODULE" == "ath5k" ]; then module_disable $MADWIFI module_enable $ATH5K module_enable $ATH9K elif [ "$MODULE" == "madwifi" ]; then module_disable $ATH5K module_disable $ATH9K module_enable $MADWIFI else echo "$USAGE" exit fi compat-drivers-2012-09-18/scripts/btunload.sh0000755000175000017500000000067212025712017020227 0ustar mcgrofmcgrof#!/bin/bash MODULES="$MODULES ath3k bcm203x bluecard_cs bnep bpa10x bt3c_cs btmrvl btmrvl_sdio btsdio" MODULES="$MODULES btusb btuart_cs cmtp dtl1_cs hidp hci_vhci hci_uart rfcomm sco bluetooth l2cap" echo Stoping bluetooth service.. /etc/init.d/bluetooth stop /etc/init.d/bluetooth status for i in $MODULES; do grep ^$i /proc/modules 2>&1 > /dev/null if [ $? -eq 0 ]; then echo Unloading $i... modprobe -r --ignore-remove $i fi done compat-drivers-2012-09-18/scripts/b43enable0000755000175000017500000000220112026176477017552 0ustar mcgrofmcgrof#!/bin/bash # # Copyright 2007 Luis R. Rodriguez # # Makes sure either b43, b43legacy (new mac80211 drivers) or bcm43xx # is enabled to be used. This allows us to choose any driver without # blacklisting each other. . /usr/lib/compat-drivers/modlib.sh if [[ $UID -ne 0 ]]; then echo "Run with root privileges" exit fi B43S="b43 b43legacy" B43_OLD="bcm43xx" B43_PROP="wl" # Appended to module file at the end when we want to ignore one USAGE="Usage: $0 [ b43 | bcm43xx | wl ]" function enable_b43 { module_disable $B43_OLD module_disable $B43_PROP for i in $B43S; do module_enable $i done } # Default behavior: disables the old bcm43xx driver and enables b43 # and b43legacy if [ $# -eq 0 ]; then enable_b43 exit elif [ $# -ne 1 ]; then echo "$USAGE" exit fi MODULE=$1 if [ "$MODULE" == "bcm43xx" ]; then for i in $B43S; do module_disable $i done module_disable $B43_PROP module_enable $B43_OLD elif [ "$MODULE" == "wl" ]; then for i in $B43S; do module_disable $i done module_disable $B43_OLD module_enable $B43_PROP elif [ "$MODULE" == "b43" ]; then enable_b43 else echo "$USAGE" exit fi compat-drivers-2012-09-18/scripts/wlunload.sh0000755000175000017500000000440012025712017020235 0ustar mcgrofmcgrof#!/bin/bash # The old stack drivers and the mac80211 rc80211_simple modules # which is no longer on recent kernels (its internal) OLD_MODULES="iwlwifi_mac80211 rc80211_simple zd1211rw-mac80211" OLD_MODULES="$OLD_MODULES ieee80211_crypt_tkip ieee80211_crypt_ccmp" OLD_MODULES="$OLD_MODULES ieee80211softmac ieee80211_crypt ieee80211" OLD_MODULES="$OLD_MODULES bcm43xx rndis_wext iwl4965" MODULES="$OLD_MODULES" MODULES="$MODULES ipw2100 ipw2200 libipw" MODULES="$MODULES wl1251 wl12xx" MODULES="$MODULES libertas_cs usb8xxx libertas libertas_sdio libertas_spi" MODULES="$MODULES libertas_tf libertas_tf_usb" MODULES="$MODULES adm8211 zd1211rw" MODULES="$MODULES orinoco_cs orinoco_nortel orinoco_pci orinoco_plx" MODULES="$MODULES orinoco_tld orinoco_usb spectrum_cs orinoco" MODULES="$MODULES b43 b44 b43legacy brcm80211 ssb" MODULES="$MODULES iwl3945 iwlwifi iwlagn iwlcore" MODULES="$MODULES ath9k ath9k_htc ath9k_common ath9k_hw " MODULES="$MODULES ath5k ath ath6kl ar9170usb carl9170" MODULES="$MODULES p54pci p54usb p54spi p54common" MODULES="$MODULES rt2400pci rt2500pci rt61pci" MODULES="$MODULES rt2500usb rt73usb" MODULES="$MODULES rt2800usb rt2800lib" MODULES="$MODULES rt2x00usb rt2x00lib" MODULES="$MODULES rtl8180 rtl8187 rtl8192ce rtlwifi" MODULES="$MODULES mwl8k mac80211_hwsim" MODULES="$MODULES at76c50x_usb at76_usb" MODULES="$MODULES rndis_wlan rndis_host cdc_ether usbnet" # eeprom_93cx6 is used by rt2x00 (rt61pci, rt2500pci, rt2400pci) # and Realtek drivers ( rtl8187, rtl8180) MODULES="$MODULES eeprom_93cx6" MODULES="$MODULES lib80211_crypt_ccmp lib80211_crypt_tkip lib80211_crypt_wep" MODULES="$MODULES mac80211 cfg80211 lib80211" MADWIFI_MODULES="ath_pci ath_rate_sample wlan_scan_sta wlan ath_hal" IPW3945D="/sbin/ipw3945d-`uname -r`" if [ -f $IPW3945D ]; then $IPW3945D --isrunning if [ ! $? ]; then echo -n "Detected ipw3945 daemon loaded we're going to " echo "shut the daemon down now and remove the module." modprobe -r --ignore-remove ipw3945 fi fi grep ath_pci /proc/modules 2>&1 > /dev/null if [ $? -eq 0 ]; then echo "MadWifi driver is loaded, going to try to unload it..." ./scripts/madwifi-unload fi for i in $MODULES; do grep ^$i /proc/modules 2>&1 > /dev/null if [ $? -eq 0 ]; then echo Unloading $i... modprobe -r --ignore-remove $i fi done compat-drivers-2012-09-18/scripts/skip-colors0000755000175000017500000000007512025712017020250 0ustar mcgrofmcgrof#!/usr/bin/env bash perl -pe 's|(\e)\[(\d+)(;*)(\d*)(\w)||g' compat-drivers-2012-09-18/scripts/b43load0000755000175000017500000000303012026176477017244 0ustar mcgrofmcgrof#!/bin/bash # Copyright 2007 Luis R. Rodriguez # # Loads new broadcom drivers (b43 and b43legacy) or the old ones (bcm43xx) . /usr/lib/compat-drivers/modlib.sh if [[ $UID -ne 0 ]]; then echo "Run with root privileges" exit fi USAGE="Usage: $0 [ b43 | bcm43xx ]" # Default behavior: unload bcm43xx and load b43 and b43legacy if [ $# -eq 0 ]; then 1=b43 elif [ $# -ne 1 ]; then echo "$USAGE" exit fi MODULE=$1 if [ "$MODULE" == "b43" ]; then grep bcm43xx /proc/modules 2>&1 > /dev/null if [ $? -eq 0 ]; then echo Unloading $i... modprobe -r --ignore-remove bcm43xx fi # Enables both b43 and b43legacy b43enable b43 modprobe b43 modprobe b43legacy CHECK=`modprobe -l b43` if [ ! -z $CHECK ]; then echo "b43 loaded successfully" fi CHECK=`modprobe -l b43legacy` if [ ! -z $CHECK ]; then echo "b43legacy loaded successfully" fi elif [ "$MODULE" == "bcm43xx" ]; then CHECK=`modprobe -l b43` if [ ! -z $CHECK ]; then echo "b43 currently loaded, going to try to unload the module..." modprobe -r --ignore-remove b43 fi CHECK=`modprobe -l b43legacy` if [ ! -z $CHECK ]; then echo "b43legacy currently loaded, going to try to unload the module..." modprobe -r --ignore-remove b43legacy fi b43enable bcm43xx # bcm43xx may be loaded already lets remove them first modprobe -r --ignore-remov bcm43xx 2>&1 > /dev/null modprobe bcm43xx CHECK=`modprobe -l bcm43xx` if [ ! -z $CHECK ]; then echo "bcm43xx loaded successfully!" fi else echo "$USAGE" exit fi compat-drivers-2012-09-18/scripts/gen-compat-autoconf.sh0000755000175000017500000001454612026176477022311 0ustar mcgrofmcgrof#!/usr/bin/env bash # # Copyright 2007 Luis R. Rodriguez # # Use this to parse a small .config equivalent looking file to generate # our own autoconf.h. This file has defines for each config option # just like the kernels include/linux/autoconf.h # # XXX: consider using scripts/kconfig/confdata.c instead. # On the downside this would require the user to have libc though. # This indicates which is the oldest kernel we support # Update this if you are adding support for older kernels. OLDEST_KERNEL_SUPPORTED="2.6.24" COMPAT_RELEASE=".compat_version" KERNEL_RELEASE=".compat_base_tree_version" MULT_DEP_FILE=".compat_pivot_dep" if [ $# -ne 2 ]; then echo "Usage $0 " exit fi COMPAT_CONFIG_1="$1" COMPAT_CONFIG_2="$2" if [[ ! -f $COMPAT_CONFIG_1 || ! -f $COMPAT_CONFIG_2 ]]; then echo "File $COMPAT_CONFIG_1 and $COMPAT_CONFIG_2 files must be present" exit fi if [ ! -f $COMPAT_RELEASE -o ! -f $KERNEL_RELEASE ]; then echo "Error: $COMPAT_RELEASE or $KERNEL_RELEASE file is missing" exit fi CREL=$(cat $COMPAT_RELEASE | tail -1) KREL=$(cat $KERNEL_RELEASE | tail -1) DATE=$(date) # Defines a CONFIG_ option if not defined yet, this helps respect # linux/autoconf.h function define_config { VAR=$1 VALUE=$2 case $VALUE in n) # Try to undefine it echo "#undef $VAR" ;; y) echo "#ifndef $VAR" echo "#define $VAR 1" echo "#endif /* $VAR */ " ;; m) echo "#ifndef $VAR" echo "#define $VAR 1" echo "#endif /* $VAR */ " ;; *) # Assume string # XXX: add better checks to make sure what was on # the right was indeed a string echo "#ifndef $VAR" echo "#define $VAR \"$VALUE\"" echo "#endif /* $VAR */ " ;; esac } # This deals with core compat-drivers kernel requirements. function define_config_req { VAR=$1 echo "#ifndef $VAR" echo -n "#error Compat-drivers requirement: $VAR must be enabled " echo "in your kernel" echo "#endif /* $VAR */" } # This handles modules which have dependencies from the kernel # which compat-drivers isn't providing yet either because # the dependency is not available as kernel module or # the module simply isn't provided by compat-drivers. function define_config_dep { VAR=$1 VALUE=$2 DEP=$3 WARN_VAR="COMPAT_WARN_$VAR" echo "#ifdef $DEP" define_config $VAR $VALUE echo "#else" # XXX: figure out a way to warn only once # define only once in case user tried to enable config option # twice in config.mk echo "#ifndef $WARN_VAR" # Lets skip these for now.. they might be too annoying #echo "#warning Skipping $VAR as $DEP was needed... " #echo "#warning This just means $VAR won't be built and is not fatal." echo "#define $WARN_VAR" echo "#endif /* $VAR */" echo "#endif /* $WARN_VAR */" } # This handles options which have *multiple* dependencies from the kernel function define_config_multiple_deps { VAR=$1 VALUE=$2 DEP_ARRAY=$3 # First, put all ifdefs for i in $(cat $MULT_DEP_FILE); do echo "#ifdef $i" done # Now put our option in the middle define_config $VAR $VALUE # Now close all ifdefs # First, put all ifdefs for i in $(cat $MULT_DEP_FILE); do echo "#endif" done } function kernel_version_req { VERSION=$(echo $1 | sed -e 's/\./,/g') echo "#if (LINUX_VERSION_CODE < KERNEL_VERSION($VERSION))" echo "#error Compat-wireless requirement: Linux >= $VERSION" echo "#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION($VERSION) */ " } cat <= \3)/' -e 's/\(#ifdef \)\(CONFIG_[^:space:]*\)/#if defined(\2) || defined(\2_MODULE)/' continue ;; 'ifndef+CONFIG_'* ) echo "#$i" | sed -e 's/+/ /' -e 's/\(ifndef CONFIG_COMPAT_KERNEL_3_\)\([0-9]*\)/if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,\2,0))/' -e 's/\(ifndef CONFIG_COMPAT_KERNEL_2_6_\)\([0-9]*\)/if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,\2))/' -e 's/\(ifndef CONFIG_COMPAT_RHEL_\)\([0-9]*\)_\([0-9]*\)/if (!defined(RHEL_MAJOR) || RHEL_MAJOR != \2 || RHEL_MINOR < \3)/' -e 's/\(#ifndef \)\(CONFIG_[^:space:]*\)/#if !defined(\2) \&\& !defined(\2_MODULE)/' continue ;; 'else+#CONFIG_'* | 'endif+#CONFIG_'* ) echo "#$i */" |sed -e 's/+#/ \/* /g' continue ;; CONFIG_* ) # Get the element on the left of the "=" VAR=$(echo $i | cut -d"=" -f 1) # Get the element on the right of the "=" VALUE=$(echo $i | cut -d"=" -f 2) # Handle core kernel module depenencies here. case $VAR in # ignore this, we have a special hanlder for this at the botttom # instead. We still need to keep this in config.mk to let Makefiles # know its enabled so just ignore it here. CONFIG_MAC80211_QOS) continue ;; esac # Any other module which can *definitely* be built as a module goes here define_config $VAR $VALUE continue ;; esac done # Deal with special cases. CONFIG_MAC80211_QOS is such a case. # We handle this specially for different kernels we support. if [ -f $KLIB_BUILD/Makefile ]; then MAJORLEVEL=$(make -C $KLIB_BUILD kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p') SUBLEVEL=$(make -C $KLIB_BUILD kernelversion | sed -n 's/^\(2\.6\|[3-9]\)\.\([0-9]\+\).*/\2/p') if [ $MAJORLEVEL -eq 2 -a $SUBLEVEL -le 22 ]; then define_config CONFIG_MAC80211_QOS y else # kernel >= 2.6.23 # CONFIG_MAC80211_QOS on these kernels requires # CONFIG_NET_SCHED and CONFIG_NETDEVICES_MULTIQUEUE rm -f $MULT_DEP_FILE echo CONFIG_NET_SCHED >> $MULT_DEP_FILE echo CONFIG_NETDEVICES_MULTIQUEUE >> $MULT_DEP_FILE define_config_multiple_deps CONFIG_MAC80211_QOS y $ALL_DEPS rm -f $MULT_DEP_FILE fi fi echo "#endif /* COMPAT_AUTOCONF_INCLUDED */" compat-drivers-2012-09-18/scripts/refresh-compat0000755000175000017500000000601412026176477020740 0ustar mcgrofmcgrof#!/bin/bash # # Copyright 2012 Luis R. Rodriguez # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # The purpose of this script is to enable developers use compat-drivers # backport work without having to carry code on their tree. This lets # developers synch to newer compat-drivers releases to gain more backport # work. If you use this throw the files into your .gitignore: # # compat/ # udev/ # include/ # scripts/ # .compat_base_tree # .compat_base_tree_version # .compat_version # config.mk # # An example git tree for standalone development will be provided later. # Once run you can then just run on your standalone development tree: # # ./refresh-compat # # This is a copy of refresh-compat-local that we maintain here # You can keep that in your git tree as otherwise its hard for users to # know what to run. # Pretty colors GREEN="\033[01;32m" YELLOW="\033[01;33m" NORMAL="\033[00m" BLUE="\033[34m" RED="\033[31m" PURPLE="\033[35m" CYAN="\033[36m" UNDERLINE="\033[02m" COMPAT_SRC="$HOME/devel/compat-drivers/" EXTRA_COPY="" if [[ -f .compat_copy ]]; then source .compat_copy fi COPY="" COPY="$COPY compat/" COPY="$COPY include/" COPY="$COPY udev/" COPY="$COPY scripts/check_config.sh" COPY="$COPY scripts/check_depmod" COPY="$COPY scripts/compress_modules" COPY="$COPY scripts/gen-compat-autoconf.sh" COPY="$COPY scripts/modlib.sh" COPY="$COPY scripts/update-initramfs" COPY="$COPY scripts/skip-colors" COPY="$COPY .compat_base" COPY="$COPY .compat_base_tree" COPY="$COPY .compat_base_tree_version" COPY="$COPY .compat_version" COPY="$COPY config.mk" COPY="$COPY $EXTRA_COPY" REFRESH_LOCAL="scripts/refresh-compat-local" REFRESH_TARGET="refresh-compat" for i in $COPY ; do if [[ -d ${COMPAT_SRC}${i} ]]; then echo -e "Copying ${BLUE}${i}${NORMAL} ..." cp -a ${COMPAT_SRC}/$i . continue fi if [[ -f ${COMPAT_SRC}${i} ]]; then DIR=$(dirname $i) if [[ ! -d $DIR ]]; then mkdir $DIR fi if [[ -x ${COMPAT_SRC}${i} ]]; then echo -e "Copying ${GREEN}${i}${NORMAL} ..." else echo -e "Copying ${i}${NORMAL} ..." fi cp -f ${COMPAT_SRC}/${i} $i continue fi done rm -f include/linux/compat_autoconfig.h rm -f .config cp -f ${COMPAT_SRC}/${REFRESH_LOCAL} ${REFRESH_TARGET} if [[ ! -f .compat_base_tree_version ]]; then echo -e "Failed at copying files over..." exit 1 fi echo echo -e "Synched with ${CYAN}$(cat .compat_version)${NORMAL}" compat-drivers-2012-09-18/scripts/iwl-load0000755000175000017500000000242312026176477017531 0ustar mcgrofmcgrof#!/bin/bash # Copyright 2008 Luis R. Rodriguez # # Loads new Intel iwl (iwlagn) or the old ones (iwl4965) . /usr/lib/compat-drivers/modlib.sh IWL_OLD="iwl4965" IWL_NEW="iwlagn" if [[ $UID -ne 0 ]]; then echo "Run with root privileges" exit fi USAGE="Usage: $0 [ iwlagn | iwl4965 ]" # Default behavior: unload iwl4965 and load iwlagn if [ $# -eq 0 ]; then 1=iwlagn elif [ $# -ne 1 ]; then echo "$USAGE" exit fi MODULE=$1 if [ "$MODULE" == "iwlagn" ]; then grep iwl4965 /proc/modules 2>&1 > /dev/null if [ $? -eq 0 ]; then echo Unloading $i... modprobe -r --ignore-remove iwl4965 fi # Enables both b43 and b43legacy iwl-enable iwlagn modprobe iwlagn CHECK=`modprobe -l iwlagn` if [ ! -z $CHECK ]; then echo "iwlagn loaded successfully" fi elif [ "$MODULE" == "iwl4965" ]; then CHECK=`modprobe -l iwlagn` if [ ! -z $CHECK ]; then echo "iwlagn currently loaded, going to try to unload the module..." modprobe -r --ignore-remove iwlagn fi iwl-enable iwl4965 # iwl4965 may be loaded already lets remove it first modprobe -r --ignore-remov iwl4965 2>&1 > /dev/null modprobe iwl4965 CHECK=`modprobe -l iwl4965` if [ ! -z $CHECK ]; then echo "iwl4965 loaded successfully!" fi else echo "$USAGE" exit fi compat-drivers-2012-09-18/scripts/admin-update.sh0000755000175000017500000005311012026176477021001 0ustar mcgrofmcgrof#!/usr/bin/env bash # # Copyright 2007, 2008, 2010 Luis R. Rodriguez # # Use this to update compat-drivers to the latest # linux-next.git tree you have. # # Usage: you should have the latest pull of linux-next.git # git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git # We assume you have it on your ~/linux-next/ directory. If you do, # just run this script from the compat-drivers directory. # You can specify where your GIT_TREE is by doing: # # export GIT_TREE=/home/mcgrof/linux-next/ # Pretty colors GREEN="\033[01;32m" YELLOW="\033[01;33m" NORMAL="\033[00m" BLUE="\033[34m" RED="\033[31m" PURPLE="\033[35m" CYAN="\033[36m" UNDERLINE="\033[02m" # File in which code metrics will be written CODE_METRICS=code-metrics.txt # The GIT URL's for linux-next and compat trees GIT_URL="git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git" GIT_COMPAT_URL="git://github.com/mcgrof/compat.git" #################### # Helper functions # # ################## # Refresh patches using quilt patchRefresh() { if [ -d patches.orig ] ; then rm -rf .pc patches/series else mkdir patches.orig fi export QUILT_PATCHES=$1 mv -u $1/* patches.orig/ for i in patches.orig/*.patch; do if [ ! -f "$i" ]; then echo -e "${RED}No patches found in $1${NORMAL}" break; fi echo -e "${GREEN}Refresh backport patch${NORMAL}: ${BLUE}$i${NORMAL}" quilt import $i quilt push -f RET=$? if [[ $RET -ne 0 ]]; then echo -e "${RED}Refreshing $i failed${NORMAL}, update it" echo -e "use ${CYAN}quilt edit [filename]${NORMAL} to apply the failed part manually" echo -e "use ${CYAN}quilt refresh${NORMAL} after the files are corrected and rerun this script" cp patches.orig/README $1/README exit $RET fi QUILT_DIFF_OPTS="-p" quilt refresh -p ab --no-index --no-timestamp done quilt pop -a cp patches.orig/README $1/README rm -rf patches.orig .pc $1/series } ### # usage() function ### usage() { printf "Usage: $0 [refresh] [ --help | -h | -s | -n | -p | -c ] [subsystems] where subsystems can be network, drm or both. Network is enabled by default.\n\n" printf "${GREEN}%10s${NORMAL} - Update all your patch offsets using quilt\n" "refresh" printf "${GREEN}%10s${NORMAL} - Get and apply pending-stable/ fixes purging old files there\n" "-s" printf "${GREEN}%10s${NORMAL} - Apply the patches from linux-next-cherry-picks directory\n" "-n" printf "${GREEN}%10s${NORMAL} - Apply the patches from linux-next-pending directory\n" "-p" printf "${GREEN}%10s${NORMAL} - Apply the patches from crap directory\n" "-c" } ### # Code metrics related functions # 4 parameters get passed to them: # (ORIG_CODE, CHANGES, ADD, DEL) ### brag_backport() { COMPAT_FILES_CODE=$(find ./ -type f -name \*.[ch] | egrep "^./compat/|include/linux/compat" | xargs wc -l | tail -1 | awk '{print $1}') let COMPAT_ALL_CHANGES=$2+$COMPAT_FILES_CODE printf "${GREEN}%10s${NORMAL} - backport code changes\n" $2 printf "${GREEN}%10s${NORMAL} - backport code additions\n" $3 printf "${GREEN}%10s${NORMAL} - backport code deletions\n" $4 printf "${GREEN}%10s${NORMAL} - backport from compat module\n" $COMPAT_FILES_CODE printf "${GREEN}%10s${NORMAL} - total backport code\n" $COMPAT_ALL_CHANGES printf "${RED}%10s${NORMAL} - %% of code consists of backport work\n" \ $(perl -e 'printf("%.4f", 100 * '$COMPAT_ALL_CHANGES' / '$1');') } nag_pending_stable() { printf "${YELLOW}%10s${NORMAL} - Code changes brought in from pending-stable\n" $2 printf "${YELLOW}%10s${NORMAL} - Code additions brought in from pending-stable\n" $3 printf "${YELLOW}%10s${NORMAL} - Code deletions brought in from pending-stable\n" $4 printf "${RED}%10s${NORMAL} - %% of code being cherry picked from pending-stable\n" $(perl -e 'printf("%.4f", 100 * '$2' / '$1');') } nag_next_cherry_pick() { printf "${YELLOW}%10s${NORMAL} - Code changes brought in from linux-next\n" $2 printf "${YELLOW}%10s${NORMAL} - Code additions brought in from linux-next\n" $3 printf "${YELLOW}%10s${NORMAL} - Code deletions brought in from linux-next\n" $4 printf "${RED}%10s${NORMAL} - %% of code being cherry picked from linux-next\n" $(perl -e 'printf("%.4f", 100 * '$2' / '$1');') } nag_pending() { printf "${YELLOW}%10s${NORMAL} - Code changes posted but not yet merged\n" $2 printf "${YELLOW}%10s${NORMAL} - Code additions posted but not yet merged\n" $3 printf "${YELLOW}%10s${NORMAL} - Code deletions posted but not yet merged\n" $4 printf "${RED}%10s${NORMAL} - %% of code not yet merged\n" $(perl -e 'printf("%.4f", 100 * '$2' / '$1');') } nag_crap() { printf "${RED}%10s${NORMAL} - Crap changes not yet posted\n" $2 printf "${RED}%10s${NORMAL} - Crap additions not yet posted\n" $3 printf "${RED}%10s${NORMAL} - Crap deletions not yet posted\n" $4 printf "${RED}%10s${NORMAL} - %% of crap code\n" $(perl -e 'printf("%.4f", 100 * '$2' / '$1');') } nagometer() { CHANGES=0 ORIG_CODE=$2 ADD=$(grep -Hc ^+ $1/*.patch| awk -F":" 'BEGIN {sum=0} {sum += $2} END { print sum}') DEL=$(grep -Hc ^- $1/*.patch| awk -F":" 'BEGIN {sum=0} {sum += $2} END { print sum}') # Total code is irrelevant unless you take into account each part, # easier to just compare against the original code. # let TOTAL_CODE=$ORIG_CODE+$ADD-$DEL let CHANGES=$ADD+$DEL case `dirname $1` in "patches") brag_backport $ORIG_CODE $CHANGES $ADD $DEL ;; "pending-stable") nag_pending_stable $ORIG_CODE $CHANGES $ADD $DEL ;; "linux-next-cherry-picks") nag_next_cherry_pick $ORIG_CODE $CHANGES $ADD $DEL ;; "linux-next-pending") nag_pending $ORIG_CODE $CHANGES $ADD $DEL ;; "crap") nag_crap $ORIG_CODE $CHANGES $ADD $DEL ;; *) ;; esac } # Copy each file in $1 into $2 copyFiles() { FILES=$1 TARGET=$2 for file in $FILES; do echo "Copying $GIT_TREE/$TARGET/$file" cp "$GIT_TREE/$TARGET/$file" $TARGET/ done } copyDirectories() { DIRS=$1 for dir in $DIRS; do echo "Copying $GIT_TREE/$dir/*.[ch]" cp $GIT_TREE/$dir/{Kconfig,Makefile,*.[ch]} $dir/ &> /dev/null done } # First check cmdline args to understand # which patches to apply and which release tag to set. # # Release tags (with corresponding cmdline switches): # --------------------------------------------------- # s: Include pending-stable/ patches (-s) # n: Include linux-next-cherry-picks/ patches (-n) # p: Include linux-next-pending/ patches (-p) # c: Include crap/ patches (-c) # Note that the patches under patches/{subsystem} are applied by default. # # If "refresh" is given as a cmdline argument, the script # uses quilt to refresh the patches. This is useful if patches # can not be applied correctly after a code update in $GIT_URL. # # A final parameter drm, wlan or both determines which subsystem # drivers will be fetched in from the GIT repository. To retain # compatibility with compat-wireless, wlan/bt/eth drivers are # fetched in by default. ENABLE_NETWORK=1 ENABLE_DRM=0 SUBSYSTEMS= EXTRA_PATCHES="patches" REFRESH="n" GET_STABLE_PENDING="n" POSTFIX_RELEASE_TAG="" if [ $# -ge 1 ]; then if [ $# -gt 6 ]; then usage $0 exit fi while [ $# -ne 0 ]; do case $1 in "-s") GET_STABLE_PENDING="y" EXTRA_PATCHES="${EXTRA_PATCHES} pending-stable" EXTRA_PATCHES="${EXTRA_PATCHES} pending-stable/backports/" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}s" shift ;; "-n") EXTRA_PATCHES="${EXTRA_PATCHES} linux-next-cherry-picks" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}n" shift ;; "-p") EXTRA_PATCHES="${EXTRA_PATCHES} linux-next-pending" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}p" shift ;; "-c") EXTRA_PATCHES="${EXTRA_PATCHES} crap" POSTFIX_RELEASE_TAG="${POSTFIX_RELEASE_TAG}c" shift ;; "refresh") REFRESH="y" shift ;; "network") ENABLE_NETWORK=1 shift ;; "drm") ENABLE_DRM=1 shift ;; "-h" | "--help") usage $0 exit ;; *) echo "Unexpected argument passed: $1" usage $0 exit ;; esac done fi # SUBSYSTEMS is used to select which patches to apply if [[ "$ENABLE_NETWORK" == "1" ]]; then SUBSYSTEMS="network" fi if [[ "$ENABLE_DRM" == "1" ]]; then SUBSYSTEMS+=" drm" fi # User exported this variable if [ -z $GIT_TREE ]; then GIT_TREE="$HOME/linux-next" if [ ! -d $GIT_TREE ]; then echo "Please tell me where your linux-next git tree is." echo "You can do this by exporting its location as follows:" echo echo " export GIT_TREE=$HOME/linux-next" echo echo "If you do not have one you can clone the repository:" echo " git clone $GIT_URL" echo echo "Alternatively, you can use get-compat-trees script " echo "from compat.git tree to fetch the necessary trees." exit 1 fi else echo "You said to use git tree at: $GIT_TREE for linux-next" fi if [ -z $GIT_COMPAT_TREE ]; then GIT_COMPAT_TREE="$HOME/compat" if [ ! -d $GIT_COMPAT_TREE ]; then echo "Please tell me where your compat git tree is." echo "You can do this by exporting its location as follows:" echo echo " export GIT_COMPAT_TREE=$HOME/compat" echo echo "If you do not have one you can clone the repository:" echo " git clone $GIT_COMPAT_URL" echo echo "Alternatively, you can use get-compat-trees script " echo "from compat.git tree to fetch the necessary trees." exit 1 fi else echo "You said to use git tree at: $GIT_COMPAT_TREE for compat" fi # Now define what files to copy from $GIT_URL INCLUDE_NET_BT="hci_core.h l2cap.h bluetooth.h rfcomm.h hci.h hci_mon.h mgmt.h sco.h smp.h a2mp.h" # Required wlan headers from include/linux INCLUDE_LINUX_WLAN="ieee80211.h nl80211.h pci_ids.h eeprom_93cx6.h ath9k_platform.h wl12xx.h rndis.h" # For rndis_wext INCLUDE_LINUX_USB_WLAN="usbnet.h rndis_host.h" # For rndis_wlan, we need a new rndis_host.ko, cdc_ether.ko and usbnet.ko RNDIS_REQUIREMENTS="Makefile rndis_host.c cdc_ether.c usbnet.c" # For libertas driver INCLUDE_LINUX_LIBERTAS_WLAN="libertas_spi.h" # 802.11 related headers INCLUDE_NET="cfg80211.h cfg80211-wext.h ieee80211_radiotap.h lib80211.h mac80211.h regulatory.h" # Network related directories NET_WLAN_DIRS="net/wireless net/mac80211 net/rfkill" # Bluetooth related directories NET_BT_DIRS="net/bluetooth net/bluetooth/bnep net/bluetooth/cmtp net/bluetooth/rfcomm net/bluetooth/hidp" # Drivers that have their own directory DRIVERS_WLAN="drivers/net/wireless/ath drivers/net/wireless/ath/carl9170 drivers/net/wireless/ath/ath5k drivers/net/wireless/ath/ath6kl drivers/net/wireless/ath/ath9k drivers/ssb drivers/bcma drivers/net/wireless/b43 drivers/net/wireless/b43legacy drivers/net/wireless/brcm80211 drivers/net/wireless/brcm80211/brcmfmac drivers/net/wireless/brcm80211/brcmsmac drivers/net/wireless/brcm80211/brcmsmac/phy drivers/net/wireless/brcm80211/brcmutil drivers/net/wireless/brcm80211/include drivers/net/wireless/iwlegacy drivers/net/wireless/iwlwifi drivers/net/wireless/iwlwifi/pcie drivers/net/wireless/iwlwifi/dvm drivers/net/wireless/rt2x00 drivers/net/wireless/zd1211rw drivers/net/wireless/libertas drivers/net/wireless/p54 drivers/net/wireless/rtl818x drivers/net/wireless/rtl818x/rtl8180 drivers/net/wireless/rtl818x/rtl8187 drivers/net/wireless/rtlwifi drivers/net/wireless/rtlwifi/rtl8192c drivers/net/wireless/rtlwifi/rtl8192ce drivers/net/wireless/rtlwifi/rtl8192cu drivers/net/wireless/rtlwifi/rtl8192se drivers/net/wireless/rtlwifi/rtl8192de drivers/net/wireless/libertas_tf drivers/net/wireless/ipw2x00 drivers/net/wireless/ti drivers/net/wireless/ti/wl12xx drivers/net/wireless/ti/wl1251 drivers/net/wireless/ti/wlcore drivers/net/wireless/ti/wl18xx drivers/net/wireless/orinoco drivers/net/wireless/mwifiex" # Staging drivers STAGING_DRIVERS="" # Ethernet drivers having their own directory DRIVERS_ETH="drivers/net/ethernet/atheros drivers/net/ethernet/atheros/atl1c drivers/net/ethernet/atheros/atl1e drivers/net/ethernet/atheros/atlx drivers/net/ethernet/atheros/alx" # Bluetooth drivers DRIVERS_BT="drivers/bluetooth" # Drivers that belong the the wireless directory DRIVERS_WLAN_FILES="adm8211.c adm8211.h at76c50x-usb.c at76c50x-usb.h mac80211_hwsim.c mac80211_hwsim.h mwl8k.c rndis_wlan.c" # DRM drivers DRIVERS_DRM="drivers/gpu/drm/ast drivers/gpu/drm/cirrus drivers/gpu/drm/gma500 drivers/gpu/drm/i2c drivers/gpu/drm/i810 drivers/gpu/drm/i915 drivers/gpu/drm/mgag200 drivers/gpu/drm/nouveau drivers/gpu/drm/radeon drivers/gpu/drm/ttm drivers/gpu/drm/via drivers/gpu/drm/vmwgfx" # UDL uses the new dma-buf API, let's disable this for now #DRIVERS="$DRIVERS drivers/gpu/drm/udl" rm -rf drivers/ mkdir -p include/net/bluetooth \ include/linux/usb \ include/linux/unaligned \ include/linux/spi \ include/trace \ include/pcmcia \ include/crypto \ drivers/bcma \ drivers/misc/eeprom \ drivers/net/usb \ drivers/net/ethernet/broadcom \ drivers/platform/x86 \ drivers/ssb \ drivers/staging \ $NET_WLAN_DIRS \ $NET_BT_DIRS \ $DRIVERS_WLAN \ $DRIVERS_ETH \ $DRIVERS_BT \ $DRIVERS_DRM if [[ "$ENABLE_NETWORK" == "1" ]]; then # WLAN and bluetooth files copyFiles "$INCLUDE_LINUX_WLAN" "include/linux" copyFiles "$INCLUDE_NET" "include/net" copyFiles "$INCLUDE_NET_BT" "include/net/bluetooth" copyFiles "$INCLUDE_LINUX_USB_WLAN" "include/linux/usb" copyFiles "$INCLUDE_LINUX_LIBERTAS_WLAN" "include/linux/spi" copyFiles "$DRIVERS_WLAN_FILES" "drivers/net/wireless" copyFiles "$RNDIS_REQUIREMENTS" "drivers/net/usb" copyDirectories "$NET_WLAN_DIRS" copyDirectories "$NET_BT_DIRS" copyDirectories "$DRIVERS_BT" copyDirectories "$DRIVERS_WLAN" copyDirectories "$DRIVERS_ETH" cp -a $GIT_TREE/include/linux/ssb include/linux/ cp -a $GIT_TREE/include/linux/bcma include/linux/ cp -a $GIT_TREE/include/linux/rfkill.h include/linux/rfkill_backport.h # Misc cp $GIT_TREE/drivers/misc/eeprom/{Makefile,eeprom_93cx6.c} drivers/misc/eeprom/ # Copy files needed for statically compiled regulatory rules database cp $GIT_TREE/net/wireless/{db.txt,genregdb.awk} net/wireless/ # Top level wireless driver Makefile cp $GIT_TREE/drivers/net/wireless/Makefile drivers/net/wireless # Broadcom case DIR="drivers/net/ethernet/broadcom" cp $GIT_TREE/$DIR/b44.[ch] drivers/net/ethernet/broadcom # Not yet echo "obj-\$(CONFIG_B44) += b44.o" > drivers/net/ethernet/broadcom/Makefile fi if [[ "$ENABLE_DRM" == "1" ]]; then # DRM drivers copyDirectories "$DRIVERS_DRM" # Copy standalone drivers echo "Copying $GIT_TREE/drivers/gpu/drm/*.[ch]" cp $GIT_TREE/drivers/gpu/drm/{Makefile,*.[ch]} drivers/gpu/drm/ # Copy DRM headers cp -a $GIT_TREE/include/drm include/ # drivers/gpu/drm/i915/intel_pm.c requires this cp $GIT_TREE/drivers/platform/x86/intel_ips.h drivers/platform/x86 # Copy radeon reg_srcs for hostprogs cp -a $GIT_TREE/drivers/gpu/drm/radeon/reg_srcs drivers/gpu/drm/radeon # Finally get the DRM top-level makefile cp $GIT_TREE/drivers/gpu/drm/Makefile drivers/gpu/drm fi # Staging drivers in their own directory for i in $STAGING_DRIVERS; do if [ ! -d $GIT_TREE/$i ]; then continue fi rm -rf $i echo -e "Copying ${RED}STAGING${NORMAL} $GIT_TREE/$i/*.[ch]" # staging drivers tend to have their own subdirs... cp -a $GIT_TREE/$i drivers/staging/ done # Finally copy MAINTAINERS file cp $GIT_TREE/MAINTAINERS ./ # Compat stuff COMPAT="compat" mkdir -p $COMPAT echo "Copying $GIT_COMPAT_TREE/ files..." cp $GIT_COMPAT_TREE/compat/*.[ch] $COMPAT/ cp $GIT_COMPAT_TREE/compat/Makefile $COMPAT/ cp -a $GIT_COMPAT_TREE/udev . cp -a $GIT_COMPAT_TREE/scripts $COMPAT/ cp $GIT_COMPAT_TREE/bin/ckmake $COMPAT/ cp -a $GIT_COMPAT_TREE/include/linux/* include/linux/ cp -a $GIT_COMPAT_TREE/include/net/* include/net/ cp -a $GIT_COMPAT_TREE/include/trace/* include/trace/ cp -a $GIT_COMPAT_TREE/include/pcmcia/* include/pcmcia/ cp -a $GIT_COMPAT_TREE/include/crypto/* include/crypto/ # Clean up possible *.mod.c leftovers find -type f -name "*.mod.c" -exec rm -f {} \; # files we suck in for wireless drivers export WSTABLE=" net/wireless/ net/mac80211/ net/rfkill/ drivers/net/wireless/ net/bluetooth/ drivers/bluetooth/ drivers/net/ethernet/atheros/atl1c/ drivers/net/ethernet/atheros/atl1e/ drivers/net/ethernet/atheros/atlx/ include/linux/nl80211.h include/linux/rfkill.h include/net/mac80211.h include/net/regulatory.h include/net/cfg80211.h" # Stable pending, if -n was passed if [[ "$GET_STABLE_PENDING" = y ]]; then if [ -z $NEXT_TREE ]; then NEXT_TREE="/$HOME/linux-next" if [ ! -d $NEXT_TREE ]; then echo "Please tell me where your linux-next git tree is." echo "You can do this by exporting its location as follows:" echo echo " export NEXT_TREE=$HOME/linux-next" echo echo "If you do not have one you can clone the repository:" echo " git clone git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git" exit 1 fi else echo "You said to use git tree at: $NEXT_TREE for linux-next" fi LAST_DIR=$PWD cd $GIT_TREE if [ -f localversion* ]; then echo -e "You should be using a stable tree to use the -s option" exit 1 fi # we now assume you are using a stable tree cd $GIT_TREE LAST_STABLE_UPDATE=$(git describe --abbrev=0) cd $NEXT_TREE PENDING_STABLE_DIR="pending-stable/" rm -rf $PENDING_STABLE_DIR git tag -l | grep $LAST_STABLE_UPDATE 2>&1 > /dev/null if [[ $? -ne 0 ]]; then echo -e "${BLUE}Tag $LAST_STABLE_UPDATE not found on $NEXT_TREE tree: bailing out${NORMAL}" exit 1 fi echo -e "${GREEN}Generating stable cherry picks... ${NORMAL}" echo -e "\nUsing command on directory $PWD:" echo -e "\ngit format-patch --grep=\"stable@vger.kernel.org\" -o $PENDING_STABLE_DIR ${LAST_STABLE_UPDATE}.. $WSTABLE" git format-patch --grep="stable@vger.kernel.org" -o $PENDING_STABLE_DIR ${LAST_STABLE_UPDATE}.. $WSTABLE if [ ! -d ${LAST_DIR}/${PENDING_STABLE_DIR} ]; then echo -e "Assumption that ${LAST_DIR}/${PENDING_STABLE_DIR} directory exists failed" exit 1 fi echo -e "${GREEN}Purging old stable cherry picks... ${NORMAL}" rm -f ${LAST_DIR}/${PENDING_STABLE_DIR}/*.patch cp ${PENDING_STABLE_DIR}/*.patch ${LAST_DIR}/${PENDING_STABLE_DIR}/ if [ -f ${LAST_DIR}/${PENDING_STABLE_DIR}/.ignore ]; then for i in $(cat ${LAST_DIR}/${PENDING_STABLE_DIR}/.ignore) ; do echo -e "Skipping $i from generated stable patches..." rm -f ${LAST_DIR}/${PENDING_STABLE_DIR}/*$i* done fi echo -e "${GREEN}Updated stable cherry picks, review with git diff and update hunks with ./scripts/admin-update.sh -s refresh${NORMAL}" cd $LAST_DIR fi ORIG_CODE=$(find ./ -type f -name \*.[ch] | egrep -v "^./compat/|include/linux/compat" | xargs wc -l | tail -1 | awk '{print $1}') printf "\n${CYAN}compat-drivers code metrics${NORMAL}\n\n" > $CODE_METRICS printf "${PURPLE}%10s${NORMAL} - Total upstream lines of code being pulled\n" $ORIG_CODE >> $CODE_METRICS for subsystem in $SUBSYSTEMS; do for dir in $EXTRA_PATCHES; do LAST_ELEM=$dir done for dir in $EXTRA_PATCHES; do PATCHDIR="$dir/$subsystem" if [[ ! -d $PATCHDIR ]]; then echo -e "${RED}Patches: $PATCHDIR empty, skipping...${NORMAL}" continue fi if [[ $LAST_ELEM = $dir && "$REFRESH" = y ]]; then patchRefresh $PATCHDIR fi FOUND=$(find $PATCHDIR/ -maxdepth 1 -name \*.patch | wc -l) if [ $FOUND -eq 0 ]; then continue fi for i in $(ls -v $PATCHDIR/*.patch); do echo -e "${GREEN}Applying backport patch${NORMAL}: ${BLUE}$i${NORMAL}" patch -p1 -N -t < $i RET=$? if [[ $RET -ne 0 ]]; then echo -e "${RED}Patching $i failed${NORMAL}, update it" exit $RET fi done nagometer $PATCHDIR $ORIG_CODE >> $CODE_METRICS done done DIR="$PWD" cd $GIT_TREE GIT_DESCRIBE=$(git describe) GIT_BRANCH=$(git branch --no-color |sed -n 's/^\* //p') GIT_BRANCH=${GIT_BRANCH:-master} GIT_REMOTE=$(git config branch.${GIT_BRANCH}.remote) GIT_REMOTE=${GIT_REMOTE:-origin} GIT_REMOTE_URL=$(git config remote.${GIT_REMOTE}.url) GIT_REMOTE_URL=${GIT_REMOTE_URL:-unknown} cd $GIT_COMPAT_TREE git describe > $DIR/.compat_base cd $DIR echo -e "${GREEN}Updated${NORMAL} from local tree: ${BLUE}${GIT_TREE}${NORMAL}" echo -e "Origin remote URL: ${CYAN}${GIT_REMOTE_URL}${NORMAL}" cd $DIR if [ -d ./.git ]; then if [[ ${POSTFIX_RELEASE_TAG} != "" ]]; then echo -e "$(git describe)-${POSTFIX_RELEASE_TAG}" > .compat_version else echo -e "$(git describe)" > .compat_version fi cd $GIT_TREE TREE_NAME=${GIT_REMOTE_URL##*/} echo $TREE_NAME > $DIR/.compat_base_tree echo $GIT_DESCRIBE > $DIR/.compat_base_tree_version case $TREE_NAME in "wireless-testing.git") # John's wireless-testing echo -e "This is a ${RED}wireless-testing.git${NORMAL} compat-drivers release" ;; "linux-next.git") # The linux-next integration testing tree echo -e "This is a ${RED}linux-next.git${NORMAL} compat-drivers release" ;; "linux-stable.git") # Greg's all stable tree echo -e "This is a ${GREEN}linux-stable.git${NORMAL} compat-drivers release" ;; "linux-2.6.git") # Linus' 2.6 tree echo -e "This is a ${GREEN}linux-2.6.git${NORMAL} compat-drivers release" ;; *) ;; esac cd $DIR echo -e "\nBase tree: ${GREEN}$(cat .compat_base_tree)${NORMAL}" >> $CODE_METRICS echo -e "Base tree version: ${PURPLE}$(cat .compat_base_tree_version)${NORMAL}" >> $CODE_METRICS echo -e "compat.git: ${CYAN}$(cat .compat_base)${NORMAL}" >> $CODE_METRICS echo -e "compat-drivers release: ${YELLOW}$(cat .compat_version)${NORMAL}" >> $CODE_METRICS fi echo -e "Code metrics archive: ${GREEN}http://bit.ly/H6BTF7${NORMAL}" >> $CODE_METRICS cat $CODE_METRICS ./scripts/driver-select restore compat-drivers-2012-09-18/config.mk0000644000175000017500000004667512026176477016223 0ustar mcgrofmcgrofifeq ($(wildcard $(KLIB_BUILD)/.config),) # These will be ignored by compat autoconf export CONFIG_PCI=y export CONFIG_USB=y export CONFIG_PCMCIA=y export CONFIG_SSB=m else include $(KLIB_BUILD)/.config endif ifneq ($(wildcard $(KLIB_BUILD)/Makefile),) KERNEL_VERSION := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p') ifneq ($(KERNEL_VERSION),2) else KERNEL_26SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^2\.6\.\([0-9]\+\).*/\1/p') endif # FIXME: This lower limit is different (e.g. 3.0) for DRM stuff. ifdef CONFIG_COMPAT_KERNEL_2_6_24 $(error "ERROR: compat-drivers by default supports kernels >= 2.6.24, try enabling only one driver though") endif #CONFIG_COMPAT_KERNEL_2_6_24 ifeq ($(CONFIG_CFG80211),y) $(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.") endif # 2.6.27 has FTRACE_DYNAMIC borked, so we will complain if # you have it enabled, otherwise you will very likely run into # a kernel panic. # XXX: move this to compat_autoconf.h script generation ifeq ($(KERNEL_VERSION),2) ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_26SUBLEVEL) -eq 27 && echo yes),yes) ifeq ($(CONFIG_DYNAMIC_FTRACE),y) $(error "ERROR: Your 2.6.27 kernel has CONFIG_DYNAMIC_FTRACE, please upgrade your distribution kernel as newer ones should not have this enabled (and if so report a bug) or remove this warning if you know what you are doing") endif endif endif # This is because with CONFIG_MAC80211 include/linux/skbuff.h will # enable on 2.6.27 a new attribute: # # skb->do_not_encrypt # # and on 2.6.28 another new attribute: # # skb->requeue # # In kernel 2.6.32 both attributes were removed. # # XXX: move this to compat_autoconf.h script generation ifeq ($(KERNEL_VERSION),2) ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_26SUBLEVEL) -ge 27 -a $(KERNEL_26SUBLEVEL) -le 31 && echo yes),yes) ifeq ($(CONFIG_MAC80211),) $(error "ERROR: Your >=2.6.27 and <= 2.6.31 kernel has CONFIG_MAC80211 disabled, you should have it CONFIG_MAC80211=m if you want to use this thing.") endif endif endif ifneq ($(KERNELRELEASE),) # This prevents a warning # We will warn when you don't have MQ support or NET_SCHED enabled. # # We could consider just quiting if MQ and NET_SCHED is disabled # as I suspect all users of this package want 802.11e (WME) and # 802.11n (HT) support. ifeq ($(CONFIG_NET_SCHED),) QOS_REQS_MISSING+=CONFIG_NET_SCHED endif ifneq ($(QOS_REQS_MISSING),) # Complain about our missing dependencies $(warning "WARNING: You are running a kernel >= 2.6.23, you should enable in it $(QOS_REQS_MISSING) for 802.11[ne] support") endif endif # build check endif # kernel Makefile check # These both are needed by 802.11 and bluetooth so enable export CONFIG_COMPAT_RFKILL=y ifeq ($(CONFIG_MAC80211),y) $(error "ERROR: you have MAC80211 compiled into the kernel, CONFIG_MAC80211=y, as such you cannot replace its mac80211 driver. You need this set to CONFIG_MAC80211=m. If you are using Fedora upgrade your kernel as later version should this set as modular. For further information on Fedora see https://bugzilla.redhat.com/show_bug.cgi?id=470143. If you are using your own kernel recompile it and make mac80211 modular") else export CONFIG_COMPAT_WIRELESS=y export CONFIG_COMPAT_WIRELESS_MODULES=m export CONFIG_COMPAT_VAR_MODULES=m # We could technically separate these but not yet, we only have b44 # Note that we don't intend on backporting network drivers that # use Multiqueue as that was a pain to backport to kernels older than # 2.6.27. But -- we could just disable those drivers from kernels # older than 2.6.27 export CONFIG_COMPAT_NETWORK_MODULES=m export CONFIG_COMPAT_NET_USB_MODULES=m endif # The Bluetooth compatibility only builds on kernels >= 2.6.27 for now ifndef CONFIG_COMPAT_KERNEL_2_6_27 ifeq ($(CONFIG_BT),y) # we'll ignore compiling bluetooth else export CONFIG_COMPAT_BLUETOOTH=y export CONFIG_COMPAT_BLUETOOTH_MODULES=m export CONFIG_HID_GENERIC=m endif endif #CONFIG_COMPAT_KERNEL_2_6_27 # # CONFIG_COMPAT_FIRMWARE_CLASS definition has no leading whitespace, # because it gets passed-on through compat_autoconf.h. # ifdef CONFIG_COMPAT_KERNEL_2_6_33 ifndef CONFIG_COMPAT_RHEL_6_1 ifdef CONFIG_FW_LOADER export CONFIG_COMPAT_FIRMWARE_CLASS=m endif #CONFIG_FW_LOADER endif #CONFIG_COMPAT_RHEL_6_1 endif #CONFIG_COMPAT_KERNEL_2_6_33 ifdef CONFIG_COMPAT_KERNEL_2_6_36 ifndef CONFIG_COMPAT_RHEL_6_1 export CONFIG_COMPAT_KFIFO=y endif #CONFIG_COMPAT_RHEL_6_1 endif #CONFIG_COMPAT_KERNEL_2_6_36 # # CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN definitions have no leading # whitespace, because they get passed-on through compat_autoconf.h. # ifndef CONFIG_COMPAT_KERNEL_2_6_33 export CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN=y endif #CONFIG_COMPAT_KERNEL_2_6_33 ifdef CONFIG_COMPAT_RHEL_6_0 export CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN=y endif #CONFIG_COMPAT_RHEL_6_0 # # CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP definition has no leading # whitespace, because it gets passed-on through compat_autoconf.h. # ifdef CONFIG_COMPAT_RHEL_6_0 export CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP=y endif #CONFIG_COMPAT_RHEL_6_0 # Wireless subsystem stuff export CONFIG_MAC80211=m ifndef CONFIG_COMPAT_KERNEL_2_6_33 export CONFIG_COMPAT_MAC80211_DRIVER_API_TRACER=y endif #CONFIG_COMPAT_KERNEL_2_6_33 # export CONFIG_MAC80211_DEBUGFS=y # export CONFIG_MAC80211_NOINLINE=y # export CONFIG_MAC80211_VERBOSE_DEBUG=y # export CONFIG_MAC80211_HT_DEBUG=y # export CONFIG_MAC80211_TKIP_DEBUG=y # export CONFIG_MAC80211_IBSS_DEBUG=y # export CONFIG_MAC80211_VERBOSE_PS_DEBUG=y # export CONFIG_MAC80211_VERBOSE_MPL_DEBUG=y # export CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG=y # export CONFIG_MAC80211_VERBOSE_TDLS_DEBUG # export CONFIG_MAC80211_DEBUG_COUNTERS=y # choose between pid and minstrel as default rate control algorithm export CONFIG_MAC80211_RC_DEFAULT=minstrel_ht export CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y # export CONFIG_MAC80211_RC_DEFAULT_PID=y # This is the one used by our compat-drivers net/mac80211/rate.c # in case you have and old kernel which is overriding this to pid. export CONFIG_COMPAT_MAC80211_RC_DEFAULT=minstrel_ht export CONFIG_MAC80211_RC_PID=y export CONFIG_MAC80211_RC_MINSTREL=y export CONFIG_MAC80211_RC_MINSTREL_HT=y ifdef CONFIG_LEDS_TRIGGERS export CONFIG_MAC80211_LEDS=y endif #CONFIG_LEDS_TRIGGERS # enable mesh networking too export CONFIG_MAC80211_MESH=y export CONFIG_CFG80211=m export CONFIG_CFG80211_DEFAULT_PS=y # export CONFIG_CFG80211_DEBUGFS=y # export CONFIG_NL80211_TESTMODE=y # export CONFIG_CFG80211_DEVELOPER_WARNINGS=y # export CONFIG_CFG80211_REG_DEBUG=y # export CONFIG_CFG80211_INTERNAL_REGDB=y # See below for wext stuff export CONFIG_LIB80211=m export CONFIG_LIB80211_CRYPT_WEP=m export CONFIG_LIB80211_CRYPT_CCMP=m export CONFIG_LIB80211_CRYPT_TKIP=m # export CONFIG_LIB80211_DEBUG=y export CONFIG_BT=m export CONFIG_BT_RFCOMM=m ifndef CONFIG_COMPAT_KERNEL_2_6_33 export CONFIG_COMPAT_BT_RFCOMM_TTY=y endif #CONFIG_COMPAT_KERNEL_2_6_33 export CONFIG_BT_BNEP=m export CONFIG_BT_BNEP_MC_FILTER=y export CONFIG_BT_BNEP_PROTO_FILTER=y # CONFIG_BT_CMTP depends on ISDN_CAPI ifdef CONFIG_ISDN_CAPI export CONFIG_BT_CMTP=m endif #CONFIG_ISDN_CAPI ifndef CONFIG_COMPAT_KERNEL_2_6_28 export CONFIG_COMPAT_BT_HIDP=m endif #CONFIG_COMPAT_KERNEL_2_6_28 export CONFIG_BT_HCIUART=M export CONFIG_BT_HCIUART_H4=y export CONFIG_BT_HCIUART_BCSP=y export CONFIG_BT_HCIUART_ATH3K=y export CONFIG_BT_HCIUART_LL=y export CONFIG_BT_HCIVHCI=m export CONFIG_BT_MRVL=m ifdef CONFIG_PCMCIA export CONFIG_BT_HCIDTL1=m export CONFIG_BT_HCIBT3C=m export CONFIG_BT_HCIBLUECARD=m export CONFIG_BT_HCIBTUART=m endif #CONFIG_PCMCIA # We need CONFIG_WIRELESS_EXT for CONFIG_CFG80211_WEXT for every kernel # version. The new way CONFIG_CFG80211_WEXT is called from the kernel # does not work with compat-drivers because it calls some callback # function on struct wiphy. This struct is shipped with compat-drivers # and changes from kernel version to version. We are using the # wireless_handlers attribute which will be activated by # export CONFIG_WIRELESS_EXT. ifdef CONFIG_WIRELESS_EXT export CONFIG_CFG80211_WEXT=y else #CONFIG_CFG80211_WEXT $(warning "WARNING: CONFIG_CFG80211_WEXT will be deactivated or not working because kernel was compiled with CONFIG_WIRELESS_EXT=n. Tools using wext interface like iwconfig will not work. To activate it build your kernel e.g. with CONFIG_LIBIPW=m.") endif #CONFIG_WIRELESS_EXT ifdef CONFIG_STAGING export CONFIG_COMPAT_STAGING=m endif #CONFIG_STAGING # mac80211 test driver export CONFIG_MAC80211_HWSIM=m export CONFIG_ATH5K=m # export CONFIG_ATH5K_DEBUG=y # export CONFIG_ATH5K_TRACER=y # export CONFIG_ATH5K_AHB=y export CONFIG_ATH9K=m export CONFIG_ATH9K_HW=m export CONFIG_ATH9K_COMMON=m # export CONFIG_ATH9K_DEBUGFS=y # export CONFIG_ATH9K_AHB=y # export CONFIG_ATH9K_PKTLOG=y # Disable this to get minstrel as default, we leave the ath9k # rate control algorithm as the default for now as that is also # default upstream on the kernel. We will move this to minstrel # as default once we get minstrel properly tested and blessed by # our systems engineering team. CCK rates also need to be used # for long range considerations. export CONFIG_COMPAT_ATH9K_RATE_CONTROL=y export CONFIG_ATH9K_BTCOEX_SUPPORT=y # WIL6210 requires MSI only available >= 2.6.30 ifndef CONFIG_COMPAT_KERNEL_2_6_30 export CONFIG_WIL6210=m endif #CONFIG_COMPAT_KERNEL_2_6_30 ifndef CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_ATH6KL=m # export CONFIG_ATH6KL_DEBUG=y endif #CONFIG_COMPAT_KERNEL_2_6_27 # PCI Drivers ifdef CONFIG_PCI export CONFIG_ATH5K_PCI=y export CONFIG_ATH9K_PCI=y export CONFIG_IWLWIFI=m export CONFIG_IWLDVM=m export CONFIG_IWLWIFI_P2P=y # export CONFIG_IWLWIFI_DEBUG=y # export CONFIG_IWLWIFI_DEBUGFS=y # export CONFIG_IWLWIFI_DEVICE_TRACING=y # export CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE=y export CONFIG_IWLEGACY=m export CONFIG_COMPAT_IWL4965=m export CONFIG_IWL3945=m # export CONFIG_IWLEGACY_DEBUG=y # export CONFIG_IWLEGACY_DEBUGFS=y export CONFIG_B43=m export CONFIG_B43_HWRNG=y export CONFIG_B43_PCI_AUTOSELECT=y ifdef CONFIG_PCMCIA export CONFIG_B43_PCMCIA=y endif #CONFIG_PCMCIA ifdef CONFIG_MAC80211_LEDS export CONFIG_B43_LEDS=y endif #CONFIG_MAC80211_LEDS export CONFIG_B43_PHY_LP=y export CONFIG_B43_PHY_N=y export CONFIG_B43_PHY_HT=y # export CONFIG_B43_PHY_LCN=y # export CONFIG_B43_DEBUG=y export CONFIG_B43LEGACY=m export CONFIG_B43LEGACY_HWRNG=y export CONFIG_B43LEGACY_PCI_AUTOSELECT=y ifdef CONFIG_MAC80211_LEDS export CONFIG_B43LEGACY_LEDS=y endif #CONFIG_MAC80211_LEDS # export CONFIG_B43LEGACY_DEBUG=y export CONFIG_B43LEGACY_DMA=y export CONFIG_B43LEGACY_PIO=y ifdef CONFIG_WIRELESS_EXT # The Intel ipws export CONFIG_LIBIPW=m # export CONFIG_LIBIPW_DEBUG=y export CONFIG_IPW2100=m export CONFIG_IPW2100_MONITOR=y # export CONFIG_IPW2100_DEBUG=y export CONFIG_IPW2200=m export CONFIG_IPW2200_MONITOR=y export CONFIG_IPW2200_RADIOTAP=y export CONFIG_IPW2200_PROMISCUOUS=y export CONFIG_IPW2200_QOS=y # export CONFIG_IPW2200_DEBUG=y # The above enables use a second interface prefixed 'rtap'. # Example usage: # # % modprobe ipw2200 rtap_iface=1 # % ifconfig rtap0 up # % tethereal -i rtap0 # # If you do not specify 'rtap_iface=1' as a module parameter then # the rtap interface will not be created and you will need to turn # it on via sysfs: # # % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface endif #CONFIG_WIRELESS_EXT ifdef CONFIG_SSB # Sonics Silicon Backplane export CONFIG_SSB_SPROM=y export CONFIG_SSB_BLOCKIO=y export CONFIG_SSB_PCIHOST=y export CONFIG_SSB_B43_PCI_BRIDGE=y ifdef CONFIG_PCMCIA export CONFIG_SSB_PCMCIAHOST=y endif #CONFIG_PCMCIA # export CONFIG_SSB_DEBUG=y export CONFIG_SSB_DRIVER_PCICORE=y export CONFIG_B43_SSB=y endif #CONFIG_SSB export CONFIG_BCMA=m export CONFIG_BCMA_BLOCKIO=y export CONFIG_BCMA_HOST_PCI=y # export CONFIG_BCMA_DEBUG=y export CONFIG_B43_BCMA=y export CONFIG_B43_BCMA_EXTRA=y export CONFIG_P54_PCI=m export CONFIG_B44=m export CONFIG_B44_PCI=y export CONFIG_RTL8180=m export CONFIG_ADM8211=m export CONFIG_RT2X00_LIB_PCI=m export CONFIG_RT2400PCI=m export CONFIG_RT2500PCI=m ifdef CONFIG_CRC_CCITT export CONFIG_RT2800PCI=m export CONFIG_RT2800PCI_RT33XX=y export CONFIG_RT2800PCI_RT35XX=y export CONFIG_RT2800PCI_RT53XX=y endif #CONFIG_CRC_CCITT NEED_RT2X00=y # Two rt2x00 drivers require firmware: rt61pci and rt73usb. They depend on # CRC to check the firmware. We check here first for the PCI # driver as we're in the PCI section. ifdef CONFIG_CRC_ITU_T export CONFIG_RT61PCI=m endif #CONFIG_CRC_ITU_T export CONFIG_MWL8K=m # Ethernet drivers go here export CONFIG_ATL1=m export CONFIG_ATL2=m export CONFIG_ATL1E=m ifndef CONFIG_COMPAT_KERNEL_2_6_28 export CONFIG_ATL1C=m export CONFIG_ALX=m endif #CONFIG_COMPAT_KERNEL_2_6_28 ifdef CONFIG_WIRELESS_EXT export CONFIG_HERMES=m export CONFIG_HERMES_CACHE_FW_ON_INIT=y ifdef CONFIG_PPC_PMAC export CONFIG_APPLE_AIRPORT=m endif #CONFIG_PPC_PMAC export CONFIG_PLX_HERMES=m export CONFIG_TMD_HERMES=m export CONFIG_NORTEL_HERMES=m export CONFIG_PCI_HERMES=m ifdef CONFIG_PCMCIA export CONFIG_PCMCIA_HERMES=m export CONFIG_PCMCIA_SPECTRUM=m endif #CONFIG_PCMCIA endif #CONFIG_WIRELESS_EXT export CONFIG_RTL8192CE=m export CONFIG_RTL8192SE=m export CONFIG_RTL8192DE=m export CONFIG_BRCMSMAC=m export CONFIG_MWIFIEX_PCIE=m endif #CONFIG_PCI ## end of PCI ifdef CONFIG_PCMCIA ifdef CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_LIBERTAS=n export CONFIG_LIBERTAS_CS=n else #CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_LIBERTAS_CS=m NEED_LIBERTAS=y endif #CONFIG_COMPAT_KERNEL_2_6_27 endif #CONFIG_PCMCIA ## end of PCMCIA # This is required for some cards export CONFIG_EEPROM_93CX6=m # USB Drivers ifdef CONFIG_USB ifndef CONFIG_COMPAT_KERNEL_2_6_29 export CONFIG_COMPAT_ZD1211RW=m # export CONFIG_ZD1211RW_DEBUG=y endif #CONFIG_COMPAT_KERNEL_2_6_29 # Sorry, rndis_wlan uses cancel_work_sync which is new and can't be done in compat... # Wireless RNDIS USB support (RTL8185 802.11g) A-Link WL54PC # All of these devices are based on Broadcom 4320 chip which # is only wireless RNDIS chip known to date. # Note: this depends on CONFIG_USB_NET_RNDIS_HOST and CONFIG_USB_NET_CDCETHER # it also requires new RNDIS_HOST and CDC_ETHER modules which we add ifdef CONFIG_COMPAT_KERNEL_2_6_29 export CONFIG_USB_COMPAT_USBNET=n export CONFIG_USB_NET_COMPAT_RNDIS_HOST=n export CONFIG_USB_NET_COMPAT_RNDIS_WLAN=n export CONFIG_USB_NET_COMPAT_CDCETHER=n else #CONFIG_COMPAT_KERNEL_2_6_29 export CONFIG_USB_COMPAT_USBNET=m ifdef CONFIG_USB_NET_CDCETHER export CONFIG_USB_NET_COMPAT_RNDIS_HOST=m export CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m endif #CONFIG_USB_NET_CDCETHER ifdef CONFIG_USB_NET_CDCETHER_MODULE export CONFIG_USB_NET_COMPAT_RNDIS_HOST=m export CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m endif #CONFIG_USB_NET_CDCETHER export CONFIG_USB_NET_COMPAT_CDCETHER=m endif #CONFIG_COMPAT_KERNEL_2_6_29 export CONFIG_P54_USB=m export CONFIG_RTL8187=m ifdef CONFIG_MAC80211_LEDS export CONFIG_RTL8187_LEDS=y endif #CONFIG_MAC80211_LEDS export CONFIG_AT76C50X_USB=m ifndef CONFIG_COMPAT_KERNEL_2_6_29 export CONFIG_CARL9170=m ifdef CONFIG_MAC80211_LEDS export CONFIG_CARL9170_LEDS=y endif #CONFIG_MAC80211_LEDS # export CONFIG_CARL9170_DEBUGFS=y export CONFIG_CARL9170_WPC=y endif #CONFIG_COMPAT_KERNEL_2_6_29 # This activates a threading fix for usb urb. # this is mainline commit: b3e670443b7fb8a2d29831b62b44a039c283e351 # This fix will be included in some stable releases. export CONFIG_COMPAT_USB_URB_THREAD_FIX=y export CONFIG_ATH9K_HTC=m # export CONFIG_ATH9K_HTC_DEBUGFS=y export CONFIG_ATH6KL_USB=m export CONFIG_BRCMFMAC_USB=y # RT2500USB does not require firmware export CONFIG_RT2500USB=m ifdef CONFIG_CRC_CCITT export CONFIG_RT2800USB=m export CONFIG_RT2800USB_RT33XX=y export CONFIG_RT2800USB_RT35XX=y export CONFIG_RT2800USB_RT53XX=y export CONFIG_RT2800USB_UNKNOWN=y endif #CONFIG_CRC_CCITT export CONFIG_RT2X00_LIB_USB=m NEED_RT2X00=y # RT73USB requires firmware ifdef CONFIG_CRC_ITU_T export CONFIG_RT73USB=m endif #CONFIG_CRC_ITU_T ifdef CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_LIBERTAS_THINFIRM_USB=n export CONFIG_LIBERTAS_USB=n NEED_LIBERTAS=n else #CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_LIBERTAS_THINFIRM_USB=m export CONFIG_LIBERTAS_USB=m NEED_LIBERTAS=y endif #CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_ORINOCO_USB=m export CONFIG_BT_HCIBTUSB=m export CONFIG_BT_HCIBCM203X=m export CONFIG_BT_HCIBPA10X=m export CONFIG_BT_HCIBFUSB=m export CONFIG_BT_ATH3K=m export CONFIG_RTL8192CU=m endif #CONFIG_USB end of USB driver list ifdef CONFIG_SPI_MASTER ifndef CONFIG_COMPAT_KERNEL_2_6_25 ifdef CONFIG_CRC7 ifndef CONFIG_COMPAT_KERNEL_2_6_37 export CONFIG_COMPAT_WL1251_SPI=m endif #CONFIG_COMPAT_KERNEL_2_6_37 export CONFIG_WLCORE_SPI=m endif #CONFIG_CRC7 export CONFIG_P54_SPI=m ifdef CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_LIBERTAS_SPI=n NEED_LIBERTAS=n else #CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_LIBERTAS_SPI=m NEED_LIBERTAS=y endif #CONFIG_COMPAT_KERNEL_2_6_27 endif #CONFIG_COMPAT_KERNEL_2_6_25 endif #CONFIG_SPI_MASTER end of SPI driver list ifdef CONFIG_MMC export CONFIG_SSB_SDIOHOST=y export CONFIG_B43_SDIO=y ifdef CONFIG_CRC7 ifdef CONFIG_WL12XX_PLATFORM_DATA ifndef CONFIG_COMPAT_KERNEL_2_6_37 export CONFIG_COMPAT_WL1251_SDIO=m endif #CONFIG_COMPAT_KERNEL_2_6_37 ifndef CONFIG_COMPAT_KERNEL_2_6_38 export CONFIG_WLCORE_SDIO=m endif #CONFIG_COMPAT_KERNEL_2_6_38 endif #CONFIG_WL12XX_PLATFORM_DATA endif #CONFIG_CRC7 export CONFIG_MWIFIEX_SDIO=m ifndef CONFIG_COMPAT_KERNEL_2_6_32 export CONFIG_COMPAT_LIBERTAS_SDIO=m NEED_LIBERTAS=y endif #CONFIG_COMPAT_KERNEL_2_6_32 export CONFIG_IWM=m # export CONFIG_IWM_DEBUG=y # export CONFIG_IWM_TRACING=y export CONFIG_BT_HCIBTSDIO=m export CONFIG_BT_MRVL_SDIO=m export CONFIG_ATH6KL_SDIO=m export CONFIG_BRCMFMAC_SDIO=y endif #CONFIG_MMC export CONFIG_RTLWIFI=m export CONFIG_RTL8192C_COMMON=m # Common rt2x00 requirements ifeq ($(NEED_RT2X00),y) export CONFIG_RT2X00=y export CONFIG_RT2X00_LIB=m export CONFIG_RT2800_LIB=m export CONFIG_RT2X00_LIB_FIRMWARE=y export CONFIG_RT2X00_LIB_CRYPTO=y # export CONFIG_RT2X00_LIB_SOC=y ifdef CONFIG_COMPAT_KERNEL_2_6_25 export CONFIG_RT2X00_LIB_LEDS=n else #CONFIG_COMPAT_KERNEL_2_6_25 ifdef CONFIG_LEDS_CLASS export CONFIG_RT2X00_LIB_LEDS=y endif #CONFIG_LEDS_CLASS endif #CONFIG_COMPAT_KERNEL_2_6_25 # export CONFIG_RT2X00_DEBUG=y # export CONFIG_RT2X00_LIB_DEBUGFS=y endif # p54 export CONFIG_P54_COMMON=m ifdef CONFIG_MAC80211_LEDS export CONFIG_P54_LEDS=y endif #CONFIG_MAC80211_LEDS # Atheros export CONFIG_ATH_COMMON=m # export CONFIG_ATH_DEBUG=y export CONFIG_BRCMUTIL=m # export CONFIG_BRCMDBG=y ifndef CONFIG_COMPAT_KERNEL_2_6_29 export CONFIG_BRCMFMAC=m endif #CONFIG_COMPAT_KERNEL_2_6_29 ifndef CONFIG_COMPAT_KERNEL_2_6_30 export CONFIG_WL_TI=y export CONFIG_WLCORE=m ifdef CONFIG_CRC7 export CONFIG_WL1251=m export CONFIG_WL12XX=m export CONFIG_WL18XX=m endif #CONFIG_CRC7 endif #CONFIG_COMPAT_KERNEL_2_6_30 export CONFIG_MWIFIEX=m ifndef CONFIG_CORDIC export CONFIG_COMPAT_CORDIC=y endif #CONFIG_CORDIC ifndef CONFIG_CRC8 export CONFIG_COMPAT_CRC8=y endif #CONFIG_CRC8 ifdef CONFIG_COMPAT_KERNEL_2_6_27 export CONFIG_LIBERTAS=n else #CONFIG_COMPAT_KERNEL_2_6_27 ifeq ($(NEED_LIBERTAS),y) export CONFIG_LIBERTAS_THINFIRM=m export CONFIG_LIBERTAS=m export CONFIG_LIBERTAS_MESH=y # export CONFIG_LIBERTAS_DEBUG=y endif endif #CONFIG_COMPAT_KERNEL_2_6_27 # We need the backported rfkill module on kernel < 2.6.31. # In more recent kernel versions use the in kernel rfkill module. ifdef CONFIG_COMPAT_KERNEL_2_6_31 export CONFIG_RFKILL_BACKPORT=m ifdef CONFIG_LEDS_TRIGGERS export CONFIG_RFKILL_BACKPORT_LEDS=y endif #CONFIG_LEDS_TRIGGERS export CONFIG_RFKILL_BACKPORT_INPUT=y endif #CONFIG_COMPAT_KERNEL_2_6_31 compat-drivers-2012-09-18/.compat_base_tree0000644000175000017500000000001712026211316017654 0ustar mcgrofmcgroflinux-next.git compat-drivers-2012-09-18/pending-stable/0000755000175000017500000000000012026176477017277 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/pending-stable/network/0000755000175000017500000000000012026176477020770 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/pending-stable/network/.gitignore0000644000175000017500000000000012026176477022746 0ustar mcgrofmcgrofcompat-drivers-2012-09-18/pending-stable/.ignore0000644000175000017500000000005612025712017020545 0ustar mcgrofmcgrofath9k-lock-reset-and-PCU-start-stopping.patch compat-drivers-2012-09-18/pending-stable/README0000644000175000017500000000213512026176477020160 0ustar mcgrofmcgrofcompat-drivers pending-stable/ patches ======================================= Often right before the merge window we get a block on non oops/regression fixes for stable fixes. Some stable fixes often get propagated afterwards during the extraversion maintenance of the kernels. Right before the merge window circa rc4 and rc5 subsystem maintainers get pegged if they throw in non oops/regression fixes for Linus or their respective upstream maintainer. While this makes sense for tree management and stable release considerations we still need to get users some stable patches propagated. This directory is there to help with that. Only patches which have been merged into linux-next.git will be included in this directory which means you must post it and the maintainer should have merged it and Stephen would have picked it up. This directory will always be empty for bleeding edge releases as bleeding edge releases are always based on linux-next already. This directory only makes sense for stable release of the kernel, and it we will always try to use it, in case there are stable fixes not yet propagated. compat-drivers-2012-09-18/Makefile0000644000175000017500000002064312026176477016050 0ustar mcgrofmcgrof# # Copyright (c) 2007-2012 Luis R. Rodriguez # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. export KMODDIR?= updates KMODDIR_ARG:= "INSTALL_MOD_DIR=$(KMODDIR)" ifneq ($(origin KLIB), undefined) KMODPATH_ARG:= "INSTALL_MOD_PATH=$(KLIB)" else export KLIB:= /lib/modules/$(shell uname -r) endif export KLIB_BUILD ?= $(KLIB)/build export MAKE DESTDIR?= ifneq ($(KERNELRELEASE),) -include $(COMPAT_CONFIG) include $(COMPAT_CONFIG_CW) NOSTDINC_FLAGS := -I$(M)/include/ \ -include $(M)/include/linux/compat-2.6.h \ $(CFLAGS) obj-y := compat/ obj-$(CONFIG_COMPAT_RFKILL) += net/rfkill/ ifeq ($(BT),) obj-$(CONFIG_COMPAT_WIRELESS) += net/wireless/ net/mac80211/ obj-$(CONFIG_COMPAT_WIRELESS_MODULES) += drivers/net/wireless/ obj-$(CONFIG_COMPAT_NET_USB_MODULES) += drivers/net/usb/ obj-$(CONFIG_COMPAT_NETWORK_MODULES) += drivers/net/ethernet/atheros/ obj-$(CONFIG_COMPAT_NETWORK_MODULES) += drivers/net/ethernet/broadcom/ obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/ssb/ obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/bcma/ obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/misc/eeprom/ ifeq ($(CONFIG_STAGING_EXCLUDE_BUILD),) endif endif obj-$(CONFIG_COMPAT_BLUETOOTH) += net/bluetooth/ obj-$(CONFIG_COMPAT_BLUETOOTH_MODULES) += drivers/bluetooth/ else export PWD := $(shell pwd) # The build will fail if there is any space in PWD. ifneq (,$(findstring $() ,$(PWD))) $(error "The path to this compat-drivers directory has spaces in it." \ "Please put it somewhere where there is no space") endif export CFLAGS += \ -DCOMPAT_BASE="\"$(shell cat $(PWD)/.compat_base)\"" \ -DCOMPAT_BASE_TREE="\"$(shell cat $(PWD)/.compat_base_tree)\"" \ -DCOMPAT_BASE_TREE_VERSION="\"$(shell cat $(PWD)/.compat_base_tree_version)\"" \ -DCOMPAT_PROJECT="\"Compat-drivers\"" \ -DCOMPAT_VERSION="\"$(shell cat $(PWD)/.compat_version)\"" # These exported as they are used by the scripts # to check config and compat autoconf export COMPAT_CONFIG_CW=$(PWD)/config.mk export COMPAT_CONFIG=$(PWD)/.config export CONFIG_CHECK=$(PWD)/.config.mk_md5sum.txt export COMPAT_AUTOCONF=include/linux/compat_autoconf.h export CREL=$(shell cat $(PWD)/.compat_version) export CREL_PRE:=.compat_autoconf_ export CREL_CHECK:=$(PWD)/$(CREL_PRE)$(CREL) all: modules $(COMPAT_CONFIG): ; modules: $(CREL_CHECK) $(MAKE) -C $(KLIB_BUILD) M=$(PWD) modules @touch $@ bt: $(CREL_CHECK) +@./scripts/check_config.sh $(MAKE) -C $(KLIB_BUILD) M=$(PWD) BT=TRUE modules @touch $@ # We use a CREL_CHECK variable which will depend on the environment used to # build. If the environment requirements change it forces a reconfiguration # check. This means we force a new reconfiguration check if a the user gets a # new updates of compat-drivers or when the user updates the $(COMPAT_CONFIG) # file. # XXX: add kernel target to the CREL_CHECK mix, this would ensure we also # reconfigure and build again fresh if we detect a new target kernel is # being used. $(CREL_CHECK): @# Force to regenerate compat autoconf +@./compat/scripts/gen-compat-config.sh > $(COMPAT_CONFIG) @rm -f $(CONFIG_CHECK) +@./scripts/check_config.sh @md5sum $(COMPAT_CONFIG_CW) > $(CONFIG_CHECK) @touch $@ btinstall: btuninstall bt-install-modules bt-install-modules: bt $(MAKE) -C $(KLIB_BUILD) M=$(PWD) $(KMODDIR_ARG) $(KMODPATH_ARG) BT=TRUE \ modules_install @/sbin/depmod -ae @echo @echo Now run: @echo @echo sudo make btunload: @echo @echo And then load the needed bluetooth modules. If unsure reboot. @echo btuninstall: @# New location, matches upstream @rm -rf $(KLIB)/$(KMODDIR)/net/bluetooth/ @rm -rf $(KLIB)/$(KMODDIR)/drivers/bluetooth/ @# Lets only remove the stuff we are sure we are providing @# on the misc directory. @/sbin/depmod -ae @echo btclean: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) BT=TRUE clean @rm -f $(CREL_PRE)* install: uninstall install-modules install-scripts install-modules: modules $(MAKE) -C $(KLIB_BUILD) M=$(PWD) $(KMODDIR_ARG) $(KMODPATH_ARG) \ modules_install @./scripts/update-initramfs install-scripts: @# All the scripts we can use @mkdir -p $(DESTDIR)/usr/lib/compat-drivers/ @install scripts/modlib.sh $(DESTDIR)/usr/lib/compat-drivers/ @install scripts/madwifi-unload $(DESTDIR)/usr/sbin/ @# This is to allow switching between drivers without blacklisting @install scripts/athenable $(DESTDIR)/usr/sbin/ @install scripts/b43enable $(DESTDIR)/usr/sbin/ @install scripts/iwl-enable $(DESTDIR)/usr/sbin/ @install scripts/athload $(DESTDIR)/usr/sbin/ @install scripts/b43load $(DESTDIR)/usr/sbin/ @install scripts/iwl-load $(DESTDIR)/usr/sbin/ @if [ $(shell modinfo ath_pci > /dev/null 2>&1 && echo 1) ]; then \ echo -n "Note: madwifi detected, we're going to disable it. " ;\ echo "If you would like to enable it later you can run:" ;\ echo " sudo athenable madwifi" ;\ echo ;\ echo Running athenable ath5k...;\ $(DESTDIR)/usr/sbin/athenable ath5k ;\ fi @if [ $(shell modinfo iwl4965 > /dev/null 2>&1 && echo 1) ]; then \ echo ;\ echo -n "Note: iwl4965 detected, we're going to disable it. " ;\ echo "If you would like to enable it later you can run:" ;\ echo " sudo iwl-load iwl4965" ;\ echo ;\ echo Running iwl-enable iwlagn...;\ $(DESTDIR)/usr/sbin/iwl-enable iwlagn ;\ fi @if [ $(shell modinfo iwlagn > /dev/null 2>&1 && echo 1) ] \ && [ $(shell modinfo iwlwifi > /dev/null 2>&1 && echo 1) ]; then \ echo ;\ echo -n "Note: iwlagn detected, we're going to disable it. " ;\ echo "If you would like to enable it later you can run:" ;\ echo " sudo iwl-load iwlagn" ;\ echo ;\ echo Running iwl-enable iwlwifi...;\ $(DESTDIR)/usr/sbin/iwl-enable iwlwifi ;\ fi @# If on distributions like Mandriva which like to @# compress their modules this will find out and do @# it for you. Reason is some old version of modutils @# won't know mac80211.ko should be used instead of @# mac80211.ko.gz @./scripts/compress_modules @# Mandrake doesn't have a depmod.d/ conf file to prefer @# the updates/ dir which is what we use so we add one for it @# (or any other distribution that doens't have this). @./scripts/check_depmod @# Udev stuff needed for the new compat_firmware_class. @./compat/scripts/compat_firmware_install @/sbin/depmod -a @echo @echo Now run: @echo @echo sudo make unload to unload all: wireless, bluetooth and ethernet modules @echo sudo make wlunload to unload wireless modules @echo sudo make btunload to unload bluetooth modules @echo @echo Run sudo modprobe 'driver-name' to load your desired driver. @echo If unsure reboot. @echo uninstall: @# New location, matches upstream @rm -rf $(KLIB)/$(KMODDIR)/compat/ @rm -rf $(KLIB)/$(KMODDIR)/net/mac80211/ @rm -rf $(KLIB)/$(KMODDIR)/net/rfkill/ @rm -rf $(KLIB)/$(KMODDIR)/net/wireless/ @rm -rf $(KLIB)/$(KMODDIR)/drivers/ssb/ @rm -rf $(KLIB)/$(KMODDIR)/drivers/net/usb/ @rm -rf $(KLIB)/$(KMODDIR)/drivers/net/wireless/ @rm -rf $(KLIB)/$(KMODDIR)/drivers/staging/ @rm -rf $(KLIB)/$(KMODDIR)/drivers/net/atl* @find $(KLIB)/$(KMODDIR)/drivers/net/ -name "alx*.ko" -o -name "atl*.ko" 2>/dev/null |xargs rm -f @# Lets only remove the stuff we are sure we are providing @# on the misc directory. @rm -f $(KLIB)/$(KMODDIR)/drivers/misc/eeprom/eeprom_93cx6.ko* @rm -f $(KLIB)/$(KMODDIR)/drivers/misc/eeprom_93cx6.ko* @rm -f $(KLIB)/$(KMODDIR)/drivers/net/b44.ko* @/sbin/depmod -a @./scripts/update-initramfs @echo clean: @if [ -d net -a -d $(KLIB_BUILD) ]; then \ $(MAKE) -C $(KLIB_BUILD) M=$(PWD) clean ;\ fi @rm -f $(CREL_PRE)* unload: @./scripts/unload.sh btunload: @./scripts/btunload.sh wlunload: @./scripts/wlunload.sh .PHONY: all clean install uninstall unload btunload wlunload modules bt Makefile endif clean-files += Module.symvers Module.markers modules modules.order clean-files += $(CREL_CHECK) $(CONFIG_CHECK) $(COMPAT_CONFIG) compat-drivers-2012-09-18/.compat_base0000644000175000017500000000002212026211315016630 0ustar mcgrofmcgrofcompat-2012-09-18 compat-drivers-2012-09-18/net/0000755000175000017500000000000012026176500015154 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/rfkill/0000755000175000017500000000000012026211315016431 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/rfkill/core.c0000644000175000017500000007254412026211315017541 0ustar mcgrofmcgrof/* * Copyright (C) 2006 - 2007 Ivo van Doorn * Copyright (C) 2007 Dmitry Torokhov * Copyright 2009 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rfkill.h" #define POLL_INTERVAL (5 * HZ) #define RFKILL_BLOCK_HW BIT(0) #define RFKILL_BLOCK_SW BIT(1) #define RFKILL_BLOCK_SW_PREV BIT(2) #define RFKILL_BLOCK_ANY (RFKILL_BLOCK_HW |\ RFKILL_BLOCK_SW |\ RFKILL_BLOCK_SW_PREV) #define RFKILL_BLOCK_SW_SETCALL BIT(31) struct rfkill { spinlock_t lock; const char *name; enum rfkill_type type; unsigned long state; u32 idx; bool registered; bool persistent; const struct rfkill_ops *ops; void *data; #ifdef CONFIG_RFKILL_LEDS struct led_trigger led_trigger; const char *ledtrigname; #endif struct device dev; struct list_head node; struct delayed_work poll_work; struct work_struct uevent_work; struct work_struct sync_work; }; #define to_rfkill(d) container_of(d, struct rfkill, dev) struct rfkill_int_event { struct list_head list; struct rfkill_event ev; }; struct rfkill_data { struct list_head list; struct list_head events; struct mutex mtx; wait_queue_head_t read_wait; bool input_handler; }; MODULE_AUTHOR("Ivo van Doorn "); MODULE_AUTHOR("Johannes Berg "); MODULE_DESCRIPTION("RF switch support"); MODULE_LICENSE("GPL"); /* * The locking here should be made much smarter, we currently have * a bit of a stupid situation because drivers might want to register * the rfkill struct under their own lock, and take this lock during * rfkill method calls -- which will cause an AB-BA deadlock situation. * * To fix that, we need to rework this code here to be mostly lock-free * and only use the mutex for list manipulations, not to protect the * various other global variables. Then we can avoid holding the mutex * around driver operations, and all is happy. */ static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_global_mutex); static LIST_HEAD(rfkill_fds); /* list of open fds of /dev/rfkill */ static unsigned int rfkill_default_state = 1; module_param_named(default_state, rfkill_default_state, uint, 0444); MODULE_PARM_DESC(default_state, "Default initial state for all radio types, 0 = radio off"); static struct { bool cur, sav; } rfkill_global_states[NUM_RFKILL_TYPES]; static bool rfkill_epo_lock_active; #ifdef CONFIG_RFKILL_LEDS static void rfkill_led_trigger_event(struct rfkill *rfkill) { struct led_trigger *trigger; if (!rfkill->registered) return; trigger = &rfkill->led_trigger; if (rfkill->state & RFKILL_BLOCK_ANY) led_trigger_event(trigger, LED_OFF); else led_trigger_event(trigger, LED_FULL); } static void rfkill_led_trigger_activate(struct led_classdev *led) { struct rfkill *rfkill; rfkill = container_of(led->trigger, struct rfkill, led_trigger); rfkill_led_trigger_event(rfkill); } const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) { return rfkill->led_trigger.name; } EXPORT_SYMBOL(rfkill_get_led_trigger_name); void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) { BUG_ON(!rfkill); rfkill->ledtrigname = name; } EXPORT_SYMBOL(rfkill_set_led_trigger_name); static int rfkill_led_trigger_register(struct rfkill *rfkill) { rfkill->led_trigger.name = rfkill->ledtrigname ? : dev_name(&rfkill->dev); rfkill->led_trigger.activate = rfkill_led_trigger_activate; return led_trigger_register(&rfkill->led_trigger); } static void rfkill_led_trigger_unregister(struct rfkill *rfkill) { led_trigger_unregister(&rfkill->led_trigger); } #else static void rfkill_led_trigger_event(struct rfkill *rfkill) { } static inline int rfkill_led_trigger_register(struct rfkill *rfkill) { return 0; } static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill) { } #endif /* CONFIG_RFKILL_LEDS */ static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill, enum rfkill_operation op) { unsigned long flags; ev->idx = rfkill->idx; ev->type = rfkill->type; ev->op = op; spin_lock_irqsave(&rfkill->lock, flags); ev->hard = !!(rfkill->state & RFKILL_BLOCK_HW); ev->soft = !!(rfkill->state & (RFKILL_BLOCK_SW | RFKILL_BLOCK_SW_PREV)); spin_unlock_irqrestore(&rfkill->lock, flags); } static void rfkill_send_events(struct rfkill *rfkill, enum rfkill_operation op) { struct rfkill_data *data; struct rfkill_int_event *ev; list_for_each_entry(data, &rfkill_fds, list) { ev = kzalloc(sizeof(*ev), GFP_KERNEL); if (!ev) continue; rfkill_fill_event(&ev->ev, rfkill, op); mutex_lock(&data->mtx); list_add_tail(&ev->list, &data->events); mutex_unlock(&data->mtx); wake_up_interruptible(&data->read_wait); } } static void rfkill_event(struct rfkill *rfkill) { if (!rfkill->registered) return; kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); /* also send event to /dev/rfkill */ rfkill_send_events(rfkill, RFKILL_OP_CHANGE); } static bool __rfkill_set_hw_state(struct rfkill *rfkill, bool blocked, bool *change) { unsigned long flags; bool prev, any; BUG_ON(!rfkill); spin_lock_irqsave(&rfkill->lock, flags); prev = !!(rfkill->state & RFKILL_BLOCK_HW); if (blocked) rfkill->state |= RFKILL_BLOCK_HW; else rfkill->state &= ~RFKILL_BLOCK_HW; *change = prev != blocked; any = !!(rfkill->state & RFKILL_BLOCK_ANY); spin_unlock_irqrestore(&rfkill->lock, flags); rfkill_led_trigger_event(rfkill); return any; } /** * rfkill_set_block - wrapper for set_block method * * @rfkill: the rfkill struct to use * @blocked: the new software state * * Calls the set_block method (when applicable) and handles notifications * etc. as well. */ static void rfkill_set_block(struct rfkill *rfkill, bool blocked) { unsigned long flags; int err; if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP)) return; /* * Some platforms (...!) generate input events which affect the * _hard_ kill state -- whenever something tries to change the * current software state query the hardware state too. */ if (rfkill->ops->query) rfkill->ops->query(rfkill, rfkill->data); spin_lock_irqsave(&rfkill->lock, flags); if (rfkill->state & RFKILL_BLOCK_SW) rfkill->state |= RFKILL_BLOCK_SW_PREV; else rfkill->state &= ~RFKILL_BLOCK_SW_PREV; if (blocked) rfkill->state |= RFKILL_BLOCK_SW; else rfkill->state &= ~RFKILL_BLOCK_SW; rfkill->state |= RFKILL_BLOCK_SW_SETCALL; spin_unlock_irqrestore(&rfkill->lock, flags); err = rfkill->ops->set_block(rfkill->data, blocked); spin_lock_irqsave(&rfkill->lock, flags); if (err) { /* * Failed -- reset status to _prev, this may be different * from what set set _PREV to earlier in this function * if rfkill_set_sw_state was invoked. */ if (rfkill->state & RFKILL_BLOCK_SW_PREV) rfkill->state |= RFKILL_BLOCK_SW; else rfkill->state &= ~RFKILL_BLOCK_SW; } rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL; rfkill->state &= ~RFKILL_BLOCK_SW_PREV; spin_unlock_irqrestore(&rfkill->lock, flags); rfkill_led_trigger_event(rfkill); rfkill_event(rfkill); } #ifdef CONFIG_RFKILL_INPUT static atomic_t rfkill_input_disabled = ATOMIC_INIT(0); /** * __rfkill_switch_all - Toggle state of all switches of given type * @type: type of interfaces to be affected * @state: the new state * * This function sets the state of all switches of given type, * unless a specific switch is claimed by userspace (in which case, * that switch is left alone) or suspended. * * Caller must have acquired rfkill_global_mutex. */ static void __rfkill_switch_all(const enum rfkill_type type, bool blocked) { struct rfkill *rfkill; rfkill_global_states[type].cur = blocked; list_for_each_entry(rfkill, &rfkill_list, node) { if (rfkill->type != type && type != RFKILL_TYPE_ALL) continue; rfkill_set_block(rfkill, blocked); } } /** * rfkill_switch_all - Toggle state of all switches of given type * @type: type of interfaces to be affected * @state: the new state * * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state). * Please refer to __rfkill_switch_all() for details. * * Does nothing if the EPO lock is active. */ void rfkill_switch_all(enum rfkill_type type, bool blocked) { if (atomic_read(&rfkill_input_disabled)) return; mutex_lock(&rfkill_global_mutex); if (!rfkill_epo_lock_active) __rfkill_switch_all(type, blocked); mutex_unlock(&rfkill_global_mutex); } /** * rfkill_epo - emergency power off all transmitters * * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED, * ignoring everything in its path but rfkill_global_mutex and rfkill->mutex. * * The global state before the EPO is saved and can be restored later * using rfkill_restore_states(). */ void rfkill_epo(void) { struct rfkill *rfkill; int i; if (atomic_read(&rfkill_input_disabled)) return; mutex_lock(&rfkill_global_mutex); rfkill_epo_lock_active = true; list_for_each_entry(rfkill, &rfkill_list, node) rfkill_set_block(rfkill, true); for (i = 0; i < NUM_RFKILL_TYPES; i++) { rfkill_global_states[i].sav = rfkill_global_states[i].cur; rfkill_global_states[i].cur = true; } mutex_unlock(&rfkill_global_mutex); } /** * rfkill_restore_states - restore global states * * Restore (and sync switches to) the global state from the * states in rfkill_default_states. This can undo the effects of * a call to rfkill_epo(). */ void rfkill_restore_states(void) { int i; if (atomic_read(&rfkill_input_disabled)) return; mutex_lock(&rfkill_global_mutex); rfkill_epo_lock_active = false; for (i = 0; i < NUM_RFKILL_TYPES; i++) __rfkill_switch_all(i, rfkill_global_states[i].sav); mutex_unlock(&rfkill_global_mutex); } /** * rfkill_remove_epo_lock - unlock state changes * * Used by rfkill-input manually unlock state changes, when * the EPO switch is deactivated. */ void rfkill_remove_epo_lock(void) { if (atomic_read(&rfkill_input_disabled)) return; mutex_lock(&rfkill_global_mutex); rfkill_epo_lock_active = false; mutex_unlock(&rfkill_global_mutex); } /** * rfkill_is_epo_lock_active - returns true EPO is active * * Returns 0 (false) if there is NOT an active EPO contidion, * and 1 (true) if there is an active EPO contition, which * locks all radios in one of the BLOCKED states. * * Can be called in atomic context. */ bool rfkill_is_epo_lock_active(void) { return rfkill_epo_lock_active; } /** * rfkill_get_global_sw_state - returns global state for a type * @type: the type to get the global state of * * Returns the current global state for a given wireless * device type. */ bool rfkill_get_global_sw_state(const enum rfkill_type type) { return rfkill_global_states[type].cur; } #endif bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) { bool ret, change; ret = __rfkill_set_hw_state(rfkill, blocked, &change); if (!rfkill->registered) return ret; if (change) schedule_work(&rfkill->uevent_work); return ret; } EXPORT_SYMBOL(rfkill_set_hw_state); static void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) { u32 bit = RFKILL_BLOCK_SW; /* if in a ops->set_block right now, use other bit */ if (rfkill->state & RFKILL_BLOCK_SW_SETCALL) bit = RFKILL_BLOCK_SW_PREV; if (blocked) rfkill->state |= bit; else rfkill->state &= ~bit; } bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) { unsigned long flags; bool prev, hwblock; BUG_ON(!rfkill); spin_lock_irqsave(&rfkill->lock, flags); prev = !!(rfkill->state & RFKILL_BLOCK_SW); __rfkill_set_sw_state(rfkill, blocked); hwblock = !!(rfkill->state & RFKILL_BLOCK_HW); blocked = blocked || hwblock; spin_unlock_irqrestore(&rfkill->lock, flags); if (!rfkill->registered) return blocked; if (prev != blocked && !hwblock) schedule_work(&rfkill->uevent_work); rfkill_led_trigger_event(rfkill); return blocked; } EXPORT_SYMBOL(rfkill_set_sw_state); void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked) { unsigned long flags; BUG_ON(!rfkill); BUG_ON(rfkill->registered); spin_lock_irqsave(&rfkill->lock, flags); __rfkill_set_sw_state(rfkill, blocked); rfkill->persistent = true; spin_unlock_irqrestore(&rfkill->lock, flags); } EXPORT_SYMBOL(rfkill_init_sw_state); void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) { unsigned long flags; bool swprev, hwprev; BUG_ON(!rfkill); spin_lock_irqsave(&rfkill->lock, flags); /* * No need to care about prev/setblock ... this is for uevent only * and that will get triggered by rfkill_set_block anyway. */ swprev = !!(rfkill->state & RFKILL_BLOCK_SW); hwprev = !!(rfkill->state & RFKILL_BLOCK_HW); __rfkill_set_sw_state(rfkill, sw); if (hw) rfkill->state |= RFKILL_BLOCK_HW; else rfkill->state &= ~RFKILL_BLOCK_HW; spin_unlock_irqrestore(&rfkill->lock, flags); if (!rfkill->registered) { rfkill->persistent = true; } else { if (swprev != sw || hwprev != hw) schedule_work(&rfkill->uevent_work); rfkill_led_trigger_event(rfkill); } } EXPORT_SYMBOL(rfkill_set_states); static ssize_t rfkill_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); return sprintf(buf, "%s\n", rfkill->name); } static const char *rfkill_get_type_str(enum rfkill_type type) { BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_FM + 1); switch (type) { case RFKILL_TYPE_WLAN: return "wlan"; case RFKILL_TYPE_BLUETOOTH: return "bluetooth"; case RFKILL_TYPE_UWB: return "ultrawideband"; case RFKILL_TYPE_WIMAX: return "wimax"; case RFKILL_TYPE_WWAN: return "wwan"; case RFKILL_TYPE_GPS: return "gps"; case RFKILL_TYPE_FM: return "fm"; default: BUG(); } } static ssize_t rfkill_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); } static ssize_t rfkill_idx_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); return sprintf(buf, "%d\n", rfkill->idx); } static ssize_t rfkill_persistent_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); return sprintf(buf, "%d\n", rfkill->persistent); } static ssize_t rfkill_hard_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); return sprintf(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_HW) ? 1 : 0 ); } static ssize_t rfkill_soft_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); return sprintf(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_SW) ? 1 : 0 ); } static ssize_t rfkill_soft_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct rfkill *rfkill = to_rfkill(dev); unsigned long state; int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; err = kstrtoul(buf, 0, &state); if (err) return err; if (state > 1 ) return -EINVAL; mutex_lock(&rfkill_global_mutex); rfkill_set_block(rfkill, state); mutex_unlock(&rfkill_global_mutex); return err ?: count; } static u8 user_state_from_blocked(unsigned long state) { if (state & RFKILL_BLOCK_HW) return RFKILL_USER_STATE_HARD_BLOCKED; if (state & RFKILL_BLOCK_SW) return RFKILL_USER_STATE_SOFT_BLOCKED; return RFKILL_USER_STATE_UNBLOCKED; } static ssize_t rfkill_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); return sprintf(buf, "%d\n", user_state_from_blocked(rfkill->state)); } static ssize_t rfkill_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct rfkill *rfkill = to_rfkill(dev); unsigned long state; int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; err = kstrtoul(buf, 0, &state); if (err) return err; if (state != RFKILL_USER_STATE_SOFT_BLOCKED && state != RFKILL_USER_STATE_UNBLOCKED) return -EINVAL; mutex_lock(&rfkill_global_mutex); rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED); mutex_unlock(&rfkill_global_mutex); return err ?: count; } static ssize_t rfkill_claim_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", 0); } static ssize_t rfkill_claim_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return -EOPNOTSUPP; } static struct device_attribute rfkill_dev_attrs[] = { __ATTR(name, S_IRUGO, rfkill_name_show, NULL), __ATTR(type, S_IRUGO, rfkill_type_show, NULL), __ATTR(index, S_IRUGO, rfkill_idx_show, NULL), __ATTR(persistent, S_IRUGO, rfkill_persistent_show, NULL), __ATTR(state, S_IRUGO|S_IWUSR, rfkill_state_show, rfkill_state_store), __ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store), __ATTR(soft, S_IRUGO|S_IWUSR, rfkill_soft_show, rfkill_soft_store), __ATTR(hard, S_IRUGO, rfkill_hard_show, NULL), __ATTR_NULL }; static void rfkill_release(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); kfree(rfkill); } static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { struct rfkill *rfkill = to_rfkill(dev); unsigned long flags; u32 state; int error; error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); if (error) return error; error = add_uevent_var(env, "RFKILL_TYPE=%s", rfkill_get_type_str(rfkill->type)); if (error) return error; spin_lock_irqsave(&rfkill->lock, flags); state = rfkill->state; spin_unlock_irqrestore(&rfkill->lock, flags); error = add_uevent_var(env, "RFKILL_STATE=%d", user_state_from_blocked(state)); return error; } void rfkill_pause_polling(struct rfkill *rfkill) { BUG_ON(!rfkill); if (!rfkill->ops->poll) return; cancel_delayed_work_sync(&rfkill->poll_work); } EXPORT_SYMBOL(rfkill_pause_polling); void rfkill_resume_polling(struct rfkill *rfkill) { BUG_ON(!rfkill); if (!rfkill->ops->poll) return; schedule_work(&rfkill->poll_work.work); } EXPORT_SYMBOL(rfkill_resume_polling); static int rfkill_suspend(struct device *dev, pm_message_t state) { struct rfkill *rfkill = to_rfkill(dev); rfkill_pause_polling(rfkill); return 0; } static int rfkill_resume(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); bool cur; if (!rfkill->persistent) { cur = !!(rfkill->state & RFKILL_BLOCK_SW); rfkill_set_block(rfkill, cur); } rfkill_resume_polling(rfkill); return 0; } static struct class rfkill_class = { .name = "rfkill_backport", .dev_release = rfkill_release, .dev_attrs = rfkill_dev_attrs, .dev_uevent = rfkill_dev_uevent, .suspend = rfkill_suspend, .resume = rfkill_resume, }; bool rfkill_blocked(struct rfkill *rfkill) { unsigned long flags; u32 state; spin_lock_irqsave(&rfkill->lock, flags); state = rfkill->state; spin_unlock_irqrestore(&rfkill->lock, flags); return !!(state & RFKILL_BLOCK_ANY); } EXPORT_SYMBOL(rfkill_blocked); struct rfkill * __must_check rfkill_alloc(const char *name, struct device *parent, const enum rfkill_type type, const struct rfkill_ops *ops, void *ops_data) { struct rfkill *rfkill; struct device *dev; if (WARN_ON(!ops)) return NULL; if (WARN_ON(!ops->set_block)) return NULL; if (WARN_ON(!name)) return NULL; if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES)) return NULL; rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); if (!rfkill) return NULL; spin_lock_init(&rfkill->lock); INIT_LIST_HEAD(&rfkill->node); rfkill->type = type; rfkill->name = name; rfkill->ops = ops; rfkill->data = ops_data; dev = &rfkill->dev; dev->class = &rfkill_class; dev->parent = parent; device_initialize(dev); return rfkill; } EXPORT_SYMBOL(rfkill_alloc); static void rfkill_poll(struct work_struct *work) { struct rfkill *rfkill; rfkill = container_of(work, struct rfkill, poll_work.work); /* * Poll hardware state -- driver will use one of the * rfkill_set{,_hw,_sw}_state functions and use its * return value to update the current status. */ rfkill->ops->poll(rfkill, rfkill->data); schedule_delayed_work(&rfkill->poll_work, round_jiffies_relative(POLL_INTERVAL)); } static void rfkill_uevent_work(struct work_struct *work) { struct rfkill *rfkill; rfkill = container_of(work, struct rfkill, uevent_work); mutex_lock(&rfkill_global_mutex); rfkill_event(rfkill); mutex_unlock(&rfkill_global_mutex); } static void rfkill_sync_work(struct work_struct *work) { struct rfkill *rfkill; bool cur; rfkill = container_of(work, struct rfkill, sync_work); mutex_lock(&rfkill_global_mutex); cur = rfkill_global_states[rfkill->type].cur; rfkill_set_block(rfkill, cur); mutex_unlock(&rfkill_global_mutex); } int __must_check rfkill_register(struct rfkill *rfkill) { static unsigned long rfkill_no; struct device *dev = &rfkill->dev; int error; BUG_ON(!rfkill); mutex_lock(&rfkill_global_mutex); if (rfkill->registered) { error = -EALREADY; goto unlock; } rfkill->idx = rfkill_no; dev_set_name(dev, "rfkill%lu", rfkill_no); rfkill_no++; list_add_tail(&rfkill->node, &rfkill_list); error = device_add(dev); if (error) goto remove; error = rfkill_led_trigger_register(rfkill); if (error) goto devdel; rfkill->registered = true; INIT_DELAYED_WORK(&rfkill->poll_work, rfkill_poll); INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work); INIT_WORK(&rfkill->sync_work, rfkill_sync_work); if (rfkill->ops->poll) schedule_delayed_work(&rfkill->poll_work, round_jiffies_relative(POLL_INTERVAL)); if (!rfkill->persistent || rfkill_epo_lock_active) { schedule_work(&rfkill->sync_work); } else { #ifdef CONFIG_RFKILL_INPUT bool soft_blocked = !!(rfkill->state & RFKILL_BLOCK_SW); if (!atomic_read(&rfkill_input_disabled)) __rfkill_switch_all(rfkill->type, soft_blocked); #endif } rfkill_send_events(rfkill, RFKILL_OP_ADD); mutex_unlock(&rfkill_global_mutex); return 0; devdel: device_del(&rfkill->dev); remove: list_del_init(&rfkill->node); unlock: mutex_unlock(&rfkill_global_mutex); return error; } EXPORT_SYMBOL(rfkill_register); void rfkill_unregister(struct rfkill *rfkill) { BUG_ON(!rfkill); if (rfkill->ops->poll) cancel_delayed_work_sync(&rfkill->poll_work); cancel_work_sync(&rfkill->uevent_work); cancel_work_sync(&rfkill->sync_work); rfkill->registered = false; device_del(&rfkill->dev); mutex_lock(&rfkill_global_mutex); rfkill_send_events(rfkill, RFKILL_OP_DEL); list_del_init(&rfkill->node); mutex_unlock(&rfkill_global_mutex); rfkill_led_trigger_unregister(rfkill); } EXPORT_SYMBOL(rfkill_unregister); void rfkill_destroy(struct rfkill *rfkill) { if (rfkill) put_device(&rfkill->dev); } EXPORT_SYMBOL(rfkill_destroy); static int rfkill_fop_open(struct inode *inode, struct file *file) { struct rfkill_data *data; struct rfkill *rfkill; struct rfkill_int_event *ev, *tmp; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; INIT_LIST_HEAD(&data->events); mutex_init(&data->mtx); init_waitqueue_head(&data->read_wait); mutex_lock(&rfkill_global_mutex); mutex_lock(&data->mtx); /* * start getting events from elsewhere but hold mtx to get * startup events added first */ list_for_each_entry(rfkill, &rfkill_list, node) { ev = kzalloc(sizeof(*ev), GFP_KERNEL); if (!ev) goto free; rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD); list_add_tail(&ev->list, &data->events); } list_add(&data->list, &rfkill_fds); mutex_unlock(&data->mtx); mutex_unlock(&rfkill_global_mutex); file->private_data = data; return nonseekable_open(inode, file); free: mutex_unlock(&data->mtx); mutex_unlock(&rfkill_global_mutex); mutex_destroy(&data->mtx); list_for_each_entry_safe(ev, tmp, &data->events, list) kfree(ev); kfree(data); return -ENOMEM; } static unsigned int rfkill_fop_poll(struct file *file, poll_table *wait) { struct rfkill_data *data = file->private_data; unsigned int res = POLLOUT | POLLWRNORM; poll_wait(file, &data->read_wait, wait); mutex_lock(&data->mtx); if (!list_empty(&data->events)) res = POLLIN | POLLRDNORM; mutex_unlock(&data->mtx); return res; } static bool rfkill_readable(struct rfkill_data *data) { bool r; mutex_lock(&data->mtx); r = !list_empty(&data->events); mutex_unlock(&data->mtx); return r; } static ssize_t rfkill_fop_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { struct rfkill_data *data = file->private_data; struct rfkill_int_event *ev; unsigned long sz; int ret; mutex_lock(&data->mtx); while (list_empty(&data->events)) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; } mutex_unlock(&data->mtx); ret = wait_event_interruptible(data->read_wait, rfkill_readable(data)); mutex_lock(&data->mtx); if (ret) goto out; } ev = list_first_entry(&data->events, struct rfkill_int_event, list); sz = min_t(unsigned long, sizeof(ev->ev), count); ret = sz; if (copy_to_user(buf, &ev->ev, sz)) ret = -EFAULT; list_del(&ev->list); kfree(ev); out: mutex_unlock(&data->mtx); return ret; } static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { struct rfkill *rfkill; struct rfkill_event ev; /* we don't need the 'hard' variable but accept it */ if (count < RFKILL_EVENT_SIZE_V1 - 1) return -EINVAL; /* * Copy as much data as we can accept into our 'ev' buffer, * but tell userspace how much we've copied so it can determine * our API version even in a write() call, if it cares. */ count = min(count, sizeof(ev)); if (copy_from_user(&ev, buf, count)) return -EFAULT; if (ev.op != RFKILL_OP_CHANGE && ev.op != RFKILL_OP_CHANGE_ALL) return -EINVAL; if (ev.type >= NUM_RFKILL_TYPES) return -EINVAL; mutex_lock(&rfkill_global_mutex); if (ev.op == RFKILL_OP_CHANGE_ALL) { if (ev.type == RFKILL_TYPE_ALL) { enum rfkill_type i; for (i = 0; i < NUM_RFKILL_TYPES; i++) rfkill_global_states[i].cur = ev.soft; } else { rfkill_global_states[ev.type].cur = ev.soft; } } list_for_each_entry(rfkill, &rfkill_list, node) { if (rfkill->idx != ev.idx && ev.op != RFKILL_OP_CHANGE_ALL) continue; if (rfkill->type != ev.type && ev.type != RFKILL_TYPE_ALL) continue; rfkill_set_block(rfkill, ev.soft); } mutex_unlock(&rfkill_global_mutex); return count; } static int rfkill_fop_release(struct inode *inode, struct file *file) { struct rfkill_data *data = file->private_data; struct rfkill_int_event *ev, *tmp; mutex_lock(&rfkill_global_mutex); list_del(&data->list); mutex_unlock(&rfkill_global_mutex); mutex_destroy(&data->mtx); list_for_each_entry_safe(ev, tmp, &data->events, list) kfree(ev); #ifdef CONFIG_RFKILL_INPUT if (data->input_handler) if (atomic_dec_return(&rfkill_input_disabled) == 0) printk(KERN_DEBUG "rfkill: input handler enabled\n"); #endif kfree(data); return 0; } #ifdef CONFIG_RFKILL_INPUT static long rfkill_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct rfkill_data *data = file->private_data; if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC) return -ENOSYS; if (_IOC_NR(cmd) != RFKILL_IOC_NOINPUT) return -ENOSYS; mutex_lock(&data->mtx); if (!data->input_handler) { if (atomic_inc_return(&rfkill_input_disabled) == 1) printk(KERN_DEBUG "rfkill: input handler disabled\n"); data->input_handler = true; } mutex_unlock(&data->mtx); return 0; } #endif static const struct file_operations rfkill_fops = { .owner = THIS_MODULE, .open = rfkill_fop_open, .read = rfkill_fop_read, .write = rfkill_fop_write, .poll = rfkill_fop_poll, .release = rfkill_fop_release, #ifdef CONFIG_RFKILL_INPUT .unlocked_ioctl = rfkill_fop_ioctl, .compat_ioctl = rfkill_fop_ioctl, #endif .llseek = no_llseek, }; static struct miscdevice rfkill_miscdev = { .name = "rfkill", .fops = &rfkill_fops, .minor = MISC_DYNAMIC_MINOR, }; static int __init rfkill_init(void) { int error; int i; for (i = 0; i < NUM_RFKILL_TYPES; i++) rfkill_global_states[i].cur = !rfkill_default_state; error = class_register(&rfkill_class); if (error) goto out; error = misc_register(&rfkill_miscdev); if (error) { class_unregister(&rfkill_class); goto out; } #ifdef CONFIG_RFKILL_INPUT error = rfkill_handler_init(); if (error) { misc_deregister(&rfkill_miscdev); class_unregister(&rfkill_class); goto out; } #endif out: return error; } subsys_initcall(rfkill_init); static void __exit rfkill_exit(void) { #ifdef CONFIG_RFKILL_INPUT rfkill_handler_exit(); #endif misc_deregister(&rfkill_miscdev); class_unregister(&rfkill_class); } module_exit(rfkill_exit); compat-drivers-2012-09-18/net/rfkill/input.c0000644000175000017500000002147712026211315017747 0ustar mcgrofmcgrof/* * Input layer to RF Kill interface connector * * Copyright (c) 2007 Dmitry Torokhov * Copyright 2009 Johannes Berg * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If you ever run into a situation in which you have a SW_ type rfkill * input device, then you can revive code that was removed in the patch * "rfkill-input: remove unused code". */ #include #include #include #include #include #include #include #include "rfkill.h" enum rfkill_input_master_mode { RFKILL_INPUT_MASTER_UNLOCK = 0, RFKILL_INPUT_MASTER_RESTORE = 1, RFKILL_INPUT_MASTER_UNBLOCKALL = 2, NUM_RFKILL_INPUT_MASTER_MODES }; /* Delay (in ms) between consecutive switch ops */ #define RFKILL_OPS_DELAY 200 static enum rfkill_input_master_mode rfkill_master_switch_mode = RFKILL_INPUT_MASTER_UNBLOCKALL; module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0); MODULE_PARM_DESC(master_switch_mode, "SW_RFKILL_ALL ON should: 0=do nothing (only unlock); 1=restore; 2=unblock all"); static spinlock_t rfkill_op_lock; static bool rfkill_op_pending; static unsigned long rfkill_sw_pending[BITS_TO_LONGS(NUM_RFKILL_TYPES)]; static unsigned long rfkill_sw_state[BITS_TO_LONGS(NUM_RFKILL_TYPES)]; enum rfkill_sched_op { RFKILL_GLOBAL_OP_EPO = 0, RFKILL_GLOBAL_OP_RESTORE, RFKILL_GLOBAL_OP_UNLOCK, RFKILL_GLOBAL_OP_UNBLOCK, }; static enum rfkill_sched_op rfkill_master_switch_op; static enum rfkill_sched_op rfkill_op; static void __rfkill_handle_global_op(enum rfkill_sched_op op) { unsigned int i; switch (op) { case RFKILL_GLOBAL_OP_EPO: rfkill_epo(); break; case RFKILL_GLOBAL_OP_RESTORE: rfkill_restore_states(); break; case RFKILL_GLOBAL_OP_UNLOCK: rfkill_remove_epo_lock(); break; case RFKILL_GLOBAL_OP_UNBLOCK: rfkill_remove_epo_lock(); for (i = 0; i < NUM_RFKILL_TYPES; i++) rfkill_switch_all(i, false); break; default: /* memory corruption or bug, fail safely */ rfkill_epo(); WARN(1, "Unknown requested operation %d! " "rfkill Emergency Power Off activated\n", op); } } static void __rfkill_handle_normal_op(const enum rfkill_type type, const bool complement) { bool blocked; blocked = rfkill_get_global_sw_state(type); if (complement) blocked = !blocked; rfkill_switch_all(type, blocked); } static void rfkill_op_handler(struct work_struct *work) { unsigned int i; bool c; spin_lock_irq(&rfkill_op_lock); do { if (rfkill_op_pending) { enum rfkill_sched_op op = rfkill_op; rfkill_op_pending = false; memset(rfkill_sw_pending, 0, sizeof(rfkill_sw_pending)); spin_unlock_irq(&rfkill_op_lock); __rfkill_handle_global_op(op); spin_lock_irq(&rfkill_op_lock); /* * handle global ops first -- during unlocked period * we might have gotten a new global op. */ if (rfkill_op_pending) continue; } if (rfkill_is_epo_lock_active()) continue; for (i = 0; i < NUM_RFKILL_TYPES; i++) { if (__test_and_clear_bit(i, rfkill_sw_pending)) { c = __test_and_clear_bit(i, rfkill_sw_state); spin_unlock_irq(&rfkill_op_lock); __rfkill_handle_normal_op(i, c); spin_lock_irq(&rfkill_op_lock); } } } while (rfkill_op_pending); spin_unlock_irq(&rfkill_op_lock); } static DECLARE_DELAYED_WORK(rfkill_op_work, rfkill_op_handler); static unsigned long rfkill_last_scheduled; static unsigned long rfkill_ratelimit(const unsigned long last) { const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY); return time_after(jiffies, last + delay) ? 0 : delay; } static void rfkill_schedule_ratelimited(void) { if (delayed_work_pending(&rfkill_op_work)) return; schedule_delayed_work(&rfkill_op_work, rfkill_ratelimit(rfkill_last_scheduled)); rfkill_last_scheduled = jiffies; } static void rfkill_schedule_global_op(enum rfkill_sched_op op) { unsigned long flags; spin_lock_irqsave(&rfkill_op_lock, flags); rfkill_op = op; rfkill_op_pending = true; if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) { /* bypass the limiter for EPO */ mod_delayed_work(system_wq, &rfkill_op_work, 0); rfkill_last_scheduled = jiffies; } else rfkill_schedule_ratelimited(); spin_unlock_irqrestore(&rfkill_op_lock, flags); } static void rfkill_schedule_toggle(enum rfkill_type type) { unsigned long flags; if (rfkill_is_epo_lock_active()) return; spin_lock_irqsave(&rfkill_op_lock, flags); if (!rfkill_op_pending) { __set_bit(type, rfkill_sw_pending); __change_bit(type, rfkill_sw_state); rfkill_schedule_ratelimited(); } spin_unlock_irqrestore(&rfkill_op_lock, flags); } static void rfkill_schedule_evsw_rfkillall(int state) { if (state) rfkill_schedule_global_op(rfkill_master_switch_op); else rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO); } static void rfkill_event(struct input_handle *handle, unsigned int type, unsigned int code, int data) { if (type == EV_KEY && data == 1) { switch (code) { case KEY_WLAN: rfkill_schedule_toggle(RFKILL_TYPE_WLAN); break; case KEY_BLUETOOTH: rfkill_schedule_toggle(RFKILL_TYPE_BLUETOOTH); break; case KEY_UWB: rfkill_schedule_toggle(RFKILL_TYPE_UWB); break; case KEY_WIMAX: rfkill_schedule_toggle(RFKILL_TYPE_WIMAX); break; case KEY_RFKILL: rfkill_schedule_toggle(RFKILL_TYPE_ALL); break; } } else if (type == EV_SW && code == SW_RFKILL_ALL) rfkill_schedule_evsw_rfkillall(data); } static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct input_handle *handle; int error; handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); if (!handle) return -ENOMEM; handle->dev = dev; handle->handler = handler; handle->name = "rfkill_backport"; /* causes rfkill_start() to be called */ error = input_register_handle(handle); if (error) goto err_free_handle; error = input_open_device(handle); if (error) goto err_unregister_handle; return 0; err_unregister_handle: input_unregister_handle(handle); err_free_handle: kfree(handle); return error; } static void rfkill_start(struct input_handle *handle) { /* * Take event_lock to guard against configuration changes, we * should be able to deal with concurrency with rfkill_event() * just fine (which event_lock will also avoid). */ spin_lock_irq(&handle->dev->event_lock); if (test_bit(EV_SW, handle->dev->evbit) && test_bit(SW_RFKILL_ALL, handle->dev->swbit)) rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL, handle->dev->sw)); spin_unlock_irq(&handle->dev->event_lock); } static void rfkill_disconnect(struct input_handle *handle) { input_close_device(handle); input_unregister_handle(handle); kfree(handle); } static const struct input_device_id rfkill_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .evbit = { BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) }, }, { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .evbit = { BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) }, }, { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .evbit = { BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) }, }, { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .evbit = { BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) }, }, { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, .evbit = { BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(KEY_RFKILL)] = BIT_MASK(KEY_RFKILL) }, }, { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT, .evbit = { BIT(EV_SW) }, .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) }, }, { } }; static struct input_handler rfkill_handler = { .name = "rfkill", .event = rfkill_event, .connect = rfkill_connect, .start = rfkill_start, .disconnect = rfkill_disconnect, .id_table = rfkill_ids, }; int __init rfkill_handler_init(void) { switch (rfkill_master_switch_mode) { case RFKILL_INPUT_MASTER_UNBLOCKALL: rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNBLOCK; break; case RFKILL_INPUT_MASTER_RESTORE: rfkill_master_switch_op = RFKILL_GLOBAL_OP_RESTORE; break; case RFKILL_INPUT_MASTER_UNLOCK: rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNLOCK; break; default: return -EINVAL; } spin_lock_init(&rfkill_op_lock); /* Avoid delay at first schedule */ rfkill_last_scheduled = jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1; return input_register_handler(&rfkill_handler); } void __exit rfkill_handler_exit(void) { input_unregister_handler(&rfkill_handler); cancel_delayed_work_sync(&rfkill_op_work); } compat-drivers-2012-09-18/net/rfkill/Makefile0000644000175000017500000000043212026211315020070 0ustar mcgrofmcgrof# # Makefile for the RF switch subsystem. # rfkill_backport-y += core.o rfkill_backport-$(CONFIG_RFKILL_BACKPORT_INPUT) += input.o obj-$(CONFIG_RFKILL_BACKPORT) += rfkill_backport.o obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o compat-drivers-2012-09-18/net/rfkill/rfkill-gpio.c0000644000175000017500000001402612026211315021017 0ustar mcgrofmcgrof/* * Copyright (c) 2011, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include enum rfkill_gpio_clk_state { UNSPECIFIED = 0, PWR_ENABLED, PWR_DISABLED }; #define PWR_CLK_SET(_RF, _EN) \ ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED)) #define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED) #define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED) struct rfkill_gpio_data { struct rfkill_gpio_platform_data *pdata; struct rfkill *rfkill_dev; char *reset_name; char *shutdown_name; enum rfkill_gpio_clk_state pwr_clk_enabled; struct clk *pwr_clk; }; static int rfkill_gpio_set_power(void *data, bool blocked) { struct rfkill_gpio_data *rfkill = data; if (blocked) { if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) gpio_direction_output(rfkill->pdata->shutdown_gpio, 0); if (gpio_is_valid(rfkill->pdata->reset_gpio)) gpio_direction_output(rfkill->pdata->reset_gpio, 0); if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) clk_disable(rfkill->pwr_clk); } else { if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill)) clk_enable(rfkill->pwr_clk); if (gpio_is_valid(rfkill->pdata->reset_gpio)) gpio_direction_output(rfkill->pdata->reset_gpio, 1); if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) gpio_direction_output(rfkill->pdata->shutdown_gpio, 1); } if (rfkill->pwr_clk) PWR_CLK_SET(rfkill, blocked); return 0; } static const struct rfkill_ops rfkill_gpio_ops = { .set_block = rfkill_gpio_set_power, }; static int rfkill_gpio_probe(struct platform_device *pdev) { struct rfkill_gpio_data *rfkill; struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; int ret = 0; int len = 0; if (!pdata) { pr_warn("%s: No platform data specified\n", __func__); return -EINVAL; } /* make sure at-least one of the GPIO is defined and that * a name is specified for this instance */ if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) && !gpio_is_valid(pdata->shutdown_gpio))) { pr_warn("%s: invalid platform data\n", __func__); return -EINVAL; } rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); if (!rfkill) return -ENOMEM; if (pdata->gpio_runtime_setup) { ret = pdata->gpio_runtime_setup(pdev); if (ret) { pr_warn("%s: can't set up gpio\n", __func__); goto fail_alloc; } } rfkill->pdata = pdata; len = strlen(pdata->name); rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL); if (!rfkill->reset_name) { ret = -ENOMEM; goto fail_alloc; } rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL); if (!rfkill->shutdown_name) { ret = -ENOMEM; goto fail_reset_name; } snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name); snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name); if (pdata->power_clk_name) { rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name); if (IS_ERR(rfkill->pwr_clk)) { pr_warn("%s: can't find pwr_clk.\n", __func__); goto fail_shutdown_name; } } if (gpio_is_valid(pdata->reset_gpio)) { ret = gpio_request(pdata->reset_gpio, rfkill->reset_name); if (ret) { pr_warn("%s: failed to get reset gpio.\n", __func__); goto fail_clock; } } if (gpio_is_valid(pdata->shutdown_gpio)) { ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name); if (ret) { pr_warn("%s: failed to get shutdown gpio.\n", __func__); goto fail_reset; } } rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, &rfkill_gpio_ops, rfkill); if (!rfkill->rfkill_dev) goto fail_shutdown; ret = rfkill_register(rfkill->rfkill_dev); if (ret < 0) goto fail_rfkill; platform_set_drvdata(pdev, rfkill); dev_info(&pdev->dev, "%s device registered.\n", pdata->name); return 0; fail_rfkill: rfkill_destroy(rfkill->rfkill_dev); fail_shutdown: if (gpio_is_valid(pdata->shutdown_gpio)) gpio_free(pdata->shutdown_gpio); fail_reset: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); fail_clock: if (rfkill->pwr_clk) clk_put(rfkill->pwr_clk); fail_shutdown_name: kfree(rfkill->shutdown_name); fail_reset_name: kfree(rfkill->reset_name); fail_alloc: kfree(rfkill); return ret; } static int rfkill_gpio_remove(struct platform_device *pdev) { struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; if (pdata->gpio_runtime_close) pdata->gpio_runtime_close(pdev); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) gpio_free(rfkill->pdata->shutdown_gpio); if (gpio_is_valid(rfkill->pdata->reset_gpio)) gpio_free(rfkill->pdata->reset_gpio); if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) clk_disable(rfkill->pwr_clk); if (rfkill->pwr_clk) clk_put(rfkill->pwr_clk); kfree(rfkill->shutdown_name); kfree(rfkill->reset_name); kfree(rfkill); return 0; } static struct platform_driver rfkill_gpio_driver = { .probe = rfkill_gpio_probe, .remove = __devexit_p(rfkill_gpio_remove), .driver = { .name = "rfkill_gpio", .owner = THIS_MODULE, }, }; module_platform_driver(rfkill_gpio_driver); MODULE_DESCRIPTION("gpio rfkill"); MODULE_AUTHOR("NVIDIA"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/net/rfkill/rfkill-regulator.c0000644000175000017500000000726612026211315022075 0ustar mcgrofmcgrof/* * rfkill-regulator.c - Regulator consumer driver for rfkill * * Copyright (C) 2009 Guiming Zhuo * Copyright (C) 2011 Antonio Ospite * * Implementation inspired by leds-regulator driver. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include struct rfkill_regulator_data { struct rfkill *rf_kill; bool reg_enabled; struct regulator *vcc; }; static int rfkill_regulator_set_block(void *data, bool blocked) { struct rfkill_regulator_data *rfkill_data = data; pr_debug("%s: blocked: %d\n", __func__, blocked); if (blocked) { if (rfkill_data->reg_enabled) { regulator_disable(rfkill_data->vcc); rfkill_data->reg_enabled = false; } } else { if (!rfkill_data->reg_enabled) { regulator_enable(rfkill_data->vcc); rfkill_data->reg_enabled = true; } } pr_debug("%s: regulator_is_enabled after set_block: %d\n", __func__, regulator_is_enabled(rfkill_data->vcc)); return 0; } struct rfkill_ops rfkill_regulator_ops = { .set_block = rfkill_regulator_set_block, }; static int __devinit rfkill_regulator_probe(struct platform_device *pdev) { struct rfkill_regulator_platform_data *pdata = pdev->dev.platform_data; struct rfkill_regulator_data *rfkill_data; struct regulator *vcc; struct rfkill *rf_kill; int ret = 0; if (pdata == NULL) { dev_err(&pdev->dev, "no platform data\n"); return -ENODEV; } if (pdata->name == NULL || pdata->type == 0) { dev_err(&pdev->dev, "invalid name or type in platform data\n"); return -EINVAL; } vcc = regulator_get_exclusive(&pdev->dev, "vrfkill"); if (IS_ERR(vcc)) { dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); ret = PTR_ERR(vcc); goto out; } rfkill_data = kzalloc(sizeof(*rfkill_data), GFP_KERNEL); if (rfkill_data == NULL) { ret = -ENOMEM; goto err_data_alloc; } rf_kill = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, &rfkill_regulator_ops, rfkill_data); if (rf_kill == NULL) { ret = -ENOMEM; goto err_rfkill_alloc; } if (regulator_is_enabled(vcc)) { dev_dbg(&pdev->dev, "Regulator already enabled\n"); rfkill_data->reg_enabled = true; } rfkill_data->vcc = vcc; rfkill_data->rf_kill = rf_kill; ret = rfkill_register(rf_kill); if (ret) { dev_err(&pdev->dev, "Cannot register rfkill device\n"); goto err_rfkill_register; } platform_set_drvdata(pdev, rfkill_data); dev_info(&pdev->dev, "%s initialized\n", pdata->name); return 0; err_rfkill_register: rfkill_destroy(rf_kill); err_rfkill_alloc: kfree(rfkill_data); err_data_alloc: regulator_put(vcc); out: return ret; } static int __devexit rfkill_regulator_remove(struct platform_device *pdev) { struct rfkill_regulator_data *rfkill_data = platform_get_drvdata(pdev); struct rfkill *rf_kill = rfkill_data->rf_kill; rfkill_unregister(rf_kill); rfkill_destroy(rf_kill); regulator_put(rfkill_data->vcc); kfree(rfkill_data); return 0; } static struct platform_driver rfkill_regulator_driver = { .probe = rfkill_regulator_probe, .remove = __devexit_p(rfkill_regulator_remove), .driver = { .name = "rfkill-regulator", .owner = THIS_MODULE, }, }; module_platform_driver(rfkill_regulator_driver); MODULE_AUTHOR("Guiming Zhuo "); MODULE_AUTHOR("Antonio Ospite "); MODULE_DESCRIPTION("Regulator consumer driver for rfkill"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:rfkill-regulator"); compat-drivers-2012-09-18/net/rfkill/rfkill.h0000644000175000017500000000134412026211315020067 0ustar mcgrofmcgrof/* * Copyright (C) 2007 Ivo van Doorn * Copyright 2009 Johannes Berg */ /* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. */ #ifndef __RFKILL_INPUT_H #define __RFKILL_INPUT_H /* core code */ void rfkill_switch_all(const enum rfkill_type type, bool blocked); void rfkill_epo(void); void rfkill_restore_states(void); void rfkill_remove_epo_lock(void); bool rfkill_is_epo_lock_active(void); bool rfkill_get_global_sw_state(const enum rfkill_type type); /* input handler */ int rfkill_handler_init(void); void rfkill_handler_exit(void); #endif /* __RFKILL_INPUT_H */ compat-drivers-2012-09-18/net/rfkill/Kconfig0000644000175000017500000000235712026211315017743 0ustar mcgrofmcgrof# # RF switch subsystem configuration # menuconfig RFKILL tristate "RF switch subsystem support" help Say Y here if you want to have control over RF switches found on many WiFi and Bluetooth cards. To compile this driver as a module, choose M here: the module will be called rfkill. # LED trigger support config RFKILL_LEDS bool depends on RFKILL depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS default y config RFKILL_INPUT bool "RF switch input support" if EXPERT depends on RFKILL depends on INPUT = y || RFKILL = INPUT default y if !EXPERT config RFKILL_REGULATOR tristate "Generic rfkill regulator driver" depends on RFKILL || !RFKILL depends on REGULATOR help This options enable controlling radio transmitters connected to voltage regulator using the regulator framework. To compile this driver as a module, choose M here: the module will be called rfkill-regulator. config RFKILL_GPIO tristate "GPIO RFKILL driver" depends on RFKILL && GPIOLIB && HAVE_CLK default n help If you say yes here you get support of a generic gpio RFKILL driver. The platform should fill in the appropriate fields in the rfkill_gpio_platform_data structure and pass that to the driver. compat-drivers-2012-09-18/net/wireless/0000755000175000017500000000000012026211316017004 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/wireless/Makefile0000644000175000017500000000127112026211315020444 0ustar mcgrofmcgrofobj-$(CONFIG_CFG80211) += cfg80211.o obj-$(CONFIG_LIB80211) += lib80211.o obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o ccflags-y += -D__CHECK_ENDIAN__ $(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk @$(AWK) -f $(src)/genregdb.awk < $< > $@ clean-files := regdb.c compat-drivers-2012-09-18/net/wireless/lib80211_crypt_tkip.c0000644000175000017500000005067312026211315022574 0ustar mcgrofmcgrof/* * lib80211 crypt: host-based TKIP encryption implementation for lib80211 * * Copyright (c) 2003-2004, Jouni Malinen * Copyright (c) 2008, John W. Linville * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Jouni Malinen"); MODULE_DESCRIPTION("lib80211 crypt: TKIP"); MODULE_LICENSE("GPL"); #define TKIP_HDR_LEN 8 struct lib80211_tkip_data { #define TKIP_KEY_LEN 32 u8 key[TKIP_KEY_LEN]; int key_set; u32 tx_iv32; u16 tx_iv16; u16 tx_ttak[5]; int tx_phase1_done; u32 rx_iv32; u16 rx_iv16; u16 rx_ttak[5]; int rx_phase1_done; u32 rx_iv32_new; u16 rx_iv16_new; u32 dot11RSNAStatsTKIPReplays; u32 dot11RSNAStatsTKIPICVErrors; u32 dot11RSNAStatsTKIPLocalMICFailures; int key_idx; struct crypto_blkcipher *rx_tfm_arc4; struct crypto_hash *rx_tfm_michael; struct crypto_blkcipher *tx_tfm_arc4; struct crypto_hash *tx_tfm_michael; /* scratch buffers for virt_to_page() (crypto API) */ u8 rx_hdr[16], tx_hdr[16]; unsigned long flags; }; static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv) { struct lib80211_tkip_data *_priv = priv; unsigned long old_flags = _priv->flags; _priv->flags = flags; return old_flags; } static unsigned long lib80211_tkip_get_flags(void *priv) { struct lib80211_tkip_data *_priv = priv; return _priv->flags; } static void *lib80211_tkip_init(int key_idx) { struct lib80211_tkip_data *priv; priv = kzalloc(sizeof(*priv), GFP_ATOMIC); if (priv == NULL) goto fail; priv->key_idx = key_idx; priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_arc4)) { priv->tx_tfm_arc4 = NULL; goto fail; } priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_michael)) { priv->tx_tfm_michael = NULL; goto fail; } priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_arc4)) { priv->rx_tfm_arc4 = NULL; goto fail; } priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_michael)) { priv->rx_tfm_michael = NULL; goto fail; } return priv; fail: if (priv) { if (priv->tx_tfm_michael) crypto_free_hash(priv->tx_tfm_michael); if (priv->tx_tfm_arc4) crypto_free_blkcipher(priv->tx_tfm_arc4); if (priv->rx_tfm_michael) crypto_free_hash(priv->rx_tfm_michael); if (priv->rx_tfm_arc4) crypto_free_blkcipher(priv->rx_tfm_arc4); kfree(priv); } return NULL; } static void lib80211_tkip_deinit(void *priv) { struct lib80211_tkip_data *_priv = priv; if (_priv) { if (_priv->tx_tfm_michael) crypto_free_hash(_priv->tx_tfm_michael); if (_priv->tx_tfm_arc4) crypto_free_blkcipher(_priv->tx_tfm_arc4); if (_priv->rx_tfm_michael) crypto_free_hash(_priv->rx_tfm_michael); if (_priv->rx_tfm_arc4) crypto_free_blkcipher(_priv->rx_tfm_arc4); } kfree(priv); } static inline u16 RotR1(u16 val) { return (val >> 1) | (val << 15); } static inline u8 Lo8(u16 val) { return val & 0xff; } static inline u8 Hi8(u16 val) { return val >> 8; } static inline u16 Lo16(u32 val) { return val & 0xffff; } static inline u16 Hi16(u32 val) { return val >> 16; } static inline u16 Mk16(u8 hi, u8 lo) { return lo | (((u16) hi) << 8); } static inline u16 Mk16_le(__le16 * v) { return le16_to_cpu(*v); } static const u16 Sbox[256] = { 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, }; static inline u16 _S_(u16 v) { u16 t = Sbox[Hi8(v)]; return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); } #define PHASE1_LOOP_COUNT 8 static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA, u32 IV32) { int i, j; /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ TTAK[0] = Lo16(IV32); TTAK[1] = Hi16(IV32); TTAK[2] = Mk16(TA[1], TA[0]); TTAK[3] = Mk16(TA[3], TA[2]); TTAK[4] = Mk16(TA[5], TA[4]); for (i = 0; i < PHASE1_LOOP_COUNT; i++) { j = 2 * (i & 1); TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; } } static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK, u16 IV16) { /* Make temporary area overlap WEP seed so that the final copy can be * avoided on little endian hosts. */ u16 *PPK = (u16 *) & WEPSeed[4]; /* Step 1 - make copy of TTAK and bring in TSC */ PPK[0] = TTAK[0]; PPK[1] = TTAK[1]; PPK[2] = TTAK[2]; PPK[3] = TTAK[3]; PPK[4] = TTAK[4]; PPK[5] = TTAK[4] + IV16; /* Step 2 - 96-bit bijective mixing using S-box */ PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0])); PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2])); PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4])); PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6])); PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8])); PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10])); PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12])); PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14])); PPK[2] += RotR1(PPK[1]); PPK[3] += RotR1(PPK[2]); PPK[4] += RotR1(PPK[3]); PPK[5] += RotR1(PPK[4]); /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value * WEPSeed[0..2] is transmitted as WEP IV */ WEPSeed[0] = Hi8(IV16); WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; WEPSeed[2] = Lo8(IV16); WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1); #ifdef __BIG_ENDIAN { int i; for (i = 0; i < 6; i++) PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); } #endif } static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, u8 * rc4key, int keylen, void *priv) { struct lib80211_tkip_data *tkey = priv; u8 *pos; struct ieee80211_hdr *hdr; hdr = (struct ieee80211_hdr *)skb->data; if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len) return -1; if (rc4key == NULL || keylen < 16) return -1; if (!tkey->tx_phase1_done) { tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, tkey->tx_iv32); tkey->tx_phase1_done = 1; } tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); pos = skb_push(skb, TKIP_HDR_LEN); memmove(pos, pos + TKIP_HDR_LEN, hdr_len); pos += hdr_len; *pos++ = *rc4key; *pos++ = *(rc4key + 1); *pos++ = *(rc4key + 2); *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ; *pos++ = tkey->tx_iv32 & 0xff; *pos++ = (tkey->tx_iv32 >> 8) & 0xff; *pos++ = (tkey->tx_iv32 >> 16) & 0xff; *pos++ = (tkey->tx_iv32 >> 24) & 0xff; tkey->tx_iv16++; if (tkey->tx_iv16 == 0) { tkey->tx_phase1_done = 0; tkey->tx_iv32++; } return TKIP_HDR_LEN; } static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_tkip_data *tkey = priv; struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 }; int len; u8 rc4key[16], *pos, *icv; u32 crc; struct scatterlist sg; if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; net_dbg_ratelimited("TKIP countermeasures: dropped TX packet to %pM\n", hdr->addr1); return -1; } if (skb_tailroom(skb) < 4 || skb->len < hdr_len) return -1; len = skb->len - hdr_len; pos = skb->data + hdr_len; if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) return -1; crc = ~crc32_le(~0, pos, len); icv = skb_put(skb, 4); icv[0] = crc; icv[1] = crc >> 8; icv[2] = crc >> 16; icv[3] = crc >> 24; crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); sg_init_one(&sg, pos, len + 4); return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); } /* * deal with seq counter wrapping correctly. * refer to timer_after() for jiffies wrapping handling */ static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n, u32 iv32_o, u16 iv16_o) { if ((s32)iv32_n - (s32)iv32_o < 0 || (iv32_n == iv32_o && iv16_n <= iv16_o)) return 1; return 0; } static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_tkip_data *tkey = priv; struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 }; u8 rc4key[16]; u8 keyidx, *pos; u32 iv32; u16 iv16; struct ieee80211_hdr *hdr; u8 icv[4]; u32 crc; struct scatterlist sg; int plen; hdr = (struct ieee80211_hdr *)skb->data; if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { net_dbg_ratelimited("TKIP countermeasures: dropped received packet from %pM\n", hdr->addr2); return -1; } if (skb->len < hdr_len + TKIP_HDR_LEN + 4) return -1; pos = skb->data + hdr_len; keyidx = pos[3]; if (!(keyidx & (1 << 5))) { net_dbg_ratelimited("TKIP: received packet without ExtIV flag from %pM\n", hdr->addr2); return -2; } keyidx >>= 6; if (tkey->key_idx != keyidx) { printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); return -6; } if (!tkey->key_set) { net_dbg_ratelimited("TKIP: received packet from %pM with keyid=%d that does not have a configured key\n", hdr->addr2, keyidx); return -3; } iv16 = (pos[0] << 8) | pos[2]; iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); pos += TKIP_HDR_LEN; if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { #ifdef CONFIG_LIB80211_DEBUG net_dbg_ratelimited("TKIP: replay detected: STA=%pM previous TSC %08x%04x received TSC %08x%04x\n", hdr->addr2, tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); #endif tkey->dot11RSNAStatsTKIPReplays++; return -4; } if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); tkey->rx_phase1_done = 1; } tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); plen = skb->len - hdr_len - 12; crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); sg_init_one(&sg, pos, plen + 4); if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) { net_dbg_ratelimited("TKIP: failed to decrypt received packet from %pM\n", hdr->addr2); return -7; } crc = ~crc32_le(~0, pos, plen); icv[0] = crc; icv[1] = crc >> 8; icv[2] = crc >> 16; icv[3] = crc >> 24; if (memcmp(icv, pos + plen, 4) != 0) { if (iv32 != tkey->rx_iv32) { /* Previously cached Phase1 result was already lost, so * it needs to be recalculated for the next packet. */ tkey->rx_phase1_done = 0; } #ifdef CONFIG_LIB80211_DEBUG net_dbg_ratelimited("TKIP: ICV error detected: STA=%pM\n", hdr->addr2); #endif tkey->dot11RSNAStatsTKIPICVErrors++; return -5; } /* Update real counters only after Michael MIC verification has * completed */ tkey->rx_iv32_new = iv32; tkey->rx_iv16_new = iv16; /* Remove IV and ICV */ memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len); skb_pull(skb, TKIP_HDR_LEN); skb_trim(skb, skb->len - 4); return keyidx; } static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, u8 * data, size_t data_len, u8 * mic) { struct hash_desc desc; struct scatterlist sg[2]; if (tfm_michael == NULL) { pr_warn("%s(): tfm_michael == NULL\n", __func__); return -1; } sg_init_table(sg, 2); sg_set_buf(&sg[0], hdr, 16); sg_set_buf(&sg[1], data, data_len); if (crypto_hash_setkey(tfm_michael, key, 8)) return -1; desc.tfm = tfm_michael; desc.flags = 0; return crypto_hash_digest(&desc, sg, data_len + 16, mic); } static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) { struct ieee80211_hdr *hdr11; hdr11 = (struct ieee80211_hdr *)skb->data; switch (le16_to_cpu(hdr11->frame_control) & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { case IEEE80211_FCTL_TODS: memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ break; case IEEE80211_FCTL_FROMDS: memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ break; case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ break; case 0: memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ break; } if (ieee80211_is_data_qos(hdr11->frame_control)) { hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11))) & IEEE80211_QOS_CTL_TID_MASK; } else hdr[12] = 0; /* priority */ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ } static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_tkip_data *tkey = priv; u8 *pos; if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { printk(KERN_DEBUG "Invalid packet for Michael MIC add " "(tailroom=%d hdr_len=%d skb->len=%d)\n", skb_tailroom(skb), hdr_len, skb->len); return -1; } michael_mic_hdr(skb, tkey->tx_hdr); pos = skb_put(skb, 8); if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) return -1; return 0; } static void lib80211_michael_mic_failure(struct net_device *dev, struct ieee80211_hdr *hdr, int keyidx) { union iwreq_data wrqu; struct iw_michaelmicfailure ev; /* TODO: needed parameters: count, keyid, key type, TSC */ memset(&ev, 0, sizeof(ev)); ev.flags = keyidx & IW_MICFAILURE_KEY_ID; if (hdr->addr1[0] & 0x01) ev.flags |= IW_MICFAILURE_GROUP; else ev.flags |= IW_MICFAILURE_PAIRWISE; ev.src_addr.sa_family = ARPHRD_ETHER; memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN); memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = sizeof(ev); wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev); } static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx, int hdr_len, void *priv) { struct lib80211_tkip_data *tkey = priv; u8 mic[8]; if (!tkey->key_set) return -1; michael_mic_hdr(skb, tkey->rx_hdr); if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) return -1; if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { struct ieee80211_hdr *hdr; hdr = (struct ieee80211_hdr *)skb->data; printk(KERN_DEBUG "%s: Michael MIC verification failed for " "MSDU from %pM keyidx=%d\n", skb->dev ? skb->dev->name : "N/A", hdr->addr2, keyidx); if (skb->dev) lib80211_michael_mic_failure(skb->dev, hdr, keyidx); tkey->dot11RSNAStatsTKIPLocalMICFailures++; return -1; } /* Update TSC counters for RX now that the packet verification has * completed. */ tkey->rx_iv32 = tkey->rx_iv32_new; tkey->rx_iv16 = tkey->rx_iv16_new; skb_trim(skb, skb->len - 8); return 0; } static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv) { struct lib80211_tkip_data *tkey = priv; int keyidx; struct crypto_hash *tfm = tkey->tx_tfm_michael; struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4; struct crypto_hash *tfm3 = tkey->rx_tfm_michael; struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4; keyidx = tkey->key_idx; memset(tkey, 0, sizeof(*tkey)); tkey->key_idx = keyidx; tkey->tx_tfm_michael = tfm; tkey->tx_tfm_arc4 = tfm2; tkey->rx_tfm_michael = tfm3; tkey->rx_tfm_arc4 = tfm4; if (len == TKIP_KEY_LEN) { memcpy(tkey->key, key, TKIP_KEY_LEN); tkey->key_set = 1; tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ if (seq) { tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | (seq[3] << 8) | seq[2]; tkey->rx_iv16 = (seq[1] << 8) | seq[0]; } } else if (len == 0) tkey->key_set = 0; else return -1; return 0; } static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv) { struct lib80211_tkip_data *tkey = priv; if (len < TKIP_KEY_LEN) return -1; if (!tkey->key_set) return 0; memcpy(key, tkey->key, TKIP_KEY_LEN); if (seq) { /* Return the sequence number of the last transmitted frame. */ u16 iv16 = tkey->tx_iv16; u32 iv32 = tkey->tx_iv32; if (iv16 == 0) iv32--; iv16--; seq[0] = tkey->tx_iv16; seq[1] = tkey->tx_iv16 >> 8; seq[2] = tkey->tx_iv32; seq[3] = tkey->tx_iv32 >> 8; seq[4] = tkey->tx_iv32 >> 16; seq[5] = tkey->tx_iv32 >> 24; } return TKIP_KEY_LEN; } static char *lib80211_tkip_print_stats(char *p, void *priv) { struct lib80211_tkip_data *tkip = priv; p += sprintf(p, "key[%d] alg=TKIP key_set=%d " "tx_pn=%02x%02x%02x%02x%02x%02x " "rx_pn=%02x%02x%02x%02x%02x%02x " "replays=%d icv_errors=%d local_mic_failures=%d\n", tkip->key_idx, tkip->key_set, (tkip->tx_iv32 >> 24) & 0xff, (tkip->tx_iv32 >> 16) & 0xff, (tkip->tx_iv32 >> 8) & 0xff, tkip->tx_iv32 & 0xff, (tkip->tx_iv16 >> 8) & 0xff, tkip->tx_iv16 & 0xff, (tkip->rx_iv32 >> 24) & 0xff, (tkip->rx_iv32 >> 16) & 0xff, (tkip->rx_iv32 >> 8) & 0xff, tkip->rx_iv32 & 0xff, (tkip->rx_iv16 >> 8) & 0xff, tkip->rx_iv16 & 0xff, tkip->dot11RSNAStatsTKIPReplays, tkip->dot11RSNAStatsTKIPICVErrors, tkip->dot11RSNAStatsTKIPLocalMICFailures); return p; } static struct lib80211_crypto_ops lib80211_crypt_tkip = { .name = "TKIP", .init = lib80211_tkip_init, .deinit = lib80211_tkip_deinit, .encrypt_mpdu = lib80211_tkip_encrypt, .decrypt_mpdu = lib80211_tkip_decrypt, .encrypt_msdu = lib80211_michael_mic_add, .decrypt_msdu = lib80211_michael_mic_verify, .set_key = lib80211_tkip_set_key, .get_key = lib80211_tkip_get_key, .print_stats = lib80211_tkip_print_stats, .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */ .extra_mpdu_postfix_len = 4, /* ICV */ .extra_msdu_postfix_len = 8, /* MIC */ .get_flags = lib80211_tkip_get_flags, .set_flags = lib80211_tkip_set_flags, .owner = THIS_MODULE, }; static int __init lib80211_crypto_tkip_init(void) { return lib80211_register_crypto_ops(&lib80211_crypt_tkip); } static void __exit lib80211_crypto_tkip_exit(void) { lib80211_unregister_crypto_ops(&lib80211_crypt_tkip); } module_init(lib80211_crypto_tkip_init); module_exit(lib80211_crypto_tkip_exit); compat-drivers-2012-09-18/net/wireless/core.c0000644000175000017500000007021412026211315020103 0ustar mcgrofmcgrof/* * This is the linux wireless configuration interface. * * Copyright 2006-2010 Johannes Berg */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nl80211.h" #include "core.h" #include "sysfs.h" #include "debugfs.h" #include "wext-compat.h" #include "ethtool.h" /* name for sysfs, %d is appended */ #define PHY_NAME "phy" MODULE_AUTHOR("Johannes Berg"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("wireless configuration support"); /* RCU-protected (and cfg80211_mutex for writers) */ LIST_HEAD(cfg80211_rdev_list); int cfg80211_rdev_list_generation; DEFINE_MUTEX(cfg80211_mutex); /* for debugfs */ static struct dentry *ieee80211_debugfs_dir; /* for the cleanup, scan and event works */ struct workqueue_struct *cfg80211_wq; static bool cfg80211_disable_40mhz_24ghz; module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, "Disable 40MHz support in the 2.4GHz band"); /* requires cfg80211_mutex to be held! */ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { struct cfg80211_registered_device *result = NULL, *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (rdev->wiphy_idx == wiphy_idx) { result = rdev; break; } } return result; } int get_wiphy_idx(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev; if (!wiphy) return WIPHY_IDX_STALE; rdev = wiphy_to_dev(wiphy); return rdev->wiphy_idx; } /* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) { struct cfg80211_registered_device *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); if (!rdev) return NULL; return &rdev->wiphy; } struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) { struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); struct net_device *dev; mutex_lock(&cfg80211_mutex); dev = dev_get_by_index(net, ifindex); if (!dev) goto out; if (dev->ieee80211_ptr) { rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); mutex_lock(&rdev->mtx); } else rdev = ERR_PTR(-ENODEV); dev_put(dev); out: mutex_unlock(&cfg80211_mutex); return rdev; } /* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { struct cfg80211_registered_device *rdev2; int wiphy_idx, taken = -1, result, digits; assert_cfg80211_lock(); /* prohibit calling the thing phy%d when %d is not its number */ sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { /* count number of places needed to print wiphy_idx */ digits = 1; while (wiphy_idx /= 10) digits++; /* * deny the name if it is phy where is printed * without leading zeroes. taken == strlen(newname) here */ if (taken == strlen(PHY_NAME) + digits) return -EINVAL; } /* Ignore nop renames */ if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) return 0; /* Ensure another device does not already have this name. */ list_for_each_entry(rdev2, &cfg80211_rdev_list, list) if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) return -EINVAL; result = device_rename(&rdev->wiphy.dev, newname); if (result) return result; if (rdev->wiphy.debugfsdir && !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, rdev->wiphy.debugfsdir, rdev->wiphy.debugfsdir->d_parent, newname)) pr_err("failed to rename debugfs dir to %s!\n", newname); nl80211_notify_dev_rename(rdev); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, struct net *net) { struct wireless_dev *wdev; int err = 0; if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) return -EOPNOTSUPP; list_for_each_entry(wdev, &rdev->wdev_list, list) { if (!wdev->netdev) continue; wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); if (err) break; wdev->netdev->features |= NETIF_F_NETNS_LOCAL; } if (err) { /* failed -- clean up to old netns */ net = wiphy_net(&rdev->wiphy); list_for_each_entry_continue_reverse(wdev, &rdev->wdev_list, list) { if (!wdev->netdev) continue; wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); WARN_ON(err); wdev->netdev->features |= NETIF_F_NETNS_LOCAL; } return err; } wiphy_net_set(&rdev->wiphy, net); err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); WARN_ON(err); return 0; } #endif static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { struct cfg80211_registered_device *rdev = data; rdev->ops->rfkill_poll(&rdev->wiphy); } static int cfg80211_rfkill_set_block(void *data, bool blocked) { struct cfg80211_registered_device *rdev = data; struct wireless_dev *wdev; if (!blocked) return 0; rtnl_lock(); mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->netdev) { dev_close(wdev->netdev); continue; } /* otherwise, check iftype */ switch (wdev->iftype) { case NL80211_IFTYPE_P2P_DEVICE: if (!wdev->p2p_started) break; rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); wdev->p2p_started = false; rdev->opencount--; break; default: break; } } mutex_unlock(&rdev->devlist_mtx); rtnl_unlock(); return 0; } static void cfg80211_rfkill_sync_work(struct work_struct *work) { struct cfg80211_registered_device *rdev; rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); } static void cfg80211_event_work(struct work_struct *work) { struct cfg80211_registered_device *rdev; rdev = container_of(work, struct cfg80211_registered_device, event_work); rtnl_lock(); cfg80211_lock_rdev(rdev); cfg80211_process_rdev_events(rdev); cfg80211_unlock_rdev(rdev); rtnl_unlock(); } /* exported functions */ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { static int wiphy_counter; struct cfg80211_registered_device *rdev; int alloc_size; /* * Make sure the padding is >= the rest of the struct so that we * always keep it large enough to pad out the entire original * kernel's struct. We really only need to make sure it's larger * than the kernel compat is compiled against, but since it'll * only increase in size make sure it's larger than the current * version of it. Subtract since it's included. */ BUILD_BUG_ON(WIPHY_COMPAT_PAD_SIZE < sizeof(struct wiphy) - WIPHY_COMPAT_PAD_SIZE); WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); WARN_ON(ops->connect && !ops->disconnect); WARN_ON(ops->join_ibss && !ops->leave_ibss); WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); WARN_ON(ops->add_station && !ops->del_station); WARN_ON(ops->add_mpath && !ops->del_mpath); WARN_ON(ops->join_mesh && !ops->leave_mesh); alloc_size = sizeof(*rdev) + sizeof_priv; rdev = kzalloc(alloc_size, GFP_KERNEL); if (!rdev) return NULL; rdev->ops = ops; mutex_lock(&cfg80211_mutex); rdev->wiphy_idx = wiphy_counter++; if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { wiphy_counter--; mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ kfree(rdev); return NULL; } mutex_unlock(&cfg80211_mutex); /* give it a proper name */ dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); mutex_init(&rdev->mtx); mutex_init(&rdev->devlist_mtx); mutex_init(&rdev->sched_scan_mtx); INIT_LIST_HEAD(&rdev->wdev_list); spin_lock_init(&rdev->bss_lock); INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); device_initialize(&rdev->wiphy.dev); rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) wiphy_net_set(&rdev->wiphy, &init_net); #endif rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), &rdev->wiphy.dev, RFKILL_TYPE_WLAN, &rdev->rfkill_ops, rdev); if (!rdev->rfkill) { kfree(rdev); return NULL; } INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); INIT_WORK(&rdev->conn_work, cfg80211_conn_work); INIT_WORK(&rdev->event_work, cfg80211_event_work); init_waitqueue_head(&rdev->dev_wait); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. * Fragmentation and RTS threshold are disabled by default with the * special -1 value. */ rdev->wiphy.retry_short = 7; rdev->wiphy.retry_long = 4; rdev->wiphy.frag_threshold = (u32) -1; rdev->wiphy.rts_threshold = (u32) -1; rdev->wiphy.coverage_class = 0; return &rdev->wiphy; } EXPORT_SYMBOL(wiphy_new); static int wiphy_verify_combinations(struct wiphy *wiphy) { const struct ieee80211_iface_combination *c; int i, j; for (i = 0; i < wiphy->n_iface_combinations; i++) { u32 cnt = 0; u16 all_iftypes = 0; c = &wiphy->iface_combinations[i]; /* Combinations with just one interface aren't real */ if (WARN_ON(c->max_interfaces < 2)) return -EINVAL; /* Need at least one channel */ if (WARN_ON(!c->num_different_channels)) return -EINVAL; /* * Put a sane limit on maximum number of different * channels to simplify channel accounting code. */ if (WARN_ON(c->num_different_channels > CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) return -EINVAL; if (WARN_ON(!c->n_limits)) return -EINVAL; for (j = 0; j < c->n_limits; j++) { u16 types = c->limits[j].types; /* * interface types shouldn't overlap, this is * used in cfg80211_can_change_interface() */ if (WARN_ON(types & all_iftypes)) return -EINVAL; all_iftypes |= types; if (WARN_ON(!c->limits[j].max)) return -EINVAL; /* Shouldn't list software iftypes in combinations! */ if (WARN_ON(wiphy->software_iftypes & types)) return -EINVAL; /* Only a single P2P_DEVICE can be allowed */ if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && c->limits[j].max > 1)) return -EINVAL; cnt += c->limits[j].max; /* * Don't advertise an unsupported type * in a combination. */ if (WARN_ON((wiphy->interface_modes & types) != types)) return -EINVAL; } /* You can't even choose that many! */ if (WARN_ON(cnt < c->max_interfaces)) return -EINVAL; } return 0; } int wiphy_register(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); int res; enum ieee80211_band band; struct ieee80211_supported_band *sband; bool have_band = false; int i; u16 ifmodes = wiphy->interface_modes; #ifdef CONFIG_PM if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) return -EINVAL; #endif if (WARN_ON(wiphy->ap_sme_capa && !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) return -EINVAL; if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) return -EINVAL; if (WARN_ON(wiphy->addresses && !is_zero_ether_addr(wiphy->perm_addr) && memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN))) return -EINVAL; if (wiphy->addresses) memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); /* sanity check ifmodes */ WARN_ON(!ifmodes); ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1; if (WARN_ON(ifmodes != wiphy->interface_modes)) wiphy->interface_modes = ifmodes; res = wiphy_verify_combinations(wiphy); if (res) return res; /* sanity check supported bands/channels */ for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; if (!sband) continue; sband->band = band; if (WARN_ON(!sband->n_channels)) return -EINVAL; /* * on 60gHz band, there are no legacy rates, so * n_bitrates is 0 */ if (WARN_ON(band != IEEE80211_BAND_60GHZ && !sband->n_bitrates)) return -EINVAL; /* * Since cfg80211_disable_40mhz_24ghz is global, we can * modify the sband's ht data even if the driver uses a * global structure for that. */ if (cfg80211_disable_40mhz_24ghz && band == IEEE80211_BAND_2GHZ && sband->ht_cap.ht_supported) { sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; } /* * Since we use a u32 for rate bitmaps in * ieee80211_get_response_rate, we cannot * have more than 32 legacy rates. */ if (WARN_ON(sband->n_bitrates > 32)) return -EINVAL; for (i = 0; i < sband->n_channels; i++) { sband->channels[i].orig_flags = sband->channels[i].flags; sband->channels[i].orig_mag = sband->channels[i].max_antenna_gain; sband->channels[i].orig_mpwr = sband->channels[i].max_power; sband->channels[i].band = band; } have_band = true; } if (!have_band) { WARN_ON(1); return -EINVAL; } #ifdef CONFIG_PM if (rdev->wiphy.wowlan.n_patterns) { if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || rdev->wiphy.wowlan.pattern_min_len > rdev->wiphy.wowlan.pattern_max_len)) return -EINVAL; } #endif /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); mutex_lock(&cfg80211_mutex); res = device_add(&rdev->wiphy.dev); if (res) { mutex_unlock(&cfg80211_mutex); return res; } /* set up regulatory info */ wiphy_regulatory_register(wiphy); list_add_rcu(&rdev->list, &cfg80211_rdev_list); cfg80211_rdev_list_generation++; /* add to debugfs */ rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy), ieee80211_debugfs_dir); if (IS_ERR(rdev->wiphy.debugfsdir)) rdev->wiphy.debugfsdir = NULL; if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { struct regulatory_request request; request.wiphy_idx = get_wiphy_idx(wiphy); request.initiator = NL80211_REGDOM_SET_BY_DRIVER; request.alpha2[0] = '9'; request.alpha2[1] = '9'; nl80211_send_reg_change_event(&request); } cfg80211_debugfs_rdev_add(rdev); mutex_unlock(&cfg80211_mutex); /* * due to a locking dependency this has to be outside of the * cfg80211_mutex lock */ res = rfkill_register(rdev->rfkill); if (res) goto out_rm_dev; rtnl_lock(); rdev->wiphy.registered = true; rtnl_unlock(); return 0; out_rm_dev: device_del(&rdev->wiphy.dev); return res; } EXPORT_SYMBOL(wiphy_register); void wiphy_rfkill_start_polling(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); if (!rdev->ops->rfkill_poll) return; rdev->rfkill_ops.poll = cfg80211_rfkill_poll; rfkill_resume_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_start_polling); void wiphy_rfkill_stop_polling(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); rfkill_pause_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_stop_polling); void wiphy_unregister(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); rtnl_lock(); rdev->wiphy.registered = false; rtnl_unlock(); rfkill_unregister(rdev->rfkill); /* protect the device list */ mutex_lock(&cfg80211_mutex); wait_event(rdev->dev_wait, ({ int __count; mutex_lock(&rdev->devlist_mtx); __count = rdev->opencount; mutex_unlock(&rdev->devlist_mtx); __count == 0; })); mutex_lock(&rdev->devlist_mtx); BUG_ON(!list_empty(&rdev->wdev_list)); mutex_unlock(&rdev->devlist_mtx); /* * First remove the hardware from everywhere, this makes * it impossible to find from userspace. */ debugfs_remove_recursive(rdev->wiphy.debugfsdir); list_del_rcu(&rdev->list); synchronize_rcu(); /* * Try to grab rdev->mtx. If a command is still in progress, * hopefully the driver will refuse it since it's tearing * down the device already. We wait for this command to complete * before unlinking the item from the list. * Note: as codified by the BUG_ON above we cannot get here if * a virtual interface is still present. Hence, we can only get * to lock contention here if userspace issues a command that * identified the hardware by wiphy index. */ cfg80211_lock_rdev(rdev); /* nothing */ cfg80211_unlock_rdev(rdev); /* * If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ wiphy_regulatory_deregister(wiphy); cfg80211_rdev_list_generation++; device_del(&rdev->wiphy.dev); mutex_unlock(&cfg80211_mutex); flush_work(&rdev->scan_done_wk); cancel_work_sync(&rdev->conn_work); flush_work(&rdev->event_work); if (rdev->wowlan && rdev->ops->set_wakeup) rdev->ops->set_wakeup(&rdev->wiphy, false); cfg80211_rdev_free_wowlan(rdev); } EXPORT_SYMBOL(wiphy_unregister); void cfg80211_dev_free(struct cfg80211_registered_device *rdev) { struct cfg80211_internal_bss *scan, *tmp; rfkill_destroy(rdev->rfkill); mutex_destroy(&rdev->mtx); mutex_destroy(&rdev->devlist_mtx); mutex_destroy(&rdev->sched_scan_mtx); list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) cfg80211_put_bss(&scan->pub); kfree(rdev); } void wiphy_free(struct wiphy *wiphy) { put_device(&wiphy->dev); } EXPORT_SYMBOL(wiphy_free); void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); if (rfkill_set_hw_state(rdev->rfkill, blocked)) schedule_work(&rdev->rfkill_sync); } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); static void wdev_cleanup_work(struct work_struct *work) { struct wireless_dev *wdev; struct cfg80211_registered_device *rdev; wdev = container_of(work, struct wireless_dev, cleanup_work); rdev = wiphy_to_dev(wdev->wiphy); cfg80211_lock_rdev(rdev); if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) { rdev->scan_req->aborted = true; ___cfg80211_scan_done(rdev, true); } cfg80211_unlock_rdev(rdev); mutex_lock(&rdev->sched_scan_mtx); if (WARN_ON(rdev->sched_scan_req && rdev->sched_scan_req->dev == wdev->netdev)) { __cfg80211_stop_sched_scan(rdev, false); } mutex_unlock(&rdev->sched_scan_mtx); mutex_lock(&rdev->devlist_mtx); rdev->opencount--; mutex_unlock(&rdev->devlist_mtx); wake_up(&rdev->dev_wait); dev_put(wdev->netdev); } void cfg80211_unregister_wdev(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); ASSERT_RTNL(); if (WARN_ON(wdev->netdev)) return; mutex_lock(&rdev->devlist_mtx); list_del_rcu(&wdev->list); rdev->devlist_generation++; switch (wdev->iftype) { case NL80211_IFTYPE_P2P_DEVICE: if (!wdev->p2p_started) break; rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); wdev->p2p_started = false; rdev->opencount--; break; default: WARN_ON_ONCE(1); break; } mutex_unlock(&rdev->devlist_mtx); } EXPORT_SYMBOL(cfg80211_unregister_wdev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type wiphy_type = { .name = "wlan", }; #endif void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num) { ASSERT_RTNL(); rdev->num_running_ifaces += num; if (iftype == NL80211_IFTYPE_MONITOR) rdev->num_running_monitor_ifaces += num; } static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ndev) { struct net_device *dev = ndev; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; int ret; if (!wdev) return NOTIFY_DONE; rdev = wiphy_to_dev(wdev->wiphy); WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); switch (state) { case NETDEV_POST_INIT: SET_NETDEV_DEVTYPE(dev, &wiphy_type); break; case NETDEV_REGISTER: /* * NB: cannot take rdev->mtx here because this may be * called within code protected by it when interfaces * are added with nl80211. */ mutex_init(&wdev->mtx); INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); mutex_lock(&rdev->devlist_mtx); wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wdev_list); rdev->devlist_generation++; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) /* can only change netns with wiphy */ dev->features |= NETIF_F_NETNS_LOCAL; #endif if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { pr_err("failed to add phy80211 symlink to netdev!\n"); } wdev->netdev = dev; wdev->sme_state = CFG80211_SME_IDLE; mutex_unlock(&rdev->devlist_mtx); #ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_WIRELESS_EXT if (!dev->wireless_handlers) dev->wireless_handlers = &cfg80211_wext_handler; #else printk_once(KERN_WARNING "cfg80211: wext will not work because " "kernel was compiled with CONFIG_WIRELESS_EXT=n. " "Tools using wext interface, like iwconfig will " "not work.\n"); #endif wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) wdev->ps = true; else wdev->ps = false; /* allow mac80211 to determine the timeout */ wdev->ps_timeout = -1; if (!dev->ethtool_ops) dev->ethtool_ops = &cfg80211_ethtool_ops; if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) dev->priv_flags |= IFF_DONT_BRIDGE; break; case NETDEV_GOING_DOWN: switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: mutex_lock(&rdev->sched_scan_mtx); __cfg80211_stop_sched_scan(rdev, false); mutex_unlock(&rdev->sched_scan_mtx); wdev_lock(wdev); #ifdef CONFIG_CFG80211_WEXT kfree(wdev->wext.ie); wdev->wext.ie = NULL; wdev->wext.ie_len = 0; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif __cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); cfg80211_mlme_down(rdev, dev); wdev_unlock(wdev); break; case NL80211_IFTYPE_MESH_POINT: cfg80211_leave_mesh(rdev, dev); break; case NL80211_IFTYPE_AP: cfg80211_stop_ap(rdev, dev); break; default: break; } wdev->beacon_interval = 0; break; case NETDEV_DOWN: cfg80211_update_iface_num(rdev, wdev->iftype, -1); dev_hold(dev); queue_work(cfg80211_wq, &wdev->cleanup_work); break; case NETDEV_UP: /* * If we have a really quick DOWN/UP succession we may * have this work still pending ... cancel it and see * if it was pending, in which case we need to account * for some of the work it would have done. */ if (cancel_work_sync(&wdev->cleanup_work)) { mutex_lock(&rdev->devlist_mtx); rdev->opencount--; mutex_unlock(&rdev->devlist_mtx); dev_put(dev); } cfg80211_update_iface_num(rdev, wdev->iftype, 1); cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); switch (wdev->iftype) { #ifdef CONFIG_CFG80211_WEXT case NL80211_IFTYPE_ADHOC: cfg80211_ibss_wext_join(rdev, wdev); break; case NL80211_IFTYPE_STATION: cfg80211_mgd_wext_connect(rdev, wdev); break; #endif #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: { /* backward compat code... */ struct mesh_setup setup; memcpy(&setup, &default_mesh_setup, sizeof(setup)); /* back compat only needed for mesh_id */ setup.mesh_id = wdev->ssid; setup.mesh_id_len = wdev->mesh_id_up_len; if (wdev->mesh_id_up_len) __cfg80211_join_mesh(rdev, dev, &setup, &default_mesh_config); break; } #endif default: break; } wdev_unlock(wdev); rdev->opencount++; mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); /* * Configure power management to the driver here so that its * correctly set also after interface type changes etc. */ if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && rdev->ops->set_power_mgmt) if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, wdev->ps_timeout)) { /* assume this means it's off */ wdev->ps = false; } break; case NETDEV_UNREGISTER: /* * NB: cannot take rdev->mtx here because this may be * called within code protected by it when interfaces * are removed with nl80211. */ mutex_lock(&rdev->devlist_mtx); /* * It is possible to get NETDEV_UNREGISTER * multiple times. To detect that, check * that the interface is still on the list * of registered interfaces, and only then * remove and clean it up. */ if (!list_empty(&wdev->list)) { sysfs_remove_link(&dev->dev.kobj, "phy80211"); list_del_rcu(&wdev->list); rdev->devlist_generation++; cfg80211_mlme_purge_registrations(wdev); #ifdef CONFIG_CFG80211_WEXT kfree(wdev->wext.keys); #endif } mutex_unlock(&rdev->devlist_mtx); /* * synchronise (so that we won't find this netdev * from other code any more) and then clear the list * head so that the above code can safely check for * !list_empty() to avoid double-cleanup. */ synchronize_rcu(); INIT_LIST_HEAD(&wdev->list); /* * Ensure that all events have been processed and * freed. */ cfg80211_process_wdev_events(wdev); break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); mutex_lock(&rdev->devlist_mtx); ret = cfg80211_can_add_interface(rdev, wdev->iftype); mutex_unlock(&rdev->devlist_mtx); if (ret) return notifier_from_errno(ret); break; } return NOTIFY_DONE; } static struct notifier_block cfg80211_netdev_notifier = { .notifier_call = cfg80211_netdev_notifier_call, }; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) static void __net_exit cfg80211_pernet_exit(struct net *net) { struct cfg80211_registered_device *rdev; rtnl_lock(); mutex_lock(&cfg80211_mutex); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (net_eq(wiphy_net(&rdev->wiphy), net)) WARN_ON(cfg80211_switch_netns(rdev, &init_net)); } mutex_unlock(&cfg80211_mutex); rtnl_unlock(); } static struct pernet_operations cfg80211_pernet_ops = { .exit = cfg80211_pernet_exit, }; #endif static int __init cfg80211_init(void) { int err; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) err = register_pernet_device(&cfg80211_pernet_ops); if (err) goto out_fail_pernet; #endif err = wiphy_sysfs_init(); if (err) goto out_fail_sysfs; err = register_netdevice_notifier(&cfg80211_netdev_notifier); if (err) goto out_fail_notifier; err = nl80211_init(); if (err) goto out_fail_nl80211; ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); err = regulatory_init(); if (err) goto out_fail_reg; cfg80211_wq = create_singlethread_workqueue("cfg80211"); if (!cfg80211_wq) goto out_fail_wq; return 0; out_fail_wq: regulatory_exit(); out_fail_reg: debugfs_remove(ieee80211_debugfs_dir); out_fail_nl80211: unregister_netdevice_notifier(&cfg80211_netdev_notifier); out_fail_notifier: wiphy_sysfs_exit(); out_fail_sysfs: #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) unregister_pernet_device(&cfg80211_pernet_ops); out_fail_pernet: #endif return err; } subsys_initcall(cfg80211_init); static void __exit cfg80211_exit(void) { debugfs_remove(ieee80211_debugfs_dir); nl80211_exit(); unregister_netdevice_notifier(&cfg80211_netdev_notifier); wiphy_sysfs_exit(); regulatory_exit(); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) unregister_pernet_device(&cfg80211_pernet_ops); #endif destroy_workqueue(cfg80211_wq); } module_exit(cfg80211_exit); compat-drivers-2012-09-18/net/wireless/reg.c0000644000175000017500000020261712026211315017734 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2007 Johannes Berg * Copyright 2008-2011 Luis R. Rodriguez * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: Wireless regulatory infrastructure * * The usual implementation is for a driver to read a device EEPROM to * determine which regulatory domain it should be operating under, then * looking up the allowable channels in a driver-local table and finally * registering those channels in the wiphy structure. * * Another set of compliance enforcement is for drivers to use their * own compliance limits which can be stored on the EEPROM. The host * driver or firmware may ensure these are used. * * In addition to all this we provide an extra layer of regulatory * conformance. For drivers which do not have any regulatory * information CRDA provides the complete regulatory solution. * For others it provides a community effort on further restrictions * to enhance compliance. * * Note: When number of rules --> infinity we will not be able to * index on alpha2 any more, instead we'll probably have to * rely on some SHA1 checksum of the regdomain for example. * */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "reg.h" #include "regdb.h" #include "nl80211.h" #ifdef CONFIG_CFG80211_REG_DEBUG #define REG_DBG_PRINT(format, args...) \ printk(KERN_DEBUG pr_fmt(format), ##args) #else #define REG_DBG_PRINT(args...) #endif static struct regulatory_request core_request_world = { .initiator = NL80211_REGDOM_SET_BY_CORE, .alpha2[0] = '0', .alpha2[1] = '0', .intersect = false, .processed = true, .country_ie_env = ENVIRON_ANY, }; /* Receipt of information from last regulatory request */ static struct regulatory_request *last_request = &core_request_world; /* To trigger userspace events */ static struct platform_device *reg_pdev; static struct device_type reg_device_type = { .uevent = reg_device_uevent, }; /* * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no * information to give us an alpha2 */ const struct ieee80211_regdomain *cfg80211_regdomain; /* * Protects static reg.c components: * - cfg80211_world_regdom * - cfg80211_regdom * - last_request * - reg_num_devs_support_basehint */ static DEFINE_MUTEX(reg_mutex); /* * Number of devices that registered to the core * that support cellular base station regulatory hints */ static int reg_num_devs_support_basehint; static inline void assert_reg_lock(void) { lockdep_assert_held(®_mutex); } /* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; /* Used to queue up beacon hints for review */ static LIST_HEAD(reg_pending_beacons); static spinlock_t reg_pending_beacons_lock; /* Used to keep track of processed beacon hints */ static LIST_HEAD(reg_beacon_list); struct reg_beacon { struct list_head list; struct ieee80211_channel chan; }; static void reg_todo(struct work_struct *work); static DECLARE_WORK(reg_work, reg_todo); static void reg_timeout_work(struct work_struct *work); static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work); /* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { .n_reg_rules = 6, .alpha2 = "00", .reg_rules = { /* IEEE 802.11b/g, channels 1..11 */ REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), /* IEEE 802.11b/g, channels 12..13. No HT40 * channel fits here. */ REG_RULE(2467-10, 2472+10, 20, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), /* IEEE 802.11 channel 14 - Only JP enables * this and for 802.11b only */ REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | NL80211_RRF_NO_OFDM), /* IEEE 802.11a, channel 36..48 */ REG_RULE(5180-10, 5240+10, 40, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), /* NB: 5260 MHz - 5700 MHz requies DFS */ /* IEEE 802.11a, channel 149..165 */ REG_RULE(5745-10, 5825+10, 40, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), /* IEEE 802.11ad (60gHz), channels 1..3 */ REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0), } }; static const struct ieee80211_regdomain *cfg80211_world_regdom = &world_regdom; static char *ieee80211_regdom = "00"; static char user_alpha2[2]; module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); static void reset_regdomains(bool full_reset) { /* avoid freeing static information or freeing something twice */ if (cfg80211_regdomain == cfg80211_world_regdom) cfg80211_regdomain = NULL; if (cfg80211_world_regdom == &world_regdom) cfg80211_world_regdom = NULL; if (cfg80211_regdomain == &world_regdom) cfg80211_regdomain = NULL; kfree(cfg80211_regdomain); kfree(cfg80211_world_regdom); cfg80211_world_regdom = &world_regdom; cfg80211_regdomain = NULL; if (!full_reset) return; if (last_request != &core_request_world) kfree(last_request); last_request = &core_request_world; } /* * Dynamic world regulatory domain requested by the wireless * core upon initialization */ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { BUG_ON(!last_request); reset_regdomains(false); cfg80211_world_regdom = rd; cfg80211_regdomain = rd; } bool is_world_regdom(const char *alpha2) { if (!alpha2) return false; if (alpha2[0] == '0' && alpha2[1] == '0') return true; return false; } static bool is_alpha2_set(const char *alpha2) { if (!alpha2) return false; if (alpha2[0] != 0 && alpha2[1] != 0) return true; return false; } static bool is_unknown_alpha2(const char *alpha2) { if (!alpha2) return false; /* * Special case where regulatory domain was built by driver * but a specific alpha2 cannot be determined */ if (alpha2[0] == '9' && alpha2[1] == '9') return true; return false; } static bool is_intersected_alpha2(const char *alpha2) { if (!alpha2) return false; /* * Special case where regulatory domain is the * result of an intersection between two regulatory domain * structures */ if (alpha2[0] == '9' && alpha2[1] == '8') return true; return false; } static bool is_an_alpha2(const char *alpha2) { if (!alpha2) return false; if (isalpha(alpha2[0]) && isalpha(alpha2[1])) return true; return false; } static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) { if (!alpha2_x || !alpha2_y) return false; if (alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]) return true; return false; } static bool regdom_changes(const char *alpha2) { assert_cfg80211_lock(); if (!cfg80211_regdomain) return true; if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) return false; return true; } /* * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER * has ever been issued. */ static bool is_user_regdom_saved(void) { if (user_alpha2[0] == '9' && user_alpha2[1] == '7') return false; /* This would indicate a mistake on the design */ if (WARN((!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2)), "Unexpected user alpha2: %c%c\n", user_alpha2[0], user_alpha2[1])) return false; return true; } static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, const struct ieee80211_regdomain *src_regd) { struct ieee80211_regdomain *regd; int size_of_regd = 0; unsigned int i; size_of_regd = sizeof(struct ieee80211_regdomain) + ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) return -ENOMEM; memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); for (i = 0; i < src_regd->n_reg_rules; i++) memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], sizeof(struct ieee80211_reg_rule)); *dst_regd = regd; return 0; } #ifdef CONFIG_CFG80211_INTERNAL_REGDB struct reg_regdb_search_request { char alpha2[2]; struct list_head list; }; static LIST_HEAD(reg_regdb_search_list); static DEFINE_MUTEX(reg_regdb_search_mutex); static void reg_regdb_search(struct work_struct *work) { struct reg_regdb_search_request *request; const struct ieee80211_regdomain *curdom, *regdom; int i, r; mutex_lock(®_regdb_search_mutex); while (!list_empty(®_regdb_search_list)) { request = list_first_entry(®_regdb_search_list, struct reg_regdb_search_request, list); list_del(&request->list); for (i=0; ialpha2, curdom->alpha2, 2)) { r = reg_copy_regd(®dom, curdom); if (r) break; mutex_lock(&cfg80211_mutex); set_regdom(regdom); mutex_unlock(&cfg80211_mutex); break; } } kfree(request); } mutex_unlock(®_regdb_search_mutex); } static DECLARE_WORK(reg_regdb_work, reg_regdb_search); static void reg_regdb_query(const char *alpha2) { struct reg_regdb_search_request *request; if (!alpha2) return; request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); if (!request) return; memcpy(request->alpha2, alpha2, 2); mutex_lock(®_regdb_search_mutex); list_add_tail(&request->list, ®_regdb_search_list); mutex_unlock(®_regdb_search_mutex); schedule_work(®_regdb_work); } /* Feel free to add any other sanity checks here */ static void reg_regdb_size_check(void) { /* We should ideally BUILD_BUG_ON() but then random builds would fail */ WARN_ONCE(!reg_regdb_size, "db.txt is empty, you should update it..."); } #else static inline void reg_regdb_size_check(void) {} static inline void reg_regdb_query(const char *alpha2) {} #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ /* * This lets us keep regulatory code which is updated on a regulatory * basis in userspace. Country information is filled in by * reg_device_uevent */ static int call_crda(const char *alpha2) { if (!is_world_regdom((char *) alpha2)) pr_info("Calling CRDA for country: %c%c\n", alpha2[0], alpha2[1]); else pr_info("Calling CRDA to update world regulatory domain\n"); /* query internal regulatory database (if it exists) */ reg_regdb_query(alpha2); return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); } /* Used by nl80211 before kmalloc'ing our regulatory domain */ bool reg_is_valid_request(const char *alpha2) { assert_cfg80211_lock(); if (!last_request) return false; return alpha2_equal(last_request->alpha2, alpha2); } /* Sanity check on a regulatory rule */ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) { const struct ieee80211_freq_range *freq_range = &rule->freq_range; u32 freq_diff; if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) return false; if (freq_range->start_freq_khz > freq_range->end_freq_khz) return false; freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; if (freq_range->end_freq_khz <= freq_range->start_freq_khz || freq_range->max_bandwidth_khz > freq_diff) return false; return true; } static bool is_valid_rd(const struct ieee80211_regdomain *rd) { const struct ieee80211_reg_rule *reg_rule = NULL; unsigned int i; if (!rd->n_reg_rules) return false; if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) return false; for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; if (!is_valid_reg_rule(reg_rule)) return false; } return true; } static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, u32 center_freq_khz, u32 bw_khz) { u32 start_freq_khz, end_freq_khz; start_freq_khz = center_freq_khz - (bw_khz/2); end_freq_khz = center_freq_khz + (bw_khz/2); if (start_freq_khz >= freq_range->start_freq_khz && end_freq_khz <= freq_range->end_freq_khz) return true; return false; } /** * freq_in_rule_band - tells us if a frequency is in a frequency band * @freq_range: frequency rule we want to query * @freq_khz: frequency we are inquiring about * * This lets us know if a specific frequency rule is or is not relevant to * a specific frequency's band. Bands are device specific and artificial * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is * safe for now to assume that a frequency rule should not be part of a * frequency's band if the start freq or end freq are off by more than 2 GHz. * This resolution can be lowered and should be considered as we add * regulatory rule support for other "bands". **/ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, u32 freq_khz) { #define ONE_GHZ_IN_KHZ 1000000 if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) return true; if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) return true; return false; #undef ONE_GHZ_IN_KHZ } /* * Helper for regdom_intersect(), this does the real * mathematical intersection fun */ static int reg_rules_intersect( const struct ieee80211_reg_rule *rule1, const struct ieee80211_reg_rule *rule2, struct ieee80211_reg_rule *intersected_rule) { const struct ieee80211_freq_range *freq_range1, *freq_range2; struct ieee80211_freq_range *freq_range; const struct ieee80211_power_rule *power_rule1, *power_rule2; struct ieee80211_power_rule *power_rule; u32 freq_diff; freq_range1 = &rule1->freq_range; freq_range2 = &rule2->freq_range; freq_range = &intersected_rule->freq_range; power_rule1 = &rule1->power_rule; power_rule2 = &rule2->power_rule; power_rule = &intersected_rule->power_rule; freq_range->start_freq_khz = max(freq_range1->start_freq_khz, freq_range2->start_freq_khz); freq_range->end_freq_khz = min(freq_range1->end_freq_khz, freq_range2->end_freq_khz); freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, freq_range2->max_bandwidth_khz); freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; if (freq_range->max_bandwidth_khz > freq_diff) freq_range->max_bandwidth_khz = freq_diff; power_rule->max_eirp = min(power_rule1->max_eirp, power_rule2->max_eirp); power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, power_rule2->max_antenna_gain); intersected_rule->flags = (rule1->flags | rule2->flags); if (!is_valid_reg_rule(intersected_rule)) return -EINVAL; return 0; } /** * regdom_intersect - do the intersection between two regulatory domains * @rd1: first regulatory domain * @rd2: second regulatory domain * * Use this function to get the intersection between two regulatory domains. * Once completed we will mark the alpha2 for the rd as intersected, "98", * as no one single alpha2 can represent this regulatory domain. * * Returns a pointer to the regulatory domain structure which will hold the * resulting intersection of rules between rd1 and rd2. We will * kzalloc() this structure for you. */ static struct ieee80211_regdomain *regdom_intersect( const struct ieee80211_regdomain *rd1, const struct ieee80211_regdomain *rd2) { int r, size_of_regd; unsigned int x, y; unsigned int num_rules = 0, rule_idx = 0; const struct ieee80211_reg_rule *rule1, *rule2; struct ieee80211_reg_rule *intersected_rule; struct ieee80211_regdomain *rd; /* This is just a dummy holder to help us count */ struct ieee80211_reg_rule irule; /* Uses the stack temporarily for counter arithmetic */ intersected_rule = &irule; memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); if (!rd1 || !rd2) return NULL; /* * First we get a count of the rules we'll need, then we actually * build them. This is to so we can malloc() and free() a * regdomain once. The reason we use reg_rules_intersect() here * is it will return -EINVAL if the rule computed makes no sense. * All rules that do check out OK are valid. */ for (x = 0; x < rd1->n_reg_rules; x++) { rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) { rule2 = &rd2->reg_rules[y]; if (!reg_rules_intersect(rule1, rule2, intersected_rule)) num_rules++; memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); } } if (!num_rules) return NULL; size_of_regd = sizeof(struct ieee80211_regdomain) + ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) return NULL; for (x = 0; x < rd1->n_reg_rules; x++) { rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) { rule2 = &rd2->reg_rules[y]; /* * This time around instead of using the stack lets * write to the target rule directly saving ourselves * a memcpy() */ intersected_rule = &rd->reg_rules[rule_idx]; r = reg_rules_intersect(rule1, rule2, intersected_rule); /* * No need to memset here the intersected rule here as * we're not using the stack anymore */ if (r) continue; rule_idx++; } } if (rule_idx != num_rules) { kfree(rd); return NULL; } rd->n_reg_rules = num_rules; rd->alpha2[0] = '9'; rd->alpha2[1] = '8'; return rd; } /* * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may * want to just have the channel structure use these */ static u32 map_regdom_flags(u32 rd_flags) { u32 channel_flags = 0; if (rd_flags & NL80211_RRF_PASSIVE_SCAN) channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; if (rd_flags & NL80211_RRF_NO_IBSS) channel_flags |= IEEE80211_CHAN_NO_IBSS; if (rd_flags & NL80211_RRF_DFS) channel_flags |= IEEE80211_CHAN_RADAR; if (rd_flags & NL80211_RRF_NO_OFDM) channel_flags |= IEEE80211_CHAN_NO_OFDM; return channel_flags; } static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule, const struct ieee80211_regdomain *custom_regd) { int i; bool band_rule_found = false; const struct ieee80211_regdomain *regd; bool bw_fits = false; if (!desired_bw_khz) desired_bw_khz = MHZ_TO_KHZ(20); regd = custom_regd ? custom_regd : cfg80211_regdomain; /* * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ if (!custom_regd && last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && last_request->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) regd = wiphy->regd; if (!regd) return -EINVAL; for (i = 0; i < regd->n_reg_rules; i++) { const struct ieee80211_reg_rule *rr; const struct ieee80211_freq_range *fr = NULL; rr = ®d->reg_rules[i]; fr = &rr->freq_range; /* * We only need to know if one frequency rule was * was in center_freq's band, that's enough, so lets * not overwrite it once found */ if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz); if (band_rule_found && bw_fits) { *reg_rule = rr; return 0; } } if (!band_rule_found) return -ERANGE; return -EINVAL; } int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule) { assert_cfg80211_lock(); return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, reg_rule, NULL); } EXPORT_SYMBOL(freq_reg_info); #ifdef CONFIG_CFG80211_REG_DEBUG static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) { switch (initiator) { case NL80211_REGDOM_SET_BY_CORE: return "Set by core"; case NL80211_REGDOM_SET_BY_USER: return "Set by user"; case NL80211_REGDOM_SET_BY_DRIVER: return "Set by driver"; case NL80211_REGDOM_SET_BY_COUNTRY_IE: return "Set by country IE"; default: WARN_ON(1); return "Set by bug"; } } static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { const struct ieee80211_power_rule *power_rule; const struct ieee80211_freq_range *freq_range; char max_antenna_gain[32]; power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; if (!power_rule->max_antenna_gain) snprintf(max_antenna_gain, 32, "N/A"); else snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); REG_DBG_PRINT("Updating information on frequency %d MHz " "for a %d MHz width channel with regulatory rule:\n", chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, max_antenna_gain, power_rule->max_eirp); } #else static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { return; } #endif /* * Note that right now we assume the desired channel bandwidth * is always 20 MHz for each individual channel (HT40 uses 20 MHz * per channel, the primary and the extension channel). To support * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a * new ieee80211_channel.target_bw and re run the regulatory check * on the wiphy with the target_bw specified. Then we can simply use * that below for the desired_bw_khz below. */ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, enum ieee80211_band band, unsigned int chan_idx) { int r; u32 flags, bw_flags = 0; u32 desired_bw_khz = MHZ_TO_KHZ(20); const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; struct wiphy *request_wiphy = NULL; assert_cfg80211_lock(); request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; flags = chan->orig_flags; r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule); if (r) { /* * We will disable all channels that do not match our * received regulatory rule unless the hint is coming * from a Country IE and the Country IE had no information * about a band. The IEEE 802.11 spec allows for an AP * to send only a subset of the regulatory rules allowed, * so an AP in the US that only supports 2.4 GHz may only send * a country IE with information for the 2.4 GHz band * while 5 GHz is still supported. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && r == -ERANGE) return; REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); chan->flags = IEEE80211_CHAN_DISABLED; return; } chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { /* * This guarantees the driver's requested regulatory domain * will always be used as a base for further regulatory * settings */ chan->flags = chan->orig_flags = map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = chan->orig_mag = (int) MBI_TO_DBI(power_rule->max_antenna_gain); chan->max_power = chan->orig_mpwr = (int) MBM_TO_DBM(power_rule->max_eirp); return; } chan->beacon_found = false; chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); chan->max_antenna_gain = min(chan->orig_mag, (int) MBI_TO_DBI(power_rule->max_antenna_gain)); chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); if (chan->orig_mpwr) { /* * Devices that have their own custom regulatory domain * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the * passed country IE power settings. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) chan->max_power = chan->max_reg_power; else chan->max_power = min(chan->orig_mpwr, chan->max_reg_power); } else chan->max_power = chan->max_reg_power; } static void handle_band(struct wiphy *wiphy, enum ieee80211_band band, enum nl80211_reg_initiator initiator) { unsigned int i; struct ieee80211_supported_band *sband; BUG_ON(!wiphy->bands[band]); sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) handle_channel(wiphy, initiator, band, i); } static bool reg_request_cell_base(struct regulatory_request *request) { if (request->initiator != NL80211_REGDOM_SET_BY_USER) return false; if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) return false; return true; } bool reg_last_request_cell_base(void) { bool val; assert_cfg80211_lock(); mutex_lock(®_mutex); val = reg_request_cell_base(last_request); mutex_unlock(®_mutex); return val; } #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS /* Core specific check */ static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { if (!reg_num_devs_support_basehint) return -EOPNOTSUPP; if (reg_request_cell_base(last_request)) { if (!regdom_changes(pending_request->alpha2)) return -EALREADY; return 0; } return 0; } /* Device specific check */ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) { if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) return true; return false; } #else static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { return -EOPNOTSUPP; } static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) { return true; } #endif static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { if (!last_request) { REG_DBG_PRINT("Ignoring regulatory request %s since " "last_request is not set\n", reg_initiator_name(initiator)); return true; } if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { REG_DBG_PRINT("Ignoring regulatory request %s " "since the driver uses its own custom " "regulatory domain\n", reg_initiator_name(initiator)); return true; } /* * wiphy->regd will be set once the device has its own * desired regulatory domain set */ if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && !is_world_regdom(last_request->alpha2)) { REG_DBG_PRINT("Ignoring regulatory request %s " "since the driver requires its own regulatory " "domain to be set first\n", reg_initiator_name(initiator)); return true; } if (reg_request_cell_base(last_request)) return reg_dev_ignore_cell_hint(wiphy); return false; } static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct reg_beacon *reg_beacon) { struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; bool channel_changed = false; struct ieee80211_channel chan_before; assert_cfg80211_lock(); sband = wiphy->bands[reg_beacon->chan.band]; chan = &sband->channels[chan_idx]; if (likely(chan->center_freq != reg_beacon->chan.center_freq)) return; if (chan->beacon_found) return; chan->beacon_found = true; if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) return; chan_before.center_freq = chan->center_freq; chan_before.flags = chan->flags; if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; channel_changed = true; } if (chan->flags & IEEE80211_CHAN_NO_IBSS) { chan->flags &= ~IEEE80211_CHAN_NO_IBSS; channel_changed = true; } if (channel_changed) nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); } /* * Called when a scan on a wiphy finds a beacon on * new channel */ static void wiphy_update_new_beacon(struct wiphy *wiphy, struct reg_beacon *reg_beacon) { unsigned int i; struct ieee80211_supported_band *sband; assert_cfg80211_lock(); if (!wiphy->bands[reg_beacon->chan.band]) return; sband = wiphy->bands[reg_beacon->chan.band]; for (i = 0; i < sband->n_channels; i++) handle_reg_beacon(wiphy, i, reg_beacon); } /* * Called upon reg changes or a new wiphy is added */ static void wiphy_update_beacon_reg(struct wiphy *wiphy) { unsigned int i; struct ieee80211_supported_band *sband; struct reg_beacon *reg_beacon; assert_cfg80211_lock(); if (list_empty(®_beacon_list)) return; list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue; sband = wiphy->bands[reg_beacon->chan.band]; for (i = 0; i < sband->n_channels; i++) handle_reg_beacon(wiphy, i, reg_beacon); } } static bool reg_is_world_roaming(struct wiphy *wiphy) { if (is_world_regdom(cfg80211_regdomain->alpha2) || (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) return true; if (last_request && last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) return true; return false; } /* Reap the advantages of previously found beacons */ static void reg_process_beacons(struct wiphy *wiphy) { /* * Means we are just firing up cfg80211, so no beacons would * have been processed yet. */ if (!last_request) return; if (!reg_is_world_roaming(wiphy)) return; wiphy_update_beacon_reg(wiphy); } static bool is_ht40_not_allowed(struct ieee80211_channel *chan) { if (!chan) return true; if (chan->flags & IEEE80211_CHAN_DISABLED) return true; /* This would happen when regulatory rules disallow HT40 completely */ if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) return true; return false; } static void reg_process_ht_flags_channel(struct wiphy *wiphy, enum ieee80211_band band, unsigned int chan_idx) { struct ieee80211_supported_band *sband; struct ieee80211_channel *channel; struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; unsigned int i; assert_cfg80211_lock(); sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); channel = &sband->channels[chan_idx]; if (is_ht40_not_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; } /* * We need to ensure the extension channels exist to * be able to use HT40- or HT40+, this finds them (or not) */ for (i = 0; i < sband->n_channels; i++) { struct ieee80211_channel *c = &sband->channels[i]; if (c->center_freq == (channel->center_freq - 20)) channel_before = c; if (c->center_freq == (channel->center_freq + 20)) channel_after = c; } /* * Please note that this assumes target bandwidth is 20 MHz, * if that ever changes we also need to change the below logic * to include that as well. */ if (is_ht40_not_allowed(channel_before)) channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; if (is_ht40_not_allowed(channel_after)) channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; } static void reg_process_ht_flags_band(struct wiphy *wiphy, enum ieee80211_band band) { unsigned int i; struct ieee80211_supported_band *sband; BUG_ON(!wiphy->bands[band]); sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) reg_process_ht_flags_channel(wiphy, band, i); } static void reg_process_ht_flags(struct wiphy *wiphy) { enum ieee80211_band band; if (!wiphy) return; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) reg_process_ht_flags_band(wiphy, band); } } static void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { enum ieee80211_band band; assert_reg_lock(); if (ignore_reg_update(wiphy, initiator)) return; last_request->dfs_region = cfg80211_regdomain->dfs_region; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) handle_band(wiphy, band, initiator); } reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); if (wiphy->reg_notifier) wiphy->reg_notifier(wiphy, last_request); } static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) { struct cfg80211_registered_device *rdev; struct wiphy *wiphy; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; wiphy_update_regulatory(wiphy, initiator); /* * Regulatory updates set by CORE are ignored for custom * regulatory cards. Let us notify the changes to the driver, * as some drivers used this to restore its orig_* reg domain. */ if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && wiphy->reg_notifier) wiphy->reg_notifier(wiphy, last_request); } } static void handle_channel_custom(struct wiphy *wiphy, enum ieee80211_band band, unsigned int chan_idx, const struct ieee80211_regdomain *regd) { int r; u32 desired_bw_khz = MHZ_TO_KHZ(20); u32 bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; assert_reg_lock(); sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule, regd); if (r) { REG_DBG_PRINT("Disabling freq %d MHz as custom " "regd has no rule that fits a %d MHz " "wide channel\n", chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); chan->flags = IEEE80211_CHAN_DISABLED; return; } chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); } static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, const struct ieee80211_regdomain *regd) { unsigned int i; struct ieee80211_supported_band *sband; BUG_ON(!wiphy->bands[band]); sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) handle_channel_custom(wiphy, band, i, regd); } /* Used by drivers prior to wiphy registration */ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { enum ieee80211_band band; unsigned int bands_set = 0; mutex_lock(®_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; handle_band_custom(wiphy, band, regd); bands_set++; } mutex_unlock(®_mutex); /* * no point in calling this if it won't have any effect * on your device's supportd bands. */ WARN_ON(!bands_set); } EXPORT_SYMBOL(wiphy_apply_custom_regulatory); /* * Return value which can be used by ignore_request() to indicate * it has been determined we should intersect two regulatory domains */ #define REG_INTERSECT 1 /* This has the logic which determines when a new request * should be ignored. */ static int ignore_request(struct wiphy *wiphy, struct regulatory_request *pending_request) { struct wiphy *last_wiphy = NULL; assert_cfg80211_lock(); /* All initial requests are respected */ if (!last_request) return 0; switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: return 0; case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (reg_request_cell_base(last_request)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) return -EOPNOTSUPP; return -EALREADY; } last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (unlikely(!is_an_alpha2(pending_request->alpha2))) return -EINVAL; if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different * Country IE alpha2s. We could * intersect them, but that seems unlikely * to be correct. Reject second one for now. */ if (regdom_changes(pending_request->alpha2)) return -EOPNOTSUPP; return -EALREADY; } /* * Two consecutive Country IE hints on the same wiphy. * This should be picked up early by the driver/stack */ if (WARN_ON(regdom_changes(pending_request->alpha2))) return 0; return -EALREADY; } return 0; case NL80211_REGDOM_SET_BY_DRIVER: if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { if (regdom_changes(pending_request->alpha2)) return 0; return -EALREADY; } /* * This would happen if you unplug and plug your card * back in or if you add a new device for which the previously * loaded card also agrees on the regulatory domain. */ if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) return -EALREADY; return REG_INTERSECT; case NL80211_REGDOM_SET_BY_USER: if (reg_request_cell_base(pending_request)) return reg_ignore_cell_hint(pending_request); if (reg_request_cell_base(last_request)) return -EOPNOTSUPP; if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && last_request->intersect) return -EOPNOTSUPP; /* * Process user requests only after previous user/driver/core * requests have been processed */ if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || last_request->initiator == NL80211_REGDOM_SET_BY_USER) { if (regdom_changes(last_request->alpha2)) return -EAGAIN; } if (!regdom_changes(pending_request->alpha2)) return -EALREADY; return 0; } return -EINVAL; } static void reg_set_request_processed(void) { bool need_more_processing = false; last_request->processed = true; spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) need_more_processing = true; spin_unlock(®_requests_lock); if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) cancel_delayed_work(®_timeout); if (need_more_processing) schedule_work(®_work); } /** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this * is required to be set to the wiphy that received the information * @pending_request: the regulatory request currently being processed * * The Wireless subsystem can use this function to hint to the wireless core * what it believes should be the current regulatory domain. * * Returns zero if all went fine, %-EALREADY if a regulatory domain had * already been set or other standard error codes. * * Caller must hold &cfg80211_mutex and ®_mutex */ static int __regulatory_hint(struct wiphy *wiphy, struct regulatory_request *pending_request) { bool intersect = false; int r = 0; assert_cfg80211_lock(); r = ignore_request(wiphy, pending_request); if (r == REG_INTERSECT) { if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); if (r) { kfree(pending_request); return r; } } intersect = true; } else if (r) { /* * If the regulatory domain being requested by the * driver has already been set just copy it to the * wiphy */ if (r == -EALREADY && pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); if (r) { kfree(pending_request); return r; } r = -EALREADY; goto new_request; } kfree(pending_request); return r; } new_request: if (last_request != &core_request_world) kfree(last_request); last_request = pending_request; last_request->intersect = intersect; pending_request = NULL; if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { user_alpha2[0] = last_request->alpha2[0]; user_alpha2[1] = last_request->alpha2[1]; } /* When r == REG_INTERSECT we do need to call CRDA */ if (r < 0) { /* * Since CRDA will not be called in this case as we already * have applied the requested regulatory domain before we just * inform userspace we have processed the request */ if (r == -EALREADY) { nl80211_send_reg_change_event(last_request); reg_set_request_processed(); } return r; } return call_crda(last_request->alpha2); } /* This processes *all* regulatory hints */ static void reg_process_hint(struct regulatory_request *reg_request, enum nl80211_reg_initiator reg_initiator) { int r = 0; struct wiphy *wiphy = NULL; BUG_ON(!reg_request->alpha2); if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { kfree(reg_request); return; } r = __regulatory_hint(wiphy, reg_request); /* This is required so that the orig_* parameters are saved */ if (r == -EALREADY && wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { wiphy_update_regulatory(wiphy, reg_initiator); return; } /* * We only time out user hints, given that they should be the only * source of bogus requests. */ if (r != -EALREADY && reg_initiator == NL80211_REGDOM_SET_BY_USER) schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } /* * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* * Regulatory hints come on a first come first serve basis and we * must process each one atomically. */ static void reg_process_pending_hints(void) { struct regulatory_request *reg_request; mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); /* When last_request->processed becomes true this will be rescheduled */ if (last_request && !last_request->processed) { REG_DBG_PRINT("Pending regulatory request, waiting " "for it to be processed...\n"); goto out; } spin_lock(®_requests_lock); if (list_empty(®_requests_list)) { spin_unlock(®_requests_lock); goto out; } reg_request = list_first_entry(®_requests_list, struct regulatory_request, list); list_del_init(®_request->list); spin_unlock(®_requests_lock); reg_process_hint(reg_request, reg_request->initiator); out: mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); } /* Processes beacon hints -- this has nothing to do with country IEs */ static void reg_process_pending_beacon_hints(void) { struct cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; /* * No need to hold the reg_mutex here as we just touch wiphys * and do not read or access regulatory variables. */ mutex_lock(&cfg80211_mutex); /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); if (list_empty(®_pending_beacons)) { spin_unlock_bh(®_pending_beacons_lock); goto out; } list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ list_for_each_entry(rdev, &cfg80211_rdev_list, list) wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); /* Remembers the beacon hint for new wiphys or reg changes */ list_add_tail(&pending_beacon->list, ®_beacon_list); } spin_unlock_bh(®_pending_beacons_lock); out: mutex_unlock(&cfg80211_mutex); } static void reg_todo(struct work_struct *work) { reg_process_pending_hints(); reg_process_pending_beacon_hints(); } static void queue_regulatory_request(struct regulatory_request *request) { if (isalpha(request->alpha2[0])) request->alpha2[0] = toupper(request->alpha2[0]); if (isalpha(request->alpha2[1])) request->alpha2[1] = toupper(request->alpha2[1]); spin_lock(®_requests_lock); list_add_tail(&request->list, ®_requests_list); spin_unlock(®_requests_lock); schedule_work(®_work); } /* * Core regulatory hint -- happens during cfg80211_init() * and when we restore regulatory settings. */ static int regulatory_hint_core(const char *alpha2) { struct regulatory_request *request; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_CORE; queue_regulatory_request(request); return 0; } /* User hints */ int regulatory_hint_user(const char *alpha2, enum nl80211_user_reg_hint_type user_reg_hint_type) { struct regulatory_request *request; BUG_ON(!alpha2); request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; request->wiphy_idx = WIPHY_IDX_STALE; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_USER; request->user_reg_hint_type = user_reg_hint_type; queue_regulatory_request(request); return 0; } /* Driver hints */ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { struct regulatory_request *request; BUG_ON(!alpha2); BUG_ON(!wiphy); request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; request->wiphy_idx = get_wiphy_idx(wiphy); /* Must have registered wiphy first */ BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_DRIVER; queue_regulatory_request(request); return 0; } EXPORT_SYMBOL(regulatory_hint); /* * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and * therefore cannot iterate over the rdev list here. */ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, u8 *country_ie, u8 country_ie_len) { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; struct regulatory_request *request; mutex_lock(®_mutex); if (unlikely(!last_request)) goto out; /* IE len must be evenly divisible by 2 */ if (country_ie_len & 0x01) goto out; if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) goto out; alpha2[0] = country_ie[0]; alpha2[1] = country_ie[1]; if (country_ie[2] == 'I') env = ENVIRON_INDOOR; else if (country_ie[2] == 'O') env = ENVIRON_OUTDOOR; /* * We will run this only upon a successful connection on cfg80211. * We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. */ if (likely(last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy_idx_valid(last_request->wiphy_idx))) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) goto out; request->wiphy_idx = get_wiphy_idx(wiphy); request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; request->country_ie_env = env; mutex_unlock(®_mutex); queue_regulatory_request(request); return; out: mutex_unlock(®_mutex); } static void restore_alpha2(char *alpha2, bool reset_user) { /* indicates there is no alpha2 to consider for restoration */ alpha2[0] = '9'; alpha2[1] = '7'; /* The user setting has precedence over the module parameter */ if (is_user_regdom_saved()) { /* Unless we're asked to ignore it and reset it */ if (reset_user) { REG_DBG_PRINT("Restoring regulatory settings " "including user preference\n"); user_alpha2[0] = '9'; user_alpha2[1] = '7'; /* * If we're ignoring user settings, we still need to * check the module parameter to ensure we put things * back as they were for a full restore. */ if (!is_world_regdom(ieee80211_regdom)) { REG_DBG_PRINT("Keeping preference on " "module parameter ieee80211_regdom: %c%c\n", ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } } else { REG_DBG_PRINT("Restoring regulatory settings " "while preserving user preference for: %c%c\n", user_alpha2[0], user_alpha2[1]); alpha2[0] = user_alpha2[0]; alpha2[1] = user_alpha2[1]; } } else if (!is_world_regdom(ieee80211_regdom)) { REG_DBG_PRINT("Keeping preference on " "module parameter ieee80211_regdom: %c%c\n", ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } else REG_DBG_PRINT("Restoring regulatory settings\n"); } static void restore_custom_reg_settings(struct wiphy *wiphy) { struct ieee80211_supported_band *sband; enum ieee80211_band band; struct ieee80211_channel *chan; int i; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; chan->flags = chan->orig_flags; chan->max_antenna_gain = chan->orig_mag; chan->max_power = chan->orig_mpwr; chan->beacon_found = false; } } } /* * Restoring regulatory settings involves ingoring any * possibly stale country IE information and user regulatory * settings if so desired, this includes any beacon hints * learned as we could have traveled outside to another country * after disconnection. To restore regulatory settings we do * exactly what we did at bootup: * * - send a core regulatory hint * - send a user regulatory hint if applicable * * Device drivers that send a regulatory hint for a specific country * keep their own regulatory domain on wiphy->regd so that does does * not need to be remembered. */ static void restore_regulatory_settings(bool reset_user) { char alpha2[2]; char world_alpha2[2]; struct reg_beacon *reg_beacon, *btmp; struct regulatory_request *reg_request, *tmp; LIST_HEAD(tmp_reg_req_list); struct cfg80211_registered_device *rdev; mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); reset_regdomains(true); restore_alpha2(alpha2, reset_user); /* * If there's any pending requests we simply * stash them to a temporary pending queue and * add then after we've restored regulatory * settings. */ spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) { list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) continue; list_move_tail(®_request->list, &tmp_reg_req_list); } } spin_unlock(®_requests_lock); /* Clear beacon hints */ spin_lock_bh(®_pending_beacons_lock); if (!list_empty(®_pending_beacons)) { list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); } } spin_unlock_bh(®_pending_beacons_lock); if (!list_empty(®_beacon_list)) { list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); } } /* First restore to the basic regulatory settings */ cfg80211_regdomain = cfg80211_world_regdom; world_alpha2[0] = cfg80211_regdomain->alpha2[0]; world_alpha2[1] = cfg80211_regdomain->alpha2[1]; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) restore_custom_reg_settings(&rdev->wiphy); } mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); regulatory_hint_core(world_alpha2); /* * This restores the ieee80211_regdom module parameter * preference or the last user requested regulatory * settings, user regulatory settings takes precedence. */ if (is_an_alpha2(alpha2)) regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); if (list_empty(&tmp_reg_req_list)) return; mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); spin_lock(®_requests_lock); list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { REG_DBG_PRINT("Adding request for country %c%c back " "into the queue\n", reg_request->alpha2[0], reg_request->alpha2[1]); list_move_tail(®_request->list, ®_requests_list); } spin_unlock(®_requests_lock); mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); REG_DBG_PRINT("Kicking the queue\n"); schedule_work(®_work); } void regulatory_hint_disconnect(void) { REG_DBG_PRINT("All devices are disconnected, going to " "restore regulatory settings\n"); restore_regulatory_settings(false); } static bool freq_is_chan_12_13_14(u16 freq) { if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) || freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) || freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ)) return true; return false; } int regulatory_hint_found_beacon(struct wiphy *wiphy, struct ieee80211_channel *beacon_chan, gfp_t gfp) { struct reg_beacon *reg_beacon; if (likely((beacon_chan->beacon_found || (beacon_chan->flags & IEEE80211_CHAN_RADAR) || (beacon_chan->band == IEEE80211_BAND_2GHZ && !freq_is_chan_12_13_14(beacon_chan->center_freq))))) return 0; reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); if (!reg_beacon) return -ENOMEM; REG_DBG_PRINT("Found new beacon on " "frequency: %d MHz (Ch %d) on %s\n", beacon_chan->center_freq, ieee80211_frequency_to_channel(beacon_chan->center_freq), wiphy_name(wiphy)); memcpy(®_beacon->chan, beacon_chan, sizeof(struct ieee80211_channel)); /* * Since we can be called from BH or and non-BH context * we must use spin_lock_bh() */ spin_lock_bh(®_pending_beacons_lock); list_add_tail(®_beacon->list, ®_pending_beacons); spin_unlock_bh(®_pending_beacons_lock); schedule_work(®_work); return 0; } static void print_rd_rules(const struct ieee80211_regdomain *rd) { unsigned int i; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; const struct ieee80211_power_rule *power_rule = NULL; pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; /* * There may not be documentation for max antenna gain * in certain regions */ if (power_rule->max_antenna_gain) pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, power_rule->max_antenna_gain, power_rule->max_eirp); else pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, power_rule->max_eirp); } } bool reg_supported_dfs_region(u8 dfs_region) { switch (dfs_region) { case NL80211_DFS_UNSET: case NL80211_DFS_FCC: case NL80211_DFS_ETSI: case NL80211_DFS_JP: return true; default: REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n", dfs_region); return false; } } static void print_dfs_region(u8 dfs_region) { if (!dfs_region) return; switch (dfs_region) { case NL80211_DFS_FCC: pr_info(" DFS Master region FCC"); break; case NL80211_DFS_ETSI: pr_info(" DFS Master region ETSI"); break; case NL80211_DFS_JP: pr_info(" DFS Master region JP"); break; default: pr_info(" DFS Master region Uknown"); break; } } static void print_regdomain(const struct ieee80211_regdomain *rd) { if (is_intersected_alpha2(rd->alpha2)) { if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *rdev; rdev = cfg80211_rdev_by_wiphy_idx( last_request->wiphy_idx); if (rdev) { pr_info("Current regulatory domain updated by AP to: %c%c\n", rdev->country_ie_alpha2[0], rdev->country_ie_alpha2[1]); } else pr_info("Current regulatory domain intersected:\n"); } else pr_info("Current regulatory domain intersected:\n"); } else if (is_world_regdom(rd->alpha2)) pr_info("World regulatory domain updated:\n"); else { if (is_unknown_alpha2(rd->alpha2)) pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); else { if (reg_request_cell_base(last_request)) pr_info("Regulatory domain changed " "to country: %c%c by Cell Station\n", rd->alpha2[0], rd->alpha2[1]); else pr_info("Regulatory domain changed " "to country: %c%c\n", rd->alpha2[0], rd->alpha2[1]); } } print_dfs_region(rd->dfs_region); print_rd_rules(rd); } static void print_regdomain_info(const struct ieee80211_regdomain *rd) { pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); print_rd_rules(rd); } /* Takes ownership of rd only if it doesn't fail */ static int __set_regdom(const struct ieee80211_regdomain *rd) { const struct ieee80211_regdomain *intersected_rd = NULL; struct cfg80211_registered_device *rdev = NULL; struct wiphy *request_wiphy; /* Some basic sanity checks first */ if (is_world_regdom(rd->alpha2)) { if (WARN_ON(!reg_is_valid_request(rd->alpha2))) return -EINVAL; update_world_regdomain(rd); return 0; } if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && !is_unknown_alpha2(rd->alpha2)) return -EINVAL; if (!last_request) return -EINVAL; /* * Lets only bother proceeding on the same alpha2 if the current * rd is non static (it means CRDA was present and was used last) * and the pending request came in from a country IE */ if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { /* * If someone else asked us to change the rd lets only bother * checking if the alpha2 changes if CRDA was already called */ if (!regdom_changes(rd->alpha2)) return -EALREADY; } /* * Now lets set the regulatory domain, update all driver channels * and finally inform them of what we have done, in case they want * to review or adjust their own settings based on their own * internal EEPROM data */ if (WARN_ON(!reg_is_valid_request(rd->alpha2))) return -EINVAL; if (!is_valid_rd(rd)) { pr_err("Invalid regulatory domain detected:\n"); print_regdomain_info(rd); return -EINVAL; } request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (!request_wiphy && (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { schedule_delayed_work(®_timeout, 0); return -ENODEV; } if (!last_request->intersect) { int r; if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { reset_regdomains(false); cfg80211_regdomain = rd; return 0; } /* * For a driver hint, lets copy the regulatory domain the * driver wanted to the wiphy to deal with conflicts */ /* * Userspace could have sent two replies with only * one kernel request. */ if (request_wiphy->regd) return -EALREADY; r = reg_copy_regd(&request_wiphy->regd, rd); if (r) return r; reset_regdomains(false); cfg80211_regdomain = rd; return 0; } /* Intersection requires a bit more work */ if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { intersected_rd = regdom_intersect(rd, cfg80211_regdomain); if (!intersected_rd) return -EINVAL; /* * We can trash what CRDA provided now. * However if a driver requested this specific regulatory * domain we keep it for its private use */ if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) request_wiphy->regd = rd; else kfree(rd); rd = NULL; reset_regdomains(false); cfg80211_regdomain = intersected_rd; return 0; } if (!intersected_rd) return -EINVAL; rdev = wiphy_to_dev(request_wiphy); rdev->country_ie_alpha2[0] = rd->alpha2[0]; rdev->country_ie_alpha2[1] = rd->alpha2[1]; rdev->env = last_request->country_ie_env; BUG_ON(intersected_rd == rd); kfree(rd); rd = NULL; reset_regdomains(false); cfg80211_regdomain = intersected_rd; return 0; } /* * Use this call to set the current regulatory domain. Conflicts with * multiple drivers can be ironed out later. Caller must've already * kmalloc'd the rd structure. Caller must hold cfg80211_mutex */ int set_regdom(const struct ieee80211_regdomain *rd) { int r; assert_cfg80211_lock(); mutex_lock(®_mutex); /* Note that this doesn't update the wiphys, this is done below */ r = __set_regdom(rd); if (r) { if (r == -EALREADY) reg_set_request_processed(); kfree(rd); mutex_unlock(®_mutex); return r; } /* This would make this whole thing pointless */ if (!last_request->intersect) BUG_ON(rd != cfg80211_regdomain); /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(last_request->initiator); print_regdomain(cfg80211_regdomain); nl80211_send_reg_change_event(last_request); reg_set_request_processed(); mutex_unlock(®_mutex); return r; } #ifdef CONFIG_HOTPLUG int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) { if (last_request && !last_request->processed) { if (add_uevent_var(env, "COUNTRY=%c%c", last_request->alpha2[0], last_request->alpha2[1])) return -ENOMEM; } return 0; } #else int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) { return -ENODEV; } #endif /* CONFIG_HOTPLUG */ void wiphy_regulatory_register(struct wiphy *wiphy) { assert_cfg80211_lock(); mutex_lock(®_mutex); if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint++; wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); mutex_unlock(®_mutex); } /* Caller must hold cfg80211_mutex */ void wiphy_regulatory_deregister(struct wiphy *wiphy) { struct wiphy *request_wiphy = NULL; assert_cfg80211_lock(); mutex_lock(®_mutex); if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint--; kfree(wiphy->regd); if (last_request) request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (!request_wiphy || request_wiphy != wiphy) goto out; last_request->wiphy_idx = WIPHY_IDX_STALE; last_request->country_ie_env = ENVIRON_ANY; out: mutex_unlock(®_mutex); } static void reg_timeout_work(struct work_struct *work) { REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " "restoring regulatory settings\n"); restore_regulatory_settings(true); } int __init regulatory_init(void) { int err = 0; reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); if (IS_ERR(reg_pdev)) return PTR_ERR(reg_pdev); reg_pdev->dev.type = ®_device_type; spin_lock_init(®_requests_lock); spin_lock_init(®_pending_beacons_lock); reg_regdb_size_check(); cfg80211_regdomain = cfg80211_world_regdom; user_alpha2[0] = '9'; user_alpha2[1] = '7'; /* We always try to get an update for the static regdomain */ err = regulatory_hint_core(cfg80211_regdomain->alpha2); if (err) { if (err == -ENOMEM) return err; /* * N.B. kobject_uevent_env() can fail mainly for when we're out * memory which is handled and propagated appropriately above * but it can also fail during a netlink_broadcast() or during * early boot for call_usermodehelper(). For now treat these * errors as non-fatal. */ pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); #ifdef CONFIG_CFG80211_REG_DEBUG /* We want to find out exactly why when debugging */ WARN_ON(err); #endif } /* * Finally, if the user set the module parameter treat it * as a user hint. */ if (!is_world_regdom(ieee80211_regdom)) regulatory_hint_user(ieee80211_regdom, NL80211_USER_REG_HINT_USER); return 0; } void /* __init_or_exit */ regulatory_exit(void) { struct regulatory_request *reg_request, *tmp; struct reg_beacon *reg_beacon, *btmp; cancel_work_sync(®_work); cancel_delayed_work_sync(®_timeout); mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); reset_regdomains(true); dev_set_uevent_suppress(®_pdev->dev, true); platform_device_unregister(reg_pdev); spin_lock_bh(®_pending_beacons_lock); if (!list_empty(®_pending_beacons)) { list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); } } spin_unlock_bh(®_pending_beacons_lock); if (!list_empty(®_beacon_list)) { list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); } } spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) { list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { list_del(®_request->list); kfree(reg_request); } } spin_unlock(®_requests_lock); mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); } compat-drivers-2012-09-18/net/wireless/lib80211.c0000644000175000017500000001620012026211315020310 0ustar mcgrofmcgrof/* * lib80211 -- common bits for IEEE802.11 drivers * * Copyright(c) 2008 John W. Linville * * Portions copied from old ieee80211 component, w/ original copyright * notices below: * * Host AP crypto routines * * Copyright (c) 2002-2003, Jouni Malinen * Portions Copyright (C) 2004, Intel Corporation * */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #define DRV_NAME "lib80211" #define DRV_DESCRIPTION "common routines for IEEE802.11 drivers" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR("John W. Linville "); MODULE_LICENSE("GPL"); struct lib80211_crypto_alg { struct list_head list; struct lib80211_crypto_ops *ops; }; static LIST_HEAD(lib80211_crypto_algs); static DEFINE_SPINLOCK(lib80211_crypto_lock); static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, int force); static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info); static void lib80211_crypt_deinit_handler(unsigned long data); const char *print_ssid(char *buf, const char *ssid, u8 ssid_len) { const char *s = ssid; char *d = buf; ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN); while (ssid_len--) { if (isprint(*s)) { *d++ = *s++; continue; } *d++ = '\\'; if (*s == '\0') *d++ = '0'; else if (*s == '\n') *d++ = 'n'; else if (*s == '\r') *d++ = 'r'; else if (*s == '\t') *d++ = 't'; else if (*s == '\\') *d++ = '\\'; else d += snprintf(d, 3, "%03o", *s); s++; } *d = '\0'; return buf; } EXPORT_SYMBOL(print_ssid); int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, spinlock_t *lock) { memset(info, 0, sizeof(*info)); info->name = name; info->lock = lock; INIT_LIST_HEAD(&info->crypt_deinit_list); setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler, (unsigned long)info); return 0; } EXPORT_SYMBOL(lib80211_crypt_info_init); void lib80211_crypt_info_free(struct lib80211_crypt_info *info) { int i; lib80211_crypt_quiescing(info); del_timer_sync(&info->crypt_deinit_timer); lib80211_crypt_deinit_entries(info, 1); for (i = 0; i < NUM_WEP_KEYS; i++) { struct lib80211_crypt_data *crypt = info->crypt[i]; if (crypt) { if (crypt->ops) { crypt->ops->deinit(crypt->priv); module_put(crypt->ops->owner); } kfree(crypt); info->crypt[i] = NULL; } } } EXPORT_SYMBOL(lib80211_crypt_info_free); static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, int force) { struct lib80211_crypt_data *entry, *next; unsigned long flags; spin_lock_irqsave(info->lock, flags); list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) { if (atomic_read(&entry->refcnt) != 0 && !force) continue; list_del(&entry->list); if (entry->ops) { entry->ops->deinit(entry->priv); module_put(entry->ops->owner); } kfree(entry); } spin_unlock_irqrestore(info->lock, flags); } /* After this, crypt_deinit_list won't accept new members */ static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info) { unsigned long flags; spin_lock_irqsave(info->lock, flags); info->crypt_quiesced = 1; spin_unlock_irqrestore(info->lock, flags); } static void lib80211_crypt_deinit_handler(unsigned long data) { struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data; unsigned long flags; lib80211_crypt_deinit_entries(info, 0); spin_lock_irqsave(info->lock, flags); if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) { printk(KERN_DEBUG "%s: entries remaining in delayed crypt " "deletion list\n", info->name); info->crypt_deinit_timer.expires = jiffies + HZ; add_timer(&info->crypt_deinit_timer); } spin_unlock_irqrestore(info->lock, flags); } void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, struct lib80211_crypt_data **crypt) { struct lib80211_crypt_data *tmp; unsigned long flags; if (*crypt == NULL) return; tmp = *crypt; *crypt = NULL; /* must not run ops->deinit() while there may be pending encrypt or * decrypt operations. Use a list of delayed deinits to avoid needing * locking. */ spin_lock_irqsave(info->lock, flags); if (!info->crypt_quiesced) { list_add(&tmp->list, &info->crypt_deinit_list); if (!timer_pending(&info->crypt_deinit_timer)) { info->crypt_deinit_timer.expires = jiffies + HZ; add_timer(&info->crypt_deinit_timer); } } spin_unlock_irqrestore(info->lock, flags); } EXPORT_SYMBOL(lib80211_crypt_delayed_deinit); int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops) { unsigned long flags; struct lib80211_crypto_alg *alg; alg = kzalloc(sizeof(*alg), GFP_KERNEL); if (alg == NULL) return -ENOMEM; alg->ops = ops; spin_lock_irqsave(&lib80211_crypto_lock, flags); list_add(&alg->list, &lib80211_crypto_algs); spin_unlock_irqrestore(&lib80211_crypto_lock, flags); printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n", ops->name); return 0; } EXPORT_SYMBOL(lib80211_register_crypto_ops); int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) { struct lib80211_crypto_alg *alg; unsigned long flags; spin_lock_irqsave(&lib80211_crypto_lock, flags); list_for_each_entry(alg, &lib80211_crypto_algs, list) { if (alg->ops == ops) goto found; } spin_unlock_irqrestore(&lib80211_crypto_lock, flags); return -EINVAL; found: printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", ops->name); list_del(&alg->list); spin_unlock_irqrestore(&lib80211_crypto_lock, flags); kfree(alg); return 0; } EXPORT_SYMBOL(lib80211_unregister_crypto_ops); struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name) { struct lib80211_crypto_alg *alg; unsigned long flags; spin_lock_irqsave(&lib80211_crypto_lock, flags); list_for_each_entry(alg, &lib80211_crypto_algs, list) { if (strcmp(alg->ops->name, name) == 0) goto found; } spin_unlock_irqrestore(&lib80211_crypto_lock, flags); return NULL; found: spin_unlock_irqrestore(&lib80211_crypto_lock, flags); return alg->ops; } EXPORT_SYMBOL(lib80211_get_crypto_ops); static void *lib80211_crypt_null_init(int keyidx) { return (void *)1; } static void lib80211_crypt_null_deinit(void *priv) { } static struct lib80211_crypto_ops lib80211_crypt_null = { .name = "NULL", .init = lib80211_crypt_null_init, .deinit = lib80211_crypt_null_deinit, .owner = THIS_MODULE, }; static int __init lib80211_init(void) { pr_info(DRV_DESCRIPTION "\n"); return lib80211_register_crypto_ops(&lib80211_crypt_null); } static void __exit lib80211_exit(void) { lib80211_unregister_crypto_ops(&lib80211_crypt_null); BUG_ON(!list_empty(&lib80211_crypto_algs)); } module_init(lib80211_init); module_exit(lib80211_exit); compat-drivers-2012-09-18/net/wireless/nl80211.c0000644000175000017500000072204512026211315020166 0ustar mcgrofmcgrof/* * This is the new netlink-based wireless configuration interface. * * Copyright 2006-2010 Johannes Berg */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "nl80211.h" #include "reg.h" static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type); static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, struct genl_info *info, struct cfg80211_crypto_settings *settings, int cipher_limit); static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); /* the netlink family */ static struct genl_family nl80211_fam = { .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ .name = "nl80211", /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, .netnsok = true, .pre_doit = nl80211_pre_doit, .post_doit = nl80211_post_doit, }; /* returns ERR_PTR values */ static struct wireless_dev * __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs) { struct cfg80211_registered_device *rdev; struct wireless_dev *result = NULL; bool have_ifidx = attrs[NL80211_ATTR_IFINDEX]; bool have_wdev_id = attrs[NL80211_ATTR_WDEV]; u64 wdev_id; int wiphy_idx = -1; int ifidx = -1; assert_cfg80211_lock(); if (!have_ifidx && !have_wdev_id) return ERR_PTR(-EINVAL); if (have_ifidx) ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); if (have_wdev_id) { wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]); wiphy_idx = wdev_id >> 32; } list_for_each_entry(rdev, &cfg80211_rdev_list, list) { struct wireless_dev *wdev; if (wiphy_net(&rdev->wiphy) != netns) continue; if (have_wdev_id && rdev->wiphy_idx != wiphy_idx) continue; mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (have_ifidx && wdev->netdev && wdev->netdev->ifindex == ifidx) { result = wdev; break; } if (have_wdev_id && wdev->identifier == (u32)wdev_id) { result = wdev; break; } } mutex_unlock(&rdev->devlist_mtx); if (result) break; } if (result) return result; return ERR_PTR(-ENODEV); } static struct cfg80211_registered_device * __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) { struct cfg80211_registered_device *rdev = NULL, *tmp; struct net_device *netdev; assert_cfg80211_lock(); if (!attrs[NL80211_ATTR_WIPHY] && !attrs[NL80211_ATTR_IFINDEX] && !attrs[NL80211_ATTR_WDEV]) return ERR_PTR(-EINVAL); if (attrs[NL80211_ATTR_WIPHY]) rdev = cfg80211_rdev_by_wiphy_idx( nla_get_u32(attrs[NL80211_ATTR_WIPHY])); if (attrs[NL80211_ATTR_WDEV]) { u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]); struct wireless_dev *wdev; bool found = false; tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32); if (tmp) { /* make sure wdev exists */ mutex_lock(&tmp->devlist_mtx); list_for_each_entry(wdev, &tmp->wdev_list, list) { if (wdev->identifier != (u32)wdev_id) continue; found = true; break; } mutex_unlock(&tmp->devlist_mtx); if (!found) tmp = NULL; if (rdev && tmp != rdev) return ERR_PTR(-EINVAL); rdev = tmp; } } if (attrs[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); netdev = dev_get_by_index(netns, ifindex); if (netdev) { if (netdev->ieee80211_ptr) tmp = wiphy_to_dev( netdev->ieee80211_ptr->wiphy); else tmp = NULL; dev_put(netdev); /* not wireless device -- return error */ if (!tmp) return ERR_PTR(-EINVAL); /* mismatch -- return error */ if (rdev && tmp != rdev) return ERR_PTR(-EINVAL); rdev = tmp; } } if (!rdev) return ERR_PTR(-ENODEV); if (netns != wiphy_net(&rdev->wiphy)) return ERR_PTR(-ENODEV); return rdev; } /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. * If successful, it returns non-NULL and also locks * the driver's mutex! * * This means that you need to call cfg80211_unlock_rdev() * before being allowed to acquire &cfg80211_mutex! * * This is necessary because we need to lock the global * mutex to get an item off the list safely, and then * we lock the rdev mutex so it doesn't go away under us. * * We don't want to keep cfg80211_mutex locked * for all the time in order to allow requests on * other interfaces to go through at the same time. * * The result of this can be a PTR_ERR and hence must * be checked with IS_ERR() for errors. */ static struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) { struct cfg80211_registered_device *rdev; mutex_lock(&cfg80211_mutex); rdev = __cfg80211_rdev_from_attrs(netns, info->attrs); /* if it is not an error we grab the lock on * it to assure it won't be going away while * we operate on it */ if (!IS_ERR(rdev)) mutex_lock(&rdev->mtx); mutex_unlock(&cfg80211_mutex); return rdev; } /* policy for the attributes */ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, .len = 20-1 }, [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, [NL80211_ATTR_MAC] = { .len = ETH_ALEN }, [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN }, [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 }, [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_STA_AID] = { .type = NLA_U16 }, [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_MESH_ID_LEN }, [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN }, [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, [NL80211_ATTR_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, [NL80211_ATTR_SSID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_SSID_LEN }, [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG }, [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG }, [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 }, [NL80211_ATTR_STA_FLAGS2] = { .len = sizeof(struct nl80211_sta_flag_update), }, [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 }, [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, [NL80211_ATTR_PID] = { .type = NLA_U32 }, [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, .len = WLAN_PMKID_LEN }, [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 }, [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 }, [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG }, [NL80211_ATTR_HT_CAPABILITY_MASK] = { .len = NL80211_HT_CAPABILITY_LEN }, [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, }; /* policy for the key attributes */ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_KEY_IDX] = { .type = NLA_U8 }, [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, [NL80211_KEY_TYPE] = { .type = NLA_U32 }, [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, }; /* policy for the key default flags */ static const struct nla_policy nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG }, [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, }; /* policy for WoWLAN attributes */ static const struct nla_policy nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, }; /* policy for GTK rekey offload attributes */ static const struct nla_policy nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN }, [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN }, [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN }, }; static const struct nla_policy nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_SSID_LEN }, [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, }; /* ifidx get helper */ static int nl80211_get_ifidx(struct netlink_callback *cb) { int res; res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy); if (res) return res; if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) return -EINVAL; res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); if (!res) return -EINVAL; return res; } static int nl80211_prepare_netdev_dump(struct sk_buff *skb, struct netlink_callback *cb, struct cfg80211_registered_device **rdev, struct net_device **dev) { int ifidx = cb->args[0]; int err; if (!ifidx) ifidx = nl80211_get_ifidx(cb); if (ifidx < 0) return ifidx; cb->args[0] = ifidx; rtnl_lock(); *dev = __dev_get_by_index(sock_net(skb->sk), ifidx); if (!*dev) { err = -ENODEV; goto out_rtnl; } *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(*rdev)) { err = PTR_ERR(*rdev); goto out_rtnl; } return 0; out_rtnl: rtnl_unlock(); return err; } static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev) { cfg80211_unlock_rdev(rdev); rtnl_unlock(); } /* IE validation */ static bool is_valid_ie_attr(const struct nlattr *attr) { const u8 *pos; int len; if (!attr) return true; pos = nla_data(attr); len = nla_len(attr); while (len) { u8 elemlen; if (len < 2) return false; len -= 2; elemlen = pos[1]; if (elemlen > len) return false; len -= elemlen; pos += 2 + elemlen; } return true; } /* message building helper */ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, int flags, u8 cmd) { /* since there is no private header just add the generic one */ return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd); } static int nl80211_msg_put_channel(struct sk_buff *msg, struct ieee80211_channel *chan) { if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ, chan->center_freq)) goto nla_put_failure; if ((chan->flags & IEEE80211_CHAN_DISABLED) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED)) goto nla_put_failure; if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN)) goto nla_put_failure; if ((chan->flags & IEEE80211_CHAN_NO_IBSS) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS)) goto nla_put_failure; if ((chan->flags & IEEE80211_CHAN_RADAR) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR)) goto nla_put_failure; if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, DBM_TO_MBM(chan->max_power))) goto nla_put_failure; return 0; nla_put_failure: return -ENOBUFS; } /* netlink command implementations */ struct key_parse { struct key_params p; int idx; int type; bool def, defmgmt; bool def_uni, def_multi; }; static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) { struct nlattr *tb[NL80211_KEY_MAX + 1]; int err = nla_parse_nested(tb, NL80211_KEY_MAX, key, nl80211_key_policy); if (err) return err; k->def = !!tb[NL80211_KEY_DEFAULT]; k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; if (k->def) { k->def_uni = true; k->def_multi = true; } if (k->defmgmt) k->def_multi = true; if (tb[NL80211_KEY_IDX]) k->idx = nla_get_u8(tb[NL80211_KEY_IDX]); if (tb[NL80211_KEY_DATA]) { k->p.key = nla_data(tb[NL80211_KEY_DATA]); k->p.key_len = nla_len(tb[NL80211_KEY_DATA]); } if (tb[NL80211_KEY_SEQ]) { k->p.seq = nla_data(tb[NL80211_KEY_SEQ]); k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]); } if (tb[NL80211_KEY_CIPHER]) k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]); if (tb[NL80211_KEY_TYPE]) { k->type = nla_get_u32(tb[NL80211_KEY_TYPE]); if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) return -EINVAL; } if (tb[NL80211_KEY_DEFAULT_TYPES]) { struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, tb[NL80211_KEY_DEFAULT_TYPES], nl80211_key_default_policy); if (err) return err; k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; } return 0; } static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) { if (info->attrs[NL80211_ATTR_KEY_DATA]) { k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); } if (info->attrs[NL80211_ATTR_KEY_SEQ]) { k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]); k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]); } if (info->attrs[NL80211_ATTR_KEY_IDX]) k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); if (info->attrs[NL80211_ATTR_KEY_CIPHER]) k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; if (k->def) { k->def_uni = true; k->def_multi = true; } if (k->defmgmt) k->def_multi = true; if (info->attrs[NL80211_ATTR_KEY_TYPE]) { k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) return -EINVAL; } if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; int err = nla_parse_nested( kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], nl80211_key_default_policy); if (err) return err; k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; } return 0; } static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) { int err; memset(k, 0, sizeof(*k)); k->idx = -1; k->type = -1; if (info->attrs[NL80211_ATTR_KEY]) err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k); else err = nl80211_parse_key_old(info, k); if (err) return err; if (k->def && k->defmgmt) return -EINVAL; if (k->defmgmt) { if (k->def_uni || !k->def_multi) return -EINVAL; } if (k->idx != -1) { if (k->defmgmt) { if (k->idx < 4 || k->idx > 5) return -EINVAL; } else if (k->def) { if (k->idx < 0 || k->idx > 3) return -EINVAL; } else { if (k->idx < 0 || k->idx > 5) return -EINVAL; } } return 0; } static struct cfg80211_cached_keys * nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, struct nlattr *keys) { struct key_parse parse; struct nlattr *key; struct cfg80211_cached_keys *result; int rem, err, def = 0; result = kzalloc(sizeof(*result), GFP_KERNEL); if (!result) return ERR_PTR(-ENOMEM); result->def = -1; result->defmgmt = -1; nla_for_each_nested(key, keys, rem) { memset(&parse, 0, sizeof(parse)); parse.idx = -1; err = nl80211_parse_key_new(key, &parse); if (err) goto error; err = -EINVAL; if (!parse.p.key) goto error; if (parse.idx < 0 || parse.idx > 4) goto error; if (parse.def) { if (def) goto error; def = 1; result->def = parse.idx; if (!parse.def_uni || !parse.def_multi) goto error; } else if (parse.defmgmt) goto error; err = cfg80211_validate_key_settings(rdev, &parse.p, parse.idx, false, NULL); if (err) goto error; result->params[parse.idx].cipher = parse.p.cipher; result->params[parse.idx].key_len = parse.p.key_len; result->params[parse.idx].key = result->data[parse.idx]; memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); } return result; error: kfree(result); return ERR_PTR(err); } static int nl80211_key_allowed(struct wireless_dev *wdev) { ASSERT_WDEV_LOCK(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_MESH_POINT: break; case NL80211_IFTYPE_ADHOC: if (!wdev->current_bss) return -ENOLINK; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (wdev->sme_state != CFG80211_SME_CONNECTED) return -ENOLINK; break; default: return -EINVAL; } return 0; } static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) { struct nlattr *nl_modes = nla_nest_start(msg, attr); int i; if (!nl_modes) goto nla_put_failure; i = 0; while (ifmodes) { if ((ifmodes & 1) && nla_put_flag(msg, i)) goto nla_put_failure; ifmodes >>= 1; i++; } nla_nest_end(msg, nl_modes); return 0; nla_put_failure: return -ENOBUFS; } static int nl80211_put_iface_combinations(struct wiphy *wiphy, struct sk_buff *msg) { struct nlattr *nl_combis; int i, j; nl_combis = nla_nest_start(msg, NL80211_ATTR_INTERFACE_COMBINATIONS); if (!nl_combis) goto nla_put_failure; for (i = 0; i < wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *c; struct nlattr *nl_combi, *nl_limits; c = &wiphy->iface_combinations[i]; nl_combi = nla_nest_start(msg, i + 1); if (!nl_combi) goto nla_put_failure; nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); if (!nl_limits) goto nla_put_failure; for (j = 0; j < c->n_limits; j++) { struct nlattr *nl_limit; nl_limit = nla_nest_start(msg, j + 1); if (!nl_limit) goto nla_put_failure; if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX, c->limits[j].max)) goto nla_put_failure; if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, c->limits[j].types)) goto nla_put_failure; nla_nest_end(msg, nl_limit); } nla_nest_end(msg, nl_limits); if (c->beacon_int_infra_match && nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH)) goto nla_put_failure; if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, c->num_different_channels) || nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM, c->max_interfaces)) goto nla_put_failure; nla_nest_end(msg, nl_combi); } nla_nest_end(msg, nl_combis); return 0; nla_put_failure: return -ENOBUFS; } static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *dev) { void *hdr; struct nlattr *nl_bands, *nl_band; struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_rates, *nl_rate; struct nlattr *nl_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; struct ieee80211_rate *rate; int i; const struct ieee80211_txrx_stypes *mgmt_stypes = dev->wiphy.mgmt_stypes; hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); if (!hdr) return -1; if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) || nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) || nla_put_u32(msg, NL80211_ATTR_GENERATION, cfg80211_rdev_list_generation) || nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, dev->wiphy.retry_short) || nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, dev->wiphy.retry_long) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, dev->wiphy.frag_threshold) || nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, dev->wiphy.rts_threshold) || nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, dev->wiphy.coverage_class) || nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, dev->wiphy.max_scan_ssids) || nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, dev->wiphy.max_sched_scan_ssids) || nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, dev->wiphy.max_scan_ie_len) || nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, dev->wiphy.max_sched_scan_ie_len) || nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS, dev->wiphy.max_match_sets)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) && nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP)) goto nla_put_failure; if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES, sizeof(u32) * dev->wiphy.n_cipher_suites, dev->wiphy.cipher_suites)) goto nla_put_failure; if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, dev->wiphy.max_num_pmkids)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE)) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, dev->wiphy.available_antennas_tx) || nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, dev->wiphy.available_antennas_rx)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) && nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD, dev->wiphy.probe_resp_offload)) goto nla_put_failure; if ((dev->wiphy.available_antennas_tx || dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) { u32 tx_ant = 0, rx_ant = 0; int res; res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant); if (!res) { if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant) || nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant)) goto nla_put_failure; } } if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, dev->wiphy.interface_modes)) goto nla_put_failure; nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); if (!nl_bands) goto nla_put_failure; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!dev->wiphy.bands[band]) continue; nl_band = nla_nest_start(msg, band); if (!nl_band) goto nla_put_failure; /* add HT info */ if (dev->wiphy.bands[band]->ht_cap.ht_supported && (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET, sizeof(dev->wiphy.bands[band]->ht_cap.mcs), &dev->wiphy.bands[band]->ht_cap.mcs) || nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA, dev->wiphy.bands[band]->ht_cap.cap) || nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, dev->wiphy.bands[band]->ht_cap.ampdu_factor) || nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, dev->wiphy.bands[band]->ht_cap.ampdu_density))) goto nla_put_failure; /* add VHT info */ if (dev->wiphy.bands[band]->vht_cap.vht_supported && (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET, sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs), &dev->wiphy.bands[band]->vht_cap.vht_mcs) || nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA, dev->wiphy.bands[band]->vht_cap.cap))) goto nla_put_failure; /* add frequencies */ nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); if (!nl_freqs) goto nla_put_failure; for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) { nl_freq = nla_nest_start(msg, i); if (!nl_freq) goto nla_put_failure; chan = &dev->wiphy.bands[band]->channels[i]; if (nl80211_msg_put_channel(msg, chan)) goto nla_put_failure; nla_nest_end(msg, nl_freq); } nla_nest_end(msg, nl_freqs); /* add bitrates */ nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES); if (!nl_rates) goto nla_put_failure; for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) { nl_rate = nla_nest_start(msg, i); if (!nl_rate) goto nla_put_failure; rate = &dev->wiphy.bands[band]->bitrates[i]; if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE, rate->bitrate)) goto nla_put_failure; if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) && nla_put_flag(msg, NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE)) goto nla_put_failure; nla_nest_end(msg, nl_rate); } nla_nest_end(msg, nl_rates); nla_nest_end(msg, nl_band); } nla_nest_end(msg, nl_bands); nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); if (!nl_cmds) goto nla_put_failure; i = 0; #define CMD(op, n) \ do { \ if (dev->ops->op) { \ i++; \ if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \ goto nla_put_failure; \ } \ } while (0) CMD(add_virtual_intf, NEW_INTERFACE); CMD(change_virtual_intf, SET_INTERFACE); CMD(add_key, NEW_KEY); CMD(start_ap, START_AP); CMD(add_station, NEW_STATION); CMD(add_mpath, NEW_MPATH); CMD(update_mesh_config, SET_MESH_CONFIG); CMD(change_bss, SET_BSS); CMD(auth, AUTHENTICATE); CMD(assoc, ASSOCIATE); CMD(deauth, DEAUTHENTICATE); CMD(disassoc, DISASSOCIATE); CMD(join_ibss, JOIN_IBSS); CMD(join_mesh, JOIN_MESH); CMD(set_pmksa, SET_PMKSA); CMD(del_pmksa, DEL_PMKSA); CMD(flush_pmksa, FLUSH_PMKSA); if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) CMD(remain_on_channel, REMAIN_ON_CHANNEL); CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); CMD(mgmt_tx, FRAME); CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { i++; if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) goto nla_put_failure; } if (dev->ops->set_monitor_channel || dev->ops->start_ap || dev->ops->join_mesh) { i++; if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) goto nla_put_failure; } CMD(set_wds_peer, SET_WDS_PEER); if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { CMD(tdls_mgmt, TDLS_MGMT); CMD(tdls_oper, TDLS_OPER); } if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) CMD(sched_scan_start, START_SCHED_SCAN); CMD(probe_client, PROBE_CLIENT); CMD(set_noack_map, SET_NOACK_MAP); if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { i++; if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS)) goto nla_put_failure; } CMD(start_p2p_device, START_P2P_DEVICE); #ifdef CONFIG_NL80211_TESTMODE CMD(testmode_cmd, TESTMODE); #endif #undef CMD if (dev->ops->connect || dev->ops->auth) { i++; if (nla_put_u32(msg, i, NL80211_CMD_CONNECT)) goto nla_put_failure; } if (dev->ops->disconnect || dev->ops->deauth) { i++; if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT)) goto nla_put_failure; } nla_nest_end(msg, nl_cmds); if (dev->ops->remain_on_channel && (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, dev->wiphy.max_remain_on_channel_duration)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) && nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) goto nla_put_failure; if (mgmt_stypes) { u16 stypes; struct nlattr *nl_ftypes, *nl_ifs; enum nl80211_iftype ift; nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES); if (!nl_ifs) goto nla_put_failure; for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { nl_ftypes = nla_nest_start(msg, ift); if (!nl_ftypes) goto nla_put_failure; i = 0; stypes = mgmt_stypes[ift].tx; while (stypes) { if ((stypes & 1) && nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, (i << 4) | IEEE80211_FTYPE_MGMT)) goto nla_put_failure; stypes >>= 1; i++; } nla_nest_end(msg, nl_ftypes); } nla_nest_end(msg, nl_ifs); nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES); if (!nl_ifs) goto nla_put_failure; for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { nl_ftypes = nla_nest_start(msg, ift); if (!nl_ftypes) goto nla_put_failure; i = 0; stypes = mgmt_stypes[ift].rx; while (stypes) { if ((stypes & 1) && nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, (i << 4) | IEEE80211_FTYPE_MGMT)) goto nla_put_failure; stypes >>= 1; i++; } nla_nest_end(msg, nl_ftypes); } nla_nest_end(msg, nl_ifs); } #ifdef CONFIG_PM if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { struct nlattr *nl_wowlan; nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); if (!nl_wowlan) goto nla_put_failure; if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) || ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) && nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) goto nla_put_failure; if (dev->wiphy.wowlan.n_patterns) { struct nl80211_wowlan_pattern_support pat = { .max_patterns = dev->wiphy.wowlan.n_patterns, .min_pattern_len = dev->wiphy.wowlan.pattern_min_len, .max_pattern_len = dev->wiphy.wowlan.pattern_max_len, }; if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, sizeof(pat), &pat)) goto nla_put_failure; } nla_nest_end(msg, nl_wowlan); } #endif if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, dev->wiphy.software_iftypes)) goto nla_put_failure; if (nl80211_put_iface_combinations(&dev->wiphy, msg)) goto nla_put_failure; if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME, dev->wiphy.ap_sme_capa)) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features)) goto nla_put_failure; if (dev->wiphy.ht_capa_mod_mask && nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sizeof(*dev->wiphy.ht_capa_mod_mask), dev->wiphy.ht_capa_mod_mask)) goto nla_put_failure; return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) { int idx = 0; int start = cb->args[0]; struct cfg80211_registered_device *dev; mutex_lock(&cfg80211_mutex); list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) continue; if (++idx <= start) continue; if (nl80211_send_wiphy(skb, NETLINK_CB_PORTID(skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) { idx--; break; } } mutex_unlock(&cfg80211_mutex); cb->args[0] = idx; return skb->len; } static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *msg; struct cfg80211_registered_device *dev = info->user_ptr[0]; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; if (nl80211_send_wiphy(msg, genl_info_snd_portid(info), info->snd_seq, 0, dev) < 0) { nlmsg_free(msg); return -ENOBUFS; } return genlmsg_reply(msg, info); } static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, }; static int parse_txq_params(struct nlattr *tb[], struct ieee80211_txq_params *txq_params) { if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] || !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || !tb[NL80211_TXQ_ATTR_AIFS]) return -EINVAL; txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]); txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); if (txq_params->ac >= NL80211_NUM_ACS) return -EINVAL; return 0; } static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) { /* * You can only set the channel explicitly for WDS interfaces, * all others have their channel managed via their respective * "establish a connection" command (connect, join, ...) * * For AP/GO and mesh mode, the channel can be set with the * channel userspace API, but is only stored and passed to the * low-level driver when the AP starts or the mesh is joined. * This is for backward compatibility, userspace can also give * the channel in the start-ap or join-mesh commands instead. * * Monitors are special as they are normally slaved to * whatever else is going on, so they have their own special * operation to set the monitor channel if possible. */ return !wdev || wdev->iftype == NL80211_IFTYPE_AP || wdev->iftype == NL80211_IFTYPE_MESH_POINT || wdev->iftype == NL80211_IFTYPE_MONITOR || wdev->iftype == NL80211_IFTYPE_P2P_GO; } static bool nl80211_valid_channel_type(struct genl_info *info, enum nl80211_channel_type *channel_type) { enum nl80211_channel_type tmp; if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) return false; tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); if (tmp != NL80211_CHAN_NO_HT && tmp != NL80211_CHAN_HT20 && tmp != NL80211_CHAN_HT40PLUS && tmp != NL80211_CHAN_HT40MINUS) return false; if (channel_type) *channel_type = tmp; return true; } static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct genl_info *info) { struct ieee80211_channel *channel; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; u32 freq; int result; enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; if (wdev) iftype = wdev->iftype; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; if (!nl80211_can_set_dev_channel(wdev)) return -EOPNOTSUPP; if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && !nl80211_valid_channel_type(info, &channel_type)) return -EINVAL; freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); mutex_lock(&rdev->devlist_mtx); switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (wdev->beacon_interval) { result = -EBUSY; break; } channel = rdev_freq_to_chan(rdev, freq, channel_type); if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, channel, channel_type)) { result = -EINVAL; break; } wdev->preset_chan = channel; wdev->preset_chantype = channel_type; result = 0; break; case NL80211_IFTYPE_MESH_POINT: result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); break; case NL80211_IFTYPE_MONITOR: result = cfg80211_set_monitor_channel(rdev, freq, channel_type); break; default: result = -EINVAL; } mutex_unlock(&rdev->devlist_mtx); return result; } static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *netdev = info->user_ptr[1]; return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); } static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *bssid; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (netif_running(dev)) return -EBUSY; if (!rdev->ops->set_wds_peer) return -EOPNOTSUPP; if (wdev->iftype != NL80211_IFTYPE_WDS) return -EOPNOTSUPP; bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid); } static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; struct net_device *netdev = NULL; struct wireless_dev *wdev; int result = 0, rem_txq_params = 0; struct nlattr *nl_txq_params; u32 changed; u8 retry_short = 0, retry_long = 0; u32 frag_threshold = 0, rts_threshold = 0; u8 coverage_class = 0; /* * Try to find the wiphy and netdev. Normally this * function shouldn't need the netdev, but this is * done for backward compatibility -- previously * setting the channel was done per wiphy, but now * it is per netdev. Previous userland like hostapd * also passed a netdev to set_wiphy, so that it is * possible to let that go to the right netdev! */ mutex_lock(&cfg80211_mutex); if (info->attrs[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); netdev = dev_get_by_index(genl_info_net(info), ifindex); if (netdev && netdev->ieee80211_ptr) { rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); mutex_lock(&rdev->mtx); } else netdev = NULL; } if (!netdev) { rdev = __cfg80211_rdev_from_attrs(genl_info_net(info), info->attrs); if (IS_ERR(rdev)) { mutex_unlock(&cfg80211_mutex); return PTR_ERR(rdev); } wdev = NULL; netdev = NULL; result = 0; mutex_lock(&rdev->mtx); } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) wdev = netdev->ieee80211_ptr; else wdev = NULL; /* * end workaround code, by now the rdev is available * and locked, and wdev may or may not be NULL. */ if (info->attrs[NL80211_ATTR_WIPHY_NAME]) result = cfg80211_dev_rename( rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); mutex_unlock(&cfg80211_mutex); if (result) goto bad_res; if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { struct ieee80211_txq_params txq_params; struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; if (!rdev->ops->set_txq_params) { result = -EOPNOTSUPP; goto bad_res; } if (!netdev) { result = -EINVAL; goto bad_res; } if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { result = -EINVAL; goto bad_res; } if (!netif_running(netdev)) { result = -ENETDOWN; goto bad_res; } nla_for_each_nested(nl_txq_params, info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], rem_txq_params) { nla_parse(tb, NL80211_TXQ_ATTR_MAX, nla_data(nl_txq_params), nla_len(nl_txq_params), txq_params_policy); result = parse_txq_params(tb, &txq_params); if (result) goto bad_res; result = rdev->ops->set_txq_params(&rdev->wiphy, netdev, &txq_params); if (result) goto bad_res; } } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { result = __nl80211_set_channel(rdev, wdev, info); if (result) goto bad_res; } if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { enum nl80211_tx_power_setting type; int idx, mbm = 0; if (!rdev->ops->set_tx_power) { result = -EOPNOTSUPP; goto bad_res; } idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING; type = nla_get_u32(info->attrs[idx]); if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] && (type != NL80211_TX_POWER_AUTOMATIC)) { result = -EINVAL; goto bad_res; } if (type != NL80211_TX_POWER_AUTOMATIC) { idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL; mbm = nla_get_u32(info->attrs[idx]); } result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); if (result) goto bad_res; } if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { u32 tx_ant, rx_ant; if ((!rdev->wiphy.available_antennas_tx && !rdev->wiphy.available_antennas_rx) || !rdev->ops->set_antenna) { result = -EOPNOTSUPP; goto bad_res; } tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); /* reject antenna configurations which don't match the * available antenna masks, except for the "all" mask */ if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) { result = -EINVAL; goto bad_res; } tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); if (result) goto bad_res; } changed = 0; if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { retry_short = nla_get_u8( info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]); if (retry_short == 0) { result = -EINVAL; goto bad_res; } changed |= WIPHY_PARAM_RETRY_SHORT; } if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) { retry_long = nla_get_u8( info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]); if (retry_long == 0) { result = -EINVAL; goto bad_res; } changed |= WIPHY_PARAM_RETRY_LONG; } if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) { frag_threshold = nla_get_u32( info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); if (frag_threshold < 256) { result = -EINVAL; goto bad_res; } if (frag_threshold != (u32) -1) { /* * Fragments (apart from the last one) are required to * have even length. Make the fragmentation code * simpler by stripping LSB should someone try to use * odd threshold value. */ frag_threshold &= ~0x1; } changed |= WIPHY_PARAM_FRAG_THRESHOLD; } if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { rts_threshold = nla_get_u32( info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); changed |= WIPHY_PARAM_RTS_THRESHOLD; } if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { coverage_class = nla_get_u8( info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); changed |= WIPHY_PARAM_COVERAGE_CLASS; } if (changed) { u8 old_retry_short, old_retry_long; u32 old_frag_threshold, old_rts_threshold; u8 old_coverage_class; if (!rdev->ops->set_wiphy_params) { result = -EOPNOTSUPP; goto bad_res; } old_retry_short = rdev->wiphy.retry_short; old_retry_long = rdev->wiphy.retry_long; old_frag_threshold = rdev->wiphy.frag_threshold; old_rts_threshold = rdev->wiphy.rts_threshold; old_coverage_class = rdev->wiphy.coverage_class; if (changed & WIPHY_PARAM_RETRY_SHORT) rdev->wiphy.retry_short = retry_short; if (changed & WIPHY_PARAM_RETRY_LONG) rdev->wiphy.retry_long = retry_long; if (changed & WIPHY_PARAM_FRAG_THRESHOLD) rdev->wiphy.frag_threshold = frag_threshold; if (changed & WIPHY_PARAM_RTS_THRESHOLD) rdev->wiphy.rts_threshold = rts_threshold; if (changed & WIPHY_PARAM_COVERAGE_CLASS) rdev->wiphy.coverage_class = coverage_class; result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); if (result) { rdev->wiphy.retry_short = old_retry_short; rdev->wiphy.retry_long = old_retry_long; rdev->wiphy.frag_threshold = old_frag_threshold; rdev->wiphy.rts_threshold = old_rts_threshold; rdev->wiphy.coverage_class = old_coverage_class; } } bad_res: mutex_unlock(&rdev->mtx); if (netdev) dev_put(netdev); return result; } static inline u64 wdev_id(struct wireless_dev *wdev) { return (u64)wdev->identifier | ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32); } static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; void *hdr; hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE); if (!hdr) return -1; if (dev && (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name))) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ (cfg80211_rdev_list_generation << 2))) goto nla_put_failure; if (rdev->ops->get_channel) { struct ieee80211_channel *chan; enum nl80211_channel_type channel_type; chan = rdev->ops->get_channel(&rdev->wiphy, wdev, &channel_type); if (chan && (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type))) goto nla_put_failure; } return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) { int wp_idx = 0; int if_idx = 0; int wp_start = cb->args[0]; int if_start = cb->args[1]; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; mutex_lock(&cfg80211_mutex); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) continue; if (wp_idx < wp_start) { wp_idx++; continue; } if_idx = 0; mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (if_idx < if_start) { if_idx++; continue; } if (nl80211_send_iface(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev) < 0) { mutex_unlock(&rdev->devlist_mtx); goto out; } if_idx++; } mutex_unlock(&rdev->devlist_mtx); wp_idx++; } out: mutex_unlock(&cfg80211_mutex); cb->args[0] = wp_idx; cb->args[1] = if_idx; return skb->len; } static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *msg; struct cfg80211_registered_device *dev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0, dev, wdev) < 0) { nlmsg_free(msg); return -ENOBUFS; } return genlmsg_reply(msg, info); } static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = { [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG }, [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG }, [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG }, [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, }; static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) { struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1]; int flag; *mntrflags = 0; if (!nla) return -EINVAL; if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, nla, mntr_flags_policy)) return -EINVAL; for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) if (flags[flag]) *mntrflags |= (1<wiphy.flags & WIPHY_FLAG_4ADDR_AP) return 0; break; case NL80211_IFTYPE_STATION: if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) return 0; break; default: break; } return -EOPNOTSUPP; } static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct vif_params params; int err; enum nl80211_iftype otype, ntype; struct net_device *dev = info->user_ptr[1]; u32 _flags, *flags = NULL; bool change = false; memset(¶ms, 0, sizeof(params)); otype = ntype = dev->ieee80211_ptr->iftype; if (info->attrs[NL80211_ATTR_IFTYPE]) { ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); if (otype != ntype) change = true; if (ntype > NL80211_IFTYPE_MAX) return -EINVAL; } if (info->attrs[NL80211_ATTR_MESH_ID]) { struct wireless_dev *wdev = dev->ieee80211_ptr; if (ntype != NL80211_IFTYPE_MESH_POINT) return -EINVAL; if (netif_running(dev)) return -EBUSY; wdev_lock(wdev); BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); wdev->mesh_id_up_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), wdev->mesh_id_up_len); wdev_unlock(wdev); } if (info->attrs[NL80211_ATTR_4ADDR]) { params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); change = true; err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype); if (err) return err; } else { params.use_4addr = -1; } if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { if (ntype != NL80211_IFTYPE_MONITOR) return -EINVAL; err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], &_flags); if (err) return err; flags = &_flags; change = true; } if (change) err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); else err = 0; if (!err && params.use_4addr != -1) dev->ieee80211_ptr->use_4addr = params.use_4addr; return err; } static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct vif_params params; struct wireless_dev *wdev; struct sk_buff *msg; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; u32 flags; memset(¶ms, 0, sizeof(params)); if (!info->attrs[NL80211_ATTR_IFNAME]) return -EINVAL; if (info->attrs[NL80211_ATTR_IFTYPE]) { type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); if (type > NL80211_IFTYPE_MAX) return -EINVAL; } if (!rdev->ops->add_virtual_intf || !(rdev->wiphy.interface_modes & (1 << type))) return -EOPNOTSUPP; if (info->attrs[NL80211_ATTR_4ADDR]) { params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); if (err) return err; } msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); wdev = rdev->ops->add_virtual_intf(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); if (IS_ERR(wdev)) { nlmsg_free(msg); return PTR_ERR(wdev); } switch (type) { case NL80211_IFTYPE_MESH_POINT: if (!info->attrs[NL80211_ATTR_MESH_ID]) break; wdev_lock(wdev); BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); wdev->mesh_id_up_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), wdev->mesh_id_up_len); wdev_unlock(wdev); break; case NL80211_IFTYPE_P2P_DEVICE: /* * P2P Device doesn't have a netdev, so doesn't go * through the netdev notifier and must be added here */ mutex_init(&wdev->mtx); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); mutex_lock(&rdev->devlist_mtx); wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wdev_list); rdev->devlist_generation++; mutex_unlock(&rdev->devlist_mtx); break; default: break; } if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0, rdev, wdev) < 0) { nlmsg_free(msg); return -ENOBUFS; } return genlmsg_reply(msg, info); } static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; if (!rdev->ops->del_virtual_intf) return -EOPNOTSUPP; /* * If we remove a wireless device without a netdev then clear * user_ptr[1] so that nl80211_post_doit won't dereference it * to check if it needs to do dev_put(). Otherwise it crashes * since the wdev has been freed, unlike with a netdev where * we need the dev_put() for the netdev to really be freed. */ if (!wdev->netdev) info->user_ptr[1] = NULL; return rdev->ops->del_virtual_intf(&rdev->wiphy, wdev); } static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u16 noack_map; if (!info->attrs[NL80211_ATTR_NOACK_MAP]) return -EINVAL; if (!rdev->ops->set_noack_map) return -EOPNOTSUPP; noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]); return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map); } struct get_key_cookie { struct sk_buff *msg; int error; int idx; }; static void get_key_callback(void *c, struct key_params *params) { struct nlattr *key; struct get_key_cookie *cookie = c; if ((params->key && nla_put(cookie->msg, NL80211_ATTR_KEY_DATA, params->key_len, params->key)) || (params->seq && nla_put(cookie->msg, NL80211_ATTR_KEY_SEQ, params->seq_len, params->seq)) || (params->cipher && nla_put_u32(cookie->msg, NL80211_ATTR_KEY_CIPHER, params->cipher))) goto nla_put_failure; key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY); if (!key) goto nla_put_failure; if ((params->key && nla_put(cookie->msg, NL80211_KEY_DATA, params->key_len, params->key)) || (params->seq && nla_put(cookie->msg, NL80211_KEY_SEQ, params->seq_len, params->seq)) || (params->cipher && nla_put_u32(cookie->msg, NL80211_KEY_CIPHER, params->cipher))) goto nla_put_failure; if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx)) goto nla_put_failure; nla_nest_end(cookie->msg, key); return; nla_put_failure: cookie->error = 1; } static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; struct net_device *dev = info->user_ptr[1]; u8 key_idx = 0; const u8 *mac_addr = NULL; bool pairwise; struct get_key_cookie cookie = { .error = 0, }; void *hdr; struct sk_buff *msg; if (info->attrs[NL80211_ATTR_KEY_IDX]) key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); if (key_idx > 5) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); pairwise = !!mac_addr; if (info->attrs[NL80211_ATTR_KEY_TYPE]) { u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); if (kt >= NUM_NL80211_KEYTYPES) return -EINVAL; if (kt != NL80211_KEYTYPE_GROUP && kt != NL80211_KEYTYPE_PAIRWISE) return -EINVAL; pairwise = kt == NL80211_KEYTYPE_PAIRWISE; } if (!rdev->ops->get_key) return -EOPNOTSUPP; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_NEW_KEY); if (IS_ERR(hdr)) return PTR_ERR(hdr); cookie.msg = msg; cookie.idx = key_idx; if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx)) goto nla_put_failure; if (mac_addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr)) goto nla_put_failure; if (pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) return -ENOENT; err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise, mac_addr, &cookie, get_key_callback); if (err) goto free_msg; if (cookie.error) goto nla_put_failure; genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); nla_put_failure: err = -ENOBUFS; free_msg: nlmsg_free(msg); return err; } static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct key_parse key; int err; struct net_device *dev = info->user_ptr[1]; err = nl80211_parse_key(info, &key); if (err) return err; if (key.idx < 0) return -EINVAL; /* only support setting default key */ if (!key.def && !key.defmgmt) return -EINVAL; wdev_lock(dev->ieee80211_ptr); if (key.def) { if (!rdev->ops->set_default_key) { err = -EOPNOTSUPP; goto out; } err = nl80211_key_allowed(dev->ieee80211_ptr); if (err) goto out; err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, key.def_uni, key.def_multi); if (err) goto out; #ifdef CONFIG_CFG80211_WEXT dev->ieee80211_ptr->wext.default_key = key.idx; #endif } else { if (key.def_uni || !key.def_multi) { err = -EINVAL; goto out; } if (!rdev->ops->set_default_mgmt_key) { err = -EOPNOTSUPP; goto out; } err = nl80211_key_allowed(dev->ieee80211_ptr); if (err) goto out; err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, dev, key.idx); if (err) goto out; #ifdef CONFIG_CFG80211_WEXT dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; #endif } out: wdev_unlock(dev->ieee80211_ptr); return err; } static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; struct net_device *dev = info->user_ptr[1]; struct key_parse key; const u8 *mac_addr = NULL; err = nl80211_parse_key(info, &key); if (err) return err; if (!key.p.key) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); if (key.type == -1) { if (mac_addr) key.type = NL80211_KEYTYPE_PAIRWISE; else key.type = NL80211_KEYTYPE_GROUP; } /* for now */ if (key.type != NL80211_KEYTYPE_PAIRWISE && key.type != NL80211_KEYTYPE_GROUP) return -EINVAL; if (!rdev->ops->add_key) return -EOPNOTSUPP; if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, key.type == NL80211_KEYTYPE_PAIRWISE, mac_addr)) return -EINVAL; wdev_lock(dev->ieee80211_ptr); err = nl80211_key_allowed(dev->ieee80211_ptr); if (!err) err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, key.type == NL80211_KEYTYPE_PAIRWISE, mac_addr, &key.p); wdev_unlock(dev->ieee80211_ptr); return err; } static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; struct net_device *dev = info->user_ptr[1]; u8 *mac_addr = NULL; struct key_parse key; err = nl80211_parse_key(info, &key); if (err) return err; if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); if (key.type == -1) { if (mac_addr) key.type = NL80211_KEYTYPE_PAIRWISE; else key.type = NL80211_KEYTYPE_GROUP; } /* for now */ if (key.type != NL80211_KEYTYPE_PAIRWISE && key.type != NL80211_KEYTYPE_GROUP) return -EINVAL; if (!rdev->ops->del_key) return -EOPNOTSUPP; wdev_lock(dev->ieee80211_ptr); err = nl80211_key_allowed(dev->ieee80211_ptr); if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) err = -ENOENT; if (!err) err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, key.type == NL80211_KEYTYPE_PAIRWISE, mac_addr); #ifdef CONFIG_CFG80211_WEXT if (!err) { if (key.idx == dev->ieee80211_ptr->wext.default_key) dev->ieee80211_ptr->wext.default_key = -1; else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key) dev->ieee80211_ptr->wext.default_mgmt_key = -1; } #endif wdev_unlock(dev->ieee80211_ptr); return err; } static int nl80211_parse_beacon(struct genl_info *info, struct cfg80211_beacon_data *bcn) { bool haveinfo = false; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) || !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) || !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) || !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP])) return -EINVAL; memset(bcn, 0, sizeof(*bcn)); if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); if (!bcn->head_len) return -EINVAL; haveinfo = true; } if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); bcn->tail_len = nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]); haveinfo = true; } if (!haveinfo) return -EINVAL; if (info->attrs[NL80211_ATTR_IE]) { bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) { bcn->proberesp_ies = nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); bcn->proberesp_ies_len = nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); } if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) { bcn->assocresp_ies = nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); bcn->assocresp_ies_len = nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); } if (info->attrs[NL80211_ATTR_PROBE_RESP]) { bcn->probe_resp = nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]); bcn->probe_resp_len = nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]); } return 0; } static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, struct cfg80211_ap_settings *params) { struct wireless_dev *wdev; bool ret = false; mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO) continue; if (!wdev->preset_chan) continue; params->channel = wdev->preset_chan; params->channel_type = wdev->preset_chantype; ret = true; break; } mutex_unlock(&rdev->devlist_mtx); return ret; } static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_ap_settings params; int err; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; if (!rdev->ops->start_ap) return -EOPNOTSUPP; if (wdev->beacon_interval) return -EALREADY; memset(¶ms, 0, sizeof(params)); /* these are required for START_AP */ if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || !info->attrs[NL80211_ATTR_DTIM_PERIOD] || !info->attrs[NL80211_ATTR_BEACON_HEAD]) return -EINVAL; err = nl80211_parse_beacon(info, ¶ms.beacon); if (err) return err; params.beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); params.dtim_period = nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); err = cfg80211_validate_beacon_int(rdev, params.beacon_interval); if (err) return err; /* * In theory, some of these attributes should be required here * but since they were not used when the command was originally * added, keep them optional for old user space programs to let * them continue to work with drivers that do not need the * additional information -- drivers must check! */ if (info->attrs[NL80211_ATTR_SSID]) { params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); params.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (params.ssid_len == 0 || params.ssid_len > IEEE80211_MAX_SSID_LEN) return -EINVAL; } if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { params.hidden_ssid = nla_get_u32( info->attrs[NL80211_ATTR_HIDDEN_SSID]); if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE && params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN && params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS) return -EINVAL; } params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { params.auth_type = nla_get_u32( info->attrs[NL80211_ATTR_AUTH_TYPE]); if (!nl80211_valid_auth_type(params.auth_type)) return -EINVAL; } else params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, NL80211_MAX_NR_CIPHER_SUITES); if (err) return err; if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) { if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER)) return -EOPNOTSUPP; params.inactivity_timeout = nla_get_u16( info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && !nl80211_valid_channel_type(info, &channel_type)) return -EINVAL; params.channel = rdev_freq_to_chan(rdev, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), channel_type); if (!params.channel) return -EINVAL; params.channel_type = channel_type; } else if (wdev->preset_chan) { params.channel = wdev->preset_chan; params.channel_type = wdev->preset_chantype; } else if (!nl80211_get_ap_channel(rdev, ¶ms)) return -EINVAL; if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel, params.channel_type)) return -EINVAL; mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_use_chan(rdev, wdev, params.channel, CHAN_MODE_SHARED); mutex_unlock(&rdev->devlist_mtx); if (err) return err; err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); if (!err) { wdev->preset_chan = params.channel; wdev->preset_chantype = params.channel_type; wdev->beacon_interval = params.beacon_interval; wdev->channel = params.channel; } return err; } static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_beacon_data params; int err; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; if (!rdev->ops->change_beacon) return -EOPNOTSUPP; if (!wdev->beacon_interval) return -EINVAL; err = nl80211_parse_beacon(info, ¶ms); if (err) return err; return rdev->ops->change_beacon(&rdev->wiphy, dev, ¶ms); } static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; return cfg80211_stop_ap(rdev, dev); } static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG }, }; static int parse_station_flags(struct genl_info *info, enum nl80211_iftype iftype, struct station_parameters *params) { struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; struct nlattr *nla; int flag; /* * Try parsing the new attribute first so userspace * can specify both for older kernels. */ nla = info->attrs[NL80211_ATTR_STA_FLAGS2]; if (nla) { struct nl80211_sta_flag_update *sta_flags; sta_flags = nla_data(nla); params->sta_flags_mask = sta_flags->mask; params->sta_flags_set = sta_flags->set; if ((params->sta_flags_mask | params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) return -EINVAL; return 0; } /* if present, parse the old attribute */ nla = info->attrs[NL80211_ATTR_STA_FLAGS]; if (!nla) return 0; if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, nla, sta_flags_policy)) return -EINVAL; /* * Only allow certain flags for interface types so that * other attributes are silently ignored. Remember that * this is backward compatibility code with old userspace * and shouldn't be hit in other cases anyway. */ switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_TDLS_PEER); break; case NL80211_IFTYPE_MESH_POINT: params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHORIZED); default: return -EINVAL; } for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) { if (flags[flag]) { params->sta_flags_set |= (1< NL80211_STA_FLAG_MAX_OLD_API) return -EINVAL; } } return 0; } static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) { struct nlattr *rate; u32 bitrate; u16 bitrate_compat; rate = nla_nest_start(msg, attr); if (!rate) goto nla_put_failure; /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ bitrate = cfg80211_calculate_bitrate(info); /* report 16-bit bitrate only if we can */ bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; if ((bitrate > 0 && nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) || (bitrate_compat > 0 && nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) || ((info->flags & RATE_INFO_FLAGS_MCS) && nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) || ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) && nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) || ((info->flags & RATE_INFO_FLAGS_SHORT_GI) && nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))) goto nla_put_failure; nla_nest_end(msg, rate); return true; nla_put_failure: return false; } static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo) { void *hdr; struct nlattr *sinfoattr, *bss_param; hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) return -1; if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) || nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation)) goto nla_put_failure; sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!sinfoattr) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) && nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME, sinfo->connected_time)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) && nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME, sinfo->inactive_time)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_RX_BYTES) && nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES, sinfo->rx_bytes)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_TX_BYTES) && nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES, sinfo->tx_bytes)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_LLID) && nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_PLID) && nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_PLINK_STATE) && nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE, sinfo->plink_state)) goto nla_put_failure; switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: if ((sinfo->filled & STATION_INFO_SIGNAL) && nla_put_u8(msg, NL80211_STA_INFO_SIGNAL, sinfo->signal)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) && nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG, sinfo->signal_avg)) goto nla_put_failure; break; default: break; } if (sinfo->filled & STATION_INFO_TX_BITRATE) { if (!nl80211_put_sta_rate(msg, &sinfo->txrate, NL80211_STA_INFO_TX_BITRATE)) goto nla_put_failure; } if (sinfo->filled & STATION_INFO_RX_BITRATE) { if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, NL80211_STA_INFO_RX_BITRATE)) goto nla_put_failure; } if ((sinfo->filled & STATION_INFO_RX_PACKETS) && nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS, sinfo->rx_packets)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_TX_PACKETS) && nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS, sinfo->tx_packets)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_TX_RETRIES) && nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES, sinfo->tx_retries)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_TX_FAILED) && nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED, sinfo->tx_failed)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) && nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, sinfo->beacon_loss_count)) goto nla_put_failure; if (sinfo->filled & STATION_INFO_BSS_PARAM) { bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); if (!bss_param) goto nla_put_failure; if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) && nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) || ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) && nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) || ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) && nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) || nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD, sinfo->bss_param.dtim_period) || nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, sinfo->bss_param.beacon_interval)) goto nla_put_failure; nla_nest_end(msg, bss_param); } if ((sinfo->filled & STATION_INFO_STA_FLAGS) && nla_put(msg, NL80211_STA_INFO_STA_FLAGS, sizeof(struct nl80211_sta_flag_update), &sinfo->sta_flags)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_T_OFFSET) && nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET, sinfo->t_offset)) goto nla_put_failure; nla_nest_end(msg, sinfoattr); if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) && nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, sinfo->assoc_req_ies)) goto nla_put_failure; return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } static int nl80211_dump_station(struct sk_buff *skb, struct netlink_callback *cb) { struct station_info sinfo; struct cfg80211_registered_device *dev; struct net_device *netdev; u8 mac_addr[ETH_ALEN]; int sta_idx = cb->args[1]; int err; err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); if (err) return err; if (!dev->ops->dump_station) { err = -EOPNOTSUPP; goto out_err; } while (1) { memset(&sinfo, 0, sizeof(sinfo)); err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, mac_addr, &sinfo); if (err == -ENOENT) break; if (err) goto out_err; if (nl80211_send_station(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, dev, netdev, mac_addr, &sinfo) < 0) goto out; sta_idx++; } out: cb->args[1] = sta_idx; err = skb->len; out_err: nl80211_finish_netdev_dump(dev); return err; } static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct station_info sinfo; struct sk_buff *msg; u8 *mac_addr = NULL; int err; memset(&sinfo, 0, sizeof(sinfo)); if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); if (!rdev->ops->get_station) return -EOPNOTSUPP; err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); if (err) return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; if (nl80211_send_station(msg, genl_info_snd_portid(info), info->snd_seq, 0, rdev, dev, mac_addr, &sinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; } return genlmsg_reply(msg, info); } /* * Get vlan interface making sure it is running and on the right wiphy. */ static struct net_device *get_vlan(struct genl_info *info, struct cfg80211_registered_device *rdev) { struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; struct net_device *v; int ret; if (!vlanattr) return NULL; v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr)); if (!v) return ERR_PTR(-ENODEV); if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) { ret = -EINVAL; goto error; } if (!netif_running(v)) { ret = -ENETDOWN; goto error; } return v; error: dev_put(v); return ERR_PTR(ret); } static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; struct net_device *dev = info->user_ptr[1]; struct station_parameters params; u8 *mac_addr = NULL; memset(¶ms, 0, sizeof(params)); params.listen_interval = -1; params.plink_state = -1; if (info->attrs[NL80211_ATTR_STA_AID]) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { params.supported_rates = nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); params.supported_rates_len = nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); } if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) params.ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); if (!rdev->ops->change_station) return -EOPNOTSUPP; if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) params.plink_state = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: /* disallow mesh-specific things */ if (params.plink_action) return -EINVAL; /* TDLS can't be set, ... */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) return -EINVAL; /* * ... but don't bother the driver with it. This works around * a hostapd/wpa_supplicant issue -- it always includes the * TLDS_PEER flag in the mask even for AP mode. */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); /* accept only the listed bits */ if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP))) return -EINVAL; /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) return PTR_ERR(params.vlan); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: /* * Don't allow userspace to change the TDLS_PEER flag, * but silently ignore attempts to change it since we * don't have state here to verify that it doesn't try * to change the flag. */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); /* fall through */ case NL80211_IFTYPE_ADHOC: /* disallow things sta doesn't support */ if (params.plink_action) return -EINVAL; if (params.ht_capa) return -EINVAL; if (params.listen_interval >= 0) return -EINVAL; /* reject any changes other than AUTHORIZED */ if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) return -EINVAL; break; case NL80211_IFTYPE_MESH_POINT: /* disallow things mesh doesn't support */ if (params.vlan) return -EINVAL; if (params.ht_capa) return -EINVAL; if (params.listen_interval >= 0) return -EINVAL; /* * No special handling for TDLS here -- the userspace * mesh code doesn't have this bug. */ if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHORIZED))) return -EINVAL; break; default: return -EOPNOTSUPP; } /* be aware of params.vlan when changing code here */ err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); if (params.vlan) dev_put(params.vlan); return err; } static struct nla_policy nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, }; static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; struct net_device *dev = info->user_ptr[1]; struct station_parameters params; u8 *mac_addr = NULL; memset(¶ms, 0, sizeof(params)); if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) return -EINVAL; if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) return -EINVAL; if (!info->attrs[NL80211_ATTR_STA_AID]) return -EINVAL; mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); params.supported_rates = nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); params.supported_rates_len = nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); if (!params.aid || params.aid > IEEE80211_MAX_AID) return -EINVAL; if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) params.ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); if (!rdev->ops->add_station) return -EOPNOTSUPP; if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: /* parse WME attributes if sta is WME capable */ if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) && info->attrs[NL80211_ATTR_STA_WME]) { struct nlattr *tb[NL80211_STA_WME_MAX + 1]; struct nlattr *nla; nla = info->attrs[NL80211_ATTR_STA_WME]; err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, nl80211_sta_wme_policy); if (err) return err; if (tb[NL80211_STA_WME_UAPSD_QUEUES]) params.uapsd_queues = nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]); if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) return -EINVAL; if (tb[NL80211_STA_WME_MAX_SP]) params.max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) return -EINVAL; params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; } /* TDLS peers cannot be added */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) return -EINVAL; /* but don't bother the driver with it */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) return PTR_ERR(params.vlan); break; case NL80211_IFTYPE_MESH_POINT: /* TDLS peers cannot be added */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) return -EINVAL; break; case NL80211_IFTYPE_STATION: /* Only TDLS peers can be added */ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) return -EINVAL; /* Can only add if TDLS ... */ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)) return -EOPNOTSUPP; /* ... with external setup is supported */ if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) return -EOPNOTSUPP; break; default: return -EOPNOTSUPP; } /* be aware of params.vlan when changing code here */ err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); if (params.vlan) dev_put(params.vlan); return err; } static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u8 *mac_addr = NULL; if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EINVAL; if (!rdev->ops->del_station) return -EOPNOTSUPP; return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); } static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct net_device *dev, u8 *dst, u8 *next_hop, struct mpath_info *pinfo) { void *hdr; struct nlattr *pinfoattr; hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) return -1; if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) || nla_put(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop) || nla_put_u32(msg, NL80211_ATTR_GENERATION, pinfo->generation)) goto nla_put_failure; pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); if (!pinfoattr) goto nla_put_failure; if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) && nla_put_u32(msg, NL80211_MPATH_INFO_FRAME_QLEN, pinfo->frame_qlen)) goto nla_put_failure; if (((pinfo->filled & MPATH_INFO_SN) && nla_put_u32(msg, NL80211_MPATH_INFO_SN, pinfo->sn)) || ((pinfo->filled & MPATH_INFO_METRIC) && nla_put_u32(msg, NL80211_MPATH_INFO_METRIC, pinfo->metric)) || ((pinfo->filled & MPATH_INFO_EXPTIME) && nla_put_u32(msg, NL80211_MPATH_INFO_EXPTIME, pinfo->exptime)) || ((pinfo->filled & MPATH_INFO_FLAGS) && nla_put_u8(msg, NL80211_MPATH_INFO_FLAGS, pinfo->flags)) || ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) && nla_put_u32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, pinfo->discovery_timeout)) || ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) && nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES, pinfo->discovery_retries))) goto nla_put_failure; nla_nest_end(msg, pinfoattr); return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } static int nl80211_dump_mpath(struct sk_buff *skb, struct netlink_callback *cb) { struct mpath_info pinfo; struct cfg80211_registered_device *dev; struct net_device *netdev; u8 dst[ETH_ALEN]; u8 next_hop[ETH_ALEN]; int path_idx = cb->args[1]; int err; err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); if (err) return err; if (!dev->ops->dump_mpath) { err = -EOPNOTSUPP; goto out_err; } if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { err = -EOPNOTSUPP; goto out_err; } while (1) { err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, dst, next_hop, &pinfo); if (err == -ENOENT) break; if (err) goto out_err; if (nl80211_send_mpath(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, netdev, dst, next_hop, &pinfo) < 0) goto out; path_idx++; } out: cb->args[1] = path_idx; err = skb->len; out_err: nl80211_finish_netdev_dump(dev); return err; } static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; struct net_device *dev = info->user_ptr[1]; struct mpath_info pinfo; struct sk_buff *msg; u8 *dst = NULL; u8 next_hop[ETH_ALEN]; memset(&pinfo, 0, sizeof(pinfo)); if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; dst = nla_data(info->attrs[NL80211_ATTR_MAC]); if (!rdev->ops->get_mpath) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); if (err) return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; if (nl80211_send_mpath(msg, genl_info_snd_portid(info), info->snd_seq, 0, dev, dst, next_hop, &pinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; } return genlmsg_reply(msg, info); } static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u8 *dst = NULL; u8 *next_hop = NULL; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) return -EINVAL; dst = nla_data(info->attrs[NL80211_ATTR_MAC]); next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); if (!rdev->ops->change_mpath) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); } static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u8 *dst = NULL; u8 *next_hop = NULL; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) return -EINVAL; dst = nla_data(info->attrs[NL80211_ATTR_MAC]); next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); if (!rdev->ops->add_mpath) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); } static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u8 *dst = NULL; if (info->attrs[NL80211_ATTR_MAC]) dst = nla_data(info->attrs[NL80211_ATTR_MAC]); if (!rdev->ops->del_mpath) return -EOPNOTSUPP; return rdev->ops->del_mpath(&rdev->wiphy, dev, dst); } static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct bss_parameters params; memset(¶ms, 0, sizeof(params)); /* default to not changing parameters */ params.use_cts_prot = -1; params.use_short_preamble = -1; params.use_short_slot_time = -1; params.ap_isolate = -1; params.ht_opmode = -1; if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) params.use_cts_prot = nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]); if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) params.use_short_preamble = nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) params.use_short_slot_time = nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { params.basic_rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); params.basic_rates_len = nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); } if (info->attrs[NL80211_ATTR_AP_ISOLATE]) params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) params.ht_opmode = nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); if (!rdev->ops->change_bss) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; return rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); } static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, }; static int parse_reg_rule(struct nlattr *tb[], struct ieee80211_reg_rule *reg_rule) { struct ieee80211_freq_range *freq_range = ®_rule->freq_range; struct ieee80211_power_rule *power_rule = ®_rule->power_rule; if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) return -EINVAL; if (!tb[NL80211_ATTR_FREQ_RANGE_START]) return -EINVAL; if (!tb[NL80211_ATTR_FREQ_RANGE_END]) return -EINVAL; if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) return -EINVAL; if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) return -EINVAL; reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); freq_range->start_freq_khz = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]); freq_range->end_freq_khz = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]); freq_range->max_bandwidth_khz = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); power_rule->max_eirp = nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) power_rule->max_antenna_gain = nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); return 0; } static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) { int r; char *data = NULL; enum nl80211_user_reg_hint_type user_reg_hint_type; /* * You should only get this when cfg80211 hasn't yet initialized * completely when built-in to the kernel right between the time * window between nl80211_init() and regulatory_init(), if that is * even possible. */ mutex_lock(&cfg80211_mutex); if (unlikely(!cfg80211_regdomain)) { mutex_unlock(&cfg80211_mutex); return -EINPROGRESS; } mutex_unlock(&cfg80211_mutex); if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) return -EINVAL; data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) user_reg_hint_type = nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); else user_reg_hint_type = NL80211_USER_REG_HINT_USER; switch (user_reg_hint_type) { case NL80211_USER_REG_HINT_USER: case NL80211_USER_REG_HINT_CELL_BASE: break; default: return -EINVAL; } r = regulatory_hint_user(data, user_reg_hint_type); return r; } static int nl80211_get_mesh_config(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct mesh_config cur_params; int err = 0; void *hdr; struct nlattr *pinfoattr; struct sk_buff *msg; if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; if (!rdev->ops->get_mesh_config) return -EOPNOTSUPP; wdev_lock(wdev); /* If not connected, get default parameters */ if (!wdev->mesh_id_len) memcpy(&cur_params, &default_mesh_config, sizeof(cur_params)); else err = rdev->ops->get_mesh_config(&rdev->wiphy, dev, &cur_params); wdev_unlock(wdev); if (err) return err; /* Draw up a netlink message to send back */ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_MESH_CONFIG); if (!hdr) goto out; pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); if (!pinfoattr) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put_u16(msg, NL80211_MESHCONF_RETRY_TIMEOUT, cur_params.dot11MeshRetryTimeout) || nla_put_u16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT, cur_params.dot11MeshConfirmTimeout) || nla_put_u16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT, cur_params.dot11MeshHoldingTimeout) || nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, cur_params.dot11MeshMaxPeerLinks) || nla_put_u8(msg, NL80211_MESHCONF_MAX_RETRIES, cur_params.dot11MeshMaxRetries) || nla_put_u8(msg, NL80211_MESHCONF_TTL, cur_params.dot11MeshTTL) || nla_put_u8(msg, NL80211_MESHCONF_ELEMENT_TTL, cur_params.element_ttl) || nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, cur_params.auto_open_plinks) || nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, cur_params.dot11MeshNbrOffsetMaxNeighbor) || nla_put_u8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, cur_params.dot11MeshHWMPmaxPREQretries) || nla_put_u32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME, cur_params.path_refresh_time) || nla_put_u16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, cur_params.min_discovery_timeout) || nla_put_u32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, cur_params.dot11MeshHWMPactivePathTimeout) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, cur_params.dot11MeshHWMPpreqMinInterval) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, cur_params.dot11MeshHWMPperrMinInterval) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, cur_params.dot11MeshHWMPnetDiameterTraversalTime) || nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, cur_params.dot11MeshHWMPRootMode) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL, cur_params.dot11MeshHWMPRannInterval) || nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, cur_params.dot11MeshGateAnnouncementProtocol) || nla_put_u8(msg, NL80211_MESHCONF_FORWARDING, cur_params.dot11MeshForwarding) || nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, cur_params.rssi_threshold) || nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE, cur_params.ht_opmode) || nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, cur_params.dot11MeshHWMPactivePathToRootTimeout) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, cur_params.dot11MeshHWMProotInterval) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, cur_params.dot11MeshHWMPconfirmationInterval)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); nla_put_failure: genlmsg_cancel(msg, hdr); out: nlmsg_free(msg); return -ENOBUFS; } static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 }, [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 }, [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 }, [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 }, [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 }, [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 }, [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, }; static const struct nla_policy nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, }; static int nl80211_parse_mesh_config(struct genl_info *info, struct mesh_config *cfg, u32 *mask_out) { struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; u32 mask = 0; #define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ do {\ if (table[attr_num]) {\ cfg->param = nla_fn(table[attr_num]); \ mask |= (1 << (attr_num - 1)); \ } \ } while (0);\ if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) return -EINVAL; if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, info->attrs[NL80211_ATTR_MESH_CONFIG], nl80211_meshconf_params_policy)) return -EINVAL; /* This makes sure that there aren't more than 32 mesh config * parameters (otherwise our bitfield scheme would not work.) */ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); /* Fill in the params struct */ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, mask, NL80211_MESHCONF_TTL, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPnetDiameterTraversalTime, mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshGateAnnouncementProtocol, mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, mask, NL80211_MESHCONF_FORWARDING, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, mask, NL80211_MESHCONF_HT_OPMODE, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, mask, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPconfirmationInterval, mask, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, nla_get_u16); if (mask_out) *mask_out = mask; return 0; #undef FILL_IN_MESH_PARAM_IF_SET } static int nl80211_parse_mesh_setup(struct genl_info *info, struct mesh_setup *setup) { struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; if (!info->attrs[NL80211_ATTR_MESH_SETUP]) return -EINVAL; if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX, info->attrs[NL80211_ATTR_MESH_SETUP], nl80211_mesh_setup_params_policy)) return -EINVAL; if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC]) setup->sync_method = (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ? IEEE80211_SYNC_METHOD_VENDOR : IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET; if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) setup->path_sel_proto = (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? IEEE80211_PATH_PROTOCOL_VENDOR : IEEE80211_PATH_PROTOCOL_HWMP; if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC]) setup->path_metric = (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ? IEEE80211_PATH_METRIC_VENDOR : IEEE80211_PATH_METRIC_AIRTIME; if (tb[NL80211_MESH_SETUP_IE]) { struct nlattr *ieattr = tb[NL80211_MESH_SETUP_IE]; if (!is_valid_ie_attr(ieattr)) return -EINVAL; setup->ie = nla_data(ieattr); setup->ie_len = nla_len(ieattr); } setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); return 0; } static int nl80211_update_mesh_config(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct mesh_config cfg; u32 mask; int err; if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; if (!rdev->ops->update_mesh_config) return -EOPNOTSUPP; err = nl80211_parse_mesh_config(info, &cfg, &mask); if (err) return err; wdev_lock(wdev); if (!wdev->mesh_id_len) err = -ENOLINK; if (!err) err = rdev->ops->update_mesh_config(&rdev->wiphy, dev, mask, &cfg); wdev_unlock(wdev); return err; } static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *msg; void *hdr = NULL; struct nlattr *nl_reg_rules; unsigned int i; int err = -EINVAL; mutex_lock(&cfg80211_mutex); if (!cfg80211_regdomain) goto out; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { err = -ENOBUFS; goto out; } hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_REG); if (!hdr) goto put_failure; if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, cfg80211_regdomain->alpha2) || (cfg80211_regdomain->dfs_region && nla_put_u8(msg, NL80211_ATTR_DFS_REGION, cfg80211_regdomain->dfs_region))) goto nla_put_failure; if (reg_last_request_cell_base() && nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, NL80211_USER_REG_HINT_CELL_BASE)) goto nla_put_failure; nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); if (!nl_reg_rules) goto nla_put_failure; for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { struct nlattr *nl_reg_rule; const struct ieee80211_reg_rule *reg_rule; const struct ieee80211_freq_range *freq_range; const struct ieee80211_power_rule *power_rule; reg_rule = &cfg80211_regdomain->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; nl_reg_rule = nla_nest_start(msg, i); if (!nl_reg_rule) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, reg_rule->flags) || nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START, freq_range->start_freq_khz) || nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END, freq_range->end_freq_khz) || nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW, freq_range->max_bandwidth_khz) || nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, power_rule->max_antenna_gain) || nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, power_rule->max_eirp)) goto nla_put_failure; nla_nest_end(msg, nl_reg_rule); } nla_nest_end(msg, nl_reg_rules); genlmsg_end(msg, hdr); err = genlmsg_reply(msg, info); goto out; nla_put_failure: genlmsg_cancel(msg, hdr); put_failure: nlmsg_free(msg); err = -EMSGSIZE; out: mutex_unlock(&cfg80211_mutex); return err; } static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) { struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; struct nlattr *nl_reg_rule; char *alpha2 = NULL; int rem_reg_rules = 0, r = 0; u32 num_rules = 0, rule_idx = 0, size_of_regd; u8 dfs_region = 0; struct ieee80211_regdomain *rd = NULL; if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) return -EINVAL; if (!info->attrs[NL80211_ATTR_REG_RULES]) return -EINVAL; alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); if (info->attrs[NL80211_ATTR_DFS_REGION]) dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]); nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], rem_reg_rules) { num_rules++; if (num_rules > NL80211_MAX_SUPP_REG_RULES) return -EINVAL; } mutex_lock(&cfg80211_mutex); if (!reg_is_valid_request(alpha2)) { r = -EINVAL; goto bad_reg; } size_of_regd = sizeof(struct ieee80211_regdomain) + (num_rules * sizeof(struct ieee80211_reg_rule)); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) { r = -ENOMEM; goto bad_reg; } rd->n_reg_rules = num_rules; rd->alpha2[0] = alpha2[0]; rd->alpha2[1] = alpha2[1]; /* * Disable DFS master mode if the DFS region was * not supported or known on this kernel. */ if (reg_supported_dfs_region(dfs_region)) rd->dfs_region = dfs_region; nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], rem_reg_rules) { nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, nla_data(nl_reg_rule), nla_len(nl_reg_rule), reg_rule_policy); r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); if (r) goto bad_reg; rule_idx++; if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { r = -EINVAL; goto bad_reg; } } BUG_ON(rule_idx != num_rules); r = set_regdom(rd); mutex_unlock(&cfg80211_mutex); return r; bad_reg: mutex_unlock(&cfg80211_mutex); kfree(rd); return r; } static int validate_scan_freqs(struct nlattr *freqs) { struct nlattr *attr1, *attr2; int n_channels = 0, tmp1, tmp2; nla_for_each_nested(attr1, freqs, tmp1) { n_channels++; /* * Some hardware has a limited channel list for * scanning, and it is pretty much nonsensical * to scan for a channel twice, so disallow that * and don't require drivers to check that the * channel list they get isn't longer than what * they can scan, as long as they can scan all * the channels they registered at once. */ nla_for_each_nested(attr2, freqs, tmp2) if (attr1 != attr2 && nla_get_u32(attr1) == nla_get_u32(attr2)) return 0; } return n_channels; } static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; struct cfg80211_scan_request *request; struct nlattr *attr; struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_channels, i; size_t ie_len; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; wiphy = &rdev->wiphy; if (!rdev->ops->scan) return -EOPNOTSUPP; if (rdev->scan_req) return -EBUSY; if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { n_channels = validate_scan_freqs( info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); if (!n_channels) return -EINVAL; } else { enum ieee80211_band band; n_channels = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) if (wiphy->bands[band]) n_channels += wiphy->bands[band]->n_channels; } if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) n_ssids++; if (n_ssids > wiphy->max_scan_ssids) return -EINVAL; if (info->attrs[NL80211_ATTR_IE]) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); else ie_len = 0; if (ie_len > wiphy->max_scan_ie_len) return -EINVAL; request = kzalloc(sizeof(*request) + sizeof(*request->ssids) * n_ssids + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); if (!request) return -ENOMEM; if (n_ssids) request->ssids = (void *)&request->channels[n_channels]; request->n_ssids = n_ssids; if (ie_len) { if (request->ssids) request->ie = (void *)(request->ssids + n_ssids); else request->ie = (void *)(request->channels + n_channels); } i = 0; if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { /* user specified, bail out if channel not found */ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { struct ieee80211_channel *chan; chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); if (!chan) { err = -EINVAL; goto out_free; } /* ignore disabled channels */ if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[i] = chan; i++; } } else { enum ieee80211_band band; /* all channels */ for (band = 0; band < IEEE80211_NUM_BANDS; band++) { int j; if (!wiphy->bands[band]) continue; for (j = 0; j < wiphy->bands[band]->n_channels; j++) { struct ieee80211_channel *chan; chan = &wiphy->bands[band]->channels[j]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[i] = chan; i++; } } } if (!i) { err = -EINVAL; goto out_free; } request->n_channels = i; i = 0; if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; goto out_free; } request->ssids[i].ssid_len = nla_len(attr); memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); i++; } } if (info->attrs[NL80211_ATTR_IE]) { request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); memcpy((void *)request->ie, nla_data(info->attrs[NL80211_ATTR_IE]), request->ie_len); } for (i = 0; i < IEEE80211_NUM_BANDS; i++) if (wiphy->bands[i]) request->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], tmp) { enum ieee80211_band band = nla_type(attr); if (band < 0 || band >= IEEE80211_NUM_BANDS) { err = -EINVAL; goto out_free; } err = ieee80211_get_ratemask(wiphy->bands[band], nla_data(attr), nla_len(attr), &request->rates[band]); if (err) goto out_free; } } request->no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); request->wdev = wdev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; err = rdev->ops->scan(&rdev->wiphy, request); if (!err) { nl80211_send_scan_start(rdev, wdev); if (wdev->netdev) dev_hold(wdev->netdev); } else { out_free: rdev->scan_req = NULL; kfree(request); } return err; } static int nl80211_start_sched_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_sched_scan_request *request; struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct nlattr *attr; struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i; u32 interval; enum ieee80211_band band; size_t ie_len; struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || !rdev->ops->sched_scan_start) return -EOPNOTSUPP; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) return -EINVAL; interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); if (interval == 0) return -EINVAL; wiphy = &rdev->wiphy; if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { n_channels = validate_scan_freqs( info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); if (!n_channels) return -EINVAL; } else { n_channels = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) if (wiphy->bands[band]) n_channels += wiphy->bands[band]->n_channels; } if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) n_ssids++; if (n_ssids > wiphy->max_sched_scan_ssids) return -EINVAL; if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], tmp) n_match_sets++; if (n_match_sets > wiphy->max_match_sets) return -EINVAL; if (info->attrs[NL80211_ATTR_IE]) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); else ie_len = 0; if (ie_len > wiphy->max_sched_scan_ie_len) return -EINVAL; mutex_lock(&rdev->sched_scan_mtx); if (rdev->sched_scan_req) { err = -EINPROGRESS; goto out; } request = kzalloc(sizeof(*request) + sizeof(*request->ssids) * n_ssids + sizeof(*request->match_sets) * n_match_sets + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); if (!request) { err = -ENOMEM; goto out; } if (n_ssids) request->ssids = (void *)&request->channels[n_channels]; request->n_ssids = n_ssids; if (ie_len) { if (request->ssids) request->ie = (void *)(request->ssids + n_ssids); else request->ie = (void *)(request->channels + n_channels); } if (n_match_sets) { if (request->ie) request->match_sets = (void *)(request->ie + ie_len); else if (request->ssids) request->match_sets = (void *)(request->ssids + n_ssids); else request->match_sets = (void *)(request->channels + n_channels); } request->n_match_sets = n_match_sets; i = 0; if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { /* user specified, bail out if channel not found */ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { struct ieee80211_channel *chan; chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); if (!chan) { err = -EINVAL; goto out_free; } /* ignore disabled channels */ if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[i] = chan; i++; } } else { /* all channels */ for (band = 0; band < IEEE80211_NUM_BANDS; band++) { int j; if (!wiphy->bands[band]) continue; for (j = 0; j < wiphy->bands[band]->n_channels; j++) { struct ieee80211_channel *chan; chan = &wiphy->bands[band]->channels[j]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[i] = chan; i++; } } } if (!i) { err = -EINVAL; goto out_free; } request->n_channels = i; i = 0; if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; goto out_free; } request->ssids[i].ssid_len = nla_len(attr); memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); i++; } } i = 0; if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], tmp) { struct nlattr *ssid, *rssi; nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, nla_data(attr), nla_len(attr), nl80211_match_policy); ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; if (ssid) { if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; goto out_free; } memcpy(request->match_sets[i].ssid.ssid, nla_data(ssid), nla_len(ssid)); request->match_sets[i].ssid.ssid_len = nla_len(ssid); } rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; if (rssi) request->rssi_thold = nla_get_u32(rssi); else request->rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF; i++; } } if (info->attrs[NL80211_ATTR_IE]) { request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); memcpy((void *)request->ie, nla_data(info->attrs[NL80211_ATTR_IE]), request->ie_len); } request->dev = dev; request->wiphy = &rdev->wiphy; request->interval = interval; err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); if (!err) { rdev->sched_scan_req = request; nl80211_send_sched_scan(rdev, dev, NL80211_CMD_START_SCHED_SCAN); goto out; } out_free: kfree(request); out: mutex_unlock(&rdev->sched_scan_mtx); return err; } static int nl80211_stop_sched_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || !rdev->ops->sched_scan_stop) return -EOPNOTSUPP; mutex_lock(&rdev->sched_scan_mtx); err = __cfg80211_stop_sched_scan(rdev, false); mutex_unlock(&rdev->sched_scan_mtx); return err; } static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_internal_bss *intbss) { struct cfg80211_bss *res = &intbss->pub; void *hdr; struct nlattr *bss; ASSERT_WDEV_LOCK(wdev); hdr = nl80211hdr_put(msg, NETLINK_CB_PORTID(cb->skb), seq, flags, NL80211_CMD_NEW_SCAN_RESULTS); if (!hdr) return -1; genl_dump_check_consistent(cb, hdr, &nl80211_fam); if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) goto nla_put_failure; bss = nla_nest_start(msg, NL80211_ATTR_BSS); if (!bss) goto nla_put_failure; if ((!is_zero_ether_addr(res->bssid) && nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) || (res->information_elements && res->len_information_elements && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, res->len_information_elements, res->information_elements)) || (res->beacon_ies && res->len_beacon_ies && res->beacon_ies != res->information_elements && nla_put(msg, NL80211_BSS_BEACON_IES, res->len_beacon_ies, res->beacon_ies))) goto nla_put_failure; if (res->tsf && nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) goto nla_put_failure; if (res->beacon_interval && nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval)) goto nla_put_failure; if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) || nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) || nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO, jiffies_to_msecs(jiffies - intbss->ts))) goto nla_put_failure; switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal)) goto nla_put_failure; break; case CFG80211_SIGNAL_TYPE_UNSPEC: if (nla_put_u8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal)) goto nla_put_failure; break; default: break; } switch (wdev->iftype) { case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: if (intbss == wdev->current_bss && nla_put_u32(msg, NL80211_BSS_STATUS, NL80211_BSS_STATUS_ASSOCIATED)) goto nla_put_failure; break; case NL80211_IFTYPE_ADHOC: if (intbss == wdev->current_bss && nla_put_u32(msg, NL80211_BSS_STATUS, NL80211_BSS_STATUS_IBSS_JOINED)) goto nla_put_failure; break; default: break; } nla_nest_end(msg, bss); return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) { struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_internal_bss *scan; struct wireless_dev *wdev; int start = cb->args[1], idx = 0; int err; err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev); if (err) return err; wdev = dev->ieee80211_ptr; wdev_lock(wdev); spin_lock_bh(&rdev->bss_lock); cfg80211_bss_expire(rdev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) cb->seq = rdev->bss_generation; #endif list_for_each_entry(scan, &rdev->bss_list, list) { if (++idx <= start) continue; if (nl80211_send_bss(skb, cb, cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev, scan) < 0) { idx--; break; } } spin_unlock_bh(&rdev->bss_lock); wdev_unlock(wdev); cb->args[1] = idx; nl80211_finish_netdev_dump(rdev); return skb->len; } static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct net_device *dev, struct survey_info *survey) { void *hdr; struct nlattr *infoattr; hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_SURVEY_RESULTS); if (!hdr) return -ENOMEM; if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex)) goto nla_put_failure; infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); if (!infoattr) goto nla_put_failure; if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY, survey->channel->center_freq)) goto nla_put_failure; if ((survey->filled & SURVEY_INFO_NOISE_DBM) && nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise)) goto nla_put_failure; if ((survey->filled & SURVEY_INFO_IN_USE) && nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE)) goto nla_put_failure; if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) && nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, survey->channel_time)) goto nla_put_failure; if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) && nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, survey->channel_time_busy)) goto nla_put_failure; if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) && nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, survey->channel_time_ext_busy)) goto nla_put_failure; if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) && nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, survey->channel_time_rx)) goto nla_put_failure; if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) && nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, survey->channel_time_tx)) goto nla_put_failure; nla_nest_end(msg, infoattr); return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) { struct survey_info survey; struct cfg80211_registered_device *dev; struct net_device *netdev; int survey_idx = cb->args[1]; int res; res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); if (res) return res; if (!dev->ops->dump_survey) { res = -EOPNOTSUPP; goto out_err; } while (1) { struct ieee80211_channel *chan; res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, &survey); if (res == -ENOENT) break; if (res) goto out_err; /* Survey without a channel doesn't make sense */ if (!survey.channel) { res = -EINVAL; goto out; } chan = ieee80211_get_channel(&dev->wiphy, survey.channel->center_freq); if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { survey_idx++; continue; } if (nl80211_send_survey(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, netdev, &survey) < 0) goto out; survey_idx++; } out: cb->args[1] = survey_idx; res = skb->len; out_err: nl80211_finish_netdev_dump(dev); return res; } static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) { return auth_type <= NL80211_AUTHTYPE_MAX; } static bool nl80211_valid_wpa_versions(u32 wpa_versions) { return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)); } static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct ieee80211_channel *chan; const u8 *bssid, *ssid, *ie = NULL; int err, ssid_len, ie_len = 0; enum nl80211_auth_type auth_type; struct key_parse key; bool local_state_change; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) return -EINVAL; if (!info->attrs[NL80211_ATTR_SSID]) return -EINVAL; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; err = nl80211_parse_key(info, &key); if (err) return err; if (key.idx >= 0) { if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP) return -EINVAL; if (!key.p.key || !key.p.key_len) return -EINVAL; if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || key.p.key_len != WLAN_KEY_LEN_WEP40) && (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || key.p.key_len != WLAN_KEY_LEN_WEP104)) return -EINVAL; if (key.idx > 4) return -EINVAL; } else { key.p.key_len = 0; key.p.key = NULL; } if (key.idx >= 0) { int i; bool ok = false; for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) { if (key.p.cipher == rdev->wiphy.cipher_suites[i]) { ok = true; break; } } if (!ok) return -EINVAL; } if (!rdev->ops->auth) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); chan = ieee80211_get_channel(&rdev->wiphy, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) return -EINVAL; ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { ie = nla_data(info->attrs[NL80211_ATTR_IE]); ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); if (!nl80211_valid_auth_type(auth_type)) return -EINVAL; local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; /* * Since we no longer track auth state, ignore * requests to only change local state. */ if (local_state_change) return 0; return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, key.p.key, key.p.key_len, key.idx); } static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, struct genl_info *info, struct cfg80211_crypto_settings *settings, int cipher_limit) { memset(settings, 0, sizeof(*settings)); settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { u16 proto; proto = nla_get_u16( info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); settings->control_port_ethertype = cpu_to_be16(proto); if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && proto != ETH_P_PAE) return -EINVAL; if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) settings->control_port_no_encrypt = true; } else settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { void *data; int len, i; data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); settings->n_ciphers_pairwise = len / sizeof(u32); if (len % sizeof(u32)) return -EINVAL; if (settings->n_ciphers_pairwise > cipher_limit) return -EINVAL; memcpy(settings->ciphers_pairwise, data, len); for (i = 0; i < settings->n_ciphers_pairwise; i++) if (!cfg80211_supported_cipher_suite( &rdev->wiphy, settings->ciphers_pairwise[i])) return -EINVAL; } if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { settings->cipher_group = nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); if (!cfg80211_supported_cipher_suite(&rdev->wiphy, settings->cipher_group)) return -EINVAL; } if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { settings->wpa_versions = nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); if (!nl80211_valid_wpa_versions(settings->wpa_versions)) return -EINVAL; } if (info->attrs[NL80211_ATTR_AKM_SUITES]) { void *data; int len; data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); settings->n_akm_suites = len / sizeof(u32); if (len % sizeof(u32)) return -EINVAL; if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES) return -EINVAL; memcpy(settings->akm_suites, data, len); } return 0; } static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct cfg80211_crypto_settings crypto; struct ieee80211_channel *chan; const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; int err, ssid_len, ie_len = 0; bool use_mfp = false; u32 flags = 0; struct ieee80211_ht_cap *ht_capa = NULL; struct ieee80211_ht_cap *ht_capa_mask = NULL; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_SSID] || !info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; if (!rdev->ops->assoc) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); chan = ieee80211_get_channel(&rdev->wiphy, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) return -EINVAL; ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { ie = nla_data(info->attrs[NL80211_ATTR_IE]); ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_USE_MFP]) { enum nl80211_mfp mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); if (mfp == NL80211_MFP_REQUIRED) use_mfp = true; else if (mfp != NL80211_MFP_NO) return -EINVAL; } if (info->attrs[NL80211_ATTR_PREV_BSSID]) prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT])) flags |= ASSOC_REQ_DISABLE_HT; if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) ht_capa_mask = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]); if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { if (!ht_capa_mask) return -EINVAL; ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); } err = nl80211_crypto_settings(rdev, info, &crypto, 1); if (!err) err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, ssid, ssid_len, ie, ie_len, use_mfp, &crypto, flags, ht_capa, ht_capa_mask); return err; } static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; const u8 *ie = NULL, *bssid; int ie_len = 0; u16 reason_code; bool local_state_change; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!info->attrs[NL80211_ATTR_REASON_CODE]) return -EINVAL; if (!rdev->ops->deauth) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); if (reason_code == 0) { /* Reason Code 0 is reserved */ return -EINVAL; } if (info->attrs[NL80211_ATTR_IE]) { ie = nla_data(info->attrs[NL80211_ATTR_IE]); ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, local_state_change); } static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; const u8 *ie = NULL, *bssid; int ie_len = 0; u16 reason_code; bool local_state_change; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!info->attrs[NL80211_ATTR_REASON_CODE]) return -EINVAL; if (!rdev->ops->disassoc) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); if (reason_code == 0) { /* Reason Code 0 is reserved */ return -EINVAL; } if (info->attrs[NL80211_ATTR_IE]) { ie = nla_data(info->attrs[NL80211_ATTR_IE]); ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, local_state_change); } static bool nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, int mcast_rate[IEEE80211_NUM_BANDS], int rateval) { struct wiphy *wiphy = &rdev->wiphy; bool found = false; int band, i; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; sband = wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_bitrates; i++) { if (sband->bitrates[i].bitrate == rateval) { mcast_rate[band] = i + 1; found = true; break; } } } return found; } static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&ibss, 0, sizeof(ibss)); if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || !info->attrs[NL80211_ATTR_SSID] || !nla_len(info->attrs[NL80211_ATTR_SSID])) return -EINVAL; ibss.beacon_interval = 100; if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { ibss.beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000) return -EINVAL; } if (!rdev->ops->join_ibss) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; wiphy = &rdev->wiphy; if (info->attrs[NL80211_ATTR_MAC]) { ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); if (!is_valid_ether_addr(ibss.bssid)) return -EINVAL; } ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]); ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { enum nl80211_channel_type channel_type; if (!nl80211_valid_channel_type(info, &channel_type)) return -EINVAL; if (channel_type != NL80211_CHAN_NO_HT && !(wiphy->features & NL80211_FEATURE_HT_IBSS)) return -EINVAL; ibss.channel_type = channel_type; } else { ibss.channel_type = NL80211_CHAN_NO_HT; } ibss.channel = rdev_freq_to_chan(rdev, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), ibss.channel_type); if (!ibss.channel || ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || ibss.channel->flags & IEEE80211_CHAN_DISABLED) return -EINVAL; /* Both channels should be able to initiate communication */ if ((ibss.channel_type == NL80211_CHAN_HT40PLUS || ibss.channel_type == NL80211_CHAN_HT40MINUS) && !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel, ibss.channel_type)) return -EINVAL; ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); int n_rates = nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); struct ieee80211_supported_band *sband = wiphy->bands[ibss.channel->band]; err = ieee80211_get_ratemask(sband, rates, n_rates, &ibss.basic_rates); if (err) return err; } if (info->attrs[NL80211_ATTR_MCAST_RATE] && !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) return -EINVAL; if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { connkeys = nl80211_parse_connkeys(rdev, info->attrs[NL80211_ATTR_KEYS]); if (IS_ERR(connkeys)) return PTR_ERR(connkeys); } ibss.control_port = nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]); err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) kfree(connkeys); return err; } static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; if (!rdev->ops->leave_ibss) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; return cfg80211_leave_ibss(rdev, dev, false); } #ifdef CONFIG_NL80211_TESTMODE static struct genl_multicast_group nl80211_testmode_mcgrp = { .name = "testmode", }; static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int err; if (!info->attrs[NL80211_ATTR_TESTDATA]) return -EINVAL; err = -EOPNOTSUPP; if (rdev->ops->testmode_cmd) { rdev->testmode_info = info; err = rdev->ops->testmode_cmd(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_TESTDATA]), nla_len(info->attrs[NL80211_ATTR_TESTDATA])); rdev->testmode_info = NULL; } return err; } static int nl80211_testmode_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct cfg80211_registered_device *rdev; int err; long phy_idx; void *data = NULL; int data_len = 0; if (cb->args[0]) { /* * 0 is a valid index, but not valid for args[0], * so we need to offset by 1. */ phy_idx = cb->args[0] - 1; } else { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) return err; mutex_lock(&cfg80211_mutex); rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); if (IS_ERR(rdev)) { mutex_unlock(&cfg80211_mutex); return PTR_ERR(rdev); } phy_idx = rdev->wiphy_idx; rdev = NULL; mutex_unlock(&cfg80211_mutex); if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) cb->args[1] = (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; } if (cb->args[1]) { data = nla_data((void *)cb->args[1]); data_len = nla_len((void *)cb->args[1]); } mutex_lock(&cfg80211_mutex); rdev = cfg80211_rdev_by_wiphy_idx(phy_idx); if (!rdev) { mutex_unlock(&cfg80211_mutex); return -ENOENT; } cfg80211_lock_rdev(rdev); mutex_unlock(&cfg80211_mutex); if (!rdev->ops->testmode_dump) { err = -EOPNOTSUPP; goto out_err; } while (1) { void *hdr = nl80211hdr_put(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, NL80211_CMD_TESTMODE); struct nlattr *tmdata; if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) { genlmsg_cancel(skb, hdr); break; } tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA); if (!tmdata) { genlmsg_cancel(skb, hdr); break; } err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, data, data_len); nla_nest_end(skb, tmdata); if (err == -ENOBUFS || err == -ENOENT) { genlmsg_cancel(skb, hdr); break; } else if (err) { genlmsg_cancel(skb, hdr); goto out_err; } genlmsg_end(skb, hdr); } err = skb->len; /* see above */ cb->args[0] = phy_idx + 1; out_err: cfg80211_unlock_rdev(rdev); return err; } static struct sk_buff * __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, int approxlen, u32 portid, u32 seq, gfp_t gfp) { struct sk_buff *skb; void *hdr; struct nlattr *data; skb = nlmsg_new(approxlen + 100, gfp); if (!skb) return NULL; hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE); if (!hdr) { kfree_skb(skb); return NULL; } if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) goto nla_put_failure; data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); ((void **)skb->cb)[0] = rdev; ((void **)skb->cb)[1] = hdr; ((void **)skb->cb)[2] = data; return skb; nla_put_failure: kfree_skb(skb); return NULL; } struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); if (WARN_ON(!rdev->testmode_info)) return NULL; return __cfg80211_testmode_alloc_skb(rdev, approxlen, genl_info_snd_portid(rdev->testmode_info), rdev->testmode_info->snd_seq, GFP_KERNEL); } EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); int cfg80211_testmode_reply(struct sk_buff *skb) { struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; void *hdr = ((void **)skb->cb)[1]; struct nlattr *data = ((void **)skb->cb)[2]; if (WARN_ON(!rdev->testmode_info)) { kfree_skb(skb); return -EINVAL; } nla_nest_end(skb, data); genlmsg_end(skb, hdr); return genlmsg_reply(skb, rdev->testmode_info); } EXPORT_SYMBOL(cfg80211_testmode_reply); struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); } EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) { void *hdr = ((void **)skb->cb)[1]; struct nlattr *data = ((void **)skb->cb)[2]; nla_nest_end(skb, data); genlmsg_end(skb, hdr); genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp); } EXPORT_SYMBOL(cfg80211_testmode_event); #endif static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct cfg80211_connect_params connect; struct wiphy *wiphy; struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&connect, 0, sizeof(connect)); if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_SSID] || !nla_len(info->attrs[NL80211_ATTR_SSID])) return -EINVAL; if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { connect.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); if (!nl80211_valid_auth_type(connect.auth_type)) return -EINVAL; } else connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; err = nl80211_crypto_settings(rdev, info, &connect.crypto, NL80211_MAX_NR_CIPHER_SUITES); if (err) return err; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; wiphy = &rdev->wiphy; connect.bg_scan_period = -1; if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] && (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) { connect.bg_scan_period = nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]); } if (info->attrs[NL80211_ATTR_MAC]) connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { connect.channel = ieee80211_get_channel(wiphy, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); if (!connect.channel || connect.channel->flags & IEEE80211_CHAN_DISABLED) return -EINVAL; } if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { connkeys = nl80211_parse_connkeys(rdev, info->attrs[NL80211_ATTR_KEYS]); if (IS_ERR(connkeys)) return PTR_ERR(connkeys); } if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT])) connect.flags |= ASSOC_REQ_DISABLE_HT; if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) memcpy(&connect.ht_capa_mask, nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), sizeof(connect.ht_capa_mask)); if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) { kfree(connkeys); return -EINVAL; } memcpy(&connect.ht_capa, nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]), sizeof(connect.ht_capa)); } err = cfg80211_connect(rdev, dev, &connect, connkeys); if (err) kfree(connkeys); return err; } static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u16 reason; if (!info->attrs[NL80211_ATTR_REASON_CODE]) reason = WLAN_REASON_DEAUTH_LEAVING; else reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); if (reason == 0) return -EINVAL; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; return cfg80211_disconnect(rdev, dev, reason, true); } static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net *net; int err; u32 pid; if (!info->attrs[NL80211_ATTR_PID]) return -EINVAL; pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); net = get_net_ns_by_pid(pid); if (IS_ERR(net)) return PTR_ERR(net); err = 0; /* check if anything to do */ if (!net_eq(wiphy_net(&rdev->wiphy), net)) err = cfg80211_switch_netns(rdev, net); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) put_net(net); #endif return err; } static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_pmksa *pmksa) = NULL; struct net_device *dev = info->user_ptr[1]; struct cfg80211_pmksa pmksa; memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!info->attrs[NL80211_ATTR_PMKID]) return -EINVAL; pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; switch (info->genlhdr->cmd) { case NL80211_CMD_SET_PMKSA: rdev_ops = rdev->ops->set_pmksa; break; case NL80211_CMD_DEL_PMKSA: rdev_ops = rdev->ops->del_pmksa; break; default: WARN_ON(1); break; } if (!rdev_ops) return -EOPNOTSUPP; return rdev_ops(&rdev->wiphy, dev, &pmksa); } static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; if (!rdev->ops->flush_pmksa) return -EOPNOTSUPP; return rdev->ops->flush_pmksa(&rdev->wiphy, dev); } static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u8 action_code, dialog_token; u16 status_code; u8 *peer; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || !rdev->ops->tdls_mgmt) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || !info->attrs[NL80211_ATTR_STATUS_CODE] || !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || !info->attrs[NL80211_ATTR_IE] || !info->attrs[NL80211_ATTR_MAC]) return -EINVAL; peer = nla_data(info->attrs[NL80211_ATTR_MAC]); action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, dialog_token, status_code, nla_data(info->attrs[NL80211_ATTR_IE]), nla_len(info->attrs[NL80211_ATTR_IE])); } static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; enum nl80211_tdls_operation operation; u8 *peer; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || !rdev->ops->tdls_oper) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || !info->attrs[NL80211_ATTR_MAC]) return -EINVAL; operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]); peer = nla_data(info->attrs[NL80211_ATTR_MAC]); return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); } static int nl80211_remain_on_channel(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; struct ieee80211_channel *chan; struct sk_buff *msg; void *hdr; u64 cookie; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; u32 freq, duration; int err; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || !info->attrs[NL80211_ATTR_DURATION]) return -EINVAL; duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); if (!rdev->ops->remain_on_channel || !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) return -EOPNOTSUPP; /* * We should be on that channel for at least a minimum amount of * time (10ms) but no longer than the driver supports. */ if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || duration > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && !nl80211_valid_channel_type(info, &channel_type)) return -EINVAL; freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); chan = rdev_freq_to_chan(rdev, freq, channel_type); if (chan == NULL) return -EINVAL; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_REMAIN_ON_CHANNEL); if (IS_ERR(hdr)) { err = PTR_ERR(hdr); goto free_msg; } err = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, channel_type, duration, &cookie); if (err) goto free_msg; if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) goto nla_put_failure; genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); nla_put_failure: err = -ENOBUFS; free_msg: nlmsg_free(msg); return err; } static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; u64 cookie; if (!info->attrs[NL80211_ATTR_COOKIE]) return -EINVAL; if (!rdev->ops->cancel_remain_on_channel) return -EOPNOTSUPP; cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); } static u32 rateset_to_mask(struct ieee80211_supported_band *sband, u8 *rates, u8 rates_len) { u8 i; u32 mask = 0; for (i = 0; i < rates_len; i++) { int rate = (rates[i] & 0x7f) * 5; int ridx; for (ridx = 0; ridx < sband->n_bitrates; ridx++) { struct ieee80211_rate *srate = &sband->bitrates[ridx]; if (rate == srate->bitrate) { mask |= 1 << ridx; break; } } if (ridx == sband->n_bitrates) return 0; /* rate not found */ } return mask; } static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, u8 *rates, u8 rates_len, u8 mcs[IEEE80211_HT_MCS_MASK_LEN]) { u8 i; memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN); for (i = 0; i < rates_len; i++) { int ridx, rbit; ridx = rates[i] / 8; rbit = BIT(rates[i] % 8); /* check validity */ if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN)) return false; /* check availability */ if (sband->ht_cap.mcs.rx_mask[ridx] & rbit) mcs[ridx] |= rbit; else return false; } return true; } static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, [NL80211_TXRATE_MCS] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_HT_RATES }, }; static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, struct genl_info *info) { struct nlattr *tb[NL80211_TXRATE_MAX + 1]; struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_bitrate_mask mask; int rem, i; struct net_device *dev = info->user_ptr[1]; struct nlattr *tx_rates; struct ieee80211_supported_band *sband; if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) return -EINVAL; if (!rdev->ops->set_bitrate_mask) return -EOPNOTSUPP; memset(&mask, 0, sizeof(mask)); /* Default to all rates enabled */ for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sband = rdev->wiphy.bands[i]; mask.control[i].legacy = sband ? (1 << sband->n_bitrates) - 1 : 0; if (sband) memcpy(mask.control[i].mcs, sband->ht_cap.mcs.rx_mask, sizeof(mask.control[i].mcs)); else memset(mask.control[i].mcs, 0, sizeof(mask.control[i].mcs)); } /* * The nested attribute uses enum nl80211_band as the index. This maps * directly to the enum ieee80211_band values used in cfg80211. */ BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) { enum ieee80211_band band = nla_type(tx_rates); if (band < 0 || band >= IEEE80211_NUM_BANDS) return -EINVAL; sband = rdev->wiphy.bands[band]; if (sband == NULL) return -EINVAL; nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), nla_len(tx_rates), nl80211_txattr_policy); if (tb[NL80211_TXRATE_LEGACY]) { mask.control[band].legacy = rateset_to_mask( sband, nla_data(tb[NL80211_TXRATE_LEGACY]), nla_len(tb[NL80211_TXRATE_LEGACY])); if ((mask.control[band].legacy == 0) && nla_len(tb[NL80211_TXRATE_LEGACY])) return -EINVAL; } if (tb[NL80211_TXRATE_MCS]) { if (!ht_rateset_to_mask( sband, nla_data(tb[NL80211_TXRATE_MCS]), nla_len(tb[NL80211_TXRATE_MCS]), mask.control[band].mcs)) return -EINVAL; } if (mask.control[band].legacy == 0) { /* don't allow empty legacy rates if HT * is not even supported. */ if (!rdev->wiphy.bands[band]->ht_cap.ht_supported) return -EINVAL; for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) if (mask.control[band].mcs[i]) break; /* legacy and mcs rates may not be both empty */ if (i == IEEE80211_HT_MCS_MASK_LEN) return -EINVAL; } } return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); } static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) return -EINVAL; if (info->attrs[NL80211_ATTR_FRAME_TYPE]) frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; } /* not much point in registering if we can't reply */ if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; return cfg80211_mlme_register_mgmt(wdev, genl_info_snd_portid(info), frame_type, nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); } static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; struct ieee80211_channel *chan; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; bool channel_type_valid = false; u32 freq; int err; void *hdr = NULL; u64 cookie; struct sk_buff *msg = NULL; unsigned int wait = 0; bool offchan, no_cck, dont_wait_for_ack; dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; if (!info->attrs[NL80211_ATTR_FRAME] || !info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; } if (info->attrs[NL80211_ATTR_DURATION]) { if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) return -EINVAL; wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); /* * We should wait on the channel for at least a minimum amount * of time (10ms) but no longer than the driver supports. */ if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || wait > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; } if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { if (!nl80211_valid_channel_type(info, &channel_type)) return -EINVAL; channel_type_valid = true; } offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) return -EINVAL; no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); chan = rdev_freq_to_chan(rdev, freq, channel_type); if (chan == NULL) return -EINVAL; if (!dont_wait_for_ack) { msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_FRAME); if (IS_ERR(hdr)) { err = PTR_ERR(hdr); goto free_msg; } } err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type, channel_type_valid, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), no_cck, dont_wait_for_ack, &cookie); if (err) goto free_msg; if (msg) { if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) goto nla_put_failure; genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); } return 0; nla_put_failure: err = -ENOBUFS; free_msg: nlmsg_free(msg); return err; } static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; u64 cookie; if (!info->attrs[NL80211_ATTR_COOKIE]) return -EINVAL; if (!rdev->ops->mgmt_tx_cancel_wait) return -EOPNOTSUPP; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; } cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); } static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev; struct net_device *dev = info->user_ptr[1]; u8 ps_state; bool state; int err; if (!info->attrs[NL80211_ATTR_PS_STATE]) return -EINVAL; ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) return -EINVAL; wdev = dev->ieee80211_ptr; if (!rdev->ops->set_power_mgmt) return -EOPNOTSUPP; state = (ps_state == NL80211_PS_ENABLED) ? true : false; if (state == wdev->ps) return 0; err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state, wdev->ps_timeout); if (!err) wdev->ps = state; return err; } static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; enum nl80211_ps_state ps_state; struct wireless_dev *wdev; struct net_device *dev = info->user_ptr[1]; struct sk_buff *msg; void *hdr; int err; wdev = dev->ieee80211_ptr; if (!rdev->ops->set_power_mgmt) return -EOPNOTSUPP; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_POWER_SAVE); if (!hdr) { err = -ENOBUFS; goto free_msg; } if (wdev->ps) ps_state = NL80211_PS_ENABLED; else ps_state = NL80211_PS_DISABLED; if (nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state)) goto nla_put_failure; genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); nla_put_failure: err = -ENOBUFS; free_msg: nlmsg_free(msg); return err; } static struct nla_policy nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, }; static int nl80211_set_cqm_txe(struct genl_info *info, u32 rate, u32 pkts, u32 intvl) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev; struct net_device *dev = info->user_ptr[1]; if ((rate < 0 || rate > 100) || (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL)) return -EINVAL; wdev = dev->ieee80211_ptr; if (!rdev->ops->set_cqm_txe_config) return -EOPNOTSUPP; if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev, rate, pkts, intvl); } static int nl80211_set_cqm_rssi(struct genl_info *info, s32 threshold, u32 hysteresis) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev; struct net_device *dev = info->user_ptr[1]; if (threshold > 0) return -EINVAL; wdev = dev->ieee80211_ptr; if (!rdev->ops->set_cqm_rssi_config) return -EOPNOTSUPP; if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, threshold, hysteresis); } static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) { struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; struct nlattr *cqm; int err; cqm = info->attrs[NL80211_ATTR_CQM]; if (!cqm) { err = -EINVAL; goto out; } err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, nl80211_attr_cqm_policy); if (err) goto out; if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && attrs[NL80211_ATTR_CQM_RSSI_HYST]) { s32 threshold; u32 hysteresis; threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); err = nl80211_set_cqm_rssi(info, threshold, hysteresis); } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] && attrs[NL80211_ATTR_CQM_TXE_PKTS] && attrs[NL80211_ATTR_CQM_TXE_INTVL]) { u32 rate, pkts, intvl; rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]); pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]); intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]); err = nl80211_set_cqm_txe(info, rate, pkts, intvl); } else err = -EINVAL; out: return err; } static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct mesh_config cfg; struct mesh_setup setup; int err; /* start with default */ memcpy(&cfg, &default_mesh_config, sizeof(cfg)); memcpy(&setup, &default_mesh_setup, sizeof(setup)); if (info->attrs[NL80211_ATTR_MESH_CONFIG]) { /* and parse parameters if given */ err = nl80211_parse_mesh_config(info, &cfg, NULL); if (err) return err; } if (!info->attrs[NL80211_ATTR_MESH_ID] || !nla_len(info->attrs[NL80211_ATTR_MESH_ID])) return -EINVAL; setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); if (info->attrs[NL80211_ATTR_MCAST_RATE] && !nl80211_parse_mcast_rate(rdev, setup.mcast_rate, nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) return -EINVAL; if (info->attrs[NL80211_ATTR_MESH_SETUP]) { /* parse additional setup parameters if given */ err = nl80211_parse_mesh_setup(info, &setup); if (err) return err; } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && !nl80211_valid_channel_type(info, &channel_type)) return -EINVAL; setup.channel = rdev_freq_to_chan(rdev, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), channel_type); if (!setup.channel) return -EINVAL; setup.channel_type = channel_type; } else { /* cfg80211_join_mesh() will sort it out */ setup.channel = NULL; } return cfg80211_join_mesh(rdev, dev, &setup, &cfg); } static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; return cfg80211_leave_mesh(rdev, dev); } #ifdef CONFIG_PM static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct sk_buff *msg; void *hdr; if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) return -EOPNOTSUPP; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_WOWLAN); if (!hdr) goto nla_put_failure; if (rdev->wowlan) { struct nlattr *nl_wowlan; nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); if (!nl_wowlan) goto nla_put_failure; if ((rdev->wowlan->any && nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || (rdev->wowlan->disconnect && nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || (rdev->wowlan->magic_pkt && nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || (rdev->wowlan->gtk_rekey_failure && nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || (rdev->wowlan->eap_identity_req && nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || (rdev->wowlan->four_way_handshake && nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || (rdev->wowlan->rfkill_release && nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) goto nla_put_failure; if (rdev->wowlan->n_patterns) { struct nlattr *nl_pats, *nl_pat; int i, pat_len; nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN); if (!nl_pats) goto nla_put_failure; for (i = 0; i < rdev->wowlan->n_patterns; i++) { nl_pat = nla_nest_start(msg, i + 1); if (!nl_pat) goto nla_put_failure; pat_len = rdev->wowlan->patterns[i].pattern_len; if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8), rdev->wowlan->patterns[i].mask) || nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, pat_len, rdev->wowlan->patterns[i].pattern)) goto nla_put_failure; nla_nest_end(msg, nl_pat); } nla_nest_end(msg, nl_pats); } nla_nest_end(msg, nl_wowlan); } genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; struct cfg80211_wowlan new_triggers = {}; struct cfg80211_wowlan *ntrig; struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; int err, i; bool prev_enabled = rdev->wowlan; if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { cfg80211_rdev_free_wowlan(rdev); rdev->wowlan = NULL; goto set_wakeup; } err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), nl80211_wowlan_policy); if (err) return err; if (tb[NL80211_WOWLAN_TRIG_ANY]) { if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) return -EINVAL; new_triggers.any = true; } if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) return -EINVAL; new_triggers.disconnect = true; } if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) return -EINVAL; new_triggers.magic_pkt = true; } if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) return -EINVAL; if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) return -EINVAL; new_triggers.gtk_rekey_failure = true; } if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) return -EINVAL; new_triggers.eap_identity_req = true; } if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) return -EINVAL; new_triggers.four_way_handshake = true; } if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) return -EINVAL; new_triggers.rfkill_release = true; } if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { struct nlattr *pat; int n_patterns = 0; int rem, pat_len, mask_len; struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) n_patterns++; if (n_patterns > wowlan->n_patterns) return -EINVAL; new_triggers.patterns = kcalloc(n_patterns, sizeof(new_triggers.patterns[0]), GFP_KERNEL); if (!new_triggers.patterns) return -ENOMEM; new_triggers.n_patterns = n_patterns; i = 0; nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) { nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, nla_data(pat), nla_len(pat), NULL); err = -EINVAL; if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) goto error; pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); mask_len = DIV_ROUND_UP(pat_len, 8); if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != mask_len) goto error; if (pat_len > wowlan->pattern_max_len || pat_len < wowlan->pattern_min_len) goto error; new_triggers.patterns[i].mask = kmalloc(mask_len + pat_len, GFP_KERNEL); if (!new_triggers.patterns[i].mask) { err = -ENOMEM; goto error; } new_triggers.patterns[i].pattern = new_triggers.patterns[i].mask + mask_len; memcpy(new_triggers.patterns[i].mask, nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), mask_len); new_triggers.patterns[i].pattern_len = pat_len; memcpy(new_triggers.patterns[i].pattern, nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), pat_len); i++; } } ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); if (!ntrig) { err = -ENOMEM; goto error; } cfg80211_rdev_free_wowlan(rdev); rdev->wowlan = ntrig; set_wakeup: if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan) rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan); return 0; error: for (i = 0; i < new_triggers.n_patterns; i++) kfree(new_triggers.patterns[i].mask); kfree(new_triggers.patterns); return err; } #endif static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct nlattr *tb[NUM_NL80211_REKEY_DATA]; struct cfg80211_gtk_rekey_data rekey_data; int err; if (!info->attrs[NL80211_ATTR_REKEY_DATA]) return -EINVAL; err = nla_parse(tb, MAX_NL80211_REKEY_DATA, nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]), nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]), nl80211_rekey_policy); if (err) return err; if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN) return -ERANGE; if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN) return -ERANGE; if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) return -ERANGE; memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]), NL80211_KEK_LEN); memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]), NL80211_KCK_LEN); memcpy(rekey_data.replay_ctr, nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]), NL80211_REPLAY_CTR_LEN); wdev_lock(wdev); if (!wdev->current_bss) { err = -ENOTCONN; goto out; } if (!rdev->ops->set_rekey_data) { err = -EOPNOTSUPP; goto out; } err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); out: wdev_unlock(wdev); return err; } static int nl80211_register_unexpected_frame(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; if (wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO) return -EINVAL; if (wdev->ap_unexpected_nlportid) return -EBUSY; wdev->ap_unexpected_nlportid = genl_info_snd_portid(info); return 0; } static int nl80211_probe_client(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct sk_buff *msg; void *hdr; const u8 *addr; u64 cookie; int err; if (wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; if (!rdev->ops->probe_client) return -EOPNOTSUPP; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_PROBE_CLIENT); if (IS_ERR(hdr)) { err = PTR_ERR(hdr); goto free_msg; } addr = nla_data(info->attrs[NL80211_ATTR_MAC]); err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); if (err) goto free_msg; if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) goto nla_put_failure; genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); nla_put_failure: err = -ENOBUFS; free_msg: nlmsg_free(msg); return err; } static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) return -EOPNOTSUPP; if (rdev->ap_beacons_nlportid) return -EBUSY; rdev->ap_beacons_nlportid = genl_info_snd_portid(info); return 0; } static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; int err; if (!rdev->ops->start_p2p_device) return -EOPNOTSUPP; if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) return -EOPNOTSUPP; if (wdev->p2p_started) return 0; mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_add_interface(rdev, wdev->iftype); mutex_unlock(&rdev->devlist_mtx); if (err) return err; err = rdev->ops->start_p2p_device(&rdev->wiphy, wdev); if (err) return err; wdev->p2p_started = true; mutex_lock(&rdev->devlist_mtx); rdev->opencount++; mutex_unlock(&rdev->devlist_mtx); return 0; } static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) return -EOPNOTSUPP; if (!rdev->ops->stop_p2p_device) return -EOPNOTSUPP; if (!wdev->p2p_started) return 0; rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); wdev->p2p_started = false; mutex_lock(&rdev->devlist_mtx); rdev->opencount--; mutex_unlock(&rdev->devlist_mtx); if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) { rdev->scan_req->aborted = true; ___cfg80211_scan_done(rdev, true); } return 0; } #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 #define NL80211_FLAG_CHECK_NETDEV_UP 0x08 #define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ NL80211_FLAG_CHECK_NETDEV_UP) #define NL80211_FLAG_NEED_WDEV 0x10 /* If a netdev is associated, it must be UP, P2P must be started */ #define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ NL80211_FLAG_CHECK_NETDEV_UP) static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; struct net_device *dev; bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL; if (rtnl) rtnl_lock(); if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) { rdev = cfg80211_get_dev_from_info(genl_info_net(info), info); if (IS_ERR(rdev)) { if (rtnl) rtnl_unlock(); return PTR_ERR(rdev); } info->user_ptr[0] = rdev; } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV || ops->internal_flags & NL80211_FLAG_NEED_WDEV) { mutex_lock(&cfg80211_mutex); wdev = __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); if (IS_ERR(wdev)) { mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return PTR_ERR(wdev); } dev = wdev->netdev; rdev = wiphy_to_dev(wdev->wiphy); if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { if (!dev) { mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -EINVAL; } info->user_ptr[1] = dev; } else { info->user_ptr[1] = wdev; } if (dev) { if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && !netif_running(dev)) { mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -ENETDOWN; } dev_hold(dev); } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) { if (!wdev->p2p_started) { mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -ENETDOWN; } } cfg80211_lock_rdev(rdev); mutex_unlock(&cfg80211_mutex); info->user_ptr[0] = rdev; } return 0; } static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info) { if (info->user_ptr[0]) cfg80211_unlock_rdev(info->user_ptr[0]); if (info->user_ptr[1]) { if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) { struct wireless_dev *wdev = info->user_ptr[1]; if (wdev->netdev) dev_put(wdev->netdev); } else { dev_put(info->user_ptr[1]); } } if (ops->internal_flags & NL80211_FLAG_NEED_RTNL) rtnl_unlock(); } static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, .doit = nl80211_get_wiphy, .dumpit = nl80211_dump_wiphy, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ .internal_flags = NL80211_FLAG_NEED_WIPHY, }, { .cmd = NL80211_CMD_SET_WIPHY, .doit = nl80211_set_wiphy, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_INTERFACE, .doit = nl80211_get_interface, .dumpit = nl80211_dump_interface, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ .internal_flags = NL80211_FLAG_NEED_WDEV, }, { .cmd = NL80211_CMD_SET_INTERFACE, .doit = nl80211_set_interface, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_NEW_INTERFACE, .doit = nl80211_new_interface, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DEL_INTERFACE, .doit = nl80211_del_interface, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_KEY, .doit = nl80211_get_key, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_KEY, .doit = nl80211_set_key, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_NEW_KEY, .doit = nl80211_new_key, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DEL_KEY, .doit = nl80211_del_key, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_BEACON, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .doit = nl80211_set_beacon, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_START_AP, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .doit = nl80211_start_ap, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_STOP_AP, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .doit = nl80211_stop_ap, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_STATION, .doit = nl80211_get_station, .dumpit = nl80211_dump_station, .policy = nl80211_policy, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_STATION, .doit = nl80211_set_station, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_NEW_STATION, .doit = nl80211_new_station, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DEL_STATION, .doit = nl80211_del_station, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_MPATH, .doit = nl80211_get_mpath, .dumpit = nl80211_dump_mpath, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_MPATH, .doit = nl80211_set_mpath, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_NEW_MPATH, .doit = nl80211_new_mpath, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DEL_MPATH, .doit = nl80211_del_mpath, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_BSS, .doit = nl80211_set_bss, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_REG, .doit = nl80211_get_reg, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ }, { .cmd = NL80211_CMD_SET_REG, .doit = nl80211_set_reg, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, { .cmd = NL80211_CMD_REQ_SET_REG, .doit = nl80211_req_set_reg, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, { .cmd = NL80211_CMD_GET_MESH_CONFIG, .doit = nl80211_get_mesh_config, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_MESH_CONFIG, .doit = nl80211_update_mesh_config, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_TRIGGER_SCAN, .doit = nl80211_trigger_scan, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_SCAN, .policy = nl80211_policy, .dumpit = nl80211_dump_scan, }, { .cmd = NL80211_CMD_START_SCHED_SCAN, .doit = nl80211_start_sched_scan, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_STOP_SCHED_SCAN, .doit = nl80211_stop_sched_scan, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_AUTHENTICATE, .doit = nl80211_authenticate, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_ASSOCIATE, .doit = nl80211_associate, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DEAUTHENTICATE, .doit = nl80211_deauthenticate, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DISASSOCIATE, .doit = nl80211_disassociate, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_JOIN_IBSS, .doit = nl80211_join_ibss, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_LEAVE_IBSS, .doit = nl80211_leave_ibss, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, #ifdef CONFIG_NL80211_TESTMODE { .cmd = NL80211_CMD_TESTMODE, .doit = nl80211_testmode_do, .dumpit = nl80211_testmode_dump, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, #endif { .cmd = NL80211_CMD_CONNECT, .doit = nl80211_connect, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DISCONNECT, .doit = nl80211_disconnect, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_WIPHY_NETNS, .doit = nl80211_wiphy_netns, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_SURVEY, .policy = nl80211_policy, .dumpit = nl80211_dump_survey, }, { .cmd = NL80211_CMD_SET_PMKSA, .doit = nl80211_setdel_pmksa, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_DEL_PMKSA, .doit = nl80211_setdel_pmksa, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_FLUSH_PMKSA, .doit = nl80211_flush_pmksa, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, .doit = nl80211_remain_on_channel, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, .doit = nl80211_cancel_remain_on_channel, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, .doit = nl80211_set_tx_bitrate_mask, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_REGISTER_FRAME, .doit = nl80211_register_mgmt, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_FRAME, .doit = nl80211_tx_mgmt, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, .doit = nl80211_tx_mgmt_cancel_wait, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_POWER_SAVE, .doit = nl80211_set_power_save, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_GET_POWER_SAVE, .doit = nl80211_get_power_save, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_CQM, .doit = nl80211_set_cqm, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_CHANNEL, .doit = nl80211_set_channel, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_WDS_PEER, .doit = nl80211_set_wds_peer, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_JOIN_MESH, .doit = nl80211_join_mesh, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_LEAVE_MESH, .doit = nl80211_leave_mesh, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, #ifdef CONFIG_PM { .cmd = NL80211_CMD_GET_WOWLAN, .doit = nl80211_get_wowlan, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_WOWLAN, .doit = nl80211_set_wowlan, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, #endif { .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, .doit = nl80211_set_rekey_data, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_TDLS_MGMT, .doit = nl80211_tdls_mgmt, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_TDLS_OPER, .doit = nl80211_tdls_oper, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_UNEXPECTED_FRAME, .doit = nl80211_register_unexpected_frame, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_PROBE_CLIENT, .doit = nl80211_probe_client, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_REGISTER_BEACONS, .doit = nl80211_register_beacons, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_NOACK_MAP, .doit = nl80211_set_noack_map, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_START_P2P_DEVICE, .doit = nl80211_start_p2p_device, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV | NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_STOP_P2P_DEVICE, .doit = nl80211_stop_p2p_device, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", }; /* multicast groups */ static struct genl_multicast_group nl80211_config_mcgrp = { .name = "config", }; static struct genl_multicast_group nl80211_scan_mcgrp = { .name = "scan", }; static struct genl_multicast_group nl80211_regulatory_mcgrp = { .name = "regulatory", }; /* notification functions */ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); } static int nl80211_add_scan_req(struct sk_buff *msg, struct cfg80211_registered_device *rdev) { struct cfg80211_scan_request *req = rdev->scan_req; struct nlattr *nest; int i; ASSERT_RDEV_LOCK(rdev); if (WARN_ON(!req)) return 0; nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); if (!nest) goto nla_put_failure; for (i = 0; i < req->n_ssids; i++) { if (nla_put(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid)) goto nla_put_failure; } nla_nest_end(msg, nest); nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); if (!nest) goto nla_put_failure; for (i = 0; i < req->n_channels; i++) { if (nla_put_u32(msg, i, req->channels[i]->center_freq)) goto nla_put_failure; } nla_nest_end(msg, nest); if (req->ie && nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie)) goto nla_put_failure; return 0; nla_put_failure: return -ENOBUFS; } static int nl80211_send_scan_msg(struct sk_buff *msg, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u32 portid, u32 seq, int flags, u32 cmd) { void *hdr; hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) return -1; if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) goto nla_put_failure; /* ignore errors and send incomplete event anyway */ nl80211_add_scan_req(msg, rdev); return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } static int nl80211_send_sched_scan_msg(struct sk_buff *msg, struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 portid, u32 seq, int flags, u32 cmd) { void *hdr; hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) return -1; if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) goto nla_put_failure; return genlmsg_end(msg, hdr); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; } void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, NL80211_CMD_TRIGGER_SCAN) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, NL80211_CMD_NEW_SCAN_RESULTS) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, NL80211_CMD_SCAN_ABORTED) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, struct net_device *netdev) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 cmd) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); } /* * This can happen on global regulatory changes or device specific settings * based on custom world regulatory domains. */ void nl80211_send_reg_change_event(struct regulatory_request *request) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); if (!hdr) { nlmsg_free(msg); return; } /* Userspace can always count this one always being set */ if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator)) goto nla_put_failure; if (request->alpha2[0] == '0' && request->alpha2[1] == '0') { if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE, NL80211_REGDOM_TYPE_WORLD)) goto nla_put_failure; } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') { if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE, NL80211_REGDOM_TYPE_CUSTOM_WORLD)) goto nla_put_failure; } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || request->intersect) { if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE, NL80211_REGDOM_TYPE_INTERSECTION)) goto nla_put_failure; } else { if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE, NL80211_REGDOM_TYPE_COUNTRY) || nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2)) goto nla_put_failure; } if (wiphy_idx_valid(request->wiphy_idx) && nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) goto nla_put_failure; genlmsg_end(msg, hdr); rcu_read_lock(); genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC); rcu_read_unlock(); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, enum nl80211_commands cmd, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_FRAME, len, buf)) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_AUTHENTICATE, gfp); } void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE, gfp); } void nl80211_send_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_DEAUTHENTICATE, gfp); } void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_DISASSOCIATE, gfp); } void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp); } void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_UNPROT_DISASSOCIATE, gfp); } static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, int cmd, const u8 *addr, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp) { nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, addr, gfp); } void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp) { nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, addr, gfp); } void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, u16 status, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) || (req_ie && nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) || (resp_ie && nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie))) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) || (req_ie && nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) || (resp_ie && nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie))) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || (from_ap && reason && nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason)) || (from_ap && nla_put_flag(msg, NL80211_ATTR_DISCONNECTED_BY_AP)) || (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie))) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) || (ie_len && ie && nla_put(msg, NL80211_ATTR_IE, ie_len , ie))) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, const u8 *tsc, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) || nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, key_type) || (key_id != -1 && nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_id)) || (tsc && nla_put(msg, NL80211_ATTR_KEY_SEQ, 6, tsc))) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_beacon_hint_event(struct wiphy *wiphy, struct ieee80211_channel *channel_before, struct ieee80211_channel *channel_after) { struct sk_buff *msg; void *hdr; struct nlattr *nl_freq; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT); if (!hdr) { nlmsg_free(msg); return; } /* * Since we are applying the beacon hint to a wiphy we know its * wiphy_idx is valid */ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) goto nla_put_failure; /* Before */ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE); if (!nl_freq) goto nla_put_failure; if (nl80211_msg_put_channel(msg, channel_before)) goto nla_put_failure; nla_nest_end(msg, nl_freq); /* After */ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER); if (!nl_freq) goto nla_put_failure; if (nl80211_msg_put_channel(msg, channel_after)) goto nla_put_failure; nla_nest_end(msg, nl_freq); genlmsg_end(msg, hdr); rcu_read_lock(); genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC); rcu_read_unlock(); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } static void nl80211_send_remain_on_chan_event( int cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) goto nla_put_failure; if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL && nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, rdev, wdev, cookie, chan, channel_type, duration, gfp); } void nl80211_send_remain_on_channel_cancel( struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, rdev, wdev, cookie, chan, channel_type, 0, gfp); } void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; if (nl80211_send_station(msg, 0, 0, 0, rdev, dev, mac_addr, sinfo) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); } void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr)) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, const u8 *addr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct sk_buff *msg; void *hdr; int err; u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid); if (!nlportid) return false; msg = nlmsg_new(100, gfp); if (!msg) return true; hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); if (!hdr) { nlmsg_free(msg); return true; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) goto nla_put_failure; err = genlmsg_end(msg, hdr); if (err < 0) { nlmsg_free(msg); return true; } genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); return true; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); return true; } bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME, addr, gfp); } bool nl80211_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_4ADDR_FRAME, addr, gfp); } int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u32 nlportid, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp) { struct net_device *netdev = wdev->netdev; struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); if (!hdr) { nlmsg_free(msg); return -ENOMEM; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || (sig_dbm && nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || nla_put(msg, NL80211_ATTR_FRAME, len, buf)) goto nla_put_failure; genlmsg_end(msg, hdr); return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); return -ENOBUFS; } void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp) { struct net_device *netdev = wdev->netdev; struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) || nla_put(msg, NL80211_ATTR_FRAME, len, buf) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) || (ack && nla_put_flag(msg, NL80211_ATTR_ACK))) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp) { struct sk_buff *msg; struct nlattr *pinfoattr; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) goto nla_put_failure; pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); if (!pinfoattr) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, rssi_event)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp) { struct sk_buff *msg; struct nlattr *rekey_attr; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) goto nla_put_failure; rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); if (!rekey_attr) goto nla_put_failure; if (nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, replay_ctr)) goto nla_put_failure; nla_nest_end(msg, rekey_attr); genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, int index, const u8 *bssid, bool preauth, gfp_t gfp) { struct sk_buff *msg; struct nlattr *attr; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) goto nla_put_failure; attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE); if (!attr) goto nla_put_failure; if (nla_put_u32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index) || nla_put(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid) || (preauth && nla_put_flag(msg, NL80211_PMKSA_CANDIDATE_PREAUTH))) goto nla_put_failure; nla_nest_end(msg, attr); genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, int freq, enum nl80211_channel_type type, gfp_t gfp) { struct sk_buff *msg; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type)) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, u32 num_packets, u32 rate, u32 intvl, gfp_t gfp) { struct sk_buff *msg; struct nlattr *pinfoattr; void *hdr; msg = nlmsg_new(NLMSG_GOODSIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) goto nla_put_failure; pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); if (!pinfoattr) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets)) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate)) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, u32 num_packets, gfp_t gfp) { struct sk_buff *msg; struct nlattr *pinfoattr; void *hdr; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) goto nla_put_failure; pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); if (!pinfoattr) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } void cfg80211_probe_status(struct net_device *dev, const u8 *addr, u64 cookie, bool acked, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct sk_buff *msg; void *hdr; int err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) || (acked && nla_put_flag(msg, NL80211_ATTR_ACK))) goto nla_put_failure; err = genlmsg_end(msg, hdr); if (err < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } EXPORT_SYMBOL(cfg80211_probe_status); void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, int freq, int sig_dbm, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct sk_buff *msg; void *hdr; u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid); if (!nlportid) return; msg = nlmsg_new(len + 100, gfp); if (!msg) return; hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || (sig_dbm && nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || nla_put(msg, NL80211_ATTR_FRAME, len, frame)) goto nla_put_failure; genlmsg_end(msg, hdr); genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } EXPORT_SYMBOL(cfg80211_report_obss_beacon); static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) { struct netlink_notify *notify = _notify; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; if (state != NETLINK_URELEASE) return NOTIFY_DONE; rcu_read_lock(); list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) cfg80211_mlme_unregister_socket(wdev, netlink_notify_portid(notify)); if (rdev->ap_beacons_nlportid == netlink_notify_portid(notify)) rdev->ap_beacons_nlportid = 0; } rcu_read_unlock(); return NOTIFY_DONE; } static struct notifier_block nl80211_netlink_notifier = { .notifier_call = nl80211_netlink_notify, }; /* initialisation/exit functions */ int nl80211_init(void) { int err; err = genl_register_family_with_ops(&nl80211_fam, nl80211_ops, ARRAY_SIZE(nl80211_ops)); if (err) return err; err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp); if (err) goto err_out; err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); if (err) goto err_out; err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp); if (err) goto err_out; err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); if (err) goto err_out; #ifdef CONFIG_NL80211_TESTMODE err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); if (err) goto err_out; #endif err = netlink_register_notifier(&nl80211_netlink_notifier); if (err) goto err_out; return 0; err_out: genl_unregister_family(&nl80211_fam); return err; } void nl80211_exit(void) { netlink_unregister_notifier(&nl80211_netlink_notifier); genl_unregister_family(&nl80211_fam); } compat-drivers-2012-09-18/net/wireless/sysfs.c0000644000175000017500000000677612026211315020336 0ustar mcgrofmcgrof/* * This file provides /sys/class/ieee80211// * and some default attributes. * * Copyright 2005-2006 Jiri Benc * Copyright 2006 Johannes Berg * * This file is GPLv2 as found in COPYING. */ #include #include #include #include #include #include #include "sysfs.h" #include "core.h" static inline struct cfg80211_registered_device *dev_to_rdev( struct device *dev) { return container_of(dev, struct cfg80211_registered_device, wiphy.dev); } #define SHOW_FMT(name, fmt, member) \ static ssize_t name ## _show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \ } SHOW_FMT(index, "%d", wiphy_idx); SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; return sprintf(buf, "%s\n", dev_name(&wiphy->dev)); } static ssize_t addresses_show(struct device *dev, struct device_attribute *attr, char *buf) { struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; char *start = buf; int i; if (!wiphy->addresses) return sprintf(buf, "%pM\n", wiphy->perm_addr); for (i = 0; i < wiphy->n_addresses; i++) buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr); return buf - start; } static struct device_attribute ieee80211_dev_attrs[] = { __ATTR_RO(index), __ATTR_RO(macaddress), __ATTR_RO(address_mask), __ATTR_RO(addresses), __ATTR_RO(name), {} }; static void wiphy_dev_release(struct device *dev) { struct cfg80211_registered_device *rdev = dev_to_rdev(dev); cfg80211_dev_free(rdev); } #ifdef CONFIG_HOTPLUG static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) { /* TODO, we probably need stuff here */ return 0; } #endif static int wiphy_suspend(struct device *dev, pm_message_t state) { struct cfg80211_registered_device *rdev = dev_to_rdev(dev); int ret = 0; rdev->suspend_at = get_seconds(); if (rdev->ops->suspend) { rtnl_lock(); if (rdev->wiphy.registered) ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); rtnl_unlock(); } return ret; } static int wiphy_resume(struct device *dev) { struct cfg80211_registered_device *rdev = dev_to_rdev(dev); int ret = 0; /* Age scan results with time spent in suspend */ spin_lock_bh(&rdev->bss_lock); cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); spin_unlock_bh(&rdev->bss_lock); if (rdev->ops->resume) { rtnl_lock(); if (rdev->wiphy.registered) ret = rdev->ops->resume(&rdev->wiphy); rtnl_unlock(); } return ret; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static const void *wiphy_namespace(struct device *d) { struct wiphy *wiphy = container_of(d, struct wiphy, dev); return wiphy_net(wiphy); } #endif struct class ieee80211_class = { .name = "ieee80211", .owner = THIS_MODULE, .dev_release = wiphy_dev_release, .dev_attrs = ieee80211_dev_attrs, #ifdef CONFIG_HOTPLUG .dev_uevent = wiphy_uevent, #endif .suspend = wiphy_suspend, .resume = wiphy_resume, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) .ns_type = &net_ns_type_operations, .namespace = wiphy_namespace, #endif }; int wiphy_sysfs_init(void) { return class_register(&ieee80211_class); } void wiphy_sysfs_exit(void) { class_unregister(&ieee80211_class); } compat-drivers-2012-09-18/net/wireless/util.c0000644000175000017500000007277512026211315020146 0ustar mcgrofmcgrof/* * Wireless utility functions * * Copyright 2007-2009 Johannes Berg */ #include #include #include #include #include #include #include #include "core.h" struct ieee80211_rate * ieee80211_get_response_rate(struct ieee80211_supported_band *sband, u32 basic_rates, int bitrate) { struct ieee80211_rate *result = &sband->bitrates[0]; int i; for (i = 0; i < sband->n_bitrates; i++) { if (!(basic_rates & BIT(i))) continue; if (sband->bitrates[i].bitrate > bitrate) continue; result = &sband->bitrates[i]; } return result; } EXPORT_SYMBOL(ieee80211_get_response_rate); int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) { /* see 802.11 17.3.8.3.2 and Annex J * there are overlapping channel numbers in 5GHz and 2GHz bands */ if (chan <= 0) return 0; /* not supported */ switch (band) { case IEEE80211_BAND_2GHZ: if (chan == 14) return 2484; else if (chan < 14) return 2407 + chan * 5; break; case IEEE80211_BAND_5GHZ: if (chan >= 182 && chan <= 196) return 4000 + chan * 5; else return 5000 + chan * 5; break; case IEEE80211_BAND_60GHZ: if (chan < 5) return 56160 + chan * 2160; break; default: ; } return 0; /* not supported */ } EXPORT_SYMBOL(ieee80211_channel_to_frequency); int ieee80211_frequency_to_channel(int freq) { /* see 802.11 17.3.8.3.2 and Annex J */ if (freq == 2484) return 14; else if (freq < 2484) return (freq - 2407) / 5; else if (freq >= 4910 && freq <= 4980) return (freq - 4000) / 5; else if (freq <= 45000) /* DMG band lower limit */ return (freq - 5000) / 5; else if (freq >= 58320 && freq <= 64800) return (freq - 56160) / 2160; else return 0; } EXPORT_SYMBOL(ieee80211_frequency_to_channel); struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, int freq) { enum ieee80211_band band; struct ieee80211_supported_band *sband; int i; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { if (sband->channels[i].center_freq == freq) return &sband->channels[i]; } } return NULL; } EXPORT_SYMBOL(__ieee80211_get_channel); static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, enum ieee80211_band band) { int i, want; switch (band) { case IEEE80211_BAND_5GHZ: want = 3; for (i = 0; i < sband->n_bitrates; i++) { if (sband->bitrates[i].bitrate == 60 || sband->bitrates[i].bitrate == 120 || sband->bitrates[i].bitrate == 240) { sband->bitrates[i].flags |= IEEE80211_RATE_MANDATORY_A; want--; } } WARN_ON(want); break; case IEEE80211_BAND_2GHZ: want = 7; for (i = 0; i < sband->n_bitrates; i++) { if (sband->bitrates[i].bitrate == 10) { sband->bitrates[i].flags |= IEEE80211_RATE_MANDATORY_B | IEEE80211_RATE_MANDATORY_G; want--; } if (sband->bitrates[i].bitrate == 20 || sband->bitrates[i].bitrate == 55 || sband->bitrates[i].bitrate == 110 || sband->bitrates[i].bitrate == 60 || sband->bitrates[i].bitrate == 120 || sband->bitrates[i].bitrate == 240) { sband->bitrates[i].flags |= IEEE80211_RATE_MANDATORY_G; want--; } if (sband->bitrates[i].bitrate != 10 && sband->bitrates[i].bitrate != 20 && sband->bitrates[i].bitrate != 55 && sband->bitrates[i].bitrate != 110) sband->bitrates[i].flags |= IEEE80211_RATE_ERP_G; } WARN_ON(want != 0 && want != 3 && want != 6); break; case IEEE80211_BAND_60GHZ: /* check for mandatory HT MCS 1..4 */ WARN_ON(!sband->ht_cap.ht_supported); WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e); break; case IEEE80211_NUM_BANDS: WARN_ON(1); break; } } void ieee80211_set_bitrate_flags(struct wiphy *wiphy) { enum ieee80211_band band; for (band = 0; band < IEEE80211_NUM_BANDS; band++) if (wiphy->bands[band]) set_mandatory_flags_band(wiphy->bands[band], band); } bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher) { int i; for (i = 0; i < wiphy->n_cipher_suites; i++) if (cipher == wiphy->cipher_suites[i]) return true; return false; } int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr) { if (key_idx > 5) return -EINVAL; if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) return -EINVAL; if (pairwise && !mac_addr) return -EINVAL; /* * Disallow pairwise keys with non-zero index unless it's WEP * or a vendor specific cipher (because current deployments use * pairwise WEP keys with non-zero indices and for vendor specific * ciphers this should be validated in the driver or hardware level * - but 802.11i clearly specifies to use zero) */ if (pairwise && key_idx && ((params->cipher == WLAN_CIPHER_SUITE_TKIP) || (params->cipher == WLAN_CIPHER_SUITE_CCMP) || (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC))) return -EINVAL; switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: if (params->key_len != WLAN_KEY_LEN_WEP40) return -EINVAL; break; case WLAN_CIPHER_SUITE_TKIP: if (params->key_len != WLAN_KEY_LEN_TKIP) return -EINVAL; break; case WLAN_CIPHER_SUITE_CCMP: if (params->key_len != WLAN_KEY_LEN_CCMP) return -EINVAL; break; case WLAN_CIPHER_SUITE_WEP104: if (params->key_len != WLAN_KEY_LEN_WEP104) return -EINVAL; break; case WLAN_CIPHER_SUITE_AES_CMAC: if (params->key_len != WLAN_KEY_LEN_AES_CMAC) return -EINVAL; break; default: /* * We don't know anything about this algorithm, * allow using it -- but the driver must check * all parameters! We still check below whether * or not the driver supports this algorithm, * of course. */ break; } if (params->seq) { switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: /* These ciphers do not use key sequence */ return -EINVAL; case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_AES_CMAC: if (params->seq_len != 6) return -EINVAL; break; } } if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher)) return -EINVAL; return 0; } unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc) { unsigned int hdrlen = 24; if (ieee80211_is_data(fc)) { if (ieee80211_has_a4(fc)) hdrlen = 30; if (ieee80211_is_data_qos(fc)) { hdrlen += IEEE80211_QOS_CTL_LEN; if (ieee80211_has_order(fc)) hdrlen += IEEE80211_HT_CTL_LEN; } goto out; } if (ieee80211_is_ctl(fc)) { /* * ACK and CTS are 10 bytes, all others 16. To see how * to get this condition consider * subtype mask: 0b0000000011110000 (0x00F0) * ACK subtype: 0b0000000011010000 (0x00D0) * CTS subtype: 0b0000000011000000 (0x00C0) * bits that matter: ^^^ (0x00E0) * value of those: 0b0000000011000000 (0x00C0) */ if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0)) hdrlen = 10; else hdrlen = 16; } out: return hdrlen; } EXPORT_SYMBOL(ieee80211_hdrlen); unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) { const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)skb->data; unsigned int hdrlen; if (unlikely(skb->len < 10)) return 0; hdrlen = ieee80211_hdrlen(hdr->frame_control); if (unlikely(hdrlen > skb->len)) return 0; return hdrlen; } EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) { int ae = meshhdr->flags & MESH_FLAGS_AE; /* 7.1.3.5a.2 */ switch (ae) { case 0: return 6; case MESH_FLAGS_AE_A4: return 12; case MESH_FLAGS_AE_A5_A6: return 18; case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6): return 24; default: return 6; } } int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u16 hdrlen, ethertype; u8 *payload; u8 dst[ETH_ALEN]; u8 src[ETH_ALEN] __aligned(2); if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) return -1; hdrlen = ieee80211_hdrlen(hdr->frame_control); /* convert IEEE 802.11 header + possible LLC headers into Ethernet * header * IEEE 802.11 address fields: * ToDS FromDS Addr1 Addr2 Addr3 Addr4 * 0 0 DA SA BSSID n/a * 0 1 DA BSSID SA n/a * 1 0 BSSID SA DA n/a * 1 1 RA TA DA SA */ memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN); memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN); switch (hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { case cpu_to_le16(IEEE80211_FCTL_TODS): if (unlikely(iftype != NL80211_IFTYPE_AP && iftype != NL80211_IFTYPE_AP_VLAN && iftype != NL80211_IFTYPE_P2P_GO)) return -1; break; case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): if (unlikely(iftype != NL80211_IFTYPE_WDS && iftype != NL80211_IFTYPE_MESH_POINT && iftype != NL80211_IFTYPE_AP_VLAN && iftype != NL80211_IFTYPE_STATION)) return -1; if (iftype == NL80211_IFTYPE_MESH_POINT) { struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); /* make sure meshdr->flags is on the linear part */ if (!pskb_may_pull(skb, hdrlen + 1)) return -1; if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { skb_copy_bits(skb, hdrlen + offsetof(struct ieee80211s_hdr, eaddr1), dst, ETH_ALEN); skb_copy_bits(skb, hdrlen + offsetof(struct ieee80211s_hdr, eaddr2), src, ETH_ALEN); } hdrlen += ieee80211_get_mesh_hdrlen(meshdr); } break; case cpu_to_le16(IEEE80211_FCTL_FROMDS): if ((iftype != NL80211_IFTYPE_STATION && iftype != NL80211_IFTYPE_P2P_CLIENT && iftype != NL80211_IFTYPE_MESH_POINT) || (is_multicast_ether_addr(dst) && ether_addr_equal(src, addr))) return -1; if (iftype == NL80211_IFTYPE_MESH_POINT) { struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); /* make sure meshdr->flags is on the linear part */ if (!pskb_may_pull(skb, hdrlen + 1)) return -1; if (meshdr->flags & MESH_FLAGS_AE_A4) skb_copy_bits(skb, hdrlen + offsetof(struct ieee80211s_hdr, eaddr1), src, ETH_ALEN); hdrlen += ieee80211_get_mesh_hdrlen(meshdr); } break; case cpu_to_le16(0): if (iftype != NL80211_IFTYPE_ADHOC && iftype != NL80211_IFTYPE_STATION) return -1; break; } if (!pskb_may_pull(skb, hdrlen + 8)) return -1; payload = skb->data + hdrlen; ethertype = (payload[6] << 8) | payload[7]; if (likely((ether_addr_equal(payload, rfc1042_header) && ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || ether_addr_equal(payload, bridge_tunnel_header))) { /* remove RFC1042 or Bridge-Tunnel encapsulation and * replace EtherType */ skb_pull(skb, hdrlen + 6); memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); } else { struct ethhdr *ehdr; __be16 len; skb_pull(skb, hdrlen); len = htons(skb->len); ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); memcpy(ehdr->h_dest, dst, ETH_ALEN); memcpy(ehdr->h_source, src, ETH_ALEN); ehdr->h_proto = len; } return 0; } EXPORT_SYMBOL(ieee80211_data_to_8023); int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype, u8 *bssid, bool qos) { struct ieee80211_hdr hdr; u16 hdrlen, ethertype; __le16 fc; const u8 *encaps_data; int encaps_len, skip_header_bytes; int nh_pos, h_pos; int head_need; if (unlikely(skb->len < ETH_HLEN)) return -EINVAL; nh_pos = skb_network_header(skb) - skb->data; h_pos = skb_transport_header(skb) - skb->data; /* convert Ethernet header to proper 802.11 header (based on * operation mode) */ ethertype = (skb->data[12] << 8) | skb->data[13]; fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, addr, ETH_ALEN); memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 24; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ memcpy(hdr.addr1, bssid, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); hdrlen = 24; break; case NL80211_IFTYPE_ADHOC: /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, bssid, ETH_ALEN); hdrlen = 24; break; default: return -EOPNOTSUPP; } if (qos) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } hdr.frame_control = fc; hdr.duration_id = 0; hdr.seq_ctrl = 0; skip_header_bytes = ETH_HLEN; if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { encaps_data = bridge_tunnel_header; encaps_len = sizeof(bridge_tunnel_header); skip_header_bytes -= 2; } else if (ethertype > 0x600) { encaps_data = rfc1042_header; encaps_len = sizeof(rfc1042_header); skip_header_bytes -= 2; } else { encaps_data = NULL; encaps_len = 0; } skb_pull(skb, skip_header_bytes); nh_pos -= skip_header_bytes; h_pos -= skip_header_bytes; head_need = hdrlen + encaps_len - skb_headroom(skb); if (head_need > 0 || skb_cloned(skb)) { head_need = max(head_need, 0); if (head_need) skb_orphan(skb); if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) return -ENOMEM; skb->truesize += head_need; } if (encaps_data) { memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); nh_pos += encaps_len; h_pos += encaps_len; } memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); nh_pos += hdrlen; h_pos += hdrlen; /* Update skb pointers to various headers since this modified frame * is going to go through Linux networking code that may potentially * need things like pointer to IP header. */ skb_set_mac_header(skb, 0); skb_set_network_header(skb, nh_pos); skb_set_transport_header(skb, h_pos); return 0; } EXPORT_SYMBOL(ieee80211_data_from_8023); void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, const u8 *addr, enum nl80211_iftype iftype, const unsigned int extra_headroom, bool has_80211_header) { struct sk_buff *frame = NULL; u16 ethertype; u8 *payload; const struct ethhdr *eth; int remaining, err; u8 dst[ETH_ALEN], src[ETH_ALEN]; if (has_80211_header) { err = ieee80211_data_to_8023(skb, addr, iftype); if (err) goto out; /* skip the wrapping header */ eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); if (!eth) goto out; } else { eth = (struct ethhdr *) skb->data; } while (skb != frame) { u8 padding; __be16 len = eth->h_proto; unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); remaining = skb->len; memcpy(dst, eth->h_dest, ETH_ALEN); memcpy(src, eth->h_source, ETH_ALEN); padding = (4 - subframe_len) & 0x3; /* the last MSDU has no padding */ if (subframe_len > remaining) goto purge; skb_pull(skb, sizeof(struct ethhdr)); /* reuse skb for the last subframe */ if (remaining <= subframe_len + padding) frame = skb; else { unsigned int hlen = ALIGN(extra_headroom, 4); /* * Allocate and reserve two bytes more for payload * alignment since sizeof(struct ethhdr) is 14. */ frame = dev_alloc_skb(hlen + subframe_len + 2); if (!frame) goto purge; skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); memcpy(skb_put(frame, ntohs(len)), skb->data, ntohs(len)); eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + padding); if (!eth) { dev_kfree_skb(frame); goto purge; } } skb_reset_network_header(frame); frame->dev = skb->dev; frame->priority = skb->priority; payload = frame->data; ethertype = (payload[6] << 8) | payload[7]; if (likely((ether_addr_equal(payload, rfc1042_header) && ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || ether_addr_equal(payload, bridge_tunnel_header))) { /* remove RFC1042 or Bridge-Tunnel * encapsulation and replace EtherType */ skb_pull(frame, 6); memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); } else { memcpy(skb_push(frame, sizeof(__be16)), &len, sizeof(__be16)); memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); } __skb_queue_tail(list, frame); } return; purge: __skb_queue_purge(list); out: dev_kfree_skb(skb); } EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); /* Given a data frame determine the 802.1p/1d tag to use. */ unsigned int cfg80211_classify8021d(struct sk_buff *skb) { unsigned int dscp; /* skb->priority values from 256->263 are magic values to * directly indicate a specific 802.1d priority. This is used * to allow 802.1d priority to be passed directly in from VLAN * tags, etc. */ if (skb->priority >= 256 && skb->priority <= 263) return skb->priority - 256; switch (skb->protocol) { case htons(ETH_P_IP): dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc; break; case htons(ETH_P_IPV6): dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc; break; default: return 0; } return dscp >> 5; } EXPORT_SYMBOL(cfg80211_classify8021d); const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) { if (bss->information_elements == NULL) return NULL; return cfg80211_find_ie(ie, bss->information_elements, bss->len_information_elements); } EXPORT_SYMBOL(ieee80211_bss_get_ie); void cfg80211_upload_connect_keys(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct net_device *dev = wdev->netdev; int i; if (!wdev->connect_keys) return; for (i = 0; i < 6; i++) { if (!wdev->connect_keys->params[i].cipher) continue; if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL, &wdev->connect_keys->params[i])) { netdev_err(dev, "failed to set key %d\n", i); continue; } if (wdev->connect_keys->def == i) if (rdev->ops->set_default_key(wdev->wiphy, dev, i, true, true)) { netdev_err(dev, "failed to set defkey %d\n", i); continue; } if (wdev->connect_keys->defmgmt == i) if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) netdev_err(dev, "failed to set mgtdef %d\n", i); } kfree(wdev->connect_keys); wdev->connect_keys = NULL; } void cfg80211_process_wdev_events(struct wireless_dev *wdev) { struct cfg80211_event *ev; unsigned long flags; const u8 *bssid = NULL; spin_lock_irqsave(&wdev->event_lock, flags); while (!list_empty(&wdev->event_list)) { ev = list_first_entry(&wdev->event_list, struct cfg80211_event, list); list_del(&ev->list); spin_unlock_irqrestore(&wdev->event_lock, flags); wdev_lock(wdev); switch (ev->type) { case EVENT_CONNECT_RESULT: if (!is_zero_ether_addr(ev->cr.bssid)) bssid = ev->cr.bssid; __cfg80211_connect_result( wdev->netdev, bssid, ev->cr.req_ie, ev->cr.req_ie_len, ev->cr.resp_ie, ev->cr.resp_ie_len, ev->cr.status, ev->cr.status == WLAN_STATUS_SUCCESS, NULL); break; case EVENT_ROAMED: __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, ev->rm.req_ie_len, ev->rm.resp_ie, ev->rm.resp_ie_len); break; case EVENT_DISCONNECTED: __cfg80211_disconnected(wdev->netdev, ev->dc.ie, ev->dc.ie_len, ev->dc.reason, true); break; case EVENT_IBSS_JOINED: __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); break; } wdev_unlock(wdev); kfree(ev); spin_lock_irqsave(&wdev->event_lock, flags); } spin_unlock_irqrestore(&wdev->event_lock, flags); } void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) { struct wireless_dev *wdev; ASSERT_RTNL(); ASSERT_RDEV_LOCK(rdev); mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) cfg80211_process_wdev_events(wdev); mutex_unlock(&rdev->devlist_mtx); } int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, u32 *flags, struct vif_params *params) { int err; enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; ASSERT_RDEV_LOCK(rdev); /* don't support changing VLANs, you just re-create them */ if (otype == NL80211_IFTYPE_AP_VLAN) return -EOPNOTSUPP; /* cannot change into P2P device type */ if (ntype == NL80211_IFTYPE_P2P_DEVICE) return -EOPNOTSUPP; if (!rdev->ops->change_virtual_intf || !(rdev->wiphy.interface_modes & (1 << ntype))) return -EOPNOTSUPP; /* if it's part of a bridge, reject changing type to station/ibss */ if (br_port_exists(dev) && (ntype == NL80211_IFTYPE_ADHOC || ntype == NL80211_IFTYPE_STATION || ntype == NL80211_IFTYPE_P2P_CLIENT)) return -EBUSY; if (ntype != otype && netif_running(dev)) { mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, ntype); mutex_unlock(&rdev->devlist_mtx); if (err) return err; dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->mesh_id_up_len = 0; switch (otype) { case NL80211_IFTYPE_AP: cfg80211_stop_ap(rdev, dev); break; case NL80211_IFTYPE_ADHOC: cfg80211_leave_ibss(rdev, dev, false); break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); break; case NL80211_IFTYPE_MESH_POINT: /* mesh should be handled? */ break; default: break; } cfg80211_process_rdev_events(rdev); } err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, ntype, flags, params); WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); if (!err && params && params->use_4addr != -1) dev->ieee80211_ptr->use_4addr = params->use_4addr; if (!err) { dev->priv_flags &= ~IFF_DONT_BRIDGE; switch (ntype) { case NL80211_IFTYPE_STATION: if (dev->ieee80211_ptr->use_4addr) break; /* fall through */ case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_ADHOC: dev->priv_flags |= IFF_DONT_BRIDGE; break; case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: /* bridging OK */ break; case NL80211_IFTYPE_MONITOR: /* monitor can't bridge anyway */ break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: /* not happening */ break; case NL80211_IFTYPE_P2P_DEVICE: WARN_ON(1); break; } } if (!err && ntype != otype && netif_running(dev)) { cfg80211_update_iface_num(rdev, ntype, 1); cfg80211_update_iface_num(rdev, otype, -1); } return err; } static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate) { static const u32 __mcs2bitrate[] = { /* control PHY */ [0] = 275, /* SC PHY */ [1] = 3850, [2] = 7700, [3] = 9625, [4] = 11550, [5] = 12512, /* 1251.25 mbps */ [6] = 15400, [7] = 19250, [8] = 23100, [9] = 25025, [10] = 30800, [11] = 38500, [12] = 46200, /* OFDM PHY */ [13] = 6930, [14] = 8662, /* 866.25 mbps */ [15] = 13860, [16] = 17325, [17] = 20790, [18] = 27720, [19] = 34650, [20] = 41580, [21] = 45045, [22] = 51975, [23] = 62370, [24] = 67568, /* 6756.75 mbps */ /* LP-SC PHY */ [25] = 6260, [26] = 8340, [27] = 11120, [28] = 12510, [29] = 16680, [30] = 22240, [31] = 25030, }; if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate))) return 0; return __mcs2bitrate[rate->mcs]; } u32 cfg80211_calculate_bitrate(struct rate_info *rate) { int modulation, streams, bitrate; if (!(rate->flags & RATE_INFO_FLAGS_MCS)) return rate->legacy; if (rate->flags & RATE_INFO_FLAGS_60G) return cfg80211_calculate_bitrate_60g(rate); /* the formula below does only work for MCS values smaller than 32 */ if (WARN_ON_ONCE(rate->mcs >= 32)) return 0; modulation = rate->mcs & 7; streams = (rate->mcs >> 3) + 1; bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? 13500000 : 6500000; if (modulation < 4) bitrate *= (modulation + 1); else if (modulation == 4) bitrate *= (modulation + 2); else bitrate *= (modulation + 3); bitrate *= streams; if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) bitrate = (bitrate / 9) * 10; /* do NOT round down here */ return (bitrate + 50000) / 100000; } EXPORT_SYMBOL(cfg80211_calculate_bitrate); int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int) { struct wireless_dev *wdev; int res = 0; if (!beacon_int) return -EINVAL; mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (!wdev->beacon_interval) continue; if (wdev->beacon_interval != beacon_int) { res = -EINVAL; break; } } mutex_unlock(&rdev->devlist_mtx); return res; } int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype, struct ieee80211_channel *chan, enum cfg80211_chan_mode chanmode) { struct wireless_dev *wdev_iter; u32 used_iftypes = BIT(iftype); int num[NUM_NL80211_IFTYPES]; struct ieee80211_channel *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS]; struct ieee80211_channel *ch; enum cfg80211_chan_mode chmode; int num_different_channels = 0; int total = 1; int i, j; ASSERT_RTNL(); lockdep_assert_held(&rdev->devlist_mtx); /* Always allow software iftypes */ if (rdev->wiphy.software_iftypes & BIT(iftype)) return 0; memset(num, 0, sizeof(num)); memset(used_channels, 0, sizeof(used_channels)); num[iftype] = 1; switch (chanmode) { case CHAN_MODE_UNDEFINED: break; case CHAN_MODE_SHARED: WARN_ON(!chan); used_channels[0] = chan; num_different_channels++; break; case CHAN_MODE_EXCLUSIVE: num_different_channels++; break; } list_for_each_entry(wdev_iter, &rdev->wdev_list, list) { if (wdev_iter == wdev) continue; if (wdev_iter->netdev) { if (!netif_running(wdev_iter->netdev)) continue; } else if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) { if (!wdev_iter->p2p_started) continue; } else { WARN_ON(1); } if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) continue; /* * We may be holding the "wdev" mutex, but now need to lock * wdev_iter. This is OK because once we get here wdev_iter * is not wdev (tested above), but we need to use the nested * locking for lockdep. */ mutex_lock_nested(&wdev_iter->mtx, 1); __acquire(wdev_iter->mtx); cfg80211_get_chan_state(wdev_iter, &ch, &chmode); wdev_unlock(wdev_iter); switch (chmode) { case CHAN_MODE_UNDEFINED: break; case CHAN_MODE_SHARED: for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++) if (!used_channels[i] || used_channels[i] == ch) break; if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS) return -EBUSY; if (used_channels[i] == NULL) { used_channels[i] = ch; num_different_channels++; } break; case CHAN_MODE_EXCLUSIVE: num_different_channels++; break; } num[wdev_iter->iftype]++; total++; used_iftypes |= BIT(wdev_iter->iftype); } if (total == 1) return 0; for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { const struct ieee80211_iface_combination *c; struct ieee80211_iface_limit *limits; u32 all_iftypes = 0; c = &rdev->wiphy.iface_combinations[i]; if (total > c->max_interfaces) continue; if (num_different_channels > c->num_different_channels) continue; limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, GFP_KERNEL); if (!limits) return -ENOMEM; for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { if (rdev->wiphy.software_iftypes & BIT(iftype)) continue; for (j = 0; j < c->n_limits; j++) { all_iftypes |= limits[j].types; if (!(limits[j].types & BIT(iftype))) continue; if (limits[j].max < num[iftype]) goto cont; limits[j].max -= num[iftype]; } } /* * Finally check that all iftypes that we're currently * using are actually part of this combination. If they * aren't then we can't use this combination and have * to continue to the next. */ if ((all_iftypes & used_iftypes) != used_iftypes) goto cont; /* * This combination covered all interface types and * supported the requested numbers, so we're good. */ kfree(limits); return 0; cont: kfree(limits); } return -EBUSY; } int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, const u8 *rates, unsigned int n_rates, u32 *mask) { int i, j; if (!sband) return -EINVAL; if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES) return -EINVAL; *mask = 0; for (i = 0; i < n_rates; i++) { int rate = (rates[i] & 0x7f) * 5; bool found = false; for (j = 0; j < sband->n_bitrates; j++) { if (sband->bitrates[j].bitrate == rate) { found = true; *mask |= BIT(j); break; } } if (!found) return -EINVAL; } /* * mask must have at least one bit set here since we * didn't accept a 0-length rates array nor allowed * entries in the array that didn't exist */ return 0; } /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ const unsigned char rfc1042_header[] __aligned(2) = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; EXPORT_SYMBOL(rfc1042_header); /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ const unsigned char bridge_tunnel_header[] __aligned(2) = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; EXPORT_SYMBOL(bridge_tunnel_header); compat-drivers-2012-09-18/net/wireless/wext-core.c0000644000175000017500000007373112026211315021077 0ustar mcgrofmcgrof/* * This file implement the Wireless Extensions core API. * * Authors : Jean Tourrilhes - HPL - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. * Copyright 2009 Johannes Berg * * (As all part of the Linux kernel, this file is GPL) */ #include #include #include #include #include #include #include #include #include #include #include #include typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, unsigned int, struct iw_request_info *, iw_handler); /* * Meta-data about all the standard Wireless Extension request we * know about. */ static const struct iw_ioctl_description standard_ioctl[] = { [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { .header_type = IW_HEADER_TYPE_NULL, }, [IW_IOCTL_IDX(SIOCGIWNAME)] = { .header_type = IW_HEADER_TYPE_CHAR, .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWNWID)] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_EVENT, }, [IW_IOCTL_IDX(SIOCGIWNWID)] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWFREQ)] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_EVENT, }, [IW_IOCTL_IDX(SIOCGIWFREQ)] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWMODE)] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_EVENT, }, [IW_IOCTL_IDX(SIOCGIWMODE)] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWSENS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWSENS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWRANGE)] = { .header_type = IW_HEADER_TYPE_NULL, }, [IW_IOCTL_IDX(SIOCGIWRANGE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_range), .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWPRIV)] = { .header_type = IW_HEADER_TYPE_NULL, }, [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_priv_args), .max_tokens = 16, .flags = IW_DESCR_FLAG_NOMAX, }, [IW_IOCTL_IDX(SIOCSIWSTATS)] = { .header_type = IW_HEADER_TYPE_NULL, }, [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_statistics), .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr), .max_tokens = IW_MAX_SPY, }, [IW_IOCTL_IDX(SIOCGIWSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_SPY, }, [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, [IW_IOCTL_IDX(SIOCSIWAP)] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IW_IOCTL_IDX(SIOCGIWAP)] = { .header_type = IW_HEADER_TYPE_ADDR, .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWMLME)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_mlme), .max_tokens = sizeof(struct iw_mlme), }, [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_AP, .flags = IW_DESCR_FLAG_NOMAX, }, [IW_IOCTL_IDX(SIOCSIWSCAN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = 0, .max_tokens = sizeof(struct iw_scan_req), }, [IW_IOCTL_IDX(SIOCGIWSCAN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_SCAN_MAX_DATA, .flags = IW_DESCR_FLAG_NOMAX, }, [IW_IOCTL_IDX(SIOCSIWESSID)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_EVENT, }, [IW_IOCTL_IDX(SIOCGIWESSID)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_DUMP, }, [IW_IOCTL_IDX(SIOCSIWNICKN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, [IW_IOCTL_IDX(SIOCGIWNICKN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, [IW_IOCTL_IDX(SIOCSIWRATE)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWRATE)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWRTS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWRTS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWFRAG)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWFRAG)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWRETRY)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWRETRY)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWENCODE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, }, [IW_IOCTL_IDX(SIOCGIWENCODE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, }, [IW_IOCTL_IDX(SIOCSIWPOWER)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWPOWER)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IW_IOCTL_IDX(SIOCGIWGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IW_IOCTL_IDX(SIOCSIWAUTH)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCGIWAUTH)] = { .header_type = IW_HEADER_TYPE_PARAM, }, [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_pmksa), .max_tokens = sizeof(struct iw_pmksa), }, }; static const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl); /* * Meta-data about all the additional standard Wireless Extension events * we know about. */ static const struct iw_ioctl_description standard_event[] = { [IW_EVENT_IDX(IWEVTXDROP)] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IW_EVENT_IDX(IWEVQUAL)] = { .header_type = IW_HEADER_TYPE_QUAL, }, [IW_EVENT_IDX(IWEVCUSTOM)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_CUSTOM_MAX, }, [IW_EVENT_IDX(IWEVREGISTERED)] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IW_EVENT_IDX(IWEVEXPIRED)] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IW_EVENT_IDX(IWEVGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_michaelmicfailure), }, [IW_EVENT_IDX(IWEVASSOCREQIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, [IW_EVENT_IDX(IWEVPMKIDCAND)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_pmkid_cand), }, }; static const unsigned int standard_event_num = ARRAY_SIZE(standard_event); /* Size (in bytes) of various events */ static const int event_type_size[] = { IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 0, IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 0, IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 0, IW_EV_POINT_LEN, /* Without variable payload */ IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ }; #ifdef CONFIG_COMPAT static const int compat_event_type_size[] = { IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 0, IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 0, IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 0, IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ }; #endif /* IW event code */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static int __net_init wext_pernet_init(struct net *net) { skb_queue_head_init(&net->wext_nlevents); return 0; } static void __net_exit wext_pernet_exit(struct net *net) { skb_queue_purge(&net->wext_nlevents); } static struct pernet_operations wext_pernet_ops = { .init = wext_pernet_init, .exit = wext_pernet_exit, }; static int __init wireless_nlevent_init(void) { return register_pernet_subsys(&wext_pernet_ops); } subsys_initcall(wireless_nlevent_init); /* Process events generated by the wireless layer or the driver. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) static void wireless_nlevent_process(struct work_struct *work) { struct sk_buff *skb; struct net *net; rtnl_lock(); for_each_net(net) { while ((skb = skb_dequeue(&net->wext_nlevents))) rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL); } rtnl_unlock(); } #else static void wireless_nlevent_process(struct work_struct *work) { struct sk_buff *skb; struct net *net; rtnl_lock(); while ((skb = skb_dequeue(&net->wext_nlevents))) rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); rtnl_unlock(); } #endif static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); #else /* Older kernels get the old way of doing stuff*/ static struct sk_buff_head wireless_nlevent_queue; static int __init wireless_nlevent_init(void) { skb_queue_head_init(&wireless_nlevent_queue); return 0; } subsys_initcall(wireless_nlevent_init); static void wireless_nlevent_process(unsigned long data) { struct sk_buff *skb; while ((skb = skb_dequeue(&wireless_nlevent_queue))) rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); } static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); #endif static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, struct sk_buff *skb) { struct ifinfomsg *r; struct nlmsghdr *nlh; nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); if (!nlh) return NULL; r = nlmsg_data(nlh); r->ifi_family = AF_UNSPEC; r->__ifi_pad = 0; r->ifi_type = dev->type; r->ifi_index = dev->ifindex; r->ifi_flags = dev_get_flags(dev); r->ifi_change = 0; /* Wireless changes don't affect those flags */ if (nla_put_string(skb, IFLA_IFNAME, dev->name)) goto nla_put_failure; return nlh; nla_put_failure: nlmsg_cancel(skb, nlh); return NULL; } /* * Main event dispatcher. Called from other parts and drivers. * Send the event on the appropriate channels. * May be called from interrupt context. */ void wireless_send_event(struct net_device * dev, unsigned int cmd, union iwreq_data * wrqu, const char * extra) { const struct iw_ioctl_description * descr = NULL; int extra_len = 0; struct iw_event *event; /* Mallocated whole event */ int event_len; /* Its size */ int hdr_len; /* Size of the event header */ int wrqu_off = 0; /* Offset in wrqu */ /* Don't "optimise" the following variable, it will crash */ unsigned int cmd_index; /* *MUST* be unsigned */ struct sk_buff *skb; struct nlmsghdr *nlh; struct nlattr *nla; #ifdef CONFIG_COMPAT struct __compat_iw_event *compat_event; struct compat_iw_point compat_wrqu; struct sk_buff *compskb; #endif /* * Nothing in the kernel sends scan events with data, be safe. * This is necessary because we cannot fix up scan event data * for compat, due to being contained in 'extra', but normally * applications are required to retrieve the scan data anyway * and no data is included in the event, this codifies that * practice. */ if (WARN_ON(cmd == SIOCGIWSCAN && extra)) extra = NULL; /* Get the description of the Event */ if (cmd <= SIOCIWLAST) { cmd_index = IW_IOCTL_IDX(cmd); if (cmd_index < standard_ioctl_num) descr = &(standard_ioctl[cmd_index]); } else { cmd_index = IW_EVENT_IDX(cmd); if (cmd_index < standard_event_num) descr = &(standard_event[cmd_index]); } /* Don't accept unknown events */ if (descr == NULL) { /* Note : we don't return an error to the driver, because * the driver would not know what to do about it. It can't * return an error to the user, because the event is not * initiated by a user request. * The best the driver could do is to log an error message. * We will do it ourselves instead... */ netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", cmd); return; } /* Check extra parameters and set extra_len */ if (descr->header_type == IW_HEADER_TYPE_POINT) { /* Check if number of token fits within bounds */ if (wrqu->data.length > descr->max_tokens) { netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too big (%d)\n", cmd, wrqu->data.length); return; } if (wrqu->data.length < descr->min_tokens) { netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too small (%d)\n", cmd, wrqu->data.length); return; } /* Calculate extra_len - extra is NULL for restricted events */ if (extra != NULL) extra_len = wrqu->data.length * descr->token_size; /* Always at an offset in wrqu */ wrqu_off = IW_EV_POINT_OFF; } /* Total length of the event */ hdr_len = event_type_size[descr->header_type]; event_len = hdr_len + extra_len; /* * The problem for 64/32 bit. * * On 64-bit, a regular event is laid out as follows: * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * | event.len | event.cmd | p a d d i n g | * | wrqu data ... (with the correct size) | * * This padding exists because we manipulate event->u, * and 'event' is not packed. * * An iw_point event is laid out like this instead: * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * | event.len | event.cmd | p a d d i n g | * | iwpnt.len | iwpnt.flg | p a d d i n g | * | extra data ... * * The second padding exists because struct iw_point is extended, * but this depends on the platform... * * On 32-bit, all the padding shouldn't be there. */ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!skb) return; /* Send via the RtNetlink event channel */ nlh = rtnetlink_ifinfo_prep(dev, skb); if (WARN_ON(!nlh)) { kfree_skb(skb); return; } /* Add the wireless events in the netlink packet */ nla = nla_reserve(skb, IFLA_WIRELESS, event_len); if (!nla) { kfree_skb(skb); return; } event = nla_data(nla); /* Fill event - first clear to avoid data leaking */ memset(event, 0, hdr_len); event->len = event_len; event->cmd = cmd; memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); if (extra_len) memcpy(((char *) event) + hdr_len, extra, extra_len); nlmsg_end(skb, nlh); #ifdef CONFIG_COMPAT hdr_len = compat_event_type_size[descr->header_type]; event_len = hdr_len + extra_len; compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!compskb) { kfree_skb(skb); return; } /* Send via the RtNetlink event channel */ nlh = rtnetlink_ifinfo_prep(dev, compskb); if (WARN_ON(!nlh)) { kfree_skb(skb); kfree_skb(compskb); return; } /* Add the wireless events in the netlink packet */ nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); if (!nla) { kfree_skb(skb); kfree_skb(compskb); return; } compat_event = nla_data(nla); compat_event->len = event_len; compat_event->cmd = cmd; if (descr->header_type == IW_HEADER_TYPE_POINT) { compat_wrqu.length = wrqu->data.length; compat_wrqu.flags = wrqu->data.flags; memcpy(&compat_event->pointer, ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, hdr_len - IW_EV_COMPAT_LCP_LEN); if (extra_len) memcpy(((char *) compat_event) + hdr_len, extra, extra_len); } else { /* extra_len must be zero, so no if (extra) needed */ memcpy(&compat_event->pointer, wrqu, hdr_len - IW_EV_COMPAT_LCP_LEN); } nlmsg_end(compskb, nlh); skb_shinfo(skb)->frag_list = compskb; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); schedule_work(&wireless_nlevent_work); #else skb_queue_tail(&wireless_nlevent_queue, skb); tasklet_schedule(&wireless_nlevent_tasklet); #endif } EXPORT_SYMBOL(wireless_send_event); /* IW handlers */ struct iw_statistics *get_wireless_stats(struct net_device *dev) { #ifdef CONFIG_WIRELESS_EXT if ((dev->wireless_handlers != NULL) && (dev->wireless_handlers->get_wireless_stats != NULL)) return dev->wireless_handlers->get_wireless_stats(dev); #endif #ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy && dev->ieee80211_ptr->wiphy->wext && dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); #endif /* not found */ return NULL; } static int iw_handler_get_iwstats(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra) { /* Get stats from the driver */ struct iw_statistics *stats; stats = get_wireless_stats(dev); if (stats) { /* Copy statistics to extra */ memcpy(extra, stats, sizeof(struct iw_statistics)); wrqu->data.length = sizeof(struct iw_statistics); /* Check if we need to clear the updated flag */ if (wrqu->data.flags != 0) stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; return 0; } else return -EOPNOTSUPP; } static iw_handler get_handler(struct net_device *dev, unsigned int cmd) { /* Don't "optimise" the following variable, it will crash */ unsigned int index; /* *MUST* be unsigned */ const struct iw_handler_def *handlers = NULL; #ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) handlers = dev->ieee80211_ptr->wiphy->wext; #endif #ifdef CONFIG_WIRELESS_EXT if (dev->wireless_handlers) handlers = dev->wireless_handlers; #endif if (!handlers) return NULL; /* Try as a standard command */ index = IW_IOCTL_IDX(cmd); if (index < handlers->num_standard) return handlers->standard[index]; #ifdef CONFIG_WEXT_PRIV /* Try as a private command */ index = cmd - SIOCIWFIRSTPRIV; if (index < handlers->num_private) return handlers->private[index]; #endif /* Not found */ return NULL; } static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, const struct iw_ioctl_description *descr, iw_handler handler, struct net_device *dev, struct iw_request_info *info) { int err, extra_size, user_length = 0, essid_compat = 0; char *extra; /* Calculate space needed by arguments. Always allocate * for max space. */ extra_size = descr->max_tokens * descr->token_size; /* Check need for ESSID compatibility for WE < 21 */ switch (cmd) { case SIOCSIWESSID: case SIOCGIWESSID: case SIOCSIWNICKN: case SIOCGIWNICKN: if (iwp->length == descr->max_tokens + 1) essid_compat = 1; else if (IW_IS_SET(cmd) && (iwp->length != 0)) { char essid[IW_ESSID_MAX_SIZE + 1]; unsigned int len; len = iwp->length * descr->token_size; if (len > IW_ESSID_MAX_SIZE) return -EFAULT; err = copy_from_user(essid, iwp->pointer, len); if (err) return -EFAULT; if (essid[iwp->length - 1] == '\0') essid_compat = 1; } break; default: break; } iwp->length -= essid_compat; /* Check what user space is giving us */ if (IW_IS_SET(cmd)) { /* Check NULL pointer */ if (!iwp->pointer && iwp->length != 0) return -EFAULT; /* Check if number of token fits within bounds */ if (iwp->length > descr->max_tokens) return -E2BIG; if (iwp->length < descr->min_tokens) return -EINVAL; } else { /* Check NULL pointer */ if (!iwp->pointer) return -EFAULT; /* Save user space buffer size for checking */ user_length = iwp->length; /* Don't check if user_length > max to allow forward * compatibility. The test user_length < min is * implied by the test at the end. */ /* Support for very large requests */ if ((descr->flags & IW_DESCR_FLAG_NOMAX) && (user_length > descr->max_tokens)) { /* Allow userspace to GET more than max so * we can support any size GET requests. * There is still a limit : -ENOMEM. */ extra_size = user_length * descr->token_size; /* Note : user_length is originally a __u16, * and token_size is controlled by us, * so extra_size won't get negative and * won't overflow... */ } } /* kzalloc() ensures NULL-termination for essid_compat. */ extra = kzalloc(extra_size, GFP_KERNEL); if (!extra) return -ENOMEM; /* If it is a SET, get all the extra data in here */ if (IW_IS_SET(cmd) && (iwp->length != 0)) { if (copy_from_user(extra, iwp->pointer, iwp->length * descr->token_size)) { err = -EFAULT; goto out; } if (cmd == SIOCSIWENCODEEXT) { struct iw_encode_ext *ee = (void *) extra; if (iwp->length < sizeof(*ee) + ee->key_len) { err = -EFAULT; goto out; } } } if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { /* * If this is a GET, but not NOMAX, it means that the extra * data is not bounded by userspace, but by max_tokens. Thus * set the length to max_tokens. This matches the extra data * allocation. * The driver should fill it with the number of tokens it * provided, and it may check iwp->length rather than having * knowledge of max_tokens. If the driver doesn't change the * iwp->length, this ioctl just copies back max_token tokens * filled with zeroes. Hopefully the driver isn't claiming * them to be valid data. */ iwp->length = descr->max_tokens; } err = handler(dev, info, (union iwreq_data *) iwp, extra); iwp->length += essid_compat; /* If we have something to return to the user */ if (!err && IW_IS_GET(cmd)) { /* Check if there is enough buffer up there */ if (user_length < iwp->length) { err = -E2BIG; goto out; } if (copy_to_user(iwp->pointer, extra, iwp->length * descr->token_size)) { err = -EFAULT; goto out; } } /* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) && ((err == 0) || (err == -EIWCOMMIT))) { union iwreq_data *data = (union iwreq_data *) iwp; if (descr->flags & IW_DESCR_FLAG_RESTRICT) /* If the event is restricted, don't * export the payload. */ wireless_send_event(dev, cmd, data, NULL); else wireless_send_event(dev, cmd, data, extra); } out: kfree(extra); return err; } /* * Call the commit handler in the driver * (if exist and if conditions are right) * * Note : our current commit strategy is currently pretty dumb, * but we will be able to improve on that... * The goal is to try to agreagate as many changes as possible * before doing the commit. Drivers that will define a commit handler * are usually those that need a reset after changing parameters, so * we want to minimise the number of reset. * A cool idea is to use a timer : at each "set" command, we re-set the * timer, when the timer eventually fires, we call the driver. * Hopefully, more on that later. * * Also, I'm waiting to see how many people will complain about the * netif_running(dev) test. I'm open on that one... * Hopefully, the driver will remember to do a commit in "open()" ;-) */ int call_commit_handler(struct net_device *dev) { #ifdef CONFIG_WIRELESS_EXT if ((netif_running(dev)) && (dev->wireless_handlers->standard[0] != NULL)) /* Call the commit handler on the driver */ return dev->wireless_handlers->standard[0](dev, NULL, NULL, NULL); else return 0; /* Command completed successfully */ #else /* cfg80211 has no commit */ return 0; #endif } /* * Main IOCTl dispatcher. * Check the type of IOCTL and call the appropriate wrapper... */ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, struct iw_request_info *info, wext_ioctl_func standard, wext_ioctl_func private) { struct iwreq *iwr = (struct iwreq *) ifr; struct net_device *dev; iw_handler handler; /* Permissions are already checked in dev_ioctl() before calling us. * The copy_to/from_user() of ifr is also dealt with in there */ /* Make sure the device exist */ if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL) return -ENODEV; /* A bunch of special cases, then the generic case... * Note that 'cmd' is already filtered in dev_ioctl() with * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ if (cmd == SIOCGIWSTATS) return standard(dev, iwr, cmd, info, &iw_handler_get_iwstats); #ifdef CONFIG_WEXT_PRIV if (cmd == SIOCGIWPRIV && dev->wireless_handlers) return standard(dev, iwr, cmd, info, iw_handler_get_private); #endif /* Basic check */ if (!netif_device_present(dev)) return -ENODEV; /* New driver API : try to find the handler */ handler = get_handler(dev, cmd); if (handler) { /* Standard and private are not the same */ if (cmd < SIOCIWFIRSTPRIV) return standard(dev, iwr, cmd, info, handler); else if (private) return private(dev, iwr, cmd, info, handler); } /* Old driver API : call driver ioctl handler */ return ndo_do_ioctl(dev, ifr, cmd); } /* If command is `set a parameter', or `get the encoding parameters', * check if the user has the right to do it. */ static int wext_permission_check(unsigned int cmd) { if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) && !capable(CAP_NET_ADMIN)) return -EPERM; return 0; } /* entry point from dev ioctl */ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, unsigned int cmd, struct iw_request_info *info, wext_ioctl_func standard, wext_ioctl_func private) { int ret = wext_permission_check(cmd); if (ret) return ret; dev_load(net, ifr->ifr_name); rtnl_lock(); ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); rtnl_unlock(); return ret; } /* * Wrapper to call a standard Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. */ static int ioctl_standard_call(struct net_device * dev, struct iwreq *iwr, unsigned int cmd, struct iw_request_info *info, iw_handler handler) { const struct iw_ioctl_description * descr; int ret = -EINVAL; /* Get the description of the IOCTL */ if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) return -EOPNOTSUPP; descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); /* Check if we have a pointer to user space data or not */ if (descr->header_type != IW_HEADER_TYPE_POINT) { /* No extra arguments. Trivial to handle */ ret = handler(dev, info, &(iwr->u), NULL); /* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) && ((ret == 0) || (ret == -EIWCOMMIT))) wireless_send_event(dev, cmd, &(iwr->u), NULL); } else { ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, handler, dev, info); } /* Call commit handler if needed and defined */ if (ret == -EIWCOMMIT) ret = call_commit_handler(dev); /* Here, we will generate the appropriate event if needed */ return ret; } int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, void __user *arg) { struct iw_request_info info = { .cmd = cmd, .flags = 0 }; int ret; ret = wext_ioctl_dispatch(net, ifr, cmd, &info, ioctl_standard_call, ioctl_private_call); if (ret >= 0 && IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq))) return -EFAULT; return ret; } #ifdef CONFIG_COMPAT static int compat_standard_call(struct net_device *dev, struct iwreq *iwr, unsigned int cmd, struct iw_request_info *info, iw_handler handler) { const struct iw_ioctl_description *descr; struct compat_iw_point *iwp_compat; struct iw_point iwp; int err; descr = standard_ioctl + IW_IOCTL_IDX(cmd); if (descr->header_type != IW_HEADER_TYPE_POINT) return ioctl_standard_call(dev, iwr, cmd, info, handler); iwp_compat = (struct compat_iw_point *) &iwr->u.data; iwp.pointer = compat_ptr(iwp_compat->pointer); iwp.length = iwp_compat->length; iwp.flags = iwp_compat->flags; err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); iwp_compat->pointer = ptr_to_compat(iwp.pointer); iwp_compat->length = iwp.length; iwp_compat->flags = iwp.flags; return err; } int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; struct iw_request_info info; struct iwreq iwr; char *colon; int ret; if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) return -EFAULT; iwr.ifr_name[IFNAMSIZ-1] = 0; colon = strchr(iwr.ifr_name, ':'); if (colon) *colon = 0; info.cmd = cmd; info.flags = IW_REQUEST_FLAG_COMPAT; ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info, compat_standard_call, compat_private_call); if (ret >= 0 && IW_IS_GET(cmd) && copy_to_user(argp, &iwr, sizeof(struct iwreq))) return -EFAULT; return ret; } #endif compat-drivers-2012-09-18/net/wireless/wext-proc.c0000644000175000017500000001004512026211315021077 0ustar mcgrofmcgrof/* * This file implement the Wireless Extensions proc API. * * Authors : Jean Tourrilhes - HPL - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. * * (As all part of the Linux kernel, this file is GPL) */ /* * The /proc/net/wireless file is a human readable user-space interface * exporting various wireless specific statistics from the wireless devices. * This is the most popular part of the Wireless Extensions ;-) * * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). * The content of the file is basically the content of "struct iw_statistics". */ #include #include #include #include #include #include #include #include static void wireless_seq_printf_stats(struct seq_file *seq, struct net_device *dev) { /* Get stats from the driver */ struct iw_statistics *stats = get_wireless_stats(dev); static struct iw_statistics nullstats = {}; /* show device if it's wireless regardless of current stats */ if (!stats) { #ifdef CONFIG_WIRELESS_EXT if (dev->wireless_handlers) stats = &nullstats; #endif #ifdef CONFIG_CFG80211 if (dev->ieee80211_ptr) stats = &nullstats; #endif } if (stats) { seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " "%6d %6d %6d\n", dev->name, stats->status, stats->qual.qual, stats->qual.updated & IW_QUAL_QUAL_UPDATED ? '.' : ' ', ((__s32) stats->qual.level) - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), stats->qual.updated & IW_QUAL_LEVEL_UPDATED ? '.' : ' ', ((__s32) stats->qual.noise) - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), stats->qual.updated & IW_QUAL_NOISE_UPDATED ? '.' : ' ', stats->discard.nwid, stats->discard.code, stats->discard.fragment, stats->discard.retries, stats->discard.misc, stats->miss.beacon); if (stats != &nullstats) stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; } } /* ---------------------------------------------------------------- */ /* * Print info for /proc/net/wireless (print all entries) */ static int wireless_dev_seq_show(struct seq_file *seq, void *v) { might_sleep(); if (v == SEQ_START_TOKEN) seq_printf(seq, "Inter-| sta-| Quality | Discarded " "packets | Missed | WE\n" " face | tus | link level noise | nwid " "crypt frag retry misc | beacon | %d\n", WIRELESS_EXT); else wireless_seq_printf_stats(seq, v); return 0; } static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) { struct net *net = seq_file_net(seq); loff_t off; struct net_device *dev; rtnl_lock(); if (!*pos) return SEQ_START_TOKEN; off = 1; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) for_each_netdev(net, dev) #else for_each_netdev(net) #endif if (off++ == *pos) return dev; return NULL; } static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct net *net = seq_file_net(seq); ++*pos; return v == SEQ_START_TOKEN ? first_net_device(net) : next_net_device(v); } static void wireless_dev_seq_stop(struct seq_file *seq, void *v) { rtnl_unlock(); } static const struct seq_operations wireless_seq_ops = { .start = wireless_dev_seq_start, .next = wireless_dev_seq_next, .stop = wireless_dev_seq_stop, .show = wireless_dev_seq_show, }; static int seq_open_wireless(struct inode *inode, struct file *file) { return seq_open_net(inode, file, &wireless_seq_ops, sizeof(struct seq_net_private)); } static const struct file_operations wireless_seq_fops = { .owner = THIS_MODULE, .open = seq_open_wireless, .read = seq_read, .llseek = seq_lseek, .release = seq_release_net, }; int __net_init wext_proc_init(struct net *net) { /* Create /proc/net/wireless entry */ if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) return -ENOMEM; return 0; } void __net_exit wext_proc_exit(struct net *net) { proc_net_remove(net, "wireless"); } compat-drivers-2012-09-18/net/wireless/scan.c0000644000175000017500000010351012026211315020073 0ustar mcgrofmcgrof/* * cfg80211 scan result handling * * Copyright 2008 Johannes Berg */ #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "nl80211.h" #include "wext-compat.h" #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) { struct cfg80211_scan_request *request; struct wireless_dev *wdev; #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif ASSERT_RDEV_LOCK(rdev); request = rdev->scan_req; if (!request) return; wdev = request->wdev; /* * This must be before sending the other events! * Otherwise, wpa_supplicant gets completely confused with * wext events. */ if (wdev->netdev) cfg80211_sme_scan_done(wdev->netdev); if (request->aborted) nl80211_send_scan_aborted(rdev, wdev); else nl80211_send_scan_done(rdev, wdev); #ifdef CONFIG_CFG80211_WEXT if (wdev->netdev && !request->aborted) { memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL); } #endif if (wdev->netdev) dev_put(wdev->netdev); rdev->scan_req = NULL; /* * OK. If this is invoked with "leak" then we can't * free this ... but we've cleaned it up anyway. The * driver failed to call the scan_done callback, so * all bets are off, it might still be trying to use * the scan request or not ... if it accesses the dev * in there (it shouldn't anyway) then it may crash. */ if (!leak) kfree(request); } void __cfg80211_scan_done(struct work_struct *wk) { struct cfg80211_registered_device *rdev; rdev = container_of(wk, struct cfg80211_registered_device, scan_done_wk); cfg80211_lock_rdev(rdev); ___cfg80211_scan_done(rdev, false); cfg80211_unlock_rdev(rdev); } void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); } EXPORT_SYMBOL(cfg80211_scan_done); void __cfg80211_sched_scan_results(struct work_struct *wk) { struct cfg80211_registered_device *rdev; rdev = container_of(wk, struct cfg80211_registered_device, sched_scan_results_wk); mutex_lock(&rdev->sched_scan_mtx); /* we don't have sched_scan_req anymore if the scan is stopping */ if (rdev->sched_scan_req) nl80211_send_sched_scan_results(rdev, rdev->sched_scan_req->dev); mutex_unlock(&rdev->sched_scan_mtx); } void cfg80211_sched_scan_results(struct wiphy *wiphy) { /* ignore if we're not scanning */ if (wiphy_to_dev(wiphy)->sched_scan_req) queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_results_wk); } EXPORT_SYMBOL(cfg80211_sched_scan_results); void cfg80211_sched_scan_stopped(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); mutex_lock(&rdev->sched_scan_mtx); __cfg80211_stop_sched_scan(rdev, true); mutex_unlock(&rdev->sched_scan_mtx); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated) { struct net_device *dev; lockdep_assert_held(&rdev->sched_scan_mtx); if (!rdev->sched_scan_req) return -ENOENT; dev = rdev->sched_scan_req->dev; if (!driver_initiated) { int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); if (err) return err; } nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); kfree(rdev->sched_scan_req); rdev->sched_scan_req = NULL; return 0; } static void bss_release(struct kref *ref) { struct cfg80211_internal_bss *bss; bss = container_of(ref, struct cfg80211_internal_bss, ref); if (bss->pub.free_priv) bss->pub.free_priv(&bss->pub); if (bss->beacon_ies_allocated) kfree(bss->pub.beacon_ies); if (bss->proberesp_ies_allocated) kfree(bss->pub.proberesp_ies); BUG_ON(atomic_read(&bss->hold)); kfree(bss); } /* must hold dev->bss_lock! */ void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs) { struct cfg80211_internal_bss *bss; unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); list_for_each_entry(bss, &dev->bss_list, list) { bss->ts -= age_jiffies; } } /* must hold dev->bss_lock! */ static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss) { list_del_init(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); kref_put(&bss->ref, bss_release); } /* must hold dev->bss_lock! */ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) { struct cfg80211_internal_bss *bss, *tmp; bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { if (atomic_read(&bss->hold)) continue; if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; __cfg80211_unlink_bss(dev, bss); expired = true; } if (expired) dev->bss_generation++; } const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) { while (len > 2 && ies[0] != eid) { len -= ies[1] + 2; ies += ies[1] + 2; } if (len < 2) return NULL; if (len < 2 + ies[1]) return NULL; return ies; } EXPORT_SYMBOL(cfg80211_find_ie); const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, const u8 *ies, int len) { struct ieee80211_vendor_ie *ie; const u8 *pos = ies, *end = ies + len; int ie_oui; while (pos < end) { pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, end - pos); if (!pos) return NULL; if (end - pos < sizeof(*ie)) return NULL; ie = (struct ieee80211_vendor_ie *)pos; ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; if (ie_oui == oui && ie->oui_type == oui_type) return pos; pos += 2 + ie->len; } return NULL; } EXPORT_SYMBOL(cfg80211_find_vendor_ie); static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) { const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); /* equal if both missing */ if (!ie1 && !ie2) return 0; /* sort missing IE before (left of) present IE */ if (!ie1) return -1; if (!ie2) return 1; /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; return memcmp(ie1 + 2, ie2 + 2, ie1[1]); } static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, const u8 *ssid, size_t ssid_len) { const u8 *ssidie; if (bssid && !ether_addr_equal(a->bssid, bssid)) return false; if (!ssid) return true; ssidie = cfg80211_find_ie(WLAN_EID_SSID, a->information_elements, a->len_information_elements); if (!ssidie) return false; if (ssidie[1] != ssid_len) return false; return memcmp(ssidie + 2, ssid, ssid_len) == 0; } static bool is_mesh_bss(struct cfg80211_bss *a) { const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements); if (!ie) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements); if (!ie) return false; return true; } static bool is_mesh(struct cfg80211_bss *a, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) { const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements); if (!ie) return false; if (ie[1] != meshidlen) return false; if (memcmp(ie + 2, meshid, meshidlen)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements); if (!ie) return false; if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) return false; /* * Ignore mesh capability (last two bytes of the IE) when * comparing since that may differ between stations taking * part in the same mesh. */ return memcmp(ie + 2, meshcfg, sizeof(struct ieee80211_meshconf_ie) - 2) == 0; } static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) { int r; if (a->channel != b->channel) return b->channel->center_freq - a->channel->center_freq; if (is_mesh_bss(a) && is_mesh_bss(b)) { r = cmp_ies(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); if (r) return r; return cmp_ies(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); } /* * we can't use compare_ether_addr here since we need a < > operator. * The binary return value of compare_ether_addr isn't enough */ return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); } static int cmp_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) { int r; r = cmp_bss_core(a, b); if (r) return r; return cmp_ies(WLAN_EID_SSID, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); } static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) { const u8 *ie1; const u8 *ie2; int i; int r; r = cmp_bss_core(a, b); if (r) return r; ie1 = cfg80211_find_ie(WLAN_EID_SSID, a->information_elements, a->len_information_elements); ie2 = cfg80211_find_ie(WLAN_EID_SSID, b->information_elements, b->len_information_elements); /* Key comparator must use same algorithm in any rb-tree * search function (order is important), otherwise ordering * of items in the tree is broken and search gives incorrect * results. This code uses same order as cmp_ies() does. */ /* sort missing IE before (left of) present IE */ if (!ie1) return -1; if (!ie2) return 1; /* zero-size SSID is used as an indication of the hidden bss */ if (!ie2[1]) return 0; /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; /* zeroed SSID ie is another indication of a hidden bss */ for (i = 0; i < ie2[1]; i++) if (ie2[i + 2]) return -1; return 0; } struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, u16 capa_mask, u16 capa_val) { struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; unsigned long now = jiffies; spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { if ((bss->pub.capability & capa_mask) != capa_val) continue; if (channel && bss->pub.channel != channel) continue; /* Don't get expired BSS structs */ if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) && !atomic_read(&bss->hold)) continue; if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { res = bss; kref_get(&res->ref); break; } } spin_unlock_bh(&dev->bss_lock); if (!res) return NULL; return &res->pub; } EXPORT_SYMBOL(cfg80211_get_bss); struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) { struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { if (channel && bss->pub.channel != channel) continue; if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { res = bss; kref_get(&res->ref); break; } } spin_unlock_bh(&dev->bss_lock); if (!res) return NULL; return &res->pub; } EXPORT_SYMBOL(cfg80211_get_mesh); static void rb_insert_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss) { struct rb_node **p = &dev->bss_tree.rb_node; struct rb_node *parent = NULL; struct cfg80211_internal_bss *tbss; int cmp; while (*p) { parent = *p; tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); cmp = cmp_bss(&bss->pub, &tbss->pub); if (WARN_ON(!cmp)) { /* will sort of leak this BSS */ return; } if (cmp < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&bss->rbn, parent, p); rb_insert_color(&bss->rbn, &dev->bss_tree); } static struct cfg80211_internal_bss * rb_find_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *res) { struct rb_node *n = dev->bss_tree.rb_node; struct cfg80211_internal_bss *bss; int r; while (n) { bss = rb_entry(n, struct cfg80211_internal_bss, rbn); r = cmp_bss(&res->pub, &bss->pub); if (r == 0) return bss; else if (r < 0) n = n->rb_left; else n = n->rb_right; } return NULL; } static struct cfg80211_internal_bss * rb_find_hidden_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *res) { struct rb_node *n = dev->bss_tree.rb_node; struct cfg80211_internal_bss *bss; int r; while (n) { bss = rb_entry(n, struct cfg80211_internal_bss, rbn); r = cmp_hidden_bss(&res->pub, &bss->pub); if (r == 0) return bss; else if (r < 0) n = n->rb_left; else n = n->rb_right; } return NULL; } static void copy_hidden_ies(struct cfg80211_internal_bss *res, struct cfg80211_internal_bss *hidden) { if (unlikely(res->pub.beacon_ies)) return; if (WARN_ON(!hidden->pub.beacon_ies)) return; res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); if (unlikely(!res->pub.beacon_ies)) return; res->beacon_ies_allocated = true; res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, res->pub.len_beacon_ies); } static struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *res) { struct cfg80211_internal_bss *found = NULL; /* * The reference to "res" is donated to this function. */ if (WARN_ON(!res->pub.channel)) { kref_put(&res->ref, bss_release); return NULL; } res->ts = jiffies; spin_lock_bh(&dev->bss_lock); found = rb_find_bss(dev, res); if (found) { found->pub.beacon_interval = res->pub.beacon_interval; found->pub.tsf = res->pub.tsf; found->pub.signal = res->pub.signal; found->pub.capability = res->pub.capability; found->ts = res->ts; /* Update IEs */ if (res->pub.proberesp_ies) { size_t used = dev->wiphy.bss_priv_size + sizeof(*res); size_t ielen = res->pub.len_proberesp_ies; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) if (0) { used = 0; /* just to shut up the compiler */ #else if (found->pub.proberesp_ies && !found->proberesp_ies_allocated && ksize(found) >= used + ielen) { #endif memcpy(found->pub.proberesp_ies, res->pub.proberesp_ies, ielen); found->pub.len_proberesp_ies = ielen; } else { u8 *ies = found->pub.proberesp_ies; if (found->proberesp_ies_allocated) ies = krealloc(ies, ielen, GFP_ATOMIC); else ies = kmalloc(ielen, GFP_ATOMIC); if (ies) { memcpy(ies, res->pub.proberesp_ies, ielen); found->proberesp_ies_allocated = true; found->pub.proberesp_ies = ies; found->pub.len_proberesp_ies = ielen; } } /* Override possible earlier Beacon frame IEs */ found->pub.information_elements = found->pub.proberesp_ies; found->pub.len_information_elements = found->pub.len_proberesp_ies; } if (res->pub.beacon_ies) { size_t used = dev->wiphy.bss_priv_size + sizeof(*res); size_t ielen = res->pub.len_beacon_ies; bool information_elements_is_beacon_ies = (found->pub.information_elements == found->pub.beacon_ies); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) if (0) { used = 0; /* just to shut up the compiler */ #else if (found->pub.beacon_ies && !found->beacon_ies_allocated && ksize(found) >= used + ielen) { #endif memcpy(found->pub.beacon_ies, res->pub.beacon_ies, ielen); found->pub.len_beacon_ies = ielen; } else { u8 *ies = found->pub.beacon_ies; if (found->beacon_ies_allocated) ies = krealloc(ies, ielen, GFP_ATOMIC); else ies = kmalloc(ielen, GFP_ATOMIC); if (ies) { memcpy(ies, res->pub.beacon_ies, ielen); found->beacon_ies_allocated = true; found->pub.beacon_ies = ies; found->pub.len_beacon_ies = ielen; } } /* Override IEs if they were from a beacon before */ if (information_elements_is_beacon_ies) { found->pub.information_elements = found->pub.beacon_ies; found->pub.len_information_elements = found->pub.len_beacon_ies; } } kref_put(&res->ref, bss_release); } else { struct cfg80211_internal_bss *hidden; /* First check if the beacon is a probe response from * a hidden bss. If so, copy beacon ies (with nullified * ssid) into the probe response bss entry (with real ssid). * It is required basically for PSM implementation * (probe responses do not contain tim ie) */ /* TODO: The code is not trying to update existing probe * response bss entries when beacon ies are * getting changed. */ hidden = rb_find_hidden_bss(dev, res); if (hidden) copy_hidden_ies(res, hidden); /* this "consumes" the reference */ list_add_tail(&res->list, &dev->bss_list); rb_insert_bss(dev, res); found = res; } dev->bss_generation++; spin_unlock_bh(&dev->bss_lock); kref_get(&found->ref); return found; } struct cfg80211_bss* cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp) { struct cfg80211_internal_bss *res; size_t privsz; if (WARN_ON(!wiphy)) return NULL; privsz = wiphy->bss_priv_size; if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && (signal < 0 || signal > 100))) return NULL; res = kzalloc(sizeof(*res) + privsz + ielen, gfp); if (!res) return NULL; memcpy(res->pub.bssid, bssid, ETH_ALEN); res->pub.channel = channel; res->pub.signal = signal; res->pub.tsf = tsf; res->pub.beacon_interval = beacon_interval; res->pub.capability = capability; /* * Since we do not know here whether the IEs are from a Beacon or Probe * Response frame, we need to pick one of the options and only use it * with the driver that does not provide the full Beacon/Probe Response * frame. Use Beacon frame pointer to avoid indicating that this should * override the information_elements pointer should we have received an * earlier indication of Probe Response data. * * The initial buffer for the IEs is allocated with the BSS entry and * is located after the private area. */ res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; memcpy(res->pub.beacon_ies, ie, ielen); res->pub.len_beacon_ies = ielen; res->pub.information_elements = res->pub.beacon_ies; res->pub.len_information_elements = res->pub.len_beacon_ies; kref_init(&res->ref); res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); if (!res) return NULL; if (res->pub.capability & WLAN_CAPABILITY_ESS) regulatory_hint_found_beacon(wiphy, channel, gfp); /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } EXPORT_SYMBOL(cfg80211_inform_bss); struct cfg80211_bss * cfg80211_inform_bss_frame(struct wiphy *wiphy, struct ieee80211_channel *channel, struct ieee80211_mgmt *mgmt, size_t len, s32 signal, gfp_t gfp) { struct cfg80211_internal_bss *res; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); size_t privsz; if (WARN_ON(!mgmt)) return NULL; if (WARN_ON(!wiphy)) return NULL; if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && (signal < 0 || signal > 100))) return NULL; if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) return NULL; privsz = wiphy->bss_priv_size; res = kzalloc(sizeof(*res) + privsz + ielen, gfp); if (!res) return NULL; memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); res->pub.channel = channel; res->pub.signal = signal; res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); /* * The initial buffer for the IEs is allocated with the BSS entry and * is located after the private area. */ if (ieee80211_is_probe_resp(mgmt->frame_control)) { res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, ielen); res->pub.len_proberesp_ies = ielen; res->pub.information_elements = res->pub.proberesp_ies; res->pub.len_information_elements = res->pub.len_proberesp_ies; } else { res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); res->pub.len_beacon_ies = ielen; res->pub.information_elements = res->pub.beacon_ies; res->pub.len_information_elements = res->pub.len_beacon_ies; } kref_init(&res->ref); res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); if (!res) return NULL; if (res->pub.capability & WLAN_CAPABILITY_ESS) regulatory_hint_found_beacon(wiphy, channel, gfp); /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } EXPORT_SYMBOL(cfg80211_inform_bss_frame); void cfg80211_ref_bss(struct cfg80211_bss *pub) { struct cfg80211_internal_bss *bss; if (!pub) return; bss = container_of(pub, struct cfg80211_internal_bss, pub); kref_get(&bss->ref); } EXPORT_SYMBOL(cfg80211_ref_bss); void cfg80211_put_bss(struct cfg80211_bss *pub) { struct cfg80211_internal_bss *bss; if (!pub) return; bss = container_of(pub, struct cfg80211_internal_bss, pub); kref_put(&bss->ref, bss_release); } EXPORT_SYMBOL(cfg80211_put_bss); void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss; if (WARN_ON(!pub)) return; bss = container_of(pub, struct cfg80211_internal_bss, pub); spin_lock_bh(&dev->bss_lock); if (!list_empty(&bss->list)) { __cfg80211_unlink_bss(dev, bss); dev->bss_generation++; } spin_unlock_bh(&dev->bss_lock); } EXPORT_SYMBOL(cfg80211_unlink_bss); #ifdef CONFIG_CFG80211_WEXT int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct cfg80211_registered_device *rdev; struct wiphy *wiphy; struct iw_scan_req *wreq = NULL; struct cfg80211_scan_request *creq = NULL; int i, err, n_channels = 0; enum ieee80211_band band; if (!netif_running(dev)) return -ENETDOWN; if (wrqu->data.length == sizeof(struct iw_scan_req)) wreq = (struct iw_scan_req *)extra; rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); if (rdev->scan_req) { err = -EBUSY; goto out; } wiphy = &rdev->wiphy; /* Determine number of channels, needed to allocate creq */ if (wreq && wreq->num_channels) n_channels = wreq->num_channels; else { for (band = 0; band < IEEE80211_NUM_BANDS; band++) if (wiphy->bands[band]) n_channels += wiphy->bands[band]->n_channels; } creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), GFP_ATOMIC); if (!creq) { err = -ENOMEM; goto out; } creq->wiphy = wiphy; creq->wdev = dev->ieee80211_ptr; /* SSIDs come after channels */ creq->ssids = (void *)&creq->channels[n_channels]; creq->n_channels = n_channels; creq->n_ssids = 1; /* translate "Scan on frequencies" request */ i = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { int j; if (!wiphy->bands[band]) continue; for (j = 0; j < wiphy->bands[band]->n_channels; j++) { /* ignore disabled channels */ if (wiphy->bands[band]->channels[j].flags & IEEE80211_CHAN_DISABLED) continue; /* If we have a wireless request structure and the * wireless request specifies frequencies, then search * for the matching hardware channel. */ if (wreq && wreq->num_channels) { int k; int wiphy_freq = wiphy->bands[band]->channels[j].center_freq; for (k = 0; k < wreq->num_channels; k++) { int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]); if (wext_freq == wiphy_freq) goto wext_freq_found; } goto wext_freq_not_found; } wext_freq_found: creq->channels[i] = &wiphy->bands[band]->channels[j]; i++; wext_freq_not_found: ; } } /* No channels found? */ if (!i) { err = -EINVAL; goto out; } /* Set real number of channels specified in creq->channels[] */ creq->n_channels = i; /* translate "Scan for SSID" request */ if (wreq) { if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; goto out; } memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len); creq->ssids[0].ssid_len = wreq->essid_len; } if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE) creq->n_ssids = 0; } for (i = 0; i < IEEE80211_NUM_BANDS; i++) if (wiphy->bands[i]) creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; rdev->scan_req = creq; err = rdev->ops->scan(wiphy, creq); if (err) { rdev->scan_req = NULL; /* creq will be freed below */ } else { nl80211_send_scan_start(rdev, dev->ieee80211_ptr); /* creq now owned by driver */ creq = NULL; dev_hold(dev); } out: kfree(creq); cfg80211_unlock_rdev(rdev); return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); static void ieee80211_scan_add_ies(struct iw_request_info *info, struct cfg80211_bss *bss, char **current_ev, char *end_buf) { u8 *pos, *end, *next; struct iw_event iwe; if (!bss->information_elements || !bss->len_information_elements) return; /* * If needed, fragment the IEs buffer (at IE boundaries) into short * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. */ pos = bss->information_elements; end = pos + bss->len_information_elements; while (end - pos > IW_GENERIC_IE_MAX) { next = pos + 2 + pos[1]; while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) next = next + 2 + next[1]; memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = next - pos; *current_ev = iwe_stream_add_point(info, *current_ev, end_buf, &iwe, pos); pos = next; } if (end > pos) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = end - pos; *current_ev = iwe_stream_add_point(info, *current_ev, end_buf, &iwe, pos); } } static inline unsigned int elapsed_jiffies_msecs(unsigned long start) { unsigned long end = jiffies; if (end >= start) return jiffies_to_msecs(end - start); return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); } static char * ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, struct cfg80211_internal_bss *bss, char *current_ev, char *end_buf) { struct iw_event iwe; u8 *buf, *cfg, *p; u8 *ie = bss->pub.information_elements; int rem = bss->pub.len_information_elements, i, sig; bool ismesh = false; memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN); current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq); iwe.u.freq.e = 0; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = bss->pub.channel->center_freq; iwe.u.freq.e = 6; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVQUAL; iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_QUAL_UPDATED; switch (wiphy->signal_type) { case CFG80211_SIGNAL_TYPE_MBM: sig = bss->pub.signal / 100; iwe.u.qual.level = sig; iwe.u.qual.updated |= IW_QUAL_DBM; if (sig < -110) /* rather bad */ sig = -110; else if (sig > -40) /* perfect */ sig = -40; /* will give a range of 0 .. 70 */ iwe.u.qual.qual = sig + 110; break; case CFG80211_SIGNAL_TYPE_UNSPEC: iwe.u.qual.level = bss->pub.signal; /* will give range 0 .. 100 */ iwe.u.qual.qual = bss->pub.signal; break; default: /* not reached */ break; } current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); } memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWENCODE; if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY) iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ""); while (rem >= 2) { /* invalid data */ if (ie[1] > rem - 2) break; switch (ie[0]) { case WLAN_EID_SSID: memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWESSID; iwe.u.data.length = ie[1]; iwe.u.data.flags = 1; current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ie + 2); break; case WLAN_EID_MESH_ID: memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWESSID; iwe.u.data.length = ie[1]; iwe.u.data.flags = 1; current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ie + 2); break; case WLAN_EID_MESH_CONFIG: ismesh = true; if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) break; buf = kmalloc(50, GFP_ATOMIC); if (!buf) break; cfg = ie + 2; memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, "Mesh Network Path Selection Protocol ID: " "0x%02X", cfg[0]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); sprintf(buf, "Path Selection Metric ID: 0x%02X", cfg[1]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); sprintf(buf, "Congestion Control Mode ID: 0x%02X", cfg[2]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); sprintf(buf, "Authentication ID: 0x%02X", cfg[4]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); sprintf(buf, "Formation Info: 0x%02X", cfg[5]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); sprintf(buf, "Capabilities: 0x%02X", cfg[6]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); kfree(buf); break; case WLAN_EID_SUPP_RATES: case WLAN_EID_EXT_SUPP_RATES: /* display all supported rates in readable format */ p = current_ev + iwe_stream_lcp_len(info); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWRATE; /* Those two flags are ignored... */ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; for (i = 0; i < ie[1]; i++) { iwe.u.bitrate.value = ((ie[i + 2] & 0x7f) * 500000); p = iwe_stream_add_value(info, current_ev, p, end_buf, &iwe, IW_EV_PARAM_LEN); } current_ev = p; break; } rem -= ie[1] + 2; ie += ie[1] + 2; } if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) || ismesh) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWMODE; if (ismesh) iwe.u.mode = IW_MODE_MESH; else if (bss->pub.capability & WLAN_CAPABILITY_ESS) iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_UINT_LEN); } buf = kmalloc(30, GFP_ATOMIC); if (buf) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, " Last beacon: %ums ago", elapsed_jiffies_msecs(bss->ts)); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); kfree(buf); } ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); return current_ev; } static int ieee80211_scan_results(struct cfg80211_registered_device *dev, struct iw_request_info *info, char *buf, size_t len) { char *current_ev = buf; char *end_buf = buf + len; struct cfg80211_internal_bss *bss; spin_lock_bh(&dev->bss_lock); cfg80211_bss_expire(dev); list_for_each_entry(bss, &dev->bss_list, list) { if (buf + len - current_ev <= IW_EV_ADDR_LEN) { spin_unlock_bh(&dev->bss_lock); return -E2BIG; } current_ev = ieee80211_bss(&dev->wiphy, info, bss, current_ev, end_buf); } spin_unlock_bh(&dev->bss_lock); return current_ev - buf; } int cfg80211_wext_giwscan(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { struct cfg80211_registered_device *rdev; int res; if (!netif_running(dev)) return -ENETDOWN; rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); if (rdev->scan_req) { res = -EAGAIN; goto out; } res = ieee80211_scan_results(rdev, info, extra, data->length); data->length = 0; if (res >= 0) { data->length = res; res = 0; } out: cfg80211_unlock_rdev(rdev); return res; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); #endif compat-drivers-2012-09-18/net/wireless/wext-spy.c0000644000175000017500000001507412026211315020756 0ustar mcgrofmcgrof/* * This file implement the Wireless Extensions spy API. * * Authors : Jean Tourrilhes - HPL - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. * * (As all part of the Linux kernel, this file is GPL) */ #include #include #include #include #include #include #include static inline struct iw_spy_data *get_spydata(struct net_device *dev) { /* This is the new way */ if (dev->wireless_data) return dev->wireless_data->spy_data; return NULL; } int iw_handler_set_spy(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra) { struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; /* Make sure driver is not buggy or using the old API */ if (!spydata) return -EOPNOTSUPP; /* Disable spy collection while we copy the addresses. * While we copy addresses, any call to wireless_spy_update() * will NOP. This is OK, as anyway the addresses are changing. */ spydata->spy_number = 0; /* We want to operate without locking, because wireless_spy_update() * most likely will happen in the interrupt handler, and therefore * have its own locking constraints and needs performance. * The rtnl_lock() make sure we don't race with the other iw_handlers. * This make sure wireless_spy_update() "see" that the spy list * is temporarily disabled. */ smp_wmb(); /* Are there are addresses to copy? */ if (wrqu->data.length > 0) { int i; /* Copy addresses */ for (i = 0; i < wrqu->data.length; i++) memcpy(spydata->spy_address[i], address[i].sa_data, ETH_ALEN); /* Reset stats */ memset(spydata->spy_stat, 0, sizeof(struct iw_quality) * IW_MAX_SPY); } /* Make sure above is updated before re-enabling */ smp_wmb(); /* Enable addresses */ spydata->spy_number = wrqu->data.length; return 0; } EXPORT_SYMBOL(iw_handler_set_spy); int iw_handler_get_spy(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra) { struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; int i; /* Make sure driver is not buggy or using the old API */ if (!spydata) return -EOPNOTSUPP; wrqu->data.length = spydata->spy_number; /* Copy addresses. */ for (i = 0; i < spydata->spy_number; i++) { memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); address[i].sa_family = AF_UNIX; } /* Copy stats to the user buffer (just after). */ if (spydata->spy_number > 0) memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), spydata->spy_stat, sizeof(struct iw_quality) * spydata->spy_number); /* Reset updated flags. */ for (i = 0; i < spydata->spy_number; i++) spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; return 0; } EXPORT_SYMBOL(iw_handler_get_spy); /*------------------------------------------------------------------*/ /* * Standard Wireless Handler : set spy threshold */ int iw_handler_set_thrspy(struct net_device * dev, struct iw_request_info *info, union iwreq_data * wrqu, char * extra) { struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; /* Make sure driver is not buggy or using the old API */ if (!spydata) return -EOPNOTSUPP; /* Just do it */ memcpy(&(spydata->spy_thr_low), &(threshold->low), 2 * sizeof(struct iw_quality)); /* Clear flag */ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); return 0; } EXPORT_SYMBOL(iw_handler_set_thrspy); /*------------------------------------------------------------------*/ /* * Standard Wireless Handler : get spy threshold */ int iw_handler_get_thrspy(struct net_device * dev, struct iw_request_info *info, union iwreq_data * wrqu, char * extra) { struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; /* Make sure driver is not buggy or using the old API */ if (!spydata) return -EOPNOTSUPP; /* Just do it */ memcpy(&(threshold->low), &(spydata->spy_thr_low), 2 * sizeof(struct iw_quality)); return 0; } EXPORT_SYMBOL(iw_handler_get_thrspy); /*------------------------------------------------------------------*/ /* * Prepare and send a Spy Threshold event */ static void iw_send_thrspy_event(struct net_device * dev, struct iw_spy_data * spydata, unsigned char * address, struct iw_quality * wstats) { union iwreq_data wrqu; struct iw_thrspy threshold; /* Init */ wrqu.data.length = 1; wrqu.data.flags = 0; /* Copy address */ memcpy(threshold.addr.sa_data, address, ETH_ALEN); threshold.addr.sa_family = ARPHRD_ETHER; /* Copy stats */ memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); /* Copy also thresholds */ memcpy(&(threshold.low), &(spydata->spy_thr_low), 2 * sizeof(struct iw_quality)); /* Send event to user space */ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); } /* ---------------------------------------------------------------- */ /* * Call for the driver to update the spy data. * For now, the spy data is a simple array. As the size of the array is * small, this is good enough. If we wanted to support larger number of * spy addresses, we should use something more efficient... */ void wireless_spy_update(struct net_device * dev, unsigned char * address, struct iw_quality * wstats) { struct iw_spy_data * spydata = get_spydata(dev); int i; int match = -1; /* Make sure driver is not buggy or using the old API */ if (!spydata) return; /* Update all records that match */ for (i = 0; i < spydata->spy_number; i++) if (ether_addr_equal(address, spydata->spy_address[i])) { memcpy(&(spydata->spy_stat[i]), wstats, sizeof(struct iw_quality)); match = i; } /* Generate an event if we cross the spy threshold. * To avoid event storms, we have a simple hysteresis : we generate * event only when we go under the low threshold or above the * high threshold. */ if (match >= 0) { if (spydata->spy_thr_under[match]) { if (wstats->level > spydata->spy_thr_high.level) { spydata->spy_thr_under[match] = 0; iw_send_thrspy_event(dev, spydata, address, wstats); } } else { if (wstats->level < spydata->spy_thr_low.level) { spydata->spy_thr_under[match] = 1; iw_send_thrspy_event(dev, spydata, address, wstats); } } } } EXPORT_SYMBOL(wireless_spy_update); compat-drivers-2012-09-18/net/wireless/mlme.c0000644000175000017500000006555512026211315020121 0ustar mcgrofmcgrof/* * cfg80211 MLME SAP interface * * Copyright (c) 2009, Jouni Malinen */ #include #include #include #include #include #include #include #include #include #include "core.h" #include "nl80211.h" void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); wdev_lock(wdev); nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); cfg80211_sme_rx_auth(dev, buf, len); wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_auth); void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, const u8 *buf, size_t len) { u16 status_code; struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u8 *ie = mgmt->u.assoc_resp.variable; int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); wdev_lock(wdev); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); /* * This is a bit of a hack, we don't notify userspace of * a (re-)association reply if we tried to send a reassoc * and got a reject -- we only try again with an assoc * frame instead of reassoc. */ if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && cfg80211_sme_failed_reassoc(wdev)) { cfg80211_put_bss(bss); goto out; } nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) { cfg80211_sme_failed_assoc(wdev); /* * do not call connect_result() now because the * sme will schedule work that does it later. */ cfg80211_put_bss(bss); goto out; } if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { /* * This is for the userspace SME, the CONNECTING * state will be changed to CONNECTED by * __cfg80211_connect_result() below. */ wdev->sme_state = CFG80211_SME_CONNECTING; } /* this consumes the bss reference */ __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, status_code, status_code == WLAN_STATUS_SUCCESS, bss); out: wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; const u8 *bssid = mgmt->bssid; bool was_current = false; ASSERT_WDEV_LOCK(wdev); if (wdev->current_bss && ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; was_current = true; } nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) { u16 reason_code; bool from_ap; reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr); __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); } } EXPORT_SYMBOL(__cfg80211_send_deauth); void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; wdev_lock(wdev); __cfg80211_send_deauth(dev, buf, len); wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_deauth); void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; const u8 *bssid = mgmt->bssid; u16 reason_code; bool from_ap; ASSERT_WDEV_LOCK(wdev); nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); if (wdev->sme_state != CFG80211_SME_CONNECTED) return; if (wdev->current_bss && ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { cfg80211_sme_disassoc(dev, wdev->current_bss); cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; } else WARN_ON(1); reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr); __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); } EXPORT_SYMBOL(__cfg80211_send_disassoc); void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; wdev_lock(wdev); __cfg80211_send_disassoc(dev, buf, len); wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_disassoc); void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC); } EXPORT_SYMBOL(cfg80211_send_unprot_deauth); void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC); } EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); wdev_lock(wdev); nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); wdev_lock(wdev); nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, enum nl80211_key_type key_type, int key_id, const u8 *tsc, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; char *buf = kmalloc(128, gfp); if (buf) { sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" "keyid=%d %scast addr=%pM)", key_id, key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", addr); memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = strlen(buf); wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); kfree(buf); } #endif nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); } EXPORT_SYMBOL(cfg80211_michael_mic_failure); /* some MLME handling for userspace SME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, const u8 *key, int key_len, int key_idx) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; int err; ASSERT_WDEV_LOCK(wdev); if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) if (!key || !key_len || key_idx < 0 || key_idx > 4) return -EINVAL; if (wdev->current_bss && ether_addr_equal(bssid, wdev->current_bss->pub.bssid)) return -EALREADY; memset(&req, 0, sizeof(req)); req.ie = ie; req.ie_len = ie_len; req.auth_type = auth_type; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); req.key = key; req.key_len = key_len; req.key_idx = key_idx; if (!req.bss) return -ENOENT; err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel, CHAN_MODE_SHARED); if (err) goto out; err = rdev->ops->auth(&rdev->wiphy, dev, &req); out: cfg80211_put_bss(req.bss); return err; } int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, const u8 *key, int key_len, int key_idx) { int err; mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, key, key_len, key_idx); wdev_unlock(dev->ieee80211_ptr); mutex_unlock(&rdev->devlist_mtx); return err; } /* Do a logical ht_capa &= ht_capa_mask. */ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask) { int i; u8 *p1, *p2; if (!ht_capa_mask) { memset(ht_capa, 0, sizeof(*ht_capa)); return; } p1 = (u8*)(ht_capa); p2 = (u8*)(ht_capa_mask); for (i = 0; iieee80211_ptr; struct cfg80211_assoc_request req; int err; bool was_connected = false; ASSERT_WDEV_LOCK(wdev); memset(&req, 0, sizeof(req)); if (wdev->current_bss && prev_bssid && ether_addr_equal(wdev->current_bss->pub.bssid, prev_bssid)) { /* * Trying to reassociate: Allow this to proceed and let the old * association to be dropped when the new one is completed. */ if (wdev->sme_state == CFG80211_SME_CONNECTED) { was_connected = true; wdev->sme_state = CFG80211_SME_CONNECTING; } } else if (wdev->current_bss) return -EALREADY; req.ie = ie; req.ie_len = ie_len; memcpy(&req.crypto, crypt, sizeof(req.crypto)); req.use_mfp = use_mfp; req.prev_bssid = prev_bssid; req.flags = assoc_flags; if (ht_capa) memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa)); if (ht_capa_mask) memcpy(&req.ht_capa_mask, ht_capa_mask, sizeof(req.ht_capa_mask)); cfg80211_oper_and_ht_capa(&req.ht_capa_mask, rdev->wiphy.ht_capa_mod_mask); req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!req.bss) { if (was_connected) wdev->sme_state = CFG80211_SME_CONNECTED; return -ENOENT; } err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel, CHAN_MODE_SHARED); if (err) goto out; err = rdev->ops->assoc(&rdev->wiphy, dev, &req); out: if (err) { if (was_connected) wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_put_bss(req.bss); } return err; } int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, const u8 *bssid, const u8 *prev_bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt, u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, struct ieee80211_ht_cap *ht_capa_mask) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, ssid, ssid_len, ie, ie_len, use_mfp, crypt, assoc_flags, ht_capa, ht_capa_mask); wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); return err; } int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_deauth_request req = { .bssid = bssid, .reason_code = reason, .ie = ie, .ie_len = ie_len, }; ASSERT_WDEV_LOCK(wdev); if (local_state_change) { if (wdev->current_bss && ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; } return 0; } return rdev->ops->deauth(&rdev->wiphy, dev, &req); } int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason, local_state_change); wdev_unlock(wdev); return err; } static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_disassoc_request req; ASSERT_WDEV_LOCK(wdev); if (wdev->sme_state != CFG80211_SME_CONNECTED) return -ENOTCONN; if (WARN_ON(!wdev->current_bss)) return -ENOTCONN; memset(&req, 0, sizeof(req)); req.reason_code = reason; req.local_state_change = local_state_change; req.ie = ie; req.ie_len = ie_len; if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) req.bss = &wdev->current_bss->pub; else return -ENOTCONN; return rdev->ops->disassoc(&rdev->wiphy, dev, &req); } int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason, local_state_change); wdev_unlock(wdev); return err; } void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_deauth_request req; u8 bssid[ETH_ALEN]; ASSERT_WDEV_LOCK(wdev); if (!rdev->ops->deauth) return; memset(&req, 0, sizeof(req)); req.reason_code = WLAN_REASON_DEAUTH_LEAVING; req.ie = NULL; req.ie_len = 0; if (!wdev->current_bss) return; memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); req.bssid = bssid; rdev->ops->deauth(&rdev->wiphy, dev, &req); if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; } } void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type, duration, gfp); } EXPORT_SYMBOL(cfg80211_ready_on_channel); void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, channel_type, gfp); } EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); } EXPORT_SYMBOL(cfg80211_new_sta); void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); } EXPORT_SYMBOL(cfg80211_del_sta); struct cfg80211_mgmt_registration { struct list_head list; u32 nlportid; int match_len; __le16 frame_type; u8 match[]; }; int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, u16 frame_type, const u8 *match_data, int match_len) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_mgmt_registration *reg, *nreg; int err = 0; u16 mgmt_type; if (!wdev->wiphy->mgmt_stypes) return -EOPNOTSUPP; if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) return -EINVAL; if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) return -EINVAL; mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) return -EINVAL; nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); if (!nreg) return -ENOMEM; spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry(reg, &wdev->mgmt_registrations, list) { int mlen = min(match_len, reg->match_len); if (frame_type != le16_to_cpu(reg->frame_type)) continue; if (memcmp(reg->match, match_data, mlen) == 0) { err = -EALREADY; break; } } if (err) { kfree(nreg); goto out; } memcpy(nreg->match, match_data, match_len); nreg->match_len = match_len; nreg->nlportid = snd_portid; nreg->frame_type = cpu_to_le16(frame_type); list_add(&nreg->list, &wdev->mgmt_registrations); if (rdev->ops->mgmt_frame_register) rdev->ops->mgmt_frame_register(wiphy, wdev, frame_type, true); out: spin_unlock_bh(&wdev->mgmt_registrations_lock); return err; } void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_mgmt_registration *reg, *tmp; spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { if (reg->nlportid != nlportid) continue; if (rdev->ops->mgmt_frame_register) { u16 frame_type = le16_to_cpu(reg->frame_type); rdev->ops->mgmt_frame_register(wiphy, wdev, frame_type, false); } list_del(®->list); kfree(reg); } spin_unlock_bh(&wdev->mgmt_registrations_lock); if (nlportid == wdev->ap_unexpected_nlportid) wdev->ap_unexpected_nlportid = 0; } void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) { struct cfg80211_mgmt_registration *reg, *tmp; spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { list_del(®->list); kfree(reg); } spin_unlock_bh(&wdev->mgmt_registrations_lock); } int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { const struct ieee80211_mgmt *mgmt; u16 stype; if (!wdev->wiphy->mgmt_stypes) return -EOPNOTSUPP; if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; if (len < 24 + 1) return -EINVAL; mgmt = (const struct ieee80211_mgmt *) buf; if (!ieee80211_is_mgmt(mgmt->frame_control)) return -EINVAL; stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4))) return -EINVAL; if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { int err = 0; wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (!wdev->current_bss) { err = -ENOTCONN; break; } if (!ether_addr_equal(wdev->current_bss->pub.bssid, mgmt->bssid)) { err = -ENOTCONN; break; } /* * check for IBSS DA must be done by driver as * cfg80211 doesn't track the stations */ if (wdev->iftype == NL80211_IFTYPE_ADHOC) break; /* for station, check that DA is the AP */ if (!ether_addr_equal(wdev->current_bss->pub.bssid, mgmt->da)) { err = -ENOTCONN; break; } break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_AP_VLAN: if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev))) err = -EINVAL; break; case NL80211_IFTYPE_MESH_POINT: if (!ether_addr_equal(mgmt->sa, mgmt->bssid)) { err = -EINVAL; break; } /* * check for mesh DA must be done by driver as * cfg80211 doesn't track the stations */ break; case NL80211_IFTYPE_P2P_DEVICE: /* * fall through, P2P device only supports * public action frames */ default: err = -EOPNOTSUPP; break; } wdev_unlock(wdev); if (err) return err; } if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) return -EINVAL; /* Transmit the Action frame as requested by user space */ return rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, channel_type, channel_type_valid, wait, buf, len, no_cck, dont_wait_for_ack, cookie); } bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, const u8 *buf, size_t len, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_mgmt_registration *reg; const struct ieee80211_txrx_stypes *stypes = &wiphy->mgmt_stypes[wdev->iftype]; struct ieee80211_mgmt *mgmt = (void *)buf; const u8 *data; int data_len; bool result = false; __le16 ftype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); u16 stype; stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; if (!(stypes->rx & BIT(stype))) return false; data = buf + ieee80211_hdrlen(mgmt->frame_control); data_len = len - ieee80211_hdrlen(mgmt->frame_control); spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry(reg, &wdev->mgmt_registrations, list) { if (reg->frame_type != ftype) continue; if (reg->match_len > data_len) continue; if (memcmp(reg->match, data, reg->match_len)) continue; /* found match! */ /* Indicate the received Action frame to user space */ if (nl80211_send_mgmt(rdev, wdev, reg->nlportid, freq, sig_mbm, buf, len, gfp)) continue; result = true; break; } spin_unlock_bh(&wdev->mgmt_registrations_lock); return result; } EXPORT_SYMBOL(cfg80211_rx_mgmt); void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); /* Indicate TX status of the Action frame to user space */ nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp); } EXPORT_SYMBOL(cfg80211_mgmt_tx_status); void cfg80211_cqm_rssi_notify(struct net_device *dev, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); /* Indicate roaming trigger event to user space */ nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); } EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); void cfg80211_cqm_pktloss_notify(struct net_device *dev, const u8 *peer, u32 num_packets, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); /* Indicate roaming trigger event to user space */ nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); } EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, u32 num_packets, u32 rate, u32 intvl, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets, rate, intvl, gfp); } EXPORT_SYMBOL(cfg80211_cqm_txe_notify); void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); } EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, const u8 *bssid, bool preauth, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); void cfg80211_ch_switch_notify(struct net_device *dev, int freq, enum nl80211_channel_type type) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_channel *chan; wdev_lock(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO)) goto out; chan = rdev_freq_to_chan(rdev, freq, type); if (WARN_ON(!chan)) goto out; wdev->channel = chan; nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); out: wdev_unlock(wdev); return; } EXPORT_SYMBOL(cfg80211_ch_switch_notify); bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO)) return false; return nl80211_unexpected_frame(dev, addr, gfp); } EXPORT_SYMBOL(cfg80211_rx_spurious_frame); bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO && wdev->iftype != NL80211_IFTYPE_AP_VLAN)) return false; return nl80211_unexpected_4addr_frame(dev, addr, gfp); } EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); compat-drivers-2012-09-18/net/wireless/sysfs.h0000644000175000017500000000030512026211315020321 0ustar mcgrofmcgrof#ifndef __WIRELESS_SYSFS_H #define __WIRELESS_SYSFS_H extern int wiphy_sysfs_init(void); extern void wiphy_sysfs_exit(void); extern struct class ieee80211_class; #endif /* __WIRELESS_SYSFS_H */ compat-drivers-2012-09-18/net/wireless/lib80211_crypt_wep.c0000644000175000017500000001612212026211315022407 0ustar mcgrofmcgrof/* * lib80211 crypt: host-based WEP encryption implementation for lib80211 * * Copyright (c) 2002-2004, Jouni Malinen * Copyright (c) 2008, John W. Linville * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. */ #include #include #include #include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Jouni Malinen"); MODULE_DESCRIPTION("lib80211 crypt: WEP"); MODULE_LICENSE("GPL"); struct lib80211_wep_data { u32 iv; #define WEP_KEY_LEN 13 u8 key[WEP_KEY_LEN + 1]; u8 key_len; u8 key_idx; struct crypto_blkcipher *tx_tfm; struct crypto_blkcipher *rx_tfm; }; static void *lib80211_wep_init(int keyidx) { struct lib80211_wep_data *priv; priv = kzalloc(sizeof(*priv), GFP_ATOMIC); if (priv == NULL) goto fail; priv->key_idx = keyidx; priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm)) { priv->tx_tfm = NULL; goto fail; } priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm)) { priv->rx_tfm = NULL; goto fail; } /* start WEP IV from a random value */ get_random_bytes(&priv->iv, 4); return priv; fail: if (priv) { if (priv->tx_tfm) crypto_free_blkcipher(priv->tx_tfm); if (priv->rx_tfm) crypto_free_blkcipher(priv->rx_tfm); kfree(priv); } return NULL; } static void lib80211_wep_deinit(void *priv) { struct lib80211_wep_data *_priv = priv; if (_priv) { if (_priv->tx_tfm) crypto_free_blkcipher(_priv->tx_tfm); if (_priv->rx_tfm) crypto_free_blkcipher(_priv->rx_tfm); } kfree(priv); } /* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, u8 *key, int keylen, void *priv) { struct lib80211_wep_data *wep = priv; u32 klen; u8 *pos; if (skb_headroom(skb) < 4 || skb->len < hdr_len) return -1; pos = skb_push(skb, 4); memmove(pos, pos + 4, hdr_len); pos += hdr_len; klen = 3 + wep->key_len; wep->iv++; /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) * can be used to speedup attacks, so avoid using them. */ if ((wep->iv & 0xff00) == 0xff00) { u8 B = (wep->iv >> 16) & 0xff; if (B >= 3 && B < klen) wep->iv += 0x0100; } /* Prepend 24-bit IV to RC4 key and TX frame */ *pos++ = (wep->iv >> 16) & 0xff; *pos++ = (wep->iv >> 8) & 0xff; *pos++ = wep->iv & 0xff; *pos++ = wep->key_idx << 6; return 0; } /* Perform WEP encryption on given skb that has at least 4 bytes of headroom * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, * so the payload length increases with 8 bytes. * * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) */ static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_wep_data *wep = priv; struct blkcipher_desc desc = { .tfm = wep->tx_tfm }; u32 crc, klen, len; u8 *pos, *icv; struct scatterlist sg; u8 key[WEP_KEY_LEN + 3]; /* other checks are in lib80211_wep_build_iv */ if (skb_tailroom(skb) < 4) return -1; /* add the IV to the frame */ if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv)) return -1; /* Copy the IV into the first 3 bytes of the key */ skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); /* Copy rest of the WEP key (the secret part) */ memcpy(key + 3, wep->key, wep->key_len); len = skb->len - hdr_len - 4; pos = skb->data + hdr_len + 4; klen = 3 + wep->key_len; /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */ crc = ~crc32_le(~0, pos, len); icv = skb_put(skb, 4); icv[0] = crc; icv[1] = crc >> 8; icv[2] = crc >> 16; icv[3] = crc >> 24; crypto_blkcipher_setkey(wep->tx_tfm, key, klen); sg_init_one(&sg, pos, len + 4); return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); } /* Perform WEP decryption on given buffer. Buffer includes whole WEP part of * the frame: IV (4 bytes), encrypted payload (including SNAP header), * ICV (4 bytes). len includes both IV and ICV. * * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on * failure. If frame is OK, IV and ICV will be removed. */ static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_wep_data *wep = priv; struct blkcipher_desc desc = { .tfm = wep->rx_tfm }; u32 crc, klen, plen; u8 key[WEP_KEY_LEN + 3]; u8 keyidx, *pos, icv[4]; struct scatterlist sg; if (skb->len < hdr_len + 8) return -1; pos = skb->data + hdr_len; key[0] = *pos++; key[1] = *pos++; key[2] = *pos++; keyidx = *pos++ >> 6; if (keyidx != wep->key_idx) return -1; klen = 3 + wep->key_len; /* Copy rest of the WEP key (the secret part) */ memcpy(key + 3, wep->key, wep->key_len); /* Apply RC4 to data and compute CRC32 over decrypted data */ plen = skb->len - hdr_len - 8; crypto_blkcipher_setkey(wep->rx_tfm, key, klen); sg_init_one(&sg, pos, plen + 4); if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) return -7; crc = ~crc32_le(~0, pos, plen); icv[0] = crc; icv[1] = crc >> 8; icv[2] = crc >> 16; icv[3] = crc >> 24; if (memcmp(icv, pos + plen, 4) != 0) { /* ICV mismatch - drop frame */ return -2; } /* Remove IV and ICV */ memmove(skb->data + 4, skb->data, hdr_len); skb_pull(skb, 4); skb_trim(skb, skb->len - 4); return 0; } static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv) { struct lib80211_wep_data *wep = priv; if (len < 0 || len > WEP_KEY_LEN) return -1; memcpy(wep->key, key, len); wep->key_len = len; return 0; } static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv) { struct lib80211_wep_data *wep = priv; if (len < wep->key_len) return -1; memcpy(key, wep->key, wep->key_len); return wep->key_len; } static char *lib80211_wep_print_stats(char *p, void *priv) { struct lib80211_wep_data *wep = priv; p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); return p; } static struct lib80211_crypto_ops lib80211_crypt_wep = { .name = "WEP", .init = lib80211_wep_init, .deinit = lib80211_wep_deinit, .encrypt_mpdu = lib80211_wep_encrypt, .decrypt_mpdu = lib80211_wep_decrypt, .encrypt_msdu = NULL, .decrypt_msdu = NULL, .set_key = lib80211_wep_set_key, .get_key = lib80211_wep_get_key, .print_stats = lib80211_wep_print_stats, .extra_mpdu_prefix_len = 4, /* IV */ .extra_mpdu_postfix_len = 4, /* ICV */ .owner = THIS_MODULE, }; static int __init lib80211_crypto_wep_init(void) { return lib80211_register_crypto_ops(&lib80211_crypt_wep); } static void __exit lib80211_crypto_wep_exit(void) { lib80211_unregister_crypto_ops(&lib80211_crypt_wep); } module_init(lib80211_crypto_wep_init); module_exit(lib80211_crypto_wep_exit); compat-drivers-2012-09-18/net/wireless/nl80211.h0000644000175000017500000001312712026211315020165 0ustar mcgrofmcgrof#ifndef __NET_WIRELESS_NL80211_H #define __NET_WIRELESS_NL80211_H #include "core.h" int nl80211_init(void); void nl80211_exit(void); void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 cmd); void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, struct net_device *netdev); void nl80211_send_reg_change_event(struct regulatory_request *request); void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, u16 status, gfp_t gfp); void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap); void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp); void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, const u8 *tsc, gfp_t gfp); void nl80211_send_beacon_hint_event(struct wiphy *wiphy, struct ieee80211_channel *channel_before, struct ieee80211_channel *channel_after); void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, gfp_t gfp); void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); void nl80211_send_remain_on_channel_cancel( struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp); void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp); void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, gfp_t gfp); int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u32 nlpid, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); void nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, u32 num_packets, gfp_t gfp); void nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, u32 num_packets, u32 rate, u32 intvl, gfp_t gfp); void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, int index, const u8 *bssid, bool preauth, gfp_t gfp); void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, struct net_device *dev, int freq, enum nl80211_channel_type type, gfp_t gfp); bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); bool nl80211_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); #endif /* __NET_WIRELESS_NL80211_H */ compat-drivers-2012-09-18/net/wireless/reg.h0000644000175000017500000001073612026211315017740 0ustar mcgrofmcgrof#ifndef __NET_WIRELESS_REG_H #define __NET_WIRELESS_REG_H /* * Copyright 2008-2011 Luis R. Rodriguez * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ extern const struct ieee80211_regdomain *cfg80211_regdomain; bool is_world_regdom(const char *alpha2); bool reg_is_valid_request(const char *alpha2); bool reg_supported_dfs_region(u8 dfs_region); int regulatory_hint_user(const char *alpha2, enum nl80211_user_reg_hint_type user_reg_hint_type); int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env); void wiphy_regulatory_register(struct wiphy *wiphy); void wiphy_regulatory_deregister(struct wiphy *wiphy); int __init regulatory_init(void); void regulatory_exit(void); int set_regdom(const struct ieee80211_regdomain *rd); bool reg_last_request_cell_base(void); /** * regulatory_hint_found_beacon - hints a beacon was found on a channel * @wiphy: the wireless device where the beacon was found on * @beacon_chan: the channel on which the beacon was found on * @gfp: context flags * * This informs the wireless core that a beacon from an AP was found on * the channel provided. This allows the wireless core to make educated * guesses on regulatory to help with world roaming. This is only used for * world roaming -- when we do not know our current location. This is * only useful on channels 12, 13 and 14 on the 2 GHz band as channels * 1-11 are already enabled by the world regulatory domain; and on * non-radar 5 GHz channels. * * Drivers do not need to call this, cfg80211 will do it for after a scan * on a newly found BSS. If you cannot make use of this feature you can * set the wiphy->disable_beacon_hints to true. */ int regulatory_hint_found_beacon(struct wiphy *wiphy, struct ieee80211_channel *beacon_chan, gfp_t gfp); /** * regulatory_hint_11d - hints a country IE as a regulatory domain * @wiphy: the wireless device giving the hint (used only for reporting * conflicts) * @band: the band on which the country IE was received on. This determines * the band we'll process the country IE channel triplets for. * @country_ie: pointer to the country IE * @country_ie_len: length of the country IE * * We will intersect the rd with the what CRDA tells us should apply * for the alpha2 this country IE belongs to, this prevents APs from * sending us incorrect or outdated information against a country. * * The AP is expected to provide Country IE channel triplets for the * band it is on. It is technically possible for APs to send channel * country IE triplets even for channels outside of the band they are * in but for that they would have to use the regulatory extension * in combination with a triplet but this behaviour is currently * not observed. For this reason if a triplet is seen with channel * information for a band the BSS is not present in it will be ignored. */ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, u8 *country_ie, u8 country_ie_len); /** * regulatory_hint_disconnect - informs all devices have been disconneted * * Regulotory rules can be enhanced further upon scanning and upon * connection to an AP. These rules become stale if we disconnect * and go to another country, whether or not we suspend and resume. * If we suspend, go to another country and resume we'll automatically * get disconnected shortly after resuming and things will be reset as well. * This routine is a helper to restore regulatory settings to how they were * prior to our first connect attempt. This includes ignoring country IE and * beacon regulatory hints. The ieee80211_regdom module parameter will always * be respected but if a user had set the regulatory domain that will take * precedence. * * Must be called from process context. */ void regulatory_hint_disconnect(void); #endif /* __NET_WIRELESS_REG_H */ compat-drivers-2012-09-18/net/wireless/chan.c0000644000175000017500000000724012026211315020063 0ustar mcgrofmcgrof/* * This file contains helper code to handle channel * settings and keeping track of what is possible at * any point in time. * * Copyright 2009 Johannes Berg */ #include #include #include "core.h" struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type) { struct ieee80211_channel *chan; struct ieee80211_sta_ht_cap *ht_cap; chan = ieee80211_get_channel(&rdev->wiphy, freq); /* Primary channel not allowed */ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) return NULL; if (channel_type == NL80211_CHAN_HT40MINUS && chan->flags & IEEE80211_CHAN_NO_HT40MINUS) return NULL; else if (channel_type == NL80211_CHAN_HT40PLUS && chan->flags & IEEE80211_CHAN_NO_HT40PLUS) return NULL; ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; if (channel_type != NL80211_CHAN_NO_HT) { if (!ht_cap->ht_supported) return NULL; if (channel_type != NL80211_CHAN_HT20 && (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) return NULL; } return chan; } bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct ieee80211_channel *sec_chan; int diff; switch (channel_type) { case NL80211_CHAN_HT40PLUS: diff = 20; break; case NL80211_CHAN_HT40MINUS: diff = -20; break; default: return true; } sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); if (!sec_chan) return false; /* we'll need a DFS capability later */ if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR)) return false; return true; } EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan); int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type chantype) { struct ieee80211_channel *chan; if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; if (!cfg80211_has_monitors_only(rdev)) return -EBUSY; chan = rdev_freq_to_chan(rdev, freq, chantype); if (!chan) return -EINVAL; return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); } void cfg80211_get_chan_state(struct wireless_dev *wdev, struct ieee80211_channel **chan, enum cfg80211_chan_mode *chanmode) { *chan = NULL; *chanmode = CHAN_MODE_UNDEFINED; ASSERT_WDEV_LOCK(wdev); if (wdev->netdev && !netif_running(wdev->netdev)) return; switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: if (wdev->current_bss) { *chan = wdev->current_bss->pub.channel; *chanmode = wdev->ibss_fixed ? CHAN_MODE_SHARED : CHAN_MODE_EXCLUSIVE; return; } case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (wdev->current_bss) { *chan = wdev->current_bss->pub.channel; *chanmode = CHAN_MODE_SHARED; return; } break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (wdev->beacon_interval) { *chan = wdev->channel; *chanmode = CHAN_MODE_SHARED; } return; case NL80211_IFTYPE_MESH_POINT: if (wdev->mesh_id_len) { *chan = wdev->channel; *chanmode = CHAN_MODE_SHARED; } return; case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: /* these interface types don't really have a channel */ return; case NL80211_IFTYPE_P2P_DEVICE: if (wdev->wiphy->features & NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL) *chanmode = CHAN_MODE_EXCLUSIVE; return; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: WARN_ON(1); } return; } compat-drivers-2012-09-18/net/wireless/sme.c0000644000175000017500000006437512026211315017752 0ustar mcgrofmcgrof/* * SME code for cfg80211's connect emulation. * * Copyright 2009 Johannes Berg * Copyright (C) 2009 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "nl80211.h" #include "reg.h" struct cfg80211_conn { struct cfg80211_connect_params params; /* these are sub-states of the _CONNECTING sme_state */ enum { CFG80211_CONN_IDLE, CFG80211_CONN_SCANNING, CFG80211_CONN_SCAN_AGAIN, CFG80211_CONN_AUTHENTICATE_NEXT, CFG80211_CONN_AUTHENTICATING, CFG80211_CONN_ASSOCIATE_NEXT, CFG80211_CONN_ASSOCIATING, CFG80211_CONN_DEAUTH_ASSOC_FAIL, } state; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 *ie; size_t ie_len; bool auto_auth, prev_bssid_valid; }; static bool cfg80211_is_all_idle(void) { struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; bool is_all_idle = true; mutex_lock(&cfg80211_mutex); /* * All devices must be idle as otherwise if you are actively * scanning some new beacon hints could be learned and would * count as new regulatory hints. */ list_for_each_entry(rdev, &cfg80211_rdev_list, list) { cfg80211_lock_rdev(rdev); list_for_each_entry(wdev, &rdev->wdev_list, list) { wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) is_all_idle = false; wdev_unlock(wdev); } cfg80211_unlock_rdev(rdev); } mutex_unlock(&cfg80211_mutex); return is_all_idle; } static void disconnect_work(struct work_struct *work) { if (!cfg80211_is_all_idle()) return; regulatory_hint_disconnect(); } static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); static int cfg80211_conn_scan(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_scan_request *request; int n_channels, err; ASSERT_RTNL(); ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); if (rdev->scan_req) return -EBUSY; if (wdev->conn->params.channel) { n_channels = 1; } else { enum ieee80211_band band; n_channels = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wdev->wiphy->bands[band]) continue; n_channels += wdev->wiphy->bands[band]->n_channels; } } request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + sizeof(request->channels[0]) * n_channels, GFP_KERNEL); if (!request) return -ENOMEM; if (wdev->conn->params.channel) request->channels[0] = wdev->conn->params.channel; else { int i = 0, j; enum ieee80211_band band; struct ieee80211_supported_band *bands; struct ieee80211_channel *channel; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { bands = wdev->wiphy->bands[band]; if (!bands) continue; for (j = 0; j < bands->n_channels; j++) { channel = &bands->channels[j]; if (channel->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[i++] = channel; } request->rates[band] = (1 << bands->n_bitrates) - 1; } n_channels = i; } request->n_channels = n_channels; request->ssids = (void *)&request->channels[n_channels]; request->n_ssids = 1; memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, wdev->conn->params.ssid_len); request->ssids[0].ssid_len = wdev->conn->params.ssid_len; request->wdev = wdev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; err = rdev->ops->scan(wdev->wiphy, request); if (!err) { wdev->conn->state = CFG80211_CONN_SCANNING; nl80211_send_scan_start(rdev, wdev); dev_hold(wdev->netdev); } else { rdev->scan_req = NULL; kfree(request); } return err; } static int cfg80211_conn_do_work(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_connect_params *params; const u8 *prev_bssid = NULL; int err; ASSERT_WDEV_LOCK(wdev); if (!wdev->conn) return 0; params = &wdev->conn->params; switch (wdev->conn->state) { case CFG80211_CONN_SCAN_AGAIN: return cfg80211_conn_scan(wdev); case CFG80211_CONN_AUTHENTICATE_NEXT: BUG_ON(!rdev->ops->auth); wdev->conn->state = CFG80211_CONN_AUTHENTICATING; return __cfg80211_mlme_auth(rdev, wdev->netdev, params->channel, params->auth_type, params->bssid, params->ssid, params->ssid_len, NULL, 0, params->key, params->key_len, params->key_idx); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; if (wdev->conn->prev_bssid_valid) prev_bssid = wdev->conn->prev_bssid; err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, params->bssid, prev_bssid, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto, params->flags, ¶ms->ht_capa, ¶ms->ht_capa_mask); if (err) __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, WLAN_REASON_DEAUTH_LEAVING, false); return err; case CFG80211_CONN_DEAUTH_ASSOC_FAIL: __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, WLAN_REASON_DEAUTH_LEAVING, false); /* return an error so that we call __cfg80211_connect_result() */ return -EINVAL; default: return 0; } } void cfg80211_conn_work(struct work_struct *work) { struct cfg80211_registered_device *rdev = container_of(work, struct cfg80211_registered_device, conn_work); struct wireless_dev *wdev; u8 bssid_buf[ETH_ALEN], *bssid = NULL; rtnl_lock(); cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { wdev_lock(wdev); if (!netif_running(wdev->netdev)) { wdev_unlock(wdev); continue; } if (wdev->sme_state != CFG80211_SME_CONNECTING) { wdev_unlock(wdev); continue; } if (wdev->conn->params.bssid) { memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); bssid = bssid_buf; } if (cfg80211_conn_do_work(wdev)) __cfg80211_connect_result( wdev->netdev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); wdev_unlock(wdev); } mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); rtnl_unlock(); } static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_bss *bss; u16 capa = WLAN_CAPABILITY_ESS; ASSERT_WDEV_LOCK(wdev); if (wdev->conn->params.privacy) capa |= WLAN_CAPABILITY_PRIVACY; bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, wdev->conn->params.bssid, wdev->conn->params.ssid, wdev->conn->params.ssid_len, WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, capa); if (!bss) return NULL; memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); wdev->conn->params.bssid = wdev->conn->bssid; wdev->conn->params.channel = bss->channel; wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); return bss; } static void __cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_bss *bss; ASSERT_WDEV_LOCK(wdev); if (wdev->sme_state != CFG80211_SME_CONNECTING) return; if (!wdev->conn) return; if (wdev->conn->state != CFG80211_CONN_SCANNING && wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) return; bss = cfg80211_get_conn_bss(wdev); if (bss) { cfg80211_put_bss(bss); } else { /* not found */ if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) schedule_work(&rdev->conn_work); else __cfg80211_connect_result( wdev->netdev, wdev->conn->params.bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); } } void cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); wdev_lock(wdev); __cfg80211_sme_scan_done(dev); wdev_unlock(wdev); mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); } void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); ASSERT_WDEV_LOCK(wdev); /* should only RX auth frames when connecting */ if (wdev->sme_state != CFG80211_SME_CONNECTING) return; if (WARN_ON(!wdev->conn)) return; if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && wdev->conn->auto_auth && wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { /* select automatically between only open, shared, leap */ switch (wdev->conn->params.auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: if (wdev->connect_keys) wdev->conn->params.auth_type = NL80211_AUTHTYPE_SHARED_KEY; else wdev->conn->params.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; break; case NL80211_AUTHTYPE_SHARED_KEY: wdev->conn->params.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; break; default: /* huh? */ wdev->conn->params.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; break; } wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); } else if (status_code != WLAN_STATUS_SUCCESS) { __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, status_code, false, NULL); } else if (wdev->sme_state == CFG80211_SME_CONNECTING && wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; schedule_work(&rdev->conn_work); } } bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); if (WARN_ON(!wdev->conn)) return false; if (!wdev->conn->prev_bssid_valid) return false; /* * Some stupid APs don't accept reassoc, so we * need to fall back to trying regular assoc. */ wdev->conn->prev_bssid_valid = false; wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; schedule_work(&rdev->conn_work); return true; } void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; schedule_work(&rdev->conn_work); } void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, u16 status, bool wextev, struct cfg80211_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; u8 *country_ie; #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif ASSERT_WDEV_LOCK(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) return; if (wdev->sme_state != CFG80211_SME_CONNECTING) return; nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT if (wextev) { if (req_ie && status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); } if (resp_ie && status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = resp_ie_len; wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; if (bssid && status == WLAN_STATUS_SUCCESS) { memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); wdev->wext.prev_bssid_valid = true; } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; } if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; if (status != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; if (wdev->conn) kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; kfree(wdev->connect_keys); wdev->connect_keys = NULL; wdev->ssid_len = 0; cfg80211_put_bss(bss); return; } if (!bss) bss = cfg80211_get_bss(wdev->wiphy, wdev->conn ? wdev->conn->params.channel : NULL, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (WARN_ON(!bss)) return; cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_upload_connect_keys(wdev); country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); if (!country_ie) return; /* * ieee80211_bss_get_ie() ensures we can access: * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ regulatory_hint_11d(wdev->wiphy, bss->channel->band, country_ie + 2, country_ie[1]); } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, u16 status, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); if (!ev) return; ev->type = EVENT_CONNECT_RESULT; if (bssid) memcpy(ev->cr.bssid, bssid, ETH_ALEN); if (req_ie_len) { ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); ev->cr.req_ie_len = req_ie_len; memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); } if (resp_ie_len) { ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; ev->cr.resp_ie_len = resp_ie_len; memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); } ev->cr.status = status; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } EXPORT_SYMBOL(cfg80211_connect_result); void __cfg80211_roamed(struct wireless_dev *wdev, struct cfg80211_bss *bss, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len) { #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif ASSERT_WDEV_LOCK(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) goto out; if (wdev->sme_state != CFG80211_SME_CONNECTED) goto out; /* internal error -- how did we get to CONNECTED w/o BSS? */ if (WARN_ON(!wdev->current_bss)) { goto out; } cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT if (req_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; wireless_send_event(wdev->netdev, IWEVASSOCREQIE, &wrqu, req_ie); } if (resp_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = resp_ie_len; wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, &wrqu, resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN); wdev->wext.prev_bssid_valid = true; wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif return; out: cfg80211_put_bss(bss); } void cfg80211_roamed(struct net_device *dev, struct ieee80211_channel *channel, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (WARN_ON(!bss)) return; cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); } EXPORT_SYMBOL(cfg80211_roamed); void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); if (WARN_ON(!bss)) return; ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); if (!ev) { cfg80211_put_bss(bss); return; } ev->type = EVENT_ROAMED; ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); ev->rm.req_ie_len = req_ie_len; memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; ev->rm.resp_ie_len = resp_ie_len; memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); ev->rm.bss = bss; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } EXPORT_SYMBOL(cfg80211_roamed_bss); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); int i; #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif ASSERT_WDEV_LOCK(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) return; if (wdev->sme_state != CFG80211_SME_CONNECTED) return; if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); } wdev->current_bss = NULL; wdev->sme_state = CFG80211_SME_IDLE; wdev->ssid_len = 0; if (wdev->conn) { kfree(wdev->conn->ie); wdev->conn->ie = NULL; kfree(wdev->conn); wdev->conn = NULL; } nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); /* * Delete all the keys ... pairwise keys can't really * exist any more anyway, but default keys might. */ if (rdev->ops->del_key) for (i = 0; i < 6; i++) rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); #ifdef CONFIG_CFG80211_WEXT memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); wdev->wext.connect.ssid_len = 0; #endif schedule_work(&cfg80211_disconnect_work); } void cfg80211_disconnected(struct net_device *dev, u16 reason, u8 *ie, size_t ie_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); ev = kzalloc(sizeof(*ev) + ie_len, gfp); if (!ev) return; ev->type = EVENT_DISCONNECTED; ev->dc.ie = ((u8 *)ev) + sizeof(*ev); ev->dc.ie_len = ie_len; memcpy((void *)ev->dc.ie, ie, ie_len); ev->dc.reason = reason; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } EXPORT_SYMBOL(cfg80211_disconnected); int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys, const u8 *prev_bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss = NULL; int err; ASSERT_WDEV_LOCK(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; if (WARN_ON(wdev->connect_keys)) { kfree(wdev->connect_keys); wdev->connect_keys = NULL; } cfg80211_oper_and_ht_capa(&connect->ht_capa_mask, rdev->wiphy.ht_capa_mod_mask); if (connkeys && connkeys->def >= 0) { int idx; u32 cipher; idx = connkeys->def; cipher = connkeys->params[idx].cipher; /* If given a WEP key we may need it for shared key auth */ if (cipher == WLAN_CIPHER_SUITE_WEP40 || cipher == WLAN_CIPHER_SUITE_WEP104) { connect->key_idx = idx; connect->key = connkeys->params[idx].key; connect->key_len = connkeys->params[idx].key_len; /* * If ciphers are not set (e.g. when going through * iwconfig), we have to set them appropriately here. */ if (connect->crypto.cipher_group == 0) connect->crypto.cipher_group = cipher; if (connect->crypto.n_ciphers_pairwise == 0) { connect->crypto.n_ciphers_pairwise = 1; connect->crypto.ciphers_pairwise[0] = cipher; } } } if (!rdev->ops->connect) { if (!rdev->ops->auth || !rdev->ops->assoc) return -EOPNOTSUPP; if (WARN_ON(wdev->conn)) return -EINPROGRESS; wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); if (!wdev->conn) return -ENOMEM; /* * Copy all parameters, and treat explicitly IEs, BSSID, SSID. */ memcpy(&wdev->conn->params, connect, sizeof(*connect)); if (connect->bssid) { wdev->conn->params.bssid = wdev->conn->bssid; memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); } if (connect->ie) { wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, GFP_KERNEL); wdev->conn->params.ie = wdev->conn->ie; if (!wdev->conn->ie) { kfree(wdev->conn); wdev->conn = NULL; return -ENOMEM; } } if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { wdev->conn->auto_auth = true; /* start with open system ... should mostly work */ wdev->conn->params.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; } else { wdev->conn->auto_auth = false; } memcpy(wdev->ssid, connect->ssid, connect->ssid_len); wdev->ssid_len = connect->ssid_len; wdev->conn->params.ssid = wdev->ssid; wdev->conn->params.ssid_len = connect->ssid_len; /* see if we have the bss already */ bss = cfg80211_get_conn_bss(wdev); wdev->sme_state = CFG80211_SME_CONNECTING; wdev->connect_keys = connkeys; if (prev_bssid) { memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); wdev->conn->prev_bssid_valid = true; } /* we're good if we have a matching bss struct */ if (bss) { wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; err = cfg80211_conn_do_work(wdev); cfg80211_put_bss(bss); } else { /* otherwise we'll need to scan for the AP first */ err = cfg80211_conn_scan(wdev); /* * If we can't scan right now, then we need to scan again * after the current scan finished, since the parameters * changed (unless we find a good AP anyway). */ if (err == -EBUSY) { err = 0; wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; } } if (err) { kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; wdev->connect_keys = NULL; wdev->ssid_len = 0; } return err; } else { wdev->sme_state = CFG80211_SME_CONNECTING; wdev->connect_keys = connkeys; err = rdev->ops->connect(&rdev->wiphy, dev, connect); if (err) { wdev->connect_keys = NULL; wdev->sme_state = CFG80211_SME_IDLE; return err; } memcpy(wdev->ssid, connect->ssid, connect->ssid_len); wdev->ssid_len = connect->ssid_len; return 0; } } int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys) { int err; mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); wdev_unlock(dev->ieee80211_ptr); mutex_unlock(&rdev->devlist_mtx); return err; } int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; ASSERT_WDEV_LOCK(wdev); if (wdev->sme_state == CFG80211_SME_IDLE) return -EINVAL; kfree(wdev->connect_keys); wdev->connect_keys = NULL; if (!rdev->ops->disconnect) { if (!rdev->ops->deauth) return -EOPNOTSUPP; /* was it connected by userspace SME? */ if (!wdev->conn) { cfg80211_mlme_down(rdev, dev); return 0; } if (wdev->sme_state == CFG80211_SME_CONNECTING && (wdev->conn->state == CFG80211_CONN_SCANNING || wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { wdev->sme_state = CFG80211_SME_IDLE; kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; wdev->ssid_len = 0; return 0; } /* wdev->conn->params.bssid must be set if > SCANNING */ err = __cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid, NULL, 0, reason, false); if (err) return err; } else { err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); if (err) return err; } if (wdev->sme_state == CFG80211_SME_CONNECTED) __cfg80211_disconnected(dev, NULL, 0, 0, false); else if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, wextev, NULL); return 0; } int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev) { int err; wdev_lock(dev->ieee80211_ptr); err = __cfg80211_disconnect(rdev, dev, reason, wextev); wdev_unlock(dev->ieee80211_ptr); return err; } void cfg80211_sme_disassoc(struct net_device *dev, struct cfg80211_internal_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 bssid[ETH_ALEN]; ASSERT_WDEV_LOCK(wdev); if (!wdev->conn) return; if (wdev->conn->state == CFG80211_CONN_IDLE) return; /* * Ok, so the association was made by this SME -- we don't * want it any more so deauthenticate too. */ memcpy(bssid, bss->pub.bssid, ETH_ALEN); __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, WLAN_REASON_DEAUTH_LEAVING, false); } compat-drivers-2012-09-18/net/wireless/debugfs.h0000644000175000017500000000045412026211315020576 0ustar mcgrofmcgrof#ifndef __CFG80211_DEBUGFS_H #define __CFG80211_DEBUGFS_H #ifdef CONFIG_CFG80211_DEBUGFS void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev); #else static inline void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {} #endif #endif /* __CFG80211_DEBUGFS_H */ compat-drivers-2012-09-18/net/wireless/core.h0000644000175000017500000003716012026211315020113 0ustar mcgrofmcgrof/* * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H #include #include #include #include #include #include #include #include #include #include #include #include "reg.h" struct cfg80211_registered_device { const struct cfg80211_ops *ops; struct list_head list; /* we hold this mutex during any call so that * we cannot do multiple calls at once, and also * to avoid the deregister call to proceed while * any call is in progress */ struct mutex mtx; /* rfkill support */ struct rfkill_ops rfkill_ops; struct rfkill *rfkill; struct work_struct rfkill_sync; /* ISO / IEC 3166 alpha2 for which this device is receiving * country IEs on, this can help disregard country IEs from APs * on the same alpha2 quickly. The alpha2 may differ from * cfg80211_regdomain's alpha2 when an intersection has occurred. * If the AP is reconfigured this can also be used to tell us if * the country on the country IE changed. */ char country_ie_alpha2[2]; /* If a Country IE has been received this tells us the environment * which its telling us its in. This defaults to ENVIRON_ANY */ enum environment_cap env; /* wiphy index, internal only */ int wiphy_idx; /* associated wireless interfaces */ struct mutex devlist_mtx; /* protected by devlist_mtx or RCU */ struct list_head wdev_list; int devlist_generation, wdev_id; int opencount; /* also protected by devlist_mtx */ wait_queue_head_t dev_wait; u32 ap_beacons_nlportid; /* protected by RTNL only */ int num_running_ifaces; int num_running_monitor_ifaces; /* BSSes/scanning */ spinlock_t bss_lock; struct list_head bss_list; struct rb_root bss_tree; u32 bss_generation; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ struct cfg80211_sched_scan_request *sched_scan_req; unsigned long suspend_at; struct work_struct scan_done_wk; struct work_struct sched_scan_results_wk; struct mutex sched_scan_mtx; #ifdef CONFIG_NL80211_TESTMODE struct genl_info *testmode_info; #endif struct work_struct conn_work; struct work_struct event_work; struct cfg80211_wowlan *wowlan; /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); }; static inline struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) { BUG_ON(!wiphy); return container_of(wiphy, struct cfg80211_registered_device, wiphy); } /* Note 0 is valid, hence phy0 */ static inline bool wiphy_idx_valid(int wiphy_idx) { return wiphy_idx >= 0; } static inline void cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) { int i; if (!rdev->wowlan) return; for (i = 0; i < rdev->wowlan->n_patterns; i++) kfree(rdev->wowlan->patterns[i].mask); kfree(rdev->wowlan->patterns); kfree(rdev->wowlan); } extern struct workqueue_struct *cfg80211_wq; extern struct mutex cfg80211_mutex; extern struct list_head cfg80211_rdev_list; extern int cfg80211_rdev_list_generation; static inline void assert_cfg80211_lock(void) { lockdep_assert_held(&cfg80211_mutex); } /* * You can use this to mark a wiphy_idx as not having an associated wiphy. * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL */ #define WIPHY_IDX_STALE -1 struct cfg80211_internal_bss { struct list_head list; struct rb_node rbn; unsigned long ts; struct kref ref; atomic_t hold; bool beacon_ies_allocated; bool proberesp_ies_allocated; /* must be last because of priv member */ struct cfg80211_bss pub; }; static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub) { return container_of(pub, struct cfg80211_internal_bss, pub); } static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) { atomic_inc(&bss->hold); } static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) { int r = atomic_dec_return(&bss->hold); WARN_ON(r < 0); } struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); /* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, struct net *net); static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) { mutex_lock(&rdev->mtx); } static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) { BUG_ON(IS_ERR(rdev) || !rdev); mutex_unlock(&rdev->mtx); } static inline void wdev_lock(struct wireless_dev *wdev) __acquires(wdev) { mutex_lock(&wdev->mtx); __acquire(wdev->mtx); } static inline void wdev_unlock(struct wireless_dev *wdev) __releases(wdev) { __release(wdev->mtx); mutex_unlock(&wdev->mtx); } #define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) { ASSERT_RTNL(); return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces && rdev->num_running_ifaces > 0; } enum cfg80211_event_type { EVENT_CONNECT_RESULT, EVENT_ROAMED, EVENT_DISCONNECTED, EVENT_IBSS_JOINED, }; struct cfg80211_event { struct list_head list; enum cfg80211_event_type type; union { struct { u8 bssid[ETH_ALEN]; const u8 *req_ie; const u8 *resp_ie; size_t req_ie_len; size_t resp_ie_len; u16 status; } cr; struct { const u8 *req_ie; const u8 *resp_ie; size_t req_ie_len; size_t resp_ie_len; struct cfg80211_bss *bss; } rm; struct { const u8 *ie; size_t ie_len; u16 reason; } dc; struct { u8 bssid[ETH_ALEN]; } ij; }; }; struct cfg80211_cached_keys { struct key_params params[6]; u8 data[6][WLAN_MAX_KEY_LEN]; int def, defmgmt; }; enum cfg80211_chan_mode { CHAN_MODE_UNDEFINED, CHAN_MODE_SHARED, CHAN_MODE_EXCLUSIVE, }; /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); void cfg80211_bss_expire(struct cfg80211_registered_device *dev); void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs); /* IBSS */ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ibss_params *params, struct cfg80211_cached_keys *connkeys); int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ibss_params *params, struct cfg80211_cached_keys *connkeys); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); /* mesh */ extern const struct mesh_config default_mesh_config; extern const struct mesh_setup default_mesh_setup; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf); int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int freq, enum nl80211_channel_type channel_type); /* AP */ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev); /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, const u8 *key, int key_len, int key_idx); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, const u8 *key, int key_len, int key_idx); int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, const u8 *bssid, const u8 *prev_bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt, u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, struct ieee80211_ht_cap *ht_capa_mask); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, const u8 *bssid, const u8 *prev_bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt, u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, struct ieee80211_ht_cap *ht_capa_mask); int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, bool local_state_change); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, bool local_state_change); int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, bool local_state_change); void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct net_device *dev); void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, u16 status, bool wextev, struct cfg80211_bss *bss); int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, u16 frame_type, const u8 *match_data, int match_len); void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie); void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask); /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys, const u8 *prev_bssid); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys); int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); void __cfg80211_roamed(struct wireless_dev *wdev, struct cfg80211_bss *bss, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len); int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_conn_work(struct work_struct *work); void cfg80211_sme_failed_assoc(struct wireless_dev *wdev); bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); /* internal helpers */ bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, struct cfg80211_internal_bss *bss); void __cfg80211_scan_done(struct work_struct *wk); void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); void __cfg80211_sched_scan_results(struct work_struct *wk); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, u32 *flags, struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); void cfg80211_process_wdev_events(struct wireless_dev *wdev); int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype, struct ieee80211_channel *chan, enum cfg80211_chan_mode chanmode); static inline int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype) { return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, CHAN_MODE_UNDEFINED); } static inline int cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype) { return cfg80211_can_change_interface(rdev, NULL, iftype); } static inline int cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, enum cfg80211_chan_mode chanmode) { return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, chan, chanmode); } void cfg80211_get_chan_state(struct wireless_dev *wdev, struct ieee80211_channel **chan, enum cfg80211_chan_mode *chanmode); struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type); int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type chantype); int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, const u8 *rates, unsigned int n_rates, u32 *mask); int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int); void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) #else /* * Trick to enable using it as a condition, * and also not give a warning when it's * not used that way. */ #define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) #endif #endif /* __NET_WIRELESS_CORE_H */ compat-drivers-2012-09-18/net/wireless/db.txt0000644000175000017500000000136712026211315020140 0ustar mcgrofmcgrof# # This file is a placeholder to prevent accidental build breakage if someone # enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to # enable that build option. # # You should be using CRDA instead. It is even better if you use the CRDA # package provided by your distribution, since they will probably keep it # up-to-date on your behalf. # # If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will # need to replace this file with one containing appropriately formatted # regulatory rules that cover the regulatory domains you will be using. Your # best option is to extract the db.txt file from the wireless-regdb git # repository: # # git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git # compat-drivers-2012-09-18/net/wireless/ethtool.c0000644000175000017500000000612212026211315020626 0ustar mcgrofmcgrof#include #include #include "core.h" #include "ethtool.h" static void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct wireless_dev *wdev = dev->ieee80211_ptr; strlcpy(info->driver, wiphy_dev(wdev->wiphy)->driver->name, sizeof(info->driver)); strlcpy(info->version, init_utsname()->release, sizeof(info->version)); if (wdev->wiphy->fw_version[0]) strncpy(info->fw_version, wdev->wiphy->fw_version, sizeof(info->fw_version)); else strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), sizeof(info->bus_info)); } static int cfg80211_get_regs_len(struct net_device *dev) { /* For now, return 0... */ return 0; } static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *data) { struct wireless_dev *wdev = dev->ieee80211_ptr; regs->version = wdev->wiphy->hw_version; regs->len = 0; } static void cfg80211_get_ringparam(struct net_device *dev, struct ethtool_ringparam *rp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); memset(rp, 0, sizeof(*rp)); if (rdev->ops->get_ringparam) rdev->ops->get_ringparam(wdev->wiphy, &rp->tx_pending, &rp->tx_max_pending, &rp->rx_pending, &rp->rx_max_pending); } static int cfg80211_set_ringparam(struct net_device *dev, struct ethtool_ringparam *rp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) return -EINVAL; if (rdev->ops->set_ringparam) return rdev->ops->set_ringparam(wdev->wiphy, rp->tx_pending, rp->rx_pending); return -ENOTSUPP; } static int cfg80211_get_sset_count(struct net_device *dev, int sset) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); if (rdev->ops->get_et_sset_count) return rdev->ops->get_et_sset_count(wdev->wiphy, dev, sset); return -EOPNOTSUPP; } static void cfg80211_get_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); if (rdev->ops->get_et_stats) rdev->ops->get_et_stats(wdev->wiphy, dev, stats, data); } static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); if (rdev->ops->get_et_strings) rdev->ops->get_et_strings(wdev->wiphy, dev, sset, data); } const struct ethtool_ops cfg80211_ethtool_ops = { .get_drvinfo = cfg80211_get_drvinfo, .get_regs_len = cfg80211_get_regs_len, .get_regs = cfg80211_get_regs, .get_link = ethtool_op_get_link, .get_ringparam = cfg80211_get_ringparam, .set_ringparam = cfg80211_set_ringparam, .get_strings = cfg80211_get_strings, .get_ethtool_stats = cfg80211_get_stats, .get_sset_count = cfg80211_get_sset_count, }; compat-drivers-2012-09-18/net/wireless/radiotap.c0000644000175000017500000002660512026211315020763 0ustar mcgrofmcgrof/* * Radiotap parser * * Copyright 2007 Andy Green * Copyright 2009 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See COPYING for more details. */ #include #include #include #include #include /* function prototypes and related defs are in include/net/cfg80211.h */ static const struct radiotap_align_size rtap_namespace_sizes[] = { [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, /* * add more here as they are defined in radiotap.h */ }; static const struct ieee80211_radiotap_namespace radiotap_ns = { .n_bits = ARRAY_SIZE(rtap_namespace_sizes), .align_size = rtap_namespace_sizes, }; /** * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization * @iterator: radiotap_iterator to initialize * @radiotap_header: radiotap header to parse * @max_length: total length we can parse into (eg, whole packet length) * * Returns: 0 or a negative error code if there is a problem. * * This function initializes an opaque iterator struct which can then * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap * argument which is present in the header. It knows about extended * present headers and handles them. * * How to use: * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) * checking for a good 0 return code. Then loop calling * __ieee80211_radiotap_iterator_next()... it returns either 0, * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. * The iterator's @this_arg member points to the start of the argument * associated with the current argument index that is present, which can be * found in the iterator's @this_arg_index member. This arg index corresponds * to the IEEE80211_RADIOTAP_... defines. * * Radiotap header length: * You can find the CPU-endian total radiotap header length in * iterator->max_length after executing ieee80211_radiotap_iterator_init() * successfully. * * Alignment Gotcha: * You must take care when dereferencing iterator.this_arg * for multibyte types... the pointer is not aligned. Use * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. * * Example code: * See Documentation/networking/radiotap-headers.txt */ int ieee80211_radiotap_iterator_init( struct ieee80211_radiotap_iterator *iterator, struct ieee80211_radiotap_header *radiotap_header, int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) { /* Linux only supports version 0 radiotap format */ if (radiotap_header->it_version) return -EINVAL; /* sanity check for allowed length and radiotap length field */ if (max_length < get_unaligned_le16(&radiotap_header->it_len)) return -EINVAL; iterator->_rtheader = radiotap_header; iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); iterator->_arg_index = 0; iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); iterator->_reset_on_ext = 0; iterator->_next_bitmap = &radiotap_header->it_present; iterator->_next_bitmap++; iterator->_vns = vns; iterator->current_namespace = &radiotap_ns; iterator->is_radiotap_ns = 1; /* find payload start allowing for extended bitmap(s) */ if (iterator->_bitmap_shifter & (1<_arg) & (1 << IEEE80211_RADIOTAP_EXT)) { iterator->_arg += sizeof(uint32_t); /* * check for insanity where the present bitmaps * keep claiming to extend up to or even beyond the * stated radiotap header length */ if ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader > (unsigned long)iterator->_max_length) return -EINVAL; } iterator->_arg += sizeof(uint32_t); /* * no need to check again for blowing past stated radiotap * header length, because ieee80211_radiotap_iterator_next * checks it before it is dereferenced */ } iterator->this_arg = iterator->_arg; /* we are all initialized happily */ return 0; } EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); static void find_ns(struct ieee80211_radiotap_iterator *iterator, uint32_t oui, uint8_t subns) { int i; iterator->current_namespace = NULL; if (!iterator->_vns) return; for (i = 0; i < iterator->_vns->n_ns; i++) { if (iterator->_vns->ns[i].oui != oui) continue; if (iterator->_vns->ns[i].subns != subns) continue; iterator->current_namespace = &iterator->_vns->ns[i]; break; } } /** * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg * @iterator: radiotap_iterator to move to next arg (if any) * * Returns: 0 if there is an argument to handle, * -ENOENT if there are no more args or -EINVAL * if there is something else wrong. * * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) * in @this_arg_index and sets @this_arg to point to the * payload for the field. It takes care of alignment handling and extended * present fields. @this_arg can be changed by the caller (eg, * incremented to move inside a compound argument like * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in * little-endian format whatever the endianess of your CPU. * * Alignment Gotcha: * You must take care when dereferencing iterator.this_arg * for multibyte types... the pointer is not aligned. Use * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. */ int ieee80211_radiotap_iterator_next( struct ieee80211_radiotap_iterator *iterator) { while (1) { int hit = 0; int pad, align, size, subns; uint32_t oui; /* if no more EXT bits, that's it */ if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && !(iterator->_bitmap_shifter & 1)) return -ENOENT; if (!(iterator->_bitmap_shifter & 1)) goto next_entry; /* arg not present */ /* get alignment/size of data */ switch (iterator->_arg_index % 32) { case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: case IEEE80211_RADIOTAP_EXT: align = 1; size = 0; break; case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: align = 2; size = 6; break; default: if (!iterator->current_namespace || iterator->_arg_index >= iterator->current_namespace->n_bits) { if (iterator->current_namespace == &radiotap_ns) return -ENOENT; align = 0; } else { align = iterator->current_namespace->align_size[iterator->_arg_index].align; size = iterator->current_namespace->align_size[iterator->_arg_index].size; } if (!align) { /* skip all subsequent data */ iterator->_arg = iterator->_next_ns_data; /* give up on this namespace */ iterator->current_namespace = NULL; goto next_entry; } break; } /* * arg is present, account for alignment padding * * Note that these alignments are relative to the start * of the radiotap header. There is no guarantee * that the radiotap header itself is aligned on any * kind of boundary. * * The above is why get_unaligned() is used to dereference * multibyte elements from the radiotap area. */ pad = ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader) & (align - 1); if (pad) iterator->_arg += align - pad; if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { int vnslen; if ((unsigned long)iterator->_arg + size - (unsigned long)iterator->_rtheader > (unsigned long)iterator->_max_length) return -EINVAL; oui = (*iterator->_arg << 16) | (*(iterator->_arg + 1) << 8) | *(iterator->_arg + 2); subns = *(iterator->_arg + 3); find_ns(iterator, oui, subns); vnslen = get_unaligned_le16(iterator->_arg + 4); iterator->_next_ns_data = iterator->_arg + size + vnslen; if (!iterator->current_namespace) size += vnslen; } /* * this is what we will return to user, but we need to * move on first so next call has something fresh to test */ iterator->this_arg_index = iterator->_arg_index; iterator->this_arg = iterator->_arg; iterator->this_arg_size = size; /* internally move on the size of this arg */ iterator->_arg += size; /* * check for insanity where we are given a bitmap that * claims to have more arg content than the length of the * radiotap section. We will normally end up equalling this * max_length on the last arg, never exceeding it. */ if ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader > (unsigned long)iterator->_max_length) return -EINVAL; /* these special ones are valid in each bitmap word */ switch (iterator->_arg_index % 32) { case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: iterator->_reset_on_ext = 1; iterator->is_radiotap_ns = 0; /* * If parser didn't register this vendor * namespace with us, allow it to show it * as 'raw. Do do that, set argument index * to vendor namespace. */ iterator->this_arg_index = IEEE80211_RADIOTAP_VENDOR_NAMESPACE; if (!iterator->current_namespace) hit = 1; goto next_entry; case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: iterator->_reset_on_ext = 1; iterator->current_namespace = &radiotap_ns; iterator->is_radiotap_ns = 1; goto next_entry; case IEEE80211_RADIOTAP_EXT: /* * bit 31 was set, there is more * -- move to next u32 bitmap */ iterator->_bitmap_shifter = get_unaligned_le32(iterator->_next_bitmap); iterator->_next_bitmap++; if (iterator->_reset_on_ext) iterator->_arg_index = 0; else iterator->_arg_index++; iterator->_reset_on_ext = 0; break; default: /* we've got a hit! */ hit = 1; next_entry: iterator->_bitmap_shifter >>= 1; iterator->_arg_index++; } /* if we found a valid arg earlier, return it now */ if (hit) return 0; } } EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); compat-drivers-2012-09-18/net/wireless/regdb.h0000644000175000017500000000166412026211315020246 0ustar mcgrofmcgrof#ifndef __REGDB_H__ #define __REGDB_H__ /* * Copyright 2009 John W. Linville * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ extern const struct ieee80211_regdomain *reg_regdb[]; extern int reg_regdb_size; #endif /* __REGDB_H__ */ compat-drivers-2012-09-18/net/wireless/ibss.c0000644000175000017500000003051512026211315020113 0ustar mcgrofmcgrof/* * Some IBSS support code for cfg80211. * * Copyright 2009 Johannes Berg */ #include #include #include #include #include #include "wext-compat.h" #include "nl80211.h" void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return; if (!wdev->ssid_len) return; bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); if (WARN_ON(!bss)) return; if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); } cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_upload_connect_keys(wdev); nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT memset(&wrqu, 0, sizeof(wrqu)); memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); ev = kzalloc(sizeof(*ev), gfp); if (!ev) return; ev->type = EVENT_IBSS_JOINED; memcpy(ev->cr.bssid, bssid, ETH_ALEN); spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } EXPORT_SYMBOL(cfg80211_ibss_joined); int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ibss_params *params, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; ASSERT_WDEV_LOCK(wdev); if (wdev->ssid_len) return -EALREADY; if (!params->basic_rates) { /* * If no rates were explicitly configured, * use the mandatory rate set for 11b or * 11a for maximum compatibility. */ struct ieee80211_supported_band *sband = rdev->wiphy.bands[params->channel->band]; int j; u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ? IEEE80211_RATE_MANDATORY_A : IEEE80211_RATE_MANDATORY_B; for (j = 0; j < sband->n_bitrates; j++) { if (sband->bitrates[j].flags & flag) params->basic_rates |= BIT(j); } } if (WARN_ON(wdev->connect_keys)) kfree(wdev->connect_keys); wdev->connect_keys = connkeys; wdev->ibss_fixed = params->channel_fixed; #ifdef CONFIG_CFG80211_WEXT wdev->wext.ibss.channel = params->channel; #endif wdev->sme_state = CFG80211_SME_CONNECTING; err = cfg80211_can_use_chan(rdev, wdev, params->channel, params->channel_fixed ? CHAN_MODE_SHARED : CHAN_MODE_EXCLUSIVE); if (err) { wdev->connect_keys = NULL; return err; } err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); if (err) { wdev->connect_keys = NULL; wdev->sme_state = CFG80211_SME_IDLE; return err; } memcpy(wdev->ssid, params->ssid, params->ssid_len); wdev->ssid_len = params->ssid_len; return 0; } int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ibss_params *params, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_join_ibss(rdev, dev, params, connkeys); wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); return err; } static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); int i; ASSERT_WDEV_LOCK(wdev); kfree(wdev->connect_keys); wdev->connect_keys = NULL; /* * Delete all the keys ... pairwise keys can't really * exist any more anyway, but default keys might. */ if (rdev->ops->del_key) for (i = 0; i < 6; i++) rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); } wdev->current_bss = NULL; wdev->sme_state = CFG80211_SME_IDLE; wdev->ssid_len = 0; #ifdef CONFIG_CFG80211_WEXT if (!nowext) wdev->wext.ibss.ssid_len = 0; #endif } void cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; wdev_lock(wdev); __cfg80211_clear_ibss(dev, nowext); wdev_unlock(wdev); } int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; ASSERT_WDEV_LOCK(wdev); if (!wdev->ssid_len) return -ENOLINK; err = rdev->ops->leave_ibss(&rdev->wiphy, dev); if (err) return err; __cfg80211_clear_ibss(dev, nowext); return 0; } int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); err = __cfg80211_leave_ibss(rdev, dev, nowext); wdev_unlock(wdev); return err; } #ifdef CONFIG_CFG80211_WEXT int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct cfg80211_cached_keys *ck = NULL; enum ieee80211_band band; int i, err; ASSERT_WDEV_LOCK(wdev); if (!wdev->wext.ibss.beacon_interval) wdev->wext.ibss.beacon_interval = 100; /* try to find an IBSS channel if none requested ... */ if (!wdev->wext.ibss.channel) { for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; sband = rdev->wiphy.bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; if (chan->flags & IEEE80211_CHAN_NO_IBSS) continue; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; wdev->wext.ibss.channel = chan; break; } if (wdev->wext.ibss.channel) break; } if (!wdev->wext.ibss.channel) return -EINVAL; } /* don't join -- SSID is not there */ if (!wdev->wext.ibss.ssid_len) return 0; if (!netif_running(wdev->netdev)) return 0; if (wdev->wext.keys) { wdev->wext.keys->def = wdev->wext.default_key; wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; } wdev->wext.ibss.privacy = wdev->wext.default_key != -1; if (wdev->wext.keys) { ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); if (!ck) return -ENOMEM; for (i = 0; i < 6; i++) ck->params[i].key = ck->data[i]; } err = __cfg80211_join_ibss(rdev, wdev->netdev, &wdev->wext.ibss, ck); if (err) kfree(ck); return err; } int cfg80211_ibss_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct ieee80211_channel *chan = NULL; int err, freq; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; if (!rdev->ops->join_ibss) return -EOPNOTSUPP; freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); if (freq < 0) return freq; if (freq) { chan = ieee80211_get_channel(wdev->wiphy, freq); if (!chan) return -EINVAL; if (chan->flags & IEEE80211_CHAN_NO_IBSS || chan->flags & IEEE80211_CHAN_DISABLED) return -EINVAL; } if (wdev->wext.ibss.channel == chan) return 0; wdev_lock(wdev); err = 0; if (wdev->ssid_len) err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) return err; if (chan) { wdev->wext.ibss.channel = chan; wdev->wext.ibss.channel_fixed = true; } else { /* cfg80211_ibss_wext_join will pick one if needed */ wdev->wext.ibss.channel_fixed = false; } mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); return err; } int cfg80211_ibss_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_channel *chan = NULL; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; else if (wdev->wext.ibss.channel) chan = wdev->wext.ibss.channel; wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; freq->e = 6; return 0; } /* no channel if not joining */ return -EINVAL; } int cfg80211_ibss_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); size_t len = data->length; int err; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; if (!rdev->ops->join_ibss) return -EOPNOTSUPP; wdev_lock(wdev); err = 0; if (wdev->ssid_len) err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) return err; /* iwconfig uses nul termination in SSID.. */ if (len > 0 && ssid[len - 1] == '\0') len--; wdev->wext.ibss.ssid = wdev->ssid; memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); return err; } int cfg80211_ibss_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; data->flags = 0; wdev_lock(wdev); if (wdev->ssid_len) { data->flags = 1; data->length = wdev->ssid_len; memcpy(ssid, wdev->ssid, data->length); } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) { data->flags = 1; data->length = wdev->wext.ibss.ssid_len; memcpy(ssid, wdev->wext.ibss.ssid, data->length); } wdev_unlock(wdev); return 0; } int cfg80211_ibss_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 *bssid = ap_addr->sa_data; int err; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; if (!rdev->ops->join_ibss) return -EOPNOTSUPP; if (ap_addr->sa_family != ARPHRD_ETHER) return -EINVAL; /* automatic mode */ if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; /* both automatic */ if (!bssid && !wdev->wext.ibss.bssid) return 0; /* fixed already - and no change */ if (wdev->wext.ibss.bssid && bssid && ether_addr_equal(bssid, wdev->wext.ibss.bssid)) return 0; wdev_lock(wdev); err = 0; if (wdev->ssid_len) err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) return err; if (bssid) { memcpy(wdev->wext.bssid, bssid, ETH_ALEN); wdev->wext.ibss.bssid = wdev->wext.bssid; } else wdev->wext.ibss.bssid = NULL; mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); return err; } int cfg80211_ibss_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; ap_addr->sa_family = ARPHRD_ETHER; wdev_lock(wdev); if (wdev->current_bss) memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else if (wdev->wext.ibss.bssid) memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); else memset(ap_addr->sa_data, 0, ETH_ALEN); wdev_unlock(wdev); return 0; } #endif compat-drivers-2012-09-18/net/wireless/ap.c0000644000175000017500000000163712026211315017556 0ustar mcgrofmcgrof#include #include #include #include "nl80211.h" #include "core.h" static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; ASSERT_WDEV_LOCK(wdev); if (!rdev->ops->stop_ap) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; if (!wdev->beacon_interval) return -ENOENT; err = rdev->ops->stop_ap(&rdev->wiphy, dev); if (!err) { wdev->beacon_interval = 0; wdev->channel = NULL; } return err; } int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); err = __cfg80211_stop_ap(rdev, dev); wdev_unlock(wdev); return err; } compat-drivers-2012-09-18/net/wireless/wext-compat.c0000644000175000017500000011575512026211315021435 0ustar mcgrofmcgrof/* * cfg80211 - wext compat code * * This is temporary code until all wireless functionality is migrated * into cfg80211, when that happens all the exports here go away and * we directly assign the wireless handlers of wireless interfaces. * * Copyright 2008-2009 Johannes Berg */ #include #include #include #include #include #include #include #include #include #include "wext-compat.h" #include "core.h" int cfg80211_wext_giwname(struct net_device *dev, struct iw_request_info *info, char *name, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_supported_band *sband; bool is_ht = false, is_a = false, is_b = false, is_g = false; if (!wdev) return -EOPNOTSUPP; sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; if (sband) { is_a = true; is_ht |= sband->ht_cap.ht_supported; } sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; if (sband) { int i; /* Check for mandatory rates */ for (i = 0; i < sband->n_bitrates; i++) { if (sband->bitrates[i].bitrate == 10) is_b = true; if (sband->bitrates[i].bitrate == 60) is_g = true; } is_ht |= sband->ht_cap.ht_supported; } strcpy(name, "IEEE 802.11"); if (is_a) strcat(name, "a"); if (is_b) strcat(name, "b"); if (is_g) strcat(name, "g"); if (is_ht) strcat(name, "n"); return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwname); int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; struct vif_params vifparams; enum nl80211_iftype type; int ret; rdev = wiphy_to_dev(wdev->wiphy); switch (*mode) { case IW_MODE_INFRA: type = NL80211_IFTYPE_STATION; break; case IW_MODE_ADHOC: type = NL80211_IFTYPE_ADHOC; break; case IW_MODE_REPEAT: type = NL80211_IFTYPE_WDS; break; case IW_MODE_MONITOR: type = NL80211_IFTYPE_MONITOR; break; default: return -EINVAL; } if (type == wdev->iftype) return 0; memset(&vifparams, 0, sizeof(vifparams)); cfg80211_lock_rdev(rdev); ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); cfg80211_unlock_rdev(rdev); return ret; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode); int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; if (!wdev) return -EOPNOTSUPP; switch (wdev->iftype) { case NL80211_IFTYPE_AP: *mode = IW_MODE_MASTER; break; case NL80211_IFTYPE_STATION: *mode = IW_MODE_INFRA; break; case NL80211_IFTYPE_ADHOC: *mode = IW_MODE_ADHOC; break; case NL80211_IFTYPE_MONITOR: *mode = IW_MODE_MONITOR; break; case NL80211_IFTYPE_WDS: *mode = IW_MODE_REPEAT; break; case NL80211_IFTYPE_AP_VLAN: *mode = IW_MODE_SECOND; /* FIXME */ break; default: *mode = IW_MODE_AUTO; break; } return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwmode); int cfg80211_wext_giwrange(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_range *range = (struct iw_range *) extra; enum ieee80211_band band; int i, c = 0; if (!wdev) return -EOPNOTSUPP; data->length = sizeof(struct iw_range); memset(range, 0, sizeof(struct iw_range)); range->we_version_compiled = WIRELESS_EXT; range->we_version_source = 21; range->retry_capa = IW_RETRY_LIMIT; range->retry_flags = IW_RETRY_LIMIT; range->min_retry = 0; range->max_retry = 255; range->min_rts = 0; range->max_rts = 2347; range->min_frag = 256; range->max_frag = 2346; range->max_encoding_tokens = 4; range->max_qual.updated = IW_QUAL_NOISE_INVALID; switch (wdev->wiphy->signal_type) { case CFG80211_SIGNAL_TYPE_NONE: break; case CFG80211_SIGNAL_TYPE_MBM: range->max_qual.level = -110; range->max_qual.qual = 70; range->avg_qual.qual = 35; range->max_qual.updated |= IW_QUAL_DBM; range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; break; case CFG80211_SIGNAL_TYPE_UNSPEC: range->max_qual.level = 100; range->max_qual.qual = 100; range->avg_qual.qual = 50; range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; break; } range->avg_qual.level = range->max_qual.level / 2; range->avg_qual.noise = range->max_qual.noise / 2; range->avg_qual.updated = range->max_qual.updated; for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) { switch (wdev->wiphy->cipher_suites[i]) { case WLAN_CIPHER_SUITE_TKIP: range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_WPA); break; case WLAN_CIPHER_SUITE_CCMP: range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP | IW_ENC_CAPA_WPA2); break; case WLAN_CIPHER_SUITE_WEP40: range->encoding_size[range->num_encoding_sizes++] = WLAN_KEY_LEN_WEP40; break; case WLAN_CIPHER_SUITE_WEP104: range->encoding_size[range->num_encoding_sizes++] = WLAN_KEY_LEN_WEP104; break; } } for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { struct ieee80211_supported_band *sband; sband = wdev->wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { struct ieee80211_channel *chan = &sband->channels[i]; if (!(chan->flags & IEEE80211_CHAN_DISABLED)) { range->freq[c].i = ieee80211_frequency_to_channel( chan->center_freq); range->freq[c].m = chan->center_freq; range->freq[c].e = 6; c++; } } } range->num_channels = c; range->num_frequency = c; IW_EVENT_CAPA_SET_KERNEL(range->event_capa); IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); if (wdev->wiphy->max_scan_ssids > 0) range->scan_capa |= IW_SCAN_CAPA_ESSID; return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); /** * cfg80211_wext_freq - get wext frequency for non-"auto" * @wiphy: the wiphy * @freq: the wext freq encoding * * Returns a frequency, or a negative error code, or 0 for auto. */ int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) { /* * Parse frequency - return 0 for auto and * -EINVAL for impossible things. */ if (freq->e == 0) { enum ieee80211_band band = IEEE80211_BAND_2GHZ; if (freq->m < 0) return 0; if (freq->m > 14) band = IEEE80211_BAND_5GHZ; return ieee80211_channel_to_frequency(freq->m, band); } else { int i, div = 1000000; for (i = 0; i < freq->e; i++) div /= 10; if (div <= 0) return -EINVAL; return freq->m / div; } } int cfg80211_wext_siwrts(struct net_device *dev, struct iw_request_info *info, struct iw_param *rts, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u32 orts = wdev->wiphy->rts_threshold; int err; if (rts->disabled || !rts->fixed) wdev->wiphy->rts_threshold = (u32) -1; else if (rts->value < 0) return -EINVAL; else wdev->wiphy->rts_threshold = rts->value; err = rdev->ops->set_wiphy_params(wdev->wiphy, WIPHY_PARAM_RTS_THRESHOLD); if (err) wdev->wiphy->rts_threshold = orts; return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwrts); int cfg80211_wext_giwrts(struct net_device *dev, struct iw_request_info *info, struct iw_param *rts, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; rts->value = wdev->wiphy->rts_threshold; rts->disabled = rts->value == (u32) -1; rts->fixed = 1; return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwrts); int cfg80211_wext_siwfrag(struct net_device *dev, struct iw_request_info *info, struct iw_param *frag, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u32 ofrag = wdev->wiphy->frag_threshold; int err; if (frag->disabled || !frag->fixed) wdev->wiphy->frag_threshold = (u32) -1; else if (frag->value < 256) return -EINVAL; else { /* Fragment length must be even, so strip LSB. */ wdev->wiphy->frag_threshold = frag->value & ~0x1; } err = rdev->ops->set_wiphy_params(wdev->wiphy, WIPHY_PARAM_FRAG_THRESHOLD); if (err) wdev->wiphy->frag_threshold = ofrag; return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwfrag); int cfg80211_wext_giwfrag(struct net_device *dev, struct iw_request_info *info, struct iw_param *frag, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; frag->value = wdev->wiphy->frag_threshold; frag->disabled = frag->value == (u32) -1; frag->fixed = 1; return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwfrag); static int cfg80211_wext_siwretry(struct net_device *dev, struct iw_request_info *info, struct iw_param *retry, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u32 changed = 0; u8 olong = wdev->wiphy->retry_long; u8 oshort = wdev->wiphy->retry_short; int err; if (retry->disabled || (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) return -EINVAL; if (retry->flags & IW_RETRY_LONG) { wdev->wiphy->retry_long = retry->value; changed |= WIPHY_PARAM_RETRY_LONG; } else if (retry->flags & IW_RETRY_SHORT) { wdev->wiphy->retry_short = retry->value; changed |= WIPHY_PARAM_RETRY_SHORT; } else { wdev->wiphy->retry_short = retry->value; wdev->wiphy->retry_long = retry->value; changed |= WIPHY_PARAM_RETRY_LONG; changed |= WIPHY_PARAM_RETRY_SHORT; } if (!changed) return 0; err = rdev->ops->set_wiphy_params(wdev->wiphy, changed); if (err) { wdev->wiphy->retry_short = oshort; wdev->wiphy->retry_long = olong; } return err; } int cfg80211_wext_giwretry(struct net_device *dev, struct iw_request_info *info, struct iw_param *retry, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; retry->disabled = 0; if (retry->flags == 0 || (retry->flags & IW_RETRY_SHORT)) { /* * First return short value, iwconfig will ask long value * later if needed */ retry->flags |= IW_RETRY_LIMIT; retry->value = wdev->wiphy->retry_short; if (wdev->wiphy->retry_long != wdev->wiphy->retry_short) retry->flags |= IW_RETRY_LONG; return 0; } if (retry->flags & IW_RETRY_LONG) { retry->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; retry->value = wdev->wiphy->retry_long; } return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, struct net_device *dev, bool pairwise, const u8 *addr, bool remove, bool tx_key, int idx, struct key_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err, i; bool rejoin = false; if (pairwise && !addr) return -EINVAL; if (!wdev->wext.keys) { wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), GFP_KERNEL); if (!wdev->wext.keys) return -ENOMEM; for (i = 0; i < 6; i++) wdev->wext.keys->params[i].key = wdev->wext.keys->data[i]; } if (wdev->iftype != NL80211_IFTYPE_ADHOC && wdev->iftype != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { if (!wdev->current_bss) return -ENOLINK; if (!rdev->ops->set_default_mgmt_key) return -EOPNOTSUPP; if (idx < 4 || idx > 5) return -EINVAL; } else if (idx < 0 || idx > 3) return -EINVAL; if (remove) { err = 0; if (wdev->current_bss) { /* * If removing the current TX key, we will need to * join a new IBSS without the privacy bit clear. */ if (idx == wdev->wext.default_key && wdev->iftype == NL80211_IFTYPE_ADHOC) { __cfg80211_leave_ibss(rdev, wdev->netdev, true); rejoin = true; } if (!pairwise && addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) err = -ENOENT; else err = rdev->ops->del_key(&rdev->wiphy, dev, idx, pairwise, addr); } wdev->wext.connect.privacy = false; /* * Applications using wireless extensions expect to be * able to delete keys that don't exist, so allow that. */ if (err == -ENOENT) err = 0; if (!err) { if (!addr) { wdev->wext.keys->params[idx].key_len = 0; wdev->wext.keys->params[idx].cipher = 0; } if (idx == wdev->wext.default_key) wdev->wext.default_key = -1; else if (idx == wdev->wext.default_mgmt_key) wdev->wext.default_mgmt_key = -1; } if (!err && rejoin) err = cfg80211_ibss_wext_join(rdev, wdev); return err; } if (addr) tx_key = false; if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr)) return -EINVAL; err = 0; if (wdev->current_bss) err = rdev->ops->add_key(&rdev->wiphy, dev, idx, pairwise, addr, params); if (err) return err; if (!addr) { wdev->wext.keys->params[idx] = *params; memcpy(wdev->wext.keys->data[idx], params->key, params->key_len); wdev->wext.keys->params[idx].key = wdev->wext.keys->data[idx]; } if ((params->cipher == WLAN_CIPHER_SUITE_WEP40 || params->cipher == WLAN_CIPHER_SUITE_WEP104) && (tx_key || (!addr && wdev->wext.default_key == -1))) { if (wdev->current_bss) { /* * If we are getting a new TX key from not having * had one before we need to join a new IBSS with * the privacy bit set. */ if (wdev->iftype == NL80211_IFTYPE_ADHOC && wdev->wext.default_key == -1) { __cfg80211_leave_ibss(rdev, wdev->netdev, true); rejoin = true; } err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx, true, true); } if (!err) { wdev->wext.default_key = idx; if (rejoin) err = cfg80211_ibss_wext_join(rdev, wdev); } return err; } if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { if (wdev->current_bss) err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, dev, idx); if (!err) wdev->wext.default_mgmt_key = idx; return err; } return 0; } static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, struct net_device *dev, bool pairwise, const u8 *addr, bool remove, bool tx_key, int idx, struct key_params *params) { int err; /* devlist mutex needed for possible IBSS re-join */ mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_set_encryption(rdev, dev, pairwise, addr, remove, tx_key, idx, params); wdev_unlock(dev->ieee80211_ptr); mutex_unlock(&rdev->devlist_mtx); return err; } static int cfg80211_wext_siwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); int idx, err; bool remove = false; struct key_params params; if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || !rdev->ops->set_default_key) return -EOPNOTSUPP; idx = erq->flags & IW_ENCODE_INDEX; if (idx == 0) { idx = wdev->wext.default_key; if (idx < 0) idx = 0; } else if (idx < 1 || idx > 4) return -EINVAL; else idx--; if (erq->flags & IW_ENCODE_DISABLED) remove = true; else if (erq->length == 0) { /* No key data - just set the default TX key index */ err = 0; wdev_lock(wdev); if (wdev->current_bss) err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx, true, true); if (!err) wdev->wext.default_key = idx; wdev_unlock(wdev); return err; } memset(¶ms, 0, sizeof(params)); params.key = keybuf; params.key_len = erq->length; if (erq->length == 5) params.cipher = WLAN_CIPHER_SUITE_WEP40; else if (erq->length == 13) params.cipher = WLAN_CIPHER_SUITE_WEP104; else if (!remove) return -EINVAL; return cfg80211_set_encryption(rdev, dev, false, NULL, remove, wdev->wext.default_key == -1, idx, ¶ms); } static int cfg80211_wext_siwencodeext(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; const u8 *addr; int idx; bool remove = false; struct key_params params; u32 cipher; if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || !rdev->ops->set_default_key) return -EOPNOTSUPP; switch (ext->alg) { case IW_ENCODE_ALG_NONE: remove = true; cipher = 0; break; case IW_ENCODE_ALG_WEP: if (ext->key_len == 5) cipher = WLAN_CIPHER_SUITE_WEP40; else if (ext->key_len == 13) cipher = WLAN_CIPHER_SUITE_WEP104; else return -EINVAL; break; case IW_ENCODE_ALG_TKIP: cipher = WLAN_CIPHER_SUITE_TKIP; break; case IW_ENCODE_ALG_CCMP: cipher = WLAN_CIPHER_SUITE_CCMP; break; case IW_ENCODE_ALG_AES_CMAC: cipher = WLAN_CIPHER_SUITE_AES_CMAC; break; default: return -EOPNOTSUPP; } if (erq->flags & IW_ENCODE_DISABLED) remove = true; idx = erq->flags & IW_ENCODE_INDEX; if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) { if (idx < 4 || idx > 5) { idx = wdev->wext.default_mgmt_key; if (idx < 0) return -EINVAL; } else idx--; } else { if (idx < 1 || idx > 4) { idx = wdev->wext.default_key; if (idx < 0) return -EINVAL; } else idx--; } addr = ext->addr.sa_data; if (is_broadcast_ether_addr(addr)) addr = NULL; memset(¶ms, 0, sizeof(params)); params.key = ext->key; params.key_len = ext->key_len; params.cipher = cipher; if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { params.seq = ext->rx_seq; params.seq_len = 6; } return cfg80211_set_encryption( rdev, dev, !(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY), addr, remove, ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, idx, ¶ms); } static int cfg80211_wext_giwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct wireless_dev *wdev = dev->ieee80211_ptr; int idx; if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; idx = erq->flags & IW_ENCODE_INDEX; if (idx == 0) { idx = wdev->wext.default_key; if (idx < 0) idx = 0; } else if (idx < 1 || idx > 4) return -EINVAL; else idx--; erq->flags = idx + 1; if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) { erq->flags |= IW_ENCODE_DISABLED; erq->length = 0; return 0; } erq->length = min_t(size_t, erq->length, wdev->wext.keys->params[idx].key_len); memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length); erq->flags |= IW_ENCODE_ENABLED; return 0; } static int cfg80211_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); int freq, err; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); case NL80211_IFTYPE_MONITOR: freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); if (freq < 0) return freq; if (freq == 0) return -EINVAL; mutex_lock(&rdev->devlist_mtx); err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); mutex_unlock(&rdev->devlist_mtx); return err; case NL80211_IFTYPE_MESH_POINT: freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); if (freq < 0) return freq; if (freq == 0) return -EINVAL; mutex_lock(&rdev->devlist_mtx); err = cfg80211_set_mesh_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); mutex_unlock(&rdev->devlist_mtx); return err; default: return -EOPNOTSUPP; } } static int cfg80211_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct ieee80211_channel *chan; enum nl80211_channel_type channel_type; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); case NL80211_IFTYPE_MONITOR: if (!rdev->ops->get_channel) return -EINVAL; chan = rdev->ops->get_channel(wdev->wiphy, wdev, &channel_type); if (!chan) return -EINVAL; freq->m = chan->center_freq; freq->e = 6; return 0; default: return -EINVAL; } } static int cfg80211_wext_siwtxpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *data, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); enum nl80211_tx_power_setting type; int dbm = 0; if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL; if (data->txpower.flags & IW_TXPOW_RANGE) return -EINVAL; if (!rdev->ops->set_tx_power) return -EOPNOTSUPP; /* only change when not disabling */ if (!data->txpower.disabled) { rfkill_set_sw_state(rdev->rfkill, false); if (data->txpower.fixed) { /* * wext doesn't support negative values, see * below where it's for automatic */ if (data->txpower.value < 0) return -EINVAL; dbm = data->txpower.value; type = NL80211_TX_POWER_FIXED; /* TODO: do regulatory check! */ } else { /* * Automatic power level setting, max being the value * passed in from userland. */ if (data->txpower.value < 0) { type = NL80211_TX_POWER_AUTOMATIC; } else { dbm = data->txpower.value; type = NL80211_TX_POWER_LIMITED; } } } else { rfkill_set_sw_state(rdev->rfkill, true); schedule_work(&rdev->rfkill_sync); return 0; } return rdev->ops->set_tx_power(wdev->wiphy, type, DBM_TO_MBM(dbm)); } static int cfg80211_wext_giwtxpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *data, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); int err, val; if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL; if (data->txpower.flags & IW_TXPOW_RANGE) return -EINVAL; if (!rdev->ops->get_tx_power) return -EOPNOTSUPP; err = rdev->ops->get_tx_power(wdev->wiphy, &val); if (err) return err; /* well... oh well */ data->txpower.fixed = 1; data->txpower.disabled = rfkill_blocked(rdev->rfkill); data->txpower.value = val; data->txpower.flags = IW_TXPOW_DBM; return 0; } static int cfg80211_set_auth_alg(struct wireless_dev *wdev, s32 auth_alg) { int nr_alg = 0; if (!auth_alg) return -EINVAL; if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM | IW_AUTH_ALG_SHARED_KEY | IW_AUTH_ALG_LEAP)) return -EINVAL; if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) { nr_alg++; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; } if (auth_alg & IW_AUTH_ALG_SHARED_KEY) { nr_alg++; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY; } if (auth_alg & IW_AUTH_ALG_LEAP) { nr_alg++; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; } if (nr_alg > 1) wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; return 0; } static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) { if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | IW_AUTH_WPA_VERSION_WPA2| IW_AUTH_WPA_VERSION_DISABLED)) return -EINVAL; if ((wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) && (wpa_versions & (IW_AUTH_WPA_VERSION_WPA| IW_AUTH_WPA_VERSION_WPA2))) return -EINVAL; if (wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) wdev->wext.connect.crypto.wpa_versions &= ~(NL80211_WPA_VERSION_1|NL80211_WPA_VERSION_2); if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) wdev->wext.connect.crypto.wpa_versions |= NL80211_WPA_VERSION_1; if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2) wdev->wext.connect.crypto.wpa_versions |= NL80211_WPA_VERSION_2; return 0; } static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) { if (cipher & IW_AUTH_CIPHER_WEP40) wdev->wext.connect.crypto.cipher_group = WLAN_CIPHER_SUITE_WEP40; else if (cipher & IW_AUTH_CIPHER_WEP104) wdev->wext.connect.crypto.cipher_group = WLAN_CIPHER_SUITE_WEP104; else if (cipher & IW_AUTH_CIPHER_TKIP) wdev->wext.connect.crypto.cipher_group = WLAN_CIPHER_SUITE_TKIP; else if (cipher & IW_AUTH_CIPHER_CCMP) wdev->wext.connect.crypto.cipher_group = WLAN_CIPHER_SUITE_CCMP; else if (cipher & IW_AUTH_CIPHER_AES_CMAC) wdev->wext.connect.crypto.cipher_group = WLAN_CIPHER_SUITE_AES_CMAC; else if (cipher & IW_AUTH_CIPHER_NONE) wdev->wext.connect.crypto.cipher_group = 0; else return -EINVAL; return 0; } static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) { int nr_ciphers = 0; u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; if (cipher & IW_AUTH_CIPHER_WEP40) { ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40; nr_ciphers++; } if (cipher & IW_AUTH_CIPHER_WEP104) { ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104; nr_ciphers++; } if (cipher & IW_AUTH_CIPHER_TKIP) { ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP; nr_ciphers++; } if (cipher & IW_AUTH_CIPHER_CCMP) { ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP; nr_ciphers++; } if (cipher & IW_AUTH_CIPHER_AES_CMAC) { ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC; nr_ciphers++; } BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; return 0; } static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) { int nr_akm_suites = 0; if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | IW_AUTH_KEY_MGMT_PSK)) return -EINVAL; if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = WLAN_AKM_SUITE_8021X; nr_akm_suites++; } if (key_mgt & IW_AUTH_KEY_MGMT_PSK) { wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = WLAN_AKM_SUITE_PSK; nr_akm_suites++; } wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; return 0; } static int cfg80211_wext_siwauth(struct net_device *dev, struct iw_request_info *info, struct iw_param *data, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; if (wdev->iftype != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; switch (data->flags & IW_AUTH_INDEX) { case IW_AUTH_PRIVACY_INVOKED: wdev->wext.connect.privacy = data->value; return 0; case IW_AUTH_WPA_VERSION: return cfg80211_set_wpa_version(wdev, data->value); case IW_AUTH_CIPHER_GROUP: return cfg80211_set_cipher_group(wdev, data->value); case IW_AUTH_KEY_MGMT: return cfg80211_set_key_mgt(wdev, data->value); case IW_AUTH_CIPHER_PAIRWISE: return cfg80211_set_cipher_pairwise(wdev, data->value); case IW_AUTH_80211_AUTH_ALG: return cfg80211_set_auth_alg(wdev, data->value); case IW_AUTH_WPA_ENABLED: case IW_AUTH_RX_UNENCRYPTED_EAPOL: case IW_AUTH_DROP_UNENCRYPTED: case IW_AUTH_MFP: return 0; default: return -EOPNOTSUPP; } } static int cfg80211_wext_giwauth(struct net_device *dev, struct iw_request_info *info, struct iw_param *data, char *extra) { /* XXX: what do we need? */ return -EOPNOTSUPP; } static int cfg80211_wext_siwpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *wrq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); bool ps = wdev->ps; int timeout = wdev->ps_timeout; int err; if (wdev->iftype != NL80211_IFTYPE_STATION) return -EINVAL; if (!rdev->ops->set_power_mgmt) return -EOPNOTSUPP; if (wrq->disabled) { ps = false; } else { switch (wrq->flags & IW_POWER_MODE) { case IW_POWER_ON: /* If not specified */ case IW_POWER_MODE: /* If set all mask */ case IW_POWER_ALL_R: /* If explicitely state all */ ps = true; break; default: /* Otherwise we ignore */ return -EINVAL; } if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) return -EINVAL; if (wrq->flags & IW_POWER_TIMEOUT) timeout = wrq->value / 1000; } err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); if (err) return err; wdev->ps = ps; wdev->ps_timeout = timeout; return 0; } static int cfg80211_wext_giwpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *wrq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; wrq->disabled = !wdev->ps; return 0; } static int cfg80211_wds_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); int err; if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) return -EINVAL; if (addr->sa_family != ARPHRD_ETHER) return -EINVAL; if (netif_running(dev)) return -EBUSY; if (!rdev->ops->set_wds_peer) return -EOPNOTSUPP; err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); if (err) return err; memcpy(&wdev->wext.bssid, (u8 *) &addr->sa_data, ETH_ALEN); return 0; } static int cfg80211_wds_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) return -EINVAL; addr->sa_family = ARPHRD_ETHER; memcpy(&addr->sa_data, wdev->wext.bssid, ETH_ALEN); return 0; } static int cfg80211_wext_siwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rate, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_bitrate_mask mask; u32 fixed, maxrate; struct ieee80211_supported_band *sband; int band, ridx; bool match = false; if (!rdev->ops->set_bitrate_mask) return -EOPNOTSUPP; memset(&mask, 0, sizeof(mask)); fixed = 0; maxrate = (u32)-1; if (rate->value < 0) { /* nothing */ } else if (rate->fixed) { fixed = rate->value / 100000; } else { maxrate = rate->value / 100000; } for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wdev->wiphy->bands[band]; if (sband == NULL) continue; for (ridx = 0; ridx < sband->n_bitrates; ridx++) { struct ieee80211_rate *srate = &sband->bitrates[ridx]; if (fixed == srate->bitrate) { mask.control[band].legacy = 1 << ridx; match = true; break; } if (srate->bitrate <= maxrate) { mask.control[band].legacy |= 1 << ridx; match = true; } } } if (!match) return -EINVAL; return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); } static int cfg80211_wext_giwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rate, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); /* we are under RTNL - globally locked - so can use a static struct */ static struct station_info sinfo; u8 addr[ETH_ALEN]; int err; if (wdev->iftype != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; if (!rdev->ops->get_station) return -EOPNOTSUPP; err = 0; wdev_lock(wdev); if (wdev->current_bss) memcpy(addr, wdev->current_bss->pub.bssid, ETH_ALEN); else err = -EOPNOTSUPP; wdev_unlock(wdev); if (err) return err; err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); if (err) return err; if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) return -EOPNOTSUPP; rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); return 0; } /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); /* we are under RTNL - globally locked - so can use static structs */ static struct iw_statistics wstats; static struct station_info sinfo; u8 bssid[ETH_ALEN]; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) return NULL; if (!rdev->ops->get_station) return NULL; /* Grab BSSID of current BSS, if any */ wdev_lock(wdev); if (!wdev->current_bss) { wdev_unlock(wdev); return NULL; } memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); wdev_unlock(wdev); if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo)) return NULL; memset(&wstats, 0, sizeof(wstats)); switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: if (sinfo.filled & STATION_INFO_SIGNAL) { int sig = sinfo.signal; wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; wstats.qual.updated |= IW_QUAL_DBM; wstats.qual.level = sig; if (sig < -110) sig = -110; else if (sig > -40) sig = -40; wstats.qual.qual = sig + 110; break; } case CFG80211_SIGNAL_TYPE_UNSPEC: if (sinfo.filled & STATION_INFO_SIGNAL) { wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; wstats.qual.level = sinfo.signal; wstats.qual.qual = sinfo.signal; break; } default: wstats.qual.updated |= IW_QUAL_LEVEL_INVALID; wstats.qual.updated |= IW_QUAL_QUAL_INVALID; } wstats.qual.updated |= IW_QUAL_NOISE_INVALID; if (sinfo.filled & STATION_INFO_RX_DROP_MISC) wstats.discard.misc = sinfo.rx_dropped_misc; if (sinfo.filled & STATION_INFO_TX_FAILED) wstats.discard.retries = sinfo.tx_failed; return &wstats; } static int cfg80211_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); case NL80211_IFTYPE_STATION: return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); case NL80211_IFTYPE_WDS: return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); default: return -EOPNOTSUPP; } } static int cfg80211_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); case NL80211_IFTYPE_STATION: return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); case NL80211_IFTYPE_WDS: return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); default: return -EOPNOTSUPP; } } static int cfg80211_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); case NL80211_IFTYPE_STATION: return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); default: return -EOPNOTSUPP; } } static int cfg80211_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; data->flags = 0; data->length = 0; switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); case NL80211_IFTYPE_STATION: return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); default: return -EOPNOTSUPP; } } static int cfg80211_wext_siwpmksa(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_pmksa cfg_pmksa; struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa)); if (wdev->iftype != NL80211_IFTYPE_STATION) return -EINVAL; cfg_pmksa.bssid = pmksa->bssid.sa_data; cfg_pmksa.pmkid = pmksa->pmkid; switch (pmksa->cmd) { case IW_PMKSA_ADD: if (!rdev->ops->set_pmksa) return -EOPNOTSUPP; return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa); case IW_PMKSA_REMOVE: if (!rdev->ops->del_pmksa) return -EOPNOTSUPP; return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa); case IW_PMKSA_FLUSH: if (!rdev->ops->flush_pmksa) return -EOPNOTSUPP; return rdev->ops->flush_pmksa(&rdev->wiphy, dev); default: return -EOPNOTSUPP; } } static const iw_handler cfg80211_handlers[] = { [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname, [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq, [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq, [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode, [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode, [IW_IOCTL_IDX(SIOCGIWRANGE)] = (iw_handler) cfg80211_wext_giwrange, [IW_IOCTL_IDX(SIOCSIWAP)] = (iw_handler) cfg80211_wext_siwap, [IW_IOCTL_IDX(SIOCGIWAP)] = (iw_handler) cfg80211_wext_giwap, [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme, [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan, [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan, [IW_IOCTL_IDX(SIOCSIWESSID)] = (iw_handler) cfg80211_wext_siwessid, [IW_IOCTL_IDX(SIOCGIWESSID)] = (iw_handler) cfg80211_wext_giwessid, [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate, [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate, [IW_IOCTL_IDX(SIOCSIWRTS)] = (iw_handler) cfg80211_wext_siwrts, [IW_IOCTL_IDX(SIOCGIWRTS)] = (iw_handler) cfg80211_wext_giwrts, [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag, [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag, [IW_IOCTL_IDX(SIOCSIWTXPOW)] = (iw_handler) cfg80211_wext_siwtxpower, [IW_IOCTL_IDX(SIOCGIWTXPOW)] = (iw_handler) cfg80211_wext_giwtxpower, [IW_IOCTL_IDX(SIOCSIWRETRY)] = (iw_handler) cfg80211_wext_siwretry, [IW_IOCTL_IDX(SIOCGIWRETRY)] = (iw_handler) cfg80211_wext_giwretry, [IW_IOCTL_IDX(SIOCSIWENCODE)] = (iw_handler) cfg80211_wext_siwencode, [IW_IOCTL_IDX(SIOCGIWENCODE)] = (iw_handler) cfg80211_wext_giwencode, [IW_IOCTL_IDX(SIOCSIWPOWER)] = (iw_handler) cfg80211_wext_siwpower, [IW_IOCTL_IDX(SIOCGIWPOWER)] = (iw_handler) cfg80211_wext_giwpower, [IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie, [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth, [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth, [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext, [IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa, }; const struct iw_handler_def cfg80211_wext_handler = { .num_standard = ARRAY_SIZE(cfg80211_handlers), .standard = cfg80211_handlers, .get_wireless_stats = cfg80211_wireless_stats, }; compat-drivers-2012-09-18/net/wireless/wext-compat.h0000644000175000017500000000416712026211315021434 0ustar mcgrofmcgrof#ifndef __WEXT_COMPAT #define __WEXT_COMPAT #include #include int cfg80211_ibss_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); int cfg80211_ibss_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); int cfg80211_ibss_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); int cfg80211_ibss_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); int cfg80211_ibss_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); int cfg80211_ibss_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); int cfg80211_mgd_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); int cfg80211_mgd_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); int cfg80211_mgd_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); int cfg80211_mgd_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); int cfg80211_mgd_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); int cfg80211_mgd_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); int cfg80211_wext_siwmlme(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra); int cfg80211_wext_siwgenie(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra); int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); extern const struct iw_handler_def cfg80211_wext_handler; #endif /* __WEXT_COMPAT */ compat-drivers-2012-09-18/net/wireless/lib80211_crypt_ccmp.c0000644000175000017500000002720612026211315022543 0ustar mcgrofmcgrof/* * lib80211 crypt: host-based CCMP encryption implementation for lib80211 * * Copyright (c) 2003-2004, Jouni Malinen * Copyright (c) 2008, John W. Linville * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Jouni Malinen"); MODULE_DESCRIPTION("Host AP crypt: CCMP"); MODULE_LICENSE("GPL"); #define AES_BLOCK_LEN 16 #define CCMP_HDR_LEN 8 #define CCMP_MIC_LEN 8 #define CCMP_TK_LEN 16 #define CCMP_PN_LEN 6 struct lib80211_ccmp_data { u8 key[CCMP_TK_LEN]; int key_set; u8 tx_pn[CCMP_PN_LEN]; u8 rx_pn[CCMP_PN_LEN]; u32 dot11RSNAStatsCCMPFormatErrors; u32 dot11RSNAStatsCCMPReplays; u32 dot11RSNAStatsCCMPDecryptErrors; int key_idx; struct crypto_cipher *tfm; /* scratch buffers for virt_to_page() (crypto API) */ u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; }; static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm, const u8 pt[16], u8 ct[16]) { crypto_cipher_encrypt_one(tfm, ct, pt); } static void *lib80211_ccmp_init(int key_idx) { struct lib80211_ccmp_data *priv; priv = kzalloc(sizeof(*priv), GFP_ATOMIC); if (priv == NULL) goto fail; priv->key_idx = key_idx; priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tfm)) { priv->tfm = NULL; goto fail; } return priv; fail: if (priv) { if (priv->tfm) crypto_free_cipher(priv->tfm); kfree(priv); } return NULL; } static void lib80211_ccmp_deinit(void *priv) { struct lib80211_ccmp_data *_priv = priv; if (_priv && _priv->tfm) crypto_free_cipher(_priv->tfm); kfree(priv); } static inline void xor_block(u8 * b, u8 * a, size_t len) { int i; for (i = 0; i < len; i++) b[i] ^= a[i]; } static void ccmp_init_blocks(struct crypto_cipher *tfm, struct ieee80211_hdr *hdr, u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0) { u8 *pos, qc = 0; size_t aad_len; int a4_included, qc_included; u8 aad[2 * AES_BLOCK_LEN]; a4_included = ieee80211_has_a4(hdr->frame_control); qc_included = ieee80211_is_data_qos(hdr->frame_control); aad_len = 22; if (a4_included) aad_len += 6; if (qc_included) { pos = (u8 *) & hdr->addr4; if (a4_included) pos += 6; qc = *pos & 0x0f; aad_len += 2; } /* CCM Initial Block: * Flag (Include authentication header, M=3 (8-octet MIC), * L=1 (2-octet Dlen)) * Nonce: 0x00 | A2 | PN * Dlen */ b0[0] = 0x59; b0[1] = qc; memcpy(b0 + 2, hdr->addr2, ETH_ALEN); memcpy(b0 + 8, pn, CCMP_PN_LEN); b0[14] = (dlen >> 8) & 0xff; b0[15] = dlen & 0xff; /* AAD: * FC with bits 4..6 and 11..13 masked to zero; 14 is always one * A1 | A2 | A3 * SC with bits 4..15 (seq#) masked to zero * A4 (if present) * QC (if present) */ pos = (u8 *) hdr; aad[0] = 0; /* aad_len >> 8 */ aad[1] = aad_len & 0xff; aad[2] = pos[0] & 0x8f; aad[3] = pos[1] & 0xc7; memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); pos = (u8 *) & hdr->seq_ctrl; aad[22] = pos[0] & 0x0f; aad[23] = 0; /* all bits masked */ memset(aad + 24, 0, 8); if (a4_included) memcpy(aad + 24, hdr->addr4, ETH_ALEN); if (qc_included) { aad[a4_included ? 30 : 24] = qc; /* rest of QC masked */ } /* Start with the first block and AAD */ lib80211_ccmp_aes_encrypt(tfm, b0, auth); xor_block(auth, aad, AES_BLOCK_LEN); lib80211_ccmp_aes_encrypt(tfm, auth, auth); xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); lib80211_ccmp_aes_encrypt(tfm, auth, auth); b0[0] &= 0x07; b0[14] = b0[15] = 0; lib80211_ccmp_aes_encrypt(tfm, b0, s0); } static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, u8 *aeskey, int keylen, void *priv) { struct lib80211_ccmp_data *key = priv; int i; u8 *pos; if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len) return -1; if (aeskey != NULL && keylen >= CCMP_TK_LEN) memcpy(aeskey, key->key, CCMP_TK_LEN); pos = skb_push(skb, CCMP_HDR_LEN); memmove(pos, pos + CCMP_HDR_LEN, hdr_len); pos += hdr_len; i = CCMP_PN_LEN - 1; while (i >= 0) { key->tx_pn[i]++; if (key->tx_pn[i] != 0) break; i--; } *pos++ = key->tx_pn[5]; *pos++ = key->tx_pn[4]; *pos++ = 0; *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ; *pos++ = key->tx_pn[3]; *pos++ = key->tx_pn[2]; *pos++ = key->tx_pn[1]; *pos++ = key->tx_pn[0]; return CCMP_HDR_LEN; } static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_ccmp_data *key = priv; int data_len, i, blocks, last, len; u8 *pos, *mic; struct ieee80211_hdr *hdr; u8 *b0 = key->tx_b0; u8 *b = key->tx_b; u8 *e = key->tx_e; u8 *s0 = key->tx_s0; if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len) return -1; data_len = skb->len - hdr_len; len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv); if (len < 0) return -1; pos = skb->data + hdr_len + CCMP_HDR_LEN; hdr = (struct ieee80211_hdr *)skb->data; ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); last = data_len % AES_BLOCK_LEN; for (i = 1; i <= blocks; i++) { len = (i == blocks && last) ? last : AES_BLOCK_LEN; /* Authentication */ xor_block(b, pos, len); lib80211_ccmp_aes_encrypt(key->tfm, b, b); /* Encryption, with counter */ b0[14] = (i >> 8) & 0xff; b0[15] = i & 0xff; lib80211_ccmp_aes_encrypt(key->tfm, b0, e); xor_block(pos, e, len); pos += len; } mic = skb_put(skb, CCMP_MIC_LEN); for (i = 0; i < CCMP_MIC_LEN; i++) mic[i] = b[i] ^ s0[i]; return 0; } /* * deal with seq counter wrapping correctly. * refer to timer_after() for jiffies wrapping handling */ static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o) { u32 iv32_n, iv16_n; u32 iv32_o, iv16_o; iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3]; iv16_n = (pn_n[4] << 8) | pn_n[5]; iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3]; iv16_o = (pn_o[4] << 8) | pn_o[5]; if ((s32)iv32_n - (s32)iv32_o < 0 || (iv32_n == iv32_o && iv16_n <= iv16_o)) return 1; return 0; } static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct lib80211_ccmp_data *key = priv; u8 keyidx, *pos; struct ieee80211_hdr *hdr; u8 *b0 = key->rx_b0; u8 *b = key->rx_b; u8 *a = key->rx_a; u8 pn[6]; int i, blocks, last, len; size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { key->dot11RSNAStatsCCMPFormatErrors++; return -1; } hdr = (struct ieee80211_hdr *)skb->data; pos = skb->data + hdr_len; keyidx = pos[3]; if (!(keyidx & (1 << 5))) { net_dbg_ratelimited("CCMP: received packet without ExtIV flag from %pM\n", hdr->addr2); key->dot11RSNAStatsCCMPFormatErrors++; return -2; } keyidx >>= 6; if (key->key_idx != keyidx) { printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); return -6; } if (!key->key_set) { net_dbg_ratelimited("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n", hdr->addr2, keyidx); return -3; } pn[0] = pos[7]; pn[1] = pos[6]; pn[2] = pos[5]; pn[3] = pos[4]; pn[4] = pos[1]; pn[5] = pos[0]; pos += 8; if (ccmp_replay_check(pn, key->rx_pn)) { #ifdef CONFIG_LIB80211_DEBUG net_dbg_ratelimited("CCMP: replay detected: STA=%pM previous PN %02x%02x%02x%02x%02x%02x received PN %02x%02x%02x%02x%02x%02x\n", hdr->addr2, key->rx_pn[0], key->rx_pn[1], key->rx_pn[2], key->rx_pn[3], key->rx_pn[4], key->rx_pn[5], pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); #endif key->dot11RSNAStatsCCMPReplays++; return -4; } ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); xor_block(mic, b, CCMP_MIC_LEN); blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); last = data_len % AES_BLOCK_LEN; for (i = 1; i <= blocks; i++) { len = (i == blocks && last) ? last : AES_BLOCK_LEN; /* Decrypt, with counter */ b0[14] = (i >> 8) & 0xff; b0[15] = i & 0xff; lib80211_ccmp_aes_encrypt(key->tfm, b0, b); xor_block(pos, b, len); /* Authentication */ xor_block(a, pos, len); lib80211_ccmp_aes_encrypt(key->tfm, a, a); pos += len; } if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM\n", hdr->addr2); key->dot11RSNAStatsCCMPDecryptErrors++; return -5; } memcpy(key->rx_pn, pn, CCMP_PN_LEN); /* Remove hdr and MIC */ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); skb_pull(skb, CCMP_HDR_LEN); skb_trim(skb, skb->len - CCMP_MIC_LEN); return keyidx; } static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) { struct lib80211_ccmp_data *data = priv; int keyidx; struct crypto_cipher *tfm = data->tfm; keyidx = data->key_idx; memset(data, 0, sizeof(*data)); data->key_idx = keyidx; data->tfm = tfm; if (len == CCMP_TK_LEN) { memcpy(data->key, key, CCMP_TK_LEN); data->key_set = 1; if (seq) { data->rx_pn[0] = seq[5]; data->rx_pn[1] = seq[4]; data->rx_pn[2] = seq[3]; data->rx_pn[3] = seq[2]; data->rx_pn[4] = seq[1]; data->rx_pn[5] = seq[0]; } crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); } else if (len == 0) data->key_set = 0; else return -1; return 0; } static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv) { struct lib80211_ccmp_data *data = priv; if (len < CCMP_TK_LEN) return -1; if (!data->key_set) return 0; memcpy(key, data->key, CCMP_TK_LEN); if (seq) { seq[0] = data->tx_pn[5]; seq[1] = data->tx_pn[4]; seq[2] = data->tx_pn[3]; seq[3] = data->tx_pn[2]; seq[4] = data->tx_pn[1]; seq[5] = data->tx_pn[0]; } return CCMP_TK_LEN; } static char *lib80211_ccmp_print_stats(char *p, void *priv) { struct lib80211_ccmp_data *ccmp = priv; p += sprintf(p, "key[%d] alg=CCMP key_set=%d " "tx_pn=%02x%02x%02x%02x%02x%02x " "rx_pn=%02x%02x%02x%02x%02x%02x " "format_errors=%d replays=%d decrypt_errors=%d\n", ccmp->key_idx, ccmp->key_set, ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2], ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5], ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2], ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5], ccmp->dot11RSNAStatsCCMPFormatErrors, ccmp->dot11RSNAStatsCCMPReplays, ccmp->dot11RSNAStatsCCMPDecryptErrors); return p; } static struct lib80211_crypto_ops lib80211_crypt_ccmp = { .name = "CCMP", .init = lib80211_ccmp_init, .deinit = lib80211_ccmp_deinit, .encrypt_mpdu = lib80211_ccmp_encrypt, .decrypt_mpdu = lib80211_ccmp_decrypt, .encrypt_msdu = NULL, .decrypt_msdu = NULL, .set_key = lib80211_ccmp_set_key, .get_key = lib80211_ccmp_get_key, .print_stats = lib80211_ccmp_print_stats, .extra_mpdu_prefix_len = CCMP_HDR_LEN, .extra_mpdu_postfix_len = CCMP_MIC_LEN, .owner = THIS_MODULE, }; static int __init lib80211_crypto_ccmp_init(void) { return lib80211_register_crypto_ops(&lib80211_crypt_ccmp); } static void __exit lib80211_crypto_ccmp_exit(void) { lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp); } module_init(lib80211_crypto_ccmp_init); module_exit(lib80211_crypto_ccmp_exit); compat-drivers-2012-09-18/net/wireless/wext-priv.c0000644000175000017500000001556312026211315021126 0ustar mcgrofmcgrof/* * This file implement the Wireless Extensions priv API. * * Authors : Jean Tourrilhes - HPL - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. * Copyright 2009 Johannes Berg * * (As all part of the Linux kernel, this file is GPL) */ #include #include #include #include #include int iw_handler_get_private(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra) { /* Check if the driver has something to export */ if ((dev->wireless_handlers->num_private_args == 0) || (dev->wireless_handlers->private_args == NULL)) return -EOPNOTSUPP; /* Check if there is enough buffer up there */ if (wrqu->data.length < dev->wireless_handlers->num_private_args) { /* User space can't know in advance how large the buffer * needs to be. Give it a hint, so that we can support * any size buffer we want somewhat efficiently... */ wrqu->data.length = dev->wireless_handlers->num_private_args; return -E2BIG; } /* Set the number of available ioctls. */ wrqu->data.length = dev->wireless_handlers->num_private_args; /* Copy structure to the user buffer. */ memcpy(extra, dev->wireless_handlers->private_args, sizeof(struct iw_priv_args) * wrqu->data.length); return 0; } /* Size (in bytes) of the various private data types */ static const char iw_priv_type_size[] = { 0, /* IW_PRIV_TYPE_NONE */ 1, /* IW_PRIV_TYPE_BYTE */ 1, /* IW_PRIV_TYPE_CHAR */ 0, /* Not defined */ sizeof(__u32), /* IW_PRIV_TYPE_INT */ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ 0, /* Not defined */ }; static int get_priv_size(__u16 args) { int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; return num * iw_priv_type_size[type]; } static int adjust_priv_size(__u16 args, struct iw_point *iwp) { int num = iwp->length; int max = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; /* Make sure the driver doesn't goof up */ if (max < num) num = max; return num * iw_priv_type_size[type]; } /* * Wrapper to call a private Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. * It's not as nice and slimline as the standard wrapper. The cause * is struct iw_priv_args, which was not really designed for the * job we are going here. * * IMPORTANT : This function prevent to set and get data on the same * IOCTL and enforce the SET/GET convention. Not doing it would be * far too hairy... * If you need to set and get data at the same time, please don't use * a iw_handler but process it in your ioctl handler (i.e. use the * old driver API). */ static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, const struct iw_priv_args **descrp) { const struct iw_priv_args *descr; int i, extra_size; descr = NULL; for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { if (cmd == dev->wireless_handlers->private_args[i].cmd) { descr = &dev->wireless_handlers->private_args[i]; break; } } extra_size = 0; if (descr) { if (IW_IS_SET(cmd)) { int offset = 0; /* For sub-ioctls */ /* Check for sub-ioctl handler */ if (descr->name[0] == '\0') /* Reserve one int for sub-ioctl index */ offset = sizeof(__u32); /* Size of set arguments */ extra_size = get_priv_size(descr->set_args); /* Does it fits in iwr ? */ if ((descr->set_args & IW_PRIV_SIZE_FIXED) && ((extra_size + offset) <= IFNAMSIZ)) extra_size = 0; } else { /* Size of get arguments */ extra_size = get_priv_size(descr->get_args); /* Does it fits in iwr ? */ if ((descr->get_args & IW_PRIV_SIZE_FIXED) && (extra_size <= IFNAMSIZ)) extra_size = 0; } } *descrp = descr; return extra_size; } static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, const struct iw_priv_args *descr, iw_handler handler, struct net_device *dev, struct iw_request_info *info, int extra_size) { char *extra; int err; /* Check what user space is giving us */ if (IW_IS_SET(cmd)) { if (!iwp->pointer && iwp->length != 0) return -EFAULT; if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) return -E2BIG; } else if (!iwp->pointer) return -EFAULT; extra = kzalloc(extra_size, GFP_KERNEL); if (!extra) return -ENOMEM; /* If it is a SET, get all the extra data in here */ if (IW_IS_SET(cmd) && (iwp->length != 0)) { if (copy_from_user(extra, iwp->pointer, extra_size)) { err = -EFAULT; goto out; } } /* Call the handler */ err = handler(dev, info, (union iwreq_data *) iwp, extra); /* If we have something to return to the user */ if (!err && IW_IS_GET(cmd)) { /* Adjust for the actual length if it's variable, * avoid leaking kernel bits outside. */ if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) extra_size = adjust_priv_size(descr->get_args, iwp); if (copy_to_user(iwp->pointer, extra, extra_size)) err = -EFAULT; } out: kfree(extra); return err; } int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, unsigned int cmd, struct iw_request_info *info, iw_handler handler) { int extra_size = 0, ret = -EINVAL; const struct iw_priv_args *descr; extra_size = get_priv_descr_and_size(dev, cmd, &descr); /* Check if we have a pointer to user space data or not. */ if (extra_size == 0) { /* No extra arguments. Trivial to handle */ ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); } else { ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, handler, dev, info, extra_size); } /* Call commit handler if needed and defined */ if (ret == -EIWCOMMIT) ret = call_commit_handler(dev); return ret; } #ifdef CONFIG_COMPAT int compat_private_call(struct net_device *dev, struct iwreq *iwr, unsigned int cmd, struct iw_request_info *info, iw_handler handler) { const struct iw_priv_args *descr; int ret, extra_size; extra_size = get_priv_descr_and_size(dev, cmd, &descr); /* Check if we have a pointer to user space data or not. */ if (extra_size == 0) { /* No extra arguments. Trivial to handle */ ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); } else { struct compat_iw_point *iwp_compat; struct iw_point iwp; iwp_compat = (struct compat_iw_point *) &iwr->u.data; iwp.pointer = compat_ptr(iwp_compat->pointer); iwp.length = iwp_compat->length; iwp.flags = iwp_compat->flags; ret = ioctl_private_iw_point(&iwp, cmd, descr, handler, dev, info, extra_size); iwp_compat->pointer = ptr_to_compat(iwp.pointer); iwp_compat->length = iwp.length; iwp_compat->flags = iwp.flags; } /* Call commit handler if needed and defined */ if (ret == -EIWCOMMIT) ret = call_commit_handler(dev); return ret; } #endif compat-drivers-2012-09-18/net/wireless/ethtool.h0000644000175000017500000000022412026211315020630 0ustar mcgrofmcgrof#ifndef __CFG80211_ETHTOOL__ #define __CFG80211_ETHTOOL__ extern const struct ethtool_ops cfg80211_ethtool_ops; #endif /* __CFG80211_ETHTOOL__ */ compat-drivers-2012-09-18/net/wireless/wext-sme.c0000644000175000017500000002276412026211315020733 0ustar mcgrofmcgrof/* * cfg80211 wext compat for managed mode. * * Copyright 2009 Johannes Berg * Copyright (C) 2009 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include "wext-compat.h" #include "nl80211.h" int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct cfg80211_cached_keys *ck = NULL; const u8 *prev_bssid = NULL; int err, i; ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); if (!netif_running(wdev->netdev)) return 0; wdev->wext.connect.ie = wdev->wext.ie; wdev->wext.connect.ie_len = wdev->wext.ie_len; /* Use default background scan period */ wdev->wext.connect.bg_scan_period = -1; if (wdev->wext.keys) { wdev->wext.keys->def = wdev->wext.default_key; wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; if (wdev->wext.default_key != -1) wdev->wext.connect.privacy = true; } if (!wdev->wext.connect.ssid_len) return 0; if (wdev->wext.keys) { ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); if (!ck) return -ENOMEM; for (i = 0; i < 6; i++) ck->params[i].key = ck->data[i]; } if (wdev->wext.prev_bssid_valid) prev_bssid = wdev->wext.prev_bssid; err = __cfg80211_connect(rdev, wdev->netdev, &wdev->wext.connect, ck, prev_bssid); if (err) kfree(ck); return err; } int cfg80211_mgd_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct ieee80211_channel *chan = NULL; int err, freq; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); if (freq < 0) return freq; if (freq) { chan = ieee80211_get_channel(wdev->wiphy, freq); if (!chan) return -EINVAL; if (chan->flags & IEEE80211_CHAN_DISABLED) return -EINVAL; } cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; if (wdev->wext.connect.channel == chan) { err = 0; goto out; } /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) event = false; err = __cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } wdev->wext.connect.channel = chan; /* * SSID is not set, we just want to switch monitor channel, * this is really just backward compatibility, if the SSID * is set then we use the channel to select the BSS to use * to connect to instead. If we were connected on another * channel we disconnected above and reconnect below. */ if (chan && !wdev->wext.connect.ssid_len) { err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); goto out; } err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); return err; } int cfg80211_mgd_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_channel *chan = NULL; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; else if (wdev->wext.connect.channel) chan = wdev->wext.connect.channel; wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; freq->e = 6; return 0; } /* no channel if not joining */ return -EINVAL; } int cfg80211_mgd_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); size_t len = data->length; int err; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; if (!data->flags) len = 0; /* iwconfig uses nul termination in SSID.. */ if (len > 0 && ssid[len - 1] == '\0') len--; cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = 0; if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; if (wdev->wext.connect.ssid && len && len == wdev->wext.connect.ssid_len && memcmp(wdev->wext.connect.ssid, ssid, len) == 0) goto out; /* if SSID set now, we'll try to connect, avoid event */ if (len) event = false; err = __cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } wdev->wext.prev_bssid_valid = false; wdev->wext.connect.ssid = wdev->wext.ssid; memcpy(wdev->wext.ssid, ssid, len); wdev->wext.connect.ssid_len = len; wdev->wext.connect.crypto.control_port = false; wdev->wext.connect.crypto.control_port_ethertype = cpu_to_be16(ETH_P_PAE); err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); return err; } int cfg80211_mgd_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; data->flags = 0; wdev_lock(wdev); if (wdev->current_bss) { const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, WLAN_EID_SSID); if (ie) { data->flags = 1; data->length = ie[1]; memcpy(ssid, ie + 2, data->length); } } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { data->flags = 1; data->length = wdev->wext.connect.ssid_len; memcpy(ssid, wdev->wext.connect.ssid, data->length); } wdev_unlock(wdev); return 0; } int cfg80211_mgd_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 *bssid = ap_addr->sa_data; int err; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; if (ap_addr->sa_family != ARPHRD_ETHER) return -EINVAL; /* automatic mode */ if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { err = 0; /* both automatic */ if (!bssid && !wdev->wext.connect.bssid) goto out; /* fixed already - and no change */ if (wdev->wext.connect.bssid && bssid && ether_addr_equal(bssid, wdev->wext.connect.bssid)) goto out; err = __cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, false); if (err) goto out; } if (bssid) { memcpy(wdev->wext.bssid, bssid, ETH_ALEN); wdev->wext.connect.bssid = wdev->wext.bssid; } else wdev->wext.connect.bssid = NULL; err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); return err; } int cfg80211_mgd_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; ap_addr->sa_family = ARPHRD_ETHER; wdev_lock(wdev); if (wdev->current_bss) memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else memset(ap_addr->sa_data, 0, ETH_ALEN); wdev_unlock(wdev); return 0; } int cfg80211_wext_siwgenie(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 *ie = extra; int ie_len = data->length, err; if (wdev->iftype != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; if (!ie_len) ie = NULL; wdev_lock(wdev); /* no change */ err = 0; if (wdev->wext.ie_len == ie_len && memcmp(wdev->wext.ie, ie, ie_len) == 0) goto out; if (ie_len) { ie = kmemdup(extra, ie_len, GFP_KERNEL); if (!ie) { err = -ENOMEM; goto out; } } else ie = NULL; kfree(wdev->wext.ie); wdev->wext.ie = ie; wdev->wext.ie_len = ie_len; if (wdev->sme_state != CFG80211_SME_IDLE) { err = __cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, false); if (err) goto out; } /* userspace better not think we'll reconnect */ err = 0; out: wdev_unlock(wdev); return err; } int cfg80211_wext_siwmlme(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_mlme *mlme = (struct iw_mlme *)extra; struct cfg80211_registered_device *rdev; int err; if (!wdev) return -EOPNOTSUPP; rdev = wiphy_to_dev(wdev->wiphy); if (wdev->iftype != NL80211_IFTYPE_STATION) return -EINVAL; if (mlme->addr.sa_family != ARPHRD_ETHER) return -EINVAL; wdev_lock(wdev); switch (mlme->cmd) { case IW_MLME_DEAUTH: case IW_MLME_DISASSOC: err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, true); break; default: err = -EOPNOTSUPP; break; } wdev_unlock(wdev); return err; } compat-drivers-2012-09-18/net/wireless/genregdb.awk0000644000175000017500000000660612026211315021274 0ustar mcgrofmcgrof#!/usr/bin/awk -f # # genregdb.awk -- generate regdb.c from db.txt # # Actually, it reads from stdin (presumed to be db.txt) and writes # to stdout (presumed to be regdb.c), but close enough... # # Copyright 2009 John W. Linville # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. BEGIN { active = 0 rules = 0; print "/*" print " * DO NOT EDIT -- file generated from data in db.txt" print " */" print "" print "#include " print "#include " print "#include \"regdb.h\"" print "" regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" } /^[ \t]*#/ { # Ignore } !active && /^[ \t]*$/ { # Ignore } !active && /country/ { country=$2 sub(/:/, "", country) printf "static const struct ieee80211_regdomain regdom_%s = {\n", country printf "\t.alpha2 = \"%s\",\n", country printf "\t.reg_rules = {\n" active = 1 regdb = regdb "\t®dom_" country ",\n" } active && /^[ \t]*\(/ { start = $1 sub(/\(/, "", start) end = $3 bw = $5 sub(/\),/, "", bw) gain = $6 sub(/\(/, "", gain) sub(/,/, "", gain) power = $7 sub(/\)/, "", power) sub(/,/, "", power) # power might be in mW... units = $8 sub(/\)/, "", units) sub(/,/, "", units) if (units == "mW") { if (power == 100) { power = 20 } else if (power == 200) { power = 23 } else if (power == 500) { power = 27 } else if (power == 1000) { power = 30 } else { print "Unknown power value in database!" } } flagstr = "" for (i=8; i<=NF; i++) flagstr = flagstr $i split(flagstr, flagarray, ",") flags = "" for (arg in flagarray) { if (flagarray[arg] == "NO-OFDM") { flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | " } else if (flagarray[arg] == "NO-CCK") { flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | " } else if (flagarray[arg] == "NO-INDOOR") { flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | " } else if (flagarray[arg] == "NO-OUTDOOR") { flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | " } else if (flagarray[arg] == "DFS") { flags = flags "\n\t\t\tNL80211_RRF_DFS | " } else if (flagarray[arg] == "PTP-ONLY") { flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | " } else if (flagarray[arg] == "PTMP-ONLY") { flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " } else if (flagarray[arg] == "PASSIVE-SCAN") { flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " } else if (flagarray[arg] == "NO-IBSS") { flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " } } flags = flags "0" printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags rules++ } active && /^[ \t]*$/ { active = 0 printf "\t},\n" printf "\t.n_reg_rules = %d\n", rules printf "};\n\n" rules = 0; } END { print regdb "};" print "" print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" } compat-drivers-2012-09-18/net/wireless/Kconfig0000644000175000017500000001124612026211315020312 0ustar mcgrofmcgrofconfig WIRELESS_EXT bool config WEXT_CORE def_bool y depends on CFG80211_WEXT || WIRELESS_EXT config WEXT_PROC def_bool y depends on PROC_FS depends on WEXT_CORE config WEXT_SPY bool config WEXT_PRIV bool config CFG80211 tristate "cfg80211 - wireless configuration API" depends on RFKILL || !RFKILL ---help--- cfg80211 is the Linux wireless LAN (802.11) configuration API. Enable this if you have a wireless device. For more information refer to documentation on the wireless wiki: http://wireless.kernel.org/en/developers/Documentation/cfg80211 When built as a module it will be called cfg80211. config NL80211_TESTMODE bool "nl80211 testmode command" depends on CFG80211 help The nl80211 testmode command helps implementing things like factory calibration or validation tools for wireless chips. Select this option ONLY for kernels that are specifically built for such purposes. Debugging tools that are supposed to end up in the hands of users should better be implemented with debugfs. Say N. config CFG80211_DEVELOPER_WARNINGS bool "enable developer warnings" depends on CFG80211 default n help This option enables some additional warnings that help cfg80211 developers and driver developers, but that can trigger due to races with userspace. For example, when a driver reports that it was disconnected from the AP, but the user disconnects manually at the same time, the warning might trigger spuriously due to races. Say Y only if you are developing cfg80211 or a driver based on it (or mac80211). config CFG80211_REG_DEBUG bool "cfg80211 regulatory debugging" depends on CFG80211 default n ---help--- You can enable this if you want to debug regulatory changes. For more information on cfg80211 regulatory refer to the wireless wiki: http://wireless.kernel.org/en/developers/Regulatory If unsure, say N. config CFG80211_CERTIFICATION_ONUS bool "cfg80211 certification onus" depends on CFG80211 && EXPERT default n ---help--- You should disable this option unless you are both capable and willing to ensure your system will remain regulatory compliant with the features available under this option. Some options may still be under heavy development and for whatever reason regulatory compliance has not or cannot yet be verified. Regulatory verification may at times only be possible until you have the final system in place. This option should only be enabled by system integrators or distributions that have done work necessary to ensure regulatory certification on the system with the enabled features. Alternatively you can enable this option if you are a wireless researcher and are working in a controlled and approved environment by your local regulatory agency. config CFG80211_DEFAULT_PS bool "enable powersave by default" depends on CFG80211 default y help This option enables powersave mode by default. If this causes your applications to misbehave you should fix your applications instead -- they need to register their network latency requirement, see Documentation/power/pm_qos_interface.txt. config CFG80211_DEBUGFS bool "cfg80211 DebugFS entries" depends on CFG80211 depends on DEBUG_FS ---help--- You can enable this if you want to debugfs entries for cfg80211. If unsure, say N. config CFG80211_INTERNAL_REGDB bool "use statically compiled regulatory rules database" if EXPERT default n depends on CFG80211 ---help--- This option generates an internal data structure representing the wireless regulatory rules described in net/wireless/db.txt and includes code to query that database. This is an alternative to using CRDA for defining regulatory rules for the kernel. For details see: http://wireless.kernel.org/en/developers/Regulatory Most distributions have a CRDA package. So if unsure, say N. config CFG80211_WEXT bool "cfg80211 wireless extensions compatibility" depends on CFG80211 select WEXT_CORE help Enable this option if you need old userspace for wireless extensions with cfg80211-based drivers. config LIB80211 tristate "Common routines for IEEE802.11 drivers" default n help This options enables a library of common routines used by IEEE802.11 wireless LAN drivers. Drivers should select this themselves if needed. Say Y if you want this built into your kernel. config LIB80211_CRYPT_WEP tristate config LIB80211_CRYPT_CCMP tristate config LIB80211_CRYPT_TKIP tristate config LIB80211_DEBUG bool "lib80211 debugging messages" depends on LIB80211 default n ---help--- You can enable this if you want verbose debugging messages from lib80211. If unsure, say N. compat-drivers-2012-09-18/net/wireless/debugfs.c0000644000175000017500000000605312026211315020572 0ustar mcgrofmcgrof/* * cfg80211 debugfs * * Copyright 2009 Luis R. Rodriguez * Copyright 2007 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include "core.h" #include "debugfs.h" #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct wiphy *wiphy= file->private_data; \ char buf[buflen]; \ int res; \ \ res = scnprintf(buf, buflen, fmt "\n", ##value); \ return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ } \ \ static const struct file_operations name## _ops = { \ .read = name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", wiphy->rts_threshold) DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", wiphy->frag_threshold); DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", wiphy->retry_short) DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", wiphy->retry_long); static int ht_print_chan(struct ieee80211_channel *chan, char *buf, int buf_size, int offset) { if (WARN_ON(offset > buf_size)) return 0; if (chan->flags & IEEE80211_CHAN_DISABLED) return snprintf(buf + offset, buf_size - offset, "%d Disabled\n", chan->center_freq); return snprintf(buf + offset, buf_size - offset, "%d HT40 %c%c\n", chan->center_freq, (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+'); } static ssize_t ht40allow_map_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wiphy *wiphy = file->private_data; char *buf; unsigned int offset = 0, buf_size = PAGE_SIZE, i, r; enum ieee80211_band band; struct ieee80211_supported_band *sband; buf = kzalloc(buf_size, GFP_KERNEL); if (!buf) return -ENOMEM; mutex_lock(&cfg80211_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) offset += ht_print_chan(&sband->channels[i], buf, buf_size, offset); } mutex_unlock(&cfg80211_mutex); r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); kfree(buf); return r; } static const struct file_operations ht40allow_map_ops = { .read = ht40allow_map_read, .open = simple_open, .llseek = default_llseek, }; #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, S_IRUGO, phyd, &rdev->wiphy, &name## _ops); void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) { struct dentry *phyd = rdev->wiphy.debugfsdir; DEBUGFS_ADD(rts_threshold); DEBUGFS_ADD(fragmentation_threshold); DEBUGFS_ADD(short_retry_limit); DEBUGFS_ADD(long_retry_limit); DEBUGFS_ADD(ht40allow_map); } compat-drivers-2012-09-18/net/wireless/mesh.c0000644000175000017500000001671712026211315020117 0ustar mcgrofmcgrof#include #include #include #include "nl80211.h" #include "core.h" /* Default values, timeouts in ms */ #define MESH_TTL 31 #define MESH_DEFAULT_ELEMENT_TTL 31 #define MESH_MAX_RETR 3 #define MESH_RET_T 100 #define MESH_CONF_T 100 #define MESH_HOLD_T 100 #define MESH_PATH_TIMEOUT 5000 #define MESH_RANN_INTERVAL 5000 #define MESH_PATH_TO_ROOT_TIMEOUT 6000 #define MESH_ROOT_INTERVAL 5000 #define MESH_ROOT_CONFIRMATION_INTERVAL 2000 /* * Minimum interval between two consecutive PREQs originated by the same * interface */ #define MESH_PREQ_MIN_INT 10 #define MESH_PERR_MIN_INT 100 #define MESH_DIAM_TRAVERSAL_TIME 50 #define MESH_RSSI_THRESHOLD 0 /* * A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds * before timing out. This way it will remain ACTIVE and no data frames * will be unnecessarily held in the pending queue. */ #define MESH_PATH_REFRESH_TIME 1000 #define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) /* Default maximum number of established plinks per interface */ #define MESH_MAX_ESTAB_PLINKS 32 #define MESH_MAX_PREQ_RETRIES 4 #define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50 const struct mesh_config default_mesh_config = { .dot11MeshRetryTimeout = MESH_RET_T, .dot11MeshConfirmTimeout = MESH_CONF_T, .dot11MeshHoldingTimeout = MESH_HOLD_T, .dot11MeshMaxRetries = MESH_MAX_RETR, .dot11MeshTTL = MESH_TTL, .element_ttl = MESH_DEFAULT_ELEMENT_TTL, .auto_open_plinks = true, .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS, .dot11MeshNbrOffsetMaxNeighbor = MESH_SYNC_NEIGHBOR_OFFSET_MAX, .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT, .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT, .dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT, .dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME, .dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES, .path_refresh_time = MESH_PATH_REFRESH_TIME, .min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT, .dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL, .dot11MeshGateAnnouncementProtocol = false, .dot11MeshForwarding = true, .rssi_threshold = MESH_RSSI_THRESHOLD, .ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED, .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT, .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL, .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, }; const struct mesh_setup default_mesh_setup = { /* cfg80211_join_mesh() will pick a channel if needed */ .channel = NULL, .channel_type = NL80211_CHAN_NO_HT, .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, .ie = NULL, .ie_len = 0, .is_secure = false, }; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); ASSERT_WDEV_LOCK(wdev); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && setup->is_secure) return -EOPNOTSUPP; if (wdev->mesh_id_len) return -EALREADY; if (!setup->mesh_id_len) return -EINVAL; if (!rdev->ops->join_mesh) return -EOPNOTSUPP; if (!setup->channel) { /* if no channel explicitly given, use preset channel */ setup->channel = wdev->preset_chan; setup->channel_type = wdev->preset_chantype; } if (!setup->channel) { /* if we don't have that either, use the first usable channel */ enum ieee80211_band band; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; int i; sband = rdev->wiphy.bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; if (chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) continue; setup->channel = chan; break; } if (setup->channel) break; } /* no usable channel ... */ if (!setup->channel) return -EINVAL; setup->channel_type = NL80211_CHAN_NO_HT; } if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel, setup->channel_type)) return -EINVAL; err = cfg80211_can_use_chan(rdev, wdev, setup->channel, CHAN_MODE_SHARED); if (err) return err; err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; wdev->channel = setup->channel; } return err; } int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_join_mesh(rdev, dev, setup, conf); wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); return err; } int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int freq, enum nl80211_channel_type channel_type) { struct ieee80211_channel *channel; int err; channel = rdev_freq_to_chan(rdev, freq, channel_type); if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, channel, channel_type)) { return -EINVAL; } /* * Workaround for libertas (only!), it puts the interface * into mesh mode but doesn't implement join_mesh. Instead, * it is configured via sysfs and then joins the mesh when * you set the channel. Note that the libertas mesh isn't * compatible with 802.11 mesh. */ if (rdev->ops->libertas_set_mesh_channel) { if (channel_type != NL80211_CHAN_NO_HT) return -EINVAL; if (!netif_running(wdev->netdev)) return -ENETDOWN; err = cfg80211_can_use_chan(rdev, wdev, channel, CHAN_MODE_SHARED); if (err) return err; err = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, wdev->netdev, channel); if (!err) wdev->channel = channel; return err; } if (wdev->mesh_id_len) return -EBUSY; wdev->preset_chan = channel; wdev->preset_chantype = channel_type; return 0; } void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) return; nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev, macaddr, ie, ie_len, gfp); } EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; ASSERT_WDEV_LOCK(wdev); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; if (!rdev->ops->leave_mesh) return -EOPNOTSUPP; if (!wdev->mesh_id_len) return -ENOTCONN; err = rdev->ops->leave_mesh(&rdev->wiphy, dev); if (!err) { wdev->mesh_id_len = 0; wdev->channel = NULL; } return err; } int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); err = __cfg80211_leave_mesh(rdev, dev); wdev_unlock(wdev); return err; } compat-drivers-2012-09-18/net/mac80211/0000755000175000017500000000000012026211315016302 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/mac80211/trace.c0000644000175000017500000000276012026211315017551 0ustar mcgrofmcgrof/* bug in tracepoint.h, it should include this */ #include /* sparse isn't too happy with all macros... */ #ifndef __CHECKER__ #include #include "driver-ops.h" #include "debug.h" #define CREATE_TRACE_POINTS #include "trace.h" #ifdef CONFIG_MAC80211_MESSAGE_TRACING void __sdata_info(const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args, args2; va_start(args, fmt); va_copy(args2, args); vaf.va = &args2; pr_info("%pV", &vaf); va_end(args2); vaf.va = &args; trace_mac80211_info(&vaf); va_end(args); } void __sdata_dbg(bool print, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); if (print) { va_list args2; va_copy(args2, args); vaf.va = &args2; pr_debug("%pV", &vaf); va_end(args2); } vaf.va = &args; trace_mac80211_dbg(&vaf); va_end(args); } void __sdata_err(const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args, args2; va_start(args, fmt); va_copy(args2, args); vaf.va = &args2; pr_err("%pV", &vaf); va_end(args2); vaf.va = &args; trace_mac80211_err(&vaf); va_end(args); } void __wiphy_dbg(struct wiphy *wiphy, bool print, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); if (print) { va_list args2; va_copy(args2, args); vaf.va = &args2; pr_debug("%pV", &vaf); va_end(args2); } vaf.va = &args; trace_mac80211_dbg(&vaf); va_end(args); } #endif #endif compat-drivers-2012-09-18/net/mac80211/Makefile0000644000175000017500000000247512026211315017752 0ustar mcgrofmcgrofobj-$(CONFIG_MAC80211) += mac80211.o # mac80211 objects mac80211-y := \ main.o status.o \ sta_info.o \ wep.o \ wpa.o \ scan.o offchannel.o \ ht.o agg-tx.o agg-rx.o \ ibss.o \ iface.o \ rate.o \ michael.o \ tkip.o \ aes_ccm.o \ aes_cmac.o \ cfg.o \ rx.o \ spectmgmt.o \ tx.o \ key.o \ util.o \ wme.o \ event.o \ chan.o \ mlme.o mac80211-$(CONFIG_COMPAT_MAC80211_DRIVER_API_TRACER) += trace.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ debugfs.o \ debugfs_sta.o \ debugfs_netdev.o \ debugfs_key.o mac80211-$(CONFIG_MAC80211_MESH) += \ mesh.o \ mesh_pathtbl.o \ mesh_plink.o \ mesh_hwmp.o \ mesh_sync.o mac80211-$(CONFIG_PM) += pm.o CFLAGS_trace.o := -I$(src) # objects for PID algorithm rc80211_pid-y := rc80211_pid_algo.o rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o rc80211_minstrel-y := rc80211_minstrel.o rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o rc80211_minstrel_ht-y := rc80211_minstrel_ht.o rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y) mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y) ccflags-y += -D__CHECK_ENDIAN__ -DDEBUG compat-drivers-2012-09-18/net/mac80211/main.c0000644000175000017500000007556612026211315017415 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "rate.h" #include "mesh.h" #include "wep.h" #include "led.h" #include "cfg.h" #include "debugfs.h" static struct lock_class_key ieee80211_rx_skb_queue_class; void ieee80211_configure_filter(struct ieee80211_local *local) { u64 mc; unsigned int changed_flags; unsigned int new_flags = 0; if (atomic_read(&local->iff_promiscs)) new_flags |= FIF_PROMISC_IN_BSS; if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; if (local->monitors || test_bit(SCAN_SW_SCANNING, &local->scanning) || test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) new_flags |= FIF_BCN_PRBRESP_PROMISC; if (local->fif_probe_req || local->probe_req_reg) new_flags |= FIF_PROBE_REQ; if (local->fif_fcsfail) new_flags |= FIF_FCSFAIL; if (local->fif_plcpfail) new_flags |= FIF_PLCPFAIL; if (local->fif_control) new_flags |= FIF_CONTROL; if (local->fif_other_bss) new_flags |= FIF_OTHER_BSS; if (local->fif_pspoll) new_flags |= FIF_PSPOLL; spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) mc = drv_prepare_multicast(local, &local->mc_list); #else mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); #endif spin_unlock_bh(&local->filter_lock); /* be a bit nasty */ new_flags |= (1<<31); drv_configure_filter(local, changed_flags, &new_flags, mc); WARN_ON(new_flags & (1<<31)); local->filter_flags = new_flags & ~(1<<31); } static void ieee80211_reconfig_filter(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, reconfig_filter); ieee80211_configure_filter(local); } int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan; int ret = 0; int power; enum nl80211_channel_type channel_type; u32 offchannel_flag; might_sleep(); offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; if (local->scan_channel) { chan = local->scan_channel; /* If scanning on oper channel, use whatever channel-type * is currently in use. */ if (chan == local->oper_channel) channel_type = local->_oper_channel_type; else channel_type = NL80211_CHAN_NO_HT; } else if (local->tmp_channel) { chan = local->tmp_channel; channel_type = local->tmp_channel_type; } else { chan = local->oper_channel; channel_type = local->_oper_channel_type; } if (chan != local->oper_channel || channel_type != local->_oper_channel_type) local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; else local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL; offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; if (offchannel_flag || chan != local->hw.conf.channel || channel_type != local->hw.conf.channel_type) { local->hw.conf.channel = chan; local->hw.conf.channel_type = channel_type; changed |= IEEE80211_CONF_CHANGE_CHANNEL; } if (!conf_is_ht(&local->hw.conf)) { /* * mac80211.h documents that this is only valid * when the channel is set to an HT type, and * that otherwise STATIC is used. */ local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC; } else if (local->hw.conf.smps_mode != local->smps_mode) { local->hw.conf.smps_mode = local->smps_mode; changed |= IEEE80211_CONF_CHANGE_SMPS; } if (test_bit(SCAN_SW_SCANNING, &local->scanning) || test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || test_bit(SCAN_HW_SCANNING, &local->scanning)) power = chan->max_power; else power = local->power_constr_level ? min(chan->max_power, (chan->max_reg_power - local->power_constr_level)) : chan->max_power; if (local->user_power_level >= 0) power = min(power, local->user_power_level); if (local->hw.conf.power_level != power) { changed |= IEEE80211_CONF_CHANGE_POWER; local->hw.conf.power_level = power; } if (changed && local->open_count) { ret = drv_config(local, changed); /* * Goal: * HW reconfiguration should never fail, the driver has told * us what it can support so it should live up to that promise. * * Current status: * rfkill is not integrated with mac80211 and a * configuration command can thus fail if hardware rfkill * is enabled * * FIXME: integrate rfkill with mac80211 and then add this * WARN_ON() back * */ /* WARN_ON(ret); */ } return ret; } void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u32 changed) { struct ieee80211_local *local = sdata->local; static const u8 zero[ETH_ALEN] = { 0 }; if (!changed) return; if (sdata->vif.type == NL80211_IFTYPE_STATION) { sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; else if (sdata->vif.type == NL80211_IFTYPE_AP) sdata->vif.bss_conf.bssid = sdata->vif.addr; else if (sdata->vif.type == NL80211_IFTYPE_WDS) sdata->vif.bss_conf.bssid = NULL; else if (ieee80211_vif_is_mesh(&sdata->vif)) { sdata->vif.bss_conf.bssid = zero; } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { sdata->vif.bss_conf.bssid = sdata->vif.addr; WARN_ONCE(changed & ~(BSS_CHANGED_IDLE), "P2P Device BSS changed %#x", changed); } else { WARN_ON(1); return; } switch (sdata->vif.type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: break; default: /* do not warn to simplify caller in scan.c */ changed &= ~BSS_CHANGED_BEACON_ENABLED; if (WARN_ON(changed & BSS_CHANGED_BEACON)) return; break; } if (changed & BSS_CHANGED_BEACON_ENABLED) { if (local->quiescing || !ieee80211_sdata_running(sdata) || test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) { sdata->vif.bss_conf.enable_beacon = false; } else { /* * Beacon should be enabled, but AP mode must * check whether there is a beacon configured. */ switch (sdata->vif.type) { case NL80211_IFTYPE_AP: sdata->vif.bss_conf.enable_beacon = !!sdata->u.ap.beacon; break; case NL80211_IFTYPE_ADHOC: sdata->vif.bss_conf.enable_beacon = !!sdata->u.ibss.presp; break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: sdata->vif.bss_conf.enable_beacon = !!sdata->u.mesh.mesh_id_len; break; #endif default: /* not reached */ WARN_ON(1); break; } } } drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); } u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) { sdata->vif.bss_conf.use_cts_prot = false; sdata->vif.bss_conf.use_short_preamble = false; sdata->vif.bss_conf.use_short_slot = false; return BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_ERP_SLOT; } static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; struct sta_info *sta, *tmp; struct skb_eosp_msg_data *eosp_data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || (skb = skb_dequeue(&local->skb_queue_unreliable))) { switch (skb->pkt_type) { case IEEE80211_RX_MSG: /* Clear skb->pkt_type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; ieee80211_rx(&local->hw, skb); break; case IEEE80211_TX_STATUS_MSG: skb->pkt_type = 0; ieee80211_tx_status(&local->hw, skb); break; case IEEE80211_EOSP_MSG: eosp_data = (void *)skb->cb; for_each_sta_info(local, eosp_data->sta, sta, tmp) { /* skip wrong virtual interface */ if (memcmp(eosp_data->iface, sta->sdata->vif.addr, ETH_ALEN)) continue; clear_sta_flag(sta, WLAN_STA_SP); break; } dev_kfree_skb(skb); break; default: WARN(1, "mac80211: Packet is of unknown type %d\n", skb->pkt_type); dev_kfree_skb(skb); break; } } } static void ieee80211_restart_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, restart_work); /* wait for scan work complete */ flush_workqueue(local->workqueue); mutex_lock(&local->mtx); WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) || rcu_dereference_protected(local->sched_scan_sdata, lockdep_is_held(&local->mtx)), "%s called with hardware scan in progress\n", __func__); mutex_unlock(&local->mtx); rtnl_lock(); ieee80211_scan_cancel(local); ieee80211_reconfig(local); rtnl_unlock(); } void ieee80211_restart_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); trace_api_restart_hw(local); wiphy_info(hw->wiphy, "Hardware restart was requested\n"); /* use this reason, ieee80211_reconfig will unblock it */ ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* * Stop all Rx during the reconfig. We don't want state changes * or driver callbacks while this is in progress. */ local->in_reconfig = true; barrier(); schedule_work(&local->restart_work); } EXPORT_SYMBOL(ieee80211_restart_hw); static void ieee80211_recalc_smps_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, recalc_smps); mutex_lock(&local->iflist_mtx); ieee80211_recalc_smps(local); mutex_unlock(&local->iflist_mtx); } #ifdef CONFIG_INET static int ieee80211_ifa_changed(struct notifier_block *nb, unsigned long data, void *arg) { struct in_ifaddr *ifa = arg; struct ieee80211_local *local = container_of(nb, struct ieee80211_local, ifa_notifier); struct net_device *ndev = ifa->ifa_dev->dev; struct wireless_dev *wdev = ndev->ieee80211_ptr; struct in_device *idev; struct ieee80211_sub_if_data *sdata; struct ieee80211_bss_conf *bss_conf; struct ieee80211_if_managed *ifmgd; int c = 0; /* Make sure it's our interface that got changed */ if (!wdev) return NOTIFY_DONE; if (wdev->wiphy != local->hw.wiphy) return NOTIFY_DONE; sdata = IEEE80211_DEV_TO_SUB_IF(ndev); bss_conf = &sdata->vif.bss_conf; /* ARP filtering is only supported in managed mode */ if (sdata->vif.type != NL80211_IFTYPE_STATION) return NOTIFY_DONE; idev = __in_dev_get_rtnl(sdata->dev); if (!idev) return NOTIFY_DONE; ifmgd = &sdata->u.mgd; mutex_lock(&ifmgd->mtx); /* Copy the addresses to the bss_conf list */ ifa = idev->ifa_list; while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) { bss_conf->arp_addr_list[c] = ifa->ifa_address; ifa = ifa->ifa_next; c++; } /* If not all addresses fit the list, disable filtering */ if (ifa) { sdata->arp_filter_state = false; c = 0; } else { sdata->arp_filter_state = true; } bss_conf->arp_addr_cnt = c; /* Configure driver only if associated (which also implies it is up) */ if (ifmgd->associated) { bss_conf->arp_filter_enabled = sdata->arp_filter_state; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_ARP_FILTER); } mutex_unlock(&ifmgd->mtx); return NOTIFY_DONE; } #endif static int ieee80211_napi_poll(struct napi_struct *napi, int budget) { struct ieee80211_local *local = container_of(napi, struct ieee80211_local, napi); return local->ops->napi_poll(&local->hw, budget); } void ieee80211_napi_schedule(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); napi_schedule(&local->napi); } EXPORT_SYMBOL(ieee80211_napi_schedule); void ieee80211_napi_complete(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); napi_complete(&local->napi); } EXPORT_SYMBOL(ieee80211_napi_complete); /* There isn't a lot of sense in it, but you can transmit anything you like */ static const struct ieee80211_txrx_stypes ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_ADHOC] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4), }, [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4), }, [NL80211_IFTYPE_AP] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4), }, [NL80211_IFTYPE_AP_VLAN] = { /* copy AP */ .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4), }, [NL80211_IFTYPE_P2P_CLIENT] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4), }, [NL80211_IFTYPE_P2P_GO] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4), }, [NL80211_IFTYPE_MESH_POINT] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4), }, [NL80211_IFTYPE_P2P_DEVICE] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4), }, }; static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { .ampdu_params_info = IEEE80211_HT_AMPDU_PARM_FACTOR | IEEE80211_HT_AMPDU_PARM_DENSITY, .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_MAX_AMSDU | IEEE80211_HT_CAP_SGI_40), .mcs = { .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, }, }; struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, const struct ieee80211_ops *ops) { struct ieee80211_local *local; int priv_size, i; struct wiphy *wiphy; if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || !ops->add_interface || !ops->remove_interface || !ops->configure_filter)) return NULL; if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) return NULL; /* Ensure 32-byte alignment of our private data and hw private data. * We use the wiphy priv data for both our ieee80211_local and for * the driver's private data * * In memory it'll be like this: * * +-------------------------+ * | struct wiphy | * +-------------------------+ * | struct ieee80211_local | * +-------------------------+ * | driver's private data | * +-------------------------+ * */ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; wiphy = wiphy_new(&mac80211_config_ops, priv_size); if (!wiphy) return NULL; wiphy->mgmt_stypes = ieee80211_default_mgmt_stypes; wiphy->privid = mac80211_wiphy_privid; wiphy->flags |= WIPHY_FLAG_NETNS_OK | WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_STATION | WIPHY_FLAG_REPORTS_OBSS | WIPHY_FLAG_OFFCHAN_TX; if (ops->remain_on_channel) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; wiphy->features = NL80211_FEATURE_SK_TX_STATUS | NL80211_FEATURE_HT_IBSS; if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; wiphy->bss_priv_size = sizeof(struct ieee80211_bss); local = wiphy_priv(wiphy); local->hw.wiphy = wiphy; local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); local->ops = ops; /* set up some defaults */ local->hw.queues = 1; local->hw.max_rates = 1; local->hw.max_report_rates = 0; local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; local->hw.max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; local->hw.offchannel_tx_hw_queue = IEEE80211_INVAL_HW_QUEUE; local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_BW; local->user_power_level = -1; wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; INIT_LIST_HEAD(&local->interfaces); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __hw_addr_init(&local->mc_list); #endif mutex_init(&local->iflist_mtx); mutex_init(&local->mtx); mutex_init(&local->key_mtx); spin_lock_init(&local->filter_lock); spin_lock_init(&local->queue_stop_reason_lock); /* * The rx_skb_queue is only accessed from tasklets, * but other SKB queues are used from within IRQ * context. Therefore, this one needs a different * locking class so our direct, non-irq-safe use of * the queue's lock doesn't throw lockdep warnings. */ skb_queue_head_init_class(&local->rx_skb_queue, &ieee80211_rx_skb_queue_class); INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); INIT_WORK(&local->restart_work, ieee80211_restart_work); INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work); local->smps_mode = IEEE80211_SMPS_OFF; INIT_WORK(&local->dynamic_ps_enable_work, ieee80211_dynamic_ps_enable_work); INIT_WORK(&local->dynamic_ps_disable_work, ieee80211_dynamic_ps_disable_work); setup_timer(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, (unsigned long) local); INIT_WORK(&local->sched_scan_stopped_work, ieee80211_sched_scan_stopped_work); spin_lock_init(&local->ack_status_lock); idr_init(&local->ack_status_frames); /* preallocate at least one entry */ idr_pre_get(&local->ack_status_frames, GFP_KERNEL); sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { skb_queue_head_init(&local->pending[i]); atomic_set(&local->agg_queue_stop[i], 0); } tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, (unsigned long)local); tasklet_init(&local->tasklet, ieee80211_tasklet_handler, (unsigned long) local); skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); /* init dummy netdev for use w/ NAPI */ init_dummy_netdev(&local->napi_dev); ieee80211_led_names(local); ieee80211_roc_setup(local); return &local->hw; } EXPORT_SYMBOL(ieee80211_alloc_hw); int ieee80211_register_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); int result, i; enum ieee80211_band band; int channels, max_bitrates; bool supp_ht, supp_vht; netdev_features_t feature_whitelist; static const u32 cipher_suites[] = { /* keep WEP first, it may be removed below */ WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, /* keep last -- depends on hw flags! */ WLAN_CIPHER_SUITE_AES_CMAC }; if (hw->flags & IEEE80211_HW_QUEUE_CONTROL && (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE || local->hw.offchannel_tx_hw_queue >= local->hw.queues)) return -EINVAL; #ifdef CONFIG_PM if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) && (!local->ops->suspend || !local->ops->resume)) return -EINVAL; #endif if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) return -EINVAL; /* Only HW csum features are currently compatible with mac80211 */ feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM; if (WARN_ON(hw->netdev_features & ~feature_whitelist)) return -EINVAL; if (hw->max_report_rates == 0) hw->max_report_rates = hw->max_rates; /* * generic code guarantees at least one band, * set this very early because much code assumes * that hw.conf.channel is assigned */ channels = 0; max_bitrates = 0; supp_ht = false; supp_vht = false; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[band]; if (!sband) continue; if (!local->oper_channel) { /* init channel we're on */ local->hw.conf.channel = local->oper_channel = &sband->channels[0]; local->hw.conf.channel_type = NL80211_CHAN_NO_HT; } channels += sband->n_channels; if (max_bitrates < sband->n_bitrates) max_bitrates = sband->n_bitrates; supp_ht = supp_ht || sband->ht_cap.ht_supported; supp_vht = supp_vht || sband->vht_cap.vht_supported; } local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + sizeof(void *) * channels, GFP_KERNEL); if (!local->int_scan_req) return -ENOMEM; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!local->hw.wiphy->bands[band]) continue; local->int_scan_req->rates[band] = (u32) -1; } /* if low-level driver supports AP, we also support VLAN */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN); } /* mac80211 always supports monitor */ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); /* * mac80211 doesn't support more than 1 channel, and also not more * than one IBSS interface */ for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *c; int j; c = &hw->wiphy->iface_combinations[i]; if (c->num_different_channels > 1) return -EINVAL; for (j = 0; j < c->n_limits; j++) if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && c->limits[j].max > 1) return -EINVAL; } #ifndef CONFIG_MAC80211_MESH /* mesh depends on Kconfig, but drivers should set it if they want */ local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); #endif /* if the underlying driver supports mesh, mac80211 will (at least) * provide routing of mesh authentication frames to userspace */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) local->hw.wiphy->flags |= WIPHY_FLAG_MESH_AUTH; /* mac80211 supports control port protocol changing */ local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL; if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK), "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"); /* * Calculate scan IE length -- we need this to alloc * memory and to subtract from the driver limit. It * includes the DS Params, (extended) supported rates, and HT * information -- SSID is the driver's responsibility. */ local->scan_ies_len = 4 + max_bitrates /* (ext) supp rates */ + 3 /* DS Params */; if (supp_ht) local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap); if (supp_vht) local->scan_ies_len += 2 + sizeof(struct ieee80211_vht_capabilities); if (!local->ops->hw_scan) { /* For hw_scan, driver needs to set these up. */ local->hw.wiphy->max_scan_ssids = 4; local->hw.wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; } /* * If the driver supports any scan IEs, then assume the * limit includes the IEs mac80211 will add, otherwise * leave it at zero and let the driver sort it out; we * still pass our IEs to the driver but userspace will * not be allowed to in that case. */ if (local->hw.wiphy->max_scan_ie_len) local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len; /* Set up cipher suites unless driver already did */ if (!local->hw.wiphy->cipher_suites) { local->hw.wiphy->cipher_suites = cipher_suites; local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) local->hw.wiphy->n_cipher_suites--; } if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) { if (local->hw.wiphy->cipher_suites == cipher_suites) { local->hw.wiphy->cipher_suites += 2; local->hw.wiphy->n_cipher_suites -= 2; } else { u32 *suites; int r, w = 0; /* Filter out WEP */ suites = kmemdup( local->hw.wiphy->cipher_suites, sizeof(u32) * local->hw.wiphy->n_cipher_suites, GFP_KERNEL); if (!suites) return -ENOMEM; for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) { u32 suite = local->hw.wiphy->cipher_suites[r]; if (suite == WLAN_CIPHER_SUITE_WEP40 || suite == WLAN_CIPHER_SUITE_WEP104) continue; suites[w++] = suite; } local->hw.wiphy->cipher_suites = suites; local->hw.wiphy->n_cipher_suites = w; local->wiphy_ciphers_allocated = true; } } if (!local->ops->remain_on_channel) local->hw.wiphy->max_remain_on_channel_duration = 5000; if (local->ops->sched_scan_start) local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; /* mac80211 based drivers don't support internal TDLS setup */ if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; /* * We use the number of queues for feature tests (QoS, HT) internally * so restrict them appropriately. */ if (hw->queues > IEEE80211_MAX_QUEUES) hw->queues = IEEE80211_MAX_QUEUES; local->workqueue = alloc_ordered_workqueue(wiphy_name(local->hw.wiphy), 0); if (!local->workqueue) { result = -ENOMEM; goto fail_workqueue; } /* * The hardware needs headroom for sending the frame, * and we need some headroom for passing the frame to monitor * interfaces, but never both at the same time. */ local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom, IEEE80211_TX_STATUS_HEADROOM); debugfs_hw_add(local); /* * if the driver doesn't specify a max listen interval we * use 5 which should be a safe default */ if (local->hw.max_listen_interval == 0) local->hw.max_listen_interval = 5; local->hw.conf.listen_interval = local->hw.max_listen_interval; local->dynamic_ps_forced_timeout = -1; result = ieee80211_wep_init(local); if (result < 0) wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", result); ieee80211_led_init(local); rtnl_lock(); result = ieee80211_init_rate_ctrl_alg(local, hw->rate_control_algorithm); if (result < 0) { wiphy_debug(local->hw.wiphy, "Failed to initialize rate control algorithm\n"); goto fail_rate; } /* add one default STA interface if supported */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) { result = ieee80211_if_add(local, "wlan%d", NULL, NL80211_IFTYPE_STATION, NULL); if (result) wiphy_warn(local->hw.wiphy, "Failed to add default virtual iface\n"); } rtnl_unlock(); local->network_latency_notifier.notifier_call = ieee80211_max_network_latency; result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY, &local->network_latency_notifier); if (result) { rtnl_lock(); goto fail_pm_qos; } #ifdef CONFIG_INET local->ifa_notifier.notifier_call = ieee80211_ifa_changed; result = register_inetaddr_notifier(&local->ifa_notifier); if (result) goto fail_ifa; #endif netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll, local->hw.napi_weight); return 0; #ifdef CONFIG_INET fail_ifa: pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, &local->network_latency_notifier); rtnl_lock(); #endif fail_pm_qos: ieee80211_led_exit(local); ieee80211_remove_interfaces(local); fail_rate: rtnl_unlock(); ieee80211_wep_free(local); sta_info_stop(local); destroy_workqueue(local->workqueue); fail_workqueue: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: if (local->wiphy_ciphers_allocated) kfree(local->hw.wiphy->cipher_suites); kfree(local->int_scan_req); return result; } EXPORT_SYMBOL(ieee80211_register_hw); void ieee80211_unregister_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); tasklet_kill(&local->tx_pending_tasklet); tasklet_kill(&local->tasklet); pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, &local->network_latency_notifier); #ifdef CONFIG_INET unregister_inetaddr_notifier(&local->ifa_notifier); #endif rtnl_lock(); /* * At this point, interface list manipulations are fine * because the driver cannot be handing us frames any * more and the tasklet is killed. */ ieee80211_remove_interfaces(local); rtnl_unlock(); cancel_work_sync(&local->restart_work); cancel_work_sync(&local->reconfig_filter); ieee80211_clear_tx_pending(local); rate_control_deinitialize(local); if (skb_queue_len(&local->skb_queue) || skb_queue_len(&local->skb_queue_unreliable)) wiphy_warn(local->hw.wiphy, "skb_queue not empty\n"); skb_queue_purge(&local->skb_queue); skb_queue_purge(&local->skb_queue_unreliable); skb_queue_purge(&local->rx_skb_queue); destroy_workqueue(local->workqueue); wiphy_unregister(local->hw.wiphy); sta_info_stop(local); ieee80211_wep_free(local); ieee80211_led_exit(local); kfree(local->int_scan_req); } EXPORT_SYMBOL(ieee80211_unregister_hw); static int ieee80211_free_ack_frame(int id, void *p, void *data) { WARN_ONCE(1, "Have pending ack frames!\n"); kfree_skb(p); return 0; } void ieee80211_free_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); mutex_destroy(&local->iflist_mtx); mutex_destroy(&local->mtx); if (local->wiphy_ciphers_allocated) kfree(local->hw.wiphy->cipher_suites); idr_for_each(&local->ack_status_frames, ieee80211_free_ack_frame, NULL); idr_destroy(&local->ack_status_frames); wiphy_free(local->hw.wiphy); } EXPORT_SYMBOL(ieee80211_free_hw); static int __init ieee80211_init(void) { struct sk_buff *skb; int ret; BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb)); BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) + IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb)); ret = rc80211_minstrel_init(); if (ret) return ret; ret = rc80211_minstrel_ht_init(); if (ret) goto err_minstrel; ret = rc80211_pid_init(); if (ret) goto err_pid; ret = ieee80211_iface_init(); if (ret) goto err_netdev; return 0; err_netdev: rc80211_pid_exit(); err_pid: rc80211_minstrel_ht_exit(); err_minstrel: rc80211_minstrel_exit(); return ret; } static void __exit ieee80211_exit(void) { rc80211_pid_exit(); rc80211_minstrel_ht_exit(); rc80211_minstrel_exit(); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) flush_scheduled_work(); #endif if (mesh_allocated) ieee80211s_stop(); ieee80211_iface_exit(); rcu_barrier(); } subsys_initcall(ieee80211_init); module_exit(ieee80211_exit); MODULE_DESCRIPTION("IEEE 802.11 subsystem"); MODULE_LICENSE("GPL"); compat-drivers-2012-09-18/net/mac80211/iface.c0000644000175000017500000012515512026211315017526 0ustar mcgrofmcgrof/* * Interface handling (except master interface) * * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2008, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "sta_info.h" #include "debugfs_netdev.h" #include "mesh.h" #include "led.h" #include "driver-ops.h" #include "wme.h" #include "rate.h" /** * DOC: Interface list locking * * The interface list in each struct ieee80211_local is protected * three-fold: * * (1) modifications may only be done under the RTNL * (2) modifications and readers are protected against each other by * the iflist_mtx. * (3) modifications are done in an RCU manner so atomic readers * can traverse the list in RCU-safe blocks. * * As a consequence, reads (traversals) of the list can be protected * by either the RTNL, the iflist_mtx or RCU. */ static u32 ieee80211_idle_off(struct ieee80211_local *local, const char *reason) { if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE)) return 0; local->hw.conf.flags &= ~IEEE80211_CONF_IDLE; return IEEE80211_CONF_CHANGE_IDLE; } static u32 ieee80211_idle_on(struct ieee80211_local *local) { if (local->hw.conf.flags & IEEE80211_CONF_IDLE) return 0; drv_flush(local, false); local->hw.conf.flags |= IEEE80211_CONF_IDLE; return IEEE80211_CONF_CHANGE_IDLE; } static u32 __ieee80211_recalc_idle(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; int count = 0; bool working = false, scanning = false; unsigned int led_trig_start = 0, led_trig_stop = 0; struct ieee80211_roc_work *roc; #ifdef CONFIG_PROVE_LOCKING WARN_ON(debug_locks && !lockdep_rtnl_is_held() && !lockdep_is_held(&local->iflist_mtx)); #endif lockdep_assert_held(&local->mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) { sdata->vif.bss_conf.idle = true; continue; } sdata->old_idle = sdata->vif.bss_conf.idle; /* do not count disabled managed interfaces */ if (sdata->vif.type == NL80211_IFTYPE_STATION && !sdata->u.mgd.associated && !sdata->u.mgd.auth_data && !sdata->u.mgd.assoc_data) { sdata->vif.bss_conf.idle = true; continue; } /* do not count unused IBSS interfaces */ if (sdata->vif.type == NL80211_IFTYPE_ADHOC && !sdata->u.ibss.ssid_len) { sdata->vif.bss_conf.idle = true; continue; } if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) continue; /* count everything else */ sdata->vif.bss_conf.idle = false; count++; } if (!local->ops->remain_on_channel) { list_for_each_entry(roc, &local->roc_list, list) { working = true; roc->sdata->vif.bss_conf.idle = false; } } sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)); if (sdata && !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) { scanning = true; sdata->vif.bss_conf.idle = false; } list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR || sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) continue; if (sdata->old_idle == sdata->vif.bss_conf.idle) continue; if (!ieee80211_sdata_running(sdata)) continue; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); } if (working || scanning) led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK; else led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK; if (count) led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED; else led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED; ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop); if (working) return ieee80211_idle_off(local, "working"); if (scanning) return ieee80211_idle_off(local, "scanning"); if (!count) return ieee80211_idle_on(local); else return ieee80211_idle_off(local, "in use"); return 0; } void ieee80211_recalc_idle(struct ieee80211_local *local) { u32 chg; mutex_lock(&local->iflist_mtx); chg = __ieee80211_recalc_idle(local); mutex_unlock(&local->iflist_mtx); if (chg) ieee80211_hw_config(local, chg); } static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) { int meshhdrlen; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0; /* FIX: what would be proper limits for MTU? * This interface uses 802.3 frames. */ if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) { return -EINVAL; } dev->mtu = new_mtu; return 0; } static int ieee80211_change_mac(struct net_device *dev, void *addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sockaddr *sa = addr; int ret; if (ieee80211_sdata_running(sdata)) return -EBUSY; ret = eth_mac_addr(dev, sa); if (ret == 0) memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN); return ret; } static inline int identical_mac_addr_allowed(int type1, int type2) { return type1 == NL80211_IFTYPE_MONITOR || type2 == NL80211_IFTYPE_MONITOR || type1 == NL80211_IFTYPE_P2P_DEVICE || type2 == NL80211_IFTYPE_P2P_DEVICE || (type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_WDS) || (type1 == NL80211_IFTYPE_WDS && (type2 == NL80211_IFTYPE_WDS || type2 == NL80211_IFTYPE_AP)) || (type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_AP_VLAN) || (type1 == NL80211_IFTYPE_AP_VLAN && (type2 == NL80211_IFTYPE_AP || type2 == NL80211_IFTYPE_AP_VLAN)); } static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype iftype) { struct ieee80211_local *local = sdata->local; struct ieee80211_sub_if_data *nsdata; ASSERT_RTNL(); /* we hold the RTNL here so can safely walk the list */ list_for_each_entry(nsdata, &local->interfaces, list) { if (nsdata != sdata && ieee80211_sdata_running(nsdata)) { /* * Allow only a single IBSS interface to be up at any * time. This is restricted because beacon distribution * cannot work properly if both are in the same IBSS. * * To remove this restriction we'd have to disallow them * from setting the same SSID on different IBSS interfaces * belonging to the same hardware. Then, however, we're * faced with having to adopt two different TSF timers... */ if (iftype == NL80211_IFTYPE_ADHOC && nsdata->vif.type == NL80211_IFTYPE_ADHOC) return -EBUSY; /* * The remaining checks are only performed for interfaces * with the same MAC address. */ if (!ether_addr_equal(sdata->vif.addr, nsdata->vif.addr)) continue; /* * check whether it may have the same address */ if (!identical_mac_addr_allowed(iftype, nsdata->vif.type)) return -ENOTUNIQ; /* * can only add VLANs to enabled APs */ if (iftype == NL80211_IFTYPE_AP_VLAN && nsdata->vif.type == NL80211_IFTYPE_AP) sdata->bss = &nsdata->u.ap; } } return 0; } static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata) { int n_queues = sdata->local->hw.queues; int i; if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) { for (i = 0; i < IEEE80211_NUM_ACS; i++) { if (WARN_ON_ONCE(sdata->vif.hw_queue[i] == IEEE80211_INVAL_HW_QUEUE)) return -EINVAL; if (WARN_ON_ONCE(sdata->vif.hw_queue[i] >= n_queues)) return -EINVAL; } } if ((sdata->vif.type != NL80211_IFTYPE_AP) || !(sdata->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) { sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; return 0; } if (WARN_ON_ONCE(sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE)) return -EINVAL; if (WARN_ON_ONCE(sdata->vif.cab_queue >= n_queues)) return -EINVAL; return 0; } void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, const int offset) { struct ieee80211_local *local = sdata->local; u32 flags = sdata->u.mntr_flags; #define ADJUST(_f, _s) do { \ if (flags & MONITOR_FLAG_##_f) \ local->fif_##_s += offset; \ } while (0) ADJUST(FCSFAIL, fcsfail); ADJUST(PLCPFAIL, plcpfail); ADJUST(CONTROL, control); ADJUST(CONTROL, pspoll); ADJUST(OTHER_BSS, other_bss); #undef ADJUST } static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; int i; for (i = 0; i < IEEE80211_NUM_ACS; i++) { if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE; else if (local->hw.queues >= IEEE80211_NUM_ACS) sdata->vif.hw_queue[i] = i; else sdata->vif.hw_queue[i] = 0; } sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; } static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; int ret = 0; if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) return 0; mutex_lock(&local->iflist_mtx); if (local->monitor_sdata) goto out_unlock; sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); if (!sdata) { ret = -ENOMEM; goto out_unlock; } /* set up data */ sdata->local = local; sdata->vif.type = NL80211_IFTYPE_MONITOR; snprintf(sdata->name, IFNAMSIZ, "%s-monitor", wiphy_name(local->hw.wiphy)); ieee80211_set_default_queues(sdata); ret = drv_add_interface(local, sdata); if (WARN_ON(ret)) { /* ok .. stupid driver, it asked for this! */ kfree(sdata); goto out_unlock; } ret = ieee80211_check_queues(sdata); if (ret) { kfree(sdata); goto out_unlock; } rcu_assign_pointer(local->monitor_sdata, sdata); out_unlock: mutex_unlock(&local->iflist_mtx); return ret; } static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) return; mutex_lock(&local->iflist_mtx); sdata = rcu_dereference_protected(local->monitor_sdata, lockdep_is_held(&local->iflist_mtx)); if (!sdata) goto out_unlock; rcu_assign_pointer(local->monitor_sdata, NULL); synchronize_net(); drv_remove_interface(local, sdata); kfree(sdata); out_unlock: mutex_unlock(&local->iflist_mtx); } /* * NOTE: Be very careful when changing this function, it must NOT return * an error on interface type changes that have been pre-checked, so most * checks should be in ieee80211_check_concurrent_iface. */ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct net_device *dev = wdev->netdev; struct ieee80211_local *local = sdata->local; struct sta_info *sta; u32 changed = 0; int res; u32 hw_reconf_flags = 0; switch (sdata->vif.type) { case NL80211_IFTYPE_WDS: if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) return -ENOLINK; break; case NL80211_IFTYPE_AP_VLAN: { struct ieee80211_sub_if_data *master; if (!sdata->bss) return -ENOLINK; list_add(&sdata->u.vlan.list, &sdata->bss->vlans); master = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); sdata->control_port_protocol = master->control_port_protocol; sdata->control_port_no_encrypt = master->control_port_no_encrypt; break; } case NL80211_IFTYPE_AP: sdata->bss = &sdata->u.ap; break; case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_P2P_DEVICE: /* no special treatment */ break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: /* cannot happen */ WARN_ON(1); break; } if (local->open_count == 0) { res = drv_start(local); if (res) goto err_del_bss; if (local->ops->napi_poll) napi_enable(&local->napi); /* we're brought up, everything changes */ hw_reconf_flags = ~0; ieee80211_led_radio(local, true); ieee80211_mod_tpt_led_trig(local, IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); } /* * Copy the hopefully now-present MAC address to * this interface, if it has the special null one. */ if (dev && is_zero_ether_addr(dev->dev_addr)) { memcpy(dev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN); if (!is_valid_ether_addr(dev->dev_addr)) { res = -EADDRNOTAVAIL; goto err_stop; } } switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: /* no need to tell driver, but set carrier */ if (rtnl_dereference(sdata->bss->beacon)) netif_carrier_on(dev); else netif_carrier_off(dev); break; case NL80211_IFTYPE_MONITOR: if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { local->cooked_mntrs++; break; } if (local->monitors == 0 && local->open_count == 0) { res = ieee80211_add_virtual_monitor(local); if (res) goto err_stop; } /* must be before the call to ieee80211_configure_filter */ local->monitors++; if (local->monitors == 1) { local->hw.conf.flags |= IEEE80211_CONF_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; } ieee80211_adjust_monitor_flags(sdata, 1); ieee80211_configure_filter(local); netif_carrier_on(dev); break; default: if (coming_up) { ieee80211_del_virtual_monitor(local); res = drv_add_interface(local, sdata); if (res) goto err_stop; res = ieee80211_check_queues(sdata); if (res) goto err_del_interface; } if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll++; local->fif_probe_req++; ieee80211_configure_filter(local); } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { local->fif_probe_req++; } if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) changed |= ieee80211_reset_erp_info(sdata); ieee80211_bss_info_change_notify(sdata, changed); switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: netif_carrier_off(dev); break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_P2P_DEVICE: break; default: netif_carrier_on(dev); } /* * set default queue parameters so drivers don't * need to initialise the hardware if the hardware * doesn't start up with sane defaults */ ieee80211_set_wmm_default(sdata, true); } set_bit(SDATA_STATE_RUNNING, &sdata->state); if (sdata->vif.type == NL80211_IFTYPE_WDS) { /* Create STA entry for the WDS peer */ sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr, GFP_KERNEL); if (!sta) { res = -ENOMEM; goto err_del_interface; } sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); res = sta_info_insert(sta); if (res) { /* STA has been freed */ goto err_del_interface; } rate_control_rate_init(sta); netif_carrier_on(dev); } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { rcu_assign_pointer(local->p2p_sdata, sdata); } /* * set_multicast_list will be invoked by the networking core * which will check whether any increments here were done in * error and sync them down to the hardware as filter flags. */ if (sdata->flags & IEEE80211_SDATA_ALLMULTI) atomic_inc(&local->iff_allmultis); if (sdata->flags & IEEE80211_SDATA_PROMISC) atomic_inc(&local->iff_promiscs); mutex_lock(&local->mtx); hw_reconf_flags |= __ieee80211_recalc_idle(local); mutex_unlock(&local->mtx); if (coming_up) local->open_count++; if (hw_reconf_flags) ieee80211_hw_config(local, hw_reconf_flags); ieee80211_recalc_ps(local, -1); if (dev) netif_tx_start_all_queues(dev); return 0; err_del_interface: drv_remove_interface(local, sdata); err_stop: if (!local->open_count) drv_stop(local); err_del_bss: sdata->bss = NULL; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) list_del(&sdata->u.vlan.list); /* might already be clear but that doesn't matter */ clear_bit(SDATA_STATE_RUNNING, &sdata->state); return res; } static int ieee80211_open(struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); int err; /* fail early if user set an invalid address */ if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); if (err) return err; return ieee80211_do_open(&sdata->wdev, true); } static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down) { struct ieee80211_local *local = sdata->local; unsigned long flags; struct sk_buff *skb, *tmp; u32 hw_reconf_flags = 0; int i; enum nl80211_channel_type orig_ct; clear_bit(SDATA_STATE_RUNNING, &sdata->state); if (rcu_access_pointer(local->scan_sdata) == sdata) ieee80211_scan_cancel(local); /* * Stop TX on this interface first. */ if (sdata->dev) netif_tx_stop_all_queues(sdata->dev); ieee80211_roc_purge(sdata); /* * Remove all stations associated with this interface. * * This must be done before calling ops->remove_interface() * because otherwise we can later invoke ops->sta_notify() * whenever the STAs are removed, and that invalidates driver * assumptions about always getting a vif pointer that is valid * (because if we remove a STA after ops->remove_interface() * the driver will have removed the vif info already!) * * This is relevant only in AP, WDS and mesh modes, since in * all other modes we've already removed all stations when * disconnecting etc. */ sta_info_flush(local, sdata); /* * Don't count this interface for promisc/allmulti while it * is down. dev_mc_unsync() will invoke set_multicast_list * on the master interface which will sync these down to the * hardware as filter flags. */ if (sdata->flags & IEEE80211_SDATA_ALLMULTI) atomic_dec(&local->iff_allmultis); if (sdata->flags & IEEE80211_SDATA_PROMISC) atomic_dec(&local->iff_promiscs); if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll--; local->fif_probe_req--; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { local->fif_probe_req--; } if (sdata->dev) { netif_addr_lock_bh(sdata->dev); spin_lock_bh(&local->filter_lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __hw_addr_unsync(&local->mc_list, &sdata->dev->mc, sdata->dev->addr_len); #else __dev_addr_unsync(&local->mc_list, &local->mc_count, &sdata->dev->mc_list, &sdata->dev->mc_count); #endif spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(sdata->dev); ieee80211_configure_filter(local); } del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); /* APs need special treatment */ if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmpsdata; struct beacon_data *old_beacon = rtnl_dereference(sdata->u.ap.beacon); struct probe_resp *old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); /* sdata_running will return false, so this will disable */ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); /* remove beacon and probe response */ RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL); synchronize_rcu(); kfree(old_beacon); kfree(old_probe_resp); /* down all dependent devices, that is VLANs */ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, u.vlan.list) dev_close(vlan->dev); WARN_ON(!list_empty(&sdata->u.ap.vlans)); /* free all potentially still buffered bcast frames */ local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf); skb_queue_purge(&sdata->u.ap.ps_bc_buf); } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { ieee80211_mgd_stop(sdata); } if (going_down) local->open_count--; switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: list_del(&sdata->u.vlan.list); /* no need to tell driver */ break; case NL80211_IFTYPE_MONITOR: if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { local->cooked_mntrs--; break; } local->monitors--; if (local->monitors == 0) { local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; ieee80211_del_virtual_monitor(local); } ieee80211_adjust_monitor_flags(sdata, -1); ieee80211_configure_filter(local); break; case NL80211_IFTYPE_P2P_DEVICE: /* relies on synchronize_rcu() below */ rcu_assign_pointer(local->p2p_sdata, NULL); /* fall through */ default: flush_work(&sdata->work); /* * When we get here, the interface is marked down. * Call synchronize_rcu() to wait for the RX path * should it be using the interface and enqueuing * frames at this very time on another CPU. */ synchronize_rcu(); skb_queue_purge(&sdata->skb_queue); /* * Free all remaining keys, there shouldn't be any, * except maybe group keys in AP more or WDS? */ ieee80211_free_keys(sdata); if (going_down) drv_remove_interface(local, sdata); } sdata->bss = NULL; mutex_lock(&local->mtx); hw_reconf_flags |= __ieee80211_recalc_idle(local); mutex_unlock(&local->mtx); ieee80211_recalc_ps(local, -1); if (local->open_count == 0) { if (local->ops->napi_poll) napi_disable(&local->napi); ieee80211_clear_tx_pending(local); ieee80211_stop_device(local); /* no reconfiguring after stop! */ hw_reconf_flags = 0; } /* Re-calculate channel-type, in case there are multiple vifs * on different channel types. */ orig_ct = local->_oper_channel_type; ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT); /* do after stop to avoid reconfiguring when we stop anyway */ if (hw_reconf_flags || (orig_ct != local->_oper_channel_type)) ieee80211_hw_config(local, hw_reconf_flags); spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { skb_queue_walk_safe(&local->pending[i], skb, tmp) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (info->control.vif == &sdata->vif) { __skb_unlink(skb, &local->pending[i]); dev_kfree_skb_irq(skb); } } } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); if (local->monitors == local->open_count && local->monitors > 0) ieee80211_add_virtual_monitor(local); } static int ieee80211_stop(struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ieee80211_do_stop(sdata, true); return 0; } static void ieee80211_set_multicast_list(struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; int allmulti, promisc, sdata_allmulti, sdata_promisc; allmulti = !!(dev->flags & IFF_ALLMULTI); promisc = !!(dev->flags & IFF_PROMISC); sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI); sdata_promisc = !!(sdata->flags & IEEE80211_SDATA_PROMISC); if (allmulti != sdata_allmulti) { if (dev->flags & IFF_ALLMULTI) atomic_inc(&local->iff_allmultis); else atomic_dec(&local->iff_allmultis); sdata->flags ^= IEEE80211_SDATA_ALLMULTI; } if (promisc != sdata_promisc) { if (dev->flags & IFF_PROMISC) atomic_inc(&local->iff_promiscs); else atomic_dec(&local->iff_promiscs); sdata->flags ^= IEEE80211_SDATA_PROMISC; } spin_lock_bh(&local->filter_lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len); #else __dev_addr_sync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); #endif spin_unlock_bh(&local->filter_lock); ieee80211_queue_work(&local->hw, &local->reconfig_filter); } /* * Called when the netdev is removed or, by the code below, before * the interface type changes. */ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; int flushed; int i; /* free extra data */ ieee80211_free_keys(sdata); ieee80211_debugfs_remove_netdev(sdata); for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) __skb_queue_purge(&sdata->fragments[i].skb_list); sdata->fragment_next = 0; if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_rmc_free(sdata); flushed = sta_info_flush(local, sdata); WARN_ON(flushed); } static void ieee80211_uninit(struct net_device *dev) { ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev)); } static u16 ieee80211_netdev_select_queue(struct net_device *dev, struct sk_buff *skb) { return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb); } static const struct net_device_ops ieee80211_dataif_ops = { .ndo_open = ieee80211_open, .ndo_stop = ieee80211_stop, .ndo_uninit = ieee80211_uninit, .ndo_start_xmit = ieee80211_subif_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = ieee80211_change_mac, .ndo_select_queue = ieee80211_netdev_select_queue, }; static u16 ieee80211_monitor_select_queue(struct net_device *dev, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_hdr *hdr; struct ieee80211_radiotap_header *rtap = (void *)skb->data; if (local->hw.queues < IEEE80211_NUM_ACS) return 0; if (skb->len < 4 || skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */) return 0; /* doesn't matter, frame will be dropped */ hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len)); return ieee80211_select_queue_80211(sdata, skb, hdr); } static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_open = ieee80211_open, .ndo_stop = ieee80211_stop, .ndo_uninit = ieee80211_uninit, .ndo_start_xmit = ieee80211_monitor_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_select_queue = ieee80211_monitor_select_queue, }; static void ieee80211_if_setup(struct net_device *dev) { ether_setup(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; netdev_attach_ops(dev, &ieee80211_dataif_ops); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) /* Do we need this ? */ /* we will validate the address ourselves in ->open */ dev->validate_addr = NULL; #endif dev->destructor = free_netdev; } static void ieee80211_iface_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, work); struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct sta_info *sta; struct ieee80211_ra_tid *ra_tid; if (!ieee80211_sdata_running(sdata)) return; if (local->scanning) return; /* * ieee80211_queue_work() should have picked up most cases, * here we'll pick the rest. */ if (WARN(local->suspended, "interface work scheduled while going to suspend\n")) return; /* first process frames */ while ((skb = skb_dequeue(&sdata->skb_queue))) { struct ieee80211_mgmt *mgmt = (void *)skb->data; if (skb->pkt_type == IEEE80211_SDATA_QUEUE_AGG_START) { ra_tid = (void *)&skb->cb; ieee80211_start_tx_ba_cb(&sdata->vif, ra_tid->ra, ra_tid->tid); } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_AGG_STOP) { ra_tid = (void *)&skb->cb; ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra, ra_tid->tid); } else if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK) { int len = skb->len; mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mgmt->sa); if (sta) { switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: ieee80211_process_addba_request( local, sta, mgmt, len); break; case WLAN_ACTION_ADDBA_RESP: ieee80211_process_addba_resp(local, sta, mgmt, len); break; case WLAN_ACTION_DELBA: ieee80211_process_delba(sdata, sta, mgmt, len); break; default: WARN_ON(1); break; } } mutex_unlock(&local->sta_mtx); } else if (ieee80211_is_data_qos(mgmt->frame_control)) { struct ieee80211_hdr *hdr = (void *)mgmt; /* * So the frame isn't mgmt, but frame_control * is at the right place anyway, of course, so * the if statement is correct. * * Warn if we have other data frame types here, * they must not get here. */ WARN_ON(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)); WARN_ON(!(hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG))); /* * This was a fragment of a frame, received while * a block-ack session was active. That cannot be * right, so terminate the session. */ mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mgmt->sa); if (sta) { u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; __ieee80211_stop_rx_ba_session( sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_REQUIRE_SETUP, true); } mutex_unlock(&local->sta_mtx); } else switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_rx_queued_mgmt(sdata, skb); break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_rx_queued_mgmt(sdata, skb); break; case NL80211_IFTYPE_MESH_POINT: if (!ieee80211_vif_is_mesh(&sdata->vif)) break; ieee80211_mesh_rx_queued_mgmt(sdata, skb); break; default: WARN(1, "frame for unexpected interface type"); break; } kfree_skb(skb); } /* then other type-dependent work */ switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_work(sdata); break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_work(sdata); break; case NL80211_IFTYPE_MESH_POINT: if (!ieee80211_vif_is_mesh(&sdata->vif)) break; ieee80211_mesh_work(sdata); break; default: break; } } /* * Helper function to initialise an interface to a specific type. */ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type) { /* clear type-dependent union */ memset(&sdata->u, 0, sizeof(sdata->u)); /* and set some type-dependent values */ sdata->vif.type = type; sdata->vif.p2p = false; sdata->wdev.iftype = type; sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE); sdata->control_port_no_encrypt = false; sdata->noack_map = 0; /* only monitor/p2p-device differ */ if (sdata->dev) { netdev_attach_ops(sdata->dev, &ieee80211_dataif_ops); sdata->dev->type = ARPHRD_ETHER; } skb_queue_head_init(&sdata->skb_queue); INIT_WORK(&sdata->work, ieee80211_iface_work); switch (type) { case NL80211_IFTYPE_P2P_GO: type = NL80211_IFTYPE_AP; sdata->vif.type = type; sdata->vif.p2p = true; /* fall through */ case NL80211_IFTYPE_AP: skb_queue_head_init(&sdata->u.ap.ps_bc_buf); INIT_LIST_HEAD(&sdata->u.ap.vlans); break; case NL80211_IFTYPE_P2P_CLIENT: type = NL80211_IFTYPE_STATION; sdata->vif.type = type; sdata->vif.p2p = true; /* fall through */ case NL80211_IFTYPE_STATION: ieee80211_sta_setup_sdata(sdata); break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_setup_sdata(sdata); break; case NL80211_IFTYPE_MESH_POINT: if (ieee80211_vif_is_mesh(&sdata->vif)) ieee80211_mesh_init_sdata(sdata); break; case NL80211_IFTYPE_MONITOR: sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP; netdev_attach_ops(sdata->dev, &ieee80211_monitorif_ops); sdata->u.mntr_flags = MONITOR_FLAG_CONTROL | MONITOR_FLAG_OTHER_BSS; break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: BUG(); break; } ieee80211_debugfs_add_netdev(sdata); } static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type) { struct ieee80211_local *local = sdata->local; int ret, err; enum nl80211_iftype internal_type = type; bool p2p = false; ASSERT_RTNL(); if (!local->ops->change_interface) return -EBUSY; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: /* * Could maybe also all others here? * Just not sure how that interacts * with the RX/config path e.g. for * mesh. */ break; default: return -EBUSY; } switch (type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: /* * Could probably support everything * but WDS here (WDS do_open can fail * under memory pressure, which this * code isn't prepared to handle). */ break; case NL80211_IFTYPE_P2P_CLIENT: p2p = true; internal_type = NL80211_IFTYPE_STATION; break; case NL80211_IFTYPE_P2P_GO: p2p = true; internal_type = NL80211_IFTYPE_AP; break; default: return -EBUSY; } ret = ieee80211_check_concurrent_iface(sdata, internal_type); if (ret) return ret; ieee80211_do_stop(sdata, false); ieee80211_teardown_sdata(sdata); ret = drv_change_interface(local, sdata, internal_type, p2p); if (ret) type = sdata->vif.type; /* * Ignore return value here, there's not much we can do since * the driver changed the interface type internally already. * The warnings will hopefully make driver authors fix it :-) */ ieee80211_check_queues(sdata); ieee80211_setup_sdata(sdata, type); err = ieee80211_do_open(&sdata->wdev, false); WARN(err, "type change: do_open returned %d", err); return ret; } int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type) { int ret; ASSERT_RTNL(); if (type == ieee80211_vif_type_p2p(&sdata->vif)) return 0; /* Setting ad-hoc mode on non-IBSS channel is not supported. */ if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS && type == NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; if (ieee80211_sdata_running(sdata)) { ret = ieee80211_runtime_change_iftype(sdata, type); if (ret) return ret; } else { /* Purge and reset type-dependent state. */ ieee80211_teardown_sdata(sdata); ieee80211_setup_sdata(sdata, type); } /* reset some values that shouldn't be kept across type changes */ sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sdata->local, sdata->local->oper_channel->band); sdata->drop_unencrypted = 0; if (type == NL80211_IFTYPE_STATION) sdata->u.mgd.use_4addr = false; return 0; } static void ieee80211_assign_perm_addr(struct ieee80211_local *local, u8 *perm_addr, enum nl80211_iftype type) { struct ieee80211_sub_if_data *sdata; u64 mask, start, addr, val, inc; u8 *m; u8 tmp_addr[ETH_ALEN]; int i; /* default ... something at least */ memcpy(perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN); if (is_zero_ether_addr(local->hw.wiphy->addr_mask) && local->hw.wiphy->n_addresses <= 1) return; mutex_lock(&local->iflist_mtx); switch (type) { case NL80211_IFTYPE_MONITOR: /* doesn't matter */ break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: /* match up with an AP interface */ list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_AP) continue; memcpy(perm_addr, sdata->vif.addr, ETH_ALEN); break; } /* keep default if no AP interface present */ break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: if (local->hw.flags & IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF) { list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) continue; if (!ieee80211_sdata_running(sdata)) continue; memcpy(perm_addr, sdata->vif.addr, ETH_ALEN); goto out_unlock; } } /* otherwise fall through */ default: /* assign a new address if possible -- try n_addresses first */ for (i = 0; i < local->hw.wiphy->n_addresses; i++) { bool used = false; list_for_each_entry(sdata, &local->interfaces, list) { if (memcmp(local->hw.wiphy->addresses[i].addr, sdata->vif.addr, ETH_ALEN) == 0) { used = true; break; } } if (!used) { memcpy(perm_addr, local->hw.wiphy->addresses[i].addr, ETH_ALEN); break; } } /* try mask if available */ if (is_zero_ether_addr(local->hw.wiphy->addr_mask)) break; m = local->hw.wiphy->addr_mask; mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); if (__ffs64(mask) + hweight64(mask) != fls64(mask)) { /* not a contiguous mask ... not handled now! */ pr_info("not contiguous\n"); break; } m = local->hw.wiphy->perm_addr; start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); inc = 1ULL<<__ffs64(mask); val = (start & mask); addr = (start & ~mask) | (val & mask); do { bool used = false; tmp_addr[5] = addr >> 0*8; tmp_addr[4] = addr >> 1*8; tmp_addr[3] = addr >> 2*8; tmp_addr[2] = addr >> 3*8; tmp_addr[1] = addr >> 4*8; tmp_addr[0] = addr >> 5*8; val += inc; list_for_each_entry(sdata, &local->interfaces, list) { if (memcmp(tmp_addr, sdata->vif.addr, ETH_ALEN) == 0) { used = true; break; } } if (!used) { memcpy(perm_addr, tmp_addr, ETH_ALEN); break; } addr = (start & ~mask) | (val & mask); } while (addr != start); break; } out_unlock: mutex_unlock(&local->iflist_mtx); } int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params) { struct net_device *ndev = NULL; struct ieee80211_sub_if_data *sdata = NULL; int ret, i; int txqs = 1; ASSERT_RTNL(); if (type == NL80211_IFTYPE_P2P_DEVICE) { struct wireless_dev *wdev; sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); if (!sdata) return -ENOMEM; wdev = &sdata->wdev; sdata->dev = NULL; strlcpy(sdata->name, name, IFNAMSIZ); ieee80211_assign_perm_addr(local, wdev->address, type); memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); } else { if (local->hw.queues >= IEEE80211_NUM_ACS) txqs = IEEE80211_NUM_ACS; ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, name, ieee80211_if_setup, txqs, 1); if (!ndev) return -ENOMEM; dev_net_set(ndev, wiphy_net(local->hw.wiphy)); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) ndev->needed_headroom = local->tx_headroom + 4*6 /* four MAC addresses */ + 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */ + 6 /* mesh */ + 8 /* rfc1042/bridge tunnel */ - ETH_HLEN /* ethernet hard_header_len */ + IEEE80211_ENCRYPT_HEADROOM; ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM; #endif ret = dev_alloc_name(ndev, ndev->name); if (ret < 0) { free_netdev(ndev); return ret; } ieee80211_assign_perm_addr(local, ndev->perm_addr, type); memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); /* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */ sdata = netdev_priv(ndev); ndev->ieee80211_ptr = &sdata->wdev; memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); memcpy(sdata->name, ndev->name, IFNAMSIZ); sdata->dev = ndev; } /* initialise type-independent data */ sdata->wdev.wiphy = local->hw.wiphy; sdata->local = local; #ifdef CONFIG_INET sdata->arp_filter_state = true; #endif for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) skb_queue_head_init(&sdata->fragments[i].skb_list); INIT_LIST_HEAD(&sdata->key_list); for (i = 0; i < IEEE80211_NUM_BANDS; i++) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[i]; sdata->rc_rateidx_mask[i] = sband ? (1 << sband->n_bitrates) - 1 : 0; if (sband) memcpy(sdata->rc_rateidx_mcs_mask[i], sband->ht_cap.mcs.rx_mask, sizeof(sdata->rc_rateidx_mcs_mask[i])); else memset(sdata->rc_rateidx_mcs_mask[i], 0, sizeof(sdata->rc_rateidx_mcs_mask[i])); } ieee80211_set_default_queues(sdata); /* setup type-dependent data */ ieee80211_setup_sdata(sdata, type); if (ndev) { if (params) { ndev->ieee80211_ptr->use_4addr = params->use_4addr; if (type == NL80211_IFTYPE_STATION) sdata->u.mgd.use_4addr = params->use_4addr; } ndev->features |= local->hw.netdev_features; ret = register_netdevice(ndev); if (ret) { free_netdev(ndev); return ret; } } mutex_lock(&local->iflist_mtx); list_add_tail_rcu(&sdata->list, &local->interfaces); mutex_unlock(&local->iflist_mtx); if (new_wdev) *new_wdev = &sdata->wdev; return 0; } void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) { ASSERT_RTNL(); mutex_lock(&sdata->local->iflist_mtx); list_del_rcu(&sdata->list); mutex_unlock(&sdata->local->iflist_mtx); synchronize_rcu(); if (sdata->dev) { unregister_netdevice(sdata->dev); } else { cfg80211_unregister_wdev(&sdata->wdev); kfree(sdata); } } void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata) { if (WARN_ON_ONCE(!test_bit(SDATA_STATE_RUNNING, &sdata->state))) return; ieee80211_do_stop(sdata, true); ieee80211_teardown_sdata(sdata); } /* * Remove all interfaces, may only be called at hardware unregistration * time because it doesn't do RCU-safe list removals. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) void ieee80211_remove_interfaces(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata, *tmp; LIST_HEAD(unreg_list); LIST_HEAD(wdev_list); ASSERT_RTNL(); mutex_lock(&local->iflist_mtx); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { list_del(&sdata->list); if (sdata->dev) unregister_netdevice_queue(sdata->dev, &unreg_list); else list_add(&sdata->list, &wdev_list); } mutex_unlock(&local->iflist_mtx); unregister_netdevice_many(&unreg_list); list_del(&unreg_list); list_for_each_entry_safe(sdata, tmp, &wdev_list, list) { list_del(&sdata->list); cfg80211_unregister_wdev(&sdata->wdev); kfree(sdata); } } #else void ieee80211_remove_interfaces(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata, *tmp; ASSERT_RTNL(); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { mutex_lock(&local->iflist_mtx); list_del(&sdata->list); mutex_unlock(&local->iflist_mtx); unregister_netdevice(sdata->dev); } } #endif static int netdev_notify(struct notifier_block *nb, unsigned long state, void *ndev) { struct net_device *dev = ndev; struct ieee80211_sub_if_data *sdata; if (state != NETDEV_CHANGENAME) return 0; if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) return 0; if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid) return 0; sdata = IEEE80211_DEV_TO_SUB_IF(dev); memcpy(sdata->name, dev->name, IFNAMSIZ); ieee80211_debugfs_rename_netdev(sdata); return 0; } static struct notifier_block mac80211_netdev_notifier = { .notifier_call = netdev_notify, }; int ieee80211_iface_init(void) { return register_netdevice_notifier(&mac80211_netdev_notifier); } void ieee80211_iface_exit(void) { unregister_netdevice_notifier(&mac80211_netdev_notifier); } compat-drivers-2012-09-18/net/mac80211/ieee80211_i.h0000644000175000017500000013270612026211315020277 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef IEEE80211_I_H #define IEEE80211_I_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "key.h" #include "sta_info.h" #include "debug.h" struct ieee80211_local; /* Maximum number of broadcast/multicast frames to buffer when some of the * associated stations are using power saving. */ #define AP_MAX_BC_BUFFER 128 /* Maximum number of frames buffered to all STAs, including multicast frames. * Note: increasing this limit increases the potential memory requirement. Each * frame can be up to about 2 kB long. */ #define TOTAL_MAX_TX_BUFFER 512 /* Required encryption head and tailroom */ #define IEEE80211_ENCRYPT_HEADROOM 8 #define IEEE80211_ENCRYPT_TAILROOM 18 /* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent * reception of at least three fragmented frames. This limit can be increased * by changing this define, at the cost of slower frame reassembly and * increased memory use (about 2 kB of RAM per entry). */ #define IEEE80211_FRAGMENT_MAX 4 #define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) #define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) /* * Some APs experience problems when working with U-APSD. Decrease the * probability of that happening by using legacy mode for all ACs but VO. * The AP that caused us trouble was a Cisco 4410N. It ignores our * setting, and always treats non-VO ACs as legacy. */ #define IEEE80211_DEFAULT_UAPSD_QUEUES \ IEEE80211_WMM_IE_STA_QOSINFO_AC_VO #define IEEE80211_DEFAULT_MAX_SP_LEN \ IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL struct ieee80211_fragment_entry { unsigned long first_frag_time; unsigned int seq; unsigned int rx_queue; unsigned int last_frag; unsigned int extra_len; struct sk_buff_head skb_list; int ccmp; /* Whether fragments were encrypted with CCMP */ u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ }; struct ieee80211_bss { /* don't want to look up all the time */ size_t ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; u32 device_ts; u8 dtim_period; bool wmm_used; bool uapsd_supported; unsigned long last_probe_resp; #ifdef CONFIG_MAC80211_MESH u8 *mesh_id; size_t mesh_id_len; u8 *mesh_cfg; #endif #define IEEE80211_MAX_SUPP_RATES 32 u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; /* * During association, we save an ERP value from a probe response so * that we can feed ERP info to the driver when handling the * association completes. these fields probably won't be up-to-date * otherwise, you probably don't want to use them. */ bool has_erp_value; u8 erp_value; /* Keep track of the corruption of the last beacon/probe response. */ u8 corrupt_data; /* Keep track of what bits of information we have valid info for. */ u8 valid_data; }; /** * enum ieee80211_corrupt_data_flags - BSS data corruption flags * @IEEE80211_BSS_CORRUPT_BEACON: last beacon frame received was corrupted * @IEEE80211_BSS_CORRUPT_PROBE_RESP: last probe response received was corrupted * * These are bss flags that are attached to a bss in the * @corrupt_data field of &struct ieee80211_bss. */ enum ieee80211_bss_corrupt_data_flags { IEEE80211_BSS_CORRUPT_BEACON = BIT(0), IEEE80211_BSS_CORRUPT_PROBE_RESP = BIT(1) }; /** * enum ieee80211_valid_data_flags - BSS valid data flags * @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE * * These are bss flags that are attached to a bss in the * @valid_data field of &struct ieee80211_bss. They show which parts * of the data structure were recieved as a result of an un-corrupted * beacon/probe response. */ enum ieee80211_bss_valid_data_flags { IEEE80211_BSS_VALID_DTIM = BIT(0), IEEE80211_BSS_VALID_WMM = BIT(1), IEEE80211_BSS_VALID_RATES = BIT(2), IEEE80211_BSS_VALID_ERP = BIT(3) }; static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss) { #ifdef CONFIG_MAC80211_MESH return bss->mesh_cfg; #endif return NULL; } static inline u8 *bss_mesh_id(struct ieee80211_bss *bss) { #ifdef CONFIG_MAC80211_MESH return bss->mesh_id; #endif return NULL; } static inline u8 bss_mesh_id_len(struct ieee80211_bss *bss) { #ifdef CONFIG_MAC80211_MESH return bss->mesh_id_len; #endif return 0; } typedef unsigned __bitwise__ ieee80211_tx_result; #define TX_CONTINUE ((__force ieee80211_tx_result) 0u) #define TX_DROP ((__force ieee80211_tx_result) 1u) #define TX_QUEUED ((__force ieee80211_tx_result) 2u) #define IEEE80211_TX_UNICAST BIT(1) #define IEEE80211_TX_PS_BUFFERED BIT(2) struct ieee80211_tx_data { struct sk_buff *skb; struct sk_buff_head skbs; struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; struct ieee80211_key *key; unsigned int flags; }; typedef unsigned __bitwise__ ieee80211_rx_result; #define RX_CONTINUE ((__force ieee80211_rx_result) 0u) #define RX_DROP_UNUSABLE ((__force ieee80211_rx_result) 1u) #define RX_DROP_MONITOR ((__force ieee80211_rx_result) 2u) #define RX_QUEUED ((__force ieee80211_rx_result) 3u) /** * enum ieee80211_packet_rx_flags - packet RX flags * @IEEE80211_RX_RA_MATCH: frame is destined to interface currently processed * (incl. multicast frames) * @IEEE80211_RX_FRAGMENTED: fragmented frame * @IEEE80211_RX_AMSDU: a-MSDU packet * @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed * @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering * * These are per-frame flags that are attached to a frame in the * @rx_flags field of &struct ieee80211_rx_status. */ enum ieee80211_packet_rx_flags { IEEE80211_RX_RA_MATCH = BIT(1), IEEE80211_RX_FRAGMENTED = BIT(2), IEEE80211_RX_AMSDU = BIT(3), IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4), IEEE80211_RX_DEFERRED_RELEASE = BIT(5), }; /** * enum ieee80211_rx_flags - RX data flags * * @IEEE80211_RX_CMNTR: received on cooked monitor already * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported * to cfg80211_report_obss_beacon(). * * These flags are used across handling multiple interfaces * for a single frame. */ enum ieee80211_rx_flags { IEEE80211_RX_CMNTR = BIT(0), IEEE80211_RX_BEACON_REPORTED = BIT(1), }; struct ieee80211_rx_data { struct sk_buff *skb; struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; struct ieee80211_key *key; unsigned int flags; /* * Index into sequence numbers array, 0..16 * since the last (16) is used for non-QoS, * will be 16 on non-QoS frames. */ int seqno_idx; /* * Index into the security IV/PN arrays, 0..16 * since the last (16) is used for CCMP-encrypted * management frames, will be set to 16 on mgmt * frames and 0 on non-QoS frames. */ int security_idx; u32 tkip_iv32; u16 tkip_iv16; }; struct beacon_data { u8 *head, *tail; int head_len, tail_len; struct rcu_head rcu_head; }; struct probe_resp { struct rcu_head rcu_head; int len; u8 data[0]; }; struct ieee80211_if_ap { struct beacon_data __rcu *beacon; struct probe_resp __rcu *probe_resp; struct list_head vlans; /* yes, this looks ugly, but guarantees that we can later use * bitmap_empty :) * NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */ u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; struct sk_buff_head ps_bc_buf; atomic_t num_sta_ps; /* number of stations in PS mode */ atomic_t num_mcast_sta; /* number of stations receiving multicast */ int dtim_count; bool dtim_bc_mc; }; struct ieee80211_if_wds { struct sta_info *sta; u8 remote_addr[ETH_ALEN]; }; struct ieee80211_if_vlan { struct list_head list; /* used for all tx if the VLAN is configured to 4-addr mode */ struct sta_info __rcu *sta; }; struct mesh_stats { __u32 fwded_mcast; /* Mesh forwarded multicast frames */ __u32 fwded_unicast; /* Mesh forwarded unicast frames */ __u32 fwded_frames; /* Mesh total forwarded frames */ __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ __u32 dropped_frames_no_route; /* Not transmitted, no route found */ __u32 dropped_frames_congestion;/* Not forwarded due to congestion */ atomic_t estab_plinks; }; #define PREQ_Q_F_START 0x1 #define PREQ_Q_F_REFRESH 0x2 struct mesh_preq_queue { struct list_head list; u8 dst[ETH_ALEN]; u8 flags; }; #if HZ/100 == 0 #define IEEE80211_ROC_MIN_LEFT 1 #else #define IEEE80211_ROC_MIN_LEFT (HZ/100) #endif struct ieee80211_roc_work { struct list_head list; struct list_head dependents; struct delayed_work work; struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *chan; enum nl80211_channel_type chan_type; bool started, abort, hw_begun, notified; unsigned long hw_start_time; u32 duration, req_duration; struct sk_buff *frame; u64 mgmt_tx_cookie; }; /* flags used in struct ieee80211_if_managed.flags */ enum ieee80211_sta_flags { IEEE80211_STA_BEACON_POLL = BIT(0), IEEE80211_STA_CONNECTION_POLL = BIT(1), IEEE80211_STA_CONTROL_PORT = BIT(2), IEEE80211_STA_DISABLE_11N = BIT(4), IEEE80211_STA_CSA_RECEIVED = BIT(5), IEEE80211_STA_MFP_ENABLED = BIT(6), IEEE80211_STA_UAPSD_ENABLED = BIT(7), IEEE80211_STA_NULLFUNC_ACKED = BIT(8), IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), IEEE80211_STA_DISABLE_40MHZ = BIT(10), IEEE80211_STA_DISABLE_VHT = BIT(11), }; struct ieee80211_mgd_auth_data { struct cfg80211_bss *bss; unsigned long timeout; int tries; u16 algorithm, expected_transaction; u8 key[WLAN_KEY_LEN_WEP104]; u8 key_len, key_idx; bool done; size_t ie_len; u8 ie[]; }; struct ieee80211_mgd_assoc_data { struct cfg80211_bss *bss; const u8 *supp_rates; unsigned long timeout; int tries; u16 capability; u8 prev_bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; u8 supp_rates_len; bool wmm, uapsd; bool have_beacon; bool sent_assoc; bool synced; u8 ap_ht_param; size_t ie_len; u8 ie[]; }; struct ieee80211_if_managed { struct timer_list timer; struct timer_list conn_mon_timer; struct timer_list bcn_mon_timer; struct timer_list chswitch_timer; struct work_struct monitor_work; struct work_struct chswitch_work; struct work_struct beacon_connection_loss_work; unsigned long beacon_timeout; unsigned long probe_timeout; int probe_send_count; bool nullfunc_failed; struct mutex mtx; struct cfg80211_bss *associated; struct ieee80211_mgd_auth_data *auth_data; struct ieee80211_mgd_assoc_data *assoc_data; u8 bssid[ETH_ALEN]; u16 aid; unsigned long timers_running; /* used for quiesce/restart */ bool powersave; /* powersave requested for this iface */ bool broken_ap; /* AP is broken -- turn off powersave */ enum ieee80211_smps_mode req_smps, /* requested smps mode */ ap_smps, /* smps mode AP thinks we're in */ driver_smps_mode; /* smps mode request */ struct work_struct request_smps_work; unsigned int flags; bool beacon_crc_valid; u32 beacon_crc; enum { IEEE80211_MFP_DISABLED, IEEE80211_MFP_OPTIONAL, IEEE80211_MFP_REQUIRED } mfp; /* management frame protection */ /* * Bitmask of enabled u-apsd queues, * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association * to take effect. */ unsigned int uapsd_queues; /* * Maximum number of buffered frames AP can deliver during a * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. * Needs a new association to take effect. */ unsigned int uapsd_max_sp_len; int wmm_last_param_set; u8 use_4addr; /* Signal strength from the last Beacon frame in the current BSS. */ int last_beacon_signal; /* * Weighted average of the signal strength from Beacon frames in the * current BSS. This is in units of 1/16 of the signal unit to maintain * accuracy and to speed up calculations, i.e., the value need to be * divided by 16 to get the actual value. */ int ave_beacon_signal; /* * Number of Beacon frames used in ave_beacon_signal. This can be used * to avoid generating less reliable cqm events that would be based * only on couple of received frames. */ unsigned int count_beacon_signal; /* * Last Beacon frame signal strength average (ave_beacon_signal / 16) * that triggered a cqm event. 0 indicates that no event has been * generated for the current association. */ int last_cqm_event_signal; /* * State variables for keeping track of RSSI of the AP currently * connected to and informing driver when RSSI has gone * below/above a certain threshold. */ int rssi_min_thold, rssi_max_thold; int last_ave_beacon_signal; struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */ struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ }; struct ieee80211_if_ibss { struct timer_list timer; struct mutex mtx; unsigned long last_scan_completed; u32 basic_rates; bool timer_running; bool fixed_bssid; bool fixed_channel; bool privacy; bool control_port; unsigned int auth_frame_registrations; u8 bssid[ETH_ALEN] __aligned(2); u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len, ie_len; u8 *ie; struct ieee80211_channel *channel; enum nl80211_channel_type channel_type; unsigned long ibss_join_req; /* probe response/beacon for IBSS */ struct sk_buff __rcu *presp; struct sk_buff *skb; spinlock_t incomplete_lock; struct list_head incomplete_stations; enum { IEEE80211_IBSS_MLME_SEARCH, IEEE80211_IBSS_MLME_JOINED, } state; }; /** * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface * * these declarations define the interface, which enables * vendor-specific mesh synchronization * */ struct ieee802_11_elems; struct ieee80211_mesh_sync_ops { void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata, u16 stype, struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, struct ieee80211_rx_status *rx_status); void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata); /* add other framework functions here */ }; struct ieee80211_if_mesh { struct timer_list housekeeping_timer; struct timer_list mesh_path_timer; struct timer_list mesh_path_root_timer; unsigned long timers_running; unsigned long wrkq_flags; u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; size_t mesh_id_len; /* Active Path Selection Protocol Identifier */ u8 mesh_pp_id; /* Active Path Selection Metric Identifier */ u8 mesh_pm_id; /* Congestion Control Mode Identifier */ u8 mesh_cc_id; /* Synchronization Protocol Identifier */ u8 mesh_sp_id; /* Authentication Protocol Identifier */ u8 mesh_auth_id; /* Local mesh Sequence Number */ u32 sn; /* Last used PREQ ID */ u32 preq_id; atomic_t mpaths; /* Timestamp of last SN update */ unsigned long last_sn_update; /* Time when it's ok to send next PERR */ unsigned long next_perr; /* Timestamp of last PREQ sent */ unsigned long last_preq; struct mesh_rmc *rmc; spinlock_t mesh_preq_queue_lock; struct mesh_preq_queue preq_queue; int preq_queue_len; struct mesh_stats mshstats; struct mesh_config mshcfg; u32 mesh_seqnum; bool accepting_plinks; int num_gates; const u8 *ie; u8 ie_len; enum { IEEE80211_MESH_SEC_NONE = 0x0, IEEE80211_MESH_SEC_AUTHED = 0x1, IEEE80211_MESH_SEC_SECURED = 0x2, } security; /* Extensible Synchronization Framework */ struct ieee80211_mesh_sync_ops *sync_ops; s64 sync_offset_clockdrift_max; spinlock_t sync_offset_lock; bool adjusting_tbtt; }; #ifdef CONFIG_MAC80211_MESH #define IEEE80211_IFSTA_MESH_CTR_INC(msh, name) \ do { (msh)->mshstats.name++; } while (0) #else #define IEEE80211_IFSTA_MESH_CTR_INC(msh, name) \ do { } while (0) #endif /** * enum ieee80211_sub_if_data_flags - virtual interface flags * * @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets * @IEEE80211_SDATA_PROMISC: interface is promisc * @IEEE80211_SDATA_OPERATING_GMODE: operating in G-only mode * @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between * associated stations and deliver multicast frames both * back to wireless media and to the local net stack. * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver */ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_ALLMULTI = BIT(0), IEEE80211_SDATA_PROMISC = BIT(1), IEEE80211_SDATA_OPERATING_GMODE = BIT(2), IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4), IEEE80211_SDATA_IN_DRIVER = BIT(5), }; /** * enum ieee80211_sdata_state_bits - virtual interface state bits * @SDATA_STATE_RUNNING: virtual interface is up & running; this * mirrors netif_running() but is separate for interface type * change handling while the interface is up * @SDATA_STATE_OFFCHANNEL: This interface is currently in offchannel * mode, so queues are stopped */ enum ieee80211_sdata_state_bits { SDATA_STATE_RUNNING, SDATA_STATE_OFFCHANNEL, }; struct ieee80211_sub_if_data { struct list_head list; struct wireless_dev wdev; /* keys */ struct list_head key_list; /* count for keys needing tailroom space allocation */ int crypto_tx_tailroom_needed_cnt; struct net_device *dev; struct ieee80211_local *local; unsigned int flags; unsigned long state; int drop_unencrypted; char name[IFNAMSIZ]; /* to detect idle changes */ bool old_idle; /* Fragment table for host-based reassembly */ struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; unsigned int fragment_next; /* TID bitmap for NoAck policy */ u16 noack_map; /* bit field of ACM bits (BIT(802.1D tag)) */ u8 wmm_acm; struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key __rcu *default_unicast_key; struct ieee80211_key __rcu *default_multicast_key; struct ieee80211_key __rcu *default_mgmt_key; u16 sequence_number; __be16 control_port_protocol; bool control_port_no_encrypt; struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; struct work_struct work; struct sk_buff_head skb_queue; bool arp_filter_state; /* * AP this belongs to: self in AP mode and * corresponding AP in VLAN mode, NULL for * all others (might be needed later in IBSS) */ struct ieee80211_if_ap *bss; /* bitmap of allowed (non-MCS) rate indexes for rate control */ u32 rc_rateidx_mask[IEEE80211_NUM_BANDS]; u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN]; union { struct ieee80211_if_ap ap; struct ieee80211_if_wds wds; struct ieee80211_if_vlan vlan; struct ieee80211_if_managed mgd; struct ieee80211_if_ibss ibss; struct ieee80211_if_mesh mesh; u32 mntr_flags; } u; #ifdef CONFIG_MAC80211_DEBUGFS struct { struct dentry *dir; struct dentry *subdir_stations; struct dentry *default_unicast_key; struct dentry *default_multicast_key; struct dentry *default_mgmt_key; } debugfs; #endif /* must be last, dynamically sized area in this! */ struct ieee80211_vif vif; }; static inline struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p) { return container_of(p, struct ieee80211_sub_if_data, vif); } enum sdata_queue_type { IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0, IEEE80211_SDATA_QUEUE_AGG_START = 1, IEEE80211_SDATA_QUEUE_AGG_STOP = 2, }; enum { IEEE80211_RX_MSG = 1, IEEE80211_TX_STATUS_MSG = 2, IEEE80211_EOSP_MSG = 3, }; struct skb_eosp_msg_data { u8 sta[ETH_ALEN], iface[ETH_ALEN]; }; enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_DRIVER, IEEE80211_QUEUE_STOP_REASON_PS, IEEE80211_QUEUE_STOP_REASON_CSA, IEEE80211_QUEUE_STOP_REASON_AGGREGATION, IEEE80211_QUEUE_STOP_REASON_SUSPEND, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, }; #ifdef CONFIG_MAC80211_LEDS struct tpt_led_trigger { struct led_trigger trig; char name[32]; const struct ieee80211_tpt_blink *blink_table; unsigned int blink_table_len; struct timer_list timer; unsigned long prev_traffic; unsigned long tx_bytes, rx_bytes; unsigned int active, want; bool running; }; #endif /** * mac80211 scan flags - currently active scan mode * * @SCAN_SW_SCANNING: We're currently in the process of scanning but may as * well be on the operating channel * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to * determine if we are on the operating channel or not * @SCAN_ONCHANNEL_SCANNING: Do a software scan on only the current operating * channel. This should not interrupt normal traffic. * @SCAN_COMPLETED: Set for our scan work function when the driver reported * that the scan completed. * @SCAN_ABORTED: Set for our scan work function when the driver reported * a scan complete for an aborted scan. */ enum { SCAN_SW_SCANNING, SCAN_HW_SCANNING, SCAN_ONCHANNEL_SCANNING, SCAN_COMPLETED, SCAN_ABORTED, }; /** * enum mac80211_scan_state - scan state machine states * * @SCAN_DECISION: Main entry point to the scan state machine, this state * determines if we should keep on scanning or switch back to the * operating channel * @SCAN_SET_CHANNEL: Set the next channel to be scanned * @SCAN_SEND_PROBE: Send probe requests and wait for probe responses * @SCAN_SUSPEND: Suspend the scan and go back to operating channel to * send out data * @SCAN_RESUME: Resume the scan and scan the next channel */ enum mac80211_scan_state { SCAN_DECISION, SCAN_SET_CHANNEL, SCAN_SEND_PROBE, SCAN_SUSPEND, SCAN_RESUME, }; struct ieee80211_local { /* embed the driver visible part. * don't cast (use the static inlines below), but we keep * it first anyway so they become a no-op */ struct ieee80211_hw hw; const struct ieee80211_ops *ops; /* * private workqueue to mac80211. mac80211 makes this accessible * via ieee80211_queue_work() */ struct workqueue_struct *workqueue; unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; int open_count; int monitors, cooked_mntrs; /* number of interfaces with corresponding FIF_ flags */ int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll, fif_probe_req; int probe_req_reg; unsigned int filter_flags; /* FIF_* */ bool wiphy_ciphers_allocated; /* protects the aggregated multicast list and filter calls */ spinlock_t filter_lock; /* used for uploading changed mc list */ struct work_struct reconfig_filter; /* used to reconfigure hardware SM PS */ struct work_struct recalc_smps; /* aggregated multicast list */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list mc_list; #else struct dev_addr_list *mc_list; int mc_count; #endif bool tim_in_locked_section; /* see ieee80211_beacon_get() */ /* * suspended is true if we finished all the suspend _and_ we have * not yet come up from resume. This is to be used by mac80211 * to ensure driver sanity during suspend and mac80211's own * sanity. It can eventually be used for WoW as well. */ bool suspended; /* * Resuming is true while suspended, but when we're reprogramming the * hardware -- at that time it's allowed to use ieee80211_queue_work() * again even though some other parts of the stack are still suspended * and we still drop received frames to avoid waking the stack. */ bool resuming; /* * quiescing is true during the suspend process _only_ to * ease timer cancelling etc. */ bool quiescing; /* device is started */ bool started; /* device is during a HW reconfig */ bool in_reconfig; /* wowlan is enabled -- don't reconfig on resume */ bool wowlan; int tx_headroom; /* required headroom for hardware/radiotap */ /* Tasklet and skb queue to process calls from IRQ mode. All frames * added to skb_queue will be processed, but frames in * skb_queue_unreliable may be dropped if the total length of these * queues increases over the limit. */ #define IEEE80211_IRQSAFE_QUEUE_LIMIT 128 struct tasklet_struct tasklet; struct sk_buff_head skb_queue; struct sk_buff_head skb_queue_unreliable; /* * Internal FIFO queue which is shared between multiple rx path * stages. Its main task is to provide a serialization mechanism, * so all rx handlers can enjoy having exclusive access to their * private data structures. */ struct sk_buff_head rx_skb_queue; bool running_rx_handler; /* protected by rx_skb_queue.lock */ /* Station data */ /* * The mutex only protects the list, hash table and * counter, reads are done with RCU. */ struct mutex sta_mtx; spinlock_t tim_lock; unsigned long num_sta; struct list_head sta_list; struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; int sta_generation; struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; atomic_t agg_queue_stop[IEEE80211_MAX_QUEUES]; /* number of interfaces with corresponding IFF_ flags */ atomic_t iff_allmultis, iff_promiscs; struct rate_control_ref *rate_ctrl; struct crypto_cipher *wep_tx_tfm; struct crypto_cipher *wep_rx_tfm; u32 wep_iv; /* see iface.c */ struct list_head interfaces; struct mutex iflist_mtx; /* * Key mutex, protects sdata's key_list and sta_info's * key pointers (write access, they're RCU.) */ struct mutex key_mtx; /* mutex for scan and work locking */ struct mutex mtx; /* Scanning and BSS list */ unsigned long scanning; struct cfg80211_ssid scan_ssid; struct cfg80211_scan_request *int_scan_req; struct cfg80211_scan_request *scan_req, *hw_scan_req; struct ieee80211_channel *scan_channel; enum ieee80211_band hw_scan_band; int scan_channel_idx; int scan_ies_len; struct ieee80211_sched_scan_ies sched_scan_ies; struct work_struct sched_scan_stopped_work; struct ieee80211_sub_if_data __rcu *sched_scan_sdata; unsigned long leave_oper_channel_time; enum mac80211_scan_state next_scan_state; struct delayed_work scan_work; struct ieee80211_sub_if_data __rcu *scan_sdata; enum nl80211_channel_type _oper_channel_type; struct ieee80211_channel *oper_channel, *csa_channel; /* Temporary remain-on-channel for off-channel operations */ struct ieee80211_channel *tmp_channel; enum nl80211_channel_type tmp_channel_type; /* SNMP counters */ /* dot11CountersTable */ u32 dot11TransmittedFragmentCount; u32 dot11MulticastTransmittedFrameCount; u32 dot11FailedCount; u32 dot11RetryCount; u32 dot11MultipleRetryCount; u32 dot11FrameDuplicateCount; u32 dot11ReceivedFragmentCount; u32 dot11MulticastReceivedFrameCount; u32 dot11TransmittedFrameCount; #ifdef CONFIG_MAC80211_LEDS int tx_led_counter, rx_led_counter; struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led; struct tpt_led_trigger *tpt_led_trigger; char tx_led_name[32], rx_led_name[32], assoc_led_name[32], radio_led_name[32]; #endif #ifdef CONFIG_MAC80211_DEBUG_COUNTERS /* TX/RX handler statistics */ unsigned int tx_handlers_drop; unsigned int tx_handlers_queued; unsigned int tx_handlers_drop_unencrypted; unsigned int tx_handlers_drop_fragment; unsigned int tx_handlers_drop_wep; unsigned int tx_handlers_drop_not_assoc; unsigned int tx_handlers_drop_unauth_port; unsigned int rx_handlers_drop; unsigned int rx_handlers_queued; unsigned int rx_handlers_drop_nullfunc; unsigned int rx_handlers_drop_defrag; unsigned int rx_handlers_drop_short; unsigned int tx_expand_skb_head; unsigned int tx_expand_skb_head_cloned; unsigned int rx_expand_skb_head; unsigned int rx_expand_skb_head2; unsigned int rx_handlers_fragments; unsigned int tx_status_drop; #define I802_DEBUG_INC(c) (c)++ #else /* CONFIG_MAC80211_DEBUG_COUNTERS */ #define I802_DEBUG_INC(c) do { } while (0) #endif /* CONFIG_MAC80211_DEBUG_COUNTERS */ int total_ps_buffered; /* total number of all buffered unicast and * multicast packets for power saving stations */ bool pspolling; bool offchannel_ps_enabled; /* * PS can only be enabled when we have exactly one managed * interface (and monitors) in PS, this then points there. */ struct ieee80211_sub_if_data *ps_sdata; struct work_struct dynamic_ps_enable_work; struct work_struct dynamic_ps_disable_work; struct timer_list dynamic_ps_timer; struct notifier_block network_latency_notifier; struct notifier_block ifa_notifier; /* * The dynamic ps timeout configured from user space via WEXT - * this will override whatever chosen by mac80211 internally. */ int dynamic_ps_forced_timeout; int dynamic_ps_user_timeout; bool disable_dynamic_ps; int user_power_level; /* in dBm */ int power_constr_level; /* in dBm */ enum ieee80211_smps_mode smps_mode; struct work_struct restart_work; #ifdef CONFIG_MAC80211_DEBUGFS struct local_debugfsdentries { struct dentry *rcdir; struct dentry *keys; } debugfs; #endif /* * Remain-on-channel support */ struct list_head roc_list; struct work_struct hw_roc_start, hw_roc_done; unsigned long hw_roc_start_time; struct idr ack_status_frames; spinlock_t ack_status_lock; struct ieee80211_sub_if_data __rcu *p2p_sdata; /* dummy netdev for use w/ NAPI */ struct net_device napi_dev; struct napi_struct napi; /* virtual monitor interface */ struct ieee80211_sub_if_data __rcu *monitor_sdata; }; static inline struct ieee80211_sub_if_data * IEEE80211_DEV_TO_SUB_IF(struct net_device *dev) { return netdev_priv(dev); } static inline struct ieee80211_sub_if_data * IEEE80211_WDEV_TO_SUB_IF(struct wireless_dev *wdev) { return container_of(wdev, struct ieee80211_sub_if_data, wdev); } /* this struct represents 802.11n's RA/TID combination */ struct ieee80211_ra_tid { u8 ra[ETH_ALEN]; u16 tid; }; /* Parsed Information Elements */ struct ieee802_11_elems { u8 *ie_start; size_t total_len; /* pointers to IEs */ u8 *ssid; u8 *supp_rates; u8 *fh_params; u8 *ds_params; u8 *cf_params; struct ieee80211_tim_ie *tim; u8 *ibss_params; u8 *challenge; u8 *wpa; u8 *rsn; u8 *erp_info; u8 *ext_supp_rates; u8 *wmm_info; u8 *wmm_param; struct ieee80211_ht_cap *ht_cap_elem; struct ieee80211_ht_operation *ht_operation; struct ieee80211_meshconf_ie *mesh_config; u8 *mesh_id; u8 *peering; u8 *preq; u8 *prep; u8 *perr; struct ieee80211_rann_ie *rann; struct ieee80211_channel_sw_ie *ch_switch_ie; u8 *country_elem; u8 *pwr_constr_elem; u8 *quiet_elem; /* first quite element */ u8 *timeout_int; /* length of them, respectively */ u8 ssid_len; u8 supp_rates_len; u8 fh_params_len; u8 ds_params_len; u8 cf_params_len; u8 tim_len; u8 ibss_params_len; u8 challenge_len; u8 wpa_len; u8 rsn_len; u8 erp_info_len; u8 ext_supp_rates_len; u8 wmm_info_len; u8 wmm_param_len; u8 mesh_id_len; u8 peering_len; u8 preq_len; u8 prep_len; u8 perr_len; u8 country_elem_len; u8 pwr_constr_elem_len; u8 quiet_elem_len; u8 num_of_quiet_elem; /* can be more the one */ u8 timeout_int_len; /* whether a parse error occurred while retrieving these elements */ bool parse_error; }; static inline struct ieee80211_local *hw_to_local( struct ieee80211_hw *hw) { return container_of(hw, struct ieee80211_local, hw); } static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) { return ether_addr_equal(raddr, addr) || is_broadcast_ether_addr(raddr); } int ieee80211_hw_config(struct ieee80211_local *local, u32 changed); void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx); void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u32 changed); void ieee80211_configure_filter(struct ieee80211_local *local); u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); /* STA code */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct cfg80211_auth_request *req); int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req); int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, struct cfg80211_deauth_request *req); int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_disassoc_request *req); void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata); int ieee80211_max_network_latency(struct notifier_block *nb, unsigned long data, void *dummy); int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel_sw_ie *sw_elem, struct ieee80211_bss *bss, u64 timestamp); void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata); /* IBSS code */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const u8 *addr, u32 supp_rates); int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, struct cfg80211_ibss_params *params); int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); /* mesh code */ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); /* scan/BSS handling */ void ieee80211_scan_work(struct work_struct *work); int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len, struct ieee80211_channel *chan); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); void ieee80211_scan_cancel(struct ieee80211_local *local); void ieee80211_run_deferred_scan(struct ieee80211_local *local); void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb); void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, struct ieee80211_channel *channel, bool beacon); void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); /* scheduled scan handling */ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); void ieee80211_sched_scan_stopped_work(struct work_struct *work); /* off-channel helpers */ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, bool offchannel_ps_enable); void ieee80211_offchannel_return(struct ieee80211_local *local, bool offchannel_ps_disable); void ieee80211_roc_setup(struct ieee80211_local *local); void ieee80211_start_next_roc(struct ieee80211_local *local); void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata); void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc); void ieee80211_sw_roc_work(struct work_struct *work); void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); /* interface handling */ int ieee80211_iface_init(void); void ieee80211_iface_exit(void); int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params); int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type); void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata); void ieee80211_remove_interfaces(struct ieee80211_local *local); void ieee80211_recalc_idle(struct ieee80211_local *local); void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, const int offset); int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up); void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata); static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) { return test_bit(SDATA_STATE_RUNNING, &sdata->state); } /* tx handling */ void ieee80211_clear_tx_pending(struct ieee80211_local *local); void ieee80211_tx_pending(unsigned long data); netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); /* HT */ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_ht_cap *ht_cap); void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_ht_cap *ht_cap_ie, struct ieee80211_sta_ht_cap *ht_cap); void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, u16 initiator, u16 reason_code); int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps, const u8 *da, const u8 *bssid); void ieee80211_request_smps_work(struct work_struct *work); void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx); void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len); void ieee80211_process_addba_resp(struct ieee80211_local *local, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len); void ieee80211_process_addba_request(struct ieee80211_local *local, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len); int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, enum ieee80211_back_parties initiator, bool tx); int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, enum ieee80211_back_parties initiator, bool tx); void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid); void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); void ieee80211_ba_session_work(struct work_struct *work); void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); /* Suspend/resume and hw reconfiguration */ int ieee80211_reconfig(struct ieee80211_local *local); void ieee80211_stop_device(struct ieee80211_local *local); #ifdef CONFIG_PM int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); static inline int __ieee80211_resume(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), "%s: resume with hardware scan still in progress\n", wiphy_name(hw->wiphy)); return ieee80211_reconfig(hw_to_local(hw)); } #else static inline int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { return 0; } static inline int __ieee80211_resume(struct ieee80211_hw *hw) { return 0; } #endif /* utility functions/constants */ extern void *mac80211_wiphy_privid; /* for wiphy privid */ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, enum nl80211_iftype type); int ieee80211_frame_duration(enum ieee80211_band band, size_t len, int rate, int erp, int short_preamble); void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, struct ieee80211_hdr *hdr, const u8 *tsc, gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, bool bss_notify); void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid); static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ ieee80211_tx_skb_tid(sdata, skb, 7); } void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems); u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, struct ieee802_11_elems *elems, u64 filter, u32 crc); u32 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band); void ieee80211_dynamic_ps_enable_work(struct work_struct *work); void ieee80211_dynamic_ps_disable_work(struct work_struct *work); void ieee80211_dynamic_ps_timer(unsigned long data); void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, int powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr, bool ack); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason); void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason); void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue); void ieee80211_add_pending_skb(struct ieee80211_local *local, struct sk_buff *skb); void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, struct sk_buff_head *skbs, void (*fn)(void *data), void *data); static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local, struct sk_buff_head *skbs) { ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); } void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u8 *extra, size_t extra_len, const u8 *bssid, const u8 *da, const u8 *key, u8 key_len, u8 key_idx); int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, const u8 *ie, size_t ie_len, enum ieee80211_band band, u32 rate_mask, u8 channel); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, bool no_cck, struct ieee80211_channel *channel); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, const u8 *supp_rates); u32 ieee80211_sta_get_rates(struct ieee80211_local *local, struct ieee802_11_elems *elems, enum ieee80211_band band, u32 *basic_rates); int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode); void ieee80211_recalc_smps(struct ieee80211_local *local); size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type, u16 prot_mode); u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u32 cap); int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool need_basic, enum ieee80211_band band); int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool need_basic, enum ieee80211_band band); /* channel management */ enum ieee80211_chan_mode { CHAN_MODE_UNDEFINED, CHAN_MODE_HOPPING, CHAN_MODE_FIXED, }; enum ieee80211_chan_mode ieee80211_get_channel_mode(struct ieee80211_local *local, struct ieee80211_sub_if_data *ignore); bool ieee80211_set_channel_type(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_channel_type chantype); enum nl80211_channel_type ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else #define debug_noinline #endif #endif /* IEEE80211_I_H */ compat-drivers-2012-09-18/net/mac80211/driver-ops.h0000644000175000017500000005260312026211315020553 0ustar mcgrofmcgrof#ifndef __MAC80211_DRIVER_OPS #define __MAC80211_DRIVER_OPS #include #include "ieee80211_i.h" #include "trace.h" static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) { WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); } static inline struct ieee80211_sub_if_data * get_bss_sdata(struct ieee80211_sub_if_data *sdata) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); return sdata; } static inline void drv_tx(struct ieee80211_local *local, struct ieee80211_tx_control *control, struct sk_buff *skb) { local->ops->tx(&local->hw, control, skb); } static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, u32 sset, u8 *data) { struct ieee80211_local *local = sdata->local; if (local->ops->get_et_strings) { trace_drv_get_et_strings(local, sset); local->ops->get_et_strings(&local->hw, &sdata->vif, sset, data); trace_drv_return_void(local); } } static inline void drv_get_et_stats(struct ieee80211_sub_if_data *sdata, struct ethtool_stats *stats, u64 *data) { struct ieee80211_local *local = sdata->local; if (local->ops->get_et_stats) { trace_drv_get_et_stats(local); local->ops->get_et_stats(&local->hw, &sdata->vif, stats, data); trace_drv_return_void(local); } } static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata, int sset) { struct ieee80211_local *local = sdata->local; int rv = 0; if (local->ops->get_et_sset_count) { trace_drv_get_et_sset_count(local, sset); rv = local->ops->get_et_sset_count(&local->hw, &sdata->vif, sset); trace_drv_return_int(local, rv); } return rv; } static inline int drv_start(struct ieee80211_local *local) { int ret; might_sleep(); trace_drv_start(local); local->started = true; smp_mb(); ret = local->ops->start(&local->hw); trace_drv_return_int(local, ret); return ret; } static inline void drv_stop(struct ieee80211_local *local) { might_sleep(); trace_drv_stop(local); local->ops->stop(&local->hw); trace_drv_return_void(local); /* sync away all work on the tasklet before clearing started */ tasklet_disable(&local->tasklet); tasklet_enable(&local->tasklet); barrier(); local->started = false; } #ifdef CONFIG_PM static inline int drv_suspend(struct ieee80211_local *local, struct cfg80211_wowlan *wowlan) { int ret; might_sleep(); trace_drv_suspend(local); ret = local->ops->suspend(&local->hw, wowlan); trace_drv_return_int(local, ret); return ret; } static inline int drv_resume(struct ieee80211_local *local) { int ret; might_sleep(); trace_drv_resume(local); ret = local->ops->resume(&local->hw); trace_drv_return_int(local, ret); return ret; } static inline void drv_set_wakeup(struct ieee80211_local *local, bool enabled) { might_sleep(); if (!local->ops->set_wakeup) return; trace_drv_set_wakeup(local, enabled); local->ops->set_wakeup(&local->hw, enabled); trace_drv_return_void(local); } #endif static inline int drv_add_interface(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { int ret; might_sleep(); if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || (sdata->vif.type == NL80211_IFTYPE_MONITOR && !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)))) return -EINVAL; trace_drv_add_interface(local, sdata); ret = local->ops->add_interface(&local->hw, &sdata->vif); trace_drv_return_int(local, ret); if (ret == 0) sdata->flags |= IEEE80211_SDATA_IN_DRIVER; return ret; } static inline int drv_change_interface(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type, bool p2p) { int ret; might_sleep(); check_sdata_in_driver(sdata); trace_drv_change_interface(local, sdata, type, p2p); ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); trace_drv_return_int(local, ret); return ret; } static inline void drv_remove_interface(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); check_sdata_in_driver(sdata); trace_drv_remove_interface(local, sdata); local->ops->remove_interface(&local->hw, &sdata->vif); sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; trace_drv_return_void(local); } static inline int drv_config(struct ieee80211_local *local, u32 changed) { int ret; might_sleep(); trace_drv_config(local, changed); ret = local->ops->config(&local->hw, changed); trace_drv_return_int(local, ret); return ret; } static inline void drv_bss_info_changed(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_bss_conf *info, u32 changed) { might_sleep(); check_sdata_in_driver(sdata); trace_drv_bss_info_changed(local, sdata, info, changed); if (local->ops->bss_info_changed) local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed); trace_drv_return_void(local); } static inline u64 drv_prepare_multicast(struct ieee80211_local *local, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) #else int mc_count, struct dev_addr_list *mc_list) #endif { u64 ret = 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) trace_drv_prepare_multicast(local, mc_list->count); #else trace_drv_prepare_multicast(local, mc_count); #endif if (local->ops->prepare_multicast) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = local->ops->prepare_multicast(&local->hw, mc_list); #else ret = local->ops->prepare_multicast(&local->hw, mc_count, mc_list); #endif trace_drv_return_u64(local, ret); return ret; } static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { might_sleep(); trace_drv_configure_filter(local, changed_flags, total_flags, multicast); local->ops->configure_filter(&local->hw, changed_flags, total_flags, multicast); trace_drv_return_void(local); } static inline int drv_set_tim(struct ieee80211_local *local, struct ieee80211_sta *sta, bool set) { int ret = 0; trace_drv_set_tim(local, sta, set); if (local->ops->set_tim) ret = local->ops->set_tim(&local->hw, sta, set); trace_drv_return_int(local, ret); return ret; } static inline int drv_set_key(struct ieee80211_local *local, enum set_key_cmd cmd, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { int ret; might_sleep(); sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); trace_drv_set_key(local, cmd, sdata, sta, key); ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); trace_drv_return_int(local, ret); return ret; } static inline void drv_update_tkip_key(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_key_conf *conf, struct sta_info *sta, u32 iv32, u16 *phase1key) { struct ieee80211_sta *ista = NULL; if (sta) ista = &sta->sta; sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); trace_drv_update_tkip_key(local, sdata, conf, ista, iv32); if (local->ops->update_tkip_key) local->ops->update_tkip_key(&local->hw, &sdata->vif, conf, ista, iv32, phase1key); trace_drv_return_void(local); } static inline int drv_hw_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req) { int ret; might_sleep(); check_sdata_in_driver(sdata); trace_drv_hw_scan(local, sdata); ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); trace_drv_return_int(local, ret); return ret; } static inline void drv_cancel_hw_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); check_sdata_in_driver(sdata); trace_drv_cancel_hw_scan(local, sdata); local->ops->cancel_hw_scan(&local->hw, &sdata->vif); trace_drv_return_void(local); } static inline int drv_sched_scan_start(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies) { int ret; might_sleep(); check_sdata_in_driver(sdata); trace_drv_sched_scan_start(local, sdata); ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, req, ies); trace_drv_return_int(local, ret); return ret; } static inline void drv_sched_scan_stop(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); check_sdata_in_driver(sdata); trace_drv_sched_scan_stop(local, sdata); local->ops->sched_scan_stop(&local->hw, &sdata->vif); trace_drv_return_void(local); } static inline void drv_sw_scan_start(struct ieee80211_local *local) { might_sleep(); trace_drv_sw_scan_start(local); if (local->ops->sw_scan_start) local->ops->sw_scan_start(&local->hw); trace_drv_return_void(local); } static inline void drv_sw_scan_complete(struct ieee80211_local *local) { might_sleep(); trace_drv_sw_scan_complete(local); if (local->ops->sw_scan_complete) local->ops->sw_scan_complete(&local->hw); trace_drv_return_void(local); } static inline int drv_get_stats(struct ieee80211_local *local, struct ieee80211_low_level_stats *stats) { int ret = -EOPNOTSUPP; might_sleep(); if (local->ops->get_stats) ret = local->ops->get_stats(&local->hw, stats); trace_drv_get_stats(local, stats, ret); return ret; } static inline void drv_get_tkip_seq(struct ieee80211_local *local, u8 hw_key_idx, u32 *iv32, u16 *iv16) { if (local->ops->get_tkip_seq) local->ops->get_tkip_seq(&local->hw, hw_key_idx, iv32, iv16); trace_drv_get_tkip_seq(local, hw_key_idx, iv32, iv16); } static inline int drv_set_frag_threshold(struct ieee80211_local *local, u32 value) { int ret = 0; might_sleep(); trace_drv_set_frag_threshold(local, value); if (local->ops->set_frag_threshold) ret = local->ops->set_frag_threshold(&local->hw, value); trace_drv_return_int(local, ret); return ret; } static inline int drv_set_rts_threshold(struct ieee80211_local *local, u32 value) { int ret = 0; might_sleep(); trace_drv_set_rts_threshold(local, value); if (local->ops->set_rts_threshold) ret = local->ops->set_rts_threshold(&local->hw, value); trace_drv_return_int(local, ret); return ret; } static inline int drv_set_coverage_class(struct ieee80211_local *local, u8 value) { int ret = 0; might_sleep(); trace_drv_set_coverage_class(local, value); if (local->ops->set_coverage_class) local->ops->set_coverage_class(&local->hw, value); else ret = -EOPNOTSUPP; trace_drv_return_int(local, ret); return ret; } static inline void drv_sta_notify(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); trace_drv_sta_notify(local, sdata, cmd, sta); if (local->ops->sta_notify) local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta); trace_drv_return_void(local); } static inline int drv_sta_add(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta) { int ret = 0; might_sleep(); sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); trace_drv_sta_add(local, sdata, sta); if (local->ops->sta_add) ret = local->ops->sta_add(&local->hw, &sdata->vif, sta); trace_drv_return_int(local, ret); return ret; } static inline void drv_sta_remove(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta) { might_sleep(); sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); trace_drv_sta_remove(local, sdata, sta); if (local->ops->sta_remove) local->ops->sta_remove(&local->hw, &sdata->vif, sta); trace_drv_return_void(local); } static inline __must_check int drv_sta_state(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct sta_info *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state) { int ret = 0; might_sleep(); sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); if (local->ops->sta_state) { ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, old_state, new_state); } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { ret = drv_sta_add(local, sdata, &sta->sta); if (ret == 0) sta->uploaded = true; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { drv_sta_remove(local, sdata, &sta->sta); } trace_drv_return_int(local, ret); return ret; } static inline void drv_sta_rc_update(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta, u32 changed) { sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && sdata->vif.type != NL80211_IFTYPE_ADHOC); trace_drv_sta_rc_update(local, sdata, sta, changed); if (local->ops->sta_rc_update) local->ops->sta_rc_update(&local->hw, &sdata->vif, sta, changed); trace_drv_return_void(local); } static inline int drv_conf_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u16 ac, const struct ieee80211_tx_queue_params *params) { int ret = -EOPNOTSUPP; might_sleep(); check_sdata_in_driver(sdata); trace_drv_conf_tx(local, sdata, ac, params); if (local->ops->conf_tx) ret = local->ops->conf_tx(&local->hw, &sdata->vif, ac, params); trace_drv_return_int(local, ret); return ret; } static inline u64 drv_get_tsf(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { u64 ret = -1ULL; might_sleep(); check_sdata_in_driver(sdata); trace_drv_get_tsf(local, sdata); if (local->ops->get_tsf) ret = local->ops->get_tsf(&local->hw, &sdata->vif); trace_drv_return_u64(local, ret); return ret; } static inline void drv_set_tsf(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u64 tsf) { might_sleep(); check_sdata_in_driver(sdata); trace_drv_set_tsf(local, sdata, tsf); if (local->ops->set_tsf) local->ops->set_tsf(&local->hw, &sdata->vif, tsf); trace_drv_return_void(local); } static inline void drv_reset_tsf(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); check_sdata_in_driver(sdata); trace_drv_reset_tsf(local, sdata); if (local->ops->reset_tsf) local->ops->reset_tsf(&local->hw, &sdata->vif); trace_drv_return_void(local); } static inline int drv_tx_last_beacon(struct ieee80211_local *local) { int ret = 0; /* default unsuported op for less congestion */ might_sleep(); trace_drv_tx_last_beacon(local); if (local->ops->tx_last_beacon) ret = local->ops->tx_last_beacon(&local->hw); trace_drv_return_int(local, ret); return ret; } static inline int drv_ampdu_action(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { int ret = -EOPNOTSUPP; might_sleep(); sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); if (local->ops->ampdu_action) ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, sta, tid, ssn, buf_size); trace_drv_return_int(local, ret); return ret; } static inline int drv_get_survey(struct ieee80211_local *local, int idx, struct survey_info *survey) { int ret = -EOPNOTSUPP; trace_drv_get_survey(local, idx, survey); if (local->ops->get_survey) ret = local->ops->get_survey(&local->hw, idx, survey); trace_drv_return_int(local, ret); return ret; } static inline void drv_rfkill_poll(struct ieee80211_local *local) { might_sleep(); if (local->ops->rfkill_poll) local->ops->rfkill_poll(&local->hw); } static inline void drv_flush(struct ieee80211_local *local, bool drop) { might_sleep(); trace_drv_flush(local, drop); if (local->ops->flush) local->ops->flush(&local->hw, drop); trace_drv_return_void(local); } static inline void drv_channel_switch(struct ieee80211_local *local, struct ieee80211_channel_switch *ch_switch) { might_sleep(); trace_drv_channel_switch(local, ch_switch); local->ops->channel_switch(&local->hw, ch_switch); trace_drv_return_void(local); } static inline int drv_set_antenna(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant) { int ret = -EOPNOTSUPP; might_sleep(); if (local->ops->set_antenna) ret = local->ops->set_antenna(&local->hw, tx_ant, rx_ant); trace_drv_set_antenna(local, tx_ant, rx_ant, ret); return ret; } static inline int drv_get_antenna(struct ieee80211_local *local, u32 *tx_ant, u32 *rx_ant) { int ret = -EOPNOTSUPP; might_sleep(); if (local->ops->get_antenna) ret = local->ops->get_antenna(&local->hw, tx_ant, rx_ant); trace_drv_get_antenna(local, *tx_ant, *rx_ant, ret); return ret; } static inline int drv_remain_on_channel(struct ieee80211_local *local, struct ieee80211_channel *chan, enum nl80211_channel_type chantype, unsigned int duration) { int ret; might_sleep(); trace_drv_remain_on_channel(local, chan, chantype, duration); ret = local->ops->remain_on_channel(&local->hw, chan, chantype, duration); trace_drv_return_int(local, ret); return ret; } static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local) { int ret; might_sleep(); trace_drv_cancel_remain_on_channel(local); ret = local->ops->cancel_remain_on_channel(&local->hw); trace_drv_return_int(local, ret); return ret; } static inline int drv_set_ringparam(struct ieee80211_local *local, u32 tx, u32 rx) { int ret = -ENOTSUPP; might_sleep(); trace_drv_set_ringparam(local, tx, rx); if (local->ops->set_ringparam) ret = local->ops->set_ringparam(&local->hw, tx, rx); trace_drv_return_int(local, ret); return ret; } static inline void drv_get_ringparam(struct ieee80211_local *local, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) { might_sleep(); trace_drv_get_ringparam(local, tx, tx_max, rx, rx_max); if (local->ops->get_ringparam) local->ops->get_ringparam(&local->hw, tx, tx_max, rx, rx_max); trace_drv_return_void(local); } static inline bool drv_tx_frames_pending(struct ieee80211_local *local) { bool ret = false; might_sleep(); trace_drv_tx_frames_pending(local); if (local->ops->tx_frames_pending) ret = local->ops->tx_frames_pending(&local->hw); trace_drv_return_bool(local, ret); return ret; } static inline int drv_set_bitrate_mask(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, const struct cfg80211_bitrate_mask *mask) { int ret = -EOPNOTSUPP; might_sleep(); check_sdata_in_driver(sdata); trace_drv_set_bitrate_mask(local, sdata, mask); if (local->ops->set_bitrate_mask) ret = local->ops->set_bitrate_mask(&local->hw, &sdata->vif, mask); trace_drv_return_int(local, ret); return ret; } static inline void drv_set_rekey_data(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_gtk_rekey_data *data) { check_sdata_in_driver(sdata); trace_drv_set_rekey_data(local, sdata, data); if (local->ops->set_rekey_data) local->ops->set_rekey_data(&local->hw, &sdata->vif, data); trace_drv_return_void(local); } static inline void drv_rssi_callback(struct ieee80211_local *local, const enum ieee80211_rssi_event event) { trace_drv_rssi_callback(local, event); if (local->ops->rssi_callback) local->ops->rssi_callback(&local->hw, event); trace_drv_return_void(local); } static inline void drv_release_buffered_frames(struct ieee80211_local *local, struct sta_info *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data) { trace_drv_release_buffered_frames(local, &sta->sta, tids, num_frames, reason, more_data); if (local->ops->release_buffered_frames) local->ops->release_buffered_frames(&local->hw, &sta->sta, tids, num_frames, reason, more_data); trace_drv_return_void(local); } static inline void drv_allow_buffered_frames(struct ieee80211_local *local, struct sta_info *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data) { trace_drv_allow_buffered_frames(local, &sta->sta, tids, num_frames, reason, more_data); if (local->ops->allow_buffered_frames) local->ops->allow_buffered_frames(&local->hw, &sta->sta, tids, num_frames, reason, more_data); trace_drv_return_void(local); } static inline int drv_get_rssi(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta, s8 *rssi_dbm) { int ret; might_sleep(); ret = local->ops->get_rssi(&local->hw, &sdata->vif, sta, rssi_dbm); trace_drv_get_rssi(local, sta, *rssi_dbm, ret); return ret; } static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); check_sdata_in_driver(sdata); WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); trace_drv_mgd_prepare_tx(local, sdata); if (local->ops->mgd_prepare_tx) local->ops->mgd_prepare_tx(&local->hw, &sdata->vif); trace_drv_return_void(local); } #endif /* __MAC80211_DRIVER_OPS */ compat-drivers-2012-09-18/net/mac80211/tx.c0000644000175000017500000022654612026211315017120 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * * Transmit and frame generation functions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "led.h" #include "mesh.h" #include "wep.h" #include "wpa.h" #include "wme.h" #include "rate.h" /* misc utils */ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, struct sk_buff *skb, int group_addr, int next_frag_len) { int rate, mrate, erp, dur, i; struct ieee80211_rate *txrate; struct ieee80211_local *local = tx->local; struct ieee80211_supported_band *sband; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); /* assume HW handles this */ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) return 0; /* uh huh? */ if (WARN_ON_ONCE(info->control.rates[0].idx < 0)) return 0; sband = local->hw.wiphy->bands[info->band]; txrate = &sband->bitrates[info->control.rates[0].idx]; erp = txrate->flags & IEEE80211_RATE_ERP_G; /* * data and mgmt (except PS Poll): * - during CFP: 32768 * - during contention period: * if addr1 is group address: 0 * if more fragments = 0 and addr1 is individual address: time to * transmit one ACK plus SIFS * if more fragments = 1 and addr1 is individual address: time to * transmit next fragment plus 2 x ACK plus 3 x SIFS * * IEEE 802.11, 9.6: * - control response frame (CTS or ACK) shall be transmitted using the * same rate as the immediately previous frame in the frame exchange * sequence, if this rate belongs to the PHY mandatory rates, or else * at the highest possible rate belonging to the PHY rates in the * BSSBasicRateSet */ hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_ctl(hdr->frame_control)) { /* TODO: These control frames are not currently sent by * mac80211, but should they be implemented, this function * needs to be updated to support duration field calculation. * * RTS: time needed to transmit pending data/mgmt frame plus * one CTS frame plus one ACK frame plus 3 x SIFS * CTS: duration of immediately previous RTS minus time * required to transmit CTS and its SIFS * ACK: 0 if immediately previous directed data/mgmt had * more=0, with more=1 duration in ACK frame is duration * from previous frame minus time needed to transmit ACK * and its SIFS * PS Poll: BIT(15) | BIT(14) | aid */ return 0; } /* data/mgmt */ if (0 /* FIX: data/mgmt during CFP */) return cpu_to_le16(32768); if (group_addr) /* Group address as the destination - no ACK */ return 0; /* Individual destination address: * IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes) * CTS and ACK frames shall be transmitted using the highest rate in * basic rate set that is less than or equal to the rate of the * immediately previous frame and that is using the same modulation * (CCK or OFDM). If no basic rate set matches with these requirements, * the highest mandatory rate of the PHY that is less than or equal to * the rate of the previous frame is used. * Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps */ rate = -1; /* use lowest available if everything fails */ mrate = sband->bitrates[0].bitrate; for (i = 0; i < sband->n_bitrates; i++) { struct ieee80211_rate *r = &sband->bitrates[i]; if (r->bitrate > txrate->bitrate) break; if (tx->sdata->vif.bss_conf.basic_rates & BIT(i)) rate = r->bitrate; switch (sband->band) { case IEEE80211_BAND_2GHZ: { u32 flag; if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) flag = IEEE80211_RATE_MANDATORY_G; else flag = IEEE80211_RATE_MANDATORY_B; if (r->flags & flag) mrate = r->bitrate; break; } case IEEE80211_BAND_5GHZ: if (r->flags & IEEE80211_RATE_MANDATORY_A) mrate = r->bitrate; break; case IEEE80211_BAND_60GHZ: /* TODO, for now fall through */ case IEEE80211_NUM_BANDS: WARN_ON(1); break; } } if (rate == -1) { /* No matching basic rate found; use highest suitable mandatory * PHY rate */ rate = mrate; } /* Don't calculate ACKs for QoS Frames with NoAck Policy set */ if (ieee80211_is_data_qos(hdr->frame_control) && *(ieee80211_get_qos_ctl(hdr)) & IEEE80211_QOS_CTL_ACK_POLICY_NOACK) dur = 0; else /* Time needed to transmit ACK * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up * to closest integer */ dur = ieee80211_frame_duration(sband->band, 10, rate, erp, tx->sdata->vif.bss_conf.use_short_preamble); if (next_frag_len) { /* Frame is fragmented: duration increases with time needed to * transmit next fragment plus ACK and 2 x SIFS. */ dur *= 2; /* ACK + SIFS */ /* next fragment */ dur += ieee80211_frame_duration(sband->band, next_frag_len, txrate->bitrate, erp, tx->sdata->vif.bss_conf.use_short_preamble); } return cpu_to_le16(dur); } /* tx handlers */ static ieee80211_tx_result debug_noinline ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) { struct ieee80211_local *local = tx->local; struct ieee80211_if_managed *ifmgd; /* driver doesn't support power save */ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) return TX_CONTINUE; /* hardware does dynamic power save */ if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) return TX_CONTINUE; /* dynamic power save disabled */ if (local->hw.conf.dynamic_ps_timeout <= 0) return TX_CONTINUE; /* we are scanning, don't enable power save */ if (local->scanning) return TX_CONTINUE; if (!local->ps_sdata) return TX_CONTINUE; /* No point if we're going to suspend */ if (local->quiescing) return TX_CONTINUE; /* dynamic ps is supported only in managed mode */ if (tx->sdata->vif.type != NL80211_IFTYPE_STATION) return TX_CONTINUE; ifmgd = &tx->sdata->u.mgd; /* * Don't wakeup from power save if u-apsd is enabled, voip ac has * u-apsd enabled and the frame is in voip class. This effectively * means that even if all access categories have u-apsd enabled, in * practise u-apsd is only used with the voip ac. This is a * workaround for the case when received voip class packets do not * have correct qos tag for some reason, due the network or the * peer application. * * Note: ifmgd->uapsd_queues access is racy here. If the value is * changed via debugfs, user needs to reassociate manually to have * everything in sync. */ if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) && (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) && skb_get_queue_mapping(tx->skb) == IEEE80211_AC_VO) return TX_CONTINUE; if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_PS); ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; ieee80211_queue_work(&local->hw, &local->dynamic_ps_disable_work); } /* Don't restart the timer if we're not disassociated */ if (!ifmgd->associated) return TX_CONTINUE; mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); return TX_CONTINUE; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); bool assoc = false; if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) return TX_CONTINUE; if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) && !ieee80211_is_probe_req(hdr->frame_control) && !ieee80211_is_nullfunc(hdr->frame_control)) /* * When software scanning only nullfunc frames (to notify * the sleep state to the AP) and probe requests (for the * active scan) are allowed, all other frames should not be * sent and we should not get here, but if we do * nonetheless, drop them to avoid sending them * off-channel. See the link below and * ieee80211_start_scan() for more. * * http://article.gmane.org/gmane.linux.kernel.wireless.general/30089 */ return TX_DROP; if (tx->sdata->vif.type == NL80211_IFTYPE_WDS) return TX_CONTINUE; if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) return TX_CONTINUE; if (tx->flags & IEEE80211_TX_PS_BUFFERED) return TX_CONTINUE; if (tx->sta) assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); if (likely(tx->flags & IEEE80211_TX_UNICAST)) { if (unlikely(!assoc && ieee80211_is_data(hdr->frame_control))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG sdata_info(tx->sdata, "dropped data frame to not associated station %pM\n", hdr->addr1); #endif I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc); return TX_DROP; } } else if (unlikely(tx->sdata->vif.type == NL80211_IFTYPE_AP && ieee80211_is_data(hdr->frame_control) && !atomic_read(&tx->sdata->u.ap.num_mcast_sta))) { /* * No associated STAs - no need to send multicast * frames. */ return TX_DROP; } return TX_CONTINUE; } /* This function is called whenever the AP is about to exceed the maximum limit * of buffered frames for power saving STAs. This situation should not really * happen often during normal operation, so dropping the oldest buffered packet * from each queue should be OK to make some room for new frames. */ static void purge_old_ps_buffers(struct ieee80211_local *local) { int total = 0, purged = 0; struct sk_buff *skb; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; /* * virtual interfaces are protected by RCU */ rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { struct ieee80211_if_ap *ap; if (sdata->vif.type != NL80211_IFTYPE_AP) continue; ap = &sdata->u.ap; skb = skb_dequeue(&ap->ps_bc_buf); if (skb) { purged++; dev_kfree_skb(skb); } total += skb_queue_len(&ap->ps_bc_buf); } /* * Drop one frame from each station from the lowest-priority * AC that has frames at all. */ list_for_each_entry_rcu(sta, &local->sta_list, list) { int ac; for (ac = IEEE80211_AC_BK; ac >= IEEE80211_AC_VO; ac--) { skb = skb_dequeue(&sta->ps_tx_buf[ac]); total += skb_queue_len(&sta->ps_tx_buf[ac]); if (skb) { purged++; dev_kfree_skb(skb); break; } } } rcu_read_unlock(); local->total_ps_buffered = total; ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged); } static ieee80211_tx_result ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; /* * broadcast/multicast frame * * If any of the associated stations is in power save mode, * the frame is buffered to be sent after DTIM beacon frame. * This is done either by the hardware or us. */ /* powersaving STAs only in AP/VLAN mode */ if (!tx->sdata->bss) return TX_CONTINUE; /* no buffering for ordered frames */ if (ieee80211_has_order(hdr->frame_control)) return TX_CONTINUE; /* no stations in PS mode */ if (!atomic_read(&tx->sdata->bss->num_sta_ps)) return TX_CONTINUE; info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) info->hw_queue = tx->sdata->vif.cab_queue; /* device releases frame after DTIM beacon */ if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)) return TX_CONTINUE; /* buffered in mac80211 */ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) { ps_dbg(tx->sdata, "BC TX buffer full - dropping the oldest frame\n"); dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); } else tx->local->total_ps_buffered++; skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); return TX_QUEUED; } static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, struct sk_buff *skb) { if (!ieee80211_is_mgmt(fc)) return 0; if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP)) return 0; if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) skb->data)) return 0; return 1; } static ieee80211_tx_result ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) { struct sta_info *sta = tx->sta; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_local *local = tx->local; if (unlikely(!sta)) return TX_CONTINUE; if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) { int ac = skb_get_queue_mapping(tx->skb); /* only deauth, disassoc and action are bufferable MMPDUs */ if (ieee80211_is_mgmt(hdr->frame_control) && !ieee80211_is_deauth(hdr->frame_control) && !ieee80211_is_disassoc(hdr->frame_control) && !ieee80211_is_action(hdr->frame_control)) { info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; return TX_CONTINUE; } ps_dbg(sta->sdata, "STA %pM aid %d: PS buffer for AC %d\n", sta->sta.addr, sta->sta.aid, ac); if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) { struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]); ps_dbg(tx->sdata, "STA %pM TX buffer for AC %d full - dropping oldest frame\n", sta->sta.addr, ac); dev_kfree_skb(old); } else tx->local->total_ps_buffered++; info->control.jiffies = jiffies; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); if (!timer_pending(&local->sta_cleanup)) mod_timer(&local->sta_cleanup, round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); /* * We queued up some frames, so the TIM bit might * need to be set, recalculate it. */ sta_info_recalc_tim(sta); return TX_QUEUED; } else if (unlikely(test_sta_flag(sta, WLAN_STA_PS_STA))) { ps_dbg(tx->sdata, "STA %pM in PS mode, but polling/in SP -> send frame\n", sta->sta.addr); } return TX_CONTINUE; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx) { if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED)) return TX_CONTINUE; if (tx->flags & IEEE80211_TX_UNICAST) return ieee80211_tx_h_unicast_ps_buf(tx); else return ieee80211_tx_h_multicast_ps_buf(tx); } static ieee80211_tx_result debug_noinline ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol && tx->sdata->control_port_no_encrypt)) info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; return TX_CONTINUE; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) { struct ieee80211_key *key; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) tx->key = NULL; else if (tx->sta && (key = rcu_dereference(tx->sta->ptk))) tx->key = key; else if (ieee80211_is_mgmt(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && ieee80211_is_robust_mgmt_frame(hdr) && (key = rcu_dereference(tx->sdata->default_mgmt_key))) tx->key = key; else if (is_multicast_ether_addr(hdr->addr1) && (key = rcu_dereference(tx->sdata->default_multicast_key))) tx->key = key; else if (!is_multicast_ether_addr(hdr->addr1) && (key = rcu_dereference(tx->sdata->default_unicast_key))) tx->key = key; else if (info->flags & IEEE80211_TX_CTL_INJECTED) tx->key = NULL; else if (!tx->sdata->drop_unencrypted) tx->key = NULL; else if (tx->skb->protocol == tx->sdata->control_port_protocol) tx->key = NULL; else if (ieee80211_is_robust_mgmt_frame(hdr) && !(ieee80211_is_action(hdr->frame_control) && tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP))) tx->key = NULL; else if (ieee80211_is_mgmt(hdr->frame_control) && !ieee80211_is_robust_mgmt_frame(hdr)) tx->key = NULL; else { I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); return TX_DROP; } if (tx->key) { bool skip_hw = false; tx->key->tx_rx_count++; /* TODO: add threshold stuff again */ switch (tx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: if (!ieee80211_is_data_present(hdr->frame_control)) tx->key = NULL; break; case WLAN_CIPHER_SUITE_CCMP: if (!ieee80211_is_data_present(hdr->frame_control) && !ieee80211_use_mfp(hdr->frame_control, tx->sta, tx->skb)) tx->key = NULL; else skip_hw = (tx->key->conf.flags & IEEE80211_KEY_FLAG_SW_MGMT) && ieee80211_is_mgmt(hdr->frame_control); break; case WLAN_CIPHER_SUITE_AES_CMAC: if (!ieee80211_is_mgmt(hdr->frame_control)) tx->key = NULL; break; } if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED)) return TX_DROP; if (!skip_hw && tx->key && tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) info->control.hw_key = &tx->key->conf; } return TX_CONTINUE; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (void *)tx->skb->data; struct ieee80211_supported_band *sband; struct ieee80211_rate *rate; int i; u32 len; bool inval = false, rts = false, short_preamble = false; struct ieee80211_tx_rate_control txrc; bool assoc = false; memset(&txrc, 0, sizeof(txrc)); sband = tx->local->hw.wiphy->bands[info->band]; len = min_t(u32, tx->skb->len + FCS_LEN, tx->local->hw.wiphy->frag_threshold); /* set up the tx rate control struct we give the RC algo */ txrc.hw = &tx->local->hw; txrc.sband = sband; txrc.bss_conf = &tx->sdata->vif.bss_conf; txrc.skb = tx->skb; txrc.reported_rate.idx = -1; txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[info->band]; if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; memcpy(txrc.rate_idx_mcs_mask, tx->sdata->rc_rateidx_mcs_mask[info->band], sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT || tx->sdata->vif.type == NL80211_IFTYPE_ADHOC); /* set up RTS protection if desired */ if (len > tx->local->hw.wiphy->rts_threshold) { txrc.rts = rts = true; } /* * Use short preamble if the BSS can handle it, but not for * management frames unless we know the receiver can handle * that -- the management frame might be to a station that * just wants a probe response. */ if (tx->sdata->vif.bss_conf.use_short_preamble && (ieee80211_is_data(hdr->frame_control) || (tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) txrc.short_preamble = short_preamble = true; if (tx->sta) assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); /* * Lets not bother rate control if we're associated and cannot * talk to the sta. This should not happen. */ if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc && !rate_usable_index_exists(sband, &tx->sta->sta), "%s: Dropped data frame as no usable bitrate found while " "scanning and associated. Target station: " "%pM on %d GHz band\n", tx->sdata->name, hdr->addr1, info->band ? 5 : 2)) return TX_DROP; /* * If we're associated with the sta at this point we know we can at * least send the frame at the lowest bit rate. */ rate_control_get_rate(tx->sdata, tx->sta, &txrc); if (unlikely(info->control.rates[0].idx < 0)) return TX_DROP; if (txrc.reported_rate.idx < 0) { txrc.reported_rate = info->control.rates[0]; if (tx->sta && ieee80211_is_data(hdr->frame_control)) tx->sta->last_tx_rate = txrc.reported_rate; } else if (tx->sta) tx->sta->last_tx_rate = txrc.reported_rate; if (unlikely(!info->control.rates[0].count)) info->control.rates[0].count = 1; if (WARN_ON_ONCE((info->control.rates[0].count > 1) && (info->flags & IEEE80211_TX_CTL_NO_ACK))) info->control.rates[0].count = 1; if (is_multicast_ether_addr(hdr->addr1)) { /* * XXX: verify the rate is in the basic rateset */ return TX_CONTINUE; } /* * set up the RTS/CTS rate as the fastest basic rate * that is not faster than the data rate * * XXX: Should this check all retry rates? */ if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) { s8 baserate = 0; rate = &sband->bitrates[info->control.rates[0].idx]; for (i = 0; i < sband->n_bitrates; i++) { /* must be a basic rate */ if (!(tx->sdata->vif.bss_conf.basic_rates & BIT(i))) continue; /* must not be faster than the data rate */ if (sband->bitrates[i].bitrate > rate->bitrate) continue; /* maximum */ if (sband->bitrates[baserate].bitrate < sband->bitrates[i].bitrate) baserate = i; } info->control.rts_cts_rate_idx = baserate; } for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { /* * make sure there's no valid rate following * an invalid one, just in case drivers don't * take the API seriously to stop at -1. */ if (inval) { info->control.rates[i].idx = -1; continue; } if (info->control.rates[i].idx < 0) { inval = true; continue; } /* * For now assume MCS is already set up correctly, this * needs to be fixed. */ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) { WARN_ON(info->control.rates[i].idx > 76); continue; } /* set up RTS protection if desired */ if (rts) info->control.rates[i].flags |= IEEE80211_TX_RC_USE_RTS_CTS; /* RC is busted */ if (WARN_ON_ONCE(info->control.rates[i].idx >= sband->n_bitrates)) { info->control.rates[i].idx = -1; continue; } rate = &sband->bitrates[info->control.rates[i].idx]; /* set up short preamble */ if (short_preamble && rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) info->control.rates[i].flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; /* set up G protection */ if (!rts && tx->sdata->vif.bss_conf.use_cts_prot && rate->flags & IEEE80211_RATE_ERP_G) info->control.rates[i].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT; } return TX_CONTINUE; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; u16 *seq; u8 *qc; int tid; /* * Packet injection may want to control the sequence * number, if we have no matching interface then we * neither assign one ourselves nor ask the driver to. */ if (unlikely(info->control.vif->type == NL80211_IFTYPE_MONITOR)) return TX_CONTINUE; if (unlikely(ieee80211_is_ctl(hdr->frame_control))) return TX_CONTINUE; if (ieee80211_hdrlen(hdr->frame_control) < 24) return TX_CONTINUE; if (ieee80211_is_qos_nullfunc(hdr->frame_control)) return TX_CONTINUE; /* * Anything but QoS data that has a sequence number field * (is long enough) gets a sequence number from the global * counter. */ if (!ieee80211_is_data_qos(hdr->frame_control)) { /* driver should assign sequence number */ info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; /* for pure STA mode without beacons, we can do it */ hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number); tx->sdata->sequence_number += 0x10; return TX_CONTINUE; } /* * This should be true for injected/management frames only, for * management frames we have set the IEEE80211_TX_CTL_ASSIGN_SEQ * above since they are not QoS-data frames. */ if (!tx->sta) return TX_CONTINUE; /* include per-STA, per-TID sequence counter */ qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; seq = &tx->sta->tid_seq[tid]; hdr->seq_ctrl = cpu_to_le16(*seq); /* Increase the sequence number. */ *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; return TX_CONTINUE; } static int ieee80211_fragment(struct ieee80211_tx_data *tx, struct sk_buff *skb, int hdrlen, int frag_threshold) { struct ieee80211_local *local = tx->local; struct ieee80211_tx_info *info; struct sk_buff *tmp; int per_fragm = frag_threshold - hdrlen - FCS_LEN; int pos = hdrlen + per_fragm; int rem = skb->len - hdrlen - per_fragm; if (WARN_ON(rem < 0)) return -EINVAL; /* first fragment was already added to queue by caller */ while (rem) { int fraglen = per_fragm; if (fraglen > rem) fraglen = rem; rem -= fraglen; tmp = dev_alloc_skb(local->tx_headroom + frag_threshold + IEEE80211_ENCRYPT_HEADROOM + IEEE80211_ENCRYPT_TAILROOM); if (!tmp) return -ENOMEM; __skb_queue_tail(&tx->skbs, tmp); skb_reserve(tmp, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM); /* copy control information */ memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); info = IEEE80211_SKB_CB(tmp); info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); if (rem) info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; skb_copy_queue_mapping(tmp, skb); tmp->priority = skb->priority; tmp->dev = skb->dev; /* copy header and data */ memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen); memcpy(skb_put(tmp, fraglen), skb->data + pos, fraglen); pos += fraglen; } /* adjust first fragment's length */ skb->len = hdrlen + per_fragm; return 0; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) { struct sk_buff *skb = tx->skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; int frag_threshold = tx->local->hw.wiphy->frag_threshold; int hdrlen; int fragnum; /* no matter what happens, tx->skb moves to tx->skbs */ __skb_queue_tail(&tx->skbs, skb); tx->skb = NULL; if (info->flags & IEEE80211_TX_CTL_DONTFRAG) return TX_CONTINUE; if (tx->local->ops->set_frag_threshold) return TX_CONTINUE; /* * Warn when submitting a fragmented A-MPDU frame and drop it. * This scenario is handled in ieee80211_tx_prepare but extra * caution taken here as fragmented ampdu may cause Tx stop. */ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) return TX_DROP; hdrlen = ieee80211_hdrlen(hdr->frame_control); /* internal error, why isn't DONTFRAG set? */ if (WARN_ON(skb->len + FCS_LEN <= frag_threshold)) return TX_DROP; /* * Now fragment the frame. This will allocate all the fragments and * chain them (using skb as the first fragment) to skb->next. * During transmission, we will remove the successfully transmitted * fragments from this list. When the low-level driver rejects one * of the fragments then we will simply pretend to accept the skb * but store it away as pending. */ if (ieee80211_fragment(tx, skb, hdrlen, frag_threshold)) return TX_DROP; /* update duration/seq/flags of fragments */ fragnum = 0; skb_queue_walk(&tx->skbs, skb) { int next_len; const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); hdr = (void *)skb->data; info = IEEE80211_SKB_CB(skb); if (!skb_queue_is_last(&tx->skbs, skb)) { hdr->frame_control |= morefrags; /* * No multi-rate retries for fragmented frames, that * would completely throw off the NAV at other STAs. */ info->control.rates[1].idx = -1; info->control.rates[2].idx = -1; info->control.rates[3].idx = -1; BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 4); info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { hdr->frame_control &= ~morefrags; next_len = 0; } hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG); fragnum++; } return TX_CONTINUE; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) { struct sk_buff *skb; if (!tx->sta) return TX_CONTINUE; tx->sta->tx_packets++; skb_queue_walk(&tx->skbs, skb) { tx->sta->tx_fragments++; tx->sta->tx_bytes += skb->len; } return TX_CONTINUE; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) { if (!tx->key) return TX_CONTINUE; switch (tx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: return ieee80211_crypto_wep_encrypt(tx); case WLAN_CIPHER_SUITE_TKIP: return ieee80211_crypto_tkip_encrypt(tx); case WLAN_CIPHER_SUITE_CCMP: return ieee80211_crypto_ccmp_encrypt(tx); case WLAN_CIPHER_SUITE_AES_CMAC: return ieee80211_crypto_aes_cmac_encrypt(tx); default: return ieee80211_crypto_hw_encrypt(tx); } return TX_DROP; } static ieee80211_tx_result debug_noinline ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) { struct sk_buff *skb; struct ieee80211_hdr *hdr; int next_len; bool group_addr; skb_queue_walk(&tx->skbs, skb) { hdr = (void *) skb->data; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) break; /* must not overwrite AID */ if (!skb_queue_is_last(&tx->skbs, skb)) { struct sk_buff *next = skb_queue_next(&tx->skbs, skb); next_len = next->len; } else next_len = 0; group_addr = is_multicast_ether_addr(hdr->addr1); hdr->duration_id = ieee80211_duration(tx, skb, group_addr, next_len); } return TX_CONTINUE; } /* actual transmit path */ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, struct sk_buff *skb, struct ieee80211_tx_info *info, struct tid_ampdu_tx *tid_tx, int tid) { bool queued = false; bool reset_agg_timer = false; struct sk_buff *purge_skb = NULL; if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { info->flags |= IEEE80211_TX_CTL_AMPDU; reset_agg_timer = true; } else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { /* * nothing -- this aggregation session is being started * but that might still fail with the driver */ } else { spin_lock(&tx->sta->lock); /* * Need to re-check now, because we may get here * * 1) in the window during which the setup is actually * already done, but not marked yet because not all * packets are spliced over to the driver pending * queue yet -- if this happened we acquire the lock * either before or after the splice happens, but * need to recheck which of these cases happened. * * 2) during session teardown, if the OPERATIONAL bit * was cleared due to the teardown but the pointer * hasn't been assigned NULL yet (or we loaded it * before it was assigned) -- in this case it may * now be NULL which means we should just let the * packet pass through because splicing the frames * back is already done. */ tid_tx = rcu_dereference_protected_tid_tx(tx->sta, tid); if (!tid_tx) { /* do nothing, let packet pass through */ } else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { info->flags |= IEEE80211_TX_CTL_AMPDU; reset_agg_timer = true; } else { queued = true; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; __skb_queue_tail(&tid_tx->pending, skb); if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER) purge_skb = __skb_dequeue(&tid_tx->pending); } spin_unlock(&tx->sta->lock); if (purge_skb) dev_kfree_skb(purge_skb); } /* reset session timer */ if (reset_agg_timer && tid_tx->timeout) tid_tx->last_tx = jiffies; return queued; } /* * initialises @tx */ static ieee80211_tx_result ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_data *tx, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int tid; u8 *qc; memset(tx, 0, sizeof(*tx)); tx->skb = skb; tx->local = local; tx->sdata = sdata; __skb_queue_head_init(&tx->skbs); /* * If this flag is set to true anywhere, and we get here, * we are doing the needed processing, so remove the flag * now. */ info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING; hdr = (struct ieee80211_hdr *) skb->data; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { tx->sta = rcu_dereference(sdata->u.vlan.sta); if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr) return TX_DROP; } else if (info->flags & IEEE80211_TX_CTL_INJECTED || tx->sdata->control_port_protocol == tx->skb->protocol) { tx->sta = sta_info_get_bss(sdata, hdr->addr1); } if (!tx->sta) tx->sta = sta_info_get(sdata, hdr->addr1); if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && !ieee80211_is_qos_nullfunc(hdr->frame_control) && (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) && !(local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) { struct tid_ampdu_tx *tid_tx; qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; tid_tx = rcu_dereference(tx->sta->ampdu_mlme.tid_tx[tid]); if (tid_tx) { bool queued; queued = ieee80211_tx_prep_agg(tx, skb, info, tid_tx, tid); if (unlikely(queued)) return TX_QUEUED; } } if (is_multicast_ether_addr(hdr->addr1)) { tx->flags &= ~IEEE80211_TX_UNICAST; info->flags |= IEEE80211_TX_CTL_NO_ACK; } else tx->flags |= IEEE80211_TX_UNICAST; if (!(info->flags & IEEE80211_TX_CTL_DONTFRAG)) { if (!(tx->flags & IEEE80211_TX_UNICAST) || skb->len + FCS_LEN <= local->hw.wiphy->frag_threshold || info->flags & IEEE80211_TX_CTL_AMPDU) info->flags |= IEEE80211_TX_CTL_DONTFRAG; } if (!tx->sta) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; else if (test_and_clear_sta_flag(tx->sta, WLAN_STA_CLEAR_PS_FILT)) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; info->flags |= IEEE80211_TX_CTL_FIRST_FRAGMENT; return TX_CONTINUE; } static bool ieee80211_tx_frags(struct ieee80211_local *local, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct sk_buff_head *skbs, bool txpending) { struct ieee80211_tx_control control; struct sk_buff *skb, *tmp; unsigned long flags; skb_queue_walk_safe(skbs, skb, tmp) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int q = info->hw_queue; #ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (WARN_ON_ONCE(q >= local->hw.queues)) { __skb_unlink(skb, skbs); dev_kfree_skb(skb); continue; } #endif spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (local->queue_stop_reasons[q] || (!txpending && !skb_queue_empty(&local->pending[q]))) { /* * Since queue is stopped, queue up frames for later * transmission from the tx-pending tasklet when the * queue is woken again. */ if (txpending) skb_queue_splice_init(skbs, &local->pending[q]); else skb_queue_splice_tail_init(skbs, &local->pending[q]); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); return false; } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); info->control.vif = vif; control.sta = sta; __skb_unlink(skb, skbs); drv_tx(local, &control, skb); } return true; } /* * Returns false if the frame couldn't be transmitted but was queued instead. */ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff_head *skbs, int led_len, struct sta_info *sta, bool txpending) { struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata; struct ieee80211_vif *vif; struct ieee80211_sta *pubsta; struct sk_buff *skb; bool result = true; __le16 fc; if (WARN_ON(skb_queue_empty(skbs))) return true; skb = skb_peek(skbs); fc = ((struct ieee80211_hdr *)skb->data)->frame_control; info = IEEE80211_SKB_CB(skb); sdata = vif_to_sdata(info->control.vif); if (sta && !sta->uploaded) sta = NULL; if (sta) pubsta = &sta->sta; else pubsta = NULL; switch (sdata->vif.type) { case NL80211_IFTYPE_MONITOR: sdata = rcu_dereference(local->monitor_sdata); if (sdata) { vif = &sdata->vif; info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)]; } else if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { dev_kfree_skb(skb); return true; } else vif = NULL; break; case NL80211_IFTYPE_AP_VLAN: sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); /* fall through */ default: vif = &sdata->vif; break; } result = ieee80211_tx_frags(local, vif, pubsta, skbs, txpending); ieee80211_tpt_led_trig_tx(local, fc, led_len); ieee80211_led_tx(local, 1); WARN_ON_ONCE(!skb_queue_empty(skbs)); return result; } /* * Invoke TX handlers, return 0 on success and non-zero if the * frame was dropped or queued. */ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); ieee80211_tx_result res = TX_DROP; #define CALL_TXH(txh) \ do { \ res = txh(tx); \ if (res != TX_CONTINUE) \ goto txh_done; \ } while (0) CALL_TXH(ieee80211_tx_h_dynamic_ps); CALL_TXH(ieee80211_tx_h_check_assoc); CALL_TXH(ieee80211_tx_h_ps_buf); CALL_TXH(ieee80211_tx_h_check_control_port_protocol); CALL_TXH(ieee80211_tx_h_select_key); if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)) CALL_TXH(ieee80211_tx_h_rate_ctrl); if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) { __skb_queue_tail(&tx->skbs, tx->skb); tx->skb = NULL; goto txh_done; } CALL_TXH(ieee80211_tx_h_michael_mic_add); CALL_TXH(ieee80211_tx_h_sequence); CALL_TXH(ieee80211_tx_h_fragment); /* handlers after fragment must be aware of tx info fragmentation! */ CALL_TXH(ieee80211_tx_h_stats); CALL_TXH(ieee80211_tx_h_encrypt); if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)) CALL_TXH(ieee80211_tx_h_calculate_duration); #undef CALL_TXH txh_done: if (unlikely(res == TX_DROP)) { I802_DEBUG_INC(tx->local->tx_handlers_drop); if (tx->skb) dev_kfree_skb(tx->skb); else __skb_queue_purge(&tx->skbs); return -1; } else if (unlikely(res == TX_QUEUED)) { I802_DEBUG_INC(tx->local->tx_handlers_queued); return -1; } return 0; } /* * Returns false if the frame couldn't be transmitted but was queued instead. */ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool txpending) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); bool result = true; int led_len; if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); return true; } rcu_read_lock(); /* initialises tx */ led_len = skb->len; res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); if (unlikely(res_prepare == TX_DROP)) { dev_kfree_skb(skb); goto out; } else if (unlikely(res_prepare == TX_QUEUED)) { goto out; } info->band = local->hw.conf.channel->band; /* set up hw_queue value early */ if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; if (!invoke_tx_handlers(&tx)) result = __ieee80211_tx(local, &tx.skbs, led_len, tx.sta, txpending); out: rcu_read_unlock(); return result; } /* device xmit handlers */ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int head_need, bool may_encrypt) { struct ieee80211_local *local = sdata->local; int tail_need = 0; if (may_encrypt && sdata->crypto_tx_tailroom_needed_cnt) { tail_need = IEEE80211_ENCRYPT_TAILROOM; tail_need -= skb_tailroom(skb); tail_need = max_t(int, tail_need, 0); } if (skb_cloned(skb)) I802_DEBUG_INC(local->tx_expand_skb_head_cloned); else if (head_need || tail_need) I802_DEBUG_INC(local->tx_expand_skb_head); else return 0; if (pskb_expand_head(skb, head_need, tail_need, GFP_ATOMIC)) { wiphy_debug(local->hw.wiphy, "failed to reallocate TX buffer\n"); return -ENOMEM; } return 0; } void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; int headroom; bool may_encrypt; rcu_read_lock(); may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT); headroom = local->tx_headroom; if (may_encrypt) headroom += IEEE80211_ENCRYPT_HEADROOM; headroom -= skb_headroom(skb); headroom = max_t(int, 0, headroom); if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) { dev_kfree_skb(skb); rcu_read_unlock(); return; } hdr = (struct ieee80211_hdr *) skb->data; info->control.vif = &sdata->vif; if (ieee80211_vif_is_mesh(&sdata->vif) && ieee80211_is_data(hdr->frame_control) && !is_multicast_ether_addr(hdr->addr1) && mesh_nexthop_resolve(skb, sdata)) { /* skb queued: don't free */ rcu_read_unlock(); return; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) /* Older kernels do not have the select_queue callback */ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb)); #endif ieee80211_set_qos_hdr(sdata, skb); ieee80211_tx(sdata, skb, false); rcu_read_unlock(); } static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) { struct ieee80211_radiotap_iterator iterator; struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); u16 txflags; info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_DONTFRAG; /* * for every radiotap entry that is present * (ieee80211_radiotap_iterator_next returns -ENOENT when no more * entries present, or -EINVAL on error) */ while (!ret) { ret = ieee80211_radiotap_iterator_next(&iterator); if (ret) continue; /* see if this argument is something we can use */ switch (iterator.this_arg_index) { /* * You must take care when dereferencing iterator.this_arg * for multibyte types... the pointer is not aligned. Use * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. */ case IEEE80211_RADIOTAP_FLAGS: if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) { /* * this indicates that the skb we have been * handed has the 32-bit FCS CRC at the end... * we should react to that by snipping it off * because it will be recomputed and added * on transmission */ if (skb->len < (iterator._max_length + FCS_LEN)) return false; skb_trim(skb, skb->len - FCS_LEN); } if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) info->flags &= ~IEEE80211_TX_CTL_DONTFRAG; break; case IEEE80211_RADIOTAP_TX_FLAGS: txflags = get_unaligned_le16(iterator.this_arg); if (txflags & IEEE80211_RADIOTAP_F_TX_NOACK) info->flags |= IEEE80211_TX_CTL_NO_ACK; break; /* * Please update the file * Documentation/networking/mac80211-injection.txt * when parsing new fields here. */ default: break; } } if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */ return false; /* * remove the radiotap header * iterator->_max_length was sanity-checked against * skb->len by iterator init */ skb_pull(skb, iterator._max_length); return true; } netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_channel *chan = local->hw.conf.channel; struct ieee80211_radiotap_header *prthdr = (struct ieee80211_radiotap_header *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *tmp_sdata, *sdata; u16 len_rthdr; int hdrlen; /* * Frame injection is not allowed if beaconing is not allowed * or if we need radar detection. Beaconing is usually not allowed when * the mode or operation (Adhoc, AP, Mesh) does not support DFS. * Passive scan is also used in world regulatory domains where * your country is not known and as such it should be treated as * NO TX unless the channel is explicitly allowed in which case * your current regulatory domain would not have the passive scan * flag. * * Since AP mode uses monitor interfaces to inject/TX management * frames we can make AP mode the exception to this rule once it * supports radar detection as its implementation can deal with * radar detection by itself. We can do that later by adding a * monitor flag interfaces used for AP support. */ if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR | IEEE80211_CHAN_PASSIVE_SCAN))) goto fail; /* check for not even having the fixed radiotap header part */ if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) goto fail; /* too short to be possibly valid */ /* is it a header version we can trust to find length from? */ if (unlikely(prthdr->it_version)) goto fail; /* only version 0 is supported */ /* then there must be a radiotap header with a length we can use */ len_rthdr = ieee80211_get_radiotap_len(skb->data); /* does the skb contain enough to deliver on the alleged length? */ if (unlikely(skb->len < len_rthdr)) goto fail; /* skb too short for claimed rt header extent */ /* * fix up the pointers accounting for the radiotap * header still being in there. We are being given * a precooked IEEE80211 header so no need for * normal processing */ skb_set_mac_header(skb, len_rthdr); /* * these are just fixed to the end of the rt area since we * don't have any better information and at this point, nobody cares */ skb_set_network_header(skb, len_rthdr); skb_set_transport_header(skb, len_rthdr); if (skb->len < len_rthdr + 2) goto fail; hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < len_rthdr + hdrlen) goto fail; /* * Initialize skb->protocol if the injected frame is a data frame * carrying a rfc1042 header */ if (ieee80211_is_data(hdr->frame_control) && skb->len >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) { u8 *payload = (u8 *)hdr + hdrlen; if (ether_addr_equal(payload, rfc1042_header)) skb->protocol = cpu_to_be16((payload[6] << 8) | payload[7]); } memset(info, 0, sizeof(*info)); info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_CTL_INJECTED; /* process and remove the injection radiotap header */ if (!ieee80211_parse_tx_radiotap(skb)) goto fail; rcu_read_lock(); /* * We process outgoing injected frames that have a local address * we handle as though they are non-injected frames. * This code here isn't entirely correct, the local MAC address * isn't always enough to find the interface to use; for proper * VLAN/WDS support we will need a different mechanism (which * likely isn't going to be monitor interfaces). */ sdata = IEEE80211_DEV_TO_SUB_IF(dev); list_for_each_entry_rcu(tmp_sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(tmp_sdata)) continue; if (tmp_sdata->vif.type == NL80211_IFTYPE_MONITOR || tmp_sdata->vif.type == NL80211_IFTYPE_AP_VLAN || tmp_sdata->vif.type == NL80211_IFTYPE_WDS) continue; if (ether_addr_equal(tmp_sdata->vif.addr, hdr->addr2)) { sdata = tmp_sdata; break; } } ieee80211_xmit(sdata, skb); rcu_read_unlock(); return NETDEV_TX_OK; fail: dev_kfree_skb(skb); return NETDEV_TX_OK; /* meaning, we dealt with the skb */ } /** * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type * subinterfaces (wlan#, WDS, and VLAN interfaces) * @skb: packet to be sent * @dev: incoming interface * * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will * not be freed, and caller is responsible for either retrying later or freeing * skb). * * This function takes in an Ethernet header and encapsulates it with suitable * IEEE 802.11 header based on which interface the packet is coming in. The * encapsulated packet will then be passed to master interface, wlan#.11, for * transmission (through low-level driver). */ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info; int head_need; u16 ethertype, hdrlen, meshhdrlen = 0; __le16 fc; struct ieee80211_hdr hdr; struct ieee80211s_hdr mesh_hdr __maybe_unused; struct mesh_path __maybe_unused *mppath = NULL, *mpath = NULL; const u8 *encaps_data; int encaps_len, skip_header_bytes; int nh_pos, h_pos; struct sta_info *sta = NULL; bool wme_sta = false, authorized = false, tdls_auth = false; bool tdls_direct = false; bool multicast; u32 info_flags = 0; u16 info_id = 0; if (unlikely(skb->len < ETH_HLEN)) goto fail; /* convert Ethernet header to proper 802.11 header (based on * operation mode) */ ethertype = (skb->data[12] << 8) | skb->data[13]; fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: rcu_read_lock(); sta = rcu_dereference(sdata->u.vlan.sta); if (sta) { fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); if (sta) break; /* fall through */ case NL80211_IFTYPE_AP: fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 24; break; case NL80211_IFTYPE_WDS: fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; goto fail; } rcu_read_lock(); if (!is_multicast_ether_addr(skb->data)) { mpath = mesh_path_lookup(skb->data, sdata); if (!mpath) mppath = mpp_path_lookup(skb->data, sdata); } /* * Use address extension if it is a packet from * another interface or if we know the destination * is being proxied by a portal (i.e. portal address * differs from proxied address) */ if (ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN) && !(mppath && !ether_addr_equal(mppath->mpp, skb->data))) { hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, skb->data, skb->data + ETH_ALEN); rcu_read_unlock(); meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata, NULL, NULL); } else { /* DS -> MBSS (802.11-2012 13.11.3.3). * For unicast with unknown forwarding information, * destination might be in the MBSS or if that fails * forwarded to another mesh gate. In either case * resolution will be handled in ieee80211_xmit(), so * leave the original DA. This also works for mcast */ const u8 *mesh_da = skb->data; if (mppath) mesh_da = mppath->mpp; else if (mpath) mesh_da = mpath->dst; rcu_read_unlock(); hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, mesh_da, sdata->vif.addr); if (is_multicast_ether_addr(mesh_da)) /* DA TA mSA AE:SA */ meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata, skb->data + ETH_ALEN, NULL); else /* RA TA mDA mSA AE:DA SA */ meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata, skb->data, skb->data + ETH_ALEN); } break; #endif case NL80211_IFTYPE_STATION: if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { bool tdls_peer = false; rcu_read_lock(); sta = sta_info_get(sdata, skb->data); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = test_sta_flag(sta, WLAN_STA_WME); tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER); tdls_auth = test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); } rcu_read_unlock(); /* * If the TDLS link is enabled, send everything * directly. Otherwise, allow TDLS setup frames * to be transmitted indirectly. */ tdls_direct = tdls_peer && (tdls_auth || !(ethertype == ETH_P_TDLS && skb->len > 14 && skb->data[14] == WLAN_TDLS_SNAP_RFTYPE)); } if (tdls_direct) { /* link during setup - throw out frames to peer */ if (!tdls_auth) goto fail; /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN); hdrlen = 24; } else if (sdata->u.mgd.use_4addr && cpu_to_be16(ethertype) != sdata->control_port_protocol) { fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; } else { fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); hdrlen = 24; } break; case NL80211_IFTYPE_ADHOC: /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN); hdrlen = 24; break; default: goto fail; } /* * There's no need to try to look up the destination * if it is a multicast address (which can only happen * in AP mode) */ multicast = is_multicast_ether_addr(hdr.addr1); if (!multicast) { rcu_read_lock(); sta = sta_info_get(sdata, hdr.addr1); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); } /* For mesh, the use of the QoS header is mandatory */ if (ieee80211_vif_is_mesh(&sdata->vif)) wme_sta = true; /* receiver and we are QoS enabled, use a QoS type frame */ if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } /* * Drop unicast frames to unauthorised stations unless they are * EAPOL frames from the local station. */ if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && !is_multicast_ether_addr(hdr.addr1) && !authorized && (cpu_to_be16(ethertype) != sdata->control_port_protocol || !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n", dev->name, hdr.addr1); #endif I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); goto fail; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) if (unlikely(!multicast && skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { struct sk_buff *orig_skb = skb; skb = skb_clone(skb, GFP_ATOMIC); if (skb) { unsigned long flags; int id, r; spin_lock_irqsave(&local->ack_status_lock, flags); r = idr_get_new_above(&local->ack_status_frames, orig_skb, 1, &id); if (r == -EAGAIN) { idr_pre_get(&local->ack_status_frames, GFP_ATOMIC); r = idr_get_new_above(&local->ack_status_frames, orig_skb, 1, &id); } if (WARN_ON(!id) || id > 0xffff) { idr_remove(&local->ack_status_frames, id); r = -ERANGE; } spin_unlock_irqrestore(&local->ack_status_lock, flags); if (!r) { info_id = id; info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; } else if (skb_shared(skb)) { kfree_skb(orig_skb); } else { kfree_skb(skb); skb = orig_skb; } } else { /* couldn't clone -- lose tx status ... */ skb = orig_skb; } } #endif /* * If the skb is shared we need to obtain our own copy. */ if (skb_shared(skb)) { struct sk_buff *tmp_skb = skb; /* can't happen -- skb is a clone if info_id != 0 */ WARN_ON(info_id); skb = skb_clone(skb, GFP_ATOMIC); kfree_skb(tmp_skb); if (!skb) goto fail; } hdr.frame_control = fc; hdr.duration_id = 0; hdr.seq_ctrl = 0; skip_header_bytes = ETH_HLEN; if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { encaps_data = bridge_tunnel_header; encaps_len = sizeof(bridge_tunnel_header); skip_header_bytes -= 2; } else if (ethertype >= 0x600) { encaps_data = rfc1042_header; encaps_len = sizeof(rfc1042_header); skip_header_bytes -= 2; } else { encaps_data = NULL; encaps_len = 0; } nh_pos = skb_network_header(skb) - skb->data; h_pos = skb_transport_header(skb) - skb->data; skb_pull(skb, skip_header_bytes); nh_pos -= skip_header_bytes; h_pos -= skip_header_bytes; head_need = hdrlen + encaps_len + meshhdrlen - skb_headroom(skb); /* * So we need to modify the skb header and hence need a copy of * that. The head_need variable above doesn't, so far, include * the needed header space that we don't need right away. If we * can, then we don't reallocate right now but only after the * frame arrives at the master device (if it does...) * * If we cannot, however, then we will reallocate to include all * the ever needed space. Also, if we need to reallocate it anyway, * make it big enough for everything we may ever need. */ if (head_need > 0 || skb_cloned(skb)) { head_need += IEEE80211_ENCRYPT_HEADROOM; head_need += local->tx_headroom; head_need = max_t(int, 0, head_need); if (ieee80211_skb_resize(sdata, skb, head_need, true)) goto fail; } if (encaps_data) { memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); nh_pos += encaps_len; h_pos += encaps_len; } #ifdef CONFIG_MAC80211_MESH if (meshhdrlen > 0) { memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen); nh_pos += meshhdrlen; h_pos += meshhdrlen; } #endif if (ieee80211_is_data_qos(fc)) { __le16 *qos_control; qos_control = (__le16*) skb_push(skb, 2); memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2); /* * Maybe we could actually set some fields here, for now just * initialise to zero to indicate no special operation. */ *qos_control = 0; } else memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); nh_pos += hdrlen; h_pos += hdrlen; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; /* Update skb pointers to various headers since this modified frame * is going to go through Linux networking code that may potentially * need things like pointer to IP header. */ skb_set_mac_header(skb, 0); skb_set_network_header(skb, nh_pos); skb_set_transport_header(skb, h_pos); info = IEEE80211_SKB_CB(skb); memset(info, 0, sizeof(*info)); dev->trans_start = jiffies; info->flags = info_flags; info->ack_frame_id = info_id; ieee80211_xmit(sdata, skb); return NETDEV_TX_OK; fail: dev_kfree_skb(skb); return NETDEV_TX_OK; } /* * ieee80211_clear_tx_pending may not be called in a context where * it is possible that it packets could come in again. */ void ieee80211_clear_tx_pending(struct ieee80211_local *local) { int i; for (i = 0; i < local->hw.queues; i++) skb_queue_purge(&local->pending[i]); } /* * Returns false if the frame couldn't be transmitted but was queued instead, * which in this case means re-queued -- take as an indication to stop sending * more pending frames. */ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sub_if_data *sdata; struct sta_info *sta; struct ieee80211_hdr *hdr; bool result; sdata = vif_to_sdata(info->control.vif); if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { result = ieee80211_tx(sdata, skb, true); } else { struct sk_buff_head skbs; __skb_queue_head_init(&skbs); __skb_queue_tail(&skbs, skb); hdr = (struct ieee80211_hdr *)skb->data; sta = sta_info_get(sdata, hdr->addr1); result = __ieee80211_tx(local, &skbs, skb->len, sta, true); } return result; } /* * Transmit all pending packets. Called from tasklet. */ void ieee80211_tx_pending(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *)data; unsigned long flags; int i; bool txok; rcu_read_lock(); spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < local->hw.queues; i++) { /* * If queue is stopped by something other than due to pending * frames, or we have no pending frames, proceed to next queue. */ if (local->queue_stop_reasons[i] || skb_queue_empty(&local->pending[i])) continue; while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = __skb_dequeue(&local->pending[i]); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (WARN_ON(!info->control.vif)) { kfree_skb(skb); continue; } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); txok = ieee80211_tx_pending_skb(local, skb); spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (!txok) break; } if (skb_queue_empty(&local->pending[i])) ieee80211_propagate_queue_wake(local, i); } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); rcu_read_unlock(); } /* functions for drivers to get certain frames */ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_ap *bss, struct sk_buff *skb, struct beacon_data *beacon) { u8 *pos, *tim; int aid0 = 0; int i, have_bits = 0, n1, n2; /* Generate bitmap for TIM only if there are any STAs in power save * mode. */ if (atomic_read(&bss->num_sta_ps) > 0) /* in the hope that this is faster than * checking byte-for-byte */ have_bits = !bitmap_empty((unsigned long*)bss->tim, IEEE80211_MAX_AID+1); if (bss->dtim_count == 0) bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1; else bss->dtim_count--; tim = pos = (u8 *) skb_put(skb, 6); *pos++ = WLAN_EID_TIM; *pos++ = 4; *pos++ = bss->dtim_count; *pos++ = sdata->vif.bss_conf.dtim_period; if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) aid0 = 1; bss->dtim_bc_mc = aid0 == 1; if (have_bits) { /* Find largest even number N1 so that bits numbered 1 through * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits * (N2 + 1) x 8 through 2007 are 0. */ n1 = 0; for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) { if (bss->tim[i]) { n1 = i & 0xfe; break; } } n2 = n1; for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) { if (bss->tim[i]) { n2 = i; break; } } /* Bitmap control */ *pos++ = n1 | aid0; /* Part Virt Bitmap */ skb_put(skb, n2 - n1); memcpy(pos, bss->tim + n1, n2 - n1 + 1); tim[1] = n2 - n1 + 4; } else { *pos++ = aid0; /* Bitmap control */ *pos++ = 0; /* Part Virt Bitmap */ } } struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 *tim_offset, u16 *tim_length) { struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb = NULL; struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata = NULL; struct ieee80211_if_ap *ap = NULL; struct beacon_data *beacon; enum ieee80211_band band = local->oper_channel->band; struct ieee80211_tx_rate_control txrc; rcu_read_lock(); sdata = vif_to_sdata(vif); if (!ieee80211_sdata_running(sdata)) goto out; if (tim_offset) *tim_offset = 0; if (tim_length) *tim_length = 0; if (sdata->vif.type == NL80211_IFTYPE_AP) { ap = &sdata->u.ap; beacon = rcu_dereference(ap->beacon); if (beacon) { /* * headroom, head length, * tail length and maximum TIM length */ skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + beacon->tail_len + 256); if (!skb) goto out; skb_reserve(skb, local->tx_headroom); memcpy(skb_put(skb, beacon->head_len), beacon->head, beacon->head_len); /* * Not very nice, but we want to allow the driver to call * ieee80211_beacon_get() as a response to the set_tim() * callback. That, however, is already invoked under the * sta_lock to guarantee consistent and race-free update * of the tim bitmap in mac80211 and the driver. */ if (local->tim_in_locked_section) { ieee80211_beacon_add_tim(sdata, ap, skb, beacon); } else { unsigned long flags; spin_lock_irqsave(&local->tim_lock, flags); ieee80211_beacon_add_tim(sdata, ap, skb, beacon); spin_unlock_irqrestore(&local->tim_lock, flags); } if (tim_offset) *tim_offset = beacon->head_len; if (tim_length) *tim_length = skb->len - beacon->head_len; if (beacon->tail) memcpy(skb_put(skb, beacon->tail_len), beacon->tail, beacon->tail_len); } else goto out; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_hdr *hdr; struct sk_buff *presp = rcu_dereference(ifibss->presp); if (!presp) goto out; skb = skb_copy(presp, GFP_ATOMIC); if (!skb) goto out; hdr = (struct ieee80211_hdr *) skb->data; hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); } else if (ieee80211_vif_is_mesh(&sdata->vif)) { struct ieee80211_mgmt *mgmt; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos; int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + sizeof(mgmt->u.beacon); #ifdef CONFIG_MAC80211_MESH if (!sdata->u.mesh.mesh_id_len) goto out; #endif if (ifmsh->sync_ops) ifmsh->sync_ops->adjust_tbtt( sdata); skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + /* NULL SSID */ 2 + 8 + /* supported rates */ 2 + 3 + /* DS params */ 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_operation) + 2 + sdata->u.mesh.mesh_id_len + 2 + sizeof(struct ieee80211_meshconf_ie) + sdata->u.mesh.ie_len); if (!skb) goto out; skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); eth_broadcast_addr(mgmt->da); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.beacon.beacon_int = cpu_to_le16(sdata->vif.bss_conf.beacon_int); mgmt->u.beacon.capab_info |= cpu_to_le16( sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0); pos = skb_put(skb, 2); *pos++ = WLAN_EID_SSID; *pos++ = 0x0; if (ieee80211_add_srates_ie(sdata, skb, true, band) || mesh_add_ds_params_ie(skb, sdata) || ieee80211_add_ext_srates_ie(sdata, skb, true, band) || mesh_add_rsn_ie(skb, sdata) || mesh_add_ht_cap_ie(skb, sdata) || mesh_add_ht_oper_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata) || mesh_add_vendor_ies(skb, sdata)) { pr_err("o11s: couldn't add ies!\n"); goto out; } } else { WARN_ON(1); goto out; } info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; info->flags |= IEEE80211_TX_CTL_NO_ACK; info->band = band; memset(&txrc, 0, sizeof(txrc)); txrc.hw = hw; txrc.sband = local->hw.wiphy->bands[band]; txrc.bss_conf = &sdata->vif.bss_conf; txrc.skb = skb; txrc.reported_rate.idx = -1; txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; if (txrc.rate_idx_mask == (1 << txrc.sband->n_bitrates) - 1) txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; memcpy(txrc.rate_idx_mcs_mask, sdata->rc_rateidx_mcs_mask[band], sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = true; rate_control_get_rate(sdata, NULL, &txrc); info->control.vif = vif; info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_ASSIGN_SEQ | IEEE80211_TX_CTL_FIRST_FRAGMENT; out: rcu_read_unlock(); return skb; } EXPORT_SYMBOL(ieee80211_beacon_get_tim); struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ieee80211_if_ap *ap = NULL; struct sk_buff *skb = NULL; struct probe_resp *presp = NULL; struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); if (sdata->vif.type != NL80211_IFTYPE_AP) return NULL; rcu_read_lock(); ap = &sdata->u.ap; presp = rcu_dereference(ap->probe_resp); if (!presp) goto out; skb = dev_alloc_skb(presp->len); if (!skb) goto out; memcpy(skb_put(skb, presp->len), presp->data, presp->len); hdr = (struct ieee80211_hdr *) skb->data; memset(hdr->addr1, 0, sizeof(hdr->addr1)); out: rcu_read_unlock(); return skb; } EXPORT_SYMBOL(ieee80211_proberesp_get); struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata; struct ieee80211_if_managed *ifmgd; struct ieee80211_pspoll *pspoll; struct ieee80211_local *local; struct sk_buff *skb; if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) return NULL; sdata = vif_to_sdata(vif); ifmgd = &sdata->u.mgd; local = sdata->local; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*pspoll)); if (!skb) return NULL; skb_reserve(skb, local->hw.extra_tx_headroom); pspoll = (struct ieee80211_pspoll *) skb_put(skb, sizeof(*pspoll)); memset(pspoll, 0, sizeof(*pspoll)); pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); pspoll->aid = cpu_to_le16(ifmgd->aid); /* aid in PS-Poll has its two MSBs each set to 1 */ pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14); memcpy(pspoll->bssid, ifmgd->bssid, ETH_ALEN); memcpy(pspoll->ta, vif->addr, ETH_ALEN); return skb; } EXPORT_SYMBOL(ieee80211_pspoll_get); struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ieee80211_hdr_3addr *nullfunc; struct ieee80211_sub_if_data *sdata; struct ieee80211_if_managed *ifmgd; struct ieee80211_local *local; struct sk_buff *skb; if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) return NULL; sdata = vif_to_sdata(vif); ifmgd = &sdata->u.mgd; local = sdata->local; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*nullfunc)); if (!skb) return NULL; skb_reserve(skb, local->hw.extra_tx_headroom); nullfunc = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*nullfunc)); memset(nullfunc, 0, sizeof(*nullfunc)); nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS); memcpy(nullfunc->addr1, ifmgd->bssid, ETH_ALEN); memcpy(nullfunc->addr2, vif->addr, ETH_ALEN); memcpy(nullfunc->addr3, ifmgd->bssid, ETH_ALEN); return skb; } EXPORT_SYMBOL(ieee80211_nullfunc_get); struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len) { struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local; struct ieee80211_hdr_3addr *hdr; struct sk_buff *skb; size_t ie_ssid_len; u8 *pos; sdata = vif_to_sdata(vif); local = sdata->local; ie_ssid_len = 2 + ssid_len; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + ie_ssid_len + ie_len); if (!skb) return NULL; skb_reserve(skb, local->hw.extra_tx_headroom); hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); eth_broadcast_addr(hdr->addr1); memcpy(hdr->addr2, vif->addr, ETH_ALEN); eth_broadcast_addr(hdr->addr3); pos = skb_put(skb, ie_ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = ssid_len; if (ssid_len) memcpy(pos, ssid, ssid_len); pos += ssid_len; if (ie) { pos = skb_put(skb, ie_len); memcpy(pos, ie, ie_len); } return skb; } EXPORT_SYMBOL(ieee80211_probereq_get); void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const void *frame, size_t frame_len, const struct ieee80211_tx_info *frame_txctl, struct ieee80211_rts *rts) { const struct ieee80211_hdr *hdr = frame; rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); rts->duration = ieee80211_rts_duration(hw, vif, frame_len, frame_txctl); memcpy(rts->ra, hdr->addr1, sizeof(rts->ra)); memcpy(rts->ta, hdr->addr2, sizeof(rts->ta)); } EXPORT_SYMBOL(ieee80211_rts_get); void ieee80211_ctstoself_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const void *frame, size_t frame_len, const struct ieee80211_tx_info *frame_txctl, struct ieee80211_cts *cts) { const struct ieee80211_hdr *hdr = frame; cts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); cts->duration = ieee80211_ctstoself_duration(hw, vif, frame_len, frame_txctl); memcpy(cts->ra, hdr->addr1, sizeof(cts->ra)); } EXPORT_SYMBOL(ieee80211_ctstoself_get); struct sk_buff * ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb = NULL; struct ieee80211_tx_data tx; struct ieee80211_sub_if_data *sdata; struct ieee80211_if_ap *bss = NULL; struct beacon_data *beacon; struct ieee80211_tx_info *info; sdata = vif_to_sdata(vif); bss = &sdata->u.ap; rcu_read_lock(); beacon = rcu_dereference(bss->beacon); if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head) goto out; if (bss->dtim_count != 0 || !bss->dtim_bc_mc) goto out; /* send buffered bc/mc only after DTIM beacon */ while (1) { skb = skb_dequeue(&bss->ps_bc_buf); if (!skb) goto out; local->total_ps_buffered--; if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; /* more buffered multicast/broadcast frames ==> set * MoreData flag in IEEE 802.11 header to inform PS * STAs */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); } if (!ieee80211_tx_prepare(sdata, &tx, skb)) break; dev_kfree_skb_any(skb); } info = IEEE80211_SKB_CB(skb); tx.flags |= IEEE80211_TX_PS_BUFFERED; info->band = local->oper_channel->band; if (invoke_tx_handlers(&tx)) skb = NULL; out: rcu_read_unlock(); return skb; } EXPORT_SYMBOL(ieee80211_get_buffered_bc); void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid) { int ac = ieee802_1d_to_ac[tid & 7]; skb_set_mac_header(skb, 0); skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); skb_set_queue_mapping(skb, ac); skb->priority = tid; /* * The other path calling ieee80211_xmit is from the tasklet, * and while we can handle concurrent transmissions locking * requirements are that we do not come into tx with bhs on. */ local_bh_disable(); ieee80211_xmit(sdata, skb); local_bh_enable(); } compat-drivers-2012-09-18/net/mac80211/rx.c0000644000175000017500000025323212026211315017106 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "led.h" #include "mesh.h" #include "wep.h" #include "wpa.h" #include "tkip.h" #include "wme.h" #include "rate.h" /* * monitor mode reception * * This function cleans up the SKB, i.e. it removes all the stuff * only useful for monitoring. */ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, struct sk_buff *skb) { if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { if (likely(skb->len > FCS_LEN)) __pskb_trim(skb, skb->len - FCS_LEN); else { /* driver bug */ WARN_ON(1); dev_kfree_skb(skb); skb = NULL; } } return skb; } static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC | RX_FLAG_AMPDU_IS_ZEROLEN)) return 1; if (unlikely(skb->len < 16 + present_fcs_len)) return 1; if (ieee80211_is_ctl(hdr->frame_control) && !ieee80211_is_pspoll(hdr->frame_control) && !ieee80211_is_back_req(hdr->frame_control)) return 1; return 0; } static int ieee80211_rx_radiotap_len(struct ieee80211_local *local, struct ieee80211_rx_status *status) { int len; /* always present fields */ len = sizeof(struct ieee80211_radiotap_header) + 9; if (status->flag & RX_FLAG_MACTIME_MPDU) len += 8; if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) len += 1; if (len & 1) /* padding for RX_FLAGS if necessary */ len++; if (status->flag & RX_FLAG_HT) /* HT info */ len += 3; if (status->flag & RX_FLAG_AMPDU_DETAILS) { /* padding */ while (len & 3) len++; len += 8; } return len; } /* * ieee80211_add_rx_radiotap_header - add radiotap header * * add a radiotap header containing all the fields which the hardware provided. */ static void ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_rate *rate, int rtap_len, bool has_fcs) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_radiotap_header *rthdr; unsigned char *pos; u16 rx_flags = 0; rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len); memset(rthdr, 0, rtap_len); /* radiotap header, set always present flags */ rthdr->it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_CHANNEL) | (1 << IEEE80211_RADIOTAP_ANTENNA) | (1 << IEEE80211_RADIOTAP_RX_FLAGS)); rthdr->it_len = cpu_to_le16(rtap_len); pos = (unsigned char *)(rthdr+1); /* the order of the following fields is important */ /* IEEE80211_RADIOTAP_TSFT */ if (status->flag & RX_FLAG_MACTIME_MPDU) { put_unaligned_le64(status->mactime, pos); rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); pos += 8; } /* IEEE80211_RADIOTAP_FLAGS */ if (has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)) *pos |= IEEE80211_RADIOTAP_F_FCS; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) *pos |= IEEE80211_RADIOTAP_F_BADFCS; if (status->flag & RX_FLAG_SHORTPRE) *pos |= IEEE80211_RADIOTAP_F_SHORTPRE; pos++; /* IEEE80211_RADIOTAP_RATE */ if (!rate || status->flag & RX_FLAG_HT) { /* * Without rate information don't add it. If we have, * MCS information is a separate field in radiotap, * added below. The byte here is needed as padding * for the channel though, so initialise it to 0. */ *pos = 0; } else { rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); *pos = rate->bitrate / 5; } pos++; /* IEEE80211_RADIOTAP_CHANNEL */ put_unaligned_le16(status->freq, pos); pos += 2; if (status->band == IEEE80211_BAND_5GHZ) put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, pos); else if (status->flag & RX_FLAG_HT) put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, pos); else if (rate && rate->flags & IEEE80211_RATE_ERP_G) put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ, pos); else if (rate) put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, pos); else put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos); pos += 2; /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM && !(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { *pos = status->signal; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); pos++; } /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */ /* IEEE80211_RADIOTAP_ANTENNA */ *pos = status->antenna; pos++; /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */ /* IEEE80211_RADIOTAP_RX_FLAGS */ /* ensure 2 byte alignment for the 2 byte field as required */ if ((pos - (u8 *)rthdr) & 1) pos++; if (status->flag & RX_FLAG_FAILED_PLCP_CRC) rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP; put_unaligned_le16(rx_flags, pos); pos += 2; if (status->flag & RX_FLAG_HT) { rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); *pos++ = local->hw.radiotap_mcs_details; *pos = 0; if (status->flag & RX_FLAG_SHORT_GI) *pos |= IEEE80211_RADIOTAP_MCS_SGI; if (status->flag & RX_FLAG_40MHZ) *pos |= IEEE80211_RADIOTAP_MCS_BW_40; if (status->flag & RX_FLAG_HT_GF) *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; pos++; *pos++ = status->rate_idx; } if (status->flag & RX_FLAG_AMPDU_DETAILS) { u16 flags = 0; /* ensure 4 byte alignment */ while ((pos - (u8 *)rthdr) & 3) pos++; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS); put_unaligned_le32(status->ampdu_reference, pos); pos += 4; if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN) flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN; if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN) flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN; if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN) flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; if (status->flag & RX_FLAG_AMPDU_IS_LAST) flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR) flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR; if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN; put_unaligned_le16(flags, pos); pos += 2; if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) *pos++ = status->ampdu_delimiter_crc; else *pos++ = 0; *pos++ = 0; } } /* * This function copies a received frame to all monitor interfaces and * returns a cleaned-up SKB that no longer includes the FCS nor the * radiotap header the driver might have added. */ static struct sk_buff * ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, struct ieee80211_rate *rate) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); struct ieee80211_sub_if_data *sdata; int needed_headroom; struct sk_buff *skb, *skb2; struct net_device *prev_dev = NULL; int present_fcs_len = 0; /* * First, we may need to make a copy of the skb because * (1) we need to modify it for radiotap (if not present), and * (2) the other RX handlers will modify the skb we got. * * We don't need to, of course, if we aren't going to return * the SKB because it has a bad FCS/PLCP checksum. */ /* room for the radiotap header based on driver features */ needed_headroom = ieee80211_rx_radiotap_len(local, status); if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) present_fcs_len = FCS_LEN; /* make sure hdr->frame_control is on the linear part */ if (!pskb_may_pull(origskb, 2)) { dev_kfree_skb(origskb); return NULL; } if (!local->monitors) { if (should_drop_frame(origskb, present_fcs_len)) { dev_kfree_skb(origskb); return NULL; } return remove_monitor_info(local, origskb); } if (should_drop_frame(origskb, present_fcs_len)) { /* only need to expand headroom if necessary */ skb = origskb; origskb = NULL; /* * This shouldn't trigger often because most devices have an * RX header they pull before we get here, and that should * be big enough for our radiotap information. We should * probably export the length to drivers so that we can have * them allocate enough headroom to start with. */ if (skb_headroom(skb) < needed_headroom && pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) { dev_kfree_skb(skb); return NULL; } } else { /* * Need to make a copy and possibly remove radiotap header * and FCS from the original. */ skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC); origskb = remove_monitor_info(local, origskb); if (!skb) return origskb; } /* prepend radiotap information */ ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, true); skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); list_for_each_entry_rcu(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_MONITOR) continue; if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) continue; if (!ieee80211_sdata_running(sdata)) continue; if (prev_dev) { skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { skb2->dev = prev_dev; netif_receive_skb(skb2); } } prev_dev = sdata->dev; sdata->dev->stats.rx_packets++; sdata->dev->stats.rx_bytes += skb->len; } if (prev_dev) { skb->dev = prev_dev; netif_receive_skb(skb); } else dev_kfree_skb(skb); return origskb; } static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); int tid, seqno_idx, security_idx; /* does the frame have a qos control field? */ if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *qc = ieee80211_get_qos_ctl(hdr); /* frame has qos control */ tid = *qc & IEEE80211_QOS_CTL_TID_MASK; if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) status->rx_flags |= IEEE80211_RX_AMSDU; seqno_idx = tid; security_idx = tid; } else { /* * IEEE 802.11-2007, 7.1.3.4.1 ("Sequence Number field"): * * Sequence numbers for management frames, QoS data * frames with a broadcast/multicast address in the * Address 1 field, and all non-QoS data frames sent * by QoS STAs are assigned using an additional single * modulo-4096 counter, [...] * * We also use that counter for non-QoS STAs. */ seqno_idx = NUM_RX_DATA_QUEUES; security_idx = 0; if (ieee80211_is_mgmt(hdr->frame_control)) security_idx = NUM_RX_DATA_QUEUES; tid = 0; } rx->seqno_idx = seqno_idx; rx->security_idx = security_idx; /* Set skb->priority to 1d tag if highest order bit of TID is not set. * For now, set skb->priority to 0 for other cases. */ rx->skb->priority = (tid > 7) ? 0 : tid; } /** * DOC: Packet alignment * * Drivers always need to pass packets that are aligned to two-byte boundaries * to the stack. * * Additionally, should, if possible, align the payload data in a way that * guarantees that the contained IP header is aligned to a four-byte * boundary. In the case of regular frames, this simply means aligning the * payload to a four-byte boundary (because either the IP header is directly * contained, or IV/RFC1042 headers that have a length divisible by four are * in front of it). If the payload data is not properly aligned and the * architecture doesn't support efficient unaligned operations, mac80211 * will align the data. * * With A-MSDU frames, however, the payload data address must yield two modulo * four because there are 14-byte 802.3 headers within the A-MSDU frames that * push the IP header further back to a multiple of four again. Thankfully, the * specs were sane enough this time around to require padding each A-MSDU * subframe to a length that is a multiple of four. * * Padding like Atheros hardware adds which is between the 802.11 header and * the payload is not supported, the driver is required to move the 802.11 * header to be directly in front of the payload in that case. */ static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG WARN_ONCE((unsigned long)rx->skb->data & 1, "unaligned packet at 0x%p\n", rx->skb->data); #endif } /* rx handlers */ static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1)) return 0; return ieee80211_is_robust_mgmt_frame(hdr); } static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1)) return 0; return ieee80211_is_robust_mgmt_frame(hdr); } /* Get the BIP key index from MMIE; return -1 if this is not a BIP frame */ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) { struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; struct ieee80211_mmie *mmie; if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da)) return -1; if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) return -1; /* not a robust management frame */ mmie = (struct ieee80211_mmie *) (skb->data + skb->len - sizeof(*mmie)); if (mmie->element_id != WLAN_EID_MMIE || mmie->length != sizeof(*mmie) - 2) return -1; return le16_to_cpu(mmie->key_id); } static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; char *dev_addr = rx->sdata->vif.addr; if (ieee80211_is_data(hdr->frame_control)) { if (is_multicast_ether_addr(hdr->addr1)) { if (ieee80211_has_tods(hdr->frame_control) || !ieee80211_has_fromds(hdr->frame_control)) return RX_DROP_MONITOR; if (ether_addr_equal(hdr->addr3, dev_addr)) return RX_DROP_MONITOR; } else { if (!ieee80211_has_a4(hdr->frame_control)) return RX_DROP_MONITOR; if (ether_addr_equal(hdr->addr4, dev_addr)) return RX_DROP_MONITOR; } } /* If there is not an established peer link and this is not a peer link * establisment frame, beacon or probe, drop the frame. */ if (!rx->sta || sta_plink_state(rx->sta) != NL80211_PLINK_ESTAB) { struct ieee80211_mgmt *mgmt; if (!ieee80211_is_mgmt(hdr->frame_control)) return RX_DROP_MONITOR; if (ieee80211_is_action(hdr->frame_control)) { u8 category; mgmt = (struct ieee80211_mgmt *)hdr; category = mgmt->u.action.category; if (category != WLAN_CATEGORY_MESH_ACTION && category != WLAN_CATEGORY_SELF_PROTECTED) return RX_DROP_MONITOR; return RX_CONTINUE; } if (ieee80211_is_probe_req(hdr->frame_control) || ieee80211_is_probe_resp(hdr->frame_control) || ieee80211_is_beacon(hdr->frame_control) || ieee80211_is_auth(hdr->frame_control)) return RX_CONTINUE; return RX_DROP_MONITOR; } return RX_CONTINUE; } #define SEQ_MODULO 0x1000 #define SEQ_MASK 0xfff static inline int seq_less(u16 sq1, u16 sq2) { return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1); } static inline u16 seq_inc(u16 sq) { return (sq + 1) & SEQ_MASK; } static inline u16 seq_sub(u16 sq1, u16 sq2) { return (sq1 - sq2) & SEQ_MASK; } static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, int index) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; struct ieee80211_rx_status *status; lockdep_assert_held(&tid_agg_rx->reorder_lock); if (!skb) goto no_frame; /* release the frame from the reorder ring buffer */ tid_agg_rx->stored_mpdu_num--; tid_agg_rx->reorder_buf[index] = NULL; status = IEEE80211_SKB_RXCB(skb); status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE; skb_queue_tail(&local->rx_skb_queue, skb); no_frame: tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); } static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, u16 head_seq_num) { int index; lockdep_assert_held(&tid_agg_rx->reorder_lock); while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; ieee80211_release_reorder_frame(sdata, tid_agg_rx, index); } } /* * Timeout (in jiffies) for skb's that are waiting in the RX reorder buffer. If * the skb was added to the buffer longer than this time ago, the earlier * frames that have not yet been received are assumed to be lost and the skb * can be released for processing. This may also release other skb's from the * reorder buffer if there are no additional gaps between the frames. * * Callers must hold tid_agg_rx->reorder_lock. */ #define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10) static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx) { int index, j; lockdep_assert_held(&tid_agg_rx->reorder_lock); /* release the buffer until next missing frame */ index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; if (!tid_agg_rx->reorder_buf[index] && tid_agg_rx->stored_mpdu_num) { /* * No buffers ready to be released, but check whether any * frames in the reorder buffer have timed out. */ int skipped = 1; for (j = (index + 1) % tid_agg_rx->buf_size; j != index; j = (j + 1) % tid_agg_rx->buf_size) { if (!tid_agg_rx->reorder_buf[j]) { skipped++; continue; } if (skipped && !time_after(jiffies, tid_agg_rx->reorder_time[j] + HT_RX_REORDER_BUF_TIMEOUT)) goto set_release_timer; ht_dbg_ratelimited(sdata, "release an RX reorder frame due to timeout on earlier frames\n"); ieee80211_release_reorder_frame(sdata, tid_agg_rx, j); /* * Increment the head seq# also for the skipped slots. */ tid_agg_rx->head_seq_num = (tid_agg_rx->head_seq_num + skipped) & SEQ_MASK; skipped = 0; } } else while (tid_agg_rx->reorder_buf[index]) { ieee80211_release_reorder_frame(sdata, tid_agg_rx, index); index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; } if (tid_agg_rx->stored_mpdu_num) { j = index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; for (; j != (index - 1) % tid_agg_rx->buf_size; j = (j + 1) % tid_agg_rx->buf_size) { if (tid_agg_rx->reorder_buf[j]) break; } set_release_timer: mod_timer(&tid_agg_rx->reorder_timer, tid_agg_rx->reorder_time[j] + 1 + HT_RX_REORDER_BUF_TIMEOUT); } else { del_timer(&tid_agg_rx->reorder_timer); } } /* * As this function belongs to the RX path it must be under * rcu_read_lock protection. It returns false if the frame * can be processed immediately, true if it was consumed. */ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u16 sc = le16_to_cpu(hdr->seq_ctrl); u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; u16 head_seq_num, buf_size; int index; bool ret = true; spin_lock(&tid_agg_rx->reorder_lock); buf_size = tid_agg_rx->buf_size; head_seq_num = tid_agg_rx->head_seq_num; /* frame with out of date sequence number */ if (seq_less(mpdu_seq_num, head_seq_num)) { dev_kfree_skb(skb); goto out; } /* * If frame the sequence number exceeds our buffering window * size release some previous frames to make room for this one. */ if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) { head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size)); /* release stored frames up to new head to stack */ ieee80211_release_reorder_frames(sdata, tid_agg_rx, head_seq_num); } /* Now the new frame is always in the range of the reordering buffer */ index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; /* check if we already stored this frame */ if (tid_agg_rx->reorder_buf[index]) { dev_kfree_skb(skb); goto out; } /* * If the current MPDU is in the right order and nothing else * is stored we can process it directly, no need to buffer it. * If it is first but there's something stored, we may be able * to release frames after this one. */ if (mpdu_seq_num == tid_agg_rx->head_seq_num && tid_agg_rx->stored_mpdu_num == 0) { tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); ret = false; goto out; } /* put the frame in the reordering buffer */ tid_agg_rx->reorder_buf[index] = skb; tid_agg_rx->reorder_time[index] = jiffies; tid_agg_rx->stored_mpdu_num++; ieee80211_sta_reorder_release(sdata, tid_agg_rx); out: spin_unlock(&tid_agg_rx->reorder_lock); return ret; } /* * Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns * true if the MPDU was buffered, false if it should be processed. */ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) { struct sk_buff *skb = rx->skb; struct ieee80211_local *local = rx->local; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct sta_info *sta = rx->sta; struct tid_ampdu_rx *tid_agg_rx; u16 sc; u8 tid, ack_policy; if (!ieee80211_is_data_qos(hdr->frame_control)) goto dont_reorder; /* * filter the QoS data rx stream according to * STA/TID and check if this STA/TID is on aggregation */ if (!sta) goto dont_reorder; ack_policy = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_ACK_POLICY_MASK; tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); if (!tid_agg_rx) goto dont_reorder; /* qos null data frames are excluded */ if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) goto dont_reorder; /* not part of a BA session */ if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) goto dont_reorder; /* not actually part of this BA session */ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) goto dont_reorder; /* new, potentially un-ordered, ampdu frame - process it */ /* reset session timer */ if (tid_agg_rx->timeout) tid_agg_rx->last_rx = jiffies; /* if this mpdu is fragmented - terminate rx aggregation session */ sc = le16_to_cpu(hdr->seq_ctrl); if (sc & IEEE80211_SCTL_FRAG) { skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; skb_queue_tail(&rx->sdata->skb_queue, skb); ieee80211_queue_work(&local->hw, &rx->sdata->work); return; } /* * No locking needed -- we will only ever process one * RX packet at a time, and thus own tid_agg_rx. All * other code manipulating it needs to (and does) make * sure that we cannot get to it any more before doing * anything with it. */ if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb)) return; dont_reorder: skb_queue_tail(&local->rx_skb_queue, skb); } static ieee80211_rx_result debug_noinline ieee80211_rx_h_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); /* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */ if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) { if (unlikely(ieee80211_has_retry(hdr->frame_control) && rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) { if (status->rx_flags & IEEE80211_RX_RA_MATCH) { rx->local->dot11FrameDuplicateCount++; rx->sta->num_duplicates++; } return RX_DROP_UNUSABLE; } else rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; } if (unlikely(rx->skb->len < 16)) { I802_DEBUG_INC(rx->local->rx_handlers_drop_short); return RX_DROP_MONITOR; } /* Drop disallowed frame classes based on STA auth/assoc state; * IEEE 802.11, Chap 5.5. * * mac80211 filters only based on association state, i.e. it drops * Class 3 frames from not associated stations. hostapd sends * deauth/disassoc frames when needed. In addition, hostapd is * responsible for filtering on both auth and assoc states. */ if (ieee80211_vif_is_mesh(&rx->sdata->vif)) return ieee80211_rx_mesh_check(rx); if (unlikely((ieee80211_is_data(hdr->frame_control) || ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && rx->sdata->vif.type != NL80211_IFTYPE_WDS && (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { /* * accept port control frames from the AP even when it's not * yet marked ASSOC to prevent a race where we don't set the * assoc bit quickly enough before it sends the first frame */ if (rx->sta && rx->sdata->vif.type == NL80211_IFTYPE_STATION && ieee80211_is_data_present(hdr->frame_control)) { u16 ethertype; u8 *payload; payload = rx->skb->data + ieee80211_hdrlen(hdr->frame_control); ethertype = (payload[6] << 8) | payload[7]; if (cpu_to_be16(ethertype) == rx->sdata->control_port_protocol) return RX_CONTINUE; } if (rx->sdata->vif.type == NL80211_IFTYPE_AP && cfg80211_rx_spurious_frame(rx->sdata->dev, hdr->addr2, GFP_ATOMIC)) return RX_DROP_UNUSABLE; return RX_DROP_MONITOR; } return RX_CONTINUE; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) { struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; int keyidx; int hdrlen; ieee80211_rx_result result = RX_DROP_UNUSABLE; struct ieee80211_key *sta_ptk = NULL; int mmie_keyidx = -1; __le16 fc; /* * Key selection 101 * * There are four types of keys: * - GTK (group keys) * - IGTK (group keys for management frames) * - PTK (pairwise keys) * - STK (station-to-station pairwise keys) * * When selecting a key, we have to distinguish between multicast * (including broadcast) and unicast frames, the latter can only * use PTKs and STKs while the former always use GTKs and IGTKs. * Unless, of course, actual WEP keys ("pre-RSNA") are used, then * unicast frames can also use key indices like GTKs. Hence, if we * don't have a PTK/STK we check the key index for a WEP key. * * Note that in a regular BSS, multicast frames are sent by the * AP only, associated stations unicast the frame to the AP first * which then multicasts it on their behalf. * * There is also a slight problem in IBSS mode: GTKs are negotiated * with each station, that is something we don't currently handle. * The spec seems to expect that one negotiates the same key with * every station but there's no such requirement; VLANs could be * possible. */ /* * No point in finding a key and decrypting if the frame is neither * addressed to us nor a multicast frame. */ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; /* start without a key */ rx->key = NULL; if (rx->sta) sta_ptk = rcu_dereference(rx->sta->ptk); fc = hdr->frame_control; if (!ieee80211_has_protected(fc)) mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) { rx->key = sta_ptk; if ((status->flag & RX_FLAG_DECRYPTED) && (status->flag & RX_FLAG_IV_STRIPPED)) return RX_CONTINUE; /* Skip decryption if the frame is not protected. */ if (!ieee80211_has_protected(fc)) return RX_CONTINUE; } else if (mmie_keyidx >= 0) { /* Broadcast/multicast robust management frame / BIP */ if ((status->flag & RX_FLAG_DECRYPTED) && (status->flag & RX_FLAG_IV_STRIPPED)) return RX_CONTINUE; if (mmie_keyidx < NUM_DEFAULT_KEYS || mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) return RX_DROP_MONITOR; /* unexpected BIP keyidx */ if (rx->sta) rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]); if (!rx->key) rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]); } else if (!ieee80211_has_protected(fc)) { /* * The frame was not protected, so skip decryption. However, we * need to set rx->key if there is a key that could have been * used so that the frame may be dropped if encryption would * have been expected. */ struct ieee80211_key *key = NULL; struct ieee80211_sub_if_data *sdata = rx->sdata; int i; if (ieee80211_is_mgmt(fc) && is_multicast_ether_addr(hdr->addr1) && (key = rcu_dereference(rx->sdata->default_mgmt_key))) rx->key = key; else { if (rx->sta) { for (i = 0; i < NUM_DEFAULT_KEYS; i++) { key = rcu_dereference(rx->sta->gtk[i]); if (key) break; } } if (!key) { for (i = 0; i < NUM_DEFAULT_KEYS; i++) { key = rcu_dereference(sdata->keys[i]); if (key) break; } } if (key) rx->key = key; } return RX_CONTINUE; } else { u8 keyid; /* * The device doesn't give us the IV so we won't be * able to look up the key. That's ok though, we * don't need to decrypt the frame, we just won't * be able to keep statistics accurate. * Except for key threshold notifications, should * we somehow allow the driver to tell us which key * the hardware used if this flag is set? */ if ((status->flag & RX_FLAG_DECRYPTED) && (status->flag & RX_FLAG_IV_STRIPPED)) return RX_CONTINUE; hdrlen = ieee80211_hdrlen(fc); if (rx->skb->len < 8 + hdrlen) return RX_DROP_UNUSABLE; /* TODO: count this? */ /* * no need to call ieee80211_wep_get_keyidx, * it verifies a bunch of things we've done already */ skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); keyidx = keyid >> 6; /* check per-station GTK first, if multicast packet */ if (is_multicast_ether_addr(hdr->addr1) && rx->sta) rx->key = rcu_dereference(rx->sta->gtk[keyidx]); /* if not found, try default key */ if (!rx->key) { rx->key = rcu_dereference(rx->sdata->keys[keyidx]); /* * RSNA-protected unicast frames should always be * sent with pairwise or station-to-station keys, * but for WEP we allow using a key index as well. */ if (rx->key && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 && !is_multicast_ether_addr(hdr->addr1)) rx->key = NULL; } } if (rx->key) { if (unlikely(rx->key->flags & KEY_FLAG_TAINTED)) return RX_DROP_MONITOR; rx->key->tx_rx_count++; /* TODO: add threshold stuff again */ } else { return RX_DROP_MONITOR; } switch (rx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: result = ieee80211_crypto_wep_decrypt(rx); break; case WLAN_CIPHER_SUITE_TKIP: result = ieee80211_crypto_tkip_decrypt(rx); break; case WLAN_CIPHER_SUITE_CCMP: result = ieee80211_crypto_ccmp_decrypt(rx); break; case WLAN_CIPHER_SUITE_AES_CMAC: result = ieee80211_crypto_aes_cmac_decrypt(rx); break; default: /* * We can reach here only with HW-only algorithms * but why didn't it decrypt the frame?! */ return RX_DROP_UNUSABLE; } /* the hdr variable is invalid after the decrypt handlers */ /* either the frame has been decrypted or will be dropped */ status->flag |= RX_FLAG_DECRYPTED; return result; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx) { struct ieee80211_local *local; struct ieee80211_hdr *hdr; struct sk_buff *skb; local = rx->local; skb = rx->skb; hdr = (struct ieee80211_hdr *) skb->data; if (!local->pspolling) return RX_CONTINUE; if (!ieee80211_has_fromds(hdr->frame_control)) /* this is not from AP */ return RX_CONTINUE; if (!ieee80211_is_data(hdr->frame_control)) return RX_CONTINUE; if (!ieee80211_has_moredata(hdr->frame_control)) { /* AP has no more frames buffered for us */ local->pspolling = false; return RX_CONTINUE; } /* more data bit is set, let's request a new frame from the AP */ ieee80211_send_pspoll(local, rx->sdata); return RX_CONTINUE; } static void ap_sta_ps_start(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; atomic_inc(&sdata->bss->num_sta_ps); set_sta_flag(sta, WLAN_STA_PS_STA); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", sta->sta.addr, sta->sta.aid); } static void ap_sta_ps_end(struct sta_info *sta) { ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n", sta->sta.addr, sta->sta.aid); if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { ps_dbg(sta->sdata, "STA %pM aid %d driver-ps-blocked\n", sta->sta.addr, sta->sta.aid); return; } ieee80211_sta_ps_deliver_wakeup(sta); } int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) { struct sta_info *sta_inf = container_of(sta, struct sta_info, sta); bool in_ps; WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS)); /* Don't let the same PS state be set twice */ in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA); if ((start && in_ps) || (!start && !in_ps)) return -EINVAL; if (start) ap_sta_ps_start(sta_inf); else ap_sta_ps_end(sta_inf); return 0; } EXPORT_SYMBOL(ieee80211_sta_ps_transition); static ieee80211_rx_result debug_noinline ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_hdr *hdr = (void *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); int tid, ac; if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; if (sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_AP_VLAN) return RX_CONTINUE; /* * The device handles station powersave, so don't do anything about * uAPSD and PS-Poll frames (the latter shouldn't even come up from * it to mac80211 since they're handled.) */ if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS) return RX_CONTINUE; /* * Don't do anything if the station isn't already asleep. In * the uAPSD case, the station will probably be marked asleep, * in the PS-Poll case the station must be confused ... */ if (!test_sta_flag(rx->sta, WLAN_STA_PS_STA)) return RX_CONTINUE; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { if (!test_sta_flag(rx->sta, WLAN_STA_SP)) { if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) ieee80211_sta_ps_deliver_poll_response(rx->sta); else set_sta_flag(rx->sta, WLAN_STA_PSPOLL); } /* Free PS Poll skb here instead of returning RX_DROP that would * count as an dropped frame. */ dev_kfree_skb(rx->skb); return RX_QUEUED; } else if (!ieee80211_has_morefrags(hdr->frame_control) && !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && ieee80211_has_pm(hdr->frame_control) && (ieee80211_is_data_qos(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control))) { tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; ac = ieee802_1d_to_ac[tid & 7]; /* * If this AC is not trigger-enabled do nothing. * * NB: This could/should check a separate bitmap of trigger- * enabled queues, but for now we only implement uAPSD w/o * TSPEC changes to the ACs, so they're always the same. */ if (!(rx->sta->sta.uapsd_queues & BIT(ac))) return RX_CONTINUE; /* if we are in a service period, do nothing */ if (test_sta_flag(rx->sta, WLAN_STA_SP)) return RX_CONTINUE; if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) ieee80211_sta_ps_deliver_uapsd(rx->sta); else set_sta_flag(rx->sta, WLAN_STA_UAPSD); } return RX_CONTINUE; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) { struct sta_info *sta = rx->sta; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (!sta) return RX_CONTINUE; /* * Update last_rx only for IBSS packets which are for the current * BSSID to avoid keeping the current IBSS network alive in cases * where other STAs start using different BSSID. */ if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, NL80211_IFTYPE_ADHOC); if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid)) { sta->last_rx = jiffies; if (ieee80211_is_data(hdr->frame_control)) { sta->last_rx_rate_idx = status->rate_idx; sta->last_rx_rate_flag = status->flag; } } } else if (!is_multicast_ether_addr(hdr->addr1)) { /* * Mesh beacons will update last_rx when if they are found to * match the current local configuration when processed. */ sta->last_rx = jiffies; if (ieee80211_is_data(hdr->frame_control)) { sta->last_rx_rate_idx = status->rate_idx; sta->last_rx_rate_flag = status->flag; } } if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) ieee80211_sta_rx_notify(rx->sdata, hdr); sta->rx_fragments++; sta->rx_bytes += rx->skb->len; if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { sta->last_signal = status->signal; ewma_add(&sta->avg_signal, -status->signal); } /* * Change STA power saving mode only at the end of a frame * exchange sequence. */ if (!(sta->local->hw.flags & IEEE80211_HW_AP_LINK_PS) && !ieee80211_has_morefrags(hdr->frame_control) && !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { if (test_sta_flag(sta, WLAN_STA_PS_STA)) { /* * Ignore doze->wake transitions that are * indicated by non-data frames, the standard * is unclear here, but for example going to * PS mode and then scanning would cause a * doze->wake transition for the probe request, * and that is clearly undesirable. */ if (ieee80211_is_data(hdr->frame_control) && !ieee80211_has_pm(hdr->frame_control)) ap_sta_ps_end(sta); } else { if (ieee80211_has_pm(hdr->frame_control)) ap_sta_ps_start(sta); } } /* * Drop (qos-)data::nullfunc frames silently, since they * are used only to control station power saving mode. */ if (ieee80211_is_nullfunc(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) { I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); /* * If we receive a 4-addr nullfunc frame from a STA * that was not moved to a 4-addr STA vlan yet send * the event to userspace and for older hostapd drop * the frame to the monitor interface. */ if (ieee80211_has_a4(hdr->frame_control) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !rx->sdata->u.vlan.sta))) { if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT)) cfg80211_rx_unexpected_4addr_frame( rx->sdata->dev, sta->sta.addr, GFP_ATOMIC); return RX_DROP_MONITOR; } /* * Update counter and free packet here to avoid * counting this as a dropped packed. */ sta->rx_packets++; dev_kfree_skb(rx->skb); return RX_QUEUED; } return RX_CONTINUE; } /* ieee80211_rx_h_sta_process */ static inline struct ieee80211_fragment_entry * ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, unsigned int frag, unsigned int seq, int rx_queue, struct sk_buff **skb) { struct ieee80211_fragment_entry *entry; int idx; idx = sdata->fragment_next; entry = &sdata->fragments[sdata->fragment_next++]; if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) sdata->fragment_next = 0; if (!skb_queue_empty(&entry->skb_list)) __skb_queue_purge(&entry->skb_list); __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */ *skb = NULL; entry->first_frag_time = jiffies; entry->seq = seq; entry->rx_queue = rx_queue; entry->last_frag = frag; entry->ccmp = 0; entry->extra_len = 0; return entry; } static inline struct ieee80211_fragment_entry * ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, unsigned int frag, unsigned int seq, int rx_queue, struct ieee80211_hdr *hdr) { struct ieee80211_fragment_entry *entry; int i, idx; idx = sdata->fragment_next; for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { struct ieee80211_hdr *f_hdr; idx--; if (idx < 0) idx = IEEE80211_FRAGMENT_MAX - 1; entry = &sdata->fragments[idx]; if (skb_queue_empty(&entry->skb_list) || entry->seq != seq || entry->rx_queue != rx_queue || entry->last_frag + 1 != frag) continue; f_hdr = (struct ieee80211_hdr *)entry->skb_list.next->data; /* * Check ftype and addresses are equal, else check next fragment */ if (((hdr->frame_control ^ f_hdr->frame_control) & cpu_to_le16(IEEE80211_FCTL_FTYPE)) || !ether_addr_equal(hdr->addr1, f_hdr->addr1) || !ether_addr_equal(hdr->addr2, f_hdr->addr2)) continue; if (time_after(jiffies, entry->first_frag_time + 2 * HZ)) { __skb_queue_purge(&entry->skb_list); continue; } return entry; } return NULL; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr; u16 sc; __le16 fc; unsigned int frag, seq; struct ieee80211_fragment_entry *entry; struct sk_buff *skb; struct ieee80211_rx_status *status; hdr = (struct ieee80211_hdr *)rx->skb->data; fc = hdr->frame_control; sc = le16_to_cpu(hdr->seq_ctrl); frag = sc & IEEE80211_SCTL_FRAG; if (likely((!ieee80211_has_morefrags(fc) && frag == 0) || (rx->skb)->len < 24 || is_multicast_ether_addr(hdr->addr1))) { /* not fragmented */ goto out; } I802_DEBUG_INC(rx->local->rx_handlers_fragments); if (skb_linearize(rx->skb)) return RX_DROP_UNUSABLE; /* * skb_linearize() might change the skb->data and * previously cached variables (in this case, hdr) need to * be refreshed with the new data. */ hdr = (struct ieee80211_hdr *)rx->skb->data; seq = (sc & IEEE80211_SCTL_SEQ) >> 4; if (frag == 0) { /* This is the first fragment of a new frame. */ entry = ieee80211_reassemble_add(rx->sdata, frag, seq, rx->seqno_idx, &(rx->skb)); if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP && ieee80211_has_protected(fc)) { int queue = rx->security_idx; /* Store CCMP PN so that we can verify that the next * fragment has a sequential PN value. */ entry->ccmp = 1; memcpy(entry->last_pn, rx->key->u.ccmp.rx_pn[queue], CCMP_PN_LEN); } return RX_QUEUED; } /* This is a fragment for a frame that should already be pending in * fragment cache. Add this fragment to the end of the pending entry. */ entry = ieee80211_reassemble_find(rx->sdata, frag, seq, rx->seqno_idx, hdr); if (!entry) { I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); return RX_DROP_MONITOR; } /* Verify that MPDUs within one MSDU have sequential PN values. * (IEEE 802.11i, 8.3.3.4.5) */ if (entry->ccmp) { int i; u8 pn[CCMP_PN_LEN], *rpn; int queue; if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP) return RX_DROP_UNUSABLE; memcpy(pn, entry->last_pn, CCMP_PN_LEN); for (i = CCMP_PN_LEN - 1; i >= 0; i--) { pn[i]++; if (pn[i]) break; } queue = rx->security_idx; rpn = rx->key->u.ccmp.rx_pn[queue]; if (memcmp(pn, rpn, CCMP_PN_LEN)) return RX_DROP_UNUSABLE; memcpy(entry->last_pn, pn, CCMP_PN_LEN); } skb_pull(rx->skb, ieee80211_hdrlen(fc)); __skb_queue_tail(&entry->skb_list, rx->skb); entry->last_frag = frag; entry->extra_len += rx->skb->len; if (ieee80211_has_morefrags(fc)) { rx->skb = NULL; return RX_QUEUED; } rx->skb = __skb_dequeue(&entry->skb_list); if (skb_tailroom(rx->skb) < entry->extra_len) { I802_DEBUG_INC(rx->local->rx_expand_skb_head2); if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len, GFP_ATOMIC))) { I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); __skb_queue_purge(&entry->skb_list); return RX_DROP_UNUSABLE; } } while ((skb = __skb_dequeue(&entry->skb_list))) { memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len); dev_kfree_skb(skb); } /* Complete frame has been reassembled - process it now */ status = IEEE80211_SKB_RXCB(rx->skb); status->rx_flags |= IEEE80211_RX_FRAGMENTED; out: if (rx->sta) rx->sta->rx_packets++; if (is_multicast_ether_addr(hdr->addr1)) rx->local->dot11MulticastReceivedFrameCount++; else ieee80211_led_rx(rx->local); return RX_CONTINUE; } static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) { if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED))) return -EACCES; return 0; } static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) { struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); /* * Pass through unencrypted frames if the hardware has * decrypted them already. */ if (status->flag & RX_FLAG_DECRYPTED) return 0; /* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) && !ieee80211_is_nullfunc(fc) && ieee80211_is_data(fc) && (rx->key || rx->sdata->drop_unencrypted))) return -EACCES; return 0; } static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); __le16 fc = hdr->frame_control; /* * Pass through unencrypted frames if the hardware has * decrypted them already. */ if (status->flag & RX_FLAG_DECRYPTED) return 0; if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) { if (unlikely(!ieee80211_has_protected(fc) && ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && rx->key)) { if (ieee80211_is_deauth(fc)) cfg80211_send_unprot_deauth(rx->sdata->dev, rx->skb->data, rx->skb->len); else if (ieee80211_is_disassoc(fc)) cfg80211_send_unprot_disassoc(rx->sdata->dev, rx->skb->data, rx->skb->len); return -EACCES; } /* BIP does not use Protected field, so need to check MMIE */ if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) && ieee80211_get_mmie_keyidx(rx->skb) < 0)) { if (ieee80211_is_deauth(fc)) cfg80211_send_unprot_deauth(rx->sdata->dev, rx->skb->data, rx->skb->len); else if (ieee80211_is_disassoc(fc)) cfg80211_send_unprot_disassoc(rx->sdata->dev, rx->skb->data, rx->skb->len); return -EACCES; } /* * When using MFP, Action frames are not allowed prior to * having configured keys. */ if (unlikely(ieee80211_is_action(fc) && !rx->key && ieee80211_is_robust_mgmt_frame( (struct ieee80211_hdr *) rx->skb->data))) return -EACCES; } return 0; } static int __ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; bool check_port_control = false; struct ethhdr *ehdr; int ret; *port_control = false; if (ieee80211_has_a4(hdr->frame_control) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta) return -1; if (sdata->vif.type == NL80211_IFTYPE_STATION && !!sdata->u.mgd.use_4addr != !!ieee80211_has_a4(hdr->frame_control)) { if (!sdata->u.mgd.use_4addr) return -1; else check_port_control = true; } if (is_multicast_ether_addr(hdr->addr1) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->u.vlan.sta) return -1; ret = ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type); if (ret < 0) return ret; ehdr = (struct ethhdr *) rx->skb->data; if (ehdr->h_proto == rx->sdata->control_port_protocol) *port_control = true; else if (check_port_control) return -1; return 0; } /* * requires that rx->skb is a frame with ethernet header */ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc) { static const u8 pae_group_addr[ETH_ALEN] __aligned(2) = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 }; struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; /* * Allow EAPOL frames to us/the PAE group address regardless * of whether the frame was encrypted or not. */ if (ehdr->h_proto == rx->sdata->control_port_protocol && (ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || ether_addr_equal(ehdr->h_dest, pae_group_addr))) return true; if (ieee80211_802_1x_port_control(rx) || ieee80211_drop_unencrypted(rx, fc)) return false; return true; } /* * requires that rx->skb is a frame with ethernet header */ static void ieee80211_deliver_skb(struct ieee80211_rx_data *rx) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct net_device *dev = sdata->dev; struct sk_buff *skb, *xmit_skb; struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; struct sta_info *dsta; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); skb = rx->skb; xmit_skb = NULL; if ((sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && (status->rx_flags & IEEE80211_RX_RA_MATCH) && (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) { if (is_multicast_ether_addr(ehdr->h_dest)) { /* * send multicast frames both to higher layers in * local net stack and back to the wireless medium */ xmit_skb = skb_copy(skb, GFP_ATOMIC); if (!xmit_skb) net_info_ratelimited("%s: failed to clone multicast frame\n", dev->name); } else { dsta = sta_info_get(sdata, skb->data); if (dsta) { /* * The destination station is associated to * this AP (in this VLAN), so send the frame * directly to it and do not pass it to local * net stack. */ xmit_skb = skb; skb = NULL; } } } if (skb) { int align __maybe_unused; #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS /* * 'align' will only take the values 0 or 2 here * since all frames are required to be aligned * to 2-byte boundaries when being passed to * mac80211. That also explains the __skb_push() * below. */ align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3; if (align) { if (WARN_ON(skb_headroom(skb) < 3)) { dev_kfree_skb(skb); skb = NULL; } else { u8 *data = skb->data; size_t len = skb_headlen(skb); skb->data -= align; memmove(skb->data, data, len); skb_set_tail_pointer(skb, len); } } #endif if (skb) { /* deliver to local stack */ skb->protocol = eth_type_trans(skb, dev); memset(skb->cb, 0, sizeof(skb->cb)); netif_receive_skb(skb); } } if (xmit_skb) { /* * Send to wireless media and increase priority by 256 to * keep the received priority instead of reclassifying * the frame (see cfg80211_classify8021d). */ xmit_skb->priority += 256; xmit_skb->protocol = htons(ETH_P_802_3); skb_reset_network_header(xmit_skb); skb_reset_mac_header(xmit_skb); dev_queue_xmit(xmit_skb); } } static ieee80211_rx_result debug_noinline ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) { struct net_device *dev = rx->sdata->dev; struct sk_buff *skb = rx->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 fc = hdr->frame_control; struct sk_buff_head frame_list; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); if (unlikely(!ieee80211_is_data(fc))) return RX_CONTINUE; if (unlikely(!ieee80211_is_data_present(fc))) return RX_DROP_MONITOR; if (!(status->rx_flags & IEEE80211_RX_AMSDU)) return RX_CONTINUE; if (ieee80211_has_a4(hdr->frame_control) && rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !rx->sdata->u.vlan.sta) return RX_DROP_UNUSABLE; if (is_multicast_ether_addr(hdr->addr1) && ((rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && rx->sdata->u.vlan.sta) || (rx->sdata->vif.type == NL80211_IFTYPE_STATION && rx->sdata->u.mgd.use_4addr))) return RX_DROP_UNUSABLE; skb->dev = dev; __skb_queue_head_init(&frame_list); if (skb_linearize(skb)) return RX_DROP_UNUSABLE; ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, rx->sdata->vif.type, rx->local->hw.extra_tx_headroom, true); while (!skb_queue_empty(&frame_list)) { rx->skb = __skb_dequeue(&frame_list); if (!ieee80211_frame_allowed(rx, fc)) { dev_kfree_skb(rx->skb); continue; } dev->stats.rx_packets++; dev->stats.rx_bytes += rx->skb->len; ieee80211_deliver_skb(rx); } return RX_QUEUED; } #ifdef CONFIG_MAC80211_MESH static ieee80211_rx_result ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *fwd_hdr, *hdr; struct ieee80211_tx_info *info; struct ieee80211s_hdr *mesh_hdr; struct sk_buff *skb = rx->skb, *fwd_skb; struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); u16 q, hdrlen; hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); /* frame is in RMC, don't forward */ if (ieee80211_is_data(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && mesh_rmc_check(hdr->addr3, mesh_hdr, rx->sdata)) return RX_DROP_MONITOR; if (!ieee80211_is_data(hdr->frame_control)) return RX_CONTINUE; if (!mesh_hdr->ttl) return RX_DROP_MONITOR; if (mesh_hdr->flags & MESH_FLAGS_AE) { struct mesh_path *mppath; char *proxied_addr; char *mpp_addr; if (is_multicast_ether_addr(hdr->addr1)) { mpp_addr = hdr->addr3; proxied_addr = mesh_hdr->eaddr1; } else { mpp_addr = hdr->addr4; proxied_addr = mesh_hdr->eaddr2; } rcu_read_lock(); mppath = mpp_path_lookup(proxied_addr, sdata); if (!mppath) { mpp_path_add(proxied_addr, mpp_addr, sdata); } else { spin_lock_bh(&mppath->state_lock); if (!ether_addr_equal(mppath->mpp, mpp_addr)) memcpy(mppath->mpp, mpp_addr, ETH_ALEN); spin_unlock_bh(&mppath->state_lock); } rcu_read_unlock(); } /* Frame has reached destination. Don't forward */ if (!is_multicast_ether_addr(hdr->addr1) && ether_addr_equal(sdata->vif.addr, hdr->addr3)) return RX_CONTINUE; q = ieee80211_select_queue_80211(sdata, skb, hdr); if (ieee80211_queue_stopped(&local->hw, q)) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); return RX_DROP_MONITOR; } skb_set_queue_mapping(skb, q); if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) goto out; if (!--mesh_hdr->ttl) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); return RX_DROP_MONITOR; } if (!ifmsh->mshcfg.dot11MeshForwarding) goto out; fwd_skb = skb_copy(skb, GFP_ATOMIC); if (!fwd_skb) { net_info_ratelimited("%s: failed to clone mesh frame\n", sdata->name); goto out; } fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; info = IEEE80211_SKB_CB(fwd_skb); memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; info->control.jiffies = jiffies; if (is_multicast_ether_addr(fwd_hdr->addr1)) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); } else { /* unable to resolve next hop */ mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, 0, reason, fwd_hdr->addr2, sdata); IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); kfree_skb(fwd_skb); return RX_DROP_MONITOR; } IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames); ieee80211_add_pending_skb(local, fwd_skb); out: if (is_multicast_ether_addr(hdr->addr1) || sdata->dev->flags & IFF_PROMISC) return RX_CONTINUE; else return RX_DROP_MONITOR; } #endif static ieee80211_rx_result debug_noinline ieee80211_rx_h_data(struct ieee80211_rx_data *rx) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_local *local = rx->local; struct net_device *dev = sdata->dev; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; __le16 fc = hdr->frame_control; bool port_control; int err; if (unlikely(!ieee80211_is_data(hdr->frame_control))) return RX_CONTINUE; if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) return RX_DROP_MONITOR; /* * Send unexpected-4addr-frame event to hostapd. For older versions, * also drop the frame to cooked monitor interfaces. */ if (ieee80211_has_a4(hdr->frame_control) && sdata->vif.type == NL80211_IFTYPE_AP) { if (rx->sta && !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) cfg80211_rx_unexpected_4addr_frame( rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC); return RX_DROP_MONITOR; } err = __ieee80211_data_to_8023(rx, &port_control); if (unlikely(err)) return RX_DROP_UNUSABLE; if (!ieee80211_frame_allowed(rx, fc)) return RX_DROP_MONITOR; if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && unlikely(port_control) && sdata->bss) { sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); dev = sdata->dev; rx->sdata = sdata; } rx->skb->dev = dev; dev->stats.rx_packets++; dev->stats.rx_bytes += rx->skb->len; if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 && !is_multicast_ether_addr( ((struct ethhdr *)rx->skb->data)->h_dest) && (!local->scanning && !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) { mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); } ieee80211_deliver_skb(rx); return RX_QUEUED; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx) { struct sk_buff *skb = rx->skb; struct ieee80211_bar *bar = (struct ieee80211_bar *)skb->data; struct tid_ampdu_rx *tid_agg_rx; u16 start_seq_num; u16 tid; if (likely(!ieee80211_is_ctl(bar->frame_control))) return RX_CONTINUE; if (ieee80211_is_back_req(bar->frame_control)) { struct { __le16 control, start_seq_num; } __packed bar_data; if (!rx->sta) return RX_DROP_MONITOR; if (skb_copy_bits(skb, offsetof(struct ieee80211_bar, control), &bar_data, sizeof(bar_data))) return RX_DROP_MONITOR; tid = le16_to_cpu(bar_data.control) >> 12; tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]); if (!tid_agg_rx) return RX_DROP_MONITOR; start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4; /* reset session timer */ if (tid_agg_rx->timeout) mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(tid_agg_rx->timeout)); spin_lock(&tid_agg_rx->reorder_lock); /* release stored frames up to start of BAR */ ieee80211_release_reorder_frames(rx->sdata, tid_agg_rx, start_seq_num); spin_unlock(&tid_agg_rx->reorder_lock); kfree_skb(skb); return RX_QUEUED; } /* * After this point, we only want management frames, * so we can drop all remaining control frames to * cooked monitor interfaces. */ return RX_DROP_MONITOR; } static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *resp; if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) { /* Not to own unicast address */ return; } if (!ether_addr_equal(mgmt->sa, sdata->u.mgd.bssid) || !ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) { /* Not from the current AP or not associated yet. */ return; } if (len < 24 + 1 + sizeof(resp->u.action.u.sa_query)) { /* Too short SA Query request frame */ return; } skb = dev_alloc_skb(sizeof(*resp) + local->hw.extra_tx_headroom); if (skb == NULL) return; skb_reserve(skb, local->hw.extra_tx_headroom); resp = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(resp, 0, 24); memcpy(resp->da, mgmt->sa, ETH_ALEN); memcpy(resp->sa, sdata->vif.addr, ETH_ALEN); memcpy(resp->bssid, sdata->u.mgd.bssid, ETH_ALEN); resp->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); skb_put(skb, 1 + sizeof(resp->u.action.u.sa_query)); resp->u.action.category = WLAN_CATEGORY_SA_QUERY; resp->u.action.u.sa_query.action = WLAN_ACTION_SA_QUERY_RESPONSE; memcpy(resp->u.action.u.sa_query.trans_id, mgmt->u.action.u.sa_query.trans_id, WLAN_SA_QUERY_TR_ID_LEN); ieee80211_tx_skb(sdata, skb); } static ieee80211_rx_result debug_noinline ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); /* * From here on, look only at management frames. * Data and control frames are already handled, * and unknown (reserved) frames are useless. */ if (rx->skb->len < 24) return RX_DROP_MONITOR; if (!ieee80211_is_mgmt(mgmt->frame_control)) return RX_DROP_MONITOR; if (rx->sdata->vif.type == NL80211_IFTYPE_AP && ieee80211_is_beacon(mgmt->frame_control) && !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { int sig = 0; if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) sig = status->signal; cfg80211_report_obss_beacon(rx->local->hw.wiphy, rx->skb->data, rx->skb->len, status->freq, sig, GFP_ATOMIC); rx->flags |= IEEE80211_RX_BEACON_REPORTED; } if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_DROP_MONITOR; if (ieee80211_drop_unencrypted_mgmt(rx)) return RX_DROP_UNUSABLE; return RX_CONTINUE; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_action(struct ieee80211_rx_data *rx) { struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); int len = rx->skb->len; if (!ieee80211_is_action(mgmt->frame_control)) return RX_CONTINUE; /* drop too small frames */ if (len < IEEE80211_MIN_ACTION_SIZE) return RX_DROP_UNUSABLE; if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) return RX_DROP_UNUSABLE; if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_DROP_UNUSABLE; switch (mgmt->u.action.category) { case WLAN_CATEGORY_HT: /* reject HT action frames from stations not supporting HT */ if (!rx->sta->sta.ht_cap.ht_supported) goto invalid; if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_ADHOC) break; /* verify action & smps_control are present */ if (len < IEEE80211_MIN_ACTION_SIZE + 2) goto invalid; switch (mgmt->u.action.u.ht_smps.action) { case WLAN_HT_ACTION_SMPS: { struct ieee80211_supported_band *sband; u8 smps; /* convert to HT capability */ switch (mgmt->u.action.u.ht_smps.smps_control) { case WLAN_HT_SMPS_CONTROL_DISABLED: smps = WLAN_HT_CAP_SM_PS_DISABLED; break; case WLAN_HT_SMPS_CONTROL_STATIC: smps = WLAN_HT_CAP_SM_PS_STATIC; break; case WLAN_HT_SMPS_CONTROL_DYNAMIC: smps = WLAN_HT_CAP_SM_PS_DYNAMIC; break; default: goto invalid; } smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; /* if no change do nothing */ if ((rx->sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SM_PS) == smps) goto handled; rx->sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS; rx->sta->sta.ht_cap.cap |= smps; sband = rx->local->hw.wiphy->bands[status->band]; rate_control_rate_update(local, sband, rx->sta, IEEE80211_RC_SMPS_CHANGED); goto handled; } default: goto invalid; } break; case WLAN_CATEGORY_BACK: if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_ADHOC) break; /* verify action_code is present */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) break; switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.addba_req))) goto invalid; break; case WLAN_ACTION_ADDBA_RESP: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.addba_resp))) goto invalid; break; case WLAN_ACTION_DELBA: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.delba))) goto invalid; break; default: goto invalid; } goto queue; case WLAN_CATEGORY_SPECTRUM_MGMT: if (status->band != IEEE80211_BAND_5GHZ) break; if (sdata->vif.type != NL80211_IFTYPE_STATION) break; /* verify action_code is present */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) break; switch (mgmt->u.action.u.measurement.action_code) { case WLAN_ACTION_SPCT_MSR_REQ: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.measurement))) break; ieee80211_process_measurement_req(sdata, mgmt, len); goto handled; case WLAN_ACTION_SPCT_CHL_SWITCH: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.chan_switch))) break; if (sdata->vif.type != NL80211_IFTYPE_STATION) break; if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) break; goto queue; } break; case WLAN_CATEGORY_SA_QUERY: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.sa_query))) break; switch (mgmt->u.action.u.sa_query.action) { case WLAN_ACTION_SA_QUERY_REQUEST: if (sdata->vif.type != NL80211_IFTYPE_STATION) break; ieee80211_process_sa_query_req(sdata, mgmt, len); goto handled; } break; case WLAN_CATEGORY_SELF_PROTECTED: switch (mgmt->u.action.u.self_prot.action_code) { case WLAN_SP_MESH_PEERING_OPEN: case WLAN_SP_MESH_PEERING_CLOSE: case WLAN_SP_MESH_PEERING_CONFIRM: if (!ieee80211_vif_is_mesh(&sdata->vif)) goto invalid; if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) /* userspace handles this frame */ break; goto queue; case WLAN_SP_MGK_INFORM: case WLAN_SP_MGK_ACK: if (!ieee80211_vif_is_mesh(&sdata->vif)) goto invalid; break; } break; case WLAN_CATEGORY_MESH_ACTION: if (!ieee80211_vif_is_mesh(&sdata->vif)) break; if (mesh_action_is_path_sel(mgmt) && (!mesh_path_sel_is_hwmp(sdata))) break; goto queue; } return RX_CONTINUE; invalid: status->rx_flags |= IEEE80211_RX_MALFORMED_ACTION_FRM; /* will return in the next handlers */ return RX_CONTINUE; handled: if (rx->sta) rx->sta->rx_packets++; dev_kfree_skb(rx->skb); return RX_QUEUED; queue: rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; skb_queue_tail(&sdata->skb_queue, rx->skb); ieee80211_queue_work(&local->hw, &sdata->work); if (rx->sta) rx->sta->rx_packets++; return RX_QUEUED; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); int sig = 0; /* skip known-bad action frames and return them in the next handler */ if (status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) return RX_CONTINUE; /* * Getting here means the kernel doesn't know how to handle * it, but maybe userspace does ... include returned frames * so userspace can register for those to know whether ones * it transmitted were processed or returned. */ if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) sig = status->signal; if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig, rx->skb->data, rx->skb->len, GFP_ATOMIC)) { if (rx->sta) rx->sta->rx_packets++; dev_kfree_skb(rx->skb); return RX_QUEUED; } return RX_CONTINUE; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx) { struct ieee80211_local *local = rx->local; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; struct sk_buff *nskb; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); if (!ieee80211_is_action(mgmt->frame_control)) return RX_CONTINUE; /* * For AP mode, hostapd is responsible for handling any action * frames that we didn't handle, including returning unknown * ones. For all other modes we will return them to the sender, * setting the 0x80 bit in the action category, as required by * 802.11-2012 9.24.4. * Newer versions of hostapd shall also use the management frame * registration mechanisms, but older ones still use cooked * monitor interfaces so push all frames there. */ if (!(status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) && (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) return RX_DROP_MONITOR; if (is_multicast_ether_addr(mgmt->da)) return RX_DROP_MONITOR; /* do not return rejected action frames */ if (mgmt->u.action.category & 0x80) return RX_DROP_UNUSABLE; nskb = skb_copy_expand(rx->skb, local->hw.extra_tx_headroom, 0, GFP_ATOMIC); if (nskb) { struct ieee80211_mgmt *nmgmt = (void *)nskb->data; nmgmt->u.action.category |= 0x80; memcpy(nmgmt->da, nmgmt->sa, ETH_ALEN); memcpy(nmgmt->sa, rx->sdata->vif.addr, ETH_ALEN); memset(nskb->cb, 0, sizeof(nskb->cb)); ieee80211_tx_skb(rx->sdata, nskb); } dev_kfree_skb(rx->skb); return RX_QUEUED; } static ieee80211_rx_result debug_noinline ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_mgmt *mgmt = (void *)rx->skb->data; __le16 stype; stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE); if (!ieee80211_vif_is_mesh(&sdata->vif) && sdata->vif.type != NL80211_IFTYPE_ADHOC && sdata->vif.type != NL80211_IFTYPE_STATION) return RX_DROP_MONITOR; switch (stype) { case cpu_to_le16(IEEE80211_STYPE_AUTH): case cpu_to_le16(IEEE80211_STYPE_BEACON): case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): /* process for all: mesh, mlme, ibss */ break; case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_DEAUTH): case cpu_to_le16(IEEE80211_STYPE_DISASSOC): if (is_multicast_ether_addr(mgmt->da) && !is_broadcast_ether_addr(mgmt->da)) return RX_DROP_MONITOR; /* process only for station */ if (sdata->vif.type != NL80211_IFTYPE_STATION) return RX_DROP_MONITOR; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): /* process only for ibss */ if (sdata->vif.type != NL80211_IFTYPE_ADHOC) return RX_DROP_MONITOR; break; default: return RX_DROP_MONITOR; } /* queue up frame and kick off work to process it */ rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; skb_queue_tail(&sdata->skb_queue, rx->skb); ieee80211_queue_work(&rx->local->hw, &sdata->work); if (rx->sta) rx->sta->rx_packets++; return RX_QUEUED; } /* TODO: use IEEE80211_RX_FRAGMENTED */ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, struct ieee80211_rate *rate) { struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local = rx->local; struct sk_buff *skb = rx->skb, *skb2; struct net_device *prev_dev = NULL; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); int needed_headroom; /* * If cooked monitor has been processed already, then * don't do it again. If not, set the flag. */ if (rx->flags & IEEE80211_RX_CMNTR) goto out_free_skb; rx->flags |= IEEE80211_RX_CMNTR; /* If there are no cooked monitor interfaces, just free the SKB */ if (!local->cooked_mntrs) goto out_free_skb; /* room for the radiotap header based on driver features */ needed_headroom = ieee80211_rx_radiotap_len(local, status); if (skb_headroom(skb) < needed_headroom && pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) goto out_free_skb; /* prepend radiotap information */ ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, false); skb_set_mac_header(skb, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); list_for_each_entry_rcu(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) continue; if (prev_dev) { skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { skb2->dev = prev_dev; netif_receive_skb(skb2); } } prev_dev = sdata->dev; sdata->dev->stats.rx_packets++; sdata->dev->stats.rx_bytes += skb->len; } if (prev_dev) { skb->dev = prev_dev; netif_receive_skb(skb); return; } out_free_skb: dev_kfree_skb(skb); } static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, ieee80211_rx_result res) { switch (res) { case RX_DROP_MONITOR: I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop); if (rx->sta) rx->sta->rx_dropped++; /* fall through */ case RX_CONTINUE: { struct ieee80211_rate *rate = NULL; struct ieee80211_supported_band *sband; struct ieee80211_rx_status *status; status = IEEE80211_SKB_RXCB((rx->skb)); sband = rx->local->hw.wiphy->bands[status->band]; if (!(status->flag & RX_FLAG_HT)) rate = &sband->bitrates[status->rate_idx]; ieee80211_rx_cooked_monitor(rx, rate); break; } case RX_DROP_UNUSABLE: I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop); if (rx->sta) rx->sta->rx_dropped++; dev_kfree_skb(rx->skb); break; case RX_QUEUED: I802_DEBUG_INC(rx->sdata->local->rx_handlers_queued); break; } } static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) { ieee80211_rx_result res = RX_DROP_MONITOR; struct sk_buff *skb; #define CALL_RXH(rxh) \ do { \ res = rxh(rx); \ if (res != RX_CONTINUE) \ goto rxh_next; \ } while (0); spin_lock(&rx->local->rx_skb_queue.lock); if (rx->local->running_rx_handler) goto unlock; rx->local->running_rx_handler = true; while ((skb = __skb_dequeue(&rx->local->rx_skb_queue))) { spin_unlock(&rx->local->rx_skb_queue.lock); /* * all the other fields are valid across frames * that belong to an aMPDU since they are on the * same TID from the same station */ rx->skb = skb; CALL_RXH(ieee80211_rx_h_decrypt) CALL_RXH(ieee80211_rx_h_check_more_data) CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll) CALL_RXH(ieee80211_rx_h_sta_process) CALL_RXH(ieee80211_rx_h_defragment) CALL_RXH(ieee80211_rx_h_michael_mic_verify) /* must be after MMIC verify so header is counted in MPDU mic */ #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&rx->sdata->vif)) CALL_RXH(ieee80211_rx_h_mesh_fwding); #endif CALL_RXH(ieee80211_rx_h_amsdu) CALL_RXH(ieee80211_rx_h_data) CALL_RXH(ieee80211_rx_h_ctrl); CALL_RXH(ieee80211_rx_h_mgmt_check) CALL_RXH(ieee80211_rx_h_action) CALL_RXH(ieee80211_rx_h_userspace_mgmt) CALL_RXH(ieee80211_rx_h_action_return) CALL_RXH(ieee80211_rx_h_mgmt) rxh_next: ieee80211_rx_handlers_result(rx, res); spin_lock(&rx->local->rx_skb_queue.lock); #undef CALL_RXH } rx->local->running_rx_handler = false; unlock: spin_unlock(&rx->local->rx_skb_queue.lock); } static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) { ieee80211_rx_result res = RX_DROP_MONITOR; #define CALL_RXH(rxh) \ do { \ res = rxh(rx); \ if (res != RX_CONTINUE) \ goto rxh_next; \ } while (0); CALL_RXH(ieee80211_rx_h_check) ieee80211_rx_reorder_ampdu(rx); ieee80211_rx_handlers(rx); return; rxh_next: ieee80211_rx_handlers_result(rx, res); #undef CALL_RXH } /* * This function makes calls into the RX path, therefore * it has to be invoked under RCU read lock. */ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) { struct ieee80211_rx_data rx = { .sta = sta, .sdata = sta->sdata, .local = sta->local, /* This is OK -- must be QoS data frame */ .security_idx = tid, .seqno_idx = tid, .flags = 0, }; struct tid_ampdu_rx *tid_agg_rx; tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); if (!tid_agg_rx) return; spin_lock(&tid_agg_rx->reorder_lock); ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx); spin_unlock(&tid_agg_rx->reorder_lock); ieee80211_rx_handlers(&rx); } /* main receive path */ static int prepare_for_handlers(struct ieee80211_rx_data *rx, struct ieee80211_hdr *hdr) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); int multicast = is_multicast_ether_addr(hdr->addr1); switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!bssid && !sdata->u.mgd.use_4addr) return 0; if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { if (!(sdata->dev->flags & IFF_PROMISC) || sdata->u.mgd.use_4addr) return 0; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } break; case NL80211_IFTYPE_ADHOC: if (!bssid) return 0; if (ieee80211_is_beacon(hdr->frame_control)) { return 1; } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) { return 0; } else if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { if (!(sdata->dev->flags & IFF_PROMISC)) return 0; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } else if (!rx->sta) { int rate_idx; if (status->flag & RX_FLAG_HT) rate_idx = 0; /* TODO: HT rates */ else rate_idx = status->rate_idx; ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2, BIT(rate_idx)); } break; case NL80211_IFTYPE_MESH_POINT: if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { if (!(sdata->dev->flags & IFF_PROMISC)) return 0; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP: if (!bssid) { if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) return 0; } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { /* * Accept public action frames even when the * BSSID doesn't match, this is used for P2P * and location updates. Note that mac80211 * itself never looks at these frames. */ if (ieee80211_is_public_action(hdr, skb->len)) return 1; if (!ieee80211_is_beacon(hdr->frame_control)) return 0; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } break; case NL80211_IFTYPE_WDS: if (bssid || !ieee80211_is_data(hdr->frame_control)) return 0; if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2)) return 0; break; case NL80211_IFTYPE_P2P_DEVICE: if (!ieee80211_is_public_action(hdr, skb->len) && !ieee80211_is_probe_req(hdr->frame_control) && !ieee80211_is_probe_resp(hdr->frame_control) && !ieee80211_is_beacon(hdr->frame_control)) return 0; if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) status->rx_flags &= ~IEEE80211_RX_RA_MATCH; break; default: /* should never get here */ WARN_ON_ONCE(1); break; } return 1; } /* * This function returns whether or not the SKB * was destined for RX processing or not, which, * if consume is true, is equivalent to whether * or not the skb was consumed. */ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, struct sk_buff *skb, bool consume) { struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; int prepares; rx->skb = skb; status->rx_flags |= IEEE80211_RX_RA_MATCH; prepares = prepare_for_handlers(rx, hdr); if (!prepares) return false; if (!consume) { skb = skb_copy(skb, GFP_ATOMIC); if (!skb) { if (net_ratelimit()) wiphy_debug(local->hw.wiphy, "failed to copy skb for %s\n", sdata->name); return true; } rx->skb = skb; } ieee80211_invoke_rx_handlers(rx); return true; } /* * This is the actual Rx frames handler. as it blongs to Rx path it must * be called with rcu_read_lock protection. */ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; struct ieee80211_hdr *hdr; __le16 fc; struct ieee80211_rx_data rx; struct ieee80211_sub_if_data *prev; struct sta_info *sta, *tmp, *prev_sta; int err = 0; fc = ((struct ieee80211_hdr *)skb->data)->frame_control; memset(&rx, 0, sizeof(rx)); rx.skb = skb; rx.local = local; if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc)) local->dot11ReceivedFragmentCount++; if (ieee80211_is_mgmt(fc)) err = skb_linearize(skb); else err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); if (err) { dev_kfree_skb(skb); return; } hdr = (struct ieee80211_hdr *)skb->data; ieee80211_parse_qos(&rx); ieee80211_verify_alignment(&rx); if (unlikely(ieee80211_is_probe_resp(hdr->frame_control) || ieee80211_is_beacon(hdr->frame_control))) ieee80211_scan_rx(local, skb); if (ieee80211_is_data(fc)) { prev_sta = NULL; for_each_sta_info(local, hdr->addr2, sta, tmp) { if (!prev_sta) { prev_sta = sta; continue; } rx.sta = prev_sta; rx.sdata = prev_sta->sdata; ieee80211_prepare_and_rx_handle(&rx, skb, false); prev_sta = sta; } if (prev_sta) { rx.sta = prev_sta; rx.sdata = prev_sta->sdata; if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) return; goto out; } } prev = NULL; list_for_each_entry_rcu(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; if (sdata->vif.type == NL80211_IFTYPE_MONITOR || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) continue; /* * frame is destined for this interface, but if it's * not also for the previous one we handle that after * the loop to avoid copying the SKB once too much */ if (!prev) { prev = sdata; continue; } rx.sta = sta_info_get_bss(prev, hdr->addr2); rx.sdata = prev; ieee80211_prepare_and_rx_handle(&rx, skb, false); prev = sdata; } if (prev) { rx.sta = sta_info_get_bss(prev, hdr->addr2); rx.sdata = prev; if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) return; } out: dev_kfree_skb(skb); } /* * This is the receive path handler. It is called by a low level driver when an * 802.11 MPDU is received from the hardware. */ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate = NULL; struct ieee80211_supported_band *sband; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); WARN_ON_ONCE(softirq_count() == 0); if (WARN_ON(status->band < 0 || status->band >= IEEE80211_NUM_BANDS)) goto drop; sband = local->hw.wiphy->bands[status->band]; if (WARN_ON(!sband)) goto drop; /* * If we're suspending, it is possible although not too likely * that we'd be receiving frames after having already partially * quiesced the stack. We can't process such frames then since * that might, for example, cause stations to be added or other * driver callbacks be invoked. */ if (unlikely(local->quiescing || local->suspended)) goto drop; /* We might be during a HW reconfig, prevent Rx for the same reason */ if (unlikely(local->in_reconfig)) goto drop; /* * The same happens when we're not even started, * but that's worth a warning. */ if (WARN_ON(!local->started)) goto drop; if (likely(!(status->flag & RX_FLAG_FAILED_PLCP_CRC))) { /* * Validate the rate, unless a PLCP error means that * we probably can't have a valid rate here anyway. */ if (status->flag & RX_FLAG_HT) { /* * rate_idx is MCS index, which can be [0-76] * as documented on: * * http://wireless.kernel.org/en/developers/Documentation/ieee80211/802.11n * * Anything else would be some sort of driver or * hardware error. The driver should catch hardware * errors. */ if (WARN((status->rate_idx < 0 || status->rate_idx > 76), "Rate marked as an HT rate but passed " "status->rate_idx is not " "an MCS index [0-76]: %d (0x%02x)\n", status->rate_idx, status->rate_idx)) goto drop; } else { if (WARN_ON(status->rate_idx < 0 || status->rate_idx >= sband->n_bitrates)) goto drop; rate = &sband->bitrates[status->rate_idx]; } } status->rx_flags = 0; /* * key references and virtual interfaces are protected using RCU * and this requires that we are in a read-side RCU section during * receive processing */ rcu_read_lock(); /* * Frames with failed FCS/PLCP checksum are not returned, * all other frames are returned without radiotap header * if it was previously present. * Also, frames with less than 16 bytes are dropped. */ skb = ieee80211_rx_monitor(local, skb, rate); if (!skb) { rcu_read_unlock(); return; } ieee80211_tpt_led_trig_rx(local, ((struct ieee80211_hdr *)skb->data)->frame_control, skb->len); __ieee80211_rx_handle_packet(hw, skb); rcu_read_unlock(); return; drop: kfree_skb(skb); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) EXPORT_SYMBOL(ieee80211_rx); #else EXPORT_SYMBOL(mac80211_ieee80211_rx); #endif /* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); skb->pkt_type = IEEE80211_RX_MSG; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); } EXPORT_SYMBOL(ieee80211_rx_irqsafe); compat-drivers-2012-09-18/net/mac80211/rate.c0000644000175000017500000003203212026211315017401 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "rate.h" #include "ieee80211_i.h" #include "debugfs.h" struct rate_control_alg { struct list_head list; struct rate_control_ops *ops; }; static LIST_HEAD(rate_ctrl_algs); static DEFINE_MUTEX(rate_ctrl_mutex); static char *ieee80211_default_rc_algo = CONFIG_COMPAT_MAC80211_RC_DEFAULT; module_param(ieee80211_default_rc_algo, charp, 0644); MODULE_PARM_DESC(ieee80211_default_rc_algo, "Default rate control algorithm for mac80211 to use"); int ieee80211_rate_control_register(struct rate_control_ops *ops) { struct rate_control_alg *alg; if (!ops->name) return -EINVAL; mutex_lock(&rate_ctrl_mutex); list_for_each_entry(alg, &rate_ctrl_algs, list) { if (!strcmp(alg->ops->name, ops->name)) { /* don't register an algorithm twice */ WARN_ON(1); mutex_unlock(&rate_ctrl_mutex); return -EALREADY; } } alg = kzalloc(sizeof(*alg), GFP_KERNEL); if (alg == NULL) { mutex_unlock(&rate_ctrl_mutex); return -ENOMEM; } alg->ops = ops; list_add_tail(&alg->list, &rate_ctrl_algs); mutex_unlock(&rate_ctrl_mutex); return 0; } EXPORT_SYMBOL(ieee80211_rate_control_register); void ieee80211_rate_control_unregister(struct rate_control_ops *ops) { struct rate_control_alg *alg; mutex_lock(&rate_ctrl_mutex); list_for_each_entry(alg, &rate_ctrl_algs, list) { if (alg->ops == ops) { list_del(&alg->list); kfree(alg); break; } } mutex_unlock(&rate_ctrl_mutex); } EXPORT_SYMBOL(ieee80211_rate_control_unregister); static struct rate_control_ops * ieee80211_try_rate_control_ops_get(const char *name) { struct rate_control_alg *alg; struct rate_control_ops *ops = NULL; if (!name) return NULL; mutex_lock(&rate_ctrl_mutex); list_for_each_entry(alg, &rate_ctrl_algs, list) { if (!strcmp(alg->ops->name, name)) if (try_module_get(alg->ops->module)) { ops = alg->ops; break; } } mutex_unlock(&rate_ctrl_mutex); return ops; } /* Get the rate control algorithm. */ static struct rate_control_ops * ieee80211_rate_control_ops_get(const char *name) { struct rate_control_ops *ops; const char *alg_name; kparam_block_sysfs_write(ieee80211_default_rc_algo); if (!name) alg_name = ieee80211_default_rc_algo; else alg_name = name; ops = ieee80211_try_rate_control_ops_get(alg_name); if (!ops) { request_module("rc80211_%s", alg_name); ops = ieee80211_try_rate_control_ops_get(alg_name); } if (!ops && name) /* try default if specific alg requested but not found */ ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); /* try built-in one if specific alg requested but not found */ if (!ops && strlen(CONFIG_COMPAT_MAC80211_RC_DEFAULT)) ops = ieee80211_try_rate_control_ops_get(CONFIG_COMPAT_MAC80211_RC_DEFAULT); kparam_unblock_sysfs_write(ieee80211_default_rc_algo); return ops; } static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops) { module_put(ops->module); } #ifdef CONFIG_MAC80211_DEBUGFS static ssize_t rcname_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct rate_control_ref *ref = file->private_data; int len = strlen(ref->ops->name); return simple_read_from_buffer(userbuf, count, ppos, ref->ops->name, len); } static const struct file_operations rcname_ops = { .read = rcname_read, .open = simple_open, .llseek = default_llseek, }; #endif static struct rate_control_ref *rate_control_alloc(const char *name, struct ieee80211_local *local) { struct dentry *debugfsdir = NULL; struct rate_control_ref *ref; ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); if (!ref) goto fail_ref; ref->local = local; ref->ops = ieee80211_rate_control_ops_get(name); if (!ref->ops) goto fail_ops; #ifdef CONFIG_MAC80211_DEBUGFS debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir); local->debugfs.rcdir = debugfsdir; debugfs_create_file("name", 0400, debugfsdir, ref, &rcname_ops); #endif ref->priv = ref->ops->alloc(&local->hw, debugfsdir); if (!ref->priv) goto fail_priv; return ref; fail_priv: ieee80211_rate_control_ops_put(ref->ops); fail_ops: kfree(ref); fail_ref: return NULL; } static void rate_control_free(struct rate_control_ref *ctrl_ref) { ctrl_ref->ops->free(ctrl_ref->priv); #ifdef CONFIG_MAC80211_DEBUGFS debugfs_remove_recursive(ctrl_ref->local->debugfs.rcdir); ctrl_ref->local->debugfs.rcdir = NULL; #endif ieee80211_rate_control_ops_put(ctrl_ref->ops); kfree(ctrl_ref); } static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); __le16 fc; fc = hdr->frame_control; return (info->flags & (IEEE80211_TX_CTL_NO_ACK | IEEE80211_TX_CTL_USE_MINRATE)) || !ieee80211_is_data(fc); } static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, struct ieee80211_supported_band *sband) { u8 i; if (basic_rates == 0) return; /* assume basic rates unknown and accept rate */ if (*idx < 0) return; if (basic_rates & (1 << *idx)) return; /* selected rate is a basic rate */ for (i = *idx + 1; i <= sband->n_bitrates; i++) { if (basic_rates & (1 << i)) { *idx = i; return; } } /* could not find a basic rate; use original selection */ } static inline s8 rate_lowest_non_cck_index(struct ieee80211_supported_band *sband, struct ieee80211_sta *sta) { int i; for (i = 0; i < sband->n_bitrates; i++) { struct ieee80211_rate *srate = &sband->bitrates[i]; if ((srate->bitrate == 10) || (srate->bitrate == 20) || (srate->bitrate == 55) || (srate->bitrate == 110)) continue; if (rate_supported(sta, sband->band, i)) return i; } /* No matching rate found */ return 0; } bool rate_control_send_low(struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); struct ieee80211_supported_band *sband = txrc->sband; int mcast_rate; if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { if ((sband->band != IEEE80211_BAND_2GHZ) || !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta); else info->control.rates[0].idx = rate_lowest_non_cck_index(txrc->sband, sta); info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 1 : txrc->hw->max_rate_tries; if (!sta && txrc->bss) { mcast_rate = txrc->bss_conf->mcast_rate[sband->band]; if (mcast_rate > 0) { info->control.rates[0].idx = mcast_rate - 1; return true; } rc_send_low_broadcast(&info->control.rates[0].idx, txrc->bss_conf->basic_rates, sband); } return true; } return false; } EXPORT_SYMBOL(rate_control_send_low); static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate, int n_bitrates, u32 mask) { int j; /* See whether the selected rate or anything below it is allowed. */ for (j = rate->idx; j >= 0; j--) { if (mask & (1 << j)) { /* Okay, found a suitable rate. Use it. */ rate->idx = j; return true; } } /* Try to find a higher rate that would be allowed */ for (j = rate->idx + 1; j < n_bitrates; j++) { if (mask & (1 << j)) { /* Okay, found a suitable rate. Use it. */ rate->idx = j; return true; } } return false; } static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate, u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) { int i, j; int ridx, rbit; ridx = rate->idx / 8; rbit = rate->idx % 8; /* sanity check */ if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN) return false; /* See whether the selected rate or anything below it is allowed. */ for (i = ridx; i >= 0; i--) { for (j = rbit; j >= 0; j--) if (mcs_mask[i] & BIT(j)) { rate->idx = i * 8 + j; return true; } rbit = 7; } /* Try to find a higher rate that would be allowed */ ridx = (rate->idx + 1) / 8; rbit = (rate->idx + 1) % 8; for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) { for (j = rbit; j < 8; j++) if (mcs_mask[i] & BIT(j)) { rate->idx = i * 8 + j; return true; } rbit = 0; } return false; } static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, struct ieee80211_tx_rate_control *txrc, u32 mask, u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) { struct ieee80211_tx_rate alt_rate; /* handle HT rates */ if (rate->flags & IEEE80211_TX_RC_MCS) { if (rate_idx_match_mcs_mask(rate, mcs_mask)) return; /* also try the legacy rates. */ alt_rate.idx = 0; /* keep protection flags */ alt_rate.flags = rate->flags & (IEEE80211_TX_RC_USE_RTS_CTS | IEEE80211_TX_RC_USE_CTS_PROTECT | IEEE80211_TX_RC_USE_SHORT_PREAMBLE); alt_rate.count = rate->count; if (rate_idx_match_legacy_mask(&alt_rate, txrc->sband->n_bitrates, mask)) { *rate = alt_rate; return; } } else { struct sk_buff *skb = txrc->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; __le16 fc; /* handle legacy rates */ if (rate_idx_match_legacy_mask(rate, txrc->sband->n_bitrates, mask)) return; /* if HT BSS, and we handle a data frame, also try HT rates */ if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT) return; fc = hdr->frame_control; if (!ieee80211_is_data(fc)) return; alt_rate.idx = 0; /* keep protection flags */ alt_rate.flags = rate->flags & (IEEE80211_TX_RC_USE_RTS_CTS | IEEE80211_TX_RC_USE_CTS_PROTECT | IEEE80211_TX_RC_USE_SHORT_PREAMBLE); alt_rate.count = rate->count; alt_rate.flags |= IEEE80211_TX_RC_MCS; if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) || (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS)) alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) { *rate = alt_rate; return; } } /* * Uh.. No suitable rate exists. This should not really happen with * sane TX rate mask configurations. However, should someone manage to * configure supported rates and TX rate mask in incompatible way, * allow the frame to be transmitted with whatever the rate control * selected. */ } void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_tx_rate_control *txrc) { struct rate_control_ref *ref = sdata->local->rate_ctrl; void *priv_sta = NULL; struct ieee80211_sta *ista = NULL; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); int i; u32 mask; u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; if (sta && test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) { ista = &sta->sta; priv_sta = sta->rate_ctrl_priv; } for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { info->control.rates[i].idx = -1; info->control.rates[i].flags = 0; info->control.rates[i].count = 0; } if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) return; ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); /* * Try to enforce the rateidx mask the user wanted. skip this if the * default mask (allow all rates) is used to save some processing for * the common case. */ mask = sdata->rc_rateidx_mask[info->band]; memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band], sizeof(mcs_mask)); if (mask != (1 << txrc->sband->n_bitrates) - 1) { if (sta) { /* Filter out rates that the STA does not support */ mask &= sta->sta.supp_rates[info->band]; for (i = 0; i < sizeof(mcs_mask); i++) mcs_mask[i] &= sta->sta.ht_cap.mcs.rx_mask[i]; } /* * Make sure the rate index selected for each TX rate is * included in the configured mask and change the rate indexes * if needed. */ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { /* Skip invalid rates */ if (info->control.rates[i].idx < 0) break; rate_idx_match_mask(&info->control.rates[i], txrc, mask, mcs_mask); } } BUG_ON(info->control.rates[0].idx < 0); } int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, const char *name) { struct rate_control_ref *ref; ASSERT_RTNL(); if (local->open_count) return -EBUSY; if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) { if (WARN_ON(!local->ops->set_rts_threshold)) return -EINVAL; return 0; } ref = rate_control_alloc(name, local); if (!ref) { wiphy_warn(local->hw.wiphy, "Failed to select rate control algorithm\n"); return -ENOENT; } WARN_ON(local->rate_ctrl); local->rate_ctrl = ref; wiphy_debug(local->hw.wiphy, "Selected rate control algorithm '%s'\n", ref->ops->name); return 0; } void rate_control_deinitialize(struct ieee80211_local *local) { struct rate_control_ref *ref; ref = local->rate_ctrl; if (!ref) return; local->rate_ctrl = NULL; rate_control_free(ref); } compat-drivers-2012-09-18/net/mac80211/key.c0000644000175000017500000004314112026211315017241 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007-2008 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "debugfs_key.h" #include "aes_ccm.h" #include "aes_cmac.h" #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) #include #endif /** * DOC: Key handling basics * * Key handling in mac80211 is done based on per-interface (sub_if_data) * keys and per-station keys. Since each station belongs to an interface, * each station key also belongs to that interface. * * Hardware acceleration is done on a best-effort basis for algorithms * that are implemented in software, for each key the hardware is asked * to enable that key for offloading but if it cannot do that the key is * simply kept for software encryption (unless it is for an algorithm * that isn't implemented in software). * There is currently no way of knowing whether a key is handled in SW * or HW except by looking into debugfs. * * All key management is internally protected by a mutex. Within all * other parts of mac80211, key references are, just as STA structure * references, protected by RCU. Note, however, that some things are * unprotected, namely the key->sta dereferences within the hardware * acceleration functions. This means that sta_info_destroy() must * remove the key which waits for an RCU grace period. */ static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static void assert_key_lock(struct ieee80211_local *local) { lockdep_assert_held(&local->key_mtx); } static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata) { /* * When this count is zero, SKB resizing for allocating tailroom * for IV or MMIC is skipped. But, this check has created two race * cases in xmit path while transiting from zero count to one: * * 1. SKB resize was skipped because no key was added but just before * the xmit key is added and SW encryption kicks off. * * 2. SKB resize was skipped because all the keys were hw planted but * just before xmit one of the key is deleted and SW encryption kicks * off. * * In both the above case SW encryption will find not enough space for * tailroom and exits with WARN_ON. (See WARN_ONs at wpa.c) * * Solution has been explained at * http://mid.gmane.org/1308590980.4322.19.camel@jlt3.sipsolutions.net */ if (!sdata->crypto_tx_tailroom_needed_cnt++) { /* * Flush all XMIT packets currently using HW encryption or no * encryption at all if the count transition is from 0 -> 1. */ synchronize_net(); } } static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) { struct ieee80211_sub_if_data *sdata; struct sta_info *sta; int ret; might_sleep(); if (!key->local->ops->set_key) goto out_unsupported; assert_key_lock(key->local); sta = key->sta; /* * If this is a per-STA GTK, check if it * is supported; if not, return. */ if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) && !(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)) goto out_unsupported; if (sta && !sta->uploaded) goto out_unsupported; sdata = key->sdata; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { /* * The driver doesn't know anything about VLAN interfaces. * Hence, don't send GTKs for VLAN interfaces to the driver. */ if (!(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE)) goto out_unsupported; } ret = drv_set_key(key->local, SET_KEY, sdata, sta ? &sta->sta : NULL, &key->conf); if (!ret) { key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) sdata->crypto_tx_tailroom_needed_cnt--; WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)); return 0; } if (ret != -ENOSPC && ret != -EOPNOTSUPP) sdata_err(sdata, "failed to set key (%d, %pM) to hardware (%d)\n", key->conf.keyidx, sta ? sta->sta.addr : bcast_addr, ret); out_unsupported: switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_AES_CMAC: /* all of these we can do in software */ return 0; default: return -EINVAL; } } static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) { struct ieee80211_sub_if_data *sdata; struct sta_info *sta; int ret; might_sleep(); if (!key || !key->local->ops->set_key) return; assert_key_lock(key->local); if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) return; sta = key->sta; sdata = key->sdata; if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) increment_tailroom_need_count(sdata); ret = drv_set_key(key->local, DISABLE_KEY, sdata, sta ? &sta->sta : NULL, &key->conf); if (ret) sdata_err(sdata, "failed to remove key (%d, %pM) from hardware (%d)\n", key->conf.keyidx, sta ? sta->sta.addr : bcast_addr, ret); key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; } static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, bool uni, bool multi) { struct ieee80211_key *key = NULL; assert_key_lock(sdata->local); if (idx >= 0 && idx < NUM_DEFAULT_KEYS) key = key_mtx_dereference(sdata->local, sdata->keys[idx]); if (uni) rcu_assign_pointer(sdata->default_unicast_key, key); if (multi) rcu_assign_pointer(sdata->default_multicast_key, key); ieee80211_debugfs_key_update_default(sdata); } void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, bool uni, bool multi) { mutex_lock(&sdata->local->key_mtx); __ieee80211_set_default_key(sdata, idx, uni, multi); mutex_unlock(&sdata->local->key_mtx); } static void __ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx) { struct ieee80211_key *key = NULL; assert_key_lock(sdata->local); if (idx >= NUM_DEFAULT_KEYS && idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) key = key_mtx_dereference(sdata->local, sdata->keys[idx]); rcu_assign_pointer(sdata->default_mgmt_key, key); ieee80211_debugfs_key_update_default(sdata); } void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx) { mutex_lock(&sdata->local->key_mtx); __ieee80211_set_default_mgmt_key(sdata, idx); mutex_unlock(&sdata->local->key_mtx); } static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, bool pairwise, struct ieee80211_key *old, struct ieee80211_key *new) { int idx; bool defunikey, defmultikey, defmgmtkey; if (new) list_add_tail(&new->list, &sdata->key_list); if (sta && pairwise) { rcu_assign_pointer(sta->ptk, new); } else if (sta) { if (old) idx = old->conf.keyidx; else idx = new->conf.keyidx; rcu_assign_pointer(sta->gtk[idx], new); } else { WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); if (old) idx = old->conf.keyidx; else idx = new->conf.keyidx; defunikey = old && old == key_mtx_dereference(sdata->local, sdata->default_unicast_key); defmultikey = old && old == key_mtx_dereference(sdata->local, sdata->default_multicast_key); defmgmtkey = old && old == key_mtx_dereference(sdata->local, sdata->default_mgmt_key); if (defunikey && !new) __ieee80211_set_default_key(sdata, -1, true, false); if (defmultikey && !new) __ieee80211_set_default_key(sdata, -1, false, true); if (defmgmtkey && !new) __ieee80211_set_default_mgmt_key(sdata, -1); rcu_assign_pointer(sdata->keys[idx], new); if (defunikey && new) __ieee80211_set_default_key(sdata, new->conf.keyidx, true, false); if (defmultikey && new) __ieee80211_set_default_key(sdata, new->conf.keyidx, false, true); if (defmgmtkey && new) __ieee80211_set_default_mgmt_key(sdata, new->conf.keyidx); } if (old) list_del(&old->list); } struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, const u8 *key_data, size_t seq_len, const u8 *seq) { struct ieee80211_key *key; int i, j, err; BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); if (!key) return ERR_PTR(-ENOMEM); /* * Default to software encryption; we'll later upload the * key to the hardware if possible. */ key->conf.flags = 0; key->flags = 0; key->conf.cipher = cipher; key->conf.keyidx = idx; key->conf.keylen = key_len; switch (cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: key->conf.iv_len = WEP_IV_LEN; key->conf.icv_len = WEP_ICV_LEN; break; case WLAN_CIPHER_SUITE_TKIP: key->conf.iv_len = TKIP_IV_LEN; key->conf.icv_len = TKIP_ICV_LEN; if (seq) { for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { key->u.tkip.rx[i].iv32 = get_unaligned_le32(&seq[2]); key->u.tkip.rx[i].iv16 = get_unaligned_le16(seq); } } spin_lock_init(&key->u.tkip.txlock); break; case WLAN_CIPHER_SUITE_CCMP: key->conf.iv_len = CCMP_HDR_LEN; key->conf.icv_len = CCMP_MIC_LEN; if (seq) { for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) for (j = 0; j < CCMP_PN_LEN; j++) key->u.ccmp.rx_pn[i][j] = seq[CCMP_PN_LEN - j - 1]; } /* * Initialize AES key state here as an optimization so that * it does not need to be initialized for every packet. */ key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data); if (IS_ERR(key->u.ccmp.tfm)) { err = PTR_ERR(key->u.ccmp.tfm); kfree(key); return ERR_PTR(err); } break; case WLAN_CIPHER_SUITE_AES_CMAC: key->conf.iv_len = 0; key->conf.icv_len = sizeof(struct ieee80211_mmie); if (seq) for (j = 0; j < 6; j++) key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1]; /* * Initialize AES key state here as an optimization so that * it does not need to be initialized for every packet. */ key->u.aes_cmac.tfm = ieee80211_aes_cmac_key_setup(key_data); if (IS_ERR(key->u.aes_cmac.tfm)) { err = PTR_ERR(key->u.aes_cmac.tfm); kfree(key); return ERR_PTR(err); } break; } memcpy(key->conf.key, key_data, key_len); INIT_LIST_HEAD(&key->list); return key; } static void __ieee80211_key_destroy(struct ieee80211_key *key) { if (!key) return; /* * Synchronize so the TX path can no longer be using * this key before we free/remove it. */ synchronize_rcu(); if (key->local) ieee80211_key_disable_hw_accel(key); if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP) ieee80211_aes_key_free(key->u.ccmp.tfm); if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); if (key->local) { ieee80211_debugfs_key_remove(key); key->sdata->crypto_tx_tailroom_needed_cnt--; } kfree(key); } int ieee80211_key_link(struct ieee80211_key *key, struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { struct ieee80211_key *old_key; int idx, ret; bool pairwise; BUG_ON(!sdata); BUG_ON(!key); pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; idx = key->conf.keyidx; key->local = sdata->local; key->sdata = sdata; key->sta = sta; if (sta) { /* * some hardware cannot handle TKIP with QoS, so * we indicate whether QoS could be in use. */ if (test_sta_flag(sta, WLAN_STA_WME)) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } else { if (sdata->vif.type == NL80211_IFTYPE_STATION) { struct sta_info *ap; /* * We're getting a sta pointer in, so must be under * appropriate locking for sta_info_get(). */ /* same here, the AP could be using QoS */ ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid); if (ap) { if (test_sta_flag(ap, WLAN_STA_WME)) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } } } mutex_lock(&sdata->local->key_mtx); if (sta && pairwise) old_key = key_mtx_dereference(sdata->local, sta->ptk); else if (sta) old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]); else old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); increment_tailroom_need_count(sdata); __ieee80211_key_replace(sdata, sta, pairwise, old_key, key); __ieee80211_key_destroy(old_key); ieee80211_debugfs_key_add(key); ret = ieee80211_key_enable_hw_accel(key); mutex_unlock(&sdata->local->key_mtx); return ret; } void __ieee80211_key_free(struct ieee80211_key *key) { if (!key) return; /* * Replace key with nothingness if it was ever used. */ if (key->sdata) __ieee80211_key_replace(key->sdata, key->sta, key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, key, NULL); __ieee80211_key_destroy(key); } void ieee80211_key_free(struct ieee80211_local *local, struct ieee80211_key *key) { mutex_lock(&local->key_mtx); __ieee80211_key_free(key); mutex_unlock(&local->key_mtx); } void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) { struct ieee80211_key *key; ASSERT_RTNL(); if (WARN_ON(!ieee80211_sdata_running(sdata))) return; mutex_lock(&sdata->local->key_mtx); sdata->crypto_tx_tailroom_needed_cnt = 0; list_for_each_entry(key, &sdata->key_list, list) { increment_tailroom_need_count(sdata); ieee80211_key_enable_hw_accel(key); } mutex_unlock(&sdata->local->key_mtx); } void ieee80211_iter_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void (*iter)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, void *data), void *iter_data) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_key *key; struct ieee80211_sub_if_data *sdata; ASSERT_RTNL(); mutex_lock(&local->key_mtx); if (vif) { sdata = vif_to_sdata(vif); list_for_each_entry(key, &sdata->key_list, list) iter(hw, &sdata->vif, key->sta ? &key->sta->sta : NULL, &key->conf, iter_data); } else { list_for_each_entry(sdata, &local->interfaces, list) list_for_each_entry(key, &sdata->key_list, list) iter(hw, &sdata->vif, key->sta ? &key->sta->sta : NULL, &key->conf, iter_data); } mutex_unlock(&local->key_mtx); } EXPORT_SYMBOL(ieee80211_iter_keys); void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) { struct ieee80211_key *key; ASSERT_RTNL(); mutex_lock(&sdata->local->key_mtx); list_for_each_entry(key, &sdata->key_list, list) ieee80211_key_disable_hw_accel(key); mutex_unlock(&sdata->local->key_mtx); } void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) { struct ieee80211_key *key, *tmp; mutex_lock(&sdata->local->key_mtx); ieee80211_debugfs_key_remove_mgmt_default(sdata); list_for_each_entry_safe(key, tmp, &sdata->key_list, list) __ieee80211_key_free(key); ieee80211_debugfs_key_update_default(sdata); mutex_unlock(&sdata->local->key_mtx); } void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); trace_api_gtk_rekey_notify(sdata, bssid, replay_ctr); cfg80211_gtk_rekey_notify(sdata->dev, bssid, replay_ctr, gfp); } EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_notify); void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, struct ieee80211_key_seq *seq) { struct ieee80211_key *key; u64 pn64; if (WARN_ON(!(keyconf->flags & IEEE80211_KEY_FLAG_GENERATE_IV))) return; key = container_of(keyconf, struct ieee80211_key, conf); switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: seq->tkip.iv32 = key->u.tkip.tx.iv32; seq->tkip.iv16 = key->u.tkip.tx.iv16; break; case WLAN_CIPHER_SUITE_CCMP: pn64 = atomic64_read(&key->u.ccmp.tx_pn); seq->ccmp.pn[5] = pn64; seq->ccmp.pn[4] = pn64 >> 8; seq->ccmp.pn[3] = pn64 >> 16; seq->ccmp.pn[2] = pn64 >> 24; seq->ccmp.pn[1] = pn64 >> 32; seq->ccmp.pn[0] = pn64 >> 40; break; case WLAN_CIPHER_SUITE_AES_CMAC: pn64 = atomic64_read(&key->u.aes_cmac.tx_pn); seq->ccmp.pn[5] = pn64; seq->ccmp.pn[4] = pn64 >> 8; seq->ccmp.pn[3] = pn64 >> 16; seq->ccmp.pn[2] = pn64 >> 24; seq->ccmp.pn[1] = pn64 >> 32; seq->ccmp.pn[0] = pn64 >> 40; break; default: WARN_ON(1); } } EXPORT_SYMBOL(ieee80211_get_key_tx_seq); void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int tid, struct ieee80211_key_seq *seq) { struct ieee80211_key *key; const u8 *pn; key = container_of(keyconf, struct ieee80211_key, conf); switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: if (WARN_ON(tid < 0 || tid >= NUM_RX_DATA_QUEUES)) return; seq->tkip.iv32 = key->u.tkip.rx[tid].iv32; seq->tkip.iv16 = key->u.tkip.rx[tid].iv16; break; case WLAN_CIPHER_SUITE_CCMP: if (WARN_ON(tid < -1 || tid >= NUM_RX_DATA_QUEUES)) return; if (tid < 0) pn = key->u.ccmp.rx_pn[NUM_RX_DATA_QUEUES]; else pn = key->u.ccmp.rx_pn[tid]; memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN); break; case WLAN_CIPHER_SUITE_AES_CMAC: if (WARN_ON(tid != 0)) return; pn = key->u.aes_cmac.rx_pn; memcpy(seq->aes_cmac.pn, pn, CMAC_PN_LEN); break; } } EXPORT_SYMBOL(ieee80211_get_key_rx_seq); compat-drivers-2012-09-18/net/mac80211/debugfs_netdev.h0000644000175000017500000000130212026211315021433 0ustar mcgrofmcgrof/* routines exported for debugfs handling */ #ifndef __IEEE80211_DEBUGFS_NETDEV_H #define __IEEE80211_DEBUGFS_NETDEV_H #ifdef CONFIG_MAC80211_DEBUGFS void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata); #else static inline void ieee80211_debugfs_add_netdev( struct ieee80211_sub_if_data *sdata) {} static inline void ieee80211_debugfs_remove_netdev( struct ieee80211_sub_if_data *sdata) {} static inline void ieee80211_debugfs_rename_netdev( struct ieee80211_sub_if_data *sdata) {} #endif #endif /* __IEEE80211_DEBUGFS_NETDEV_H */ compat-drivers-2012-09-18/net/mac80211/tkip.c0000644000175000017500000002531112026211315017417 0ustar mcgrofmcgrof/* * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include "driver-ops.h" #include "key.h" #include "tkip.h" #include "wep.h" #define PHASE1_LOOP_COUNT 8 /* * 2-byte by 2-byte subset of the full AES S-box table; second part of this * table is identical to first part but byte-swapped */ static const u16 tkip_sbox[256] = { 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, }; static u16 tkipS(u16 val) { return tkip_sbox[val & 0xff] ^ swab16(tkip_sbox[val >> 8]); } static u8 *write_tkip_iv(u8 *pos, u16 iv16) { *pos++ = iv16 >> 8; *pos++ = ((iv16 >> 8) | 0x20) & 0x7f; *pos++ = iv16 & 0xFF; return pos; } /* * P1K := Phase1(TA, TK, TSC) * TA = transmitter address (48 bits) * TK = dot11DefaultKeyValue or dot11KeyMappingValue (128 bits) * TSC = TKIP sequence counter (48 bits, only 32 msb bits used) * P1K: 80 bits */ static void tkip_mixing_phase1(const u8 *tk, struct tkip_ctx *ctx, const u8 *ta, u32 tsc_IV32) { int i, j; u16 *p1k = ctx->p1k; p1k[0] = tsc_IV32 & 0xFFFF; p1k[1] = tsc_IV32 >> 16; p1k[2] = get_unaligned_le16(ta + 0); p1k[3] = get_unaligned_le16(ta + 2); p1k[4] = get_unaligned_le16(ta + 4); for (i = 0; i < PHASE1_LOOP_COUNT; i++) { j = 2 * (i & 1); p1k[0] += tkipS(p1k[4] ^ get_unaligned_le16(tk + 0 + j)); p1k[1] += tkipS(p1k[0] ^ get_unaligned_le16(tk + 4 + j)); p1k[2] += tkipS(p1k[1] ^ get_unaligned_le16(tk + 8 + j)); p1k[3] += tkipS(p1k[2] ^ get_unaligned_le16(tk + 12 + j)); p1k[4] += tkipS(p1k[3] ^ get_unaligned_le16(tk + 0 + j)) + i; } ctx->state = TKIP_STATE_PHASE1_DONE; ctx->p1k_iv32 = tsc_IV32; } static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx, u16 tsc_IV16, u8 *rc4key) { u16 ppk[6]; const u16 *p1k = ctx->p1k; int i; ppk[0] = p1k[0]; ppk[1] = p1k[1]; ppk[2] = p1k[2]; ppk[3] = p1k[3]; ppk[4] = p1k[4]; ppk[5] = p1k[4] + tsc_IV16; ppk[0] += tkipS(ppk[5] ^ get_unaligned_le16(tk + 0)); ppk[1] += tkipS(ppk[0] ^ get_unaligned_le16(tk + 2)); ppk[2] += tkipS(ppk[1] ^ get_unaligned_le16(tk + 4)); ppk[3] += tkipS(ppk[2] ^ get_unaligned_le16(tk + 6)); ppk[4] += tkipS(ppk[3] ^ get_unaligned_le16(tk + 8)); ppk[5] += tkipS(ppk[4] ^ get_unaligned_le16(tk + 10)); ppk[0] += ror16(ppk[5] ^ get_unaligned_le16(tk + 12), 1); ppk[1] += ror16(ppk[0] ^ get_unaligned_le16(tk + 14), 1); ppk[2] += ror16(ppk[1], 1); ppk[3] += ror16(ppk[2], 1); ppk[4] += ror16(ppk[3], 1); ppk[5] += ror16(ppk[4], 1); rc4key = write_tkip_iv(rc4key, tsc_IV16); *rc4key++ = ((ppk[5] ^ get_unaligned_le16(tk)) >> 1) & 0xFF; for (i = 0; i < 6; i++) put_unaligned_le16(ppk[i], rc4key + 2 * i); } /* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets * of the IV. Returns pointer to the octet following IVs (i.e., beginning of * the packet payload). */ u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key) { lockdep_assert_held(&key->u.tkip.txlock); pos = write_tkip_iv(pos, key->u.tkip.tx.iv16); *pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */; put_unaligned_le32(key->u.tkip.tx.iv32, pos); return pos + 4; } static void ieee80211_compute_tkip_p1k(struct ieee80211_key *key, u32 iv32) { struct ieee80211_sub_if_data *sdata = key->sdata; struct tkip_ctx *ctx = &key->u.tkip.tx; const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; lockdep_assert_held(&key->u.tkip.txlock); /* * Update the P1K when the IV32 is different from the value it * had when we last computed it (or when not initialised yet). * This might flip-flop back and forth if packets are processed * out-of-order due to the different ACs, but then we have to * just compute the P1K more often. */ if (ctx->p1k_iv32 != iv32 || ctx->state == TKIP_STATE_NOT_INIT) tkip_mixing_phase1(tk, ctx, sdata->vif.addr, iv32); } void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, u32 iv32, u16 *p1k) { struct ieee80211_key *key = (struct ieee80211_key *) container_of(keyconf, struct ieee80211_key, conf); struct tkip_ctx *ctx = &key->u.tkip.tx; unsigned long flags; spin_lock_irqsave(&key->u.tkip.txlock, flags); ieee80211_compute_tkip_p1k(key, iv32); memcpy(p1k, ctx->p1k, sizeof(ctx->p1k)); spin_unlock_irqrestore(&key->u.tkip.txlock, flags); } EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv); void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, const u8 *ta, u32 iv32, u16 *p1k) { const u8 *tk = &keyconf->key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; struct tkip_ctx ctx; tkip_mixing_phase1(tk, &ctx, ta, iv32); memcpy(p1k, ctx.p1k, sizeof(ctx.p1k)); } EXPORT_SYMBOL(ieee80211_get_tkip_rx_p1k); void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, struct sk_buff *skb, u8 *p2k) { struct ieee80211_key *key = (struct ieee80211_key *) container_of(keyconf, struct ieee80211_key, conf); const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; struct tkip_ctx *ctx = &key->u.tkip.tx; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); u32 iv32 = get_unaligned_le32(&data[4]); u16 iv16 = data[2] | (data[0] << 8); unsigned long flags; spin_lock_irqsave(&key->u.tkip.txlock, flags); ieee80211_compute_tkip_p1k(key, iv32); tkip_mixing_phase2(tk, ctx, iv16, p2k); spin_unlock_irqrestore(&key->u.tkip.txlock, flags); } EXPORT_SYMBOL(ieee80211_get_tkip_p2k); /* * Encrypt packet payload with TKIP using @key. @pos is a pointer to the * beginning of the buffer containing payload. This payload must include * the IV/Ext.IV and space for (taildroom) four octets for ICV. * @payload_len is the length of payload (_not_ including IV/ICV length). * @ta is the transmitter addresses. */ int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, struct sk_buff *skb, u8 *payload, size_t payload_len) { u8 rc4key[16]; ieee80211_get_tkip_p2k(&key->conf, skb, rc4key); return ieee80211_wep_encrypt_data(tfm, rc4key, 16, payload, payload_len); } /* Decrypt packet payload with TKIP using @key. @pos is a pointer to the * beginning of the buffer containing IEEE 802.11 header payload, i.e., * including IV, Ext. IV, real data, Michael MIC, ICV. @payload_len is the * length of payload, including IV, Ext. IV, MIC, ICV. */ int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, u8 *payload, size_t payload_len, u8 *ta, u8 *ra, int only_iv, int queue, u32 *out_iv32, u16 *out_iv16) { u32 iv32; u32 iv16; u8 rc4key[16], keyid, *pos = payload; int res; const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; if (payload_len < 12) return -1; iv16 = (pos[0] << 8) | pos[2]; keyid = pos[3]; iv32 = get_unaligned_le32(pos + 4); pos += 8; if (!(keyid & (1 << 5))) return TKIP_DECRYPT_NO_EXT_IV; if ((keyid >> 6) != key->conf.keyidx) return TKIP_DECRYPT_INVALID_KEYIDX; if (key->u.tkip.rx[queue].state != TKIP_STATE_NOT_INIT && (iv32 < key->u.tkip.rx[queue].iv32 || (iv32 == key->u.tkip.rx[queue].iv32 && iv16 <= key->u.tkip.rx[queue].iv16))) return TKIP_DECRYPT_REPLAY; if (only_iv) { res = TKIP_DECRYPT_OK; key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED; goto done; } if (key->u.tkip.rx[queue].state == TKIP_STATE_NOT_INIT || key->u.tkip.rx[queue].iv32 != iv32) { /* IV16 wrapped around - perform TKIP phase 1 */ tkip_mixing_phase1(tk, &key->u.tkip.rx[queue], ta, iv32); } if (key->local->ops->update_tkip_key && key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE && key->u.tkip.rx[queue].state != TKIP_STATE_PHASE1_HW_UPLOADED) { struct ieee80211_sub_if_data *sdata = key->sdata; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(key->sdata->bss, struct ieee80211_sub_if_data, u.ap); drv_update_tkip_key(key->local, sdata, &key->conf, key->sta, iv32, key->u.tkip.rx[queue].p1k); key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED; } tkip_mixing_phase2(tk, &key->u.tkip.rx[queue], iv16, rc4key); res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12); done: if (res == TKIP_DECRYPT_OK) { /* * Record previously received IV, will be copied into the * key information after MIC verification. It is possible * that we don't catch replays of fragments but that's ok * because the Michael MIC verication will then fail. */ *out_iv32 = iv32; *out_iv16 = iv16; } return res; } compat-drivers-2012-09-18/net/mac80211/offchannel.c0000644000175000017500000003142012026211315020551 0ustar mcgrofmcgrof/* * Off-channel operation helpers * * Copyright 2003, Jouni Malinen * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2009 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include "ieee80211_i.h" #include "driver-ops.h" /* * Tell our hardware to disable PS. * Optionally inform AP that we will go to sleep so that it will buffer * the frames while we are doing off-channel work. This is optional * because we *may* be doing work on-operating channel, and want our * hardware unconditionally awake, but still let the AP send us normal frames. */ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; local->offchannel_ps_enabled = false; /* FIXME: what to do when local->pspolling is true? */ del_timer_sync(&local->dynamic_ps_timer); del_timer_sync(&ifmgd->bcn_mon_timer); del_timer_sync(&ifmgd->conn_mon_timer); cancel_work_sync(&local->dynamic_ps_enable_work); if (local->hw.conf.flags & IEEE80211_CONF_PS) { local->offchannel_ps_enabled = true; local->hw.conf.flags &= ~IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } if (!local->offchannel_ps_enabled || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) /* * If power save was enabled, no need to send a nullfunc * frame because AP knows that we are sleeping. But if the * hardware is creating the nullfunc frame for power save * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not * enabled) and power save was enabled, the firmware just * sent a null frame with power save disabled. So we need * to send a new nullfunc frame to inform the AP that we * are again sleeping. */ ieee80211_send_nullfunc(local, sdata, 1); } /* inform AP that we are awake again, unless power save is enabled */ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; if (!local->ps_sdata) ieee80211_send_nullfunc(local, sdata, 0); else if (local->offchannel_ps_enabled) { /* * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware * will send a nullfunc frame with the powersave bit set * even though the AP already knows that we are sleeping. * This could be avoided by sending a null frame with power * save bit disabled before enabling the power save, but * this doesn't gain anything. * * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need * to send a nullfunc frame because AP already knows that * we are sleeping, let's just enable power save mode in * hardware. */ /* TODO: Only set hardware if CONF_PS changed? * TODO: Should we set offchannel_ps_enabled to false? */ local->hw.conf.flags |= IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } else if (local->hw.conf.dynamic_ps_timeout > 0) { /* * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer * had been running before leaving the operating channel, * restart the timer now and send a nullfunc frame to inform * the AP that we are awake. */ ieee80211_send_nullfunc(local, sdata, 0); mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); } ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata); } void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, bool offchannel_ps_enable) { struct ieee80211_sub_if_data *sdata; /* * notify the AP about us leaving the channel and stop all * STA interfaces. */ mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) continue; if (sdata->vif.type != NL80211_IFTYPE_MONITOR) set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); /* Check to see if we should disable beaconing. */ if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_ADHOC || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { netif_tx_stop_all_queues(sdata->dev); if (offchannel_ps_enable && (sdata->vif.type == NL80211_IFTYPE_STATION) && sdata->u.mgd.associated) ieee80211_offchannel_ps_enable(sdata); } } mutex_unlock(&local->iflist_mtx); } void ieee80211_offchannel_return(struct ieee80211_local *local, bool offchannel_ps_disable) { struct ieee80211_sub_if_data *sdata; mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) continue; if (sdata->vif.type != NL80211_IFTYPE_MONITOR) clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); if (!ieee80211_sdata_running(sdata)) continue; /* Tell AP we're back */ if (offchannel_ps_disable && sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.associated) ieee80211_offchannel_ps_disable(sdata); } if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { /* * This may wake up queues even though the driver * currently has them stopped. This is not very * likely, since the driver won't have gotten any * (or hardly any) new packets while we weren't * on the right channel, and even if it happens * it will at most lead to queueing up one more * packet per queue in mac80211 rather than on * the interface qdisc. */ netif_tx_wake_all_queues(sdata->dev); } if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_ADHOC || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); } mutex_unlock(&local->iflist_mtx); } void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) { if (roc->notified) return; if (roc->mgmt_tx_cookie) { if (!WARN_ON(!roc->frame)) { ieee80211_tx_skb(roc->sdata, roc->frame); roc->frame = NULL; } } else { cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc, roc->chan, roc->chan_type, roc->req_duration, GFP_KERNEL); } roc->notified = true; } static void ieee80211_hw_roc_start(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, hw_roc_start); struct ieee80211_roc_work *roc, *dep, *tmp; mutex_lock(&local->mtx); if (list_empty(&local->roc_list)) goto out_unlock; roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, list); if (!roc->started) goto out_unlock; roc->hw_begun = true; roc->hw_start_time = local->hw_roc_start_time; ieee80211_handle_roc_started(roc); list_for_each_entry_safe(dep, tmp, &roc->dependents, list) { ieee80211_handle_roc_started(dep); if (dep->duration > roc->duration) { u32 dur = dep->duration; dep->duration = dur - roc->duration; roc->duration = dur; list_del(&dep->list); list_add(&dep->list, &roc->list); } } out_unlock: mutex_unlock(&local->mtx); } void ieee80211_ready_on_channel(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); local->hw_roc_start_time = jiffies; trace_api_ready_on_channel(local); ieee80211_queue_work(hw, &local->hw_roc_start); } EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); void ieee80211_start_next_roc(struct ieee80211_local *local) { struct ieee80211_roc_work *roc; lockdep_assert_held(&local->mtx); if (list_empty(&local->roc_list)) { ieee80211_run_deferred_scan(local); return; } roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, list); if (WARN_ON_ONCE(roc->started)) return; if (local->ops->remain_on_channel) { int ret, duration = roc->duration; /* XXX: duplicated, see ieee80211_start_roc_work() */ if (!duration) duration = 10; ret = drv_remain_on_channel(local, roc->chan, roc->chan_type, duration); roc->started = true; if (ret) { wiphy_warn(local->hw.wiphy, "failed to start next HW ROC (%d)\n", ret); /* * queue the work struct again to avoid recursion * when multiple failures occur */ ieee80211_remain_on_channel_expired(&local->hw); } } else { /* delay it a bit */ ieee80211_queue_delayed_work(&local->hw, &roc->work, round_jiffies_relative(HZ/2)); } } void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) { struct ieee80211_roc_work *dep, *tmp; /* was never transmitted */ if (roc->frame) { cfg80211_mgmt_tx_status(&roc->sdata->wdev, (unsigned long)roc->frame, roc->frame->data, roc->frame->len, false, GFP_KERNEL); kfree_skb(roc->frame); } if (!roc->mgmt_tx_cookie) cfg80211_remain_on_channel_expired(&roc->sdata->wdev, (unsigned long)roc, roc->chan, roc->chan_type, GFP_KERNEL); list_for_each_entry_safe(dep, tmp, &roc->dependents, list) ieee80211_roc_notify_destroy(dep); kfree(roc); } void ieee80211_sw_roc_work(struct work_struct *work) { struct ieee80211_roc_work *roc = container_of(work, struct ieee80211_roc_work, work.work); struct ieee80211_sub_if_data *sdata = roc->sdata; struct ieee80211_local *local = sdata->local; bool started; mutex_lock(&local->mtx); if (roc->abort) goto finish; if (WARN_ON(list_empty(&local->roc_list))) goto out_unlock; if (WARN_ON(roc != list_first_entry(&local->roc_list, struct ieee80211_roc_work, list))) goto out_unlock; if (!roc->started) { struct ieee80211_roc_work *dep; /* start this ROC */ /* switch channel etc */ ieee80211_recalc_idle(local); local->tmp_channel = roc->chan; local->tmp_channel_type = roc->chan_type; ieee80211_hw_config(local, 0); /* tell userspace or send frame */ ieee80211_handle_roc_started(roc); list_for_each_entry(dep, &roc->dependents, list) ieee80211_handle_roc_started(dep); /* if it was pure TX, just finish right away */ if (!roc->duration) goto finish; roc->started = true; ieee80211_queue_delayed_work(&local->hw, &roc->work, msecs_to_jiffies(roc->duration)); } else { /* finish this ROC */ finish: list_del(&roc->list); started = roc->started; ieee80211_roc_notify_destroy(roc); if (started) { drv_flush(local, false); local->tmp_channel = NULL; ieee80211_hw_config(local, 0); ieee80211_offchannel_return(local, true); } ieee80211_recalc_idle(local); if (started) ieee80211_start_next_roc(local); } out_unlock: mutex_unlock(&local->mtx); } static void ieee80211_hw_roc_done(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, hw_roc_done); struct ieee80211_roc_work *roc; mutex_lock(&local->mtx); if (list_empty(&local->roc_list)) goto out_unlock; roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, list); if (!roc->started) goto out_unlock; list_del(&roc->list); ieee80211_roc_notify_destroy(roc); /* if there's another roc, start it now */ ieee80211_start_next_roc(local); out_unlock: mutex_unlock(&local->mtx); } void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); trace_api_remain_on_channel_expired(local); ieee80211_queue_work(hw, &local->hw_roc_done); } EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); void ieee80211_roc_setup(struct ieee80211_local *local) { INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start); INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done); INIT_LIST_HEAD(&local->roc_list); } void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_roc_work *roc, *tmp; LIST_HEAD(tmp_list); mutex_lock(&local->mtx); list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { if (roc->sdata != sdata) continue; if (roc->started && local->ops->remain_on_channel) { /* can race, so ignore return value */ drv_cancel_remain_on_channel(local); } list_move_tail(&roc->list, &tmp_list); roc->abort = true; } ieee80211_start_next_roc(local); mutex_unlock(&local->mtx); list_for_each_entry_safe(roc, tmp, &tmp_list, list) { if (local->ops->remain_on_channel) { list_del(&roc->list); ieee80211_roc_notify_destroy(roc); } else { ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); /* work will clean up etc */ flush_delayed_work(&roc->work); } } WARN_ON_ONCE(!list_empty(&tmp_list)); } compat-drivers-2012-09-18/net/mac80211/aes_ccm.h0000644000175000017500000000142012026211315020042 0ustar mcgrofmcgrof/* * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2006, Devicescape Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef AES_CCM_H #define AES_CCM_H #include struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *data, size_t data_len, u8 *cdata, u8 *mic); int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *cdata, size_t data_len, u8 *mic, u8 *data); void ieee80211_aes_key_free(struct crypto_cipher *tfm); #endif /* AES_CCM_H */ compat-drivers-2012-09-18/net/mac80211/agg-rx.c0000644000175000017500000002564712026211315017651 0ustar mcgrofmcgrof/* * HT handling * * Copyright 2003, Jouni Malinen * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /** * DOC: RX A-MPDU aggregation * * Aggregation on the RX side requires only implementing the * @ampdu_action callback that is invoked to start/stop any * block-ack sessions for RX aggregation. * * When RX aggregation is started by the peer, the driver is * notified via @ampdu_action function, with the * %IEEE80211_AMPDU_RX_START action, and may reject the request * in which case a negative response is sent to the peer, if it * accepts it a positive response is sent. * * While the session is active, the device/driver are required * to de-aggregate frames and pass them up one by one to mac80211, * which will handle the reorder buffer. * * When the aggregation session is stopped again by the peer or * ourselves, the driver's @ampdu_action function will be called * with the action %IEEE80211_AMPDU_RX_STOP. In this case, the * call must not fail. */ #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" static void ieee80211_free_tid_rx(struct rcu_head *h) { struct tid_ampdu_rx *tid_rx = container_of(h, struct tid_ampdu_rx, rcu_head); int i; del_timer_sync(&tid_rx->reorder_timer); for (i = 0; i < tid_rx->buf_size; i++) dev_kfree_skb(tid_rx->reorder_buf[i]); kfree(tid_rx->reorder_buf); kfree(tid_rx->reorder_time); kfree(tid_rx); } void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool tx) { struct ieee80211_local *local = sta->local; struct tid_ampdu_rx *tid_rx; lockdep_assert_held(&sta->ampdu_mlme.mtx); tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid], lockdep_is_held(&sta->ampdu_mlme.mtx)); if (!tid_rx) return; RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL); ht_dbg(sta->sdata, "Rx BA session stop requested for %pM tid %u %s reason: %d\n", sta->sta.addr, tid, initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", (int)reason); if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, &sta->sta, tid, NULL, 0)) sdata_info(sta->sdata, "HW problem - can not stop rx aggregation for tid %d\n", tid); /* check if this is a self generated aggregation halt */ if (initiator == WLAN_BACK_RECIPIENT && tx) ieee80211_send_delba(sta->sdata, sta->sta.addr, tid, WLAN_BACK_RECIPIENT, reason); del_timer_sync(&tid_rx->session_timer); call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx); } void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool tx) { mutex_lock(&sta->ampdu_mlme.mtx); ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, tx); mutex_unlock(&sta->ampdu_mlme.mtx); } void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, const u8 *addr) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct sta_info *sta; int i; rcu_read_lock(); sta = sta_info_get_bss(sdata, addr); if (!sta) { rcu_read_unlock(); return; } for (i = 0; i < STA_TID_NUM; i++) if (ba_rx_bitmap & BIT(i)) set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_stop_rx_ba_session); /* * After accepting the AddBA Request we activated a timer, * resetting it after each frame that arrives from the originator. */ static void sta_rx_agg_session_timer_expired(unsigned long data) { /* not an elegant detour, but there is no choice as the timer passes * only one argument, and various sta_info are needed here, so init * flow in sta_info_create gives the TID as data, while the timer_to_id * array gives the sta through container_of */ u8 *ptid = (u8 *)data; u8 *timer_to_id = ptid - *ptid; struct sta_info *sta = container_of(timer_to_id, struct sta_info, timer_to_tid[0]); struct tid_ampdu_rx *tid_rx; unsigned long timeout; rcu_read_lock(); tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[*ptid]); if (!tid_rx) { rcu_read_unlock(); return; } timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout); if (time_is_after_jiffies(timeout)) { mod_timer(&tid_rx->session_timer, timeout); rcu_read_unlock(); return; } rcu_read_unlock(); ht_dbg(sta->sdata, "rx session timer expired on tid %d\n", (u16)*ptid); set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired); ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); } static void sta_rx_agg_reorder_timer_expired(unsigned long data) { u8 *ptid = (u8 *)data; u8 *timer_to_id = ptid - *ptid; struct sta_info *sta = container_of(timer_to_id, struct sta_info, timer_to_tid[0]); rcu_read_lock(); ieee80211_release_reorder_timeout(sta, *ptid); rcu_read_unlock(); } static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid, u8 dialog_token, u16 status, u16 policy, u16 buf_size, u16 timeout) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u16 capab; skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp)); mgmt->u.action.category = WLAN_CATEGORY_BACK; mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; mgmt->u.action.u.addba_resp.dialog_token = dialog_token; capab = (u16)(policy << 1); /* bit 1 aggregation policy */ capab |= (u16)(tid << 2); /* bit 5:2 TID number */ capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */ mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab); mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout); mgmt->u.action.u.addba_resp.status = cpu_to_le16(status); ieee80211_tx_skb(sdata, skb); } void ieee80211_process_addba_request(struct ieee80211_local *local, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len) { struct tid_ampdu_rx *tid_agg_rx; u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status; u8 dialog_token; int ret = -EOPNOTSUPP; /* extract session parameters from addba request frame */ dialog_token = mgmt->u.action.u.addba_req.dialog_token; timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); start_seq_num = le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4; capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; status = WLAN_STATUS_REQUEST_DECLINED; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request\n"); goto end_no_lock; } /* sanity check for incoming parameters: * check if configuration can support the BA policy * and if buffer size does not exceeds max value */ /* XXX: check own ht delayed BA capability?? */ if (((ba_policy != 1) && (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) || (buf_size > IEEE80211_MAX_AMPDU_BUF)) { status = WLAN_STATUS_INVALID_QOS_PARAM; ht_dbg_ratelimited(sta->sdata, "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", mgmt->sa, tid, ba_policy, buf_size); goto end_no_lock; } /* determine default buffer size */ if (buf_size == 0) buf_size = IEEE80211_MAX_AMPDU_BUF; /* make sure the size doesn't exceed the maximum supported by the hw */ if (buf_size > local->hw.max_rx_aggregation_subframes) buf_size = local->hw.max_rx_aggregation_subframes; /* examine state machine */ mutex_lock(&sta->ampdu_mlme.mtx); if (sta->ampdu_mlme.tid_rx[tid]) { ht_dbg_ratelimited(sta->sdata, "unexpected AddBA Req from %pM on tid %u\n", mgmt->sa, tid); /* delete existing Rx BA session on the same tid */ ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, WLAN_STATUS_UNSPECIFIED_QOS, false); } /* prepare A-MPDU MLME for Rx aggregation */ tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_KERNEL); if (!tid_agg_rx) goto end; spin_lock_init(&tid_agg_rx->reorder_lock); /* rx timer */ tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired; tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; init_timer_deferrable(&tid_agg_rx->session_timer); /* rx reorder timer */ tid_agg_rx->reorder_timer.function = sta_rx_agg_reorder_timer_expired; tid_agg_rx->reorder_timer.data = (unsigned long)&sta->timer_to_tid[tid]; init_timer(&tid_agg_rx->reorder_timer); /* prepare reordering buffer */ tid_agg_rx->reorder_buf = kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL); tid_agg_rx->reorder_time = kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL); if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { kfree(tid_agg_rx->reorder_buf); kfree(tid_agg_rx->reorder_time); kfree(tid_agg_rx); goto end; } ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, &sta->sta, tid, &start_seq_num, 0); ht_dbg(sta->sdata, "Rx A-MPDU request on tid %d result %d\n", tid, ret); if (ret) { kfree(tid_agg_rx->reorder_buf); kfree(tid_agg_rx->reorder_time); kfree(tid_agg_rx); goto end; } /* update data */ tid_agg_rx->dialog_token = dialog_token; tid_agg_rx->ssn = start_seq_num; tid_agg_rx->head_seq_num = start_seq_num; tid_agg_rx->buf_size = buf_size; tid_agg_rx->timeout = timeout; tid_agg_rx->stored_mpdu_num = 0; status = WLAN_STATUS_SUCCESS; /* activate it for RX */ rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx); if (timeout) { mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout)); tid_agg_rx->last_rx = jiffies; } end: mutex_unlock(&sta->ampdu_mlme.mtx); end_no_lock: ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, dialog_token, status, 1, buf_size, timeout); } compat-drivers-2012-09-18/net/mac80211/mlme.c0000644000175000017500000030761312026211315017412 0ustar mcgrofmcgrof/* * BSS client mode implementation * Copyright 2003-2008, Jouni Malinen * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "rate.h" #include "led.h" #define IEEE80211_AUTH_TIMEOUT (HZ / 5) #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 static int max_nullfunc_tries = 2; module_param(max_nullfunc_tries, int, 0644); MODULE_PARM_DESC(max_nullfunc_tries, "Maximum nullfunc tx tries before disconnecting (reason 4)."); static int max_probe_tries = 5; module_param(max_probe_tries, int, 0644); MODULE_PARM_DESC(max_probe_tries, "Maximum probe tries before disconnecting (reason 4)."); /* * Beacon loss timeout is calculated as N frames times the * advertised beacon interval. This may need to be somewhat * higher than what hardware might detect to account for * delays in the host processing frames. But since we also * probe on beacon miss before declaring the connection lost * default to what we want. */ #define IEEE80211_BEACON_LOSS_COUNT 7 /* * Time the connection can be idle before we probe * it to see if we can still talk to the AP. */ #define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ) /* * Time we wait for a probe response after sending * a probe request because of beacon loss or for * checking the connection still works. */ static int probe_wait_ms = 500; module_param(probe_wait_ms, int, 0644); MODULE_PARM_DESC(probe_wait_ms, "Maximum time(ms) to wait for probe response" " before disconnecting (reason 4)."); /* * Weight given to the latest Beacon frame when calculating average signal * strength for Beacon frames received in the current BSS. This must be * between 1 and 15. */ #define IEEE80211_SIGNAL_AVE_WEIGHT 3 /* * How many Beacon frames need to have been used in average signal strength * before starting to indicate signal change events. */ #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4 #define TMR_RUNNING_TIMER 0 #define TMR_RUNNING_CHANSW 1 #define DEAUTH_DISASSOC_LEN (24 /* hdr */ + 2 /* reason */) /* * All cfg80211 functions have to be called outside a locked * section so that they can acquire a lock themselves... This * is much simpler than queuing up things in cfg80211, but we * do need some indirection for that here. */ enum rx_mgmt_action { /* no action required */ RX_MGMT_NONE, /* caller must call cfg80211_send_deauth() */ RX_MGMT_CFG80211_DEAUTH, /* caller must call cfg80211_send_disassoc() */ RX_MGMT_CFG80211_DISASSOC, /* caller must call cfg80211_send_rx_auth() */ RX_MGMT_CFG80211_RX_AUTH, /* caller must call cfg80211_send_rx_assoc() */ RX_MGMT_CFG80211_RX_ASSOC, /* caller must call cfg80211_send_assoc_timeout() */ RX_MGMT_CFG80211_ASSOC_TIMEOUT, }; /* utils */ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) { lockdep_assert_held(&ifmgd->mtx); } /* * We can have multiple work items (and connection probing) * scheduling this timer, but we need to take care to only * reschedule it when it should fire _earlier_ than it was * asked for before, or if it's not pending right now. This * function ensures that. Note that it then is required to * run this function for all timeouts after the first one * has happened -- the work that runs from this timer will * do that. */ static void run_again(struct ieee80211_if_managed *ifmgd, unsigned long timeout) { ASSERT_MGD_MTX(ifmgd); if (!timer_pending(&ifmgd->timer) || time_before(timeout, ifmgd->timer.expires)) mod_timer(&ifmgd->timer, timeout); } void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) { if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) return; if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) return; mod_timer(&sdata->u.mgd.bcn_mon_timer, round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout)); } void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (unlikely(!sdata->u.mgd.associated)) return; if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) return; mod_timer(&sdata->u.mgd.conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); ifmgd->probe_send_count = 0; } static int ecw2cw(int ecw) { return (1 << ecw) - 1; } static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, struct ieee80211_ht_operation *ht_oper, const u8 *bssid, bool reconfig) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; struct sta_info *sta; u32 changed = 0; u16 ht_opmode; bool disable_40 = false; sband = local->hw.wiphy->bands[local->oper_channel->band]; switch (sdata->vif.bss_conf.channel_type) { case NL80211_CHAN_HT40PLUS: if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS) disable_40 = true; break; case NL80211_CHAN_HT40MINUS: if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS) disable_40 = true; break; default: break; } /* This can change during the lifetime of the BSS */ if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) disable_40 = true; mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, bssid); WARN_ON_ONCE(!sta); if (sta && !sta->supports_40mhz) disable_40 = true; if (sta && (!reconfig || (disable_40 != !(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) { if (disable_40) sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; else sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; rate_control_rate_update(local, sband, sta, IEEE80211_RC_BW_CHANGED); } mutex_unlock(&local->sta_mtx); ht_opmode = le16_to_cpu(ht_oper->operation_mode); /* if bss configuration changed store the new one */ if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) { changed |= BSS_CHANGED_HT; sdata->vif.bss_conf.ht_operation_mode = ht_opmode; } return changed; } /* frame sending functions */ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, struct ieee80211_supported_band *sband, u32 *rates) { int i, j, count; *rates = 0; count = 0; for (i = 0; i < supp_rates_len; i++) { int rate = (supp_rates[i] & 0x7F) * 5; for (j = 0; j < sband->n_bitrates; j++) if (sband->bitrates[j].bitrate == rate) { *rates |= BIT(j); count++; break; } } return count; } static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u8 ap_ht_param, struct ieee80211_supported_band *sband, struct ieee80211_channel *channel, enum ieee80211_smps_mode smps) { u8 *pos; u32 flags = channel->flags; u16 cap; struct ieee80211_sta_ht_cap ht_cap; BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap)); memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); ieee80211_apply_htcap_overrides(sdata, &ht_cap); /* determine capability flags */ cap = ht_cap.cap; switch (ap_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: if (flags & IEEE80211_CHAN_NO_HT40PLUS) { cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; cap &= ~IEEE80211_HT_CAP_SGI_40; } break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: if (flags & IEEE80211_CHAN_NO_HT40MINUS) { cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; cap &= ~IEEE80211_HT_CAP_SGI_40; } break; } /* * If 40 MHz was disabled associate as though we weren't * capable of 40 MHz -- some broken APs will never fall * back to trying to transmit in 20 MHz. */ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) { cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; cap &= ~IEEE80211_HT_CAP_SGI_40; } /* set SM PS mode properly */ cap &= ~IEEE80211_HT_CAP_SM_PS; switch (smps) { case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_NUM_MODES: WARN_ON(1); case IEEE80211_SMPS_OFF: cap |= WLAN_HT_CAP_SM_PS_DISABLED << IEEE80211_HT_CAP_SM_PS_SHIFT; break; case IEEE80211_SMPS_STATIC: cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; break; case IEEE80211_SMPS_DYNAMIC: cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << IEEE80211_HT_CAP_SM_PS_SHIFT; break; } /* reserve and fill IE */ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); ieee80211_ie_build_ht_cap(pos, &ht_cap, cap); } static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_supported_band *sband) { u8 *pos; u32 cap; struct ieee80211_sta_vht_cap vht_cap; BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); /* determine capability flags */ cap = vht_cap.cap; /* reserve and fill IE */ pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); } static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos, qos_info; size_t offset = 0, noffset; int i, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_supported_band *sband; u32 rates = 0; lockdep_assert_held(&ifmgd->mtx); sband = local->hw.wiphy->bands[local->oper_channel->band]; if (assoc_data->supp_rates_len) { /* * Get all rates supported by the device and the AP as * some APs don't like getting a superset of their rates * in the association request (e.g. D-Link DAP 1353 in * b-only mode)... */ rates_len = ieee80211_compatible_rates(assoc_data->supp_rates, assoc_data->supp_rates_len, sband, &rates); } else { /* * In case AP not provide any supported rates information * before association, we send information element(s) with * all rates that we support. */ rates = ~0; rates_len = sband->n_bitrates; } skb = alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + /* bit too much but doesn't matter */ 2 + assoc_data->ssid_len + /* SSID */ 4 + rates_len + /* (extended) rates */ 4 + /* power capability */ 2 + 2 * sband->n_channels + /* supported channels */ 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ 2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */ assoc_data->ie_len + /* extra IEs */ 9, /* WMM */ GFP_KERNEL); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); capab = WLAN_CAPABILITY_ESS; if (sband->band == IEEE80211_BAND_2GHZ) { if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; } if (assoc_data->capability & WLAN_CAPABILITY_PRIVACY) capab |= WLAN_CAPABILITY_PRIVACY; if ((assoc_data->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, assoc_data->bss->bssid, ETH_ALEN); if (!is_zero_ether_addr(assoc_data->prev_bssid)) { skb_put(skb, 10); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); mgmt->u.reassoc_req.listen_interval = cpu_to_le16(local->hw.conf.listen_interval); memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid, ETH_ALEN); } else { skb_put(skb, 4); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); mgmt->u.assoc_req.listen_interval = cpu_to_le16(local->hw.conf.listen_interval); } /* SSID */ pos = skb_put(skb, 2 + assoc_data->ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = assoc_data->ssid_len; memcpy(pos, assoc_data->ssid, assoc_data->ssid_len); /* add all rates which were marked to be used above */ supp_rates_len = rates_len; if (supp_rates_len > 8) supp_rates_len = 8; pos = skb_put(skb, supp_rates_len + 2); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = supp_rates_len; count = 0; for (i = 0; i < sband->n_bitrates; i++) { if (BIT(i) & rates) { int rate = sband->bitrates[i].bitrate; *pos++ = (u8) (rate / 5); if (++count == 8) break; } } if (rates_len > count) { pos = skb_put(skb, rates_len - count + 2); *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = rates_len - count; for (i++; i < sband->n_bitrates; i++) { if (BIT(i) & rates) { int rate = sband->bitrates[i].bitrate; *pos++ = (u8) (rate / 5); } } } if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { /* 1. power capabilities */ pos = skb_put(skb, 4); *pos++ = WLAN_EID_PWR_CAPABILITY; *pos++ = 2; *pos++ = 0; /* min tx power */ *pos++ = local->oper_channel->max_power; /* max tx power */ /* 2. supported channels */ /* TODO: get this in reg domain format */ pos = skb_put(skb, 2 * sband->n_channels + 2); *pos++ = WLAN_EID_SUPPORTED_CHANNELS; *pos++ = 2 * sband->n_channels; for (i = 0; i < sband->n_channels; i++) { *pos++ = ieee80211_frequency_to_channel( sband->channels[i].center_freq); *pos++ = 1; /* one channel in the subband*/ } } /* if present, add any custom IEs that go before HT */ if (assoc_data->ie_len && assoc_data->ie) { static const u8 before_ht[] = { WLAN_EID_SSID, WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES, WLAN_EID_PWR_CAPABILITY, WLAN_EID_SUPPORTED_CHANNELS, WLAN_EID_RSN, WLAN_EID_QOS_CAPA, WLAN_EID_RRM_ENABLED_CAPABILITIES, WLAN_EID_MOBILITY_DOMAIN, WLAN_EID_SUPPORTED_REGULATORY_CLASSES, }; noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, before_ht, ARRAY_SIZE(before_ht), offset); pos = skb_put(skb, noffset - offset); memcpy(pos, assoc_data->ie + offset, noffset - offset); offset = noffset; } if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, sband, local->oper_channel, ifmgd->ap_smps); if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) ieee80211_add_vht_ie(sdata, skb, sband); /* if present, add any custom non-vendor IEs that go after HT */ if (assoc_data->ie_len && assoc_data->ie) { noffset = ieee80211_ie_split_vendor(assoc_data->ie, assoc_data->ie_len, offset); pos = skb_put(skb, noffset - offset); memcpy(pos, assoc_data->ie + offset, noffset - offset); offset = noffset; } if (assoc_data->wmm) { if (assoc_data->uapsd) { qos_info = ifmgd->uapsd_queues; qos_info |= (ifmgd->uapsd_max_sp_len << IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); } else { qos_info = 0; } pos = skb_put(skb, 9); *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 7; /* len */ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ *pos++ = 0x50; *pos++ = 0xf2; *pos++ = 2; /* WME */ *pos++ = 0; /* WME info */ *pos++ = 1; /* WME ver */ *pos++ = qos_info; } /* add any remaining custom (i.e. vendor specific here) IEs */ if (assoc_data->ie_len && assoc_data->ie) { noffset = assoc_data->ie_len; pos = skb_put(skb, noffset - offset); memcpy(pos, assoc_data->ie + offset, noffset - offset); } drv_mgd_prepare_tx(local, sdata); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *bssid, u16 stype, u16 reason, bool send_frame, u8 *frame_buf) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct sk_buff *skb; struct ieee80211_mgmt *mgmt = (void *)frame_buf; /* build frame */ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); mgmt->duration = 0; /* initialize only */ mgmt->seq_ctrl = 0; /* initialize only */ memcpy(mgmt->da, bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, bssid, ETH_ALEN); /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); if (send_frame) { skb = dev_alloc_skb(local->hw.extra_tx_headroom + DEAUTH_DISASSOC_LEN); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); /* copy in frame */ memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN), mgmt, DEAUTH_DISASSOC_LEN); if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } } void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { struct ieee80211_pspoll *pspoll; struct sk_buff *skb; skb = ieee80211_pspoll_get(&local->hw, &sdata->vif); if (!skb) return; pspoll = (struct ieee80211_pspoll *) skb->data; pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, int powersave) { struct sk_buff *skb; struct ieee80211_hdr_3addr *nullfunc; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif); if (!skb) return; nullfunc = (struct ieee80211_hdr_3addr *) skb->data; if (powersave) nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL)) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; ieee80211_tx_skb(sdata, skb); } static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { struct sk_buff *skb; struct ieee80211_hdr *nullfunc; __le16 fc; if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return; skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30); memset(nullfunc, 0, 30); fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); nullfunc->frame_control = fc; memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN); memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } /* spectrum management related things */ static void ieee80211_chswitch_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (!ieee80211_sdata_running(sdata)) return; mutex_lock(&ifmgd->mtx); if (!ifmgd->associated) goto out; sdata->local->oper_channel = sdata->local->csa_channel; if (!sdata->local->ops->channel_switch) { /* call "hw_config" only if doing sw channel switch */ ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); } else { /* update the device channel directly */ sdata->local->hw.conf.channel = sdata->local->oper_channel; } /* XXX: shouldn't really modify cfg80211-owned data! */ ifmgd->associated->channel = sdata->local->oper_channel; /* XXX: wait for a beacon first? */ ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); out: ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; mutex_unlock(&ifmgd->mtx); } void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) { struct ieee80211_sub_if_data *sdata; struct ieee80211_if_managed *ifmgd; sdata = vif_to_sdata(vif); ifmgd = &sdata->u.mgd; trace_api_chswitch_done(sdata, success); if (!success) { /* * If the channel switch was not successful, stay * around on the old channel. We currently lack * good handling of this situation, possibly we * should just drop the association. */ sdata->local->csa_channel = sdata->local->oper_channel; } ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); } EXPORT_SYMBOL(ieee80211_chswitch_done); static void ieee80211_chswitch_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (sdata->local->quiescing) { set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); return; } ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); } void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel_sw_ie *sw_elem, struct ieee80211_bss *bss, u64 timestamp) { struct cfg80211_bss *cbss = container_of((void *)bss, struct cfg80211_bss, priv); struct ieee80211_channel *new_ch; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, cbss->channel->band); ASSERT_MGD_MTX(ifmgd); if (!ifmgd->associated) return; if (sdata->local->scanning) return; /* Disregard subsequent beacons if we are already running a timer processing a CSA */ if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) return; new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) return; sdata->local->csa_channel = new_ch; ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; if (sw_elem->mode) ieee80211_stop_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); if (sdata->local->ops->channel_switch) { /* use driver's channel switch callback */ struct ieee80211_channel_switch ch_switch = { .timestamp = timestamp, .block_tx = sw_elem->mode, .channel = new_ch, .count = sw_elem->count, }; drv_channel_switch(sdata->local, &ch_switch); return; } /* channel switch handled in software */ if (sw_elem->count <= 1) ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); else mod_timer(&ifmgd->chswitch_timer, TU_TO_EXP_TIME(sw_elem->count * cbss->beacon_interval)); } static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, u16 capab_info, u8 *pwr_constr_elem, u8 pwr_constr_elem_len) { struct ieee80211_conf *conf = &sdata->local->hw.conf; if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT)) return; /* Power constraint IE length should be 1 octet */ if (pwr_constr_elem_len != 1) return; if ((*pwr_constr_elem <= conf->channel->max_reg_power) && (*pwr_constr_elem != sdata->local->power_constr_level)) { sdata->local->power_constr_level = *pwr_constr_elem; ieee80211_hw_config(sdata->local, 0); } } void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct ieee80211_conf *conf = &local->hw.conf; WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); local->disable_dynamic_ps = false; conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout; } EXPORT_SYMBOL(ieee80211_enable_dyn_ps); void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct ieee80211_conf *conf = &local->hw.conf; WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); local->disable_dynamic_ps = true; conf->dynamic_ps_timeout = 0; del_timer_sync(&local->dynamic_ps_timer); ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); } EXPORT_SYMBOL(ieee80211_disable_dyn_ps); /* powersave */ static void ieee80211_enable_ps(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { struct ieee80211_conf *conf = &local->hw.conf; /* * If we are scanning right now then the parameters will * take effect when scan finishes. */ if (local->scanning) return; if (conf->dynamic_ps_timeout > 0 && !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) { mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(conf->dynamic_ps_timeout)); } else { if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) ieee80211_send_nullfunc(local, sdata, 1); if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) return; conf->flags |= IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } } static void ieee80211_change_ps(struct ieee80211_local *local) { struct ieee80211_conf *conf = &local->hw.conf; if (local->ps_sdata) { ieee80211_enable_ps(local, local->ps_sdata); } else if (conf->flags & IEEE80211_CONF_PS) { conf->flags &= ~IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); } } static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *mgd = &sdata->u.mgd; struct sta_info *sta = NULL; bool authorized = false; if (!mgd->powersave) return false; if (mgd->broken_ap) return false; if (!mgd->associated) return false; if (mgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL)) return false; rcu_read_lock(); sta = sta_info_get(sdata, mgd->bssid); if (sta) authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); rcu_read_unlock(); return authorized; } /* need to hold RTNL or interface lock */ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) { struct ieee80211_sub_if_data *sdata, *found = NULL; int count = 0; int timeout; if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) { local->ps_sdata = NULL; return; } list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; if (sdata->vif.type == NL80211_IFTYPE_AP) { /* If an AP vif is found, then disable PS * by setting the count to zero thereby setting * ps_sdata to NULL. */ count = 0; break; } if (sdata->vif.type != NL80211_IFTYPE_STATION) continue; found = sdata; count++; } if (count == 1 && ieee80211_powersave_allowed(found)) { struct ieee80211_conf *conf = &local->hw.conf; s32 beaconint_us; if (latency < 0) latency = pm_qos_request(PM_QOS_NETWORK_LATENCY); beaconint_us = ieee80211_tu_to_usec( found->vif.bss_conf.beacon_int); timeout = local->dynamic_ps_forced_timeout; if (timeout < 0) { /* * Go to full PSM if the user configures a very low * latency requirement. * The 2000 second value is there for compatibility * until the PM_QOS_NETWORK_LATENCY is configured * with real values. */ if (latency > (1900 * USEC_PER_MSEC) && latency != (2000 * USEC_PER_SEC)) timeout = 0; else timeout = 100; } local->dynamic_ps_user_timeout = timeout; if (!local->disable_dynamic_ps) conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout; if (beaconint_us > latency) { local->ps_sdata = NULL; } else { struct ieee80211_bss *bss; int maxslp = 1; u8 dtimper; bss = (void *)found->u.mgd.associated->priv; dtimper = bss->dtim_period; /* If the TIM IE is invalid, pretend the value is 1 */ if (!dtimper) dtimper = 1; else if (dtimper > 1) maxslp = min_t(int, dtimper, latency / beaconint_us); local->hw.conf.max_sleep_period = maxslp; local->hw.conf.ps_dtim_period = dtimper; local->ps_sdata = found; } } else { local->ps_sdata = NULL; } ieee80211_change_ps(local); } void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata) { bool ps_allowed = ieee80211_powersave_allowed(sdata); if (sdata->vif.bss_conf.ps != ps_allowed) { sdata->vif.bss_conf.ps = ps_allowed; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_PS); } } void ieee80211_dynamic_ps_disable_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, dynamic_ps_disable_work); if (local->hw.conf.flags & IEEE80211_CONF_PS) { local->hw.conf.flags &= ~IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_PS); } void ieee80211_dynamic_ps_enable_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, dynamic_ps_enable_work); struct ieee80211_sub_if_data *sdata = local->ps_sdata; struct ieee80211_if_managed *ifmgd; unsigned long flags; int q; /* can only happen when PS was just disabled anyway */ if (!sdata) return; ifmgd = &sdata->u.mgd; if (local->hw.conf.flags & IEEE80211_CONF_PS) return; if (!local->disable_dynamic_ps && local->hw.conf.dynamic_ps_timeout > 0) { /* don't enter PS if TX frames are pending */ if (drv_tx_frames_pending(local)) { mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies( local->hw.conf.dynamic_ps_timeout)); return; } /* * transmission can be stopped by others which leads to * dynamic_ps_timer expiry. Postpone the ps timer if it * is not the actual idle state. */ spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (q = 0; q < local->hw.queues; q++) { if (local->queue_stop_reasons[q]) { spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies( local->hw.conf.dynamic_ps_timeout)); return; } } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { netif_tx_stop_all_queues(sdata->dev); if (drv_tx_frames_pending(local)) mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies( local->hw.conf.dynamic_ps_timeout)); else { ieee80211_send_nullfunc(local, sdata, 1); /* Flush to get the tx status of nullfunc frame */ drv_flush(local, false); } } if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) || (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; local->hw.conf.flags |= IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) netif_tx_wake_all_queues(sdata->dev); } void ieee80211_dynamic_ps_timer(unsigned long data) { struct ieee80211_local *local = (void *) data; if (local->quiescing || local->suspended) return; ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); } /* MLME */ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u8 *wmm_param, size_t wmm_param_len) { struct ieee80211_tx_queue_params params; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t left; int count; u8 *pos, uapsd_queues = 0; if (!local->ops->conf_tx) return false; if (local->hw.queues < IEEE80211_NUM_ACS) return false; if (!wmm_param) return false; if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) return false; if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) uapsd_queues = ifmgd->uapsd_queues; count = wmm_param[6] & 0x0f; if (count == ifmgd->wmm_last_param_set) return false; ifmgd->wmm_last_param_set = count; pos = wmm_param + 8; left = wmm_param_len - 8; memset(¶ms, 0, sizeof(params)); sdata->wmm_acm = 0; for (; left >= 4; left -= 4, pos += 4) { int aci = (pos[0] >> 5) & 0x03; int acm = (pos[0] >> 4) & 0x01; bool uapsd = false; int queue; switch (aci) { case 1: /* AC_BK */ queue = 3; if (acm) sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) uapsd = true; break; case 2: /* AC_VI */ queue = 1; if (acm) sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) uapsd = true; break; case 3: /* AC_VO */ queue = 0; if (acm) sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) uapsd = true; break; case 0: /* AC_BE */ default: queue = 2; if (acm) sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) uapsd = true; break; } params.aifs = pos[0] & 0x0f; params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); params.cw_min = ecw2cw(pos[1] & 0x0f); params.txop = get_unaligned_le16(pos + 2); params.uapsd = uapsd; mlme_dbg(sdata, "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n", queue, aci, acm, params.aifs, params.cw_min, params.cw_max, params.txop, params.uapsd); sdata->tx_conf[queue] = params; if (drv_conf_tx(local, sdata, queue, ¶ms)) sdata_err(sdata, "failed to set TX queue parameters for queue %d\n", queue); } /* enable WMM or activate new settings */ sdata->vif.bss_conf.qos = true; return true; } static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) { lockdep_assert_held(&sdata->local->mtx); sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | IEEE80211_STA_BEACON_POLL); ieee80211_run_deferred_scan(sdata->local); } static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) { mutex_lock(&sdata->local->mtx); __ieee80211_stop_poll(sdata); mutex_unlock(&sdata->local->mtx); } static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, u16 capab, bool erp_valid, u8 erp) { struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; u32 changed = 0; bool use_protection; bool use_short_preamble; bool use_short_slot; if (erp_valid) { use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0; use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0; } else { use_protection = false; use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE); } use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) use_short_slot = true; if (use_protection != bss_conf->use_cts_prot) { bss_conf->use_cts_prot = use_protection; changed |= BSS_CHANGED_ERP_CTS_PROT; } if (use_short_preamble != bss_conf->use_short_preamble) { bss_conf->use_short_preamble = use_short_preamble; changed |= BSS_CHANGED_ERP_PREAMBLE; } if (use_short_slot != bss_conf->use_short_slot) { bss_conf->use_short_slot = use_short_slot; changed |= BSS_CHANGED_ERP_SLOT; } return changed; } static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss, u32 bss_info_changed) { struct ieee80211_bss *bss = (void *)cbss->priv; struct ieee80211_local *local = sdata->local; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; bss_info_changed |= BSS_CHANGED_ASSOC; bss_info_changed |= ieee80211_handle_bss_capability(sdata, bss_conf->assoc_capability, bss->has_erp_value, bss->erp_value); sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec( IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int)); sdata->u.mgd.associated = cbss; memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; /* just to be sure */ ieee80211_stop_poll(sdata); ieee80211_led_assoc(local, 1); if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) bss_conf->dtim_period = bss->dtim_period; else bss_conf->dtim_period = 0; bss_conf->assoc = 1; /* Tell the driver to monitor connection quality (if supported) */ if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI && bss_conf->cqm_rssi_thold) bss_info_changed |= BSS_CHANGED_CQM; /* Enable ARP filtering */ if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) { bss_conf->arp_filter_enabled = sdata->arp_filter_state; bss_info_changed |= BSS_CHANGED_ARP_FILTER; } ieee80211_bss_info_change_notify(sdata, bss_info_changed); mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, -1); ieee80211_recalc_smps(local); mutex_unlock(&local->iflist_mtx); ieee80211_recalc_ps_vif(sdata); netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); } static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, u16 stype, u16 reason, bool tx, u8 *frame_buf) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sta_info *sta; u32 changed = 0; ASSERT_MGD_MTX(ifmgd); if (WARN_ON_ONCE(tx && !frame_buf)) return; if (WARN_ON(!ifmgd->associated)) return; ieee80211_stop_poll(sdata); ifmgd->associated = NULL; /* * we need to commit the associated = NULL change because the * scan code uses that to determine whether this iface should * go to/wake up from powersave or not -- and could otherwise * wake the queues erroneously. */ smp_mb(); /* * Thus, we can only afterwards stop the queues -- to account * for the case where another CPU is finishing a scan at this * time -- we don't want the scan code to enable queues. */ netif_tx_stop_all_queues(sdata->dev); netif_carrier_off(sdata->dev); mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, ifmgd->bssid); if (sta) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, tx); } mutex_unlock(&local->sta_mtx); /* * if we want to get out of ps before disassoc (why?) we have * to do it before sending disassoc, as otherwise the null-packet * won't be valid. */ if (local->hw.conf.flags & IEEE80211_CONF_PS) { local->hw.conf.flags &= ~IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } local->ps_sdata = NULL; /* disable per-vif ps */ ieee80211_recalc_ps_vif(sdata); /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */ if (tx) drv_flush(local, false); /* deauthenticate/disassociate now */ if (tx || frame_buf) ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype, reason, tx, frame_buf); /* flush out frame */ if (tx) drv_flush(local, false); /* clear bssid only after building the needed mgmt frames */ memset(ifmgd->bssid, 0, ETH_ALEN); /* remove AP and TDLS peers */ sta_info_flush(local, sdata); /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); ieee80211_led_assoc(local, 0); changed |= BSS_CHANGED_ASSOC; sdata->vif.bss_conf.assoc = false; /* on the next assoc, re-program HT parameters */ memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); local->power_constr_level = 0; del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); /* Disable ARP filtering */ if (sdata->vif.bss_conf.arp_filter_enabled) { sdata->vif.bss_conf.arp_filter_enabled = false; changed |= BSS_CHANGED_ARP_FILTER; } sdata->vif.bss_conf.qos = false; changed |= BSS_CHANGED_QOS; /* The BSSID (not really interesting) and HT changed */ changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); /* channel(_type) changes are handled by ieee80211_hw_config */ WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); ieee80211_hw_config(local, 0); /* disassociated - set to defaults now */ ieee80211_set_wmm_default(sdata, false); del_timer_sync(&sdata->u.mgd.conn_mon_timer); del_timer_sync(&sdata->u.mgd.bcn_mon_timer); del_timer_sync(&sdata->u.mgd.timer); del_timer_sync(&sdata->u.mgd.chswitch_timer); sdata->u.mgd.timers_running = 0; } void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr) { /* * We can postpone the mgd.timer whenever receiving unicast frames * from AP because we know that the connection is working both ways * at that time. But multicast frames (and hence also beacons) must * be ignored here, because we need to trigger the timer during * data idle periods for sending the periodic probe request to the * AP we're connected to. */ if (is_multicast_ether_addr(hdr->addr1)) return; ieee80211_sta_reset_conn_monitor(sdata); } static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; mutex_lock(&local->mtx); if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL))) { mutex_unlock(&local->mtx); return; } __ieee80211_stop_poll(sdata); mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, -1); mutex_unlock(&local->iflist_mtx); if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) goto out; /* * We've received a probe response, but are not sure whether * we have or will be receiving any beacons or data, so let's * schedule the timers again, just in case. */ ieee80211_sta_reset_beacon_monitor(sdata); mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); out: mutex_unlock(&local->mtx); } void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr, bool ack) { if (!ieee80211_is_data(hdr->frame_control)) return; if (ack) ieee80211_sta_reset_conn_monitor(sdata); if (ieee80211_is_nullfunc(hdr->frame_control) && sdata->u.mgd.probe_send_count > 0) { if (ack) sdata->u.mgd.probe_send_count = 0; else sdata->u.mgd.nullfunc_failed = true; ieee80211_queue_work(&sdata->local->hw, &sdata->work); } } static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; const u8 *ssid; u8 *dst = ifmgd->associated->bssid; u8 unicast_limit = max(1, max_probe_tries - 3); /* * Try sending broadcast probe requests for the last three * probe requests after the first ones failed since some * buggy APs only support broadcast probe requests. */ if (ifmgd->probe_send_count >= unicast_limit) dst = NULL; /* * When the hardware reports an accurate Tx ACK status, it's * better to send a nullfunc frame instead of a probe request, * as it will kick us off the AP quickly if we aren't associated * anymore. The timeout will be reset if the frame is ACKed by * the AP. */ ifmgd->probe_send_count++; if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { ifmgd->nullfunc_failed = false; ieee80211_send_nullfunc(sdata->local, sdata, 0); } else { int ssid_len; ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); if (WARN_ON_ONCE(ssid == NULL)) ssid_len = 0; else ssid_len = ssid[1]; ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, 0, (u32) -1, true, false, ifmgd->associated->channel); } ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); run_again(ifmgd, ifmgd->probe_timeout); if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) drv_flush(sdata->local, false); } static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, bool beacon) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; bool already = false; if (!ieee80211_sdata_running(sdata)) return; mutex_lock(&ifmgd->mtx); if (!ifmgd->associated) goto out; mutex_lock(&sdata->local->mtx); if (sdata->local->tmp_channel || sdata->local->scanning) { mutex_unlock(&sdata->local->mtx); goto out; } if (beacon) mlme_dbg_ratelimited(sdata, "detected beacon loss from AP - sending probe request\n"); ieee80211_cqm_rssi_notify(&sdata->vif, NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL); /* * The driver/our work has already reported this event or the * connection monitoring has kicked in and we have already sent * a probe request. Or maybe the AP died and the driver keeps * reporting until we disassociate... * * In either case we have to ignore the current call to this * function (except for setting the correct probe reason bit) * because otherwise we would reset the timer every time and * never check whether we received a probe response! */ if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL)) already = true; if (beacon) ifmgd->flags |= IEEE80211_STA_BEACON_POLL; else ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; mutex_unlock(&sdata->local->mtx); if (already) goto out; mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_ps(sdata->local, -1); mutex_unlock(&sdata->local->iflist_mtx); ifmgd->probe_send_count = 0; ieee80211_mgd_probe_ap_send(sdata); out: mutex_unlock(&ifmgd->mtx); } struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct cfg80211_bss *cbss; struct sk_buff *skb; const u8 *ssid; int ssid_len; if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return NULL; ASSERT_MGD_MTX(ifmgd); if (ifmgd->associated) cbss = ifmgd->associated; else if (ifmgd->auth_data) cbss = ifmgd->auth_data->bss; else if (ifmgd->assoc_data) cbss = ifmgd->assoc_data->bss; else return NULL; ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); if (WARN_ON_ONCE(ssid == NULL)) ssid_len = 0; else ssid_len = ssid[1]; skb = ieee80211_build_probe_req(sdata, cbss->bssid, (u32) -1, sdata->local->oper_channel, ssid + 2, ssid_len, NULL, 0, true); return skb; } EXPORT_SYMBOL(ieee80211_ap_probereq_get); static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; u8 frame_buf[DEAUTH_DISASSOC_LEN]; mutex_lock(&ifmgd->mtx); if (!ifmgd->associated) { mutex_unlock(&ifmgd->mtx); return; } sdata_info(sdata, "Connection to AP %pM lost\n", ifmgd->associated->bssid); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false, frame_buf); mutex_unlock(&ifmgd->mtx); /* * must be outside lock due to cfg80211, * but that's not a problem. */ cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); mutex_lock(&local->mtx); ieee80211_recalc_idle(local); mutex_unlock(&local->mtx); } static void ieee80211_beacon_connection_loss_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.beacon_connection_loss_work); struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct sta_info *sta; if (ifmgd->associated) { rcu_read_lock(); sta = sta_info_get(sdata, ifmgd->bssid); if (sta) sta->beacon_loss_count++; rcu_read_unlock(); } if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) __ieee80211_connection_loss(sdata); else ieee80211_mgd_probe_ap(sdata, true); } void ieee80211_beacon_loss(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_hw *hw = &sdata->local->hw; trace_api_beacon_loss(sdata); WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } EXPORT_SYMBOL(ieee80211_beacon_loss); void ieee80211_connection_loss(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_hw *hw = &sdata->local->hw; trace_api_connection_loss(sdata); WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } EXPORT_SYMBOL(ieee80211_connection_loss); static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, bool assoc) { struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; lockdep_assert_held(&sdata->u.mgd.mtx); if (!assoc) { sta_info_destroy_addr(sdata, auth_data->bss->bssid); memset(sdata->u.mgd.bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); } cfg80211_put_bss(auth_data->bss); kfree(auth_data); sdata->u.mgd.auth_data = NULL; } static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; u8 *pos; struct ieee802_11_elems elems; pos = mgmt->u.auth.variable; ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); if (!elems.challenge) return; auth_data->expected_transaction = 4; drv_mgd_prepare_tx(sdata->local, sdata); ieee80211_send_auth(sdata, 3, auth_data->algorithm, elems.challenge - 2, elems.challenge_len + 2, auth_data->bss->bssid, auth_data->bss->bssid, auth_data->key, auth_data->key_len, auth_data->key_idx); } static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 bssid[ETH_ALEN]; u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; lockdep_assert_held(&ifmgd->mtx); if (len < 24 + 6) return RX_MGMT_NONE; if (!ifmgd->auth_data || ifmgd->auth_data->done) return RX_MGMT_NONE; memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); if (!ether_addr_equal(bssid, mgmt->bssid)) return RX_MGMT_NONE; auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); status_code = le16_to_cpu(mgmt->u.auth.status_code); if (auth_alg != ifmgd->auth_data->algorithm || auth_transaction != ifmgd->auth_data->expected_transaction) return RX_MGMT_NONE; if (status_code != WLAN_STATUS_SUCCESS) { sdata_info(sdata, "%pM denied authentication (status %d)\n", mgmt->sa, status_code); ieee80211_destroy_auth_data(sdata, false); return RX_MGMT_CFG80211_RX_AUTH; } switch (ifmgd->auth_data->algorithm) { case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: case WLAN_AUTH_FT: break; case WLAN_AUTH_SHARED_KEY: if (ifmgd->auth_data->expected_transaction != 4) { ieee80211_auth_challenge(sdata, mgmt, len); /* need another frame */ return RX_MGMT_NONE; } break; default: WARN_ONCE(1, "invalid auth alg %d", ifmgd->auth_data->algorithm); return RX_MGMT_NONE; } sdata_info(sdata, "authenticated\n"); ifmgd->auth_data->done = true; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; run_again(ifmgd, ifmgd->auth_data->timeout); /* move station state to auth */ mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get(sdata, bssid); if (!sta) { WARN_ONCE(1, "%s: STA %pM not found", sdata->name, bssid); goto out_err; } if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) { sdata_info(sdata, "failed moving %pM to auth\n", bssid); goto out_err; } mutex_unlock(&sdata->local->sta_mtx); return RX_MGMT_CFG80211_RX_AUTH; out_err: mutex_unlock(&sdata->local->sta_mtx); /* ignore frame -- wait for timeout */ return RX_MGMT_NONE; } static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; const u8 *bssid = NULL; u16 reason_code; lockdep_assert_held(&ifmgd->mtx); if (len < 24 + 2) return RX_MGMT_NONE; if (!ifmgd->associated || !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) return RX_MGMT_NONE; bssid = ifmgd->associated->bssid; reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); sdata_info(sdata, "deauthenticated from %pM (Reason: %u)\n", bssid, reason_code); ieee80211_set_disassoc(sdata, 0, 0, false, NULL); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); return RX_MGMT_CFG80211_DEAUTH; } static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 reason_code; lockdep_assert_held(&ifmgd->mtx); if (len < 24 + 2) return RX_MGMT_NONE; if (!ifmgd->associated || !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) return RX_MGMT_NONE; reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); sdata_info(sdata, "disassociated from %pM (Reason: %u)\n", mgmt->sa, reason_code); ieee80211_set_disassoc(sdata, 0, 0, false, NULL); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); return RX_MGMT_CFG80211_DISASSOC; } static void ieee80211_get_rates(struct ieee80211_supported_band *sband, u8 *supp_rates, unsigned int supp_rates_len, u32 *rates, u32 *basic_rates, bool *have_higher_than_11mbit, int *min_rate, int *min_rate_index) { int i, j; for (i = 0; i < supp_rates_len; i++) { int rate = (supp_rates[i] & 0x7f) * 5; bool is_basic = !!(supp_rates[i] & 0x80); if (rate > 110) *have_higher_than_11mbit = true; /* * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009 * 7.3.2.2 as a magic value instead of a rate. Hence, skip it. * * Note: Even through the membership selector and the basic * rate flag share the same bit, they are not exactly * the same. */ if (!!(supp_rates[i] & 0x80) && (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) continue; for (j = 0; j < sband->n_bitrates; j++) { if (sband->bitrates[j].bitrate == rate) { *rates |= BIT(j); if (is_basic) *basic_rates |= BIT(j); if (rate < *min_rate) { *min_rate = rate; *min_rate_index = j; } break; } } } } static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, bool assoc) { struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; lockdep_assert_held(&sdata->u.mgd.mtx); if (!assoc) { sta_info_destroy_addr(sdata, assoc_data->bss->bssid); memset(sdata->u.mgd.bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); } kfree(assoc_data); sdata->u.mgd.assoc_data = NULL; } static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; struct sta_info *sta; u8 *pos; u16 capab_info, aid; struct ieee802_11_elems elems; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; u32 changed = 0; int err; /* AssocResp and ReassocResp have identical structure */ aid = le16_to_cpu(mgmt->u.assoc_resp.aid); capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) sdata_info(sdata, "invalid AID value 0x%x; bits 15:14 not set\n", aid); aid &= ~(BIT(15) | BIT(14)); ifmgd->broken_ap = false; if (aid == 0 || aid > IEEE80211_MAX_AID) { sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n", aid); aid = 0; ifmgd->broken_ap = true; } pos = mgmt->u.assoc_resp.variable; ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); if (!elems.supp_rates) { sdata_info(sdata, "no SuppRates element in AssocResp\n"); return false; } ifmgd->aid = aid; mutex_lock(&sdata->local->sta_mtx); /* * station info was already allocated and inserted before * the association and should be available to us */ sta = sta_info_get(sdata, cbss->bssid); if (WARN_ON(!sta)) { mutex_unlock(&sdata->local->sta_mtx); return false; } sband = local->hw.wiphy->bands[local->oper_channel->band]; if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems.ht_cap_elem, &sta->sta.ht_cap); sta->supports_40mhz = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; rate_control_rate_init(sta); if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) set_sta_flag(sta, WLAN_STA_MFP); if (elems.wmm_param) set_sta_flag(sta, WLAN_STA_WME); err = sta_info_move_state(sta, IEEE80211_STA_AUTH); if (!err) err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); if (err) { sdata_info(sdata, "failed to move station %pM to desired state\n", sta->sta.addr); WARN_ON(__sta_info_destroy(sta)); mutex_unlock(&sdata->local->sta_mtx); return false; } mutex_unlock(&sdata->local->sta_mtx); /* * Always handle WMM once after association regardless * of the first value the AP uses. Setting -1 here has * that effect because the AP values is an unsigned * 4-bit value. */ ifmgd->wmm_last_param_set = -1; if (elems.wmm_param) ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len); else ieee80211_set_wmm_default(sdata, false); changed |= BSS_CHANGED_QOS; if (elems.ht_operation && elems.wmm_param && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, cbss->bssid, false); /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; bss_conf->assoc_capability = capab_info; ieee80211_set_associated(sdata, cbss, changed); /* * If we're using 4-addr mode, let the AP know that we're * doing so, so that it can create the STA VLAN on its side */ if (ifmgd->use_4addr) ieee80211_send_4addr_nullfunc(local, sdata); /* * Start timer to probe the connection to the AP now. * Also start the timer that will detect beacon loss. */ ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt); ieee80211_sta_reset_beacon_monitor(sdata); return true; } static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct cfg80211_bss **bss) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; u16 capab_info, status_code, aid; struct ieee802_11_elems elems; u8 *pos; bool reassoc; lockdep_assert_held(&ifmgd->mtx); if (!assoc_data) return RX_MGMT_NONE; if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid)) return RX_MGMT_NONE; /* * AssocResp and ReassocResp have identical structure, so process both * of them in this function. */ if (len < 24 + 6) return RX_MGMT_NONE; reassoc = ieee80211_is_reassoc_req(mgmt->frame_control); capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); aid = le16_to_cpu(mgmt->u.assoc_resp.aid); sdata_info(sdata, "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n", reassoc ? "Rea" : "A", mgmt->sa, capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); pos = mgmt->u.assoc_resp.variable; ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && elems.timeout_int && elems.timeout_int_len == 5 && elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { u32 tu, ms; tu = get_unaligned_le32(elems.timeout_int + 1); ms = tu * 1024 / 1000; sdata_info(sdata, "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n", mgmt->sa, tu, ms); assoc_data->timeout = jiffies + msecs_to_jiffies(ms); if (ms > IEEE80211_ASSOC_TIMEOUT) run_again(ifmgd, assoc_data->timeout); return RX_MGMT_NONE; } *bss = assoc_data->bss; if (status_code != WLAN_STATUS_SUCCESS) { sdata_info(sdata, "%pM denied association (code=%d)\n", mgmt->sa, status_code); ieee80211_destroy_assoc_data(sdata, false); } else { if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ ieee80211_destroy_assoc_data(sdata, false); cfg80211_put_bss(*bss); return RX_MGMT_CFG80211_ASSOC_TIMEOUT; } sdata_info(sdata, "associated\n"); /* * destroy assoc_data afterwards, as otherwise an idle * recalc after assoc_data is NULL but before associated * is set can cause the interface to go idle */ ieee80211_destroy_assoc_data(sdata, true); } return RX_MGMT_CFG80211_RX_ASSOC; } static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status, struct ieee802_11_elems *elems, bool beacon) { struct ieee80211_local *local = sdata->local; int freq; struct ieee80211_bss *bss; struct ieee80211_channel *channel; bool need_ps = false; if (sdata->u.mgd.associated && ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) { bss = (void *)sdata->u.mgd.associated->priv; /* not previously set so we may need to recalc */ need_ps = !bss->dtim_period; } if (elems->ds_params && elems->ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems->ds_params[0], rx_status->band); else freq = rx_status->freq; channel = ieee80211_get_channel(local->hw.wiphy, freq); if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, channel, beacon); if (bss) ieee80211_rx_bss_put(local, bss); if (!sdata->u.mgd.associated) return; if (need_ps) { mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, -1); mutex_unlock(&local->iflist_mtx); } if (elems->ch_switch_ie && memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0) ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie, bss, rx_status->mactime); } static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt = (void *)skb->data; struct ieee80211_if_managed *ifmgd; struct ieee80211_rx_status *rx_status = (void *) skb->cb; size_t baselen, len = skb->len; struct ieee802_11_elems elems; ifmgd = &sdata->u.mgd; ASSERT_MGD_MTX(ifmgd); if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) return; /* ignore ProbeResp to foreign address */ baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; if (baselen > len) return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); if (ifmgd->associated && ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) ieee80211_reset_ap_probe(sdata); if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies && ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) { /* got probe response, continue with auth */ sdata_info(sdata, "direct probe responded\n"); ifmgd->auth_data->tries = 0; ifmgd->auth_data->timeout = jiffies; run_again(ifmgd, ifmgd->auth_data->timeout); } } /* * This is the canonical list of information elements we care about, * the filter code also gives us all changes to the Microsoft OUI * (00:50:F2) vendor IE which is used for WMM which we need to track. * * We implement beacon filtering in software since that means we can * avoid processing the frame here and in cfg80211, and userspace * will not be able to tell whether the hardware supports it or not. * * XXX: This list needs to be dynamic -- userspace needs to be able to * add items it requires. It also needs to be able to tell us to * look out for other vendor IEs. */ static const u64 care_about_ies = (1ULL << WLAN_EID_COUNTRY) | (1ULL << WLAN_EID_ERP_INFO) | (1ULL << WLAN_EID_CHANNEL_SWITCH) | (1ULL << WLAN_EID_PWR_CONSTRAINT) | (1ULL << WLAN_EID_HT_CAPABILITY) | (1ULL << WLAN_EID_HT_OPERATION); static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; size_t baselen; struct ieee802_11_elems elems; struct ieee80211_local *local = sdata->local; u32 changed = 0; bool erp_valid, directed_tim = false; u8 erp_value = 0; u32 ncrc; u8 *bssid; lockdep_assert_held(&ifmgd->mtx); /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; if (baselen > len) return; if (rx_status->freq != local->oper_channel->center_freq) return; if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); ifmgd->assoc_data->have_beacon = true; ifmgd->assoc_data->sent_assoc = false; /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; run_again(ifmgd, ifmgd->assoc_data->timeout); return; } if (!ifmgd->associated || !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) return; bssid = ifmgd->associated->bssid; /* Track average RSSI from the Beacon frames of the current AP */ ifmgd->last_beacon_signal = rx_status->signal; if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) { ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE; ifmgd->ave_beacon_signal = rx_status->signal * 16; ifmgd->last_cqm_event_signal = 0; ifmgd->count_beacon_signal = 1; ifmgd->last_ave_beacon_signal = 0; } else { ifmgd->ave_beacon_signal = (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 + (16 - IEEE80211_SIGNAL_AVE_WEIGHT) * ifmgd->ave_beacon_signal) / 16; ifmgd->count_beacon_signal++; } if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold && ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { int sig = ifmgd->ave_beacon_signal; int last_sig = ifmgd->last_ave_beacon_signal; /* * if signal crosses either of the boundaries, invoke callback * with appropriate parameters */ if (sig > ifmgd->rssi_max_thold && (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; drv_rssi_callback(local, RSSI_EVENT_HIGH); } else if (sig < ifmgd->rssi_min_thold && (last_sig >= ifmgd->rssi_max_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; drv_rssi_callback(local, RSSI_EVENT_LOW); } } if (bss_conf->cqm_rssi_thold && ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT && !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) { int sig = ifmgd->ave_beacon_signal / 16; int last_event = ifmgd->last_cqm_event_signal; int thold = bss_conf->cqm_rssi_thold; int hyst = bss_conf->cqm_rssi_hyst; if (sig < thold && (last_event == 0 || sig < last_event - hyst)) { ifmgd->last_cqm_event_signal = sig; ieee80211_cqm_rssi_notify( &sdata->vif, NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, GFP_KERNEL); } else if (sig > thold && (last_event == 0 || sig > last_event + hyst)) { ifmgd->last_cqm_event_signal = sig; ieee80211_cqm_rssi_notify( &sdata->vif, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, GFP_KERNEL); } } if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { mlme_dbg_ratelimited(sdata, "cancelling probereq poll due to a received beacon\n"); mutex_lock(&local->mtx); ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; ieee80211_run_deferred_scan(local); mutex_unlock(&local->mtx); mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, -1); mutex_unlock(&local->iflist_mtx); } /* * Push the beacon loss detection into the future since * we are processing a beacon from the AP just now. */ ieee80211_sta_reset_beacon_monitor(sdata); ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4); ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, len - baselen, &elems, care_about_ies, ncrc); if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid); if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { if (directed_tim) { if (local->hw.conf.dynamic_ps_timeout > 0) { if (local->hw.conf.flags & IEEE80211_CONF_PS) { local->hw.conf.flags &= ~IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } ieee80211_send_nullfunc(local, sdata, 0); } else if (!local->pspolling && sdata->u.mgd.powersave) { local->pspolling = true; /* * Here is assumed that the driver will be * able to send ps-poll frame and receive a * response even though power save mode is * enabled, but some drivers might require * to disable power save here. This needs * to be investigated. */ ieee80211_send_pspoll(local, sdata); } } } if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) return; ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc_valid = true; ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len)) changed |= BSS_CHANGED_QOS; if (elems.erp_info && elems.erp_info_len >= 1) { erp_valid = true; erp_value = elems.erp_info[0]; } else { erp_valid = false; } changed |= ieee80211_handle_bss_capability(sdata, le16_to_cpu(mgmt->u.beacon.capab_info), erp_valid, erp_value); if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[local->oper_channel->band]; changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, bssid, true); } /* Note: country IE parsing is done for us by cfg80211 */ if (elems.country_elem) { /* TODO: IBSS also needs this */ if (elems.pwr_constr_elem) ieee80211_handle_pwr_constr(sdata, le16_to_cpu(mgmt->u.probe_resp.capab_info), elems.pwr_constr_elem, elems.pwr_constr_elem_len); } ieee80211_bss_info_change_notify(sdata, changed); } void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; struct cfg80211_bss *bss = NULL; enum rx_mgmt_action rma = RX_MGMT_NONE; u16 fc; rx_status = (struct ieee80211_rx_status *) skb->cb; mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); mutex_lock(&ifmgd->mtx); switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_BEACON: ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status); break; case IEEE80211_STYPE_PROBE_RESP: ieee80211_rx_mgmt_probe_resp(sdata, skb); break; case IEEE80211_STYPE_AUTH: rma = ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); break; case IEEE80211_STYPE_DEAUTH: rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); break; case IEEE80211_STYPE_DISASSOC: rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); break; case IEEE80211_STYPE_ASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP: rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss); break; case IEEE80211_STYPE_ACTION: switch (mgmt->u.action.category) { case WLAN_CATEGORY_SPECTRUM_MGMT: ieee80211_sta_process_chanswitch(sdata, &mgmt->u.action.u.chan_switch.sw_elem, (void *)ifmgd->associated->priv, rx_status->mactime); break; } } mutex_unlock(&ifmgd->mtx); switch (rma) { case RX_MGMT_NONE: /* no action */ break; case RX_MGMT_CFG80211_DEAUTH: cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); break; case RX_MGMT_CFG80211_DISASSOC: cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); break; case RX_MGMT_CFG80211_RX_AUTH: cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, skb->len); break; case RX_MGMT_CFG80211_RX_ASSOC: cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, skb->len); break; case RX_MGMT_CFG80211_ASSOC_TIMEOUT: cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); break; default: WARN(1, "unexpected: %d", rma); } } static void ieee80211_sta_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; if (local->quiescing) { set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); return; } ieee80211_queue_work(&local->hw, &sdata->work); } static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, u8 *bssid, u8 reason) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[DEAUTH_DISASSOC_LEN]; ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, false, frame_buf); mutex_unlock(&ifmgd->mtx); /* * must be outside lock due to cfg80211, * but that's not a problem. */ cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); mutex_lock(&local->mtx); ieee80211_recalc_idle(local); mutex_unlock(&local->mtx); mutex_lock(&ifmgd->mtx); } static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; lockdep_assert_held(&ifmgd->mtx); if (WARN_ON_ONCE(!auth_data)) return -EINVAL; auth_data->tries++; if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { sdata_info(sdata, "authentication with %pM timed out\n", auth_data->bss->bssid); /* * Most likely AP is not in the range so remove the * bss struct for that AP. */ cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss); return -ETIMEDOUT; } drv_mgd_prepare_tx(local, sdata); if (auth_data->bss->proberesp_ies) { sdata_info(sdata, "send auth to %pM (try %d/%d)\n", auth_data->bss->bssid, auth_data->tries, IEEE80211_AUTH_MAX_TRIES); auth_data->expected_transaction = 2; ieee80211_send_auth(sdata, 1, auth_data->algorithm, auth_data->ie, auth_data->ie_len, auth_data->bss->bssid, auth_data->bss->bssid, NULL, 0, 0); } else { const u8 *ssidie; sdata_info(sdata, "direct probe to %pM (try %d/%i)\n", auth_data->bss->bssid, auth_data->tries, IEEE80211_AUTH_MAX_TRIES); ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); if (!ssidie) return -EINVAL; /* * Direct probe is sent to broadcast address as some APs * will not answer to direct packet in unassociated state. */ ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], NULL, 0, (u32) -1, true, false, auth_data->bss->channel); } auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; run_again(ifmgd, auth_data->timeout); return 0; } static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; struct ieee80211_local *local = sdata->local; lockdep_assert_held(&sdata->u.mgd.mtx); assoc_data->tries++; if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { sdata_info(sdata, "association with %pM timed out\n", assoc_data->bss->bssid); /* * Most likely AP is not in the range so remove the * bss struct for that AP. */ cfg80211_unlink_bss(local->hw.wiphy, assoc_data->bss); return -ETIMEDOUT; } sdata_info(sdata, "associate with %pM (try %d/%d)\n", assoc_data->bss->bssid, assoc_data->tries, IEEE80211_ASSOC_MAX_TRIES); ieee80211_send_assoc(sdata); assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; run_again(&sdata->u.mgd, assoc_data->timeout); return 0; } void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; mutex_lock(&ifmgd->mtx); if (ifmgd->auth_data && time_after(jiffies, ifmgd->auth_data->timeout)) { if (ifmgd->auth_data->done) { /* * ok ... we waited for assoc but userspace didn't, * so let's just kill the auth data */ ieee80211_destroy_auth_data(sdata, false); } else if (ieee80211_probe_auth(sdata)) { u8 bssid[ETH_ALEN]; memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); ieee80211_destroy_auth_data(sdata, false); mutex_unlock(&ifmgd->mtx); cfg80211_send_auth_timeout(sdata->dev, bssid); mutex_lock(&ifmgd->mtx); } } else if (ifmgd->auth_data) run_again(ifmgd, ifmgd->auth_data->timeout); if (ifmgd->assoc_data && time_after(jiffies, ifmgd->assoc_data->timeout)) { if (!ifmgd->assoc_data->have_beacon || ieee80211_do_assoc(sdata)) { u8 bssid[ETH_ALEN]; memcpy(bssid, ifmgd->assoc_data->bss->bssid, ETH_ALEN); ieee80211_destroy_assoc_data(sdata, false); mutex_unlock(&ifmgd->mtx); cfg80211_send_assoc_timeout(sdata->dev, bssid); mutex_lock(&ifmgd->mtx); } } else if (ifmgd->assoc_data) run_again(ifmgd, ifmgd->assoc_data->timeout); if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL) && ifmgd->associated) { u8 bssid[ETH_ALEN]; int max_tries; memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) max_tries = max_nullfunc_tries; else max_tries = max_probe_tries; /* ACK received for nullfunc probing frame */ if (!ifmgd->probe_send_count) ieee80211_reset_ap_probe(sdata); else if (ifmgd->nullfunc_failed) { if (ifmgd->probe_send_count < max_tries) { mlme_dbg(sdata, "No ack for nullfunc frame to AP %pM, try %d/%i\n", bssid, ifmgd->probe_send_count, max_tries); ieee80211_mgd_probe_ap_send(sdata); } else { mlme_dbg(sdata, "No ack for nullfunc frame to AP %pM, disconnecting.\n", bssid); ieee80211_sta_connection_lost(sdata, bssid, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } } else if (time_is_after_jiffies(ifmgd->probe_timeout)) run_again(ifmgd, ifmgd->probe_timeout); else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { mlme_dbg(sdata, "Failed to send nullfunc to AP %pM after %dms, disconnecting\n", bssid, probe_wait_ms); ieee80211_sta_connection_lost(sdata, bssid, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } else if (ifmgd->probe_send_count < max_tries) { mlme_dbg(sdata, "No probe response from AP %pM after %dms, try %d/%i\n", bssid, probe_wait_ms, ifmgd->probe_send_count, max_tries); ieee80211_mgd_probe_ap_send(sdata); } else { /* * We actually lost the connection ... or did we? * Let's make sure! */ wiphy_debug(local->hw.wiphy, "%s: No probe response from AP %pM" " after %dms, disconnecting.\n", sdata->name, bssid, probe_wait_ms); ieee80211_sta_connection_lost(sdata, bssid, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } } mutex_unlock(&ifmgd->mtx); mutex_lock(&local->mtx); ieee80211_recalc_idle(local); mutex_unlock(&local->mtx); } static void ieee80211_sta_bcn_mon_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_local *local = sdata->local; if (local->quiescing) return; ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_connection_loss_work); } static void ieee80211_sta_conn_mon_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; if (local->quiescing) return; ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); } static void ieee80211_sta_monitor_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.monitor_work); ieee80211_mgd_probe_ap(sdata, false); } static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) { u32 flags; if (sdata->vif.type == NL80211_IFTYPE_STATION) { __ieee80211_stop_poll(sdata); /* let's probe the connection once */ flags = sdata->local->hw.flags; if (!(flags & IEEE80211_HW_CONNECTION_MONITOR)) ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work); /* and do all the other regular work too */ ieee80211_queue_work(&sdata->local->hw, &sdata->work); } } #ifdef CONFIG_PM void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; /* * we need to use atomic bitops for the running bits * only because both timers might fire at the same * time -- the code here is properly synchronised. */ cancel_work_sync(&ifmgd->request_smps_work); cancel_work_sync(&ifmgd->monitor_work); cancel_work_sync(&ifmgd->beacon_connection_loss_work); if (del_timer_sync(&ifmgd->timer)) set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); cancel_work_sync(&ifmgd->chswitch_work); if (del_timer_sync(&ifmgd->chswitch_timer)) set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); /* these will just be re-established on connection */ del_timer_sync(&ifmgd->conn_mon_timer); del_timer_sync(&ifmgd->bcn_mon_timer); } void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (!ifmgd->associated) return; if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; mutex_lock(&ifmgd->mtx); if (ifmgd->associated) { mlme_dbg(sdata, "driver requested disconnect after resume\n"); ieee80211_sta_connection_lost(sdata, ifmgd->associated->bssid, WLAN_REASON_UNSPECIFIED); mutex_unlock(&ifmgd->mtx); return; } mutex_unlock(&ifmgd->mtx); } if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) add_timer(&ifmgd->timer); if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) add_timer(&ifmgd->chswitch_timer); ieee80211_sta_reset_beacon_monitor(sdata); mutex_lock(&sdata->local->mtx); ieee80211_restart_sta_timer(sdata); mutex_unlock(&sdata->local->mtx); } #endif /* interface setup */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd; ifmgd = &sdata->u.mgd; INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); INIT_WORK(&ifmgd->beacon_connection_loss_work, ieee80211_beacon_connection_loss_work); INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, (unsigned long) sdata); setup_timer(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, (unsigned long) sdata); ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; ifmgd->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; ifmgd->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; mutex_init(&ifmgd->mtx); if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; else ifmgd->req_smps = IEEE80211_SMPS_OFF; } /* scan finished notification */ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; /* Restart STA timers */ rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) ieee80211_restart_sta_timer(sdata); rcu_read_unlock(); } int ieee80211_max_network_latency(struct notifier_block *nb, unsigned long data, void *dummy) { s32 latency_usec = (s32) data; struct ieee80211_local *local = container_of(nb, struct ieee80211_local, network_latency_notifier); mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, latency_usec); mutex_unlock(&local->iflist_mtx); return 0; } static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; int ht_cfreq; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; const u8 *ht_oper_ie; const struct ieee80211_ht_operation *ht_oper = NULL; struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[cbss->channel->band]; ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ; if (sband->ht_cap.ht_supported) { ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION, cbss->information_elements, cbss->len_information_elements); if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) ht_oper = (void *)(ht_oper_ie + 2); } if (ht_oper) { ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, cbss->channel->band); /* check that channel matches the right operating channel */ if (cbss->channel->center_freq != ht_cfreq) { /* * It's possible that some APs are confused here; * Netgear WNDR3700 sometimes reports 4 higher than * the actual channel in association responses, but * since we look at probe response/beacon data here * it should be OK. */ sdata_info(sdata, "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", cbss->channel->center_freq, ht_cfreq, ht_oper->primary_chan, cbss->channel->band); ht_oper = NULL; } } if (ht_oper) { channel_type = NL80211_CHAN_HT20; if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: channel_type = NL80211_CHAN_HT40PLUS; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: channel_type = NL80211_CHAN_HT40MINUS; break; } } } if (!ieee80211_set_channel_type(local, sdata, channel_type)) { /* can only fail due to HT40+/- mismatch */ channel_type = NL80211_CHAN_HT20; sdata_info(sdata, "disabling 40 MHz due to multi-vif mismatch\n"); ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); } local->oper_channel = cbss->channel; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); return 0; } static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss, bool assoc) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; bool have_sta = false; int err; if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) return -EINVAL; if (assoc) { rcu_read_lock(); have_sta = sta_info_get(sdata, cbss->bssid); rcu_read_unlock(); } if (!have_sta) { new_sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); if (!new_sta) return -ENOMEM; } mutex_lock(&local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&local->mtx); if (new_sta) { u32 rates = 0, basic_rates = 0; bool have_higher_than_11mbit; int min_rate = INT_MAX, min_rate_index = -1; struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[cbss->channel->band]; err = ieee80211_prep_channel(sdata, cbss); if (err) { sta_info_free(local, new_sta); return err; } ieee80211_get_rates(sband, bss->supp_rates, bss->supp_rates_len, &rates, &basic_rates, &have_higher_than_11mbit, &min_rate, &min_rate_index); /* * This used to be a workaround for basic rates missing * in the association response frame. Now that we no * longer use the basic rates from there, it probably * doesn't happen any more, but keep the workaround so * in case some *other* APs are buggy in different ways * we can connect -- with a warning. */ if (!basic_rates && min_rate_index >= 0) { sdata_info(sdata, "No basic rates, using min rate instead\n"); basic_rates = BIT(min_rate_index); } new_sta->sta.supp_rates[cbss->channel->band] = rates; sdata->vif.bss_conf.basic_rates = basic_rates; /* cf. IEEE 802.11 9.2.12 */ if (local->oper_channel->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit) sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN); /* set timing information */ sdata->vif.bss_conf.beacon_int = cbss->beacon_interval; sdata->vif.bss_conf.sync_tsf = cbss->tsf; sdata->vif.bss_conf.sync_device_ts = bss->device_ts; /* tell driver about BSSID, basic rates and timing */ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT); if (assoc) sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH); err = sta_info_insert(new_sta); new_sta = NULL; if (err) { sdata_info(sdata, "failed to insert STA entry for the AP (error %d)\n", err); return err; } } else WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid)); return 0; } /* config hooks */ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct cfg80211_auth_request *req) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_auth_data *auth_data; u16 auth_alg; int err; /* prepare auth data structure */ switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: auth_alg = WLAN_AUTH_OPEN; break; case NL80211_AUTHTYPE_SHARED_KEY: if (IS_ERR(local->wep_tx_tfm)) return -EOPNOTSUPP; auth_alg = WLAN_AUTH_SHARED_KEY; break; case NL80211_AUTHTYPE_FT: auth_alg = WLAN_AUTH_FT; break; case NL80211_AUTHTYPE_NETWORK_EAP: auth_alg = WLAN_AUTH_LEAP; break; default: return -EOPNOTSUPP; } auth_data = kzalloc(sizeof(*auth_data) + req->ie_len, GFP_KERNEL); if (!auth_data) return -ENOMEM; auth_data->bss = req->bss; if (req->ie && req->ie_len) { memcpy(auth_data->ie, req->ie, req->ie_len); auth_data->ie_len = req->ie_len; } if (req->key && req->key_len) { auth_data->key_len = req->key_len; auth_data->key_idx = req->key_idx; memcpy(auth_data->key, req->key, req->key_len); } auth_data->algorithm = auth_alg; /* try to authenticate/probe */ mutex_lock(&ifmgd->mtx); if ((ifmgd->auth_data && !ifmgd->auth_data->done) || ifmgd->assoc_data) { err = -EBUSY; goto err_free; } if (ifmgd->auth_data) ieee80211_destroy_auth_data(sdata, false); /* prep auth_data so we don't go into idle on disassoc */ ifmgd->auth_data = auth_data; if (ifmgd->associated) ieee80211_set_disassoc(sdata, 0, 0, false, NULL); sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); err = ieee80211_prep_connection(sdata, req->bss, false); if (err) goto err_clear; err = ieee80211_probe_auth(sdata); if (err) { sta_info_destroy_addr(sdata, req->bss->bssid); goto err_clear; } /* hold our own reference */ cfg80211_ref_bss(auth_data->bss); err = 0; goto out_unlock; err_clear: memset(ifmgd->bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); ifmgd->auth_data = NULL; err_free: kfree(auth_data); out_unlock: mutex_unlock(&ifmgd->mtx); return err; } int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss *bss = (void *)req->bss->priv; struct ieee80211_mgd_assoc_data *assoc_data; struct ieee80211_supported_band *sband; const u8 *ssidie, *ht_ie; int i, err; ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); if (!ssidie) return -EINVAL; assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); if (!assoc_data) return -ENOMEM; mutex_lock(&ifmgd->mtx); if (ifmgd->associated) ieee80211_set_disassoc(sdata, 0, 0, false, NULL); if (ifmgd->auth_data && !ifmgd->auth_data->done) { err = -EBUSY; goto err_free; } if (ifmgd->assoc_data) { err = -EBUSY; goto err_free; } if (ifmgd->auth_data) { bool match; /* keep sta info, bssid if matching */ match = ether_addr_equal(ifmgd->bssid, req->bss->bssid); ieee80211_destroy_auth_data(sdata, match); } /* prepare assoc data */ /* * keep only the 40 MHz disable bit set as it might have * been set during authentication already, all other bits * should be reset for a new connection */ ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ; ifmgd->beacon_crc_valid = false; /* * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. * We still associate in non-HT mode (11a/b/g) if any one of these * ciphers is configured as pairwise. * We can set this to true for non-11n hardware, that'll be checked * separately along with the peer capabilities. */ for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) { if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { ifmgd->flags |= IEEE80211_STA_DISABLE_11N; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; netdev_info(sdata->dev, "disabling HT/VHT due to WEP/TKIP use\n"); } } if (req->flags & ASSOC_REQ_DISABLE_HT) { ifmgd->flags |= IEEE80211_STA_DISABLE_11N; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; } /* Also disable HT if we don't support it or the AP doesn't use WMM */ sband = local->hw.wiphy->bands[req->bss->channel->band]; if (!sband->ht_cap.ht_supported || local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { ifmgd->flags |= IEEE80211_STA_DISABLE_11N; if (!bss->wmm_used) netdev_info(sdata->dev, "disabling HT as WMM/QoS is not supported by the AP\n"); } /* disable VHT if we don't support it or the AP doesn't use WMM */ if (!sband->vht_cap.vht_supported || local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; if (!bss->wmm_used) netdev_info(sdata->dev, "disabling VHT as WMM/QoS is not supported by the AP\n"); } memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa)); memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask, sizeof(ifmgd->ht_capa_mask)); if (req->ie && req->ie_len) { memcpy(assoc_data->ie, req->ie, req->ie_len); assoc_data->ie_len = req->ie_len; } assoc_data->bss = req->bss; if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { if (ifmgd->powersave) ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; else ifmgd->ap_smps = IEEE80211_SMPS_OFF; } else ifmgd->ap_smps = ifmgd->req_smps; assoc_data->capability = req->bss->capability; assoc_data->wmm = bss->wmm_used && (local->hw.queues >= IEEE80211_NUM_ACS); assoc_data->supp_rates = bss->supp_rates; assoc_data->supp_rates_len = bss->supp_rates_len; ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) assoc_data->ap_ht_param = ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; else ifmgd->flags |= IEEE80211_STA_DISABLE_11N; if (bss->wmm_used && bss->uapsd_supported && (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { assoc_data->uapsd = true; ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED; } else { assoc_data->uapsd = false; ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; } memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); assoc_data->ssid_len = ssidie[1]; if (req->prev_bssid) memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); if (req->use_mfp) { ifmgd->mfp = IEEE80211_MFP_REQUIRED; ifmgd->flags |= IEEE80211_STA_MFP_ENABLED; } else { ifmgd->mfp = IEEE80211_MFP_DISABLED; ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED; } if (req->crypto.control_port) ifmgd->flags |= IEEE80211_STA_CONTROL_PORT; else ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; sdata->control_port_protocol = req->crypto.control_port_ethertype; sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt; /* kick off associate process */ ifmgd->assoc_data = assoc_data; err = ieee80211_prep_connection(sdata, req->bss, true); if (err) goto err_clear; if (!bss->dtim_period && sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { /* * Wait up to one beacon interval ... * should this be more if we miss one? */ sdata_info(sdata, "waiting for beacon from %pM\n", ifmgd->bssid); assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); } else { assoc_data->have_beacon = true; assoc_data->sent_assoc = false; assoc_data->timeout = jiffies; } run_again(ifmgd, assoc_data->timeout); if (bss->corrupt_data) { char *corrupt_type = "data"; if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_BEACON) { if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) corrupt_type = "beacon and probe response"; else corrupt_type = "beacon"; } else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) corrupt_type = "probe response"; sdata_info(sdata, "associating with AP with corrupt %s\n", corrupt_type); } err = 0; goto out; err_clear: memset(ifmgd->bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); ifmgd->assoc_data = NULL; err_free: kfree(assoc_data); out: mutex_unlock(&ifmgd->mtx); return err; } int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, struct cfg80211_deauth_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[DEAUTH_DISASSOC_LEN]; mutex_lock(&ifmgd->mtx); if (ifmgd->auth_data) { ieee80211_destroy_auth_data(sdata, false); mutex_unlock(&ifmgd->mtx); return 0; } sdata_info(sdata, "deauthenticating from %pM by local choice (reason=%d)\n", req->bssid, req->reason_code); if (ifmgd->associated && ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, req->reason_code, true, frame_buf); } else { drv_mgd_prepare_tx(sdata->local, sdata); ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, req->reason_code, true, frame_buf); } mutex_unlock(&ifmgd->mtx); __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); return 0; } int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_disassoc_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 bssid[ETH_ALEN]; u8 frame_buf[DEAUTH_DISASSOC_LEN]; mutex_lock(&ifmgd->mtx); /* * cfg80211 should catch this ... but it's racy since * we can receive a disassoc frame, process it, hand it * to cfg80211 while that's in a locked section already * trying to tell us that the user wants to disconnect. */ if (ifmgd->associated != req->bss) { mutex_unlock(&ifmgd->mtx); return -ENOLINK; } sdata_info(sdata, "disassociating from %pM by local choice (reason=%d)\n", req->bss->bssid, req->reason_code); memcpy(bssid, req->bss->bssid, ETH_ALEN); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, req->reason_code, !req->local_state_change, frame_buf); mutex_unlock(&ifmgd->mtx); __cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); return 0; } void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; mutex_lock(&ifmgd->mtx); if (ifmgd->assoc_data) ieee80211_destroy_assoc_data(sdata, false); if (ifmgd->auth_data) ieee80211_destroy_auth_data(sdata, false); del_timer_sync(&ifmgd->timer); mutex_unlock(&ifmgd->mtx); } void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); trace_api_cqm_rssi_notify(sdata, rssi_event); cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); } EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); compat-drivers-2012-09-18/net/mac80211/debugfs_key.h0000644000175000017500000000223012026211315020737 0ustar mcgrofmcgrof#ifndef __MAC80211_DEBUGFS_KEY_H #define __MAC80211_DEBUGFS_KEY_H #ifdef CONFIG_MAC80211_DEBUGFS void ieee80211_debugfs_key_add(struct ieee80211_key *key); void ieee80211_debugfs_key_remove(struct ieee80211_key *key); void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_key_add_mgmt_default( struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_key_remove_mgmt_default( struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta); #else static inline void ieee80211_debugfs_key_add(struct ieee80211_key *key) {} static inline void ieee80211_debugfs_key_remove(struct ieee80211_key *key) {} static inline void ieee80211_debugfs_key_update_default( struct ieee80211_sub_if_data *sdata) {} static inline void ieee80211_debugfs_key_add_mgmt_default( struct ieee80211_sub_if_data *sdata) {} static inline void ieee80211_debugfs_key_remove_mgmt_default( struct ieee80211_sub_if_data *sdata) {} static inline void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta) {} #endif #endif /* __MAC80211_DEBUGFS_KEY_H */ compat-drivers-2012-09-18/net/mac80211/debugfs_netdev.c0000644000175000017500000004671612026211315021450 0ustar mcgrofmcgrof/* * Copyright (c) 2006 Jiri Benc * Copyright 2007 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "rate.h" #include "debugfs.h" #include "debugfs_netdev.h" #include "driver-ops.h" static ssize_t ieee80211_if_read( struct ieee80211_sub_if_data *sdata, char __user *userbuf, size_t count, loff_t *ppos, ssize_t (*format)(const struct ieee80211_sub_if_data *, char *, int)) { char buf[70]; ssize_t ret = -EINVAL; read_lock(&dev_base_lock); if (sdata->dev->reg_state == NETREG_REGISTERED) ret = (*format)(sdata, buf, sizeof(buf)); read_unlock(&dev_base_lock); if (ret >= 0) ret = simple_read_from_buffer(userbuf, count, ppos, buf, ret); return ret; } static ssize_t ieee80211_if_write( struct ieee80211_sub_if_data *sdata, const char __user *userbuf, size_t count, loff_t *ppos, ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int)) { char buf[64]; ssize_t ret; if (count >= sizeof(buf)) return -E2BIG; if (copy_from_user(buf, userbuf, count)) return -EFAULT; buf[count] = '\0'; ret = -ENODEV; rtnl_lock(); if (sdata->dev->reg_state == NETREG_REGISTERED) ret = (*write)(sdata, buf, count); rtnl_unlock(); return ret; } #define IEEE80211_IF_FMT(name, field, format_string) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, char *buf, \ int buflen) \ { \ return scnprintf(buf, buflen, format_string, sdata->field); \ } #define IEEE80211_IF_FMT_DEC(name, field) \ IEEE80211_IF_FMT(name, field, "%d\n") #define IEEE80211_IF_FMT_HEX(name, field) \ IEEE80211_IF_FMT(name, field, "%#x\n") #define IEEE80211_IF_FMT_LHEX(name, field) \ IEEE80211_IF_FMT(name, field, "%#lx\n") #define IEEE80211_IF_FMT_SIZE(name, field) \ IEEE80211_IF_FMT(name, field, "%zd\n") #define IEEE80211_IF_FMT_HEXARRAY(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, \ char *buf, int buflen) \ { \ char *p = buf; \ int i; \ for (i = 0; i < sizeof(sdata->field); i++) { \ p += scnprintf(p, buflen + buf - p, "%.2x ", \ sdata->field[i]); \ } \ p += scnprintf(p, buflen + buf - p, "\n"); \ return p - buf; \ } #define IEEE80211_IF_FMT_ATOMIC(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, \ char *buf, int buflen) \ { \ return scnprintf(buf, buflen, "%d\n", atomic_read(&sdata->field));\ } #define IEEE80211_IF_FMT_MAC(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, char *buf, \ int buflen) \ { \ return scnprintf(buf, buflen, "%pM\n", sdata->field); \ } #define IEEE80211_IF_FMT_DEC_DIV_16(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, \ char *buf, int buflen) \ { \ return scnprintf(buf, buflen, "%d\n", sdata->field / 16); \ } #define __IEEE80211_IF_FILE(name, _write) \ static ssize_t ieee80211_if_read_##name(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ return ieee80211_if_read(file->private_data, \ userbuf, count, ppos, \ ieee80211_if_fmt_##name); \ } \ static const struct file_operations name##_ops = { \ .read = ieee80211_if_read_##name, \ .write = (_write), \ .open = simple_open, \ .llseek = generic_file_llseek, \ } #define __IEEE80211_IF_FILE_W(name) \ static ssize_t ieee80211_if_write_##name(struct file *file, \ const char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ return ieee80211_if_write(file->private_data, userbuf, count, \ ppos, ieee80211_if_parse_##name); \ } \ __IEEE80211_IF_FILE(name, ieee80211_if_write_##name) #define IEEE80211_IF_FILE(name, field, format) \ IEEE80211_IF_FMT_##format(name, field) \ __IEEE80211_IF_FILE(name, NULL) /* common attributes */ IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ], HEX); IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ], HEX); IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz, rc_rateidx_mcs_mask[IEEE80211_BAND_2GHZ], HEXARRAY); IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz, rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY); IEEE80211_IF_FILE(flags, flags, HEX); IEEE80211_IF_FILE(state, state, LHEX); IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC); /* STA attributes */ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); IEEE80211_IF_FILE(last_beacon, u.mgd.last_beacon_signal, DEC); IEEE80211_IF_FILE(ave_beacon, u.mgd.ave_beacon_signal, DEC_DIV_16); static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode) { struct ieee80211_local *local = sdata->local; int err; if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) && smps_mode == IEEE80211_SMPS_STATIC) return -EINVAL; /* auto should be dynamic if in PS mode */ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) && (smps_mode == IEEE80211_SMPS_DYNAMIC || smps_mode == IEEE80211_SMPS_AUTOMATIC)) return -EINVAL; /* supported only on managed interfaces for now */ if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; mutex_lock(&sdata->u.mgd.mtx); err = __ieee80211_request_smps(sdata, smps_mode); mutex_unlock(&sdata->u.mgd.mtx); return err; } static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { [IEEE80211_SMPS_AUTOMATIC] = "auto", [IEEE80211_SMPS_OFF] = "off", [IEEE80211_SMPS_STATIC] = "static", [IEEE80211_SMPS_DYNAMIC] = "dynamic", }; static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; return snprintf(buf, buflen, "request: %s\nused: %s\n", smps_modes[sdata->u.mgd.req_smps], smps_modes[sdata->u.mgd.ap_smps]); } static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) { enum ieee80211_smps_mode mode; for (mode = 0; mode < IEEE80211_SMPS_NUM_MODES; mode++) { if (strncmp(buf, smps_modes[mode], buflen) == 0) { int err = ieee80211_set_smps(sdata, mode); if (!err) return buflen; return err; } } return -EINVAL; } __IEEE80211_IF_FILE_W(smps); static ssize_t ieee80211_if_fmt_tkip_mic_test( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { return -EOPNOTSUPP; } static int hwaddr_aton(const char *txt, u8 *addr) { int i; for (i = 0; i < ETH_ALEN; i++) { int a, b; a = hex_to_bin(*txt++); if (a < 0) return -1; b = hex_to_bin(*txt++); if (b < 0) return -1; *addr++ = (a << 4) | b; if (i < 5 && *txt++ != ':') return -1; } return 0; } static ssize_t ieee80211_if_parse_tkip_mic_test( struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) { struct ieee80211_local *local = sdata->local; u8 addr[ETH_ALEN]; struct sk_buff *skb; struct ieee80211_hdr *hdr; __le16 fc; /* * Assume colon-delimited MAC address with possible white space * following. */ if (buflen < 3 * ETH_ALEN - 1) return -EINVAL; if (hwaddr_aton(buf, addr) < 0) return -EINVAL; if (!ieee80211_sdata_running(sdata)) return -ENOTCONN; skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 100); if (!skb) return -ENOMEM; skb_reserve(skb, local->hw.extra_tx_headroom); hdr = (struct ieee80211_hdr *) skb_put(skb, 24); memset(hdr, 0, 24); fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); switch (sdata->vif.type) { case NL80211_IFTYPE_AP: fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ memcpy(hdr->addr1, addr, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr3, sdata->vif.addr, ETH_ALEN); break; case NL80211_IFTYPE_STATION: fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ if (sdata->vif.bss_conf.bssid == NULL) { dev_kfree_skb(skb); return -ENOTCONN; } memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr3, addr, ETH_ALEN); break; default: dev_kfree_skb(skb); return -EOPNOTSUPP; } hdr->frame_control = fc; /* * Add some length to the test frame to make it look bit more valid. * The exact contents does not matter since the recipient is required * to drop this because of the Michael MIC failure. */ memset(skb_put(skb, 50), 0, 50); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_TKIP_MIC_FAILURE; ieee80211_tx_skb(sdata, skb); return buflen; } __IEEE80211_IF_FILE_W(tkip_mic_test); static ssize_t ieee80211_if_fmt_uapsd_queues( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_queues); } static ssize_t ieee80211_if_parse_uapsd_queues( struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 val; int ret; ret = kstrtou8(buf, 0, &val); if (ret) return ret; if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) return -ERANGE; ifmgd->uapsd_queues = val; return buflen; } __IEEE80211_IF_FILE_W(uapsd_queues); static ssize_t ieee80211_if_fmt_uapsd_max_sp_len( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_max_sp_len); } static ssize_t ieee80211_if_parse_uapsd_max_sp_len( struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; unsigned long val; int ret; ret = kstrtoul(buf, 0, &val); if (ret) return -EINVAL; if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) return -ERANGE; ifmgd->uapsd_max_sp_len = val; return buflen; } __IEEE80211_IF_FILE_W(uapsd_max_sp_len); /* AP attributes */ IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC); IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); static ssize_t ieee80211_if_fmt_num_buffered_multicast( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { return scnprintf(buf, buflen, "%u\n", skb_queue_len(&sdata->u.ap.ps_bc_buf)); } __IEEE80211_IF_FILE(num_buffered_multicast, NULL); /* IBSS attributes */ static ssize_t ieee80211_if_fmt_tsf( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { struct ieee80211_local *local = sdata->local; u64 tsf; tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata); return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf); } static ssize_t ieee80211_if_parse_tsf( struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) { struct ieee80211_local *local = sdata->local; unsigned long long tsf; int ret; int tsf_is_delta = 0; if (strncmp(buf, "reset", 5) == 0) { if (local->ops->reset_tsf) { drv_reset_tsf(local, sdata); wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); } } else { if (buflen > 10 && buf[1] == '=') { if (buf[0] == '+') tsf_is_delta = 1; else if (buf[0] == '-') tsf_is_delta = -1; else return -EINVAL; buf += 2; } ret = kstrtoull(buf, 10, &tsf); if (ret < 0) return -EINVAL; if (tsf_is_delta) tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf; if (local->ops->set_tsf) { drv_set_tsf(local, sdata, tsf); wiphy_info(local->hw.wiphy, "debugfs set TSF to %#018llx\n", tsf); } } return buflen; } __IEEE80211_IF_FILE_W(tsf); /* WDS attributes */ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); #ifdef CONFIG_MAC80211_MESH /* Mesh stats attributes */ IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC); IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); IEEE80211_IF_FILE(dropped_frames_congestion, u.mesh.mshstats.dropped_frames_congestion, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, u.mesh.mshstats.dropped_frames_no_route, DEC); IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC); /* Mesh parameters */ IEEE80211_IF_FILE(dot11MeshMaxRetries, u.mesh.mshcfg.dot11MeshMaxRetries, DEC); IEEE80211_IF_FILE(dot11MeshRetryTimeout, u.mesh.mshcfg.dot11MeshRetryTimeout, DEC); IEEE80211_IF_FILE(dot11MeshConfirmTimeout, u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHoldingTimeout, u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC); IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC); IEEE80211_IF_FILE(element_ttl, u.mesh.mshcfg.element_ttl, DEC); IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC); IEEE80211_IF_FILE(dot11MeshMaxPeerLinks, u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC); IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout, u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval, u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPperrMinInterval, u.mesh.mshcfg.dot11MeshHWMPperrMinInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime, u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC); IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries, u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC); IEEE80211_IF_FILE(path_refresh_time, u.mesh.mshcfg.path_refresh_time, DEC); IEEE80211_IF_FILE(min_discovery_timeout, u.mesh.mshcfg.min_discovery_timeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMPRootMode, u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol, u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC); IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC); IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC); IEEE80211_IF_FILE(ht_opmode, u.mesh.mshcfg.ht_opmode, DEC); IEEE80211_IF_FILE(dot11MeshHWMPactivePathToRootTimeout, u.mesh.mshcfg.dot11MeshHWMPactivePathToRootTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMProotInterval, u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval, u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC); #endif #define DEBUGFS_ADD_MODE(name, mode) \ debugfs_create_file(#name, mode, sdata->debugfs.dir, \ sdata, &name##_ops); #define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400) static void add_common_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(drop_unencrypted); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); } static void add_sta_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(bssid); DEBUGFS_ADD(aid); DEBUGFS_ADD(last_beacon); DEBUGFS_ADD(ave_beacon); DEBUGFS_ADD_MODE(smps, 0600); DEBUGFS_ADD_MODE(tkip_mic_test, 0200); DEBUGFS_ADD_MODE(uapsd_queues, 0600); DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(num_mcast_sta); DEBUGFS_ADD(num_sta_ps); DEBUGFS_ADD(dtim_count); DEBUGFS_ADD(num_buffered_multicast); DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } static void add_ibss_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD_MODE(tsf, 0600); } static void add_wds_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(peer); } #ifdef CONFIG_MAC80211_MESH static void add_mesh_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD_MODE(tsf, 0600); } static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) { struct dentry *dir = debugfs_create_dir("mesh_stats", sdata->debugfs.dir); #define MESHSTATS_ADD(name)\ debugfs_create_file(#name, 0400, dir, sdata, &name##_ops); MESHSTATS_ADD(fwded_mcast); MESHSTATS_ADD(fwded_unicast); MESHSTATS_ADD(fwded_frames); MESHSTATS_ADD(dropped_frames_ttl); MESHSTATS_ADD(dropped_frames_no_route); MESHSTATS_ADD(dropped_frames_congestion); MESHSTATS_ADD(estab_plinks); #undef MESHSTATS_ADD } static void add_mesh_config(struct ieee80211_sub_if_data *sdata) { struct dentry *dir = debugfs_create_dir("mesh_config", sdata->debugfs.dir); #define MESHPARAMS_ADD(name) \ debugfs_create_file(#name, 0600, dir, sdata, &name##_ops); MESHPARAMS_ADD(dot11MeshMaxRetries); MESHPARAMS_ADD(dot11MeshRetryTimeout); MESHPARAMS_ADD(dot11MeshConfirmTimeout); MESHPARAMS_ADD(dot11MeshHoldingTimeout); MESHPARAMS_ADD(dot11MeshTTL); MESHPARAMS_ADD(element_ttl); MESHPARAMS_ADD(auto_open_plinks); MESHPARAMS_ADD(dot11MeshMaxPeerLinks); MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout); MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval); MESHPARAMS_ADD(dot11MeshHWMPperrMinInterval); MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime); MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries); MESHPARAMS_ADD(path_refresh_time); MESHPARAMS_ADD(min_discovery_timeout); MESHPARAMS_ADD(dot11MeshHWMPRootMode); MESHPARAMS_ADD(dot11MeshHWMPRannInterval); MESHPARAMS_ADD(dot11MeshForwarding); MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol); MESHPARAMS_ADD(rssi_threshold); MESHPARAMS_ADD(ht_opmode); MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout); MESHPARAMS_ADD(dot11MeshHWMProotInterval); MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval); #undef MESHPARAMS_ADD } #endif static void add_files(struct ieee80211_sub_if_data *sdata) { if (!sdata->debugfs.dir) return; DEBUGFS_ADD(flags); DEBUGFS_ADD(state); DEBUGFS_ADD(channel_type); if (sdata->vif.type != NL80211_IFTYPE_MONITOR) add_common_files(sdata); switch (sdata->vif.type) { case NL80211_IFTYPE_MESH_POINT: #ifdef CONFIG_MAC80211_MESH add_mesh_files(sdata); add_mesh_stats(sdata); add_mesh_config(sdata); #endif break; case NL80211_IFTYPE_STATION: add_sta_files(sdata); break; case NL80211_IFTYPE_ADHOC: add_ibss_files(sdata); break; case NL80211_IFTYPE_AP: add_ap_files(sdata); break; case NL80211_IFTYPE_WDS: add_wds_files(sdata); break; default: break; } } void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata) { char buf[10+IFNAMSIZ]; sprintf(buf, "netdev:%s", sdata->name); sdata->debugfs.dir = debugfs_create_dir(buf, sdata->local->hw.wiphy->debugfsdir); if (sdata->debugfs.dir) sdata->debugfs.subdir_stations = debugfs_create_dir("stations", sdata->debugfs.dir); add_files(sdata); } void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata) { if (!sdata->debugfs.dir) return; debugfs_remove_recursive(sdata->debugfs.dir); sdata->debugfs.dir = NULL; } void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) { struct dentry *dir; char buf[10 + IFNAMSIZ]; dir = sdata->debugfs.dir; if (!dir) return; sprintf(buf, "netdev:%s", sdata->name); if (!debugfs_rename(dir->d_parent, dir, dir->d_parent, buf)) sdata_err(sdata, "debugfs: failed to rename debugfs dir to %s\n", buf); } compat-drivers-2012-09-18/net/mac80211/michael.h0000644000175000017500000000107512026211315020060 0ustar mcgrofmcgrof/* * Michael MIC implementation - optimized for TKIP MIC operations * Copyright 2002-2003, Instant802 Networks, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef MICHAEL_H #define MICHAEL_H #include #define MICHAEL_MIC_LEN 8 struct michael_mic_ctx { u32 l, r; }; void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, const u8 *data, size_t data_len, u8 *mic); #endif /* MICHAEL_H */ compat-drivers-2012-09-18/net/mac80211/sta_info.h0000644000175000017500000004504512026211315020265 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Devicescape Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef STA_INFO_H #define STA_INFO_H #include #include #include #include #include #include #include "key.h" /** * enum ieee80211_sta_info_flags - Stations flags * * These flags are used with &struct sta_info's @flags member, but * only indirectly with set_sta_flag() and friends. * * @WLAN_STA_AUTH: Station is authenticated. * @WLAN_STA_ASSOC: Station is associated. * @WLAN_STA_PS_STA: Station is in power-save mode * @WLAN_STA_AUTHORIZED: Station is authorized to send/receive traffic. * This bit is always checked so needs to be enabled for all stations * when virtual port control is not in use. * @WLAN_STA_SHORT_PREAMBLE: Station is capable of receiving short-preamble * frames. * @WLAN_STA_WME: Station is a QoS-STA. * @WLAN_STA_WDS: Station is one of our WDS peers. * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next * frame to this station is transmitted. * @WLAN_STA_MFP: Management frame protection is used with this STA. * @WLAN_STA_BLOCK_BA: Used to deny ADDBA requests (both TX and RX) * during suspend/resume and station removal. * @WLAN_STA_PS_DRIVER: driver requires keeping this station in * power-save mode logically to flush frames that might still * be in the queues * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping * station in power-save mode, reply when the driver unblocks. * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct * packets. This means the link is enabled. * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was * keeping station in power-save mode, reply when the driver * unblocks the station. * @WLAN_STA_SP: Station is in a service period, so don't try to * reply to other uAPSD trigger frames or PS-Poll. * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame. * @WLAN_STA_INSERTED: This station is inserted into the hash table. * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH, WLAN_STA_ASSOC, WLAN_STA_PS_STA, WLAN_STA_AUTHORIZED, WLAN_STA_SHORT_PREAMBLE, WLAN_STA_WME, WLAN_STA_WDS, WLAN_STA_CLEAR_PS_FILT, WLAN_STA_MFP, WLAN_STA_BLOCK_BA, WLAN_STA_PS_DRIVER, WLAN_STA_PSPOLL, WLAN_STA_TDLS_PEER, WLAN_STA_TDLS_PEER_AUTH, WLAN_STA_UAPSD, WLAN_STA_SP, WLAN_STA_4ADDR_EVENT, WLAN_STA_INSERTED, WLAN_STA_RATE_CONTROL, WLAN_STA_TOFFSET_KNOWN, }; #define STA_TID_NUM 16 #define ADDBA_RESP_INTERVAL HZ #define HT_AGG_MAX_RETRIES 15 #define HT_AGG_BURST_RETRIES 3 #define HT_AGG_RETRIES_PERIOD (15 * HZ) #define HT_AGG_STATE_DRV_READY 0 #define HT_AGG_STATE_RESPONSE_RECEIVED 1 #define HT_AGG_STATE_OPERATIONAL 2 #define HT_AGG_STATE_STOPPING 3 #define HT_AGG_STATE_WANT_START 4 #define HT_AGG_STATE_WANT_STOP 5 /** * struct tid_ampdu_tx - TID aggregation information (Tx). * * @rcu_head: rcu head for freeing structure * @session_timer: check if we keep Tx-ing on the TID (by timeout value) * @addba_resp_timer: timer for peer's response to addba request * @pending: pending frames queue -- use sta's spinlock to protect * @dialog_token: dialog token for aggregation session * @timeout: session timeout value to be filled in ADDBA requests * @state: session state (see above) * @last_tx: jiffies of last tx activity * @stop_initiator: initiator of a session stop * @tx_stop: TX DelBA frame when stopping * @buf_size: reorder buffer size at receiver * @failed_bar_ssn: ssn of the last failed BAR tx attempt * @bar_pending: BAR needs to be re-sent * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. * * The TX path can access it under RCU lock-free if, and * only if, the state has the flag %HT_AGG_STATE_OPERATIONAL * set. Otherwise, the TX path must also acquire the spinlock * and re-check the state, see comments in the tx code * touching it. */ struct tid_ampdu_tx { struct rcu_head rcu_head; struct timer_list session_timer; struct timer_list addba_resp_timer; struct sk_buff_head pending; unsigned long state; unsigned long last_tx; u16 timeout; u8 dialog_token; u8 stop_initiator; bool tx_stop; u8 buf_size; u16 failed_bar_ssn; bool bar_pending; }; /** * struct tid_ampdu_rx - TID aggregation information (Rx). * * @reorder_buf: buffer to reorder incoming aggregated MPDUs * @reorder_time: jiffies when skb was added * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) * @reorder_timer: releases expired frames from the reorder buffer. * @last_rx: jiffies of last rx activity * @head_seq_num: head sequence number in reordering buffer. * @stored_mpdu_num: number of MPDUs in reordering buffer * @ssn: Starting Sequence Number expected to be aggregated. * @buf_size: buffer size for incoming A-MPDUs * @timeout: reset timer value (in TUs). * @dialog_token: dialog token for aggregation session * @rcu_head: RCU head used for freeing this struct * @reorder_lock: serializes access to reorder buffer, see below. * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. * * The @reorder_lock is used to protect the members of this * struct, except for @timeout, @buf_size and @dialog_token, * which are constant across the lifetime of the struct (the * dialog token being used only for debugging). */ struct tid_ampdu_rx { struct rcu_head rcu_head; spinlock_t reorder_lock; struct sk_buff **reorder_buf; unsigned long *reorder_time; struct timer_list session_timer; struct timer_list reorder_timer; unsigned long last_rx; u16 head_seq_num; u16 stored_mpdu_num; u16 ssn; u16 buf_size; u16 timeout; u8 dialog_token; }; /** * struct sta_ampdu_mlme - STA aggregation information. * * @tid_rx: aggregation info for Rx per TID -- RCU protected * @tid_tx: aggregation info for Tx per TID * @tid_start_tx: sessions where start was requested * @addba_req_num: number of times addBA request has been sent. * @last_addba_req_time: timestamp of the last addBA request. * @dialog_token_allocator: dialog token enumerator for each new session; * @work: work struct for starting/stopping aggregation * @tid_rx_timer_expired: bitmap indicating on which TIDs the * RX timer expired until the work for it runs * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the * driver requested to close until the work for it runs * @mtx: mutex to protect all TX data (except non-NULL assignments * to tid_tx[idx], which are protected by the sta spinlock) */ struct sta_ampdu_mlme { struct mutex mtx; /* rx */ struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)]; unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)]; /* tx */ struct work_struct work; struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM]; struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM]; unsigned long last_addba_req_time[STA_TID_NUM]; u8 addba_req_num[STA_TID_NUM]; u8 dialog_token_allocator; }; /** * struct sta_info - STA information * * This structure collects information about a station that * mac80211 is communicating with. * * @list: global linked list entry * @hnext: hash table linked list pointer * @local: pointer to the global information * @sdata: virtual interface this station belongs to * @ptk: peer key negotiated with this station, if any * @gtk: group keys negotiated with this station, if any * @rate_ctrl: rate control algorithm reference * @rate_ctrl_priv: rate control private per-STA pointer * @last_tx_rate: rate used for last transmit, to report to userspace as * "the" transmit rate * @last_rx_rate_idx: rx status rate index of the last data packet * @last_rx_rate_flag: rx status flag of the last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. * @drv_unblock_wk: used for driver PS unblocking * @listen_interval: listen interval of this station, when we're acting as AP * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly * @ps_tx_buf: buffers (per AC) of frames to transmit to this station * when it leaves power saving state or polls * @tx_filtered: buffers (per AC) of frames we already tried to * transmit but were filtered by hardware due to STA having * entered power saving state, these are also delivered to * the station when it leaves powersave or polls for frames * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on * @rx_packets: Number of MSDUs received from this STA * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station * @last_rx: time (in jiffies) when last frame was received from this STA * @last_connected: time (in seconds) when a station got connected * @num_duplicates: number of duplicate frames received from this STA * @rx_fragments: number of received MPDUs * @rx_dropped: number of dropped MPDUs from this STA * @last_signal: signal of last received frame from this STA * @avg_signal: moving average of signal of received frames from this STA * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue) * @tx_filtered_count: number of frames the hardware filtered for this STA * @tx_retry_failed: number of frames that failed retry * @tx_retry_count: total number of retries for frames to this STA * @fail_avg: moving percentage of failed MSDUs * @tx_packets: number of RX/TX MSDUs * @tx_bytes: number of bytes transmitted to this STA * @tx_fragments: number of transmitted MPDUs * @tid_seq: per-TID sequence numbers for sending to this STA * @ampdu_mlme: A-MPDU state machine state * @timer_to_tid: identity mapping to ID timers * @llid: Local link ID * @plid: Peer link ID * @reason: Cancel reason on PLINK_HOLDING state * @plink_retries: Retries in establishment * @ignore_plink_timer: ignore the peer-link timer (used internally) * @plink_state: peer link state * @plink_timeout: timeout of peer link * @plink_timer: peer link watch timer * @plink_timer_was_running: used by suspend/resume to restore timers * @t_offset: timing offset relative to this host * @t_offset_setpoint: reference timing offset of this sta to be used when * calculating clockdrift * @ch_type: peer's channel type * @debugfs: debug filesystem info * @dead: set to true when sta is unlinked * @uploaded: set to true when sta is uploaded to the driver * @lost_packets: number of consecutive lost packets * @sta: station information we share with the driver * @sta_state: duplicates information about station state (for debug) * @beacon_loss_count: number of times beacon loss has triggered * @supports_40mhz: tracks whether the station advertised 40 MHz support * as we overwrite its HT parameters with the currently used value */ struct sta_info { /* General information, mostly static */ struct list_head list; struct sta_info __rcu *hnext; struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key __rcu *ptk; struct rate_control_ref *rate_ctrl; void *rate_ctrl_priv; spinlock_t lock; struct work_struct drv_unblock_wk; u16 listen_interval; bool dead; bool uploaded; enum ieee80211_sta_state sta_state; /* use the accessors defined below */ unsigned long _flags; /* * STA powersave frame queues, no more than the internal * locking required. */ struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; unsigned long driver_buffered_tids; /* Updated from RX path only, no locking requirements */ unsigned long rx_packets, rx_bytes; unsigned long wep_weak_iv_count; unsigned long last_rx; long last_connected; unsigned long num_duplicates; unsigned long rx_fragments; unsigned long rx_dropped; int last_signal; struct ewma avg_signal; /* Plus 1 for non-QoS frames */ __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1]; /* Updated from TX status path only, no locking requirements */ unsigned long tx_filtered_count; unsigned long tx_retry_failed, tx_retry_count; /* moving percentage of failed MSDUs */ unsigned int fail_avg; /* Updated from TX path only, no locking requirements */ unsigned long tx_packets; unsigned long tx_bytes; unsigned long tx_fragments; struct ieee80211_tx_rate last_tx_rate; int last_rx_rate_idx; int last_rx_rate_flag; u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; /* * Aggregation information, locked with lock. */ struct sta_ampdu_mlme ampdu_mlme; u8 timer_to_tid[STA_TID_NUM]; #ifdef CONFIG_MAC80211_MESH /* * Mesh peer link attributes * TODO: move to a sub-structure that is referenced with pointer? */ __le16 llid; __le16 plid; __le16 reason; u8 plink_retries; bool ignore_plink_timer; bool plink_timer_was_running; enum nl80211_plink_state plink_state; u32 plink_timeout; struct timer_list plink_timer; s64 t_offset; s64 t_offset_setpoint; enum nl80211_channel_type ch_type; #endif #ifdef CONFIG_MAC80211_DEBUGFS struct sta_info_debugfsdentries { struct dentry *dir; bool add_has_run; } debugfs; #endif unsigned int lost_packets; unsigned int beacon_loss_count; bool supports_40mhz; /* keep last! */ struct ieee80211_sta sta; }; static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta) { #ifdef CONFIG_MAC80211_MESH return sta->plink_state; #endif return NL80211_PLINK_LISTEN; } static inline void set_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { WARN_ON(flag == WLAN_STA_AUTH || flag == WLAN_STA_ASSOC || flag == WLAN_STA_AUTHORIZED); set_bit(flag, &sta->_flags); } static inline void clear_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { WARN_ON(flag == WLAN_STA_AUTH || flag == WLAN_STA_ASSOC || flag == WLAN_STA_AUTHORIZED); clear_bit(flag, &sta->_flags); } static inline int test_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { return test_bit(flag, &sta->_flags); } static inline int test_and_clear_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { WARN_ON(flag == WLAN_STA_AUTH || flag == WLAN_STA_ASSOC || flag == WLAN_STA_AUTHORIZED); return test_and_clear_bit(flag, &sta->_flags); } static inline int test_and_set_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { WARN_ON(flag == WLAN_STA_AUTH || flag == WLAN_STA_ASSOC || flag == WLAN_STA_AUTHORIZED); return test_and_set_bit(flag, &sta->_flags); } int sta_info_move_state(struct sta_info *sta, enum ieee80211_sta_state new_state); static inline void sta_info_pre_move_state(struct sta_info *sta, enum ieee80211_sta_state new_state) { int ret; WARN_ON_ONCE(test_sta_flag(sta, WLAN_STA_INSERTED)); ret = sta_info_move_state(sta, new_state); WARN_ON_ONCE(ret); } void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx); static inline struct tid_ampdu_tx * rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) { return rcu_dereference_protected(sta->ampdu_mlme.tid_tx[tid], lockdep_is_held(&sta->lock) || lockdep_is_held(&sta->ampdu_mlme.mtx)); } #define STA_HASH_SIZE 256 #define STA_HASH(sta) (sta[5]) /* Maximum number of frames to buffer per power saving station per AC */ #define STA_MAX_TX_BUFFER 64 /* Minimum buffered frame expiry time. If STA uses listen interval that is * smaller than this value, the minimum value here is used instead. */ #define STA_TX_BUFFER_EXPIRE (10 * HZ) /* How often station data is cleaned up (e.g., expiration of buffered frames) */ #define STA_INFO_CLEANUP_INTERVAL (10 * HZ) /* * Get a STA info, must be under RCU read lock. */ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr); struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); static inline void for_each_sta_info_type_check(struct ieee80211_local *local, const u8 *addr, struct sta_info *sta, struct sta_info *nxt) { } #define for_each_sta_info(local, _addr, _sta, nxt) \ for ( /* initialise loop */ \ _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ /* typecheck */ \ for_each_sta_info_type_check(local, (_addr), _sta, nxt),\ /* continue condition */ \ _sta; \ /* advance loop */ \ _sta = nxt, \ nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ ) \ /* compare address and run code only if it matches */ \ if (ether_addr_equal(_sta->sta.addr, (_addr))) /* * Get STA info by index, BROKEN! */ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, int idx); /* * Create a new STA info, caller owns returned structure * until sta_info_insert(). */ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, const u8 *addr, gfp_t gfp); void sta_info_free(struct ieee80211_local *local, struct sta_info *sta); /* * Insert STA info into hash table/list, returns zero or a * -EEXIST if (if the same MAC address is already present). * * Calling the non-rcu version makes the caller relinquish, * the _rcu version calls read_lock_rcu() and must be called * without it held. */ int sta_info_insert(struct sta_info *sta); int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU); int __must_check __sta_info_destroy(struct sta_info *sta); int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr); int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); int sta_info_flush(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time); void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); #endif /* STA_INFO_H */ compat-drivers-2012-09-18/net/mac80211/rc80211_pid_algo.c0000644000175000017500000003515012026211315021310 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2007, Mattias Nissler * Copyright 2007-2008, Stefano Brivio * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "rate.h" #include "mesh.h" #include "rc80211_pid.h" /* This is an implementation of a TX rate control algorithm that uses a PID * controller. Given a target failed frames rate, the controller decides about * TX rate changes to meet the target failed frames rate. * * The controller basically computes the following: * * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening) * * where * adj adjustment value that is used to switch TX rate (see below) * err current error: target vs. current failed frames percentage * last_err last error * err_avg average (i.e. poor man's integral) of recent errors * sharpening non-zero when fast response is needed (i.e. right after * association or no frames sent for a long time), heading * to zero over time * CP Proportional coefficient * CI Integral coefficient * CD Derivative coefficient * * CP, CI, CD are subject to careful tuning. * * The integral component uses a exponential moving average approach instead of * an actual sliding window. The advantage is that we don't need to keep an * array of the last N error values and computation is easier. * * Once we have the adj value, we map it to a rate by means of a learning * algorithm. This algorithm keeps the state of the percentual failed frames * difference between rates. The behaviour of the lowest available rate is kept * as a reference value, and every time we switch between two rates, we compute * the difference between the failed frames each rate exhibited. By doing so, * we compare behaviours which different rates exhibited in adjacent timeslices, * thus the comparison is minimally affected by external conditions. This * difference gets propagated to the whole set of measurements, so that the * reference is always the same. Periodically, we normalize this set so that * recent events weigh the most. By comparing the adj value with this set, we * avoid pejorative switches to lower rates and allow for switches to higher * rates if they behaved well. * * Note that for the computations we use a fixed-point representation to avoid * floating point arithmetic. Hence, all values are shifted left by * RC_PID_ARITH_SHIFT. */ /* Adjust the rate while ensuring that we won't switch to a lower rate if it * exhibited a worse failed frames behaviour and we'll choose the highest rate * whose failed frames behaviour is not worse than the one of the original rate * target. While at it, check that the new rate is valid. */ static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, struct rc_pid_sta_info *spinfo, int adj, struct rc_pid_rateinfo *rinfo) { int cur_sorted, new_sorted, probe, tmp, n_bitrates, band; int cur = spinfo->txrate_idx; band = sband->band; n_bitrates = sband->n_bitrates; /* Map passed arguments to sorted values. */ cur_sorted = rinfo[cur].rev_index; new_sorted = cur_sorted + adj; /* Check limits. */ if (new_sorted < 0) new_sorted = rinfo[0].rev_index; else if (new_sorted >= n_bitrates) new_sorted = rinfo[n_bitrates - 1].rev_index; tmp = new_sorted; if (adj < 0) { /* Ensure that the rate decrease isn't disadvantageous. */ for (probe = cur_sorted; probe >= new_sorted; probe--) if (rinfo[probe].diff <= rinfo[cur_sorted].diff && rate_supported(sta, band, rinfo[probe].index)) tmp = probe; } else { /* Look for rate increase with zero (or below) cost. */ for (probe = new_sorted + 1; probe < n_bitrates; probe++) if (rinfo[probe].diff <= rinfo[new_sorted].diff && rate_supported(sta, band, rinfo[probe].index)) tmp = probe; } /* Fit the rate found to the nearest supported rate. */ do { if (rate_supported(sta, band, rinfo[tmp].index)) { spinfo->txrate_idx = rinfo[tmp].index; break; } if (adj < 0) tmp--; else tmp++; } while (tmp < n_bitrates && tmp >= 0); #ifdef CONFIG_MAC80211_DEBUGFS rate_control_pid_event_rate_change(&spinfo->events, spinfo->txrate_idx, sband->bitrates[spinfo->txrate_idx].bitrate); #endif } /* Normalize the failed frames per-rate differences. */ static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l) { int i, norm_offset = pinfo->norm_offset; struct rc_pid_rateinfo *r = pinfo->rinfo; if (r[0].diff > norm_offset) r[0].diff -= norm_offset; else if (r[0].diff < -norm_offset) r[0].diff += norm_offset; for (i = 0; i < l - 1; i++) if (r[i + 1].diff > r[i].diff + norm_offset) r[i + 1].diff -= norm_offset; else if (r[i + 1].diff <= r[i].diff) r[i + 1].diff += norm_offset; } static void rate_control_pid_sample(struct rc_pid_info *pinfo, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, struct rc_pid_sta_info *spinfo) { struct rc_pid_rateinfo *rinfo = pinfo->rinfo; u32 pf; s32 err_avg; u32 err_prop; u32 err_int; u32 err_der; int adj, i, j, tmp; unsigned long period; /* In case nothing happened during the previous control interval, turn * the sharpening factor on. */ period = msecs_to_jiffies(pinfo->sampling_period); if (jiffies - spinfo->last_sample > 2 * period) spinfo->sharp_cnt = pinfo->sharpen_duration; spinfo->last_sample = jiffies; /* This should never happen, but in case, we assume the old sample is * still a good measurement and copy it. */ if (unlikely(spinfo->tx_num_xmit == 0)) pf = spinfo->last_pf; else pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit; spinfo->tx_num_xmit = 0; spinfo->tx_num_failed = 0; /* If we just switched rate, update the rate behaviour info. */ if (pinfo->oldrate != spinfo->txrate_idx) { i = rinfo[pinfo->oldrate].rev_index; j = rinfo[spinfo->txrate_idx].rev_index; tmp = (pf - spinfo->last_pf); tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT); rinfo[j].diff = rinfo[i].diff + tmp; pinfo->oldrate = spinfo->txrate_idx; } rate_control_pid_normalize(pinfo, sband->n_bitrates); /* Compute the proportional, integral and derivative errors. */ err_prop = (pinfo->target - pf) << RC_PID_ARITH_SHIFT; err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift; spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop; err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift; err_der = (pf - spinfo->last_pf) * (1 + pinfo->sharpen_factor * spinfo->sharp_cnt); spinfo->last_pf = pf; if (spinfo->sharp_cnt) spinfo->sharp_cnt--; #ifdef CONFIG_MAC80211_DEBUGFS rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int, err_der); #endif /* Compute the controller output. */ adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i + err_der * pinfo->coeff_d); adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT); /* Change rate. */ if (adj) rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo); } static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { struct rc_pid_info *pinfo = priv; struct rc_pid_sta_info *spinfo = priv_sta; unsigned long period; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (!spinfo) return; /* Ignore all frames that were sent with a different rate than the rate * we currently advise mac80211 to use. */ if (info->status.rates[0].idx != spinfo->txrate_idx) return; spinfo->tx_num_xmit++; #ifdef CONFIG_MAC80211_DEBUGFS rate_control_pid_event_tx_status(&spinfo->events, info); #endif /* We count frames that totally failed to be transmitted as two bad * frames, those that made it out but had some retries as one good and * one bad frame. */ if (!(info->flags & IEEE80211_TX_STAT_ACK)) { spinfo->tx_num_failed += 2; spinfo->tx_num_xmit++; } else if (info->status.rates[0].count > 1) { spinfo->tx_num_failed++; spinfo->tx_num_xmit++; } /* Update PID controller state. */ period = msecs_to_jiffies(pinfo->sampling_period); if (time_after(jiffies, spinfo->last_sample + period)) rate_control_pid_sample(pinfo, sband, sta, spinfo); } static void rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_supported_band *sband = txrc->sband; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rc_pid_sta_info *spinfo = priv_sta; int rateidx; if (txrc->rts) info->control.rates[0].count = txrc->hw->conf.long_frame_max_tx_count; else info->control.rates[0].count = txrc->hw->conf.short_frame_max_tx_count; /* Send management frames and NO_ACK data using lowest rate. */ if (rate_control_send_low(sta, priv_sta, txrc)) return; rateidx = spinfo->txrate_idx; if (rateidx >= sband->n_bitrates) rateidx = sband->n_bitrates - 1; info->control.rates[0].idx = rateidx; #ifdef CONFIG_MAC80211_DEBUGFS rate_control_pid_event_tx_rate(&spinfo->events, rateidx, sband->bitrates[rateidx].bitrate); #endif } static void rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { struct rc_pid_sta_info *spinfo = priv_sta; struct rc_pid_info *pinfo = priv; struct rc_pid_rateinfo *rinfo = pinfo->rinfo; int i, j, tmp; bool s; /* TODO: This routine should consider using RSSI from previous packets * as we need to have IEEE 802.1X auth succeed immediately after assoc.. * Until that method is implemented, we will use the lowest supported * rate as a workaround. */ /* Sort the rates. This is optimized for the most common case (i.e. * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed * mapping too. */ for (i = 0; i < sband->n_bitrates; i++) { rinfo[i].index = i; rinfo[i].rev_index = i; if (RC_PID_FAST_START) rinfo[i].diff = 0; else rinfo[i].diff = i * pinfo->norm_offset; } for (i = 1; i < sband->n_bitrates; i++) { s = false; for (j = 0; j < sband->n_bitrates - i; j++) if (unlikely(sband->bitrates[rinfo[j].index].bitrate > sband->bitrates[rinfo[j + 1].index].bitrate)) { tmp = rinfo[j].index; rinfo[j].index = rinfo[j + 1].index; rinfo[j + 1].index = tmp; rinfo[rinfo[j].index].rev_index = j; rinfo[rinfo[j + 1].index].rev_index = j + 1; s = true; } if (!s) break; } spinfo->txrate_idx = rate_lowest_index(sband, sta); } static void *rate_control_pid_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { struct rc_pid_info *pinfo; struct rc_pid_rateinfo *rinfo; struct ieee80211_supported_band *sband; int i, max_rates = 0; #ifdef CONFIG_MAC80211_DEBUGFS struct rc_pid_debugfs_entries *de; #endif pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC); if (!pinfo) return NULL; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sband = hw->wiphy->bands[i]; if (sband && sband->n_bitrates > max_rates) max_rates = sband->n_bitrates; } rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC); if (!rinfo) { kfree(pinfo); return NULL; } pinfo->target = RC_PID_TARGET_PF; pinfo->sampling_period = RC_PID_INTERVAL; pinfo->coeff_p = RC_PID_COEFF_P; pinfo->coeff_i = RC_PID_COEFF_I; pinfo->coeff_d = RC_PID_COEFF_D; pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT; pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR; pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION; pinfo->norm_offset = RC_PID_NORM_OFFSET; pinfo->rinfo = rinfo; pinfo->oldrate = 0; #ifdef CONFIG_MAC80211_DEBUGFS de = &pinfo->dentries; de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR, debugfsdir, &pinfo->target); de->sampling_period = debugfs_create_u32("sampling_period", S_IRUSR | S_IWUSR, debugfsdir, &pinfo->sampling_period); de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR, debugfsdir, (u32 *)&pinfo->coeff_p); de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR, debugfsdir, (u32 *)&pinfo->coeff_i); de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR, debugfsdir, (u32 *)&pinfo->coeff_d); de->smoothing_shift = debugfs_create_u32("smoothing_shift", S_IRUSR | S_IWUSR, debugfsdir, &pinfo->smoothing_shift); de->sharpen_factor = debugfs_create_u32("sharpen_factor", S_IRUSR | S_IWUSR, debugfsdir, &pinfo->sharpen_factor); de->sharpen_duration = debugfs_create_u32("sharpen_duration", S_IRUSR | S_IWUSR, debugfsdir, &pinfo->sharpen_duration); de->norm_offset = debugfs_create_u32("norm_offset", S_IRUSR | S_IWUSR, debugfsdir, &pinfo->norm_offset); #endif return pinfo; } static void rate_control_pid_free(void *priv) { struct rc_pid_info *pinfo = priv; #ifdef CONFIG_MAC80211_DEBUGFS struct rc_pid_debugfs_entries *de = &pinfo->dentries; debugfs_remove(de->norm_offset); debugfs_remove(de->sharpen_duration); debugfs_remove(de->sharpen_factor); debugfs_remove(de->smoothing_shift); debugfs_remove(de->coeff_d); debugfs_remove(de->coeff_i); debugfs_remove(de->coeff_p); debugfs_remove(de->sampling_period); debugfs_remove(de->target); #endif kfree(pinfo->rinfo); kfree(pinfo); } static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) { struct rc_pid_sta_info *spinfo; spinfo = kzalloc(sizeof(*spinfo), gfp); if (spinfo == NULL) return NULL; spinfo->last_sample = jiffies; #ifdef CONFIG_MAC80211_DEBUGFS spin_lock_init(&spinfo->events.lock); init_waitqueue_head(&spinfo->events.waitqueue); #endif return spinfo; } static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) { kfree(priv_sta); } static struct rate_control_ops mac80211_rcpid = { .name = "pid", .tx_status = rate_control_pid_tx_status, .get_rate = rate_control_pid_get_rate, .rate_init = rate_control_pid_rate_init, .alloc = rate_control_pid_alloc, .free = rate_control_pid_free, .alloc_sta = rate_control_pid_alloc_sta, .free_sta = rate_control_pid_free_sta, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = rate_control_pid_add_sta_debugfs, .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs, #endif }; int __init rc80211_pid_init(void) { return ieee80211_rate_control_register(&mac80211_rcpid); } void rc80211_pid_exit(void) { ieee80211_rate_control_unregister(&mac80211_rcpid); } compat-drivers-2012-09-18/net/mac80211/wep.c0000644000175000017500000002301512026211315017242 0ustar mcgrofmcgrof/* * Software WEP encryption implementation * Copyright 2002, Jouni Malinen * Copyright 2003, Instant802 Networks, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "wep.h" int ieee80211_wep_init(struct ieee80211_local *local) { /* start WEP IV from a random value */ get_random_bytes(&local->wep_iv, WEP_IV_LEN); local->wep_tx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(local->wep_tx_tfm)) { local->wep_rx_tfm = ERR_PTR(-EINVAL); return PTR_ERR(local->wep_tx_tfm); } local->wep_rx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(local->wep_rx_tfm)) { crypto_free_cipher(local->wep_tx_tfm); local->wep_tx_tfm = ERR_PTR(-EINVAL); return PTR_ERR(local->wep_rx_tfm); } return 0; } void ieee80211_wep_free(struct ieee80211_local *local) { if (!IS_ERR(local->wep_tx_tfm)) crypto_free_cipher(local->wep_tx_tfm); if (!IS_ERR(local->wep_rx_tfm)) crypto_free_cipher(local->wep_rx_tfm); } static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen) { /* * Fluhrer, Mantin, and Shamir have reported weaknesses in the * key scheduling algorithm of RC4. At least IVs (KeyByte + 3, * 0xff, N) can be used to speedup attacks, so avoid using them. */ if ((iv & 0xff00) == 0xff00) { u8 B = (iv >> 16) & 0xff; if (B >= 3 && B < 3 + keylen) return true; } return false; } static void ieee80211_wep_get_iv(struct ieee80211_local *local, int keylen, int keyidx, u8 *iv) { local->wep_iv++; if (ieee80211_wep_weak_iv(local->wep_iv, keylen)) local->wep_iv += 0x0100; if (!iv) return; *iv++ = (local->wep_iv >> 16) & 0xff; *iv++ = (local->wep_iv >> 8) & 0xff; *iv++ = local->wep_iv & 0xff; *iv++ = keyidx << 6; } static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, struct sk_buff *skb, int keylen, int keyidx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); unsigned int hdrlen; u8 *newhdr; hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN || skb_headroom(skb) < WEP_IV_LEN)) return NULL; hdrlen = ieee80211_hdrlen(hdr->frame_control); newhdr = skb_push(skb, WEP_IV_LEN); memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); /* the HW only needs room for the IV, but not the actual IV */ if (info->control.hw_key && (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) return newhdr + hdrlen; skb_set_network_header(skb, skb_network_offset(skb) + WEP_IV_LEN); ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen); return newhdr + hdrlen; } static void ieee80211_wep_remove_iv(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_key *key) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; unsigned int hdrlen; hdrlen = ieee80211_hdrlen(hdr->frame_control); memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); skb_pull(skb, WEP_IV_LEN); } /* Perform WEP encryption using given key. data buffer must have tailroom * for 4-byte ICV. data_len must not include this ICV. Note: this function * does _not_ add IV. data = RC4(data | CRC32(data)) */ int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len) { __le32 icv; int i; if (IS_ERR(tfm)) return -1; icv = cpu_to_le32(~crc32_le(~0, data, data_len)); put_unaligned(icv, (__le32 *)(data + data_len)); crypto_cipher_setkey(tfm, rc4key, klen); for (i = 0; i < data_len + WEP_ICV_LEN; i++) crypto_cipher_encrypt_one(tfm, data + i, data + i); return 0; } /* Perform WEP encryption on given skb. 4 bytes of extra space (IV) in the * beginning of the buffer 4 bytes of extra space (ICV) in the end of the * buffer will be added. Both IV and ICV will be transmitted, so the * payload length increases with 8 bytes. * * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) */ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, const u8 *key, int keylen, int keyidx) { u8 *iv; size_t len; u8 rc4key[3 + WLAN_KEY_LEN_WEP104]; iv = ieee80211_wep_add_iv(local, skb, keylen, keyidx); if (!iv) return -1; len = skb->len - (iv + WEP_IV_LEN - skb->data); /* Prepend 24-bit IV to RC4 key */ memcpy(rc4key, iv, 3); /* Copy rest of the WEP key (the secret part) */ memcpy(rc4key + 3, key, keylen); /* Add room for ICV */ skb_put(skb, WEP_ICV_LEN); return ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, keylen + 3, iv + WEP_IV_LEN, len); } /* Perform WEP decryption using given key. data buffer includes encrypted * payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV. * Return 0 on success and -1 on ICV mismatch. */ int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len) { __le32 crc; int i; if (IS_ERR(tfm)) return -1; crypto_cipher_setkey(tfm, rc4key, klen); for (i = 0; i < data_len + WEP_ICV_LEN; i++) crypto_cipher_decrypt_one(tfm, data + i, data + i); crc = cpu_to_le32(~crc32_le(~0, data, data_len)); if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0) /* ICV mismatch */ return -1; return 0; } /* Perform WEP decryption on given skb. Buffer includes whole WEP part of * the frame: IV (4 bytes), encrypted payload (including SNAP header), * ICV (4 bytes). skb->len includes both IV and ICV. * * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload * is moved to the beginning of the skb and skb length will be reduced. */ static int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_key *key) { u32 klen; u8 rc4key[3 + WLAN_KEY_LEN_WEP104]; u8 keyidx; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; unsigned int hdrlen; size_t len; int ret = 0; if (!ieee80211_has_protected(hdr->frame_control)) return -1; hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < hdrlen + WEP_IV_LEN + WEP_ICV_LEN) return -1; len = skb->len - hdrlen - WEP_IV_LEN - WEP_ICV_LEN; keyidx = skb->data[hdrlen + 3] >> 6; if (!key || keyidx != key->conf.keyidx) return -1; klen = 3 + key->conf.keylen; /* Prepend 24-bit IV to RC4 key */ memcpy(rc4key, skb->data + hdrlen, 3); /* Copy rest of the WEP key (the secret part) */ memcpy(rc4key + 3, key->conf.key, key->conf.keylen); if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen, skb->data + hdrlen + WEP_IV_LEN, len)) ret = -1; /* Trim ICV */ skb_trim(skb, skb->len - WEP_ICV_LEN); /* Remove IV */ memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); skb_pull(skb, WEP_IV_LEN); return ret; } static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; unsigned int hdrlen; u8 *ivpos; u32 iv; hdrlen = ieee80211_hdrlen(hdr->frame_control); ivpos = skb->data + hdrlen; iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; return ieee80211_wep_weak_iv(iv, key->conf.keylen); } ieee80211_rx_result ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx) { struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 fc = hdr->frame_control; if (!ieee80211_is_data(fc) && !ieee80211_is_auth(fc)) return RX_CONTINUE; if (!(status->flag & RX_FLAG_DECRYPTED)) { if (skb_linearize(rx->skb)) return RX_DROP_UNUSABLE; if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) rx->sta->wep_weak_iv_count++; if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) return RX_DROP_UNUSABLE; } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) { if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + WEP_IV_LEN)) return RX_DROP_UNUSABLE; if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) rx->sta->wep_weak_iv_count++; ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); /* remove ICV */ if (pskb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN)) return RX_DROP_UNUSABLE; } return RX_CONTINUE; } static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *hw_key = info->control.hw_key; if (!hw_key) { if (ieee80211_wep_encrypt(tx->local, skb, tx->key->conf.key, tx->key->conf.keylen, tx->key->conf.keyidx)) return -1; } else if ((hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) || (hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { if (!ieee80211_wep_add_iv(tx->local, skb, tx->key->conf.keylen, tx->key->conf.keyidx)) return -1; } return 0; } ieee80211_tx_result ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb; ieee80211_tx_set_protected(tx); skb_queue_walk(&tx->skbs, skb) { if (wep_encrypt_skb(tx, skb) < 0) { I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); return TX_DROP; } } return TX_CONTINUE; } compat-drivers-2012-09-18/net/mac80211/mesh.h0000644000175000017500000003220512026211315017411 0ustar mcgrofmcgrof/* * Copyright (c) 2008, 2009 open80211s Ltd. * Authors: Luis Carlos Cobo * Javier Cardona * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef IEEE80211S_H #define IEEE80211S_H #include #include #include "ieee80211_i.h" /* Data structures */ /** * enum mesh_config_capab_flags - mesh config IE capability flags * * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish * additional mesh peerings with other mesh STAs * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing */ enum mesh_config_capab_flags { MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0), MESHCONF_CAPAB_FORWARDING = BIT(3), MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5), }; /** * enum mesh_path_flags - mac80211 mesh path flags * * * * @MESH_PATH_ACTIVE: the mesh path can be used for forwarding * @MESH_PATH_RESOLVING: the discovery process is running for this mesh path * @MESH_PATH_SN_VALID: the mesh path contains a valid destination sequence * number * @MESH_PATH_FIXED: the mesh path has been manually set and should not be * modified * @MESH_PATH_RESOLVED: the mesh path can has been resolved * @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination * already queued up, waiting for the discovery process to start. * * MESH_PATH_RESOLVED is used by the mesh path timer to * decide when to stop or cancel the mesh path discovery. */ enum mesh_path_flags { MESH_PATH_ACTIVE = BIT(0), MESH_PATH_RESOLVING = BIT(1), MESH_PATH_SN_VALID = BIT(2), MESH_PATH_FIXED = BIT(3), MESH_PATH_RESOLVED = BIT(4), MESH_PATH_REQ_QUEUED = BIT(5), }; /** * enum mesh_deferred_task_flags - mac80211 mesh deferred tasks * * * * @MESH_WORK_HOUSEKEEPING: run the periodic mesh housekeeping tasks * @MESH_WORK_GROW_MPATH_TABLE: the mesh path table is full and needs * to grow. * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to * grow * @MESH_WORK_ROOT: the mesh root station needs to send a frame * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other * mesh nodes */ enum mesh_deferred_task_flags { MESH_WORK_HOUSEKEEPING, MESH_WORK_GROW_MPATH_TABLE, MESH_WORK_GROW_MPP_TABLE, MESH_WORK_ROOT, MESH_WORK_DRIFT_ADJUST, }; /** * struct mesh_path - mac80211 mesh path structure * * @dst: mesh path destination mac address * @sdata: mesh subif * @next_hop: mesh neighbor to which frames for this destination will be * forwarded * @timer: mesh path discovery timer * @frame_queue: pending queue for frames sent to this destination while the * path is unresolved * @sn: target sequence number * @metric: current metric to this destination * @hop_count: hops to destination * @exp_time: in jiffies, when the path will expire or when it expired * @discovery_timeout: timeout (lapse in jiffies) used for the last discovery * retry * @discovery_retries: number of discovery retries * @flags: mesh path flags, as specified on &enum mesh_path_flags * @state_lock: mesh path state lock used to protect changes to the * mpath itself. No need to take this lock when adding or removing * an mpath to a hash bucket on a path table. * @rann_snd_addr: the RANN sender address * @rann_metric: the aggregated path metric towards the root node * @last_preq_to_root: Timestamp of last PREQ sent to root * @is_root: the destination station of this path is a root node * @is_gate: the destination station of this path is a mesh gate * * * The combination of dst and sdata is unique in the mesh path table. Since the * next_hop STA is only protected by RCU as well, deleting the STA must also * remove/substitute the mesh_path structure and wait until that is no longer * reachable before destroying the STA completely. */ struct mesh_path { u8 dst[ETH_ALEN]; u8 mpp[ETH_ALEN]; /* used for MPP or MAP */ struct ieee80211_sub_if_data *sdata; struct sta_info __rcu *next_hop; struct timer_list timer; struct sk_buff_head frame_queue; struct rcu_head rcu; u32 sn; u32 metric; u8 hop_count; unsigned long exp_time; u32 discovery_timeout; u8 discovery_retries; enum mesh_path_flags flags; spinlock_t state_lock; u8 rann_snd_addr[ETH_ALEN]; u32 rann_metric; unsigned long last_preq_to_root; bool is_root; bool is_gate; }; /** * struct mesh_table * * @hash_buckets: array of hash buckets of the table * @hashwlock: array of locks to protect write operations, one per bucket * @hash_mask: 2^size_order - 1, used to compute hash idx * @hash_rnd: random value used for hash computations * @entries: number of entries in the table * @free_node: function to free nodes of the table * @copy_node: function to copy nodes of the table * @size_order: determines size of the table, there will be 2^size_order hash * buckets * @mean_chain_len: maximum average length for the hash buckets' list, if it is * reached, the table will grow * @known_gates: list of known mesh gates and their mpaths by the station. The * gate's mpath may or may not be resolved and active. * * rcu_head: RCU head to free the table */ struct mesh_table { /* Number of buckets will be 2^N */ struct hlist_head *hash_buckets; spinlock_t *hashwlock; /* One per bucket, for add/del */ unsigned int hash_mask; /* (2^size_order) - 1 */ __u32 hash_rnd; /* Used for hash generation */ atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */ void (*free_node) (struct hlist_node *p, bool free_leafs); int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); int size_order; int mean_chain_len; struct hlist_head *known_gates; spinlock_t gates_lock; struct rcu_head rcu_head; }; /* Recent multicast cache */ /* RMC_BUCKETS must be a power of 2, maximum 256 */ #define RMC_BUCKETS 256 #define RMC_QUEUE_MAX_LEN 4 #define RMC_TIMEOUT (3 * HZ) /** * struct rmc_entry - entry in the Recent Multicast Cache * * @seqnum: mesh sequence number of the frame * @exp_time: expiration time of the entry, in jiffies * @sa: source address of the frame * * The Recent Multicast Cache keeps track of the latest multicast frames that * have been received by a mesh interface and discards received multicast frames * that are found in the cache. */ struct rmc_entry { struct list_head list; u32 seqnum; unsigned long exp_time; u8 sa[ETH_ALEN]; }; struct mesh_rmc { struct rmc_entry bucket[RMC_BUCKETS]; u32 idx_mask; }; #define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) #define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ #define MESH_PATH_EXPIRE (600 * HZ) /* Default maximum number of plinks per interface */ #define MESH_MAX_PLINKS 256 /* Maximum number of paths per interface */ #define MESH_MAX_MPATHS 1024 /* Number of frames buffered per destination for unresolved destinations */ #define MESH_FRAME_QUEUE_LEN 10 /* Public interfaces */ /* Various */ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, const u8 *da, const u8 *sa); int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, struct ieee80211_sub_if_data *sdata, char *addr4or5, char *addr6); int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr, struct ieee80211_sub_if_data *sdata); bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *ie); void mesh_ids_set_default(struct ieee80211_if_mesh *mesh); void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_ht_cap_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_ht_oper_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); void ieee80211s_update_metric(struct ieee80211_local *local, struct sta_info *sta, struct sk_buff *skb); void ieee80211s_stop(void); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); /* Mesh paths */ int mesh_nexthop_lookup(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_nexthop_resolve(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata); struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata); int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata); struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata); void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); void mesh_path_expire(struct ieee80211_sub_if_data *sdata); void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); int mesh_path_add_gate(struct mesh_path *mpath); int mesh_path_send_to_gates(struct mesh_path *mpath); int mesh_gate_num(struct ieee80211_sub_if_data *sdata); /* Mesh plinks */ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, u8 *hw_addr, struct ieee802_11_elems *ie); bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); void mesh_plink_broken(struct sta_info *sta); void mesh_plink_deactivate(struct sta_info *sta); int mesh_plink_open(struct sta_info *sta); void mesh_plink_block(struct sta_info *sta); void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status); /* Private interfaces */ /* Mesh tables */ void mesh_mpath_table_grow(void); void mesh_mpp_table_grow(void); /* Mesh paths */ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode, const u8 *ra, struct ieee80211_sub_if_data *sdata); void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta); void mesh_path_flush_pending(struct mesh_path *mpath); void mesh_path_tx_pending(struct mesh_path *mpath); int mesh_pathtbl_init(void); void mesh_pathtbl_unregister(void); int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata); void mesh_path_timer(unsigned long data); void mesh_path_flush_by_nexthop(struct sta_info *sta); void mesh_path_discard_frame(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); void mesh_path_restart(struct ieee80211_sub_if_data *sdata); void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); extern int mesh_paths_generation; #ifdef CONFIG_MAC80211_MESH extern int mesh_allocated; static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata) { return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks - atomic_read(&sdata->u.mesh.mshstats.estab_plinks); } static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata) { return (min_t(long, mesh_plink_free_count(sdata), MESH_MAX_PLINKS - sdata->local->num_sta)) > 0; } static inline void mesh_path_activate(struct mesh_path *mpath) { mpath->flags |= MESH_PATH_ACTIVE | MESH_PATH_RESOLVED; } static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) { return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP; } void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata); void mesh_plink_quiesce(struct sta_info *sta); void mesh_plink_restart(struct sta_info *sta); void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata); void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata); #else #define mesh_allocated 0 static inline void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) {} static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) {} static inline void mesh_plink_quiesce(struct sta_info *sta) {} static inline void mesh_plink_restart(struct sta_info *sta) {} static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) { return false; } static inline void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) {} #endif #endif /* IEEE80211S_H */ compat-drivers-2012-09-18/net/mac80211/chan.c0000644000175000017500000000576112026211315017370 0ustar mcgrofmcgrof/* * mac80211 - channel management */ #include #include #include "ieee80211_i.h" static enum ieee80211_chan_mode __ieee80211_get_channel_mode(struct ieee80211_local *local, struct ieee80211_sub_if_data *ignore) { struct ieee80211_sub_if_data *sdata; lockdep_assert_held(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (sdata == ignore) continue; if (!ieee80211_sdata_running(sdata)) continue; switch (sdata->vif.type) { case NL80211_IFTYPE_MONITOR: continue; case NL80211_IFTYPE_STATION: if (!sdata->u.mgd.associated) continue; break; case NL80211_IFTYPE_ADHOC: if (!sdata->u.ibss.ssid_len) continue; if (!sdata->u.ibss.fixed_channel) return CHAN_MODE_HOPPING; break; case NL80211_IFTYPE_AP_VLAN: /* will also have _AP interface */ continue; case NL80211_IFTYPE_AP: if (!sdata->u.ap.beacon) continue; break; case NL80211_IFTYPE_MESH_POINT: if (!sdata->wdev.mesh_id_len) continue; break; default: break; } return CHAN_MODE_FIXED; } return CHAN_MODE_UNDEFINED; } enum ieee80211_chan_mode ieee80211_get_channel_mode(struct ieee80211_local *local, struct ieee80211_sub_if_data *ignore) { enum ieee80211_chan_mode mode; mutex_lock(&local->iflist_mtx); mode = __ieee80211_get_channel_mode(local, ignore); mutex_unlock(&local->iflist_mtx); return mode; } bool ieee80211_set_channel_type(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_channel_type chantype) { struct ieee80211_sub_if_data *tmp; enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; bool result; mutex_lock(&local->iflist_mtx); list_for_each_entry(tmp, &local->interfaces, list) { if (tmp == sdata) continue; if (!ieee80211_sdata_running(tmp)) continue; switch (tmp->vif.bss_conf.channel_type) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: if (superchan > tmp->vif.bss_conf.channel_type) break; superchan = tmp->vif.bss_conf.channel_type; break; case NL80211_CHAN_HT40PLUS: WARN_ON(superchan == NL80211_CHAN_HT40MINUS); superchan = NL80211_CHAN_HT40PLUS; break; case NL80211_CHAN_HT40MINUS: WARN_ON(superchan == NL80211_CHAN_HT40PLUS); superchan = NL80211_CHAN_HT40MINUS; break; } } switch (superchan) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: /* * allow any change that doesn't go to no-HT * (if it already is no-HT no change is needed) */ if (chantype == NL80211_CHAN_NO_HT) break; superchan = chantype; break; case NL80211_CHAN_HT40PLUS: case NL80211_CHAN_HT40MINUS: /* allow smaller bandwidth and same */ if (chantype == NL80211_CHAN_NO_HT) break; if (chantype == NL80211_CHAN_HT20) break; if (superchan == chantype) break; result = false; goto out; } local->_oper_channel_type = superchan; if (sdata) sdata->vif.bss_conf.channel_type = chantype; result = true; out: mutex_unlock(&local->iflist_mtx); return result; } compat-drivers-2012-09-18/net/mac80211/rc80211_minstrel.h0000644000175000017500000000451112026211315021371 0ustar mcgrofmcgrof/* * Copyright (C) 2008 Felix Fietkau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef __RC_MINSTREL_H #define __RC_MINSTREL_H struct minstrel_rate { int bitrate; int rix; unsigned int perfect_tx_time; unsigned int ack_time; int sample_limit; unsigned int retry_count; unsigned int retry_count_cts; unsigned int retry_count_rtscts; unsigned int adjusted_retry_count; u32 success; u32 attempts; u32 last_attempts; u32 last_success; /* parts per thousand */ u32 cur_prob; u32 probability; /* per-rate throughput */ u32 cur_tp; u64 succ_hist; u64 att_hist; }; struct minstrel_sta_info { unsigned long stats_update; unsigned int sp_ack_dur; unsigned int rate_avg; unsigned int lowest_rix; unsigned int max_tp_rate; unsigned int max_tp_rate2; unsigned int max_prob_rate; unsigned int packet_count; unsigned int sample_count; int sample_deferred; unsigned int sample_idx; unsigned int sample_column; int n_rates; struct minstrel_rate *r; bool prev_sample; /* sampling table */ u8 *sample_table; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *dbg_stats; #endif }; struct minstrel_priv { struct ieee80211_hw *hw; bool has_mrr; unsigned int cw_min; unsigned int cw_max; unsigned int max_retry; unsigned int ewma_level; unsigned int segment_size; unsigned int update_interval; unsigned int lookaround_rate; unsigned int lookaround_rate_mrr; #ifdef CONFIG_MAC80211_DEBUGFS /* * enable fixed rate processing per RC * - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx * - write -1 to enable RC processing again * - setting will be applied on next update */ u32 fixed_rate_idx; struct dentry *dbg_fixed_rate; #endif }; struct minstrel_debugfs_info { size_t len; char buf[]; }; extern struct rate_control_ops mac80211_minstrel; void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); /* debugfs */ int minstrel_stats_open(struct inode *inode, struct file *file); ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); int minstrel_stats_release(struct inode *inode, struct file *file); #endif compat-drivers-2012-09-18/net/mac80211/rc80211_minstrel_ht.c0000644000175000017500000005603512026211315022067 0ustar mcgrofmcgrof/* * Copyright (C) 2010 Felix Fietkau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include "rate.h" #include "rc80211_minstrel.h" #include "rc80211_minstrel_ht.h" #define AVG_PKT_SIZE 1200 #define SAMPLE_COLUMNS 10 #define EWMA_LEVEL 75 /* Number of bits for an average sized packet */ #define MCS_NBITS (AVG_PKT_SIZE << 3) /* Number of symbols for a packet with (bps) bits per symbol */ #define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps)) /* Transmission time for a packet containing (syms) symbols */ #define MCS_SYMBOL_TIME(sgi, syms) \ (sgi ? \ ((syms) * 18 + 4) / 5 : /* syms * 3.6 us */ \ (syms) << 2 /* syms * 4 us */ \ ) /* Transmit duration for the raw data part of an average sized packet */ #define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) /* * Define group sort order: HT40 -> SGI -> #streams */ #define GROUP_IDX(_streams, _sgi, _ht40) \ MINSTREL_MAX_STREAMS * 2 * _ht40 + \ MINSTREL_MAX_STREAMS * _sgi + \ _streams - 1 /* MCS rate information for an MCS group */ #define MCS_GROUP(_streams, _sgi, _ht40) \ [GROUP_IDX(_streams, _sgi, _ht40)] = { \ .streams = _streams, \ .flags = \ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ (_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \ .duration = { \ MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26), \ MCS_DURATION(_streams, _sgi, _ht40 ? 108 : 52), \ MCS_DURATION(_streams, _sgi, _ht40 ? 162 : 78), \ MCS_DURATION(_streams, _sgi, _ht40 ? 216 : 104), \ MCS_DURATION(_streams, _sgi, _ht40 ? 324 : 156), \ MCS_DURATION(_streams, _sgi, _ht40 ? 432 : 208), \ MCS_DURATION(_streams, _sgi, _ht40 ? 486 : 234), \ MCS_DURATION(_streams, _sgi, _ht40 ? 540 : 260) \ } \ } /* * To enable sufficiently targeted rate sampling, MCS rates are divided into * groups, based on the number of streams and flags (HT40, SGI) that they * use. * * Sortorder has to be fixed for GROUP_IDX macro to be applicable: * HT40 -> SGI -> #streams */ const struct mcs_group minstrel_mcs_groups[] = { MCS_GROUP(1, 0, 0), MCS_GROUP(2, 0, 0), #if MINSTREL_MAX_STREAMS >= 3 MCS_GROUP(3, 0, 0), #endif MCS_GROUP(1, 1, 0), MCS_GROUP(2, 1, 0), #if MINSTREL_MAX_STREAMS >= 3 MCS_GROUP(3, 1, 0), #endif MCS_GROUP(1, 0, 1), MCS_GROUP(2, 0, 1), #if MINSTREL_MAX_STREAMS >= 3 MCS_GROUP(3, 0, 1), #endif MCS_GROUP(1, 1, 1), MCS_GROUP(2, 1, 1), #if MINSTREL_MAX_STREAMS >= 3 MCS_GROUP(3, 1, 1), #endif }; static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; /* * Perform EWMA (Exponentially Weighted Moving Average) calculation */ static int minstrel_ewma(int old, int new, int weight) { return (new * (100 - weight) + old * weight) / 100; } /* * Look up an MCS group index based on mac80211 rate information */ static int minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) { return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1, !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); } static inline struct minstrel_rate_stats * minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) { return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; } /* * Recalculate success probabilities and counters for a rate using EWMA */ static void minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr) { if (unlikely(mr->attempts > 0)) { mr->sample_skipped = 0; mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts); if (!mr->att_hist) mr->probability = mr->cur_prob; else mr->probability = minstrel_ewma(mr->probability, mr->cur_prob, EWMA_LEVEL); mr->att_hist += mr->attempts; mr->succ_hist += mr->success; } else { mr->sample_skipped++; } mr->last_success = mr->success; mr->last_attempts = mr->attempts; mr->success = 0; mr->attempts = 0; } /* * Calculate throughput based on the average A-MPDU length, taking into account * the expected number of retransmissions and their expected length */ static void minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) { struct minstrel_rate_stats *mr; unsigned int usecs; mr = &mi->groups[group].rates[rate]; if (mr->probability < MINSTREL_FRAC(1, 10)) { mr->cur_tp = 0; return; } usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); usecs += minstrel_mcs_groups[group].duration[rate]; mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability); } /* * Update rate statistics and select new primary rates * * Rules for rate selection: * - max_prob_rate must use only one stream, as a tradeoff between delivery * probability and throughput during strong fluctuations * - as long as the max prob rate has a probability of more than 3/4, pick * higher throughput rates, even if the probablity is a bit lower */ static void minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; struct minstrel_rate_stats *mr; int cur_prob, cur_prob_tp, cur_tp, cur_tp2; int group, i, index; if (mi->ampdu_packets > 0) { mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, MINSTREL_FRAC(mi->ampdu_len, mi->ampdu_packets), EWMA_LEVEL); mi->ampdu_len = 0; mi->ampdu_packets = 0; } mi->sample_slow = 0; mi->sample_count = 0; mi->max_tp_rate = 0; mi->max_tp_rate2 = 0; mi->max_prob_rate = 0; for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { cur_prob = 0; cur_prob_tp = 0; cur_tp = 0; cur_tp2 = 0; mg = &mi->groups[group]; if (!mg->supported) continue; mg->max_tp_rate = 0; mg->max_tp_rate2 = 0; mg->max_prob_rate = 0; mi->sample_count++; for (i = 0; i < MCS_GROUP_RATES; i++) { if (!(mg->supported & BIT(i))) continue; mr = &mg->rates[i]; mr->retry_updated = false; index = MCS_GROUP_RATES * group + i; minstrel_calc_rate_ewma(mr); minstrel_ht_calc_tp(mi, group, i); if (!mr->cur_tp) continue; /* ignore the lowest rate of each single-stream group */ if (!i && minstrel_mcs_groups[group].streams == 1) continue; if ((mr->cur_tp > cur_prob_tp && mr->probability > MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) { mg->max_prob_rate = index; cur_prob = mr->probability; cur_prob_tp = mr->cur_tp; } if (mr->cur_tp > cur_tp) { swap(index, mg->max_tp_rate); cur_tp = mr->cur_tp; mr = minstrel_get_ratestats(mi, index); } if (index >= mg->max_tp_rate) continue; if (mr->cur_tp > cur_tp2) { mg->max_tp_rate2 = index; cur_tp2 = mr->cur_tp; } } } /* try to sample up to half of the available rates during each interval */ mi->sample_count *= 4; cur_prob = 0; cur_prob_tp = 0; cur_tp = 0; cur_tp2 = 0; for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { mg = &mi->groups[group]; if (!mg->supported) continue; mr = minstrel_get_ratestats(mi, mg->max_prob_rate); if (cur_prob_tp < mr->cur_tp && minstrel_mcs_groups[group].streams == 1) { mi->max_prob_rate = mg->max_prob_rate; cur_prob = mr->cur_prob; cur_prob_tp = mr->cur_tp; } mr = minstrel_get_ratestats(mi, mg->max_tp_rate); if (cur_tp < mr->cur_tp) { mi->max_tp_rate2 = mi->max_tp_rate; cur_tp2 = cur_tp; mi->max_tp_rate = mg->max_tp_rate; cur_tp = mr->cur_tp; } mr = minstrel_get_ratestats(mi, mg->max_tp_rate2); if (cur_tp2 < mr->cur_tp) { mi->max_tp_rate2 = mg->max_tp_rate2; cur_tp2 = mr->cur_tp; } } mi->stats_update = jiffies; } static bool minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) { if (rate->idx < 0) return false; if (!rate->count) return false; return !!(rate->flags & IEEE80211_TX_RC_MCS); } static void minstrel_next_sample_idx(struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; for (;;) { mi->sample_group++; mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); mg = &mi->groups[mi->sample_group]; if (!mg->supported) continue; if (++mg->index >= MCS_GROUP_RATES) { mg->index = 0; if (++mg->column >= ARRAY_SIZE(sample_table)) mg->column = 0; } break; } } static void minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx, bool primary) { int group, orig_group; orig_group = group = *idx / MCS_GROUP_RATES; while (group > 0) { group--; if (!mi->groups[group].supported) continue; if (minstrel_mcs_groups[group].streams > minstrel_mcs_groups[orig_group].streams) continue; if (primary) *idx = mi->groups[group].max_tp_rate; else *idx = mi->groups[group].max_tp_rate2; break; } } static void minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct sta_info *sta = container_of(pubsta, struct sta_info, sta); u16 tid; if (unlikely(!ieee80211_is_data_qos(hdr->frame_control))) return; if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))) return; tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; if (likely(sta->ampdu_mlme.tid_tx[tid])) return; if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO) return; ieee80211_start_tx_ba_session(pubsta, tid, 5000); } static void minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { struct minstrel_ht_sta_priv *msp = priv_sta; struct minstrel_ht_sta *mi = &msp->ht; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *ar = info->status.rates; struct minstrel_rate_stats *rate, *rate2; struct minstrel_priv *mp = priv; bool last = false; int group; int i = 0; if (!msp->is_ht) return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); /* This packet was aggregated but doesn't carry status info */ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && !(info->flags & IEEE80211_TX_STAT_AMPDU)) return; if (!(info->flags & IEEE80211_TX_STAT_AMPDU)) { info->status.ampdu_ack_len = (info->flags & IEEE80211_TX_STAT_ACK ? 1 : 0); info->status.ampdu_len = 1; } mi->ampdu_packets++; mi->ampdu_len += info->status.ampdu_len; if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) { mi->sample_wait = 16 + 2 * MINSTREL_TRUNC(mi->avg_ampdu_len); mi->sample_tries = 2; mi->sample_count--; } if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) mi->sample_packets += info->status.ampdu_len; for (i = 0; !last; i++) { last = (i == IEEE80211_TX_MAX_RATES - 1) || !minstrel_ht_txstat_valid(&ar[i + 1]); if (!minstrel_ht_txstat_valid(&ar[i])) break; group = minstrel_ht_get_group_idx(&ar[i]); rate = &mi->groups[group].rates[ar[i].idx % 8]; if (last) rate->success += info->status.ampdu_ack_len; rate->attempts += ar[i].count * info->status.ampdu_len; } /* * check for sudden death of spatial multiplexing, * downgrade to a lower number of streams if necessary. */ rate = minstrel_get_ratestats(mi, mi->max_tp_rate); if (rate->attempts > 30 && MINSTREL_FRAC(rate->success, rate->attempts) < MINSTREL_FRAC(20, 100)) minstrel_downgrade_rate(mi, &mi->max_tp_rate, true); rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2); if (rate2->attempts > 30 && MINSTREL_FRAC(rate2->success, rate2->attempts) < MINSTREL_FRAC(20, 100)) minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false); if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { minstrel_ht_update_stats(mp, mi); if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) minstrel_aggr_check(sta, skb); } } static void minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, int index) { struct minstrel_rate_stats *mr; const struct mcs_group *group; unsigned int tx_time, tx_time_rtscts, tx_time_data; unsigned int cw = mp->cw_min; unsigned int ctime = 0; unsigned int t_slot = 9; /* FIXME */ unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); mr = minstrel_get_ratestats(mi, index); if (mr->probability < MINSTREL_FRAC(1, 10)) { mr->retry_count = 1; mr->retry_count_rtscts = 1; return; } mr->retry_count = 2; mr->retry_count_rtscts = 2; mr->retry_updated = true; group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len; /* Contention time for first 2 tries */ ctime = (t_slot * cw) >> 1; cw = min((cw << 1) | 1, mp->cw_max); ctime += (t_slot * cw) >> 1; cw = min((cw << 1) | 1, mp->cw_max); /* Total TX time for data and Contention after first 2 tries */ tx_time = ctime + 2 * (mi->overhead + tx_time_data); tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data); /* See how many more tries we can fit inside segment size */ do { /* Contention time for this try */ ctime = (t_slot * cw) >> 1; cw = min((cw << 1) | 1, mp->cw_max); /* Total TX time after this try */ tx_time += ctime + mi->overhead + tx_time_data; tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data; if (tx_time_rtscts < mp->segment_size) mr->retry_count_rtscts++; } while ((tx_time < mp->segment_size) && (++mr->retry_count < mp->max_retry)); } static void minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_tx_rate *rate, int index, bool sample, bool rtscts) { const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; struct minstrel_rate_stats *mr; mr = minstrel_get_ratestats(mi, index); if (!mr->retry_updated) minstrel_calc_retransmit(mp, mi, index); if (sample) rate->count = 1; else if (mr->probability < MINSTREL_FRAC(20, 100)) rate->count = 2; else if (rtscts) rate->count = mr->retry_count_rtscts; else rate->count = mr->retry_count; rate->flags = IEEE80211_TX_RC_MCS | group->flags; if (rtscts) rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES; } static inline int minstrel_get_duration(int index) { const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; return group->duration[index % MCS_GROUP_RATES]; } static int minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) { struct minstrel_rate_stats *mr; struct minstrel_mcs_group_data *mg; int sample_idx = 0; if (mi->sample_wait > 0) { mi->sample_wait--; return -1; } if (!mi->sample_tries) return -1; mi->sample_tries--; mg = &mi->groups[mi->sample_group]; sample_idx = sample_table[mg->column][mg->index]; mr = &mg->rates[sample_idx]; sample_idx += mi->sample_group * MCS_GROUP_RATES; minstrel_next_sample_idx(mi); /* * Sampling might add some overhead (RTS, no aggregation) * to the frame. Hence, don't use sampling for the currently * used max TP rate. */ if (sample_idx == mi->max_tp_rate) return -1; /* * When not using MRR, do not sample if the probability is already * higher than 95% to avoid wasting airtime */ if (!mp->has_mrr && (mr->probability > MINSTREL_FRAC(95, 100))) return -1; /* * Make sure that lower rates get sampled only occasionally, * if the link is working perfectly. */ if (minstrel_get_duration(sample_idx) > minstrel_get_duration(mi->max_tp_rate)) { if (mr->sample_skipped < 20) return -1; if (mi->sample_slow++ > 2) return -1; } return sample_idx; } static void minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); struct ieee80211_tx_rate *ar = info->status.rates; struct minstrel_ht_sta_priv *msp = priv_sta; struct minstrel_ht_sta *mi = &msp->ht; struct minstrel_priv *mp = priv; int sample_idx; bool sample = false; if (rate_control_send_low(sta, priv_sta, txrc)) return; if (!msp->is_ht) return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); info->flags |= mi->tx_flags; /* Don't use EAPOL frames for sampling on non-mrr hw */ if (mp->hw->max_rates == 1 && txrc->skb->protocol == cpu_to_be16(ETH_P_PAE)) sample_idx = -1; else sample_idx = minstrel_get_sample_rate(mp, mi); #ifdef CONFIG_MAC80211_DEBUGFS /* use fixed index if set */ if (mp->fixed_rate_idx != -1) { mi->max_tp_rate = mp->fixed_rate_idx; mi->max_tp_rate2 = mp->fixed_rate_idx; mi->max_prob_rate = mp->fixed_rate_idx; sample_idx = -1; } #endif if (sample_idx >= 0) { sample = true; minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx, true, false); info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate, false, false); } if (mp->hw->max_rates >= 3) { /* * At least 3 tx rates supported, use * sample_rate -> max_tp_rate -> max_prob_rate for sampling and * max_tp_rate -> max_tp_rate2 -> max_prob_rate by default. */ if (sample_idx >= 0) minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate, false, false); else minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2, false, true); minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate, false, !sample); ar[3].count = 0; ar[3].idx = -1; } else if (mp->hw->max_rates == 2) { /* * Only 2 tx rates supported, use * sample_rate -> max_prob_rate for sampling and * max_tp_rate -> max_prob_rate by default. */ minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_prob_rate, false, !sample); ar[2].count = 0; ar[2].idx = -1; } else { /* Not using MRR, only use the first rate */ ar[1].count = 0; ar[1].idx = -1; } mi->total_packets++; /* wraparound */ if (mi->total_packets == ~0) { mi->total_packets = 0; mi->sample_packets = 0; } } static void minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { struct minstrel_priv *mp = priv; struct minstrel_ht_sta_priv *msp = priv_sta; struct minstrel_ht_sta *mi = &msp->ht; struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; u16 sta_cap = sta->ht_cap.cap; int n_supported = 0; int ack_dur; int stbc; int i; unsigned int smps; /* fall back to the old minstrel for legacy stations */ if (!sta->ht_cap.ht_supported) goto use_legacy; BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); msp->is_ht = true; memset(mi, 0, sizeof(*mi)); mi->stats_update = jiffies; ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1); mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur; mi->overhead_rtscts = mi->overhead + 2 * ack_dur; mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); /* When using MRR, sample more on the first attempt, without delay */ if (mp->has_mrr) { mi->sample_count = 16; mi->sample_wait = 0; } else { mi->sample_count = 8; mi->sample_wait = 8; } mi->sample_tries = 4; stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >> IEEE80211_HT_CAP_RX_STBC_SHIFT; mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT; if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING) mi->tx_flags |= IEEE80211_TX_CTL_LDPC; smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >> IEEE80211_HT_CAP_SM_PS_SHIFT; for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { u16 req = 0; mi->groups[i].supported = 0; if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) req |= IEEE80211_HT_CAP_SGI_40; else req |= IEEE80211_HT_CAP_SGI_20; } if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; if ((sta_cap & req) != req) continue; /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ if (smps == WLAN_HT_CAP_SM_PS_STATIC && minstrel_mcs_groups[i].streams > 1) continue; mi->groups[i].supported = mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; if (mi->groups[i].supported) n_supported++; } if (!n_supported) goto use_legacy; return; use_legacy: msp->is_ht = false; memset(&msp->legacy, 0, sizeof(msp->legacy)); msp->legacy.r = msp->ratelist; msp->legacy.sample_table = msp->sample_table; return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); } static void minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { minstrel_ht_update_caps(priv, sband, sta, priv_sta); } static void minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, u32 changed) { minstrel_ht_update_caps(priv, sband, sta, priv_sta); } static void * minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) { struct ieee80211_supported_band *sband; struct minstrel_ht_sta_priv *msp; struct minstrel_priv *mp = priv; struct ieee80211_hw *hw = mp->hw; int max_rates = 0; int i; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sband = hw->wiphy->bands[i]; if (sband && sband->n_bitrates > max_rates) max_rates = sband->n_bitrates; } msp = kzalloc(sizeof(*msp), gfp); if (!msp) return NULL; msp->ratelist = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp); if (!msp->ratelist) goto error; msp->sample_table = kmalloc(SAMPLE_COLUMNS * max_rates, gfp); if (!msp->sample_table) goto error1; return msp; error1: kfree(msp->ratelist); error: kfree(msp); return NULL; } static void minstrel_ht_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) { struct minstrel_ht_sta_priv *msp = priv_sta; kfree(msp->sample_table); kfree(msp->ratelist); kfree(msp); } static void * minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { return mac80211_minstrel.alloc(hw, debugfsdir); } static void minstrel_ht_free(void *priv) { mac80211_minstrel.free(priv); } static struct rate_control_ops mac80211_minstrel_ht = { .name = "minstrel_ht", .tx_status = minstrel_ht_tx_status, .get_rate = minstrel_ht_get_rate, .rate_init = minstrel_ht_rate_init, .rate_update = minstrel_ht_rate_update, .alloc_sta = minstrel_ht_alloc_sta, .free_sta = minstrel_ht_free_sta, .alloc = minstrel_ht_alloc, .free = minstrel_ht_free, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = minstrel_ht_add_sta_debugfs, .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs, #endif }; static void init_sample_table(void) { int col, i, new_idx; u8 rnd[MCS_GROUP_RATES]; memset(sample_table, 0xff, sizeof(sample_table)); for (col = 0; col < SAMPLE_COLUMNS; col++) { for (i = 0; i < MCS_GROUP_RATES; i++) { get_random_bytes(rnd, sizeof(rnd)); new_idx = (i + rnd[i]) % MCS_GROUP_RATES; while (sample_table[col][new_idx] != 0xff) new_idx = (new_idx + 1) % MCS_GROUP_RATES; sample_table[col][new_idx] = i; } } } int __init rc80211_minstrel_ht_init(void) { init_sample_table(); return ieee80211_rate_control_register(&mac80211_minstrel_ht); } void rc80211_minstrel_ht_exit(void) { ieee80211_rate_control_unregister(&mac80211_minstrel_ht); } compat-drivers-2012-09-18/net/mac80211/rc80211_minstrel.c0000644000175000017500000003742212026211315021373 0ustar mcgrofmcgrof/* * Copyright (C) 2008 Felix Fietkau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Based on minstrel.c: * Copyright (C) 2005-2007 Derek Smithies * Sponsored by Indranet Technologies Ltd * * Based on sample.c: * Copyright (c) 2005 John Bicket * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include #include #include #include #include #include "rate.h" #include "rc80211_minstrel.h" #define SAMPLE_COLUMNS 10 #define SAMPLE_TBL(_mi, _idx, _col) \ _mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col] /* convert mac80211 rate index to local array index */ static inline int rix_to_ndx(struct minstrel_sta_info *mi, int rix) { int i = rix; for (i = rix; i >= 0; i--) if (mi->r[i].rix == rix) break; return i; } static void minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) { u32 max_tp = 0, index_max_tp = 0, index_max_tp2 = 0; u32 max_prob = 0, index_max_prob = 0; u32 usecs; u32 p; int i; mi->stats_update = jiffies; for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; usecs = mr->perfect_tx_time; if (!usecs) usecs = 1000000; /* To avoid rounding issues, probabilities scale from 0 (0%) * to 18000 (100%) */ if (mr->attempts) { p = (mr->success * 18000) / mr->attempts; mr->succ_hist += mr->success; mr->att_hist += mr->attempts; mr->cur_prob = p; p = ((p * (100 - mp->ewma_level)) + (mr->probability * mp->ewma_level)) / 100; mr->probability = p; mr->cur_tp = p * (1000000 / usecs); } mr->last_success = mr->success; mr->last_attempts = mr->attempts; mr->success = 0; mr->attempts = 0; /* Sample less often below the 10% chance of success. * Sample less often above the 95% chance of success. */ if ((mr->probability > 17100) || (mr->probability < 1800)) { mr->adjusted_retry_count = mr->retry_count >> 1; if (mr->adjusted_retry_count > 2) mr->adjusted_retry_count = 2; mr->sample_limit = 4; } else { mr->sample_limit = -1; mr->adjusted_retry_count = mr->retry_count; } if (!mr->adjusted_retry_count) mr->adjusted_retry_count = 2; } for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; if (max_tp < mr->cur_tp) { index_max_tp = i; max_tp = mr->cur_tp; } if (max_prob < mr->probability) { index_max_prob = i; max_prob = mr->probability; } } max_tp = 0; for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; if (i == index_max_tp) continue; if (max_tp < mr->cur_tp) { index_max_tp2 = i; max_tp = mr->cur_tp; } } mi->max_tp_rate = index_max_tp; mi->max_tp_rate2 = index_max_tp2; mi->max_prob_rate = index_max_prob; } static void minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { struct minstrel_sta_info *mi = priv_sta; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *ar = info->status.rates; int i, ndx; int success; success = !!(info->flags & IEEE80211_TX_STAT_ACK); for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if (ar[i].idx < 0) break; ndx = rix_to_ndx(mi, ar[i].idx); if (ndx < 0) continue; mi->r[ndx].attempts += ar[i].count; if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) mi->r[ndx].success += success; } if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) mi->sample_count++; if (mi->sample_deferred > 0) mi->sample_deferred--; } static inline unsigned int minstrel_get_retry_count(struct minstrel_rate *mr, struct ieee80211_tx_info *info) { unsigned int retry = mr->adjusted_retry_count; if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) retry = max(2U, min(mr->retry_count_rtscts, retry)); else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) retry = max(2U, min(mr->retry_count_cts, retry)); return retry; } static int minstrel_get_next_sample(struct minstrel_sta_info *mi) { unsigned int sample_ndx; sample_ndx = SAMPLE_TBL(mi, mi->sample_idx, mi->sample_column); mi->sample_idx++; if ((int) mi->sample_idx > (mi->n_rates - 2)) { mi->sample_idx = 0; mi->sample_column++; if (mi->sample_column >= SAMPLE_COLUMNS) mi->sample_column = 0; } return sample_ndx; } static void minstrel_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct minstrel_sta_info *mi = priv_sta; struct minstrel_priv *mp = priv; struct ieee80211_tx_rate *ar = info->control.rates; unsigned int ndx, sample_ndx = 0; bool mrr; bool sample_slower = false; bool sample = false; int i, delta; int mrr_ndx[3]; int sample_rate; if (rate_control_send_low(sta, priv_sta, txrc)) return; mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot; if (time_after(jiffies, mi->stats_update + (mp->update_interval * HZ) / 1000)) minstrel_update_stats(mp, mi); ndx = mi->max_tp_rate; if (mrr) sample_rate = mp->lookaround_rate_mrr; else sample_rate = mp->lookaround_rate; mi->packet_count++; delta = (mi->packet_count * sample_rate / 100) - (mi->sample_count + mi->sample_deferred / 2); /* delta > 0: sampling required */ if ((delta > 0) && (mrr || !mi->prev_sample)) { struct minstrel_rate *msr; if (mi->packet_count >= 10000) { mi->sample_deferred = 0; mi->sample_count = 0; mi->packet_count = 0; } else if (delta > mi->n_rates * 2) { /* With multi-rate retry, not every planned sample * attempt actually gets used, due to the way the retry * chain is set up - [max_tp,sample,prob,lowest] for * sample_rate < max_tp. * * If there's too much sampling backlog and the link * starts getting worse, minstrel would start bursting * out lots of sampling frames, which would result * in a large throughput loss. */ mi->sample_count += (delta - mi->n_rates * 2); } sample_ndx = minstrel_get_next_sample(mi); msr = &mi->r[sample_ndx]; sample = true; sample_slower = mrr && (msr->perfect_tx_time > mi->r[ndx].perfect_tx_time); if (!sample_slower) { if (msr->sample_limit != 0) { ndx = sample_ndx; mi->sample_count++; if (msr->sample_limit > 0) msr->sample_limit--; } else { sample = false; } } else { /* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark * packets that have the sampling rate deferred to the * second MRR stage. Increase the sample counter only * if the deferred sample rate was actually used. * Use the sample_deferred counter to make sure that * the sampling is not done in large bursts */ info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; mi->sample_deferred++; } } mi->prev_sample = sample; /* If we're not using MRR and the sampling rate already * has a probability of >95%, we shouldn't be attempting * to use it, as this only wastes precious airtime */ if (!mrr && sample && (mi->r[ndx].probability > 17100)) ndx = mi->max_tp_rate; ar[0].idx = mi->r[ndx].rix; ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info); if (!mrr) { if (!sample) ar[0].count = mp->max_retry; ar[1].idx = mi->lowest_rix; ar[1].count = mp->max_retry; return; } /* MRR setup */ if (sample) { if (sample_slower) mrr_ndx[0] = sample_ndx; else mrr_ndx[0] = mi->max_tp_rate; } else { mrr_ndx[0] = mi->max_tp_rate2; } mrr_ndx[1] = mi->max_prob_rate; mrr_ndx[2] = 0; for (i = 1; i < 4; i++) { ar[i].idx = mi->r[mrr_ndx[i - 1]].rix; ar[i].count = mi->r[mrr_ndx[i - 1]].adjusted_retry_count; } } static void calc_rate_durations(enum ieee80211_band band, struct minstrel_rate *d, struct ieee80211_rate *rate) { int erp = !!(rate->flags & IEEE80211_RATE_ERP_G); d->perfect_tx_time = ieee80211_frame_duration(band, 1200, rate->bitrate, erp, 1); d->ack_time = ieee80211_frame_duration(band, 10, rate->bitrate, erp, 1); } static void init_sample_table(struct minstrel_sta_info *mi) { unsigned int i, col, new_idx; unsigned int n_srates = mi->n_rates - 1; u8 rnd[8]; mi->sample_column = 0; mi->sample_idx = 0; memset(mi->sample_table, 0, SAMPLE_COLUMNS * mi->n_rates); for (col = 0; col < SAMPLE_COLUMNS; col++) { for (i = 0; i < n_srates; i++) { get_random_bytes(rnd, sizeof(rnd)); new_idx = (i + rnd[i & 7]) % n_srates; while (SAMPLE_TBL(mi, new_idx, col) != 0) new_idx = (new_idx + 1) % n_srates; /* Don't sample the slowest rate (i.e. slowest base * rate). We must presume that the slowest rate works * fine, or else other management frames will also be * failing and the link will break */ SAMPLE_TBL(mi, new_idx, col) = i + 1; } } } static void minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { struct minstrel_sta_info *mi = priv_sta; struct minstrel_priv *mp = priv; struct ieee80211_rate *ctl_rate; unsigned int i, n = 0; unsigned int t_slot = 9; /* FIXME: get real slot time */ mi->lowest_rix = rate_lowest_index(sband, sta); ctl_rate = &sband->bitrates[mi->lowest_rix]; mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10, ctl_rate->bitrate, !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1); for (i = 0; i < sband->n_bitrates; i++) { struct minstrel_rate *mr = &mi->r[n]; unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; unsigned int tx_time_single; unsigned int cw = mp->cw_min; if (!rate_supported(sta, sband->band, i)) continue; n++; memset(mr, 0, sizeof(*mr)); mr->rix = i; mr->bitrate = sband->bitrates[i].bitrate / 5; calc_rate_durations(sband->band, mr, &sband->bitrates[i]); /* calculate maximum number of retransmissions before * fallback (based on maximum segment size) */ mr->sample_limit = -1; mr->retry_count = 1; mr->retry_count_cts = 1; mr->retry_count_rtscts = 1; tx_time = mr->perfect_tx_time + mi->sp_ack_dur; do { /* add one retransmission */ tx_time_single = mr->ack_time + mr->perfect_tx_time; /* contention window */ tx_time_single += (t_slot * cw) >> 1; cw = min((cw << 1) | 1, mp->cw_max); tx_time += tx_time_single; tx_time_cts += tx_time_single + mi->sp_ack_dur; tx_time_rtscts += tx_time_single + 2 * mi->sp_ack_dur; if ((tx_time_cts < mp->segment_size) && (mr->retry_count_cts < mp->max_retry)) mr->retry_count_cts++; if ((tx_time_rtscts < mp->segment_size) && (mr->retry_count_rtscts < mp->max_retry)) mr->retry_count_rtscts++; } while ((tx_time < mp->segment_size) && (++mr->retry_count < mp->max_retry)); mr->adjusted_retry_count = mr->retry_count; } for (i = n; i < sband->n_bitrates; i++) { struct minstrel_rate *mr = &mi->r[i]; mr->rix = -1; } mi->n_rates = n; mi->stats_update = jiffies; init_sample_table(mi); } static void * minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) { struct ieee80211_supported_band *sband; struct minstrel_sta_info *mi; struct minstrel_priv *mp = priv; struct ieee80211_hw *hw = mp->hw; int max_rates = 0; int i; mi = kzalloc(sizeof(struct minstrel_sta_info), gfp); if (!mi) return NULL; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sband = hw->wiphy->bands[i]; if (sband && sband->n_bitrates > max_rates) max_rates = sband->n_bitrates; } mi->r = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp); if (!mi->r) goto error; mi->sample_table = kmalloc(SAMPLE_COLUMNS * max_rates, gfp); if (!mi->sample_table) goto error1; mi->stats_update = jiffies; return mi; error1: kfree(mi->r); error: kfree(mi); return NULL; } static void minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) { struct minstrel_sta_info *mi = priv_sta; kfree(mi->sample_table); kfree(mi->r); kfree(mi); } static void * minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { struct minstrel_priv *mp; mp = kzalloc(sizeof(struct minstrel_priv), GFP_ATOMIC); if (!mp) return NULL; /* contention window settings * Just an approximation. Using the per-queue values would complicate * the calculations and is probably unnecessary */ mp->cw_min = 15; mp->cw_max = 1023; /* number of packets (in %) to use for sampling other rates * sample less often for non-mrr packets, because the overhead * is much higher than with mrr */ mp->lookaround_rate = 5; mp->lookaround_rate_mrr = 10; /* moving average weight for EWMA */ mp->ewma_level = 75; /* maximum time that the hw is allowed to stay in one MRR segment */ mp->segment_size = 6000; if (hw->max_rate_tries > 0) mp->max_retry = hw->max_rate_tries; else /* safe default, does not necessarily have to match hw properties */ mp->max_retry = 7; if (hw->max_rates >= 4) mp->has_mrr = true; mp->hw = hw; mp->update_interval = 100; #ifdef CONFIG_MAC80211_DEBUGFS mp->fixed_rate_idx = (u32) -1; mp->dbg_fixed_rate = debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx); #endif return mp; } static void minstrel_free(void *priv) { #ifdef CONFIG_MAC80211_DEBUGFS debugfs_remove(((struct minstrel_priv *)priv)->dbg_fixed_rate); #endif kfree(priv); } struct rate_control_ops mac80211_minstrel = { .name = "minstrel", .tx_status = minstrel_tx_status, .get_rate = minstrel_get_rate, .rate_init = minstrel_rate_init, .alloc = minstrel_alloc, .free = minstrel_free, .alloc_sta = minstrel_alloc_sta, .free_sta = minstrel_free_sta, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = minstrel_add_sta_debugfs, .remove_sta_debugfs = minstrel_remove_sta_debugfs, #endif }; int __init rc80211_minstrel_init(void) { return ieee80211_rate_control_register(&mac80211_minstrel); } void rc80211_minstrel_exit(void) { ieee80211_rate_control_unregister(&mac80211_minstrel); } compat-drivers-2012-09-18/net/mac80211/wep.h0000644000175000017500000000213612026211315017250 0ustar mcgrofmcgrof/* * Software WEP encryption implementation * Copyright 2002, Jouni Malinen * Copyright 2003, Instant802 Networks, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef WEP_H #define WEP_H #include #include #include "ieee80211_i.h" #include "key.h" int ieee80211_wep_init(struct ieee80211_local *local); void ieee80211_wep_free(struct ieee80211_local *local); int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, const u8 *key, int keylen, int keyidx); int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); ieee80211_rx_result ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx); ieee80211_tx_result ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx); #endif /* WEP_H */ compat-drivers-2012-09-18/net/mac80211/rc80211_minstrel_ht_debugfs.c0000644000175000017500000000644612026211315023567 0ustar mcgrofmcgrof/* * Copyright (C) 2010 Felix Fietkau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include "rc80211_minstrel.h" #include "rc80211_minstrel_ht.h" static int minstrel_ht_stats_open(struct inode *inode, struct file *file) { struct minstrel_ht_sta_priv *msp = inode->i_private; struct minstrel_ht_sta *mi = &msp->ht; struct minstrel_debugfs_info *ms; unsigned int i, j, tp, prob, eprob; char *p; int ret; if (!msp->is_ht) { inode->i_private = &msp->legacy; ret = minstrel_stats_open(inode, file); inode->i_private = msp; return ret; } ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL); if (!ms) return -ENOMEM; file->private_data = ms; p = ms->buf; p += sprintf(p, "type rate throughput ewma prob this prob " "this succ/attempt success attempts\n"); for (i = 0; i < MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; i++) { char htmode = '2'; char gimode = 'L'; if (!mi->groups[i].supported) continue; if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) htmode = '4'; if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) gimode = 'S'; for (j = 0; j < MCS_GROUP_RATES; j++) { struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; int idx = i * MCS_GROUP_RATES + j; if (!(mi->groups[i].supported & BIT(j))) continue; p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; p += sprintf(p, "MCS%-2u", (minstrel_mcs_groups[i].streams - 1) * MCS_GROUP_RATES + j); tp = mr->cur_tp / 10; prob = MINSTREL_TRUNC(mr->cur_prob * 1000); eprob = MINSTREL_TRUNC(mr->probability * 1000); p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " "%3u(%3u) %8llu %8llu\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, mr->last_success, mr->last_attempts, (unsigned long long)mr->succ_hist, (unsigned long long)mr->att_hist); } } p += sprintf(p, "\nTotal packet count:: ideal %d " "lookaround %d\n", max(0, (int) mi->total_packets - (int) mi->sample_packets), mi->sample_packets); p += sprintf(p, "Average A-MPDU length: %d.%d\n", MINSTREL_TRUNC(mi->avg_ampdu_len), MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); ms->len = p - ms->buf; return nonseekable_open(inode, file); } static const struct file_operations minstrel_ht_stat_fops = { .owner = THIS_MODULE, .open = minstrel_ht_stats_open, .read = minstrel_stats_read, .release = minstrel_stats_release, .llseek = no_llseek, }; void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) { struct minstrel_ht_sta_priv *msp = priv_sta; msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp, &minstrel_ht_stat_fops); } void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta) { struct minstrel_ht_sta_priv *msp = priv_sta; debugfs_remove(msp->dbg_stats); } compat-drivers-2012-09-18/net/mac80211/rc80211_pid.h0000644000175000017500000001725512026211315020321 0ustar mcgrofmcgrof/* * Copyright 2007, Mattias Nissler * Copyright 2007, Stefano Brivio * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef RC80211_PID_H #define RC80211_PID_H /* Sampling period for measuring percentage of failed frames in ms. */ #define RC_PID_INTERVAL 125 /* Exponential averaging smoothness (used for I part of PID controller) */ #define RC_PID_SMOOTHING_SHIFT 3 #define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT) /* Sharpening factor (used for D part of PID controller) */ #define RC_PID_SHARPENING_FACTOR 0 #define RC_PID_SHARPENING_DURATION 0 /* Fixed point arithmetic shifting amount. */ #define RC_PID_ARITH_SHIFT 8 /* Proportional PID component coefficient. */ #define RC_PID_COEFF_P 15 /* Integral PID component coefficient. */ #define RC_PID_COEFF_I 9 /* Derivative PID component coefficient. */ #define RC_PID_COEFF_D 15 /* Target failed frames rate for the PID controller. NB: This effectively gives * maximum failed frames percentage we're willing to accept. If the wireless * link quality is good, the controller will fail to adjust failed frames * percentage to the target. This is intentional. */ #define RC_PID_TARGET_PF 14 /* Rate behaviour normalization quantity over time. */ #define RC_PID_NORM_OFFSET 3 /* Push high rates right after loading. */ #define RC_PID_FAST_START 0 /* Arithmetic right shift for positive and negative values for ISO C. */ #define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \ ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y)) enum rc_pid_event_type { RC_PID_EVENT_TYPE_TX_STATUS, RC_PID_EVENT_TYPE_RATE_CHANGE, RC_PID_EVENT_TYPE_TX_RATE, RC_PID_EVENT_TYPE_PF_SAMPLE, }; union rc_pid_event_data { /* RC_PID_EVENT_TX_STATUS */ struct { u32 flags; struct ieee80211_tx_info tx_status; }; /* RC_PID_EVENT_TYPE_RATE_CHANGE */ /* RC_PID_EVENT_TYPE_TX_RATE */ struct { int index; int rate; }; /* RC_PID_EVENT_TYPE_PF_SAMPLE */ struct { s32 pf_sample; s32 prop_err; s32 int_err; s32 der_err; }; }; struct rc_pid_event { /* The time when the event occurred */ unsigned long timestamp; /* Event ID number */ unsigned int id; /* Type of event */ enum rc_pid_event_type type; /* type specific data */ union rc_pid_event_data data; }; /* Size of the event ring buffer. */ #define RC_PID_EVENT_RING_SIZE 32 struct rc_pid_event_buffer { /* Counter that generates event IDs */ unsigned int ev_count; /* Ring buffer of events */ struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE]; /* Index to the entry in events_buf to be reused */ unsigned int next_entry; /* Lock that guards against concurrent access to this buffer struct */ spinlock_t lock; /* Wait queue for poll/select and blocking I/O */ wait_queue_head_t waitqueue; }; struct rc_pid_events_file_info { /* The event buffer we read */ struct rc_pid_event_buffer *events; /* The entry we have should read next */ unsigned int next_entry; }; /** * struct rc_pid_debugfs_entries - tunable parameters * * Algorithm parameters, tunable via debugfs. * @target: target percentage for failed frames * @sampling_period: error sampling interval in milliseconds * @coeff_p: absolute value of the proportional coefficient * @coeff_i: absolute value of the integral coefficient * @coeff_d: absolute value of the derivative coefficient * @smoothing_shift: absolute value of the integral smoothing factor (i.e. * amount of smoothing introduced by the exponential moving average) * @sharpen_factor: absolute value of the derivative sharpening factor (i.e. * amount of emphasis given to the derivative term after low activity * events) * @sharpen_duration: duration of the sharpening effect after the detected low * activity event, relative to sampling_period * @norm_offset: amount of normalization periodically performed on the learnt * rate behaviour values (lower means we should trust more what we learnt * about behaviour of rates, higher means we should trust more the natural * ordering of rates) */ struct rc_pid_debugfs_entries { struct dentry *target; struct dentry *sampling_period; struct dentry *coeff_p; struct dentry *coeff_i; struct dentry *coeff_d; struct dentry *smoothing_shift; struct dentry *sharpen_factor; struct dentry *sharpen_duration; struct dentry *norm_offset; }; void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, struct ieee80211_tx_info *stat); void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, int index, int rate); void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, int index, int rate); void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, s32 pf_sample, s32 prop_err, s32 int_err, s32 der_err); void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta); struct rc_pid_sta_info { unsigned long last_change; unsigned long last_sample; u32 tx_num_failed; u32 tx_num_xmit; int txrate_idx; /* Average failed frames percentage error (i.e. actual vs. target * percentage), scaled by RC_PID_SMOOTHING. This value is computed * using using an exponential weighted average technique: * * (RC_PID_SMOOTHING - 1) * err_avg_old + err * err_avg = ------------------------------------------ * RC_PID_SMOOTHING * * where err_avg is the new approximation, err_avg_old the previous one * and err is the error w.r.t. to the current failed frames percentage * sample. Note that the bigger RC_PID_SMOOTHING the more weight is * given to the previous estimate, resulting in smoother behavior (i.e. * corresponding to a longer integration window). * * For computation, we actually don't use the above formula, but this * one: * * err_avg_scaled = err_avg_old_scaled - err_avg_old + err * * where: * err_avg_scaled = err * RC_PID_SMOOTHING * err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING * * This avoids floating point numbers and the per_failed_old value can * easily be obtained by shifting per_failed_old_scaled right by * RC_PID_SMOOTHING_SHIFT. */ s32 err_avg_sc; /* Last framed failes percentage sample. */ u32 last_pf; /* Sharpening needed. */ u8 sharp_cnt; #ifdef CONFIG_MAC80211_DEBUGFS /* Event buffer */ struct rc_pid_event_buffer events; /* Events debugfs file entry */ struct dentry *events_entry; #endif }; /* Algorithm parameters. We keep them on a per-algorithm approach, so they can * be tuned individually for each interface. */ struct rc_pid_rateinfo { /* Map sorted rates to rates in ieee80211_hw_mode. */ int index; /* Map rates in ieee80211_hw_mode to sorted rates. */ int rev_index; /* Did we do any measurement on this rate? */ bool valid; /* Comparison with the lowest rate. */ int diff; }; struct rc_pid_info { /* The failed frames percentage target. */ unsigned int target; /* Rate at which failed frames percentage is sampled in 0.001s. */ unsigned int sampling_period; /* P, I and D coefficients. */ int coeff_p; int coeff_i; int coeff_d; /* Exponential averaging shift. */ unsigned int smoothing_shift; /* Sharpening factor and duration. */ unsigned int sharpen_factor; unsigned int sharpen_duration; /* Normalization offset. */ unsigned int norm_offset; /* Rates information. */ struct rc_pid_rateinfo *rinfo; /* Index of the last used rate. */ int oldrate; #ifdef CONFIG_MAC80211_DEBUGFS /* Debugfs entries created for the parameters above. */ struct rc_pid_debugfs_entries dentries; #endif }; #endif /* RC80211_PID_H */ compat-drivers-2012-09-18/net/mac80211/debugfs_sta.c0000644000175000017500000002724012026211315020741 0ustar mcgrofmcgrof/* * Copyright 2003-2005 Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2007 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include "ieee80211_i.h" #include "debugfs.h" #include "debugfs_sta.h" #include "sta_info.h" /* sta attributtes */ #define STA_READ(name, field, format_string) \ static ssize_t sta_ ##name## _read(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct sta_info *sta = file->private_data; \ return mac80211_format_buffer(userbuf, count, ppos, \ format_string, sta->field); \ } #define STA_READ_D(name, field) STA_READ(name, field, "%d\n") #define STA_READ_U(name, field) STA_READ(name, field, "%u\n") #define STA_READ_S(name, field) STA_READ(name, field, "%s\n") #define STA_OPS(name) \ static const struct file_operations sta_ ##name## _ops = { \ .read = sta_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ } #define STA_OPS_RW(name) \ static const struct file_operations sta_ ##name## _ops = { \ .read = sta_##name##_read, \ .write = sta_##name##_write, \ .open = simple_open, \ .llseek = generic_file_llseek, \ } #define STA_FILE(name, field, format) \ STA_READ_##format(name, field) \ STA_OPS(name) STA_FILE(aid, sta.aid, D); STA_FILE(dev, sdata->name, S); STA_FILE(last_signal, last_signal, D); static ssize_t sta_flags_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { char buf[121]; struct sta_info *sta = file->private_data; #define TEST(flg) \ test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", TEST(AUTH), TEST(ASSOC), TEST(PS_STA), TEST(PS_DRIVER), TEST(AUTHORIZED), TEST(SHORT_PREAMBLE), TEST(WME), TEST(WDS), TEST(CLEAR_PS_FILT), TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), TEST(INSERTED), TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN)); #undef TEST return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(flags); static ssize_t sta_num_ps_buf_frames_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; char buf[17*IEEE80211_NUM_ACS], *p = buf; int ac; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac, skb_queue_len(&sta->ps_tx_buf[ac]) + skb_queue_len(&sta->tx_filtered[ac])); return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } STA_OPS(num_ps_buf_frames); static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; return mac80211_format_buffer(userbuf, count, ppos, "%d\n", jiffies_to_msecs(jiffies - sta->last_rx)); } STA_OPS(inactive_ms); static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; struct timespec uptime; struct tm result; long connected_time_secs; char buf[100]; int res; do_posix_clock_monotonic_gettime(&uptime); connected_time_secs = uptime.tv_sec - sta->last_connected; time_to_tm(connected_time_secs, 0, &result); result.tm_year -= 70; result.tm_mday -= 1; res = scnprintf(buf, sizeof(buf), "years - %ld\nmonths - %d\ndays - %d\nclock - %d:%d:%d\n\n", result.tm_year, result.tm_mon, result.tm_mday, result.tm_hour, result.tm_min, result.tm_sec); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(connected_time); static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { char buf[15*NUM_RX_DATA_QUEUES], *p = buf; int i; struct sta_info *sta = file->private_data; for (i = 0; i < NUM_RX_DATA_QUEUES; i++) p += scnprintf(p, sizeof(buf)+buf-p, "%x ", le16_to_cpu(sta->last_seq_ctrl[i])); p += scnprintf(p, sizeof(buf)+buf-p, "\n"); return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } STA_OPS(last_seq_ctrl); static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { char buf[71 + STA_TID_NUM * 40], *p = buf; int i; struct sta_info *sta = file->private_data; struct tid_ampdu_rx *tid_rx; struct tid_ampdu_tx *tid_tx; rcu_read_lock(); p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n", sta->ampdu_mlme.dialog_token_allocator + 1); p += scnprintf(p, sizeof(buf) + buf - p, "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n"); for (i = 0; i < STA_TID_NUM; i++) { tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]); tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]); p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_rx); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", tid_rx ? tid_rx->dialog_token : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", tid_rx ? tid_rx->ssn : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_tx); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", tid_tx ? tid_tx->dialog_token : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d", tid_tx ? skb_queue_len(&tid_tx->pending) : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\n"); } rcu_read_unlock(); return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { char _buf[12], *buf = _buf; struct sta_info *sta = file->private_data; bool start, tx; unsigned long tid; int ret; if (count > sizeof(_buf)) return -EINVAL; if (copy_from_user(buf, userbuf, count)) return -EFAULT; buf[sizeof(_buf) - 1] = '\0'; if (strncmp(buf, "tx ", 3) == 0) { buf += 3; tx = true; } else if (strncmp(buf, "rx ", 3) == 0) { buf += 3; tx = false; } else return -EINVAL; if (strncmp(buf, "start ", 6) == 0) { buf += 6; start = true; if (!tx) return -EINVAL; } else if (strncmp(buf, "stop ", 5) == 0) { buf += 5; start = false; } else return -EINVAL; tid = simple_strtoul(buf, NULL, 0); if (tid >= STA_TID_NUM) return -EINVAL; if (tx) { if (start) ret = ieee80211_start_tx_ba_session(&sta->sta, tid, 5000); else ret = ieee80211_stop_tx_ba_session(&sta->sta, tid); } else { __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, 3, true); ret = 0; } return ret ?: count; } STA_OPS_RW(agg_status); static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { #define PRINT_HT_CAP(_cond, _str) \ do { \ if (_cond) \ p += scnprintf(p, sizeof(buf)+buf-p, "\t" _str "\n"); \ } while (0) char buf[512], *p = buf; int i; struct sta_info *sta = file->private_data; struct ieee80211_sta_ht_cap *htc = &sta->sta.ht_cap; p += scnprintf(p, sizeof(buf) + buf - p, "ht %ssupported\n", htc->ht_supported ? "" : "not "); if (htc->ht_supported) { p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.4x\n", htc->cap); PRINT_HT_CAP((htc->cap & BIT(0)), "RX LDPC"); PRINT_HT_CAP((htc->cap & BIT(1)), "HT20/HT40"); PRINT_HT_CAP(!(htc->cap & BIT(1)), "HT20"); PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 0, "Static SM Power Save"); PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 1, "Dynamic SM Power Save"); PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 3, "SM Power Save disabled"); PRINT_HT_CAP((htc->cap & BIT(4)), "RX Greenfield"); PRINT_HT_CAP((htc->cap & BIT(5)), "RX HT20 SGI"); PRINT_HT_CAP((htc->cap & BIT(6)), "RX HT40 SGI"); PRINT_HT_CAP((htc->cap & BIT(7)), "TX STBC"); PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 0, "No RX STBC"); PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 1, "RX STBC 1-stream"); PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 2, "RX STBC 2-streams"); PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 3, "RX STBC 3-streams"); PRINT_HT_CAP((htc->cap & BIT(10)), "HT Delayed Block Ack"); PRINT_HT_CAP(!(htc->cap & BIT(11)), "Max AMSDU length: " "3839 bytes"); PRINT_HT_CAP((htc->cap & BIT(11)), "Max AMSDU length: " "7935 bytes"); /* * For beacons and probe response this would mean the BSS * does or does not allow the usage of DSSS/CCK HT40. * Otherwise it means the STA does or does not use * DSSS/CCK HT40. */ PRINT_HT_CAP((htc->cap & BIT(12)), "DSSS/CCK HT40"); PRINT_HT_CAP(!(htc->cap & BIT(12)), "No DSSS/CCK HT40"); /* BIT(13) is reserved */ PRINT_HT_CAP((htc->cap & BIT(14)), "40 MHz Intolerant"); PRINT_HT_CAP((htc->cap & BIT(15)), "L-SIG TXOP protection"); p += scnprintf(p, sizeof(buf)+buf-p, "ampdu factor/density: %d/%d\n", htc->ampdu_factor, htc->ampdu_density); p += scnprintf(p, sizeof(buf)+buf-p, "MCS mask:"); for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) p += scnprintf(p, sizeof(buf)+buf-p, " %.2x", htc->mcs.rx_mask[i]); p += scnprintf(p, sizeof(buf)+buf-p, "\n"); /* If not set this is meaningless */ if (le16_to_cpu(htc->mcs.rx_highest)) { p += scnprintf(p, sizeof(buf)+buf-p, "MCS rx highest: %d Mbps\n", le16_to_cpu(htc->mcs.rx_highest)); } p += scnprintf(p, sizeof(buf)+buf-p, "MCS tx params: %x\n", htc->mcs.tx_params); } return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } STA_OPS(ht_capa); #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, \ sta->debugfs.dir, sta, &sta_ ##name## _ops); #define DEBUGFS_ADD_COUNTER(name, field) \ if (sizeof(sta->field) == sizeof(u32)) \ debugfs_create_u32(#name, 0400, sta->debugfs.dir, \ (u32 *) &sta->field); \ else \ debugfs_create_u64(#name, 0400, sta->debugfs.dir, \ (u64 *) &sta->field); void ieee80211_sta_debugfs_add(struct sta_info *sta) { struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations; u8 mac[3*ETH_ALEN]; sta->debugfs.add_has_run = true; if (!stations_dir) return; snprintf(mac, sizeof(mac), "%pM", sta->sta.addr); /* * This might fail due to a race condition: * When mac80211 unlinks a station, the debugfs entries * remain, but it is already possible to link a new * station with the same address which triggers adding * it to debugfs; therefore, if the old station isn't * destroyed quickly enough the old station's debugfs * dir might still be around. */ sta->debugfs.dir = debugfs_create_dir(mac, stations_dir); if (!sta->debugfs.dir) return; DEBUGFS_ADD(flags); DEBUGFS_ADD(num_ps_buf_frames); DEBUGFS_ADD(inactive_ms); DEBUGFS_ADD(connected_time); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); DEBUGFS_ADD(dev); DEBUGFS_ADD(last_signal); DEBUGFS_ADD(ht_capa); DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); DEBUGFS_ADD_COUNTER(rx_bytes, rx_bytes); DEBUGFS_ADD_COUNTER(tx_bytes, tx_bytes); DEBUGFS_ADD_COUNTER(rx_duplicates, num_duplicates); DEBUGFS_ADD_COUNTER(rx_fragments, rx_fragments); DEBUGFS_ADD_COUNTER(rx_dropped, rx_dropped); DEBUGFS_ADD_COUNTER(tx_fragments, tx_fragments); DEBUGFS_ADD_COUNTER(tx_filtered, tx_filtered_count); DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed); DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count); DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count); } void ieee80211_sta_debugfs_remove(struct sta_info *sta) { debugfs_remove_recursive(sta->debugfs.dir); sta->debugfs.dir = NULL; } compat-drivers-2012-09-18/net/mac80211/debugfs.h0000644000175000017500000000056512026211315020100 0ustar mcgrofmcgrof#ifndef __MAC80211_DEBUGFS_H #define __MAC80211_DEBUGFS_H #ifdef CONFIG_MAC80211_DEBUGFS extern void debugfs_hw_add(struct ieee80211_local *local); extern int mac80211_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...); #else static inline void debugfs_hw_add(struct ieee80211_local *local) { } #endif #endif /* __MAC80211_DEBUGFS_H */ compat-drivers-2012-09-18/net/mac80211/michael.c0000644000175000017500000000427512026211315020060 0ustar mcgrofmcgrof/* * Michael MIC implementation - optimized for TKIP MIC operations * Copyright 2002-2003, Instant802 Networks, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "michael.h" static void michael_block(struct michael_mic_ctx *mctx, u32 val) { mctx->l ^= val; mctx->r ^= rol32(mctx->l, 17); mctx->l += mctx->r; mctx->r ^= ((mctx->l & 0xff00ff00) >> 8) | ((mctx->l & 0x00ff00ff) << 8); mctx->l += mctx->r; mctx->r ^= rol32(mctx->l, 3); mctx->l += mctx->r; mctx->r ^= ror32(mctx->l, 2); mctx->l += mctx->r; } static void michael_mic_hdr(struct michael_mic_ctx *mctx, const u8 *key, struct ieee80211_hdr *hdr) { u8 *da, *sa, tid; da = ieee80211_get_DA(hdr); sa = ieee80211_get_SA(hdr); if (ieee80211_is_data_qos(hdr->frame_control)) tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; else tid = 0; mctx->l = get_unaligned_le32(key); mctx->r = get_unaligned_le32(key + 4); /* * A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC * calculation, but it is _not_ transmitted */ michael_block(mctx, get_unaligned_le32(da)); michael_block(mctx, get_unaligned_le16(&da[4]) | (get_unaligned_le16(sa) << 16)); michael_block(mctx, get_unaligned_le32(&sa[2])); michael_block(mctx, tid); } void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, const u8 *data, size_t data_len, u8 *mic) { u32 val; size_t block, blocks, left; struct michael_mic_ctx mctx; michael_mic_hdr(&mctx, key, hdr); /* Real data */ blocks = data_len / 4; left = data_len % 4; for (block = 0; block < blocks; block++) michael_block(&mctx, get_unaligned_le32(&data[block * 4])); /* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make * total length a multiple of 4. */ val = 0x5a; while (left > 0) { val <<= 8; left--; val |= data[blocks * 4 + left]; } michael_block(&mctx, val); michael_block(&mctx, 0); put_unaligned_le32(mctx.l, mic); put_unaligned_le32(mctx.r, mic + 4); } compat-drivers-2012-09-18/net/mac80211/wme.h0000644000175000017500000000135412026211315017246 0ustar mcgrofmcgrof/* * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef _WME_H #define _WME_H #include #include "ieee80211_i.h" extern const int ieee802_1d_to_ac[8]; u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_hdr *hdr); u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); #endif /* _WME_H */ compat-drivers-2012-09-18/net/mac80211/status.c0000644000175000017500000004713712026211315020005 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2008-2010 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "ieee80211_i.h" #include "rate.h" #include "mesh.h" #include "led.h" #include "wme.h" void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int tmp; skb->pkt_type = IEEE80211_TX_STATUS_MSG; skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ? &local->skb_queue : &local->skb_queue_unreliable, skb); tmp = skb_queue_len(&local->skb_queue) + skb_queue_len(&local->skb_queue_unreliable); while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT && (skb = skb_dequeue(&local->skb_queue_unreliable))) { dev_kfree_skb_irq(skb); tmp--; I802_DEBUG_INC(local->tx_status_drop); } tasklet_schedule(&local->tasklet); } EXPORT_SYMBOL(ieee80211_tx_status_irqsafe); static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; int ac; /* * This skb 'survived' a round-trip through the driver, and * hopefully the driver didn't mangle it too badly. However, * we can definitely not rely on the control information * being correct. Clear it so we don't get junk there, and * indicate that it needs new processing, but must not be * modified/encrypted again. */ memset(&info->control, 0, sizeof(info->control)); info->control.jiffies = jiffies; info->control.vif = &sta->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING | IEEE80211_TX_INTFL_RETRANSMISSION; info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS; sta->tx_filtered_count++; /* * Clear more-data bit on filtered frames, it might be set * but later frames might time out so it might have to be * clear again ... It's all rather unlikely (this frame * should time out first, right?) but let's not confuse * peers unnecessarily. */ if (hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); int tid = *p & IEEE80211_QOS_CTL_TID_MASK; /* * Clear EOSP if set, this could happen e.g. * if an absence period (us being a P2P GO) * shortens the SP. */ if (*p & IEEE80211_QOS_CTL_EOSP) *p &= ~IEEE80211_QOS_CTL_EOSP; ac = ieee802_1d_to_ac[tid & 7]; } else { ac = IEEE80211_AC_BE; } /* * Clear the TX filter mask for this STA when sending the next * packet. If the STA went to power save mode, this will happen * when it wakes up for the next time. */ set_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT); /* * This code races in the following way: * * (1) STA sends frame indicating it will go to sleep and does so * (2) hardware/firmware adds STA to filter list, passes frame up * (3) hardware/firmware processes TX fifo and suppresses a frame * (4) we get TX status before having processed the frame and * knowing that the STA has gone to sleep. * * This is actually quite unlikely even when both those events are * processed from interrupts coming in quickly after one another or * even at the same time because we queue both TX status events and * RX frames to be processed by a tasklet and process them in the * same order that they were received or TX status last. Hence, there * is no race as long as the frame RX is processed before the next TX * status, which drivers can ensure, see below. * * Note that this can only happen if the hardware or firmware can * actually add STAs to the filter list, if this is done by the * driver in response to set_tim() (which will only reduce the race * this whole filtering tries to solve, not completely solve it) * this situation cannot happen. * * To completely solve this race drivers need to make sure that they * (a) don't mix the irq-safe/not irq-safe TX status/RX processing * functions and * (b) always process RX events before TX status events if ordering * can be unknown, for example with different interrupt status * bits. * (c) if PS mode transitions are manual (i.e. the flag * %IEEE80211_HW_AP_LINK_PS is set), always process PS state * changes before calling TX status events if ordering can be * unknown. */ if (test_sta_flag(sta, WLAN_STA_PS_STA) && skb_queue_len(&sta->tx_filtered[ac]) < STA_MAX_TX_BUFFER) { skb_queue_tail(&sta->tx_filtered[ac], skb); sta_info_recalc_tim(sta); if (!timer_pending(&local->sta_cleanup)) mod_timer(&local->sta_cleanup, round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); return; } if (!test_sta_flag(sta, WLAN_STA_PS_STA) && !(info->flags & IEEE80211_TX_INTFL_RETRIED)) { /* Software retry the packet once */ info->flags |= IEEE80211_TX_INTFL_RETRIED; ieee80211_add_pending_skb(local, skb); return; } ps_dbg_ratelimited(sta->sdata, "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", skb_queue_len(&sta->tx_filtered[ac]), !!test_sta_flag(sta, WLAN_STA_PS_STA), jiffies); dev_kfree_skb(skb); } static void ieee80211_check_pending_bar(struct sta_info *sta, u8 *addr, u8 tid) { struct tid_ampdu_tx *tid_tx; tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); if (!tid_tx || !tid_tx->bar_pending) return; tid_tx->bar_pending = false; ieee80211_send_bar(&sta->sdata->vif, addr, tid, tid_tx->failed_bar_ssn); } static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt = (void *) skb->data; struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; if (ieee80211_is_data_qos(mgmt->frame_control)) { struct ieee80211_hdr *hdr = (void *) skb->data; u8 *qc = ieee80211_get_qos_ctl(hdr); u16 tid = qc[0] & 0xf; ieee80211_check_pending_bar(sta, hdr->addr1, tid); } if (ieee80211_is_action(mgmt->frame_control) && sdata->vif.type == NL80211_IFTYPE_STATION && mgmt->u.action.category == WLAN_CATEGORY_HT && mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) { /* * This update looks racy, but isn't -- if we come * here we've definitely got a station that we're * talking to, and on a managed interface that can * only be the AP. And the only other place updating * this variable is before we're associated. */ switch (mgmt->u.action.u.ht_smps.smps_control) { case WLAN_HT_SMPS_CONTROL_DYNAMIC: sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC; break; case WLAN_HT_SMPS_CONTROL_STATIC: sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC; break; case WLAN_HT_SMPS_CONTROL_DISABLED: default: /* shouldn't happen since we don't send that */ sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF; break; } ieee80211_queue_work(&local->hw, &local->recalc_smps); } } static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn) { struct tid_ampdu_tx *tid_tx; tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); if (!tid_tx) return; tid_tx->failed_bar_ssn = ssn; tid_tx->bar_pending = true; } static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info) { int len = sizeof(struct ieee80211_radiotap_header); /* IEEE80211_RADIOTAP_RATE rate */ if (info->status.rates[0].idx >= 0 && !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) len += 2; /* IEEE80211_RADIOTAP_TX_FLAGS */ len += 2; /* IEEE80211_RADIOTAP_DATA_RETRIES */ len += 1; /* IEEE80211_TX_RC_MCS */ if (info->status.rates[0].idx >= 0 && info->status.rates[0].flags & IEEE80211_TX_RC_MCS) len += 3; return len; } static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband, struct sk_buff *skb, int retry_count, int rtap_len) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_radiotap_header *rthdr; unsigned char *pos; u16 txflags; rthdr = (struct ieee80211_radiotap_header *) skb_push(skb, rtap_len); memset(rthdr, 0, rtap_len); rthdr->it_len = cpu_to_le16(rtap_len); rthdr->it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) | (1 << IEEE80211_RADIOTAP_DATA_RETRIES)); pos = (unsigned char *)(rthdr + 1); /* * XXX: Once radiotap gets the bitmap reset thing the vendor * extensions proposal contains, we can actually report * the whole set of tries we did. */ /* IEEE80211_RADIOTAP_RATE */ if (info->status.rates[0].idx >= 0 && !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) { rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5; /* padding for tx flags */ pos += 2; } /* IEEE80211_RADIOTAP_TX_FLAGS */ txflags = 0; if (!(info->flags & IEEE80211_TX_STAT_ACK) && !is_multicast_ether_addr(hdr->addr1)) txflags |= IEEE80211_RADIOTAP_F_TX_FAIL; if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) txflags |= IEEE80211_RADIOTAP_F_TX_CTS; else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) txflags |= IEEE80211_RADIOTAP_F_TX_RTS; put_unaligned_le16(txflags, pos); pos += 2; /* IEEE80211_RADIOTAP_DATA_RETRIES */ /* for now report the total retry_count */ *pos = retry_count; pos++; /* IEEE80211_TX_RC_MCS */ if (info->status.rates[0].idx >= 0 && info->status.rates[0].flags & IEEE80211_TX_RC_MCS) { rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_BW; if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) pos[1] |= IEEE80211_RADIOTAP_MCS_SGI; if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) pos[1] |= IEEE80211_RADIOTAP_MCS_BW_40; if (info->status.rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD) pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF; pos[2] = info->status.rates[0].idx; pos += 3; } } /* * Use a static threshold for now, best value to be determined * by testing ... * Should it depend on: * - on # of retransmissions * - current throughput (higher value for higher tpt)? */ #define STA_LOST_PKT_THRESHOLD 50 void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) { struct sk_buff *skb2; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); __le16 fc; struct ieee80211_supported_band *sband; struct ieee80211_sub_if_data *sdata; struct net_device *prev_dev = NULL; struct sta_info *sta, *tmp; int retry_count = -1, i; int rates_idx = -1; bool send_to_cooked; bool acked; struct ieee80211_bar *bar; int rtap_len; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if ((info->flags & IEEE80211_TX_CTL_AMPDU) && !(info->flags & IEEE80211_TX_STAT_AMPDU)) { /* just the first aggr frame carry status info */ info->status.rates[i].idx = -1; info->status.rates[i].count = 0; break; } else if (info->status.rates[i].idx < 0) { break; } else if (i >= hw->max_report_rates) { /* the HW cannot have attempted that rate */ info->status.rates[i].idx = -1; info->status.rates[i].count = 0; break; } retry_count += info->status.rates[i].count; } rates_idx = i - 1; if (retry_count < 0) retry_count = 0; rcu_read_lock(); sband = local->hw.wiphy->bands[info->band]; fc = hdr->frame_control; for_each_sta_info(local, hdr->addr1, sta, tmp) { /* skip wrong virtual interface */ if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) continue; if (info->flags & IEEE80211_TX_STATUS_EOSP) clear_sta_flag(sta, WLAN_STA_SP); acked = !!(info->flags & IEEE80211_TX_STAT_ACK); if (!acked && test_sta_flag(sta, WLAN_STA_PS_STA)) { /* * The STA is in power save mode, so assume * that this TX packet failed because of that. */ ieee80211_handle_filtered_frame(local, sta, skb); rcu_read_unlock(); return; } if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) && (rates_idx != -1)) sta->last_tx_rate = info->status.rates[rates_idx]; if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) && (ieee80211_is_data_qos(fc))) { u16 tid, ssn; u8 *qc; qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10) & IEEE80211_SCTL_SEQ); ieee80211_send_bar(&sta->sdata->vif, hdr->addr1, tid, ssn); } if (!acked && ieee80211_is_back_req(fc)) { u16 tid, control; /* * BAR failed, store the last SSN and retry sending * the BAR when the next unicast transmission on the * same TID succeeds. */ bar = (struct ieee80211_bar *) skb->data; control = le16_to_cpu(bar->control); if (!(control & IEEE80211_BAR_CTRL_MULTI_TID)) { u16 ssn = le16_to_cpu(bar->start_seq_num); tid = (control & IEEE80211_BAR_CTRL_TID_INFO_MASK) >> IEEE80211_BAR_CTRL_TID_INFO_SHIFT; ieee80211_set_bar_pending(sta, tid, ssn); } } if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { ieee80211_handle_filtered_frame(local, sta, skb); rcu_read_unlock(); return; } else { if (!acked) sta->tx_retry_failed++; sta->tx_retry_count += retry_count; } rate_control_tx_status(local, sband, sta, skb); if (ieee80211_vif_is_mesh(&sta->sdata->vif)) ieee80211s_update_metric(local, sta, skb); if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked) ieee80211_frame_acked(sta, skb); if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) && (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked); if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { if (info->flags & IEEE80211_TX_STAT_ACK) { if (sta->lost_packets) sta->lost_packets = 0; } else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) { cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, sta->lost_packets, GFP_ATOMIC); sta->lost_packets = 0; } } } rcu_read_unlock(); ieee80211_led_tx(local, 0); /* SNMP counters * Fragments are passed to low-level drivers as separate skbs, so these * are actually fragments, not frames. Update frame counters only for * the first fragment of the frame. */ if (info->flags & IEEE80211_TX_STAT_ACK) { if (ieee80211_is_first_frag(hdr->seq_ctrl)) { local->dot11TransmittedFrameCount++; if (is_multicast_ether_addr(hdr->addr1)) local->dot11MulticastTransmittedFrameCount++; if (retry_count > 0) local->dot11RetryCount++; if (retry_count > 1) local->dot11MultipleRetryCount++; } /* This counter shall be incremented for an acknowledged MPDU * with an individual address in the address 1 field or an MPDU * with a multicast address in the address 1 field of type Data * or Management. */ if (!is_multicast_ether_addr(hdr->addr1) || ieee80211_is_data(fc) || ieee80211_is_mgmt(fc)) local->dot11TransmittedFragmentCount++; } else { if (ieee80211_is_first_frag(hdr->seq_ctrl)) local->dot11FailedCount++; } if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) && (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) && !(info->flags & IEEE80211_TX_CTL_INJECTED) && local->ps_sdata && !(local->scanning)) { if (info->flags & IEEE80211_TX_STAT_ACK) { local->ps_sdata->u.mgd.flags |= IEEE80211_STA_NULLFUNC_ACKED; } else mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(10)); } if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { u64 cookie = (unsigned long)skb; acked = info->flags & IEEE80211_TX_STAT_ACK; if (ieee80211_is_nullfunc(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) { cfg80211_probe_status(skb->dev, hdr->addr1, cookie, acked, GFP_ATOMIC); } else if (skb->dev) { cfg80211_mgmt_tx_status( skb->dev->ieee80211_ptr, cookie, skb->data, skb->len, acked, GFP_ATOMIC); } else { struct ieee80211_sub_if_data *p2p_sdata; rcu_read_lock(); p2p_sdata = rcu_dereference(local->p2p_sdata); if (p2p_sdata) { cfg80211_mgmt_tx_status( &p2p_sdata->wdev, cookie, skb->data, skb->len, acked, GFP_ATOMIC); } rcu_read_unlock(); } } if (unlikely(info->ack_frame_id)) { struct sk_buff *ack_skb; unsigned long flags; spin_lock_irqsave(&local->ack_status_lock, flags); ack_skb = idr_find(&local->ack_status_frames, info->ack_frame_id); if (ack_skb) idr_remove(&local->ack_status_frames, info->ack_frame_id); spin_unlock_irqrestore(&local->ack_status_lock, flags); /* consumes ack_skb */ if (ack_skb) skb_complete_wifi_ack(ack_skb, info->flags & IEEE80211_TX_STAT_ACK); } /* this was a transmitted frame, but now we want to reuse it */ skb_orphan(skb); /* Need to make a copy before skb->cb gets cleared */ send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) || !(ieee80211_is_data(fc)); /* * This is a bit racy but we can avoid a lot of work * with this test... */ if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) { dev_kfree_skb(skb); return; } /* send frame to monitor interfaces now */ rtap_len = ieee80211_tx_radiotap_len(info); if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) { pr_err("ieee80211_tx_status: headroom too small\n"); dev_kfree_skb(skb); return; } ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len); /* XXX: is this sufficient for BPF? */ skb_set_mac_header(skb, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); memset(skb->cb, 0, sizeof(skb->cb)); rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { if (!ieee80211_sdata_running(sdata)) continue; if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) && !send_to_cooked) continue; if (prev_dev) { skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { skb2->dev = prev_dev; netif_rx(skb2); } } prev_dev = sdata->dev; } } if (prev_dev) { skb->dev = prev_dev; netif_rx(skb); skb = NULL; } rcu_read_unlock(); dev_kfree_skb(skb); } EXPORT_SYMBOL(ieee80211_tx_status); void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, num_packets, GFP_ATOMIC); } EXPORT_SYMBOL(ieee80211_report_low_ack); void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (unlikely(info->ack_frame_id)) { struct sk_buff *ack_skb; unsigned long flags; spin_lock_irqsave(&local->ack_status_lock, flags); ack_skb = idr_find(&local->ack_status_frames, info->ack_frame_id); if (ack_skb) idr_remove(&local->ack_status_frames, info->ack_frame_id); spin_unlock_irqrestore(&local->ack_status_lock, flags); /* consumes ack_skb */ if (ack_skb) dev_kfree_skb_any(ack_skb); } dev_kfree_skb_any(skb); } EXPORT_SYMBOL(ieee80211_free_txskb); compat-drivers-2012-09-18/net/mac80211/key.h0000644000175000017500000000755012026211315017252 0ustar mcgrofmcgrof/* * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef IEEE80211_KEY_H #define IEEE80211_KEY_H #include #include #include #include #include #define NUM_DEFAULT_KEYS 4 #define NUM_DEFAULT_MGMT_KEYS 2 #define WEP_IV_LEN 4 #define WEP_ICV_LEN 4 #define ALG_CCMP_KEY_LEN 16 #define CCMP_HDR_LEN 8 #define CCMP_MIC_LEN 8 #define CCMP_TK_LEN 16 #define CCMP_PN_LEN 6 #define TKIP_IV_LEN 8 #define TKIP_ICV_LEN 4 #define CMAC_PN_LEN 6 #define NUM_RX_DATA_QUEUES 16 struct ieee80211_local; struct ieee80211_sub_if_data; struct sta_info; /** * enum ieee80211_internal_key_flags - internal key flags * * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present * in the hardware for TX crypto hardware acceleration. * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped. */ enum ieee80211_internal_key_flags { KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), KEY_FLAG_TAINTED = BIT(1), }; enum ieee80211_internal_tkip_state { TKIP_STATE_NOT_INIT, TKIP_STATE_PHASE1_DONE, TKIP_STATE_PHASE1_HW_UPLOADED, }; struct tkip_ctx { u32 iv32; /* current iv32 */ u16 iv16; /* current iv16 */ u16 p1k[5]; /* p1k cache */ u32 p1k_iv32; /* iv32 for which p1k computed */ enum ieee80211_internal_tkip_state state; }; struct ieee80211_key { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; /* for sdata list */ struct list_head list; /* protected by key mutex */ unsigned int flags; union { struct { /* protects tx context */ spinlock_t txlock; /* last used TSC */ struct tkip_ctx tx; /* last received RSC */ struct tkip_ctx rx[NUM_RX_DATA_QUEUES]; } tkip; struct { atomic64_t tx_pn; /* * Last received packet number. The first * NUM_RX_DATA_QUEUES counters are used with Data * frames and the last counter is used with Robust * Management frames. */ u8 rx_pn[NUM_RX_DATA_QUEUES + 1][CCMP_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ } ccmp; struct { atomic64_t tx_pn; u8 rx_pn[CMAC_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCMACReplays */ u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ } aes_cmac; } u; /* number of times this key has been used */ int tx_rx_count; #ifdef CONFIG_MAC80211_DEBUGFS struct { struct dentry *stalink; struct dentry *dir; int cnt; } debugfs; #endif /* * key config, must be last because it contains key * material as variable length member */ struct ieee80211_key_conf conf; }; struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, const u8 *key_data, size_t seq_len, const u8 *seq); /* * Insert a key into data structures (sdata, sta if necessary) * to make it used, free old key. */ int __must_check ieee80211_key_link(struct ieee80211_key *key, struct ieee80211_sub_if_data *sdata, struct sta_info *sta); void __ieee80211_key_free(struct ieee80211_key *key); void ieee80211_key_free(struct ieee80211_local *local, struct ieee80211_key *key); void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, bool uni, bool multi); void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx); void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata); #define key_mtx_dereference(local, ref) \ rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx))) #endif /* IEEE80211_KEY_H */ compat-drivers-2012-09-18/net/mac80211/rc80211_minstrel_debugfs.c0000644000175000017500000001127312026211315023066 0ustar mcgrofmcgrof/* * Copyright (C) 2008 Felix Fietkau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Based on minstrel.c: * Copyright (C) 2005-2007 Derek Smithies * Sponsored by Indranet Technologies Ltd * * Based on sample.c: * Copyright (c) 2005 John Bicket * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include #include #include #include #include #include "rc80211_minstrel.h" int minstrel_stats_open(struct inode *inode, struct file *file) { struct minstrel_sta_info *mi = inode->i_private; struct minstrel_debugfs_info *ms; unsigned int i, tp, prob, eprob; char *p; ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL); if (!ms) return -ENOMEM; file->private_data = ms; p = ms->buf; p += sprintf(p, "rate throughput ewma prob this prob " "this succ/attempt success attempts\n"); for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; *(p++) = (i == mi->max_tp_rate) ? 'T' : ' '; *(p++) = (i == mi->max_tp_rate2) ? 't' : ' '; *(p++) = (i == mi->max_prob_rate) ? 'P' : ' '; p += sprintf(p, "%3u%s", mr->bitrate / 2, (mr->bitrate & 1 ? ".5" : " ")); tp = mr->cur_tp / ((18000 << 10) / 96); prob = mr->cur_prob / 18; eprob = mr->probability / 18; p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " "%3u(%3u) %8llu %8llu\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, mr->last_success, mr->last_attempts, (unsigned long long)mr->succ_hist, (unsigned long long)mr->att_hist); } p += sprintf(p, "\nTotal packet count:: ideal %d " "lookaround %d\n\n", mi->packet_count - mi->sample_count, mi->sample_count); ms->len = p - ms->buf; return 0; } ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { struct minstrel_debugfs_info *ms; ms = file->private_data; return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len); } int minstrel_stats_release(struct inode *inode, struct file *file) { kfree(file->private_data); return 0; } static const struct file_operations minstrel_stat_fops = { .owner = THIS_MODULE, .open = minstrel_stats_open, .read = minstrel_stats_read, .release = minstrel_stats_release, .llseek = default_llseek, }; void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) { struct minstrel_sta_info *mi = priv_sta; mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi, &minstrel_stat_fops); } void minstrel_remove_sta_debugfs(void *priv, void *priv_sta) { struct minstrel_sta_info *mi = priv_sta; debugfs_remove(mi->dbg_stats); } compat-drivers-2012-09-18/net/mac80211/led.c0000644000175000017500000001767112026211315017226 0ustar mcgrofmcgrof/* * Copyright 2006, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* just for IFNAMSIZ */ #include #include #include #include "led.h" void ieee80211_led_rx(struct ieee80211_local *local) { if (unlikely(!local->rx_led)) return; if (local->rx_led_counter++ % 2 == 0) led_trigger_event(local->rx_led, LED_OFF); else led_trigger_event(local->rx_led, LED_FULL); } /* q is 1 if a packet was enqueued, 0 if it has been transmitted */ void ieee80211_led_tx(struct ieee80211_local *local, int q) { if (unlikely(!local->tx_led)) return; /* not sure how this is supposed to work ... */ local->tx_led_counter += 2*q-1; if (local->tx_led_counter % 2 == 0) led_trigger_event(local->tx_led, LED_OFF); else led_trigger_event(local->tx_led, LED_FULL); } void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) { if (unlikely(!local->assoc_led)) return; if (associated) led_trigger_event(local->assoc_led, LED_FULL); else led_trigger_event(local->assoc_led, LED_OFF); } void ieee80211_led_radio(struct ieee80211_local *local, bool enabled) { if (unlikely(!local->radio_led)) return; if (enabled) led_trigger_event(local->radio_led, LED_FULL); else led_trigger_event(local->radio_led, LED_OFF); } void ieee80211_led_names(struct ieee80211_local *local) { snprintf(local->rx_led_name, sizeof(local->rx_led_name), "%srx", wiphy_name(local->hw.wiphy)); snprintf(local->tx_led_name, sizeof(local->tx_led_name), "%stx", wiphy_name(local->hw.wiphy)); snprintf(local->assoc_led_name, sizeof(local->assoc_led_name), "%sassoc", wiphy_name(local->hw.wiphy)); snprintf(local->radio_led_name, sizeof(local->radio_led_name), "%sradio", wiphy_name(local->hw.wiphy)); } void ieee80211_led_init(struct ieee80211_local *local) { local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); if (local->rx_led) { local->rx_led->name = local->rx_led_name; if (led_trigger_register(local->rx_led)) { kfree(local->rx_led); local->rx_led = NULL; } } local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); if (local->tx_led) { local->tx_led->name = local->tx_led_name; if (led_trigger_register(local->tx_led)) { kfree(local->tx_led); local->tx_led = NULL; } } local->assoc_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); if (local->assoc_led) { local->assoc_led->name = local->assoc_led_name; if (led_trigger_register(local->assoc_led)) { kfree(local->assoc_led); local->assoc_led = NULL; } } local->radio_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); if (local->radio_led) { local->radio_led->name = local->radio_led_name; if (led_trigger_register(local->radio_led)) { kfree(local->radio_led); local->radio_led = NULL; } } if (local->tpt_led_trigger) { if (led_trigger_register(&local->tpt_led_trigger->trig)) { kfree(local->tpt_led_trigger); local->tpt_led_trigger = NULL; } } } void ieee80211_led_exit(struct ieee80211_local *local) { if (local->radio_led) { led_trigger_unregister(local->radio_led); kfree(local->radio_led); } if (local->assoc_led) { led_trigger_unregister(local->assoc_led); kfree(local->assoc_led); } if (local->tx_led) { led_trigger_unregister(local->tx_led); kfree(local->tx_led); } if (local->rx_led) { led_trigger_unregister(local->rx_led); kfree(local->rx_led); } if (local->tpt_led_trigger) { led_trigger_unregister(&local->tpt_led_trigger->trig); kfree(local->tpt_led_trigger); } } char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); return local->radio_led_name; } EXPORT_SYMBOL(__ieee80211_get_radio_led_name); char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); return local->assoc_led_name; } EXPORT_SYMBOL(__ieee80211_get_assoc_led_name); char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); return local->tx_led_name; } EXPORT_SYMBOL(__ieee80211_get_tx_led_name); char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); return local->rx_led_name; } EXPORT_SYMBOL(__ieee80211_get_rx_led_name); static unsigned long tpt_trig_traffic(struct ieee80211_local *local, struct tpt_led_trigger *tpt_trig) { unsigned long traffic, delta; traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes; delta = traffic - tpt_trig->prev_traffic; tpt_trig->prev_traffic = traffic; return DIV_ROUND_UP(delta, 1024 / 8); } static void tpt_trig_timer(unsigned long data) { struct ieee80211_local *local = (void *)data; struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; struct led_classdev *led_cdev; unsigned long on, off, tpt; int i; if (!tpt_trig->running) return; mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); tpt = tpt_trig_traffic(local, tpt_trig); /* default to just solid on */ on = 1; off = 0; for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) { if (tpt_trig->blink_table[i].throughput < 0 || tpt > tpt_trig->blink_table[i].throughput) { off = tpt_trig->blink_table[i].blink_time / 2; on = tpt_trig->blink_table[i].blink_time - off; break; } } read_lock(&tpt_trig->trig.leddev_list_lock); list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) led_blink_set(led_cdev, &on, &off); read_unlock(&tpt_trig->trig.leddev_list_lock); } char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags, const struct ieee80211_tpt_blink *blink_table, unsigned int blink_table_len) { struct ieee80211_local *local = hw_to_local(hw); struct tpt_led_trigger *tpt_trig; if (WARN_ON(local->tpt_led_trigger)) return NULL; tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL); if (!tpt_trig) return NULL; snprintf(tpt_trig->name, sizeof(tpt_trig->name), "%stpt", wiphy_name(local->hw.wiphy)); tpt_trig->trig.name = tpt_trig->name; tpt_trig->blink_table = blink_table; tpt_trig->blink_table_len = blink_table_len; tpt_trig->want = flags; setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local); local->tpt_led_trigger = tpt_trig; return tpt_trig->name; } EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger); static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local) { struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; if (tpt_trig->running) return; /* reset traffic */ tpt_trig_traffic(local, tpt_trig); tpt_trig->running = true; tpt_trig_timer((unsigned long)local); mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); } static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) { struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; struct led_classdev *led_cdev; if (!tpt_trig->running) return; tpt_trig->running = false; del_timer_sync(&tpt_trig->timer); read_lock(&tpt_trig->trig.leddev_list_lock); list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) led_set_brightness(led_cdev, LED_OFF); read_unlock(&tpt_trig->trig.leddev_list_lock); } void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, unsigned int types_on, unsigned int types_off) { struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; bool allowed; WARN_ON(types_on & types_off); if (!tpt_trig) return; tpt_trig->active &= ~types_off; tpt_trig->active |= types_on; /* * Regardless of wanted state, we shouldn't blink when * the radio is disabled -- this can happen due to some * code ordering issues with __ieee80211_recalc_idle() * being called before the radio is started. */ allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO; if (!allowed || !(tpt_trig->active & tpt_trig->want)) ieee80211_stop_tpt_led_trig(local); else ieee80211_start_tpt_led_trig(local); } compat-drivers-2012-09-18/net/mac80211/wme.c0000644000175000017500000001070112026211315017235 0ustar mcgrofmcgrof/* * Copyright 2004, Instant802 Networks, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "wme.h" /* Default mapping in classifier to work with default * queue setup. */ const int ieee802_1d_to_ac[8] = { IEEE80211_AC_BE, IEEE80211_AC_BK, IEEE80211_AC_BK, IEEE80211_AC_BE, IEEE80211_AC_VI, IEEE80211_AC_VI, IEEE80211_AC_VO, IEEE80211_AC_VO }; static int wme_downgrade_ac(struct sk_buff *skb) { switch (skb->priority) { case 6: case 7: skb->priority = 5; /* VO -> VI */ return 0; case 4: case 5: skb->priority = 3; /* VI -> BE */ return 0; case 0: case 3: skb->priority = 2; /* BE -> BK */ return 0; default: return -1; } } static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { /* in case we are a client verify acm is not set for this ac */ while (unlikely(sdata->wmm_acm & BIT(skb->priority))) { if (wme_downgrade_ac(skb)) { /* * This should not really happen. The AP has marked all * lower ACs to require admission control which is not * a reasonable configuration. Allow the frame to be * transmitted using AC_BK as a workaround. */ break; } } /* look up which queue to use for frames with this 1d tag */ return ieee802_1d_to_ac[skb->priority]; } /* Indicate which queue to use for this fully formed 802.11 frame */ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_hdr *hdr) { struct ieee80211_local *local = sdata->local; u8 *p; if (local->hw.queues < IEEE80211_NUM_ACS) return 0; if (!ieee80211_is_data(hdr->frame_control)) { skb->priority = 7; return ieee802_1d_to_ac[skb->priority]; } if (!ieee80211_is_data_qos(hdr->frame_control)) { skb->priority = 0; return ieee802_1d_to_ac[skb->priority]; } p = ieee80211_get_qos_ctl(hdr); skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; return ieee80211_downgrade_queue(sdata, skb); } /* Indicate which queue to use. */ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct sta_info *sta = NULL; const u8 *ra = NULL; bool qos = false; if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) { skb->priority = 0; /* required for correct WPA/11i MIC */ return 0; } rcu_read_lock(); switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: sta = rcu_dereference(sdata->u.vlan.sta); if (sta) { qos = test_sta_flag(sta, WLAN_STA_WME); break; } case NL80211_IFTYPE_AP: ra = skb->data; break; case NL80211_IFTYPE_WDS: ra = sdata->u.wds.remote_addr; break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: qos = true; break; #endif case NL80211_IFTYPE_STATION: ra = sdata->u.mgd.bssid; break; case NL80211_IFTYPE_ADHOC: ra = skb->data; break; default: break; } if (!sta && ra && !is_multicast_ether_addr(ra)) { sta = sta_info_get(sdata, ra); if (sta) qos = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); if (!qos) { skb->priority = 0; /* required for correct WPA/11i MIC */ return IEEE80211_AC_BE; } /* use the data classifier to determine what 802.1d tag the * data frame has */ skb->priority = cfg80211_classify8021d(skb); return ieee80211_downgrade_queue(sdata, skb); } void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); /* Fill in the QoS header if there is one. */ if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); u8 ack_policy, tid; tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; /* preserve EOSP bit */ ack_policy = *p & IEEE80211_QOS_CTL_EOSP; if (is_multicast_ether_addr(hdr->addr1) || sdata->noack_map & BIT(tid)) { ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; info->flags |= IEEE80211_TX_CTL_NO_ACK; } /* qos header is 2 bytes */ *p++ = ack_policy | tid; *p = ieee80211_vif_is_mesh(&sdata->vif) ? (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0; } } compat-drivers-2012-09-18/net/mac80211/aes_cmac.h0000644000175000017500000000107612026211315020212 0ustar mcgrofmcgrof/* * Copyright 2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef AES_CMAC_H #define AES_CMAC_H #include struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]); void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, const u8 *data, size_t data_len, u8 *mic); void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm); #endif /* AES_CMAC_H */ compat-drivers-2012-09-18/net/mac80211/aes_ccm.c0000644000175000017500000000670212026211315020045 0ustar mcgrofmcgrof/* * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "key.h" #include "aes_ccm.h" static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) { int i; u8 *b_0, *aad, *b, *s_0; b_0 = scratch + 3 * AES_BLOCK_SIZE; aad = scratch + 4 * AES_BLOCK_SIZE; b = scratch; s_0 = scratch + AES_BLOCK_SIZE; crypto_cipher_encrypt_one(tfm, b, b_0); /* Extra Authenticate-only data (always two AES blocks) */ for (i = 0; i < AES_BLOCK_SIZE; i++) aad[i] ^= b[i]; crypto_cipher_encrypt_one(tfm, b, aad); aad += AES_BLOCK_SIZE; for (i = 0; i < AES_BLOCK_SIZE; i++) aad[i] ^= b[i]; crypto_cipher_encrypt_one(tfm, a, aad); /* Mask out bits from auth-only-b_0 */ b_0[0] &= 0x07; /* S_0 is used to encrypt T (= MIC) */ b_0[14] = 0; b_0[15] = 0; crypto_cipher_encrypt_one(tfm, s_0, b_0); } void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *data, size_t data_len, u8 *cdata, u8 *mic) { int i, j, last_len, num_blocks; u8 *pos, *cpos, *b, *s_0, *e, *b_0; b = scratch; s_0 = scratch + AES_BLOCK_SIZE; e = scratch + 2 * AES_BLOCK_SIZE; b_0 = scratch + 3 * AES_BLOCK_SIZE; num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); last_len = data_len % AES_BLOCK_SIZE; aes_ccm_prepare(tfm, scratch, b); /* Process payload blocks */ pos = data; cpos = cdata; for (j = 1; j <= num_blocks; j++) { int blen = (j == num_blocks && last_len) ? last_len : AES_BLOCK_SIZE; /* Authentication followed by encryption */ for (i = 0; i < blen; i++) b[i] ^= pos[i]; crypto_cipher_encrypt_one(tfm, b, b); b_0[14] = (j >> 8) & 0xff; b_0[15] = j & 0xff; crypto_cipher_encrypt_one(tfm, e, b_0); for (i = 0; i < blen; i++) *cpos++ = *pos++ ^ e[i]; } for (i = 0; i < CCMP_MIC_LEN; i++) mic[i] = b[i] ^ s_0[i]; } int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *cdata, size_t data_len, u8 *mic, u8 *data) { int i, j, last_len, num_blocks; u8 *pos, *cpos, *b, *s_0, *a, *b_0; b = scratch; s_0 = scratch + AES_BLOCK_SIZE; a = scratch + 2 * AES_BLOCK_SIZE; b_0 = scratch + 3 * AES_BLOCK_SIZE; num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); last_len = data_len % AES_BLOCK_SIZE; aes_ccm_prepare(tfm, scratch, a); /* Process payload blocks */ cpos = cdata; pos = data; for (j = 1; j <= num_blocks; j++) { int blen = (j == num_blocks && last_len) ? last_len : AES_BLOCK_SIZE; /* Decryption followed by authentication */ b_0[14] = (j >> 8) & 0xff; b_0[15] = j & 0xff; crypto_cipher_encrypt_one(tfm, b, b_0); for (i = 0; i < blen; i++) { *pos = *cpos++ ^ b[i]; a[i] ^= *pos++; } crypto_cipher_encrypt_one(tfm, a, a); } for (i = 0; i < CCMP_MIC_LEN; i++) { if ((mic[i] ^ s_0[i]) != a[i]) return -1; } return 0; } struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) { struct crypto_cipher *tfm; tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); if (!IS_ERR(tfm)) crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN); return tfm; } void ieee80211_aes_key_free(struct crypto_cipher *tfm) { crypto_free_cipher(tfm); } compat-drivers-2012-09-18/net/mac80211/spectmgmt.c0000644000175000017500000000551312026211315020455 0ustar mcgrofmcgrof/* * spectrum management * * Copyright 2003, Jouni Malinen * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2007-2008, Intel Corporation * Copyright 2008, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "ieee80211_i.h" #include "sta_info.h" #include "wme.h" static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata, struct ieee80211_msrment_ie *request_ie, const u8 *da, const u8 *bssid, u8 dialog_token) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *msr_report; skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + sizeof(struct ieee80211_msrment_ie)); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); memset(msr_report, 0, 24); memcpy(msr_report->da, da, ETH_ALEN); memcpy(msr_report->sa, sdata->vif.addr, ETH_ALEN); memcpy(msr_report->bssid, bssid, ETH_ALEN); msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; msr_report->u.action.u.measurement.action_code = WLAN_ACTION_SPCT_MSR_RPRT; msr_report->u.action.u.measurement.dialog_token = dialog_token; msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; msr_report->u.action.u.measurement.length = sizeof(struct ieee80211_msrment_ie); memset(&msr_report->u.action.u.measurement.msr_elem, 0, sizeof(struct ieee80211_msrment_ie)); msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; msr_report->u.action.u.measurement.msr_elem.mode |= IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; ieee80211_tx_skb(sdata, skb); } void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { /* * Ignoring measurement request is spec violation. * Mandatory measurements must be reported optional * measurements might be refused or reported incapable * For now just refuse * TODO: Answer basic measurement as unmeasured */ ieee80211_send_refuse_measurement_request(sdata, &mgmt->u.action.u.measurement.msr_elem, mgmt->sa, mgmt->bssid, mgmt->u.action.u.measurement.dialog_token); } compat-drivers-2012-09-18/net/mac80211/mesh_pathtbl.c0000644000175000017500000007146112026211315021131 0ustar mcgrofmcgrof/* * Copyright (c) 2008, 2009 open80211s Ltd. * Author: Luis Carlos Cobo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include "wme.h" #include "ieee80211_i.h" #include "mesh.h" /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ #define INIT_PATHS_SIZE_ORDER 2 /* Keep the mean chain length below this constant */ #define MEAN_CHAIN_LEN 2 #define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \ time_after(jiffies, mpath->exp_time) && \ !(mpath->flags & MESH_PATH_FIXED)) struct mpath_node { struct hlist_node list; struct rcu_head rcu; /* This indirection allows two different tables to point to the same * mesh_path structure, useful when resizing */ struct mesh_path *mpath; }; static struct mesh_table __rcu *mesh_paths; static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ int mesh_paths_generation; /* This lock will have the grow table function as writer and add / delete nodes * as readers. RCU provides sufficient protection only when reading the table * (i.e. doing lookups). Adding or adding or removing nodes requires we take * the read lock or we risk operating on an old table. The write lock is only * needed when modifying the number of buckets a table. */ static DEFINE_RWLOCK(pathtbl_resize_lock); static inline struct mesh_table *resize_dereference_mesh_paths(void) { return rcu_dereference_protected(mesh_paths, lockdep_is_held(&pathtbl_resize_lock)); } static inline struct mesh_table *resize_dereference_mpp_paths(void) { return rcu_dereference_protected(mpp_paths, lockdep_is_held(&pathtbl_resize_lock)); } /* * CAREFUL -- "tbl" must not be an expression, * in particular not an rcu_dereference(), since * it's used twice. So it is illegal to do * for_each_mesh_entry(rcu_dereference(...), ...) */ #define for_each_mesh_entry(tbl, p, node, i) \ for (i = 0; i <= tbl->hash_mask; i++) \ hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list) static struct mesh_table *mesh_table_alloc(int size_order) { int i; struct mesh_table *newtbl; newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); if (!newtbl) return NULL; newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * (1 << size_order), GFP_ATOMIC); if (!newtbl->hash_buckets) { kfree(newtbl); return NULL; } newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * (1 << size_order), GFP_ATOMIC); if (!newtbl->hashwlock) { kfree(newtbl->hash_buckets); kfree(newtbl); return NULL; } newtbl->size_order = size_order; newtbl->hash_mask = (1 << size_order) - 1; atomic_set(&newtbl->entries, 0); get_random_bytes(&newtbl->hash_rnd, sizeof(newtbl->hash_rnd)); for (i = 0; i <= newtbl->hash_mask; i++) spin_lock_init(&newtbl->hashwlock[i]); spin_lock_init(&newtbl->gates_lock); return newtbl; } static void __mesh_table_free(struct mesh_table *tbl) { kfree(tbl->hash_buckets); kfree(tbl->hashwlock); kfree(tbl); } static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) { struct hlist_head *mesh_hash; struct hlist_node *p, *q; struct mpath_node *gate; int i; mesh_hash = tbl->hash_buckets; for (i = 0; i <= tbl->hash_mask; i++) { spin_lock_bh(&tbl->hashwlock[i]); hlist_for_each_safe(p, q, &mesh_hash[i]) { tbl->free_node(p, free_leafs); atomic_dec(&tbl->entries); } spin_unlock_bh(&tbl->hashwlock[i]); } if (free_leafs) { spin_lock_bh(&tbl->gates_lock); hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) { hlist_del(&gate->list); kfree(gate); } kfree(tbl->known_gates); spin_unlock_bh(&tbl->gates_lock); } __mesh_table_free(tbl); } static int mesh_table_grow(struct mesh_table *oldtbl, struct mesh_table *newtbl) { struct hlist_head *oldhash; struct hlist_node *p, *q; int i; if (atomic_read(&oldtbl->entries) < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1)) return -EAGAIN; newtbl->free_node = oldtbl->free_node; newtbl->mean_chain_len = oldtbl->mean_chain_len; newtbl->copy_node = oldtbl->copy_node; newtbl->known_gates = oldtbl->known_gates; atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); oldhash = oldtbl->hash_buckets; for (i = 0; i <= oldtbl->hash_mask; i++) hlist_for_each(p, &oldhash[i]) if (oldtbl->copy_node(p, newtbl) < 0) goto errcopy; return 0; errcopy: for (i = 0; i <= newtbl->hash_mask; i++) { hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) oldtbl->free_node(p, 0); } return -ENOMEM; } static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl) { /* Use last four bytes of hw addr and interface index as hash index */ return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd) & tbl->hash_mask; } /** * * mesh_path_assign_nexthop - update mesh path next hop * * @mpath: mesh path to update * @sta: next hop to assign * * Locking: mpath->state_lock must be held when calling this function */ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) { struct sk_buff *skb; struct ieee80211_hdr *hdr; unsigned long flags; rcu_assign_pointer(mpath->next_hop, sta); spin_lock_irqsave(&mpath->frame_queue.lock, flags); skb_queue_walk(&mpath->frame_queue, skb) { hdr = (struct ieee80211_hdr *) skb->data; memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); } spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); } static void prepare_for_gate(struct sk_buff *skb, char *dst_addr, struct mesh_path *gate_mpath) { struct ieee80211_hdr *hdr; struct ieee80211s_hdr *mshdr; int mesh_hdrlen, hdrlen; char *next_hop; hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); if (!(mshdr->flags & MESH_FLAGS_AE)) { /* size of the fixed part of the mesh header */ mesh_hdrlen = 6; /* make room for the two extended addresses */ skb_push(skb, 2 * ETH_ALEN); memmove(skb->data, hdr, hdrlen + mesh_hdrlen); hdr = (struct ieee80211_hdr *) skb->data; /* we preserve the previous mesh header and only add * the new addreses */ mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); mshdr->flags = MESH_FLAGS_AE_A5_A6; memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN); memcpy(mshdr->eaddr2, hdr->addr4, ETH_ALEN); } /* update next hop */ hdr = (struct ieee80211_hdr *) skb->data; rcu_read_lock(); next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr; memcpy(hdr->addr1, next_hop, ETH_ALEN); rcu_read_unlock(); memcpy(hdr->addr2, gate_mpath->sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr3, dst_addr, ETH_ALEN); } /** * * mesh_path_move_to_queue - Move or copy frames from one mpath queue to another * * This function is used to transfer or copy frames from an unresolved mpath to * a gate mpath. The function also adds the Address Extension field and * updates the next hop. * * If a frame already has an Address Extension field, only the next hop and * destination addresses are updated. * * The gate mpath must be an active mpath with a valid mpath->next_hop. * * @mpath: An active mpath the frames will be sent to (i.e. the gate) * @from_mpath: The failed mpath * @copy: When true, copy all the frames to the new mpath queue. When false, * move them. */ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, struct mesh_path *from_mpath, bool copy) { struct sk_buff *skb, *fskb, *tmp; struct sk_buff_head failq; unsigned long flags; BUG_ON(gate_mpath == from_mpath); BUG_ON(!gate_mpath->next_hop); __skb_queue_head_init(&failq); spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); skb_queue_splice_init(&from_mpath->frame_queue, &failq); spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); skb_queue_walk_safe(&failq, fskb, tmp) { if (skb_queue_len(&gate_mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN) { mpath_dbg(gate_mpath->sdata, "mpath queue full!\n"); break; } skb = skb_copy(fskb, GFP_ATOMIC); if (WARN_ON(!skb)) break; prepare_for_gate(skb, gate_mpath->dst, gate_mpath); skb_queue_tail(&gate_mpath->frame_queue, skb); if (copy) continue; __skb_unlink(fskb, &failq); kfree_skb(fskb); } mpath_dbg(gate_mpath->sdata, "Mpath queue for gate %pM has %d frames\n", gate_mpath->dst, skb_queue_len(&gate_mpath->frame_queue)); if (!copy) return; spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); skb_queue_splice(&failq, &from_mpath->frame_queue); spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); } static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst, struct ieee80211_sub_if_data *sdata) { struct mesh_path *mpath; struct hlist_node *n; struct hlist_head *bucket; struct mpath_node *node; bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; hlist_for_each_entry_rcu(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && ether_addr_equal(dst, mpath->dst)) { if (MPATH_EXPIRED(mpath)) { spin_lock_bh(&mpath->state_lock); mpath->flags &= ~MESH_PATH_ACTIVE; spin_unlock_bh(&mpath->state_lock); } return mpath; } } return NULL; } /** * mesh_path_lookup - look up a path in the mesh path table * @dst: hardware address (ETH_ALEN length) of destination * @sdata: local subif * * Returns: pointer to the mesh path structure, or NULL if not found * * Locking: must be called within a read rcu section. */ struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) { return mpath_lookup(rcu_dereference(mesh_paths), dst, sdata); } struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) { return mpath_lookup(rcu_dereference(mpp_paths), dst, sdata); } /** * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index * @idx: index * @sdata: local subif, or NULL for all entries * * Returns: pointer to the mesh path structure, or NULL if not found. * * Locking: must be called within a read rcu section. */ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) { struct mesh_table *tbl = rcu_dereference(mesh_paths); struct mpath_node *node; struct hlist_node *p; int i; int j = 0; for_each_mesh_entry(tbl, p, node, i) { if (sdata && node->mpath->sdata != sdata) continue; if (j++ == idx) { if (MPATH_EXPIRED(node->mpath)) { spin_lock_bh(&node->mpath->state_lock); node->mpath->flags &= ~MESH_PATH_ACTIVE; spin_unlock_bh(&node->mpath->state_lock); } return node->mpath; } } return NULL; } /** * mesh_path_add_gate - add the given mpath to a mesh gate to our path table * @mpath: gate path to add to table */ int mesh_path_add_gate(struct mesh_path *mpath) { struct mesh_table *tbl; struct mpath_node *gate, *new_gate; struct hlist_node *n; int err; rcu_read_lock(); tbl = rcu_dereference(mesh_paths); hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list) if (gate->mpath == mpath) { err = -EEXIST; goto err_rcu; } new_gate = kzalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (!new_gate) { err = -ENOMEM; goto err_rcu; } mpath->is_gate = true; mpath->sdata->u.mesh.num_gates++; new_gate->mpath = mpath; spin_lock_bh(&tbl->gates_lock); hlist_add_head_rcu(&new_gate->list, tbl->known_gates); spin_unlock_bh(&tbl->gates_lock); rcu_read_unlock(); mpath_dbg(mpath->sdata, "Mesh path: Recorded new gate: %pM. %d known gates\n", mpath->dst, mpath->sdata->u.mesh.num_gates); return 0; err_rcu: rcu_read_unlock(); return err; } /** * mesh_gate_del - remove a mesh gate from the list of known gates * @tbl: table which holds our list of known gates * @mpath: gate mpath * * Returns: 0 on success * * Locking: must be called inside rcu_read_lock() section */ static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) { struct mpath_node *gate; struct hlist_node *p, *q; hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) if (gate->mpath == mpath) { spin_lock_bh(&tbl->gates_lock); hlist_del_rcu(&gate->list); kfree_rcu(gate, rcu); spin_unlock_bh(&tbl->gates_lock); mpath->sdata->u.mesh.num_gates--; mpath->is_gate = false; mpath_dbg(mpath->sdata, "Mesh path: Deleted gate: %pM. %d known gates\n", mpath->dst, mpath->sdata->u.mesh.num_gates); break; } return 0; } /** * mesh_gate_num - number of gates known to this interface * @sdata: subif data */ int mesh_gate_num(struct ieee80211_sub_if_data *sdata) { return sdata->u.mesh.num_gates; } /** * mesh_path_add - allocate and add a new path to the mesh path table * @addr: destination address of the path (ETH_ALEN length) * @sdata: local subif * * Returns: 0 on success * * State: the initial state of the new path is set to 0 */ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; struct mesh_table *tbl; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; struct hlist_node *n; int grow = 0; int err = 0; u32 hash_idx; if (ether_addr_equal(dst, sdata->vif.addr)) /* never add ourselves as neighbours */ return -ENOTSUPP; if (is_multicast_ether_addr(dst)) return -ENOTSUPP; if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0) return -ENOSPC; err = -ENOMEM; new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); if (!new_mpath) goto err_path_alloc; new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (!new_node) goto err_node_alloc; read_lock_bh(&pathtbl_resize_lock); memcpy(new_mpath->dst, dst, ETH_ALEN); eth_broadcast_addr(new_mpath->rann_snd_addr); new_mpath->is_root = false; new_mpath->sdata = sdata; new_mpath->flags = 0; skb_queue_head_init(&new_mpath->frame_queue); new_node->mpath = new_mpath; new_mpath->timer.data = (unsigned long) new_mpath; new_mpath->timer.function = mesh_path_timer; new_mpath->exp_time = jiffies; spin_lock_init(&new_mpath->state_lock); init_timer(&new_mpath->timer); tbl = resize_dereference_mesh_paths(); hash_idx = mesh_table_hash(dst, sdata, tbl); bucket = &tbl->hash_buckets[hash_idx]; spin_lock(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && ether_addr_equal(dst, mpath->dst)) goto err_exists; } hlist_add_head_rcu(&new_node->list, bucket); if (atomic_inc_return(&tbl->entries) >= tbl->mean_chain_len * (tbl->hash_mask + 1)) grow = 1; mesh_paths_generation++; spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); } return 0; err_exists: spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: kfree(new_mpath); err_path_alloc: atomic_dec(&sdata->u.mesh.mpaths); return err; } static void mesh_table_free_rcu(struct rcu_head *rcu) { struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head); mesh_table_free(tbl, false); } void mesh_mpath_table_grow(void) { struct mesh_table *oldtbl, *newtbl; write_lock_bh(&pathtbl_resize_lock); oldtbl = resize_dereference_mesh_paths(); newtbl = mesh_table_alloc(oldtbl->size_order + 1); if (!newtbl) goto out; if (mesh_table_grow(oldtbl, newtbl) < 0) { __mesh_table_free(newtbl); goto out; } rcu_assign_pointer(mesh_paths, newtbl); call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); out: write_unlock_bh(&pathtbl_resize_lock); } void mesh_mpp_table_grow(void) { struct mesh_table *oldtbl, *newtbl; write_lock_bh(&pathtbl_resize_lock); oldtbl = resize_dereference_mpp_paths(); newtbl = mesh_table_alloc(oldtbl->size_order + 1); if (!newtbl) goto out; if (mesh_table_grow(oldtbl, newtbl) < 0) { __mesh_table_free(newtbl); goto out; } rcu_assign_pointer(mpp_paths, newtbl); call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); out: write_unlock_bh(&pathtbl_resize_lock); } int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; struct mesh_table *tbl; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; struct hlist_node *n; int grow = 0; int err = 0; u32 hash_idx; if (ether_addr_equal(dst, sdata->vif.addr)) /* never add ourselves as neighbours */ return -ENOTSUPP; if (is_multicast_ether_addr(dst)) return -ENOTSUPP; err = -ENOMEM; new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); if (!new_mpath) goto err_path_alloc; new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (!new_node) goto err_node_alloc; read_lock_bh(&pathtbl_resize_lock); memcpy(new_mpath->dst, dst, ETH_ALEN); memcpy(new_mpath->mpp, mpp, ETH_ALEN); new_mpath->sdata = sdata; new_mpath->flags = 0; skb_queue_head_init(&new_mpath->frame_queue); new_node->mpath = new_mpath; init_timer(&new_mpath->timer); new_mpath->exp_time = jiffies; spin_lock_init(&new_mpath->state_lock); tbl = resize_dereference_mpp_paths(); hash_idx = mesh_table_hash(dst, sdata, tbl); bucket = &tbl->hash_buckets[hash_idx]; spin_lock(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && ether_addr_equal(dst, mpath->dst)) goto err_exists; } hlist_add_head_rcu(&new_node->list, bucket); if (atomic_inc_return(&tbl->entries) >= tbl->mean_chain_len * (tbl->hash_mask + 1)) grow = 1; spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); } return 0; err_exists: spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: kfree(new_mpath); err_path_alloc: return err; } /** * mesh_plink_broken - deactivates paths and sends perr when a link breaks * * @sta: broken peer link * * This function must be called from the rate control algorithm if enough * delivery errors suggest that a peer link is no longer usable. */ void mesh_plink_broken(struct sta_info *sta) { struct mesh_table *tbl; static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; struct ieee80211_sub_if_data *sdata = sta->sdata; int i; __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE); rcu_read_lock(); tbl = rcu_dereference(mesh_paths); for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; if (rcu_dereference(mpath->next_hop) == sta && mpath->flags & MESH_PATH_ACTIVE && !(mpath->flags & MESH_PATH_FIXED)) { spin_lock_bh(&mpath->state_lock); mpath->flags &= ~MESH_PATH_ACTIVE; ++mpath->sn; spin_unlock_bh(&mpath->state_lock); mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, mpath->dst, cpu_to_le32(mpath->sn), reason, bcast, sdata); } } rcu_read_unlock(); } static void mesh_path_node_reclaim(struct rcu_head *rp) { struct mpath_node *node = container_of(rp, struct mpath_node, rcu); struct ieee80211_sub_if_data *sdata = node->mpath->sdata; del_timer_sync(&node->mpath->timer); atomic_dec(&sdata->u.mesh.mpaths); kfree(node->mpath); kfree(node); } /* needs to be called with the corresponding hashwlock taken */ static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node) { struct mesh_path *mpath; mpath = node->mpath; spin_lock(&mpath->state_lock); mpath->flags |= MESH_PATH_RESOLVING; if (mpath->is_gate) mesh_gate_del(tbl, mpath); hlist_del_rcu(&node->list); call_rcu(&node->rcu, mesh_path_node_reclaim); spin_unlock(&mpath->state_lock); atomic_dec(&tbl->entries); } /** * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches * * @sta: mesh peer to match * * RCU notes: this function is called when a mesh plink transitions from * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that * allows path creation. This will happen before the sta can be freed (because * sta_info_destroy() calls this) so any reader in a rcu read block will be * protected against the plink disappearing. */ void mesh_path_flush_by_nexthop(struct sta_info *sta) { struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; rcu_read_lock(); read_lock_bh(&pathtbl_resize_lock); tbl = resize_dereference_mesh_paths(); for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; if (rcu_dereference(mpath->next_hop) == sta) { spin_lock(&tbl->hashwlock[i]); __mesh_path_del(tbl, node); spin_unlock(&tbl->hashwlock[i]); } } read_unlock_bh(&pathtbl_resize_lock); rcu_read_unlock(); } static void table_flush_by_iface(struct mesh_table *tbl, struct ieee80211_sub_if_data *sdata) { struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; WARN_ON(!rcu_read_lock_held()); for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; if (mpath->sdata != sdata) continue; spin_lock_bh(&tbl->hashwlock[i]); __mesh_path_del(tbl, node); spin_unlock_bh(&tbl->hashwlock[i]); } } /** * mesh_path_flush_by_iface - Deletes all mesh paths associated with a given iface * * This function deletes both mesh paths as well as mesh portal paths. * * @sdata: interface data to match * */ void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) { struct mesh_table *tbl; rcu_read_lock(); read_lock_bh(&pathtbl_resize_lock); tbl = resize_dereference_mesh_paths(); table_flush_by_iface(tbl, sdata); tbl = resize_dereference_mpp_paths(); table_flush_by_iface(tbl, sdata); read_unlock_bh(&pathtbl_resize_lock); rcu_read_unlock(); } /** * mesh_path_del - delete a mesh path from the table * * @addr: dst address (ETH_ALEN length) * @sdata: local subif * * Returns: 0 if successful */ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) { struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_head *bucket; struct hlist_node *n; int hash_idx; int err = 0; read_lock_bh(&pathtbl_resize_lock); tbl = resize_dereference_mesh_paths(); hash_idx = mesh_table_hash(addr, sdata, tbl); bucket = &tbl->hash_buckets[hash_idx]; spin_lock(&tbl->hashwlock[hash_idx]); hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && ether_addr_equal(addr, mpath->dst)) { __mesh_path_del(tbl, node); goto enddel; } } err = -ENXIO; enddel: mesh_paths_generation++; spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); return err; } /** * mesh_path_tx_pending - sends pending frames in a mesh path queue * * @mpath: mesh path to activate * * Locking: the state_lock of the mpath structure must NOT be held when calling * this function. */ void mesh_path_tx_pending(struct mesh_path *mpath) { if (mpath->flags & MESH_PATH_ACTIVE) ieee80211_add_pending_skbs(mpath->sdata->local, &mpath->frame_queue); } /** * mesh_path_send_to_gates - sends pending frames to all known mesh gates * * @mpath: mesh path whose queue will be emptied * * If there is only one gate, the frames are transferred from the failed mpath * queue to that gate's queue. If there are more than one gates, the frames * are copied from each gate to the next. After frames are copied, the * mpath queues are emptied onto the transmission queue. */ int mesh_path_send_to_gates(struct mesh_path *mpath) { struct ieee80211_sub_if_data *sdata = mpath->sdata; struct hlist_node *n; struct mesh_table *tbl; struct mesh_path *from_mpath = mpath; struct mpath_node *gate = NULL; bool copy = false; struct hlist_head *known_gates; rcu_read_lock(); tbl = rcu_dereference(mesh_paths); known_gates = tbl->known_gates; rcu_read_unlock(); if (!known_gates) return -EHOSTUNREACH; hlist_for_each_entry_rcu(gate, n, known_gates, list) { if (gate->mpath->sdata != sdata) continue; if (gate->mpath->flags & MESH_PATH_ACTIVE) { mpath_dbg(sdata, "Forwarding to %pM\n", gate->mpath->dst); mesh_path_move_to_queue(gate->mpath, from_mpath, copy); from_mpath = gate->mpath; copy = true; } else { mpath_dbg(sdata, "Not forwarding %p (flags %#x)\n", gate->mpath, gate->mpath->flags); } } hlist_for_each_entry_rcu(gate, n, known_gates, list) if (gate->mpath->sdata == sdata) { mpath_dbg(sdata, "Sending to %pM\n", gate->mpath->dst); mesh_path_tx_pending(gate->mpath); } return (from_mpath == mpath) ? -EHOSTUNREACH : 0; } /** * mesh_path_discard_frame - discard a frame whose path could not be resolved * * @skb: frame to discard * @sdata: network subif the frame was to be sent through * * Locking: the function must me called within a rcu_read_lock region */ void mesh_path_discard_frame(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { kfree_skb(skb); sdata->u.mesh.mshstats.dropped_frames_no_route++; } /** * mesh_path_flush_pending - free the pending queue of a mesh path * * @mpath: mesh path whose queue has to be freed * * Locking: the function must me called within a rcu_read_lock region */ void mesh_path_flush_pending(struct mesh_path *mpath) { struct sk_buff *skb; while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) mesh_path_discard_frame(skb, mpath->sdata); } /** * mesh_path_fix_nexthop - force a specific next hop for a mesh path * * @mpath: the mesh path to modify * @next_hop: the next hop to force * * Locking: this function must be called holding mpath->state_lock */ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) { spin_lock_bh(&mpath->state_lock); mesh_path_assign_nexthop(mpath, next_hop); mpath->sn = 0xffff; mpath->metric = 0; mpath->hop_count = 0; mpath->exp_time = 0; mpath->flags |= MESH_PATH_FIXED; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); mesh_path_tx_pending(mpath); } static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) { struct mesh_path *mpath; struct mpath_node *node = hlist_entry(p, struct mpath_node, list); mpath = node->mpath; hlist_del_rcu(p); if (free_leafs) { del_timer_sync(&mpath->timer); kfree(mpath); } kfree(node); } static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) { struct mesh_path *mpath; struct mpath_node *node, *new_node; u32 hash_idx; new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (new_node == NULL) return -ENOMEM; node = hlist_entry(p, struct mpath_node, list); mpath = node->mpath; new_node->mpath = mpath; hash_idx = mesh_table_hash(mpath->dst, mpath->sdata, newtbl); hlist_add_head(&new_node->list, &newtbl->hash_buckets[hash_idx]); return 0; } int mesh_pathtbl_init(void) { struct mesh_table *tbl_path, *tbl_mpp; int ret; tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); if (!tbl_path) return -ENOMEM; tbl_path->free_node = &mesh_path_node_free; tbl_path->copy_node = &mesh_path_node_copy; tbl_path->mean_chain_len = MEAN_CHAIN_LEN; tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); if (!tbl_path->known_gates) { ret = -ENOMEM; goto free_path; } INIT_HLIST_HEAD(tbl_path->known_gates); tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); if (!tbl_mpp) { ret = -ENOMEM; goto free_path; } tbl_mpp->free_node = &mesh_path_node_free; tbl_mpp->copy_node = &mesh_path_node_copy; tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); if (!tbl_mpp->known_gates) { ret = -ENOMEM; goto free_mpp; } INIT_HLIST_HEAD(tbl_mpp->known_gates); /* Need no locking since this is during init */ RCU_INIT_POINTER(mesh_paths, tbl_path); RCU_INIT_POINTER(mpp_paths, tbl_mpp); return 0; free_mpp: mesh_table_free(tbl_mpp, true); free_path: mesh_table_free(tbl_path, true); return ret; } void mesh_path_expire(struct ieee80211_sub_if_data *sdata) { struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; rcu_read_lock(); tbl = rcu_dereference(mesh_paths); for_each_mesh_entry(tbl, p, node, i) { if (node->mpath->sdata != sdata) continue; mpath = node->mpath; if ((!(mpath->flags & MESH_PATH_RESOLVING)) && (!(mpath->flags & MESH_PATH_FIXED)) && time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) mesh_path_del(mpath->dst, mpath->sdata); } rcu_read_unlock(); } void mesh_pathtbl_unregister(void) { /* no need for locking during exit path */ mesh_table_free(rcu_dereference_protected(mesh_paths, 1), true); mesh_table_free(rcu_dereference_protected(mpp_paths, 1), true); } compat-drivers-2012-09-18/net/mac80211/wpa.c0000644000175000017500000004203112026211315017235 0ustar mcgrofmcgrof/* * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "michael.h" #include "tkip.h" #include "aes_ccm.h" #include "aes_cmac.h" #include "wpa.h" ieee80211_tx_result ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) { u8 *data, *key, *mic; size_t data_len; unsigned int hdrlen; struct ieee80211_hdr *hdr; struct sk_buff *skb = tx->skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int tail; hdr = (struct ieee80211_hdr *)skb->data; if (!tx->key || tx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || skb->len < 24 || !ieee80211_is_data_present(hdr->frame_control)) return TX_CONTINUE; hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < hdrlen) return TX_DROP; data = skb->data + hdrlen; data_len = skb->len - hdrlen; if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) { /* Need to use software crypto for the test */ info->control.hw_key = NULL; } if (info->control.hw_key && (info->flags & IEEE80211_TX_CTL_DONTFRAG || tx->local->ops->set_frag_threshold) && !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { /* hwaccel - with no need for SW-generated MMIC */ return TX_CONTINUE; } tail = MICHAEL_MIC_LEN; if (!info->control.hw_key) tail += TKIP_ICV_LEN; if (WARN_ON(skb_tailroom(skb) < tail || skb_headroom(skb) < TKIP_IV_LEN)) return TX_DROP; key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]; mic = skb_put(skb, MICHAEL_MIC_LEN); michael_mic(key, hdr, data, data_len, mic); if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) mic[0]++; return TX_CONTINUE; } ieee80211_rx_result ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) { u8 *data, *key = NULL; size_t data_len; unsigned int hdrlen; u8 mic[MICHAEL_MIC_LEN]; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; /* * it makes no sense to check for MIC errors on anything other * than data frames. */ if (!ieee80211_is_data_present(hdr->frame_control)) return RX_CONTINUE; /* * No way to verify the MIC if the hardware stripped it or * the IV with the key index. In this case we have solely rely * on the driver to set RX_FLAG_MMIC_ERROR in the event of a * MIC failure report. */ if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) { if (status->flag & RX_FLAG_MMIC_ERROR) goto mic_fail; if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key) goto update_iv; return RX_CONTINUE; } /* * Some hardware seems to generate Michael MIC failure reports; even * though, the frame was not encrypted with TKIP and therefore has no * MIC. Ignore the flag them to avoid triggering countermeasures. */ if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || !(status->flag & RX_FLAG_DECRYPTED)) return RX_CONTINUE; if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) { /* * APs with pairwise keys should never receive Michael MIC * errors for non-zero keyidx because these are reserved for * group keys and only the AP is sending real multicast * frames in the BSS. ( */ return RX_DROP_UNUSABLE; } if (status->flag & RX_FLAG_MMIC_ERROR) goto mic_fail; hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < hdrlen + MICHAEL_MIC_LEN) return RX_DROP_UNUSABLE; if (skb_linearize(rx->skb)) return RX_DROP_UNUSABLE; hdr = (void *)skb->data; data = skb->data + hdrlen; data_len = skb->len - hdrlen - MICHAEL_MIC_LEN; key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; michael_mic(key, hdr, data, data_len, mic); if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) goto mic_fail; /* remove Michael MIC from payload */ skb_trim(skb, skb->len - MICHAEL_MIC_LEN); update_iv: /* update IV in key information to be able to detect replays */ rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32; rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16; return RX_CONTINUE; mic_fail: /* * In some cases the key can be unset - e.g. a multicast packet, in * a driver that supports HW encryption. Send up the key idx only if * the key is set. */ mac80211_ev_michael_mic_failure(rx->sdata, rx->key ? rx->key->conf.keyidx : -1, (void *) skb->data, NULL, GFP_ATOMIC); return RX_DROP_UNUSABLE; } static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_key *key = tx->key; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); unsigned long flags; unsigned int hdrlen; int len, tail; u8 *pos; if (info->control.hw_key && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { /* hwaccel - with no need for software-generated IV */ return 0; } hdrlen = ieee80211_hdrlen(hdr->frame_control); len = skb->len - hdrlen; if (info->control.hw_key) tail = 0; else tail = TKIP_ICV_LEN; if (WARN_ON(skb_tailroom(skb) < tail || skb_headroom(skb) < TKIP_IV_LEN)) return -1; pos = skb_push(skb, TKIP_IV_LEN); memmove(pos, pos + TKIP_IV_LEN, hdrlen); skb_set_network_header(skb, skb_network_offset(skb) + TKIP_IV_LEN); pos += hdrlen; /* the HW only needs room for the IV, but not the actual IV */ if (info->control.hw_key && (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) return 0; /* Increase IV for the frame */ spin_lock_irqsave(&key->u.tkip.txlock, flags); key->u.tkip.tx.iv16++; if (key->u.tkip.tx.iv16 == 0) key->u.tkip.tx.iv32++; pos = ieee80211_tkip_add_iv(pos, key); spin_unlock_irqrestore(&key->u.tkip.txlock, flags); /* hwaccel - with software IV */ if (info->control.hw_key) return 0; /* Add room for ICV */ skb_put(skb, TKIP_ICV_LEN); return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm, key, skb, pos, len); } ieee80211_tx_result ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb; ieee80211_tx_set_protected(tx); skb_queue_walk(&tx->skbs, skb) { if (tkip_encrypt_skb(tx, skb) < 0) return TX_DROP; } return TX_CONTINUE; } ieee80211_rx_result ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; int hdrlen, res, hwaccel = 0; struct ieee80211_key *key = rx->key; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); hdrlen = ieee80211_hdrlen(hdr->frame_control); if (!ieee80211_is_data(hdr->frame_control)) return RX_CONTINUE; if (!rx->sta || skb->len - hdrlen < 12) return RX_DROP_UNUSABLE; /* it may be possible to optimize this a bit more */ if (skb_linearize(rx->skb)) return RX_DROP_UNUSABLE; hdr = (void *)skb->data; /* * Let TKIP code verify IV, but skip decryption. * In the case where hardware checks the IV as well, * we don't even get here, see ieee80211_rx_h_decrypt() */ if (status->flag & RX_FLAG_DECRYPTED) hwaccel = 1; res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm, key, skb->data + hdrlen, skb->len - hdrlen, rx->sta->sta.addr, hdr->addr1, hwaccel, rx->security_idx, &rx->tkip_iv32, &rx->tkip_iv16); if (res != TKIP_DECRYPT_OK) return RX_DROP_UNUSABLE; /* Trim ICV */ skb_trim(skb, skb->len - TKIP_ICV_LEN); /* Remove IV */ memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen); skb_pull(skb, TKIP_IV_LEN); return RX_CONTINUE; } static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, int encrypted) { __le16 mask_fc; int a4_included, mgmt; u8 qos_tid; u8 *b_0, *aad; u16 data_len, len_a; unsigned int hdrlen; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; memset(scratch, 0, 6 * AES_BLOCK_SIZE); b_0 = scratch + 3 * AES_BLOCK_SIZE; aad = scratch + 4 * AES_BLOCK_SIZE; /* * Mask FC: zero subtype b4 b5 b6 (if not mgmt) * Retry, PwrMgt, MoreData; set Protected */ mgmt = ieee80211_is_mgmt(hdr->frame_control); mask_fc = hdr->frame_control; mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA); if (!mgmt) mask_fc &= ~cpu_to_le16(0x0070); mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); hdrlen = ieee80211_hdrlen(hdr->frame_control); len_a = hdrlen - 2; a4_included = ieee80211_has_a4(hdr->frame_control); if (ieee80211_is_data_qos(hdr->frame_control)) qos_tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; else qos_tid = 0; data_len = skb->len - hdrlen - CCMP_HDR_LEN; if (encrypted) data_len -= CCMP_MIC_LEN; /* First block, b_0 */ b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ /* Nonce: Nonce Flags | A2 | PN * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) */ b_0[1] = qos_tid | (mgmt << 4); memcpy(&b_0[2], hdr->addr2, ETH_ALEN); memcpy(&b_0[8], pn, CCMP_PN_LEN); /* l(m) */ put_unaligned_be16(data_len, &b_0[14]); /* AAD (extra authenticate-only data) / masked 802.11 header * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ put_unaligned_be16(len_a, &aad[0]); put_unaligned(mask_fc, (__le16 *)&aad[2]); memcpy(&aad[4], &hdr->addr1, 3 * ETH_ALEN); /* Mask Seq#, leave Frag# */ aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f; aad[23] = 0; if (a4_included) { memcpy(&aad[24], hdr->addr4, ETH_ALEN); aad[30] = qos_tid; aad[31] = 0; } else { memset(&aad[24], 0, ETH_ALEN + IEEE80211_QOS_CTL_LEN); aad[24] = qos_tid; } } static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id) { hdr[0] = pn[5]; hdr[1] = pn[4]; hdr[2] = 0; hdr[3] = 0x20 | (key_id << 6); hdr[4] = pn[3]; hdr[5] = pn[2]; hdr[6] = pn[1]; hdr[7] = pn[0]; } static inline void ccmp_hdr2pn(u8 *pn, u8 *hdr) { pn[0] = hdr[7]; pn[1] = hdr[6]; pn[2] = hdr[5]; pn[3] = hdr[4]; pn[4] = hdr[1]; pn[5] = hdr[0]; } static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_key *key = tx->key; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int hdrlen, len, tail; u8 *pos; u8 pn[6]; u64 pn64; u8 scratch[6 * AES_BLOCK_SIZE]; if (info->control.hw_key && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { /* * hwaccel has no need for preallocated room for CCMP * header or MIC fields */ return 0; } hdrlen = ieee80211_hdrlen(hdr->frame_control); len = skb->len - hdrlen; if (info->control.hw_key) tail = 0; else tail = CCMP_MIC_LEN; if (WARN_ON(skb_tailroom(skb) < tail || skb_headroom(skb) < CCMP_HDR_LEN)) return -1; pos = skb_push(skb, CCMP_HDR_LEN); memmove(pos, pos + CCMP_HDR_LEN, hdrlen); skb_set_network_header(skb, skb_network_offset(skb) + CCMP_HDR_LEN); /* the HW only needs room for the IV, but not the actual IV */ if (info->control.hw_key && (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) return 0; hdr = (struct ieee80211_hdr *) pos; pos += hdrlen; pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn); pn[5] = pn64; pn[4] = pn64 >> 8; pn[3] = pn64 >> 16; pn[2] = pn64 >> 24; pn[1] = pn64 >> 32; pn[0] = pn64 >> 40; ccmp_pn2hdr(pos, pn, key->conf.keyidx); /* hwaccel - with software CCMP header */ if (info->control.hw_key) return 0; pos += CCMP_HDR_LEN; ccmp_special_blocks(skb, pn, scratch, 0); ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, pos, skb_put(skb, CCMP_MIC_LEN)); return 0; } ieee80211_tx_result ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb; ieee80211_tx_set_protected(tx); skb_queue_walk(&tx->skbs, skb) { if (ccmp_encrypt_skb(tx, skb) < 0) return TX_DROP; } return TX_CONTINUE; } ieee80211_rx_result ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; int hdrlen; struct ieee80211_key *key = rx->key; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u8 pn[CCMP_PN_LEN]; int data_len; int queue; hdrlen = ieee80211_hdrlen(hdr->frame_control); if (!ieee80211_is_data(hdr->frame_control) && !ieee80211_is_robust_mgmt_frame(hdr)) return RX_CONTINUE; data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN; if (!rx->sta || data_len < 0) return RX_DROP_UNUSABLE; if (status->flag & RX_FLAG_DECRYPTED) { if (!pskb_may_pull(rx->skb, hdrlen + CCMP_HDR_LEN)) return RX_DROP_UNUSABLE; } else { if (skb_linearize(rx->skb)) return RX_DROP_UNUSABLE; } ccmp_hdr2pn(pn, skb->data + hdrlen); queue = rx->security_idx; if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) { key->u.ccmp.replays++; return RX_DROP_UNUSABLE; } if (!(status->flag & RX_FLAG_DECRYPTED)) { u8 scratch[6 * AES_BLOCK_SIZE]; /* hardware didn't decrypt/verify MIC */ ccmp_special_blocks(skb, pn, scratch, 1); if (ieee80211_aes_ccm_decrypt( key->u.ccmp.tfm, scratch, skb->data + hdrlen + CCMP_HDR_LEN, data_len, skb->data + skb->len - CCMP_MIC_LEN, skb->data + hdrlen + CCMP_HDR_LEN)) return RX_DROP_UNUSABLE; } memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN); /* Remove CCMP header and MIC */ if (pskb_trim(skb, skb->len - CCMP_MIC_LEN)) return RX_DROP_UNUSABLE; memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen); skb_pull(skb, CCMP_HDR_LEN); return RX_CONTINUE; } static void bip_aad(struct sk_buff *skb, u8 *aad) { /* BIP AAD: FC(masked) || A1 || A2 || A3 */ /* FC type/subtype */ aad[0] = skb->data[0]; /* Mask FC Retry, PwrMgt, MoreData flags to zero */ aad[1] = skb->data[1] & ~(BIT(4) | BIT(5) | BIT(6)); /* A1 || A2 || A3 */ memcpy(aad + 2, skb->data + 4, 3 * ETH_ALEN); } static inline void bip_ipn_set64(u8 *d, u64 pn) { *d++ = pn; *d++ = pn >> 8; *d++ = pn >> 16; *d++ = pn >> 24; *d++ = pn >> 32; *d = pn >> 40; } static inline void bip_ipn_swap(u8 *d, const u8 *s) { *d++ = s[5]; *d++ = s[4]; *d++ = s[3]; *d++ = s[2]; *d++ = s[1]; *d = s[0]; } ieee80211_tx_result ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb; struct ieee80211_tx_info *info; struct ieee80211_key *key = tx->key; struct ieee80211_mmie *mmie; u8 aad[20]; u64 pn64; if (WARN_ON(skb_queue_len(&tx->skbs) != 1)) return TX_DROP; skb = skb_peek(&tx->skbs); info = IEEE80211_SKB_CB(skb); if (info->control.hw_key) return TX_CONTINUE; if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie))) return TX_DROP; mmie = (struct ieee80211_mmie *) skb_put(skb, sizeof(*mmie)); mmie->element_id = WLAN_EID_MMIE; mmie->length = sizeof(*mmie) - 2; mmie->key_id = cpu_to_le16(key->conf.keyidx); /* PN = PN + 1 */ pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn); bip_ipn_set64(mmie->sequence_number, pn64); bip_aad(skb, aad); /* * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64) */ ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad, skb->data + 24, skb->len - 24, mmie->mic); return TX_CONTINUE; } ieee80211_rx_result ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx) { struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_key *key = rx->key; struct ieee80211_mmie *mmie; u8 aad[20], mic[8], ipn[6]; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; if (!ieee80211_is_mgmt(hdr->frame_control)) return RX_CONTINUE; /* management frames are already linear */ if (skb->len < 24 + sizeof(*mmie)) return RX_DROP_UNUSABLE; mmie = (struct ieee80211_mmie *) (skb->data + skb->len - sizeof(*mmie)); if (mmie->element_id != WLAN_EID_MMIE || mmie->length != sizeof(*mmie) - 2) return RX_DROP_UNUSABLE; /* Invalid MMIE */ bip_ipn_swap(ipn, mmie->sequence_number); if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) { key->u.aes_cmac.replays++; return RX_DROP_UNUSABLE; } if (!(status->flag & RX_FLAG_DECRYPTED)) { /* hardware didn't decrypt/verify MIC */ bip_aad(skb, aad); ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad, skb->data + 24, skb->len - 24, mic); if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) { key->u.aes_cmac.icverrors++; return RX_DROP_UNUSABLE; } } memcpy(key->u.aes_cmac.rx_pn, ipn, 6); /* Remove MMIE */ skb_trim(skb, skb->len - sizeof(*mmie)); return RX_CONTINUE; } ieee80211_tx_result ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb; struct ieee80211_tx_info *info = NULL; skb_queue_walk(&tx->skbs, skb) { info = IEEE80211_SKB_CB(skb); /* handle hw-only algorithm */ if (!info->control.hw_key) return TX_DROP; } ieee80211_tx_set_protected(tx); return TX_CONTINUE; } compat-drivers-2012-09-18/net/mac80211/agg-tx.c0000644000175000017500000006370412026211315017647 0ustar mcgrofmcgrof/* * HT handling * * Copyright 2003, Jouni Malinen * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "wme.h" /** * DOC: TX A-MPDU aggregation * * Aggregation on the TX side requires setting the hardware flag * %IEEE80211_HW_AMPDU_AGGREGATION. The driver will then be handed * packets with a flag indicating A-MPDU aggregation. The driver * or device is responsible for actually aggregating the frames, * as well as deciding how many and which to aggregate. * * When TX aggregation is started by some subsystem (usually the rate * control algorithm would be appropriate) by calling the * ieee80211_start_tx_ba_session() function, the driver will be * notified via its @ampdu_action function, with the * %IEEE80211_AMPDU_TX_START action. * * In response to that, the driver is later required to call the * ieee80211_start_tx_ba_cb_irqsafe() function, which will really * start the aggregation session after the peer has also responded. * If the peer responds negatively, the session will be stopped * again right away. Note that it is possible for the aggregation * session to be stopped before the driver has indicated that it * is done setting it up, in which case it must not indicate the * setup completion. * * Also note that, since we also need to wait for a response from * the peer, the driver is notified of the completion of the * handshake by the %IEEE80211_AMPDU_TX_OPERATIONAL action to the * @ampdu_action callback. * * Similarly, when the aggregation session is stopped by the peer * or something calling ieee80211_stop_tx_ba_session(), the driver's * @ampdu_action function will be called with the action * %IEEE80211_AMPDU_TX_STOP. In this case, the call must not fail, * and the driver must later call ieee80211_stop_tx_ba_cb_irqsafe(). * Note that the sta can get destroyed before the BA tear down is * complete. */ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, u8 dialog_token, u16 start_seq_num, u16 agg_size, u16 timeout) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u16 capab; skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req)); mgmt->u.action.category = WLAN_CATEGORY_BACK; mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ; mgmt->u.action.u.addba_req.dialog_token = dialog_token; capab = (u16)(1 << 1); /* bit 1 aggregation policy */ capab |= (u16)(tid << 2); /* bit 5:2 TID number */ capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */ mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab); mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout); mgmt->u.action.u.addba_req.start_seq_num = cpu_to_le16(start_seq_num << 4); ieee80211_tx_skb_tid(sdata, skb, tid); } void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_bar *bar; u16 bar_control = 0; skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar)); memset(bar, 0, sizeof(*bar)); bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); memcpy(bar->ra, ra, ETH_ALEN); memcpy(bar->ta, sdata->vif.addr, ETH_ALEN); bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; bar_control |= (u16)(tid << IEEE80211_BAR_CTRL_TID_INFO_SHIFT); bar->control = cpu_to_le16(bar_control); bar->start_seq_num = cpu_to_le16(ssn); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_REQ_TX_STATUS; ieee80211_tx_skb_tid(sdata, skb, tid); } EXPORT_SYMBOL(ieee80211_send_bar); void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx) { lockdep_assert_held(&sta->ampdu_mlme.mtx); lockdep_assert_held(&sta->lock); rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); } int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, enum ieee80211_back_parties initiator, bool tx) { struct ieee80211_local *local = sta->local; struct tid_ampdu_tx *tid_tx; int ret; lockdep_assert_held(&sta->ampdu_mlme.mtx); spin_lock_bh(&sta->lock); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx) { spin_unlock_bh(&sta->lock); return -ENOENT; } /* if we're already stopping ignore any new requests to stop */ if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { spin_unlock_bh(&sta->lock); return -EALREADY; } if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { /* not even started yet! */ ieee80211_assign_tid_tx(sta, tid, NULL); spin_unlock_bh(&sta->lock); kfree_rcu(tid_tx, rcu_head); return 0; } set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state); spin_unlock_bh(&sta->lock); ht_dbg(sta->sdata, "Tx BA session stop requested for %pM tid %u\n", sta->sta.addr, tid); del_timer_sync(&tid_tx->addba_resp_timer); del_timer_sync(&tid_tx->session_timer); /* * After this packets are no longer handed right through * to the driver but are put onto tid_tx->pending instead, * with locking to ensure proper access. */ clear_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); /* * There might be a few packets being processed right now (on * another CPU) that have already gotten past the aggregation * check when it was still OPERATIONAL and consequently have * IEEE80211_TX_CTL_AMPDU set. In that case, this code might * call into the driver at the same time or even before the * TX paths calls into it, which could confuse the driver. * * Wait for all currently running TX paths to finish before * telling the driver. New packets will not go through since * the aggregation session is no longer OPERATIONAL. */ synchronize_net(); tid_tx->stop_initiator = initiator; tid_tx->tx_stop = tx; ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_STOP, &sta->sta, tid, NULL, 0); /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { /* * We may have pending packets get stuck in this case... * Not bothering with a workaround for now. */ } return ret; } /* * After sending add Block Ack request we activated a timer until * add Block Ack response will arrive from the recipient. * If this timer expires sta_addba_resp_timer_expired will be executed. */ static void sta_addba_resp_timer_expired(unsigned long data) { /* not an elegant detour, but there is no choice as the timer passes * only one argument, and both sta_info and TID are needed, so init * flow in sta_info_create gives the TID as data, while the timer_to_id * array gives the sta through container_of */ u16 tid = *(u8 *)data; struct sta_info *sta = container_of((void *)data, struct sta_info, timer_to_tid[tid]); struct tid_ampdu_tx *tid_tx; /* check if the TID waits for addBA response */ rcu_read_lock(); tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); if (!tid_tx || test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { rcu_read_unlock(); ht_dbg(sta->sdata, "timer expired on tid %d but we are not (or no longer) expecting addBA response there\n", tid); return; } ht_dbg(sta->sdata, "addBA response timer expired on tid %d\n", tid); ieee80211_stop_tx_ba_session(&sta->sta, tid); rcu_read_unlock(); } static inline int ieee80211_ac_from_tid(int tid) { return ieee802_1d_to_ac[tid & 7]; } /* * When multiple aggregation sessions on multiple stations * are being created/destroyed simultaneously, we need to * refcount the global queue stop caused by that in order * to not get into a situation where one of the aggregation * setup or teardown re-enables queues before the other is * ready to handle that. * * These two functions take care of this issue by keeping * a global "agg_queue_stop" refcount. */ static void __acquires(agg_queue) ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) { int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1) ieee80211_stop_queue_by_reason( &sdata->local->hw, queue, IEEE80211_QUEUE_STOP_REASON_AGGREGATION); __acquire(agg_queue); } static void __releases(agg_queue) ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) { int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0) ieee80211_wake_queue_by_reason( &sdata->local->hw, queue, IEEE80211_QUEUE_STOP_REASON_AGGREGATION); __release(agg_queue); } /* * splice packets from the STA's pending to the local pending, * requires a call to ieee80211_agg_splice_finish later */ static void __acquires(agg_queue) ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_tx *tid_tx, u16 tid) { struct ieee80211_local *local = sdata->local; int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; unsigned long flags; ieee80211_stop_queue_agg(sdata, tid); if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates from the pending queue\n", tid)) return; if (!skb_queue_empty(&tid_tx->pending)) { spin_lock_irqsave(&local->queue_stop_reason_lock, flags); /* copy over remaining packets */ skb_queue_splice_tail_init(&tid_tx->pending, &local->pending[queue]); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } } static void __releases(agg_queue) ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid) { ieee80211_wake_queue_agg(sdata, tid); } void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) { struct tid_ampdu_tx *tid_tx; struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; u16 start_seq_num; int ret; tid_tx = rcu_dereference_protected_tid_tx(sta, tid); /* * Start queuing up packets for this aggregation session. * We're going to release them once the driver is OK with * that. */ clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); /* * Make sure no packets are being processed. This ensures that * we have a valid starting sequence number and that in-flight * packets have been flushed out and no packets for this TID * will go into the driver during the ampdu_action call. */ synchronize_net(); start_seq_num = sta->tid_seq[tid] >> 4; ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, &sta->sta, tid, &start_seq_num, 0); if (ret) { ht_dbg(sdata, "BA request denied - HW unavailable for tid %d\n", tid); spin_lock_bh(&sta->lock); ieee80211_agg_splice_packets(sdata, tid_tx, tid); ieee80211_assign_tid_tx(sta, tid, NULL); ieee80211_agg_splice_finish(sdata, tid); spin_unlock_bh(&sta->lock); kfree_rcu(tid_tx, rcu_head); return; } /* activate the timer for the recipient's addBA response */ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); ht_dbg(sdata, "activated addBA response timer on tid %d\n", tid); spin_lock_bh(&sta->lock); sta->ampdu_mlme.last_addba_req_time[tid] = jiffies; sta->ampdu_mlme.addba_req_num[tid]++; spin_unlock_bh(&sta->lock); /* send AddBA request */ ieee80211_send_addba_request(sdata, sta->sta.addr, tid, tid_tx->dialog_token, start_seq_num, local->hw.max_tx_aggregation_subframes, tid_tx->timeout); } /* * After accepting the AddBA Response we activated a timer, * resetting it after each frame that we send. */ static void sta_tx_agg_session_timer_expired(unsigned long data) { /* not an elegant detour, but there is no choice as the timer passes * only one argument, and various sta_info are needed here, so init * flow in sta_info_create gives the TID as data, while the timer_to_id * array gives the sta through container_of */ u8 *ptid = (u8 *)data; u8 *timer_to_id = ptid - *ptid; struct sta_info *sta = container_of(timer_to_id, struct sta_info, timer_to_tid[0]); struct tid_ampdu_tx *tid_tx; unsigned long timeout; rcu_read_lock(); tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[*ptid]); if (!tid_tx || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { rcu_read_unlock(); return; } timeout = tid_tx->last_tx + TU_TO_JIFFIES(tid_tx->timeout); if (time_is_after_jiffies(timeout)) { mod_timer(&tid_tx->session_timer, timeout); rcu_read_unlock(); return; } rcu_read_unlock(); ht_dbg(sta->sdata, "tx session timer expired on tid %d\n", (u16)*ptid); ieee80211_stop_tx_ba_session(&sta->sta, *ptid); } int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, u16 timeout) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct tid_ampdu_tx *tid_tx; int ret = 0; trace_api_start_tx_ba_session(pubsta, tid); if (WARN_ON(!local->ops->ampdu_action)) return -EINVAL; if ((tid >= STA_TID_NUM) || !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) || (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) return -EINVAL; ht_dbg(sdata, "Open BA session requested for %pM tid %u\n", pubsta->addr, tid); if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_ADHOC) return -EINVAL; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { ht_dbg(sdata, "BA sessions blocked - Denying BA session request\n"); return -EINVAL; } /* * 802.11n-2009 11.5.1.1: If the initiating STA is an HT STA, is a * member of an IBSS, and has no other existing Block Ack agreement * with the recipient STA, then the initiating STA shall transmit a * Probe Request frame to the recipient STA and shall not transmit an * ADDBA Request frame unless it receives a Probe Response frame * from the recipient within dot11ADDBAFailureTimeout. * * The probe request mechanism for ADDBA is currently not implemented, * but we only build up Block Ack session with HT STAs. This information * is set when we receive a bss info from a probe response or a beacon. */ if (sta->sdata->vif.type == NL80211_IFTYPE_ADHOC && !sta->sta.ht_cap.ht_supported) { ht_dbg(sdata, "BA request denied - IBSS STA %pM does not advertise HT support\n", pubsta->addr); return -EINVAL; } spin_lock_bh(&sta->lock); /* we have tried too many times, receiver does not want A-MPDU */ if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { ret = -EBUSY; goto err_unlock_sta; } /* * if we have tried more than HT_AGG_BURST_RETRIES times we * will spread our requests in time to avoid stalling connection * for too long */ if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_BURST_RETRIES && time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] + HT_AGG_RETRIES_PERIOD)) { ht_dbg(sdata, "BA request denied - waiting a grace period after %d failed requests on tid %u\n", sta->ampdu_mlme.addba_req_num[tid], tid); ret = -EBUSY; goto err_unlock_sta; } tid_tx = rcu_dereference_protected_tid_tx(sta, tid); /* check if the TID is not in aggregation flow already */ if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { ht_dbg(sdata, "BA request denied - session is not idle on tid %u\n", tid); ret = -EAGAIN; goto err_unlock_sta; } /* prepare A-MPDU MLME for Tx aggregation */ tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); if (!tid_tx) { ret = -ENOMEM; goto err_unlock_sta; } skb_queue_head_init(&tid_tx->pending); __set_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); tid_tx->timeout = timeout; /* response timer */ tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; init_timer(&tid_tx->addba_resp_timer); /* tx timer */ tid_tx->session_timer.function = sta_tx_agg_session_timer_expired; tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; init_timer_deferrable(&tid_tx->session_timer); /* assign a dialog token */ sta->ampdu_mlme.dialog_token_allocator++; tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; /* * Finally, assign it to the start array; the work item will * collect it and move it to the normal array. */ sta->ampdu_mlme.tid_start_tx[tid] = tid_tx; ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); /* this flow continues off the work */ err_unlock_sta: spin_unlock_bh(&sta->lock); return ret; } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); static void ieee80211_agg_tx_operational(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { struct tid_ampdu_tx *tid_tx; lockdep_assert_held(&sta->ampdu_mlme.mtx); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); ht_dbg(sta->sdata, "Aggregation is on for tid %d\n", tid); drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, &sta->sta, tid, NULL, tid_tx->buf_size); /* * synchronize with TX path, while splicing the TX path * should block so it won't put more packets onto pending. */ spin_lock_bh(&sta->lock); ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); /* * Now mark as operational. This will be visible * in the TX path, and lets it go lock-free in * the common case. */ set_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); ieee80211_agg_splice_finish(sta->sdata, tid); spin_unlock_bh(&sta->lock); } void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct tid_ampdu_tx *tid_tx; trace_api_start_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); return; } mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, ra); if (!sta) { mutex_unlock(&local->sta_mtx); ht_dbg(sdata, "Could not find station: %pM\n", ra); return; } mutex_lock(&sta->ampdu_mlme.mtx); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (WARN_ON(!tid_tx)) { ht_dbg(sdata, "addBA was not requested!\n"); goto unlock; } if (WARN_ON(test_and_set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))) goto unlock; if (test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) ieee80211_agg_tx_operational(local, sta, tid); unlock: mutex_unlock(&sta->ampdu_mlme.mtx); mutex_unlock(&local->sta_mtx); } void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, u16 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct ieee80211_ra_tid *ra_tid; struct sk_buff *skb = dev_alloc_skb(0); if (unlikely(!skb)) return; ra_tid = (struct ieee80211_ra_tid *) &skb->cb; memcpy(&ra_tid->ra, ra, ETH_ALEN); ra_tid->tid = tid; skb->pkt_type = IEEE80211_SDATA_QUEUE_AGG_START; skb_queue_tail(&sdata->skb_queue, skb); ieee80211_queue_work(&local->hw, &sdata->work); } EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe); int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, enum ieee80211_back_parties initiator, bool tx) { int ret; mutex_lock(&sta->ampdu_mlme.mtx); ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx); mutex_unlock(&sta->ampdu_mlme.mtx); return ret; } int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct tid_ampdu_tx *tid_tx; int ret = 0; trace_api_stop_tx_ba_session(pubsta, tid); if (!local->ops->ampdu_action) return -EINVAL; if (tid >= STA_TID_NUM) return -EINVAL; spin_lock_bh(&sta->lock); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx) { ret = -ENOENT; goto unlock; } if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { /* already in progress stopping it */ ret = 0; goto unlock; } set_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state); ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); unlock: spin_unlock_bh(&sta->lock); return ret; } EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct tid_ampdu_tx *tid_tx; trace_api_stop_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); return; } ht_dbg(sdata, "Stopping Tx BA session for %pM tid %d\n", ra, tid); mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, ra); if (!sta) { ht_dbg(sdata, "Could not find station: %pM\n", ra); goto unlock; } mutex_lock(&sta->ampdu_mlme.mtx); spin_lock_bh(&sta->lock); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { ht_dbg(sdata, "unexpected callback to A-MPDU stop\n"); goto unlock_sta; } if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR && tid_tx->tx_stop) ieee80211_send_delba(sta->sdata, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); /* * When we get here, the TX path will not be lockless any more wrt. * aggregation, since the OPERATIONAL bit has long been cleared. * Thus it will block on getting the lock, if it occurs. So if we * stop the queue now, we will not get any more packets, and any * that might be being processed will wait for us here, thereby * guaranteeing that no packets go to the tid_tx pending queue any * more. */ ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); /* future packets must not find the tid_tx struct any more */ ieee80211_assign_tid_tx(sta, tid, NULL); ieee80211_agg_splice_finish(sta->sdata, tid); kfree_rcu(tid_tx, rcu_head); unlock_sta: spin_unlock_bh(&sta->lock); mutex_unlock(&sta->ampdu_mlme.mtx); unlock: mutex_unlock(&local->sta_mtx); } void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, u16 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct ieee80211_ra_tid *ra_tid; struct sk_buff *skb = dev_alloc_skb(0); if (unlikely(!skb)) return; ra_tid = (struct ieee80211_ra_tid *) &skb->cb; memcpy(&ra_tid->ra, ra, ETH_ALEN); ra_tid->tid = tid; skb->pkt_type = IEEE80211_SDATA_QUEUE_AGG_STOP; skb_queue_tail(&sdata->skb_queue, skb); ieee80211_queue_work(&local->hw, &sdata->work); } EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe); void ieee80211_process_addba_resp(struct ieee80211_local *local, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len) { struct tid_ampdu_tx *tid_tx; u16 capab, tid; u8 buf_size; capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; mutex_lock(&sta->ampdu_mlme.mtx); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx) goto out; if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { ht_dbg(sta->sdata, "wrong addBA response token, tid %d\n", tid); goto out; } del_timer_sync(&tid_tx->addba_resp_timer); ht_dbg(sta->sdata, "switched off addBA timer for tid %d\n", tid); /* * addba_resp_timer may have fired before we got here, and * caused WANT_STOP to be set. If the stop then was already * processed further, STOPPING might be set. */ if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { ht_dbg(sta->sdata, "got addBA resp for tid %d but we already gave up\n", tid); goto out; } /* * IEEE 802.11-2007 7.3.1.14: * In an ADDBA Response frame, when the Status Code field * is set to 0, the Buffer Size subfield is set to a value * of at least 1. */ if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) == WLAN_STATUS_SUCCESS && buf_size) { if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { /* ignore duplicate response */ goto out; } tid_tx->buf_size = buf_size; if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state)) ieee80211_agg_tx_operational(local, sta, tid); sta->ampdu_mlme.addba_req_num[tid] = 0; if (tid_tx->timeout) { mod_timer(&tid_tx->session_timer, TU_TO_EXP_TIME(tid_tx->timeout)); tid_tx->last_tx = jiffies; } } else { ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, true); } out: mutex_unlock(&sta->ampdu_mlme.mtx); } compat-drivers-2012-09-18/net/mac80211/tkip.h0000644000175000017500000000162512026211315017426 0ustar mcgrofmcgrof/* * Copyright 2002-2004, Instant802 Networks, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef TKIP_H #define TKIP_H #include #include #include "key.h" u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key); int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, struct sk_buff *skb, u8 *payload, size_t payload_len); enum { TKIP_DECRYPT_OK = 0, TKIP_DECRYPT_NO_EXT_IV = -1, TKIP_DECRYPT_INVALID_KEYIDX = -2, TKIP_DECRYPT_REPLAY = -3, }; int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, u8 *payload, size_t payload_len, u8 *ta, u8 *ra, int only_iv, int queue, u32 *out_iv32, u16 *out_iv16); #endif /* TKIP_H */ compat-drivers-2012-09-18/net/mac80211/led.h0000644000175000017500000000414412026211315017222 0ustar mcgrofmcgrof/* * Copyright 2006, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "ieee80211_i.h" #ifdef CONFIG_MAC80211_LEDS void ieee80211_led_rx(struct ieee80211_local *local); void ieee80211_led_tx(struct ieee80211_local *local, int q); void ieee80211_led_assoc(struct ieee80211_local *local, bool associated); void ieee80211_led_radio(struct ieee80211_local *local, bool enabled); void ieee80211_led_names(struct ieee80211_local *local); void ieee80211_led_init(struct ieee80211_local *local); void ieee80211_led_exit(struct ieee80211_local *local); void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, unsigned int types_on, unsigned int types_off); #else static inline void ieee80211_led_rx(struct ieee80211_local *local) { } static inline void ieee80211_led_tx(struct ieee80211_local *local, int q) { } static inline void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) { } static inline void ieee80211_led_radio(struct ieee80211_local *local, bool enabled) { } static inline void ieee80211_led_names(struct ieee80211_local *local) { } static inline void ieee80211_led_init(struct ieee80211_local *local) { } static inline void ieee80211_led_exit(struct ieee80211_local *local) { } static inline void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, unsigned int types_on, unsigned int types_off) { } #endif static inline void ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes) { #ifdef CONFIG_MAC80211_LEDS if (local->tpt_led_trigger && ieee80211_is_data(fc)) local->tpt_led_trigger->tx_bytes += bytes; #endif } static inline void ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes) { #ifdef CONFIG_MAC80211_LEDS if (local->tpt_led_trigger && ieee80211_is_data(fc)) local->tpt_led_trigger->rx_bytes += bytes; #endif } compat-drivers-2012-09-18/net/mac80211/rc80211_minstrel_ht.h0000644000175000017500000000560012026211315022064 0ustar mcgrofmcgrof/* * Copyright (C) 2010 Felix Fietkau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef __RC_MINSTREL_HT_H #define __RC_MINSTREL_HT_H /* * The number of streams can be changed to 2 to reduce code * size and memory footprint. */ #define MINSTREL_MAX_STREAMS 3 #define MINSTREL_STREAM_GROUPS 4 /* scaled fraction values */ #define MINSTREL_SCALE 16 #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) #define MCS_GROUP_RATES 8 struct mcs_group { u32 flags; unsigned int streams; unsigned int duration[MCS_GROUP_RATES]; }; extern const struct mcs_group minstrel_mcs_groups[]; struct minstrel_rate_stats { /* current / last sampling period attempts/success counters */ unsigned int attempts, last_attempts; unsigned int success, last_success; /* total attempts/success counters */ u64 att_hist, succ_hist; /* current throughput */ unsigned int cur_tp; /* packet delivery probabilities */ unsigned int cur_prob, probability; /* maximum retry counts */ unsigned int retry_count; unsigned int retry_count_rtscts; bool retry_updated; u8 sample_skipped; }; struct minstrel_mcs_group_data { u8 index; u8 column; /* bitfield of supported MCS rates of this group */ u8 supported; /* selected primary rates */ unsigned int max_tp_rate; unsigned int max_tp_rate2; unsigned int max_prob_rate; /* MCS rate statistics */ struct minstrel_rate_stats rates[MCS_GROUP_RATES]; }; struct minstrel_ht_sta { /* ampdu length (average, per sampling interval) */ unsigned int ampdu_len; unsigned int ampdu_packets; /* ampdu length (EWMA) */ unsigned int avg_ampdu_len; /* best throughput rate */ unsigned int max_tp_rate; /* second best throughput rate */ unsigned int max_tp_rate2; /* best probability rate */ unsigned int max_prob_rate; /* time of last status update */ unsigned long stats_update; /* overhead time in usec for each frame */ unsigned int overhead; unsigned int overhead_rtscts; unsigned int total_packets; unsigned int sample_packets; /* tx flags to add for frames for this sta */ u32 tx_flags; u8 sample_wait; u8 sample_tries; u8 sample_count; u8 sample_slow; /* current MCS group to be sampled */ u8 sample_group; /* MCS rate group info and statistics */ struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS]; }; struct minstrel_ht_sta_priv { union { struct minstrel_ht_sta ht; struct minstrel_sta_info legacy; }; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *dbg_stats; #endif void *ratelist; void *sample_table; bool is_ht; }; void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta); #endif compat-drivers-2012-09-18/net/mac80211/ibss.c0000644000175000017500000007775212026211315017430 0ustar mcgrofmcgrof/* * IBSS mode implementation * Copyright 2003-2008, Jouni Malinen * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2009, Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "rate.h" #define IEEE80211_SCAN_INTERVAL (2 * HZ) #define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ) #define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ) #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ) #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const int beacon_int, struct ieee80211_channel *chan, const u32 basic_rates, const u16 capability, u64 tsf) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; int rates, i; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos; struct ieee80211_supported_band *sband; struct cfg80211_bss *bss; u32 bss_change; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; enum nl80211_channel_type channel_type; lockdep_assert_held(&ifibss->mtx); /* Reset own TSF to allow time synchronization work. */ drv_reset_tsf(local, sdata); skb = ifibss->skb; RCU_INIT_POINTER(ifibss->presp, NULL); synchronize_rcu(); skb->data = skb->head; skb->len = 0; skb_reset_tail_pointer(skb); skb_reserve(skb, sdata->local->hw.extra_tx_headroom); if (!ether_addr_equal(ifibss->bssid, bssid)) sta_info_flush(sdata->local, sdata); /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.bss_conf.ibss_joined) { sdata->vif.bss_conf.ibss_joined = false; netif_carrier_off(sdata->dev); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS); } memcpy(ifibss->bssid, bssid, ETH_ALEN); sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; local->oper_channel = chan; channel_type = ifibss->channel_type; if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) channel_type = NL80211_CHAN_HT20; if (!ieee80211_set_channel_type(local, sdata, channel_type)) { /* can only fail due to HT40+/- mismatch */ channel_type = NL80211_CHAN_HT20; WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_HT20)); } ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); sband = local->hw.wiphy->bands[chan->band]; /* build supported rates array */ pos = supp_rates; for (i = 0; i < sband->n_bitrates; i++) { int rate = sband->bitrates[i].bitrate; u8 basic = 0; if (basic_rates & BIT(i)) basic = 0x80; *pos++ = basic | (u8) (rate / 5); } /* Build IBSS probe response */ mgmt = (void *) skb_put(skb, 24 + sizeof(mgmt->u.beacon)); memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); eth_broadcast_addr(mgmt->da); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int); mgmt->u.beacon.timestamp = cpu_to_le64(tsf); mgmt->u.beacon.capab_info = cpu_to_le16(capability); pos = skb_put(skb, 2 + ifibss->ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = ifibss->ssid_len; memcpy(pos, ifibss->ssid, ifibss->ssid_len); rates = sband->n_bitrates; if (rates > 8) rates = 8; pos = skb_put(skb, 2 + rates); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = rates; memcpy(pos, supp_rates, rates); if (sband->band == IEEE80211_BAND_2GHZ) { pos = skb_put(skb, 2 + 1); *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = ieee80211_frequency_to_channel(chan->center_freq); } pos = skb_put(skb, 2 + 2); *pos++ = WLAN_EID_IBSS_PARAMS; *pos++ = 2; /* FIX: set ATIM window based on scan results */ *pos++ = 0; *pos++ = 0; if (sband->n_bitrates > 8) { rates = sband->n_bitrates - 8; pos = skb_put(skb, 2 + rates); *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = rates; memcpy(pos, &supp_rates[8], rates); } if (ifibss->ie_len) memcpy(skb_put(skb, ifibss->ie_len), ifibss->ie, ifibss->ie_len); /* add HT capability and information IEs */ if (channel_type && sband->ht_cap.ht_supported) { pos = skb_put(skb, 4 + sizeof(struct ieee80211_ht_cap) + sizeof(struct ieee80211_ht_operation)); pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); /* * Note: According to 802.11n-2009 9.13.3.1, HT Protection * field and RIFS Mode are reserved in IBSS mode, therefore * keep them at 0 */ pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, chan, channel_type, 0); } if (local->hw.queues >= IEEE80211_NUM_ACS) { pos = skb_put(skb, 9); *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 7; /* len */ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ *pos++ = 0x50; *pos++ = 0xf2; *pos++ = 2; /* WME */ *pos++ = 0; /* WME info */ *pos++ = 1; /* WME ver */ *pos++ = 0; /* U-APSD no in use */ } rcu_assign_pointer(ifibss->presp, skb); sdata->vif.bss_conf.beacon_int = beacon_int; sdata->vif.bss_conf.basic_rates = basic_rates; bss_change = BSS_CHANGED_BEACON_INT; bss_change |= ieee80211_reset_erp_info(sdata); bss_change |= BSS_CHANGED_BSSID; bss_change |= BSS_CHANGED_BEACON; bss_change |= BSS_CHANGED_BEACON_ENABLED; bss_change |= BSS_CHANGED_BASIC_RATES; bss_change |= BSS_CHANGED_HT; bss_change |= BSS_CHANGED_IBSS; sdata->vif.bss_conf.ibss_joined = true; ieee80211_bss_info_change_notify(sdata, bss_change); ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates); ifibss->state = IEEE80211_IBSS_MLME_JOINED; mod_timer(&ifibss->timer, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan, mgmt, skb->len, 0, GFP_KERNEL); cfg80211_put_bss(bss); netif_carrier_on(sdata->dev); cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); } static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss) { struct cfg80211_bss *cbss = container_of((void *)bss, struct cfg80211_bss, priv); struct ieee80211_supported_band *sband; u32 basic_rates; int i, j; u16 beacon_int = cbss->beacon_interval; lockdep_assert_held(&sdata->u.ibss.mtx); if (beacon_int < 10) beacon_int = 10; sband = sdata->local->hw.wiphy->bands[cbss->channel->band]; basic_rates = 0; for (i = 0; i < bss->supp_rates_len; i++) { int rate = (bss->supp_rates[i] & 0x7f) * 5; bool is_basic = !!(bss->supp_rates[i] & 0x80); for (j = 0; j < sband->n_bitrates; j++) { if (sband->bitrates[j].bitrate == rate) { if (is_basic) basic_rates |= BIT(j); break; } } } __ieee80211_sta_join_ibss(sdata, cbss->bssid, beacon_int, cbss->channel, basic_rates, cbss->capability, cbss->tsf); } static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, bool auth) __acquires(RCU) { struct ieee80211_sub_if_data *sdata = sta->sdata; u8 addr[ETH_ALEN]; memcpy(addr, sta->sta.addr, ETH_ALEN); ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr); sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); /* authorize the station only if the network is not RSN protected. If * not wait for the userspace to authorize it */ if (!sta->sdata->u.ibss.control_port) sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); rate_control_rate_init(sta); /* If it fails, maybe we raced another insertion? */ if (sta_info_insert_rcu(sta)) return sta_info_get(sdata, addr); if (auth && !sdata->u.ibss.auth_frame_registrations) { ibss_dbg(sdata, "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n", sdata->vif.addr, sdata->u.ibss.bssid, addr); ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0, addr, sdata->u.ibss.bssid, NULL, 0, 0); } return sta; } static struct sta_info * ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const u8 *addr, u32 supp_rates, bool auth) __acquires(RCU) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct sta_info *sta; int band = local->oper_channel->band; /* * XXX: Consider removing the least recently used entry and * allow new one to be added. */ if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n", sdata->name, addr); rcu_read_lock(); return NULL; } if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) { rcu_read_lock(); return NULL; } if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) { rcu_read_lock(); return NULL; } sta = sta_info_alloc(sdata, addr, GFP_KERNEL); if (!sta) { rcu_read_lock(); return NULL; } sta->last_rx = jiffies; /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | ieee80211_mandatory_rates(local, band); return ieee80211_ibss_finish_sta(sta, auth); } static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { u16 auth_alg, auth_transaction; lockdep_assert_held(&sdata->u.ibss.mtx); if (len < 24 + 6) return; auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) return; ibss_dbg(sdata, "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction); sta_info_destroy_addr(sdata, mgmt->sa); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false); rcu_read_unlock(); /* * IEEE 802.11 standard does not require authentication in IBSS * networks and most implementations do not seem to use it. * However, try to reply to authentication attempts if someone * has actually implemented this. */ ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0, mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0); } static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status, struct ieee802_11_elems *elems, bool beacon) { struct ieee80211_local *local = sdata->local; int freq; struct cfg80211_bss *cbss; struct ieee80211_bss *bss; struct sta_info *sta; struct ieee80211_channel *channel; u64 beacon_timestamp, rx_timestamp; u32 supp_rates = 0; enum ieee80211_band band = rx_status->band; struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; bool rates_updated = false; if (elems->ds_params && elems->ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems->ds_params[0], band); else freq = rx_status->freq; channel = ieee80211_get_channel(local->hw.wiphy, freq); if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; if (sdata->vif.type == NL80211_IFTYPE_ADHOC && ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) { rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (elems->supp_rates) { supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL); if (sta) { u32 prev_rates; prev_rates = sta->sta.supp_rates[band]; /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | ieee80211_mandatory_rates(local, band); if (sta->sta.supp_rates[band] != prev_rates) { ibss_dbg(sdata, "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", sta->sta.addr, prev_rates, sta->sta.supp_rates[band]); rates_updated = true; } } else { rcu_read_unlock(); sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates, true); } } if (sta && elems->wmm_info) set_sta_flag(sta, WLAN_STA_WME); if (sta && elems->ht_operation && elems->ht_cap_elem && sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { /* we both use HT */ struct ieee80211_sta_ht_cap sta_ht_cap_new; enum nl80211_channel_type channel_type = ieee80211_ht_oper_to_channel_type( elems->ht_operation); ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, &sta_ht_cap_new); /* * fall back to HT20 if we don't use or use * the other extension channel */ if (!(channel_type == NL80211_CHAN_HT40MINUS || channel_type == NL80211_CHAN_HT40PLUS) || channel_type != sdata->u.ibss.channel_type) sta_ht_cap_new.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new, sizeof(sta_ht_cap_new))) { memcpy(&sta->sta.ht_cap, &sta_ht_cap_new, sizeof(sta_ht_cap_new)); rates_updated = true; } } if (sta && rates_updated) { drv_sta_rc_update(local, sdata, &sta->sta, IEEE80211_RC_SUPP_RATES_CHANGED); rate_control_rate_init(sta); } rcu_read_unlock(); } bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, channel, beacon); if (!bss) return; cbss = container_of((void *)bss, struct cfg80211_bss, priv); /* was just updated in ieee80211_bss_info_update */ beacon_timestamp = cbss->tsf; /* check if we need to merge IBSS */ /* we use a fixed BSSID */ if (sdata->u.ibss.fixed_bssid) goto put_bss; /* not an IBSS */ if (!(cbss->capability & WLAN_CAPABILITY_IBSS)) goto put_bss; /* different channel */ if (cbss->channel != local->oper_channel) goto put_bss; /* different SSID */ if (elems->ssid_len != sdata->u.ibss.ssid_len || memcmp(elems->ssid, sdata->u.ibss.ssid, sdata->u.ibss.ssid_len)) goto put_bss; /* same BSSID */ if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) goto put_bss; if (rx_status->flag & RX_FLAG_MACTIME_MPDU) { /* * For correct IBSS merging we need mactime; since mactime is * defined as the time the first data symbol of the frame hits * the PHY, and the timestamp of the beacon is defined as "the * time that the data symbol containing the first bit of the * timestamp is transmitted to the PHY plus the transmitting * STA's delays through its local PHY from the MAC-PHY * interface to its interface with the WM" (802.11 11.1.2) * - equals the time this bit arrives at the receiver - we have * to take into account the offset between the two. * * E.g. at 1 MBit that means mactime is 192 usec earlier * (=24 bytes * 8 usecs/byte) than the beacon timestamp. */ int rate; if (rx_status->flag & RX_FLAG_HT) rate = 65; /* TODO: HT rates */ else rate = local->hw.wiphy->bands[band]-> bitrates[rx_status->rate_idx].bitrate; rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate); } else { /* * second best option: get current TSF * (will return -1 if not supported) */ rx_timestamp = drv_get_tsf(local, sdata); } ibss_dbg(sdata, "RX beacon SA=%pM BSSID=%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n", mgmt->sa, mgmt->bssid, (unsigned long long)rx_timestamp, (unsigned long long)beacon_timestamp, (unsigned long long)(rx_timestamp - beacon_timestamp), jiffies); if (beacon_timestamp > rx_timestamp) { ibss_dbg(sdata, "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n", mgmt->bssid); ieee80211_sta_join_ibss(sdata, bss); supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates, true); rcu_read_unlock(); } put_bss: ieee80211_rx_bss_put(local, bss); } void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const u8 *addr, u32 supp_rates) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct sta_info *sta; int band = local->oper_channel->band; /* * XXX: Consider removing the least recently used entry and * allow new one to be added. */ if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n", sdata->name, addr); return; } if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) return; if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) return; sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); if (!sta) return; sta->last_rx = jiffies; /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | ieee80211_mandatory_rates(local, band); spin_lock(&ifibss->incomplete_lock); list_add(&sta->list, &ifibss->incomplete_stations); spin_unlock(&ifibss->incomplete_lock); ieee80211_queue_work(&local->hw, &sdata->work); } static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; int active = 0; struct sta_info *sta; lockdep_assert_held(&sdata->u.ibss.mtx); rcu_read_lock(); list_for_each_entry_rcu(sta, &local->sta_list, list) { if (sta->sdata == sdata && time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, jiffies)) { active++; break; } } rcu_read_unlock(); return active; } /* * This function is called with state == IEEE80211_IBSS_MLME_JOINED */ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; lockdep_assert_held(&ifibss->mtx); mod_timer(&ifibss->timer, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT); if (time_before(jiffies, ifibss->last_scan_completed + IEEE80211_IBSS_MERGE_INTERVAL)) return; if (ieee80211_sta_active_ibss(sdata)) return; if (ifibss->fixed_channel) return; sdata_info(sdata, "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n"); ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len, NULL); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; u8 bssid[ETH_ALEN]; u16 capability; int i; lockdep_assert_held(&ifibss->mtx); if (ifibss->fixed_bssid) { memcpy(bssid, ifibss->bssid, ETH_ALEN); } else { /* Generate random, not broadcast, locally administered BSSID. Mix in * own MAC address to make sure that devices that do not have proper * random number generator get different BSSID. */ get_random_bytes(bssid, ETH_ALEN); for (i = 0; i < ETH_ALEN; i++) bssid[i] ^= sdata->vif.addr[i]; bssid[0] &= ~0x01; bssid[0] |= 0x02; } sdata_info(sdata, "Creating new IBSS network, BSSID %pM\n", bssid); capability = WLAN_CAPABILITY_IBSS; if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; else sdata->drop_unencrypted = 0; __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, ifibss->channel, ifibss->basic_rates, capability, 0); } /* * This function is called with state == IEEE80211_IBSS_MLME_SEARCH */ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct cfg80211_bss *cbss; struct ieee80211_channel *chan = NULL; const u8 *bssid = NULL; int active_ibss; u16 capability; lockdep_assert_held(&ifibss->mtx); active_ibss = ieee80211_sta_active_ibss(sdata); ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss); if (active_ibss) return; capability = WLAN_CAPABILITY_IBSS; if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; if (ifibss->fixed_bssid) bssid = ifibss->bssid; if (ifibss->fixed_channel) chan = ifibss->channel; if (!is_zero_ether_addr(ifibss->bssid)) bssid = ifibss->bssid; cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid, ifibss->ssid, ifibss->ssid_len, WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY, capability); if (cbss) { struct ieee80211_bss *bss; bss = (void *)cbss->priv; ibss_dbg(sdata, "sta_find_ibss: selected %pM current %pM\n", cbss->bssid, ifibss->bssid); sdata_info(sdata, "Selected IBSS BSSID %pM based on configured SSID\n", cbss->bssid); ieee80211_sta_join_ibss(sdata, bss); ieee80211_rx_bss_put(local, bss); return; } ibss_dbg(sdata, "sta_find_ibss: did not try to join ibss\n"); /* Selected IBSS not found in current scan results - try to scan */ if (time_after(jiffies, ifibss->last_scan_completed + IEEE80211_SCAN_INTERVAL)) { sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len, ifibss->fixed_channel ? ifibss->channel : NULL); } else { int interval = IEEE80211_SCAN_INTERVAL; if (time_after(jiffies, ifibss->ibss_join_req + IEEE80211_IBSS_JOIN_TIMEOUT)) { if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) { ieee80211_sta_create_ibss(sdata); return; } sdata_info(sdata, "IBSS not allowed on %d MHz\n", local->oper_channel->center_freq); /* No IBSS found - decrease scan interval and continue * scanning. */ interval = IEEE80211_SCAN_INTERVAL_SLOW; } mod_timer(&ifibss->timer, round_jiffies(jiffies + interval)); } } static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, struct sk_buff *req) { struct ieee80211_mgmt *mgmt = (void *)req->data; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; int tx_last_beacon, len = req->len; struct sk_buff *skb; struct ieee80211_mgmt *resp; struct sk_buff *presp; u8 *pos, *end; lockdep_assert_held(&ifibss->mtx); presp = rcu_dereference_protected(ifibss->presp, lockdep_is_held(&ifibss->mtx)); if (ifibss->state != IEEE80211_IBSS_MLME_JOINED || len < 24 + 2 || !presp) return; tx_last_beacon = drv_tx_last_beacon(local); ibss_dbg(sdata, "RX ProbeReq SA=%pM DA=%pM BSSID=%pM (tx_last_beacon=%d)\n", mgmt->sa, mgmt->da, mgmt->bssid, tx_last_beacon); if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da)) return; if (!ether_addr_equal(mgmt->bssid, ifibss->bssid) && !is_broadcast_ether_addr(mgmt->bssid)) return; end = ((u8 *) mgmt) + len; pos = mgmt->u.probe_req.variable; if (pos[0] != WLAN_EID_SSID || pos + 2 + pos[1] > end) { ibss_dbg(sdata, "Invalid SSID IE in ProbeReq from %pM\n", mgmt->sa); return; } if (pos[1] != 0 && (pos[1] != ifibss->ssid_len || memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) { /* Ignore ProbeReq for foreign SSID */ return; } /* Reply with ProbeResp */ skb = skb_copy(presp, GFP_KERNEL); if (!skb) return; resp = (struct ieee80211_mgmt *) skb->data; memcpy(resp->da, mgmt->sa, ETH_ALEN); ibss_dbg(sdata, "Sending ProbeResp to %pM\n", resp->da); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { size_t baselen; struct ieee802_11_elems elems; baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; if (baselen > len) return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); } static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { size_t baselen; struct ieee802_11_elems elems; /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; if (baselen > len) return; ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); } void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; u16 fc; rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); mutex_lock(&sdata->u.ibss.mtx); if (!sdata->u.ibss.ssid_len) goto mgmt_out; /* not ready to merge yet */ switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PROBE_REQ: ieee80211_rx_mgmt_probe_req(sdata, skb); break; case IEEE80211_STYPE_PROBE_RESP: ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, rx_status); break; case IEEE80211_STYPE_BEACON: ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status); break; case IEEE80211_STYPE_AUTH: ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len); break; } mgmt_out: mutex_unlock(&sdata->u.ibss.mtx); } void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct sta_info *sta; mutex_lock(&ifibss->mtx); /* * Work could be scheduled after scan or similar * when we aren't even joined (or trying) with a * network. */ if (!ifibss->ssid_len) goto out; spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { sta = list_first_entry(&ifibss->incomplete_stations, struct sta_info, list); list_del(&sta->list); spin_unlock_bh(&ifibss->incomplete_lock); ieee80211_ibss_finish_sta(sta, true); rcu_read_unlock(); spin_lock_bh(&ifibss->incomplete_lock); } spin_unlock_bh(&ifibss->incomplete_lock); switch (ifibss->state) { case IEEE80211_IBSS_MLME_SEARCH: ieee80211_sta_find_ibss(sdata); break; case IEEE80211_IBSS_MLME_JOINED: ieee80211_sta_merge_ibss(sdata); break; default: WARN_ON(1); break; } out: mutex_unlock(&ifibss->mtx); } static void ieee80211_ibss_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; if (local->quiescing) { ifibss->timer_running = true; return; } ieee80211_queue_work(&local->hw, &sdata->work); } #ifdef CONFIG_PM void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; if (del_timer_sync(&ifibss->timer)) ifibss->timer_running = true; } void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; if (ifibss->timer_running) { add_timer(&ifibss->timer); ifibss->timer_running = false; } } #endif void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; setup_timer(&ifibss->timer, ieee80211_ibss_timer, (unsigned long) sdata); mutex_init(&ifibss->mtx); INIT_LIST_HEAD(&ifibss->incomplete_stations); spin_lock_init(&ifibss->incomplete_lock); } /* scan finished notification */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; if (sdata->vif.type != NL80211_IFTYPE_ADHOC) continue; sdata->u.ibss.last_scan_completed = jiffies; ieee80211_queue_work(&local->hw, &sdata->work); } mutex_unlock(&local->iflist_mtx); } int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, struct cfg80211_ibss_params *params) { struct sk_buff *skb; u32 changed = 0; skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + sizeof(struct ieee80211_hdr_3addr) + 12 /* struct ieee80211_mgmt.u.beacon */ + 2 + IEEE80211_MAX_SSID_LEN /* max SSID */ + 2 + 8 /* max Supported Rates */ + 3 /* max DS params */ + 4 /* IBSS params */ + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_operation) + params->ie_len); if (!skb) return -ENOMEM; mutex_lock(&sdata->u.ibss.mtx); if (params->bssid) { memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); sdata->u.ibss.fixed_bssid = true; } else sdata->u.ibss.fixed_bssid = false; sdata->u.ibss.privacy = params->privacy; sdata->u.ibss.control_port = params->control_port; sdata->u.ibss.basic_rates = params->basic_rates; memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate, sizeof(params->mcast_rate)); sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->u.ibss.channel = params->channel; sdata->u.ibss.channel_type = params->channel_type; sdata->u.ibss.fixed_channel = params->channel_fixed; /* fix ourselves to that channel now already */ if (params->channel_fixed) { sdata->local->oper_channel = params->channel; if (!ieee80211_set_channel_type(sdata->local, sdata, params->channel_type)) { mutex_unlock(&sdata->u.ibss.mtx); kfree_skb(skb); return -EINVAL; } } if (params->ie) { sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len, GFP_KERNEL); if (sdata->u.ibss.ie) sdata->u.ibss.ie_len = params->ie_len; } sdata->u.ibss.skb = skb; sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH; sdata->u.ibss.ibss_join_req = jiffies; memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); sdata->u.ibss.ssid_len = params->ssid_len; mutex_unlock(&sdata->u.ibss.mtx); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); /* * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is * reserved, but an HT STA shall protect HT transmissions as though * the HT Protection field were set to non-HT mixed mode. * * In an IBSS, the RIFS Mode field of the HT Operation element is * also reserved, but an HT STA shall operate as though this field * were set to 1. */ sdata->vif.bss_conf.ht_operation_mode |= IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED | IEEE80211_HT_PARAM_RIFS_MODE; changed |= BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); ieee80211_queue_work(&sdata->local->hw, &sdata->work); return 0; } int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) { struct sk_buff *skb; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct cfg80211_bss *cbss; u16 capability; int active_ibss; struct sta_info *sta; mutex_lock(&sdata->u.ibss.mtx); sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH; memset(sdata->u.ibss.bssid, 0, ETH_ALEN); sdata->u.ibss.ssid_len = 0; active_ibss = ieee80211_sta_active_ibss(sdata); if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { capability = WLAN_CAPABILITY_IBSS; if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->channel, ifibss->bssid, ifibss->ssid, ifibss->ssid_len, WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY, capability); if (cbss) { cfg80211_unlink_bss(local->hw.wiphy, cbss); cfg80211_put_bss(cbss); } } sta_info_flush(sdata->local, sdata); spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { sta = list_first_entry(&ifibss->incomplete_stations, struct sta_info, list); list_del(&sta->list); spin_unlock_bh(&ifibss->incomplete_lock); sta_info_free(local, sta); spin_lock_bh(&ifibss->incomplete_lock); } spin_unlock_bh(&ifibss->incomplete_lock); netif_carrier_off(sdata->dev); /* remove beacon */ kfree(sdata->u.ibss.ie); skb = rcu_dereference_protected(sdata->u.ibss.presp, lockdep_is_held(&sdata->u.ibss.mtx)); RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); sdata->vif.bss_conf.ibss_joined = false; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); synchronize_rcu(); kfree_skb(skb); skb_queue_purge(&sdata->skb_queue); del_timer_sync(&sdata->u.ibss.timer); mutex_unlock(&sdata->u.ibss.mtx); mutex_lock(&local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&local->mtx); return 0; } compat-drivers-2012-09-18/net/mac80211/debugfs_key.c0000644000175000017500000002200412026211315020733 0ustar mcgrofmcgrof/* * Copyright 2003-2005 Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2007 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include "ieee80211_i.h" #include "key.h" #include "debugfs.h" #include "debugfs_key.h" #define KEY_READ(name, prop, format_string) \ static ssize_t key_##name##_read(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct ieee80211_key *key = file->private_data; \ return mac80211_format_buffer(userbuf, count, ppos, \ format_string, key->prop); \ } #define KEY_READ_D(name) KEY_READ(name, name, "%d\n") #define KEY_READ_X(name) KEY_READ(name, name, "0x%x\n") #define KEY_OPS(name) \ static const struct file_operations key_ ##name## _ops = { \ .read = key_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ } #define KEY_FILE(name, format) \ KEY_READ_##format(name) \ KEY_OPS(name) #define KEY_CONF_READ(name, format_string) \ KEY_READ(conf_##name, conf.name, format_string) #define KEY_CONF_READ_D(name) KEY_CONF_READ(name, "%d\n") #define KEY_CONF_OPS(name) \ static const struct file_operations key_ ##name## _ops = { \ .read = key_conf_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ } #define KEY_CONF_FILE(name, format) \ KEY_CONF_READ_##format(name) \ KEY_CONF_OPS(name) KEY_CONF_FILE(keylen, D); KEY_CONF_FILE(keyidx, D); KEY_CONF_FILE(hw_key_idx, D); KEY_FILE(flags, X); KEY_FILE(tx_rx_count, D); KEY_READ(ifindex, sdata->name, "%s\n"); KEY_OPS(ifindex); static ssize_t key_algorithm_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { char buf[15]; struct ieee80211_key *key = file->private_data; u32 c = key->conf.cipher; sprintf(buf, "%.2x-%.2x-%.2x:%d\n", c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); } KEY_OPS(algorithm); static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { u64 pn; char buf[20]; int len; struct ieee80211_key *key = file->private_data; switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: len = scnprintf(buf, sizeof(buf), "\n"); break; case WLAN_CIPHER_SUITE_TKIP: len = scnprintf(buf, sizeof(buf), "%08x %04x\n", key->u.tkip.tx.iv32, key->u.tkip.tx.iv16); break; case WLAN_CIPHER_SUITE_CCMP: pn = atomic64_read(&key->u.ccmp.tx_pn); len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn); break; case WLAN_CIPHER_SUITE_AES_CMAC: pn = atomic64_read(&key->u.aes_cmac.tx_pn); len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn); break; default: return 0; } return simple_read_from_buffer(userbuf, count, ppos, buf, len); } KEY_OPS(tx_spec); static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct ieee80211_key *key = file->private_data; char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf; int i, len; const u8 *rpn; switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: len = scnprintf(buf, sizeof(buf), "\n"); break; case WLAN_CIPHER_SUITE_TKIP: for (i = 0; i < NUM_RX_DATA_QUEUES; i++) p += scnprintf(p, sizeof(buf)+buf-p, "%08x %04x\n", key->u.tkip.rx[i].iv32, key->u.tkip.rx[i].iv16); len = p - buf; break; case WLAN_CIPHER_SUITE_CCMP: for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) { rpn = key->u.ccmp.rx_pn[i]; p += scnprintf(p, sizeof(buf)+buf-p, "%02x%02x%02x%02x%02x%02x\n", rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5]); } len = p - buf; break; case WLAN_CIPHER_SUITE_AES_CMAC: rpn = key->u.aes_cmac.rx_pn; p += scnprintf(p, sizeof(buf)+buf-p, "%02x%02x%02x%02x%02x%02x\n", rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5]); len = p - buf; break; default: return 0; } return simple_read_from_buffer(userbuf, count, ppos, buf, len); } KEY_OPS(rx_spec); static ssize_t key_replays_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct ieee80211_key *key = file->private_data; char buf[20]; int len; switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_CCMP: len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); break; case WLAN_CIPHER_SUITE_AES_CMAC: len = scnprintf(buf, sizeof(buf), "%u\n", key->u.aes_cmac.replays); break; default: return 0; } return simple_read_from_buffer(userbuf, count, ppos, buf, len); } KEY_OPS(replays); static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct ieee80211_key *key = file->private_data; char buf[20]; int len; switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_AES_CMAC: len = scnprintf(buf, sizeof(buf), "%u\n", key->u.aes_cmac.icverrors); break; default: return 0; } return simple_read_from_buffer(userbuf, count, ppos, buf, len); } KEY_OPS(icverrors); static ssize_t key_key_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct ieee80211_key *key = file->private_data; int i, bufsize = 2 * key->conf.keylen + 2; char *buf = kmalloc(bufsize, GFP_KERNEL); char *p = buf; ssize_t res; if (!buf) return -ENOMEM; for (i = 0; i < key->conf.keylen; i++) p += scnprintf(p, bufsize + buf - p, "%02x", key->conf.key[i]); p += scnprintf(p, bufsize+buf-p, "\n"); res = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); kfree(buf); return res; } KEY_OPS(key); #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, key->debugfs.dir, \ key, &key_##name##_ops); void ieee80211_debugfs_key_add(struct ieee80211_key *key) { static int keycount; char buf[100]; struct sta_info *sta; if (!key->local->debugfs.keys) return; sprintf(buf, "%d", keycount); key->debugfs.cnt = keycount; keycount++; key->debugfs.dir = debugfs_create_dir(buf, key->local->debugfs.keys); if (!key->debugfs.dir) return; sta = key->sta; if (sta) { sprintf(buf, "../../netdev:%s/stations/%pM", sta->sdata->name, sta->sta.addr); key->debugfs.stalink = debugfs_create_symlink("station", key->debugfs.dir, buf); } DEBUGFS_ADD(keylen); DEBUGFS_ADD(flags); DEBUGFS_ADD(keyidx); DEBUGFS_ADD(hw_key_idx); DEBUGFS_ADD(tx_rx_count); DEBUGFS_ADD(algorithm); DEBUGFS_ADD(tx_spec); DEBUGFS_ADD(rx_spec); DEBUGFS_ADD(replays); DEBUGFS_ADD(icverrors); DEBUGFS_ADD(key); DEBUGFS_ADD(ifindex); }; void ieee80211_debugfs_key_remove(struct ieee80211_key *key) { if (!key) return; debugfs_remove_recursive(key->debugfs.dir); key->debugfs.dir = NULL; } void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) { char buf[50]; struct ieee80211_key *key; if (!sdata->debugfs.dir) return; lockdep_assert_held(&sdata->local->key_mtx); if (sdata->debugfs.default_unicast_key) { debugfs_remove(sdata->debugfs.default_unicast_key); sdata->debugfs.default_unicast_key = NULL; } if (sdata->default_unicast_key) { key = key_mtx_dereference(sdata->local, sdata->default_unicast_key); sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_unicast_key = debugfs_create_symlink("default_unicast_key", sdata->debugfs.dir, buf); } if (sdata->debugfs.default_multicast_key) { debugfs_remove(sdata->debugfs.default_multicast_key); sdata->debugfs.default_multicast_key = NULL; } if (sdata->default_multicast_key) { key = key_mtx_dereference(sdata->local, sdata->default_multicast_key); sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_multicast_key = debugfs_create_symlink("default_multicast_key", sdata->debugfs.dir, buf); } } void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) { char buf[50]; struct ieee80211_key *key; if (!sdata->debugfs.dir) return; key = key_mtx_dereference(sdata->local, sdata->default_mgmt_key); if (key) { sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_mgmt_key = debugfs_create_symlink("default_mgmt_key", sdata->debugfs.dir, buf); } else ieee80211_debugfs_key_remove_mgmt_default(sdata); } void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sdata) { if (!sdata) return; debugfs_remove(sdata->debugfs.default_mgmt_key); sdata->debugfs.default_mgmt_key = NULL; } void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta) { debugfs_remove(key->debugfs.stalink); key->debugfs.stalink = NULL; } compat-drivers-2012-09-18/net/mac80211/debugfs_sta.h0000644000175000017500000000065312026211315020745 0ustar mcgrofmcgrof#ifndef __MAC80211_DEBUGFS_STA_H #define __MAC80211_DEBUGFS_STA_H #include "sta_info.h" #ifdef CONFIG_MAC80211_DEBUGFS void ieee80211_sta_debugfs_add(struct sta_info *sta); void ieee80211_sta_debugfs_remove(struct sta_info *sta); #else static inline void ieee80211_sta_debugfs_add(struct sta_info *sta) {} static inline void ieee80211_sta_debugfs_remove(struct sta_info *sta) {} #endif #endif /* __MAC80211_DEBUGFS_STA_H */ compat-drivers-2012-09-18/net/mac80211/rc80211_pid_debugfs.c0000644000175000017500000001364512026211315022012 0ustar mcgrofmcgrof/* * Copyright 2007, Mattias Nissler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "rate.h" #include "rc80211_pid.h" static void rate_control_pid_event(struct rc_pid_event_buffer *buf, enum rc_pid_event_type type, union rc_pid_event_data *data) { struct rc_pid_event *ev; unsigned long status; spin_lock_irqsave(&buf->lock, status); ev = &(buf->ring[buf->next_entry]); buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE; ev->timestamp = jiffies; ev->id = buf->ev_count++; ev->type = type; ev->data = *data; spin_unlock_irqrestore(&buf->lock, status); wake_up_all(&buf->waitqueue); } void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, struct ieee80211_tx_info *stat) { union rc_pid_event_data evd; evd.flags = stat->flags; memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info)); rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd); } void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, int index, int rate) { union rc_pid_event_data evd; evd.index = index; evd.rate = rate; rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd); } void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, int index, int rate) { union rc_pid_event_data evd; evd.index = index; evd.rate = rate; rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd); } void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, s32 pf_sample, s32 prop_err, s32 int_err, s32 der_err) { union rc_pid_event_data evd; evd.pf_sample = pf_sample; evd.prop_err = prop_err; evd.int_err = int_err; evd.der_err = der_err; rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd); } static int rate_control_pid_events_open(struct inode *inode, struct file *file) { struct rc_pid_sta_info *sinfo = inode->i_private; struct rc_pid_event_buffer *events = &sinfo->events; struct rc_pid_events_file_info *file_info; unsigned long status; /* Allocate a state struct */ file_info = kmalloc(sizeof(*file_info), GFP_KERNEL); if (file_info == NULL) return -ENOMEM; spin_lock_irqsave(&events->lock, status); file_info->next_entry = events->next_entry; file_info->events = events; spin_unlock_irqrestore(&events->lock, status); file->private_data = file_info; return 0; } static int rate_control_pid_events_release(struct inode *inode, struct file *file) { struct rc_pid_events_file_info *file_info = file->private_data; kfree(file_info); return 0; } static unsigned int rate_control_pid_events_poll(struct file *file, poll_table *wait) { struct rc_pid_events_file_info *file_info = file->private_data; poll_wait(file, &file_info->events->waitqueue, wait); return POLLIN | POLLRDNORM; } #define RC_PID_PRINT_BUF_SIZE 64 static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, size_t length, loff_t *offset) { struct rc_pid_events_file_info *file_info = file->private_data; struct rc_pid_event_buffer *events = file_info->events; struct rc_pid_event *ev; char pb[RC_PID_PRINT_BUF_SIZE]; int ret; int p; unsigned long status; /* Check if there is something to read. */ if (events->next_entry == file_info->next_entry) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; /* Wait */ ret = wait_event_interruptible(events->waitqueue, events->next_entry != file_info->next_entry); if (ret) return ret; } /* Write out one event per call. I don't care whether it's a little * inefficient, this is debugging code anyway. */ spin_lock_irqsave(&events->lock, status); /* Get an event */ ev = &(events->ring[file_info->next_entry]); file_info->next_entry = (file_info->next_entry + 1) % RC_PID_EVENT_RING_SIZE; /* Print information about the event. Note that userspace needs to * provide large enough buffers. */ length = length < RC_PID_PRINT_BUF_SIZE ? length : RC_PID_PRINT_BUF_SIZE; p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); switch (ev->type) { case RC_PID_EVENT_TYPE_TX_STATUS: p += snprintf(pb + p, length - p, "tx_status %u %u", !(ev->data.flags & IEEE80211_TX_STAT_ACK), ev->data.tx_status.status.rates[0].idx); break; case RC_PID_EVENT_TYPE_RATE_CHANGE: p += snprintf(pb + p, length - p, "rate_change %d %d", ev->data.index, ev->data.rate); break; case RC_PID_EVENT_TYPE_TX_RATE: p += snprintf(pb + p, length - p, "tx_rate %d %d", ev->data.index, ev->data.rate); break; case RC_PID_EVENT_TYPE_PF_SAMPLE: p += snprintf(pb + p, length - p, "pf_sample %d %d %d %d", ev->data.pf_sample, ev->data.prop_err, ev->data.int_err, ev->data.der_err); break; } p += snprintf(pb + p, length - p, "\n"); spin_unlock_irqrestore(&events->lock, status); if (copy_to_user(buf, pb, p)) return -EFAULT; return p; } #undef RC_PID_PRINT_BUF_SIZE static const struct file_operations rc_pid_fop_events = { .owner = THIS_MODULE, .read = rate_control_pid_events_read, .poll = rate_control_pid_events_poll, .open = rate_control_pid_events_open, .release = rate_control_pid_events_release, .llseek = noop_llseek, }; void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) { struct rc_pid_sta_info *spinfo = priv_sta; spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO, dir, spinfo, &rc_pid_fop_events); } void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta) { struct rc_pid_sta_info *spinfo = priv_sta; debugfs_remove(spinfo->events_entry); } compat-drivers-2012-09-18/net/mac80211/wpa.h0000644000175000017500000000213512026211315017243 0ustar mcgrofmcgrof/* * Copyright 2002-2004, Instant802 Networks, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef WPA_H #define WPA_H #include #include #include "ieee80211_i.h" ieee80211_tx_result ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx); ieee80211_rx_result ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx); ieee80211_tx_result ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx); ieee80211_rx_result ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx); ieee80211_tx_result ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx); ieee80211_rx_result ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx); ieee80211_tx_result ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx); ieee80211_rx_result ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx); ieee80211_tx_result ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx); #endif /* WPA_H */ compat-drivers-2012-09-18/net/mac80211/mesh_plink.c0000644000175000017500000006311212026211315020602 0ustar mcgrofmcgrof/* * Copyright (c) 2008, 2009 open80211s Ltd. * Author: Luis Carlos Cobo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "ieee80211_i.h" #include "rate.h" #include "mesh.h" #define PLINK_GET_LLID(p) (p + 2) #define PLINK_GET_PLID(p) (p + 4) #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ jiffies + HZ * t / 1000)) #define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries) #define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout) #define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout) #define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) /* We only need a valid sta if user configured a minimum rssi_threshold. */ #define rssi_threshold_check(sta, sdata) \ (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ (sta && (s8) -ewma_read(&sta->avg_signal) > \ sdata->u.mesh.mshcfg.rssi_threshold)) enum plink_event { PLINK_UNDEFINED, OPN_ACPT, OPN_RJCT, OPN_IGNR, CNF_ACPT, CNF_RJCT, CNF_IGNR, CLS_ACPT, CLS_IGNR }; static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason); static inline u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) { atomic_inc(&sdata->u.mesh.mshstats.estab_plinks); return mesh_accept_plinks_update(sdata); } static inline u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) { atomic_dec(&sdata->u.mesh.mshstats.estab_plinks); return mesh_accept_plinks_update(sdata); } /** * mesh_plink_fsm_restart - restart a mesh peer link finite state machine * * @sta: mesh peer link to restart * * Locking: this function must be called holding sta->lock */ static inline void mesh_plink_fsm_restart(struct sta_info *sta) { sta->plink_state = NL80211_PLINK_LISTEN; sta->llid = sta->plid = sta->reason = 0; sta->plink_retries = 0; } /* * Allocate mesh sta entry and insert into station table */ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) { struct sta_info *sta; if (sdata->local->num_sta >= MESH_MAX_PLINKS) return NULL; sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); if (!sta) return NULL; sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); set_sta_flag(sta, WLAN_STA_WME); return sta; } /** * mesh_set_ht_prot_mode - set correct HT protection mode * * Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT * mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT * mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is * selected if any non-HT peers are present in our MBSS. 20MHz-protection mode * is selected if all peers in our 20/40MHz MBSS support HT and atleast one * HT20 peer is present. Otherwise no-protection mode is selected. */ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; u32 changed = 0; u16 ht_opmode; bool non_ht_sta = false, ht20_sta = false; if (sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) return 0; rcu_read_lock(); list_for_each_entry_rcu(sta, &local->sta_list, list) { if (sdata != sta->sdata || sta->plink_state != NL80211_PLINK_ESTAB) continue; switch (sta->ch_type) { case NL80211_CHAN_NO_HT: mpl_dbg(sdata, "mesh_plink %pM: nonHT sta (%pM) is present\n", sdata->vif.addr, sta->sta.addr); non_ht_sta = true; goto out; case NL80211_CHAN_HT20: mpl_dbg(sdata, "mesh_plink %pM: HT20 sta (%pM) is present\n", sdata->vif.addr, sta->sta.addr); ht20_sta = true; default: break; } } out: rcu_read_unlock(); if (non_ht_sta) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; else if (ht20_sta && sdata->vif.bss_conf.channel_type > NL80211_CHAN_HT20) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; else ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { sdata->vif.bss_conf.ht_operation_mode = ht_opmode; sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; changed = BSS_CHANGED_HT; mpl_dbg(sdata, "mesh_plink %pM: protection mode changed to %d\n", sdata->vif.addr, ht_opmode); } return changed; } /** * __mesh_plink_deactivate - deactivate mesh peer link * * @sta: mesh peer link to deactivate * * All mesh paths with this peer as next hop will be flushed * Returns beacon changed flag if the beacon content changed. * * Locking: the caller must hold sta->lock */ static u32 __mesh_plink_deactivate(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; u32 changed = 0; if (sta->plink_state == NL80211_PLINK_ESTAB) changed = mesh_plink_dec_estab_count(sdata); sta->plink_state = NL80211_PLINK_BLOCKED; mesh_path_flush_by_nexthop(sta); return changed; } /** * mesh_plink_deactivate - deactivate mesh peer link * * @sta: mesh peer link to deactivate * * All mesh paths with this peer as next hop will be flushed */ void mesh_plink_deactivate(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; u32 changed; spin_lock_bh(&sta->lock); changed = __mesh_plink_deactivate(sta); sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, sta->llid, sta->plid, sta->reason); spin_unlock_bh(&sta->lock); ieee80211_bss_info_change_notify(sdata, changed); } static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_tx_info *info; struct ieee80211_mgmt *mgmt; bool include_plid = false; u16 peering_proto = 0; u8 *pos, ie_len = 4; int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + sizeof(mgmt->u.action.u.self_prot); int err = -ENOMEM; skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + /* capability info */ 2 + /* AID */ 2 + 8 + /* supported rates */ 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sdata->u.mesh.mesh_id_len + 2 + sizeof(struct ieee80211_meshconf_ie) + 2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_operation) + 2 + 8 + /* peering IE */ sdata->u.mesh.ie_len); if (!skb) return -1; info = IEEE80211_SKB_CB(skb); skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; mgmt->u.action.u.self_prot.action_code = action; if (action != WLAN_SP_MESH_PEERING_CLOSE) { /* capability info */ pos = skb_put(skb, 2); memset(pos, 0, 2); if (action == WLAN_SP_MESH_PEERING_CONFIRM) { /* AID */ pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } if (ieee80211_add_srates_ie(sdata, skb, true, local->oper_channel->band) || ieee80211_add_ext_srates_ie(sdata, skb, true, local->oper_channel->band) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata)) goto free; } else { /* WLAN_SP_MESH_PEERING_CLOSE */ info->flags |= IEEE80211_TX_CTL_NO_ACK; if (mesh_add_meshid_ie(skb, sdata)) goto free; } /* Add Mesh Peering Management element */ switch (action) { case WLAN_SP_MESH_PEERING_OPEN: break; case WLAN_SP_MESH_PEERING_CONFIRM: ie_len += 2; include_plid = true; break; case WLAN_SP_MESH_PEERING_CLOSE: if (plid) { ie_len += 2; include_plid = true; } ie_len += 2; /* reason code */ break; default: err = -EINVAL; goto free; } if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) goto free; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PEER_MGMT; *pos++ = ie_len; memcpy(pos, &peering_proto, 2); pos += 2; memcpy(pos, &llid, 2); pos += 2; if (include_plid) { memcpy(pos, &plid, 2); pos += 2; } if (action == WLAN_SP_MESH_PEERING_CLOSE) { memcpy(pos, &reason, 2); pos += 2; } if (action != WLAN_SP_MESH_PEERING_CLOSE) { if (mesh_add_ht_cap_ie(skb, sdata) || mesh_add_ht_oper_ie(skb, sdata)) goto free; } if (mesh_add_vendor_ies(skb, sdata)) goto free; ieee80211_tx_skb(sdata, skb); return 0; free: kfree_skb(skb); return err; } /** * mesh_peer_init - initialize new mesh peer and return resulting sta_info * * @sdata: local meshif * @addr: peer's address * @elems: IEs from beacon or mesh peering frame * * call under RCU */ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, u8 *addr, struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; enum ieee80211_band band = local->oper_channel->band; struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0; struct sta_info *sta; bool insert = false; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); sta = sta_info_get(sdata, addr); if (!sta) { /* Userspace handles peer allocation when security is enabled */ if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { cfg80211_notify_new_peer_candidate(sdata->dev, addr, elems->ie_start, elems->total_len, GFP_ATOMIC); return NULL; } sta = mesh_plink_alloc(sdata, addr); if (!sta) return NULL; insert = true; } spin_lock_bh(&sta->lock); sta->last_rx = jiffies; if (sta->plink_state == NL80211_PLINK_ESTAB) { spin_unlock_bh(&sta->lock); return sta; } sta->sta.supp_rates[band] = rates; if (elems->ht_cap_elem && sdata->vif.bss_conf.channel_type != NL80211_CHAN_NO_HT) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, &sta->sta.ht_cap); else memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); if (elems->ht_operation) { if (!(elems->ht_operation->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; sta->ch_type = ieee80211_ht_oper_to_channel_type(elems->ht_operation); } rate_control_rate_init(sta); spin_unlock_bh(&sta->lock); if (insert && sta_info_insert(sta)) return NULL; return sta; } void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, u8 *hw_addr, struct ieee802_11_elems *elems) { struct sta_info *sta; rcu_read_lock(); sta = mesh_peer_init(sdata, hw_addr, elems); if (!sta) goto out; if (mesh_peer_accepts_plinks(elems) && sta->plink_state == NL80211_PLINK_LISTEN && sdata->u.mesh.accepting_plinks && sdata->u.mesh.mshcfg.auto_open_plinks && rssi_threshold_check(sta, sdata)) mesh_plink_open(sta); out: rcu_read_unlock(); } static void mesh_plink_timer(unsigned long data) { struct sta_info *sta; __le16 llid, plid, reason; struct ieee80211_sub_if_data *sdata; /* * This STA is valid because sta_info_destroy() will * del_timer_sync() this timer after having made sure * it cannot be readded (by deleting the plink.) */ sta = (struct sta_info *) data; if (sta->sdata->local->quiescing) { sta->plink_timer_was_running = true; return; } spin_lock_bh(&sta->lock); if (sta->ignore_plink_timer) { sta->ignore_plink_timer = false; spin_unlock_bh(&sta->lock); return; } mpl_dbg(sta->sdata, "Mesh plink timer for %pM fired on state %d\n", sta->sta.addr, sta->plink_state); reason = 0; llid = sta->llid; plid = sta->plid; sdata = sta->sdata; switch (sta->plink_state) { case NL80211_PLINK_OPN_RCVD: case NL80211_PLINK_OPN_SNT: /* retry timer */ if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { u32 rand; mpl_dbg(sta->sdata, "Mesh plink for %pM (retry, timeout): %d %d\n", sta->sta.addr, sta->plink_retries, sta->plink_timeout); get_random_bytes(&rand, sizeof(u32)); sta->plink_timeout = sta->plink_timeout + rand % sta->plink_timeout; ++sta->plink_retries; mod_plink_timer(sta, sta->plink_timeout); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, sta->sta.addr, llid, 0, 0); break; } reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES); /* fall through on else */ case NL80211_PLINK_CNF_RCVD: /* confirm timer */ if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); sta->plink_state = NL80211_PLINK_HOLDING; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case NL80211_PLINK_HOLDING: /* holding timer */ del_timer(&sta->plink_timer); mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; default: spin_unlock_bh(&sta->lock); break; } } #ifdef CONFIG_PM void mesh_plink_quiesce(struct sta_info *sta) { if (del_timer_sync(&sta->plink_timer)) sta->plink_timer_was_running = true; } void mesh_plink_restart(struct sta_info *sta) { if (sta->plink_timer_was_running) { add_timer(&sta->plink_timer); sta->plink_timer_was_running = false; } } #endif static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) { sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); sta->plink_timer.data = (unsigned long) sta; sta->plink_timer.function = mesh_plink_timer; sta->plink_timeout = timeout; add_timer(&sta->plink_timer); } int mesh_plink_open(struct sta_info *sta) { __le16 llid; struct ieee80211_sub_if_data *sdata = sta->sdata; if (!test_sta_flag(sta, WLAN_STA_AUTH)) return -EPERM; spin_lock_bh(&sta->lock); get_random_bytes(&llid, 2); sta->llid = llid; if (sta->plink_state != NL80211_PLINK_LISTEN) { spin_unlock_bh(&sta->lock); return -EBUSY; } sta->plink_state = NL80211_PLINK_OPN_SNT; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); mpl_dbg(sdata, "Mesh plink: starting establishment with %pM\n", sta->sta.addr); return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, sta->sta.addr, llid, 0, 0); } void mesh_plink_block(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; u32 changed; spin_lock_bh(&sta->lock); changed = __mesh_plink_deactivate(sta); sta->plink_state = NL80211_PLINK_BLOCKED; spin_unlock_bh(&sta->lock); ieee80211_bss_info_change_notify(sdata, changed); } void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { struct ieee802_11_elems elems; struct sta_info *sta; enum plink_event event; enum ieee80211_self_protected_actioncode ftype; size_t baselen; bool matches_local = true; u8 ie_len; u8 *baseaddr; u32 changed = 0; __le16 plid, llid, reason; static const char *mplstates[] = { [NL80211_PLINK_LISTEN] = "LISTEN", [NL80211_PLINK_OPN_SNT] = "OPN-SNT", [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", [NL80211_PLINK_ESTAB] = "ESTAB", [NL80211_PLINK_HOLDING] = "HOLDING", [NL80211_PLINK_BLOCKED] = "BLOCKED" }; /* need action_code, aux */ if (len < IEEE80211_MIN_ACTION_SIZE + 3) return; if (is_multicast_ether_addr(mgmt->da)) { mpl_dbg(sdata, "Mesh plink: ignore frame from multicast address\n"); return; } baseaddr = mgmt->u.action.u.self_prot.variable; baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; if (mgmt->u.action.u.self_prot.action_code == WLAN_SP_MESH_PEERING_CONFIRM) { baseaddr += 4; baselen += 4; } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); if (!elems.peering) { mpl_dbg(sdata, "Mesh plink: missing necessary peer link ie\n"); return; } if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { mpl_dbg(sdata, "Mesh plink: can't establish link with secure peer\n"); return; } ftype = mgmt->u.action.u.self_prot.action_code; ie_len = elems.peering_len; if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 && ie_len != 8)) { mpl_dbg(sdata, "Mesh plink: incorrect plink ie length %d %d\n", ftype, ie_len); return; } if (ftype != WLAN_SP_MESH_PEERING_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); return; } /* Note the lines below are correct, the llid in the frame is the plid * from the point of view of this host. */ memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); rcu_read_unlock(); return; } if (ftype == WLAN_SP_MESH_PEERING_OPEN && !rssi_threshold_check(sta, sdata)) { mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", mgmt->sa); rcu_read_unlock(); return; } if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); rcu_read_unlock(); return; } if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) { rcu_read_unlock(); return; } /* Now we will figure out the appropriate event... */ event = PLINK_UNDEFINED; if (ftype != WLAN_SP_MESH_PEERING_CLOSE && !mesh_matches_local(sdata, &elems)) { matches_local = false; switch (ftype) { case WLAN_SP_MESH_PEERING_OPEN: event = OPN_RJCT; break; case WLAN_SP_MESH_PEERING_CONFIRM: event = CNF_RJCT; break; default: break; } } if (!sta && !matches_local) { rcu_read_unlock(); reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); llid = 0; mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, mgmt->sa, llid, plid, reason); return; } else if (!sta) { /* ftype == WLAN_SP_MESH_PEERING_OPEN */ if (!mesh_plink_free_count(sdata)) { mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); rcu_read_unlock(); return; } event = OPN_ACPT; } else if (matches_local) { switch (ftype) { case WLAN_SP_MESH_PEERING_OPEN: if (!mesh_plink_free_count(sdata) || (sta->plid && sta->plid != plid)) event = OPN_IGNR; else event = OPN_ACPT; break; case WLAN_SP_MESH_PEERING_CONFIRM: if (!mesh_plink_free_count(sdata) || (sta->llid != llid || sta->plid != plid)) event = CNF_IGNR; else event = CNF_ACPT; break; case WLAN_SP_MESH_PEERING_CLOSE: if (sta->plink_state == NL80211_PLINK_ESTAB) /* Do not check for llid or plid. This does not * follow the standard but since multiple plinks * per sta are not supported, it is necessary in * order to avoid a livelock when MP A sees an * establish peer link to MP B but MP B does not * see it. This can be caused by a timeout in * B's peer link establishment or B beign * restarted. */ event = CLS_ACPT; else if (sta->plid != plid) event = CLS_IGNR; else if (ie_len == 7 && sta->llid != llid) event = CLS_IGNR; else event = CLS_ACPT; break; default: mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); rcu_read_unlock(); return; } } if (event == OPN_ACPT) { /* allocate sta entry if necessary and update info */ sta = mesh_peer_init(sdata, mgmt->sa, &elems); if (!sta) { mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); rcu_read_unlock(); return; } } mpl_dbg(sdata, "Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", mgmt->sa, mplstates[sta->plink_state], le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), event); reason = 0; spin_lock_bh(&sta->lock); switch (sta->plink_state) { /* spin_unlock as soon as state is updated at each case */ case NL80211_PLINK_LISTEN: switch (event) { case CLS_ACPT: mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; get_random_bytes(&llid, 2); sta->llid = llid; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, sta->sta.addr, llid, 0, 0); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: /* retry timer is left untouched */ sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: sta->plink_state = NL80211_PLINK_CNF_RCVD; if (!mod_plink_timer(sta, dot11MeshConfirmTimeout(sdata))) sta->ignore_plink_timer = true; spin_unlock_bh(&sta->lock); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: del_timer(&sta->plink_timer); sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); changed |= mesh_plink_inc_estab_count(sdata); changed |= mesh_set_ht_prot_mode(sdata); mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: del_timer(&sta->plink_timer); sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); changed |= mesh_plink_inc_estab_count(sdata); changed |= mesh_set_ht_prot_mode(sdata); mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_ESTAB: switch (event) { case CLS_ACPT: reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; changed |= __mesh_plink_deactivate(sta); sta->plink_state = NL80211_PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); changed |= mesh_set_ht_prot_mode(sdata); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_HOLDING: switch (event) { case CLS_ACPT: if (del_timer(&sta->plink_timer)) sta->ignore_plink_timer = 1; mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: case CNF_ACPT: case OPN_RJCT: case CNF_RJCT: llid = sta->llid; reason = sta->reason; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; default: spin_unlock_bh(&sta->lock); } break; default: /* should not get here, PLINK_BLOCKED is dealt with at the * beginning of the function */ spin_unlock_bh(&sta->lock); break; } rcu_read_unlock(); if (changed) ieee80211_bss_info_change_notify(sdata, changed); } compat-drivers-2012-09-18/net/mac80211/ht.c0000644000175000017500000002770712026211315017076 0ustar mcgrofmcgrof/* * HT handling * * Copyright 2003, Jouni Malinen * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "ieee80211_i.h" #include "rate.h" static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_ht_cap *ht_cap, u16 flag) { __le16 le_flag = cpu_to_le16(flag); if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) { if (!(sdata->u.mgd.ht_capa.cap_info & le_flag)) ht_cap->cap &= ~flag; } } void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_ht_cap *ht_cap) { u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask); u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); int i; if (sdata->vif.type != NL80211_IFTYPE_STATION) { /* AP interfaces call this code when adding new stations, * so just silently ignore non station interfaces. */ return; } /* NOTE: If you add more over-rides here, update register_hw * ht_capa_mod_msk logic in main.c as well. * And, if this method can ever change ht_cap.ht_supported, fix * the check in ieee80211_add_ht_ie. */ /* check for HT over-rides, MCS rates first. */ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { u8 m = smask[i]; ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */ /* Add back rates that are supported */ ht_cap->mcs.rx_mask[i] |= (m & scaps[i]); } /* Force removal of HT-40 capabilities? */ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40); __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40); /* Allow user to disable the max-AMSDU bit. */ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU); /* Allow user to decrease AMPDU factor */ if (sdata->u.mgd.ht_capa_mask.ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR) { u8 n = sdata->u.mgd.ht_capa.ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR; if (n < ht_cap->ampdu_factor) ht_cap->ampdu_factor = n; } /* Allow the user to increase AMPDU density. */ if (sdata->u.mgd.ht_capa_mask.ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) { u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; if (n > ht_cap->ampdu_density) ht_cap->ampdu_density = n; } } void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_ht_cap *ht_cap_ie, struct ieee80211_sta_ht_cap *ht_cap) { u8 ampdu_info, tx_mcs_set_cap; int i, max_tx_streams; BUG_ON(!ht_cap); memset(ht_cap, 0, sizeof(*ht_cap)); if (!ht_cap_ie || !sband->ht_cap.ht_supported) return; ht_cap->ht_supported = true; /* * The bits listed in this expression should be * the same for the peer and us, if the station * advertises more then we can't use those thus * we mask them out. */ ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & (sband->ht_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40)); /* * The STBC bits are asymmetric -- if we don't have * TX then mask out the peer's RX and vice versa. */ if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC; if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC; ampdu_info = ht_cap_ie->ampdu_params_info; ht_cap->ampdu_factor = ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; ht_cap->ampdu_density = (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; /* own MCS TX capabilities */ tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; /* Copy peer MCS TX capabilities, the driver might need them. */ ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params; /* can we TX with MCS rates? */ if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) return; /* Counting from 0, therefore +1 */ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) max_tx_streams = ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; else max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS; /* * 802.11n-2009 20.3.5 / 20.6 says: * - indices 0 to 7 and 32 are single spatial stream * - 8 to 31 are multiple spatial streams using equal modulation * [8..15 for two streams, 16..23 for three and 24..31 for four] * - remainder are multiple spatial streams using unequal modulation */ for (i = 0; i < max_tx_streams; i++) ht_cap->mcs.rx_mask[i] = sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; i < IEEE80211_HT_MCS_MASK_LEN; i++) ht_cap->mcs.rx_mask[i] = sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; /* handle MCS rate 32 too */ if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) ht_cap->mcs.rx_mask[32/8] |= 1; /* * If user has specified capability over-rides, take care * of that here. */ ieee80211_apply_htcap_overrides(sdata, ht_cap); } void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx) { int i; cancel_work_sync(&sta->ampdu_mlme.work); for (i = 0; i < STA_TID_NUM; i++) { __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx); __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_LEAVE_QBSS, tx); } } void ieee80211_ba_session_work(struct work_struct *work) { struct sta_info *sta = container_of(work, struct sta_info, ampdu_mlme.work); struct tid_ampdu_tx *tid_tx; int tid; /* * When this flag is set, new sessions should be * blocked, and existing sessions will be torn * down by the code that set the flag, so this * need not run. */ if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) return; mutex_lock(&sta->ampdu_mlme.mtx); for (tid = 0; tid < STA_TID_NUM; tid++) { if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired)) ___ieee80211_stop_rx_ba_session( sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_TIMEOUT, true); if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_stop_requested)) ___ieee80211_stop_rx_ba_session( sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_UNSPECIFIED, true); tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; if (tid_tx) { /* * Assign it over to the normal tid_tx array * where it "goes live". */ spin_lock_bh(&sta->lock); sta->ampdu_mlme.tid_start_tx[tid] = NULL; /* could there be a race? */ if (sta->ampdu_mlme.tid_tx[tid]) kfree(tid_tx); else ieee80211_assign_tid_tx(sta, tid, tid_tx); spin_unlock_bh(&sta->lock); ieee80211_tx_ba_session_handle_start(sta, tid); continue; } tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, true); } mutex_unlock(&sta->ampdu_mlme.mtx); } void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, u16 initiator, u16 reason_code) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u16 params; skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); mgmt->u.action.category = WLAN_CATEGORY_BACK; mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; params = (u16)(initiator << 11); /* bit 11 initiator */ params |= (u16)(tid << 12); /* bit 15:12 TID number */ mgmt->u.action.u.delba.params = cpu_to_le16(params); mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); ieee80211_tx_skb_tid(sdata, skb, tid); } void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len) { u16 tid, params; u16 initiator; params = le16_to_cpu(mgmt->u.action.u.delba.params); tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n", mgmt->sa, initiator ? "initiator" : "recipient", tid, le16_to_cpu(mgmt->u.action.u.delba.reason_code)); if (initiator == WLAN_BACK_INITIATOR) __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0, true); else __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, true); } int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps, const u8 *da, const u8 *bssid) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *action_frame; /* 27 = header + category + action + smps mode */ skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom); if (!skb) return -ENOMEM; skb_reserve(skb, local->hw.extra_tx_headroom); action_frame = (void *)skb_put(skb, 27); memcpy(action_frame->da, da, ETH_ALEN); memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN); memcpy(action_frame->bssid, bssid, ETH_ALEN); action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); action_frame->u.action.category = WLAN_CATEGORY_HT; action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS; switch (smps) { case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_NUM_MODES: WARN_ON(1); case IEEE80211_SMPS_OFF: action_frame->u.action.u.ht_smps.smps_control = WLAN_HT_SMPS_CONTROL_DISABLED; break; case IEEE80211_SMPS_STATIC: action_frame->u.action.u.ht_smps.smps_control = WLAN_HT_SMPS_CONTROL_STATIC; break; case IEEE80211_SMPS_DYNAMIC: action_frame->u.action.u.ht_smps.smps_control = WLAN_HT_SMPS_CONTROL_DYNAMIC; break; } /* we'll do more on status of this frame */ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; ieee80211_tx_skb(sdata, skb); return 0; } void ieee80211_request_smps_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.request_smps_work); mutex_lock(&sdata->u.mgd.mtx); __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode); mutex_unlock(&sdata->u.mgd.mtx); } void ieee80211_request_smps(struct ieee80211_vif *vif, enum ieee80211_smps_mode smps_mode) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) return; if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) smps_mode = IEEE80211_SMPS_AUTOMATIC; sdata->u.mgd.driver_smps_mode = smps_mode; ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.request_smps_work); } /* this might change ... don't want non-open drivers using it */ EXPORT_SYMBOL_GPL(ieee80211_request_smps); compat-drivers-2012-09-18/net/mac80211/aes_cmac.c0000644000175000017500000000513312026211315020203 0ustar mcgrofmcgrof/* * AES-128-CMAC with TLen 16 for IEEE 802.11w BIP * Copyright 2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "key.h" #include "aes_cmac.h" #define AES_CMAC_KEY_LEN 16 #define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */ #define AAD_LEN 20 static void gf_mulx(u8 *pad) { int i, carry; carry = pad[0] & 0x80; for (i = 0; i < AES_BLOCK_SIZE - 1; i++) pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); pad[AES_BLOCK_SIZE - 1] <<= 1; if (carry) pad[AES_BLOCK_SIZE - 1] ^= 0x87; } static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; const u8 *pos, *end; size_t i, e, left, total_len; memset(cbc, 0, AES_BLOCK_SIZE); total_len = 0; for (e = 0; e < num_elem; e++) total_len += len[e]; left = total_len; e = 0; pos = addr[0]; end = pos + len[0]; while (left >= AES_BLOCK_SIZE) { for (i = 0; i < AES_BLOCK_SIZE; i++) { cbc[i] ^= *pos++; if (pos >= end) { e++; pos = addr[e]; end = pos + len[e]; } } if (left > AES_BLOCK_SIZE) crypto_cipher_encrypt_one(tfm, cbc, cbc); left -= AES_BLOCK_SIZE; } memset(pad, 0, AES_BLOCK_SIZE); crypto_cipher_encrypt_one(tfm, pad, pad); gf_mulx(pad); if (left || total_len == 0) { for (i = 0; i < left; i++) { cbc[i] ^= *pos++; if (pos >= end) { e++; pos = addr[e]; end = pos + len[e]; } } cbc[left] ^= 0x80; gf_mulx(pad); } for (i = 0; i < AES_BLOCK_SIZE; i++) pad[i] ^= cbc[i]; crypto_cipher_encrypt_one(tfm, pad, pad); memcpy(mac, pad, CMAC_TLEN); } void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, const u8 *data, size_t data_len, u8 *mic) { const u8 *addr[3]; size_t len[3]; u8 zero[CMAC_TLEN]; memset(zero, 0, CMAC_TLEN); addr[0] = aad; len[0] = AAD_LEN; addr[1] = data; len[1] = data_len - CMAC_TLEN; addr[2] = zero; len[2] = CMAC_TLEN; aes_128_cmac_vector(tfm, 3, addr, len, mic); } struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]) { struct crypto_cipher *tfm; tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); if (!IS_ERR(tfm)) crypto_cipher_setkey(tfm, key, AES_CMAC_KEY_LEN); return tfm; } void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm) { crypto_free_cipher(tfm); } compat-drivers-2012-09-18/net/mac80211/cfg.h0000644000175000017500000000023312026211315017210 0ustar mcgrofmcgrof/* * mac80211 configuration hooks for cfg80211 */ #ifndef __CFG_H #define __CFG_H extern struct cfg80211_ops mac80211_config_ops; #endif /* __CFG_H */ compat-drivers-2012-09-18/net/mac80211/rate.h0000644000175000017500000001023212026211315017404 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef IEEE80211_RATE_H #define IEEE80211_RATE_H #include #include #include #include #include "ieee80211_i.h" #include "sta_info.h" #include "driver-ops.h" struct rate_control_ref { struct ieee80211_local *local; struct rate_control_ops *ops; void *priv; }; void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_tx_rate_control *txrc); static inline void rate_control_tx_status(struct ieee80211_local *local, struct ieee80211_supported_band *sband, struct sta_info *sta, struct sk_buff *skb) { struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) return; ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); } static inline void rate_control_rate_init(struct sta_info *sta) { struct ieee80211_local *local = sta->sdata->local; struct rate_control_ref *ref = sta->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; struct ieee80211_supported_band *sband; if (!ref) return; sband = local->hw.wiphy->bands[local->oper_channel->band]; ref->ops->rate_init(ref->priv, sband, ista, priv_sta); set_sta_flag(sta, WLAN_STA_RATE_CONTROL); } static inline void rate_control_rate_update(struct ieee80211_local *local, struct ieee80211_supported_band *sband, struct sta_info *sta, u32 changed) { struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; if (ref && ref->ops->rate_update) ref->ops->rate_update(ref->priv, sband, ista, priv_sta, changed); drv_sta_rc_update(local, sta->sdata, &sta->sta, changed); } static inline void *rate_control_alloc_sta(struct rate_control_ref *ref, struct ieee80211_sta *sta, gfp_t gfp) { return ref->ops->alloc_sta(ref->priv, sta, gfp); } static inline void rate_control_free_sta(struct sta_info *sta) { struct rate_control_ref *ref = sta->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; ref->ops->free_sta(ref->priv, ista, priv_sta); } static inline void rate_control_add_sta_debugfs(struct sta_info *sta) { #ifdef CONFIG_MAC80211_DEBUGFS struct rate_control_ref *ref = sta->rate_ctrl; if (ref && sta->debugfs.dir && ref->ops->add_sta_debugfs) ref->ops->add_sta_debugfs(ref->priv, sta->rate_ctrl_priv, sta->debugfs.dir); #endif } static inline void rate_control_remove_sta_debugfs(struct sta_info *sta) { #ifdef CONFIG_MAC80211_DEBUGFS struct rate_control_ref *ref = sta->rate_ctrl; if (ref && ref->ops->remove_sta_debugfs) ref->ops->remove_sta_debugfs(ref->priv, sta->rate_ctrl_priv); #endif } /* Get a reference to the rate control algorithm. If `name' is NULL, get the * first available algorithm. */ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, const char *name); void rate_control_deinitialize(struct ieee80211_local *local); /* Rate control algorithms */ #ifdef CONFIG_MAC80211_RC_PID extern int rc80211_pid_init(void); extern void rc80211_pid_exit(void); #else static inline int rc80211_pid_init(void) { return 0; } static inline void rc80211_pid_exit(void) { } #endif #ifdef CONFIG_MAC80211_RC_MINSTREL extern int rc80211_minstrel_init(void); extern void rc80211_minstrel_exit(void); #else static inline int rc80211_minstrel_init(void) { return 0; } static inline void rc80211_minstrel_exit(void) { } #endif #ifdef CONFIG_MAC80211_RC_MINSTREL_HT extern int rc80211_minstrel_ht_init(void); extern void rc80211_minstrel_ht_exit(void); #else static inline int rc80211_minstrel_ht_init(void) { return 0; } static inline void rc80211_minstrel_ht_exit(void) { } #endif #endif /* IEEE80211_RATE_H */ compat-drivers-2012-09-18/net/mac80211/mesh_sync.c0000644000175000017500000002210412026211315020435 0ustar mcgrofmcgrof/* * Copyright 2011-2012, Pavel Zubarev * Copyright 2011-2012, Marco Porsch * Copyright 2011-2012, cozybit Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include "ieee80211_i.h" #include "mesh.h" #include "driver-ops.h" /* This is not in the standard. It represents a tolerable tbtt drift below * which we do no TSF adjustment. */ #define TOFFSET_MINIMUM_ADJUSTMENT 10 /* This is not in the standard. It is a margin added to the * Toffset setpoint to mitigate TSF overcorrection * introduced by TSF adjustment latency. */ #define TOFFSET_SET_MARGIN 20 /* This is not in the standard. It represents the maximum Toffset jump above * which we'll invalidate the Toffset setpoint and choose a new setpoint. This * could be, for instance, in case a neighbor is restarted and its TSF counter * reset. */ #define TOFFSET_MAXIMUM_ADJUSTMENT 30000 /* 30 ms */ struct sync_method { u8 method; struct ieee80211_mesh_sync_ops ops; }; /** * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT * * @ie: information elements of a management frame from the mesh peer */ static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie) { return (ie->mesh_config->meshconf_cap & MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; } void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; /* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */ u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500; u64 tsf; u64 tsfdelta; spin_lock_bh(&ifmsh->sync_offset_lock); if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) { msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting\n", (long long) ifmsh->sync_offset_clockdrift_max); tsfdelta = -ifmsh->sync_offset_clockdrift_max; ifmsh->sync_offset_clockdrift_max = 0; } else { msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting by %llu\n", (long long) ifmsh->sync_offset_clockdrift_max, (unsigned long long) beacon_int_fraction); tsfdelta = -beacon_int_fraction; ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction; } tsf = drv_get_tsf(local, sdata); if (tsf != -1ULL) drv_set_tsf(local, sdata, tsf + tsfdelta); spin_unlock_bh(&ifmsh->sync_offset_lock); } static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype, struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, struct ieee80211_rx_status *rx_status) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; struct sta_info *sta; u64 t_t, t_r; WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); /* standard mentions only beacons */ if (stype != IEEE80211_STYPE_BEACON) return; /* The current tsf is a first approximation for the timestamp * for the received beacon. Further down we try to get a * better value from the rx_status->mactime field if * available. Also we have to call drv_get_tsf() before * entering the rcu-read section.*/ t_r = drv_get_tsf(local, sdata); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta) goto no_sync; /* check offset sync conditions (13.13.2.2.1) * * TODO also sync to * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors */ if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", sta->sta.addr); goto no_sync; } if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) { /* * The mactime is defined as the time the first data symbol * of the frame hits the PHY, and the timestamp of the beacon * is defined as "the time that the data symbol containing the * first bit of the timestamp is transmitted to the PHY plus * the transmitting STA's delays through its local PHY from the * MAC-PHY interface to its interface with the WM" (802.11 * 11.1.2) * * T_r, in 13.13.2.2.2, is just defined as "the frame reception * time" but we unless we interpret that time to be the same * time of the beacon timestamp, the offset calculation will be * off. Below we adjust t_r to be "the time at which the first * symbol of the timestamp element in the beacon is received". * This correction depends on the rate. * * Based on similar code in ibss.c */ int rate; if (rx_status->flag & RX_FLAG_HT) { /* TODO: * In principle there could be HT-beacons (Dual Beacon * HT Operation options), but for now ignore them and * just use the primary (i.e. non-HT) beacons for * synchronization. * */ goto no_sync; } else rate = local->hw.wiphy->bands[rx_status->band]-> bitrates[rx_status->rate_idx].bitrate; /* 24 bytes of header * 8 bits/byte * * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/ t_r = rx_status->mactime + (24 * 8 * 10 / rate); } /* Timing offset calculation (see 13.13.2.2.2) */ t_t = le64_to_cpu(mgmt->u.beacon.timestamp); sta->t_offset = t_t - t_r; if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { s64 t_clockdrift = sta->t_offset_setpoint - sta->t_offset; msync_dbg(sdata, "STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n", sta->sta.addr, (long long) sta->t_offset, (long long) sta->t_offset_setpoint, (long long) t_clockdrift); if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { msync_dbg(sdata, "STA %pM : t_clockdrift=%lld too large, setpoint reset\n", sta->sta.addr, (long long) t_clockdrift); clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); goto no_sync; } rcu_read_unlock(); spin_lock_bh(&ifmsh->sync_offset_lock); if (t_clockdrift > ifmsh->sync_offset_clockdrift_max) ifmsh->sync_offset_clockdrift_max = t_clockdrift; spin_unlock_bh(&ifmsh->sync_offset_lock); } else { sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN; set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); msync_dbg(sdata, "STA %pM : offset was invalid, sta->t_offset=%lld\n", sta->sta.addr, (long long) sta->t_offset); rcu_read_unlock(); } return; no_sync: rcu_read_unlock(); } static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); BUG_ON(!rcu_read_lock_held()); spin_lock_bh(&ifmsh->sync_offset_lock); if (ifmsh->sync_offset_clockdrift_max > TOFFSET_MINIMUM_ADJUSTMENT) { /* Since ajusting the tsf here would * require a possibly blocking call * to the driver tsf setter, we punt * the tsf adjustment to the mesh tasklet */ msync_dbg(sdata, "TBTT : kicking off TBTT adjustment with clockdrift_max=%lld\n", ifmsh->sync_offset_clockdrift_max); set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); } else { msync_dbg(sdata, "TBTT : max clockdrift=%lld; too small to adjust\n", (long long)ifmsh->sync_offset_clockdrift_max); ifmsh->sync_offset_clockdrift_max = 0; } spin_unlock_bh(&ifmsh->sync_offset_lock); } static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 offset; if (!ifmsh->ie || !ifmsh->ie_len) return NULL; offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0); if (!offset) return NULL; return ifmsh->ie + offset + 2; } static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype, struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, struct ieee80211_rx_status *rx_status) { const u8 *oui; WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); msync_dbg(sdata, "called mesh_sync_vendor_rx_bcn_presp\n"); oui = mesh_get_vendor_oui(sdata); /* here you would implement the vendor offset tracking for this oui */ } static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata) { const u8 *oui; WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); msync_dbg(sdata, "called mesh_sync_vendor_adjust_tbtt\n"); oui = mesh_get_vendor_oui(sdata); /* here you would implement the vendor tsf adjustment for this oui */ } /* global variable */ static struct sync_method sync_methods[] = { { .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .ops = { .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp, .adjust_tbtt = &mesh_sync_offset_adjust_tbtt, } }, { .method = IEEE80211_SYNC_METHOD_VENDOR, .ops = { .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp, .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt, } }, }; struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) { struct ieee80211_mesh_sync_ops *ops = NULL; u8 i; for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) { if (sync_methods[i].method == method) { ops = &sync_methods[i].ops; break; } } return ops; } compat-drivers-2012-09-18/net/mac80211/scan.c0000644000175000017500000006540312026211315017402 0ustar mcgrofmcgrof/* * Scanning implementation * * Copyright 2003, Jouni Malinen * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "mesh.h" #define IEEE80211_PROBE_DELAY (HZ / 33) #define IEEE80211_CHANNEL_TIME (HZ / 33) #define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8) static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) { struct ieee80211_bss *bss = (void *)cbss->priv; kfree(bss_mesh_id(bss)); kfree(bss_mesh_cfg(bss)); } void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss) { if (!bss) return; cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv)); } static bool is_uapsd_supported(struct ieee802_11_elems *elems) { u8 qos_info; if (elems->wmm_info && elems->wmm_info_len == 7 && elems->wmm_info[5] == 1) qos_info = elems->wmm_info[6]; else if (elems->wmm_param && elems->wmm_param_len == 24 && elems->wmm_param[5] == 1) qos_info = elems->wmm_param[6]; else /* no valid wmm information or parameter element found */ return false; return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD; } struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, struct ieee80211_channel *channel, bool beacon) { struct cfg80211_bss *cbss; struct ieee80211_bss *bss; int clen, srlen; s32 signal = 0; if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) signal = rx_status->signal * 100; else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) signal = (rx_status->signal * 100) / local->hw.max_signal; cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel, mgmt, len, signal, GFP_ATOMIC); if (!cbss) return NULL; cbss->free_priv = ieee80211_rx_bss_free; bss = (void *)cbss->priv; bss->device_ts = rx_status->device_timestamp; if (elems->parse_error) { if (beacon) bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON; else bss->corrupt_data |= IEEE80211_BSS_CORRUPT_PROBE_RESP; } else { if (beacon) bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_BEACON; else bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_PROBE_RESP; } /* save the ERP value so that it is available at association time */ if (elems->erp_info && elems->erp_info_len >= 1 && (!elems->parse_error || !(bss->valid_data & IEEE80211_BSS_VALID_ERP))) { bss->erp_value = elems->erp_info[0]; bss->has_erp_value = true; if (!elems->parse_error) bss->valid_data |= IEEE80211_BSS_VALID_ERP; } if (elems->tim && (!elems->parse_error || !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) { struct ieee80211_tim_ie *tim_ie = elems->tim; bss->dtim_period = tim_ie->dtim_period; if (!elems->parse_error) bss->valid_data |= IEEE80211_BSS_VALID_DTIM; } /* If the beacon had no TIM IE, or it was invalid, use 1 */ if (beacon && !bss->dtim_period) bss->dtim_period = 1; /* replace old supported rates if we get new values */ if (!elems->parse_error || !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) { srlen = 0; if (elems->supp_rates) { clen = IEEE80211_MAX_SUPP_RATES; if (clen > elems->supp_rates_len) clen = elems->supp_rates_len; memcpy(bss->supp_rates, elems->supp_rates, clen); srlen += clen; } if (elems->ext_supp_rates) { clen = IEEE80211_MAX_SUPP_RATES - srlen; if (clen > elems->ext_supp_rates_len) clen = elems->ext_supp_rates_len; memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen); srlen += clen; } if (srlen) { bss->supp_rates_len = srlen; if (!elems->parse_error) bss->valid_data |= IEEE80211_BSS_VALID_RATES; } } if (!elems->parse_error || !(bss->valid_data & IEEE80211_BSS_VALID_WMM)) { bss->wmm_used = elems->wmm_param || elems->wmm_info; bss->uapsd_supported = is_uapsd_supported(elems); if (!elems->parse_error) bss->valid_data |= IEEE80211_BSS_VALID_WMM; } if (!beacon) bss->last_probe_resp = jiffies; return bss; } void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_sub_if_data *sdata1, *sdata2; struct ieee80211_mgmt *mgmt = (void *)skb->data; struct ieee80211_bss *bss; u8 *elements; struct ieee80211_channel *channel; size_t baselen; int freq; bool beacon; struct ieee802_11_elems elems; if (skb->len < 24 || (!ieee80211_is_probe_resp(mgmt->frame_control) && !ieee80211_is_beacon(mgmt->frame_control))) return; sdata1 = rcu_dereference(local->scan_sdata); sdata2 = rcu_dereference(local->sched_scan_sdata); if (likely(!sdata1 && !sdata2)) return; if (ieee80211_is_probe_resp(mgmt->frame_control)) { /* ignore ProbeResp to foreign address */ if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) && (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr))) return; elements = mgmt->u.probe_resp.variable; baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); beacon = false; } else { baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable); elements = mgmt->u.beacon.variable; beacon = true; } if (baselen > skb->len) return; ieee802_11_parse_elems(elements, skb->len - baselen, &elems); if (elems.ds_params && elems.ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems.ds_params[0], rx_status->band); else freq = rx_status->freq; channel = ieee80211_get_channel(local->hw.wiphy, freq); if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; bss = ieee80211_bss_info_update(local, rx_status, mgmt, skb->len, &elems, channel, beacon); if (bss) ieee80211_rx_bss_put(local, bss); } /* return false if no more work */ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) { struct cfg80211_scan_request *req = local->scan_req; enum ieee80211_band band; int i, ielen, n_chans; do { if (local->hw_scan_band == IEEE80211_NUM_BANDS) return false; band = local->hw_scan_band; n_chans = 0; for (i = 0; i < req->n_channels; i++) { if (req->channels[i]->band == band) { local->hw_scan_req->channels[n_chans] = req->channels[i]; n_chans++; } } local->hw_scan_band++; } while (!n_chans); local->hw_scan_req->n_channels = n_chans; ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, req->ie, req->ie_len, band, req->rates[band], 0); local->hw_scan_req->ie_len = ielen; local->hw_scan_req->no_cck = req->no_cck; return true; } static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, bool was_hw_scan) { struct ieee80211_local *local = hw_to_local(hw); lockdep_assert_held(&local->mtx); /* * It's ok to abort a not-yet-running scan (that * we have one at all will be verified by checking * local->scan_req next), but not to complete it * successfully. */ if (WARN_ON(!local->scanning && !aborted)) aborted = true; if (WARN_ON(!local->scan_req)) return; if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { int rc; rc = drv_hw_scan(local, rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)), local->hw_scan_req); if (rc == 0) return; } kfree(local->hw_scan_req); local->hw_scan_req = NULL; if (local->scan_req != local->int_scan_req) cfg80211_scan_done(local->scan_req, aborted); local->scan_req = NULL; rcu_assign_pointer(local->scan_sdata, NULL); local->scanning = 0; local->scan_channel = NULL; /* Set power back to normal operating levels. */ ieee80211_hw_config(local, 0); if (!was_hw_scan) { ieee80211_configure_filter(local); drv_sw_scan_complete(local); ieee80211_offchannel_return(local, true); } ieee80211_recalc_idle(local); ieee80211_mlme_notify_scan_completed(local); ieee80211_ibss_notify_scan_completed(local); ieee80211_mesh_notify_scan_completed(local); ieee80211_start_next_roc(local); } void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); trace_api_scan_completed(local, aborted); set_bit(SCAN_COMPLETED, &local->scanning); if (aborted) set_bit(SCAN_ABORTED, &local->scanning); ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); } EXPORT_SYMBOL(ieee80211_scan_completed); static int ieee80211_start_sw_scan(struct ieee80211_local *local) { /* * Hardware/driver doesn't support hw_scan, so use software * scanning instead. First send a nullfunc frame with power save * bit on so that AP will buffer the frames for us while we are not * listening, then send probe requests to each channel and wait for * the responses. After all channels are scanned, tune back to the * original channel and send a nullfunc frame with power save bit * off to trigger the AP to send us all the buffered frames. * * Note that while local->sw_scanning is true everything else but * nullfunc frames and probe requests will be dropped in * ieee80211_tx_h_check_assoc(). */ drv_sw_scan_start(local); local->leave_oper_channel_time = jiffies; local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; ieee80211_offchannel_stop_vifs(local, true); ieee80211_configure_filter(local); /* We need to set power level at maximum rate for scanning. */ ieee80211_hw_config(local, 0); ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); return 0; } static bool ieee80211_can_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { if (!list_empty(&local->roc_list)) return false; if (sdata->vif.type == NL80211_IFTYPE_STATION && sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL)) return false; return true; } void ieee80211_run_deferred_scan(struct ieee80211_local *local) { lockdep_assert_held(&local->mtx); if (!local->scan_req || local->scanning) return; if (!ieee80211_can_scan(local, rcu_dereference_protected( local->scan_sdata, lockdep_is_held(&local->mtx)))) return; ieee80211_queue_delayed_work(&local->hw, &local->scan_work, round_jiffies_relative(0)); } static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, unsigned long *next_delay) { int i; struct ieee80211_sub_if_data *sdata; enum ieee80211_band band = local->hw.conf.channel->band; sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx));; for (i = 0; i < local->scan_req->n_ssids; i++) ieee80211_send_probe_req( sdata, NULL, local->scan_req->ssids[i].ssid, local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len, local->scan_req->rates[band], false, local->scan_req->no_cck, local->hw.conf.channel); /* * After sending probe requests, wait for probe responses * on the channel. */ *next_delay = IEEE80211_CHANNEL_TIME; local->next_scan_state = SCAN_DECISION; } static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req) { struct ieee80211_local *local = sdata->local; int rc; lockdep_assert_held(&local->mtx); if (local->scan_req) return -EBUSY; if (!ieee80211_can_scan(local, sdata)) { /* wait for the work to finish/time out */ local->scan_req = req; rcu_assign_pointer(local->scan_sdata, sdata); return 0; } if (local->ops->hw_scan) { u8 *ies; local->hw_scan_req = kmalloc( sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]) + 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + req->ie_len, GFP_KERNEL); if (!local->hw_scan_req) return -ENOMEM; local->hw_scan_req->ssids = req->ssids; local->hw_scan_req->n_ssids = req->n_ssids; ies = (u8 *)local->hw_scan_req + sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]); local->hw_scan_req->ie = ies; local->hw_scan_band = 0; /* * After allocating local->hw_scan_req, we must * go through until ieee80211_prep_hw_scan(), so * anything that might be changed here and leave * this function early must not go after this * allocation. */ } local->scan_req = req; rcu_assign_pointer(local->scan_sdata, sdata); if (local->ops->hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && (req->channels[0] == local->oper_channel)) { /* * If we are scanning only on the operating channel * then we do not need to stop normal activities */ unsigned long next_delay; __set_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); ieee80211_recalc_idle(local); /* Notify driver scan is starting, keep order of operations * same as normal software scan, in case that matters. */ drv_sw_scan_start(local); ieee80211_configure_filter(local); /* accept probe-responses */ /* We need to ensure power level is at max for scanning. */ ieee80211_hw_config(local, 0); if ((req->channels[0]->flags & IEEE80211_CHAN_PASSIVE_SCAN) || !local->scan_req->n_ssids) { next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; } else { ieee80211_scan_state_send_probe(local, &next_delay); next_delay = IEEE80211_CHANNEL_TIME; } /* Now, just wait a bit and we are all done! */ ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); return 0; } else { /* Do normal software scan */ __set_bit(SCAN_SW_SCANNING, &local->scanning); } ieee80211_recalc_idle(local); if (local->ops->hw_scan) { WARN_ON(!ieee80211_prep_hw_scan(local)); rc = drv_hw_scan(local, sdata, local->hw_scan_req); } else rc = ieee80211_start_sw_scan(local); if (rc) { kfree(local->hw_scan_req); local->hw_scan_req = NULL; local->scanning = 0; ieee80211_recalc_idle(local); local->scan_req = NULL; rcu_assign_pointer(local->scan_sdata, NULL); } return rc; } static unsigned long ieee80211_scan_get_channel_time(struct ieee80211_channel *chan) { /* * TODO: channel switching also consumes quite some time, * add that delay as well to get a better estimation */ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) return IEEE80211_PASSIVE_CHANNEL_TIME; return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME; } static void ieee80211_scan_state_decision(struct ieee80211_local *local, unsigned long *next_delay) { bool associated = false; bool tx_empty = true; bool bad_latency; bool listen_int_exceeded; unsigned long min_beacon_int = 0; struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *next_chan; /* * check if at least one STA interface is associated, * check if at least one STA interface has pending tx frames * and grab the lowest used beacon interval */ mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.associated) { associated = true; if (sdata->vif.bss_conf.beacon_int < min_beacon_int || min_beacon_int == 0) min_beacon_int = sdata->vif.bss_conf.beacon_int; if (!qdisc_all_tx_empty(sdata->dev)) { tx_empty = false; break; } } } } mutex_unlock(&local->iflist_mtx); next_chan = local->scan_req->channels[local->scan_channel_idx]; /* * we're currently scanning a different channel, let's * see if we can scan another channel without interfering * with the current traffic situation. * * Since we don't know if the AP has pending frames for us * we can only check for our tx queues and use the current * pm_qos requirements for rx. Hence, if no tx traffic occurs * at all we will scan as many channels in a row as the pm_qos * latency allows us to. Additionally we also check for the * currently negotiated listen interval to prevent losing * frames unnecessarily. * * Otherwise switch back to the operating channel. */ bad_latency = time_after(jiffies + ieee80211_scan_get_channel_time(next_chan), local->leave_oper_channel_time + usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); listen_int_exceeded = time_after(jiffies + ieee80211_scan_get_channel_time(next_chan), local->leave_oper_channel_time + usecs_to_jiffies(min_beacon_int * 1024) * local->hw.conf.listen_interval); if (associated && (!tx_empty || bad_latency || listen_int_exceeded)) local->next_scan_state = SCAN_SUSPEND; else local->next_scan_state = SCAN_SET_CHANNEL; *next_delay = 0; } static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, unsigned long *next_delay) { int skip; struct ieee80211_channel *chan; skip = 0; chan = local->scan_req->channels[local->scan_channel_idx]; local->scan_channel = chan; if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) skip = 1; /* advance state machine to next channel/band */ local->scan_channel_idx++; if (skip) { /* if we skip this channel return to the decision state */ local->next_scan_state = SCAN_DECISION; return; } /* * Probe delay is used to update the NAV, cf. 11.1.3.2.2 * (which unfortunately doesn't say _why_ step a) is done, * but it waits for the probe delay or until a frame is * received - and the received frame would update the NAV). * For now, we do not support waiting until a frame is * received. * * In any case, it is not necessary for a passive scan. */ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || !local->scan_req->n_ssids) { *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; local->next_scan_state = SCAN_DECISION; return; } /* active scan, send probes */ *next_delay = IEEE80211_PROBE_DELAY; local->next_scan_state = SCAN_SEND_PROBE; } static void ieee80211_scan_state_suspend(struct ieee80211_local *local, unsigned long *next_delay) { /* switch back to the operating channel */ local->scan_channel = NULL; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); /* * Re-enable vifs and beaconing. Leave PS * in off-channel state..will put that back * on-channel at the end of scanning. */ ieee80211_offchannel_return(local, false); *next_delay = HZ / 5; /* afterwards, resume scan & go to next channel */ local->next_scan_state = SCAN_RESUME; } static void ieee80211_scan_state_resume(struct ieee80211_local *local, unsigned long *next_delay) { /* PS already is in off-channel mode */ ieee80211_offchannel_stop_vifs(local, false); if (local->ops->flush) { drv_flush(local, false); *next_delay = 0; } else *next_delay = HZ / 10; /* remember when we left the operating channel */ local->leave_oper_channel_time = jiffies; /* advance to the next channel to be scanned */ local->next_scan_state = SCAN_SET_CHANNEL; } void ieee80211_scan_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, scan_work.work); struct ieee80211_sub_if_data *sdata; unsigned long next_delay = 0; bool aborted, hw_scan; mutex_lock(&local->mtx); sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)); /* When scanning on-channel, the first-callback means completed. */ if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); goto out_complete; } if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); goto out_complete; } if (!sdata || !local->scan_req) goto out; if (local->scan_req && !local->scanning) { struct cfg80211_scan_request *req = local->scan_req; int rc; local->scan_req = NULL; rcu_assign_pointer(local->scan_sdata, NULL); rc = __ieee80211_start_scan(sdata, req); if (rc) { /* need to complete scan in cfg80211 */ local->scan_req = req; aborted = true; goto out_complete; } else goto out; } /* * Avoid re-scheduling when the sdata is going away. */ if (!ieee80211_sdata_running(sdata)) { aborted = true; goto out_complete; } /* * as long as no delay is required advance immediately * without scheduling a new work */ do { if (!ieee80211_sdata_running(sdata)) { aborted = true; goto out_complete; } switch (local->next_scan_state) { case SCAN_DECISION: /* if no more bands/channels left, complete scan */ if (local->scan_channel_idx >= local->scan_req->n_channels) { aborted = false; goto out_complete; } ieee80211_scan_state_decision(local, &next_delay); break; case SCAN_SET_CHANNEL: ieee80211_scan_state_set_channel(local, &next_delay); break; case SCAN_SEND_PROBE: ieee80211_scan_state_send_probe(local, &next_delay); break; case SCAN_SUSPEND: ieee80211_scan_state_suspend(local, &next_delay); break; case SCAN_RESUME: ieee80211_scan_state_resume(local, &next_delay); break; } } while (next_delay == 0); ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); goto out; out_complete: hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); __ieee80211_scan_completed(&local->hw, aborted, hw_scan); out: mutex_unlock(&local->mtx); } int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req) { int res; mutex_lock(&sdata->local->mtx); res = __ieee80211_start_scan(sdata, req); mutex_unlock(&sdata->local->mtx); return res; } int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len, struct ieee80211_channel *chan) { struct ieee80211_local *local = sdata->local; int ret = -EBUSY; enum ieee80211_band band; mutex_lock(&local->mtx); /* busy scanning */ if (local->scan_req) goto unlock; /* fill internal scan request */ if (!chan) { int i, nchan = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!local->hw.wiphy->bands[band]) continue; for (i = 0; i < local->hw.wiphy->bands[band]->n_channels; i++) { local->int_scan_req->channels[nchan] = &local->hw.wiphy->bands[band]->channels[i]; nchan++; } } local->int_scan_req->n_channels = nchan; } else { local->int_scan_req->channels[0] = chan; local->int_scan_req->n_channels = 1; } local->int_scan_req->ssids = &local->scan_ssid; local->int_scan_req->n_ssids = 1; memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); local->int_scan_req->ssids[0].ssid_len = ssid_len; ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req); unlock: mutex_unlock(&local->mtx); return ret; } /* * Only call this function when a scan can't be queued -- under RTNL. */ void ieee80211_scan_cancel(struct ieee80211_local *local) { /* * We are canceling software scan, or deferred scan that was not * yet really started (see __ieee80211_start_scan ). * * Regarding hardware scan: * - we can not call __ieee80211_scan_completed() as when * SCAN_HW_SCANNING bit is set this function change * local->hw_scan_req to operate on 5G band, what race with * driver which can use local->hw_scan_req * * - we can not cancel scan_work since driver can schedule it * by ieee80211_scan_completed(..., true) to finish scan * * Hence we only call the cancel_hw_scan() callback, but the low-level * driver is still responsible for calling ieee80211_scan_completed() * after the scan was completed/aborted. */ mutex_lock(&local->mtx); if (!local->scan_req) goto out; if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { if (local->ops->cancel_hw_scan) drv_cancel_hw_scan(local, rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx))); goto out; } /* * If the work is currently running, it must be blocked on * the mutex, but we'll set scan_sdata = NULL and it'll * simply exit once it acquires the mutex. */ cancel_delayed_work(&local->scan_work); /* and clean up */ __ieee80211_scan_completed(&local->hw, true, false); out: mutex_unlock(&local->mtx); } int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req) { struct ieee80211_local *local = sdata->local; int ret, i; mutex_lock(&local->mtx); if (rcu_access_pointer(local->sched_scan_sdata)) { ret = -EBUSY; goto out; } if (!local->ops->sched_scan_start) { ret = -ENOTSUPP; goto out; } for (i = 0; i < IEEE80211_NUM_BANDS; i++) { if (!local->hw.wiphy->bands[i]) continue; local->sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + req->ie_len, GFP_KERNEL); if (!local->sched_scan_ies.ie[i]) { ret = -ENOMEM; goto out_free; } local->sched_scan_ies.len[i] = ieee80211_build_preq_ies(local, local->sched_scan_ies.ie[i], req->ie, req->ie_len, i, (u32) -1, 0); } ret = drv_sched_scan_start(local, sdata, req, &local->sched_scan_ies); if (ret == 0) { rcu_assign_pointer(local->sched_scan_sdata, sdata); goto out; } out_free: while (i > 0) kfree(local->sched_scan_ies.ie[--i]); out: mutex_unlock(&local->mtx); return ret; } int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; int ret = 0, i; mutex_lock(&local->mtx); if (!local->ops->sched_scan_stop) { ret = -ENOTSUPP; goto out; } if (rcu_access_pointer(local->sched_scan_sdata)) { for (i = 0; i < IEEE80211_NUM_BANDS; i++) kfree(local->sched_scan_ies.ie[i]); drv_sched_scan_stop(local, sdata); } out: mutex_unlock(&local->mtx); return ret; } void ieee80211_sched_scan_results(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); trace_api_sched_scan_results(local); cfg80211_sched_scan_results(hw->wiphy); } EXPORT_SYMBOL(ieee80211_sched_scan_results); void ieee80211_sched_scan_stopped_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, sched_scan_stopped_work); int i; mutex_lock(&local->mtx); if (!rcu_access_pointer(local->sched_scan_sdata)) { mutex_unlock(&local->mtx); return; } for (i = 0; i < IEEE80211_NUM_BANDS; i++) kfree(local->sched_scan_ies.ie[i]); rcu_assign_pointer(local->sched_scan_sdata, NULL); mutex_unlock(&local->mtx); cfg80211_sched_scan_stopped(local->hw.wiphy); } void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); trace_api_sched_scan_stopped(local); ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work); } EXPORT_SYMBOL(ieee80211_sched_scan_stopped); compat-drivers-2012-09-18/net/mac80211/debug.h0000644000175000017500000001044412026211315017544 0ustar mcgrofmcgrof#ifndef __MAC80211_DEBUG_H #define __MAC80211_DEBUG_H #include #ifdef CONFIG_MAC80211_IBSS_DEBUG #define MAC80211_IBSS_DEBUG 1 #else #define MAC80211_IBSS_DEBUG 0 #endif #ifdef CONFIG_MAC80211_PS_DEBUG #define MAC80211_PS_DEBUG 1 #else #define MAC80211_PS_DEBUG 0 #endif #ifdef CONFIG_MAC80211_HT_DEBUG #define MAC80211_HT_DEBUG 1 #else #define MAC80211_HT_DEBUG 0 #endif #ifdef CONFIG_MAC80211_MPL_DEBUG #define MAC80211_MPL_DEBUG 1 #else #define MAC80211_MPL_DEBUG 0 #endif #ifdef CONFIG_MAC80211_MPATH_DEBUG #define MAC80211_MPATH_DEBUG 1 #else #define MAC80211_MPATH_DEBUG 0 #endif #ifdef CONFIG_MAC80211_MHWMP_DEBUG #define MAC80211_MHWMP_DEBUG 1 #else #define MAC80211_MHWMP_DEBUG 0 #endif #ifdef CONFIG_MAC80211_MESH_SYNC_DEBUG #define MAC80211_MESH_SYNC_DEBUG 1 #else #define MAC80211_MESH_SYNC_DEBUG 0 #endif #ifdef CONFIG_MAC80211_TDLS_DEBUG #define MAC80211_TDLS_DEBUG 1 #else #define MAC80211_TDLS_DEBUG 0 #endif #ifdef CONFIG_MAC80211_STA_DEBUG #define MAC80211_STA_DEBUG 1 #else #define MAC80211_STA_DEBUG 0 #endif #ifdef CONFIG_MAC80211_MLME_DEBUG #define MAC80211_MLME_DEBUG 1 #else #define MAC80211_MLME_DEBUG 0 #endif #ifdef CONFIG_MAC80211_MESSAGE_TRACING void __sdata_info(const char *fmt, ...) __printf(1, 2); void __sdata_dbg(bool print, const char *fmt, ...) __printf(2, 3); void __sdata_err(const char *fmt, ...) __printf(1, 2); void __wiphy_dbg(struct wiphy *wiphy, bool print, const char *fmt, ...) __printf(3, 4); #define _sdata_info(sdata, fmt, ...) \ __sdata_info("%s: " fmt, (sdata)->name, ##__VA_ARGS__) #define _sdata_dbg(print, sdata, fmt, ...) \ __sdata_dbg(print, "%s: " fmt, (sdata)->name, ##__VA_ARGS__) #define _sdata_err(sdata, fmt, ...) \ __sdata_err("%s: " fmt, (sdata)->name, ##__VA_ARGS__) #define _wiphy_dbg(print, wiphy, fmt, ...) \ __wiphy_dbg(wiphy, print, fmt, ##__VA_ARGS__) #else #define _sdata_info(sdata, fmt, ...) \ do { \ pr_info("%s: " fmt, \ (sdata)->name, ##__VA_ARGS__); \ } while (0) #define _sdata_dbg(print, sdata, fmt, ...) \ do { \ if (print) \ pr_debug("%s: " fmt, \ (sdata)->name, ##__VA_ARGS__); \ } while (0) #define _sdata_err(sdata, fmt, ...) \ do { \ pr_err("%s: " fmt, \ (sdata)->name, ##__VA_ARGS__); \ } while (0) #define _wiphy_dbg(print, wiphy, fmt, ...) \ do { \ if (print) \ wiphy_dbg((wiphy), fmt, ##__VA_ARGS__); \ } while (0) #endif #define sdata_info(sdata, fmt, ...) \ _sdata_info(sdata, fmt, ##__VA_ARGS__) #define sdata_err(sdata, fmt, ...) \ _sdata_err(sdata, fmt, ##__VA_ARGS__) #define sdata_dbg(sdata, fmt, ...) \ _sdata_dbg(1, sdata, fmt, ##__VA_ARGS__) #define ht_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_HT_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define ht_dbg_ratelimited(sdata, fmt, ...) \ _sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \ sdata, fmt, ##__VA_ARGS__) #define ibss_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_IBSS_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define ps_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_PS_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define ps_dbg_hw(hw, fmt, ...) \ _wiphy_dbg(MAC80211_PS_DEBUG, \ (hw)->wiphy, fmt, ##__VA_ARGS__) #define ps_dbg_ratelimited(sdata, fmt, ...) \ _sdata_dbg(MAC80211_PS_DEBUG && net_ratelimit(), \ sdata, fmt, ##__VA_ARGS__) #define mpl_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MPL_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define mpath_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MPATH_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define mhwmp_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MHWMP_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define msync_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define tdls_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_TDLS_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define sta_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_STA_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define mlme_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MLME_DEBUG, \ sdata, fmt, ##__VA_ARGS__) #define mlme_dbg_ratelimited(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MLME_DEBUG && net_ratelimit(), \ sdata, fmt, ##__VA_ARGS__) #endif /* __MAC80211_DEBUG_H */ compat-drivers-2012-09-18/net/mac80211/Kconfig0000644000175000017500000002042312026211315017606 0ustar mcgrofmcgrofconfig MAC80211 tristate "Generic IEEE 802.11 Networking Stack (mac80211)" depends on CFG80211 select CRYPTO select CRYPTO_ARC4 select CRYPTO_AES select CRC32 select AVERAGE ---help--- This option enables the hardware independent IEEE 802.11 networking stack. comment "CFG80211 needs to be enabled for MAC80211" depends on CFG80211=n if MAC80211 != n config MAC80211_HAS_RC bool config MAC80211_RC_PID bool "PID controller based rate control algorithm" if EXPERT select MAC80211_HAS_RC ---help--- This option enables a TX rate control algorithm for mac80211 that uses a PID controller to select the TX rate. config MAC80211_RC_MINSTREL bool "Minstrel" if EXPERT select MAC80211_HAS_RC default y ---help--- This option enables the 'minstrel' TX rate control algorithm config MAC80211_RC_MINSTREL_HT bool "Minstrel 802.11n support" if EXPERT depends on MAC80211_RC_MINSTREL default y ---help--- This option enables the 'minstrel_ht' TX rate control algorithm choice prompt "Default rate control algorithm" depends on MAC80211_HAS_RC default MAC80211_RC_DEFAULT_MINSTREL ---help--- This option selects the default rate control algorithm mac80211 will use. Note that this default can still be overridden through the ieee80211_default_rc_algo module parameter if different algorithms are available. config MAC80211_RC_DEFAULT_PID bool "PID controller based rate control algorithm" depends on MAC80211_RC_PID ---help--- Select the PID controller based rate control as the default rate control algorithm. You should choose this unless you know what you are doing. config MAC80211_RC_DEFAULT_MINSTREL bool "Minstrel" depends on MAC80211_RC_MINSTREL ---help--- Select Minstrel as the default rate control algorithm. endchoice config MAC80211_RC_DEFAULT string default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL default "pid" if MAC80211_RC_DEFAULT_PID default "" endif comment "Some wireless drivers require a rate control algorithm" depends on MAC80211 && MAC80211_HAS_RC=n config MAC80211_MESH bool "Enable mac80211 mesh networking (pre-802.11s) support" depends on MAC80211 && EXPERIMENTAL ---help--- This options enables support of Draft 802.11s mesh networking. The implementation is based on Draft 2.08 of the Mesh Networking amendment. However, no compliance with that draft is claimed or even possible, as drafts leave a number of identifiers to be defined after ratification. For more information visit http://o11s.org/. config MAC80211_LEDS bool "Enable LED triggers" depends on MAC80211 depends on LEDS_CLASS select LEDS_TRIGGERS ---help--- This option enables a few LED triggers for different packet receive/transmit events. config MAC80211_DEBUGFS bool "Export mac80211 internals in DebugFS" depends on MAC80211 && DEBUG_FS ---help--- Select this to see extensive information about the internal state of mac80211 in debugfs. Say N unless you know you need this. config MAC80211_MESSAGE_TRACING bool "Trace all mac80211 debug messages" depends on MAC80211 ---help--- Select this option to have mac80211 register the mac80211_msg trace subsystem with tracepoints to collect all debugging messages, independent of printing them into the kernel log. The overhead in this option is that all the messages need to be present in the binary and formatted at runtime for tracing. menuconfig MAC80211_DEBUG_MENU bool "Select mac80211 debugging features" depends on MAC80211 ---help--- This option collects various mac80211 debug settings. config MAC80211_NOINLINE bool "Do not inline TX/RX handlers" depends on MAC80211_DEBUG_MENU ---help--- This option affects code generation in mac80211, when selected some functions are marked "noinline" to allow easier debugging of problems in the transmit and receive paths. This option increases code size a bit and inserts a lot of function calls in the code, but is otherwise safe to enable. If unsure, say N unless you expect to be finding problems in mac80211. config MAC80211_VERBOSE_DEBUG bool "Verbose debugging output" depends on MAC80211_DEBUG_MENU ---help--- Selecting this option causes mac80211 to print out many debugging messages. It should not be selected on production systems as some of the messages are remotely triggerable. Do not select this option. config MAC80211_MLME_DEBUG bool "Verbose managed MLME output" depends on MAC80211_DEBUG_MENU ---help--- Selecting this option causes mac80211 to print out debugging messages for the managed-mode MLME. It should not be selected on production systems as some of the messages are remotely triggerable. Do not select this option. config MAC80211_STA_DEBUG bool "Verbose station debugging" depends on MAC80211_DEBUG_MENU ---help--- Selecting this option causes mac80211 to print out debugging messages for station addition/removal. Do not select this option. config MAC80211_HT_DEBUG bool "Verbose HT debugging" depends on MAC80211_DEBUG_MENU ---help--- This option enables 802.11n High Throughput features debug tracing output. It should not be selected on production systems as some of the messages are remotely triggerable. Do not select this option. config MAC80211_IBSS_DEBUG bool "Verbose IBSS debugging" depends on MAC80211_DEBUG_MENU ---help--- Selecting this option causes mac80211 to print out very verbose IBSS debugging messages. It should not be selected on production systems as those messages are remotely triggerable. Do not select this option. config MAC80211_PS_DEBUG bool "Verbose powersave mode debugging" depends on MAC80211_DEBUG_MENU ---help--- Selecting this option causes mac80211 to print out very verbose power save mode debugging messages (when mac80211 is an AP and has power saving stations.) It should not be selected on production systems as those messages are remotely triggerable. Do not select this option. config MAC80211_MPL_DEBUG bool "Verbose mesh peer link debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH ---help--- Selecting this option causes mac80211 to print out very verbose mesh peer link debugging messages (when mac80211 is taking part in a mesh network). It should not be selected on production systems as those messages are remotely triggerable. Do not select this option. config MAC80211_MPATH_DEBUG bool "Verbose mesh path debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH ---help--- Selecting this option causes mac80211 to print out very verbose mesh path selection debugging messages (when mac80211 is taking part in a mesh network). It should not be selected on production systems as those messages are remotely triggerable. Do not select this option. config MAC80211_MHWMP_DEBUG bool "Verbose mesh HWMP routing debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH ---help--- Selecting this option causes mac80211 to print out very verbose mesh routing (HWMP) debugging messages (when mac80211 is taking part in a mesh network). It should not be selected on production systems as those messages are remotely triggerable. Do not select this option. config MAC80211_MESH_SYNC_DEBUG bool "Verbose mesh mesh synchronization debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH ---help--- Selecting this option causes mac80211 to print out very verbose mesh synchronization debugging messages (when mac80211 is taking part in a mesh network). Do not select this option. config MAC80211_TDLS_DEBUG bool "Verbose TDLS debugging" depends on MAC80211_DEBUG_MENU ---help--- Selecting this option causes mac80211 to print out very verbose TDLS selection debugging messages (when mac80211 is a TDLS STA). It should not be selected on production systems as those messages are remotely triggerable. Do not select this option. config MAC80211_DEBUG_COUNTERS bool "Extra statistics for TX/RX debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_DEBUGFS ---help--- Selecting this option causes mac80211 to keep additional and very verbose statistics about TX and RX handler use and show them in debugfs. If unsure, say N. compat-drivers-2012-09-18/net/mac80211/mesh_hwmp.c0000644000175000017500000010374412026211315020446 0ustar mcgrofmcgrof/* * Copyright (c) 2008, 2009 open80211s Ltd. * Author: Luis Carlos Cobo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include "wme.h" #include "mesh.h" #define TEST_FRAME_LEN 8192 #define MAX_METRIC 0xffffffff #define ARITH_SHIFT 8 #define MAX_PREQ_QUEUE_LEN 64 /* Destination only */ #define MP_F_DO 0x1 /* Reply and forward */ #define MP_F_RF 0x2 /* Unknown Sequence Number */ #define MP_F_USN 0x01 /* Reason code Present */ #define MP_F_RCODE 0x02 static void mesh_queue_preq(struct mesh_path *, u8); static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae) { if (ae) offset += 6; return get_unaligned_le32(preq_elem + offset); } static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae) { if (ae) offset += 6; return get_unaligned_le16(preq_elem + offset); } /* HWMP IE processing macros */ #define AE_F (1<<6) #define AE_F_SET(x) (*x & AE_F) #define PREQ_IE_FLAGS(x) (*(x)) #define PREQ_IE_HOPCOUNT(x) (*(x + 1)) #define PREQ_IE_TTL(x) (*(x + 2)) #define PREQ_IE_PREQ_ID(x) u32_field_get(x, 3, 0) #define PREQ_IE_ORIG_ADDR(x) (x + 7) #define PREQ_IE_ORIG_SN(x) u32_field_get(x, 13, 0) #define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x)) #define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x)) #define PREQ_IE_TARGET_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26)) #define PREQ_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27) #define PREQ_IE_TARGET_SN(x) u32_field_get(x, 33, AE_F_SET(x)) #define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x) #define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x) #define PREP_IE_TTL(x) PREQ_IE_TTL(x) #define PREP_IE_ORIG_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) #define PREP_IE_ORIG_SN(x) u32_field_get(x, 27, AE_F_SET(x)) #define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)) #define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x)) #define PREP_IE_TARGET_ADDR(x) (x + 3) #define PREP_IE_TARGET_SN(x) u32_field_get(x, 9, 0) #define PERR_IE_TTL(x) (*(x)) #define PERR_IE_TARGET_FLAGS(x) (*(x + 2)) #define PERR_IE_TARGET_ADDR(x) (x + 3) #define PERR_IE_TARGET_SN(x) u32_field_get(x, 9, 0) #define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0) #define MSEC_TO_TU(x) (x*1000/1024) #define SN_GT(x, y) ((s32)(y - x) < 0) #define SN_LT(x, y) ((s32)(x - y) < 0) #define net_traversal_jiffies(s) \ msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime) #define default_lifetime(s) \ MSEC_TO_TU(s->u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout) #define min_preq_int_jiff(s) \ (msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval)) #define max_preq_retries(s) (s->u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries) #define disc_timeout_jiff(s) \ msecs_to_jiffies(sdata->u.mesh.mshcfg.min_discovery_timeout) #define root_path_confirmation_jiffies(s) \ msecs_to_jiffies(sdata->u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval) enum mpath_frame_type { MPATH_PREQ = 0, MPATH_PREP, MPATH_PERR, MPATH_RANN }; static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, u8 *orig_addr, __le32 orig_sn, u8 target_flags, u8 *target, __le32 target_sn, const u8 *da, u8 hop_count, u8 ttl, __le32 lifetime, __le32 metric, __le32 preq_id, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos, ie_len; int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + sizeof(mgmt->u.action.u.mesh_action); skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + 37); /* max HWMP IE */ if (!skb) return -1; skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); /* BSSID == SA */ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; mgmt->u.action.u.mesh_action.action_code = WLAN_MESH_ACTION_HWMP_PATH_SELECTION; switch (action) { case MPATH_PREQ: mhwmp_dbg(sdata, "sending PREQ to %pM\n", target); ie_len = 37; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PREQ; break; case MPATH_PREP: mhwmp_dbg(sdata, "sending PREP to %pM\n", target); ie_len = 31; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PREP; break; case MPATH_RANN: mhwmp_dbg(sdata, "sending RANN from %pM\n", orig_addr); ie_len = sizeof(struct ieee80211_rann_ie); pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_RANN; break; default: kfree_skb(skb); return -ENOTSUPP; break; } *pos++ = ie_len; *pos++ = flags; *pos++ = hop_count; *pos++ = ttl; if (action == MPATH_PREP) { memcpy(pos, target, ETH_ALEN); pos += ETH_ALEN; memcpy(pos, &target_sn, 4); pos += 4; } else { if (action == MPATH_PREQ) { memcpy(pos, &preq_id, 4); pos += 4; } memcpy(pos, orig_addr, ETH_ALEN); pos += ETH_ALEN; memcpy(pos, &orig_sn, 4); pos += 4; } memcpy(pos, &lifetime, 4); /* interval for RANN */ pos += 4; memcpy(pos, &metric, 4); pos += 4; if (action == MPATH_PREQ) { *pos++ = 1; /* destination count */ *pos++ = target_flags; memcpy(pos, target, ETH_ALEN); pos += ETH_ALEN; memcpy(pos, &target_sn, 4); pos += 4; } else if (action == MPATH_PREP) { memcpy(pos, orig_addr, ETH_ALEN); pos += ETH_ALEN; memcpy(pos, &orig_sn, 4); pos += 4; } ieee80211_tx_skb(sdata, skb); return 0; } /* Headroom is not adjusted. Caller should ensure that skb has sufficient * headroom in case the frame is encrypted. */ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); skb_set_mac_header(skb, 0); skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ skb_set_queue_mapping(skb, IEEE80211_AC_VO); skb->priority = 7; info->control.vif = &sdata->vif; ieee80211_set_qos_hdr(sdata, skb); } /** * mesh_send_path error - Sends a PERR mesh management frame * * @target: broken destination * @target_sn: SN of the broken destination * @target_rcode: reason code for this PERR * @ra: node this frame is addressed to * * Note: This function may be called with driver locks taken that the driver * also acquires in the TX path. To avoid a deadlock we don't transmit the * frame directly but add it to the pending queue instead. */ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode, const u8 *ra, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_mgmt *mgmt; u8 *pos, ie_len; int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + sizeof(mgmt->u.action.u.mesh_action); if (time_before(jiffies, ifmsh->next_perr)) return -EAGAIN; skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + 15 /* PERR IE */); if (!skb) return -1; skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, ra, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); /* BSSID == SA */ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; mgmt->u.action.u.mesh_action.action_code = WLAN_MESH_ACTION_HWMP_PATH_SELECTION; ie_len = 15; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PERR; *pos++ = ie_len; /* ttl */ *pos++ = ttl; /* number of destinations */ *pos++ = 1; /* * flags bit, bit 1 is unset if we know the sequence number and * bit 2 is set if we have a reason code */ *pos = 0; if (!target_sn) *pos |= MP_F_USN; if (target_rcode) *pos |= MP_F_RCODE; pos++; memcpy(pos, target, ETH_ALEN); pos += ETH_ALEN; memcpy(pos, &target_sn, 4); pos += 4; memcpy(pos, &target_rcode, 2); /* see note in function header */ prepare_frame_for_deferred_tx(sdata, skb); ifmsh->next_perr = TU_TO_EXP_TIME( ifmsh->mshcfg.dot11MeshHWMPperrMinInterval); ieee80211_add_pending_skb(local, skb); return 0; } void ieee80211s_update_metric(struct ieee80211_local *local, struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; int failed; if (!ieee80211_is_data(hdr->frame_control)) return; failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); /* moving average, scaled to 100 */ sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed); if (sta->fail_avg > 95) mesh_plink_broken(sta); } static u32 airtime_link_metric_get(struct ieee80211_local *local, struct sta_info *sta) { struct rate_info rinfo; /* This should be adjusted for each device */ int device_constant = 1 << ARITH_SHIFT; int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT; int s_unit = 1 << ARITH_SHIFT; int rate, err; u32 tx_time, estimated_retx; u64 result; if (sta->fail_avg >= 100) return MAX_METRIC; sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo); rate = cfg80211_calculate_bitrate(&rinfo); if (WARN_ON(!rate)) return MAX_METRIC; err = (sta->fail_avg << ARITH_SHIFT) / 100; /* bitrate is in units of 100 Kbps, while we need rate in units of * 1Mbps. This will be corrected on tx_time computation. */ tx_time = (device_constant + 10 * test_frame_len / rate); estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err)); result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ; return (u32)result; } /** * hwmp_route_info_get - Update routing info to originator and transmitter * * @sdata: local mesh subif * @mgmt: mesh management frame * @hwmp_ie: hwmp information element (PREP or PREQ) * * This function updates the path routing information to the originator and the * transmitter of a HWMP PREQ or PREP frame. * * Returns: metric to frame originator or 0 if the frame should not be further * processed * * Notes: this function is the only place (besides user-provided info) where * path routing information is updated. */ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, u8 *hwmp_ie, enum mpath_frame_type action) { struct ieee80211_local *local = sdata->local; struct mesh_path *mpath; struct sta_info *sta; bool fresh_info; u8 *orig_addr, *ta; u32 orig_sn, orig_metric; unsigned long orig_lifetime, exp_time; u32 last_hop_metric, new_metric; bool process = true; rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta) { rcu_read_unlock(); return 0; } last_hop_metric = airtime_link_metric_get(local, sta); /* Update and check originator routing info */ fresh_info = true; switch (action) { case MPATH_PREQ: orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie); orig_sn = PREQ_IE_ORIG_SN(hwmp_ie); orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie); orig_metric = PREQ_IE_METRIC(hwmp_ie); break; case MPATH_PREP: /* Originator here refers to the MP that was the target in the * Path Request. We divert from the nomenclature in the draft * so that we can easily use a single function to gather path * information from both PREQ and PREP frames. */ orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie); orig_sn = PREP_IE_TARGET_SN(hwmp_ie); orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); orig_metric = PREP_IE_METRIC(hwmp_ie); break; default: rcu_read_unlock(); return 0; } new_metric = orig_metric + last_hop_metric; if (new_metric < orig_metric) new_metric = MAX_METRIC; exp_time = TU_TO_EXP_TIME(orig_lifetime); if (ether_addr_equal(orig_addr, sdata->vif.addr)) { /* This MP is the originator, we are not interested in this * frame, except for updating transmitter's path info. */ process = false; fresh_info = false; } else { mpath = mesh_path_lookup(orig_addr, sdata); if (mpath) { spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_FIXED) fresh_info = false; else if ((mpath->flags & MESH_PATH_ACTIVE) && (mpath->flags & MESH_PATH_SN_VALID)) { if (SN_GT(mpath->sn, orig_sn) || (mpath->sn == orig_sn && new_metric >= mpath->metric)) { process = false; fresh_info = false; } } } else { mesh_path_add(orig_addr, sdata); mpath = mesh_path_lookup(orig_addr, sdata); if (!mpath) { rcu_read_unlock(); return 0; } spin_lock_bh(&mpath->state_lock); } if (fresh_info) { mesh_path_assign_nexthop(mpath, sta); mpath->flags |= MESH_PATH_SN_VALID; mpath->metric = new_metric; mpath->sn = orig_sn; mpath->exp_time = time_after(mpath->exp_time, exp_time) ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); mesh_path_tx_pending(mpath); /* draft says preq_id should be saved to, but there does * not seem to be any use for it, skipping by now */ } else spin_unlock_bh(&mpath->state_lock); } /* Update and check transmitter routing info */ ta = mgmt->sa; if (ether_addr_equal(orig_addr, ta)) fresh_info = false; else { fresh_info = true; mpath = mesh_path_lookup(ta, sdata); if (mpath) { spin_lock_bh(&mpath->state_lock); if ((mpath->flags & MESH_PATH_FIXED) || ((mpath->flags & MESH_PATH_ACTIVE) && (last_hop_metric > mpath->metric))) fresh_info = false; } else { mesh_path_add(ta, sdata); mpath = mesh_path_lookup(ta, sdata); if (!mpath) { rcu_read_unlock(); return 0; } spin_lock_bh(&mpath->state_lock); } if (fresh_info) { mesh_path_assign_nexthop(mpath, sta); mpath->metric = last_hop_metric; mpath->exp_time = time_after(mpath->exp_time, exp_time) ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); mesh_path_tx_pending(mpath); } else spin_unlock_bh(&mpath->state_lock); } rcu_read_unlock(); return process ? new_metric : 0; } static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, u8 *preq_elem, u32 metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath = NULL; u8 *target_addr, *orig_addr; const u8 *da; u8 target_flags, ttl, flags; u32 orig_sn, target_sn, lifetime, orig_metric; bool reply = false; bool forward = true; bool root_is_gate; /* Update target SN, if present */ target_addr = PREQ_IE_TARGET_ADDR(preq_elem); orig_addr = PREQ_IE_ORIG_ADDR(preq_elem); target_sn = PREQ_IE_TARGET_SN(preq_elem); orig_sn = PREQ_IE_ORIG_SN(preq_elem); target_flags = PREQ_IE_TARGET_F(preq_elem); orig_metric = metric; /* Proactive PREQ gate announcements */ flags = PREQ_IE_FLAGS(preq_elem); root_is_gate = !!(flags & RANN_FLAG_IS_GATE); mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr); if (ether_addr_equal(target_addr, sdata->vif.addr)) { mhwmp_dbg(sdata, "PREQ is for us\n"); forward = false; reply = true; metric = 0; if (time_after(jiffies, ifmsh->last_sn_update + net_traversal_jiffies(sdata)) || time_before(jiffies, ifmsh->last_sn_update)) { target_sn = ++ifmsh->sn; ifmsh->last_sn_update = jiffies; } } else if (is_broadcast_ether_addr(target_addr) && (target_flags & IEEE80211_PREQ_TO_FLAG)) { rcu_read_lock(); mpath = mesh_path_lookup(orig_addr, sdata); if (mpath) { if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) { reply = true; target_addr = sdata->vif.addr; target_sn = ++ifmsh->sn; metric = 0; ifmsh->last_sn_update = jiffies; } if (root_is_gate) mesh_path_add_gate(mpath); } rcu_read_unlock(); } else { rcu_read_lock(); mpath = mesh_path_lookup(target_addr, sdata); if (mpath) { if ((!(mpath->flags & MESH_PATH_SN_VALID)) || SN_LT(mpath->sn, target_sn)) { mpath->sn = target_sn; mpath->flags |= MESH_PATH_SN_VALID; } else if ((!(target_flags & MP_F_DO)) && (mpath->flags & MESH_PATH_ACTIVE)) { reply = true; metric = mpath->metric; target_sn = mpath->sn; if (target_flags & MP_F_RF) target_flags |= MP_F_DO; else forward = false; } } rcu_read_unlock(); } if (reply) { lifetime = PREQ_IE_LIFETIME(preq_elem); ttl = ifmsh->mshcfg.element_ttl; if (ttl != 0) { mhwmp_dbg(sdata, "replying to the PREQ\n"); mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr, cpu_to_le32(orig_sn), 0, target_addr, cpu_to_le32(target_sn), mgmt->sa, 0, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), 0, sdata); } else { ifmsh->mshstats.dropped_frames_ttl++; } } if (forward && ifmsh->mshcfg.dot11MeshForwarding) { u32 preq_id; u8 hopcount; ttl = PREQ_IE_TTL(preq_elem); lifetime = PREQ_IE_LIFETIME(preq_elem); if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; return; } mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr); --ttl; preq_id = PREQ_IE_PREQ_ID(preq_elem); hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1; da = (mpath && mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) { target_addr = PREQ_IE_TARGET_ADDR(preq_elem); target_sn = PREQ_IE_TARGET_SN(preq_elem); metric = orig_metric; } mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr, cpu_to_le32(orig_sn), target_flags, target_addr, cpu_to_le32(target_sn), da, hopcount, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), cpu_to_le32(preq_id), sdata); if (!is_multicast_ether_addr(da)) ifmsh->mshstats.fwded_unicast++; else ifmsh->mshstats.fwded_mcast++; ifmsh->mshstats.fwded_frames++; } } static inline struct sta_info * next_hop_deref_protected(struct mesh_path *mpath) { return rcu_dereference_protected(mpath->next_hop, lockdep_is_held(&mpath->state_lock)); } static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, u8 *prep_elem, u32 metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; u8 *target_addr, *orig_addr; u8 ttl, hopcount, flags; u8 next_hop[ETH_ALEN]; u32 target_sn, orig_sn, lifetime; mhwmp_dbg(sdata, "received PREP from %pM\n", PREP_IE_ORIG_ADDR(prep_elem)); orig_addr = PREP_IE_ORIG_ADDR(prep_elem); if (ether_addr_equal(orig_addr, sdata->vif.addr)) /* destination, no forwarding required */ return; if (!ifmsh->mshcfg.dot11MeshForwarding) return; ttl = PREP_IE_TTL(prep_elem); if (ttl <= 1) { sdata->u.mesh.mshstats.dropped_frames_ttl++; return; } rcu_read_lock(); mpath = mesh_path_lookup(orig_addr, sdata); if (mpath) spin_lock_bh(&mpath->state_lock); else goto fail; if (!(mpath->flags & MESH_PATH_ACTIVE)) { spin_unlock_bh(&mpath->state_lock); goto fail; } memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN); spin_unlock_bh(&mpath->state_lock); --ttl; flags = PREP_IE_FLAGS(prep_elem); lifetime = PREP_IE_LIFETIME(prep_elem); hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1; target_addr = PREP_IE_TARGET_ADDR(prep_elem); target_sn = PREP_IE_TARGET_SN(prep_elem); orig_sn = PREP_IE_ORIG_SN(prep_elem); mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, cpu_to_le32(orig_sn), 0, target_addr, cpu_to_le32(target_sn), next_hop, hopcount, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), 0, sdata); rcu_read_unlock(); sdata->u.mesh.mshstats.fwded_unicast++; sdata->u.mesh.mshstats.fwded_frames++; return; fail: rcu_read_unlock(); sdata->u.mesh.mshstats.dropped_frames_no_route++; } static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, u8 *perr_elem) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; u8 ttl; u8 *ta, *target_addr; u32 target_sn; u16 target_rcode; ta = mgmt->sa; ttl = PERR_IE_TTL(perr_elem); if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; return; } ttl--; target_addr = PERR_IE_TARGET_ADDR(perr_elem); target_sn = PERR_IE_TARGET_SN(perr_elem); target_rcode = PERR_IE_TARGET_RCODE(perr_elem); rcu_read_lock(); mpath = mesh_path_lookup(target_addr, sdata); if (mpath) { struct sta_info *sta; spin_lock_bh(&mpath->state_lock); sta = next_hop_deref_protected(mpath); if (mpath->flags & MESH_PATH_ACTIVE && ether_addr_equal(ta, sta->sta.addr) && (!(mpath->flags & MESH_PATH_SN_VALID) || SN_GT(target_sn, mpath->sn))) { mpath->flags &= ~MESH_PATH_ACTIVE; mpath->sn = target_sn; spin_unlock_bh(&mpath->state_lock); if (!ifmsh->mshcfg.dot11MeshForwarding) goto endperr; mesh_path_error_tx(ttl, target_addr, cpu_to_le32(target_sn), cpu_to_le16(target_rcode), broadcast_addr, sdata); } else spin_unlock_bh(&mpath->state_lock); } endperr: rcu_read_unlock(); } static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, struct ieee80211_rann_ie *rann) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct mesh_path *mpath; u8 ttl, flags, hopcount; u8 *orig_addr; u32 orig_sn, metric, metric_txsta, interval; bool root_is_gate; ttl = rann->rann_ttl; flags = rann->rann_flags; root_is_gate = !!(flags & RANN_FLAG_IS_GATE); orig_addr = rann->rann_addr; orig_sn = le32_to_cpu(rann->rann_seq); interval = le32_to_cpu(rann->rann_interval); hopcount = rann->rann_hopcount; hopcount++; metric = le32_to_cpu(rann->rann_metric); /* Ignore our own RANNs */ if (ether_addr_equal(orig_addr, sdata->vif.addr)) return; mhwmp_dbg(sdata, "received RANN from %pM via neighbour %pM (is_gate=%d)\n", orig_addr, mgmt->sa, root_is_gate); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta) { rcu_read_unlock(); return; } metric_txsta = airtime_link_metric_get(local, sta); mpath = mesh_path_lookup(orig_addr, sdata); if (!mpath) { mesh_path_add(orig_addr, sdata); mpath = mesh_path_lookup(orig_addr, sdata); if (!mpath) { rcu_read_unlock(); sdata->u.mesh.mshstats.dropped_frames_no_route++; return; } } if (!(SN_LT(mpath->sn, orig_sn)) && !(mpath->sn == orig_sn && metric < mpath->rann_metric)) { rcu_read_unlock(); return; } if ((!(mpath->flags & (MESH_PATH_ACTIVE | MESH_PATH_RESOLVING)) || (time_after(jiffies, mpath->last_preq_to_root + root_path_confirmation_jiffies(sdata)) || time_before(jiffies, mpath->last_preq_to_root))) && !(mpath->flags & MESH_PATH_FIXED) && (ttl != 0)) { mhwmp_dbg(sdata, "time to refresh root mpath %pM\n", orig_addr); mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); mpath->last_preq_to_root = jiffies; } mpath->sn = orig_sn; mpath->rann_metric = metric + metric_txsta; mpath->is_root = true; /* Recording RANNs sender address to send individually * addressed PREQs destined for root mesh STA */ memcpy(mpath->rann_snd_addr, mgmt->sa, ETH_ALEN); if (root_is_gate) mesh_path_add_gate(mpath); if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; rcu_read_unlock(); return; } ttl--; if (ifmsh->mshcfg.dot11MeshForwarding) { mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, cpu_to_le32(orig_sn), 0, NULL, 0, broadcast_addr, hopcount, ttl, cpu_to_le32(interval), cpu_to_le32(metric + metric_txsta), 0, sdata); } rcu_read_unlock(); } void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee802_11_elems elems; size_t baselen; u32 last_hop_metric; struct sta_info *sta; /* need action_code */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) return; rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) { rcu_read_unlock(); return; } rcu_read_unlock(); baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, len - baselen, &elems); if (elems.preq) { if (elems.preq_len != 37) /* Right now we support just 1 destination and no AE */ return; last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.preq, MPATH_PREQ); if (last_hop_metric) hwmp_preq_frame_process(sdata, mgmt, elems.preq, last_hop_metric); } if (elems.prep) { if (elems.prep_len != 31) /* Right now we support no AE */ return; last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.prep, MPATH_PREP); if (last_hop_metric) hwmp_prep_frame_process(sdata, mgmt, elems.prep, last_hop_metric); } if (elems.perr) { if (elems.perr_len != 15) /* Right now we support only one destination per PERR */ return; hwmp_perr_frame_process(sdata, mgmt, elems.perr); } if (elems.rann) hwmp_rann_frame_process(sdata, mgmt, elems.rann); } /** * mesh_queue_preq - queue a PREQ to a given destination * * @mpath: mesh path to discover * @flags: special attributes of the PREQ to be sent * * Locking: the function must be called from within a rcu read lock block. * */ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) { struct ieee80211_sub_if_data *sdata = mpath->sdata; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_preq_queue *preq_node; preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC); if (!preq_node) { mhwmp_dbg(sdata, "could not allocate PREQ node\n"); return; } spin_lock_bh(&ifmsh->mesh_preq_queue_lock); if (ifmsh->preq_queue_len == MAX_PREQ_QUEUE_LEN) { spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); kfree(preq_node); if (printk_ratelimit()) mhwmp_dbg(sdata, "PREQ node queue full\n"); return; } spin_lock(&mpath->state_lock); if (mpath->flags & MESH_PATH_REQ_QUEUED) { spin_unlock(&mpath->state_lock); spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); kfree(preq_node); return; } memcpy(preq_node->dst, mpath->dst, ETH_ALEN); preq_node->flags = flags; mpath->flags |= MESH_PATH_REQ_QUEUED; spin_unlock(&mpath->state_lock); list_add_tail(&preq_node->list, &ifmsh->preq_queue.list); ++ifmsh->preq_queue_len; spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) ieee80211_queue_work(&sdata->local->hw, &sdata->work); else if (time_before(jiffies, ifmsh->last_preq)) { /* avoid long wait if did not send preqs for a long time * and jiffies wrapped around */ ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1; ieee80211_queue_work(&sdata->local->hw, &sdata->work); } else mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq + min_preq_int_jiff(sdata)); } /** * mesh_path_start_discovery - launch a path discovery from the PREQ queue * * @sdata: local mesh subif */ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_preq_queue *preq_node; struct mesh_path *mpath; u8 ttl, target_flags; const u8 *da; u32 lifetime; spin_lock_bh(&ifmsh->mesh_preq_queue_lock); if (!ifmsh->preq_queue_len || time_before(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) { spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); return; } preq_node = list_first_entry(&ifmsh->preq_queue.list, struct mesh_preq_queue, list); list_del(&preq_node->list); --ifmsh->preq_queue_len; spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); rcu_read_lock(); mpath = mesh_path_lookup(preq_node->dst, sdata); if (!mpath) goto enddiscovery; spin_lock_bh(&mpath->state_lock); mpath->flags &= ~MESH_PATH_REQ_QUEUED; if (preq_node->flags & PREQ_Q_F_START) { if (mpath->flags & MESH_PATH_RESOLVING) { spin_unlock_bh(&mpath->state_lock); goto enddiscovery; } else { mpath->flags &= ~MESH_PATH_RESOLVED; mpath->flags |= MESH_PATH_RESOLVING; mpath->discovery_retries = 0; mpath->discovery_timeout = disc_timeout_jiff(sdata); } } else if (!(mpath->flags & MESH_PATH_RESOLVING) || mpath->flags & MESH_PATH_RESOLVED) { mpath->flags &= ~MESH_PATH_RESOLVING; spin_unlock_bh(&mpath->state_lock); goto enddiscovery; } ifmsh->last_preq = jiffies; if (time_after(jiffies, ifmsh->last_sn_update + net_traversal_jiffies(sdata)) || time_before(jiffies, ifmsh->last_sn_update)) { ++ifmsh->sn; sdata->u.mesh.last_sn_update = jiffies; } lifetime = default_lifetime(sdata); ttl = sdata->u.mesh.mshcfg.element_ttl; if (ttl == 0) { sdata->u.mesh.mshstats.dropped_frames_ttl++; spin_unlock_bh(&mpath->state_lock); goto enddiscovery; } if (preq_node->flags & PREQ_Q_F_REFRESH) target_flags = MP_F_DO; else target_flags = MP_F_RF; spin_unlock_bh(&mpath->state_lock); da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, cpu_to_le32(ifmsh->sn), target_flags, mpath->dst, cpu_to_le32(mpath->sn), da, 0, ttl, cpu_to_le32(lifetime), 0, cpu_to_le32(ifmsh->preq_id++), sdata); mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout); enddiscovery: rcu_read_unlock(); kfree(preq_node); } /** * mesh_nexthop_resolve - lookup next hop; conditionally start path discovery * * @skb: 802.11 frame to be sent * @sdata: network subif the frame will be sent through * * Lookup next hop for given skb and start path discovery if no * forwarding information is found. * * Returns: 0 if the next hop was found and -ENOENT if the frame was queued. * skb is freeed here if no mpath could be allocated. */ int mesh_nexthop_resolve(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct mesh_path *mpath; struct sk_buff *skb_to_free = NULL; u8 *target_addr = hdr->addr3; int err = 0; rcu_read_lock(); err = mesh_nexthop_lookup(skb, sdata); if (!err) goto endlookup; /* no nexthop found, start resolving */ mpath = mesh_path_lookup(target_addr, sdata); if (!mpath) { mesh_path_add(target_addr, sdata); mpath = mesh_path_lookup(target_addr, sdata); if (!mpath) { mesh_path_discard_frame(skb, sdata); err = -ENOSPC; goto endlookup; } } if (!(mpath->flags & MESH_PATH_RESOLVING)) mesh_queue_preq(mpath, PREQ_Q_F_START); if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN) skb_to_free = skb_dequeue(&mpath->frame_queue); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; ieee80211_set_qos_hdr(sdata, skb); skb_queue_tail(&mpath->frame_queue, skb); err = -ENOENT; if (skb_to_free) mesh_path_discard_frame(skb_to_free, sdata); endlookup: rcu_read_unlock(); return err; } /** * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling * this function is considered "using" the associated mpath, so preempt a path * refresh if this mpath expires soon. * * @skb: 802.11 frame to be sent * @sdata: network subif the frame will be sent through * * Returns: 0 if the next hop was found. Nonzero otherwise. */ int mesh_nexthop_lookup(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct mesh_path *mpath; struct sta_info *next_hop; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u8 *target_addr = hdr->addr3; int err = -ENOENT; rcu_read_lock(); mpath = mesh_path_lookup(target_addr, sdata); if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) goto endlookup; if (time_after(jiffies, mpath->exp_time - msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && ether_addr_equal(sdata->vif.addr, hdr->addr4) && !(mpath->flags & MESH_PATH_RESOLVING) && !(mpath->flags & MESH_PATH_FIXED)) mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); next_hop = rcu_dereference(mpath->next_hop); if (next_hop) { memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); err = 0; } endlookup: rcu_read_unlock(); return err; } void mesh_path_timer(unsigned long data) { struct mesh_path *mpath = (void *) data; struct ieee80211_sub_if_data *sdata = mpath->sdata; int ret; if (sdata->local->quiescing) return; spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_RESOLVED || (!(mpath->flags & MESH_PATH_RESOLVING))) { mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED); spin_unlock_bh(&mpath->state_lock); } else if (mpath->discovery_retries < max_preq_retries(sdata)) { ++mpath->discovery_retries; mpath->discovery_timeout *= 2; mpath->flags &= ~MESH_PATH_REQ_QUEUED; spin_unlock_bh(&mpath->state_lock); mesh_queue_preq(mpath, 0); } else { mpath->flags = 0; mpath->exp_time = jiffies; spin_unlock_bh(&mpath->state_lock); if (!mpath->is_gate && mesh_gate_num(sdata) > 0) { ret = mesh_path_send_to_gates(mpath); if (ret) mhwmp_dbg(sdata, "no gate was reachable\n"); } else mesh_path_flush_pending(mpath); } } void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; u8 flags, target_flags = 0; flags = (ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol) ? RANN_FLAG_IS_GATE : 0; switch (ifmsh->mshcfg.dot11MeshHWMPRootMode) { case IEEE80211_PROACTIVE_RANN: mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr, cpu_to_le32(++ifmsh->sn), 0, NULL, 0, broadcast_addr, 0, ifmsh->mshcfg.element_ttl, cpu_to_le32(interval), 0, 0, sdata); break; case IEEE80211_PROACTIVE_PREQ_WITH_PREP: flags |= IEEE80211_PREQ_PROACTIVE_PREP_FLAG; case IEEE80211_PROACTIVE_PREQ_NO_PREP: interval = ifmsh->mshcfg.dot11MeshHWMPactivePathToRootTimeout; target_flags |= IEEE80211_PREQ_TO_FLAG | IEEE80211_PREQ_USN_FLAG; mesh_path_sel_frame_tx(MPATH_PREQ, flags, sdata->vif.addr, cpu_to_le32(++ifmsh->sn), target_flags, (u8 *) broadcast_addr, 0, broadcast_addr, 0, ifmsh->mshcfg.element_ttl, cpu_to_le32(interval), 0, cpu_to_le32(ifmsh->preq_id++), sdata); break; default: mhwmp_dbg(sdata, "Proactive mechanism not supported\n"); return; } } compat-drivers-2012-09-18/net/mac80211/debugfs.c0000644000175000017500000002462612026211315020077 0ustar mcgrofmcgrof /* * mac80211 debugfs for wireless PHYs * * Copyright 2007 Johannes Berg * * GPLv2 * */ #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "rate.h" #include "debugfs.h" #define DEBUGFS_FORMAT_BUFFER_SIZE 100 int mac80211_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...) { va_list args; char buf[DEBUGFS_FORMAT_BUFFER_SIZE]; int res; va_start(args, fmt); res = vscnprintf(buf, sizeof(buf), fmt, args); va_end(args); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } #define DEBUGFS_READONLY_FILE_FN(name, fmt, value...) \ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ struct ieee80211_local *local = file->private_data; \ \ return mac80211_format_buffer(userbuf, count, ppos, \ fmt "\n", ##value); \ } #define DEBUGFS_READONLY_FILE_OPS(name) \ static const struct file_operations name## _ops = { \ .read = name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_READONLY_FILE(name, fmt, value...) \ DEBUGFS_READONLY_FILE_FN(name, fmt, value) \ DEBUGFS_READONLY_FILE_OPS(name) #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, phyd, local, &name## _ops); #define DEBUGFS_ADD_MODE(name, mode) \ debugfs_create_file(#name, mode, phyd, local, &name## _ops); DEBUGFS_READONLY_FILE(user_power, "%d", local->user_power_level); DEBUGFS_READONLY_FILE(power, "%d", local->hw.conf.power_level); DEBUGFS_READONLY_FILE(total_ps_buffered, "%d", local->total_ps_buffered); DEBUGFS_READONLY_FILE(wep_iv, "%#08x", local->wep_iv & 0xffffff); DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s", local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); static ssize_t reset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ieee80211_local *local = file->private_data; rtnl_lock(); __ieee80211_suspend(&local->hw, NULL); __ieee80211_resume(&local->hw); rtnl_unlock(); return count; } static const struct file_operations reset_ops = { .write = reset_write, .open = simple_open, .llseek = noop_llseek, }; static ssize_t hwflags_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ieee80211_local *local = file->private_data; int mxln = 500; ssize_t rv; char *buf = kzalloc(mxln, GFP_KERNEL); int sf = 0; /* how many written so far */ if (!buf) return 0; sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags); if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n"); if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n"); if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) sf += snprintf(buf + sf, mxln - sf, "HOST_BCAST_PS_BUFFERING\n"); if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE) sf += snprintf(buf + sf, mxln - sf, "2GHZ_SHORT_SLOT_INCAPABLE\n"); if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE) sf += snprintf(buf + sf, mxln - sf, "2GHZ_SHORT_PREAMBLE_INCAPABLE\n"); if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n"); if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n"); if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_PERIOD\n"); if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT) sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n"); if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n"); if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n"); if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW) sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); if (local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE) sf += snprintf(buf + sf, mxln - sf, "SCAN_WHILE_IDLE\n"); rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); kfree(buf); return rv; } static ssize_t queues_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ieee80211_local *local = file->private_data; unsigned long flags; char buf[IEEE80211_MAX_QUEUES * 20]; int q, res = 0; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (q = 0; q < local->hw.queues; q++) res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q, local->queue_stop_reasons[q], skb_queue_len(&local->pending[q])); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); return simple_read_from_buffer(user_buf, count, ppos, buf, res); } DEBUGFS_READONLY_FILE_OPS(hwflags); DEBUGFS_READONLY_FILE_OPS(queues); /* statistics stuff */ static ssize_t format_devstat_counter(struct ieee80211_local *local, char __user *userbuf, size_t count, loff_t *ppos, int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf, int buflen)) { struct ieee80211_low_level_stats stats; char buf[20]; int res; rtnl_lock(); res = drv_get_stats(local, &stats); rtnl_unlock(); if (res) return res; res = printvalue(&stats, buf, sizeof(buf)); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } #define DEBUGFS_DEVSTATS_FILE(name) \ static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\ char *buf, int buflen) \ { \ return scnprintf(buf, buflen, "%u\n", stats->name); \ } \ static ssize_t stats_ ##name## _read(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ return format_devstat_counter(file->private_data, \ userbuf, \ count, \ ppos, \ print_devstats_##name); \ } \ \ static const struct file_operations stats_ ##name## _ops = { \ .read = stats_ ##name## _read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; #define DEBUGFS_STATS_ADD(name, field) \ debugfs_create_u32(#name, 0400, statsd, (u32 *) &field); #define DEBUGFS_DEVSTATS_ADD(name) \ debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops); DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount); DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount); DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount); DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount); void debugfs_hw_add(struct ieee80211_local *local) { struct dentry *phyd = local->hw.wiphy->debugfsdir; struct dentry *statsd; if (!phyd) return; local->debugfs.keys = debugfs_create_dir("keys", phyd); DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); DEBUGFS_ADD(hwflags); DEBUGFS_ADD(user_power); DEBUGFS_ADD(power); statsd = debugfs_create_dir("statistics", phyd); /* if the dir failed, don't put all the other things into the root! */ if (!statsd) return; DEBUGFS_STATS_ADD(transmitted_fragment_count, local->dot11TransmittedFragmentCount); DEBUGFS_STATS_ADD(multicast_transmitted_frame_count, local->dot11MulticastTransmittedFrameCount); DEBUGFS_STATS_ADD(failed_count, local->dot11FailedCount); DEBUGFS_STATS_ADD(retry_count, local->dot11RetryCount); DEBUGFS_STATS_ADD(multiple_retry_count, local->dot11MultipleRetryCount); DEBUGFS_STATS_ADD(frame_duplicate_count, local->dot11FrameDuplicateCount); DEBUGFS_STATS_ADD(received_fragment_count, local->dot11ReceivedFragmentCount); DEBUGFS_STATS_ADD(multicast_received_frame_count, local->dot11MulticastReceivedFrameCount); DEBUGFS_STATS_ADD(transmitted_frame_count, local->dot11TransmittedFrameCount); #ifdef CONFIG_MAC80211_DEBUG_COUNTERS DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop); DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued); DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted, local->tx_handlers_drop_unencrypted); DEBUGFS_STATS_ADD(tx_handlers_drop_fragment, local->tx_handlers_drop_fragment); DEBUGFS_STATS_ADD(tx_handlers_drop_wep, local->tx_handlers_drop_wep); DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc, local->tx_handlers_drop_not_assoc); DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port, local->tx_handlers_drop_unauth_port); DEBUGFS_STATS_ADD(rx_handlers_drop, local->rx_handlers_drop); DEBUGFS_STATS_ADD(rx_handlers_queued, local->rx_handlers_queued); DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc, local->rx_handlers_drop_nullfunc); DEBUGFS_STATS_ADD(rx_handlers_drop_defrag, local->rx_handlers_drop_defrag); DEBUGFS_STATS_ADD(rx_handlers_drop_short, local->rx_handlers_drop_short); DEBUGFS_STATS_ADD(tx_expand_skb_head, local->tx_expand_skb_head); DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned, local->tx_expand_skb_head_cloned); DEBUGFS_STATS_ADD(rx_expand_skb_head, local->rx_expand_skb_head); DEBUGFS_STATS_ADD(rx_expand_skb_head2, local->rx_expand_skb_head2); DEBUGFS_STATS_ADD(rx_handlers_fragments, local->rx_handlers_fragments); DEBUGFS_STATS_ADD(tx_status_drop, local->tx_status_drop); #endif DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount); DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount); DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount); DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); } compat-drivers-2012-09-18/net/mac80211/pm.c0000644000175000017500000000754612026211315017076 0ustar mcgrofmcgrof#include #include #include "ieee80211_i.h" #include "mesh.h" #include "driver-ops.h" #include "led.h" /* return value indicates whether the driver should be further notified */ static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) { switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_quiesce(sdata); return true; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_quiesce(sdata); return true; case NL80211_IFTYPE_MESH_POINT: ieee80211_mesh_quiesce(sdata); return true; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: /* don't tell driver about this */ return false; default: return true; } } int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; struct sta_info *sta; if (!local->open_count) goto suspend; ieee80211_scan_cancel(local); if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); } mutex_unlock(&local->sta_mtx); } ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* flush out all packets */ synchronize_net(); drv_flush(local, false); local->quiescing = true; /* make quiescing visible to timers everywhere */ mb(); flush_workqueue(local->workqueue); /* Don't try to run timers while suspended. */ del_timer_sync(&local->sta_cleanup); /* * Note that this particular timer doesn't need to be * restarted at resume. */ cancel_work_sync(&local->dynamic_ps_enable_work); del_timer_sync(&local->dynamic_ps_timer); local->wowlan = wowlan && local->open_count; if (local->wowlan) { int err = drv_suspend(local, wowlan); if (err < 0) { local->quiescing = false; local->wowlan = false; if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } mutex_unlock(&local->sta_mtx); } ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); return err; } else if (err > 0) { WARN_ON(err != 1); local->wowlan = false; } else { list_for_each_entry(sdata, &local->interfaces, list) { cancel_work_sync(&sdata->work); ieee80211_quiesce(sdata); } goto suspend; } } /* disable keys */ list_for_each_entry(sdata, &local->interfaces, list) ieee80211_disable_keys(sdata); /* tear down aggregation sessions and remove STAs */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { if (sta->uploaded) { enum ieee80211_sta_state state; state = sta->sta_state; for (; state > IEEE80211_STA_NOTEXIST; state--) WARN_ON(drv_sta_state(local, sta->sdata, sta, state, state - 1)); } mesh_plink_quiesce(sta); } mutex_unlock(&local->sta_mtx); /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { cancel_work_sync(&sdata->work); if (!ieee80211_quiesce(sdata)) continue; if (!ieee80211_sdata_running(sdata)) continue; /* disable beaconing */ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_remove_interface(local, sdata); } sdata = rtnl_dereference(local->monitor_sdata); if (sdata) drv_remove_interface(local, sdata); /* stop hardware - this must stop RX */ if (local->open_count) ieee80211_stop_device(local); suspend: local->suspended = true; /* need suspended to be visible before quiescing is false */ barrier(); local->quiescing = false; return 0; } /* * __ieee80211_resume() is a static inline which just calls * ieee80211_reconfig(), which is also needed for hardware * hang/firmware failure/etc. recovery. */ compat-drivers-2012-09-18/net/mac80211/mesh.c0000644000175000017500000005560312026211315017413 0ustar mcgrofmcgrof/* * Copyright (c) 2008, 2009 open80211s Ltd. * Authors: Luis Carlos Cobo * Javier Cardona * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include "ieee80211_i.h" #include "mesh.h" #define TMR_RUNNING_HK 0 #define TMR_RUNNING_MP 1 #define TMR_RUNNING_MPR 2 int mesh_allocated; static struct kmem_cache *rm_cache; #ifdef CONFIG_MAC80211_MESH bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) { return (mgmt->u.action.u.mesh_action.action_code == WLAN_MESH_ACTION_HWMP_PATH_SELECTION); } #else bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) { return false; } #endif void ieee80211s_init(void) { mesh_pathtbl_init(); mesh_allocated = 1; rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry), 0, 0, NULL); } void ieee80211s_stop(void) { mesh_pathtbl_unregister(); kmem_cache_destroy(rm_cache); } static void ieee80211_mesh_housekeeping_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (void *) data; struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); if (local->quiescing) { set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); return; } ieee80211_queue_work(&local->hw, &sdata->work); } /** * mesh_matches_local - check if the config of a mesh point matches ours * * @sdata: local mesh subif * @ie: information elements of a management frame from the mesh peer * * This function checks if the mesh configuration of a mesh point matches the * local mesh configuration, i.e. if both nodes belong to the same mesh network. */ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *ie) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; u32 basic_rates = 0; enum nl80211_channel_type sta_channel_type = NL80211_CHAN_NO_HT; /* * As support for each feature is added, check for matching * - On mesh config capabilities * - Power Save Support En * - Sync support enabled * - Sync support active * - Sync support required from peer * - MDA enabled * - Power management control on fc */ if (!(ifmsh->mesh_id_len == ie->mesh_id_len && memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) goto mismatch; ieee80211_sta_get_rates(local, ie, local->oper_channel->band, &basic_rates); if (sdata->vif.bss_conf.basic_rates != basic_rates) goto mismatch; if (ie->ht_operation) sta_channel_type = ieee80211_ht_oper_to_channel_type(ie->ht_operation); /* Disallow HT40+/- mismatch */ if (ie->ht_operation && (sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40MINUS || sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40PLUS) && (sta_channel_type == NL80211_CHAN_HT40MINUS || sta_channel_type == NL80211_CHAN_HT40PLUS) && sdata->vif.bss_conf.channel_type != sta_channel_type) goto mismatch; return true; mismatch: return false; } /** * mesh_peer_accepts_plinks - check if an mp is willing to establish peer links * * @ie: information elements of a management frame from the mesh peer */ bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie) { return (ie->mesh_config->meshconf_cap & MESHCONF_CAPAB_ACCEPT_PLINKS) != 0; } /** * mesh_accept_plinks_update - update accepting_plink in local mesh beacons * * @sdata: mesh interface in which mesh beacons are going to be updated * * Returns: beacon changed flag if the beacon content changed. */ u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) { bool free_plinks; u32 changed = 0; /* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0, * the mesh interface might be able to establish plinks with peers that * are already on the table but are not on PLINK_ESTAB state. However, * in general the mesh interface is not accepting peer link requests * from new peers, and that must be reflected in the beacon */ free_plinks = mesh_plink_availables(sdata); if (free_plinks != sdata->u.mesh.accepting_plinks) { sdata->u.mesh.accepting_plinks = free_plinks; changed = BSS_CHANGED_BEACON; } return changed; } int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) { int i; sdata->u.mesh.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL); if (!sdata->u.mesh.rmc) return -ENOMEM; sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1; for (i = 0; i < RMC_BUCKETS; i++) INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list); return 0; } void mesh_rmc_free(struct ieee80211_sub_if_data *sdata) { struct mesh_rmc *rmc = sdata->u.mesh.rmc; struct rmc_entry *p, *n; int i; if (!sdata->u.mesh.rmc) return; for (i = 0; i < RMC_BUCKETS; i++) list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) { list_del(&p->list); kmem_cache_free(rm_cache, p); } kfree(rmc); sdata->u.mesh.rmc = NULL; } /** * mesh_rmc_check - Check frame in recent multicast cache and add if absent. * * @sa: source address * @mesh_hdr: mesh_header * * Returns: 0 if the frame is not in the cache, nonzero otherwise. * * Checks using the source address and the mesh sequence number if we have * received this frame lately. If the frame is not in the cache, it is added to * it. */ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, struct ieee80211_sub_if_data *sdata) { struct mesh_rmc *rmc = sdata->u.mesh.rmc; u32 seqnum = 0; int entries = 0; u8 idx; struct rmc_entry *p, *n; /* Don't care about endianness since only match matters */ memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum)); idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask; list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) { ++entries; if (time_after(jiffies, p->exp_time) || (entries == RMC_QUEUE_MAX_LEN)) { list_del(&p->list); kmem_cache_free(rm_cache, p); --entries; } else if ((seqnum == p->seqnum) && (ether_addr_equal(sa, p->sa))) return -1; } p = kmem_cache_alloc(rm_cache, GFP_ATOMIC); if (!p) return 0; p->seqnum = seqnum; p->exp_time = jiffies + RMC_TIMEOUT; memcpy(p->sa, sa, ETH_ALEN); list_add(&p->list, &rmc->bucket[idx].list); return 0; } int mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos, neighbors; u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); if (skb_tailroom(skb) < 2 + meshconf_len) return -ENOMEM; pos = skb_put(skb, 2 + meshconf_len); *pos++ = WLAN_EID_MESH_CONFIG; *pos++ = meshconf_len; /* Active path selection protocol ID */ *pos++ = ifmsh->mesh_pp_id; /* Active path selection metric ID */ *pos++ = ifmsh->mesh_pm_id; /* Congestion control mode identifier */ *pos++ = ifmsh->mesh_cc_id; /* Synchronization protocol identifier */ *pos++ = ifmsh->mesh_sp_id; /* Authentication Protocol identifier */ *pos++ = ifmsh->mesh_auth_id; /* Mesh Formation Info - number of neighbors */ neighbors = atomic_read(&ifmsh->mshstats.estab_plinks); /* Number of neighbor mesh STAs or 15 whichever is smaller */ neighbors = (neighbors > 15) ? 15 : neighbors; *pos++ = neighbors << 1; /* Mesh capability */ *pos = MESHCONF_CAPAB_FORWARDING; *pos |= ifmsh->accepting_plinks ? MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; *pos++ |= ifmsh->adjusting_tbtt ? MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; *pos++ = 0x00; return 0; } int mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos; if (skb_tailroom(skb) < 2 + ifmsh->mesh_id_len) return -ENOMEM; pos = skb_put(skb, 2 + ifmsh->mesh_id_len); *pos++ = WLAN_EID_MESH_ID; *pos++ = ifmsh->mesh_id_len; if (ifmsh->mesh_id_len) memcpy(pos, ifmsh->mesh_id, ifmsh->mesh_id_len); return 0; } int mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 offset, len; const u8 *data; if (!ifmsh->ie || !ifmsh->ie_len) return 0; /* fast-forward to vendor IEs */ offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0); if (offset) { len = ifmsh->ie_len - offset; data = ifmsh->ie + offset; if (skb_tailroom(skb) < len) return -ENOMEM; memcpy(skb_put(skb, len), data, len); } return 0; } int mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 len = 0; const u8 *data; if (!ifmsh->ie || !ifmsh->ie_len) return 0; /* find RSN IE */ data = ifmsh->ie; while (data < ifmsh->ie + ifmsh->ie_len) { if (*data == WLAN_EID_RSN) { len = data[1] + 2; break; } data++; } if (len) { if (skb_tailroom(skb) < len) return -ENOMEM; memcpy(skb_put(skb, len), data, len); } return 0; } int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan = local->oper_channel; u8 *pos; if (skb_tailroom(skb) < 3) return -ENOMEM; sband = local->hw.wiphy->bands[chan->band]; if (sband->band == IEEE80211_BAND_2GHZ) { pos = skb_put(skb, 2 + 1); *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = ieee80211_frequency_to_channel(chan->center_freq); } return 0; } int mesh_add_ht_cap_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; u8 *pos; sband = local->hw.wiphy->bands[local->oper_channel->band]; if (!sband->ht_cap.ht_supported || sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) return -ENOMEM; pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap)); ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); return 0; } int mesh_add_ht_oper_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_channel *channel = local->oper_channel; enum nl80211_channel_type channel_type = sdata->vif.bss_conf.channel_type; struct ieee80211_supported_band *sband = local->hw.wiphy->bands[channel->band]; struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; u8 *pos; if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation)) return -ENOMEM; pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type, sdata->vif.bss_conf.ht_operation_mode); return 0; } static void ieee80211_mesh_path_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; if (local->quiescing) { set_bit(TMR_RUNNING_MP, &ifmsh->timers_running); return; } ieee80211_queue_work(&local->hw, &sdata->work); } static void ieee80211_mesh_path_root_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags); if (local->quiescing) { set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running); return; } ieee80211_queue_work(&local->hw, &sdata->work); } void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh) { if (ifmsh->mshcfg.dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT) set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags); else { clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags); /* stop running timer */ del_timer_sync(&ifmsh->mesh_path_root_timer); } } /** * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame * @hdr: 802.11 frame header * @fc: frame control field * @meshda: destination address in the mesh * @meshsa: source address address in the mesh. Same as TA, as frame is * locally originated. * * Return the length of the 802.11 (does not include a mesh control header) */ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, const u8 *meshda, const u8 *meshsa) { if (is_multicast_ether_addr(meshda)) { *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA TA SA */ memcpy(hdr->addr1, meshda, ETH_ALEN); memcpy(hdr->addr2, meshsa, ETH_ALEN); memcpy(hdr->addr3, meshsa, ETH_ALEN); return 24; } else { *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */ memcpy(hdr->addr2, meshsa, ETH_ALEN); memcpy(hdr->addr3, meshda, ETH_ALEN); memcpy(hdr->addr4, meshsa, ETH_ALEN); return 30; } } /** * ieee80211_new_mesh_header - create a new mesh header * @meshhdr: uninitialized mesh header * @sdata: mesh interface to be used * @addr4or5: 1st address in the ae header, which may correspond to address 4 * (if addr6 is NULL) or address 5 (if addr6 is present). It may * be NULL. * @addr6: 2nd address in the ae header, which corresponds to addr6 of the * mesh frame * * Return the header length. */ int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, struct ieee80211_sub_if_data *sdata, char *addr4or5, char *addr6) { int aelen = 0; BUG_ON(!addr4or5 && addr6); memset(meshhdr, 0, sizeof(*meshhdr)); meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum); sdata->u.mesh.mesh_seqnum++; if (addr4or5 && !addr6) { meshhdr->flags |= MESH_FLAGS_AE_A4; aelen += ETH_ALEN; memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); } else if (addr4or5 && addr6) { meshhdr->flags |= MESH_FLAGS_AE_A5_A6; aelen += 2 * ETH_ALEN; memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); memcpy(meshhdr->eaddr2, addr6, ETH_ALEN); } return 6 + aelen; } static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh) { u32 changed; ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT); mesh_path_expire(sdata); changed = mesh_accept_plinks_update(sdata); ieee80211_bss_info_change_notify(sdata, changed); mod_timer(&ifmsh->housekeeping_timer, round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); } static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 interval; mesh_path_tx_root_frame(sdata); if (ifmsh->mshcfg.dot11MeshHWMPRootMode == IEEE80211_PROACTIVE_RANN) interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; else interval = ifmsh->mshcfg.dot11MeshHWMProotInterval; mod_timer(&ifmsh->mesh_path_root_timer, round_jiffies(TU_TO_EXP_TIME(interval))); } #ifdef CONFIG_PM void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; /* use atomic bitops in case all timers fire at the same time */ if (del_timer_sync(&ifmsh->housekeeping_timer)) set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); if (del_timer_sync(&ifmsh->mesh_path_timer)) set_bit(TMR_RUNNING_MP, &ifmsh->timers_running); if (del_timer_sync(&ifmsh->mesh_path_root_timer)) set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running); } void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running)) add_timer(&ifmsh->housekeeping_timer); if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running)) add_timer(&ifmsh->mesh_path_timer); if (test_and_clear_bit(TMR_RUNNING_MPR, &ifmsh->timers_running)) add_timer(&ifmsh->mesh_path_root_timer); ieee80211_mesh_root_setup(ifmsh); } #endif void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; local->fif_other_bss++; /* mesh ifaces must set allmulti to forward mcast traffic */ atomic_inc(&local->iff_allmultis); ieee80211_configure_filter(local); ifmsh->mesh_cc_id = 0; /* Disabled */ ifmsh->mesh_auth_id = 0; /* Disabled */ /* register sync ops from extensible synchronization framework */ ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id); ifmsh->adjusting_tbtt = false; ifmsh->sync_offset_clockdrift_max = 0; set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); ieee80211_mesh_root_setup(ifmsh); ieee80211_queue_work(&local->hw, &sdata->work); sdata->vif.bss_conf.ht_operation_mode = ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sdata->local, sdata->local->oper_channel->band); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_HT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT); netif_carrier_on(sdata->dev); } void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; netif_carrier_off(sdata->dev); /* stop the beacon */ ifmsh->mesh_id_len = 0; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); /* flush STAs and mpaths on this iface */ sta_info_flush(sdata->local, sdata); mesh_path_flush_by_iface(sdata); del_timer_sync(&sdata->u.mesh.housekeeping_timer); del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); del_timer_sync(&sdata->u.mesh.mesh_path_timer); /* * If the timer fired while we waited for it, it will have * requeued the work. Now the work will be running again * but will not rearm the timer again because it checks * whether the interface is running, which, at this point, * it no longer is. */ cancel_work_sync(&sdata->work); local->fif_other_bss--; atomic_dec(&local->iff_allmultis); ieee80211_configure_filter(local); sdata->u.mesh.timers_running = 0; } static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee802_11_elems elems; struct ieee80211_channel *channel; size_t baselen; int freq; enum ieee80211_band band = rx_status->band; /* ignore ProbeResp to foreign address */ if (stype == IEEE80211_STYPE_PROBE_RESP && !ether_addr_equal(mgmt->da, sdata->vif.addr)) return; baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; if (baselen > len) return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); /* ignore beacons from secure mesh peers if our security is off */ if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) return; if (elems.ds_params && elems.ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems.ds_params[0], band); else freq = rx_status->freq; channel = ieee80211_get_channel(local->hw.wiphy, freq); if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; if (elems.mesh_id && elems.mesh_config && mesh_matches_local(sdata, &elems)) mesh_neighbour_update(sdata, mgmt->sa, &elems); if (ifmsh->sync_ops) ifmsh->sync_ops->rx_bcn_presp(sdata, stype, mgmt, &elems, rx_status); } static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { switch (mgmt->u.action.category) { case WLAN_CATEGORY_SELF_PROTECTED: switch (mgmt->u.action.u.self_prot.action_code) { case WLAN_SP_MESH_PEERING_OPEN: case WLAN_SP_MESH_PEERING_CLOSE: case WLAN_SP_MESH_PEERING_CONFIRM: mesh_rx_plink_frame(sdata, mgmt, len, rx_status); break; } break; case WLAN_CATEGORY_MESH_ACTION: if (mesh_action_is_path_sel(mgmt)) mesh_rx_path_sel_frame(sdata, mgmt, len); break; } } void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; u16 stype; rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; switch (stype) { case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len, rx_status); break; case IEEE80211_STYPE_ACTION: ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status); break; } } void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; if (ifmsh->preq_queue_len && time_after(jiffies, ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) mesh_path_start_discovery(sdata); if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags)) mesh_mpath_table_grow(); if (test_and_clear_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags)) mesh_mpp_table_grow(); if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags)) ieee80211_mesh_housekeeping(sdata, ifmsh); if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags)) ieee80211_mesh_rootpath(sdata); if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) mesh_sync_adjust_tbtt(sdata); } void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) if (ieee80211_vif_is_mesh(&sdata->vif)) ieee80211_queue_work(&local->hw, &sdata->work); rcu_read_unlock(); } void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; setup_timer(&ifmsh->housekeeping_timer, ieee80211_mesh_housekeeping_timer, (unsigned long) sdata); ifmsh->accepting_plinks = true; ifmsh->preq_id = 0; ifmsh->sn = 0; ifmsh->num_gates = 0; atomic_set(&ifmsh->mpaths, 0); mesh_rmc_init(sdata); ifmsh->last_preq = jiffies; ifmsh->next_perr = jiffies; /* Allocate all mesh structures when creating the first mesh interface. */ if (!mesh_allocated) ieee80211s_init(); setup_timer(&ifmsh->mesh_path_timer, ieee80211_mesh_path_timer, (unsigned long) sdata); setup_timer(&ifmsh->mesh_path_root_timer, ieee80211_mesh_path_root_timer, (unsigned long) sdata); INIT_LIST_HEAD(&ifmsh->preq_queue.list); spin_lock_init(&ifmsh->mesh_preq_queue_lock); spin_lock_init(&ifmsh->sync_offset_lock); } compat-drivers-2012-09-18/net/mac80211/sta_info.c0000644000175000017500000010716612026211315020263 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2006-2007 Jiri Benc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "rate.h" #include "sta_info.h" #include "debugfs_sta.h" #include "mesh.h" #include "wme.h" /** * DOC: STA information lifetime rules * * STA info structures (&struct sta_info) are managed in a hash table * for faster lookup and a list for iteration. They are managed using * RCU, i.e. access to the list and hash table is protected by RCU. * * Upon allocating a STA info structure with sta_info_alloc(), the caller * owns that structure. It must then insert it into the hash table using * either sta_info_insert() or sta_info_insert_rcu(); only in the latter * case (which acquires an rcu read section but must not be called from * within one) will the pointer still be valid after the call. Note that * the caller may not do much with the STA info before inserting it, in * particular, it may not start any mesh peer link management or add * encryption keys. * * When the insertion fails (sta_info_insert()) returns non-zero), the * structure will have been freed by sta_info_insert()! * * Station entries are added by mac80211 when you establish a link with a * peer. This means different things for the different type of interfaces * we support. For a regular station this mean we add the AP sta when we * receive an association response from the AP. For IBSS this occurs when * get to know about a peer on the same IBSS. For WDS we add the sta for * the peer immediately upon device open. When using AP mode we add stations * for each respective station upon request from userspace through nl80211. * * In order to remove a STA info structure, various sta_info_destroy_*() * calls are available. * * There is no concept of ownership on a STA entry, each structure is * owned by the global hash table/list until it is removed. All users of * the structure need to be RCU protected so that the structure won't be * freed before they are done using it. */ /* Caller must hold local->sta_mtx */ static int sta_info_hash_del(struct ieee80211_local *local, struct sta_info *sta) { struct sta_info *s; s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)], lockdep_is_held(&local->sta_mtx)); if (!s) return -ENOENT; if (s == sta) { rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], s->hnext); return 0; } while (rcu_access_pointer(s->hnext) && rcu_access_pointer(s->hnext) != sta) s = rcu_dereference_protected(s->hnext, lockdep_is_held(&local->sta_mtx)); if (rcu_access_pointer(s->hnext)) { rcu_assign_pointer(s->hnext, sta->hnext); return 0; } return -ENOENT; } /* protected by RCU */ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], lockdep_is_held(&local->sta_mtx)); while (sta) { if (sta->sdata == sdata && ether_addr_equal(sta->sta.addr, addr)) break; sta = rcu_dereference_check(sta->hnext, lockdep_is_held(&local->sta_mtx)); } return sta; } /* * Get sta info either from the specified interface * or from one of its vlans */ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], lockdep_is_held(&local->sta_mtx)); while (sta) { if ((sta->sdata == sdata || (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && ether_addr_equal(sta->sta.addr, addr)) break; sta = rcu_dereference_check(sta->hnext, lockdep_is_held(&local->sta_mtx)); } return sta; } struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, int idx) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; int i = 0; list_for_each_entry_rcu(sta, &local->sta_list, list) { if (sdata != sta->sdata) continue; if (i < idx) { ++i; continue; } return sta; } return NULL; } /** * sta_info_free - free STA * * @local: pointer to the global information * @sta: STA info to free * * This function must undo everything done by sta_info_alloc() * that may happen before sta_info_insert(). It may only be * called when sta_info_insert() has not been attempted (and * if that fails, the station is freed anyway.) */ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) { if (sta->rate_ctrl) rate_control_free_sta(sta); sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); kfree(sta); } /* Caller must hold local->sta_mtx */ static void sta_info_hash_add(struct ieee80211_local *local, struct sta_info *sta) { lockdep_assert_held(&local->sta_mtx); sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)]; rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); } static void sta_unblock(struct work_struct *wk) { struct sta_info *sta; sta = container_of(wk, struct sta_info, drv_unblock_wk); if (sta->dead) return; if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { local_bh_disable(); ieee80211_sta_ps_deliver_wakeup(sta); local_bh_enable(); } else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { clear_sta_flag(sta, WLAN_STA_PS_DRIVER); local_bh_disable(); ieee80211_sta_ps_deliver_poll_response(sta); local_bh_enable(); } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) { clear_sta_flag(sta, WLAN_STA_PS_DRIVER); local_bh_disable(); ieee80211_sta_ps_deliver_uapsd(sta); local_bh_enable(); } else clear_sta_flag(sta, WLAN_STA_PS_DRIVER); } static int sta_prepare_rate_control(struct ieee80211_local *local, struct sta_info *sta, gfp_t gfp) { if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) return 0; sta->rate_ctrl = local->rate_ctrl; sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, &sta->sta, gfp); if (!sta->rate_ctrl_priv) return -ENOMEM; return 0; } struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, const u8 *addr, gfp_t gfp) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct timespec uptime; int i; sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); if (!sta) return NULL; spin_lock_init(&sta->lock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); mutex_init(&sta->ampdu_mlme.mtx); memcpy(sta->sta.addr, addr, ETH_ALEN); sta->local = local; sta->sdata = sdata; sta->last_rx = jiffies; sta->sta_state = IEEE80211_STA_NONE; do_posix_clock_monotonic_gettime(&uptime); sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); if (sta_prepare_rate_control(local, sta, gfp)) { kfree(sta); return NULL; } for (i = 0; i < STA_TID_NUM; i++) { /* * timer_to_tid must be initialized with identity mapping * to enable session_timer's data differentiation. See * sta_rx_agg_session_timer_expired for usage. */ sta->timer_to_tid[i] = i; } for (i = 0; i < IEEE80211_NUM_ACS; i++) { skb_queue_head_init(&sta->ps_tx_buf[i]); skb_queue_head_init(&sta->tx_filtered[i]); } for (i = 0; i < NUM_RX_DATA_QUEUES; i++) sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); #ifdef CONFIG_MAC80211_MESH sta->plink_state = NL80211_PLINK_LISTEN; init_timer(&sta->plink_timer); #endif return sta; } static int sta_info_insert_check(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; /* * Can't be a WARN_ON because it can be triggered through a race: * something inserts a STA (on one CPU) without holding the RTNL * and another CPU turns off the net device. */ if (unlikely(!ieee80211_sdata_running(sdata))) return -ENETDOWN; if (WARN_ON(ether_addr_equal(sta->sta.addr, sdata->vif.addr) || is_multicast_ether_addr(sta->sta.addr))) return -EINVAL; return 0; } static int sta_info_insert_drv_state(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { enum ieee80211_sta_state state; int err = 0; for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) { err = drv_sta_state(local, sdata, sta, state, state + 1); if (err) break; } if (!err) { /* * Drivers using legacy sta_add/sta_remove callbacks only * get uploaded set to true after sta_add is called. */ if (!local->ops->sta_add) sta->uploaded = true; return 0; } if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { sdata_info(sdata, "failed to move IBSS STA %pM to state %d (%d) - keeping it anyway\n", sta->sta.addr, state + 1, err); err = 0; } /* unwind on error */ for (; state > IEEE80211_STA_NOTEXIST; state--) WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1)); return err; } /* * should be called with sta_mtx locked * this function replaces the mutex lock * with a RCU lock */ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; struct station_info sinfo; int err = 0; lockdep_assert_held(&local->sta_mtx); /* check if STA exists already */ if (sta_info_get_bss(sdata, sta->sta.addr)) { err = -EEXIST; goto out_err; } /* notify driver */ err = sta_info_insert_drv_state(local, sdata, sta); if (err) goto out_err; local->num_sta++; local->sta_generation++; smp_mb(); /* make the station visible */ sta_info_hash_add(local, sta); list_add_rcu(&sta->list, &local->sta_list); set_sta_flag(sta, WLAN_STA_INSERTED); ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); memset(&sinfo, 0, sizeof(sinfo)); sinfo.filled = 0; sinfo.generation = local->sta_generation; cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr); /* move reference to rcu-protected */ rcu_read_lock(); mutex_unlock(&local->sta_mtx); if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata); return 0; out_err: mutex_unlock(&local->sta_mtx); rcu_read_lock(); return err; } int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; int err = 0; might_sleep(); err = sta_info_insert_check(sta); if (err) { rcu_read_lock(); goto out_free; } mutex_lock(&local->sta_mtx); err = sta_info_insert_finish(sta); if (err) goto out_free; return 0; out_free: BUG_ON(!err); sta_info_free(local, sta); return err; } int sta_info_insert(struct sta_info *sta) { int err = sta_info_insert_rcu(sta); rcu_read_unlock(); return err; } static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid) { /* * This format has been mandated by the IEEE specifications, * so this line may not be changed to use the __set_bit() format. */ bss->tim[aid / 8] |= (1 << (aid % 8)); } static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) { /* * This format has been mandated by the IEEE specifications, * so this line may not be changed to use the __clear_bit() format. */ bss->tim[aid / 8] &= ~(1 << (aid % 8)); } static unsigned long ieee80211_tids_for_ac(int ac) { /* If we ever support TIDs > 7, this obviously needs to be adjusted */ switch (ac) { case IEEE80211_AC_VO: return BIT(6) | BIT(7); case IEEE80211_AC_VI: return BIT(4) | BIT(5); case IEEE80211_AC_BE: return BIT(0) | BIT(3); case IEEE80211_AC_BK: return BIT(1) | BIT(2); default: WARN_ON(1); return 0; } } void sta_info_recalc_tim(struct sta_info *sta) { struct ieee80211_local *local = sta->local; struct ieee80211_if_ap *bss = sta->sdata->bss; unsigned long flags; bool indicate_tim = false; u8 ignore_for_tim = sta->sta.uapsd_queues; int ac; if (WARN_ON_ONCE(!sta->sdata->bss)) return; /* No need to do anything if the driver does all */ if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) return; if (sta->dead) goto done; /* * If all ACs are delivery-enabled then we should build * the TIM bit for all ACs anyway; if only some are then * we ignore those and build the TIM bit using only the * non-enabled ones. */ if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1) ignore_for_tim = 0; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; if (ignore_for_tim & BIT(ac)) continue; indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) || !skb_queue_empty(&sta->ps_tx_buf[ac]); if (indicate_tim) break; tids = ieee80211_tids_for_ac(ac); indicate_tim |= sta->driver_buffered_tids & tids; } done: spin_lock_irqsave(&local->tim_lock, flags); if (indicate_tim) __bss_tim_set(bss, sta->sta.aid); else __bss_tim_clear(bss, sta->sta.aid); if (local->ops->set_tim) { local->tim_in_locked_section = true; drv_set_tim(local, &sta->sta, indicate_tim); local->tim_in_locked_section = false; } spin_unlock_irqrestore(&local->tim_lock, flags); } static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_tx_info *info; int timeout; if (!skb) return false; info = IEEE80211_SKB_CB(skb); /* Timeout: (2 * listen_interval * beacon_int * 1024 / 1000000) sec */ timeout = (sta->listen_interval * sta->sdata->vif.bss_conf.beacon_int * 32 / 15625) * HZ; if (timeout < STA_TX_BUFFER_EXPIRE) timeout = STA_TX_BUFFER_EXPIRE; return time_after(jiffies, info->control.jiffies + timeout); } static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, struct sta_info *sta, int ac) { unsigned long flags; struct sk_buff *skb; /* * First check for frames that should expire on the filtered * queue. Frames here were rejected by the driver and are on * a separate queue to avoid reordering with normal PS-buffered * frames. They also aren't accounted for right now in the * total_ps_buffered counter. */ for (;;) { spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags); skb = skb_peek(&sta->tx_filtered[ac]); if (sta_info_buffer_expired(sta, skb)) skb = __skb_dequeue(&sta->tx_filtered[ac]); else skb = NULL; spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); /* * Frames are queued in order, so if this one * hasn't expired yet we can stop testing. If * we actually reached the end of the queue we * also need to stop, of course. */ if (!skb) break; dev_kfree_skb(skb); } /* * Now also check the normal PS-buffered queue, this will * only find something if the filtered queue was emptied * since the filtered frames are all before the normal PS * buffered frames. */ for (;;) { spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags); skb = skb_peek(&sta->ps_tx_buf[ac]); if (sta_info_buffer_expired(sta, skb)) skb = __skb_dequeue(&sta->ps_tx_buf[ac]); else skb = NULL; spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags); /* * frames are queued in order, so if this one * hasn't expired yet (or we reached the end of * the queue) we can stop testing */ if (!skb) break; local->total_ps_buffered--; ps_dbg(sta->sdata, "Buffered frame expired (STA %pM)\n", sta->sta.addr); dev_kfree_skb(skb); } /* * Finally, recalculate the TIM bit for this station -- it might * now be clear because the station was too slow to retrieve its * frames. */ sta_info_recalc_tim(sta); /* * Return whether there are any frames still buffered, this is * used to check whether the cleanup timer still needs to run, * if there are no frames we don't need to rearm the timer. */ return !(skb_queue_empty(&sta->ps_tx_buf[ac]) && skb_queue_empty(&sta->tx_filtered[ac])); } static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, struct sta_info *sta) { bool have_buffered = false; int ac; /* This is only necessary for stations on BSS interfaces */ if (!sta->sdata->bss) return false; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) have_buffered |= sta_info_cleanup_expire_buffered_ac(local, sta, ac); return have_buffered; } int __must_check __sta_info_destroy(struct sta_info *sta) { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; int ret, i, ac; struct tid_ampdu_tx *tid_tx; might_sleep(); if (!sta) return -ENOENT; local = sta->local; sdata = sta->sdata; lockdep_assert_held(&local->sta_mtx); /* * Before removing the station from the driver and * rate control, it might still start new aggregation * sessions -- block that to make sure the tear-down * will be sufficient. */ set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); ret = sta_info_hash_del(local, sta); if (ret) return ret; list_del_rcu(&sta->list); mutex_lock(&local->key_mtx); for (i = 0; i < NUM_DEFAULT_KEYS; i++) __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i])); if (sta->ptk) __ieee80211_key_free(key_mtx_dereference(local, sta->ptk)); mutex_unlock(&local->key_mtx); sta->dead = true; local->num_sta--; local->sta_generation++; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); while (sta->sta_state > IEEE80211_STA_NONE) { ret = sta_info_move_state(sta, sta->sta_state - 1); if (ret) { WARN_ON_ONCE(1); break; } } if (sta->uploaded) { ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE, IEEE80211_STA_NOTEXIST); WARN_ON_ONCE(ret != 0); } /* * At this point, after we wait for an RCU grace period, * neither mac80211 nor the driver can reference this * sta struct any more except by still existing timers * associated with this station that we clean up below. */ synchronize_rcu(); if (test_sta_flag(sta, WLAN_STA_PS_STA)) { BUG_ON(!sdata->bss); clear_sta_flag(sta, WLAN_STA_PS_STA); atomic_dec(&sdata->bss->num_sta_ps); sta_info_recalc_tim(sta); } for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); __skb_queue_purge(&sta->ps_tx_buf[ac]); __skb_queue_purge(&sta->tx_filtered[ac]); } #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata); #endif sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); cancel_work_sync(&sta->drv_unblock_wk); cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { mesh_plink_deactivate(sta); del_timer_sync(&sta->plink_timer); } #endif /* * Destroy aggregation state here. It would be nice to wait for the * driver to finish aggregation stop and then clean up, but for now * drivers have to handle aggregation stop being requested, followed * directly by station destruction. */ for (i = 0; i < STA_TID_NUM; i++) { tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); if (!tid_tx) continue; __skb_queue_purge(&tid_tx->pending); kfree(tid_tx); } sta_info_free(local, sta); return 0; } int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct sta_info *sta; int ret; mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get(sdata, addr); ret = __sta_info_destroy(sta); mutex_unlock(&sdata->local->sta_mtx); return ret; } int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct sta_info *sta; int ret; mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get_bss(sdata, addr); ret = __sta_info_destroy(sta); mutex_unlock(&sdata->local->sta_mtx); return ret; } static void sta_info_cleanup(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; struct sta_info *sta; bool timer_needed = false; rcu_read_lock(); list_for_each_entry_rcu(sta, &local->sta_list, list) if (sta_info_cleanup_expire_buffered(local, sta)) timer_needed = true; rcu_read_unlock(); if (local->quiescing) return; if (!timer_needed) return; mod_timer(&local->sta_cleanup, round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); } void sta_info_init(struct ieee80211_local *local) { spin_lock_init(&local->tim_lock); mutex_init(&local->sta_mtx); INIT_LIST_HEAD(&local->sta_list); setup_timer(&local->sta_cleanup, sta_info_cleanup, (unsigned long)local); } void sta_info_stop(struct ieee80211_local *local) { del_timer(&local->sta_cleanup); sta_info_flush(local, NULL); } /** * sta_info_flush - flush matching STA entries from the STA table * * Returns the number of removed STA entries. * * @local: local interface data * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs */ int sta_info_flush(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { struct sta_info *sta, *tmp; int ret = 0; might_sleep(); mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { if (!sdata || sdata == sta->sdata) { WARN_ON(__sta_info_destroy(sta)); ret++; } } mutex_unlock(&local->sta_mtx); return ret; } void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time) { struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { if (sdata != sta->sdata) continue; if (time_after(jiffies, sta->last_rx + exp_time)) { ibss_dbg(sdata, "expiring inactive STA %pM\n", sta->sta.addr); WARN_ON(__sta_info_destroy(sta)); } } mutex_unlock(&local->sta_mtx); } struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, const u8 *addr, const u8 *localaddr) { struct sta_info *sta, *nxt; /* * Just return a random station if localaddr is NULL * ... first in list. */ for_each_sta_info(hw_to_local(hw), addr, sta, nxt) { if (localaddr && !ether_addr_equal(sta->sdata->vif.addr, localaddr)) continue; if (!sta->uploaded) return NULL; return &sta->sta; } return NULL; } EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_ifaddr); struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *addr) { struct sta_info *sta; if (!vif) return NULL; sta = sta_info_get_bss(vif_to_sdata(vif), addr); if (!sta) return NULL; if (!sta->uploaded) return NULL; return &sta->sta; } EXPORT_SYMBOL(ieee80211_find_sta); static void clear_sta_ps_flags(void *_sta) { struct sta_info *sta = _sta; struct ieee80211_sub_if_data *sdata = sta->sdata; clear_sta_flag(sta, WLAN_STA_PS_DRIVER); if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA)) atomic_dec(&sdata->bss->num_sta_ps); } /* powersave support code */ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct sk_buff_head pending; int filtered = 0, buffered = 0, ac; clear_sta_flag(sta, WLAN_STA_SP); BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); sta->driver_buffered_tids = 0; if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); skb_queue_head_init(&pending); /* Send all buffered frames to the station */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { int count = skb_queue_len(&pending), tmp; skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending); tmp = skb_queue_len(&pending); filtered += tmp - count; count = tmp; skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending); tmp = skb_queue_len(&pending); buffered += tmp - count; } ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); local->total_ps_buffered -= buffered; sta_info_recalc_tim(sta); ps_dbg(sdata, "STA %pM aid %d sending %d filtered/%d PS frames since STA not sleeping anymore\n", sta->sta.addr, sta->sta.aid, filtered, buffered); } static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, int tid, enum ieee80211_frame_release_type reason) { struct ieee80211_local *local = sdata->local; struct ieee80211_qos_hdr *nullfunc; struct sk_buff *skb; int size = sizeof(*nullfunc); __le16 fc; bool qos = test_sta_flag(sta, WLAN_STA_WME); struct ieee80211_tx_info *info; if (qos) { fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC | IEEE80211_FCTL_FROMDS); } else { size -= 2; fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_FROMDS); } skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); nullfunc = (void *) skb_put(skb, size); nullfunc->frame_control = fc; nullfunc->duration_id = 0; memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); skb->priority = tid; skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); if (qos) { nullfunc->qos_ctrl = cpu_to_le16(tid); if (reason == IEEE80211_FRAME_RELEASE_UAPSD) nullfunc->qos_ctrl |= cpu_to_le16(IEEE80211_QOS_CTL_EOSP); } info = IEEE80211_SKB_CB(skb); /* * Tell TX path to send this frame even though the * STA may still remain is PS mode after this frame * exchange. Also set EOSP to indicate this packet * ends the poll/service period. */ info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); ieee80211_xmit(sdata, skb); } static void ieee80211_sta_ps_deliver_response(struct sta_info *sta, int n_frames, u8 ignored_acs, enum ieee80211_frame_release_type reason) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; bool found = false; bool more_data = false; int ac; unsigned long driver_release_tids = 0; struct sk_buff_head frames; /* Service or PS-Poll period starts */ set_sta_flag(sta, WLAN_STA_SP); __skb_queue_head_init(&frames); /* * Get response frame(s) and more data bit for it. */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; if (ignored_acs & BIT(ac)) continue; tids = ieee80211_tids_for_ac(ac); if (!found) { driver_release_tids = sta->driver_buffered_tids & tids; if (driver_release_tids) { found = true; } else { struct sk_buff *skb; while (n_frames > 0) { skb = skb_dequeue(&sta->tx_filtered[ac]); if (!skb) { skb = skb_dequeue( &sta->ps_tx_buf[ac]); if (skb) local->total_ps_buffered--; } if (!skb) break; n_frames--; found = true; __skb_queue_tail(&frames, skb); } } /* * If the driver has data on more than one TID then * certainly there's more data if we release just a * single frame now (from a single TID). */ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL && hweight16(driver_release_tids) > 1) { more_data = true; driver_release_tids = BIT(ffs(driver_release_tids) - 1); break; } } if (!skb_queue_empty(&sta->tx_filtered[ac]) || !skb_queue_empty(&sta->ps_tx_buf[ac])) { more_data = true; break; } } if (!found) { int tid; /* * For PS-Poll, this can only happen due to a race condition * when we set the TIM bit and the station notices it, but * before it can poll for the frame we expire it. * * For uAPSD, this is said in the standard (11.2.1.5 h): * At each unscheduled SP for a non-AP STA, the AP shall * attempt to transmit at least one MSDU or MMPDU, but no * more than the value specified in the Max SP Length field * in the QoS Capability element from delivery-enabled ACs, * that are destined for the non-AP STA. * * Since we have no other MSDU/MMPDU, transmit a QoS null frame. */ /* This will evaluate to 1, 3, 5 or 7. */ tid = 7 - ((ffs(~ignored_acs) - 1) << 1); ieee80211_send_null_response(sdata, sta, tid, reason); return; } if (!driver_release_tids) { struct sk_buff_head pending; struct sk_buff *skb; int num = 0; u16 tids = 0; skb_queue_head_init(&pending); while ((skb = __skb_dequeue(&frames))) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *) skb->data; u8 *qoshdr = NULL; num++; /* * Tell TX path to send this frame even though the * STA may still remain is PS mode after this frame * exchange. */ info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; /* * Use MoreData flag to indicate whether there are * more buffered frames for this STA */ if (more_data || !skb_queue_empty(&frames)) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); else hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); if (ieee80211_is_data_qos(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) qoshdr = ieee80211_get_qos_ctl(hdr); /* end service period after last frame */ if (skb_queue_empty(&frames)) { if (reason == IEEE80211_FRAME_RELEASE_UAPSD && qoshdr) *qoshdr |= IEEE80211_QOS_CTL_EOSP; info->flags |= IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; } if (qoshdr) tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); else tids |= BIT(0); __skb_queue_tail(&pending, skb); } drv_allow_buffered_frames(local, sta, tids, num, reason, more_data); ieee80211_add_pending_skbs(local, &pending); sta_info_recalc_tim(sta); } else { /* * We need to release a frame that is buffered somewhere in the * driver ... it'll have to handle that. * Note that, as per the comment above, it'll also have to see * if there is more than just one frame on the specific TID that * we're releasing from, and it needs to set the more-data bit * accordingly if we tell it that there's no more data. If we do * tell it there's more data, then of course the more-data bit * needs to be set anyway. */ drv_release_buffered_frames(local, sta, driver_release_tids, n_frames, reason, more_data); /* * Note that we don't recalculate the TIM bit here as it would * most likely have no effect at all unless the driver told us * that the TID became empty before returning here from the * release function. * Either way, however, when the driver tells us that the TID * became empty we'll do the TIM recalculation. */ } } void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) { u8 ignore_for_response = sta->sta.uapsd_queues; /* * If all ACs are delivery-enabled then we should reply * from any of them, if only some are enabled we reply * only from the non-enabled ones. */ if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1) ignore_for_response = 0; ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response, IEEE80211_FRAME_RELEASE_PSPOLL); } void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) { int n_frames = sta->sta.max_sp; u8 delivery_enabled = sta->sta.uapsd_queues; /* * If we ever grow support for TSPEC this might happen if * the TSPEC update from hostapd comes in between a trigger * frame setting WLAN_STA_UAPSD in the RX path and this * actually getting called. */ if (!delivery_enabled) return; switch (sta->sta.max_sp) { case 1: n_frames = 2; break; case 2: n_frames = 4; break; case 3: n_frames = 6; break; case 0: /* XXX: what is a good value? */ n_frames = 8; break; } ieee80211_sta_ps_deliver_response(sta, n_frames, ~delivery_enabled, IEEE80211_FRAME_RELEASE_UAPSD); } void ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, bool block) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); trace_api_sta_block_awake(sta->local, pubsta, block); if (block) set_sta_flag(sta, WLAN_STA_PS_DRIVER); else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) ieee80211_queue_work(hw, &sta->drv_unblock_wk); } EXPORT_SYMBOL(ieee80211_sta_block_awake); void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct ieee80211_local *local = sta->local; struct sk_buff *skb; struct skb_eosp_msg_data *data; trace_api_eosp(local, pubsta); skb = alloc_skb(0, GFP_ATOMIC); if (!skb) { /* too bad ... but race is better than loss */ clear_sta_flag(sta, WLAN_STA_SP); return; } data = (void *)skb->cb; memcpy(data->sta, pubsta->addr, ETH_ALEN); memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN); skb->pkt_type = IEEE80211_EOSP_MSG; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); } EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe); void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, u8 tid, bool buffered) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); if (WARN_ON(tid >= STA_TID_NUM)) return; if (buffered) set_bit(tid, &sta->driver_buffered_tids); else clear_bit(tid, &sta->driver_buffered_tids); sta_info_recalc_tim(sta); } EXPORT_SYMBOL(ieee80211_sta_set_buffered); int sta_info_move_state(struct sta_info *sta, enum ieee80211_sta_state new_state) { might_sleep(); if (sta->sta_state == new_state) return 0; /* check allowed transitions first */ switch (new_state) { case IEEE80211_STA_NONE: if (sta->sta_state != IEEE80211_STA_AUTH) return -EINVAL; break; case IEEE80211_STA_AUTH: if (sta->sta_state != IEEE80211_STA_NONE && sta->sta_state != IEEE80211_STA_ASSOC) return -EINVAL; break; case IEEE80211_STA_ASSOC: if (sta->sta_state != IEEE80211_STA_AUTH && sta->sta_state != IEEE80211_STA_AUTHORIZED) return -EINVAL; break; case IEEE80211_STA_AUTHORIZED: if (sta->sta_state != IEEE80211_STA_ASSOC) return -EINVAL; break; default: WARN(1, "invalid state %d", new_state); return -EINVAL; } sta_dbg(sta->sdata, "moving STA %pM to state %d\n", sta->sta.addr, new_state); /* * notify the driver before the actual changes so it can * fail the transition */ if (test_sta_flag(sta, WLAN_STA_INSERTED)) { int err = drv_sta_state(sta->local, sta->sdata, sta, sta->sta_state, new_state); if (err) return err; } /* reflect the change in all state variables */ switch (new_state) { case IEEE80211_STA_NONE: if (sta->sta_state == IEEE80211_STA_AUTH) clear_bit(WLAN_STA_AUTH, &sta->_flags); break; case IEEE80211_STA_AUTH: if (sta->sta_state == IEEE80211_STA_NONE) set_bit(WLAN_STA_AUTH, &sta->_flags); else if (sta->sta_state == IEEE80211_STA_ASSOC) clear_bit(WLAN_STA_ASSOC, &sta->_flags); break; case IEEE80211_STA_ASSOC: if (sta->sta_state == IEEE80211_STA_AUTH) { set_bit(WLAN_STA_ASSOC, &sta->_flags); } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { if (sta->sdata->vif.type == NL80211_IFTYPE_AP || (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sta->sdata->u.vlan.sta)) atomic_dec(&sta->sdata->bss->num_mcast_sta); clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); } break; case IEEE80211_STA_AUTHORIZED: if (sta->sta_state == IEEE80211_STA_ASSOC) { if (sta->sdata->vif.type == NL80211_IFTYPE_AP || (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sta->sdata->u.vlan.sta)) atomic_inc(&sta->sdata->bss->num_mcast_sta); set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); } break; default: break; } sta->sta_state = new_state; return 0; } compat-drivers-2012-09-18/net/mac80211/cfg.c0000644000175000017500000024135212026211315017214 0ustar mcgrofmcgrof/* * mac80211 configuration hooks for cfg80211 * * Copyright 2006-2010 Johannes Berg * * This file is GPLv2 as found in COPYING. */ #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "cfg.h" #include "rate.h" #include "mesh.h" static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct ieee80211_local *local = wiphy_priv(wiphy); struct wireless_dev *wdev; struct ieee80211_sub_if_data *sdata; int err; err = ieee80211_if_add(local, name, &wdev, type, params); if (err) return ERR_PTR(err); if (type == NL80211_IFTYPE_MONITOR && flags) { sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); sdata->u.mntr_flags = *flags; } return wdev; } static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) { ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev)); return 0; } static int ieee80211_change_iface(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); int ret; ret = ieee80211_if_change_type(sdata, type); if (ret) return ret; if (type == NL80211_IFTYPE_AP_VLAN && params && params->use_4addr == 0) RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); else if (type == NL80211_IFTYPE_STATION && params && params->use_4addr >= 0) sdata->u.mgd.use_4addr = params->use_4addr; if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) { struct ieee80211_local *local = sdata->local; if (ieee80211_sdata_running(sdata)) { /* * Prohibit MONITOR_FLAG_COOK_FRAMES to be * changed while the interface is up. * Else we would need to add a lot of cruft * to update everything: * cooked_mntrs, monitor and all fif_* counters * reconfigure hardware */ if ((*flags & MONITOR_FLAG_COOK_FRAMES) != (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) return -EBUSY; ieee80211_adjust_monitor_flags(sdata, -1); sdata->u.mntr_flags = *flags; ieee80211_adjust_monitor_flags(sdata, 1); ieee80211_configure_filter(local); } else { /* * Because the interface is down, ieee80211_do_stop * and ieee80211_do_open take care of "everything" * mentioned in the comment above. */ sdata->u.mntr_flags = *flags; } } return 0; } static int ieee80211_start_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) { return ieee80211_do_open(wdev, true); } static void ieee80211_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) { ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev)); } static int ieee80211_set_noack_map(struct wiphy *wiphy, struct net_device *dev, u16 noack_map) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); sdata->noack_map = noack_map; return 0; } static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta = NULL; struct ieee80211_key *key; int err; if (!ieee80211_sdata_running(sdata)) return -ENETDOWN; /* reject WEP and TKIP keys if WEP failed to initialize */ switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_WEP104: if (IS_ERR(sdata->local->wep_tx_tfm)) return -EINVAL; break; default: break; } key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len, params->key, params->seq_len, params->seq); if (IS_ERR(key)) return PTR_ERR(key); if (pairwise) key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; mutex_lock(&sdata->local->sta_mtx); if (mac_addr) { if (ieee80211_vif_is_mesh(&sdata->vif)) sta = sta_info_get(sdata, mac_addr); else sta = sta_info_get_bss(sdata, mac_addr); if (!sta) { ieee80211_key_free(sdata->local, key); err = -ENOENT; goto out_unlock; } } err = ieee80211_key_link(key, sdata, sta); if (err) ieee80211_key_free(sdata->local, key); out_unlock: mutex_unlock(&sdata->local->sta_mtx); return err; } static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct ieee80211_key *key = NULL; int ret; mutex_lock(&local->sta_mtx); mutex_lock(&local->key_mtx); if (mac_addr) { ret = -ENOENT; sta = sta_info_get_bss(sdata, mac_addr); if (!sta) goto out_unlock; if (pairwise) key = key_mtx_dereference(local, sta->ptk); else key = key_mtx_dereference(local, sta->gtk[key_idx]); } else key = key_mtx_dereference(local, sdata->keys[key_idx]); if (!key) { ret = -ENOENT; goto out_unlock; } __ieee80211_key_free(key); ret = 0; out_unlock: mutex_unlock(&local->key_mtx); mutex_unlock(&local->sta_mtx); return ret; } static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *params)) { struct ieee80211_sub_if_data *sdata; struct sta_info *sta = NULL; u8 seq[6] = {0}; struct key_params params; struct ieee80211_key *key = NULL; u64 pn64; u32 iv32; u16 iv16; int err = -ENOENT; sdata = IEEE80211_DEV_TO_SUB_IF(dev); rcu_read_lock(); if (mac_addr) { sta = sta_info_get_bss(sdata, mac_addr); if (!sta) goto out; if (pairwise) key = rcu_dereference(sta->ptk); else if (key_idx < NUM_DEFAULT_KEYS) key = rcu_dereference(sta->gtk[key_idx]); } else key = rcu_dereference(sdata->keys[key_idx]); if (!key) goto out; memset(¶ms, 0, sizeof(params)); params.cipher = key->conf.cipher; switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: iv32 = key->u.tkip.tx.iv32; iv16 = key->u.tkip.tx.iv16; if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) drv_get_tkip_seq(sdata->local, key->conf.hw_key_idx, &iv32, &iv16); seq[0] = iv16 & 0xff; seq[1] = (iv16 >> 8) & 0xff; seq[2] = iv32 & 0xff; seq[3] = (iv32 >> 8) & 0xff; seq[4] = (iv32 >> 16) & 0xff; seq[5] = (iv32 >> 24) & 0xff; params.seq = seq; params.seq_len = 6; break; case WLAN_CIPHER_SUITE_CCMP: pn64 = atomic64_read(&key->u.ccmp.tx_pn); seq[0] = pn64; seq[1] = pn64 >> 8; seq[2] = pn64 >> 16; seq[3] = pn64 >> 24; seq[4] = pn64 >> 32; seq[5] = pn64 >> 40; params.seq = seq; params.seq_len = 6; break; case WLAN_CIPHER_SUITE_AES_CMAC: pn64 = atomic64_read(&key->u.aes_cmac.tx_pn); seq[0] = pn64; seq[1] = pn64 >> 8; seq[2] = pn64 >> 16; seq[3] = pn64 >> 24; seq[4] = pn64 >> 32; seq[5] = pn64 >> 40; params.seq = seq; params.seq_len = 6; break; } params.key = key->conf.key; params.key_len = key->conf.keylen; callback(cookie, ¶ms); err = 0; out: rcu_read_unlock(); return err; } static int ieee80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool uni, bool multi) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ieee80211_set_default_key(sdata, key_idx, uni, multi); return 0; } static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ieee80211_set_default_mgmt_key(sdata, key_idx); return 0; } static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx) { if (!(rate->flags & RATE_INFO_FLAGS_MCS)) { struct ieee80211_supported_band *sband; sband = sta->local->hw.wiphy->bands[ sta->local->oper_channel->band]; rate->legacy = sband->bitrates[idx].bitrate; } else rate->mcs = idx; } void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo) { rinfo->flags = 0; if (rate->flags & IEEE80211_TX_RC_MCS) rinfo->flags |= RATE_INFO_FLAGS_MCS; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; if (rate->flags & IEEE80211_TX_RC_SHORT_GI) rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; rate_idx_to_bitrate(rinfo, sta, rate->idx); } static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct timespec uptime; sinfo->generation = sdata->local->sta_generation; sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | STATION_INFO_TX_RETRIES | STATION_INFO_TX_FAILED | STATION_INFO_TX_BITRATE | STATION_INFO_RX_BITRATE | STATION_INFO_RX_DROP_MISC | STATION_INFO_BSS_PARAM | STATION_INFO_CONNECTED_TIME | STATION_INFO_STA_FLAGS | STATION_INFO_BEACON_LOSS_COUNT; do_posix_clock_monotonic_gettime(&uptime); sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; sinfo->tx_bytes = sta->tx_bytes; sinfo->rx_packets = sta->rx_packets; sinfo->tx_packets = sta->tx_packets; sinfo->tx_retries = sta->tx_retry_count; sinfo->tx_failed = sta->tx_retry_failed; sinfo->rx_dropped_misc = sta->rx_dropped; sinfo->beacon_loss_count = sta->beacon_loss_count; if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; if (!local->ops->get_rssi || drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal)) sinfo->signal = (s8)sta->last_signal; sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); } sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); sinfo->rxrate.flags = 0; if (sta->last_rx_rate_flag & RX_FLAG_HT) sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS; if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx); if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH sinfo->filled |= STATION_INFO_LLID | STATION_INFO_PLID | STATION_INFO_PLINK_STATE; sinfo->llid = le16_to_cpu(sta->llid); sinfo->plid = le16_to_cpu(sta->plid); sinfo->plink_state = sta->plink_state; if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { sinfo->filled |= STATION_INFO_T_OFFSET; sinfo->t_offset = sta->t_offset; } #endif } sinfo->bss_param.flags = 0; if (sdata->vif.bss_conf.use_cts_prot) sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; if (sdata->vif.bss_conf.use_short_preamble) sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; if (sdata->vif.bss_conf.use_short_slot) sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; sinfo->sta_flags.set = 0; sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHENTICATED) | BIT(NL80211_STA_FLAG_TDLS_PEER); if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); if (test_sta_flag(sta, WLAN_STA_WME)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); if (test_sta_flag(sta, WLAN_STA_MFP)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); if (test_sta_flag(sta, WLAN_STA_AUTH)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); } static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { "rx_packets", "rx_bytes", "wep_weak_iv_count", "rx_duplicates", "rx_fragments", "rx_dropped", "tx_packets", "tx_bytes", "tx_fragments", "tx_filtered", "tx_retry_failed", "tx_retries", "beacon_loss", "sta_state", "txrate", "rxrate", "signal", "channel", "noise", "ch_time", "ch_time_busy", "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" }; #define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) static int ieee80211_get_et_sset_count(struct wiphy *wiphy, struct net_device *dev, int sset) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); int rv = 0; if (sset == ETH_SS_STATS) rv += STA_STATS_LEN; rv += drv_get_et_sset_count(sdata, sset); if (rv == 0) return -EOPNOTSUPP; return rv; } static void ieee80211_get_et_stats(struct wiphy *wiphy, struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta; struct ieee80211_local *local = sdata->local; struct station_info sinfo; struct survey_info survey; int i, q; #define STA_STATS_SURVEY_LEN 7 memset(data, 0, sizeof(u64) * STA_STATS_LEN); #define ADD_STA_STATS(sta) \ do { \ data[i++] += sta->rx_packets; \ data[i++] += sta->rx_bytes; \ data[i++] += sta->wep_weak_iv_count; \ data[i++] += sta->num_duplicates; \ data[i++] += sta->rx_fragments; \ data[i++] += sta->rx_dropped; \ \ data[i++] += sta->tx_packets; \ data[i++] += sta->tx_bytes; \ data[i++] += sta->tx_fragments; \ data[i++] += sta->tx_filtered_count; \ data[i++] += sta->tx_retry_failed; \ data[i++] += sta->tx_retry_count; \ data[i++] += sta->beacon_loss_count; \ } while (0) /* For Managed stations, find the single station based on BSSID * and use that. For interface types, iterate through all available * stations and add stats for any station that is assigned to this * network device. */ mutex_lock(&local->sta_mtx); if (sdata->vif.type == NL80211_IFTYPE_STATION) { sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); if (!(sta && !WARN_ON(sta->sdata->dev != dev))) goto do_survey; i = 0; ADD_STA_STATS(sta); data[i++] = sta->sta_state; sinfo.filled = 0; sta_set_sinfo(sta, &sinfo); if (sinfo.filled & STATION_INFO_TX_BITRATE) data[i] = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); i++; if (sinfo.filled & STATION_INFO_RX_BITRATE) data[i] = 100000 * cfg80211_calculate_bitrate(&sinfo.rxrate); i++; if (sinfo.filled & STATION_INFO_SIGNAL_AVG) data[i] = (u8)sinfo.signal_avg; i++; } else { list_for_each_entry(sta, &local->sta_list, list) { /* Make sure this station belongs to the proper dev */ if (sta->sdata->dev != dev) continue; i = 0; ADD_STA_STATS(sta); } } do_survey: i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; /* Get survey stats for current channel */ q = 0; while (true) { survey.filled = 0; if (drv_get_survey(local, q, &survey) != 0) { survey.filled = 0; break; } if (survey.channel && (local->oper_channel->center_freq == survey.channel->center_freq)) break; q++; } if (survey.filled) data[i++] = survey.channel->center_freq; else data[i++] = 0; if (survey.filled & SURVEY_INFO_NOISE_DBM) data[i++] = (u8)survey.noise; else data[i++] = -1LL; if (survey.filled & SURVEY_INFO_CHANNEL_TIME) data[i++] = survey.channel_time; else data[i++] = -1LL; if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) data[i++] = survey.channel_time_busy; else data[i++] = -1LL; if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) data[i++] = survey.channel_time_ext_busy; else data[i++] = -1LL; if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) data[i++] = survey.channel_time_rx; else data[i++] = -1LL; if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) data[i++] = survey.channel_time_tx; else data[i++] = -1LL; mutex_unlock(&local->sta_mtx); if (WARN_ON(i != STA_STATS_LEN)) return; drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); } static void ieee80211_get_et_strings(struct wiphy *wiphy, struct net_device *dev, u32 sset, u8 *data) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); int sz_sta_stats = 0; if (sset == ETH_SS_STATS) { sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats); } drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); } static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct sta_info *sta; int ret = -ENOENT; mutex_lock(&local->sta_mtx); sta = sta_info_get_by_idx(sdata, idx); if (sta) { ret = 0; memcpy(mac, sta->sta.addr, ETH_ALEN); sta_set_sinfo(sta, sinfo); } mutex_unlock(&local->sta_mtx); return ret; } static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, int idx, struct survey_info *survey) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); return drv_get_survey(local, idx, survey); } static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct sta_info *sta; int ret = -ENOENT; mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mac); if (sta) { ret = 0; sta_set_sinfo(sta, sinfo); } mutex_unlock(&local->sta_mtx); return ret; } static int ieee80211_set_channel(struct wiphy *wiphy, struct net_device *netdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = NULL; if (netdev) sdata = IEEE80211_DEV_TO_SUB_IF(netdev); switch (ieee80211_get_channel_mode(local, NULL)) { case CHAN_MODE_HOPPING: return -EBUSY; case CHAN_MODE_FIXED: if (local->oper_channel != chan || (!sdata && local->_oper_channel_type != channel_type)) return -EBUSY; if (!sdata && local->_oper_channel_type == channel_type) return 0; break; case CHAN_MODE_UNDEFINED: break; } if (!ieee80211_set_channel_type(local, sdata, channel_type)) return -EBUSY; local->oper_channel = chan; /* auto-detects changes */ ieee80211_hw_config(local, 0); return 0; } static int ieee80211_set_monitor_channel(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { return ieee80211_set_channel(wiphy, NULL, chan, channel_type); } static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, const u8 *resp, size_t resp_len) { struct probe_resp *new, *old; if (!resp || !resp_len) return 1; old = rtnl_dereference(sdata->u.ap.probe_resp); new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL); if (!new) return -ENOMEM; new->len = resp_len; memcpy(new->data, resp, resp_len); rcu_assign_pointer(sdata->u.ap.probe_resp, new); if (old) kfree_rcu(old, rcu_head); return 0; } static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, struct cfg80211_beacon_data *params) { struct beacon_data *new, *old; int new_head_len, new_tail_len; int size, err; u32 changed = BSS_CHANGED_BEACON; old = rtnl_dereference(sdata->u.ap.beacon); /* Need to have a beacon head if we don't have one yet */ if (!params->head && !old) return -EINVAL; /* new or old head? */ if (params->head) new_head_len = params->head_len; else new_head_len = old->head_len; /* new or old tail? */ if (params->tail || !old) /* params->tail_len will be zero for !params->tail */ new_tail_len = params->tail_len; else new_tail_len = old->tail_len; size = sizeof(*new) + new_head_len + new_tail_len; new = kzalloc(size, GFP_KERNEL); if (!new) return -ENOMEM; /* start filling the new info now */ /* * pointers go into the block we allocated, * memory is | beacon_data | head | tail | */ new->head = ((u8 *) new) + sizeof(*new); new->tail = new->head + new_head_len; new->head_len = new_head_len; new->tail_len = new_tail_len; /* copy in head */ if (params->head) memcpy(new->head, params->head, new_head_len); else memcpy(new->head, old->head, new_head_len); /* copy in optional tail */ if (params->tail) memcpy(new->tail, params->tail, new_tail_len); else if (old) memcpy(new->tail, old->tail, new_tail_len); err = ieee80211_set_probe_resp(sdata, params->probe_resp, params->probe_resp_len); if (err < 0) return err; if (err == 0) changed |= BSS_CHANGED_AP_PROBE_RESP; rcu_assign_pointer(sdata->u.ap.beacon, new); if (old) kfree_rcu(old, rcu_head); return changed; } static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct beacon_data *old; struct ieee80211_sub_if_data *vlan; u32 changed = BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | BSS_CHANGED_SSID; int err; old = rtnl_dereference(sdata->u.ap.beacon); if (old) return -EALREADY; err = ieee80211_set_channel(wiphy, dev, params->channel, params->channel_type); if (err) return err; /* * Apply control port protocol, this allows us to * not encrypt dynamic WEP control frames. */ sdata->control_port_protocol = params->crypto.control_port_ethertype; sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { vlan->control_port_protocol = params->crypto.control_port_ethertype; vlan->control_port_no_encrypt = params->crypto.control_port_no_encrypt; } sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->vif.bss_conf.dtim_period = params->dtim_period; sdata->vif.bss_conf.ssid_len = params->ssid_len; if (params->ssid_len) memcpy(sdata->vif.bss_conf.ssid, params->ssid, params->ssid_len); sdata->vif.bss_conf.hidden_ssid = (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); err = ieee80211_assign_beacon(sdata, ¶ms->beacon); if (err < 0) return err; changed |= err; ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(dev); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) netif_carrier_on(vlan->dev); return 0; } static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_beacon_data *params) { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; int err; sdata = IEEE80211_DEV_TO_SUB_IF(dev); old = rtnl_dereference(sdata->u.ap.beacon); if (!old) return -ENOENT; err = ieee80211_assign_beacon(sdata, params); if (err < 0) return err; ieee80211_bss_info_change_notify(sdata, err); return 0; } static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata, *vlan; struct beacon_data *old; sdata = IEEE80211_DEV_TO_SUB_IF(dev); old = rtnl_dereference(sdata->u.ap.beacon); if (!old) return -ENOENT; list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) netif_carrier_off(vlan->dev); netif_carrier_off(dev); RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); kfree_rcu(old, rcu_head); sta_info_flush(sdata->local, sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); return 0; } /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ struct iapp_layer2_update { u8 da[ETH_ALEN]; /* broadcast */ u8 sa[ETH_ALEN]; /* STA addr */ __be16 len; /* 6 */ u8 dsap; /* 0 */ u8 ssap; /* 0 */ u8 control; u8 xid_info[3]; } __packed; static void ieee80211_send_layer2_update(struct sta_info *sta) { struct iapp_layer2_update *msg; struct sk_buff *skb; /* Send Level 2 Update Frame to update forwarding tables in layer 2 * bridge devices */ skb = dev_alloc_skb(sizeof(*msg)); if (!skb) return; msg = (struct iapp_layer2_update *)skb_put(skb, sizeof(*msg)); /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ eth_broadcast_addr(msg->da); memcpy(msg->sa, sta->sta.addr, ETH_ALEN); msg->len = htons(6); msg->dsap = 0; msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */ msg->control = 0xaf; /* XID response lsb.1111F101. * F=0 (no poll command; unsolicited frame) */ msg->xid_info[0] = 0x81; /* XID format identifier */ msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ skb->dev = sta->sdata->dev; skb->protocol = eth_type_trans(skb, sta->sdata->dev); memset(skb->cb, 0, sizeof(skb->cb)); netif_rx_ni(skb); } static int sta_apply_parameters(struct ieee80211_local *local, struct sta_info *sta, struct station_parameters *params) { int ret = 0; u32 rates; int i, j; struct ieee80211_supported_band *sband; struct ieee80211_sub_if_data *sdata = sta->sdata; u32 mask, set; sband = local->hw.wiphy->bands[local->oper_channel->band]; mask = params->sta_flags_mask; set = params->sta_flags_set; /* * In mesh mode, we can clear AUTHENTICATED flag but must * also make ASSOCIATED follow appropriately for the driver * API. See also below, after AUTHORIZED changes. */ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { /* cfg80211 should not allow this in non-mesh modes */ if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) return -EINVAL; if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && !test_sta_flag(sta, WLAN_STA_AUTH)) { ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); if (ret) return ret; ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (ret) return ret; } } if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (ret) return ret; } if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { /* cfg80211 should not allow this in non-mesh modes */ if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) return -EINVAL; if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && test_sta_flag(sta, WLAN_STA_AUTH)) { ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); if (ret) return ret; ret = sta_info_move_state(sta, IEEE80211_STA_NONE); if (ret) return ret; } } if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); else clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); } if (mask & BIT(NL80211_STA_FLAG_WME)) { if (set & BIT(NL80211_STA_FLAG_WME)) { set_sta_flag(sta, WLAN_STA_WME); sta->sta.wme = true; } else { clear_sta_flag(sta, WLAN_STA_WME); sta->sta.wme = false; } } if (mask & BIT(NL80211_STA_FLAG_MFP)) { if (set & BIT(NL80211_STA_FLAG_MFP)) set_sta_flag(sta, WLAN_STA_MFP); else clear_sta_flag(sta, WLAN_STA_MFP); } if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) set_sta_flag(sta, WLAN_STA_TDLS_PEER); else clear_sta_flag(sta, WLAN_STA_TDLS_PEER); } if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { sta->sta.uapsd_queues = params->uapsd_queues; sta->sta.max_sp = params->max_sp; } /* * cfg80211 validates this (1-2007) and allows setting the AID * only when creating a new station entry */ if (params->aid) sta->sta.aid = params->aid; /* * FIXME: updating the following information is racy when this * function is called from ieee80211_change_station(). * However, all this information should be static so * maybe we should just reject attemps to change it. */ if (params->listen_interval >= 0) sta->listen_interval = params->listen_interval; if (params->supported_rates) { rates = 0; for (i = 0; i < params->supported_rates_len; i++) { int rate = (params->supported_rates[i] & 0x7f) * 5; for (j = 0; j < sband->n_bitrates; j++) { if (sband->bitrates[j].bitrate == rate) rates |= BIT(j); } } sta->sta.supp_rates[local->oper_channel->band] = rates; } if (params->ht_capa) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, params->ht_capa, &sta->sta.ht_cap); if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) switch (params->plink_state) { case NL80211_PLINK_LISTEN: case NL80211_PLINK_ESTAB: case NL80211_PLINK_BLOCKED: sta->plink_state = params->plink_state; break; default: /* nothing */ break; } else switch (params->plink_action) { case PLINK_ACTION_OPEN: mesh_plink_open(sta); break; case PLINK_ACTION_BLOCK: mesh_plink_block(sta); break; } #endif } return 0; } static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params) { struct ieee80211_local *local = wiphy_priv(wiphy); struct sta_info *sta; struct ieee80211_sub_if_data *sdata; int err; int layer2_update; if (params->vlan) { sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP) return -EINVAL; } else sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (ether_addr_equal(mac, sdata->vif.addr)) return -EINVAL; if (is_multicast_ether_addr(mac)) return -EINVAL; sta = sta_info_alloc(sdata, mac, GFP_KERNEL); if (!sta) return -ENOMEM; sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); err = sta_apply_parameters(local, sta, params); if (err) { sta_info_free(local, sta); return err; } /* * for TDLS, rate control should be initialized only when supported * rates are known. */ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) rate_control_rate_init(sta); layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sdata->vif.type == NL80211_IFTYPE_AP; err = sta_info_insert_rcu(sta); if (err) { rcu_read_unlock(); return err; } if (layer2_update) ieee80211_send_layer2_update(sta); rcu_read_unlock(); return 0; } static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (mac) return sta_info_destroy_addr_bss(sdata, mac); sta_info_flush(local, sdata); return 0; } static int ieee80211_change_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wiphy_priv(wiphy); struct sta_info *sta; struct ieee80211_sub_if_data *vlansdata; int err; mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mac); if (!sta) { mutex_unlock(&local->sta_mtx); return -ENOENT; } /* in station mode, supported rates are only valid with TDLS */ if (sdata->vif.type == NL80211_IFTYPE_STATION && params->supported_rates && !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { mutex_unlock(&local->sta_mtx); return -EINVAL; } if (params->vlan && params->vlan != sta->sdata->dev) { bool prev_4addr = false; bool new_4addr = false; vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN && vlansdata->vif.type != NL80211_IFTYPE_AP) { mutex_unlock(&local->sta_mtx); return -EINVAL; } if (params->vlan->ieee80211_ptr->use_4addr) { if (vlansdata->u.vlan.sta) { mutex_unlock(&local->sta_mtx); return -EBUSY; } rcu_assign_pointer(vlansdata->u.vlan.sta, sta); new_4addr = true; } if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sta->sdata->u.vlan.sta) { rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL); prev_4addr = true; } sta->sdata = vlansdata; if (sta->sta_state == IEEE80211_STA_AUTHORIZED && prev_4addr != new_4addr) { if (new_4addr) atomic_dec(&sta->sdata->bss->num_mcast_sta); else atomic_inc(&sta->sdata->bss->num_mcast_sta); } ieee80211_send_layer2_update(sta); } err = sta_apply_parameters(local, sta, params); if (err) { mutex_unlock(&local->sta_mtx); return err; } if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates) rate_control_rate_init(sta); mutex_unlock(&local->sta_mtx); if (sdata->vif.type == NL80211_IFTYPE_STATION && params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { ieee80211_recalc_ps(local, -1); ieee80211_recalc_ps_vif(sdata); } return 0; } #ifdef CONFIG_MAC80211_MESH static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, u8 *dst, u8 *next_hop) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; struct sta_info *sta; int err; sdata = IEEE80211_DEV_TO_SUB_IF(dev); rcu_read_lock(); sta = sta_info_get(sdata, next_hop); if (!sta) { rcu_read_unlock(); return -ENOENT; } err = mesh_path_add(dst, sdata); if (err) { rcu_read_unlock(); return err; } mpath = mesh_path_lookup(dst, sdata); if (!mpath) { rcu_read_unlock(); return -ENXIO; } mesh_path_fix_nexthop(mpath, sta); rcu_read_unlock(); return 0; } static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, u8 *dst) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (dst) return mesh_path_del(dst, sdata); mesh_path_flush_by_iface(sdata); return 0; } static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev, u8 *dst, u8 *next_hop) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; struct sta_info *sta; sdata = IEEE80211_DEV_TO_SUB_IF(dev); rcu_read_lock(); sta = sta_info_get(sdata, next_hop); if (!sta) { rcu_read_unlock(); return -ENOENT; } mpath = mesh_path_lookup(dst, sdata); if (!mpath) { rcu_read_unlock(); return -ENOENT; } mesh_path_fix_nexthop(mpath, sta); rcu_read_unlock(); return 0; } static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, struct mpath_info *pinfo) { struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop); if (next_hop_sta) memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN); else memset(next_hop, 0, ETH_ALEN); memset(pinfo, 0, sizeof(*pinfo)); pinfo->generation = mesh_paths_generation; pinfo->filled = MPATH_INFO_FRAME_QLEN | MPATH_INFO_SN | MPATH_INFO_METRIC | MPATH_INFO_EXPTIME | MPATH_INFO_DISCOVERY_TIMEOUT | MPATH_INFO_DISCOVERY_RETRIES | MPATH_INFO_FLAGS; pinfo->frame_qlen = mpath->frame_queue.qlen; pinfo->sn = mpath->sn; pinfo->metric = mpath->metric; if (time_before(jiffies, mpath->exp_time)) pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies); pinfo->discovery_timeout = jiffies_to_msecs(mpath->discovery_timeout); pinfo->discovery_retries = mpath->discovery_retries; if (mpath->flags & MESH_PATH_ACTIVE) pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE; if (mpath->flags & MESH_PATH_RESOLVING) pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING; if (mpath->flags & MESH_PATH_SN_VALID) pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID; if (mpath->flags & MESH_PATH_FIXED) pinfo->flags |= NL80211_MPATH_FLAG_FIXED; if (mpath->flags & MESH_PATH_RESOLVED) pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED; } static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, u8 *dst, u8 *next_hop, struct mpath_info *pinfo) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; sdata = IEEE80211_DEV_TO_SUB_IF(dev); rcu_read_lock(); mpath = mesh_path_lookup(dst, sdata); if (!mpath) { rcu_read_unlock(); return -ENOENT; } memcpy(dst, mpath->dst, ETH_ALEN); mpath_set_pinfo(mpath, next_hop, pinfo); rcu_read_unlock(); return 0; } static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *dst, u8 *next_hop, struct mpath_info *pinfo) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; sdata = IEEE80211_DEV_TO_SUB_IF(dev); rcu_read_lock(); mpath = mesh_path_lookup_by_idx(idx, sdata); if (!mpath) { rcu_read_unlock(); return -ENOENT; } memcpy(dst, mpath->dst, ETH_ALEN); mpath_set_pinfo(mpath, next_hop, pinfo); rcu_read_unlock(); return 0; } static int ieee80211_get_mesh_config(struct wiphy *wiphy, struct net_device *dev, struct mesh_config *conf) { struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config)); return 0; } static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask) { return (mask >> (parm-1)) & 0x1; } static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, const struct mesh_setup *setup) { u8 *new_ie; const u8 *old_ie; struct ieee80211_sub_if_data *sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh); /* allocate information elements */ new_ie = NULL; old_ie = ifmsh->ie; if (setup->ie_len) { new_ie = kmemdup(setup->ie, setup->ie_len, GFP_KERNEL); if (!new_ie) return -ENOMEM; } ifmsh->ie_len = setup->ie_len; ifmsh->ie = new_ie; kfree(old_ie); /* now copy the rest of the setup parameters */ ifmsh->mesh_id_len = setup->mesh_id_len; memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); ifmsh->mesh_sp_id = setup->sync_method; ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; ifmsh->security = IEEE80211_MESH_SEC_NONE; if (setup->is_authenticated) ifmsh->security |= IEEE80211_MESH_SEC_AUTHED; if (setup->is_secure) ifmsh->security |= IEEE80211_MESH_SEC_SECURED; /* mcast rate setting in Mesh Node */ memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, sizeof(setup->mcast_rate)); return 0; } static int ieee80211_update_mesh_config(struct wiphy *wiphy, struct net_device *dev, u32 mask, const struct mesh_config *nconf) { struct mesh_config *conf; struct ieee80211_sub_if_data *sdata; struct ieee80211_if_mesh *ifmsh; sdata = IEEE80211_DEV_TO_SUB_IF(dev); ifmsh = &sdata->u.mesh; /* Set the config options which we are interested in setting */ conf = &(sdata->u.mesh.mshcfg); if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask)) conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout; if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask)) conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout; if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask)) conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout; if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask)) conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks; if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask)) conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries; if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask)) conf->dot11MeshTTL = nconf->dot11MeshTTL; if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask)) conf->element_ttl = nconf->element_ttl; if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) conf->auto_open_plinks = nconf->auto_open_plinks; if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask)) conf->dot11MeshNbrOffsetMaxNeighbor = nconf->dot11MeshNbrOffsetMaxNeighbor; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask)) conf->dot11MeshHWMPmaxPREQretries = nconf->dot11MeshHWMPmaxPREQretries; if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask)) conf->path_refresh_time = nconf->path_refresh_time; if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask)) conf->min_discovery_timeout = nconf->min_discovery_timeout; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask)) conf->dot11MeshHWMPactivePathTimeout = nconf->dot11MeshHWMPactivePathTimeout; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask)) conf->dot11MeshHWMPpreqMinInterval = nconf->dot11MeshHWMPpreqMinInterval; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask)) conf->dot11MeshHWMPperrMinInterval = nconf->dot11MeshHWMPperrMinInterval; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, mask)) conf->dot11MeshHWMPnetDiameterTraversalTime = nconf->dot11MeshHWMPnetDiameterTraversalTime; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOTMODE, mask)) { conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode; ieee80211_mesh_root_setup(ifmsh); } if (_chg_mesh_attr(NL80211_MESHCONF_GATE_ANNOUNCEMENTS, mask)) { /* our current gate announcement implementation rides on root * announcements, so require this ifmsh to also be a root node * */ if (nconf->dot11MeshGateAnnouncementProtocol && !(conf->dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)) { conf->dot11MeshHWMPRootMode = IEEE80211_PROACTIVE_RANN; ieee80211_mesh_root_setup(ifmsh); } conf->dot11MeshGateAnnouncementProtocol = nconf->dot11MeshGateAnnouncementProtocol; } if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask)) conf->dot11MeshHWMPRannInterval = nconf->dot11MeshHWMPRannInterval; if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) conf->dot11MeshForwarding = nconf->dot11MeshForwarding; if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) { /* our RSSI threshold implementation is supported only for * devices that report signal in dBm. */ if (!(sdata->local->hw.flags & IEEE80211_HW_SIGNAL_DBM)) return -ENOTSUPP; conf->rssi_threshold = nconf->rssi_threshold; } if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) { conf->ht_opmode = nconf->ht_opmode; sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); } if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, mask)) conf->dot11MeshHWMPactivePathToRootTimeout = nconf->dot11MeshHWMPactivePathToRootTimeout; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOT_INTERVAL, mask)) conf->dot11MeshHWMProotInterval = nconf->dot11MeshHWMProotInterval; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask)) conf->dot11MeshHWMPconfirmationInterval = nconf->dot11MeshHWMPconfirmationInterval; return 0; } static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, const struct mesh_config *conf, const struct mesh_setup *setup) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; int err; memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config)); err = copy_mesh_setup(ifmsh, setup); if (err) return err; err = ieee80211_set_channel(wiphy, dev, setup->channel, setup->channel_type); if (err) return err; ieee80211_start_mesh(sdata); return 0; } static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ieee80211_stop_mesh(sdata); return 0; } #endif static int ieee80211_change_bss(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params) { struct ieee80211_sub_if_data *sdata; u32 changed = 0; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (params->use_cts_prot >= 0) { sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; changed |= BSS_CHANGED_ERP_CTS_PROT; } if (params->use_short_preamble >= 0) { sdata->vif.bss_conf.use_short_preamble = params->use_short_preamble; changed |= BSS_CHANGED_ERP_PREAMBLE; } if (!sdata->vif.bss_conf.use_short_slot && sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) { sdata->vif.bss_conf.use_short_slot = true; changed |= BSS_CHANGED_ERP_SLOT; } if (params->use_short_slot_time >= 0) { sdata->vif.bss_conf.use_short_slot = params->use_short_slot_time; changed |= BSS_CHANGED_ERP_SLOT; } if (params->basic_rates) { int i, j; u32 rates = 0; struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_supported_band *sband = wiphy->bands[local->oper_channel->band]; for (i = 0; i < params->basic_rates_len; i++) { int rate = (params->basic_rates[i] & 0x7f) * 5; for (j = 0; j < sband->n_bitrates; j++) { if (sband->bitrates[j].bitrate == rate) rates |= BIT(j); } } sdata->vif.bss_conf.basic_rates = rates; changed |= BSS_CHANGED_BASIC_RATES; } if (params->ap_isolate >= 0) { if (params->ap_isolate) sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS; else sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS; } if (params->ht_opmode >= 0) { sdata->vif.bss_conf.ht_operation_mode = (u16) params->ht_opmode; changed |= BSS_CHANGED_HT; } ieee80211_bss_info_change_notify(sdata, changed); return 0; } static int ieee80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_txq_params *params) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_tx_queue_params p; if (!local->ops->conf_tx) return -EOPNOTSUPP; if (local->hw.queues < IEEE80211_NUM_ACS) return -EOPNOTSUPP; memset(&p, 0, sizeof(p)); p.aifs = params->aifs; p.cw_max = params->cwmax; p.cw_min = params->cwmin; p.txop = params->txop; /* * Setting tx queue params disables u-apsd because it's only * called in master mode. */ p.uapsd = false; sdata->tx_conf[params->ac] = p; if (drv_conf_tx(local, sdata, params->ac, &p)) { wiphy_debug(local->hw.wiphy, "failed to set TX queue parameters for AC %d\n", params->ac); return -EINVAL; } ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); return 0; } #ifdef CONFIG_PM static int ieee80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) { return __ieee80211_suspend(wiphy_priv(wiphy), wowlan); } static int ieee80211_resume(struct wiphy *wiphy) { return __ieee80211_resume(wiphy_priv(wiphy)); } #else #define ieee80211_suspend NULL #define ieee80211_resume NULL #endif static int ieee80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *req) { struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev); switch (ieee80211_vif_type_p2p(&sdata->vif)) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_P2P_GO: if (sdata->local->ops->hw_scan) break; /* * FIXME: implement NoA while scanning in software, * for now fall through to allow scanning only when * beaconing hasn't been configured yet */ case NL80211_IFTYPE_AP: if (sdata->u.ap.beacon) return -EOPNOTSUPP; break; default: return -EOPNOTSUPP; } return ieee80211_request_scan(sdata, req); } static int ieee80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *req) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (!sdata->local->ops->sched_scan_start) return -EOPNOTSUPP; return ieee80211_request_sched_scan_start(sdata, req); } static int ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (!sdata->local->ops->sched_scan_stop) return -EOPNOTSUPP; return ieee80211_request_sched_scan_stop(sdata); } static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { return ieee80211_mgd_auth(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); switch (ieee80211_get_channel_mode(local, sdata)) { case CHAN_MODE_HOPPING: return -EBUSY; case CHAN_MODE_FIXED: if (local->oper_channel == req->bss->channel) break; return -EBUSY; case CHAN_MODE_UNDEFINED: break; } return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_deauth_request *req) { return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req) { return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); switch (ieee80211_get_channel_mode(local, sdata)) { case CHAN_MODE_HOPPING: return -EBUSY; case CHAN_MODE_FIXED: if (!params->channel_fixed) return -EBUSY; if (local->oper_channel == params->channel) break; return -EBUSY; case CHAN_MODE_UNDEFINED: break; } return ieee80211_ibss_join(sdata, params); } static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); return ieee80211_ibss_leave(sdata); } static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct ieee80211_local *local = wiphy_priv(wiphy); int err; if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { err = drv_set_frag_threshold(local, wiphy->frag_threshold); if (err) return err; } if (changed & WIPHY_PARAM_COVERAGE_CLASS) { err = drv_set_coverage_class(local, wiphy->coverage_class); if (err) return err; } if (changed & WIPHY_PARAM_RTS_THRESHOLD) { err = drv_set_rts_threshold(local, wiphy->rts_threshold); if (err) return err; } if (changed & WIPHY_PARAM_RETRY_SHORT) local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; if (changed & WIPHY_PARAM_RETRY_LONG) local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; if (changed & (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG)) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS); return 0; } static int ieee80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_channel *chan = local->oper_channel; u32 changes = 0; switch (type) { case NL80211_TX_POWER_AUTOMATIC: local->user_power_level = -1; break; case NL80211_TX_POWER_LIMITED: if (mbm < 0 || (mbm % 100)) return -EOPNOTSUPP; local->user_power_level = MBM_TO_DBM(mbm); break; case NL80211_TX_POWER_FIXED: if (mbm < 0 || (mbm % 100)) return -EOPNOTSUPP; /* TODO: move to cfg80211 when it knows the channel */ if (MBM_TO_DBM(mbm) > chan->max_power) return -EINVAL; local->user_power_level = MBM_TO_DBM(mbm); break; } ieee80211_hw_config(local, changes); return 0; } static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) { struct ieee80211_local *local = wiphy_priv(wiphy); *dbm = local->hw.conf.power_level; return 0; } static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, const u8 *addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); memcpy(&sdata->u.wds.remote_addr, addr, ETH_ALEN); return 0; } static void ieee80211_rfkill_poll(struct wiphy *wiphy) { struct ieee80211_local *local = wiphy_priv(wiphy); drv_rfkill_poll(local); } #ifdef CONFIG_NL80211_TESTMODE static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) { struct ieee80211_local *local = wiphy_priv(wiphy); if (!local->ops->testmode_cmd) return -EOPNOTSUPP; return local->ops->testmode_cmd(&local->hw, data, len); } static int ieee80211_testmode_dump(struct wiphy *wiphy, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len) { struct ieee80211_local *local = wiphy_priv(wiphy); if (!local->ops->testmode_dump) return -EOPNOTSUPP; return local->ops->testmode_dump(&local->hw, skb, cb, data, len); } #endif int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode) { const u8 *ap; enum ieee80211_smps_mode old_req; int err; lockdep_assert_held(&sdata->u.mgd.mtx); old_req = sdata->u.mgd.req_smps; sdata->u.mgd.req_smps = smps_mode; if (old_req == smps_mode && smps_mode != IEEE80211_SMPS_AUTOMATIC) return 0; /* * If not associated, or current association is not an HT * association, there's no need to send an action frame. */ if (!sdata->u.mgd.associated || sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_smps(sdata->local); mutex_unlock(&sdata->local->iflist_mtx); return 0; } ap = sdata->u.mgd.associated->bssid; if (smps_mode == IEEE80211_SMPS_AUTOMATIC) { if (sdata->u.mgd.powersave) smps_mode = IEEE80211_SMPS_DYNAMIC; else smps_mode = IEEE80211_SMPS_OFF; } /* send SM PS frame to AP */ err = ieee80211_send_smps_action(sdata, smps_mode, ap, ap); if (err) sdata->u.mgd.req_smps = old_req; return err; } static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) return -EOPNOTSUPP; if (enabled == sdata->u.mgd.powersave && timeout == local->dynamic_ps_forced_timeout) return 0; sdata->u.mgd.powersave = enabled; local->dynamic_ps_forced_timeout = timeout; /* no change, but if automatic follow powersave */ mutex_lock(&sdata->u.mgd.mtx); __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); mutex_unlock(&sdata->u.mgd.mtx); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_recalc_ps(local, -1); ieee80211_recalc_ps_vif(sdata); return 0; } static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_vif *vif = &sdata->vif; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; if (rssi_thold == bss_conf->cqm_rssi_thold && rssi_hyst == bss_conf->cqm_rssi_hyst) return 0; bss_conf->cqm_rssi_thold = rssi_thold; bss_conf->cqm_rssi_hyst = rssi_hyst; /* tell the driver upon association, unless already associated */ if (sdata->u.mgd.associated && sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); return 0; } static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, const u8 *addr, const struct cfg80211_bitrate_mask *mask) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); int i, ret; if (!ieee80211_sdata_running(sdata)) return -ENETDOWN; if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) { ret = drv_set_bitrate_mask(local, sdata, mask); if (ret) return ret; } for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sdata->rc_rateidx_mask[i] = mask->control[i].legacy; memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs, sizeof(mask->control[i].mcs)); } return 0; } static int ieee80211_start_roc_work(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie, struct sk_buff *txskb) { struct ieee80211_roc_work *roc, *tmp; bool queued = false; int ret; lockdep_assert_held(&local->mtx); roc = kzalloc(sizeof(*roc), GFP_KERNEL); if (!roc) return -ENOMEM; roc->chan = channel; roc->chan_type = channel_type; roc->duration = duration; roc->req_duration = duration; roc->frame = txskb; roc->mgmt_tx_cookie = (unsigned long)txskb; roc->sdata = sdata; INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work); INIT_LIST_HEAD(&roc->dependents); /* if there's one pending or we're scanning, queue this one */ if (!list_empty(&local->roc_list) || local->scanning) goto out_check_combine; /* if not HW assist, just queue & schedule work */ if (!local->ops->remain_on_channel) { ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); goto out_queue; } /* otherwise actually kick it off here (for error handling) */ /* * If the duration is zero, then the driver * wouldn't actually do anything. Set it to * 10 for now. * * TODO: cancel the off-channel operation * when we get the SKB's TX status and * the wait time was zero before. */ if (!duration) duration = 10; ret = drv_remain_on_channel(local, channel, channel_type, duration); if (ret) { kfree(roc); return ret; } roc->started = true; goto out_queue; out_check_combine: list_for_each_entry(tmp, &local->roc_list, list) { if (tmp->chan != channel || tmp->chan_type != channel_type) continue; /* * Extend this ROC if possible: * * If it hasn't started yet, just increase the duration * and add the new one to the list of dependents. */ if (!tmp->started) { list_add_tail(&roc->list, &tmp->dependents); tmp->duration = max(tmp->duration, roc->duration); queued = true; break; } /* If it has already started, it's more difficult ... */ if (local->ops->remain_on_channel) { unsigned long j = jiffies; /* * In the offloaded ROC case, if it hasn't begun, add * this new one to the dependent list to be handled * when the the master one begins. If it has begun, * check that there's still a minimum time left and * if so, start this one, transmitting the frame, but * add it to the list directly after this one with a * a reduced time so we'll ask the driver to execute * it right after finishing the previous one, in the * hope that it'll also be executed right afterwards, * effectively extending the old one. * If there's no minimum time left, just add it to the * normal list. */ if (!tmp->hw_begun) { list_add_tail(&roc->list, &tmp->dependents); queued = true; break; } if (time_before(j + IEEE80211_ROC_MIN_LEFT, tmp->hw_start_time + msecs_to_jiffies(tmp->duration))) { int new_dur; ieee80211_handle_roc_started(roc); new_dur = roc->duration - jiffies_to_msecs(tmp->hw_start_time + msecs_to_jiffies( tmp->duration) - j); if (new_dur > 0) { /* add right after tmp */ list_add(&roc->list, &tmp->list); } else { list_add_tail(&roc->list, &tmp->dependents); } queued = true; } } else if (del_timer_sync(&tmp->work.timer)) { unsigned long new_end; /* * In the software ROC case, cancel the timer, if * that fails then the finish work is already * queued/pending and thus we queue the new ROC * normally, if that succeeds then we can extend * the timer duration and TX the frame (if any.) */ list_add_tail(&roc->list, &tmp->dependents); queued = true; new_end = jiffies + msecs_to_jiffies(roc->duration); /* ok, it was started & we canceled timer */ if (time_after(new_end, tmp->work.timer.expires)) mod_timer(&tmp->work.timer, new_end); else add_timer(&tmp->work.timer); ieee80211_handle_roc_started(roc); } break; } out_queue: if (!queued) list_add_tail(&roc->list, &local->roc_list); /* * cookie is either the roc (for normal roc) * or the SKB (for mgmt TX) */ if (txskb) *cookie = (unsigned long)txskb; else *cookie = (unsigned long)roc; return 0; } static int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; int ret; mutex_lock(&local->mtx); ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, duration, cookie, NULL); mutex_unlock(&local->mtx); return ret; } static int ieee80211_cancel_roc(struct ieee80211_local *local, u64 cookie, bool mgmt_tx) { struct ieee80211_roc_work *roc, *tmp, *found = NULL; int ret; mutex_lock(&local->mtx); list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { struct ieee80211_roc_work *dep, *tmp2; list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) { if (!mgmt_tx && (unsigned long)dep != cookie) continue; else if (mgmt_tx && dep->mgmt_tx_cookie != cookie) continue; /* found dependent item -- just remove it */ list_del(&dep->list); mutex_unlock(&local->mtx); ieee80211_roc_notify_destroy(dep); return 0; } if (!mgmt_tx && (unsigned long)roc != cookie) continue; else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) continue; found = roc; break; } if (!found) { mutex_unlock(&local->mtx); return -ENOENT; } /* * We found the item to cancel, so do that. Note that it * may have dependents, which we also cancel (and send * the expired signal for.) Not doing so would be quite * tricky here, but we may need to fix it later. */ if (local->ops->remain_on_channel) { if (found->started) { ret = drv_cancel_remain_on_channel(local); if (WARN_ON_ONCE(ret)) { mutex_unlock(&local->mtx); return ret; } } list_del(&found->list); if (found->started) ieee80211_start_next_roc(local); mutex_unlock(&local->mtx); ieee80211_roc_notify_destroy(found); } else { /* work may be pending so use it all the time */ found->abort = true; ieee80211_queue_delayed_work(&local->hw, &found->work, 0); mutex_unlock(&local->mtx); /* work will clean up etc */ flush_delayed_work(&found->work); } return 0; } static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; return ieee80211_cancel_roc(local, cookie, false); } static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct sta_info *sta; const struct ieee80211_mgmt *mgmt = (void *)buf; bool need_offchan = false; u32 flags; int ret; if (dont_wait_for_ack) flags = IEEE80211_TX_CTL_NO_ACK; else flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | IEEE80211_TX_CTL_REQ_TX_STATUS; if (no_cck) flags |= IEEE80211_TX_CTL_NO_CCK_RATE; switch (sdata->vif.type) { case NL80211_IFTYPE_ADHOC: if (!sdata->vif.bss_conf.ibss_joined) need_offchan = true; /* fall through */ #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: if (ieee80211_vif_is_mesh(&sdata->vif) && !sdata->u.mesh.mesh_id_len) need_offchan = true; /* fall through */ #endif case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: if (sdata->vif.type != NL80211_IFTYPE_ADHOC && !ieee80211_vif_is_mesh(&sdata->vif) && !rcu_access_pointer(sdata->bss->beacon)) need_offchan = true; if (!ieee80211_is_action(mgmt->frame_control) || mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) break; rcu_read_lock(); sta = sta_info_get(sdata, mgmt->da); rcu_read_unlock(); if (!sta) return -ENOLINK; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (!sdata->u.mgd.associated) need_offchan = true; break; case NL80211_IFTYPE_P2P_DEVICE: need_offchan = true; break; default: return -EOPNOTSUPP; } mutex_lock(&local->mtx); /* Check if the operating channel is the requested channel */ if (!need_offchan) { need_offchan = chan != local->oper_channel; if (channel_type_valid && channel_type != local->_oper_channel_type) need_offchan = true; } if (need_offchan && !offchan) { ret = -EBUSY; goto out_unlock; } skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); if (!skb) { ret = -ENOMEM; goto out_unlock; } skb_reserve(skb, local->hw.extra_tx_headroom); memcpy(skb_put(skb, len), buf, len); IEEE80211_SKB_CB(skb)->flags = flags; skb->dev = sdata->dev; if (!need_offchan) { *cookie = (unsigned long) skb; ieee80211_tx_skb(sdata, skb); ret = 0; goto out_unlock; } IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) IEEE80211_SKB_CB(skb)->hw_queue = local->hw.offchannel_tx_hw_queue; /* This will handle all kinds of coalescing and immediate TX */ ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, wait, cookie, skb); if (ret) kfree_skb(skb); out_unlock: mutex_unlock(&local->mtx); return ret; } static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie) { struct ieee80211_local *local = wiphy_priv(wiphy); return ieee80211_cancel_roc(local, cookie, true); } static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); switch (frame_type) { case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH: if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; if (reg) ifibss->auth_frame_registrations++; else ifibss->auth_frame_registrations--; } break; case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ: if (reg) local->probe_req_reg++; else local->probe_req_reg--; ieee80211_queue_work(&local->hw, &local->reconfig_filter); break; default: break; } } static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) { struct ieee80211_local *local = wiphy_priv(wiphy); if (local->started) return -EOPNOTSUPP; return drv_set_antenna(local, tx_ant, rx_ant); } static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) { struct ieee80211_local *local = wiphy_priv(wiphy); return drv_get_antenna(local, tx_ant, rx_ant); } static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx) { struct ieee80211_local *local = wiphy_priv(wiphy); return drv_set_ringparam(local, tx, rx); } static void ieee80211_get_ringparam(struct wiphy *wiphy, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) { struct ieee80211_local *local = wiphy_priv(wiphy); drv_get_ringparam(local, tx, tx_max, rx, rx_max); } static int ieee80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (!local->ops->set_rekey_data) return -EOPNOTSUPP; drv_set_rekey_data(local, sdata, data); return 0; } static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) { u8 *pos = (void *)skb_put(skb, 7); *pos++ = WLAN_EID_EXT_CAPABILITY; *pos++ = 5; /* len */ *pos++ = 0x0; *pos++ = 0x0; *pos++ = 0x0; *pos++ = 0x0; *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; } static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; u16 capab; capab = 0; if (local->oper_channel->band != IEEE80211_BAND_2GHZ) return capab; if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; return capab; } static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) { struct ieee80211_tdls_lnkie *lnkid; lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); lnkid->ie_type = WLAN_EID_LINK_ID; lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; memcpy(lnkid->bssid, bssid, ETH_ALEN); memcpy(lnkid->init_sta, src_addr, ETH_ALEN); memcpy(lnkid->resp_sta, peer, ETH_ALEN); } static int ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_tdls_data *tf; tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); memcpy(tf->da, peer, ETH_ALEN); memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); tf->ether_type = cpu_to_be16(ETH_P_TDLS); tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: tf->category = WLAN_CATEGORY_TDLS; tf->action_code = WLAN_TDLS_SETUP_REQUEST; skb_put(skb, sizeof(tf->u.setup_req)); tf->u.setup_req.dialog_token = dialog_token; tf->u.setup_req.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); ieee80211_add_srates_ie(sdata, skb, false, local->oper_channel->band); ieee80211_add_ext_srates_ie(sdata, skb, false, local->oper_channel->band); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_RESPONSE: tf->category = WLAN_CATEGORY_TDLS; tf->action_code = WLAN_TDLS_SETUP_RESPONSE; skb_put(skb, sizeof(tf->u.setup_resp)); tf->u.setup_resp.status_code = cpu_to_le16(status_code); tf->u.setup_resp.dialog_token = dialog_token; tf->u.setup_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); ieee80211_add_srates_ie(sdata, skb, false, local->oper_channel->band); ieee80211_add_ext_srates_ie(sdata, skb, false, local->oper_channel->band); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_CONFIRM: tf->category = WLAN_CATEGORY_TDLS; tf->action_code = WLAN_TDLS_SETUP_CONFIRM; skb_put(skb, sizeof(tf->u.setup_cfm)); tf->u.setup_cfm.status_code = cpu_to_le16(status_code); tf->u.setup_cfm.dialog_token = dialog_token; break; case WLAN_TDLS_TEARDOWN: tf->category = WLAN_CATEGORY_TDLS; tf->action_code = WLAN_TDLS_TEARDOWN; skb_put(skb, sizeof(tf->u.teardown)); tf->u.teardown.reason_code = cpu_to_le16(status_code); break; case WLAN_TDLS_DISCOVERY_REQUEST: tf->category = WLAN_CATEGORY_TDLS; tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; skb_put(skb, sizeof(tf->u.discover_req)); tf->u.discover_req.dialog_token = dialog_token; break; default: return -EINVAL; } return 0; } static int ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; mgmt = (void *)skb_put(skb, 24); memset(mgmt, 0, 24); memcpy(mgmt->da, peer, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); switch (action_code) { case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; mgmt->u.action.u.tdls_discover_resp.action_code = WLAN_PUB_ACTION_TDLS_DISCOVER_RES; mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token; mgmt->u.action.u.tdls_discover_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); ieee80211_add_srates_ie(sdata, skb, false, local->oper_channel->band); ieee80211_add_ext_srates_ie(sdata, skb, false, local->oper_channel->band); ieee80211_tdls_add_ext_capab(skb); break; default: return -EINVAL; } return 0; } static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *extra_ies, size_t extra_ies_len) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info; struct sk_buff *skb = NULL; bool send_direct; int ret; if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) return -ENOTSUPP; /* make sure we are in managed mode, and associated */ if (sdata->vif.type != NL80211_IFTYPE_STATION || !sdata->u.mgd.associated) return -EINVAL; tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", action_code, peer); skb = dev_alloc_skb(local->hw.extra_tx_headroom + max(sizeof(struct ieee80211_mgmt), sizeof(struct ieee80211_tdls_data)) + 50 + /* supported rates */ 7 + /* ext capab */ extra_ies_len + sizeof(struct ieee80211_tdls_lnkie)); if (!skb) return -ENOMEM; info = IEEE80211_SKB_CB(skb); skb_reserve(skb, local->hw.extra_tx_headroom); switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: case WLAN_TDLS_SETUP_RESPONSE: case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_TEARDOWN: case WLAN_TDLS_DISCOVERY_REQUEST: ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, action_code, dialog_token, status_code, skb); send_direct = false; break; case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, dialog_token, status_code, skb); send_direct = true; break; default: ret = -ENOTSUPP; break; } if (ret < 0) goto fail; if (extra_ies_len) memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); /* the TDLS link IE is always added last */ switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_TEARDOWN: case WLAN_TDLS_DISCOVERY_REQUEST: /* we are the initiator */ ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, sdata->u.mgd.bssid); break; case WLAN_TDLS_SETUP_RESPONSE: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: /* we are the responder */ ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, sdata->u.mgd.bssid); break; default: ret = -ENOTSUPP; goto fail; } if (send_direct) { ieee80211_tx_skb(sdata, skb); return 0; } /* * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise * we should default to AC_VI. */ switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: case WLAN_TDLS_SETUP_RESPONSE: skb_set_queue_mapping(skb, IEEE80211_AC_BK); skb->priority = 2; break; default: skb_set_queue_mapping(skb, IEEE80211_AC_VI); skb->priority = 5; break; } /* disable bottom halves when entering the Tx path */ local_bh_disable(); ret = ieee80211_subif_start_xmit(skb, dev); local_bh_enable(); return ret; fail: dev_kfree_skb(skb); return ret; } static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper) { struct sta_info *sta; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) return -ENOTSUPP; if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EINVAL; tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); switch (oper) { case NL80211_TDLS_ENABLE_LINK: rcu_read_lock(); sta = sta_info_get(sdata, peer); if (!sta) { rcu_read_unlock(); return -ENOLINK; } set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); rcu_read_unlock(); break; case NL80211_TDLS_DISABLE_LINK: return sta_info_destroy_addr(sdata, peer); case NL80211_TDLS_TEARDOWN: case NL80211_TDLS_SETUP: case NL80211_TDLS_DISCOVERY_REQ: /* We don't support in-driver setup/teardown/discovery */ return -ENOTSUPP; default: return -ENOTSUPP; } return 0; } static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_qos_hdr *nullfunc; struct sk_buff *skb; int size = sizeof(*nullfunc); __le16 fc; bool qos; struct ieee80211_tx_info *info; struct sta_info *sta; rcu_read_lock(); sta = sta_info_get(sdata, peer); if (sta) { qos = test_sta_flag(sta, WLAN_STA_WME); rcu_read_unlock(); } else { rcu_read_unlock(); return -ENOLINK; } if (qos) { fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC | IEEE80211_FCTL_FROMDS); } else { size -= 2; fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_FROMDS); } skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); if (!skb) return -ENOMEM; skb->dev = dev; skb_reserve(skb, local->hw.extra_tx_headroom); nullfunc = (void *) skb_put(skb, size); nullfunc->frame_control = fc; nullfunc->duration_id = 0; memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); nullfunc->seq_ctrl = 0; info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_INTFL_NL80211_FRAME_TX; skb_set_queue_mapping(skb, IEEE80211_AC_VO); skb->priority = 7; if (qos) nullfunc->qos_ctrl = cpu_to_le16(7); local_bh_disable(); ieee80211_xmit(sdata, skb); local_bh_enable(); *cookie = (unsigned long) skb; return 0; } static struct ieee80211_channel * ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_channel_type *type) { struct ieee80211_local *local = wiphy_priv(wiphy); *type = local->_oper_channel_type; return local->oper_channel; } #ifdef CONFIG_PM static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled) { drv_set_wakeup(wiphy_priv(wiphy), enabled); } #endif struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, .change_virtual_intf = ieee80211_change_iface, .start_p2p_device = ieee80211_start_p2p_device, .stop_p2p_device = ieee80211_stop_p2p_device, .add_key = ieee80211_add_key, .del_key = ieee80211_del_key, .get_key = ieee80211_get_key, .set_default_key = ieee80211_config_default_key, .set_default_mgmt_key = ieee80211_config_default_mgmt_key, .start_ap = ieee80211_start_ap, .change_beacon = ieee80211_change_beacon, .stop_ap = ieee80211_stop_ap, .add_station = ieee80211_add_station, .del_station = ieee80211_del_station, .change_station = ieee80211_change_station, .get_station = ieee80211_get_station, .dump_station = ieee80211_dump_station, .dump_survey = ieee80211_dump_survey, #ifdef CONFIG_MAC80211_MESH .add_mpath = ieee80211_add_mpath, .del_mpath = ieee80211_del_mpath, .change_mpath = ieee80211_change_mpath, .get_mpath = ieee80211_get_mpath, .dump_mpath = ieee80211_dump_mpath, .update_mesh_config = ieee80211_update_mesh_config, .get_mesh_config = ieee80211_get_mesh_config, .join_mesh = ieee80211_join_mesh, .leave_mesh = ieee80211_leave_mesh, #endif .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_monitor_channel = ieee80211_set_monitor_channel, .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, .sched_scan_start = ieee80211_sched_scan_start, .sched_scan_stop = ieee80211_sched_scan_stop, .auth = ieee80211_auth, .assoc = ieee80211_assoc, .deauth = ieee80211_deauth, .disassoc = ieee80211_disassoc, .join_ibss = ieee80211_join_ibss, .leave_ibss = ieee80211_leave_ibss, .set_wiphy_params = ieee80211_set_wiphy_params, .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) .set_power_mgmt = ieee80211_set_power_mgmt, .set_bitrate_mask = ieee80211_set_bitrate_mask, .remain_on_channel = ieee80211_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, .mgmt_tx = ieee80211_mgmt_tx, .mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait, .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, .mgmt_frame_register = ieee80211_mgmt_frame_register, .set_antenna = ieee80211_set_antenna, .get_antenna = ieee80211_get_antenna, .set_ringparam = ieee80211_set_ringparam, .get_ringparam = ieee80211_get_ringparam, .set_rekey_data = ieee80211_set_rekey_data, .tdls_oper = ieee80211_tdls_oper, .tdls_mgmt = ieee80211_tdls_mgmt, .probe_client = ieee80211_probe_client, .set_noack_map = ieee80211_set_noack_map, #ifdef CONFIG_PM .set_wakeup = ieee80211_set_wakeup, #endif .get_et_sset_count = ieee80211_get_et_sset_count, .get_et_stats = ieee80211_get_et_stats, .get_et_strings = ieee80211_get_et_strings, .get_channel = ieee80211_cfg_get_channel, }; compat-drivers-2012-09-18/net/mac80211/event.c0000644000175000017500000000162112026211315017567 0ustar mcgrofmcgrof/* * Copyright 2007 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * mac80211 - events */ #include #include "ieee80211_i.h" /* * Indicate a failed Michael MIC to userspace. If the caller knows the TSC of * the frame that generated the MIC failure (i.e., if it was provided by the * driver or is still in the frame), it should provide that information. */ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, struct ieee80211_hdr *hdr, const u8 *tsc, gfp_t gfp) { cfg80211_michael_mic_failure(sdata->dev, hdr->addr2, (hdr->addr1[0] & 0x01) ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE, keyidx, tsc, gfp); } compat-drivers-2012-09-18/net/mac80211/util.c0000644000175000017500000013620712026211315017434 0ustar mcgrofmcgrof/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * utilities for mac80211 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ieee80211_i.h" #include "driver-ops.h" #include "rate.h" #include "mesh.h" #include "wme.h" #include "led.h" #include "wep.h" /* privid for wiphys to determine whether they belong to us or not */ void *mac80211_wiphy_privid = &mac80211_wiphy_privid; struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) { struct ieee80211_local *local; BUG_ON(!wiphy); local = wiphy_priv(wiphy); return &local->hw; } EXPORT_SYMBOL(wiphy_to_ieee80211_hw); u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, enum nl80211_iftype type) { __le16 fc = hdr->frame_control; /* drop ACK/CTS frames and incorrect hdr len (ctrl) */ if (len < 16) return NULL; if (ieee80211_is_data(fc)) { if (len < 24) /* drop incorrect hdr len (data) */ return NULL; if (ieee80211_has_a4(fc)) return NULL; if (ieee80211_has_tods(fc)) return hdr->addr1; if (ieee80211_has_fromds(fc)) return hdr->addr2; return hdr->addr3; } if (ieee80211_is_mgmt(fc)) { if (len < 24) /* drop incorrect hdr len (mgmt) */ return NULL; return hdr->addr3; } if (ieee80211_is_ctl(fc)) { if(ieee80211_is_pspoll(fc)) return hdr->addr1; if (ieee80211_is_back_req(fc)) { switch (type) { case NL80211_IFTYPE_STATION: return hdr->addr2; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: return hdr->addr1; default: break; /* fall through to the return */ } } } return NULL; } void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) { struct sk_buff *skb; struct ieee80211_hdr *hdr; skb_queue_walk(&tx->skbs, skb) { hdr = (struct ieee80211_hdr *) skb->data; hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); } } int ieee80211_frame_duration(enum ieee80211_band band, size_t len, int rate, int erp, int short_preamble) { int dur; /* calculate duration (in microseconds, rounded up to next higher * integer if it includes a fractional microsecond) to send frame of * len bytes (does not include FCS) at the given rate. Duration will * also include SIFS. * * rate is in 100 kbps, so divident is multiplied by 10 in the * DIV_ROUND_UP() operations. */ if (band == IEEE80211_BAND_5GHZ || erp) { /* * OFDM: * * N_DBPS = DATARATE x 4 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) * (16 = SIGNAL time, 6 = tail bits) * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext * * T_SYM = 4 usec * 802.11a - 17.5.2: aSIFSTime = 16 usec * 802.11g - 19.8.4: aSIFSTime = 10 usec + * signal ext = 6 usec */ dur = 16; /* SIFS + signal ext */ dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */ dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */ dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, 4 * rate); /* T_SYM x N_SYM */ } else { /* * 802.11b or 802.11g with 802.11b compatibility: * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. * * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 * aSIFSTime = 10 usec * aPreambleLength = 144 usec or 72 usec with short preamble * aPLCPHeaderLength = 48 usec or 24 usec with short preamble */ dur = 10; /* aSIFSTime = 10 usec */ dur += short_preamble ? (72 + 24) : (144 + 48); dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); } return dur; } /* Exported duration function for driver use */ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_band band, size_t frame_len, struct ieee80211_rate *rate) { struct ieee80211_sub_if_data *sdata; u16 dur; int erp; bool short_preamble = false; erp = 0; if (vif) { sdata = vif_to_sdata(vif); short_preamble = sdata->vif.bss_conf.use_short_preamble; if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) erp = rate->flags & IEEE80211_RATE_ERP_G; } dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, short_preamble); return cpu_to_le16(dur); } EXPORT_SYMBOL(ieee80211_generic_frame_duration); __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, size_t frame_len, const struct ieee80211_tx_info *frame_txctl) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate; struct ieee80211_sub_if_data *sdata; bool short_preamble; int erp; u16 dur; struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[frame_txctl->band]; short_preamble = false; rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; erp = 0; if (vif) { sdata = vif_to_sdata(vif); short_preamble = sdata->vif.bss_conf.use_short_preamble; if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) erp = rate->flags & IEEE80211_RATE_ERP_G; } /* CTS duration */ dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate, erp, short_preamble); /* Data frame duration */ dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, erp, short_preamble); /* ACK duration */ dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, erp, short_preamble); return cpu_to_le16(dur); } EXPORT_SYMBOL(ieee80211_rts_duration); __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, size_t frame_len, const struct ieee80211_tx_info *frame_txctl) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate; struct ieee80211_sub_if_data *sdata; bool short_preamble; int erp; u16 dur; struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[frame_txctl->band]; short_preamble = false; rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; erp = 0; if (vif) { sdata = vif_to_sdata(vif); short_preamble = sdata->vif.bss_conf.use_short_preamble; if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) erp = rate->flags & IEEE80211_RATE_ERP_G; } /* Data frame duration */ dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate, erp, short_preamble); if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { /* ACK duration */ dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate, erp, short_preamble); } return cpu_to_le16(dur); } EXPORT_SYMBOL(ieee80211_ctstoself_duration); void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) { struct ieee80211_sub_if_data *sdata; int n_acs = IEEE80211_NUM_ACS; if (local->hw.queues < IEEE80211_NUM_ACS) n_acs = 1; list_for_each_entry_rcu(sdata, &local->interfaces, list) { int ac; if (!sdata->dev) continue; if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) continue; if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && local->queue_stop_reasons[sdata->vif.cab_queue] != 0) continue; for (ac = 0; ac < n_acs; ac++) { int ac_queue = sdata->vif.hw_queue[ac]; if (ac_queue == queue || (sdata->vif.cab_queue == queue && local->queue_stop_reasons[ac_queue] == 0 && skb_queue_empty(&local->pending[ac_queue]))) netif_wake_subqueue(sdata->dev, ac); } } } static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); trace_wake_queue(local, queue, reason); if (WARN_ON(queue >= hw->queues)) return; if (!test_bit(reason, &local->queue_stop_reasons[queue])) return; __clear_bit(reason, &local->queue_stop_reasons[queue]); if (local->queue_stop_reasons[queue] != 0) /* someone still has this queue stopped */ return; if (skb_queue_empty(&local->pending[queue])) { rcu_read_lock(); ieee80211_propagate_queue_wake(local, queue); rcu_read_unlock(); } else tasklet_schedule(&local->tx_pending_tasklet); } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); __ieee80211_wake_queue(hw, queue, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) { ieee80211_wake_queue_by_reason(hw, queue, IEEE80211_QUEUE_STOP_REASON_DRIVER); } EXPORT_SYMBOL(ieee80211_wake_queue); static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; int n_acs = IEEE80211_NUM_ACS; trace_stop_queue(local, queue, reason); if (WARN_ON(queue >= hw->queues)) return; if (test_bit(reason, &local->queue_stop_reasons[queue])) return; __set_bit(reason, &local->queue_stop_reasons[queue]); if (local->hw.queues < IEEE80211_NUM_ACS) n_acs = 1; rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { int ac; if (!sdata->dev) continue; for (ac = 0; ac < n_acs; ac++) { if (sdata->vif.hw_queue[ac] == queue || sdata->vif.cab_queue == queue) netif_stop_subqueue(sdata->dev, ac); } } rcu_read_unlock(); } void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); __ieee80211_stop_queue(hw, queue, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) { ieee80211_stop_queue_by_reason(hw, queue, IEEE80211_QUEUE_STOP_REASON_DRIVER); } EXPORT_SYMBOL(ieee80211_stop_queue); void ieee80211_add_pending_skb(struct ieee80211_local *local, struct sk_buff *skb) { struct ieee80211_hw *hw = &local->hw; unsigned long flags; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int queue = info->hw_queue; if (WARN_ON(!info->control.vif)) { kfree_skb(skb); return; } spin_lock_irqsave(&local->queue_stop_reason_lock, flags); __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); __skb_queue_tail(&local->pending[queue], skb); __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, struct sk_buff_head *skbs, void (*fn)(void *data), void *data) { struct ieee80211_hw *hw = &local->hw; struct sk_buff *skb; unsigned long flags; int queue, i; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); while ((skb = skb_dequeue(skbs))) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (WARN_ON(!info->control.vif)) { kfree_skb(skb); continue; } queue = info->hw_queue; __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); __skb_queue_tail(&local->pending[queue], skb); } if (fn) fn(data); for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; int i; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < hw->queues; i++) __ieee80211_stop_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_stop_queues(struct ieee80211_hw *hw) { ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER); } EXPORT_SYMBOL(ieee80211_stop_queues); int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; int ret; if (WARN_ON(queue >= hw->queues)) return true; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); ret = !!local->queue_stop_reasons[queue]; spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); return ret; } EXPORT_SYMBOL(ieee80211_queue_stopped); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; int i; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } void ieee80211_wake_queues(struct ieee80211_hw *hw) { ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER); } EXPORT_SYMBOL(ieee80211_wake_queues); void ieee80211_iterate_active_interfaces( struct ieee80211_hw *hw, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { switch (sdata->vif.type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: continue; default: break; } if (ieee80211_sdata_running(sdata)) iterator(data, sdata->vif.addr, &sdata->vif); } sdata = rcu_dereference_protected(local->monitor_sdata, lockdep_is_held(&local->iflist_mtx)); if (sdata) iterator(data, sdata->vif.addr, &sdata->vif); mutex_unlock(&local->iflist_mtx); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); void ieee80211_iterate_active_interfaces_atomic( struct ieee80211_hw *hw, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { switch (sdata->vif.type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: continue; default: break; } if (ieee80211_sdata_running(sdata)) iterator(data, sdata->vif.addr, &sdata->vif); } sdata = rcu_dereference(local->monitor_sdata); if (sdata) iterator(data, sdata->vif.addr, &sdata->vif); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); /* * Nothing should have been stuffed into the workqueue during * the suspend->resume cycle. If this WARN is seen then there * is a bug with either the driver suspend or something in * mac80211 stuffing into the workqueue which we haven't yet * cleared during mac80211's suspend cycle. */ static bool ieee80211_can_queue_work(struct ieee80211_local *local) { if (WARN(local->suspended && !local->resuming, "queueing ieee80211 work while going to suspend\n")) return false; return true; } void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) { struct ieee80211_local *local = hw_to_local(hw); if (!ieee80211_can_queue_work(local)) return; queue_work(local->workqueue, work); } EXPORT_SYMBOL(ieee80211_queue_work); void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, struct delayed_work *dwork, unsigned long delay) { struct ieee80211_local *local = hw_to_local(hw); if (!ieee80211_can_queue_work(local)) return; queue_delayed_work(local->workqueue, dwork, delay); } EXPORT_SYMBOL(ieee80211_queue_delayed_work); u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, struct ieee802_11_elems *elems, u64 filter, u32 crc) { size_t left = len; u8 *pos = start; bool calc_crc = filter != 0; DECLARE_BITMAP(seen_elems, 256); bitmap_zero(seen_elems, 256); memset(elems, 0, sizeof(*elems)); elems->ie_start = start; elems->total_len = len; while (left >= 2) { u8 id, elen; bool elem_parse_failed; id = *pos++; elen = *pos++; left -= 2; if (elen > left) { elems->parse_error = true; break; } if (id != WLAN_EID_VENDOR_SPECIFIC && id != WLAN_EID_QUIET && test_bit(id, seen_elems)) { elems->parse_error = true; left -= elen; pos += elen; continue; } if (calc_crc && id < 64 && (filter & (1ULL << id))) crc = crc32_be(crc, pos - 2, elen + 2); elem_parse_failed = false; switch (id) { case WLAN_EID_SSID: elems->ssid = pos; elems->ssid_len = elen; break; case WLAN_EID_SUPP_RATES: elems->supp_rates = pos; elems->supp_rates_len = elen; break; case WLAN_EID_FH_PARAMS: elems->fh_params = pos; elems->fh_params_len = elen; break; case WLAN_EID_DS_PARAMS: elems->ds_params = pos; elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: elems->cf_params = pos; elems->cf_params_len = elen; break; case WLAN_EID_TIM: if (elen >= sizeof(struct ieee80211_tim_ie)) { elems->tim = (void *)pos; elems->tim_len = elen; } else elem_parse_failed = true; break; case WLAN_EID_IBSS_PARAMS: elems->ibss_params = pos; elems->ibss_params_len = elen; break; case WLAN_EID_CHALLENGE: elems->challenge = pos; elems->challenge_len = elen; break; case WLAN_EID_VENDOR_SPECIFIC: if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && pos[2] == 0xf2) { /* Microsoft OUI (00:50:F2) */ if (calc_crc) crc = crc32_be(crc, pos - 2, elen + 2); if (pos[3] == 1) { /* OUI Type 1 - WPA IE */ elems->wpa = pos; elems->wpa_len = elen; } else if (elen >= 5 && pos[3] == 2) { /* OUI Type 2 - WMM IE */ if (pos[4] == 0) { elems->wmm_info = pos; elems->wmm_info_len = elen; } else if (pos[4] == 1) { elems->wmm_param = pos; elems->wmm_param_len = elen; } } } break; case WLAN_EID_RSN: elems->rsn = pos; elems->rsn_len = elen; break; case WLAN_EID_ERP_INFO: elems->erp_info = pos; elems->erp_info_len = elen; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; elems->ext_supp_rates_len = elen; break; case WLAN_EID_HT_CAPABILITY: if (elen >= sizeof(struct ieee80211_ht_cap)) elems->ht_cap_elem = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_HT_OPERATION: if (elen >= sizeof(struct ieee80211_ht_operation)) elems->ht_operation = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_MESH_ID: elems->mesh_id = pos; elems->mesh_id_len = elen; break; case WLAN_EID_MESH_CONFIG: if (elen >= sizeof(struct ieee80211_meshconf_ie)) elems->mesh_config = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_PEER_MGMT: elems->peering = pos; elems->peering_len = elen; break; case WLAN_EID_PREQ: elems->preq = pos; elems->preq_len = elen; break; case WLAN_EID_PREP: elems->prep = pos; elems->prep_len = elen; break; case WLAN_EID_PERR: elems->perr = pos; elems->perr_len = elen; break; case WLAN_EID_RANN: if (elen >= sizeof(struct ieee80211_rann_ie)) elems->rann = (void *)pos; else elem_parse_failed = true; break; case WLAN_EID_CHANNEL_SWITCH: if (elen != sizeof(struct ieee80211_channel_sw_ie)) { elem_parse_failed = true; break; } elems->ch_switch_ie = (void *)pos; break; case WLAN_EID_QUIET: if (!elems->quiet_elem) { elems->quiet_elem = pos; elems->quiet_elem_len = elen; } elems->num_of_quiet_elem++; break; case WLAN_EID_COUNTRY: elems->country_elem = pos; elems->country_elem_len = elen; break; case WLAN_EID_PWR_CONSTRAINT: elems->pwr_constr_elem = pos; elems->pwr_constr_elem_len = elen; break; case WLAN_EID_TIMEOUT_INTERVAL: elems->timeout_int = pos; elems->timeout_int_len = elen; break; default: break; } if (elem_parse_failed) elems->parse_error = true; else set_bit(id, seen_elems); left -= elen; pos += elen; } if (left != 0) elems->parse_error = true; return crc; } void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems) { ieee802_11_parse_elems_crc(start, len, elems, 0, 0); } void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, bool bss_notify) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_queue_params qparam; int ac; bool use_11b, enable_qos; int aCWmin, aCWmax; if (!local->ops->conf_tx) return; if (local->hw.queues < IEEE80211_NUM_ACS) return; memset(&qparam, 0, sizeof(qparam)); use_11b = (local->oper_channel->band == IEEE80211_BAND_2GHZ) && !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); /* * By default disable QoS in STA mode for old access points, which do * not support 802.11e. New APs will provide proper queue parameters, * that we will configure later. */ enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { /* Set defaults according to 802.11-2007 Table 7-37 */ aCWmax = 1023; if (use_11b) aCWmin = 31; else aCWmin = 15; if (enable_qos) { switch (ac) { case IEEE80211_AC_BK: qparam.cw_max = aCWmax; qparam.cw_min = aCWmin; qparam.txop = 0; qparam.aifs = 7; break; /* never happens but let's not leave undefined */ default: case IEEE80211_AC_BE: qparam.cw_max = aCWmax; qparam.cw_min = aCWmin; qparam.txop = 0; qparam.aifs = 3; break; case IEEE80211_AC_VI: qparam.cw_max = aCWmin; qparam.cw_min = (aCWmin + 1) / 2 - 1; if (use_11b) qparam.txop = 6016/32; else qparam.txop = 3008/32; qparam.aifs = 2; break; case IEEE80211_AC_VO: qparam.cw_max = (aCWmin + 1) / 2 - 1; qparam.cw_min = (aCWmin + 1) / 4 - 1; if (use_11b) qparam.txop = 3264/32; else qparam.txop = 1504/32; qparam.aifs = 2; break; } } else { /* Confiure old 802.11b/g medium access rules. */ qparam.cw_max = aCWmax; qparam.cw_min = aCWmin; qparam.txop = 0; qparam.aifs = 2; } qparam.uapsd = false; sdata->tx_conf[ac] = qparam; drv_conf_tx(local, sdata, ac, &qparam); } if (sdata->vif.type != NL80211_IFTYPE_MONITOR && sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) { sdata->vif.bss_conf.qos = enable_qos; if (bss_notify) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); } } void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, const u8 *supp_rates) { struct ieee80211_local *local = sdata->local; int i, have_higher_than_11mbit = 0; /* cf. IEEE 802.11 9.2.12 */ for (i = 0; i < supp_rates_len; i++) if ((supp_rates[i] & 0x7f) * 5 > 110) have_higher_than_11mbit = 1; if (local->oper_channel->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit) sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; ieee80211_set_wmm_default(sdata, true); } u32 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band) { struct ieee80211_supported_band *sband; struct ieee80211_rate *bitrates; u32 mandatory_rates; enum ieee80211_rate_flags mandatory_flag; int i; sband = local->hw.wiphy->bands[band]; if (WARN_ON(!sband)) return 1; if (band == IEEE80211_BAND_2GHZ) mandatory_flag = IEEE80211_RATE_MANDATORY_B; else mandatory_flag = IEEE80211_RATE_MANDATORY_A; bitrates = sband->bitrates; mandatory_rates = 0; for (i = 0; i < sband->n_bitrates; i++) if (bitrates[i].flags & mandatory_flag) mandatory_rates |= BIT(i); return mandatory_rates; } void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u8 *extra, size_t extra_len, const u8 *da, const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; int err; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 6 + extra_len); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); memset(mgmt, 0, 24 + 6); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, bssid, ETH_ALEN); mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); mgmt->u.auth.status_code = cpu_to_le16(0); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); WARN_ON(err); } IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, const u8 *ie, size_t ie_len, enum ieee80211_band band, u32 rate_mask, u8 channel) { struct ieee80211_supported_band *sband; u8 *pos; size_t offset = 0, noffset; int supp_rates_len, i; u8 rates[32]; int num_rates; int ext_rates_len; sband = local->hw.wiphy->bands[band]; if (WARN_ON_ONCE(!sband)) return 0; pos = buffer; num_rates = 0; for (i = 0; i < sband->n_bitrates; i++) { if ((BIT(i) & rate_mask) == 0) continue; /* skip rate */ rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5); } supp_rates_len = min_t(int, num_rates, 8); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = supp_rates_len; memcpy(pos, rates, supp_rates_len); pos += supp_rates_len; /* insert "request information" if in custom IEs */ if (ie && ie_len) { static const u8 before_extrates[] = { WLAN_EID_SSID, WLAN_EID_SUPP_RATES, WLAN_EID_REQUEST, }; noffset = ieee80211_ie_split(ie, ie_len, before_extrates, ARRAY_SIZE(before_extrates), offset); memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; offset = noffset; } ext_rates_len = num_rates - supp_rates_len; if (ext_rates_len > 0) { *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = ext_rates_len; memcpy(pos, rates + supp_rates_len, ext_rates_len); pos += ext_rates_len; } if (channel && sband->band == IEEE80211_BAND_2GHZ) { *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = channel; } /* insert custom IEs that go before HT */ if (ie && ie_len) { static const u8 before_ht[] = { WLAN_EID_SSID, WLAN_EID_SUPP_RATES, WLAN_EID_REQUEST, WLAN_EID_EXT_SUPP_RATES, WLAN_EID_DS_PARAMS, WLAN_EID_SUPPORTED_REGULATORY_CLASSES, }; noffset = ieee80211_ie_split(ie, ie_len, before_ht, ARRAY_SIZE(before_ht), offset); memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; offset = noffset; } if (sband->ht_cap.ht_supported) pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); /* * If adding more here, adjust code in main.c * that calculates local->scan_ies_len. */ /* add any remaining custom IEs */ if (ie && ie_len) { noffset = ie_len; memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; } if (sband->vht_cap.vht_supported) pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, sband->vht_cap.cap); return pos - buffer; } struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; size_t buf_len; u8 *buf; u8 chan_no; /* FIXME: come up with a proper value */ buf = kmalloc(200 + ie_len, GFP_KERNEL); if (!buf) return NULL; /* * Do not send DS Channel parameter for directed probe requests * in order to maximize the chance that we get a response. Some * badly-behaved APs don't respond when this parameter is included. */ if (directed) chan_no = 0; else chan_no = ieee80211_frequency_to_channel(chan->center_freq); buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band, ratemask, chan_no); skb = ieee80211_probereq_get(&local->hw, &sdata->vif, ssid, ssid_len, buf, buf_len); if (!skb) goto out; if (dst) { mgmt = (struct ieee80211_mgmt *) skb->data; memcpy(mgmt->da, dst, ETH_ALEN); memcpy(mgmt->bssid, dst, ETH_ALEN); } IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; out: kfree(buf); return skb; } void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, bool no_cck, struct ieee80211_channel *channel) { struct sk_buff *skb; skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel, ssid, ssid_len, ie, ie_len, directed); if (skb) { if (no_cck) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; ieee80211_tx_skb(sdata, skb); } } u32 ieee80211_sta_get_rates(struct ieee80211_local *local, struct ieee802_11_elems *elems, enum ieee80211_band band, u32 *basic_rates) { struct ieee80211_supported_band *sband; struct ieee80211_rate *bitrates; size_t num_rates; u32 supp_rates; int i, j; sband = local->hw.wiphy->bands[band]; if (WARN_ON(!sband)) return 1; bitrates = sband->bitrates; num_rates = sband->n_bitrates; supp_rates = 0; for (i = 0; i < elems->supp_rates_len + elems->ext_supp_rates_len; i++) { u8 rate = 0; int own_rate; bool is_basic; if (i < elems->supp_rates_len) rate = elems->supp_rates[i]; else if (elems->ext_supp_rates) rate = elems->ext_supp_rates [i - elems->supp_rates_len]; own_rate = 5 * (rate & 0x7f); is_basic = !!(rate & 0x80); if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) continue; for (j = 0; j < num_rates; j++) { if (bitrates[j].bitrate == own_rate) { supp_rates |= BIT(j); if (basic_rates && is_basic) *basic_rates |= BIT(j); } } } return supp_rates; } void ieee80211_stop_device(struct ieee80211_local *local) { ieee80211_led_radio(local, false); ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); cancel_work_sync(&local->reconfig_filter); flush_workqueue(local->workqueue); drv_stop(local); } int ieee80211_reconfig(struct ieee80211_local *local) { struct ieee80211_hw *hw = &local->hw; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; int res, i; #ifdef CONFIG_PM if (local->suspended) local->resuming = true; if (local->wowlan) { local->wowlan = false; res = drv_resume(local); if (res < 0) { local->resuming = false; return res; } if (res == 0) goto wake_up; WARN_ON(res > 1); /* * res is 1, which means the driver requested * to go through a regular reset on wakeup. */ } #endif /* everything else happens only if HW was up & running */ if (!local->open_count) goto wake_up; /* * Upon resume hardware can sometimes be goofy due to * various platform / driver / bus issues, so restarting * the device may at times not work immediately. Propagate * the error. */ res = drv_start(local); if (res) { WARN(local->suspended, "Hardware became unavailable " "upon resume. This could be a software issue " "prior to suspend or a hardware issue.\n"); return res; } /* setup fragmentation threshold */ drv_set_frag_threshold(local, hw->wiphy->frag_threshold); /* setup RTS threshold */ drv_set_rts_threshold(local, hw->wiphy->rts_threshold); /* reset coverage class */ drv_set_coverage_class(local, hw->wiphy->coverage_class); ieee80211_led_radio(local, true); ieee80211_mod_tpt_led_trig(local, IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); /* add interfaces */ sdata = rtnl_dereference(local->monitor_sdata); if (sdata) { res = drv_add_interface(local, sdata); if (WARN_ON(res)) { rcu_assign_pointer(local->monitor_sdata, NULL); synchronize_net(); kfree(sdata); } } list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && ieee80211_sdata_running(sdata)) res = drv_add_interface(local, sdata); } /* add STAs back */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { enum ieee80211_sta_state state; if (!sta->uploaded) continue; /* AP-mode stations will be added later */ if (sta->sdata->vif.type == NL80211_IFTYPE_AP) continue; for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) WARN_ON(drv_sta_state(local, sta->sdata, sta, state, state + 1)); } mutex_unlock(&local->sta_mtx); /* reconfigure tx conf */ if (hw->queues >= IEEE80211_NUM_ACS) { list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sdata->vif.type == NL80211_IFTYPE_MONITOR || !ieee80211_sdata_running(sdata)) continue; for (i = 0; i < IEEE80211_NUM_ACS; i++) drv_conf_tx(local, sdata, i, &sdata->tx_conf[i]); } } /* reconfigure hardware */ ieee80211_hw_config(local, ~0); ieee80211_configure_filter(local); /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { u32 changed; if (!ieee80211_sdata_running(sdata)) continue; /* common change flags for all interface types */ changed = BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_ERP_SLOT | BSS_CHANGED_HT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT | BSS_CHANGED_BSSID | BSS_CHANGED_CQM | BSS_CHANGED_QOS | BSS_CHANGED_IDLE; switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: changed |= BSS_CHANGED_ASSOC | BSS_CHANGED_ARP_FILTER | BSS_CHANGED_PS; mutex_lock(&sdata->u.mgd.mtx); ieee80211_bss_info_change_notify(sdata, changed); mutex_unlock(&sdata->u.mgd.mtx); break; case NL80211_IFTYPE_ADHOC: changed |= BSS_CHANGED_IBSS; /* fall through */ case NL80211_IFTYPE_AP: changed |= BSS_CHANGED_SSID; if (sdata->vif.type == NL80211_IFTYPE_AP) changed |= BSS_CHANGED_AP_PROBE_RESP; /* fall through */ case NL80211_IFTYPE_MESH_POINT: changed |= BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED; ieee80211_bss_info_change_notify(sdata, changed); break; case NL80211_IFTYPE_WDS: break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: /* ignore virtual */ break; case NL80211_IFTYPE_P2P_DEVICE: changed = BSS_CHANGED_IDLE; break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: WARN_ON(1); break; } } ieee80211_recalc_ps(local, -1); /* * The sta might be in psm against the ap (e.g. because * this was the state before a hw restart), so we * explicitly send a null packet in order to make sure * it'll sync against the ap (and get out of psm). */ if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_STATION) continue; ieee80211_send_nullfunc(local, sdata, 0); } } /* APs are now beaconing, add back stations */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { enum ieee80211_sta_state state; if (!sta->uploaded) continue; if (sta->sdata->vif.type != NL80211_IFTYPE_AP) continue; for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) WARN_ON(drv_sta_state(local, sta->sdata, sta, state, state + 1)); } mutex_unlock(&local->sta_mtx); /* add back keys */ list_for_each_entry(sdata, &local->interfaces, list) if (ieee80211_sdata_running(sdata)) ieee80211_enable_keys(sdata); wake_up: local->in_reconfig = false; barrier(); /* * Clear the WLAN_STA_BLOCK_BA flag so new aggregation * sessions can be established after a resume. * * Also tear down aggregation sessions since reconfiguring * them in a hardware restart scenario is not easily done * right now, and the hardware will have lost information * about the sessions, but we and the AP still think they * are active. This is really a workaround though. */ if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { ieee80211_sta_tear_down_BA_sessions(sta, true); clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } mutex_unlock(&local->sta_mtx); } ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* * If this is for hw restart things are still running. * We may want to change that later, however. */ if (!local->suspended) return 0; #ifdef CONFIG_PM /* first set suspended false, then resuming */ local->suspended = false; mb(); local->resuming = false; list_for_each_entry(sdata, &local->interfaces, list) { switch(sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_restart(sdata); break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_restart(sdata); break; case NL80211_IFTYPE_MESH_POINT: ieee80211_mesh_restart(sdata); break; default: break; } } mod_timer(&local->sta_cleanup, jiffies + 1); mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) mesh_plink_restart(sta); mutex_unlock(&local->sta_mtx); #else WARN_ON(1); #endif return 0; } void ieee80211_resume_disconnect(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local; struct ieee80211_key *key; if (WARN_ON(!vif)) return; sdata = vif_to_sdata(vif); local = sdata->local; if (WARN_ON(!local->resuming)) return; if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) return; sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; mutex_lock(&local->key_mtx); list_for_each_entry(key, &sdata->key_list, list) key->flags |= KEY_FLAG_TAINTED; mutex_unlock(&local->key_mtx); } EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, enum ieee80211_smps_mode *smps_mode) { if (ifmgd->associated) { *smps_mode = ifmgd->ap_smps; if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { if (ifmgd->powersave) *smps_mode = IEEE80211_SMPS_DYNAMIC; else *smps_mode = IEEE80211_SMPS_OFF; } return 1; } return 0; } /* must hold iflist_mtx */ void ieee80211_recalc_smps(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; int count = 0; lockdep_assert_held(&local->iflist_mtx); /* * This function could be improved to handle multiple * interfaces better, but right now it makes any * non-station interfaces force SM PS to be turned * off. If there are multiple station interfaces it * could also use the best possible mode, e.g. if * one is in static and the other in dynamic then * dynamic is ok. */ list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) continue; if (sdata->vif.type != NL80211_IFTYPE_STATION) goto set; count += check_mgd_smps(&sdata->u.mgd, &smps_mode); if (count > 1) { smps_mode = IEEE80211_SMPS_OFF; break; } } if (smps_mode == local->smps_mode) return; set: local->smps_mode = smps_mode; /* changed flag is auto-detected for this */ ieee80211_hw_config(local, 0); } static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) { int i; for (i = 0; i < n_ids; i++) if (ids[i] == id) return true; return false; } /** * ieee80211_ie_split - split an IE buffer according to ordering * * @ies: the IE buffer * @ielen: the length of the IE buffer * @ids: an array with element IDs that are allowed before * the split * @n_ids: the size of the element ID array * @offset: offset where to start splitting in the buffer * * This function splits an IE buffer by updating the @offset * variable to point to the location where the buffer should be * split. * * It assumes that the given IE buffer is well-formed, this * has to be guaranteed by the caller! * * It also assumes that the IEs in the buffer are ordered * correctly, if not the result of using this function will not * be ordered correctly either, i.e. it does no reordering. * * The function returns the offset where the next part of the * buffer starts, which may be @ielen if the entire (remainder) * of the buffer should be used. */ size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset) { size_t pos = offset; while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) pos += 2 + ies[pos + 1]; return pos; } size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) { size_t pos = offset; while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) pos += 2 + ies[pos + 1]; return pos; } static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, int rssi_min_thold, int rssi_max_thold) { trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return; /* * Scale up threshold values before storing it, as the RSSI averaging * algorithm uses a scaled up value as well. Change this scaling * factor if the RSSI averaging algorithm changes. */ sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; } void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, int rssi_min_thold, int rssi_max_thold) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); WARN_ON(rssi_min_thold == rssi_max_thold || rssi_min_thold > rssi_max_thold); _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); } EXPORT_SYMBOL(ieee80211_enable_rssi_reports); void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); _ieee80211_enable_rssi_reports(sdata, 0, 0); } EXPORT_SYMBOL(ieee80211_disable_rssi_reports); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap) { __le16 tmp; *pos++ = WLAN_EID_HT_CAPABILITY; *pos++ = sizeof(struct ieee80211_ht_cap); memset(pos, 0, sizeof(struct ieee80211_ht_cap)); /* capability flags */ tmp = cpu_to_le16(cap); memcpy(pos, &tmp, sizeof(u16)); pos += sizeof(u16); /* AMPDU parameters */ *pos++ = ht_cap->ampdu_factor | (ht_cap->ampdu_density << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); /* MCS set */ memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); pos += sizeof(ht_cap->mcs); /* extended capabilities */ pos += sizeof(__le16); /* BF capabilities */ pos += sizeof(__le32); /* antenna selection */ pos += sizeof(u8); return pos; } u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u32 cap) { __le32 tmp; *pos++ = WLAN_EID_VHT_CAPABILITY; *pos++ = sizeof(struct ieee80211_vht_capabilities); memset(pos, 0, sizeof(struct ieee80211_vht_capabilities)); /* capability flags */ tmp = cpu_to_le32(cap); memcpy(pos, &tmp, sizeof(u32)); pos += sizeof(u32); /* VHT MCS set */ memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); pos += sizeof(vht_cap->vht_mcs); return pos; } u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type, u16 prot_mode) { struct ieee80211_ht_operation *ht_oper; /* Build HT Information */ *pos++ = WLAN_EID_HT_OPERATION; *pos++ = sizeof(struct ieee80211_ht_operation); ht_oper = (struct ieee80211_ht_operation *)pos; ht_oper->primary_chan = ieee80211_frequency_to_channel(channel->center_freq); switch (channel_type) { case NL80211_CHAN_HT40MINUS: ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; break; case NL80211_CHAN_HT40PLUS: ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; break; case NL80211_CHAN_HT20: default: ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; break; } if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && channel_type != NL80211_CHAN_NO_HT && channel_type != NL80211_CHAN_HT20) ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; ht_oper->operation_mode = cpu_to_le16(prot_mode); ht_oper->stbc_param = 0x0000; /* It seems that Basic MCS set and Supported MCS set are identical for the first 10 bytes */ memset(&ht_oper->basic_set, 0, 16); memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); return pos + sizeof(struct ieee80211_ht_operation); } enum nl80211_channel_type ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper) { enum nl80211_channel_type channel_type; if (!ht_oper) return NL80211_CHAN_NO_HT; switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_NONE: channel_type = NL80211_CHAN_HT20; break; case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: channel_type = NL80211_CHAN_HT40PLUS; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: channel_type = NL80211_CHAN_HT40MINUS; break; default: channel_type = NL80211_CHAN_NO_HT; } return channel_type; } int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool need_basic, enum ieee80211_band band) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; int rate; u8 i, rates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; sband = local->hw.wiphy->bands[band]; rates = sband->n_bitrates; if (rates > 8) rates = 8; if (skb_tailroom(skb) < rates + 2) return -ENOMEM; pos = skb_put(skb, rates + 2); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = rates; for (i = 0; i < rates; i++) { u8 basic = 0; if (need_basic && basic_rates & BIT(i)) basic = 0x80; rate = sband->bitrates[i].bitrate; *pos++ = basic | (u8) (rate / 5); } return 0; } int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool need_basic, enum ieee80211_band band) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; int rate; u8 i, exrates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; sband = local->hw.wiphy->bands[band]; exrates = sband->n_bitrates; if (exrates > 8) exrates -= 8; else exrates = 0; if (skb_tailroom(skb) < exrates + 2) return -ENOMEM; if (exrates) { pos = skb_put(skb, exrates + 2); *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = exrates; for (i = 8; i < sband->n_bitrates; i++) { u8 basic = 0; if (need_basic && basic_rates & BIT(i)) basic = 0x80; rate = sband->bitrates[i].bitrate; *pos++ = basic | (u8) (rate / 5); } } return 0; } int ieee80211_ave_rssi(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) { /* non-managed type inferfaces */ return 0; } return ifmgd->ave_beacon_signal; } EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); compat-drivers-2012-09-18/net/mac80211/trace.h0000644000175000017500000010162612026211315017557 0ustar mcgrofmcgrof#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) #define __MAC80211_DRIVER_TRACE #include #include #include "ieee80211_i.h" #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211 #define MAXNAME 32 #define LOCAL_ENTRY __array(char, wiphy_name, 32) #define LOCAL_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(local->hw.wiphy), MAXNAME) #define LOCAL_PR_FMT "%s" #define LOCAL_PR_ARG __entry->wiphy_name #define STA_ENTRY __array(char, sta_addr, ETH_ALEN) #define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) #define STA_PR_FMT " sta:%pM" #define STA_PR_ARG __entry->sta_addr #define VIF_ENTRY __field(enum nl80211_iftype, vif_type) __field(void *, sdata) \ __field(bool, p2p) \ __string(vif_name, sdata->dev ? sdata->dev->name : "") #define VIF_ASSIGN __entry->vif_type = sdata->vif.type; __entry->sdata = sdata; \ __entry->p2p = sdata->vif.p2p; \ __assign_str(vif_name, sdata->dev ? sdata->dev->name : sdata->name) #define VIF_PR_FMT " vif:%s(%d%s)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" /* * Tracing for driver callbacks. */ DECLARE_EVENT_CLASS(local_only_evt, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local), TP_STRUCT__entry( LOCAL_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; ), TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG) ); DECLARE_EVENT_CLASS(local_sdata_addr_evt, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __array(char, addr, 6) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; memcpy(__entry->addr, sdata->vif.addr, 6); ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr ) ); DECLARE_EVENT_CLASS(local_u32_evt, TP_PROTO(struct ieee80211_local *local, u32 value), TP_ARGS(local, value), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, value) ), TP_fast_assign( LOCAL_ASSIGN; __entry->value = value; ), TP_printk( LOCAL_PR_FMT " value:%d", LOCAL_PR_ARG, __entry->value ) ); DECLARE_EVENT_CLASS(local_sdata_evt, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT, LOCAL_PR_ARG, VIF_PR_ARG ) ); DEFINE_EVENT(local_only_evt, drv_return_void, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(drv_return_int, TP_PROTO(struct ieee80211_local *local, int ret), TP_ARGS(local, ret), TP_STRUCT__entry( LOCAL_ENTRY __field(int, ret) ), TP_fast_assign( LOCAL_ASSIGN; __entry->ret = ret; ), TP_printk(LOCAL_PR_FMT " - %d", LOCAL_PR_ARG, __entry->ret) ); TRACE_EVENT(drv_return_bool, TP_PROTO(struct ieee80211_local *local, bool ret), TP_ARGS(local, ret), TP_STRUCT__entry( LOCAL_ENTRY __field(bool, ret) ), TP_fast_assign( LOCAL_ASSIGN; __entry->ret = ret; ), TP_printk(LOCAL_PR_FMT " - %s", LOCAL_PR_ARG, (__entry->ret) ? "true" : "false") ); TRACE_EVENT(drv_return_u64, TP_PROTO(struct ieee80211_local *local, u64 ret), TP_ARGS(local, ret), TP_STRUCT__entry( LOCAL_ENTRY __field(u64, ret) ), TP_fast_assign( LOCAL_ASSIGN; __entry->ret = ret; ), TP_printk(LOCAL_PR_FMT " - %llu", LOCAL_PR_ARG, __entry->ret) ); DEFINE_EVENT(local_only_evt, drv_start, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); DEFINE_EVENT(local_u32_evt, drv_get_et_strings, TP_PROTO(struct ieee80211_local *local, u32 sset), TP_ARGS(local, sset) ); DEFINE_EVENT(local_u32_evt, drv_get_et_sset_count, TP_PROTO(struct ieee80211_local *local, u32 sset), TP_ARGS(local, sset) ); DEFINE_EVENT(local_only_evt, drv_get_et_stats, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); DEFINE_EVENT(local_only_evt, drv_suspend, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); DEFINE_EVENT(local_only_evt, drv_resume, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(drv_set_wakeup, TP_PROTO(struct ieee80211_local *local, bool enabled), TP_ARGS(local, enabled), TP_STRUCT__entry( LOCAL_ENTRY __field(bool, enabled) ), TP_fast_assign( LOCAL_ASSIGN; __entry->enabled = enabled; ), TP_printk(LOCAL_PR_FMT " enabled:%d", LOCAL_PR_ARG, __entry->enabled) ); DEFINE_EVENT(local_only_evt, drv_stop, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); DEFINE_EVENT(local_sdata_addr_evt, drv_add_interface, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); TRACE_EVENT(drv_change_interface, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type, bool p2p), TP_ARGS(local, sdata, type, p2p), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __field(u32, new_type) __field(bool, new_p2p) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; __entry->new_type = type; __entry->new_p2p = p2p; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT " new type:%d%s", LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type, __entry->new_p2p ? "/p2p" : "" ) ); DEFINE_EVENT(local_sdata_addr_evt, drv_remove_interface, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); TRACE_EVENT(drv_config, TP_PROTO(struct ieee80211_local *local, u32 changed), TP_ARGS(local, changed), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, changed) __field(u32, flags) __field(int, power_level) __field(int, dynamic_ps_timeout) __field(int, max_sleep_period) __field(u16, listen_interval) __field(u8, long_frame_max_tx_count) __field(u8, short_frame_max_tx_count) __field(int, center_freq) __field(int, channel_type) __field(int, smps) ), TP_fast_assign( LOCAL_ASSIGN; __entry->changed = changed; __entry->flags = local->hw.conf.flags; __entry->power_level = local->hw.conf.power_level; __entry->dynamic_ps_timeout = local->hw.conf.dynamic_ps_timeout; __entry->max_sleep_period = local->hw.conf.max_sleep_period; __entry->listen_interval = local->hw.conf.listen_interval; __entry->long_frame_max_tx_count = local->hw.conf.long_frame_max_tx_count; __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count; __entry->center_freq = local->hw.conf.channel ? local->hw.conf.channel->center_freq : 0; __entry->channel_type = local->hw.conf.channel_type; __entry->smps = local->hw.conf.smps_mode; ), TP_printk( LOCAL_PR_FMT " ch:%#x freq:%d", LOCAL_PR_ARG, __entry->changed, __entry->center_freq ) ); TRACE_EVENT(drv_bss_info_changed, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_bss_conf *info, u32 changed), TP_ARGS(local, sdata, info, changed), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __field(bool, assoc) __field(u16, aid) __field(bool, cts) __field(bool, shortpre) __field(bool, shortslot) __field(u8, dtimper) __field(u16, bcnint) __field(u16, assoc_cap) __field(u64, sync_tsf) __field(u32, sync_device_ts) __field(u32, basic_rates) __field(u32, changed) __field(bool, enable_beacon) __field(u16, ht_operation_mode) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; __entry->changed = changed; __entry->aid = info->aid; __entry->assoc = info->assoc; __entry->shortpre = info->use_short_preamble; __entry->cts = info->use_cts_prot; __entry->shortslot = info->use_short_slot; __entry->dtimper = info->dtim_period; __entry->bcnint = info->beacon_int; __entry->assoc_cap = info->assoc_capability; __entry->sync_tsf = info->sync_tsf; __entry->sync_device_ts = info->sync_device_ts; __entry->basic_rates = info->basic_rates; __entry->enable_beacon = info->enable_beacon; __entry->ht_operation_mode = info->ht_operation_mode; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT " changed:%#x", LOCAL_PR_ARG, VIF_PR_ARG, __entry->changed ) ); TRACE_EVENT(drv_prepare_multicast, TP_PROTO(struct ieee80211_local *local, int mc_count), TP_ARGS(local, mc_count), TP_STRUCT__entry( LOCAL_ENTRY __field(int, mc_count) ), TP_fast_assign( LOCAL_ASSIGN; __entry->mc_count = mc_count; ), TP_printk( LOCAL_PR_FMT " prepare mc (%d)", LOCAL_PR_ARG, __entry->mc_count ) ); TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, u64 multicast), TP_ARGS(local, changed_flags, total_flags, multicast), TP_STRUCT__entry( LOCAL_ENTRY __field(unsigned int, changed) __field(unsigned int, total) __field(u64, multicast) ), TP_fast_assign( LOCAL_ASSIGN; __entry->changed = changed_flags; __entry->total = *total_flags; __entry->multicast = multicast; ), TP_printk( LOCAL_PR_FMT " changed:%#x total:%#x", LOCAL_PR_ARG, __entry->changed, __entry->total ) ); TRACE_EVENT(drv_set_tim, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, bool set), TP_ARGS(local, sta, set), TP_STRUCT__entry( LOCAL_ENTRY STA_ENTRY __field(bool, set) ), TP_fast_assign( LOCAL_ASSIGN; STA_ASSIGN; __entry->set = set; ), TP_printk( LOCAL_PR_FMT STA_PR_FMT " set:%d", LOCAL_PR_ARG, STA_PR_FMT, __entry->set ) ); TRACE_EVENT(drv_set_key, TP_PROTO(struct ieee80211_local *local, enum set_key_cmd cmd, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta, struct ieee80211_key_conf *key), TP_ARGS(local, cmd, sdata, sta, key), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY STA_ENTRY __field(u32, cipher) __field(u8, hw_key_idx) __field(u8, flags) __field(s8, keyidx) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; __entry->cipher = key->cipher; __entry->flags = key->flags; __entry->keyidx = key->keyidx; __entry->hw_key_idx = key->hw_key_idx; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT, LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG ) ); TRACE_EVENT(drv_update_tkip_key, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_key_conf *conf, struct ieee80211_sta *sta, u32 iv32), TP_ARGS(local, sdata, conf, sta, iv32), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY STA_ENTRY __field(u32, iv32) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; __entry->iv32 = iv32; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " iv32:%#x", LOCAL_PR_ARG,VIF_PR_ARG,STA_PR_ARG, __entry->iv32 ) ); DEFINE_EVENT(local_sdata_evt, drv_hw_scan, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); DEFINE_EVENT(local_sdata_evt, drv_cancel_hw_scan, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); DEFINE_EVENT(local_only_evt, drv_sw_scan_start, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); DEFINE_EVENT(local_only_evt, drv_sw_scan_complete, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(drv_get_stats, TP_PROTO(struct ieee80211_local *local, struct ieee80211_low_level_stats *stats, int ret), TP_ARGS(local, stats, ret), TP_STRUCT__entry( LOCAL_ENTRY __field(int, ret) __field(unsigned int, ackfail) __field(unsigned int, rtsfail) __field(unsigned int, fcserr) __field(unsigned int, rtssucc) ), TP_fast_assign( LOCAL_ASSIGN; __entry->ret = ret; __entry->ackfail = stats->dot11ACKFailureCount; __entry->rtsfail = stats->dot11RTSFailureCount; __entry->fcserr = stats->dot11FCSErrorCount; __entry->rtssucc = stats->dot11RTSSuccessCount; ), TP_printk( LOCAL_PR_FMT " ret:%d", LOCAL_PR_ARG, __entry->ret ) ); TRACE_EVENT(drv_get_tkip_seq, TP_PROTO(struct ieee80211_local *local, u8 hw_key_idx, u32 *iv32, u16 *iv16), TP_ARGS(local, hw_key_idx, iv32, iv16), TP_STRUCT__entry( LOCAL_ENTRY __field(u8, hw_key_idx) __field(u32, iv32) __field(u16, iv16) ), TP_fast_assign( LOCAL_ASSIGN; __entry->hw_key_idx = hw_key_idx; __entry->iv32 = *iv32; __entry->iv16 = *iv16; ), TP_printk( LOCAL_PR_FMT, LOCAL_PR_ARG ) ); DEFINE_EVENT(local_u32_evt, drv_set_frag_threshold, TP_PROTO(struct ieee80211_local *local, u32 value), TP_ARGS(local, value) ); DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold, TP_PROTO(struct ieee80211_local *local, u32 value), TP_ARGS(local, value) ); TRACE_EVENT(drv_set_coverage_class, TP_PROTO(struct ieee80211_local *local, u8 value), TP_ARGS(local, value), TP_STRUCT__entry( LOCAL_ENTRY __field(u8, value) ), TP_fast_assign( LOCAL_ASSIGN; __entry->value = value; ), TP_printk( LOCAL_PR_FMT " value:%d", LOCAL_PR_ARG, __entry->value ) ); TRACE_EVENT(drv_sta_notify, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum sta_notify_cmd cmd, struct ieee80211_sta *sta), TP_ARGS(local, sdata, cmd, sta), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY STA_ENTRY __field(u32, cmd) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; __entry->cmd = cmd; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " cmd:%d", LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->cmd ) ); TRACE_EVENT(drv_sta_state, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state), TP_ARGS(local, sdata, sta, old_state, new_state), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY STA_ENTRY __field(u32, old_state) __field(u32, new_state) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; __entry->old_state = old_state; __entry->new_state = new_state; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " state: %d->%d", LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->old_state, __entry->new_state ) ); TRACE_EVENT(drv_sta_rc_update, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta, u32 changed), TP_ARGS(local, sdata, sta, changed), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY STA_ENTRY __field(u32, changed) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; __entry->changed = changed; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " changed: 0x%x", LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->changed ) ); TRACE_EVENT(drv_sta_add, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta), TP_ARGS(local, sdata, sta), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY STA_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT, LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG ) ); TRACE_EVENT(drv_sta_remove, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta), TP_ARGS(local, sdata, sta), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY STA_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT, LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG ) ); TRACE_EVENT(drv_conf_tx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u16 ac, const struct ieee80211_tx_queue_params *params), TP_ARGS(local, sdata, ac, params), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __field(u16, ac) __field(u16, txop) __field(u16, cw_min) __field(u16, cw_max) __field(u8, aifs) __field(bool, uapsd) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; __entry->ac = ac; __entry->txop = params->txop; __entry->cw_max = params->cw_max; __entry->cw_min = params->cw_min; __entry->aifs = params->aifs; __entry->uapsd = params->uapsd; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT " AC:%d", LOCAL_PR_ARG, VIF_PR_ARG, __entry->ac ) ); DEFINE_EVENT(local_sdata_evt, drv_get_tsf, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); TRACE_EVENT(drv_set_tsf, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u64 tsf), TP_ARGS(local, sdata, tsf), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __field(u64, tsf) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; __entry->tsf = tsf; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT " tsf:%llu", LOCAL_PR_ARG, VIF_PR_ARG, (unsigned long long)__entry->tsf ) ); DEFINE_EVENT(local_sdata_evt, drv_reset_tsf, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); DEFINE_EVENT(local_only_evt, drv_tx_last_beacon, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(drv_ampdu_action, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size), TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size), TP_STRUCT__entry( LOCAL_ENTRY STA_ENTRY __field(u32, action) __field(u16, tid) __field(u16, ssn) __field(u8, buf_size) VIF_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; STA_ASSIGN; __entry->action = action; __entry->tid = tid; __entry->ssn = ssn ? *ssn : 0; __entry->buf_size = buf_size; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d", LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->buf_size ) ); TRACE_EVENT(drv_get_survey, TP_PROTO(struct ieee80211_local *local, int idx, struct survey_info *survey), TP_ARGS(local, idx, survey), TP_STRUCT__entry( LOCAL_ENTRY __field(int, idx) ), TP_fast_assign( LOCAL_ASSIGN; __entry->idx = idx; ), TP_printk( LOCAL_PR_FMT " idx:%d", LOCAL_PR_ARG, __entry->idx ) ); TRACE_EVENT(drv_flush, TP_PROTO(struct ieee80211_local *local, bool drop), TP_ARGS(local, drop), TP_STRUCT__entry( LOCAL_ENTRY __field(bool, drop) ), TP_fast_assign( LOCAL_ASSIGN; __entry->drop = drop; ), TP_printk( LOCAL_PR_FMT " drop:%d", LOCAL_PR_ARG, __entry->drop ) ); TRACE_EVENT(drv_channel_switch, TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel_switch *ch_switch), TP_ARGS(local, ch_switch), TP_STRUCT__entry( LOCAL_ENTRY __field(u64, timestamp) __field(bool, block_tx) __field(u16, freq) __field(u8, count) ), TP_fast_assign( LOCAL_ASSIGN; __entry->timestamp = ch_switch->timestamp; __entry->block_tx = ch_switch->block_tx; __entry->freq = ch_switch->channel->center_freq; __entry->count = ch_switch->count; ), TP_printk( LOCAL_PR_FMT " new freq:%u count:%d", LOCAL_PR_ARG, __entry->freq, __entry->count ) ); TRACE_EVENT(drv_set_antenna, TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), TP_ARGS(local, tx_ant, rx_ant, ret), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, tx_ant) __field(u32, rx_ant) __field(int, ret) ), TP_fast_assign( LOCAL_ASSIGN; __entry->tx_ant = tx_ant; __entry->rx_ant = rx_ant; __entry->ret = ret; ), TP_printk( LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret ) ); TRACE_EVENT(drv_get_antenna, TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), TP_ARGS(local, tx_ant, rx_ant, ret), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, tx_ant) __field(u32, rx_ant) __field(int, ret) ), TP_fast_assign( LOCAL_ASSIGN; __entry->tx_ant = tx_ant; __entry->rx_ant = rx_ant; __entry->ret = ret; ), TP_printk( LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret ) ); TRACE_EVENT(drv_remain_on_channel, TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan, enum nl80211_channel_type chantype, unsigned int duration), TP_ARGS(local, chan, chantype, duration), TP_STRUCT__entry( LOCAL_ENTRY __field(int, center_freq) __field(int, channel_type) __field(unsigned int, duration) ), TP_fast_assign( LOCAL_ASSIGN; __entry->center_freq = chan->center_freq; __entry->channel_type = chantype; __entry->duration = duration; ), TP_printk( LOCAL_PR_FMT " freq:%dMHz duration:%dms", LOCAL_PR_ARG, __entry->center_freq, __entry->duration ) ); DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(drv_offchannel_tx, TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int wait), TP_ARGS(local, skb, chan, channel_type, wait), TP_STRUCT__entry( LOCAL_ENTRY __field(int, center_freq) __field(int, channel_type) __field(unsigned int, wait) ), TP_fast_assign( LOCAL_ASSIGN; __entry->center_freq = chan->center_freq; __entry->channel_type = channel_type; __entry->wait = wait; ), TP_printk( LOCAL_PR_FMT " freq:%dMHz, wait:%dms", LOCAL_PR_ARG, __entry->center_freq, __entry->wait ) ); TRACE_EVENT(drv_set_ringparam, TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx), TP_ARGS(local, tx, rx), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, tx) __field(u32, rx) ), TP_fast_assign( LOCAL_ASSIGN; __entry->tx = tx; __entry->rx = rx; ), TP_printk( LOCAL_PR_FMT " tx:%d rx %d", LOCAL_PR_ARG, __entry->tx, __entry->rx ) ); TRACE_EVENT(drv_get_ringparam, TP_PROTO(struct ieee80211_local *local, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max), TP_ARGS(local, tx, tx_max, rx, rx_max), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, tx) __field(u32, tx_max) __field(u32, rx) __field(u32, rx_max) ), TP_fast_assign( LOCAL_ASSIGN; __entry->tx = *tx; __entry->tx_max = *tx_max; __entry->rx = *rx; __entry->rx_max = *rx_max; ), TP_printk( LOCAL_PR_FMT " tx:%d tx_max %d rx %d rx_max %d", LOCAL_PR_ARG, __entry->tx, __entry->tx_max, __entry->rx, __entry->rx_max ) ); DEFINE_EVENT(local_only_evt, drv_tx_frames_pending, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(drv_set_bitrate_mask, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, const struct cfg80211_bitrate_mask *mask), TP_ARGS(local, sdata, mask), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __field(u32, legacy_2g) __field(u32, legacy_5g) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; __entry->legacy_2g = mask->control[IEEE80211_BAND_2GHZ].legacy; __entry->legacy_5g = mask->control[IEEE80211_BAND_5GHZ].legacy; ), TP_printk( LOCAL_PR_FMT VIF_PR_FMT " 2G Mask:0x%x 5G Mask:0x%x", LOCAL_PR_ARG, VIF_PR_ARG, __entry->legacy_2g, __entry->legacy_5g ) ); TRACE_EVENT(drv_set_rekey_data, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_gtk_rekey_data *data), TP_ARGS(local, sdata, data), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __array(u8, kek, NL80211_KEK_LEN) __array(u8, kck, NL80211_KCK_LEN) __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; memcpy(__entry->kek, data->kek, NL80211_KEK_LEN); memcpy(__entry->kck, data->kck, NL80211_KCK_LEN); memcpy(__entry->replay_ctr, data->replay_ctr, NL80211_REPLAY_CTR_LEN); ), TP_printk(LOCAL_PR_FMT VIF_PR_FMT, LOCAL_PR_ARG, VIF_PR_ARG) ); TRACE_EVENT(drv_rssi_callback, TP_PROTO(struct ieee80211_local *local, enum ieee80211_rssi_event rssi_event), TP_ARGS(local, rssi_event), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, rssi_event) ), TP_fast_assign( LOCAL_ASSIGN; __entry->rssi_event = rssi_event; ), TP_printk( LOCAL_PR_FMT " rssi_event:%d", LOCAL_PR_ARG, __entry->rssi_event ) ); DECLARE_EVENT_CLASS(release_evt, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data), TP_ARGS(local, sta, tids, num_frames, reason, more_data), TP_STRUCT__entry( LOCAL_ENTRY STA_ENTRY __field(u16, tids) __field(int, num_frames) __field(int, reason) __field(bool, more_data) ), TP_fast_assign( LOCAL_ASSIGN; STA_ASSIGN; __entry->tids = tids; __entry->num_frames = num_frames; __entry->reason = reason; __entry->more_data = more_data; ), TP_printk( LOCAL_PR_FMT STA_PR_FMT " TIDs:0x%.4x frames:%d reason:%d more:%d", LOCAL_PR_ARG, STA_PR_ARG, __entry->tids, __entry->num_frames, __entry->reason, __entry->more_data ) ); DEFINE_EVENT(release_evt, drv_release_buffered_frames, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data), TP_ARGS(local, sta, tids, num_frames, reason, more_data) ); DEFINE_EVENT(release_evt, drv_allow_buffered_frames, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data), TP_ARGS(local, sta, tids, num_frames, reason, more_data) ); TRACE_EVENT(drv_get_rssi, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, s8 rssi, int ret), TP_ARGS(local, sta, rssi, ret), TP_STRUCT__entry( LOCAL_ENTRY STA_ENTRY __field(s8, rssi) __field(int, ret) ), TP_fast_assign( LOCAL_ASSIGN; STA_ASSIGN; __entry->rssi = rssi; __entry->ret = ret; ), TP_printk( LOCAL_PR_FMT STA_PR_FMT " rssi:%d ret:%d", LOCAL_PR_ARG, STA_PR_ARG, __entry->rssi, __entry->ret ) ); DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), TP_ARGS(local, sdata) ); /* * Tracing for API calls that drivers call. */ TRACE_EVENT(api_start_tx_ba_session, TP_PROTO(struct ieee80211_sta *sta, u16 tid), TP_ARGS(sta, tid), TP_STRUCT__entry( STA_ENTRY __field(u16, tid) ), TP_fast_assign( STA_ASSIGN; __entry->tid = tid; ), TP_printk( STA_PR_FMT " tid:%d", STA_PR_ARG, __entry->tid ) ); TRACE_EVENT(api_start_tx_ba_cb, TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), TP_ARGS(sdata, ra, tid), TP_STRUCT__entry( VIF_ENTRY __array(u8, ra, ETH_ALEN) __field(u16, tid) ), TP_fast_assign( VIF_ASSIGN; memcpy(__entry->ra, ra, ETH_ALEN); __entry->tid = tid; ), TP_printk( VIF_PR_FMT " ra:%pM tid:%d", VIF_PR_ARG, __entry->ra, __entry->tid ) ); TRACE_EVENT(api_stop_tx_ba_session, TP_PROTO(struct ieee80211_sta *sta, u16 tid), TP_ARGS(sta, tid), TP_STRUCT__entry( STA_ENTRY __field(u16, tid) ), TP_fast_assign( STA_ASSIGN; __entry->tid = tid; ), TP_printk( STA_PR_FMT " tid:%d", STA_PR_ARG, __entry->tid ) ); TRACE_EVENT(api_stop_tx_ba_cb, TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), TP_ARGS(sdata, ra, tid), TP_STRUCT__entry( VIF_ENTRY __array(u8, ra, ETH_ALEN) __field(u16, tid) ), TP_fast_assign( VIF_ASSIGN; memcpy(__entry->ra, ra, ETH_ALEN); __entry->tid = tid; ), TP_printk( VIF_PR_FMT " ra:%pM tid:%d", VIF_PR_ARG, __entry->ra, __entry->tid ) ); DEFINE_EVENT(local_only_evt, api_restart_hw, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(api_beacon_loss, TP_PROTO(struct ieee80211_sub_if_data *sdata), TP_ARGS(sdata), TP_STRUCT__entry( VIF_ENTRY ), TP_fast_assign( VIF_ASSIGN; ), TP_printk( VIF_PR_FMT, VIF_PR_ARG ) ); TRACE_EVENT(api_connection_loss, TP_PROTO(struct ieee80211_sub_if_data *sdata), TP_ARGS(sdata), TP_STRUCT__entry( VIF_ENTRY ), TP_fast_assign( VIF_ASSIGN; ), TP_printk( VIF_PR_FMT, VIF_PR_ARG ) ); TRACE_EVENT(api_cqm_rssi_notify, TP_PROTO(struct ieee80211_sub_if_data *sdata, enum nl80211_cqm_rssi_threshold_event rssi_event), TP_ARGS(sdata, rssi_event), TP_STRUCT__entry( VIF_ENTRY __field(u32, rssi_event) ), TP_fast_assign( VIF_ASSIGN; __entry->rssi_event = rssi_event; ), TP_printk( VIF_PR_FMT " event:%d", VIF_PR_ARG, __entry->rssi_event ) ); TRACE_EVENT(api_scan_completed, TP_PROTO(struct ieee80211_local *local, bool aborted), TP_ARGS(local, aborted), TP_STRUCT__entry( LOCAL_ENTRY __field(bool, aborted) ), TP_fast_assign( LOCAL_ASSIGN; __entry->aborted = aborted; ), TP_printk( LOCAL_PR_FMT " aborted:%d", LOCAL_PR_ARG, __entry->aborted ) ); TRACE_EVENT(api_sched_scan_results, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local), TP_STRUCT__entry( LOCAL_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; ), TP_printk( LOCAL_PR_FMT, LOCAL_PR_ARG ) ); TRACE_EVENT(api_sched_scan_stopped, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local), TP_STRUCT__entry( LOCAL_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; ), TP_printk( LOCAL_PR_FMT, LOCAL_PR_ARG ) ); TRACE_EVENT(api_sta_block_awake, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, bool block), TP_ARGS(local, sta, block), TP_STRUCT__entry( LOCAL_ENTRY STA_ENTRY __field(bool, block) ), TP_fast_assign( LOCAL_ASSIGN; STA_ASSIGN; __entry->block = block; ), TP_printk( LOCAL_PR_FMT STA_PR_FMT " block:%d", LOCAL_PR_ARG, STA_PR_FMT, __entry->block ) ); TRACE_EVENT(api_chswitch_done, TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success), TP_ARGS(sdata, success), TP_STRUCT__entry( VIF_ENTRY __field(bool, success) ), TP_fast_assign( VIF_ASSIGN; __entry->success = success; ), TP_printk( VIF_PR_FMT " success=%d", VIF_PR_ARG, __entry->success ) ); DEFINE_EVENT(local_only_evt, api_ready_on_channel, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); DEFINE_EVENT(local_only_evt, api_remain_on_channel_expired, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); TRACE_EVENT(api_gtk_rekey_notify, TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const u8 *replay_ctr), TP_ARGS(sdata, bssid, replay_ctr), TP_STRUCT__entry( VIF_ENTRY __array(u8, bssid, ETH_ALEN) __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN) ), TP_fast_assign( VIF_ASSIGN; memcpy(__entry->bssid, bssid, ETH_ALEN); memcpy(__entry->replay_ctr, replay_ctr, NL80211_REPLAY_CTR_LEN); ), TP_printk(VIF_PR_FMT, VIF_PR_ARG) ); TRACE_EVENT(api_enable_rssi_reports, TP_PROTO(struct ieee80211_sub_if_data *sdata, int rssi_min_thold, int rssi_max_thold), TP_ARGS(sdata, rssi_min_thold, rssi_max_thold), TP_STRUCT__entry( VIF_ENTRY __field(int, rssi_min_thold) __field(int, rssi_max_thold) ), TP_fast_assign( VIF_ASSIGN; __entry->rssi_min_thold = rssi_min_thold; __entry->rssi_max_thold = rssi_max_thold; ), TP_printk( VIF_PR_FMT " rssi_min_thold =%d, rssi_max_thold = %d", VIF_PR_ARG, __entry->rssi_min_thold, __entry->rssi_max_thold ) ); TRACE_EVENT(api_eosp, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta), TP_ARGS(local, sta), TP_STRUCT__entry( LOCAL_ENTRY STA_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; STA_ASSIGN; ), TP_printk( LOCAL_PR_FMT STA_PR_FMT, LOCAL_PR_ARG, STA_PR_FMT ) ); /* * Tracing for internal functions * (which may also be called in response to driver calls) */ TRACE_EVENT(wake_queue, TP_PROTO(struct ieee80211_local *local, u16 queue, enum queue_stop_reason reason), TP_ARGS(local, queue, reason), TP_STRUCT__entry( LOCAL_ENTRY __field(u16, queue) __field(u32, reason) ), TP_fast_assign( LOCAL_ASSIGN; __entry->queue = queue; __entry->reason = reason; ), TP_printk( LOCAL_PR_FMT " queue:%d, reason:%d", LOCAL_PR_ARG, __entry->queue, __entry->reason ) ); TRACE_EVENT(stop_queue, TP_PROTO(struct ieee80211_local *local, u16 queue, enum queue_stop_reason reason), TP_ARGS(local, queue, reason), TP_STRUCT__entry( LOCAL_ENTRY __field(u16, queue) __field(u32, reason) ), TP_fast_assign( LOCAL_ASSIGN; __entry->queue = queue; __entry->reason = reason; ), TP_printk( LOCAL_PR_FMT " queue:%d, reason:%d", LOCAL_PR_ARG, __entry->queue, __entry->reason ) ); #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg #define MAX_MSG_LEN 100 DECLARE_EVENT_CLASS(mac80211_msg_event, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf), TP_STRUCT__entry( __dynamic_array(char, msg, MAX_MSG_LEN) ), TP_fast_assign( WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), MAX_MSG_LEN, vaf->fmt, *vaf->va) >= MAX_MSG_LEN); ), TP_printk("%s", __get_str(msg)) ); DEFINE_EVENT(mac80211_msg_event, mac80211_info, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) ); DEFINE_EVENT(mac80211_msg_event, mac80211_dbg, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) ); DEFINE_EVENT(mac80211_msg_event, mac80211_err, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) ); #endif #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH . #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace #include compat-drivers-2012-09-18/net/bluetooth/0000755000175000017500000000000012026211315017153 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/bluetooth/lib.c0000644000175000017500000000630212026211315020066 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth kernel library. */ #undef pr_fmt #define pr_fmt(fmt) "Bluetooth: " fmt #include #include #include void baswap(bdaddr_t *dst, bdaddr_t *src) { unsigned char *d = (unsigned char *) dst; unsigned char *s = (unsigned char *) src; unsigned int i; for (i = 0; i < 6; i++) d[i] = s[5 - i]; } EXPORT_SYMBOL(baswap); char *batostr(bdaddr_t *ba) { static char str[2][18]; static int i = 1; i ^= 1; sprintf(str[i], "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]); return str[i]; } EXPORT_SYMBOL(batostr); /* Bluetooth error codes to Unix errno mapping */ int bt_to_errno(__u16 code) { switch (code) { case 0: return 0; case 0x01: return EBADRQC; case 0x02: return ENOTCONN; case 0x03: return EIO; case 0x04: return EHOSTDOWN; case 0x05: return EACCES; case 0x06: return EBADE; case 0x07: return ENOMEM; case 0x08: return ETIMEDOUT; case 0x09: return EMLINK; case 0x0a: return EMLINK; case 0x0b: return EALREADY; case 0x0c: return EBUSY; case 0x0d: case 0x0e: case 0x0f: return ECONNREFUSED; case 0x10: return ETIMEDOUT; case 0x11: case 0x27: case 0x29: case 0x20: return EOPNOTSUPP; case 0x12: return EINVAL; case 0x13: case 0x14: case 0x15: return ECONNRESET; case 0x16: return ECONNABORTED; case 0x17: return ELOOP; case 0x18: return EACCES; case 0x1a: return EPROTONOSUPPORT; case 0x1b: return ECONNREFUSED; case 0x19: case 0x1e: case 0x23: case 0x24: case 0x25: return EPROTO; default: return ENOSYS; } } EXPORT_SYMBOL(bt_to_errno); int bt_info(const char *format, ...) { struct va_format vaf; va_list args; int r; va_start(args, format); vaf.fmt = format; vaf.va = &args; r = pr_info("%pV", &vaf); va_end(args); return r; } EXPORT_SYMBOL(bt_info); int bt_err(const char *format, ...) { struct va_format vaf; va_list args; int r; va_start(args, format); vaf.fmt = format; vaf.va = &args; r = pr_err("%pV", &vaf); va_end(args); return r; } EXPORT_SYMBOL(bt_err); compat-drivers-2012-09-18/net/bluetooth/hci_core.c0000644000175000017500000017151712026211315021106 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2011 ProFUSION Embedded Systems Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth HCI core. */ #include #include #include #include #include static void hci_rx_work(struct work_struct *work); static void hci_cmd_work(struct work_struct *work); static void hci_tx_work(struct work_struct *work); /* HCI device list */ LIST_HEAD(hci_dev_list); DEFINE_RWLOCK(hci_dev_list_lock); /* HCI callback list */ LIST_HEAD(hci_cb_list); DEFINE_RWLOCK(hci_cb_list_lock); /* HCI ID Numbering */ static DEFINE_IDA(hci_index_ida); /* ---- HCI notifications ---- */ static void hci_notify(struct hci_dev *hdev, int event) { hci_sock_dev_event(hdev, event); } /* ---- HCI requests ---- */ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) { BT_DBG("%s command 0x%4.4x result 0x%2.2x", hdev->name, cmd, result); /* If this is the init phase check if the completed command matches * the last init command, and if not just return. */ if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) { struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; u16 opcode = __le16_to_cpu(sent->opcode); struct sk_buff *skb; /* Some CSR based controllers generate a spontaneous * reset complete event during init and any pending * command will never be completed. In such a case we * need to resend whatever was the last sent * command. */ if (cmd != HCI_OP_RESET || opcode == HCI_OP_RESET) return; skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC); if (skb) { skb_queue_head(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); } return; } if (hdev->req_status == HCI_REQ_PEND) { hdev->req_result = result; hdev->req_status = HCI_REQ_DONE; wake_up_interruptible(&hdev->req_wait_q); } } static void hci_req_cancel(struct hci_dev *hdev, int err) { BT_DBG("%s err 0x%2.2x", hdev->name, err); if (hdev->req_status == HCI_REQ_PEND) { hdev->req_result = err; hdev->req_status = HCI_REQ_CANCELED; wake_up_interruptible(&hdev->req_wait_q); } } /* Execute request and wait for completion. */ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), unsigned long opt, __u32 timeout) { DECLARE_WAITQUEUE(wait, current); int err = 0; BT_DBG("%s start", hdev->name); hdev->req_status = HCI_REQ_PEND; add_wait_queue(&hdev->req_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); req(hdev, opt); schedule_timeout(timeout); remove_wait_queue(&hdev->req_wait_q, &wait); if (signal_pending(current)) return -EINTR; switch (hdev->req_status) { case HCI_REQ_DONE: err = -bt_to_errno(hdev->req_result); break; case HCI_REQ_CANCELED: err = -hdev->req_result; break; default: err = -ETIMEDOUT; break; } hdev->req_status = hdev->req_result = 0; BT_DBG("%s end: err %d", hdev->name, err); return err; } static int hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), unsigned long opt, __u32 timeout) { int ret; if (!test_bit(HCI_UP, &hdev->flags)) return -ENETDOWN; /* Serialize all requests */ hci_req_lock(hdev); ret = __hci_request(hdev, req, opt, timeout); hci_req_unlock(hdev); return ret; } static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) { BT_DBG("%s %ld", hdev->name, opt); /* Reset device */ set_bit(HCI_RESET, &hdev->flags); hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); } static void bredr_init(struct hci_dev *hdev) { struct hci_cp_delete_stored_link_key cp; __le16 param; __u8 flt_type; hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; /* Mandatory initialization */ /* Read Local Supported Features */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); /* Read Local Version */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); /* Read Buffer Size (ACL mtu, max pkt, etc.) */ hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); /* Read BD Address */ hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); /* Read Class of Device */ hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); /* Read Local Name */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); /* Read Voice Setting */ hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); /* Optional initialization */ /* Clear Event Filters */ flt_type = HCI_FLT_CLEAR_ALL; hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); /* Connection accept timeout ~20 secs */ param = __constant_cpu_to_le16(0x7d00); hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); bacpy(&cp.bdaddr, BDADDR_ANY); cp.delete_all = 1; hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); } static void amp_init(struct hci_dev *hdev) { hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; /* Read Local Version */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); /* Read Local AMP Info */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); /* Read Data Blk size */ hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL); } static void hci_init_req(struct hci_dev *hdev, unsigned long opt) { struct sk_buff *skb; BT_DBG("%s %ld", hdev->name, opt); /* Driver initialization */ /* Special commands */ while ((skb = skb_dequeue(&hdev->driver_init))) { bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; skb->dev = (void *) hdev; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); } skb_queue_purge(&hdev->driver_init); /* Reset */ if (!test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) hci_reset_req(hdev, 0); switch (hdev->dev_type) { case HCI_BREDR: bredr_init(hdev); break; case HCI_AMP: amp_init(hdev); break; default: BT_ERR("Unknown device type %d", hdev->dev_type); break; } } static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt) { BT_DBG("%s", hdev->name); /* Read LE buffer size */ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); } static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) { __u8 scan = opt; BT_DBG("%s %x", hdev->name, scan); /* Inquiry and Page scans */ hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } static void hci_auth_req(struct hci_dev *hdev, unsigned long opt) { __u8 auth = opt; BT_DBG("%s %x", hdev->name, auth); /* Authentication */ hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); } static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) { __u8 encrypt = opt; BT_DBG("%s %x", hdev->name, encrypt); /* Encryption */ hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); } static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) { __le16 policy = cpu_to_le16(opt); BT_DBG("%s %x", hdev->name, policy); /* Default link policy */ hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); } /* Get HCI device by index. * Device is held on return. */ struct hci_dev *hci_dev_get(int index) { struct hci_dev *hdev = NULL, *d; BT_DBG("%d", index); if (index < 0) return NULL; read_lock(&hci_dev_list_lock); list_for_each_entry(d, &hci_dev_list, list) { if (d->id == index) { hdev = hci_dev_hold(d); break; } } read_unlock(&hci_dev_list_lock); return hdev; } /* ---- Inquiry support ---- */ bool hci_discovery_active(struct hci_dev *hdev) { struct discovery_state *discov = &hdev->discovery; switch (discov->state) { case DISCOVERY_FINDING: case DISCOVERY_RESOLVING: return true; default: return false; } } void hci_discovery_set_state(struct hci_dev *hdev, int state) { BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state); if (hdev->discovery.state == state) return; switch (state) { case DISCOVERY_STOPPED: if (hdev->discovery.state != DISCOVERY_STARTING) mgmt_discovering(hdev, 0); break; case DISCOVERY_STARTING: break; case DISCOVERY_FINDING: mgmt_discovering(hdev, 1); break; case DISCOVERY_RESOLVING: break; case DISCOVERY_STOPPING: break; } hdev->discovery.state = state; } static void inquiry_cache_flush(struct hci_dev *hdev) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *p, *n; list_for_each_entry_safe(p, n, &cache->all, all) { list_del(&p->all); kfree(p); } INIT_LIST_HEAD(&cache->unknown); INIT_LIST_HEAD(&cache->resolve); } struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; BT_DBG("cache %p, %s", cache, batostr(bdaddr)); list_for_each_entry(e, &cache->all, all) { if (!bacmp(&e->data.bdaddr, bdaddr)) return e; } return NULL; } struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; BT_DBG("cache %p, %s", cache, batostr(bdaddr)); list_for_each_entry(e, &cache->unknown, list) { if (!bacmp(&e->data.bdaddr, bdaddr)) return e; } return NULL; } struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, bdaddr_t *bdaddr, int state) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state); list_for_each_entry(e, &cache->resolve, list) { if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state) return e; if (!bacmp(&e->data.bdaddr, bdaddr)) return e; } return NULL; } void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, struct inquiry_entry *ie) { struct discovery_state *cache = &hdev->discovery; struct list_head *pos = &cache->resolve; struct inquiry_entry *p; list_del(&ie->list); list_for_each_entry(p, &cache->resolve, list) { if (p->name_state != NAME_PENDING && abs(p->data.rssi) >= abs(ie->data.rssi)) break; pos = &p->list; } list_add(&ie->list, pos); } bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known, bool *ssp) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); if (ssp) *ssp = data->ssp_mode; ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); if (ie) { if (ie->data.ssp_mode && ssp) *ssp = true; if (ie->name_state == NAME_NEEDED && data->rssi != ie->data.rssi) { ie->data.rssi = data->rssi; hci_inquiry_cache_update_resolve(hdev, ie); } goto update; } /* Entry not in the cache. Add new one. */ ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); if (!ie) return false; list_add(&ie->all, &cache->all); if (name_known) { ie->name_state = NAME_KNOWN; } else { ie->name_state = NAME_NOT_KNOWN; list_add(&ie->list, &cache->unknown); } update: if (name_known && ie->name_state != NAME_KNOWN && ie->name_state != NAME_PENDING) { ie->name_state = NAME_KNOWN; list_del(&ie->list); } memcpy(&ie->data, data, sizeof(*data)); ie->timestamp = jiffies; cache->timestamp = jiffies; if (ie->name_state == NAME_NOT_KNOWN) return false; return true; } static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) { struct discovery_state *cache = &hdev->discovery; struct inquiry_info *info = (struct inquiry_info *) buf; struct inquiry_entry *e; int copied = 0; list_for_each_entry(e, &cache->all, all) { struct inquiry_data *data = &e->data; if (copied >= num) break; bacpy(&info->bdaddr, &data->bdaddr); info->pscan_rep_mode = data->pscan_rep_mode; info->pscan_period_mode = data->pscan_period_mode; info->pscan_mode = data->pscan_mode; memcpy(info->dev_class, data->dev_class, 3); info->clock_offset = data->clock_offset; info++; copied++; } BT_DBG("cache %p, copied %d", cache, copied); return copied; } static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) { struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt; struct hci_cp_inquiry cp; BT_DBG("%s", hdev->name); if (test_bit(HCI_INQUIRY, &hdev->flags)) return; /* Start Inquiry */ memcpy(&cp.lap, &ir->lap, 3); cp.length = ir->length; cp.num_rsp = ir->num_rsp; hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); } int hci_inquiry(void __user *arg) { __u8 __user *ptr = arg; struct hci_inquiry_req ir; struct hci_dev *hdev; int err = 0, do_inquiry = 0, max_rsp; long timeo; __u8 *buf; if (copy_from_user(&ir, ptr, sizeof(ir))) return -EFAULT; hdev = hci_dev_get(ir.dev_id); if (!hdev) return -ENODEV; hci_dev_lock(hdev); if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) { inquiry_cache_flush(hdev); do_inquiry = 1; } hci_dev_unlock(hdev); timeo = ir.length * msecs_to_jiffies(2000); if (do_inquiry) { err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo); if (err < 0) goto done; } /* for unlimited number of responses we will use buffer with * 255 entries */ max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp; /* cache_dump can't sleep. Therefore we allocate temp buffer and then * copy it to the user space. */ buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL); if (!buf) { err = -ENOMEM; goto done; } hci_dev_lock(hdev); ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf); hci_dev_unlock(hdev); BT_DBG("num_rsp %d", ir.num_rsp); if (!copy_to_user(ptr, &ir, sizeof(ir))) { ptr += sizeof(ir); if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) * ir.num_rsp)) err = -EFAULT; } else err = -EFAULT; kfree(buf); done: hci_dev_put(hdev); return err; } /* ---- HCI ioctl helpers ---- */ int hci_dev_open(__u16 dev) { struct hci_dev *hdev; int ret = 0; hdev = hci_dev_get(dev); if (!hdev) return -ENODEV; BT_DBG("%s %p", hdev->name, hdev); hci_req_lock(hdev); if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) { ret = -ENODEV; goto done; } if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) { ret = -ERFKILL; goto done; } if (test_bit(HCI_UP, &hdev->flags)) { ret = -EALREADY; goto done; } if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) set_bit(HCI_RAW, &hdev->flags); /* Treat all non BR/EDR controllers as raw devices if enable_hs is not set */ if (hdev->dev_type != HCI_BREDR && !enable_hs) set_bit(HCI_RAW, &hdev->flags); if (hdev->open(hdev)) { ret = -EIO; goto done; } if (!test_bit(HCI_RAW, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); hdev->init_last_cmd = 0; ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); if (lmp_host_le_capable(hdev)) ret = __hci_request(hdev, hci_le_init_req, 0, HCI_INIT_TIMEOUT); clear_bit(HCI_INIT, &hdev->flags); } if (!ret) { hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); mgmt_powered(hdev, 1); hci_dev_unlock(hdev); } } else { /* Init failed, cleanup */ flush_work(&hdev->tx_work); flush_work(&hdev->cmd_work); flush_work(&hdev->rx_work); skb_queue_purge(&hdev->cmd_q); skb_queue_purge(&hdev->rx_q); if (hdev->flush) hdev->flush(hdev); if (hdev->sent_cmd) { kfree_skb(hdev->sent_cmd); hdev->sent_cmd = NULL; } hdev->close(hdev); hdev->flags = 0; } done: hci_req_unlock(hdev); hci_dev_put(hdev); return ret; } static int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); cancel_work_sync(&hdev->le_scan); hci_req_cancel(hdev, ENODEV); hci_req_lock(hdev); if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { del_timer_sync(&hdev->cmd_timer); hci_req_unlock(hdev); return 0; } /* Flush RX and TX works */ flush_work(&hdev->tx_work); flush_work(&hdev->rx_work); if (hdev->discov_timeout > 0) { cancel_delayed_work(&hdev->discov_off); hdev->discov_timeout = 0; clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) cancel_delayed_work(&hdev->service_cache); cancel_delayed_work_sync(&hdev->le_scan_disable); hci_dev_lock(hdev); inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); hci_dev_unlock(hdev); hci_notify(hdev, HCI_DEV_DOWN); if (hdev->flush) hdev->flush(hdev); /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); if (!test_bit(HCI_RAW, &hdev->flags) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); __hci_request(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); clear_bit(HCI_INIT, &hdev->flags); } /* flush cmd work */ flush_work(&hdev->cmd_work); /* Drop queues */ skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->cmd_q); skb_queue_purge(&hdev->raw_q); /* Drop last sent command */ if (hdev->sent_cmd) { del_timer_sync(&hdev->cmd_timer); kfree_skb(hdev->sent_cmd); hdev->sent_cmd = NULL; } /* After this point our queues are empty * and no tasks are scheduled. */ hdev->close(hdev); if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) && mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); mgmt_powered(hdev, 0); hci_dev_unlock(hdev); } /* Clear flags */ hdev->flags = 0; memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); hci_req_unlock(hdev); hci_dev_put(hdev); return 0; } int hci_dev_close(__u16 dev) { struct hci_dev *hdev; int err; hdev = hci_dev_get(dev); if (!hdev) return -ENODEV; if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work(&hdev->power_off); err = hci_dev_do_close(hdev); hci_dev_put(hdev); return err; } int hci_dev_reset(__u16 dev) { struct hci_dev *hdev; int ret = 0; hdev = hci_dev_get(dev); if (!hdev) return -ENODEV; hci_req_lock(hdev); if (!test_bit(HCI_UP, &hdev->flags)) goto done; /* Drop queues */ skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->cmd_q); hci_dev_lock(hdev); inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); hci_dev_unlock(hdev); if (hdev->flush) hdev->flush(hdev); atomic_set(&hdev->cmd_cnt, 1); hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0; if (!test_bit(HCI_RAW, &hdev->flags)) ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); done: hci_req_unlock(hdev); hci_dev_put(hdev); return ret; } int hci_dev_reset_stat(__u16 dev) { struct hci_dev *hdev; int ret = 0; hdev = hci_dev_get(dev); if (!hdev) return -ENODEV; memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); hci_dev_put(hdev); return ret; } int hci_dev_cmd(unsigned int cmd, void __user *arg) { struct hci_dev *hdev; struct hci_dev_req dr; int err = 0; if (copy_from_user(&dr, arg, sizeof(dr))) return -EFAULT; hdev = hci_dev_get(dr.dev_id); if (!hdev) return -ENODEV; switch (cmd) { case HCISETAUTH: err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT); break; case HCISETENCRYPT: if (!lmp_encrypt_capable(hdev)) { err = -EOPNOTSUPP; break; } if (!test_bit(HCI_AUTH, &hdev->flags)) { /* Auth must be enabled first */ err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT); if (err) break; } err = hci_request(hdev, hci_encrypt_req, dr.dev_opt, HCI_INIT_TIMEOUT); break; case HCISETSCAN: err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); break; case HCISETLINKPOL: err = hci_request(hdev, hci_linkpol_req, dr.dev_opt, HCI_INIT_TIMEOUT); break; case HCISETLINKMODE: hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT); break; case HCISETPTYPE: hdev->pkt_type = (__u16) dr.dev_opt; break; case HCISETACLMTU: hdev->acl_mtu = *((__u16 *) &dr.dev_opt + 1); hdev->acl_pkts = *((__u16 *) &dr.dev_opt + 0); break; case HCISETSCOMTU: hdev->sco_mtu = *((__u16 *) &dr.dev_opt + 1); hdev->sco_pkts = *((__u16 *) &dr.dev_opt + 0); break; default: err = -EINVAL; break; } hci_dev_put(hdev); return err; } int hci_get_dev_list(void __user *arg) { struct hci_dev *hdev; struct hci_dev_list_req *dl; struct hci_dev_req *dr; int n = 0, size, err; __u16 dev_num; if (get_user(dev_num, (__u16 __user *) arg)) return -EFAULT; if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr)) return -EINVAL; size = sizeof(*dl) + dev_num * sizeof(*dr); dl = kzalloc(size, GFP_KERNEL); if (!dl) return -ENOMEM; dr = dl->dev_req; read_lock(&hci_dev_list_lock); list_for_each_entry(hdev, &hci_dev_list, list) { if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work(&hdev->power_off); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) set_bit(HCI_PAIRABLE, &hdev->dev_flags); (dr + n)->dev_id = hdev->id; (dr + n)->dev_opt = hdev->flags; if (++n >= dev_num) break; } read_unlock(&hci_dev_list_lock); dl->dev_num = n; size = sizeof(*dl) + n * sizeof(*dr); err = copy_to_user(arg, dl, size); kfree(dl); return err ? -EFAULT : 0; } int hci_get_dev_info(void __user *arg) { struct hci_dev *hdev; struct hci_dev_info di; int err = 0; if (copy_from_user(&di, arg, sizeof(di))) return -EFAULT; hdev = hci_dev_get(di.dev_id); if (!hdev) return -ENODEV; if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work_sync(&hdev->power_off); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) set_bit(HCI_PAIRABLE, &hdev->dev_flags); strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); di.flags = hdev->flags; di.pkt_type = hdev->pkt_type; di.acl_mtu = hdev->acl_mtu; di.acl_pkts = hdev->acl_pkts; di.sco_mtu = hdev->sco_mtu; di.sco_pkts = hdev->sco_pkts; di.link_policy = hdev->link_policy; di.link_mode = hdev->link_mode; memcpy(&di.stat, &hdev->stat, sizeof(di.stat)); memcpy(&di.features, &hdev->features, sizeof(di.features)); if (copy_to_user(arg, &di, sizeof(di))) err = -EFAULT; hci_dev_put(hdev); return err; } /* ---- Interface to HCI drivers ---- */ static int hci_rfkill_set_block(void *data, bool blocked) { struct hci_dev *hdev = data; BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked); if (!blocked) return 0; hci_dev_do_close(hdev); return 0; } static const struct rfkill_ops hci_rfkill_ops = { .set_block = hci_rfkill_set_block, }; static void hci_power_on(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, power_on); BT_DBG("%s", hdev->name); if (hci_dev_open(hdev->id) < 0) return; if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) schedule_delayed_work(&hdev->power_off, HCI_AUTO_OFF_TIMEOUT); if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) mgmt_index_added(hdev); } static void hci_power_off(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, power_off.work); BT_DBG("%s", hdev->name); hci_dev_do_close(hdev); } static void hci_discov_off(struct work_struct *work) { struct hci_dev *hdev; u8 scan = SCAN_PAGE; hdev = container_of(work, struct hci_dev, discov_off.work); BT_DBG("%s", hdev->name); hci_dev_lock(hdev); hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); hdev->discov_timeout = 0; hci_dev_unlock(hdev); } int hci_uuids_clear(struct hci_dev *hdev) { struct list_head *p, *n; list_for_each_safe(p, n, &hdev->uuids) { struct bt_uuid *uuid; uuid = list_entry(p, struct bt_uuid, list); list_del(p); kfree(uuid); } return 0; } int hci_link_keys_clear(struct hci_dev *hdev) { struct list_head *p, *n; list_for_each_safe(p, n, &hdev->link_keys) { struct link_key *key; key = list_entry(p, struct link_key, list); list_del(p); kfree(key); } return 0; } int hci_smp_ltks_clear(struct hci_dev *hdev) { struct smp_ltk *k, *tmp; list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { list_del(&k->list); kfree(k); } return 0; } struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct link_key *k; list_for_each_entry(k, &hdev->link_keys, list) if (bacmp(bdaddr, &k->bdaddr) == 0) return k; return NULL; } static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, u8 key_type, u8 old_key_type) { /* Legacy key */ if (key_type < 0x03) return true; /* Debug keys are insecure so don't store them persistently */ if (key_type == HCI_LK_DEBUG_COMBINATION) return false; /* Changed combination key and there's no previous one */ if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff) return false; /* Security mode 3 case */ if (!conn) return true; /* Neither local nor remote side had no-bonding as requirement */ if (conn->auth_type > 0x01 && conn->remote_auth > 0x01) return true; /* Local side had dedicated bonding as requirement */ if (conn->auth_type == 0x02 || conn->auth_type == 0x03) return true; /* Remote side had dedicated bonding as requirement */ if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) return true; /* If none of the above criteria match, then don't store the key * persistently */ return false; } struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) { struct smp_ltk *k; list_for_each_entry(k, &hdev->long_term_keys, list) { if (k->ediv != ediv || memcmp(rand, k->rand, sizeof(k->rand))) continue; return k; } return NULL; } struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) { struct smp_ltk *k; list_for_each_entry(k, &hdev->long_term_keys, list) if (addr_type == k->bdaddr_type && bacmp(bdaddr, &k->bdaddr) == 0) return k; return NULL; } int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { struct link_key *key, *old_key; u8 old_key_type; bool persistent; old_key = hci_find_link_key(hdev, bdaddr); if (old_key) { old_key_type = old_key->type; key = old_key; } else { old_key_type = conn ? conn->key_type : 0xff; key = kzalloc(sizeof(*key), GFP_ATOMIC); if (!key) return -ENOMEM; list_add(&key->list, &hdev->link_keys); } BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); /* Some buggy controller combinations generate a changed * combination key for legacy pairing even when there's no * previous key */ if (type == HCI_LK_CHANGED_COMBINATION && (!conn || conn->remote_auth == 0xff) && old_key_type == 0xff) { type = HCI_LK_COMBINATION; if (conn) conn->key_type = type; } bacpy(&key->bdaddr, bdaddr); memcpy(key->val, val, HCI_LINK_KEY_SIZE); key->pin_len = pin_len; if (type == HCI_LK_CHANGED_COMBINATION) key->type = old_key_type; else key->type = type; if (!new_key) return 0; persistent = hci_persistent_key(hdev, conn, type, old_key_type); mgmt_new_link_key(hdev, key, persistent); if (conn) conn->flush_key = !persistent; return 0; } int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, int new_key, u8 authenticated, u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]) { struct smp_ltk *key, *old_key; if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK)) return 0; old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type); if (old_key) key = old_key; else { key = kzalloc(sizeof(*key), GFP_ATOMIC); if (!key) return -ENOMEM; list_add(&key->list, &hdev->long_term_keys); } bacpy(&key->bdaddr, bdaddr); key->bdaddr_type = addr_type; memcpy(key->val, tk, sizeof(key->val)); key->authenticated = authenticated; key->ediv = ediv; key->enc_size = enc_size; key->type = type; memcpy(key->rand, rand, sizeof(key->rand)); if (!new_key) return 0; if (type & HCI_SMP_LTK) mgmt_new_ltk(hdev, key, 1); return 0; } int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct link_key *key; key = hci_find_link_key(hdev, bdaddr); if (!key) return -ENOENT; BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); list_del(&key->list); kfree(key); return 0; } int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct smp_ltk *k, *tmp; list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { if (bacmp(bdaddr, &k->bdaddr)) continue; BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); list_del(&k->list); kfree(k); } return 0; } /* HCI command timer function */ static void hci_cmd_timeout(unsigned long arg) { struct hci_dev *hdev = (void *) arg; if (hdev->sent_cmd) { struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; u16 opcode = __le16_to_cpu(sent->opcode); BT_ERR("%s command 0x%4.4x tx timeout", hdev->name, opcode); } else { BT_ERR("%s command tx timeout", hdev->name); } atomic_set(&hdev->cmd_cnt, 1); queue_work(hdev->workqueue, &hdev->cmd_work); } struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct oob_data *data; list_for_each_entry(data, &hdev->remote_oob_data, list) if (bacmp(bdaddr, &data->bdaddr) == 0) return data; return NULL; } int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct oob_data *data; data = hci_find_remote_oob_data(hdev, bdaddr); if (!data) return -ENOENT; BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); list_del(&data->list); kfree(data); return 0; } int hci_remote_oob_data_clear(struct hci_dev *hdev) { struct oob_data *data, *n; list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) { list_del(&data->list); kfree(data); } return 0; } int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, u8 *randomizer) { struct oob_data *data; data = hci_find_remote_oob_data(hdev, bdaddr); if (!data) { data = kmalloc(sizeof(*data), GFP_ATOMIC); if (!data) return -ENOMEM; bacpy(&data->bdaddr, bdaddr); list_add(&data->list, &hdev->remote_oob_data); } memcpy(data->hash, hash, sizeof(data->hash)); memcpy(data->randomizer, randomizer, sizeof(data->randomizer)); BT_DBG("%s for %s", hdev->name, batostr(bdaddr)); return 0; } struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *b; list_for_each_entry(b, &hdev->blacklist, list) if (bacmp(bdaddr, &b->bdaddr) == 0) return b; return NULL; } int hci_blacklist_clear(struct hci_dev *hdev) { struct list_head *p, *n; list_for_each_safe(p, n, &hdev->blacklist) { struct bdaddr_list *b; b = list_entry(p, struct bdaddr_list, list); list_del(p); kfree(b); } return 0; } int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; if (bacmp(bdaddr, BDADDR_ANY) == 0) return -EBADF; if (hci_blacklist_lookup(hdev, bdaddr)) return -EEXIST; entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); if (!entry) return -ENOMEM; bacpy(&entry->bdaddr, bdaddr); list_add(&entry->list, &hdev->blacklist); return mgmt_device_blocked(hdev, bdaddr, type); } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; if (bacmp(bdaddr, BDADDR_ANY) == 0) return hci_blacklist_clear(hdev); entry = hci_blacklist_lookup(hdev, bdaddr); if (!entry) return -ENOENT; list_del(&entry->list); kfree(entry); return mgmt_device_unblocked(hdev, bdaddr, type); } static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) { struct le_scan_params *param = (struct le_scan_params *) opt; struct hci_cp_le_set_scan_param cp; memset(&cp, 0, sizeof(cp)); cp.type = param->type; cp.interval = cpu_to_le16(param->interval); cp.window = cpu_to_le16(param->window); hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); } static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) { struct hci_cp_le_set_scan_enable cp; memset(&cp, 0, sizeof(cp)); cp.enable = 1; cp.filter_dup = 1; hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, int timeout) { long timeo = msecs_to_jiffies(3000); struct le_scan_params param; int err; BT_DBG("%s", hdev->name); if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) return -EINPROGRESS; param.type = type; param.interval = interval; param.window = window; hci_req_lock(hdev); err = __hci_request(hdev, le_scan_param_req, (unsigned long) ¶m, timeo); if (!err) err = __hci_request(hdev, le_scan_enable_req, 0, timeo); hci_req_unlock(hdev); if (err < 0) return err; schedule_delayed_work(&hdev->le_scan_disable, msecs_to_jiffies(timeout)); return 0; } int hci_cancel_le_scan(struct hci_dev *hdev) { BT_DBG("%s", hdev->name); if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) return -EALREADY; if (cancel_delayed_work(&hdev->le_scan_disable)) { struct hci_cp_le_set_scan_enable cp; /* Send HCI command to disable LE Scan */ memset(&cp, 0, sizeof(cp)); hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } return 0; } static void le_scan_disable_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan_disable.work); struct hci_cp_le_set_scan_enable cp; BT_DBG("%s", hdev->name); memset(&cp, 0, sizeof(cp)); hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } static void le_scan_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan); struct le_scan_params *param = &hdev->le_scan_params; BT_DBG("%s", hdev->name); hci_do_le_scan(hdev, param->type, param->interval, param->window, param->timeout); } int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, int timeout) { struct le_scan_params *param = &hdev->le_scan_params; BT_DBG("%s", hdev->name); if (work_busy(&hdev->le_scan)) return -EINPROGRESS; param->type = type; param->interval = interval; param->window = window; param->timeout = timeout; queue_work(system_long_wq, &hdev->le_scan); return 0; } /* Alloc HCI device */ struct hci_dev *hci_alloc_dev(void) { struct hci_dev *hdev; hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL); if (!hdev) return NULL; hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->esco_type = (ESCO_HV1); hdev->link_mode = (HCI_LM_ACCEPT); hdev->io_capability = 0x03; /* No Input No Output */ hdev->sniff_max_interval = 800; hdev->sniff_min_interval = 80; mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); INIT_LIST_HEAD(&hdev->mgmt_pending); INIT_LIST_HEAD(&hdev->blacklist); INIT_LIST_HEAD(&hdev->uuids); INIT_LIST_HEAD(&hdev->link_keys); INIT_LIST_HEAD(&hdev->long_term_keys); INIT_LIST_HEAD(&hdev->remote_oob_data); INIT_LIST_HEAD(&hdev->conn_hash.list); INIT_WORK(&hdev->rx_work, hci_rx_work); INIT_WORK(&hdev->cmd_work, hci_cmd_work); INIT_WORK(&hdev->tx_work, hci_tx_work); INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->le_scan, le_scan_work); INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off); INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work); skb_queue_head_init(&hdev->driver_init); skb_queue_head_init(&hdev->rx_q); skb_queue_head_init(&hdev->cmd_q); skb_queue_head_init(&hdev->raw_q); init_waitqueue_head(&hdev->req_wait_q); setup_timer(&hdev->cmd_timer, hci_cmd_timeout, (unsigned long) hdev); hci_init_sysfs(hdev); discovery_init(hdev); return hdev; } EXPORT_SYMBOL(hci_alloc_dev); /* Free HCI device */ void hci_free_dev(struct hci_dev *hdev) { skb_queue_purge(&hdev->driver_init); /* will free via device release */ put_device(&hdev->dev); } EXPORT_SYMBOL(hci_free_dev); /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { int id, error; if (!hdev->open || !hdev->close) return -EINVAL; /* Do not allow HCI_AMP devices to register at index 0, * so the index can be used as the AMP controller ID. */ switch (hdev->dev_type) { case HCI_BREDR: id = ida_simple_get(&hci_index_ida, 0, 0, GFP_KERNEL); break; case HCI_AMP: id = ida_simple_get(&hci_index_ida, 1, 0, GFP_KERNEL); break; default: return -EINVAL; } if (id < 0) return id; sprintf(hdev->name, "hci%d", id); hdev->id = id; BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); write_lock(&hci_dev_list_lock); list_add(&hdev->list, &hci_dev_list); write_unlock(&hci_dev_list_lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM, 1); #else hdev->workqueue = create_singlethread_workqueue(hdev->name); #endif if (!hdev->workqueue) { error = -ENOMEM; goto err; } error = hci_add_sysfs(hdev); if (error < 0) goto err_wqueue; hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); if (hdev->rfkill) { if (rfkill_register(hdev->rfkill) < 0) { rfkill_destroy(hdev->rfkill); hdev->rfkill = NULL; } } set_bit(HCI_SETUP, &hdev->dev_flags); if (hdev->dev_type != HCI_AMP) set_bit(HCI_AUTO_OFF, &hdev->dev_flags); schedule_work(&hdev->power_on); hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); return id; err_wqueue: destroy_workqueue(hdev->workqueue); err: ida_simple_remove(&hci_index_ida, hdev->id); write_lock(&hci_dev_list_lock); list_del(&hdev->list); write_unlock(&hci_dev_list_lock); return error; } EXPORT_SYMBOL(hci_register_dev); /* Unregister HCI device */ void hci_unregister_dev(struct hci_dev *hdev) { int i, id; BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); set_bit(HCI_UNREGISTER, &hdev->dev_flags); id = hdev->id; write_lock(&hci_dev_list_lock); list_del(&hdev->list); write_unlock(&hci_dev_list_lock); hci_dev_do_close(hdev); for (i = 0; i < NUM_REASSEMBLY; i++) kfree_skb(hdev->reassembly[i]); if (!test_bit(HCI_INIT, &hdev->flags) && !test_bit(HCI_SETUP, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); } /* mgmt_index_removed should take care of emptying the * pending list */ BUG_ON(!list_empty(&hdev->mgmt_pending)); hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { rfkill_unregister(hdev->rfkill); rfkill_destroy(hdev->rfkill); } hci_del_sysfs(hdev); destroy_workqueue(hdev->workqueue); hci_dev_lock(hdev); hci_blacklist_clear(hdev); hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_smp_ltks_clear(hdev); hci_remote_oob_data_clear(hdev); hci_dev_unlock(hdev); hci_dev_put(hdev); ida_simple_remove(&hci_index_ida, id); } EXPORT_SYMBOL(hci_unregister_dev); /* Suspend HCI device */ int hci_suspend_dev(struct hci_dev *hdev) { hci_notify(hdev, HCI_DEV_SUSPEND); return 0; } EXPORT_SYMBOL(hci_suspend_dev); /* Resume HCI device */ int hci_resume_dev(struct hci_dev *hdev) { hci_notify(hdev, HCI_DEV_RESUME); return 0; } EXPORT_SYMBOL(hci_resume_dev); /* Receive frame from HCI drivers */ int hci_recv_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; if (!hdev || (!test_bit(HCI_UP, &hdev->flags) && !test_bit(HCI_INIT, &hdev->flags))) { kfree_skb(skb); return -ENXIO; } /* Incomming skb */ bt_cb(skb)->incoming = 1; /* Time stamp */ __net_timestamp(skb); skb_queue_tail(&hdev->rx_q, skb); queue_work(hdev->workqueue, &hdev->rx_work); return 0; } EXPORT_SYMBOL(hci_recv_frame); static int hci_reassembly(struct hci_dev *hdev, int type, void *data, int count, __u8 index) { int len = 0; int hlen = 0; int remain = count; struct sk_buff *skb; struct bt_skb_cb *scb; if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) || index >= NUM_REASSEMBLY) return -EILSEQ; skb = hdev->reassembly[index]; if (!skb) { switch (type) { case HCI_ACLDATA_PKT: len = HCI_MAX_FRAME_SIZE; hlen = HCI_ACL_HDR_SIZE; break; case HCI_EVENT_PKT: len = HCI_MAX_EVENT_SIZE; hlen = HCI_EVENT_HDR_SIZE; break; case HCI_SCODATA_PKT: len = HCI_MAX_SCO_SIZE; hlen = HCI_SCO_HDR_SIZE; break; } skb = bt_skb_alloc(len, GFP_ATOMIC); if (!skb) return -ENOMEM; scb = (void *) skb->cb; scb->expect = hlen; scb->pkt_type = type; skb->dev = (void *) hdev; hdev->reassembly[index] = skb; } while (count) { scb = (void *) skb->cb; len = min_t(uint, scb->expect, count); memcpy(skb_put(skb, len), data, len); count -= len; data += len; scb->expect -= len; remain = count; switch (type) { case HCI_EVENT_PKT: if (skb->len == HCI_EVENT_HDR_SIZE) { struct hci_event_hdr *h = hci_event_hdr(skb); scb->expect = h->plen; if (skb_tailroom(skb) < scb->expect) { kfree_skb(skb); hdev->reassembly[index] = NULL; return -ENOMEM; } } break; case HCI_ACLDATA_PKT: if (skb->len == HCI_ACL_HDR_SIZE) { struct hci_acl_hdr *h = hci_acl_hdr(skb); scb->expect = __le16_to_cpu(h->dlen); if (skb_tailroom(skb) < scb->expect) { kfree_skb(skb); hdev->reassembly[index] = NULL; return -ENOMEM; } } break; case HCI_SCODATA_PKT: if (skb->len == HCI_SCO_HDR_SIZE) { struct hci_sco_hdr *h = hci_sco_hdr(skb); scb->expect = h->dlen; if (skb_tailroom(skb) < scb->expect) { kfree_skb(skb); hdev->reassembly[index] = NULL; return -ENOMEM; } } break; } if (scb->expect == 0) { /* Complete frame */ bt_cb(skb)->pkt_type = type; hci_recv_frame(skb); hdev->reassembly[index] = NULL; return remain; } } return remain; } int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count) { int rem = 0; if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) return -EILSEQ; while (count) { rem = hci_reassembly(hdev, type, data, count, type - 1); if (rem < 0) return rem; data += (count - rem); count = rem; } return rem; } EXPORT_SYMBOL(hci_recv_fragment); #define STREAM_REASSEMBLY 0 int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) { int type; int rem = 0; while (count) { struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; if (!skb) { struct { char type; } *pkt; /* Start of the frame */ pkt = data; type = pkt->type; data++; count--; } else type = bt_cb(skb)->pkt_type; rem = hci_reassembly(hdev, type, data, count, STREAM_REASSEMBLY); if (rem < 0) return rem; data += (count - rem); count = rem; } return rem; } EXPORT_SYMBOL(hci_recv_stream_fragment); /* ---- Interface to upper protocols ---- */ int hci_register_cb(struct hci_cb *cb) { BT_DBG("%p name %s", cb, cb->name); write_lock(&hci_cb_list_lock); list_add(&cb->list, &hci_cb_list); write_unlock(&hci_cb_list_lock); return 0; } EXPORT_SYMBOL(hci_register_cb); int hci_unregister_cb(struct hci_cb *cb) { BT_DBG("%p name %s", cb, cb->name); write_lock(&hci_cb_list_lock); list_del(&cb->list); write_unlock(&hci_cb_list_lock); return 0; } EXPORT_SYMBOL(hci_unregister_cb); static int hci_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; if (!hdev) { kfree_skb(skb); return -ENODEV; } BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); /* Time stamp */ __net_timestamp(skb); /* Send copy to monitor */ hci_send_to_monitor(hdev, skb); if (atomic_read(&hdev->promisc)) { /* Send copy to the sockets */ hci_send_to_sock(hdev, skb); } /* Get rid of skb owner, prior to sending to the driver. */ skb_orphan(skb); return hdev->send(skb); } /* Send HCI command */ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) { int len = HCI_COMMAND_HDR_SIZE + plen; struct hci_command_hdr *hdr; struct sk_buff *skb; BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); skb = bt_skb_alloc(len, GFP_ATOMIC); if (!skb) { BT_ERR("%s no memory for command", hdev->name); return -ENOMEM; } hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); hdr->opcode = cpu_to_le16(opcode); hdr->plen = plen; if (plen) memcpy(skb_put(skb, plen), param, plen); BT_DBG("skb len %d", skb->len); bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; skb->dev = (void *) hdev; if (test_bit(HCI_INIT, &hdev->flags)) hdev->init_last_cmd = opcode; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); return 0; } /* Get data from the previously sent command */ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) { struct hci_command_hdr *hdr; if (!hdev->sent_cmd) return NULL; hdr = (void *) hdev->sent_cmd->data; if (hdr->opcode != cpu_to_le16(opcode)) return NULL; BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE; } /* Send ACL data */ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) { struct hci_acl_hdr *hdr; int len = skb->len; skb_push(skb, HCI_ACL_HDR_SIZE); skb_reset_transport_header(skb); hdr = (struct hci_acl_hdr *)skb_transport_header(skb); hdr->handle = cpu_to_le16(hci_handle_pack(handle, flags)); hdr->dlen = cpu_to_le16(len); } static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, struct sk_buff *skb, __u16 flags) { struct hci_dev *hdev = conn->hdev; struct sk_buff *list; skb->len = skb_headlen(skb); skb->data_len = 0; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; hci_add_acl_hdr(skb, conn->handle, flags); list = skb_shinfo(skb)->frag_list; if (!list) { /* Non fragmented */ BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); skb_queue_tail(queue, skb); } else { /* Fragmented */ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); skb_shinfo(skb)->frag_list = NULL; /* Queue all fragments atomically */ spin_lock(&queue->lock); __skb_queue_tail(queue, skb); flags &= ~ACL_START; flags |= ACL_CONT; do { skb = list; list = list->next; skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; hci_add_acl_hdr(skb, conn->handle, flags); BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); __skb_queue_tail(queue, skb); } while (list); spin_unlock(&queue->lock); } } void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) { struct hci_conn *conn = chan->conn; struct hci_dev *hdev = conn->hdev; BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags); skb->dev = (void *) hdev; hci_queue_acl(conn, &chan->data_q, skb, flags); queue_work(hdev->workqueue, &hdev->tx_work); } /* Send SCO data */ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) { struct hci_dev *hdev = conn->hdev; struct hci_sco_hdr hdr; BT_DBG("%s len %d", hdev->name, skb->len); hdr.handle = cpu_to_le16(conn->handle); hdr.dlen = skb->len; skb_push(skb, HCI_SCO_HDR_SIZE); skb_reset_transport_header(skb); memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE); skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; skb_queue_tail(&conn->data_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } /* ---- HCI TX task (outgoing data) ---- */ /* HCI Connection scheduler */ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *conn = NULL, *c; unsigned int num = 0, min = ~0; /* We don't have to lock device here. Connections are always * added and removed with TX task disabled. */ rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { if (c->type != type || skb_queue_empty(&c->data_q)) continue; if (c->state != BT_CONNECTED && c->state != BT_CONFIG) continue; num++; if (c->sent < min) { min = c->sent; conn = c; } if (hci_conn_num(hdev, type) == num) break; } rcu_read_unlock(); if (conn) { int cnt, q; switch (conn->type) { case ACL_LINK: cnt = hdev->acl_cnt; break; case SCO_LINK: case ESCO_LINK: cnt = hdev->sco_cnt; break; case LE_LINK: cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; break; default: cnt = 0; BT_ERR("Unknown link type"); } q = cnt / num; *quote = q ? q : 1; } else *quote = 0; BT_DBG("conn %p quote %d", conn, *quote); return conn; } static void hci_link_tx_to(struct hci_dev *hdev, __u8 type) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; BT_ERR("%s link tx timeout", hdev->name); rcu_read_lock(); /* Kill stalled connections */ list_for_each_entry_rcu(c, &h->list, list) { if (c->type == type && c->sent) { BT_ERR("%s killing stalled connection %s", hdev->name, batostr(&c->dst)); hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM); } } rcu_read_unlock(); } static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, int *quote) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_chan *chan = NULL; unsigned int num = 0, min = ~0, cur_prio = 0; struct hci_conn *conn; int cnt, q, conn_num = 0; BT_DBG("%s", hdev->name); rcu_read_lock(); list_for_each_entry_rcu(conn, &h->list, list) { struct hci_chan *tmp; if (conn->type != type) continue; if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) continue; conn_num++; list_for_each_entry_rcu(tmp, &conn->chan_list, list) { struct sk_buff *skb; if (skb_queue_empty(&tmp->data_q)) continue; skb = skb_peek(&tmp->data_q); if (skb->priority < cur_prio) continue; if (skb->priority > cur_prio) { num = 0; min = ~0; cur_prio = skb->priority; } num++; if (conn->sent < min) { min = conn->sent; chan = tmp; } } if (hci_conn_num(hdev, type) == conn_num) break; } rcu_read_unlock(); if (!chan) return NULL; switch (chan->conn->type) { case ACL_LINK: cnt = hdev->acl_cnt; break; case SCO_LINK: case ESCO_LINK: cnt = hdev->sco_cnt; break; case LE_LINK: cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; break; default: cnt = 0; BT_ERR("Unknown link type"); } q = cnt / num; *quote = q ? q : 1; BT_DBG("chan %p quote %d", chan, *quote); return chan; } static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *conn; int num = 0; BT_DBG("%s", hdev->name); rcu_read_lock(); list_for_each_entry_rcu(conn, &h->list, list) { struct hci_chan *chan; if (conn->type != type) continue; if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) continue; num++; list_for_each_entry_rcu(chan, &conn->chan_list, list) { struct sk_buff *skb; if (chan->sent) { chan->sent = 0; continue; } if (skb_queue_empty(&chan->data_q)) continue; skb = skb_peek(&chan->data_q); if (skb->priority >= HCI_PRIO_MAX - 1) continue; skb->priority = HCI_PRIO_MAX - 1; BT_DBG("chan %p skb %p promoted to %d", chan, skb, skb->priority); } if (hci_conn_num(hdev, type) == num) break; } rcu_read_unlock(); } static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb) { /* Calculate count of blocks used by this packet */ return DIV_ROUND_UP(skb->len - HCI_ACL_HDR_SIZE, hdev->block_len); } static void __check_timeout(struct hci_dev *hdev, unsigned int cnt) { if (!test_bit(HCI_RAW, &hdev->flags)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!cnt && time_after(jiffies, hdev->acl_last_tx + HCI_ACL_TX_TIMEOUT)) hci_link_tx_to(hdev, ACL_LINK); } } static void hci_sched_acl_pkt(struct hci_dev *hdev) { unsigned int cnt = hdev->acl_cnt; struct hci_chan *chan; struct sk_buff *skb; int quote; __check_timeout(hdev, cnt); while (hdev->acl_cnt && (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote-- && (skb = skb_peek(&chan->data_q))) { BT_DBG("chan %p skb %p len %d priority %u", chan, skb, skb->len, skb->priority); /* Stop if priority has changed */ if (skb->priority < priority) break; skb = skb_dequeue(&chan->data_q); hci_conn_enter_active_mode(chan->conn, bt_cb(skb)->force_active); hci_send_frame(skb); hdev->acl_last_tx = jiffies; hdev->acl_cnt--; chan->sent++; chan->conn->sent++; } } if (cnt != hdev->acl_cnt) hci_prio_recalculate(hdev, ACL_LINK); } static void hci_sched_acl_blk(struct hci_dev *hdev) { unsigned int cnt = hdev->block_cnt; struct hci_chan *chan; struct sk_buff *skb; int quote; __check_timeout(hdev, cnt); while (hdev->block_cnt > 0 && (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote > 0 && (skb = skb_peek(&chan->data_q))) { int blocks; BT_DBG("chan %p skb %p len %d priority %u", chan, skb, skb->len, skb->priority); /* Stop if priority has changed */ if (skb->priority < priority) break; skb = skb_dequeue(&chan->data_q); blocks = __get_blocks(hdev, skb); if (blocks > hdev->block_cnt) return; hci_conn_enter_active_mode(chan->conn, bt_cb(skb)->force_active); hci_send_frame(skb); hdev->acl_last_tx = jiffies; hdev->block_cnt -= blocks; quote -= blocks; chan->sent += blocks; chan->conn->sent += blocks; } } if (cnt != hdev->block_cnt) hci_prio_recalculate(hdev, ACL_LINK); } static void hci_sched_acl(struct hci_dev *hdev) { BT_DBG("%s", hdev->name); if (!hci_conn_num(hdev, ACL_LINK)) return; switch (hdev->flow_ctl_mode) { case HCI_FLOW_CTL_MODE_PACKET_BASED: hci_sched_acl_pkt(hdev); break; case HCI_FLOW_CTL_MODE_BLOCK_BASED: hci_sched_acl_blk(hdev); break; } } /* Schedule SCO */ static void hci_sched_sco(struct hci_dev *hdev) { struct hci_conn *conn; struct sk_buff *skb; int quote; BT_DBG("%s", hdev->name); if (!hci_conn_num(hdev, SCO_LINK)) return; while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); hci_send_frame(skb); conn->sent++; if (conn->sent == ~0) conn->sent = 0; } } } static void hci_sched_esco(struct hci_dev *hdev) { struct hci_conn *conn; struct sk_buff *skb; int quote; BT_DBG("%s", hdev->name); if (!hci_conn_num(hdev, ESCO_LINK)) return; while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); hci_send_frame(skb); conn->sent++; if (conn->sent == ~0) conn->sent = 0; } } } static void hci_sched_le(struct hci_dev *hdev) { struct hci_chan *chan; struct sk_buff *skb; int quote, cnt, tmp; BT_DBG("%s", hdev->name); if (!hci_conn_num(hdev, LE_LINK)) return; if (!test_bit(HCI_RAW, &hdev->flags)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!hdev->le_cnt && hdev->le_pkts && time_after(jiffies, hdev->le_last_tx + HZ * 45)) hci_link_tx_to(hdev, LE_LINK); } cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; tmp = cnt; while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote-- && (skb = skb_peek(&chan->data_q))) { BT_DBG("chan %p skb %p len %d priority %u", chan, skb, skb->len, skb->priority); /* Stop if priority has changed */ if (skb->priority < priority) break; skb = skb_dequeue(&chan->data_q); hci_send_frame(skb); hdev->le_last_tx = jiffies; cnt--; chan->sent++; chan->conn->sent++; } } if (hdev->le_pkts) hdev->le_cnt = cnt; else hdev->acl_cnt = cnt; if (cnt != tmp) hci_prio_recalculate(hdev, LE_LINK); } static void hci_tx_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work); struct sk_buff *skb; BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt, hdev->le_cnt); /* Schedule queues and send stuff to HCI driver */ hci_sched_acl(hdev); hci_sched_sco(hdev); hci_sched_esco(hdev); hci_sched_le(hdev); /* Send next queued raw (unknown type) packet */ while ((skb = skb_dequeue(&hdev->raw_q))) hci_send_frame(skb); } /* ----- HCI RX task (incoming data processing) ----- */ /* ACL data packet */ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_acl_hdr *hdr = (void *) skb->data; struct hci_conn *conn; __u16 handle, flags; skb_pull(skb, HCI_ACL_HDR_SIZE); handle = __le16_to_cpu(hdr->handle); flags = hci_flags(handle); handle = hci_handle(handle); BT_DBG("%s len %d handle 0x%4.4x flags 0x%4.4x", hdev->name, skb->len, handle, flags); hdev->stat.acl_rx++; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, handle); hci_dev_unlock(hdev); if (conn) { hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags) && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &conn->dst, conn->type, conn->dst_type, 0, NULL, 0, conn->dev_class); hci_dev_unlock(hdev); /* Send to upper protocol */ l2cap_recv_acldata(conn, skb, flags); return; } else { BT_ERR("%s ACL packet for unknown connection handle %d", hdev->name, handle); } kfree_skb(skb); } /* SCO data packet */ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_sco_hdr *hdr = (void *) skb->data; struct hci_conn *conn; __u16 handle; skb_pull(skb, HCI_SCO_HDR_SIZE); handle = __le16_to_cpu(hdr->handle); BT_DBG("%s len %d handle 0x%4.4x", hdev->name, skb->len, handle); hdev->stat.sco_rx++; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, handle); hci_dev_unlock(hdev); if (conn) { /* Send to upper protocol */ sco_recv_scodata(conn, skb); return; } else { BT_ERR("%s SCO packet for unknown connection handle %d", hdev->name, handle); } kfree_skb(skb); } static void hci_rx_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work); struct sk_buff *skb; BT_DBG("%s", hdev->name); while ((skb = skb_dequeue(&hdev->rx_q))) { /* Send copy to monitor */ hci_send_to_monitor(hdev, skb); if (atomic_read(&hdev->promisc)) { /* Send copy to the sockets */ hci_send_to_sock(hdev, skb); } if (test_bit(HCI_RAW, &hdev->flags)) { kfree_skb(skb); continue; } if (test_bit(HCI_INIT, &hdev->flags)) { /* Don't process data packets in this states. */ switch (bt_cb(skb)->pkt_type) { case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: kfree_skb(skb); continue; } } /* Process frame */ switch (bt_cb(skb)->pkt_type) { case HCI_EVENT_PKT: BT_DBG("%s Event packet", hdev->name); hci_event_packet(hdev, skb); break; case HCI_ACLDATA_PKT: BT_DBG("%s ACL data packet", hdev->name); hci_acldata_packet(hdev, skb); break; case HCI_SCODATA_PKT: BT_DBG("%s SCO data packet", hdev->name); hci_scodata_packet(hdev, skb); break; default: kfree_skb(skb); break; } } } static void hci_cmd_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_work); struct sk_buff *skb; BT_DBG("%s cmd_cnt %d cmd queued %d", hdev->name, atomic_read(&hdev->cmd_cnt), skb_queue_len(&hdev->cmd_q)); /* Send queued commands */ if (atomic_read(&hdev->cmd_cnt)) { skb = skb_dequeue(&hdev->cmd_q); if (!skb) return; kfree_skb(hdev->sent_cmd); hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC); if (hdev->sent_cmd) { atomic_dec(&hdev->cmd_cnt); hci_send_frame(skb); if (test_bit(HCI_RESET, &hdev->flags)) del_timer(&hdev->cmd_timer); else mod_timer(&hdev->cmd_timer, jiffies + HCI_CMD_TIMEOUT); } else { skb_queue_head(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); } } } int hci_do_inquiry(struct hci_dev *hdev, u8 length) { /* General inquiry access code (GIAC) */ u8 lap[3] = { 0x33, 0x8b, 0x9e }; struct hci_cp_inquiry cp; BT_DBG("%s", hdev->name); if (test_bit(HCI_INQUIRY, &hdev->flags)) return -EINPROGRESS; inquiry_cache_flush(hdev); memset(&cp, 0, sizeof(cp)); memcpy(&cp.lap, lap, sizeof(cp.lap)); cp.length = length; return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); } int hci_cancel_inquiry(struct hci_dev *hdev) { BT_DBG("%s", hdev->name); if (!test_bit(HCI_INQUIRY, &hdev->flags)) return -EALREADY; return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); } u8 bdaddr_to_le(u8 bdaddr_type) { switch (bdaddr_type) { case BDADDR_LE_PUBLIC: return ADDR_LE_DEV_PUBLIC; default: /* Fallback to LE Random address type */ return ADDR_LE_DEV_RANDOM; } } compat-drivers-2012-09-18/net/bluetooth/l2cap_sock.c0000644000175000017500000006562012026211315021350 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. Copyright (C) 2011 ProFUSION Embedded Systems Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth L2CAP sockets. */ #include #include #include #include #include static struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) }; static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct sockaddr_l2 la; int len, err = 0; BT_DBG("sk %p", sk); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; memset(&la, 0, sizeof(la)); len = min_t(unsigned int, sizeof(la), alen); memcpy(&la, addr, len); if (la.l2_cid && la.l2_psm) return -EINVAL; lock_sock(sk); if (sk->sk_state != BT_OPEN) { err = -EBADFD; goto done; } if (la.l2_psm) { __u16 psm = __le16_to_cpu(la.l2_psm); /* PSM must be odd and lsb of upper byte must be 0 */ if ((psm & 0x0101) != 0x0001) { err = -EINVAL; goto done; } /* Restrict usage of well-known PSMs */ if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { err = -EACCES; goto done; } } if (la.l2_cid) err = l2cap_add_scid(chan, __le16_to_cpu(la.l2_cid)); else err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm); if (err < 0) goto done; if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP || __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM) chan->sec_level = BT_SECURITY_SDP; bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); chan->state = BT_BOUND; sk->sk_state = BT_BOUND; done: release_sock(sk); return err; } static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct sockaddr_l2 la; int len, err = 0; BT_DBG("sk %p", sk); if (!addr || alen < sizeof(addr->sa_family) || addr->sa_family != AF_BLUETOOTH) return -EINVAL; memset(&la, 0, sizeof(la)); len = min_t(unsigned int, sizeof(la), alen); memcpy(&la, addr, len); if (la.l2_cid && la.l2_psm) return -EINVAL; err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), &la.l2_bdaddr, la.l2_bdaddr_type); if (err) return err; lock_sock(sk); err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK)); release_sock(sk); return err; } static int l2cap_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); lock_sock(sk); if (sk->sk_state != BT_BOUND) { err = -EBADFD; goto done; } if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) { err = -EINVAL; goto done; } switch (chan->mode) { case L2CAP_MODE_BASIC: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: if (!disable_ertm) break; /* fall through */ default: err = -ENOTSUPP; goto done; } sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; chan->state = BT_LISTEN; sk->sk_state = BT_LISTEN; done: release_sock(sk); return err; } static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) { DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; long timeo; int err = 0; lock_sock_nested(sk, SINGLE_DEPTH_NESTING); timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk_sleep(sk), &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; break; } nsk = bt_accept_dequeue(sk, newsock); if (nsk) break; if (!timeo) { err = -EAGAIN; break; } if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock_nested(sk, SINGLE_DEPTH_NESTING); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); if (err) goto done; newsock->state = SS_CONNECTED; BT_DBG("new socket %p", nsk); done: release_sock(sk); return err; } static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; BT_DBG("sock %p, sk %p", sock, sk); memset(la, 0, sizeof(struct sockaddr_l2)); addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_l2); if (peer) { la->l2_psm = chan->psm; bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); la->l2_cid = cpu_to_le16(chan->dcid); } else { la->l2_psm = chan->sport; bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); la->l2_cid = cpu_to_le16(chan->scid); } return 0; } static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct l2cap_options opts; struct l2cap_conninfo cinfo; int len, err = 0; u32 opt; BT_DBG("sk %p", sk); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: memset(&opts, 0, sizeof(opts)); opts.imtu = chan->imtu; opts.omtu = chan->omtu; opts.flush_to = chan->flush_to; opts.mode = chan->mode; opts.fcs = chan->fcs; opts.max_tx = chan->max_tx; opts.txwin_size = chan->tx_win; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) err = -EFAULT; break; case L2CAP_LM: switch (chan->sec_level) { case BT_SECURITY_LOW: opt = L2CAP_LM_AUTH; break; case BT_SECURITY_MEDIUM: opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; break; case BT_SECURITY_HIGH: opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE; break; default: opt = 0; break; } if (test_bit(FLAG_ROLE_SWITCH, &chan->flags)) opt |= L2CAP_LM_MASTER; if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) opt |= L2CAP_LM_RELIABLE; if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; break; case L2CAP_CONNINFO: if (sk->sk_state != BT_CONNECTED && !(sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) { err = -ENOTCONN; break; } memset(&cinfo, 0, sizeof(cinfo)); cinfo.hci_handle = chan->conn->hcon->handle; memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *) &cinfo, len)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; struct bt_power pwr; int len, err = 0; BT_DBG("sk %p", sk); if (level == SOL_L2CAP) return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case BT_SECURITY: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } memset(&sec, 0, sizeof(sec)); if (chan->conn) sec.level = chan->conn->hcon->sec_level; else sec.level = chan->sec_level; if (sk->sk_state == BT_CONNECTED) sec.key_size = chan->conn->hcon->enc_key_size; len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) err = -EFAULT; break; case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; break; } if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), (u32 __user *) optval)) err = -EFAULT; break; case BT_FLUSHABLE: if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), (u32 __user *) optval)) err = -EFAULT; break; case BT_POWER: if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM && sk->sk_type != SOCK_RAW) { err = -EINVAL; break; } pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); len = min_t(unsigned int, len, sizeof(pwr)); if (copy_to_user(optval, (char *) &pwr, len)) err = -EFAULT; break; case BT_CHANNEL_POLICY: if (!enable_hs) { err = -ENOPROTOOPT; break; } if (put_user(chan->chan_policy, (u32 __user *) optval)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu) { switch (chan->scid) { case L2CAP_CID_LE_DATA: if (mtu < L2CAP_LE_MIN_MTU) return false; break; default: if (mtu < L2CAP_DEFAULT_MIN_MTU) return false; } return true; } static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct l2cap_options opts; int len, err = 0; u32 opt; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: if (sk->sk_state == BT_CONNECTED) { err = -EINVAL; break; } opts.imtu = chan->imtu; opts.omtu = chan->omtu; opts.flush_to = chan->flush_to; opts.mode = chan->mode; opts.fcs = chan->fcs; opts.max_tx = chan->max_tx; opts.txwin_size = chan->tx_win; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { err = -EFAULT; break; } if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) { err = -EINVAL; break; } if (!l2cap_valid_mtu(chan, opts.imtu)) { err = -EINVAL; break; } chan->mode = opts.mode; switch (chan->mode) { case L2CAP_MODE_BASIC: clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: if (!disable_ertm) break; /* fall through */ default: err = -EINVAL; break; } chan->imtu = opts.imtu; chan->omtu = opts.omtu; chan->fcs = opts.fcs; chan->max_tx = opts.max_tx; chan->tx_win = opts.txwin_size; break; case L2CAP_LM: if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } if (opt & L2CAP_LM_AUTH) chan->sec_level = BT_SECURITY_LOW; if (opt & L2CAP_LM_ENCRYPT) chan->sec_level = BT_SECURITY_MEDIUM; if (opt & L2CAP_LM_SECURE) chan->sec_level = BT_SECURITY_HIGH; if (opt & L2CAP_LM_MASTER) set_bit(FLAG_ROLE_SWITCH, &chan->flags); else clear_bit(FLAG_ROLE_SWITCH, &chan->flags); if (opt & L2CAP_LM_RELIABLE) set_bit(FLAG_FORCE_RELIABLE, &chan->flags); else clear_bit(FLAG_FORCE_RELIABLE, &chan->flags); break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) #else static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) #endif { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; struct bt_power pwr; struct l2cap_conn *conn; int len, err = 0; u32 opt; BT_DBG("sk %p", sk); if (level == SOL_L2CAP) return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; lock_sock(sk); switch (optname) { case BT_SECURITY: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } sec.level = BT_SECURITY_LOW; len = min_t(unsigned int, sizeof(sec), optlen); if (copy_from_user((char *) &sec, optval, len)) { err = -EFAULT; break; } if (sec.level < BT_SECURITY_LOW || sec.level > BT_SECURITY_HIGH) { err = -EINVAL; break; } chan->sec_level = sec.level; if (!chan->conn) break; conn = chan->conn; /*change security for LE channels */ if (chan->scid == L2CAP_CID_LE_DATA) { if (!conn->hcon->out) { err = -EINVAL; break; } if (smp_conn_security(conn->hcon, sec.level)) break; sk->sk_state = BT_CONFIG; chan->state = BT_CONFIG; /* or for ACL link */ } else if ((sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) || sk->sk_state == BT_CONNECTED) { if (!l2cap_chan_check_security(chan)) set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); else sk->sk_state_change(sk); } else { err = -EINVAL; } break; case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; break; } if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } if (opt) set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); else clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); break; case BT_FLUSHABLE: if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } if (opt > BT_FLUSHABLE_ON) { err = -EINVAL; break; } if (opt == BT_FLUSHABLE_OFF) { struct l2cap_conn *conn = chan->conn; /* proceed further only when we have l2cap_conn and No Flush support in the LM */ if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { err = -EINVAL; break; } } if (opt) set_bit(FLAG_FLUSHABLE, &chan->flags); else clear_bit(FLAG_FLUSHABLE, &chan->flags); break; case BT_POWER: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } pwr.force_active = BT_POWER_FORCE_ACTIVE_ON; len = min_t(unsigned int, sizeof(pwr), optlen); if (copy_from_user((char *) &pwr, optval, len)) { err = -EFAULT; break; } if (pwr.force_active) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); else clear_bit(FLAG_FORCE_ACTIVE, &chan->flags); break; case BT_CHANNEL_POLICY: if (!enable_hs) { err = -ENOPROTOOPT; break; } if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) { err = -EINVAL; break; } if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) { err = -EOPNOTSUPP; break; } chan->chan_policy = (u8) opt; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; int err; BT_DBG("sock %p, sk %p", sock, sk); err = sock_error(sk); if (err) return err; if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; if (sk->sk_state != BT_CONNECTED) return -ENOTCONN; l2cap_chan_lock(chan); err = l2cap_chan_send(chan, msg, len, sk->sk_priority); l2cap_chan_unlock(chan); return err; } static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct sock *sk = sock->sk; struct l2cap_pinfo *pi = l2cap_pi(sk); int err; lock_sock(sk); if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { sk->sk_state = BT_CONFIG; pi->chan->state = BT_CONFIG; __l2cap_connect_rsp_defer(pi->chan); release_sock(sk); return 0; } release_sock(sk); if (sock->type == SOCK_STREAM) err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); else err = bt_sock_recvmsg(iocb, sock, msg, len, flags); if (pi->chan->mode != L2CAP_MODE_ERTM) return err; /* Attempt to put pending rx data in the socket buffer */ lock_sock(sk); if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state)) goto done; if (pi->rx_busy_skb) { if (!sock_queue_rcv_skb(sk, pi->rx_busy_skb)) pi->rx_busy_skb = NULL; else goto done; } /* Restore data flow when half of the receive buffer is * available. This avoids resending large numbers of * frames. */ if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) l2cap_chan_busy(pi->chan, 0); done: release_sock(sk); return err; } /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */ static void l2cap_sock_kill(struct sock *sk) { if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); /* Kill poor orphan */ l2cap_chan_put(l2cap_pi(sk)->chan); sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; struct l2cap_chan *chan; struct l2cap_conn *conn; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; chan = l2cap_pi(sk)->chan; conn = chan->conn; if (conn) mutex_lock(&conn->chan_lock); l2cap_chan_lock(chan); lock_sock(sk); if (!sk->sk_shutdown) { if (chan->mode == L2CAP_MODE_ERTM) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; release_sock(sk); l2cap_chan_close(chan, 0); lock_sock(sk); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); } if (!err && sk->sk_err) err = -sk->sk_err; release_sock(sk); l2cap_chan_unlock(chan); if (conn) mutex_unlock(&conn->chan_lock); return err; } static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; bt_sock_unlink(&l2cap_sk_list, sk); err = l2cap_sock_shutdown(sock, 2); sock_orphan(sk); l2cap_sock_kill(sk); return err; } static void l2cap_sock_cleanup_listen(struct sock *parent) { struct sock *sk; BT_DBG("parent %p", parent); /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; l2cap_chan_lock(chan); __clear_chan_timer(chan); l2cap_chan_close(chan, ECONNRESET); l2cap_chan_unlock(chan); l2cap_sock_kill(sk); } } static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) { struct sock *sk, *parent = chan->data; /* Check for backlog size */ if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); return NULL; } sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); if (!sk) return NULL; bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); l2cap_sock_init(sk, parent); return l2cap_pi(sk)->chan; } static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { int err; struct sock *sk = chan->data; struct l2cap_pinfo *pi = l2cap_pi(sk); lock_sock(sk); if (pi->rx_busy_skb) { err = -ENOMEM; goto done; } err = sock_queue_rcv_skb(sk, skb); /* For ERTM, handle one skb that doesn't fit into the recv * buffer. This is important to do because the data frames * have already been acked, so the skb cannot be discarded. * * Notify the l2cap core that the buffer is full, so the * LOCAL_BUSY state is entered and no more frames are * acked and reassembled until there is buffer space * available. */ if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) { pi->rx_busy_skb = skb; l2cap_chan_busy(pi->chan, 1); err = 0; } done: release_sock(sk); return err; } static void l2cap_sock_close_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; l2cap_sock_kill(sk); } static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err) { struct sock *sk = chan->data; struct sock *parent; lock_sock(sk); parent = bt_sk(sk)->parent; sock_set_flag(sk, SOCK_ZAPPED); switch (chan->state) { case BT_OPEN: case BT_BOUND: case BT_CLOSED: break; case BT_LISTEN: l2cap_sock_cleanup_listen(sk); sk->sk_state = BT_CLOSED; chan->state = BT_CLOSED; break; default: sk->sk_state = BT_CLOSED; chan->state = BT_CLOSED; sk->sk_err = err; if (parent) { bt_accept_unlink(sk); parent->sk_data_ready(parent, 0); } else { sk->sk_state_change(sk); } break; } release_sock(sk); } static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state) { struct sock *sk = chan->data; sk->sk_state = state; } static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, unsigned long len, int nb) { struct sk_buff *skb; int err; l2cap_chan_unlock(chan); skb = bt_skb_send_alloc(chan->sk, len, nb, &err); l2cap_chan_lock(chan); if (!skb) return ERR_PTR(err); return skb; } static void l2cap_sock_ready_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; struct sock *parent; lock_sock(sk); parent = bt_sk(sk)->parent; BT_DBG("sk %p, parent %p", sk, parent); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); if (parent) parent->sk_data_ready(parent, 0); release_sock(sk); } static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, .recv = l2cap_sock_recv_cb, .close = l2cap_sock_close_cb, .teardown = l2cap_sock_teardown_cb, .state_change = l2cap_sock_state_change_cb, .ready = l2cap_sock_ready_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, }; static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); l2cap_chan_put(l2cap_pi(sk)->chan); if (l2cap_pi(sk)->rx_busy_skb) { kfree_skb(l2cap_pi(sk)->rx_busy_skb); l2cap_pi(sk)->rx_busy_skb = NULL; } skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); } static void l2cap_sock_init(struct sock *sk, struct sock *parent) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_chan *chan = pi->chan; BT_DBG("sk %p", sk); if (parent) { struct l2cap_chan *pchan = l2cap_pi(parent)->chan; sk->sk_type = parent->sk_type; bt_sk(sk)->flags = bt_sk(parent)->flags; chan->chan_type = pchan->chan_type; chan->imtu = pchan->imtu; chan->omtu = pchan->omtu; chan->conf_state = pchan->conf_state; chan->mode = pchan->mode; chan->fcs = pchan->fcs; chan->max_tx = pchan->max_tx; chan->tx_win = pchan->tx_win; chan->tx_win_max = pchan->tx_win_max; chan->sec_level = pchan->sec_level; chan->flags = pchan->flags; security_sk_clone(parent, sk); } else { switch (sk->sk_type) { case SOCK_RAW: chan->chan_type = L2CAP_CHAN_RAW; break; case SOCK_DGRAM: chan->chan_type = L2CAP_CHAN_CONN_LESS; break; case SOCK_SEQPACKET: case SOCK_STREAM: chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; break; } chan->imtu = L2CAP_DEFAULT_MTU; chan->omtu = 0; if (!disable_ertm && sk->sk_type == SOCK_STREAM) { chan->mode = L2CAP_MODE_ERTM; set_bit(CONF_STATE2_DEVICE, &chan->conf_state); } else { chan->mode = L2CAP_MODE_BASIC; } l2cap_chan_set_defaults(chan); } /* Default config options */ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; chan->data = sk; chan->ops = &l2cap_chan_ops; } static struct proto l2cap_proto = { .name = "L2CAP", .owner = THIS_MODULE, .obj_size = sizeof(struct l2cap_pinfo) }; static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) { struct sock *sk; struct l2cap_chan *chan; sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); if (!sk) return NULL; sock_init_data(sock, sk); INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = l2cap_sock_destruct; sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = proto; sk->sk_state = BT_OPEN; chan = l2cap_chan_create(); if (!chan) { sk_free(sk); return NULL; } l2cap_chan_hold(chan); chan->sk = sk; l2cap_pi(sk)->chan = chan; return sk; } #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) #else static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol) #endif { struct sock *sk; BT_DBG("sock %p", sock); sock->state = SS_UNCONNECTED; if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)) if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) #else if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW)) #endif return -EPERM; sock->ops = &l2cap_sock_ops; sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); if (!sk) return -ENOMEM; l2cap_sock_init(sk, NULL); bt_sock_link(&l2cap_sk_list, sk); return 0; } static const struct proto_ops l2cap_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = l2cap_sock_release, .bind = l2cap_sock_bind, .connect = l2cap_sock_connect, .listen = l2cap_sock_listen, .accept = l2cap_sock_accept, .getname = l2cap_sock_getname, .sendmsg = l2cap_sock_sendmsg, .recvmsg = l2cap_sock_recvmsg, .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, .shutdown = l2cap_sock_shutdown, .setsockopt = l2cap_sock_setsockopt, .getsockopt = l2cap_sock_getsockopt }; static const struct net_proto_family l2cap_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = l2cap_sock_create, }; int __init l2cap_init_sockets(void) { int err; err = proto_register(&l2cap_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); if (err < 0) { BT_ERR("L2CAP socket registration failed"); goto error; } err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list, NULL); if (err < 0) { BT_ERR("Failed to create L2CAP proc file"); bt_sock_unregister(BTPROTO_L2CAP); goto error; } BT_INFO("L2CAP socket layer initialized"); return 0; error: proto_unregister(&l2cap_proto); return err; } void l2cap_cleanup_sockets(void) { bt_procfs_cleanup(&init_net, "l2cap"); if (bt_sock_unregister(BTPROTO_L2CAP) < 0) BT_ERR("L2CAP socket unregistration failed"); proto_unregister(&l2cap_proto); } compat-drivers-2012-09-18/net/bluetooth/sco.c0000644000175000017500000005142312026211315020110 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth SCO sockets. */ #include #include #include #include #include #include static bool disable_esco; static const struct proto_ops sco_sock_ops; static struct bt_sock_list sco_sk_list = { .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock) }; static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); static void sco_chan_del(struct sock *sk, int err); static void sco_sock_close(struct sock *sk); static void sco_sock_kill(struct sock *sk); /* ---- SCO timers ---- */ static void sco_sock_timeout(unsigned long arg) { struct sock *sk = (struct sock *) arg; BT_DBG("sock %p state %d", sk, sk->sk_state); bh_lock_sock(sk); sk->sk_err = ETIMEDOUT; sk->sk_state_change(sk); bh_unlock_sock(sk); sco_sock_kill(sk); sock_put(sk); } static void sco_sock_set_timer(struct sock *sk, long timeout) { BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); } static void sco_sock_clear_timer(struct sock *sk) { BT_DBG("sock %p state %d", sk, sk->sk_state); sk_stop_timer(sk, &sk->sk_timer); } /* ---- SCO connections ---- */ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) { struct hci_dev *hdev = hcon->hdev; struct sco_conn *conn = hcon->sco_data; if (conn) return conn; conn = kzalloc(sizeof(struct sco_conn), GFP_ATOMIC); if (!conn) return NULL; spin_lock_init(&conn->lock); hcon->sco_data = conn; conn->hcon = hcon; conn->src = &hdev->bdaddr; conn->dst = &hcon->dst; if (hdev->sco_mtu > 0) conn->mtu = hdev->sco_mtu; else conn->mtu = 60; BT_DBG("hcon %p conn %p", hcon, conn); return conn; } static struct sock *sco_chan_get(struct sco_conn *conn) { struct sock *sk = NULL; sco_conn_lock(conn); sk = conn->sk; sco_conn_unlock(conn); return sk; } static int sco_conn_del(struct hci_conn *hcon, int err) { struct sco_conn *conn = hcon->sco_data; struct sock *sk; if (!conn) return 0; BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); /* Kill socket */ sk = sco_chan_get(conn); if (sk) { bh_lock_sock(sk); sco_sock_clear_timer(sk); sco_chan_del(sk, err); bh_unlock_sock(sk); sco_conn_lock(conn); conn->sk = NULL; sco_pi(sk)->conn = NULL; sco_conn_unlock(conn); if (conn->hcon) hci_conn_put(conn->hcon); sco_sock_kill(sk); } hcon->sco_data = NULL; kfree(conn); return 0; } static int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) { int err = 0; sco_conn_lock(conn); if (conn->sk) err = -EBUSY; else __sco_chan_add(conn, sk, parent); sco_conn_unlock(conn); return err; } static int sco_connect(struct sock *sk) { bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *dst = &bt_sk(sk)->dst; struct sco_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; int err, type; BT_DBG("%s -> %s", batostr(src), batostr(dst)); hdev = hci_get_route(dst, src); if (!hdev) return -EHOSTUNREACH; hci_dev_lock(hdev); if (lmp_esco_capable(hdev) && !disable_esco) type = ESCO_LINK; else type = SCO_LINK; hcon = hci_connect(hdev, type, dst, BDADDR_BREDR, BT_SECURITY_LOW, HCI_AT_NO_BONDING); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); goto done; } conn = sco_conn_add(hcon); if (!conn) { hci_conn_put(hcon); err = -ENOMEM; goto done; } /* Update source addr of the socket */ bacpy(src, conn->src); err = sco_chan_add(conn, sk, NULL); if (err) goto done; if (hcon->state == BT_CONNECTED) { sco_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; } else { sk->sk_state = BT_CONNECT; sco_sock_set_timer(sk, sk->sk_sndtimeo); } done: hci_dev_unlock(hdev); hci_dev_put(hdev); return err; } static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) { struct sco_conn *conn = sco_pi(sk)->conn; struct sk_buff *skb; int err; /* Check outgoing MTU */ if (len > conn->mtu) return -EINVAL; BT_DBG("sk %p len %d", sk, len); skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return err; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { kfree_skb(skb); return -EFAULT; } hci_send_sco(conn->hcon, skb); return len; } static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb) { struct sock *sk = sco_chan_get(conn); if (!sk) goto drop; BT_DBG("sk %p len %d", sk, skb->len); if (sk->sk_state != BT_CONNECTED) goto drop; if (!sock_queue_rcv_skb(sk, skb)) return; drop: kfree_skb(skb); } /* -------- Socket interface ---------- */ static struct sock *__sco_get_sock_listen_by_addr(bdaddr_t *ba) { struct hlist_node *node; struct sock *sk; sk_for_each(sk, node, &sco_sk_list.head) { if (sk->sk_state != BT_LISTEN) continue; if (!bacmp(&bt_sk(sk)->src, ba)) return sk; } return NULL; } /* Find socket listening on source bdaddr. * Returns closest match. */ static struct sock *sco_get_sock_listen(bdaddr_t *src) { struct sock *sk = NULL, *sk1 = NULL; struct hlist_node *node; read_lock(&sco_sk_list.lock); sk_for_each(sk, node, &sco_sk_list.head) { if (sk->sk_state != BT_LISTEN) continue; /* Exact match. */ if (!bacmp(&bt_sk(sk)->src, src)) break; /* Closest match */ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) sk1 = sk; } read_unlock(&sco_sk_list.lock); return node ? sk : sk1; } static void sco_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); } static void sco_sock_cleanup_listen(struct sock *parent) { struct sock *sk; BT_DBG("parent %p", parent); /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { sco_sock_close(sk); sco_sock_kill(sk); } parent->sk_state = BT_CLOSED; sock_set_flag(parent, SOCK_ZAPPED); } /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */ static void sco_sock_kill(struct sock *sk) { if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; BT_DBG("sk %p state %d", sk, sk->sk_state); /* Kill poor orphan */ bt_sock_unlink(&sco_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } static void __sco_sock_close(struct sock *sk) { BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); switch (sk->sk_state) { case BT_LISTEN: sco_sock_cleanup_listen(sk); break; case BT_CONNECTED: case BT_CONFIG: if (sco_pi(sk)->conn) { sk->sk_state = BT_DISCONN; sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT); hci_conn_put(sco_pi(sk)->conn->hcon); sco_pi(sk)->conn->hcon = NULL; } else sco_chan_del(sk, ECONNRESET); break; case BT_CONNECT: case BT_DISCONN: sco_chan_del(sk, ECONNRESET); break; default: sock_set_flag(sk, SOCK_ZAPPED); break; } } /* Must be called on unlocked socket. */ static void sco_sock_close(struct sock *sk) { sco_sock_clear_timer(sk); lock_sock(sk); __sco_sock_close(sk); release_sock(sk); sco_sock_kill(sk); } static void sco_sock_init(struct sock *sk, struct sock *parent) { BT_DBG("sk %p", sk); if (parent) { sk->sk_type = parent->sk_type; security_sk_clone(parent, sk); } } static struct proto sco_proto = { .name = "SCO", .owner = THIS_MODULE, .obj_size = sizeof(struct sco_pinfo) }; static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) { struct sock *sk; sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto); if (!sk) return NULL; sock_init_data(sock, sk); INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = sco_sock_destruct; sk->sk_sndtimeo = SCO_CONN_TIMEOUT; sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = proto; sk->sk_state = BT_OPEN; setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk); bt_sock_link(&sco_sk_list, sk); return sk; } #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int sco_sock_create(struct net *net, struct socket *sock, int protocol, int kern) #else static int sco_sock_create(struct net *net, struct socket *sock, int protocol) #endif { struct sock *sk; BT_DBG("sock %p", sock); sock->state = SS_UNCONNECTED; if (sock->type != SOCK_SEQPACKET) return -ESOCKTNOSUPPORT; sock->ops = &sco_sock_ops; sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC); if (!sk) return -ENOMEM; sco_sock_init(sk, NULL); return 0; } static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; lock_sock(sk); if (sk->sk_state != BT_OPEN) { err = -EBADFD; goto done; } if (sk->sk_type != SOCK_SEQPACKET) { err = -EINVAL; goto done; } bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr); sk->sk_state = BT_BOUND; done: release_sock(sk); return err; } static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p", sk); if (alen < sizeof(struct sockaddr_sco) || addr->sa_family != AF_BLUETOOTH) return -EINVAL; if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) return -EBADFD; if (sk->sk_type != SOCK_SEQPACKET) return -EINVAL; lock_sock(sk); /* Set destination address and psm */ bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); err = sco_connect(sk); if (err) goto done; err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK)); done: release_sock(sk); return err; } static int sco_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; bdaddr_t *src = &bt_sk(sk)->src; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); lock_sock(sk); if (sk->sk_state != BT_BOUND) { err = -EBADFD; goto done; } if (sk->sk_type != SOCK_SEQPACKET) { err = -EINVAL; goto done; } write_lock(&sco_sk_list.lock); if (__sco_get_sock_listen_by_addr(src)) { err = -EADDRINUSE; goto unlock; } sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; sk->sk_state = BT_LISTEN; unlock: write_unlock(&sco_sk_list.lock); done: release_sock(sk); return err; } static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags) { DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *ch; long timeo; int err = 0; lock_sock(sk); timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk_sleep(sk), &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; break; } ch = bt_accept_dequeue(sk, newsock); if (ch) break; if (!timeo) { err = -EAGAIN; break; } if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); if (err) goto done; newsock->state = SS_CONNECTED; BT_DBG("new socket %p", ch); done: release_sock(sk); return err; } static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) { struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; struct sock *sk = sock->sk; BT_DBG("sock %p, sk %p", sock, sk); addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_sco); if (peer) bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); else bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); return 0; } static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; int err; BT_DBG("sock %p, sk %p", sock, sk); err = sock_error(sk); if (err) return err; if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; lock_sock(sk); if (sk->sk_state == BT_CONNECTED) err = sco_send_frame(sk, msg, len); else err = -ENOTCONN; release_sock(sk); return err; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) #else static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) #endif { struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct sco_options opts; struct sco_conninfo cinfo; int len, err = 0; BT_DBG("sk %p", sk); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case SCO_OPTIONS: if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; break; } opts.mtu = sco_pi(sk)->conn->mtu; BT_DBG("mtu %d", opts.mtu); len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *)&opts, len)) err = -EFAULT; break; case SCO_CONNINFO: if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; break; } memset(&cinfo, 0, sizeof(cinfo)); cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *)&cinfo, len)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; int len, err = 0; BT_DBG("sk %p", sk); if (level == SOL_SCO) return sco_sock_getsockopt_old(sock, optname, optval, optlen); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int sco_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; lock_sock(sk); if (!sk->sk_shutdown) { sk->sk_shutdown = SHUTDOWN_MASK; sco_sock_clear_timer(sk); __sco_sock_close(sk); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); } release_sock(sk); return err; } static int sco_sock_release(struct socket *sock) { struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; sco_sock_close(sk); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) { lock_sock(sk); err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); release_sock(sk); } sock_orphan(sk); sco_sock_kill(sk); return err; } static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) { BT_DBG("conn %p", conn); sco_pi(sk)->conn = conn; conn->sk = sk; if (parent) bt_accept_enqueue(parent, sk); } /* Delete channel. * Must be called on the locked socket. */ static void sco_chan_del(struct sock *sk, int err) { struct sco_conn *conn; conn = sco_pi(sk)->conn; BT_DBG("sk %p, conn %p, err %d", sk, conn, err); sk->sk_state = BT_CLOSED; sk->sk_err = err; sk->sk_state_change(sk); sock_set_flag(sk, SOCK_ZAPPED); } static void sco_conn_ready(struct sco_conn *conn) { struct sock *parent; struct sock *sk = conn->sk; BT_DBG("conn %p", conn); sco_conn_lock(conn); if (sk) { sco_sock_clear_timer(sk); bh_lock_sock(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); bh_unlock_sock(sk); } else { parent = sco_get_sock_listen(conn->src); if (!parent) goto done; bh_lock_sock(parent); sk = sco_sock_alloc(sock_net(parent), NULL, BTPROTO_SCO, GFP_ATOMIC); if (!sk) { bh_unlock_sock(parent); goto done; } sco_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); sk->sk_state = BT_CONNECTED; /* Wake up parent */ parent->sk_data_ready(parent, 1); bh_unlock_sock(parent); } done: sco_conn_unlock(conn); } /* ----- SCO interface with lower layer (HCI) ----- */ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct sock *sk; struct hlist_node *node; int lm = 0; BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); /* Find listening sockets */ read_lock(&sco_sk_list.lock); sk_for_each(sk, node, &sco_sk_list.head) { if (sk->sk_state != BT_LISTEN) continue; if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) || !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { lm |= HCI_LM_ACCEPT; break; } } read_unlock(&sco_sk_list.lock); return lm; } void sco_connect_cfm(struct hci_conn *hcon, __u8 status) { BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); if (!status) { struct sco_conn *conn; conn = sco_conn_add(hcon); if (conn) sco_conn_ready(conn); } else sco_conn_del(hcon, bt_to_errno(status)); } void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); sco_conn_del(hcon, bt_to_errno(reason)); } int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb) { struct sco_conn *conn = hcon->sco_data; if (!conn) goto drop; BT_DBG("conn %p len %d", conn, skb->len); if (skb->len) { sco_recv_frame(conn, skb); return 0; } drop: kfree_skb(skb); return 0; } static int sco_debugfs_show(struct seq_file *f, void *p) { struct sock *sk; struct hlist_node *node; read_lock(&sco_sk_list.lock); sk_for_each(sk, node, &sco_sk_list.head) { seq_printf(f, "%s %s %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state); } read_unlock(&sco_sk_list.lock); return 0; } static int sco_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, sco_debugfs_show, inode->i_private); } static const struct file_operations sco_debugfs_fops = { .open = sco_debugfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *sco_debugfs; static const struct proto_ops sco_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = sco_sock_release, .bind = sco_sock_bind, .connect = sco_sock_connect, .listen = sco_sock_listen, .accept = sco_sock_accept, .getname = sco_sock_getname, .sendmsg = sco_sock_sendmsg, .recvmsg = bt_sock_recvmsg, .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, .shutdown = sco_sock_shutdown, .setsockopt = sco_sock_setsockopt, .getsockopt = sco_sock_getsockopt }; static const struct net_proto_family sco_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = sco_sock_create, }; int __init sco_init(void) { int err; err = proto_register(&sco_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_SCO, &sco_sock_family_ops); if (err < 0) { BT_ERR("SCO socket registration failed"); goto error; } err = bt_procfs_init(THIS_MODULE, &init_net, "sco", &sco_sk_list, NULL); if (err < 0) { BT_ERR("Failed to create SCO proc file"); bt_sock_unregister(BTPROTO_SCO); goto error; } if (bt_debugfs) { sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs, NULL, &sco_debugfs_fops); if (!sco_debugfs) BT_ERR("Failed to create SCO debug file"); } BT_INFO("SCO socket layer initialized"); return 0; error: proto_unregister(&sco_proto); return err; } void __exit sco_exit(void) { bt_procfs_cleanup(&init_net, "sco"); debugfs_remove(sco_debugfs); if (bt_sock_unregister(BTPROTO_SCO) < 0) BT_ERR("SCO socket unregistration failed"); proto_unregister(&sco_proto); } module_param(disable_esco, bool, 0644); MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation"); compat-drivers-2012-09-18/net/bluetooth/hci_sysfs.c0000644000175000017500000003341512026211315021317 0ustar mcgrofmcgrof/* Bluetooth HCI driver model support. */ #include #include #include #include static struct class *bt_class; struct dentry *bt_debugfs; EXPORT_SYMBOL_GPL(bt_debugfs); static inline char *link_typetostr(int type) { switch (type) { case ACL_LINK: return "ACL"; case SCO_LINK: return "SCO"; case ESCO_LINK: return "eSCO"; case LE_LINK: return "LE"; default: return "UNKNOWN"; } } static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_conn *conn = to_hci_conn(dev); return sprintf(buf, "%s\n", link_typetostr(conn->type)); } static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_conn *conn = to_hci_conn(dev); return sprintf(buf, "%s\n", batostr(&conn->dst)); } static ssize_t show_link_features(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_conn *conn = to_hci_conn(dev); return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", conn->features[0], conn->features[1], conn->features[2], conn->features[3], conn->features[4], conn->features[5], conn->features[6], conn->features[7]); } #define LINK_ATTR(_name, _mode, _show, _store) \ struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store) static LINK_ATTR(type, S_IRUGO, show_link_type, NULL); static LINK_ATTR(address, S_IRUGO, show_link_address, NULL); static LINK_ATTR(features, S_IRUGO, show_link_features, NULL); static struct attribute *bt_link_attrs[] = { &link_attr_type.attr, &link_attr_address.attr, &link_attr_features.attr, NULL }; static struct attribute_group bt_link_group = { .attrs = bt_link_attrs, }; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static const struct attribute_group *bt_link_groups[] = { #else static struct attribute_group *bt_link_groups[] = { #endif &bt_link_group, NULL }; static void bt_link_release(struct device *dev) { struct hci_conn *conn = to_hci_conn(dev); kfree(conn); } static struct device_type bt_link = { .name = "link", .groups = bt_link_groups, .release = bt_link_release, }; /* * The rfcomm tty device will possibly retain even when conn * is down, and sysfs doesn't support move zombie device, * so we should move the device before conn device is destroyed. */ static int __match_tty(struct device *dev, void *data) { return !strncmp(dev_name(dev), "rfcomm", 6); } void hci_conn_init_sysfs(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; BT_DBG("conn %p", conn); conn->dev.type = &bt_link; conn->dev.class = bt_class; conn->dev.parent = &hdev->dev; device_initialize(&conn->dev); } void hci_conn_add_sysfs(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; BT_DBG("conn %p", conn); dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); if (device_add(&conn->dev) < 0) { BT_ERR("Failed to register connection device"); return; } hci_dev_hold(hdev); } void hci_conn_del_sysfs(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; if (!device_is_registered(&conn->dev)) return; while (1) { struct device *dev; dev = device_find_child(&conn->dev, NULL, __match_tty); if (!dev) break; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) device_move(dev, NULL, DPM_ORDER_DEV_LAST); #else device_move(dev, NULL); #endif put_device(dev); } device_del(&conn->dev); put_device(&conn->dev); hci_dev_put(hdev); } static inline char *host_bustostr(int bus) { switch (bus) { case HCI_VIRTUAL: return "VIRTUAL"; case HCI_USB: return "USB"; case HCI_PCCARD: return "PCCARD"; case HCI_UART: return "UART"; case HCI_RS232: return "RS232"; case HCI_PCI: return "PCI"; case HCI_SDIO: return "SDIO"; default: return "UNKNOWN"; } } static inline char *host_typetostr(int type) { switch (type) { case HCI_BREDR: return "BR/EDR"; case HCI_AMP: return "AMP"; default: return "UNKNOWN"; } } static ssize_t show_bus(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%s\n", host_bustostr(hdev->bus)); } static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type)); } static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); char name[HCI_MAX_NAME_LENGTH + 1]; int i; for (i = 0; i < HCI_MAX_NAME_LENGTH; i++) name[i] = hdev->dev_name[i]; name[HCI_MAX_NAME_LENGTH] = '\0'; return sprintf(buf, "%s\n", name); } static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "0x%.2x%.2x%.2x\n", hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); } static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%s\n", batostr(&hdev->bdaddr)); } static ssize_t show_features(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", hdev->features[0], hdev->features[1], hdev->features[2], hdev->features[3], hdev->features[4], hdev->features[5], hdev->features[6], hdev->features[7]); } static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->manufacturer); } static ssize_t show_hci_version(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->hci_ver); } static ssize_t show_hci_revision(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->hci_rev); } static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->idle_timeout); } static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct hci_dev *hdev = to_hci_dev(dev); unsigned int val; int rv; rv = kstrtouint(buf, 0, &val); if (rv < 0) return rv; if (val != 0 && (val < 500 || val > 3600000)) return -EINVAL; hdev->idle_timeout = val; return count; } static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->sniff_max_interval); } static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct hci_dev *hdev = to_hci_dev(dev); u16 val; int rv; rv = kstrtou16(buf, 0, &val); if (rv < 0) return rv; if (val == 0 || val % 2 || val < hdev->sniff_min_interval) return -EINVAL; hdev->sniff_max_interval = val; return count; } static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->sniff_min_interval); } static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct hci_dev *hdev = to_hci_dev(dev); u16 val; int rv; rv = kstrtou16(buf, 0, &val); if (rv < 0) return rv; if (val == 0 || val % 2 || val > hdev->sniff_max_interval) return -EINVAL; hdev->sniff_min_interval = val; return count; } static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL); static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(class, S_IRUGO, show_class, NULL); static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); static DEVICE_ATTR(features, S_IRUGO, show_features, NULL); static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL); static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL); static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL); static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR, show_idle_timeout, store_idle_timeout); static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR, show_sniff_max_interval, store_sniff_max_interval); static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, show_sniff_min_interval, store_sniff_min_interval); static struct attribute *bt_host_attrs[] = { &dev_attr_bus.attr, &dev_attr_type.attr, &dev_attr_name.attr, &dev_attr_class.attr, &dev_attr_address.attr, &dev_attr_features.attr, &dev_attr_manufacturer.attr, &dev_attr_hci_version.attr, &dev_attr_hci_revision.attr, &dev_attr_idle_timeout.attr, &dev_attr_sniff_max_interval.attr, &dev_attr_sniff_min_interval.attr, NULL }; static struct attribute_group bt_host_group = { .attrs = bt_host_attrs, }; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static const struct attribute_group *bt_host_groups[] = { #else static struct attribute_group *bt_host_groups[] = { #endif &bt_host_group, NULL }; static void bt_host_release(struct device *dev) { struct hci_dev *hdev = to_hci_dev(dev); kfree(hdev); module_put(THIS_MODULE); } static struct device_type bt_host = { .name = "host", .groups = bt_host_groups, .release = bt_host_release, }; static int inquiry_cache_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; hci_dev_lock(hdev); list_for_each_entry(e, &cache->all, all) { struct inquiry_data *data = &e->data; seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", batostr(&data->bdaddr), data->pscan_rep_mode, data->pscan_period_mode, data->pscan_mode, data->dev_class[2], data->dev_class[1], data->dev_class[0], __le16_to_cpu(data->clock_offset), data->rssi, data->ssp_mode, e->timestamp); } hci_dev_unlock(hdev); return 0; } static int inquiry_cache_open(struct inode *inode, struct file *file) { return single_open(file, inquiry_cache_show, inode->i_private); } static const struct file_operations inquiry_cache_fops = { .open = inquiry_cache_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int blacklist_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; struct bdaddr_list *b; hci_dev_lock(hdev); list_for_each_entry(b, &hdev->blacklist, list) seq_printf(f, "%s\n", batostr(&b->bdaddr)); hci_dev_unlock(hdev); return 0; } static int blacklist_open(struct inode *inode, struct file *file) { return single_open(file, blacklist_show, inode->i_private); } static const struct file_operations blacklist_fops = { .open = blacklist_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static void print_bt_uuid(struct seq_file *f, u8 *uuid) { __be32 data0, data4; __be16 data1, data2, data3, data5; memcpy(&data0, &uuid[0], 4); memcpy(&data1, &uuid[4], 2); memcpy(&data2, &uuid[6], 2); memcpy(&data3, &uuid[8], 2); memcpy(&data4, &uuid[10], 4); memcpy(&data5, &uuid[14], 2); seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n", ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), ntohl(data4), ntohs(data5)); } static int uuids_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; struct bt_uuid *uuid; hci_dev_lock(hdev); list_for_each_entry(uuid, &hdev->uuids, list) print_bt_uuid(f, uuid->uuid); hci_dev_unlock(hdev); return 0; } static int uuids_open(struct inode *inode, struct file *file) { return single_open(file, uuids_show, inode->i_private); } static const struct file_operations uuids_fops = { .open = uuids_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int auto_accept_delay_set(void *data, u64 val) { struct hci_dev *hdev = data; hci_dev_lock(hdev); hdev->auto_accept_delay = val; hci_dev_unlock(hdev); return 0; } static int auto_accept_delay_get(void *data, u64 *val) { struct hci_dev *hdev = data; hci_dev_lock(hdev); *val = hdev->auto_accept_delay; hci_dev_unlock(hdev); return 0; } DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, auto_accept_delay_set, "%llu\n"); void hci_init_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; dev->type = &bt_host; dev->class = bt_class; __module_get(THIS_MODULE); device_initialize(dev); } int hci_add_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; int err; BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); dev_set_name(dev, "%s", hdev->name); err = device_add(dev); if (err < 0) return err; if (!bt_debugfs) return 0; hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs); if (!hdev->debugfs) return 0; debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev, &inquiry_cache_fops); debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, &blacklist_fops); debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev, &auto_accept_delay_fops); return 0; } void hci_del_sysfs(struct hci_dev *hdev) { BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); debugfs_remove_recursive(hdev->debugfs); device_del(&hdev->dev); } int __init bt_sysfs_init(void) { bt_debugfs = debugfs_create_dir("bluetooth", NULL); bt_class = class_create(THIS_MODULE, "bluetooth"); if (IS_ERR(bt_class)) return PTR_ERR(bt_class); return 0; } void bt_sysfs_cleanup(void) { class_destroy(bt_class); debugfs_remove_recursive(bt_debugfs); } compat-drivers-2012-09-18/net/bluetooth/hci_sock.c0000644000175000017500000005602412026211315021110 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth HCI sockets. */ #include #include #include #include #include #include static atomic_t monitor_promisc = ATOMIC_INIT(0); /* ----- HCI socket interface ----- */ static inline int hci_test_bit(int nr, void *addr) { return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); } /* Security filter */ static struct hci_sec_filter hci_sec_filter = { /* Packet types */ 0x10, /* Events */ { 0x1000d9fe, 0x0000b00c }, /* Commands */ { { 0x0 }, /* OGF_LINK_CTL */ { 0xbe000006, 0x00000001, 0x00000000, 0x00 }, /* OGF_LINK_POLICY */ { 0x00005200, 0x00000000, 0x00000000, 0x00 }, /* OGF_HOST_CTL */ { 0xaab00200, 0x2b402aaa, 0x05220154, 0x00 }, /* OGF_INFO_PARAM */ { 0x000002be, 0x00000000, 0x00000000, 0x00 }, /* OGF_STATUS_PARAM */ { 0x000000ea, 0x00000000, 0x00000000, 0x00 } } }; static struct bt_sock_list hci_sk_list = { .lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock) }; /* Send frame to RAW socket */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) { struct sock *sk; struct hlist_node *node; struct sk_buff *skb_copy = NULL; BT_DBG("hdev %p len %d", hdev, skb->len); read_lock(&hci_sk_list.lock); sk_for_each(sk, node, &hci_sk_list.head) { struct hci_filter *flt; struct sk_buff *nskb; if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) continue; /* Don't send frame to the socket it came from */ if (skb->sk == sk) continue; if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) continue; /* Apply filter */ flt = &hci_pi(sk)->filter; if (!test_bit((bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) ? 0 : (bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask)) continue; if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) { int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); if (!hci_test_bit(evt, &flt->event_mask)) continue; if (flt->opcode && ((evt == HCI_EV_CMD_COMPLETE && flt->opcode != get_unaligned((__le16 *)(skb->data + 3))) || (evt == HCI_EV_CMD_STATUS && flt->opcode != get_unaligned((__le16 *)(skb->data + 4))))) continue; } if (!skb_copy) { /* Create a private copy with headroom */ skb_copy = __pskb_copy(skb, 1, GFP_ATOMIC); if (!skb_copy) continue; /* Put type byte before the data */ memcpy(skb_push(skb_copy, 1), &bt_cb(skb)->pkt_type, 1); } nskb = skb_clone(skb_copy, GFP_ATOMIC); if (!nskb) continue; if (sock_queue_rcv_skb(sk, nskb)) kfree_skb(nskb); } read_unlock(&hci_sk_list.lock); kfree_skb(skb_copy); } /* Send frame to control socket */ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) { struct sock *sk; struct hlist_node *node; BT_DBG("len %d", skb->len); read_lock(&hci_sk_list.lock); sk_for_each(sk, node, &hci_sk_list.head) { struct sk_buff *nskb; /* Skip the original socket */ if (sk == skip_sk) continue; if (sk->sk_state != BT_BOUND) continue; if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL) continue; nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) continue; if (sock_queue_rcv_skb(sk, nskb)) kfree_skb(nskb); } read_unlock(&hci_sk_list.lock); } /* Send frame to monitor socket */ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) { struct sock *sk; struct hlist_node *node; struct sk_buff *skb_copy = NULL; __le16 opcode; if (!atomic_read(&monitor_promisc)) return; BT_DBG("hdev %p len %d", hdev, skb->len); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT); break; case HCI_EVENT_PKT: opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT); break; case HCI_ACLDATA_PKT: if (bt_cb(skb)->incoming) opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT); else opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT); break; case HCI_SCODATA_PKT: if (bt_cb(skb)->incoming) opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT); else opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT); break; default: return; } read_lock(&hci_sk_list.lock); sk_for_each(sk, node, &hci_sk_list.head) { struct sk_buff *nskb; if (sk->sk_state != BT_BOUND) continue; if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) continue; if (!skb_copy) { struct hci_mon_hdr *hdr; /* Create a private copy with headroom */ skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC); if (!skb_copy) continue; /* Put header before the data */ hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE); hdr->opcode = opcode; hdr->index = cpu_to_le16(hdev->id); hdr->len = cpu_to_le16(skb->len); } nskb = skb_clone(skb_copy, GFP_ATOMIC); if (!nskb) continue; if (sock_queue_rcv_skb(sk, nskb)) kfree_skb(nskb); } read_unlock(&hci_sk_list.lock); kfree_skb(skb_copy); } static void send_monitor_event(struct sk_buff *skb) { struct sock *sk; struct hlist_node *node; BT_DBG("len %d", skb->len); read_lock(&hci_sk_list.lock); sk_for_each(sk, node, &hci_sk_list.head) { struct sk_buff *nskb; if (sk->sk_state != BT_BOUND) continue; if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) continue; nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) continue; if (sock_queue_rcv_skb(sk, nskb)) kfree_skb(nskb); } read_unlock(&hci_sk_list.lock); } static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) { struct hci_mon_hdr *hdr; struct hci_mon_new_index *ni; struct sk_buff *skb; __le16 opcode; switch (event) { case HCI_DEV_REG: skb = bt_skb_alloc(HCI_MON_NEW_INDEX_SIZE, GFP_ATOMIC); if (!skb) return NULL; ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE); ni->type = hdev->dev_type; ni->bus = hdev->bus; bacpy(&ni->bdaddr, &hdev->bdaddr); memcpy(ni->name, hdev->name, 8); opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX); break; case HCI_DEV_UNREG: skb = bt_skb_alloc(0, GFP_ATOMIC); if (!skb) return NULL; opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX); break; default: return NULL; } __net_timestamp(skb); hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE); hdr->opcode = opcode; hdr->index = cpu_to_le16(hdev->id); hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE); return skb; } static void send_monitor_replay(struct sock *sk) { struct hci_dev *hdev; read_lock(&hci_dev_list_lock); list_for_each_entry(hdev, &hci_dev_list, list) { struct sk_buff *skb; skb = create_monitor_event(hdev, HCI_DEV_REG); if (!skb) continue; if (sock_queue_rcv_skb(sk, skb)) kfree_skb(skb); } read_unlock(&hci_dev_list_lock); } /* Generate internal stack event */ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) { struct hci_event_hdr *hdr; struct hci_ev_stack_internal *ev; struct sk_buff *skb; skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC); if (!skb) return; hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE); hdr->evt = HCI_EV_STACK_INTERNAL; hdr->plen = sizeof(*ev) + dlen; ev = (void *) skb_put(skb, sizeof(*ev) + dlen); ev->type = type; memcpy(ev->data, data, dlen); bt_cb(skb)->incoming = 1; __net_timestamp(skb); bt_cb(skb)->pkt_type = HCI_EVENT_PKT; skb->dev = (void *) hdev; hci_send_to_sock(hdev, skb); kfree_skb(skb); } void hci_sock_dev_event(struct hci_dev *hdev, int event) { struct hci_ev_si_device ev; BT_DBG("hdev %s event %d", hdev->name, event); /* Send event to monitor */ if (atomic_read(&monitor_promisc)) { struct sk_buff *skb; skb = create_monitor_event(hdev, event); if (skb) { send_monitor_event(skb); kfree_skb(skb); } } /* Send event to sockets */ ev.event = event; ev.dev_id = hdev->id; hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev); if (event == HCI_DEV_UNREG) { struct sock *sk; struct hlist_node *node; /* Detach sockets from device */ read_lock(&hci_sk_list.lock); sk_for_each(sk, node, &hci_sk_list.head) { bh_lock_sock_nested(sk); if (hci_pi(sk)->hdev == hdev) { hci_pi(sk)->hdev = NULL; sk->sk_err = EPIPE; sk->sk_state = BT_OPEN; sk->sk_state_change(sk); hci_dev_put(hdev); } bh_unlock_sock(sk); } read_unlock(&hci_sk_list.lock); } } static int hci_sock_release(struct socket *sock) { struct sock *sk = sock->sk; struct hci_dev *hdev; BT_DBG("sock %p sk %p", sock, sk); if (!sk) return 0; hdev = hci_pi(sk)->hdev; if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR) atomic_dec(&monitor_promisc); bt_sock_unlink(&hci_sk_list, sk); if (hdev) { atomic_dec(&hdev->promisc); hci_dev_put(hdev); } sock_orphan(sk); skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); sock_put(sk); return 0; } static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; hci_dev_lock(hdev); err = hci_blacklist_add(hdev, &bdaddr, 0); hci_dev_unlock(hdev); return err; } static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; hci_dev_lock(hdev); err = hci_blacklist_del(hdev, &bdaddr, 0); hci_dev_unlock(hdev); return err; } /* Ioctls that require bound socket */ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) { struct hci_dev *hdev = hci_pi(sk)->hdev; if (!hdev) return -EBADFD; switch (cmd) { case HCISETRAW: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) return -EPERM; if (arg) set_bit(HCI_RAW, &hdev->flags); else clear_bit(HCI_RAW, &hdev->flags); return 0; case HCIGETCONNINFO: return hci_get_conn_info(hdev, (void __user *) arg); case HCIGETAUTHINFO: return hci_get_auth_info(hdev, (void __user *) arg); case HCIBLOCKADDR: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_sock_blacklist_add(hdev, (void __user *) arg); case HCIUNBLOCKADDR: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_sock_blacklist_del(hdev, (void __user *) arg); default: if (hdev->ioctl) return hdev->ioctl(hdev, cmd, arg); return -EINVAL; } } static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; void __user *argp = (void __user *) arg; int err; BT_DBG("cmd %x arg %lx", cmd, arg); switch (cmd) { case HCIGETDEVLIST: return hci_get_dev_list(argp); case HCIGETDEVINFO: return hci_get_dev_info(argp); case HCIGETCONNLIST: return hci_get_conn_list(argp); case HCIDEVUP: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_open(arg); case HCIDEVDOWN: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_close(arg); case HCIDEVRESET: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_reset(arg); case HCIDEVRESTAT: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_reset_stat(arg); case HCISETSCAN: case HCISETAUTH: case HCISETENCRYPT: case HCISETPTYPE: case HCISETLINKPOL: case HCISETLINKMODE: case HCISETACLMTU: case HCISETSCOMTU: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_cmd(cmd, argp); case HCIINQUIRY: return hci_inquiry(argp); default: lock_sock(sk); err = hci_sock_bound_ioctl(sk, cmd, arg); release_sock(sk); return err; } } static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sockaddr_hci haddr; struct sock *sk = sock->sk; struct hci_dev *hdev = NULL; int len, err = 0; BT_DBG("sock %p sk %p", sock, sk); if (!addr) return -EINVAL; memset(&haddr, 0, sizeof(haddr)); len = min_t(unsigned int, sizeof(haddr), addr_len); memcpy(&haddr, addr, len); if (haddr.hci_family != AF_BLUETOOTH) return -EINVAL; lock_sock(sk); if (sk->sk_state == BT_BOUND) { err = -EALREADY; goto done; } switch (haddr.hci_channel) { case HCI_CHANNEL_RAW: if (hci_pi(sk)->hdev) { err = -EALREADY; goto done; } if (haddr.hci_dev != HCI_DEV_NONE) { hdev = hci_dev_get(haddr.hci_dev); if (!hdev) { err = -ENODEV; goto done; } atomic_inc(&hdev->promisc); } hci_pi(sk)->hdev = hdev; break; case HCI_CHANNEL_CONTROL: if (haddr.hci_dev != HCI_DEV_NONE) { err = -EINVAL; goto done; } if (!capable(CAP_NET_ADMIN)) { err = -EPERM; goto done; } break; case HCI_CHANNEL_MONITOR: if (haddr.hci_dev != HCI_DEV_NONE) { err = -EINVAL; goto done; } if (!capable(CAP_NET_RAW)) { err = -EPERM; goto done; } send_monitor_replay(sk); atomic_inc(&monitor_promisc); break; default: err = -EINVAL; goto done; } hci_pi(sk)->channel = haddr.hci_channel; sk->sk_state = BT_BOUND; done: release_sock(sk); return err; } static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) { struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; struct sock *sk = sock->sk; struct hci_dev *hdev = hci_pi(sk)->hdev; BT_DBG("sock %p sk %p", sock, sk); if (!hdev) return -EBADFD; lock_sock(sk); *addr_len = sizeof(*haddr); haddr->hci_family = AF_BLUETOOTH; haddr->hci_dev = hdev->id; haddr->hci_channel= 0; release_sock(sk); return 0; } static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) { __u32 mask = hci_pi(sk)->cmsg_mask; if (mask & HCI_CMSG_DIR) { int incoming = bt_cb(skb)->incoming; put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(incoming), &incoming); } if (mask & HCI_CMSG_TSTAMP) { #ifdef CONFIG_COMPAT struct compat_timeval ctv; #endif struct timeval tv; void *data; int len; skb_get_timestamp(skb, &tv); data = &tv; len = sizeof(tv); #ifdef CONFIG_COMPAT if (!COMPAT_USE_64BIT_TIME && (msg->msg_flags & MSG_CMSG_COMPAT)) { ctv.tv_sec = tv.tv_sec; ctv.tv_usec = tv.tv_usec; data = &ctv; len = sizeof(ctv); } #endif put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, len, data); } } static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; struct sk_buff *skb; int copied, err; BT_DBG("sock %p, sk %p", sock, sk); if (flags & (MSG_OOB)) return -EOPNOTSUPP; if (sk->sk_state == BT_CLOSED) return 0; skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) return err; msg->msg_namelen = 0; copied = skb->len; if (len < copied) { msg->msg_flags |= MSG_TRUNC; copied = len; } skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW: hci_sock_cmsg(sk, msg, skb); break; case HCI_CHANNEL_CONTROL: case HCI_CHANNEL_MONITOR: sock_recv_timestamp(msg, sk, skb); break; } skb_free_datagram(sk, skb); return err ? : copied; } static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct hci_dev *hdev; struct sk_buff *skb; int err; BT_DBG("sock %p sk %p", sock, sk); if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) return -EINVAL; if (len < 4 || len > HCI_MAX_FRAME_SIZE) return -EINVAL; lock_sock(sk); switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW: break; case HCI_CHANNEL_CONTROL: err = mgmt_control(sk, msg, len); goto done; case HCI_CHANNEL_MONITOR: err = -EOPNOTSUPP; goto done; default: err = -EINVAL; goto done; } hdev = hci_pi(sk)->hdev; if (!hdev) { err = -EBADFD; goto done; } if (!test_bit(HCI_UP, &hdev->flags)) { err = -ENETDOWN; goto done; } skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) goto done; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { err = -EFAULT; goto drop; } bt_cb(skb)->pkt_type = *((unsigned char *) skb->data); skb_pull(skb, 1); skb->dev = (void *) hdev; if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { u16 opcode = get_unaligned_le16(skb->data); u16 ogf = hci_opcode_ogf(opcode); u16 ocf = hci_opcode_ocf(opcode); if (((ogf > HCI_SFLT_MAX_OGF) || !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && !capable(CAP_NET_RAW)) { err = -EPERM; goto drop; } if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) { skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } else { skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); } } else { if (!capable(CAP_NET_RAW)) { err = -EPERM; goto drop; } skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } err = len; done: release_sock(sk); return err; drop: kfree_skb(skb); goto done; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len) #else static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int len) #endif { struct hci_ufilter uf = { .opcode = 0 }; struct sock *sk = sock->sk; int err = 0, opt = 0; BT_DBG("sk %p, opt %d", sk, optname); lock_sock(sk); if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { err = -EINVAL; goto done; } switch (optname) { case HCI_DATA_DIR: if (get_user(opt, (int __user *)optval)) { err = -EFAULT; break; } if (opt) hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR; else hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR; break; case HCI_TIME_STAMP: if (get_user(opt, (int __user *)optval)) { err = -EFAULT; break; } if (opt) hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP; else hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP; break; case HCI_FILTER: { struct hci_filter *f = &hci_pi(sk)->filter; uf.type_mask = f->type_mask; uf.opcode = f->opcode; uf.event_mask[0] = *((u32 *) f->event_mask + 0); uf.event_mask[1] = *((u32 *) f->event_mask + 1); } len = min_t(unsigned int, len, sizeof(uf)); if (copy_from_user(&uf, optval, len)) { err = -EFAULT; break; } if (!capable(CAP_NET_RAW)) { uf.type_mask &= hci_sec_filter.type_mask; uf.event_mask[0] &= *((u32 *) hci_sec_filter.event_mask + 0); uf.event_mask[1] &= *((u32 *) hci_sec_filter.event_mask + 1); } { struct hci_filter *f = &hci_pi(sk)->filter; f->type_mask = uf.type_mask; f->opcode = uf.opcode; *((u32 *) f->event_mask + 0) = uf.event_mask[0]; *((u32 *) f->event_mask + 1) = uf.event_mask[1]; } break; default: err = -ENOPROTOOPT; break; } done: release_sock(sk); return err; } static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct hci_ufilter uf; struct sock *sk = sock->sk; int len, opt, err = 0; BT_DBG("sk %p, opt %d", sk, optname); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { err = -EINVAL; goto done; } switch (optname) { case HCI_DATA_DIR: if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR) opt = 1; else opt = 0; if (put_user(opt, optval)) err = -EFAULT; break; case HCI_TIME_STAMP: if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP) opt = 1; else opt = 0; if (put_user(opt, optval)) err = -EFAULT; break; case HCI_FILTER: { struct hci_filter *f = &hci_pi(sk)->filter; memset(&uf, 0, sizeof(uf)); uf.type_mask = f->type_mask; uf.opcode = f->opcode; uf.event_mask[0] = *((u32 *) f->event_mask + 0); uf.event_mask[1] = *((u32 *) f->event_mask + 1); } len = min_t(unsigned int, len, sizeof(uf)); if (copy_to_user(optval, &uf, len)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } done: release_sock(sk); return err; } static const struct proto_ops hci_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = hci_sock_release, .bind = hci_sock_bind, .getname = hci_sock_getname, .sendmsg = hci_sock_sendmsg, .recvmsg = hci_sock_recvmsg, .ioctl = hci_sock_ioctl, .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = hci_sock_setsockopt, .getsockopt = hci_sock_getsockopt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .mmap = sock_no_mmap }; static struct proto hci_sk_proto = { .name = "HCI", .owner = THIS_MODULE, .obj_size = sizeof(struct hci_pinfo) }; #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int hci_sock_create(struct net *net, struct socket *sock, int protocol, int kern) #else static int hci_sock_create(struct net *net, struct socket *sock, int protocol) #endif { struct sock *sk; BT_DBG("sock %p", sock); if (sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; sock->ops = &hci_sock_ops; sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto); if (!sk) return -ENOMEM; sock_init_data(sock, sk); sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = protocol; sock->state = SS_UNCONNECTED; sk->sk_state = BT_OPEN; bt_sock_link(&hci_sk_list, sk); return 0; } static const struct net_proto_family hci_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = hci_sock_create, }; int __init hci_sock_init(void) { int err; err = proto_register(&hci_sk_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_HCI, &hci_sock_family_ops); if (err < 0) { BT_ERR("HCI socket registration failed"); goto error; } err = bt_procfs_init(THIS_MODULE, &init_net, "hci", &hci_sk_list, NULL); if (err < 0) { BT_ERR("Failed to create HCI proc file"); bt_sock_unregister(BTPROTO_HCI); goto error; } BT_INFO("HCI socket layer initialized"); return 0; error: proto_unregister(&hci_sk_proto); return err; } void hci_sock_cleanup(void) { bt_procfs_cleanup(&init_net, "hci"); if (bt_sock_unregister(BTPROTO_HCI) < 0) BT_ERR("HCI socket unregistration failed"); proto_unregister(&hci_sk_proto); } compat-drivers-2012-09-18/net/bluetooth/af_bluetooth.c0000644000175000017500000004017412026211315022000 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth address family and sockets. */ #include #include #include #include #define VERSION "2.16" /* Bluetooth sockets */ #define BT_MAX_PROTO 8 static const struct net_proto_family *bt_proto[BT_MAX_PROTO]; static DEFINE_RWLOCK(bt_proto_lock); static struct lock_class_key bt_lock_key[BT_MAX_PROTO]; static const char *const bt_key_strings[BT_MAX_PROTO] = { "sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP", "sk_lock-AF_BLUETOOTH-BTPROTO_HCI", "sk_lock-AF_BLUETOOTH-BTPROTO_SCO", "sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM", "sk_lock-AF_BLUETOOTH-BTPROTO_BNEP", "sk_lock-AF_BLUETOOTH-BTPROTO_CMTP", "sk_lock-AF_BLUETOOTH-BTPROTO_HIDP", "sk_lock-AF_BLUETOOTH-BTPROTO_AVDTP", }; static struct lock_class_key bt_slock_key[BT_MAX_PROTO]; static const char *const bt_slock_key_strings[BT_MAX_PROTO] = { "slock-AF_BLUETOOTH-BTPROTO_L2CAP", "slock-AF_BLUETOOTH-BTPROTO_HCI", "slock-AF_BLUETOOTH-BTPROTO_SCO", "slock-AF_BLUETOOTH-BTPROTO_RFCOMM", "slock-AF_BLUETOOTH-BTPROTO_BNEP", "slock-AF_BLUETOOTH-BTPROTO_CMTP", "slock-AF_BLUETOOTH-BTPROTO_HIDP", "slock-AF_BLUETOOTH-BTPROTO_AVDTP", }; void bt_sock_reclassify_lock(struct sock *sk, int proto) { BUG_ON(!sk); BUG_ON(sock_owned_by_user(sk)); sock_lock_init_class_and_name(sk, bt_slock_key_strings[proto], &bt_slock_key[proto], bt_key_strings[proto], &bt_lock_key[proto]); } EXPORT_SYMBOL(bt_sock_reclassify_lock); int bt_sock_register(int proto, const struct net_proto_family *ops) { int err = 0; if (proto < 0 || proto >= BT_MAX_PROTO) return -EINVAL; write_lock(&bt_proto_lock); if (bt_proto[proto]) err = -EEXIST; else bt_proto[proto] = ops; write_unlock(&bt_proto_lock); return err; } EXPORT_SYMBOL(bt_sock_register); int bt_sock_unregister(int proto) { int err = 0; if (proto < 0 || proto >= BT_MAX_PROTO) return -EINVAL; write_lock(&bt_proto_lock); if (!bt_proto[proto]) err = -ENOENT; else bt_proto[proto] = NULL; write_unlock(&bt_proto_lock); return err; } EXPORT_SYMBOL(bt_sock_unregister); #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int bt_sock_create(struct net *net, struct socket *sock, int proto, int kern) #else static int bt_sock_create(struct net *net, struct socket *sock, int proto) #endif { int err; if (net != &init_net) return -EAFNOSUPPORT; if (proto < 0 || proto >= BT_MAX_PROTO) return -EINVAL; if (!bt_proto[proto]) request_module("bt-proto-%d", proto); err = -EPROTONOSUPPORT; read_lock(&bt_proto_lock); if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) { #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) err = bt_proto[proto]->create(net, sock, proto, kern); #else err = bt_proto[proto]->create(net, sock, proto); #endif if (!err) bt_sock_reclassify_lock(sock->sk, proto); module_put(bt_proto[proto]->owner); } read_unlock(&bt_proto_lock); return err; } void bt_sock_link(struct bt_sock_list *l, struct sock *sk) { write_lock(&l->lock); sk_add_node(sk, &l->head); write_unlock(&l->lock); } EXPORT_SYMBOL(bt_sock_link); void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk) { write_lock(&l->lock); sk_del_node_init(sk); write_unlock(&l->lock); } EXPORT_SYMBOL(bt_sock_unlink); void bt_accept_enqueue(struct sock *parent, struct sock *sk) { BT_DBG("parent %p, sk %p", parent, sk); sock_hold(sk); list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); bt_sk(sk)->parent = parent; parent->sk_ack_backlog++; } EXPORT_SYMBOL(bt_accept_enqueue); void bt_accept_unlink(struct sock *sk) { BT_DBG("sk %p state %d", sk, sk->sk_state); list_del_init(&bt_sk(sk)->accept_q); bt_sk(sk)->parent->sk_ack_backlog--; bt_sk(sk)->parent = NULL; sock_put(sk); } EXPORT_SYMBOL(bt_accept_unlink); struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) { struct list_head *p, *n; struct sock *sk; BT_DBG("parent %p", parent); list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); lock_sock(sk); /* FIXME: Is this check still needed */ if (sk->sk_state == BT_CLOSED) { release_sock(sk); bt_accept_unlink(sk); continue; } if (sk->sk_state == BT_CONNECTED || !newsock || test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) { bt_accept_unlink(sk); if (newsock) sock_graft(sk, newsock); release_sock(sk); return sk; } release_sock(sk); } return NULL; } EXPORT_SYMBOL(bt_accept_dequeue); int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; struct sk_buff *skb; size_t copied; int err; BT_DBG("sock %p sk %p len %zu", sock, sk, len); if (flags & (MSG_OOB)) return -EOPNOTSUPP; skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) { if (sk->sk_shutdown & RCV_SHUTDOWN) return 0; return err; } msg->msg_namelen = 0; copied = skb->len; if (len < copied) { msg->msg_flags |= MSG_TRUNC; copied = len; } skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (err == 0) sock_recv_ts_and_drops(msg, sk, skb); skb_free_datagram(sk, skb); return err ? : copied; } EXPORT_SYMBOL(bt_sock_recvmsg); static long bt_sock_data_wait(struct sock *sk, long timeo) { DECLARE_WAITQUEUE(wait, current); add_wait_queue(sk_sleep(sk), &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (!skb_queue_empty(&sk->sk_receive_queue)) break; if (sk->sk_err || (sk->sk_shutdown & RCV_SHUTDOWN)) break; if (signal_pending(current) || !timeo) break; set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); return timeo; } int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; int err = 0; size_t target, copied = 0; long timeo; if (flags & MSG_OOB) return -EOPNOTSUPP; msg->msg_namelen = 0; BT_DBG("sk %p size %zu", sk, size); lock_sock(sk); target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); do { struct sk_buff *skb; int chunk; skb = skb_dequeue(&sk->sk_receive_queue); if (!skb) { if (copied >= target) break; err = sock_error(sk); if (err) break; if (sk->sk_shutdown & RCV_SHUTDOWN) break; err = -EAGAIN; if (!timeo) break; timeo = bt_sock_data_wait(sk, timeo); if (signal_pending(current)) { err = sock_intr_errno(timeo); goto out; } continue; } chunk = min_t(unsigned int, skb->len, size); if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) { skb_queue_head(&sk->sk_receive_queue, skb); if (!copied) copied = -EFAULT; break; } copied += chunk; size -= chunk; sock_recv_ts_and_drops(msg, sk, skb); if (!(flags & MSG_PEEK)) { int skb_len = skb_headlen(skb); if (chunk <= skb_len) { __skb_pull(skb, chunk); } else { struct sk_buff *frag; __skb_pull(skb, skb_len); chunk -= skb_len; skb_walk_frags(skb, frag) { if (chunk <= frag->len) { /* Pulling partial data */ skb->len -= chunk; skb->data_len -= chunk; __skb_pull(frag, chunk); break; } else if (frag->len) { /* Pulling all frag data */ chunk -= frag->len; skb->len -= frag->len; skb->data_len -= frag->len; __skb_pull(frag, frag->len); } } } if (skb->len) { skb_queue_head(&sk->sk_receive_queue, skb); break; } kfree_skb(skb); } else { /* put message back and return */ skb_queue_head(&sk->sk_receive_queue, skb); break; } } while (size); out: release_sock(sk); return copied ? : err; } EXPORT_SYMBOL(bt_sock_stream_recvmsg); static inline unsigned int bt_accept_poll(struct sock *parent) { struct list_head *p, *n; struct sock *sk; list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); if (sk->sk_state == BT_CONNECTED || (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags) && sk->sk_state == BT_CONNECT2)) return POLLIN | POLLRDNORM; } return 0; } unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; unsigned int mask = 0; BT_DBG("sock %p, sk %p", sock, sk); poll_wait(file, sk_sleep(sk), wait); if (sk->sk_state == BT_LISTEN) return bt_accept_poll(sk); if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) mask |= POLLERR; if (sk->sk_shutdown & RCV_SHUTDOWN) mask |= POLLRDHUP | POLLIN | POLLRDNORM; if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= POLLIN | POLLRDNORM; if (sk->sk_state == BT_CLOSED) mask |= POLLHUP; if (sk->sk_state == BT_CONNECT || sk->sk_state == BT_CONNECT2 || sk->sk_state == BT_CONFIG) return mask; if (!test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags) && sock_writeable(sk)) mask |= POLLOUT | POLLWRNORM | POLLWRBAND; else set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); return mask; } EXPORT_SYMBOL(bt_sock_poll); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; struct sk_buff *skb; long amount; int err; BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); switch (cmd) { case TIOCOUTQ: if (sk->sk_state == BT_LISTEN) return -EINVAL; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); #else amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); #endif if (amount < 0) amount = 0; err = put_user(amount, (int __user *) arg); break; case TIOCINQ: if (sk->sk_state == BT_LISTEN) return -EINVAL; lock_sock(sk); skb = skb_peek(&sk->sk_receive_queue); amount = skb ? skb->len : 0; release_sock(sk); err = put_user(amount, (int __user *) arg); break; case SIOCGSTAMP: err = sock_get_timestamp(sk, (struct timeval __user *) arg); break; case SIOCGSTAMPNS: err = sock_get_timestampns(sk, (struct timespec __user *) arg); break; default: err = -ENOIOCTLCMD; break; } return err; } EXPORT_SYMBOL(bt_sock_ioctl); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) { DECLARE_WAITQUEUE(wait, current); int err = 0; BT_DBG("sk %p", sk); add_wait_queue(sk_sleep(sk), &wait); set_current_state(TASK_INTERRUPTIBLE); while (sk->sk_state != state) { if (!timeo) { err = -EINPROGRESS; break; } if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); set_current_state(TASK_INTERRUPTIBLE); err = sock_error(sk); if (err) break; } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); return err; } EXPORT_SYMBOL(bt_sock_wait_state); #ifdef CONFIG_PROC_FS struct bt_seq_state { struct bt_sock_list *l; }; static void *bt_seq_start(struct seq_file *seq, loff_t *pos) __acquires(seq->private->l->lock) { struct bt_seq_state *s = seq->private; struct bt_sock_list *l = s->l; read_lock(&l->lock); return seq_hlist_start_head(&l->head, *pos); } static void *bt_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct bt_seq_state *s = seq->private; struct bt_sock_list *l = s->l; return seq_hlist_next(v, &l->head, pos); } static void bt_seq_stop(struct seq_file *seq, void *v) __releases(seq->private->l->lock) { struct bt_seq_state *s = seq->private; struct bt_sock_list *l = s->l; read_unlock(&l->lock); } static int bt_seq_show(struct seq_file *seq, void *v) { struct bt_seq_state *s = seq->private; struct bt_sock_list *l = s->l; bdaddr_t src_baswapped, dst_baswapped; if (v == SEQ_START_TOKEN) { seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent"); if (l->custom_seq_show) { seq_putc(seq, ' '); l->custom_seq_show(seq, v); } seq_putc(seq, '\n'); } else { struct sock *sk = sk_entry(v); struct bt_sock *bt = bt_sk(sk); baswap(&src_baswapped, &bt->src); baswap(&dst_baswapped, &bt->dst); seq_printf(seq, "%pK %-6d %-6u %-6u %-6u %-6lu %pM %pM %-6lu", sk, atomic_read(&sk->sk_refcnt), sk_rmem_alloc_get(sk), sk_wmem_alloc_get(sk), sock_i_uid(sk), sock_i_ino(sk), &src_baswapped, &dst_baswapped, bt->parent? sock_i_ino(bt->parent): 0LU); if (l->custom_seq_show) { seq_putc(seq, ' '); l->custom_seq_show(seq, v); } seq_putc(seq, '\n'); } return 0; } static struct seq_operations bt_seq_ops = { .start = bt_seq_start, .next = bt_seq_next, .stop = bt_seq_stop, .show = bt_seq_show, }; static int bt_seq_open(struct inode *inode, struct file *file) { struct bt_sock_list *sk_list; struct bt_seq_state *s; sk_list = PDE(inode)->data; s = __seq_open_private(file, &bt_seq_ops, sizeof(struct bt_seq_state)); if (!s) return -ENOMEM; s->l = sk_list; return 0; } int bt_procfs_init(struct module* module, struct net *net, const char *name, struct bt_sock_list* sk_list, int (* seq_show)(struct seq_file *, void *)) { struct proc_dir_entry * pde; sk_list->custom_seq_show = seq_show; sk_list->fops.owner = module; sk_list->fops.open = bt_seq_open; sk_list->fops.read = seq_read; sk_list->fops.llseek = seq_lseek; sk_list->fops.release = seq_release_private; pde = proc_net_fops_create(net, name, 0, &sk_list->fops); if (!pde) return -ENOMEM; pde->data = sk_list; return 0; } void bt_procfs_cleanup(struct net *net, const char *name) { proc_net_remove(net, name); } #else int bt_procfs_init(struct module* module, struct net *net, const char *name, struct bt_sock_list* sk_list, int (* seq_show)(struct seq_file *, void *)) { return 0; } void bt_procfs_cleanup(struct net *net, const char *name) { } #endif EXPORT_SYMBOL(bt_procfs_init); EXPORT_SYMBOL(bt_procfs_cleanup); static struct net_proto_family bt_sock_family_ops = { .owner = THIS_MODULE, .family = PF_BLUETOOTH, .create = bt_sock_create, }; static int __init bt_init(void) { int err; BT_INFO("Core ver %s", VERSION); err = bt_sysfs_init(); if (err < 0) return err; err = sock_register(&bt_sock_family_ops); if (err < 0) { bt_sysfs_cleanup(); return err; } BT_INFO("HCI device and connection manager initialized"); err = hci_sock_init(); if (err < 0) goto error; err = l2cap_init(); if (err < 0) goto sock_err; err = sco_init(); if (err < 0) { l2cap_exit(); goto sock_err; } return 0; sock_err: hci_sock_cleanup(); error: sock_unregister(PF_BLUETOOTH); bt_sysfs_cleanup(); return err; } static void __exit bt_exit(void) { sco_exit(); l2cap_exit(); hci_sock_cleanup(); sock_unregister(PF_BLUETOOTH); bt_sysfs_cleanup(); } subsys_initcall(bt_init); module_exit(bt_exit); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth Core ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS_NETPROTO(PF_BLUETOOTH); compat-drivers-2012-09-18/net/bluetooth/Makefile0000644000175000017500000000056212026211315020616 0ustar mcgrofmcgrof# # Makefile for the Linux Bluetooth subsystem. # obj-$(CONFIG_BT) += bluetooth.o obj-$(CONFIG_BT_RFCOMM) += rfcomm/ obj-$(CONFIG_BT_BNEP) += bnep/ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_COMPAT_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ a2mp.o compat-drivers-2012-09-18/net/bluetooth/hci_event.c0000644000175000017500000025253112026211315021273 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth HCI event handling. */ #include #include #include #include #include /* Handle HCI Event packets */ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); if (status) { hci_dev_lock(hdev); mgmt_stop_discovery_failed(hdev, status); hci_dev_unlock(hdev); return; } clear_bit(HCI_INQUIRY, &hdev->flags); hci_dev_lock(hdev); hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); hci_conn_check_pending(hdev); } static void hci_cc_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); if (status) return; set_bit(HCI_PERIODIC_INQ, &hdev->dev_flags); } static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); if (status) return; clear_bit(HCI_PERIODIC_INQ, &hdev->dev_flags); hci_conn_check_pending(hdev); } static void hci_cc_remote_name_req_cancel(struct hci_dev *hdev, struct sk_buff *skb) { BT_DBG("%s", hdev->name); } static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_role_discovery *rp = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); if (conn) { if (rp->role) conn->link_mode &= ~HCI_LM_MASTER; else conn->link_mode |= HCI_LM_MASTER; } hci_dev_unlock(hdev); } static void hci_cc_read_link_policy(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_link_policy *rp = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); if (conn) conn->link_policy = __le16_to_cpu(rp->policy); hci_dev_unlock(hdev); } static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_write_link_policy *rp = (void *) skb->data; struct hci_conn *conn; void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LINK_POLICY); if (!sent) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); if (conn) conn->link_policy = get_unaligned_le16(sent + 2); hci_dev_unlock(hdev); } static void hci_cc_read_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_def_link_policy *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hdev->link_policy = __le16_to_cpu(rp->policy); } static void hci_cc_write_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY); if (!sent) return; if (!status) hdev->link_policy = get_unaligned_le16(sent); hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status); } static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); clear_bit(HCI_RESET, &hdev->flags); hci_req_complete(hdev, HCI_OP_RESET, status); /* Reset all non-persistent flags */ hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS) | BIT(HCI_PERIODIC_INQ)); hdev->discovery.state = DISCOVERY_STOPPED; } static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME); if (!sent) return; hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_set_local_name_complete(hdev, sent, status); else if (!status) memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status); } static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_name *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; if (test_bit(HCI_SETUP, &hdev->dev_flags)) memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH); } static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_ENABLE); if (!sent) return; if (!status) { __u8 param = *((__u8 *) sent); if (param == AUTH_ENABLED) set_bit(HCI_AUTH, &hdev->flags); else clear_bit(HCI_AUTH, &hdev->flags); } if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_auth_enable_complete(hdev, status); hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status); } static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_ENCRYPT_MODE); if (!sent) return; if (!status) { __u8 param = *((__u8 *) sent); if (param) set_bit(HCI_ENCRYPT, &hdev->flags); else clear_bit(HCI_ENCRYPT, &hdev->flags); } hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status); } static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { __u8 param, status = *((__u8 *) skb->data); int old_pscan, old_iscan; void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE); if (!sent) return; param = *((__u8 *) sent); hci_dev_lock(hdev); if (status) { mgmt_write_scan_failed(hdev, param, status); hdev->discov_timeout = 0; goto done; } old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); if (param & SCAN_INQUIRY) { set_bit(HCI_ISCAN, &hdev->flags); if (!old_iscan) mgmt_discoverable(hdev, 1); if (hdev->discov_timeout > 0) { int to = msecs_to_jiffies(hdev->discov_timeout * 1000); queue_delayed_work(hdev->workqueue, &hdev->discov_off, to); } } else if (old_iscan) mgmt_discoverable(hdev, 0); if (param & SCAN_PAGE) { set_bit(HCI_PSCAN, &hdev->flags); if (!old_pscan) mgmt_connectable(hdev, 1); } else if (old_pscan) mgmt_connectable(hdev, 0); done: hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); } static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_class_of_dev *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; memcpy(hdev->dev_class, rp->dev_class, 3); BT_DBG("%s class 0x%.2x%.2x%.2x", hdev->name, hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); } static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV); if (!sent) return; hci_dev_lock(hdev); if (status == 0) memcpy(hdev->dev_class, sent, 3); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_set_class_of_dev_complete(hdev, sent, status); hci_dev_unlock(hdev); } static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_voice_setting *rp = (void *) skb->data; __u16 setting; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; setting = __le16_to_cpu(rp->voice_setting); if (hdev->voice_setting == setting) return; hdev->voice_setting = setting; BT_DBG("%s voice setting 0x%4.4x", hdev->name, setting); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); } static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); __u16 setting; void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (status) return; sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_VOICE_SETTING); if (!sent) return; setting = get_unaligned_le16(sent); if (hdev->voice_setting == setting) return; hdev->voice_setting = setting; BT_DBG("%s voice setting 0x%4.4x", hdev->name, setting); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); } static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status); } static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE); if (!sent) return; if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status); else if (!status) { if (*((u8 *) sent)) set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); else clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } } static u8 hci_get_inquiry_mode(struct hci_dev *hdev) { if (hdev->features[6] & LMP_EXT_INQ) return 2; if (hdev->features[3] & LMP_RSSI_INQ) return 1; if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x0757) return 1; if (hdev->manufacturer == 15) { if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) return 1; if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) return 1; if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) return 1; } if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && hdev->lmp_subver == 0x1805) return 1; return 0; } static void hci_setup_inquiry_mode(struct hci_dev *hdev) { u8 mode; mode = hci_get_inquiry_mode(hdev); hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); } static void hci_setup_event_mask(struct hci_dev *hdev) { /* The second byte is 0xff instead of 0x9f (two reserved bits * disabled) since a Broadcom 1.2 dongle doesn't respond to the * command otherwise */ u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; /* CSR 1.1 dongles does not accept any bitfield so don't try to set * any event mask for pre 1.2 devices */ if (hdev->hci_ver < BLUETOOTH_VER_1_2) return; events[4] |= 0x01; /* Flow Specification Complete */ events[4] |= 0x02; /* Inquiry Result with RSSI */ events[4] |= 0x04; /* Read Remote Extended Features Complete */ events[5] |= 0x08; /* Synchronous Connection Complete */ events[5] |= 0x10; /* Synchronous Connection Changed */ if (hdev->features[3] & LMP_RSSI_INQ) events[4] |= 0x02; /* Inquiry Result with RSSI */ if (lmp_sniffsubr_capable(hdev)) events[5] |= 0x20; /* Sniff Subrating */ if (hdev->features[5] & LMP_PAUSE_ENC) events[5] |= 0x80; /* Encryption Key Refresh Complete */ if (hdev->features[6] & LMP_EXT_INQ) events[5] |= 0x40; /* Extended Inquiry Result */ if (lmp_no_flush_capable(hdev)) events[7] |= 0x01; /* Enhanced Flush Complete */ if (hdev->features[7] & LMP_LSTO) events[6] |= 0x80; /* Link Supervision Timeout Changed */ if (lmp_ssp_capable(hdev)) { events[6] |= 0x01; /* IO Capability Request */ events[6] |= 0x02; /* IO Capability Response */ events[6] |= 0x04; /* User Confirmation Request */ events[6] |= 0x08; /* User Passkey Request */ events[6] |= 0x10; /* Remote OOB Data Request */ events[6] |= 0x20; /* Simple Pairing Complete */ events[7] |= 0x04; /* User Passkey Notification */ events[7] |= 0x08; /* Keypress Notification */ events[7] |= 0x10; /* Remote Host Supported * Features Notification */ } if (lmp_le_capable(hdev)) events[7] |= 0x20; /* LE Meta-Event */ hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); } static void hci_setup(struct hci_dev *hdev) { if (hdev->dev_type != HCI_BREDR) return; hci_setup_event_mask(hdev); if (hdev->hci_ver > BLUETOOTH_VER_1_1) hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); if (lmp_ssp_capable(hdev)) { if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 mode = 0x01; hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode); } else { struct hci_cp_write_eir cp; memset(hdev->eir, 0, sizeof(hdev->eir)); memset(&cp, 0, sizeof(cp)); hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } } if (hdev->features[3] & LMP_RSSI_INQ) hci_setup_inquiry_mode(hdev); if (hdev->features[7] & LMP_INQ_TX_PWR) hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); if (hdev->features[7] & LMP_EXTFEATURES) { struct hci_cp_read_local_ext_features cp; cp.page = 0x01; hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp); } if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { u8 enable = 1; hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), &enable); } } static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_version *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) goto done; hdev->hci_ver = rp->hci_ver; hdev->hci_rev = __le16_to_cpu(rp->hci_rev); hdev->lmp_ver = rp->lmp_ver; hdev->manufacturer = __le16_to_cpu(rp->manufacturer); hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver); BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name, hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); if (test_bit(HCI_INIT, &hdev->flags)) hci_setup(hdev); done: hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status); } static void hci_setup_link_policy(struct hci_dev *hdev) { struct hci_cp_write_def_link_policy cp; u16 link_policy = 0; if (lmp_rswitch_capable(hdev)) link_policy |= HCI_LP_RSWITCH; if (hdev->features[0] & LMP_HOLD) link_policy |= HCI_LP_HOLD; if (lmp_sniff_capable(hdev)) link_policy |= HCI_LP_SNIFF; if (hdev->features[1] & LMP_PARK) link_policy |= HCI_LP_PARK; cp.policy = cpu_to_le16(link_policy); hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp); } static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_commands *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) goto done; memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10)) hci_setup_link_policy(hdev); done: hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); } static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_features *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; memcpy(hdev->features, rp->features, 8); /* Adjust default settings according to features * supported by device. */ if (hdev->features[0] & LMP_3SLOT) hdev->pkt_type |= (HCI_DM3 | HCI_DH3); if (hdev->features[0] & LMP_5SLOT) hdev->pkt_type |= (HCI_DM5 | HCI_DH5); if (hdev->features[1] & LMP_HV2) { hdev->pkt_type |= (HCI_HV2); hdev->esco_type |= (ESCO_HV2); } if (hdev->features[1] & LMP_HV3) { hdev->pkt_type |= (HCI_HV3); hdev->esco_type |= (ESCO_HV3); } if (lmp_esco_capable(hdev)) hdev->esco_type |= (ESCO_EV3); if (hdev->features[4] & LMP_EV4) hdev->esco_type |= (ESCO_EV4); if (hdev->features[4] & LMP_EV5) hdev->esco_type |= (ESCO_EV5); if (hdev->features[5] & LMP_EDR_ESCO_2M) hdev->esco_type |= (ESCO_2EV3); if (hdev->features[5] & LMP_EDR_ESCO_3M) hdev->esco_type |= (ESCO_3EV3); if (hdev->features[5] & LMP_EDR_3S_ESCO) hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5); BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name, hdev->features[0], hdev->features[1], hdev->features[2], hdev->features[3], hdev->features[4], hdev->features[5], hdev->features[6], hdev->features[7]); } static void hci_set_le_support(struct hci_dev *hdev) { struct hci_cp_write_le_host_supported cp; memset(&cp, 0, sizeof(cp)); if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { cp.le = 1; cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); } if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE)) hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); } static void hci_cc_read_local_ext_features(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_ext_features *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) goto done; switch (rp->page) { case 0: memcpy(hdev->features, rp->features, 8); break; case 1: memcpy(hdev->host_features, rp->features, 8); break; } if (test_bit(HCI_INIT, &hdev->flags) && lmp_le_capable(hdev)) hci_set_le_support(hdev); done: hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); } static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_flow_control_mode *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hdev->flow_ctl_mode = rp->mode; hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status); } static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_buffer_size *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hdev->acl_mtu = __le16_to_cpu(rp->acl_mtu); hdev->sco_mtu = rp->sco_mtu; hdev->acl_pkts = __le16_to_cpu(rp->acl_max_pkt); hdev->sco_pkts = __le16_to_cpu(rp->sco_max_pkt); if (test_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks)) { hdev->sco_mtu = 64; hdev->sco_pkts = 8; } hdev->acl_cnt = hdev->acl_pkts; hdev->sco_cnt = hdev->sco_pkts; BT_DBG("%s acl mtu %d:%d sco mtu %d:%d", hdev->name, hdev->acl_mtu, hdev->acl_pkts, hdev->sco_mtu, hdev->sco_pkts); } static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_bd_addr *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (!rp->status) bacpy(&hdev->bdaddr, &rp->bdaddr); hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status); } static void hci_cc_read_data_block_size(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_data_block_size *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hdev->block_mtu = __le16_to_cpu(rp->max_acl_len); hdev->block_len = __le16_to_cpu(rp->block_len); hdev->num_blocks = __le16_to_cpu(rp->num_blocks); hdev->block_cnt = hdev->num_blocks; BT_DBG("%s blk mtu %d cnt %d len %d", hdev->name, hdev->block_mtu, hdev->block_cnt, hdev->block_len); hci_req_complete(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, rp->status); } static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); } static void hci_cc_read_local_amp_info(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_amp_info *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hdev->amp_status = rp->amp_status; hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); hdev->amp_max_bw = __le32_to_cpu(rp->max_bw); hdev->amp_min_latency = __le32_to_cpu(rp->min_latency); hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu); hdev->amp_type = rp->amp_type; hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap); hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size); hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to); hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); } static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); } static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status); } static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status); } static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_inq_rsp_tx_power *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (!rp->status) hdev->inq_tx_power = rp->tx_power; hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, rp->status); } static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); } static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_pin_code_reply *rp = (void *) skb->data; struct hci_cp_pin_code_reply *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); if (rp->status) goto unlock; cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); if (!cp) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (conn) conn->pin_length = cp->pin_len; unlock: hci_dev_unlock(hdev); } static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_pin_code_neg_reply *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, rp->status); hci_dev_unlock(hdev); } static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_le_read_buffer_size *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hdev->le_mtu = __le16_to_cpu(rp->le_mtu); hdev->le_pkts = rp->le_max_pkt; hdev->le_cnt = hdev->le_pkts; BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts); hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_user_confirm_reply *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); } static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_user_confirm_reply *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); } static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_user_confirm_reply *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); } static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_user_confirm_reply *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); } static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_oob_data *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); mgmt_read_local_oob_data_reply_complete(hdev, rp->hash, rp->randomizer, rp->status); hci_dev_unlock(hdev); } static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status); if (status) { hci_dev_lock(hdev); mgmt_start_discovery_failed(hdev, status); hci_dev_unlock(hdev); return; } } static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_cp_le_set_scan_enable *cp; __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE); if (!cp) return; switch (cp->enable) { case LE_SCANNING_ENABLED: hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status); if (status) { hci_dev_lock(hdev); mgmt_start_discovery_failed(hdev, status); hci_dev_unlock(hdev); return; } set_bit(HCI_LE_SCAN, &hdev->dev_flags); hci_dev_lock(hdev); hci_discovery_set_state(hdev, DISCOVERY_FINDING); hci_dev_unlock(hdev); break; case LE_SCANNING_DISABLED: if (status) { hci_dev_lock(hdev); mgmt_stop_discovery_failed(hdev, status); hci_dev_unlock(hdev); return; } clear_bit(HCI_LE_SCAN, &hdev->dev_flags); if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && hdev->discovery.state == DISCOVERY_FINDING) { mgmt_interleaved_discovery(hdev); } else { hci_dev_lock(hdev); hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); } break; default: BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable); break; } } static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_le_ltk_reply *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status); } static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) return; hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); } static void hci_cc_write_le_host_supported(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_cp_write_le_host_supported *sent; __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED); if (!sent) return; if (!status) { if (sent->le) hdev->host_features[0] |= LMP_HOST_LE; else hdev->host_features[0] &= ~LMP_HOST_LE; } if (test_bit(HCI_MGMT, &hdev->dev_flags) && !test_bit(HCI_INIT, &hdev->flags)) mgmt_le_enable_complete(hdev, sent->le, status); hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); } static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); if (status) { hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_start_discovery_failed(hdev, status); hci_dev_unlock(hdev); return; } set_bit(HCI_INQUIRY, &hdev->flags); hci_dev_lock(hdev); hci_discovery_set_state(hdev, DISCOVERY_FINDING); hci_dev_unlock(hdev); } static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) { struct hci_cp_create_conn *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_CONN); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); BT_DBG("%s bdaddr %s hcon %p", hdev->name, batostr(&cp->bdaddr), conn); if (status) { if (conn && conn->state == BT_CONNECT) { if (status != 0x0c || conn->attempt > 2) { conn->state = BT_CLOSED; hci_proto_connect_cfm(conn, status); hci_conn_del(conn); } else conn->state = BT_CONNECT2; } } else { if (!conn) { conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); if (conn) { conn->out = true; conn->link_mode |= HCI_LM_MASTER; } else BT_ERR("No memory for new connection"); } } hci_dev_unlock(hdev); } static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) { struct hci_cp_add_sco *cp; struct hci_conn *acl, *sco; __u16 handle; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_ADD_SCO); if (!cp) return; handle = __le16_to_cpu(cp->handle); BT_DBG("%s handle 0x%4.4x", hdev->name, handle); hci_dev_lock(hdev); acl = hci_conn_hash_lookup_handle(hdev, handle); if (acl) { sco = acl->link; if (sco) { sco->state = BT_CLOSED; hci_proto_connect_cfm(sco, status); hci_conn_del(sco); } } hci_dev_unlock(hdev); } static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status) { struct hci_cp_auth_requested *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_AUTH_REQUESTED); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { hci_proto_connect_cfm(conn, status); hci_conn_put(conn); } } hci_dev_unlock(hdev); } static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status) { struct hci_cp_set_conn_encrypt *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_SET_CONN_ENCRYPT); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { hci_proto_connect_cfm(conn, status); hci_conn_put(conn); } } hci_dev_unlock(hdev); } static int hci_outgoing_auth_needed(struct hci_dev *hdev, struct hci_conn *conn) { if (conn->state != BT_CONFIG || !conn->out) return 0; if (conn->pending_sec_level == BT_SECURITY_SDP) return 0; /* Only request authentication for SSP connections or non-SSP * devices with sec_level HIGH or if MITM protection is requested */ if (!hci_conn_ssp_enabled(conn) && !(conn->auth_type & 0x01) && conn->pending_sec_level != BT_SECURITY_HIGH) return 0; return 1; } static int hci_resolve_name(struct hci_dev *hdev, struct inquiry_entry *e) { struct hci_cp_remote_name_req cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, &e->data.bdaddr); cp.pscan_rep_mode = e->data.pscan_rep_mode; cp.pscan_mode = e->data.pscan_mode; cp.clock_offset = e->data.clock_offset; return hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } static bool hci_resolve_next_name(struct hci_dev *hdev) { struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; if (list_empty(&discov->resolve)) return false; e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); if (!e) return false; if (hci_resolve_name(hdev, e) == 0) { e->name_state = NAME_PENDING; return true; } return false; } static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, bdaddr_t *bdaddr, u8 *name, u8 name_len) { struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name, name_len, conn->dev_class); if (discov->state == DISCOVERY_STOPPED) return; if (discov->state == DISCOVERY_STOPPING) goto discov_complete; if (discov->state != DISCOVERY_RESOLVING) return; e = hci_inquiry_cache_lookup_resolve(hdev, bdaddr, NAME_PENDING); /* If the device was not found in a list of found devices names of which * are pending. there is no need to continue resolving a next name as it * will be done upon receiving another Remote Name Request Complete * Event */ if (!e) return; list_del(&e->list); if (name) { e->name_state = NAME_KNOWN; mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00, e->data.rssi, name, name_len); } else { e->name_state = NAME_NOT_KNOWN; } if (hci_resolve_next_name(hdev)) return; discov_complete: hci_discovery_set_state(hdev, DISCOVERY_STOPPED); } static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) { struct hci_cp_remote_name_req *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); /* If successful wait for the name req complete event before * checking for the need to do authentication */ if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_REMOTE_NAME_REQ); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (test_bit(HCI_MGMT, &hdev->dev_flags)) hci_check_pending_name(hdev, conn, &cp->bdaddr, NULL, 0); if (!conn) goto unlock; if (!hci_outgoing_auth_needed(hdev, conn)) goto unlock; if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested cp; cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } unlock: hci_dev_unlock(hdev); } static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status) { struct hci_cp_read_remote_features *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_FEATURES); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { hci_proto_connect_cfm(conn, status); hci_conn_put(conn); } } hci_dev_unlock(hdev); } static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status) { struct hci_cp_read_remote_ext_features *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { if (conn->state == BT_CONFIG) { hci_proto_connect_cfm(conn, status); hci_conn_put(conn); } } hci_dev_unlock(hdev); } static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) { struct hci_cp_setup_sync_conn *cp; struct hci_conn *acl, *sco; __u16 handle; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN); if (!cp) return; handle = __le16_to_cpu(cp->handle); BT_DBG("%s handle 0x%4.4x", hdev->name, handle); hci_dev_lock(hdev); acl = hci_conn_hash_lookup_handle(hdev, handle); if (acl) { sco = acl->link; if (sco) { sco->state = BT_CLOSED; hci_proto_connect_cfm(sco, status); hci_conn_del(sco); } } hci_dev_unlock(hdev); } static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) { struct hci_cp_sniff_mode *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_SNIFF_MODE); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) hci_sco_setup(conn, status); } hci_dev_unlock(hdev); } static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) { struct hci_cp_exit_sniff_mode *cp; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_EXIT_SNIFF_MODE); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) hci_sco_setup(conn, status); } hci_dev_unlock(hdev); } static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) { struct hci_cp_disconnect *cp; struct hci_conn *conn; if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONNECT); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) mgmt_disconnect_failed(hdev, &conn->dst, conn->type, conn->dst_type, status); hci_dev_unlock(hdev); } static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) { struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, status); if (status) { hci_dev_lock(hdev); conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (!conn) { hci_dev_unlock(hdev); return; } BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&conn->dst), conn); conn->state = BT_CLOSED; mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, status); hci_proto_connect_cfm(conn, status); hci_conn_del(conn); hci_dev_unlock(hdev); } } static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); } static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) return; if (!test_bit(HCI_MGMT, &hdev->dev_flags)) return; hci_dev_lock(hdev); if (discov->state != DISCOVERY_FINDING) goto unlock; if (list_empty(&discov->resolve)) { hci_discovery_set_state(hdev, DISCOVERY_STOPPED); goto unlock; } e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); if (e && hci_resolve_name(hdev, e) == 0) { e->name_state = NAME_PENDING; hci_discovery_set_state(hdev, DISCOVERY_RESOLVING); } else { hci_discovery_set_state(hdev, DISCOVERY_STOPPED); } unlock: hci_dev_unlock(hdev); } static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct inquiry_data data; struct inquiry_info *info = (void *) (skb->data + 1); int num_rsp = *((__u8 *) skb->data); BT_DBG("%s num_rsp %d", hdev->name, num_rsp); if (!num_rsp) return; if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) return; hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { bool name_known, ssp; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; data.pscan_mode = info->pscan_mode; memcpy(data.dev_class, info->dev_class, 3); data.clock_offset = info->clock_offset; data.rssi = 0x00; data.ssp_mode = 0x00; name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, 0, !name_known, ssp, NULL, 0); } hci_dev_unlock(hdev); } static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { if (ev->link_type != SCO_LINK) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); if (!conn) goto unlock; conn->type = SCO_LINK; } if (!ev->status) { conn->handle = __le16_to_cpu(ev->handle); if (conn->type == ACL_LINK) { conn->state = BT_CONFIG; hci_conn_hold(conn); if (!conn->out && !hci_conn_ssp_enabled(conn) && !hci_find_link_key(hdev, &ev->bdaddr)) conn->disc_timeout = HCI_PAIRING_TIMEOUT; else conn->disc_timeout = HCI_DISCONN_TIMEOUT; } else conn->state = BT_CONNECTED; hci_conn_hold_device(conn); hci_conn_add_sysfs(conn); if (test_bit(HCI_AUTH, &hdev->flags)) conn->link_mode |= HCI_LM_AUTH; if (test_bit(HCI_ENCRYPT, &hdev->flags)) conn->link_mode |= HCI_LM_ENCRYPT; /* Get remote features */ if (conn->type == ACL_LINK) { struct hci_cp_read_remote_features cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp); } /* Set packet type for incoming connection */ if (!conn->out && hdev->hci_ver < BLUETOOTH_VER_2_0) { struct hci_cp_change_conn_ptype cp; cp.handle = ev->handle; cp.pkt_type = cpu_to_le16(conn->pkt_type); hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), &cp); } } else { conn->state = BT_CLOSED; if (conn->type == ACL_LINK) mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, conn->dst_type, ev->status); } if (conn->type == ACL_LINK) hci_sco_setup(conn, ev->status); if (ev->status) { hci_proto_connect_cfm(conn, ev->status); hci_conn_del(conn); } else if (ev->link_type != ACL_LINK) hci_proto_connect_cfm(conn, ev->status); unlock: hci_dev_unlock(hdev); hci_conn_check_pending(hdev); } static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; BT_DBG("%s bdaddr %s type 0x%x", hdev->name, batostr(&ev->bdaddr), ev->link_type); mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); if ((mask & HCI_LM_ACCEPT) && !hci_blacklist_lookup(hdev, &ev->bdaddr)) { /* Connection accepted */ struct inquiry_entry *ie; struct hci_conn *conn; hci_dev_lock(hdev); ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); if (ie) memcpy(ie->data.dev_class, ev->dev_class, 3); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); if (!conn) { BT_ERR("No memory for new connection"); hci_dev_unlock(hdev); return; } } memcpy(conn->dev_class, ev->dev_class, 3); conn->state = BT_CONNECT; hci_dev_unlock(hdev); if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) { struct hci_cp_accept_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) cp.role = 0x00; /* Become master */ else cp.role = 0x01; /* Remain slave */ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); } else { struct hci_cp_accept_sync_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); cp.pkt_type = cpu_to_le16(conn->pkt_type); cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40); cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40); cp.max_latency = __constant_cpu_to_le16(0xffff); cp.content_format = cpu_to_le16(hdev->voice_setting); cp.retrans_effort = 0xff; hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(cp), &cp); } } else { /* Connection rejected */ struct hci_cp_reject_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); cp.reason = HCI_ERROR_REJ_BAD_ADDR; hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); } } static u8 hci_to_mgmt_reason(u8 err) { switch (err) { case HCI_ERROR_CONNECTION_TIMEOUT: return MGMT_DEV_DISCONN_TIMEOUT; case HCI_ERROR_REMOTE_USER_TERM: case HCI_ERROR_REMOTE_LOW_RESOURCES: case HCI_ERROR_REMOTE_POWER_OFF: return MGMT_DEV_DISCONN_REMOTE; case HCI_ERROR_LOCAL_HOST_TERM: return MGMT_DEV_DISCONN_LOCAL_HOST; default: return MGMT_DEV_DISCONN_UNKNOWN; } } static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_disconn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (!conn) goto unlock; if (ev->status == 0) conn->state = BT_CLOSED; if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) && (conn->type == ACL_LINK || conn->type == LE_LINK)) { if (ev->status) { mgmt_disconnect_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); } else { u8 reason = hci_to_mgmt_reason(ev->reason); mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type, reason); } } if (ev->status == 0) { if (conn->type == ACL_LINK && conn->flush_key) hci_remove_link_key(hdev, &conn->dst); hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); } unlock: hci_dev_unlock(hdev); } static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_auth_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (!conn) goto unlock; if (!ev->status) { if (!hci_conn_ssp_enabled(conn) && test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { BT_INFO("re-auth of legacy device is not possible."); } else { conn->link_mode |= HCI_LM_AUTH; conn->sec_level = conn->pending_sec_level; } } else { mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); } clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags); if (conn->state == BT_CONFIG) { if (!ev->status && hci_conn_ssp_enabled(conn)) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } } else { hci_auth_cfm(conn, ev->status); hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; hci_conn_put(conn); } if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { if (!ev->status) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); hci_encrypt_cfm(conn, ev->status, 0x00); } } unlock: hci_dev_unlock(hdev); } static void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_remote_name *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_conn_check_pending(hdev); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto check_auth; if (ev->status == 0) hci_check_pending_name(hdev, conn, &ev->bdaddr, ev->name, strnlen(ev->name, HCI_MAX_NAME_LENGTH)); else hci_check_pending_name(hdev, conn, &ev->bdaddr, NULL, 0); check_auth: if (!conn) goto unlock; if (!hci_outgoing_auth_needed(hdev, conn)) goto unlock; if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested cp; cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } unlock: hci_dev_unlock(hdev); } static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_encrypt_change *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) { if (ev->encrypt) { /* Encryption implies authentication */ conn->link_mode |= HCI_LM_AUTH; conn->link_mode |= HCI_LM_ENCRYPT; conn->sec_level = conn->pending_sec_level; } else conn->link_mode &= ~HCI_LM_ENCRYPT; } clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); if (ev->status && conn->state == BT_CONNECTED) { hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE); hci_conn_put(conn); goto unlock; } if (conn->state == BT_CONFIG) { if (!ev->status) conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } else hci_encrypt_cfm(conn, ev->status, ev->encrypt); } unlock: hci_dev_unlock(hdev); } static void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_change_link_key_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) conn->link_mode |= HCI_LM_SECURE; clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); hci_key_change_cfm(conn, ev->status); } hci_dev_unlock(hdev); } static void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_remote_features *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (!conn) goto unlock; if (!ev->status) memcpy(conn->features, ev->features, 8); if (conn->state != BT_CONFIG) goto unlock; if (!ev->status && lmp_ssp_capable(hdev) && lmp_ssp_capable(conn)) { struct hci_cp_read_remote_ext_features cp; cp.handle = ev->handle; cp.page = 0x01; hci_send_cmd(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES, sizeof(cp), &cp); goto unlock; } if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) { struct hci_cp_remote_name_req cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &conn->dst, conn->type, conn->dst_type, 0, NULL, 0, conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } unlock: hci_dev_unlock(hdev); } static void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb) { BT_DBG("%s", hdev->name); } static void hci_qos_setup_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { BT_DBG("%s", hdev->name); } static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_cmd_complete *ev = (void *) skb->data; __u16 opcode; skb_pull(skb, sizeof(*ev)); opcode = __le16_to_cpu(ev->opcode); switch (opcode) { case HCI_OP_INQUIRY_CANCEL: hci_cc_inquiry_cancel(hdev, skb); break; case HCI_OP_PERIODIC_INQ: hci_cc_periodic_inq(hdev, skb); break; case HCI_OP_EXIT_PERIODIC_INQ: hci_cc_exit_periodic_inq(hdev, skb); break; case HCI_OP_REMOTE_NAME_REQ_CANCEL: hci_cc_remote_name_req_cancel(hdev, skb); break; case HCI_OP_ROLE_DISCOVERY: hci_cc_role_discovery(hdev, skb); break; case HCI_OP_READ_LINK_POLICY: hci_cc_read_link_policy(hdev, skb); break; case HCI_OP_WRITE_LINK_POLICY: hci_cc_write_link_policy(hdev, skb); break; case HCI_OP_READ_DEF_LINK_POLICY: hci_cc_read_def_link_policy(hdev, skb); break; case HCI_OP_WRITE_DEF_LINK_POLICY: hci_cc_write_def_link_policy(hdev, skb); break; case HCI_OP_RESET: hci_cc_reset(hdev, skb); break; case HCI_OP_WRITE_LOCAL_NAME: hci_cc_write_local_name(hdev, skb); break; case HCI_OP_READ_LOCAL_NAME: hci_cc_read_local_name(hdev, skb); break; case HCI_OP_WRITE_AUTH_ENABLE: hci_cc_write_auth_enable(hdev, skb); break; case HCI_OP_WRITE_ENCRYPT_MODE: hci_cc_write_encrypt_mode(hdev, skb); break; case HCI_OP_WRITE_SCAN_ENABLE: hci_cc_write_scan_enable(hdev, skb); break; case HCI_OP_READ_CLASS_OF_DEV: hci_cc_read_class_of_dev(hdev, skb); break; case HCI_OP_WRITE_CLASS_OF_DEV: hci_cc_write_class_of_dev(hdev, skb); break; case HCI_OP_READ_VOICE_SETTING: hci_cc_read_voice_setting(hdev, skb); break; case HCI_OP_WRITE_VOICE_SETTING: hci_cc_write_voice_setting(hdev, skb); break; case HCI_OP_HOST_BUFFER_SIZE: hci_cc_host_buffer_size(hdev, skb); break; case HCI_OP_WRITE_SSP_MODE: hci_cc_write_ssp_mode(hdev, skb); break; case HCI_OP_READ_LOCAL_VERSION: hci_cc_read_local_version(hdev, skb); break; case HCI_OP_READ_LOCAL_COMMANDS: hci_cc_read_local_commands(hdev, skb); break; case HCI_OP_READ_LOCAL_FEATURES: hci_cc_read_local_features(hdev, skb); break; case HCI_OP_READ_LOCAL_EXT_FEATURES: hci_cc_read_local_ext_features(hdev, skb); break; case HCI_OP_READ_BUFFER_SIZE: hci_cc_read_buffer_size(hdev, skb); break; case HCI_OP_READ_BD_ADDR: hci_cc_read_bd_addr(hdev, skb); break; case HCI_OP_READ_DATA_BLOCK_SIZE: hci_cc_read_data_block_size(hdev, skb); break; case HCI_OP_WRITE_CA_TIMEOUT: hci_cc_write_ca_timeout(hdev, skb); break; case HCI_OP_READ_FLOW_CONTROL_MODE: hci_cc_read_flow_control_mode(hdev, skb); break; case HCI_OP_READ_LOCAL_AMP_INFO: hci_cc_read_local_amp_info(hdev, skb); break; case HCI_OP_DELETE_STORED_LINK_KEY: hci_cc_delete_stored_link_key(hdev, skb); break; case HCI_OP_SET_EVENT_MASK: hci_cc_set_event_mask(hdev, skb); break; case HCI_OP_WRITE_INQUIRY_MODE: hci_cc_write_inquiry_mode(hdev, skb); break; case HCI_OP_READ_INQ_RSP_TX_POWER: hci_cc_read_inq_rsp_tx_power(hdev, skb); break; case HCI_OP_SET_EVENT_FLT: hci_cc_set_event_flt(hdev, skb); break; case HCI_OP_PIN_CODE_REPLY: hci_cc_pin_code_reply(hdev, skb); break; case HCI_OP_PIN_CODE_NEG_REPLY: hci_cc_pin_code_neg_reply(hdev, skb); break; case HCI_OP_READ_LOCAL_OOB_DATA: hci_cc_read_local_oob_data_reply(hdev, skb); break; case HCI_OP_LE_READ_BUFFER_SIZE: hci_cc_le_read_buffer_size(hdev, skb); break; case HCI_OP_USER_CONFIRM_REPLY: hci_cc_user_confirm_reply(hdev, skb); break; case HCI_OP_USER_CONFIRM_NEG_REPLY: hci_cc_user_confirm_neg_reply(hdev, skb); break; case HCI_OP_USER_PASSKEY_REPLY: hci_cc_user_passkey_reply(hdev, skb); break; case HCI_OP_USER_PASSKEY_NEG_REPLY: hci_cc_user_passkey_neg_reply(hdev, skb); break; case HCI_OP_LE_SET_SCAN_PARAM: hci_cc_le_set_scan_param(hdev, skb); break; case HCI_OP_LE_SET_SCAN_ENABLE: hci_cc_le_set_scan_enable(hdev, skb); break; case HCI_OP_LE_LTK_REPLY: hci_cc_le_ltk_reply(hdev, skb); break; case HCI_OP_LE_LTK_NEG_REPLY: hci_cc_le_ltk_neg_reply(hdev, skb); break; case HCI_OP_WRITE_LE_HOST_SUPPORTED: hci_cc_write_le_host_supported(hdev, skb); break; default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; } if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); if (ev->ncmd) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) queue_work(hdev->workqueue, &hdev->cmd_work); } } static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_cmd_status *ev = (void *) skb->data; __u16 opcode; skb_pull(skb, sizeof(*ev)); opcode = __le16_to_cpu(ev->opcode); switch (opcode) { case HCI_OP_INQUIRY: hci_cs_inquiry(hdev, ev->status); break; case HCI_OP_CREATE_CONN: hci_cs_create_conn(hdev, ev->status); break; case HCI_OP_ADD_SCO: hci_cs_add_sco(hdev, ev->status); break; case HCI_OP_AUTH_REQUESTED: hci_cs_auth_requested(hdev, ev->status); break; case HCI_OP_SET_CONN_ENCRYPT: hci_cs_set_conn_encrypt(hdev, ev->status); break; case HCI_OP_REMOTE_NAME_REQ: hci_cs_remote_name_req(hdev, ev->status); break; case HCI_OP_READ_REMOTE_FEATURES: hci_cs_read_remote_features(hdev, ev->status); break; case HCI_OP_READ_REMOTE_EXT_FEATURES: hci_cs_read_remote_ext_features(hdev, ev->status); break; case HCI_OP_SETUP_SYNC_CONN: hci_cs_setup_sync_conn(hdev, ev->status); break; case HCI_OP_SNIFF_MODE: hci_cs_sniff_mode(hdev, ev->status); break; case HCI_OP_EXIT_SNIFF_MODE: hci_cs_exit_sniff_mode(hdev, ev->status); break; case HCI_OP_DISCONNECT: hci_cs_disconnect(hdev, ev->status); break; case HCI_OP_LE_CREATE_CONN: hci_cs_le_create_conn(hdev, ev->status); break; case HCI_OP_LE_START_ENC: hci_cs_le_start_enc(hdev, ev->status); break; default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; } if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) queue_work(hdev->workqueue, &hdev->cmd_work); } } static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_role_change *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) { if (!ev->status) { if (ev->role) conn->link_mode &= ~HCI_LM_MASTER; else conn->link_mode |= HCI_LM_MASTER; } clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); hci_role_switch_cfm(conn, ev->status, ev->role); } hci_dev_unlock(hdev); } static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_num_comp_pkts *ev = (void *) skb->data; int i; if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_PACKET_BASED) { BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); return; } if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) + ev->num_hndl * sizeof(struct hci_comp_pkts_info)) { BT_DBG("%s bad parameters", hdev->name); return; } BT_DBG("%s num_hndl %d", hdev->name, ev->num_hndl); for (i = 0; i < ev->num_hndl; i++) { struct hci_comp_pkts_info *info = &ev->handles[i]; struct hci_conn *conn; __u16 handle, count; handle = __le16_to_cpu(info->handle); count = __le16_to_cpu(info->count); conn = hci_conn_hash_lookup_handle(hdev, handle); if (!conn) continue; conn->sent -= count; switch (conn->type) { case ACL_LINK: hdev->acl_cnt += count; if (hdev->acl_cnt > hdev->acl_pkts) hdev->acl_cnt = hdev->acl_pkts; break; case LE_LINK: if (hdev->le_pkts) { hdev->le_cnt += count; if (hdev->le_cnt > hdev->le_pkts) hdev->le_cnt = hdev->le_pkts; } else { hdev->acl_cnt += count; if (hdev->acl_cnt > hdev->acl_pkts) hdev->acl_cnt = hdev->acl_pkts; } break; case SCO_LINK: hdev->sco_cnt += count; if (hdev->sco_cnt > hdev->sco_pkts) hdev->sco_cnt = hdev->sco_pkts; break; default: BT_ERR("Unknown type %d conn %p", conn->type, conn); break; } } queue_work(hdev->workqueue, &hdev->tx_work); } static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_num_comp_blocks *ev = (void *) skb->data; int i; if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_BLOCK_BASED) { BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); return; } if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) + ev->num_hndl * sizeof(struct hci_comp_blocks_info)) { BT_DBG("%s bad parameters", hdev->name); return; } BT_DBG("%s num_blocks %d num_hndl %d", hdev->name, ev->num_blocks, ev->num_hndl); for (i = 0; i < ev->num_hndl; i++) { struct hci_comp_blocks_info *info = &ev->handles[i]; struct hci_conn *conn; __u16 handle, block_count; handle = __le16_to_cpu(info->handle); block_count = __le16_to_cpu(info->blocks); conn = hci_conn_hash_lookup_handle(hdev, handle); if (!conn) continue; conn->sent -= block_count; switch (conn->type) { case ACL_LINK: hdev->block_cnt += block_count; if (hdev->block_cnt > hdev->num_blocks) hdev->block_cnt = hdev->num_blocks; break; default: BT_ERR("Unknown type %d conn %p", conn->type, conn); break; } } queue_work(hdev->workqueue, &hdev->tx_work); } static void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_mode_change *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { conn->mode = ev->mode; conn->interval = __le16_to_cpu(ev->interval); if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { if (conn->mode == HCI_CM_ACTIVE) set_bit(HCI_CONN_POWER_SAVE, &conn->flags); else clear_bit(HCI_CONN_POWER_SAVE, &conn->flags); } if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) hci_sco_setup(conn, ev->status); } hci_dev_unlock(hdev); } static void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_pin_code_req *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) goto unlock; if (conn->state == BT_CONNECTED) { hci_conn_hold(conn); conn->disc_timeout = HCI_PAIRING_TIMEOUT; hci_conn_put(conn); } if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags)) hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); else if (test_bit(HCI_MGMT, &hdev->dev_flags)) { u8 secure; if (conn->pending_sec_level == BT_SECURITY_HIGH) secure = 1; else secure = 0; mgmt_pin_code_request(hdev, &ev->bdaddr, secure); } unlock: hci_dev_unlock(hdev); } static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_link_key_req *ev = (void *) skb->data; struct hci_cp_link_key_reply cp; struct hci_conn *conn; struct link_key *key; BT_DBG("%s", hdev->name); if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) return; hci_dev_lock(hdev); key = hci_find_link_key(hdev, &ev->bdaddr); if (!key) { BT_DBG("%s link key not found for %s", hdev->name, batostr(&ev->bdaddr)); goto not_found; } BT_DBG("%s found key type %u for %s", hdev->name, key->type, batostr(&ev->bdaddr)); if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && key->type == HCI_LK_DEBUG_COMBINATION) { BT_DBG("%s ignoring debug key", hdev->name); goto not_found; } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) { if (key->type == HCI_LK_UNAUTH_COMBINATION && conn->auth_type != 0xff && (conn->auth_type & 0x01)) { BT_DBG("%s ignoring unauthenticated key", hdev->name); goto not_found; } if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 && conn->pending_sec_level == BT_SECURITY_HIGH) { BT_DBG("%s ignoring key unauthenticated for high security", hdev->name); goto not_found; } conn->key_type = key->type; conn->pin_length = key->pin_len; } bacpy(&cp.bdaddr, &ev->bdaddr); memcpy(cp.link_key, key->val, HCI_LINK_KEY_SIZE); hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp); hci_dev_unlock(hdev); return; not_found: hci_send_cmd(hdev, HCI_OP_LINK_KEY_NEG_REPLY, 6, &ev->bdaddr); hci_dev_unlock(hdev); } static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_link_key_notify *ev = (void *) skb->data; struct hci_conn *conn; u8 pin_len = 0; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) { hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; pin_len = conn->pin_length; if (ev->key_type != HCI_LK_CHANGED_COMBINATION) conn->key_type = ev->key_type; hci_conn_put(conn); } if (test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, ev->key_type, pin_len); hci_dev_unlock(hdev); } static void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_clock_offset *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn && !ev->status) { struct inquiry_entry *ie; ie = hci_inquiry_cache_lookup(hdev, &conn->dst); if (ie) { ie->data.clock_offset = ev->clock_offset; ie->timestamp = jiffies; } } hci_dev_unlock(hdev); } static void hci_pkt_type_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_pkt_type_change *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn && !ev->status) conn->pkt_type = __le16_to_cpu(ev->pkt_type); hci_dev_unlock(hdev); } static void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_pscan_rep_mode *ev = (void *) skb->data; struct inquiry_entry *ie; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); if (ie) { ie->data.pscan_rep_mode = ev->pscan_rep_mode; ie->timestamp = jiffies; } hci_dev_unlock(hdev); } static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct inquiry_data data; int num_rsp = *((__u8 *) skb->data); bool name_known, ssp; BT_DBG("%s num_rsp %d", hdev->name, num_rsp); if (!num_rsp) return; if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) return; hci_dev_lock(hdev); if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { struct inquiry_info_with_rssi_and_pscan_mode *info; info = (void *) (skb->data + 1); for (; num_rsp; num_rsp--, info++) { bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; data.pscan_mode = info->pscan_mode; memcpy(data.dev_class, info->dev_class, 3); data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, !name_known, ssp, NULL, 0); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); for (; num_rsp; num_rsp--, info++) { bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; data.pscan_mode = 0x00; memcpy(data.dev_class, info->dev_class, 3); data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, !name_known, ssp, NULL, 0); } } hci_dev_unlock(hdev); } static void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_remote_ext_features *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (!conn) goto unlock; if (!ev->status && ev->page == 0x01) { struct inquiry_entry *ie; ie = hci_inquiry_cache_lookup(hdev, &conn->dst); if (ie) ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP); if (ev->features[0] & LMP_HOST_SSP) set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } if (conn->state != BT_CONFIG) goto unlock; if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) { struct hci_cp_remote_name_req cp; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &conn->dst, conn->type, conn->dst_type, 0, NULL, 0, conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } unlock: hci_dev_unlock(hdev); } static void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_sync_conn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { if (ev->link_type == ESCO_LINK) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); if (!conn) goto unlock; conn->type = SCO_LINK; } switch (ev->status) { case 0x00: conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; hci_conn_hold_device(conn); hci_conn_add_sysfs(conn); break; case 0x11: /* Unsupported Feature or Parameter Value */ case 0x1c: /* SCO interval rejected */ case 0x1a: /* Unsupported Remote Feature */ case 0x1f: /* Unspecified error */ if (conn->out && conn->attempt < 2) { conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | (hdev->esco_type & EDR_ESCO_MASK); hci_setup_sync(conn, conn->link->handle); goto unlock; } /* fall through */ default: conn->state = BT_CLOSED; break; } hci_proto_connect_cfm(conn, ev->status); if (ev->status) hci_conn_del(conn); unlock: hci_dev_unlock(hdev); } static void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb) { BT_DBG("%s", hdev->name); } static void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_sniff_subrate *ev = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); } static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct inquiry_data data; struct extended_inquiry_info *info = (void *) (skb->data + 1); int num_rsp = *((__u8 *) skb->data); size_t eir_len; BT_DBG("%s num_rsp %d", hdev->name, num_rsp); if (!num_rsp) return; if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) return; hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { bool name_known, ssp; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; data.pscan_mode = 0x00; memcpy(data.dev_class, info->dev_class, 3); data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x01; if (test_bit(HCI_MGMT, &hdev->dev_flags)) name_known = eir_has_data_type(info->data, sizeof(info->data), EIR_NAME_COMPLETE); else name_known = true; name_known = hci_inquiry_cache_update(hdev, &data, name_known, &ssp); eir_len = eir_get_length(info->data, sizeof(info->data)); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, !name_known, ssp, info->data, eir_len); } hci_dev_unlock(hdev); } static void hci_key_refresh_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_key_refresh_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x handle 0x%4.4x", hdev->name, ev->status, __le16_to_cpu(ev->handle)); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (!conn) goto unlock; if (!ev->status) conn->sec_level = conn->pending_sec_level; clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); if (ev->status && conn->state == BT_CONNECTED) { hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE); hci_conn_put(conn); goto unlock; } if (conn->state == BT_CONFIG) { if (!ev->status) conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } else { hci_auth_cfm(conn, ev->status); hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; hci_conn_put(conn); } unlock: hci_dev_unlock(hdev); } static u8 hci_get_auth_req(struct hci_conn *conn) { /* If remote requests dedicated bonding follow that lead */ if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) { /* If both remote and local IO capabilities allow MITM * protection then require it, otherwise don't */ if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) return 0x02; else return 0x03; } /* If remote requests no-bonding follow that lead */ if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01) return conn->remote_auth | (conn->auth_type & 0x01); return conn->auth_type; } static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_io_capa_request *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) goto unlock; hci_conn_hold(conn); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) || (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { struct hci_cp_io_capability_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); /* Change the IO capability from KeyboardDisplay * to DisplayYesNo as it is not supported by BT spec. */ cp.capability = (conn->io_capability == 0x04) ? 0x01 : conn->io_capability; conn->auth_type = hci_get_auth_req(conn); cp.authentication = conn->auth_type; if (hci_find_remote_oob_data(hdev, &conn->dst) && (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags))) cp.oob_data = 0x01; else cp.oob_data = 0x00; hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY, sizeof(cp), &cp); } else { struct hci_cp_io_capability_neg_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED; hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, sizeof(cp), &cp); } unlock: hci_dev_unlock(hdev); } static void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_io_capa_reply *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) goto unlock; conn->remote_cap = ev->capability; conn->remote_auth = ev->authentication; if (ev->oob_data) set_bit(HCI_CONN_REMOTE_OOB, &conn->flags); unlock: hci_dev_unlock(hdev); } static void hci_user_confirm_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_user_confirm_req *ev = (void *) skb->data; int loc_mitm, rem_mitm, confirm_hint = 0; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) goto unlock; loc_mitm = (conn->auth_type & 0x01); rem_mitm = (conn->remote_auth & 0x01); /* If we require MITM but the remote device can't provide that * (it has NoInputNoOutput) then reject the confirmation * request. The only exception is when we're dedicated bonding * initiators (connect_cfm_cb set) since then we always have the MITM * bit set. */ if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) { BT_DBG("Rejecting request: remote device can't provide MITM"); hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); goto unlock; } /* If no side requires MITM protection; auto-accept */ if ((!loc_mitm || conn->remote_cap == 0x03) && (!rem_mitm || conn->io_capability == 0x03)) { /* If we're not the initiators request authorization to * proceed from user space (mgmt_user_confirm with * confirm_hint set to 1). */ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { BT_DBG("Confirming auto-accept as acceptor"); confirm_hint = 1; goto confirm; } BT_DBG("Auto-accept of user confirmation with %ums delay", hdev->auto_accept_delay); if (hdev->auto_accept_delay > 0) { int delay = msecs_to_jiffies(hdev->auto_accept_delay); mod_timer(&conn->auto_accept_timer, jiffies + delay); goto unlock; } hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); goto unlock; } confirm: mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey, confirm_hint); unlock: hci_dev_unlock(hdev); } static void hci_user_passkey_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_user_passkey_req *ev = (void *) skb->data; BT_DBG("%s", hdev->name); if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0); } static void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_simple_pair_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) goto unlock; /* To avoid duplicate auth_failed events to user space we check * the HCI_CONN_AUTH_PEND flag which will be set if we * initiated the authentication. A traditional auth_complete * event gets always produced as initiator and is also mapped to * the mgmt_auth_failed event */ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status) mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); hci_conn_put(conn); unlock: hci_dev_unlock(hdev); } static void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_remote_host_features *ev = (void *) skb->data; struct inquiry_entry *ie; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); if (ie) ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP); hci_dev_unlock(hdev); } static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_remote_oob_data_request *ev = (void *) skb->data; struct oob_data *data; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; data = hci_find_remote_oob_data(hdev, &ev->bdaddr); if (data) { struct hci_cp_remote_oob_data_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); memcpy(cp.hash, data->hash, sizeof(cp.hash)); memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer)); hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp), &cp); } else { struct hci_cp_remote_oob_data_neg_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp), &cp); } unlock: hci_dev_unlock(hdev); } static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (!conn) { conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); if (!conn) { BT_ERR("No memory for new connection"); goto unlock; } conn->dst_type = ev->bdaddr_type; if (ev->role == LE_CONN_ROLE_MASTER) { conn->out = true; conn->link_mode |= HCI_LM_MASTER; } } if (ev->status) { mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); hci_proto_connect_cfm(conn, ev->status); conn->state = BT_CLOSED; hci_conn_del(conn); goto unlock; } if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type, 0, NULL, 0, NULL); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; hci_conn_hold_device(conn); hci_conn_add_sysfs(conn); hci_proto_connect_cfm(conn, ev->status); unlock: hci_dev_unlock(hdev); } static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) { u8 num_reports = skb->data[0]; void *ptr = &skb->data[1]; s8 rssi; hci_dev_lock(hdev); while (num_reports--) { struct hci_ev_le_advertising_info *ev = ptr; rssi = ev->data[ev->length]; mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type, NULL, rssi, 0, 1, ev->data, ev->length); ptr += sizeof(*ev) + ev->length + 1; } hci_dev_unlock(hdev); } static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_ltk_req *ev = (void *) skb->data; struct hci_cp_le_ltk_reply cp; struct hci_cp_le_ltk_neg_reply neg; struct hci_conn *conn; struct smp_ltk *ltk; BT_DBG("%s handle 0x%4.4x", hdev->name, __le16_to_cpu(ev->handle)); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn == NULL) goto not_found; ltk = hci_find_ltk(hdev, ev->ediv, ev->random); if (ltk == NULL) goto not_found; memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); cp.handle = cpu_to_le16(conn->handle); if (ltk->authenticated) conn->sec_level = BT_SECURITY_HIGH; hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); if (ltk->type & HCI_SMP_STK) { list_del(<k->list); kfree(ltk); } hci_dev_unlock(hdev); return; not_found: neg.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(neg), &neg); hci_dev_unlock(hdev); } static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_meta *le_ev = (void *) skb->data; skb_pull(skb, sizeof(*le_ev)); switch (le_ev->subevent) { case HCI_EV_LE_CONN_COMPLETE: hci_le_conn_complete_evt(hdev, skb); break; case HCI_EV_LE_ADVERTISING_REPORT: hci_le_adv_report_evt(hdev, skb); break; case HCI_EV_LE_LTK_REQ: hci_le_ltk_request_evt(hdev, skb); break; default: break; } } void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; __u8 event = hdr->evt; skb_pull(skb, HCI_EVENT_HDR_SIZE); switch (event) { case HCI_EV_INQUIRY_COMPLETE: hci_inquiry_complete_evt(hdev, skb); break; case HCI_EV_INQUIRY_RESULT: hci_inquiry_result_evt(hdev, skb); break; case HCI_EV_CONN_COMPLETE: hci_conn_complete_evt(hdev, skb); break; case HCI_EV_CONN_REQUEST: hci_conn_request_evt(hdev, skb); break; case HCI_EV_DISCONN_COMPLETE: hci_disconn_complete_evt(hdev, skb); break; case HCI_EV_AUTH_COMPLETE: hci_auth_complete_evt(hdev, skb); break; case HCI_EV_REMOTE_NAME: hci_remote_name_evt(hdev, skb); break; case HCI_EV_ENCRYPT_CHANGE: hci_encrypt_change_evt(hdev, skb); break; case HCI_EV_CHANGE_LINK_KEY_COMPLETE: hci_change_link_key_complete_evt(hdev, skb); break; case HCI_EV_REMOTE_FEATURES: hci_remote_features_evt(hdev, skb); break; case HCI_EV_REMOTE_VERSION: hci_remote_version_evt(hdev, skb); break; case HCI_EV_QOS_SETUP_COMPLETE: hci_qos_setup_complete_evt(hdev, skb); break; case HCI_EV_CMD_COMPLETE: hci_cmd_complete_evt(hdev, skb); break; case HCI_EV_CMD_STATUS: hci_cmd_status_evt(hdev, skb); break; case HCI_EV_ROLE_CHANGE: hci_role_change_evt(hdev, skb); break; case HCI_EV_NUM_COMP_PKTS: hci_num_comp_pkts_evt(hdev, skb); break; case HCI_EV_MODE_CHANGE: hci_mode_change_evt(hdev, skb); break; case HCI_EV_PIN_CODE_REQ: hci_pin_code_request_evt(hdev, skb); break; case HCI_EV_LINK_KEY_REQ: hci_link_key_request_evt(hdev, skb); break; case HCI_EV_LINK_KEY_NOTIFY: hci_link_key_notify_evt(hdev, skb); break; case HCI_EV_CLOCK_OFFSET: hci_clock_offset_evt(hdev, skb); break; case HCI_EV_PKT_TYPE_CHANGE: hci_pkt_type_change_evt(hdev, skb); break; case HCI_EV_PSCAN_REP_MODE: hci_pscan_rep_mode_evt(hdev, skb); break; case HCI_EV_INQUIRY_RESULT_WITH_RSSI: hci_inquiry_result_with_rssi_evt(hdev, skb); break; case HCI_EV_REMOTE_EXT_FEATURES: hci_remote_ext_features_evt(hdev, skb); break; case HCI_EV_SYNC_CONN_COMPLETE: hci_sync_conn_complete_evt(hdev, skb); break; case HCI_EV_SYNC_CONN_CHANGED: hci_sync_conn_changed_evt(hdev, skb); break; case HCI_EV_SNIFF_SUBRATE: hci_sniff_subrate_evt(hdev, skb); break; case HCI_EV_EXTENDED_INQUIRY_RESULT: hci_extended_inquiry_result_evt(hdev, skb); break; case HCI_EV_KEY_REFRESH_COMPLETE: hci_key_refresh_complete_evt(hdev, skb); break; case HCI_EV_IO_CAPA_REQUEST: hci_io_capa_request_evt(hdev, skb); break; case HCI_EV_IO_CAPA_REPLY: hci_io_capa_reply_evt(hdev, skb); break; case HCI_EV_USER_CONFIRM_REQUEST: hci_user_confirm_request_evt(hdev, skb); break; case HCI_EV_USER_PASSKEY_REQUEST: hci_user_passkey_request_evt(hdev, skb); break; case HCI_EV_SIMPLE_PAIR_COMPLETE: hci_simple_pair_complete_evt(hdev, skb); break; case HCI_EV_REMOTE_HOST_FEATURES: hci_remote_host_features_evt(hdev, skb); break; case HCI_EV_LE_META: hci_le_meta_evt(hdev, skb); break; case HCI_EV_REMOTE_OOB_DATA_REQUEST: hci_remote_oob_data_request_evt(hdev, skb); break; case HCI_EV_NUM_COMP_BLOCKS: hci_num_comp_blocks_evt(hdev, skb); break; default: BT_DBG("%s event 0x%2.2x", hdev->name, event); break; } kfree_skb(skb); hdev->stat.evt_rx++; } compat-drivers-2012-09-18/net/bluetooth/smp.c0000644000175000017500000005624512026211315020132 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include #include #include #include #include #define SMP_TIMEOUT msecs_to_jiffies(30000) static inline void swap128(u8 src[16], u8 dst[16]) { int i; for (i = 0; i < 16; i++) dst[15 - i] = src[i]; } static inline void swap56(u8 src[7], u8 dst[7]) { int i; for (i = 0; i < 7; i++) dst[6 - i] = src[i]; } static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) { struct blkcipher_desc desc; struct scatterlist sg; int err, iv_len; unsigned char iv[128]; if (tfm == NULL) { BT_ERR("tfm %p", tfm); return -EINVAL; } desc.tfm = tfm; desc.flags = 0; err = crypto_blkcipher_setkey(tfm, k, 16); if (err) { BT_ERR("cipher setkey failed: %d", err); return err; } sg_init_one(&sg, r, 16); iv_len = crypto_blkcipher_ivsize(tfm); if (iv_len) { memset(&iv, 0xff, iv_len); crypto_blkcipher_set_iv(tfm, iv, iv_len); } err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16); if (err) BT_ERR("Encrypt data error %d", err); return err; } static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra, u8 res[16]) { u8 p1[16], p2[16]; int err; memset(p1, 0, 16); /* p1 = pres || preq || _rat || _iat */ swap56(pres, p1); swap56(preq, p1 + 7); p1[14] = _rat; p1[15] = _iat; memset(p2, 0, 16); /* p2 = padding || ia || ra */ baswap((bdaddr_t *) (p2 + 4), ia); baswap((bdaddr_t *) (p2 + 10), ra); /* res = r XOR p1 */ u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); /* res = e(k, res) */ err = smp_e(tfm, k, res); if (err) { BT_ERR("Encrypt data error"); return err; } /* res = res XOR p2 */ u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); /* res = e(k, res) */ err = smp_e(tfm, k, res); if (err) BT_ERR("Encrypt data error"); return err; } static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16], u8 r2[16], u8 _r[16]) { int err; /* Just least significant octets from r1 and r2 are considered */ memcpy(_r, r1 + 8, 8); memcpy(_r + 8, r2 + 8, 8); err = smp_e(tfm, k, _r); if (err) BT_ERR("Encrypt data error"); return err; } static int smp_rand(u8 *buf) { get_random_bytes(buf, 16); return 0; } static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, u16 dlen, void *data) { struct sk_buff *skb; struct l2cap_hdr *lh; int len; len = L2CAP_HDR_SIZE + sizeof(code) + dlen; if (len > conn->mtu) return NULL; skb = bt_skb_alloc(len, GFP_ATOMIC); if (!skb) return NULL; lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(sizeof(code) + dlen); lh->cid = cpu_to_le16(L2CAP_CID_SMP); memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); memcpy(skb_put(skb, dlen), data, dlen); return skb; } static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) { struct sk_buff *skb = smp_build_cmd(conn, code, len, data); BT_DBG("code 0x%2.2x", code); if (!skb) return; skb->priority = HCI_PRIO_MAX; hci_send_acl(conn->hchan, skb, 0); cancel_delayed_work_sync(&conn->security_timer); schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT); } static __u8 authreq_to_seclevel(__u8 authreq) { if (authreq & SMP_AUTH_MITM) return BT_SECURITY_HIGH; else return BT_SECURITY_MEDIUM; } static __u8 seclevel_to_authreq(__u8 sec_level) { switch (sec_level) { case BT_SECURITY_HIGH: return SMP_AUTH_MITM | SMP_AUTH_BONDING; case BT_SECURITY_MEDIUM: return SMP_AUTH_BONDING; default: return SMP_AUTH_NONE; } } static void build_pairing_cmd(struct l2cap_conn *conn, struct smp_cmd_pairing *req, struct smp_cmd_pairing *rsp, __u8 authreq) { u8 dist_keys = 0; if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) { dist_keys = SMP_DIST_ENC_KEY; authreq |= SMP_AUTH_BONDING; } else { authreq &= ~SMP_AUTH_BONDING; } if (rsp == NULL) { req->io_capability = conn->hcon->io_capability; req->oob_flag = SMP_OOB_NOT_PRESENT; req->max_key_size = SMP_MAX_ENC_KEY_SIZE; req->init_key_dist = 0; req->resp_key_dist = dist_keys; req->auth_req = authreq; return; } rsp->io_capability = conn->hcon->io_capability; rsp->oob_flag = SMP_OOB_NOT_PRESENT; rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; rsp->init_key_dist = 0; rsp->resp_key_dist = req->resp_key_dist & dist_keys; rsp->auth_req = authreq; } static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) { struct smp_chan *smp = conn->smp_chan; if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || (max_key_size < SMP_MIN_ENC_KEY_SIZE)) return SMP_ENC_KEY_SIZE; smp->enc_key_size = max_key_size; return 0; } static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) { struct hci_conn *hcon = conn->hcon; if (send) smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type, hcon->dst_type, reason); cancel_delayed_work_sync(&conn->security_timer); if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) smp_chan_destroy(conn); } #define JUST_WORKS 0x00 #define JUST_CFM 0x01 #define REQ_PASSKEY 0x02 #define CFM_PASSKEY 0x03 #define REQ_OOB 0x04 #define OVERLAP 0xFF static const u8 gen_method[5][5] = { { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM }, { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP }, }; static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, u8 local_io, u8 remote_io) { struct hci_conn *hcon = conn->hcon; struct smp_chan *smp = conn->smp_chan; u8 method; u32 passkey = 0; int ret = 0; /* Initialize key for JUST WORKS */ memset(smp->tk, 0, sizeof(smp->tk)); clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); /* If neither side wants MITM, use JUST WORKS */ /* If either side has unknown io_caps, use JUST WORKS */ /* Otherwise, look up method from the table */ if (!(auth & SMP_AUTH_MITM) || local_io > SMP_IO_KEYBOARD_DISPLAY || remote_io > SMP_IO_KEYBOARD_DISPLAY) method = JUST_WORKS; else method = gen_method[remote_io][local_io]; /* If not bonding, don't ask user to confirm a Zero TK */ if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) method = JUST_WORKS; /* If Just Works, Continue with Zero TK */ if (method == JUST_WORKS) { set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); return 0; } /* Not Just Works/Confirm results in MITM Authentication */ if (method != JUST_CFM) set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags); /* If both devices have Keyoard-Display I/O, the master * Confirms and the slave Enters the passkey. */ if (method == OVERLAP) { if (hcon->link_mode & HCI_LM_MASTER) method = CFM_PASSKEY; else method = REQ_PASSKEY; } /* Generate random passkey. Not valid until confirmed. */ if (method == CFM_PASSKEY) { u8 key[16]; memset(key, 0, sizeof(key)); get_random_bytes(&passkey, sizeof(passkey)); passkey %= 1000000; put_unaligned_le32(passkey, key); swap128(key, smp->tk); BT_DBG("PassKey: %d", passkey); } hci_dev_lock(hcon->hdev); if (method == REQ_PASSKEY) ret = mgmt_user_passkey_request(hcon->hdev, conn->dst, hcon->type, hcon->dst_type); else ret = mgmt_user_confirm_request(hcon->hdev, conn->dst, hcon->type, hcon->dst_type, cpu_to_le32(passkey), 0); hci_dev_unlock(hcon->hdev); return ret; } static void confirm_work(struct work_struct *work) { struct smp_chan *smp = container_of(work, struct smp_chan, confirm); struct l2cap_conn *conn = smp->conn; struct crypto_blkcipher *tfm; struct smp_cmd_pairing_confirm cp; int ret; u8 res[16], reason; BT_DBG("conn %p", conn); tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { reason = SMP_UNSPECIFIED; goto error; } smp->tfm = tfm; if (conn->hcon->out) ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, conn->src, conn->hcon->dst_type, conn->dst, res); else ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, conn->hcon->dst_type, conn->dst, 0, conn->src, res); if (ret) { reason = SMP_UNSPECIFIED; goto error; } clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); swap128(res, cp.confirm_val); smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); return; error: smp_failure(conn, reason, 1); } static void random_work(struct work_struct *work) { struct smp_chan *smp = container_of(work, struct smp_chan, random); struct l2cap_conn *conn = smp->conn; struct hci_conn *hcon = conn->hcon; struct crypto_blkcipher *tfm = smp->tfm; u8 reason, confirm[16], res[16], key[16]; int ret; if (IS_ERR_OR_NULL(tfm)) { reason = SMP_UNSPECIFIED; goto error; } BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); if (hcon->out) ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, conn->src, hcon->dst_type, conn->dst, res); else ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, hcon->dst_type, conn->dst, 0, conn->src, res); if (ret) { reason = SMP_UNSPECIFIED; goto error; } swap128(res, confirm); if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { BT_ERR("Pairing failed (confirmation values mismatch)"); reason = SMP_CONFIRM_FAILED; goto error; } if (hcon->out) { u8 stk[16], rand[8]; __le16 ediv; memset(rand, 0, sizeof(rand)); ediv = 0; smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); swap128(key, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { reason = SMP_UNSPECIFIED; goto error; } hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = smp->enc_key_size; } else { u8 stk[16], r[16], rand[8]; __le16 ediv; memset(rand, 0, sizeof(rand)); ediv = 0; swap128(smp->prnd, r); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key); swap128(key, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type, HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size, ediv, rand); } return; error: smp_failure(conn, reason, 1); } static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) { struct smp_chan *smp; smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC); if (!smp) return NULL; INIT_WORK(&smp->confirm, confirm_work); INIT_WORK(&smp->random, random_work); smp->conn = conn; conn->smp_chan = smp; conn->hcon->smp_conn = conn; hci_conn_hold(conn->hcon); return smp; } void smp_chan_destroy(struct l2cap_conn *conn) { struct smp_chan *smp = conn->smp_chan; BUG_ON(!smp); if (smp->tfm) crypto_free_blkcipher(smp->tfm); kfree(smp); conn->smp_chan = NULL; conn->hcon->smp_conn = NULL; hci_conn_put(conn->hcon); } int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) { struct l2cap_conn *conn = hcon->smp_conn; struct smp_chan *smp; u32 value; u8 key[16]; BT_DBG(""); if (!conn) return -ENOTCONN; smp = conn->smp_chan; switch (mgmt_op) { case MGMT_OP_USER_PASSKEY_REPLY: value = le32_to_cpu(passkey); memset(key, 0, sizeof(key)); BT_DBG("PassKey: %d", value); put_unaligned_le32(value, key); swap128(key, smp->tk); /* Fall Through */ case MGMT_OP_USER_CONFIRM_REPLY: set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); break; case MGMT_OP_USER_PASSKEY_NEG_REPLY: case MGMT_OP_USER_CONFIRM_NEG_REPLY: smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1); return 0; default: smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1); return -EOPNOTSUPP; } /* If it is our turn to send Pairing Confirm, do so now */ if (test_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags)) queue_work(hcon->hdev->workqueue, &smp->confirm); return 0; } static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; struct smp_chan *smp; u8 key_size; u8 auth = SMP_AUTH_NONE; int ret; BT_DBG("conn %p", conn); if (conn->hcon->link_mode & HCI_LM_MASTER) return SMP_CMD_NOTSUPP; if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) smp = smp_chan_create(conn); else smp = conn->smp_chan; if (!smp) return SMP_UNSPECIFIED; smp->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); /* We didn't start the pairing, so match remote */ if (req->auth_req & SMP_AUTH_BONDING) auth = req->auth_req; conn->hcon->pending_sec_level = authreq_to_seclevel(auth); build_pairing_cmd(conn, req, &rsp, auth); key_size = min(req->max_key_size, rsp.max_key_size); if (check_enc_key_size(conn, key_size)) return SMP_ENC_KEY_SIZE; ret = smp_rand(smp->prnd); if (ret) return SMP_UNSPECIFIED; smp->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); /* Request setup of TK */ ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability); if (ret) return SMP_UNSPECIFIED; return 0; } static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; struct smp_chan *smp = conn->smp_chan; struct hci_dev *hdev = conn->hcon->hdev; u8 key_size, auth = SMP_AUTH_NONE; int ret; BT_DBG("conn %p", conn); if (!(conn->hcon->link_mode & HCI_LM_MASTER)) return SMP_CMD_NOTSUPP; skb_pull(skb, sizeof(*rsp)); req = (void *) &smp->preq[1]; key_size = min(req->max_key_size, rsp->max_key_size); if (check_enc_key_size(conn, key_size)) return SMP_ENC_KEY_SIZE; ret = smp_rand(smp->prnd); if (ret) return SMP_UNSPECIFIED; smp->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); if ((req->auth_req & SMP_AUTH_BONDING) && (rsp->auth_req & SMP_AUTH_BONDING)) auth = SMP_AUTH_BONDING; auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM; ret = tk_request(conn, 0, auth, req->io_capability, rsp->io_capability); if (ret) return SMP_UNSPECIFIED; set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); /* Can't compose response until we have been confirmed */ if (!test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) return 0; queue_work(hdev->workqueue, &smp->confirm); return 0; } static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_chan *smp = conn->smp_chan; struct hci_dev *hdev = conn->hcon->hdev; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); skb_pull(skb, sizeof(smp->pcnf)); if (conn->hcon->out) { u8 random[16]; swap128(smp->prnd, random); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), random); } else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) { queue_work(hdev->workqueue, &smp->confirm); } else { set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); } return 0; } static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_chan *smp = conn->smp_chan; struct hci_dev *hdev = conn->hcon->hdev; BT_DBG("conn %p", conn); swap128(skb->data, smp->rrnd); skb_pull(skb, sizeof(smp->rrnd)); queue_work(hdev->workqueue, &smp->random); return 0; } static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) { struct smp_ltk *key; struct hci_conn *hcon = conn->hcon; key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type); if (!key) return 0; if (sec_level > BT_SECURITY_MEDIUM && !key->authenticated) return 0; if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) return 1; hci_le_start_enc(hcon, key->ediv, key->rand, key->val); hcon->enc_key_size = key->enc_size; return 1; } static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_security_req *rp = (void *) skb->data; struct smp_cmd_pairing cp; struct hci_conn *hcon = conn->hcon; struct smp_chan *smp; BT_DBG("conn %p", conn); hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req); if (smp_ltk_encrypt(conn, hcon->pending_sec_level)) return 0; if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return 0; smp = smp_chan_create(conn); skb_pull(skb, sizeof(*rp)); memset(&cp, 0, sizeof(cp)); build_pairing_cmd(conn, &cp, NULL, rp->auth_req); smp->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); return 0; } int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) { struct l2cap_conn *conn = hcon->l2cap_data; struct smp_chan *smp = conn->smp_chan; __u8 authreq; BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); if (!lmp_host_le_capable(hcon->hdev)) return 1; if (sec_level == BT_SECURITY_LOW) return 1; if (hcon->sec_level >= sec_level) return 1; if (hcon->link_mode & HCI_LM_MASTER) if (smp_ltk_encrypt(conn, sec_level)) goto done; if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return 0; smp = smp_chan_create(conn); if (!smp) return 1; authreq = seclevel_to_authreq(sec_level); if (hcon->link_mode & HCI_LM_MASTER) { struct smp_cmd_pairing cp; build_pairing_cmd(conn, &cp, NULL, authreq); smp->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { struct smp_cmd_security_req cp; cp.auth_req = authreq; smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); } done: hcon->pending_sec_level = sec_level; return 0; } static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_encrypt_info *rp = (void *) skb->data; struct smp_chan *smp = conn->smp_chan; skb_pull(skb, sizeof(*rp)); memcpy(smp->tk, rp->ltk, sizeof(smp->tk)); return 0; } static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_master_ident *rp = (void *) skb->data; struct smp_chan *smp = conn->smp_chan; struct hci_dev *hdev = conn->hcon->hdev; struct hci_conn *hcon = conn->hcon; u8 authenticated; skb_pull(skb, sizeof(*rp)); hci_dev_lock(hdev); authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH); hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size, rp->ediv, rp->rand); smp_distribute_keys(conn, 1); hci_dev_unlock(hdev); return 0; } int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { __u8 code = skb->data[0]; __u8 reason; int err = 0; if (!lmp_host_le_capable(conn->hcon->hdev)) { err = -ENOTSUPP; reason = SMP_PAIRING_NOTSUPP; goto done; } skb_pull(skb, sizeof(code)); switch (code) { case SMP_CMD_PAIRING_REQ: reason = smp_cmd_pairing_req(conn, skb); break; case SMP_CMD_PAIRING_FAIL: smp_failure(conn, skb->data[0], 0); reason = 0; err = -EPERM; break; case SMP_CMD_PAIRING_RSP: reason = smp_cmd_pairing_rsp(conn, skb); break; case SMP_CMD_SECURITY_REQ: reason = smp_cmd_security_req(conn, skb); break; case SMP_CMD_PAIRING_CONFIRM: reason = smp_cmd_pairing_confirm(conn, skb); break; case SMP_CMD_PAIRING_RANDOM: reason = smp_cmd_pairing_random(conn, skb); break; case SMP_CMD_ENCRYPT_INFO: reason = smp_cmd_encrypt_info(conn, skb); break; case SMP_CMD_MASTER_IDENT: reason = smp_cmd_master_ident(conn, skb); break; case SMP_CMD_IDENT_INFO: case SMP_CMD_IDENT_ADDR_INFO: case SMP_CMD_SIGN_INFO: /* Just ignored */ reason = 0; break; default: BT_DBG("Unknown command code 0x%2.2x", code); reason = SMP_CMD_NOTSUPP; err = -EOPNOTSUPP; goto done; } done: if (reason) smp_failure(conn, reason, 1); kfree_skb(skb); return err; } int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) { struct smp_cmd_pairing *req, *rsp; struct smp_chan *smp = conn->smp_chan; __u8 *keydist; BT_DBG("conn %p force %d", conn, force); if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) return 0; rsp = (void *) &smp->prsp[1]; /* The responder sends its keys first */ if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07)) return 0; req = (void *) &smp->preq[1]; if (conn->hcon->out) { keydist = &rsp->init_key_dist; *keydist &= req->init_key_dist; } else { keydist = &rsp->resp_key_dist; *keydist &= req->resp_key_dist; } BT_DBG("keydist 0x%x", *keydist); if (*keydist & SMP_DIST_ENC_KEY) { struct smp_cmd_encrypt_info enc; struct smp_cmd_master_ident ident; struct hci_conn *hcon = conn->hcon; u8 authenticated; __le16 ediv; get_random_bytes(enc.ltk, sizeof(enc.ltk)); get_random_bytes(&ediv, sizeof(ediv)); get_random_bytes(ident.rand, sizeof(ident.rand)); smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); authenticated = hcon->sec_level == BT_SECURITY_HIGH; hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, HCI_SMP_LTK_SLAVE, 1, authenticated, enc.ltk, smp->enc_key_size, ediv, ident.rand); ident.ediv = ediv; smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); *keydist &= ~SMP_DIST_ENC_KEY; } if (*keydist & SMP_DIST_ID_KEY) { struct smp_cmd_ident_addr_info addrinfo; struct smp_cmd_ident_info idinfo; /* Send a dummy key */ get_random_bytes(idinfo.irk, sizeof(idinfo.irk)); smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo); /* Just public address */ memset(&addrinfo, 0, sizeof(addrinfo)); bacpy(&addrinfo.bdaddr, conn->src); smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo), &addrinfo); *keydist &= ~SMP_DIST_ID_KEY; } if (*keydist & SMP_DIST_SIGN) { struct smp_cmd_sign_info sign; /* Send a dummy key */ get_random_bytes(sign.csrk, sizeof(sign.csrk)); smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign); *keydist &= ~SMP_DIST_SIGN; } if (conn->hcon->out || force) { clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags); cancel_delayed_work_sync(&conn->security_timer); smp_chan_destroy(conn); } return 0; } compat-drivers-2012-09-18/net/bluetooth/mgmt.c0000644000175000017500000024654612026211315020304 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2010 Nokia Corporation Copyright (C) 2011-2012 Intel Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth HCI Management interface */ #include #include #include #include #include #include bool enable_hs; #define MGMT_VERSION 1 #define MGMT_REVISION 1 static const u16 mgmt_commands[] = { MGMT_OP_READ_INDEX_LIST, MGMT_OP_READ_INFO, MGMT_OP_SET_POWERED, MGMT_OP_SET_DISCOVERABLE, MGMT_OP_SET_CONNECTABLE, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_OP_SET_PAIRABLE, MGMT_OP_SET_LINK_SECURITY, MGMT_OP_SET_SSP, MGMT_OP_SET_HS, MGMT_OP_SET_LE, MGMT_OP_SET_DEV_CLASS, MGMT_OP_SET_LOCAL_NAME, MGMT_OP_ADD_UUID, MGMT_OP_REMOVE_UUID, MGMT_OP_LOAD_LINK_KEYS, MGMT_OP_LOAD_LONG_TERM_KEYS, MGMT_OP_DISCONNECT, MGMT_OP_GET_CONNECTIONS, MGMT_OP_PIN_CODE_REPLY, MGMT_OP_PIN_CODE_NEG_REPLY, MGMT_OP_SET_IO_CAPABILITY, MGMT_OP_PAIR_DEVICE, MGMT_OP_CANCEL_PAIR_DEVICE, MGMT_OP_UNPAIR_DEVICE, MGMT_OP_USER_CONFIRM_REPLY, MGMT_OP_USER_CONFIRM_NEG_REPLY, MGMT_OP_USER_PASSKEY_REPLY, MGMT_OP_USER_PASSKEY_NEG_REPLY, MGMT_OP_READ_LOCAL_OOB_DATA, MGMT_OP_ADD_REMOTE_OOB_DATA, MGMT_OP_REMOVE_REMOTE_OOB_DATA, MGMT_OP_START_DISCOVERY, MGMT_OP_STOP_DISCOVERY, MGMT_OP_CONFIRM_NAME, MGMT_OP_BLOCK_DEVICE, MGMT_OP_UNBLOCK_DEVICE, MGMT_OP_SET_DEVICE_ID, }; static const u16 mgmt_events[] = { MGMT_EV_CONTROLLER_ERROR, MGMT_EV_INDEX_ADDED, MGMT_EV_INDEX_REMOVED, MGMT_EV_NEW_SETTINGS, MGMT_EV_CLASS_OF_DEV_CHANGED, MGMT_EV_LOCAL_NAME_CHANGED, MGMT_EV_NEW_LINK_KEY, MGMT_EV_NEW_LONG_TERM_KEY, MGMT_EV_DEVICE_CONNECTED, MGMT_EV_DEVICE_DISCONNECTED, MGMT_EV_CONNECT_FAILED, MGMT_EV_PIN_CODE_REQUEST, MGMT_EV_USER_CONFIRM_REQUEST, MGMT_EV_USER_PASSKEY_REQUEST, MGMT_EV_AUTH_FAILED, MGMT_EV_DEVICE_FOUND, MGMT_EV_DISCOVERING, MGMT_EV_DEVICE_BLOCKED, MGMT_EV_DEVICE_UNBLOCKED, MGMT_EV_DEVICE_UNPAIRED, }; /* * These LE scan and inquiry parameters were chosen according to LE General * Discovery Procedure specification. */ #define LE_SCAN_TYPE 0x01 #define LE_SCAN_WIN 0x12 #define LE_SCAN_INT 0x12 #define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */ #define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */ #define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ #define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */ #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) #define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) struct pending_cmd { struct list_head list; u16 opcode; int index; void *param; struct sock *sk; void *user_data; }; /* HCI to MGMT error code conversion table */ static u8 mgmt_status_table[] = { MGMT_STATUS_SUCCESS, MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */ MGMT_STATUS_NOT_CONNECTED, /* No Connection */ MGMT_STATUS_FAILED, /* Hardware Failure */ MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */ MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */ MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */ MGMT_STATUS_NO_RESOURCES, /* Memory Full */ MGMT_STATUS_TIMEOUT, /* Connection Timeout */ MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */ MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */ MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */ MGMT_STATUS_BUSY, /* Command Disallowed */ MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */ MGMT_STATUS_REJECTED, /* Rejected Security */ MGMT_STATUS_REJECTED, /* Rejected Personal */ MGMT_STATUS_TIMEOUT, /* Host Timeout */ MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */ MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */ MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */ MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */ MGMT_STATUS_DISCONNECTED, /* OE Power Off */ MGMT_STATUS_DISCONNECTED, /* Connection Terminated */ MGMT_STATUS_BUSY, /* Repeated Attempts */ MGMT_STATUS_REJECTED, /* Pairing Not Allowed */ MGMT_STATUS_FAILED, /* Unknown LMP PDU */ MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */ MGMT_STATUS_REJECTED, /* SCO Offset Rejected */ MGMT_STATUS_REJECTED, /* SCO Interval Rejected */ MGMT_STATUS_REJECTED, /* Air Mode Rejected */ MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */ MGMT_STATUS_FAILED, /* Unspecified Error */ MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */ MGMT_STATUS_FAILED, /* Role Change Not Allowed */ MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */ MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */ MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */ MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */ MGMT_STATUS_FAILED, /* Unit Link Key Used */ MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */ MGMT_STATUS_TIMEOUT, /* Instant Passed */ MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */ MGMT_STATUS_FAILED, /* Transaction Collision */ MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */ MGMT_STATUS_REJECTED, /* QoS Rejected */ MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */ MGMT_STATUS_REJECTED, /* Insufficient Security */ MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */ MGMT_STATUS_BUSY, /* Role Switch Pending */ MGMT_STATUS_FAILED, /* Slot Violation */ MGMT_STATUS_FAILED, /* Role Switch Failed */ MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */ MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */ MGMT_STATUS_BUSY, /* Host Busy Pairing */ MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */ MGMT_STATUS_BUSY, /* Controller Busy */ MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */ MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */ MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */ MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */ MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */ }; bool mgmt_valid_hdev(struct hci_dev *hdev) { return hdev->dev_type == HCI_BREDR; } static u8 mgmt_status(u8 hci_status) { if (hci_status < ARRAY_SIZE(mgmt_status_table)) return mgmt_status_table[hci_status]; return MGMT_STATUS_FAILED; } static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) { struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_status *ev; int err; BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL); if (!skb) return -ENOMEM; hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev)); ev = (void *) skb_put(skb, sizeof(*ev)); ev->status = status; ev->opcode = cpu_to_le16(cmd); err = sock_queue_rcv_skb(sk, skb); if (err < 0) kfree_skb(skb); return err; } static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, void *rp, size_t rp_len) { struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *ev; int err; BT_DBG("sock %p", sk); skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL); if (!skb) return -ENOMEM; hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); ev->opcode = cpu_to_le16(cmd); ev->status = status; if (rp) memcpy(ev->data, rp, rp_len); err = sock_queue_rcv_skb(sk, skb); if (err < 0) kfree_skb(skb); return err; } static int read_version(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_version rp; BT_DBG("sock %p", sk); rp.version = MGMT_VERSION; rp.revision = __constant_cpu_to_le16(MGMT_REVISION); return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp, sizeof(rp)); } static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_commands *rp; const u16 num_commands = ARRAY_SIZE(mgmt_commands); const u16 num_events = ARRAY_SIZE(mgmt_events); __le16 *opcode; size_t rp_size; int i, err; BT_DBG("sock %p", sk); rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16)); rp = kmalloc(rp_size, GFP_KERNEL); if (!rp) return -ENOMEM; rp->num_commands = __constant_cpu_to_le16(num_commands); rp->num_events = __constant_cpu_to_le16(num_events); for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++) put_unaligned_le16(mgmt_commands[i], opcode); for (i = 0; i < num_events; i++, opcode++) put_unaligned_le16(mgmt_events[i], opcode); err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp, rp_size); kfree(rp); return err; } static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_index_list *rp; struct hci_dev *d; size_t rp_len; u16 count; int i, err; BT_DBG("sock %p", sk); read_lock(&hci_dev_list_lock); count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (!mgmt_valid_hdev(d)) continue; count++; } rp_len = sizeof(*rp) + (2 * count); rp = kmalloc(rp_len, GFP_ATOMIC); if (!rp) { read_unlock(&hci_dev_list_lock); return -ENOMEM; } rp->num_controllers = cpu_to_le16(count); i = 0; list_for_each_entry(d, &hci_dev_list, list) { if (test_bit(HCI_SETUP, &d->dev_flags)) continue; if (!mgmt_valid_hdev(d)) continue; rp->index[i++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); } read_unlock(&hci_dev_list_lock); err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp, rp_len); kfree(rp); return err; } static u32 get_supported_settings(struct hci_dev *hdev) { u32 settings = 0; settings |= MGMT_SETTING_POWERED; settings |= MGMT_SETTING_CONNECTABLE; settings |= MGMT_SETTING_FAST_CONNECTABLE; settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_PAIRABLE; if (lmp_ssp_capable(hdev)) settings |= MGMT_SETTING_SSP; if (lmp_bredr_capable(hdev)) { settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_LINK_SECURITY; } if (enable_hs) settings |= MGMT_SETTING_HS; if (lmp_le_capable(hdev)) settings |= MGMT_SETTING_LE; return settings; } static u32 get_current_settings(struct hci_dev *hdev) { u32 settings = 0; if (hdev_is_powered(hdev)) settings |= MGMT_SETTING_POWERED; if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_CONNECTABLE; if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_DISCOVERABLE; if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_PAIRABLE; if (lmp_bredr_capable(hdev)) settings |= MGMT_SETTING_BREDR; if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_LE; if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) settings |= MGMT_SETTING_LINK_SECURITY; if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_SSP; if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_HS; return settings; } #define PNP_INFO_SVCLASS_ID 0x1200 static u8 bluetooth_base_uuid[] = { 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static u16 get_uuid16(u8 *uuid128) { u32 val; int i; for (i = 0; i < 12; i++) { if (bluetooth_base_uuid[i] != uuid128[i]) return 0; } val = get_unaligned_le32(&uuid128[12]); if (val > 0xffff) return 0; return (u16) val; } static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; u16 eir_len = 0; u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; int i, truncated = 0; struct bt_uuid *uuid; size_t name_len; name_len = strlen(hdev->dev_name); if (name_len > 0) { /* EIR Data type */ if (name_len > 48) { name_len = 48; ptr[1] = EIR_NAME_SHORT; } else ptr[1] = EIR_NAME_COMPLETE; /* EIR Data length */ ptr[0] = name_len + 1; memcpy(ptr + 2, hdev->dev_name, name_len); eir_len += (name_len + 2); ptr += (name_len + 2); } if (hdev->inq_tx_power) { ptr[0] = 2; ptr[1] = EIR_TX_POWER; ptr[2] = (u8) hdev->inq_tx_power; eir_len += 3; ptr += 3; } if (hdev->devid_source > 0) { ptr[0] = 9; ptr[1] = EIR_DEVICE_ID; put_unaligned_le16(hdev->devid_source, ptr + 2); put_unaligned_le16(hdev->devid_vendor, ptr + 4); put_unaligned_le16(hdev->devid_product, ptr + 6); put_unaligned_le16(hdev->devid_version, ptr + 8); eir_len += 10; ptr += 10; } memset(uuid16_list, 0, sizeof(uuid16_list)); /* Group all UUID16 types */ list_for_each_entry(uuid, &hdev->uuids, list) { u16 uuid16; uuid16 = get_uuid16(uuid->uuid); if (uuid16 == 0) return; if (uuid16 < 0x1100) continue; if (uuid16 == PNP_INFO_SVCLASS_ID) continue; /* Stop if not enough space to put next UUID */ if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { truncated = 1; break; } /* Check for duplicates */ for (i = 0; uuid16_list[i] != 0; i++) if (uuid16_list[i] == uuid16) break; if (uuid16_list[i] == 0) { uuid16_list[i] = uuid16; eir_len += sizeof(u16); } } if (uuid16_list[0] != 0) { u8 *length = ptr; /* EIR Data type */ ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; ptr += 2; eir_len += 2; for (i = 0; uuid16_list[i] != 0; i++) { *ptr++ = (uuid16_list[i] & 0x00ff); *ptr++ = (uuid16_list[i] & 0xff00) >> 8; } /* EIR Data length */ *length = (i * sizeof(u16)) + 1; } } static int update_eir(struct hci_dev *hdev) { struct hci_cp_write_eir cp; if (!hdev_is_powered(hdev)) return 0; if (!(hdev->features[6] & LMP_EXT_INQ)) return 0; if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) return 0; if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return 0; memset(&cp, 0, sizeof(cp)); create_eir(hdev, cp.data); if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) return 0; memcpy(hdev->eir, cp.data, sizeof(cp.data)); return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } static u8 get_service_classes(struct hci_dev *hdev) { struct bt_uuid *uuid; u8 val = 0; list_for_each_entry(uuid, &hdev->uuids, list) val |= uuid->svc_hint; return val; } static int update_class(struct hci_dev *hdev) { u8 cod[3]; int err; BT_DBG("%s", hdev->name); if (!hdev_is_powered(hdev)) return 0; if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return 0; cod[0] = hdev->minor_class; cod[1] = hdev->major_class; cod[2] = get_service_classes(hdev); if (memcmp(cod, hdev->dev_class, 3) == 0) return 0; err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); if (err == 0) set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); return err; } static void service_cache_off(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, service_cache.work); if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return; hci_dev_lock(hdev); update_eir(hdev); update_class(hdev); hci_dev_unlock(hdev); } static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) { if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) return; INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); /* Non-mgmt controlled devices get this bit set * implicitly so that pairing works for them, however * for mgmt we require user-space to explicitly enable * it */ clear_bit(HCI_PAIRABLE, &hdev->dev_flags); } static int read_controller_info(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_info rp; BT_DBG("sock %p %s", sk, hdev->name); hci_dev_lock(hdev); memset(&rp, 0, sizeof(rp)); bacpy(&rp.bdaddr, &hdev->bdaddr); rp.version = hdev->hci_ver; rp.manufacturer = cpu_to_le16(hdev->manufacturer); rp.supported_settings = cpu_to_le32(get_supported_settings(hdev)); rp.current_settings = cpu_to_le32(get_current_settings(hdev)); memcpy(rp.dev_class, hdev->dev_class, 3); memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); memcpy(rp.short_name, hdev->short_name, sizeof(hdev->short_name)); hci_dev_unlock(hdev); return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp, sizeof(rp)); } static void mgmt_pending_free(struct pending_cmd *cmd) { sock_put(cmd->sk); kfree(cmd->param); kfree(cmd); } static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, struct hci_dev *hdev, void *data, u16 len) { struct pending_cmd *cmd; cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return NULL; cmd->opcode = opcode; cmd->index = hdev->id; cmd->param = kmalloc(len, GFP_KERNEL); if (!cmd->param) { kfree(cmd); return NULL; } if (data) memcpy(cmd->param, data, len); cmd->sk = sk; sock_hold(sk); list_add(&cmd->list, &hdev->mgmt_pending); return cmd; } static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, void (*cb)(struct pending_cmd *cmd, void *data), void *data) { struct list_head *p, *n; list_for_each_safe(p, n, &hdev->mgmt_pending) { struct pending_cmd *cmd; cmd = list_entry(p, struct pending_cmd, list); if (opcode > 0 && cmd->opcode != opcode) continue; cb(cmd, data); } } static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) { struct pending_cmd *cmd; list_for_each_entry(cmd, &hdev->mgmt_pending, list) { if (cmd->opcode == opcode) return cmd; } return NULL; } static void mgmt_pending_remove(struct pending_cmd *cmd) { list_del(&cmd->list); mgmt_pending_free(cmd); } static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) { __le32 settings = cpu_to_le32(get_current_settings(hdev)); return cmd_complete(sk, hdev->id, opcode, 0, &settings, sizeof(settings)); } static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; int err; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { cancel_delayed_work(&hdev->power_off); if (cp->val) { err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); mgmt_powered(hdev, 1); goto failed; } } if (!!cp->val == hdev_is_powered(hdev)) { err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); goto failed; } if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, MGMT_STATUS_BUSY); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } if (cp->val) schedule_work(&hdev->power_on); else schedule_work(&hdev->power_off.work); err = 0; failed: hci_dev_unlock(hdev); return err; } static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); if (!skb) return -ENOMEM; hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(event); if (hdev) hdr->index = cpu_to_le16(hdev->id); else hdr->index = cpu_to_le16(MGMT_INDEX_NONE); hdr->len = cpu_to_le16(data_len); if (data) memcpy(skb_put(skb, data_len), data, data_len); /* Time stamp */ __net_timestamp(skb); hci_send_to_control(skb, skip_sk); kfree_skb(skb); return 0; } static int new_settings(struct hci_dev *hdev, struct sock *skip) { __le32 ev; ev = cpu_to_le32(get_current_settings(hdev)); return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); } static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_discoverable *cp = data; struct pending_cmd *cmd; u16 timeout; u8 scan; int err; BT_DBG("request for %s", hdev->name); timeout = __le16_to_cpu(cp->timeout); if (!cp->val && timeout > 0) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_INVALID_PARAMS); hci_dev_lock(hdev); if (!hdev_is_powered(hdev) && timeout > 0) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_NOT_POWERED); goto failed; } if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_BUSY); goto failed; } if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_REJECTED); goto failed; } if (!hdev_is_powered(hdev)) { bool changed = false; if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { change_bit(HCI_DISCOVERABLE, &hdev->dev_flags); changed = true; } err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); if (err < 0) goto failed; if (changed) err = new_settings(hdev, sk); goto failed; } if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { if (hdev->discov_timeout > 0) { cancel_delayed_work(&hdev->discov_off); hdev->discov_timeout = 0; } if (cp->val && timeout > 0) { hdev->discov_timeout = timeout; queue_delayed_work(hdev->workqueue, &hdev->discov_off, msecs_to_jiffies(hdev->discov_timeout * 1000)); } err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } scan = SCAN_PAGE; if (cp->val) scan |= SCAN_INQUIRY; else cancel_delayed_work(&hdev->discov_off); err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) mgmt_pending_remove(cmd); if (cp->val) hdev->discov_timeout = timeout; failed: hci_dev_unlock(hdev); return err; } static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; u8 scan; int err; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { bool changed = false; if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) changed = true; if (cp->val) { set_bit(HCI_CONNECTABLE, &hdev->dev_flags); } else { clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); if (err < 0) goto failed; if (changed) err = new_settings(hdev, sk); goto failed; } if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, MGMT_STATUS_BUSY); goto failed; } if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } if (cp->val) { scan = SCAN_PAGE; } else { scan = 0; if (test_bit(HCI_ISCAN, &hdev->flags) && hdev->discov_timeout > 0) cancel_delayed_work(&hdev->discov_off); } err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) mgmt_pending_remove(cmd); failed: hci_dev_unlock(hdev); return err; } static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; int err; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (cp->val) set_bit(HCI_PAIRABLE, &hdev->dev_flags); else clear_bit(HCI_PAIRABLE, &hdev->dev_flags); err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev); if (err < 0) goto failed; err = new_settings(hdev, sk); failed: hci_dev_unlock(hdev); return err; } static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; u8 val; int err; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { bool changed = false; if (!!cp->val != test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { change_bit(HCI_LINK_SECURITY, &hdev->dev_flags); changed = true; } err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); if (err < 0) goto failed; if (changed) err = new_settings(hdev, sk); goto failed; } if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, MGMT_STATUS_BUSY); goto failed; } val = !!cp->val; if (test_bit(HCI_AUTH, &hdev->flags) == val) { err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val); if (err < 0) { mgmt_pending_remove(cmd); goto failed; } failed: hci_dev_unlock(hdev); return err; } static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; u8 val; int err; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (!lmp_ssp_capable(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, MGMT_STATUS_NOT_SUPPORTED); goto failed; } val = !!cp->val; if (!hdev_is_powered(hdev)) { bool changed = false; if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { change_bit(HCI_SSP_ENABLED, &hdev->dev_flags); changed = true; } err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); if (err < 0) goto failed; if (changed) err = new_settings(hdev, sk); goto failed; } if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY); goto failed; } if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) { err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val); if (err < 0) { mgmt_pending_remove(cmd); goto failed; } failed: hci_dev_unlock(hdev); return err; } static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; BT_DBG("request for %s", hdev->name); if (!enable_hs) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_NOT_SUPPORTED); if (cp->val) set_bit(HCI_HS_ENABLED, &hdev->dev_flags); else clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); } static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct hci_cp_write_le_host_supported hci_cp; struct pending_cmd *cmd; int err; u8 val, enabled; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (!lmp_le_capable(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_NOT_SUPPORTED); goto unlock; } val = !!cp->val; enabled = !!(hdev->host_features[0] & LMP_HOST_LE); if (!hdev_is_powered(hdev) || val == enabled) { bool changed = false; if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { change_bit(HCI_LE_ENABLED, &hdev->dev_flags); changed = true; } err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); if (err < 0) goto unlock; if (changed) err = new_settings(hdev, sk); goto unlock; } if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_BUSY); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto unlock; } memset(&hci_cp, 0, sizeof(hci_cp)); if (val) { hci_cp.le = val; hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); } err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), &hci_cp); if (err < 0) mgmt_pending_remove(cmd); unlock: hci_dev_unlock(hdev); return err; } static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; struct pending_cmd *cmd; struct bt_uuid *uuid; int err; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID, MGMT_STATUS_BUSY); goto failed; } uuid = kmalloc(sizeof(*uuid), GFP_KERNEL); if (!uuid) { err = -ENOMEM; goto failed; } memcpy(uuid->uuid, cp->uuid, 16); uuid->svc_hint = cp->svc_hint; list_add(&uuid->list, &hdev->uuids); err = update_class(hdev); if (err < 0) goto failed; err = update_eir(hdev); if (err < 0) goto failed; if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, hdev->dev_class, 3); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); if (!cmd) err = -ENOMEM; failed: hci_dev_unlock(hdev); return err; } static bool enable_service_cache(struct hci_dev *hdev) { if (!hdev_is_powered(hdev)) return false; if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT); return true; } return false; } static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_remove_uuid *cp = data; struct pending_cmd *cmd; struct list_head *p, *n; u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int err, found; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, MGMT_STATUS_BUSY); goto unlock; } if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { err = hci_uuids_clear(hdev); if (enable_service_cache(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, hdev->dev_class, 3); goto unlock; } goto update_class; } found = 0; list_for_each_safe(p, n, &hdev->uuids) { struct bt_uuid *match = list_entry(p, struct bt_uuid, list); if (memcmp(match->uuid, cp->uuid, 16) != 0) continue; list_del(&match->list); found++; } if (found == 0) { err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, MGMT_STATUS_INVALID_PARAMS); goto unlock; } update_class: err = update_class(hdev); if (err < 0) goto unlock; err = update_eir(hdev); if (err < 0) goto unlock; if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, hdev->dev_class, 3); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); if (!cmd) err = -ENOMEM; unlock: hci_dev_unlock(hdev); return err; } static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_dev_class *cp = data; struct pending_cmd *cmd; int err; BT_DBG("request for %s", hdev->name); hci_dev_lock(hdev); if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, MGMT_STATUS_BUSY); goto unlock; } hdev->major_class = cp->major; hdev->minor_class = cp->minor; if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, hdev->dev_class, 3); goto unlock; } if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { hci_dev_unlock(hdev); cancel_delayed_work_sync(&hdev->service_cache); hci_dev_lock(hdev); update_eir(hdev); } err = update_class(hdev); if (err < 0) goto unlock; if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, hdev->dev_class, 3); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); if (!cmd) err = -ENOMEM; unlock: hci_dev_unlock(hdev); return err; } static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_load_link_keys *cp = data; u16 key_count, expected_len; int i; key_count = __le16_to_cpu(cp->key_count); expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_link_key_info); if (expected_len != len) { BT_ERR("load_link_keys: expected %u bytes, got %u bytes", len, expected_len); return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, MGMT_STATUS_INVALID_PARAMS); } BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, key_count); hci_dev_lock(hdev); hci_link_keys_clear(hdev); set_bit(HCI_LINK_KEYS, &hdev->dev_flags); if (cp->debug_keys) set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); else clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); for (i = 0; i < key_count; i++) { struct mgmt_link_key_info *key = &cp->keys[i]; hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val, key->type, key->pin_len); } cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); hci_dev_unlock(hdev); return 0; } static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, struct sock *skip_sk) { struct mgmt_ev_device_unpaired ev; bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = addr_type; return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev), skip_sk); } static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_unpair_device *cp = data; struct mgmt_rp_unpair_device rp; struct hci_cp_disconnect dc; struct pending_cmd *cmd; struct hci_conn *conn; int err; hci_dev_lock(hdev); memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); goto unlock; } if (cp->addr.type == BDADDR_BREDR) err = hci_remove_link_key(hdev, &cp->addr.bdaddr); else err = hci_remove_ltk(hdev, &cp->addr.bdaddr); if (err < 0) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, MGMT_STATUS_NOT_PAIRED, &rp, sizeof(rp)); goto unlock; } if (cp->disconnect) { if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); else conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); } else { conn = NULL; } if (!conn) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0, &rp, sizeof(rp)); device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp, sizeof(*cp)); if (!cmd) { err = -ENOMEM; goto unlock; } dc.handle = cpu_to_le16(conn->handle); dc.reason = 0x13; /* Remote User Terminated Connection */ err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); if (err < 0) mgmt_pending_remove(cmd); unlock: hci_dev_unlock(hdev); return err; } static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_disconnect *cp = data; struct hci_cp_disconnect dc; struct pending_cmd *cmd; struct hci_conn *conn; int err; BT_DBG(""); hci_dev_lock(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, MGMT_STATUS_NOT_POWERED); goto failed; } if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, MGMT_STATUS_BUSY); goto failed; } if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); else conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) { err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, MGMT_STATUS_NOT_CONNECTED); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } dc.handle = cpu_to_le16(conn->handle); dc.reason = HCI_ERROR_REMOTE_USER_TERM; err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); if (err < 0) mgmt_pending_remove(cmd); failed: hci_dev_unlock(hdev); return err; } static u8 link_to_bdaddr(u8 link_type, u8 addr_type) { switch (link_type) { case LE_LINK: switch (addr_type) { case ADDR_LE_DEV_PUBLIC: return BDADDR_LE_PUBLIC; default: /* Fallback to LE Random address type */ return BDADDR_LE_RANDOM; } default: /* Fallback to BR/EDR type */ return BDADDR_BREDR; } } static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_get_connections *rp; struct hci_conn *c; size_t rp_len; int err; u16 i; BT_DBG(""); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, MGMT_STATUS_NOT_POWERED); goto unlock; } i = 0; list_for_each_entry(c, &hdev->conn_hash.list, list) { if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) i++; } rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); rp = kmalloc(rp_len, GFP_KERNEL); if (!rp) { err = -ENOMEM; goto unlock; } i = 0; list_for_each_entry(c, &hdev->conn_hash.list, list) { if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) continue; bacpy(&rp->addr[i].bdaddr, &c->dst); rp->addr[i].type = link_to_bdaddr(c->type, c->dst_type); if (c->type == SCO_LINK || c->type == ESCO_LINK) continue; i++; } rp->conn_count = cpu_to_le16(i); /* Recalculate length in case of filtered SCO connections, etc */ rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp, rp_len); kfree(rp); unlock: hci_dev_unlock(hdev); return err; } static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp) { struct pending_cmd *cmd; int err; cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp, sizeof(*cp)); if (!cmd) return -ENOMEM; err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->addr.bdaddr), &cp->addr.bdaddr); if (err < 0) mgmt_pending_remove(cmd); return err; } static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct hci_conn *conn; struct mgmt_cp_pin_code_reply *cp = data; struct hci_cp_pin_code_reply reply; struct pending_cmd *cmd; int err; BT_DBG(""); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, MGMT_STATUS_NOT_POWERED); goto failed; } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); if (!conn) { err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, MGMT_STATUS_NOT_CONNECTED); goto failed; } if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) { struct mgmt_cp_pin_code_neg_reply ncp; memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr)); BT_ERR("PIN code is not 16 bytes long"); err = send_pin_code_neg_reply(sk, hdev, &ncp); if (err >= 0) err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, MGMT_STATUS_INVALID_PARAMS); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } bacpy(&reply.bdaddr, &cp->addr.bdaddr); reply.pin_len = cp->pin_len; memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code)); err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); if (err < 0) mgmt_pending_remove(cmd); failed: hci_dev_unlock(hdev); return err; } static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_io_capability *cp = data; BT_DBG(""); hci_dev_lock(hdev); hdev->io_capability = cp->io_capability; BT_DBG("%s IO capability set to 0x%02x", hdev->name, hdev->io_capability); hci_dev_unlock(hdev); return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, 0); } static struct pending_cmd *find_pairing(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; struct pending_cmd *cmd; list_for_each_entry(cmd, &hdev->mgmt_pending, list) { if (cmd->opcode != MGMT_OP_PAIR_DEVICE) continue; if (cmd->user_data != conn) continue; return cmd; } return NULL; } static void pairing_complete(struct pending_cmd *cmd, u8 status) { struct mgmt_rp_pair_device rp; struct hci_conn *conn = cmd->user_data; bacpy(&rp.addr.bdaddr, &conn->dst); rp.addr.type = link_to_bdaddr(conn->type, conn->dst_type); cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status, &rp, sizeof(rp)); /* So we don't get further callbacks for this connection */ conn->connect_cfm_cb = NULL; conn->security_cfm_cb = NULL; conn->disconn_cfm_cb = NULL; hci_conn_put(conn); mgmt_pending_remove(cmd); } static void pairing_complete_cb(struct hci_conn *conn, u8 status) { struct pending_cmd *cmd; BT_DBG("status %u", status); cmd = find_pairing(conn); if (!cmd) BT_DBG("Unable to find a pending command"); else pairing_complete(cmd, mgmt_status(status)); } static void le_connect_complete_cb(struct hci_conn *conn, u8 status) { struct pending_cmd *cmd; BT_DBG("status %u", status); if (!status) return; cmd = find_pairing(conn); if (!cmd) BT_DBG("Unable to find a pending command"); else pairing_complete(cmd, mgmt_status(status)); } static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_pair_device *cp = data; struct mgmt_rp_pair_device rp; struct pending_cmd *cmd; u8 sec_level, auth_type; struct hci_conn *conn; int err; BT_DBG(""); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE, MGMT_STATUS_NOT_POWERED); goto unlock; } sec_level = BT_SECURITY_MEDIUM; if (cp->io_cap == 0x03) auth_type = HCI_AT_DEDICATED_BONDING; else auth_type = HCI_AT_DEDICATED_BONDING_MITM; if (cp->addr.type == BDADDR_BREDR) conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, cp->addr.type, sec_level, auth_type); else conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, cp->addr.type, sec_level, auth_type); memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; if (IS_ERR(conn)) { int status; if (PTR_ERR(conn) == -EBUSY) status = MGMT_STATUS_BUSY; else status = MGMT_STATUS_CONNECT_FAILED; err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, status, &rp, sizeof(rp)); goto unlock; } if (conn->connect_cfm_cb) { hci_conn_put(conn); err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, MGMT_STATUS_BUSY, &rp, sizeof(rp)); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len); if (!cmd) { err = -ENOMEM; hci_conn_put(conn); goto unlock; } /* For LE, just connecting isn't a proof that the pairing finished */ if (cp->addr.type == BDADDR_BREDR) conn->connect_cfm_cb = pairing_complete_cb; else conn->connect_cfm_cb = le_connect_complete_cb; conn->security_cfm_cb = pairing_complete_cb; conn->disconn_cfm_cb = pairing_complete_cb; conn->io_capability = cp->io_cap; cmd->user_data = conn; if (conn->state == BT_CONNECTED && hci_conn_security(conn, sec_level, auth_type)) pairing_complete(cmd, 0); err = 0; unlock: hci_dev_unlock(hdev); return err; } static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_addr_info *addr = data; struct pending_cmd *cmd; struct hci_conn *conn; int err; BT_DBG(""); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, MGMT_STATUS_NOT_POWERED); goto unlock; } cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev); if (!cmd) { err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, MGMT_STATUS_INVALID_PARAMS); goto unlock; } conn = cmd->user_data; if (bacmp(&addr->bdaddr, &conn->dst) != 0) { err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, MGMT_STATUS_INVALID_PARAMS); goto unlock; } pairing_complete(cmd, MGMT_STATUS_CANCELLED); err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr, sizeof(*addr)); unlock: hci_dev_unlock(hdev); return err; } static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, u16 mgmt_op, u16 hci_op, __le32 passkey) { struct pending_cmd *cmd; struct hci_conn *conn; int err; hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, mgmt_op, MGMT_STATUS_NOT_POWERED); goto done; } if (type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); else conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr); if (!conn) { err = cmd_status(sk, hdev->id, mgmt_op, MGMT_STATUS_NOT_CONNECTED); goto done; } if (type == BDADDR_LE_PUBLIC || type == BDADDR_LE_RANDOM) { /* Continue with pairing via SMP */ err = smp_user_confirm_reply(conn, mgmt_op, passkey); if (!err) err = cmd_status(sk, hdev->id, mgmt_op, MGMT_STATUS_SUCCESS); else err = cmd_status(sk, hdev->id, mgmt_op, MGMT_STATUS_FAILED); goto done; } cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr)); if (!cmd) { err = -ENOMEM; goto done; } /* Continue with pairing via HCI */ if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { struct hci_cp_user_passkey_reply cp; bacpy(&cp.bdaddr, bdaddr); cp.passkey = passkey; err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp); } else err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr); if (err < 0) mgmt_pending_remove(cmd); done: hci_dev_unlock(hdev); return err; } static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_pin_code_neg_reply *cp = data; BT_DBG(""); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, MGMT_OP_PIN_CODE_NEG_REPLY, HCI_OP_PIN_CODE_NEG_REPLY, 0); } static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_user_confirm_reply *cp = data; BT_DBG(""); if (len != sizeof(*cp)) return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY, MGMT_STATUS_INVALID_PARAMS); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, MGMT_OP_USER_CONFIRM_REPLY, HCI_OP_USER_CONFIRM_REPLY, 0); } static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_user_confirm_neg_reply *cp = data; BT_DBG(""); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, MGMT_OP_USER_CONFIRM_NEG_REPLY, HCI_OP_USER_CONFIRM_NEG_REPLY, 0); } static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_user_passkey_reply *cp = data; BT_DBG(""); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, MGMT_OP_USER_PASSKEY_REPLY, HCI_OP_USER_PASSKEY_REPLY, cp->passkey); } static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_user_passkey_neg_reply *cp = data; BT_DBG(""); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, MGMT_OP_USER_PASSKEY_NEG_REPLY, HCI_OP_USER_PASSKEY_NEG_REPLY, 0); } static int update_name(struct hci_dev *hdev, const char *name) { struct hci_cp_write_local_name cp; memcpy(cp.name, name, sizeof(cp.name)); return hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); } static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_local_name *cp = data; struct pending_cmd *cmd; int err; BT_DBG(""); hci_dev_lock(hdev); memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); if (!hdev_is_powered(hdev)) { memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, data, len); if (err < 0) goto failed; err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len, sk); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } err = update_name(hdev, cp->name); if (err < 0) mgmt_pending_remove(cmd); failed: hci_dev_unlock(hdev); return err; } static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct pending_cmd *cmd; int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, MGMT_STATUS_NOT_POWERED); goto unlock; } if (!lmp_ssp_capable(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, MGMT_STATUS_NOT_SUPPORTED); goto unlock; } if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, MGMT_STATUS_BUSY); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto unlock; } err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); if (err < 0) mgmt_pending_remove(cmd); unlock: hci_dev_unlock(hdev); return err; } static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_add_remote_oob_data *cp = data; u8 status; int err; BT_DBG("%s ", hdev->name); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, MGMT_STATUS_NOT_POWERED, &cp->addr, sizeof(cp->addr)); goto unlock; } err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, cp->randomizer); if (err < 0) status = MGMT_STATUS_FAILED; else status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); unlock: hci_dev_unlock(hdev); return err; } static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_remove_remote_oob_data *cp = data; u8 status; int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, MGMT_STATUS_NOT_POWERED, &cp->addr, sizeof(cp->addr)); goto unlock; } err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); unlock: hci_dev_unlock(hdev); return err; } int mgmt_interleaved_discovery(struct hci_dev *hdev) { int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE); if (err < 0) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); return err; } static int start_discovery(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_start_discovery *cp = data; struct pending_cmd *cmd; int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_NOT_POWERED); goto failed; } if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_BUSY); goto failed; } if (hdev->discovery.state != DISCOVERY_STOPPED) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_BUSY); goto failed; } cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto failed; } hdev->discovery.type = cp->type; switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: if (lmp_bredr_capable(hdev)) err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); else err = -ENOTSUPP; break; case DISCOV_TYPE_LE: if (lmp_host_le_capable(hdev)) err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); else err = -ENOTSUPP; break; case DISCOV_TYPE_INTERLEAVED: if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev)) err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE); else err = -ENOTSUPP; break; default: err = -EINVAL; } if (err < 0) mgmt_pending_remove(cmd); else hci_discovery_set_state(hdev, DISCOVERY_STARTING); failed: hci_dev_unlock(hdev); return err; } static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_stop_discovery *mgmt_cp = data; struct pending_cmd *cmd; struct hci_cp_remote_name_req_cancel cp; struct inquiry_entry *e; int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); if (!hci_discovery_active(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, MGMT_STATUS_REJECTED, &mgmt_cp->type, sizeof(mgmt_cp->type)); goto unlock; } if (hdev->discovery.type != mgmt_cp->type) { err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, MGMT_STATUS_INVALID_PARAMS, &mgmt_cp->type, sizeof(mgmt_cp->type)); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto unlock; } switch (hdev->discovery.state) { case DISCOVERY_FINDING: if (test_bit(HCI_INQUIRY, &hdev->flags)) err = hci_cancel_inquiry(hdev); else err = hci_cancel_le_scan(hdev); break; case DISCOVERY_RESOLVING: e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING); if (!e) { mgmt_pending_remove(cmd); err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0, &mgmt_cp->type, sizeof(mgmt_cp->type)); hci_discovery_set_state(hdev, DISCOVERY_STOPPED); goto unlock; } bacpy(&cp.bdaddr, &e->data.bdaddr); err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp), &cp); break; default: BT_DBG("unknown discovery state %u", hdev->discovery.state); err = -EFAULT; } if (err < 0) mgmt_pending_remove(cmd); else hci_discovery_set_state(hdev, DISCOVERY_STOPPING); unlock: hci_dev_unlock(hdev); return err; } static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_confirm_name *cp = data; struct inquiry_entry *e; int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); if (!hci_discovery_active(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, MGMT_STATUS_FAILED); goto failed; } e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr); if (!e) { err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, MGMT_STATUS_INVALID_PARAMS); goto failed; } if (cp->name_known) { e->name_state = NAME_KNOWN; list_del(&e->list); } else { e->name_state = NAME_NEEDED; hci_inquiry_cache_update_resolve(hdev, e); } err = 0; failed: hci_dev_unlock(hdev); return err; } static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_block_device *cp = data; u8 status; int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); if (err < 0) status = MGMT_STATUS_FAILED; else status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); hci_dev_unlock(hdev); return err; } static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_unblock_device *cp = data; u8 status; int err; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); hci_dev_unlock(hdev); return err; } static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_device_id *cp = data; int err; __u16 source; BT_DBG("%s", hdev->name); source = __le16_to_cpu(cp->source); if (source > 0x0002) return cmd_status(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, MGMT_STATUS_INVALID_PARAMS); hci_dev_lock(hdev); hdev->devid_source = source; hdev->devid_vendor = __le16_to_cpu(cp->vendor); hdev->devid_product = __le16_to_cpu(cp->product); hdev->devid_version = __le16_to_cpu(cp->version); err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, NULL, 0); update_eir(hdev); hci_dev_unlock(hdev); return err; } static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct hci_cp_write_page_scan_activity acp; u8 type; int err; BT_DBG("%s", hdev->name); if (!hdev_is_powered(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_POWERED); if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_REJECTED); hci_dev_lock(hdev); if (cp->val) { type = PAGE_SCAN_TYPE_INTERLACED; /* 160 msec page scan interval */ acp.interval = __constant_cpu_to_le16(0x0100); } else { type = PAGE_SCAN_TYPE_STANDARD; /* default */ /* default 1.28 sec page scan */ acp.interval = __constant_cpu_to_le16(0x0800); } /* default 11.25 msec page scan window */ acp.window = __constant_cpu_to_le16(0x0012); err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), &acp); if (err < 0) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_FAILED); goto done; } err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); if (err < 0) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_FAILED); goto done; } err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0, NULL, 0); done: hci_dev_unlock(hdev); return err; } static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, void *cp_data, u16 len) { struct mgmt_cp_load_long_term_keys *cp = cp_data; u16 key_count, expected_len; int i; key_count = __le16_to_cpu(cp->key_count); expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_ltk_info); if (expected_len != len) { BT_ERR("load_keys: expected %u bytes, got %u bytes", len, expected_len); return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, EINVAL); } BT_DBG("%s key_count %u", hdev->name, key_count); hci_dev_lock(hdev); hci_smp_ltks_clear(hdev); for (i = 0; i < key_count; i++) { struct mgmt_ltk_info *key = &cp->keys[i]; u8 type; if (key->master) type = HCI_SMP_LTK; else type = HCI_SMP_LTK_SLAVE; hci_add_ltk(hdev, &key->addr.bdaddr, bdaddr_to_le(key->addr.type), type, 0, key->authenticated, key->val, key->enc_size, key->ediv, key->rand); } hci_dev_unlock(hdev); return 0; } static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); bool var_len; size_t data_len; } mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, false, MGMT_READ_VERSION_SIZE }, { read_commands, false, MGMT_READ_COMMANDS_SIZE }, { read_index_list, false, MGMT_READ_INDEX_LIST_SIZE }, { read_controller_info, false, MGMT_READ_INFO_SIZE }, { set_powered, false, MGMT_SETTING_SIZE }, { set_discoverable, false, MGMT_SET_DISCOVERABLE_SIZE }, { set_connectable, false, MGMT_SETTING_SIZE }, { set_fast_connectable, false, MGMT_SETTING_SIZE }, { set_pairable, false, MGMT_SETTING_SIZE }, { set_link_security, false, MGMT_SETTING_SIZE }, { set_ssp, false, MGMT_SETTING_SIZE }, { set_hs, false, MGMT_SETTING_SIZE }, { set_le, false, MGMT_SETTING_SIZE }, { set_dev_class, false, MGMT_SET_DEV_CLASS_SIZE }, { set_local_name, false, MGMT_SET_LOCAL_NAME_SIZE }, { add_uuid, false, MGMT_ADD_UUID_SIZE }, { remove_uuid, false, MGMT_REMOVE_UUID_SIZE }, { load_link_keys, true, MGMT_LOAD_LINK_KEYS_SIZE }, { load_long_term_keys, true, MGMT_LOAD_LONG_TERM_KEYS_SIZE }, { disconnect, false, MGMT_DISCONNECT_SIZE }, { get_connections, false, MGMT_GET_CONNECTIONS_SIZE }, { pin_code_reply, false, MGMT_PIN_CODE_REPLY_SIZE }, { pin_code_neg_reply, false, MGMT_PIN_CODE_NEG_REPLY_SIZE }, { set_io_capability, false, MGMT_SET_IO_CAPABILITY_SIZE }, { pair_device, false, MGMT_PAIR_DEVICE_SIZE }, { cancel_pair_device, false, MGMT_CANCEL_PAIR_DEVICE_SIZE }, { unpair_device, false, MGMT_UNPAIR_DEVICE_SIZE }, { user_confirm_reply, false, MGMT_USER_CONFIRM_REPLY_SIZE }, { user_confirm_neg_reply, false, MGMT_USER_CONFIRM_NEG_REPLY_SIZE }, { user_passkey_reply, false, MGMT_USER_PASSKEY_REPLY_SIZE }, { user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE }, { read_local_oob_data, false, MGMT_READ_LOCAL_OOB_DATA_SIZE }, { add_remote_oob_data, false, MGMT_ADD_REMOTE_OOB_DATA_SIZE }, { remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE }, { start_discovery, false, MGMT_START_DISCOVERY_SIZE }, { stop_discovery, false, MGMT_STOP_DISCOVERY_SIZE }, { confirm_name, false, MGMT_CONFIRM_NAME_SIZE }, { block_device, false, MGMT_BLOCK_DEVICE_SIZE }, { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { void *buf; u8 *cp; struct mgmt_hdr *hdr; u16 opcode, index, len; struct hci_dev *hdev = NULL; const struct mgmt_handler *handler; int err; BT_DBG("got %zu bytes", msglen); if (msglen < sizeof(*hdr)) return -EINVAL; buf = kmalloc(msglen, GFP_KERNEL); if (!buf) return -ENOMEM; if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { err = -EFAULT; goto done; } hdr = buf; opcode = __le16_to_cpu(hdr->opcode); index = __le16_to_cpu(hdr->index); len = __le16_to_cpu(hdr->len); if (len != msglen - sizeof(*hdr)) { err = -EINVAL; goto done; } if (index != MGMT_INDEX_NONE) { hdev = hci_dev_get(index); if (!hdev) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; } } if (opcode >= ARRAY_SIZE(mgmt_handlers) || mgmt_handlers[opcode].func == NULL) { BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, MGMT_STATUS_UNKNOWN_COMMAND); goto done; } if ((hdev && opcode < MGMT_OP_READ_INFO) || (!hdev && opcode >= MGMT_OP_READ_INFO)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; } handler = &mgmt_handlers[opcode]; if ((handler->var_len && len < handler->data_len) || (!handler->var_len && len != handler->data_len)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_PARAMS); goto done; } if (hdev) mgmt_init_hdev(sk, hdev); cp = buf + sizeof(*hdr); err = handler->func(sk, hdev, cp, len); if (err < 0) goto done; err = msglen; done: if (hdev) hci_dev_put(hdev); kfree(buf); return err; } static void cmd_status_rsp(struct pending_cmd *cmd, void *data) { u8 *status = data; cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); mgmt_pending_remove(cmd); } int mgmt_index_added(struct hci_dev *hdev) { if (!mgmt_valid_hdev(hdev)) return -ENOTSUPP; return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); } int mgmt_index_removed(struct hci_dev *hdev) { u8 status = MGMT_STATUS_INVALID_INDEX; if (!mgmt_valid_hdev(hdev)) return -ENOTSUPP; mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); } struct cmd_lookup { struct sock *sk; struct hci_dev *hdev; u8 mgmt_status; }; static void settings_rsp(struct pending_cmd *cmd, void *data) { struct cmd_lookup *match = data; send_settings_rsp(cmd->sk, cmd->opcode, match->hdev); list_del(&cmd->list); if (match->sk == NULL) { match->sk = cmd->sk; sock_hold(match->sk); } mgmt_pending_free(cmd); } int mgmt_powered(struct hci_dev *hdev, u8 powered) { struct cmd_lookup match = { NULL, hdev }; int err; if (!test_bit(HCI_MGMT, &hdev->dev_flags)) return 0; mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); if (powered) { u8 scan = 0; if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) scan |= SCAN_PAGE; if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) scan |= SCAN_INQUIRY; if (scan) hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); update_class(hdev); update_name(hdev, hdev->dev_name); update_eir(hdev); } else { u8 status = MGMT_STATUS_NOT_POWERED; mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); } err = new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); return err; } int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) { struct cmd_lookup match = { NULL, hdev }; bool changed = false; int err = 0; if (discoverable) { if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) changed = true; } else { if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) changed = true; } mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match); if (changed) err = new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); return err; } int mgmt_connectable(struct hci_dev *hdev, u8 connectable) { struct cmd_lookup match = { NULL, hdev }; bool changed = false; int err = 0; if (connectable) { if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags)) changed = true; } else { if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags)) changed = true; } mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, &match); if (changed) err = new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); return err; } int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); if (scan & SCAN_PAGE) mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, cmd_status_rsp, &mgmt_err); if (scan & SCAN_INQUIRY) mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, cmd_status_rsp, &mgmt_err); return 0; } int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent) { struct mgmt_ev_new_link_key ev; memset(&ev, 0, sizeof(ev)); ev.store_hint = persistent; bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = BDADDR_BREDR; ev.key.type = key->type; memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE); ev.key.pin_len = key->pin_len; return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) { struct mgmt_ev_new_long_term_key ev; memset(&ev, 0, sizeof(ev)); ev.store_hint = persistent; bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type); ev.key.authenticated = key->authenticated; ev.key.enc_size = key->enc_size; ev.key.ediv = key->ediv; if (key->type == HCI_SMP_LTK) ev.key.master = 1; memcpy(ev.key.rand, key->rand, sizeof(key->rand)); memcpy(ev.key.val, key->val, sizeof(key->val)); return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL); } int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u32 flags, u8 *name, u8 name_len, u8 *dev_class) { char buf[512]; struct mgmt_ev_device_connected *ev = (void *) buf; u16 eir_len = 0; bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->flags = __cpu_to_le32(flags); if (name_len > 0) eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, name_len); if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0) eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, dev_class, 3); ev->eir_len = cpu_to_le16(eir_len); return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, sizeof(*ev) + eir_len, NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) { struct mgmt_cp_disconnect *cp = cmd->param; struct sock **sk = data; struct mgmt_rp_disconnect rp; bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp, sizeof(rp)); *sk = cmd->sk; sock_hold(*sk); mgmt_pending_remove(cmd); } static void unpair_device_rsp(struct pending_cmd *cmd, void *data) { struct hci_dev *hdev = data; struct mgmt_cp_unpair_device *cp = cmd->param; struct mgmt_rp_unpair_device rp; memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk); cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp)); mgmt_pending_remove(cmd); } int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 reason) { struct mgmt_ev_device_disconnected ev; struct sock *sk = NULL; int err; mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.reason = reason; err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk); if (sk) sock_put(sk); mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, hdev); return err; } int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { struct mgmt_rp_disconnect rp; struct pending_cmd *cmd; int err; cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) return -ENOENT; bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = link_to_bdaddr(link_type, addr_type); err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, hdev); return err; } int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { struct mgmt_ev_connect_failed ev; bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.status = mgmt_status(status); return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); } int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) { struct mgmt_ev_pin_code_request ev; bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = BDADDR_BREDR; ev.secure = secure; return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL); } int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; int err; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); if (!cmd) return -ENOENT; bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = BDADDR_BREDR; err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); return err; } int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; int err; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); if (!cmd) return -ENOENT; bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = BDADDR_BREDR; err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); return err; } int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, __le32 value, u8 confirm_hint) { struct mgmt_ev_user_confirm_request ev; BT_DBG("%s", hdev->name); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.confirm_hint = confirm_hint; ev.value = value; return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), NULL); } int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type) { struct mgmt_ev_user_passkey_request ev; BT_DBG("%s", hdev->name); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(link_type, addr_type); return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev), NULL); } static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status, u8 opcode) { struct pending_cmd *cmd; struct mgmt_rp_user_confirm_reply rp; int err; cmd = mgmt_pending_find(opcode, hdev); if (!cmd) return -ENOENT; bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = link_to_bdaddr(link_type, addr_type); err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); return err; } int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, status, MGMT_OP_USER_CONFIRM_REPLY); } int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, status, MGMT_OP_USER_CONFIRM_NEG_REPLY); } int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, status, MGMT_OP_USER_PASSKEY_REPLY); } int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, status, MGMT_OP_USER_PASSKEY_NEG_REPLY); } int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { struct mgmt_ev_auth_failed ev; bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.status = mgmt_status(status); return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); } int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) { struct cmd_lookup match = { NULL, hdev }; bool changed = false; int err = 0; if (status) { u8 mgmt_err = mgmt_status(status); mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, cmd_status_rsp, &mgmt_err); return 0; } if (test_bit(HCI_AUTH, &hdev->flags)) { if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) changed = true; } else { if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) changed = true; } mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, &match); if (changed) err = new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); return err; } static int clear_eir(struct hci_dev *hdev) { struct hci_cp_write_eir cp; if (!(hdev->features[6] & LMP_EXT_INQ)) return 0; memset(hdev->eir, 0, sizeof(hdev->eir)); memset(&cp, 0, sizeof(cp)); return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) { struct cmd_lookup match = { NULL, hdev }; bool changed = false; int err = 0; if (status) { u8 mgmt_err = mgmt_status(status); if (enable && test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) err = new_settings(hdev, NULL); mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp, &mgmt_err); return err; } if (enable) { if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) changed = true; } else { if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) changed = true; } mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); if (changed) err = new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) update_eir(hdev); else clear_eir(hdev); return err; } static void class_rsp(struct pending_cmd *cmd, void *data) { struct cmd_lookup *match = data; cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status, match->hdev->dev_class, 3); list_del(&cmd->list); if (match->sk == NULL) { match->sk = cmd->sk; sock_hold(match->sk); } mgmt_pending_free(cmd); } int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, u8 status) { struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; int err = 0; clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags); mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, class_rsp, &match); mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, class_rsp, &match); mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match); if (!status) err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3, NULL); if (match.sk) sock_put(match.sk); return err; } int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { struct pending_cmd *cmd; struct mgmt_cp_set_local_name ev; bool changed = false; int err = 0; if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) { memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); changed = true; } memset(&ev, 0, sizeof(ev)); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH); cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); if (!cmd) goto send_event; /* Always assume that either the short or the complete name has * changed if there was a pending mgmt command */ changed = true; if (status) { err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, mgmt_status(status)); goto failed; } err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev, sizeof(ev)); if (err < 0) goto failed; send_event: if (changed) err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); update_eir(hdev); failed: if (cmd) mgmt_pending_remove(cmd); return err; } int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status) { struct pending_cmd *cmd; int err; BT_DBG("%s status %u", hdev->name, status); cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); if (!cmd) return -ENOENT; if (status) { err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, mgmt_status(status)); } else { struct mgmt_rp_read_local_oob_data rp; memcpy(rp.hash, hash, sizeof(rp.hash)); memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp, sizeof(rp)); } mgmt_pending_remove(cmd); return err; } int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) { struct cmd_lookup match = { NULL, hdev }; bool changed = false; int err = 0; if (status) { u8 mgmt_err = mgmt_status(status); if (enable && test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags)) err = new_settings(hdev, NULL); mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp, &mgmt_err); return err; } if (enable) { if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags)) changed = true; } else { if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags)) changed = true; } mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); if (changed) err = new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); return err; } int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len) { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; size_t ev_size; /* Leave 5 bytes for a potential CoD field */ if (sizeof(*ev) + eir_len + 5 > sizeof(buf)) return -EINVAL; memset(buf, 0, sizeof(buf)); bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; if (cfm_name) ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); if (!ssp) ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); if (eir_len > 0) memcpy(ev->eir, eir, eir_len); if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, dev_class, 3); ev->eir_len = cpu_to_le16(eir_len); ev_size = sizeof(*ev) + eir_len; return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); } int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len) { struct mgmt_ev_device_found *ev; char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2]; u16 eir_len; ev = (struct mgmt_ev_device_found *) buf; memset(buf, 0, sizeof(buf)); bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, name_len); ev->eir_len = cpu_to_le16(eir_len); return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, sizeof(*ev) + eir_len, NULL); } int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; u8 type; int err; hci_discovery_set_state(hdev, DISCOVERY_STOPPED); cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); if (!cmd) return -ENOENT; type = hdev->discovery.type; err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), &type, sizeof(type)); mgmt_pending_remove(cmd); return err; } int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; int err; cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); if (!cmd) return -ENOENT; err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), &hdev->discovery.type, sizeof(hdev->discovery.type)); mgmt_pending_remove(cmd); return err; } int mgmt_discovering(struct hci_dev *hdev, u8 discovering) { struct mgmt_ev_discovering ev; struct pending_cmd *cmd; BT_DBG("%s discovering %u", hdev->name, discovering); if (discovering) cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); else cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); if (cmd != NULL) { u8 type = hdev->discovery.type; cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, sizeof(type)); mgmt_pending_remove(cmd); } memset(&ev, 0, sizeof(ev)); ev.type = hdev->discovery.type; ev.discovering = discovering; return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct pending_cmd *cmd; struct mgmt_ev_device_blocked ev; cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = type; return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); } int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct pending_cmd *cmd; struct mgmt_ev_device_unblocked ev; cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = type; return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); } module_param(enable_hs, bool, 0644); MODULE_PARM_DESC(enable_hs, "Enable High Speed support"); compat-drivers-2012-09-18/net/bluetooth/bnep/0000755000175000017500000000000012026211315020077 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/bluetooth/bnep/netdev.c0000644000175000017500000001422512026211315021534 0ustar mcgrofmcgrof/* BNEP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2001-2002 Inventel Systemes Written 2001-2002 by Clément Moreau David Libault Copyright (C) 2002 Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include #include #include "bnep.h" #define BNEP_TX_QUEUE_LEN 20 static int bnep_net_open(struct net_device *dev) { netif_start_queue(dev); return 0; } static int bnep_net_close(struct net_device *dev) { netif_stop_queue(dev); return 0; } static void bnep_net_set_mc_list(struct net_device *dev) { #ifdef CONFIG_BT_BNEP_MC_FILTER struct bnep_session *s = netdev_priv(dev); struct sock *sk = s->sock->sk; struct bnep_set_filter_req *r; struct sk_buff *skb; int size; BT_DBG("%s mc_count %d", dev->name, netdev_mc_count(dev)); size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2; skb = alloc_skb(size, GFP_ATOMIC); if (!skb) { BT_ERR("%s Multicast list allocation failed", dev->name); return; } r = (void *) skb->data; __skb_put(skb, sizeof(*r)); r->type = BNEP_CONTROL; r->ctrl = BNEP_FILTER_MULTI_ADDR_SET; if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { u8 start[ETH_ALEN] = { 0x01 }; /* Request all addresses */ memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN); memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); r->len = htons(ETH_ALEN * 2); } else { struct netdev_hw_addr *ha; int i, len = skb->len; if (dev->flags & IFF_BROADCAST) { memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); } /* FIXME: We should group addresses here. */ i = 0; netdev_for_each_mc_addr(ha, dev) { if (i == BNEP_MAX_MULTICAST_FILTERS) break; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); #else memcpy(__skb_put(skb, ETH_ALEN), ha->dmi_addr, ETH_ALEN); memcpy(__skb_put(skb, ETH_ALEN), ha->dmi_addr, ETH_ALEN); #endif i++; } r->len = htons(skb->len - len); } skb_queue_tail(&sk->sk_write_queue, skb); wake_up_interruptible(sk_sleep(sk)); #endif } static int bnep_net_set_mac_addr(struct net_device *dev, void *arg) { BT_DBG("%s", dev->name); return 0; } static void bnep_net_timeout(struct net_device *dev) { BT_DBG("net_timeout"); netif_wake_queue(dev); } #ifdef CONFIG_BT_BNEP_MC_FILTER static int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s) { struct ethhdr *eh = (void *) skb->data; if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), (ulong *) &s->mc_filter)) return 1; return 0; } #endif #ifdef CONFIG_BT_BNEP_PROTO_FILTER /* Determine ether protocol. Based on eth_type_trans. */ static u16 bnep_net_eth_proto(struct sk_buff *skb) { struct ethhdr *eh = (void *) skb->data; u16 proto = ntohs(eh->h_proto); if (proto >= 1536) return proto; if (get_unaligned((__be16 *) skb->data) == htons(0xFFFF)) return ETH_P_802_3; return ETH_P_802_2; } static int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s) { u16 proto = bnep_net_eth_proto(skb); struct bnep_proto_filter *f = s->proto_filter; int i; for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) { if (proto >= f[i].start && proto <= f[i].end) return 0; } BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto); return 1; } #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static netdev_tx_t bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) #else static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) #endif { struct bnep_session *s = netdev_priv(dev); struct sock *sk = s->sock->sk; BT_DBG("skb %p, dev %p", skb, dev); #ifdef CONFIG_BT_BNEP_MC_FILTER if (bnep_net_mc_filter(skb, s)) { kfree_skb(skb); return NETDEV_TX_OK; } #endif #ifdef CONFIG_BT_BNEP_PROTO_FILTER if (bnep_net_proto_filter(skb, s)) { kfree_skb(skb); return NETDEV_TX_OK; } #endif /* * We cannot send L2CAP packets from here as we are potentially in a bh. * So we have to queue them and wake up session thread which is sleeping * on the sk_sleep(sk). */ dev->trans_start = jiffies; skb_queue_tail(&sk->sk_write_queue, skb); wake_up_interruptible(sk_sleep(sk)); if (skb_queue_len(&sk->sk_write_queue) >= BNEP_TX_QUEUE_LEN) { BT_DBG("tx queue is full"); /* Stop queuing. * Session thread will do netif_wake_queue() */ netif_stop_queue(dev); } return NETDEV_TX_OK; } static const struct net_device_ops bnep_netdev_ops = { .ndo_open = bnep_net_open, .ndo_stop = bnep_net_close, .ndo_start_xmit = bnep_net_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = bnep_net_set_mc_list, .ndo_set_mac_address = bnep_net_set_mac_addr, .ndo_tx_timeout = bnep_net_timeout, .ndo_change_mtu = eth_change_mtu, }; void bnep_net_setup(struct net_device *dev) { memset(dev->broadcast, 0xff, ETH_ALEN); dev->addr_len = ETH_ALEN; ether_setup(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; netdev_attach_ops(dev, &bnep_netdev_ops); dev->watchdog_timeo = HZ * 2; } compat-drivers-2012-09-18/net/bluetooth/bnep/sock.c0000644000175000017500000001347212026211315021211 0ustar mcgrofmcgrof/* BNEP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2001-2002 Inventel Systemes Written 2001-2002 by David Libault Copyright (C) 2002 Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include "bnep.h" static struct bt_sock_list bnep_sk_list = { .lock = __RW_LOCK_UNLOCKED(bnep_sk_list.lock) }; static int bnep_sock_release(struct socket *sock) { struct sock *sk = sock->sk; BT_DBG("sock %p sk %p", sock, sk); if (!sk) return 0; bt_sock_unlink(&bnep_sk_list, sk); sock_orphan(sk); sock_put(sk); return 0; } static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct bnep_connlist_req cl; struct bnep_connadd_req ca; struct bnep_conndel_req cd; struct bnep_conninfo ci; struct socket *nsock; void __user *argp = (void __user *)arg; int err; BT_DBG("cmd %x arg %lx", cmd, arg); switch (cmd) { case BNEPCONNADD: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (copy_from_user(&ca, argp, sizeof(ca))) return -EFAULT; nsock = sockfd_lookup(ca.sock, &err); if (!nsock) return err; if (nsock->sk->sk_state != BT_CONNECTED) { sockfd_put(nsock); return -EBADFD; } ca.device[sizeof(ca.device)-1] = 0; err = bnep_add_connection(&ca, nsock); if (!err) { if (copy_to_user(argp, &ca, sizeof(ca))) err = -EFAULT; } else sockfd_put(nsock); return err; case BNEPCONNDEL: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (copy_from_user(&cd, argp, sizeof(cd))) return -EFAULT; return bnep_del_connection(&cd); case BNEPGETCONNLIST: if (copy_from_user(&cl, argp, sizeof(cl))) return -EFAULT; if (cl.cnum <= 0) return -EINVAL; err = bnep_get_connlist(&cl); if (!err && copy_to_user(argp, &cl, sizeof(cl))) return -EFAULT; return err; case BNEPGETCONNINFO: if (copy_from_user(&ci, argp, sizeof(ci))) return -EFAULT; err = bnep_get_conninfo(&ci); if (!err && copy_to_user(argp, &ci, sizeof(ci))) return -EFAULT; return err; default: return -EINVAL; } return 0; } #ifdef CONFIG_COMPAT static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { if (cmd == BNEPGETCONNLIST) { struct bnep_connlist_req cl; u32 uci; int err; if (get_user(cl.cnum, (u32 __user *) arg) || get_user(uci, (u32 __user *) (arg + 4))) return -EFAULT; cl.ci = compat_ptr(uci); if (cl.cnum <= 0) return -EINVAL; err = bnep_get_connlist(&cl); if (!err && put_user(cl.cnum, (u32 __user *) arg)) err = -EFAULT; return err; } return bnep_sock_ioctl(sock, cmd, arg); } #endif static const struct proto_ops bnep_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = bnep_sock_release, .ioctl = bnep_sock_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = bnep_sock_compat_ioctl, #endif .bind = sock_no_bind, .getname = sock_no_getname, .sendmsg = sock_no_sendmsg, .recvmsg = sock_no_recvmsg, .poll = sock_no_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .mmap = sock_no_mmap }; static struct proto bnep_proto = { .name = "BNEP", .owner = THIS_MODULE, .obj_size = sizeof(struct bt_sock) }; #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int bnep_sock_create(struct net *net, struct socket *sock, int protocol, int kern) #else static int bnep_sock_create(struct net *net, struct socket *sock, int protocol) #endif { struct sock *sk; BT_DBG("sock %p", sock); if (sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto); if (!sk) return -ENOMEM; sock_init_data(sock, sk); sock->ops = &bnep_sock_ops; sock->state = SS_UNCONNECTED; sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = protocol; sk->sk_state = BT_OPEN; bt_sock_link(&bnep_sk_list, sk); return 0; } static const struct net_proto_family bnep_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = bnep_sock_create }; int __init bnep_sock_init(void) { int err; err = proto_register(&bnep_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops); if (err < 0) { BT_ERR("Can't register BNEP socket"); goto error; } err = bt_procfs_init(THIS_MODULE, &init_net, "bnep", &bnep_sk_list, NULL); if (err < 0) { BT_ERR("Failed to create BNEP proc file"); bt_sock_unregister(BTPROTO_BNEP); goto error; } BT_INFO("BNEP socket layer initialized"); return 0; error: proto_unregister(&bnep_proto); return err; } void __exit bnep_sock_cleanup(void) { bt_procfs_cleanup(&init_net, "bnep"); if (bt_sock_unregister(BTPROTO_BNEP) < 0) BT_ERR("Can't unregister BNEP socket"); proto_unregister(&bnep_proto); } compat-drivers-2012-09-18/net/bluetooth/bnep/core.c0000644000175000017500000003704112026211315021200 0ustar mcgrofmcgrof/* BNEP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2001-2002 Inventel Systemes Written 2001-2002 by Clément Moreau David Libault Copyright (C) 2002 Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include #include #include #include #include #include "bnep.h" #define VERSION "1.3" static bool compress_src = true; static bool compress_dst = true; static LIST_HEAD(bnep_session_list); static DECLARE_RWSEM(bnep_session_sem); static struct bnep_session *__bnep_get_session(u8 *dst) { struct bnep_session *s; BT_DBG(""); list_for_each_entry(s, &bnep_session_list, list) if (ether_addr_equal(dst, s->eh.h_source)) return s; return NULL; } static void __bnep_link_session(struct bnep_session *s) { list_add(&s->list, &bnep_session_list); } static void __bnep_unlink_session(struct bnep_session *s) { list_del(&s->list); } static int bnep_send(struct bnep_session *s, void *data, size_t len) { struct socket *sock = s->sock; struct kvec iv = { data, len }; return kernel_sendmsg(sock, &s->msg, &iv, 1, len); } static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp) { struct bnep_control_rsp rsp; rsp.type = BNEP_CONTROL; rsp.ctrl = ctrl; rsp.resp = htons(resp); return bnep_send(s, &rsp, sizeof(rsp)); } #ifdef CONFIG_BT_BNEP_PROTO_FILTER static inline void bnep_set_default_proto_filter(struct bnep_session *s) { /* (IPv4, ARP) */ s->proto_filter[0].start = ETH_P_IP; s->proto_filter[0].end = ETH_P_ARP; /* (RARP, AppleTalk) */ s->proto_filter[1].start = ETH_P_RARP; s->proto_filter[1].end = ETH_P_AARP; /* (IPX, IPv6) */ s->proto_filter[2].start = ETH_P_IPX; s->proto_filter[2].end = ETH_P_IPV6; } #endif static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len) { int n; if (len < 2) return -EILSEQ; n = get_unaligned_be16(data); data++; len -= 2; if (len < n) return -EILSEQ; BT_DBG("filter len %d", n); #ifdef CONFIG_BT_BNEP_PROTO_FILTER n /= 4; if (n <= BNEP_MAX_PROTO_FILTERS) { struct bnep_proto_filter *f = s->proto_filter; int i; for (i = 0; i < n; i++) { f[i].start = get_unaligned_be16(data++); f[i].end = get_unaligned_be16(data++); BT_DBG("proto filter start %d end %d", f[i].start, f[i].end); } if (i < BNEP_MAX_PROTO_FILTERS) memset(f + i, 0, sizeof(*f)); if (n == 0) bnep_set_default_proto_filter(s); bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS); } else { bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED); } #else bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ); #endif return 0; } static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) { int n; if (len < 2) return -EILSEQ; n = get_unaligned_be16(data); data += 2; len -= 2; if (len < n) return -EILSEQ; BT_DBG("filter len %d", n); #ifdef CONFIG_BT_BNEP_MC_FILTER n /= (ETH_ALEN * 2); if (n > 0) { int i; s->mc_filter = 0; /* Always send broadcast */ set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter); /* Add address ranges to the multicast hash */ for (; n > 0; n--) { u8 a1[6], *a2; memcpy(a1, data, ETH_ALEN); data += ETH_ALEN; a2 = data; data += ETH_ALEN; BT_DBG("mc filter %s -> %s", batostr((void *) a1), batostr((void *) a2)); /* Iterate from a1 to a2 */ set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) { /* Increment a1 */ i = 5; while (i >= 0 && ++a1[i--] == 0) ; set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); } } } BT_DBG("mc filter hash 0x%llx", s->mc_filter); bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS); #else bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ); #endif return 0; } static int bnep_rx_control(struct bnep_session *s, void *data, int len) { u8 cmd = *(u8 *)data; int err = 0; data++; len--; switch (cmd) { case BNEP_CMD_NOT_UNDERSTOOD: case BNEP_SETUP_CONN_RSP: case BNEP_FILTER_NET_TYPE_RSP: case BNEP_FILTER_MULTI_ADDR_RSP: /* Ignore these for now */ break; case BNEP_FILTER_NET_TYPE_SET: err = bnep_ctrl_set_netfilter(s, data, len); break; case BNEP_FILTER_MULTI_ADDR_SET: err = bnep_ctrl_set_mcfilter(s, data, len); break; case BNEP_SETUP_CONN_REQ: err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED); break; default: { u8 pkt[3]; pkt[0] = BNEP_CONTROL; pkt[1] = BNEP_CMD_NOT_UNDERSTOOD; pkt[2] = cmd; bnep_send(s, pkt, sizeof(pkt)); } break; } return err; } static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb) { struct bnep_ext_hdr *h; int err = 0; do { h = (void *) skb->data; if (!skb_pull(skb, sizeof(*h))) { err = -EILSEQ; break; } BT_DBG("type 0x%x len %d", h->type, h->len); switch (h->type & BNEP_TYPE_MASK) { case BNEP_EXT_CONTROL: bnep_rx_control(s, skb->data, skb->len); break; default: /* Unknown extension, skip it. */ break; } if (!skb_pull(skb, h->len)) { err = -EILSEQ; break; } } while (!err && (h->type & BNEP_EXT_HEADER)); return err; } static u8 __bnep_rx_hlen[] = { ETH_HLEN, /* BNEP_GENERAL */ 0, /* BNEP_CONTROL */ 2, /* BNEP_COMPRESSED */ ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */ ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */ }; static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) { struct net_device *dev = s->dev; struct sk_buff *nskb; u8 type; dev->stats.rx_bytes += skb->len; type = *(u8 *) skb->data; skb_pull(skb, 1); if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen)) goto badframe; if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { bnep_rx_control(s, skb->data, skb->len); kfree_skb(skb); return 0; } skb_reset_mac_header(skb); /* Verify and pull out header */ if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) goto badframe; s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); if (type & BNEP_EXT_HEADER) { if (bnep_rx_extension(s, skb) < 0) goto badframe; } /* Strip 802.1p header */ if (ntohs(s->eh.h_proto) == ETH_P_8021Q) { if (!skb_pull(skb, 4)) goto badframe; s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); } /* We have to alloc new skb and copy data here :(. Because original skb * may not be modified and because of the alignment requirements. */ nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL); if (!nskb) { dev->stats.rx_dropped++; kfree_skb(skb); return -ENOMEM; } skb_reserve(nskb, 2); /* Decompress header and construct ether frame */ switch (type & BNEP_TYPE_MASK) { case BNEP_COMPRESSED: memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN); break; case BNEP_COMPRESSED_SRC_ONLY: memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN); memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN); put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); break; case BNEP_COMPRESSED_DST_ONLY: memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN); memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2); break; case BNEP_GENERAL: memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb), ETH_ALEN * 2); put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); break; } skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len); kfree_skb(skb); dev->stats.rx_packets++; nskb->ip_summed = CHECKSUM_NONE; nskb->protocol = eth_type_trans(nskb, dev); netif_rx_ni(nskb); return 0; badframe: dev->stats.rx_errors++; kfree_skb(skb); return 0; } static u8 __bnep_tx_types[] = { BNEP_GENERAL, BNEP_COMPRESSED_SRC_ONLY, BNEP_COMPRESSED_DST_ONLY, BNEP_COMPRESSED }; static int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb) { struct ethhdr *eh = (void *) skb->data; struct socket *sock = s->sock; struct kvec iv[3]; int len = 0, il = 0; u8 type = 0; BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type); if (!skb->dev) { /* Control frame sent by us */ goto send; } iv[il++] = (struct kvec) { &type, 1 }; len++; if (compress_src && ether_addr_equal(eh->h_dest, s->eh.h_source)) type |= 0x01; if (compress_dst && ether_addr_equal(eh->h_source, s->eh.h_dest)) type |= 0x02; if (type) skb_pull(skb, ETH_ALEN * 2); type = __bnep_tx_types[type]; switch (type) { case BNEP_COMPRESSED_SRC_ONLY: iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN }; len += ETH_ALEN; break; case BNEP_COMPRESSED_DST_ONLY: iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN }; len += ETH_ALEN; break; } send: iv[il++] = (struct kvec) { skb->data, skb->len }; len += skb->len; /* FIXME: linearize skb */ { len = kernel_sendmsg(sock, &s->msg, iv, il, len); } kfree_skb(skb); if (len > 0) { s->dev->stats.tx_bytes += len; s->dev->stats.tx_packets++; return 0; } return len; } static int bnep_session(void *arg) { struct bnep_session *s = arg; struct net_device *dev = s->dev; struct sock *sk = s->sock->sk; struct sk_buff *skb; wait_queue_t wait; BT_DBG(""); set_user_nice(current, -15); init_waitqueue_entry(&wait, current); add_wait_queue(sk_sleep(sk), &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (atomic_read(&s->terminate)) break; /* RX */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) bnep_rx_frame(s, skb); else kfree_skb(skb); } if (sk->sk_state != BT_CONNECTED) break; /* TX */ while ((skb = skb_dequeue(&sk->sk_write_queue))) if (bnep_tx_frame(s, skb)) break; netif_wake_queue(dev); schedule(); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); /* Cleanup session */ down_write(&bnep_session_sem); /* Delete network device */ unregister_netdev(dev); /* Wakeup user-space polling for socket errors */ s->sock->sk->sk_err = EUNATCH; wake_up_interruptible(sk_sleep(s->sock->sk)); /* Release the socket */ fput(s->sock->file); __bnep_unlink_session(s); up_write(&bnep_session_sem); free_netdev(dev); module_put_and_exit(0); return 0; } static struct device *bnep_get_device(struct bnep_session *session) { bdaddr_t *src = &bt_sk(session->sock->sk)->src; bdaddr_t *dst = &bt_sk(session->sock->sk)->dst; struct hci_dev *hdev; struct hci_conn *conn; hdev = hci_get_route(dst, src); if (!hdev) return NULL; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); hci_dev_put(hdev); return conn ? &conn->dev : NULL; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type bnep_type = { .name = "bluetooth", }; #endif int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) { struct net_device *dev; struct bnep_session *s, *ss; u8 dst[ETH_ALEN], src[ETH_ALEN]; int err; BT_DBG(""); baswap((void *) dst, &bt_sk(sock->sk)->dst); baswap((void *) src, &bt_sk(sock->sk)->src); /* session struct allocated as private part of net_device */ dev = alloc_netdev(sizeof(struct bnep_session), (*req->device) ? req->device : "bnep%d", bnep_net_setup); if (!dev) return -ENOMEM; down_write(&bnep_session_sem); ss = __bnep_get_session(dst); if (ss && ss->state == BT_CONNECTED) { err = -EEXIST; goto failed; } s = netdev_priv(dev); /* This is rx header therefore addresses are swapped. * ie. eh.h_dest is our local address. */ memcpy(s->eh.h_dest, &src, ETH_ALEN); memcpy(s->eh.h_source, &dst, ETH_ALEN); memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN); s->dev = dev; s->sock = sock; s->role = req->role; s->state = BT_CONNECTED; s->msg.msg_flags = MSG_NOSIGNAL; #ifdef CONFIG_BT_BNEP_MC_FILTER /* Set default mc filter */ set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter); #endif #ifdef CONFIG_BT_BNEP_PROTO_FILTER /* Set default protocol filter */ bnep_set_default_proto_filter(s); #endif SET_NETDEV_DEV(dev, bnep_get_device(s)); SET_NETDEV_DEVTYPE(dev, &bnep_type); err = register_netdev(dev); if (err) goto failed; __bnep_link_session(s); __module_get(THIS_MODULE); s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name); if (IS_ERR(s->task)) { /* Session thread start failed, gotta cleanup. */ module_put(THIS_MODULE); unregister_netdev(dev); __bnep_unlink_session(s); err = PTR_ERR(s->task); goto failed; } up_write(&bnep_session_sem); strcpy(req->device, dev->name); return 0; failed: up_write(&bnep_session_sem); free_netdev(dev); return err; } int bnep_del_connection(struct bnep_conndel_req *req) { struct bnep_session *s; int err = 0; BT_DBG(""); down_read(&bnep_session_sem); s = __bnep_get_session(req->dst); if (s) { atomic_inc(&s->terminate); wake_up_process(s->task); } else err = -ENOENT; up_read(&bnep_session_sem); return err; } static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) { memset(ci, 0, sizeof(*ci)); memcpy(ci->dst, s->eh.h_source, ETH_ALEN); strcpy(ci->device, s->dev->name); ci->flags = s->flags; ci->state = s->state; ci->role = s->role; } int bnep_get_connlist(struct bnep_connlist_req *req) { struct bnep_session *s; int err = 0, n = 0; down_read(&bnep_session_sem); list_for_each_entry(s, &bnep_session_list, list) { struct bnep_conninfo ci; __bnep_copy_ci(&ci, s); if (copy_to_user(req->ci, &ci, sizeof(ci))) { err = -EFAULT; break; } if (++n >= req->cnum) break; req->ci++; } req->cnum = n; up_read(&bnep_session_sem); return err; } int bnep_get_conninfo(struct bnep_conninfo *ci) { struct bnep_session *s; int err = 0; down_read(&bnep_session_sem); s = __bnep_get_session(ci->dst); if (s) __bnep_copy_ci(ci, s); else err = -ENOENT; up_read(&bnep_session_sem); return err; } static int __init bnep_init(void) { char flt[50] = ""; #ifdef CONFIG_BT_BNEP_PROTO_FILTER strcat(flt, "protocol "); #endif #ifdef CONFIG_BT_BNEP_MC_FILTER strcat(flt, "multicast"); #endif BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION); if (flt[0]) BT_INFO("BNEP filters: %s", flt); bnep_sock_init(); return 0; } static void __exit bnep_exit(void) { bnep_sock_cleanup(); } module_init(bnep_init); module_exit(bnep_exit); module_param(compress_src, bool, 0644); MODULE_PARM_DESC(compress_src, "Compress sources headers"); module_param(compress_dst, bool, 0644); MODULE_PARM_DESC(compress_dst, "Compress destination headers"); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("bt-proto-4"); compat-drivers-2012-09-18/net/bluetooth/bnep/bnep.h0000644000175000017500000001006012026211315021171 0ustar mcgrofmcgrof/* BNEP protocol definition for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _BNEP_H #define _BNEP_H #include #include #include /* Limits */ #define BNEP_MAX_PROTO_FILTERS 5 #define BNEP_MAX_MULTICAST_FILTERS 20 /* UUIDs */ #define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB #define BNEP_UUID16 0x02 #define BNEP_UUID32 0x04 #define BNEP_UUID128 0x16 #define BNEP_SVC_PANU 0x1115 #define BNEP_SVC_NAP 0x1116 #define BNEP_SVC_GN 0x1117 /* Packet types */ #define BNEP_GENERAL 0x00 #define BNEP_CONTROL 0x01 #define BNEP_COMPRESSED 0x02 #define BNEP_COMPRESSED_SRC_ONLY 0x03 #define BNEP_COMPRESSED_DST_ONLY 0x04 /* Control types */ #define BNEP_CMD_NOT_UNDERSTOOD 0x00 #define BNEP_SETUP_CONN_REQ 0x01 #define BNEP_SETUP_CONN_RSP 0x02 #define BNEP_FILTER_NET_TYPE_SET 0x03 #define BNEP_FILTER_NET_TYPE_RSP 0x04 #define BNEP_FILTER_MULTI_ADDR_SET 0x05 #define BNEP_FILTER_MULTI_ADDR_RSP 0x06 /* Extension types */ #define BNEP_EXT_CONTROL 0x00 /* Response messages */ #define BNEP_SUCCESS 0x00 #define BNEP_CONN_INVALID_DST 0x01 #define BNEP_CONN_INVALID_SRC 0x02 #define BNEP_CONN_INVALID_SVC 0x03 #define BNEP_CONN_NOT_ALLOWED 0x04 #define BNEP_FILTER_UNSUPPORTED_REQ 0x01 #define BNEP_FILTER_INVALID_RANGE 0x02 #define BNEP_FILTER_INVALID_MCADDR 0x02 #define BNEP_FILTER_LIMIT_REACHED 0x03 #define BNEP_FILTER_DENIED_SECURITY 0x04 /* L2CAP settings */ #define BNEP_MTU 1691 #define BNEP_PSM 0x0f #define BNEP_FLUSH_TO 0xffff #define BNEP_CONNECT_TO 15 #define BNEP_FILTER_TO 15 /* Headers */ #define BNEP_TYPE_MASK 0x7f #define BNEP_EXT_HEADER 0x80 struct bnep_setup_conn_req { __u8 type; __u8 ctrl; __u8 uuid_size; __u8 service[0]; } __packed; struct bnep_set_filter_req { __u8 type; __u8 ctrl; __be16 len; __u8 list[0]; } __packed; struct bnep_control_rsp { __u8 type; __u8 ctrl; __be16 resp; } __packed; struct bnep_ext_hdr { __u8 type; __u8 len; __u8 data[0]; } __packed; /* BNEP ioctl defines */ #define BNEPCONNADD _IOW('B', 200, int) #define BNEPCONNDEL _IOW('B', 201, int) #define BNEPGETCONNLIST _IOR('B', 210, int) #define BNEPGETCONNINFO _IOR('B', 211, int) struct bnep_connadd_req { int sock; /* Connected socket */ __u32 flags; __u16 role; char device[16]; /* Name of the Ethernet device */ }; struct bnep_conndel_req { __u32 flags; __u8 dst[ETH_ALEN]; }; struct bnep_conninfo { __u32 flags; __u16 role; __u16 state; __u8 dst[ETH_ALEN]; char device[16]; }; struct bnep_connlist_req { __u32 cnum; struct bnep_conninfo __user *ci; }; struct bnep_proto_filter { __u16 start; __u16 end; }; int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock); int bnep_del_connection(struct bnep_conndel_req *req); int bnep_get_connlist(struct bnep_connlist_req *req); int bnep_get_conninfo(struct bnep_conninfo *ci); /* BNEP sessions */ struct bnep_session { struct list_head list; unsigned int role; unsigned long state; unsigned long flags; atomic_t terminate; struct task_struct *task; struct ethhdr eh; struct msghdr msg; struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS]; unsigned long long mc_filter; struct socket *sock; struct net_device *dev; }; void bnep_net_setup(struct net_device *dev); int bnep_sock_init(void); void bnep_sock_cleanup(void); static inline int bnep_mc_hash(__u8 *addr) { return crc32_be(~0, addr, ETH_ALEN) >> 26; } #endif compat-drivers-2012-09-18/net/bluetooth/bnep/Makefile0000644000175000017500000000017112026211315021536 0ustar mcgrofmcgrof# # Makefile for the Linux Bluetooth BNEP layer. # obj-$(CONFIG_BT_BNEP) += bnep.o bnep-objs := core.o sock.o netdev.o compat-drivers-2012-09-18/net/bluetooth/bnep/Kconfig0000644000175000017500000000120612026211315021401 0ustar mcgrofmcgrofconfig BT_BNEP tristate "BNEP protocol support" depends on BT select CRC32 help BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet emulation layer on top of Bluetooth. BNEP is required for Bluetooth PAN (Personal Area Network). Say Y here to compile BNEP support into the kernel or say M to compile it as module (bnep). config BT_BNEP_MC_FILTER bool "Multicast filter support" depends on BT_BNEP help This option enables the multicast filter support for BNEP. config BT_BNEP_PROTO_FILTER bool "Protocol filter support" depends on BT_BNEP help This option enables the protocol filter support for BNEP. compat-drivers-2012-09-18/net/bluetooth/cmtp/0000755000175000017500000000000012026211315020116 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/bluetooth/cmtp/capi.c0000644000175000017500000004132312026211315021201 0ustar mcgrofmcgrof/* CMTP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002-2003 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmtp.h" #define CAPI_INTEROPERABILITY 0x20 #define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) #define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) #define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) #define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) #define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) #define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) #define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) #define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) #define CAPI_FUNCTION_REGISTER 0 #define CAPI_FUNCTION_RELEASE 1 #define CAPI_FUNCTION_GET_PROFILE 2 #define CAPI_FUNCTION_GET_MANUFACTURER 3 #define CAPI_FUNCTION_GET_VERSION 4 #define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 #define CAPI_FUNCTION_MANUFACTURER 6 #define CAPI_FUNCTION_LOOPBACK 7 #define CMTP_MSGNUM 1 #define CMTP_APPLID 2 #define CMTP_MAPPING 3 static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) { struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL); BT_DBG("session %p application %p appl %d", session, app, appl); if (!app) return NULL; app->state = BT_OPEN; app->appl = appl; list_add_tail(&app->list, &session->applications); return app; } static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) { BT_DBG("session %p application %p", session, app); if (app) { list_del(&app->list); kfree(app); } } static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) { struct cmtp_application *app; struct list_head *p, *n; list_for_each_safe(p, n, &session->applications) { app = list_entry(p, struct cmtp_application, list); switch (pattern) { case CMTP_MSGNUM: if (app->msgnum == value) return app; break; case CMTP_APPLID: if (app->appl == value) return app; break; case CMTP_MAPPING: if (app->mapping == value) return app; break; } } return NULL; } static int cmtp_msgnum_get(struct cmtp_session *session) { session->msgnum++; if ((session->msgnum & 0xff) > 200) session->msgnum = CMTP_INITIAL_MSGNUM + 1; return session->msgnum; } static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) { struct cmtp_scb *scb = (void *) skb->cb; BT_DBG("session %p skb %p len %d", session, skb, skb->len); scb->id = -1; scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); skb_queue_tail(&session->transmit, skb); wake_up_interruptible(sk_sleep(session->sock->sk)); } static void cmtp_send_interopmsg(struct cmtp_session *session, __u8 subcmd, __u16 appl, __u16 msgnum, __u16 function, unsigned char *buf, int len) { struct sk_buff *skb; unsigned char *s; BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC); if (!skb) { BT_ERR("Can't allocate memory for interoperability packet"); return; } s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); capimsg_setu16(s, 2, appl); capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); capimsg_setu8 (s, 5, subcmd); capimsg_setu16(s, 6, msgnum); /* Interoperability selector (Bluetooth Device Management) */ capimsg_setu16(s, 8, 0x0001); capimsg_setu8 (s, 10, 3 + len); capimsg_setu16(s, 11, function); capimsg_setu8 (s, 13, len); if (len > 0) memcpy(s + 14, buf, len); cmtp_send_capimsg(session, skb); } static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) { struct capi_ctr *ctrl = &session->ctrl; struct cmtp_application *application; __u16 appl, msgnum, func, info; __u32 controller; BT_DBG("session %p skb %p len %d", session, skb, skb->len); switch (CAPIMSG_SUBCOMMAND(skb->data)) { case CAPI_CONF: if (skb->len < CAPI_MSG_BASELEN + 10) break; func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); switch (func) { case CAPI_FUNCTION_REGISTER: msgnum = CAPIMSG_MSGID(skb->data); application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); if (application) { application->state = BT_CONNECTED; application->msgnum = 0; application->mapping = CAPIMSG_APPID(skb->data); wake_up_interruptible(&session->wait); } break; case CAPI_FUNCTION_RELEASE: appl = CAPIMSG_APPID(skb->data); application = cmtp_application_get(session, CMTP_MAPPING, appl); if (application) { application->state = BT_CLOSED; application->msgnum = 0; wake_up_interruptible(&session->wait); } break; case CAPI_FUNCTION_GET_PROFILE: if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile)) break; controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); msgnum = CAPIMSG_MSGID(skb->data); if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { session->ncontroller = controller; wake_up_interruptible(&session->wait); break; } if (!info && ctrl) { memcpy(&ctrl->profile, skb->data + CAPI_MSG_BASELEN + 11, sizeof(capi_profile)); session->state = BT_CONNECTED; capi_ctr_ready(ctrl); } break; case CAPI_FUNCTION_GET_MANUFACTURER: if (skb->len < CAPI_MSG_BASELEN + 15) break; controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10); if (!info && ctrl) { int len = min_t(uint, CAPI_MANUFACTURER_LEN, skb->data[CAPI_MSG_BASELEN + 14]); memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN); strncpy(ctrl->manu, skb->data + CAPI_MSG_BASELEN + 15, len); } break; case CAPI_FUNCTION_GET_VERSION: if (skb->len < CAPI_MSG_BASELEN + 32) break; controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); if (!info && ctrl) { ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); } break; case CAPI_FUNCTION_GET_SERIAL_NUMBER: if (skb->len < CAPI_MSG_BASELEN + 17) break; controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); if (!info && ctrl) { int len = min_t(uint, CAPI_SERIAL_LEN, skb->data[CAPI_MSG_BASELEN + 16]); memset(ctrl->serial, 0, CAPI_SERIAL_LEN); strncpy(ctrl->serial, skb->data + CAPI_MSG_BASELEN + 17, len); } break; } break; case CAPI_IND: if (skb->len < CAPI_MSG_BASELEN + 6) break; func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); if (func == CAPI_FUNCTION_LOOPBACK) { int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6, skb->data[CAPI_MSG_BASELEN + 5]); appl = CAPIMSG_APPID(skb->data); msgnum = CAPIMSG_MSGID(skb->data); cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, skb->data + CAPI_MSG_BASELEN + 6, len); } break; } kfree_skb(skb); } void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) { struct capi_ctr *ctrl = &session->ctrl; struct cmtp_application *application; __u16 appl; __u32 contr; BT_DBG("session %p skb %p len %d", session, skb, skb->len); if (skb->len < CAPI_MSG_BASELEN) return; if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { cmtp_recv_interopmsg(session, skb); return; } if (session->flags & (1 << CMTP_LOOPBACK)) { kfree_skb(skb); return; } appl = CAPIMSG_APPID(skb->data); contr = CAPIMSG_CONTROL(skb->data); application = cmtp_application_get(session, CMTP_MAPPING, appl); if (application) { appl = application->appl; CAPIMSG_SETAPPID(skb->data, appl); } else { BT_ERR("Can't find application with id %d", appl); kfree_skb(skb); return; } if ((contr & 0x7f) == 0x01) { contr = (contr & 0xffffff80) | session->num; CAPIMSG_SETCONTROL(skb->data, contr); } if (!ctrl) { BT_ERR("Can't find controller %d for message", session->num); kfree_skb(skb); return; } capi_ctr_handle_message(ctrl, appl, skb); } static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) { BT_DBG("ctrl %p data %p", ctrl, data); return 0; } static void cmtp_reset_ctr(struct capi_ctr *ctrl) { struct cmtp_session *session = ctrl->driverdata; BT_DBG("ctrl %p", ctrl); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) capi_ctr_down(ctrl); #else capi_ctr_reseted(ctrl); #endif atomic_inc(&session->terminate); wake_up_process(session->task); } static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) { DECLARE_WAITQUEUE(wait, current); struct cmtp_session *session = ctrl->driverdata; struct cmtp_application *application; unsigned long timeo = CMTP_INTEROP_TIMEOUT; unsigned char buf[8]; int err = 0, nconn, want = rp->level3cnt; BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d", ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); application = cmtp_application_add(session, appl); if (!application) { BT_ERR("Can't allocate memory for new application"); return; } if (want < 0) nconn = ctrl->profile.nbchannel * -want; else nconn = want; if (nconn == 0) nconn = ctrl->profile.nbchannel; capimsg_setu16(buf, 0, nconn); capimsg_setu16(buf, 2, rp->datablkcnt); capimsg_setu16(buf, 4, rp->datablklen); application->state = BT_CONFIG; application->msgnum = cmtp_msgnum_get(session); cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, CAPI_FUNCTION_REGISTER, buf, 6); add_wait_queue(&session->wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { err = -EAGAIN; break; } if (application->state == BT_CLOSED) { err = -application->err; break; } if (application->state == BT_CONNECTED) break; if (signal_pending(current)) { err = -EINTR; break; } timeo = schedule_timeout(timeo); } set_current_state(TASK_RUNNING); remove_wait_queue(&session->wait, &wait); if (err) { cmtp_application_del(session, application); return; } } static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) { struct cmtp_session *session = ctrl->driverdata; struct cmtp_application *application; BT_DBG("ctrl %p appl %d", ctrl, appl); application = cmtp_application_get(session, CMTP_APPLID, appl); if (!application) { BT_ERR("Can't find application"); return; } application->msgnum = cmtp_msgnum_get(session); cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, CAPI_FUNCTION_RELEASE, NULL, 0); wait_event_interruptible_timeout(session->wait, (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT); cmtp_application_del(session, application); } static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) { struct cmtp_session *session = ctrl->driverdata; struct cmtp_application *application; __u16 appl; __u32 contr; BT_DBG("ctrl %p skb %p", ctrl, skb); appl = CAPIMSG_APPID(skb->data); contr = CAPIMSG_CONTROL(skb->data); application = cmtp_application_get(session, CMTP_APPLID, appl); if ((!application) || (application->state != BT_CONNECTED)) { BT_ERR("Can't find application with id %d", appl); return CAPI_ILLAPPNR; } CAPIMSG_SETAPPID(skb->data, application->mapping); if ((contr & 0x7f) == session->num) { contr = (contr & 0xffffff80) | 0x01; CAPIMSG_SETCONTROL(skb->data, contr); } cmtp_send_capimsg(session, skb); return CAPI_NOERROR; } static char *cmtp_procinfo(struct capi_ctr *ctrl) { return "CAPI Message Transport Protocol"; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int cmtp_proc_show(struct seq_file *m, void *v) { struct capi_ctr *ctrl = m->private; struct cmtp_session *session = ctrl->driverdata; struct cmtp_application *app; struct list_head *p, *n; seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl)); seq_printf(m, "addr %s\n", session->name); seq_printf(m, "ctrl %d\n", session->num); list_for_each_safe(p, n, &session->applications) { app = list_entry(p, struct cmtp_application, list); seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping); } return 0; } static int cmtp_proc_open(struct inode *inode, struct file *file) { return single_open(file, cmtp_proc_show, PDE(inode)->data); } static const struct file_operations cmtp_proc_fops = { .owner = THIS_MODULE, .open = cmtp_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #else static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) { struct cmtp_session *session = ctrl->driverdata; struct cmtp_application *app; struct list_head *p, *n; int len = 0; len += sprintf(page + len, "%s\n\n", cmtp_procinfo(ctrl)); len += sprintf(page + len, "addr %s\n", session->name); len += sprintf(page + len, "ctrl %d\n", session->num); list_for_each_safe(p, n, &session->applications) { app = list_entry(p, struct cmtp_application, list); len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping); } if (off + count >= len) *eof = 1; if (len < off) return 0; *start = page + off; return ((count < len - off) ? count : len - off); } #endif int cmtp_attach_device(struct cmtp_session *session) { unsigned char buf[4]; long ret; BT_DBG("session %p", session); capimsg_setu32(buf, 0, 0); cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, CAPI_FUNCTION_GET_PROFILE, buf, 4); ret = wait_event_interruptible_timeout(session->wait, session->ncontroller, CMTP_INTEROP_TIMEOUT); BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); if (!ret) return -ETIMEDOUT; if (!session->ncontroller) return -ENODEV; if (session->ncontroller > 1) BT_INFO("Setting up only CAPI controller 1"); session->ctrl.owner = THIS_MODULE; session->ctrl.driverdata = session; strcpy(session->ctrl.name, session->name); session->ctrl.driver_name = "cmtp"; session->ctrl.load_firmware = cmtp_load_firmware; session->ctrl.reset_ctr = cmtp_reset_ctr; session->ctrl.register_appl = cmtp_register_appl; session->ctrl.release_appl = cmtp_release_appl; session->ctrl.send_message = cmtp_send_message; session->ctrl.procinfo = cmtp_procinfo; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) session->ctrl.proc_fops = &cmtp_proc_fops; #else session->ctrl.ctr_read_proc = cmtp_ctr_read_proc; #endif if (attach_capi_ctr(&session->ctrl) < 0) { BT_ERR("Can't attach new controller"); return -EBUSY; } session->num = session->ctrl.cnr; BT_DBG("session %p num %d", session, session->num); capimsg_setu32(buf, 0, 1); cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), CAPI_FUNCTION_GET_VERSION, buf, 4); cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), CAPI_FUNCTION_GET_PROFILE, buf, 4); return 0; } void cmtp_detach_device(struct cmtp_session *session) { BT_DBG("session %p", session); detach_capi_ctr(&session->ctrl); } compat-drivers-2012-09-18/net/bluetooth/cmtp/sock.c0000644000175000017500000001373612026211315021233 0ustar mcgrofmcgrof/* CMTP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002-2003 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmtp.h" static struct bt_sock_list cmtp_sk_list = { .lock = __RW_LOCK_UNLOCKED(cmtp_sk_list.lock) }; static int cmtp_sock_release(struct socket *sock) { struct sock *sk = sock->sk; BT_DBG("sock %p sk %p", sock, sk); if (!sk) return 0; bt_sock_unlink(&cmtp_sk_list, sk); sock_orphan(sk); sock_put(sk); return 0; } static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct cmtp_connadd_req ca; struct cmtp_conndel_req cd; struct cmtp_connlist_req cl; struct cmtp_conninfo ci; struct socket *nsock; void __user *argp = (void __user *)arg; int err; BT_DBG("cmd %x arg %lx", cmd, arg); switch (cmd) { case CMTPCONNADD: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (copy_from_user(&ca, argp, sizeof(ca))) return -EFAULT; nsock = sockfd_lookup(ca.sock, &err); if (!nsock) return err; if (nsock->sk->sk_state != BT_CONNECTED) { sockfd_put(nsock); return -EBADFD; } err = cmtp_add_connection(&ca, nsock); if (!err) { if (copy_to_user(argp, &ca, sizeof(ca))) err = -EFAULT; } else sockfd_put(nsock); return err; case CMTPCONNDEL: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (copy_from_user(&cd, argp, sizeof(cd))) return -EFAULT; return cmtp_del_connection(&cd); case CMTPGETCONNLIST: if (copy_from_user(&cl, argp, sizeof(cl))) return -EFAULT; if (cl.cnum <= 0) return -EINVAL; err = cmtp_get_connlist(&cl); if (!err && copy_to_user(argp, &cl, sizeof(cl))) return -EFAULT; return err; case CMTPGETCONNINFO: if (copy_from_user(&ci, argp, sizeof(ci))) return -EFAULT; err = cmtp_get_conninfo(&ci); if (!err && copy_to_user(argp, &ci, sizeof(ci))) return -EFAULT; return err; } return -EINVAL; } #ifdef CONFIG_COMPAT static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { if (cmd == CMTPGETCONNLIST) { struct cmtp_connlist_req cl; u32 uci; int err; if (get_user(cl.cnum, (u32 __user *) arg) || get_user(uci, (u32 __user *) (arg + 4))) return -EFAULT; cl.ci = compat_ptr(uci); if (cl.cnum <= 0) return -EINVAL; err = cmtp_get_connlist(&cl); if (!err && put_user(cl.cnum, (u32 __user *) arg)) err = -EFAULT; return err; } return cmtp_sock_ioctl(sock, cmd, arg); } #endif static const struct proto_ops cmtp_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = cmtp_sock_release, .ioctl = cmtp_sock_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = cmtp_sock_compat_ioctl, #endif .bind = sock_no_bind, .getname = sock_no_getname, .sendmsg = sock_no_sendmsg, .recvmsg = sock_no_recvmsg, .poll = sock_no_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .mmap = sock_no_mmap }; static struct proto cmtp_proto = { .name = "CMTP", .owner = THIS_MODULE, .obj_size = sizeof(struct bt_sock) }; #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol, int kern) #else static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol) #endif { struct sock *sk; BT_DBG("sock %p", sock); if (sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto); if (!sk) return -ENOMEM; sock_init_data(sock, sk); sock->ops = &cmtp_sock_ops; sock->state = SS_UNCONNECTED; sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = protocol; sk->sk_state = BT_OPEN; bt_sock_link(&cmtp_sk_list, sk); return 0; } static const struct net_proto_family cmtp_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = cmtp_sock_create }; int cmtp_init_sockets(void) { int err; err = proto_register(&cmtp_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops); if (err < 0) { BT_ERR("Can't register CMTP socket"); goto error; } err = bt_procfs_init(THIS_MODULE, &init_net, "cmtp", &cmtp_sk_list, NULL); if (err < 0) { BT_ERR("Failed to create CMTP proc file"); bt_sock_unregister(BTPROTO_HIDP); goto error; } BT_INFO("CMTP socket layer initialized"); return 0; error: proto_unregister(&cmtp_proto); return err; } void cmtp_cleanup_sockets(void) { bt_procfs_cleanup(&init_net, "cmtp"); if (bt_sock_unregister(BTPROTO_CMTP) < 0) BT_ERR("Can't unregister CMTP socket"); proto_unregister(&cmtp_proto); } compat-drivers-2012-09-18/net/bluetooth/cmtp/core.c0000644000175000017500000002461712026211315021224 0ustar mcgrofmcgrof/* CMTP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002-2003 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmtp.h" #define VERSION "1.0" static DECLARE_RWSEM(cmtp_session_sem); static LIST_HEAD(cmtp_session_list); static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) { struct cmtp_session *session; BT_DBG(""); list_for_each_entry(session, &cmtp_session_list, list) if (!bacmp(bdaddr, &session->bdaddr)) return session; return NULL; } static void __cmtp_link_session(struct cmtp_session *session) { list_add(&session->list, &cmtp_session_list); } static void __cmtp_unlink_session(struct cmtp_session *session) { list_del(&session->list); } static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) { memset(ci, 0, sizeof(*ci)); bacpy(&ci->bdaddr, &session->bdaddr); ci->flags = session->flags; ci->state = session->state; ci->num = session->num; } static inline int cmtp_alloc_block_id(struct cmtp_session *session) { int i, id = -1; for (i = 0; i < 16; i++) if (!test_and_set_bit(i, &session->blockids)) { id = i; break; } return id; } static inline void cmtp_free_block_id(struct cmtp_session *session, int id) { clear_bit(id, &session->blockids); } static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) { struct sk_buff *skb = session->reassembly[id], *nskb; int size; BT_DBG("session %p buf %p count %d", session, buf, count); size = (skb) ? skb->len + count : count; nskb = alloc_skb(size, GFP_ATOMIC); if (!nskb) { BT_ERR("Can't allocate memory for CAPI message"); return; } if (skb && (skb->len > 0)) skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len); memcpy(skb_put(nskb, count), buf, count); session->reassembly[id] = nskb; kfree_skb(skb); } static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) { __u8 hdr, hdrlen, id; __u16 len; BT_DBG("session %p skb %p len %d", session, skb, skb->len); while (skb->len > 0) { hdr = skb->data[0]; switch (hdr & 0xc0) { case 0x40: hdrlen = 2; len = skb->data[1]; break; case 0x80: hdrlen = 3; len = skb->data[1] | (skb->data[2] << 8); break; default: hdrlen = 1; len = 0; break; } id = (hdr & 0x3c) >> 2; BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); if (hdrlen + len > skb->len) { BT_ERR("Wrong size or header information in CMTP frame"); break; } if (len == 0) { skb_pull(skb, hdrlen); continue; } switch (hdr & 0x03) { case 0x00: cmtp_add_msgpart(session, id, skb->data + hdrlen, len); cmtp_recv_capimsg(session, session->reassembly[id]); session->reassembly[id] = NULL; break; case 0x01: cmtp_add_msgpart(session, id, skb->data + hdrlen, len); break; default: if (session->reassembly[id] != NULL) kfree_skb(session->reassembly[id]); session->reassembly[id] = NULL; break; } skb_pull(skb, hdrlen + len); } kfree_skb(skb); return 0; } static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) { struct socket *sock = session->sock; struct kvec iv = { data, len }; struct msghdr msg; BT_DBG("session %p data %p len %d", session, data, len); if (!len) return 0; memset(&msg, 0, sizeof(msg)); return kernel_sendmsg(sock, &msg, &iv, 1, len); } static void cmtp_process_transmit(struct cmtp_session *session) { struct sk_buff *skb, *nskb; unsigned char *hdr; unsigned int size, tail; BT_DBG("session %p", session); nskb = alloc_skb(session->mtu, GFP_ATOMIC); if (!nskb) { BT_ERR("Can't allocate memory for new frame"); return; } while ((skb = skb_dequeue(&session->transmit))) { struct cmtp_scb *scb = (void *) skb->cb; tail = session->mtu - nskb->len; if (tail < 5) { cmtp_send_frame(session, nskb->data, nskb->len); skb_trim(nskb, 0); tail = session->mtu; } size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); if (scb->id < 0) { scb->id = cmtp_alloc_block_id(session); if (scb->id < 0) { skb_queue_head(&session->transmit, skb); break; } } if (size < 256) { hdr = skb_put(nskb, 2); hdr[0] = 0x40 | ((scb->id << 2) & 0x3c) | ((skb->len == size) ? 0x00 : 0x01); hdr[1] = size; } else { hdr = skb_put(nskb, 3); hdr[0] = 0x80 | ((scb->id << 2) & 0x3c) | ((skb->len == size) ? 0x00 : 0x01); hdr[1] = size & 0xff; hdr[2] = size >> 8; } skb_copy_from_linear_data(skb, skb_put(nskb, size), size); skb_pull(skb, size); if (skb->len > 0) { skb_queue_head(&session->transmit, skb); } else { cmtp_free_block_id(session, scb->id); if (scb->data) { cmtp_send_frame(session, nskb->data, nskb->len); skb_trim(nskb, 0); } kfree_skb(skb); } } cmtp_send_frame(session, nskb->data, nskb->len); kfree_skb(nskb); } static int cmtp_session(void *arg) { struct cmtp_session *session = arg; struct sock *sk = session->sock->sk; struct sk_buff *skb; wait_queue_t wait; BT_DBG("session %p", session); set_user_nice(current, -15); init_waitqueue_entry(&wait, current); add_wait_queue(sk_sleep(sk), &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (atomic_read(&session->terminate)) break; if (sk->sk_state != BT_CONNECTED) break; while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) cmtp_recv_frame(session, skb); else kfree_skb(skb); } cmtp_process_transmit(session); schedule(); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); down_write(&cmtp_session_sem); if (!(session->flags & (1 << CMTP_LOOPBACK))) cmtp_detach_device(session); fput(session->sock->file); __cmtp_unlink_session(session); up_write(&cmtp_session_sem); kfree(session); module_put_and_exit(0); return 0; } int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) { struct cmtp_session *session, *s; int i, err; BT_DBG(""); session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL); if (!session) return -ENOMEM; down_write(&cmtp_session_sem); s = __cmtp_get_session(&bt_sk(sock->sk)->dst); if (s && s->state == BT_CONNECTED) { err = -EEXIST; goto failed; } bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst); session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu, l2cap_pi(sock->sk)->chan->imtu); BT_DBG("mtu %d", session->mtu); sprintf(session->name, "%s", batostr(&bt_sk(sock->sk)->dst)); session->sock = sock; session->state = BT_CONFIG; init_waitqueue_head(&session->wait); session->msgnum = CMTP_INITIAL_MSGNUM; INIT_LIST_HEAD(&session->applications); skb_queue_head_init(&session->transmit); for (i = 0; i < 16; i++) session->reassembly[i] = NULL; session->flags = req->flags; __cmtp_link_session(session); __module_get(THIS_MODULE); session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d", session->num); if (IS_ERR(session->task)) { module_put(THIS_MODULE); err = PTR_ERR(session->task); goto unlink; } if (!(session->flags & (1 << CMTP_LOOPBACK))) { err = cmtp_attach_device(session); if (err < 0) { atomic_inc(&session->terminate); wake_up_process(session->task); up_write(&cmtp_session_sem); return err; } } up_write(&cmtp_session_sem); return 0; unlink: __cmtp_unlink_session(session); failed: up_write(&cmtp_session_sem); kfree(session); return err; } int cmtp_del_connection(struct cmtp_conndel_req *req) { struct cmtp_session *session; int err = 0; BT_DBG(""); down_read(&cmtp_session_sem); session = __cmtp_get_session(&req->bdaddr); if (session) { /* Flush the transmit queue */ skb_queue_purge(&session->transmit); /* Stop session thread */ atomic_inc(&session->terminate); wake_up_process(session->task); } else err = -ENOENT; up_read(&cmtp_session_sem); return err; } int cmtp_get_connlist(struct cmtp_connlist_req *req) { struct cmtp_session *session; int err = 0, n = 0; BT_DBG(""); down_read(&cmtp_session_sem); list_for_each_entry(session, &cmtp_session_list, list) { struct cmtp_conninfo ci; __cmtp_copy_session(session, &ci); if (copy_to_user(req->ci, &ci, sizeof(ci))) { err = -EFAULT; break; } if (++n >= req->cnum) break; req->ci++; } req->cnum = n; up_read(&cmtp_session_sem); return err; } int cmtp_get_conninfo(struct cmtp_conninfo *ci) { struct cmtp_session *session; int err = 0; down_read(&cmtp_session_sem); session = __cmtp_get_session(&ci->bdaddr); if (session) __cmtp_copy_session(session, ci); else err = -ENOENT; up_read(&cmtp_session_sem); return err; } static int __init cmtp_init(void) { BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION); cmtp_init_sockets(); return 0; } static void __exit cmtp_exit(void) { cmtp_cleanup_sockets(); } module_init(cmtp_init); module_exit(cmtp_exit); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("bt-proto-5"); compat-drivers-2012-09-18/net/bluetooth/cmtp/cmtp.h0000644000175000017500000000565012026211315021240 0ustar mcgrofmcgrof/* CMTP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002-2003 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __CMTP_H #define __CMTP_H #include #include #define BTNAMSIZ 18 /* CMTP ioctl defines */ #define CMTPCONNADD _IOW('C', 200, int) #define CMTPCONNDEL _IOW('C', 201, int) #define CMTPGETCONNLIST _IOR('C', 210, int) #define CMTPGETCONNINFO _IOR('C', 211, int) #define CMTP_LOOPBACK 0 struct cmtp_connadd_req { int sock; /* Connected socket */ __u32 flags; }; struct cmtp_conndel_req { bdaddr_t bdaddr; __u32 flags; }; struct cmtp_conninfo { bdaddr_t bdaddr; __u32 flags; __u16 state; int num; }; struct cmtp_connlist_req { __u32 cnum; struct cmtp_conninfo __user *ci; }; int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); int cmtp_del_connection(struct cmtp_conndel_req *req); int cmtp_get_connlist(struct cmtp_connlist_req *req); int cmtp_get_conninfo(struct cmtp_conninfo *ci); /* CMTP session defines */ #define CMTP_INTEROP_TIMEOUT (HZ * 5) #define CMTP_INITIAL_MSGNUM 0xff00 struct cmtp_session { struct list_head list; struct socket *sock; bdaddr_t bdaddr; unsigned long state; unsigned long flags; uint mtu; char name[BTNAMSIZ]; atomic_t terminate; struct task_struct *task; wait_queue_head_t wait; int ncontroller; int num; struct capi_ctr ctrl; struct list_head applications; unsigned long blockids; int msgnum; struct sk_buff_head transmit; struct sk_buff *reassembly[16]; }; struct cmtp_application { struct list_head list; unsigned long state; int err; __u16 appl; __u16 mapping; __u16 msgnum; }; struct cmtp_scb { int id; int data; }; int cmtp_attach_device(struct cmtp_session *session); void cmtp_detach_device(struct cmtp_session *session); void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); /* CMTP init defines */ int cmtp_init_sockets(void); void cmtp_cleanup_sockets(void); #endif /* __CMTP_H */ compat-drivers-2012-09-18/net/bluetooth/cmtp/Makefile0000644000175000017500000000016612026211315021561 0ustar mcgrofmcgrof# # Makefile for the Linux Bluetooth CMTP layer # obj-$(CONFIG_BT_CMTP) += cmtp.o cmtp-objs := core.o sock.o capi.o compat-drivers-2012-09-18/net/bluetooth/cmtp/Kconfig0000644000175000017500000000051712026211315021424 0ustar mcgrofmcgrofconfig BT_CMTP tristate "CMTP protocol support" depends on BT && ISDN_CAPI help CMTP (CAPI Message Transport Protocol) is a transport layer for CAPI messages. CMTP is required for the Bluetooth Common ISDN Access Profile. Say Y here to compile CMTP support into the kernel or say M to compile it as module (cmtp). compat-drivers-2012-09-18/net/bluetooth/l2cap_core.c0000644000175000017500000040630612026211315021341 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. Copyright (C) 2011 ProFUSION Embedded Systems Copyright (c) 2012 Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth L2CAP core. */ #include #include #include #include #include #include #include #include bool disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, }; static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data); static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err); static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event); /* ---- L2CAP channels ---- */ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) { struct l2cap_chan *c; list_for_each_entry(c, &conn->chan_l, list) { if (c->dcid == cid) return c; } return NULL; } static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) { struct l2cap_chan *c; list_for_each_entry(c, &conn->chan_l, list) { if (c->scid == cid) return c; } return NULL; } /* Find channel with given SCID. * Returns locked channel. */ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) { struct l2cap_chan *c; mutex_lock(&conn->chan_lock); c = __l2cap_get_chan_by_scid(conn, cid); if (c) l2cap_chan_lock(c); mutex_unlock(&conn->chan_lock); return c; } static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) { struct l2cap_chan *c; list_for_each_entry(c, &conn->chan_l, list) { if (c->ident == ident) return c; } return NULL; } static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) { struct l2cap_chan *c; list_for_each_entry(c, &chan_list, global_l) { if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src)) return c; } return NULL; } int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) { int err; write_lock(&chan_list_lock); if (psm && __l2cap_global_chan_by_addr(psm, src)) { err = -EADDRINUSE; goto done; } if (psm) { chan->psm = psm; chan->sport = psm; err = 0; } else { u16 p; err = -EINVAL; for (p = 0x1001; p < 0x1100; p += 2) if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) { chan->psm = cpu_to_le16(p); chan->sport = cpu_to_le16(p); err = 0; break; } } done: write_unlock(&chan_list_lock); return err; } int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) { write_lock(&chan_list_lock); chan->scid = scid; write_unlock(&chan_list_lock); return 0; } static u16 l2cap_alloc_cid(struct l2cap_conn *conn) { u16 cid = L2CAP_CID_DYN_START; for (; cid < L2CAP_CID_DYN_END; cid++) { if (!__l2cap_get_chan_by_scid(conn, cid)) return cid; } return 0; } static void __l2cap_state_change(struct l2cap_chan *chan, int state) { BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state), state_to_string(state)); chan->state = state; chan->ops->state_change(chan, state); } static void l2cap_state_change(struct l2cap_chan *chan, int state) { struct sock *sk = chan->sk; lock_sock(sk); __l2cap_state_change(chan, state); release_sock(sk); } static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; sk->sk_err = err; } static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; lock_sock(sk); __l2cap_chan_set_err(chan, err); release_sock(sk); } static void __set_retrans_timer(struct l2cap_chan *chan) { if (!delayed_work_pending(&chan->monitor_timer) && chan->retrans_timeout) { l2cap_set_timer(chan, &chan->retrans_timer, msecs_to_jiffies(chan->retrans_timeout)); } } static void __set_monitor_timer(struct l2cap_chan *chan) { __clear_retrans_timer(chan); if (chan->monitor_timeout) { l2cap_set_timer(chan, &chan->monitor_timer, msecs_to_jiffies(chan->monitor_timeout)); } } static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head, u16 seq) { struct sk_buff *skb; skb_queue_walk(head, skb) { if (bt_cb(skb)->control.txseq == seq) return skb; } return NULL; } /* ---- L2CAP sequence number lists ---- */ /* For ERTM, ordered lists of sequence numbers must be tracked for * SREJ requests that are received and for frames that are to be * retransmitted. These seq_list functions implement a singly-linked * list in an array, where membership in the list can also be checked * in constant time. Items can also be added to the tail of the list * and removed from the head in constant time, without further memory * allocs or frees. */ static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size) { size_t alloc_size, i; /* Allocated size is a power of 2 to map sequence numbers * (which may be up to 14 bits) in to a smaller array that is * sized for the negotiated ERTM transmit windows. */ alloc_size = roundup_pow_of_two(size); seq_list->list = kmalloc(sizeof(u16) * alloc_size, GFP_KERNEL); if (!seq_list->list) return -ENOMEM; seq_list->mask = alloc_size - 1; seq_list->head = L2CAP_SEQ_LIST_CLEAR; seq_list->tail = L2CAP_SEQ_LIST_CLEAR; for (i = 0; i < alloc_size; i++) seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR; return 0; } static inline void l2cap_seq_list_free(struct l2cap_seq_list *seq_list) { kfree(seq_list->list); } static inline bool l2cap_seq_list_contains(struct l2cap_seq_list *seq_list, u16 seq) { /* Constant-time check for list membership */ return seq_list->list[seq & seq_list->mask] != L2CAP_SEQ_LIST_CLEAR; } static u16 l2cap_seq_list_remove(struct l2cap_seq_list *seq_list, u16 seq) { u16 mask = seq_list->mask; if (seq_list->head == L2CAP_SEQ_LIST_CLEAR) { /* In case someone tries to pop the head of an empty list */ return L2CAP_SEQ_LIST_CLEAR; } else if (seq_list->head == seq) { /* Head can be removed in constant time */ seq_list->head = seq_list->list[seq & mask]; seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR; if (seq_list->head == L2CAP_SEQ_LIST_TAIL) { seq_list->head = L2CAP_SEQ_LIST_CLEAR; seq_list->tail = L2CAP_SEQ_LIST_CLEAR; } } else { /* Walk the list to find the sequence number */ u16 prev = seq_list->head; while (seq_list->list[prev & mask] != seq) { prev = seq_list->list[prev & mask]; if (prev == L2CAP_SEQ_LIST_TAIL) return L2CAP_SEQ_LIST_CLEAR; } /* Unlink the number from the list and clear it */ seq_list->list[prev & mask] = seq_list->list[seq & mask]; seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR; if (seq_list->tail == seq) seq_list->tail = prev; } return seq; } static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list) { /* Remove the head in constant time */ return l2cap_seq_list_remove(seq_list, seq_list->head); } static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list) { u16 i; if (seq_list->head == L2CAP_SEQ_LIST_CLEAR) return; for (i = 0; i <= seq_list->mask; i++) seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR; seq_list->head = L2CAP_SEQ_LIST_CLEAR; seq_list->tail = L2CAP_SEQ_LIST_CLEAR; } static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq) { u16 mask = seq_list->mask; /* All appends happen in constant time */ if (seq_list->list[seq & mask] != L2CAP_SEQ_LIST_CLEAR) return; if (seq_list->tail == L2CAP_SEQ_LIST_CLEAR) seq_list->head = seq; else seq_list->list[seq_list->tail & mask] = seq; seq_list->tail = seq; seq_list->list[seq & mask] = L2CAP_SEQ_LIST_TAIL; } static void l2cap_chan_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, chan_timer.work); struct l2cap_conn *conn = chan->conn; int reason; BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); mutex_lock(&conn->chan_lock); l2cap_chan_lock(chan); if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; else if (chan->state == BT_CONNECT && chan->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else reason = ETIMEDOUT; l2cap_chan_close(chan, reason); l2cap_chan_unlock(chan); chan->ops->close(chan); mutex_unlock(&conn->chan_lock); l2cap_chan_put(chan); } struct l2cap_chan *l2cap_chan_create(void) { struct l2cap_chan *chan; chan = kzalloc(sizeof(*chan), GFP_ATOMIC); if (!chan) return NULL; mutex_init(&chan->lock); write_lock(&chan_list_lock); list_add(&chan->global_l, &chan_list); write_unlock(&chan_list_lock); INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout); chan->state = BT_OPEN; kref_init(&chan->kref); /* This flag is cleared in l2cap_chan_ready() */ set_bit(CONF_NOT_COMPLETE, &chan->conf_state); BT_DBG("chan %p", chan); return chan; } static void l2cap_chan_destroy(struct kref *kref) { struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref); BT_DBG("chan %p", chan); write_lock(&chan_list_lock); list_del(&chan->global_l); write_unlock(&chan_list_lock); kfree(chan); } void l2cap_chan_hold(struct l2cap_chan *c) { BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount)); kref_get(&c->kref); } void l2cap_chan_put(struct l2cap_chan *c) { BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount)); kref_put(&c->kref, l2cap_chan_destroy); } void l2cap_chan_set_defaults(struct l2cap_chan *chan) { chan->fcs = L2CAP_FCS_CRC16; chan->max_tx = L2CAP_DEFAULT_MAX_TX; chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; chan->ack_win = L2CAP_DEFAULT_TX_WINDOW; chan->sec_level = BT_SECURITY_LOW; set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, __le16_to_cpu(chan->psm), chan->dcid); conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; chan->conn = conn; switch (chan->chan_type) { case L2CAP_CHAN_CONN_ORIENTED: if (conn->hcon->type == LE_LINK) { /* LE connection */ chan->omtu = L2CAP_DEFAULT_MTU; chan->scid = L2CAP_CID_LE_DATA; chan->dcid = L2CAP_CID_LE_DATA; } else { /* Alloc CID for connection-oriented socket */ chan->scid = l2cap_alloc_cid(conn); chan->omtu = L2CAP_DEFAULT_MTU; } break; case L2CAP_CHAN_CONN_LESS: /* Connectionless socket */ chan->scid = L2CAP_CID_CONN_LESS; chan->dcid = L2CAP_CID_CONN_LESS; chan->omtu = L2CAP_DEFAULT_MTU; break; case L2CAP_CHAN_CONN_FIX_A2MP: chan->scid = L2CAP_CID_A2MP; chan->dcid = L2CAP_CID_A2MP; chan->omtu = L2CAP_A2MP_DEFAULT_MTU; chan->imtu = L2CAP_A2MP_DEFAULT_MTU; break; default: /* Raw socket can send/recv signalling messages only */ chan->scid = L2CAP_CID_SIGNALING; chan->dcid = L2CAP_CID_SIGNALING; chan->omtu = L2CAP_DEFAULT_MTU; } chan->local_id = L2CAP_BESTEFFORT_ID; chan->local_stype = L2CAP_SERV_BESTEFFORT; chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE; chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME; chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT; chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO; l2cap_chan_hold(chan); list_add(&chan->list, &conn->chan_l); } void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { mutex_lock(&conn->chan_lock); __l2cap_chan_add(conn, chan); mutex_unlock(&conn->chan_lock); } void l2cap_chan_del(struct l2cap_chan *chan, int err) { struct l2cap_conn *conn = chan->conn; __clear_chan_timer(chan); BT_DBG("chan %p, conn %p, err %d", chan, conn, err); if (conn) { /* Delete from channel list */ list_del(&chan->list); l2cap_chan_put(chan); chan->conn = NULL; if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP) hci_conn_put(conn->hcon); } if (chan->ops->teardown) chan->ops->teardown(chan, err); if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) return; switch(chan->mode) { case L2CAP_MODE_BASIC: break; case L2CAP_MODE_ERTM: __clear_retrans_timer(chan); __clear_monitor_timer(chan); __clear_ack_timer(chan); skb_queue_purge(&chan->srej_q); l2cap_seq_list_free(&chan->srej_list); l2cap_seq_list_free(&chan->retrans_list); /* fall through */ case L2CAP_MODE_STREAMING: skb_queue_purge(&chan->tx_q); break; } return; } void l2cap_chan_close(struct l2cap_chan *chan, int reason) { struct l2cap_conn *conn = chan->conn; struct sock *sk = chan->sk; BT_DBG("chan %p state %s sk %p", chan, state_to_string(chan->state), sk); switch (chan->state) { case BT_LISTEN: if (chan->ops->teardown) chan->ops->teardown(chan, 0); break; case BT_CONNECTED: case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { __set_chan_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else l2cap_chan_del(chan, reason); break; case BT_CONNECT2: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { struct l2cap_conn_rsp rsp; __u16 result; if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) result = L2CAP_CR_SEC_BLOCK; else result = L2CAP_CR_BAD_PSM; l2cap_state_change(chan, BT_DISCONN); rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); rsp.result = cpu_to_le16(result); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); } l2cap_chan_del(chan, reason); break; case BT_CONNECT: case BT_DISCONN: l2cap_chan_del(chan, reason); break; default: if (chan->ops->teardown) chan->ops->teardown(chan, 0); break; } } static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { if (chan->chan_type == L2CAP_CHAN_RAW) { switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_DEDICATED_BONDING_MITM; case BT_SECURITY_MEDIUM: return HCI_AT_DEDICATED_BONDING; default: return HCI_AT_NO_BONDING; } } else if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) { if (chan->sec_level == BT_SECURITY_LOW) chan->sec_level = BT_SECURITY_SDP; if (chan->sec_level == BT_SECURITY_HIGH) return HCI_AT_NO_BONDING_MITM; else return HCI_AT_NO_BONDING; } else { switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_GENERAL_BONDING_MITM; case BT_SECURITY_MEDIUM: return HCI_AT_GENERAL_BONDING; default: return HCI_AT_NO_BONDING; } } } /* Service level security */ int l2cap_chan_check_security(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; __u8 auth_type; auth_type = l2cap_get_auth_type(chan); return hci_conn_security(conn->hcon, chan->sec_level, auth_type); } static u8 l2cap_get_ident(struct l2cap_conn *conn) { u8 id; /* Get next available identificator. * 1 - 128 are used by kernel. * 129 - 199 are reserved. * 200 - 254 are used by utilities like l2ping, etc. */ spin_lock(&conn->lock); if (++conn->tx_ident > 128) conn->tx_ident = 1; id = conn->tx_ident; spin_unlock(&conn->lock); return id; } static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); u8 flags; BT_DBG("code 0x%2.2x", code); if (!skb) return; if (lmp_no_flush_capable(conn->hcon->hdev)) flags = ACL_START_NO_FLUSH; else flags = ACL_START; bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; skb->priority = HCI_PRIO_MAX; hci_send_acl(conn->hchan, skb, flags); } static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) { struct hci_conn *hcon = chan->conn->hcon; u16 flags; BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, skb->priority); if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && lmp_no_flush_capable(hcon->hdev)) flags = ACL_START_NO_FLUSH; else flags = ACL_START; bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); hci_send_acl(chan->conn->hchan, skb, flags); } static void __unpack_enhanced_control(u16 enh, struct l2cap_ctrl *control) { control->reqseq = (enh & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT; control->final = (enh & L2CAP_CTRL_FINAL) >> L2CAP_CTRL_FINAL_SHIFT; if (enh & L2CAP_CTRL_FRAME_TYPE) { /* S-Frame */ control->sframe = 1; control->poll = (enh & L2CAP_CTRL_POLL) >> L2CAP_CTRL_POLL_SHIFT; control->super = (enh & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT; control->sar = 0; control->txseq = 0; } else { /* I-Frame */ control->sframe = 0; control->sar = (enh & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT; control->txseq = (enh & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT; control->poll = 0; control->super = 0; } } static void __unpack_extended_control(u32 ext, struct l2cap_ctrl *control) { control->reqseq = (ext & L2CAP_EXT_CTRL_REQSEQ) >> L2CAP_EXT_CTRL_REQSEQ_SHIFT; control->final = (ext & L2CAP_EXT_CTRL_FINAL) >> L2CAP_EXT_CTRL_FINAL_SHIFT; if (ext & L2CAP_EXT_CTRL_FRAME_TYPE) { /* S-Frame */ control->sframe = 1; control->poll = (ext & L2CAP_EXT_CTRL_POLL) >> L2CAP_EXT_CTRL_POLL_SHIFT; control->super = (ext & L2CAP_EXT_CTRL_SUPERVISE) >> L2CAP_EXT_CTRL_SUPER_SHIFT; control->sar = 0; control->txseq = 0; } else { /* I-Frame */ control->sframe = 0; control->sar = (ext & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT; control->txseq = (ext & L2CAP_EXT_CTRL_TXSEQ) >> L2CAP_EXT_CTRL_TXSEQ_SHIFT; control->poll = 0; control->super = 0; } } static inline void __unpack_control(struct l2cap_chan *chan, struct sk_buff *skb) { if (test_bit(FLAG_EXT_CTRL, &chan->flags)) { __unpack_extended_control(get_unaligned_le32(skb->data), &bt_cb(skb)->control); skb_pull(skb, L2CAP_EXT_CTRL_SIZE); } else { __unpack_enhanced_control(get_unaligned_le16(skb->data), &bt_cb(skb)->control); skb_pull(skb, L2CAP_ENH_CTRL_SIZE); } } static u32 __pack_extended_control(struct l2cap_ctrl *control) { u32 packed; packed = control->reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT; packed |= control->final << L2CAP_EXT_CTRL_FINAL_SHIFT; if (control->sframe) { packed |= control->poll << L2CAP_EXT_CTRL_POLL_SHIFT; packed |= control->super << L2CAP_EXT_CTRL_SUPER_SHIFT; packed |= L2CAP_EXT_CTRL_FRAME_TYPE; } else { packed |= control->sar << L2CAP_EXT_CTRL_SAR_SHIFT; packed |= control->txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT; } return packed; } static u16 __pack_enhanced_control(struct l2cap_ctrl *control) { u16 packed; packed = control->reqseq << L2CAP_CTRL_REQSEQ_SHIFT; packed |= control->final << L2CAP_CTRL_FINAL_SHIFT; if (control->sframe) { packed |= control->poll << L2CAP_CTRL_POLL_SHIFT; packed |= control->super << L2CAP_CTRL_SUPER_SHIFT; packed |= L2CAP_CTRL_FRAME_TYPE; } else { packed |= control->sar << L2CAP_CTRL_SAR_SHIFT; packed |= control->txseq << L2CAP_CTRL_TXSEQ_SHIFT; } return packed; } static inline void __pack_control(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff *skb) { if (test_bit(FLAG_EXT_CTRL, &chan->flags)) { put_unaligned_le32(__pack_extended_control(control), skb->data + L2CAP_HDR_SIZE); } else { put_unaligned_le16(__pack_enhanced_control(control), skb->data + L2CAP_HDR_SIZE); } } static inline unsigned int __ertm_hdr_size(struct l2cap_chan *chan) { if (test_bit(FLAG_EXT_CTRL, &chan->flags)) return L2CAP_EXT_HDR_SIZE; else return L2CAP_ENH_HDR_SIZE; } static struct sk_buff *l2cap_create_sframe_pdu(struct l2cap_chan *chan, u32 control) { struct sk_buff *skb; struct l2cap_hdr *lh; int hlen = __ertm_hdr_size(chan); if (chan->fcs == L2CAP_FCS_CRC16) hlen += L2CAP_FCS_SIZE; skb = bt_skb_alloc(hlen, GFP_KERNEL); if (!skb) return ERR_PTR(-ENOMEM); lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); if (test_bit(FLAG_EXT_CTRL, &chan->flags)) put_unaligned_le32(control, skb_put(skb, L2CAP_EXT_CTRL_SIZE)); else put_unaligned_le16(control, skb_put(skb, L2CAP_ENH_CTRL_SIZE)); if (chan->fcs == L2CAP_FCS_CRC16) { u16 fcs = crc16(0, (u8 *)skb->data, skb->len); put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); } skb->priority = HCI_PRIO_MAX; return skb; } static void l2cap_send_sframe(struct l2cap_chan *chan, struct l2cap_ctrl *control) { struct sk_buff *skb; u32 control_field; BT_DBG("chan %p, control %p", chan, control); if (!control->sframe) return; if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) && !control->poll) control->final = 1; if (control->super == L2CAP_SUPER_RR) clear_bit(CONN_RNR_SENT, &chan->conn_state); else if (control->super == L2CAP_SUPER_RNR) set_bit(CONN_RNR_SENT, &chan->conn_state); if (control->super != L2CAP_SUPER_SREJ) { chan->last_acked_seq = control->reqseq; __clear_ack_timer(chan); } BT_DBG("reqseq %d, final %d, poll %d, super %d", control->reqseq, control->final, control->poll, control->super); if (test_bit(FLAG_EXT_CTRL, &chan->flags)) control_field = __pack_extended_control(control); else control_field = __pack_enhanced_control(control); skb = l2cap_create_sframe_pdu(chan, control_field); if (!IS_ERR(skb)) l2cap_do_send(chan, skb); } static void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, bool poll) { struct l2cap_ctrl control; BT_DBG("chan %p, poll %d", chan, poll); memset(&control, 0, sizeof(control)); control.sframe = 1; control.poll = poll; if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) control.super = L2CAP_SUPER_RNR; else control.super = L2CAP_SUPER_RR; control.reqseq = chan->buffer_seq; l2cap_send_sframe(chan, &control); } static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) { return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); } static void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; struct l2cap_conn_req req; req.scid = cpu_to_le16(chan->scid); req.psm = chan->psm; chan->ident = l2cap_get_ident(conn); set_bit(CONF_CONNECT_PEND, &chan->conf_state); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } static void l2cap_chan_ready(struct l2cap_chan *chan) { /* This clears all conf flags, including CONF_NOT_COMPLETE */ chan->conf_state = 0; __clear_chan_timer(chan); chan->state = BT_CONNECTED; chan->ops->ready(chan); } static void l2cap_do_start(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; if (conn->hcon->type == LE_LINK) { l2cap_chan_ready(chan); return; } if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) return; if (l2cap_chan_check_security(chan) && __l2cap_no_conn_pending(chan)) l2cap_send_conn_req(chan); } else { struct l2cap_info_req req; req.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; conn->info_ident = l2cap_get_ident(conn); schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, sizeof(req), &req); } } static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) { u32 local_feat_mask = l2cap_feat_mask; if (!disable_ertm) local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; switch (mode) { case L2CAP_MODE_ERTM: return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask; case L2CAP_MODE_STREAMING: return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask; default: return 0x00; } } static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; struct l2cap_disconn_req req; if (!conn) return; if (chan->mode == L2CAP_MODE_ERTM) { __clear_retrans_timer(chan); __clear_monitor_timer(chan); __clear_ack_timer(chan); } if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { __l2cap_state_change(chan, BT_DISCONN); return; } req.dcid = cpu_to_le16(chan->dcid); req.scid = cpu_to_le16(chan->scid); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); lock_sock(sk); __l2cap_state_change(chan, BT_DISCONN); __l2cap_chan_set_err(chan, err); release_sock(sk); } /* ---- L2CAP connections ---- */ static void l2cap_conn_start(struct l2cap_conn *conn) { struct l2cap_chan *chan, *tmp; BT_DBG("conn %p", conn); mutex_lock(&conn->chan_lock); list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { struct sock *sk = chan->sk; l2cap_chan_lock(chan); if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_chan_unlock(chan); continue; } if (chan->state == BT_CONNECT) { if (!l2cap_chan_check_security(chan) || !__l2cap_no_conn_pending(chan)) { l2cap_chan_unlock(chan); continue; } if (!l2cap_mode_supported(chan->mode, conn->feat_mask) && test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { l2cap_chan_close(chan, ECONNRESET); l2cap_chan_unlock(chan); continue; } l2cap_send_conn_req(chan); } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; char buf[128]; rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); if (l2cap_chan_check_security(chan)) { lock_sock(sk); if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { struct sock *parent = bt_sk(sk)->parent; rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND); rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND); if (parent) parent->sk_data_ready(parent, 0); } else { __l2cap_state_change(chan, BT_CONFIG); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); } release_sock(sk); } else { rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND); rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND); } l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); if (test_bit(CONF_REQ_SENT, &chan->conf_state) || rsp.result != L2CAP_CR_SUCCESS) { l2cap_chan_unlock(chan); continue; } set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } l2cap_chan_unlock(chan); } mutex_unlock(&conn->chan_lock); } /* Find socket with cid and source/destination bdaddr. * Returns closest match, locked. */ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid, bdaddr_t *src, bdaddr_t *dst) { struct l2cap_chan *c, *c1 = NULL; read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; if (state && c->state != state) continue; if (c->scid == cid) { int src_match, dst_match; int src_any, dst_any; /* Exact match. */ src_match = !bacmp(&bt_sk(sk)->src, src); dst_match = !bacmp(&bt_sk(sk)->dst, dst); if (src_match && dst_match) { read_unlock(&chan_list_lock); return c; } /* Closest match */ src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY); dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY); if ((src_match && dst_any) || (src_any && dst_match) || (src_any && dst_any)) c1 = c; } } read_unlock(&chan_list_lock); return c1; } static void l2cap_le_conn_ready(struct l2cap_conn *conn) { struct sock *parent, *sk; struct l2cap_chan *chan, *pchan; BT_DBG(""); /* Check if we have socket listening on cid */ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA, conn->src, conn->dst); if (!pchan) return; parent = pchan->sk; lock_sock(parent); chan = pchan->ops->new_connection(pchan); if (!chan) goto clean; sk = chan->sk; hci_conn_hold(conn->hcon); conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT; bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); bt_accept_enqueue(parent, sk); l2cap_chan_add(conn, chan); l2cap_chan_ready(chan); clean: release_sock(parent); } static void l2cap_conn_ready(struct l2cap_conn *conn) { struct l2cap_chan *chan; struct hci_conn *hcon = conn->hcon; BT_DBG("conn %p", conn); if (!hcon->out && hcon->type == LE_LINK) l2cap_le_conn_ready(conn); if (hcon->out && hcon->type == LE_LINK) smp_conn_security(hcon, hcon->pending_sec_level); mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { l2cap_chan_lock(chan); if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { l2cap_chan_unlock(chan); continue; } if (hcon->type == LE_LINK) { if (smp_conn_security(hcon, chan->sec_level)) l2cap_chan_ready(chan); } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { struct sock *sk = chan->sk; __clear_chan_timer(chan); lock_sock(sk); __l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); release_sock(sk); } else if (chan->state == BT_CONNECT) l2cap_do_start(chan); l2cap_chan_unlock(chan); } mutex_unlock(&conn->chan_lock); } /* Notify sockets that we cannot guaranty reliability anymore */ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) { struct l2cap_chan *chan; BT_DBG("conn %p", conn); mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) __l2cap_chan_set_err(chan, err); } mutex_unlock(&conn->chan_lock); } static void l2cap_info_timeout(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, info_timer.work); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; l2cap_conn_start(conn); } static void l2cap_conn_del(struct hci_conn *hcon, int err) { struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_chan *chan, *l; if (!conn) return; BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); kfree_skb(conn->rx_skb); mutex_lock(&conn->chan_lock); /* Kill channels */ list_for_each_entry_safe(chan, l, &conn->chan_l, list) { l2cap_chan_hold(chan); l2cap_chan_lock(chan); l2cap_chan_del(chan, err); l2cap_chan_unlock(chan); chan->ops->close(chan); l2cap_chan_put(chan); } mutex_unlock(&conn->chan_lock); hci_chan_del(conn->hchan); if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) cancel_delayed_work_sync(&conn->info_timer); if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) { cancel_delayed_work_sync(&conn->security_timer); smp_chan_destroy(conn); } hcon->l2cap_data = NULL; kfree(conn); } static void security_timeout(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, security_timer.work); BT_DBG("conn %p", conn); if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) { smp_chan_destroy(conn); l2cap_conn_del(conn->hcon, ETIMEDOUT); } } static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn = hcon->l2cap_data; struct hci_chan *hchan; if (conn || status) return conn; hchan = hci_chan_create(hcon); if (!hchan) return NULL; conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); if (!conn) { hci_chan_del(hchan); return NULL; } hcon->l2cap_data = conn; conn->hcon = hcon; conn->hchan = hchan; BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); if (hcon->hdev->le_mtu && hcon->type == LE_LINK) conn->mtu = hcon->hdev->le_mtu; else conn->mtu = hcon->hdev->acl_mtu; conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; conn->feat_mask = 0; spin_lock_init(&conn->lock); mutex_init(&conn->chan_lock); INIT_LIST_HEAD(&conn->chan_l); if (hcon->type == LE_LINK) INIT_DELAYED_WORK(&conn->security_timer, security_timeout); else INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout); conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; return conn; } /* ---- Socket interface ---- */ /* Find socket with psm and source / destination bdaddr. * Returns closest match. */ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *src, bdaddr_t *dst) { struct l2cap_chan *c, *c1 = NULL; read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; if (state && c->state != state) continue; if (c->psm == psm) { int src_match, dst_match; int src_any, dst_any; /* Exact match. */ src_match = !bacmp(&bt_sk(sk)->src, src); dst_match = !bacmp(&bt_sk(sk)->dst, dst); if (src_match && dst_match) { read_unlock(&chan_list_lock); return c; } /* Closest match */ src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY); dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY); if ((src_match && dst_any) || (src_any && dst_match) || (src_any && dst_any)) c1 = c; } } read_unlock(&chan_list_lock); return c1; } int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type) { struct sock *sk = chan->sk; bdaddr_t *src = &bt_sk(sk)->src; struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; __u8 auth_type; int err; BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst), dst_type, __le16_to_cpu(psm)); hdev = hci_get_route(dst, src); if (!hdev) return -EHOSTUNREACH; hci_dev_lock(hdev); l2cap_chan_lock(chan); /* PSM must be odd and lsb of upper byte must be 0 */ if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid && chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; goto done; } if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) { err = -EINVAL; goto done; } switch (chan->mode) { case L2CAP_MODE_BASIC: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: if (!disable_ertm) break; /* fall through */ default: err = -ENOTSUPP; goto done; } switch (chan->state) { case BT_CONNECT: case BT_CONNECT2: case BT_CONFIG: /* Already connecting */ err = 0; goto done; case BT_CONNECTED: /* Already connected */ err = -EISCONN; goto done; case BT_OPEN: case BT_BOUND: /* Can connect */ break; default: err = -EBADFD; goto done; } /* Set destination address and psm */ lock_sock(sk); bacpy(&bt_sk(sk)->dst, dst); release_sock(sk); chan->psm = psm; chan->dcid = cid; auth_type = l2cap_get_auth_type(chan); if (chan->dcid == L2CAP_CID_LE_DATA) hcon = hci_connect(hdev, LE_LINK, dst, dst_type, chan->sec_level, auth_type); else hcon = hci_connect(hdev, ACL_LINK, dst, dst_type, chan->sec_level, auth_type); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); goto done; } conn = l2cap_conn_add(hcon, 0); if (!conn) { hci_conn_put(hcon); err = -ENOMEM; goto done; } if (hcon->type == LE_LINK) { err = 0; if (!list_empty(&conn->chan_l)) { err = -EBUSY; hci_conn_put(hcon); } if (err) goto done; } /* Update source addr of the socket */ bacpy(src, conn->src); l2cap_chan_unlock(chan); l2cap_chan_add(conn, chan); l2cap_chan_lock(chan); l2cap_state_change(chan, BT_CONNECT); __set_chan_timer(chan, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { __clear_chan_timer(chan); if (l2cap_chan_check_security(chan)) l2cap_state_change(chan, BT_CONNECTED); } else l2cap_do_start(chan); } err = 0; done: l2cap_chan_unlock(chan); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; } int __l2cap_wait_ack(struct sock *sk) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; DECLARE_WAITQUEUE(wait, current); int err = 0; int timeo = HZ/5; add_wait_queue(sk_sleep(sk), &wait); set_current_state(TASK_INTERRUPTIBLE); while (chan->unacked_frames > 0 && chan->conn) { if (!timeo) timeo = HZ/5; if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); set_current_state(TASK_INTERRUPTIBLE); err = sock_error(sk); if (err) break; } set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); return err; } static void l2cap_monitor_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, monitor_timer.work); BT_DBG("chan %p", chan); l2cap_chan_lock(chan); if (!chan->conn) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; } l2cap_tx(chan, NULL, NULL, L2CAP_EV_MONITOR_TO); l2cap_chan_unlock(chan); l2cap_chan_put(chan); } static void l2cap_retrans_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, retrans_timer.work); BT_DBG("chan %p", chan); l2cap_chan_lock(chan); if (!chan->conn) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; } l2cap_tx(chan, NULL, NULL, L2CAP_EV_RETRANS_TO); l2cap_chan_unlock(chan); l2cap_chan_put(chan); } static void l2cap_streaming_send(struct l2cap_chan *chan, struct sk_buff_head *skbs) { struct sk_buff *skb; struct l2cap_ctrl *control; BT_DBG("chan %p, skbs %p", chan, skbs); skb_queue_splice_tail_init(skbs, &chan->tx_q); while (!skb_queue_empty(&chan->tx_q)) { skb = skb_dequeue(&chan->tx_q); bt_cb(skb)->control.retries = 1; control = &bt_cb(skb)->control; control->reqseq = 0; control->txseq = chan->next_tx_seq; __pack_control(chan, control, skb); if (chan->fcs == L2CAP_FCS_CRC16) { u16 fcs = crc16(0, (u8 *) skb->data, skb->len); put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); } l2cap_do_send(chan, skb); BT_DBG("Sent txseq %u", control->txseq); chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); chan->frames_sent++; } } static int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; struct l2cap_ctrl *control; int sent = 0; BT_DBG("chan %p", chan); if (chan->state != BT_CONNECTED) return -ENOTCONN; if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) return 0; while (chan->tx_send_head && chan->unacked_frames < chan->remote_tx_win && chan->tx_state == L2CAP_TX_STATE_XMIT) { skb = chan->tx_send_head; bt_cb(skb)->control.retries = 1; control = &bt_cb(skb)->control; if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control->final = 1; control->reqseq = chan->buffer_seq; chan->last_acked_seq = chan->buffer_seq; control->txseq = chan->next_tx_seq; __pack_control(chan, control, skb); if (chan->fcs == L2CAP_FCS_CRC16) { u16 fcs = crc16(0, (u8 *) skb->data, skb->len); put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); } /* Clone after data has been modified. Data is assumed to be read-only (for locking purposes) on cloned sk_buffs. */ tx_skb = skb_clone(skb, GFP_KERNEL); if (!tx_skb) break; __set_retrans_timer(chan); chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); chan->unacked_frames++; chan->frames_sent++; sent++; if (skb_queue_is_last(&chan->tx_q, skb)) chan->tx_send_head = NULL; else chan->tx_send_head = skb_queue_next(&chan->tx_q, skb); l2cap_do_send(chan, tx_skb); BT_DBG("Sent txseq %u", control->txseq); } BT_DBG("Sent %d, %u unacked, %u in ERTM queue", sent, chan->unacked_frames, skb_queue_len(&chan->tx_q)); return sent; } static void l2cap_ertm_resend(struct l2cap_chan *chan) { struct l2cap_ctrl control; struct sk_buff *skb; struct sk_buff *tx_skb; u16 seq; BT_DBG("chan %p", chan); if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) return; while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) { seq = l2cap_seq_list_pop(&chan->retrans_list); skb = l2cap_ertm_seq_in_queue(&chan->tx_q, seq); if (!skb) { BT_DBG("Error: Can't retransmit seq %d, frame missing", seq); continue; } bt_cb(skb)->control.retries++; control = bt_cb(skb)->control; if (chan->max_tx != 0 && bt_cb(skb)->control.retries > chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); l2cap_seq_list_clear(&chan->retrans_list); break; } control.reqseq = chan->buffer_seq; if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control.final = 1; else control.final = 0; if (skb_cloned(skb)) { /* Cloned sk_buffs are read-only, so we need a * writeable copy */ tx_skb = skb_copy(skb, GFP_ATOMIC); } else { tx_skb = skb_clone(skb, GFP_ATOMIC); } if (!tx_skb) { l2cap_seq_list_clear(&chan->retrans_list); break; } /* Update skb contents */ if (test_bit(FLAG_EXT_CTRL, &chan->flags)) { put_unaligned_le32(__pack_extended_control(&control), tx_skb->data + L2CAP_HDR_SIZE); } else { put_unaligned_le16(__pack_enhanced_control(&control), tx_skb->data + L2CAP_HDR_SIZE); } if (chan->fcs == L2CAP_FCS_CRC16) { u16 fcs = crc16(0, (u8 *) tx_skb->data, tx_skb->len); put_unaligned_le16(fcs, skb_put(tx_skb, L2CAP_FCS_SIZE)); } l2cap_do_send(chan, tx_skb); BT_DBG("Resent txseq %d", control.txseq); chan->last_acked_seq = chan->buffer_seq; } } static void l2cap_retransmit(struct l2cap_chan *chan, struct l2cap_ctrl *control) { BT_DBG("chan %p, control %p", chan, control); l2cap_seq_list_append(&chan->retrans_list, control->reqseq); l2cap_ertm_resend(chan); } static void l2cap_retransmit_all(struct l2cap_chan *chan, struct l2cap_ctrl *control) { struct sk_buff *skb; BT_DBG("chan %p, control %p", chan, control); if (control->poll) set_bit(CONN_SEND_FBIT, &chan->conn_state); l2cap_seq_list_clear(&chan->retrans_list); if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) return; if (chan->unacked_frames) { skb_queue_walk(&chan->tx_q, skb) { if (bt_cb(skb)->control.txseq == control->reqseq || skb == chan->tx_send_head) break; } skb_queue_walk_from(&chan->tx_q, skb) { if (skb == chan->tx_send_head) break; l2cap_seq_list_append(&chan->retrans_list, bt_cb(skb)->control.txseq); } l2cap_ertm_resend(chan); } } static void l2cap_send_ack(struct l2cap_chan *chan) { struct l2cap_ctrl control; u16 frames_to_ack = __seq_offset(chan, chan->buffer_seq, chan->last_acked_seq); int threshold; BT_DBG("chan %p last_acked_seq %d buffer_seq %d", chan, chan->last_acked_seq, chan->buffer_seq); memset(&control, 0, sizeof(control)); control.sframe = 1; if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && chan->rx_state == L2CAP_RX_STATE_RECV) { __clear_ack_timer(chan); control.super = L2CAP_SUPER_RNR; control.reqseq = chan->buffer_seq; l2cap_send_sframe(chan, &control); } else { if (!test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) { l2cap_ertm_send(chan); /* If any i-frames were sent, they included an ack */ if (chan->buffer_seq == chan->last_acked_seq) frames_to_ack = 0; } /* Ack now if the window is 3/4ths full. * Calculate without mul or div */ threshold = chan->ack_win; threshold += threshold << 1; threshold >>= 2; BT_DBG("frames_to_ack %u, threshold %d", frames_to_ack, threshold); if (frames_to_ack >= threshold) { __clear_ack_timer(chan); control.super = L2CAP_SUPER_RR; control.reqseq = chan->buffer_seq; l2cap_send_sframe(chan, &control); frames_to_ack = 0; } if (frames_to_ack) __set_ack_timer(chan); } } static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct msghdr *msg, int len, int count, struct sk_buff *skb) { struct l2cap_conn *conn = chan->conn; struct sk_buff **frag; int sent = 0; if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) return -EFAULT; sent += count; len -= count; /* Continuation fragments (no L2CAP header) */ frag = &skb_shinfo(skb)->frag_list; while (len) { struct sk_buff *tmp; count = min_t(unsigned int, conn->mtu, len); tmp = chan->ops->alloc_skb(chan, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(tmp)) return PTR_ERR(tmp); *frag = tmp; if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) return -EFAULT; (*frag)->priority = skb->priority; sent += count; len -= count; skb->len += (*frag)->len; skb->data_len += (*frag)->len; frag = &(*frag)->next; } return sent; } static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u32 priority) { struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; struct l2cap_hdr *lh; BT_DBG("chan %p len %zu priority %u", chan, len, priority); count = min_t(unsigned int, (conn->mtu - hlen), len); skb = chan->ops->alloc_skb(chan, count + hlen, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; skb->priority = priority; /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE); put_unaligned(chan->psm, skb_put(skb, L2CAP_PSMLEN_SIZE)); err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); if (unlikely(err < 0)) { kfree_skb(skb); return ERR_PTR(err); } return skb; } static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u32 priority) { struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count; struct l2cap_hdr *lh; BT_DBG("chan %p len %zu", chan, len); count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len); skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; skb->priority = priority; /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len); err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); if (unlikely(err < 0)) { kfree_skb(skb); return ERR_PTR(err); } return skb; } static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 sdulen) { struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count, hlen; struct l2cap_hdr *lh; BT_DBG("chan %p len %zu", chan, len); if (!conn) return ERR_PTR(-ENOTCONN); hlen = __ertm_hdr_size(chan); if (sdulen) hlen += L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) hlen += L2CAP_FCS_SIZE; count = min_t(unsigned int, (conn->mtu - hlen), len); skb = chan->ops->alloc_skb(chan, count + hlen, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); /* Control header is populated later */ if (test_bit(FLAG_EXT_CTRL, &chan->flags)) put_unaligned_le32(0, skb_put(skb, L2CAP_EXT_CTRL_SIZE)); else put_unaligned_le16(0, skb_put(skb, L2CAP_ENH_CTRL_SIZE)); if (sdulen) put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE)); err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); if (unlikely(err < 0)) { kfree_skb(skb); return ERR_PTR(err); } bt_cb(skb)->control.fcs = chan->fcs; bt_cb(skb)->control.retries = 0; return skb; } static int l2cap_segment_sdu(struct l2cap_chan *chan, struct sk_buff_head *seg_queue, struct msghdr *msg, size_t len) { struct sk_buff *skb; u16 sdu_len; size_t pdu_len; u8 sar; BT_DBG("chan %p, msg %p, len %zu", chan, msg, len); /* It is critical that ERTM PDUs fit in a single HCI fragment, * so fragmented skbs are not used. The HCI layer's handling * of fragmented skbs is not compatible with ERTM's queueing. */ /* PDU size is derived from the HCI MTU */ pdu_len = chan->conn->mtu; pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD); /* Adjust for largest possible L2CAP overhead. */ if (chan->fcs) pdu_len -= L2CAP_FCS_SIZE; pdu_len -= __ertm_hdr_size(chan); /* Remote device may have requested smaller PDUs */ pdu_len = min_t(size_t, pdu_len, chan->remote_mps); if (len <= pdu_len) { sar = L2CAP_SAR_UNSEGMENTED; sdu_len = 0; pdu_len = len; } else { sar = L2CAP_SAR_START; sdu_len = len; pdu_len -= L2CAP_SDULEN_SIZE; } while (len > 0) { skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len); if (IS_ERR(skb)) { __skb_queue_purge(seg_queue); return PTR_ERR(skb); } bt_cb(skb)->control.sar = sar; __skb_queue_tail(seg_queue, skb); len -= pdu_len; if (sdu_len) { sdu_len = 0; pdu_len += L2CAP_SDULEN_SIZE; } if (len <= pdu_len) { sar = L2CAP_SAR_END; pdu_len = len; } else { sar = L2CAP_SAR_CONTINUE; } } return 0; } int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u32 priority) { struct sk_buff *skb; int err; struct sk_buff_head seg_queue; /* Connectionless channel */ if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { skb = l2cap_create_connless_pdu(chan, msg, len, priority); if (IS_ERR(skb)) return PTR_ERR(skb); l2cap_do_send(chan, skb); return len; } switch (chan->mode) { case L2CAP_MODE_BASIC: /* Check outgoing MTU */ if (len > chan->omtu) return -EMSGSIZE; /* Create a basic PDU */ skb = l2cap_create_basic_pdu(chan, msg, len, priority); if (IS_ERR(skb)) return PTR_ERR(skb); l2cap_do_send(chan, skb); err = len; break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: /* Check outgoing MTU */ if (len > chan->omtu) { err = -EMSGSIZE; break; } __skb_queue_head_init(&seg_queue); /* Do segmentation before calling in to the state machine, * since it's possible to block while waiting for memory * allocation. */ err = l2cap_segment_sdu(chan, &seg_queue, msg, len); /* The channel could have been closed while segmenting, * check that it is still connected. */ if (chan->state != BT_CONNECTED) { __skb_queue_purge(&seg_queue); err = -ENOTCONN; } if (err) break; if (chan->mode == L2CAP_MODE_ERTM) l2cap_tx(chan, NULL, &seg_queue, L2CAP_EV_DATA_REQUEST); else l2cap_streaming_send(chan, &seg_queue); err = len; /* If the skbs were not queued for sending, they'll still be in * seg_queue and need to be purged. */ __skb_queue_purge(&seg_queue); break; default: BT_DBG("bad state %1.1x", chan->mode); err = -EBADFD; } return err; } static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq) { struct l2cap_ctrl control; u16 seq; BT_DBG("chan %p, txseq %u", chan, txseq); memset(&control, 0, sizeof(control)); control.sframe = 1; control.super = L2CAP_SUPER_SREJ; for (seq = chan->expected_tx_seq; seq != txseq; seq = __next_seq(chan, seq)) { if (!l2cap_ertm_seq_in_queue(&chan->srej_q, seq)) { control.reqseq = seq; l2cap_send_sframe(chan, &control); l2cap_seq_list_append(&chan->srej_list, seq); } } chan->expected_tx_seq = __next_seq(chan, txseq); } static void l2cap_send_srej_tail(struct l2cap_chan *chan) { struct l2cap_ctrl control; BT_DBG("chan %p", chan); if (chan->srej_list.tail == L2CAP_SEQ_LIST_CLEAR) return; memset(&control, 0, sizeof(control)); control.sframe = 1; control.super = L2CAP_SUPER_SREJ; control.reqseq = chan->srej_list.tail; l2cap_send_sframe(chan, &control); } static void l2cap_send_srej_list(struct l2cap_chan *chan, u16 txseq) { struct l2cap_ctrl control; u16 initial_head; u16 seq; BT_DBG("chan %p, txseq %u", chan, txseq); memset(&control, 0, sizeof(control)); control.sframe = 1; control.super = L2CAP_SUPER_SREJ; /* Capture initial list head to allow only one pass through the list. */ initial_head = chan->srej_list.head; do { seq = l2cap_seq_list_pop(&chan->srej_list); if (seq == txseq || seq == L2CAP_SEQ_LIST_CLEAR) break; control.reqseq = seq; l2cap_send_sframe(chan, &control); l2cap_seq_list_append(&chan->srej_list, seq); } while (chan->srej_list.head != initial_head); } static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq) { struct sk_buff *acked_skb; u16 ackseq; BT_DBG("chan %p, reqseq %u", chan, reqseq); if (chan->unacked_frames == 0 || reqseq == chan->expected_ack_seq) return; BT_DBG("expected_ack_seq %u, unacked_frames %u", chan->expected_ack_seq, chan->unacked_frames); for (ackseq = chan->expected_ack_seq; ackseq != reqseq; ackseq = __next_seq(chan, ackseq)) { acked_skb = l2cap_ertm_seq_in_queue(&chan->tx_q, ackseq); if (acked_skb) { skb_unlink(acked_skb, &chan->tx_q); kfree_skb(acked_skb); chan->unacked_frames--; } } chan->expected_ack_seq = reqseq; if (chan->unacked_frames == 0) __clear_retrans_timer(chan); BT_DBG("unacked_frames %u", chan->unacked_frames); } static void l2cap_abort_rx_srej_sent(struct l2cap_chan *chan) { BT_DBG("chan %p", chan); chan->expected_tx_seq = chan->buffer_seq; l2cap_seq_list_clear(&chan->srej_list); skb_queue_purge(&chan->srej_q); chan->rx_state = L2CAP_RX_STATE_RECV; } static void l2cap_tx_state_xmit(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event) { BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs, event); switch (event) { case L2CAP_EV_DATA_REQUEST: if (chan->tx_send_head == NULL) chan->tx_send_head = skb_peek(skbs); skb_queue_splice_tail_init(skbs, &chan->tx_q); l2cap_ertm_send(chan); break; case L2CAP_EV_LOCAL_BUSY_DETECTED: BT_DBG("Enter LOCAL_BUSY"); set_bit(CONN_LOCAL_BUSY, &chan->conn_state); if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) { /* The SREJ_SENT state must be aborted if we are to * enter the LOCAL_BUSY state. */ l2cap_abort_rx_srej_sent(chan); } l2cap_send_ack(chan); break; case L2CAP_EV_LOCAL_BUSY_CLEAR: BT_DBG("Exit LOCAL_BUSY"); clear_bit(CONN_LOCAL_BUSY, &chan->conn_state); if (test_bit(CONN_RNR_SENT, &chan->conn_state)) { struct l2cap_ctrl local_control; memset(&local_control, 0, sizeof(local_control)); local_control.sframe = 1; local_control.super = L2CAP_SUPER_RR; local_control.poll = 1; local_control.reqseq = chan->buffer_seq; l2cap_send_sframe(chan, &local_control); chan->retry_count = 1; __set_monitor_timer(chan); chan->tx_state = L2CAP_TX_STATE_WAIT_F; } break; case L2CAP_EV_RECV_REQSEQ_AND_FBIT: l2cap_process_reqseq(chan, control->reqseq); break; case L2CAP_EV_EXPLICIT_POLL: l2cap_send_rr_or_rnr(chan, 1); chan->retry_count = 1; __set_monitor_timer(chan); __clear_ack_timer(chan); chan->tx_state = L2CAP_TX_STATE_WAIT_F; break; case L2CAP_EV_RETRANS_TO: l2cap_send_rr_or_rnr(chan, 1); chan->retry_count = 1; __set_monitor_timer(chan); chan->tx_state = L2CAP_TX_STATE_WAIT_F; break; case L2CAP_EV_RECV_FBIT: /* Nothing to process */ break; default: break; } } static void l2cap_tx_state_wait_f(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event) { BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs, event); switch (event) { case L2CAP_EV_DATA_REQUEST: if (chan->tx_send_head == NULL) chan->tx_send_head = skb_peek(skbs); /* Queue data, but don't send. */ skb_queue_splice_tail_init(skbs, &chan->tx_q); break; case L2CAP_EV_LOCAL_BUSY_DETECTED: BT_DBG("Enter LOCAL_BUSY"); set_bit(CONN_LOCAL_BUSY, &chan->conn_state); if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) { /* The SREJ_SENT state must be aborted if we are to * enter the LOCAL_BUSY state. */ l2cap_abort_rx_srej_sent(chan); } l2cap_send_ack(chan); break; case L2CAP_EV_LOCAL_BUSY_CLEAR: BT_DBG("Exit LOCAL_BUSY"); clear_bit(CONN_LOCAL_BUSY, &chan->conn_state); if (test_bit(CONN_RNR_SENT, &chan->conn_state)) { struct l2cap_ctrl local_control; memset(&local_control, 0, sizeof(local_control)); local_control.sframe = 1; local_control.super = L2CAP_SUPER_RR; local_control.poll = 1; local_control.reqseq = chan->buffer_seq; l2cap_send_sframe(chan, &local_control); chan->retry_count = 1; __set_monitor_timer(chan); chan->tx_state = L2CAP_TX_STATE_WAIT_F; } break; case L2CAP_EV_RECV_REQSEQ_AND_FBIT: l2cap_process_reqseq(chan, control->reqseq); /* Fall through */ case L2CAP_EV_RECV_FBIT: if (control && control->final) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) __set_retrans_timer(chan); chan->retry_count = 0; chan->tx_state = L2CAP_TX_STATE_XMIT; BT_DBG("recv fbit tx_state 0x2.2%x", chan->tx_state); } break; case L2CAP_EV_EXPLICIT_POLL: /* Ignore */ break; case L2CAP_EV_MONITOR_TO: if (chan->max_tx == 0 || chan->retry_count < chan->max_tx) { l2cap_send_rr_or_rnr(chan, 1); __set_monitor_timer(chan); chan->retry_count++; } else { l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); } break; default: break; } } static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event) { BT_DBG("chan %p, control %p, skbs %p, event %d, state %d", chan, control, skbs, event, chan->tx_state); switch (chan->tx_state) { case L2CAP_TX_STATE_XMIT: l2cap_tx_state_xmit(chan, control, skbs, event); break; case L2CAP_TX_STATE_WAIT_F: l2cap_tx_state_wait_f(chan, control, skbs, event); break; default: /* Ignore event */ break; } } static void l2cap_pass_to_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control) { BT_DBG("chan %p, control %p", chan, control); l2cap_tx(chan, control, NULL, L2CAP_EV_RECV_REQSEQ_AND_FBIT); } static void l2cap_pass_to_tx_fbit(struct l2cap_chan *chan, struct l2cap_ctrl *control) { BT_DBG("chan %p, control %p", chan, control); l2cap_tx(chan, control, NULL, L2CAP_EV_RECV_FBIT); } /* Copy frame to all raw sockets on that connection */ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) { struct sk_buff *nskb; struct l2cap_chan *chan; BT_DBG("conn %p", conn); mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { struct sock *sk = chan->sk; if (chan->chan_type != L2CAP_CHAN_RAW) continue; /* Don't send frame to the socket it came from */ if (skb->sk == sk) continue; nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) continue; if (chan->ops->recv(chan, nskb)) kfree_skb(nskb); } mutex_unlock(&conn->chan_lock); } /* ---- L2CAP signalling commands ---- */ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data) { struct sk_buff *skb, **frag; struct l2cap_cmd_hdr *cmd; struct l2cap_hdr *lh; int len, count; BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %u", conn, code, ident, dlen); len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; count = min_t(unsigned int, conn->mtu, len); skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) return NULL; lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); if (conn->hcon->type == LE_LINK) lh->cid = __constant_cpu_to_le16(L2CAP_CID_LE_SIGNALING); else lh->cid = __constant_cpu_to_le16(L2CAP_CID_SIGNALING); cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); cmd->code = code; cmd->ident = ident; cmd->len = cpu_to_le16(dlen); if (dlen) { count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE; memcpy(skb_put(skb, count), data, count); data += count; } len -= skb->len; /* Continuation fragments (no L2CAP header) */ frag = &skb_shinfo(skb)->frag_list; while (len) { count = min_t(unsigned int, conn->mtu, len); *frag = bt_skb_alloc(count, GFP_ATOMIC); if (!*frag) goto fail; memcpy(skb_put(*frag, count), data, count); len -= count; data += count; frag = &(*frag)->next; } return skb; fail: kfree_skb(skb); return NULL; } static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val) { struct l2cap_conf_opt *opt = *ptr; int len; len = L2CAP_CONF_OPT_SIZE + opt->len; *ptr += len; *type = opt->type; *olen = opt->len; switch (opt->len) { case 1: *val = *((u8 *) opt->val); break; case 2: *val = get_unaligned_le16(opt->val); break; case 4: *val = get_unaligned_le32(opt->val); break; default: *val = (unsigned long) opt->val; break; } BT_DBG("type 0x%2.2x len %u val 0x%lx", *type, opt->len, *val); return len; } static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) { struct l2cap_conf_opt *opt = *ptr; BT_DBG("type 0x%2.2x len %u val 0x%lx", type, len, val); opt->type = type; opt->len = len; switch (len) { case 1: *((u8 *) opt->val) = val; break; case 2: put_unaligned_le16(val, opt->val); break; case 4: put_unaligned_le32(val, opt->val); break; default: memcpy(opt->val, (void *) val, len); break; } *ptr += L2CAP_CONF_OPT_SIZE + len; } static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) { struct l2cap_conf_efs efs; switch (chan->mode) { case L2CAP_MODE_ERTM: efs.id = chan->local_id; efs.stype = chan->local_stype; efs.msdu = cpu_to_le16(chan->local_msdu); efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); efs.acc_lat = __constant_cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); efs.flush_to = __constant_cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); break; case L2CAP_MODE_STREAMING: efs.id = 1; efs.stype = L2CAP_SERV_BESTEFFORT; efs.msdu = cpu_to_le16(chan->local_msdu); efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); efs.acc_lat = 0; efs.flush_to = 0; break; default: return; } l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); } static void l2cap_ack_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, ack_timer.work); u16 frames_to_ack; BT_DBG("chan %p", chan); l2cap_chan_lock(chan); frames_to_ack = __seq_offset(chan, chan->buffer_seq, chan->last_acked_seq); if (frames_to_ack) l2cap_send_rr_or_rnr(chan, 0); l2cap_chan_unlock(chan); l2cap_chan_put(chan); } int l2cap_ertm_init(struct l2cap_chan *chan) { int err; chan->next_tx_seq = 0; chan->expected_tx_seq = 0; chan->expected_ack_seq = 0; chan->unacked_frames = 0; chan->buffer_seq = 0; chan->frames_sent = 0; chan->last_acked_seq = 0; chan->sdu = NULL; chan->sdu_last_frag = NULL; chan->sdu_len = 0; skb_queue_head_init(&chan->tx_q); if (chan->mode != L2CAP_MODE_ERTM) return 0; chan->rx_state = L2CAP_RX_STATE_RECV; chan->tx_state = L2CAP_TX_STATE_XMIT; INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout); INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout); INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout); skb_queue_head_init(&chan->srej_q); err = l2cap_seq_list_init(&chan->srej_list, chan->tx_win); if (err < 0) return err; err = l2cap_seq_list_init(&chan->retrans_list, chan->remote_tx_win); if (err < 0) l2cap_seq_list_free(&chan->srej_list); return err; } static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) { switch (mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: if (l2cap_mode_supported(mode, remote_feat_mask)) return mode; /* fall through */ default: return L2CAP_MODE_BASIC; } } static inline bool __l2cap_ews_supported(struct l2cap_chan *chan) { return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; } static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) { return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; } static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && __l2cap_ews_supported(chan)) { /* use extended control field */ set_bit(FLAG_EXT_CTRL, &chan->flags); chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; } else { chan->tx_win = min_t(u16, chan->tx_win, L2CAP_DEFAULT_TX_WINDOW); chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; } chan->ack_win = chan->tx_win; } static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) { struct l2cap_conf_req *req = data; struct l2cap_conf_rfc rfc = { .mode = chan->mode }; void *ptr = req->data; u16 size; BT_DBG("chan %p", chan); if (chan->num_conf_req || chan->num_conf_rsp) goto done; switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) break; if (__l2cap_efs_supported(chan)) set_bit(FLAG_EFS_ENABLE, &chan->flags); /* fall through */ default: chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); break; } done: if (chan->imtu != L2CAP_DEFAULT_MTU) l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); switch (chan->mode) { case L2CAP_MODE_BASIC: if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) && !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) break; rfc.mode = L2CAP_MODE_BASIC; rfc.txwin_size = 0; rfc.max_transmit = 0; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; rfc.max_pdu_size = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); break; case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; rfc.max_transmit = chan->max_tx; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); l2cap_txwin_setup(chan); rfc.txwin_size = min_t(u16, chan->tx_win, L2CAP_DEFAULT_TX_WINDOW); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; if (chan->fcs == L2CAP_FCS_NONE || test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } if (test_bit(FLAG_EXT_CTRL, &chan->flags)) l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, chan->tx_win); break; case L2CAP_MODE_STREAMING: l2cap_txwin_setup(chan); rfc.mode = L2CAP_MODE_STREAMING; rfc.txwin_size = 0; rfc.max_transmit = 0; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; if (chan->fcs == L2CAP_FCS_NONE || test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } break; } req->dcid = cpu_to_le16(chan->dcid); req->flags = __constant_cpu_to_le16(0); return ptr - data; } static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) { struct l2cap_conf_rsp *rsp = data; void *ptr = rsp->data; void *req = chan->conf_req; int len = chan->conf_len; int type, hint, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; struct l2cap_conf_efs efs; u8 remote_efs = 0; u16 mtu = L2CAP_DEFAULT_MTU; u16 result = L2CAP_CONF_SUCCESS; u16 size; BT_DBG("chan %p", chan); while (len >= L2CAP_CONF_OPT_SIZE) { len -= l2cap_get_conf_opt(&req, &type, &olen, &val); hint = type & L2CAP_CONF_HINT; type &= L2CAP_CONF_MASK; switch (type) { case L2CAP_CONF_MTU: mtu = val; break; case L2CAP_CONF_FLUSH_TO: chan->flush_to = val; break; case L2CAP_CONF_QOS: break; case L2CAP_CONF_RFC: if (olen == sizeof(rfc)) memcpy(&rfc, (void *) val, olen); break; case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) set_bit(CONF_NO_FCS_RECV, &chan->conf_state); break; case L2CAP_CONF_EFS: remote_efs = 1; if (olen == sizeof(efs)) memcpy(&efs, (void *) val, olen); break; case L2CAP_CONF_EWS: if (!enable_hs) return -ECONNREFUSED; set_bit(FLAG_EXT_CTRL, &chan->flags); set_bit(CONF_EWS_RECV, &chan->conf_state); chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; chan->remote_tx_win = val; break; default: if (hint) break; result = L2CAP_CONF_UNKNOWN; *((u8 *) ptr++) = type; break; } } if (chan->num_conf_rsp || chan->num_conf_req > 1) goto done; switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); break; } if (remote_efs) { if (__l2cap_efs_supported(chan)) set_bit(FLAG_EFS_ENABLE, &chan->flags); else return -ECONNREFUSED; } if (chan->mode != rfc.mode) return -ECONNREFUSED; break; } done: if (chan->mode != rfc.mode) { result = L2CAP_CONF_UNACCEPT; rfc.mode = chan->mode; if (chan->num_conf_rsp == 1) return -ECONNREFUSED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); } if (result == L2CAP_CONF_SUCCESS) { /* Configure output options and let the other side know * which ones we don't like. */ if (mtu < L2CAP_DEFAULT_MIN_MTU) result = L2CAP_CONF_UNACCEPT; else { chan->omtu = mtu; set_bit(CONF_MTU_DONE, &chan->conf_state); } l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); if (remote_efs) { if (chan->local_stype != L2CAP_SERV_NOTRAFIC && efs.stype != L2CAP_SERV_NOTRAFIC && efs.stype != chan->local_stype) { result = L2CAP_CONF_UNACCEPT; if (chan->num_conf_req >= 1) return -ECONNREFUSED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); } else { /* Send PENDING Conf Rsp */ result = L2CAP_CONF_PENDING; set_bit(CONF_LOC_CONF_PEND, &chan->conf_state); } } switch (rfc.mode) { case L2CAP_MODE_BASIC: chan->fcs = L2CAP_FCS_NONE; set_bit(CONF_MODE_DONE, &chan->conf_state); break; case L2CAP_MODE_ERTM: if (!test_bit(CONF_EWS_RECV, &chan->conf_state)) chan->remote_tx_win = rfc.txwin_size; else rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; chan->remote_max_tx = rfc.max_transmit; size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), chan->conn->mtu - L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; rfc.retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); rfc.monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { chan->remote_id = efs.id; chan->remote_stype = efs.stype; chan->remote_msdu = le16_to_cpu(efs.msdu); chan->remote_flush_to = le32_to_cpu(efs.flush_to); chan->remote_acc_lat = le32_to_cpu(efs.acc_lat); chan->remote_sdu_itime = le32_to_cpu(efs.sdu_itime); l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); } break; case L2CAP_MODE_STREAMING: size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), chan->conn->mtu - L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); break; default: result = L2CAP_CONF_UNACCEPT; memset(&rfc, 0, sizeof(rfc)); rfc.mode = chan->mode; } if (result == L2CAP_CONF_SUCCESS) set_bit(CONF_OUTPUT_DONE, &chan->conf_state); } rsp->scid = cpu_to_le16(chan->dcid); rsp->result = cpu_to_le16(result); rsp->flags = __constant_cpu_to_le16(0); return ptr - data; } static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result) { struct l2cap_conf_req *req = data; void *ptr = req->data; int type, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; struct l2cap_conf_efs efs; BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); while (len >= L2CAP_CONF_OPT_SIZE) { len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); switch (type) { case L2CAP_CONF_MTU: if (val < L2CAP_DEFAULT_MIN_MTU) { *result = L2CAP_CONF_UNACCEPT; chan->imtu = L2CAP_DEFAULT_MIN_MTU; } else chan->imtu = val; l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); break; case L2CAP_CONF_FLUSH_TO: chan->flush_to = val; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, chan->flush_to); break; case L2CAP_CONF_RFC: if (olen == sizeof(rfc)) memcpy(&rfc, (void *)val, olen); if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && rfc.mode != chan->mode) return -ECONNREFUSED; chan->fcs = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); break; case L2CAP_CONF_EWS: chan->ack_win = min_t(u16, val, chan->ack_win); l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, chan->tx_win); break; case L2CAP_CONF_EFS: if (olen == sizeof(efs)) memcpy(&efs, (void *)val, olen); if (chan->local_stype != L2CAP_SERV_NOTRAFIC && efs.stype != L2CAP_SERV_NOTRAFIC && efs.stype != chan->local_stype) return -ECONNREFUSED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); break; } } if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode) return -ECONNREFUSED; chan->mode = rfc.mode; if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) { switch (rfc.mode) { case L2CAP_MODE_ERTM: chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); chan->mps = le16_to_cpu(rfc.max_pdu_size); if (!test_bit(FLAG_EXT_CTRL, &chan->flags)) chan->ack_win = min_t(u16, chan->ack_win, rfc.txwin_size); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { chan->local_msdu = le16_to_cpu(efs.msdu); chan->local_sdu_itime = le32_to_cpu(efs.sdu_itime); chan->local_acc_lat = le32_to_cpu(efs.acc_lat); chan->local_flush_to = le32_to_cpu(efs.flush_to); } break; case L2CAP_MODE_STREAMING: chan->mps = le16_to_cpu(rfc.max_pdu_size); } } req->dcid = cpu_to_le16(chan->dcid); req->flags = __constant_cpu_to_le16(0); return ptr - data; } static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, u16 result, u16 flags) { struct l2cap_conf_rsp *rsp = data; void *ptr = rsp->data; BT_DBG("chan %p", chan); rsp->scid = cpu_to_le16(chan->dcid); rsp->result = cpu_to_le16(result); rsp->flags = cpu_to_le16(flags); return ptr - data; } void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) { struct l2cap_conn_rsp rsp; struct l2cap_conn *conn = chan->conn; u8 buf[128]; rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) return; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) { int type, olen; unsigned long val; /* Use sane default values in case a misbehaving remote device * did not send an RFC or extended window size option. */ u16 txwin_ext = chan->ack_win; struct l2cap_conf_rfc rfc = { .mode = chan->mode, .retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO), .monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO), .max_pdu_size = cpu_to_le16(chan->imtu), .txwin_size = min_t(u16, chan->ack_win, L2CAP_DEFAULT_TX_WINDOW), }; BT_DBG("chan %p, rsp %p, len %d", chan, rsp, len); if ((chan->mode != L2CAP_MODE_ERTM) && (chan->mode != L2CAP_MODE_STREAMING)) return; while (len >= L2CAP_CONF_OPT_SIZE) { len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); switch (type) { case L2CAP_CONF_RFC: if (olen == sizeof(rfc)) memcpy(&rfc, (void *)val, olen); break; case L2CAP_CONF_EWS: txwin_ext = val; break; } } switch (rfc.mode) { case L2CAP_MODE_ERTM: chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); chan->mps = le16_to_cpu(rfc.max_pdu_size); if (test_bit(FLAG_EXT_CTRL, &chan->flags)) chan->ack_win = min_t(u16, chan->ack_win, txwin_ext); else chan->ack_win = min_t(u16, chan->ack_win, rfc.txwin_size); break; case L2CAP_MODE_STREAMING: chan->mps = le16_to_cpu(rfc.max_pdu_size); } } static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data; if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD) return 0; if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && cmd->ident == conn->info_ident) { cancel_delayed_work(&conn->info_timer); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; l2cap_conn_start(conn); } return 0; } static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; struct l2cap_chan *chan = NULL, *pchan; struct sock *parent, *sk = NULL; int result, status = L2CAP_CS_NO_INFO; u16 dcid = 0, scid = __le16_to_cpu(req->scid); __le16 psm = req->psm; BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid); /* Check if we have socket listening on psm */ pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src, conn->dst); if (!pchan) { result = L2CAP_CR_BAD_PSM; goto sendresp; } parent = pchan->sk; mutex_lock(&conn->chan_lock); lock_sock(parent); /* Check if the ACL is secure enough (if not SDP) */ if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) && !hci_conn_check_link_mode(conn->hcon)) { conn->disc_reason = HCI_ERROR_AUTH_FAILURE; result = L2CAP_CR_SEC_BLOCK; goto response; } result = L2CAP_CR_NO_MEM; /* Check if we already have channel with that dcid */ if (__l2cap_get_chan_by_dcid(conn, scid)) goto response; chan = pchan->ops->new_connection(pchan); if (!chan) goto response; sk = chan->sk; hci_conn_hold(conn->hcon); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); chan->psm = psm; chan->dcid = scid; bt_accept_enqueue(parent, sk); __l2cap_chan_add(conn, chan); dcid = chan->scid; __set_chan_timer(chan, sk->sk_sndtimeo); chan->ident = cmd->ident; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { if (l2cap_chan_check_security(chan)) { if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { __l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHOR_PEND; parent->sk_data_ready(parent, 0); } else { __l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; status = L2CAP_CS_NO_INFO; } } else { __l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHEN_PEND; } } else { __l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_NO_INFO; } response: release_sock(parent); mutex_unlock(&conn->chan_lock); sendresp: rsp.scid = cpu_to_le16(scid); rsp.dcid = cpu_to_le16(dcid); rsp.result = cpu_to_le16(result); rsp.status = cpu_to_le16(status); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) { struct l2cap_info_req info; info.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; conn->info_ident = l2cap_get_ident(conn); schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, sizeof(info), &info); } if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) && result == L2CAP_CR_SUCCESS) { u8 buf[128]; set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } return 0; } static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; u16 scid, dcid, result, status; struct l2cap_chan *chan; u8 req[128]; int err; scid = __le16_to_cpu(rsp->scid); dcid = __le16_to_cpu(rsp->dcid); result = __le16_to_cpu(rsp->result); status = __le16_to_cpu(rsp->status); BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status); mutex_lock(&conn->chan_lock); if (scid) { chan = __l2cap_get_chan_by_scid(conn, scid); if (!chan) { err = -EFAULT; goto unlock; } } else { chan = __l2cap_get_chan_by_ident(conn, cmd->ident); if (!chan) { err = -EFAULT; goto unlock; } } err = 0; l2cap_chan_lock(chan); switch (result) { case L2CAP_CR_SUCCESS: l2cap_state_change(chan, BT_CONFIG); chan->ident = 0; chan->dcid = dcid; clear_bit(CONF_CONNECT_PEND, &chan->conf_state); if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) break; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, req), req); chan->num_conf_req++; break; case L2CAP_CR_PEND: set_bit(CONF_CONNECT_PEND, &chan->conf_state); break; default: l2cap_chan_del(chan, ECONNREFUSED); break; } l2cap_chan_unlock(chan); unlock: mutex_unlock(&conn->chan_lock); return err; } static inline void set_default_fcs(struct l2cap_chan *chan) { /* FCS is enabled only in ERTM or streaming mode, if one or both * sides request it. */ if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) chan->fcs = L2CAP_FCS_NONE; else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) chan->fcs = L2CAP_FCS_CRC16; } static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) { struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; u16 dcid, flags; u8 rsp[64]; struct l2cap_chan *chan; int len, err = 0; dcid = __le16_to_cpu(req->dcid); flags = __le16_to_cpu(req->flags); BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); chan = l2cap_get_chan_by_scid(conn, dcid); if (!chan) return -ENOENT; if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) { struct l2cap_cmd_rej_cid rej; rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID); rej.scid = cpu_to_le16(chan->scid); rej.dcid = cpu_to_le16(chan->dcid); l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); goto unlock; } /* Reject if config buffer is too small. */ len = cmd_len - sizeof(*req); if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) { l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(chan, rsp, L2CAP_CONF_REJECT, flags), rsp); goto unlock; } /* Store config. */ memcpy(chan->conf_req + chan->conf_len, req->data, len); chan->conf_len += len; if (flags & L2CAP_CONF_FLAG_CONTINUATION) { /* Incomplete config. Send empty response. */ l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(chan, rsp, L2CAP_CONF_SUCCESS, flags), rsp); goto unlock; } /* Complete config. */ len = l2cap_parse_conf_req(chan, rsp); if (len < 0) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto unlock; } l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); chan->num_conf_rsp++; /* Reset config buffer. */ chan->conf_len = 0; if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) goto unlock; if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { set_default_fcs(chan); if (chan->mode == L2CAP_MODE_ERTM || chan->mode == L2CAP_MODE_STREAMING) err = l2cap_ertm_init(chan); if (err < 0) l2cap_send_disconn_req(chan->conn, chan, -err); else l2cap_chan_ready(chan); goto unlock; } if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) { u8 buf[64]; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } /* Got Conf Rsp PENDING from remote side and asume we sent Conf Rsp PENDING in the code above */ if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) && test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { /* check compatibility */ clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); set_bit(CONF_OUTPUT_DONE, &chan->conf_state); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(chan, rsp, L2CAP_CONF_SUCCESS, flags), rsp); } unlock: l2cap_chan_unlock(chan); return err; } static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data; u16 scid, flags, result; struct l2cap_chan *chan; int len = le16_to_cpu(cmd->len) - sizeof(*rsp); int err = 0; scid = __le16_to_cpu(rsp->scid); flags = __le16_to_cpu(rsp->flags); result = __le16_to_cpu(rsp->result); BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x len %d", scid, flags, result, len); chan = l2cap_get_chan_by_scid(conn, scid); if (!chan) return 0; switch (result) { case L2CAP_CONF_SUCCESS: l2cap_conf_rfc_get(chan, rsp->data, len); clear_bit(CONF_REM_CONF_PEND, &chan->conf_state); break; case L2CAP_CONF_PENDING: set_bit(CONF_REM_CONF_PEND, &chan->conf_state); if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { char buf[64]; len = l2cap_parse_conf_rsp(chan, rsp->data, len, buf, &result); if (len < 0) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } /* check compatibility */ clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); set_bit(CONF_OUTPUT_DONE, &chan->conf_state); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(chan, buf, L2CAP_CONF_SUCCESS, 0x0000), buf); } goto done; case L2CAP_CONF_UNACCEPT: if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { char req[64]; if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } /* throw out any old stored conf requests */ result = L2CAP_CONF_SUCCESS; len = l2cap_parse_conf_rsp(chan, rsp->data, len, req, &result); if (len < 0) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, len, req); chan->num_conf_req++; if (result != L2CAP_CONF_SUCCESS) goto done; break; } default: l2cap_chan_set_err(chan, ECONNRESET); __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } if (flags & L2CAP_CONF_FLAG_CONTINUATION) goto done; set_bit(CONF_INPUT_DONE, &chan->conf_state); if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) { set_default_fcs(chan); if (chan->mode == L2CAP_MODE_ERTM || chan->mode == L2CAP_MODE_STREAMING) err = l2cap_ertm_init(chan); if (err < 0) l2cap_send_disconn_req(chan->conn, chan, -err); else l2cap_chan_ready(chan); } done: l2cap_chan_unlock(chan); return err; } static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data; struct l2cap_disconn_rsp rsp; u16 dcid, scid; struct l2cap_chan *chan; struct sock *sk; scid = __le16_to_cpu(req->scid); dcid = __le16_to_cpu(req->dcid); BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); mutex_lock(&conn->chan_lock); chan = __l2cap_get_chan_by_scid(conn, dcid); if (!chan) { mutex_unlock(&conn->chan_lock); return 0; } l2cap_chan_lock(chan); sk = chan->sk; rsp.dcid = cpu_to_le16(chan->scid); rsp.scid = cpu_to_le16(chan->dcid); l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; release_sock(sk); l2cap_chan_hold(chan); l2cap_chan_del(chan, ECONNRESET); l2cap_chan_unlock(chan); chan->ops->close(chan); l2cap_chan_put(chan); mutex_unlock(&conn->chan_lock); return 0; } static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data; u16 dcid, scid; struct l2cap_chan *chan; scid = __le16_to_cpu(rsp->scid); dcid = __le16_to_cpu(rsp->dcid); BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); mutex_lock(&conn->chan_lock); chan = __l2cap_get_chan_by_scid(conn, scid); if (!chan) { mutex_unlock(&conn->chan_lock); return 0; } l2cap_chan_lock(chan); l2cap_chan_hold(chan); l2cap_chan_del(chan, 0); l2cap_chan_unlock(chan); chan->ops->close(chan); l2cap_chan_put(chan); mutex_unlock(&conn->chan_lock); return 0; } static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_info_req *req = (struct l2cap_info_req *) data; u16 type; type = __le16_to_cpu(req->type); BT_DBG("type 0x%4.4x", type); if (type == L2CAP_IT_FEAT_MASK) { u8 buf[8]; u32 feat_mask = l2cap_feat_mask; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; rsp->type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK); rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS); if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; if (enable_hs) feat_mask |= L2CAP_FEAT_EXT_FLOW | L2CAP_FEAT_EXT_WINDOW; put_unaligned_le32(feat_mask, rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else if (type == L2CAP_IT_FIXED_CHAN) { u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; if (enable_hs) l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; else l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; rsp->type = __constant_cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS); memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else { struct l2cap_info_rsp rsp; rsp.type = cpu_to_le16(type); rsp.result = __constant_cpu_to_le16(L2CAP_IR_NOTSUPP); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp); } return 0; } static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data; u16 type, result; type = __le16_to_cpu(rsp->type); result = __le16_to_cpu(rsp->result); BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); /* L2CAP Info req/rsp are unbound to channels, add extra checks */ if (cmd->ident != conn->info_ident || conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) return 0; cancel_delayed_work(&conn->info_timer); if (result != L2CAP_IR_SUCCESS) { conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; l2cap_conn_start(conn); return 0; } switch (type) { case L2CAP_IT_FEAT_MASK: conn->feat_mask = get_unaligned_le32(rsp->data); if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) { struct l2cap_info_req req; req.type = __constant_cpu_to_le16(L2CAP_IT_FIXED_CHAN); conn->info_ident = l2cap_get_ident(conn); l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, sizeof(req), &req); } else { conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; l2cap_conn_start(conn); } break; case L2CAP_IT_FIXED_CHAN: conn->fixed_chan_mask = rsp->data[0]; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; l2cap_conn_start(conn); break; } return 0; } static inline int l2cap_create_channel_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) { struct l2cap_create_chan_req *req = data; struct l2cap_create_chan_rsp rsp; u16 psm, scid; if (cmd_len != sizeof(*req)) return -EPROTO; if (!enable_hs) return -EINVAL; psm = le16_to_cpu(req->psm); scid = le16_to_cpu(req->scid); BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id); /* Placeholder: Always reject */ rsp.dcid = 0; rsp.scid = cpu_to_le16(scid); rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, sizeof(rsp), &rsp); return 0; } static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, void *data) { BT_DBG("conn %p", conn); return l2cap_connect_rsp(conn, cmd, data); } static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, u16 icid, u16 result) { struct l2cap_move_chan_rsp rsp; BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); rsp.icid = cpu_to_le16(icid); rsp.result = cpu_to_le16(result); l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); } static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, struct l2cap_chan *chan, u16 icid, u16 result) { struct l2cap_move_chan_cfm cfm; u8 ident; BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); ident = l2cap_get_ident(conn); if (chan) chan->ident = ident; cfm.icid = cpu_to_le16(icid); cfm.result = cpu_to_le16(result); l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); } static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, u16 icid) { struct l2cap_move_chan_cfm_rsp rsp; BT_DBG("icid 0x%4.4x", icid); rsp.icid = cpu_to_le16(icid); l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); } static inline int l2cap_move_channel_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) { struct l2cap_move_chan_req *req = data; u16 icid = 0; u16 result = L2CAP_MR_NOT_ALLOWED; if (cmd_len != sizeof(*req)) return -EPROTO; icid = le16_to_cpu(req->icid); BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id); if (!enable_hs) return -EINVAL; /* Placeholder: Always refuse */ l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); return 0; } static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) { struct l2cap_move_chan_rsp *rsp = data; u16 icid, result; if (cmd_len != sizeof(*rsp)) return -EPROTO; icid = le16_to_cpu(rsp->icid); result = le16_to_cpu(rsp->result); BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); /* Placeholder: Always unconfirmed */ l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED); return 0; } static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) { struct l2cap_move_chan_cfm *cfm = data; u16 icid, result; if (cmd_len != sizeof(*cfm)) return -EPROTO; icid = le16_to_cpu(cfm->icid); result = le16_to_cpu(cfm->result); BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); return 0; } static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) { struct l2cap_move_chan_cfm_rsp *rsp = data; u16 icid; if (cmd_len != sizeof(*rsp)) return -EPROTO; icid = le16_to_cpu(rsp->icid); BT_DBG("icid 0x%4.4x", icid); return 0; } static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, u16 to_multiplier) { u16 max_latency; if (min > max || min < 6 || max > 3200) return -EINVAL; if (to_multiplier < 10 || to_multiplier > 3200) return -EINVAL; if (max >= to_multiplier * 8) return -EINVAL; max_latency = (to_multiplier * 8 / max) - 1; if (latency > 499 || latency > max_latency) return -EINVAL; return 0; } static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct hci_conn *hcon = conn->hcon; struct l2cap_conn_param_update_req *req; struct l2cap_conn_param_update_rsp rsp; u16 min, max, latency, to_multiplier, cmd_len; int err; if (!(hcon->link_mode & HCI_LM_MASTER)) return -EINVAL; cmd_len = __le16_to_cpu(cmd->len); if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) return -EPROTO; req = (struct l2cap_conn_param_update_req *) data; min = __le16_to_cpu(req->min); max = __le16_to_cpu(req->max); latency = __le16_to_cpu(req->latency); to_multiplier = __le16_to_cpu(req->to_multiplier); BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x", min, max, latency, to_multiplier); memset(&rsp, 0, sizeof(rsp)); err = l2cap_check_conn_param(min, max, latency, to_multiplier); if (err) rsp.result = __constant_cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); else rsp.result = __constant_cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); if (!err) hci_le_conn_update(hcon, min, max, latency, to_multiplier); return 0; } static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) { int err = 0; switch (cmd->code) { case L2CAP_COMMAND_REJ: l2cap_command_rej(conn, cmd, data); break; case L2CAP_CONN_REQ: err = l2cap_connect_req(conn, cmd, data); break; case L2CAP_CONN_RSP: err = l2cap_connect_rsp(conn, cmd, data); break; case L2CAP_CONF_REQ: err = l2cap_config_req(conn, cmd, cmd_len, data); break; case L2CAP_CONF_RSP: err = l2cap_config_rsp(conn, cmd, data); break; case L2CAP_DISCONN_REQ: err = l2cap_disconnect_req(conn, cmd, data); break; case L2CAP_DISCONN_RSP: err = l2cap_disconnect_rsp(conn, cmd, data); break; case L2CAP_ECHO_REQ: l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data); break; case L2CAP_ECHO_RSP: break; case L2CAP_INFO_REQ: err = l2cap_information_req(conn, cmd, data); break; case L2CAP_INFO_RSP: err = l2cap_information_rsp(conn, cmd, data); break; case L2CAP_CREATE_CHAN_REQ: err = l2cap_create_channel_req(conn, cmd, cmd_len, data); break; case L2CAP_CREATE_CHAN_RSP: err = l2cap_create_channel_rsp(conn, cmd, data); break; case L2CAP_MOVE_CHAN_REQ: err = l2cap_move_channel_req(conn, cmd, cmd_len, data); break; case L2CAP_MOVE_CHAN_RSP: err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data); break; case L2CAP_MOVE_CHAN_CFM: err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data); break; case L2CAP_MOVE_CHAN_CFM_RSP: err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data); break; default: BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code); err = -EINVAL; break; } return err; } static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { switch (cmd->code) { case L2CAP_COMMAND_REJ: return 0; case L2CAP_CONN_PARAM_UPDATE_REQ: return l2cap_conn_param_update_req(conn, cmd, data); case L2CAP_CONN_PARAM_UPDATE_RSP: return 0; default: BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); return -EINVAL; } } static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { u8 *data = skb->data; int len = skb->len; struct l2cap_cmd_hdr cmd; int err; l2cap_raw_recv(conn, skb); while (len >= L2CAP_CMD_HDR_SIZE) { u16 cmd_len; memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); data += L2CAP_CMD_HDR_SIZE; len -= L2CAP_CMD_HDR_SIZE; cmd_len = le16_to_cpu(cmd.len); BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident); if (cmd_len > len || !cmd.ident) { BT_DBG("corrupted command"); break; } if (conn->hcon->type == LE_LINK) err = l2cap_le_sig_cmd(conn, &cmd, data); else err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data); if (err) { struct l2cap_cmd_rej_unk rej; BT_ERR("Wrong link type (%d)", err); /* FIXME: Map err to a valid reason */ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); } data += cmd_len; len -= cmd_len; } kfree_skb(skb); } static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) { u16 our_fcs, rcv_fcs; int hdr_size; if (test_bit(FLAG_EXT_CTRL, &chan->flags)) hdr_size = L2CAP_EXT_HDR_SIZE; else hdr_size = L2CAP_ENH_HDR_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) { skb_trim(skb, skb->len - L2CAP_FCS_SIZE); rcv_fcs = get_unaligned_le16(skb->data + skb->len); our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); if (our_fcs != rcv_fcs) return -EBADMSG; } return 0; } static void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) { struct l2cap_ctrl control; BT_DBG("chan %p", chan); memset(&control, 0, sizeof(control)); control.sframe = 1; control.final = 1; control.reqseq = chan->buffer_seq; set_bit(CONN_SEND_FBIT, &chan->conn_state); if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control.super = L2CAP_SUPER_RNR; l2cap_send_sframe(chan, &control); } if (test_and_clear_bit(CONN_REMOTE_BUSY, &chan->conn_state) && chan->unacked_frames > 0) __set_retrans_timer(chan); /* Send pending iframes */ l2cap_ertm_send(chan); if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && test_bit(CONN_SEND_FBIT, &chan->conn_state)) { /* F-bit wasn't sent in an s-frame or i-frame yet, so * send it now. */ control.super = L2CAP_SUPER_RR; l2cap_send_sframe(chan, &control); } } static void append_skb_frag(struct sk_buff *skb, struct sk_buff *new_frag, struct sk_buff **last_frag) { /* skb->len reflects data in skb as well as all fragments * skb->data_len reflects only data in fragments */ if (!skb_has_frag_list(skb)) skb_shinfo(skb)->frag_list = new_frag; new_frag->next = NULL; (*last_frag)->next = new_frag; *last_frag = new_frag; skb->len += new_frag->len; skb->data_len += new_frag->len; skb->truesize += new_frag->truesize; } static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, struct l2cap_ctrl *control) { int err = -EINVAL; switch (control->sar) { case L2CAP_SAR_UNSEGMENTED: if (chan->sdu) break; err = chan->ops->recv(chan, skb); break; case L2CAP_SAR_START: if (chan->sdu) break; chan->sdu_len = get_unaligned_le16(skb->data); skb_pull(skb, L2CAP_SDULEN_SIZE); if (chan->sdu_len > chan->imtu) { err = -EMSGSIZE; break; } if (skb->len >= chan->sdu_len) break; chan->sdu = skb; chan->sdu_last_frag = skb; skb = NULL; err = 0; break; case L2CAP_SAR_CONTINUE: if (!chan->sdu) break; append_skb_frag(chan->sdu, skb, &chan->sdu_last_frag); skb = NULL; if (chan->sdu->len >= chan->sdu_len) break; err = 0; break; case L2CAP_SAR_END: if (!chan->sdu) break; append_skb_frag(chan->sdu, skb, &chan->sdu_last_frag); skb = NULL; if (chan->sdu->len != chan->sdu_len) break; err = chan->ops->recv(chan, chan->sdu); if (!err) { /* Reassembly complete */ chan->sdu = NULL; chan->sdu_last_frag = NULL; chan->sdu_len = 0; } break; } if (err) { kfree_skb(skb); kfree_skb(chan->sdu); chan->sdu = NULL; chan->sdu_last_frag = NULL; chan->sdu_len = 0; } return err; } void l2cap_chan_busy(struct l2cap_chan *chan, int busy) { u8 event; if (chan->mode != L2CAP_MODE_ERTM) return; event = busy ? L2CAP_EV_LOCAL_BUSY_DETECTED : L2CAP_EV_LOCAL_BUSY_CLEAR; l2cap_tx(chan, NULL, NULL, event); } static int l2cap_rx_queued_iframes(struct l2cap_chan *chan) { int err = 0; /* Pass sequential frames to l2cap_reassemble_sdu() * until a gap is encountered. */ BT_DBG("chan %p", chan); while (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { struct sk_buff *skb; BT_DBG("Searching for skb with txseq %d (queue len %d)", chan->buffer_seq, skb_queue_len(&chan->srej_q)); skb = l2cap_ertm_seq_in_queue(&chan->srej_q, chan->buffer_seq); if (!skb) break; skb_unlink(skb, &chan->srej_q); chan->buffer_seq = __next_seq(chan, chan->buffer_seq); err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->control); if (err) break; } if (skb_queue_empty(&chan->srej_q)) { chan->rx_state = L2CAP_RX_STATE_RECV; l2cap_send_ack(chan); } return err; } static void l2cap_handle_srej(struct l2cap_chan *chan, struct l2cap_ctrl *control) { struct sk_buff *skb; BT_DBG("chan %p, control %p", chan, control); if (control->reqseq == chan->next_tx_seq) { BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); return; } skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq); if (skb == NULL) { BT_DBG("Seq %d not available for retransmission", control->reqseq); return; } if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); return; } clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); if (control->poll) { l2cap_pass_to_tx(chan, control); set_bit(CONN_SEND_FBIT, &chan->conn_state); l2cap_retransmit(chan, control); l2cap_ertm_send(chan); if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) { set_bit(CONN_SREJ_ACT, &chan->conn_state); chan->srej_save_reqseq = control->reqseq; } } else { l2cap_pass_to_tx_fbit(chan, control); if (control->final) { if (chan->srej_save_reqseq != control->reqseq || !test_and_clear_bit(CONN_SREJ_ACT, &chan->conn_state)) l2cap_retransmit(chan, control); } else { l2cap_retransmit(chan, control); if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) { set_bit(CONN_SREJ_ACT, &chan->conn_state); chan->srej_save_reqseq = control->reqseq; } } } } static void l2cap_handle_rej(struct l2cap_chan *chan, struct l2cap_ctrl *control) { struct sk_buff *skb; BT_DBG("chan %p, control %p", chan, control); if (control->reqseq == chan->next_tx_seq) { BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); return; } skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq); if (chan->max_tx && skb && bt_cb(skb)->control.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); return; } clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); l2cap_pass_to_tx(chan, control); if (control->final) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_all(chan, control); } else { l2cap_retransmit_all(chan, control); l2cap_ertm_send(chan); if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) set_bit(CONN_REJ_ACT, &chan->conn_state); } } static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq) { BT_DBG("chan %p, txseq %d", chan, txseq); BT_DBG("last_acked_seq %d, expected_tx_seq %d", chan->last_acked_seq, chan->expected_tx_seq); if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) { if (__seq_offset(chan, txseq, chan->last_acked_seq) >= chan->tx_win) { /* See notes below regarding "double poll" and * invalid packets. */ if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) { BT_DBG("Invalid/Ignore - after SREJ"); return L2CAP_TXSEQ_INVALID_IGNORE; } else { BT_DBG("Invalid - in window after SREJ sent"); return L2CAP_TXSEQ_INVALID; } } if (chan->srej_list.head == txseq) { BT_DBG("Expected SREJ"); return L2CAP_TXSEQ_EXPECTED_SREJ; } if (l2cap_ertm_seq_in_queue(&chan->srej_q, txseq)) { BT_DBG("Duplicate SREJ - txseq already stored"); return L2CAP_TXSEQ_DUPLICATE_SREJ; } if (l2cap_seq_list_contains(&chan->srej_list, txseq)) { BT_DBG("Unexpected SREJ - not requested"); return L2CAP_TXSEQ_UNEXPECTED_SREJ; } } if (chan->expected_tx_seq == txseq) { if (__seq_offset(chan, txseq, chan->last_acked_seq) >= chan->tx_win) { BT_DBG("Invalid - txseq outside tx window"); return L2CAP_TXSEQ_INVALID; } else { BT_DBG("Expected"); return L2CAP_TXSEQ_EXPECTED; } } if (__seq_offset(chan, txseq, chan->last_acked_seq) < __seq_offset(chan, chan->expected_tx_seq, chan->last_acked_seq)){ BT_DBG("Duplicate - expected_tx_seq later than txseq"); return L2CAP_TXSEQ_DUPLICATE; } if (__seq_offset(chan, txseq, chan->last_acked_seq) >= chan->tx_win) { /* A source of invalid packets is a "double poll" condition, * where delays cause us to send multiple poll packets. If * the remote stack receives and processes both polls, * sequence numbers can wrap around in such a way that a * resent frame has a sequence number that looks like new data * with a sequence gap. This would trigger an erroneous SREJ * request. * * Fortunately, this is impossible with a tx window that's * less than half of the maximum sequence number, which allows * invalid frames to be safely ignored. * * With tx window sizes greater than half of the tx window * maximum, the frame is invalid and cannot be ignored. This * causes a disconnect. */ if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) { BT_DBG("Invalid/Ignore - txseq outside tx window"); return L2CAP_TXSEQ_INVALID_IGNORE; } else { BT_DBG("Invalid - txseq outside tx window"); return L2CAP_TXSEQ_INVALID; } } else { BT_DBG("Unexpected - txseq indicates missing frames"); return L2CAP_TXSEQ_UNEXPECTED; } } static int l2cap_rx_state_recv(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff *skb, u8 event) { int err = 0; bool skb_in_use = 0; BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb, event); switch (event) { case L2CAP_EV_RECV_IFRAME: switch (l2cap_classify_txseq(chan, control->txseq)) { case L2CAP_TXSEQ_EXPECTED: l2cap_pass_to_tx(chan, control); if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { BT_DBG("Busy, discarding expected seq %d", control->txseq); break; } chan->expected_tx_seq = __next_seq(chan, control->txseq); chan->buffer_seq = chan->expected_tx_seq; skb_in_use = 1; err = l2cap_reassemble_sdu(chan, skb, control); if (err) break; if (control->final) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) { control->final = 0; l2cap_retransmit_all(chan, control); l2cap_ertm_send(chan); } } if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) l2cap_send_ack(chan); break; case L2CAP_TXSEQ_UNEXPECTED: l2cap_pass_to_tx(chan, control); /* Can't issue SREJ frames in the local busy state. * Drop this frame, it will be seen as missing * when local busy is exited. */ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { BT_DBG("Busy, discarding unexpected seq %d", control->txseq); break; } /* There was a gap in the sequence, so an SREJ * must be sent for each missing frame. The * current frame is stored for later use. */ skb_queue_tail(&chan->srej_q, skb); skb_in_use = 1; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); clear_bit(CONN_SREJ_ACT, &chan->conn_state); l2cap_seq_list_clear(&chan->srej_list); l2cap_send_srej(chan, control->txseq); chan->rx_state = L2CAP_RX_STATE_SREJ_SENT; break; case L2CAP_TXSEQ_DUPLICATE: l2cap_pass_to_tx(chan, control); break; case L2CAP_TXSEQ_INVALID_IGNORE: break; case L2CAP_TXSEQ_INVALID: default: l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); break; } break; case L2CAP_EV_RECV_RR: l2cap_pass_to_tx(chan, control); if (control->final) { clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) { control->final = 0; l2cap_retransmit_all(chan, control); } l2cap_ertm_send(chan); } else if (control->poll) { l2cap_send_i_or_rr_or_rnr(chan); } else { if (test_and_clear_bit(CONN_REMOTE_BUSY, &chan->conn_state) && chan->unacked_frames) __set_retrans_timer(chan); l2cap_ertm_send(chan); } break; case L2CAP_EV_RECV_RNR: set_bit(CONN_REMOTE_BUSY, &chan->conn_state); l2cap_pass_to_tx(chan, control); if (control && control->poll) { set_bit(CONN_SEND_FBIT, &chan->conn_state); l2cap_send_rr_or_rnr(chan, 0); } __clear_retrans_timer(chan); l2cap_seq_list_clear(&chan->retrans_list); break; case L2CAP_EV_RECV_REJ: l2cap_handle_rej(chan, control); break; case L2CAP_EV_RECV_SREJ: l2cap_handle_srej(chan, control); break; default: break; } if (skb && !skb_in_use) { BT_DBG("Freeing %p", skb); kfree_skb(skb); } return err; } static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff *skb, u8 event) { int err = 0; u16 txseq = control->txseq; bool skb_in_use = 0; BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb, event); switch (event) { case L2CAP_EV_RECV_IFRAME: switch (l2cap_classify_txseq(chan, txseq)) { case L2CAP_TXSEQ_EXPECTED: /* Keep frame for reassembly later */ l2cap_pass_to_tx(chan, control); skb_queue_tail(&chan->srej_q, skb); skb_in_use = 1; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); chan->expected_tx_seq = __next_seq(chan, txseq); break; case L2CAP_TXSEQ_EXPECTED_SREJ: l2cap_seq_list_pop(&chan->srej_list); l2cap_pass_to_tx(chan, control); skb_queue_tail(&chan->srej_q, skb); skb_in_use = 1; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); err = l2cap_rx_queued_iframes(chan); if (err) break; break; case L2CAP_TXSEQ_UNEXPECTED: /* Got a frame that can't be reassembled yet. * Save it for later, and send SREJs to cover * the missing frames. */ skb_queue_tail(&chan->srej_q, skb); skb_in_use = 1; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); l2cap_pass_to_tx(chan, control); l2cap_send_srej(chan, control->txseq); break; case L2CAP_TXSEQ_UNEXPECTED_SREJ: /* This frame was requested with an SREJ, but * some expected retransmitted frames are * missing. Request retransmission of missing * SREJ'd frames. */ skb_queue_tail(&chan->srej_q, skb); skb_in_use = 1; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); l2cap_pass_to_tx(chan, control); l2cap_send_srej_list(chan, control->txseq); break; case L2CAP_TXSEQ_DUPLICATE_SREJ: /* We've already queued this frame. Drop this copy. */ l2cap_pass_to_tx(chan, control); break; case L2CAP_TXSEQ_DUPLICATE: /* Expecting a later sequence number, so this frame * was already received. Ignore it completely. */ break; case L2CAP_TXSEQ_INVALID_IGNORE: break; case L2CAP_TXSEQ_INVALID: default: l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); break; } break; case L2CAP_EV_RECV_RR: l2cap_pass_to_tx(chan, control); if (control->final) { clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) { control->final = 0; l2cap_retransmit_all(chan, control); } l2cap_ertm_send(chan); } else if (control->poll) { if (test_and_clear_bit(CONN_REMOTE_BUSY, &chan->conn_state) && chan->unacked_frames) { __set_retrans_timer(chan); } set_bit(CONN_SEND_FBIT, &chan->conn_state); l2cap_send_srej_tail(chan); } else { if (test_and_clear_bit(CONN_REMOTE_BUSY, &chan->conn_state) && chan->unacked_frames) __set_retrans_timer(chan); l2cap_send_ack(chan); } break; case L2CAP_EV_RECV_RNR: set_bit(CONN_REMOTE_BUSY, &chan->conn_state); l2cap_pass_to_tx(chan, control); if (control->poll) { l2cap_send_srej_tail(chan); } else { struct l2cap_ctrl rr_control; memset(&rr_control, 0, sizeof(rr_control)); rr_control.sframe = 1; rr_control.super = L2CAP_SUPER_RR; rr_control.reqseq = chan->buffer_seq; l2cap_send_sframe(chan, &rr_control); } break; case L2CAP_EV_RECV_REJ: l2cap_handle_rej(chan, control); break; case L2CAP_EV_RECV_SREJ: l2cap_handle_srej(chan, control); break; } if (skb && !skb_in_use) { BT_DBG("Freeing %p", skb); kfree_skb(skb); } return err; } static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq) { /* Make sure reqseq is for a packet that has been sent but not acked */ u16 unacked; unacked = __seq_offset(chan, chan->next_tx_seq, chan->expected_ack_seq); return __seq_offset(chan, chan->next_tx_seq, reqseq) <= unacked; } static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff *skb, u8 event) { int err = 0; BT_DBG("chan %p, control %p, skb %p, event %d, state %d", chan, control, skb, event, chan->rx_state); if (__valid_reqseq(chan, control->reqseq)) { switch (chan->rx_state) { case L2CAP_RX_STATE_RECV: err = l2cap_rx_state_recv(chan, control, skb, event); break; case L2CAP_RX_STATE_SREJ_SENT: err = l2cap_rx_state_srej_sent(chan, control, skb, event); break; default: /* shut it down */ break; } } else { BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d", control->reqseq, chan->next_tx_seq, chan->expected_ack_seq); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); } return err; } static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff *skb) { int err = 0; BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb, chan->rx_state); if (l2cap_classify_txseq(chan, control->txseq) == L2CAP_TXSEQ_EXPECTED) { l2cap_pass_to_tx(chan, control); BT_DBG("buffer_seq %d->%d", chan->buffer_seq, __next_seq(chan, chan->buffer_seq)); chan->buffer_seq = __next_seq(chan, chan->buffer_seq); l2cap_reassemble_sdu(chan, skb, control); } else { if (chan->sdu) { kfree_skb(chan->sdu); chan->sdu = NULL; } chan->sdu_last_frag = NULL; chan->sdu_len = 0; if (skb) { BT_DBG("Freeing %p", skb); kfree_skb(skb); } } chan->last_acked_seq = control->txseq; chan->expected_tx_seq = __next_seq(chan, control->txseq); return err; } static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) { struct l2cap_ctrl *control = &bt_cb(skb)->control; u16 len; u8 event; __unpack_control(chan, skb); len = skb->len; /* * We can just drop the corrupted I-frame here. * Receiver will miss it and start proper recovery * procedures and ask for retransmission. */ if (l2cap_check_fcs(chan, skb)) goto drop; if (!control->sframe && control->sar == L2CAP_SAR_START) len -= L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) len -= L2CAP_FCS_SIZE; if (len > chan->mps) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } if (!control->sframe) { int err; BT_DBG("iframe sar %d, reqseq %d, final %d, txseq %d", control->sar, control->reqseq, control->final, control->txseq); /* Validate F-bit - F=0 always valid, F=1 only * valid in TX WAIT_F */ if (control->final && chan->tx_state != L2CAP_TX_STATE_WAIT_F) goto drop; if (chan->mode != L2CAP_MODE_STREAMING) { event = L2CAP_EV_RECV_IFRAME; err = l2cap_rx(chan, control, skb, event); } else { err = l2cap_stream_rx(chan, control, skb); } if (err) l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); } else { const u8 rx_func_to_event[4] = { L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ, L2CAP_EV_RECV_RNR, L2CAP_EV_RECV_SREJ }; /* Only I-frames are expected in streaming mode */ if (chan->mode == L2CAP_MODE_STREAMING) goto drop; BT_DBG("sframe reqseq %d, final %d, poll %d, super %d", control->reqseq, control->final, control->poll, control->super); if (len != 0) { BT_ERR("%d", len); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } /* Validate F and P bits */ if (control->final && (control->poll || chan->tx_state != L2CAP_TX_STATE_WAIT_F)) goto drop; event = rx_func_to_event[control->super]; if (l2cap_rx(chan, control, skb, event)) l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); } return 0; drop: kfree_skb(skb); return 0; } static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) { struct l2cap_chan *chan; chan = l2cap_get_chan_by_scid(conn, cid); if (!chan) { if (cid == L2CAP_CID_A2MP) { chan = a2mp_channel_create(conn, skb); if (!chan) { kfree_skb(skb); return; } l2cap_chan_lock(chan); } else { BT_DBG("unknown cid 0x%4.4x", cid); /* Drop packet and return */ kfree_skb(skb); return; } } BT_DBG("chan %p, len %d", chan, skb->len); if (chan->state != BT_CONNECTED) goto drop; switch (chan->mode) { case L2CAP_MODE_BASIC: /* If socket recv buffers overflows we drop data here * which is *bad* because L2CAP has to be reliable. * But we don't have any other choice. L2CAP doesn't * provide flow control mechanism. */ if (chan->imtu < skb->len) goto drop; if (!chan->ops->recv(chan, skb)) goto done; break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: l2cap_data_rcv(chan, skb); goto done; default: BT_DBG("chan %p: bad mode 0x%2.2x", chan, chan->mode); break; } drop: kfree_skb(skb); done: l2cap_chan_unlock(chan); } static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb) { struct l2cap_chan *chan; chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst); if (!chan) goto drop; BT_DBG("chan %p, len %d", chan, skb->len); if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; if (chan->imtu < skb->len) goto drop; if (!chan->ops->recv(chan, skb)) return; drop: kfree_skb(skb); } static void l2cap_att_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) { struct l2cap_chan *chan; chan = l2cap_global_chan_by_scid(0, cid, conn->src, conn->dst); if (!chan) goto drop; BT_DBG("chan %p, len %d", chan, skb->len); if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; if (chan->imtu < skb->len) goto drop; if (!chan->ops->recv(chan, skb)) return; drop: kfree_skb(skb); } static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) { struct l2cap_hdr *lh = (void *) skb->data; u16 cid, len; __le16 psm; skb_pull(skb, L2CAP_HDR_SIZE); cid = __le16_to_cpu(lh->cid); len = __le16_to_cpu(lh->len); if (len != skb->len) { kfree_skb(skb); return; } BT_DBG("len %d, cid 0x%4.4x", len, cid); switch (cid) { case L2CAP_CID_LE_SIGNALING: case L2CAP_CID_SIGNALING: l2cap_sig_channel(conn, skb); break; case L2CAP_CID_CONN_LESS: psm = get_unaligned((__le16 *) skb->data); skb_pull(skb, L2CAP_PSMLEN_SIZE); l2cap_conless_channel(conn, psm, skb); break; case L2CAP_CID_LE_DATA: l2cap_att_channel(conn, cid, skb); break; case L2CAP_CID_SMP: if (smp_sig_channel(conn, skb)) l2cap_conn_del(conn->hcon, EACCES); break; default: l2cap_data_channel(conn, cid, skb); break; } } /* ---- L2CAP interface with lower layer (HCI) ---- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) { int exact = 0, lm1 = 0, lm2 = 0; struct l2cap_chan *c; BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); /* Find listening sockets and check their link_mode */ read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; if (c->state != BT_LISTEN) continue; if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { lm1 |= HCI_LM_ACCEPT; if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm1 |= HCI_LM_MASTER; exact++; } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { lm2 |= HCI_LM_ACCEPT; if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm2 |= HCI_LM_MASTER; } } read_unlock(&chan_list_lock); return exact ? lm1 : lm2; } void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn; BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); if (!status) { conn = l2cap_conn_add(hcon, status); if (conn) l2cap_conn_ready(conn); } else l2cap_conn_del(hcon, bt_to_errno(status)); } int l2cap_disconn_ind(struct hci_conn *hcon) { struct l2cap_conn *conn = hcon->l2cap_data; BT_DBG("hcon %p", hcon); if (!conn) return HCI_ERROR_REMOTE_USER_TERM; return conn->disc_reason; } void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); l2cap_conn_del(hcon, bt_to_errno(reason)); } static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) return; if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { __set_chan_timer(chan, L2CAP_ENC_TIMEOUT); } else if (chan->sec_level == BT_SECURITY_HIGH) l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) __clear_chan_timer(chan); } } int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) { struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_chan *chan; if (!conn) return 0; BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt); if (hcon->type == LE_LINK) { if (!status && encrypt) smp_distribute_keys(conn, 0); cancel_delayed_work(&conn->security_timer); } mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { l2cap_chan_lock(chan); BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid, state_to_string(chan->state)); if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { l2cap_chan_unlock(chan); continue; } if (chan->scid == L2CAP_CID_LE_DATA) { if (!status && encrypt) { chan->sec_level = hcon->sec_level; l2cap_chan_ready(chan); } l2cap_chan_unlock(chan); continue; } if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { l2cap_chan_unlock(chan); continue; } if (!status && (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)) { struct sock *sk = chan->sk; clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); sk->sk_state_change(sk); l2cap_check_encryption(chan, encrypt); l2cap_chan_unlock(chan); continue; } if (chan->state == BT_CONNECT) { if (!status) { l2cap_send_conn_req(chan); } else { __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } } else if (chan->state == BT_CONNECT2) { struct sock *sk = chan->sk; struct l2cap_conn_rsp rsp; __u16 res, stat; lock_sock(sk); if (!status) { if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { struct sock *parent = bt_sk(sk)->parent; res = L2CAP_CR_PEND; stat = L2CAP_CS_AUTHOR_PEND; if (parent) parent->sk_data_ready(parent, 0); } else { __l2cap_state_change(chan, BT_CONFIG); res = L2CAP_CR_SUCCESS; stat = L2CAP_CS_NO_INFO; } } else { __l2cap_state_change(chan, BT_DISCONN); __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); res = L2CAP_CR_SEC_BLOCK; stat = L2CAP_CS_NO_INFO; } release_sock(sk); rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); rsp.result = cpu_to_le16(res); rsp.status = cpu_to_le16(stat); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); if (!test_bit(CONF_REQ_SENT, &chan->conf_state) && res == L2CAP_CR_SUCCESS) { char buf[128]; set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } } l2cap_chan_unlock(chan); } mutex_unlock(&conn->chan_lock); return 0; } int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) { struct l2cap_conn *conn = hcon->l2cap_data; if (!conn) conn = l2cap_conn_add(hcon, 0); if (!conn) goto drop; BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); if (!(flags & ACL_CONT)) { struct l2cap_hdr *hdr; int len; if (conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; conn->rx_len = 0; l2cap_conn_unreliable(conn, ECOMM); } /* Start fragment always begin with Basic L2CAP header */ if (skb->len < L2CAP_HDR_SIZE) { BT_ERR("Frame is too short (len %d)", skb->len); l2cap_conn_unreliable(conn, ECOMM); goto drop; } hdr = (struct l2cap_hdr *) skb->data; len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; if (len == skb->len) { /* Complete frame received */ l2cap_recv_frame(conn, skb); return 0; } BT_DBG("Start: total len %d, frag len %d", len, skb->len); if (skb->len > len) { BT_ERR("Frame is too long (len %d, expected len %d)", skb->len, len); l2cap_conn_unreliable(conn, ECOMM); goto drop; } /* Allocate skb for the complete frame (with header) */ conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC); if (!conn->rx_skb) goto drop; skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len = len - skb->len; } else { BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); if (!conn->rx_len) { BT_ERR("Unexpected continuation frame (len %d)", skb->len); l2cap_conn_unreliable(conn, ECOMM); goto drop; } if (skb->len > conn->rx_len) { BT_ERR("Fragment is too long (len %d, expected %d)", skb->len, conn->rx_len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; conn->rx_len = 0; l2cap_conn_unreliable(conn, ECOMM); goto drop; } skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len -= skb->len; if (!conn->rx_len) { /* Complete frame received */ l2cap_recv_frame(conn, conn->rx_skb); conn->rx_skb = NULL; } } drop: kfree_skb(skb); return 0; } static int l2cap_debugfs_show(struct seq_file *f, void *p) { struct l2cap_chan *c; read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), c->state, __le16_to_cpu(c->psm), c->scid, c->dcid, c->imtu, c->omtu, c->sec_level, c->mode); } read_unlock(&chan_list_lock); return 0; } static int l2cap_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, l2cap_debugfs_show, inode->i_private); } static const struct file_operations l2cap_debugfs_fops = { .open = l2cap_debugfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *l2cap_debugfs; int __init l2cap_init(void) { int err; err = l2cap_init_sockets(); if (err < 0) return err; if (bt_debugfs) { l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs, NULL, &l2cap_debugfs_fops); if (!l2cap_debugfs) BT_ERR("Failed to create L2CAP debug file"); } return 0; } void l2cap_exit(void) { debugfs_remove(l2cap_debugfs); l2cap_cleanup_sockets(); } module_param(disable_ertm, bool, 0644); MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); compat-drivers-2012-09-18/net/bluetooth/hci_conn.c0000644000175000017500000005512212026211315021104 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth HCI connection handling. */ #include #include #include #include #include static void hci_le_create_connection(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; struct hci_cp_le_create_conn cp; conn->state = BT_CONNECT; conn->out = true; conn->link_mode |= HCI_LM_MASTER; conn->sec_level = BT_SECURITY_LOW; memset(&cp, 0, sizeof(cp)); cp.scan_interval = __constant_cpu_to_le16(0x0060); cp.scan_window = __constant_cpu_to_le16(0x0030); bacpy(&cp.peer_addr, &conn->dst); cp.peer_addr_type = conn->dst_type; cp.conn_interval_min = __constant_cpu_to_le16(0x0028); cp.conn_interval_max = __constant_cpu_to_le16(0x0038); cp.supervision_timeout = __constant_cpu_to_le16(0x002a); cp.min_ce_len = __constant_cpu_to_le16(0x0000); cp.max_ce_len = __constant_cpu_to_le16(0x0000); hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); } static void hci_le_create_connection_cancel(struct hci_conn *conn) { hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); } static void hci_acl_create_connection(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; struct inquiry_entry *ie; struct hci_cp_create_conn cp; BT_DBG("hcon %p", conn); conn->state = BT_CONNECT; conn->out = true; conn->link_mode = HCI_LM_MASTER; conn->attempt++; conn->link_policy = hdev->link_policy; memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; ie = hci_inquiry_cache_lookup(hdev, &conn->dst); if (ie) { if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { cp.pscan_rep_mode = ie->data.pscan_rep_mode; cp.pscan_mode = ie->data.pscan_mode; cp.clock_offset = ie->data.clock_offset | __constant_cpu_to_le16(0x8000); } memcpy(conn->dev_class, ie->data.dev_class, 3); if (ie->data.ssp_mode > 0) set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } cp.pkt_type = cpu_to_le16(conn->pkt_type); if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) cp.role_switch = 0x01; else cp.role_switch = 0x00; hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp); } static void hci_acl_create_connection_cancel(struct hci_conn *conn) { struct hci_cp_create_conn_cancel cp; BT_DBG("hcon %p", conn); if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2) return; bacpy(&cp.bdaddr, &conn->dst); hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp); } void hci_acl_disconn(struct hci_conn *conn, __u8 reason) { struct hci_cp_disconnect cp; BT_DBG("hcon %p", conn); conn->state = BT_DISCONN; cp.handle = cpu_to_le16(conn->handle); cp.reason = reason; hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); } static void hci_add_sco(struct hci_conn *conn, __u16 handle) { struct hci_dev *hdev = conn->hdev; struct hci_cp_add_sco cp; BT_DBG("hcon %p", conn); conn->state = BT_CONNECT; conn->out = true; conn->attempt++; cp.handle = cpu_to_le16(handle); cp.pkt_type = cpu_to_le16(conn->pkt_type); hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp); } void hci_setup_sync(struct hci_conn *conn, __u16 handle) { struct hci_dev *hdev = conn->hdev; struct hci_cp_setup_sync_conn cp; BT_DBG("hcon %p", conn); conn->state = BT_CONNECT; conn->out = true; conn->attempt++; cp.handle = cpu_to_le16(handle); cp.pkt_type = cpu_to_le16(conn->pkt_type); cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40); cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40); cp.max_latency = __constant_cpu_to_le16(0xffff); cp.voice_setting = cpu_to_le16(hdev->voice_setting); cp.retrans_effort = 0xff; hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); } void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier) { struct hci_cp_le_conn_update cp; struct hci_dev *hdev = conn->hdev; memset(&cp, 0, sizeof(cp)); cp.handle = cpu_to_le16(conn->handle); cp.conn_interval_min = cpu_to_le16(min); cp.conn_interval_max = cpu_to_le16(max); cp.conn_latency = cpu_to_le16(latency); cp.supervision_timeout = cpu_to_le16(to_multiplier); cp.min_ce_len = __constant_cpu_to_le16(0x0001); cp.max_ce_len = __constant_cpu_to_le16(0x0001); hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); } void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], __u8 ltk[16]) { struct hci_dev *hdev = conn->hdev; struct hci_cp_le_start_enc cp; BT_DBG("hcon %p", conn); memset(&cp, 0, sizeof(cp)); cp.handle = cpu_to_le16(conn->handle); memcpy(cp.ltk, ltk, sizeof(cp.ltk)); cp.ediv = ediv; memcpy(cp.rand, rand, sizeof(cp.rand)); hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); } /* Device _must_ be locked */ void hci_sco_setup(struct hci_conn *conn, __u8 status) { struct hci_conn *sco = conn->link; if (!sco) return; BT_DBG("hcon %p", conn); if (!status) { if (lmp_esco_capable(conn->hdev)) hci_setup_sync(sco, conn->handle); else hci_add_sco(sco, conn->handle); } else { hci_proto_connect_cfm(sco, status); hci_conn_del(sco); } } static void hci_conn_timeout(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, disc_work.work); __u8 reason; BT_DBG("hcon %p state %s", conn, state_to_string(conn->state)); if (atomic_read(&conn->refcnt)) return; switch (conn->state) { case BT_CONNECT: case BT_CONNECT2: if (conn->out) { if (conn->type == ACL_LINK) hci_acl_create_connection_cancel(conn); else if (conn->type == LE_LINK) hci_le_create_connection_cancel(conn); } break; case BT_CONFIG: case BT_CONNECTED: reason = hci_proto_disconn_ind(conn); hci_acl_disconn(conn, reason); break; default: conn->state = BT_CLOSED; break; } } /* Enter sniff mode */ static void hci_conn_enter_sniff_mode(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; BT_DBG("hcon %p mode %d", conn, conn->mode); if (test_bit(HCI_RAW, &hdev->flags)) return; if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) return; if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF)) return; if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { struct hci_cp_sniff_subrate cp; cp.handle = cpu_to_le16(conn->handle); cp.max_latency = __constant_cpu_to_le16(0); cp.min_remote_timeout = __constant_cpu_to_le16(0); cp.min_local_timeout = __constant_cpu_to_le16(0); hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp); } if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { struct hci_cp_sniff_mode cp; cp.handle = cpu_to_le16(conn->handle); cp.max_interval = cpu_to_le16(hdev->sniff_max_interval); cp.min_interval = cpu_to_le16(hdev->sniff_min_interval); cp.attempt = __constant_cpu_to_le16(4); cp.timeout = __constant_cpu_to_le16(1); hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp); } } static void hci_conn_idle(unsigned long arg) { struct hci_conn *conn = (void *) arg; BT_DBG("hcon %p mode %d", conn, conn->mode); hci_conn_enter_sniff_mode(conn); } static void hci_conn_auto_accept(unsigned long arg) { struct hci_conn *conn = (void *) arg; struct hci_dev *hdev = conn->hdev; hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst), &conn->dst); } struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) { struct hci_conn *conn; BT_DBG("%s dst %s", hdev->name, batostr(dst)); conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL); if (!conn) return NULL; bacpy(&conn->dst, dst); conn->hdev = hdev; conn->type = type; conn->mode = HCI_CM_ACTIVE; conn->state = BT_OPEN; conn->auth_type = HCI_AT_GENERAL_BONDING; conn->io_capability = hdev->io_capability; conn->remote_auth = 0xff; conn->key_type = 0xff; set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; switch (type) { case ACL_LINK: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; break; case SCO_LINK: if (lmp_esco_capable(hdev)) conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | (hdev->esco_type & EDR_ESCO_MASK); else conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; break; case ESCO_LINK: conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; break; } skb_queue_head_init(&conn->data_q); INIT_LIST_HEAD(&conn->chan_list); INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, (unsigned long) conn); atomic_set(&conn->refcnt, 0); hci_dev_hold(hdev); hci_conn_hash_add(hdev, conn); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); atomic_set(&conn->devref, 0); hci_conn_init_sysfs(conn); return conn; } int hci_conn_del(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle); del_timer(&conn->idle_timer); cancel_delayed_work_sync(&conn->disc_work); del_timer(&conn->auto_accept_timer); if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; if (sco) sco->link = NULL; /* Unacked frames */ hdev->acl_cnt += conn->sent; } else if (conn->type == LE_LINK) { if (hdev->le_pkts) hdev->le_cnt += conn->sent; else hdev->acl_cnt += conn->sent; } else { struct hci_conn *acl = conn->link; if (acl) { acl->link = NULL; hci_conn_put(acl); } } hci_chan_list_flush(conn); if (conn->amp_mgr) amp_mgr_put(conn->amp_mgr); hci_conn_hash_del(hdev, conn); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); skb_queue_purge(&conn->data_q); hci_conn_put_device(conn); hci_dev_put(hdev); if (conn->handle == 0) kfree(conn); return 0; } struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) { int use_src = bacmp(src, BDADDR_ANY); struct hci_dev *hdev = NULL, *d; BT_DBG("%s -> %s", batostr(src), batostr(dst)); read_lock(&hci_dev_list_lock); list_for_each_entry(d, &hci_dev_list, list) { if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags) || d->dev_type != HCI_BREDR) continue; /* Simple routing: * No source address - find interface with bdaddr != dst * Source address - find interface with bdaddr == src */ if (use_src) { if (!bacmp(&d->bdaddr, src)) { hdev = d; break; } } else { if (bacmp(&d->bdaddr, dst)) { hdev = d; break; } } } if (hdev) hdev = hci_dev_hold(hdev); read_unlock(&hci_dev_list_lock); return hdev; } EXPORT_SYMBOL(hci_get_route); static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u8 auth_type) { struct hci_conn *le; le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); if (!le) { le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (le) return ERR_PTR(-EBUSY); le = hci_conn_add(hdev, LE_LINK, dst); if (!le) return ERR_PTR(-ENOMEM); le->dst_type = bdaddr_to_le(dst_type); hci_le_create_connection(le); } le->pending_sec_level = sec_level; le->auth_type = auth_type; hci_conn_hold(le); return le; } static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type) { struct hci_conn *acl; acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (!acl) { acl = hci_conn_add(hdev, ACL_LINK, dst); if (!acl) return ERR_PTR(-ENOMEM); } hci_conn_hold(acl); if (acl->state == BT_OPEN || acl->state == BT_CLOSED) { acl->sec_level = BT_SECURITY_LOW; acl->pending_sec_level = sec_level; acl->auth_type = auth_type; hci_acl_create_connection(acl); } return acl; } static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, u8 sec_level, u8 auth_type) { struct hci_conn *acl; struct hci_conn *sco; acl = hci_connect_acl(hdev, dst, sec_level, auth_type); if (IS_ERR(acl)) return acl; sco = hci_conn_hash_lookup_ba(hdev, type, dst); if (!sco) { sco = hci_conn_add(hdev, type, dst); if (!sco) { hci_conn_put(acl); return ERR_PTR(-ENOMEM); } } acl->link = sco; sco->link = acl; hci_conn_hold(sco); if (acl->state == BT_CONNECTED && (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { set_bit(HCI_CONN_POWER_SAVE, &acl->flags); hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) { /* defer SCO setup until mode change completed */ set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags); return sco; } hci_sco_setup(acl, 0x00); } return sco; } /* Create SCO, ACL or LE connection. */ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type) { BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type); switch (type) { case LE_LINK: return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type); case ACL_LINK: return hci_connect_acl(hdev, dst, sec_level, auth_type); case SCO_LINK: case ESCO_LINK: return hci_connect_sco(hdev, type, dst, sec_level, auth_type); } return ERR_PTR(-EINVAL); } /* Check link security requirement */ int hci_conn_check_link_mode(struct hci_conn *conn) { BT_DBG("hcon %p", conn); if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT)) return 0; return 1; } /* Authenticate remote device */ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("hcon %p", conn); if (conn->pending_sec_level > sec_level) sec_level = conn->pending_sec_level; if (sec_level > conn->sec_level) conn->pending_sec_level = sec_level; else if (conn->link_mode & HCI_LM_AUTH) return 1; /* Make sure we preserve an existing MITM requirement*/ auth_type |= (conn->auth_type & 0x01); conn->auth_type = auth_type; if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested cp; /* encrypt must be pending if auth is also pending */ set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); if (conn->key_type != 0xff) set_bit(HCI_CONN_REAUTH_PEND, &conn->flags); } return 0; } /* Encrypt the the link */ static void hci_conn_encrypt(struct hci_conn *conn) { BT_DBG("hcon %p", conn); if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { struct hci_cp_set_conn_encrypt cp; cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 0x01; hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } } /* Enable security */ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("hcon %p", conn); if (conn->type == LE_LINK) return smp_conn_security(conn, sec_level); /* For sdp we don't need the link key. */ if (sec_level == BT_SECURITY_SDP) return 1; /* For non 2.1 devices and low security level we don't need the link key. */ if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn)) return 1; /* For other security levels we need the link key. */ if (!(conn->link_mode & HCI_LM_AUTH)) goto auth; /* An authenticated combination key has sufficient security for any security level. */ if (conn->key_type == HCI_LK_AUTH_COMBINATION) goto encrypt; /* An unauthenticated combination key has sufficient security for security level 1 and 2. */ if (conn->key_type == HCI_LK_UNAUTH_COMBINATION && (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW)) goto encrypt; /* A combination key has always sufficient security for the security levels 1 or 2. High security level requires the combination key is generated using maximum PIN code length (16). For pre 2.1 units. */ if (conn->key_type == HCI_LK_COMBINATION && (sec_level != BT_SECURITY_HIGH || conn->pin_length == 16)) goto encrypt; auth: if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return 0; if (!hci_conn_auth(conn, sec_level, auth_type)) return 0; encrypt: if (conn->link_mode & HCI_LM_ENCRYPT) return 1; hci_conn_encrypt(conn); return 0; } EXPORT_SYMBOL(hci_conn_security); /* Check secure link requirement */ int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level) { BT_DBG("hcon %p", conn); if (sec_level != BT_SECURITY_HIGH) return 1; /* Accept if non-secure is required */ if (conn->sec_level == BT_SECURITY_HIGH) return 1; return 0; /* Reject not secure link */ } EXPORT_SYMBOL(hci_conn_check_secure); /* Change link key */ int hci_conn_change_link_key(struct hci_conn *conn) { BT_DBG("hcon %p", conn); if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_change_conn_link_key cp; cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp); } return 0; } /* Switch role */ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) { BT_DBG("hcon %p", conn); if (!role && conn->link_mode & HCI_LM_MASTER) return 1; if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) { struct hci_cp_switch_role cp; bacpy(&cp.bdaddr, &conn->dst); cp.role = role; hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp); } return 0; } EXPORT_SYMBOL(hci_conn_switch_role); /* Enter active mode */ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) { struct hci_dev *hdev = conn->hdev; BT_DBG("hcon %p mode %d", conn, conn->mode); if (test_bit(HCI_RAW, &hdev->flags)) return; if (conn->mode != HCI_CM_SNIFF) goto timer; if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active) goto timer; if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { struct hci_cp_exit_sniff_mode cp; cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp); } timer: if (hdev->idle_timeout > 0) mod_timer(&conn->idle_timer, jiffies + msecs_to_jiffies(hdev->idle_timeout)); } /* Drop all connection on the device */ void hci_conn_hash_flush(struct hci_dev *hdev) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c, *n; BT_DBG("hdev %s", hdev->name); list_for_each_entry_safe(c, n, &h->list, list) { c->state = BT_CLOSED; hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); hci_conn_del(c); } } /* Check pending connect attempts */ void hci_conn_check_pending(struct hci_dev *hdev) { struct hci_conn *conn; BT_DBG("hdev %s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); if (conn) hci_acl_create_connection(conn); hci_dev_unlock(hdev); } void hci_conn_hold_device(struct hci_conn *conn) { atomic_inc(&conn->devref); } EXPORT_SYMBOL(hci_conn_hold_device); void hci_conn_put_device(struct hci_conn *conn) { if (atomic_dec_and_test(&conn->devref)) hci_conn_del_sysfs(conn); } EXPORT_SYMBOL(hci_conn_put_device); int hci_get_conn_list(void __user *arg) { struct hci_conn *c; struct hci_conn_list_req req, *cl; struct hci_conn_info *ci; struct hci_dev *hdev; int n = 0, size, err; if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci)) return -EINVAL; size = sizeof(req) + req.conn_num * sizeof(*ci); cl = kmalloc(size, GFP_KERNEL); if (!cl) return -ENOMEM; hdev = hci_dev_get(req.dev_id); if (!hdev) { kfree(cl); return -ENODEV; } ci = cl->conn_info; hci_dev_lock(hdev); list_for_each_entry(c, &hdev->conn_hash.list, list) { bacpy(&(ci + n)->bdaddr, &c->dst); (ci + n)->handle = c->handle; (ci + n)->type = c->type; (ci + n)->out = c->out; (ci + n)->state = c->state; (ci + n)->link_mode = c->link_mode; if (++n >= req.conn_num) break; } hci_dev_unlock(hdev); cl->dev_id = hdev->id; cl->conn_num = n; size = sizeof(req) + n * sizeof(*ci); hci_dev_put(hdev); err = copy_to_user(arg, cl, size); kfree(cl); return err ? -EFAULT : 0; } int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) { struct hci_conn_info_req req; struct hci_conn_info ci; struct hci_conn *conn; char __user *ptr = arg + sizeof(req); if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr); if (conn) { bacpy(&ci.bdaddr, &conn->dst); ci.handle = conn->handle; ci.type = conn->type; ci.out = conn->out; ci.state = conn->state; ci.link_mode = conn->link_mode; } hci_dev_unlock(hdev); if (!conn) return -ENOENT; return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0; } int hci_get_auth_info(struct hci_dev *hdev, void __user *arg) { struct hci_auth_info_req req; struct hci_conn *conn; if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr); if (conn) req.type = conn->auth_type; hci_dev_unlock(hdev); if (!conn) return -ENOENT; return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; } struct hci_chan *hci_chan_create(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; struct hci_chan *chan; BT_DBG("%s hcon %p", hdev->name, conn); chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL); if (!chan) return NULL; chan->conn = conn; skb_queue_head_init(&chan->data_q); list_add_rcu(&chan->list, &conn->chan_list); return chan; } void hci_chan_del(struct hci_chan *chan) { struct hci_conn *conn = chan->conn; struct hci_dev *hdev = conn->hdev; BT_DBG("%s hcon %p chan %p", hdev->name, conn, chan); list_del_rcu(&chan->list); synchronize_rcu(); skb_queue_purge(&chan->data_q); kfree(chan); } void hci_chan_list_flush(struct hci_conn *conn) { struct hci_chan *chan, *n; BT_DBG("hcon %p", conn); list_for_each_entry_safe(chan, n, &conn->chan_list, list) hci_chan_del(chan); } compat-drivers-2012-09-18/net/bluetooth/hidp/0000755000175000017500000000000012026211315020077 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/bluetooth/hidp/sock.c0000644000175000017500000001636212026211315021212 0ustar mcgrofmcgrof/* HIDP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2003-2004 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include "hidp.h" static struct bt_sock_list hidp_sk_list = { .lock = __RW_LOCK_UNLOCKED(hidp_sk_list.lock) }; static int hidp_sock_release(struct socket *sock) { struct sock *sk = sock->sk; BT_DBG("sock %p sk %p", sock, sk); if (!sk) return 0; bt_sock_unlink(&hidp_sk_list, sk); sock_orphan(sk); sock_put(sk); return 0; } static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *) arg; struct hidp_connadd_req ca; struct hidp_conndel_req cd; struct hidp_connlist_req cl; struct hidp_conninfo ci; struct socket *csock; struct socket *isock; int err; BT_DBG("cmd %x arg %lx", cmd, arg); switch (cmd) { case HIDPCONNADD: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (copy_from_user(&ca, argp, sizeof(ca))) return -EFAULT; csock = sockfd_lookup(ca.ctrl_sock, &err); if (!csock) return err; isock = sockfd_lookup(ca.intr_sock, &err); if (!isock) { sockfd_put(csock); return err; } if (csock->sk->sk_state != BT_CONNECTED || isock->sk->sk_state != BT_CONNECTED) { sockfd_put(csock); sockfd_put(isock); return -EBADFD; } err = hidp_add_connection(&ca, csock, isock); if (!err) { if (copy_to_user(argp, &ca, sizeof(ca))) err = -EFAULT; } else { sockfd_put(csock); sockfd_put(isock); } return err; case HIDPCONNDEL: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (copy_from_user(&cd, argp, sizeof(cd))) return -EFAULT; return hidp_del_connection(&cd); case HIDPGETCONNLIST: if (copy_from_user(&cl, argp, sizeof(cl))) return -EFAULT; if (cl.cnum <= 0) return -EINVAL; err = hidp_get_connlist(&cl); if (!err && copy_to_user(argp, &cl, sizeof(cl))) return -EFAULT; return err; case HIDPGETCONNINFO: if (copy_from_user(&ci, argp, sizeof(ci))) return -EFAULT; err = hidp_get_conninfo(&ci); if (!err && copy_to_user(argp, &ci, sizeof(ci))) return -EFAULT; return err; } return -EINVAL; } #ifdef CONFIG_COMPAT struct compat_hidp_connadd_req { int ctrl_sock; /* Connected control socket */ int intr_sock; /* Connected interrupt socket */ __u16 parser; __u16 rd_size; compat_uptr_t rd_data; __u8 country; __u8 subclass; __u16 vendor; __u16 product; __u16 version; __u32 flags; __u32 idle_to; char name[128]; }; static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { if (cmd == HIDPGETCONNLIST) { struct hidp_connlist_req cl; u32 uci; int err; if (get_user(cl.cnum, (u32 __user *) arg) || get_user(uci, (u32 __user *) (arg + 4))) return -EFAULT; cl.ci = compat_ptr(uci); if (cl.cnum <= 0) return -EINVAL; err = hidp_get_connlist(&cl); if (!err && put_user(cl.cnum, (u32 __user *) arg)) err = -EFAULT; return err; } else if (cmd == HIDPCONNADD) { struct compat_hidp_connadd_req ca; struct hidp_connadd_req __user *uca; uca = compat_alloc_user_space(sizeof(*uca)); if (copy_from_user(&ca, (void __user *) arg, sizeof(ca))) return -EFAULT; if (put_user(ca.ctrl_sock, &uca->ctrl_sock) || put_user(ca.intr_sock, &uca->intr_sock) || put_user(ca.parser, &uca->parser) || put_user(ca.rd_size, &uca->rd_size) || put_user(compat_ptr(ca.rd_data), &uca->rd_data) || put_user(ca.country, &uca->country) || put_user(ca.subclass, &uca->subclass) || put_user(ca.vendor, &uca->vendor) || put_user(ca.product, &uca->product) || put_user(ca.version, &uca->version) || put_user(ca.flags, &uca->flags) || put_user(ca.idle_to, &uca->idle_to) || copy_to_user(&uca->name[0], &ca.name[0], 128)) return -EFAULT; arg = (unsigned long) uca; /* Fall through. We don't actually write back any _changes_ to the structure anyway, so there's no need to copy back into the original compat version */ } return hidp_sock_ioctl(sock, cmd, arg); } #endif static const struct proto_ops hidp_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = hidp_sock_release, .ioctl = hidp_sock_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = hidp_sock_compat_ioctl, #endif .bind = sock_no_bind, .getname = sock_no_getname, .sendmsg = sock_no_sendmsg, .recvmsg = sock_no_recvmsg, .poll = sock_no_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .mmap = sock_no_mmap }; static struct proto hidp_proto = { .name = "HIDP", .owner = THIS_MODULE, .obj_size = sizeof(struct bt_sock) }; #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int hidp_sock_create(struct net *net, struct socket *sock, int protocol, int kern) #else static int hidp_sock_create(struct net *net, struct socket *sock, int protocol) #endif { struct sock *sk; BT_DBG("sock %p", sock); if (sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto); if (!sk) return -ENOMEM; sock_init_data(sock, sk); sock->ops = &hidp_sock_ops; sock->state = SS_UNCONNECTED; sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = protocol; sk->sk_state = BT_OPEN; bt_sock_link(&hidp_sk_list, sk); return 0; } static const struct net_proto_family hidp_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = hidp_sock_create }; int __init hidp_init_sockets(void) { int err; err = proto_register(&hidp_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops); if (err < 0) { BT_ERR("Can't register HIDP socket"); goto error; } err = bt_procfs_init(THIS_MODULE, &init_net, "hidp", &hidp_sk_list, NULL); if (err < 0) { BT_ERR("Failed to create HIDP proc file"); bt_sock_unregister(BTPROTO_HIDP); goto error; } BT_INFO("HIDP socket layer initialized"); return 0; error: BT_ERR("Can't register HIDP socket"); proto_unregister(&hidp_proto); return err; } void __exit hidp_cleanup_sockets(void) { bt_procfs_cleanup(&init_net, "hidp"); if (bt_sock_unregister(BTPROTO_HIDP) < 0) BT_ERR("Can't unregister HIDP socket"); proto_unregister(&hidp_proto); } compat-drivers-2012-09-18/net/bluetooth/hidp/core.c0000644000175000017500000010016112026211315021172 0ustar mcgrofmcgrof/* HIDP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2003-2004 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #include #include #include #include #include #include #include #include "hidp.h" #define VERSION "1.2" static DECLARE_RWSEM(hidp_session_sem); static LIST_HEAD(hidp_session_list); static unsigned char hidp_keycode[256] = { 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88, 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135, 136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94, 95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115, 114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140 }; static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) { struct hidp_session *session; BT_DBG(""); list_for_each_entry(session, &hidp_session_list, list) { if (!bacmp(bdaddr, &session->bdaddr)) return session; } return NULL; } static void __hidp_link_session(struct hidp_session *session) { list_add(&session->list, &hidp_session_list); } static void __hidp_unlink_session(struct hidp_session *session) { hci_conn_put_device(session->conn); list_del(&session->list); } static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) { memset(ci, 0, sizeof(*ci)); bacpy(&ci->bdaddr, &session->bdaddr); ci->flags = session->flags; ci->state = session->state; ci->vendor = 0x0000; ci->product = 0x0000; ci->version = 0x0000; if (session->input) { ci->vendor = session->input->id.vendor; ci->product = session->input->id.product; ci->version = session->input->id.version; if (session->input->name) strncpy(ci->name, session->input->name, 128); else strncpy(ci->name, "HID Boot Device", 128); } if (session->hid) { ci->vendor = session->hid->vendor; ci->product = session->hid->product; ci->version = session->hid->version; strncpy(ci->name, session->hid->name, 128); } } static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev, unsigned int type, unsigned int code, int value) { unsigned char newleds; struct sk_buff *skb; BT_DBG("session %p type %d code %d value %d", session, type, code, value); if (type != EV_LED) return -1; newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | (!!test_bit(LED_NUML, dev->led)); if (session->leds == newleds) return 0; session->leds = newleds; skb = alloc_skb(3, GFP_ATOMIC); if (!skb) { BT_ERR("Can't allocate memory for new frame"); return -ENOMEM; } *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; *skb_put(skb, 1) = 0x01; *skb_put(skb, 1) = newleds; skb_queue_tail(&session->intr_transmit, skb); hidp_schedule(session); return 0; } static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hid_device *hid = input_get_drvdata(dev); struct hidp_session *session = hid->driver_data; return hidp_queue_event(session, dev, type, code, value); } static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hidp_session *session = input_get_drvdata(dev); return hidp_queue_event(session, dev, type, code, value); } static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) { struct input_dev *dev = session->input; unsigned char *keys = session->keys; unsigned char *udata = skb->data + 1; signed char *sdata = skb->data + 1; int i, size = skb->len - 1; switch (skb->data[0]) { case 0x01: /* Keyboard report */ for (i = 0; i < 8; i++) input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1); /* If all the key codes have been set to 0x01, it means * too many keys were pressed at the same time. */ if (!memcmp(udata + 2, hidp_mkeyspat, 6)) break; for (i = 2; i < 8; i++) { if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) { if (hidp_keycode[keys[i]]) input_report_key(dev, hidp_keycode[keys[i]], 0); else BT_ERR("Unknown key (scancode %#x) released.", keys[i]); } if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) { if (hidp_keycode[udata[i]]) input_report_key(dev, hidp_keycode[udata[i]], 1); else BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]); } } memcpy(keys, udata, 8); break; case 0x02: /* Mouse report */ input_report_key(dev, BTN_LEFT, sdata[0] & 0x01); input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02); input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04); input_report_key(dev, BTN_SIDE, sdata[0] & 0x08); input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10); input_report_rel(dev, REL_X, sdata[1]); input_report_rel(dev, REL_Y, sdata[2]); if (size > 3) input_report_rel(dev, REL_WHEEL, sdata[3]); break; } input_sync(dev); } static int __hidp_send_ctrl_message(struct hidp_session *session, unsigned char hdr, unsigned char *data, int size) { struct sk_buff *skb; BT_DBG("session %p data %p size %d", session, data, size); if (atomic_read(&session->terminate)) return -EIO; skb = alloc_skb(size + 1, GFP_ATOMIC); if (!skb) { BT_ERR("Can't allocate memory for new frame"); return -ENOMEM; } *skb_put(skb, 1) = hdr; if (data && size > 0) memcpy(skb_put(skb, size), data, size); skb_queue_tail(&session->ctrl_transmit, skb); return 0; } static int hidp_send_ctrl_message(struct hidp_session *session, unsigned char hdr, unsigned char *data, int size) { int err; err = __hidp_send_ctrl_message(session, hdr, data, size); hidp_schedule(session); return err; } static int hidp_queue_report(struct hidp_session *session, unsigned char *data, int size) { struct sk_buff *skb; BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size); skb = alloc_skb(size + 1, GFP_ATOMIC); if (!skb) { BT_ERR("Can't allocate memory for new frame"); return -ENOMEM; } *skb_put(skb, 1) = 0xa2; if (size > 0) memcpy(skb_put(skb, size), data, size); skb_queue_tail(&session->intr_transmit, skb); hidp_schedule(session); return 0; } static int hidp_send_report(struct hidp_session *session, struct hid_report *report) { unsigned char buf[32]; int rsize; rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); if (rsize > sizeof(buf)) return -EIO; hid_output_report(report, buf); return hidp_queue_report(session, buf, rsize); } static int hidp_get_raw_report(struct hid_device *hid, unsigned char report_number, unsigned char *data, size_t count, unsigned char report_type) { struct hidp_session *session = hid->driver_data; struct sk_buff *skb; size_t len; int numbered_reports = hid->report_enum[report_type].numbered; int ret; switch (report_type) { case HID_FEATURE_REPORT: report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; break; case HID_INPUT_REPORT: report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; break; case HID_OUTPUT_REPORT: report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; break; default: return -EINVAL; } if (mutex_lock_interruptible(&session->report_mutex)) return -ERESTARTSYS; /* Set up our wait, and send the report request to the device. */ session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK; session->waiting_report_number = numbered_reports ? report_number : -1; set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); data[0] = report_number; ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1); if (ret) goto err; /* Wait for the return of the report. The returned report gets put in session->report_return. */ while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { int res; res = wait_event_interruptible_timeout(session->report_queue, !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags), 5*HZ); if (res == 0) { /* timeout */ ret = -EIO; goto err; } if (res < 0) { /* signal */ ret = -ERESTARTSYS; goto err; } } skb = session->report_return; if (skb) { len = skb->len < count ? skb->len : count; memcpy(data, skb->data, len); kfree_skb(skb); session->report_return = NULL; } else { /* Device returned a HANDSHAKE, indicating protocol error. */ len = -EIO; } clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); mutex_unlock(&session->report_mutex); return len; err: clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); mutex_unlock(&session->report_mutex); return ret; } #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, unsigned char report_type) { struct hidp_session *session = hid->driver_data; int ret; switch (report_type) { case HID_FEATURE_REPORT: report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; break; case HID_OUTPUT_REPORT: report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; break; default: return -EINVAL; } if (mutex_lock_interruptible(&session->report_mutex)) return -ERESTARTSYS; /* Set up our wait, and send the report request to the device. */ set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, count); if (ret) goto err; /* Wait for the ACK from the device. */ while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { int res; res = wait_event_interruptible_timeout(session->report_queue, !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags), 10*HZ); if (res == 0) { /* timeout */ ret = -EIO; goto err; } if (res < 0) { /* signal */ ret = -ERESTARTSYS; goto err; } } if (!session->output_report_success) { ret = -EIO; goto err; } ret = count; err: clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); mutex_unlock(&session->report_mutex); return ret; } #elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count) { if (hidp_send_ctrl_message(hid->driver_data, HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE, data, count)) return -ENOMEM; return count; } #endif static void hidp_idle_timeout(unsigned long arg) { struct hidp_session *session = (struct hidp_session *) arg; atomic_inc(&session->terminate); wake_up_process(session->task); } static void hidp_set_timer(struct hidp_session *session) { if (session->idle_to > 0) mod_timer(&session->timer, jiffies + HZ * session->idle_to); } static void hidp_del_timer(struct hidp_session *session) { if (session->idle_to > 0) del_timer(&session->timer); } static void hidp_process_handshake(struct hidp_session *session, unsigned char param) { BT_DBG("session %p param 0x%02x", session, param); session->output_report_success = 0; /* default condition */ switch (param) { case HIDP_HSHK_SUCCESSFUL: /* FIXME: Call into SET_ GET_ handlers here */ session->output_report_success = 1; break; case HIDP_HSHK_NOT_READY: case HIDP_HSHK_ERR_INVALID_REPORT_ID: case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: case HIDP_HSHK_ERR_INVALID_PARAMETER: if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) wake_up_interruptible(&session->report_queue); /* FIXME: Call into SET_ GET_ handlers here */ break; case HIDP_HSHK_ERR_UNKNOWN: break; case HIDP_HSHK_ERR_FATAL: /* Device requests a reboot, as this is the only way this error * can be recovered. */ __hidp_send_ctrl_message(session, HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0); break; default: __hidp_send_ctrl_message(session, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); break; } /* Wake up the waiting thread. */ if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) wake_up_interruptible(&session->report_queue); } static void hidp_process_hid_control(struct hidp_session *session, unsigned char param) { BT_DBG("session %p param 0x%02x", session, param); if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) { /* Flush the transmit queues */ skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); atomic_inc(&session->terminate); wake_up_process(current); } } /* Returns true if the passed-in skb should be freed by the caller. */ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, unsigned char param) { int done_with_skb = 1; BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); switch (param) { case HIDP_DATA_RTYPE_INPUT: hidp_set_timer(session); if (session->input) hidp_input_report(session, skb); if (session->hid) hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); break; case HIDP_DATA_RTYPE_OTHER: case HIDP_DATA_RTYPE_OUPUT: case HIDP_DATA_RTYPE_FEATURE: break; default: __hidp_send_ctrl_message(session, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); } if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && param == session->waiting_report_type) { if (session->waiting_report_number < 0 || session->waiting_report_number == skb->data[0]) { /* hidp_get_raw_report() is waiting on this report. */ session->report_return = skb; done_with_skb = 0; clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); wake_up_interruptible(&session->report_queue); } } return done_with_skb; } static void hidp_recv_ctrl_frame(struct hidp_session *session, struct sk_buff *skb) { unsigned char hdr, type, param; int free_skb = 1; BT_DBG("session %p skb %p len %d", session, skb, skb->len); hdr = skb->data[0]; skb_pull(skb, 1); type = hdr & HIDP_HEADER_TRANS_MASK; param = hdr & HIDP_HEADER_PARAM_MASK; switch (type) { case HIDP_TRANS_HANDSHAKE: hidp_process_handshake(session, param); break; case HIDP_TRANS_HID_CONTROL: hidp_process_hid_control(session, param); break; case HIDP_TRANS_DATA: free_skb = hidp_process_data(session, skb, param); break; default: __hidp_send_ctrl_message(session, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0); break; } if (free_skb) kfree_skb(skb); } static void hidp_recv_intr_frame(struct hidp_session *session, struct sk_buff *skb) { unsigned char hdr; BT_DBG("session %p skb %p len %d", session, skb, skb->len); hdr = skb->data[0]; skb_pull(skb, 1); if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { hidp_set_timer(session); if (session->input) hidp_input_report(session, skb); if (session->hid) { hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1); BT_DBG("report len %d", skb->len); } } else { BT_DBG("Unsupported protocol header 0x%02x", hdr); } kfree_skb(skb); } static int hidp_send_frame(struct socket *sock, unsigned char *data, int len) { struct kvec iv = { data, len }; struct msghdr msg; BT_DBG("sock %p data %p len %d", sock, data, len); if (!len) return 0; memset(&msg, 0, sizeof(msg)); return kernel_sendmsg(sock, &msg, &iv, 1, len); } static void hidp_process_intr_transmit(struct hidp_session *session) { struct sk_buff *skb; BT_DBG("session %p", session); while ((skb = skb_dequeue(&session->intr_transmit))) { if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { skb_queue_head(&session->intr_transmit, skb); break; } hidp_set_timer(session); kfree_skb(skb); } } static void hidp_process_ctrl_transmit(struct hidp_session *session) { struct sk_buff *skb; BT_DBG("session %p", session); while ((skb = skb_dequeue(&session->ctrl_transmit))) { if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { skb_queue_head(&session->ctrl_transmit, skb); break; } hidp_set_timer(session); kfree_skb(skb); } } static int hidp_session(void *arg) { struct hidp_session *session = arg; struct sock *ctrl_sk = session->ctrl_sock->sk; struct sock *intr_sk = session->intr_sock->sk; struct sk_buff *skb; wait_queue_t ctrl_wait, intr_wait; BT_DBG("session %p", session); __module_get(THIS_MODULE); set_user_nice(current, -15); init_waitqueue_entry(&ctrl_wait, current); init_waitqueue_entry(&intr_wait, current); add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); add_wait_queue(sk_sleep(intr_sk), &intr_wait); session->waiting_for_startup = 0; wake_up_interruptible(&session->startup_queue); set_current_state(TASK_INTERRUPTIBLE); while (!atomic_read(&session->terminate)) { if (ctrl_sk->sk_state != BT_CONNECTED || intr_sk->sk_state != BT_CONNECTED) break; while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) hidp_recv_intr_frame(session, skb); else kfree_skb(skb); } hidp_process_intr_transmit(session); while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) hidp_recv_ctrl_frame(session, skb); else kfree_skb(skb); } hidp_process_ctrl_transmit(session); schedule(); set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(intr_sk), &intr_wait); remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); wake_up_interruptible(&session->report_queue); down_write(&hidp_session_sem); hidp_del_timer(session); if (session->input) { input_unregister_device(session->input); session->input = NULL; } if (session->hid) { #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) hid_destroy_device(session->hid); session->hid = NULL; #else if (session->hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(session->hid); hid_free_device(session->hid); #endif } /* Wakeup user-space polling for socket errors */ session->intr_sock->sk->sk_err = EUNATCH; session->ctrl_sock->sk->sk_err = EUNATCH; hidp_schedule(session); fput(session->intr_sock->file); wait_event_timeout(*(sk_sleep(ctrl_sk)), (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500)); fput(session->ctrl_sock->file); __hidp_unlink_session(session); up_write(&hidp_session_sem); kfree(session->rd_data); kfree(session); module_put_and_exit(0); return 0; } static struct hci_conn *hidp_get_connection(struct hidp_session *session) { bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; struct hci_conn *conn; struct hci_dev *hdev; hdev = hci_get_route(dst, src); if (!hdev) return NULL; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (conn) hci_conn_hold_device(conn); hci_dev_unlock(hdev); hci_dev_put(hdev); return conn; } static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { struct input_dev *input; int i; input = input_allocate_device(); if (!input) return -ENOMEM; session->input = input; input_set_drvdata(input, session); input->name = "Bluetooth HID Boot Protocol Device"; input->id.bustype = BUS_BLUETOOTH; input->id.vendor = req->vendor; input->id.product = req->product; input->id.version = req->version; if (req->subclass & 0x40) { set_bit(EV_KEY, input->evbit); set_bit(EV_LED, input->evbit); set_bit(EV_REP, input->evbit); set_bit(LED_NUML, input->ledbit); set_bit(LED_CAPSL, input->ledbit); set_bit(LED_SCROLLL, input->ledbit); set_bit(LED_COMPOSE, input->ledbit); set_bit(LED_KANA, input->ledbit); for (i = 0; i < sizeof(hidp_keycode); i++) set_bit(hidp_keycode[i], input->keybit); clear_bit(0, input->keybit); } if (req->subclass & 0x80) { input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); input->relbit[0] |= BIT_MASK(REL_WHEEL); } input->dev.parent = &session->conn->dev; input->event = hidp_input_event; return 0; } static int hidp_open(struct hid_device *hid) { return 0; } static void hidp_close(struct hid_device *hid) { } #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)) static const struct { __u16 idVendor; __u16 idProduct; unsigned quirks; } hidp_blacklist[] = { /* Apple wireless Mighty Mouse */ { 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, { } /* Terminating entry */ }; static void hidp_setup_quirks(struct hid_device *hid) { unsigned int n; for (n = 0; hidp_blacklist[n].idVendor; n++) if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) && hidp_blacklist[n].idProduct == le16_to_cpu(hid->product)) hid->quirks = hidp_blacklist[n].quirks; } static void hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { struct hid_device *hid = session->hid; struct hid_report *report; bdaddr_t src, dst; session->hid = hid; hid->driver_data = session; baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); hid->bus = BUS_BLUETOOTH; hid->vendor = req->vendor; hid->product = req->product; hid->version = req->version; hid->country = req->country; strncpy(hid->name, req->name, 128); strncpy(hid->phys, batostr(&src), 64); strncpy(hid->uniq, batostr(&dst), 64); hid->dev = hidp_get_device(session); hid->hid_open = hidp_open; hid->hid_close = hidp_close; hid->hidinput_input_event = hidp_hidinput_event; hidp_setup_quirks(hid); list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) hidp_send_report(session, report); list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) hidp_send_report(session, report); if (hidinput_connect(hid) == 0) hid->claimed |= HID_CLAIMED_INPUT; } #else static int hidp_parse(struct hid_device *hid) { struct hidp_session *session = hid->driver_data; return hid_parse_report(session->hid, session->rd_data, session->rd_size); } static int hidp_start(struct hid_device *hid) { struct hidp_session *session = hid->driver_data; struct hid_report *report; if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS) return 0; list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. report_list, list) hidp_send_report(session, report); list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT]. report_list, list) hidp_send_report(session, report); return 0; } static void hidp_stop(struct hid_device *hid) { struct hidp_session *session = hid->driver_data; skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); hid->claimed = 0; } static struct hid_ll_driver hidp_hid_driver = { .parse = hidp_parse, .start = hidp_start, .stop = hidp_stop, .open = hidp_open, .close = hidp_close, .hidinput_input_event = hidp_hidinput_event, }; /* This function sets up the hid device. It does not add it to the HID system. That is done in hidp_add_connection(). */ static int hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { struct hid_device *hid; int err; session->rd_data = kzalloc(req->rd_size, GFP_KERNEL); if (!session->rd_data) return -ENOMEM; if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) { err = -EFAULT; goto fault; } session->rd_size = req->rd_size; hid = hid_allocate_device(); if (IS_ERR(hid)) { err = PTR_ERR(hid); goto fault; } session->hid = hid; hid->driver_data = session; hid->bus = BUS_BLUETOOTH; hid->vendor = req->vendor; hid->product = req->product; hid->version = req->version; hid->country = req->country; strncpy(hid->name, req->name, 128); strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); hid->dev.parent = &session->conn->dev; hid->ll_driver = &hidp_hid_driver; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) hid->hid_get_raw_report = hidp_get_raw_report; #endif hid->hid_output_raw_report = hidp_output_raw_report; return 0; fault: kfree(session->rd_data); session->rd_data = NULL; return err; } #endif int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) { struct hidp_session *session, *s; int vendor, product; int err; BT_DBG(""); if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) || bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst)) return -ENOTUNIQ; BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)) if (req->rd_size > 0) { unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL); if (!buf) { kfree(session); return -ENOMEM; } if (copy_from_user(buf, req->rd_data, req->rd_size)) { kfree(buf); kfree(session); return -EFAULT; } session->hid = hid_parse_report(buf, req->rd_size); kfree(buf); if (!session->hid) { kfree(session); return -EINVAL; } } if (!session->hid) { session->input = input_allocate_device(); if (!session->input) { kfree(session); return -ENOMEM; } } #endif down_write(&hidp_session_sem); s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); if (s && s->state == BT_CONNECTED) { up_write(&hidp_session_sem); return -EEXIST; } session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL); if (!session) { up_write(&hidp_session_sem); return -ENOMEM; } bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu, l2cap_pi(ctrl_sock->sk)->chan->imtu); session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->chan->omtu, l2cap_pi(intr_sock->sk)->chan->imtu); BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu); session->ctrl_sock = ctrl_sock; session->intr_sock = intr_sock; session->state = BT_CONNECTED; session->conn = hidp_get_connection(session); if (!session->conn) { err = -ENOTCONN; goto failed; } setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); skb_queue_head_init(&session->ctrl_transmit); skb_queue_head_init(&session->intr_transmit); mutex_init(&session->report_mutex); init_waitqueue_head(&session->report_queue); init_waitqueue_head(&session->startup_queue); session->waiting_for_startup = 1; session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; __hidp_link_session(session); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) if (req->rd_size > 0) { err = hidp_setup_hid(session, req); if (err) goto purge; } if (!session->hid) { err = hidp_setup_input(session, req); if (err < 0) goto purge; } #else if (session->input) { err = hidp_setup_input(session, req); if (err < 0) goto failed; } if (session->hid) hidp_setup_hid(session, req); #endif hidp_set_timer(session); if (session->hid) { vendor = session->hid->vendor; product = session->hid->product; } else if (session->input) { vendor = session->input->id.vendor; product = session->input->id.product; } else { vendor = 0x0000; product = 0x0000; } session->task = kthread_run(hidp_session, session, "khidpd_%04x%04x", vendor, product); if (IS_ERR(session->task)) { err = PTR_ERR(session->task); goto unlink; } while (session->waiting_for_startup) { wait_event_interruptible(session->startup_queue, !session->waiting_for_startup); } if (session->hid) err = hid_add_device(session->hid); else err = input_register_device(session->input); if (err < 0) { atomic_inc(&session->terminate); wake_up_process(session->task); up_write(&hidp_session_sem); return err; } if (session->input) { hidp_send_ctrl_message(session, HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0); session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); session->leds = 0xff; hidp_input_event(session->input, EV_LED, 0, 0); } up_write(&hidp_session_sem); return 0; unlink: hidp_del_timer(session); if (session->input) { input_unregister_device(session->input); session->input = NULL; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) if (session->hid) { hid_destroy_device(session->hid); session->hid = NULL; } kfree(session->rd_data); session->rd_data = NULL; purge: __hidp_unlink_session(session); skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); #endif failed: up_write(&hidp_session_sem); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)) if (session->hid) hid_free_device(session->hid); #endif kfree(session); return err; } int hidp_del_connection(struct hidp_conndel_req *req) { struct hidp_session *session; int err = 0; BT_DBG(""); down_read(&hidp_session_sem); session = __hidp_get_session(&req->bdaddr); if (session) { if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) { hidp_send_ctrl_message(session, HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0); } else { /* Flush the transmit queues */ skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); atomic_inc(&session->terminate); wake_up_process(session->task); } } else err = -ENOENT; up_read(&hidp_session_sem); return err; } int hidp_get_connlist(struct hidp_connlist_req *req) { struct hidp_session *session; int err = 0, n = 0; BT_DBG(""); down_read(&hidp_session_sem); list_for_each_entry(session, &hidp_session_list, list) { struct hidp_conninfo ci; __hidp_copy_session(session, &ci); if (copy_to_user(req->ci, &ci, sizeof(ci))) { err = -EFAULT; break; } if (++n >= req->cnum) break; req->ci++; } req->cnum = n; up_read(&hidp_session_sem); return err; } int hidp_get_conninfo(struct hidp_conninfo *ci) { struct hidp_session *session; int err = 0; down_read(&hidp_session_sem); session = __hidp_get_session(&ci->bdaddr); if (session) __hidp_copy_session(session, ci); else err = -ENOENT; up_read(&hidp_session_sem); return err; } static int __init hidp_init(void) { BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); return hidp_init_sockets(); } static void __exit hidp_exit(void) { hidp_cleanup_sockets(); } module_init(hidp_init); module_exit(hidp_exit); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("bt-proto-6"); compat-drivers-2012-09-18/net/bluetooth/hidp/Makefile0000644000175000017500000000016612026211315021542 0ustar mcgrofmcgrof# # Makefile for the Linux Bluetooth HIDP layer # obj-$(CONFIG_COMPAT_BT_HIDP) += hidp.o hidp-objs := core.o sock.o compat-drivers-2012-09-18/net/bluetooth/hidp/hidp.h0000644000175000017500000001213112026211315021172 0ustar mcgrofmcgrof/* HIDP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2003-2004 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __HIDP_H #define __HIDP_H #include #include /* HIDP header masks */ #define HIDP_HEADER_TRANS_MASK 0xf0 #define HIDP_HEADER_PARAM_MASK 0x0f /* HIDP transaction types */ #define HIDP_TRANS_HANDSHAKE 0x00 #define HIDP_TRANS_HID_CONTROL 0x10 #define HIDP_TRANS_GET_REPORT 0x40 #define HIDP_TRANS_SET_REPORT 0x50 #define HIDP_TRANS_GET_PROTOCOL 0x60 #define HIDP_TRANS_SET_PROTOCOL 0x70 #define HIDP_TRANS_GET_IDLE 0x80 #define HIDP_TRANS_SET_IDLE 0x90 #define HIDP_TRANS_DATA 0xa0 #define HIDP_TRANS_DATC 0xb0 /* HIDP handshake results */ #define HIDP_HSHK_SUCCESSFUL 0x00 #define HIDP_HSHK_NOT_READY 0x01 #define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02 #define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03 #define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04 #define HIDP_HSHK_ERR_UNKNOWN 0x0e #define HIDP_HSHK_ERR_FATAL 0x0f /* HIDP control operation parameters */ #define HIDP_CTRL_NOP 0x00 #define HIDP_CTRL_HARD_RESET 0x01 #define HIDP_CTRL_SOFT_RESET 0x02 #define HIDP_CTRL_SUSPEND 0x03 #define HIDP_CTRL_EXIT_SUSPEND 0x04 #define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05 /* HIDP data transaction headers */ #define HIDP_DATA_RTYPE_MASK 0x03 #define HIDP_DATA_RSRVD_MASK 0x0c #define HIDP_DATA_RTYPE_OTHER 0x00 #define HIDP_DATA_RTYPE_INPUT 0x01 #define HIDP_DATA_RTYPE_OUPUT 0x02 #define HIDP_DATA_RTYPE_FEATURE 0x03 /* HIDP protocol header parameters */ #define HIDP_PROTO_BOOT 0x00 #define HIDP_PROTO_REPORT 0x01 /* HIDP ioctl defines */ #define HIDPCONNADD _IOW('H', 200, int) #define HIDPCONNDEL _IOW('H', 201, int) #define HIDPGETCONNLIST _IOR('H', 210, int) #define HIDPGETCONNINFO _IOR('H', 211, int) #define HIDP_VIRTUAL_CABLE_UNPLUG 0 #define HIDP_BOOT_PROTOCOL_MODE 1 #define HIDP_BLUETOOTH_VENDOR_ID 9 #define HIDP_WAITING_FOR_RETURN 10 #define HIDP_WAITING_FOR_SEND_ACK 11 struct hidp_connadd_req { int ctrl_sock; /* Connected control socket */ int intr_sock; /* Connected interrupt socket */ __u16 parser; __u16 rd_size; __u8 __user *rd_data; __u8 country; __u8 subclass; __u16 vendor; __u16 product; __u16 version; __u32 flags; __u32 idle_to; char name[128]; }; struct hidp_conndel_req { bdaddr_t bdaddr; __u32 flags; }; struct hidp_conninfo { bdaddr_t bdaddr; __u32 flags; __u16 state; __u16 vendor; __u16 product; __u16 version; char name[128]; }; struct hidp_connlist_req { __u32 cnum; struct hidp_conninfo __user *ci; }; int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock); int hidp_del_connection(struct hidp_conndel_req *req); int hidp_get_connlist(struct hidp_connlist_req *req); int hidp_get_conninfo(struct hidp_conninfo *ci); /* HIDP session defines */ struct hidp_session { struct list_head list; struct hci_conn *conn; struct socket *ctrl_sock; struct socket *intr_sock; bdaddr_t bdaddr; unsigned long state; unsigned long flags; unsigned long idle_to; uint ctrl_mtu; uint intr_mtu; atomic_t terminate; struct task_struct *task; unsigned char keys[8]; unsigned char leds; struct input_dev *input; struct hid_device *hid; struct timer_list timer; struct sk_buff_head ctrl_transmit; struct sk_buff_head intr_transmit; /* Used in hidp_get_raw_report() */ int waiting_report_type; /* HIDP_DATA_RTYPE_* */ int waiting_report_number; /* -1 for not numbered */ struct mutex report_mutex; struct sk_buff *report_return; wait_queue_head_t report_queue; /* Used in hidp_output_raw_report() */ int output_report_success; /* boolean */ /* Report descriptor */ __u8 *rd_data; uint rd_size; wait_queue_head_t startup_queue; int waiting_for_startup; }; static inline void hidp_schedule(struct hidp_session *session) { struct sock *ctrl_sk = session->ctrl_sock->sk; struct sock *intr_sk = session->intr_sock->sk; wake_up_interruptible(sk_sleep(ctrl_sk)); wake_up_interruptible(sk_sleep(intr_sk)); } /* HIDP init defines */ extern int __init hidp_init_sockets(void); extern void __exit hidp_cleanup_sockets(void); #endif /* __HIDP_H */ compat-drivers-2012-09-18/net/bluetooth/hidp/Kconfig0000644000175000017500000000053112026211315021401 0ustar mcgrofmcgrofconfig BT_HIDP tristate "HIDP protocol support" depends on BT && INPUT select HID help HIDP (Human Interface Device Protocol) is a transport layer for HID reports. HIDP is required for the Bluetooth Human Interface Device Profile. Say Y here to compile HIDP support into the kernel or say M to compile it as module (hidp). compat-drivers-2012-09-18/net/bluetooth/rfcomm/0000755000175000017500000000000012026211315020436 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/net/bluetooth/rfcomm/tty.c0000644000175000017500000006646512026211315021443 0ustar mcgrofmcgrof/* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* * RFCOMM TTY. */ #include #include #include #include #include #include #include #define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */ #define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */ #define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */ #define RFCOMM_TTY_MINOR 0 static struct tty_driver *rfcomm_tty_driver; struct rfcomm_dev { struct tty_port port; struct list_head list; char name[12]; int id; unsigned long flags; int err; bdaddr_t src; bdaddr_t dst; u8 channel; uint modem_status; struct rfcomm_dlc *dlc; wait_queue_head_t wait; struct device *tty_dev; atomic_t wmem_alloc; struct sk_buff_head pending; }; static LIST_HEAD(rfcomm_dev_list); static DEFINE_SPINLOCK(rfcomm_dev_lock); static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); /* ---- Device functions ---- */ /* * The reason this isn't actually a race, as you no doubt have a little voice * screaming at you in your head, is that the refcount should never actually * reach zero unless the device has already been taken off the list, in * rfcomm_dev_del(). And if that's not true, we'll hit the BUG() in * rfcomm_dev_destruct() anyway. */ static void rfcomm_dev_destruct(struct tty_port *port) { struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port); struct rfcomm_dlc *dlc = dev->dlc; BT_DBG("dev %p dlc %p", dev, dlc); /* Refcount should only hit zero when called from rfcomm_dev_del() which will have taken us off the list. Everything else are refcounting bugs. */ BUG_ON(!list_empty(&dev->list)); rfcomm_dlc_lock(dlc); /* Detach DLC if it's owned by this dev */ if (dlc->owner == dev) dlc->owner = NULL; rfcomm_dlc_unlock(dlc); rfcomm_dlc_put(dlc); tty_unregister_device(rfcomm_tty_driver, dev->id); kfree(dev); /* It's safe to call module_put() here because socket still holds reference to this module. */ module_put(THIS_MODULE); } static const struct tty_port_operations rfcomm_port_ops = { .destruct = rfcomm_dev_destruct, }; static struct rfcomm_dev *__rfcomm_dev_get(int id) { struct rfcomm_dev *dev; list_for_each_entry(dev, &rfcomm_dev_list, list) if (dev->id == id) return dev; return NULL; } static struct rfcomm_dev *rfcomm_dev_get(int id) { struct rfcomm_dev *dev; spin_lock(&rfcomm_dev_lock); dev = __rfcomm_dev_get(id); if (dev) { if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) dev = NULL; else tty_port_get(&dev->port); } spin_unlock(&rfcomm_dev_lock); return dev; } static struct device *rfcomm_get_device(struct rfcomm_dev *dev) { struct hci_dev *hdev; struct hci_conn *conn; hdev = hci_get_route(&dev->dst, &dev->src); if (!hdev) return NULL; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst); hci_dev_put(hdev); return conn ? &conn->dev : NULL; } static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf) { struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); return sprintf(buf, "%s\n", batostr(&dev->dst)); } static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf) { struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); return sprintf(buf, "%d\n", dev->channel); } static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) { struct rfcomm_dev *dev, *entry; struct list_head *head = &rfcomm_dev_list; int err = 0; BT_DBG("id %d channel %d", req->dev_id, req->channel); dev = kzalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); if (!dev) return -ENOMEM; spin_lock(&rfcomm_dev_lock); if (req->dev_id < 0) { dev->id = 0; list_for_each_entry(entry, &rfcomm_dev_list, list) { if (entry->id != dev->id) break; dev->id++; head = &entry->list; } } else { dev->id = req->dev_id; list_for_each_entry(entry, &rfcomm_dev_list, list) { if (entry->id == dev->id) { err = -EADDRINUSE; goto out; } if (entry->id > dev->id - 1) break; head = &entry->list; } } if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) { err = -ENFILE; goto out; } sprintf(dev->name, "rfcomm%d", dev->id); list_add(&dev->list, head); bacpy(&dev->src, &req->src); bacpy(&dev->dst, &req->dst); dev->channel = req->channel; dev->flags = req->flags & ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); tty_port_init(&dev->port); dev->port.ops = &rfcomm_port_ops; init_waitqueue_head(&dev->wait); skb_queue_head_init(&dev->pending); rfcomm_dlc_lock(dlc); if (req->flags & (1 << RFCOMM_REUSE_DLC)) { struct sock *sk = dlc->owner; struct sk_buff *skb; BUG_ON(!sk); rfcomm_dlc_throttle(dlc); while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); skb_queue_tail(&dev->pending, skb); atomic_sub(skb->len, &sk->sk_rmem_alloc); } } dlc->data_ready = rfcomm_dev_data_ready; dlc->state_change = rfcomm_dev_state_change; dlc->modem_status = rfcomm_dev_modem_status; dlc->owner = dev; dev->dlc = dlc; rfcomm_dev_modem_status(dlc, dlc->remote_v24_sig); rfcomm_dlc_unlock(dlc); /* It's safe to call __module_get() here because socket already holds reference to this module. */ __module_get(THIS_MODULE); out: spin_unlock(&rfcomm_dev_lock); if (err < 0) goto free; dev->tty_dev = tty_port_register_device(&dev->port, rfcomm_tty_driver, dev->id, NULL); if (IS_ERR(dev->tty_dev)) { err = PTR_ERR(dev->tty_dev); list_del(&dev->list); goto free; } dev_set_drvdata(dev->tty_dev, dev); if (device_create_file(dev->tty_dev, &dev_attr_address) < 0) BT_ERR("Failed to create address attribute"); if (device_create_file(dev->tty_dev, &dev_attr_channel) < 0) BT_ERR("Failed to create channel attribute"); return dev->id; free: kfree(dev); return err; } static void rfcomm_dev_del(struct rfcomm_dev *dev) { unsigned long flags; BT_DBG("dev %p", dev); BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags)); spin_lock_irqsave(&dev->port.lock, flags); if (dev->port.count > 0) { spin_unlock_irqrestore(&dev->port.lock, flags); return; } spin_unlock_irqrestore(&dev->port.lock, flags); spin_lock(&rfcomm_dev_lock); list_del_init(&dev->list); spin_unlock(&rfcomm_dev_lock); tty_port_put(&dev->port); } /* ---- Send buffer ---- */ static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) { /* We can't let it be zero, because we don't get a callback when tx_credits becomes nonzero, hence we'd never wake up */ return dlc->mtu * (dlc->tx_credits?:1); } static void rfcomm_wfree(struct sk_buff *skb) { struct rfcomm_dev *dev = (void *) skb->sk; struct tty_struct *tty = dev->port.tty; atomic_sub(skb->truesize, &dev->wmem_alloc); if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty) tty_wakeup(tty); tty_port_put(&dev->port); } static void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) { tty_port_get(&dev->port); atomic_add(skb->truesize, &dev->wmem_alloc); skb->sk = (void *) dev; skb->destructor = rfcomm_wfree; } static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority) { if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { struct sk_buff *skb = alloc_skb(size, priority); if (skb) { rfcomm_set_owner_w(skb, dev); return skb; } } return NULL; } /* ---- Device IOCTLs ---- */ #define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP)) static int rfcomm_create_dev(struct sock *sk, void __user *arg) { struct rfcomm_dev_req req; struct rfcomm_dlc *dlc; int id; if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; BT_DBG("sk %p dev_id %d flags 0x%x", sk, req.dev_id, req.flags); if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) return -EPERM; if (req.flags & (1 << RFCOMM_REUSE_DLC)) { /* Socket must be connected */ if (sk->sk_state != BT_CONNECTED) return -EBADFD; dlc = rfcomm_pi(sk)->dlc; rfcomm_dlc_hold(dlc); } else { dlc = rfcomm_dlc_alloc(GFP_KERNEL); if (!dlc) return -ENOMEM; } id = rfcomm_dev_add(&req, dlc); if (id < 0) { rfcomm_dlc_put(dlc); return id; } if (req.flags & (1 << RFCOMM_REUSE_DLC)) { /* DLC is now used by device. * Socket must be disconnected */ sk->sk_state = BT_CLOSED; } return id; } static int rfcomm_release_dev(void __user *arg) { struct rfcomm_dev_req req; struct rfcomm_dev *dev; if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; BT_DBG("dev_id %d flags 0x%x", req.dev_id, req.flags); dev = rfcomm_dev_get(req.dev_id); if (!dev) return -ENODEV; if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) { tty_port_put(&dev->port); return -EPERM; } if (req.flags & (1 << RFCOMM_HANGUP_NOW)) rfcomm_dlc_close(dev->dlc, 0); /* Shut down TTY synchronously before freeing rfcomm_dev */ if (dev->port.tty) tty_vhangup(dev->port.tty); if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) rfcomm_dev_del(dev); tty_port_put(&dev->port); return 0; } static int rfcomm_get_dev_list(void __user *arg) { struct rfcomm_dev *dev; struct rfcomm_dev_list_req *dl; struct rfcomm_dev_info *di; int n = 0, size, err; u16 dev_num; BT_DBG(""); if (get_user(dev_num, (u16 __user *) arg)) return -EFAULT; if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di)) return -EINVAL; size = sizeof(*dl) + dev_num * sizeof(*di); dl = kzalloc(size, GFP_KERNEL); if (!dl) return -ENOMEM; di = dl->dev_info; spin_lock(&rfcomm_dev_lock); list_for_each_entry(dev, &rfcomm_dev_list, list) { if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) continue; (di + n)->id = dev->id; (di + n)->flags = dev->flags; (di + n)->state = dev->dlc->state; (di + n)->channel = dev->channel; bacpy(&(di + n)->src, &dev->src); bacpy(&(di + n)->dst, &dev->dst); if (++n >= dev_num) break; } spin_unlock(&rfcomm_dev_lock); dl->dev_num = n; size = sizeof(*dl) + n * sizeof(*di); err = copy_to_user(arg, dl, size); kfree(dl); return err ? -EFAULT : 0; } static int rfcomm_get_dev_info(void __user *arg) { struct rfcomm_dev *dev; struct rfcomm_dev_info di; int err = 0; BT_DBG(""); if (copy_from_user(&di, arg, sizeof(di))) return -EFAULT; dev = rfcomm_dev_get(di.id); if (!dev) return -ENODEV; di.flags = dev->flags; di.channel = dev->channel; di.state = dev->dlc->state; bacpy(&di.src, &dev->src); bacpy(&di.dst, &dev->dst); if (copy_to_user(arg, &di, sizeof(di))) err = -EFAULT; tty_port_put(&dev->port); return err; } int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) { BT_DBG("cmd %d arg %p", cmd, arg); switch (cmd) { case RFCOMMCREATEDEV: return rfcomm_create_dev(sk, arg); case RFCOMMRELEASEDEV: return rfcomm_release_dev(arg); case RFCOMMGETDEVLIST: return rfcomm_get_dev_list(arg); case RFCOMMGETDEVINFO: return rfcomm_get_dev_info(arg); } return -EINVAL; } /* ---- DLC callbacks ---- */ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) { struct rfcomm_dev *dev = dlc->owner; struct tty_struct *tty; if (!dev) { kfree_skb(skb); return; } tty = dev->port.tty; if (!tty || !skb_queue_empty(&dev->pending)) { skb_queue_tail(&dev->pending, skb); return; } BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); tty_insert_flip_string(tty, skb->data, skb->len); tty_flip_buffer_push(tty); kfree_skb(skb); } static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) { struct rfcomm_dev *dev = dlc->owner; if (!dev) return; BT_DBG("dlc %p dev %p err %d", dlc, dev, err); dev->err = err; wake_up_interruptible(&dev->wait); if (dlc->state == BT_CLOSED) { if (!dev->port.tty) { if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { /* Drop DLC lock here to avoid deadlock * 1. rfcomm_dev_get will take rfcomm_dev_lock * but in rfcomm_dev_add there's lock order: * rfcomm_dev_lock -> dlc lock * 2. tty_port_put will deadlock if it's * the last reference */ rfcomm_dlc_unlock(dlc); if (rfcomm_dev_get(dev->id) == NULL) { rfcomm_dlc_lock(dlc); return; } rfcomm_dev_del(dev); tty_port_put(&dev->port); rfcomm_dlc_lock(dlc); } } else tty_hangup(dev->port.tty); } } static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) { struct rfcomm_dev *dev = dlc->owner; if (!dev) return; BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) { if (dev->port.tty && !C_CLOCAL(dev->port.tty)) tty_hangup(dev->port.tty); } dev->modem_status = ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) | ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) | ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0); } /* ---- TTY functions ---- */ static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev) { struct tty_struct *tty = dev->port.tty; struct sk_buff *skb; int inserted = 0; if (!tty) return; BT_DBG("dev %p tty %p", dev, tty); rfcomm_dlc_lock(dev->dlc); while ((skb = skb_dequeue(&dev->pending))) { inserted += tty_insert_flip_string(tty, skb->data, skb->len); kfree_skb(skb); } rfcomm_dlc_unlock(dev->dlc); if (inserted > 0) tty_flip_buffer_push(tty); } static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) { DECLARE_WAITQUEUE(wait, current); struct rfcomm_dev *dev; struct rfcomm_dlc *dlc; unsigned long flags; int err, id; id = tty->index; BT_DBG("tty %p id %d", tty, id); /* We don't leak this refcount. For reasons which are not entirely clear, the TTY layer will call our ->close() method even if the open fails. We decrease the refcount there, and decreasing it here too would cause breakage. */ dev = rfcomm_dev_get(id); if (!dev) return -ENODEV; BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->port.count); spin_lock_irqsave(&dev->port.lock, flags); if (++dev->port.count > 1) { spin_unlock_irqrestore(&dev->port.lock, flags); return 0; } spin_unlock_irqrestore(&dev->port.lock, flags); dlc = dev->dlc; /* Attach TTY and open DLC */ rfcomm_dlc_lock(dlc); tty->driver_data = dev; dev->port.tty = tty; rfcomm_dlc_unlock(dlc); set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); if (err < 0) return err; /* Wait for DLC to connect */ add_wait_queue(&dev->wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (dlc->state == BT_CLOSED) { err = -dev->err; break; } if (dlc->state == BT_CONNECTED) break; if (signal_pending(current)) { err = -EINTR; break; } tty_unlock(tty); schedule(); tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&dev->wait, &wait); if (err == 0) #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) device_move(dev->tty_dev, rfcomm_get_device(dev), DPM_ORDER_DEV_AFTER_PARENT); #else device_move(dev->tty_dev, rfcomm_get_device(dev)); #endif rfcomm_tty_copy_pending(dev); rfcomm_dlc_unthrottle(dev->dlc); return err; } static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; unsigned long flags; if (!dev) return; BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->port.count); spin_lock_irqsave(&dev->port.lock, flags); if (!--dev->port.count) { spin_unlock_irqrestore(&dev->port.lock, flags); if (dev->tty_dev->parent) #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); #else device_move(dev->tty_dev, NULL); #endif /* Close DLC and dettach TTY */ rfcomm_dlc_close(dev->dlc, 0); clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); rfcomm_dlc_lock(dev->dlc); tty->driver_data = NULL; dev->port.tty = NULL; rfcomm_dlc_unlock(dev->dlc); if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) { spin_lock(&rfcomm_dev_lock); list_del_init(&dev->list); spin_unlock(&rfcomm_dev_lock); tty_port_put(&dev->port); } } else spin_unlock_irqrestore(&dev->port.lock, flags); tty_port_put(&dev->port); } static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; struct sk_buff *skb; int err = 0, sent = 0, size; BT_DBG("tty %p count %d", tty, count); while (count) { size = min_t(uint, count, dlc->mtu); skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); if (!skb) break; skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); memcpy(skb_put(skb, size), buf + sent, size); err = rfcomm_dlc_send(dlc, skb); if (err < 0) { kfree_skb(skb); break; } sent += size; count -= size; } return sent ? sent : err; } static int rfcomm_tty_write_room(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; int room; BT_DBG("tty %p", tty); if (!dev || !dev->dlc) return 0; room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc); if (room < 0) room = 0; return room; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) static int rfcomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) #else static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) #endif { BT_DBG("tty %p cmd 0x%02x", tty, cmd); switch (cmd) { case TCGETS: BT_DBG("TCGETS is not supported"); return -ENOIOCTLCMD; case TCSETS: BT_DBG("TCSETS is not supported"); return -ENOIOCTLCMD; case TIOCMIWAIT: BT_DBG("TIOCMIWAIT"); break; case TIOCGSERIAL: BT_ERR("TIOCGSERIAL is not supported"); return -ENOIOCTLCMD; case TIOCSSERIAL: BT_ERR("TIOCSSERIAL is not supported"); return -ENOIOCTLCMD; case TIOCSERGSTRUCT: BT_ERR("TIOCSERGSTRUCT is not supported"); return -ENOIOCTLCMD; case TIOCSERGETLSR: BT_ERR("TIOCSERGETLSR is not supported"); return -ENOIOCTLCMD; case TIOCSERCONFIG: BT_ERR("TIOCSERCONFIG is not supported"); return -ENOIOCTLCMD; default: return -ENOIOCTLCMD; /* ioctls which we must ignore */ } return -ENOIOCTLCMD; } static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) struct ktermios *new = &tty->termios; #else struct ktermios *new = tty->termios; #endif int old_baud_rate = tty_termios_baud_rate(old); int new_baud_rate = tty_termios_baud_rate(new); u8 baud, data_bits, stop_bits, parity, x_on, x_off; u16 changes = 0; struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p termios %p", tty, old); if (!dev || !dev->dlc || !dev->dlc->session) return; /* Handle turning off CRTSCTS */ if ((old->c_cflag & CRTSCTS) && !(new->c_cflag & CRTSCTS)) BT_DBG("Turning off CRTSCTS unsupported"); /* Parity on/off and when on, odd/even */ if (((old->c_cflag & PARENB) != (new->c_cflag & PARENB)) || ((old->c_cflag & PARODD) != (new->c_cflag & PARODD))) { changes |= RFCOMM_RPN_PM_PARITY; BT_DBG("Parity change detected."); } /* Mark and space parity are not supported! */ if (new->c_cflag & PARENB) { if (new->c_cflag & PARODD) { BT_DBG("Parity is ODD"); parity = RFCOMM_RPN_PARITY_ODD; } else { BT_DBG("Parity is EVEN"); parity = RFCOMM_RPN_PARITY_EVEN; } } else { BT_DBG("Parity is OFF"); parity = RFCOMM_RPN_PARITY_NONE; } /* Setting the x_on / x_off characters */ if (old->c_cc[VSTOP] != new->c_cc[VSTOP]) { BT_DBG("XOFF custom"); x_on = new->c_cc[VSTOP]; changes |= RFCOMM_RPN_PM_XON; } else { BT_DBG("XOFF default"); x_on = RFCOMM_RPN_XON_CHAR; } if (old->c_cc[VSTART] != new->c_cc[VSTART]) { BT_DBG("XON custom"); x_off = new->c_cc[VSTART]; changes |= RFCOMM_RPN_PM_XOFF; } else { BT_DBG("XON default"); x_off = RFCOMM_RPN_XOFF_CHAR; } /* Handle setting of stop bits */ if ((old->c_cflag & CSTOPB) != (new->c_cflag & CSTOPB)) changes |= RFCOMM_RPN_PM_STOP; /* POSIX does not support 1.5 stop bits and RFCOMM does not * support 2 stop bits. So a request for 2 stop bits gets * translated to 1.5 stop bits */ if (new->c_cflag & CSTOPB) stop_bits = RFCOMM_RPN_STOP_15; else stop_bits = RFCOMM_RPN_STOP_1; /* Handle number of data bits [5-8] */ if ((old->c_cflag & CSIZE) != (new->c_cflag & CSIZE)) changes |= RFCOMM_RPN_PM_DATA; switch (new->c_cflag & CSIZE) { case CS5: data_bits = RFCOMM_RPN_DATA_5; break; case CS6: data_bits = RFCOMM_RPN_DATA_6; break; case CS7: data_bits = RFCOMM_RPN_DATA_7; break; case CS8: data_bits = RFCOMM_RPN_DATA_8; break; default: data_bits = RFCOMM_RPN_DATA_8; break; } /* Handle baudrate settings */ if (old_baud_rate != new_baud_rate) changes |= RFCOMM_RPN_PM_BITRATE; switch (new_baud_rate) { case 2400: baud = RFCOMM_RPN_BR_2400; break; case 4800: baud = RFCOMM_RPN_BR_4800; break; case 7200: baud = RFCOMM_RPN_BR_7200; break; case 9600: baud = RFCOMM_RPN_BR_9600; break; case 19200: baud = RFCOMM_RPN_BR_19200; break; case 38400: baud = RFCOMM_RPN_BR_38400; break; case 57600: baud = RFCOMM_RPN_BR_57600; break; case 115200: baud = RFCOMM_RPN_BR_115200; break; case 230400: baud = RFCOMM_RPN_BR_230400; break; default: /* 9600 is standard accordinag to the RFCOMM specification */ baud = RFCOMM_RPN_BR_9600; break; } if (changes) rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud, data_bits, stop_bits, parity, RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); } static void rfcomm_tty_throttle(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); rfcomm_dlc_throttle(dev->dlc); } static void rfcomm_tty_unthrottle(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); rfcomm_dlc_unthrottle(dev->dlc); } static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); if (!dev || !dev->dlc) return 0; if (!skb_queue_empty(&dev->dlc->tx_queue)) return dev->dlc->mtu; return 0; } static void rfcomm_tty_flush_buffer(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); if (!dev || !dev->dlc) return; skb_queue_purge(&dev->dlc->tx_queue); tty_wakeup(tty); } static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) { BT_DBG("tty %p ch %c", tty, ch); } static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) { BT_DBG("tty %p timeout %d", tty, timeout); } static void rfcomm_tty_hangup(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); if (!dev) return; rfcomm_tty_flush_buffer(tty); if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { if (rfcomm_dev_get(dev->id) == NULL) return; rfcomm_dev_del(dev); tty_port_put(&dev->port); } } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) static int rfcomm_tty_tiocmget(struct tty_struct *tty) #else static int rfcomm_tty_tiocmget(struct tty_struct *tty, struct file *filp) #endif { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); return dev->modem_status; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) static int rfcomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) #else static int rfcomm_tty_tiocmset(struct tty_struct *tty, struct file *filp, unsigned int set, unsigned int clear) #endif { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; u8 v24_sig; BT_DBG("tty %p dev %p set 0x%02x clear 0x%02x", tty, dev, set, clear); rfcomm_dlc_get_modem_status(dlc, &v24_sig); if (set & TIOCM_DSR || set & TIOCM_DTR) v24_sig |= RFCOMM_V24_RTC; if (set & TIOCM_RTS || set & TIOCM_CTS) v24_sig |= RFCOMM_V24_RTR; if (set & TIOCM_RI) v24_sig |= RFCOMM_V24_IC; if (set & TIOCM_CD) v24_sig |= RFCOMM_V24_DV; if (clear & TIOCM_DSR || clear & TIOCM_DTR) v24_sig &= ~RFCOMM_V24_RTC; if (clear & TIOCM_RTS || clear & TIOCM_CTS) v24_sig &= ~RFCOMM_V24_RTR; if (clear & TIOCM_RI) v24_sig &= ~RFCOMM_V24_IC; if (clear & TIOCM_CD) v24_sig &= ~RFCOMM_V24_DV; rfcomm_dlc_set_modem_status(dlc, v24_sig); return 0; } /* ---- TTY structure ---- */ static const struct tty_operations rfcomm_ops = { .open = rfcomm_tty_open, .close = rfcomm_tty_close, .write = rfcomm_tty_write, .write_room = rfcomm_tty_write_room, .chars_in_buffer = rfcomm_tty_chars_in_buffer, .flush_buffer = rfcomm_tty_flush_buffer, .ioctl = rfcomm_tty_ioctl, .throttle = rfcomm_tty_throttle, .unthrottle = rfcomm_tty_unthrottle, .set_termios = rfcomm_tty_set_termios, .send_xchar = rfcomm_tty_send_xchar, .hangup = rfcomm_tty_hangup, .wait_until_sent = rfcomm_tty_wait_until_sent, .tiocmget = rfcomm_tty_tiocmget, .tiocmset = rfcomm_tty_tiocmset, }; int __init rfcomm_init_ttys(void) { int error; rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); if (!rfcomm_tty_driver) return -ENOMEM; rfcomm_tty_driver->driver_name = "rfcomm"; rfcomm_tty_driver->name = "rfcomm"; rfcomm_tty_driver->major = RFCOMM_TTY_MAJOR; rfcomm_tty_driver->minor_start = RFCOMM_TTY_MINOR; rfcomm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL; rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; rfcomm_tty_driver->init_termios = tty_std_termios; rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON; tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); error = tty_register_driver(rfcomm_tty_driver); if (error) { BT_ERR("Can't register RFCOMM TTY driver"); put_tty_driver(rfcomm_tty_driver); return error; } BT_INFO("RFCOMM TTY layer initialized"); return 0; } void rfcomm_cleanup_ttys(void) { tty_unregister_driver(rfcomm_tty_driver); put_tty_driver(rfcomm_tty_driver); } compat-drivers-2012-09-18/net/bluetooth/rfcomm/sock.c0000644000175000017500000005452312026211315021552 0ustar mcgrofmcgrof/* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* * RFCOMM sockets. */ #include #include #include #include #include #include static const struct proto_ops rfcomm_sock_ops; static struct bt_sock_list rfcomm_sk_list = { .lock = __RW_LOCK_UNLOCKED(rfcomm_sk_list.lock) }; static void rfcomm_sock_close(struct sock *sk); static void rfcomm_sock_kill(struct sock *sk); /* ---- DLC callbacks ---- * * called under rfcomm_dlc_lock() */ static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb) { struct sock *sk = d->owner; if (!sk) return; atomic_add(skb->len, &sk->sk_rmem_alloc); skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, skb->len); if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) rfcomm_dlc_throttle(d); } static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) { struct sock *sk = d->owner, *parent; unsigned long flags; if (!sk) return; BT_DBG("dlc %p state %ld err %d", d, d->state, err); local_irq_save(flags); bh_lock_sock(sk); if (err) sk->sk_err = err; sk->sk_state = d->state; parent = bt_sk(sk)->parent; if (parent) { if (d->state == BT_CLOSED) { sock_set_flag(sk, SOCK_ZAPPED); bt_accept_unlink(sk); } parent->sk_data_ready(parent, 0); } else { if (d->state == BT_CONNECTED) rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL); sk->sk_state_change(sk); } bh_unlock_sock(sk); local_irq_restore(flags); if (parent && sock_flag(sk, SOCK_ZAPPED)) { /* We have to drop DLC lock here, otherwise * rfcomm_sock_destruct() will dead lock. */ rfcomm_dlc_unlock(d); rfcomm_sock_kill(sk); rfcomm_dlc_lock(d); } } /* ---- Socket functions ---- */ static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src) { struct sock *sk = NULL; struct hlist_node *node; sk_for_each(sk, node, &rfcomm_sk_list.head) { if (rfcomm_pi(sk)->channel == channel && !bacmp(&bt_sk(sk)->src, src)) break; } return node ? sk : NULL; } /* Find socket with channel and source bdaddr. * Returns closest match. */ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src) { struct sock *sk = NULL, *sk1 = NULL; struct hlist_node *node; read_lock(&rfcomm_sk_list.lock); sk_for_each(sk, node, &rfcomm_sk_list.head) { if (state && sk->sk_state != state) continue; if (rfcomm_pi(sk)->channel == channel) { /* Exact match. */ if (!bacmp(&bt_sk(sk)->src, src)) break; /* Closest match */ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) sk1 = sk; } } read_unlock(&rfcomm_sk_list.lock); return node ? sk : sk1; } static void rfcomm_sock_destruct(struct sock *sk) { struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; BT_DBG("sk %p dlc %p", sk, d); skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); rfcomm_dlc_lock(d); rfcomm_pi(sk)->dlc = NULL; /* Detach DLC if it's owned by this socket */ if (d->owner == sk) d->owner = NULL; rfcomm_dlc_unlock(d); rfcomm_dlc_put(d); } static void rfcomm_sock_cleanup_listen(struct sock *parent) { struct sock *sk; BT_DBG("parent %p", parent); /* Close not yet accepted dlcs */ while ((sk = bt_accept_dequeue(parent, NULL))) { rfcomm_sock_close(sk); rfcomm_sock_kill(sk); } parent->sk_state = BT_CLOSED; sock_set_flag(parent, SOCK_ZAPPED); } /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */ static void rfcomm_sock_kill(struct sock *sk) { if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; BT_DBG("sk %p state %d refcnt %d", sk, sk->sk_state, atomic_read(&sk->sk_refcnt)); /* Kill poor orphan */ bt_sock_unlink(&rfcomm_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } static void __rfcomm_sock_close(struct sock *sk) { struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); switch (sk->sk_state) { case BT_LISTEN: rfcomm_sock_cleanup_listen(sk); break; case BT_CONNECT: case BT_CONNECT2: case BT_CONFIG: case BT_CONNECTED: rfcomm_dlc_close(d, 0); default: sock_set_flag(sk, SOCK_ZAPPED); break; } } /* Close socket. * Must be called on unlocked socket. */ static void rfcomm_sock_close(struct sock *sk) { lock_sock(sk); __rfcomm_sock_close(sk); release_sock(sk); } static void rfcomm_sock_init(struct sock *sk, struct sock *parent) { struct rfcomm_pinfo *pi = rfcomm_pi(sk); BT_DBG("sk %p", sk); if (parent) { sk->sk_type = parent->sk_type; pi->dlc->defer_setup = test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags); pi->sec_level = rfcomm_pi(parent)->sec_level; pi->role_switch = rfcomm_pi(parent)->role_switch; security_sk_clone(parent, sk); } else { pi->dlc->defer_setup = 0; pi->sec_level = BT_SECURITY_LOW; pi->role_switch = 0; } pi->dlc->sec_level = pi->sec_level; pi->dlc->role_switch = pi->role_switch; } static struct proto rfcomm_proto = { .name = "RFCOMM", .owner = THIS_MODULE, .obj_size = sizeof(struct rfcomm_pinfo) }; static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) { struct rfcomm_dlc *d; struct sock *sk; sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto); if (!sk) return NULL; sock_init_data(sock, sk); INIT_LIST_HEAD(&bt_sk(sk)->accept_q); d = rfcomm_dlc_alloc(prio); if (!d) { sk_free(sk); return NULL; } d->data_ready = rfcomm_sk_data_ready; d->state_change = rfcomm_sk_state_change; rfcomm_pi(sk)->dlc = d; d->owner = sk; sk->sk_destruct = rfcomm_sock_destruct; sk->sk_sndtimeo = RFCOMM_CONN_TIMEOUT; sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = proto; sk->sk_state = BT_OPEN; bt_sock_link(&rfcomm_sk_list, sk); BT_DBG("sk %p", sk); return sk; } #if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int rfcomm_sock_create(struct net *net, struct socket *sock, int protocol, int kern) #else static int rfcomm_sock_create(struct net *net, struct socket *sock, int protocol) #endif { struct sock *sk; BT_DBG("sock %p", sock); sock->state = SS_UNCONNECTED; if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; sock->ops = &rfcomm_sock_ops; sk = rfcomm_sock_alloc(net, sock, protocol, GFP_ATOMIC); if (!sk) return -ENOMEM; rfcomm_sock_init(sk, NULL); return 0; } static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr)); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; lock_sock(sk); if (sk->sk_state != BT_OPEN) { err = -EBADFD; goto done; } if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; goto done; } write_lock(&rfcomm_sk_list.lock); if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) { err = -EADDRINUSE; } else { /* Save source address */ bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; sk->sk_state = BT_BOUND; } write_unlock(&rfcomm_sk_list.lock); done: release_sock(sk); return err; } static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; struct sock *sk = sock->sk; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; int err = 0; BT_DBG("sk %p", sk); if (alen < sizeof(struct sockaddr_rc) || addr->sa_family != AF_BLUETOOTH) return -EINVAL; lock_sock(sk); if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { err = -EBADFD; goto done; } if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; goto done; } sk->sk_state = BT_CONNECT; bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; d->sec_level = rfcomm_pi(sk)->sec_level; d->role_switch = rfcomm_pi(sk)->role_switch; err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); if (!err) err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK)); done: release_sock(sk); return err; } static int rfcomm_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); lock_sock(sk); if (sk->sk_state != BT_BOUND) { err = -EBADFD; goto done; } if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; goto done; } if (!rfcomm_pi(sk)->channel) { bdaddr_t *src = &bt_sk(sk)->src; u8 channel; err = -EINVAL; write_lock(&rfcomm_sk_list.lock); for (channel = 1; channel < 31; channel++) if (!__rfcomm_get_sock_by_addr(channel, src)) { rfcomm_pi(sk)->channel = channel; err = 0; break; } write_unlock(&rfcomm_sk_list.lock); if (err < 0) goto done; } sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; sk->sk_state = BT_LISTEN; done: release_sock(sk); return err; } static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags) { DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; long timeo; int err = 0; lock_sock(sk); if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; goto done; } timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk_sleep(sk), &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; break; } nsk = bt_accept_dequeue(sk, newsock); if (nsk) break; if (!timeo) { err = -EAGAIN; break; } if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); if (err) goto done; newsock->state = SS_CONNECTED; BT_DBG("new socket %p", nsk); done: release_sock(sk); return err; } static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) { struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; struct sock *sk = sock->sk; BT_DBG("sock %p, sk %p", sock, sk); memset(sa, 0, sizeof(*sa)); sa->rc_family = AF_BLUETOOTH; sa->rc_channel = rfcomm_pi(sk)->channel; if (peer) bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst); else bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src); *len = sizeof(struct sockaddr_rc); return 0; } static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; struct sk_buff *skb; int sent = 0; if (test_bit(RFCOMM_DEFER_SETUP, &d->flags)) return -ENOTCONN; if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; if (sk->sk_shutdown & SEND_SHUTDOWN) return -EPIPE; BT_DBG("sock %p, sk %p", sock, sk); lock_sock(sk); while (len) { size_t size = min_t(size_t, len, d->mtu); int err; skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) { if (sent == 0) sent = err; break; } skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); if (err) { kfree_skb(skb); if (sent == 0) sent = err; break; } skb->priority = sk->sk_priority; err = rfcomm_dlc_send(d, skb); if (err < 0) { kfree_skb(skb); if (sent == 0) sent = err; break; } sent += size; len -= size; } release_sock(sk); return sent; } static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; int len; if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { rfcomm_dlc_accept(d); return 0; } len = bt_sock_stream_recvmsg(iocb, sock, msg, size, flags); lock_sock(sk); if (!(flags & MSG_PEEK) && len > 0) atomic_sub(len, &sk->sk_rmem_alloc); if (atomic_read(&sk->sk_rmem_alloc) <= (sk->sk_rcvbuf >> 2)) rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc); release_sock(sk); return len; } static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; int err = 0; u32 opt; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { case RFCOMM_LM: if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } if (opt & RFCOMM_LM_AUTH) rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW; if (opt & RFCOMM_LM_ENCRYPT) rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM; if (opt & RFCOMM_LM_SECURE) rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH; rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER); break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) #else static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) #endif { struct sock *sk = sock->sk; struct bt_security sec; int err = 0; size_t len; u32 opt; BT_DBG("sk %p", sk); if (level == SOL_RFCOMM) return rfcomm_sock_setsockopt_old(sock, optname, optval, optlen); if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; lock_sock(sk); switch (optname) { case BT_SECURITY: if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; break; } sec.level = BT_SECURITY_LOW; len = min_t(unsigned int, sizeof(sec), optlen); if (copy_from_user((char *) &sec, optval, len)) { err = -EFAULT; break; } if (sec.level > BT_SECURITY_HIGH) { err = -EINVAL; break; } rfcomm_pi(sk)->sec_level = sec.level; break; case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; break; } if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } if (opt) set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); else clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct rfcomm_conninfo cinfo; struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; int len, err = 0; u32 opt; BT_DBG("sk %p", sk); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case RFCOMM_LM: switch (rfcomm_pi(sk)->sec_level) { case BT_SECURITY_LOW: opt = RFCOMM_LM_AUTH; break; case BT_SECURITY_MEDIUM: opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; break; case BT_SECURITY_HIGH: opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE; break; default: opt = 0; break; } if (rfcomm_pi(sk)->role_switch) opt |= RFCOMM_LM_MASTER; if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; break; case RFCOMM_CONNINFO: if (sk->sk_state != BT_CONNECTED && !rfcomm_pi(sk)->dlc->defer_setup) { err = -ENOTCONN; break; } memset(&cinfo, 0, sizeof(cinfo)); cinfo.hci_handle = conn->hcon->handle; memcpy(cinfo.dev_class, conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *) &cinfo, len)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct bt_security sec; int len, err = 0; BT_DBG("sk %p", sk); if (level == SOL_RFCOMM) return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen); if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case BT_SECURITY: if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; break; } sec.level = rfcomm_pi(sk)->sec_level; sec.key_size = 0; len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) err = -EFAULT; break; case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; break; } if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), (u32 __user *) optval)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk __maybe_unused = sock->sk; int err; BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); err = bt_sock_ioctl(sock, cmd, arg); if (err == -ENOIOCTLCMD) { #ifdef CONFIG_COMPAT_BT_RFCOMM_TTY lock_sock(sk); err = rfcomm_dev_ioctl(sk, cmd, (void __user *) arg); release_sock(sk); #else err = -EOPNOTSUPP; #endif } return err; } static int rfcomm_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; lock_sock(sk); if (!sk->sk_shutdown) { sk->sk_shutdown = SHUTDOWN_MASK; __rfcomm_sock_close(sk); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); } release_sock(sk); return err; } static int rfcomm_sock_release(struct socket *sock) { struct sock *sk = sock->sk; int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; err = rfcomm_sock_shutdown(sock, 2); sock_orphan(sk); rfcomm_sock_kill(sk); return err; } /* ---- RFCOMM core layer callbacks ---- * * called under rfcomm_lock() */ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d) { struct sock *sk, *parent; bdaddr_t src, dst; int result = 0; BT_DBG("session %p channel %d", s, channel); rfcomm_session_getaddr(s, &src, &dst); /* Check if we have socket listening on channel */ parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &src); if (!parent) return 0; bh_lock_sock(parent); /* Check for backlog size */ if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); goto done; } sk = rfcomm_sock_alloc(sock_net(parent), NULL, BTPROTO_RFCOMM, GFP_ATOMIC); if (!sk) goto done; bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM); rfcomm_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, &src); bacpy(&bt_sk(sk)->dst, &dst); rfcomm_pi(sk)->channel = channel; sk->sk_state = BT_CONFIG; bt_accept_enqueue(parent, sk); /* Accept connection and return socket DLC */ *d = rfcomm_pi(sk)->dlc; result = 1; done: bh_unlock_sock(parent); if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) parent->sk_state_change(parent); return result; } static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p) { struct sock *sk; struct hlist_node *node; read_lock(&rfcomm_sk_list.lock); sk_for_each(sk, node, &rfcomm_sk_list.head) { seq_printf(f, "%s %s %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state, rfcomm_pi(sk)->channel); } read_unlock(&rfcomm_sk_list.lock); return 0; } static int rfcomm_sock_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, rfcomm_sock_debugfs_show, inode->i_private); } static const struct file_operations rfcomm_sock_debugfs_fops = { .open = rfcomm_sock_debugfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *rfcomm_sock_debugfs; static const struct proto_ops rfcomm_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = rfcomm_sock_release, .bind = rfcomm_sock_bind, .connect = rfcomm_sock_connect, .listen = rfcomm_sock_listen, .accept = rfcomm_sock_accept, .getname = rfcomm_sock_getname, .sendmsg = rfcomm_sock_sendmsg, .recvmsg = rfcomm_sock_recvmsg, .shutdown = rfcomm_sock_shutdown, .setsockopt = rfcomm_sock_setsockopt, .getsockopt = rfcomm_sock_getsockopt, .ioctl = rfcomm_sock_ioctl, .poll = bt_sock_poll, .socketpair = sock_no_socketpair, .mmap = sock_no_mmap }; static const struct net_proto_family rfcomm_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = rfcomm_sock_create }; int __init rfcomm_init_sockets(void) { int err; err = proto_register(&rfcomm_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops); if (err < 0) { BT_ERR("RFCOMM socket layer registration failed"); goto error; } err = bt_procfs_init(THIS_MODULE, &init_net, "rfcomm", &rfcomm_sk_list, NULL); if (err < 0) { BT_ERR("Failed to create RFCOMM proc file"); bt_sock_unregister(BTPROTO_RFCOMM); goto error; } if (bt_debugfs) { rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444, bt_debugfs, NULL, &rfcomm_sock_debugfs_fops); if (!rfcomm_sock_debugfs) BT_ERR("Failed to create RFCOMM debug file"); } BT_INFO("RFCOMM socket layer initialized"); return 0; error: proto_unregister(&rfcomm_proto); return err; } void __exit rfcomm_cleanup_sockets(void) { bt_procfs_cleanup(&init_net, "rfcomm"); debugfs_remove(rfcomm_sock_debugfs); if (bt_sock_unregister(BTPROTO_RFCOMM) < 0) BT_ERR("RFCOMM socket layer unregistration failed"); proto_unregister(&rfcomm_proto); } compat-drivers-2012-09-18/net/bluetooth/rfcomm/Makefile0000644000175000017500000000024612026211315022100 0ustar mcgrofmcgrof# # Makefile for the Linux Bluetooth RFCOMM layer. # obj-$(CONFIG_BT_RFCOMM) += rfcomm.o rfcomm-y := core.o sock.o rfcomm-$(CONFIG_COMPAT_BT_RFCOMM_TTY) += tty.o compat-drivers-2012-09-18/net/bluetooth/rfcomm/core.c0000644000175000017500000014314712026211315021544 0ustar mcgrofmcgrof/* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* * Bluetooth RFCOMM core. */ #include #include #include #include #include #include #include #include #define VERSION "1.11" static bool disable_cfc; static bool l2cap_ertm; static int channel_mtu = -1; static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU; static struct task_struct *rfcomm_thread; static DEFINE_MUTEX(rfcomm_mutex); #define rfcomm_lock() mutex_lock(&rfcomm_mutex) #define rfcomm_unlock() mutex_unlock(&rfcomm_mutex) static LIST_HEAD(session_list); static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci); static int rfcomm_queue_disc(struct rfcomm_dlc *d); static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type); static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d); static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig); static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len); static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits); static void rfcomm_make_uih(struct sk_buff *skb, u8 addr); static void rfcomm_process_connect(struct rfcomm_session *s); static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, u8 sec_level, int *err); static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); static void rfcomm_session_del(struct rfcomm_session *s); /* ---- RFCOMM frame parsing macros ---- */ #define __get_dlci(b) ((b & 0xfc) >> 2) #define __get_channel(b) ((b & 0xf8) >> 3) #define __get_dir(b) ((b & 0x04) >> 2) #define __get_type(b) ((b & 0xef)) #define __test_ea(b) ((b & 0x01)) #define __test_cr(b) ((b & 0x02)) #define __test_pf(b) ((b & 0x10)) #define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) #define __ctrl(type, pf) (((type & 0xef) | (pf << 4))) #define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir) #define __srv_channel(dlci) (dlci >> 1) #define __dir(dlci) (dlci & 0x01) #define __len8(len) (((len) << 1) | 1) #define __len16(len) ((len) << 1) /* MCC macros */ #define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01)) #define __get_mcc_type(b) ((b & 0xfc) >> 2) #define __get_mcc_len(b) ((b & 0xfe) >> 1) /* RPN macros */ #define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x7) << 3)) #define __get_rpn_data_bits(line) ((line) & 0x3) #define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1) #define __get_rpn_parity(line) (((line) >> 3) & 0x7) static void rfcomm_schedule(void) { if (!rfcomm_thread) return; wake_up_process(rfcomm_thread); } static void rfcomm_session_put(struct rfcomm_session *s) { if (atomic_dec_and_test(&s->refcnt)) rfcomm_session_del(s); } /* ---- RFCOMM FCS computation ---- */ /* reversed, 8-bit, poly=0x07 */ static unsigned char rfcomm_crc_table[256] = { 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf }; /* CRC on 2 bytes */ #define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) /* FCS on 2 bytes */ static inline u8 __fcs(u8 *data) { return 0xff - __crc(data); } /* FCS on 3 bytes */ static inline u8 __fcs2(u8 *data) { return 0xff - rfcomm_crc_table[__crc(data) ^ data[2]]; } /* Check FCS */ static inline int __check_fcs(u8 *data, int type, u8 fcs) { u8 f = __crc(data); if (type != RFCOMM_UIH) f = rfcomm_crc_table[f ^ data[2]]; return rfcomm_crc_table[f ^ fcs] != 0xcf; } /* ---- L2CAP callbacks ---- */ static void rfcomm_l2state_change(struct sock *sk) { BT_DBG("%p state %d", sk, sk->sk_state); rfcomm_schedule(); } static void rfcomm_l2data_ready(struct sock *sk, int bytes) { BT_DBG("%p bytes %d", sk, bytes); rfcomm_schedule(); } static int rfcomm_l2sock_create(struct socket **sock) { int err; BT_DBG(""); err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock); if (!err) { struct sock *sk = (*sock)->sk; sk->sk_data_ready = rfcomm_l2data_ready; sk->sk_state_change = rfcomm_l2state_change; } return err; } static int rfcomm_check_security(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; __u8 auth_type; switch (d->sec_level) { case BT_SECURITY_HIGH: auth_type = HCI_AT_GENERAL_BONDING_MITM; break; case BT_SECURITY_MEDIUM: auth_type = HCI_AT_GENERAL_BONDING; break; default: auth_type = HCI_AT_NO_BONDING; break; } return hci_conn_security(conn->hcon, d->sec_level, auth_type); } static void rfcomm_session_timeout(unsigned long arg) { struct rfcomm_session *s = (void *) arg; BT_DBG("session %p state %ld", s, s->state); set_bit(RFCOMM_TIMED_OUT, &s->flags); rfcomm_schedule(); } static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) { BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); if (!mod_timer(&s->timer, jiffies + timeout)) rfcomm_session_hold(s); } static void rfcomm_session_clear_timer(struct rfcomm_session *s) { BT_DBG("session %p state %ld", s, s->state); if (timer_pending(&s->timer) && del_timer(&s->timer)) rfcomm_session_put(s); } /* ---- RFCOMM DLCs ---- */ static void rfcomm_dlc_timeout(unsigned long arg) { struct rfcomm_dlc *d = (void *) arg; BT_DBG("dlc %p state %ld", d, d->state); set_bit(RFCOMM_TIMED_OUT, &d->flags); rfcomm_dlc_put(d); rfcomm_schedule(); } static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout) { BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout); if (!mod_timer(&d->timer, jiffies + timeout)) rfcomm_dlc_hold(d); } static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d) { BT_DBG("dlc %p state %ld", d, d->state); if (timer_pending(&d->timer) && del_timer(&d->timer)) rfcomm_dlc_put(d); } static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d) { BT_DBG("%p", d); d->state = BT_OPEN; d->flags = 0; d->mscex = 0; d->sec_level = BT_SECURITY_LOW; d->mtu = RFCOMM_DEFAULT_MTU; d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV; d->cfc = RFCOMM_CFC_DISABLED; d->rx_credits = RFCOMM_DEFAULT_CREDITS; } struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) { struct rfcomm_dlc *d = kzalloc(sizeof(*d), prio); if (!d) return NULL; setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d); skb_queue_head_init(&d->tx_queue); spin_lock_init(&d->lock); atomic_set(&d->refcnt, 1); rfcomm_dlc_clear_state(d); BT_DBG("%p", d); return d; } void rfcomm_dlc_free(struct rfcomm_dlc *d) { BT_DBG("%p", d); skb_queue_purge(&d->tx_queue); kfree(d); } static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) { BT_DBG("dlc %p session %p", d, s); rfcomm_session_hold(s); rfcomm_session_clear_timer(s); rfcomm_dlc_hold(d); list_add(&d->list, &s->dlcs); d->session = s; } static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) { struct rfcomm_session *s = d->session; BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s); list_del(&d->list); d->session = NULL; rfcomm_dlc_put(d); if (list_empty(&s->dlcs)) rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); rfcomm_session_put(s); } static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) { struct rfcomm_dlc *d; list_for_each_entry(d, &s->dlcs, list) if (d->dlci == dlci) return d; return NULL; } static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) { struct rfcomm_session *s; int err = 0; u8 dlci; BT_DBG("dlc %p state %ld %s %s channel %d", d, d->state, batostr(src), batostr(dst), channel); if (channel < 1 || channel > 30) return -EINVAL; if (d->state != BT_OPEN && d->state != BT_CLOSED) return 0; s = rfcomm_session_get(src, dst); if (!s) { s = rfcomm_session_create(src, dst, d->sec_level, &err); if (!s) return err; } dlci = __dlci(!s->initiator, channel); /* Check if DLCI already exists */ if (rfcomm_dlc_get(s, dlci)) return -EBUSY; rfcomm_dlc_clear_state(d); d->dlci = dlci; d->addr = __addr(s->initiator, dlci); d->priority = 7; d->state = BT_CONFIG; rfcomm_dlc_link(s, d); d->out = 1; d->mtu = s->mtu; d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; if (s->state == BT_CONNECTED) { if (rfcomm_check_security(d)) rfcomm_send_pn(s, 1, d); else set_bit(RFCOMM_AUTH_PENDING, &d->flags); } rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); return 0; } int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) { int r; rfcomm_lock(); r = __rfcomm_dlc_open(d, src, dst, channel); rfcomm_unlock(); return r; } static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) { struct rfcomm_session *s = d->session; if (!s) return 0; BT_DBG("dlc %p state %ld dlci %d err %d session %p", d, d->state, d->dlci, err, s); switch (d->state) { case BT_CONNECT: case BT_CONFIG: if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { set_bit(RFCOMM_AUTH_REJECT, &d->flags); rfcomm_schedule(); break; } /* Fall through */ case BT_CONNECTED: d->state = BT_DISCONN; if (skb_queue_empty(&d->tx_queue)) { rfcomm_send_disc(s, d->dlci); rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT); } else { rfcomm_queue_disc(d); rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2); } break; case BT_OPEN: case BT_CONNECT2: if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { set_bit(RFCOMM_AUTH_REJECT, &d->flags); rfcomm_schedule(); break; } /* Fall through */ default: rfcomm_dlc_clear_timer(d); rfcomm_dlc_lock(d); d->state = BT_CLOSED; d->state_change(d, err); rfcomm_dlc_unlock(d); skb_queue_purge(&d->tx_queue); rfcomm_dlc_unlink(d); } return 0; } int rfcomm_dlc_close(struct rfcomm_dlc *d, int err) { int r; rfcomm_lock(); r = __rfcomm_dlc_close(d, err); rfcomm_unlock(); return r; } int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) { int len = skb->len; if (d->state != BT_CONNECTED) return -ENOTCONN; BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); if (len > d->mtu) return -EINVAL; rfcomm_make_uih(skb, d->addr); skb_queue_tail(&d->tx_queue, skb); if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags)) rfcomm_schedule(); return len; } void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) { BT_DBG("dlc %p state %ld", d, d->state); if (!d->cfc) { d->v24_sig |= RFCOMM_V24_FC; set_bit(RFCOMM_MSC_PENDING, &d->flags); } rfcomm_schedule(); } void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) { BT_DBG("dlc %p state %ld", d, d->state); if (!d->cfc) { d->v24_sig &= ~RFCOMM_V24_FC; set_bit(RFCOMM_MSC_PENDING, &d->flags); } rfcomm_schedule(); } /* Set/get modem status functions use _local_ status i.e. what we report to the other side. Remote status is provided by dlc->modem_status() callback. */ int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig) { BT_DBG("dlc %p state %ld v24_sig 0x%x", d, d->state, v24_sig); if (test_bit(RFCOMM_RX_THROTTLED, &d->flags)) v24_sig |= RFCOMM_V24_FC; else v24_sig &= ~RFCOMM_V24_FC; d->v24_sig = v24_sig; if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags)) rfcomm_schedule(); return 0; } int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig) { BT_DBG("dlc %p state %ld v24_sig 0x%x", d, d->state, d->v24_sig); *v24_sig = d->v24_sig; return 0; } /* ---- RFCOMM sessions ---- */ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) { struct rfcomm_session *s = kzalloc(sizeof(*s), GFP_KERNEL); if (!s) return NULL; BT_DBG("session %p sock %p", s, sock); setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s); INIT_LIST_HEAD(&s->dlcs); s->state = state; s->sock = sock; s->mtu = RFCOMM_DEFAULT_MTU; s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN; /* Do not increment module usage count for listening sessions. * Otherwise we won't be able to unload the module. */ if (state != BT_LISTEN) if (!try_module_get(THIS_MODULE)) { kfree(s); return NULL; } list_add(&s->list, &session_list); return s; } static void rfcomm_session_del(struct rfcomm_session *s) { int state = s->state; BT_DBG("session %p state %ld", s, s->state); list_del(&s->list); if (state == BT_CONNECTED) rfcomm_send_disc(s, 0); rfcomm_session_clear_timer(s); sock_release(s->sock); kfree(s); if (state != BT_LISTEN) module_put(THIS_MODULE); } static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) { struct rfcomm_session *s; struct list_head *p, *n; struct bt_sock *sk; list_for_each_safe(p, n, &session_list) { s = list_entry(p, struct rfcomm_session, list); sk = bt_sk(s->sock->sk); if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) && !bacmp(&sk->dst, dst)) return s; } return NULL; } static void rfcomm_session_close(struct rfcomm_session *s, int err) { struct rfcomm_dlc *d; struct list_head *p, *n; BT_DBG("session %p state %ld err %d", s, s->state, err); rfcomm_session_hold(s); s->state = BT_CLOSED; /* Close all dlcs */ list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); d->state = BT_CLOSED; __rfcomm_dlc_close(d, err); } rfcomm_session_clear_timer(s); rfcomm_session_put(s); } static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, u8 sec_level, int *err) { struct rfcomm_session *s = NULL; struct sockaddr_l2 addr; struct socket *sock; struct sock *sk; BT_DBG("%s %s", batostr(src), batostr(dst)); *err = rfcomm_l2sock_create(&sock); if (*err < 0) return NULL; bacpy(&addr.l2_bdaddr, src); addr.l2_family = AF_BLUETOOTH; addr.l2_psm = 0; addr.l2_cid = 0; *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); if (*err < 0) goto failed; /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); l2cap_pi(sk)->chan->imtu = l2cap_mtu; l2cap_pi(sk)->chan->sec_level = sec_level; if (l2cap_ertm) l2cap_pi(sk)->chan->mode = L2CAP_MODE_ERTM; release_sock(sk); s = rfcomm_session_add(sock, BT_BOUND); if (!s) { *err = -ENOMEM; goto failed; } s->initiator = 1; bacpy(&addr.l2_bdaddr, dst); addr.l2_family = AF_BLUETOOTH; addr.l2_psm = cpu_to_le16(RFCOMM_PSM); addr.l2_cid = 0; *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); if (*err == 0 || *err == -EINPROGRESS) return s; rfcomm_session_del(s); return NULL; failed: sock_release(sock); return NULL; } void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst) { struct sock *sk = s->sock->sk; if (src) bacpy(src, &bt_sk(sk)->src); if (dst) bacpy(dst, &bt_sk(sk)->dst); } /* ---- RFCOMM frame sending ---- */ static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) { struct kvec iv = { data, len }; struct msghdr msg; BT_DBG("session %p len %d", s, len); memset(&msg, 0, sizeof(msg)); return kernel_sendmsg(s->sock, &msg, &iv, 1, len); } static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd) { BT_DBG("%p cmd %u", s, cmd->ctrl); return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd)); } static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) { struct rfcomm_cmd cmd; BT_DBG("%p dlci %d", s, dlci); cmd.addr = __addr(s->initiator, dlci); cmd.ctrl = __ctrl(RFCOMM_SABM, 1); cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) { struct rfcomm_cmd cmd; BT_DBG("%p dlci %d", s, dlci); cmd.addr = __addr(!s->initiator, dlci); cmd.ctrl = __ctrl(RFCOMM_UA, 1); cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) { struct rfcomm_cmd cmd; BT_DBG("%p dlci %d", s, dlci); cmd.addr = __addr(s->initiator, dlci); cmd.ctrl = __ctrl(RFCOMM_DISC, 1); cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); return rfcomm_send_cmd(s, &cmd); } static int rfcomm_queue_disc(struct rfcomm_dlc *d) { struct rfcomm_cmd *cmd; struct sk_buff *skb; BT_DBG("dlc %p dlci %d", d, d->dlci); skb = alloc_skb(sizeof(*cmd), GFP_KERNEL); if (!skb) return -ENOMEM; cmd = (void *) __skb_put(skb, sizeof(*cmd)); cmd->addr = d->addr; cmd->ctrl = __ctrl(RFCOMM_DISC, 1); cmd->len = __len8(0); cmd->fcs = __fcs2((u8 *) cmd); skb_queue_tail(&d->tx_queue, skb); rfcomm_schedule(); return 0; } static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) { struct rfcomm_cmd cmd; BT_DBG("%p dlci %d", s, dlci); cmd.addr = __addr(!s->initiator, dlci); cmd.ctrl = __ctrl(RFCOMM_DM, 1); cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; u8 buf[16], *ptr = buf; BT_DBG("%p cr %d type %d", s, cr, type); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); hdr->ctrl = __ctrl(RFCOMM_UIH, 0); hdr->len = __len8(sizeof(*mcc) + 1); mcc = (void *) ptr; ptr += sizeof(*mcc); mcc->type = __mcc_type(cr, RFCOMM_NSC); mcc->len = __len8(1); /* Type that we didn't like */ *ptr = __mcc_type(cr, type); ptr++; *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; struct rfcomm_pn *pn; u8 buf[16], *ptr = buf; BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); hdr->ctrl = __ctrl(RFCOMM_UIH, 0); hdr->len = __len8(sizeof(*mcc) + sizeof(*pn)); mcc = (void *) ptr; ptr += sizeof(*mcc); mcc->type = __mcc_type(cr, RFCOMM_PN); mcc->len = __len8(sizeof(*pn)); pn = (void *) ptr; ptr += sizeof(*pn); pn->dlci = d->dlci; pn->priority = d->priority; pn->ack_timer = 0; pn->max_retrans = 0; if (s->cfc) { pn->flow_ctrl = cr ? 0xf0 : 0xe0; pn->credits = RFCOMM_DEFAULT_CREDITS; } else { pn->flow_ctrl = 0; pn->credits = 0; } if (cr && channel_mtu >= 0) pn->mtu = cpu_to_le16(channel_mtu); else pn->mtu = cpu_to_le16(d->mtu); *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, u8 bit_rate, u8 data_bits, u8 stop_bits, u8 parity, u8 flow_ctrl_settings, u8 xon_char, u8 xoff_char, u16 param_mask) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; struct rfcomm_rpn *rpn; u8 buf[16], *ptr = buf; BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x" " flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", s, cr, dlci, bit_rate, data_bits, stop_bits, parity, flow_ctrl_settings, xon_char, xoff_char, param_mask); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); hdr->ctrl = __ctrl(RFCOMM_UIH, 0); hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn)); mcc = (void *) ptr; ptr += sizeof(*mcc); mcc->type = __mcc_type(cr, RFCOMM_RPN); mcc->len = __len8(sizeof(*rpn)); rpn = (void *) ptr; ptr += sizeof(*rpn); rpn->dlci = __addr(1, dlci); rpn->bit_rate = bit_rate; rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity); rpn->flow_ctrl = flow_ctrl_settings; rpn->xon_char = xon_char; rpn->xoff_char = xoff_char; rpn->param_mask = cpu_to_le16(param_mask); *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; struct rfcomm_rls *rls; u8 buf[16], *ptr = buf; BT_DBG("%p cr %d status 0x%x", s, cr, status); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); hdr->ctrl = __ctrl(RFCOMM_UIH, 0); hdr->len = __len8(sizeof(*mcc) + sizeof(*rls)); mcc = (void *) ptr; ptr += sizeof(*mcc); mcc->type = __mcc_type(cr, RFCOMM_RLS); mcc->len = __len8(sizeof(*rls)); rls = (void *) ptr; ptr += sizeof(*rls); rls->dlci = __addr(1, dlci); rls->status = status; *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; struct rfcomm_msc *msc; u8 buf[16], *ptr = buf; BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); hdr->ctrl = __ctrl(RFCOMM_UIH, 0); hdr->len = __len8(sizeof(*mcc) + sizeof(*msc)); mcc = (void *) ptr; ptr += sizeof(*mcc); mcc->type = __mcc_type(cr, RFCOMM_MSC); mcc->len = __len8(sizeof(*msc)); msc = (void *) ptr; ptr += sizeof(*msc); msc->dlci = __addr(1, dlci); msc->v24_sig = v24_sig | 0x01; *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; u8 buf[16], *ptr = buf; BT_DBG("%p cr %d", s, cr); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); hdr->ctrl = __ctrl(RFCOMM_UIH, 0); hdr->len = __len8(sizeof(*mcc)); mcc = (void *) ptr; ptr += sizeof(*mcc); mcc->type = __mcc_type(cr, RFCOMM_FCOFF); mcc->len = __len8(0); *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; u8 buf[16], *ptr = buf; BT_DBG("%p cr %d", s, cr); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); hdr->ctrl = __ctrl(RFCOMM_UIH, 0); hdr->len = __len8(sizeof(*mcc)); mcc = (void *) ptr; ptr += sizeof(*mcc); mcc->type = __mcc_type(cr, RFCOMM_FCON); mcc->len = __len8(0); *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) { struct socket *sock = s->sock; struct kvec iv[3]; struct msghdr msg; unsigned char hdr[5], crc[1]; if (len > 125) return -EINVAL; BT_DBG("%p cr %d", s, cr); hdr[0] = __addr(s->initiator, 0); hdr[1] = __ctrl(RFCOMM_UIH, 0); hdr[2] = 0x01 | ((len + 2) << 1); hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2); hdr[4] = 0x01 | (len << 1); crc[0] = __fcs(hdr); iv[0].iov_base = hdr; iv[0].iov_len = 5; iv[1].iov_base = pattern; iv[1].iov_len = len; iv[2].iov_base = crc; iv[2].iov_len = 1; memset(&msg, 0, sizeof(msg)); return kernel_sendmsg(sock, &msg, iv, 3, 6 + len); } static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits) { struct rfcomm_hdr *hdr; u8 buf[16], *ptr = buf; BT_DBG("%p addr %d credits %d", s, addr, credits); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = addr; hdr->ctrl = __ctrl(RFCOMM_UIH, 1); hdr->len = __len8(0); *ptr = credits; ptr++; *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) { struct rfcomm_hdr *hdr; int len = skb->len; u8 *crc; if (len > 127) { hdr = (void *) skb_push(skb, 4); put_unaligned(cpu_to_le16(__len16(len)), (__le16 *) &hdr->len); } else { hdr = (void *) skb_push(skb, 3); hdr->len = __len8(len); } hdr->addr = addr; hdr->ctrl = __ctrl(RFCOMM_UIH, 0); crc = skb_put(skb, 1); *crc = __fcs((void *) hdr); } /* ---- RFCOMM frame reception ---- */ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) { BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); if (dlci) { /* Data channel */ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); if (!d) { rfcomm_send_dm(s, dlci); return 0; } switch (d->state) { case BT_CONNECT: rfcomm_dlc_clear_timer(d); rfcomm_dlc_lock(d); d->state = BT_CONNECTED; d->state_change(d, 0); rfcomm_dlc_unlock(d); rfcomm_send_msc(s, 1, dlci, d->v24_sig); break; case BT_DISCONN: d->state = BT_CLOSED; __rfcomm_dlc_close(d, 0); if (list_empty(&s->dlcs)) { s->state = BT_DISCONN; rfcomm_send_disc(s, 0); rfcomm_session_clear_timer(s); } break; } } else { /* Control channel */ switch (s->state) { case BT_CONNECT: s->state = BT_CONNECTED; rfcomm_process_connect(s); break; case BT_DISCONN: /* rfcomm_session_put is called later so don't do * anything here otherwise we will mess up the session * reference counter: * * (a) when we are the initiator dlc_unlink will drive * the reference counter to 0 (there is no initial put * after session_add) * * (b) when we are not the initiator rfcomm_rx_process * will explicitly call put to balance the initial hold * done after session add. */ break; } } return 0; } static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) { int err = 0; BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); if (dlci) { /* Data DLC */ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); if (d) { if (d->state == BT_CONNECT || d->state == BT_CONFIG) err = ECONNREFUSED; else err = ECONNRESET; d->state = BT_CLOSED; __rfcomm_dlc_close(d, err); } } else { if (s->state == BT_CONNECT) err = ECONNREFUSED; else err = ECONNRESET; s->state = BT_CLOSED; rfcomm_session_close(s, err); } return 0; } static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) { int err = 0; BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); if (dlci) { struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); if (d) { rfcomm_send_ua(s, dlci); if (d->state == BT_CONNECT || d->state == BT_CONFIG) err = ECONNREFUSED; else err = ECONNRESET; d->state = BT_CLOSED; __rfcomm_dlc_close(d, err); } else rfcomm_send_dm(s, dlci); } else { rfcomm_send_ua(s, 0); if (s->state == BT_CONNECT) err = ECONNREFUSED; else err = ECONNRESET; s->state = BT_CLOSED; rfcomm_session_close(s, err); } return 0; } void rfcomm_dlc_accept(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; BT_DBG("dlc %p", d); rfcomm_send_ua(d->session, d->dlci); rfcomm_dlc_clear_timer(d); rfcomm_dlc_lock(d); d->state = BT_CONNECTED; d->state_change(d, 0); rfcomm_dlc_unlock(d); if (d->role_switch) hci_conn_switch_role(conn->hcon, 0x00); rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); } static void rfcomm_check_accept(struct rfcomm_dlc *d) { if (rfcomm_check_security(d)) { if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); rfcomm_dlc_lock(d); d->state = BT_CONNECT2; d->state_change(d, 0); rfcomm_dlc_unlock(d); } else rfcomm_dlc_accept(d); } else { set_bit(RFCOMM_AUTH_PENDING, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); } } static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) { struct rfcomm_dlc *d; u8 channel; BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); if (!dlci) { rfcomm_send_ua(s, 0); if (s->state == BT_OPEN) { s->state = BT_CONNECTED; rfcomm_process_connect(s); } return 0; } /* Check if DLC exists */ d = rfcomm_dlc_get(s, dlci); if (d) { if (d->state == BT_OPEN) { /* DLC was previously opened by PN request */ rfcomm_check_accept(d); } return 0; } /* Notify socket layer about incoming connection */ channel = __srv_channel(dlci); if (rfcomm_connect_ind(s, channel, &d)) { d->dlci = dlci; d->addr = __addr(s->initiator, dlci); rfcomm_dlc_link(s, d); rfcomm_check_accept(d); } else { rfcomm_send_dm(s, dlci); } return 0; } static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) { struct rfcomm_session *s = d->session; BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); if ((pn->flow_ctrl == 0xf0 && s->cfc != RFCOMM_CFC_DISABLED) || pn->flow_ctrl == 0xe0) { d->cfc = RFCOMM_CFC_ENABLED; d->tx_credits = pn->credits; } else { d->cfc = RFCOMM_CFC_DISABLED; set_bit(RFCOMM_TX_THROTTLED, &d->flags); } if (s->cfc == RFCOMM_CFC_UNKNOWN) s->cfc = d->cfc; d->priority = pn->priority; d->mtu = __le16_to_cpu(pn->mtu); if (cr && d->mtu > s->mtu) d->mtu = s->mtu; return 0; } static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) { struct rfcomm_pn *pn = (void *) skb->data; struct rfcomm_dlc *d; u8 dlci = pn->dlci; BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); if (!dlci) return 0; d = rfcomm_dlc_get(s, dlci); if (d) { if (cr) { /* PN request */ rfcomm_apply_pn(d, cr, pn); rfcomm_send_pn(s, 0, d); } else { /* PN response */ switch (d->state) { case BT_CONFIG: rfcomm_apply_pn(d, cr, pn); d->state = BT_CONNECT; rfcomm_send_sabm(s, d->dlci); break; } } } else { u8 channel = __srv_channel(dlci); if (!cr) return 0; /* PN request for non existing DLC. * Assume incoming connection. */ if (rfcomm_connect_ind(s, channel, &d)) { d->dlci = dlci; d->addr = __addr(s->initiator, dlci); rfcomm_dlc_link(s, d); rfcomm_apply_pn(d, cr, pn); d->state = BT_OPEN; rfcomm_send_pn(s, 0, d); } else { rfcomm_send_dm(s, dlci); } } return 0; } static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb) { struct rfcomm_rpn *rpn = (void *) skb->data; u8 dlci = __get_dlci(rpn->dlci); u8 bit_rate = 0; u8 data_bits = 0; u8 stop_bits = 0; u8 parity = 0; u8 flow_ctrl = 0; u8 xon_char = 0; u8 xoff_char = 0; u16 rpn_mask = RFCOMM_RPN_PM_ALL; BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, rpn->xon_char, rpn->xoff_char, rpn->param_mask); if (!cr) return 0; if (len == 1) { /* This is a request, return default (according to ETSI TS 07.10) settings */ bit_rate = RFCOMM_RPN_BR_9600; data_bits = RFCOMM_RPN_DATA_8; stop_bits = RFCOMM_RPN_STOP_1; parity = RFCOMM_RPN_PARITY_NONE; flow_ctrl = RFCOMM_RPN_FLOW_NONE; xon_char = RFCOMM_RPN_XON_CHAR; xoff_char = RFCOMM_RPN_XOFF_CHAR; goto rpn_out; } /* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit, * no parity, no flow control lines, normal XON/XOFF chars */ if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_BITRATE)) { bit_rate = rpn->bit_rate; if (bit_rate > RFCOMM_RPN_BR_230400) { BT_DBG("RPN bit rate mismatch 0x%x", bit_rate); bit_rate = RFCOMM_RPN_BR_9600; rpn_mask ^= RFCOMM_RPN_PM_BITRATE; } } if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_DATA)) { data_bits = __get_rpn_data_bits(rpn->line_settings); if (data_bits != RFCOMM_RPN_DATA_8) { BT_DBG("RPN data bits mismatch 0x%x", data_bits); data_bits = RFCOMM_RPN_DATA_8; rpn_mask ^= RFCOMM_RPN_PM_DATA; } } if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_STOP)) { stop_bits = __get_rpn_stop_bits(rpn->line_settings); if (stop_bits != RFCOMM_RPN_STOP_1) { BT_DBG("RPN stop bits mismatch 0x%x", stop_bits); stop_bits = RFCOMM_RPN_STOP_1; rpn_mask ^= RFCOMM_RPN_PM_STOP; } } if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_PARITY)) { parity = __get_rpn_parity(rpn->line_settings); if (parity != RFCOMM_RPN_PARITY_NONE) { BT_DBG("RPN parity mismatch 0x%x", parity); parity = RFCOMM_RPN_PARITY_NONE; rpn_mask ^= RFCOMM_RPN_PM_PARITY; } } if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_FLOW)) { flow_ctrl = rpn->flow_ctrl; if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) { BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl); flow_ctrl = RFCOMM_RPN_FLOW_NONE; rpn_mask ^= RFCOMM_RPN_PM_FLOW; } } if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XON)) { xon_char = rpn->xon_char; if (xon_char != RFCOMM_RPN_XON_CHAR) { BT_DBG("RPN XON char mismatch 0x%x", xon_char); xon_char = RFCOMM_RPN_XON_CHAR; rpn_mask ^= RFCOMM_RPN_PM_XON; } } if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XOFF)) { xoff_char = rpn->xoff_char; if (xoff_char != RFCOMM_RPN_XOFF_CHAR) { BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char); xoff_char = RFCOMM_RPN_XOFF_CHAR; rpn_mask ^= RFCOMM_RPN_PM_XOFF; } } rpn_out: rfcomm_send_rpn(s, 0, dlci, bit_rate, data_bits, stop_bits, parity, flow_ctrl, xon_char, xoff_char, rpn_mask); return 0; } static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb) { struct rfcomm_rls *rls = (void *) skb->data; u8 dlci = __get_dlci(rls->dlci); BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); if (!cr) return 0; /* We should probably do something with this information here. But * for now it's sufficient just to reply -- Bluetooth 1.1 says it's * mandatory to recognise and respond to RLS */ rfcomm_send_rls(s, 0, dlci, rls->status); return 0; } static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) { struct rfcomm_msc *msc = (void *) skb->data; struct rfcomm_dlc *d; u8 dlci = __get_dlci(msc->dlci); BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); d = rfcomm_dlc_get(s, dlci); if (!d) return 0; if (cr) { if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc) set_bit(RFCOMM_TX_THROTTLED, &d->flags); else clear_bit(RFCOMM_TX_THROTTLED, &d->flags); rfcomm_dlc_lock(d); d->remote_v24_sig = msc->v24_sig; if (d->modem_status) d->modem_status(d, msc->v24_sig); rfcomm_dlc_unlock(d); rfcomm_send_msc(s, 0, dlci, msc->v24_sig); d->mscex |= RFCOMM_MSCEX_RX; } else d->mscex |= RFCOMM_MSCEX_TX; return 0; } static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) { struct rfcomm_mcc *mcc = (void *) skb->data; u8 type, cr, len; cr = __test_cr(mcc->type); type = __get_mcc_type(mcc->type); len = __get_mcc_len(mcc->len); BT_DBG("%p type 0x%x cr %d", s, type, cr); skb_pull(skb, 2); switch (type) { case RFCOMM_PN: rfcomm_recv_pn(s, cr, skb); break; case RFCOMM_RPN: rfcomm_recv_rpn(s, cr, len, skb); break; case RFCOMM_RLS: rfcomm_recv_rls(s, cr, skb); break; case RFCOMM_MSC: rfcomm_recv_msc(s, cr, skb); break; case RFCOMM_FCOFF: if (cr) { set_bit(RFCOMM_TX_THROTTLED, &s->flags); rfcomm_send_fcoff(s, 0); } break; case RFCOMM_FCON: if (cr) { clear_bit(RFCOMM_TX_THROTTLED, &s->flags); rfcomm_send_fcon(s, 0); } break; case RFCOMM_TEST: if (cr) rfcomm_send_test(s, 0, skb->data, skb->len); break; case RFCOMM_NSC: break; default: BT_ERR("Unknown control type 0x%02x", type); rfcomm_send_nsc(s, cr, type); break; } return 0; } static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb) { struct rfcomm_dlc *d; BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf); d = rfcomm_dlc_get(s, dlci); if (!d) { rfcomm_send_dm(s, dlci); goto drop; } if (pf && d->cfc) { u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); d->tx_credits += credits; if (d->tx_credits) clear_bit(RFCOMM_TX_THROTTLED, &d->flags); } if (skb->len && d->state == BT_CONNECTED) { rfcomm_dlc_lock(d); d->rx_credits--; d->data_ready(d, skb); rfcomm_dlc_unlock(d); return 0; } drop: kfree_skb(skb); return 0; } static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) { struct rfcomm_hdr *hdr = (void *) skb->data; u8 type, dlci, fcs; dlci = __get_dlci(hdr->addr); type = __get_type(hdr->ctrl); /* Trim FCS */ skb->len--; skb->tail--; fcs = *(u8 *)skb_tail_pointer(skb); if (__check_fcs(skb->data, type, fcs)) { BT_ERR("bad checksum in packet"); kfree_skb(skb); return -EILSEQ; } if (__test_ea(hdr->len)) skb_pull(skb, 3); else skb_pull(skb, 4); switch (type) { case RFCOMM_SABM: if (__test_pf(hdr->ctrl)) rfcomm_recv_sabm(s, dlci); break; case RFCOMM_DISC: if (__test_pf(hdr->ctrl)) rfcomm_recv_disc(s, dlci); break; case RFCOMM_UA: if (__test_pf(hdr->ctrl)) rfcomm_recv_ua(s, dlci); break; case RFCOMM_DM: rfcomm_recv_dm(s, dlci); break; case RFCOMM_UIH: if (dlci) return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); rfcomm_recv_mcc(s, skb); break; default: BT_ERR("Unknown packet type 0x%02x", type); break; } kfree_skb(skb); return 0; } /* ---- Connection and data processing ---- */ static void rfcomm_process_connect(struct rfcomm_session *s) { struct rfcomm_dlc *d; struct list_head *p, *n; BT_DBG("session %p state %ld", s, s->state); list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); if (d->state == BT_CONFIG) { d->mtu = s->mtu; if (rfcomm_check_security(d)) { rfcomm_send_pn(s, 1, d); } else { set_bit(RFCOMM_AUTH_PENDING, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); } } } } /* Send data queued for the DLC. * Return number of frames left in the queue. */ static int rfcomm_process_tx(struct rfcomm_dlc *d) { struct sk_buff *skb; int err; BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d", d, d->state, d->cfc, d->rx_credits, d->tx_credits); /* Send pending MSC */ if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags)) rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); if (d->cfc) { /* CFC enabled. * Give them some credits */ if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) && d->rx_credits <= (d->cfc >> 2)) { rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits); d->rx_credits = d->cfc; } } else { /* CFC disabled. * Give ourselves some credits */ d->tx_credits = 5; } if (test_bit(RFCOMM_TX_THROTTLED, &d->flags)) return skb_queue_len(&d->tx_queue); while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { err = rfcomm_send_frame(d->session, skb->data, skb->len); if (err < 0) { skb_queue_head(&d->tx_queue, skb); break; } kfree_skb(skb); d->tx_credits--; } if (d->cfc && !d->tx_credits) { /* We're out of TX credits. * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */ set_bit(RFCOMM_TX_THROTTLED, &d->flags); } return skb_queue_len(&d->tx_queue); } static void rfcomm_process_dlcs(struct rfcomm_session *s) { struct rfcomm_dlc *d; struct list_head *p, *n; BT_DBG("session %p state %ld", s, s->state); list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) { __rfcomm_dlc_close(d, ETIMEDOUT); continue; } if (test_bit(RFCOMM_ENC_DROP, &d->flags)) { __rfcomm_dlc_close(d, ECONNREFUSED); continue; } if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) { rfcomm_dlc_clear_timer(d); if (d->out) { rfcomm_send_pn(s, 1, d); rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); } else { if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); rfcomm_dlc_lock(d); d->state = BT_CONNECT2; d->state_change(d, 0); rfcomm_dlc_unlock(d); } else rfcomm_dlc_accept(d); } continue; } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) { rfcomm_dlc_clear_timer(d); if (!d->out) rfcomm_send_dm(s, d->dlci); else d->state = BT_CLOSED; __rfcomm_dlc_close(d, ECONNREFUSED); continue; } if (test_bit(RFCOMM_SEC_PENDING, &d->flags)) continue; if (test_bit(RFCOMM_TX_THROTTLED, &s->flags)) continue; if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) && d->mscex == RFCOMM_MSCEX_OK) rfcomm_process_tx(d); } } static void rfcomm_process_rx(struct rfcomm_session *s) { struct socket *sock = s->sock; struct sock *sk = sock->sk; struct sk_buff *skb; BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->sk_receive_queue)); /* Get data directly from socket receive queue without copying it. */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) rfcomm_recv_frame(s, skb); else kfree_skb(skb); } if (sk->sk_state == BT_CLOSED) { if (!s->initiator) rfcomm_session_put(s); rfcomm_session_close(s, sk->sk_err); } } static void rfcomm_accept_connection(struct rfcomm_session *s) { struct socket *sock = s->sock, *nsock; int err; /* Fast check for a new connection. * Avoids unnesesary socket allocations. */ if (list_empty(&bt_sk(sock->sk)->accept_q)) return; BT_DBG("session %p", s); err = kernel_accept(sock, &nsock, O_NONBLOCK); if (err < 0) return; /* Set our callbacks */ nsock->sk->sk_data_ready = rfcomm_l2data_ready; nsock->sk->sk_state_change = rfcomm_l2state_change; s = rfcomm_session_add(nsock, BT_OPEN); if (s) { rfcomm_session_hold(s); /* We should adjust MTU on incoming sessions. * L2CAP MTU minus UIH header and FCS. */ s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu, l2cap_pi(nsock->sk)->chan->imtu) - 5; rfcomm_schedule(); } else sock_release(nsock); } static void rfcomm_check_connection(struct rfcomm_session *s) { struct sock *sk = s->sock->sk; BT_DBG("%p state %ld", s, s->state); switch (sk->sk_state) { case BT_CONNECTED: s->state = BT_CONNECT; /* We can adjust MTU on outgoing sessions. * L2CAP MTU minus UIH header and FCS. */ s->mtu = min(l2cap_pi(sk)->chan->omtu, l2cap_pi(sk)->chan->imtu) - 5; rfcomm_send_sabm(s, 0); break; case BT_CLOSED: s->state = BT_CLOSED; rfcomm_session_close(s, sk->sk_err); break; } } static void rfcomm_process_sessions(void) { struct list_head *p, *n; rfcomm_lock(); list_for_each_safe(p, n, &session_list) { struct rfcomm_session *s; s = list_entry(p, struct rfcomm_session, list); if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { s->state = BT_DISCONN; rfcomm_send_disc(s, 0); rfcomm_session_put(s); continue; } if (s->state == BT_LISTEN) { rfcomm_accept_connection(s); continue; } rfcomm_session_hold(s); switch (s->state) { case BT_BOUND: rfcomm_check_connection(s); break; default: rfcomm_process_rx(s); break; } rfcomm_process_dlcs(s); rfcomm_session_put(s); } rfcomm_unlock(); } static int rfcomm_add_listener(bdaddr_t *ba) { struct sockaddr_l2 addr; struct socket *sock; struct sock *sk; struct rfcomm_session *s; int err = 0; /* Create socket */ err = rfcomm_l2sock_create(&sock); if (err < 0) { BT_ERR("Create socket failed %d", err); return err; } /* Bind socket */ bacpy(&addr.l2_bdaddr, ba); addr.l2_family = AF_BLUETOOTH; addr.l2_psm = cpu_to_le16(RFCOMM_PSM); addr.l2_cid = 0; err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { BT_ERR("Bind failed %d", err); goto failed; } /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); l2cap_pi(sk)->chan->imtu = l2cap_mtu; release_sock(sk); /* Start listening on the socket */ err = kernel_listen(sock, 10); if (err) { BT_ERR("Listen failed %d", err); goto failed; } /* Add listening session */ s = rfcomm_session_add(sock, BT_LISTEN); if (!s) goto failed; rfcomm_session_hold(s); return 0; failed: sock_release(sock); return err; } static void rfcomm_kill_listener(void) { struct rfcomm_session *s; struct list_head *p, *n; BT_DBG(""); list_for_each_safe(p, n, &session_list) { s = list_entry(p, struct rfcomm_session, list); rfcomm_session_del(s); } } static int rfcomm_run(void *unused) { BT_DBG(""); set_user_nice(current, -10); rfcomm_add_listener(BDADDR_ANY); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) break; /* Process stuff */ rfcomm_process_sessions(); schedule(); } __set_current_state(TASK_RUNNING); rfcomm_kill_listener(); return 0; } static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) { struct rfcomm_session *s; struct rfcomm_dlc *d; struct list_head *p, *n; BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt); s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); if (!s) return; rfcomm_session_hold(s); list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { rfcomm_dlc_clear_timer(d); if (status || encrypt == 0x00) { set_bit(RFCOMM_ENC_DROP, &d->flags); continue; } } if (d->state == BT_CONNECTED && !status && encrypt == 0x00) { if (d->sec_level == BT_SECURITY_MEDIUM) { set_bit(RFCOMM_SEC_PENDING, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); continue; } else if (d->sec_level == BT_SECURITY_HIGH) { set_bit(RFCOMM_ENC_DROP, &d->flags); continue; } } if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) continue; if (!status && hci_conn_check_secure(conn, d->sec_level)) set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); else set_bit(RFCOMM_AUTH_REJECT, &d->flags); } rfcomm_session_put(s); rfcomm_schedule(); } static struct hci_cb rfcomm_cb = { .name = "RFCOMM", .security_cfm = rfcomm_security_cfm }; static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) { struct rfcomm_session *s; rfcomm_lock(); list_for_each_entry(s, &session_list, list) { struct rfcomm_dlc *d; list_for_each_entry(d, &s->dlcs, list) { struct sock *sk = s->sock->sk; seq_printf(f, "%s %s %ld %d %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits); } } rfcomm_unlock(); return 0; } static int rfcomm_dlc_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, rfcomm_dlc_debugfs_show, inode->i_private); } static const struct file_operations rfcomm_dlc_debugfs_fops = { .open = rfcomm_dlc_debugfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *rfcomm_dlc_debugfs; /* ---- Initialization ---- */ static int __init rfcomm_init(void) { int err; hci_register_cb(&rfcomm_cb); rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); if (IS_ERR(rfcomm_thread)) { err = PTR_ERR(rfcomm_thread); goto unregister; } if (bt_debugfs) { rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444, bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops); if (!rfcomm_dlc_debugfs) BT_ERR("Failed to create RFCOMM debug file"); } err = rfcomm_init_ttys(); if (err < 0) goto stop; err = rfcomm_init_sockets(); if (err < 0) goto cleanup; BT_INFO("RFCOMM ver %s", VERSION); return 0; cleanup: rfcomm_cleanup_ttys(); stop: kthread_stop(rfcomm_thread); unregister: hci_unregister_cb(&rfcomm_cb); return err; } static void __exit rfcomm_exit(void) { debugfs_remove(rfcomm_dlc_debugfs); hci_unregister_cb(&rfcomm_cb); kthread_stop(rfcomm_thread); rfcomm_cleanup_ttys(); rfcomm_cleanup_sockets(); } module_init(rfcomm_init); module_exit(rfcomm_exit); module_param(disable_cfc, bool, 0644); MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control"); module_param(channel_mtu, int, 0644); MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel"); module_param(l2cap_mtu, uint, 0644); MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); module_param(l2cap_ertm, bool, 0644); MODULE_PARM_DESC(l2cap_ertm, "Use L2CAP ERTM mode for connection"); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("bt-proto-3"); compat-drivers-2012-09-18/net/bluetooth/rfcomm/Kconfig0000644000175000017500000000073212026211315021743 0ustar mcgrofmcgrofconfig BT_RFCOMM tristate "RFCOMM protocol support" depends on BT help RFCOMM provides connection oriented stream transport. RFCOMM support is required for Dialup Networking, OBEX and other Bluetooth applications. Say Y here to compile RFCOMM support into the kernel or say M to compile it as module (rfcomm). config BT_RFCOMM_TTY bool "RFCOMM TTY support" depends on BT_RFCOMM help This option enables TTY emulation support for RFCOMM channels. compat-drivers-2012-09-18/net/bluetooth/Kconfig0000644000175000017500000000317312026211315020462 0ustar mcgrofmcgrof# # Bluetooth subsystem configuration # menuconfig BT tristate "Bluetooth subsystem support" depends on NET && !S390 depends on RFKILL || !RFKILL select CRC16 select CRYPTO select CRYPTO_BLKCIPHER select CRYPTO_AES select CRYPTO_ECB help Bluetooth is low-cost, low-power, short-range wireless technology. It was designed as a replacement for cables and other short-range technologies like IrDA. Bluetooth operates in personal area range that typically extends up to 10 meters. More information about Bluetooth can be found at . Linux Bluetooth subsystem consist of several layers: Bluetooth Core HCI device and connection manager, scheduler SCO audio links L2CAP (Logical Link Control and Adaptation Protocol) SMP (Security Manager Protocol) on LE (Low Energy) links HCI Device drivers (Interface to the hardware) RFCOMM Module (RFCOMM Protocol) BNEP Module (Bluetooth Network Encapsulation Protocol) CMTP Module (CAPI Message Transport Protocol) HIDP Module (Human Interface Device Protocol) Say Y here to compile Bluetooth support into the kernel or say M to compile it as module (bluetooth). To use Linux Bluetooth subsystem, you will need several user-space utilities like hciconfig and bluetoothd. These utilities and updates to Bluetooth kernel modules are provided in the BlueZ packages. For more information, see . source "net/bluetooth/rfcomm/Kconfig" source "net/bluetooth/bnep/Kconfig" source "net/bluetooth/cmtp/Kconfig" source "net/bluetooth/hidp/Kconfig" source "drivers/bluetooth/Kconfig" compat-drivers-2012-09-18/net/bluetooth/a2mp.c0000644000175000017500000002760512026211315020170 0ustar mcgrofmcgrof/* Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved. Copyright (c) 2011,2012 Intel Corp. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 and only version 2 as published by the Free Software Foundation. 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 #include #include #include /* A2MP build & send command helper functions */ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) { struct a2mp_cmd *cmd; int plen; plen = sizeof(*cmd) + len; cmd = kzalloc(plen, GFP_KERNEL); if (!cmd) return NULL; cmd->code = code; cmd->ident = ident; cmd->len = cpu_to_le16(len); memcpy(cmd->data, data, len); return cmd; } static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) { struct l2cap_chan *chan = mgr->a2mp_chan; struct a2mp_cmd *cmd; u16 total_len = len + sizeof(*cmd); struct kvec iv; struct msghdr msg; cmd = __a2mp_build(code, ident, len, data); if (!cmd) return; iv.iov_base = cmd; iv.iov_len = total_len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = (struct iovec *) &iv; msg.msg_iovlen = 1; l2cap_chan_send(chan, &msg, total_len, 0); kfree(cmd); } static inline void __a2mp_cl_bredr(struct a2mp_cl *cl) { cl->id = 0; cl->type = 0; cl->status = 1; } /* hci_dev_list shall be locked */ static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl) { int i = 0; struct hci_dev *hdev; __a2mp_cl_bredr(cl); list_for_each_entry(hdev, &hci_dev_list, list) { /* Iterate through AMP controllers */ if (hdev->id == HCI_BREDR_ID) continue; /* Starting from second entry */ if (++i >= num_ctrl) return; cl[i].id = hdev->id; cl[i].type = hdev->amp_type; cl[i].status = hdev->amp_status; } } /* Processing A2MP messages */ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_cmd_rej *rej = (void *) skb->data; if (le16_to_cpu(hdr->len) < sizeof(*rej)) return -EINVAL; BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason)); skb_pull(skb, sizeof(*rej)); return 0; } static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_discov_req *req = (void *) skb->data; u16 len = le16_to_cpu(hdr->len); struct a2mp_discov_rsp *rsp; u16 ext_feat; u8 num_ctrl; if (len < sizeof(*req)) return -EINVAL; skb_pull(skb, sizeof(*req)); ext_feat = le16_to_cpu(req->ext_feat); BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->mtu), ext_feat); /* check that packet is not broken for now */ while (ext_feat & A2MP_FEAT_EXT) { if (len < sizeof(ext_feat)) return -EINVAL; ext_feat = get_unaligned_le16(skb->data); BT_DBG("efm 0x%4.4x", ext_feat); len -= sizeof(ext_feat); skb_pull(skb, sizeof(ext_feat)); } read_lock(&hci_dev_list_lock); num_ctrl = __hci_num_ctrl(); len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp); rsp = kmalloc(len, GFP_ATOMIC); if (!rsp) { read_unlock(&hci_dev_list_lock); return -ENOMEM; } rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); rsp->ext_feat = 0; __a2mp_add_cl(mgr, rsp->cl, num_ctrl); read_unlock(&hci_dev_list_lock); a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp); kfree(rsp); return 0; } static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_cl *cl = (void *) skb->data; while (skb->len >= sizeof(*cl)) { BT_DBG("Controller id %d type %d status %d", cl->id, cl->type, cl->status); cl = (struct a2mp_cl *) skb_pull(skb, sizeof(*cl)); } /* TODO send A2MP_CHANGE_RSP */ return 0; } static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_info_req *req = (void *) skb->data; struct a2mp_info_rsp rsp; struct hci_dev *hdev; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; BT_DBG("id %d", req->id); rsp.id = req->id; rsp.status = A2MP_STATUS_INVALID_CTRL_ID; hdev = hci_dev_get(req->id); if (hdev && hdev->amp_type != HCI_BREDR) { rsp.status = 0; rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); } if (hdev) hci_dev_put(hdev); a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp); skb_pull(skb, sizeof(*req)); return 0; } static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_amp_assoc_req *req = (void *) skb->data; struct hci_dev *hdev; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; BT_DBG("id %d", req->id); hdev = hci_dev_get(req->id); if (!hdev || hdev->amp_type == HCI_BREDR) { struct a2mp_amp_assoc_rsp rsp; rsp.id = req->id; rsp.status = A2MP_STATUS_INVALID_CTRL_ID; a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp), &rsp); goto clean; } /* Placeholder for HCI Read AMP Assoc */ clean: if (hdev) hci_dev_put(hdev); skb_pull(skb, sizeof(*req)); return 0; } static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_physlink_req *req = (void *) skb->data; struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id); rsp.local_id = req->remote_id; rsp.remote_id = req->local_id; hdev = hci_dev_get(req->remote_id); if (!hdev || hdev->amp_type != HCI_AMP) { rsp.status = A2MP_STATUS_INVALID_CTRL_ID; goto send_rsp; } /* TODO process physlink create */ rsp.status = A2MP_STATUS_SUCCESS; send_rsp: if (hdev) hci_dev_put(hdev); a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp); skb_pull(skb, le16_to_cpu(hdr->len)); return 0; } static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_physlink_req *req = (void *) skb->data; struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id); rsp.local_id = req->remote_id; rsp.remote_id = req->local_id; rsp.status = A2MP_STATUS_SUCCESS; hdev = hci_dev_get(req->local_id); if (!hdev) { rsp.status = A2MP_STATUS_INVALID_CTRL_ID; goto send_rsp; } /* TODO Disconnect Phys Link here */ hci_dev_put(hdev); send_rsp: a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp); skb_pull(skb, sizeof(*req)); return 0; } static inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { BT_DBG("ident %d code 0x%2.2x", hdr->ident, hdr->code); skb_pull(skb, le16_to_cpu(hdr->len)); return 0; } /* Handle A2MP signalling */ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { struct a2mp_cmd *hdr; struct amp_mgr *mgr = chan->data; int err = 0; amp_mgr_get(mgr); while (skb->len >= sizeof(*hdr)) { u16 len; hdr = (void *) skb->data; len = le16_to_cpu(hdr->len); BT_DBG("code 0x%2.2x id %d len %u", hdr->code, hdr->ident, len); skb_pull(skb, sizeof(*hdr)); if (len > skb->len || !hdr->ident) { err = -EINVAL; break; } mgr->ident = hdr->ident; switch (hdr->code) { case A2MP_COMMAND_REJ: a2mp_command_rej(mgr, skb, hdr); break; case A2MP_DISCOVER_REQ: err = a2mp_discover_req(mgr, skb, hdr); break; case A2MP_CHANGE_NOTIFY: err = a2mp_change_notify(mgr, skb, hdr); break; case A2MP_GETINFO_REQ: err = a2mp_getinfo_req(mgr, skb, hdr); break; case A2MP_GETAMPASSOC_REQ: err = a2mp_getampassoc_req(mgr, skb, hdr); break; case A2MP_CREATEPHYSLINK_REQ: err = a2mp_createphyslink_req(mgr, skb, hdr); break; case A2MP_DISCONNPHYSLINK_REQ: err = a2mp_discphyslink_req(mgr, skb, hdr); break; case A2MP_CHANGE_RSP: case A2MP_DISCOVER_RSP: case A2MP_GETINFO_RSP: case A2MP_GETAMPASSOC_RSP: case A2MP_CREATEPHYSLINK_RSP: case A2MP_DISCONNPHYSLINK_RSP: err = a2mp_cmd_rsp(mgr, skb, hdr); break; default: BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code); err = -EINVAL; break; } } if (err) { struct a2mp_cmd_rej rej; rej.reason = __constant_cpu_to_le16(0); hdr = (void *) skb->data; BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err); a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej), &rej); } /* Always free skb and return success error code to prevent from sending L2CAP Disconnect over A2MP channel */ kfree_skb(skb); amp_mgr_put(mgr); return 0; } static void a2mp_chan_close_cb(struct l2cap_chan *chan) { l2cap_chan_put(chan); } static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state) { struct amp_mgr *mgr = chan->data; if (!mgr) return; BT_DBG("chan %p state %s", chan, state_to_string(state)); chan->state = state; switch (state) { case BT_CLOSED: if (mgr) amp_mgr_put(mgr); break; } } static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan, unsigned long len, int nb) { return bt_skb_alloc(len, GFP_KERNEL); } static struct l2cap_ops a2mp_chan_ops = { .name = "L2CAP A2MP channel", .recv = a2mp_chan_recv_cb, .close = a2mp_chan_close_cb, .state_change = a2mp_chan_state_change_cb, .alloc_skb = a2mp_chan_alloc_skb_cb, /* Not implemented for A2MP */ .new_connection = l2cap_chan_no_new_connection, .teardown = l2cap_chan_no_teardown, .ready = l2cap_chan_no_ready, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) { struct l2cap_chan *chan; int err; chan = l2cap_chan_create(); if (!chan) return NULL; BT_DBG("chan %p", chan); chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP; chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; chan->ops = &a2mp_chan_ops; l2cap_chan_set_defaults(chan); chan->remote_max_tx = chan->max_tx; chan->remote_tx_win = chan->tx_win; chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; skb_queue_head_init(&chan->tx_q); chan->mode = L2CAP_MODE_ERTM; err = l2cap_ertm_init(chan); if (err < 0) { l2cap_chan_del(chan, 0); return NULL; } chan->conf_state = 0; l2cap_chan_add(conn, chan); chan->remote_mps = chan->omtu; chan->mps = chan->omtu; chan->state = BT_CONNECTED; return chan; } /* AMP Manager functions */ void amp_mgr_get(struct amp_mgr *mgr) { BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount)); kref_get(&mgr->kref); } static void amp_mgr_destroy(struct kref *kref) { struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref); BT_DBG("mgr %p", mgr); kfree(mgr); } int amp_mgr_put(struct amp_mgr *mgr) { BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount)); return kref_put(&mgr->kref, &_mgr_destroy); } static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) { struct amp_mgr *mgr; struct l2cap_chan *chan; mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); if (!mgr) return NULL; BT_DBG("conn %p mgr %p", conn, mgr); mgr->l2cap_conn = conn; chan = a2mp_chan_open(conn); if (!chan) { kfree(mgr); return NULL; } mgr->a2mp_chan = chan; chan->data = mgr; conn->hcon->amp_mgr = mgr; kref_init(&mgr->kref); return mgr; } struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb) { struct amp_mgr *mgr; mgr = amp_mgr_create(conn); if (!mgr) { BT_ERR("Could not create AMP manager"); return NULL; } BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan); return mgr->a2mp_chan; } compat-drivers-2012-09-18/enable-older-kernels/0000755000175000017500000000000012026176477020375 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/enable-older-kernels/enable-2.6.21.patch0000644000175000017500000000222112026176477023365 0ustar mcgrofmcgrof--- a/config.mk +++ b/config.mk @@ -33,9 +33,9 @@ endif COMPAT_VERSIONS := $(shell I=$(COMPAT_LATEST_VERSION); while [ "$$I" -gt $(KERNEL_SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done) $(foreach ver,$(COMPAT_VERSIONS),$(eval CONFIG_COMPAT_KERNEL_3_$(ver)=y)) -ifdef CONFIG_COMPAT_KERNEL_2_6_24 -$(error "ERROR: compat-drivers by default supports kernels >= 2.6.24, try enabling only one driver though") -endif #CONFIG_COMPAT_KERNEL_2_6_24 +ifdef CONFIG_COMPAT_KERNEL_2_6_21 +$(error "ERROR: compat-drivers by default supports kernels >= 2.6.21, try enabling only one driver though") +endif #CONFIG_COMPAT_KERNEL_2_6_21 ifeq ($(CONFIG_CFG80211),y) $(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.") --- a/scripts/gen-compat-autoconf.sh +++ b/scripts/gen-compat-autoconf.sh @@ -11,7 +11,7 @@ # This indicates which is the oldest kernel we support # Update this if you are adding support for older kernels. -OLDEST_KERNEL_SUPPORTED="2.6.24" +OLDEST_KERNEL_SUPPORTED="2.6.21" COMPAT_RELEASE=".compat_version" KERNEL_RELEASE=".compat_base_tree_version" MULT_DEP_FILE=".compat_pivot_dep" compat-drivers-2012-09-18/enable-older-kernels/enable-2.6.23.patch0000644000175000017500000000222112026176477023367 0ustar mcgrofmcgrof--- a/config.mk +++ b/config.mk @@ -33,9 +33,9 @@ endif COMPAT_VERSIONS := $(shell I=$(COMPAT_LATEST_VERSION); while [ "$$I" -gt $(KERNEL_SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done) $(foreach ver,$(COMPAT_VERSIONS),$(eval CONFIG_COMPAT_KERNEL_3_$(ver)=y)) -ifdef CONFIG_COMPAT_KERNEL_2_6_24 -$(error "ERROR: compat-drivers by default supports kernels >= 2.6.24, try enabling only one driver though") -endif #CONFIG_COMPAT_KERNEL_2_6_24 +ifdef CONFIG_COMPAT_KERNEL_2_6_23 +$(error "ERROR: compat-drivers by default supports kernels >= 2.6.23, try enabling only one driver though") +endif #CONFIG_COMPAT_KERNEL_2_6_23 ifeq ($(CONFIG_CFG80211),y) $(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.") --- a/scripts/gen-compat-autoconf.sh +++ b/scripts/gen-compat-autoconf.sh @@ -11,7 +11,7 @@ # This indicates which is the oldest kernel we support # Update this if you are adding support for older kernels. -OLDEST_KERNEL_SUPPORTED="2.6.24" +OLDEST_KERNEL_SUPPORTED="2.6.23" COMPAT_RELEASE=".compat_version" KERNEL_RELEASE=".compat_base_tree_version" MULT_DEP_FILE=".compat_pivot_dep" compat-drivers-2012-09-18/enable-older-kernels/README0000644000175000017500000000134512026176477021260 0ustar mcgrofmcgrofcompat-drivers as a whole aims to always be compiled and used with the oldest stable kernel supported by kernel.org. This will be the oldest stable kernel on the 2.6. series listed on the kernel.org front page. Sometimes we'll go even beyond that. Backporting compat-drivers involves backporting some subsystems but at times we may want to support compiling only certain drivers on older kernels since its easier to backport some subsystems. Such is the case with PCI drivers. This directly exists to allow developers enable compilation of compat-drivers on older drivers using ./scripts/driver-select Upon selection of a driver a patch from this directly will be applied to allow further compilation of one driver onto even older kernels. compat-drivers-2012-09-18/enable-older-kernels/enable-2.6.22.patch0000644000175000017500000000222112026176477023366 0ustar mcgrofmcgrof--- a/config.mk +++ b/config.mk @@ -33,9 +33,9 @@ endif COMPAT_VERSIONS := $(shell I=$(COMPAT_LATEST_VERSION); while [ "$$I" -gt $(KERNEL_SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done) $(foreach ver,$(COMPAT_VERSIONS),$(eval CONFIG_COMPAT_KERNEL_3_$(ver)=y)) -ifdef CONFIG_COMPAT_KERNEL_2_6_24 -$(error "ERROR: compat-drivers by default supports kernels >= 2.6.24, try enabling only one driver though") -endif #CONFIG_COMPAT_KERNEL_2_6_24 +ifdef CONFIG_COMPAT_KERNEL_2_6_22 +$(error "ERROR: compat-drivers by default supports kernels >= 2.6.22, try enabling only one driver though") +endif #CONFIG_COMPAT_KERNEL_2_6_22 ifeq ($(CONFIG_CFG80211),y) $(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.") --- a/scripts/gen-compat-autoconf.sh +++ b/scripts/gen-compat-autoconf.sh @@ -11,7 +11,7 @@ # This indicates which is the oldest kernel we support # Update this if you are adding support for older kernels. -OLDEST_KERNEL_SUPPORTED="2.6.24" +OLDEST_KERNEL_SUPPORTED="2.6.22" COMPAT_RELEASE=".compat_version" KERNEL_RELEASE=".compat_base_tree_version" MULT_DEP_FILE=".compat_pivot_dep" compat-drivers-2012-09-18/MAINTAINERS0000644000175000017500000065053412026211315016072 0ustar mcgrofmcgrof List of maintainers and how to submit kernel changes Please try to follow the guidelines below. This will make things easier on the maintainers. Not all of these guidelines matter for every trivial patch so apply some common sense. 1. Always _test_ your changes, however small, on at least 4 or 5 people, preferably many more. 2. Try to release a few ALPHA test versions to the net. Announce them onto the kernel channel and await results. This is especially important for device drivers, because often that's the only way you will find things like the fact version 3 firmware needs a magic fix you didn't know about, or some clown changed the chips on a board and not its name. (Don't laugh! Look at the SMC etherpower for that.) 3. Make sure your changes compile correctly in multiple configurations. In particular check that changes work both as a module and built into the kernel. 4. When you are happy with a change make it generally available for testing and await feedback. 5. Make a patch available to the relevant maintainer in the list. Use 'diff -u' to make the patch easy to merge. Be prepared to get your changes sent back with seemingly silly requests about formatting and variable names. These aren't as silly as they seem. One job the maintainers (and especially Linus) do is to keep things looking the same. Sometimes this means that the clever hack in your driver to get around a problem actually needs to become a generalized kernel feature ready for next time. PLEASE check your patch with the automated style checker (scripts/checkpatch.pl) to catch trival style violations. See Documentation/CodingStyle for guidance here. PLEASE CC: the maintainers and mailing lists that are generated by scripts/get_maintainer.pl. The results returned by the script will be best if you have git installed and are making your changes in a branch derived from Linus' latest git tree. See Documentation/SubmittingPatches for details. PLEASE try to include any credit lines you want added with the patch. It avoids people being missed off by mistake and makes it easier to know who wants adding and who doesn't. PLEASE document known bugs. If it doesn't work for everything or does something very odd once a month document it. PLEASE remember that submissions must be made under the terms of the OSDL certificate of contribution and should include a Signed-off-by: line. The current version of this "Developer's Certificate of Origin" (DCO) is listed in the file Documentation/SubmittingPatches. 6. Make sure you have the right to send any changes you make. If you do changes at work you may find your employer owns the patch not you. 7. When sending security related changes or reports to a maintainer please Cc: security@kernel.org, especially if the maintainer does not respond. 8. Happy hacking. Descriptions of section entries: P: Person (obsolete) M: Mail patches to: FullName L: Mailing list that is relevant to this area W: Web-page with status/info Q: Patchwork web based patch tracking system site T: SCM tree type and location. Type is one of: git, hg, quilt, stgit, topgit. S: Status, one of the following: Supported: Someone is actually paid to look after this. Maintained: Someone actually looks after it. Odd Fixes: It has a maintainer but they don't have time to do much other than throw the odd patch in. See below.. Orphan: No current maintainer [but maybe you could take the role as you write your new code]. Obsolete: Old code. Something tagged obsolete generally means it has been replaced by a better system and you should be using that. F: Files and directories with wildcard patterns. A trailing slash includes all files and subdirectory files. F: drivers/net/ all files in and below drivers/net F: drivers/net/* all files in drivers/net, but not below F: */net/* all files in "any top level directory"/net One pattern per line. Multiple F: lines acceptable. X: Files and directories that are NOT maintained, same rules as F: Files exclusions are tested before file matches. Can be useful for excluding a specific subdirectory, for instance: F: net/ X: net/ipv6/ matches all files in and below net excluding net/ipv6/ K: Keyword perl extended regex pattern to match content in a patch or file. For instance: K: of_get_profile matches patches or files that contain "of_get_profile" K: \b(printk|pr_(info|err))\b matches patches or files that contain one or more of the words printk, pr_info or pr_err One regex pattern per line. Multiple K: lines acceptable. Note: For the hard of thinking, this list is meant to remain in alphabetical order. If you could add yourselves to it in alphabetical order that would be so much easier [Ed] Maintainers List (try to look for most precise areas first) ----------------------------------- 3C505 NETWORK DRIVER M: Philip Blundell L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/i825xx/3c505* 3C59X NETWORK DRIVER M: Steffen Klassert L: netdev@vger.kernel.org S: Maintained F: Documentation/networking/vortex.txt F: drivers/net/ethernet/3com/3c59x.c 3CR990 NETWORK DRIVER M: David Dillow L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/3com/typhoon* 3WARE SAS/SATA-RAID SCSI DRIVERS (3W-XXXX, 3W-9XXX, 3W-SAS) M: Adam Radford L: linux-scsi@vger.kernel.org W: http://www.lsi.com S: Supported F: drivers/scsi/3w-* 53C700 AND 53C700-66 SCSI DRIVER M: "James E.J. Bottomley" L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/53c700* 6PACK NETWORK DRIVER FOR AX.25 M: Andreas Koensgen L: linux-hams@vger.kernel.org S: Maintained F: drivers/net/hamradio/6pack.c 8169 10/100/1000 GIGABIT ETHERNET DRIVER M: Realtek linux nic maintainers M: Francois Romieu L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/realtek/r8169.c 8250/16?50 (AND CLONE UARTS) SERIAL DRIVER M: Greg Kroah-Hartman L: linux-serial@vger.kernel.org W: http://serial.sourceforge.net S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git F: drivers/tty/serial/8250* F: include/linux/serial_8250.h 8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] L: netdev@vger.kernel.org S: Orphan / Obsolete F: drivers/net/ethernet/8390/ 9P FILE SYSTEM M: Eric Van Hensbergen M: Ron Minnich M: Latchesar Ionkov L: v9fs-developer@lists.sourceforge.net W: http://swik.net/v9fs Q: http://patchwork.kernel.org/project/v9fs-devel/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git S: Maintained F: Documentation/filesystems/9p.txt F: fs/9p/ AACRAID SCSI RAID DRIVER M: Adaptec OEM Raid Solutions L: linux-scsi@vger.kernel.org W: http://www.adaptec.com/ S: Supported F: Documentation/scsi/aacraid.txt F: drivers/scsi/aacraid/ ABIT UGURU 1,2 HARDWARE MONITOR DRIVER M: Hans de Goede L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/abituguru.c ABIT UGURU 3 HARDWARE MONITOR DRIVER M: Alistair John Strachan L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/abituguru3.c ACENIC DRIVER M: Jes Sorensen L: linux-acenic@sunsite.dk S: Maintained F: drivers/net/ethernet/alteon/acenic* ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER M: Peter Feuerer L: platform-driver-x86@vger.kernel.org W: http://piie.net/?section=acerhdf S: Maintained F: drivers/platform/x86/acerhdf.c ACER WMI LAPTOP EXTRAS M: Joey Lee L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/acer-wmi.c ACPI M: Len Brown L: linux-acpi@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ Q: http://patchwork.kernel.org/project/linux-acpi/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux S: Supported F: drivers/acpi/ F: drivers/pnp/pnpacpi/ F: include/linux/acpi.h F: include/acpi/ ACPI FAN DRIVER M: Zhang Rui L: linux-acpi@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ S: Supported F: drivers/acpi/fan.c ACPI THERMAL DRIVER M: Zhang Rui L: linux-acpi@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ S: Supported F: drivers/acpi/*thermal* ACPI VIDEO DRIVER M: Zhang Rui L: linux-acpi@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ S: Supported F: drivers/acpi/video.c ACPI WMI DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan F: drivers/platform/x86/wmi.c AD1889 ALSA SOUND DRIVER M: Thibaut Varene W: http://wiki.parisc-linux.org/AD1889 L: linux-parisc@vger.kernel.org S: Maintained F: sound/pci/ad1889.* AD525X ANALOG DEVICES DIGITAL POTENTIOMETERS DRIVER M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/AD5254 S: Supported F: drivers/misc/ad525x_dpot.c AD5398 CURRENT REGULATOR DRIVER (AD5398/AD5821) M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/AD5398 S: Supported F: drivers/regulator/ad5398.c AD714X CAPACITANCE TOUCH SENSOR DRIVER (AD7142/3/7/8/7A) M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/AD7142 S: Supported F: drivers/input/misc/ad714x.c AD7877 TOUCHSCREEN DRIVER M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/AD7877 S: Supported F: drivers/input/touchscreen/ad7877.c AD7879 TOUCHSCREEN DRIVER (AD7879/AD7889) M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/AD7879 S: Supported F: drivers/input/touchscreen/ad7879.c ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR) M: Jiri Kosina S: Maintained ADM1025 HARDWARE MONITOR DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/adm1025 F: drivers/hwmon/adm1025.c ADM1029 HARDWARE MONITOR DRIVER M: Corentin Labbe L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/adm1029.c ADM8211 WIRELESS DRIVER L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ S: Orphan F: drivers/net/wireless/adm8211.* ADP5520 BACKLIGHT DRIVER WITH IO EXPANDER (ADP5520/ADP5501) M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/ADP5520 S: Supported F: drivers/mfd/adp5520.c F: drivers/video/backlight/adp5520_bl.c F: drivers/leds/leds-adp5520.c F: drivers/gpio/gpio-adp5520.c F: drivers/input/keyboard/adp5520-keys.c ADP5588 QWERTY KEYPAD AND IO EXPANDER DRIVER (ADP5588/ADP5587) M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/ADP5588 S: Supported F: drivers/input/keyboard/adp5588-keys.c F: drivers/gpio/gpio-adp5588.c ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863) M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/ADP8860 S: Supported F: drivers/video/backlight/adp8860_bl.c ADS1015 HARDWARE MONITOR DRIVER M: Dirk Eibach L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/ads1015 F: drivers/hwmon/ads1015.c F: include/linux/i2c/ads1015.h ADT746X FAN DRIVER M: Colin Leroy S: Maintained F: drivers/macintosh/therm_adt746x.c ADT7475 HARDWARE MONITOR DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/adt7475 F: drivers/hwmon/adt7475.c ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346) M: Michael Hennerich L: device-drivers-devel@blackfin.uclinux.org W: http://wiki.analog.com/ADXL345 S: Supported F: drivers/input/misc/adxl34x.c ADVANSYS SCSI DRIVER M: Matthew Wilcox L: linux-scsi@vger.kernel.org S: Maintained F: Documentation/scsi/advansys.txt F: drivers/scsi/advansys.c AEDSP16 DRIVER M: Riccardo Facchetti S: Maintained F: sound/oss/aedsp16.c AFFS FILE SYSTEM L: linux-fsdevel@vger.kernel.org S: Orphan F: Documentation/filesystems/affs.txt F: fs/affs/ AFS FILESYSTEM & AF_RXRPC SOCKET DOMAIN M: David Howells L: linux-afs@lists.infradead.org S: Supported F: fs/afs/ F: include/net/af_rxrpc.h F: net/rxrpc/af_rxrpc.c AGPGART DRIVER M: David Airlie T: git git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6.git S: Maintained F: drivers/char/agp/ F: include/linux/agp* AHA152X SCSI DRIVER M: "Juergen E. Fischer" L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/aha152x* F: drivers/scsi/pcmcia/aha152x* AIC7XXX / AIC79XX SCSI DRIVER M: Hannes Reinecke L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/aic7xxx/ F: drivers/scsi/aic7xxx_old/ AIO M: Benjamin LaHaise L: linux-aio@kvack.org S: Supported F: fs/aio.c F: include/linux/*aio*.h ALCATEL SPEEDTOUCH USB DRIVER M: Duncan Sands L: linux-usb@vger.kernel.org W: http://www.linux-usb.org/SpeedTouch/ S: Maintained F: drivers/usb/atm/speedtch.c F: drivers/usb/atm/usbatm.c ALCHEMY AU1XX0 MMC DRIVER M: Manuel Lauss S: Maintained F: drivers/mmc/host/au1xmmc.c ALI1563 I2C DRIVER M: Rudolf Marek L: linux-i2c@vger.kernel.org S: Maintained F: Documentation/i2c/busses/i2c-ali1563 F: drivers/i2c/busses/i2c-ali1563.c ALPHA PORT M: Richard Henderson M: Ivan Kokshaysky M: Matt Turner S: Odd Fixes L: linux-alpha@vger.kernel.org F: arch/alpha/ ALTERA UART/JTAG UART SERIAL DRIVERS M: Tobias Klauser L: linux-serial@vger.kernel.org L: nios2-dev@sopc.et.ntust.edu.tw (moderated for non-subscribers) S: Maintained F: drivers/tty/serial/altera_uart.c F: drivers/tty/serial/altera_jtaguart.c F: include/linux/altera_uart.h F: include/linux/altera_jtaguart.h AMD FAM15H PROCESSOR POWER MONITORING DRIVER M: Andreas Herrmann L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/fam15h_power F: drivers/hwmon/fam15h_power.c AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER M: Thomas Dahlmann L: linux-geode@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/usb/gadget/amd5536udc.* AMD GEODE PROCESSOR/CHIPSET SUPPORT P: Andres Salomon L: linux-geode@lists.infradead.org (moderated for non-subscribers) W: http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html S: Supported F: drivers/char/hw_random/geode-rng.c F: drivers/crypto/geode* F: drivers/video/geode/ F: arch/x86/include/asm/geode.h AMD IOMMU (AMD-VI) M: Joerg Roedel L: iommu@lists.linux-foundation.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git S: Supported F: drivers/iommu/amd_iommu*.[ch] F: include/linux/amd-iommu.h AMD MICROCODE UPDATE SUPPORT M: Andreas Herrmann L: amd64-microcode@amd64.org S: Supported F: arch/x86/kernel/microcode_amd.c AMS (Apple Motion Sensor) DRIVER M: Michael Hanselmann S: Supported F: drivers/macintosh/ams/ AMSO1100 RNIC DRIVER M: Tom Tucker M: Steve Wise L: linux-rdma@vger.kernel.org S: Maintained F: drivers/infiniband/hw/amso1100/ ANALOG DEVICES INC ASOC CODEC DRIVERS M: Lars-Peter Clausen L: device-drivers-devel@blackfin.uclinux.org L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://wiki.analog.com/ S: Supported F: sound/soc/codecs/adau* F: sound/soc/codecs/adav* F: sound/soc/codecs/ad1* F: sound/soc/codecs/ssm* F: sound/soc/codecs/sigmadsp.* ANALOG DEVICES INC ASOC DRIVERS L: uclinux-dist-devel@blackfin.uclinux.org L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://blackfin.uclinux.org/ S: Supported F: sound/soc/blackfin/* AOA (Apple Onboard Audio) ALSA DRIVER M: Johannes Berg L: linuxppc-dev@lists.ozlabs.org L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/aoa/ APM DRIVER M: Jiri Kosina S: Odd fixes F: arch/x86/kernel/apm_32.c F: include/linux/apm_bios.h F: drivers/char/apm-emulation.c APPLE BCM5974 MULTITOUCH DRIVER M: Henrik Rydberg L: linux-input@vger.kernel.org S: Maintained F: drivers/input/mouse/bcm5974.c APPLE SMC DRIVER M: Henrik Rydberg L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/applesmc.c APPLETALK NETWORK LAYER M: Arnaldo Carvalho de Melo S: Maintained F: drivers/net/appletalk/ F: net/appletalk/ ARASAN COMPACT FLASH PATA CONTROLLER M: Viresh Kumar L: linux-ide@vger.kernel.org S: Maintained F: include/linux/pata_arasan_cf_data.h F: drivers/ata/pata_arasan_cf.c ARC FRAMEBUFFER DRIVER M: Jaya Kumar S: Maintained F: drivers/video/arcfb.c F: drivers/video/fb_defio.c ARM MFM AND FLOPPY DRIVERS M: Ian Molton S: Maintained F: arch/arm/lib/floppydma.S F: arch/arm/include/asm/floppy.h ARM PMU PROFILING AND DEBUGGING M: Will Deacon S: Maintained F: arch/arm/kernel/perf_event* F: arch/arm/oprofile/common.c F: arch/arm/include/asm/pmu.h F: arch/arm/kernel/hw_breakpoint.c F: arch/arm/include/asm/hw_breakpoint.h ARM PORT M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.arm.linux.org.uk/ S: Maintained F: arch/arm/ ARM PRIMECELL AACI PL041 DRIVER M: Russell King S: Maintained F: sound/arm/aaci.* ARM PRIMECELL CLCD PL110 DRIVER M: Russell King S: Maintained F: drivers/video/amba-clcd.* ARM PRIMECELL KMI PL050 DRIVER M: Russell King S: Maintained F: drivers/input/serio/ambakmi.* F: include/linux/amba/kmi.h ARM PRIMECELL MMCI PL180/1 DRIVER S: Orphan F: drivers/mmc/host/mmci.* ARM PRIMECELL BUS SUPPORT M: Russell King S: Maintained F: drivers/amba/ F: include/linux/amba/bus.h ARM/ADS SPHERE MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/AFEB9260 MACHINE SUPPORT M: Sergey Lapin L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/AJECO 1ARM MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/ATMEL AT91RM9200 AND AT91SAM ARM ARCHITECTURES M: Andrew Victor M: Nicolas Ferre M: Jean-Christophe Plagniol-Villard L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://maxim.org.za/at91_26.html W: http://www.linux4sam.org S: Supported F: arch/arm/mach-at91/ ARM/BCMRING ARM ARCHITECTURE M: Jiandong Zheng M: Scott Branden L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-bcmring ARM/BCMRING MTD NAND DRIVER M: Jiandong Zheng M: Scott Branden L: linux-mtd@lists.infradead.org S: Maintained F: drivers/mtd/nand/bcm_umi_nand.c F: drivers/mtd/nand/bcm_umi_bch.c F: drivers/mtd/nand/nand_bcm_umi.h ARM/CALXEDA HIGHBANK ARCHITECTURE M: Rob Herring L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-highbank/ ARM/CAVIUM NETWORKS CNS3XXX MACHINE SUPPORT M: Anton Vorontsov S: Maintained F: arch/arm/mach-cns3xxx/ T: git git://git.infradead.org/users/cbou/linux-cns3xxx.git ARM/CIRRUS LOGIC EP93XX ARM ARCHITECTURE M: Hartley Sweeten M: Ryan Mallon L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-ep93xx/ F: arch/arm/mach-ep93xx/include/mach/ ARM/CIRRUS LOGIC EDB9315A MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/CLKDEV SUPPORT M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/include/asm/clkdev.h F: drivers/clk/clkdev.c ARM/COMPULAB CM-X270/EM-X270 and CM-X300 MACHINE SUPPORT M: Mike Rapoport L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/CONTEC MICRO9 MACHINE SUPPORT M: Hubert Feurstein S: Maintained F: arch/arm/mach-ep93xx/micro9.c ARM/CORGI MACHINE SUPPORT M: Richard Purdie S: Maintained ARM/CORTINA SYSTEMS GEMINI ARM ARCHITECTURE M: Hans Ulli Kroll L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) T: git git://git.berlios.de/gemini-board S: Maintained F: arch/arm/mach-gemini/ ARM/CSR SIRFPRIMA2 MACHINE SUPPORT M: Barry Song L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-prima2/ F: drivers/dma/sirf-dma.c F: drivers/i2c/busses/i2c-sirf.c F: drivers/pinctrl/pinctrl-sirf.c F: drivers/spi/spi-sirf.c ARM/EBSA110 MACHINE SUPPORT M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.arm.linux.org.uk/ S: Maintained F: arch/arm/mach-ebsa110/ F: drivers/net/ethernet/amd/am79c961a.* ARM/EZX SMARTPHONES (A780, A910, A1200, E680, ROKR E2 and ROKR E6) M: Daniel Ribeiro M: Stefan Schmidt M: Harald Welte L: openezx-devel@lists.openezx.org (moderated for non-subscribers) W: http://www.openezx.org/ S: Maintained T: topgit git://git.openezx.org/openezx.git F: arch/arm/mach-pxa/ezx.c ARM/FARADAY FA526 PORT M: Hans Ulli Kroll L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.berlios.de/gemini-board F: arch/arm/mm/*-fa* ARM/FOOTBRIDGE ARCHITECTURE M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.arm.linux.org.uk/ S: Maintained F: arch/arm/include/asm/hardware/dec21285.h F: arch/arm/mach-footbridge/ ARM/FREESCALE IMX / MXC ARM ARCHITECTURE M: Sascha Hauer L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.pengutronix.de/git/imx/linux-2.6.git F: arch/arm/mach-imx/ F: arch/arm/plat-mxc/ F: arch/arm/configs/imx*_defconfig ARM/FREESCALE IMX6 M: Shawn Guo L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.linaro.org/people/shawnguo/linux-2.6.git F: arch/arm/mach-imx/*imx6* ARM/FREESCALE MXS ARM ARCHITECTURE M: Shawn Guo L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.linaro.org/people/shawnguo/linux-2.6.git F: arch/arm/mach-mxs/ ARM/GLOMATION GESBC9312SX MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/GUMSTIX MACHINE SUPPORT M: Steve Sakoman L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/H4700 (HP IPAQ HX4700) MACHINE SUPPORT M: Philipp Zabel M: Paul Parsons L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-pxa/hx4700.c F: arch/arm/mach-pxa/include/mach/hx4700.h F: sound/soc/pxa/hx4700.c ARM/HP JORNADA 7XX MACHINE SUPPORT M: Kristoffer Ericson W: www.jlime.com S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/kristoffer/linux-hpc.git F: arch/arm/mach-sa1100/jornada720.c F: arch/arm/mach-sa1100/include/mach/jornada720.h ARM/INCOME PXA270 SUPPORT M: Marek Vasut L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-pxa/colibri-pxa270-income.c ARM/INTEL IOP32X ARM ARCHITECTURE M: Lennert Buytenhek M: Dan Williams L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/INTEL IOP33X ARM ARCHITECTURE M: Dan Williams L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/INTEL IOP13XX ARM ARCHITECTURE M: Lennert Buytenhek M: Dan Williams L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/INTEL IQ81342EX MACHINE SUPPORT M: Lennert Buytenhek M: Dan Williams L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/INTEL IXDP2850 MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/INTEL IXP4XX ARM ARCHITECTURE M: Imre Kaloz M: Krzysztof Halasa L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-ixp4xx/ ARM/INTEL RESEARCH IMOTE/STARGATE 2 MACHINE SUPPORT M: Jonathan Cameron L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-pxa/stargate2.c F: drivers/pcmcia/pxa2xx_stargate2.c ARM/INTEL XSC3 (MANZANO) ARM CORE M: Lennert Buytenhek M: Dan Williams L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/IP FABRICS DOUBLE ESPRESSO MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/LOGICPD PXA270 MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/MAGICIAN MACHINE SUPPORT M: Philipp Zabel S: Maintained ARM/Marvell Armada 370 and Armada XP SOC support M: Jason Cooper M: Andrew Lunn M: Gregory Clement L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-mvebu/ ARM/Marvell Dove/Kirkwood/MV78xx0/Orion SOC support M: Jason Cooper M: Andrew Lunn L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-dove/ F: arch/arm/mach-kirkwood/ F: arch/arm/mach-mv78xx0/ F: arch/arm/mach-orion5x/ F: arch/arm/plat-orion/ ARM/Orion SoC/Technologic Systems TS-78xx platform support M: Alexander Clouter L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.digriz.org.uk/ts78xx/kernel S: Maintained F: arch/arm/mach-orion5x/ts78xx-* ARM/MICREL KS8695 ARCHITECTURE M: Greg Ungerer L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) F: arch/arm/mach-ks8695 S: Odd Fixes ARM/MIOA701 MACHINE SUPPORT M: Robert Jarzmik L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) F: arch/arm/mach-pxa/mioa701.c S: Maintained ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT M: Michael Petchkovsky S: Maintained ARM/NOMADIK ARCHITECTURE M: Alessandro Rubini M: Linus Walleij M: STEricsson L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-nomadik/ F: arch/arm/plat-nomadik/ F: drivers/i2c/busses/i2c-nomadik.c T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git ARM/OPENMOKO NEO FREERUNNER (GTA02) MACHINE SUPPORT M: Nelson Castillo L: openmoko-kernel@lists.openmoko.org (subscribers-only) W: http://wiki.openmoko.org/wiki/Neo_FreeRunner S: Supported ARM/QUALCOMM MSM MACHINE SUPPORT M: David Brown M: Daniel Walker M: Bryan Huntsman L: linux-arm-msm@vger.kernel.org F: arch/arm/mach-msm/ F: drivers/video/msm/ F: drivers/mmc/host/msm_sdcc.c F: drivers/mmc/host/msm_sdcc.h F: drivers/tty/serial/msm_serial.h F: drivers/tty/serial/msm_serial.c F: drivers/platform/msm/ F: drivers/*/pm8???-* F: include/linux/mfd/pm8xxx/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/davidb/linux-msm.git S: Maintained ARM/TOSA MACHINE SUPPORT M: Dmitry Eremin-Solenikov M: Dirk Opfer S: Maintained ARM/PALMTX,PALMT5,PALMLD,PALMTE2,PALMTC SUPPORT M: Marek Vasut L: linux-arm-kernel@lists.infradead.org W: http://hackndev.com S: Maintained F: arch/arm/mach-pxa/include/mach/palmtx.h F: arch/arm/mach-pxa/palmtx.c F: arch/arm/mach-pxa/include/mach/palmt5.h F: arch/arm/mach-pxa/palmt5.c F: arch/arm/mach-pxa/include/mach/palmld.h F: arch/arm/mach-pxa/palmld.c F: arch/arm/mach-pxa/include/mach/palmte2.h F: arch/arm/mach-pxa/palmte2.c F: arch/arm/mach-pxa/include/mach/palmtc.h F: arch/arm/mach-pxa/palmtc.c ARM/PALM TREO SUPPORT M: Tomas Cech L: linux-arm-kernel@lists.infradead.org W: http://hackndev.com S: Maintained F: arch/arm/mach-pxa/include/mach/palmtreo.h F: arch/arm/mach-pxa/palmtreo.c ARM/PALMZ72 SUPPORT M: Sergey Lapin L: linux-arm-kernel@lists.infradead.org W: http://hackndev.com S: Maintained F: arch/arm/mach-pxa/include/mach/palmz72.h F: arch/arm/mach-pxa/palmz72.c ARM/PLEB SUPPORT M: Peter Chubb W: http://www.disy.cse.unsw.edu.au/Hardware/PLEB S: Maintained ARM/PT DIGITAL BOARD PORT M: Stefan Eletzhofer L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.arm.linux.org.uk/ S: Maintained ARM/RADISYS ENP2611 MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/RISCPC ARCHITECTURE M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.arm.linux.org.uk/ S: Maintained F: arch/arm/common/time-acorn.c F: arch/arm/include/asm/hardware/entry-macro-iomd.S F: arch/arm/include/asm/hardware/ioc.h F: arch/arm/include/asm/hardware/iomd.h F: arch/arm/include/asm/hardware/memc.h F: arch/arm/mach-rpc/ F: drivers/net/ethernet/8390/etherh.c F: drivers/net/ethernet/i825xx/ether1* F: drivers/net/ethernet/seeq/ether3* F: drivers/scsi/arm/ ARM/SHARK MACHINE SUPPORT M: Alexander Schulz W: http://www.shark-linux.de/shark.html S: Maintained ARM/SAMSUNG ARM ARCHITECTURES M: Ben Dooks M: Kukjin Kim L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers) W: http://www.fluff.org/ben/linux/ S: Maintained F: arch/arm/plat-samsung/ F: arch/arm/plat-s3c24xx/ F: arch/arm/plat-s5p/ F: arch/arm/mach-s3c24*/ F: arch/arm/mach-s3c64xx/ F: drivers/*/*s3c2410* F: drivers/*/*/*s3c2410* F: drivers/spi/spi-s3c* F: sound/soc/samsung/* ARM/S5P EXYNOS ARM ARCHITECTURES M: Kukjin Kim L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-s5p*/ F: arch/arm/mach-exynos*/ ARM/SAMSUNG MOBILE MACHINE SUPPORT M: Kyungmin Park L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-s5pv210/mach-aquila.c F: arch/arm/mach-s5pv210/mach-goni.c F: arch/arm/mach-exynos/mach-universal_c210.c F: arch/arm/mach-exynos/mach-nuri.c ARM/SAMSUNG S5P SERIES FIMC SUPPORT M: Kyungmin Park M: Sylwester Nawrocki L: linux-arm-kernel@lists.infradead.org L: linux-media@vger.kernel.org S: Maintained F: arch/arm/plat-s5p/dev-fimc* F: arch/arm/plat-samsung/include/plat/*fimc* F: drivers/media/video/s5p-fimc/ ARM/SAMSUNG S5P SERIES Multi Format Codec (MFC) SUPPORT M: Kyungmin Park M: Kamil Debski M: Jeongtae Park L: linux-arm-kernel@lists.infradead.org L: linux-media@vger.kernel.org S: Maintained F: arch/arm/plat-s5p/dev-mfc.c F: drivers/media/video/s5p-mfc/ ARM/SAMSUNG S5P SERIES TV SUBSYSTEM SUPPORT M: Kyungmin Park M: Tomasz Stanislawski L: linux-arm-kernel@lists.infradead.org L: linux-media@vger.kernel.org S: Maintained F: drivers/media/video/s5p-tv/ ARM/SHMOBILE ARM ARCHITECTURE M: Paul Mundt M: Magnus Damm L: linux-sh@vger.kernel.org W: http://oss.renesas.com Q: http://patchwork.kernel.org/project/linux-sh/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6.git rmobile-latest S: Supported F: arch/arm/mach-shmobile/ F: drivers/sh/ ARM/SOCFPGA ARCHITECTURE M: Dinh Nguyen S: Maintained F: arch/arm/mach-socfpga/ ARM/SOCFPGA CLOCK FRAMEWORK SUPPORT M: Dinh Nguyen S: Maintained F: drivers/clk/socfpga/ ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/TETON BGA MACHINE SUPPORT M: "Mark F. Brown" L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/THECUS N2100 MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/NUVOTON W90X900 ARM ARCHITECTURE M: Wan ZongShun L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.mcuos.com S: Maintained F: arch/arm/mach-w90x900/ F: drivers/input/keyboard/w90p910_keypad.c F: drivers/input/touchscreen/w90p910_ts.c F: drivers/watchdog/nuc900_wdt.c F: drivers/net/ethernet/nuvoton/w90p910_ether.c F: drivers/mtd/nand/nuc900_nand.c F: drivers/rtc/rtc-nuc900.c F: drivers/spi/spi-nuc900.c F: drivers/usb/host/ehci-w90x900.c F: drivers/video/nuc900fb.c ARM/U300 MACHINE SUPPORT M: Linus Walleij L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: arch/arm/mach-u300/ F: drivers/i2c/busses/i2c-stu300.c F: drivers/rtc/rtc-coh901331.c F: drivers/watchdog/coh901327_wdt.c F: drivers/dma/coh901318* F: drivers/mfd/ab3100* F: drivers/rtc/rtc-ab3100.c F: drivers/rtc/rtc-coh901331.c T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git ARM/Ux500 ARM ARCHITECTURE M: Srinidhi Kasagar M: Linus Walleij L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-ux500/ F: drivers/clocksource/clksrc-dbx500-prcmu.c F: drivers/dma/ste_dma40* F: drivers/hwspinlock/u8500_hsem.c F: drivers/mfd/abx500* F: drivers/mfd/ab8500* F: drivers/mfd/dbx500* F: drivers/mfd/db8500* F: drivers/pinctrl/pinctrl-nomadik* F: drivers/rtc/rtc-ab8500.c F: drivers/rtc/rtc-pl031.c T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git ARM/VFP SUPPORT M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.arm.linux.org.uk/ S: Maintained F: arch/arm/vfp/ ARM/VOIPAC PXA270 SUPPORT M: Marek Vasut L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-pxa/vpac270.c F: arch/arm/mach-pxa/include/mach/vpac270.h ARM/VT8500 ARM ARCHITECTURE M: Tony Prisk L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-vt8500/ F: drivers/video/vt8500lcdfb.* F: drivers/video/wm8505fb* F: drivers/video/wmt_ge_rops.* F: drivers/tty/serial/vt8500_serial.c F: drivers/rtc/rtc-vt8500-c ARM/ZIPIT Z2 SUPPORT M: Marek Vasut L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-pxa/z2.c F: arch/arm/mach-pxa/include/mach/z2.h ARM64 PORT (AARCH64 ARCHITECTURE) M: Catalin Marinas L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm64/ ASC7621 HARDWARE MONITOR DRIVER M: George Joseph L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/asc7621 F: drivers/hwmon/asc7621.c ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS M: Corentin Chary L: acpi4asus-user@lists.sourceforge.net L: platform-driver-x86@vger.kernel.org W: http://acpi4asus.sf.net S: Maintained F: drivers/platform/x86/asus*.c F: drivers/platform/x86/eeepc*.c ASUS ASB100 HARDWARE MONITOR DRIVER M: "Mark M. Hoffman" L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/asb100.c ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API M: Dan Williams W: http://sourceforge.net/projects/xscaleiop S: Maintained F: Documentation/crypto/async-tx-api.txt F: crypto/async_tx/ F: drivers/dma/ F: include/linux/dmaengine.h F: include/linux/async_tx.h AT24 EEPROM DRIVER M: Wolfram Sang L: linux-i2c@vger.kernel.org S: Maintained F: drivers/misc/eeprom/at24.c F: include/linux/i2c/at24.h ATA OVER ETHERNET (AOE) DRIVER M: "Ed L. Cashin" W: http://support.coraid.com/support/linux S: Supported F: Documentation/aoe/ F: drivers/block/aoe/ ATHEROS ATH GENERIC UTILITIES M: "Luis R. Rodriguez" L: linux-wireless@vger.kernel.org S: Supported F: drivers/net/wireless/ath/* ATHEROS ATH5K WIRELESS DRIVER M: Jiri Slaby M: Nick Kossifidis M: "Luis R. Rodriguez" L: linux-wireless@vger.kernel.org L: ath5k-devel@lists.ath5k.org W: http://wireless.kernel.org/en/users/Drivers/ath5k S: Maintained F: drivers/net/wireless/ath/ath5k/ ATHEROS ATH6KL WIRELESS DRIVER M: Kalle Valo L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/ath6kl T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath6kl.git S: Supported F: drivers/net/wireless/ath/ath6kl/ ATHEROS ATH9K WIRELESS DRIVER M: "Luis R. Rodriguez" M: Jouni Malinen M: Vasanthakumar Thiagarajan M: Senthil Balasubramanian L: linux-wireless@vger.kernel.org L: ath9k-devel@lists.ath9k.org W: http://wireless.kernel.org/en/users/Drivers/ath9k S: Supported F: drivers/net/wireless/ath/ath9k/ CARL9170 LINUX COMMUNITY WIRELESS DRIVER M: Christian Lamparter L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/carl9170 S: Maintained F: drivers/net/wireless/ath/carl9170/ ATK0110 HWMON DRIVER M: Luca Tettamanti L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/asus_atk0110.c ATI_REMOTE2 DRIVER M: Ville Syrjala S: Maintained F: drivers/input/misc/ati_remote2.c ATLX ETHERNET DRIVERS M: Jay Cliburn M: Chris Snook L: netdev@vger.kernel.org W: http://sourceforge.net/projects/atl1 W: http://atl1.sourceforge.net S: Maintained F: drivers/net/ethernet/atheros/ ATM M: Chas Williams L: linux-atm-general@lists.sourceforge.net (moderated for non-subscribers) L: netdev@vger.kernel.org W: http://linux-atm.sourceforge.net S: Maintained F: drivers/atm/ F: include/linux/atm* ATMEL AT91 MCI DRIVER M: Ludovic Desroches L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.atmel.com/products/AT91/ W: http://www.at91.com/ S: Maintained F: drivers/mmc/host/at91_mci.c ATMEL AT91 / AT32 MCI DRIVER M: Ludovic Desroches S: Maintained F: drivers/mmc/host/atmel-mci.c F: drivers/mmc/host/atmel-mci-regs.h ATMEL AT91 / AT32 SERIAL DRIVER M: Nicolas Ferre S: Supported F: drivers/tty/serial/atmel_serial.c ATMEL DMA DRIVER M: Nicolas Ferre L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/dma/at_hdmac.c F: drivers/dma/at_hdmac_regs.h F: arch/arm/mach-at91/include/mach/at_hdmac.h ATMEL ISI DRIVER M: Josh Wu L: linux-media@vger.kernel.org S: Supported F: drivers/media/video/atmel-isi.c F: include/media/atmel-isi.h ATMEL LCDFB DRIVER M: Nicolas Ferre L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/atmel_lcdfb.c F: include/video/atmel_lcdc.h ATMEL MACB ETHERNET DRIVER M: Nicolas Ferre S: Supported F: drivers/net/ethernet/cadence/ ATMEL SPI DRIVER M: Nicolas Ferre S: Supported F: drivers/spi/spi-atmel.* ATMEL Timer Counter (TC) AND CLOCKSOURCE DRIVERS M: Nicolas Ferre L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/misc/atmel_tclib.c F: drivers/clocksource/tcb_clksrc.c ATMEL TSADCC DRIVER M: Josh Wu L: linux-input@vger.kernel.org S: Supported F: drivers/input/touchscreen/atmel_tsadcc.c ATMEL USBA UDC DRIVER M: Nicolas Ferre L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/usb/gadget/atmel_usba_udc.* ATMEL WIRELESS DRIVER M: Simon Kelley L: linux-wireless@vger.kernel.org W: http://www.thekelleys.org.uk/atmel W: http://atmelwlandriver.sourceforge.net/ S: Maintained F: drivers/net/wireless/atmel* AUDIT SUBSYSTEM M: Al Viro M: Eric Paris L: linux-audit@redhat.com (subscribers-only) W: http://people.redhat.com/sgrubb/audit/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current.git S: Maintained F: include/linux/audit.h F: kernel/audit* AUXILIARY DISPLAY DRIVERS M: Miguel Ojeda Sandonis W: http://miguelojeda.es/auxdisplay.htm W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained F: drivers/auxdisplay/ F: include/linux/cfag12864b.h AVR32 ARCHITECTURE M: Haavard Skinnemoen M: Hans-Christian Egtvedt W: http://www.atmel.com/products/AVR32/ W: http://avr32linux.org/ W: http://avrfreaks.net/ S: Maintained F: arch/avr32/ AVR32/AT32AP MACHINE SUPPORT M: Haavard Skinnemoen M: Hans-Christian Egtvedt S: Maintained F: arch/avr32/mach-at32ap/ AX.25 NETWORK LAYER M: Ralf Baechle L: linux-hams@vger.kernel.org W: http://www.linux-ax25.org/ S: Maintained F: include/linux/ax25.h F: include/net/ax25.h F: net/ax25/ B43 WIRELESS DRIVER M: Stefano Brivio L: linux-wireless@vger.kernel.org L: b43-dev@lists.infradead.org W: http://wireless.kernel.org/en/users/Drivers/b43 S: Maintained F: drivers/net/wireless/b43/ B43LEGACY WIRELESS DRIVER M: Larry Finger M: Stefano Brivio L: linux-wireless@vger.kernel.org L: b43-dev@lists.infradead.org W: http://wireless.kernel.org/en/users/Drivers/b43 S: Maintained F: drivers/net/wireless/b43legacy/ BACKLIGHT CLASS/SUBSYSTEM M: Richard Purdie S: Maintained F: drivers/video/backlight/ F: include/linux/backlight.h BATMAN ADVANCED M: Marek Lindner M: Simon Wunderlich M: Antonio Quartulli L: b.a.t.m.a.n@lists.open-mesh.org W: http://www.open-mesh.org/ S: Maintained F: net/batman-adv/ BAYCOM/HDLCDRV DRIVERS FOR AX.25 M: Thomas Sailer L: linux-hams@vger.kernel.org W: http://www.baycom.org/~tom/ham/ham.html S: Maintained F: drivers/net/hamradio/baycom* BEFS FILE SYSTEM S: Orphan F: Documentation/filesystems/befs.txt F: fs/befs/ BFS FILE SYSTEM M: "Tigran A. Aivazian" S: Maintained F: Documentation/filesystems/bfs.txt F: fs/bfs/ F: include/linux/bfs_fs.h BLACKFIN ARCHITECTURE M: Mike Frysinger L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org S: Supported F: arch/blackfin/ BLACKFIN EMAC DRIVER L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org S: Supported F: drivers/net/ethernet/adi/ BLACKFIN RTC DRIVER M: Mike Frysinger L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org S: Supported F: drivers/rtc/rtc-bfin.c BLACKFIN SDH DRIVER M: Sonic Zhang L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org S: Supported F: drivers/mmc/host/bfin_sdh.c BLACKFIN SERIAL DRIVER M: Sonic Zhang L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org S: Supported F: drivers/tty/serial/bfin_uart.c BLACKFIN WATCHDOG DRIVER M: Mike Frysinger L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org S: Supported F: drivers/watchdog/bfin_wdt.c BLACKFIN I2C TWI DRIVER M: Sonic Zhang L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org/ S: Supported F: drivers/i2c/busses/i2c-bfin-twi.c BLINKM RGB LED DRIVER M: Jan-Simon Moeller S: Maintained F: drivers/leds/leds-blinkm.c BLOCK LAYER M: Jens Axboe T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git S: Maintained F: block/ BLOCK2MTD DRIVER M: Joern Engel L: linux-mtd@lists.infradead.org S: Maintained F: drivers/mtd/devices/block2mtd.c BLUETOOTH DRIVERS M: Marcel Holtmann M: Gustavo Padovan M: Johan Hedberg L: linux-bluetooth@vger.kernel.org W: http://www.bluez.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git S: Maintained F: drivers/bluetooth/ BLUETOOTH SUBSYSTEM M: Marcel Holtmann M: Gustavo Padovan M: Johan Hedberg L: linux-bluetooth@vger.kernel.org W: http://www.bluez.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git S: Maintained F: net/bluetooth/ F: include/net/bluetooth/ BONDING DRIVER M: Jay Vosburgh M: Andy Gospodarek L: netdev@vger.kernel.org W: http://sourceforge.net/projects/bonding/ S: Supported F: drivers/net/bonding/ F: include/linux/if_bonding.h BROADCOM B44 10/100 ETHERNET DRIVER M: Gary Zambrano L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/broadcom/b44.* BROADCOM BNX2 GIGABIT ETHERNET DRIVER M: Michael Chan L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/broadcom/bnx2.* F: drivers/net/ethernet/broadcom/bnx2_* BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER M: Eilon Greenstein L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/broadcom/bnx2x/ BROADCOM TG3 GIGABIT ETHERNET DRIVER M: Matt Carlson M: Michael Chan L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/broadcom/tg3.* BROADCOM BRCM80211 IEEE802.11n WIRELESS DRIVER M: Brett Rudley M: Roland Vossen M: Arend van Spriel M: Franky (Zhenhui) Lin M: Kan Yan L: linux-wireless@vger.kernel.org L: brcm80211-dev-list@broadcom.com S: Supported F: drivers/net/wireless/brcm80211/ BROADCOM BNX2FC 10 GIGABIT FCOE DRIVER M: Bhanu Prakash Gollapudi L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/bnx2fc/ BROADCOM SPECIFIC AMBA DRIVER (BCMA) M: RafaÅ‚ MiÅ‚ecki L: linux-wireless@vger.kernel.org S: Maintained F: drivers/bcma/ F: include/linux/bcma/ BROCADE BFA FC SCSI DRIVER M: Krishna C Gudipati L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/bfa/ BROCADE BNA 10 GIGABIT ETHERNET DRIVER M: Rasesh Mody L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/brocade/bna/ BSG (block layer generic sg v4 driver) M: FUJITA Tomonori L: linux-scsi@vger.kernel.org S: Supported F: block/bsg.c F: include/linux/bsg.h BT87X AUDIO DRIVER M: Clemens Ladisch L: alsa-devel@alsa-project.org (moderated for non-subscribers) T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: Documentation/sound/alsa/Bt87x.txt F: sound/pci/bt87x.c BT8XXGPIO DRIVER M: Michael Buesch W: http://bu3sch.de/btgpio.php S: Maintained F: drivers/gpio/gpio-bt8xx.c BTRFS FILE SYSTEM M: Chris Mason L: linux-btrfs@vger.kernel.org W: http://btrfs.wiki.kernel.org/ Q: http://patchwork.kernel.org/project/linux-btrfs/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs.git S: Maintained F: Documentation/filesystems/btrfs.txt F: fs/btrfs/ BTTV VIDEO4LINUX DRIVER M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: http://linuxtv.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: Documentation/video4linux/bttv/ F: drivers/media/video/bt8xx/bttv* C-MEDIA CMI8788 DRIVER M: Clemens Ladisch L: alsa-devel@alsa-project.org (moderated for non-subscribers) T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: sound/pci/oxygen/ C6X ARCHITECTURE M: Mark Salter M: Aurelien Jacquiot L: linux-c6x-dev@linux-c6x.org W: http://www.linux-c6x.org/wiki/index.php/Main_Page S: Maintained F: arch/c6x/ CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS M: David Howells L: linux-cachefs@redhat.com S: Supported F: Documentation/filesystems/caching/cachefiles.txt F: fs/cachefiles/ CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER M: Jonathan Corbet L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: Documentation/video4linux/cafe_ccic F: drivers/media/video/marvell-ccic/ CAIF NETWORK LAYER M: Sjur Braendeland L: netdev@vger.kernel.org S: Supported F: Documentation/networking/caif/ F: drivers/net/caif/ F: include/linux/caif/ F: include/net/caif/ F: net/caif/ CALGARY x86-64 IOMMU M: Muli Ben-Yehuda M: "Jon D. Mason" L: discuss@x86-64.org S: Maintained F: arch/x86/kernel/pci-calgary_64.c F: arch/x86/kernel/tce_64.c F: arch/x86/include/asm/calgary.h F: arch/x86/include/asm/tce.h CAN NETWORK LAYER M: Oliver Hartkopp L: linux-can@vger.kernel.org W: http://gitorious.org/linux-can T: git git://gitorious.org/linux-can/linux-can-next.git S: Maintained F: net/can/ F: include/linux/can.h F: include/linux/can/core.h F: include/linux/can/bcm.h F: include/linux/can/raw.h F: include/linux/can/gw.h CAN NETWORK DRIVERS M: Wolfgang Grandegger M: Marc Kleine-Budde L: linux-can@vger.kernel.org W: http://gitorious.org/linux-can T: git git://gitorious.org/linux-can/linux-can-next.git S: Maintained F: drivers/net/can/ F: include/linux/can/dev.h F: include/linux/can/error.h F: include/linux/can/netlink.h F: include/linux/can/platform/ CAPABILITIES M: Serge Hallyn L: linux-security-module@vger.kernel.org S: Supported F: include/linux/capability.h F: security/capability.c F: security/commoncap.c F: kernel/capability.c CELL BROADBAND ENGINE ARCHITECTURE M: Arnd Bergmann L: linuxppc-dev@lists.ozlabs.org L: cbe-oss-dev@lists.ozlabs.org W: http://www.ibm.com/developerworks/power/cell/ S: Supported F: arch/powerpc/include/asm/cell*.h F: arch/powerpc/include/asm/spu*.h F: arch/powerpc/oprofile/*cell* F: arch/powerpc/platforms/cell/ CEPH DISTRIBUTED FILE SYSTEM CLIENT M: Sage Weil L: ceph-devel@vger.kernel.org W: http://ceph.com/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git S: Supported F: Documentation/filesystems/ceph.txt F: fs/ceph F: net/ceph F: include/linux/ceph F: include/linux/crush CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM: L: linux-usb@vger.kernel.org S: Orphan F: Documentation/usb/WUSB-Design-overview.txt F: Documentation/usb/wusb-cbaf F: drivers/usb/host/hwa-hc.c F: drivers/usb/host/whci/ F: drivers/usb/wusbcore/ F: include/linux/usb/wusb* CFAG12864B LCD DRIVER M: Miguel Ojeda Sandonis W: http://miguelojeda.es/auxdisplay.htm W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained F: drivers/auxdisplay/cfag12864b.c F: include/linux/cfag12864b.h CFAG12864BFB LCD FRAMEBUFFER DRIVER M: Miguel Ojeda Sandonis W: http://miguelojeda.es/auxdisplay.htm W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained F: drivers/auxdisplay/cfag12864bfb.c F: include/linux/cfag12864b.h CFG80211 and NL80211 M: Johannes Berg L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git S: Maintained F: include/linux/nl80211.h F: include/net/cfg80211.h F: net/wireless/* X: net/wireless/wext* CHAR and MISC DRIVERS M: Arnd Bergmann M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git S: Supported F: drivers/char/* F: drivers/misc/* CHECKPATCH M: Andy Whitcroft S: Supported F: scripts/checkpatch.pl CHINESE DOCUMENTATION M: Harry Wei L: xiyoulinuxkernelgroup@googlegroups.com L: linux-kernel@zh-kernel.org (moderated for non-subscribers) S: Maintained F: Documentation/zh_CN/ CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER M: Alexander Shishkin L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/chipidea/ CISCO VIC ETHERNET NIC DRIVER M: Christian Benvenuti M: Roopa Prabhu M: Neel Patel M: Nishank Trivedi S: Supported F: drivers/net/ethernet/cisco/enic/ CIRRUS LOGIC EP93XX ETHERNET DRIVER M: Hartley Sweeten L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/cirrus/ep93xx_eth.c CIRRUS LOGIC EP93XX OHCI USB HOST DRIVER M: Lennert Buytenhek L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/host/ohci-ep93xx.c CIRRUS LOGIC CS4270 SOUND DRIVER M: Timur Tabi L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Supported F: sound/soc/codecs/cs4270* CLEANCACHE API M: Konrad Rzeszutek Wilk L: linux-kernel@vger.kernel.org S: Maintained F: mm/cleancache.c F: include/linux/cleancache.h CLK API M: Russell King S: Maintained F: include/linux/clk.h CISCO FCOE HBA DRIVER M: Abhijeet Joglekar M: Venkata Siva Vijayendra Bhamidipati M: Brian Uchino L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/fnic/ CMPC ACPI DRIVER M: Thadeu Lima de Souza Cascardo M: Daniel Oliveira Nascimento L: platform-driver-x86@vger.kernel.org S: Supported F: drivers/platform/x86/classmate-laptop.c COCCINELLE/Semantic Patches (SmPL) M: Julia Lawall M: Gilles Muller M: Nicolas Palix L: cocci@diku.dk (moderated for non-subscribers) W: http://coccinelle.lip6.fr/ S: Supported F: scripts/coccinelle/ F: scripts/coccicheck CODA FILE SYSTEM M: Jan Harkes M: coda@cs.cmu.edu L: codalist@coda.cs.cmu.edu W: http://www.coda.cs.cmu.edu/ S: Maintained F: Documentation/filesystems/coda.txt F: fs/coda/ F: include/linux/coda*.h COMMON CLK FRAMEWORK M: Mike Turquette M: Mike Turquette L: linux-arm-kernel@lists.infradead.org (same as CLK API & CLKDEV) T: git git://git.linaro.org/people/mturquette/linux.git S: Maintained F: drivers/clk/clk.c F: drivers/clk/clk-* F: include/linux/clk-pr* COMMON INTERNET FILE SYSTEM (CIFS) M: Steve French L: linux-cifs@vger.kernel.org L: samba-technical@lists.samba.org (moderated for non-subscribers) W: http://linux-cifs.samba.org/ Q: http://patchwork.ozlabs.org/project/linux-cifs-client/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git S: Supported F: Documentation/filesystems/cifs.txt F: fs/cifs/ COMPACTPCI HOTPLUG CORE M: Scott Murray L: linux-pci@vger.kernel.org S: Maintained F: drivers/pci/hotplug/cpci_hotplug* COMPACTPCI HOTPLUG ZIATECH ZT5550 DRIVER M: Scott Murray L: linux-pci@vger.kernel.org S: Maintained F: drivers/pci/hotplug/cpcihp_zt5550.* COMPACTPCI HOTPLUG GENERIC DRIVER M: Scott Murray L: linux-pci@vger.kernel.org S: Maintained F: drivers/pci/hotplug/cpcihp_generic.c COMPAL LAPTOP SUPPORT M: Cezary Jackiewicz L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/compal-laptop.c CONEXANT ACCESSRUNNER USB DRIVER M: Simon Arlott L: accessrunner-general@lists.sourceforge.net W: http://accessrunner.sourceforge.net/ S: Maintained F: drivers/usb/atm/cxacru.c CONFIGFS M: Joel Becker T: git git://git.kernel.org/pub/scm/linux/kernel/git/jlbec/configfs.git S: Supported F: fs/configfs/ F: include/linux/configfs.h CONNECTOR M: Evgeniy Polyakov L: netdev@vger.kernel.org S: Maintained F: drivers/connector/ CONTROL GROUPS (CGROUPS) M: Tejun Heo M: Li Zefan L: containers@lists.linux-foundation.org L: cgroups@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git S: Maintained F: include/linux/cgroup* F: kernel/cgroup* F: mm/*cgroup* CORETEMP HARDWARE MONITORING DRIVER M: Fenghua Yu L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/coretemp F: drivers/hwmon/coretemp.c COSA/SRP SYNC SERIAL DRIVER M: Jan "Yenya" Kasprzak W: http://www.fi.muni.cz/~kas/cosa/ S: Maintained F: drivers/net/wan/cosa* CPMAC ETHERNET DRIVER M: Florian Fainelli L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/ti/cpmac.c CPU FREQUENCY DRIVERS M: Rafael J. Wysocki L: cpufreq@vger.kernel.org L: linux-pm@vger.kernel.org S: Maintained F: drivers/cpufreq/ F: include/linux/cpufreq.h CPUID/MSR DRIVER M: "H. Peter Anvin" S: Maintained F: arch/x86/kernel/cpuid.c F: arch/x86/kernel/msr.c CPU POWER MONITORING SUBSYSTEM M: Dominik Brodowski M: Thomas Renninger S: Maintained F: tools/power/cpupower CPUSETS M: Paul Menage W: http://www.bullopensource.org/cpuset/ W: http://oss.sgi.com/projects/cpusets/ S: Supported F: Documentation/cgroups/cpusets.txt F: include/linux/cpuset.h F: kernel/cpuset.c CRAMFS FILESYSTEM W: http://sourceforge.net/projects/cramfs/ S: Orphan F: Documentation/filesystems/cramfs.txt F: fs/cramfs/ CRIS PORT M: Mikael Starvik M: Jesper Nilsson L: linux-cris-kernel@axis.com W: http://developer.axis.com S: Maintained F: arch/cris/ F: drivers/tty/serial/crisv10.* CRYPTO API M: Herbert Xu M: "David S. Miller" L: linux-crypto@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6.git S: Maintained F: Documentation/crypto/ F: arch/*/crypto/ F: crypto/ F: drivers/crypto/ F: include/crypto/ CRYPTOGRAPHIC RANDOM NUMBER GENERATOR M: Neil Horman L: linux-crypto@vger.kernel.org S: Maintained F: crypto/ansi_cprng.c F: crypto/rng.c CS5535 Audio ALSA driver M: Jaya Kumar S: Maintained F: sound/pci/cs5535audio/ CX18 VIDEO4LINUX DRIVER M: Andy Walls L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers) L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://linuxtv.org W: http://www.ivtvdriver.org/index.php/Cx18 S: Maintained F: Documentation/video4linux/cx18.txt F: drivers/media/video/cx18/ CXGB3 ETHERNET DRIVER (CXGB3) M: Divy Le Ray L: netdev@vger.kernel.org W: http://www.chelsio.com S: Supported F: drivers/net/ethernet/chelsio/cxgb3/ CXGB3 IWARP RNIC DRIVER (IW_CXGB3) M: Steve Wise L: linux-rdma@vger.kernel.org W: http://www.openfabrics.org S: Supported F: drivers/infiniband/hw/cxgb3/ CXGB4 ETHERNET DRIVER (CXGB4) M: Dimitris Michailidis L: netdev@vger.kernel.org W: http://www.chelsio.com S: Supported F: drivers/net/ethernet/chelsio/cxgb4/ CXGB4 IWARP RNIC DRIVER (IW_CXGB4) M: Steve Wise L: linux-rdma@vger.kernel.org W: http://www.openfabrics.org S: Supported F: drivers/infiniband/hw/cxgb4/ CXGB4VF ETHERNET DRIVER (CXGB4VF) M: Casey Leedom L: netdev@vger.kernel.org W: http://www.chelsio.com S: Supported F: drivers/net/ethernet/chelsio/cxgb4vf/ STMMAC ETHERNET DRIVER M: Giuseppe Cavallaro L: netdev@vger.kernel.org W: http://www.stlinux.com S: Supported F: drivers/net/ethernet/stmicro/stmmac/ CYBERPRO FB DRIVER M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.arm.linux.org.uk/ S: Maintained F: drivers/video/cyber2000fb.* CYCLADES 2X SYNC CARD DRIVER M: Arnaldo Carvalho de Melo W: http://oops.ghostprotocols.net:81/blog S: Maintained F: drivers/net/wan/cycx* CYCLADES ASYNC MUX DRIVER W: http://www.cyclades.com/ S: Orphan F: drivers/tty/cyclades.c F: include/linux/cyclades.h CYCLADES PC300 DRIVER W: http://www.cyclades.com/ S: Orphan F: drivers/net/wan/pc300* CYTTSP TOUCHSCREEN DRIVER M: Javier Martinez Canillas L: linux-input@vger.kernel.org S: Maintained F: drivers/input/touchscreen/cyttsp* F: include/linux/input/cyttsp.h DAMA SLAVE for AX.25 M: Joerg Reuter W: http://yaina.de/jreuter/ W: http://www.qsl.net/dl1bke/ L: linux-hams@vger.kernel.org S: Maintained F: net/ax25/af_ax25.c F: net/ax25/ax25_dev.c F: net/ax25/ax25_ds_* F: net/ax25/ax25_in.c F: net/ax25/ax25_out.c F: net/ax25/ax25_timer.c F: net/ax25/sysctl_net_ax25.c DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER L: netdev@vger.kernel.org S: Orphan F: Documentation/networking/dmfe.txt F: drivers/net/ethernet/dec/tulip/dmfe.c DC390/AM53C974 SCSI driver M: Kurt Garloff W: http://www.garloff.de/kurt/linux/dc390/ M: Guennadi Liakhovetski S: Maintained F: drivers/scsi/tmscsim.* DC395x SCSI driver M: Oliver Neukum M: Ali Akcaagac M: Jamie Lenehan W: http://twibble.org/dist/dc395x/ L: dc395x@twibble.org L: http://lists.twibble.org/mailman/listinfo/dc395x/ S: Maintained F: Documentation/scsi/dc395x.txt F: drivers/scsi/dc395x.* DCCP PROTOCOL M: Gerrit Renker L: dccp@vger.kernel.org W: http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp S: Maintained F: include/linux/dccp.h F: include/linux/tfrc.h F: net/dccp/ DECnet NETWORK LAYER W: http://linux-decnet.sourceforge.net L: linux-decnet-user@lists.sourceforge.net S: Orphan F: Documentation/networking/decnet.txt F: net/decnet/ DEFXX FDDI NETWORK DRIVER M: "Maciej W. Rozycki" S: Maintained F: drivers/net/fddi/defxx.* DELL LAPTOP DRIVER M: Matthew Garrett L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/dell-laptop.c DELL LAPTOP SMM DRIVER M: Massimo Dal Zotto W: http://www.debian.org/~dz/i8k/ S: Maintained F: drivers/char/i8k.c F: include/linux/i8k.h DELL SYSTEMS MANAGEMENT BASE DRIVER (dcdbas) M: Doug Warzecha S: Maintained F: Documentation/dcdbas.txt F: drivers/firmware/dcdbas.* DELL WMI EXTRAS DRIVER M: Matthew Garrett S: Maintained F: drivers/platform/x86/dell-wmi.c DESIGNWARE USB3 DRD IP DRIVER M: Felipe Balbi L: linux-usb@vger.kernel.org L: linux-omap@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/dwc3/ DEVICE FREQUENCY (DEVFREQ) M: MyungJoo Ham M: Kyungmin Park L: linux-kernel@vger.kernel.org S: Maintained F: drivers/devfreq/ DEVICE NUMBER REGISTRY M: Torben Mathiasen W: http://lanana.org/docs/device-list/index.html S: Maintained DEVICE-MAPPER (LVM) M: Alasdair Kergon M: dm-devel@redhat.com L: dm-devel@redhat.com W: http://sources.redhat.com/dm Q: http://patchwork.kernel.org/project/dm-devel/list/ T: quilt http://people.redhat.com/agk/patches/linux/editing/ S: Maintained F: Documentation/device-mapper/ F: drivers/md/dm* F: drivers/md/persistent-data/ F: include/linux/device-mapper.h F: include/linux/dm-*.h DIOLAN U2C-12 I2C DRIVER M: Guenter Roeck L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-diolan-u2c.c DIRECTORY NOTIFICATION (DNOTIFY) M: Eric Paris S: Maintained F: Documentation/filesystems/dnotify.txt F: fs/notify/dnotify/ F: include/linux/dnotify.h DISK GEOMETRY AND PARTITION HANDLING M: Andries Brouwer W: http://www.win.tue.nl/~aeb/linux/Large-Disk.html W: http://www.win.tue.nl/~aeb/linux/zip/zip-1.html W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html S: Maintained DISKQUOTA M: Jan Kara S: Maintained F: Documentation/filesystems/quota.txt F: fs/quota/ F: include/linux/quota*.h DISPLAYLINK USB 2.0 FRAMEBUFFER DRIVER (UDLFB) M: Bernie Thompson L: linux-fbdev@vger.kernel.org S: Maintained W: http://plugable.com/category/projects/udlfb/ F: drivers/video/udlfb.c F: include/video/udlfb.h F: Documentation/fb/udlfb.txt DISTRIBUTED LOCK MANAGER (DLM) M: Christine Caulfield M: David Teigland L: cluster-devel@redhat.com W: http://sources.redhat.com/cluster/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/teigland/dlm.git S: Supported F: fs/dlm/ DMA BUFFER SHARING FRAMEWORK M: Sumit Semwal S: Maintained L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org L: linaro-mm-sig@lists.linaro.org F: drivers/base/dma-buf* F: include/linux/dma-buf* F: Documentation/dma-buf-sharing.txt T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git DMA GENERIC OFFLOAD ENGINE SUBSYSTEM M: Vinod Koul M: Dan Williams S: Supported F: drivers/dma/ F: include/linux/dma* T: git git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx.git T: git git://git.infradead.org/users/vkoul/slave-dma.git (slave-dma) DME1737 HARDWARE MONITOR DRIVER M: Juerg Haefliger L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/dme1737 F: drivers/hwmon/dme1737.c DOCKING STATION DRIVER M: Shaohua Li L: linux-acpi@vger.kernel.org S: Supported F: drivers/acpi/dock.c DOCUMENTATION M: Rob Landley L: linux-doc@vger.kernel.org T: TBD S: Maintained F: Documentation/ DOUBLETALK DRIVER M: "James R. Van Zandt" L: blinux-list@redhat.com S: Maintained F: drivers/char/dtlk.c F: include/linux/dtlk.h DPT_I2O SCSI RAID DRIVER M: Adaptec OEM Raid Solutions L: linux-scsi@vger.kernel.org W: http://www.adaptec.com/ S: Maintained F: drivers/scsi/dpt* F: drivers/scsi/dpt/ DRBD DRIVER P: Philipp Reisner P: Lars Ellenberg M: drbd-dev@lists.linbit.com L: drbd-user@lists.linbit.com W: http://www.drbd.org T: git git://git.drbd.org/linux-2.6-drbd.git drbd T: git git://git.drbd.org/drbd-8.3.git S: Supported F: drivers/block/drbd/ F: lib/lru_cache.c F: Documentation/blockdev/drbd/ DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git S: Supported F: Documentation/kobject.txt F: drivers/base/ F: fs/sysfs/ F: fs/debugfs/ F: include/linux/kobj* F: include/linux/debugfs.h F: lib/kobj* DRM DRIVERS M: David Airlie L: dri-devel@lists.freedesktop.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6.git S: Maintained F: drivers/gpu/drm/ F: include/drm/ INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets) M: Daniel Vetter L: intel-gfx@lists.freedesktop.org (subscribers-only) L: dri-devel@lists.freedesktop.org T: git git://people.freedesktop.org/~danvet/drm-intel S: Supported F: drivers/gpu/drm/i915 F: include/drm/i915* DRM DRIVERS FOR EXYNOS M: Inki Dae M: Joonyoung Shim M: Seung-Woo Kim M: Kyungmin Park L: dri-devel@lists.freedesktop.org S: Supported F: drivers/gpu/drm/exynos F: include/drm/exynos* DSCC4 DRIVER M: Francois Romieu L: netdev@vger.kernel.org S: Maintained F: drivers/net/wan/dscc4.c DYNAMIC DEBUG M: Jason Baron S: Maintained F: lib/dynamic_debug.c F: include/linux/dynamic_debug.h DZ DECSTATION DZ11 SERIAL DRIVER M: "Maciej W. Rozycki" S: Maintained F: drivers/tty/serial/dz.* EATA-DMA SCSI DRIVER M: Michael Neuffer L: linux-eata@i-connect.net L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/eata* EATA ISA/EISA/PCI SCSI DRIVER M: Dario Ballabio L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/eata.c EATA-PIO SCSI DRIVER M: Michael Neuffer L: linux-eata@i-connect.net L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/eata_pio.* EBTABLES M: Bart De Schuymer L: netfilter-devel@vger.kernel.org W: http://ebtables.sourceforge.net/ S: Maintained F: include/linux/netfilter_bridge/ebt_*.h F: net/bridge/netfilter/ebt*.c ECRYPT FILE SYSTEM M: Tyler Hicks M: Dustin Kirkland L: ecryptfs@vger.kernel.org W: https://launchpad.net/ecryptfs S: Supported F: Documentation/filesystems/ecryptfs.txt F: fs/ecryptfs/ EDAC-CORE M: Doug Thompson L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Supported F: Documentation/edac.txt F: drivers/edac/ F: include/linux/edac.h EDAC-AMD64 M: Doug Thompson M: Borislav Petkov L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Supported F: drivers/edac/amd64_edac* EDAC-E752X M: Mark Gross M: Doug Thompson L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/e752x_edac.c EDAC-E7XXX M: Doug Thompson L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/e7xxx_edac.c EDAC-I82443BXGX M: Tim Small L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i82443bxgx_edac.c EDAC-I3000 M: Jason Uhlenkott L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i3000_edac.c EDAC-I5000 M: Doug Thompson L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i5000_edac.c EDAC-I5400 M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i5400_edac.c EDAC-I7300 M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i7300_edac.c EDAC-I7CORE M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i7core_edac.c EDAC-I82975X M: Ranganathan Desikan M: "Arvind R." L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/i82975x_edac.c EDAC-PASEMI M: Egor Martovetsky L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/pasemi_edac.c EDAC-R82600 M: Tim Small L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/r82600_edac.c EDAC-SBRIDGE M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org W: bluesmoke.sourceforge.net S: Maintained F: drivers/edac/sb_edac.c EDIROL UA-101/UA-1000 DRIVER M: Clemens Ladisch L: alsa-devel@alsa-project.org (moderated for non-subscribers) T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: sound/usb/misc/ua101.c EFIFB FRAMEBUFFER DRIVER L: linux-fbdev@vger.kernel.org M: Peter Jones S: Maintained F: drivers/video/efifb.c EFS FILESYSTEM W: http://aeschi.ch.eu.org/efs/ S: Orphan F: fs/efs/ EHCA (IBM GX bus InfiniBand adapter) DRIVER M: Hoang-Nam Nguyen M: Christoph Raisch L: linux-rdma@vger.kernel.org S: Supported F: drivers/infiniband/hw/ehca/ EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER M: Thadeu Lima de Souza Cascardo L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/ibm/ehea/ EMBEDDED LINUX M: Paul Gortmaker M: Matt Mackall M: David Woodhouse L: linux-embedded@vger.kernel.org S: Maintained EMULEX LPFC FC SCSI DRIVER M: James Smart L: linux-scsi@vger.kernel.org W: http://sourceforge.net/projects/lpfcxxxx S: Supported F: drivers/scsi/lpfc/ ENE CB710 FLASH CARD READER DRIVER M: MichaÅ‚ MirosÅ‚aw S: Maintained F: drivers/misc/cb710/ F: drivers/mmc/host/cb710-mmc.* F: include/linux/cb710.h ENE KB2426 (ENE0100/ENE020XX) INFRARED RECEIVER M: Maxim Levitsky S: Maintained F: drivers/media/rc/ene_ir.* EPSON 1355 FRAMEBUFFER DRIVER M: Christopher Hoover M: Christopher Hoover S: Maintained F: drivers/video/epson1355fb.c EPSON S1D13XXX FRAMEBUFFER DRIVER M: Kristoffer Ericson S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/kristoffer/linux-hpc.git F: drivers/video/s1d13xxxfb.c F: include/video/s1d13xxxfb.h ETHEREXPRESS-16 NETWORK DRIVER M: Philip Blundell L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/i825xx/eexpress.* ETHERNET BRIDGE M: Stephen Hemminger L: bridge@lists.linux-foundation.org L: netdev@vger.kernel.org W: http://www.linuxfoundation.org/en/Net:Bridge S: Maintained F: include/linux/netfilter_bridge/ F: net/bridge/ ETHERTEAM 16I DRIVER M: Mika Kuoppala S: Maintained F: drivers/net/ethernet/fujitsu/eth16i.c EXT2 FILE SYSTEM M: Jan Kara L: linux-ext4@vger.kernel.org S: Maintained F: Documentation/filesystems/ext2.txt F: fs/ext2/ F: include/linux/ext2* EXT3 FILE SYSTEM M: Jan Kara M: Andrew Morton M: Andreas Dilger L: linux-ext4@vger.kernel.org S: Maintained F: Documentation/filesystems/ext3.txt F: fs/ext3/ F: include/linux/ext3* EXT4 FILE SYSTEM M: "Theodore Ts'o" M: Andreas Dilger L: linux-ext4@vger.kernel.org W: http://ext4.wiki.kernel.org Q: http://patchwork.ozlabs.org/project/linux-ext4/list/ S: Maintained F: Documentation/filesystems/ext4.txt F: fs/ext4/ Extended Verification Module (EVM) M: Mimi Zohar S: Supported F: security/integrity/evm/ EXTERNAL CONNECTOR SUBSYSTEM (EXTCON) M: MyungJoo Ham M: Chanwoo Choi L: linux-kernel@vger.kernel.org S: Maintained F: drivers/extcon/ F: Documentation/extcon/ EXYNOS DP DRIVER M: Jingoo Han L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/exynos/exynos_dp* F: include/video/exynos_dp* EXYNOS MIPI DISPLAY DRIVERS M: Inki Dae M: Donghwa Lee M: Kyungmin Park L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/exynos/exynos_mipi* F: include/video/exynos_mipi* F71805F HARDWARE MONITORING DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/f71805f F: drivers/hwmon/f71805f.c FC0011 TUNER DRIVER M: Michael Buesch L: linux-media@vger.kernel.org S: Maintained F: drivers/media/tuners/fc0011.h F: drivers/media/tuners/fc0011.c FANOTIFY M: Eric Paris S: Maintained F: fs/notify/fanotify/ F: include/linux/fanotify.h FARSYNC SYNCHRONOUS DRIVER M: Kevin Curtis W: http://www.farsite.co.uk/ S: Supported F: drivers/net/wan/farsync.* FAULT INJECTION SUPPORT M: Akinobu Mita S: Supported F: Documentation/fault-injection/ F: lib/fault-inject.c FCOE SUBSYSTEM (libfc, libfcoe, fcoe) M: Robert Love L: devel@open-fcoe.org W: www.Open-FCoE.org S: Supported F: drivers/scsi/libfc/ F: drivers/scsi/fcoe/ F: include/scsi/fc/ F: include/scsi/libfc.h F: include/scsi/libfcoe.h FILE LOCKING (flock() and fcntl()/lockf()) M: Matthew Wilcox L: linux-fsdevel@vger.kernel.org S: Maintained F: include/linux/fcntl.h F: include/linux/fs.h F: fs/fcntl.c F: fs/locks.c FILESYSTEMS (VFS and infrastructure) M: Alexander Viro L: linux-fsdevel@vger.kernel.org S: Maintained F: fs/* FINTEK F75375S HARDWARE MONITOR AND FAN CONTROLLER DRIVER M: Riku Voipio L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/f75375s.c F: include/linux/f75375s.h FIREWIRE AUDIO DRIVERS M: Clemens Ladisch L: alsa-devel@alsa-project.org (moderated for non-subscribers) T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: sound/firewire/ FIREWIRE SBP-2 TARGET M: Chris Boot L: linux-scsi@vger.kernel.org L: target-devel@vger.kernel.org L: linux1394-devel@lists.sourceforge.net T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master S: Maintained F: drivers/target/sbp/ FIREWIRE SUBSYSTEM M: Stefan Richter L: linux1394-devel@lists.sourceforge.net W: http://ieee1394.wiki.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394.git S: Maintained F: drivers/firewire/ F: include/linux/firewire*.h F: tools/firewire/ FIRMWARE LOADER (request_firmware) M: Ming Lei L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/firmware_class/ F: drivers/base/firmware*.c F: include/linux/firmware.h FLOPPY DRIVER M: Jiri Kosina T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy.git S: Odd fixes F: drivers/block/floppy.c FPU EMULATOR M: Bill Metzenthen W: http://floatingpoint.sourceforge.net/emulator/index.html S: Maintained F: arch/x86/math-emu/ FRAME RELAY DLCI/FRAD (Sangoma drivers too) L: netdev@vger.kernel.org S: Orphan F: drivers/net/wan/dlci.c F: drivers/net/wan/sdla.c FRAMEBUFFER LAYER M: Florian Tobias Schandinat L: linux-fbdev@vger.kernel.org W: http://linux-fbdev.sourceforge.net/ Q: http://patchwork.kernel.org/project/linux-fbdev/list/ T: git git://github.com/schandinat/linux-2.6.git fbdev-next S: Maintained F: Documentation/fb/ F: Documentation/devicetree/bindings/fb/ F: drivers/video/ F: include/video/ F: include/linux/fb.h FREESCALE DMA DRIVER M: Li Yang M: Zhang Wei L: linuxppc-dev@lists.ozlabs.org S: Maintained F: drivers/dma/fsldma.* FREESCALE I2C CPM DRIVER M: Jochen Friedrich L: linuxppc-dev@lists.ozlabs.org L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-cpm.c FREESCALE IMX / MXC FRAMEBUFFER DRIVER M: Sascha Hauer L: linux-fbdev@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/plat-mxc/include/mach/imxfb.h F: drivers/video/imxfb.c FREESCALE SOC FS_ENET DRIVER M: Pantelis Antoniou M: Vitaly Bordug L: linuxppc-dev@lists.ozlabs.org L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/freescale/fs_enet/ F: include/linux/fs_enet_pd.h FREESCALE QUICC ENGINE LIBRARY M: Timur Tabi L: linuxppc-dev@lists.ozlabs.org S: Supported F: arch/powerpc/sysdev/qe_lib/ F: arch/powerpc/include/asm/*qe.h FREESCALE USB PERIPHERAL DRIVERS M: Li Yang L: linux-usb@vger.kernel.org L: linuxppc-dev@lists.ozlabs.org S: Maintained F: drivers/usb/gadget/fsl* FREESCALE QUICC ENGINE UCC ETHERNET DRIVER M: Li Yang L: netdev@vger.kernel.org L: linuxppc-dev@lists.ozlabs.org S: Maintained F: drivers/net/ethernet/freescale/ucc_geth* FREESCALE QUICC ENGINE UCC UART DRIVER M: Timur Tabi L: linuxppc-dev@lists.ozlabs.org S: Supported F: drivers/tty/serial/ucc_uart.c FREESCALE SOC SOUND DRIVERS M: Timur Tabi L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linuxppc-dev@lists.ozlabs.org S: Supported F: sound/soc/fsl/fsl* F: sound/soc/fsl/mpc8610_hpcd.c FREEVXFS FILESYSTEM M: Christoph Hellwig W: ftp://ftp.openlinux.org/pub/people/hch/vxfs S: Maintained F: fs/freevxfs/ FREEZER M: Pavel Machek M: "Rafael J. Wysocki" L: linux-pm@vger.kernel.org S: Supported F: Documentation/power/freezing-of-tasks.txt F: include/linux/freezer.h F: kernel/freezer.c FRONTSWAP API M: Konrad Rzeszutek Wilk L: linux-kernel@vger.kernel.org S: Maintained F: mm/frontswap.c F: include/linux/frontswap.h FS-CACHE: LOCAL CACHING FOR NETWORK FILESYSTEMS M: David Howells L: linux-cachefs@redhat.com S: Supported F: Documentation/filesystems/caching/ F: fs/fscache/ F: include/linux/fscache*.h FUJITSU FR-V (FRV) PORT M: David Howells S: Maintained F: arch/frv/ FUJITSU LAPTOP EXTRAS M: Jonathan Woithe L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/fujitsu-laptop.c FUJITSU M-5MO LS CAMERA ISP DRIVER M: Kyungmin Park M: Heungjun Kim L: linux-media@vger.kernel.org S: Maintained F: drivers/media/video/m5mols/ F: include/media/m5mols.h FUJITSU TABLET EXTRAS M: Robert Gerlach L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/fujitsu-tablet.c FUSE: FILESYSTEM IN USERSPACE M: Miklos Szeredi L: fuse-devel@lists.sourceforge.net W: http://fuse.sourceforge.net/ S: Maintained F: fs/fuse/ F: include/linux/fuse.h FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit) M: Rik Faith L: linux-scsi@vger.kernel.org S: Odd Fixes (e.g., new signatures) F: drivers/scsi/fdomain.* GDT SCSI DISK ARRAY CONTROLLER DRIVER M: Achim Leubner L: linux-scsi@vger.kernel.org W: http://www.icp-vortex.com/ S: Supported F: drivers/scsi/gdt* GENERIC GPIO I2C DRIVER M: Haavard Skinnemoen S: Supported F: drivers/i2c/busses/i2c-gpio.c F: include/linux/i2c-gpio.h GENERIC GPIO I2C MULTIPLEXER DRIVER M: Peter Korsgaard L: linux-i2c@vger.kernel.org S: Supported F: drivers/i2c/muxes/i2c-mux-gpio.c F: include/linux/i2c-mux-gpio.h F: Documentation/i2c/muxes/i2c-mux-gpio GENERIC HDLC (WAN) DRIVERS M: Krzysztof Halasa W: http://www.kernel.org/pub/linux/utils/net/hdlc/ S: Maintained F: drivers/net/wan/c101.c F: drivers/net/wan/hd6457* F: drivers/net/wan/hdlc* F: drivers/net/wan/n2.c F: drivers/net/wan/pc300too.c F: drivers/net/wan/pci200syn.c F: drivers/net/wan/wanxl* GENERIC INCLUDE/ASM HEADER FILES M: Arnd Bergmann L: linux-arch@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/arnd/asm-generic.git S: Maintained F: include/asm-generic GENERIC UIO DRIVER FOR PCI DEVICES M: "Michael S. Tsirkin" L: kvm@vger.kernel.org S: Supported F: drivers/uio/uio_pci_generic.c GFS2 FILE SYSTEM M: Steven Whitehouse L: cluster-devel@redhat.com W: http://sources.redhat.com/cluster/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-3.0-fixes.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-3.0-nmw.git S: Supported F: Documentation/filesystems/gfs2*.txt F: fs/gfs2/ F: include/linux/gfs2_ondisk.h GIGASET ISDN DRIVERS M: Hansjoerg Lipp M: Tilman Schmidt L: gigaset307x-common@lists.sourceforge.net W: http://gigaset307x.sourceforge.net/ S: Maintained F: Documentation/isdn/README.gigaset F: drivers/isdn/gigaset/ F: include/linux/gigaset_dev.h GPIO SUBSYSTEM M: Grant Likely M: Linus Walleij S: Maintained T: git git://git.secretlab.ca/git/linux-2.6.git F: Documentation/gpio.txt F: drivers/gpio/ F: include/linux/gpio* F: include/asm-generic/gpio.h GRE DEMULTIPLEXER DRIVER M: Dmitry Kozlov L: netdev@vger.kernel.org S: Maintained F: net/ipv4/gre.c F: include/net/gre.h GRETH 10/100/1G Ethernet MAC device driver M: Kristoffer Glembo L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/aeroflex/ GSPCA FINEPIX SUBDRIVER M: Frank Zago L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/usb/gspca/finepix.c GSPCA GL860 SUBDRIVER M: Olivier Lorin L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/usb/gspca/gl860/ GSPCA M5602 SUBDRIVER M: Erik Andren L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/usb/gspca/m5602/ GSPCA PAC207 SONIXB SUBDRIVER M: Hans de Goede L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/usb/gspca/pac207.c GSPCA SN9C20X SUBDRIVER M: Brian Johnson L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/usb/gspca/sn9c20x.c GSPCA T613 SUBDRIVER M: Leandro Costantino L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/usb/gspca/t613.c GSPCA USB WEBCAM DRIVER M: Hans de Goede L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/usb/gspca/ HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER M: Frank Seidel L: platform-driver-x86@vger.kernel.org W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/ S: Maintained F: drivers/platform/x86/hdaps.c HWPOISON MEMORY FAILURE HANDLING M: Andi Kleen L: linux-mm@kvack.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-mce-2.6.git hwpoison S: Maintained F: mm/memory-failure.c F: mm/hwpoison-inject.c HYPERVISOR VIRTUAL CONSOLE DRIVER L: linuxppc-dev@lists.ozlabs.org S: Odd Fixes F: drivers/tty/hvc/ HARDWARE MONITORING M: Jean Delvare M: Guenter Roeck L: lm-sensors@lm-sensors.org W: http://www.lm-sensors.org/ T: quilt kernel.org/pub/linux/kernel/people/jdelvare/linux-2.6/jdelvare-hwmon/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git S: Maintained F: Documentation/hwmon/ F: drivers/hwmon/ F: include/linux/hwmon*.h HARDWARE RANDOM NUMBER GENERATOR CORE M: Matt Mackall M: Herbert Xu S: Odd fixes F: Documentation/hw_random.txt F: drivers/char/hw_random/ F: include/linux/hw_random.h HARDWARE SPINLOCK CORE M: Ohad Ben-Cohen S: Maintained F: Documentation/hwspinlock.txt F: drivers/hwspinlock/hwspinlock_* F: include/linux/hwspinlock.h HARMONY SOUND DRIVER L: linux-parisc@vger.kernel.org S: Maintained F: sound/parisc/harmony.* HEWLETT-PACKARD SMART2 RAID DRIVER M: Chirag Kantharia L: iss_storagedev@hp.com S: Maintained F: Documentation/blockdev/cpqarray.txt F: drivers/block/cpqarray.* HEWLETT-PACKARD SMART ARRAY RAID DRIVER (hpsa) M: "Stephen M. Cameron" L: iss_storagedev@hp.com S: Supported F: Documentation/scsi/hpsa.txt F: drivers/scsi/hpsa*.[ch] F: include/linux/cciss*.h HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss) M: Mike Miller L: iss_storagedev@hp.com S: Supported F: Documentation/blockdev/cciss.txt F: drivers/block/cciss* F: include/linux/cciss_ioctl.h HFS FILESYSTEM L: linux-fsdevel@vger.kernel.org S: Orphan F: Documentation/filesystems/hfs.txt F: fs/hfs/ HGA FRAMEBUFFER DRIVER M: Ferenc Bakonyi L: linux-nvidia@lists.surfsouth.com W: http://drama.obuda.kando.hu/~fero/cgi-bin/hgafb.shtml S: Maintained F: drivers/video/hgafb.c HIBERNATION (aka Software Suspend, aka swsusp) M: Pavel Machek M: "Rafael J. Wysocki" L: linux-pm@vger.kernel.org S: Supported F: arch/x86/power/ F: drivers/base/power/ F: kernel/power/ F: include/linux/suspend.h F: include/linux/freezer.h F: include/linux/pm.h F: arch/*/include/asm/suspend*.h HID CORE LAYER M: Jiri Kosina L: linux-input@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git S: Maintained F: drivers/hid/ F: include/linux/hid* HIGH-RESOLUTION TIMERS, CLOCKEVENTS, DYNTICKS M: Thomas Gleixner T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core S: Maintained F: Documentation/timers/ F: kernel/hrtimer.c F: kernel/time/clockevents.c F: kernel/time/tick*.* F: kernel/time/timer_*.c F: include/linux/clockchips.h F: include/linux/hrtimer.h HIGH-SPEED SCC DRIVER FOR AX.25 L: linux-hams@vger.kernel.org S: Orphan F: drivers/net/hamradio/dmascc.c F: drivers/net/hamradio/scc.c HIGHPOINT ROCKETRAID 3xxx RAID DRIVER M: HighPoint Linux Team W: http://www.highpoint-tech.com S: Supported F: Documentation/scsi/hptiop.txt F: drivers/scsi/hptiop.c HIPPI M: Jes Sorensen L: linux-hippi@sunsite.dk S: Maintained F: include/linux/hippidevice.h F: include/linux/if_hippi.h F: net/802/hippi.c F: drivers/net/hippi/ HOST AP DRIVER M: Jouni Malinen L: hostap@shmoo.com (subscribers-only) L: linux-wireless@vger.kernel.org W: http://hostap.epitest.fi/ S: Maintained F: drivers/net/wireless/hostap/ HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan F: drivers/platform/x86/tc1100-wmi.c HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series M: Jaroslav Kysela S: Maintained F: drivers/net/ethernet/hp/hp100.* HPET: High Precision Event Timers driver M: Clemens Ladisch S: Maintained F: Documentation/timers/hpet.txt F: drivers/char/hpet.c F: include/linux/hpet.h HPET: x86 M: "Venkatesh Pallipadi (Venki)" S: Maintained F: arch/x86/kernel/hpet.c F: arch/x86/include/asm/hpet.h HPFS FILESYSTEM M: Mikulas Patocka W: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi S: Maintained F: fs/hpfs/ HSO 3G MODEM DRIVER M: Jan Dumon W: http://www.pharscape.org S: Maintained F: drivers/net/usb/hso.c HTCPEN TOUCHSCREEN DRIVER M: Pau Oliva Fora L: linux-input@vger.kernel.org S: Maintained F: drivers/input/touchscreen/htcpen.c HUGETLB FILESYSTEM M: William Irwin S: Maintained F: fs/hugetlbfs/ Hyper-V CORE AND DRIVERS M: K. Y. Srinivasan M: Haiyang Zhang L: devel@linuxdriverproject.org S: Maintained F: drivers/hv/ F: drivers/hid/hid-hyperv.c F: drivers/net/hyperv/ F: drivers/staging/hv/ I2C/SMBUS STUB DRIVER M: "Mark M. Hoffman" L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-stub.c I2C SUBSYSTEM M: "Jean Delvare (PC drivers, core)" M: "Ben Dooks (embedded platforms)" M: "Wolfram Sang (embedded platforms)" L: linux-i2c@vger.kernel.org W: http://i2c.wiki.kernel.org/ T: quilt kernel.org/pub/linux/kernel/people/jdelvare/linux-2.6/jdelvare-i2c/ T: git git://git.pengutronix.de/git/wsa/linux.git S: Maintained F: Documentation/i2c/ F: drivers/i2c/ F: include/linux/i2c.h F: include/linux/i2c-*.h I2C-TINY-USB DRIVER M: Till Harbaum L: linux-i2c@vger.kernel.org W: http://www.harbaum.org/till/i2c_tiny_usb S: Maintained F: drivers/i2c/busses/i2c-tiny-usb.c i386 BOOT CODE M: "H. Peter Anvin" S: Maintained F: arch/x86/boot/ i386 SETUP CODE / CPU ERRATA WORKAROUNDS M: "H. Peter Anvin" T: git git://git.kernel.org/pub/scm/linux/kernel/git/hpa/linux-2.6-x86setup.git S: Maintained IA64 (Itanium) PLATFORM M: Tony Luck M: Fenghua Yu L: linux-ia64@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux.git S: Maintained F: arch/ia64/ IBM Power in-Nest Crypto Acceleration M: Kent Yoder L: linux-crypto@vger.kernel.org S: Supported F: drivers/crypto/nx/ IBM Power 842 compression accelerator M: Robert Jennings S: Supported F: drivers/crypto/nx/nx-842.c F: include/linux/nx842.h IBM Power Linux RAID adapter M: Brian King S: Supported F: drivers/scsi/ipr.* IBM Power Virtual Ethernet Device Driver M: Santiago Leon L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/ibm/ibmveth.* IBM Power Virtual SCSI/FC Device Drivers M: Robert Jennings L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/ibmvscsi/ X: drivers/scsi/ibmvscsi/ibmvstgt.c IBM ServeRAID RAID DRIVER P: Jack Hammer M: Dave Jeffery W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html S: Supported F: drivers/scsi/ips.* ICH LPC AND GPIO DRIVER M: Peter Tyser S: Maintained F: drivers/mfd/lpc_ich.c F: drivers/gpio/gpio-ich.c IDE SUBSYSTEM M: "David S. Miller" L: linux-ide@vger.kernel.org Q: http://patchwork.ozlabs.org/project/linux-ide/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide.git S: Maintained F: Documentation/ide/ F: drivers/ide/ F: include/linux/ide.h IDEAPAD LAPTOP EXTRAS DRIVER M: Ike Panhc L: platform-driver-x86@vger.kernel.org W: http://launchpad.net/ideapad-laptop S: Maintained F: drivers/platform/x86/ideapad-laptop.c IDE/ATAPI DRIVERS M: Borislav Petkov L: linux-ide@vger.kernel.org S: Maintained F: Documentation/cdrom/ide-cd F: drivers/ide/ide-cd* IDLE-I7300 M: Andy Henroid L: linux-pm@vger.kernel.org S: Supported F: drivers/idle/i7300_idle.c IEEE 802.15.4 SUBSYSTEM M: Alexander Smirnov M: Dmitry Eremin-Solenikov L: linux-zigbee-devel@lists.sourceforge.net (moderated for non-subscribers) W: http://apps.sourceforge.net/trac/linux-zigbee T: git git://git.kernel.org/pub/scm/linux/kernel/git/lowpan/lowpan.git S: Maintained F: net/ieee802154/ F: net/mac802154/ F: drivers/ieee802154/ IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron L: linux-iio@vger.kernel.org S: Maintained F: drivers/iio/ F: drivers/staging/iio/ IKANOS/ADI EAGLE ADSL USB DRIVER M: Matthieu Castet M: Stanislaw Gruszka S: Maintained F: drivers/usb/atm/ueagle-atm.c INTEGRITY MEASUREMENT ARCHITECTURE (IMA) M: Mimi Zohar S: Supported F: security/integrity/ima/ IMS TWINTURBO FRAMEBUFFER DRIVER L: linux-fbdev@vger.kernel.org S: Orphan F: drivers/video/imsttfb.c INFINIBAND SUBSYSTEM M: Roland Dreier M: Sean Hefty M: Hal Rosenstock L: linux-rdma@vger.kernel.org W: http://www.openfabrics.org/ Q: http://patchwork.kernel.org/project/linux-rdma/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/roland/infiniband.git S: Supported F: Documentation/infiniband/ F: drivers/infiniband/ F: include/linux/if_infiniband.h INOTIFY M: John McCutchan M: Robert Love M: Eric Paris S: Maintained F: Documentation/filesystems/inotify.txt F: fs/notify/inotify/ F: include/linux/inotify.h INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS M: Dmitry Torokhov M: Dmitry Torokhov L: linux-input@vger.kernel.org Q: http://patchwork.kernel.org/project/linux-input/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git S: Maintained F: drivers/input/ F: include/linux/input.h F: include/linux/input/ INPUT MULTITOUCH (MT) PROTOCOL M: Henrik Rydberg L: linux-input@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/rydberg/input-mt.git S: Maintained F: Documentation/input/multi-touch-protocol.txt F: drivers/input/input-mt.c K: \b(ABS|SYN)_MT_ INTEL C600 SERIES SAS CONTROLLER DRIVER M: Intel SCU Linux support M: Dave Jiang M: Ed Nadolski L: linux-scsi@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci.git S: Maintained F: drivers/scsi/isci/ F: firmware/isci/ INTEL IDLE DRIVER M: Len Brown L: linux-pm@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git S: Supported F: drivers/idle/intel_idle.c INTEL FRAMEBUFFER DRIVER (excluding 810 and 815) M: Maik Broemme L: linux-fbdev@vger.kernel.org S: Maintained F: Documentation/fb/intelfb.txt F: drivers/video/intelfb/ INTEL 810/815 FRAMEBUFFER DRIVER M: Antonino Daplas L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/i810/ INTEL MENLOW THERMAL DRIVER M: Sujith Thomas L: platform-driver-x86@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ S: Supported F: drivers/platform/x86/intel_menlow.c INTEL IA32 MICROCODE UPDATE SUPPORT M: Tigran Aivazian S: Maintained F: arch/x86/kernel/microcode_core.c F: arch/x86/kernel/microcode_intel.c INTEL I/OAT DMA DRIVER M: Dan Williams S: Maintained F: drivers/dma/ioat* INTEL IOMMU (VT-d) M: David Woodhouse L: iommu@lists.linux-foundation.org T: git git://git.infradead.org/iommu-2.6.git S: Supported F: drivers/iommu/intel-iommu.c F: include/linux/intel-iommu.h INTEL IOP-ADMA DMA DRIVER M: Dan Williams S: Odd fixes F: drivers/dma/iop-adma.c INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT M: Krzysztof Halasa S: Maintained F: arch/arm/mach-ixp4xx/include/mach/qmgr.h F: arch/arm/mach-ixp4xx/include/mach/npe.h F: arch/arm/mach-ixp4xx/ixp4xx_qmgr.c F: arch/arm/mach-ixp4xx/ixp4xx_npe.c F: drivers/net/ethernet/xscale/ixp4xx_eth.c F: drivers/net/wan/ixp4xx_hss.c INTEL IXP4XX RANDOM NUMBER GENERATOR SUPPORT M: Deepak Saxena S: Maintained F: drivers/char/hw_random/ixp4xx-rng.c INTEL ETHERNET DRIVERS (e100/e1000/e1000e/igb/igbvf/ixgb/ixgbe/ixgbevf) M: Jeff Kirsher M: Jesse Brandeburg M: Bruce Allan M: Carolyn Wyborny M: Don Skidmore M: Greg Rose M: Peter P Waskiewicz Jr M: Alex Duyck M: John Ronciak L: e1000-devel@lists.sourceforge.net W: http://e1000.sourceforge.net/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next.git S: Supported F: Documentation/networking/e100.txt F: Documentation/networking/e1000.txt F: Documentation/networking/e1000e.txt F: Documentation/networking/igb.txt F: Documentation/networking/igbvf.txt F: Documentation/networking/ixgb.txt F: Documentation/networking/ixgbe.txt F: Documentation/networking/ixgbevf.txt F: drivers/net/ethernet/intel/ INTEL MRST PMU DRIVER M: Len Brown L: linux-pm@vger.kernel.org S: Supported F: arch/x86/platform/mrst/pmu.* INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT M: Stanislav Yakovlev L: linux-wireless@vger.kernel.org S: Maintained F: Documentation/networking/README.ipw2100 F: Documentation/networking/README.ipw2200 F: drivers/net/wireless/ipw2x00/ INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT) M: Richard L Maliszewski M: Gang Wei M: Shane Wang L: tboot-devel@lists.sourceforge.net W: http://tboot.sourceforge.net T: hg http://tboot.hg.sourceforge.net:8000/hgroot/tboot/tboot S: Supported F: Documentation/intel_txt.txt F: include/linux/tboot.h F: arch/x86/kernel/tboot.c INTEL WIRELESS WIMAX CONNECTION 2400 M: Inaky Perez-Gonzalez M: linux-wimax@intel.com L: wimax@linuxwimax.org S: Supported W: http://linuxwimax.org F: Documentation/wimax/README.i2400m F: drivers/net/wimax/i2400m/ F: include/linux/wimax/i2400m.h INTEL WIRELESS 3945ABG/BG, 4965AGN (iwlegacy) M: Stanislaw Gruszka L: linux-wireless@vger.kernel.org S: Supported F: drivers/net/wireless/iwlegacy/ INTEL WIRELESS WIFI LINK (iwlwifi) M: Johannes Berg M: Wey-Yi Guy M: Intel Linux Wireless L: linux-wireless@vger.kernel.org W: http://intellinuxwireless.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git S: Supported F: drivers/net/wireless/iwlwifi/ INTEL MANAGEMENT ENGINE (mei) M: Tomas Winkler L: linux-kernel@vger.kernel.org S: Supported F: include/linux/mei.h F: drivers/misc/mei/* F: Documentation/mei/* IOC3 ETHERNET DRIVER M: Ralf Baechle L: linux-mips@linux-mips.org S: Maintained F: drivers/net/ethernet/sgi/ioc3-eth.c IOC3 SERIAL DRIVER M: Pat Gefre L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial/ioc3_serial.c IP MASQUERADING M: Juanjo Ciarlante S: Maintained F: net/ipv4/netfilter/ipt_MASQUERADE.c IP1000A 10/100/1000 GIGABIT ETHERNET DRIVER M: Francois Romieu M: Sorbica Shieh L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/icplus/ipg.* IPATH DRIVER M: Mike Marciniszyn L: linux-rdma@vger.kernel.org S: Maintained F: drivers/infiniband/hw/ipath/ IPMI SUBSYSTEM M: Corey Minyard L: openipmi-developer@lists.sourceforge.net (moderated for non-subscribers) W: http://openipmi.sourceforge.net/ S: Supported F: Documentation/IPMI.txt F: drivers/char/ipmi/ F: include/linux/ipmi* IPS SCSI RAID DRIVER M: Adaptec OEM Raid Solutions L: linux-scsi@vger.kernel.org W: http://www.adaptec.com/ S: Maintained F: drivers/scsi/ips* IPVS M: Wensong Zhang M: Simon Horman M: Julian Anastasov L: netdev@vger.kernel.org L: lvs-devel@vger.kernel.org S: Maintained F: Documentation/networking/ipvs-sysctl.txt F: include/net/ip_vs.h F: include/linux/ip_vs.h F: net/netfilter/ipvs/ IPWIRELESS DRIVER M: Jiri Kosina M: David Sterba S: Odd Fixes F: drivers/tty/ipwireless/ IPX NETWORK LAYER M: Arnaldo Carvalho de Melo L: netdev@vger.kernel.org S: Maintained F: include/linux/ipx.h F: include/net/ipx.h F: net/ipx/ IRDA SUBSYSTEM M: Samuel Ortiz L: irda-users@lists.sourceforge.net (subscribers-only) L: netdev@vger.kernel.org W: http://irda.sourceforge.net/ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/irda-2.6.git F: Documentation/networking/irda.txt F: drivers/net/irda/ F: include/net/irda/ F: net/irda/ IRQ SUBSYSTEM M: Thomas Gleixner S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core F: kernel/irq/ IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) M: Benjamin Herrenschmidt M: Grant Likely T: git git://git.secretlab.ca/git/linux-2.6.git irqdomain/next S: Maintained F: Documentation/IRQ-domain.txt F: include/linux/irqdomain.h F: kernel/irq/irqdomain.c ISAPNP M: Jaroslav Kysela S: Maintained F: Documentation/isapnp.txt F: drivers/pnp/isapnp/ F: include/linux/isapnp.h iSCSI BOOT FIRMWARE TABLE (iBFT) DRIVER M: Peter Jones M: Konrad Rzeszutek Wilk S: Maintained F: drivers/firmware/iscsi_ibft* ISCSI M: Mike Christie L: open-iscsi@googlegroups.com W: www.open-iscsi.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mnc/linux-2.6-iscsi.git S: Maintained F: drivers/scsi/*iscsi* F: include/scsi/*iscsi* ISDN SUBSYSTEM M: Karsten Keil L: isdn4linux@listserv.isdn4linux.de (subscribers-only) L: netdev@vger.kernel.org W: http://www.isdn4linux.de T: git git://git.kernel.org/pub/scm/linux/kernel/git/kkeil/isdn-2.6.git S: Maintained F: Documentation/isdn/ F: drivers/isdn/ F: include/linux/isdn.h F: include/linux/isdn/ ISDN SUBSYSTEM (Eicon active card driver) M: Armin Schindler L: isdn4linux@listserv.isdn4linux.de (subscribers-only) W: http://www.melware.de S: Maintained F: drivers/isdn/hardware/eicon/ IT87 HARDWARE MONITORING DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/it87 F: drivers/hwmon/it87.c IVTV VIDEO4LINUX DRIVER M: Andy Walls L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers) L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.ivtvdriver.org S: Maintained F: Documentation/video4linux/*.ivtv F: drivers/media/video/ivtv/ F: include/linux/ivtv* JC42.4 TEMPERATURE SENSOR DRIVER M: Guenter Roeck L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/jc42.c F: Documentation/hwmon/jc42 JFS FILESYSTEM M: Dave Kleikamp L: jfs-discussion@lists.sourceforge.net W: http://jfs.sourceforge.net/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git S: Maintained F: Documentation/filesystems/jfs.txt F: fs/jfs/ JME NETWORK DRIVER M: Guo-Fu Tseng L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/jme.* JOURNALLING FLASH FILE SYSTEM V2 (JFFS2) M: David Woodhouse L: linux-mtd@lists.infradead.org W: http://www.linux-mtd.infradead.org/doc/jffs2.html S: Maintained F: fs/jffs2/ F: include/linux/jffs2.h JOURNALLING LAYER FOR BLOCK DEVICES (JBD) M: Andrew Morton M: Jan Kara L: linux-ext4@vger.kernel.org S: Maintained F: fs/jbd/ F: include/linux/ext3_jbd.h F: include/linux/jbd.h JOURNALLING LAYER FOR BLOCK DEVICES (JBD2) M: "Theodore Ts'o" L: linux-ext4@vger.kernel.org S: Maintained F: fs/jbd2/ F: include/linux/jbd2.h JSM Neo PCI based serial card M: Lucas Tavares L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial/jsm/ K10TEMP HARDWARE MONITORING DRIVER M: Clemens Ladisch L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/k10temp F: drivers/hwmon/k10temp.c K8TEMP HARDWARE MONITORING DRIVER M: Rudolf Marek L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/k8temp F: drivers/hwmon/k8temp.c KCONFIG M: Michal Marek L: linux-kbuild@vger.kernel.org S: Odd Fixes F: Documentation/kbuild/kconfig-language.txt F: scripts/kconfig/ KDUMP M: Vivek Goyal M: Haren Myneni L: kexec@lists.infradead.org W: http://lse.sourceforge.net/kdump/ S: Maintained F: Documentation/kdump/ KERNEL AUTOMOUNTER v4 (AUTOFS4) M: Ian Kent L: autofs@vger.kernel.org S: Maintained F: fs/autofs4/ KERNEL BUILD + files below scripts/ (unless maintained elsewhere) M: Michal Marek T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild.git for-next T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild.git rc-fixes L: linux-kbuild@vger.kernel.org S: Maintained F: Documentation/kbuild/ F: Makefile F: scripts/Makefile.* F: scripts/basic/ F: scripts/mk* F: scripts/package/ KERNEL JANITORS L: kernel-janitors@vger.kernel.org W: http://kernelnewbies.org/KernelJanitors S: Odd Fixes KERNEL NFSD, SUNRPC, AND LOCKD SERVERS M: "J. Bruce Fields" L: linux-nfs@vger.kernel.org W: http://nfs.sourceforge.net/ S: Supported F: fs/nfsd/ F: include/linux/nfsd/ F: fs/lockd/ F: fs/nfs_common/ F: net/sunrpc/ F: include/linux/lockd/ F: include/linux/sunrpc/ KERNEL VIRTUAL MACHINE (KVM) M: Avi Kivity M: Marcelo Tosatti L: kvm@vger.kernel.org W: http://kvm.qumranet.com S: Supported F: Documentation/*/kvm.txt F: arch/*/kvm/ F: arch/*/include/asm/kvm* F: include/linux/kvm* F: virt/kvm/ KERNEL VIRTUAL MACHINE (KVM) FOR AMD-V M: Joerg Roedel L: kvm@vger.kernel.org W: http://kvm.qumranet.com S: Supported F: arch/x86/include/asm/svm.h F: arch/x86/kvm/svm.c KERNEL VIRTUAL MACHINE (KVM) FOR POWERPC M: Alexander Graf L: kvm-ppc@vger.kernel.org W: http://kvm.qumranet.com S: Supported F: arch/powerpc/include/asm/kvm* F: arch/powerpc/kvm/ KERNEL VIRTUAL MACHINE For Itanium (KVM/IA64) M: Xiantao Zhang L: kvm-ia64@vger.kernel.org W: http://kvm.qumranet.com S: Supported F: Documentation/ia64/kvm.txt F: arch/ia64/include/asm/kvm* F: arch/ia64/kvm/ KERNEL VIRTUAL MACHINE for s390 (KVM/s390) M: Christian Borntraeger M: Cornelia Huck M: linux390@de.ibm.com L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: Documentation/s390/kvm.txt F: arch/s390/include/asm/kvm* F: arch/s390/kvm/ F: drivers/s390/kvm/ KEXEC M: Eric Biederman W: http://kernel.org/pub/linux/utils/kernel/kexec/ L: kexec@lists.infradead.org S: Maintained F: include/linux/kexec.h F: kernel/kexec.c KEYS/KEYRINGS: M: David Howells L: keyrings@linux-nfs.org S: Maintained F: Documentation/security/keys.txt F: include/linux/key.h F: include/linux/key-type.h F: include/keys/ F: security/keys/ KEYS-TRUSTED M: David Safford M: Mimi Zohar L: linux-security-module@vger.kernel.org L: keyrings@linux-nfs.org S: Supported F: Documentation/security/keys-trusted-encrypted.txt F: include/keys/trusted-type.h F: security/keys/trusted.c F: security/keys/trusted.h KEYS-ENCRYPTED M: Mimi Zohar M: David Safford L: linux-security-module@vger.kernel.org L: keyrings@linux-nfs.org S: Supported F: Documentation/security/keys-trusted-encrypted.txt F: include/keys/encrypted-type.h F: security/keys/encrypted-keys/ KGDB / KDB /debug_core M: Jason Wessel W: http://kgdb.wiki.kernel.org/ L: kgdb-bugreport@lists.sourceforge.net S: Maintained F: Documentation/DocBook/kgdb.tmpl F: drivers/misc/kgdbts.c F: drivers/tty/serial/kgdboc.c F: include/linux/kdb.h F: include/linux/kgdb.h F: kernel/debug/ KMEMCHECK M: Vegard Nossum M: Pekka Enberg S: Maintained F: Documentation/kmemcheck.txt F: arch/x86/include/asm/kmemcheck.h F: arch/x86/mm/kmemcheck/ F: include/linux/kmemcheck.h F: mm/kmemcheck.c KMEMLEAK M: Catalin Marinas S: Maintained F: Documentation/kmemleak.txt F: include/linux/kmemleak.h F: mm/kmemleak.c F: mm/kmemleak-test.c KPROBES M: Ananth N Mavinakayanahalli M: Anil S Keshavamurthy M: "David S. Miller" M: Masami Hiramatsu S: Maintained F: Documentation/kprobes.txt F: include/linux/kprobes.h F: kernel/kprobes.c KS0108 LCD CONTROLLER DRIVER M: Miguel Ojeda Sandonis W: http://miguelojeda.es/auxdisplay.htm W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained F: Documentation/auxdisplay/ks0108 F: drivers/auxdisplay/ks0108.c F: include/linux/ks0108.h LAPB module L: linux-x25@vger.kernel.org S: Orphan F: Documentation/networking/lapb-module.txt F: include/*/lapb.h F: net/lapb/ LASI 53c700 driver for PARISC M: "James E.J. Bottomley" L: linux-scsi@vger.kernel.org S: Maintained F: Documentation/scsi/53c700.txt F: drivers/scsi/53c700* LED SUBSYSTEM M: Bryan Wu M: Richard Purdie L: linux-leds@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds.git S: Maintained F: drivers/leds/ F: include/linux/leds.h LEGACY EEPROM DRIVER M: Jean Delvare S: Maintained F: Documentation/misc-devices/eeprom F: drivers/misc/eeprom/eeprom.c LEGO USB Tower driver M: Juergen Stuber L: legousb-devel@lists.sourceforge.net W: http://legousb.sourceforge.net/ S: Maintained F: drivers/usb/misc/legousbtower.c LGUEST M: Rusty Russell L: lguest@lists.ozlabs.org W: http://lguest.ozlabs.org/ S: Odd Fixes F: arch/x86/include/asm/lguest*.h F: arch/x86/lguest/ F: drivers/lguest/ F: include/linux/lguest*.h F: tools/lguest/ LINUX FOR IBM pSERIES (RS/6000) M: Paul Mackerras W: http://www.ibm.com/linux/ltc/projects/ppc S: Supported F: arch/powerpc/boot/rs6000.h LINUX FOR POWERPC (32-BIT AND 64-BIT) M: Benjamin Herrenschmidt M: Paul Mackerras W: http://www.penguinppc.org/ L: linuxppc-dev@lists.ozlabs.org Q: http://patchwork.ozlabs.org/project/linuxppc-dev/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git S: Supported F: Documentation/powerpc/ F: arch/powerpc/ LINUX FOR POWER MACINTOSH M: Benjamin Herrenschmidt W: http://www.penguinppc.org/ L: linuxppc-dev@lists.ozlabs.org S: Maintained F: arch/powerpc/platforms/powermac/ F: drivers/macintosh/ LINUX FOR POWERPC EMBEDDED MPC5XXX M: Anatolij Gustschin L: linuxppc-dev@lists.ozlabs.org T: git git://git.denx.de/linux-2.6-agust.git S: Maintained F: arch/powerpc/platforms/512x/ F: arch/powerpc/platforms/52xx/ LINUX FOR POWERPC EMBEDDED PPC4XX M: Josh Boyer M: Matt Porter W: http://www.penguinppc.org/ L: linuxppc-dev@lists.ozlabs.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jwboyer/powerpc-4xx.git S: Maintained F: arch/powerpc/platforms/40x/ F: arch/powerpc/platforms/44x/ LINUX FOR POWERPC EMBEDDED XILINX VIRTEX M: Grant Likely W: http://wiki.secretlab.ca/index.php/Linux_on_Xilinx_Virtex L: linuxppc-dev@lists.ozlabs.org T: git git://git.secretlab.ca/git/linux-2.6.git S: Maintained F: arch/powerpc/*/*virtex* F: arch/powerpc/*/*/*virtex* LINUX FOR POWERPC EMBEDDED PPC8XX M: Vitaly Bordug M: Marcelo Tosatti W: http://www.penguinppc.org/ L: linuxppc-dev@lists.ozlabs.org S: Maintained F: arch/powerpc/platforms/8xx/ LINUX FOR POWERPC EMBEDDED PPC83XX AND PPC85XX M: Kumar Gala W: http://www.penguinppc.org/ L: linuxppc-dev@lists.ozlabs.org S: Maintained F: arch/powerpc/platforms/83xx/ F: arch/powerpc/platforms/85xx/ LINUX FOR POWERPC PA SEMI PWRFICIENT M: Olof Johansson L: linuxppc-dev@lists.ozlabs.org S: Maintained F: arch/powerpc/platforms/pasemi/ F: drivers/*/*pasemi* F: drivers/*/*/*pasemi* LINUX SECURITY MODULE (LSM) FRAMEWORK M: Chris Wright L: linux-security-module@vger.kernel.org S: Supported LIS3LV02D ACCELEROMETER DRIVER M: Eric Piel S: Maintained F: Documentation/misc-devices/lis3lv02d F: drivers/misc/lis3lv02d/ F: drivers/platform/x86/hp_accel.c LLC (802.2) M: Arnaldo Carvalho de Melo S: Maintained F: include/linux/llc.h F: include/net/llc* F: net/llc/ LM73 HARDWARE MONITOR DRIVER M: Guillaume Ligneul L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/lm73.c LM78 HARDWARE MONITOR DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/lm78 F: drivers/hwmon/lm78.c LM83 HARDWARE MONITOR DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/lm83 F: drivers/hwmon/lm83.c LM90 HARDWARE MONITOR DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/lm90 F: drivers/hwmon/lm90.c LOCKDEP AND LOCKSTAT M: Peter Zijlstra M: Ingo Molnar T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core/locking S: Maintained F: Documentation/lockdep*.txt F: Documentation/lockstat.txt F: include/linux/lockdep.h F: kernel/lockdep* LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP/Vista Dynamic Disks) M: "Richard Russon (FlatCap)" L: linux-ntfs-dev@lists.sourceforge.net W: http://www.linux-ntfs.org/content/view/19/37/ S: Maintained F: Documentation/ldm.txt F: block/partitions/ldm.* LogFS M: Joern Engel M: Prasad Joshi L: logfs@logfs.org W: logfs.org S: Maintained F: fs/logfs/ LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) M: Eric Moore M: support@lsi.com L: DL-MPTFusionLinux@lsi.com L: linux-scsi@vger.kernel.org W: http://www.lsilogic.com/support S: Supported F: drivers/message/fusion/ LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers M: Matthew Wilcox L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/sym53c8xx_2/ LTC4261 HARDWARE MONITOR DRIVER M: Guenter Roeck L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/ltc4261 F: drivers/hwmon/ltc4261.c LTP (Linux Test Project) M: Shubham Goyal M: Mike Frysinger M: Cyril Hrubis M: Caspar Zhang M: Wanlong Gao L: ltp-list@lists.sourceforge.net (subscribers-only) W: http://ltp.sourceforge.net/ T: git git://github.com/linux-test-project/ltp.git T: git git://ltp.git.sourceforge.net/gitroot/ltp/ltp-dev S: Maintained M32R ARCHITECTURE M: Hirokazu Takata L: linux-m32r@ml.linux-m32r.org (moderated for non-subscribers) L: linux-m32r-ja@ml.linux-m32r.org (in Japanese) W: http://www.linux-m32r.org/ S: Maintained F: arch/m32r/ M68K ARCHITECTURE M: Geert Uytterhoeven L: linux-m68k@lists.linux-m68k.org W: http://www.linux-m68k.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/linux-m68k.git S: Maintained F: arch/m68k/ F: drivers/zorro/ M68K ON APPLE MACINTOSH M: Joshua Thompson W: http://www.mac.linux-m68k.org/ L: linux-m68k@lists.linux-m68k.org S: Maintained F: arch/m68k/mac/ M68K ON HP9000/300 M: Philip Blundell W: http://www.tazenda.demon.co.uk/phil/linux-hp S: Maintained F: arch/m68k/hp300/ MAC80211 M: Johannes Berg L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git S: Maintained F: Documentation/networking/mac80211-injection.txt F: include/net/mac80211.h F: net/mac80211/ MAC80211 PID RATE CONTROL M: Stefano Brivio M: Mattias Nissler L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git S: Maintained F: net/mac80211/rc80211_pid* MACVLAN DRIVER M: Patrick McHardy L: netdev@vger.kernel.org S: Maintained F: drivers/net/macvlan.c F: include/linux/if_macvlan.h MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 M: Michael Kerrisk W: http://www.kernel.org/doc/man-pages L: linux-man@vger.kernel.org S: Maintained MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2) M: Mirko Lindner M: Stephen Hemminger L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/marvell/sk* MARVELL LIBERTAS WIRELESS DRIVER M: Dan Williams L: libertas-dev@lists.infradead.org S: Maintained F: drivers/net/wireless/libertas/ MARVELL MV643XX ETHERNET DRIVER M: Lennert Buytenhek L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/marvell/mv643xx_eth.* F: include/linux/mv643xx.h MARVELL MWIFIEX WIRELESS DRIVER M: Bing Zhao L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/mwifiex/ MARVELL MWL8K WIRELESS DRIVER M: Lennert Buytenhek L: linux-wireless@vger.kernel.org S: Odd Fixes F: drivers/net/wireless/mwl8k.c MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER M: Nicolas Pitre S: Odd Fixes F: drivers/mmc/host/mvsdio.* MATROX FRAMEBUFFER DRIVER L: linux-fbdev@vger.kernel.org S: Orphan F: drivers/video/matrox/matroxfb_* F: include/linux/matroxfb.h MAX16065 HARDWARE MONITOR DRIVER M: Guenter Roeck L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/max16065 F: drivers/hwmon/max16065.c MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER M: "Hans J. Koch" L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/max6650 F: drivers/hwmon/max6650.c MEDIA INPUT INFRASTRUCTURE (V4L/DVB) M: Mauro Carvalho Chehab P: LinuxTV.org Project L: linux-media@vger.kernel.org W: http://linuxtv.org Q: http://patchwork.kernel.org/project/linux-media/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: Documentation/dvb/ F: Documentation/video4linux/ F: Documentation/DocBook/media/ F: drivers/media/ F: drivers/staging/media/ F: include/media/ F: include/linux/dvb/ F: include/linux/videodev*.h MEGARAID SCSI DRIVERS M: Neela Syam Kolli L: linux-scsi@vger.kernel.org W: http://megaraid.lsilogic.com S: Maintained F: Documentation/scsi/megaraid.txt F: drivers/scsi/megaraid.* F: drivers/scsi/megaraid/ MEMORY MANAGEMENT L: linux-mm@kvack.org W: http://www.linux-mm.org S: Maintained F: include/linux/mm.h F: mm/ MEMORY RESOURCE CONTROLLER M: Johannes Weiner M: Michal Hocko M: Balbir Singh M: KAMEZAWA Hiroyuki L: cgroups@vger.kernel.org L: linux-mm@kvack.org S: Maintained F: mm/memcontrol.c F: mm/page_cgroup.c MEMORY TECHNOLOGY DEVICES (MTD) M: David Woodhouse L: linux-mtd@lists.infradead.org W: http://www.linux-mtd.infradead.org/ Q: http://patchwork.ozlabs.org/project/linux-mtd/list/ T: git git://git.infradead.org/mtd-2.6.git S: Maintained F: drivers/mtd/ F: include/linux/mtd/ F: include/mtd/ MICROBLAZE ARCHITECTURE M: Michal Simek L: microblaze-uclinux@itee.uq.edu.au (moderated for non-subscribers) W: http://www.monstr.eu/fdt/ T: git git://git.monstr.eu/linux-2.6-microblaze.git S: Supported F: arch/microblaze/ MICROTEK X6 SCANNER M: Oliver Neukum S: Maintained F: drivers/usb/image/microtek.* MIPS M: Ralf Baechle L: linux-mips@linux-mips.org W: http://www.linux-mips.org/ T: git git://git.linux-mips.org/pub/scm/ralf/linux.git Q: http://patchwork.linux-mips.org/project/linux-mips/list/ S: Supported F: Documentation/mips/ F: arch/mips/ MODULE SUPPORT M: Rusty Russell S: Maintained F: include/linux/module.h F: kernel/module.c MOTION EYE VAIO PICTUREBOOK CAMERA DRIVER W: http://popies.net/meye/ S: Orphan F: Documentation/video4linux/meye.txt F: drivers/media/video/meye.* F: include/linux/meye.h MOTOROLA IMX MMC/SD HOST CONTROLLER INTERFACE DRIVER M: Pavel Pisa L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/mmc/host/imxmmc.* MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD M: Jiri Slaby S: Maintained F: Documentation/serial/moxa-smartio F: drivers/tty/mxser.* MSI LAPTOP SUPPORT M: "Lee, Chun-Yi" L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/msi-laptop.c MSI WMI SUPPORT M: Anisse Astier L: platform-driver-x86@vger.kernel.org S: Supported F: drivers/platform/x86/msi-wmi.c MULTIFUNCTION DEVICES (MFD) M: Samuel Ortiz T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6.git S: Supported F: drivers/mfd/ MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM M: Chris Ball L: linux-mmc@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git S: Maintained F: drivers/mmc/ F: include/linux/mmc/ MULTIMEDIA CARD (MMC) ETC. OVER SPI S: Orphan F: drivers/mmc/host/mmc_spi.c F: include/linux/spi/mmc_spi.h MULTISOUND SOUND DRIVER M: Andrew Veliath S: Maintained F: Documentation/sound/oss/MultiSound F: sound/oss/msnd* MULTITECH MULTIPORT CARD (ISICOM) S: Orphan F: drivers/tty/isicom.c F: include/linux/isicom.h MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER M: Felipe Balbi L: linux-usb@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/musb/ MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE) M: Andrew Gallatin L: netdev@vger.kernel.org W: http://www.myri.com/scs/download-Myri10GE.html S: Supported F: drivers/net/ethernet/myricom/myri10ge/ NATSEMI ETHERNET DRIVER (DP8381x) S: Orphan F: drivers/net/ethernet/natsemi/natsemi.c NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER M: Daniel Mack S: Maintained L: alsa-devel@alsa-project.org W: http://www.native-instruments.com F: sound/usb/caiaq/ NATIVE LINUX KVM TOOL M: Pekka Enberg M: Sasha Levin M: Asias He L: kvm@vger.kernel.org S: Maintained F: tools/kvm/ NCP FILESYSTEM M: Petr Vandrovec S: Odd Fixes F: fs/ncpfs/ NCR DUAL 700 SCSI DRIVER (MICROCHANNEL) M: "James E.J. Bottomley" L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/NCR_D700.* NETEFFECT IWARP RNIC DRIVER (IW_NES) M: Faisal Latif L: linux-rdma@vger.kernel.org W: http://www.intel.com/Products/Server/Adapters/Server-Cluster/Server-Cluster-overview.htm S: Supported F: drivers/infiniband/hw/nes/ NETEM NETWORK EMULATOR M: Stephen Hemminger L: netem@lists.linux-foundation.org S: Maintained F: net/sched/sch_netem.c NETERION 10GbE DRIVERS (s2io/vxge) M: Jon Mason L: netdev@vger.kernel.org S: Supported F: Documentation/networking/s2io.txt F: Documentation/networking/vxge.txt F: drivers/net/ethernet/neterion/ NETFILTER/IPTABLES/IPCHAINS P: Harald Welte P: Jozsef Kadlecsik M: Pablo Neira Ayuso M: Patrick McHardy L: netfilter-devel@vger.kernel.org L: netfilter@vger.kernel.org L: coreteam@netfilter.org W: http://www.netfilter.org/ W: http://www.iptables.org/ T: git git://1984.lsi.us.es/nf T: git git://1984.lsi.us.es/nf-next S: Supported F: include/linux/netfilter* F: include/linux/netfilter/ F: include/net/netfilter/ F: net/*/netfilter.c F: net/*/netfilter/ F: net/netfilter/ NETLABEL M: Paul Moore W: http://netlabel.sf.net L: netdev@vger.kernel.org S: Maintained F: Documentation/netlabel/ F: include/net/netlabel.h F: net/netlabel/ NETROM NETWORK LAYER M: Ralf Baechle L: linux-hams@vger.kernel.org W: http://www.linux-ax25.org/ S: Maintained F: include/linux/netrom.h F: include/net/netrom.h F: net/netrom/ NETWORK BLOCK DEVICE (NBD) M: Paul Clements S: Maintained F: Documentation/blockdev/nbd.txt F: drivers/block/nbd.c F: include/linux/nbd.h NETWORK DROP MONITOR M: Neil Horman L: netdev@vger.kernel.org S: Maintained W: https://fedorahosted.org/dropwatch/ F: net/core/drop_monitor.c NETWORKING [GENERAL] M: "David S. Miller" L: netdev@vger.kernel.org W: http://www.linuxfoundation.org/en/Net W: http://patchwork.ozlabs.org/project/netdev/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git S: Maintained F: net/ F: include/net/ F: include/linux/in.h F: include/linux/net.h F: include/linux/netdevice.h NETWORKING [IPv4/IPv6] M: "David S. Miller" M: Alexey Kuznetsov M: James Morris M: Hideaki YOSHIFUJI M: Patrick McHardy L: netdev@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git S: Maintained F: net/ipv4/ F: net/ipv6/ F: include/net/ip* F: arch/x86/net/* NETWORKING [LABELED] (NetLabel, CIPSO, Labeled IPsec, SECMARK) M: Paul Moore L: netdev@vger.kernel.org S: Maintained NETWORKING [WIRELESS] M: "John W. Linville" L: linux-wireless@vger.kernel.org Q: http://patchwork.kernel.org/project/linux-wireless/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git S: Maintained F: net/mac80211/ F: net/rfkill/ F: net/wireless/ F: include/net/ieee80211* F: include/linux/wireless.h F: include/net/iw_handler.h F: drivers/net/wireless/ NETWORKING DRIVERS L: netdev@vger.kernel.org W: http://www.linuxfoundation.org/en/Net T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git S: Odd Fixes F: drivers/net/ F: include/linux/if_* F: include/linux/*device.h NETXEN (1/10) GbE SUPPORT M: Sony Chacko M: Rajesh Borundia L: netdev@vger.kernel.org W: http://www.qlogic.com S: Supported F: drivers/net/ethernet/qlogic/netxen/ NFC SUBSYSTEM M: Lauro Ramos Venancio M: Aloisio Almeida Jr M: Samuel Ortiz L: linux-wireless@vger.kernel.org S: Maintained F: net/nfc/ F: include/linux/nfc.h F: include/net/nfc/ F: drivers/nfc/ NFS, SUNRPC, AND LOCKD CLIENTS M: Trond Myklebust L: linux-nfs@vger.kernel.org W: http://client.linux-nfs.org T: git git://git.linux-nfs.org/pub/linux/nfs-2.6.git S: Maintained F: fs/lockd/ F: fs/nfs/ F: fs/nfs_common/ F: net/sunrpc/ F: include/linux/lockd/ F: include/linux/nfs* F: include/linux/sunrpc/ NI5010 NETWORK DRIVER M: Jan-Pascal van Best M: Andreas Mohr L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/racal/ni5010.* NILFS2 FILESYSTEM M: KONISHI Ryusuke L: linux-nilfs@vger.kernel.org W: http://www.nilfs.org/en/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2.git S: Supported F: Documentation/filesystems/nilfs2.txt F: fs/nilfs2/ F: include/linux/nilfs2_fs.h NINJA SCSI-3 / NINJA SCSI-32Bi (16bit/CardBus) PCMCIA SCSI HOST ADAPTER DRIVER M: YOKOTA Hiroshi W: http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/ S: Maintained F: Documentation/scsi/NinjaSCSI.txt F: drivers/scsi/pcmcia/nsp_* NINJA SCSI-32Bi/UDE PCI/CARDBUS SCSI HOST ADAPTER DRIVER M: GOTO Masanori M: YOKOTA Hiroshi W: http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/ S: Maintained F: Documentation/scsi/NinjaSCSI.txt F: drivers/scsi/nsp32* NTFS FILESYSTEM M: Anton Altaparmakov L: linux-ntfs-dev@lists.sourceforge.net W: http://www.tuxera.com/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs.git S: Supported F: Documentation/filesystems/ntfs.txt F: fs/ntfs/ NVIDIA (rivafb and nvidiafb) FRAMEBUFFER DRIVER M: Antonino Daplas L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/riva/ F: drivers/video/nvidia/ OMAP SUPPORT M: Tony Lindgren L: linux-omap@vger.kernel.org W: http://www.muru.com/linux/omap/ W: http://linux.omap.com/ Q: http://patchwork.kernel.org/project/linux-omap/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git S: Maintained F: arch/arm/*omap*/ F: drivers/i2c/busses/i2c-omap.c F: include/linux/i2c-omap.h OMAP CLOCK FRAMEWORK SUPPORT M: Paul Walmsley L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/*omap*/*clock* OMAP POWER MANAGEMENT SUPPORT M: Kevin Hilman L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/*omap*/*pm* F: drivers/cpufreq/omap-cpufreq.c OMAP POWERDOMAIN/CLOCKDOMAIN SOC ADAPTATION LAYER SUPPORT M: Rajendra Nayak M: Paul Walmsley L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/powerdomain2xxx_3xxx.c F: arch/arm/mach-omap2/powerdomain44xx.c F: arch/arm/mach-omap2/clockdomain2xxx_3xxx.c F: arch/arm/mach-omap2/clockdomain44xx.c OMAP AUDIO SUPPORT M: Peter Ujfalusi M: Jarkko Nikula L: alsa-devel@alsa-project.org (subscribers-only) L: linux-omap@vger.kernel.org S: Maintained F: sound/soc/omap/ OMAP FRAMEBUFFER SUPPORT M: Tomi Valkeinen L: linux-fbdev@vger.kernel.org L: linux-omap@vger.kernel.org S: Maintained F: drivers/video/omap/ OMAP DISPLAY SUBSYSTEM and FRAMEBUFFER SUPPORT (DSS2) M: Tomi Valkeinen L: linux-omap@vger.kernel.org L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/omap2/ F: Documentation/arm/OMAP/DSS OMAP HARDWARE SPINLOCK SUPPORT M: Ohad Ben-Cohen L: linux-omap@vger.kernel.org S: Maintained F: drivers/hwspinlock/omap_hwspinlock.c F: arch/arm/mach-omap2/hwspinlock.c OMAP MMC SUPPORT M: Jarkko Lavinen L: linux-omap@vger.kernel.org S: Maintained F: drivers/mmc/host/omap.c OMAP HS MMC SUPPORT M: Venkatraman S L: linux-mmc@vger.kernel.org L: linux-omap@vger.kernel.org S: Maintained F: drivers/mmc/host/omap_hsmmc.c OMAP RANDOM NUMBER GENERATOR SUPPORT M: Deepak Saxena S: Maintained F: drivers/char/hw_random/omap-rng.c OMAP HWMOD SUPPORT M: Benoît Cousson M: Paul Walmsley L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/omap_hwmod.c F: arch/arm/plat-omap/include/plat/omap_hwmod.h OMAP HWMOD DATA FOR OMAP4-BASED DEVICES M: Benoît Cousson L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c OMAP IMAGE SIGNAL PROCESSOR (ISP) M: Laurent Pinchart L: linux-media@vger.kernel.org S: Maintained F: drivers/media/video/omap3isp/* OMAP USB SUPPORT M: Felipe Balbi L: linux-usb@vger.kernel.org L: linux-omap@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/*/*omap* F: arch/arm/*omap*/usb* OMAP GPIO DRIVER M: Santosh Shilimkar M: Kevin Hilman L: linux-omap@vger.kernel.org S: Maintained F: drivers/gpio/gpio-omap.c OMFS FILESYSTEM M: Bob Copeland L: linux-karma-devel@lists.sourceforge.net S: Maintained F: Documentation/filesystems/omfs.txt F: fs/omfs/ OMNIKEY CARDMAN 4000 DRIVER M: Harald Welte S: Maintained F: drivers/char/pcmcia/cm4000_cs.c F: include/linux/cm4000_cs.h OMNIKEY CARDMAN 4040 DRIVER M: Harald Welte S: Maintained F: drivers/char/pcmcia/cm4040_cs.* OMNIVISION OV7670 SENSOR DRIVER M: Jonathan Corbet L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: drivers/media/video/ov7670.c ONENAND FLASH DRIVER M: Kyungmin Park L: linux-mtd@lists.infradead.org S: Maintained F: drivers/mtd/onenand/ F: include/linux/mtd/onenand*.h ONSTREAM SCSI TAPE DRIVER M: Willem Riede L: osst-users@lists.sourceforge.net L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/osst* F: drivers/scsi/st* OPENCORES I2C BUS DRIVER M: Peter Korsgaard L: linux-i2c@vger.kernel.org S: Maintained F: Documentation/i2c/busses/i2c-ocores F: drivers/i2c/busses/i2c-ocores.c OPEN FIRMWARE AND FLATTENED DEVICE TREE M: Grant Likely M: Rob Herring L: devicetree-discuss@lists.ozlabs.org (moderated for non-subscribers) W: http://fdt.secretlab.ca T: git git://git.secretlab.ca/git/linux-2.6.git S: Maintained F: Documentation/devicetree F: drivers/of F: include/linux/of*.h K: of_get_property K: of_match_table OPENRISC ARCHITECTURE M: Jonas Bonn W: http://openrisc.net L: linux@lists.openrisc.net (moderated for non-subscribers) S: Maintained T: git git://openrisc.net/~jonas/linux F: arch/openrisc OPENVSWITCH M: Jesse Gross L: dev@openvswitch.org W: http://openvswitch.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jesse/openvswitch.git S: Maintained F: net/openvswitch/ OPL4 DRIVER M: Clemens Ladisch L: alsa-devel@alsa-project.org (moderated for non-subscribers) T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: sound/drivers/opl4/ OPROFILE M: Robert Richter L: oprofile-list@lists.sf.net S: Maintained F: arch/*/include/asm/oprofile*.h F: arch/*/oprofile/ F: drivers/oprofile/ F: include/linux/oprofile.h ORACLE CLUSTER FILESYSTEM 2 (OCFS2) M: Mark Fasheh M: Joel Becker L: ocfs2-devel@oss.oracle.com (moderated for non-subscribers) W: http://oss.oracle.com/projects/ocfs2/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jlbec/ocfs2.git S: Supported F: Documentation/filesystems/ocfs2.txt F: Documentation/filesystems/dlmfs.txt F: fs/ocfs2/ ORINOCO DRIVER L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/orinoco W: http://www.nongnu.org/orinoco/ S: Orphan F: drivers/net/wireless/orinoco/ OSD LIBRARY and FILESYSTEM M: Boaz Harrosh M: Benny Halevy L: osd-dev@open-osd.org W: http://open-osd.org T: git git://git.open-osd.org/open-osd.git S: Maintained F: drivers/scsi/osd/ F: include/scsi/osd_* F: fs/exofs/ P54 WIRELESS DRIVER M: Christian Lamparter L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/p54 S: Maintained F: drivers/net/wireless/p54/ PA SEMI ETHERNET DRIVER M: Olof Johansson L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/pasemi/* PA SEMI SMBUS DRIVER M: Olof Johansson L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-pasemi.c PADATA PARALLEL EXECUTION MECHANISM M: Steffen Klassert L: linux-crypto@vger.kernel.org S: Maintained F: kernel/padata.c F: include/linux/padata.h F: Documentation/padata.txt PANASONIC LAPTOP ACPI EXTRAS DRIVER M: Harald Welte L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/panasonic-laptop.c PANASONIC MN10300/AM33/AM34 PORT M: David Howells M: Koichi Yasutake L: linux-am33-list@redhat.com (moderated for non-subscribers) W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/ S: Maintained F: Documentation/mn10300/ F: arch/mn10300/ PARALLEL PORT SUPPORT L: linux-parport@lists.infradead.org (subscribers-only) S: Orphan F: drivers/parport/ F: include/linux/parport*.h F: drivers/char/ppdev.c F: include/linux/ppdev.h PARAVIRT_OPS INTERFACE M: Jeremy Fitzhardinge M: Chris Wright M: Alok Kataria M: Rusty Russell L: virtualization@lists.linux-foundation.org S: Supported F: Documentation/ia64/paravirt_ops.txt F: arch/*/kernel/paravirt* F: arch/*/include/asm/paravirt.h PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES M: Tim Waugh L: linux-parport@lists.infradead.org (subscribers-only) W: http://www.torque.net/linux-pp.html S: Maintained F: Documentation/blockdev/paride.txt F: drivers/block/paride/ PARISC ARCHITECTURE M: "James E.J. Bottomley" M: Helge Deller L: linux-parisc@vger.kernel.org W: http://www.parisc-linux.org/ Q: http://patchwork.kernel.org/project/linux-parisc/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/parisc-2.6.git S: Maintained F: arch/parisc/ F: drivers/parisc/ PC87360 HARDWARE MONITORING DRIVER M: Jim Cromie L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/pc87360 F: drivers/hwmon/pc87360.c PC8736x GPIO DRIVER M: Jim Cromie S: Maintained F: drivers/char/pc8736x_gpio.c PC87427 HARDWARE MONITORING DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/pc87427 F: drivers/hwmon/pc87427.c PCA9532 LED DRIVER M: Riku Voipio S: Maintained F: drivers/leds/leds-pca9532.c F: include/linux/leds-pca9532.h PCA9541 I2C BUS MASTER SELECTOR DRIVER M: Guenter Roeck L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/muxes/i2c-mux-pca9541.c PCA9564/PCA9665 I2C BUS DRIVER M: Wolfram Sang L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/algos/i2c-algo-pca.c F: drivers/i2c/busses/i2c-pca-* F: include/linux/i2c-algo-pca.h F: include/linux/i2c-pca-platform.h PCDP - PRIMARY CONSOLE AND DEBUG PORT M: Khalid Aziz S: Maintained F: drivers/firmware/pcdp.* PCI ERROR RECOVERY M: Linas Vepstas L: linux-pci@vger.kernel.org S: Supported F: Documentation/PCI/pci-error-recovery.txt F: Documentation/powerpc/eeh-pci-error-recovery.txt PCI SUBSYSTEM M: Bjorn Helgaas L: linux-pci@vger.kernel.org Q: http://patchwork.ozlabs.org/project/linux-pci/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git S: Supported F: Documentation/PCI/ F: drivers/pci/ F: include/linux/pci* PCMCIA SUBSYSTEM P: Linux PCMCIA Team L: linux-pcmcia@lists.infradead.org W: http://lists.infradead.org/mailman/listinfo/linux-pcmcia T: git git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia-2.6.git S: Maintained F: Documentation/pcmcia/ F: drivers/pcmcia/ F: include/pcmcia/ PCNET32 NETWORK DRIVER M: Don Fry L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/amd/pcnet32.c PCRYPT PARALLEL CRYPTO ENGINE M: Steffen Klassert L: linux-crypto@vger.kernel.org S: Maintained F: crypto/pcrypt.c F: include/crypto/pcrypt.h PER-CPU MEMORY ALLOCATOR M: Tejun Heo M: Christoph Lameter T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu.git S: Maintained F: include/linux/percpu*.h F: mm/percpu*.c F: arch/*/include/asm/percpu.h PER-TASK DELAY ACCOUNTING M: Balbir Singh S: Maintained F: include/linux/delayacct.h F: kernel/delayacct.c PERFORMANCE EVENTS SUBSYSTEM M: Peter Zijlstra M: Paul Mackerras M: Ingo Molnar M: Arnaldo Carvalho de Melo T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core S: Supported F: kernel/events/* F: include/linux/perf_event.h F: arch/*/kernel/perf_event*.c F: arch/*/kernel/*/perf_event*.c F: arch/*/kernel/*/*/perf_event*.c F: arch/*/include/asm/perf_event.h F: arch/*/lib/perf_event*.c F: arch/*/kernel/perf_callchain.c F: tools/perf/ PERSONALITY HANDLING M: Christoph Hellwig L: linux-abi-devel@lists.sourceforge.net S: Maintained F: include/linux/personality.h PHONET PROTOCOL M: Remi Denis-Courmont S: Supported F: Documentation/networking/phonet.txt F: include/linux/phonet.h F: include/net/phonet/ F: net/phonet/ PHRAM MTD DRIVER M: Joern Engel L: linux-mtd@lists.infradead.org S: Maintained F: drivers/mtd/devices/phram.c PICOLCD HID DRIVER M: Bruno Prémont L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/hid-picolcd* PICOXCELL SUPPORT M: Jamie Iles L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) T: git git://github.com/jamieiles/linux-2.6-ji.git S: Supported F: arch/arm/mach-picoxcell F: drivers/*/picoxcell* F: drivers/*/*/picoxcell* PIN CONTROL SUBSYSTEM M: Linus Walleij S: Maintained F: drivers/pinctrl/ F: include/linux/pinctrl/ PIN CONTROLLER - ST SPEAR M: Viresh Kumar L: spear-devel@list.st.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.st.com/spear S: Maintained F: drivers/pinctrl/spear/ PKTCDVD DRIVER M: Jiri Kosina S: Maintained F: drivers/block/pktcdvd.c F: include/linux/pktcdvd.h PKUNITY SOC DRIVERS M: Guan Xuetao W: http://mprc.pku.edu.cn/~guanxuetao/linux S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/epip/linux-2.6-unicore32.git F: drivers/input/serio/i8042-unicore32io.h F: drivers/i2c/busses/i2c-puv3.c F: drivers/video/fb-puv3.c F: drivers/rtc/rtc-puv3.c PMBUS HARDWARE MONITORING DRIVERS M: Guenter Roeck L: lm-sensors@lm-sensors.org W: http://www.lm-sensors.org/ W: http://www.roeck-us.net/linux/drivers/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git S: Maintained F: Documentation/hwmon/pmbus F: drivers/hwmon/pmbus/ F: include/linux/i2c/pmbus.h PMC SIERRA MaxRAID DRIVER M: Anil Ravindranath L: linux-scsi@vger.kernel.org W: http://www.pmc-sierra.com/ S: Supported F: drivers/scsi/pmcraid.* PMC SIERRA PM8001 DRIVER M: jack_wang@usish.com M: lindar_liu@usish.com L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/pm8001/ POSIX CLOCKS and TIMERS M: Thomas Gleixner T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core S: Supported F: fs/timerfd.c F: include/linux/timer* F: kernel/*timer* POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS M: Anton Vorontsov M: David Woodhouse T: git git://git.infradead.org/battery-2.6.git S: Maintained F: include/linux/power_supply.h F: drivers/power/ PNP SUPPORT M: Adam Belay M: Bjorn Helgaas S: Maintained F: drivers/pnp/ PNXxxxx I2C DRIVER M: Vitaly Wool L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-pnx.c PPP PROTOCOL DRIVERS AND COMPRESSORS M: Paul Mackerras L: linux-ppp@vger.kernel.org S: Maintained F: drivers/net/ppp/ppp_* PPP OVER ATM (RFC 2364) M: Mitchell Blank Jr S: Maintained F: net/atm/pppoatm.c F: include/linux/atmppp.h PPP OVER ETHERNET M: Michal Ostrowski S: Maintained F: drivers/net/ppp/pppoe.c F: drivers/net/ppp/pppox.c PPP OVER L2TP M: James Chapman S: Maintained F: net/l2tp/l2tp_ppp.c F: include/linux/if_pppol2tp.h PPS SUPPORT M: Rodolfo Giometti W: http://wiki.enneenne.com/index.php/LinuxPPS_support L: linuxpps@ml.enneenne.com (subscribers-only) S: Maintained F: Documentation/pps/ F: drivers/pps/ F: include/linux/pps*.h PPTP DRIVER M: Dmitry Kozlov L: netdev@vger.kernel.org S: Maintained F: drivers/net/ppp/pptp.c W: http://sourceforge.net/projects/accel-pptp PREEMPTIBLE KERNEL M: Robert Love L: kpreempt-tech@lists.sourceforge.net W: ftp://ftp.kernel.org/pub/linux/kernel/people/rml/preempt-kernel S: Supported F: Documentation/preempt-locking.txt F: include/linux/preempt.h PRISM54 WIRELESS DRIVER M: "Luis R. Rodriguez" L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/p54 S: Obsolete F: drivers/net/wireless/prism54/ PROMISE SATA TX2/TX4 CONTROLLER LIBATA DRIVER M: Mikael Pettersson L: linux-ide@vger.kernel.org S: Maintained F: drivers/ata/sata_promise.* PS3 NETWORK SUPPORT M: Geoff Levand L: netdev@vger.kernel.org L: cbe-oss-dev@lists.ozlabs.org S: Maintained F: drivers/net/ethernet/toshiba/ps3_gelic_net.* PS3 PLATFORM SUPPORT M: Geoff Levand L: linuxppc-dev@lists.ozlabs.org L: cbe-oss-dev@lists.ozlabs.org S: Maintained F: arch/powerpc/boot/ps3* F: arch/powerpc/include/asm/lv1call.h F: arch/powerpc/include/asm/ps3*.h F: arch/powerpc/platforms/ps3/ F: drivers/*/ps3* F: drivers/ps3/ F: drivers/rtc/rtc-ps3.c F: drivers/usb/host/*ps3.c F: sound/ppc/snd_ps3* PS3VRAM DRIVER M: Jim Paris L: cbe-oss-dev@lists.ozlabs.org S: Maintained F: drivers/block/ps3vram.c PSTORE FILESYSTEM M: Anton Vorontsov M: Colin Cross M: Kees Cook M: Tony Luck S: Maintained T: git git://git.infradead.org/users/cbou/linux-pstore.git F: fs/pstore/ F: include/linux/pstore* F: drivers/firmware/efivars.c F: drivers/acpi/apei/erst.c PTP HARDWARE CLOCK SUPPORT M: Richard Cochran S: Maintained W: http://linuxptp.sourceforge.net/ F: Documentation/ABI/testing/sysfs-ptp F: Documentation/ptp/* F: drivers/net/ethernet/freescale/gianfar_ptp.c F: drivers/net/phy/dp83640* F: drivers/ptp/* F: include/linux/ptp_cl* PTRACE SUPPORT M: Roland McGrath M: Oleg Nesterov S: Maintained F: include/asm-generic/syscall.h F: include/linux/ptrace.h F: include/linux/regset.h F: include/linux/tracehook.h F: kernel/ptrace.c PVRUSB2 VIDEO4LINUX DRIVER M: Mike Isely L: pvrusb2@isely.net (subscribers-only) L: linux-media@vger.kernel.org W: http://www.isely.net/pvrusb2/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: Documentation/video4linux/README.pvrusb2 F: drivers/media/usb/pvrusb2/ PWM SUBSYSTEM M: Thierry Reding L: linux-kernel@vger.kernel.org S: Maintained W: http://gitorious.org/linux-pwm T: git git://gitorious.org/linux-pwm/linux-pwm.git F: Documentation/pwm.txt F: Documentation/devicetree/bindings/pwm/ F: include/linux/pwm.h F: include/linux/of_pwm.h F: drivers/pwm/ PXA2xx/PXA3xx SUPPORT M: Eric Miao M: Russell King M: Haojian Zhuang L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) T: git git://github.com/hzhuang1/linux.git T: git git://git.linaro.org/people/ycmiao/pxa-linux.git S: Maintained F: arch/arm/mach-pxa/ F: drivers/pcmcia/pxa2xx* F: drivers/spi/spi-pxa2xx* F: drivers/usb/gadget/pxa2* F: include/sound/pxa2xx-lib.h F: sound/arm/pxa* F: sound/soc/pxa MMP SUPPORT M: Eric Miao M: Haojian Zhuang L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) T: git git://github.com/hzhuang1/linux.git T: git git://git.linaro.org/people/ycmiao/pxa-linux.git S: Maintained F: arch/arm/mach-mmp/ PXA MMCI DRIVER S: Orphan PXA RTC DRIVER M: Robert Jarzmik L: rtc-linux@googlegroups.com S: Maintained QIB DRIVER M: Mike Marciniszyn L: linux-rdma@vger.kernel.org S: Supported F: drivers/infiniband/hw/qib/ QLOGIC QLA1280 SCSI DRIVER M: Michael Reed L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/qla1280.[ch] QLOGIC QLA2XXX FC-SCSI DRIVER M: Andrew Vasquez M: linux-driver@qlogic.com L: linux-scsi@vger.kernel.org S: Supported F: Documentation/scsi/LICENSE.qla2xxx F: drivers/scsi/qla2xxx/ QLOGIC QLA4XXX iSCSI DRIVER M: Ravi Anand M: Vikas Chaudhary M: iscsi-driver@qlogic.com L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/qla4xxx/ QLOGIC QLA3XXX NETWORK DRIVER M: Jitendra Kalsaria M: Ron Mercer M: linux-driver@qlogic.com L: netdev@vger.kernel.org S: Supported F: Documentation/networking/LICENSE.qla3xxx F: drivers/net/ethernet/qlogic/qla3xxx.* QLOGIC QLCNIC (1/10)Gb ETHERNET DRIVER M: Jitendra Kalsaria M: Sony Chacko M: linux-driver@qlogic.com L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/qlogic/qlcnic/ QLOGIC QLGE 10Gb ETHERNET DRIVER M: Jitendra Kalsaria M: Ron Mercer M: linux-driver@qlogic.com L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/qlogic/qlge/ QNX4 FILESYSTEM M: Anders Larsen W: http://www.alarsen.net/linux/qnx4fs/ S: Maintained F: fs/qnx4/ F: include/linux/qnx4_fs.h F: include/linux/qnxtypes.h QUALCOMM HEXAGON ARCHITECTURE M: Richard Kuo L: linux-hexagon@vger.kernel.org S: Supported F: arch/hexagon/ RADOS BLOCK DEVICE (RBD) M: Yehuda Sadeh M: Sage Weil M: Alex Elder M: ceph-devel@vger.kernel.org W: http://ceph.com/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git S: Supported F: drivers/block/rbd.c F: drivers/block/rbd_types.h RADEON FRAMEBUFFER DISPLAY DRIVER M: Benjamin Herrenschmidt L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/aty/radeon* F: include/linux/radeonfb.h RAGE128 FRAMEBUFFER DISPLAY DRIVER M: Paul Mackerras L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/aty/aty128fb.c RALINK RT2X00 WIRELESS LAN DRIVER P: rt2x00 project M: Ivo van Doorn M: Gertjan van Wingerde M: Helmut Schaa L: linux-wireless@vger.kernel.org L: users@rt2x00.serialmonkey.com (moderated for non-subscribers) W: http://rt2x00.serialmonkey.com/ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/ivd/rt2x00.git F: drivers/net/wireless/rt2x00/ RAMDISK RAM BLOCK DEVICE DRIVER M: Nick Piggin S: Maintained F: Documentation/blockdev/ramdisk.txt F: drivers/block/brd.c RANDOM NUMBER DRIVER M: Theodore Ts'o" S: Maintained F: drivers/char/random.c RAPIDIO SUBSYSTEM M: Matt Porter M: Alexandre Bounine S: Maintained F: drivers/rapidio/ RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER L: linux-wireless@vger.kernel.org S: Orphan F: drivers/net/wireless/ray* RCUTORTURE MODULE M: Josh Triplett M: "Paul E. McKenney" S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git F: Documentation/RCU/torture.txt F: kernel/rcutorture.c RDC R-321X SoC M: Florian Fainelli S: Maintained RDC R6040 FAST ETHERNET DRIVER M: Florian Fainelli L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/rdc/r6040.c RDS - RELIABLE DATAGRAM SOCKETS M: Venkat Venkatsubra L: rds-devel@oss.oracle.com (moderated for non-subscribers) S: Supported F: net/rds/ READ-COPY UPDATE (RCU) M: Dipankar Sarma M: "Paul E. McKenney" W: http://www.rdrop.com/users/paulmck/RCU/ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git F: Documentation/RCU/ X: Documentation/RCU/torture.txt F: include/linux/rcu* F: kernel/rcu* X: kernel/rcutorture.c REAL TIME CLOCK (RTC) SUBSYSTEM M: Alessandro Zummo L: rtc-linux@googlegroups.com Q: http://patchwork.ozlabs.org/project/rtc-linux/list/ S: Maintained F: Documentation/rtc.txt F: drivers/rtc/ F: include/linux/rtc.h REISERFS FILE SYSTEM L: reiserfs-devel@vger.kernel.org S: Supported F: fs/reiserfs/ REGISTER MAP ABSTRACTION M: Mark Brown T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git S: Supported F: drivers/base/regmap/ F: include/linux/regmap.h REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM M: Ohad Ben-Cohen T: git git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc.git S: Maintained F: drivers/remoteproc/ F: Documentation/remoteproc.txt F: include/linux/remoteproc.h RFKILL M: Johannes Berg L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git S: Maintained F: Documentation/rfkill.txt F: net/rfkill/ RICOH SMARTMEDIA/XD DRIVER M: Maxim Levitsky S: Maintained F: drivers/mtd/nand/r852.c F: drivers/mtd/nand/r852.h RICOH R5C592 MEMORYSTICK DRIVER M: Maxim Levitsky S: Maintained F: drivers/memstick/host/r592.* ROCKETPORT DRIVER P: Comtrol Corp. W: http://www.comtrol.com S: Maintained F: Documentation/serial/rocket.txt F: drivers/tty/rocket* ROSE NETWORK LAYER M: Ralf Baechle L: linux-hams@vger.kernel.org W: http://www.linux-ax25.org/ S: Maintained F: include/linux/rose.h F: include/net/rose.h F: net/rose/ RTL8180 WIRELESS DRIVER M: "John W. Linville" L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained F: drivers/net/wireless/rtl818x/rtl8180/ RTL8187 WIRELESS DRIVER M: Herton Ronaldo Krzesinski M: Hin-Tak Leung M: Larry Finger L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained F: drivers/net/wireless/rtl818x/rtl8187/ RTL8192CE WIRELESS DRIVER M: Larry Finger M: Chaoming Li L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained F: drivers/net/wireless/rtlwifi/ F: drivers/net/wireless/rtlwifi/rtl8192ce/ S3 SAVAGE FRAMEBUFFER DRIVER M: Antonino Daplas L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/savage/ S390 M: Martin Schwidefsky M: Heiko Carstens M: linux390@de.ibm.com L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: arch/s390/ F: drivers/s390/ F: block/partitions/ibm.c F: Documentation/s390/ F: Documentation/DocBook/s390* S390 NETWORK DRIVERS M: Ursula Braun M: Frank Blaschka M: linux390@de.ibm.com L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: drivers/s390/net/ S390 ZCRYPT DRIVER M: Holger Dengler M: linux390@de.ibm.com L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: drivers/s390/crypto/ S390 ZFCP DRIVER M: Steffen Maier M: linux390@de.ibm.com L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: drivers/s390/scsi/zfcp_* S390 IUCV NETWORK LAYER M: Ursula Braun M: linux390@de.ibm.com L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: drivers/s390/net/*iucv* F: include/net/iucv/ F: net/iucv/ S3C24XX SD/MMC Driver M: Ben Dooks L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/mmc/host/s3cmci.* SAA7146 VIDEO4LINUX-2 DRIVER M: Michael Hunold L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.mihu.de/linux/saa7146 S: Maintained F: drivers/media/common/saa7146* F: drivers/media/video/*7146* F: include/media/*7146* SAMSUNG LAPTOP DRIVER M: Corentin Chary L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/samsung-laptop.c SAMSUNG AUDIO (ASoC) DRIVERS M: Sangbeom Kim L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Supported F: sound/soc/samsung SAMSUNG FRAMEBUFFER DRIVER M: Jingoo Han L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/s3c-fb.c SAMSUNG MULTIFUNCTION DEVICE DRIVERS M: Sangbeom Kim L: linux-kernel@vger.kernel.org S: Supported F: drivers/mfd/sec*.c F: drivers/regulator/s2m*.c F: drivers/regulator/s5m*.c F: drivers/rtc/rtc-sec.c F: include/linux/mfd/samsung/ SERIAL DRIVERS M: Alan Cox L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial SYNOPSYS DESIGNWARE DMAC DRIVER M: Viresh Kumar S: Maintained F: include/linux/dw_dmac.h F: drivers/dma/dw_dmac_regs.h F: drivers/dma/dw_dmac.c TIMEKEEPING, NTP M: John Stultz M: Thomas Gleixner T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core S: Supported F: include/linux/clocksource.h F: include/linux/time.h F: include/linux/timex.h F: kernel/time/clocksource.c F: kernel/time/time*.c F: kernel/time/ntp.c F: drivers/clocksource TLG2300 VIDEO4LINUX-2 DRIVER M: Huang Shijie M: Kang Yong M: Zhang Xiaobing S: Supported F: drivers/media/usb/tlg2300 SC1200 WDT DRIVER M: Zwane Mwaikambo S: Maintained F: drivers/watchdog/sc1200wdt.c SCHEDULER M: Ingo Molnar M: Peter Zijlstra T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git sched/core S: Maintained F: kernel/sched/ F: include/linux/sched.h SCORE ARCHITECTURE M: Chen Liqin M: Lennox Wu W: http://www.sunplusct.com S: Supported F: arch/score/ SCSI CDROM DRIVER M: Jens Axboe L: linux-scsi@vger.kernel.org W: http://www.kernel.dk S: Maintained F: drivers/scsi/sr* SCSI RDMA PROTOCOL (SRP) INITIATOR M: David Dillow L: linux-rdma@vger.kernel.org S: Supported W: http://www.openfabrics.org Q: http://patchwork.kernel.org/project/linux-rdma/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/dad/srp-initiator.git F: drivers/infiniband/ulp/srp/ F: include/scsi/srp.h SCSI SG DRIVER M: Doug Gilbert L: linux-scsi@vger.kernel.org W: http://www.torque.net/sg S: Maintained F: drivers/scsi/sg.c F: include/scsi/sg.h SCSI SUBSYSTEM M: "James E.J. Bottomley" L: linux-scsi@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-pending-2.6.git S: Maintained F: drivers/scsi/ F: include/scsi/ SCSI TAPE DRIVER M: Kai Mäkisara L: linux-scsi@vger.kernel.org S: Maintained F: Documentation/scsi/st.txt F: drivers/scsi/st* SCTP PROTOCOL M: Vlad Yasevich M: Sridhar Samudrala L: linux-sctp@vger.kernel.org W: http://lksctp.sourceforge.net S: Maintained F: Documentation/networking/sctp.txt F: include/linux/sctp.h F: include/net/sctp/ F: net/sctp/ SCx200 CPU SUPPORT M: Jim Cromie S: Odd Fixes F: Documentation/i2c/busses/scx200_acb F: arch/x86/platform/scx200/ F: drivers/watchdog/scx200_wdt.c F: drivers/i2c/busses/scx200* F: drivers/mtd/maps/scx200_docflash.c F: include/linux/scx200.h SCx200 GPIO DRIVER M: Jim Cromie S: Maintained F: drivers/char/scx200_gpio.c F: include/linux/scx200_gpio.h SCx200 HRT CLOCKSOURCE DRIVER M: Jim Cromie S: Maintained F: drivers/clocksource/scx200_hrt.c SDRICOH_CS MMC/SD HOST CONTROLLER INTERFACE DRIVER M: Sascha Sommer L: sdricohcs-devel@lists.sourceforge.net (subscribers-only) S: Maintained F: drivers/mmc/host/sdricoh_cs.c SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER M: Chris Ball L: linux-mmc@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git S: Maintained F: drivers/mmc/host/sdhci.* F: drivers/mmc/host/sdhci-pltfm.[ch] SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF) M: Anton Vorontsov L: linuxppc-dev@lists.ozlabs.org L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/sdhci-pltfm.[ch] SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER M: Ben Dooks L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/sdhci-s3c.c SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) ST SPEAR DRIVER M: Viresh Kumar L: spear-devel@list.st.com L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/sdhci-spear.c SECURITY SUBSYSTEM M: James Morris L: linux-security-module@vger.kernel.org (suggested Cc:) T: git git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git W: http://kernsec.org/ S: Supported F: security/ SECURITY CONTACT M: Security Officers S: Supported SELINUX SECURITY MODULE M: Stephen Smalley M: James Morris M: Eric Paris L: selinux@tycho.nsa.gov (subscribers-only, general discussion) W: http://selinuxproject.org T: git git://git.infradead.org/users/eparis/selinux.git S: Supported F: include/linux/selinux* F: security/selinux/ F: scripts/selinux/ APPARMOR SECURITY MODULE M: John Johansen L: apparmor@lists.ubuntu.com (subscribers-only, general discussion) W: apparmor.wiki.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git S: Supported F: security/apparmor/ SENSABLE PHANTOM M: Jiri Slaby S: Maintained F: drivers/misc/phantom.c F: include/linux/phantom.h SERIAL ATA (SATA) SUBSYSTEM M: Jeff Garzik L: linux-ide@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git S: Supported F: drivers/ata/ F: include/linux/ata.h F: include/linux/libata.h SERVER ENGINES 10Gbps iSCSI - BladeEngine 2 DRIVER M: Jayamohan Kallickal L: linux-scsi@vger.kernel.org W: http://www.emulex.com S: Supported F: drivers/scsi/be2iscsi/ SERVER ENGINES 10Gbps NIC - BladeEngine 2 DRIVER M: Sathya Perla M: Subbu Seetharaman M: Ajit Khaparde L: netdev@vger.kernel.org W: http://www.emulex.com S: Supported F: drivers/net/ethernet/emulex/benet/ SFC NETWORK DRIVER M: Solarflare linux maintainers M: Ben Hutchings L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/sfc/ SGI GRU DRIVER M: Jack Steiner S: Maintained F: drivers/misc/sgi-gru/ SGI SN-IA64 (Altix) SERIAL CONSOLE DRIVER M: Pat Gefre L: linux-ia64@vger.kernel.org S: Supported F: Documentation/ia64/serial.txt F: drivers/tty/serial/ioc?_serial.c F: include/linux/ioc?.h SGI VISUAL WORKSTATION 320 AND 540 M: Andrey Panin L: linux-visws-devel@lists.sf.net W: http://linux-visws.sf.net S: Maintained for 2.6. F: Documentation/sgi-visws.txt SGI XP/XPC/XPNET DRIVER M: Robin Holt S: Maintained F: drivers/misc/sgi-xp/ SIMPLE FIRMWARE INTERFACE (SFI) M: Len Brown L: sfi-devel@simplefirmware.org W: http://simplefirmware.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-sfi-2.6.git S: Supported F: arch/x86/platform/sfi/ F: drivers/sfi/ F: include/linux/sfi*.h SIMTEC EB110ATX (Chalice CATS) P: Ben Dooks P: Vincent Sanders M: Simtec Linux Team W: http://www.simtec.co.uk/products/EB110ATX/ S: Supported SIMTEC EB2410ITX (BAST) P: Ben Dooks P: Vincent Sanders M: Simtec Linux Team W: http://www.simtec.co.uk/products/EB2410ITX/ S: Supported F: arch/arm/mach-s3c2410/mach-bast.c F: arch/arm/mach-s3c2410/bast-ide.c F: arch/arm/mach-s3c2410/bast-irq.c TI DAVINCI MACHINE SUPPORT M: Sekhar Nori M: Kevin Hilman L: davinci-linux-open-source@linux.davincidsp.com (moderated for non-subscribers) T: git git://gitorious.org/linux-davinci/linux-davinci.git Q: http://patchwork.kernel.org/project/linux-davinci/list/ S: Supported F: arch/arm/mach-davinci F: drivers/i2c/busses/i2c-davinci.c SIS 190 ETHERNET DRIVER M: Francois Romieu L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/sis/sis190.c SIS 900/7016 FAST ETHERNET DRIVER M: Daniele Venzano W: http://www.brownhat.org/sis900.html L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/sis/sis900.* SIS 96X I2C/SMBUS DRIVER M: "Mark M. Hoffman" L: linux-i2c@vger.kernel.org S: Maintained F: Documentation/i2c/busses/i2c-sis96x F: drivers/i2c/busses/i2c-sis96x.c SIS FRAMEBUFFER DRIVER M: Thomas Winischhofer W: http://www.winischhofer.net/linuxsisvga.shtml S: Maintained F: Documentation/fb/sisfb.txt F: drivers/video/sis/ F: include/video/sisfb.h SIS USB2VGA DRIVER M: Thomas Winischhofer W: http://www.winischhofer.at/linuxsisusbvga.shtml S: Maintained F: drivers/usb/misc/sisusbvga/ SLAB ALLOCATOR M: Christoph Lameter M: Pekka Enberg M: Matt Mackall L: linux-mm@kvack.org S: Maintained F: include/linux/sl?b*.h F: mm/sl?b.c SLEEPABLE READ-COPY UPDATE (SRCU) M: Lai Jiangshan M: "Paul E. McKenney" W: http://www.rdrop.com/users/paulmck/RCU/ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git F: include/linux/srcu* F: kernel/srcu* SMACK SECURITY MODULE M: Casey Schaufler L: linux-security-module@vger.kernel.org W: http://schaufler-ca.com T: git git://git.gitorious.org/smack-next/kernel.git S: Maintained F: Documentation/security/Smack.txt F: security/smack/ SMC91x ETHERNET DRIVER M: Nicolas Pitre S: Odd Fixes F: drivers/net/ethernet/smsc/smc91x.* SMM665 HARDWARE MONITOR DRIVER M: Guenter Roeck L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/smm665 F: drivers/hwmon/smm665.c SMSC EMC2103 HARDWARE MONITOR DRIVER M: Steve Glendinning L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/emc2103 F: drivers/hwmon/emc2103.c SMSC SCH5627 HARDWARE MONITOR DRIVER M: Hans de Goede L: lm-sensors@lm-sensors.org S: Supported F: Documentation/hwmon/sch5627 F: drivers/hwmon/sch5627.c SMSC47B397 HARDWARE MONITOR DRIVER M: "Mark M. Hoffman" L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/smsc47b397 F: drivers/hwmon/smsc47b397.c SMSC911x ETHERNET DRIVER M: Steve Glendinning L: netdev@vger.kernel.org S: Maintained F: include/linux/smsc911x.h F: drivers/net/ethernet/smsc/smsc911x.* SMSC9420 PCI ETHERNET DRIVER M: Steve Glendinning L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/smsc/smsc9420.* SMSC UFX6000 and UFX7000 USB to VGA DRIVER M: Steve Glendinning L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/smscufx.c SN-IA64 (Itanium) SUB-PLATFORM M: Jes Sorensen L: linux-altix@sgi.com L: linux-ia64@vger.kernel.org W: http://www.sgi.com/altix S: Maintained F: arch/ia64/sn/ SOC-CAMERA V4L2 SUBSYSTEM M: Guennadi Liakhovetski L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git S: Maintained F: include/media/v4l2* F: drivers/media/video/v4l2* SOEKRIS NET48XX LED SUPPORT M: Chris Boot S: Maintained F: drivers/leds/leds-net48xx.c SOFTWARE RAID (Multiple Disks) SUPPORT M: Neil Brown L: linux-raid@vger.kernel.org S: Supported F: drivers/md/ F: include/linux/raid/ SONIC NETWORK DRIVER M: Thomas Bogendoerfer L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/natsemi/sonic.* SONICS SILICON BACKPLANE DRIVER (SSB) M: Michael Buesch L: netdev@vger.kernel.org S: Maintained F: drivers/ssb/ F: include/linux/ssb/ SONY VAIO CONTROL DEVICE DRIVER M: Mattia Dongili L: platform-driver-x86@vger.kernel.org W: http://www.linux.it/~malattia/wiki/index.php/Sony_drivers S: Maintained F: Documentation/laptops/sony-laptop.txt F: drivers/char/sonypi.c F: drivers/platform/x86/sony-laptop.c F: include/linux/sony-laptop.h SONY MEMORYSTICK CARD SUPPORT M: Alex Dubov W: http://tifmxx.berlios.de/ S: Maintained F: drivers/memstick/host/tifm_ms.c SOUND M: Jaroslav Kysela M: Takashi Iwai L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://www.alsa-project.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: Documentation/sound/ F: include/sound/ F: sound/ SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC) M: Liam Girdwood M: Mark Brown T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://alsa-project.org/main/index.php/ASoC S: Supported F: sound/soc/ F: include/sound/soc* SPARC + UltraSPARC (sparc/sparc64) M: "David S. Miller" L: sparclinux@vger.kernel.org Q: http://patchwork.ozlabs.org/project/sparclinux/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next.git S: Maintained F: arch/sparc/ F: drivers/sbus/ SPARC SERIAL DRIVERS M: "David S. Miller" L: sparclinux@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next.git S: Maintained F: include/linux/sunserialcore.h F: drivers/tty/serial/suncore.c F: drivers/tty/serial/sunhv.c F: drivers/tty/serial/sunsab.c F: drivers/tty/serial/sunsab.h F: drivers/tty/serial/sunsu.c F: drivers/tty/serial/sunzilog.c F: drivers/tty/serial/sunzilog.h SPARSE CHECKER M: "Christopher Li" L: linux-sparse@vger.kernel.org W: https://sparse.wiki.kernel.org/ T: git git://git.kernel.org/pub/scm/devel/sparse/sparse.git T: git git://git.kernel.org/pub/scm/devel/sparse/chrisl/sparse.git S: Maintained F: include/linux/compiler.h SPEAR PLATFORM SUPPORT M: Viresh Kumar M: Shiraz Hashim L: spear-devel@list.st.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.st.com/spear S: Maintained F: arch/arm/plat-spear/ SPEAR13XX MACHINE SUPPORT M: Viresh Kumar M: Shiraz Hashim L: spear-devel@list.st.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.st.com/spear S: Maintained F: arch/arm/mach-spear13xx/ SPEAR3XX MACHINE SUPPORT M: Viresh Kumar M: Shiraz Hashim L: spear-devel@list.st.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.st.com/spear S: Maintained F: arch/arm/mach-spear3xx/ SPEAR6XX MACHINE SUPPORT M: Rajeev Kumar M: Shiraz Hashim M: Viresh Kumar L: spear-devel@list.st.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.st.com/spear S: Maintained F: arch/arm/mach-spear6xx/ SPEAR CLOCK FRAMEWORK SUPPORT M: Viresh Kumar L: spear-devel@list.st.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://www.st.com/spear S: Maintained F: drivers/clk/spear/ SPI SUBSYSTEM M: Grant Likely L: spi-devel-general@lists.sourceforge.net Q: http://patchwork.kernel.org/project/spi-devel-general/list/ T: git git://git.secretlab.ca/git/linux-2.6.git S: Maintained F: Documentation/spi/ F: drivers/spi/ F: include/linux/spi/ SPIDERNET NETWORK DRIVER for CELL M: Ishizaki Kou M: Jens Osterkamp L: netdev@vger.kernel.org S: Supported F: Documentation/networking/spider_net.txt F: drivers/net/ethernet/toshiba/spider_net* SPU FILE SYSTEM M: Jeremy Kerr L: linuxppc-dev@lists.ozlabs.org L: cbe-oss-dev@lists.ozlabs.org W: http://www.ibm.com/developerworks/power/cell/ S: Supported F: Documentation/filesystems/spufs.txt F: arch/powerpc/platforms/cell/spufs/ SQUASHFS FILE SYSTEM M: Phillip Lougher L: squashfs-devel@lists.sourceforge.net (subscribers-only) W: http://squashfs.org.uk S: Maintained F: Documentation/filesystems/squashfs.txt F: fs/squashfs/ SRM (Alpha) environment access M: Jan-Benedict Glaw S: Maintained F: arch/alpha/kernel/srm_env.c STABLE BRANCH M: Greg Kroah-Hartman L: stable@vger.kernel.org S: Supported STAGING SUBSYSTEM M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git L: devel@driverdev.osuosl.org S: Supported F: drivers/staging/ STAGING - AGERE HERMES II and II.5 WIRELESS DRIVERS M: Henk de Groot S: Odd Fixes F: drivers/staging/wlags49_h2/ F: drivers/staging/wlags49_h25/ STAGING - ASUS OLED M: Jakub Schmidtke S: Odd Fixes F: drivers/staging/asus_oled/ STAGING - COMEDI M: Ian Abbott M: Mori Hess S: Odd Fixes F: drivers/staging/comedi/ STAGING - CRYSTAL HD VIDEO DECODER M: Naren Sankar M: Jarod Wilson M: Scott Davilla M: Manu Abraham S: Odd Fixes F: drivers/staging/crystalhd/ STAGING - ECHO CANCELLER M: Steve Underwood M: David Rowe S: Odd Fixes F: drivers/staging/echo/ STAGING - ET131X NETWORK DRIVER M: Mark Einon S: Odd Fixes F: drivers/staging/et131x/ STAGING - FLARION FT1000 DRIVERS M: Marek Belisko S: Odd Fixes F: drivers/staging/ft1000/ STAGING - FRONTIER TRANZPORT AND ALPHATRACK M: David Täht S: Odd Fixes F: drivers/staging/frontier/ STAGING - INDUSTRIAL IO M: Jonathan Cameron L: linux-iio@vger.kernel.org S: Odd Fixes F: drivers/staging/iio/ STAGING - LIRC (LINUX INFRARED REMOTE CONTROL) DRIVERS M: Jarod Wilson W: http://www.lirc.org/ S: Odd Fixes F: drivers/staging/media/lirc/ STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec) M: Julian Andres Klode M: Marc Dietrich L: ac100@lists.launchpad.net (moderated for non-subscribers) S: Maintained F: drivers/staging/nvec/ STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON) M: Andres Salomon M: Chris Ball M: Jon Nettleton W: http://wiki.laptop.org/go/DCON S: Odd Fixes F: drivers/staging/olpc_dcon/ STAGING - OZMO DEVICES USB OVER WIFI DRIVER M: Rupesh Gujare M: Chris Kelly S: Maintained F: drivers/staging/ozwpan/ STAGING - PARALLEL LCD/KEYPAD PANEL DRIVER M: Willy Tarreau S: Odd Fixes F: drivers/staging/panel/ STAGING - REALTEK RTL8712U DRIVERS M: Larry Finger M: Florian Schilhabel . S: Odd Fixes F: drivers/staging/rtl8712/ STAGING - SILICON MOTION SM7XX FRAME BUFFER DRIVER M: Teddy Wang S: Odd Fixes F: drivers/staging/sm7xx/ STAGING - SOFTLOGIC 6x10 MPEG CODEC M: Ben Collins S: Odd Fixes F: drivers/staging/media/solo6x10/ STAGING - SPEAKUP CONSOLE SPEECH DRIVER M: William Hubbs M: Chris Brannon M: Kirk Reiser M: Samuel Thibault L: speakup@braille.uwo.ca W: http://www.linux-speakup.org/ S: Odd Fixes F: drivers/staging/speakup/ STAGING - TI DSP BRIDGE DRIVERS M: Omar Ramirez Luna S: Odd Fixes F: drivers/staging/tidspbridge/ STAGING - USB ENE SM/MS CARD READER DRIVER M: Al Cho S: Odd Fixes F: drivers/staging/keucr/ STAGING - VIA VT665X DRIVERS M: Forest Bond S: Odd Fixes F: drivers/staging/vt665?/ STAGING - WINBOND IS89C35 WLAN USB DRIVER M: Pavel Machek S: Odd Fixes F: drivers/staging/winbond/ STAGING - XGI Z7,Z9,Z11 PCI DISPLAY DRIVER M: Arnaud Patard S: Odd Fixes F: drivers/staging/xgifb/ STARFIRE/DURALAN NETWORK DRIVER M: Ion Badulescu S: Odd Fixes F: drivers/net/ethernet/adaptec/starfire* SUN3/3X M: Sam Creasey W: http://sammy.net/sun3/ S: Maintained F: arch/m68k/kernel/*sun3* F: arch/m68k/sun3*/ F: arch/m68k/include/asm/sun3* F: drivers/net/ethernet/i825xx/sun3* SUPERH M: Paul Mundt L: linux-sh@vger.kernel.org W: http://www.linux-sh.org Q: http://patchwork.kernel.org/project/linux-sh/list/ T: git git://github.com/pmundt/linux-sh.git sh-latest S: Supported F: Documentation/sh/ F: arch/sh/ F: drivers/sh/ SUSPEND TO RAM M: Len Brown M: Pavel Machek M: "Rafael J. Wysocki" L: linux-pm@vger.kernel.org S: Supported F: Documentation/power/ F: arch/x86/kernel/acpi/ F: drivers/base/power/ F: kernel/power/ F: include/linux/suspend.h F: include/linux/freezer.h F: include/linux/pm.h SVGA HANDLING M: Martin Mares L: linux-video@atrey.karlin.mff.cuni.cz S: Maintained F: Documentation/svga.txt F: arch/x86/boot/video* SYSV FILESYSTEM M: Christoph Hellwig S: Maintained F: Documentation/filesystems/sysv-fs.txt F: fs/sysv/ F: include/linux/sysv_fs.h TARGET SUBSYSTEM M: Nicholas A. Bellinger L: linux-scsi@vger.kernel.org L: target-devel@vger.kernel.org L: http://groups.google.com/group/linux-iscsi-target-dev W: http://www.linux-iscsi.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core.git master S: Supported F: drivers/target/ F: include/target/ F: Documentation/target/ TASKSTATS STATISTICS INTERFACE M: Balbir Singh S: Maintained F: Documentation/accounting/taskstats* F: include/linux/taskstats* F: kernel/taskstats.c TC CLASSIFIER M: Jamal Hadi Salim L: netdev@vger.kernel.org S: Maintained F: include/linux/pkt_cls.h F: include/net/pkt_cls.h F: net/sched/ TCP LOW PRIORITY MODULE M: "Wong Hoi Sing, Edison" M: "Hung Hing Lun, Mike" W: http://tcp-lp-mod.sourceforge.net/ S: Maintained F: net/ipv4/tcp_lp.c TEAM DRIVER M: Jiri Pirko L: netdev@vger.kernel.org S: Supported F: drivers/net/team/ F: include/linux/if_team.h TEGRA SUPPORT M: Stephen Warren L: linux-tegra@vger.kernel.org Q: http://patchwork.ozlabs.org/project/linux-tegra/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra.git S: Supported F: arch/arm/mach-tegra F: arch/arm/boot/dts/tegra* F: arch/arm/configs/tegra_defconfig TEHUTI ETHERNET DRIVER M: Andy Gospodarek L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/tehuti/* Telecom Clock Driver for MCPL0010 M: Mark Gross S: Supported F: drivers/char/tlclk.c TENSILICA XTENSA PORT (xtensa) M: Chris Zankel M: Max Filippov L: linux-xtensa@linux-xtensa.org S: Maintained F: arch/xtensa/ THINKPAD ACPI EXTRAS DRIVER M: Henrique de Moraes Holschuh L: ibm-acpi-devel@lists.sourceforge.net L: platform-driver-x86@vger.kernel.org W: http://ibm-acpi.sourceforge.net W: http://thinkwiki.org/wiki/Ibm-acpi T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git S: Maintained F: drivers/platform/x86/thinkpad_acpi.c TI FLASH MEDIA INTERFACE DRIVER M: Alex Dubov S: Maintained F: drivers/misc/tifm* F: drivers/mmc/host/tifm_sd.c F: include/linux/tifm.h TI LM49xxx FAMILY ASoC CODEC DRIVERS M: M R Swami Reddy M: Vishwas A Deshpande L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/soc/codecs/lm49453* F: sound/soc/codecs/isabelle* TI TWL4030 SERIES SOC CODEC DRIVER M: Peter Ujfalusi L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/soc/codecs/twl4030* TI WILINK WIRELESS DRIVERS M: Luciano Coelho L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/wl12xx W: http://wireless.kernel.org/en/users/Drivers/wl1251 T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git S: Maintained F: drivers/net/wireless/ti/ F: include/linux/wl12xx.h TIPC NETWORK LAYER M: Jon Maloy M: Allan Stephens L: netdev@vger.kernel.org (core kernel code) L: tipc-discussion@lists.sourceforge.net (user apps, general discussion) W: http://tipc.sourceforge.net/ S: Maintained F: include/linux/tipc*.h F: net/tipc/ TILE ARCHITECTURE M: Chris Metcalf W: http://www.tilera.com/scm/ S: Supported F: arch/tile/ F: drivers/tty/hvc/hvc_tile.c F: drivers/net/ethernet/tile/ F: drivers/edac/tile_edac.c TLAN NETWORK DRIVER M: Samuel Chessman L: tlan-devel@lists.sourceforge.net (subscribers-only) W: http://sourceforge.net/projects/tlan/ S: Maintained F: Documentation/networking/tlan.txt F: drivers/net/ethernet/ti/tlan.* TOMOYO SECURITY MODULE M: Kentaro Takeda M: Tetsuo Handa L: tomoyo-dev-en@lists.sourceforge.jp (subscribers-only, for developers in English) L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English) L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese) L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese) W: http://tomoyo.sourceforge.jp/ T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.5.x/tomoyo-lsm/patches/ S: Maintained F: security/tomoyo/ TOPSTAR LAPTOP EXTRAS DRIVER M: Herton Ronaldo Krzesinski L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/topstar-laptop.c TOSHIBA ACPI EXTRAS DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan F: drivers/platform/x86/toshiba_acpi.c TOSHIBA SMM DRIVER M: Jonathan Buzzard L: tlinux-users@tce.toshiba-dme.co.jp W: http://www.buzzard.org.uk/toshiba/ S: Maintained F: drivers/char/toshiba.c F: include/linux/toshiba.h TMIO MMC DRIVER M: Guennadi Liakhovetski M: Ian Molton L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/tmio_mmc* F: drivers/mmc/host/sh_mobile_sdhi.c F: include/linux/mmc/tmio.h F: include/linux/mmc/sh_mobile_sdhi.h TMPFS (SHMEM FILESYSTEM) M: Hugh Dickins L: linux-mm@kvack.org S: Maintained F: include/linux/shmem_fs.h F: mm/shmem.c TPM DEVICE DRIVER M: Kent Yoder M: Rajiv Andrade W: http://tpmdd.sourceforge.net M: Marcel Selhorst M: Sirrix AG W: http://www.sirrix.com L: tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers) S: Maintained F: drivers/char/tpm/ TRACING M: Steven Rostedt M: Frederic Weisbecker M: Ingo Molnar T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core S: Maintained F: Documentation/trace/ftrace.txt F: arch/*/*/*/ftrace.h F: arch/*/kernel/ftrace.c F: include/*/ftrace.h F: include/linux/trace*.h F: include/trace/ F: kernel/trace/ TRIVIAL PATCHES M: Jiri Kosina T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial.git S: Maintained K: ^Subject:.*(?i)trivial TTY LAYER M: Greg Kroah-Hartman S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git F: drivers/tty/ F: drivers/tty/serial/serial_core.c F: include/linux/serial_core.h F: include/linux/serial.h F: include/linux/tty.h TULIP NETWORK DRIVERS M: Grant Grundler L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/dec/tulip/ TUN/TAP driver M: Maxim Krasnyansky L: vtun@office.satix.net W: http://vtun.sourceforge.net/tun S: Maintained F: Documentation/networking/tuntap.txt F: arch/um/os-Linux/drivers/ TURBOCHANNEL SUBSYSTEM M: "Maciej W. Rozycki" S: Maintained F: drivers/tc/ F: include/linux/tc.h U14-34F SCSI DRIVER M: Dario Ballabio L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/u14-34f.c UBI FILE SYSTEM (UBIFS) M: Artem Bityutskiy M: Adrian Hunter L: linux-mtd@lists.infradead.org T: git git://git.infradead.org/ubifs-2.6.git W: http://www.linux-mtd.infradead.org/doc/ubifs.html S: Maintained F: Documentation/filesystems/ubifs.txt F: fs/ubifs/ UCLINUX (AND M68KNOMMU) M: Greg Ungerer W: http://www.uclinux.org/ L: uclinux-dev@uclinux.org (subscribers-only) S: Maintained F: arch/m68k/*/*_no.* F: arch/m68k/include/asm/*_no.* UCLINUX FOR RENESAS H8/300 (H8300) M: Yoshinori Sato W: http://uclinux-h8.sourceforge.jp/ S: Supported F: arch/h8300/ F: drivers/ide/ide-h8300.c F: drivers/net/ethernet/8390/ne-h8300.c UDF FILESYSTEM M: Jan Kara S: Maintained F: Documentation/filesystems/udf.txt F: fs/udf/ UFS FILESYSTEM M: Evgeniy Dushistov S: Maintained F: Documentation/filesystems/ufs.txt F: fs/ufs/ UHID USERSPACE HID IO DRIVER: M: David Herrmann L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/uhid.c F: include/linux/uhid.h ULTRA-WIDEBAND (UWB) SUBSYSTEM: L: linux-usb@vger.kernel.org S: Orphan F: drivers/uwb/ F: include/linux/uwb.h F: include/linux/uwb/ UNICORE32 ARCHITECTURE: M: Guan Xuetao W: http://mprc.pku.edu.cn/~guanxuetao/linux S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/epip/linux-2.6-unicore32.git F: arch/unicore32/ UNIFDEF M: Tony Finch W: http://dotat.at/prog/unifdef S: Maintained F: scripts/unifdef.c UNIFORM CDROM DRIVER M: Jens Axboe W: http://www.kernel.dk S: Maintained F: Documentation/cdrom/ F: drivers/cdrom/cdrom.c F: include/linux/cdrom.h UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER M: Vinayak Holikatti M: Santosh Y L: linux-scsi@vger.kernel.org S: Supported F: Documentation/scsi/ufs.txt F: drivers/scsi/ufs/ UNSORTED BLOCK IMAGES (UBI) M: Artem Bityutskiy W: http://www.linux-mtd.infradead.org/ L: linux-mtd@lists.infradead.org T: git git://git.infradead.org/ubi-2.6.git S: Maintained F: drivers/mtd/ubi/ F: include/linux/mtd/ubi.h F: include/mtd/ubi-user.h USB ACM DRIVER M: Oliver Neukum L: linux-usb@vger.kernel.org S: Maintained F: Documentation/usb/acm.txt F: drivers/usb/class/cdc-acm.* USB ATTACHED SCSI M: Matthew Wilcox M: Sarah Sharp L: linux-usb@vger.kernel.org L: linux-scsi@vger.kernel.org S: Supported F: drivers/usb/storage/uas.c USB BLOCK DRIVER (UB ub) M: Pete Zaitcev L: linux-usb@vger.kernel.org S: Supported F: drivers/block/ub.c USB CDC ETHERNET DRIVER M: Oliver Neukum L: linux-usb@vger.kernel.org S: Maintained F: drivers/net/usb/cdc_*.c F: include/linux/usb/cdc.h USB CYPRESS C67X00 DRIVER M: Peter Korsgaard L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/c67x00/ USB DAVICOM DM9601 DRIVER M: Peter Korsgaard L: netdev@vger.kernel.org W: http://www.linux-usb.org/usbnet S: Maintained F: drivers/net/usb/dm9601.c USB DIAMOND RIO500 DRIVER M: Cesar Miquel L: rio500-users@lists.sourceforge.net W: http://rio500.sourceforge.net S: Maintained F: drivers/usb/misc/rio500* USB EHCI DRIVER M: Alan Stern L: linux-usb@vger.kernel.org S: Maintained F: Documentation/usb/ehci.txt F: drivers/usb/host/ehci* USB ET61X[12]51 DRIVER M: Luca Risolia L: linux-usb@vger.kernel.org L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.linux-projects.org S: Maintained F: drivers/media/video/et61x251/ USB GADGET/PERIPHERAL SUBSYSTEM M: Felipe Balbi L: linux-usb@vger.kernel.org W: http://www.linux-usb.org/gadget T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/gadget/ F: include/linux/usb/gadget* USB HID/HIDBP DRIVERS (USB KEYBOARDS, MICE, REMOTE CONTROLS, ...) M: Jiri Kosina L: linux-usb@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git S: Maintained F: Documentation/hid/hiddev.txt F: drivers/hid/usbhid/ USB/IP DRIVERS M: Matt Mooney L: linux-usb@vger.kernel.org S: Maintained F: drivers/staging/usbip/ USB ISP116X DRIVER M: Olav Kongas L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/host/isp116x* F: include/linux/usb/isp116x.h USB KAWASAKI LSI DRIVER M: Oliver Neukum L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/serial/kl5kusb105.* USB MASS STORAGE DRIVER M: Matthew Dharm L: linux-usb@vger.kernel.org L: usb-storage@lists.one-eyed-alien.net S: Maintained W: http://www.one-eyed-alien.net/~mdharm/linux-usb/ F: drivers/usb/storage/ USB MIDI DRIVER M: Clemens Ladisch L: alsa-devel@alsa-project.org (moderated for non-subscribers) T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: sound/usb/midi.* USB OHCI DRIVER M: Alan Stern L: linux-usb@vger.kernel.org S: Maintained F: Documentation/usb/ohci.txt F: drivers/usb/host/ohci* USB OPTION-CARD DRIVER M: Matthias Urlichs L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/serial/option.c USB PEGASUS DRIVER M: Petko Manolov L: linux-usb@vger.kernel.org L: netdev@vger.kernel.org W: http://pegasus2.sourceforge.net/ S: Maintained F: drivers/net/usb/pegasus.* USB PHY LAYER M: Felipe Balbi L: linux-usb@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/phy/ F: drivers/usb/otg/ USB PRINTER DRIVER (usblp) M: Pete Zaitcev L: linux-usb@vger.kernel.org S: Supported F: drivers/usb/class/usblp.c USB RTL8150 DRIVER M: Petko Manolov L: linux-usb@vger.kernel.org L: netdev@vger.kernel.org W: http://pegasus2.sourceforge.net/ S: Maintained F: drivers/net/usb/rtl8150.c USB SERIAL BELKIN F5U103 DRIVER M: William Greathouse L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/serial/belkin_sa.* USB SERIAL CYPRESS M8 DRIVER M: Lonnie Mendez L: linux-usb@vger.kernel.org S: Maintained W: http://geocities.com/i0xox0i W: http://firstlight.net/cvs F: drivers/usb/serial/cypress_m8.* USB SERIAL CYBERJACK DRIVER M: Matthias Bruestle and Harald Welte W: http://www.reiner-sct.de/support/treiber_cyberjack.php S: Maintained F: drivers/usb/serial/cyberjack.c USB SERIAL DIGI ACCELEPORT DRIVER M: Peter Berger M: Al Borchers L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/serial/digi_acceleport.c USB SERIAL DRIVER M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org S: Supported F: Documentation/usb/usb-serial.txt F: drivers/usb/serial/generic.c F: drivers/usb/serial/usb-serial.c F: include/linux/usb/serial.h USB SERIAL EMPEG EMPEG-CAR MARK I/II DRIVER M: Gary Brubaker L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/serial/empeg.c USB SERIAL KEYSPAN DRIVER M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/serial/*keyspan* USB SERIAL WHITEHEAT DRIVER M: Support Department L: linux-usb@vger.kernel.org W: http://www.connecttech.com S: Supported F: drivers/usb/serial/whiteheat* USB SMSC75XX ETHERNET DRIVER M: Steve Glendinning L: netdev@vger.kernel.org S: Maintained F: drivers/net/usb/smsc75xx.* USB SMSC95XX ETHERNET DRIVER M: Steve Glendinning L: netdev@vger.kernel.org S: Maintained F: drivers/net/usb/smsc95xx.* USB SN9C1xx DRIVER M: Luca Risolia L: linux-usb@vger.kernel.org L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.linux-projects.org S: Maintained F: Documentation/video4linux/sn9c102.txt F: drivers/media/usb/sn9c102/ USB SUBSYSTEM M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org W: http://www.linux-usb.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git S: Supported F: Documentation/usb/ F: drivers/net/usb/ F: drivers/usb/ F: include/linux/usb.h F: include/linux/usb/ USB UHCI DRIVER M: Alan Stern L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/host/uhci* USB "USBNET" DRIVER FRAMEWORK M: Oliver Neukum L: netdev@vger.kernel.org W: http://www.linux-usb.org/usbnet S: Maintained F: drivers/net/usb/usbnet.c F: include/linux/usb/usbnet.h USB VIDEO CLASS M: Laurent Pinchart L: linux-uvc-devel@lists.sourceforge.net (subscribers-only) L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.ideasonboard.org/uvc/ S: Maintained F: drivers/media/usb/uvc/ USB W996[87]CF DRIVER M: Luca Risolia L: linux-usb@vger.kernel.org L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.linux-projects.org S: Maintained F: Documentation/video4linux/w9968cf.txt F: drivers/media/video/w996* USB WIRELESS RNDIS DRIVER (rndis_wlan) M: Jussi Kivilinna L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/rndis_wlan.c USB XHCI DRIVER M: Sarah Sharp L: linux-usb@vger.kernel.org S: Supported F: drivers/usb/host/xhci* F: drivers/usb/host/pci-quirks* USB ZD1201 DRIVER L: linux-wireless@vger.kernel.org W: http://linux-lc100020.sourceforge.net S: Orphan F: drivers/net/wireless/zd1201.* USB ZR364XX DRIVER M: Antoine Jacquet L: linux-usb@vger.kernel.org L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://royale.zerezo.com/zr364xx/ S: Maintained F: Documentation/video4linux/zr364xx.txt F: drivers/media/usb/zr364xx.c USER-MODE LINUX (UML) M: Jeff Dike M: Richard Weinberger L: user-mode-linux-devel@lists.sourceforge.net L: user-mode-linux-user@lists.sourceforge.net W: http://user-mode-linux.sourceforge.net S: Maintained F: Documentation/virtual/uml/ F: arch/um/ F: arch/x86/um/ F: fs/hostfs/ F: fs/hppfs/ USERSPACE I/O (UIO) M: "Hans J. Koch" M: Greg Kroah-Hartman S: Maintained F: Documentation/DocBook/uio-howto.tmpl F: drivers/uio/ F: include/linux/uio*.h UTIL-LINUX PACKAGE M: Karel Zak L: util-linux@vger.kernel.org W: http://en.wikipedia.org/wiki/Util-linux T: git git://git.kernel.org/pub/scm/utils/util-linux/util-linux.git S: Maintained UVESAFB DRIVER M: Michal Januszewski L: linux-fbdev@vger.kernel.org W: http://dev.gentoo.org/~spock/projects/uvesafb/ S: Maintained F: Documentation/fb/uvesafb.txt F: drivers/video/uvesafb.* VFAT/FAT/MSDOS FILESYSTEM M: OGAWA Hirofumi S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ VFIO DRIVER M: Alex Williamson L: kvm@vger.kernel.org S: Maintained F: Documentation/vfio.txt F: drivers/vfio/ F: include/linux/vfio.h VIDEOBUF2 FRAMEWORK M: Pawel Osciak M: Marek Szyprowski M: Kyungmin Park L: linux-media@vger.kernel.org S: Maintained F: drivers/media/video/videobuf2-* F: include/media/videobuf2-* VIRTIO CONSOLE DRIVER M: Amit Shah L: virtualization@lists.linux-foundation.org S: Maintained F: drivers/char/virtio_console.c F: include/linux/virtio_console.h VIRTIO CORE, NET AND BLOCK DRIVERS M: Rusty Russell M: "Michael S. Tsirkin" L: virtualization@lists.linux-foundation.org S: Maintained F: drivers/virtio/ F: drivers/net/virtio_net.c F: drivers/block/virtio_blk.c F: include/linux/virtio_*.h VIRTIO HOST (VHOST) M: "Michael S. Tsirkin" L: kvm@vger.kernel.org L: virtualization@lists.linux-foundation.org L: netdev@vger.kernel.org S: Maintained F: drivers/vhost/ F: include/linux/vhost.h VIA RHINE NETWORK DRIVER M: Roger Luethi S: Maintained F: drivers/net/ethernet/via/via-rhine.c VIAPRO SMBUS DRIVER M: Jean Delvare L: linux-i2c@vger.kernel.org S: Maintained F: Documentation/i2c/busses/i2c-viapro F: drivers/i2c/busses/i2c-viapro.c VIA SD/MMC CARD CONTROLLER DRIVER M: Bruce Chang M: Harald Welte S: Maintained F: drivers/mmc/host/via-sdmmc.c VIA UNICHROME(PRO)/CHROME9 FRAMEBUFFER DRIVER M: Florian Tobias Schandinat L: linux-fbdev@vger.kernel.org S: Maintained F: include/linux/via-core.h F: include/linux/via-gpio.h F: include/linux/via_i2c.h F: drivers/video/via/ VIA VELOCITY NETWORK DRIVER M: Francois Romieu L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/via/via-velocity.* VLAN (802.1Q) M: Patrick McHardy L: netdev@vger.kernel.org S: Maintained F: drivers/net/macvlan.c F: include/linux/if_*vlan.h F: net/8021q/ VLYNQ BUS M: Florian Fainelli L: openwrt-devel@lists.openwrt.org (subscribers-only) S: Maintained F: drivers/vlynq/vlynq.c F: include/linux/vlynq.h VME SUBSYSTEM M: Martyn Welch M: Manohar Vanga M: Greg Kroah-Hartman L: devel@driverdev.osuosl.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git F: Documentation/vme_api.txt F: drivers/staging/vme/ F: drivers/vme/ F: include/linux/vme* VMWARE VMXNET3 ETHERNET DRIVER M: Shreyas Bhatewara M: "VMware, Inc." L: netdev@vger.kernel.org S: Maintained F: drivers/net/vmxnet3/ VMware PVSCSI driver M: Arvind Kumar M: VMware PV-Drivers L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/vmw_pvscsi.c F: drivers/scsi/vmw_pvscsi.h VOLTAGE AND CURRENT REGULATOR FRAMEWORK M: Liam Girdwood M: Mark Brown W: http://opensource.wolfsonmicro.com/node/15 W: http://www.slimlogic.co.uk/?p=48 T: git git://git.kernel.org/pub/scm/linux/kernel/git/lrg/regulator.git S: Supported F: drivers/regulator/ F: include/linux/regulator/ VT1211 HARDWARE MONITOR DRIVER M: Juerg Haefliger L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/vt1211 F: drivers/hwmon/vt1211.c VT8231 HARDWARE MONITOR DRIVER M: Roger Lucas L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/vt8231.c VUB300 USB to SDIO/SD/MMC bridge chip M: Tony Olech L: linux-mmc@vger.kernel.org L: linux-usb@vger.kernel.org S: Supported F: drivers/mmc/host/vub300.c W1 DALLAS'S 1-WIRE BUS M: Evgeniy Polyakov S: Maintained F: Documentation/w1/ F: drivers/w1/ W83791D HARDWARE MONITORING DRIVER M: Marc Hulsman L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/w83791d F: drivers/hwmon/w83791d.c W83793 HARDWARE MONITORING DRIVER M: Rudolf Marek L: lm-sensors@lm-sensors.org S: Maintained F: Documentation/hwmon/w83793 F: drivers/hwmon/w83793.c W83795 HARDWARE MONITORING DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/w83795.c W83L51xD SD/MMC CARD INTERFACE DRIVER M: Pierre Ossman S: Maintained F: drivers/mmc/host/wbsd.* WATCHDOG DEVICE DRIVERS M: Wim Van Sebroeck L: linux-watchdog@vger.kernel.org W: http://www.linux-watchdog.org/ T: git git://www.linux-watchdog.org/linux-watchdog.git S: Maintained F: Documentation/watchdog/ F: drivers/watchdog/ F: include/linux/watchdog.h WD7000 SCSI DRIVER M: Miroslav Zagorac L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/wd7000.c WIIMOTE HID DRIVER M: David Herrmann L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/hid-wiimote* WINBOND CIR DRIVER M: David Härdeman S: Maintained F: drivers/media/rc/winbond-cir.c WIMAX STACK M: Inaky Perez-Gonzalez M: linux-wimax@intel.com L: wimax@linuxwimax.org S: Supported W: http://linuxwimax.org F: Documentation/wimax/README.wimax F: include/linux/wimax.h F: include/linux/wimax/debug.h F: include/net/wimax.h F: net/wimax/ WISTRON LAPTOP BUTTON DRIVER M: Miloslav Trmac S: Maintained F: drivers/input/misc/wistron_btns.c WL3501 WIRELESS PCMCIA CARD DRIVER M: Arnaldo Carvalho de Melo L: linux-wireless@vger.kernel.org W: http://oops.ghostprotocols.net:81/blog S: Maintained F: drivers/net/wireless/wl3501* WM97XX TOUCHSCREEN DRIVERS M: Mark Brown M: Liam Girdwood L: linux-input@vger.kernel.org T: git git://opensource.wolfsonmicro.com/linux-2.6-touch W: http://opensource.wolfsonmicro.com/node/7 S: Supported F: drivers/input/touchscreen/*wm97* F: include/linux/wm97xx.h WOLFSON MICROELECTRONICS DRIVERS M: Mark Brown L: patches@opensource.wolfsonmicro.com T: git git://opensource.wolfsonmicro.com/linux-2.6-asoc T: git git://opensource.wolfsonmicro.com/linux-2.6-audioplus W: http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices S: Supported F: Documentation/hwmon/wm83?? F: arch/arm/mach-s3c64xx/mach-crag6410* F: drivers/clk/clk-wm83*.c F: drivers/extcon/extcon-arizona.c F: drivers/leds/leds-wm83*.c F: drivers/gpio/gpio-*wm*.c F: drivers/gpio/gpio-arizona.c F: drivers/hwmon/wm83??-hwmon.c F: drivers/input/misc/wm831x-on.c F: drivers/input/touchscreen/wm831x-ts.c F: drivers/input/touchscreen/wm97*.c F: drivers/mfd/arizona* F: drivers/mfd/wm*.c F: drivers/power/wm83*.c F: drivers/rtc/rtc-wm83*.c F: drivers/regulator/wm8*.c F: drivers/video/backlight/wm83*_bl.c F: drivers/watchdog/wm83*_wdt.c F: include/linux/mfd/arizona/ F: include/linux/mfd/wm831x/ F: include/linux/mfd/wm8350/ F: include/linux/mfd/wm8400* F: include/linux/wm97xx.h F: include/sound/wm????.h F: sound/soc/codecs/arizona.? F: sound/soc/codecs/wm* WORKQUEUE M: Tejun Heo T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git S: Maintained F: include/linux/workqueue.h F: kernel/workqueue.c F: Documentation/workqueue.txt X.25 NETWORK LAYER M: Andrew Hendry L: linux-x25@vger.kernel.org S: Odd Fixes F: Documentation/networking/x25* F: include/net/x25* F: net/x25/ X86 ARCHITECTURE (32-BIT AND 64-BIT) M: Thomas Gleixner M: Ingo Molnar M: "H. Peter Anvin" M: x86@kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/core S: Maintained F: Documentation/x86/ F: arch/x86/ X86 PLATFORM DRIVERS M: Matthew Garrett L: platform-driver-x86@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86.git S: Maintained F: drivers/platform/x86 X86 MCE INFRASTRUCTURE M: Tony Luck M: Borislav Petkov L: linux-edac@vger.kernel.org S: Maintained F: arch/x86/kernel/cpu/mcheck/* XEN HYPERVISOR INTERFACE M: Konrad Rzeszutek Wilk M: Jeremy Fitzhardinge L: xen-devel@lists.xensource.com (moderated for non-subscribers) L: virtualization@lists.linux-foundation.org S: Supported F: arch/x86/xen/ F: drivers/*/xen-*front.c F: drivers/xen/ F: arch/x86/include/asm/xen/ F: include/xen/ XEN NETWORK BACKEND DRIVER M: Ian Campbell L: xen-devel@lists.xensource.com (moderated for non-subscribers) L: netdev@vger.kernel.org S: Supported F: drivers/net/xen-netback/* XEN PCI SUBSYSTEM M: Konrad Rzeszutek Wilk L: xen-devel@lists.xensource.com (moderated for non-subscribers) S: Supported F: arch/x86/pci/*xen* F: drivers/pci/*xen* XEN SWIOTLB SUBSYSTEM M: Konrad Rzeszutek Wilk L: xen-devel@lists.xensource.com (moderated for non-subscribers) S: Supported F: arch/x86/xen/*swiotlb* F: drivers/xen/*swiotlb* XFS FILESYSTEM P: Silicon Graphics Inc M: Ben Myers M: Alex Elder M: xfs@oss.sgi.com L: xfs@oss.sgi.com W: http://oss.sgi.com/projects/xfs T: git git://oss.sgi.com/xfs/xfs.git S: Supported F: Documentation/filesystems/xfs.txt F: fs/xfs/ XILINX AXI ETHERNET DRIVER M: Anirudha Sarangi M: John Linn S: Maintained F: drivers/net/ethernet/xilinx/xilinx_axienet* XILINX SYSTEMACE DRIVER M: Grant Likely W: http://www.secretlab.ca/ S: Maintained F: drivers/block/xsysace.c XILINX UARTLITE SERIAL DRIVER M: Peter Korsgaard L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial/uartlite.c YAM DRIVER FOR AX.25 M: Jean-Paul Roubelat L: linux-hams@vger.kernel.org S: Maintained F: drivers/net/hamradio/yam* F: include/linux/yam.h YEALINK PHONE DRIVER M: Henk Vergonet L: usbb2k-api-dev@nongnu.org S: Maintained F: Documentation/input/yealink.txt F: drivers/input/misc/yealink.* Z8530 DRIVER FOR AX.25 M: Joerg Reuter W: http://yaina.de/jreuter/ W: http://www.qsl.net/dl1bke/ L: linux-hams@vger.kernel.org S: Maintained F: Documentation/networking/z8530drv.txt F: drivers/net/hamradio/*scc.c F: drivers/net/hamradio/z8530.h ZD1211RW WIRELESS DRIVER M: Daniel Drake M: Ulrich Kunitz W: http://zd1211.ath.cx/wiki/DriverRewrite L: linux-wireless@vger.kernel.org L: zd1211-devs@lists.sourceforge.net (subscribers-only) S: Maintained F: drivers/net/wireless/zd1211rw/ ZR36067 VIDEO FOR LINUX DRIVER L: mjpeg-users@lists.sourceforge.net L: linux-media@vger.kernel.org W: http://mjpeg.sourceforge.net/driver-zoran/ T: Mercurial http://linuxtv.org/hg/v4l-dvb S: Odd Fixes F: drivers/media/video/zoran/ ZS DECSTATION Z85C30 SERIAL DRIVER M: "Maciej W. Rozycki" S: Maintained F: drivers/tty/serial/zs.* THE REST M: Linus Torvalds L: linux-kernel@vger.kernel.org Q: http://patchwork.kernel.org/project/LKML/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git S: Buried alive in reporters F: * F: */ compat-drivers-2012-09-18/patches/0000755000175000017500000000000012026176477016032 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/patches/network/0000755000175000017500000000000012026176533017514 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/patches/network/12-iw_handler-changes.patch0000644000175000017500000000107412026176511024475 0ustar mcgrofmcgrof--- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -6109,7 +6109,11 @@ static struct net_device *ipw2100_alloc_ netdev_attach_ops(dev, &ipw2100_netdev_ops); dev->ethtool_ops = &ipw2100_ethtool_ops; dev->wireless_handlers = &ipw2100_wx_handler_def; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) priv->wireless_data.libipw = priv->ieee; +#else + priv->wireless_data.ieee80211 = (struct ieee80211_device *) priv->ieee; +#endif dev->wireless_data = &priv->wireless_data; dev->watchdog_timeo = 3 * HZ; dev->irq = 0; compat-drivers-2012-09-18/patches/network/0002-net-misc.patch0000644000175000017500000000425112026176504022633 0ustar mcgrofmcgrofThese are things that removed in later kernels but no good explanatin was provided as to their removal. We should review if this is needed and if not remove these hunks. Pretty sure we can remove the netdev_tx_t hunk change on net/bluetooth/bnep/netdev.c, removing that hunk just needs to be compile tested against older kernels. --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -332,6 +332,11 @@ generic_rndis_bind(struct usbnet *dev, s u.init->major_version = cpu_to_le32(1); u.init->minor_version = cpu_to_le32(0); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) + /* can't we remove this? */ + net->change_mtu = NULL; +#endif + /* max transfer (in spec) is 0x4000 at full speed, but for * TX we'll stick to one Ethernet packet plus RNDIS framing. * For RX we handle drivers that zero-pad to end-of-packet. --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -979,6 +979,11 @@ static void ieee80211_if_setup(struct ne ether_setup(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; netdev_attach_ops(dev, &ieee80211_dataif_ops); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) + /* Do we need this ? */ + /* we will validate the address ourselves in ->open */ + dev->validate_addr = NULL; +#endif dev->destructor = free_netdev; } --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -162,8 +162,12 @@ static int bnep_net_proto_filter(struct } #endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static netdev_tx_t bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) +#else +static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) +#endif { struct bnep_session *s = netdev_priv(dev); struct sock *sk = s->sock->sk; --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -16,6 +16,14 @@ #define DRV_NAME "libertas" #endif +/* + * Really nasty hack to avoid stuffing compat.diff with tons of ifdefs, + * we could add this to a compat header file but too lazy to check ml_priv + * is not used anywhere else + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) +#define ml_priv priv +#endif #define LBS_DEB_ENTER 0x00000001 #define LBS_DEB_LEAVE 0x00000002 compat-drivers-2012-09-18/patches/network/56-mac80211-trace-fix.patch0000644000175000017500000000262212026176524024003 0ustar mcgrofmcgrof--- a/net/mac80211/trace.c +++ b/net/mac80211/trace.c @@ -15,12 +15,16 @@ void __sdata_info(const char *fmt, ...) struct va_format vaf = { .fmt = fmt, }; - va_list args; + va_list args, args2; va_start(args, fmt); - vaf.va = &args; + va_copy(args2, args); + vaf.va = &args2; pr_info("%pV", &vaf); + va_end(args2); + + vaf.va = &args; trace_mac80211_info(&vaf); va_end(args); } @@ -33,10 +37,16 @@ void __sdata_dbg(bool print, const char va_list args; va_start(args, fmt); - vaf.va = &args; - if (print) + if (print) { + va_list args2; + + va_copy(args2, args); + vaf.va = &args2; pr_debug("%pV", &vaf); + va_end(args2); + } + vaf.va = &args; trace_mac80211_dbg(&vaf); va_end(args); } @@ -46,12 +56,16 @@ void __sdata_err(const char *fmt, ...) struct va_format vaf = { .fmt = fmt, }; - va_list args; + va_list args, args2; va_start(args, fmt); - vaf.va = &args; + va_copy(args2, args); + vaf.va = &args2; pr_err("%pV", &vaf); + va_end(args2); + + vaf.va = &args; trace_mac80211_err(&vaf); va_end(args); } @@ -64,10 +78,16 @@ void __wiphy_dbg(struct wiphy *wiphy, bo va_list args; va_start(args, fmt); - vaf.va = &args; - if (print) - wiphy_dbg(wiphy, "%pV", &vaf); + if (print) { + va_list args2; + + va_copy(args2, args); + vaf.va = &args2; + pr_debug("%pV", &vaf); + va_end(args2); + } + vaf.va = &args; trace_mac80211_dbg(&vaf); va_end(args); } compat-drivers-2012-09-18/patches/network/37-vsnprintk.patch0000644000175000017500000000143012026176517023022 0ustar mcgrofmcgrof--- a/drivers/net/wireless/ath/main.c +++ b/drivers/net/wireless/ath/main.c @@ -59,6 +59,7 @@ struct sk_buff *ath_rxbuf_alloc(struct a } EXPORT_SYMBOL(ath_rxbuf_alloc); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) void ath_printk(const char *level, const struct ath_common* common, const char *fmt, ...) { @@ -78,4 +79,24 @@ void ath_printk(const char *level, const va_end(args); } +#else +void ath_printk(const char *level, const struct ath_common* common, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (common && common->hw && common->hw->wiphy) + printk("%sath: %s: ", + level, wiphy_name(common->hw->wiphy)); + else + printk("%sath: ", level); + + vprintk(fmt, args); + + va_end(args); +} +#endif + EXPORT_SYMBOL(ath_printk); compat-drivers-2012-09-18/patches/network/45-remove-platform-id-table.patch0000644000175000017500000000127712026176521025565 0ustar mcgrofmcgrof--- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1698,16 +1698,20 @@ static int __devinit wl12xx_probe(struct return wlcore_probe(wl, pdev); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) static const struct platform_device_id wl12xx_id_table[] __devinitconst = { { "wl12xx", 0 }, { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(platform, wl12xx_id_table); +#endif static struct platform_driver wl12xx_driver = { .probe = wl12xx_probe, .remove = __devexit_p(wlcore_remove), +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) .id_table = wl12xx_id_table, +#endif .driver = { .name = "wl12xx_driver", .owner = THIS_MODULE, compat-drivers-2012-09-18/patches/network/38-led-max-brightness.patch0000644000175000017500000000152112026176517024463 0ustar mcgrofmcgrof--- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -562,7 +562,9 @@ il_leds_init(struct il_priv *il) kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy)); il->led.brightness_set = il_led_brightness_set; il->led.blink_set = il_led_blink_set; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) il->led.max_brightness = 1; +#endif switch (mode) { case IL_LED_DEFAULT: --- a/drivers/net/wireless/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/iwlwifi/dvm/led.c @@ -187,7 +187,9 @@ void iwl_leds_init(struct iwl_priv *priv wiphy_name(priv->hw->wiphy)); priv->led.brightness_set = iwl_led_brightness_set; priv->led.blink_set = iwl_led_blink_set; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) priv->led.max_brightness = 1; +#endif switch (mode) { case IWL_LED_DEFAULT: compat-drivers-2012-09-18/patches/network/29-sdio_no_suspend.patch0000644000175000017500000001401612026176515024162 0ustar mcgrofmcgrofStarting with commit 66fceb69b72ff7e9cd8da2ca70033982d5376e0e "libertas: Added callback functions to support SDIO suspend/resume." libertas uses new functions from the in kernel sdio framework for suspend and resume that are not backported. --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -807,6 +807,7 @@ out: return ret; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); @@ -956,6 +957,17 @@ static int ath6kl_sdio_resume(struct ath return 0; } +#else +static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + return 0; +} + +static int ath6kl_sdio_resume(struct ath6kl *ar) +{ + return 0; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ /* set the window address register (using 4-byte register access ). */ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) @@ -1267,7 +1279,7 @@ static const struct ath6kl_hif_ops ath6k .stop = ath6kl_sdio_stop, }; -#ifdef CONFIG_PM_SLEEP +#if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* * Empty handlers so that mmc subsystem doesn't remove us entirely during @@ -1417,7 +1429,9 @@ static struct sdio_driver ath6kl_sdio_dr .id_table = ath6kl_sdio_devices, .probe = ath6kl_sdio_probe, .remove = ath6kl_sdio_remove, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .drv.pm = ATH6KL_SDIO_PM_OPS, +#endif }; static int __init ath6kl_sdio_init(void) --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -1320,6 +1320,7 @@ static void if_sdio_remove(struct sdio_f lbs_deb_leave(LBS_DEB_SDIO); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int if_sdio_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); @@ -1378,15 +1379,18 @@ static const struct dev_pm_ops if_sdio_p .suspend = if_sdio_suspend, .resume = if_sdio_resume, }; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ static struct sdio_driver if_sdio_driver = { .name = "libertas_sdio", .id_table = if_sdio_ids, .probe = if_sdio_probe, .remove = if_sdio_remove, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .drv = { .pm = &if_sdio_pm_ops, }, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ }; /*******************************************************************/ --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -129,8 +129,10 @@ mwifiex_sdio_remove(struct sdio_func *fu wait_for_completion(&adapter->fw_load); if (user_rmmod) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == @@ -147,6 +149,7 @@ mwifiex_sdio_remove(struct sdio_func *fu kfree(card); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* * SDIO suspend. * @@ -254,6 +257,7 @@ static int mwifiex_sdio_resume(struct de return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ /* Device ID for SD8786 */ #define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) @@ -272,10 +276,12 @@ static const struct sdio_device_id mwifi MODULE_DEVICE_TABLE(sdio, mwifiex_ids); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static const struct dev_pm_ops mwifiex_sdio_pm_ops = { .suspend = mwifiex_sdio_suspend, .resume = mwifiex_sdio_resume, }; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ static struct sdio_driver mwifiex_sdio = { .name = "mwifiex_sdio", @@ -284,7 +290,9 @@ static struct sdio_driver mwifiex_sdio = .remove = mwifiex_sdio_remove, .drv = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .pm = &mwifiex_sdio_pm_ops, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ } }; --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -73,7 +73,7 @@ static bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) { bool is_err = false; -#ifdef CONFIG_PM_SLEEP +#if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) is_err = atomic_read(&sdiodev->suspend); #endif return is_err; @@ -82,7 +82,7 @@ brcmf_pm_resume_error(struct brcmf_sdio_ static void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq) { -#ifdef CONFIG_PM_SLEEP +#if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) int retry = 0; while (atomic_read(&sdiodev->suspend) && retry++ != 30) wait_event_timeout(*wq, false, HZ/100); @@ -567,7 +567,7 @@ static void brcmf_ops_sdio_remove(struct } } -#ifdef CONFIG_PM_SLEEP +#if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int brcmf_sdio_suspend(struct device *dev) { mmc_pm_flag_t sdio_flags; @@ -617,7 +617,7 @@ static struct sdio_driver brcmf_sdmmc_dr .remove = brcmf_ops_sdio_remove, .name = "brcmfmac", .id_table = brcmf_sdmmc_ids, -#ifdef CONFIG_PM_SLEEP +#if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .drv = { .pm = &brcmf_sdio_pm_ops, }, --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1044,6 +1044,7 @@ static void btmrvl_sdio_remove(struct sd } } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int btmrvl_sdio_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); @@ -1139,6 +1140,7 @@ static const struct dev_pm_ops btmrvl_sd .suspend = btmrvl_sdio_suspend, .resume = btmrvl_sdio_resume, }; +#endif static struct sdio_driver bt_mrvl_sdio = { .name = "btmrvl_sdio", @@ -1147,7 +1149,9 @@ static struct sdio_driver bt_mrvl_sdio = .remove = btmrvl_sdio_remove, .drv = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .pm = &btmrvl_sdio_pm_ops, +#endif } }; compat-drivers-2012-09-18/patches/network/0003-netdev-needed_headroom_tailroom.patch0000644000175000017500000000233112026176504027405 0ustar mcgrofmcgrofThis is an optimization introduced on newer kernels, just ignore for older kernels on mac80211. For others the netdev->hard_header_len could be used. --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1463,6 +1463,7 @@ int ieee80211_if_add(struct ieee80211_lo return -ENOMEM; dev_net_set(ndev, wiphy_net(local->hw.wiphy)); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) ndev->needed_headroom = local->tx_headroom + 4*6 /* four MAC addresses */ + 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */ @@ -1471,6 +1472,7 @@ int ieee80211_if_add(struct ieee80211_lo - ETH_HLEN /* ethernet hard_header_len */ + IEEE80211_ENCRYPT_HEADROOM; ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM; +#endif ret = dev_alloc_name(ndev, ndev->name); if (ret < 0) { --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -2285,7 +2285,11 @@ int orinoco_if_add(struct orinoco_privat /* we use the default eth_mac_addr for setting the MAC addr */ /* Reserve space in skb for the SNAP header */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) dev->needed_headroom = ENCAPS_OVERHEAD; +#else + dev->hard_header_len += ENCAPS_OVERHEAD; +#endif netif_carrier_off(dev); compat-drivers-2012-09-18/patches/network/02-ksize.patch0000644000175000017500000000427112026176505022104 0ustar mcgrofmcgrofksize() was added as of 2.6.29, it gives you the actual size of the allocated data. Since we have no support for this we simply do not optimize for it and deal with large alloocations for the IEs. We technically could backport this as define ksize(bleh) SOME_LARGE_NUMBER but doing it this way emphasis careful review of the situation. --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -31,8 +31,22 @@ static int orinoco_set_key(struct orinoc enum orinoco_alg alg, const u8 *key, int key_len, const u8 *seq, int seq_len) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) + int len; + if (!unlikely(ZERO_OR_NULL_PTR(priv->keys[index].key))) { + len = priv->keys[index].key_len; + memset(priv->keys[index].key, 0, len); + kfree(priv->keys[index].key); + } + if (!unlikely(ZERO_OR_NULL_PTR(priv->keys[index].seq))) { + len = priv->keys[index].seq_len; + memset(priv->keys[index].seq, 0, len); + kfree(priv->keys[index].seq); + } +#else kzfree(priv->keys[index].key); kzfree(priv->keys[index].seq); +#endif if (key_len) { priv->keys[index].key = kzalloc(key_len, GFP_ATOMIC); --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -639,9 +639,14 @@ cfg80211_bss_update(struct cfg80211_regi size_t used = dev->wiphy.bss_priv_size + sizeof(*res); size_t ielen = res->pub.len_proberesp_ies; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) + if (0) { + used = 0; /* just to shut up the compiler */ +#else if (found->pub.proberesp_ies && !found->proberesp_ies_allocated && ksize(found) >= used + ielen) { +#endif memcpy(found->pub.proberesp_ies, res->pub.proberesp_ies, ielen); found->pub.len_proberesp_ies = ielen; @@ -675,9 +680,14 @@ cfg80211_bss_update(struct cfg80211_regi (found->pub.information_elements == found->pub.beacon_ies); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) + if (0) { + used = 0; /* just to shut up the compiler */ +#else if (found->pub.beacon_ies && !found->beacon_ies_allocated && ksize(found) >= used + ielen) { +#endif memcpy(found->pub.beacon_ies, res->pub.beacon_ies, ielen); found->pub.len_beacon_ies = ielen; compat-drivers-2012-09-18/patches/network/04-netns.patch0000644000175000017500000001016312026176506022106 0ustar mcgrofmcgrofThe only other namespace change. Note that pach 01-netdev.patch also has some other namespace changes there, look at that file for the other changes. It'd be nice to figure out a way to bring thise here cleanly and seprately but they touch the same files... --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5714,7 +5714,9 @@ static int nl80211_wiphy_netns(struct sk if (!net_eq(wiphy_net(&rdev->wiphy), net)) err = cfg80211_switch_netns(rdev, net); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) put_net(net); +#endif return err; } --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -167,6 +167,7 @@ int cfg80211_dev_rename(struct cfg80211_ return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, struct net *net) { @@ -211,6 +212,7 @@ int cfg80211_switch_netns(struct cfg8021 return 0; } +#endif static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { @@ -341,7 +343,9 @@ struct wiphy *wiphy_new(const struct cfg rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) wiphy_net_set(&rdev->wiphy, &init_net); +#endif rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), @@ -834,8 +838,10 @@ static int cfg80211_netdev_notifier_call wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wdev_list); rdev->devlist_generation++; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) /* can only change netns with wiphy */ dev->features |= NETIF_F_NETNS_LOCAL; +#endif if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { @@ -1027,6 +1033,7 @@ static struct notifier_block cfg80211_ne .notifier_call = cfg80211_netdev_notifier_call, }; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) static void __net_exit cfg80211_pernet_exit(struct net *net) { struct cfg80211_registered_device *rdev; @@ -1044,14 +1051,17 @@ static void __net_exit cfg80211_pernet_e static struct pernet_operations cfg80211_pernet_ops = { .exit = cfg80211_pernet_exit, }; +#endif static int __init cfg80211_init(void) { int err; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) err = register_pernet_device(&cfg80211_pernet_ops); if (err) goto out_fail_pernet; +#endif err = wiphy_sysfs_init(); if (err) @@ -1086,8 +1096,10 @@ out_fail_nl80211: out_fail_notifier: wiphy_sysfs_exit(); out_fail_sysfs: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) unregister_pernet_device(&cfg80211_pernet_ops); out_fail_pernet: +#endif return err; } subsys_initcall(cfg80211_init); @@ -1099,7 +1111,9 @@ static void __exit cfg80211_exit(void) unregister_netdevice_notifier(&cfg80211_netdev_notifier); wiphy_sysfs_exit(); regulatory_exit(); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) unregister_pernet_device(&cfg80211_pernet_ops); +#endif destroy_workqueue(cfg80211_wq); } module_exit(cfg80211_exit); --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -367,6 +367,7 @@ static int __init wireless_nlevent_init( subsys_initcall(wireless_nlevent_init); /* Process events generated by the wireless layer or the driver. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) static void wireless_nlevent_process(struct work_struct *work) { struct sk_buff *skb; @@ -382,6 +383,20 @@ static void wireless_nlevent_process(str rtnl_unlock(); } +#else +static void wireless_nlevent_process(struct work_struct *work) +{ + struct sk_buff *skb; + struct net *net; + + rtnl_lock(); + + while ((skb = skb_dequeue(&net->wext_nlevents))) + rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + + rtnl_unlock(); +} +#endif static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); --- a/net/wireless/wext-proc.c +++ b/net/wireless/wext-proc.c @@ -98,7 +98,11 @@ static void *wireless_dev_seq_start(stru return SEQ_START_TOKEN; off = 1; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) for_each_netdev(net, dev) +#else + for_each_netdev(net) +#endif if (off++ == *pos) return dev; return NULL; compat-drivers-2012-09-18/patches/network/09-threaded-irq.patch0000644000175000017500000000751112026176510023333 0ustar mcgrofmcgrofThe 2.6.31 kernel has threaded IRQ support and b43 is the first wireless driver that makes use of it. To support threaded IRSs on older kernels we built our own struct compat_threaded_irq to queue_work() onto it as the kernel thread be running the thread in process context as well. --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4212,8 +4212,13 @@ redo: if (b43_bus_host_is_sdio(dev->dev)) { b43_sdio_free_irq(dev); } else { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + compat_synchronize_threaded_irq(&dev->irq_compat); + compat_free_threaded_irq(&dev->irq_compat); +#else synchronize_irq(dev->dev->irq); free_irq(dev->dev->irq, dev); +#endif } mutex_lock(&wl->mutex); dev = wl->current_dev; @@ -4255,9 +4260,17 @@ static int b43_wireless_core_start(struc goto out; } } else { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + err = compat_request_threaded_irq(&dev->irq_compat, + dev->dev->irq, + b43_interrupt_handler, + b43_interrupt_thread_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); +#else err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, b43_interrupt_thread_handler, IRQF_SHARED, KBUILD_MODNAME, dev); +#endif if (err) { b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); @@ -5080,6 +5093,10 @@ static int b43_setup_bands(struct b43_wl static void b43_wireless_core_detach(struct b43_wldev *dev) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + if (dev->dev->sdev->bus->bustype != SSB_BUSTYPE_SDIO) + compat_destroy_threaded_irq(&dev->irq_compat); +#endif /* We release firmware that late to not be required to re-request * is all the time when we reinit the core. */ b43_release_firmware(dev); --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -859,6 +859,9 @@ struct b43_wldev { unsigned int tx_count; unsigned int rx_count; #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + struct compat_threaded_irq irq_compat; +#endif }; /* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5540,14 +5540,25 @@ int __devinit wlcore_probe(struct wl1271 platform_set_drvdata(pdev, wl); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) + irqflags = IRQF_TRIGGER_RISING; +#else if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) irqflags = IRQF_TRIGGER_RISING; else irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + ret = compat_request_threaded_irq(&wl->irq_compat, wl->irq, + wl12xx_hardirq, wlcore_irq, + irqflags, + pdev->name, wl); +#else ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wlcore_irq, irqflags, pdev->name, wl); +#endif if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); goto out_free_hw; @@ -5621,7 +5632,11 @@ out_unreg: wl1271_unregister_hw(wl); out_irq: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + compat_free_threaded_irq(&wl->irq_compat); +#else free_irq(wl->irq, wl); +#endif out_free_hw: wlcore_free_hw(wl); @@ -5640,7 +5655,12 @@ int __devexit wlcore_remove(struct platf disable_irq_wake(wl->irq); } wl1271_unregister_hw(wl); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + compat_free_threaded_irq(&wl->irq_compat); + compat_destroy_threaded_irq(&wl->irq_compat); +#else free_irq(wl->irq, wl); +#endif wlcore_free_hw(wl); return 0; --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -141,7 +141,9 @@ struct wl1271_stats { struct wl1271 { struct ieee80211_hw *hw; bool mac80211_registered; - +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + struct compat_threaded_irq irq_compat; +#endif struct device *dev; void *if_priv; compat-drivers-2012-09-18/patches/network/99-change-makefiles.patch0000644000175000017500000000674012026176526024170 0ustar mcgrofmcgrof This patch removes all drivers we do not support or do not want to support. This lets us build only the wireless stuff. --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,7 +1 @@ -obj-$(CONFIG_EEPROM_AT24) += at24.o -obj-$(CONFIG_EEPROM_AT25) += at25.o -obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o -obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o -obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o -obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -2,33 +2,6 @@ # Makefile for USB Network drivers # -obj-$(CONFIG_USB_CATC) += catc.o -obj-$(CONFIG_USB_KAWETH) += kaweth.o -obj-$(CONFIG_USB_PEGASUS) += pegasus.o -obj-$(CONFIG_USB_RTL8150) += rtl8150.o -obj-$(CONFIG_USB_HSO) += hso.o -obj-$(CONFIG_USB_NET_AX8817X) += asix.o -asix-y := asix_devices.o asix_common.o ax88172a.o obj-$(CONFIG_USB_NET_COMPAT_CDCETHER) += cdc_ether.o -obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o -obj-$(CONFIG_USB_NET_DM9601) += dm9601.o -obj-$(CONFIG_USB_NET_SMSC75XX) += smsc75xx.o -obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95xx.o -obj-$(CONFIG_USB_NET_GL620A) += gl620a.o -obj-$(CONFIG_USB_NET_NET1080) += net1080.o -obj-$(CONFIG_USB_NET_PLUSB) += plusb.o obj-$(CONFIG_USB_NET_COMPAT_RNDIS_HOST) += rndis_host.o -obj-$(CONFIG_USB_NET_CDC_SUBSET) += cdc_subset.o -obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o -obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o obj-$(CONFIG_USB_COMPAT_USBNET) += usbnet.o -obj-$(CONFIG_USB_NET_INT51X1) += int51x1.o -obj-$(CONFIG_USB_CDC_PHONET) += cdc-phonet.o -obj-$(CONFIG_USB_NET_KALMIA) += kalmia.o -obj-$(CONFIG_USB_IPHETH) += ipheth.o -obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o -obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o -obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o -obj-$(CONFIG_USB_VL600) += lg-vl600.o -obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o - --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -7,18 +7,8 @@ obj-$(CONFIG_IPW2200) += ipw2x00/ obj-$(CONFIG_HERMES) += orinoco/ -obj-$(CONFIG_AIRO) += airo.o -obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o - -obj-$(CONFIG_ATMEL) += atmel.o -obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o -obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o - obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o -obj-$(CONFIG_PRISM54) += prism54/ - -obj-$(CONFIG_HOSTAP) += hostap/ obj-$(CONFIG_B43) += b43/ obj-$(CONFIG_B43LEGACY) += b43legacy/ obj-$(CONFIG_COMPAT_ZD1211RW) += zd1211rw/ @@ -26,13 +16,8 @@ obj-$(CONFIG_RTL8180) += rtl818x/ obj-$(CONFIG_RTL8187) += rtl818x/ obj-$(CONFIG_RTLWIFI) += rtlwifi/ -# 16-bit wireless PCMCIA client drivers -obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o -obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o - obj-$(CONFIG_USB_NET_COMPAT_RNDIS_WLAN) += rndis_wlan.o -obj-$(CONFIG_USB_ZD1201) += zd1201.o obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -4,11 +4,6 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib8 obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -obj-$(CONFIG_WEXT_CORE) += wext-core.o -obj-$(CONFIG_WEXT_PROC) += wext-proc.o -obj-$(CONFIG_WEXT_SPY) += wext-spy.o -obj-$(CONFIG_WEXT_PRIV) += wext-priv.o - cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o compat-drivers-2012-09-18/patches/network/18-rename-usb-net-symbols.patch0000644000175000017500000000420312026176513025270 0ustar mcgrofmcgrofRename config names for usbnet to deactivate them also if activated in the main kernel configuration. This is needed because usb_autopm_put_interface_async and usb_autopm_get_interface_async are not backported to kernel 2.6.28 and earlier. Remove this patch if these symbols are backported. --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_USB_RTL8150) += rtl8150.o obj-$(CONFIG_USB_HSO) += hso.o obj-$(CONFIG_USB_NET_AX8817X) += asix.o asix-y := asix_devices.o asix_common.o ax88172a.o -obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o +obj-$(CONFIG_USB_NET_COMPAT_CDCETHER) += cdc_ether.o obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o obj-$(CONFIG_USB_NET_DM9601) += dm9601.o obj-$(CONFIG_USB_NET_SMSC75XX) += smsc75xx.o @@ -17,11 +17,11 @@ obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95 obj-$(CONFIG_USB_NET_GL620A) += gl620a.o obj-$(CONFIG_USB_NET_NET1080) += net1080.o obj-$(CONFIG_USB_NET_PLUSB) += plusb.o -obj-$(CONFIG_USB_NET_RNDIS_HOST) += rndis_host.o +obj-$(CONFIG_USB_NET_COMPAT_RNDIS_HOST) += rndis_host.o obj-$(CONFIG_USB_NET_CDC_SUBSET) += cdc_subset.o obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o -obj-$(CONFIG_USB_USBNET) += usbnet.o +obj-$(CONFIG_USB_COMPAT_USBNET) += usbnet.o obj-$(CONFIG_USB_NET_INT51X1) += int51x1.o obj-$(CONFIG_USB_CDC_PHONET) += cdc-phonet.o obj-$(CONFIG_USB_NET_KALMIA) += kalmia.o --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -33,7 +33,7 @@ #include -#if defined(CONFIG_USB_NET_RNDIS_HOST) || defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) +#if defined(CONFIG_USB_NET_COMPAT_RNDIS_HOST) || defined(CONFIG_USB_NET_COMPAT_RNDIS_HOST_MODULE) static int is_rndis(struct usb_interface_descriptor *desc) { --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -30,7 +30,7 @@ obj-$(CONFIG_RTLWIFI) += rtlwifi/ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o -obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o +obj-$(CONFIG_USB_NET_COMPAT_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_USB_ZD1201) += zd1201.o obj-$(CONFIG_LIBERTAS) += libertas/ compat-drivers-2012-09-18/patches/network/07-change-default-rate-alg.patch0000644000175000017500000000307212026176507025325 0ustar mcgrofmcgrof Your current kernels configuration (.config and linux/autoconf.h) are always respected when compiling external modules. Because of this if you are using an old kernel which preferred the PID rate control algorithm we cannot force it to use minstrel instead. Minstrel is now the default rate control algorithm and we want you to use it. To let you use it we redefine here the CONFIG_MAC80211_RC_DEFAULT to CONFIG_COMPAT_MAC80211_RC_DEFAULT and define CONFIG_COMPAT_MAC80211_RC_DEFAULT on config.mk. Through the compat autoconf we then get it also defined there at compilation time. --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -24,7 +24,7 @@ struct rate_control_alg { static LIST_HEAD(rate_ctrl_algs); static DEFINE_MUTEX(rate_ctrl_mutex); -static char *ieee80211_default_rc_algo = CONFIG_MAC80211_RC_DEFAULT; +static char *ieee80211_default_rc_algo = CONFIG_COMPAT_MAC80211_RC_DEFAULT; module_param(ieee80211_default_rc_algo, charp, 0644); MODULE_PARM_DESC(ieee80211_default_rc_algo, "Default rate control algorithm for mac80211 to use"); @@ -120,8 +120,8 @@ ieee80211_rate_control_ops_get(const cha ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); /* try built-in one if specific alg requested but not found */ - if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT)) - ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT); + if (!ops && strlen(CONFIG_COMPAT_MAC80211_RC_DEFAULT)) + ops = ieee80211_try_rate_control_ops_get(CONFIG_COMPAT_MAC80211_RC_DEFAULT); kparam_unblock_sysfs_write(ieee80211_default_rc_algo); return ops; compat-drivers-2012-09-18/patches/network/0005-netlink-portid.patch0000644000175000017500000002350512026176505024066 0ustar mcgrofmcgrofThe patch: commit 15e473046cb6e5d18a4d0057e61d76315230382b Author: Eric W. Biederman Date: Fri Sep 7 20:12:54 2012 +0000 netlink: Rename pid to portid to avoid confusion It is a frequent mistake to confuse the netlink port identifier with a process identifier. Try to reduce this confusion by renaming fields that hold port identifiers portid instead of pid. I have carefully avoided changing the structures exported to userspace to avoid changing the userspace API. I have successfully built an allyesconfig kernel with this change. Signed-off-by: "Eric W. Biederman" Acked-by: Stephen Hemminger Signed-off-by: David S. Miller Changed the struct members: struct netlink_notify->pid to struct netlink_notify->portid struct genl_info->snd_pid to struct genl_info->snd_portid To help backport this and not have to #ifdef around it against kernel versions compat has introduced two helpers for us to simply do the backport with two macro helpers: genl_info_snd_portid() netlink_notify_portid() This takes care of the work for us requiring only one single line change. If we get another patch thrown into this file then I suspect we can extract SMPL out of it and use it to backport further collateral evolutions like this one should other drivers / subsystem need this change. --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1632,10 +1632,10 @@ static int hwsim_register_received_nl(st if (info == NULL) goto out; - wmediumd_portid = info->snd_portid; + wmediumd_portid = genl_info_snd_portid(info); printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, " - "switching to wmediumd mode with pid %d\n", info->snd_portid); + "switching to wmediumd mode with pid %d\n", genl_info_snd_portid(info)); return 0; out: @@ -1672,7 +1672,7 @@ static int mac80211_hwsim_netlink_notify if (state != NETLINK_URELEASE) return NOTIFY_DONE; - if (notify->portid == wmediumd_portid) { + if (netlink_notify_portid(notify) == wmediumd_portid) { printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink" " socket, switching to perfect channel medium\n"); wmediumd_portid = 0; --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1267,7 +1267,7 @@ static int nl80211_dump_wiphy(struct sk_ continue; if (++idx <= start) continue; - if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid, + if (nl80211_send_wiphy(skb, NETLINK_CB_PORTID(skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) { idx--; @@ -1290,7 +1290,7 @@ static int nl80211_get_wiphy(struct sk_b if (!msg) return -ENOMEM; - if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) { + if (nl80211_send_wiphy(msg, genl_info_snd_portid(info), info->snd_seq, 0, dev) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -1807,7 +1807,7 @@ static int nl80211_dump_interface(struct if_idx++; continue; } - if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid, + if (nl80211_send_iface(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev) < 0) { mutex_unlock(&rdev->devlist_mtx); @@ -1838,7 +1838,7 @@ static int nl80211_get_interface(struct if (!msg) return -ENOMEM; - if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, + if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0, dev, wdev) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -2056,7 +2056,7 @@ static int nl80211_new_interface(struct break; } - if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, + if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0, rdev, wdev) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -2191,7 +2191,7 @@ static int nl80211_get_key(struct sk_buf if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_NEW_KEY); if (IS_ERR(hdr)) return PTR_ERR(hdr); @@ -2931,7 +2931,7 @@ static int nl80211_dump_station(struct s goto out_err; if (nl80211_send_station(skb, - NETLINK_CB(cb->skb).portid, + NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, dev, netdev, mac_addr, &sinfo) < 0) @@ -2977,7 +2977,7 @@ static int nl80211_get_station(struct sk if (!msg) return -ENOMEM; - if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0, + if (nl80211_send_station(msg, genl_info_snd_portid(info), info->snd_seq, 0, rdev, dev, mac_addr, &sinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -3389,7 +3389,7 @@ static int nl80211_dump_mpath(struct sk_ if (err) goto out_err; - if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid, + if (nl80211_send_mpath(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, netdev, dst, next_hop, &pinfo) < 0) @@ -3438,7 +3438,7 @@ static int nl80211_get_mpath(struct sk_b if (!msg) return -ENOMEM; - if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0, + if (nl80211_send_mpath(msg, genl_info_snd_portid(info), info->snd_seq, 0, dev, dst, next_hop, &pinfo) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -3679,7 +3679,7 @@ static int nl80211_get_mesh_config(struc msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_MESH_CONFIG); if (!hdr) goto out; @@ -3998,7 +3998,7 @@ static int nl80211_get_reg(struct sk_buf goto out; } - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_REG); if (!hdr) goto put_failure; @@ -4616,7 +4616,7 @@ static int nl80211_send_bss(struct sk_bu ASSERT_WDEV_LOCK(wdev); - hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags, + hdr = nl80211hdr_put(msg, NETLINK_CB_PORTID(cb->skb), seq, flags, NL80211_CMD_NEW_SCAN_RESULTS); if (!hdr) return -1; @@ -4836,7 +4836,7 @@ static int nl80211_dump_survey(struct sk } if (nl80211_send_survey(skb, - NETLINK_CB(cb->skb).portid, + NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, netdev, &survey) < 0) @@ -5451,7 +5451,7 @@ static int nl80211_testmode_dump(struct } while (1) { - void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, + void *hdr = nl80211hdr_put(skb, NETLINK_CB_PORTID(cb->skb), cb->nlh->nlmsg_seq, NLM_F_MULTI, NL80211_CMD_TESTMODE); struct nlattr *tmdata; @@ -5531,7 +5531,7 @@ struct sk_buff *cfg80211_testmode_alloc_ return NULL; return __cfg80211_testmode_alloc_skb(rdev, approxlen, - rdev->testmode_info->snd_portid, + genl_info_snd_portid(rdev->testmode_info), rdev->testmode_info->snd_seq, GFP_KERNEL); } @@ -5869,7 +5869,7 @@ static int nl80211_remain_on_channel(str if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_REMAIN_ON_CHANNEL); if (IS_ERR(hdr)) { @@ -6088,7 +6088,7 @@ static int nl80211_register_mgmt(struct if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; - return cfg80211_mlme_register_mgmt(wdev, info->snd_portid, frame_type, + return cfg80211_mlme_register_mgmt(wdev, genl_info_snd_portid(info), frame_type, nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); } @@ -6169,7 +6169,7 @@ static int nl80211_tx_mgmt(struct sk_buf if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_FRAME); if (IS_ERR(hdr)) { @@ -6286,7 +6286,7 @@ static int nl80211_get_power_save(struct if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_POWER_SAVE); if (!hdr) { err = -ENOBUFS; @@ -6488,7 +6488,7 @@ static int nl80211_get_wowlan(struct sk_ if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_GET_WOWLAN); if (!hdr) goto nla_put_failure; @@ -6765,7 +6765,7 @@ static int nl80211_register_unexpected_f if (wdev->ap_unexpected_nlportid) return -EBUSY; - wdev->ap_unexpected_nlportid = info->snd_portid; + wdev->ap_unexpected_nlportid = genl_info_snd_portid(info); return 0; } @@ -6795,7 +6795,7 @@ static int nl80211_probe_client(struct s if (!msg) return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq, 0, NL80211_CMD_PROBE_CLIENT); if (IS_ERR(hdr)) { @@ -6833,7 +6833,7 @@ static int nl80211_register_beacons(stru if (rdev->ap_beacons_nlportid) return -EBUSY; - rdev->ap_beacons_nlportid = info->snd_portid; + rdev->ap_beacons_nlportid = genl_info_snd_portid(info); return 0; } @@ -8855,8 +8855,8 @@ static int nl80211_netlink_notify(struct list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) - cfg80211_mlme_unregister_socket(wdev, notify->portid); - if (rdev->ap_beacons_nlportid == notify->portid) + cfg80211_mlme_unregister_socket(wdev, netlink_notify_portid(notify)); + if (rdev->ap_beacons_nlportid == netlink_notify_portid(notify)) rdev->ap_beacons_nlportid = 0; } compat-drivers-2012-09-18/patches/network/11-dev-pm-ops.patch0000644000175000017500000002777012026176511022754 0ustar mcgrofmcgrofThe 2.6.29 kernel has new struct dev_pm_ops [1] which are used on the pci device to distinguish power management hooks for suspend to RAM and hibernation. Older kernels don't have these so we need to resort back to the good ol' suspend/resume. Fortunately the calls are not so different so it should be possible to resuse the same calls on compat code with only slight modifications. [1] http://lxr.linux.no/#linux+v2.6.29/include/linux/pm.h#L170 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -257,6 +257,9 @@ static int bcma_host_pci_resume(struct d return bcma_bus_resume(bus); } +compat_pci_suspend(bcma_host_pci_suspend) +compat_pci_resume(bcma_host_pci_resume) + static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend, bcma_host_pci_resume); #define BCMA_PM_OPS (&bcma_pm_ops) @@ -284,7 +287,12 @@ static struct pci_driver bcma_pci_bridge .id_table = bcma_pci_bridge_tbl, .probe = bcma_host_pci_probe, .remove = __devexit_p(bcma_host_pci_remove), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = BCMA_PM_OPS, +#elif defined(CONFIG_PM) + .suspend = bcma_host_pci_suspend_compat, + .resume = bcma_host_pci_resume_compat, +#endif }; int __init bcma_host_pci_init(void) --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -2691,6 +2691,9 @@ static const struct pci_error_handlers a .resume = atl1c_io_resume, }; +compat_pci_suspend(atl1c_suspend) +compat_pci_resume(atl1c_resume) + static SIMPLE_DEV_PM_OPS(atl1c_pm_ops, atl1c_suspend, atl1c_resume); static struct pci_driver atl1c_driver = { @@ -2700,7 +2703,12 @@ static struct pci_driver atl1c_driver = .remove = __devexit_p(atl1c_remove), .shutdown = atl1c_shutdown, .err_handler = &atl1c_err_handler, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &atl1c_pm_ops, +#elif defined(CONFIG_PM_SLEEP) + .suspend = atl1c_suspend_compat, + .resume = atl1c_resume_compat, +#endif }; /** --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -2877,6 +2877,9 @@ static int atl1_resume(struct device *de return 0; } +compat_pci_suspend(atl1_suspend) +compat_pci_resume(atl1_resume) + static SIMPLE_DEV_PM_OPS(atl1_pm_ops, atl1_suspend, atl1_resume); #define ATL1_PM_OPS (&atl1_pm_ops) @@ -3148,7 +3151,12 @@ static struct pci_driver atl1_driver = { .probe = atl1_probe, .remove = __devexit_p(atl1_remove), .shutdown = atl1_shutdown, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATL1_PM_OPS, +#elif defined(CONFIG_PM_SLEEP) + .suspend = atl1_suspend_compat, + .resume = atl1_resume_compat, +#endif }; /** --- a/drivers/net/wireless/ath/ath5k/pci.c +++ b/drivers/net/wireless/ath/ath5k/pci.c @@ -326,6 +326,9 @@ static int ath5k_pci_resume(struct devic return 0; } +compat_pci_suspend(ath5k_pci_suspend) +compat_pci_resume(ath5k_pci_resume) + static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); #define ATH5K_PM_OPS (&ath5k_pm_ops) #else @@ -337,7 +340,12 @@ static struct pci_driver ath5k_pci_drive .id_table = ath5k_pci_id_table, .probe = ath5k_pci_probe, .remove = __devexit_p(ath5k_pci_remove), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATH5K_PM_OPS, +#elif defined(CONFIG_PM_SLEEP) + .suspend = ath5k_pci_suspend_compat, + .resume = ath5k_pci_resume_compat, +#endif }; module_pci_driver(ath5k_pci_driver); --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -337,14 +337,10 @@ static int ath_pci_resume(struct device return 0; } -static const struct dev_pm_ops ath9k_pm_ops = { - .suspend = ath_pci_suspend, - .resume = ath_pci_resume, - .freeze = ath_pci_suspend, - .thaw = ath_pci_resume, - .poweroff = ath_pci_suspend, - .restore = ath_pci_resume, -}; +compat_pci_suspend(ath_pci_suspend) +compat_pci_resume(ath_pci_resume) + +static SIMPLE_DEV_PM_OPS(ath9k_pm_ops, ath_pci_suspend, ath_pci_resume); #define ATH9K_PM_OPS (&ath9k_pm_ops) @@ -362,7 +358,12 @@ static struct pci_driver ath_pci_driver .id_table = ath_pci_id_table, .probe = ath_pci_probe, .remove = ath_pci_remove, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATH9K_PM_OPS, +#elif defined(CONFIG_PM) + .suspend = ath_pci_suspend_compat, + .resume = ath_pci_resume_compat, +#endif }; int ath_pci_init(void) --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3885,7 +3885,12 @@ static struct pci_driver il3945_driver = .id_table = il3945_hw_card_ids, .probe = il3945_pci_probe, .remove = __devexit_p(il3945_pci_remove), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = IL_LEGACY_PM_OPS, +#elif defined(CONFIG_PM) + .suspend = il_pci_suspend_compat, + .resume = il_pci_resume_compat, +#endif }; static int __init --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -6773,7 +6773,12 @@ static struct pci_driver il4965_driver = .id_table = il4965_hw_card_ids, .probe = il4965_pci_probe, .remove = __devexit_p(il4965_pci_remove), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = IL_LEGACY_PM_OPS, +#elif defined(CONFIG_PM) + .suspend = il_pci_suspend_compat, + .resume = il_pci_resume_compat, +#endif }; static int __init --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -4906,8 +4906,17 @@ il_pci_resume(struct device *device) return 0; } +compat_pci_suspend(il_pci_suspend) +compat_pci_resume(il_pci_resume) + SIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) EXPORT_SYMBOL(il_pm_ops); +#else +EXPORT_SYMBOL(il_pci_suspend_compat); +EXPORT_SYMBOL(il_pci_resume_compat); +#endif #endif /* CONFIG_PM */ --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1843,7 +1843,14 @@ __le32 il_add_beacon_time(struct il_priv u32 beacon_interval); #ifdef CONFIG_PM +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) +int il_pci_suspend_compat(struct pci_dev *pdev, pm_message_t state); +int il_pci_resume_compat(struct pci_dev *pdev); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) +extern struct dev_pm_ops il_pm_ops; +#else extern const struct dev_pm_ops il_pm_ops; +#endif #define IL_LEGACY_PM_OPS (&il_pm_ops) --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -343,6 +343,9 @@ static int iwl_pci_resume(struct device return iwl_trans_resume(iwl_trans); } +compat_pci_suspend(iwl_pci_suspend) +compat_pci_resume(iwl_pci_resume) + static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); #define IWL_PM_OPS (&iwl_dev_pm_ops) @@ -367,7 +370,12 @@ static struct pci_driver iwl_pci_driver .id_table = iwl_hw_card_ids, .probe = iwl_pci_probe, .remove = __devexit_p(iwl_pci_remove), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = IWL_PM_OPS, +#elif defined(CONFIG_PM) + .suspend = iwl_pci_suspend_compat, + .resume = iwl_pci_resume_compat, +#endif }; int __must_check iwl_pci_register_driver(void) --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -1249,6 +1249,7 @@ static int __devexit libertas_spi_remove return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) static int if_spi_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); @@ -1282,6 +1283,7 @@ static const struct dev_pm_ops if_spi_pm .suspend = if_spi_suspend, .resume = if_spi_resume, }; +#endif static struct spi_driver libertas_spi_driver = { .probe = if_spi_probe, @@ -1289,7 +1291,9 @@ static struct spi_driver libertas_spi_dr .driver = { .name = "libertas_spi", .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .pm = &if_spi_pm_ops, +#endif }, }; --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -374,21 +374,22 @@ MODULE_PARM_DESC(swlps, "Set to 1 to use MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); -static const struct dev_pm_ops rtlwifi_pm_ops = { - .suspend = rtl_pci_suspend, - .resume = rtl_pci_resume, - .freeze = rtl_pci_suspend, - .thaw = rtl_pci_resume, - .poweroff = rtl_pci_suspend, - .restore = rtl_pci_resume, -}; +compat_pci_suspend(rtl_pci_suspend) +compat_pci_resume(rtl_pci_resume) + +static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); static struct pci_driver rtl92ce_driver = { .name = KBUILD_MODNAME, .id_table = rtl92ce_pci_ids, .probe = rtl_pci_probe, .remove = rtl_pci_disconnect, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &rtlwifi_pm_ops, +#elif defined(CONFIG_PM) + .suspend = rtl_pci_suspend_compat, + .resume = rtl_pci_resume_compat, +#endif }; module_pci_driver(rtl92ce_driver); --- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c @@ -378,21 +378,22 @@ MODULE_PARM_DESC(swlps, "Set to 1 to use MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); -static const struct dev_pm_ops rtlwifi_pm_ops = { - .suspend = rtl_pci_suspend, - .resume = rtl_pci_resume, - .freeze = rtl_pci_suspend, - .thaw = rtl_pci_resume, - .poweroff = rtl_pci_suspend, - .restore = rtl_pci_resume, -}; +compat_pci_suspend(rtl_pci_suspend) +compat_pci_resume(rtl_pci_resume) + +static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); static struct pci_driver rtl92de_driver = { .name = KBUILD_MODNAME, .id_table = rtl92de_pci_ids, .probe = rtl_pci_probe, .remove = rtl_pci_disconnect, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &rtlwifi_pm_ops, +#elif defined(CONFIG_PM) + .suspend = rtl_pci_suspend_compat, + .resume = rtl_pci_resume_compat, +#endif }; /* add global spin lock to solve the problem that --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -432,21 +432,22 @@ MODULE_PARM_DESC(swlps, "Set to 1 to use MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); -static const struct dev_pm_ops rtlwifi_pm_ops = { - .suspend = rtl_pci_suspend, - .resume = rtl_pci_resume, - .freeze = rtl_pci_suspend, - .thaw = rtl_pci_resume, - .poweroff = rtl_pci_suspend, - .restore = rtl_pci_resume, -}; +compat_pci_suspend(rtl_pci_suspend) +compat_pci_resume(rtl_pci_resume) + +static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); static struct pci_driver rtl92se_driver = { .name = KBUILD_MODNAME, .id_table = rtl92se_pci_ids, .probe = rtl_pci_probe, .remove = rtl_pci_disconnect, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = &rtlwifi_pm_ops, +#elif defined(CONFIG_PM) + .suspend = rtl_pci_suspend_compat, + .resume = rtl_pci_resume_compat, +#endif }; module_pci_driver(rtl92se_driver); --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -681,14 +681,10 @@ static int p54p_resume(struct device *de return pci_set_power_state(pdev, PCI_D0); } -static const struct dev_pm_ops p54pci_pm_ops = { - .suspend = p54p_suspend, - .resume = p54p_resume, - .freeze = p54p_suspend, - .thaw = p54p_resume, - .poweroff = p54p_suspend, - .restore = p54p_resume, -}; +compat_pci_suspend(p54p_suspend) +compat_pci_resume(p54p_resume) + +static SIMPLE_DEV_PM_OPS(p54pci_pm_ops, p54p_suspend, p54p_resume); #define P54P_PM_OPS (&p54pci_pm_ops) #else @@ -700,7 +696,12 @@ static struct pci_driver p54p_driver = { .id_table = p54p_table, .probe = p54p_probe, .remove = __devexit_p(p54p_remove), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = P54P_PM_OPS, +#elif defined(CONFIG_PM) + .suspend = p54p_suspend_compat, + .resume = p54p_resume_compat, +#endif }; module_pci_driver(p54p_driver); compat-drivers-2012-09-18/patches/network/15-symbol-export-conflicts.patch0000644000175000017500000000102312026176512025557 0ustar mcgrofmcgrofIn kernel < 2.6.32 libipw also exports ieee80211_rx. To avoid conflicts with the other export we rename our. --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3104,7 +3104,12 @@ void ieee80211_rx(struct ieee80211_hw *h drop: kfree_skb(skb); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) EXPORT_SYMBOL(ieee80211_rx); +#else +EXPORT_SYMBOL(mac80211_ieee80211_rx); +#endif + /* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ compat-drivers-2012-09-18/patches/network/25-multicast-list_head.patch0000644000175000017500000005725312026176514024733 0ustar mcgrofmcgrofBackport commit 22bedad3ce112d5ca1eaf043d4990fa2ed698c87: net: convert multicast list to list_head Converts the list and the core manipulating with it to be the same as uc_list. +uses two functions for adding/removing mc address (normal and "global" variant) instead of a function parameter. +removes dev_mcast.c completely. +exposes netdev_hw_addr_list_* macros along with __hw_addr_* functions for manipulation with lists on a sandbox (used in bonding and 80211 drivers) This also backport commit 2f787b0b76bf5de2eaa3ca3a29d89123ae03c856 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -410,7 +410,11 @@ static void atl1c_set_multi(struct net_d /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atl1c_hash_mc_addr(hw, ha->addr); +#else + hash_value = atl1c_hash_mc_addr(hw, ha->dmi_addr); +#endif atl1c_hash_set(hw, hash_value); } } --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -308,7 +308,11 @@ static void atl1e_set_multi(struct net_d /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atl1e_hash_mc_addr(hw, ha->addr); +#else + hash_value = atl1e_hash_mc_addr(hw, ha->dmi_addr); +#endif atl1e_hash_set(hw, hash_value); } } --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -159,7 +159,11 @@ static void atl2_set_multi(struct net_de /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atl2_hash_mc_addr(hw, ha->addr); +#else + hash_value = atl2_hash_mc_addr(hw, ha->dmi_addr); +#endif atl2_hash_set(hw, hash_value); } } --- a/drivers/net/ethernet/atheros/atlx/atlx.c +++ b/drivers/net/ethernet/atheros/atlx/atlx.c @@ -150,7 +150,11 @@ static void atlx_set_multi(struct net_de /* compute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hash_value = atlx_hash_mc_addr(hw, ha->addr); +#else + hash_value = atlx_hash_mc_addr(hw, ha->dmi_addr); +#endif atlx_hash_set(hw, hash_value); } } --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -1696,7 +1696,11 @@ static int __b44_load_mcast(struct b44 * netdev_for_each_mc_addr(ha, dev) { if (i == num_ents) break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __b44_cam_write(bp, ha->addr, i++ + 1); +#else + __b44_cam_write(bp, ha->dmi_addr, i++ + 1); +#endif } return i+1; } --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1320,19 +1320,37 @@ static void adm8211_bss_info_changed(str } static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *ha) +#endif { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) unsigned int bit_nr; - u32 mc_filter[2]; struct netdev_hw_addr *ha; +#else + unsigned int bit_nr, i; +#endif + u32 mc_filter[2]; mc_filter[1] = mc_filter[0] = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; +#else + for (i = 0; i < mc_count; i++) { + if (!ha) + break; + bit_nr = ether_crc(ETH_ALEN, ha->dmi_addr) >> 26; +#endif bit_nr &= 0x3F; mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + ha = ha->next; +#endif } return mc_filter[0] | ((u64)(mc_filter[1]) << 32); --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -318,20 +318,42 @@ ath5k_bss_info_changed(struct ieee80211_ static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *ha) +#endif { u32 mfilt[2], val; u8 pos; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; +#else + int i; +#endif mfilt[0] = 0; mfilt[1] = 1; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { +#else + for (i = 0; i < mc_count; i++) { + if (!ha) + break; +#endif /* calculate XOR of eight 6-bit values */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) val = get_unaligned_le32(ha->addr + 0); +#else + val = get_unaligned_le32(ha->dmi_addr + 0); +#endif pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) val = get_unaligned_le32(ha->addr + 3); +#else + val = get_unaligned_le32(ha->dmi_addr + 3); +#endif pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; pos &= 0x3f; mfilt[pos / 32] |= (1 << (pos % 32)); @@ -340,6 +362,9 @@ ath5k_prepare_multicast(struct ieee80211 * need to inform below not to reset the mcast */ /* ath5k_hw_set_mcast_filterindex(ah, * ha->addr[5]); */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + ha = ha->next; +#endif } return ((u64)(mfilt[1]) << 32) | mfilt[0]; --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -899,17 +899,35 @@ out: return err; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) +#else +static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *ha) +#endif { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; +#else + int i; +#endif u64 mchash; /* always get broadcast frames */ mchash = 1ULL << (0xff >> 2); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) mchash |= 1ULL << (ha->addr[5] >> 2); +#else + for (i = 0; i < mc_count; i++) { + if (WARN_ON(!ha)) + break; + mchash |= 1ULL << (ha->dmi_addr[5] >> 2); + ha = ha->next; + } +#endif return mchash; } --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -349,18 +349,34 @@ static int lbs_add_mcast_addrs(struct cm netif_addr_lock_bh(dev); cnt = netdev_mc_count(dev); netdev_for_each_mc_addr(ha, dev) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { +#else + if (mac_in_list(cmd->maclist, nr_addrs, ha->dmi_addr)) { +#endif lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr); +#else + ha->dmi_addr); +#endif cnt--; continue; } if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); +#else + memcpy(&cmd->maclist[6*i], ha->dmi_addr, ETH_ALEN); +#endif lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr); +#else + ha->dmi_addr); +#endif i++; cnt--; } --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -421,20 +421,36 @@ static int lbtf_op_config(struct ieee802 } static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *ha) +#endif { struct lbtf_private *priv = hw->priv; int i; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; int mc_count = netdev_hw_addr_list_count(mc_list); +#endif if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) return mc_count; priv->nr_of_multicastmacaddr = mc_count; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = 0; netdev_hw_addr_list_for_each(ha, mc_list) memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN); +#else + for (i = 0; i < mc_count; i++) { + if (!ha) + break; + memcpy(&priv->multicastlist[i], ha->da_addr, + ETH_ALEN); + ha = ha->next; + } +#endif return mc_count; } --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -40,7 +40,11 @@ int mwifiex_copy_mcast_addr(struct mwifi struct netdev_hw_addr *ha; netdev_for_each_mc_addr(ha, dev) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); +#else + memcpy(&mlist->mac_list[i++], ha->dmi_addr, ETH_ALEN); +#endif return i; } --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -216,7 +216,11 @@ mwifiex_info_read(struct file *file, cha netdev_for_each_mc_addr(ha, netdev) p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i++, ha->addr); +#else + i++, ha->dmi_addr); +#endif } p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2567,15 +2567,21 @@ struct mwl8k_cmd_mac_multicast_adr { static struct mwl8k_cmd_pkt * __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *ha) +#endif { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_mac_multicast_adr *cmd; int size; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) int mc_count = 0; if (mc_list) mc_count = netdev_hw_addr_list_count(mc_list); +#endif if (allmulti || mc_count > priv->num_mcaddrs) { allmulti = 1; @@ -2596,13 +2602,27 @@ __mwl8k_cmd_mac_multicast_adr(struct iee if (allmulti) { cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST); } else if (mc_count) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; int i = 0; +#else + int i; +#endif cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); cmd->numaddr = cpu_to_le16(mc_count); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { memcpy(cmd->addr[i], ha->addr, ETH_ALEN); +#else + for (i = 0; i < mc_count && ha; i++) { + if (ha->da_addrlen != ETH_ALEN) { + kfree(cmd); + return NULL; + } + memcpy(cmd->addr[i], ha->da_addr, ETH_ALEN); + ha = ha->next; +#endif } } @@ -4828,7 +4848,11 @@ mwl8k_bss_info_changed(struct ieee80211_ } static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *ha) +#endif { struct mwl8k_cmd_pkt *cmd; @@ -4839,7 +4863,11 @@ static u64 mwl8k_prepare_multicast(struc * we'll end up throwing this packet away and creating a new * one in mwl8k_configure_filter(). */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list); +#else + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_count, ha); +#endif return (unsigned long)cmd; } @@ -4961,7 +4989,11 @@ static void mwl8k_configure_filter(struc */ if (*total_flags & FIF_ALLMULTI) { kfree(cmd); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL); +#else + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, 0, NULL); +#endif } if (cmd != NULL) { --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -1093,7 +1093,11 @@ int __orinoco_hw_set_multicast_list(stru netdev_for_each_mc_addr(ha, dev) { if (i == mc_count) break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(mclist.addr[i++], ha->addr, ETH_ALEN); +#else + memcpy(mclist.addr[i++], ha->dmi_addr, ETH_ALEN); +#endif } err = hw->ops->write_ltv(hw, USER_BAP, --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -22,6 +22,9 @@ /* Forward declarations */ struct orinoco_private; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) +struct dev_addr_list; +#endif int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, size_t fw_name_len, u32 *hw_ver); --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -364,11 +364,18 @@ out: return ret; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 p54_prepare_multicast(struct ieee80211_hw *dev, struct netdev_hw_addr_list *mc_list) +#else +static u64 p54_prepare_multicast(struct ieee80211_hw *dev, int mc_count, + struct dev_addr_list *ha) +#endif { struct p54_common *priv = dev->priv; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; +#endif int i; BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) != @@ -378,12 +385,23 @@ static u64 p54_prepare_multicast(struct * Otherwise the firmware will drop it and ARP will no longer work. */ i = 1; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i; netdev_hw_addr_list_for_each(ha, mc_list) { memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN); +#else + priv->mc_maclist_num = mc_count + i; + while (i <= mc_count) { + if (!ha) + break; + memcpy(&priv->mc_maclist[i], ha->dmi_addr, ETH_ALEN); +#endif i++; if (i >= ARRAY_SIZE(priv->mc_maclist)) break; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + ha = ha->next; +#endif } return 1; /* update */ --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -1629,7 +1629,11 @@ static void set_multicast_list(struct us netdev_for_each_mc_addr(ha, usbdev->net) memcpy(mc_addrs + i++ * ETH_ALEN, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr, ETH_ALEN); +#else + ha->dmi_addr, ETH_ALEN); +#endif } netif_addr_unlock_bh(usbdev->net); --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -818,10 +818,19 @@ static void rtl8180_bss_info_changed(str } } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, struct netdev_hw_addr_list *mc_list) +#else +static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, int mc_count, + struct dev_addr_list *mc_list) +#endif { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) return netdev_hw_addr_list_count(mc_list); +#else + return mc_count; +#endif } static void rtl8180_configure_filter(struct ieee80211_hw *dev, --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -1290,9 +1290,17 @@ static void rtl8187_bss_info_changed(str } static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *mc_list) +#endif { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) return netdev_hw_addr_list_count(mc_list); +#else + return mc_count; +#endif } static void rtl8187_configure_filter(struct ieee80211_hw *dev, --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2801,11 +2801,20 @@ struct wl1271_filter_params { u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; }; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) +#else +static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mc_list) +#endif { struct wl1271_filter_params *fp; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; +#else + int i; +#endif struct wl1271 *wl = hw->priv; if (unlikely(wl->state == WL1271_STATE_OFF)) @@ -2818,16 +2827,40 @@ static u64 wl1271_op_prepare_multicast(s } /* update multicast filtering parameters */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) fp->mc_list_length = 0; if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) { +#else + fp->enabled = true; + if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) { + mc_count = 0; +#endif fp->enabled = false; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) } else { fp->enabled = true; netdev_hw_addr_list_for_each(ha, mc_list) { +#else + } + + fp->mc_list_length = 0; + for (i = 0; i < mc_count; i++) { + if (mc_list->da_addrlen == ETH_ALEN) { +#endif memcpy(fp->mc_list[fp->mc_list_length], +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ha->addr, ETH_ALEN); +#else + mc_list->da_addr, ETH_ALEN); +#endif fp->mc_list_length++; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) } +#else + } else + wl1271_warning("Unknown mc address length."); + mc_list = mc_list->next; +#endif } return (u64)(unsigned long)fp; --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -1215,17 +1215,34 @@ static void zd_process_intr(struct work_ static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *ha) +#endif { struct zd_mac *mac = zd_hw_mac(hw); struct zd_mc_hash hash; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; +#else + int i; +#endif zd_mc_clear(&hash); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->addr); zd_mc_add_addr(&hash, ha->addr); +#else + for (i = 0; i < mc_count; i++) { + if (!ha) + break; + dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->dmi_addr); + zd_mc_add_addr(&hash, ha->dmi_addr); + ha = ha->next; +#endif } return hash.low | ((u64)hash.high << 32); --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -142,7 +142,11 @@ static void _brcmf_set_multicast_list(st netdev_for_each_mc_addr(ha, ndev) { if (!cnt) break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(bufp, ha->addr, ETH_ALEN); +#else + memcpy(bufp, ha->dmi_addr, ETH_ALEN); +#endif bufp += ETH_ALEN; cnt--; } --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2335,7 +2335,11 @@ struct ieee80211_ops { u32 changed); u64 (*prepare_multicast)(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list); +#else + int mc_count, struct dev_addr_list *mc_list); +#endif void (*configure_filter)(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -94,8 +94,13 @@ static void bnep_net_set_mc_list(struct netdev_for_each_mc_addr(ha, dev) { if (i == BNEP_MAX_MULTICAST_FILTERS) break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); +#else + memcpy(__skb_put(skb, ETH_ALEN), ha->dmi_addr, ETH_ALEN); + memcpy(__skb_put(skb, ETH_ALEN), ha->dmi_addr, ETH_ALEN); +#endif i++; } --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -216,14 +216,28 @@ static inline void drv_bss_info_changed( } static inline u64 drv_prepare_multicast(struct ieee80211_local *local, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, + struct dev_addr_list *mc_list) +#endif { u64 ret = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) trace_drv_prepare_multicast(local, mc_list->count); +#else + trace_drv_prepare_multicast(local, mc_count); +#endif if (local->ops->prepare_multicast) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = local->ops->prepare_multicast(&local->hw, mc_list); +#else + ret = local->ops->prepare_multicast(&local->hw, mc_count, + mc_list); +#endif trace_drv_return_u64(local, ret); --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -865,7 +865,12 @@ struct ieee80211_local { struct work_struct recalc_smps; /* aggregated multicast list */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list mc_list; +#else + struct dev_addr_list *mc_list; + int mc_count; +#endif bool tim_in_locked_section; /* see ieee80211_beacon_get() */ --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -718,8 +718,13 @@ static void ieee80211_do_stop(struct iee if (sdata->dev) { netif_addr_lock_bh(sdata->dev); spin_lock_bh(&local->filter_lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __hw_addr_unsync(&local->mc_list, &sdata->dev->mc, sdata->dev->addr_len); +#else + __dev_addr_unsync(&local->mc_list, &local->mc_count, + &sdata->dev->mc_list, &sdata->dev->mc_count); +#endif spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(sdata->dev); @@ -890,7 +895,12 @@ static void ieee80211_set_multicast_list sdata->flags ^= IEEE80211_SDATA_PROMISC; } spin_lock_bh(&local->filter_lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len); +#else + __dev_addr_sync(&local->mc_list, &local->mc_count, + &dev->mc_list, &dev->mc_count); +#endif spin_unlock_bh(&local->filter_lock); ieee80211_queue_work(&local->hw, &local->reconfig_filter); } --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -72,7 +72,11 @@ void ieee80211_configure_filter(struct i spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) mc = drv_prepare_multicast(local, &local->mc_list); +#else + mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); +#endif spin_unlock_bh(&local->filter_lock); /* be a bit nasty */ @@ -620,9 +624,11 @@ struct ieee80211_hw *ieee80211_alloc_hw( wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; INIT_LIST_HEAD(&local->interfaces); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) __hw_addr_init(&local->mc_list); +#endif mutex_init(&local->iflist_mtx); mutex_init(&local->mtx); --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1201,7 +1201,11 @@ static void ath6kl_set_multicast_list(st list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) { found = false; netdev_for_each_mc_addr(ha, ndev) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (memcmp(ha->addr, mc_filter->hw_addr, +#else + if (memcmp(ha->dmi_addr, mc_filter->hw_addr, +#endif ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { found = true; break; @@ -1235,7 +1239,11 @@ static void ath6kl_set_multicast_list(st netdev_for_each_mc_addr(ha, ndev) { found = false; list_for_each_entry(mc_filter, &vif->mc_filter, list) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (memcmp(ha->addr, mc_filter->hw_addr, +#else + if (memcmp(ha->dmi_addr, mc_filter->hw_addr, +#endif ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { found = true; break; @@ -1250,7 +1258,11 @@ static void ath6kl_set_multicast_list(st goto out; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) memcpy(mc_filter->hw_addr, ha->addr, +#else + memcpy(mc_filter->hw_addr, ha->dmi_addr, +#endif ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE); /* Set the multicast filter */ ath6kl_dbg(ATH6KL_DBG_TRC, compat-drivers-2012-09-18/patches/network/16-bluetooth.patch0000644000175000017500000003733712026176512023000 0ustar mcgrofmcgrofThese changes are required to backport blueooth. A lot can be optimized here still, but for now we keep this here. --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -302,8 +302,13 @@ static int hci_uart_tty_open(struct tty_ /* FIXME: why is this needed. Note don't use ldisc_ref here as the open path is before the ldisc is referencable */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) if (tty->ldisc->ops->flush_buffer) tty->ldisc->ops->flush_buffer(tty); +#else + if (tty->ldisc.ops->flush_buffer) + tty->ldisc.ops->flush_buffer(tty); +#endif tty_driver_flush_buffer(tty); return 0; @@ -529,7 +534,11 @@ static int hci_uart_tty_ioctl(struct tty return hu->hdev_flags; default: +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) err = n_tty_ioctl_helper(tty, file, cmd, arg); +#else + err = n_tty_ioctl(tty, file, cmd, arg); +#endif break; } --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -112,8 +112,12 @@ int bt_sock_unregister(int proto) } EXPORT_SYMBOL(bt_sock_unregister); +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int bt_sock_create(struct net *net, struct socket *sock, int proto, int kern) +#else +static int bt_sock_create(struct net *net, struct socket *sock, int proto) +#endif { int err; @@ -131,7 +135,11 @@ static int bt_sock_create(struct net *ne read_lock(&bt_proto_lock); if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) { +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) err = bt_proto[proto]->create(net, sock, proto, kern); +#else + err = bt_proto[proto]->create(net, sock, proto); +#endif if (!err) bt_sock_reclassify_lock(sock->sk, proto); module_put(bt_proto[proto]->owner); @@ -464,7 +472,11 @@ int bt_sock_ioctl(struct socket *sock, u if (sk->sk_state == BT_LISTEN) return -EINVAL; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); +#else + amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); +#endif if (amount < 0) amount = 0; err = put_user(amount, (int __user *) arg); --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -384,7 +384,11 @@ static void cmtp_reset_ctr(struct capi_c BT_DBG("ctrl %p", ctrl); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) capi_ctr_down(ctrl); +#else + capi_ctr_reseted(ctrl); +#endif atomic_inc(&session->terminate); wake_up_process(session->task); --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -884,8 +884,13 @@ drop: goto done; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len) +#else +static int hci_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int len) +#endif { struct hci_ufilter uf = { .opcode = 0 }; struct sock *sk = sock->sk; @@ -1059,8 +1064,12 @@ static struct proto hci_sk_proto = { .obj_size = sizeof(struct hci_pinfo) }; +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int hci_sock_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +static int hci_sock_create(struct net *net, struct socket *sock, int protocol) +#endif { struct sock *sk; --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -71,7 +71,11 @@ static struct attribute_group bt_link_gr .attrs = bt_link_attrs, }; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static const struct attribute_group *bt_link_groups[] = { +#else +static struct attribute_group *bt_link_groups[] = { +#endif &bt_link_group, NULL }; @@ -140,7 +144,11 @@ void hci_conn_del_sysfs(struct hci_conn dev = device_find_child(&conn->dev, NULL, __match_tty); if (!dev) break; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) device_move(dev, NULL, DPM_ORDER_DEV_LAST); +#else + device_move(dev, NULL); +#endif put_device(dev); } @@ -378,7 +386,11 @@ static struct attribute_group bt_host_gr .attrs = bt_host_attrs, }; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static const struct attribute_group *bt_host_groups[] = { +#else +static struct attribute_group *bt_host_groups[] = { +#endif &bt_host_group, NULL }; --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -380,6 +380,7 @@ err: return ret; } +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, unsigned char report_type) { @@ -438,6 +439,16 @@ err: mutex_unlock(&session->report_mutex); return ret; } +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) +static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count) +{ + if (hidp_send_ctrl_message(hid->driver_data, + HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE, + data, count)) + return -ENOMEM; + return count; +} +#endif static void hidp_idle_timeout(unsigned long arg) { @@ -739,8 +750,14 @@ static int hidp_session(void *arg) } if (session->hid) { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) hid_destroy_device(session->hid); session->hid = NULL; +#else + if (session->hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(session->hid); + hid_free_device(session->hid); +#endif } /* Wakeup user-space polling for socket errors */ @@ -851,6 +868,70 @@ static void hidp_close(struct hid_device { } +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)) +static const struct { + __u16 idVendor; + __u16 idProduct; + unsigned quirks; +} hidp_blacklist[] = { + /* Apple wireless Mighty Mouse */ + { 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, + + { } /* Terminating entry */ +}; +static void hidp_setup_quirks(struct hid_device *hid) +{ + unsigned int n; + + for (n = 0; hidp_blacklist[n].idVendor; n++) + if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) && + hidp_blacklist[n].idProduct == le16_to_cpu(hid->product)) + hid->quirks = hidp_blacklist[n].quirks; +} + +static void hidp_setup_hid(struct hidp_session *session, + struct hidp_connadd_req *req) +{ + struct hid_device *hid = session->hid; + struct hid_report *report; + bdaddr_t src, dst; + + session->hid = hid; + + hid->driver_data = session; + + baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); + baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); + + hid->bus = BUS_BLUETOOTH; + hid->vendor = req->vendor; + hid->product = req->product; + hid->version = req->version; + hid->country = req->country; + + strncpy(hid->name, req->name, 128); + strncpy(hid->phys, batostr(&src), 64); + strncpy(hid->uniq, batostr(&dst), 64); + + hid->dev = hidp_get_device(session); + hid->hid_open = hidp_open; + hid->hid_close = hidp_close; + + hid->hidinput_input_event = hidp_hidinput_event; + + hidp_setup_quirks(hid); + + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) + hidp_send_report(session, report); + + list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + hidp_send_report(session, report); + + if (hidinput_connect(hid) == 0) + hid->claimed |= HID_CLAIMED_INPUT; +} +#else + static int hidp_parse(struct hid_device *hid) { struct hidp_session *session = hid->driver_data; @@ -938,7 +1019,9 @@ static int hidp_setup_hid(struct hidp_se hid->dev.parent = &session->conn->dev; hid->ll_driver = &hidp_hid_driver; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) hid->hid_get_raw_report = hidp_get_raw_report; +#endif hid->hid_output_raw_report = hidp_output_raw_report; return 0; @@ -949,6 +1032,7 @@ fault: return err; } +#endif int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) { @@ -964,6 +1048,39 @@ int hidp_add_connection(struct hidp_conn BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)) + if (req->rd_size > 0) { + unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL); + + if (!buf) { + kfree(session); + return -ENOMEM; + } + + if (copy_from_user(buf, req->rd_data, req->rd_size)) { + kfree(buf); + kfree(session); + return -EFAULT; + } + + session->hid = hid_parse_report(buf, req->rd_size); + + kfree(buf); + + if (!session->hid) { + kfree(session); + return -EINVAL; + } + } + + if (!session->hid) { + session->input = input_allocate_device(); + if (!session->input) { + kfree(session); + return -ENOMEM; + } + } +#endif down_write(&hidp_session_sem); s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); @@ -1011,6 +1128,7 @@ int hidp_add_connection(struct hidp_conn __hidp_link_session(session); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) if (req->rd_size > 0) { err = hidp_setup_hid(session, req); if (err) @@ -1022,6 +1140,16 @@ int hidp_add_connection(struct hidp_conn if (err < 0) goto purge; } +#else + if (session->input) { + err = hidp_setup_input(session, req); + if (err < 0) + goto failed; + } + + if (session->hid) + hidp_setup_hid(session, req); +#endif hidp_set_timer(session); @@ -1080,6 +1208,7 @@ unlink: session->input = NULL; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) if (session->hid) { hid_destroy_device(session->hid); session->hid = NULL; @@ -1093,10 +1222,15 @@ purge: skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); +#endif failed: up_write(&hidp_session_sem); +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)) + if (session->hid) + hid_free_device(session->hid); +#endif kfree(session); return err; } --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -306,8 +306,13 @@ static struct sock *rfcomm_sock_alloc(st return sk; } +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int rfcomm_sock_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +static int rfcomm_sock_create(struct net *net, struct socket *sock, + int protocol) +#endif { struct sock *sk; @@ -662,7 +667,11 @@ static int rfcomm_sock_setsockopt_old(st return err; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +#else +static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +#endif { struct sock *sk = sock->sk; struct bt_security sec; --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -713,8 +713,12 @@ static int rfcomm_tty_open(struct tty_st remove_wait_queue(&dev->wait, &wait); if (err == 0) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) device_move(dev->tty_dev, rfcomm_get_device(dev), DPM_ORDER_DEV_AFTER_PARENT); +#else + device_move(dev->tty_dev, rfcomm_get_device(dev)); +#endif rfcomm_tty_copy_pending(dev); @@ -738,7 +742,11 @@ static void rfcomm_tty_close(struct tty_ if (!--dev->port.count) { spin_unlock_irqrestore(&dev->port.lock, flags); if (dev->tty_dev->parent) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); +#else + device_move(dev->tty_dev, NULL); +#endif /* Close DLC and dettach TTY */ rfcomm_dlc_close(dev->dlc, 0); @@ -814,7 +822,11 @@ static int rfcomm_tty_write_room(struct return room; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) static int rfcomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) +#else +static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) +#endif { BT_DBG("tty %p cmd 0x%02x", tty, cmd); @@ -1073,7 +1085,11 @@ static void rfcomm_tty_hangup(struct tty } } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) static int rfcomm_tty_tiocmget(struct tty_struct *tty) +#else +static int rfcomm_tty_tiocmget(struct tty_struct *tty, struct file *filp) +#endif { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; @@ -1082,7 +1098,11 @@ static int rfcomm_tty_tiocmget(struct tt return dev->modem_status; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38)) static int rfcomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) +#else +static int rfcomm_tty_tiocmset(struct tty_struct *tty, struct file *filp, unsigned int set, unsigned int clear) +#endif { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -432,8 +432,12 @@ static struct sock *sco_sock_alloc(struc return sk; } +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int sco_sock_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +static int sco_sock_create(struct net *net, struct socket *sock, int protocol) +#endif { struct sock *sk; @@ -662,7 +666,11 @@ static int sco_sock_sendmsg(struct kiocb return err; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +#else +static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +#endif { struct sock *sk = sock->sk; int err = 0; --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -186,8 +186,12 @@ static struct proto bnep_proto = { .obj_size = sizeof(struct bt_sock) }; +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int bnep_sock_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +static int bnep_sock_create(struct net *net, struct socket *sock, int protocol) +#endif { struct sock *sk; --- a/net/bluetooth/cmtp/sock.c +++ b/net/bluetooth/cmtp/sock.c @@ -195,8 +195,12 @@ static struct proto cmtp_proto = { .obj_size = sizeof(struct bt_sock) }; +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol) +#endif { struct sock *sk; --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -235,8 +235,12 @@ static struct proto hidp_proto = { .obj_size = sizeof(struct bt_sock) }; +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int hidp_sock_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +static int hidp_sock_create(struct net *net, struct socket *sock, int protocol) +#endif { struct sock *sk; --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -564,7 +564,14 @@ static int l2cap_sock_setsockopt_old(str return err; } + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31)) static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +#else +static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +#endif + + { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -1194,8 +1201,12 @@ static struct sock *l2cap_sock_alloc(str return sk; } +#if defined(CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN) static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol) +#endif { struct sock *sk; @@ -1207,7 +1218,11 @@ static int l2cap_sock_create(struct net sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)) if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) +#else + if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW)) +#endif return -EPERM; sock->ops = &l2cap_sock_ops; compat-drivers-2012-09-18/patches/network/42-netlink_seq.patch0000644000175000017500000000054412026176520023273 0ustar mcgrofmcgrof--- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4713,7 +4713,9 @@ static int nl80211_dump_scan(struct sk_b spin_lock_bh(&rdev->bss_lock); cfg80211_bss_expire(rdev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) cb->seq = rdev->bss_generation; +#endif list_for_each_entry(scan, &rdev->bss_list, list) { if (++idx <= start) compat-drivers-2012-09-18/patches/network/36-workqueue.patch0000644000175000017500000000064512026176516023020 0ustar mcgrofmcgrofBackport commit 99b88a0ecbdbc6df03527292571b2b442965814a The rest is backported in include/linux/compat-2.6.37.h --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1131,6 +1131,10 @@ static void __exit ieee80211_exit(void) rc80211_minstrel_ht_exit(); rc80211_minstrel_exit(); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) + flush_scheduled_work(); +#endif + if (mesh_allocated) ieee80211s_stop(); compat-drivers-2012-09-18/patches/network/27-hermes-read-pda-conflict.patch0000644000175000017500000000420112026176515025515 0ustar mcgrofmcgrofRename read_pda to something else because this symbol is used in a define for something else in arch/um/include/asm/pda.h on older kernels. --- a/drivers/net/wireless/orinoco/fw.c +++ b/drivers/net/wireless/orinoco/fw.c @@ -123,7 +123,7 @@ orinoco_dl_firmware(struct orinoco_priva dev_dbg(dev, "Attempting to download firmware %s\n", firmware); /* Read current plug data */ - err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); + err = hw->ops->read_pda_h(hw, pda, fw->pda_addr, fw->pda_size); dev_dbg(dev, "Read PDA returned %d\n", err); if (err) goto free; @@ -225,7 +225,7 @@ symbol_dl_image(struct orinoco_private * if (!pda) return -ENOMEM; - ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); + ret = hw->ops->read_pda_h(hw, pda, fw->pda_addr, fw->pda_size); if (ret) goto free; } --- a/drivers/net/wireless/orinoco/hermes.c +++ b/drivers/net/wireless/orinoco/hermes.c @@ -767,7 +767,7 @@ static const struct hermes_ops hermes_op .write_ltv = hermes_write_ltv, .bap_pread = hermes_bap_pread, .bap_pwrite = hermes_bap_pwrite, - .read_pda = hermes_read_pda, + .read_pda_h = hermes_read_pda, .program_init = hermesi_program_init, .program_end = hermesi_program_end, .program = hermes_program_bytes, --- a/drivers/net/wireless/orinoco/hermes.h +++ b/drivers/net/wireless/orinoco/hermes.h @@ -393,7 +393,7 @@ struct hermes_ops { u16 id, u16 offset); int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf, int len, u16 id, u16 offset); - int (*read_pda)(struct hermes *hw, __le16 *pda, + int (*read_pda_h)(struct hermes *hw, __le16 *pda, u32 pda_addr, u16 pda_len); int (*program_init)(struct hermes *hw, u32 entry_point); int (*program_end)(struct hermes *hw); --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -1548,7 +1548,7 @@ static const struct hermes_ops ezusb_ops .read_ltv = ezusb_read_ltv, .write_ltv = ezusb_write_ltv, .bap_pread = ezusb_bap_pread, - .read_pda = ezusb_read_pda, + .read_pda_h = ezusb_read_pda, .program_init = ezusb_program_init, .program_end = ezusb_program_end, .program = ezusb_program, compat-drivers-2012-09-18/patches/network/44-deactivate-mac80211-tracing.patch0000644000175000017500000000055312026176520025651 0ustar mcgrofmcgrofDo not activate the mac80211 tracing for kernels <= 2.6.32 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -24,7 +24,9 @@ mac80211-y := \ wme.o \ event.o \ chan.o \ - trace.o mlme.o + mlme.o + +mac80211-$(CONFIG_COMPAT_MAC80211_DRIVER_API_TRACER) += trace.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ compat-drivers-2012-09-18/patches/network/39-remove_blink_set.patch0000644000175000017500000000343612026176517024325 0ustar mcgrofmcgrof--- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -540,6 +540,7 @@ il_led_brightness_set(struct led_classde il_led_cmd(il, on, 0); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) static int il_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) @@ -548,6 +549,7 @@ il_led_blink_set(struct led_classdev *le return il_led_cmd(il, *delay_on, *delay_off); } +#endif void il_leds_init(struct il_priv *il) @@ -561,7 +563,9 @@ il_leds_init(struct il_priv *il) il->led.name = kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy)); il->led.brightness_set = il_led_brightness_set; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) il->led.blink_set = il_led_blink_set; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) il->led.max_brightness = 1; #endif --- a/drivers/net/wireless/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/iwlwifi/dvm/led.c @@ -162,6 +162,7 @@ static void iwl_led_brightness_set(struc iwl_led_cmd(priv, on, 0); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) static int iwl_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) @@ -170,6 +171,7 @@ static int iwl_led_blink_set(struct led_ return iwl_led_cmd(priv, *delay_on, *delay_off); } +#endif void iwl_leds_init(struct iwl_priv *priv) { @@ -186,7 +188,9 @@ void iwl_leds_init(struct iwl_priv *priv priv->led.name = kasprintf(GFP_KERNEL, "%s-led", wiphy_name(priv->hw->wiphy)); priv->led.brightness_set = iwl_led_brightness_set; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) priv->led.blink_set = iwl_led_blink_set; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) priv->led.max_brightness = 1; #endif compat-drivers-2012-09-18/patches/network/08-rename-config-options.patch0000644000175000017500000001346712026176507025201 0ustar mcgrofmcgrofThis file renames CONFIG_ options that may be defined in older kernels but that we know we can *safely* rename to other config option names to ensure we disable building these options at through this framework. An example here is ath9k's rate control aglorithm is always selected by default via CONFIG_ATH9K_RATE_CONTROL. By renaming this to CONFIG_COMPAT_ATH9K_RATE_CONTROL we have the flexibility to disable it for a replacement driver. In kernel 2.6.26 and older CONFIG_IWL4965 was build as an extra module, but now it is directly included in the iwlagn. CONFIG_IWL4965 has to be set to y, to build correctly. zd1211rw does not build with kernel < 2.6.28, but it is often activated in the kernel config of older kernels. We rename the option to deactivate it on older kernels. CONFIG_BT_L2CAP and CONFIG_BT_SCO are boolean now, but often set to m in the kernel config. CONFIG_BT_HIDP does not build with older kernel versions. --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ obj-$(CONFIG_B43) += b43/ obj-$(CONFIG_B43LEGACY) += b43legacy/ -obj-$(CONFIG_ZD1211RW) += zd1211rw/ +obj-$(CONFIG_COMPAT_ZD1211RW) += zd1211rw/ obj-$(CONFIG_RTL8180) += rtl818x/ obj-$(CONFIG_RTL8187) += rtl818x/ obj-$(CONFIG_RTLWIFI) += rtlwifi/ --- a/drivers/net/wireless/iwlegacy/Makefile +++ b/drivers/net/wireless/iwlegacy/Makefile @@ -5,7 +5,7 @@ iwlegacy-$(CONFIG_IWLEGACY_DEBUGFS) += d iwlegacy-objs += $(iwlegacy-m) # 4965 -obj-$(CONFIG_IWL4965) += iwl4965.o +obj-$(CONFIG_COMPAT_IWL4965) += iwl4965.o iwl4965-objs := 4965.o 4965-mac.o 4965-rs.o 4965-calib.o iwl4965-$(CONFIG_IWLEGACY_DEBUGFS) += 4965-debug.o --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1352,7 +1352,7 @@ struct il_priv { } _3945; #endif -#if defined(CONFIG_IWL4965) || defined(CONFIG_IWL4965_MODULE) +#if defined(CONFIG_COMPAT_IWL4965) || defined(CONFIG_COMPAT_IWL4965_MODULE) struct { struct il_rx_phy_res last_phy_res; bool last_phy_res_valid; --- a/drivers/net/wireless/libertas/Makefile +++ b/drivers/net/wireless/libertas/Makefile @@ -17,5 +17,5 @@ libertas_spi-objs += if_spi.o obj-$(CONFIG_LIBERTAS) += libertas.o obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o -obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o +obj-$(CONFIG_COMPAT_LIBERTAS_SDIO) += libertas_sdio.o obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o --- a/drivers/net/wireless/zd1211rw/Makefile +++ b/drivers/net/wireless/zd1211rw/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_ZD1211RW) += zd1211rw.o +obj-$(CONFIG_COMPAT_ZD1211RW) += zd1211rw.o zd1211rw-objs := zd_chip.o zd_mac.o \ zd_rf_al2230.o zd_rf_rf2959.o \ --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_BT) += bluetooth.o obj-$(CONFIG_BT_RFCOMM) += rfcomm/ obj-$(CONFIG_BT_BNEP) += bnep/ obj-$(CONFIG_BT_CMTP) += cmtp/ -obj-$(CONFIG_BT_HIDP) += hidp/ +obj-$(CONFIG_COMPAT_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ --- a/net/bluetooth/hidp/Makefile +++ b/net/bluetooth/hidp/Makefile @@ -2,6 +2,6 @@ # Makefile for the Linux Bluetooth HIDP layer # -obj-$(CONFIG_BT_HIDP) += hidp.o +obj-$(CONFIG_COMPAT_BT_HIDP) += hidp.o hidp-objs := core.o sock.o --- a/drivers/net/wireless/ti/wl1251/Makefile +++ b/drivers/net/wireless/ti/wl1251/Makefile @@ -4,7 +4,7 @@ wl1251_spi-objs += spi.o wl1251_sdio-objs += sdio.o obj-$(CONFIG_WL1251) += wl1251.o -obj-$(CONFIG_WL1251_SPI) += wl1251_spi.o -obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o +obj-$(CONFIG_COMPAT_WL1251_SPI)+= wl1251_spi.o +obj-$(CONFIG_COMPAT_WL1251_SDIO)+= wl1251_sdio.o ccflags-y += -D__CHECK_ENDIAN__ --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -8,7 +8,7 @@ ath9k-y += beacon.o \ antenna.o ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o -ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o +ath9k-$(CONFIG_COMPAT_ATH9K_RATE_CONTROL) += rc.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o ath9k-$(CONFIG_ATH9K_AHB) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -750,7 +750,7 @@ void ath9k_set_hw_capab(struct ath_softc sc->ant_rx = hw->wiphy->available_antennas_rx; sc->ant_tx = hw->wiphy->available_antennas_tx; -#ifdef CONFIG_ATH9K_RATE_CONTROL +#ifdef CONFIG_COMPAT_ATH9K_RATE_CONTROL hw->rate_control_algorithm = "ath9k_rate_control"; #endif --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -215,7 +215,7 @@ struct ath_rate_priv { struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; }; -#ifdef CONFIG_ATH9K_RATE_CONTROL +#ifdef CONFIG_COMPAT_ATH9K_RATE_CONTROL int ath_rate_control_register(void); void ath_rate_control_unregister(void); #else --- a/net/bluetooth/rfcomm/Makefile +++ b/net/bluetooth/rfcomm/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm.o rfcomm-y := core.o sock.o -rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o +rfcomm-$(CONFIG_COMPAT_BT_RFCOMM_TTY) += tty.o --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -357,7 +357,7 @@ struct rfcomm_dev_list_req { int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); -#ifdef CONFIG_BT_RFCOMM_TTY +#ifdef CONFIG_COMPAT_BT_RFCOMM_TTY int rfcomm_init_ttys(void); void rfcomm_cleanup_ttys(void); #else --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -862,7 +862,7 @@ static int rfcomm_sock_ioctl(struct sock err = bt_sock_ioctl(sock, cmd, arg); if (err == -ENOIOCTLCMD) { -#ifdef CONFIG_BT_RFCOMM_TTY +#ifdef CONFIG_COMPAT_BT_RFCOMM_TTY lock_sock(sk); err = rfcomm_dev_ioctl(sk, cmd, (void __user *) arg); release_sock(sk); compat-drivers-2012-09-18/patches/network/24-pcmcia.patch0000644000175000017500000011766112026176514022227 0ustar mcgrofmcgrof--- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -158,7 +158,12 @@ static void bluecard_detach(struct pcmci static void bluecard_activity_led_timeout(u_long arg) { bluecard_info_t *info = (bluecard_info_t *)arg; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif + if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) return; @@ -175,7 +180,11 @@ static void bluecard_activity_led_timeou static void bluecard_enable_activity_led(bluecard_info_t *info) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) return; @@ -231,7 +240,11 @@ static void bluecard_write_wakeup(blueca } do { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif unsigned int offset; unsigned char command; unsigned long ready_bit; @@ -378,7 +391,11 @@ static void bluecard_receive(bluecard_in return; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif if (test_bit(XMIT_SENDING_READY, &(info->tx_state))) bluecard_enable_activity_led(info); @@ -507,7 +524,11 @@ static irqreturn_t bluecard_interrupt(in if (!test_bit(CARD_READY, &(info->hw_state))) return IRQ_HANDLED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif spin_lock(&(info->lock)); @@ -629,7 +650,11 @@ static int bluecard_hci_open(struct hci_ return 0; if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif /* Enable LED */ outb(0x08 | 0x20, iobase + 0x30); @@ -649,7 +674,11 @@ static int bluecard_hci_close(struct hci bluecard_hci_flush(hdev); if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif /* Disable LED */ outb(0x00, iobase + 0x30); @@ -705,7 +734,11 @@ static int bluecard_hci_ioctl(struct hci static int bluecard_open(bluecard_info_t *info) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif struct hci_dev *hdev; unsigned char id; @@ -821,7 +854,11 @@ static int bluecard_open(bluecard_info_t static int bluecard_close(bluecard_info_t *info) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif struct hci_dev *hdev = info->hdev; if (!hdev) @@ -856,7 +893,18 @@ static int bluecard_probe(struct pcmcia_ info->p_dev = link; link->priv = info; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + + link->irq.Handler = bluecard_interrupt; +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ; +#else + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; +#endif return bluecard_config(link); } @@ -873,15 +921,30 @@ static int bluecard_config(struct pcmcia bluecard_info_t *info = link->priv; int i, n; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_index = 0x20; +#else + link->conf.ConfigIndex = 0x20; +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 64; link->io_lines = 6; +#else + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts1 = 64; + link->io.IOAddrLines = 6; +#endif for (n = 0; n < 0x400; n += 0x40) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->start = n ^ 0x300; i = pcmcia_request_io(link); +#else + link->io.BasePort1 = n ^ 0x300; + i = pcmcia_request_io(link, &link->io); +#endif if (i == 0) break; } @@ -889,9 +952,15 @@ static int bluecard_config(struct pcmcia if (i != 0) goto failed; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = pcmcia_request_irq(link, bluecard_interrupt); if (i != 0) goto failed; +#else + i = pcmcia_request_irq(link, &link->irq); + if (i != 0) + link->irq.AssignedIRQ = 0; +#endif i = pcmcia_enable_device(link); if (i != 0) @@ -929,7 +998,13 @@ MODULE_DEVICE_TABLE(pcmcia, bluecard_ids static struct pcmcia_driver bluecard_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "bluecard_cs", +#else + .drv = { + .name = "bluecard_cs", + }, +#endif .probe = bluecard_probe, .remove = bluecard_detach, .id_table = bluecard_ids, --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -186,7 +186,11 @@ static void bt3c_write_wakeup(bt3c_info_ return; do { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif register struct sk_buff *skb; int len; @@ -224,7 +228,11 @@ static void bt3c_receive(bt3c_info_t *in return; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif avail = bt3c_read(iobase, 0x7006); //printk("bt3c_cs: receiving %d bytes\n", avail); @@ -345,7 +353,11 @@ static irqreturn_t bt3c_interrupt(int ir /* our irq handler is shared */ return IRQ_NONE; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif spin_lock(&(info->lock)); @@ -473,7 +485,11 @@ static int bt3c_load_firmware(bt3c_info_ unsigned int iobase, size, addr, fcs, tmp; int i, err = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif /* Reset */ bt3c_io_write(iobase, 0x8040, 0x0404); @@ -645,8 +661,27 @@ static int bt3c_probe(struct pcmcia_devi info->p_dev = link; link->priv = info; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + link->resource[0]->end = 8; +#else + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts1= 8; +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + + link->irq.Handler = bt3c_interrupt; +#endif + + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; +#endif return bt3c_config(link); } @@ -657,6 +692,7 @@ static void bt3c_detach(struct pcmcia_de bt3c_release(link); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data) { int *try = priv_data; @@ -695,6 +731,63 @@ static int bt3c_check_config_notpicky(st } return -ENODEV; } +#else +static int bt3c_check_config(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cf, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ + unsigned long try = (unsigned long) priv_data; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; +#endif + + if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) + p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && + (cf->io.win[0].base != 0)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->resource[0]->start = cf->io.win[0].base; + if (!pcmcia_request_io(p_dev)) +#else + p_dev->io.BasePort1 = cf->io.win[0].base; + p_dev->io.IOAddrLines = (try == 0) ? 16 : + cf->io.flags & CISTPL_IO_LINES_MASK; + if (!pcmcia_request_io(p_dev, &p_dev->io)) +#endif + return 0; + } + return -ENODEV; +} + +static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cf, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ + static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + int j; + + if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { + for (j = 0; j < 5; j++) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) +#else + p_dev->io.BasePort1 = base[j]; + p_dev->io.IOAddrLines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev, &p_dev->io)) +#endif + return 0; + } + } + return -ENODEV; +} +#endif static int bt3c_config(struct pcmcia_device *link) { @@ -718,9 +811,15 @@ static int bt3c_config(struct pcmcia_dev goto failed; found_port: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = pcmcia_request_irq(link, &bt3c_interrupt); if (i != 0) goto failed; +#else + i = pcmcia_request_irq(link, &link->irq); + if (i != 0) + link->irq.AssignedIRQ = 0; +#endif i = pcmcia_enable_device(link); if (i != 0) @@ -755,7 +854,13 @@ MODULE_DEVICE_TABLE(pcmcia, bt3c_ids); static struct pcmcia_driver bt3c_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "bt3c_cs", +#else + .drv = { + .name = "bt3c_cs", + }, +#endif .probe = bt3c_probe, .remove = bt3c_detach, .id_table = bt3c_ids, --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -140,7 +140,11 @@ static void btuart_write_wakeup(btuart_i } do { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif register struct sk_buff *skb; int len; @@ -181,7 +185,11 @@ static void btuart_receive(btuart_info_t return; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif do { info->hdev->stat.byte_rx++; @@ -295,7 +303,11 @@ static irqreturn_t btuart_interrupt(int /* our irq handler is shared */ return IRQ_NONE; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif spin_lock(&(info->lock)); @@ -352,7 +364,11 @@ static void btuart_change_speed(btuart_i return; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif spin_lock_irqsave(&(info->lock), flags); @@ -471,7 +487,11 @@ static int btuart_hci_ioctl(struct hci_d static int btuart_open(btuart_info_t *info) { unsigned long flags; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif struct hci_dev *hdev; spin_lock_init(&(info->lock)); @@ -538,7 +558,11 @@ static int btuart_open(btuart_info_t *in static int btuart_close(btuart_info_t *info) { unsigned long flags; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif struct hci_dev *hdev = info->hdev; if (!hdev) @@ -574,8 +598,27 @@ static int btuart_probe(struct pcmcia_de info->p_dev = link; link->priv = info; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + link->resource[0]->end = 8; +#else + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts1= 8; +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + + link->irq.Handler = btuart_interrupt; +#endif + + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; +#endif return btuart_config(link); } @@ -586,6 +629,7 @@ static void btuart_detach(struct pcmcia_ btuart_release(link); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data) { int *try = priv_data; @@ -624,6 +668,63 @@ static int btuart_check_config_notpicky( } return -ENODEV; } +#else +static int btuart_check_config(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cf, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ + int *try = priv_data; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; +#endif + + if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) + p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && + (cf->io.win[0].base != 0)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->resource[0]->start = cf->io.win[0].base; + if (!pcmcia_request_io(p_dev)) +#else + p_dev->io.BasePort1 = cf->io.win[0].base; + p_dev->io.IOAddrLines = (*try == 0) ? 16 : + cf->io.flags & CISTPL_IO_LINES_MASK; + if (!pcmcia_request_io(p_dev, &p_dev->io)) +#endif + return 0; + } + return -ENODEV; +} + +static int btuart_check_config_notpicky(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cf, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ + static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + int j; + + if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { + for (j = 0; j < 5; j++) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) +#else + p_dev->io.BasePort1 = base[j]; + p_dev->io.IOAddrLines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev, &p_dev->io)) +#endif + return 0; + } + } + return -ENODEV; +} +#endif static int btuart_config(struct pcmcia_device *link) { @@ -647,9 +748,15 @@ static int btuart_config(struct pcmcia_d goto failed; found_port: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) i = pcmcia_request_irq(link, btuart_interrupt); if (i != 0) goto failed; +#else + i = pcmcia_request_irq(link, &link->irq); + if (i != 0) + link->irq.AssignedIRQ = 0; +#endif i = pcmcia_enable_device(link); if (i != 0) @@ -683,7 +790,13 @@ MODULE_DEVICE_TABLE(pcmcia, btuart_ids); static struct pcmcia_driver btuart_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "btuart_cs", +#else + .drv = { + .name = "btuart_cs", + }, +#endif .probe = btuart_probe, .remove = btuart_detach, .id_table = btuart_ids, --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -144,7 +144,11 @@ static void dtl1_write_wakeup(dtl1_info_ } do { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif register struct sk_buff *skb; int len; @@ -209,7 +213,11 @@ static void dtl1_receive(dtl1_info_t *in return; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif do { info->hdev->stat.byte_rx++; @@ -296,7 +304,11 @@ static irqreturn_t dtl1_interrupt(int ir /* our irq handler is shared */ return IRQ_NONE; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) iobase = info->p_dev->resource[0]->start; +#else + iobase = info->p_dev->io.BasePort1; +#endif spin_lock(&(info->lock)); @@ -451,7 +463,11 @@ static int dtl1_hci_ioctl(struct hci_dev static int dtl1_open(dtl1_info_t *info) { unsigned long flags; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif struct hci_dev *hdev; spin_lock_init(&(info->lock)); @@ -495,8 +511,13 @@ static int dtl1_open(dtl1_info_t *info) outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR) & UART_MSR_RI; +#else + info->ri_latch = inb(info->p_dev->io.BasePort1 + UART_MSR) + & UART_MSR_RI; +#endif /* Turn on interrupts */ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); @@ -521,7 +542,11 @@ static int dtl1_open(dtl1_info_t *info) static int dtl1_close(dtl1_info_t *info) { unsigned long flags; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) unsigned int iobase = info->p_dev->resource[0]->start; +#else + unsigned int iobase = info->p_dev->io.BasePort1; +#endif struct hci_dev *hdev = info->hdev; if (!hdev) @@ -557,7 +582,24 @@ static int dtl1_probe(struct pcmcia_devi info->p_dev = link; link->priv = info; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + link->resource[0]->end = 8; +#else + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts1= 8; +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + link->irq.Handler = dtl1_interrupt; +#endif + + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; +#endif return dtl1_config(link); } @@ -571,6 +613,7 @@ static void dtl1_detach(struct pcmcia_de pcmcia_disable_device(link); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data) { if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8)) @@ -581,6 +624,29 @@ static int dtl1_confcheck(struct pcmcia_ return pcmcia_request_io(p_dev); } +#else +static int dtl1_confcheck(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cf, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ + if ((cf->io.nwin != 1) || (cf->io.win[0].len <= 8)) + return -ENODEV; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->resource[0]->start = cf->io.win[0].base; + p_dev->resource[0]->end = cf->io.win[0].len; /*yo */ + p_dev->io_lines = cf->io.flags & CISTPL_IO_LINES_MASK; + return pcmcia_request_io(p_dev); +#else + p_dev->io.BasePort1 = cf->io.win[0].base; + p_dev->io.NumPorts1 = cf->io.win[0].len; /*yo */ + p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; + return pcmcia_request_io(p_dev, &p_dev->io); +#endif +} +#endif static int dtl1_config(struct pcmcia_device *link) { @@ -588,14 +654,24 @@ static int dtl1_config(struct pcmcia_dev int ret; /* Look for a generic full-sized window */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) link->resource[0]->end = 8; +#else + link->io.NumPorts1 = 8; +#endif ret = pcmcia_loop_config(link, dtl1_confcheck, NULL); if (ret) goto failed; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = pcmcia_request_irq(link, dtl1_interrupt); if (ret) goto failed; +#else + ret = pcmcia_request_irq(link, &link->irq); + if (ret != 0) + link->irq.AssignedIRQ = 0; +#endif ret = pcmcia_enable_device(link); if (ret) @@ -623,7 +699,13 @@ MODULE_DEVICE_TABLE(pcmcia, dtl1_ids); static struct pcmcia_driver dtl1_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "dtl1_cs", +#else + .drv = { + .name = "dtl1_cs", + }, +#endif .probe = dtl1_probe, .remove = dtl1_detach, .id_table = dtl1_ids, --- a/drivers/net/wireless/b43/pcmcia.c +++ b/drivers/net/wireless/b43/pcmcia.c @@ -63,6 +63,9 @@ static int b43_pcmcia_resume(struct pcmc static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) { struct ssb_bus *ssb; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) + win_req_t win; +#endif int err = -ENOMEM; int res = 0; @@ -72,6 +75,7 @@ static int __devinit b43_pcmcia_probe(st err = -ENODEV; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) dev->config_flags |= CONF_ENABLE_IRQ; dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 | @@ -79,21 +83,46 @@ static int __devinit b43_pcmcia_probe(st dev->resource[2]->start = 0; dev->resource[2]->end = SSB_CORE_SIZE; res = pcmcia_request_window(dev, dev->resource[2], 250); +#else + dev->conf.Attributes = CONF_ENABLE_IRQ; + dev->conf.IntType = INT_MEMORY_AND_IO; + + win.Attributes = WIN_ENABLE | WIN_DATA_WIDTH_16 | + WIN_USE_WAIT; + win.Base = 0; + win.Size = SSB_CORE_SIZE; + win.AccessSpeed = 250; + res = pcmcia_request_window(dev, &win, &dev->win); +#endif if (res != 0) goto err_kfree_ssb; - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) res = pcmcia_map_mem_page(dev, dev->resource[2], 0); +#else + res = pcmcia_map_mem_page(dev, dev->win, 0); +#endif if (res != 0) goto err_disable; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (!dev->irq) +#else + dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + dev->irq.Handler = NULL; /* The handler is registered later. */ + res = pcmcia_request_irq(dev, &dev->irq); + if (res != 0) +#endif goto err_disable; res = pcmcia_enable_device(dev); if (res != 0) goto err_disable; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start); +#else + err = ssb_bus_pcmciabus_register(ssb, dev, win.Base); +#endif if (err) goto err_disable; dev->priv = ssb; @@ -122,7 +151,13 @@ static void __devexit b43_pcmcia_remove( static struct pcmcia_driver b43_pcmcia_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = "b43-pcmcia", +#else + .drv = { + .name = "b43-pcmcia", + }, +#endif .id_table = b43_pcmcia_tbl, .probe = b43_pcmcia_probe, .remove = __devexit_p(b43_pcmcia_remove), --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -757,7 +757,11 @@ static void if_cs_prog_firmware(struct l goto out; /* Now actually get the IRQ */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = request_irq(card->p_dev->irq, if_cs_interrupt, +#else + ret = request_irq(card->p_dev->irq.AssignedIRQ, if_cs_interrupt, +#endif IRQF_SHARED, DRV_NAME, card); if (ret) { pr_err("error in request_irq\n"); @@ -775,7 +779,11 @@ static void if_cs_prog_firmware(struct l priv->fw_ready = 1; if (lbs_start_card(priv) != 0) { pr_err("could not activate card\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) free_irq(card->p_dev->irq, card); +#else + free_irq(card->p_dev->irq.AssignedIRQ, card); +#endif } out: @@ -824,7 +832,11 @@ static void if_cs_release(struct pcmcia_ lbs_deb_enter(LBS_DEB_CS); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) free_irq(p_dev->irq, card); +#else + free_irq(p_dev->irq.AssignedIRQ, card); +#endif pcmcia_disable_device(p_dev); if (card->iobase) ioport_unmap(card->iobase); @@ -832,7 +844,7 @@ static void if_cs_release(struct pcmcia_ lbs_deb_leave(LBS_DEB_CS); } - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) { p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; @@ -842,9 +854,39 @@ static int if_cs_ioprobe(struct pcmcia_d pr_err("wrong CIS (check number of IO windows)\n"); return -ENODEV; } +#else +static int if_cs_ioprobe(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + p_dev->resource[0]->start = cfg->io.win[0].base; + p_dev->resource[0]->end = cfg->io.win[0].len; +#else + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + p_dev->io.BasePort1 = cfg->io.win[0].base; + p_dev->io.NumPorts1 = cfg->io.win[0].len; +#endif + + /* Do we need to allocate an interrupt? */ + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + + /* IO window settings */ + if (cfg->io.nwin != 1) { + pr_err("wrong CIS (check number of IO windows)\n"); + return -ENODEV; + } +#endif /* This reserves IO space but doesn't actually enable it */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) return pcmcia_request_io(p_dev); +#else + return pcmcia_request_io(p_dev, &p_dev->io); +#endif } static int if_cs_probe(struct pcmcia_device *p_dev) @@ -863,7 +905,16 @@ static int if_cs_probe(struct pcmcia_dev card->p_dev = p_dev; p_dev->priv = card; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; +#else +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + p_dev->irq.Handler = NULL; +#endif + p_dev->conf.Attributes = 0; + p_dev->conf.IntType = INT_MEMORY_AND_IO; +#endif if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { pr_err("error in pcmcia_loop_config\n"); @@ -875,12 +926,26 @@ static int if_cs_probe(struct pcmcia_dev * a handler to the interrupt, unless the 'Handler' member of * the irq structure is initialized. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) if (!p_dev->irq) goto out1; +#else + if (p_dev->conf.Attributes & CONF_ENABLE_IRQ) { + ret = pcmcia_request_irq(p_dev, &p_dev->irq); + if (ret) { + pr_err("error in pcmcia_request_irq\n"); + goto out1; + } + } +#endif /* Initialize io access */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) card->iobase = ioport_map(p_dev->resource[0]->start, resource_size(p_dev->resource[0])); +#else + card->iobase = ioport_map(p_dev->io.BasePort1, p_dev->io.NumPorts1); +#endif if (!card->iobase) { pr_err("error in ioport_map\n"); ret = -EIO; @@ -894,7 +959,17 @@ static int if_cs_probe(struct pcmcia_dev } /* Finally, report what we've done */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) + lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n", + p_dev->irq, p_dev->io.BasePort1, + p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1); +#else + lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n", + p_dev->irq.AssignedIRQ, p_dev->io.BasePort1, + p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1); +#endif /* * Most of the libertas cards can do unaligned register access, but some @@ -1002,7 +1077,13 @@ MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); static struct pcmcia_driver lbs_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = DRV_NAME, +#else + .drv = { + .name = DRV_NAME, + }, +#endif .probe = if_cs_probe, .remove = if_cs_detach, .id_table = if_cs_ids, --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -78,7 +78,11 @@ orinoco_cs_hard_reset(struct orinoco_pri /* We need atomic ops here, because we're not holding the lock */ set_bit(0, &card->hard_reset_in_progress); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27) + err = pcmcia_reset_card(link, NULL); +#else err = pcmcia_reset_card(link->socket); +#endif if (err) return err; @@ -108,6 +112,16 @@ orinoco_cs_probe(struct pcmcia_device *l card->p_dev = link; link->priv = priv; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + link->irq.Handler = orinoco_interrupt; +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) + link->conf.Attributes = 0; + link->conf.IntType = INT_MEMORY_AND_IO; +#endif + return orinoco_cs_config(link); } /* orinoco_cs_attach */ @@ -122,6 +136,7 @@ static void orinoco_cs_detach(struct pcm free_orinocodev(priv); } /* orinoco_cs_detach */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int orinoco_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { if (p_dev->config_index == 0) @@ -129,6 +144,98 @@ static int orinoco_cs_config_check(struc return pcmcia_request_io(p_dev); }; +#else +static int orinoco_cs_config_check(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ + if (cfg->index == 0) + goto next_entry; + + /* Use power settings for Vcc and Vpp if present */ + /* Note that the CIS values need to be rescaled */ + if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) { + DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", + __func__, vcc, + cfg->vcc.param[CISTPL_POWER_VNOM] / 10000); + if (!ignore_cis_vcc) + goto next_entry; + } + } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) { + DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", + __func__, vcc, + dflt->vcc.param[CISTPL_POWER_VNOM] / 10000); + if (!ignore_cis_vcc) + goto next_entry; + } + } + + if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) + p_dev->conf.Vpp = + cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; + else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM)) + p_dev->conf.Vpp = + dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; + + /* Do we need to allocate an interrupt? */ + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + + /* IO window settings */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + p_dev->resource[0]->end = p_dev->resource[1]->end = 0; +#else + p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; +#endif + if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= + pcmcia_io_cfg_data_width(io->flags); + p_dev->resource[0]->start = io->win[0].base; + p_dev->resource[0]->end = io->win[0].len; +#else + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + if (!(io->flags & CISTPL_IO_8BIT)) + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + if (!(io->flags & CISTPL_IO_16BIT)) + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; + p_dev->io.BasePort1 = io->win[0].base; + p_dev->io.NumPorts1 = io->win[0].len; +#endif + if (io->nwin > 1) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + p_dev->resource[1]->flags = p_dev->resource[0]->flags; + p_dev->resource[1]->start = io->win[1].base; + p_dev->resource[1]->end = io->win[1].len; +#else + p_dev->io.Attributes2 = p_dev->io.Attributes1; + p_dev->io.BasePort2 = io->win[1].base; + p_dev->io.NumPorts2 = io->win[1].len; +#endif + } + + /* This reserves IO space but doesn't actually enable it */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + if (pcmcia_request_io(p_dev) != 0) +#else + if (pcmcia_request_io(p_dev, &p_dev->io) != 0) +#endif + goto next_entry; + } + return 0; + +next_entry: + pcmcia_disable_device(p_dev); + return -ENODEV; +}; +#endif static int orinoco_cs_config(struct pcmcia_device *link) @@ -138,10 +245,12 @@ orinoco_cs_config(struct pcmcia_device * int ret; void __iomem *mem; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; if (ignore_cis_vcc) link->config_flags &= ~CONF_AUTO_CHECK_VCC; +#endif ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL); if (ret) { if (!ignore_cis_vcc) @@ -151,8 +260,12 @@ orinoco_cs_config(struct pcmcia_device * goto failed; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) mem = ioport_map(link->resource[0]->start, resource_size(link->resource[0])); +#else + mem = ioport_map(link->io.BasePort1, link->io.NumPorts1); +#endif if (!mem) goto failed; @@ -161,7 +274,11 @@ orinoco_cs_config(struct pcmcia_device * * called. */ hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = pcmcia_request_irq(link, orinoco_interrupt); +#else + ret = pcmcia_request_irq(link, &link->irq); +#endif if (ret) goto failed; @@ -176,8 +293,16 @@ orinoco_cs_config(struct pcmcia_device * } /* Register an interface with the stack */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) if (orinoco_if_add(priv, link->resource[0]->start, link->irq, NULL) != 0) { +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) + if (orinoco_if_add(priv, link->io.BasePort1, + link->irq, NULL) != 0) { +#else + if (orinoco_if_add(priv, link->io.BasePort1, + link->irq.AssignedIRQ, NULL) != 0) { +#endif printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } @@ -331,7 +456,13 @@ MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_i static struct pcmcia_driver orinoco_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = DRIVER_NAME, +#else + .drv = { + .name = DRIVER_NAME, + }, +#endif .probe = orinoco_cs_probe, .remove = orinoco_cs_detach, .id_table = orinoco_cs_ids, --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -170,6 +170,16 @@ spectrum_cs_probe(struct pcmcia_device * card->p_dev = link; link->priv = priv; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + link->irq.Handler = orinoco_interrupt; +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) + link->conf.Attributes = 0; + link->conf.IntType = INT_MEMORY_AND_IO; +#endif + return spectrum_cs_config(link); } /* spectrum_cs_attach */ @@ -184,6 +194,7 @@ static void spectrum_cs_detach(struct pc free_orinocodev(priv); } /* spectrum_cs_detach */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static int spectrum_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { @@ -192,6 +203,98 @@ static int spectrum_cs_config_check(stru return pcmcia_request_io(p_dev); }; +#else +static int spectrum_cs_config_check(struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data) +{ + if (cfg->index == 0) + goto next_entry; + + /* Use power settings for Vcc and Vpp if present */ + /* Note that the CIS values need to be rescaled */ + if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) { + DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", + __func__, vcc, + cfg->vcc.param[CISTPL_POWER_VNOM] / 10000); + if (!ignore_cis_vcc) + goto next_entry; + } + } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) { + DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n", + __func__, vcc, + dflt->vcc.param[CISTPL_POWER_VNOM] / 10000); + if (!ignore_cis_vcc) + goto next_entry; + } + } + + if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) + p_dev->conf.Vpp = + cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; + else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM)) + p_dev->conf.Vpp = + dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; + + /* Do we need to allocate an interrupt? */ + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + + /* IO window settings */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + p_dev->resource[0]->end = p_dev->resource[1]->end = 0; +#else + p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; +#endif + if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= + pcmcia_io_cfg_data_width(io->flags); + p_dev->resource[0]->start = io->win[0].base; + p_dev->resource[0]->end = io->win[0].len; +#else + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + if (!(io->flags & CISTPL_IO_8BIT)) + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + if (!(io->flags & CISTPL_IO_16BIT)) + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; + p_dev->io.BasePort1 = io->win[0].base; + p_dev->io.NumPorts1 = io->win[0].len; +#endif + if (io->nwin > 1) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + p_dev->resource[1]->flags = p_dev->resource[0]->flags; + p_dev->resource[1]->start = io->win[1].base; + p_dev->resource[1]->end = io->win[1].len; +#else + p_dev->io.Attributes2 = p_dev->io.Attributes1; + p_dev->io.BasePort2 = io->win[1].base; + p_dev->io.NumPorts2 = io->win[1].len; +#endif + } + + /* This reserves IO space but doesn't actually enable it */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + if (pcmcia_request_io(p_dev) != 0) +#else + if (pcmcia_request_io(p_dev, &p_dev->io) != 0) +#endif + goto next_entry; + } + return 0; + +next_entry: + pcmcia_disable_device(p_dev); + return -ENODEV; +}; +#endif static int spectrum_cs_config(struct pcmcia_device *link) @@ -201,10 +304,12 @@ spectrum_cs_config(struct pcmcia_device int ret; void __iomem *mem; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; if (ignore_cis_vcc) link->config_flags &= ~CONF_AUTO_CHECK_VCC; +#endif ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL); if (ret) { if (!ignore_cis_vcc) @@ -214,8 +319,12 @@ spectrum_cs_config(struct pcmcia_device goto failed; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) mem = ioport_map(link->resource[0]->start, resource_size(link->resource[0])); +#else + mem = ioport_map(link->io.BasePort1, link->io.NumPorts1); +#endif if (!mem) goto failed; @@ -225,7 +334,11 @@ spectrum_cs_config(struct pcmcia_device hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); hw->eeprom_pda = true; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) ret = pcmcia_request_irq(link, orinoco_interrupt); +#else + ret = pcmcia_request_irq(link, &link->irq); +#endif if (ret) goto failed; @@ -244,8 +357,16 @@ spectrum_cs_config(struct pcmcia_device } /* Register an interface with the stack */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) if (orinoco_if_add(priv, link->resource[0]->start, link->irq, NULL) != 0) { +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) + if (orinoco_if_add(priv, link->io.BasePort1, + link->irq, NULL) != 0) { +#else + if (orinoco_if_add(priv, link->io.BasePort1, + link->irq.AssignedIRQ, NULL) != 0) { +#endif printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } @@ -311,7 +432,13 @@ MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ static struct pcmcia_driver orinoco_driver = { .owner = THIS_MODULE, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) .name = DRIVER_NAME, +#else + .drv = { + .name = DRIVER_NAME, + }, +#endif .probe = spectrum_cs_probe, .remove = spectrum_cs_detach, .suspend = spectrum_cs_suspend, --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -504,7 +504,11 @@ static int ssb_devices_register(struct s break; case SSB_BUSTYPE_PCMCIA: #ifdef CONFIG_SSB_PCMCIAHOST +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) sdev->irq = bus->host_pcmcia->irq; +#else + sdev->irq = bus->host_pcmcia->irq.AssignedIRQ; +#endif dev->parent = &bus->host_pcmcia->dev; #endif break; compat-drivers-2012-09-18/patches/network/65-ignore-dismantle.patch0000644000175000017500000000136212026176526024232 0ustar mcgrofmcgrofThe following patch makes use of the dismantle member in struct net_device, this patch removes that access on older kernels. commit 9d5d496c3464b7ad0ba942b4ada5f27c07e07079 Author: Daniel Drake Date: Mon Jul 30 22:58:04 2012 +0100 libertas: don't reset card on error when it is being removed --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -592,7 +592,11 @@ static int lbs_thread(void *data) /* Reset card, but only when it isn't in the process * of being shutdown anyway. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)) if (!dev->dismantle && priv->reset_card) +#else + if (priv->reset_card) +#endif priv->reset_card(priv); } priv->cmd_timed_out = 0; compat-drivers-2012-09-18/patches/network/12-mac80211-disable-tx-status.patch0000644000175000017500000000124612026176511025463 0ustar mcgrofmcgrofWe can't possibly backport the wifi TX status since skb_shinfo()->tx_flags used to be a union and there is no way to make the & work properly in that case. So we need to just ifdef this part out. --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1952,6 +1952,7 @@ netdev_tx_t ieee80211_subif_start_xmit(s goto fail; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) if (unlikely(!multicast && skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { struct sk_buff *orig_skb = skb; @@ -1990,6 +1991,7 @@ netdev_tx_t ieee80211_subif_start_xmit(s skb = orig_skb; } } +#endif /* * If the skb is shared we need to obtain our own copy. compat-drivers-2012-09-18/patches/network/50-libertas-olpc-ec-wakeup.patch0000644000175000017500000000130612026176522025374 0ustar mcgrofmcgrofThis section of the libertas driver calls functions that simply don't exist before the release of 3.1. This code in question was an addition, not a change from any existing code. It is safe to simply remove it for older kernels. --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -962,6 +962,7 @@ static int if_usb_suspend(struct usb_int goto out; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) #ifdef CONFIG_OLPC if (machine_is_olpc()) { if (priv->wol_criteria == EHS_REMOVE_WAKEUP) @@ -970,6 +971,7 @@ static int if_usb_suspend(struct usb_int olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); } #endif +#endif ret = lbs_suspend(priv); if (ret) compat-drivers-2012-09-18/patches/network/53-pr_fmt.patch0000644000175000017500000005147412026176522022262 0ustar mcgrofmcgrofThis is the correct way to use pr_fmt. This helps avoid compiler warnings. This is going to be sent upstream. --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -1,10 +1,10 @@ #ifndef LINUX_BCMA_PRIVATE_H_ #define LINUX_BCMA_PRIVATE_H_ -#ifndef pr_fmt +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#endif +#include #include #include --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -10,9 +10,11 @@ * Distribute under GPL. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -27,9 +27,11 @@ * *****************************************************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -27,9 +27,11 @@ * *****************************************************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -6,8 +6,10 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -2,8 +2,10 @@ * This file contains functions used in USB interface module. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -26,9 +26,11 @@ * if_sdio_card_to_host() to pad the data. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -17,8 +17,10 @@ * (at your option) any later version. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -2,8 +2,10 @@ * This file contains the handling of RX in wlan driver. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -21,8 +21,10 @@ */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -1,5 +1,7 @@ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -4,8 +4,10 @@ * thread etc.. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas_tf/cmd.c +++ b/drivers/net/wireless/libertas_tf/cmd.c @@ -7,8 +7,10 @@ * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -9,11 +9,13 @@ */ #define DRV_NAME "lbtf_usb" +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "libertas_tf.h" #include "if_usb.h" +#include #include #include #include --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -7,8 +7,10 @@ * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c @@ -14,9 +14,11 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -15,8 +15,10 @@ */ #define __UNDEF_NO_VERSION__ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c @@ -13,9 +13,10 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -14,8 +14,10 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmutil/utils.c +++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c @@ -14,8 +14,10 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -14,8 +14,10 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -19,8 +19,10 @@ * For certain dcmd codes, the dongle interprets string data from the host. ******************************************************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -15,8 +15,10 @@ */ /* ****************** SDIO CARD Interface Functions **************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -14,10 +14,12 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -14,9 +14,11 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -15,8 +15,10 @@ */ /* ***** SDIO interface chip backplane handle functions ***** */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -16,9 +16,11 @@ /* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -14,6 +14,7 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -24,9 +24,11 @@ /* Bluetooth kernel library. */ +#undef pr_fmt #define pr_fmt(fmt) "Bluetooth: " fmt #include +#include #include --- a/net/wireless/lib80211.c +++ b/net/wireless/lib80211.c @@ -13,8 +13,10 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -42,9 +42,11 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -4,8 +4,10 @@ * Copyright 2006-2010 Johannes Berg */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -10,8 +10,10 @@ * more details. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c @@ -16,8 +16,10 @@ * File contents: support functions for PCI/PCIe */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -30,8 +30,10 @@ #ifndef __RTL_WIFI_H__ #define __RTL_WIFI_H__ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/main.c +++ b/drivers/net/wireless/ath/main.c @@ -14,9 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include "ath.h" --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -14,9 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include --- a/drivers/net/wireless/ath/ath5k/initvals.c +++ b/drivers/net/wireless/ath/ath5k/initvals.c @@ -19,8 +19,11 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "ath5k.h" #include "reg.h" #include "debug.h" --- a/drivers/net/wireless/ath/ath5k/desc.c +++ b/drivers/net/wireless/ath/ath5k/desc.c @@ -21,8 +21,11 @@ Hardware Descriptor Functions \******************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "ath5k.h" #include "reg.h" #include "debug.h" --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -29,8 +29,11 @@ * status registers (ISR). */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "ath5k.h" #include "reg.h" #include "debug.h" --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -20,8 +20,11 @@ Queue Control Unit, DCF Control Unit Functions \********************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "ath5k.h" #include "reg.h" #include "debug.h" --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -22,8 +22,10 @@ * PHY related functions * \***********************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -23,10 +23,12 @@ Reset function and helpers \****************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include /* To determine if a card is pci-e */ #include #include --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -20,8 +20,10 @@ * Attach/Detach Functions and helpers * \*************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include "ath5k.h" --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -40,8 +40,10 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -39,8 +39,10 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include "ath5k.h" --- a/drivers/net/wireless/ath/ath5k/ani.c +++ b/drivers/net/wireless/ath/ath5k/ani.c @@ -14,8 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "ath5k.h" #include "reg.h" #include "debug.h" --- a/drivers/net/wireless/ath/ath5k/sysfs.c +++ b/drivers/net/wireless/ath/ath5k/sysfs.c @@ -1,5 +1,7 @@ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -41,11 +41,14 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include +#include + #include "ath5k.h" #include "base.h" #include "reg.h" --- a/drivers/net/wireless/ath/ath5k/pci.c +++ b/drivers/net/wireless/ath/ath5k/pci.c @@ -14,8 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -15,8 +15,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -16,8 +16,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -15,8 +15,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "core.h" #include "hif-ops.h" #include "cfg80211.h" --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -15,8 +15,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "core.h" #include "debug.h" #include "htc-ops.h" --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -14,8 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -14,8 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -14,8 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "htc.h" static int htc_issue_send(struct htc_target *target, struct sk_buff* skb, --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -14,8 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include + #include "htc.h" MODULE_AUTHOR("Atheros Communications"); --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -27,8 +27,10 @@ * *****************************************************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -61,8 +61,10 @@ * *****************************************************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -21,8 +21,10 @@ * EEPROM access functions and helpers * \*************************************/ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include "ath5k.h" --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -58,8 +58,10 @@ * THE POSSIBILITY OF SUCH DAMAGES. */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include compat-drivers-2012-09-18/patches/network/22-multiqueue.patch0000644000175000017500000000325512026176513023160 0ustar mcgrofmcgrofBackport multiqueue support for kernels 2.6.23-27 The 2.6.23 kernel added some initial multiqueue support. That release relied on the on the notion of struct net_device_subqueue attached to the netdevice struct as an array. The 2.6.27 renamed these to struct netdev_queue, and enhanced MQ support by providing locks separately onto each queue. MQ support on 2.6.27 also extended each netdev to be able to assign a select_queue callback to be used by core networking for prior to pushing the skb out to the device driver so that queue selection can be dealt with and customized internally on the driver. For kernels 2.6.23..2.6.26 then we backport MQ support by using the equivalent calls on the struct netdev_queue to the struct net_device_subqueue. The performance penalty here is just that all these queues share a common lock so stateful operations on one queue would imply a delay on other queues. The select_queue callback was only added as of 2.6.27 via commit eae792b7 so for kernels 2.6.23 up to 2.6.27 we must ensure we do the selection of the queue once the core networking calls mac80211's dev_hard_start_xmit() (ndo_start_xmit() callback on newer kernels). This patch thus only addresses the lack of select_queue on kernels older than 2.6.27, naming differences are handled in compat. --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1482,6 +1482,10 @@ void ieee80211_xmit(struct ieee80211_sub return; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) + /* Older kernels do not have the select_queue callback */ + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb)); +#endif ieee80211_set_qos_hdr(sdata, skb); ieee80211_tx(sdata, skb, false); rcu_read_unlock(); compat-drivers-2012-09-18/patches/network/35-fix-makefile-includes.patch0000644000175000017500000000425212026176516025133 0ustar mcgrofmcgrofWhen some other headers are included in some makefile it must be relative to the current object file processed. When giving the full path the make process will search in the kernel tree for the headers. --- a/drivers/net/wireless/rtl818x/rtl8180/Makefile +++ b/drivers/net/wireless/rtl818x/rtl8180/Makefile @@ -2,4 +2,4 @@ rtl8180-objs := dev.o rtl8225.o sa2400. obj-$(CONFIG_RTL8180) += rtl8180.o -ccflags-y += -Idrivers/net/wireless/rtl818x +ccflags-y += -I$(obj)/.. --- a/drivers/net/wireless/rtl818x/rtl8187/Makefile +++ b/drivers/net/wireless/rtl818x/rtl8187/Makefile @@ -2,4 +2,4 @@ rtl8187-objs := dev.o rtl8225.o leds.o obj-$(CONFIG_RTL8187) += rtl8187.o -ccflags-y += -Idrivers/net/wireless/rtl818x +ccflags-y += -I$(obj)/.. --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -16,8 +16,8 @@ # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ccflags-y += \ - -Idrivers/net/wireless/brcm80211/brcmfmac \ - -Idrivers/net/wireless/brcm80211/include + -I$(obj) \ + -I$(obj)/../include ccflags-y += -D__CHECK_ENDIAN__ --- a/drivers/net/wireless/brcm80211/brcmsmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmsmac/Makefile @@ -17,9 +17,9 @@ ccflags-y := \ -D__CHECK_ENDIAN__ \ - -Idrivers/net/wireless/brcm80211/brcmsmac \ - -Idrivers/net/wireless/brcm80211/brcmsmac/phy \ - -Idrivers/net/wireless/brcm80211/include + -I$(obj) \ + -I$(obj)/phy \ + -I$(obj)/../include BRCMSMAC_OFILES := \ mac80211_if.o \ --- a/drivers/net/wireless/brcm80211/brcmutil/Makefile +++ b/drivers/net/wireless/brcm80211/brcmutil/Makefile @@ -16,8 +16,8 @@ # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ccflags-y := \ - -Idrivers/net/wireless/brcm80211/brcmutil \ - -Idrivers/net/wireless/brcm80211/include + -I$(obj) \ + -I$(obj)/../include BRCMUTIL_OFILES := \ utils.o --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -18,6 +18,6 @@ cfg80211-$(CONFIG_CFG80211_INTERNAL_REGD ccflags-y += -D__CHECK_ENDIAN__ $(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk - @$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@ + @$(AWK) -f $(src)/genregdb.awk < $< > $@ clean-files := regdb.c compat-drivers-2012-09-18/patches/network/05-usb.patch0000644000175000017500000000135112026176506021550 0ustar mcgrofmcgrofUSB opt soft_unbid was added as of 2.6.27. --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -1139,7 +1139,9 @@ static struct usb_driver p54u_driver = { .resume = p54u_resume, .reset_resume = p54u_resume, #endif /* CONFIG_PM */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) .soft_unbind = 1, +#endif .disable_hub_initiated_lpm = 1, }; --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1368,7 +1368,9 @@ static struct usb_driver ath9k_hif_usb_d .reset_resume = ath9k_hif_usb_resume, #endif .id_table = ath9k_hif_usb_ids, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) .soft_unbind = 1, +#endif .disable_hub_initiated_lpm = 1, }; compat-drivers-2012-09-18/patches/network/61-netdev-addr_assign_type.patch0000644000175000017500000000373412026176525025573 0ustar mcgrofmcgrofYou cannot backport assignment of netdev->addr_assign_type given that its part of the netdev data structure only in future kernels. mcgrof@tux ~/linux-next (git::master)$ git describe --contains c1f79426 v2.6.36-rc1~571^2~104 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -476,7 +476,9 @@ static int atl1c_set_mac_addr(struct net memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type &= ~NET_ADDR_RANDOM; +#endif atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr); @@ -2569,7 +2571,9 @@ static int __devinit atl1c_probe(struct } if (atl1c_read_mac_addr(&adapter->hw)) { /* got a random MAC address, set NET_ADDR_RANDOM to netdev */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type |= NET_ADDR_RANDOM; +#endif } memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); --- a/drivers/net/ethernet/atheros/atlx/atlx.c +++ b/drivers/net/ethernet/atheros/atlx/atlx.c @@ -84,7 +84,9 @@ static int atlx_set_mac(struct net_devic memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type &= ~NET_ADDR_RANDOM; +#endif atlx_set_mac_addr(&adapter->hw); return 0; --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -3061,7 +3061,9 @@ static int __devinit atl1_probe(struct p /* copy the MAC address out of the EEPROM */ if (atl1_read_mac_addr(&adapter->hw)) { /* mark random mac */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) netdev->addr_assign_type |= NET_ADDR_RANDOM; +#endif } memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); compat-drivers-2012-09-18/patches/network/52-tty-dev.patch0000644000175000017500000000201412026176522022350 0ustar mcgrofmcgrof tty->dev does not exist until 2.6.37. Typically subsystems will assign the tty device to a child to make a symlink under /sys/class/foobar. An example is the bluetooth subsystem. commit 7f4b2b04c88377af30c022f36c060190182850fb Author: Andrei Warkentin Date: Fri Feb 11 17:19:26 2011 -0600 Bluetooth: Make hci a child of the corresponding tty device. Make /sys/class/bluetooth/hciX a symlink to path under corresponding tty. Signed-off-by: Andrei Warkentin Signed-off-by: Gustavo F. Padovan --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -426,7 +426,10 @@ static int hci_uart_register_dev(struct hdev->close = hci_uart_close; hdev->flush = hci_uart_flush; hdev->send = hci_uart_send_frame; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) SET_HCIDEV_DEV(hdev, hu->tty->dev); +#endif if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); compat-drivers-2012-09-18/patches/network/48-use_skb_get_queue_mapping.patch0000644000175000017500000000264712026176522026206 0ustar mcgrofmcgrofUse skb_get_queue_mapping() for getting the queue_mapping member of skb. Some old kernels do not have the member queue_mapping, but this function always returns something. --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3424,11 +3424,11 @@ static void b43_op_tx(struct ieee80211_h } B43_WARN_ON(skb_shinfo(skb)->nr_frags); - skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb); - if (!wl->tx_queue_stopped[skb->queue_mapping]) { + skb_queue_tail(&wl->tx_queue[skb_get_queue_mapping(skb)], skb); + if (!wl->tx_queue_stopped[skb_get_queue_mapping(skb)]) { ieee80211_queue_work(wl->hw, &wl->tx_work); } else { - ieee80211_stop_queue(wl->hw, skb->queue_mapping); + ieee80211_stop_queue(wl->hw, skb_get_queue_mapping(skb)); } } --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2504,11 +2504,11 @@ static void b43legacy_op_tx(struct ieee8 } B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags); - skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb); - if (!wl->tx_queue_stopped[skb->queue_mapping]) + skb_queue_tail(&wl->tx_queue[skb_get_queue_mapping(skb)], skb); + if (!wl->tx_queue_stopped[skb_get_queue_mapping(skb)]) ieee80211_queue_work(wl->hw, &wl->tx_work); else - ieee80211_stop_queue(wl->hw, skb->queue_mapping); + ieee80211_stop_queue(wl->hw, skb_get_queue_mapping(skb)); } static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, compat-drivers-2012-09-18/patches/network/0004-wext-namespace.patch0000644000175000017500000000311312026176505024034 0ustar mcgrofmcgrof This patch backports the namespace changes added through net/wireless/wext.c. --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -342,6 +342,7 @@ static const int compat_event_type_size[ /* IW event code */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static int __net_init wext_pernet_init(struct net *net) { skb_queue_head_init(&net->wext_nlevents); @@ -384,6 +385,29 @@ static void wireless_nlevent_process(str static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); +#else +/* Older kernels get the old way of doing stuff*/ +static struct sk_buff_head wireless_nlevent_queue; + +static int __init wireless_nlevent_init(void) +{ + skb_queue_head_init(&wireless_nlevent_queue); + return 0; +} + +subsys_initcall(wireless_nlevent_init); + +static void wireless_nlevent_process(unsigned long data) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&wireless_nlevent_queue))) + rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); +} + +static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); + +#endif + static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, struct sk_buff *skb) { @@ -597,8 +621,13 @@ void wireless_send_event(struct net_devi skb_shinfo(skb)->frag_list = compskb; #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); schedule_work(&wireless_nlevent_work); +#else + skb_queue_tail(&wireless_nlevent_queue, skb); + tasklet_schedule(&wireless_nlevent_tasklet); +#endif } EXPORT_SYMBOL(wireless_send_event); compat-drivers-2012-09-18/patches/network/54-get_ts_info.patch0000644000175000017500000000301212026176523023256 0ustar mcgrofmcgrofLinux 3.5 will have get_ts_info to support the Precision Time Protocol. http://linuxptp.sourceforge.net/ http://en.wikipedia.org/wiki/Precision_Time_Protocol We cannot backport this support given that this introduces a data structure change on the ethtool_ops. commit c8f3a8c31069137fe0100e6920558f1a7487ef3c Author: Richard Cochran Date: Tue Apr 3 22:59:17 2012 +0000 ethtool: Introduce a method for getting time stamping capabilities. This commit adds a new ethtool ioctl that exposes the SO_TIMESTAMPING capabilities of a network interface. In addition, user space programs can use this ioctl to discover the PTP Hardware Clock (PHC) device associated with the interface. Since software receive time stamps are handled by the stack, the generic ethtool code can answer the query correctly in case the MAC or PHY drivers lack special time stamping features. Signed-off-by: Richard Cochran Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -908,7 +908,9 @@ static const struct ethtool_ops usbnet_e .get_drvinfo = usbnet_get_drvinfo, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .get_ts_info = ethtool_op_get_ts_info, +#endif }; /*-------------------------------------------------------------------------*/ compat-drivers-2012-09-18/patches/network/46-use_other_workqueue.patch0000644000175000017500000000121212026176521025061 0ustar mcgrofmcgrofOld kernel versions do not support WQ_HIGHPRI and WQ_MEM_RECLAIM so we should use create_singlethread_workqueue() which was used at this position before. --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1726,8 +1726,12 @@ int hci_register_dev(struct hci_dev *hde list_add(&hdev->list, &hci_dev_list); write_unlock(&hci_dev_list_lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM, 1); +#else + hdev->workqueue = create_singlethread_workqueue(hdev->name); +#endif if (!hdev->workqueue) { error = -ENOMEM; goto err; compat-drivers-2012-09-18/patches/network/0001-netdev_ops.patch0000644000175000017500000002433512026176504023266 0ustar mcgrofmcgrof This patch backports the struct net_device_ops changes added on 2.6.29. If we add the compat.git netdev_attach_ops() implementation for newer kernels upstream it means we do not have to use this patch at all for older kernels. mcgrof@tux ~/linux-stable (git::master)$ git describe --contains d314774cf2cd5dfeb39a00d37deee65d4c627927 v2.6.29-rc1~581^2~677 commit d314774cf2cd5dfeb39a00d37deee65d4c627927 Author: Stephen Hemminger Date: Wed Nov 19 21:32:24 2008 -0800 netdev: network device operations infrastructure This patch changes the network device internal API to move adminstrative operations out of the network device structure and into a separate structure. This patch involves some hackery to maintain compatablity between the new and old model, so all 300+ drivers don't have to be changed at once. For drivers that aren't converted yet, the netdevice_ops virt function list still resides in the net_device structure. For old protocols, the new net_device_ops are copied out to the old net_device pointers. After the transistion is completed the nag message can be changed to an WARN_ON, and the compatiablity code can be made configurable. Some function pointers aren't moved: * destructor can't be in net_device_ops because it may need to be referenced after the module is unloaded. * neighbor setup is manipulated in a couple of places that need special consideration * hard_start_xmit is in the fast path for transmit. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -358,7 +358,7 @@ generic_rndis_bind(struct usbnet *dev, s dev->rx_urb_size &= ~(dev->maxpacket - 1); u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); - net->netdev_ops = &rndis_netdev_ops; + netdev_attach_ops(net, &rndis_netdev_ops); retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1417,7 +1417,7 @@ usbnet_probe (struct usb_interface *udev net->features |= NETIF_F_HIGHDMA; #endif - net->netdev_ops = &usbnet_netdev_ops; + netdev_attach_ops(net, &usbnet_netdev_ops); net->watchdog_timeo = TX_TIMEOUT_JIFFIES; net->ethtool_ops = &usbnet_ethtool_ops; --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1285,7 +1285,7 @@ static const struct net_device_ops ath6k void init_netdev(struct net_device *dev) { - dev->netdev_ops = &ath6kl_netdev_ops; + netdev_attach_ops(dev, &ath6kl_netdev_ops); dev->destructor = free_netdev; dev->watchdog_timeo = ATH6KL_TX_TIMEOUT; --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -3436,7 +3436,7 @@ static int rndis_wlan_bind(struct usbnet * rndis_host wants to avoid all OID as much as possible * so do promisc/multicast handling in rndis_wlan. */ - usbdev->net->netdev_ops = &rndis_wlan_netdev_ops; + netdev_attach_ops(usbdev->net, &rndis_wlan_netdev_ops); tmp = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST); retval = rndis_set_oid(usbdev, --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2175,7 +2175,7 @@ static int __devinit b44_init_one(struct bp->rx_pending = B44_DEF_RX_RING_PENDING; bp->tx_pending = B44_DEF_TX_RING_PENDING; - dev->netdev_ops = &b44_netdev_ops; + netdev_attach_ops(dev, &b44_netdev_ops); netif_napi_add(dev, &bp->napi, b44_poll, 64); dev->watchdog_timeo = B44_TX_TIMEOUT; dev->irq = sdev->irq; --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -6106,7 +6106,7 @@ static struct net_device *ipw2100_alloc_ priv->ieee->perfect_rssi = -20; priv->ieee->worst_rssi = -85; - dev->netdev_ops = &ipw2100_netdev_ops; + netdev_attach_ops(dev, &ipw2100_netdev_ops); dev->ethtool_ops = &ipw2100_ethtool_ops; dev->wireless_handlers = &ipw2100_wx_handler_def; priv->wireless_data.libipw = priv->ieee; --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -11684,7 +11684,7 @@ static int ipw_prom_alloc(struct ipw_pri memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN); priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP; - priv->prom_net_dev->netdev_ops = &ipw_prom_netdev_ops; + netdev_attach_ops(priv->prom_net_dev, &ipw_prom_netdev_ops); priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR; SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev); @@ -11822,7 +11822,7 @@ static int __devinit ipw_pci_probe(struc priv->ieee->perfect_rssi = -20; priv->ieee->worst_rssi = -85; - net_dev->netdev_ops = &ipw_netdev_ops; + netdev_attach_ops(net_dev, &ipw_netdev_ops); priv->wireless_data.spy_data = &priv->ieee->spy_data; net_dev->wireless_data = &priv->wireless_data; net_dev->wireless_handlers = &ipw_wx_handler_def; --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -993,7 +993,7 @@ struct lbs_private *lbs_add_card(void *c wdev->netdev = dev; priv->dev = dev; - dev->netdev_ops = &lbs_netdev_ops; + netdev_attach_ops(dev, &lbs_netdev_ops); dev->watchdog_timeo = 5 * HZ; dev->ethtool_ops = &lbs_ethtool_ops; dev->flags |= IFF_BROADCAST | IFF_MULTICAST; --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -1015,7 +1015,7 @@ static int lbs_add_mesh(struct lbs_priva mesh_dev->ieee80211_ptr = mesh_wdev; priv->mesh_dev = mesh_dev; - mesh_dev->netdev_ops = &mesh_netdev_ops; + netdev_attach_ops(mesh_dev, &mesh_netdev_ops); mesh_dev->ethtool_ops = &lbs_ethtool_ops; memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN); --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1299,7 +1299,7 @@ static const struct net_device_ops hwsim static void hwsim_mon_setup(struct net_device *dev) { - dev->netdev_ops = &hwsim_netdev_ops; + netdev_attach_ops(dev, &hwsim_netdev_ops); dev->destructor = free_netdev; ether_setup(dev); dev->tx_queue_len = 0; --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -638,7 +638,7 @@ static const struct net_device_ops mwifi void mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev) { - dev->netdev_ops = &mwifiex_netdev_ops; + netdev_attach_ops(dev, &mwifiex_netdev_ops); /* Initialize private structure */ priv->current_key_index = 0; priv->media_connected = false; --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -224,7 +224,7 @@ void bnep_net_setup(struct net_device *d ether_setup(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; - dev->netdev_ops = &bnep_netdev_ops; + netdev_attach_ops(dev, &bnep_netdev_ops); dev->watchdog_timeo = HZ * 2; } --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -2211,7 +2211,7 @@ static int atl1e_init_netdev(struct net_ SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); - netdev->netdev_ops = &atl1e_netdev_ops; + netdev_attach_ops(netdev, &atl1e_netdev_ops); netdev->watchdog_timeo = AT_TX_WATCHDOG; atl1e_set_ethtool_ops(netdev); --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -2416,7 +2416,7 @@ static int atl1c_init_netdev(struct net_ SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); - netdev->netdev_ops = &atl1c_netdev_ops; + netdev_attach_ops(netdev, &atl1c_netdev_ops); netdev->watchdog_timeo = AT_TX_WATCHDOG; atl1c_set_ethtool_ops(netdev); --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -3010,7 +3010,7 @@ static int __devinit atl1_probe(struct p adapter->mii.phy_id_mask = 0x1f; adapter->mii.reg_num_mask = 0x1f; - netdev->netdev_ops = &atl1_netdev_ops; + netdev_attach_ops(netdev, &atl1_netdev_ops); netdev->watchdog_timeo = 5 * HZ; netif_napi_add(netdev, &adapter->napi, atl1_rings_clean, 64); --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -1400,7 +1400,7 @@ static int __devinit atl2_probe(struct p atl2_setup_pcicmd(pdev); - netdev->netdev_ops = &atl2_netdev_ops; + netdev_attach_ops(netdev, &atl2_netdev_ops); atl2_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -978,7 +978,7 @@ static void ieee80211_if_setup(struct ne { ether_setup(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; - dev->netdev_ops = &ieee80211_dataif_ops; + netdev_attach_ops(dev, &ieee80211_dataif_ops); dev->destructor = free_netdev; } @@ -1134,7 +1134,7 @@ static void ieee80211_setup_sdata(struct /* only monitor/p2p-device differ */ if (sdata->dev) { - sdata->dev->netdev_ops = &ieee80211_dataif_ops; + netdev_attach_ops(sdata->dev, &ieee80211_dataif_ops); sdata->dev->type = ARPHRD_ETHER; } @@ -1168,7 +1168,7 @@ static void ieee80211_setup_sdata(struct break; case NL80211_IFTYPE_MONITOR: sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP; - sdata->dev->netdev_ops = &ieee80211_monitorif_ops; + netdev_attach_ops(sdata->dev, &ieee80211_monitorif_ops); sdata->u.mntr_flags = MONITOR_FLAG_CONTROL | MONITOR_FLAG_OTHER_BSS; break; --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -2278,9 +2278,9 @@ int orinoco_if_add(struct orinoco_privat #endif /* Default to standard ops if not set */ if (ops) - dev->netdev_ops = ops; + netdev_attach_ops(dev, ops); else - dev->netdev_ops = &orinoco_netdev_ops; + netdev_attach_ops(dev, &orinoco_netdev_ops); /* we use the default eth_mac_addr for setting the MAC addr */ --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -925,9 +925,7 @@ static int wireless_process_ioctl(struct return private(dev, iwr, cmd, info, handler); } /* Old driver API : call driver ioctl handler */ - if (dev->netdev_ops->ndo_do_ioctl) - return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd); - return -EOPNOTSUPP; + return ndo_do_ioctl(dev, ifr, cmd); } /* If command is `set a parameter', or `get the encoding parameters', compat-drivers-2012-09-18/patches/network/09-cfg80211-wext-padding.patch0000644000175000017500000000420112026176507024505 0ustar mcgrofmcgrofThis is a tricky one. Consider a kernel that has this code in net/wireless/wext-core.c: #ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) handlers = dev->ieee80211_ptr->wiphy->wext; #endif #ifdef CONFIG_WIRELESS_EXT if (dev->wireless_handlers) handlers = dev->wireless_handlers; #endif If a kernel is compiled without CONFIG_WIRELESS_EXT then compat-drivers can't do wireless extensions against it. However, if the kernel is compiled with CONFIG_CFG80211_WEXT then it will try to get the wext handlers from struct wiphy. Now, struct wiphy in the base kernel and struct wiphy in compat-drivers don't match, so the kernel crashes!! To fix this, add lots of padding to compat-drivers's struct wiphy so that the "wext" pointer is guaranteed to be NULL. Make sure the padding is larger than the struct so we don't ever run into this again because the wext pointer moved due to struct enlargements. --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2155,6 +2155,9 @@ struct wiphy_wowlan_support { struct wiphy { /* assign these fields before you register the wiphy */ +#define WIPHY_COMPAT_PAD_SIZE 2048 + u8 padding[WIPHY_COMPAT_PAD_SIZE]; + /* permanent MAC address(es) */ u8 perm_addr[ETH_ALEN]; u8 addr_mask[ETH_ALEN]; --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -289,6 +289,17 @@ struct wiphy *wiphy_new(const struct cfg struct cfg80211_registered_device *rdev; int alloc_size; + /* + * Make sure the padding is >= the rest of the struct so that we + * always keep it large enough to pad out the entire original + * kernel's struct. We really only need to make sure it's larger + * than the kernel compat is compiled against, but since it'll + * only increase in size make sure it's larger than the current + * version of it. Subtract since it's included. + */ + BUILD_BUG_ON(WIPHY_COMPAT_PAD_SIZE < + sizeof(struct wiphy) - WIPHY_COMPAT_PAD_SIZE); + WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); WARN_ON(ops->connect && !ops->disconnect); compat-drivers-2012-09-18/patches/network/62-usb_driver_lpm.patch0000644000175000017500000002041512026176525024001 0ustar mcgrofmcgrof--- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -452,7 +452,9 @@ static struct usb_driver ath3k_driver = .probe = ath3k_probe, .disconnect = ath3k_disconnect, .id_table = ath3k_table, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(ath3k_driver); --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -273,7 +273,9 @@ static struct usb_driver bcm203x_driver .probe = bcm203x_probe, .disconnect = bcm203x_disconnect, .id_table = bcm203x_table, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(bcm203x_driver); --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -745,7 +745,9 @@ static struct usb_driver bfusb_driver = .probe = bfusb_probe, .disconnect = bfusb_disconnect, .id_table = bfusb_table, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(bfusb_driver); --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -504,7 +504,9 @@ static struct usb_driver bpa10x_driver = .probe = bpa10x_probe, .disconnect = bpa10x_disconnect, .id_table = bpa10x_table, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(bpa10x_driver); --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1221,7 +1221,9 @@ static struct usb_driver btusb_driver = #endif .id_table = btusb_table, .supports_autosuspend = 1, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(btusb_driver); --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -705,7 +705,9 @@ static struct usb_driver cdc_driver = { .resume = usbnet_resume, .reset_resume = usbnet_resume, .supports_autosuspend = 1, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(cdc_driver); --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -643,7 +643,9 @@ static struct usb_driver rndis_driver = .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(rndis_driver); --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -2458,7 +2458,9 @@ static struct usb_driver at76_driver = { .probe = at76_probe, .disconnect = at76_disconnect, .id_table = dev_table, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; static int __init at76_mod_init(void) --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1191,7 +1191,9 @@ static struct usb_driver ath6kl_usb_driv .disconnect = ath6kl_usb_remove, .id_table = ath6kl_usb_ids, .supports_autosuspend = true, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; static int ath6kl_usb_init(void) --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1371,7 +1371,9 @@ static struct usb_driver ath9k_hif_usb_d #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) .soft_unbind = 1, #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; int ath9k_hif_usb_init(void) --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -1159,7 +1159,9 @@ static struct usb_driver carl9170_driver .resume = carl9170_usb_resume, .reset_resume = carl9170_usb_resume, #endif /* CONFIG_PM */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(carl9170_driver); --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1593,7 +1593,9 @@ static struct usb_driver brcmf_usbdrvr = .suspend = brcmf_usb_suspend, .resume = brcmf_usb_resume, .supports_autosuspend = 1, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; void brcmf_usb_exit(void) --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -1015,7 +1015,9 @@ static struct usb_driver if_usb_driver = .suspend = if_usb_suspend, .resume = if_usb_resume, .reset_resume = if_usb_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(if_usb_driver); --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -922,7 +922,9 @@ static struct usb_driver if_usb_driver = .id_table = if_usb_table, .suspend = if_usb_suspend, .resume = if_usb_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(if_usb_driver); --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -1752,7 +1752,9 @@ static struct usb_driver orinoco_driver .probe = ezusb_probe, .disconnect = ezusb_disconnect, .id_table = ezusb_table, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(orinoco_driver); --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -1142,7 +1142,9 @@ static struct usb_driver p54u_driver = { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) .soft_unbind = 1, #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(p54u_driver); --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -3755,7 +3755,9 @@ static struct usb_driver rndis_wlan_driv .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(rndis_wlan_driver); --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1989,7 +1989,9 @@ static struct usb_driver rt2500usb_drive .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(rt2500usb_driver); --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -1283,7 +1283,9 @@ static struct usb_driver rt2800usb_drive .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(rt2800usb_driver); --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2536,7 +2536,9 @@ static struct usb_driver rt73usb_driver .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(rt73usb_driver); --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -1673,7 +1673,9 @@ static struct usb_driver rtl8187_driver .id_table = rtl8187_table, .probe = rtl8187_probe, .disconnect = __devexit_p(rtl8187_disconnect), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(rtl8187_driver); --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -376,7 +376,9 @@ static struct usb_driver rtl8192cu_drive #ifdef CONFIG_AUTOSUSPEND .supports_autosuspend = 1, #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; module_usb_driver(rtl8192cu_driver); --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -1541,7 +1541,9 @@ static struct usb_driver driver = { .disconnect = disconnect, .pre_reset = pre_reset, .post_reset = post_reset, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .disable_hub_initiated_lpm = 1, +#endif }; struct workqueue_struct *zd_workqueue; compat-drivers-2012-09-18/patches/network/30-bridge-port.patch0000644000175000017500000000316212026176515023175 0ustar mcgrofmcgrofThe patch titled: bridge: use rx_handler_data pointer to store net_bridge_port pointer by Jiri Pirko moved the br_ports pointer out of the netdev which older kernels relied on and then added a flag to the netdev to check for it as follows: - dev->br_port) { + (dev->priv_flags & IFF_BRIDGE_PORT)) { So to backport this we instead rely on a br_port_exists() call which compat.git provides and depending on the kernel it will either check for the dev->br_port or the IFF_BRIDGE_PORT flag. A patch to get the blow code merged upstream and to use br_port_exists() everywhere else will be submitted but not sure if it will be accepted. This patch can be removed once we get br_port_exists() accessible to drivers and not just private bridge code. If that patch doesn't get accepted upstream we'l have to carry this patch around for compat-drivers. --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -809,7 +809,7 @@ int cfg80211_change_iface(struct cfg8021 return -EOPNOTSUPP; /* if it's part of a bridge, reject changing type to station/ibss */ - if ((dev->priv_flags & IFF_BRIDGE_PORT) && + if (br_port_exists(dev) && (ntype == NL80211_IFTYPE_ADHOC || ntype == NL80211_IFTYPE_STATION || ntype == NL80211_IFTYPE_P2P_CLIENT)) --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1881,7 +1881,7 @@ static int nl80211_valid_4addr(struct cf enum nl80211_iftype iftype) { if (!use_4addr) { - if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT)) + if (netdev && br_port_exists(netdev)) return -EBUSY; return 0; } compat-drivers-2012-09-18/patches/network/32-remove-ns-type.patch0000644000175000017500000000125412026176516023654 0ustar mcgrofmcgrof--- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -121,12 +121,14 @@ static int wiphy_resume(struct device *d return ret; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static const void *wiphy_namespace(struct device *d) { struct wiphy *wiphy = container_of(d, struct wiphy, dev); return wiphy_net(wiphy); } +#endif struct class ieee80211_class = { .name = "ieee80211", @@ -138,8 +140,10 @@ struct class ieee80211_class = { #endif .suspend = wiphy_suspend, .resume = wiphy_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) .ns_type = &net_ns_type_operations, .namespace = wiphy_namespace, +#endif }; int wiphy_sysfs_init(void) compat-drivers-2012-09-18/patches/network/21-capi-proc_fops.patch0000644000175000017500000000400612026176513023657 0ustar mcgrofmcgrofBackport kernel patch 9a58a80a701bdb2d220cdab4914218df5b48d781 proc_fops: convert drivers/isdn/ to seq_file --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -21,8 +21,10 @@ */ #include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) #include #include +#endif #include #include #include @@ -522,6 +524,7 @@ static char *cmtp_procinfo(struct capi_c return "CAPI Message Transport Protocol"; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int cmtp_proc_show(struct seq_file *m, void *v) { struct capi_ctr *ctrl = m->private; @@ -554,6 +557,36 @@ static const struct file_operations cmtp .release = single_release, }; +#else + +static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) +{ + struct cmtp_session *session = ctrl->driverdata; + struct cmtp_application *app; + struct list_head *p, *n; + int len = 0; + + len += sprintf(page + len, "%s\n\n", cmtp_procinfo(ctrl)); + len += sprintf(page + len, "addr %s\n", session->name); + len += sprintf(page + len, "ctrl %d\n", session->num); + + list_for_each_safe(p, n, &session->applications) { + app = list_entry(p, struct cmtp_application, list); + len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping); + } + + if (off + count >= len) + *eof = 1; + + if (len < off) + return 0; + + *start = page + off; + + return ((count < len - off) ? count : len - off); +} +#endif + int cmtp_attach_device(struct cmtp_session *session) { unsigned char buf[4]; @@ -592,7 +625,11 @@ int cmtp_attach_device(struct cmtp_sessi session->ctrl.send_message = cmtp_send_message; session->ctrl.procinfo = cmtp_procinfo; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) session->ctrl.proc_fops = &cmtp_proc_fops; +#else + session->ctrl.ctr_read_proc = cmtp_ctr_read_proc; +#endif if (attach_capi_ctr(&session->ctrl) < 0) { BT_ERR("Can't attach new controller"); compat-drivers-2012-09-18/patches/network/43-rename_pm_qos_request.patch0000644000175000017500000000100112026176520025342 0ustar mcgrofmcgrof--- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -175,7 +175,11 @@ that only one external action is invoked #define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) +static struct pm_qos_request_list ipw2100_pm_qos_req; +#else static struct pm_qos_request ipw2100_pm_qos_req; +#endif /* Debugging stuff */ #ifdef CONFIG_IPW2100_DEBUG compat-drivers-2012-09-18/patches/network/14-device-type.patch0000644000175000017500000000317212026176511023174 0ustar mcgrofmcgrofKernels >= 2.6.32 can identify the type of device netdevice so that sysfs can be used to get this. We never really had a systematic way of doing this -- now we do through the SET_NETDEV_DEVTYPE() macro. For older kernels we make the SET_NETDEV_DEVTYPE() be a no-op this means the wireless type we define is unused so we ucomment it simply to avoid a compile warning. --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -794,9 +794,11 @@ void cfg80211_unregister_wdev(struct wir } EXPORT_SYMBOL(cfg80211_unregister_wdev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type wiphy_type = { .name = "wlan", }; +#endif void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num) --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -528,9 +528,11 @@ static struct device *bnep_get_device(st return conn ? &conn->dev : NULL; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type bnep_type = { .name = "bluetooth", }; +#endif int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) { --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1334,13 +1334,17 @@ static const struct net_device_ops usbne // precondition: never called in_interrupt +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type wlan_type = { .name = "wlan", }; +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) static struct device_type wwan_type = { .name = "wwan", }; +#endif int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) compat-drivers-2012-09-18/patches/network/06-header-changes.patch0000644000175000017500000000540012026176506023615 0ustar mcgrofmcgrof Every kernel release there are a few changes to headers made. Some code gets shifted around between headers or new headers are defined. This patch deals with such cases. --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -18,6 +18,10 @@ #include "debug.h" #include "hif-ops.h" +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) +#include +#endif + #define HTC_PACKET_CONTAINER_ALLOCATION 32 #define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH) --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -3,6 +3,9 @@ #include #include +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,28)) +#include +#endif struct b43_wldev; --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -36,6 +36,7 @@ #include "regd.h" #include +#include #include /* --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -24,6 +24,9 @@ #include #include #include +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,28)) +#include +#endif #include #include #include --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -24,6 +24,9 @@ #include #include #include +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,28)) +#include +#endif #include #include --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -24,6 +24,9 @@ #include "aes_ccm.h" #include "aes_cmac.h" +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) +#include +#endif /** * DOC: Key handling basics --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -26,6 +26,7 @@ #include #include +#include #include "bnep.h" --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -25,6 +25,7 @@ /* Bluetooth HCI sockets. */ #include +#include #include #include --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -22,6 +22,7 @@ #include #include +#include #include "hidp.h" --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "core.h" #include "cfg80211.h" compat-drivers-2012-09-18/patches/network/26-sdio-quirks.patch0000644000175000017500000000174612026176515023244 0ustar mcgrofmcgrofThe quirks attribute is not available on older kernels. --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -852,6 +852,7 @@ static int if_sdio_power_on(struct if_sd if (ret) goto release; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) /* For 1-bit transfers to the 8686 model, we need to enable the * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 * bit to allow access to non-vendor registers. */ @@ -870,6 +871,7 @@ static int if_sdio_power_on(struct if_sd if (ret) goto disable; } +#endif card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); if (ret) --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -75,7 +75,9 @@ mwifiex_sdio_probe(struct sdio_func *fun card->func = func; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; +#endif sdio_claim_host(func); ret = sdio_enable_func(func); compat-drivers-2012-09-18/patches/network/57-iwlwifi-debug-fix.patch0000644000175000017500000000113112026176524024304 0ustar mcgrofmcgrofDue to compat including linux/device.h before we do, we don't get a chance to #define DEBUG to get our debugging prints. This patch replaces dev_dbg with dev_printk to make them show up again. --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/iwlwifi/iwl-debug.c @@ -138,8 +138,9 @@ void __iwl_dbg(struct device *dev, va_copy(args2, args); vaf.va = &args2; - dev_dbg(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U', - function, &vaf); + dev_printk(KERN_DEBUG, dev, "%c %s %pV", + in_interrupt() ? 'I' : 'U', + function, &vaf); va_end(args2); } #endif compat-drivers-2012-09-18/patches/network/55-iwlwifi-msg-trace-fix.patch0000644000175000017500000000332612026176524025106 0ustar mcgrofmcgrofIn recent kernels, %pV will copy the va_list before using it. This isn't true for all kernels, so copy the va_list for use by the dev_*() functions, otherwise the kernel will crash if the message is printed and traced. --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/iwlwifi/iwl-debug.c @@ -75,13 +75,16 @@ void __iwl_ ##fn(struct device *dev, con struct va_format vaf = { \ .fmt = fmt, \ }; \ - va_list args; \ + va_list args1, args2; \ \ - va_start(args, fmt); \ - vaf.va = &args; \ + va_start(args1, fmt); \ + va_copy(args2, args1); \ + vaf.va = &args2; \ dev_ ##fn(dev, "%pV", &vaf); \ + va_end(args2); \ + vaf.va = &args1; \ trace_iwlwifi_ ##fn(&vaf); \ - va_end(args); \ + va_end(args1); \ } __iwl_fn(warn) @@ -100,13 +103,18 @@ void __iwl_err(struct device *dev, bool va_list args; va_start(args, fmt); - vaf.va = &args; if (!trace_only) { + va_list args2; + + va_copy(args2, args); + vaf.va = &args2; if (rfkill_prefix) dev_err(dev, "(RFKILL) %pV", &vaf); else dev_err(dev, "%pV", &vaf); + va_end(args2); } + vaf.va = &args; trace_iwlwifi_err(&vaf); va_end(args); } @@ -123,13 +131,19 @@ void __iwl_dbg(struct device *dev, va_list args; va_start(args, fmt); - vaf.va = &args; #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(level) && - (!limit || net_ratelimit())) + (!limit || net_ratelimit())) { + va_list args2; + + va_copy(args2, args); + vaf.va = &args2; dev_dbg(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U', function, &vaf); + va_end(args2); + } #endif + vaf.va = &args; trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); va_end(args); } compat-drivers-2012-09-18/patches/network/47-no_trans_start_on_netdev_queue.patch0000644000175000017500000000076712026176521027301 0ustar mcgrofmcgrofThe struct netdev_queue does not have the attribute trans_start in kernel < 2.6.31. trans_start on struct net_device does the same on older kernels. --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -372,8 +372,10 @@ void mwifiex_set_trans_start(struct net_ { int i; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) for (i = 0; i < dev->num_tx_queues; i++) netdev_get_tx_queue(dev, i)->trans_start = jiffies; +#endif dev->trans_start = jiffies; } compat-drivers-2012-09-18/patches/network/10-add-wext-handlers-to-netdev.patch0000644000175000017500000000357212026176510026173 0ustar mcgrofmcgrofThe patch "wext: refactor" by Johannes Berg refactored wext code so that new kernels no longer get the wext handlers through struct netdevice, instead they get it through the struct wiphy which is cfg80211 specific. For old kernels this means you get not wext handlers anymore when backporting code, this adds the wext handler back to the netdevice wireless_handlers to let compat users use wext again. We do this for every kernel version because the struct wiphy is changing from kernel version to version. At least the struct from kernel 2.6.33 and 2.6.34 are incompatible and the kernel would dereference some wrong type in the struct and oops. The old interface is not affected by this. This will cause that CONFIG_CFG80211_WEXT still depends on CONFIG_WIRELESS_EXT in compat-drivers. --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -342,10 +342,6 @@ struct wiphy *wiphy_new(const struct cfg INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); -#ifdef CONFIG_CFG80211_WEXT - rdev->wiphy.wext = &cfg80211_wext_handler; -#endif - device_initialize(&rdev->wiphy.dev); rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; @@ -862,6 +858,15 @@ static int cfg80211_netdev_notifier_call wdev->sme_state = CFG80211_SME_IDLE; mutex_unlock(&rdev->devlist_mtx); #ifdef CONFIG_CFG80211_WEXT +#ifdef CONFIG_WIRELESS_EXT + if (!dev->wireless_handlers) + dev->wireless_handlers = &cfg80211_wext_handler; +#else + printk_once(KERN_WARNING "cfg80211: wext will not work because " + "kernel was compiled with CONFIG_WIRELESS_EXT=n. " + "Tools using wext interface, like iwconfig will " + "not work.\n"); +#endif wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; compat-drivers-2012-09-18/patches/network/64-b44-32bit-stats.patch0000644000175000017500000000323012026176526023432 0ustar mcgrofmcgrofThis patch requires a manual backport: commit eeda8585522bcc173f91d6254dfa63e871087c54 Author: Kevin Groeneveld Date: Tue Jul 17 17:46:01 2012 +0000 b44: add 64 bit stats Add support for 64 bit stats to Broadcom b44 ethernet driver. Signed-off-by: Kevin Groeneveld Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -1641,10 +1642,17 @@ static int b44_close(struct net_device * return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) static struct rtnl_link_stats64 *b44_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *nstat) +#else +static struct net_device_stats *b44_get_stats(struct net_device *dev) +#endif { struct b44 *bp = netdev_priv(dev); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) + struct net_device_stats *nstat = &dev->stats; +#endif struct b44_hw_stats *hwstat = &bp->hw_stats; unsigned int start; @@ -2135,7 +2143,11 @@ static const struct net_device_ops b44_n .ndo_open = b44_open, .ndo_stop = b44_close, .ndo_start_xmit = b44_start_xmit, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) .ndo_get_stats64 = b44_get_stats64, +#else + .ndo_get_stats = b44_get_stats, +#endif .ndo_set_rx_mode = b44_set_rx_mode, .ndo_set_mac_address = b44_set_mac_addr, .ndo_validate_addr = eth_validate_addr, compat-drivers-2012-09-18/patches/network/40-netdev-hw-features.patch0000644000175000017500000003136312026176520024475 0ustar mcgrofmcgrofThis reverts the commits that deal with hw_features and set_features, fix_features for kernels < 2.6.39. Below is one example commit being reverted, but we also do this for ath6kl and any driver that uses this in this file. commit 782d640afd15af7a1faf01cfe566ca4ac511319d Author: MichaÅ‚ MirosÅ‚aw Date: Thu Apr 7 07:32:18 2011 +0000 net: atl*: convert to hw_features Things left as they were: - atl1: is RX checksum really enabled? - atl2: copy-paste from atl1, with-errors-on-modify I presume - atl1c: there's a bug: MTU can't be changed if device is not up Signed-off-by: MichaÅ‚ MirosÅ‚aw Signed-off-by: David S. Miller --- a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c @@ -114,6 +114,13 @@ static int atl1c_set_settings(struct net return 0; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) +static u32 atl1c_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ + static u32 atl1c_get_msglevel(struct net_device *netdev) { struct atl1c_adapter *adapter = netdev_priv(netdev); @@ -301,6 +308,11 @@ static const struct ethtool_ops atl1c_et .get_link = ethtool_op_get_link, .get_eeprom_len = atl1c_get_eeprom_len, .get_eeprom = atl1c_get_eeprom, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) + .get_tx_csum = atl1c_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; void atl1c_set_ethtool_ops(struct net_device *netdev) --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -492,6 +492,7 @@ static void atl1c_set_rxbufsize(struct a roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atl1c_fix_features(struct net_device *netdev, netdev_features_t features) { @@ -520,6 +521,7 @@ static int atl1c_set_features(struct net return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /** * atl1c_change_mtu - Change the Maximum Transfer Unit @@ -552,8 +554,19 @@ static int atl1c_change_mtu(struct net_d netdev->mtu = new_mtu; adapter->hw.max_frame_size = new_mtu; atl1c_set_rxbufsize(adapter, netdev); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) + if (new_mtu > MAX_TSO_FRAME_SIZE) { + adapter->netdev->features &= ~NETIF_F_TSO; + adapter->netdev->features &= ~NETIF_F_TSO6; + } else { + adapter->netdev->features |= NETIF_F_TSO; + adapter->netdev->features |= NETIF_F_TSO6; + } +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ atl1c_down(adapter); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev_update_features(netdev); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ atl1c_up(adapter); clear_bit(__AT_RESETTING, &adapter->flags); } @@ -2405,8 +2418,10 @@ static const struct net_device_ops atl1c .ndo_set_mac_address = atl1c_set_mac_addr, .ndo_set_rx_mode = atl1c_set_multi, .ndo_change_mtu = atl1c_change_mtu, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atl1c_fix_features, .ndo_set_features = atl1c_set_features, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_do_ioctl = atl1c_ioctl, .ndo_tx_timeout = atl1c_tx_timeout, .ndo_get_stats = atl1c_get_stats, @@ -2425,6 +2440,7 @@ static int atl1c_init_netdev(struct net_ atl1c_set_ethtool_ops(netdev); /* TODO: add when ready */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_RX | @@ -2432,6 +2448,14 @@ static int atl1c_init_netdev(struct net_ NETIF_F_TSO6; netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_TX; +#else + netdev->features = NETIF_F_SG | + NETIF_F_HW_CSUM | + NETIF_F_HW_VLAN_TX | + NETIF_F_HW_VLAN_RX | + NETIF_F_TSO | + NETIF_F_TSO6; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ return 0; } --- a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c @@ -384,6 +384,11 @@ static const struct ethtool_ops atl1e_et .get_eeprom_len = atl1e_get_eeprom_len, .get_eeprom = atl1e_get_eeprom, .set_eeprom = atl1e_set_eeprom, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) + .set_tx_csum = ethtool_op_set_tx_hw_csum, + .set_sg = ethtool_op_set_sg, + .set_tso = ethtool_op_set_tso, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; void atl1e_set_ethtool_ops(struct net_device *netdev) --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -375,6 +375,7 @@ static int atl1e_set_mac_addr(struct net return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atl1e_fix_features(struct net_device *netdev, netdev_features_t features) { @@ -400,6 +401,7 @@ static int atl1e_set_features(struct net return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /** * atl1e_change_mtu - Change the Maximum Transfer Unit @@ -1929,7 +1931,11 @@ void atl1e_down(struct atl1e_adapter *ad * reschedule our watchdog timer */ set_bit(__AT_DOWN, &adapter->flags); +#if defined(NETIF_F_LLTX) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netif_stop_queue(netdev); +#else + netif_tx_disable(netdev); +#endif /* reset MAC to disable all RX/TX */ atl1e_reset_hw(&adapter->hw); @@ -2199,8 +2205,10 @@ static const struct net_device_ops atl1e .ndo_set_rx_mode = atl1e_set_multi, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = atl1e_set_mac_addr, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atl1e_fix_features, .ndo_set_features = atl1e_set_features, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_change_mtu = atl1e_change_mtu, .ndo_do_ioctl = atl1e_ioctl, .ndo_tx_timeout = atl1e_tx_timeout, @@ -2220,10 +2228,15 @@ static int atl1e_init_netdev(struct net_ netdev->watchdog_timeo = AT_TX_WATCHDOG; atl1e_set_ethtool_ops(netdev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_HW_VLAN_RX; netdev->features = netdev->hw_features | NETIF_F_LLTX | NETIF_F_HW_VLAN_TX; +#else + netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | + NETIF_F_HW_VLAN_RX | NETIF_F_LLTX | NETIF_F_HW_VLAN_TX; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ return 0; } --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -2917,8 +2917,10 @@ static const struct net_device_ops atl1_ .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = atl1_set_mac, .ndo_change_mtu = atl1_change_mtu, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atlx_fix_features, .ndo_set_features = atlx_set_features, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_do_ioctl = atlx_ioctl, .ndo_tx_timeout = atlx_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -3029,11 +3031,13 @@ static int __devinit atl1_probe(struct p netdev->features |= NETIF_F_SG; netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_VLAN_RX; /* is this valid? see atl1_setup_mac_ctrl() */ netdev->features |= NETIF_F_RXCSUM; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /* * patch for some L1 of old version, @@ -3646,6 +3650,14 @@ static int atl1_set_pauseparam(struct ne return 0; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) +/* FIXME: is this right? -- CHS */ +static u32 atl1_get_rx_csum(struct net_device *netdev) +{ + return 1; +} +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ + static void atl1_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { @@ -3718,4 +3730,10 @@ static const struct ethtool_ops atl1_eth .nway_reset = atl1_nway_reset, .get_ethtool_stats = atl1_get_ethtool_stats, .get_sset_count = atl1_get_sset_count, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) + .get_rx_csum = atl1_get_rx_csum, + .set_tx_csum = ethtool_op_set_tx_hw_csum, + .set_sg = ethtool_op_set_sg, + .set_tso = ethtool_op_set_tso, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -396,6 +396,7 @@ static void atl2_restore_vlan(struct atl atl2_vlan_mode(adapter->netdev, adapter->netdev->features); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atl2_fix_features(struct net_device *netdev, netdev_features_t features) { @@ -421,6 +422,7 @@ static int atl2_set_features(struct net_ return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ static void atl2_intr_rx(struct atl2_adapter *adapter) { @@ -1322,8 +1324,10 @@ static const struct net_device_ops atl2_ .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = atl2_set_mac, .ndo_change_mtu = atl2_change_mtu, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = atl2_fix_features, .ndo_set_features = atl2_set_features, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_do_ioctl = atl2_ioctl, .ndo_tx_timeout = atl2_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -1421,8 +1425,12 @@ static int __devinit atl2_probe(struct p err = -EIO; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_RX; +#endif +#if defined(NETIF_F_HW_VLAN_TX) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); +#endif /* Init PHY as early as possible due to power saving issue */ atl2_phy_init(&adapter->hw); @@ -1849,6 +1857,13 @@ static int atl2_set_settings(struct net_ return 0; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) +static u32 atl2_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ + static u32 atl2_get_msglevel(struct net_device *netdev) { return 0; @@ -2118,6 +2133,14 @@ static const struct ethtool_ops atl2_eth .get_eeprom_len = atl2_get_eeprom_len, .get_eeprom = atl2_get_eeprom, .set_eeprom = atl2_set_eeprom, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) + .get_tx_csum = atl2_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +#ifdef NETIF_F_TSO + .get_tso = ethtool_op_get_tso, +#endif +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ }; static void atl2_set_ethtool_ops(struct net_device *netdev) --- a/drivers/net/ethernet/atheros/atlx/atlx.c +++ b/drivers/net/ethernet/atheros/atlx/atlx.c @@ -255,6 +255,7 @@ static void atlx_restore_vlan(struct atl atlx_vlan_mode(adapter->netdev, adapter->netdev->features); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t atlx_fix_features(struct net_device *netdev, netdev_features_t features) { @@ -280,5 +281,6 @@ static int atlx_set_features(struct net_ return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ #endif /* ATLX_C */ --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1112,6 +1112,7 @@ static struct net_device_stats *ath6kl_g return &vif->net_stats; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static int ath6kl_set_features(struct net_device *dev, netdev_features_t features) { @@ -1144,6 +1145,7 @@ static int ath6kl_set_features(struct ne return err; } +#endif static void ath6kl_set_multicast_list(struct net_device *ndev) { @@ -1291,7 +1293,9 @@ static const struct net_device_ops ath6k .ndo_stop = ath6kl_close, .ndo_start_xmit = ath6kl_data_tx, .ndo_get_stats = ath6kl_get_stats, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_set_features = ath6kl_set_features, +#endif .ndo_set_rx_mode = ath6kl_set_multicast_list, }; @@ -1306,7 +1310,11 @@ void init_netdev(struct net_device *dev) sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; +#else + dev->features |= NETIF_F_IP_CSUM; +#endif return; } compat-drivers-2012-09-18/patches/network/63-tty-termios.patch0000644000175000017500000000305212026176525023264 0ustar mcgrofmcgrofThis patch requires a manual backport: commit adc8d746caa67fff4b53ba3e5163a6cbacc3b523 Author: Alan Cox Date: Sat Jul 14 15:31:47 2012 +0100 tty: move the termios object into the tty This will let us sort out a whole pile of tty related races. The alternative would be to keep points and refcount the termios objects. However 1. They are tiny anyway 2. Many devices don't use the stored copies 3. We can remove a pty special case Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman We cannot use compat.git for this given that the assignment was not done through a static inline helper. --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -58,7 +58,11 @@ static int ath_wakeup_ar3k(struct tty_st return status; /* Disable Automatic RTSCTS */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) ktermios = tty->termios; +#else + memcpy(&ktermios, tty->termios, sizeof(ktermios)); +#endif ktermios.c_cflag &= ~CRTSCTS; tty_set_termios(tty, &ktermios); --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -873,7 +873,11 @@ static int rfcomm_tty_ioctl(struct tty_s static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) struct ktermios *new = &tty->termios; +#else + struct ktermios *new = tty->termios; +#endif int old_baud_rate = tty_termios_baud_rate(old); int new_baud_rate = tty_termios_baud_rate(new); compat-drivers-2012-09-18/patches/network/03-rfkill.patch0000644000175000017500000000410512026176505022237 0ustar mcgrofmcgrofrfkill was re-implemented on 2.6.31. We port it to older kernels with a simple hack, just rename the module as a new one rfkill_backport, and every exported symbol gets redefined with a _backport postfix through compat-2.6.31.h. The changes below are the ones we could not do through compat-2.6.31.h Do older kernels have /dev/rfkill ? I not then we can just keep /dev/rfkill and not /dev/rfkill_backport. Note that 2.6.31 added netdevice notifier upon interface dev_open() which on cfg80211 will check if checks to see if rfkill is enabled (or if the mode of operation is not supported) on the cfg80211_netdev_notifier_call() and if so deny bringing the interface up. This was added via commit: 3b8bcfd5d31ea0fec58681d035544ace707d2536 Since older kernels will not have the notifier call on dev_open() if we *really want* to port this we could have mac80211's subif_open() call : ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev); ret = notifier_to_errno(ret); if (ret) return ret; This would do the policing from within mac80211. --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile @@ -2,8 +2,8 @@ # Makefile for the RF switch subsystem. # -rfkill-y += core.o -rfkill-$(CONFIG_RFKILL_INPUT) += input.o -obj-$(CONFIG_RFKILL) += rfkill.o +rfkill_backport-y += core.o +rfkill_backport-$(CONFIG_RFKILL_BACKPORT_INPUT) += input.o +obj-$(CONFIG_RFKILL_BACKPORT) += rfkill_backport.o obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o --- a/net/rfkill/input.c +++ b/net/rfkill/input.c @@ -232,7 +232,7 @@ static int rfkill_connect(struct input_h handle->dev = dev; handle->handler = handler; - handle->name = "rfkill"; + handle->name = "rfkill_backport"; /* causes rfkill_start() to be called */ error = input_register_handle(handle); --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -820,7 +820,7 @@ static int rfkill_resume(struct device * } static struct class rfkill_class = { - .name = "rfkill", + .name = "rfkill_backport", .dev_release = rfkill_release, .dev_attrs = rfkill_dev_attrs, .dev_uevent = rfkill_dev_uevent, compat-drivers-2012-09-18/patches/network/17-netdev-queue.patch0000644000175000017500000000273612026176512023376 0ustar mcgrofmcgrofThis patch addresses changes made by usage of new symbols like unregister_netdevice_queue() which are not possible to backport due to their reliance on internal symbols on net/core/dev.c The patch that introduced this on mac80211 was: mac80211: Speedup ieee80211_remove_interfaces() Speedup ieee80211_remove_interfaces() by factorizing synchronize_rcu() calls Signed-off-by: Eric Dumazet Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1580,6 +1580,7 @@ void ieee80211_sdata_stop(struct ieee802 * Remove all interfaces, may only be called at hardware unregistration * time because it doesn't do RCU-safe list removals. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) void ieee80211_remove_interfaces(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata, *tmp; @@ -1607,6 +1608,22 @@ void ieee80211_remove_interfaces(struct kfree(sdata); } } +#else +void ieee80211_remove_interfaces(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata, *tmp; + + ASSERT_RTNL(); + + list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { + mutex_lock(&local->iflist_mtx); + list_del(&sdata->list); + mutex_unlock(&local->iflist_mtx); + + unregister_netdevice(sdata->dev); + } +} +#endif static int netdev_notify(struct notifier_block *nb, unsigned long state, compat-drivers-2012-09-18/patches/README0000644000175000017500000000074612026176477016721 0ustar mcgrofmcgrof compat-drivers patches ======================= You must have a really good reason to be adding files in this directory. Your reasoning should either match the explanation already present on the top of each patch file or you should add your own. We try to avoid having patch files because: * Its a pain in the ass to maintain them. * Most backport changes can be pulled off through some macro magic or new files which implement the new functionality on the old kernels. compat-drivers-2012-09-18/patches/drm/0000755000175000017500000000000012026176477016614 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/patches/drm/99-change-makefile.patch0000644000175000017500000000157212026176477023101 0ustar mcgrofmcgrofRemove drivers that we do not want to build from gpu/drm/Makefile --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -28,21 +28,14 @@ CFLAGS_drm_trace_points.o := -I$(src) obj-$(CONFIG_DRM) += drm.o obj-$(CONFIG_DRM_USB) += drm_usb.o obj-$(CONFIG_DRM_TTM) += ttm/ -obj-$(CONFIG_DRM_TDFX) += tdfx/ -obj-$(CONFIG_DRM_R128) += r128/ obj-$(CONFIG_DRM_RADEON)+= radeon/ -obj-$(CONFIG_DRM_MGA) += mga/ obj-$(CONFIG_DRM_I810) += i810/ obj-$(CONFIG_DRM_I915) += i915/ obj-$(CONFIG_DRM_MGAG200) += mgag200/ obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/ -obj-$(CONFIG_DRM_SIS) += sis/ -obj-$(CONFIG_DRM_SAVAGE)+= savage/ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VIA) +=via/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ -obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_GMA500) += gma500/ -obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-y += i2c/ compat-drivers-2012-09-18/patches/drm/05-i915-define-acpi-video-class.patch0000644000175000017500000000101212026176477025111 0ustar mcgrofmcgrofThe definition of ACPI_VIDEO_CLASS was moved from video.c to video.h in 3.1. Define it here to fix build for kernels older than 3.1. --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -305,6 +305,9 @@ static int intel_opregion_video_event(st struct acpi_bus_event *event = data; int ret = NOTIFY_OK; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)) +#define ACPI_VIDEO_CLASS "video" +#endif if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) return NOTIFY_DONE; compat-drivers-2012-09-18/patches/drm/03-swiotlb.patch0000644000175000017500000000420012026176477021534 0ustar mcgrofmcgrof swiotlb_nr_tbl() was available since 3.2 but was exported since 3.3. Since it uses an internal global state variable, it is impossible to backport it to compat.git. So revert the changes. --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1297,11 +1297,13 @@ nouveau_ttm_tt_populate(struct ttm_tt *t } #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { return ttm_dma_populate((void *)ttm, dev->dev); } #endif +#endif r = ttm_pool_populate(ttm); if (r) { @@ -1347,12 +1349,14 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt } #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { ttm_dma_unpopulate((void *)ttm, dev->dev); return; } #endif +#endif for (i = 0; i < ttm->num_pages; i++) { if (ttm_dma->dma_address[i]) { --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -602,11 +602,13 @@ static int radeon_ttm_tt_populate(struct } #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { return ttm_dma_populate(>t->ttm, rdev->dev); } #endif +#endif r = ttm_pool_populate(ttm); if (r) { @@ -648,12 +650,14 @@ static void radeon_ttm_tt_unpopulate(str } #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { ttm_dma_unpopulate(>t->ttm, rdev->dev); return; } #endif +#endif for (i = 0; i < ttm->num_pages; i++) { if (gtt->ttm.dma_address[i]) { @@ -876,6 +880,7 @@ static int radeon_ttm_debugfs_init(struc radeon_mem_types_list[i].show = &ttm_page_alloc_debugfs; radeon_mem_types_list[i].driver_features = 0; radeon_mem_types_list[i++].data = NULL; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { sprintf(radeon_mem_types_names[i], "ttm_dma_page_pool"); @@ -885,6 +890,7 @@ static int radeon_ttm_debugfs_init(struc radeon_mem_types_list[i++].data = NULL; } #endif +#endif return radeon_debugfs_add_files(rdev, radeon_mem_types_list, i); #endif compat-drivers-2012-09-18/patches/drm/98-pr_fmt.patch0000644000175000017500000001011412026176477021357 0ustar mcgrofmcgrof Undef/define/include printk.h for fixing redefinition warnings during build. Patch adapted from compat-wireless tree. --- a/drivers/gpu/drm/ttm/ttm_agp_backend.c +++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c @@ -29,8 +29,10 @@ * Keith Packard. */ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt +#include #include "ttm/ttm_module.h" #include "ttm/ttm_bo_driver.h" #include "ttm/ttm_page_alloc.h" --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -28,11 +28,13 @@ * Authors: Thomas Hellstrom */ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt #include "ttm/ttm_module.h" #include "ttm/ttm_bo_driver.h" #include "ttm/ttm_placement.h" +#include #include #include #include --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -28,8 +28,10 @@ * Authors: Thomas Hellstrom */ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt +#include #include #include #include --- a/drivers/gpu/drm/ttm/ttm_memory.c +++ b/drivers/gpu/drm/ttm/ttm_memory.c @@ -25,11 +25,13 @@ * **************************************************************************/ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt #include "ttm/ttm_memory.h" #include "ttm/ttm_module.h" #include "ttm/ttm_page_alloc.h" +#include #include #include #include --- a/drivers/gpu/drm/ttm/ttm_object.c +++ b/drivers/gpu/drm/ttm/ttm_object.c @@ -49,10 +49,12 @@ * for fast lookup of ref objects given a base object. */ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt #include "ttm/ttm_object.h" #include "ttm/ttm_module.h" +#include #include #include #include --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -31,8 +31,10 @@ * - doesn't track currently in use pages */ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt +#include #include #include #include --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c @@ -33,8 +33,10 @@ * when freed). */ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt +#include #include #include #include /* for seq_printf */ --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -28,8 +28,10 @@ * Authors: Thomas Hellstrom */ +#undef pr_fmt #define pr_fmt(fmt) "[TTM] " fmt +#include #include #include #include --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -26,6 +26,7 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "drmP.h" @@ -36,6 +37,7 @@ #include "i915_drm.h" #include "i915_drv.h" #include "i915_trace.h" +#include #include #include #include --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -26,8 +26,10 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include "drmP.h" --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -25,8 +25,10 @@ * */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -28,8 +28,10 @@ * Chris Wilson */ +#undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include "intel_drv.h" compat-drivers-2012-09-18/patches/drm/02-revert-vm_mmap.patch0000644000175000017500000000611612026176477023021 0ustar mcgrofmcgrofvm_mmap() and vm_munmap() were introduced in kernels >= 3.4.0. Revert those changes for versions older than that. These can't be backported as they rely on non-exported symbols. --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1541,6 +1541,20 @@ int drm_mapbufs(struct drm_device *dev, retcode = -EINVAL; goto done; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(file_priv->filp, 0, map->size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + token); + up_write(¤t->mm->mmap_sem); + } else { + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(file_priv->filp, 0, dma->byte_count, + PROT_READ | PROT_WRITE, + MAP_SHARED, 0); + up_write(¤t->mm->mmap_sem); +#else virtual = vm_mmap(file_priv->filp, 0, map->size, PROT_READ | PROT_WRITE, MAP_SHARED, @@ -1549,6 +1563,7 @@ int drm_mapbufs(struct drm_device *dev, virtual = vm_mmap(file_priv->filp, 0, dma->byte_count, PROT_READ | PROT_WRITE, MAP_SHARED, 0); +#endif } if (virtual > -1024UL) { /* Real error */ --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -133,9 +133,17 @@ static int i810_map_buffer(struct drm_bu old_fops = file_priv->filp->f_op; file_priv->filp->f_op = &i810_buffer_fops; dev_priv->mmap_buffer = buf; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) + down_write(¤t->mm->mmap_sem); + buf_priv->virtual = (void *)do_mmap(file_priv->filp, 0, buf->total, + PROT_READ | PROT_WRITE, + MAP_SHARED, buf->bus_address); + up_write(¤t->mm->mmap_sem); +#else buf_priv->virtual = (void *)vm_mmap(file_priv->filp, 0, buf->total, PROT_READ | PROT_WRITE, MAP_SHARED, buf->bus_address); +#endif dev_priv->mmap_buffer = NULL; file_priv->filp->f_op = old_fops; if (IS_ERR(buf_priv->virtual)) { @@ -156,9 +164,15 @@ static int i810_unmap_buffer(struct drm_ if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EINVAL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) + down_write(¤t->mm->mmap_sem); + retcode = do_munmap(current->mm, (unsigned long)buf_priv->virtual, + (size_t) buf->total); + up_write(¤t->mm->mmap_sem); +#else retcode = vm_munmap((unsigned long)buf_priv->virtual, (size_t) buf->total); - +#endif buf_priv->currently_mapped = I810_BUF_UNMAPPED; buf_priv->virtual = NULL; --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1301,10 +1301,17 @@ i915_gem_mmap_ioctl(struct drm_device *d drm_gem_object_unreference_unlocked(obj); return -EINVAL; } - +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) + down_write(¤t->mm->mmap_sem); + addr = do_mmap(obj->filp, 0, args->size, + PROT_READ | PROT_WRITE, MAP_SHARED, + args->offset); + up_write(¤t->mm->mmap_sem); +#else addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE, MAP_SHARED, args->offset); +#endif drm_gem_object_unreference_unlocked(obj); if (IS_ERR((void *)addr)) return addr; compat-drivers-2012-09-18/patches/drm/04-revert-prime-support.patch0000644000175000017500000002524512026176477024221 0ustar mcgrofmcgrofDisable PRIME support in core drm, radeon, nouveau and i915 for kernels < 3.4.0. PRIME depends on dma-buf which is added to the kernel with 3.3 but the one in 3.3 is mostly stub, e.g. it is a skeleton API which is highly modified in 3.4. So disable PRIME for kernels < 3.4.0, not < 3.3.0. --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -137,8 +137,10 @@ static struct drm_ioctl_desc drm_ioctls[ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED), +#endif DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED), --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -267,8 +267,10 @@ static int drm_open_helper(struct inode if (dev->driver->driver_features & DRIVER_GEM) drm_gem_open(dev, priv); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_init_file_private(&priv->prime); +#endif if (dev->driver->open) { ret = dev->driver->open(dev, priv); @@ -521,8 +523,10 @@ int drm_release(struct inode *inode, str if (dev->driver->postclose) dev->driver->postclose(dev, file_priv); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_destroy_file_private(&file_priv->prime); +#endif kfree(file_priv); --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -35,7 +35,11 @@ #include #include #include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #include +#endif + #include "drmP.h" /** @file drm_gem.c @@ -204,6 +208,7 @@ EXPORT_SYMBOL(drm_gem_object_alloc); static void drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (obj->import_attach) { drm_prime_remove_imported_buf_handle(&filp->prime, obj->import_attach->dmabuf); @@ -212,6 +217,7 @@ drm_gem_remove_prime_handles(struct drm_ drm_prime_remove_imported_buf_handle(&filp->prime, obj->export_dma_buf); } +#endif } /** --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -26,6 +26,8 @@ * */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) + #include #include #include "drmP.h" @@ -350,3 +352,4 @@ void drm_prime_remove_imported_buf_handl mutex_unlock(&prime_fpriv->lock); } EXPORT_SYMBOL(drm_prime_remove_imported_buf_handle); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) */ --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -22,6 +22,7 @@ * Authors: Dave Airlie */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #include "drmP.h" #include "drm.h" @@ -230,4 +231,4 @@ fail_detach: dma_buf_detach(dma_buf, attach); return ERR_PTR(ret); } - +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) */ --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -23,6 +23,8 @@ * * Authors: Alex Deucher */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #include "drmP.h" #include "drm.h" @@ -226,3 +228,4 @@ fail_detach: dma_buf_detach(dma_buf, attach); return ERR_PTR(ret); } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) */ --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -344,8 +344,10 @@ static const struct file_operations rade static struct drm_driver kms_driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | - DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM | - DRIVER_PRIME, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) + DRIVER_PRIME | +#endif + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM, .dev_priv_size = 0, .load = radeon_driver_load_kms, .firstopen = radeon_driver_firstopen_kms, @@ -380,10 +382,12 @@ static struct drm_driver kms_driver = { .dumb_destroy = radeon_mode_dumb_destroy, .fops = &radeon_driver_kms_fops, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = radeon_gem_prime_export, .gem_prime_import = radeon_gem_prime_import, +#endif .name = DRIVER_NAME, .desc = DRIVER_DESC, --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -42,8 +42,10 @@ void radeon_gem_object_free(struct drm_g struct radeon_bo *robj = gem_to_radeon_bo(gobj); if (robj) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (robj->gem_base.import_attach) drm_prime_gem_destroy(&robj->gem_base, robj->tbo.sg); +#endif radeon_bo_unref(&robj); } } --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -583,17 +583,21 @@ static int radeon_ttm_tt_populate(struct struct radeon_ttm_tt *gtt = (void *)ttm; unsigned i; int r; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); +#endif if (ttm->state != tt_unpopulated) return 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (slave && ttm->sg) { drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, gtt->ttm.dma_address, ttm->num_pages); ttm->state = tt_unbound; return 0; } +#endif rdev = radeon_get_rdev(ttm->bdev); #if __OS_HAS_AGP @@ -637,10 +641,12 @@ static void radeon_ttm_tt_unpopulate(str struct radeon_device *rdev; struct radeon_ttm_tt *gtt = (void *)ttm; unsigned i; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); if (slave) return; +#endif rdev = radeon_get_rdev(ttm->bdev); #if __OS_HAS_AGP --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1275,11 +1275,14 @@ nouveau_ttm_tt_populate(struct ttm_tt *t struct drm_device *dev; unsigned i; int r; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); +#endif if (ttm->state != tt_unpopulated) return 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (slave && ttm->sg) { /* make userspace faulting work */ drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, @@ -1287,6 +1290,7 @@ nouveau_ttm_tt_populate(struct ttm_tt *t ttm->state = tt_unbound; return 0; } +#endif dev_priv = nouveau_bdev(ttm->bdev); dev = dev_priv->dev; @@ -1334,10 +1338,12 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt struct drm_nouveau_private *dev_priv; struct drm_device *dev; unsigned i; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); if (slave) return; +#endif dev_priv = nouveau_bdev(ttm->bdev); dev = dev_priv->dev; --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -419,7 +419,10 @@ static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | - DRIVER_MODESET | DRIVER_PRIME, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) + DRIVER_PRIME | +#endif + DRIVER_MODESET, .load = nouveau_load, .firstopen = nouveau_firstopen, .lastclose = nouveau_lastclose, @@ -441,10 +444,12 @@ static struct drm_driver driver = { .ioctls = nouveau_ioctls, .fops = &nouveau_driver_fops, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = nouveau_gem_prime_export, .gem_prime_import = nouveau_gem_prime_import, +#endif .gem_init_object = nouveau_gem_object_new, .gem_free_object = nouveau_gem_object_del, --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -23,7 +23,9 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #include +#endif #include "drmP.h" #include "drm.h" @@ -55,8 +57,10 @@ nouveau_gem_object_del(struct drm_gem_ob nouveau_bo_unpin(nvbo); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (gem->import_attach) drm_prime_gem_destroy(gem, nvbo->bo.sg); +#endif ttm_bo_unref(&bo); --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -956,7 +956,11 @@ static struct drm_driver driver = { */ .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME, +#else + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM, +#endif .load = i915_driver_load, .unload = i915_driver_unload, .open = i915_driver_open, @@ -979,10 +983,12 @@ static struct drm_driver driver = { .gem_free_object = i915_gem_free_object, .gem_vm_ops = &i915_gem_vm_ops, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = i915_gem_prime_export, .gem_prime_import = i915_gem_prime_import, +#endif .dumb_create = i915_gem_dumb_create, .dumb_map_offset = i915_gem_mmap_gtt, --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -35,7 +35,9 @@ #include #include #include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #include +#endif static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj); @@ -3700,8 +3702,10 @@ void i915_gem_free_object(struct drm_gem trace_i915_gem_object_destroy(obj); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) if (gem_obj->import_attach) drm_prime_gem_destroy(gem_obj, obj->sg_table); +#endif if (obj->phys_obj) i915_gem_detach_phys_object(dev, obj); --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -23,6 +23,7 @@ * Authors: * Dave Airlie */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #include "drmP.h" #include "i915_drv.h" #include @@ -249,3 +250,4 @@ fail_detach: dma_buf_detach(dma_buf, attach); return ERR_PTR(ret); } +#endif compat-drivers-2012-09-18/patches/drm/01-dma_buf_ops-addition.patch0000644000175000017500000000366412026176477024133 0ustar mcgrofmcgrofAssign vmap, vunmap and mmap fields in dma_buf_ops structs only if kernel version >= 3.5.0 as they are added in 3.5. The begin_cpu_access was added as of the original dma-buf code via 3.4. mcgrof@frijol ~/linux-next (git::master)$ git describe --contains fc13020e086bfedf2afb95c91c026d5af1f80107 v3.4-rc1~57^2~3 Note that the dma-buf itself was added with 3.4. It is not available on kernels < 3.4. --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -173,10 +173,14 @@ static const struct dma_buf_ops i915_dma .kmap_atomic = i915_gem_dmabuf_kmap_atomic, .kunmap = i915_gem_dmabuf_kunmap, .kunmap_atomic = i915_gem_dmabuf_kunmap_atomic, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .mmap = i915_gem_dmabuf_mmap, .vmap = i915_gem_dmabuf_vmap, .vunmap = i915_gem_dmabuf_vunmap, +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) .begin_cpu_access = i915_gem_begin_cpu_access, +#endif }; struct dma_buf *i915_gem_prime_export(struct drm_device *dev, --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -134,9 +134,11 @@ static const struct dma_buf_ops nouveau_ .kmap_atomic = nouveau_gem_kmap_atomic, .kunmap = nouveau_gem_kunmap, .kunmap_atomic = nouveau_gem_kunmap_atomic, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .mmap = nouveau_gem_prime_mmap, .vmap = nouveau_gem_prime_vmap, .vunmap = nouveau_gem_prime_vunmap, +#endif }; static int --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -134,9 +134,11 @@ const static struct dma_buf_ops radeon_d .kmap_atomic = radeon_gem_kmap_atomic, .kunmap = radeon_gem_kunmap, .kunmap_atomic = radeon_gem_kunmap_atomic, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) .mmap = radeon_gem_prime_mmap, .vmap = radeon_gem_prime_vmap, .vunmap = radeon_gem_prime_vunmap, +#endif }; static int radeon_prime_create(struct drm_device *dev, compat-drivers-2012-09-18/code-metrics.txt0000644000175000017500000000121612026211316017500 0ustar mcgrofmcgrof compat-drivers code metrics  844390 - Total upstream lines of code being pulled  3107 - backport code changes  2580 - backport code additions  527 - backport code deletions  12556 - backport from compat module  15663 - total backport code  1.8549 - % of code consists of backport work Base tree: linux-next.git Base tree version: next-20120918 compat.git: compat-2012-09-18 compat-drivers release: compat-drivers-2012-09-18 Code metrics archive: http://bit.ly/H6BTF7 compat-drivers-2012-09-18/include/0000755000175000017500000000000012026176500016011 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/crypto/0000755000175000017500000000000012026176501017332 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/crypto/aes.h0000644000175000017500000000077412025673354020272 0ustar mcgrofmcgrof#ifndef _COMPAT_CRYPTO_AES_H #define _COMPAT_CRYPTO_AES_H #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24)) #include_next #else #define AES_MIN_KEY_SIZE 16 #define AES_MAX_KEY_SIZE 32 #define AES_KEYSIZE_128 16 #define AES_KEYSIZE_192 24 #define AES_KEYSIZE_256 32 #define AES_BLOCK_SIZE 16 #define AES_MAX_KEYLENGTH (15 * 16) #define AES_MAX_KEYLENGTH_U32 (AES_MAX_KEYLENGTH / sizeof(u32)) #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24)) */ #endif compat-drivers-2012-09-18/include/trace/0000755000175000017500000000000012026176501017110 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/trace/define_trace.h0000644000175000017500000000025612025673354021703 0ustar mcgrofmcgrof#include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) #include_next #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) */ compat-drivers-2012-09-18/include/linux/0000755000175000017500000000000012026176501017151 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/linux/u64_stats_sync.h0000644000175000017500000001050612025673354022223 0ustar mcgrofmcgrof#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) #include_next #else #ifndef _LINUX_U64_STATS_SYNC_H #define _LINUX_U64_STATS_SYNC_H /* * To properly implement 64bits network statistics on 32bit and 64bit hosts, * we provide a synchronization point, that is a noop on 64bit or UP kernels. * * Key points : * 1) Use a seqcount on SMP 32bits, with low overhead. * 2) Whole thing is a noop on 64bit arches or UP kernels. * 3) Write side must ensure mutual exclusion or one seqcount update could * be lost, thus blocking readers forever. * If this synchronization point is not a mutex, but a spinlock or * spinlock_bh() or disable_bh() : * 3.1) Write side should not sleep. * 3.2) Write side should not allow preemption. * 3.3) If applicable, interrupts should be disabled. * * 4) If reader fetches several counters, there is no guarantee the whole values * are consistent (remember point 1) : this is a noop on 64bit arches anyway) * * 5) readers are allowed to sleep or be preempted/interrupted : They perform * pure reads. But if they have to fetch many values, it's better to not allow * preemptions/interruptions to avoid many retries. * * 6) If counter might be written by an interrupt, readers should block interrupts. * (On UP, there is no seqcount_t protection, a reader allowing interrupts could * read partial values) * * 7) For softirq uses, readers can use u64_stats_fetch_begin_bh() and * u64_stats_fetch_retry_bh() helpers * * Usage : * * Stats producer (writer) should use following template granted it already got * an exclusive access to counters (a lock is already taken, or per cpu * data is used [in a non preemptable context]) * * spin_lock_bh(...) or other synchronization to get exclusive access * ... * u64_stats_update_begin(&stats->syncp); * stats->bytes64 += len; // non atomic operation * stats->packets64++; // non atomic operation * u64_stats_update_end(&stats->syncp); * * While a consumer (reader) should use following template to get consistent * snapshot for each variable (but no guarantee on several ones) * * u64 tbytes, tpackets; * unsigned int start; * * do { * start = u64_stats_fetch_begin(&stats->syncp); * tbytes = stats->bytes64; // non atomic operation * tpackets = stats->packets64; // non atomic operation * } while (u64_stats_fetch_retry(&stats->syncp, start)); * * * Example of use in drivers/net/loopback.c, using per_cpu containers, * in BH disabled context. */ #include struct u64_stats_sync { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) seqcount_t seq; #endif }; static inline void u64_stats_update_begin(struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) write_seqcount_begin(&syncp->seq); #endif } static inline void u64_stats_update_end(struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) write_seqcount_end(&syncp->seq); #endif } static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_begin(&syncp->seq); #else #if BITS_PER_LONG==32 preempt_disable(); #endif return 0; #endif } static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp, unsigned int start) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_retry(&syncp->seq, start); #else #if BITS_PER_LONG==32 preempt_enable(); #endif return false; #endif } /* * In case softirq handlers can update u64 counters, readers can use following helpers * - SMP 32bit arches use seqcount protection, irq safe. * - UP 32bit must disable BH. * - 64bit have no problem atomically reading u64 values, irq safe. */ static inline unsigned int u64_stats_fetch_begin_bh(const struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_begin(&syncp->seq); #else #if BITS_PER_LONG==32 local_bh_disable(); #endif return 0; #endif } static inline bool u64_stats_fetch_retry_bh(const struct u64_stats_sync *syncp, unsigned int start) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_retry(&syncp->seq, start); #else #if BITS_PER_LONG==32 local_bh_enable(); #endif return false; #endif } #endif /* _LINUX_U64_STATS_SYNC_H */ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) */ compat-drivers-2012-09-18/include/linux/compat-2.6.39.h0000644000175000017500000001144012025673354021351 0ustar mcgrofmcgrof#ifndef LINUX_26_39_COMPAT_H #define LINUX_26_39_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) #include #include #include #define tiocmget(tty) tiocmget(tty, NULL) #define tiocmset(tty, set, clear) tiocmset(tty, NULL, set, clear) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) */ static inline int irq_set_irq_wake(unsigned int irq, unsigned int on) { return set_irq_wake(irq, on); } static inline int irq_set_chip(unsigned int irq, struct irq_chip *chip) { return set_irq_chip(irq, chip); } static inline int irq_set_handler_data(unsigned int irq, void *data) { return set_irq_data(irq, data); } static inline int irq_set_chip_data(unsigned int irq, void *data) { return set_irq_chip_data(irq, data); } static inline int irq_set_irq_type(unsigned int irq, unsigned int type) { return set_irq_type(irq, type); } static inline int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) { return set_irq_msi(irq, entry); } static inline struct irq_chip *irq_get_chip(unsigned int irq) { return get_irq_chip(irq); } static inline void *irq_get_chip_data(unsigned int irq) { return get_irq_chip_data(irq); } static inline void *irq_get_handler_data(unsigned int irq) { return get_irq_data(irq); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) static inline void *irq_data_get_irq_handler_data(struct irq_data *d) { return irq_data_get_irq_data(d); } #endif static inline struct msi_desc *irq_get_msi_desc(unsigned int irq) { return get_irq_msi(irq); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) static inline void irq_set_noprobe(unsigned int irq) { set_irq_noprobe(irq); } static inline void irq_set_probe(unsigned int irq) { set_irq_probe(irq); } #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) static inline struct irq_chip *irq_desc_get_chip(struct irq_desc *desc) { return get_irq_desc_chip(desc); } static inline void *irq_desc_get_handler_data(struct irq_desc *desc) { return get_irq_desc_data(desc); } static inline void *irq_desc_get_chip_data(struct irq_desc *desc) { return get_irq_desc_chip_data(desc); } static inline struct msi_desc *irq_desc_get_msi_desc(struct irq_desc *desc) { return get_irq_desc_msi(desc); } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) */ /* * kstrto* was included in kernel 2.6.38.4 and causes conflicts with the * version included in compat-drivers. We use strict_strtol to check if * kstrto* is already available. */ #ifndef strict_strtol /* Internal, do not use. */ int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res); int __must_check _kstrtol(const char *s, unsigned int base, long *res); int __must_check kstrtoull(const char *s, unsigned int base, unsigned long long *res); int __must_check kstrtoll(const char *s, unsigned int base, long long *res); static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res) { /* * We want to shortcut function call, but * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0. */ if (sizeof(unsigned long) == sizeof(unsigned long long) && __alignof__(unsigned long) == __alignof__(unsigned long long)) return kstrtoull(s, base, (unsigned long long *)res); else return _kstrtoul(s, base, res); } static inline int __must_check kstrtol(const char *s, unsigned int base, long *res) { /* * We want to shortcut function call, but * __builtin_types_compatible_p(long, long long) = 0. */ if (sizeof(long) == sizeof(long long) && __alignof__(long) == __alignof__(long long)) return kstrtoll(s, base, (long long *)res); else return _kstrtol(s, base, res); } int __must_check kstrtouint(const char *s, unsigned int base, unsigned int *res); int __must_check kstrtoint(const char *s, unsigned int base, int *res); static inline int __must_check kstrtou64(const char *s, unsigned int base, u64 *res) { return kstrtoull(s, base, res); } static inline int __must_check kstrtos64(const char *s, unsigned int base, s64 *res) { return kstrtoll(s, base, res); } static inline int __must_check kstrtou32(const char *s, unsigned int base, u32 *res) { return kstrtouint(s, base, res); } static inline int __must_check kstrtos32(const char *s, unsigned int base, s32 *res) { return kstrtoint(s, base, res); } int __must_check kstrtou16(const char *s, unsigned int base, u16 *res); int __must_check kstrtos16(const char *s, unsigned int base, s16 *res); int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); #endif /* ifndef strict_strtol */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ #endif /* LINUX_26_39_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-3.4.h0000644000175000017500000000702112025673354021116 0ustar mcgrofmcgrof#ifndef LINUX_3_4_COMPAT_H #define LINUX_3_4_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) /* This backports: * * commit 63b2001169e75cd71e917ec953fdab572e3f944a * Author: Thomas Gleixner * Date: Thu Dec 1 00:04:00 2011 +0100 * sched/wait: Add __wake_up_all_locked() API */ #include extern void compat_wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr); #define wake_up_all_locked(x) compat_wake_up_locked((x), TASK_NORMAL, 0) /* This backports: * * commit a8203725dfded5c1f79dca3368a4a273e24b59bb * Author: Xi Wang * Date: Mon Mar 5 15:14:41 2012 -0800 * * slab: introduce kmalloc_array() */ /* SIZE_MAX is backported in compat-3.5.h so include it */ #include static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags) { if (size != 0 && n > SIZE_MAX / size) return NULL; return __kmalloc(n * size, flags); } #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) extern const struct i2c_algorithm i2c_bit_algo; #endif extern int simple_open(struct inode *inode, struct file *file); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) #define skb_add_rx_frag(skb, i, page, off, size, truesize) \ v2_6_28_skb_add_rx_frag(skb, i, page, off, size) #else #define skb_add_rx_frag(skb, i, page, off, size, truesize) \ skb_add_rx_frag(skb, i, page, off, size) #endif #ifdef CONFIG_X86_X32_ABI #define COMPAT_USE_64BIT_TIME \ (!!(task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT)) #else #define COMPAT_USE_64BIT_TIME 0 #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)) static inline void eth_hw_addr_random(struct net_device *dev) { #error eth_hw_addr_random() needs to be implemented for < 2.6.12 } #else /* kernels >= 2.6.12 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) static inline void eth_hw_addr_random(struct net_device *dev) { get_random_bytes(dev->dev_addr, ETH_ALEN); dev->dev_addr[0] &= 0xfe; /* clear multicast bit */ dev->dev_addr[0] |= 0x02; /* set local assignment bit (IEEE802) */ } #else /* kernels >= 2.6.31 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) /* So this is 2.6.31..2.6.35 */ /* Just have the flags present, they won't really mean anything though */ #define NET_ADDR_PERM 0 /* address is permanent (default) */ #define NET_ADDR_RANDOM 1 /* address is generated randomly */ #define NET_ADDR_STOLEN 2 /* address is stolen from other device */ static inline void eth_hw_addr_random(struct net_device *dev) { random_ether_addr(dev->dev_addr); } #else /* 2.6.36 and on */ static inline void eth_hw_addr_random(struct net_device *dev) { dev_hw_addr_random(dev, dev->dev_addr); } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)) */ /* source include/linux/pci.h */ /** * module_pci_driver() - Helper macro for registering a PCI driver * @__pci_driver: pci_driver struct * * Helper macro for PCI drivers which do not do anything special in module * init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() */ #define module_pci_driver(__pci_driver) \ module_driver(__pci_driver, pci_register_driver, \ pci_unregister_driver) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) */ #endif /* LINUX_5_4_COMPAT_H */ compat-drivers-2012-09-18/include/linux/of.h0000644000175000017500000000051312025673354017734 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_OF_H #define _COMPAT_LINUX_OF_H 1 #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) #include_next #else #ifdef CONFIG_OF #include_next #endif /* CONFIG_OF */ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ #endif /* _COMPAT_LINUX_OF_H */ compat-drivers-2012-09-18/include/linux/bitops.h0000644000175000017500000001225612025673354020637 0ustar mcgrofmcgrof#ifndef _LINUX_BITOPS_H #define _LINUX_BITOPS_H #include #ifdef __KERNEL__ #define BIT(nr) (1UL << (nr)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BITS_PER_BYTE 8 #ifndef BITS_TO_LONGS /* Older kernels define this already */ #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #endif #endif extern unsigned int __sw_hweight8(unsigned int w); extern unsigned int __sw_hweight16(unsigned int w); extern unsigned int __sw_hweight32(unsigned int w); extern unsigned long __sw_hweight64(__u64 w); /* * Include this here because some architectures need generic_ffs/fls in * scope */ #include #define for_each_set_bit(bit, addr, size) \ for ((bit) = find_first_bit((addr), (size)); \ (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) static __inline__ int get_bitmask_order(unsigned int count) { int order; order = fls(count); return order; /* We could be slightly more clever with -1 here... */ } static __inline__ int get_count_order(unsigned int count) { int order; order = fls(count) - 1; if (count & (count - 1)) order++; return order; } static inline unsigned long hweight_long(unsigned long w) { return sizeof(w) == 4 ? hweight32(w) : hweight64(w); } /** * rol32 - rotate a 32-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u32 rol32(__u32 word, unsigned int shift) { return (word << shift) | (word >> (32 - shift)); } /** * ror32 - rotate a 32-bit value right * @word: value to rotate * @shift: bits to roll */ static inline __u32 ror32(__u32 word, unsigned int shift) { return (word >> shift) | (word << (32 - shift)); } /** * rol16 - rotate a 16-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u16 rol16(__u16 word, unsigned int shift) { return (word << shift) | (word >> (16 - shift)); } /** * ror16 - rotate a 16-bit value right * @word: value to rotate * @shift: bits to roll */ static inline __u16 ror16(__u16 word, unsigned int shift) { return (word >> shift) | (word << (16 - shift)); } /** * rol8 - rotate an 8-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u8 rol8(__u8 word, unsigned int shift) { return (word << shift) | (word >> (8 - shift)); } /** * ror8 - rotate an 8-bit value right * @word: value to rotate * @shift: bits to roll */ static inline __u8 ror8(__u8 word, unsigned int shift) { return (word >> shift) | (word << (8 - shift)); } /** * sign_extend32 - sign extend a 32-bit value using specified bit as sign-bit * @value: value to sign extend * @index: 0 based bit index (0<=index<32) to sign bit */ static inline __s32 sign_extend32(__u32 value, int index) { __u8 shift = 31 - index; return (__s32)(value << shift) >> shift; } static inline unsigned fls_long(unsigned long l) { if (sizeof(l) == 4) return fls(l); return fls64(l); } /** * __ffs64 - find first set bit in a 64 bit word * @word: The 64 bit word * * On 64 bit arches this is a synomyn for __ffs * The result is not defined if no bits are set, so check that @word * is non-zero before calling this. */ static inline unsigned long __ffs64(u64 word) { #if BITS_PER_LONG == 32 if (((u32)word) == 0UL) return __ffs((u32)(word >> 32)) + 32; #elif BITS_PER_LONG != 64 #error BITS_PER_LONG not 32 or 64 #endif return __ffs((unsigned long)word); } #ifdef __KERNEL__ #ifdef CONFIG_GENERIC_FIND_FIRST_BIT /** * find_first_bit - find the first set bit in a memory region * @addr: The address to start the search at * @size: The maximum size to search * * Returns the bit number of the first set bit. */ extern unsigned long find_first_bit(const unsigned long *addr, unsigned long size); /** * find_first_zero_bit - find the first cleared bit in a memory region * @addr: The address to start the search at * @size: The maximum size to search * * Returns the bit number of the first cleared bit. */ extern unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size); #endif /* CONFIG_GENERIC_FIND_FIRST_BIT */ #ifdef CONFIG_GENERIC_FIND_LAST_BIT /** * find_last_bit - find the last set bit in a memory region * @addr: The address to start the search at * @size: The maximum size to search * * Returns the bit number of the first set bit, or size. */ extern unsigned long find_last_bit(const unsigned long *addr, unsigned long size); #endif /* CONFIG_GENERIC_FIND_LAST_BIT */ #ifdef CONFIG_GENERIC_FIND_NEXT_BIT /** * find_next_bit - find the next set bit in a memory region * @addr: The address to base the search on * @offset: The bitnumber to start searching at * @size: The bitmap size in bits */ extern unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset); /** * find_next_zero_bit - find the next cleared bit in a memory region * @addr: The address to base the search on * @offset: The bitnumber to start searching at * @size: The bitmap size in bits */ extern unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset); #endif /* CONFIG_GENERIC_FIND_NEXT_BIT */ #endif /* __KERNEL__ */ #endif compat-drivers-2012-09-18/include/linux/kfifo.h0000644000175000017500000006271612025673354020443 0ustar mcgrofmcgrof#include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) #include_next #else /* * A generic kernel FIFO implementation * * Copyright (C) 2009/2010 Stefani Seibold * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _LINUX_KFIFO_H #define _LINUX_KFIFO_H /* * How to porting drivers to the new generic FIFO API: * * - Modify the declaration of the "struct kfifo *" object into a * in-place "struct kfifo" object * - Init the in-place object with kfifo_alloc() or kfifo_init() * Note: The address of the in-place "struct kfifo" object must be * passed as the first argument to this functions * - Replace the use of __kfifo_put into kfifo_in and __kfifo_get * into kfifo_out * - Replace the use of kfifo_put into kfifo_in_spinlocked and kfifo_get * into kfifo_out_spinlocked * Note: the spinlock pointer formerly passed to kfifo_init/kfifo_alloc * must be passed now to the kfifo_in_spinlocked and kfifo_out_spinlocked * as the last parameter * - The formerly __kfifo_* functions are renamed into kfifo_* */ /* * Note about locking : There is no locking required until only * one reader * and one writer is using the fifo and no kfifo_reset() will be * called * kfifo_reset_out() can be safely used, until it will be only called * in the reader thread. * For multiple writer and one reader there is only a need to lock the writer. * And vice versa for only one writer and multiple reader there is only a need * to lock the reader. */ #include #include #include #include struct __kfifo { unsigned int in; unsigned int out; unsigned int mask; unsigned int esize; void *data; }; #define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \ union { \ struct __kfifo kfifo; \ datatype *type; \ char (*rectype)[recsize]; \ ptrtype *ptr; \ const ptrtype *ptr_const; \ } #define __STRUCT_KFIFO(type, size, recsize, ptrtype) \ { \ __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \ type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \ } #define STRUCT_KFIFO(type, size) \ struct __STRUCT_KFIFO(type, size, 0, type) #define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \ { \ __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \ type buf[0]; \ } #define STRUCT_KFIFO_PTR(type) \ struct __STRUCT_KFIFO_PTR(type, 0, type) /* * define compatibility "struct kfifo" for dynamic allocated fifos */ struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void); #define STRUCT_KFIFO_REC_1(size) \ struct __STRUCT_KFIFO(unsigned char, size, 1, void) #define STRUCT_KFIFO_REC_2(size) \ struct __STRUCT_KFIFO(unsigned char, size, 2, void) /* * define kfifo_rec types */ struct kfifo_rec_ptr_1 __STRUCT_KFIFO_PTR(unsigned char, 1, void); struct kfifo_rec_ptr_2 __STRUCT_KFIFO_PTR(unsigned char, 2, void); /* * helper macro to distinguish between real in place fifo where the fifo * array is a part of the structure and the fifo type where the array is * outside of the fifo structure. */ #define __is_kfifo_ptr(fifo) (sizeof(*fifo) == sizeof(struct __kfifo)) /** * DECLARE_KFIFO_PTR - macro to declare a fifo pointer object * @fifo: name of the declared fifo * @type: type of the fifo elements */ #define DECLARE_KFIFO_PTR(fifo, type) STRUCT_KFIFO_PTR(type) fifo /** * DECLARE_KFIFO - macro to declare a fifo object * @fifo: name of the declared fifo * @type: type of the fifo elements * @size: the number of elements in the fifo, this must be a power of 2 */ #define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo /** * INIT_KFIFO - Initialize a fifo declared by DECLARE_KFIFO * @fifo: name of the declared fifo datatype */ #define INIT_KFIFO(fifo) \ (void)({ \ typeof(&(fifo)) __tmp = &(fifo); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ __kfifo->in = 0; \ __kfifo->out = 0; \ __kfifo->mask = __is_kfifo_ptr(__tmp) ? 0 : ARRAY_SIZE(__tmp->buf) - 1;\ __kfifo->esize = sizeof(*__tmp->buf); \ __kfifo->data = __is_kfifo_ptr(__tmp) ? NULL : __tmp->buf; \ }) /** * DEFINE_KFIFO - macro to define and initialize a fifo * @fifo: name of the declared fifo datatype * @type: type of the fifo elements * @size: the number of elements in the fifo, this must be a power of 2 * * Note: the macro can be used for global and local fifo data type variables. */ #define DEFINE_KFIFO(fifo, type, size) \ DECLARE_KFIFO(fifo, type, size) = \ (typeof(fifo)) { \ { \ { \ .in = 0, \ .out = 0, \ .mask = __is_kfifo_ptr(&(fifo)) ? \ 0 : \ ARRAY_SIZE((fifo).buf) - 1, \ .esize = sizeof(*(fifo).buf), \ .data = __is_kfifo_ptr(&(fifo)) ? \ NULL : \ (fifo).buf, \ } \ } \ } static inline unsigned int __must_check __kfifo_uint_must_check_helper(unsigned int val) { return val; } static inline int __must_check __kfifo_int_must_check_helper(int val) { return val; } /** * kfifo_initialized - Check if the fifo is initialized * @fifo: address of the fifo to check * * Return %true if fifo is initialized, otherwise %false. * Assumes the fifo was 0 before. */ #define kfifo_initialized(fifo) ((fifo)->kfifo.mask) /** * kfifo_esize - returns the size of the element managed by the fifo * @fifo: address of the fifo to be used */ #define kfifo_esize(fifo) ((fifo)->kfifo.esize) /** * kfifo_recsize - returns the size of the record length field * @fifo: address of the fifo to be used */ #define kfifo_recsize(fifo) (sizeof(*(fifo)->rectype)) /** * kfifo_size - returns the size of the fifo in elements * @fifo: address of the fifo to be used */ #define kfifo_size(fifo) ((fifo)->kfifo.mask + 1) /** * kfifo_reset - removes the entire fifo content * @fifo: address of the fifo to be used * * Note: usage of kfifo_reset() is dangerous. It should be only called when the * fifo is exclusived locked or when it is secured that no other thread is * accessing the fifo. */ #define kfifo_reset(fifo) \ (void)({ \ typeof((fifo) + 1) __tmp = (fifo); \ __tmp->kfifo.in = __tmp->kfifo.out = 0; \ }) /** * kfifo_reset_out - skip fifo content * @fifo: address of the fifo to be used * * Note: The usage of kfifo_reset_out() is safe until it will be only called * from the reader thread and there is only one concurrent reader. Otherwise * it is dangerous and must be handled in the same way as kfifo_reset(). */ #define kfifo_reset_out(fifo) \ (void)({ \ typeof((fifo) + 1) __tmp = (fifo); \ __tmp->kfifo.out = __tmp->kfifo.in; \ }) /** * kfifo_len - returns the number of used elements in the fifo * @fifo: address of the fifo to be used */ #define kfifo_len(fifo) \ ({ \ typeof((fifo) + 1) __tmpl = (fifo); \ __tmpl->kfifo.in - __tmpl->kfifo.out; \ }) /** * kfifo_is_empty - returns true if the fifo is empty * @fifo: address of the fifo to be used */ #define kfifo_is_empty(fifo) \ ({ \ typeof((fifo) + 1) __tmpq = (fifo); \ __tmpq->kfifo.in == __tmpq->kfifo.out; \ }) /** * kfifo_is_full - returns true if the fifo is full * @fifo: address of the fifo to be used */ #define kfifo_is_full(fifo) \ ({ \ typeof((fifo) + 1) __tmpq = (fifo); \ kfifo_len(__tmpq) > __tmpq->kfifo.mask; \ }) /** * kfifo_avail - returns the number of unused elements in the fifo * @fifo: address of the fifo to be used */ #define kfifo_avail(fifo) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmpq = (fifo); \ const size_t __recsize = sizeof(*__tmpq->rectype); \ unsigned int __avail = kfifo_size(__tmpq) - kfifo_len(__tmpq); \ (__recsize) ? ((__avail <= __recsize) ? 0 : \ __kfifo_max_r(__avail - __recsize, __recsize)) : \ __avail; \ }) \ ) /** * kfifo_skip - skip output data * @fifo: address of the fifo to be used */ #define kfifo_skip(fifo) \ (void)({ \ typeof((fifo) + 1) __tmp = (fifo); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (__recsize) \ __kfifo_skip_r(__kfifo, __recsize); \ else \ __kfifo->out++; \ }) /** * kfifo_peek_len - gets the size of the next fifo record * @fifo: address of the fifo to be used * * This function returns the size of the next fifo record in number of bytes. */ #define kfifo_peek_len(fifo) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ (!__recsize) ? kfifo_len(__tmp) * sizeof(*__tmp->type) : \ __kfifo_len_r(__kfifo, __recsize); \ }) \ ) /** * kfifo_alloc - dynamically allocates a new fifo buffer * @fifo: pointer to the fifo * @size: the number of elements in the fifo, this must be a power of 2 * @gfp_mask: get_free_pages mask, passed to kmalloc() * * This macro dynamically allocates a new fifo buffer. * * The numer of elements will be rounded-up to a power of 2. * The fifo will be release with kfifo_free(). * Return 0 if no error, otherwise an error code. */ #define kfifo_alloc(fifo, size, gfp_mask) \ __kfifo_int_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ __is_kfifo_ptr(__tmp) ? \ __kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \ -EINVAL; \ }) \ ) /** * kfifo_free - frees the fifo * @fifo: the fifo to be freed */ #define kfifo_free(fifo) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (__is_kfifo_ptr(__tmp)) \ __kfifo_free(__kfifo); \ }) /** * kfifo_init - initialize a fifo using a preallocated buffer * @fifo: the fifo to assign the buffer * @buffer: the preallocated buffer to be used * @size: the size of the internal buffer, this have to be a power of 2 * * This macro initialize a fifo using a preallocated buffer. * * The numer of elements will be rounded-up to a power of 2. * Return 0 if no error, otherwise an error code. */ #define kfifo_init(fifo, buffer, size) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ __is_kfifo_ptr(__tmp) ? \ __kfifo_init(__kfifo, buffer, size, sizeof(*__tmp->type)) : \ -EINVAL; \ }) /** * kfifo_put - put data into the fifo * @fifo: address of the fifo to be used * @val: the data to be added * * This macro copies the given value into the fifo. * It returns 0 if the fifo was full. Otherwise it returns the number * processed elements. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_put(fifo, val) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ typeof((val) + 1) __val = (val); \ unsigned int __ret; \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (0) { \ typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \ __dummy = (typeof(__val))NULL; \ } \ if (__recsize) \ __ret = __kfifo_in_r(__kfifo, __val, sizeof(*__val), \ __recsize); \ else { \ __ret = !kfifo_is_full(__tmp); \ if (__ret) { \ (__is_kfifo_ptr(__tmp) ? \ ((typeof(__tmp->type))__kfifo->data) : \ (__tmp->buf) \ )[__kfifo->in & __tmp->kfifo.mask] = \ *(typeof(__tmp->type))__val; \ smp_wmb(); \ __kfifo->in++; \ } \ } \ __ret; \ }) /** * kfifo_get - get data from the fifo * @fifo: address of the fifo to be used * @val: the var where to store the data to be added * * This macro reads the data from the fifo. * It returns 0 if the fifo was empty. Otherwise it returns the number * processed elements. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_get(fifo, val) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ typeof((val) + 1) __val = (val); \ unsigned int __ret; \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (0) \ __val = (typeof(__tmp->ptr))0; \ if (__recsize) \ __ret = __kfifo_out_r(__kfifo, __val, sizeof(*__val), \ __recsize); \ else { \ __ret = !kfifo_is_empty(__tmp); \ if (__ret) { \ *(typeof(__tmp->type))__val = \ (__is_kfifo_ptr(__tmp) ? \ ((typeof(__tmp->type))__kfifo->data) : \ (__tmp->buf) \ )[__kfifo->out & __tmp->kfifo.mask]; \ smp_wmb(); \ __kfifo->out++; \ } \ } \ __ret; \ }) \ ) /** * kfifo_peek - get data from the fifo without removing * @fifo: address of the fifo to be used * @val: the var where to store the data to be added * * This reads the data from the fifo without removing it from the fifo. * It returns 0 if the fifo was empty. Otherwise it returns the number * processed elements. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_peek(fifo, val) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ typeof((val) + 1) __val = (val); \ unsigned int __ret; \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (0) \ __val = (typeof(__tmp->ptr))NULL; \ if (__recsize) \ __ret = __kfifo_out_peek_r(__kfifo, __val, sizeof(*__val), \ __recsize); \ else { \ __ret = !kfifo_is_empty(__tmp); \ if (__ret) { \ *(typeof(__tmp->type))__val = \ (__is_kfifo_ptr(__tmp) ? \ ((typeof(__tmp->type))__kfifo->data) : \ (__tmp->buf) \ )[__kfifo->out & __tmp->kfifo.mask]; \ smp_wmb(); \ } \ } \ __ret; \ }) \ ) /** * kfifo_in - put data into the fifo * @fifo: address of the fifo to be used * @buf: the data to be added * @n: number of elements to be added * * This macro copies the given buffer into the fifo and returns the * number of copied elements. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_in(fifo, buf, n) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ typeof((buf) + 1) __buf = (buf); \ unsigned long __n = (n); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (0) { \ typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \ __dummy = (typeof(__buf))NULL; \ } \ (__recsize) ?\ __kfifo_in_r(__kfifo, __buf, __n, __recsize) : \ __kfifo_in(__kfifo, __buf, __n); \ }) /** * kfifo_in_spinlocked - put data into the fifo using a spinlock for locking * @fifo: address of the fifo to be used * @buf: the data to be added * @n: number of elements to be added * @lock: pointer to the spinlock to use for locking * * This macro copies the given values buffer into the fifo and returns the * number of copied elements. */ #define kfifo_in_spinlocked(fifo, buf, n, lock) \ ({ \ unsigned long __flags; \ unsigned int __ret; \ spin_lock_irqsave(lock, __flags); \ __ret = kfifo_in(fifo, buf, n); \ spin_unlock_irqrestore(lock, __flags); \ __ret; \ }) /* alias for kfifo_in_spinlocked, will be removed in a future release */ #define kfifo_in_locked(fifo, buf, n, lock) \ kfifo_in_spinlocked(fifo, buf, n, lock) /** * kfifo_out - get data from the fifo * @fifo: address of the fifo to be used * @buf: pointer to the storage buffer * @n: max. number of elements to get * * This macro get some data from the fifo and return the numbers of elements * copied. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_out(fifo, buf, n) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ typeof((buf) + 1) __buf = (buf); \ unsigned long __n = (n); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (0) { \ typeof(__tmp->ptr) __dummy = NULL; \ __buf = __dummy; \ } \ (__recsize) ?\ __kfifo_out_r(__kfifo, __buf, __n, __recsize) : \ __kfifo_out(__kfifo, __buf, __n); \ }) \ ) /** * kfifo_out_spinlocked - get data from the fifo using a spinlock for locking * @fifo: address of the fifo to be used * @buf: pointer to the storage buffer * @n: max. number of elements to get * @lock: pointer to the spinlock to use for locking * * This macro get the data from the fifo and return the numbers of elements * copied. */ #define kfifo_out_spinlocked(fifo, buf, n, lock) \ __kfifo_uint_must_check_helper( \ ({ \ unsigned long __flags; \ unsigned int __ret; \ spin_lock_irqsave(lock, __flags); \ __ret = kfifo_out(fifo, buf, n); \ spin_unlock_irqrestore(lock, __flags); \ __ret; \ }) \ ) /* alias for kfifo_out_spinlocked, will be removed in a future release */ #define kfifo_out_locked(fifo, buf, n, lock) \ kfifo_out_spinlocked(fifo, buf, n, lock) /** * kfifo_from_user - puts some data from user space into the fifo * @fifo: address of the fifo to be used * @from: pointer to the data to be added * @len: the length of the data to be added * @copied: pointer to output variable to store the number of copied bytes * * This macro copies at most @len bytes from the @from into the * fifo, depending of the available space and returns -EFAULT/0. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_from_user(fifo, from, len, copied) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ const void __user *__from = (from); \ unsigned int __len = (len); \ unsigned int *__copied = (copied); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ (__recsize) ? \ __kfifo_from_user_r(__kfifo, __from, __len, __copied, __recsize) : \ __kfifo_from_user(__kfifo, __from, __len, __copied); \ }) \ ) /** * kfifo_to_user - copies data from the fifo into user space * @fifo: address of the fifo to be used * @to: where the data must be copied * @len: the size of the destination buffer * @copied: pointer to output variable to store the number of copied bytes * * This macro copies at most @len bytes from the fifo into the * @to buffer and returns -EFAULT/0. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_to_user(fifo, to, len, copied) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ void __user *__to = (to); \ unsigned int __len = (len); \ unsigned int *__copied = (copied); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ (__recsize) ? \ __kfifo_to_user_r(__kfifo, __to, __len, __copied, __recsize) : \ __kfifo_to_user(__kfifo, __to, __len, __copied); \ }) \ ) /** * kfifo_dma_in_prepare - setup a scatterlist for DMA input * @fifo: address of the fifo to be used * @sgl: pointer to the scatterlist array * @nents: number of entries in the scatterlist array * @len: number of elements to transfer * * This macro fills a scatterlist for DMA input. * It returns the number entries in the scatterlist array. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macros. */ #define kfifo_dma_in_prepare(fifo, sgl, nents, len) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ struct scatterlist *__sgl = (sgl); \ int __nents = (nents); \ unsigned int __len = (len); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ (__recsize) ? \ __kfifo_dma_in_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \ __kfifo_dma_in_prepare(__kfifo, __sgl, __nents, __len); \ }) /** * kfifo_dma_in_finish - finish a DMA IN operation * @fifo: address of the fifo to be used * @len: number of bytes to received * * This macro finish a DMA IN operation. The in counter will be updated by * the len parameter. No error checking will be done. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macros. */ #define kfifo_dma_in_finish(fifo, len) \ (void)({ \ typeof((fifo) + 1) __tmp = (fifo); \ unsigned int __len = (len); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (__recsize) \ __kfifo_dma_in_finish_r(__kfifo, __len, __recsize); \ else \ __kfifo->in += __len / sizeof(*__tmp->type); \ }) /** * kfifo_dma_out_prepare - setup a scatterlist for DMA output * @fifo: address of the fifo to be used * @sgl: pointer to the scatterlist array * @nents: number of entries in the scatterlist array * @len: number of elements to transfer * * This macro fills a scatterlist for DMA output which at most @len bytes * to transfer. * It returns the number entries in the scatterlist array. * A zero means there is no space available and the scatterlist is not filled. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macros. */ #define kfifo_dma_out_prepare(fifo, sgl, nents, len) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ struct scatterlist *__sgl = (sgl); \ int __nents = (nents); \ unsigned int __len = (len); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ (__recsize) ? \ __kfifo_dma_out_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \ __kfifo_dma_out_prepare(__kfifo, __sgl, __nents, __len); \ }) /** * kfifo_dma_out_finish - finish a DMA OUT operation * @fifo: address of the fifo to be used * @len: number of bytes transferd * * This macro finish a DMA OUT operation. The out counter will be updated by * the len parameter. No error checking will be done. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macros. */ #define kfifo_dma_out_finish(fifo, len) \ (void)({ \ typeof((fifo) + 1) __tmp = (fifo); \ unsigned int __len = (len); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (__recsize) \ __kfifo_dma_out_finish_r(__kfifo, __recsize); \ else \ __kfifo->out += __len / sizeof(*__tmp->type); \ }) /** * kfifo_out_peek - gets some data from the fifo * @fifo: address of the fifo to be used * @buf: pointer to the storage buffer * @n: max. number of elements to get * * This macro get the data from the fifo and return the numbers of elements * copied. The data is not removed from the fifo. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macro. */ #define kfifo_out_peek(fifo, buf, n) \ __kfifo_uint_must_check_helper( \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ typeof((buf) + 1) __buf = (buf); \ unsigned long __n = (n); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (0) { \ typeof(__tmp->ptr) __dummy __attribute__ ((unused)) = NULL; \ __buf = __dummy; \ } \ (__recsize) ? \ __kfifo_out_peek_r(__kfifo, __buf, __n, __recsize) : \ __kfifo_out_peek(__kfifo, __buf, __n); \ }) \ ) extern int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, size_t esize, gfp_t gfp_mask); extern void __kfifo_free(struct __kfifo *fifo); extern int __kfifo_init(struct __kfifo *fifo, void *buffer, unsigned int size, size_t esize); extern unsigned int __kfifo_in(struct __kfifo *fifo, const void *buf, unsigned int len); extern unsigned int __kfifo_out(struct __kfifo *fifo, void *buf, unsigned int len); extern int __kfifo_from_user(struct __kfifo *fifo, const void __user *from, unsigned long len, unsigned int *copied); extern int __kfifo_to_user(struct __kfifo *fifo, void __user *to, unsigned long len, unsigned int *copied); extern unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len); extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len); extern unsigned int __kfifo_out_peek(struct __kfifo *fifo, void *buf, unsigned int len); extern unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf, unsigned int len, size_t recsize); extern unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize); extern int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from, unsigned long len, unsigned int *copied, size_t recsize); extern int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to, unsigned long len, unsigned int *copied, size_t recsize); extern unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len, size_t recsize); extern void __kfifo_dma_in_finish_r(struct __kfifo *fifo, unsigned int len, size_t recsize); extern unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len, size_t recsize); extern void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize); extern unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize); extern void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize); extern unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize); extern unsigned int __kfifo_max_r(unsigned int len, size_t recsize); #endif #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) */ compat-drivers-2012-09-18/include/linux/compat-3.5.h0000644000175000017500000001635312025673354021127 0ustar mcgrofmcgrof#ifndef LINUX_3_5_COMPAT_H #define LINUX_3_5_COMPAT_H #include #include #include #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)) /* * This backports: * * commit f56f821feb7b36223f309e0ec05986bb137ce418 * Author: Daniel Vetter * Date: Sun Mar 25 19:47:41 2012 +0200 * * mm: extend prefault helpers to fault in more than PAGE_SIZE * * The new functions are used by drm/i915 driver. * */ static inline int fault_in_multipages_writeable(char __user *uaddr, int size) { int ret = 0; char __user *end = uaddr + size - 1; if (unlikely(size == 0)) return ret; /* * Writing zeroes into userspace here is OK, because we know that if * the zero gets there, we'll be overwriting it. */ while (uaddr <= end) { ret = __put_user(0, uaddr); if (ret != 0) return ret; uaddr += PAGE_SIZE; } /* Check whether the range spilled into the next page. */ if (((unsigned long)uaddr & PAGE_MASK) == ((unsigned long)end & PAGE_MASK)) ret = __put_user(0, end); return ret; } static inline int fault_in_multipages_readable(const char __user *uaddr, int size) { volatile char c; int ret = 0; const char __user *end = uaddr + size - 1; if (unlikely(size == 0)) return ret; while (uaddr <= end) { ret = __get_user(c, uaddr); if (ret != 0) return ret; uaddr += PAGE_SIZE; } /* Check whether the range spilled into the next page. */ if (((unsigned long)uaddr & PAGE_MASK) == ((unsigned long)end & PAGE_MASK)) { ret = __get_user(c, end); (void)c; } return ret; } /* switcheroo is available on >= 2.6.34 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) #include /* * This backports: * * From 26ec685ff9d9c16525d8ec4c97e52fcdb187b302 Mon Sep 17 00:00:00 2001 * From: Takashi Iwai * Date: Fri, 11 May 2012 07:51:17 +0200 * Subject: [PATCH] vga_switcheroo: Introduce struct vga_switcheroo_client_ops * */ struct vga_switcheroo_client_ops { void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state); void (*reprobe)(struct pci_dev *dev); bool (*can_switch)(struct pci_dev *dev); }; /* Wrap around the old code and redefine vga_switcheroo_register_client() * for older kernels < 3.5.0. */ static inline int compat_vga_switcheroo_register_client(struct pci_dev *dev, const struct vga_switcheroo_client_ops *ops) { return vga_switcheroo_register_client(dev, ops->set_gpu_state, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)) ops->reprobe, #endif ops->can_switch); } #define vga_switcheroo_register_client(_dev, _ops) \ compat_vga_switcheroo_register_client(_dev, _ops) #endif /* This backports * * commit 14674e70119ea01549ce593d8901a797f8a90f74 * Author: Mark Brown * Date: Wed May 30 10:55:34 2012 +0200 * * i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */ /* * This backports: * * From a3860c1c5dd1137db23d7786d284939c5761d517 Mon Sep 17 00:00:00 2001 * From: Xi Wang * Date: Thu, 31 May 2012 16:26:04 -0700 * Subject: [PATCH] introduce SIZE_MAX */ #define SIZE_MAX (~(size_t)0) #include /* * This backports: * * From 76e3cc126bb223013a6b9a0e2a51238d1ef2e409 Mon Sep 17 00:00:00 2001 * From: Eric Dumazet * Date: Thu, 10 May 2012 07:51:25 +0000 * Subject: [PATCH] codel: Controlled Delay AQM */ #ifndef TCA_CODEL_MAX /* CODEL */ #define COMPAT_CODEL_BACKPORT enum { TCA_CODEL_UNSPEC, TCA_CODEL_TARGET, TCA_CODEL_LIMIT, TCA_CODEL_INTERVAL, TCA_CODEL_ECN, __TCA_CODEL_MAX }; #define TCA_CODEL_MAX (__TCA_CODEL_MAX - 1) struct tc_codel_xstats { __u32 maxpacket; /* largest packet we've seen so far */ __u32 count; /* how many drops we've done since the last time we * entered dropping state */ __u32 lastcount; /* count at entry to dropping state */ __u32 ldelay; /* in-queue delay seen by most recently dequeued packet */ __s32 drop_next; /* time to drop next packet */ __u32 drop_overlimit; /* number of time max qdisc packet limit was hit */ __u32 ecn_mark; /* number of packets we ECN marked instead of dropped */ __u32 dropping; /* are we in dropping state ? */ }; /* This backports: * * commit 4b549a2ef4bef9965d97cbd992ba67930cd3e0fe * Author: Eric Dumazet * Date: Fri May 11 09:30:50 2012 +0000 * fq_codel: Fair Queue Codel AQM */ /* FQ_CODEL */ enum { TCA_FQ_CODEL_UNSPEC, TCA_FQ_CODEL_TARGET, TCA_FQ_CODEL_LIMIT, TCA_FQ_CODEL_INTERVAL, TCA_FQ_CODEL_ECN, TCA_FQ_CODEL_FLOWS, TCA_FQ_CODEL_QUANTUM, __TCA_FQ_CODEL_MAX }; #define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1) enum { TCA_FQ_CODEL_XSTATS_QDISC, TCA_FQ_CODEL_XSTATS_CLASS, }; struct tc_fq_codel_qd_stats { __u32 maxpacket; /* largest packet we've seen so far */ __u32 drop_overlimit; /* number of time max qdisc * packet limit was hit */ __u32 ecn_mark; /* number of packets we ECN marked * instead of being dropped */ __u32 new_flow_count; /* number of time packets * created a 'new flow' */ __u32 new_flows_len; /* count of flows in new list */ __u32 old_flows_len; /* count of flows in old list */ }; struct tc_fq_codel_cl_stats { __s32 deficit; __u32 ldelay; /* in-queue delay seen by most recently * dequeued packet */ __u32 count; __u32 lastcount; __u32 dropping; __s32 drop_next; }; struct tc_fq_codel_xstats { __u32 type; union { struct tc_fq_codel_qd_stats qdisc_stats; struct tc_fq_codel_cl_stats class_stats; }; }; #endif /* TCA_CODEL_MAX */ /* Backport ether_addr_equal */ static inline bool ether_addr_equal(const u8 *addr1, const u8 *addr2) { return !compare_ether_addr(addr1, addr2); } #define net_ratelimited_function(function, ...) \ do { \ if (net_ratelimit()) \ function(__VA_ARGS__); \ } while (0) #define net_emerg_ratelimited(fmt, ...) \ net_ratelimited_function(pr_emerg, fmt, ##__VA_ARGS__) #define net_alert_ratelimited(fmt, ...) \ net_ratelimited_function(pr_alert, fmt, ##__VA_ARGS__) #define net_crit_ratelimited(fmt, ...) \ net_ratelimited_function(pr_crit, fmt, ##__VA_ARGS__) #define net_err_ratelimited(fmt, ...) \ net_ratelimited_function(pr_err, fmt, ##__VA_ARGS__) #define net_notice_ratelimited(fmt, ...) \ net_ratelimited_function(pr_notice, fmt, ##__VA_ARGS__) #define net_warn_ratelimited(fmt, ...) \ net_ratelimited_function(pr_warn, fmt, ##__VA_ARGS__) #define net_info_ratelimited(fmt, ...) \ net_ratelimited_function(pr_info, fmt, ##__VA_ARGS__) #define net_dbg_ratelimited(fmt, ...) \ net_ratelimited_function(pr_debug, fmt, ##__VA_ARGS__) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)) */ #endif /* LINUX_3_5_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.19.h0000644000175000017500000000076612025673354021360 0ustar mcgrofmcgrof#ifndef LINUX_26_19_COMPAT_H #define LINUX_26_19_COMPAT_H #include /* Compat work for 2.6.19 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) #include static inline int compat_kmem_cache_destroy(struct kmem_cache *cachep) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) return kmem_cache_destroy(cachep); #else kmem_cache_destroy(cachep); return 0; #endif } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) */ #endif /* LINUX_26_19_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.25.h0000644000175000017500000001702212025673354021346 0ustar mcgrofmcgrof#ifndef LINUX_26_25_COMPAT_H #define LINUX_26_25_COMPAT_H #include /* Compat work for 2.6.24 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Backports b718989da7 */ int __must_check pci_enable_device_mem(struct pci_dev *dev); /* * Backports 312b1485fb509c9bc32eda28ad29537896658cb8 * Author: Sam Ravnborg * Date: Mon Jan 28 20:21:15 2008 +0100 * * Introduce new section reference annotations tags: __ref, __refdata, __refconst */ #define __ref __init_refok #define __refdata __initdata_refok /* * backports 2658fa803111dae1353602e7f586de8e537803e2 */ static inline bool ipv4_is_loopback(__be32 addr) { return (addr & htonl(0xff000000)) == htonl(0x7f000000); } static inline bool ipv4_is_multicast(__be32 addr) { return (addr & htonl(0xf0000000)) == htonl(0xe0000000); } static inline bool ipv4_is_local_multicast(__be32 addr) { return (addr & htonl(0xffffff00)) == htonl(0xe0000000); } static inline bool ipv4_is_lbcast(__be32 addr) { /* limited broadcast */ return addr == htonl(INADDR_BROADCAST); } static inline bool ipv4_is_zeronet(__be32 addr) { return (addr & htonl(0xff000000)) == htonl(0x00000000); } /* Special-Use IPv4 Addresses (RFC3330) */ static inline bool ipv4_is_private_10(__be32 addr) { return (addr & htonl(0xff000000)) == htonl(0x0a000000); } static inline bool ipv4_is_private_172(__be32 addr) { return (addr & htonl(0xfff00000)) == htonl(0xac100000); } static inline bool ipv4_is_private_192(__be32 addr) { return (addr & htonl(0xffff0000)) == htonl(0xc0a80000); } static inline bool ipv4_is_linklocal_169(__be32 addr) { return (addr & htonl(0xffff0000)) == htonl(0xa9fe0000); } static inline bool ipv4_is_anycast_6to4(__be32 addr) { return (addr & htonl(0xffffff00)) == htonl(0xc0586300); } static inline bool ipv4_is_test_192(__be32 addr) { return (addr & htonl(0xffffff00)) == htonl(0xc0000200); } static inline bool ipv4_is_test_198(__be32 addr) { return (addr & htonl(0xfffe0000)) == htonl(0xc6120000); } /* * phys_addr_t was added as a generic arch typedef on 2.6.28, * that backport is dealt with in compat-2.6.28.h */ #if defined(CONFIG_X86) || defined(CONFIG_X86_64) #if defined(CONFIG_64BIT) || defined(CONFIG_X86_PAE) || defined(CONFIG_PHYS_64BIT) typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif #endif /* x86 */ /* The macro below uses a const upstream, this differs */ /** * DEFINE_PCI_DEVICE_TABLE - macro used to describe a pci device table * @_table: device table name * * This macro is used to create a struct pci_device_id array (a device table) * in a generic manner. */ #define DEFINE_PCI_DEVICE_TABLE(_table) \ const struct pci_device_id _table[] __devinitdata /* * Backport work for QoS dependencies (kernel/pm_qos_params.c) * pm-qos stuff written by mark gross mgross@linux.intel.com. * * ipw2100 now makes use of: * * pm_qos_add_requirement(), * pm_qos_update_requirement() and * pm_qos_remove_requirement() from it * * mac80211 uses the network latency to determine if to enable or not * dynamic PS. mac80211 also and registers a notifier for when * the latency changes. Since older kernels do no thave pm-qos stuff * we just implement it completley here and register it upon cfg80211 * init. I haven't tested ipw2100 on 2.6.24 though. * * This pm-qos implementation is copied verbatim from the kernel * written by mark gross mgross@linux.intel.com. You don't have * to do anythinig to use pm-qos except use the same exported * routines as used in newer kernels. The compat_pm_qos_power_init() * defned below is used by the compat module to initialize pm-qos. */ int compat_pm_qos_power_init(void); int compat_pm_qos_power_deinit(void); /* * 2.6.25 adds PM_EVENT_HIBERNATE as well here but * we don't have this on <= 2.6.23) */ #ifndef PM_EVENT_SLEEP /* some distribution have mucked with their own headers to add this.. */ #define PM_EVENT_SLEEP (PM_EVENT_SUSPEND) #endif /* Although we don't care about wimax this is needed for rfkill input stuff */ #define KEY_WIMAX 246 /* Although pm_qos stuff is not implemented on <= 2.6.24 lets keep the define */ #define PM_QOS_DEFAULT_VALUE -1 #define __WARN(foo) dump_stack() #define dev_emerg(dev, format, arg...) \ dev_printk(KERN_EMERG , dev , format , ## arg) #define dev_alert(dev, format, arg...) \ dev_printk(KERN_ALERT , dev , format , ## arg) #define dev_crit(dev, format, arg...) \ dev_printk(KERN_CRIT , dev , format , ## arg) extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); #define seq_file_net &init_net; enum nf_inet_hooks { NF_INET_PRE_ROUTING = 0, NF_INET_LOCAL_IN = 1, NF_INET_FORWARD = 2, NF_INET_LOCAL_OUT = 3, NF_INET_POST_ROUTING = 4, NF_INET_NUMHOOKS = 5 }; /* The patch: * commit 8b5f6883683c91ad7e1af32b7ceeb604d68e2865 * Author: Marcin Slusarz * Date: Fri Feb 8 04:20:12 2008 -0800 * * byteorder: move le32_add_cpu & friends from OCFS2 to core * * moves le*_add_cpu and be*_add_cpu functions from OCFS2 to core * header (1st) and converted some existing code to it. We port * it here as later kernels will most likely use it. */ static inline void le16_add_cpu(__le16 *var, u16 val) { *var = cpu_to_le16(le16_to_cpu(*var) + val); } static inline void le32_add_cpu(__le32 *var, u32 val) { *var = cpu_to_le32(le32_to_cpu(*var) + val); } static inline void le64_add_cpu(__le64 *var, u64 val) { *var = cpu_to_le64(le64_to_cpu(*var) + val); } static inline void be16_add_cpu(__be16 *var, u16 val) { u16 v = be16_to_cpu(*var); *var = cpu_to_be16(v + val); } static inline void be32_add_cpu(__be32 *var, u32 val) { u32 v = be32_to_cpu(*var); *var = cpu_to_be32(v + val); } static inline void be64_add_cpu(__be64 *var, u64 val) { u64 v = be64_to_cpu(*var); *var = cpu_to_be64(v + val); } /* 2.6.25 changes hwrng_unregister()'s behaviour by supporting * suspend of its parent device (the misc device, which is itself the * hardware random number generator). It does this by passing a parameter to * unregister_miscdev() which is not supported in older kernels. The suspend * parameter allows us to enable access to the device's hardware * number generator during suspend. As far as wireless is concerned this means * if a driver goes to suspend it you won't have the HNR available in * older kernels. */ static inline void __hwrng_unregister(struct hwrng *rng, bool suspended) { hwrng_unregister(rng); } static inline void led_classdev_unregister_suspended(struct led_classdev *lcd) { led_classdev_unregister(lcd); } /** * The following things are out of ./include/linux/kernel.h * The new iwlwifi driver is using them. */ extern int strict_strtoul(const char *, unsigned int, unsigned long *); extern int strict_strtol(const char *, unsigned int, long *); #else /* * Kernels >= 2.6.25 have pm-qos and its initialized as part of * the bootup process */ static inline int compat_pm_qos_power_init(void) { return 0; } static inline int compat_pm_qos_power_deinit(void) { return 0; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) */ #endif /* LINUX_26_25_COMPAT_H */ compat-drivers-2012-09-18/include/linux/math64.h0000644000175000017500000000041612025673354020435 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_MATH64_H #define _COMPAT_LINUX_MATH64_H 1 #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) #include_next #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) */ #endif /* _COMPAT_LINUX_MATH64_H */ compat-drivers-2012-09-18/include/linux/nl80211.h0000644000175000017500000036537712026211315020345 0ustar mcgrofmcgrof#ifndef __LINUX_NL80211_H #define __LINUX_NL80211_H /* * 802.11 netlink interface public header * * Copyright 2006-2010 Johannes Berg * Copyright 2008 Michael Wu * Copyright 2008 Luis Carlos Cobo * Copyright 2008 Michael Buesch * Copyright 2008, 2009 Luis R. Rodriguez * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include /** * DOC: Station handling * * Stations are added per interface, but a special case exists with VLAN * interfaces. When a station is bound to an AP interface, it may be moved * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). * The station is still assumed to belong to the AP interface it was added * to. * * TODO: need more info? */ /** * DOC: Frame transmission/registration support * * Frame transmission and registration support exists to allow userspace * management entities such as wpa_supplicant react to management frames * that are not being handled by the kernel. This includes, for example, * certain classes of action frames that cannot be handled in the kernel * for various reasons. * * Frame registration is done on a per-interface basis and registrations * cannot be removed other than by closing the socket. It is possible to * specify a registration filter to register, for example, only for a * certain type of action frame. In particular with action frames, those * that userspace registers for will not be returned as unhandled by the * driver, so that the registered application has to take responsibility * for doing that. * * The type of frame that can be registered for is also dependent on the * driver and interface type. The frame types are advertised in wiphy * attributes so applications know what to expect. * * NOTE: When an interface changes type while registrations are active, * these registrations are ignored until the interface type is * changed again. This means that changing the interface type can * lead to a situation that couldn't otherwise be produced, but * any such registrations will be dormant in the sense that they * will not be serviced, i.e. they will not receive any frames. * * Frame transmission allows userspace to send for example the required * responses to action frames. It is subject to some sanity checking, * but many frames can be transmitted. When a frame was transmitted, its * status is indicated to the sending socket. * * For more technical details, see the corresponding command descriptions * below. */ /** * DOC: Virtual interface / concurrency capabilities * * Some devices are able to operate with virtual MACs, they can have * more than one virtual interface. The capability handling for this * is a bit complex though, as there may be a number of restrictions * on the types of concurrency that are supported. * * To start with, each device supports the interface types listed in * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the * types there no concurrency is implied. * * Once concurrency is desired, more attributes must be observed: * To start with, since some interface types are purely managed in * software, like the AP-VLAN type in mac80211 for example, there's * an additional list of these, they can be added at any time and * are only restricted by some semantic restrictions (e.g. AP-VLAN * cannot be added without a corresponding AP interface). This list * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. * * Further, the list of supported combinations is exported. This is * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, * it exports a list of "groups", and at any point in time the * interfaces that are currently active must fall into any one of * the advertised groups. Within each group, there are restrictions * on the number of interfaces of different types that are supported * and also the number of different channels, along with potentially * some other restrictions. See &enum nl80211_if_combination_attrs. * * All together, these attributes define the concurrency of virtual * interfaces that a given device supports. */ /** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors * * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL * instead, the support here is for backward compatibility only. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. * * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; * either a dump request on a %NL80211_ATTR_WIPHY or a specific get * on an %NL80211_ATTR_IFINDEX is supported. * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also * be sent from userspace to request creation of a new virtual interface, * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and * %NL80211_ATTR_IFNAME. * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from * userspace to request deletion of a virtual interface, then requires * attribute %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, * and %NL80211_ATTR_KEY_SEQ attributes. * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX * or %NL80211_ATTR_MAC. * * @NL80211_CMD_GET_BEACON: (not used) * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL * attributes. For drivers that generate the beacon and probe responses * internally, the following attributes must be provided: %NL80211_ATTR_IE, * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. * The channel to use can be set on the interface or be given using the * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP * * @NL80211_CMD_GET_STATION: Get station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_STATION: Set station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the * the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC * or, if no MAC address given, all stations, on the interface identified * by %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by * %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by * %NL80211_ATTR_IFINDEX. * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by * %NL80211_ATTR_MAC. * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the * the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC * or, if no MAC address given, all mesh paths, on the interface identified * by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by * %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set * regulatory domain. * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command * after being queried by the kernel. CRDA replies by sending a regulatory * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our * current alpha2 if it found a match. It also provides * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each * regulatory rule is a nested set of attributes given by * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain * to the specified ISO/IEC 3166-1 alpha2 country code. The core will * store this as a valid request and then query userspace for it. * * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The * interface is identified with %NL80211_ATTR_IFINDEX and the management * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be * added to the end of the specified management frame is specified with * %NL80211_ATTR_IE. If the command succeeds, the requested data will be * added to all specified management frames generated by * kernel/firmware/driver. * Note: This command has been removed and it is only reserved at this * point to avoid re-using existing command number. The functionality this * command was planned for has been provided with cleaner design with the * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * probe requests at CCK rate or not. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * NL80211_CMD_GET_SCAN and on the "scan" multicast group) * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) * are passed, they are used in the probe requests. For * broadcast, a broadcast SSID must be passed (ie. an empty * string). If no SSID is passed, no probe requests are sent and * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, * if passed, define which channels should be scanned; if not * passed, all channels allowed for the current regulatory domain * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT * if scheduled scan is not running. * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan * results available. * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has * stopped. The driver may issue this event at any time during a * scheduled scan. One reason for stopping the scan is if the hardware * does not support starting an association or a normal scan while running * a scheduled scan. This event is also sent when the * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface * is brought down while a scheduled scan was running. * * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation * or noise level * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC * (for the BSSID) and %NL80211_ATTR_PMKID. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC * (for the BSSID) and %NL80211_ATTR_PMKID. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain * has been changed and provides details of the request information * that caused the change such as who initiated the regulatory request * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on * to (%NL80211_ATTR_REG_ALPHA2). * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon * has been found while world roaming thus enabling active scan or * any mode of operation that initiates TX (beacons) on a channel * where we would not have been able to do either before. As an example * if you are world roaming (regulatory domain set to world or if your * driver is using a custom world roaming regulatory domain) and while * doing a passive scan on the 5 GHz band you find an AP there (if not * on a DFS channel) you will now be able to actively scan for that AP * or use AP mode on your card on that same channel. Note that this will * never be used for channels 1-11 on the 2 GHz band as they are always * enabled world wide. This beacon hint is only sent if your device had * either disabled active scanning or beaconing on a channel. We send to * userspace the wiphy on which we removed a restriction from * (%NL80211_ATTR_WIPHY) and the channel on which this occurred * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) * the beacon hint was processed. * * @NL80211_CMD_AUTHENTICATE: authentication request and notification. * This command is used both as a command (request to authenticate) and * as an event on the "mlme" multicast group indicating completion of the * authentication process. * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify * the SSID (mainly for association, but is included in authentication * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE * is used to specify the authentication type. %NL80211_ATTR_IE is used to * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) * to be added to the frame. * When used as an event, this reports reception of an Authentication * frame in station and IBSS modes when the local MLME processed the * frame, i.e., it was for the local STA and was received in correct * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the * MLME SAP interface (kernel providing MLME, userspace SME). The * included %NL80211_ATTR_FRAME attribute contains the management frame * (including both the header and frame body, but not FCS). This event is * also used to indicate if the authentication attempt timed out. In that * case the %NL80211_ATTR_FRAME attribute is replaced with a * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which * pending authentication timed out). * @NL80211_CMD_ASSOCIATE: association request and notification; like * NL80211_CMD_AUTHENTICATE but for Association and Reassociation * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication * primitives). * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). * * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the * event includes %NL80211_ATTR_MAC to describe the source MAC address of * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this * event matches with MLME-MICHAELMICFAILURE.indication() primitive * * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a * FREQ attribute (for the initial frequency if no peer can be found) * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those * should be fixed rather than automatically determined. Can only be * executed on a network interface that is UP, and fixed BSSID/FREQ * may be rejected. Another optional parameter is the beacon interval, * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not * given defaults to 100 TU (102.4ms). * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is * determined by the network interface. * * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute * to identify the device, and the TESTDATA blob attribute to pass through * to the driver. * * @NL80211_CMD_CONNECT: connection request and notification; this command * requests to connect to a specified network but without separating * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. * Background scan period can optionally be * specified in %NL80211_ATTR_BG_SCAN_PERIOD, * if not specified default background scan configuration * in driver is used and if period value is 0, bg scan will be disabled. * This attribute is ignored if driver does not support roam scan. * It is also sent as an event, with the BSSID and response IEs when the * connection is established or failed to be established. This can be * determined by the STATUS_CODE attribute. * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), * sent as an event when the card/driver roamed by itself. * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify * userspace that a connection was dropped by the AP or due to other * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and * %NL80211_ATTR_REASON_CODE attributes are used. * * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices * associated with this wiphy must be down and will follow. * * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified * channel for the specified amount of time. This can be used to do * off-channel operations like transmit a Public Action frame and wait for * a response while being associated to an AP on another channel. * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be * optionally used to specify additional channel parameters. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the * driver to schedule this time due to other concurrent needs for the * radio). * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) * that will be included with any events pertaining to this request; * the cookie is also used to cancel the request. * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a * pending remain-on-channel duration if the desired operation has been * completed prior to expiration of the originally requested duration. * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to * uniquely identify the request. * This command is also used as an event to notify when a requested * remain-on-channel duration has expired. * * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface * and @NL80211_ATTR_TX_RATES the set of allowed rates. * * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames * (via @NL80211_CMD_FRAME) for processing in userspace. This command * requires an interface index, a frame type attribute (optional for * backward compatibility reasons, if not given assumes action frames) * and a match attribute containing the first few bytes of the frame * that should match, e.g. a single byte for only a category match or * four bytes for vendor frames including the OUI. The registration * cannot be dropped, but is removed automatically when the netlink * socket is closed. Multiple registrations can be made. * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for * backward compatibility * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This * command is used both as a request to transmit a management frame and * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on * which channel the frame is to be transmitted or was received. If this * channel is not the current channel (remain-on-channel or the * operational channel) the device will switch to the given channel and * transmit the frame, optionally waiting for a response for the time * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * management frames at CCK rate or not in 2GHz band. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies * the TX command and %NL80211_ATTR_FRAME includes the contents of the * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * the frame. * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for * backward compatibility. * * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE * * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command * is used to configure connection quality monitoring notification trigger * levels. * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This * command is used as an event to indicate the that a trigger level was * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed * by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while * no other interfaces are operating to avoid disturbing the operation * of any other interfaces, and other interfaces will again take * precedence when they are used. * * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. * * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial * mesh config parameters may be given. * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the * network is determined by the network interface. * * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame * notification. This event is used to indicate that an unprotected * deauthentication frame was dropped when MFP is in use. * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame * notification. This event is used to indicate that an unprotected * disassociation frame was dropped when MFP is in use. * * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a * beacon or probe response from a compatible mesh peer. This is only * sent while no station information (sta_info) exists for the new peer * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On * reception of this notification, userspace may decide to create a new * station (@NL80211_CMD_NEW_STATION). To stop this notification from * reoccurring, the userspace authentication daemon may want to create the * new station with the AUTHENTICATED flag unset and maybe change it later * depending on the authentication result. * * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. * Since wireless is more complex than wired ethernet, it supports * various triggers. These triggers can be configured through this * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For * more background information, see * http://wireless.kernel.org/en/users/Documentation/WoWLAN. * * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver * the necessary information for supporting GTK rekey offload. This * feature is typically used during WoWLAN. The configuration data * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and * contains the data in sub-attributes). After rekeying happened, * this command may also be sent by the driver as an MLME event to * inform userspace of the new replay counter. * * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace * of PMKSA caching dandidates. * * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. * * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP * (or GO) interface (i.e. hostapd) to ask for unexpected frames to * implement sending deauth to stations that send unexpected class 3 * frames. Also used as the event sent by the kernel when such a frame * is received. * For the event, the %NL80211_ATTR_MAC attribute carries the TA and * other attributes like the interface index are present. * If used as the command it must have an interface index and you can * only unsubscribe from the event by closing the socket. Subscription * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. * * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame * and wasn't already in a 4-addr VLAN. The event will be sent similarly * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. * * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface * by sending a null data frame to it and reporting when the frame is * acknowleged. This is used to allow timing out inactive clients. Uses * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a * direct reply with an %NL80211_ATTR_COOKIE that is later used to match * up the event with the request. The event includes the same data and * has %NL80211_ATTR_ACK set if the frame was ACKed. * * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from * other BSSes when any interfaces are in AP mode. This helps implement * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME * messages. Note that per PHY only one application may register. * * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether * No Acknowledgement Policy should be applied. * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. * * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. It must have been created with * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the * P2P Device can be used for P2P operations, e.g. remain-on-channel and * public action frame TX. * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ enum nl80211_commands { /* don't change the order or add anything between, this is ABI! */ NL80211_CMD_UNSPEC, NL80211_CMD_GET_WIPHY, /* can dump */ NL80211_CMD_SET_WIPHY, NL80211_CMD_NEW_WIPHY, NL80211_CMD_DEL_WIPHY, NL80211_CMD_GET_INTERFACE, /* can dump */ NL80211_CMD_SET_INTERFACE, NL80211_CMD_NEW_INTERFACE, NL80211_CMD_DEL_INTERFACE, NL80211_CMD_GET_KEY, NL80211_CMD_SET_KEY, NL80211_CMD_NEW_KEY, NL80211_CMD_DEL_KEY, NL80211_CMD_GET_BEACON, NL80211_CMD_SET_BEACON, NL80211_CMD_START_AP, NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, NL80211_CMD_STOP_AP, NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, NL80211_CMD_GET_STATION, NL80211_CMD_SET_STATION, NL80211_CMD_NEW_STATION, NL80211_CMD_DEL_STATION, NL80211_CMD_GET_MPATH, NL80211_CMD_SET_MPATH, NL80211_CMD_NEW_MPATH, NL80211_CMD_DEL_MPATH, NL80211_CMD_SET_BSS, NL80211_CMD_SET_REG, NL80211_CMD_REQ_SET_REG, NL80211_CMD_GET_MESH_CONFIG, NL80211_CMD_SET_MESH_CONFIG, NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, NL80211_CMD_GET_REG, NL80211_CMD_GET_SCAN, NL80211_CMD_TRIGGER_SCAN, NL80211_CMD_NEW_SCAN_RESULTS, NL80211_CMD_SCAN_ABORTED, NL80211_CMD_REG_CHANGE, NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, NL80211_CMD_DEAUTHENTICATE, NL80211_CMD_DISASSOCIATE, NL80211_CMD_MICHAEL_MIC_FAILURE, NL80211_CMD_REG_BEACON_HINT, NL80211_CMD_JOIN_IBSS, NL80211_CMD_LEAVE_IBSS, NL80211_CMD_TESTMODE, NL80211_CMD_CONNECT, NL80211_CMD_ROAM, NL80211_CMD_DISCONNECT, NL80211_CMD_SET_WIPHY_NETNS, NL80211_CMD_GET_SURVEY, NL80211_CMD_NEW_SURVEY_RESULTS, NL80211_CMD_SET_PMKSA, NL80211_CMD_DEL_PMKSA, NL80211_CMD_FLUSH_PMKSA, NL80211_CMD_REMAIN_ON_CHANNEL, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, NL80211_CMD_SET_TX_BITRATE_MASK, NL80211_CMD_REGISTER_FRAME, NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, NL80211_CMD_FRAME, NL80211_CMD_ACTION = NL80211_CMD_FRAME, NL80211_CMD_FRAME_TX_STATUS, NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE, NL80211_CMD_SET_CQM, NL80211_CMD_NOTIFY_CQM, NL80211_CMD_SET_CHANNEL, NL80211_CMD_SET_WDS_PEER, NL80211_CMD_FRAME_WAIT_CANCEL, NL80211_CMD_JOIN_MESH, NL80211_CMD_LEAVE_MESH, NL80211_CMD_UNPROT_DEAUTHENTICATE, NL80211_CMD_UNPROT_DISASSOCIATE, NL80211_CMD_NEW_PEER_CANDIDATE, NL80211_CMD_GET_WOWLAN, NL80211_CMD_SET_WOWLAN, NL80211_CMD_START_SCHED_SCAN, NL80211_CMD_STOP_SCHED_SCAN, NL80211_CMD_SCHED_SCAN_RESULTS, NL80211_CMD_SCHED_SCAN_STOPPED, NL80211_CMD_SET_REKEY_OFFLOAD, NL80211_CMD_PMKSA_CANDIDATE, NL80211_CMD_TDLS_OPER, NL80211_CMD_TDLS_MGMT, NL80211_CMD_UNEXPECTED_FRAME, NL80211_CMD_PROBE_CLIENT, NL80211_CMD_REGISTER_BEACONS, NL80211_CMD_UNEXPECTED_4ADDR_FRAME, NL80211_CMD_SET_NOACK_MAP, NL80211_CMD_CH_SWITCH_NOTIFY, NL80211_CMD_START_P2P_DEVICE, NL80211_CMD_STOP_P2P_DEVICE, /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ __NL80211_CMD_AFTER_LAST, NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 }; /* * Allow user space programs to use #ifdef on new commands by defining them * here */ #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE #define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE #define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE #define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE #define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS /* source-level API compatibility */ #define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG #define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG #define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE /** * enum nl80211_attrs - nl80211 netlink attributes * * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors * * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) * NL80211_CHAN_HT20 = HT20 only * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is * less than or equal to the RTS threshold; allowed range: 1..255; * dot11ShortRetryLimit; u8 * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is * greater than the RTS threshold; allowed range: 1..255; * dot11ShortLongLimit; u8 * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum * length in octets for frames; allowed range: 256..8000, disable * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length * larger than or equal to this use RTS/CTS handshake); allowed range: * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 * section 7.3.2.9; dot11CoverageClass; u8 * * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on * @NL80211_ATTR_IFNAME: network interface name * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype * * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices * that don't have a netdev (u64) * * @NL80211_ATTR_MAC: MAC address (various uses) * * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC * keys * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 * section 7.3.2.25.1, e.g. 0x000FAC04) * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and * CCMP keys, each six bytes in little endian * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the * default management key * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or * other commands, indicates which pairwise cipher suites are used * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or * other commands, indicates which group cipher suite is used * * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE * * @NL80211_ATTR_STA_AID: Association ID for the station (u16) * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by * IEEE 802.11 7.3.1.6 (u16). * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported * rates as defined by IEEE 802.11 7.3.2.2 but without the length * restriction (at most %NL80211_MAX_SUPP_RATES). * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station * to, or the AP interface the station was originally added to to. * @NL80211_ATTR_STA_INFO: information about a station, part of station info * given for %NL80211_CMD_GET_STATION, nested attribute containing * info as possible, see &enum nl80211_sta_info. * * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, * consisting of a nested array. * * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link. * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path * info given for %NL80211_CMD_GET_MPATH, nested attribute described at * &enum nl80211_mpath_info. * * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of * &enum nl80211_mntr_flags. * * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the * current regulatory domain should be set to or is already set to. * For example, 'CR', for Costa Rica. This attribute is used by the kernel * to query the CRDA to retrieve one regulatory domain. This attribute can * also be used by userspace to query the kernel for the currently set * regulatory domain. We chose an alpha2 as that is also used by the * IEEE-802.11d country information element to identify a country. * Users can also simply ask the wireless core to set regulatory domain * to a specific alpha2. * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory * rules. * * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled * (u8, 0 or 1) * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled * (u8, 0 or 1) * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic * rates in format defined by IEEE 802.11 7.3.2.2 but without the length * restriction (at most %NL80211_MAX_SUPP_RATES). * * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) * * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all * supported interface types, each a flag attribute with the number * of the interface mode. * * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for * %NL80211_CMD_SET_MGMT_EXTRA_IE. * * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with * %NL80211_CMD_SET_MGMT_EXTRA_IE). * * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with * a single scan request, a wiphy attribute. * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can * scan with a single scheduled scan request, a wiphy attribute. * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements * that can be added to a scan request * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information * elements that can be added to a scheduled scan request * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. * * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive * scanning and include a zero-length SSID (wildcard) for wildcard scan * @NL80211_ATTR_BSS: scan result BSS * * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) * * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies * an array of command numbers (i.e. a mapping index to command number) * that the driver for the given wiphy supports. * * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and * NL80211_CMD_ASSOCIATE events * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, * represented as a u32 * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and * %NL80211_CMD_DISASSOCIATE, u16 * * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as * a u32 * * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change * due to considerations from a beacon hint. This attribute reflects * the state of the channel _before_ the beacon hint processing. This * attributes consists of a nested attribute containing * NL80211_FREQUENCY_ATTR_* * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change * due to considerations from a beacon hint. This attribute reflects * the state of the channel _after_ the beacon hint processing. This * attributes consists of a nested attribute containing * NL80211_FREQUENCY_ATTR_* * * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported * cipher suites * * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look * for other networks on different channels * * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this * is used, e.g., with %NL80211_CMD_AUTHENTICATE event * * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); * this attribute can be used * with %NL80211_CMD_ASSOCIATE request * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. * * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE * request, the driver will assume that the port is unauthorized until * authorized by user space. Otherwise, port is marked authorized by * default in station mode. * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the * ethertype that will be used for key negotiation. It can be * specified with the associate and connect commands. If it is not * specified, the value defaults to 0x888E (PAE, 802.1X). This * attribute is also used as a flag in the wiphy information to * indicate that protocols other than PAE are supported. * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom * ethertype frames used for key negotiation must not be encrypted. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. * * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT * event was due to the AP disconnecting the station, and not due to * a local disconnect request. * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT * event (u16) * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating * that protected APs should be used. This is also used with NEW_BEACON to * indicate that the BSS is to use protection. * * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON * to indicate which unicast key ciphers will be used with the connection * (an array of u32). * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which group key cipher will be used with the connection (a * u32). * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which WPA version(s) the AP we want to associate with is using * (a u32 with flags from &enum nl80211_wpa_versions). * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which key management algorithm(s) to use (an array of u32). * * @NL80211_ATTR_REQ_IE: (Re)association request information elements as * sent out by the card, for ROAM and successful CONNECT events. * @NL80211_ATTR_RESP_IE: (Re)association response information elements as * sent by peer, for ROAM and successful CONNECT events. * * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE * commands to specify using a reassociate frame * * @NL80211_ATTR_KEY: key information in a nested attribute with * %NL80211_KEY_* sub-attributes * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() * and join_ibss(), key information is in a nested attribute each * with %NL80211_KEY_* sub-attributes * * @NL80211_ATTR_PID: Process ID of a network namespace. * * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for * dumps. This number increases whenever the object list being * dumped changes, and as such userspace can verify that it has * obtained a complete and consistent snapshot by verifying that * all dump messages contain the same generation number. If it * changed then the list changed and the dump should be repeated * completely from scratch. * * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface * * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute * containing info as possible, see &enum survey_info. * * @NL80211_ATTR_PMKID: PMK material for PMKSA caching. * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can * cache, a wiphy attribute. * * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that * specifies the maximum duration that can be requested with the * remain-on-channel operation, in milliseconds, u32. * * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. * * @NL80211_ATTR_TX_RATES: Nested set of attributes * (enum nl80211_tx_rate_attributes) describing TX rates per band. The * enum nl80211_band value is used as the index (nla_type() of the nested * data. If a band is not included, it will be configured to allow all * rates based on negotiated supported rates information. This attribute * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the * @NL80211_CMD_REGISTER_FRAME command. * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing * information about which frame types can be transmitted with * %NL80211_CMD_FRAME. * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing * information about which frame types can be registered for RX. * * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. * * @NL80211_ATTR_CQM: connection quality monitor configuration in a * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. * * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command * is requesting a local authentication/association state change without * invoking actual management frame exchange. This can be used with * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, * NL80211_CMD_DISASSOCIATE. * * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations * connected to this BSS. * * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See * &enum nl80211_tx_power_setting for possible values. * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING * for non-automatic settings. * * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly * means support for per-station GTKs. * * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. * This can be used to mask out antennas which are not attached or should * not be used for transmitting. If an antenna is not selected in this * bitmap the hardware is not allowed to transmit on this antenna. * * Each bit represents one antenna, starting with antenna 1 at the first * bit. Depending on which antennas are selected in the bitmap, 802.11n * drivers can derive which chainmasks to use (if all antennas belonging to * a particular chain are disabled this chain should be disabled) and if * a chain has diversity antennas wether diversity should be used or not. * HT capabilities (STBC, TX Beamforming, Antenna selection) can be * derived from the available chains after applying the antenna mask. * Non-802.11n drivers can derive wether to use diversity or not. * Drivers may reject configurations or RX/TX mask combinations they cannot * support by returning -EINVAL. * * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. * This can be used to mask out antennas which are not attached or should * not be used for receiving. If an antenna is not selected in this bitmap * the hardware should not be configured to receive on this antenna. * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. * * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available * for configuration as TX antennas via the above parameters. * * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available * for configuration as RX antennas via the above parameters. * * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS * * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be * transmitted on another channel when the channel given doesn't match * the current channel. If the current channel doesn't match and this * flag isn't set, the frame will be rejected. This is also used as an * nl80211 capability flag. * * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16) * * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags * attributes, specifying what a key should be set as default as. * See &enum nl80211_key_default_types. * * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be * changed once the mesh is active. * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute * containing attributes from &enum nl80211_meshconf_params. * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as * defined in &enum nl80211_plink_state. Used when userspace is * driving the peer link management state machine. * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. * * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy * capabilities, the supported WoWLAN triggers * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to * indicate which WoW triggers should be enabled. This is also * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN * triggers. * * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan * cycles, in msecs. * * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more * sets of attributes to match during scheduled scans. Only BSSs * that match any of the sets will be reported. These are * pass-thru filter rules. * For a match to succeed, the BSS must match all attributes of a * set. Since not every hardware supports matching all types of * attributes, there is no guarantee that the reported BSSs are * fully complying with the match sets and userspace needs to be * able to ignore them by itself. * Thus, the implementation is somewhat hardware-dependent, but * this is only an optimization and the userspace application * needs to handle all the non-filtered results anyway. * If the match attributes don't make sense when combined with * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID * is included in the probe request, but the match attributes * will never let it go through), -EINVAL may be returned. * If ommited, no filtering is done. * * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported * interface combinations. In each nested item, it contains attributes * defined in &enum nl80211_if_combination_attrs. * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that * are managed in software: interfaces of these types aren't subject to * any restrictions in their number or combinations. * * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. * * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, * nested array attribute containing an entry for each band, with the entry * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but * without the length restriction (at most %NL80211_MAX_SUPP_RATES). * * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon * and Probe Response (when response to wildcard Probe Request); see * &enum nl80211_hidden_ssid, represented as a u32 * * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the * driver (or firmware) replies to Probe Request frames. * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association * Response frames. This is used with %NL80211_CMD_NEW_BEACON and * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into * (Re)Association Response frames when the driver (or firmware) replies to * (Re)Association Request frames. * * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration * of the station, see &enum nl80211_sta_wme_attr. * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working * as AP. * * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of * roaming to another AP in the same ESS if the signal lever is low. * * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching * candidate information, see &enum nl80211_pmksa_candidate_attr. * * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not * for management frames transmission. In order to avoid p2p probe/action * frames are being transmitted at CCK rate in 2GHz band, the user space * applications use this attribute. * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and * %NL80211_CMD_FRAME commands. * * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup * request, link setup confirm, link teardown, etc.). Values are * described in the TDLS (802.11z) specification. * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a * TDLS conversation between two devices. * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see * &enum nl80211_tdls_operation, represented as a u8. * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate * as a TDLS peer sta. * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown * procedures should be performed by sending TDLS packets via * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be * used for asking the driver to perform a TDLS operation. * * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices * that have AP support to indicate that they have the AP SME integrated * with support for the features listed in this attribute, see * &enum nl80211_ap_sme_features. * * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells * the driver to not wait for an acknowledgement. Note that due to this, * it will also not give a status callback nor return a cookie. This is * mostly useful for probe responses to save airtime. * * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from * &enum nl80211_feature_flags and is advertised in wiphy information. * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe * requests while operating in AP-mode. * This attribute holds a bitmap of the supported protocols for * offloading (see &enum nl80211_probe_resp_offload_support_attr). * * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire * probe-response frame. The DA field in the 802.11 header is zero-ed out, * to be filled by the FW. * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable * this feature. Currently, only supported in mac80211 drivers. * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the * ATTR_HT_CAPABILITY to which attention should be paid. * Currently, only mac80211 NICs support this feature. * The values that may be configured are: * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40 * AMPDU density and AMPDU factor. * All values are treated as suggestions and may be ignored * by the driver as required. The actual values may be seen in * the station debugfs ht_caps file. * * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country * abides to when initiating radiation on DFS channels. A country maps * to one DFS region. * * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of * up to 16 TIDs. * * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be * used by the drivers which has MLME in firmware and does not have support * to report per station tx/rx activity to free up the staion entry from * the list. This needs to be used when the driver advertises the * capability to timeout the stations. * * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int); * this attribute is (depending on the driver capabilities) added to * received frames indicated with %NL80211_CMD_FRAME. * * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds * or 0 to disable background scan. * * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from * userspace. If unset it is assumed the hint comes directly from * a user. If set code could specify exactly what type of source * was used to provide the hint. For the different types of * allowed user regulatory hints see nl80211_user_reg_hint_type. * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ enum nl80211_attrs { /* don't change the order or add anything between, this is ABI! */ NL80211_ATTR_UNSPEC, NL80211_ATTR_WIPHY, NL80211_ATTR_WIPHY_NAME, NL80211_ATTR_IFINDEX, NL80211_ATTR_IFNAME, NL80211_ATTR_IFTYPE, NL80211_ATTR_MAC, NL80211_ATTR_KEY_DATA, NL80211_ATTR_KEY_IDX, NL80211_ATTR_KEY_CIPHER, NL80211_ATTR_KEY_SEQ, NL80211_ATTR_KEY_DEFAULT, NL80211_ATTR_BEACON_INTERVAL, NL80211_ATTR_DTIM_PERIOD, NL80211_ATTR_BEACON_HEAD, NL80211_ATTR_BEACON_TAIL, NL80211_ATTR_STA_AID, NL80211_ATTR_STA_FLAGS, NL80211_ATTR_STA_LISTEN_INTERVAL, NL80211_ATTR_STA_SUPPORTED_RATES, NL80211_ATTR_STA_VLAN, NL80211_ATTR_STA_INFO, NL80211_ATTR_WIPHY_BANDS, NL80211_ATTR_MNTR_FLAGS, NL80211_ATTR_MESH_ID, NL80211_ATTR_STA_PLINK_ACTION, NL80211_ATTR_MPATH_NEXT_HOP, NL80211_ATTR_MPATH_INFO, NL80211_ATTR_BSS_CTS_PROT, NL80211_ATTR_BSS_SHORT_PREAMBLE, NL80211_ATTR_BSS_SHORT_SLOT_TIME, NL80211_ATTR_HT_CAPABILITY, NL80211_ATTR_SUPPORTED_IFTYPES, NL80211_ATTR_REG_ALPHA2, NL80211_ATTR_REG_RULES, NL80211_ATTR_MESH_CONFIG, NL80211_ATTR_BSS_BASIC_RATES, NL80211_ATTR_WIPHY_TXQ_PARAMS, NL80211_ATTR_WIPHY_FREQ, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_ATTR_KEY_DEFAULT_MGMT, NL80211_ATTR_MGMT_SUBTYPE, NL80211_ATTR_IE, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, NL80211_ATTR_SCAN_FREQUENCIES, NL80211_ATTR_SCAN_SSIDS, NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ NL80211_ATTR_BSS, NL80211_ATTR_REG_INITIATOR, NL80211_ATTR_REG_TYPE, NL80211_ATTR_SUPPORTED_COMMANDS, NL80211_ATTR_FRAME, NL80211_ATTR_SSID, NL80211_ATTR_AUTH_TYPE, NL80211_ATTR_REASON_CODE, NL80211_ATTR_KEY_TYPE, NL80211_ATTR_MAX_SCAN_IE_LEN, NL80211_ATTR_CIPHER_SUITES, NL80211_ATTR_FREQ_BEFORE, NL80211_ATTR_FREQ_AFTER, NL80211_ATTR_FREQ_FIXED, NL80211_ATTR_WIPHY_RETRY_SHORT, NL80211_ATTR_WIPHY_RETRY_LONG, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, NL80211_ATTR_WIPHY_RTS_THRESHOLD, NL80211_ATTR_TIMED_OUT, NL80211_ATTR_USE_MFP, NL80211_ATTR_STA_FLAGS2, NL80211_ATTR_CONTROL_PORT, NL80211_ATTR_TESTDATA, NL80211_ATTR_PRIVACY, NL80211_ATTR_DISCONNECTED_BY_AP, NL80211_ATTR_STATUS_CODE, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, NL80211_ATTR_CIPHER_SUITE_GROUP, NL80211_ATTR_WPA_VERSIONS, NL80211_ATTR_AKM_SUITES, NL80211_ATTR_REQ_IE, NL80211_ATTR_RESP_IE, NL80211_ATTR_PREV_BSSID, NL80211_ATTR_KEY, NL80211_ATTR_KEYS, NL80211_ATTR_PID, NL80211_ATTR_4ADDR, NL80211_ATTR_SURVEY_INFO, NL80211_ATTR_PMKID, NL80211_ATTR_MAX_NUM_PMKIDS, NL80211_ATTR_DURATION, NL80211_ATTR_COOKIE, NL80211_ATTR_WIPHY_COVERAGE_CLASS, NL80211_ATTR_TX_RATES, NL80211_ATTR_FRAME_MATCH, NL80211_ATTR_ACK, NL80211_ATTR_PS_STATE, NL80211_ATTR_CQM, NL80211_ATTR_LOCAL_STATE_CHANGE, NL80211_ATTR_AP_ISOLATE, NL80211_ATTR_WIPHY_TX_POWER_SETTING, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, NL80211_ATTR_TX_FRAME_TYPES, NL80211_ATTR_RX_FRAME_TYPES, NL80211_ATTR_FRAME_TYPE, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, NL80211_ATTR_SUPPORT_IBSS_RSN, NL80211_ATTR_WIPHY_ANTENNA_TX, NL80211_ATTR_WIPHY_ANTENNA_RX, NL80211_ATTR_MCAST_RATE, NL80211_ATTR_OFFCHANNEL_TX_OK, NL80211_ATTR_BSS_HT_OPMODE, NL80211_ATTR_KEY_DEFAULT_TYPES, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, NL80211_ATTR_MESH_SETUP, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, NL80211_ATTR_SUPPORT_MESH_AUTH, NL80211_ATTR_STA_PLINK_STATE, NL80211_ATTR_WOWLAN_TRIGGERS, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, NL80211_ATTR_SCHED_SCAN_INTERVAL, NL80211_ATTR_INTERFACE_COMBINATIONS, NL80211_ATTR_SOFTWARE_IFTYPES, NL80211_ATTR_REKEY_DATA, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, NL80211_ATTR_SCAN_SUPP_RATES, NL80211_ATTR_HIDDEN_SSID, NL80211_ATTR_IE_PROBE_RESP, NL80211_ATTR_IE_ASSOC_RESP, NL80211_ATTR_STA_WME, NL80211_ATTR_SUPPORT_AP_UAPSD, NL80211_ATTR_ROAM_SUPPORT, NL80211_ATTR_SCHED_SCAN_MATCH, NL80211_ATTR_MAX_MATCH_SETS, NL80211_ATTR_PMKSA_CANDIDATE, NL80211_ATTR_TX_NO_CCK_RATE, NL80211_ATTR_TDLS_ACTION, NL80211_ATTR_TDLS_DIALOG_TOKEN, NL80211_ATTR_TDLS_OPERATION, NL80211_ATTR_TDLS_SUPPORT, NL80211_ATTR_TDLS_EXTERNAL_SETUP, NL80211_ATTR_DEVICE_AP_SME, NL80211_ATTR_DONT_WAIT_FOR_ACK, NL80211_ATTR_FEATURE_FLAGS, NL80211_ATTR_PROBE_RESP_OFFLOAD, NL80211_ATTR_PROBE_RESP, NL80211_ATTR_DFS_REGION, NL80211_ATTR_DISABLE_HT, NL80211_ATTR_HT_CAPABILITY_MASK, NL80211_ATTR_NOACK_MAP, NL80211_ATTR_INACTIVITY_TIMEOUT, NL80211_ATTR_RX_SIGNAL_DBM, NL80211_ATTR_BG_SCAN_PERIOD, NL80211_ATTR_WDEV, NL80211_ATTR_USER_REG_HINT_TYPE, /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; /* source-level API compatibility */ #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION #define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG /* * Allow user space programs to use #ifdef on new attributes by defining them * here */ #define NL80211_CMD_CONNECT NL80211_CMD_CONNECT #define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY #define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE #define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE #define NL80211_ATTR_IE NL80211_ATTR_IE #define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR #define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE #define NL80211_ATTR_FRAME NL80211_ATTR_FRAME #define NL80211_ATTR_SSID NL80211_ATTR_SSID #define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE #define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE #define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE #define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP #define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS #define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_ATTR_KEY NL80211_ATTR_KEY #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 #define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 /* default RSSI threshold for scan results if none specified. */ #define NL80211_SCAN_RSSI_THOLD_OFF -300 #define NL80211_CQM_TXE_MAX_INTVL 1800 /** * enum nl80211_iftype - (virtual) interface types * * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides * @NL80211_IFTYPE_ADHOC: independent BSS member * @NL80211_IFTYPE_STATION: managed BSS member * @NL80211_IFTYPE_AP: access point * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces * are a bit special in that they must always be tied to a pre-existing * AP type interface. * @NL80211_IFTYPE_WDS: wireless distribution interface * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames * @NL80211_IFTYPE_MESH_POINT: mesh point * @NL80211_IFTYPE_P2P_CLIENT: P2P client * @NL80211_IFTYPE_P2P_GO: P2P group owner * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev * and therefore can't be created in the normal ways, use the * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE * commands to create and destroy one * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * * These values are used with the %NL80211_ATTR_IFTYPE * to set the type of an interface. * */ enum nl80211_iftype { NL80211_IFTYPE_UNSPECIFIED, NL80211_IFTYPE_ADHOC, NL80211_IFTYPE_STATION, NL80211_IFTYPE_AP, NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_WDS, NL80211_IFTYPE_MONITOR, NL80211_IFTYPE_MESH_POINT, NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, NL80211_IFTYPE_P2P_DEVICE, /* keep last */ NUM_NL80211_IFTYPES, NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 }; /** * enum nl80211_sta_flags - station flags * * Station flags. When a station is added to an AP interface, it is * assumed to be already associated (and hence authenticated.) * * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short barker preamble * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should * only be used in managed mode (even in the flags mask). Note that the * flag can't be changed, it is only valid while adding a station, and * attempts to change it will silently be ignored (rather than rejected * as errors.) * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ enum nl80211_sta_flags { __NL80211_STA_FLAG_INVALID, NL80211_STA_FLAG_AUTHORIZED, NL80211_STA_FLAG_SHORT_PREAMBLE, NL80211_STA_FLAG_WME, NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 }; #define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER /** * struct nl80211_sta_flag_update - station flags mask/set * @mask: mask of station flags to set * @set: which values to set them to * * Both mask and set contain bits as per &enum nl80211_sta_flags. */ struct nl80211_sta_flag_update { __u32 mask; __u32 set; } __attribute__((packed)); /** * enum nl80211_rate_info - bitrate information * * These attribute types are used with %NL80211_STA_INFO_TXRATE * when getting information about the bitrate of a station. * There are 2 attributes for bitrate, a legacy one that represents * a 16-bit value, and new one that represents a 32-bit value. * If the rate value fits into 16 bit, both attributes are reported * with the same value. If the rate is too high to fit into 16 bits * (>6.5535Gbps) only 32-bit attribute is included. * User space tools encouraged to use the 32-bit attribute and fall * back to the 16-bit one for compatibility with older kernels. * * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { __NL80211_RATE_INFO_INVALID, NL80211_RATE_INFO_BITRATE, NL80211_RATE_INFO_MCS, NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, NL80211_RATE_INFO_BITRATE32, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 }; /** * enum nl80211_sta_bss_param - BSS information collected by STA * * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM * when getting information about the bitrate of a station. * * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled * (flag) * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled * (flag) * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use */ enum nl80211_sta_bss_param { __NL80211_STA_BSS_PARAM_INVALID, NL80211_STA_BSS_PARAM_CTS_PROT, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, NL80211_STA_BSS_PARAM_DTIM_PERIOD, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, /* keep last */ __NL80211_STA_BSS_PARAM_AFTER_LAST, NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 }; /** * enum nl80211_sta_info - station information * * These attribute types are used with %NL80211_ATTR_STA_INFO * when getting information about a station. * * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_rate_info * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this * station) * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station * (see %enum nl80211_plink_state) * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested * attribute, like NL80211_STA_INFO_TX_BITRATE. * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute * containing info as possible, see &enum nl80211_sta_bss_param * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ enum nl80211_sta_info { __NL80211_STA_INFO_INVALID, NL80211_STA_INFO_INACTIVE_TIME, NL80211_STA_INFO_RX_BYTES, NL80211_STA_INFO_TX_BYTES, NL80211_STA_INFO_LLID, NL80211_STA_INFO_PLID, NL80211_STA_INFO_PLINK_STATE, NL80211_STA_INFO_SIGNAL, NL80211_STA_INFO_TX_BITRATE, NL80211_STA_INFO_RX_PACKETS, NL80211_STA_INFO_TX_PACKETS, NL80211_STA_INFO_TX_RETRIES, NL80211_STA_INFO_TX_FAILED, NL80211_STA_INFO_SIGNAL_AVG, NL80211_STA_INFO_RX_BITRATE, NL80211_STA_INFO_BSS_PARAM, NL80211_STA_INFO_CONNECTED_TIME, NL80211_STA_INFO_STA_FLAGS, NL80211_STA_INFO_BEACON_LOSS, NL80211_STA_INFO_T_OFFSET, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 }; /** * enum nl80211_mpath_flags - nl80211 mesh path flags * * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded */ enum nl80211_mpath_flags { NL80211_MPATH_FLAG_ACTIVE = 1<<0, NL80211_MPATH_FLAG_RESOLVING = 1<<1, NL80211_MPATH_FLAG_SN_VALID = 1<<2, NL80211_MPATH_FLAG_FIXED = 1<<3, NL80211_MPATH_FLAG_RESOLVED = 1<<4, }; /** * enum nl80211_mpath_info - mesh path information * * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting * information about a mesh path. * * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination * @NL80211_MPATH_INFO_SN: destination sequence number * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in * &enum nl80211_mpath_flags; * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number * currently defind * @__NL80211_MPATH_INFO_AFTER_LAST: internal use */ enum nl80211_mpath_info { __NL80211_MPATH_INFO_INVALID, NL80211_MPATH_INFO_FRAME_QLEN, NL80211_MPATH_INFO_SN, NL80211_MPATH_INFO_METRIC, NL80211_MPATH_INFO_EXPTIME, NL80211_MPATH_INFO_FLAGS, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, NL80211_MPATH_INFO_DISCOVERY_RETRIES, /* keep last */ __NL80211_MPATH_INFO_AFTER_LAST, NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 }; /** * enum nl80211_band_attr - band attributes * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, * an array of nested frequency attributes * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, * an array of nested bitrate attributes * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as * defined in 802.11n * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as * defined in 802.11ac * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ enum nl80211_band_attr { __NL80211_BAND_ATTR_INVALID, NL80211_BAND_ATTR_FREQS, NL80211_BAND_ATTR_RATES, NL80211_BAND_ATTR_HT_MCS_SET, NL80211_BAND_ATTR_HT_CAPA, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, NL80211_BAND_ATTR_VHT_MCS_SET, NL80211_BAND_ATTR_VHT_CAPA, /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 }; #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA /** * enum nl80211_frequency_attr - frequency attributes * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current * regulatory domain. * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is * permitted on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, NL80211_FREQUENCY_ATTR_FREQ, NL80211_FREQUENCY_ATTR_DISABLED, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, NL80211_FREQUENCY_ATTR_NO_IBSS, NL80211_FREQUENCY_ATTR_RADAR, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 }; #define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER /** * enum nl80211_bitrate_attr - bitrate attributes * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported * in 2.4 GHz band. * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number * currently defined * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use */ enum nl80211_bitrate_attr { __NL80211_BITRATE_ATTR_INVALID, NL80211_BITRATE_ATTR_RATE, NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, /* keep last */ __NL80211_BITRATE_ATTR_AFTER_LAST, NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_initiator - Indicates the initiator of a reg domain request * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world * regulatory domain. * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the * regulatory domain. * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the * wireless core it thinks its knows the regulatory domain we should be in. * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an * 802.11 country information element with regulatory information it * thinks we should consider. cfg80211 only processes the country * code from the IE, and relies on the regulatory domain information * structure passed by userspace (CRDA) from our wireless-regdb. * If a channel is enabled but the country code indicates it should * be disabled we disable the channel and re-enable it upon disassociation. */ enum nl80211_reg_initiator { NL80211_REGDOM_SET_BY_CORE, NL80211_REGDOM_SET_BY_USER, NL80211_REGDOM_SET_BY_DRIVER, NL80211_REGDOM_SET_BY_COUNTRY_IE, }; /** * enum nl80211_reg_type - specifies the type of regulatory domain * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains * to a specific country. When this is set you can count on the * ISO / IEC 3166 alpha2 country code being valid. * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory * domain. * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom * driver specific world regulatory domain. These do not apply system-wide * and are only applicable to the individual devices which have requested * them to be applied. * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product * of an intersection between two regulatory domains -- the previously * set regulatory domain on the system and the last accepted regulatory * domain request to be processed. */ enum nl80211_reg_type { NL80211_REGDOM_TYPE_COUNTRY, NL80211_REGDOM_TYPE_WORLD, NL80211_REGDOM_TYPE_CUSTOM_WORLD, NL80211_REGDOM_TYPE_INTERSECTION, }; /** * enum nl80211_reg_rule_attr - regulatory rule attributes * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional * considerations for a given frequency range. These are the * &enum nl80211_reg_rule_flags. * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory * rule in KHz. This is not a center of frequency but an actual regulatory * band edge. * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule * in KHz. This is not a center a frequency but an actual regulatory * band edge. * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this * frequency range, in KHz. * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain * for a given frequency range. The value is in mBi (100 * dBi). * If you don't have one then don't send this. * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for * a given frequency range. The value is in mBm (100 * dBm). * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number * currently defined * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use */ enum nl80211_reg_rule_attr { __NL80211_REG_RULE_ATTR_INVALID, NL80211_ATTR_REG_RULE_FLAGS, NL80211_ATTR_FREQ_RANGE_START, NL80211_ATTR_FREQ_RANGE_END, NL80211_ATTR_FREQ_RANGE_MAX_BW, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, NL80211_ATTR_POWER_RULE_MAX_EIRP, /* keep last */ __NL80211_REG_RULE_ATTR_AFTER_LAST, NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_sched_scan_match_attr - scheduled scan match attributes * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a * BSS in scan results. Filtering is turned off if not specified. * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use */ enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, NL80211_SCHED_SCAN_MATCH_ATTR_SSID, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, NL80211_SCHED_SCAN_MATCH_ATTR_MAX = __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 }; /* only for backward compatibility */ #define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID /** * enum nl80211_reg_rule_flags - regulatory rule flags * * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed * @NL80211_RRF_NO_CCK: CCK modulation not allowed * @NL80211_RRF_NO_INDOOR: indoor operation not allowed * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed * @NL80211_RRF_DFS: DFS support is required to be used * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links * @NL80211_RRF_PASSIVE_SCAN: passive scan is required * @NL80211_RRF_NO_IBSS: no IBSS is allowed */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, NL80211_RRF_NO_CCK = 1<<1, NL80211_RRF_NO_INDOOR = 1<<2, NL80211_RRF_NO_OUTDOOR = 1<<3, NL80211_RRF_DFS = 1<<4, NL80211_RRF_PTP_ONLY = 1<<5, NL80211_RRF_PTMP_ONLY = 1<<6, NL80211_RRF_PASSIVE_SCAN = 1<<7, NL80211_RRF_NO_IBSS = 1<<8, }; /** * enum nl80211_dfs_regions - regulatory DFS regions * * @NL80211_DFS_UNSET: Country has no DFS master region specified * @NL80211_DFS_FCC: Country follows DFS master rules from FCC * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec */ enum nl80211_dfs_regions { NL80211_DFS_UNSET = 0, NL80211_DFS_FCC = 1, NL80211_DFS_ETSI = 2, NL80211_DFS_JP = 3, }; /** * enum nl80211_user_reg_hint_type - type of user regulatory hint * * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always * assumed if the attribute is not set. * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular * base station. Device drivers that have been tested to work * properly to support this type of hint can enable these hints * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature * capability on the struct wiphy. The wireless core will * ignore all cell base station hints until at least one device * present has been registered with the wireless core that * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a * supported feature. */ enum nl80211_user_reg_hint_type { NL80211_USER_REG_HINT_USER = 0, NL80211_USER_REG_HINT_CELL_BASE = 1, }; /** * enum nl80211_survey_info - survey information * * These attribute types are used with %NL80211_ATTR_SURVEY_INFO * when getting information about a survey. * * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio * spent on this channel * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary * channel was sensed busy (either due to activity or energy detect) * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension * channel was sensed busy * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent * receiving data * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent * transmitting data * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use */ enum nl80211_survey_info { __NL80211_SURVEY_INFO_INVALID, NL80211_SURVEY_INFO_FREQUENCY, NL80211_SURVEY_INFO_NOISE, NL80211_SURVEY_INFO_IN_USE, NL80211_SURVEY_INFO_CHANNEL_TIME, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 }; /** * enum nl80211_mntr_flags - monitor configuration flags * * Monitor configuration flags. * * @__NL80211_MNTR_FLAG_INVALID: reserved * * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP * @NL80211_MNTR_FLAG_CONTROL: pass control frames * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. * overrides all other flags. * * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag */ enum nl80211_mntr_flags { __NL80211_MNTR_FLAG_INVALID, NL80211_MNTR_FLAG_FCSFAIL, NL80211_MNTR_FLAG_PLCPFAIL, NL80211_MNTR_FLAG_CONTROL, NL80211_MNTR_FLAG_OTHER_BSS, NL80211_MNTR_FLAG_COOK_FRAMES, /* keep last */ __NL80211_MNTR_FLAG_AFTER_LAST, NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 }; /** * enum nl80211_meshconf_params - mesh configuration parameters * * Mesh configuration parameters. These can be changed while the mesh is * active. * * @__NL80211_MESHCONF_INVALID: internal use * * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in * millisecond units, used by the Peer Link Open message * * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in * millisecond units, used by the peer link management to close a peer link * * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in * millisecond units * * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed * on this mesh interface * * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link * open retries that can be sent to establish a new peer link instance in a * mesh * * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh * point. * * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically * open peer links when we detect compatible mesh peers. * * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames * containing a PREQ that an MP can send to a particular destination (path * target) * * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths * (in milliseconds) * * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait * until giving up on a path discovery (in milliseconds) * * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh * points receiving a PREQ shall consider the forwarding information from * the root to be valid. (TU = time unit) * * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in * TUs) during which an MP can send only one action frame containing a PREQ * reference element * * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) * that it takes for an HWMP information element to propagate across the * mesh * * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not * * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a * source mesh point for path selection elements. * * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between * root announcements are transmitted. * * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has * access to a broader network beyond the MBSS. This is done via Root * Announcement frames. * * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in * TUs) during which a mesh STA can send only one Action frame containing a * PERR element. * * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding * or forwarding entity (default is TRUE - forwarding entity) * * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the * threshold for average signal strength of candidate station to establish * a peer link. * * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors * to synchronize to for 11s default synchronization method * (see 11C.12.2.2) * * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for * which mesh STAs receiving a proactive PREQ shall consider the forwarding * information to the root mesh STA to be valid. * * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between * proactive PREQs are transmitted. * * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time * (in TUs) during which a mesh STA can send only one Action frame * containing a PREQ element for root path confirmation. * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { __NL80211_MESHCONF_INVALID, NL80211_MESHCONF_RETRY_TIMEOUT, NL80211_MESHCONF_CONFIRM_TIMEOUT, NL80211_MESHCONF_HOLDING_TIMEOUT, NL80211_MESHCONF_MAX_PEER_LINKS, NL80211_MESHCONF_MAX_RETRIES, NL80211_MESHCONF_TTL, NL80211_MESHCONF_AUTO_OPEN_PLINKS, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, NL80211_MESHCONF_PATH_REFRESH_TIME, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, NL80211_MESHCONF_HWMP_ROOTMODE, NL80211_MESHCONF_ELEMENT_TTL, NL80211_MESHCONF_HWMP_RANN_INTERVAL, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, NL80211_MESHCONF_FORWARDING, NL80211_MESHCONF_RSSI_THRESHOLD, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, NL80211_MESHCONF_HT_OPMODE, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_mesh_setup_params - mesh setup parameters * * Mesh setup parameters. These are used to start/join a mesh and cannot be * changed while the mesh is active. * * @__NL80211_MESH_SETUP_INVALID: Internal use * * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a * vendor specific path selection algorithm or disable it to use the * default HWMP. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a * vendor specific path metric or disable it to use the default Airtime * metric. * * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a * robust security network ie, or a vendor specific information element * that vendors will use to identify the path selection methods and * metrics in use. * * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication * daemon will be authenticating mesh candidates. * * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication * daemon will be securing peer link frames. AMPE is a secured version of * Mesh Peering Management (MPM) and is implemented with the assistance of * a userspace daemon. When this flag is set, the kernel will send peer * management frames to a userspace daemon that will implement AMPE * functionality (security capabilities selection, key confirmation, and * key management). When the flag is unset (default), the kernel can * autonomously complete (unsecured) mesh peering without the need of a * userspace daemon. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a * vendor specific synchronization method or disable it to use the default * neighbor offset synchronization * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ enum nl80211_mesh_setup_params { __NL80211_MESH_SETUP_INVALID, NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, NL80211_MESH_SETUP_IE, NL80211_MESH_SETUP_USERSPACE_AUTH, NL80211_MESH_SETUP_USERSPACE_AMPE, NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 }; /** * enum nl80211_txq_attr - TX queue parameter attributes * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*) * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning * disabled * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form * 2^n-1 in the range 1..32767] * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form * 2^n-1 in the range 1..32767] * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number */ enum nl80211_txq_attr { __NL80211_TXQ_ATTR_INVALID, NL80211_TXQ_ATTR_AC, NL80211_TXQ_ATTR_TXOP, NL80211_TXQ_ATTR_CWMIN, NL80211_TXQ_ATTR_CWMAX, NL80211_TXQ_ATTR_AIFS, /* keep last */ __NL80211_TXQ_ATTR_AFTER_LAST, NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 }; enum nl80211_ac { NL80211_AC_VO, NL80211_AC_VI, NL80211_AC_BE, NL80211_AC_BK, NL80211_NUM_ACS }; /* backward compat */ #define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC #define NL80211_TXQ_Q_VO NL80211_AC_VO #define NL80211_TXQ_Q_VI NL80211_AC_VI #define NL80211_TXQ_Q_BE NL80211_AC_BE #define NL80211_TXQ_Q_BK NL80211_AC_BK enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, NL80211_CHAN_HT40MINUS, NL80211_CHAN_HT40PLUS }; /** * enum nl80211_bss - netlink attributes for a BSS * * @__NL80211_BSS_INVALID: invalid * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the * raw information elements from the probe response/beacon (bin); * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are * from a Probe Response frame; otherwise they are from a Beacon frame. * However, if the driver does not indicate the source of the IEs, these * IEs may be from either frame subtype. * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon * in mBm (100 * dBm) (s32) * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon * in unspecified units, scaled to 0..100 (u8) * @NL80211_BSS_STATUS: status, if this BSS is "used" * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information * elements from a Beacon frame (bin); not present if no Beacon frame has * yet been received * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ enum nl80211_bss { __NL80211_BSS_INVALID, NL80211_BSS_BSSID, NL80211_BSS_FREQUENCY, NL80211_BSS_TSF, NL80211_BSS_BEACON_INTERVAL, NL80211_BSS_CAPABILITY, NL80211_BSS_INFORMATION_ELEMENTS, NL80211_BSS_SIGNAL_MBM, NL80211_BSS_SIGNAL_UNSPEC, NL80211_BSS_STATUS, NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_BEACON_IES, /* keep last */ __NL80211_BSS_AFTER_LAST, NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 }; /** * enum nl80211_bss_status - BSS "status" * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. * * The BSS status is a BSS attribute in scan dumps, which * indicates the status the interface has wrt. this BSS. */ enum nl80211_bss_status { NL80211_BSS_STATUS_AUTHENTICATED, NL80211_BSS_STATUS_ASSOCIATED, NL80211_BSS_STATUS_IBSS_JOINED, }; /** * enum nl80211_auth_type - AuthenticationType * * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by * trying multiple times); this is invalid in netlink -- leave out * the attribute for this on CONNECT commands. */ enum nl80211_auth_type { NL80211_AUTHTYPE_OPEN_SYSTEM, NL80211_AUTHTYPE_SHARED_KEY, NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, /* keep last */ __NL80211_AUTHTYPE_NUM, NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, NL80211_AUTHTYPE_AUTOMATIC }; /** * enum nl80211_key_type - Key Type * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) * @NUM_NL80211_KEYTYPES: number of defined key types */ enum nl80211_key_type { NL80211_KEYTYPE_GROUP, NL80211_KEYTYPE_PAIRWISE, NL80211_KEYTYPE_PEERKEY, NUM_NL80211_KEYTYPES }; /** * enum nl80211_mfp - Management frame protection state * @NL80211_MFP_NO: Management frame protection not used * @NL80211_MFP_REQUIRED: Management frame protection required */ enum nl80211_mfp { NL80211_MFP_NO, NL80211_MFP_REQUIRED, }; enum nl80211_wpa_versions { NL80211_WPA_VERSION_1 = 1 << 0, NL80211_WPA_VERSION_2 = 1 << 1, }; /** * enum nl80211_key_default_types - key default types * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default * unicast key * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default * multicast key * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types */ enum nl80211_key_default_types { __NL80211_KEY_DEFAULT_TYPE_INVALID, NL80211_KEY_DEFAULT_TYPE_UNICAST, NL80211_KEY_DEFAULT_TYPE_MULTICAST, NUM_NL80211_KEY_DEFAULT_TYPES }; /** * enum nl80211_key_attributes - key attributes * @__NL80211_KEY_INVALID: invalid * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC * keys * @NL80211_KEY_IDX: key ID (u8, 0-3) * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 * section 7.3.2.25.1, e.g. 0x000FAC04) * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and * CCMP keys, each six bytes in little endian * @NL80211_KEY_DEFAULT: flag indicating default key * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not * specified the default depends on whether a MAC address was * given with the command using the key or not (u32) * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags * attributes, specifying what a key should be set as default as. * See &enum nl80211_key_default_types. * @__NL80211_KEY_AFTER_LAST: internal * @NL80211_KEY_MAX: highest key attribute */ enum nl80211_key_attributes { __NL80211_KEY_INVALID, NL80211_KEY_DATA, NL80211_KEY_IDX, NL80211_KEY_CIPHER, NL80211_KEY_SEQ, NL80211_KEY_DEFAULT, NL80211_KEY_DEFAULT_MGMT, NL80211_KEY_TYPE, NL80211_KEY_DEFAULT_TYPES, /* keep last */ __NL80211_KEY_AFTER_LAST, NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 }; /** * enum nl80211_tx_rate_attributes - TX rate set attributes * @__NL80211_TXRATE_INVALID: invalid * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection * in an array of MCS numbers. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, NL80211_TXRATE_MCS, /* keep last */ __NL80211_TXRATE_AFTER_LAST, NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 }; /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, NL80211_BAND_60GHZ, }; /** * enum nl80211_ps_state - powersave state * @NL80211_PS_DISABLED: powersave is disabled * @NL80211_PS_ENABLED: powersave is enabled */ enum nl80211_ps_state { NL80211_PS_DISABLED, NL80211_PS_ENABLED, }; /** * enum nl80211_attr_cqm - connection quality monitor attributes * @__NL80211_ATTR_CQM_INVALID: invalid * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies * the threshold for the RSSI level at which an event will be sent. Zero * to disable. * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many * consecutive packets were not acknowledged by the peer * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures * during the given %NL80211_ATTR_CQM_TXE_INTVL before an * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and * %NL80211_ATTR_CQM_TXE_PKTS is generated. * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is * checked. * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic * interval in which %NL80211_ATTR_CQM_TXE_PKTS and * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ enum nl80211_attr_cqm { __NL80211_ATTR_CQM_INVALID, NL80211_ATTR_CQM_RSSI_THOLD, NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, NL80211_ATTR_CQM_PKT_LOSS_EVENT, NL80211_ATTR_CQM_TXE_RATE, NL80211_ATTR_CQM_TXE_PKTS, NL80211_ATTR_CQM_TXE_INTVL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 }; /** * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the * configured threshold * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. * (Note that deauth/disassoc will still follow if the AP is not * available. This event might get used as roaming event, etc.) */ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, NL80211_CQM_RSSI_BEACON_LOSS_EVENT, }; /** * enum nl80211_tx_power_setting - TX power adjustment * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter */ enum nl80211_tx_power_setting { NL80211_TX_POWER_AUTOMATIC, NL80211_TX_POWER_LIMITED, NL80211_TX_POWER_FIXED, }; /** * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has * a zero bit are ignored * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have * a bit for each byte in the pattern. The lowest-order bit corresponds * to the first byte of the pattern, but the bytes of the pattern are * in a little-endian-like format, i.e. the 9th byte of the pattern * corresponds to the lowest-order bit in the second byte of the mask. * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where * xx indicates "don't care") would be represented by a pattern of * twelve zero bytes, and a mask of "0xed,0x07". * Note that the pattern matching is done as though frames were not * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked * first (including SNAP header unpacking) and then matched. * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number */ enum nl80211_wowlan_packet_pattern_attr { __NL80211_WOWLAN_PKTPAT_INVALID, NL80211_WOWLAN_PKTPAT_MASK, NL80211_WOWLAN_PKTPAT_PATTERN, NUM_NL80211_WOWLAN_PKTPAT, MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, }; /** * struct nl80211_wowlan_pattern_support - pattern support information * @max_patterns: maximum number of patterns supported * @min_pattern_len: minimum length of each pattern * @max_pattern_len: maximum length of each pattern * * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the * capability information given by the kernel to userspace. */ struct nl80211_wowlan_pattern_support { __u32 max_patterns; __u32 min_pattern_len; __u32 max_pattern_len; } __attribute__((packed)); /** * enum nl80211_wowlan_triggers - WoWLAN trigger definitions * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put * the chip into a special state -- works best with chips that have * support for low-power operation already (flag) * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect * is detected is implementation-specific (flag) * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed * by 16 repetitions of MAC addr, anywhere in payload) (flag) * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns * which are passed in an array of nested attributes, each nested attribute * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. * Each pattern defines a wakeup packet. The matching is done on the MSDU, * i.e. as though the packet was an 802.3 packet, so the pattern matching * is done after the packet is converted to the MSDU. * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute * carrying a &struct nl80211_wowlan_pattern_support. * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be * used when setting, used only to indicate that GTK rekeying is supported * by the device (flag) * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if * done by the device) (flag) * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request * packet (flag) * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released * (on devices that have rfkill in the device) (flag) * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number */ enum nl80211_wowlan_triggers { __NL80211_WOWLAN_TRIG_INVALID, NL80211_WOWLAN_TRIG_ANY, NL80211_WOWLAN_TRIG_DISCONNECT, NL80211_WOWLAN_TRIG_MAGIC_PKT, NL80211_WOWLAN_TRIG_PKT_PATTERN, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, NL80211_WOWLAN_TRIG_RFKILL_RELEASE, /* keep last */ NUM_NL80211_WOWLAN_TRIG, MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 }; /** * enum nl80211_iface_limit_attrs - limit attributes * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that * can be chosen from this set of interface types (u32) * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a * flag attribute for each interface type in this set * @NUM_NL80211_IFACE_LIMIT: number of attributes * @MAX_NL80211_IFACE_LIMIT: highest attribute number */ enum nl80211_iface_limit_attrs { NL80211_IFACE_LIMIT_UNSPEC, NL80211_IFACE_LIMIT_MAX, NL80211_IFACE_LIMIT_TYPES, /* keep last */ NUM_NL80211_IFACE_LIMIT, MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 }; /** * enum nl80211_if_combination_attrs -- interface combination attributes * * @NL80211_IFACE_COMB_UNSPEC: (reserved) * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits * for given interface types, see &enum nl80211_iface_limit_attrs. * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of * interfaces that can be created in this group. This number doesn't * apply to interfaces purely managed in software, which are listed * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that * beacon intervals within this group must be all the same even for * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt * the infrastructure network's beacon interval. * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many * different channels may be used within this group. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * * Examples: * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 * => allows an AP and a STA that must match BIs * * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 * => allows 8 of AP/GO * * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 * => allows two STAs on different channels * * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 * => allows a STA plus three P2P interfaces * * The list of these four possiblities could completely be contained * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate * that any of these groups must match. * * "Combinations" of just a single interface will not be listed here, * a single interface of any valid interface type is assumed to always * be possible by itself. This means that implicitly, for each valid * interface type, the following group always exists: * numbers = [ #{} <= 1 ], channels = 1, max = 1 */ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_UNSPEC, NL80211_IFACE_COMB_LIMITS, NL80211_IFACE_COMB_MAXNUM, NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, /* keep last */ NUM_NL80211_IFACE_COMB, MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 }; /** * enum nl80211_plink_state - state of a mesh peer link finite state machine * * @NL80211_PLINK_LISTEN: initial state, considered the implicit * state of non existant mesh peer links * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to * this mesh peer * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received * from this mesh peer * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been * received from this mesh peer * @NL80211_PLINK_ESTAB: mesh peer link is established * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh * plink are discarded * @NUM_NL80211_PLINK_STATES: number of peer link states * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states */ enum nl80211_plink_state { NL80211_PLINK_LISTEN, NL80211_PLINK_OPN_SNT, NL80211_PLINK_OPN_RCVD, NL80211_PLINK_CNF_RCVD, NL80211_PLINK_ESTAB, NL80211_PLINK_HOLDING, NL80211_PLINK_BLOCKED, /* keep last */ NUM_NL80211_PLINK_STATES, MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 }; #define NL80211_KCK_LEN 16 #define NL80211_KEK_LEN 16 #define NL80211_REPLAY_CTR_LEN 8 /** * enum nl80211_rekey_data - attributes for GTK rekey offload * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes * @NL80211_REKEY_DATA_KEK: key encryption key (binary) * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) */ enum nl80211_rekey_data { __NL80211_REKEY_DATA_INVALID, NL80211_REKEY_DATA_KEK, NL80211_REKEY_DATA_KCK, NL80211_REKEY_DATA_REPLAY_CTR, /* keep last */ NUM_NL80211_REKEY_DATA, MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 }; /** * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in * Beacon frames) * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element * in Beacon frames * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID * element in Beacon frames but zero out each byte in the SSID */ enum nl80211_hidden_ssid { NL80211_HIDDEN_SSID_NOT_IN_USE, NL80211_HIDDEN_SSID_ZERO_LEN, NL80211_HIDDEN_SSID_ZERO_CONTENTS }; /** * enum nl80211_sta_wme_attr - station WME attributes * @__NL80211_STA_WME_INVALID: invalid number for nested attribute * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format * is the same as the AC bitmap in the QoS info field. * @NL80211_STA_WME_MAX_SP: max service period. the format is the same * as the MAX_SP field in the QoS info field (but already shifted down). * @__NL80211_STA_WME_AFTER_LAST: internal * @NL80211_STA_WME_MAX: highest station WME attribute */ enum nl80211_sta_wme_attr { __NL80211_STA_WME_INVALID, NL80211_STA_WME_UAPSD_QUEUES, NL80211_STA_WME_MAX_SP, /* keep last */ __NL80211_STA_WME_AFTER_LAST, NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 }; /** * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher * priority) * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes * (internal) * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute * (internal) */ enum nl80211_pmksa_candidate_attr { __NL80211_PMKSA_CANDIDATE_INVALID, NL80211_PMKSA_CANDIDATE_INDEX, NL80211_PMKSA_CANDIDATE_BSSID, NL80211_PMKSA_CANDIDATE_PREAUTH, /* keep last */ NUM_NL80211_PMKSA_CANDIDATE, MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 }; /** * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request * @NL80211_TDLS_SETUP: Setup TDLS link * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link */ enum nl80211_tdls_operation { NL80211_TDLS_DISCOVERY_REQ, NL80211_TDLS_SETUP, NL80211_TDLS_TEARDOWN, NL80211_TDLS_ENABLE_LINK, NL80211_TDLS_DISABLE_LINK, }; /* * enum nl80211_ap_sme_features - device-integrated AP features * Reserved for future use, no bits are defined in * NL80211_ATTR_DEVICE_AP_SME yet. enum nl80211_ap_sme_features { }; */ /** * enum nl80211_feature_flags - device/driver features * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back * TX status to the socket error queue when requested with the * socket option. * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up * the connected inactive stations in AP mode. * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested * to work properly to suppport receiving regulatory hints from * cellular base stations. * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel * in the interface combinations, even when it's only used for scan * and remain-on-channel. This could be due to, for example, the * remain-on-channel implementation requiring a channel context. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, }; /** * enum nl80211_probe_resp_offload_support_attr - optional supported * protocols for probe-response offloading by the driver/FW. * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. * Each enum value represents a bit in the bitmap of supported * protocols. Typically a subset of probe-requests belonging to a * supported protocol will be excluded from offload and uploaded * to the host. * * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1 * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2 * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u */ enum nl80211_probe_resp_offload_support_attr { NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0, NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1, NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2, NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, }; #endif /* __LINUX_NL80211_H */ compat-drivers-2012-09-18/include/linux/average.h0000644000175000017500000000025112025673354020741 0ustar mcgrofmcgrof#include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)) #include_next #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)) */ compat-drivers-2012-09-18/include/linux/rfkill_backport.h0000644000175000017500000003002212020223432022455 0ustar mcgrofmcgrof#ifndef __RFKILL_H #define __RFKILL_H /* * Copyright (C) 2006 - 2007 Ivo van Doorn * Copyright (C) 2007 Dmitry Torokhov * Copyright 2009 Johannes Berg * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* define userspace visible states */ #define RFKILL_STATE_SOFT_BLOCKED 0 #define RFKILL_STATE_UNBLOCKED 1 #define RFKILL_STATE_HARD_BLOCKED 2 /** * enum rfkill_type - type of rfkill switch. * * @RFKILL_TYPE_ALL: toggles all switches (requests only - not a switch type) * @RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device. * @RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. * @RFKILL_TYPE_UWB: switch is on a ultra wideband device. * @RFKILL_TYPE_WIMAX: switch is on a WiMAX device. * @RFKILL_TYPE_WWAN: switch is on a wireless WAN device. * @RFKILL_TYPE_GPS: switch is on a GPS device. * @RFKILL_TYPE_FM: switch is on a FM radio device. * @NUM_RFKILL_TYPES: number of defined rfkill types */ enum rfkill_type { RFKILL_TYPE_ALL = 0, RFKILL_TYPE_WLAN, RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_UWB, RFKILL_TYPE_WIMAX, RFKILL_TYPE_WWAN, RFKILL_TYPE_GPS, RFKILL_TYPE_FM, NUM_RFKILL_TYPES, }; /** * enum rfkill_operation - operation types * @RFKILL_OP_ADD: a device was added * @RFKILL_OP_DEL: a device was removed * @RFKILL_OP_CHANGE: a device's state changed -- userspace changes one device * @RFKILL_OP_CHANGE_ALL: userspace changes all devices (of a type, or all) */ enum rfkill_operation { RFKILL_OP_ADD = 0, RFKILL_OP_DEL, RFKILL_OP_CHANGE, RFKILL_OP_CHANGE_ALL, }; /** * struct rfkill_event - events for userspace on /dev/rfkill * @idx: index of dev rfkill * @type: type of the rfkill struct * @op: operation code * @hard: hard state (0/1) * @soft: soft state (0/1) * * Structure used for userspace communication on /dev/rfkill, * used for events from the kernel and control to the kernel. */ struct rfkill_event { __u32 idx; __u8 type; __u8 op; __u8 soft, hard; } __attribute__((packed)); /* * We are planning to be backward and forward compatible with changes * to the event struct, by adding new, optional, members at the end. * When reading an event (whether the kernel from userspace or vice * versa) we need to accept anything that's at least as large as the * version 1 event size, but might be able to accept other sizes in * the future. * * One exception is the kernel -- we already have two event sizes in * that we've made the 'hard' member optional since our only option * is to ignore it anyway. */ #define RFKILL_EVENT_SIZE_V1 8 /* ioctl for turning off rfkill-input (if present) */ #define RFKILL_IOC_MAGIC 'R' #define RFKILL_IOC_NOINPUT 1 #define RFKILL_IOCTL_NOINPUT _IO(RFKILL_IOC_MAGIC, RFKILL_IOC_NOINPUT) /* and that's all userspace gets */ #ifdef __KERNEL__ /* don't allow anyone to use these in the kernel */ enum rfkill_user_states { RFKILL_USER_STATE_SOFT_BLOCKED = RFKILL_STATE_SOFT_BLOCKED, RFKILL_USER_STATE_UNBLOCKED = RFKILL_STATE_UNBLOCKED, RFKILL_USER_STATE_HARD_BLOCKED = RFKILL_STATE_HARD_BLOCKED, }; #undef RFKILL_STATE_SOFT_BLOCKED #undef RFKILL_STATE_UNBLOCKED #undef RFKILL_STATE_HARD_BLOCKED #include #include #include #include #include struct device; /* this is opaque */ struct rfkill; /** * struct rfkill_ops - rfkill driver methods * * @poll: poll the rfkill block state(s) -- only assign this method * when you need polling. When called, simply call one of the * rfkill_set{,_hw,_sw}_state family of functions. If the hw * is getting unblocked you need to take into account the return * value of those functions to make sure the software block is * properly used. * @query: query the rfkill block state(s) and call exactly one of the * rfkill_set{,_hw,_sw}_state family of functions. Assign this * method if input events can cause hardware state changes to make * the rfkill core query your driver before setting a requested * block. * @set_block: turn the transmitter on (blocked == false) or off * (blocked == true) -- ignore and return 0 when hard blocked. * This callback must be assigned. */ struct rfkill_ops { void (*poll)(struct rfkill *rfkill, void *data); void (*query)(struct rfkill *rfkill, void *data); int (*set_block)(void *data, bool blocked); }; #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) /** * rfkill_alloc - allocate rfkill structure * @name: name of the struct -- the string is not copied internally * @parent: device that has rf switch on it * @type: type of the switch (RFKILL_TYPE_*) * @ops: rfkill methods * @ops_data: data passed to each method * * This function should be called by the transmitter driver to allocate an * rfkill structure. Returns %NULL on failure. */ struct rfkill * __must_check rfkill_alloc(const char *name, struct device *parent, const enum rfkill_type type, const struct rfkill_ops *ops, void *ops_data); /** * rfkill_register - Register a rfkill structure. * @rfkill: rfkill structure to be registered * * This function should be called by the transmitter driver to register * the rfkill structure. Before calling this function the driver needs * to be ready to service method calls from rfkill. * * If rfkill_init_sw_state() is not called before registration, * set_block() will be called to initialize the software blocked state * to a default value. * * If the hardware blocked state is not set before registration, * it is assumed to be unblocked. */ int __must_check rfkill_register(struct rfkill *rfkill); /** * rfkill_pause_polling(struct rfkill *rfkill) * * Pause polling -- say transmitter is off for other reasons. * NOTE: not necessary for suspend/resume -- in that case the * core stops polling anyway */ void rfkill_pause_polling(struct rfkill *rfkill); /** * rfkill_resume_polling(struct rfkill *rfkill) * * Pause polling -- say transmitter is off for other reasons. * NOTE: not necessary for suspend/resume -- in that case the * core stops polling anyway */ void rfkill_resume_polling(struct rfkill *rfkill); /** * rfkill_unregister - Unregister a rfkill structure. * @rfkill: rfkill structure to be unregistered * * This function should be called by the network driver during device * teardown to destroy rfkill structure. Until it returns, the driver * needs to be able to service method calls. */ void rfkill_unregister(struct rfkill *rfkill); /** * rfkill_destroy - free rfkill structure * @rfkill: rfkill structure to be destroyed * * Destroys the rfkill structure. */ void rfkill_destroy(struct rfkill *rfkill); /** * rfkill_set_hw_state - Set the internal rfkill hardware block state * @rfkill: pointer to the rfkill class to modify. * @state: the current hardware block state to set * * rfkill drivers that get events when the hard-blocked state changes * use this function to notify the rfkill core (and through that also * userspace) of the current state. They should also use this after * resume if the state could have changed. * * You need not (but may) call this function if poll_state is assigned. * * This function can be called in any context, even from within rfkill * callbacks. * * The function returns the combined block state (true if transmitter * should be blocked) so that drivers need not keep track of the soft * block state -- which they might not be able to. */ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked); /** * rfkill_set_sw_state - Set the internal rfkill software block state * @rfkill: pointer to the rfkill class to modify. * @state: the current software block state to set * * rfkill drivers that get events when the soft-blocked state changes * (yes, some platforms directly act on input but allow changing again) * use this function to notify the rfkill core (and through that also * userspace) of the current state. * * Drivers should also call this function after resume if the state has * been changed by the user. This only makes sense for "persistent" * devices (see rfkill_init_sw_state()). * * This function can be called in any context, even from within rfkill * callbacks. * * The function returns the combined block state (true if transmitter * should be blocked). */ bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked); /** * rfkill_init_sw_state - Initialize persistent software block state * @rfkill: pointer to the rfkill class to modify. * @state: the current software block state to set * * rfkill drivers that preserve their software block state over power off * use this function to notify the rfkill core (and through that also * userspace) of their initial state. It should only be used before * registration. * * In addition, it marks the device as "persistent", an attribute which * can be read by userspace. Persistent devices are expected to preserve * their own state when suspended. */ void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked); /** * rfkill_set_states - Set the internal rfkill block states * @rfkill: pointer to the rfkill class to modify. * @sw: the current software block state to set * @hw: the current hardware block state to set * * This function can be called in any context, even from within rfkill * callbacks. */ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw); /** * rfkill_blocked - query rfkill block * * @rfkill: rfkill struct to query */ bool rfkill_blocked(struct rfkill *rfkill); #else /* !RFKILL */ static inline struct rfkill * __must_check rfkill_alloc(const char *name, struct device *parent, const enum rfkill_type type, const struct rfkill_ops *ops, void *ops_data) { return ERR_PTR(-ENODEV); } static inline int __must_check rfkill_register(struct rfkill *rfkill) { if (rfkill == ERR_PTR(-ENODEV)) return 0; return -EINVAL; } static inline void rfkill_pause_polling(struct rfkill *rfkill) { } static inline void rfkill_resume_polling(struct rfkill *rfkill) { } static inline void rfkill_unregister(struct rfkill *rfkill) { } static inline void rfkill_destroy(struct rfkill *rfkill) { } static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) { return blocked; } static inline bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) { return blocked; } static inline void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked) { } static inline void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) { } static inline bool rfkill_blocked(struct rfkill *rfkill) { return false; } #endif /* RFKILL || RFKILL_MODULE */ #ifdef CONFIG_RFKILL_LEDS /** * rfkill_get_led_trigger_name - Get the LED trigger name for the button's LED. * This function might return a NULL pointer if registering of the * LED trigger failed. Use this as "default_trigger" for the LED. */ const char *rfkill_get_led_trigger_name(struct rfkill *rfkill); /** * rfkill_set_led_trigger_name -- set the LED trigger name * @rfkill: rfkill struct * @name: LED trigger name * * This function sets the LED trigger name of the radio LED * trigger that rfkill creates. It is optional, but if called * must be called before rfkill_register() to be effective. */ void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name); #else static inline const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) { return NULL; } static inline void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) { } #endif #endif /* __KERNEL__ */ #endif /* RFKILL_H */ compat-drivers-2012-09-18/include/linux/bcma/0000755000175000017500000000000012015204042020041 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/linux/bcma/bcma_regs.h0000644000175000017500000001067112015204042022141 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_REGS_H_ #define LINUX_BCMA_REGS_H_ /* Some single registers are shared between many cores */ /* BCMA_CLKCTLST: ChipCommon (rev >= 20), PCIe, 80211 */ #define BCMA_CLKCTLST 0x01E0 /* Clock control and status */ #define BCMA_CLKCTLST_FORCEALP 0x00000001 /* Force ALP request */ #define BCMA_CLKCTLST_FORCEHT 0x00000002 /* Force HT request */ #define BCMA_CLKCTLST_FORCEILP 0x00000004 /* Force ILP request */ #define BCMA_CLKCTLST_HAVEALPREQ 0x00000008 /* ALP available request */ #define BCMA_CLKCTLST_HAVEHTREQ 0x00000010 /* HT available request */ #define BCMA_CLKCTLST_HWCROFF 0x00000020 /* Force HW clock request off */ #define BCMA_CLKCTLST_EXTRESREQ 0x00000700 /* Mask of external resource requests */ #define BCMA_CLKCTLST_EXTRESREQ_SHIFT 8 #define BCMA_CLKCTLST_HAVEALP 0x00010000 /* ALP available */ #define BCMA_CLKCTLST_HAVEHT 0x00020000 /* HT available */ #define BCMA_CLKCTLST_BP_ON_ALP 0x00040000 /* RO: running on ALP clock */ #define BCMA_CLKCTLST_BP_ON_HT 0x00080000 /* RO: running on HT clock */ #define BCMA_CLKCTLST_EXTRESST 0x07000000 /* Mask of external resource status */ #define BCMA_CLKCTLST_EXTRESST_SHIFT 24 /* Is there any BCM4328 on BCMA bus? */ #define BCMA_CLKCTLST_4328A0_HAVEHT 0x00010000 /* 4328a0 has reversed bits */ #define BCMA_CLKCTLST_4328A0_HAVEALP 0x00020000 /* 4328a0 has reversed bits */ /* Agent registers (common for every core) */ #define BCMA_IOCTL 0x0408 /* IO control */ #define BCMA_IOCTL_CLK 0x0001 #define BCMA_IOCTL_FGC 0x0002 #define BCMA_IOCTL_CORE_BITS 0x3FFC #define BCMA_IOCTL_PME_EN 0x4000 #define BCMA_IOCTL_BIST_EN 0x8000 #define BCMA_IOST 0x0500 /* IO status */ #define BCMA_IOST_CORE_BITS 0x0FFF #define BCMA_IOST_DMA64 0x1000 #define BCMA_IOST_GATED_CLK 0x2000 #define BCMA_IOST_BIST_ERROR 0x4000 #define BCMA_IOST_BIST_DONE 0x8000 #define BCMA_RESET_CTL 0x0800 #define BCMA_RESET_CTL_RESET 0x0001 /* BCMA PCI config space registers. */ #define BCMA_PCI_PMCSR 0x44 #define BCMA_PCI_PE 0x100 #define BCMA_PCI_BAR0_WIN 0x80 /* Backplane address space 0 */ #define BCMA_PCI_BAR1_WIN 0x84 /* Backplane address space 1 */ #define BCMA_PCI_SPROMCTL 0x88 /* SPROM control */ #define BCMA_PCI_SPROMCTL_WE 0x10 /* SPROM write enable */ #define BCMA_PCI_BAR1_CONTROL 0x8c /* Address space 1 burst control */ #define BCMA_PCI_IRQS 0x90 /* PCI interrupts */ #define BCMA_PCI_IRQMASK 0x94 /* PCI IRQ control and mask (pcirev >= 6 only) */ #define BCMA_PCI_BACKPLANE_IRQS 0x98 /* Backplane Interrupts */ #define BCMA_PCI_BAR0_WIN2 0xAC #define BCMA_PCI_GPIO_IN 0xB0 /* GPIO Input (pcirev >= 3 only) */ #define BCMA_PCI_GPIO_OUT 0xB4 /* GPIO Output (pcirev >= 3 only) */ #define BCMA_PCI_GPIO_OUT_ENABLE 0xB8 /* GPIO Output Enable/Disable (pcirev >= 3 only) */ #define BCMA_PCI_GPIO_SCS 0x10 /* PCI config space bit 4 for 4306c0 slow clock source */ #define BCMA_PCI_GPIO_HWRAD 0x20 /* PCI config space GPIO 13 for hw radio disable */ #define BCMA_PCI_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */ #define BCMA_PCI_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */ /* SiliconBackplane Address Map. * All regions may not exist on all chips. */ #define BCMA_SOC_SDRAM_BASE 0x00000000U /* Physical SDRAM */ #define BCMA_SOC_PCI_MEM 0x08000000U /* Host Mode sb2pcitranslation0 (64 MB) */ #define BCMA_SOC_PCI_MEM_SZ (64 * 1024 * 1024) #define BCMA_SOC_PCI_CFG 0x0c000000U /* Host Mode sb2pcitranslation1 (64 MB) */ #define BCMA_SOC_SDRAM_SWAPPED 0x10000000U /* Byteswapped Physical SDRAM */ #define BCMA_SOC_SDRAM_R2 0x80000000U /* Region 2 for sdram (512 MB) */ #define BCMA_SOC_PCI_DMA 0x40000000U /* Client Mode sb2pcitranslation2 (1 GB) */ #define BCMA_SOC_PCI_DMA2 0x80000000U /* Client Mode sb2pcitranslation2 (1 GB) */ #define BCMA_SOC_PCI_DMA_SZ 0x40000000U /* Client Mode sb2pcitranslation2 size in bytes */ #define BCMA_SOC_PCIE_DMA_L32 0x00000000U /* PCIE Client Mode sb2pcitranslation2 * (2 ZettaBytes), low 32 bits */ #define BCMA_SOC_PCIE_DMA_H32 0x80000000U /* PCIE Client Mode sb2pcitranslation2 * (2 ZettaBytes), high 32 bits */ #define BCMA_SOC_PCI1_MEM 0x40000000U /* Host Mode sb2pcitranslation0 (64 MB) */ #define BCMA_SOC_PCI1_CFG 0x44000000U /* Host Mode sb2pcitranslation1 (64 MB) */ #define BCMA_SOC_PCIE1_DMA_H32 0xc0000000U /* PCIE Client Mode sb2pcitranslation2 * (2 ZettaBytes), high 32 bits */ #define BCMA_SFLASH 0x1c000000 #endif /* LINUX_BCMA_REGS_H_ */ compat-drivers-2012-09-18/include/linux/bcma/bcma_driver_mips.h0000644000175000017500000000315112006264414023530 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_DRIVER_MIPS_H_ #define LINUX_BCMA_DRIVER_MIPS_H_ #define BCMA_MIPS_IPSFLAG 0x0F08 /* which sbflags get routed to mips interrupt 1 */ #define BCMA_MIPS_IPSFLAG_IRQ1 0x0000003F #define BCMA_MIPS_IPSFLAG_IRQ1_SHIFT 0 /* which sbflags get routed to mips interrupt 2 */ #define BCMA_MIPS_IPSFLAG_IRQ2 0x00003F00 #define BCMA_MIPS_IPSFLAG_IRQ2_SHIFT 8 /* which sbflags get routed to mips interrupt 3 */ #define BCMA_MIPS_IPSFLAG_IRQ3 0x003F0000 #define BCMA_MIPS_IPSFLAG_IRQ3_SHIFT 16 /* which sbflags get routed to mips interrupt 4 */ #define BCMA_MIPS_IPSFLAG_IRQ4 0x3F000000 #define BCMA_MIPS_IPSFLAG_IRQ4_SHIFT 24 /* MIPS 74K core registers */ #define BCMA_MIPS_MIPS74K_CORECTL 0x0000 #define BCMA_MIPS_MIPS74K_EXCEPTBASE 0x0004 #define BCMA_MIPS_MIPS74K_BIST 0x000C #define BCMA_MIPS_MIPS74K_INTMASK_INT0 0x0014 #define BCMA_MIPS_MIPS74K_INTMASK(int) \ ((int) * 4 + BCMA_MIPS_MIPS74K_INTMASK_INT0) #define BCMA_MIPS_MIPS74K_NMIMASK 0x002C #define BCMA_MIPS_MIPS74K_GPIOSEL 0x0040 #define BCMA_MIPS_MIPS74K_GPIOOUT 0x0044 #define BCMA_MIPS_MIPS74K_GPIOEN 0x0048 #define BCMA_MIPS_MIPS74K_CLKCTLST 0x01E0 #define BCMA_MIPS_OOBSELOUTA30 0x100 struct bcma_device; struct bcma_drv_mips { struct bcma_device *core; u8 setup_done:1; unsigned int assigned_irqs; }; #ifdef CONFIG_BCMA_DRIVER_MIPS extern void bcma_core_mips_init(struct bcma_drv_mips *mcore); #else static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { } #endif extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore); extern unsigned int bcma_core_mips_irq(struct bcma_device *dev); #endif /* LINUX_BCMA_DRIVER_MIPS_H_ */ compat-drivers-2012-09-18/include/linux/bcma/bcma_driver_pci.h0000644000175000017500000003046012006264414023336 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_DRIVER_PCI_H_ #define LINUX_BCMA_DRIVER_PCI_H_ #include struct pci_dev; /** PCI core registers. **/ #define BCMA_CORE_PCI_CTL 0x0000 /* PCI Control */ #define BCMA_CORE_PCI_CTL_RST_OE 0x00000001 /* PCI_RESET Output Enable */ #define BCMA_CORE_PCI_CTL_RST 0x00000002 /* PCI_RESET driven out to pin */ #define BCMA_CORE_PCI_CTL_CLK_OE 0x00000004 /* Clock gate Output Enable */ #define BCMA_CORE_PCI_CTL_CLK 0x00000008 /* Gate for clock driven out to pin */ #define BCMA_CORE_PCI_ARBCTL 0x0010 /* PCI Arbiter Control */ #define BCMA_CORE_PCI_ARBCTL_INTERN 0x00000001 /* Use internal arbiter */ #define BCMA_CORE_PCI_ARBCTL_EXTERN 0x00000002 /* Use external arbiter */ #define BCMA_CORE_PCI_ARBCTL_PARKID 0x00000006 /* Mask, selects which agent is parked on an idle bus */ #define BCMA_CORE_PCI_ARBCTL_PARKID_LAST 0x00000000 /* Last requestor */ #define BCMA_CORE_PCI_ARBCTL_PARKID_4710 0x00000002 /* 4710 */ #define BCMA_CORE_PCI_ARBCTL_PARKID_EXT0 0x00000004 /* External requestor 0 */ #define BCMA_CORE_PCI_ARBCTL_PARKID_EXT1 0x00000006 /* External requestor 1 */ #define BCMA_CORE_PCI_ISTAT 0x0020 /* Interrupt status */ #define BCMA_CORE_PCI_ISTAT_INTA 0x00000001 /* PCI INTA# */ #define BCMA_CORE_PCI_ISTAT_INTB 0x00000002 /* PCI INTB# */ #define BCMA_CORE_PCI_ISTAT_SERR 0x00000004 /* PCI SERR# (write to clear) */ #define BCMA_CORE_PCI_ISTAT_PERR 0x00000008 /* PCI PERR# (write to clear) */ #define BCMA_CORE_PCI_ISTAT_PME 0x00000010 /* PCI PME# */ #define BCMA_CORE_PCI_IMASK 0x0024 /* Interrupt mask */ #define BCMA_CORE_PCI_IMASK_INTA 0x00000001 /* PCI INTA# */ #define BCMA_CORE_PCI_IMASK_INTB 0x00000002 /* PCI INTB# */ #define BCMA_CORE_PCI_IMASK_SERR 0x00000004 /* PCI SERR# */ #define BCMA_CORE_PCI_IMASK_PERR 0x00000008 /* PCI PERR# */ #define BCMA_CORE_PCI_IMASK_PME 0x00000010 /* PCI PME# */ #define BCMA_CORE_PCI_MBOX 0x0028 /* Backplane to PCI Mailbox */ #define BCMA_CORE_PCI_MBOX_F0_0 0x00000100 /* PCI function 0, INT 0 */ #define BCMA_CORE_PCI_MBOX_F0_1 0x00000200 /* PCI function 0, INT 1 */ #define BCMA_CORE_PCI_MBOX_F1_0 0x00000400 /* PCI function 1, INT 0 */ #define BCMA_CORE_PCI_MBOX_F1_1 0x00000800 /* PCI function 1, INT 1 */ #define BCMA_CORE_PCI_MBOX_F2_0 0x00001000 /* PCI function 2, INT 0 */ #define BCMA_CORE_PCI_MBOX_F2_1 0x00002000 /* PCI function 2, INT 1 */ #define BCMA_CORE_PCI_MBOX_F3_0 0x00004000 /* PCI function 3, INT 0 */ #define BCMA_CORE_PCI_MBOX_F3_1 0x00008000 /* PCI function 3, INT 1 */ #define BCMA_CORE_PCI_BCAST_ADDR 0x0050 /* Backplane Broadcast Address */ #define BCMA_CORE_PCI_BCAST_ADDR_MASK 0x000000FF #define BCMA_CORE_PCI_BCAST_DATA 0x0054 /* Backplane Broadcast Data */ #define BCMA_CORE_PCI_GPIO_IN 0x0060 /* rev >= 2 only */ #define BCMA_CORE_PCI_GPIO_OUT 0x0064 /* rev >= 2 only */ #define BCMA_CORE_PCI_GPIO_ENABLE 0x0068 /* rev >= 2 only */ #define BCMA_CORE_PCI_GPIO_CTL 0x006C /* rev >= 2 only */ #define BCMA_CORE_PCI_SBTOPCI0 0x0100 /* Backplane to PCI translation 0 (sbtopci0) */ #define BCMA_CORE_PCI_SBTOPCI0_MASK 0xFC000000 #define BCMA_CORE_PCI_SBTOPCI1 0x0104 /* Backplane to PCI translation 1 (sbtopci1) */ #define BCMA_CORE_PCI_SBTOPCI1_MASK 0xFC000000 #define BCMA_CORE_PCI_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */ #define BCMA_CORE_PCI_SBTOPCI2_MASK 0xC0000000 #define BCMA_CORE_PCI_CONFIG_ADDR 0x0120 /* pcie config space access */ #define BCMA_CORE_PCI_CONFIG_DATA 0x0124 /* pcie config space access */ #define BCMA_CORE_PCI_MDIO_CONTROL 0x0128 /* controls the mdio access */ #define BCMA_CORE_PCI_MDIOCTL_DIVISOR_MASK 0x7f /* clock to be used on MDIO */ #define BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL 0x2 #define BCMA_CORE_PCI_MDIOCTL_PREAM_EN 0x80 /* Enable preamble sequnce */ #define BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE 0x100 /* Tranaction complete */ #define BCMA_CORE_PCI_MDIO_DATA 0x012c /* Data to the mdio access */ #define BCMA_CORE_PCI_MDIODATA_MASK 0x0000ffff /* data 2 bytes */ #define BCMA_CORE_PCI_MDIODATA_TA 0x00020000 /* Turnaround */ #define BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD 18 /* Regaddr shift (rev < 10) */ #define BCMA_CORE_PCI_MDIODATA_REGADDR_MASK_OLD 0x003c0000 /* Regaddr Mask (rev < 10) */ #define BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD 22 /* Physmedia devaddr shift (rev < 10) */ #define BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK_OLD 0x0fc00000 /* Physmedia devaddr Mask (rev < 10) */ #define BCMA_CORE_PCI_MDIODATA_REGADDR_SHF 18 /* Regaddr shift */ #define BCMA_CORE_PCI_MDIODATA_REGADDR_MASK 0x007c0000 /* Regaddr Mask */ #define BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF 23 /* Physmedia devaddr shift */ #define BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK 0x0f800000 /* Physmedia devaddr Mask */ #define BCMA_CORE_PCI_MDIODATA_WRITE 0x10000000 /* write Transaction */ #define BCMA_CORE_PCI_MDIODATA_READ 0x20000000 /* Read Transaction */ #define BCMA_CORE_PCI_MDIODATA_START 0x40000000 /* start of Transaction */ #define BCMA_CORE_PCI_MDIODATA_DEV_ADDR 0x0 /* dev address for serdes */ #define BCMA_CORE_PCI_MDIODATA_BLK_ADDR 0x1F /* blk address for serdes */ #define BCMA_CORE_PCI_MDIODATA_DEV_PLL 0x1d /* SERDES PLL Dev */ #define BCMA_CORE_PCI_MDIODATA_DEV_TX 0x1e /* SERDES TX Dev */ #define BCMA_CORE_PCI_MDIODATA_DEV_RX 0x1f /* SERDES RX Dev */ #define BCMA_CORE_PCI_PCIEIND_ADDR 0x0130 /* indirect access to the internal register */ #define BCMA_CORE_PCI_PCIEIND_DATA 0x0134 /* Data to/from the internal regsiter */ #define BCMA_CORE_PCI_CLKREQENCTRL 0x0138 /* >= rev 6, Clkreq rdma control */ #define BCMA_CORE_PCI_PCICFG0 0x0400 /* PCI config space 0 (rev >= 8) */ #define BCMA_CORE_PCI_PCICFG1 0x0500 /* PCI config space 1 (rev >= 8) */ #define BCMA_CORE_PCI_PCICFG2 0x0600 /* PCI config space 2 (rev >= 8) */ #define BCMA_CORE_PCI_PCICFG3 0x0700 /* PCI config space 3 (rev >= 8) */ #define BCMA_CORE_PCI_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) /* SPROM shadow area (72 bytes) */ #define BCMA_CORE_PCI_SPROM_PI_OFFSET 0 /* first word */ #define BCMA_CORE_PCI_SPROM_PI_MASK 0xf000 /* bit 15:12 */ #define BCMA_CORE_PCI_SPROM_PI_SHIFT 12 /* bit 15:12 */ #define BCMA_CORE_PCI_SPROM_MISC_CONFIG 5 /* word 5 */ #define BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST 0x8000 /* bit 15 */ #define BCMA_CORE_PCI_SPROM_CLKREQ_OFFSET_REV5 20 /* word 20 for srom rev <= 5 */ #define BCMA_CORE_PCI_SPROM_CLKREQ_ENB 0x0800 /* bit 11 */ /* SBtoPCIx */ #define BCMA_CORE_PCI_SBTOPCI_MEM 0x00000000 #define BCMA_CORE_PCI_SBTOPCI_IO 0x00000001 #define BCMA_CORE_PCI_SBTOPCI_CFG0 0x00000002 #define BCMA_CORE_PCI_SBTOPCI_CFG1 0x00000003 #define BCMA_CORE_PCI_SBTOPCI_PREF 0x00000004 /* Prefetch enable */ #define BCMA_CORE_PCI_SBTOPCI_BURST 0x00000008 /* Burst enable */ #define BCMA_CORE_PCI_SBTOPCI_MRM 0x00000020 /* Memory Read Multiple */ #define BCMA_CORE_PCI_SBTOPCI_RC 0x00000030 /* Read Command mask (rev >= 11) */ #define BCMA_CORE_PCI_SBTOPCI_RC_READ 0x00000000 /* Memory read */ #define BCMA_CORE_PCI_SBTOPCI_RC_READL 0x00000010 /* Memory read line */ #define BCMA_CORE_PCI_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */ /* PCIE protocol PHY diagnostic registers */ #define BCMA_CORE_PCI_PLP_MODEREG 0x200 /* Mode */ #define BCMA_CORE_PCI_PLP_STATUSREG 0x204 /* Status */ #define BCMA_CORE_PCI_PLP_POLARITYINV_STAT 0x10 /* Status reg PCIE_PLP_STATUSREG */ #define BCMA_CORE_PCI_PLP_LTSSMCTRLREG 0x208 /* LTSSM control */ #define BCMA_CORE_PCI_PLP_LTLINKNUMREG 0x20c /* Link Training Link number */ #define BCMA_CORE_PCI_PLP_LTLANENUMREG 0x210 /* Link Training Lane number */ #define BCMA_CORE_PCI_PLP_LTNFTSREG 0x214 /* Link Training N_FTS */ #define BCMA_CORE_PCI_PLP_ATTNREG 0x218 /* Attention */ #define BCMA_CORE_PCI_PLP_ATTNMASKREG 0x21C /* Attention Mask */ #define BCMA_CORE_PCI_PLP_RXERRCTR 0x220 /* Rx Error */ #define BCMA_CORE_PCI_PLP_RXFRMERRCTR 0x224 /* Rx Framing Error */ #define BCMA_CORE_PCI_PLP_RXERRTHRESHREG 0x228 /* Rx Error threshold */ #define BCMA_CORE_PCI_PLP_TESTCTRLREG 0x22C /* Test Control reg */ #define BCMA_CORE_PCI_PLP_SERDESCTRLOVRDREG 0x230 /* SERDES Control Override */ #define BCMA_CORE_PCI_PLP_TIMINGOVRDREG 0x234 /* Timing param override */ #define BCMA_CORE_PCI_PLP_RXTXSMDIAGREG 0x238 /* RXTX State Machine Diag */ #define BCMA_CORE_PCI_PLP_LTSSMDIAGREG 0x23C /* LTSSM State Machine Diag */ /* PCIE protocol DLLP diagnostic registers */ #define BCMA_CORE_PCI_DLLP_LCREG 0x100 /* Link Control */ #define BCMA_CORE_PCI_DLLP_LSREG 0x104 /* Link Status */ #define BCMA_CORE_PCI_DLLP_LAREG 0x108 /* Link Attention */ #define BCMA_CORE_PCI_DLLP_LSREG_LINKUP (1 << 16) #define BCMA_CORE_PCI_DLLP_LAMASKREG 0x10C /* Link Attention Mask */ #define BCMA_CORE_PCI_DLLP_NEXTTXSEQNUMREG 0x110 /* Next Tx Seq Num */ #define BCMA_CORE_PCI_DLLP_ACKEDTXSEQNUMREG 0x114 /* Acked Tx Seq Num */ #define BCMA_CORE_PCI_DLLP_PURGEDTXSEQNUMREG 0x118 /* Purged Tx Seq Num */ #define BCMA_CORE_PCI_DLLP_RXSEQNUMREG 0x11C /* Rx Sequence Number */ #define BCMA_CORE_PCI_DLLP_LRREG 0x120 /* Link Replay */ #define BCMA_CORE_PCI_DLLP_LACKTOREG 0x124 /* Link Ack Timeout */ #define BCMA_CORE_PCI_DLLP_PMTHRESHREG 0x128 /* Power Management Threshold */ #define BCMA_CORE_PCI_ASPMTIMER_EXTEND 0x01000000 /* > rev7: enable extend ASPM timer */ #define BCMA_CORE_PCI_DLLP_RTRYWPREG 0x12C /* Retry buffer write ptr */ #define BCMA_CORE_PCI_DLLP_RTRYRPREG 0x130 /* Retry buffer Read ptr */ #define BCMA_CORE_PCI_DLLP_RTRYPPREG 0x134 /* Retry buffer Purged ptr */ #define BCMA_CORE_PCI_DLLP_RTRRWREG 0x138 /* Retry buffer Read/Write */ #define BCMA_CORE_PCI_DLLP_ECTHRESHREG 0x13C /* Error Count Threshold */ #define BCMA_CORE_PCI_DLLP_TLPERRCTRREG 0x140 /* TLP Error Counter */ #define BCMA_CORE_PCI_DLLP_ERRCTRREG 0x144 /* Error Counter */ #define BCMA_CORE_PCI_DLLP_NAKRXCTRREG 0x148 /* NAK Received Counter */ #define BCMA_CORE_PCI_DLLP_TESTREG 0x14C /* Test */ #define BCMA_CORE_PCI_DLLP_PKTBIST 0x150 /* Packet BIST */ #define BCMA_CORE_PCI_DLLP_PCIE11 0x154 /* DLLP PCIE 1.1 reg */ /* SERDES RX registers */ #define BCMA_CORE_PCI_SERDES_RX_CTRL 1 /* Rx cntrl */ #define BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE 0x80 /* rxpolarity_force */ #define BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY 0x40 /* rxpolarity_value */ #define BCMA_CORE_PCI_SERDES_RX_TIMER1 2 /* Rx Timer1 */ #define BCMA_CORE_PCI_SERDES_RX_CDR 6 /* CDR */ #define BCMA_CORE_PCI_SERDES_RX_CDRBW 7 /* CDR BW */ /* SERDES PLL registers */ #define BCMA_CORE_PCI_SERDES_PLL_CTRL 1 /* PLL control reg */ #define BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN 0x4000 /* bit 14 is FREQDET on */ /* PCIcore specific boardflags */ #define BCMA_CORE_PCI_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */ /* PCIE Config space accessing MACROS */ #define BCMA_CORE_PCI_CFG_BUS_SHIFT 24 /* Bus shift */ #define BCMA_CORE_PCI_CFG_SLOT_SHIFT 19 /* Slot/Device shift */ #define BCMA_CORE_PCI_CFG_FUN_SHIFT 16 /* Function shift */ #define BCMA_CORE_PCI_CFG_OFF_SHIFT 0 /* Register shift */ #define BCMA_CORE_PCI_CFG_BUS_MASK 0xff /* Bus mask */ #define BCMA_CORE_PCI_CFG_SLOT_MASK 0x1f /* Slot/Device mask */ #define BCMA_CORE_PCI_CFG_FUN_MASK 7 /* Function mask */ #define BCMA_CORE_PCI_CFG_OFF_MASK 0xfff /* Register mask */ /* PCIE Root Capability Register bits (Host mode only) */ #define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001 struct bcma_drv_pci; #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE struct bcma_drv_pci_host { struct bcma_drv_pci *pdev; u32 host_cfg_addr; spinlock_t cfgspace_lock; struct pci_controller pci_controller; struct pci_ops pci_ops; struct resource mem_resource; struct resource io_resource; }; #endif struct bcma_drv_pci { struct bcma_device *core; u8 setup_done:1; u8 hostmode:1; #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE struct bcma_drv_pci_host *host_controller; #endif }; /* Register access */ #define pcicore_read16(pc, offset) bcma_read16((pc)->core, offset) #define pcicore_read32(pc, offset) bcma_read32((pc)->core, offset) #define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val) #define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val) extern void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); extern void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend); extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev); extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev); #endif /* LINUX_BCMA_DRIVER_PCI_H_ */ compat-drivers-2012-09-18/include/linux/bcma/bcma_driver_chipcommon.h0000644000175000017500000006711412015204042024714 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_DRIVER_CC_H_ #define LINUX_BCMA_DRIVER_CC_H_ /** ChipCommon core registers. **/ #define BCMA_CC_ID 0x0000 #define BCMA_CC_ID_ID 0x0000FFFF #define BCMA_CC_ID_ID_SHIFT 0 #define BCMA_CC_ID_REV 0x000F0000 #define BCMA_CC_ID_REV_SHIFT 16 #define BCMA_CC_ID_PKG 0x00F00000 #define BCMA_CC_ID_PKG_SHIFT 20 #define BCMA_CC_ID_NRCORES 0x0F000000 #define BCMA_CC_ID_NRCORES_SHIFT 24 #define BCMA_CC_ID_TYPE 0xF0000000 #define BCMA_CC_ID_TYPE_SHIFT 28 #define BCMA_CC_CAP 0x0004 /* Capabilities */ #define BCMA_CC_CAP_NRUART 0x00000003 /* # of UARTs */ #define BCMA_CC_CAP_MIPSEB 0x00000004 /* MIPS in BigEndian Mode */ #define BCMA_CC_CAP_UARTCLK 0x00000018 /* UART clock select */ #define BCMA_CC_CAP_UARTCLK_INT 0x00000008 /* UARTs are driven by internal divided clock */ #define BCMA_CC_CAP_UARTGPIO 0x00000020 /* UARTs on GPIO 15-12 */ #define BCMA_CC_CAP_EXTBUS 0x000000C0 /* External buses present */ #define BCMA_CC_CAP_FLASHT 0x00000700 /* Flash Type */ #define BCMA_CC_FLASHT_NONE 0x00000000 /* No flash */ #define BCMA_CC_FLASHT_STSER 0x00000100 /* ST serial flash */ #define BCMA_CC_FLASHT_ATSER 0x00000200 /* Atmel serial flash */ #define BCMA_CC_FLASHT_NFLASH 0x00000200 /* NAND flash */ #define BCMA_CC_FLASHT_PARA 0x00000700 /* Parallel flash */ #define BCMA_CC_CAP_PLLT 0x00038000 /* PLL Type */ #define BCMA_PLLTYPE_NONE 0x00000000 #define BCMA_PLLTYPE_1 0x00010000 /* 48Mhz base, 3 dividers */ #define BCMA_PLLTYPE_2 0x00020000 /* 48Mhz, 4 dividers */ #define BCMA_PLLTYPE_3 0x00030000 /* 25Mhz, 2 dividers */ #define BCMA_PLLTYPE_4 0x00008000 /* 48Mhz, 4 dividers */ #define BCMA_PLLTYPE_5 0x00018000 /* 25Mhz, 4 dividers */ #define BCMA_PLLTYPE_6 0x00028000 /* 100/200 or 120/240 only */ #define BCMA_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */ #define BCMA_CC_CAP_PCTL 0x00040000 /* Power Control */ #define BCMA_CC_CAP_OTPS 0x00380000 /* OTP size */ #define BCMA_CC_CAP_OTPS_SHIFT 19 #define BCMA_CC_CAP_OTPS_BASE 5 #define BCMA_CC_CAP_JTAGM 0x00400000 /* JTAG master present */ #define BCMA_CC_CAP_BROM 0x00800000 /* Internal boot ROM active */ #define BCMA_CC_CAP_64BIT 0x08000000 /* 64-bit Backplane */ #define BCMA_CC_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */ #define BCMA_CC_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */ #define BCMA_CC_CAP_SPROM 0x40000000 /* SPROM present */ #define BCMA_CC_CAP_NFLASH 0x80000000 /* NAND flash present (rev >= 35 or BCM4706?) */ #define BCMA_CC_CORECTL 0x0008 #define BCMA_CC_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */ #define BCMA_CC_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ #define BCMA_CC_CORECTL_UARTCLKEN 0x00000008 /* UART clock enable (rev >= 21) */ #define BCMA_CC_BIST 0x000C #define BCMA_CC_OTPS 0x0010 /* OTP status */ #define BCMA_CC_OTPS_PROGFAIL 0x80000000 #define BCMA_CC_OTPS_PROTECT 0x00000007 #define BCMA_CC_OTPS_HW_PROTECT 0x00000001 #define BCMA_CC_OTPS_SW_PROTECT 0x00000002 #define BCMA_CC_OTPS_CID_PROTECT 0x00000004 #define BCMA_CC_OTPS_GU_PROG_IND 0x00000F00 /* General Use programmed indication */ #define BCMA_CC_OTPS_GU_PROG_IND_SHIFT 8 #define BCMA_CC_OTPS_GU_PROG_HW 0x00000100 /* HW region programmed */ #define BCMA_CC_OTPC 0x0014 /* OTP control */ #define BCMA_CC_OTPC_RECWAIT 0xFF000000 #define BCMA_CC_OTPC_PROGWAIT 0x00FFFF00 #define BCMA_CC_OTPC_PRW_SHIFT 8 #define BCMA_CC_OTPC_MAXFAIL 0x00000038 #define BCMA_CC_OTPC_VSEL 0x00000006 #define BCMA_CC_OTPC_SELVL 0x00000001 #define BCMA_CC_OTPP 0x0018 /* OTP prog */ #define BCMA_CC_OTPP_COL 0x000000FF #define BCMA_CC_OTPP_ROW 0x0000FF00 #define BCMA_CC_OTPP_ROW_SHIFT 8 #define BCMA_CC_OTPP_READERR 0x10000000 #define BCMA_CC_OTPP_VALUE 0x20000000 #define BCMA_CC_OTPP_READ 0x40000000 #define BCMA_CC_OTPP_START 0x80000000 #define BCMA_CC_OTPP_BUSY 0x80000000 #define BCMA_CC_OTPL 0x001C /* OTP layout */ #define BCMA_CC_OTPL_GURGN_OFFSET 0x00000FFF /* offset of general use region */ #define BCMA_CC_IRQSTAT 0x0020 #define BCMA_CC_IRQMASK 0x0024 #define BCMA_CC_IRQ_GPIO 0x00000001 /* gpio intr */ #define BCMA_CC_IRQ_EXT 0x00000002 /* ro: ext intr pin (corerev >= 3) */ #define BCMA_CC_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ #define BCMA_CC_CHIPCTL 0x0028 /* Rev >= 11 only */ #define BCMA_CC_CHIPSTAT 0x002C /* Rev >= 11 only */ #define BCMA_CC_CHIPST_4313_SPROM_PRESENT 1 #define BCMA_CC_CHIPST_4313_OTP_PRESENT 2 #define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2 #define BCMA_CC_CHIPST_4331_OTP_PRESENT 4 #define BCMA_CC_CHIPST_43228_ILP_DIV_EN 0x00000001 #define BCMA_CC_CHIPST_43228_OTP_PRESENT 0x00000002 #define BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL 0x00000004 #define BCMA_CC_CHIPST_43228_SDIO_MODE 0x00000008 #define BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT 0x00000010 #define BCMA_CC_CHIPST_43228_SDIO_RESET 0x00000020 #define BCMA_CC_CHIPST_4706_PKG_OPTION BIT(0) /* 0: full-featured package 1: low-cost package */ #define BCMA_CC_CHIPST_4706_SFLASH_PRESENT BIT(1) /* 0: parallel, 1: serial flash is present */ #define BCMA_CC_CHIPST_4706_SFLASH_TYPE BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */ #define BCMA_CC_CHIPST_4706_MIPS_BENDIAN BIT(3) /* 0: little, 1: big endian */ #define BCMA_CC_CHIPST_4706_PCIE1_DISABLE BIT(5) /* PCIE1 enable strap pin */ #define BCMA_CC_CHIPST_5357_NAND_BOOT BIT(4) /* NAND boot, valid for CC rev 38 and/or BCM5357 */ #define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */ #define BCMA_CC_JCMD_START 0x80000000 #define BCMA_CC_JCMD_BUSY 0x80000000 #define BCMA_CC_JCMD_PAUSE 0x40000000 #define BCMA_CC_JCMD0_ACC_MASK 0x0000F000 #define BCMA_CC_JCMD0_ACC_IRDR 0x00000000 #define BCMA_CC_JCMD0_ACC_DR 0x00001000 #define BCMA_CC_JCMD0_ACC_IR 0x00002000 #define BCMA_CC_JCMD0_ACC_RESET 0x00003000 #define BCMA_CC_JCMD0_ACC_IRPDR 0x00004000 #define BCMA_CC_JCMD0_ACC_PDR 0x00005000 #define BCMA_CC_JCMD0_IRW_MASK 0x00000F00 #define BCMA_CC_JCMD_ACC_MASK 0x000F0000 /* Changes for corerev 11 */ #define BCMA_CC_JCMD_ACC_IRDR 0x00000000 #define BCMA_CC_JCMD_ACC_DR 0x00010000 #define BCMA_CC_JCMD_ACC_IR 0x00020000 #define BCMA_CC_JCMD_ACC_RESET 0x00030000 #define BCMA_CC_JCMD_ACC_IRPDR 0x00040000 #define BCMA_CC_JCMD_ACC_PDR 0x00050000 #define BCMA_CC_JCMD_IRW_MASK 0x00001F00 #define BCMA_CC_JCMD_IRW_SHIFT 8 #define BCMA_CC_JCMD_DRW_MASK 0x0000003F #define BCMA_CC_JIR 0x0034 /* Rev >= 10 only */ #define BCMA_CC_JDR 0x0038 /* Rev >= 10 only */ #define BCMA_CC_JCTL 0x003C /* Rev >= 10 only */ #define BCMA_CC_JCTL_FORCE_CLK 4 /* Force clock */ #define BCMA_CC_JCTL_EXT_EN 2 /* Enable external targets */ #define BCMA_CC_JCTL_EN 1 /* Enable Jtag master */ #define BCMA_CC_FLASHCTL 0x0040 /* Start/busy bit in flashcontrol */ #define BCMA_CC_FLASHCTL_OPCODE 0x000000ff #define BCMA_CC_FLASHCTL_ACTION 0x00000700 #define BCMA_CC_FLASHCTL_CS_ACTIVE 0x00001000 /* Chip Select Active, rev >= 20 */ #define BCMA_CC_FLASHCTL_START 0x80000000 #define BCMA_CC_FLASHCTL_BUSY BCMA_CC_FLASHCTL_START /* Flashcontrol action + opcodes for ST flashes */ #define BCMA_CC_FLASHCTL_ST_WREN 0x0006 /* Write Enable */ #define BCMA_CC_FLASHCTL_ST_WRDIS 0x0004 /* Write Disable */ #define BCMA_CC_FLASHCTL_ST_RDSR 0x0105 /* Read Status Register */ #define BCMA_CC_FLASHCTL_ST_WRSR 0x0101 /* Write Status Register */ #define BCMA_CC_FLASHCTL_ST_READ 0x0303 /* Read Data Bytes */ #define BCMA_CC_FLASHCTL_ST_PP 0x0302 /* Page Program */ #define BCMA_CC_FLASHCTL_ST_SE 0x02d8 /* Sector Erase */ #define BCMA_CC_FLASHCTL_ST_BE 0x00c7 /* Bulk Erase */ #define BCMA_CC_FLASHCTL_ST_DP 0x00b9 /* Deep Power-down */ #define BCMA_CC_FLASHCTL_ST_RES 0x03ab /* Read Electronic Signature */ #define BCMA_CC_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */ #define BCMA_CC_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */ /* Flashcontrol action + opcodes for Atmel flashes */ #define BCMA_CC_FLASHCTL_AT_READ 0x07e8 #define BCMA_CC_FLASHCTL_AT_PAGE_READ 0x07d2 #define BCMA_CC_FLASHCTL_AT_STATUS 0x01d7 #define BCMA_CC_FLASHCTL_AT_BUF1_WRITE 0x0384 #define BCMA_CC_FLASHCTL_AT_BUF2_WRITE 0x0387 #define BCMA_CC_FLASHCTL_AT_BUF1_ERASE_PROGRAM 0x0283 #define BCMA_CC_FLASHCTL_AT_BUF2_ERASE_PROGRAM 0x0286 #define BCMA_CC_FLASHCTL_AT_BUF1_PROGRAM 0x0288 #define BCMA_CC_FLASHCTL_AT_BUF2_PROGRAM 0x0289 #define BCMA_CC_FLASHCTL_AT_PAGE_ERASE 0x0281 #define BCMA_CC_FLASHCTL_AT_BLOCK_ERASE 0x0250 #define BCMA_CC_FLASHCTL_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382 #define BCMA_CC_FLASHCTL_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385 #define BCMA_CC_FLASHCTL_AT_BUF1_LOAD 0x0253 #define BCMA_CC_FLASHCTL_AT_BUF2_LOAD 0x0255 #define BCMA_CC_FLASHCTL_AT_BUF1_COMPARE 0x0260 #define BCMA_CC_FLASHCTL_AT_BUF2_COMPARE 0x0261 #define BCMA_CC_FLASHCTL_AT_BUF1_REPROGRAM 0x0258 #define BCMA_CC_FLASHCTL_AT_BUF2_REPROGRAM 0x0259 #define BCMA_CC_FLASHADDR 0x0044 #define BCMA_CC_FLASHDATA 0x0048 /* Status register bits for ST flashes */ #define BCMA_CC_FLASHDATA_ST_WIP 0x01 /* Write In Progress */ #define BCMA_CC_FLASHDATA_ST_WEL 0x02 /* Write Enable Latch */ #define BCMA_CC_FLASHDATA_ST_BP_MASK 0x1c /* Block Protect */ #define BCMA_CC_FLASHDATA_ST_BP_SHIFT 2 #define BCMA_CC_FLASHDATA_ST_SRWD 0x80 /* Status Register Write Disable */ /* Status register bits for Atmel flashes */ #define BCMA_CC_FLASHDATA_AT_READY 0x80 #define BCMA_CC_FLASHDATA_AT_MISMATCH 0x40 #define BCMA_CC_FLASHDATA_AT_ID_MASK 0x38 #define BCMA_CC_FLASHDATA_AT_ID_SHIFT 3 #define BCMA_CC_BCAST_ADDR 0x0050 #define BCMA_CC_BCAST_DATA 0x0054 #define BCMA_CC_GPIOPULLUP 0x0058 /* Rev >= 20 only */ #define BCMA_CC_GPIOPULLDOWN 0x005C /* Rev >= 20 only */ #define BCMA_CC_GPIOIN 0x0060 #define BCMA_CC_GPIOOUT 0x0064 #define BCMA_CC_GPIOOUTEN 0x0068 #define BCMA_CC_GPIOCTL 0x006C #define BCMA_CC_GPIOPOL 0x0070 #define BCMA_CC_GPIOIRQ 0x0074 #define BCMA_CC_WATCHDOG 0x0080 #define BCMA_CC_GPIOTIMER 0x0088 /* LED powersave (corerev >= 16) */ #define BCMA_CC_GPIOTIMER_OFFTIME 0x0000FFFF #define BCMA_CC_GPIOTIMER_OFFTIME_SHIFT 0 #define BCMA_CC_GPIOTIMER_ONTIME 0xFFFF0000 #define BCMA_CC_GPIOTIMER_ONTIME_SHIFT 16 #define BCMA_CC_GPIOTOUTM 0x008C /* LED powersave (corerev >= 16) */ #define BCMA_CC_CLOCK_N 0x0090 #define BCMA_CC_CLOCK_SB 0x0094 #define BCMA_CC_CLOCK_PCI 0x0098 #define BCMA_CC_CLOCK_M2 0x009C #define BCMA_CC_CLOCK_MIPS 0x00A0 #define BCMA_CC_CLKDIV 0x00A4 /* Rev >= 3 only */ #define BCMA_CC_CLKDIV_SFLASH 0x0F000000 #define BCMA_CC_CLKDIV_SFLASH_SHIFT 24 #define BCMA_CC_CLKDIV_OTP 0x000F0000 #define BCMA_CC_CLKDIV_OTP_SHIFT 16 #define BCMA_CC_CLKDIV_JTAG 0x00000F00 #define BCMA_CC_CLKDIV_JTAG_SHIFT 8 #define BCMA_CC_CLKDIV_UART 0x000000FF #define BCMA_CC_CAP_EXT 0x00AC /* Capabilities */ #define BCMA_CC_PLLONDELAY 0x00B0 /* Rev >= 4 only */ #define BCMA_CC_FREFSELDELAY 0x00B4 /* Rev >= 4 only */ #define BCMA_CC_SLOWCLKCTL 0x00B8 /* 6 <= Rev <= 9 only */ #define BCMA_CC_SLOWCLKCTL_SRC 0x00000007 /* slow clock source mask */ #define BCMA_CC_SLOWCLKCTL_SRC_LPO 0x00000000 /* source of slow clock is LPO */ #define BCMA_CC_SLOWCLKCTL_SRC_XTAL 0x00000001 /* source of slow clock is crystal */ #define BCMA_CC_SLOECLKCTL_SRC_PCI 0x00000002 /* source of slow clock is PCI */ #define BCMA_CC_SLOWCLKCTL_LPOFREQ 0x00000200 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ #define BCMA_CC_SLOWCLKCTL_LPOPD 0x00000400 /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ #define BCMA_CC_SLOWCLKCTL_FSLOW 0x00000800 /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ #define BCMA_CC_SLOWCLKCTL_IPLL 0x00001000 /* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */ #define BCMA_CC_SLOWCLKCTL_ENXTAL 0x00002000 /* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */ #define BCMA_CC_SLOWCLKCTL_XTALPU 0x00004000 /* XtalPU (RO), 1/0: crystal running/disabled */ #define BCMA_CC_SLOWCLKCTL_CLKDIV 0xFFFF0000 /* ClockDivider (SlowClk = 1/(4+divisor)) */ #define BCMA_CC_SLOWCLKCTL_CLKDIV_SHIFT 16 #define BCMA_CC_SYSCLKCTL 0x00C0 /* Rev >= 3 only */ #define BCMA_CC_SYSCLKCTL_IDLPEN 0x00000001 /* ILPen: Enable Idle Low Power */ #define BCMA_CC_SYSCLKCTL_ALPEN 0x00000002 /* ALPen: Enable Active Low Power */ #define BCMA_CC_SYSCLKCTL_PLLEN 0x00000004 /* ForcePLLOn */ #define BCMA_CC_SYSCLKCTL_FORCEALP 0x00000008 /* Force ALP (or HT if ALPen is not set */ #define BCMA_CC_SYSCLKCTL_FORCEHT 0x00000010 /* Force HT */ #define BCMA_CC_SYSCLKCTL_CLKDIV 0xFFFF0000 /* ClkDiv (ILP = 1/(4+divisor)) */ #define BCMA_CC_SYSCLKCTL_CLKDIV_SHIFT 16 #define BCMA_CC_CLKSTSTR 0x00C4 /* Rev >= 3 only */ #define BCMA_CC_EROM 0x00FC #define BCMA_CC_PCMCIA_CFG 0x0100 #define BCMA_CC_PCMCIA_MEMWAIT 0x0104 #define BCMA_CC_PCMCIA_ATTRWAIT 0x0108 #define BCMA_CC_PCMCIA_IOWAIT 0x010C #define BCMA_CC_IDE_CFG 0x0110 #define BCMA_CC_IDE_MEMWAIT 0x0114 #define BCMA_CC_IDE_ATTRWAIT 0x0118 #define BCMA_CC_IDE_IOWAIT 0x011C #define BCMA_CC_PROG_CFG 0x0120 #define BCMA_CC_PROG_WAITCNT 0x0124 #define BCMA_CC_FLASH_CFG 0x0128 #define BCMA_CC_FLASH_CFG_DS 0x0010 /* Data size, 0=8bit, 1=16bit */ #define BCMA_CC_FLASH_WAITCNT 0x012C #define BCMA_CC_SROM_CONTROL 0x0190 #define BCMA_CC_SROM_CONTROL_START 0x80000000 #define BCMA_CC_SROM_CONTROL_BUSY 0x80000000 #define BCMA_CC_SROM_CONTROL_OPCODE 0x60000000 #define BCMA_CC_SROM_CONTROL_OP_READ 0x00000000 #define BCMA_CC_SROM_CONTROL_OP_WRITE 0x20000000 #define BCMA_CC_SROM_CONTROL_OP_WRDIS 0x40000000 #define BCMA_CC_SROM_CONTROL_OP_WREN 0x60000000 #define BCMA_CC_SROM_CONTROL_OTPSEL 0x00000010 #define BCMA_CC_SROM_CONTROL_LOCK 0x00000008 #define BCMA_CC_SROM_CONTROL_SIZE_MASK 0x00000006 #define BCMA_CC_SROM_CONTROL_SIZE_1K 0x00000000 #define BCMA_CC_SROM_CONTROL_SIZE_4K 0x00000002 #define BCMA_CC_SROM_CONTROL_SIZE_16K 0x00000004 #define BCMA_CC_SROM_CONTROL_SIZE_SHIFT 1 #define BCMA_CC_SROM_CONTROL_PRESENT 0x00000001 /* Block 0x140 - 0x190 registers are chipset specific */ #define BCMA_CC_4706_FLASHSCFG 0x18C /* Flash struct configuration */ #define BCMA_CC_4706_FLASHSCFG_MASK 0x000000ff #define BCMA_CC_4706_FLASHSCFG_SF1 0x00000001 /* 2nd serial flash present */ #define BCMA_CC_4706_FLASHSCFG_PF1 0x00000002 /* 2nd parallel flash present */ #define BCMA_CC_4706_FLASHSCFG_SF1_TYPE 0x00000004 /* 2nd serial flash type : 0 : ST, 1 : Atmel */ #define BCMA_CC_4706_FLASHSCFG_NF1 0x00000008 /* 2nd NAND flash present */ #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_MASK 0x000000f0 #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_4MB 0x00000010 /* 4MB */ #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_8MB 0x00000020 /* 8MB */ #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_16MB 0x00000030 /* 16MB */ #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_32MB 0x00000040 /* 32MB */ #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_64MB 0x00000050 /* 64MB */ #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_128MB 0x00000060 /* 128MB */ #define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_256MB 0x00000070 /* 256MB */ /* NAND flash registers for BCM4706 (corerev = 31) */ #define BCMA_CC_NFLASH_CTL 0x01A0 #define BCMA_CC_NFLASH_CTL_ERR 0x08000000 #define BCMA_CC_NFLASH_CONF 0x01A4 #define BCMA_CC_NFLASH_COL_ADDR 0x01A8 #define BCMA_CC_NFLASH_ROW_ADDR 0x01AC #define BCMA_CC_NFLASH_DATA 0x01B0 #define BCMA_CC_NFLASH_WAITCNT0 0x01B4 /* 0x1E0 is defined as shared BCMA_CLKCTLST */ #define BCMA_CC_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */ #define BCMA_CC_UART0_DATA 0x0300 #define BCMA_CC_UART0_IMR 0x0304 #define BCMA_CC_UART0_FCR 0x0308 #define BCMA_CC_UART0_LCR 0x030C #define BCMA_CC_UART0_MCR 0x0310 #define BCMA_CC_UART0_LSR 0x0314 #define BCMA_CC_UART0_MSR 0x0318 #define BCMA_CC_UART0_SCRATCH 0x031C #define BCMA_CC_UART1_DATA 0x0400 #define BCMA_CC_UART1_IMR 0x0404 #define BCMA_CC_UART1_FCR 0x0408 #define BCMA_CC_UART1_LCR 0x040C #define BCMA_CC_UART1_MCR 0x0410 #define BCMA_CC_UART1_LSR 0x0414 #define BCMA_CC_UART1_MSR 0x0418 #define BCMA_CC_UART1_SCRATCH 0x041C /* PMU registers (rev >= 20) */ #define BCMA_CC_PMU_CTL 0x0600 /* PMU control */ #define BCMA_CC_PMU_CTL_ILP_DIV 0xFFFF0000 /* ILP div mask */ #define BCMA_CC_PMU_CTL_ILP_DIV_SHIFT 16 #define BCMA_CC_PMU_CTL_PLL_UPD 0x00000400 #define BCMA_CC_PMU_CTL_NOILPONW 0x00000200 /* No ILP on wait */ #define BCMA_CC_PMU_CTL_HTREQEN 0x00000100 /* HT req enable */ #define BCMA_CC_PMU_CTL_ALPREQEN 0x00000080 /* ALP req enable */ #define BCMA_CC_PMU_CTL_XTALFREQ 0x0000007C /* Crystal freq */ #define BCMA_CC_PMU_CTL_XTALFREQ_SHIFT 2 #define BCMA_CC_PMU_CTL_ILPDIVEN 0x00000002 /* ILP div enable */ #define BCMA_CC_PMU_CTL_LPOSEL 0x00000001 /* LPO sel */ #define BCMA_CC_PMU_CAP 0x0604 /* PMU capabilities */ #define BCMA_CC_PMU_CAP_REVISION 0x000000FF /* Revision mask */ #define BCMA_CC_PMU_STAT 0x0608 /* PMU status */ #define BCMA_CC_PMU_STAT_INTPEND 0x00000040 /* Interrupt pending */ #define BCMA_CC_PMU_STAT_SBCLKST 0x00000030 /* Backplane clock status? */ #define BCMA_CC_PMU_STAT_HAVEALP 0x00000008 /* ALP available */ #define BCMA_CC_PMU_STAT_HAVEHT 0x00000004 /* HT available */ #define BCMA_CC_PMU_STAT_RESINIT 0x00000003 /* Res init */ #define BCMA_CC_PMU_RES_STAT 0x060C /* PMU res status */ #define BCMA_CC_PMU_RES_PEND 0x0610 /* PMU res pending */ #define BCMA_CC_PMU_TIMER 0x0614 /* PMU timer */ #define BCMA_CC_PMU_MINRES_MSK 0x0618 /* PMU min res mask */ #define BCMA_CC_PMU_MAXRES_MSK 0x061C /* PMU max res mask */ #define BCMA_CC_PMU_RES_TABSEL 0x0620 /* PMU res table sel */ #define BCMA_CC_PMU_RES_DEPMSK 0x0624 /* PMU res dep mask */ #define BCMA_CC_PMU_RES_UPDNTM 0x0628 /* PMU res updown timer */ #define BCMA_CC_PMU_RES_TIMER 0x062C /* PMU res timer */ #define BCMA_CC_PMU_CLKSTRETCH 0x0630 /* PMU clockstretch */ #define BCMA_CC_PMU_WATCHDOG 0x0634 /* PMU watchdog */ #define BCMA_CC_PMU_RES_REQTS 0x0640 /* PMU res req timer sel */ #define BCMA_CC_PMU_RES_REQT 0x0644 /* PMU res req timer */ #define BCMA_CC_PMU_RES_REQM 0x0648 /* PMU res req mask */ #define BCMA_CC_CHIPCTL_ADDR 0x0650 #define BCMA_CC_CHIPCTL_DATA 0x0654 #define BCMA_CC_REGCTL_ADDR 0x0658 #define BCMA_CC_REGCTL_DATA 0x065C #define BCMA_CC_PLLCTL_ADDR 0x0660 #define BCMA_CC_PLLCTL_DATA 0x0664 #define BCMA_CC_SPROM 0x0800 /* SPROM beginning */ /* NAND flash MLC controller registers (corerev >= 38) */ #define BCMA_CC_NAND_REVISION 0x0C00 #define BCMA_CC_NAND_CMD_START 0x0C04 #define BCMA_CC_NAND_CMD_ADDR_X 0x0C08 #define BCMA_CC_NAND_CMD_ADDR 0x0C0C #define BCMA_CC_NAND_CMD_END_ADDR 0x0C10 #define BCMA_CC_NAND_CS_NAND_SELECT 0x0C14 #define BCMA_CC_NAND_CS_NAND_XOR 0x0C18 #define BCMA_CC_NAND_SPARE_RD0 0x0C20 #define BCMA_CC_NAND_SPARE_RD4 0x0C24 #define BCMA_CC_NAND_SPARE_RD8 0x0C28 #define BCMA_CC_NAND_SPARE_RD12 0x0C2C #define BCMA_CC_NAND_SPARE_WR0 0x0C30 #define BCMA_CC_NAND_SPARE_WR4 0x0C34 #define BCMA_CC_NAND_SPARE_WR8 0x0C38 #define BCMA_CC_NAND_SPARE_WR12 0x0C3C #define BCMA_CC_NAND_ACC_CONTROL 0x0C40 #define BCMA_CC_NAND_CONFIG 0x0C48 #define BCMA_CC_NAND_TIMING_1 0x0C50 #define BCMA_CC_NAND_TIMING_2 0x0C54 #define BCMA_CC_NAND_SEMAPHORE 0x0C58 #define BCMA_CC_NAND_DEVID 0x0C60 #define BCMA_CC_NAND_DEVID_X 0x0C64 #define BCMA_CC_NAND_BLOCK_LOCK_STATUS 0x0C68 #define BCMA_CC_NAND_INTFC_STATUS 0x0C6C #define BCMA_CC_NAND_ECC_CORR_ADDR_X 0x0C70 #define BCMA_CC_NAND_ECC_CORR_ADDR 0x0C74 #define BCMA_CC_NAND_ECC_UNC_ADDR_X 0x0C78 #define BCMA_CC_NAND_ECC_UNC_ADDR 0x0C7C #define BCMA_CC_NAND_READ_ERROR_COUNT 0x0C80 #define BCMA_CC_NAND_CORR_STAT_THRESHOLD 0x0C84 #define BCMA_CC_NAND_READ_ADDR_X 0x0C90 #define BCMA_CC_NAND_READ_ADDR 0x0C94 #define BCMA_CC_NAND_PAGE_PROGRAM_ADDR_X 0x0C98 #define BCMA_CC_NAND_PAGE_PROGRAM_ADDR 0x0C9C #define BCMA_CC_NAND_COPY_BACK_ADDR_X 0x0CA0 #define BCMA_CC_NAND_COPY_BACK_ADDR 0x0CA4 #define BCMA_CC_NAND_BLOCK_ERASE_ADDR_X 0x0CA8 #define BCMA_CC_NAND_BLOCK_ERASE_ADDR 0x0CAC #define BCMA_CC_NAND_INV_READ_ADDR_X 0x0CB0 #define BCMA_CC_NAND_INV_READ_ADDR 0x0CB4 #define BCMA_CC_NAND_BLK_WR_PROTECT 0x0CC0 #define BCMA_CC_NAND_ACC_CONTROL_CS1 0x0CD0 #define BCMA_CC_NAND_CONFIG_CS1 0x0CD4 #define BCMA_CC_NAND_TIMING_1_CS1 0x0CD8 #define BCMA_CC_NAND_TIMING_2_CS1 0x0CDC #define BCMA_CC_NAND_SPARE_RD16 0x0D30 #define BCMA_CC_NAND_SPARE_RD20 0x0D34 #define BCMA_CC_NAND_SPARE_RD24 0x0D38 #define BCMA_CC_NAND_SPARE_RD28 0x0D3C #define BCMA_CC_NAND_CACHE_ADDR 0x0D40 #define BCMA_CC_NAND_CACHE_DATA 0x0D44 #define BCMA_CC_NAND_CTRL_CONFIG 0x0D48 #define BCMA_CC_NAND_CTRL_STATUS 0x0D4C /* Divider allocation in 4716/47162/5356 */ #define BCMA_CC_PMU5_MAINPLL_CPU 1 #define BCMA_CC_PMU5_MAINPLL_MEM 2 #define BCMA_CC_PMU5_MAINPLL_SSB 3 /* PLL usage in 4716/47162 */ #define BCMA_CC_PMU4716_MAINPLL_PLL0 12 /* PLL usage in 5356/5357 */ #define BCMA_CC_PMU5356_MAINPLL_PLL0 0 #define BCMA_CC_PMU5357_MAINPLL_PLL0 0 /* 4706 PMU */ #define BCMA_CC_PMU4706_MAINPLL_PLL0 0 #define BCMA_CC_PMU6_4706_PROCPLL_OFF 4 /* The CPU PLL */ #define BCMA_CC_PMU6_4706_PROC_P2DIV_MASK 0x000f0000 #define BCMA_CC_PMU6_4706_PROC_P2DIV_SHIFT 16 #define BCMA_CC_PMU6_4706_PROC_P1DIV_MASK 0x0000f000 #define BCMA_CC_PMU6_4706_PROC_P1DIV_SHIFT 12 #define BCMA_CC_PMU6_4706_PROC_NDIV_INT_MASK 0x00000ff8 #define BCMA_CC_PMU6_4706_PROC_NDIV_INT_SHIFT 3 #define BCMA_CC_PMU6_4706_PROC_NDIV_MODE_MASK 0x00000007 #define BCMA_CC_PMU6_4706_PROC_NDIV_MODE_SHIFT 0 /* ALP clock on pre-PMU chips */ #define BCMA_CC_PMU_ALP_CLOCK 20000000 /* HT clock for systems with PMU-enabled chipcommon */ #define BCMA_CC_PMU_HT_CLOCK 80000000 /* PMU rev 5 (& 6) */ #define BCMA_CC_PPL_P1P2_OFF 0 #define BCMA_CC_PPL_P1_MASK 0x0f000000 #define BCMA_CC_PPL_P1_SHIFT 24 #define BCMA_CC_PPL_P2_MASK 0x00f00000 #define BCMA_CC_PPL_P2_SHIFT 20 #define BCMA_CC_PPL_M14_OFF 1 #define BCMA_CC_PPL_MDIV_MASK 0x000000ff #define BCMA_CC_PPL_MDIV_WIDTH 8 #define BCMA_CC_PPL_NM5_OFF 2 #define BCMA_CC_PPL_NDIV_MASK 0xfff00000 #define BCMA_CC_PPL_NDIV_SHIFT 20 #define BCMA_CC_PPL_FMAB_OFF 3 #define BCMA_CC_PPL_MRAT_MASK 0xf0000000 #define BCMA_CC_PPL_MRAT_SHIFT 28 #define BCMA_CC_PPL_ABRAT_MASK 0x08000000 #define BCMA_CC_PPL_ABRAT_SHIFT 27 #define BCMA_CC_PPL_FDIV_MASK 0x07ffffff #define BCMA_CC_PPL_PLLCTL_OFF 4 #define BCMA_CC_PPL_PCHI_OFF 5 #define BCMA_CC_PPL_PCHI_MASK 0x0000003f #define BCMA_CC_PMU_PLL_CTL0 0 #define BCMA_CC_PMU_PLL_CTL1 1 #define BCMA_CC_PMU_PLL_CTL2 2 #define BCMA_CC_PMU_PLL_CTL3 3 #define BCMA_CC_PMU_PLL_CTL4 4 #define BCMA_CC_PMU_PLL_CTL5 5 #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000 #define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20 #define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000 #define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT 20 /* BCM4331 ChipControl numbers. */ #define BCMA_CHIPCTL_4331_BT_COEXIST BIT(0) /* 0 disable */ #define BCMA_CHIPCTL_4331_SECI BIT(1) /* 0 SECI is disabled (JATG functional) */ #define BCMA_CHIPCTL_4331_EXT_LNA BIT(2) /* 0 disable */ #define BCMA_CHIPCTL_4331_SPROM_GPIO13_15 BIT(3) /* sprom/gpio13-15 mux */ #define BCMA_CHIPCTL_4331_EXTPA_EN BIT(4) /* 0 ext pa disable, 1 ext pa enabled */ #define BCMA_CHIPCTL_4331_GPIOCLK_ON_SPROMCS BIT(5) /* set drive out GPIO_CLK on sprom_cs pin */ #define BCMA_CHIPCTL_4331_PCIE_MDIO_ON_SPROMCS BIT(6) /* use sprom_cs pin as PCIE mdio interface */ #define BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5 BIT(7) /* aband extpa will be at gpio2/5 and sprom_dout */ #define BCMA_CHIPCTL_4331_OVR_PIPEAUXCLKEN BIT(8) /* override core control on pipe_AuxClkEnable */ #define BCMA_CHIPCTL_4331_OVR_PIPEAUXPWRDOWN BIT(9) /* override core control on pipe_AuxPowerDown */ #define BCMA_CHIPCTL_4331_PCIE_AUXCLKEN BIT(10) /* pcie_auxclkenable */ #define BCMA_CHIPCTL_4331_PCIE_PIPE_PLLDOWN BIT(11) /* pcie_pipe_pllpowerdown */ #define BCMA_CHIPCTL_4331_EXTPA_EN2 BIT(12) /* 0 ext pa disable, 1 ext pa enabled */ #define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */ #define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */ /* 43224 chip-specific ChipControl register bits */ #define BCMA_CCTRL_43224_GPIO_TOGGLE 0x8000 /* gpio[3:0] pins as btcoex or s/w gpio */ #define BCMA_CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */ #define BCMA_CCTRL_43224B0_12MA_LED_DRIVE 0xF0 /* 12 mA drive strength for later 43224s */ /* 4313 Chip specific ChipControl register bits */ #define BCMA_CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */ /* BCM5357 ChipControl register bits */ #define BCMA_CHIPCTL_5357_EXTPA BIT(14) #define BCMA_CHIPCTL_5357_ANT_MUX_2O3 BIT(15) #define BCMA_CHIPCTL_5357_NFLASH BIT(16) #define BCMA_CHIPCTL_5357_I2S_PINS_ENABLE BIT(18) #define BCMA_CHIPCTL_5357_I2CSPI_PINS_ENABLE BIT(19) /* Data for the PMU, if available. * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) */ struct bcma_chipcommon_pmu { u8 rev; /* PMU revision */ u32 crystalfreq; /* The active crystal frequency (in kHz) */ }; #ifdef CONFIG_BCMA_DRIVER_MIPS struct bcma_pflash { u8 buswidth; u32 window; u32 window_size; }; #ifdef CONFIG_BCMA_SFLASH struct bcma_sflash { bool present; u32 window; u32 blocksize; u16 numblocks; u32 size; }; #endif #ifdef CONFIG_BCMA_NFLASH struct mtd_info; struct bcma_nflash { bool present; struct mtd_info *mtd; }; #endif struct bcma_serial_port { void *regs; unsigned long clockspeed; unsigned int irq; unsigned int baud_base; unsigned int reg_shift; }; #endif /* CONFIG_BCMA_DRIVER_MIPS */ struct bcma_drv_cc { struct bcma_device *core; u32 status; u32 capabilities; u32 capabilities_ext; u8 setup_done:1; /* Fast Powerup Delay constant */ u16 fast_pwrup_delay; struct bcma_chipcommon_pmu pmu; #ifdef CONFIG_BCMA_DRIVER_MIPS struct bcma_pflash pflash; #ifdef CONFIG_BCMA_SFLASH struct bcma_sflash sflash; #endif #ifdef CONFIG_BCMA_NFLASH struct bcma_nflash nflash; #endif int nr_serial_ports; struct bcma_serial_port serial_ports[4]; #endif /* CONFIG_BCMA_DRIVER_MIPS */ }; /* Register access */ #define bcma_cc_read32(cc, offset) \ bcma_read32((cc)->core, offset) #define bcma_cc_write32(cc, offset, val) \ bcma_write32((cc)->core, offset, val) #define bcma_cc_mask32(cc, offset, mask) \ bcma_cc_write32(cc, offset, bcma_cc_read32(cc, offset) & (mask)) #define bcma_cc_set32(cc, offset, set) \ bcma_cc_write32(cc, offset, bcma_cc_read32(cc, offset) | (set)) #define bcma_cc_maskset32(cc, offset, mask, set) \ bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set)) extern void bcma_core_chipcommon_init(struct bcma_drv_cc *cc); extern void bcma_chipco_suspend(struct bcma_drv_cc *cc); extern void bcma_chipco_resume(struct bcma_drv_cc *cc); void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable); extern void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks); void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value); u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask); /* Chipcommon GPIO pin access. */ u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask); u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value); u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value); u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value); u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value); u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value); /* PMU support */ extern void bcma_pmu_init(struct bcma_drv_cc *cc); extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value); extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set); extern void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set); extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set); extern void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid); #endif /* LINUX_BCMA_DRIVER_CC_H_ */ compat-drivers-2012-09-18/include/linux/bcma/bcma.h0000644000175000017500000002431212006264414021127 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_H_ #define LINUX_BCMA_H_ #include #include #include #include #include #include #include /* SPROM sharing */ #include "bcma_regs.h" struct bcma_device; struct bcma_bus; enum bcma_hosttype { BCMA_HOSTTYPE_PCI, BCMA_HOSTTYPE_SDIO, BCMA_HOSTTYPE_SOC, }; struct bcma_chipinfo { u16 id; u8 rev; u8 pkg; }; struct bcma_boardinfo { u16 vendor; u16 type; }; enum bcma_clkmode { BCMA_CLKMODE_FAST, BCMA_CLKMODE_DYNAMIC, }; struct bcma_host_ops { u8 (*read8)(struct bcma_device *core, u16 offset); u16 (*read16)(struct bcma_device *core, u16 offset); u32 (*read32)(struct bcma_device *core, u16 offset); void (*write8)(struct bcma_device *core, u16 offset, u8 value); void (*write16)(struct bcma_device *core, u16 offset, u16 value); void (*write32)(struct bcma_device *core, u16 offset, u32 value); #ifdef CONFIG_BCMA_BLOCKIO void (*block_read)(struct bcma_device *core, void *buffer, size_t count, u16 offset, u8 reg_width); void (*block_write)(struct bcma_device *core, const void *buffer, size_t count, u16 offset, u8 reg_width); #endif /* Agent ops */ u32 (*aread32)(struct bcma_device *core, u16 offset); void (*awrite32)(struct bcma_device *core, u16 offset, u32 value); }; /* Core manufacturers */ #define BCMA_MANUF_ARM 0x43B #define BCMA_MANUF_MIPS 0x4A7 #define BCMA_MANUF_BCM 0x4BF /* Core class values. */ #define BCMA_CL_SIM 0x0 #define BCMA_CL_EROM 0x1 #define BCMA_CL_CORESIGHT 0x9 #define BCMA_CL_VERIF 0xB #define BCMA_CL_OPTIMO 0xD #define BCMA_CL_GEN 0xE #define BCMA_CL_PRIMECELL 0xF /* Core-ID values. */ #define BCMA_CORE_OOB_ROUTER 0x367 /* Out of band */ #define BCMA_CORE_4706_CHIPCOMMON 0x500 #define BCMA_CORE_4706_SOC_RAM 0x50E #define BCMA_CORE_4706_MAC_GBIT 0x52D #define BCMA_CORE_AMEMC 0x52E /* DDR1/2 memory controller core */ #define BCMA_CORE_ALTA 0x534 /* I2S core */ #define BCMA_CORE_4706_MAC_GBIT_COMMON 0x5DC #define BCMA_CORE_DDR23_PHY 0x5DD #define BCMA_CORE_INVALID 0x700 #define BCMA_CORE_CHIPCOMMON 0x800 #define BCMA_CORE_ILINE20 0x801 #define BCMA_CORE_SRAM 0x802 #define BCMA_CORE_SDRAM 0x803 #define BCMA_CORE_PCI 0x804 #define BCMA_CORE_MIPS 0x805 #define BCMA_CORE_ETHERNET 0x806 #define BCMA_CORE_V90 0x807 #define BCMA_CORE_USB11_HOSTDEV 0x808 #define BCMA_CORE_ADSL 0x809 #define BCMA_CORE_ILINE100 0x80A #define BCMA_CORE_IPSEC 0x80B #define BCMA_CORE_UTOPIA 0x80C #define BCMA_CORE_PCMCIA 0x80D #define BCMA_CORE_INTERNAL_MEM 0x80E #define BCMA_CORE_MEMC_SDRAM 0x80F #define BCMA_CORE_OFDM 0x810 #define BCMA_CORE_EXTIF 0x811 #define BCMA_CORE_80211 0x812 #define BCMA_CORE_PHY_A 0x813 #define BCMA_CORE_PHY_B 0x814 #define BCMA_CORE_PHY_G 0x815 #define BCMA_CORE_MIPS_3302 0x816 #define BCMA_CORE_USB11_HOST 0x817 #define BCMA_CORE_USB11_DEV 0x818 #define BCMA_CORE_USB20_HOST 0x819 #define BCMA_CORE_USB20_DEV 0x81A #define BCMA_CORE_SDIO_HOST 0x81B #define BCMA_CORE_ROBOSWITCH 0x81C #define BCMA_CORE_PARA_ATA 0x81D #define BCMA_CORE_SATA_XORDMA 0x81E #define BCMA_CORE_ETHERNET_GBIT 0x81F #define BCMA_CORE_PCIE 0x820 #define BCMA_CORE_PHY_N 0x821 #define BCMA_CORE_SRAM_CTL 0x822 #define BCMA_CORE_MINI_MACPHY 0x823 #define BCMA_CORE_ARM_1176 0x824 #define BCMA_CORE_ARM_7TDMI 0x825 #define BCMA_CORE_PHY_LP 0x826 #define BCMA_CORE_PMU 0x827 #define BCMA_CORE_PHY_SSN 0x828 #define BCMA_CORE_SDIO_DEV 0x829 #define BCMA_CORE_ARM_CM3 0x82A #define BCMA_CORE_PHY_HT 0x82B #define BCMA_CORE_MIPS_74K 0x82C #define BCMA_CORE_MAC_GBIT 0x82D #define BCMA_CORE_DDR12_MEM_CTL 0x82E #define BCMA_CORE_PCIE_RC 0x82F /* PCIe Root Complex */ #define BCMA_CORE_OCP_OCP_BRIDGE 0x830 #define BCMA_CORE_SHARED_COMMON 0x831 #define BCMA_CORE_OCP_AHB_BRIDGE 0x832 #define BCMA_CORE_SPI_HOST 0x833 #define BCMA_CORE_I2S 0x834 #define BCMA_CORE_SDR_DDR1_MEM_CTL 0x835 /* SDR/DDR1 memory controller core */ #define BCMA_CORE_SHIM 0x837 /* SHIM component in ubus/6362 */ #define BCMA_CORE_DEFAULT 0xFFF #define BCMA_MAX_NR_CORES 16 /* Chip IDs of PCIe devices */ #define BCMA_CHIP_ID_BCM4313 0x4313 #define BCMA_CHIP_ID_BCM43224 43224 #define BCMA_PKG_ID_BCM43224_FAB_CSM 0x8 #define BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa #define BCMA_CHIP_ID_BCM43225 43225 #define BCMA_CHIP_ID_BCM43227 43227 #define BCMA_CHIP_ID_BCM43228 43228 #define BCMA_CHIP_ID_BCM43421 43421 #define BCMA_CHIP_ID_BCM43428 43428 #define BCMA_CHIP_ID_BCM43431 43431 #define BCMA_CHIP_ID_BCM43460 43460 #define BCMA_CHIP_ID_BCM4331 0x4331 #define BCMA_CHIP_ID_BCM6362 0x6362 #define BCMA_CHIP_ID_BCM4360 0x4360 #define BCMA_CHIP_ID_BCM4352 0x4352 /* Chip IDs of SoCs */ #define BCMA_CHIP_ID_BCM4706 0x5300 #define BCMA_CHIP_ID_BCM4716 0x4716 #define BCMA_PKG_ID_BCM4716 8 #define BCMA_PKG_ID_BCM4717 9 #define BCMA_PKG_ID_BCM4718 10 #define BCMA_CHIP_ID_BCM47162 47162 #define BCMA_CHIP_ID_BCM4748 0x4748 #define BCMA_CHIP_ID_BCM4749 0x4749 #define BCMA_CHIP_ID_BCM5356 0x5356 #define BCMA_CHIP_ID_BCM5357 0x5357 #define BCMA_CHIP_ID_BCM53572 53572 struct bcma_device { struct bcma_bus *bus; struct bcma_device_id id; struct device dev; struct device *dma_dev; unsigned int irq; bool dev_registered; u8 core_index; u8 core_unit; u32 addr; u32 addr1; u32 wrap; void __iomem *io_addr; void __iomem *io_wrap; void *drvdata; struct list_head list; }; static inline void *bcma_get_drvdata(struct bcma_device *core) { return core->drvdata; } static inline void bcma_set_drvdata(struct bcma_device *core, void *drvdata) { core->drvdata = drvdata; } struct bcma_driver { const char *name; const struct bcma_device_id *id_table; int (*probe)(struct bcma_device *dev); void (*remove)(struct bcma_device *dev); int (*suspend)(struct bcma_device *dev); int (*resume)(struct bcma_device *dev); void (*shutdown)(struct bcma_device *dev); struct device_driver drv; }; extern int __bcma_driver_register(struct bcma_driver *drv, struct module *owner); #define bcma_driver_register(drv) \ __bcma_driver_register(drv, THIS_MODULE) extern void bcma_driver_unregister(struct bcma_driver *drv); /* Set a fallback SPROM. * See kdoc at the function definition for complete documentation. */ extern int bcma_arch_register_fallback_sprom( int (*sprom_callback)(struct bcma_bus *bus, struct ssb_sprom *out)); struct bcma_bus { /* The MMIO area. */ void __iomem *mmio; const struct bcma_host_ops *ops; enum bcma_hosttype hosttype; union { /* Pointer to the PCI bus (only for BCMA_HOSTTYPE_PCI) */ struct pci_dev *host_pci; /* Pointer to the SDIO device (only for BCMA_HOSTTYPE_SDIO) */ struct sdio_func *host_sdio; }; struct bcma_chipinfo chipinfo; struct bcma_boardinfo boardinfo; struct bcma_device *mapped_core; struct list_head cores; u8 nr_cores; u8 init_done:1; u8 num; struct bcma_drv_cc drv_cc; struct bcma_drv_pci drv_pci; struct bcma_drv_mips drv_mips; struct bcma_drv_gmac_cmn drv_gmac_cmn; /* We decided to share SPROM struct with SSB as long as we do not need * any hacks for BCMA. This simplifies drivers code. */ struct ssb_sprom sprom; }; static inline u32 bcma_read8(struct bcma_device *core, u16 offset) { return core->bus->ops->read8(core, offset); } static inline u32 bcma_read16(struct bcma_device *core, u16 offset) { return core->bus->ops->read16(core, offset); } static inline u32 bcma_read32(struct bcma_device *core, u16 offset) { return core->bus->ops->read32(core, offset); } static inline void bcma_write8(struct bcma_device *core, u16 offset, u32 value) { core->bus->ops->write8(core, offset, value); } static inline void bcma_write16(struct bcma_device *core, u16 offset, u32 value) { core->bus->ops->write16(core, offset, value); } static inline void bcma_write32(struct bcma_device *core, u16 offset, u32 value) { core->bus->ops->write32(core, offset, value); } #ifdef CONFIG_BCMA_BLOCKIO static inline void bcma_block_read(struct bcma_device *core, void *buffer, size_t count, u16 offset, u8 reg_width) { core->bus->ops->block_read(core, buffer, count, offset, reg_width); } static inline void bcma_block_write(struct bcma_device *core, const void *buffer, size_t count, u16 offset, u8 reg_width) { core->bus->ops->block_write(core, buffer, count, offset, reg_width); } #endif static inline u32 bcma_aread32(struct bcma_device *core, u16 offset) { return core->bus->ops->aread32(core, offset); } static inline void bcma_awrite32(struct bcma_device *core, u16 offset, u32 value) { core->bus->ops->awrite32(core, offset, value); } static inline void bcma_mask32(struct bcma_device *cc, u16 offset, u32 mask) { bcma_write32(cc, offset, bcma_read32(cc, offset) & mask); } static inline void bcma_set32(struct bcma_device *cc, u16 offset, u32 set) { bcma_write32(cc, offset, bcma_read32(cc, offset) | set); } static inline void bcma_maskset32(struct bcma_device *cc, u16 offset, u32 mask, u32 set) { bcma_write32(cc, offset, (bcma_read32(cc, offset) & mask) | set); } static inline void bcma_mask16(struct bcma_device *cc, u16 offset, u16 mask) { bcma_write16(cc, offset, bcma_read16(cc, offset) & mask); } static inline void bcma_set16(struct bcma_device *cc, u16 offset, u16 set) { bcma_write16(cc, offset, bcma_read16(cc, offset) | set); } static inline void bcma_maskset16(struct bcma_device *cc, u16 offset, u16 mask, u16 set) { bcma_write16(cc, offset, (bcma_read16(cc, offset) & mask) | set); } extern struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid); extern bool bcma_core_is_enabled(struct bcma_device *core); extern void bcma_core_disable(struct bcma_device *core, u32 flags); extern int bcma_core_enable(struct bcma_device *core, u32 flags); extern void bcma_core_set_clockmode(struct bcma_device *core, enum bcma_clkmode clkmode); extern void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on); #define BCMA_DMA_TRANSLATION_MASK 0xC0000000 #define BCMA_DMA_TRANSLATION_NONE 0x00000000 #define BCMA_DMA_TRANSLATION_DMA32_CMT 0x40000000 /* Client Mode Translation for 32-bit DMA */ #define BCMA_DMA_TRANSLATION_DMA64_CMT 0x80000000 /* Client Mode Translation for 64-bit DMA */ extern u32 bcma_core_dma_translation(struct bcma_device *core); #endif /* LINUX_BCMA_H_ */ compat-drivers-2012-09-18/include/linux/bcma/bcma_driver_gmac_cmn.h0000644000175000017500000000741512006264414024333 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_DRIVER_GMAC_CMN_H_ #define LINUX_BCMA_DRIVER_GMAC_CMN_H_ #include #define BCMA_GMAC_CMN_STAG0 0x000 #define BCMA_GMAC_CMN_STAG1 0x004 #define BCMA_GMAC_CMN_STAG2 0x008 #define BCMA_GMAC_CMN_STAG3 0x00C #define BCMA_GMAC_CMN_PARSER_CTL 0x020 #define BCMA_GMAC_CMN_MIB_MAX_LEN 0x024 #define BCMA_GMAC_CMN_PHY_ACCESS 0x100 #define BCMA_GMAC_CMN_PA_DATA_MASK 0x0000ffff #define BCMA_GMAC_CMN_PA_ADDR_MASK 0x001f0000 #define BCMA_GMAC_CMN_PA_ADDR_SHIFT 16 #define BCMA_GMAC_CMN_PA_REG_MASK 0x1f000000 #define BCMA_GMAC_CMN_PA_REG_SHIFT 24 #define BCMA_GMAC_CMN_PA_WRITE 0x20000000 #define BCMA_GMAC_CMN_PA_START 0x40000000 #define BCMA_GMAC_CMN_PHY_CTL 0x104 #define BCMA_GMAC_CMN_PC_EPA_MASK 0x0000001f #define BCMA_GMAC_CMN_PC_MCT_MASK 0x007f0000 #define BCMA_GMAC_CMN_PC_MCT_SHIFT 16 #define BCMA_GMAC_CMN_PC_MTE 0x00800000 #define BCMA_GMAC_CMN_GMAC0_RGMII_CTL 0x110 #define BCMA_GMAC_CMN_CFP_ACCESS 0x200 #define BCMA_GMAC_CMN_CFP_TCAM_DATA0 0x210 #define BCMA_GMAC_CMN_CFP_TCAM_DATA1 0x214 #define BCMA_GMAC_CMN_CFP_TCAM_DATA2 0x218 #define BCMA_GMAC_CMN_CFP_TCAM_DATA3 0x21C #define BCMA_GMAC_CMN_CFP_TCAM_DATA4 0x220 #define BCMA_GMAC_CMN_CFP_TCAM_DATA5 0x224 #define BCMA_GMAC_CMN_CFP_TCAM_DATA6 0x228 #define BCMA_GMAC_CMN_CFP_TCAM_DATA7 0x22C #define BCMA_GMAC_CMN_CFP_TCAM_MASK0 0x230 #define BCMA_GMAC_CMN_CFP_TCAM_MASK1 0x234 #define BCMA_GMAC_CMN_CFP_TCAM_MASK2 0x238 #define BCMA_GMAC_CMN_CFP_TCAM_MASK3 0x23C #define BCMA_GMAC_CMN_CFP_TCAM_MASK4 0x240 #define BCMA_GMAC_CMN_CFP_TCAM_MASK5 0x244 #define BCMA_GMAC_CMN_CFP_TCAM_MASK6 0x248 #define BCMA_GMAC_CMN_CFP_TCAM_MASK7 0x24C #define BCMA_GMAC_CMN_CFP_ACTION_DATA 0x250 #define BCMA_GMAC_CMN_TCAM_BIST_CTL 0x2A0 #define BCMA_GMAC_CMN_TCAM_BIST_STATUS 0x2A4 #define BCMA_GMAC_CMN_TCAM_CMP_STATUS 0x2A8 #define BCMA_GMAC_CMN_TCAM_DISABLE 0x2AC #define BCMA_GMAC_CMN_TCAM_TEST_CTL 0x2F0 #define BCMA_GMAC_CMN_UDF_0_A3_A0 0x300 #define BCMA_GMAC_CMN_UDF_0_A7_A4 0x304 #define BCMA_GMAC_CMN_UDF_0_A8 0x308 #define BCMA_GMAC_CMN_UDF_1_A3_A0 0x310 #define BCMA_GMAC_CMN_UDF_1_A7_A4 0x314 #define BCMA_GMAC_CMN_UDF_1_A8 0x318 #define BCMA_GMAC_CMN_UDF_2_A3_A0 0x320 #define BCMA_GMAC_CMN_UDF_2_A7_A4 0x324 #define BCMA_GMAC_CMN_UDF_2_A8 0x328 #define BCMA_GMAC_CMN_UDF_0_B3_B0 0x330 #define BCMA_GMAC_CMN_UDF_0_B7_B4 0x334 #define BCMA_GMAC_CMN_UDF_0_B8 0x338 #define BCMA_GMAC_CMN_UDF_1_B3_B0 0x340 #define BCMA_GMAC_CMN_UDF_1_B7_B4 0x344 #define BCMA_GMAC_CMN_UDF_1_B8 0x348 #define BCMA_GMAC_CMN_UDF_2_B3_B0 0x350 #define BCMA_GMAC_CMN_UDF_2_B7_B4 0x354 #define BCMA_GMAC_CMN_UDF_2_B8 0x358 #define BCMA_GMAC_CMN_UDF_0_C3_C0 0x360 #define BCMA_GMAC_CMN_UDF_0_C7_C4 0x364 #define BCMA_GMAC_CMN_UDF_0_C8 0x368 #define BCMA_GMAC_CMN_UDF_1_C3_C0 0x370 #define BCMA_GMAC_CMN_UDF_1_C7_C4 0x374 #define BCMA_GMAC_CMN_UDF_1_C8 0x378 #define BCMA_GMAC_CMN_UDF_2_C3_C0 0x380 #define BCMA_GMAC_CMN_UDF_2_C7_C4 0x384 #define BCMA_GMAC_CMN_UDF_2_C8 0x388 #define BCMA_GMAC_CMN_UDF_0_D3_D0 0x390 #define BCMA_GMAC_CMN_UDF_0_D7_D4 0x394 #define BCMA_GMAC_CMN_UDF_0_D11_D8 0x394 struct bcma_drv_gmac_cmn { struct bcma_device *core; /* Drivers accessing BCMA_GMAC_CMN_PHY_ACCESS and * BCMA_GMAC_CMN_PHY_CTL need to take that mutex first. */ struct mutex phy_mutex; }; /* Register access */ #define gmac_cmn_read16(gc, offset) bcma_read16((gc)->core, offset) #define gmac_cmn_read32(gc, offset) bcma_read32((gc)->core, offset) #define gmac_cmn_write16(gc, offset, val) bcma_write16((gc)->core, offset, val) #define gmac_cmn_write32(gc, offset, val) bcma_write32((gc)->core, offset, val) #ifdef CONFIG_BCMA_DRIVER_GMAC_CMN extern void __devinit bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc); #else static inline void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc) { } #endif #endif /* LINUX_BCMA_DRIVER_GMAC_CMN_H_ */ compat-drivers-2012-09-18/include/linux/bcma/bcma_soc.h0000644000175000017500000000050212006264414021766 0ustar mcgrofmcgrof#ifndef LINUX_BCMA_SOC_H_ #define LINUX_BCMA_SOC_H_ #include struct bcma_soc { struct bcma_bus bus; struct bcma_device core_cc; struct bcma_device core_mips; }; int __init bcma_host_soc_register(struct bcma_soc *soc); int bcma_bus_register(struct bcma_bus *bus); #endif /* LINUX_BCMA_SOC_H_ */ compat-drivers-2012-09-18/include/linux/compat-2.6.31.h0000644000175000017500000001441512025673354021346 0ustar mcgrofmcgrof#ifndef LINUX_26_31_COMPAT_H #define LINUX_26_31_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) #include #include #include #include #include #include #include /* * These macros allow us to backport rfkill without any * changes on cfg80211 through compat.diff. Note that this * file will be included by rfkill_backport.h so we must * not conflict with things there. */ #define rfkill_get_led_trigger_name backport_rfkill_get_led_trigger_name #define rfkill_set_led_trigger_name backport_rfkill_set_led_trigger_name #define rfkill_set_hw_state backport_rfkill_set_hw_state #define rfkill_set_sw_state backport_rfkill_set_sw_state #define rfkill_init_sw_state backport_rfkill_init_sw_state #define rfkill_set_states backport_rfkill_set_states #define rfkill_pause_polling backport_rfkill_pause_polling #define rfkill_resume_polling backport_rfkill_resume_polling #define rfkill_blocked backport_rfkill_blocked #define rfkill_alloc backport_rfkill_alloc #define rfkill_register backport_rfkill_register #define rfkill_unregister backport_rfkill_unregister #define rfkill_destroy backport_rfkill_destroy #ifndef ERFKILL #if !defined(CONFIG_ALPHA) && !defined(CONFIG_MIPS) && !defined(CONFIG_PARISC) && !defined(CONFIG_SPARC) #define ERFKILL 132 /* Operation not possible due to RF-kill */ #endif #ifdef CONFIG_ALPHA #define ERFKILL 138 /* Operation not possible due to RF-kill */ #endif #ifdef CONFIG_MIPS #define ERFKILL 167 /* Operation not possible due to RF-kill */ #endif #ifdef CONFIG_PARISC #define ERFKILL 256 /* Operation not possible due to RF-kill */ #endif #ifdef CONFIG_SPARC #define ERFKILL 134 /* Operation not possible due to RF-kill */ #endif #endif #ifndef NETDEV_PRE_UP #define NETDEV_PRE_UP 0x000D #endif #ifndef SDIO_DEVICE_ID_MARVELL_8688WLAN #define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104 #endif struct compat_threaded_irq { unsigned int irq; irq_handler_t handler; irq_handler_t thread_fn; void *dev_id; char wq_name[64]; struct workqueue_struct *wq; struct work_struct work; }; /* * kmemleak was introduced on 2.6.31, since older kernels do not have * we simply ignore its tuning. */ static inline void kmemleak_ignore(const void *ptr) { return; } static inline void kmemleak_not_leak(const void *ptr) { return; } static inline void kmemleak_no_scan(const void *ptr) { return; } /* * Added via adf30907d63893e4208dfe3f5c88ae12bc2f25d5 * * There is no _sk_dst on older kernels, so just set the * old dst to NULL and release it directly. */ static inline void skb_dst_drop(struct sk_buff *skb) { dst_release(skb->dst); skb->dst = NULL; } static inline struct dst_entry *skb_dst(const struct sk_buff *skb) { return (struct dst_entry *)skb->dst; } static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) { skb->dst = dst; } static inline struct rtable *skb_rtable(const struct sk_buff *skb) { return (struct rtable *)skb_dst(skb); } /* Backport threaded IRQ support */ static inline void compat_irq_work(struct work_struct *work) { struct compat_threaded_irq *comp = container_of(work, struct compat_threaded_irq, work); comp->thread_fn(comp->irq, comp->dev_id); } static inline irqreturn_t compat_irq_dispatcher(int irq, void *dev_id) { struct compat_threaded_irq *comp = dev_id; irqreturn_t res; res = comp->handler(irq, comp->dev_id); if (res == IRQ_WAKE_THREAD) { queue_work(comp->wq, &comp->work); res = IRQ_HANDLED; } return res; } static inline int compat_request_threaded_irq(struct compat_threaded_irq *comp, unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags, const char *name, void *dev_id) { comp->irq = irq; comp->handler = handler; comp->thread_fn = thread_fn; comp->dev_id = dev_id; INIT_WORK(&comp->work, compat_irq_work); if (!comp->wq) { snprintf(comp->wq_name, sizeof(comp->wq_name), "compirq/%u-%s", irq, name); comp->wq = create_singlethread_workqueue(comp->wq_name); if (!comp->wq) { printk(KERN_ERR "Failed to create compat-threaded-IRQ workqueue %s\n", comp->wq_name); return -ENOMEM; } } return request_irq(irq, compat_irq_dispatcher, flags, name, comp); } static inline void compat_free_threaded_irq(struct compat_threaded_irq *comp) { free_irq(comp->irq, comp); } static inline void compat_destroy_threaded_irq(struct compat_threaded_irq *comp) { if (comp->wq) destroy_workqueue(comp->wq); comp->wq = NULL; } static inline void compat_synchronize_threaded_irq(struct compat_threaded_irq *comp) { synchronize_irq(comp->irq); cancel_work_sync(&comp->work); } /** * list_entry_rcu - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. * * This primitive may safely run concurrently with the _rcu list-mutation * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock(). */ #define list_entry_rcu(ptr, type, member) \ container_of(rcu_dereference(ptr), type, member) #define skb_walk_frags(skb, iter) \ for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next) #ifndef CONFIG_64BIT typedef struct { long long counter; } atomic64_t; extern long long atomic64_read(const atomic64_t *v); extern long long atomic64_add_return(long long a, atomic64_t *v); #define atomic64_inc_return(v) atomic64_add_return(1LL, (v)) #endif /** * sk_rmem_alloc_get - returns read allocations * @sk: socket * * Returns sk_rmem_alloc */ static inline int sk_rmem_alloc_get(const struct sock *sk) { return atomic_read(&sk->sk_rmem_alloc); } /** * sk_wmem_alloc_get - returns write allocations * @sk: socket * * Returns sk_wmem_alloc minus initial offset of one */ static inline int sk_wmem_alloc_get(const struct sock *sk) { return atomic_read(&sk->sk_wmem_alloc) - 1; } /** * sk_has_allocations - check if allocations are outstanding * @sk: socket * * Returns true if socket has write or read allocations */ static inline bool sk_has_allocations(const struct sock *sk) { return sk_wmem_alloc_get(sk) || sk_rmem_alloc_get(sk); } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) */ #endif /* LINUX_26_31_COMPAT_H */ compat-drivers-2012-09-18/include/linux/wl12xx.h0000644000175000017500000000425412026211315020465 0ustar mcgrofmcgrof/* * This file is part of wl12xx * * Copyright (C) 2009 Nokia Corporation * * Contact: Luciano Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef _LINUX_WL12XX_H #define _LINUX_WL12XX_H /* Reference clock values */ enum { WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */ WL12XX_REFCLOCK_52 = 3, /* 52 MHz */ WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */ WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */ }; /* TCXO clock values */ enum { WL12XX_TCXOCLOCK_19_2 = 0, /* 19.2MHz */ WL12XX_TCXOCLOCK_26 = 1, /* 26 MHz */ WL12XX_TCXOCLOCK_38_4 = 2, /* 38.4MHz */ WL12XX_TCXOCLOCK_52 = 3, /* 52 MHz */ WL12XX_TCXOCLOCK_16_368 = 4, /* 16.368 MHz */ WL12XX_TCXOCLOCK_32_736 = 5, /* 32.736 MHz */ WL12XX_TCXOCLOCK_16_8 = 6, /* 16.8 MHz */ WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */ }; struct wl12xx_platform_data { void (*set_power)(bool enable); /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ int irq; bool use_eeprom; int board_ref_clock; int board_tcxo_clock; unsigned long platform_quirks; bool pwr_in_suspend; struct wl1271_if_operations *ops; }; /* Platform does not support level trigger interrupts */ #define WL12XX_PLATFORM_QUIRK_EDGE_IRQ BIT(0) #ifdef CONFIG_WL12XX_PLATFORM_DATA int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); #else static inline int wl12xx_set_platform_data(const struct wl12xx_platform_data *data) { return -ENOSYS; } #endif struct wl12xx_platform_data *wl12xx_get_platform_data(void); #endif compat-drivers-2012-09-18/include/linux/export.h0000644000175000017500000000045612025673354020657 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_EXPORT_H #define _COMPAT_LINUX_EXPORT_H 1 #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) #include_next #else #include #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) */ #endif /* _COMPAT_LINUX_EXPORT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.20.h0000644000175000017500000000103512025673354021336 0ustar mcgrofmcgrof#ifndef LINUX_26_20_COMPAT_H #define LINUX_26_20_COMPAT_H #include /* Compat work for 2.6.20 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) #include typedef void (*work_func_t)(struct work_struct *work); typedef void (*compat_work_func_t)(void *work); static inline void (INIT_WORK)(struct work_struct *work, work_func_t func) { INIT_WORK(work, (compat_work_func_t)func, work); } #undef INIT_WORK #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) */ #endif /* LINUX_26_20_COMPAT_H */ compat-drivers-2012-09-18/include/linux/vga_switcheroo.h0000644000175000017500000000076312025673354022362 0ustar mcgrofmcgrof#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* * XXX: The include guard was sent upstream, drop this * once the guard is merged. */ #ifndef LINUX_VGA_SWITCHEROO_H /* in case this gets upstream */ #include_next #ifndef LINUX_VGA_SWITCHEROO_H /* do not redefine once this gets upstream */ #define LINUX_VGA_SWITCHEROO_H #endif /* case 1 LINUX_VGA_SWITCHEROO_H */ #endif /* case 2 LINUX_VGA_SWITCHEROO_H */ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ compat-drivers-2012-09-18/include/linux/rfkill.h0000644000175000017500000000077112025673354020621 0ustar mcgrofmcgrof#ifndef __COMPAT_RFKILL_H #define __COMPAT_RFKILL_H #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) #include_next #else #include #undef CONFIG_RFKILL #undef CONFIG_RFKILL_INPUT #undef CONFIG_RFKILL_LEDS #ifdef CONFIG_RFKILL_BACKPORT #define CONFIG_RFKILL 1 #endif #ifdef CONFIG_RFKILL_BACKPORT_INPUT #define CONFIG_RFKILL_INPUT #endif #ifdef CONFIG_RFKILL_BACKPORT_LEDS #define CONFIG_RFKILL_LEDS #endif #include #endif #endif compat-drivers-2012-09-18/include/linux/printk.h0000644000175000017500000000045612025673354020645 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_PRINTK_H #define _COMPAT_LINUX_PRINTK_H 1 #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) #include_next #else #include #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) */ #endif /* _COMPAT_LINUX_PRINTK_H */ compat-drivers-2012-09-18/include/linux/wireless.h0000644000175000017500000012537612025673354021204 0ustar mcgrofmcgrof/* * This file define a set of standard wireless extensions * * Version : 22 16.3.07 * * Authors : Jean Tourrilhes - HPL - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. */ #ifndef _LINUX_WIRELESS_H #define _LINUX_WIRELESS_H /************************** DOCUMENTATION **************************/ /* * Initial APIs (1996 -> onward) : * ----------------------------- * Basically, the wireless extensions are for now a set of standard ioctl * call + /proc/net/wireless * * The entry /proc/net/wireless give statistics and information on the * driver. * This is better than having each driver having its entry because * its centralised and we may remove the driver module safely. * * Ioctl are used to configure the driver and issue commands. This is * better than command line options of insmod because we may want to * change dynamically (while the driver is running) some parameters. * * The ioctl mechanimsm are copied from standard devices ioctl. * We have the list of command plus a structure descibing the * data exchanged... * Note that to add these ioctl, I was obliged to modify : * # net/core/dev.c (two place + add include) * # net/ipv4/af_inet.c (one place + add include) * * /proc/net/wireless is a copy of /proc/net/dev. * We have a structure for data passed from the driver to /proc/net/wireless * Too add this, I've modified : * # net/core/dev.c (two other places) * # include/linux/netdevice.h (one place) * # include/linux/proc_fs.h (one place) * * New driver API (2002 -> onward) : * ------------------------------- * This file is only concerned with the user space API and common definitions. * The new driver API is defined and documented in : * # include/net/iw_handler.h * * Note as well that /proc/net/wireless implementation has now moved in : * # net/core/wireless.c * * Wireless Events (2002 -> onward) : * -------------------------------- * Events are defined at the end of this file, and implemented in : * # net/core/wireless.c * * Other comments : * -------------- * Do not add here things that are redundant with other mechanisms * (drivers init, ifconfig, /proc/net/dev, ...) and with are not * wireless specific. * * These wireless extensions are not magic : each driver has to provide * support for them... * * IMPORTANT NOTE : As everything in the kernel, this is very much a * work in progress. Contact me if you have ideas of improvements... */ /***************************** INCLUDES *****************************/ #include /* for __u* and __s* typedefs */ #include /* for "struct sockaddr" et al */ #include /* for IFNAMSIZ and co... */ /***************************** VERSION *****************************/ /* * This constant is used to know the availability of the wireless * extensions and to know which version of wireless extensions it is * (there is some stuff that will be added in the future...) * I just plan to increment with each new version. */ #define WIRELESS_EXT 22 /* * Changes : * * V2 to V3 * -------- * Alan Cox start some incompatibles changes. I've integrated a bit more. * - Encryption renamed to Encode to avoid US regulation problems * - Frequency changed from float to struct to avoid problems on old 386 * * V3 to V4 * -------- * - Add sensitivity * * V4 to V5 * -------- * - Missing encoding definitions in range * - Access points stuff * * V5 to V6 * -------- * - 802.11 support (ESSID ioctls) * * V6 to V7 * -------- * - define IW_ESSID_MAX_SIZE and IW_MAX_AP * * V7 to V8 * -------- * - Changed my e-mail address * - More 802.11 support (nickname, rate, rts, frag) * - List index in frequencies * * V8 to V9 * -------- * - Support for 'mode of operation' (ad-hoc, managed...) * - Support for unicast and multicast power saving * - Change encoding to support larger tokens (>64 bits) * - Updated iw_params (disable, flags) and use it for NWID * - Extracted iw_point from iwreq for clarity * * V9 to V10 * --------- * - Add PM capability to range structure * - Add PM modifier : MAX/MIN/RELATIVE * - Add encoding option : IW_ENCODE_NOKEY * - Add TxPower ioctls (work like TxRate) * * V10 to V11 * ---------- * - Add WE version in range (help backward/forward compatibility) * - Add retry ioctls (work like PM) * * V11 to V12 * ---------- * - Add SIOCSIWSTATS to get /proc/net/wireless programatically * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space * - Add new statistics (frag, retry, beacon) * - Add average quality (for user space calibration) * * V12 to V13 * ---------- * - Document creation of new driver API. * - Extract union iwreq_data from struct iwreq (for new driver API). * - Rename SIOCSIWNAME as SIOCSIWCOMMIT * * V13 to V14 * ---------- * - Wireless Events support : define struct iw_event * - Define additional specific event numbers * - Add "addr" and "param" fields in union iwreq_data * - AP scanning stuff (SIOCSIWSCAN and friends) * * V14 to V15 * ---------- * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg * - Make struct iw_freq signed (both m & e), add explicit padding * - Add IWEVCUSTOM for driver specific event/scanning token * - Add IW_MAX_GET_SPY for driver returning a lot of addresses * - Add IW_TXPOW_RANGE for range of Tx Powers * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points * - Add IW_MODE_MONITOR for passive monitor * * V15 to V16 * ---------- * - Increase the number of bitrates in iw_range to 32 (for 802.11g) * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) * - Reshuffle struct iw_range for increases, add filler * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index * * V16 to V17 * ---------- * - Add flags to frequency -> auto/fixed * - Document (struct iw_quality *)->updated, add new flags (INVALID) * - Wireless Event capability in struct iw_range * - Add support for relative TxPower (yick !) * * V17 to V18 (From Jouni Malinen ) * ---------- * - Add support for WPA/WPA2 * - Add extended encoding configuration (SIOCSIWENCODEEXT and * SIOCGIWENCODEEXT) * - Add SIOCSIWGENIE/SIOCGIWGENIE * - Add SIOCSIWMLME * - Add SIOCSIWPMKSA * - Add struct iw_range bit field for supported encoding capabilities * - Add optional scan request parameters for SIOCSIWSCAN * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA * related parameters (extensible up to 4096 parameter values) * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND * * V18 to V19 * ---------- * - Remove (struct iw_point *)->pointer from events and streams * - Remove header includes to help user space * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros * * V19 to V20 * ---------- * - RtNetlink requests support (SET/GET) * * V20 to V21 * ---------- * - Remove (struct net_device *)->get_wireless_stats() * - Change length in ESSID and NICK to strlen() instead of strlen()+1 * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers * - Power/Retry relative values no longer * 100000 * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI * * V21 to V22 * ---------- * - Prevent leaking of kernel space in stream on 64 bits. */ /**************************** CONSTANTS ****************************/ /* -------------------------- IOCTL LIST -------------------------- */ /* Wireless Identification */ #define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ #define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ /* SIOCGIWNAME is used to verify the presence of Wireless Extensions. * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... * Don't put the name of your driver there, it's useless. */ /* Basic operations */ #define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ #define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ #define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ #define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ #define SIOCSIWMODE 0x8B06 /* set operation mode */ #define SIOCGIWMODE 0x8B07 /* get operation mode */ #define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ #define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ /* Informative stuff */ #define SIOCSIWRANGE 0x8B0A /* Unused */ #define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ #define SIOCSIWPRIV 0x8B0C /* Unused */ #define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ #define SIOCSIWSTATS 0x8B0E /* Unused */ #define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ /* SIOCGIWSTATS is strictly used between user space and the kernel, and * is never passed to the driver (i.e. the driver will never see it). */ /* Spy support (statistics per MAC address - used for Mobile IP support) */ #define SIOCSIWSPY 0x8B10 /* set spy addresses */ #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ #define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ #define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ /* Access Point manipulation */ #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ #define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ #define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ #define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ #define SIOCGIWSCAN 0x8B19 /* get scanning results */ /* 802.11 specific support */ #define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ #define SIOCGIWESSID 0x8B1B /* get ESSID */ #define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ #define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ /* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit * within the 'iwreq' structure, so we need to use the 'data' member to * point to a string in user space, like it is done for RANGE... */ /* Other parameters useful in 802.11 and some other devices */ #define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ #define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ #define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ #define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ #define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ #define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ #define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ #define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ #define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ #define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ /* Encoding stuff (scrambling, hardware security, WEP...) */ #define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ #define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ /* Power saving stuff (power management, unicast and multicast) */ #define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ #define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ /* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). * This ioctl uses struct iw_point and data buffer that includes IE id and len * fields. More than one IE may be included in the request. Setting the generic * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers * are required to report the used IE as a wireless event, e.g., when * associating with an AP. */ #define SIOCSIWGENIE 0x8B30 /* set generic IE */ #define SIOCGIWGENIE 0x8B31 /* get generic IE */ /* WPA : IEEE 802.11 MLME requests */ #define SIOCSIWMLME 0x8B16 /* request MLME operation; uses * struct iw_mlme */ /* WPA : Authentication mode parameters */ #define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ #define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ /* WPA : Extended version of encoding configuration */ #define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ #define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ /* WPA2 : PMKSA cache management */ #define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ /* These 32 ioctl are wireless device private, for 16 commands. * Each driver is free to use them for whatever purpose it chooses, * however the driver *must* export the description of those ioctls * with SIOCGIWPRIV and *must* use arguments as defined below. * If you don't follow those rules, DaveM is going to hate you (reason : * it make mixed 32/64bit operation impossible). */ #define SIOCIWFIRSTPRIV 0x8BE0 #define SIOCIWLASTPRIV 0x8BFF /* Previously, we were using SIOCDEVPRIVATE, but we now have our * separate range because of collisions with other tools such as * 'mii-tool'. * We now have 32 commands, so a bit more space ;-). * Also, all 'even' commands are only usable by root and don't return the * content of ifr/iwr to user (but you are not obliged to use the set/get * convention, just use every other two command). More details in iwpriv.c. * And I repeat : you are not forced to use them with iwpriv, but you * must be compliant with it. */ /* ------------------------- IOCTL STUFF ------------------------- */ /* The first and the last (range) */ #define SIOCIWFIRST 0x8B00 #define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) #define IW_HANDLER(id, func) \ [IW_IOCTL_IDX(id)] = func /* Odd : get (world access), even : set (root access) */ #define IW_IS_SET(cmd) (!((cmd) & 0x1)) #define IW_IS_GET(cmd) ((cmd) & 0x1) /* ----------------------- WIRELESS EVENTS ----------------------- */ /* Those are *NOT* ioctls, do not issue request on them !!! */ /* Most events use the same identifier as ioctl requests */ #define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ #define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ #define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ #define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ #define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ #define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) * (scan results); This includes id and * length fields. One IWEVGENIE may * contain more than one IE. Scan * results may contain one or more * IWEVGENIE events. */ #define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure * (struct iw_michaelmicfailure) */ #define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. * The data includes id and length * fields and may contain more than one * IE. This event is required in * Managed mode if the driver * generates its own WPA/RSN IE. This * should be sent just before * IWEVREGISTERED event for the * association. */ #define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association * Response. The data includes id and * length fields and may contain more * than one IE. This may be sent * between IWEVASSOCREQIE and * IWEVREGISTERED events for the * association. */ #define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN * pre-authentication * (struct iw_pmkid_cand) */ #define IWEVFIRST 0x8C00 #define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) /* ------------------------- PRIVATE INFO ------------------------- */ /* * The following is used with SIOCGIWPRIV. It allow a driver to define * the interface (name, type of data) for its private ioctl. * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV */ #define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ #define IW_PRIV_TYPE_NONE 0x0000 #define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ #define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ #define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ #define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ #define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ #define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ #define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ /* * Note : if the number of args is fixed and the size < 16 octets, * instead of passing a pointer we will put args in the iwreq struct... */ /* ----------------------- OTHER CONSTANTS ----------------------- */ /* Maximum frequencies in the range struct */ #define IW_MAX_FREQUENCIES 32 /* Note : if you have something like 80 frequencies, * don't increase this constant and don't fill the frequency list. * The user will be able to set by channel anyway... */ /* Maximum bit rates in the range struct */ #define IW_MAX_BITRATES 32 /* Maximum tx powers in the range struct */ #define IW_MAX_TXPOWER 8 /* Note : if you more than 8 TXPowers, just set the max and min or * a few of them in the struct iw_range. */ /* Maximum of address that you may set with SPY */ #define IW_MAX_SPY 8 /* Maximum of address that you may get in the list of access points in range */ #define IW_MAX_AP 64 /* Maximum size of the ESSID and NICKN strings */ #define IW_ESSID_MAX_SIZE 32 /* Modes of operation */ #define IW_MODE_AUTO 0 /* Let the driver decides */ #define IW_MODE_ADHOC 1 /* Single cell network */ #define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ #define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ #define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ #define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ #define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */ /* Statistics flags (bitmask in updated) */ #define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ #define IW_QUAL_LEVEL_UPDATED 0x02 #define IW_QUAL_NOISE_UPDATED 0x04 #define IW_QUAL_ALL_UPDATED 0x07 #define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ #define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ #define IW_QUAL_LEVEL_INVALID 0x20 #define IW_QUAL_NOISE_INVALID 0x40 #define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */ #define IW_QUAL_ALL_INVALID 0x70 /* Frequency flags */ #define IW_FREQ_AUTO 0x00 /* Let the driver decides */ #define IW_FREQ_FIXED 0x01 /* Force a specific value */ /* Maximum number of size of encoding token available * they are listed in the range structure */ #define IW_MAX_ENCODING_SIZES 8 /* Maximum size of the encoding token in bytes */ #define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ /* Flags for encoding (along with the token) */ #define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ #define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ #define IW_ENCODE_MODE 0xF000 /* Modes defined below */ #define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ #define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ #define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ #define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ #define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ #define IW_ENCODE_TEMP 0x0400 /* Temporary key */ /* Power management flags available (along with the value, if any) */ #define IW_POWER_ON 0x0000 /* No details... */ #define IW_POWER_TYPE 0xF000 /* Type of parameter */ #define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ #define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ #define IW_POWER_MODE 0x0F00 /* Power Management mode */ #define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ #define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ #define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ #define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ #define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ #define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ #define IW_POWER_MIN 0x0001 /* Value is a minimum */ #define IW_POWER_MAX 0x0002 /* Value is a maximum */ #define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ /* Transmit Power flags available */ #define IW_TXPOW_TYPE 0x00FF /* Type of value */ #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ #define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ #define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ /* Retry limits and lifetime flags available */ #define IW_RETRY_ON 0x0000 /* No details... */ #define IW_RETRY_TYPE 0xF000 /* Type of parameter */ #define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ #define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ #define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */ #define IW_RETRY_MIN 0x0001 /* Value is a minimum */ #define IW_RETRY_MAX 0x0002 /* Value is a maximum */ #define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ #define IW_RETRY_SHORT 0x0010 /* Value is for short packets */ #define IW_RETRY_LONG 0x0020 /* Value is for long packets */ /* Scanning request flags */ #define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ #define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ #define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ #define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ #define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ #define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ #define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ #define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ #define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ /* struct iw_scan_req scan_type */ #define IW_SCAN_TYPE_ACTIVE 0 #define IW_SCAN_TYPE_PASSIVE 1 /* Maximum size of returned data */ #define IW_SCAN_MAX_DATA 4096 /* In bytes */ /* Scan capability flags - in (struct iw_range *)->scan_capa */ #define IW_SCAN_CAPA_NONE 0x00 #define IW_SCAN_CAPA_ESSID 0x01 #define IW_SCAN_CAPA_BSSID 0x02 #define IW_SCAN_CAPA_CHANNEL 0x04 #define IW_SCAN_CAPA_MODE 0x08 #define IW_SCAN_CAPA_RATE 0x10 #define IW_SCAN_CAPA_TYPE 0x20 #define IW_SCAN_CAPA_TIME 0x40 /* Max number of char in custom event - use multiple of them if needed */ #define IW_CUSTOM_MAX 256 /* In bytes */ /* Generic information element */ #define IW_GENERIC_IE_MAX 1024 /* MLME requests (SIOCSIWMLME / struct iw_mlme) */ #define IW_MLME_DEAUTH 0 #define IW_MLME_DISASSOC 1 #define IW_MLME_AUTH 2 #define IW_MLME_ASSOC 3 /* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ #define IW_AUTH_INDEX 0x0FFF #define IW_AUTH_FLAGS 0xF000 /* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the * parameter that is being set/get to; value will be read/written to * struct iw_param value field) */ #define IW_AUTH_WPA_VERSION 0 #define IW_AUTH_CIPHER_PAIRWISE 1 #define IW_AUTH_CIPHER_GROUP 2 #define IW_AUTH_KEY_MGMT 3 #define IW_AUTH_TKIP_COUNTERMEASURES 4 #define IW_AUTH_DROP_UNENCRYPTED 5 #define IW_AUTH_80211_AUTH_ALG 6 #define IW_AUTH_WPA_ENABLED 7 #define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 #define IW_AUTH_ROAMING_CONTROL 9 #define IW_AUTH_PRIVACY_INVOKED 10 #define IW_AUTH_CIPHER_GROUP_MGMT 11 #define IW_AUTH_MFP 12 /* IW_AUTH_WPA_VERSION values (bit field) */ #define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 #define IW_AUTH_WPA_VERSION_WPA 0x00000002 #define IW_AUTH_WPA_VERSION_WPA2 0x00000004 /* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT * values (bit field) */ #define IW_AUTH_CIPHER_NONE 0x00000001 #define IW_AUTH_CIPHER_WEP40 0x00000002 #define IW_AUTH_CIPHER_TKIP 0x00000004 #define IW_AUTH_CIPHER_CCMP 0x00000008 #define IW_AUTH_CIPHER_WEP104 0x00000010 #define IW_AUTH_CIPHER_AES_CMAC 0x00000020 /* IW_AUTH_KEY_MGMT values (bit field) */ #define IW_AUTH_KEY_MGMT_802_1X 1 #define IW_AUTH_KEY_MGMT_PSK 2 /* IW_AUTH_80211_AUTH_ALG values (bit field) */ #define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 #define IW_AUTH_ALG_SHARED_KEY 0x00000002 #define IW_AUTH_ALG_LEAP 0x00000004 /* IW_AUTH_ROAMING_CONTROL values */ #define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ #define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming * control */ /* IW_AUTH_MFP (management frame protection) values */ #define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ #define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ #define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ /* SIOCSIWENCODEEXT definitions */ #define IW_ENCODE_SEQ_MAX_SIZE 8 /* struct iw_encode_ext ->alg */ #define IW_ENCODE_ALG_NONE 0 #define IW_ENCODE_ALG_WEP 1 #define IW_ENCODE_ALG_TKIP 2 #define IW_ENCODE_ALG_CCMP 3 #define IW_ENCODE_ALG_PMK 4 #define IW_ENCODE_ALG_AES_CMAC 5 /* struct iw_encode_ext ->ext_flags */ #define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 #define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 #define IW_ENCODE_EXT_GROUP_KEY 0x00000004 #define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 /* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ #define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ #define IW_MICFAILURE_GROUP 0x00000004 #define IW_MICFAILURE_PAIRWISE 0x00000008 #define IW_MICFAILURE_STAKEY 0x00000010 #define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) */ /* Bit field values for enc_capa in struct iw_range */ #define IW_ENC_CAPA_WPA 0x00000001 #define IW_ENC_CAPA_WPA2 0x00000002 #define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 #define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 #define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 /* Event capability macros - in (struct iw_range *)->event_capa * Because we have more than 32 possible events, we use an array of * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ #define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ (cmd - SIOCIWFIRSTPRIV + 0x60) : \ (cmd - SIOCIWFIRST)) #define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) #define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) /* Event capability constants - event autogenerated by the kernel * This list is valid for most 802.11 devices, customise as needed... */ #define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ IW_EVENT_CAPA_MASK(0x8B06) | \ IW_EVENT_CAPA_MASK(0x8B1A)) #define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) /* "Easy" macro to set events in iw_range (less efficient) */ #define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) #define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } /****************************** TYPES ******************************/ /* --------------------------- SUBTYPES --------------------------- */ /* * Generic format for most parameters that fit in an int */ struct iw_param { __s32 value; /* The value of the parameter itself */ __u8 fixed; /* Hardware should not use auto select */ __u8 disabled; /* Disable the feature */ __u16 flags; /* Various specifc flags (if any) */ }; /* * For all data larger than 16 octets, we need to use a * pointer to memory allocated in user space. */ struct iw_point { void __user *pointer; /* Pointer to the data (in user space) */ __u16 length; /* number of fields or size in bytes */ __u16 flags; /* Optional params */ }; #ifdef __KERNEL__ #ifdef CONFIG_COMPAT #include struct compat_iw_point { compat_caddr_t pointer; __u16 length; __u16 flags; }; #endif #endif /* * A frequency * For numbers lower than 10^9, we encode the number in 'm' and * set 'e' to 0 * For number greater than 10^9, we divide it by the lowest power * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... * The power of 10 is in 'e', the result of the division is in 'm'. */ struct iw_freq { __s32 m; /* Mantissa */ __s16 e; /* Exponent */ __u8 i; /* List index (when in range struct) */ __u8 flags; /* Flags (fixed/auto) */ }; /* * Quality of the link */ struct iw_quality { __u8 qual; /* link quality (%retries, SNR, %missed beacons or better...) */ __u8 level; /* signal level (dBm) */ __u8 noise; /* noise level (dBm) */ __u8 updated; /* Flags to know if updated */ }; /* * Packet discarded in the wireless adapter due to * "wireless" specific problems... * Note : the list of counter and statistics in net_device_stats * is already pretty exhaustive, and you should use that first. * This is only additional stats... */ struct iw_discarded { __u32 nwid; /* Rx : Wrong nwid/essid */ __u32 code; /* Rx : Unable to code/decode (WEP) */ __u32 fragment; /* Rx : Can't perform MAC reassembly */ __u32 retries; /* Tx : Max MAC retries num reached */ __u32 misc; /* Others cases */ }; /* * Packet/Time period missed in the wireless adapter due to * "wireless" specific problems... */ struct iw_missed { __u32 beacon; /* Missed beacons/superframe */ }; /* * Quality range (for spy threshold) */ struct iw_thrspy { struct sockaddr addr; /* Source address (hw/mac) */ struct iw_quality qual; /* Quality of the link */ struct iw_quality low; /* Low threshold */ struct iw_quality high; /* High threshold */ }; /* * Optional data for scan request * * Note: these optional parameters are controlling parameters for the * scanning behavior, these do not apply to getting scan results * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and * provide a merged results with all BSSes even if the previous scan * request limited scanning to a subset, e.g., by specifying an SSID. * Especially, scan results are required to include an entry for the * current BSS if the driver is in Managed mode and associated with an AP. */ struct iw_scan_req { __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ __u8 essid_len; __u8 num_channels; /* num entries in channel_list; * 0 = scan all allowed channels */ __u8 flags; /* reserved as padding; use zero, this may * be used in the future for adding flags * to request different scan behavior */ struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or * individual address of a specific BSS */ /* * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using * the current ESSID. This allows scan requests for specific ESSID * without having to change the current ESSID and potentially breaking * the current association. */ __u8 essid[IW_ESSID_MAX_SIZE]; /* * Optional parameters for changing the default scanning behavior. * These are based on the MLME-SCAN.request from IEEE Std 802.11. * TU is 1.024 ms. If these are set to 0, driver is expected to use * reasonable default values. min_channel_time defines the time that * will be used to wait for the first reply on each channel. If no * replies are received, next channel will be scanned after this. If * replies are received, total time waited on the channel is defined by * max_channel_time. */ __u32 min_channel_time; /* in TU */ __u32 max_channel_time; /* in TU */ struct iw_freq channel_list[IW_MAX_FREQUENCIES]; }; /* ------------------------- WPA SUPPORT ------------------------- */ /* * Extended data structure for get/set encoding (this is used with * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and * only the data contents changes (key data -> this structure, including * key data). * * If the new key is the first group key, it will be set as the default * TX key. Otherwise, default TX key index is only changed if * IW_ENCODE_EXT_SET_TX_KEY flag is set. * * Key will be changed with SIOCSIWENCODEEXT in all cases except for * special "change TX key index" operation which is indicated by setting * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. * * tx_seq/rx_seq are only used when respective * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally * used only by an Authenticator (AP or an IBSS station) to get the * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for * debugging/testing. */ struct iw_encode_ext { __u32 ext_flags; /* IW_ENCODE_EXT_* */ __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast * (group) keys or unicast address for * individual keys */ __u16 alg; /* IW_ENCODE_ALG_* */ __u16 key_len; __u8 key[0]; }; /* SIOCSIWMLME data */ struct iw_mlme { __u16 cmd; /* IW_MLME_* */ __u16 reason_code; struct sockaddr addr; }; /* SIOCSIWPMKSA data */ #define IW_PMKSA_ADD 1 #define IW_PMKSA_REMOVE 2 #define IW_PMKSA_FLUSH 3 #define IW_PMKID_LEN 16 struct iw_pmksa { __u32 cmd; /* IW_PMKSA_* */ struct sockaddr bssid; __u8 pmkid[IW_PMKID_LEN]; }; /* IWEVMICHAELMICFAILURE data */ struct iw_michaelmicfailure { __u32 flags; struct sockaddr src_addr; __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ }; /* IWEVPMKIDCAND data */ #define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ struct iw_pmkid_cand { __u32 flags; /* IW_PMKID_CAND_* */ __u32 index; /* the smaller the index, the higher the * priority */ struct sockaddr bssid; }; /* ------------------------ WIRELESS STATS ------------------------ */ /* * Wireless statistics (used for /proc/net/wireless) */ struct iw_statistics { __u16 status; /* Status * - device dependent for now */ struct iw_quality qual; /* Quality of the link * (instant/mean/max) */ struct iw_discarded discard; /* Packet discarded counts */ struct iw_missed miss; /* Packet missed counts */ }; /* ------------------------ IOCTL REQUEST ------------------------ */ /* * This structure defines the payload of an ioctl, and is used * below. * * Note that this structure should fit on the memory footprint * of iwreq (which is the same as ifreq), which mean a max size of * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... * You should check this when increasing the structures defined * above in this file... */ union iwreq_data { /* Config - generic */ char name[IFNAMSIZ]; /* Name : used to verify the presence of wireless extensions. * Name of the protocol/provider... */ struct iw_point essid; /* Extended network name */ struct iw_param nwid; /* network id (or domain - the cell) */ struct iw_freq freq; /* frequency or channel : * 0-1000 = channel * > 1000 = frequency in Hz */ struct iw_param sens; /* signal level threshold */ struct iw_param bitrate; /* default bit rate */ struct iw_param txpower; /* default transmit power */ struct iw_param rts; /* RTS threshold threshold */ struct iw_param frag; /* Fragmentation threshold */ __u32 mode; /* Operation mode */ struct iw_param retry; /* Retry limits & lifetime */ struct iw_point encoding; /* Encoding stuff : tokens */ struct iw_param power; /* PM duration/timeout */ struct iw_quality qual; /* Quality part of statistics */ struct sockaddr ap_addr; /* Access point address */ struct sockaddr addr; /* Destination address (hw/mac) */ struct iw_param param; /* Other small parameters */ struct iw_point data; /* Other large parameters */ }; /* * The structure to exchange data for ioctl. * This structure is the same as 'struct ifreq', but (re)defined for * convenience... * Do I need to remind you about structure size (32 octets) ? */ struct iwreq { union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ } ifr_ifrn; /* Data part (defined just above) */ union iwreq_data u; }; /* -------------------------- IOCTL DATA -------------------------- */ /* * For those ioctl which want to exchange mode data that what could * fit in the above structure... */ /* * Range of parameters */ struct iw_range { /* Informative stuff (to choose between different interface) */ __u32 throughput; /* To give an idea... */ /* In theory this value should be the maximum benchmarked * TCP/IP throughput, because with most of these devices the * bit rate is meaningless (overhead an co) to estimate how * fast the connection will go and pick the fastest one. * I suggest people to play with Netperf or any benchmark... */ /* NWID (or domain id) */ __u32 min_nwid; /* Minimal NWID we are able to set */ __u32 max_nwid; /* Maximal NWID we are able to set */ /* Old Frequency (backward compat - moved lower ) */ __u16 old_num_channels; __u8 old_num_frequency; /* Scan capabilities */ __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */ /* Wireless event capability bitmasks */ __u32 event_capa[6]; /* signal level threshold range */ __s32 sensitivity; /* Quality of link & SNR stuff */ /* Quality range (link, level, noise) * If the quality is absolute, it will be in the range [0 ; max_qual], * if the quality is dBm, it will be in the range [max_qual ; 0]. * Don't forget that we use 8 bit arithmetics... */ struct iw_quality max_qual; /* Quality of the link */ /* This should contain the average/typical values of the quality * indicator. This should be the threshold between a "good" and * a "bad" link (example : monitor going from green to orange). * Currently, user space apps like quality monitors don't have any * way to calibrate the measurement. With this, they can split * the range between 0 and max_qual in different quality level * (using a geometric subdivision centered on the average). * I expect that people doing the user space apps will feedback * us on which value we need to put in each driver... */ struct iw_quality avg_qual; /* Quality of the link */ /* Rates */ __u8 num_bitrates; /* Number of entries in the list */ __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ /* RTS threshold */ __s32 min_rts; /* Minimal RTS threshold */ __s32 max_rts; /* Maximal RTS threshold */ /* Frag threshold */ __s32 min_frag; /* Minimal frag threshold */ __s32 max_frag; /* Maximal frag threshold */ /* Power Management duration & timeout */ __s32 min_pmp; /* Minimal PM period */ __s32 max_pmp; /* Maximal PM period */ __s32 min_pmt; /* Minimal PM timeout */ __s32 max_pmt; /* Maximal PM timeout */ __u16 pmp_flags; /* How to decode max/min PM period */ __u16 pmt_flags; /* How to decode max/min PM timeout */ __u16 pm_capa; /* What PM options are supported */ /* Encoder stuff */ __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ __u8 num_encoding_sizes; /* Number of entry in the list */ __u8 max_encoding_tokens; /* Max number of tokens */ /* For drivers that need a "login/passwd" form */ __u8 encoding_login_index; /* token index for login token */ /* Transmit power */ __u16 txpower_capa; /* What options are supported */ __u8 num_txpower; /* Number of entries in the list */ __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ /* Wireless Extension version info */ __u8 we_version_compiled; /* Must be WIRELESS_EXT */ __u8 we_version_source; /* Last update of source */ /* Retry limits and lifetime */ __u16 retry_capa; /* What retry options are supported */ __u16 retry_flags; /* How to decode max/min retry limit */ __u16 r_time_flags; /* How to decode max/min retry life */ __s32 min_retry; /* Minimal number of retries */ __s32 max_retry; /* Maximal number of retries */ __s32 min_r_time; /* Minimal retry lifetime */ __s32 max_r_time; /* Maximal retry lifetime */ /* Frequency */ __u16 num_channels; /* Number of channels [0; num - 1] */ __u8 num_frequency; /* Number of entry in the list */ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ /* Note : this frequency list doesn't need to fit channel numbers, * because each entry contain its channel index */ __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ }; /* * Private ioctl interface information */ struct iw_priv_args { __u32 cmd; /* Number of the ioctl to issue */ __u16 set_args; /* Type and number of args */ __u16 get_args; /* Type and number of args */ char name[IFNAMSIZ]; /* Name of the extension */ }; /* ----------------------- WIRELESS EVENTS ----------------------- */ /* * Wireless events are carried through the rtnetlink socket to user * space. They are encapsulated in the IFLA_WIRELESS field of * a RTM_NEWLINK message. */ /* * A Wireless Event. Contains basically the same data as the ioctl... */ struct iw_event { __u16 len; /* Real length of this stuff */ __u16 cmd; /* Wireless IOCTL */ union iwreq_data u; /* IOCTL fixed payload */ }; /* Size of the Event prefix (including padding and alignement junk) */ #define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) /* Size of the various events */ #define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) #define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) #define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) #define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) #define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) #define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) /* iw_point events are special. First, the payload (extra data) come at * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, * we omit the pointer, so start at an offset. */ #define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ (char *) NULL) #define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ IW_EV_POINT_OFF) #ifdef __KERNEL__ #ifdef CONFIG_COMPAT struct __compat_iw_event { __u16 len; /* Real length of this stuff */ __u16 cmd; /* Wireless IOCTL */ compat_caddr_t pointer; }; #define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer) #define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) /* Size of the various events for compat */ #define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ) #define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32)) #define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq)) #define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param)) #define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr)) #define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality)) #define IW_EV_COMPAT_POINT_LEN \ (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \ IW_EV_COMPAT_POINT_OFF) #endif #endif /* Size of the Event prefix when packed in stream */ #define IW_EV_LCP_PK_LEN (4) /* Size of the various events when packed in stream */ #define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ) #define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32)) #define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq)) #define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param)) #define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr)) #define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality)) #define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4) #endif /* _LINUX_WIRELESS_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.34.h0000644000175000017500000002454712025673354021360 0ustar mcgrofmcgrof#ifndef LINUX_26_34_COMPAT_H #define LINUX_26_34_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) #include #include #include #include /* * Backports da68c4eb25 * sdio: introduce API for special power management features * * We wimply carry around the data structures and flags, and * make the host return no flags set by the driver. */ typedef unsigned int mmc_pm_flag_t; #define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func); extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags); void init_compat_mmc_pm_flags(void); #define netdev_mc_count(dev) ((dev)->mc_count) #define netdev_mc_empty(dev) (netdev_mc_count(dev) == 0) /* mask netdev_for_each_mc_addr as RHEL6 backports this */ #if !defined(netdev_for_each_mc_addr) #define netdev_for_each_mc_addr(mclist, dev) \ for (mclist = dev->mc_list; mclist; mclist = mclist->next) #endif /* source: include/linux/netdevice.h */ /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* netdev_printk helpers, similar to dev_printk */ #ifndef netdev_name #define netdev_name(__dev) \ ((__dev->reg_state != NETREG_REGISTERED) ? \ "(unregistered net_device)" : __dev->name) #endif #define netdev_printk(level, netdev, format, args...) \ dev_printk(level, (netdev)->dev.parent, \ "%s: " format, \ netdev_name(netdev), ##args) #define netdev_emerg(dev, format, args...) \ netdev_printk(KERN_EMERG, dev, format, ##args) #define netdev_alert(dev, format, args...) \ netdev_printk(KERN_ALERT, dev, format, ##args) #define netdev_crit(dev, format, args...) \ netdev_printk(KERN_CRIT, dev, format, ##args) #define netdev_err(dev, format, args...) \ netdev_printk(KERN_ERR, dev, format, ##args) #define netdev_warn(dev, format, args...) \ netdev_printk(KERN_WARNING, dev, format, ##args) #define netdev_notice(dev, format, args...) \ netdev_printk(KERN_NOTICE, dev, format, ##args) #define netdev_info(dev, format, args...) \ netdev_printk(KERN_INFO, dev, format, ##args) /* mask netdev_dbg as RHEL6 backports this */ #if !defined(netdev_dbg) #if defined(DEBUG) #define netdev_dbg(__dev, format, args...) \ netdev_printk(KERN_DEBUG, __dev, format, ##args) #elif defined(CONFIG_DYNAMIC_DEBUG) #define netdev_dbg(__dev, format, args...) \ do { \ dynamic_dev_dbg((__dev)->dev.parent, "%s: " format, \ netdev_name(__dev), ##args); \ } while (0) #else #define netdev_dbg(__dev, format, args...) \ ({ \ if (0) \ netdev_printk(KERN_DEBUG, __dev, format, ##args); \ 0; \ }) #endif #endif /* mask netdev_vdbg as RHEL6 backports this */ #if !defined(netdev_dbg) #if defined(VERBOSE_DEBUG) #define netdev_vdbg netdev_dbg #else #define netdev_vdbg(dev, format, args...) \ ({ \ if (0) \ netdev_printk(KERN_DEBUG, dev, format, ##args); \ 0; \ }) #endif #endif /* * netdev_WARN() acts like dev_printk(), but with the key difference * of using a WARN/WARN_ON to get the message out, including the * file/line information and a backtrace. */ #define netdev_WARN(dev, format, args...) \ WARN(1, "netdevice: %s\n" format, netdev_name(dev), ##args); /* netif printk helpers, similar to netdev_printk */ #define netif_printk(priv, type, level, dev, fmt, args...) \ do { \ if (netif_msg_##type(priv)) \ netdev_printk(level, (dev), fmt, ##args); \ } while (0) #define netif_emerg(priv, type, dev, fmt, args...) \ netif_printk(priv, type, KERN_EMERG, dev, fmt, ##args) #define netif_alert(priv, type, dev, fmt, args...) \ netif_printk(priv, type, KERN_ALERT, dev, fmt, ##args) #define netif_crit(priv, type, dev, fmt, args...) \ netif_printk(priv, type, KERN_CRIT, dev, fmt, ##args) #define netif_err(priv, type, dev, fmt, args...) \ netif_printk(priv, type, KERN_ERR, dev, fmt, ##args) #define netif_warn(priv, type, dev, fmt, args...) \ netif_printk(priv, type, KERN_WARNING, dev, fmt, ##args) #define netif_notice(priv, type, dev, fmt, args...) \ netif_printk(priv, type, KERN_NOTICE, dev, fmt, ##args) #define netif_info(priv, type, dev, fmt, args...) \ netif_printk(priv, type, KERN_INFO, (dev), fmt, ##args) /* mask netif_dbg as RHEL6 backports this */ #if !defined(netif_dbg) #if defined(DEBUG) #define netif_dbg(priv, type, dev, format, args...) \ netif_printk(priv, type, KERN_DEBUG, dev, format, ##args) #elif defined(CONFIG_DYNAMIC_DEBUG) #define netif_dbg(priv, type, netdev, format, args...) \ do { \ if (netif_msg_##type(priv)) \ dynamic_dev_dbg((netdev)->dev.parent, \ "%s: " format, \ netdev_name(netdev), ##args); \ } while (0) #else #define netif_dbg(priv, type, dev, format, args...) \ ({ \ if (0) \ netif_printk(priv, type, KERN_DEBUG, dev, format, ##args); \ 0; \ }) #endif #endif /* mask netif_vdbg as RHEL6 backports this */ #if !defined(netif_vdbg) #if defined(VERBOSE_DEBUG) #define netif_vdbg netdev_dbg #else #define netif_vdbg(priv, type, dev, format, args...) \ ({ \ if (0) \ netif_printk(KERN_DEBUG, dev, format, ##args); \ 0; \ }) #endif #endif /* source: include/linux/netdevice.h */ static inline void device_lock(struct device *dev) { #if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_PREEMPT_DESKTOP) mutex_lock(&dev->mutex); #else down(&dev->sem); #endif } static inline int device_trylock(struct device *dev) { #if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_PREEMPT_DESKTOP) return mutex_trylock(&dev->mutex); #else return down_trylock(&dev->sem); #endif } static inline void device_unlock(struct device *dev) { #if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_PREEMPT_DESKTOP) mutex_unlock(&dev->mutex); #else up(&dev->sem); #endif } #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) #define PCMCIA_DEVICE_PROD_ID3(v3, vh3) { \ .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID3, \ .prod_id = { NULL, NULL, (v3), NULL }, \ .prod_id_hash = { 0, 0, (vh3), 0 }, } #endif #define rcu_dereference_check(p, c) rcu_dereference(p) /** * sysfs_attr_init - initialize a dynamically allocated sysfs attribute * @attr: struct attribute to initialize * * Initialize a dynamically allocated struct attribute so we can * make lockdep happy. This is a new requirement for attributes * and initially this is only needed when lockdep is enabled. * Lockdep gives a nice error when your attribute is added to * sysfs if you don't have this. */ #ifdef CONFIG_DEBUG_LOCK_ALLOC #define sysfs_attr_init(attr) \ do { \ static struct lock_class_key __key; \ \ (attr)->key = &__key; \ } while(0) #else #define sysfs_attr_init(attr) do {} while(0) #endif /* mask sysfs_bin_attr_init as RHEL6 backports this */ #if !defined(sysfs_bin_attr_init) /** * sysfs_bin_attr_init - initialize a dynamically allocated bin_attribute * @attr: struct bin_attribute to initialize * * Initialize a dynamically allocated struct bin_attribute so we * can make lockdep happy. This is a new requirement for * attributes and initially this is only needed when lockdep is * enabled. Lockdep gives a nice error when your attribute is * added to sysfs if you don't have this. */ #define sysfs_bin_attr_init(bin_attr) sysfs_attr_init(&(bin_attr)->attr) #endif #define usb_alloc_coherent(dev, size, mem_flags, dma) usb_buffer_alloc(dev, size, mem_flags, dma) #define usb_free_coherent(dev, size, addr, dma) usb_buffer_free(dev, size, addr, dma) /* only include this if DEFINE_DMA_UNMAP_ADDR is not set as debian squeeze also backports this */ #ifndef DEFINE_DMA_UNMAP_ADDR #ifdef CONFIG_NEED_DMA_MAP_STATE #define DEFINE_DMA_UNMAP_ADDR(ADDR_NAME) dma_addr_t ADDR_NAME #define DEFINE_DMA_UNMAP_LEN(LEN_NAME) __u32 LEN_NAME #define dma_unmap_addr(PTR, ADDR_NAME) ((PTR)->ADDR_NAME) #define dma_unmap_addr_set(PTR, ADDR_NAME, VAL) (((PTR)->ADDR_NAME) = (VAL)) #define dma_unmap_len(PTR, LEN_NAME) ((PTR)->LEN_NAME) #define dma_unmap_len_set(PTR, LEN_NAME, VAL) (((PTR)->LEN_NAME) = (VAL)) #else #define DEFINE_DMA_UNMAP_ADDR(ADDR_NAME) #define DEFINE_DMA_UNMAP_LEN(LEN_NAME) #define dma_unmap_addr(PTR, ADDR_NAME) (0) #define dma_unmap_addr_set(PTR, ADDR_NAME, VAL) do { } while (0) #define dma_unmap_len(PTR, LEN_NAME) (0) #define dma_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) #endif #endif /* mask dma_set_coherent_mask as debian squeeze also backports this */ #define dma_set_coherent_mask(a, b) compat_dma_set_coherent_mask(a, b) static inline int dma_set_coherent_mask(struct device *dev, u64 mask) { if (!dma_supported(dev, mask)) return -EIO; dev->coherent_dma_mask = mask; return 0; } /* USB autosuspend and autoresume */ static inline int usb_enable_autosuspend(struct usb_device *udev) { return 0; } static inline int usb_disable_autosuspend(struct usb_device *udev) { return 0; } #define rcu_dereference_protected(p, c) (p) #define rcu_access_pointer(p) ACCESS_ONCE(p) #define rcu_dereference_raw(p) rcu_dereference(p) #define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ /* * This looks more complex than it should be. But we need to * get the type for the ~ right in round_down (it needs to be * as wide as the result!), and we want to evaluate the macro * arguments just once each. */ #define __round_mask(x, y) ((__typeof__(x))((y)-1)) #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) #define round_down(x, y) ((x) & ~__round_mask(x, y)) static inline int rcu_read_lock_held(void) { return 1; } #ifdef CONFIG_PROVE_LOCKING /* * Obviously, this is wrong. But the base kernel will have rtnl_mutex * declared static, with no way to access it. I think this is the best * we can do... */ static inline int lockdep_rtnl_is_held(void) { return 1; } #endif /* #ifdef CONFIG_PROVE_LOCKING */ extern struct hlist_node *seq_hlist_start_head(struct hlist_head *head, loff_t pos); extern struct hlist_node *seq_hlist_next(void *v, struct hlist_head *head, loff_t *ppos); static inline struct sock *sk_entry(const struct hlist_node *node) { return hlist_entry(node, struct sock, sk_node); } #else /* Kernels >= 2.6.34 */ static inline void init_compat_mmc_pm_flags(void) { } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) */ #endif /* LINUX_26_34_COMPAT_H */ compat-drivers-2012-09-18/include/linux/crc8.h0000644000175000017500000000723512025673354020177 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CRC8_H_ #define __CRC8_H_ #include /* see usage of this value in crc8() description */ #define CRC8_INIT_VALUE 0xFF /* * Return value of crc8() indicating valid message+crc. This is true * if a CRC is inverted before transmission. The CRC computed over the * whole received bitstream is _table[x], where x is the bit pattern * of the modification (almost always 0xff). */ #define CRC8_GOOD_VALUE(_table) (_table[0xFF]) /* required table size for crc8 algorithm */ #define CRC8_TABLE_SIZE 256 /* helper macro assuring right table size is used */ #define DECLARE_CRC8_TABLE(_table) \ static u8 _table[CRC8_TABLE_SIZE] /** * crc8_populate_lsb - fill crc table for given polynomial in regular bit order. * * @table: table to be filled. * @polynomial: polynomial for which table is to be filled. * * This function fills the provided table according the polynomial provided for * regular bit order (lsb first). Polynomials in CRC algorithms are typically * represented as shown below. * * poly = x^8 + x^7 + x^6 + x^4 + x^2 + 1 * * For lsb first direction x^7 maps to the lsb. So the polynomial is as below. * * - lsb first: poly = 10101011(1) = 0xAB */ void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial); /** * crc8_populate_msb - fill crc table for given polynomial in reverse bit order. * * @table: table to be filled. * @polynomial: polynomial for which table is to be filled. * * This function fills the provided table according the polynomial provided for * reverse bit order (msb first). Polynomials in CRC algorithms are typically * represented as shown below. * * poly = x^8 + x^7 + x^6 + x^4 + x^2 + 1 * * For msb first direction x^7 maps to the msb. So the polynomial is as below. * * - msb first: poly = (1)11010101 = 0xD5 */ void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial); /** * crc8() - calculate a crc8 over the given input data. * * @table: crc table used for calculation. * @pdata: pointer to data buffer. * @nbytes: number of bytes in data buffer. * @crc: previous returned crc8 value. * * The CRC8 is calculated using the polynomial given in crc8_populate_msb() * or crc8_populate_lsb(). * * The caller provides the initial value (either %CRC8_INIT_VALUE * or the previous returned value) to allow for processing of * discontiguous blocks of data. When generating the CRC the * caller is responsible for complementing the final return value * and inserting it into the byte stream. When validating a byte * stream (including CRC8), a final return value of %CRC8_GOOD_VALUE * indicates the byte stream data can be considered valid. * * Reference: * "A Painless Guide to CRC Error Detection Algorithms", ver 3, Aug 1993 * Williams, Ross N., rossross.net * (see URL http://www.ross.net/crc/download/crc_v3.txt). */ u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc); #endif /* __CRC8_H_ */ compat-drivers-2012-09-18/include/linux/pci-aspm.h0000644000175000017500000000025212025673354021041 0ustar mcgrofmcgrof#include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) #include_next #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) */ compat-drivers-2012-09-18/include/linux/semaphore.h0000644000175000017500000000047312025673354021320 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_SEMAPHORE_H #define _COMPAT_LINUX_SEMAPHORE_H 1 #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) #include_next #else #include #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) */ #endif /* _COMPAT_LINUX_SEMAPHORE_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.26.h0000644000175000017500000002604512025673354021354 0ustar mcgrofmcgrof#ifndef LINUX_26_26_COMPAT_H #define LINUX_26_26_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) #include #include #include #include #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) #include #endif #include #include #include /* These jiffie helpers added as of 2.6.26 */ /* * These four macros compare jiffies and 'a' for convenience. */ /* time_is_before_jiffies(a) return true if a is before jiffies */ #define time_is_before_jiffies(a) time_after(jiffies, a) /* time_is_after_jiffies(a) return true if a is after jiffies */ #define time_is_after_jiffies(a) time_before(jiffies, a) /* time_is_before_eq_jiffies(a) return true if a is before or equal to jiffies*/ #define time_is_before_eq_jiffies(a) time_after_eq(jiffies, a) /* time_is_after_eq_jiffies(a) return true if a is after or equal to jiffies*/ #define time_is_after_eq_jiffies(a) time_before_eq(jiffies, a) /* This comes from include/linux/input.h */ #define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" set = radio enabled */ /* From kernel.h */ #define USHORT_MAX ((u16)(~0U)) #define SHORT_MAX ((s16)(USHORT_MAX>>1)) #define SHORT_MIN (-SHORT_MAX - 1) extern int dev_set_name(struct device *dev, const char *name, ...) __attribute__((format(printf, 2, 3))); /** * clamp - return a value clamped to a given range with strict typechecking * @val: current value * @min: minimum allowable value * @max: maximum allowable value * * This macro does strict typechecking of min/max to make sure they are of the * same type as val. See the unnecessary pointer comparisons. */ #define clamp(val, min, max) ({ \ typeof(val) __val = (val); \ typeof(min) __min = (min); \ typeof(max) __max = (max); \ (void) (&__val == &__min); \ (void) (&__val == &__max); \ __val = __val < __min ? __min: __val; \ __val > __max ? __max: __val; }) /** * clamp_t - return a value clamped to a given range using a given type * @type: the type of variable to use * @val: current value * @min: minimum allowable value * @max: maximum allowable value * * This macro does no typechecking and uses temporary variables of type * 'type' to make all the comparisons. */ #define clamp_t(type, val, min, max) ({ \ type __val = (val); \ type __min = (min); \ type __max = (max); \ __val = __val < __min ? __min: __val; \ __val > __max ? __max: __val; }) /* from include/linux/device.h */ /* device_create_drvdata() is new */ extern struct device *device_create_drvdata(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) __attribute__((format(printf, 5, 6))); /* This is from include/linux/list.h */ /** * list_is_singular - tests whether a list has just one entry. * @head: the list to test. */ static inline int list_is_singular(const struct list_head *head) { return !list_empty(head) && (head->next == head->prev); } /* This is from include/linux/device.h, which was added as of 2.6.26 */ static inline const char *dev_name(struct device *dev) { /* will be changed into kobject_name(&dev->kobj) in the near future */ return dev->bus_id; } /* This is from include/linux/kernel.h, which was added as of 2.6.26 */ /** * clamp_val - return a value clamped to a given range using val's type * @val: current value * @min: minimum allowable value * @max: maximum allowable value * * This macro does no typechecking and uses temporary variables of whatever * type the input argument 'val' is. This is useful when val is an unsigned * type and min and max are literals that will otherwise be assigned a signed * integer type. */ #define clamp_val(val, min, max) ({ \ typeof(val) __val = (val); \ typeof(val) __min = (min); \ typeof(val) __max = (max); \ __val = __val < __min ? __min: __val; \ __val > __max ? __max: __val; }) /* This comes from include/net/net_namespace.h */ #ifdef CONFIG_NET_NS static inline int net_eq(const struct net *net1, const struct net *net2) { return net1 == net2; } #else static inline int net_eq(const struct net *net1, const struct net *net2) { return 1; } #endif static inline void dev_net_set(struct net_device *dev, struct net *net) { #ifdef CONFIG_NET_NS release_net(dev->nd_net); dev->nd_net = hold_net(net); #endif } static inline struct net *sock_net(const struct sock *sk) { #ifdef CONFIG_NET_NS return sk->sk_net; #else return &init_net; #endif } /* This comes from include/linux/netdevice.h */ /* * Net namespace inlines */ static inline struct net *dev_net(const struct net_device *dev) { #ifdef CONFIG_NET_NS /* * compat-wirelss backport note: * For older kernels we may just need to always return init_net, * not sure when we added dev->nd_net. */ return dev->nd_net; #else return &init_net; #endif } /* * 2.6.26 added its own unaligned API which the * new drivers can use. Lets port it here by including it in older * kernels and also deal with the architecture handling here. */ #ifdef CONFIG_ALPHA #include #include #include #endif /* alpha */ #ifdef CONFIG_ARM /* arm */ #include #include #include #endif /* arm */ #ifdef CONFIG_AVR32 /* * AVR32 can handle some unaligned accesses, depending on the * implementation. The AVR32 AP implementation can handle unaligned * words, but halfwords must be halfword-aligned, and doublewords must * be word-aligned. * * However, swapped word loads must be word-aligned so we can't * optimize word loads in general. */ #include #include #include #endif #ifdef CONFIG_BLACKFIN #include #include #include #endif /* blackfin */ #ifdef CONFIG_CRIS /* * CRIS can do unaligned accesses itself. */ #include #include #endif /* cris */ #ifdef CONFIG_FRV #include #include #include #endif /* frv */ #ifdef CONFIG_H8300 #include #include #include #endif /* h8300 */ #ifdef CONFIG_IA64 #include #include #include #endif /* ia64 */ #ifdef CONFIG_M32R #if defined(__LITTLE_ENDIAN__) # include # include # include #else # include # include # include #endif #endif /* m32r */ #ifdef CONFIG_M68K /* this handles both m68k and m68knommu */ #ifdef CONFIG_COLDFIRE #include #include #include #else /* * The m68k can do unaligned accesses itself. */ #include #include #endif #endif /* m68k and m68knommu */ #ifdef CONFIG_MIPS #if defined(__MIPSEB__) # include # include # include # define get_unaligned __get_unaligned_be # define put_unaligned __put_unaligned_be #elif defined(__MIPSEL__) # include # include # include #endif #endif /* mips */ #ifdef CONFIG_MN10300 #include #include #endif /* mn10300 */ #ifdef CONFIG_PARISC #include #include #include #endif /* parisc */ #ifdef CONFIG_PPC /* * The PowerPC can do unaligned accesses itself in big endian mode. */ #include #include #endif /* ppc */ #ifdef CONFIG_S390 /* * The S390 can do unaligned accesses itself. */ #include #include #endif /* s390 */ #ifdef CONFIG_SUPERH /* SH can't handle unaligned accesses. */ #ifdef __LITTLE_ENDIAN__ # include # include # include #else # include # include # include #endif #endif /* sh - SUPERH */ #ifdef CONFIG_SPARC /* sparc and sparc64 */ #include #include #include #endif /* sparc */ #ifdef CONFIG_UML #include "asm/arch/unaligned.h" #endif /* um - uml */ #ifdef CONFIG_V850 #include #include #include #endif /* v850 */ #ifdef CONFIG_X86 /* * The x86 can do unaligned accesses itself. */ #include #include #endif /* x86 */ #ifdef CONFIG_XTENSA #ifdef __XTENSA_EL__ # include # include # include #elif defined(__XTENSA_EB__) # include # include # include #else # error processor byte order undefined! #endif #endif /* xtensa */ #define PCIE_LINK_STATE_L0S 1 #define PCIE_LINK_STATE_L1 2 #define PCIE_LINK_STATE_CLKPM 4 static inline void pci_disable_link_state(struct pci_dev *pdev, int state) { } /* source: include/linux/pci-aspm.h */ #if BITS_PER_LONG == 64 /** * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder * * This is commonly provided by 32bit archs to provide an optimized 64bit * divide. */ static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) { *remainder = dividend % divisor; return dividend / divisor; } #elif BITS_PER_LONG == 32 #ifndef div_u64_rem static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) { *remainder = do_div(dividend, divisor); return dividend; } #endif #endif /* BITS_PER_LONG */ /** * div_u64 - unsigned 64bit divide with 32bit divisor * * This is the most common 64bit divide and should be used if possible, * as many 32bit archs can optimize this variant better than a full 64bit * divide. */ #ifndef div_u64 static inline u64 div_u64(u64 dividend, u32 divisor) { u32 remainder; return div_u64_rem(dividend, divisor, &remainder); } #endif /* source: include/math64.h */ #define hex_asc_lo(x) hex_asc((x) & 0x0f) #define hex_asc_hi(x) hex_asc(((x) & 0xf0) >> 4) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) */ #endif /* LINUX_26_26_COMPAT_H */ compat-drivers-2012-09-18/include/linux/cordic.h0000644000175000017500000000337612025673354020605 0ustar mcgrofmcgrof/* * Copyright (c) 2011 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __CORDIC_H_ #define __CORDIC_H_ #include /** * struct cordic_iq - i/q coordinate. * * @i: real part of coordinate (in phase). * @q: imaginary part of coordinate (quadrature). */ struct cordic_iq { s32 i; s32 q; }; /** * cordic_calc_iq() - calculates the i/q coordinate for given angle. * * @theta: angle in degrees for which i/q coordinate is to be calculated. * @coord: function output parameter holding the i/q coordinate. * * The function calculates the i/q coordinate for a given angle using * cordic algorithm. The coordinate consists of a real (i) and an * imaginary (q) part. The real part is essentially the cosine of the * angle and the imaginary part is the sine of the angle. The returned * values are scaled by 2^16 for precision. The range for theta is * for -180 degrees to +180 degrees. Passed values outside this range are * converted before doing the actual calculation. */ struct cordic_iq cordic_calc_iq(s32 theta); #endif /* __CORDIC_H_ */ compat-drivers-2012-09-18/include/linux/pci_ids.h0000644000175000017500000034204312026211315020733 0ustar mcgrofmcgrof/* * PCI Class, Vendor and Device IDs * * Please keep sorted. * * Do not add new entries to this file unless the definitions * are shared between multiple drivers. */ /* Device classes and subclasses */ #define PCI_CLASS_NOT_DEFINED 0x0000 #define PCI_CLASS_NOT_DEFINED_VGA 0x0001 #define PCI_BASE_CLASS_STORAGE 0x01 #define PCI_CLASS_STORAGE_SCSI 0x0100 #define PCI_CLASS_STORAGE_IDE 0x0101 #define PCI_CLASS_STORAGE_FLOPPY 0x0102 #define PCI_CLASS_STORAGE_IPI 0x0103 #define PCI_CLASS_STORAGE_RAID 0x0104 #define PCI_CLASS_STORAGE_SATA 0x0106 #define PCI_CLASS_STORAGE_SATA_AHCI 0x010601 #define PCI_CLASS_STORAGE_SAS 0x0107 #define PCI_CLASS_STORAGE_OTHER 0x0180 #define PCI_BASE_CLASS_NETWORK 0x02 #define PCI_CLASS_NETWORK_ETHERNET 0x0200 #define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 #define PCI_CLASS_NETWORK_FDDI 0x0202 #define PCI_CLASS_NETWORK_ATM 0x0203 #define PCI_CLASS_NETWORK_OTHER 0x0280 #define PCI_BASE_CLASS_DISPLAY 0x03 #define PCI_CLASS_DISPLAY_VGA 0x0300 #define PCI_CLASS_DISPLAY_XGA 0x0301 #define PCI_CLASS_DISPLAY_3D 0x0302 #define PCI_CLASS_DISPLAY_OTHER 0x0380 #define PCI_BASE_CLASS_MULTIMEDIA 0x04 #define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 #define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 #define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 #define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 #define PCI_BASE_CLASS_MEMORY 0x05 #define PCI_CLASS_MEMORY_RAM 0x0500 #define PCI_CLASS_MEMORY_FLASH 0x0501 #define PCI_CLASS_MEMORY_OTHER 0x0580 #define PCI_BASE_CLASS_BRIDGE 0x06 #define PCI_CLASS_BRIDGE_HOST 0x0600 #define PCI_CLASS_BRIDGE_ISA 0x0601 #define PCI_CLASS_BRIDGE_EISA 0x0602 #define PCI_CLASS_BRIDGE_MC 0x0603 #define PCI_CLASS_BRIDGE_PCI 0x0604 #define PCI_CLASS_BRIDGE_PCMCIA 0x0605 #define PCI_CLASS_BRIDGE_NUBUS 0x0606 #define PCI_CLASS_BRIDGE_CARDBUS 0x0607 #define PCI_CLASS_BRIDGE_RACEWAY 0x0608 #define PCI_CLASS_BRIDGE_OTHER 0x0680 #define PCI_BASE_CLASS_COMMUNICATION 0x07 #define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 #define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 #define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 #define PCI_CLASS_COMMUNICATION_MODEM 0x0703 #define PCI_CLASS_COMMUNICATION_OTHER 0x0780 #define PCI_BASE_CLASS_SYSTEM 0x08 #define PCI_CLASS_SYSTEM_PIC 0x0800 #define PCI_CLASS_SYSTEM_PIC_IOAPIC 0x080010 #define PCI_CLASS_SYSTEM_PIC_IOXAPIC 0x080020 #define PCI_CLASS_SYSTEM_DMA 0x0801 #define PCI_CLASS_SYSTEM_TIMER 0x0802 #define PCI_CLASS_SYSTEM_RTC 0x0803 #define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 #define PCI_CLASS_SYSTEM_SDHCI 0x0805 #define PCI_CLASS_SYSTEM_OTHER 0x0880 #define PCI_BASE_CLASS_INPUT 0x09 #define PCI_CLASS_INPUT_KEYBOARD 0x0900 #define PCI_CLASS_INPUT_PEN 0x0901 #define PCI_CLASS_INPUT_MOUSE 0x0902 #define PCI_CLASS_INPUT_SCANNER 0x0903 #define PCI_CLASS_INPUT_GAMEPORT 0x0904 #define PCI_CLASS_INPUT_OTHER 0x0980 #define PCI_BASE_CLASS_DOCKING 0x0a #define PCI_CLASS_DOCKING_GENERIC 0x0a00 #define PCI_CLASS_DOCKING_OTHER 0x0a80 #define PCI_BASE_CLASS_PROCESSOR 0x0b #define PCI_CLASS_PROCESSOR_386 0x0b00 #define PCI_CLASS_PROCESSOR_486 0x0b01 #define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 #define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 #define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 #define PCI_CLASS_PROCESSOR_MIPS 0x0b30 #define PCI_CLASS_PROCESSOR_CO 0x0b40 #define PCI_BASE_CLASS_SERIAL 0x0c #define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 #define PCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010 #define PCI_CLASS_SERIAL_ACCESS 0x0c01 #define PCI_CLASS_SERIAL_SSA 0x0c02 #define PCI_CLASS_SERIAL_USB 0x0c03 #define PCI_CLASS_SERIAL_USB_UHCI 0x0c0300 #define PCI_CLASS_SERIAL_USB_OHCI 0x0c0310 #define PCI_CLASS_SERIAL_USB_EHCI 0x0c0320 #define PCI_CLASS_SERIAL_USB_XHCI 0x0c0330 #define PCI_CLASS_SERIAL_FIBER 0x0c04 #define PCI_CLASS_SERIAL_SMBUS 0x0c05 #define PCI_BASE_CLASS_WIRELESS 0x0d #define PCI_CLASS_WIRELESS_RF_CONTROLLER 0x0d10 #define PCI_CLASS_WIRELESS_WHCI 0x0d1010 #define PCI_BASE_CLASS_INTELLIGENT 0x0e #define PCI_CLASS_INTELLIGENT_I2O 0x0e00 #define PCI_BASE_CLASS_SATELLITE 0x0f #define PCI_CLASS_SATELLITE_TV 0x0f00 #define PCI_CLASS_SATELLITE_AUDIO 0x0f01 #define PCI_CLASS_SATELLITE_VOICE 0x0f03 #define PCI_CLASS_SATELLITE_DATA 0x0f04 #define PCI_BASE_CLASS_CRYPT 0x10 #define PCI_CLASS_CRYPT_NETWORK 0x1000 #define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 #define PCI_CLASS_CRYPT_OTHER 0x1080 #define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 #define PCI_CLASS_SP_DPIO 0x1100 #define PCI_CLASS_SP_OTHER 0x1180 #define PCI_CLASS_OTHERS 0xff /* Vendors and devices. Sort key: vendor first, device next. */ #define PCI_VENDOR_ID_TTTECH 0x0357 #define PCI_DEVICE_ID_TTTECH_MC322 0x000a #define PCI_VENDOR_ID_DYNALINK 0x0675 #define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702 #define PCI_VENDOR_ID_BERKOM 0x0871 #define PCI_DEVICE_ID_BERKOM_A1T 0xffa1 #define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xffa2 #define PCI_DEVICE_ID_BERKOM_A4T 0xffa4 #define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO 0xffa8 #define PCI_VENDOR_ID_COMPAQ 0x0e11 #define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508 #define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc #define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10 #define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32 #define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34 #define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33 #define PCI_DEVICE_ID_COMPAQ_NETFLEX3I 0xae35 #define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40 #define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43 #define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011 #define PCI_DEVICE_ID_COMPAQ_CISS 0xb060 #define PCI_DEVICE_ID_COMPAQ_CISSB 0xb178 #define PCI_DEVICE_ID_COMPAQ_CISSC 0x46 #define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130 #define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150 #define PCI_VENDOR_ID_NCR 0x1000 #define PCI_VENDOR_ID_LSI_LOGIC 0x1000 #define PCI_DEVICE_ID_NCR_53C810 0x0001 #define PCI_DEVICE_ID_NCR_53C820 0x0002 #define PCI_DEVICE_ID_NCR_53C825 0x0003 #define PCI_DEVICE_ID_NCR_53C815 0x0004 #define PCI_DEVICE_ID_LSI_53C810AP 0x0005 #define PCI_DEVICE_ID_NCR_53C860 0x0006 #define PCI_DEVICE_ID_LSI_53C1510 0x000a #define PCI_DEVICE_ID_NCR_53C896 0x000b #define PCI_DEVICE_ID_NCR_53C895 0x000c #define PCI_DEVICE_ID_NCR_53C885 0x000d #define PCI_DEVICE_ID_NCR_53C875 0x000f #define PCI_DEVICE_ID_NCR_53C1510 0x0010 #define PCI_DEVICE_ID_LSI_53C895A 0x0012 #define PCI_DEVICE_ID_LSI_53C875A 0x0013 #define PCI_DEVICE_ID_LSI_53C1010_33 0x0020 #define PCI_DEVICE_ID_LSI_53C1010_66 0x0021 #define PCI_DEVICE_ID_LSI_53C1030 0x0030 #define PCI_DEVICE_ID_LSI_1030_53C1035 0x0032 #define PCI_DEVICE_ID_LSI_53C1035 0x0040 #define PCI_DEVICE_ID_NCR_53C875J 0x008f #define PCI_DEVICE_ID_LSI_FC909 0x0621 #define PCI_DEVICE_ID_LSI_FC929 0x0622 #define PCI_DEVICE_ID_LSI_FC929_LAN 0x0623 #define PCI_DEVICE_ID_LSI_FC919 0x0624 #define PCI_DEVICE_ID_LSI_FC919_LAN 0x0625 #define PCI_DEVICE_ID_LSI_FC929X 0x0626 #define PCI_DEVICE_ID_LSI_FC939X 0x0642 #define PCI_DEVICE_ID_LSI_FC949X 0x0640 #define PCI_DEVICE_ID_LSI_FC949ES 0x0646 #define PCI_DEVICE_ID_LSI_FC919X 0x0628 #define PCI_DEVICE_ID_NCR_YELLOWFIN 0x0701 #define PCI_DEVICE_ID_LSI_61C102 0x0901 #define PCI_DEVICE_ID_LSI_63C815 0x1000 #define PCI_DEVICE_ID_LSI_SAS1064 0x0050 #define PCI_DEVICE_ID_LSI_SAS1064R 0x0411 #define PCI_DEVICE_ID_LSI_SAS1066 0x005E #define PCI_DEVICE_ID_LSI_SAS1068 0x0054 #define PCI_DEVICE_ID_LSI_SAS1064A 0x005C #define PCI_DEVICE_ID_LSI_SAS1064E 0x0056 #define PCI_DEVICE_ID_LSI_SAS1066E 0x005A #define PCI_DEVICE_ID_LSI_SAS1068E 0x0058 #define PCI_DEVICE_ID_LSI_SAS1078 0x0060 #define PCI_VENDOR_ID_ATI 0x1002 /* Mach64 */ #define PCI_DEVICE_ID_ATI_68800 0x4158 #define PCI_DEVICE_ID_ATI_215CT222 0x4354 #define PCI_DEVICE_ID_ATI_210888CX 0x4358 #define PCI_DEVICE_ID_ATI_215ET222 0x4554 /* Mach64 / Rage */ #define PCI_DEVICE_ID_ATI_215GB 0x4742 #define PCI_DEVICE_ID_ATI_215GD 0x4744 #define PCI_DEVICE_ID_ATI_215GI 0x4749 #define PCI_DEVICE_ID_ATI_215GP 0x4750 #define PCI_DEVICE_ID_ATI_215GQ 0x4751 #define PCI_DEVICE_ID_ATI_215XL 0x4752 #define PCI_DEVICE_ID_ATI_215GT 0x4754 #define PCI_DEVICE_ID_ATI_215GTB 0x4755 #define PCI_DEVICE_ID_ATI_215_IV 0x4756 #define PCI_DEVICE_ID_ATI_215_IW 0x4757 #define PCI_DEVICE_ID_ATI_215_IZ 0x475A #define PCI_DEVICE_ID_ATI_210888GX 0x4758 #define PCI_DEVICE_ID_ATI_215_LB 0x4c42 #define PCI_DEVICE_ID_ATI_215_LD 0x4c44 #define PCI_DEVICE_ID_ATI_215_LG 0x4c47 #define PCI_DEVICE_ID_ATI_215_LI 0x4c49 #define PCI_DEVICE_ID_ATI_215_LM 0x4c4D #define PCI_DEVICE_ID_ATI_215_LN 0x4c4E #define PCI_DEVICE_ID_ATI_215_LR 0x4c52 #define PCI_DEVICE_ID_ATI_215_LS 0x4c53 #define PCI_DEVICE_ID_ATI_264_LT 0x4c54 /* Mach64 VT */ #define PCI_DEVICE_ID_ATI_264VT 0x5654 #define PCI_DEVICE_ID_ATI_264VU 0x5655 #define PCI_DEVICE_ID_ATI_264VV 0x5656 /* Rage128 GL */ #define PCI_DEVICE_ID_ATI_RAGE128_RE 0x5245 #define PCI_DEVICE_ID_ATI_RAGE128_RF 0x5246 #define PCI_DEVICE_ID_ATI_RAGE128_RG 0x5247 /* Rage128 VR */ #define PCI_DEVICE_ID_ATI_RAGE128_RK 0x524b #define PCI_DEVICE_ID_ATI_RAGE128_RL 0x524c #define PCI_DEVICE_ID_ATI_RAGE128_SE 0x5345 #define PCI_DEVICE_ID_ATI_RAGE128_SF 0x5346 #define PCI_DEVICE_ID_ATI_RAGE128_SG 0x5347 #define PCI_DEVICE_ID_ATI_RAGE128_SH 0x5348 #define PCI_DEVICE_ID_ATI_RAGE128_SK 0x534b #define PCI_DEVICE_ID_ATI_RAGE128_SL 0x534c #define PCI_DEVICE_ID_ATI_RAGE128_SM 0x534d #define PCI_DEVICE_ID_ATI_RAGE128_SN 0x534e /* Rage128 Ultra */ #define PCI_DEVICE_ID_ATI_RAGE128_TF 0x5446 #define PCI_DEVICE_ID_ATI_RAGE128_TL 0x544c #define PCI_DEVICE_ID_ATI_RAGE128_TR 0x5452 #define PCI_DEVICE_ID_ATI_RAGE128_TS 0x5453 #define PCI_DEVICE_ID_ATI_RAGE128_TT 0x5454 #define PCI_DEVICE_ID_ATI_RAGE128_TU 0x5455 /* Rage128 M3 */ #define PCI_DEVICE_ID_ATI_RAGE128_LE 0x4c45 #define PCI_DEVICE_ID_ATI_RAGE128_LF 0x4c46 /* Rage128 M4 */ #define PCI_DEVICE_ID_ATI_RAGE128_MF 0x4d46 #define PCI_DEVICE_ID_ATI_RAGE128_ML 0x4d4c /* Rage128 Pro GL */ #define PCI_DEVICE_ID_ATI_RAGE128_PA 0x5041 #define PCI_DEVICE_ID_ATI_RAGE128_PB 0x5042 #define PCI_DEVICE_ID_ATI_RAGE128_PC 0x5043 #define PCI_DEVICE_ID_ATI_RAGE128_PD 0x5044 #define PCI_DEVICE_ID_ATI_RAGE128_PE 0x5045 #define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 /* Rage128 Pro VR */ #define PCI_DEVICE_ID_ATI_RAGE128_PG 0x5047 #define PCI_DEVICE_ID_ATI_RAGE128_PH 0x5048 #define PCI_DEVICE_ID_ATI_RAGE128_PI 0x5049 #define PCI_DEVICE_ID_ATI_RAGE128_PJ 0x504A #define PCI_DEVICE_ID_ATI_RAGE128_PK 0x504B #define PCI_DEVICE_ID_ATI_RAGE128_PL 0x504C #define PCI_DEVICE_ID_ATI_RAGE128_PM 0x504D #define PCI_DEVICE_ID_ATI_RAGE128_PN 0x504E #define PCI_DEVICE_ID_ATI_RAGE128_PO 0x504F #define PCI_DEVICE_ID_ATI_RAGE128_PP 0x5050 #define PCI_DEVICE_ID_ATI_RAGE128_PQ 0x5051 #define PCI_DEVICE_ID_ATI_RAGE128_PR 0x5052 #define PCI_DEVICE_ID_ATI_RAGE128_PS 0x5053 #define PCI_DEVICE_ID_ATI_RAGE128_PT 0x5054 #define PCI_DEVICE_ID_ATI_RAGE128_PU 0x5055 #define PCI_DEVICE_ID_ATI_RAGE128_PV 0x5056 #define PCI_DEVICE_ID_ATI_RAGE128_PW 0x5057 #define PCI_DEVICE_ID_ATI_RAGE128_PX 0x5058 /* Rage128 M4 */ /* Radeon R100 */ #define PCI_DEVICE_ID_ATI_RADEON_QD 0x5144 #define PCI_DEVICE_ID_ATI_RADEON_QE 0x5145 #define PCI_DEVICE_ID_ATI_RADEON_QF 0x5146 #define PCI_DEVICE_ID_ATI_RADEON_QG 0x5147 /* Radeon RV100 (VE) */ #define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 #define PCI_DEVICE_ID_ATI_RADEON_QZ 0x515a /* Radeon R200 (8500) */ #define PCI_DEVICE_ID_ATI_RADEON_QL 0x514c #define PCI_DEVICE_ID_ATI_RADEON_QN 0x514e #define PCI_DEVICE_ID_ATI_RADEON_QO 0x514f #define PCI_DEVICE_ID_ATI_RADEON_Ql 0x516c #define PCI_DEVICE_ID_ATI_RADEON_BB 0x4242 /* Radeon R200 (9100) */ #define PCI_DEVICE_ID_ATI_RADEON_QM 0x514d /* Radeon RV200 (7500) */ #define PCI_DEVICE_ID_ATI_RADEON_QW 0x5157 #define PCI_DEVICE_ID_ATI_RADEON_QX 0x5158 /* Radeon NV-100 */ /* Radeon RV250 (9000) */ #define PCI_DEVICE_ID_ATI_RADEON_Id 0x4964 #define PCI_DEVICE_ID_ATI_RADEON_Ie 0x4965 #define PCI_DEVICE_ID_ATI_RADEON_If 0x4966 #define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967 /* Radeon RV280 (9200) */ #define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961 #define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964 /* Radeon R300 (9500) */ /* Radeon R300 (9700) */ #define PCI_DEVICE_ID_ATI_RADEON_ND 0x4e44 #define PCI_DEVICE_ID_ATI_RADEON_NE 0x4e45 #define PCI_DEVICE_ID_ATI_RADEON_NF 0x4e46 #define PCI_DEVICE_ID_ATI_RADEON_NG 0x4e47 /* Radeon R350 (9800) */ /* Radeon RV350 (9600) */ /* Radeon M6 */ #define PCI_DEVICE_ID_ATI_RADEON_LY 0x4c59 #define PCI_DEVICE_ID_ATI_RADEON_LZ 0x4c5a /* Radeon M7 */ #define PCI_DEVICE_ID_ATI_RADEON_LW 0x4c57 #define PCI_DEVICE_ID_ATI_RADEON_LX 0x4c58 /* Radeon M9 */ #define PCI_DEVICE_ID_ATI_RADEON_Ld 0x4c64 #define PCI_DEVICE_ID_ATI_RADEON_Le 0x4c65 #define PCI_DEVICE_ID_ATI_RADEON_Lf 0x4c66 #define PCI_DEVICE_ID_ATI_RADEON_Lg 0x4c67 /* Radeon */ /* RadeonIGP */ #define PCI_DEVICE_ID_ATI_RS100 0xcab0 #define PCI_DEVICE_ID_ATI_RS200 0xcab2 #define PCI_DEVICE_ID_ATI_RS200_B 0xcbb2 #define PCI_DEVICE_ID_ATI_RS250 0xcab3 #define PCI_DEVICE_ID_ATI_RS300_100 0x5830 #define PCI_DEVICE_ID_ATI_RS300_133 0x5831 #define PCI_DEVICE_ID_ATI_RS300_166 0x5832 #define PCI_DEVICE_ID_ATI_RS300_200 0x5833 #define PCI_DEVICE_ID_ATI_RS350_100 0x7830 #define PCI_DEVICE_ID_ATI_RS350_133 0x7831 #define PCI_DEVICE_ID_ATI_RS350_166 0x7832 #define PCI_DEVICE_ID_ATI_RS350_200 0x7833 #define PCI_DEVICE_ID_ATI_RS400_100 0x5a30 #define PCI_DEVICE_ID_ATI_RS400_133 0x5a31 #define PCI_DEVICE_ID_ATI_RS400_166 0x5a32 #define PCI_DEVICE_ID_ATI_RS400_200 0x5a33 #define PCI_DEVICE_ID_ATI_RS480 0x5950 /* ATI IXP Chipset */ #define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 #define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353 #define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363 #define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369 #define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e #define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372 #define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 #define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 #define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a #define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380 #define PCI_DEVICE_ID_ATI_SBX00_SMBUS 0x4385 #define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c #define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390 #define PCI_DEVICE_ID_ATI_IXP700_IDE 0x439c #define PCI_VENDOR_ID_VLSI 0x1004 #define PCI_DEVICE_ID_VLSI_82C592 0x0005 #define PCI_DEVICE_ID_VLSI_82C593 0x0006 #define PCI_DEVICE_ID_VLSI_82C594 0x0007 #define PCI_DEVICE_ID_VLSI_82C597 0x0009 #define PCI_DEVICE_ID_VLSI_82C541 0x000c #define PCI_DEVICE_ID_VLSI_82C543 0x000d #define PCI_DEVICE_ID_VLSI_82C532 0x0101 #define PCI_DEVICE_ID_VLSI_82C534 0x0102 #define PCI_DEVICE_ID_VLSI_82C535 0x0104 #define PCI_DEVICE_ID_VLSI_82C147 0x0105 #define PCI_DEVICE_ID_VLSI_VAS96011 0x0702 /* AMD RD890 Chipset */ #define PCI_DEVICE_ID_RD890_IOMMU 0x5a23 #define PCI_VENDOR_ID_ADL 0x1005 #define PCI_DEVICE_ID_ADL_2301 0x2301 #define PCI_VENDOR_ID_NS 0x100b #define PCI_DEVICE_ID_NS_87415 0x0002 #define PCI_DEVICE_ID_NS_87560_LIO 0x000e #define PCI_DEVICE_ID_NS_87560_USB 0x0012 #define PCI_DEVICE_ID_NS_83815 0x0020 #define PCI_DEVICE_ID_NS_83820 0x0022 #define PCI_DEVICE_ID_NS_CS5535_ISA 0x002b #define PCI_DEVICE_ID_NS_CS5535_IDE 0x002d #define PCI_DEVICE_ID_NS_CS5535_AUDIO 0x002e #define PCI_DEVICE_ID_NS_CS5535_USB 0x002f #define PCI_DEVICE_ID_NS_GX_VIDEO 0x0030 #define PCI_DEVICE_ID_NS_SATURN 0x0035 #define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500 #define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501 #define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502 #define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503 #define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504 #define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505 #define PCI_DEVICE_ID_NS_SC1100_BRIDGE 0x0510 #define PCI_DEVICE_ID_NS_SC1100_SMI 0x0511 #define PCI_DEVICE_ID_NS_SC1100_XBUS 0x0515 #define PCI_DEVICE_ID_NS_87410 0xd001 #define PCI_DEVICE_ID_NS_GX_HOST_BRIDGE 0x0028 #define PCI_VENDOR_ID_TSENG 0x100c #define PCI_DEVICE_ID_TSENG_W32P_2 0x3202 #define PCI_DEVICE_ID_TSENG_W32P_b 0x3205 #define PCI_DEVICE_ID_TSENG_W32P_c 0x3206 #define PCI_DEVICE_ID_TSENG_W32P_d 0x3207 #define PCI_DEVICE_ID_TSENG_ET6000 0x3208 #define PCI_VENDOR_ID_WEITEK 0x100e #define PCI_DEVICE_ID_WEITEK_P9000 0x9001 #define PCI_DEVICE_ID_WEITEK_P9100 0x9100 #define PCI_VENDOR_ID_DEC 0x1011 #define PCI_DEVICE_ID_DEC_BRD 0x0001 #define PCI_DEVICE_ID_DEC_TULIP 0x0002 #define PCI_DEVICE_ID_DEC_TGA 0x0004 #define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 #define PCI_DEVICE_ID_DEC_TGA2 0x000D #define PCI_DEVICE_ID_DEC_FDDI 0x000F #define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 #define PCI_DEVICE_ID_DEC_21142 0x0019 #define PCI_DEVICE_ID_DEC_21052 0x0021 #define PCI_DEVICE_ID_DEC_21150 0x0022 #define PCI_DEVICE_ID_DEC_21152 0x0024 #define PCI_DEVICE_ID_DEC_21153 0x0025 #define PCI_DEVICE_ID_DEC_21154 0x0026 #define PCI_DEVICE_ID_DEC_21285 0x1065 #define PCI_DEVICE_ID_COMPAQ_42XX 0x0046 #define PCI_VENDOR_ID_CIRRUS 0x1013 #define PCI_DEVICE_ID_CIRRUS_7548 0x0038 #define PCI_DEVICE_ID_CIRRUS_5430 0x00a0 #define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4 #define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8 #define PCI_DEVICE_ID_CIRRUS_5436 0x00ac #define PCI_DEVICE_ID_CIRRUS_5446 0x00b8 #define PCI_DEVICE_ID_CIRRUS_5480 0x00bc #define PCI_DEVICE_ID_CIRRUS_5462 0x00d0 #define PCI_DEVICE_ID_CIRRUS_5464 0x00d4 #define PCI_DEVICE_ID_CIRRUS_5465 0x00d6 #define PCI_DEVICE_ID_CIRRUS_6729 0x1100 #define PCI_DEVICE_ID_CIRRUS_6832 0x1110 #define PCI_DEVICE_ID_CIRRUS_7543 0x1202 #define PCI_DEVICE_ID_CIRRUS_4610 0x6001 #define PCI_DEVICE_ID_CIRRUS_4612 0x6003 #define PCI_DEVICE_ID_CIRRUS_4615 0x6004 #define PCI_VENDOR_ID_IBM 0x1014 #define PCI_DEVICE_ID_IBM_TR 0x0018 #define PCI_DEVICE_ID_IBM_TR_WAKE 0x003e #define PCI_DEVICE_ID_IBM_CPC710_PCI64 0x00fc #define PCI_DEVICE_ID_IBM_SNIPE 0x0180 #define PCI_DEVICE_ID_IBM_CITRINE 0x028C #define PCI_DEVICE_ID_IBM_GEMSTONE 0xB166 #define PCI_DEVICE_ID_IBM_OBSIDIAN 0x02BD #define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1 0x0031 #define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2 0x0219 #define PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX 0x021A #define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM 0x0251 #define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361 #define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252 #define PCI_SUBVENDOR_ID_IBM 0x1014 #define PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT 0x03d4 #define PCI_VENDOR_ID_UNISYS 0x1018 #define PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR 0x001C #define PCI_VENDOR_ID_COMPEX2 0x101a /* pci.ids says "AT&T GIS (NCR)" */ #define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 #define PCI_VENDOR_ID_WD 0x101c #define PCI_DEVICE_ID_WD_90C 0xc24a #define PCI_VENDOR_ID_AMI 0x101e #define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 #define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 #define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060 #define PCI_VENDOR_ID_AMD 0x1022 #define PCI_DEVICE_ID_AMD_K8_NB 0x1100 #define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 #define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 #define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 #define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 #define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 #define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 #define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 #define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 #define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300 #define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301 #define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 #define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 #define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 #define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403 #define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600 #define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601 #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 #define PCI_DEVICE_ID_AMD_15H_NB_F3 0x1603 #define PCI_DEVICE_ID_AMD_15H_NB_F4 0x1604 #define PCI_DEVICE_ID_AMD_15H_NB_F5 0x1605 #define PCI_DEVICE_ID_AMD_CNB17H_F3 0x1703 #define PCI_DEVICE_ID_AMD_LANCE 0x2000 #define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 #define PCI_DEVICE_ID_AMD_SCSI 0x2020 #define PCI_DEVICE_ID_AMD_SERENADE 0x36c0 #define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006 #define PCI_DEVICE_ID_AMD_FE_GATE_7007 0x7007 #define PCI_DEVICE_ID_AMD_FE_GATE_700C 0x700C #define PCI_DEVICE_ID_AMD_FE_GATE_700E 0x700E #define PCI_DEVICE_ID_AMD_COBRA_7401 0x7401 #define PCI_DEVICE_ID_AMD_VIPER_7409 0x7409 #define PCI_DEVICE_ID_AMD_VIPER_740B 0x740B #define PCI_DEVICE_ID_AMD_VIPER_7410 0x7410 #define PCI_DEVICE_ID_AMD_VIPER_7411 0x7411 #define PCI_DEVICE_ID_AMD_VIPER_7413 0x7413 #define PCI_DEVICE_ID_AMD_VIPER_7440 0x7440 #define PCI_DEVICE_ID_AMD_OPUS_7441 0x7441 #define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443 #define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443 #define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445 #define PCI_DEVICE_ID_AMD_8111_PCI 0x7460 #define PCI_DEVICE_ID_AMD_8111_LPC 0x7468 #define PCI_DEVICE_ID_AMD_8111_IDE 0x7469 #define PCI_DEVICE_ID_AMD_8111_SMBUS2 0x746a #define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746b #define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d #define PCI_DEVICE_ID_AMD_8151_0 0x7454 #define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 #define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 #define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458 #define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b #define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F #define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 #define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 #define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093 #define PCI_DEVICE_ID_AMD_CS5536_OHC 0x2094 #define PCI_DEVICE_ID_AMD_CS5536_EHC 0x2095 #define PCI_DEVICE_ID_AMD_CS5536_UDC 0x2096 #define PCI_DEVICE_ID_AMD_CS5536_UOC 0x2097 #define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A #define PCI_DEVICE_ID_AMD_LX_VIDEO 0x2081 #define PCI_DEVICE_ID_AMD_LX_AES 0x2082 #define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c #define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE 0x7800 #define PCI_VENDOR_ID_TRIDENT 0x1023 #define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 #define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 #define PCI_DEVICE_ID_TRIDENT_9320 0x9320 #define PCI_DEVICE_ID_TRIDENT_9388 0x9388 #define PCI_DEVICE_ID_TRIDENT_9397 0x9397 #define PCI_DEVICE_ID_TRIDENT_939A 0x939A #define PCI_DEVICE_ID_TRIDENT_9520 0x9520 #define PCI_DEVICE_ID_TRIDENT_9525 0x9525 #define PCI_DEVICE_ID_TRIDENT_9420 0x9420 #define PCI_DEVICE_ID_TRIDENT_9440 0x9440 #define PCI_DEVICE_ID_TRIDENT_9660 0x9660 #define PCI_DEVICE_ID_TRIDENT_9750 0x9750 #define PCI_DEVICE_ID_TRIDENT_9850 0x9850 #define PCI_DEVICE_ID_TRIDENT_9880 0x9880 #define PCI_DEVICE_ID_TRIDENT_8400 0x8400 #define PCI_DEVICE_ID_TRIDENT_8420 0x8420 #define PCI_DEVICE_ID_TRIDENT_8500 0x8500 #define PCI_VENDOR_ID_AI 0x1025 #define PCI_DEVICE_ID_AI_M1435 0x1435 #define PCI_VENDOR_ID_DELL 0x1028 #define PCI_DEVICE_ID_DELL_RACIII 0x0008 #define PCI_DEVICE_ID_DELL_RAC4 0x0012 #define PCI_DEVICE_ID_DELL_PERC5 0x0015 #define PCI_VENDOR_ID_MATROX 0x102B #define PCI_DEVICE_ID_MATROX_MGA_2 0x0518 #define PCI_DEVICE_ID_MATROX_MIL 0x0519 #define PCI_DEVICE_ID_MATROX_MYS 0x051A #define PCI_DEVICE_ID_MATROX_MIL_2 0x051b #define PCI_DEVICE_ID_MATROX_MYS_AGP 0x051e #define PCI_DEVICE_ID_MATROX_MIL_2_AGP 0x051f #define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10 #define PCI_DEVICE_ID_MATROX_G100_MM 0x1000 #define PCI_DEVICE_ID_MATROX_G100_AGP 0x1001 #define PCI_DEVICE_ID_MATROX_G200_PCI 0x0520 #define PCI_DEVICE_ID_MATROX_G200_AGP 0x0521 #define PCI_DEVICE_ID_MATROX_G400 0x0525 #define PCI_DEVICE_ID_MATROX_G200EV_PCI 0x0530 #define PCI_DEVICE_ID_MATROX_G550 0x2527 #define PCI_DEVICE_ID_MATROX_VIA 0x4536 #define PCI_VENDOR_ID_MOBILITY_ELECTRONICS 0x14f2 #define PCI_VENDOR_ID_CT 0x102c #define PCI_DEVICE_ID_CT_69000 0x00c0 #define PCI_DEVICE_ID_CT_65545 0x00d8 #define PCI_DEVICE_ID_CT_65548 0x00dc #define PCI_DEVICE_ID_CT_65550 0x00e0 #define PCI_DEVICE_ID_CT_65554 0x00e4 #define PCI_DEVICE_ID_CT_65555 0x00e5 #define PCI_VENDOR_ID_MIRO 0x1031 #define PCI_DEVICE_ID_MIRO_36050 0x5601 #define PCI_DEVICE_ID_MIRO_DC10PLUS 0x7efe #define PCI_DEVICE_ID_MIRO_DC30PLUS 0xd801 #define PCI_VENDOR_ID_NEC 0x1033 #define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */ #define PCI_DEVICE_ID_NEC_LOCAL 0x0002 /* Local Bridge */ #define PCI_DEVICE_ID_NEC_ATM 0x0003 /* ATM LAN Controller */ #define PCI_DEVICE_ID_NEC_R4000 0x0004 /* R4000 Bridge */ #define PCI_DEVICE_ID_NEC_486 0x0005 /* 486 Like Peripheral Bus Bridge */ #define PCI_DEVICE_ID_NEC_ACCEL_1 0x0006 /* Graphic Accelerator */ #define PCI_DEVICE_ID_NEC_UXBUS 0x0007 /* UX-Bus Bridge */ #define PCI_DEVICE_ID_NEC_ACCEL_2 0x0008 /* Graphic Accelerator */ #define PCI_DEVICE_ID_NEC_GRAPH 0x0009 /* PCI-CoreGraph Bridge */ #define PCI_DEVICE_ID_NEC_VL 0x0016 /* PCI-VL Bridge */ #define PCI_DEVICE_ID_NEC_STARALPHA2 0x002c /* STAR ALPHA2 */ #define PCI_DEVICE_ID_NEC_CBUS_2 0x002d /* PCI-Cbus Bridge */ #define PCI_DEVICE_ID_NEC_USB 0x0035 /* PCI-USB Host */ #define PCI_DEVICE_ID_NEC_CBUS_3 0x003b #define PCI_DEVICE_ID_NEC_NAPCCARD 0x003e #define PCI_DEVICE_ID_NEC_PCX2 0x0046 /* PowerVR */ #define PCI_DEVICE_ID_NEC_VRC5476 0x009b #define PCI_DEVICE_ID_NEC_VRC4173 0x00a5 #define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00a6 #define PCI_DEVICE_ID_NEC_PC9821CS01 0x800c /* PC-9821-CS01 */ #define PCI_DEVICE_ID_NEC_PC9821NRB06 0x800d /* PC-9821NR-B06 */ #define PCI_VENDOR_ID_FD 0x1036 #define PCI_DEVICE_ID_FD_36C70 0x0000 #define PCI_VENDOR_ID_SI 0x1039 #define PCI_DEVICE_ID_SI_5591_AGP 0x0001 #define PCI_DEVICE_ID_SI_6202 0x0002 #define PCI_DEVICE_ID_SI_503 0x0008 #define PCI_DEVICE_ID_SI_ACPI 0x0009 #define PCI_DEVICE_ID_SI_SMBUS 0x0016 #define PCI_DEVICE_ID_SI_LPC 0x0018 #define PCI_DEVICE_ID_SI_5597_VGA 0x0200 #define PCI_DEVICE_ID_SI_6205 0x0205 #define PCI_DEVICE_ID_SI_501 0x0406 #define PCI_DEVICE_ID_SI_496 0x0496 #define PCI_DEVICE_ID_SI_300 0x0300 #define PCI_DEVICE_ID_SI_315H 0x0310 #define PCI_DEVICE_ID_SI_315 0x0315 #define PCI_DEVICE_ID_SI_315PRO 0x0325 #define PCI_DEVICE_ID_SI_530 0x0530 #define PCI_DEVICE_ID_SI_540 0x0540 #define PCI_DEVICE_ID_SI_550 0x0550 #define PCI_DEVICE_ID_SI_540_VGA 0x5300 #define PCI_DEVICE_ID_SI_550_VGA 0x5315 #define PCI_DEVICE_ID_SI_620 0x0620 #define PCI_DEVICE_ID_SI_630 0x0630 #define PCI_DEVICE_ID_SI_633 0x0633 #define PCI_DEVICE_ID_SI_635 0x0635 #define PCI_DEVICE_ID_SI_640 0x0640 #define PCI_DEVICE_ID_SI_645 0x0645 #define PCI_DEVICE_ID_SI_646 0x0646 #define PCI_DEVICE_ID_SI_648 0x0648 #define PCI_DEVICE_ID_SI_650 0x0650 #define PCI_DEVICE_ID_SI_651 0x0651 #define PCI_DEVICE_ID_SI_655 0x0655 #define PCI_DEVICE_ID_SI_661 0x0661 #define PCI_DEVICE_ID_SI_730 0x0730 #define PCI_DEVICE_ID_SI_733 0x0733 #define PCI_DEVICE_ID_SI_630_VGA 0x6300 #define PCI_DEVICE_ID_SI_735 0x0735 #define PCI_DEVICE_ID_SI_740 0x0740 #define PCI_DEVICE_ID_SI_741 0x0741 #define PCI_DEVICE_ID_SI_745 0x0745 #define PCI_DEVICE_ID_SI_746 0x0746 #define PCI_DEVICE_ID_SI_755 0x0755 #define PCI_DEVICE_ID_SI_760 0x0760 #define PCI_DEVICE_ID_SI_900 0x0900 #define PCI_DEVICE_ID_SI_961 0x0961 #define PCI_DEVICE_ID_SI_962 0x0962 #define PCI_DEVICE_ID_SI_963 0x0963 #define PCI_DEVICE_ID_SI_965 0x0965 #define PCI_DEVICE_ID_SI_966 0x0966 #define PCI_DEVICE_ID_SI_968 0x0968 #define PCI_DEVICE_ID_SI_1180 0x1180 #define PCI_DEVICE_ID_SI_5511 0x5511 #define PCI_DEVICE_ID_SI_5513 0x5513 #define PCI_DEVICE_ID_SI_5517 0x5517 #define PCI_DEVICE_ID_SI_5518 0x5518 #define PCI_DEVICE_ID_SI_5571 0x5571 #define PCI_DEVICE_ID_SI_5581 0x5581 #define PCI_DEVICE_ID_SI_5582 0x5582 #define PCI_DEVICE_ID_SI_5591 0x5591 #define PCI_DEVICE_ID_SI_5596 0x5596 #define PCI_DEVICE_ID_SI_5597 0x5597 #define PCI_DEVICE_ID_SI_5598 0x5598 #define PCI_DEVICE_ID_SI_5600 0x5600 #define PCI_DEVICE_ID_SI_7012 0x7012 #define PCI_DEVICE_ID_SI_7013 0x7013 #define PCI_DEVICE_ID_SI_7016 0x7016 #define PCI_DEVICE_ID_SI_7018 0x7018 #define PCI_VENDOR_ID_HP 0x103c #define PCI_DEVICE_ID_HP_VISUALIZE_EG 0x1005 #define PCI_DEVICE_ID_HP_VISUALIZE_FX6 0x1006 #define PCI_DEVICE_ID_HP_VISUALIZE_FX4 0x1008 #define PCI_DEVICE_ID_HP_VISUALIZE_FX2 0x100a #define PCI_DEVICE_ID_HP_TACHYON 0x1028 #define PCI_DEVICE_ID_HP_TACHLITE 0x1029 #define PCI_DEVICE_ID_HP_J2585A 0x1030 #define PCI_DEVICE_ID_HP_J2585B 0x1031 #define PCI_DEVICE_ID_HP_J2973A 0x1040 #define PCI_DEVICE_ID_HP_J2970A 0x1042 #define PCI_DEVICE_ID_HP_DIVA 0x1048 #define PCI_DEVICE_ID_HP_DIVA_TOSCA1 0x1049 #define PCI_DEVICE_ID_HP_DIVA_TOSCA2 0x104A #define PCI_DEVICE_ID_HP_DIVA_MAESTRO 0x104B #define PCI_DEVICE_ID_HP_REO_IOC 0x10f1 #define PCI_DEVICE_ID_HP_VISUALIZE_FXE 0x108b #define PCI_DEVICE_ID_HP_DIVA_HALFDOME 0x1223 #define PCI_DEVICE_ID_HP_DIVA_KEYSTONE 0x1226 #define PCI_DEVICE_ID_HP_DIVA_POWERBAR 0x1227 #define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a #define PCI_DEVICE_ID_HP_PCIX_LBA 0x122e #define PCI_DEVICE_ID_HP_SX1000_IOC 0x127c #define PCI_DEVICE_ID_HP_DIVA_EVEREST 0x1282 #define PCI_DEVICE_ID_HP_DIVA_AUX 0x1290 #define PCI_DEVICE_ID_HP_DIVA_RMP3 0x1301 #define PCI_DEVICE_ID_HP_DIVA_HURRICANE 0x132a #define PCI_DEVICE_ID_HP_CISSA 0x3220 #define PCI_DEVICE_ID_HP_CISSC 0x3230 #define PCI_DEVICE_ID_HP_CISSD 0x3238 #define PCI_DEVICE_ID_HP_CISSE 0x323a #define PCI_DEVICE_ID_HP_CISSF 0x323b #define PCI_DEVICE_ID_HP_ZX2_IOC 0x4031 #define PCI_VENDOR_ID_PCTECH 0x1042 #define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000 #define PCI_DEVICE_ID_PCTECH_RZ1001 0x1001 #define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020 #define PCI_VENDOR_ID_ASUSTEK 0x1043 #define PCI_DEVICE_ID_ASUSTEK_0675 0x0675 #define PCI_VENDOR_ID_DPT 0x1044 #define PCI_DEVICE_ID_DPT 0xa400 #define PCI_VENDOR_ID_OPTI 0x1045 #define PCI_DEVICE_ID_OPTI_82C558 0xc558 #define PCI_DEVICE_ID_OPTI_82C621 0xc621 #define PCI_DEVICE_ID_OPTI_82C700 0xc700 #define PCI_DEVICE_ID_OPTI_82C825 0xd568 #define PCI_VENDOR_ID_ELSA 0x1048 #define PCI_DEVICE_ID_ELSA_MICROLINK 0x1000 #define PCI_DEVICE_ID_ELSA_QS3000 0x3000 #define PCI_VENDOR_ID_STMICRO 0x104A #define PCI_DEVICE_ID_STMICRO_USB_HOST 0xCC00 #define PCI_DEVICE_ID_STMICRO_USB_OHCI 0xCC01 #define PCI_DEVICE_ID_STMICRO_USB_OTG 0xCC02 #define PCI_DEVICE_ID_STMICRO_UART_HWFC 0xCC03 #define PCI_DEVICE_ID_STMICRO_UART_NO_HWFC 0xCC04 #define PCI_DEVICE_ID_STMICRO_SOC_DMA 0xCC05 #define PCI_DEVICE_ID_STMICRO_SATA 0xCC06 #define PCI_DEVICE_ID_STMICRO_I2C 0xCC07 #define PCI_DEVICE_ID_STMICRO_SPI_HS 0xCC08 #define PCI_DEVICE_ID_STMICRO_MAC 0xCC09 #define PCI_DEVICE_ID_STMICRO_SDIO_EMMC 0xCC0A #define PCI_DEVICE_ID_STMICRO_SDIO 0xCC0B #define PCI_DEVICE_ID_STMICRO_GPIO 0xCC0C #define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D #define PCI_DEVICE_ID_STMICRO_AUDIO_ROUTER_DMA 0xCC0E #define PCI_DEVICE_ID_STMICRO_AUDIO_ROUTER_SRCS 0xCC0F #define PCI_DEVICE_ID_STMICRO_AUDIO_ROUTER_MSPS 0xCC10 #define PCI_DEVICE_ID_STMICRO_CAN 0xCC11 #define PCI_DEVICE_ID_STMICRO_MLB 0xCC12 #define PCI_DEVICE_ID_STMICRO_DBP 0xCC13 #define PCI_DEVICE_ID_STMICRO_SATA_PHY 0xCC14 #define PCI_DEVICE_ID_STMICRO_ESRAM 0xCC15 #define PCI_DEVICE_ID_STMICRO_VIC 0xCC16 #define PCI_VENDOR_ID_BUSLOGIC 0x104B #define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140 #define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040 #define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130 #define PCI_VENDOR_ID_TI 0x104c #define PCI_DEVICE_ID_TI_TVP4020 0x3d07 #define PCI_DEVICE_ID_TI_4450 0x8011 #define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 #define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 #define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 #define PCI_DEVICE_ID_TI_X515 0x8036 #define PCI_DEVICE_ID_TI_XX12 0x8039 #define PCI_DEVICE_ID_TI_XX12_FM 0x803b #define PCI_DEVICE_ID_TI_XIO2000A 0x8231 #define PCI_DEVICE_ID_TI_1130 0xac12 #define PCI_DEVICE_ID_TI_1031 0xac13 #define PCI_DEVICE_ID_TI_1131 0xac15 #define PCI_DEVICE_ID_TI_1250 0xac16 #define PCI_DEVICE_ID_TI_1220 0xac17 #define PCI_DEVICE_ID_TI_1221 0xac19 #define PCI_DEVICE_ID_TI_1210 0xac1a #define PCI_DEVICE_ID_TI_1450 0xac1b #define PCI_DEVICE_ID_TI_1225 0xac1c #define PCI_DEVICE_ID_TI_1251A 0xac1d #define PCI_DEVICE_ID_TI_1211 0xac1e #define PCI_DEVICE_ID_TI_1251B 0xac1f #define PCI_DEVICE_ID_TI_4410 0xac41 #define PCI_DEVICE_ID_TI_4451 0xac42 #define PCI_DEVICE_ID_TI_4510 0xac44 #define PCI_DEVICE_ID_TI_4520 0xac46 #define PCI_DEVICE_ID_TI_7510 0xac47 #define PCI_DEVICE_ID_TI_7610 0xac48 #define PCI_DEVICE_ID_TI_7410 0xac49 #define PCI_DEVICE_ID_TI_1410 0xac50 #define PCI_DEVICE_ID_TI_1420 0xac51 #define PCI_DEVICE_ID_TI_1451A 0xac52 #define PCI_DEVICE_ID_TI_1620 0xac54 #define PCI_DEVICE_ID_TI_1520 0xac55 #define PCI_DEVICE_ID_TI_1510 0xac56 #define PCI_DEVICE_ID_TI_X620 0xac8d #define PCI_DEVICE_ID_TI_X420 0xac8e #define PCI_DEVICE_ID_TI_XX20_FM 0xac8f #define PCI_VENDOR_ID_SONY 0x104d /* Winbond have two vendor IDs! See 0x10ad as well */ #define PCI_VENDOR_ID_WINBOND2 0x1050 #define PCI_DEVICE_ID_WINBOND2_89C940F 0x5a5a #define PCI_DEVICE_ID_WINBOND2_6692 0x6692 #define PCI_VENDOR_ID_ANIGMA 0x1051 #define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100 #define PCI_VENDOR_ID_EFAR 0x1055 #define PCI_DEVICE_ID_EFAR_SLC90E66_1 0x9130 #define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463 #define PCI_VENDOR_ID_MOTOROLA 0x1057 #define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001 #define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 #define PCI_DEVICE_ID_MOTOROLA_MPC107 0x0004 #define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 #define PCI_DEVICE_ID_MOTOROLA_FALCON 0x4802 #define PCI_DEVICE_ID_MOTOROLA_HAWK 0x4803 #define PCI_DEVICE_ID_MOTOROLA_HARRIER 0x480b #define PCI_DEVICE_ID_MOTOROLA_MPC5200 0x5803 #define PCI_DEVICE_ID_MOTOROLA_MPC5200B 0x5809 #define PCI_VENDOR_ID_PROMISE 0x105a #define PCI_DEVICE_ID_PROMISE_20265 0x0d30 #define PCI_DEVICE_ID_PROMISE_20267 0x4d30 #define PCI_DEVICE_ID_PROMISE_20246 0x4d33 #define PCI_DEVICE_ID_PROMISE_20262 0x4d38 #define PCI_DEVICE_ID_PROMISE_20263 0x0D38 #define PCI_DEVICE_ID_PROMISE_20268 0x4d68 #define PCI_DEVICE_ID_PROMISE_20269 0x4d69 #define PCI_DEVICE_ID_PROMISE_20270 0x6268 #define PCI_DEVICE_ID_PROMISE_20271 0x6269 #define PCI_DEVICE_ID_PROMISE_20275 0x1275 #define PCI_DEVICE_ID_PROMISE_20276 0x5275 #define PCI_DEVICE_ID_PROMISE_20277 0x7275 #define PCI_VENDOR_ID_FOXCONN 0x105b #define PCI_VENDOR_ID_UMC 0x1060 #define PCI_DEVICE_ID_UMC_UM8673F 0x0101 #define PCI_DEVICE_ID_UMC_UM8886BF 0x673a #define PCI_DEVICE_ID_UMC_UM8886A 0x886a #define PCI_VENDOR_ID_PICOPOWER 0x1066 #define PCI_DEVICE_ID_PICOPOWER_PT86C523 0x0002 #define PCI_DEVICE_ID_PICOPOWER_PT86C523BBP 0x8002 #define PCI_VENDOR_ID_MYLEX 0x1069 #define PCI_DEVICE_ID_MYLEX_DAC960_P 0x0001 #define PCI_DEVICE_ID_MYLEX_DAC960_PD 0x0002 #define PCI_DEVICE_ID_MYLEX_DAC960_PG 0x0010 #define PCI_DEVICE_ID_MYLEX_DAC960_LA 0x0020 #define PCI_DEVICE_ID_MYLEX_DAC960_LP 0x0050 #define PCI_DEVICE_ID_MYLEX_DAC960_BA 0xBA56 #define PCI_DEVICE_ID_MYLEX_DAC960_GEM 0xB166 #define PCI_VENDOR_ID_APPLE 0x106b #define PCI_DEVICE_ID_APPLE_BANDIT 0x0001 #define PCI_DEVICE_ID_APPLE_HYDRA 0x000e #define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 #define PCI_DEVICE_ID_APPLE_UNI_N_GMACP 0x0024 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P 0x0027 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP15 0x002d #define PCI_DEVICE_ID_APPLE_UNI_N_PCI15 0x002e #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2 0x0032 #define PCI_DEVICE_ID_APPLE_UNI_N_ATA 0x0033 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP2 0x0034 #define PCI_DEVICE_ID_APPLE_IPID_ATA100 0x003b #define PCI_DEVICE_ID_APPLE_K2_ATA100 0x0043 #define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b #define PCI_DEVICE_ID_APPLE_K2_GMAC 0x004c #define PCI_DEVICE_ID_APPLE_SH_ATA 0x0050 #define PCI_DEVICE_ID_APPLE_SH_SUNGEM 0x0051 #define PCI_DEVICE_ID_APPLE_U3L_AGP 0x0058 #define PCI_DEVICE_ID_APPLE_U3H_AGP 0x0059 #define PCI_DEVICE_ID_APPLE_U4_PCIE 0x005b #define PCI_DEVICE_ID_APPLE_IPID2_AGP 0x0066 #define PCI_DEVICE_ID_APPLE_IPID2_ATA 0x0069 #define PCI_DEVICE_ID_APPLE_IPID2_FW 0x006a #define PCI_DEVICE_ID_APPLE_IPID2_GMAC 0x006b #define PCI_DEVICE_ID_APPLE_TIGON3 0x1645 #define PCI_VENDOR_ID_YAMAHA 0x1073 #define PCI_DEVICE_ID_YAMAHA_724 0x0004 #define PCI_DEVICE_ID_YAMAHA_724F 0x000d #define PCI_DEVICE_ID_YAMAHA_740 0x000a #define PCI_DEVICE_ID_YAMAHA_740C 0x000c #define PCI_DEVICE_ID_YAMAHA_744 0x0010 #define PCI_DEVICE_ID_YAMAHA_754 0x0012 #define PCI_VENDOR_ID_QLOGIC 0x1077 #define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016 #define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020 #define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080 #define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216 #define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240 #define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280 #define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100 #define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200 #define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300 #define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312 #define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322 #define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312 #define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322 #define PCI_DEVICE_ID_QLOGIC_ISP2422 0x2422 #define PCI_DEVICE_ID_QLOGIC_ISP2432 0x2432 #define PCI_DEVICE_ID_QLOGIC_ISP2512 0x2512 #define PCI_DEVICE_ID_QLOGIC_ISP2522 0x2522 #define PCI_DEVICE_ID_QLOGIC_ISP5422 0x5422 #define PCI_DEVICE_ID_QLOGIC_ISP5432 0x5432 #define PCI_VENDOR_ID_CYRIX 0x1078 #define PCI_DEVICE_ID_CYRIX_5510 0x0000 #define PCI_DEVICE_ID_CYRIX_PCI_MASTER 0x0001 #define PCI_DEVICE_ID_CYRIX_5520 0x0002 #define PCI_DEVICE_ID_CYRIX_5530_LEGACY 0x0100 #define PCI_DEVICE_ID_CYRIX_5530_IDE 0x0102 #define PCI_DEVICE_ID_CYRIX_5530_AUDIO 0x0103 #define PCI_DEVICE_ID_CYRIX_5530_VIDEO 0x0104 #define PCI_VENDOR_ID_CONTAQ 0x1080 #define PCI_DEVICE_ID_CONTAQ_82C693 0xc693 #define PCI_VENDOR_ID_OLICOM 0x108d #define PCI_DEVICE_ID_OLICOM_OC2325 0x0012 #define PCI_DEVICE_ID_OLICOM_OC2183 0x0013 #define PCI_DEVICE_ID_OLICOM_OC2326 0x0014 #define PCI_VENDOR_ID_SUN 0x108e #define PCI_DEVICE_ID_SUN_EBUS 0x1000 #define PCI_DEVICE_ID_SUN_HAPPYMEAL 0x1001 #define PCI_DEVICE_ID_SUN_RIO_EBUS 0x1100 #define PCI_DEVICE_ID_SUN_RIO_GEM 0x1101 #define PCI_DEVICE_ID_SUN_RIO_1394 0x1102 #define PCI_DEVICE_ID_SUN_RIO_USB 0x1103 #define PCI_DEVICE_ID_SUN_GEM 0x2bad #define PCI_DEVICE_ID_SUN_SIMBA 0x5000 #define PCI_DEVICE_ID_SUN_PBM 0x8000 #define PCI_DEVICE_ID_SUN_SCHIZO 0x8001 #define PCI_DEVICE_ID_SUN_SABRE 0xa000 #define PCI_DEVICE_ID_SUN_HUMMINGBIRD 0xa001 #define PCI_DEVICE_ID_SUN_TOMATILLO 0xa801 #define PCI_DEVICE_ID_SUN_CASSINI 0xabba #define PCI_VENDOR_ID_NI 0x1093 #define PCI_DEVICE_ID_NI_PCI2322 0xd130 #define PCI_DEVICE_ID_NI_PCI2324 0xd140 #define PCI_DEVICE_ID_NI_PCI2328 0xd150 #define PCI_DEVICE_ID_NI_PXI8422_2322 0xd190 #define PCI_DEVICE_ID_NI_PXI8422_2324 0xd1a0 #define PCI_DEVICE_ID_NI_PXI8420_2322 0xd1d0 #define PCI_DEVICE_ID_NI_PXI8420_2324 0xd1e0 #define PCI_DEVICE_ID_NI_PXI8420_2328 0xd1f0 #define PCI_DEVICE_ID_NI_PXI8420_23216 0xd1f1 #define PCI_DEVICE_ID_NI_PCI2322I 0xd250 #define PCI_DEVICE_ID_NI_PCI2324I 0xd270 #define PCI_DEVICE_ID_NI_PCI23216 0xd2b0 #define PCI_DEVICE_ID_NI_PXI8430_2322 0x7080 #define PCI_DEVICE_ID_NI_PCI8430_2322 0x70db #define PCI_DEVICE_ID_NI_PXI8430_2324 0x70dd #define PCI_DEVICE_ID_NI_PCI8430_2324 0x70df #define PCI_DEVICE_ID_NI_PXI8430_2328 0x70e2 #define PCI_DEVICE_ID_NI_PCI8430_2328 0x70e4 #define PCI_DEVICE_ID_NI_PXI8430_23216 0x70e6 #define PCI_DEVICE_ID_NI_PCI8430_23216 0x70e7 #define PCI_DEVICE_ID_NI_PXI8432_2322 0x70e8 #define PCI_DEVICE_ID_NI_PCI8432_2322 0x70ea #define PCI_DEVICE_ID_NI_PXI8432_2324 0x70ec #define PCI_DEVICE_ID_NI_PCI8432_2324 0x70ee #define PCI_VENDOR_ID_CMD 0x1095 #define PCI_DEVICE_ID_CMD_643 0x0643 #define PCI_DEVICE_ID_CMD_646 0x0646 #define PCI_DEVICE_ID_CMD_648 0x0648 #define PCI_DEVICE_ID_CMD_649 0x0649 #define PCI_DEVICE_ID_SII_680 0x0680 #define PCI_DEVICE_ID_SII_3112 0x3112 #define PCI_DEVICE_ID_SII_1210SA 0x0240 #define PCI_VENDOR_ID_BROOKTREE 0x109e #define PCI_DEVICE_ID_BROOKTREE_878 0x0878 #define PCI_DEVICE_ID_BROOKTREE_879 0x0879 #define PCI_VENDOR_ID_SGI 0x10a9 #define PCI_DEVICE_ID_SGI_IOC3 0x0003 #define PCI_DEVICE_ID_SGI_LITHIUM 0x1002 #define PCI_DEVICE_ID_SGI_IOC4 0x100a #define PCI_VENDOR_ID_WINBOND 0x10ad #define PCI_DEVICE_ID_WINBOND_82C105 0x0105 #define PCI_DEVICE_ID_WINBOND_83C553 0x0565 #define PCI_VENDOR_ID_PLX 0x10b5 #define PCI_DEVICE_ID_PLX_R685 0x1030 #define PCI_DEVICE_ID_PLX_ROMULUS 0x106a #define PCI_DEVICE_ID_PLX_SPCOM800 0x1076 #define PCI_DEVICE_ID_PLX_1077 0x1077 #define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 #define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 #define PCI_DEVICE_ID_PLX_R753 0x1152 #define PCI_DEVICE_ID_PLX_OLITEC 0x1187 #define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196 #define PCI_DEVICE_ID_PLX_9030 0x9030 #define PCI_DEVICE_ID_PLX_9050 0x9050 #define PCI_DEVICE_ID_PLX_9056 0x9056 #define PCI_DEVICE_ID_PLX_9080 0x9080 #define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001 #define PCI_VENDOR_ID_MADGE 0x10b6 #define PCI_DEVICE_ID_MADGE_MK2 0x0002 #define PCI_VENDOR_ID_3COM 0x10b7 #define PCI_DEVICE_ID_3COM_3C985 0x0001 #define PCI_DEVICE_ID_3COM_3C940 0x1700 #define PCI_DEVICE_ID_3COM_3C339 0x3390 #define PCI_DEVICE_ID_3COM_3C359 0x3590 #define PCI_DEVICE_ID_3COM_3C940B 0x80eb #define PCI_DEVICE_ID_3COM_3CR990 0x9900 #define PCI_DEVICE_ID_3COM_3CR990_TX_95 0x9902 #define PCI_DEVICE_ID_3COM_3CR990_TX_97 0x9903 #define PCI_DEVICE_ID_3COM_3CR990B 0x9904 #define PCI_DEVICE_ID_3COM_3CR990_FX 0x9905 #define PCI_DEVICE_ID_3COM_3CR990SVR95 0x9908 #define PCI_DEVICE_ID_3COM_3CR990SVR97 0x9909 #define PCI_DEVICE_ID_3COM_3CR990SVR 0x990a #define PCI_VENDOR_ID_AL 0x10b9 #define PCI_DEVICE_ID_AL_M1533 0x1533 #define PCI_DEVICE_ID_AL_M1535 0x1535 #define PCI_DEVICE_ID_AL_M1541 0x1541 #define PCI_DEVICE_ID_AL_M1563 0x1563 #define PCI_DEVICE_ID_AL_M1621 0x1621 #define PCI_DEVICE_ID_AL_M1631 0x1631 #define PCI_DEVICE_ID_AL_M1632 0x1632 #define PCI_DEVICE_ID_AL_M1641 0x1641 #define PCI_DEVICE_ID_AL_M1644 0x1644 #define PCI_DEVICE_ID_AL_M1647 0x1647 #define PCI_DEVICE_ID_AL_M1651 0x1651 #define PCI_DEVICE_ID_AL_M1671 0x1671 #define PCI_DEVICE_ID_AL_M1681 0x1681 #define PCI_DEVICE_ID_AL_M1683 0x1683 #define PCI_DEVICE_ID_AL_M1689 0x1689 #define PCI_DEVICE_ID_AL_M5219 0x5219 #define PCI_DEVICE_ID_AL_M5228 0x5228 #define PCI_DEVICE_ID_AL_M5229 0x5229 #define PCI_DEVICE_ID_AL_M5451 0x5451 #define PCI_DEVICE_ID_AL_M7101 0x7101 #define PCI_VENDOR_ID_NEOMAGIC 0x10c8 #define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 #define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 #define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016 #define PCI_VENDOR_ID_TCONRAD 0x10da #define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508 #define PCI_VENDOR_ID_NVIDIA 0x10de #define PCI_DEVICE_ID_NVIDIA_TNT 0x0020 #define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028 #define PCI_DEVICE_ID_NVIDIA_UTNT2 0x0029 #define PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN 0x002a #define PCI_DEVICE_ID_NVIDIA_VTNT2 0x002C #define PCI_DEVICE_ID_NVIDIA_UVTNT2 0x002D #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS 0x0034 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE 0x0035 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA 0x0036 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2 0x003e #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA 0x0040 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800 0x0041 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE 0x0042 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x0045 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000 0x004E #define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS 0x0052 #define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE 0x0053 #define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA 0x0054 #define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2 0x0055 #define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 #define PCI_DEVICE_ID_NVIDIA_CK804_PCIE 0x005d #define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 #define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065 #define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069 #define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a #define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS 0x0084 #define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE 0x0085 #define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089 #define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a #define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA 0x008e #define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT 0x0090 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX 0x0091 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800 0x0098 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX 0x0099 #define PCI_DEVICE_ID_NVIDIA_ITNT2 0x00A0 #define PCI_DEVICE_ID_GEFORCE_6800A 0x00c1 #define PCI_DEVICE_ID_GEFORCE_6800A_LE 0x00c2 #define PCI_DEVICE_ID_GEFORCE_GO_6800 0x00c8 #define PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA 0x00c9 #define PCI_DEVICE_ID_QUADRO_FX_GO1400 0x00cc #define PCI_DEVICE_ID_QUADRO_FX_1400 0x00ce #define PCI_DEVICE_ID_NVIDIA_NFORCE3 0x00d1 #define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS 0x00d4 #define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE 0x00d5 #define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9 #define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da #define PCI_DEVICE_ID_NVIDIA_NFORCE3S 0x00e1 #define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA 0x00e3 #define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS 0x00e4 #define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE 0x00e5 #define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea #define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2 0x00ee #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_ALT1 0x00f0 #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT1 0x00f1 #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2 #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3 #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9 #define PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280 0x00fd #define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101 #define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX 0x0110 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2 0x0111 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO 0x0112 #define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR 0x0113 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT 0x0140 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600 0x0141 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL 0x0145 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540 0x014E #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200 0x014F #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS 0x0150 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2 0x0151 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA 0x0152 #define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO 0x0153 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE 0x0161 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200 0x0164 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250 0x0166 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1 0x0167 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1 0x0168 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460 0x0170 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440 0x0171 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420 0x0172 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE 0x0173 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO 0x0174 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO 0x0175 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32 0x0176 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO 0x0177 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL 0x0178 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64 0x0179 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_200 0x017A #define PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL 0x017B #define PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL 0x017C #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16 0x017D #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X 0x0181 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X 0x0182 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X 0x0183 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_4000 0x0185 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO 0x0186 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO 0x0187 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL 0x0188 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC 0x0189 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS 0x018A #define PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL 0x018B #define PCI_DEVICE_ID_NVIDIA_IGEFORCE2 0x01a0 #define PCI_DEVICE_ID_NVIDIA_NFORCE 0x01a4 #define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 #define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01b4 #define PCI_DEVICE_ID_NVIDIA_NFORCE_IDE 0x01bc #define PCI_DEVICE_ID_NVIDIA_MCP1_MODEM 0x01c1 #define PCI_DEVICE_ID_NVIDIA_NFORCE2 0x01e0 #define PCI_DEVICE_ID_NVIDIA_GEFORCE3 0x0200 #define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1 0x0201 #define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2 0x0202 #define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC 0x0203 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B 0x0211 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE 0x0212 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT 0x0215 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600 0x0250 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400 0x0251 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200 0x0253 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800 0x0280 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X 0x0281 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE 0x0282 #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO 0x0286 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL 0x0288 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL 0x0289 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL 0x028C #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA 0x0301 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800 0x0302 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000 0x0308 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000 0x0309 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA 0x0311 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600 0x0312 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE 0x0314 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600 0x031A #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650 0x031B #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700 0x031C #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200 0x0320 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA 0x0321 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1 0x0322 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE 0x0323 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200 0x0324 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250 0x0325 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500 0x0326 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100 0x0327 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32 0x0328 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200 0x0329 #define PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI 0x032A #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500 0x032B #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300 0x032C #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100 0x032D #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA 0x0330 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900 0x0331 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT 0x0332 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA 0x0333 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT 0x0334 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000 0x0338 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700 0x033F #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA 0x0341 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700 0x0342 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE 0x0343 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE 0x0344 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1 0x0347 #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E #define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0 0x0360 #define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4 0x0364 #define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2 0x03F6 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3 0x03F7 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS 0x0446 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS 0x0542 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS 0x0752 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS 0x07D8 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS 0x0AA2 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA 0x0D85 #define PCI_VENDOR_ID_IMS 0x10e0 #define PCI_DEVICE_ID_IMS_TT128 0x9128 #define PCI_DEVICE_ID_IMS_TT3D 0x9135 #define PCI_VENDOR_ID_INTERG 0x10ea #define PCI_DEVICE_ID_INTERG_1682 0x1682 #define PCI_DEVICE_ID_INTERG_2000 0x2000 #define PCI_DEVICE_ID_INTERG_2010 0x2010 #define PCI_DEVICE_ID_INTERG_5000 0x5000 #define PCI_DEVICE_ID_INTERG_5050 0x5050 #define PCI_VENDOR_ID_REALTEK 0x10ec #define PCI_DEVICE_ID_REALTEK_8139 0x8139 #define PCI_VENDOR_ID_XILINX 0x10ee #define PCI_DEVICE_ID_RME_DIGI96 0x3fc0 #define PCI_DEVICE_ID_RME_DIGI96_8 0x3fc1 #define PCI_DEVICE_ID_RME_DIGI96_8_PRO 0x3fc2 #define PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST 0x3fc3 #define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5 #define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6 #define PCI_VENDOR_ID_INIT 0x1101 #define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */ #define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 #define PCI_DEVICE_ID_CREATIVE_20K1 0x0005 #define PCI_DEVICE_ID_CREATIVE_20K2 0x000b #define PCI_SUBDEVICE_ID_CREATIVE_SB0760 0x0024 #define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041 #define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042 #define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043 #define PCI_SUBDEVICE_ID_CREATIVE_SB1270 0x0062 #define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000 #define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */ #define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 #define PCI_VENDOR_ID_TTI 0x1103 #define PCI_DEVICE_ID_TTI_HPT343 0x0003 #define PCI_DEVICE_ID_TTI_HPT366 0x0004 #define PCI_DEVICE_ID_TTI_HPT372 0x0005 #define PCI_DEVICE_ID_TTI_HPT302 0x0006 #define PCI_DEVICE_ID_TTI_HPT371 0x0007 #define PCI_DEVICE_ID_TTI_HPT374 0x0008 #define PCI_DEVICE_ID_TTI_HPT372N 0x0009 /* apparently a 372N variant? */ #define PCI_VENDOR_ID_VIA 0x1106 #define PCI_DEVICE_ID_VIA_8763_0 0x0198 #define PCI_DEVICE_ID_VIA_8380_0 0x0204 #define PCI_DEVICE_ID_VIA_3238_0 0x0238 #define PCI_DEVICE_ID_VIA_PT880 0x0258 #define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0308 #define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259 #define PCI_DEVICE_ID_VIA_3269_0 0x0269 #define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282 #define PCI_DEVICE_ID_VIA_3296_0 0x0296 #define PCI_DEVICE_ID_VIA_8363_0 0x0305 #define PCI_DEVICE_ID_VIA_P4M800CE 0x0314 #define PCI_DEVICE_ID_VIA_P4M890 0x0327 #define PCI_DEVICE_ID_VIA_VT3324 0x0324 #define PCI_DEVICE_ID_VIA_VT3336 0x0336 #define PCI_DEVICE_ID_VIA_VT3351 0x0351 #define PCI_DEVICE_ID_VIA_VT3364 0x0364 #define PCI_DEVICE_ID_VIA_8371_0 0x0391 #define PCI_DEVICE_ID_VIA_6415 0x0415 #define PCI_DEVICE_ID_VIA_8501_0 0x0501 #define PCI_DEVICE_ID_VIA_82C561 0x0561 #define PCI_DEVICE_ID_VIA_82C586_1 0x0571 #define PCI_DEVICE_ID_VIA_82C576 0x0576 #define PCI_DEVICE_ID_VIA_82C586_0 0x0586 #define PCI_DEVICE_ID_VIA_82C596 0x0596 #define PCI_DEVICE_ID_VIA_82C597_0 0x0597 #define PCI_DEVICE_ID_VIA_82C598_0 0x0598 #define PCI_DEVICE_ID_VIA_8601_0 0x0601 #define PCI_DEVICE_ID_VIA_8605_0 0x0605 #define PCI_DEVICE_ID_VIA_82C686 0x0686 #define PCI_DEVICE_ID_VIA_82C691_0 0x0691 #define PCI_DEVICE_ID_VIA_82C576_1 0x1571 #define PCI_DEVICE_ID_VIA_82C586_2 0x3038 #define PCI_DEVICE_ID_VIA_82C586_3 0x3040 #define PCI_DEVICE_ID_VIA_82C596_3 0x3050 #define PCI_DEVICE_ID_VIA_82C596B_3 0x3051 #define PCI_DEVICE_ID_VIA_82C686_4 0x3057 #define PCI_DEVICE_ID_VIA_82C686_5 0x3058 #define PCI_DEVICE_ID_VIA_8233_5 0x3059 #define PCI_DEVICE_ID_VIA_8233_0 0x3074 #define PCI_DEVICE_ID_VIA_8633_0 0x3091 #define PCI_DEVICE_ID_VIA_8367_0 0x3099 #define PCI_DEVICE_ID_VIA_8653_0 0x3101 #define PCI_DEVICE_ID_VIA_8622 0x3102 #define PCI_DEVICE_ID_VIA_8235_USB_2 0x3104 #define PCI_DEVICE_ID_VIA_8233C_0 0x3109 #define PCI_DEVICE_ID_VIA_8361 0x3112 #define PCI_DEVICE_ID_VIA_XM266 0x3116 #define PCI_DEVICE_ID_VIA_612X 0x3119 #define PCI_DEVICE_ID_VIA_862X_0 0x3123 #define PCI_DEVICE_ID_VIA_8753_0 0x3128 #define PCI_DEVICE_ID_VIA_8233A 0x3147 #define PCI_DEVICE_ID_VIA_8703_51_0 0x3148 #define PCI_DEVICE_ID_VIA_8237_SATA 0x3149 #define PCI_DEVICE_ID_VIA_XN266 0x3156 #define PCI_DEVICE_ID_VIA_6410 0x3164 #define PCI_DEVICE_ID_VIA_8754C_0 0x3168 #define PCI_DEVICE_ID_VIA_8235 0x3177 #define PCI_DEVICE_ID_VIA_8385_0 0x3188 #define PCI_DEVICE_ID_VIA_8377_0 0x3189 #define PCI_DEVICE_ID_VIA_8378_0 0x3205 #define PCI_DEVICE_ID_VIA_8783_0 0x3208 #define PCI_DEVICE_ID_VIA_8237 0x3227 #define PCI_DEVICE_ID_VIA_8251 0x3287 #define PCI_DEVICE_ID_VIA_8261 0x3402 #define PCI_DEVICE_ID_VIA_8237A 0x3337 #define PCI_DEVICE_ID_VIA_8237S 0x3372 #define PCI_DEVICE_ID_VIA_SATA_EIDE 0x5324 #define PCI_DEVICE_ID_VIA_8231 0x8231 #define PCI_DEVICE_ID_VIA_8231_4 0x8235 #define PCI_DEVICE_ID_VIA_8365_1 0x8305 #define PCI_DEVICE_ID_VIA_CX700 0x8324 #define PCI_DEVICE_ID_VIA_CX700_IDE 0x0581 #define PCI_DEVICE_ID_VIA_VX800 0x8353 #define PCI_DEVICE_ID_VIA_VX855 0x8409 #define PCI_DEVICE_ID_VIA_8371_1 0x8391 #define PCI_DEVICE_ID_VIA_82C598_1 0x8598 #define PCI_DEVICE_ID_VIA_838X_1 0xB188 #define PCI_DEVICE_ID_VIA_83_87XX_1 0xB198 #define PCI_DEVICE_ID_VIA_VX855_IDE 0xC409 #define PCI_DEVICE_ID_VIA_ANON 0xFFFF #define PCI_VENDOR_ID_SIEMENS 0x110A #define PCI_DEVICE_ID_SIEMENS_DSCC4 0x2102 #define PCI_VENDOR_ID_VORTEX 0x1119 #define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000 #define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001 #define PCI_DEVICE_ID_VORTEX_GDT6x10 0x0002 #define PCI_DEVICE_ID_VORTEX_GDT6x20 0x0003 #define PCI_DEVICE_ID_VORTEX_GDT6530 0x0004 #define PCI_DEVICE_ID_VORTEX_GDT6550 0x0005 #define PCI_DEVICE_ID_VORTEX_GDT6x17 0x0006 #define PCI_DEVICE_ID_VORTEX_GDT6x27 0x0007 #define PCI_DEVICE_ID_VORTEX_GDT6537 0x0008 #define PCI_DEVICE_ID_VORTEX_GDT6557 0x0009 #define PCI_DEVICE_ID_VORTEX_GDT6x15 0x000a #define PCI_DEVICE_ID_VORTEX_GDT6x25 0x000b #define PCI_DEVICE_ID_VORTEX_GDT6535 0x000c #define PCI_DEVICE_ID_VORTEX_GDT6555 0x000d #define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x0100 #define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x0101 #define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x0102 #define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x0103 #define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x0104 #define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x0105 #define PCI_VENDOR_ID_EF 0x111a #define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000 #define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002 #define PCI_DEVICE_ID_EF_ATM_LANAI2 0x0003 #define PCI_DEVICE_ID_EF_ATM_LANAIHB 0x0005 #define PCI_VENDOR_ID_IDT 0x111d #define PCI_DEVICE_ID_IDT_IDT77201 0x0001 #define PCI_VENDOR_ID_FORE 0x1127 #define PCI_DEVICE_ID_FORE_PCA200E 0x0300 #define PCI_VENDOR_ID_PHILIPS 0x1131 #define PCI_DEVICE_ID_PHILIPS_SAA7146 0x7146 #define PCI_DEVICE_ID_PHILIPS_SAA9730 0x9730 #define PCI_VENDOR_ID_EICON 0x1133 #define PCI_DEVICE_ID_EICON_DIVA20 0xe002 #define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004 #define PCI_DEVICE_ID_EICON_DIVA201 0xe005 #define PCI_DEVICE_ID_EICON_DIVA202 0xe00b #define PCI_DEVICE_ID_EICON_MAESTRA 0xe010 #define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012 #define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013 #define PCI_DEVICE_ID_EICON_MAESTRAP 0xe014 #define PCI_VENDOR_ID_CISCO 0x1137 #define PCI_VENDOR_ID_ZIATECH 0x1138 #define PCI_DEVICE_ID_ZIATECH_5550_HC 0x5550 #define PCI_VENDOR_ID_SYSKONNECT 0x1148 #define PCI_DEVICE_ID_SYSKONNECT_TR 0x4200 #define PCI_DEVICE_ID_SYSKONNECT_GE 0x4300 #define PCI_DEVICE_ID_SYSKONNECT_YU 0x4320 #define PCI_DEVICE_ID_SYSKONNECT_9DXX 0x4400 #define PCI_DEVICE_ID_SYSKONNECT_9MXX 0x4500 #define PCI_VENDOR_ID_DIGI 0x114f #define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070 #define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 #define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 #define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 #define PCI_DEVICE_ID_DIGI_NEO_8 0x00B1 #define PCI_DEVICE_ID_NEO_2DB9 0x00C8 #define PCI_DEVICE_ID_NEO_2DB9PRI 0x00C9 #define PCI_DEVICE_ID_NEO_2RJ45 0x00CA #define PCI_DEVICE_ID_NEO_2RJ45PRI 0x00CB #define PCIE_DEVICE_ID_NEO_4_IBM 0x00F4 #define PCI_VENDOR_ID_XIRCOM 0x115d #define PCI_DEVICE_ID_XIRCOM_RBM56G 0x0101 #define PCI_DEVICE_ID_XIRCOM_X3201_MDM 0x0103 #define PCI_VENDOR_ID_SERVERWORKS 0x1166 #define PCI_DEVICE_ID_SERVERWORKS_HE 0x0008 #define PCI_DEVICE_ID_SERVERWORKS_LE 0x0009 #define PCI_DEVICE_ID_SERVERWORKS_GCNB_LE 0x0017 #define PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB 0x0036 #define PCI_DEVICE_ID_SERVERWORKS_EPB 0x0103 #define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132 #define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200 #define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 #define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203 #define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205 #define PCI_DEVICE_ID_SERVERWORKS_OSB4IDE 0x0211 #define PCI_DEVICE_ID_SERVERWORKS_CSB5IDE 0x0212 #define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE 0x0213 #define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214 #define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217 #define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 #define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408 #define PCI_VENDOR_ID_SBE 0x1176 #define PCI_DEVICE_ID_SBE_WANXL100 0x0301 #define PCI_DEVICE_ID_SBE_WANXL200 0x0302 #define PCI_DEVICE_ID_SBE_WANXL400 0x0104 #define PCI_SUBDEVICE_ID_SBE_T3E3 0x0009 #define PCI_SUBDEVICE_ID_SBE_2T3E3_P0 0x0901 #define PCI_SUBDEVICE_ID_SBE_2T3E3_P1 0x0902 #define PCI_VENDOR_ID_TOSHIBA 0x1179 #define PCI_DEVICE_ID_TOSHIBA_PICCOLO_1 0x0101 #define PCI_DEVICE_ID_TOSHIBA_PICCOLO_2 0x0102 #define PCI_DEVICE_ID_TOSHIBA_PICCOLO_3 0x0103 #define PCI_DEVICE_ID_TOSHIBA_PICCOLO_5 0x0105 #define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a #define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f #define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x0617 #define PCI_VENDOR_ID_TOSHIBA_2 0x102f #define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030 #define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU 0x0031 #define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939 0x0032 #define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE 0x0105 #define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108 #define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3 #define PCI_VENDOR_ID_ATTO 0x117c #define PCI_VENDOR_ID_RICOH 0x1180 #define PCI_DEVICE_ID_RICOH_RL5C465 0x0465 #define PCI_DEVICE_ID_RICOH_RL5C466 0x0466 #define PCI_DEVICE_ID_RICOH_RL5C475 0x0475 #define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 #define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 #define PCI_DEVICE_ID_RICOH_R5C822 0x0822 #define PCI_DEVICE_ID_RICOH_R5CE823 0xe823 #define PCI_DEVICE_ID_RICOH_R5C832 0x0832 #define PCI_DEVICE_ID_RICOH_R5C843 0x0843 #define PCI_VENDOR_ID_DLINK 0x1186 #define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00 #define PCI_VENDOR_ID_ARTOP 0x1191 #define PCI_DEVICE_ID_ARTOP_ATP850UF 0x0005 #define PCI_DEVICE_ID_ARTOP_ATP860 0x0006 #define PCI_DEVICE_ID_ARTOP_ATP860R 0x0007 #define PCI_DEVICE_ID_ARTOP_ATP865 0x0008 #define PCI_DEVICE_ID_ARTOP_ATP865R 0x0009 #define PCI_DEVICE_ID_ARTOP_ATP867A 0x000A #define PCI_DEVICE_ID_ARTOP_ATP867B 0x000B #define PCI_DEVICE_ID_ARTOP_AEC7610 0x8002 #define PCI_DEVICE_ID_ARTOP_AEC7612UW 0x8010 #define PCI_DEVICE_ID_ARTOP_AEC7612U 0x8020 #define PCI_DEVICE_ID_ARTOP_AEC7612S 0x8030 #define PCI_DEVICE_ID_ARTOP_AEC7612D 0x8040 #define PCI_DEVICE_ID_ARTOP_AEC7612SUW 0x8050 #define PCI_DEVICE_ID_ARTOP_8060 0x8060 #define PCI_VENDOR_ID_ZEITNET 0x1193 #define PCI_DEVICE_ID_ZEITNET_1221 0x0001 #define PCI_DEVICE_ID_ZEITNET_1225 0x0002 #define PCI_VENDOR_ID_FUJITSU_ME 0x119e #define PCI_DEVICE_ID_FUJITSU_FS155 0x0001 #define PCI_DEVICE_ID_FUJITSU_FS50 0x0003 #define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 #define PCI_SUBDEVICE_ID_KEYSPAN_SX2 0x5334 #define PCI_VENDOR_ID_MARVELL 0x11ab #define PCI_DEVICE_ID_MARVELL_GT64111 0x4146 #define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 #define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 #define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 #define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 #define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 #define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 #define PCI_VENDOR_ID_V3 0x11b0 #define PCI_DEVICE_ID_V3_V960 0x0001 #define PCI_DEVICE_ID_V3_V351 0x0002 #define PCI_VENDOR_ID_ATT 0x11c1 #define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480 #define PCI_VENDOR_ID_SPECIALIX 0x11cb #define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000 #define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 #define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004 #define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 #define PCI_DEVICE_ID_AD1889JS 0x1889 #define PCI_DEVICE_ID_SEGA_BBA 0x1234 #define PCI_VENDOR_ID_ZORAN 0x11de #define PCI_DEVICE_ID_ZORAN_36057 0x6057 #define PCI_DEVICE_ID_ZORAN_36120 0x6120 #define PCI_VENDOR_ID_COMPEX 0x11f6 #define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 #define PCI_VENDOR_ID_PMC_Sierra 0x11f8 #define PCI_VENDOR_ID_RP 0x11fe #define PCI_DEVICE_ID_RP32INTF 0x0001 #define PCI_DEVICE_ID_RP8INTF 0x0002 #define PCI_DEVICE_ID_RP16INTF 0x0003 #define PCI_DEVICE_ID_RP4QUAD 0x0004 #define PCI_DEVICE_ID_RP8OCTA 0x0005 #define PCI_DEVICE_ID_RP8J 0x0006 #define PCI_DEVICE_ID_RP4J 0x0007 #define PCI_DEVICE_ID_RP8SNI 0x0008 #define PCI_DEVICE_ID_RP16SNI 0x0009 #define PCI_DEVICE_ID_RPP4 0x000A #define PCI_DEVICE_ID_RPP8 0x000B #define PCI_DEVICE_ID_RP4M 0x000D #define PCI_DEVICE_ID_RP2_232 0x000E #define PCI_DEVICE_ID_RP2_422 0x000F #define PCI_DEVICE_ID_URP32INTF 0x0801 #define PCI_DEVICE_ID_URP8INTF 0x0802 #define PCI_DEVICE_ID_URP16INTF 0x0803 #define PCI_DEVICE_ID_URP8OCTA 0x0805 #define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C #define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D #define PCI_DEVICE_ID_CRP16INTF 0x0903 #define PCI_VENDOR_ID_CYCLADES 0x120e #define PCI_DEVICE_ID_CYCLOM_Y_Lo 0x0100 #define PCI_DEVICE_ID_CYCLOM_Y_Hi 0x0101 #define PCI_DEVICE_ID_CYCLOM_4Y_Lo 0x0102 #define PCI_DEVICE_ID_CYCLOM_4Y_Hi 0x0103 #define PCI_DEVICE_ID_CYCLOM_8Y_Lo 0x0104 #define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105 #define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200 #define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201 #define PCI_DEVICE_ID_PC300_RX_2 0x0300 #define PCI_DEVICE_ID_PC300_RX_1 0x0301 #define PCI_DEVICE_ID_PC300_TE_2 0x0310 #define PCI_DEVICE_ID_PC300_TE_1 0x0311 #define PCI_DEVICE_ID_PC300_TE_M_2 0x0320 #define PCI_DEVICE_ID_PC300_TE_M_1 0x0321 #define PCI_VENDOR_ID_ESSENTIAL 0x120f #define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001 #define PCI_VENDOR_ID_O2 0x1217 #define PCI_DEVICE_ID_O2_6729 0x6729 #define PCI_DEVICE_ID_O2_6730 0x673a #define PCI_DEVICE_ID_O2_6832 0x6832 #define PCI_DEVICE_ID_O2_6836 0x6836 #define PCI_DEVICE_ID_O2_6812 0x6872 #define PCI_DEVICE_ID_O2_6933 0x6933 #define PCI_DEVICE_ID_O2_8120 0x8120 #define PCI_DEVICE_ID_O2_8220 0x8220 #define PCI_DEVICE_ID_O2_8221 0x8221 #define PCI_DEVICE_ID_O2_8320 0x8320 #define PCI_DEVICE_ID_O2_8321 0x8321 #define PCI_VENDOR_ID_3DFX 0x121a #define PCI_DEVICE_ID_3DFX_VOODOO 0x0001 #define PCI_DEVICE_ID_3DFX_VOODOO2 0x0002 #define PCI_DEVICE_ID_3DFX_BANSHEE 0x0003 #define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005 #define PCI_DEVICE_ID_3DFX_VOODOO5 0x0009 #define PCI_VENDOR_ID_AVM 0x1244 #define PCI_DEVICE_ID_AVM_B1 0x0700 #define PCI_DEVICE_ID_AVM_C4 0x0800 #define PCI_DEVICE_ID_AVM_A1 0x0a00 #define PCI_DEVICE_ID_AVM_A1_V2 0x0e00 #define PCI_DEVICE_ID_AVM_C2 0x1100 #define PCI_DEVICE_ID_AVM_T1 0x1200 #define PCI_VENDOR_ID_STALLION 0x124d /* Allied Telesyn */ #define PCI_VENDOR_ID_AT 0x1259 #define PCI_SUBDEVICE_ID_AT_2700FX 0x2701 #define PCI_SUBDEVICE_ID_AT_2701FX 0x2703 #define PCI_VENDOR_ID_ESS 0x125d #define PCI_DEVICE_ID_ESS_ESS1968 0x1968 #define PCI_DEVICE_ID_ESS_ESS1978 0x1978 #define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 #define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 #define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 #define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 #define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 #define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 #define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a #define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b #define PCI_VENDOR_ID_SATSAGEM 0x1267 #define PCI_DEVICE_ID_SATSAGEM_NICCY 0x1016 #define PCI_VENDOR_ID_ENSONIQ 0x1274 #define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 #define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 #define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 #define PCI_VENDOR_ID_TRANSMETA 0x1279 #define PCI_DEVICE_ID_EFFICEON 0x0060 #define PCI_VENDOR_ID_ROCKWELL 0x127A #define PCI_VENDOR_ID_ITE 0x1283 #define PCI_DEVICE_ID_ITE_8172 0x8172 #define PCI_DEVICE_ID_ITE_8211 0x8211 #define PCI_DEVICE_ID_ITE_8212 0x8212 #define PCI_DEVICE_ID_ITE_8213 0x8213 #define PCI_DEVICE_ID_ITE_8152 0x8152 #define PCI_DEVICE_ID_ITE_8872 0x8872 #define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886 /* formerly Platform Tech */ #define PCI_DEVICE_ID_ESS_ESS0100 0x0100 #define PCI_VENDOR_ID_ALTEON 0x12ae #define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232 0x0001 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232 0x0002 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232 0x0003 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485 0x0004 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4 0x0005 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485 0x0006 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2 0x0007 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485 0x0008 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6 0x0009 #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1 0x000A #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1 0x000B #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ 0x000C #define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_PTM 0x000D #define PCI_SUBDEVICE_ID_CONNECT_TECH_NT960PCI 0x0100 #define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2 0x0201 #define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4 0x0202 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232 0x0300 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232 0x0301 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232 0x0302 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1 0x0310 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2 0x0311 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4 0x0312 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2 0x0320 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4 0x0321 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8 0x0322 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485 0x0330 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485 0x0331 #define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485 0x0332 #define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 #define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018 #define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 #define PCI_SUBDEVICE_ID_CHASE_PCIFAST4 0x0031 #define PCI_SUBDEVICE_ID_CHASE_PCIFAST8 0x0021 #define PCI_SUBDEVICE_ID_CHASE_PCIFAST16 0x0011 #define PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC 0x0041 #define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D #define PCI_SUBDEVICE_ID_CHASE_PCIRAS4 0xF001 #define PCI_SUBDEVICE_ID_CHASE_PCIRAS8 0xF010 #define PCI_VENDOR_ID_AUREAL 0x12eb #define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001 #define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002 #define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003 #define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8 #define PCI_DEVICE_ID_LML_33R10 0x8a02 #define PCI_VENDOR_ID_ESDGMBH 0x12fe #define PCI_DEVICE_ID_ESDGMBH_CPCIASIO4 0x0111 #define PCI_VENDOR_ID_SIIG 0x131f #define PCI_SUBVENDOR_ID_SIIG 0x131f #define PCI_DEVICE_ID_SIIG_1S_10x_550 0x1000 #define PCI_DEVICE_ID_SIIG_1S_10x_650 0x1001 #define PCI_DEVICE_ID_SIIG_1S_10x_850 0x1002 #define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010 #define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011 #define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012 #define PCI_DEVICE_ID_SIIG_1P_10x 0x1020 #define PCI_DEVICE_ID_SIIG_2P_10x 0x1021 #define PCI_DEVICE_ID_SIIG_2S_10x_550 0x1030 #define PCI_DEVICE_ID_SIIG_2S_10x_650 0x1031 #define PCI_DEVICE_ID_SIIG_2S_10x_850 0x1032 #define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034 #define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035 #define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036 #define PCI_DEVICE_ID_SIIG_4S_10x_550 0x1050 #define PCI_DEVICE_ID_SIIG_4S_10x_650 0x1051 #define PCI_DEVICE_ID_SIIG_4S_10x_850 0x1052 #define PCI_DEVICE_ID_SIIG_1S_20x_550 0x2000 #define PCI_DEVICE_ID_SIIG_1S_20x_650 0x2001 #define PCI_DEVICE_ID_SIIG_1S_20x_850 0x2002 #define PCI_DEVICE_ID_SIIG_1P_20x 0x2020 #define PCI_DEVICE_ID_SIIG_2P_20x 0x2021 #define PCI_DEVICE_ID_SIIG_2S_20x_550 0x2030 #define PCI_DEVICE_ID_SIIG_2S_20x_650 0x2031 #define PCI_DEVICE_ID_SIIG_2S_20x_850 0x2032 #define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040 #define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041 #define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042 #define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010 #define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011 #define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012 #define PCI_DEVICE_ID_SIIG_4S_20x_550 0x2050 #define PCI_DEVICE_ID_SIIG_4S_20x_650 0x2051 #define PCI_DEVICE_ID_SIIG_4S_20x_850 0x2052 #define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060 #define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061 #define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062 #define PCI_DEVICE_ID_SIIG_8S_20x_550 0x2080 #define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081 #define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082 #define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050 #define PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL 0x2530 #define PCI_VENDOR_ID_RADISYS 0x1331 #define PCI_VENDOR_ID_MICRO_MEMORY 0x1332 #define PCI_DEVICE_ID_MICRO_MEMORY_5415CN 0x5415 #define PCI_DEVICE_ID_MICRO_MEMORY_5425CN 0x5425 #define PCI_DEVICE_ID_MICRO_MEMORY_6155 0x6155 #define PCI_VENDOR_ID_DOMEX 0x134a #define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001 #define PCI_VENDOR_ID_INTASHIELD 0x135a #define PCI_DEVICE_ID_INTASHIELD_IS200 0x0d80 #define PCI_DEVICE_ID_INTASHIELD_IS400 0x0dc0 #define PCI_VENDOR_ID_QUATECH 0x135C #define PCI_DEVICE_ID_QUATECH_QSC100 0x0010 #define PCI_DEVICE_ID_QUATECH_DSC100 0x0020 #define PCI_DEVICE_ID_QUATECH_ESC100D 0x0050 #define PCI_DEVICE_ID_QUATECH_ESC100M 0x0060 #define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278 #define PCI_VENDOR_ID_SEALEVEL 0x135e #define PCI_DEVICE_ID_SEALEVEL_U530 0x7101 #define PCI_DEVICE_ID_SEALEVEL_UCOMM2 0x7201 #define PCI_DEVICE_ID_SEALEVEL_UCOMM422 0x7402 #define PCI_DEVICE_ID_SEALEVEL_UCOMM232 0x7202 #define PCI_DEVICE_ID_SEALEVEL_COMM4 0x7401 #define PCI_DEVICE_ID_SEALEVEL_COMM8 0x7801 #define PCI_DEVICE_ID_SEALEVEL_7803 0x7803 #define PCI_DEVICE_ID_SEALEVEL_UCOMM8 0x7804 #define PCI_VENDOR_ID_HYPERCOPE 0x1365 #define PCI_DEVICE_ID_HYPERCOPE_PLX 0x9050 #define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO 0x0104 #define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO 0x0106 #define PCI_SUBDEVICE_ID_HYPERCOPE_METRO 0x0107 #define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2 0x0108 #define PCI_VENDOR_ID_DIGIGRAM 0x1369 #define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001 #define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 0xc002 #define PCI_VENDOR_ID_KAWASAKI 0x136b #define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01 #define PCI_VENDOR_ID_CNET 0x1371 #define PCI_DEVICE_ID_CNET_GIGACARD 0x434e #define PCI_VENDOR_ID_LMC 0x1376 #define PCI_DEVICE_ID_LMC_HSSI 0x0003 #define PCI_DEVICE_ID_LMC_DS3 0x0004 #define PCI_DEVICE_ID_LMC_SSI 0x0005 #define PCI_DEVICE_ID_LMC_T1 0x0006 #define PCI_VENDOR_ID_NETGEAR 0x1385 #define PCI_DEVICE_ID_NETGEAR_GA620 0x620a #define PCI_VENDOR_ID_APPLICOM 0x1389 #define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 #define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 #define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 #define PCI_VENDOR_ID_MOXA 0x1393 #define PCI_DEVICE_ID_MOXA_RC7000 0x0001 #define PCI_DEVICE_ID_MOXA_CP102 0x1020 #define PCI_DEVICE_ID_MOXA_CP102UL 0x1021 #define PCI_DEVICE_ID_MOXA_CP102U 0x1022 #define PCI_DEVICE_ID_MOXA_C104 0x1040 #define PCI_DEVICE_ID_MOXA_CP104U 0x1041 #define PCI_DEVICE_ID_MOXA_CP104JU 0x1042 #define PCI_DEVICE_ID_MOXA_CP104EL 0x1043 #define PCI_DEVICE_ID_MOXA_CT114 0x1140 #define PCI_DEVICE_ID_MOXA_CP114 0x1141 #define PCI_DEVICE_ID_MOXA_CP118U 0x1180 #define PCI_DEVICE_ID_MOXA_CP118EL 0x1181 #define PCI_DEVICE_ID_MOXA_CP132 0x1320 #define PCI_DEVICE_ID_MOXA_CP132U 0x1321 #define PCI_DEVICE_ID_MOXA_CP134U 0x1340 #define PCI_DEVICE_ID_MOXA_C168 0x1680 #define PCI_DEVICE_ID_MOXA_CP168U 0x1681 #define PCI_DEVICE_ID_MOXA_CP168EL 0x1682 #define PCI_DEVICE_ID_MOXA_CP204J 0x2040 #define PCI_DEVICE_ID_MOXA_C218 0x2180 #define PCI_DEVICE_ID_MOXA_C320 0x3200 #define PCI_VENDOR_ID_CCD 0x1397 #define PCI_DEVICE_ID_CCD_HFC4S 0x08B4 #define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234 #define PCI_DEVICE_ID_CCD_HFC8S 0x16B8 #define PCI_DEVICE_ID_CCD_2BD0 0x2bd0 #define PCI_DEVICE_ID_CCD_HFCE1 0x30B1 #define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136 #define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137 #define PCI_DEVICE_ID_CCD_B000 0xb000 #define PCI_DEVICE_ID_CCD_B006 0xb006 #define PCI_DEVICE_ID_CCD_B007 0xb007 #define PCI_DEVICE_ID_CCD_B008 0xb008 #define PCI_DEVICE_ID_CCD_B009 0xb009 #define PCI_DEVICE_ID_CCD_B00A 0xb00a #define PCI_DEVICE_ID_CCD_B00B 0xb00b #define PCI_DEVICE_ID_CCD_B00C 0xb00c #define PCI_DEVICE_ID_CCD_B100 0xb100 #define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520 #define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521 #define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522 #define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523 #define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540 #define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550 #define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552 #define PCI_SUBDEVICE_ID_CCD_JHSE1 0xB553 #define PCI_SUBDEVICE_ID_CCD_JH8S 0xB55B #define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560 #define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562 #define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563 #define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564 #define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565 #define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566 #define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567 #define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568 #define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569 #define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A #define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B #define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620 #define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622 #define PCI_DEVICE_ID_CCD_B700 0xb700 #define PCI_DEVICE_ID_CCD_B701 0xb701 #define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523 #define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884 #define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888 #define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998 #define PCI_VENDOR_ID_EXAR 0x13a8 #define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 #define PCI_DEVICE_ID_EXAR_XR17C154 0x0154 #define PCI_DEVICE_ID_EXAR_XR17C158 0x0158 #define PCI_VENDOR_ID_MICROGATE 0x13c0 #define PCI_DEVICE_ID_MICROGATE_USC 0x0010 #define PCI_DEVICE_ID_MICROGATE_SCA 0x0030 #define PCI_VENDOR_ID_3WARE 0x13C1 #define PCI_DEVICE_ID_3WARE_1000 0x1000 #define PCI_DEVICE_ID_3WARE_7000 0x1001 #define PCI_DEVICE_ID_3WARE_9000 0x1002 #define PCI_VENDOR_ID_IOMEGA 0x13ca #define PCI_DEVICE_ID_IOMEGA_BUZ 0x4231 #define PCI_VENDOR_ID_ABOCOM 0x13D1 #define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 #define PCI_VENDOR_ID_SUNDANCE 0x13f0 #define PCI_VENDOR_ID_CMEDIA 0x13f6 #define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 #define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 #define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 #define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 #define PCI_VENDOR_ID_LAVA 0x1407 #define PCI_DEVICE_ID_LAVA_DSERIAL 0x0100 /* 2x 16550 */ #define PCI_DEVICE_ID_LAVA_QUATRO_A 0x0101 /* 2x 16550, half of 4 port */ #define PCI_DEVICE_ID_LAVA_QUATRO_B 0x0102 /* 2x 16550, half of 4 port */ #define PCI_DEVICE_ID_LAVA_QUATTRO_A 0x0120 /* 2x 16550A, half of 4 port */ #define PCI_DEVICE_ID_LAVA_QUATTRO_B 0x0121 /* 2x 16550A, half of 4 port */ #define PCI_DEVICE_ID_LAVA_OCTO_A 0x0180 /* 4x 16550A, half of 8 port */ #define PCI_DEVICE_ID_LAVA_OCTO_B 0x0181 /* 4x 16550A, half of 8 port */ #define PCI_DEVICE_ID_LAVA_PORT_PLUS 0x0200 /* 2x 16650 */ #define PCI_DEVICE_ID_LAVA_QUAD_A 0x0201 /* 2x 16650, half of 4 port */ #define PCI_DEVICE_ID_LAVA_QUAD_B 0x0202 /* 2x 16650, half of 4 port */ #define PCI_DEVICE_ID_LAVA_SSERIAL 0x0500 /* 1x 16550 */ #define PCI_DEVICE_ID_LAVA_PORT_650 0x0600 /* 1x 16650 */ #define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000 #define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8002 /* The Lava Dual Parallel is */ #define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8003 /* two PCI devices on a card */ #define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR 0x8800 #define PCI_VENDOR_ID_TIMEDIA 0x1409 #define PCI_DEVICE_ID_TIMEDIA_1889 0x7168 #define PCI_VENDOR_ID_ICE 0x1412 #define PCI_DEVICE_ID_ICE_1712 0x1712 #define PCI_DEVICE_ID_VT1724 0x1724 #define PCI_VENDOR_ID_OXSEMI 0x1415 #define PCI_DEVICE_ID_OXSEMI_12PCI840 0x8403 #define PCI_DEVICE_ID_OXSEMI_PCIe840 0xC000 #define PCI_DEVICE_ID_OXSEMI_PCIe840_G 0xC004 #define PCI_DEVICE_ID_OXSEMI_PCIe952_0 0xC100 #define PCI_DEVICE_ID_OXSEMI_PCIe952_0_G 0xC104 #define PCI_DEVICE_ID_OXSEMI_PCIe952_1 0xC110 #define PCI_DEVICE_ID_OXSEMI_PCIe952_1_G 0xC114 #define PCI_DEVICE_ID_OXSEMI_PCIe952_1_U 0xC118 #define PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU 0xC11C #define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501 #define PCI_DEVICE_ID_OXSEMI_C950 0x950B #define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511 #define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513 #define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521 #define PCI_DEVICE_ID_OXSEMI_16PCI952PP 0x9523 #define PCI_SUBDEVICE_ID_OXSEMI_C950 0x0001 #define PCI_VENDOR_ID_CHELSIO 0x1425 #define PCI_VENDOR_ID_SAMSUNG 0x144d #define PCI_VENDOR_ID_GIGABYTE 0x1458 #define PCI_VENDOR_ID_AMBIT 0x1468 #define PCI_VENDOR_ID_MYRICOM 0x14c1 #define PCI_VENDOR_ID_TITAN 0x14D2 #define PCI_DEVICE_ID_TITAN_010L 0x8001 #define PCI_DEVICE_ID_TITAN_100L 0x8010 #define PCI_DEVICE_ID_TITAN_110L 0x8011 #define PCI_DEVICE_ID_TITAN_200L 0x8020 #define PCI_DEVICE_ID_TITAN_210L 0x8021 #define PCI_DEVICE_ID_TITAN_400L 0x8040 #define PCI_DEVICE_ID_TITAN_800L 0x8080 #define PCI_DEVICE_ID_TITAN_100 0xA001 #define PCI_DEVICE_ID_TITAN_200 0xA005 #define PCI_DEVICE_ID_TITAN_400 0xA003 #define PCI_DEVICE_ID_TITAN_800B 0xA004 #define PCI_VENDOR_ID_PANACOM 0x14d4 #define PCI_DEVICE_ID_PANACOM_QUADMODEM 0x0400 #define PCI_DEVICE_ID_PANACOM_DUALMODEM 0x0402 #define PCI_VENDOR_ID_SIPACKETS 0x14d9 #define PCI_DEVICE_ID_SP1011 0x0010 #define PCI_VENDOR_ID_AFAVLAB 0x14db #define PCI_DEVICE_ID_AFAVLAB_P028 0x2180 #define PCI_DEVICE_ID_AFAVLAB_P030 0x2182 #define PCI_SUBDEVICE_ID_AFAVLAB_P061 0x2150 #define PCI_VENDOR_ID_BCM_GVC 0x14a4 #define PCI_VENDOR_ID_BROADCOM 0x14e4 #define PCI_DEVICE_ID_TIGON3_5752 0x1600 #define PCI_DEVICE_ID_TIGON3_5752M 0x1601 #define PCI_DEVICE_ID_NX2_5709 0x1639 #define PCI_DEVICE_ID_NX2_5709S 0x163a #define PCI_DEVICE_ID_TIGON3_5700 0x1644 #define PCI_DEVICE_ID_TIGON3_5701 0x1645 #define PCI_DEVICE_ID_TIGON3_5702 0x1646 #define PCI_DEVICE_ID_TIGON3_5703 0x1647 #define PCI_DEVICE_ID_TIGON3_5704 0x1648 #define PCI_DEVICE_ID_TIGON3_5704S_2 0x1649 #define PCI_DEVICE_ID_NX2_5706 0x164a #define PCI_DEVICE_ID_NX2_5708 0x164c #define PCI_DEVICE_ID_TIGON3_5702FE 0x164d #define PCI_DEVICE_ID_NX2_57710 0x164e #define PCI_DEVICE_ID_NX2_57711 0x164f #define PCI_DEVICE_ID_NX2_57711E 0x1650 #define PCI_DEVICE_ID_TIGON3_5705 0x1653 #define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 #define PCI_DEVICE_ID_TIGON3_5719 0x1657 #define PCI_DEVICE_ID_TIGON3_5721 0x1659 #define PCI_DEVICE_ID_TIGON3_5722 0x165a #define PCI_DEVICE_ID_TIGON3_5723 0x165b #define PCI_DEVICE_ID_TIGON3_5705M 0x165d #define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e #define PCI_DEVICE_ID_NX2_57712 0x1662 #define PCI_DEVICE_ID_NX2_57712E 0x1663 #define PCI_DEVICE_ID_TIGON3_5714 0x1668 #define PCI_DEVICE_ID_TIGON3_5714S 0x1669 #define PCI_DEVICE_ID_TIGON3_5780 0x166a #define PCI_DEVICE_ID_TIGON3_5780S 0x166b #define PCI_DEVICE_ID_TIGON3_5705F 0x166e #define PCI_DEVICE_ID_TIGON3_5754M 0x1672 #define PCI_DEVICE_ID_TIGON3_5755M 0x1673 #define PCI_DEVICE_ID_TIGON3_5756 0x1674 #define PCI_DEVICE_ID_TIGON3_5751 0x1677 #define PCI_DEVICE_ID_TIGON3_5715 0x1678 #define PCI_DEVICE_ID_TIGON3_5715S 0x1679 #define PCI_DEVICE_ID_TIGON3_5754 0x167a #define PCI_DEVICE_ID_TIGON3_5755 0x167b #define PCI_DEVICE_ID_TIGON3_5751M 0x167d #define PCI_DEVICE_ID_TIGON3_5751F 0x167e #define PCI_DEVICE_ID_TIGON3_5787F 0x167f #define PCI_DEVICE_ID_TIGON3_5761E 0x1680 #define PCI_DEVICE_ID_TIGON3_5761 0x1681 #define PCI_DEVICE_ID_TIGON3_5764 0x1684 #define PCI_DEVICE_ID_NX2_57800 0x168a #define PCI_DEVICE_ID_NX2_57840 0x168d #define PCI_DEVICE_ID_NX2_57810 0x168e #define PCI_DEVICE_ID_TIGON3_5787M 0x1693 #define PCI_DEVICE_ID_TIGON3_5782 0x1696 #define PCI_DEVICE_ID_TIGON3_5784 0x1698 #define PCI_DEVICE_ID_TIGON3_5786 0x169a #define PCI_DEVICE_ID_TIGON3_5787 0x169b #define PCI_DEVICE_ID_TIGON3_5788 0x169c #define PCI_DEVICE_ID_TIGON3_5789 0x169d #define PCI_DEVICE_ID_NX2_57800_MF 0x16a5 #define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 #define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 #define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 #define PCI_DEVICE_ID_NX2_57800_VF 0x16a9 #define PCI_DEVICE_ID_NX2_5706S 0x16aa #define PCI_DEVICE_ID_NX2_57840_MF 0x16a4 #define PCI_DEVICE_ID_NX2_5708S 0x16ac #define PCI_DEVICE_ID_NX2_57840_VF 0x16ad #define PCI_DEVICE_ID_NX2_57810_MF 0x16ae #define PCI_DEVICE_ID_NX2_57810_VF 0x16af #define PCI_DEVICE_ID_TIGON3_5702A3 0x16c6 #define PCI_DEVICE_ID_TIGON3_5703A3 0x16c7 #define PCI_DEVICE_ID_TIGON3_5781 0x16dd #define PCI_DEVICE_ID_TIGON3_5753 0x16f7 #define PCI_DEVICE_ID_TIGON3_5753M 0x16fd #define PCI_DEVICE_ID_TIGON3_5753F 0x16fe #define PCI_DEVICE_ID_TIGON3_5901 0x170d #define PCI_DEVICE_ID_BCM4401B1 0x170c #define PCI_DEVICE_ID_TIGON3_5901_2 0x170e #define PCI_DEVICE_ID_TIGON3_5906 0x1712 #define PCI_DEVICE_ID_TIGON3_5906M 0x1713 #define PCI_DEVICE_ID_BCM4401 0x4401 #define PCI_DEVICE_ID_BCM4401B0 0x4402 #define PCI_VENDOR_ID_TOPIC 0x151f #define PCI_DEVICE_ID_TOPIC_TP560 0x0000 #define PCI_VENDOR_ID_MAINPINE 0x1522 #define PCI_DEVICE_ID_MAINPINE_PBRIDGE 0x0100 #define PCI_VENDOR_ID_ENE 0x1524 #define PCI_DEVICE_ID_ENE_CB710_FLASH 0x0510 #define PCI_DEVICE_ID_ENE_CB712_SD 0x0550 #define PCI_DEVICE_ID_ENE_CB712_SD_2 0x0551 #define PCI_DEVICE_ID_ENE_CB714_SD 0x0750 #define PCI_DEVICE_ID_ENE_CB714_SD_2 0x0751 #define PCI_DEVICE_ID_ENE_1211 0x1211 #define PCI_DEVICE_ID_ENE_1225 0x1225 #define PCI_DEVICE_ID_ENE_1410 0x1410 #define PCI_DEVICE_ID_ENE_710 0x1411 #define PCI_DEVICE_ID_ENE_712 0x1412 #define PCI_DEVICE_ID_ENE_1420 0x1420 #define PCI_DEVICE_ID_ENE_720 0x1421 #define PCI_DEVICE_ID_ENE_722 0x1422 #define PCI_SUBVENDOR_ID_PERLE 0x155f #define PCI_SUBDEVICE_ID_PCI_RAS4 0xf001 #define PCI_SUBDEVICE_ID_PCI_RAS8 0xf010 #define PCI_VENDOR_ID_SYBA 0x1592 #define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782 #define PCI_DEVICE_ID_SYBA_1P_ECP 0x0783 #define PCI_VENDOR_ID_MORETON 0x15aa #define PCI_DEVICE_ID_RASTEL_2PORT 0x2000 #define PCI_VENDOR_ID_ZOLTRIX 0x15b0 #define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd0 #define PCI_VENDOR_ID_MELLANOX 0x15b3 #define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44 #define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46 #define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278 #define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282 #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c #define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 #define PCI_VENDOR_ID_DFI 0x15bd #define PCI_VENDOR_ID_QUICKNET 0x15e2 #define PCI_DEVICE_ID_QUICKNET_XJ 0x0500 /* * ADDI-DATA GmbH communication cards */ #define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 #define PCI_VENDOR_ID_ADDIDATA 0x15B8 #define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000 #define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001 #define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002 #define PCI_DEVICE_ID_ADDIDATA_APCI7800 0x818E #define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009 #define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A #define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B #define PCI_DEVICE_ID_ADDIDATA_APCI7500_3 0x700C #define PCI_DEVICE_ID_ADDIDATA_APCI7420_3 0x700D #define PCI_DEVICE_ID_ADDIDATA_APCI7300_3 0x700E #define PCI_DEVICE_ID_ADDIDATA_APCI7800_3 0x700F #define PCI_DEVICE_ID_ADDIDATA_APCIe7300 0x7010 #define PCI_DEVICE_ID_ADDIDATA_APCIe7420 0x7011 #define PCI_DEVICE_ID_ADDIDATA_APCIe7500 0x7012 #define PCI_DEVICE_ID_ADDIDATA_APCIe7800 0x7013 #define PCI_VENDOR_ID_PDC 0x15e9 #define PCI_VENDOR_ID_FARSITE 0x1619 #define PCI_DEVICE_ID_FARSITE_T2P 0x0400 #define PCI_DEVICE_ID_FARSITE_T4P 0x0440 #define PCI_DEVICE_ID_FARSITE_T1U 0x0610 #define PCI_DEVICE_ID_FARSITE_T2U 0x0620 #define PCI_DEVICE_ID_FARSITE_T4U 0x0640 #define PCI_DEVICE_ID_FARSITE_TE1 0x1610 #define PCI_DEVICE_ID_FARSITE_TE1C 0x1612 #define PCI_VENDOR_ID_ARIMA 0x161f #define PCI_VENDOR_ID_BROCADE 0x1657 #define PCI_DEVICE_ID_BROCADE_CT 0x0014 #define PCI_DEVICE_ID_BROCADE_FC_8G1P 0x0017 #define PCI_DEVICE_ID_BROCADE_CT_FC 0x0021 #define PCI_VENDOR_ID_SIBYTE 0x166d #define PCI_DEVICE_ID_BCM1250_PCI 0x0001 #define PCI_DEVICE_ID_BCM1250_HT 0x0002 #define PCI_VENDOR_ID_ATHEROS 0x168c #define PCI_VENDOR_ID_NETCELL 0x169c #define PCI_DEVICE_ID_REVOLUTION 0x0044 #define PCI_VENDOR_ID_CENATEK 0x16CA #define PCI_DEVICE_ID_CENATEK_IDE 0x0001 #define PCI_VENDOR_ID_VITESSE 0x1725 #define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 #define PCI_VENDOR_ID_LINKSYS 0x1737 #define PCI_DEVICE_ID_LINKSYS_EG1064 0x1064 #define PCI_VENDOR_ID_ALTIMA 0x173b #define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8 #define PCI_DEVICE_ID_ALTIMA_AC1001 0x03e9 #define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea #define PCI_DEVICE_ID_ALTIMA_AC1003 0x03eb #define PCI_VENDOR_ID_BELKIN 0x1799 #define PCI_DEVICE_ID_BELKIN_F5D7010V7 0x701f #define PCI_VENDOR_ID_RDC 0x17f3 #define PCI_DEVICE_ID_RDC_R6020 0x6020 #define PCI_DEVICE_ID_RDC_R6030 0x6030 #define PCI_DEVICE_ID_RDC_R6040 0x6040 #define PCI_DEVICE_ID_RDC_R6060 0x6060 #define PCI_DEVICE_ID_RDC_R6061 0x6061 #define PCI_DEVICE_ID_RDC_D1010 0x1010 #define PCI_VENDOR_ID_LENOVO 0x17aa #define PCI_VENDOR_ID_ARECA 0x17d3 #define PCI_DEVICE_ID_ARECA_1110 0x1110 #define PCI_DEVICE_ID_ARECA_1120 0x1120 #define PCI_DEVICE_ID_ARECA_1130 0x1130 #define PCI_DEVICE_ID_ARECA_1160 0x1160 #define PCI_DEVICE_ID_ARECA_1170 0x1170 #define PCI_DEVICE_ID_ARECA_1200 0x1200 #define PCI_DEVICE_ID_ARECA_1201 0x1201 #define PCI_DEVICE_ID_ARECA_1202 0x1202 #define PCI_DEVICE_ID_ARECA_1210 0x1210 #define PCI_DEVICE_ID_ARECA_1220 0x1220 #define PCI_DEVICE_ID_ARECA_1230 0x1230 #define PCI_DEVICE_ID_ARECA_1260 0x1260 #define PCI_DEVICE_ID_ARECA_1270 0x1270 #define PCI_DEVICE_ID_ARECA_1280 0x1280 #define PCI_DEVICE_ID_ARECA_1380 0x1380 #define PCI_DEVICE_ID_ARECA_1381 0x1381 #define PCI_DEVICE_ID_ARECA_1680 0x1680 #define PCI_DEVICE_ID_ARECA_1681 0x1681 #define PCI_VENDOR_ID_S2IO 0x17d5 #define PCI_DEVICE_ID_S2IO_WIN 0x5731 #define PCI_DEVICE_ID_S2IO_UNI 0x5831 #define PCI_DEVICE_ID_HERC_WIN 0x5732 #define PCI_DEVICE_ID_HERC_UNI 0x5832 #define PCI_VENDOR_ID_SITECOM 0x182d #define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069 #define PCI_VENDOR_ID_TOPSPIN 0x1867 #define PCI_VENDOR_ID_SILAN 0x1904 #define PCI_VENDOR_ID_RENESAS 0x1912 #define PCI_DEVICE_ID_RENESAS_SH7781 0x0001 #define PCI_DEVICE_ID_RENESAS_SH7780 0x0002 #define PCI_DEVICE_ID_RENESAS_SH7763 0x0004 #define PCI_DEVICE_ID_RENESAS_SH7785 0x0007 #define PCI_DEVICE_ID_RENESAS_SH7786 0x0010 #define PCI_VENDOR_ID_SOLARFLARE 0x1924 #define PCI_DEVICE_ID_SOLARFLARE_SFC4000A_0 0x0703 #define PCI_DEVICE_ID_SOLARFLARE_SFC4000A_1 0x6703 #define PCI_DEVICE_ID_SOLARFLARE_SFC4000B 0x0710 #define PCI_VENDOR_ID_TDI 0x192E #define PCI_DEVICE_ID_TDI_EHCI 0x0101 #define PCI_VENDOR_ID_FREESCALE 0x1957 #define PCI_DEVICE_ID_MPC8308 0xc006 #define PCI_DEVICE_ID_MPC8315E 0x00b4 #define PCI_DEVICE_ID_MPC8315 0x00b5 #define PCI_DEVICE_ID_MPC8314E 0x00b6 #define PCI_DEVICE_ID_MPC8314 0x00b7 #define PCI_DEVICE_ID_MPC8378E 0x00c4 #define PCI_DEVICE_ID_MPC8378 0x00c5 #define PCI_DEVICE_ID_MPC8377E 0x00c6 #define PCI_DEVICE_ID_MPC8377 0x00c7 #define PCI_DEVICE_ID_MPC8548E 0x0012 #define PCI_DEVICE_ID_MPC8548 0x0013 #define PCI_DEVICE_ID_MPC8543E 0x0014 #define PCI_DEVICE_ID_MPC8543 0x0015 #define PCI_DEVICE_ID_MPC8547E 0x0018 #define PCI_DEVICE_ID_MPC8545E 0x0019 #define PCI_DEVICE_ID_MPC8545 0x001a #define PCI_DEVICE_ID_MPC8569E 0x0061 #define PCI_DEVICE_ID_MPC8569 0x0060 #define PCI_DEVICE_ID_MPC8568E 0x0020 #define PCI_DEVICE_ID_MPC8568 0x0021 #define PCI_DEVICE_ID_MPC8567E 0x0022 #define PCI_DEVICE_ID_MPC8567 0x0023 #define PCI_DEVICE_ID_MPC8533E 0x0030 #define PCI_DEVICE_ID_MPC8533 0x0031 #define PCI_DEVICE_ID_MPC8544E 0x0032 #define PCI_DEVICE_ID_MPC8544 0x0033 #define PCI_DEVICE_ID_MPC8572E 0x0040 #define PCI_DEVICE_ID_MPC8572 0x0041 #define PCI_DEVICE_ID_MPC8536E 0x0050 #define PCI_DEVICE_ID_MPC8536 0x0051 #define PCI_DEVICE_ID_P2020E 0x0070 #define PCI_DEVICE_ID_P2020 0x0071 #define PCI_DEVICE_ID_P2010E 0x0078 #define PCI_DEVICE_ID_P2010 0x0079 #define PCI_DEVICE_ID_P1020E 0x0100 #define PCI_DEVICE_ID_P1020 0x0101 #define PCI_DEVICE_ID_P1021E 0x0102 #define PCI_DEVICE_ID_P1021 0x0103 #define PCI_DEVICE_ID_P1011E 0x0108 #define PCI_DEVICE_ID_P1011 0x0109 #define PCI_DEVICE_ID_P1022E 0x0110 #define PCI_DEVICE_ID_P1022 0x0111 #define PCI_DEVICE_ID_P1013E 0x0118 #define PCI_DEVICE_ID_P1013 0x0119 #define PCI_DEVICE_ID_P4080E 0x0400 #define PCI_DEVICE_ID_P4080 0x0401 #define PCI_DEVICE_ID_P4040E 0x0408 #define PCI_DEVICE_ID_P4040 0x0409 #define PCI_DEVICE_ID_P2040E 0x0410 #define PCI_DEVICE_ID_P2040 0x0411 #define PCI_DEVICE_ID_P3041E 0x041E #define PCI_DEVICE_ID_P3041 0x041F #define PCI_DEVICE_ID_P5020E 0x0420 #define PCI_DEVICE_ID_P5020 0x0421 #define PCI_DEVICE_ID_P5010E 0x0428 #define PCI_DEVICE_ID_P5010 0x0429 #define PCI_DEVICE_ID_MPC8641 0x7010 #define PCI_DEVICE_ID_MPC8641D 0x7011 #define PCI_DEVICE_ID_MPC8610 0x7018 #define PCI_VENDOR_ID_PASEMI 0x1959 #define PCI_VENDOR_ID_ATTANSIC 0x1969 #define PCI_DEVICE_ID_ATTANSIC_L1 0x1048 #define PCI_DEVICE_ID_ATTANSIC_L2 0x2048 #define PCI_VENDOR_ID_JMICRON 0x197B #define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 #define PCI_DEVICE_ID_JMICRON_JMB361 0x2361 #define PCI_DEVICE_ID_JMICRON_JMB362 0x2362 #define PCI_DEVICE_ID_JMICRON_JMB363 0x2363 #define PCI_DEVICE_ID_JMICRON_JMB364 0x2364 #define PCI_DEVICE_ID_JMICRON_JMB365 0x2365 #define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 #define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 #define PCI_DEVICE_ID_JMICRON_JMB369 0x2369 #define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381 #define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382 #define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383 #define PCI_DEVICE_ID_JMICRON_JMB385_MS 0x2388 #define PCI_DEVICE_ID_JMICRON_JMB388_SD 0x2391 #define PCI_DEVICE_ID_JMICRON_JMB388_ESD 0x2392 #define PCI_DEVICE_ID_JMICRON_JMB390_MS 0x2393 #define PCI_VENDOR_ID_KORENIX 0x1982 #define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600 #define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff #define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700 #define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff #define PCI_VENDOR_ID_QMI 0x1a32 #define PCI_VENDOR_ID_AZWAVE 0x1a3b #define PCI_VENDOR_ID_ASMEDIA 0x1b21 #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 #define PCI_VENDOR_ID_TEHUTI 0x1fc9 #define PCI_DEVICE_ID_TEHUTI_3009 0x3009 #define PCI_DEVICE_ID_TEHUTI_3010 0x3010 #define PCI_DEVICE_ID_TEHUTI_3014 0x3014 #define PCI_VENDOR_ID_HINT 0x3388 #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 #define PCI_VENDOR_ID_3DLABS 0x3d3d #define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007 #define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009 #define PCI_VENDOR_ID_NETXEN 0x4040 #define PCI_DEVICE_ID_NX2031_10GXSR 0x0001 #define PCI_DEVICE_ID_NX2031_10GCX4 0x0002 #define PCI_DEVICE_ID_NX2031_4GCU 0x0003 #define PCI_DEVICE_ID_NX2031_IMEZ 0x0004 #define PCI_DEVICE_ID_NX2031_HMEZ 0x0005 #define PCI_DEVICE_ID_NX2031_XG_MGMT 0x0024 #define PCI_DEVICE_ID_NX2031_XG_MGMT2 0x0025 #define PCI_DEVICE_ID_NX3031 0x0100 #define PCI_VENDOR_ID_AKS 0x416c #define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100 #define PCI_VENDOR_ID_ACCESSIO 0x494f #define PCI_DEVICE_ID_ACCESSIO_WDG_CSM 0x22c0 #define PCI_VENDOR_ID_S3 0x5333 #define PCI_DEVICE_ID_S3_TRIO 0x8811 #define PCI_DEVICE_ID_S3_868 0x8880 #define PCI_DEVICE_ID_S3_968 0x88f0 #define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25 #define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04 #define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 #define PCI_VENDOR_ID_DUNORD 0x5544 #define PCI_DEVICE_ID_DUNORD_I3000 0x0001 #define PCI_VENDOR_ID_DCI 0x6666 #define PCI_DEVICE_ID_DCI_PCCOM4 0x0001 #define PCI_DEVICE_ID_DCI_PCCOM8 0x0002 #define PCI_DEVICE_ID_DCI_PCCOM2 0x0004 #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE_ID_INTEL_EESSC 0x0008 #define PCI_DEVICE_ID_INTEL_PXHD_0 0x0320 #define PCI_DEVICE_ID_INTEL_PXHD_1 0x0321 #define PCI_DEVICE_ID_INTEL_PXH_0 0x0329 #define PCI_DEVICE_ID_INTEL_PXH_1 0x032A #define PCI_DEVICE_ID_INTEL_PXHV 0x032C #define PCI_DEVICE_ID_INTEL_80332_0 0x0330 #define PCI_DEVICE_ID_INTEL_80332_1 0x0332 #define PCI_DEVICE_ID_INTEL_80333_0 0x0370 #define PCI_DEVICE_ID_INTEL_80333_1 0x0372 #define PCI_DEVICE_ID_INTEL_82375 0x0482 #define PCI_DEVICE_ID_INTEL_82424 0x0483 #define PCI_DEVICE_ID_INTEL_82378 0x0484 #define PCI_DEVICE_ID_INTEL_MRST_SD0 0x0807 #define PCI_DEVICE_ID_INTEL_MRST_SD1 0x0808 #define PCI_DEVICE_ID_INTEL_MFD_SD 0x0820 #define PCI_DEVICE_ID_INTEL_MFD_SDIO1 0x0821 #define PCI_DEVICE_ID_INTEL_MFD_SDIO2 0x0822 #define PCI_DEVICE_ID_INTEL_MFD_EMMC0 0x0823 #define PCI_DEVICE_ID_INTEL_MFD_EMMC1 0x0824 #define PCI_DEVICE_ID_INTEL_MRST_SD2 0x084F #define PCI_DEVICE_ID_INTEL_I960 0x0960 #define PCI_DEVICE_ID_INTEL_I960RM 0x0962 #define PCI_DEVICE_ID_INTEL_CENTERTON_ILB 0x0c60 #define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062 #define PCI_DEVICE_ID_INTEL_82573E_SOL 0x1085 #define PCI_DEVICE_ID_INTEL_82573L_SOL 0x108F #define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 #define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 #define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 #define PCI_DEVICE_ID_INTEL_7505_0 0x2550 #define PCI_DEVICE_ID_INTEL_7205_0 0x255d #define PCI_DEVICE_ID_INTEL_82437 0x122d #define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e #define PCI_DEVICE_ID_INTEL_82371FB_1 0x1230 #define PCI_DEVICE_ID_INTEL_82371MX 0x1234 #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82380FB 0x124b #define PCI_DEVICE_ID_INTEL_82439 0x1250 #define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 #define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 #define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 #define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 #define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN 0x1c41 #define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX 0x1c5f #define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_0 0x1d40 #define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_1 0x1d41 #define PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI 0x1e31 #define PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MIN 0x1e40 #define PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MAX 0x1e5f #define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN 0x2310 #define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX 0x231f #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 #define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 #define PCI_DEVICE_ID_INTEL_82801AA_6 0x2416 #define PCI_DEVICE_ID_INTEL_82801AA_8 0x2418 #define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420 #define PCI_DEVICE_ID_INTEL_82801AB_1 0x2421 #define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 #define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425 #define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426 #define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428 #define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440 #define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443 #define PCI_DEVICE_ID_INTEL_82801BA_4 0x2445 #define PCI_DEVICE_ID_INTEL_82801BA_6 0x2448 #define PCI_DEVICE_ID_INTEL_82801BA_8 0x244a #define PCI_DEVICE_ID_INTEL_82801BA_9 0x244b #define PCI_DEVICE_ID_INTEL_82801BA_10 0x244c #define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e #define PCI_DEVICE_ID_INTEL_82801E_0 0x2450 #define PCI_DEVICE_ID_INTEL_82801E_11 0x245b #define PCI_DEVICE_ID_INTEL_82801CA_0 0x2480 #define PCI_DEVICE_ID_INTEL_82801CA_3 0x2483 #define PCI_DEVICE_ID_INTEL_82801CA_5 0x2485 #define PCI_DEVICE_ID_INTEL_82801CA_6 0x2486 #define PCI_DEVICE_ID_INTEL_82801CA_10 0x248a #define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b #define PCI_DEVICE_ID_INTEL_82801CA_12 0x248c #define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0 #define PCI_DEVICE_ID_INTEL_82801DB_1 0x24c1 #define PCI_DEVICE_ID_INTEL_82801DB_2 0x24c2 #define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3 #define PCI_DEVICE_ID_INTEL_82801DB_5 0x24c5 #define PCI_DEVICE_ID_INTEL_82801DB_6 0x24c6 #define PCI_DEVICE_ID_INTEL_82801DB_9 0x24c9 #define PCI_DEVICE_ID_INTEL_82801DB_10 0x24ca #define PCI_DEVICE_ID_INTEL_82801DB_11 0x24cb #define PCI_DEVICE_ID_INTEL_82801DB_12 0x24cc #define PCI_DEVICE_ID_INTEL_82801EB_0 0x24d0 #define PCI_DEVICE_ID_INTEL_82801EB_1 0x24d1 #define PCI_DEVICE_ID_INTEL_82801EB_3 0x24d3 #define PCI_DEVICE_ID_INTEL_82801EB_5 0x24d5 #define PCI_DEVICE_ID_INTEL_82801EB_6 0x24d6 #define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db #define PCI_DEVICE_ID_INTEL_82801EB_12 0x24dc #define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd #define PCI_DEVICE_ID_INTEL_ESB_1 0x25a1 #define PCI_DEVICE_ID_INTEL_ESB_2 0x25a2 #define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4 #define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6 #define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab #define PCI_DEVICE_ID_INTEL_ESB_10 0x25ac #define PCI_DEVICE_ID_INTEL_82820_HB 0x2500 #define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501 #define PCI_DEVICE_ID_INTEL_82850_HB 0x2530 #define PCI_DEVICE_ID_INTEL_82860_HB 0x2531 #define PCI_DEVICE_ID_INTEL_E7501_MCH 0x254c #define PCI_DEVICE_ID_INTEL_82845G_HB 0x2560 #define PCI_DEVICE_ID_INTEL_82845G_IG 0x2562 #define PCI_DEVICE_ID_INTEL_82865_HB 0x2570 #define PCI_DEVICE_ID_INTEL_82865_IG 0x2572 #define PCI_DEVICE_ID_INTEL_82875_HB 0x2578 #define PCI_DEVICE_ID_INTEL_82915G_HB 0x2580 #define PCI_DEVICE_ID_INTEL_82915G_IG 0x2582 #define PCI_DEVICE_ID_INTEL_82915GM_HB 0x2590 #define PCI_DEVICE_ID_INTEL_82915GM_IG 0x2592 #define PCI_DEVICE_ID_INTEL_5000_ERR 0x25F0 #define PCI_DEVICE_ID_INTEL_5000_FBD0 0x25F5 #define PCI_DEVICE_ID_INTEL_5000_FBD1 0x25F6 #define PCI_DEVICE_ID_INTEL_82945G_HB 0x2770 #define PCI_DEVICE_ID_INTEL_82945G_IG 0x2772 #define PCI_DEVICE_ID_INTEL_3000_HB 0x2778 #define PCI_DEVICE_ID_INTEL_82945GM_HB 0x27A0 #define PCI_DEVICE_ID_INTEL_82945GM_IG 0x27A2 #define PCI_DEVICE_ID_INTEL_ICH6_0 0x2640 #define PCI_DEVICE_ID_INTEL_ICH6_1 0x2641 #define PCI_DEVICE_ID_INTEL_ICH6_2 0x2642 #define PCI_DEVICE_ID_INTEL_ICH6_16 0x266a #define PCI_DEVICE_ID_INTEL_ICH6_17 0x266d #define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e #define PCI_DEVICE_ID_INTEL_ICH6_19 0x266f #define PCI_DEVICE_ID_INTEL_ESB2_0 0x2670 #define PCI_DEVICE_ID_INTEL_ESB2_14 0x2698 #define PCI_DEVICE_ID_INTEL_ESB2_17 0x269b #define PCI_DEVICE_ID_INTEL_ESB2_18 0x269e #define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b8 #define PCI_DEVICE_ID_INTEL_ICH7_1 0x27b9 #define PCI_DEVICE_ID_INTEL_ICH7_30 0x27b0 #define PCI_DEVICE_ID_INTEL_TGP_LPC 0x27bc #define PCI_DEVICE_ID_INTEL_ICH7_31 0x27bd #define PCI_DEVICE_ID_INTEL_ICH7_17 0x27da #define PCI_DEVICE_ID_INTEL_ICH7_19 0x27dd #define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de #define PCI_DEVICE_ID_INTEL_ICH7_21 0x27df #define PCI_DEVICE_ID_INTEL_ICH8_0 0x2810 #define PCI_DEVICE_ID_INTEL_ICH8_1 0x2811 #define PCI_DEVICE_ID_INTEL_ICH8_2 0x2812 #define PCI_DEVICE_ID_INTEL_ICH8_3 0x2814 #define PCI_DEVICE_ID_INTEL_ICH8_4 0x2815 #define PCI_DEVICE_ID_INTEL_ICH8_5 0x283e #define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 #define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 #define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 #define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 #define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 #define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 #define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 #define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 #define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 #define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 #define PCI_DEVICE_ID_INTEL_I7_MCR 0x2c18 #define PCI_DEVICE_ID_INTEL_I7_MC_TAD 0x2c19 #define PCI_DEVICE_ID_INTEL_I7_MC_RAS 0x2c1a #define PCI_DEVICE_ID_INTEL_I7_MC_TEST 0x2c1c #define PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL 0x2c20 #define PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR 0x2c21 #define PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK 0x2c22 #define PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC 0x2c23 #define PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL 0x2c28 #define PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR 0x2c29 #define PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK 0x2c2a #define PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC 0x2c2b #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL 0x2c30 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR 0x2c31 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33 #define PCI_DEVICE_ID_INTEL_I7_NONCORE 0x2c41 #define PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT 0x2c40 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE 0x2c50 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT 0x2c51 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2 0x2c70 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_SAD 0x2c81 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0 0x2c90 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_PHY0 0x2c91 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR 0x2c98 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD 0x2c99 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST 0x2c9C #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL 0x2ca0 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR 0x2ca1 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK 0x2ca2 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC 0x2ca3 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL 0x2ca8 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR 0x2ca9 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK 0x2caa #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC 0x2cab #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2 0x2d98 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2 0x2d99 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2 0x2d9a #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2 0x2d9c #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2 0x2da0 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2 0x2da1 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2 0x2da2 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2 0x2da3 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2 0x2da8 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2 0x2da9 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2 0x2daa #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2 0x2dab #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2 0x2db0 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2 0x2db1 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2 0x2db2 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2 0x2db3 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a #define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b #define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c #define PCI_DEVICE_ID_INTEL_X58_HUB_MGMT 0x342e #define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 #define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 #define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 #define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433 #define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 #define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 #define PCI_DEVICE_ID_INTEL_82854_HB 0x358c #define PCI_DEVICE_ID_INTEL_82854_IG 0x358e #define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580 #define PCI_DEVICE_ID_INTEL_82855GM_IG 0x3582 #define PCI_DEVICE_ID_INTEL_E7520_MCH 0x3590 #define PCI_DEVICE_ID_INTEL_E7320_MCH 0x3592 #define PCI_DEVICE_ID_INTEL_MCH_PA 0x3595 #define PCI_DEVICE_ID_INTEL_MCH_PA1 0x3596 #define PCI_DEVICE_ID_INTEL_MCH_PB 0x3597 #define PCI_DEVICE_ID_INTEL_MCH_PB1 0x3598 #define PCI_DEVICE_ID_INTEL_MCH_PC 0x3599 #define PCI_DEVICE_ID_INTEL_MCH_PC1 0x359a #define PCI_DEVICE_ID_INTEL_E7525_MCH 0x359e #define PCI_DEVICE_ID_INTEL_I7300_MCH_ERR 0x360c #define PCI_DEVICE_ID_INTEL_I7300_MCH_FB0 0x360f #define PCI_DEVICE_ID_INTEL_I7300_MCH_FB1 0x3610 #define PCI_DEVICE_ID_INTEL_IOAT_CNB 0x360b #define PCI_DEVICE_ID_INTEL_FBD_CNB 0x360c #define PCI_DEVICE_ID_INTEL_IOAT_JSF0 0x3710 #define PCI_DEVICE_ID_INTEL_IOAT_JSF1 0x3711 #define PCI_DEVICE_ID_INTEL_IOAT_JSF2 0x3712 #define PCI_DEVICE_ID_INTEL_IOAT_JSF3 0x3713 #define PCI_DEVICE_ID_INTEL_IOAT_JSF4 0x3714 #define PCI_DEVICE_ID_INTEL_IOAT_JSF5 0x3715 #define PCI_DEVICE_ID_INTEL_IOAT_JSF6 0x3716 #define PCI_DEVICE_ID_INTEL_IOAT_JSF7 0x3717 #define PCI_DEVICE_ID_INTEL_IOAT_JSF8 0x3718 #define PCI_DEVICE_ID_INTEL_IOAT_JSF9 0x3719 #define PCI_DEVICE_ID_INTEL_ICH10_0 0x3a14 #define PCI_DEVICE_ID_INTEL_ICH10_1 0x3a16 #define PCI_DEVICE_ID_INTEL_ICH10_2 0x3a18 #define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a #define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 #define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN 0x3b00 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX 0x3b1f #define PCI_DEVICE_ID_INTEL_IOAT_SNB0 0x3c20 #define PCI_DEVICE_ID_INTEL_IOAT_SNB1 0x3c21 #define PCI_DEVICE_ID_INTEL_IOAT_SNB2 0x3c22 #define PCI_DEVICE_ID_INTEL_IOAT_SNB3 0x3c23 #define PCI_DEVICE_ID_INTEL_IOAT_SNB4 0x3c24 #define PCI_DEVICE_ID_INTEL_IOAT_SNB5 0x3c25 #define PCI_DEVICE_ID_INTEL_IOAT_SNB6 0x3c26 #define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27 #define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e #define PCI_DEVICE_ID_INTEL_IOAT_SNB9 0x3c2f #define PCI_DEVICE_ID_INTEL_UNC_HA 0x3c46 #define PCI_DEVICE_ID_INTEL_UNC_IMC0 0x3cb0 #define PCI_DEVICE_ID_INTEL_UNC_IMC1 0x3cb1 #define PCI_DEVICE_ID_INTEL_UNC_IMC2 0x3cb4 #define PCI_DEVICE_ID_INTEL_UNC_IMC3 0x3cb5 #define PCI_DEVICE_ID_INTEL_UNC_QPI0 0x3c41 #define PCI_DEVICE_ID_INTEL_UNC_QPI1 0x3c42 #define PCI_DEVICE_ID_INTEL_UNC_R2PCIE 0x3c43 #define PCI_DEVICE_ID_INTEL_UNC_R3QPI0 0x3c44 #define PCI_DEVICE_ID_INTEL_UNC_R3QPI1 0x3c45 #define PCI_DEVICE_ID_INTEL_JAKETOWN_UBOX 0x3ce0 #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f #define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 #define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 #define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 #define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030 #define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 #define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 #define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff #define PCI_DEVICE_ID_INTEL_EP80579_0 0x5031 #define PCI_DEVICE_ID_INTEL_EP80579_1 0x5032 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 #define PCI_DEVICE_ID_INTEL_82437VX 0x7030 #define PCI_DEVICE_ID_INTEL_82439TX 0x7100 #define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 #define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120 #define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 #define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122 #define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 #define PCI_DEVICE_ID_INTEL_82810E_MC 0x7124 #define PCI_DEVICE_ID_INTEL_82810E_IG 0x7125 #define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 #define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 #define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 #define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 #define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 #define PCI_DEVICE_ID_INTEL_440MX 0x7195 #define PCI_DEVICE_ID_INTEL_440MX_6 0x7196 #define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198 #define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199 #define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b #define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 #define PCI_DEVICE_ID_INTEL_82443GX_2 0x71a2 #define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601 #define PCI_DEVICE_ID_INTEL_SCH_LPC 0x8119 #define PCI_DEVICE_ID_INTEL_SCH_IDE 0x811a #define PCI_DEVICE_ID_INTEL_ITC_LPC 0x8186 #define PCI_DEVICE_ID_INTEL_82454GX 0x84c4 #define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 #define PCI_DEVICE_ID_INTEL_82451NX 0x84ca #define PCI_DEVICE_ID_INTEL_82454NX 0x84cb #define PCI_DEVICE_ID_INTEL_84460GX 0x84ea #define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500 #define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 #define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 #define PCI_VENDOR_ID_SCALEMP 0x8686 #define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 #define PCI_VENDOR_ID_COMPUTONE 0x8e0e #define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 #define PCI_DEVICE_ID_COMPUTONE_PG 0x0302 #define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e #define PCI_SUBDEVICE_ID_COMPUTONE_PG4 0x0001 #define PCI_SUBDEVICE_ID_COMPUTONE_PG8 0x0002 #define PCI_SUBDEVICE_ID_COMPUTONE_PG6 0x0003 #define PCI_VENDOR_ID_KTI 0x8e2e #define PCI_VENDOR_ID_ADAPTEC 0x9004 #define PCI_DEVICE_ID_ADAPTEC_7810 0x1078 #define PCI_DEVICE_ID_ADAPTEC_7821 0x2178 #define PCI_DEVICE_ID_ADAPTEC_38602 0x3860 #define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 #define PCI_DEVICE_ID_ADAPTEC_7855 0x5578 #define PCI_DEVICE_ID_ADAPTEC_3860 0x6038 #define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 #define PCI_DEVICE_ID_ADAPTEC_7860 0x6078 #define PCI_DEVICE_ID_ADAPTEC_7861 0x6178 #define PCI_DEVICE_ID_ADAPTEC_7870 0x7078 #define PCI_DEVICE_ID_ADAPTEC_7871 0x7178 #define PCI_DEVICE_ID_ADAPTEC_7872 0x7278 #define PCI_DEVICE_ID_ADAPTEC_7873 0x7378 #define PCI_DEVICE_ID_ADAPTEC_7874 0x7478 #define PCI_DEVICE_ID_ADAPTEC_7895 0x7895 #define PCI_DEVICE_ID_ADAPTEC_7880 0x8078 #define PCI_DEVICE_ID_ADAPTEC_7881 0x8178 #define PCI_DEVICE_ID_ADAPTEC_7882 0x8278 #define PCI_DEVICE_ID_ADAPTEC_7883 0x8378 #define PCI_DEVICE_ID_ADAPTEC_7884 0x8478 #define PCI_DEVICE_ID_ADAPTEC_7885 0x8578 #define PCI_DEVICE_ID_ADAPTEC_7886 0x8678 #define PCI_DEVICE_ID_ADAPTEC_7887 0x8778 #define PCI_DEVICE_ID_ADAPTEC_7888 0x8878 #define PCI_VENDOR_ID_ADAPTEC2 0x9005 #define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010 #define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011 #define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013 #define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f #define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050 #define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051 #define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f #define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080 #define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081 #define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083 #define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f #define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0 #define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1 #define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3 #define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf #define PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN 0x0500 #define PCI_DEVICE_ID_ADAPTEC2_SCAMP 0x0503 #define PCI_VENDOR_ID_HOLTEK 0x9412 #define PCI_DEVICE_ID_HOLTEK_6565 0x6565 #define PCI_VENDOR_ID_NETMOS 0x9710 #define PCI_DEVICE_ID_NETMOS_9705 0x9705 #define PCI_DEVICE_ID_NETMOS_9715 0x9715 #define PCI_DEVICE_ID_NETMOS_9735 0x9735 #define PCI_DEVICE_ID_NETMOS_9745 0x9745 #define PCI_DEVICE_ID_NETMOS_9755 0x9755 #define PCI_DEVICE_ID_NETMOS_9805 0x9805 #define PCI_DEVICE_ID_NETMOS_9815 0x9815 #define PCI_DEVICE_ID_NETMOS_9835 0x9835 #define PCI_DEVICE_ID_NETMOS_9845 0x9845 #define PCI_DEVICE_ID_NETMOS_9855 0x9855 #define PCI_DEVICE_ID_NETMOS_9865 0x9865 #define PCI_DEVICE_ID_NETMOS_9900 0x9900 #define PCI_DEVICE_ID_NETMOS_9901 0x9901 #define PCI_DEVICE_ID_NETMOS_9904 0x9904 #define PCI_DEVICE_ID_NETMOS_9912 0x9912 #define PCI_DEVICE_ID_NETMOS_9922 0x9922 #define PCI_VENDOR_ID_3COM_2 0xa727 #define PCI_VENDOR_ID_DIGIUM 0xd161 #define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410 #define PCI_SUBVENDOR_ID_EXSYS 0xd84d #define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 #define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055 #define PCI_VENDOR_ID_TIGERJET 0xe159 #define PCI_DEVICE_ID_TIGERJET_300 0x0001 #define PCI_DEVICE_ID_TIGERJET_100 0x0002 #define PCI_VENDOR_ID_XILINX_RME 0xea60 #define PCI_DEVICE_ID_RME_DIGI32 0x9896 #define PCI_DEVICE_ID_RME_DIGI32_PRO 0x9897 #define PCI_DEVICE_ID_RME_DIGI32_8 0x9898 #define PCI_VENDOR_ID_XEN 0x5853 #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 #define PCI_VENDOR_ID_OCZ 0x1b85 compat-drivers-2012-09-18/include/linux/compat-2.6.32.h0000644000175000017500000001330412025673354021343 0ustar mcgrofmcgrof#ifndef LINUX_26_32_COMPAT_H #define LINUX_26_32_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) #include #include #include #include #include #include #define TCQ_F_CAN_BYPASS 4 static inline int qdisc_qlen(const struct Qdisc *q) { return q->q.qlen; } #define SDIO_VENDOR_ID_INTEL 0x0089 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIFI 0x1403 #define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404 #define SDIO_DEVICE_ID_INTEL_IWMC3200GPS 0x1405 #define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406 /* * Backports 5e928f77a09a07f9dd595bb8a489965d69a83458 * run-time power management cannot really be backported * given that the implementation added bus specific * callbacks that we won't have on older kernels. If * you really want run-time power management or good * power management upgrade your kernel. We'll just * compile this out as if run-time power management was * disabled just as the kernel disables run-time power management * when CONFIG_PM_RUNTIME is disabled. */ static inline void pm_runtime_init(struct device *dev) {} static inline void pm_runtime_remove(struct device *dev) {} static inline int pm_runtime_get(struct device *dev) { return 0; } static inline int pm_runtime_get_sync(struct device *dev) { return 0; } static inline int pm_runtime_put(struct device *dev) { return 0; } static inline int pm_runtime_put_sync(struct device *dev) { return 0; } static inline int pm_runtime_set_active(struct device *dev) { return 0; } static inline void pm_runtime_set_suspended(struct device *dev) { } static inline void pm_runtime_disable(struct device *dev) { } static inline void pm_runtime_put_noidle(struct device *dev) {} static inline void pm_runtime_get_noresume(struct device *dev) {} static inline void flush_delayed_work(struct delayed_work *dwork) { if (del_timer_sync(&dwork->timer)) { /* * This is what would happen on 2.6.32 but since we don't have * access to the singlethread_cpu we can't really backport this, * so avoid really *flush*ing the work... Oh well. Any better ideas? struct cpu_workqueue_struct *cwq; cwq = wq_per_cpu(keventd_wq, get_cpu()); __queue_work(cwq, &dwork->work); put_cpu(); */ } flush_work(&dwork->work); } /* * struct genl_multicast_group was made netns aware through * patch "genetlink: make netns aware" by johannes, we just * force this to always use the default init_net */ #define genl_info_net(x) &init_net /* Just use init_net for older kernels */ #define get_net_ns_by_pid(x) &init_net /* net namespace is lost */ #define genlmsg_multicast_netns(a, b, c, d, e) genlmsg_multicast(b, c, d, e) #define genlmsg_multicast_allns(a, b, c, d) genlmsg_multicast(a, b, c, d) #define genlmsg_unicast(net, skb, pid) genlmsg_unicast(skb, pid) #define dev_change_net_namespace(a, b, c) (-EOPNOTSUPP) #define SET_NETDEV_DEVTYPE(netdev, type) #ifdef __KERNEL__ /* Driver transmit return codes */ enum netdev_tx { BACKPORT_NETDEV_TX_OK = NETDEV_TX_OK, /* driver took care of packet */ BACKPORT_NETDEV_TX_BUSY = NETDEV_TX_BUSY, /* driver tx path was busy*/ BACKPORT_NETDEV_TX_LOCKED = NETDEV_TX_LOCKED, /* driver tx lock was already taken */ }; typedef enum netdev_tx netdev_tx_t; #endif /* __KERNEL__ */ /* * dev_pm_ops is only available on kernels >= 2.6.29, for * older kernels we rely on reverting the work to old * power management style stuff. On 2.6.29 the pci calls * weren't included yet though, so include them here. */ #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,29)) #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ struct dev_pm_ops name = { \ .suspend = suspend_fn ## _compat, \ .resume = resume_fn ## _compat, \ .freeze = suspend_fn ## _compat, \ .thaw = resume_fn ## _compat, \ .poweroff = suspend_fn ## _compat, \ .restore = resume_fn ## _compat, \ } #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) /* * Use this if you want to use the same suspend and resume callbacks for suspend * to RAM and hibernation. */ #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ struct dev_pm_ops name = { \ .suspend = suspend_fn, \ .resume = resume_fn, \ .freeze = suspend_fn, \ .thaw = resume_fn, \ .poweroff = suspend_fn, \ .restore = resume_fn, \ } #else #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) #endif /* >= 2.6.29 */ #define wireless_send_event(a, b, c, d) wireless_send_event(a, b, c, (char * ) d) /* The export symbol in changed in compat/patches/15-symbol-export-conflicts.patch */ #define ieee80211_rx(hw, skb) mac80211_ieee80211_rx(hw, skb) #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) #define lockdep_assert_held(l) do { } while (0) /* * Similar to the struct tm in userspace , but it needs to be here so * that the kernel source is self contained. */ struct tm { /* * the number of seconds after the minute, normally in the range * 0 to 59, but can be up to 60 to allow for leap seconds */ int tm_sec; /* the number of minutes after the hour, in the range 0 to 59*/ int tm_min; /* the number of hours past midnight, in the range 0 to 23 */ int tm_hour; /* the day of the month, in the range 1 to 31 */ int tm_mday; /* the number of months since January, in the range 0 to 11 */ int tm_mon; /* the number of years since 1900 */ long tm_year; /* the number of days since Sunday, in the range 0 to 6 */ int tm_wday; /* the number of days since January 1, in the range 0 to 365 */ int tm_yday; }; void time_to_tm(time_t totalsecs, int offset, struct tm *result); #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) */ #endif /* LINUX_26_32_COMPAT_H */ compat-drivers-2012-09-18/include/linux/ath9k_platform.h0000644000175000017500000000235312026211315022242 0ustar mcgrofmcgrof/* * Copyright (c) 2008 Atheros Communications Inc. * Copyright (c) 2009 Gabor Juhos * Copyright (c) 2009 Imre Kaloz * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _LINUX_ATH9K_PLATFORM_H #define _LINUX_ATH9K_PLATFORM_H #define ATH9K_PLAT_EEP_MAX_WORDS 2048 struct ath9k_platform_data { u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; u8 *macaddr; int led_pin; u32 gpio_mask; u32 gpio_val; bool is_clk_25mhz; int (*get_mac_revision)(void); int (*external_reset)(void); }; #endif /* _LINUX_ATH9K_PLATFORM_H */ compat-drivers-2012-09-18/include/linux/compat-3.1.h0000644000175000017500000000772012025673354021121 0ustar mcgrofmcgrof#ifndef LINUX_3_1_COMPAT_H #define LINUX_3_1_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)) #include #include #include #include #include /* This backports: * * commit 36a26c69b4c70396ef569c3452690fba0c1dec08 * Author: Nicholas Bellinger * Date: Tue Jul 26 00:35:26 2011 -0700 * * kernel.h: Add DIV_ROUND_UP_ULL and DIV_ROUND_UP_SECTOR_T macro usage */ #define DIV_ROUND_UP_ULL(ll,d) \ ({ unsigned long long _tmp = (ll)+(d)-1; do_div(_tmp, d); _tmp; }) /* Backports 56f8a75c */ static inline bool ip_is_fragment(const struct iphdr *iph) { return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0; } /* mask __netdev_alloc_skb_ip_align as RHEL6 backports this */ #define __netdev_alloc_skb_ip_align(a,b,c) compat__netdev_alloc_skb_ip_align(a,b,c) static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, unsigned int length, gfp_t gfp) { struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); if (NET_IP_ALIGN && skb) skb_reserve(skb, NET_IP_ALIGN); return skb; } /* * Getting something that works in C and CPP for an arg that may or may * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1" * we match on the placeholder define, insert the "0," for arg1 and generate * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one). * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when * the last step cherry picks the 2nd arg, we get a zero. */ #define __ARG_PLACEHOLDER_1 0, #define config_enabled(cfg) _config_enabled(cfg) #define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) #define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0) #define ___config_enabled(__ignored, val, ...) val #define genl_dump_check_consistent(cb, user_hdr, family) /* * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm', * 0 otherwise. * */ #define IS_ENABLED(option) \ (config_enabled(option) || config_enabled(option##_MODULE)) #define IFF_TX_SKB_SHARING 0x10000 /* The interface supports sharing * skbs on transmit */ #define PCMCIA_DEVICE_MANF_CARD_PROD_ID3(manf, card, v3, vh3) { \ .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ PCMCIA_DEV_ID_MATCH_CARD_ID| \ PCMCIA_DEV_ID_MATCH_PROD_ID3, \ .manf_id = (manf), \ .card_id = (card), \ .prod_id = { NULL, NULL, (v3), NULL }, \ .prod_id_hash = { 0, 0, (vh3), 0 }, } /* * This has been defined in include/linux/security.h for some time, but was * only given an EXPORT_SYMBOL for 3.1. Add a compat_* definition to avoid * breaking the compile. */ #define security_sk_clone(a, b) compat_security_sk_clone(a, b) static inline void security_sk_clone(const struct sock *sk, struct sock *newsk) { } /* * In many versions, several architectures do not seem to include an * atomic64_t implementation, and do not include the software emulation from * asm-generic/atomic64_t. * Detect and handle this here. */ #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) && !defined(ATOMIC64_INIT) && !defined(CONFIG_X86) && !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) && defined(CONFIG_ARM) && !defined(CONFIG_GENERIC_ATOMIC64)) #include #endif /* mask ida_simple_get as RHEL6 backports this */ #define ida_simple_get(a,b,c,d) compat_ida_simple_get(a,b,c,d) int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, gfp_t gfp_mask); /* mask ida_simple_remove as RHEL6 backports this */ #define ida_simple_remove(a,b) compat_ida_simple_remove(a,b) void ida_simple_remove(struct ida *ida, unsigned int id); /* mask cpufreq_quick_get_max as RHEL6 backports this */ #define cpufreq_quick_get_max(a) compat_cpufreq_quick_get_max(a) unsigned int cpufreq_quick_get_max(unsigned int cpu); #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)) */ #endif /* LINUX_3_1_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.29.h0000644000175000017500000003246512025673354021362 0ustar mcgrofmcgrof#ifndef LINUX_26_29_COMPAT_H #define LINUX_26_29_COMPAT_H #include #include #include /* * I kow this looks odd.. but 2.6.32 added the netdev_tx_t * and we backport that there so inlcude that header first * as we need it for the netdev ops. */ #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) #include #include #include #include /* backports */ static inline void usb_autopm_put_interface_async(struct usb_interface *intf) { } static inline int usb_autopm_get_interface_async(struct usb_interface *intf) { return 0; } #if \ defined(CONFIG_ALPHA) || defined(CONFIG_AVR32) || \ defined(CONFIG_BLACKFIN) || defined(CONFIG_CRIS) || \ defined(CONFIG_H8300) || defined(CONFIG_IA64) || \ defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \ defined(CONFIG_PARISC) || defined(CONFIG_S390) || \ defined(CONFIG_PPC64) || defined(CONFIG_PPC32) || \ defined(CONFIG_SUPERH) || defined(CONFIG_SPARC) || \ defined(CONFIG_FRV) || defined(CONFIG_X86) || \ defined(CONFIG_M32R) || defined(CONFIG_M68K) || \ defined(CONFIG_MN10300) || defined(CONFIG_XTENSA) || \ defined(CONFIG_ARM) #include #else typedef struct { volatile int counter; } atomic_t; #ifdef CONFIG_64BIT typedef struct { volatile long counter; } atomic64_t; #endif /* CONFIG_64BIT */ #endif #define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ /* * Older kernels do not have struct net_device_ops but what we can * do is just define the data structure and use a caller to let us * set the data structure's routines onto the old netdev, essentially * doing it the old way. This avoids huge deltas on our backports. */ /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are * optional and can be filled with a null pointer. * * int (*ndo_init)(struct net_device *dev); * This function is called once when network device is registered. * The network device can use this to any late stage initializaton * or semantic validattion. It can fail with an error code which will * be propogated back to register_netdev * * void (*ndo_uninit)(struct net_device *dev); * This function is called when device is unregistered or when registration * fails. It is not called if init fails. * * int (*ndo_open)(struct net_device *dev); * This function is called when network device transistions to the up * state. * * int (*ndo_stop)(struct net_device *dev); * This function is called when network device transistions to the down * state. * * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, * struct net_device *dev); * Called when a packet needs to be transmitted. * Must return NETDEV_TX_OK , NETDEV_TX_BUSY. * (can also return NETDEV_TX_LOCKED iff NETIF_F_LLTX) * Required can not be NULL. * * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb); * Called to decide which queue to when device supports multiple * transmit queues. * * void (*ndo_change_rx_flags)(struct net_device *dev, int flags); * This function is called to allow device receiver to make * changes to configuration when multicast or promiscious is enabled. * * void (*ndo_set_rx_mode)(struct net_device *dev); * This function is called device changes address list filtering. * * void (*ndo_set_multicast_list)(struct net_device *dev); * This function is called when the multicast address list changes. * * int (*ndo_set_mac_address)(struct net_device *dev, void *addr); * This function is called when the Media Access Control address * needs to be changed. If this interface is not defined, the * mac address can not be changed. * * int (*ndo_validate_addr)(struct net_device *dev); * Test if Media Access Control address is valid for the device. * * int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); * Called when a user request an ioctl which can't be handled by * the generic interface code. If not defined ioctl's return * not supported error code. * * int (*ndo_set_config)(struct net_device *dev, struct ifmap *map); * Used to set network devices bus interface parameters. This interface * is retained for legacy reason, new devices should use the bus * interface (PCI) for low level management. * * int (*ndo_change_mtu)(struct net_device *dev, int new_mtu); * Called when a user wants to change the Maximum Transfer Unit * of a device. If not defined, any request to change MTU will * will return an error. * * void (*ndo_tx_timeout)(struct net_device *dev); * Callback uses when the transmitter has not made any progress * for dev->watchdog ticks. * * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); * Called when a user wants to get the network device usage * statistics. If not defined, the counters in dev->stats will * be used. * * void (*ndo_vlan_rx_register)(struct net_device *dev, struct vlan_group *grp); * If device support VLAN receive accleration * (ie. dev->features & NETIF_F_HW_VLAN_RX), then this function is called * when vlan groups for the device changes. Note: grp is NULL * if no vlan's groups are being used. * * void (*ndo_vlan_rx_add_vid)(struct net_device *dev, unsigned short vid); * If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER) * this function is called when a VLAN id is registered. * * void (*ndo_vlan_rx_kill_vid)(struct net_device *dev, unsigned short vid); * If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER) * this function is called when a VLAN id is unregistered. * * void (*ndo_poll_controller)(struct net_device *dev); * * SR-IOV management functions. * int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac); * int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos); * int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate); * int (*ndo_get_vf_config)(struct net_device *dev, * int vf, struct ifla_vf_info *ivf); */ #define HAVE_NET_DEVICE_OPS struct net_device_ops { int (*ndo_init)(struct net_device *dev); void (*ndo_uninit)(struct net_device *dev); int (*ndo_open)(struct net_device *dev); int (*ndo_stop)(struct net_device *dev); netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb); void (*ndo_change_rx_flags)(struct net_device *dev, int flags); void (*ndo_set_rx_mode)(struct net_device *dev); void (*ndo_set_multicast_list)(struct net_device *dev); int (*ndo_set_mac_address)(struct net_device *dev, void *addr); int (*ndo_validate_addr)(struct net_device *dev); int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); int (*ndo_set_config)(struct net_device *dev, struct ifmap *map); int (*ndo_change_mtu)(struct net_device *dev, int new_mtu); int (*ndo_neigh_setup)(struct net_device *dev, struct neigh_parms *); void (*ndo_tx_timeout) (struct net_device *dev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev, struct rtnl_link_stats64 *storage); #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) */ struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); void (*ndo_vlan_rx_register)(struct net_device *dev, struct vlan_group *grp); void (*ndo_vlan_rx_add_vid)(struct net_device *dev, unsigned short vid); void (*ndo_vlan_rx_kill_vid)(struct net_device *dev, unsigned short vid); #ifdef CONFIG_NET_POLL_CONTROLLER void (*ndo_poll_controller)(struct net_device *dev); #endif int (*ndo_set_vf_mac)(struct net_device *dev, int queue, u8 *mac); int (*ndo_set_vf_vlan)(struct net_device *dev, int queue, u16 vlan, u8 qos); int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate); /* * The struct ifla_vf_info was added via b280da8d54b8d82b52f368a8703b7ada6c1744d5 * on the v2.6.34-rc1~233^2~338 release */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) int (*ndo_get_vf_config)(struct net_device *dev, int vf, struct ifla_vf_info *ivf); #endif #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) int (*ndo_fcoe_enable)(struct net_device *dev); int (*ndo_fcoe_disable)(struct net_device *dev); int (*ndo_fcoe_ddp_setup)(struct net_device *dev, u16 xid, struct scatterlist *sgl, unsigned int sgc); int (*ndo_fcoe_ddp_done)(struct net_device *dev, u16 xid); #define NETDEV_FCOE_WWNN 0 #define NETDEV_FCOE_WWPN 1 int (*ndo_fcoe_get_wwn)(struct net_device *dev, u64 *wwn, int type); #endif }; static inline int ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { if (dev->do_ioctl) return dev->do_ioctl(dev, ifr, cmd); return -EOPNOTSUPP; } void netdev_attach_ops(struct net_device *dev, const struct net_device_ops *ops); /** * skb_queue_is_first - check if skb is the first entry in the queue * @list: queue head * @skb: buffer * * Returns true if @skb is the first buffer on the list. */ static inline bool skb_queue_is_first(const struct sk_buff_head *list, const struct sk_buff *skb) { return (skb->prev == (struct sk_buff *) list); } /** * skb_queue_prev - return the prev packet in the queue * @list: queue head * @skb: current buffer * * Return the prev packet in @list before @skb. It is only valid to * call this if skb_queue_is_first() evaluates to false. */ static inline struct sk_buff *skb_queue_prev(const struct sk_buff_head *list, const struct sk_buff *skb) { /* This BUG_ON may seem severe, but if we just return then we * are going to dereference garbage. */ BUG_ON(skb_queue_is_first(list, skb)); return skb->prev; } static inline struct net_device_stats *dev_get_stats(struct net_device *dev) { return dev->get_stats(dev); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) #if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE) extern void usb_unpoison_anchored_urbs(struct usb_anchor *anchor); #endif /* CONFIG_USB */ #endif #define DIV_ROUND_CLOSEST(x, divisor)( \ { \ typeof(divisor) __divisor = divisor; \ (((x) + ((__divisor) / 2)) / (__divisor)); \ } \ ) extern int eth_mac_addr(struct net_device *dev, void *p); extern int eth_change_mtu(struct net_device *dev, int new_mtu); extern int eth_validate_addr(struct net_device *dev); #ifdef CONFIG_NET_NS static inline void write_pnet(struct net **pnet, struct net *net) { *pnet = net; } static inline struct net *read_pnet(struct net * const *pnet) { return *pnet; } #else #define write_pnet(pnet, net) do { (void)(net);} while (0) #define read_pnet(pnet) (&init_net) /* * swap - swap value of @a and @b */ #define swap(a, b) \ do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) #endif extern int init_dummy_netdev(struct net_device *dev); #else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) */ /* Kernels >= 2.6.29 follows */ /* XXX: this can probably just go upstream ! */ static inline void netdev_attach_ops(struct net_device *dev, const struct net_device_ops *ops) { dev->netdev_ops = ops; } /* XXX: this can probably just go upstream! */ static inline int ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { if (dev->netdev_ops && dev->netdev_ops->ndo_do_ioctl) return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd); return -EOPNOTSUPP; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) #define compat_pci_suspend(fn) \ int fn##_compat(struct pci_dev *pdev, pm_message_t state) \ { \ int r; \ \ r = fn(&pdev->dev); \ if (r) \ return r; \ \ pci_save_state(pdev); \ pci_disable_device(pdev); \ pci_set_power_state(pdev, PCI_D3hot); \ \ return 0; \ } #define compat_pci_resume(fn) \ int fn##_compat(struct pci_dev *pdev) \ { \ int r; \ \ pci_set_power_state(pdev, PCI_D0); \ r = pci_enable_device(pdev); \ if (r) \ return r; \ pci_restore_state(pdev); \ \ return fn(&pdev->dev); \ } #elif LINUX_VERSION_CODE == KERNEL_VERSION(2,6,29) #define compat_pci_suspend(fn) \ int fn##_compat(struct device *dev) \ { \ struct pci_dev *pdev = to_pci_dev(dev); \ int r; \ \ r = fn(&pdev->dev); \ if (r) \ return r; \ \ pci_save_state(pdev); \ pci_disable_device(pdev); \ pci_set_power_state(pdev, PCI_D3hot); \ \ return 0; \ } #define compat_pci_resume(fn) \ int fn##_compat(struct device *dev) \ { \ struct pci_dev *pdev = to_pci_dev(dev); \ int r; \ \ pci_set_power_state(pdev, PCI_D0); \ r = pci_enable_device(pdev); \ if (r) \ return r; \ pci_restore_state(pdev); \ \ return fn(&pdev->dev); \ } #else #define compat_pci_suspend(fn) #define compat_pci_resume(fn) #endif #define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */ #endif /* LINUX_26_29_COMPAT_H */ compat-drivers-2012-09-18/include/linux/pm_qos.h0000644000175000017500000000046512025673354020634 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_PM_QOS_H #define _COMPAT_LINUX_PM_QOS_H 1 #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) #include_next #else #include #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) */ #endif /* _COMPAT_LINUX_PM_QOS_H */ compat-drivers-2012-09-18/include/linux/pm_qos_params.h0000644000175000017500000000202512025673354022171 0ustar mcgrofmcgrof#include #ifndef __COMPAT_LINUX_PM_QOS_PARAMS_H #define __COMPAT_LINUX_PM_QOS_PARAMS_H #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) #include_next #else /* interface for the pm_qos_power infrastructure of the linux kernel. * * Mark Gross */ #include #include #include #define PM_QOS_RESERVED 0 #define PM_QOS_CPU_DMA_LATENCY 1 #define PM_QOS_NETWORK_LATENCY 2 #define PM_QOS_NETWORK_THROUGHPUT 3 #define PM_QOS_SYSTEM_BUS_FREQ 4 #define PM_QOS_NUM_CLASSES 5 #define PM_QOS_DEFAULT_VALUE -1 int pm_qos_add_requirement(int qos, char *name, s32 value); int pm_qos_update_requirement(int qos, char *name, s32 new_value); void pm_qos_remove_requirement(int qos, char *name); int pm_qos_requirement(int qos); int pm_qos_add_notifier(int qos, struct notifier_block *notifier); int pm_qos_remove_notifier(int qos, struct notifier_block *notifier); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) */ #endif compat-drivers-2012-09-18/include/linux/compat-2.6.35.h0000644000175000017500000000345612025673354021355 0ustar mcgrofmcgrof#ifndef LINUX_26_35_COMPAT_H #define LINUX_26_35_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) #include #include #include #include /* added on linux/kernel.h */ #define USHRT_MAX ((u16)(~0U)) #define SHRT_MAX ((s16)(USHRT_MAX>>1)) #define SHRT_MIN ((s16)(-SHRT_MAX - 1)) #define SDIO_BUS_ECSI 0x20 /* Enable continuous SPI interrupt */ #define SDIO_BUS_SCSI 0x40 /* Support continuous SPI interrupt */ #define netdev_hw_addr dev_mc_list /* mask irq_set_affinity_hint as RHEL6 backports this */ #define irq_set_affinity_hint(a,b) compat_irq_set_affinity_hint(a,b) /* * We cannot backport this guy as the IRQ data structure * was modified in the kernel itself to support this. We * treat the system as uni-processor in this case. */ static inline int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m) { return -EINVAL; } static inline wait_queue_head_t *sk_sleep(struct sock *sk) { return sk->sk_sleep; } #define sdio_writeb_readb(func, write_byte, addr, err_ret) sdio_readb(func, addr, err_ret) /* mask hex_to_bin as RHEL6 backports this */ #define hex_to_bin(a) compat_hex_to_bin(a) int hex_to_bin(char ch); extern loff_t noop_llseek(struct file *file, loff_t offset, int origin); #define pm_qos_request(_qos) pm_qos_requirement(_qos) /* mask usb_pipe_endpoint as RHEL6 backports this */ #define usb_pipe_endpoint(a,b) compat_usb_pipe_endpoint(a,b) static inline struct usb_host_endpoint * usb_pipe_endpoint(struct usb_device *dev, unsigned int pipe) { struct usb_host_endpoint **eps; eps = usb_pipein(pipe) ? dev->ep_in : dev->ep_out; return eps[usb_pipeendpoint(pipe)]; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) */ #endif /* LINUX_26_35_COMPAT_H */ compat-drivers-2012-09-18/include/linux/rndis.h0000644000175000017500000004143612026211315020442 0ustar mcgrofmcgrof/* * Remote Network Driver Interface Specification (RNDIS) * definitions of the magic numbers used by this protocol */ /* Remote NDIS Versions */ #define RNDIS_MAJOR_VERSION 0x00000001 #define RNDIS_MINOR_VERSION 0x00000000 /* Device Flags */ #define RNDIS_DF_CONNECTIONLESS 0x00000001U #define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U #define RNDIS_DF_RAW_DATA 0x00000004U /* * Codes for "msg_type" field of rndis messages; * only the data channel uses packet messages (maybe batched); * everything else goes on the control channel. */ #define RNDIS_MSG_COMPLETION 0x80000000 #define RNDIS_MSG_PACKET 0x00000001 /* 1-N packets */ #define RNDIS_MSG_INIT 0x00000002 #define RNDIS_MSG_INIT_C (RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION) #define RNDIS_MSG_HALT 0x00000003 #define RNDIS_MSG_QUERY 0x00000004 #define RNDIS_MSG_QUERY_C (RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION) #define RNDIS_MSG_SET 0x00000005 #define RNDIS_MSG_SET_C (RNDIS_MSG_SET|RNDIS_MSG_COMPLETION) #define RNDIS_MSG_RESET 0x00000006 #define RNDIS_MSG_RESET_C (RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION) #define RNDIS_MSG_INDICATE 0x00000007 #define RNDIS_MSG_KEEPALIVE 0x00000008 #define RNDIS_MSG_KEEPALIVE_C (RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION) /* * Reserved message type for private communication between lower-layer host * driver and remote device, if necessary. */ #define RNDIS_MSG_BUS 0xff000001 /* codes for "status" field of completion messages */ #define RNDIS_STATUS_SUCCESS 0x00000000 #define RNDIS_STATUS_PENDING 0x00000103 /* Status codes */ #define RNDIS_STATUS_NOT_RECOGNIZED 0x00010001 #define RNDIS_STATUS_NOT_COPIED 0x00010002 #define RNDIS_STATUS_NOT_ACCEPTED 0x00010003 #define RNDIS_STATUS_CALL_ACTIVE 0x00010007 #define RNDIS_STATUS_ONLINE 0x40010003 #define RNDIS_STATUS_RESET_START 0x40010004 #define RNDIS_STATUS_RESET_END 0x40010005 #define RNDIS_STATUS_RING_STATUS 0x40010006 #define RNDIS_STATUS_CLOSED 0x40010007 #define RNDIS_STATUS_WAN_LINE_UP 0x40010008 #define RNDIS_STATUS_WAN_LINE_DOWN 0x40010009 #define RNDIS_STATUS_WAN_FRAGMENT 0x4001000A #define RNDIS_STATUS_MEDIA_CONNECT 0x4001000B #define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000C #define RNDIS_STATUS_HARDWARE_LINE_UP 0x4001000D #define RNDIS_STATUS_HARDWARE_LINE_DOWN 0x4001000E #define RNDIS_STATUS_INTERFACE_UP 0x4001000F #define RNDIS_STATUS_INTERFACE_DOWN 0x40010010 #define RNDIS_STATUS_MEDIA_BUSY 0x40010011 #define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 #define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION #define RNDIS_STATUS_LINK_SPEED_CHANGE 0x40010013L #define RNDIS_STATUS_NOT_RESETTABLE 0x80010001 #define RNDIS_STATUS_SOFT_ERRORS 0x80010003 #define RNDIS_STATUS_HARD_ERRORS 0x80010004 #define RNDIS_STATUS_BUFFER_OVERFLOW 0x80000005 #define RNDIS_STATUS_FAILURE 0xC0000001 #define RNDIS_STATUS_RESOURCES 0xC000009A #define RNDIS_STATUS_NOT_SUPPORTED 0xc00000BB #define RNDIS_STATUS_CLOSING 0xC0010002 #define RNDIS_STATUS_BAD_VERSION 0xC0010004 #define RNDIS_STATUS_BAD_CHARACTERISTICS 0xC0010005 #define RNDIS_STATUS_ADAPTER_NOT_FOUND 0xC0010006 #define RNDIS_STATUS_OPEN_FAILED 0xC0010007 #define RNDIS_STATUS_DEVICE_FAILED 0xC0010008 #define RNDIS_STATUS_MULTICAST_FULL 0xC0010009 #define RNDIS_STATUS_MULTICAST_EXISTS 0xC001000A #define RNDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B #define RNDIS_STATUS_REQUEST_ABORTED 0xC001000C #define RNDIS_STATUS_RESET_IN_PROGRESS 0xC001000D #define RNDIS_STATUS_CLOSING_INDICATING 0xC001000E #define RNDIS_STATUS_INVALID_PACKET 0xC001000F #define RNDIS_STATUS_OPEN_LIST_FULL 0xC0010010 #define RNDIS_STATUS_ADAPTER_NOT_READY 0xC0010011 #define RNDIS_STATUS_ADAPTER_NOT_OPEN 0xC0010012 #define RNDIS_STATUS_NOT_INDICATING 0xC0010013 #define RNDIS_STATUS_INVALID_LENGTH 0xC0010014 #define RNDIS_STATUS_INVALID_DATA 0xC0010015 #define RNDIS_STATUS_BUFFER_TOO_SHORT 0xC0010016 #define RNDIS_STATUS_INVALID_OID 0xC0010017 #define RNDIS_STATUS_ADAPTER_REMOVED 0xC0010018 #define RNDIS_STATUS_UNSUPPORTED_MEDIA 0xC0010019 #define RNDIS_STATUS_GROUP_ADDRESS_IN_USE 0xC001001A #define RNDIS_STATUS_FILE_NOT_FOUND 0xC001001B #define RNDIS_STATUS_ERROR_READING_FILE 0xC001001C #define RNDIS_STATUS_ALREADY_MAPPED 0xC001001D #define RNDIS_STATUS_RESOURCE_CONFLICT 0xC001001E #define RNDIS_STATUS_NO_CABLE 0xC001001F #define RNDIS_STATUS_INVALID_SAP 0xC0010020 #define RNDIS_STATUS_SAP_IN_USE 0xC0010021 #define RNDIS_STATUS_INVALID_ADDRESS 0xC0010022 #define RNDIS_STATUS_VC_NOT_ACTIVATED 0xC0010023 #define RNDIS_STATUS_DEST_OUT_OF_ORDER 0xC0010024 #define RNDIS_STATUS_VC_NOT_AVAILABLE 0xC0010025 #define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE 0xC0010026 #define RNDIS_STATUS_INCOMPATABLE_QOS 0xC0010027 #define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED 0xC0010028 #define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION 0xC0010029 #define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR 0xC0011000 /* codes for RNDIS_OID_GEN_PHYSICAL_MEDIUM */ #define RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED 0x00000000 #define RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN 0x00000001 #define RNDIS_PHYSICAL_MEDIUM_CABLE_MODEM 0x00000002 #define RNDIS_PHYSICAL_MEDIUM_PHONE_LINE 0x00000003 #define RNDIS_PHYSICAL_MEDIUM_POWER_LINE 0x00000004 #define RNDIS_PHYSICAL_MEDIUM_DSL 0x00000005 #define RNDIS_PHYSICAL_MEDIUM_FIBRE_CHANNEL 0x00000006 #define RNDIS_PHYSICAL_MEDIUM_1394 0x00000007 #define RNDIS_PHYSICAL_MEDIUM_WIRELESS_WAN 0x00000008 #define RNDIS_PHYSICAL_MEDIUM_MAX 0x00000009 /* Remote NDIS medium types. */ #define RNDIS_MEDIUM_UNSPECIFIED 0x00000000 #define RNDIS_MEDIUM_802_3 0x00000000 #define RNDIS_MEDIUM_802_5 0x00000001 #define RNDIS_MEDIUM_FDDI 0x00000002 #define RNDIS_MEDIUM_WAN 0x00000003 #define RNDIS_MEDIUM_LOCAL_TALK 0x00000004 #define RNDIS_MEDIUM_ARCNET_RAW 0x00000006 #define RNDIS_MEDIUM_ARCNET_878_2 0x00000007 #define RNDIS_MEDIUM_ATM 0x00000008 #define RNDIS_MEDIUM_WIRELESS_LAN 0x00000009 #define RNDIS_MEDIUM_IRDA 0x0000000A #define RNDIS_MEDIUM_BPC 0x0000000B #define RNDIS_MEDIUM_CO_WAN 0x0000000C #define RNDIS_MEDIUM_1394 0x0000000D /* Not a real medium, defined as an upper-bound */ #define RNDIS_MEDIUM_MAX 0x0000000E /* Remote NDIS medium connection states. */ #define RNDIS_MEDIA_STATE_CONNECTED 0x00000000 #define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001 /* packet filter bits used by RNDIS_OID_GEN_CURRENT_PACKET_FILTER */ #define RNDIS_PACKET_TYPE_DIRECTED 0x00000001 #define RNDIS_PACKET_TYPE_MULTICAST 0x00000002 #define RNDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 #define RNDIS_PACKET_TYPE_BROADCAST 0x00000008 #define RNDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 #define RNDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 #define RNDIS_PACKET_TYPE_SMT 0x00000040 #define RNDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 #define RNDIS_PACKET_TYPE_GROUP 0x00001000 #define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00002000 #define RNDIS_PACKET_TYPE_FUNCTIONAL 0x00004000 #define RNDIS_PACKET_TYPE_MAC_FRAME 0x00008000 /* RNDIS_OID_GEN_MINIPORT_INFO constants */ #define RNDIS_MINIPORT_BUS_MASTER 0x00000001 #define RNDIS_MINIPORT_WDM_DRIVER 0x00000002 #define RNDIS_MINIPORT_SG_LIST 0x00000004 #define RNDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 #define RNDIS_MINIPORT_INDICATES_PACKETS 0x00000010 #define RNDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 #define RNDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 #define RNDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 #define RNDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 #define RNDIS_MINIPORT_IS_NDIS_5 0x00000200 #define RNDIS_MINIPORT_IS_CO 0x00000400 #define RNDIS_MINIPORT_DESERIALIZE 0x00000800 #define RNDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 #define RNDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 #define RNDIS_MINIPORT_NETBOOT_CARD 0x00004000 #define RNDIS_MINIPORT_PM_SUPPORTED 0x00008000 #define RNDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 #define RNDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 #define RNDIS_MINIPORT_HIDDEN 0x00040000 #define RNDIS_MINIPORT_SWENUM 0x00080000 #define RNDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 #define RNDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 #define RNDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 #define RNDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 #define RNDIS_MINIPORT_64BITS_DMA 0x01000000 #define RNDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 #define RNDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 #define RNDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 #define RNDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 #define RNDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 #define RNDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 #define RNDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 #define RNDIS_MAC_OPTION_RESERVED 0x80000000 /* Object Identifiers used by NdisRequest Query/Set Information */ /* General (Required) Objects */ #define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101 #define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102 #define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103 #define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104 #define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 #define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 #define RNDIS_OID_GEN_LINK_SPEED 0x00010107 #define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 #define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 #define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A #define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B #define RNDIS_OID_GEN_VENDOR_ID 0x0001010C #define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D #define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E #define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F #define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110 #define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 #define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112 #define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113 #define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 #define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 #define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 #define RNDIS_OID_GEN_SUPPORTED_GUIDS 0x00010117 #define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 #define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 #define RNDIS_OID_GEN_PHYSICAL_MEDIUM 0x00010202 #define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A #define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B #define RNDIS_OID_GEN_VLAN_ID 0x0001021C /* Optional OIDs */ #define RNDIS_OID_GEN_MEDIA_CAPABILITIES 0x00010201 /* Required statistics OIDs */ #define RNDIS_OID_GEN_XMIT_OK 0x00020101 #define RNDIS_OID_GEN_RCV_OK 0x00020102 #define RNDIS_OID_GEN_XMIT_ERROR 0x00020103 #define RNDIS_OID_GEN_RCV_ERROR 0x00020104 #define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105 /* Optional statistics OIDs */ #define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 #define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 #define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 #define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 #define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 #define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 #define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207 #define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 #define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209 #define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A #define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B #define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C #define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D #define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E #define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F #define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210 #define RNDIS_OID_GEN_NETCARD_LOAD 0x00020211 #define RNDIS_OID_GEN_DEVICE_PROFILE 0x00020212 #define RNDIS_OID_GEN_INIT_TIME_MS 0x00020213 #define RNDIS_OID_GEN_RESET_COUNTS 0x00020214 #define RNDIS_OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 #define RNDIS_OID_GEN_FRIENDLY_NAME 0x00020216 #define RNDIS_OID_GEN_MINIPORT_INFO 0x00020217 #define RNDIS_OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 /* These are connection-oriented general OIDs. */ /* These replace the above OIDs for connection-oriented media. */ #define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101 #define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102 #define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103 #define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104 #define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105 #define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106 #define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107 #define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108 #define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109 #define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A #define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B #define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C #define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D #define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201 #define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202 /* These are connection-oriented statistics OIDs. */ #define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101 #define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102 #define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103 #define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104 #define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105 #define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201 #define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202 #define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203 #define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204 #define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205 #define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206 /* These are objects for Connection-oriented media call-managers. */ #define RNDIS_OID_CO_ADD_PVC 0xFF000001 #define RNDIS_OID_CO_DELETE_PVC 0xFF000002 #define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003 #define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004 #define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005 #define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006 #define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007 #define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008 #define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009 /* 802.3 Objects (Ethernet) */ #define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101 #define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102 #define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103 #define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 #define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105 #define RNDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 #define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 #define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102 #define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 #define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201 #define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 #define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203 #define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204 #define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 #define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 #define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 #define RNDIS_OID_802_11_BSSID 0x0d010101 #define RNDIS_OID_802_11_SSID 0x0d010102 #define RNDIS_OID_802_11_INFRASTRUCTURE_MODE 0x0d010108 #define RNDIS_OID_802_11_ADD_WEP 0x0d010113 #define RNDIS_OID_802_11_REMOVE_WEP 0x0d010114 #define RNDIS_OID_802_11_DISASSOCIATE 0x0d010115 #define RNDIS_OID_802_11_AUTHENTICATION_MODE 0x0d010118 #define RNDIS_OID_802_11_PRIVACY_FILTER 0x0d010119 #define RNDIS_OID_802_11_BSSID_LIST_SCAN 0x0d01011a #define RNDIS_OID_802_11_ENCRYPTION_STATUS 0x0d01011b #define RNDIS_OID_802_11_ADD_KEY 0x0d01011d #define RNDIS_OID_802_11_REMOVE_KEY 0x0d01011e #define RNDIS_OID_802_11_ASSOCIATION_INFORMATION 0x0d01011f #define RNDIS_OID_802_11_CAPABILITY 0x0d010122 #define RNDIS_OID_802_11_PMKID 0x0d010123 #define RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED 0x0d010203 #define RNDIS_OID_802_11_NETWORK_TYPE_IN_USE 0x0d010204 #define RNDIS_OID_802_11_TX_POWER_LEVEL 0x0d010205 #define RNDIS_OID_802_11_RSSI 0x0d010206 #define RNDIS_OID_802_11_RSSI_TRIGGER 0x0d010207 #define RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD 0x0d010209 #define RNDIS_OID_802_11_RTS_THRESHOLD 0x0d01020a #define RNDIS_OID_802_11_SUPPORTED_RATES 0x0d01020e #define RNDIS_OID_802_11_CONFIGURATION 0x0d010211 #define RNDIS_OID_802_11_POWER_MODE 0x0d010216 #define RNDIS_OID_802_11_BSSID_LIST 0x0d010217 /* Plug and Play capabilities */ #define RNDIS_OID_PNP_CAPABILITIES 0xFD010100 #define RNDIS_OID_PNP_SET_POWER 0xFD010101 #define RNDIS_OID_PNP_QUERY_POWER 0xFD010102 #define RNDIS_OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 #define RNDIS_OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 #define RNDIS_OID_PNP_ENABLE_WAKE_UP 0xFD010106 /* RNDIS_PNP_CAPABILITIES.Flags constants */ #define RNDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 #define RNDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 #define RNDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 #define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001 #define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002 #define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005 #define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006 #define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007 #define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001 #define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002 #define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005 #define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006 compat-drivers-2012-09-18/include/linux/compat-2.6.23.h0000644000175000017500000001010112025673354021333 0ustar mcgrofmcgrof#ifndef LINUX_26_23_COMPAT_H #define LINUX_26_23_COMPAT_H #include /* Compat work for < 2.6.23 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) #include #include #include #include #include /* * Tell gcc if a function is cold. The compiler will assume any path * directly leading to the call is unlikely. */ #if !(__GNUC__ == 4 && __GNUC_MINOR__ < 3) /* Mark functions as cold. gcc will assume any path leading to a call * to them will be unlikely. This means a lot of manual unlikely()s * are unnecessary now for any paths leading to the usual suspects * like BUG(), printk(), panic() etc. [but let's keep them for now for * older compilers] * * Early snapshots of gcc 4.3 don't support this and we can't detect this * in the preprocessor, but we can live with this because they're unreleased. * Maketime probing would be overkill here. * * gcc also has a __attribute__((__hot__)) to move hot functions into * a special section, but I don't see any sense in this right now in * the kernel context */ #define __cold __attribute__((__cold__)) #endif /* gcc 4.3 check */ #ifndef __cold #define __cold #endif /* Added as of 2.6.23 in include/linux/netdevice.h */ #define alloc_netdev_mq(sizeof_priv, name, setup, queue) \ alloc_netdev(sizeof_priv, name, setup) #define NETIF_F_MULTI_QUEUE 16384 /* Added as of 2.6.23 on include/linux/netdevice.h */ static inline int netif_is_multiqueue(const struct net_device *dev) { return (!!(NETIF_F_MULTI_QUEUE & dev->features)); } /* 2.6.23 fixed a bug in tcf_destroy_chain and the parameter changed */ static inline void tcf_destroy_chain_compat(struct tcf_proto **fl) { struct tcf_proto *tp; while ((tp = *fl) != NULL) { *fl = tp->next; tp->ops->destroy(tp); module_put(tp->ops->owner); kfree(tp); } } /* dev_mc_list was replaced with dev_addr_list as of 2.6.23, * only new member added is da_synced. */ #define dev_addr_list dev_mc_list #define da_addr dmi_addr #define da_addrlen dmi_addrlen #define da_users dmi_users #define da_gusers dmi_gusers /* dev_set_promiscuity() was moved to __dev_set_promiscuity() on 2.6.23 and * dev_set_promiscuity() became a wrapper. */ #define __dev_set_promiscuity dev_set_promiscuity /* Our own 2.6.22 port on compat.c */ extern void dev_mc_unsync(struct net_device *to, struct net_device *from); extern int dev_mc_sync(struct net_device *to, struct net_device *from); /* Our own 2.6.22 port on compat.c */ extern void __dev_set_rx_mode(struct net_device *dev); /* Simple to add this */ extern int cancel_delayed_work_sync(struct delayed_work *work); #define cancel_delayed_work_sync cancel_rearming_delayed_work #define debugfs_rename(a, b, c, d) 1 /* nl80211 requires multicast group support which is new and added on * 2.6.23. We can't add support for it for older kernels to support it * genl_family structure was changed. Lets just let through the * genl_register_mc_group call. This means no multicast group suppport */ #define genl_register_mc_group(a, b) 0 /** * struct genl_multicast_group - generic netlink multicast group * @name: name of the multicast group, names are per-family * @id: multicast group ID, assigned by the core, to use with * genlmsg_multicast(). * @list: list entry for linking * @family: pointer to family, need not be set before registering */ struct genl_multicast_group { struct genl_family *family; /* private */ struct list_head list; /* private */ char name[GENL_NAMSIZ]; u32 id; }; /* Added as of 2.6.23 */ int pci_try_set_mwi(struct pci_dev *dev); /* Added as of 2.6.23 */ #ifdef CONFIG_PM_SLEEP /* * Tell the freezer that the current task should be frozen by it */ static inline void set_freezable(void) { current->flags &= ~PF_NOFREEZE; } #else static inline void set_freezable(void) {} #endif /* CONFIG_PM_SLEEP */ #else #define tcf_destroy_chain_compat tcf_destroy_chain #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) */ #endif /* LINUX_26_23_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-3.0.h0000644000175000017500000001255612025673354021123 0ustar mcgrofmcgrof#ifndef LINUX_3_0_COMPAT_H #define LINUX_3_0_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)) #include /* This pulls-in a lot of non-exported symbol backports * on kernels older than 2.6.32. There's no harm for not * making this available on kernels < 2.6.32. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) #include /* This backports the 2nd part of: * * commit d9d90e5eb70e09903dadff42099b6c948f814050 * Author: Hugh Dickins * Date: Mon Jun 27 16:18:04 2011 -0700 * * tmpfs: add shmem_read_mapping_page_gfp * * First part is in compat-3.0.c. */ extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp); static inline struct page *shmem_read_mapping_page( struct address_space *mapping, pgoff_t index) { return shmem_read_mapping_page_gfp(mapping, index, mapping_gfp_mask(mapping)); } #endif /* * since commit 1c5cae815d19ffe02bdfda1260949ef2b1806171 * "net: call dev_alloc_name from register_netdevice" dev_alloc_name is * called automatically. This is not implemented in older kernel * versions so it will result in device wrong names. */ static inline int register_netdevice_name(struct net_device *dev) { int err; if (strchr(dev->name, '%')) { err = dev_alloc_name(dev, dev->name); if (err < 0) return err; } return register_netdevice(dev); } #define register_netdevice(dev) register_netdevice_name(dev) /* BCMA core, see drivers/bcma/ */ #ifndef BCMA_CORE /* Broadcom's specific AMBA core, see drivers/bcma/ */ struct bcma_device_id { __u16 manuf; __u16 id; __u8 rev; __u8 class; }; #define BCMA_CORE(_manuf, _id, _rev, _class) \ { .manuf = _manuf, .id = _id, .rev = _rev, .class = _class, } #define BCMA_CORETABLE_END \ { 0, }, #define BCMA_ANY_MANUF 0xFFFF #define BCMA_ANY_ID 0xFFFF #define BCMA_ANY_REV 0xFF #define BCMA_ANY_CLASS 0xFF #endif /* BCMA_CORE */ int mac_pton(const char *s, u8 *mac); int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res); int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res); int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res); int __must_check kstrtol_from_user(const char __user *s, size_t count, unsigned int base, long *res); int __must_check kstrtouint_from_user(const char __user *s, size_t count, unsigned int base, unsigned int *res); int __must_check kstrtoint_from_user(const char __user *s, size_t count, unsigned int base, int *res); int __must_check kstrtou16_from_user(const char __user *s, size_t count, unsigned int base, u16 *res); int __must_check kstrtos16_from_user(const char __user *s, size_t count, unsigned int base, s16 *res); int __must_check kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, u8 *res); int __must_check kstrtos8_from_user(const char __user *s, size_t count, unsigned int base, s8 *res); static inline int __must_check kstrtou64_from_user(const char __user *s, size_t count, unsigned int base, u64 *res) { return kstrtoull_from_user(s, count, base, res); } static inline int __must_check kstrtos64_from_user(const char __user *s, size_t count, unsigned int base, s64 *res) { return kstrtoll_from_user(s, count, base, res); } static inline int __must_check kstrtou32_from_user(const char __user *s, size_t count, unsigned int base, u32 *res) { return kstrtouint_from_user(s, count, base, res); } static inline int __must_check kstrtos32_from_user(const char __user *s, size_t count, unsigned int base, s32 *res) { return kstrtoint_from_user(s, count, base, res); } /* * This adds a nested function everywhere kfree_rcu() was called. This * function frees the memory and is given as a function to call_rcu(). * The rcu callback could happen every time also after the module was * unloaded and this will cause problems. */ #define kfree_rcu(data, rcuhead) do { \ void __kfree_rcu_fn(struct rcu_head *rcu_head) \ { \ void *___ptr; \ ___ptr = container_of(rcu_head, typeof(*(data)), rcuhead);\ kfree(___ptr); \ } \ call_rcu(&(data)->rcuhead, __kfree_rcu_fn); \ } while (0) #ifdef MODULE /* * The define overwriting module_exit is based on the original module_exit * which looks like this: * #define module_exit(exitfn) \ * static inline exitcall_t __exittest(void) \ * { return exitfn; } \ * void cleanup_module(void) __attribute__((alias(#exitfn))); * * We replaced the call to the actual function exitfn() with a call to our * function which calls the original exitfn() and then rcu_barrier() * * As a module will not be unloaded that ofter it should not have a big * performance impact when rcu_barrier() is called on every module exit, * also when no kfree_rcu() backport is used in that module. */ #undef module_exit #define module_exit(exitfn) \ static void __exit __exit_compat(void) \ { \ exitfn(); \ rcu_barrier(); \ } \ void cleanup_module(void) __attribute__((alias("__exit_compat"))); #endif #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)) */ #endif /* LINUX_3_0_COMPAT_H */ compat-drivers-2012-09-18/include/linux/spi/0000755000175000017500000000000012026176500017743 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/linux/spi/libertas_spi.h0000644000175000017500000000161312026211315022567 0ustar mcgrofmcgrof/* * board-specific data for the libertas_spi driver. * * Copyright 2008 Analog Devices Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #ifndef _LIBERTAS_SPI_H_ #define _LIBERTAS_SPI_H_ struct spi_device; struct libertas_spi_platform_data { /* There are two ways to read data from the WLAN module's SPI * interface. Setting 0 or 1 here controls which one is used. * * Usually you want to set use_dummy_writes = 1. * However, if that doesn't work or if you are using a slow SPI clock * speed, you may want to use 0 here. */ u16 use_dummy_writes; /* Board specific setup/teardown */ int (*setup)(struct spi_device *spi); int (*teardown)(struct spi_device *spi); }; #endif compat-drivers-2012-09-18/include/linux/compat-2.6.21.h0000644000175000017500000000060312025673354021337 0ustar mcgrofmcgrof#ifndef LINUX_26_21_COMPAT_H #define LINUX_26_21_COMPAT_H #include /* Compat work for 2.6.21 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) #include #define register_sysctl_table(table) \ ({ \ register_sysctl_table((table), 0); \ }) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) */ #endif /* LINUX_26_21_COMPAT_H */ compat-drivers-2012-09-18/include/linux/olpc-ec.h0000644000175000017500000000041712025673354020655 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_OLPC_EC_H #define _COMPAT_LINUX_OLPC_EC_H #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) #include_next #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(3,6,0)) */ #endif /* _COMPAT_LINUX_OLPC_EC_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.28.h0000644000175000017500000001750512025673354021357 0ustar mcgrofmcgrof#ifndef LINUX_26_28_COMPAT_H #define LINUX_26_28_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) #include #include #include #include #include #include #ifndef ETH_P_PAE #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #endif #include #include typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } compat_cpumask_t; #if defined(CONFIG_X86) || defined(CONFIG_X86_64) || defined(CONFIG_PPC) /* * CONFIG_PHYS_ADDR_T_64BIT was added as new to all architectures * as of 2.6.28 but x86 and ppc had it already. x86 only got phys_addr_t * as of 2.6.25 but then is backported in compat-2.6.25.h */ #else #if defined(CONFIG_64BIT) || defined(CONFIG_X86_PAE) || defned(CONFIG_PPC64) || defined(CONFIG_PHYS_64BIT) #define CONFIG_PHYS_ADDR_T_64BIT 1 typedef u64 phys_addr_t; #else typedef u32 phys_addr_t; #endif #endif /* non x86 and ppc */ #ifndef WARN_ONCE #define WARN_ONCE(condition, format...) ({ \ static int __warned; \ int __ret_warn_once = !!(condition); \ \ if (unlikely(__ret_warn_once)) \ if (WARN(!__warned, format)) \ __warned = 1; \ unlikely(__ret_warn_once); \ }) #endif /* From include/asm-generic/bug.h */ #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) #include #include #include #ifdef pcmcia_parse_tuple #undef pcmcia_parse_tuple #define pcmcia_parse_tuple(tuple, parse) pccard_parse_tuple(tuple, parse) #endif /* From : include/pcmcia/ds.h */ /* loop CIS entries for valid configuration */ int pcmcia_loop_config(struct pcmcia_device *p_dev, int (*conf_check) (struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cfg, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data), void *priv_data); #endif /* CONFIG_PCMCIA */ /* USB anchors were added as of 2.6.23 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) #if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE) #if 0 extern void usb_poison_urb(struct urb *urb); #endif extern void usb_unpoison_urb(struct urb *urb); #if 0 extern void usb_poison_anchored_urbs(struct usb_anchor *anchor); #endif extern int usb_anchor_empty(struct usb_anchor *anchor); #endif /* CONFIG_USB */ #endif void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); /** * skb_queue_is_last - check if skb is the last entry in the queue * @list: queue head * @skb: buffer * * Returns true if @skb is the last buffer on the list. */ static inline bool skb_queue_is_last(const struct sk_buff_head *list, const struct sk_buff *skb) { return (skb->next == (struct sk_buff *) list); } /** * skb_queue_next - return the next packet in the queue * @list: queue head * @skb: current buffer * * Return the next packet in @list after @skb. It is only valid to * call this if skb_queue_is_last() evaluates to false. */ static inline struct sk_buff *skb_queue_next(const struct sk_buff_head *list, const struct sk_buff *skb) { /* This BUG_ON may seem severe, but if we just return then we * are going to dereference garbage. */ BUG_ON(skb_queue_is_last(list, skb)); return skb->next; } /** * __skb_queue_head_init - initialize non-spinlock portions of sk_buff_head * @list: queue to initialize * * This initializes only the list and queue length aspects of * an sk_buff_head object. This allows to initialize the list * aspects of an sk_buff_head without reinitializing things like * the spinlock. It can also be used for on-stack sk_buff_head * objects where the spinlock is known to not be used. */ static inline void __skb_queue_head_init(struct sk_buff_head *list) { list->prev = list->next = (struct sk_buff *)list; list->qlen = 0; } static inline void __skb_queue_splice(const struct sk_buff_head *list, struct sk_buff *prev, struct sk_buff *next) { struct sk_buff *first = list->next; struct sk_buff *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * skb_queue_splice - join two skb lists, this is designed for stacks * @list: the new list to add * @head: the place to add it in the first list */ static inline void skb_queue_splice(const struct sk_buff_head *list, struct sk_buff_head *head) { if (!skb_queue_empty(list)) { __skb_queue_splice(list, (struct sk_buff *) head, head->next); head->qlen += list->qlen; } } /** * skb_queue_splice - join two skb lists and reinitialise the emptied list * @list: the new list to add * @head: the place to add it in the first list * * The list at @list is reinitialised */ static inline void skb_queue_splice_init(struct sk_buff_head *list, struct sk_buff_head *head) { if (!skb_queue_empty(list)) { __skb_queue_splice(list, (struct sk_buff *) head, head->next); head->qlen += list->qlen; __skb_queue_head_init(list); } } /** * skb_queue_splice_tail - join two skb lists and reinitialise the emptied list * @list: the new list to add * @head: the place to add it in the first list * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void skb_queue_splice_tail_init(struct sk_buff_head *list, struct sk_buff_head *head) { if (!skb_queue_empty(list)) { __skb_queue_splice(list, head->prev, (struct sk_buff *) head); head->qlen += list->qlen; __skb_queue_head_init(list); } } /* From include/linux/skbuff.h */ /** * skb_queue_splice_tail - join two skb lists, each list being a queue * @list: the new list to add * @head: the place to add it in the first list */ static inline void skb_queue_splice_tail(const struct sk_buff_head *list, struct sk_buff_head *head) { if (!skb_queue_empty(list)) { __skb_queue_splice(list, head->prev, (struct sk_buff *) head); head->qlen += list->qlen; } } #define skb_queue_walk_from(queue, skb) \ for (; skb != (struct sk_buff *)(queue); \ skb = skb->next) #ifndef DECLARE_TRACE #define TP_PROTO(args...) args #define TP_ARGS(args...) args #define DECLARE_TRACE(name, proto, args) \ static inline void _do_trace_##name(struct tracepoint *tp, proto) \ { } \ static inline void trace_##name(proto) \ { } \ static inline int register_trace_##name(void (*probe)(proto)) \ { \ return -ENOSYS; \ } \ static inline int unregister_trace_##name(void (*probe)(proto)) \ { \ return -ENOSYS; \ } #define EXPORT_TRACEPOINT_SYMBOL_GPL(name) #define EXPORT_TRACEPOINT_SYMBOL(name) #endif /* openSuse includes round_jiffies_up in it's kernel 2.6.27. * This is needed to prevent conflicts with the openSuse definition. */ #define round_jiffies_up backport_round_jiffies_up unsigned long round_jiffies_up(unsigned long j); extern void v2_6_28_skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, int size); #define wake_up_interruptible_poll(x, m) \ __wake_up(x, TASK_INTERRUPTIBLE, 1, (void *) (m)) extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); int pci_wake_from_d3(struct pci_dev *dev, bool enable); #define alloc_workqueue(name, flags, max_active) __create_workqueue(name, flags, max_active) #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) */ #endif /* LINUX_26_28_COMPAT_H */ compat-drivers-2012-09-18/include/linux/eeprom_93cx6.h0000644000175000017500000000537412026211315021547 0ustar mcgrofmcgrof/* Copyright (C) 2004 - 2006 rt2x00 SourceForge Project This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Module: eeprom_93cx6 Abstract: EEPROM reader datastructures for 93cx6 chipsets. Supported chipsets: 93c46, 93c56 and 93c66. */ /* * EEPROM operation defines. */ #define PCI_EEPROM_WIDTH_93C46 6 #define PCI_EEPROM_WIDTH_93C56 8 #define PCI_EEPROM_WIDTH_93C66 8 #define PCI_EEPROM_WIDTH_93C86 8 #define PCI_EEPROM_WIDTH_OPCODE 3 #define PCI_EEPROM_WRITE_OPCODE 0x05 #define PCI_EEPROM_ERASE_OPCODE 0x07 #define PCI_EEPROM_READ_OPCODE 0x06 #define PCI_EEPROM_EWDS_OPCODE 0x10 #define PCI_EEPROM_EWEN_OPCODE 0x13 /** * struct eeprom_93cx6 - control structure for setting the commands * for reading the eeprom data. * @data: private pointer for the driver. * @register_read(struct eeprom_93cx6 *eeprom): handler to * read the eeprom register, this function should set all reg_* fields. * @register_write(struct eeprom_93cx6 *eeprom): handler to * write to the eeprom register by using all reg_* fields. * @width: eeprom width, should be one of the PCI_EEPROM_WIDTH_* defines * @drive_data: Set if we're driving the data line. * @reg_data_in: register field to indicate data input * @reg_data_out: register field to indicate data output * @reg_data_clock: register field to set the data clock * @reg_chip_select: register field to set the chip select * * This structure is used for the communication between the driver * and the eeprom_93cx6 handlers for reading the eeprom. */ struct eeprom_93cx6 { void *data; void (*register_read)(struct eeprom_93cx6 *eeprom); void (*register_write)(struct eeprom_93cx6 *eeprom); int width; char drive_data; char reg_data_in; char reg_data_out; char reg_data_clock; char reg_chip_select; }; extern void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, const u8 word, u16 *data); extern void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word, __le16 *data, const u16 words); extern void eeprom_93cx6_wren(struct eeprom_93cx6 *eeprom, bool enable); extern void eeprom_93cx6_write(struct eeprom_93cx6 *eeprom, u8 addr, u16 data); compat-drivers-2012-09-18/include/linux/pm_runtime.h0000644000175000017500000000050312025673354021506 0ustar mcgrofmcgrof#include #ifndef __COMPAT_LINUX_PM_RUNTIME_H #define __COMPAT_LINUX_PM_RUNTIME_H #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) #include_next #else static inline void pm_runtime_enable(struct device *dev) {} #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) */ #endif compat-drivers-2012-09-18/include/linux/compat-2.6.36.h0000644000175000017500000001436712025673354021361 0ustar mcgrofmcgrof#ifndef LINUX_26_36_COMPAT_H #define LINUX_26_36_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) #include #include #include #include #include #define kparam_block_sysfs_write(a) #define kparam_unblock_sysfs_write(a) /* mask va_format as RHEL6 backports this */ #define va_format compat_va_format struct va_format { const char *fmt; va_list *va; }; #define device_rename(dev, new_name) device_rename(dev, (char *)new_name) #ifdef CONFIG_COMPAT_USB_URB_THREAD_FIX #define usb_scuttle_anchored_urbs(anchor) compat_usb_scuttle_anchored_urbs(anchor) #define usb_get_from_anchor(anchor) compat_usb_get_from_anchor(anchor) #define usb_unlink_anchored_urbs(anchor) compat_usb_unlink_anchored_urbs(anchor) extern void compat_usb_unlink_anchored_urbs(struct usb_anchor *anchor); extern struct urb *compat_usb_get_from_anchor(struct usb_anchor *anchor); extern void compat_usb_scuttle_anchored_urbs(struct usb_anchor *anchor); #endif /** * pcmcia_read_config_byte() - read a byte from a card configuration register * * pcmcia_read_config_byte() reads a byte from a configuration register in * attribute memory. */ static inline int pcmcia_read_config_byte(struct pcmcia_device *p_dev, off_t where, u8 *val) { int ret; conf_reg_t reg = { 0, CS_READ, where, 0 }; ret = pcmcia_access_configuration_register(p_dev, ®); *val = reg.Value; return ret; } /** * pcmcia_write_config_byte() - write a byte to a card configuration register * * pcmcia_write_config_byte() writes a byte to a configuration register in * attribute memory. */ static inline int pcmcia_write_config_byte(struct pcmcia_device *p_dev, off_t where, u8 val) { conf_reg_t reg = { 0, CS_WRITE, where, val }; return pcmcia_access_configuration_register(p_dev, ®); } struct pm_qos_request_list { u32 qos; void *request; }; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) #define pm_qos_add_request(_req, _class, _value) do { \ (_req)->request = #_req; \ (_req)->qos = _class; \ pm_qos_add_requirement((_class), (_req)->request, (_value)); \ } while(0) #define pm_qos_update_request(_req, _value) \ pm_qos_update_requirement((_req)->qos, (_req)->request, (_value)) #define pm_qos_remove_request(_req) \ pm_qos_remove_requirement((_req)->qos, (_req)->request) #else #define pm_qos_add_request(_req, _class, _value) do { \ (_req)->request = pm_qos_add_request((_class), (_value)); \ } while (0) #define pm_qos_update_request(_req, _value) \ pm_qos_update_request((_req)->request, (_value)) #define pm_qos_remove_request(_req) \ pm_qos_remove_request((_req)->request) #endif /* * Dummy printk for disabled debugging statements to use whilst maintaining * gcc's format and side-effect checking. */ /* mask no_printk as RHEL6 backports this */ #define no_printk(a, ...) compat_no_printk(a, ##__VA_ARGS__) static inline __attribute__ ((format (printf, 1, 2))) int no_printk(const char *s, ...) { return 0; } #ifndef alloc_workqueue #define alloc_workqueue(name, flags, max_active) __create_workqueue(name, flags, max_active, 0) #endif #define EXTPROC 0200000 #define TIOCPKT_IOCTL 64 static inline void tty_lock(void) __acquires(kernel_lock) { #ifdef CONFIG_LOCK_KERNEL /* kernel_locked is 1 for !CONFIG_LOCK_KERNEL */ WARN_ON(kernel_locked()); #endif lock_kernel(); } static inline void tty_unlock(void) __releases(kernel_lock) { unlock_kernel(); } #define tty_locked() (kernel_locked()) #define usleep_range(_min, _max) msleep((_max) / 1000) #define __rcu static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {} static inline bool skb_defer_rx_timestamp(struct sk_buff *skb) { return false; } static inline void skb_tx_timestamp(struct sk_buff *skb) { } /* * System-wide workqueues which are always present. * * system_wq is the one used by schedule[_delayed]_work[_on](). * Multi-CPU multi-threaded. There are users which expect relatively * short queue flush time. Don't queue works which can run for too * long. * * system_long_wq is similar to system_wq but may host long running * works. Queue flushing might take relatively long. * * system_nrt_wq is non-reentrant and guarantees that any given work * item is never executed in parallel by multiple CPUs. Queue * flushing might take relatively long. */ extern struct workqueue_struct *system_wq; extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_nrt_wq; void compat_system_workqueue_create(void); void compat_system_workqueue_destroy(void); int compat_schedule_work(struct work_struct *work); int compat_schedule_work_on(int cpu, struct work_struct *work); int compat_schedule_delayed_work(struct delayed_work *dwork, unsigned long delay); int compat_schedule_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay); void compat_flush_scheduled_work(void); enum { /* bit mask for work_busy() return values */ WORK_BUSY_PENDING = 1 << 0, WORK_BUSY_RUNNING = 1 << 1, }; extern unsigned int work_busy(struct work_struct *work); #define schedule_work(work) compat_schedule_work(work) #define schedule_work_on(cpu, work) compat_schedule_work_on(cpu, work) #define schedule_delayed_work(dwork, delay) compat_schedule_delayed_work(dwork, delay) #define schedule_delayed_work_on(cpu, dwork, delay) compat_schedule_delayed_work_on(cpu, dwork, delay) #define flush_scheduled_work(a) compat_flush_scheduled_work(a) #define br_port_exists(dev) (dev->br_port) #else static inline void compat_system_workqueue_create(void) { } static inline void compat_system_workqueue_destroy(void) { } /* * This is not part of The 2.6.37 kernel yet but we * we use it to optimize the backport code we * need to implement. Instead of using ifdefs * to check what version of the check we use * we just replace all checks on current code * with this. I'll submit this upstream too, that * way all we'd have to do is to implement this * for older kernels, then we would not have to * edit the upstrema code for backport efforts. */ #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) */ #endif /* LINUX_26_36_COMPAT_H */ compat-drivers-2012-09-18/include/linux/atomic.h0000644000175000017500000000213212025673354020603 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_ATOMIC_H #define _COMPAT_LINUX_ATOMIC_H 1 #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) #include_next #else #include /** * atomic_inc_not_zero_hint - increment if not null * @v: pointer of type atomic_t * @hint: probable value of the atomic before the increment * * This version of atomic_inc_not_zero() gives a hint of probable * value of the atomic. This helps processor to not read the memory * before doing the atomic read/modify/write cycle, lowering * number of bus transactions on some arches. * * Returns: 0 if increment was not done, 1 otherwise. */ #ifndef atomic_inc_not_zero_hint static inline int atomic_inc_not_zero_hint(atomic_t *v, int hint) { int val, c = hint; /* sanity test, should be removed by compiler if hint is a constant */ if (!hint) return atomic_inc_not_zero(v); do { val = atomic_cmpxchg(v, c, c + 1); if (val == c) return 1; c = val; } while (c); return 0; } #endif #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) */ #endif /* _COMPAT_LINUX_ATOMIC_H */ compat-drivers-2012-09-18/include/linux/compat-3.3.h0000644000175000017500000000626012025673354021121 0ustar mcgrofmcgrof#ifndef LINUX_3_3_COMPAT_H #define LINUX_3_3_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)) #include /* include to override NL80211_FEATURE_SK_TX_STATUS */ #include #include #include #if !((LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,9) && LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,23) && LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0))) #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)) /* mask qdisc_cb_private_validate as RHEL6 backports this */ #define qdisc_cb_private_validate(a,b) compat_qdisc_cb_private_validate(a,b) static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) { BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct qdisc_skb_cb) + sz); } #else /* mask qdisc_cb_private_validate as RHEL6 backports this */ #define qdisc_cb_private_validate(a,b) compat_qdisc_cb_private_validate(a,b) static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) { /* XXX ? */ } #endif #endif extern struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask); static inline void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) { WARN_ON(1); } #define NL80211_FEATURE_SK_TX_STATUS 0 typedef u32 netdev_features_t; /* source include/linux/device.h */ /** * module_driver() - Helper macro for drivers that don't do anything * special in module init/exit. This eliminates a lot of boilerplate. * Each module may only use this macro once, and calling it replaces * module_init() and module_exit(). * * Use this macro to construct bus specific macros for registering * drivers, and do not use it on its own. */ #define module_driver(__driver, __register, __unregister) \ static int __init __driver##_init(void) \ { \ return __register(&(__driver)); \ } \ module_init(__driver##_init); \ static void __exit __driver##_exit(void) \ { \ __unregister(&(__driver)); \ } \ module_exit(__driver##_exit); /* source include/linux/usb.h */ /** * module_usb_driver() - Helper macro for registering a USB driver * @__usb_driver: usb_driver struct * * Helper macro for USB drivers which do not do anything special in module * init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() */ #define module_usb_driver(__usb_driver) \ module_driver(__usb_driver, usb_register, \ usb_deregister) /* * PCI_EXP_TYPE_RC_EC was added via 1b6b8ce2 on v2.6.30-rc4~20 : * * mcgrof@frijol ~/linux-next (git::master)$ git describe --contains 1b6b8ce2 * v2.6.30-rc4~20^2 * * but the fix for its definition was merged on v3.3-rc1~101^2~67 * * mcgrof@frijol ~/linux-next (git::master)$ git describe --contains 1830ea91 * v3.3-rc1~101^2~67 * * while we can assume it got merged and backported on v3.2.28 (which it did * see c1c3cd9) we cannot assume every kernel has it fixed so lets just undef * it here and redefine it. */ #undef PCI_EXP_TYPE_RC_EC #define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)) */ #endif /* LINUX_3_3_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-3.7.h0000644000175000017500000000434612026200333021110 0ustar mcgrofmcgrof#ifndef LINUX_3_7_COMPAT_H #define LINUX_3_7_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) #include #include #include #include #define netlink_notify_portid(__notify) (__notify->pid) #define genl_info_snd_portid(__genl_info) (__genl_info->snd_pid) #define NETLINK_CB_PORTID(__skb) NETLINK_CB(cb->skb).pid bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay); /* Backports tty_lock: Localise the lock */ #define tty_lock(__tty) tty_lock() #define tty_unlock(__tty) tty_unlock() #define tty_port_register_device(port, driver, index, device) \ tty_register_device(driver, index, device) int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val); int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val); int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val); int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val); int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos, u16 clear, u16 set); int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos, u32 clear, u32 set); static inline int pcie_capability_set_word(struct pci_dev *dev, int pos, u16 set) { return pcie_capability_clear_and_set_word(dev, pos, 0, set); } static inline int pcie_capability_set_dword(struct pci_dev *dev, int pos, u32 set) { return pcie_capability_clear_and_set_dword(dev, pos, 0, set); } static inline int pcie_capability_clear_word(struct pci_dev *dev, int pos, u16 clear) { return pcie_capability_clear_and_set_word(dev, pos, clear, 0); } static inline int pcie_capability_clear_dword(struct pci_dev *dev, int pos, u32 clear) { return pcie_capability_clear_and_set_dword(dev, pos, clear, 0); } #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #else /* (LINUX_VERSION_CODE > KERNEL_VERSION(3,7,0)) */ #define netlink_notify_portid(__notify) (__notify->portid) #define genl_info_snd_portid(__genl_info) (__genl_info->snd_portid) #define NETLINK_CB_PORTID(__skb) NETLINK_CB(cb->skb).portid #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) */ #endif /* LINUX_3_7_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.24.h0000644000175000017500000001632512025673354021352 0ustar mcgrofmcgrof#ifndef LINUX_26_24_COMPAT_H #define LINUX_26_24_COMPAT_H #include /* Compat work for 2.6.21, 2.6.22 and 2.6.23 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) #include #include #include #include #include #include #include #define KEY_BLUETOOTH 237 #define KEY_WLAN 238 #define KEY_UWB 239 #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) struct proc_dir_entry; struct net_device; struct net { atomic_t count; /* To decided when the network * namespace should be freed. */ atomic_t use_count; /* To track references we * destroy on demand */ struct list_head list; /* list of network namespaces */ struct work_struct work; /* work struct for freeing */ struct proc_dir_entry *proc_net; struct proc_dir_entry *proc_net_stat; struct proc_dir_entry *proc_net_root; struct net_device *loopback_dev; /* The loopback */ struct list_head dev_base_head; struct hlist_head *dev_name_head; struct hlist_head *dev_index_head; }; #ifdef CONFIG_NET /* Init's network namespace */ extern struct net init_net; #define INIT_NET_NS(net_ns) .net_ns = &init_net, #else #define INIT_NET_NS(net_ns) #endif /* Added on 2.6.24 in include/linux/types.h by Al viro on commit 142956af */ typedef unsigned long uintptr_t; /* From include/linux/net.h */ enum sock_shutdown_cmd { SHUT_RD = 0, SHUT_WR = 1, SHUT_RDWR = 2, }; #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,23)) /* Local check */ /* Added as of 2.6.24 in include/linux/skbuff.h. * * Although 2.6.23 does support for CONFIG_NETDEVICES_MULTIQUEUE * this helper was not added until 2.6.24. This implementation * is exactly as it is on newer kernels. * * For older kernels we use the an internal mac80211 hack. * For details see changes to include/net/mac80211.h through * compat.diff and compat/mq_compat.h */ static inline u16 skb_get_queue_mapping(struct sk_buff *skb) { #ifdef CONFIG_NETDEVICES_MULTIQUEUE return skb->queue_mapping; #else return 0; #endif } #endif /* Local 2.6.23 check */ /* On older kernels we handle this a bit differently, so we yield to that * code for its implementation in mq_compat.h as we want to make * use of the internal mac80211 __ieee80211_queue_stopped() which itself * uses internal mac80211 data structure hacks. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) /* Local check */ /** * netif_subqueue_stopped - test status of subqueue * @dev: network device * @queue_index: sub queue index * * Check individual transmit queue of a device with multiple transmit queues. */ static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { #ifdef CONFIG_NETDEVICES_MULTIQUEUE return test_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); #else return 0; #endif } /* Note: although the backport implementation for netif_subqueue_stopped * on older kernels is identical to upstream __netif_subqueue_stopped() * (except for a const qualifier) we implement netif_subqueue_stopped() * as part of mac80211 as it relies on internal mac80211 structures we * use for MQ support. We this implement it in mq_compat.h */ #endif /* Local 2.6.23 check */ /* * Force link bug if constructor is used, can't be done compatibly * because constructor arguments were swapped since then! */ extern void __incompatible_kmem_cache_create(void); /* 2.6.21 and 2.6.22 kmem_cache_create() takes 6 arguments */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) #define kmem_cache_create(name, objsize, align, flags, ctor) \ ({ \ if (ctor) __incompatible_kmem_cache_create(); \ kmem_cache_create((name), (objsize), (align), \ (flags), NULL, NULL); \ }) #endif /* 2.6.23 kmem_cache_create() takes 5 arguments */ #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,23)) #define kmem_cache_create(name, objsize, align, flags, ctor) \ ({ \ if (ctor) __incompatible_kmem_cache_create(); \ kmem_cache_create((name), (objsize), (align), \ (flags), NULL); \ }) #endif /* From include/linux/mod_devicetable.h */ /* SSB core, see drivers/ssb/ */ #ifndef SSB_DEVICE struct ssb_device_id { __u16 vendor; __u16 coreid; __u8 revision; }; #define SSB_DEVICE(_vendor, _coreid, _revision) \ { .vendor = _vendor, .coreid = _coreid, .revision = _revision, } #define SSB_DEVTABLE_END \ { 0, }, #define SSB_ANY_VENDOR 0xFFFF #define SSB_ANY_ID 0xFFFF #define SSB_ANY_REV 0xFF #endif /* Namespace stuff, introduced on 2.6.24 */ #define dev_get_by_index(a, b) dev_get_by_index(b) #define __dev_get_by_index(a, b) __dev_get_by_index(b) extern int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); extern int eth_rebuild_header(struct sk_buff *skb); extern void eth_header_cache_update(struct hh_cache *hh, struct net_device *dev, unsigned char * haddr); extern int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh); /* This structure is simply not present on 2.6.22 and 2.6.23 */ struct header_ops { int (*create) (struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); int (*parse)(const struct sk_buff *skb, unsigned char *haddr); int (*rebuild)(struct sk_buff *skb); #define HAVE_HEADER_CACHE int (*cache)(struct neighbour *neigh, struct hh_cache *hh); void (*cache_update)(struct hh_cache *hh, struct net_device *dev, unsigned char *haddr); }; /* net/ieee80211/ieee80211_crypt_tkip uses sg_init_table. This was added on * 2.6.24. CONFIG_DEBUG_SG was added in 2.6.24 as well, so lets just ignore * the debug stuff. Note that adding this required changes to the struct * scatterlist on include/asm/scatterlist*, so the right way to port this * is to simply ignore the new structure changes and zero the scatterlist * array. We lave the kdoc intact for reference. */ /** * sg_mark_end - Mark the end of the scatterlist * @sg: SG entryScatterlist * * Description: * Marks the passed in sg entry as the termination point for the sg * table. A call to sg_next() on this entry will return NULL. * **/ static inline void sg_mark_end(struct scatterlist *sg) { #ifdef CONFIG_DEBUG_SG BUG_ON(sg->sg_magic != SG_MAGIC); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) /* * Set termination bit, clear potential chain bit */ sg->page_link |= 0x02; sg->page_link &= ~0x01; #endif } /** * sg_init_table - Initialize SG table * @sgl: The SG table * @nents: Number of entries in table * * Notes: * If this is part of a chained sg table, sg_mark_end() should be * used only on the last table part. * **/ static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) { memset(sgl, 0, sizeof(*sgl) * nents); } /** * usb_endpoint_num - get the endpoint's number * @epd: endpoint to be checked * * Returns @epd's number: 0 to 15. */ static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd) { return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) */ #endif /* LINUX_26_24_COMPAT_H */ compat-drivers-2012-09-18/include/linux/usb/0000755000175000017500000000000012026176500017741 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/linux/usb/rndis_host.h0000644000175000017500000001363312026211315022266 0ustar mcgrofmcgrof/* * Host Side support for RNDIS Networking Links * Copyright (C) 2005 by David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_RNDIS_HOST_H #define __LINUX_USB_RNDIS_HOST_H #include /* * CONTROL uses CDC "encapsulated commands" with funky notifications. * - control-out: SEND_ENCAPSULATED * - interrupt-in: RESPONSE_AVAILABLE * - control-in: GET_ENCAPSULATED * * We'll try to ignore the RESPONSE_AVAILABLE notifications. * * REVISIT some RNDIS implementations seem to have curious issues still * to be resolved. */ struct rndis_msg_hdr { __le32 msg_type; /* RNDIS_MSG_* */ __le32 msg_len; /* followed by data that varies between messages */ __le32 request_id; __le32 status; /* ... and more */ } __attribute__ ((packed)); /* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */ #define CONTROL_BUFFER_SIZE 1025 /* RNDIS defines an (absurdly huge) 10 second control timeout, * but ActiveSync seems to use a more usual 5 second timeout * (which matches the USB 2.0 spec). */ #define RNDIS_CONTROL_TIMEOUT_MS (5 * 1000) struct rndis_data_hdr { __le32 msg_type; /* RNDIS_MSG_PACKET */ __le32 msg_len; /* rndis_data_hdr + data_len + pad */ __le32 data_offset; /* 36 -- right after header */ __le32 data_len; /* ... real packet size */ __le32 oob_data_offset; /* zero */ __le32 oob_data_len; /* zero */ __le32 num_oob; /* zero */ __le32 packet_data_offset; /* zero */ __le32 packet_data_len; /* zero */ __le32 vc_handle; /* zero */ __le32 reserved; /* zero */ } __attribute__ ((packed)); struct rndis_init { /* OUT */ /* header and: */ __le32 msg_type; /* RNDIS_MSG_INIT */ __le32 msg_len; /* 24 */ __le32 request_id; __le32 major_version; /* of rndis (1.0) */ __le32 minor_version; __le32 max_transfer_size; } __attribute__ ((packed)); struct rndis_init_c { /* IN */ /* header and: */ __le32 msg_type; /* RNDIS_MSG_INIT_C */ __le32 msg_len; __le32 request_id; __le32 status; __le32 major_version; /* of rndis (1.0) */ __le32 minor_version; __le32 device_flags; __le32 medium; /* zero == 802.3 */ __le32 max_packets_per_message; __le32 max_transfer_size; __le32 packet_alignment; /* max 7; (1< * Copyright (C) 2003-2005 David Hollis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_USBNET_H #define __LINUX_USB_USBNET_H /* interface from usbnet core to each USB networking link we handle */ struct usbnet { /* housekeeping */ struct usb_device *udev; struct usb_interface *intf; struct driver_info *driver_info; const char *driver_name; void *driver_priv; wait_queue_head_t *wait; struct mutex phy_mutex; unsigned char suspend_count; /* i/o info: pipes etc */ unsigned in, out; struct usb_host_endpoint *status; unsigned maxpacket; struct timer_list delay; /* protocol/interface state */ struct net_device *net; int msg_enable; unsigned long data[5]; u32 xid; u32 hard_mtu; /* count any extra framing */ size_t rx_urb_size; /* size for rx urbs */ struct mii_if_info mii; /* various kinds of pending driver work */ struct sk_buff_head rxq; struct sk_buff_head txq; struct sk_buff_head done; struct sk_buff_head rxq_pause; struct urb *interrupt; struct usb_anchor deferred; struct tasklet_struct bh; struct work_struct kevent; unsigned long flags; # define EVENT_TX_HALT 0 # define EVENT_RX_HALT 1 # define EVENT_RX_MEMORY 2 # define EVENT_STS_SPLIT 3 # define EVENT_LINK_RESET 4 # define EVENT_RX_PAUSED 5 # define EVENT_DEV_ASLEEP 6 # define EVENT_DEV_OPEN 7 }; static inline struct usb_driver *driver_of(struct usb_interface *intf) { return to_usb_driver(intf->dev.driver); } /* interface from the device/framing level "minidriver" to core */ struct driver_info { char *description; int flags; /* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */ #define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */ #define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */ #define FLAG_FRAMING_Z 0x0004 /* zaurus adds a trailer */ #define FLAG_FRAMING_RN 0x0008 /* RNDIS batches, plus huge header */ #define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */ #define FLAG_ETHER 0x0020 /* maybe use "eth%d" names */ #define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */ #define FLAG_WLAN 0x0080 /* use "wlan%d" names */ #define FLAG_AVOID_UNLINK_URBS 0x0100 /* don't unlink urbs at usbnet_stop() */ #define FLAG_SEND_ZLP 0x0200 /* hw requires ZLPs are sent */ #define FLAG_WWAN 0x0400 /* use "wwan%d" names */ #define FLAG_LINK_INTR 0x0800 /* updates link (carrier) status */ #define FLAG_POINTTOPOINT 0x1000 /* possibly use "usb%d" names */ /* * Indicates to usbnet, that USB driver accumulates multiple IP packets. * Affects statistic (counters) and short packet handling. */ #define FLAG_MULTI_PACKET 0x2000 #define FLAG_RX_ASSEMBLE 0x4000 /* rx packets may span >1 frames */ /* init device ... can sleep, or cause probe() failure */ int (*bind)(struct usbnet *, struct usb_interface *); /* cleanup device ... can sleep, but can't fail */ void (*unbind)(struct usbnet *, struct usb_interface *); /* reset device ... can sleep */ int (*reset)(struct usbnet *); /* stop device ... can sleep */ int (*stop)(struct usbnet *); /* see if peer is connected ... can sleep */ int (*check_connect)(struct usbnet *); /* (dis)activate runtime power management */ int (*manage_power)(struct usbnet *, int); /* for status polling */ void (*status)(struct usbnet *, struct urb *); /* link reset handling, called from defer_kevent */ int (*link_reset)(struct usbnet *); /* fixup rx packet (strip framing) */ int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb); /* fixup tx packet (add framing) */ struct sk_buff *(*tx_fixup)(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); /* early initialization code, can sleep. This is for minidrivers * having 'subminidrivers' that need to do extra initialization * right after minidriver have initialized hardware. */ int (*early_init)(struct usbnet *dev); /* called by minidriver when receiving indication */ void (*indication)(struct usbnet *dev, void *ind, int indlen); /* for new devices, use the descriptor-reading code instead */ int in; /* rx endpoint */ int out; /* tx endpoint */ unsigned long data; /* Misc driver specific data */ }; /* Minidrivers are just drivers using the "usbnet" core as a powerful * network-specific subroutine library ... that happens to do pretty * much everything except custom framing and chip-specific stuff. */ extern int usbnet_probe(struct usb_interface *, const struct usb_device_id *); extern int usbnet_suspend(struct usb_interface *, pm_message_t); extern int usbnet_resume(struct usb_interface *); extern void usbnet_disconnect(struct usb_interface *); /* Drivers that reuse some of the standard USB CDC infrastructure * (notably, using multiple interfaces according to the CDC * union descriptor) get some helper code. */ struct cdc_state { struct usb_cdc_header_desc *header; struct usb_cdc_union_desc *u; struct usb_cdc_ether_desc *ether; struct usb_interface *control; struct usb_interface *data; }; extern int usbnet_generic_cdc_bind(struct usbnet *, struct usb_interface *); extern int usbnet_cdc_bind(struct usbnet *, struct usb_interface *); extern void usbnet_cdc_unbind(struct usbnet *, struct usb_interface *); extern void usbnet_cdc_status(struct usbnet *, struct urb *); /* CDC and RNDIS support the same host-chosen packet filters for IN transfers */ #define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ |USB_CDC_PACKET_TYPE_PROMISCUOUS \ |USB_CDC_PACKET_TYPE_DIRECTED) /* we record the state for each of our queued skbs */ enum skb_state { illegal = 0, tx_start, tx_done, rx_start, rx_done, rx_cleanup, unlink_start }; struct skb_data { /* skb->cb is one of these */ struct urb *urb; struct usbnet *dev; enum skb_state state; size_t length; }; extern int usbnet_open(struct net_device *net); extern int usbnet_stop(struct net_device *net); extern netdev_tx_t usbnet_start_xmit(struct sk_buff *skb, struct net_device *net); extern void usbnet_tx_timeout(struct net_device *net); extern int usbnet_change_mtu(struct net_device *net, int new_mtu); extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *); extern int usbnet_get_ethernet_addr(struct usbnet *, int); extern void usbnet_defer_kevent(struct usbnet *, int); extern void usbnet_skb_return(struct usbnet *, struct sk_buff *); extern void usbnet_unlink_rx_urbs(struct usbnet *); extern void usbnet_pause_rx(struct usbnet *); extern void usbnet_resume_rx(struct usbnet *); extern void usbnet_purge_paused_rxq(struct usbnet *); extern int usbnet_get_settings(struct net_device *net, struct ethtool_cmd *cmd); extern int usbnet_set_settings(struct net_device *net, struct ethtool_cmd *cmd); extern u32 usbnet_get_link(struct net_device *net); extern u32 usbnet_get_msglevel(struct net_device *); extern void usbnet_set_msglevel(struct net_device *, u32); extern void usbnet_get_drvinfo(struct net_device *, struct ethtool_drvinfo *); extern int usbnet_nway_reset(struct net_device *net); #endif /* __LINUX_USB_USBNET_H */ compat-drivers-2012-09-18/include/linux/ssb/0000755000175000017500000000000012015204042017726 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/linux/ssb/ssb_regs.h0000644000175000017500000007514112006264415021730 0ustar mcgrofmcgrof#ifndef LINUX_SSB_REGS_H_ #define LINUX_SSB_REGS_H_ /* SiliconBackplane Address Map. * All regions may not exist on all chips. */ #define SSB_SDRAM_BASE 0x00000000U /* Physical SDRAM */ #define SSB_PCI_MEM 0x08000000U /* Host Mode sb2pcitranslation0 (64 MB) */ #define SSB_PCI_CFG 0x0c000000U /* Host Mode sb2pcitranslation1 (64 MB) */ #define SSB_SDRAM_SWAPPED 0x10000000U /* Byteswapped Physical SDRAM */ #define SSB_ENUM_BASE 0x18000000U /* Enumeration space base */ #define SSB_ENUM_LIMIT 0x18010000U /* Enumeration space limit */ #define SSB_FLASH2 0x1c000000U /* Flash Region 2 (region 1 shadowed here) */ #define SSB_FLASH2_SZ 0x02000000U /* Size of Flash Region 2 */ #define SSB_EXTIF_BASE 0x1f000000U /* External Interface region base address */ #define SSB_FLASH1 0x1fc00000U /* Flash Region 1 */ #define SSB_FLASH1_SZ 0x00400000U /* Size of Flash Region 1 */ #define SSB_PCI_DMA 0x40000000U /* Client Mode sb2pcitranslation2 (1 GB) */ #define SSB_PCI_DMA_SZ 0x40000000U /* Client Mode sb2pcitranslation2 size in bytes */ #define SSB_PCIE_DMA_L32 0x00000000U /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), low 32 bits */ #define SSB_PCIE_DMA_H32 0x80000000U /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */ #define SSB_EUART (SSB_EXTIF_BASE + 0x00800000) #define SSB_LED (SSB_EXTIF_BASE + 0x00900000) /* Enumeration space constants */ #define SSB_CORE_SIZE 0x1000 /* Size of a core MMIO area */ #define SSB_MAX_NR_CORES ((SSB_ENUM_LIMIT - SSB_ENUM_BASE) / SSB_CORE_SIZE) /* mips address */ #define SSB_EJTAG 0xff200000 /* MIPS EJTAG space (2M) */ /* SSB PCI config space registers. */ #define SSB_PMCSR 0x44 #define SSB_PE 0x100 #define SSB_BAR0_WIN 0x80 /* Backplane address space 0 */ #define SSB_BAR1_WIN 0x84 /* Backplane address space 1 */ #define SSB_SPROMCTL 0x88 /* SPROM control */ #define SSB_SPROMCTL_WE 0x10 /* SPROM write enable */ #define SSB_BAR1_CONTROL 0x8c /* Address space 1 burst control */ #define SSB_PCI_IRQS 0x90 /* PCI interrupts */ #define SSB_PCI_IRQMASK 0x94 /* PCI IRQ control and mask (pcirev >= 6 only) */ #define SSB_BACKPLANE_IRQS 0x98 /* Backplane Interrupts */ #define SSB_GPIO_IN 0xB0 /* GPIO Input (pcirev >= 3 only) */ #define SSB_GPIO_OUT 0xB4 /* GPIO Output (pcirev >= 3 only) */ #define SSB_GPIO_OUT_ENABLE 0xB8 /* GPIO Output Enable/Disable (pcirev >= 3 only) */ #define SSB_GPIO_SCS 0x10 /* PCI config space bit 4 for 4306c0 slow clock source */ #define SSB_GPIO_HWRAD 0x20 /* PCI config space GPIO 13 for hw radio disable */ #define SSB_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */ #define SSB_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */ #define SSB_BAR0_MAX_RETRIES 50 /* Silicon backplane configuration register definitions */ #define SSB_IPSFLAG 0x0F08 #define SSB_IPSFLAG_IRQ1 0x0000003F /* which sbflags get routed to mips interrupt 1 */ #define SSB_IPSFLAG_IRQ1_SHIFT 0 #define SSB_IPSFLAG_IRQ2 0x00003F00 /* which sbflags get routed to mips interrupt 2 */ #define SSB_IPSFLAG_IRQ2_SHIFT 8 #define SSB_IPSFLAG_IRQ3 0x003F0000 /* which sbflags get routed to mips interrupt 3 */ #define SSB_IPSFLAG_IRQ3_SHIFT 16 #define SSB_IPSFLAG_IRQ4 0x3F000000 /* which sbflags get routed to mips interrupt 4 */ #define SSB_IPSFLAG_IRQ4_SHIFT 24 #define SSB_TPSFLAG 0x0F18 #define SSB_TPSFLAG_BPFLAG 0x0000003F /* Backplane flag # */ #define SSB_TPSFLAG_ALWAYSIRQ 0x00000040 /* IRQ is always sent on the Backplane */ #define SSB_TMERRLOGA 0x0F48 #define SSB_TMERRLOG 0x0F50 #define SSB_ADMATCH3 0x0F60 #define SSB_ADMATCH2 0x0F68 #define SSB_ADMATCH1 0x0F70 #define SSB_IMSTATE 0x0F90 /* SB Initiator Agent State */ #define SSB_IMSTATE_PC 0x0000000f /* Pipe Count */ #define SSB_IMSTATE_AP_MASK 0x00000030 /* Arbitration Priority */ #define SSB_IMSTATE_AP_BOTH 0x00000000 /* Use both timeslices and token */ #define SSB_IMSTATE_AP_TS 0x00000010 /* Use timeslices only */ #define SSB_IMSTATE_AP_TK 0x00000020 /* Use token only */ #define SSB_IMSTATE_AP_RSV 0x00000030 /* Reserved */ #define SSB_IMSTATE_IBE 0x00020000 /* In Band Error */ #define SSB_IMSTATE_TO 0x00040000 /* Timeout */ #define SSB_IMSTATE_BUSY 0x01800000 /* Busy (Backplane rev >= 2.3 only) */ #define SSB_IMSTATE_REJECT 0x02000000 /* Reject (Backplane rev >= 2.3 only) */ #define SSB_INTVEC 0x0F94 /* SB Interrupt Mask */ #define SSB_INTVEC_PCI 0x00000001 /* Enable interrupts for PCI */ #define SSB_INTVEC_ENET0 0x00000002 /* Enable interrupts for enet 0 */ #define SSB_INTVEC_ILINE20 0x00000004 /* Enable interrupts for iline20 */ #define SSB_INTVEC_CODEC 0x00000008 /* Enable interrupts for v90 codec */ #define SSB_INTVEC_USB 0x00000010 /* Enable interrupts for usb */ #define SSB_INTVEC_EXTIF 0x00000020 /* Enable interrupts for external i/f */ #define SSB_INTVEC_ENET1 0x00000040 /* Enable interrupts for enet 1 */ #define SSB_TMSLOW 0x0F98 /* SB Target State Low */ #define SSB_TMSLOW_RESET 0x00000001 /* Reset */ #define SSB_TMSLOW_REJECT 0x00000002 /* Reject (Standard Backplane) */ #define SSB_TMSLOW_REJECT_23 0x00000004 /* Reject (Backplane rev 2.3) */ #define SSB_TMSLOW_CLOCK 0x00010000 /* Clock Enable */ #define SSB_TMSLOW_FGC 0x00020000 /* Force Gated Clocks On */ #define SSB_TMSLOW_PE 0x40000000 /* Power Management Enable */ #define SSB_TMSLOW_BE 0x80000000 /* BIST Enable */ #define SSB_TMSHIGH 0x0F9C /* SB Target State High */ #define SSB_TMSHIGH_SERR 0x00000001 /* S-error */ #define SSB_TMSHIGH_INT 0x00000002 /* Interrupt */ #define SSB_TMSHIGH_BUSY 0x00000004 /* Busy */ #define SSB_TMSHIGH_TO 0x00000020 /* Timeout. Backplane rev >= 2.3 only */ #define SSB_TMSHIGH_COREFL 0x1FFF0000 /* Core specific flags */ #define SSB_TMSHIGH_COREFL_SHIFT 16 #define SSB_TMSHIGH_DMA64 0x10000000 /* 64bit DMA supported */ #define SSB_TMSHIGH_GCR 0x20000000 /* Gated Clock Request */ #define SSB_TMSHIGH_BISTF 0x40000000 /* BIST Failed */ #define SSB_TMSHIGH_BISTD 0x80000000 /* BIST Done */ #define SSB_BWA0 0x0FA0 #define SSB_IMCFGLO 0x0FA8 #define SSB_IMCFGLO_SERTO 0x00000007 /* Service timeout */ #define SSB_IMCFGLO_REQTO 0x00000070 /* Request timeout */ #define SSB_IMCFGLO_REQTO_SHIFT 4 #define SSB_IMCFGLO_CONNID 0x00FF0000 /* Connection ID */ #define SSB_IMCFGLO_CONNID_SHIFT 16 #define SSB_IMCFGHI 0x0FAC #define SSB_ADMATCH0 0x0FB0 #define SSB_TMCFGLO 0x0FB8 #define SSB_TMCFGHI 0x0FBC #define SSB_BCONFIG 0x0FC0 #define SSB_BSTATE 0x0FC8 #define SSB_ACTCFG 0x0FD8 #define SSB_FLAGST 0x0FE8 #define SSB_IDLOW 0x0FF8 #define SSB_IDLOW_CFGSP 0x00000003 /* Config Space */ #define SSB_IDLOW_ADDRNGE 0x00000038 /* Address Ranges supported */ #define SSB_IDLOW_ADDRNGE_SHIFT 3 #define SSB_IDLOW_SYNC 0x00000040 #define SSB_IDLOW_INITIATOR 0x00000080 #define SSB_IDLOW_MIBL 0x00000F00 /* Minimum Backplane latency */ #define SSB_IDLOW_MIBL_SHIFT 8 #define SSB_IDLOW_MABL 0x0000F000 /* Maximum Backplane latency */ #define SSB_IDLOW_MABL_SHIFT 12 #define SSB_IDLOW_TIF 0x00010000 /* This Initiator is first */ #define SSB_IDLOW_CCW 0x000C0000 /* Cycle counter width */ #define SSB_IDLOW_CCW_SHIFT 18 #define SSB_IDLOW_TPT 0x00F00000 /* Target ports */ #define SSB_IDLOW_TPT_SHIFT 20 #define SSB_IDLOW_INITP 0x0F000000 /* Initiator ports */ #define SSB_IDLOW_INITP_SHIFT 24 #define SSB_IDLOW_SSBREV 0xF0000000 /* Sonics Backplane Revision code */ #define SSB_IDLOW_SSBREV_22 0x00000000 /* <= 2.2 */ #define SSB_IDLOW_SSBREV_23 0x10000000 /* 2.3 */ #define SSB_IDLOW_SSBREV_24 0x40000000 /* ?? Found in BCM4328 */ #define SSB_IDLOW_SSBREV_25 0x50000000 /* ?? Not Found yet */ #define SSB_IDLOW_SSBREV_26 0x60000000 /* ?? Found in some BCM4311/2 */ #define SSB_IDLOW_SSBREV_27 0x70000000 /* ?? Found in some BCM4311/2 */ #define SSB_IDHIGH 0x0FFC /* SB Identification High */ #define SSB_IDHIGH_RCLO 0x0000000F /* Revision Code (low part) */ #define SSB_IDHIGH_CC 0x00008FF0 /* Core Code */ #define SSB_IDHIGH_CC_SHIFT 4 #define SSB_IDHIGH_RCHI 0x00007000 /* Revision Code (high part) */ #define SSB_IDHIGH_RCHI_SHIFT 8 /* yes, shift 8 is right */ #define SSB_IDHIGH_VC 0xFFFF0000 /* Vendor Code */ #define SSB_IDHIGH_VC_SHIFT 16 /* SPROM shadow area. If not otherwise noted, fields are * two bytes wide. Note that the SPROM can _only_ be read * in two-byte quantities. */ #define SSB_SPROMSIZE_WORDS 64 #define SSB_SPROMSIZE_BYTES (SSB_SPROMSIZE_WORDS * sizeof(u16)) #define SSB_SPROMSIZE_WORDS_R123 64 #define SSB_SPROMSIZE_WORDS_R4 220 #define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16)) #define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16)) #define SSB_SPROM_BASE1 0x1000 #define SSB_SPROM_BASE31 0x0800 #define SSB_SPROM_REVISION 0x007E #define SSB_SPROM_REVISION_REV 0x00FF /* SPROM Revision number */ #define SSB_SPROM_REVISION_CRC 0xFF00 /* SPROM CRC8 value */ #define SSB_SPROM_REVISION_CRC_SHIFT 8 /* SPROM Revision 1 */ #define SSB_SPROM1_SPID 0x0004 /* Subsystem Product ID for PCI */ #define SSB_SPROM1_SVID 0x0006 /* Subsystem Vendor ID for PCI */ #define SSB_SPROM1_PID 0x0008 /* Product ID for PCI */ #define SSB_SPROM1_IL0MAC 0x0048 /* 6 bytes MAC address for 802.11b/g */ #define SSB_SPROM1_ET0MAC 0x004E /* 6 bytes MAC address for Ethernet */ #define SSB_SPROM1_ET1MAC 0x0054 /* 6 bytes MAC address for 802.11a */ #define SSB_SPROM1_ETHPHY 0x005A /* Ethernet PHY settings */ #define SSB_SPROM1_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ #define SSB_SPROM1_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ #define SSB_SPROM1_ETHPHY_ET1A_SHIFT 5 #define SSB_SPROM1_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ #define SSB_SPROM1_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ #define SSB_SPROM1_BINF 0x005C /* Board info */ #define SSB_SPROM1_BINF_BREV 0x00FF /* Board Revision */ #define SSB_SPROM1_BINF_CCODE 0x0F00 /* Country Code */ #define SSB_SPROM1_BINF_CCODE_SHIFT 8 #define SSB_SPROM1_BINF_ANTBG 0x3000 /* Available B-PHY and G-PHY antennas */ #define SSB_SPROM1_BINF_ANTBG_SHIFT 12 #define SSB_SPROM1_BINF_ANTA 0xC000 /* Available A-PHY antennas */ #define SSB_SPROM1_BINF_ANTA_SHIFT 14 #define SSB_SPROM1_PA0B0 0x005E #define SSB_SPROM1_PA0B1 0x0060 #define SSB_SPROM1_PA0B2 0x0062 #define SSB_SPROM1_GPIOA 0x0064 /* General Purpose IO pins 0 and 1 */ #define SSB_SPROM1_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM1_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM1_GPIOA_P1_SHIFT 8 #define SSB_SPROM1_GPIOB 0x0066 /* General Purpuse IO pins 2 and 3 */ #define SSB_SPROM1_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM1_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM1_GPIOB_P3_SHIFT 8 #define SSB_SPROM1_MAXPWR 0x0068 /* Power Amplifier Max Power */ #define SSB_SPROM1_MAXPWR_BG 0x00FF /* B-PHY and G-PHY (in dBm Q5.2) */ #define SSB_SPROM1_MAXPWR_A 0xFF00 /* A-PHY (in dBm Q5.2) */ #define SSB_SPROM1_MAXPWR_A_SHIFT 8 #define SSB_SPROM1_PA1B0 0x006A #define SSB_SPROM1_PA1B1 0x006C #define SSB_SPROM1_PA1B2 0x006E #define SSB_SPROM1_ITSSI 0x0070 /* Idle TSSI Target */ #define SSB_SPROM1_ITSSI_BG 0x00FF /* B-PHY and G-PHY*/ #define SSB_SPROM1_ITSSI_A 0xFF00 /* A-PHY */ #define SSB_SPROM1_ITSSI_A_SHIFT 8 #define SSB_SPROM1_BFLLO 0x0072 /* Boardflags (low 16 bits) */ #define SSB_SPROM1_AGAIN 0x0074 /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM1_AGAIN_BG 0x00FF /* B-PHY and G-PHY */ #define SSB_SPROM1_AGAIN_BG_SHIFT 0 #define SSB_SPROM1_AGAIN_A 0xFF00 /* A-PHY */ #define SSB_SPROM1_AGAIN_A_SHIFT 8 #define SSB_SPROM1_CCODE 0x0076 /* SPROM Revision 2 (inherits from rev 1) */ #define SSB_SPROM2_BFLHI 0x0038 /* Boardflags (high 16 bits) */ #define SSB_SPROM2_MAXP_A 0x003A /* A-PHY Max Power */ #define SSB_SPROM2_MAXP_A_HI 0x00FF /* Max Power High */ #define SSB_SPROM2_MAXP_A_LO 0xFF00 /* Max Power Low */ #define SSB_SPROM2_MAXP_A_LO_SHIFT 8 #define SSB_SPROM2_PA1LOB0 0x003C /* A-PHY PowerAmplifier Low Settings */ #define SSB_SPROM2_PA1LOB1 0x003E /* A-PHY PowerAmplifier Low Settings */ #define SSB_SPROM2_PA1LOB2 0x0040 /* A-PHY PowerAmplifier Low Settings */ #define SSB_SPROM2_PA1HIB0 0x0042 /* A-PHY PowerAmplifier High Settings */ #define SSB_SPROM2_PA1HIB1 0x0044 /* A-PHY PowerAmplifier High Settings */ #define SSB_SPROM2_PA1HIB2 0x0046 /* A-PHY PowerAmplifier High Settings */ #define SSB_SPROM2_OPO 0x0078 /* OFDM Power Offset from CCK Level */ #define SSB_SPROM2_OPO_VALUE 0x00FF #define SSB_SPROM2_OPO_UNUSED 0xFF00 #define SSB_SPROM2_CCODE 0x007C /* Two char Country Code */ /* SPROM Revision 3 (inherits most data from rev 2) */ #define SSB_SPROM3_OFDMAPO 0x002C /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */ #define SSB_SPROM3_OFDMALPO 0x0030 /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */ #define SSB_SPROM3_OFDMAHPO 0x0034 /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */ #define SSB_SPROM3_GPIOLDC 0x0042 /* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */ #define SSB_SPROM3_GPIOLDC_OFF 0x0000FF00 /* Off Count */ #define SSB_SPROM3_GPIOLDC_OFF_SHIFT 8 #define SSB_SPROM3_GPIOLDC_ON 0x00FF0000 /* On Count */ #define SSB_SPROM3_GPIOLDC_ON_SHIFT 16 #define SSB_SPROM3_IL0MAC 0x004A /* 6 bytes MAC address for 802.11b/g */ #define SSB_SPROM3_CCKPO 0x0078 /* CCK Power Offset */ #define SSB_SPROM3_CCKPO_1M 0x000F /* 1M Rate PO */ #define SSB_SPROM3_CCKPO_2M 0x00F0 /* 2M Rate PO */ #define SSB_SPROM3_CCKPO_2M_SHIFT 4 #define SSB_SPROM3_CCKPO_55M 0x0F00 /* 5.5M Rate PO */ #define SSB_SPROM3_CCKPO_55M_SHIFT 8 #define SSB_SPROM3_CCKPO_11M 0xF000 /* 11M Rate PO */ #define SSB_SPROM3_CCKPO_11M_SHIFT 12 #define SSB_SPROM3_OFDMGPO 0x107A /* G-PHY OFDM Power Offset (4 bytes, BigEndian) */ /* SPROM Revision 4 */ #define SSB_SPROM4_BOARDREV 0x0042 /* Board revision */ #define SSB_SPROM4_BFLLO 0x0044 /* Boardflags (low 16 bits) */ #define SSB_SPROM4_BFLHI 0x0046 /* Board Flags Hi */ #define SSB_SPROM4_BFL2LO 0x0048 /* Board flags 2 (low 16 bits) */ #define SSB_SPROM4_BFL2HI 0x004A /* Board flags 2 Hi */ #define SSB_SPROM4_IL0MAC 0x004C /* 6 byte MAC address for a/b/g/n */ #define SSB_SPROM4_CCODE 0x0052 /* Country Code (2 bytes) */ #define SSB_SPROM4_GPIOA 0x0056 /* Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM4_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM4_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM4_GPIOA_P1_SHIFT 8 #define SSB_SPROM4_GPIOB 0x0058 /* Gen. Purpose IO # 2 and 3 */ #define SSB_SPROM4_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM4_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM4_GPIOB_P3_SHIFT 8 #define SSB_SPROM4_ETHPHY 0x005A /* Ethernet PHY settings ?? */ #define SSB_SPROM4_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ #define SSB_SPROM4_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ #define SSB_SPROM4_ETHPHY_ET1A_SHIFT 5 #define SSB_SPROM4_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ #define SSB_SPROM4_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ #define SSB_SPROM4_ANTAVAIL 0x005D /* Antenna available bitfields */ #define SSB_SPROM4_ANTAVAIL_A 0x00FF /* A-PHY bitfield */ #define SSB_SPROM4_ANTAVAIL_A_SHIFT 0 #define SSB_SPROM4_ANTAVAIL_BG 0xFF00 /* B-PHY and G-PHY bitfield */ #define SSB_SPROM4_ANTAVAIL_BG_SHIFT 8 #define SSB_SPROM4_AGAIN01 0x005E /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM4_AGAIN0 0x00FF /* Antenna 0 */ #define SSB_SPROM4_AGAIN0_SHIFT 0 #define SSB_SPROM4_AGAIN1 0xFF00 /* Antenna 1 */ #define SSB_SPROM4_AGAIN1_SHIFT 8 #define SSB_SPROM4_AGAIN23 0x0060 #define SSB_SPROM4_AGAIN2 0x00FF /* Antenna 2 */ #define SSB_SPROM4_AGAIN2_SHIFT 0 #define SSB_SPROM4_AGAIN3 0xFF00 /* Antenna 3 */ #define SSB_SPROM4_AGAIN3_SHIFT 8 #define SSB_SPROM4_TXPID2G01 0x0062 /* TX Power Index 2GHz */ #define SSB_SPROM4_TXPID2G0 0x00FF #define SSB_SPROM4_TXPID2G0_SHIFT 0 #define SSB_SPROM4_TXPID2G1 0xFF00 #define SSB_SPROM4_TXPID2G1_SHIFT 8 #define SSB_SPROM4_TXPID2G23 0x0064 /* TX Power Index 2GHz */ #define SSB_SPROM4_TXPID2G2 0x00FF #define SSB_SPROM4_TXPID2G2_SHIFT 0 #define SSB_SPROM4_TXPID2G3 0xFF00 #define SSB_SPROM4_TXPID2G3_SHIFT 8 #define SSB_SPROM4_TXPID5G01 0x0066 /* TX Power Index 5GHz middle subband */ #define SSB_SPROM4_TXPID5G0 0x00FF #define SSB_SPROM4_TXPID5G0_SHIFT 0 #define SSB_SPROM4_TXPID5G1 0xFF00 #define SSB_SPROM4_TXPID5G1_SHIFT 8 #define SSB_SPROM4_TXPID5G23 0x0068 /* TX Power Index 5GHz middle subband */ #define SSB_SPROM4_TXPID5G2 0x00FF #define SSB_SPROM4_TXPID5G2_SHIFT 0 #define SSB_SPROM4_TXPID5G3 0xFF00 #define SSB_SPROM4_TXPID5G3_SHIFT 8 #define SSB_SPROM4_TXPID5GL01 0x006A /* TX Power Index 5GHz low subband */ #define SSB_SPROM4_TXPID5GL0 0x00FF #define SSB_SPROM4_TXPID5GL0_SHIFT 0 #define SSB_SPROM4_TXPID5GL1 0xFF00 #define SSB_SPROM4_TXPID5GL1_SHIFT 8 #define SSB_SPROM4_TXPID5GL23 0x006C /* TX Power Index 5GHz low subband */ #define SSB_SPROM4_TXPID5GL2 0x00FF #define SSB_SPROM4_TXPID5GL2_SHIFT 0 #define SSB_SPROM4_TXPID5GL3 0xFF00 #define SSB_SPROM4_TXPID5GL3_SHIFT 8 #define SSB_SPROM4_TXPID5GH01 0x006E /* TX Power Index 5GHz high subband */ #define SSB_SPROM4_TXPID5GH0 0x00FF #define SSB_SPROM4_TXPID5GH0_SHIFT 0 #define SSB_SPROM4_TXPID5GH1 0xFF00 #define SSB_SPROM4_TXPID5GH1_SHIFT 8 #define SSB_SPROM4_TXPID5GH23 0x0070 /* TX Power Index 5GHz high subband */ #define SSB_SPROM4_TXPID5GH2 0x00FF #define SSB_SPROM4_TXPID5GH2_SHIFT 0 #define SSB_SPROM4_TXPID5GH3 0xFF00 #define SSB_SPROM4_TXPID5GH3_SHIFT 8 #define SSB_SPROM4_MAXP_BG 0x0080 /* Max Power BG in path 1 */ #define SSB_SPROM4_MAXP_BG_MASK 0x00FF /* Mask for Max Power BG */ #define SSB_SPROM4_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ #define SSB_SPROM4_ITSSI_BG_SHIFT 8 #define SSB_SPROM4_MAXP_A 0x008A /* Max Power A in path 1 */ #define SSB_SPROM4_MAXP_A_MASK 0x00FF /* Mask for Max Power A */ #define SSB_SPROM4_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ #define SSB_SPROM4_ITSSI_A_SHIFT 8 #define SSB_SPROM4_PA0B0 0x0082 /* The paXbY locations are */ #define SSB_SPROM4_PA0B1 0x0084 /* only guesses */ #define SSB_SPROM4_PA0B2 0x0086 #define SSB_SPROM4_PA1B0 0x008E #define SSB_SPROM4_PA1B1 0x0090 #define SSB_SPROM4_PA1B2 0x0092 /* SPROM Revision 5 (inherits most data from rev 4) */ #define SSB_SPROM5_CCODE 0x0044 /* Country Code (2 bytes) */ #define SSB_SPROM5_BFLLO 0x004A /* Boardflags (low 16 bits) */ #define SSB_SPROM5_BFLHI 0x004C /* Board Flags Hi */ #define SSB_SPROM5_BFL2LO 0x004E /* Board flags 2 (low 16 bits) */ #define SSB_SPROM5_BFL2HI 0x0050 /* Board flags 2 Hi */ #define SSB_SPROM5_IL0MAC 0x0052 /* 6 byte MAC address for a/b/g/n */ #define SSB_SPROM5_GPIOA 0x0076 /* Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM5_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM5_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM5_GPIOA_P1_SHIFT 8 #define SSB_SPROM5_GPIOB 0x0078 /* Gen. Purpose IO # 2 and 3 */ #define SSB_SPROM5_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM5_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM5_GPIOB_P3_SHIFT 8 /* SPROM Revision 8 */ #define SSB_SPROM8_BOARDREV 0x0082 /* Board revision */ #define SSB_SPROM8_BFLLO 0x0084 /* Board flags (bits 0-15) */ #define SSB_SPROM8_BFLHI 0x0086 /* Board flags (bits 16-31) */ #define SSB_SPROM8_BFL2LO 0x0088 /* Board flags (bits 32-47) */ #define SSB_SPROM8_BFL2HI 0x008A /* Board flags (bits 48-63) */ #define SSB_SPROM8_IL0MAC 0x008C /* 6 byte MAC address */ #define SSB_SPROM8_CCODE 0x0092 /* 2 byte country code */ #define SSB_SPROM8_GPIOA 0x0096 /*Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM8_GPIOA_P0 0x00FF /* Pin 0 */ #define SSB_SPROM8_GPIOA_P1 0xFF00 /* Pin 1 */ #define SSB_SPROM8_GPIOA_P1_SHIFT 8 #define SSB_SPROM8_GPIOB 0x0098 /* Gen. Purpose IO # 2 and 3 */ #define SSB_SPROM8_GPIOB_P2 0x00FF /* Pin 2 */ #define SSB_SPROM8_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM8_GPIOB_P3_SHIFT 8 #define SSB_SPROM8_LEDDC 0x009A #define SSB_SPROM8_LEDDC_ON 0xFF00 /* oncount */ #define SSB_SPROM8_LEDDC_ON_SHIFT 8 #define SSB_SPROM8_LEDDC_OFF 0x00FF /* offcount */ #define SSB_SPROM8_LEDDC_OFF_SHIFT 0 #define SSB_SPROM8_ANTAVAIL 0x009C /* Antenna available bitfields*/ #define SSB_SPROM8_ANTAVAIL_A 0xFF00 /* A-PHY bitfield */ #define SSB_SPROM8_ANTAVAIL_A_SHIFT 8 #define SSB_SPROM8_ANTAVAIL_BG 0x00FF /* B-PHY and G-PHY bitfield */ #define SSB_SPROM8_ANTAVAIL_BG_SHIFT 0 #define SSB_SPROM8_AGAIN01 0x009E /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM8_AGAIN0 0x00FF /* Antenna 0 */ #define SSB_SPROM8_AGAIN0_SHIFT 0 #define SSB_SPROM8_AGAIN1 0xFF00 /* Antenna 1 */ #define SSB_SPROM8_AGAIN1_SHIFT 8 #define SSB_SPROM8_AGAIN23 0x00A0 #define SSB_SPROM8_AGAIN2 0x00FF /* Antenna 2 */ #define SSB_SPROM8_AGAIN2_SHIFT 0 #define SSB_SPROM8_AGAIN3 0xFF00 /* Antenna 3 */ #define SSB_SPROM8_AGAIN3_SHIFT 8 #define SSB_SPROM8_TXRXC 0x00A2 #define SSB_SPROM8_TXRXC_TXCHAIN 0x000f #define SSB_SPROM8_TXRXC_TXCHAIN_SHIFT 0 #define SSB_SPROM8_TXRXC_RXCHAIN 0x00f0 #define SSB_SPROM8_TXRXC_RXCHAIN_SHIFT 4 #define SSB_SPROM8_TXRXC_SWITCH 0xff00 #define SSB_SPROM8_TXRXC_SWITCH_SHIFT 8 #define SSB_SPROM8_RSSIPARM2G 0x00A4 /* RSSI params for 2GHz */ #define SSB_SPROM8_RSSISMF2G 0x000F #define SSB_SPROM8_RSSISMC2G 0x00F0 #define SSB_SPROM8_RSSISMC2G_SHIFT 4 #define SSB_SPROM8_RSSISAV2G 0x0700 #define SSB_SPROM8_RSSISAV2G_SHIFT 8 #define SSB_SPROM8_BXA2G 0x1800 #define SSB_SPROM8_BXA2G_SHIFT 11 #define SSB_SPROM8_RSSIPARM5G 0x00A6 /* RSSI params for 5GHz */ #define SSB_SPROM8_RSSISMF5G 0x000F #define SSB_SPROM8_RSSISMC5G 0x00F0 #define SSB_SPROM8_RSSISMC5G_SHIFT 4 #define SSB_SPROM8_RSSISAV5G 0x0700 #define SSB_SPROM8_RSSISAV5G_SHIFT 8 #define SSB_SPROM8_BXA5G 0x1800 #define SSB_SPROM8_BXA5G_SHIFT 11 #define SSB_SPROM8_TRI25G 0x00A8 /* TX isolation 2.4&5.3GHz */ #define SSB_SPROM8_TRI2G 0x00FF /* TX isolation 2.4GHz */ #define SSB_SPROM8_TRI5G 0xFF00 /* TX isolation 5.3GHz */ #define SSB_SPROM8_TRI5G_SHIFT 8 #define SSB_SPROM8_TRI5GHL 0x00AA /* TX isolation 5.2/5.8GHz */ #define SSB_SPROM8_TRI5GL 0x00FF /* TX isolation 5.2GHz */ #define SSB_SPROM8_TRI5GH 0xFF00 /* TX isolation 5.8GHz */ #define SSB_SPROM8_TRI5GH_SHIFT 8 #define SSB_SPROM8_RXPO 0x00AC /* RX power offsets */ #define SSB_SPROM8_RXPO2G 0x00FF /* 2GHz RX power offset */ #define SSB_SPROM8_RXPO2G_SHIFT 0 #define SSB_SPROM8_RXPO5G 0xFF00 /* 5GHz RX power offset */ #define SSB_SPROM8_RXPO5G_SHIFT 8 #define SSB_SPROM8_FEM2G 0x00AE #define SSB_SPROM8_FEM5G 0x00B0 #define SSB_SROM8_FEM_TSSIPOS 0x0001 #define SSB_SROM8_FEM_TSSIPOS_SHIFT 0 #define SSB_SROM8_FEM_EXTPA_GAIN 0x0006 #define SSB_SROM8_FEM_EXTPA_GAIN_SHIFT 1 #define SSB_SROM8_FEM_PDET_RANGE 0x00F8 #define SSB_SROM8_FEM_PDET_RANGE_SHIFT 3 #define SSB_SROM8_FEM_TR_ISO 0x0700 #define SSB_SROM8_FEM_TR_ISO_SHIFT 8 #define SSB_SROM8_FEM_ANTSWLUT 0xF800 #define SSB_SROM8_FEM_ANTSWLUT_SHIFT 11 #define SSB_SPROM8_THERMAL 0x00B2 #define SSB_SPROM8_THERMAL_OFFSET 0x00ff #define SSB_SPROM8_THERMAL_OFFSET_SHIFT 0 #define SSB_SPROM8_THERMAL_TRESH 0xff00 #define SSB_SPROM8_THERMAL_TRESH_SHIFT 8 /* Temp sense related entries */ #define SSB_SPROM8_RAWTS 0x00B4 #define SSB_SPROM8_RAWTS_RAWTEMP 0x01ff #define SSB_SPROM8_RAWTS_RAWTEMP_SHIFT 0 #define SSB_SPROM8_RAWTS_MEASPOWER 0xfe00 #define SSB_SPROM8_RAWTS_MEASPOWER_SHIFT 9 #define SSB_SPROM8_OPT_CORRX 0x00B6 #define SSB_SPROM8_OPT_CORRX_TEMP_SLOPE 0x00ff #define SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT 0 #define SSB_SPROM8_OPT_CORRX_TEMPCORRX 0xfc00 #define SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT 10 #define SSB_SPROM8_OPT_CORRX_TEMP_OPTION 0x0300 #define SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT 8 /* FOC: freiquency offset correction, HWIQ: H/W IOCAL enable, IQSWP: IQ CAL swap disable */ #define SSB_SPROM8_HWIQ_IQSWP 0x00B8 #define SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR 0x000f #define SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT 0 #define SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP 0x0010 #define SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT 4 #define SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL 0x0020 #define SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT 5 #define SSB_SPROM8_TEMPDELTA 0x00BA #define SSB_SPROM8_TEMPDELTA_PHYCAL 0x00ff #define SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT 0 #define SSB_SPROM8_TEMPDELTA_PERIOD 0x0f00 #define SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT 8 #define SSB_SPROM8_TEMPDELTA_HYSTERESIS 0xf000 #define SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT 12 /* There are 4 blocks with power info sharing the same layout */ #define SSB_SROM8_PWR_INFO_CORE0 0x00C0 #define SSB_SROM8_PWR_INFO_CORE1 0x00E0 #define SSB_SROM8_PWR_INFO_CORE2 0x0100 #define SSB_SROM8_PWR_INFO_CORE3 0x0120 #define SSB_SROM8_2G_MAXP_ITSSI 0x00 #define SSB_SPROM8_2G_MAXP 0x00FF #define SSB_SPROM8_2G_ITSSI 0xFF00 #define SSB_SPROM8_2G_ITSSI_SHIFT 8 #define SSB_SROM8_2G_PA_0 0x02 /* 2GHz power amp settings */ #define SSB_SROM8_2G_PA_1 0x04 #define SSB_SROM8_2G_PA_2 0x06 #define SSB_SROM8_5G_MAXP_ITSSI 0x08 /* 5GHz ITSSI and 5.3GHz Max Power */ #define SSB_SPROM8_5G_MAXP 0x00FF #define SSB_SPROM8_5G_ITSSI 0xFF00 #define SSB_SPROM8_5G_ITSSI_SHIFT 8 #define SSB_SPROM8_5GHL_MAXP 0x0A /* 5.2GHz and 5.8GHz Max Power */ #define SSB_SPROM8_5GH_MAXP 0x00FF #define SSB_SPROM8_5GL_MAXP 0xFF00 #define SSB_SPROM8_5GL_MAXP_SHIFT 8 #define SSB_SROM8_5G_PA_0 0x0C /* 5.3GHz power amp settings */ #define SSB_SROM8_5G_PA_1 0x0E #define SSB_SROM8_5G_PA_2 0x10 #define SSB_SROM8_5GL_PA_0 0x12 /* 5.2GHz power amp settings */ #define SSB_SROM8_5GL_PA_1 0x14 #define SSB_SROM8_5GL_PA_2 0x16 #define SSB_SROM8_5GH_PA_0 0x18 /* 5.8GHz power amp settings */ #define SSB_SROM8_5GH_PA_1 0x1A #define SSB_SROM8_5GH_PA_2 0x1C /* TODO: Make it deprecated */ #define SSB_SPROM8_MAXP_BG 0x00C0 /* Max Power 2GHz in path 1 */ #define SSB_SPROM8_MAXP_BG_MASK 0x00FF /* Mask for Max Power 2GHz */ #define SSB_SPROM8_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ #define SSB_SPROM8_ITSSI_BG_SHIFT 8 #define SSB_SPROM8_PA0B0 0x00C2 /* 2GHz power amp settings */ #define SSB_SPROM8_PA0B1 0x00C4 #define SSB_SPROM8_PA0B2 0x00C6 #define SSB_SPROM8_MAXP_A 0x00C8 /* Max Power 5.3GHz */ #define SSB_SPROM8_MAXP_A_MASK 0x00FF /* Mask for Max Power 5.3GHz */ #define SSB_SPROM8_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ #define SSB_SPROM8_ITSSI_A_SHIFT 8 #define SSB_SPROM8_MAXP_AHL 0x00CA /* Max Power 5.2/5.8GHz */ #define SSB_SPROM8_MAXP_AH_MASK 0x00FF /* Mask for Max Power 5.8GHz */ #define SSB_SPROM8_MAXP_AL_MASK 0xFF00 /* Mask for Max Power 5.2GHz */ #define SSB_SPROM8_MAXP_AL_SHIFT 8 #define SSB_SPROM8_PA1B0 0x00CC /* 5.3GHz power amp settings */ #define SSB_SPROM8_PA1B1 0x00CE #define SSB_SPROM8_PA1B2 0x00D0 #define SSB_SPROM8_PA1LOB0 0x00D2 /* 5.2GHz power amp settings */ #define SSB_SPROM8_PA1LOB1 0x00D4 #define SSB_SPROM8_PA1LOB2 0x00D6 #define SSB_SPROM8_PA1HIB0 0x00D8 /* 5.8GHz power amp settings */ #define SSB_SPROM8_PA1HIB1 0x00DA #define SSB_SPROM8_PA1HIB2 0x00DC #define SSB_SPROM8_CCK2GPO 0x0140 /* CCK power offset */ #define SSB_SPROM8_OFDM2GPO 0x0142 /* 2.4GHz OFDM power offset */ #define SSB_SPROM8_OFDM5GPO 0x0146 /* 5.3GHz OFDM power offset */ #define SSB_SPROM8_OFDM5GLPO 0x014A /* 5.2GHz OFDM power offset */ #define SSB_SPROM8_OFDM5GHPO 0x014E /* 5.8GHz OFDM power offset */ #define SSB_SPROM8_2G_MCSPO 0x0152 #define SSB_SPROM8_5G_MCSPO 0x0162 #define SSB_SPROM8_5GL_MCSPO 0x0172 #define SSB_SPROM8_5GH_MCSPO 0x0182 #define SSB_SPROM8_CDDPO 0x0192 #define SSB_SPROM8_STBCPO 0x0194 #define SSB_SPROM8_BW40PO 0x0196 #define SSB_SPROM8_BWDUPPO 0x0198 /* Values for boardflags_lo read from SPROM */ #define SSB_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ #define SSB_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ #define SSB_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ #define SSB_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ #define SSB_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ #define SSB_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ #define SSB_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ #define SSB_BFL_ENETADM 0x0080 /* has ADMtek switch */ #define SSB_BFL_ENETVLAN 0x0100 /* can do vlan */ #define SSB_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ #define SSB_BFL_NOPCI 0x0400 /* board leaves PCI floating */ #define SSB_BFL_FEM 0x0800 /* supports the Front End Module */ #define SSB_BFL_EXTLNA 0x1000 /* has an external LNA */ #define SSB_BFL_HGPA 0x2000 /* had high gain PA */ #define SSB_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ #define SSB_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ /* Values for boardflags_hi read from SPROM */ #define SSB_BFH_NOPA 0x0001 /* has no PA */ #define SSB_BFH_RSSIINV 0x0002 /* RSSI uses positive slope (not TSSI) */ #define SSB_BFH_PAREF 0x0004 /* uses the PARef LDO */ #define SSB_BFH_3TSWITCH 0x0008 /* uses a triple throw switch shared with bluetooth */ #define SSB_BFH_PHASESHIFT 0x0010 /* can support phase shifter */ #define SSB_BFH_BUCKBOOST 0x0020 /* has buck/booster */ #define SSB_BFH_FEM_BT 0x0040 /* has FEM and switch to share antenna with bluetooth */ /* Values for boardflags2_lo read from SPROM */ #define SSB_BFL2_RXBB_INT_REG_DIS 0x0001 /* external RX BB regulator present */ #define SSB_BFL2_APLL_WAR 0x0002 /* alternative A-band PLL settings implemented */ #define SSB_BFL2_TXPWRCTRL_EN 0x0004 /* permits enabling TX Power Control */ #define SSB_BFL2_2X4_DIV 0x0008 /* 2x4 diversity switch */ #define SSB_BFL2_5G_PWRGAIN 0x0010 /* supports 5G band power gain */ #define SSB_BFL2_PCIEWAR_OVR 0x0020 /* overrides ASPM and Clkreq settings */ #define SSB_BFL2_CAESERS_BRD 0x0040 /* is Caesers board (unused) */ #define SSB_BFL2_BTC3WIRE 0x0080 /* used 3-wire bluetooth coexist */ #define SSB_BFL2_SKWRKFEM_BRD 0x0100 /* 4321mcm93 uses Skyworks FEM */ #define SSB_BFL2_SPUR_WAR 0x0200 /* has a workaround for clock-harmonic spurs */ #define SSB_BFL2_GPLL_WAR 0x0400 /* altenative G-band PLL settings implemented */ /* Values for SSB_SPROM1_BINF_CCODE */ enum { SSB_SPROM1CCODE_WORLD = 0, SSB_SPROM1CCODE_THAILAND, SSB_SPROM1CCODE_ISRAEL, SSB_SPROM1CCODE_JORDAN, SSB_SPROM1CCODE_CHINA, SSB_SPROM1CCODE_JAPAN, SSB_SPROM1CCODE_USA_CANADA_ANZ, SSB_SPROM1CCODE_EUROPE, SSB_SPROM1CCODE_USA_LOW, SSB_SPROM1CCODE_JAPAN_HIGH, SSB_SPROM1CCODE_ALL, SSB_SPROM1CCODE_NONE, }; /* Address-Match values and masks (SSB_ADMATCHxxx) */ #define SSB_ADM_TYPE 0x00000003 /* Address type */ #define SSB_ADM_TYPE0 0 #define SSB_ADM_TYPE1 1 #define SSB_ADM_TYPE2 2 #define SSB_ADM_AD64 0x00000004 #define SSB_ADM_SZ0 0x000000F8 /* Type0 size */ #define SSB_ADM_SZ0_SHIFT 3 #define SSB_ADM_SZ1 0x000001F8 /* Type1 size */ #define SSB_ADM_SZ1_SHIFT 3 #define SSB_ADM_SZ2 0x000001F8 /* Type2 size */ #define SSB_ADM_SZ2_SHIFT 3 #define SSB_ADM_EN 0x00000400 /* Enable */ #define SSB_ADM_NEG 0x00000800 /* Negative decode */ #define SSB_ADM_BASE0 0xFFFFFF00 /* Type0 base address */ #define SSB_ADM_BASE0_SHIFT 8 #define SSB_ADM_BASE1 0xFFFFF000 /* Type1 base address for the core */ #define SSB_ADM_BASE1_SHIFT 12 #define SSB_ADM_BASE2 0xFFFF0000 /* Type2 base address for the core */ #define SSB_ADM_BASE2_SHIFT 16 #endif /* LINUX_SSB_REGS_H_ */ compat-drivers-2012-09-18/include/linux/ssb/ssb_driver_mips.h0000644000175000017500000000145012006264415023303 0ustar mcgrofmcgrof#ifndef LINUX_SSB_MIPSCORE_H_ #define LINUX_SSB_MIPSCORE_H_ #ifdef CONFIG_SSB_DRIVER_MIPS struct ssb_device; struct ssb_serial_port { void *regs; unsigned long clockspeed; unsigned int irq; unsigned int baud_base; unsigned int reg_shift; }; struct ssb_mipscore { struct ssb_device *dev; int nr_serial_ports; struct ssb_serial_port serial_ports[4]; u8 flash_buswidth; u32 flash_window; u32 flash_window_size; }; extern void ssb_mipscore_init(struct ssb_mipscore *mcore); extern u32 ssb_cpu_clock(struct ssb_mipscore *mcore); extern unsigned int ssb_mips_irq(struct ssb_device *dev); #else /* CONFIG_SSB_DRIVER_MIPS */ struct ssb_mipscore { }; static inline void ssb_mipscore_init(struct ssb_mipscore *mcore) { } #endif /* CONFIG_SSB_DRIVER_MIPS */ #endif /* LINUX_SSB_MIPSCORE_H_ */ compat-drivers-2012-09-18/include/linux/ssb/ssb_driver_extif.h0000644000175000017500000001631212006264415023455 0ustar mcgrofmcgrof/* * Hardware-specific External Interface I/O core definitions * for the BCM47xx family of SiliconBackplane-based chips. * * The External Interface core supports a total of three external chip selects * supporting external interfaces. One of the external chip selects is * used for Flash, one is used for PCMCIA, and the other may be * programmed to support either a synchronous interface or an * asynchronous interface. The asynchronous interface can be used to * support external devices such as UARTs and the BCM2019 Bluetooth * baseband processor. * The external interface core also contains 2 on-chip 16550 UARTs, clock * frequency control, a watchdog interrupt timer, and a GPIO interface. * * Copyright 2005, Broadcom Corporation * Copyright 2006, Michael Buesch * * Licensed under the GPL version 2. See COPYING for details. */ #ifndef LINUX_SSB_EXTIFCORE_H_ #define LINUX_SSB_EXTIFCORE_H_ /* external interface address space */ #define SSB_EXTIF_PCMCIA_MEMBASE(x) (x) #define SSB_EXTIF_PCMCIA_IOBASE(x) ((x) + 0x100000) #define SSB_EXTIF_PCMCIA_CFGBASE(x) ((x) + 0x200000) #define SSB_EXTIF_CFGIF_BASE(x) ((x) + 0x800000) #define SSB_EXTIF_FLASH_BASE(x) ((x) + 0xc00000) #define SSB_EXTIF_NR_GPIOOUT 5 /* GPIO NOTE: * The multiple instances of output and output enable registers * are present to allow driver software for multiple cores to control * gpio outputs without needing to share a single register pair. * Use the following helper macro to get a register offset value. */ #define SSB_EXTIF_GPIO_OUT(index) ({ \ BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT); \ SSB_EXTIF_GPIO_OUT_BASE + ((index) * 8); \ }) #define SSB_EXTIF_GPIO_OUTEN(index) ({ \ BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT); \ SSB_EXTIF_GPIO_OUTEN_BASE + ((index) * 8); \ }) /** EXTIF core registers **/ #define SSB_EXTIF_CTL 0x0000 #define SSB_EXTIF_CTL_UARTEN (1 << 0) /* UART enable */ #define SSB_EXTIF_EXTSTAT 0x0004 #define SSB_EXTIF_EXTSTAT_EMODE (1 << 0) /* Endian mode (ro) */ #define SSB_EXTIF_EXTSTAT_EIRQPIN (1 << 1) /* External interrupt pin (ro) */ #define SSB_EXTIF_EXTSTAT_GPIOIRQPIN (1 << 2) /* GPIO interrupt pin (ro) */ #define SSB_EXTIF_PCMCIA_CFG 0x0010 #define SSB_EXTIF_PCMCIA_MEMWAIT 0x0014 #define SSB_EXTIF_PCMCIA_ATTRWAIT 0x0018 #define SSB_EXTIF_PCMCIA_IOWAIT 0x001C #define SSB_EXTIF_PROG_CFG 0x0020 #define SSB_EXTIF_PROG_WAITCNT 0x0024 #define SSB_EXTIF_FLASH_CFG 0x0028 #define SSB_EXTIF_FLASH_WAITCNT 0x002C #define SSB_EXTIF_WATCHDOG 0x0040 #define SSB_EXTIF_CLOCK_N 0x0044 #define SSB_EXTIF_CLOCK_SB 0x0048 #define SSB_EXTIF_CLOCK_PCI 0x004C #define SSB_EXTIF_CLOCK_MII 0x0050 #define SSB_EXTIF_GPIO_IN 0x0060 #define SSB_EXTIF_GPIO_OUT_BASE 0x0064 #define SSB_EXTIF_GPIO_OUTEN_BASE 0x0068 #define SSB_EXTIF_EJTAG_OUTEN 0x0090 #define SSB_EXTIF_GPIO_INTPOL 0x0094 #define SSB_EXTIF_GPIO_INTMASK 0x0098 #define SSB_EXTIF_UART_DATA 0x0300 #define SSB_EXTIF_UART_TIMER 0x0310 #define SSB_EXTIF_UART_FCR 0x0320 #define SSB_EXTIF_UART_LCR 0x0330 #define SSB_EXTIF_UART_MCR 0x0340 #define SSB_EXTIF_UART_LSR 0x0350 #define SSB_EXTIF_UART_MSR 0x0360 #define SSB_EXTIF_UART_SCRATCH 0x0370 /* pcmcia/prog/flash_config */ #define SSB_EXTCFG_EN (1 << 0) /* enable */ #define SSB_EXTCFG_MODE 0xE /* mode */ #define SSB_EXTCFG_MODE_SHIFT 1 #define SSB_EXTCFG_MODE_FLASH 0x0 /* flash/asynchronous mode */ #define SSB_EXTCFG_MODE_SYNC 0x2 /* synchronous mode */ #define SSB_EXTCFG_MODE_PCMCIA 0x4 /* pcmcia mode */ #define SSB_EXTCFG_DS16 (1 << 4) /* destsize: 0=8bit, 1=16bit */ #define SSB_EXTCFG_BSWAP (1 << 5) /* byteswap */ #define SSB_EXTCFG_CLKDIV 0xC0 /* clock divider */ #define SSB_EXTCFG_CLKDIV_SHIFT 6 #define SSB_EXTCFG_CLKDIV_2 0x0 /* backplane/2 */ #define SSB_EXTCFG_CLKDIV_3 0x40 /* backplane/3 */ #define SSB_EXTCFG_CLKDIV_4 0x80 /* backplane/4 */ #define SSB_EXTCFG_CLKEN (1 << 8) /* clock enable */ #define SSB_EXTCFG_STROBE (1 << 9) /* size/bytestrobe (synch only) */ /* pcmcia_memwait */ #define SSB_PCMCIA_MEMW_0 0x0000003F /* waitcount0 */ #define SSB_PCMCIA_MEMW_1 0x00001F00 /* waitcount1 */ #define SSB_PCMCIA_MEMW_1_SHIFT 8 #define SSB_PCMCIA_MEMW_2 0x001F0000 /* waitcount2 */ #define SSB_PCMCIA_MEMW_2_SHIFT 16 #define SSB_PCMCIA_MEMW_3 0x1F000000 /* waitcount3 */ #define SSB_PCMCIA_MEMW_3_SHIFT 24 /* pcmcia_attrwait */ #define SSB_PCMCIA_ATTW_0 0x0000003F /* waitcount0 */ #define SSB_PCMCIA_ATTW_1 0x00001F00 /* waitcount1 */ #define SSB_PCMCIA_ATTW_1_SHIFT 8 #define SSB_PCMCIA_ATTW_2 0x001F0000 /* waitcount2 */ #define SSB_PCMCIA_ATTW_2_SHIFT 16 #define SSB_PCMCIA_ATTW_3 0x1F000000 /* waitcount3 */ #define SSB_PCMCIA_ATTW_3_SHIFT 24 /* pcmcia_iowait */ #define SSB_PCMCIA_IOW_0 0x0000003F /* waitcount0 */ #define SSB_PCMCIA_IOW_1 0x00001F00 /* waitcount1 */ #define SSB_PCMCIA_IOW_1_SHIFT 8 #define SSB_PCMCIA_IOW_2 0x001F0000 /* waitcount2 */ #define SSB_PCMCIA_IOW_2_SHIFT 16 #define SSB_PCMCIA_IOW_3 0x1F000000 /* waitcount3 */ #define SSB_PCMCIA_IOW_3_SHIFT 24 /* prog_waitcount */ #define SSB_PROG_WCNT_0 0x0000001F /* waitcount0 */ #define SSB_PROG_WCNT_1 0x00001F00 /* waitcount1 */ #define SSB_PROG_WCNT_1_SHIFT 8 #define SSB_PROG_WCNT_2 0x001F0000 /* waitcount2 */ #define SSB_PROG_WCNT_2_SHIFT 16 #define SSB_PROG_WCNT_3 0x1F000000 /* waitcount3 */ #define SSB_PROG_WCNT_3_SHIFT 24 #define SSB_PROG_W0 0x0000000C #define SSB_PROG_W1 0x00000A00 #define SSB_PROG_W2 0x00020000 #define SSB_PROG_W3 0x01000000 /* flash_waitcount */ #define SSB_FLASH_WCNT_0 0x0000001F /* waitcount0 */ #define SSB_FLASH_WCNT_1 0x00001F00 /* waitcount1 */ #define SSB_FLASH_WCNT_1_SHIFT 8 #define SSB_FLASH_WCNT_2 0x001F0000 /* waitcount2 */ #define SSB_FLASH_WCNT_2_SHIFT 16 #define SSB_FLASH_WCNT_3 0x1F000000 /* waitcount3 */ #define SSB_FLASH_WCNT_3_SHIFT 24 /* watchdog */ #define SSB_EXTIF_WATCHDOG_CLK 48000000 /* Hz */ #ifdef CONFIG_SSB_DRIVER_EXTIF struct ssb_extif { struct ssb_device *dev; }; static inline bool ssb_extif_available(struct ssb_extif *extif) { return (extif->dev != NULL); } extern void ssb_extif_get_clockcontrol(struct ssb_extif *extif, u32 *plltype, u32 *n, u32 *m); extern void ssb_extif_timing_init(struct ssb_extif *extif, unsigned long ns); extern void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks); /* Extif GPIO pin access */ u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask); u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value); u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value); u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, u32 value); u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, u32 value); #ifdef CONFIG_SSB_SERIAL extern int ssb_extif_serial_init(struct ssb_extif *extif, struct ssb_serial_port *ports); #endif /* CONFIG_SSB_SERIAL */ #else /* CONFIG_SSB_DRIVER_EXTIF */ /* extif disabled */ struct ssb_extif { }; static inline bool ssb_extif_available(struct ssb_extif *extif) { return 0; } static inline void ssb_extif_get_clockcontrol(struct ssb_extif *extif, u32 *plltype, u32 *n, u32 *m) { } static inline void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks) { } #endif /* CONFIG_SSB_DRIVER_EXTIF */ #endif /* LINUX_SSB_EXTIFCORE_H_ */ compat-drivers-2012-09-18/include/linux/ssb/ssb_embedded.h0000644000175000017500000000113512006264415022511 0ustar mcgrofmcgrof#ifndef LINUX_SSB_EMBEDDED_H_ #define LINUX_SSB_EMBEDDED_H_ #include #include extern int ssb_watchdog_timer_set(struct ssb_bus *bus, u32 ticks); /* Generic GPIO API */ u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask); u32 ssb_gpio_out(struct ssb_bus *bus, u32 mask, u32 value); u32 ssb_gpio_outen(struct ssb_bus *bus, u32 mask, u32 value); u32 ssb_gpio_control(struct ssb_bus *bus, u32 mask, u32 value); u32 ssb_gpio_intmask(struct ssb_bus *bus, u32 mask, u32 value); u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value); #endif /* LINUX_SSB_EMBEDDED_H_ */ compat-drivers-2012-09-18/include/linux/ssb/ssb_driver_gige.h0000644000175000017500000001162012006264415023246 0ustar mcgrofmcgrof#ifndef LINUX_SSB_DRIVER_GIGE_H_ #define LINUX_SSB_DRIVER_GIGE_H_ #include #include #include #include #ifdef CONFIG_SSB_DRIVER_GIGE #define SSB_GIGE_PCIIO 0x0000 /* PCI I/O Registers (1024 bytes) */ #define SSB_GIGE_RESERVED 0x0400 /* Reserved (1024 bytes) */ #define SSB_GIGE_PCICFG 0x0800 /* PCI config space (256 bytes) */ #define SSB_GIGE_SHIM_FLUSHSTAT 0x0C00 /* PCI to OCP: Flush status control (32bit) */ #define SSB_GIGE_SHIM_FLUSHRDA 0x0C04 /* PCI to OCP: Flush read address (32bit) */ #define SSB_GIGE_SHIM_FLUSHTO 0x0C08 /* PCI to OCP: Flush timeout counter (32bit) */ #define SSB_GIGE_SHIM_BARRIER 0x0C0C /* PCI to OCP: Barrier register (32bit) */ #define SSB_GIGE_SHIM_MAOCPSI 0x0C10 /* PCI to OCP: MaocpSI Control (32bit) */ #define SSB_GIGE_SHIM_SIOCPMA 0x0C14 /* PCI to OCP: SiocpMa Control (32bit) */ /* TM Status High flags */ #define SSB_GIGE_TMSHIGH_RGMII 0x00010000 /* Have an RGMII PHY-bus */ /* TM Status Low flags */ #define SSB_GIGE_TMSLOW_TXBYPASS 0x00080000 /* TX bypass (no delay) */ #define SSB_GIGE_TMSLOW_RXBYPASS 0x00100000 /* RX bypass (no delay) */ #define SSB_GIGE_TMSLOW_DLLEN 0x01000000 /* Enable DLL controls */ /* Boardflags (low) */ #define SSB_GIGE_BFL_ROBOSWITCH 0x0010 #define SSB_GIGE_MEM_RES_NAME "SSB Broadcom 47xx GigE memory" #define SSB_GIGE_IO_RES_NAME "SSB Broadcom 47xx GigE I/O" struct ssb_gige { struct ssb_device *dev; spinlock_t lock; /* True, if the device has an RGMII bus. * False, if the device has a GMII bus. */ bool has_rgmii; /* The PCI controller device. */ struct pci_controller pci_controller; struct pci_ops pci_ops; struct resource mem_resource; struct resource io_resource; }; /* Check whether a PCI device is a SSB Gigabit Ethernet core. */ extern bool pdev_is_ssb_gige_core(struct pci_dev *pdev); /* Convert a pci_dev pointer to a ssb_gige pointer. */ static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev) { if (!pdev_is_ssb_gige_core(pdev)) return NULL; return container_of(pdev->bus->ops, struct ssb_gige, pci_ops); } /* Returns whether the PHY is connected by an RGMII bus. */ static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev) { struct ssb_gige *dev = pdev_to_ssb_gige(pdev); return (dev ? dev->has_rgmii : 0); } /* Returns whether we have a Roboswitch. */ static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev) { struct ssb_gige *dev = pdev_to_ssb_gige(pdev); if (dev) return !!(dev->dev->bus->sprom.boardflags_lo & SSB_GIGE_BFL_ROBOSWITCH); return 0; } /* Returns whether we can only do one DMA at once. */ static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev) { struct ssb_gige *dev = pdev_to_ssb_gige(pdev); if (dev) return ((dev->dev->bus->chip_id == 0x4785) && (dev->dev->bus->chip_rev < 2)); return 0; } /* Returns whether we must flush posted writes. */ static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev) { struct ssb_gige *dev = pdev_to_ssb_gige(pdev); if (dev) return (dev->dev->bus->chip_id == 0x4785); return 0; } #ifdef CONFIG_BCM47XX #include /* Get the device MAC address */ static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) { char buf[20]; if (nvram_getenv("et0macaddr", buf, sizeof(buf)) < 0) return; nvram_parse_macaddr(buf, macaddr); } #else static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) { } #endif extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, struct pci_dev *pdev); extern int ssb_gige_map_irq(struct ssb_device *sdev, const struct pci_dev *pdev); /* The GigE driver is not a standalone module, because we don't have support * for unregistering the driver. So we could not unload the module anyway. */ extern int ssb_gige_init(void); static inline void ssb_gige_exit(void) { /* Currently we can not unregister the GigE driver, * because we can not unregister the PCI bridge. */ BUG(); } #else /* CONFIG_SSB_DRIVER_GIGE */ /* Gigabit Ethernet driver disabled */ static inline int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, struct pci_dev *pdev) { return -ENOSYS; } static inline int ssb_gige_map_irq(struct ssb_device *sdev, const struct pci_dev *pdev) { return -ENOSYS; } static inline int ssb_gige_init(void) { return 0; } static inline void ssb_gige_exit(void) { } static inline bool pdev_is_ssb_gige_core(struct pci_dev *pdev) { return 0; } static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev) { return NULL; } static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev) { return 0; } static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev) { return 0; } static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev) { return 0; } static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev) { return 0; } #endif /* CONFIG_SSB_DRIVER_GIGE */ #endif /* LINUX_SSB_DRIVER_GIGE_H_ */ compat-drivers-2012-09-18/include/linux/ssb/ssb_driver_pci.h0000644000175000017500000001251212006264415023107 0ustar mcgrofmcgrof#ifndef LINUX_SSB_PCICORE_H_ #define LINUX_SSB_PCICORE_H_ #include struct pci_dev; #ifdef CONFIG_SSB_DRIVER_PCICORE /* PCI core registers. */ #define SSB_PCICORE_CTL 0x0000 /* PCI Control */ #define SSB_PCICORE_CTL_RST_OE 0x00000001 /* PCI_RESET Output Enable */ #define SSB_PCICORE_CTL_RST 0x00000002 /* PCI_RESET driven out to pin */ #define SSB_PCICORE_CTL_CLK_OE 0x00000004 /* Clock gate Output Enable */ #define SSB_PCICORE_CTL_CLK 0x00000008 /* Gate for clock driven out to pin */ #define SSB_PCICORE_ARBCTL 0x0010 /* PCI Arbiter Control */ #define SSB_PCICORE_ARBCTL_INTERN 0x00000001 /* Use internal arbiter */ #define SSB_PCICORE_ARBCTL_EXTERN 0x00000002 /* Use external arbiter */ #define SSB_PCICORE_ARBCTL_PARKID 0x00000006 /* Mask, selects which agent is parked on an idle bus */ #define SSB_PCICORE_ARBCTL_PARKID_LAST 0x00000000 /* Last requestor */ #define SSB_PCICORE_ARBCTL_PARKID_4710 0x00000002 /* 4710 */ #define SSB_PCICORE_ARBCTL_PARKID_EXT0 0x00000004 /* External requestor 0 */ #define SSB_PCICORE_ARBCTL_PARKID_EXT1 0x00000006 /* External requestor 1 */ #define SSB_PCICORE_ISTAT 0x0020 /* Interrupt status */ #define SSB_PCICORE_ISTAT_INTA 0x00000001 /* PCI INTA# */ #define SSB_PCICORE_ISTAT_INTB 0x00000002 /* PCI INTB# */ #define SSB_PCICORE_ISTAT_SERR 0x00000004 /* PCI SERR# (write to clear) */ #define SSB_PCICORE_ISTAT_PERR 0x00000008 /* PCI PERR# (write to clear) */ #define SSB_PCICORE_ISTAT_PME 0x00000010 /* PCI PME# */ #define SSB_PCICORE_IMASK 0x0024 /* Interrupt mask */ #define SSB_PCICORE_IMASK_INTA 0x00000001 /* PCI INTA# */ #define SSB_PCICORE_IMASK_INTB 0x00000002 /* PCI INTB# */ #define SSB_PCICORE_IMASK_SERR 0x00000004 /* PCI SERR# */ #define SSB_PCICORE_IMASK_PERR 0x00000008 /* PCI PERR# */ #define SSB_PCICORE_IMASK_PME 0x00000010 /* PCI PME# */ #define SSB_PCICORE_MBOX 0x0028 /* Backplane to PCI Mailbox */ #define SSB_PCICORE_MBOX_F0_0 0x00000100 /* PCI function 0, INT 0 */ #define SSB_PCICORE_MBOX_F0_1 0x00000200 /* PCI function 0, INT 1 */ #define SSB_PCICORE_MBOX_F1_0 0x00000400 /* PCI function 1, INT 0 */ #define SSB_PCICORE_MBOX_F1_1 0x00000800 /* PCI function 1, INT 1 */ #define SSB_PCICORE_MBOX_F2_0 0x00001000 /* PCI function 2, INT 0 */ #define SSB_PCICORE_MBOX_F2_1 0x00002000 /* PCI function 2, INT 1 */ #define SSB_PCICORE_MBOX_F3_0 0x00004000 /* PCI function 3, INT 0 */ #define SSB_PCICORE_MBOX_F3_1 0x00008000 /* PCI function 3, INT 1 */ #define SSB_PCICORE_BCAST_ADDR 0x0050 /* Backplane Broadcast Address */ #define SSB_PCICORE_BCAST_ADDR_MASK 0x000000FF #define SSB_PCICORE_BCAST_DATA 0x0054 /* Backplane Broadcast Data */ #define SSB_PCICORE_GPIO_IN 0x0060 /* rev >= 2 only */ #define SSB_PCICORE_GPIO_OUT 0x0064 /* rev >= 2 only */ #define SSB_PCICORE_GPIO_ENABLE 0x0068 /* rev >= 2 only */ #define SSB_PCICORE_GPIO_CTL 0x006C /* rev >= 2 only */ #define SSB_PCICORE_SBTOPCI0 0x0100 /* Backplane to PCI translation 0 (sbtopci0) */ #define SSB_PCICORE_SBTOPCI0_MASK 0xFC000000 #define SSB_PCICORE_SBTOPCI1 0x0104 /* Backplane to PCI translation 1 (sbtopci1) */ #define SSB_PCICORE_SBTOPCI1_MASK 0xFC000000 #define SSB_PCICORE_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */ #define SSB_PCICORE_SBTOPCI2_MASK 0xC0000000 #define SSB_PCICORE_PCICFG0 0x0400 /* PCI config space 0 (rev >= 8) */ #define SSB_PCICORE_PCICFG1 0x0500 /* PCI config space 1 (rev >= 8) */ #define SSB_PCICORE_PCICFG2 0x0600 /* PCI config space 2 (rev >= 8) */ #define SSB_PCICORE_PCICFG3 0x0700 /* PCI config space 3 (rev >= 8) */ #define SSB_PCICORE_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) /* SPROM shadow area (72 bytes) */ /* SBtoPCIx */ #define SSB_PCICORE_SBTOPCI_MEM 0x00000000 #define SSB_PCICORE_SBTOPCI_IO 0x00000001 #define SSB_PCICORE_SBTOPCI_CFG0 0x00000002 #define SSB_PCICORE_SBTOPCI_CFG1 0x00000003 #define SSB_PCICORE_SBTOPCI_PREF 0x00000004 /* Prefetch enable */ #define SSB_PCICORE_SBTOPCI_BURST 0x00000008 /* Burst enable */ #define SSB_PCICORE_SBTOPCI_MRM 0x00000020 /* Memory Read Multiple */ #define SSB_PCICORE_SBTOPCI_RC 0x00000030 /* Read Command mask (rev >= 11) */ #define SSB_PCICORE_SBTOPCI_RC_READ 0x00000000 /* Memory read */ #define SSB_PCICORE_SBTOPCI_RC_READL 0x00000010 /* Memory read line */ #define SSB_PCICORE_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */ /* PCIcore specific boardflags */ #define SSB_PCICORE_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */ struct ssb_pcicore { struct ssb_device *dev; u8 setup_done:1; u8 hostmode:1; u8 cardbusmode:1; }; extern void ssb_pcicore_init(struct ssb_pcicore *pc); /* Enable IRQ routing for a specific device */ extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, struct ssb_device *dev); int ssb_pcicore_plat_dev_init(struct pci_dev *d); int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); #else /* CONFIG_SSB_DRIVER_PCICORE */ struct ssb_pcicore { }; static inline void ssb_pcicore_init(struct ssb_pcicore *pc) { } static inline int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, struct ssb_device *dev) { return 0; } static inline int ssb_pcicore_plat_dev_init(struct pci_dev *d) { return -ENODEV; } static inline int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { return -ENODEV; } #endif /* CONFIG_SSB_DRIVER_PCICORE */ #endif /* LINUX_SSB_PCICORE_H_ */ compat-drivers-2012-09-18/include/linux/ssb/ssb.h0000644000175000017500000004410512006264415020704 0ustar mcgrofmcgrof#ifndef LINUX_SSB_H_ #define LINUX_SSB_H_ #include #include #include #include #include #include #include #include struct pcmcia_device; struct ssb_bus; struct ssb_driver; struct ssb_sprom_core_pwr_info { u8 itssi_2g, itssi_5g; u8 maxpwr_2g, maxpwr_5gl, maxpwr_5g, maxpwr_5gh; u16 pa_2g[4], pa_5gl[4], pa_5g[4], pa_5gh[4]; }; struct ssb_sprom { u8 revision; u8 il0mac[6]; /* MAC address for 802.11b/g */ u8 et0mac[6]; /* MAC address for Ethernet */ u8 et1mac[6]; /* MAC address for 802.11a */ u8 et0phyaddr; /* MII address for enet0 */ u8 et1phyaddr; /* MII address for enet1 */ u8 et0mdcport; /* MDIO for enet0 */ u8 et1mdcport; /* MDIO for enet1 */ u16 board_rev; /* Board revision number from SPROM. */ u16 board_num; /* Board number from SPROM. */ u16 board_type; /* Board type from SPROM. */ u8 country_code; /* Country Code */ char alpha2[2]; /* Country Code as two chars like EU or US */ u8 leddc_on_time; /* LED Powersave Duty Cycle On Count */ u8 leddc_off_time; /* LED Powersave Duty Cycle Off Count */ u8 ant_available_a; /* 2GHz antenna available bits (up to 4) */ u8 ant_available_bg; /* 5GHz antenna available bits (up to 4) */ u16 pa0b0; u16 pa0b1; u16 pa0b2; u16 pa1b0; u16 pa1b1; u16 pa1b2; u16 pa1lob0; u16 pa1lob1; u16 pa1lob2; u16 pa1hib0; u16 pa1hib1; u16 pa1hib2; u8 gpio0; /* GPIO pin 0 */ u8 gpio1; /* GPIO pin 1 */ u8 gpio2; /* GPIO pin 2 */ u8 gpio3; /* GPIO pin 3 */ u8 maxpwr_bg; /* 2.4GHz Amplifier Max Power (in dBm Q5.2) */ u8 maxpwr_al; /* 5.2GHz Amplifier Max Power (in dBm Q5.2) */ u8 maxpwr_a; /* 5.3GHz Amplifier Max Power (in dBm Q5.2) */ u8 maxpwr_ah; /* 5.8GHz Amplifier Max Power (in dBm Q5.2) */ u8 itssi_a; /* Idle TSSI Target for A-PHY */ u8 itssi_bg; /* Idle TSSI Target for B/G-PHY */ u8 tri2g; /* 2.4GHz TX isolation */ u8 tri5gl; /* 5.2GHz TX isolation */ u8 tri5g; /* 5.3GHz TX isolation */ u8 tri5gh; /* 5.8GHz TX isolation */ u8 txpid2g[4]; /* 2GHz TX power index */ u8 txpid5gl[4]; /* 4.9 - 5.1GHz TX power index */ u8 txpid5g[4]; /* 5.1 - 5.5GHz TX power index */ u8 txpid5gh[4]; /* 5.5 - ...GHz TX power index */ s8 rxpo2g; /* 2GHz RX power offset */ s8 rxpo5g; /* 5GHz RX power offset */ u8 rssisav2g; /* 2GHz RSSI params */ u8 rssismc2g; u8 rssismf2g; u8 bxa2g; /* 2GHz BX arch */ u8 rssisav5g; /* 5GHz RSSI params */ u8 rssismc5g; u8 rssismf5g; u8 bxa5g; /* 5GHz BX arch */ u16 cck2gpo; /* CCK power offset */ u32 ofdm2gpo; /* 2.4GHz OFDM power offset */ u32 ofdm5glpo; /* 5.2GHz OFDM power offset */ u32 ofdm5gpo; /* 5.3GHz OFDM power offset */ u32 ofdm5ghpo; /* 5.8GHz OFDM power offset */ u16 boardflags_lo; /* Board flags (bits 0-15) */ u16 boardflags_hi; /* Board flags (bits 16-31) */ u16 boardflags2_lo; /* Board flags (bits 32-47) */ u16 boardflags2_hi; /* Board flags (bits 48-63) */ /* TODO store board flags in a single u64 */ struct ssb_sprom_core_pwr_info core_pwr_info[4]; /* Antenna gain values for up to 4 antennas * on each band. Values in dBm/4 (Q5.2). Negative gain means the * loss in the connectors is bigger than the gain. */ struct { s8 a0, a1, a2, a3; } antenna_gain; struct { struct { u8 tssipos, extpa_gain, pdet_range, tr_iso, antswlut; } ghz2; struct { u8 tssipos, extpa_gain, pdet_range, tr_iso, antswlut; } ghz5; } fem; u16 mcs2gpo[8]; u16 mcs5gpo[8]; u16 mcs5glpo[8]; u16 mcs5ghpo[8]; u8 opo; u8 rxgainerr2ga[3]; u8 rxgainerr5gla[3]; u8 rxgainerr5gma[3]; u8 rxgainerr5gha[3]; u8 rxgainerr5gua[3]; u8 noiselvl2ga[3]; u8 noiselvl5gla[3]; u8 noiselvl5gma[3]; u8 noiselvl5gha[3]; u8 noiselvl5gua[3]; u8 regrev; u8 txchain; u8 rxchain; u8 antswitch; u16 cddpo; u16 stbcpo; u16 bw40po; u16 bwduppo; u8 tempthresh; u8 tempoffset; u16 rawtempsense; u8 measpower; u8 tempsense_slope; u8 tempcorrx; u8 tempsense_option; u8 freqoffset_corr; u8 iqcal_swp_dis; u8 hw_iqcal_en; u8 elna2g; u8 elna5g; u8 phycal_tempdelta; u8 temps_period; u8 temps_hysteresis; u8 measpower1; u8 measpower2; u8 pcieingress_war; /* power per rate from sromrev 9 */ u16 cckbw202gpo; u16 cckbw20ul2gpo; u32 legofdmbw202gpo; u32 legofdmbw20ul2gpo; u32 legofdmbw205glpo; u32 legofdmbw20ul5glpo; u32 legofdmbw205gmpo; u32 legofdmbw20ul5gmpo; u32 legofdmbw205ghpo; u32 legofdmbw20ul5ghpo; u32 mcsbw202gpo; u32 mcsbw20ul2gpo; u32 mcsbw402gpo; u32 mcsbw205glpo; u32 mcsbw20ul5glpo; u32 mcsbw405glpo; u32 mcsbw205gmpo; u32 mcsbw20ul5gmpo; u32 mcsbw405gmpo; u32 mcsbw205ghpo; u32 mcsbw20ul5ghpo; u32 mcsbw405ghpo; u16 mcs32po; u16 legofdm40duppo; u8 sar2g; u8 sar5g; }; /* Information about the PCB the circuitry is soldered on. */ struct ssb_boardinfo { u16 vendor; u16 type; }; struct ssb_device; /* Lowlevel read/write operations on the device MMIO. * Internal, don't use that outside of ssb. */ struct ssb_bus_ops { u8 (*read8)(struct ssb_device *dev, u16 offset); u16 (*read16)(struct ssb_device *dev, u16 offset); u32 (*read32)(struct ssb_device *dev, u16 offset); void (*write8)(struct ssb_device *dev, u16 offset, u8 value); void (*write16)(struct ssb_device *dev, u16 offset, u16 value); void (*write32)(struct ssb_device *dev, u16 offset, u32 value); #ifdef CONFIG_SSB_BLOCKIO void (*block_read)(struct ssb_device *dev, void *buffer, size_t count, u16 offset, u8 reg_width); void (*block_write)(struct ssb_device *dev, const void *buffer, size_t count, u16 offset, u8 reg_width); #endif }; /* Core-ID values. */ #define SSB_DEV_CHIPCOMMON 0x800 #define SSB_DEV_ILINE20 0x801 #define SSB_DEV_SDRAM 0x803 #define SSB_DEV_PCI 0x804 #define SSB_DEV_MIPS 0x805 #define SSB_DEV_ETHERNET 0x806 #define SSB_DEV_V90 0x807 #define SSB_DEV_USB11_HOSTDEV 0x808 #define SSB_DEV_ADSL 0x809 #define SSB_DEV_ILINE100 0x80A #define SSB_DEV_IPSEC 0x80B #define SSB_DEV_PCMCIA 0x80D #define SSB_DEV_INTERNAL_MEM 0x80E #define SSB_DEV_MEMC_SDRAM 0x80F #define SSB_DEV_EXTIF 0x811 #define SSB_DEV_80211 0x812 #define SSB_DEV_MIPS_3302 0x816 #define SSB_DEV_USB11_HOST 0x817 #define SSB_DEV_USB11_DEV 0x818 #define SSB_DEV_USB20_HOST 0x819 #define SSB_DEV_USB20_DEV 0x81A #define SSB_DEV_SDIO_HOST 0x81B #define SSB_DEV_ROBOSWITCH 0x81C #define SSB_DEV_PARA_ATA 0x81D #define SSB_DEV_SATA_XORDMA 0x81E #define SSB_DEV_ETHERNET_GBIT 0x81F #define SSB_DEV_PCIE 0x820 #define SSB_DEV_MIMO_PHY 0x821 #define SSB_DEV_SRAM_CTRLR 0x822 #define SSB_DEV_MINI_MACPHY 0x823 #define SSB_DEV_ARM_1176 0x824 #define SSB_DEV_ARM_7TDMI 0x825 #define SSB_DEV_ARM_CM3 0x82A /* Vendor-ID values */ #define SSB_VENDOR_BROADCOM 0x4243 /* Some kernel subsystems poke with dev->drvdata, so we must use the * following ugly workaround to get from struct device to struct ssb_device */ struct __ssb_dev_wrapper { struct device dev; struct ssb_device *sdev; }; struct ssb_device { /* Having a copy of the ops pointer in each dev struct * is an optimization. */ const struct ssb_bus_ops *ops; struct device *dev, *dma_dev; struct ssb_bus *bus; struct ssb_device_id id; u8 core_index; unsigned int irq; /* Internal-only stuff follows. */ void *drvdata; /* Per-device data */ void *devtypedata; /* Per-devicetype (eg 802.11) data */ }; /* Go from struct device to struct ssb_device. */ static inline struct ssb_device * dev_to_ssb_dev(struct device *dev) { struct __ssb_dev_wrapper *wrap; wrap = container_of(dev, struct __ssb_dev_wrapper, dev); return wrap->sdev; } /* Device specific user data */ static inline void ssb_set_drvdata(struct ssb_device *dev, void *data) { dev->drvdata = data; } static inline void * ssb_get_drvdata(struct ssb_device *dev) { return dev->drvdata; } /* Devicetype specific user data. This is per device-type (not per device) */ void ssb_set_devtypedata(struct ssb_device *dev, void *data); static inline void * ssb_get_devtypedata(struct ssb_device *dev) { return dev->devtypedata; } struct ssb_driver { const char *name; const struct ssb_device_id *id_table; int (*probe)(struct ssb_device *dev, const struct ssb_device_id *id); void (*remove)(struct ssb_device *dev); int (*suspend)(struct ssb_device *dev, pm_message_t state); int (*resume)(struct ssb_device *dev); void (*shutdown)(struct ssb_device *dev); struct device_driver drv; }; #define drv_to_ssb_drv(_drv) container_of(_drv, struct ssb_driver, drv) extern int __ssb_driver_register(struct ssb_driver *drv, struct module *owner); #define ssb_driver_register(drv) \ __ssb_driver_register(drv, THIS_MODULE) extern void ssb_driver_unregister(struct ssb_driver *drv); enum ssb_bustype { SSB_BUSTYPE_SSB, /* This SSB bus is the system bus */ SSB_BUSTYPE_PCI, /* SSB is connected to PCI bus */ SSB_BUSTYPE_PCMCIA, /* SSB is connected to PCMCIA bus */ SSB_BUSTYPE_SDIO, /* SSB is connected to SDIO bus */ }; /* board_vendor */ #define SSB_BOARDVENDOR_BCM 0x14E4 /* Broadcom */ #define SSB_BOARDVENDOR_DELL 0x1028 /* Dell */ #define SSB_BOARDVENDOR_HP 0x0E11 /* HP */ /* board_type */ #define SSB_BOARD_BCM94306MP 0x0418 #define SSB_BOARD_BCM4309G 0x0421 #define SSB_BOARD_BCM4306CB 0x0417 #define SSB_BOARD_BCM4309MP 0x040C #define SSB_BOARD_MP4318 0x044A #define SSB_BOARD_BU4306 0x0416 #define SSB_BOARD_BU4309 0x040A /* chip_package */ #define SSB_CHIPPACK_BCM4712S 1 /* Small 200pin 4712 */ #define SSB_CHIPPACK_BCM4712M 2 /* Medium 225pin 4712 */ #define SSB_CHIPPACK_BCM4712L 0 /* Large 340pin 4712 */ #include #include #include #include struct ssb_bus { /* The MMIO area. */ void __iomem *mmio; const struct ssb_bus_ops *ops; /* The core currently mapped into the MMIO window. * Not valid on all host-buses. So don't use outside of SSB. */ struct ssb_device *mapped_device; union { /* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */ u8 mapped_pcmcia_seg; /* Current SSB base address window for SDIO. */ u32 sdio_sbaddr; }; /* Lock for core and segment switching. * On PCMCIA-host busses this is used to protect the whole MMIO access. */ spinlock_t bar_lock; /* The host-bus this backplane is running on. */ enum ssb_bustype bustype; /* Pointers to the host-bus. Check bustype before using any of these pointers. */ union { /* Pointer to the PCI bus (only valid if bustype == SSB_BUSTYPE_PCI). */ struct pci_dev *host_pci; /* Pointer to the PCMCIA device (only if bustype == SSB_BUSTYPE_PCMCIA). */ struct pcmcia_device *host_pcmcia; /* Pointer to the SDIO device (only if bustype == SSB_BUSTYPE_SDIO). */ struct sdio_func *host_sdio; }; /* See enum ssb_quirks */ unsigned int quirks; #ifdef CONFIG_SSB_SPROM /* Mutex to protect the SPROM writing. */ struct mutex sprom_mutex; #endif /* ID information about the Chip. */ u16 chip_id; u8 chip_rev; u16 sprom_offset; u16 sprom_size; /* number of words in sprom */ u8 chip_package; /* List of devices (cores) on the backplane. */ struct ssb_device devices[SSB_MAX_NR_CORES]; u8 nr_devices; /* Software ID number for this bus. */ unsigned int busnumber; /* The ChipCommon device (if available). */ struct ssb_chipcommon chipco; /* The PCI-core device (if available). */ struct ssb_pcicore pcicore; /* The MIPS-core device (if available). */ struct ssb_mipscore mipscore; /* The EXTif-core device (if available). */ struct ssb_extif extif; /* The following structure elements are not available in early * SSB initialization. Though, they are available for regular * registered drivers at any stage. So be careful when * using them in the ssb core code. */ /* ID information about the PCB. */ struct ssb_boardinfo boardinfo; /* Contents of the SPROM. */ struct ssb_sprom sprom; /* If the board has a cardbus slot, this is set to true. */ bool has_cardbus_slot; #ifdef CONFIG_SSB_EMBEDDED /* Lock for GPIO register access. */ spinlock_t gpio_lock; #endif /* EMBEDDED */ /* Internal-only stuff follows. Do not touch. */ struct list_head list; #ifdef CONFIG_SSB_DEBUG /* Is the bus already powered up? */ bool powered_up; int power_warn_count; #endif /* DEBUG */ }; enum ssb_quirks { /* SDIO connected card requires performing a read after writing a 32-bit value */ SSB_QUIRK_SDIO_READ_AFTER_WRITE32 = (1 << 0), }; /* The initialization-invariants. */ struct ssb_init_invariants { /* Versioning information about the PCB. */ struct ssb_boardinfo boardinfo; /* The SPROM information. That's either stored in an * EEPROM or NVRAM on the board. */ struct ssb_sprom sprom; /* If the board has a cardbus slot, this is set to true. */ bool has_cardbus_slot; }; /* Type of function to fetch the invariants. */ typedef int (*ssb_invariants_func_t)(struct ssb_bus *bus, struct ssb_init_invariants *iv); /* Register a SSB system bus. get_invariants() is called after the * basic system devices are initialized. * The invariants are usually fetched from some NVRAM. * Put the invariants into the struct pointed to by iv. */ extern int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr, ssb_invariants_func_t get_invariants); #ifdef CONFIG_SSB_PCIHOST extern int ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci); #endif /* CONFIG_SSB_PCIHOST */ #ifdef CONFIG_SSB_PCMCIAHOST extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus, struct pcmcia_device *pcmcia_dev, unsigned long baseaddr); #endif /* CONFIG_SSB_PCMCIAHOST */ #ifdef CONFIG_SSB_SDIOHOST extern int ssb_bus_sdiobus_register(struct ssb_bus *bus, struct sdio_func *sdio_func, unsigned int quirks); #endif /* CONFIG_SSB_SDIOHOST */ extern void ssb_bus_unregister(struct ssb_bus *bus); /* Does the device have an SPROM? */ extern bool ssb_is_sprom_available(struct ssb_bus *bus); /* Set a fallback SPROM. * See kdoc at the function definition for complete documentation. */ extern int ssb_arch_register_fallback_sprom( int (*sprom_callback)(struct ssb_bus *bus, struct ssb_sprom *out)); /* Suspend a SSB bus. * Call this from the parent bus suspend routine. */ extern int ssb_bus_suspend(struct ssb_bus *bus); /* Resume a SSB bus. * Call this from the parent bus resume routine. */ extern int ssb_bus_resume(struct ssb_bus *bus); extern u32 ssb_clockspeed(struct ssb_bus *bus); /* Is the device enabled in hardware? */ int ssb_device_is_enabled(struct ssb_device *dev); /* Enable a device and pass device-specific SSB_TMSLOW flags. * If no device-specific flags are available, use 0. */ void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags); /* Disable a device in hardware and pass SSB_TMSLOW flags (if any). */ void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags); /* Device MMIO register read/write functions. */ static inline u8 ssb_read8(struct ssb_device *dev, u16 offset) { return dev->ops->read8(dev, offset); } static inline u16 ssb_read16(struct ssb_device *dev, u16 offset) { return dev->ops->read16(dev, offset); } static inline u32 ssb_read32(struct ssb_device *dev, u16 offset) { return dev->ops->read32(dev, offset); } static inline void ssb_write8(struct ssb_device *dev, u16 offset, u8 value) { dev->ops->write8(dev, offset, value); } static inline void ssb_write16(struct ssb_device *dev, u16 offset, u16 value) { dev->ops->write16(dev, offset, value); } static inline void ssb_write32(struct ssb_device *dev, u16 offset, u32 value) { dev->ops->write32(dev, offset, value); } #ifdef CONFIG_SSB_BLOCKIO static inline void ssb_block_read(struct ssb_device *dev, void *buffer, size_t count, u16 offset, u8 reg_width) { dev->ops->block_read(dev, buffer, count, offset, reg_width); } static inline void ssb_block_write(struct ssb_device *dev, const void *buffer, size_t count, u16 offset, u8 reg_width) { dev->ops->block_write(dev, buffer, count, offset, reg_width); } #endif /* CONFIG_SSB_BLOCKIO */ /* The SSB DMA API. Use this API for any DMA operation on the device. * This API basically is a wrapper that calls the correct DMA API for * the host device type the SSB device is attached to. */ /* Translation (routing) bits that need to be ORed to DMA * addresses before they are given to a device. */ extern u32 ssb_dma_translation(struct ssb_device *dev); #define SSB_DMA_TRANSLATION_MASK 0xC0000000 #define SSB_DMA_TRANSLATION_SHIFT 30 static inline void __cold __ssb_dma_not_implemented(struct ssb_device *dev) { #ifdef CONFIG_SSB_DEBUG printk(KERN_ERR "SSB: BUG! Calling DMA API for " "unsupported bustype %d\n", dev->bus->bustype); #endif /* DEBUG */ } #ifdef CONFIG_SSB_PCIHOST /* PCI-host wrapper driver */ extern int ssb_pcihost_register(struct pci_driver *driver); static inline void ssb_pcihost_unregister(struct pci_driver *driver) { pci_unregister_driver(driver); } static inline void ssb_pcihost_set_power_state(struct ssb_device *sdev, pci_power_t state) { if (sdev->bus->bustype == SSB_BUSTYPE_PCI) pci_set_power_state(sdev->bus->host_pci, state); } #else static inline void ssb_pcihost_unregister(struct pci_driver *driver) { } static inline void ssb_pcihost_set_power_state(struct ssb_device *sdev, pci_power_t state) { } #endif /* CONFIG_SSB_PCIHOST */ /* If a driver is shutdown or suspended, call this to signal * that the bus may be completely powered down. SSB will decide, * if it's really time to power down the bus, based on if there * are other devices that want to run. */ extern int ssb_bus_may_powerdown(struct ssb_bus *bus); /* Before initializing and enabling a device, call this to power-up the bus. * If you want to allow use of dynamic-power-control, pass the flag. * Otherwise static always-on powercontrol will be used. */ extern int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl); extern void ssb_commit_settings(struct ssb_bus *bus); /* Various helper functions */ extern u32 ssb_admatch_base(u32 adm); extern u32 ssb_admatch_size(u32 adm); /* PCI device mapping and fixup routines. * Called from the architecture pcibios init code. * These are only available on SSB_EMBEDDED configurations. */ #ifdef CONFIG_SSB_EMBEDDED int ssb_pcibios_plat_dev_init(struct pci_dev *dev); int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); #endif /* CONFIG_SSB_EMBEDDED */ #endif /* LINUX_SSB_H_ */ compat-drivers-2012-09-18/include/linux/ssb/ssb_driver_chipcommon.h0000644000175000017500000007171312015204042024466 0ustar mcgrofmcgrof#ifndef LINUX_SSB_CHIPCO_H_ #define LINUX_SSB_CHIPCO_H_ /* SonicsSiliconBackplane CHIPCOMMON core hardware definitions * * The chipcommon core provides chip identification, SB control, * jtag, 0/1/2 uarts, clock frequency control, a watchdog interrupt timer, * gpio interface, extbus, and support for serial and parallel flashes. * * Copyright 2005, Broadcom Corporation * Copyright 2006, Michael Buesch * * Licensed under the GPL version 2. See COPYING for details. */ /** ChipCommon core registers. **/ #define SSB_CHIPCO_CHIPID 0x0000 #define SSB_CHIPCO_IDMASK 0x0000FFFF #define SSB_CHIPCO_REVMASK 0x000F0000 #define SSB_CHIPCO_REVSHIFT 16 #define SSB_CHIPCO_PACKMASK 0x00F00000 #define SSB_CHIPCO_PACKSHIFT 20 #define SSB_CHIPCO_NRCORESMASK 0x0F000000 #define SSB_CHIPCO_NRCORESSHIFT 24 #define SSB_CHIPCO_CAP 0x0004 /* Capabilities */ #define SSB_CHIPCO_CAP_NRUART 0x00000003 /* # of UARTs */ #define SSB_CHIPCO_CAP_MIPSEB 0x00000004 /* MIPS in BigEndian Mode */ #define SSB_CHIPCO_CAP_UARTCLK 0x00000018 /* UART clock select */ #define SSB_CHIPCO_CAP_UARTCLK_INT 0x00000008 /* UARTs are driven by internal divided clock */ #define SSB_CHIPCO_CAP_UARTGPIO 0x00000020 /* UARTs on GPIO 15-12 */ #define SSB_CHIPCO_CAP_EXTBUS 0x000000C0 /* External buses present */ #define SSB_CHIPCO_CAP_FLASHT 0x00000700 /* Flash Type */ #define SSB_CHIPCO_FLASHT_NONE 0x00000000 /* No flash */ #define SSB_CHIPCO_FLASHT_STSER 0x00000100 /* ST serial flash */ #define SSB_CHIPCO_FLASHT_ATSER 0x00000200 /* Atmel serial flash */ #define SSB_CHIPCO_FLASHT_PARA 0x00000700 /* Parallel flash */ #define SSB_CHIPCO_CAP_PLLT 0x00038000 /* PLL Type */ #define SSB_PLLTYPE_NONE 0x00000000 #define SSB_PLLTYPE_1 0x00010000 /* 48Mhz base, 3 dividers */ #define SSB_PLLTYPE_2 0x00020000 /* 48Mhz, 4 dividers */ #define SSB_PLLTYPE_3 0x00030000 /* 25Mhz, 2 dividers */ #define SSB_PLLTYPE_4 0x00008000 /* 48Mhz, 4 dividers */ #define SSB_PLLTYPE_5 0x00018000 /* 25Mhz, 4 dividers */ #define SSB_PLLTYPE_6 0x00028000 /* 100/200 or 120/240 only */ #define SSB_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */ #define SSB_CHIPCO_CAP_PCTL 0x00040000 /* Power Control */ #define SSB_CHIPCO_CAP_OTPS 0x00380000 /* OTP size */ #define SSB_CHIPCO_CAP_OTPS_SHIFT 19 #define SSB_CHIPCO_CAP_OTPS_BASE 5 #define SSB_CHIPCO_CAP_JTAGM 0x00400000 /* JTAG master present */ #define SSB_CHIPCO_CAP_BROM 0x00800000 /* Internal boot ROM active */ #define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */ #define SSB_CHIPCO_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */ #define SSB_CHIPCO_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */ #define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */ #define SSB_CHIPCO_CORECTL 0x0008 #define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */ #define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ #define SSB_CHIPCO_CORECTL_UARTCLKEN 0x00000008 /* UART clock enable (rev >= 21) */ #define SSB_CHIPCO_BIST 0x000C #define SSB_CHIPCO_OTPS 0x0010 /* OTP status */ #define SSB_CHIPCO_OTPS_PROGFAIL 0x80000000 #define SSB_CHIPCO_OTPS_PROTECT 0x00000007 #define SSB_CHIPCO_OTPS_HW_PROTECT 0x00000001 #define SSB_CHIPCO_OTPS_SW_PROTECT 0x00000002 #define SSB_CHIPCO_OTPS_CID_PROTECT 0x00000004 #define SSB_CHIPCO_OTPC 0x0014 /* OTP control */ #define SSB_CHIPCO_OTPC_RECWAIT 0xFF000000 #define SSB_CHIPCO_OTPC_PROGWAIT 0x00FFFF00 #define SSB_CHIPCO_OTPC_PRW_SHIFT 8 #define SSB_CHIPCO_OTPC_MAXFAIL 0x00000038 #define SSB_CHIPCO_OTPC_VSEL 0x00000006 #define SSB_CHIPCO_OTPC_SELVL 0x00000001 #define SSB_CHIPCO_OTPP 0x0018 /* OTP prog */ #define SSB_CHIPCO_OTPP_COL 0x000000FF #define SSB_CHIPCO_OTPP_ROW 0x0000FF00 #define SSB_CHIPCO_OTPP_ROW_SHIFT 8 #define SSB_CHIPCO_OTPP_READERR 0x10000000 #define SSB_CHIPCO_OTPP_VALUE 0x20000000 #define SSB_CHIPCO_OTPP_READ 0x40000000 #define SSB_CHIPCO_OTPP_START 0x80000000 #define SSB_CHIPCO_OTPP_BUSY 0x80000000 #define SSB_CHIPCO_IRQSTAT 0x0020 #define SSB_CHIPCO_IRQMASK 0x0024 #define SSB_CHIPCO_IRQ_GPIO 0x00000001 /* gpio intr */ #define SSB_CHIPCO_IRQ_EXT 0x00000002 /* ro: ext intr pin (corerev >= 3) */ #define SSB_CHIPCO_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ #define SSB_CHIPCO_CHIPCTL 0x0028 /* Rev >= 11 only */ #define SSB_CHIPCO_CHIPSTAT 0x002C /* Rev >= 11 only */ #define SSB_CHIPCO_JCMD 0x0030 /* Rev >= 10 only */ #define SSB_CHIPCO_JCMD_START 0x80000000 #define SSB_CHIPCO_JCMD_BUSY 0x80000000 #define SSB_CHIPCO_JCMD_PAUSE 0x40000000 #define SSB_CHIPCO_JCMD0_ACC_MASK 0x0000F000 #define SSB_CHIPCO_JCMD0_ACC_IRDR 0x00000000 #define SSB_CHIPCO_JCMD0_ACC_DR 0x00001000 #define SSB_CHIPCO_JCMD0_ACC_IR 0x00002000 #define SSB_CHIPCO_JCMD0_ACC_RESET 0x00003000 #define SSB_CHIPCO_JCMD0_ACC_IRPDR 0x00004000 #define SSB_CHIPCO_JCMD0_ACC_PDR 0x00005000 #define SSB_CHIPCO_JCMD0_IRW_MASK 0x00000F00 #define SSB_CHIPCO_JCMD_ACC_MASK 0x000F0000 /* Changes for corerev 11 */ #define SSB_CHIPCO_JCMD_ACC_IRDR 0x00000000 #define SSB_CHIPCO_JCMD_ACC_DR 0x00010000 #define SSB_CHIPCO_JCMD_ACC_IR 0x00020000 #define SSB_CHIPCO_JCMD_ACC_RESET 0x00030000 #define SSB_CHIPCO_JCMD_ACC_IRPDR 0x00040000 #define SSB_CHIPCO_JCMD_ACC_PDR 0x00050000 #define SSB_CHIPCO_JCMD_IRW_MASK 0x00001F00 #define SSB_CHIPCO_JCMD_IRW_SHIFT 8 #define SSB_CHIPCO_JCMD_DRW_MASK 0x0000003F #define SSB_CHIPCO_JIR 0x0034 /* Rev >= 10 only */ #define SSB_CHIPCO_JDR 0x0038 /* Rev >= 10 only */ #define SSB_CHIPCO_JCTL 0x003C /* Rev >= 10 only */ #define SSB_CHIPCO_JCTL_FORCE_CLK 4 /* Force clock */ #define SSB_CHIPCO_JCTL_EXT_EN 2 /* Enable external targets */ #define SSB_CHIPCO_JCTL_EN 1 /* Enable Jtag master */ #define SSB_CHIPCO_FLASHCTL 0x0040 #define SSB_CHIPCO_FLASHCTL_START 0x80000000 #define SSB_CHIPCO_FLASHCTL_BUSY SSB_CHIPCO_FLASHCTL_START #define SSB_CHIPCO_FLASHADDR 0x0044 #define SSB_CHIPCO_FLASHDATA 0x0048 #define SSB_CHIPCO_BCAST_ADDR 0x0050 #define SSB_CHIPCO_BCAST_DATA 0x0054 #define SSB_CHIPCO_GPIOPULLUP 0x0058 /* Rev >= 20 only */ #define SSB_CHIPCO_GPIOPULLDOWN 0x005C /* Rev >= 20 only */ #define SSB_CHIPCO_GPIOIN 0x0060 #define SSB_CHIPCO_GPIOOUT 0x0064 #define SSB_CHIPCO_GPIOOUTEN 0x0068 #define SSB_CHIPCO_GPIOCTL 0x006C #define SSB_CHIPCO_GPIOPOL 0x0070 #define SSB_CHIPCO_GPIOIRQ 0x0074 #define SSB_CHIPCO_WATCHDOG 0x0080 #define SSB_CHIPCO_GPIOTIMER 0x0088 /* LED powersave (corerev >= 16) */ #define SSB_CHIPCO_GPIOTIMER_OFFTIME 0x0000FFFF #define SSB_CHIPCO_GPIOTIMER_OFFTIME_SHIFT 0 #define SSB_CHIPCO_GPIOTIMER_ONTIME 0xFFFF0000 #define SSB_CHIPCO_GPIOTIMER_ONTIME_SHIFT 16 #define SSB_CHIPCO_GPIOTOUTM 0x008C /* LED powersave (corerev >= 16) */ #define SSB_CHIPCO_CLOCK_N 0x0090 #define SSB_CHIPCO_CLOCK_SB 0x0094 #define SSB_CHIPCO_CLOCK_PCI 0x0098 #define SSB_CHIPCO_CLOCK_M2 0x009C #define SSB_CHIPCO_CLOCK_MIPS 0x00A0 #define SSB_CHIPCO_CLKDIV 0x00A4 /* Rev >= 3 only */ #define SSB_CHIPCO_CLKDIV_SFLASH 0x0F000000 #define SSB_CHIPCO_CLKDIV_SFLASH_SHIFT 24 #define SSB_CHIPCO_CLKDIV_OTP 0x000F0000 #define SSB_CHIPCO_CLKDIV_OTP_SHIFT 16 #define SSB_CHIPCO_CLKDIV_JTAG 0x00000F00 #define SSB_CHIPCO_CLKDIV_JTAG_SHIFT 8 #define SSB_CHIPCO_CLKDIV_UART 0x000000FF #define SSB_CHIPCO_PLLONDELAY 0x00B0 /* Rev >= 4 only */ #define SSB_CHIPCO_FREFSELDELAY 0x00B4 /* Rev >= 4 only */ #define SSB_CHIPCO_SLOWCLKCTL 0x00B8 /* 6 <= Rev <= 9 only */ #define SSB_CHIPCO_SLOWCLKCTL_SRC 0x00000007 /* slow clock source mask */ #define SSB_CHIPCO_SLOWCLKCTL_SRC_LPO 0x00000000 /* source of slow clock is LPO */ #define SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL 0x00000001 /* source of slow clock is crystal */ #define SSB_CHIPCO_SLOECLKCTL_SRC_PCI 0x00000002 /* source of slow clock is PCI */ #define SSB_CHIPCO_SLOWCLKCTL_LPOFREQ 0x00000200 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ #define SSB_CHIPCO_SLOWCLKCTL_LPOPD 0x00000400 /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ #define SSB_CHIPCO_SLOWCLKCTL_FSLOW 0x00000800 /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ #define SSB_CHIPCO_SLOWCLKCTL_IPLL 0x00001000 /* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */ #define SSB_CHIPCO_SLOWCLKCTL_ENXTAL 0x00002000 /* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */ #define SSB_CHIPCO_SLOWCLKCTL_XTALPU 0x00004000 /* XtalPU (RO), 1/0: crystal running/disabled */ #define SSB_CHIPCO_SLOWCLKCTL_CLKDIV 0xFFFF0000 /* ClockDivider (SlowClk = 1/(4+divisor)) */ #define SSB_CHIPCO_SLOWCLKCTL_CLKDIV_SHIFT 16 #define SSB_CHIPCO_SYSCLKCTL 0x00C0 /* Rev >= 3 only */ #define SSB_CHIPCO_SYSCLKCTL_IDLPEN 0x00000001 /* ILPen: Enable Idle Low Power */ #define SSB_CHIPCO_SYSCLKCTL_ALPEN 0x00000002 /* ALPen: Enable Active Low Power */ #define SSB_CHIPCO_SYSCLKCTL_PLLEN 0x00000004 /* ForcePLLOn */ #define SSB_CHIPCO_SYSCLKCTL_FORCEALP 0x00000008 /* Force ALP (or HT if ALPen is not set */ #define SSB_CHIPCO_SYSCLKCTL_FORCEHT 0x00000010 /* Force HT */ #define SSB_CHIPCO_SYSCLKCTL_CLKDIV 0xFFFF0000 /* ClkDiv (ILP = 1/(4+divisor)) */ #define SSB_CHIPCO_SYSCLKCTL_CLKDIV_SHIFT 16 #define SSB_CHIPCO_CLKSTSTR 0x00C4 /* Rev >= 3 only */ #define SSB_CHIPCO_PCMCIA_CFG 0x0100 #define SSB_CHIPCO_PCMCIA_MEMWAIT 0x0104 #define SSB_CHIPCO_PCMCIA_ATTRWAIT 0x0108 #define SSB_CHIPCO_PCMCIA_IOWAIT 0x010C #define SSB_CHIPCO_IDE_CFG 0x0110 #define SSB_CHIPCO_IDE_MEMWAIT 0x0114 #define SSB_CHIPCO_IDE_ATTRWAIT 0x0118 #define SSB_CHIPCO_IDE_IOWAIT 0x011C #define SSB_CHIPCO_PROG_CFG 0x0120 #define SSB_CHIPCO_PROG_WAITCNT 0x0124 #define SSB_CHIPCO_FLASH_CFG 0x0128 #define SSB_CHIPCO_FLASH_WAITCNT 0x012C #define SSB_CHIPCO_CLKCTLST 0x01E0 /* Clock control and status (rev >= 20) */ #define SSB_CHIPCO_CLKCTLST_FORCEALP 0x00000001 /* Force ALP request */ #define SSB_CHIPCO_CLKCTLST_FORCEHT 0x00000002 /* Force HT request */ #define SSB_CHIPCO_CLKCTLST_FORCEILP 0x00000004 /* Force ILP request */ #define SSB_CHIPCO_CLKCTLST_HAVEALPREQ 0x00000008 /* ALP available request */ #define SSB_CHIPCO_CLKCTLST_HAVEHTREQ 0x00000010 /* HT available request */ #define SSB_CHIPCO_CLKCTLST_HWCROFF 0x00000020 /* Force HW clock request off */ #define SSB_CHIPCO_CLKCTLST_HAVEALP 0x00010000 /* ALP available */ #define SSB_CHIPCO_CLKCTLST_HAVEHT 0x00020000 /* HT available */ #define SSB_CHIPCO_CLKCTLST_4328A0_HAVEHT 0x00010000 /* 4328a0 has reversed bits */ #define SSB_CHIPCO_CLKCTLST_4328A0_HAVEALP 0x00020000 /* 4328a0 has reversed bits */ #define SSB_CHIPCO_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */ #define SSB_CHIPCO_UART0_DATA 0x0300 #define SSB_CHIPCO_UART0_IMR 0x0304 #define SSB_CHIPCO_UART0_FCR 0x0308 #define SSB_CHIPCO_UART0_LCR 0x030C #define SSB_CHIPCO_UART0_MCR 0x0310 #define SSB_CHIPCO_UART0_LSR 0x0314 #define SSB_CHIPCO_UART0_MSR 0x0318 #define SSB_CHIPCO_UART0_SCRATCH 0x031C #define SSB_CHIPCO_UART1_DATA 0x0400 #define SSB_CHIPCO_UART1_IMR 0x0404 #define SSB_CHIPCO_UART1_FCR 0x0408 #define SSB_CHIPCO_UART1_LCR 0x040C #define SSB_CHIPCO_UART1_MCR 0x0410 #define SSB_CHIPCO_UART1_LSR 0x0414 #define SSB_CHIPCO_UART1_MSR 0x0418 #define SSB_CHIPCO_UART1_SCRATCH 0x041C /* PMU registers (rev >= 20) */ #define SSB_CHIPCO_PMU_CTL 0x0600 /* PMU control */ #define SSB_CHIPCO_PMU_CTL_ILP_DIV 0xFFFF0000 /* ILP div mask */ #define SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT 16 #define SSB_CHIPCO_PMU_CTL_NOILPONW 0x00000200 /* No ILP on wait */ #define SSB_CHIPCO_PMU_CTL_HTREQEN 0x00000100 /* HT req enable */ #define SSB_CHIPCO_PMU_CTL_ALPREQEN 0x00000080 /* ALP req enable */ #define SSB_CHIPCO_PMU_CTL_XTALFREQ 0x0000007C /* Crystal freq */ #define SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT 2 #define SSB_CHIPCO_PMU_CTL_ILPDIVEN 0x00000002 /* ILP div enable */ #define SSB_CHIPCO_PMU_CTL_LPOSEL 0x00000001 /* LPO sel */ #define SSB_CHIPCO_PMU_CAP 0x0604 /* PMU capabilities */ #define SSB_CHIPCO_PMU_CAP_REVISION 0x000000FF /* Revision mask */ #define SSB_CHIPCO_PMU_STAT 0x0608 /* PMU status */ #define SSB_CHIPCO_PMU_STAT_INTPEND 0x00000040 /* Interrupt pending */ #define SSB_CHIPCO_PMU_STAT_SBCLKST 0x00000030 /* Backplane clock status? */ #define SSB_CHIPCO_PMU_STAT_HAVEALP 0x00000008 /* ALP available */ #define SSB_CHIPCO_PMU_STAT_HAVEHT 0x00000004 /* HT available */ #define SSB_CHIPCO_PMU_STAT_RESINIT 0x00000003 /* Res init */ #define SSB_CHIPCO_PMU_RES_STAT 0x060C /* PMU res status */ #define SSB_CHIPCO_PMU_RES_PEND 0x0610 /* PMU res pending */ #define SSB_CHIPCO_PMU_TIMER 0x0614 /* PMU timer */ #define SSB_CHIPCO_PMU_MINRES_MSK 0x0618 /* PMU min res mask */ #define SSB_CHIPCO_PMU_MAXRES_MSK 0x061C /* PMU max res mask */ #define SSB_CHIPCO_PMU_RES_TABSEL 0x0620 /* PMU res table sel */ #define SSB_CHIPCO_PMU_RES_DEPMSK 0x0624 /* PMU res dep mask */ #define SSB_CHIPCO_PMU_RES_UPDNTM 0x0628 /* PMU res updown timer */ #define SSB_CHIPCO_PMU_RES_TIMER 0x062C /* PMU res timer */ #define SSB_CHIPCO_PMU_CLKSTRETCH 0x0630 /* PMU clockstretch */ #define SSB_CHIPCO_PMU_WATCHDOG 0x0634 /* PMU watchdog */ #define SSB_CHIPCO_PMU_RES_REQTS 0x0640 /* PMU res req timer sel */ #define SSB_CHIPCO_PMU_RES_REQT 0x0644 /* PMU res req timer */ #define SSB_CHIPCO_PMU_RES_REQM 0x0648 /* PMU res req mask */ #define SSB_CHIPCO_CHIPCTL_ADDR 0x0650 #define SSB_CHIPCO_CHIPCTL_DATA 0x0654 #define SSB_CHIPCO_REGCTL_ADDR 0x0658 #define SSB_CHIPCO_REGCTL_DATA 0x065C #define SSB_CHIPCO_PLLCTL_ADDR 0x0660 #define SSB_CHIPCO_PLLCTL_DATA 0x0664 /** PMU PLL registers */ /* PMU rev 0 PLL registers */ #define SSB_PMU0_PLLCTL0 0 #define SSB_PMU0_PLLCTL0_PDIV_MSK 0x00000001 #define SSB_PMU0_PLLCTL0_PDIV_FREQ 25000 /* kHz */ #define SSB_PMU0_PLLCTL1 1 #define SSB_PMU0_PLLCTL1_WILD_IMSK 0xF0000000 /* Wild int mask (low nibble) */ #define SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT 28 #define SSB_PMU0_PLLCTL1_WILD_FMSK 0x0FFFFF00 /* Wild frac mask */ #define SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT 8 #define SSB_PMU0_PLLCTL1_STOPMOD 0x00000040 /* Stop mod */ #define SSB_PMU0_PLLCTL2 2 #define SSB_PMU0_PLLCTL2_WILD_IMSKHI 0x0000000F /* Wild int mask (high nibble) */ #define SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT 0 /* PMU rev 1 PLL registers */ #define SSB_PMU1_PLLCTL0 0 #define SSB_PMU1_PLLCTL0_P1DIV 0x00F00000 /* P1 div */ #define SSB_PMU1_PLLCTL0_P1DIV_SHIFT 20 #define SSB_PMU1_PLLCTL0_P2DIV 0x0F000000 /* P2 div */ #define SSB_PMU1_PLLCTL0_P2DIV_SHIFT 24 #define SSB_PMU1_PLLCTL1 1 #define SSB_PMU1_PLLCTL1_M1DIV 0x000000FF /* M1 div */ #define SSB_PMU1_PLLCTL1_M1DIV_SHIFT 0 #define SSB_PMU1_PLLCTL1_M2DIV 0x0000FF00 /* M2 div */ #define SSB_PMU1_PLLCTL1_M2DIV_SHIFT 8 #define SSB_PMU1_PLLCTL1_M3DIV 0x00FF0000 /* M3 div */ #define SSB_PMU1_PLLCTL1_M3DIV_SHIFT 16 #define SSB_PMU1_PLLCTL1_M4DIV 0xFF000000 /* M4 div */ #define SSB_PMU1_PLLCTL1_M4DIV_SHIFT 24 #define SSB_PMU1_PLLCTL2 2 #define SSB_PMU1_PLLCTL2_M5DIV 0x000000FF /* M5 div */ #define SSB_PMU1_PLLCTL2_M5DIV_SHIFT 0 #define SSB_PMU1_PLLCTL2_M6DIV 0x0000FF00 /* M6 div */ #define SSB_PMU1_PLLCTL2_M6DIV_SHIFT 8 #define SSB_PMU1_PLLCTL2_NDIVMODE 0x000E0000 /* NDIV mode */ #define SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT 17 #define SSB_PMU1_PLLCTL2_NDIVINT 0x1FF00000 /* NDIV int */ #define SSB_PMU1_PLLCTL2_NDIVINT_SHIFT 20 #define SSB_PMU1_PLLCTL3 3 #define SSB_PMU1_PLLCTL3_NDIVFRAC 0x00FFFFFF /* NDIV frac */ #define SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT 0 #define SSB_PMU1_PLLCTL4 4 #define SSB_PMU1_PLLCTL5 5 #define SSB_PMU1_PLLCTL5_CLKDRV 0xFFFFFF00 /* clk drv */ #define SSB_PMU1_PLLCTL5_CLKDRV_SHIFT 8 /* BCM4312 PLL resource numbers. */ #define SSB_PMURES_4312_SWITCHER_BURST 0 #define SSB_PMURES_4312_SWITCHER_PWM 1 #define SSB_PMURES_4312_PA_REF_LDO 2 #define SSB_PMURES_4312_CORE_LDO_BURST 3 #define SSB_PMURES_4312_CORE_LDO_PWM 4 #define SSB_PMURES_4312_RADIO_LDO 5 #define SSB_PMURES_4312_ILP_REQUEST 6 #define SSB_PMURES_4312_BG_FILTBYP 7 #define SSB_PMURES_4312_TX_FILTBYP 8 #define SSB_PMURES_4312_RX_FILTBYP 9 #define SSB_PMURES_4312_XTAL_PU 10 #define SSB_PMURES_4312_ALP_AVAIL 11 #define SSB_PMURES_4312_BB_PLL_FILTBYP 12 #define SSB_PMURES_4312_RF_PLL_FILTBYP 13 #define SSB_PMURES_4312_HT_AVAIL 14 /* BCM4325 PLL resource numbers. */ #define SSB_PMURES_4325_BUCK_BOOST_BURST 0 #define SSB_PMURES_4325_CBUCK_BURST 1 #define SSB_PMURES_4325_CBUCK_PWM 2 #define SSB_PMURES_4325_CLDO_CBUCK_BURST 3 #define SSB_PMURES_4325_CLDO_CBUCK_PWM 4 #define SSB_PMURES_4325_BUCK_BOOST_PWM 5 #define SSB_PMURES_4325_ILP_REQUEST 6 #define SSB_PMURES_4325_ABUCK_BURST 7 #define SSB_PMURES_4325_ABUCK_PWM 8 #define SSB_PMURES_4325_LNLDO1_PU 9 #define SSB_PMURES_4325_LNLDO2_PU 10 #define SSB_PMURES_4325_LNLDO3_PU 11 #define SSB_PMURES_4325_LNLDO4_PU 12 #define SSB_PMURES_4325_XTAL_PU 13 #define SSB_PMURES_4325_ALP_AVAIL 14 #define SSB_PMURES_4325_RX_PWRSW_PU 15 #define SSB_PMURES_4325_TX_PWRSW_PU 16 #define SSB_PMURES_4325_RFPLL_PWRSW_PU 17 #define SSB_PMURES_4325_LOGEN_PWRSW_PU 18 #define SSB_PMURES_4325_AFE_PWRSW_PU 19 #define SSB_PMURES_4325_BBPLL_PWRSW_PU 20 #define SSB_PMURES_4325_HT_AVAIL 21 /* BCM4328 PLL resource numbers. */ #define SSB_PMURES_4328_EXT_SWITCHER_PWM 0 #define SSB_PMURES_4328_BB_SWITCHER_PWM 1 #define SSB_PMURES_4328_BB_SWITCHER_BURST 2 #define SSB_PMURES_4328_BB_EXT_SWITCHER_BURST 3 #define SSB_PMURES_4328_ILP_REQUEST 4 #define SSB_PMURES_4328_RADIO_SWITCHER_PWM 5 #define SSB_PMURES_4328_RADIO_SWITCHER_BURST 6 #define SSB_PMURES_4328_ROM_SWITCH 7 #define SSB_PMURES_4328_PA_REF_LDO 8 #define SSB_PMURES_4328_RADIO_LDO 9 #define SSB_PMURES_4328_AFE_LDO 10 #define SSB_PMURES_4328_PLL_LDO 11 #define SSB_PMURES_4328_BG_FILTBYP 12 #define SSB_PMURES_4328_TX_FILTBYP 13 #define SSB_PMURES_4328_RX_FILTBYP 14 #define SSB_PMURES_4328_XTAL_PU 15 #define SSB_PMURES_4328_XTAL_EN 16 #define SSB_PMURES_4328_BB_PLL_FILTBYP 17 #define SSB_PMURES_4328_RF_PLL_FILTBYP 18 #define SSB_PMURES_4328_BB_PLL_PU 19 /* BCM5354 PLL resource numbers. */ #define SSB_PMURES_5354_EXT_SWITCHER_PWM 0 #define SSB_PMURES_5354_BB_SWITCHER_PWM 1 #define SSB_PMURES_5354_BB_SWITCHER_BURST 2 #define SSB_PMURES_5354_BB_EXT_SWITCHER_BURST 3 #define SSB_PMURES_5354_ILP_REQUEST 4 #define SSB_PMURES_5354_RADIO_SWITCHER_PWM 5 #define SSB_PMURES_5354_RADIO_SWITCHER_BURST 6 #define SSB_PMURES_5354_ROM_SWITCH 7 #define SSB_PMURES_5354_PA_REF_LDO 8 #define SSB_PMURES_5354_RADIO_LDO 9 #define SSB_PMURES_5354_AFE_LDO 10 #define SSB_PMURES_5354_PLL_LDO 11 #define SSB_PMURES_5354_BG_FILTBYP 12 #define SSB_PMURES_5354_TX_FILTBYP 13 #define SSB_PMURES_5354_RX_FILTBYP 14 #define SSB_PMURES_5354_XTAL_PU 15 #define SSB_PMURES_5354_XTAL_EN 16 #define SSB_PMURES_5354_BB_PLL_FILTBYP 17 #define SSB_PMURES_5354_RF_PLL_FILTBYP 18 #define SSB_PMURES_5354_BB_PLL_PU 19 /** Chip specific Chip-Status register contents. */ #define SSB_CHIPCO_CHST_4322_SPROM_EXISTS 0x00000040 /* SPROM present */ #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003 #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */ #define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */ #define SSB_CHIPCO_CHST_4325_OTP_SEL 2 /* OTP is powered up, no SPROM */ #define SSB_CHIPCO_CHST_4325_OTP_PWRDN 3 /* OTP is powered down, SPROM is present */ #define SSB_CHIPCO_CHST_4325_SDIO_USB_MODE 0x00000004 #define SSB_CHIPCO_CHST_4325_SDIO_USB_MODE_SHIFT 2 #define SSB_CHIPCO_CHST_4325_RCAL_VALID 0x00000008 #define SSB_CHIPCO_CHST_4325_RCAL_VALID_SHIFT 3 #define SSB_CHIPCO_CHST_4325_RCAL_VALUE 0x000001F0 #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4 #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */ /** Macros to determine SPROM presence based on Chip-Status register. */ #define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ SSB_CHIPCO_CHST_4325_OTP_SEL) #define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \ (status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS) #define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \ (((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ SSB_CHIPCO_CHST_4325_OTP_SEL)) /** Clockcontrol masks and values **/ /* SSB_CHIPCO_CLOCK_N */ #define SSB_CHIPCO_CLK_N1 0x0000003F /* n1 control */ #define SSB_CHIPCO_CLK_N2 0x00003F00 /* n2 control */ #define SSB_CHIPCO_CLK_N2_SHIFT 8 #define SSB_CHIPCO_CLK_PLLC 0x000F0000 /* pll control */ #define SSB_CHIPCO_CLK_PLLC_SHIFT 16 /* SSB_CHIPCO_CLOCK_SB/PCI/UART */ #define SSB_CHIPCO_CLK_M1 0x0000003F /* m1 control */ #define SSB_CHIPCO_CLK_M2 0x00003F00 /* m2 control */ #define SSB_CHIPCO_CLK_M2_SHIFT 8 #define SSB_CHIPCO_CLK_M3 0x003F0000 /* m3 control */ #define SSB_CHIPCO_CLK_M3_SHIFT 16 #define SSB_CHIPCO_CLK_MC 0x1F000000 /* mux control */ #define SSB_CHIPCO_CLK_MC_SHIFT 24 /* N3M Clock control magic field values */ #define SSB_CHIPCO_CLK_F6_2 0x02 /* A factor of 2 in */ #define SSB_CHIPCO_CLK_F6_3 0x03 /* 6-bit fields like */ #define SSB_CHIPCO_CLK_F6_4 0x05 /* N1, M1 or M3 */ #define SSB_CHIPCO_CLK_F6_5 0x09 #define SSB_CHIPCO_CLK_F6_6 0x11 #define SSB_CHIPCO_CLK_F6_7 0x21 #define SSB_CHIPCO_CLK_F5_BIAS 5 /* 5-bit fields get this added */ #define SSB_CHIPCO_CLK_MC_BYPASS 0x08 #define SSB_CHIPCO_CLK_MC_M1 0x04 #define SSB_CHIPCO_CLK_MC_M1M2 0x02 #define SSB_CHIPCO_CLK_MC_M1M2M3 0x01 #define SSB_CHIPCO_CLK_MC_M1M3 0x11 /* Type 2 Clock control magic field values */ #define SSB_CHIPCO_CLK_T2_BIAS 2 /* n1, n2, m1 & m3 bias */ #define SSB_CHIPCO_CLK_T2M2_BIAS 3 /* m2 bias */ #define SSB_CHIPCO_CLK_T2MC_M1BYP 1 #define SSB_CHIPCO_CLK_T2MC_M2BYP 2 #define SSB_CHIPCO_CLK_T2MC_M3BYP 4 /* Type 6 Clock control magic field values */ #define SSB_CHIPCO_CLK_T6_MMASK 1 /* bits of interest in m */ #define SSB_CHIPCO_CLK_T6_M0 120000000 /* sb clock for m = 0 */ #define SSB_CHIPCO_CLK_T6_M1 100000000 /* sb clock for m = 1 */ #define SSB_CHIPCO_CLK_SB2MIPS_T6(sb) (2 * (sb)) /* Common clock base */ #define SSB_CHIPCO_CLK_BASE1 24000000 /* Half the clock freq */ #define SSB_CHIPCO_CLK_BASE2 12500000 /* Alternate crystal on some PLL's */ /* Clock control values for 200Mhz in 5350 */ #define SSB_CHIPCO_CLK_5350_N 0x0311 #define SSB_CHIPCO_CLK_5350_M 0x04020009 /** Bits in the config registers **/ #define SSB_CHIPCO_CFG_EN 0x0001 /* Enable */ #define SSB_CHIPCO_CFG_EXTM 0x000E /* Extif Mode */ #define SSB_CHIPCO_CFG_EXTM_ASYNC 0x0002 /* Async/Parallel flash */ #define SSB_CHIPCO_CFG_EXTM_SYNC 0x0004 /* Synchronous */ #define SSB_CHIPCO_CFG_EXTM_PCMCIA 0x0008 /* PCMCIA */ #define SSB_CHIPCO_CFG_EXTM_IDE 0x000A /* IDE */ #define SSB_CHIPCO_CFG_DS16 0x0010 /* Data size, 0=8bit, 1=16bit */ #define SSB_CHIPCO_CFG_CLKDIV 0x0060 /* Sync: Clock divisor */ #define SSB_CHIPCO_CFG_CLKEN 0x0080 /* Sync: Clock enable */ #define SSB_CHIPCO_CFG_BSTRO 0x0100 /* Sync: Size/Bytestrobe */ /** Flash-specific control/status values */ /* flashcontrol opcodes for ST flashes */ #define SSB_CHIPCO_FLASHCTL_ST_WREN 0x0006 /* Write Enable */ #define SSB_CHIPCO_FLASHCTL_ST_WRDIS 0x0004 /* Write Disable */ #define SSB_CHIPCO_FLASHCTL_ST_RDSR 0x0105 /* Read Status Register */ #define SSB_CHIPCO_FLASHCTL_ST_WRSR 0x0101 /* Write Status Register */ #define SSB_CHIPCO_FLASHCTL_ST_READ 0x0303 /* Read Data Bytes */ #define SSB_CHIPCO_FLASHCTL_ST_PP 0x0302 /* Page Program */ #define SSB_CHIPCO_FLASHCTL_ST_SE 0x02D8 /* Sector Erase */ #define SSB_CHIPCO_FLASHCTL_ST_BE 0x00C7 /* Bulk Erase */ #define SSB_CHIPCO_FLASHCTL_ST_DP 0x00B9 /* Deep Power-down */ #define SSB_CHIPCO_FLASHCTL_ST_RES 0x03AB /* Read Electronic Signature */ #define SSB_CHIPCO_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */ #define SSB_CHIPCO_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */ /* Status register bits for ST flashes */ #define SSB_CHIPCO_FLASHSTA_ST_WIP 0x01 /* Write In Progress */ #define SSB_CHIPCO_FLASHSTA_ST_WEL 0x02 /* Write Enable Latch */ #define SSB_CHIPCO_FLASHSTA_ST_BP 0x1C /* Block Protect */ #define SSB_CHIPCO_FLASHSTA_ST_BP_SHIFT 2 #define SSB_CHIPCO_FLASHSTA_ST_SRWD 0x80 /* Status Register Write Disable */ /* flashcontrol opcodes for Atmel flashes */ #define SSB_CHIPCO_FLASHCTL_AT_READ 0x07E8 #define SSB_CHIPCO_FLASHCTL_AT_PAGE_READ 0x07D2 #define SSB_CHIPCO_FLASHCTL_AT_BUF1_READ /* FIXME */ #define SSB_CHIPCO_FLASHCTL_AT_BUF2_READ /* FIXME */ #define SSB_CHIPCO_FLASHCTL_AT_STATUS 0x01D7 #define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE 0x0384 #define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRITE 0x0387 #define SSB_CHIPCO_FLASHCTL_AT_BUF1_ERASE_PRGM 0x0283 /* Erase program */ #define SSB_CHIPCO_FLASHCTL_AT_BUF2_ERASE_PRGM 0x0286 /* Erase program */ #define SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM 0x0288 #define SSB_CHIPCO_FLASHCTL_AT_BUF2_PROGRAM 0x0289 #define SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE 0x0281 #define SSB_CHIPCO_FLASHCTL_AT_BLOCK_ERASE 0x0250 #define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRER_PRGM 0x0382 /* Write erase program */ #define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRER_PRGM 0x0385 /* Write erase program */ #define SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD 0x0253 #define SSB_CHIPCO_FLASHCTL_AT_BUF2_LOAD 0x0255 #define SSB_CHIPCO_FLASHCTL_AT_BUF1_COMPARE 0x0260 #define SSB_CHIPCO_FLASHCTL_AT_BUF2_COMPARE 0x0261 #define SSB_CHIPCO_FLASHCTL_AT_BUF1_REPROGRAM 0x0258 #define SSB_CHIPCO_FLASHCTL_AT_BUF2_REPROGRAM 0x0259 /* Status register bits for Atmel flashes */ #define SSB_CHIPCO_FLASHSTA_AT_READY 0x80 #define SSB_CHIPCO_FLASHSTA_AT_MISMATCH 0x40 #define SSB_CHIPCO_FLASHSTA_AT_ID 0x38 #define SSB_CHIPCO_FLASHSTA_AT_ID_SHIFT 3 /** OTP **/ /* OTP regions */ #define SSB_CHIPCO_OTP_HW_REGION SSB_CHIPCO_OTPS_HW_PROTECT #define SSB_CHIPCO_OTP_SW_REGION SSB_CHIPCO_OTPS_SW_PROTECT #define SSB_CHIPCO_OTP_CID_REGION SSB_CHIPCO_OTPS_CID_PROTECT /* OTP regions (Byte offsets from otp size) */ #define SSB_CHIPCO_OTP_SWLIM_OFF (-8) #define SSB_CHIPCO_OTP_CIDBASE_OFF 0 #define SSB_CHIPCO_OTP_CIDLIM_OFF 8 /* Predefined OTP words (Word offset from otp size) */ #define SSB_CHIPCO_OTP_BOUNDARY_OFF (-4) #define SSB_CHIPCO_OTP_HWSIGN_OFF (-3) #define SSB_CHIPCO_OTP_SWSIGN_OFF (-2) #define SSB_CHIPCO_OTP_CIDSIGN_OFF (-1) #define SSB_CHIPCO_OTP_CID_OFF 0 #define SSB_CHIPCO_OTP_PKG_OFF 1 #define SSB_CHIPCO_OTP_FID_OFF 2 #define SSB_CHIPCO_OTP_RSV_OFF 3 #define SSB_CHIPCO_OTP_LIM_OFF 4 #define SSB_CHIPCO_OTP_SIGNATURE 0x578A #define SSB_CHIPCO_OTP_MAGIC 0x4E56 struct ssb_device; struct ssb_serial_port; /* Data for the PMU, if available. * Check availability with ((struct ssb_chipcommon)->capabilities & SSB_CHIPCO_CAP_PMU) */ struct ssb_chipcommon_pmu { u8 rev; /* PMU revision */ u32 crystalfreq; /* The active crystal frequency (in kHz) */ }; struct ssb_chipcommon { struct ssb_device *dev; u32 capabilities; u32 status; /* Fast Powerup Delay constant */ u16 fast_pwrup_delay; struct ssb_chipcommon_pmu pmu; }; static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) { return (cc->dev != NULL); } /* Register access */ #define chipco_read32(cc, offset) ssb_read32((cc)->dev, offset) #define chipco_write32(cc, offset, val) ssb_write32((cc)->dev, offset, val) #define chipco_mask32(cc, offset, mask) \ chipco_write32(cc, offset, chipco_read32(cc, offset) & (mask)) #define chipco_set32(cc, offset, set) \ chipco_write32(cc, offset, chipco_read32(cc, offset) | (set)) #define chipco_maskset32(cc, offset, mask, set) \ chipco_write32(cc, offset, (chipco_read32(cc, offset) & (mask)) | (set)) extern void ssb_chipcommon_init(struct ssb_chipcommon *cc); extern void ssb_chipco_suspend(struct ssb_chipcommon *cc); extern void ssb_chipco_resume(struct ssb_chipcommon *cc); extern void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc, u32 *plltype, u32 *n, u32 *m); extern void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc, u32 *plltype, u32 *n, u32 *m); extern void ssb_chipco_timing_init(struct ssb_chipcommon *cc, unsigned long ns_per_cycle); enum ssb_clkmode { SSB_CLKMODE_SLOW, SSB_CLKMODE_FAST, SSB_CLKMODE_DYNAMIC, }; extern void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc, enum ssb_clkmode mode); extern void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks); void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value); u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask); /* Chipcommon GPIO pin access. */ u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask); u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value); u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value); u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value); u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value); u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value); #ifdef CONFIG_SSB_SERIAL extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc, struct ssb_serial_port *ports); #endif /* CONFIG_SSB_SERIAL */ /* PMU support */ extern void ssb_pmu_init(struct ssb_chipcommon *cc); enum ssb_pmu_ldo_volt_id { LDO_PAREF = 0, LDO_VOLT1, LDO_VOLT2, LDO_VOLT3, }; void ssb_pmu_set_ldo_voltage(struct ssb_chipcommon *cc, enum ssb_pmu_ldo_volt_id id, u32 voltage); void ssb_pmu_set_ldo_paref(struct ssb_chipcommon *cc, bool on); #endif /* LINUX_SSB_CHIPCO_H_ */ compat-drivers-2012-09-18/include/linux/unaligned/0000755000175000017500000000000012025673354021126 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/linux/unaligned/le_byteshift.h0000644000175000017500000000261712025673354023766 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_LE_BYTESHIFT_H #define _LINUX_UNALIGNED_LE_BYTESHIFT_H #include static inline u16 __get_unaligned_le16(const u8 *p) { return p[0] | p[1] << 8; } static inline u32 __get_unaligned_le32(const u8 *p) { return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; } static inline u64 __get_unaligned_le64(const u8 *p) { return (u64)__get_unaligned_le32(p + 4) << 32 | __get_unaligned_le32(p); } static inline void __put_unaligned_le16(u16 val, u8 *p) { *p++ = val; *p++ = val >> 8; } static inline void __put_unaligned_le32(u32 val, u8 *p) { __put_unaligned_le16(val >> 16, p + 2); __put_unaligned_le16(val, p); } static inline void __put_unaligned_le64(u64 val, u8 *p) { __put_unaligned_le32(val >> 32, p + 4); __put_unaligned_le32(val, p); } static inline u16 get_unaligned_le16(const void *p) { return __get_unaligned_le16((const u8 *)p); } static inline u32 get_unaligned_le32(const void *p) { return __get_unaligned_le32((const u8 *)p); } static inline u64 get_unaligned_le64(const void *p) { return __get_unaligned_le64((const u8 *)p); } static inline void put_unaligned_le16(u16 val, void *p) { __put_unaligned_le16(val, p); } static inline void put_unaligned_le32(u32 val, void *p) { __put_unaligned_le32(val, p); } static inline void put_unaligned_le64(u64 val, void *p) { __put_unaligned_le64(val, p); } #endif /* _LINUX_UNALIGNED_LE_BYTESHIFT_H */ compat-drivers-2012-09-18/include/linux/unaligned/le_struct.h0000644000175000017500000000135512025673354023307 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_LE_STRUCT_H #define _LINUX_UNALIGNED_LE_STRUCT_H #include static inline u16 get_unaligned_le16(const void *p) { return __get_unaligned_cpu16((const u8 *)p); } static inline u32 get_unaligned_le32(const void *p) { return __get_unaligned_cpu32((const u8 *)p); } static inline u64 get_unaligned_le64(const void *p) { return __get_unaligned_cpu64((const u8 *)p); } static inline void put_unaligned_le16(u16 val, void *p) { __put_unaligned_cpu16(val, p); } static inline void put_unaligned_le32(u32 val, void *p) { __put_unaligned_cpu32(val, p); } static inline void put_unaligned_le64(u64 val, void *p) { __put_unaligned_cpu64(val, p); } #endif /* _LINUX_UNALIGNED_LE_STRUCT_H */ compat-drivers-2012-09-18/include/linux/unaligned/be_struct.h0000644000175000017500000000135512025673354023275 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_BE_STRUCT_H #define _LINUX_UNALIGNED_BE_STRUCT_H #include static inline u16 get_unaligned_be16(const void *p) { return __get_unaligned_cpu16((const u8 *)p); } static inline u32 get_unaligned_be32(const void *p) { return __get_unaligned_cpu32((const u8 *)p); } static inline u64 get_unaligned_be64(const void *p) { return __get_unaligned_cpu64((const u8 *)p); } static inline void put_unaligned_be16(u16 val, void *p) { __put_unaligned_cpu16(val, p); } static inline void put_unaligned_be32(u32 val, void *p) { __put_unaligned_cpu32(val, p); } static inline void put_unaligned_be64(u64 val, void *p) { __put_unaligned_cpu64(val, p); } #endif /* _LINUX_UNALIGNED_BE_STRUCT_H */ compat-drivers-2012-09-18/include/linux/unaligned/be_memmove.h0000644000175000017500000000140212025673354023407 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_BE_MEMMOVE_H #define _LINUX_UNALIGNED_BE_MEMMOVE_H #include static inline u16 get_unaligned_be16(const void *p) { return __get_unaligned_memmove16((const u8 *)p); } static inline u32 get_unaligned_be32(const void *p) { return __get_unaligned_memmove32((const u8 *)p); } static inline u64 get_unaligned_be64(const void *p) { return __get_unaligned_memmove64((const u8 *)p); } static inline void put_unaligned_be16(u16 val, void *p) { __put_unaligned_memmove16(val, p); } static inline void put_unaligned_be32(u32 val, void *p) { __put_unaligned_memmove32(val, p); } static inline void put_unaligned_be64(u64 val, void *p) { __put_unaligned_memmove64(val, p); } #endif /* _LINUX_UNALIGNED_LE_MEMMOVE_H */ compat-drivers-2012-09-18/include/linux/unaligned/packed_struct.h0000644000175000017500000000212112025673354024126 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_PACKED_STRUCT_H #define _LINUX_UNALIGNED_PACKED_STRUCT_H #include struct __una_u16 { u16 x __attribute__((packed)); }; struct __una_u32 { u32 x __attribute__((packed)); }; struct __una_u64 { u64 x __attribute__((packed)); }; static inline u16 __get_unaligned_cpu16(const void *p) { const struct __una_u16 *ptr = (const struct __una_u16 *)p; return ptr->x; } static inline u32 __get_unaligned_cpu32(const void *p) { const struct __una_u32 *ptr = (const struct __una_u32 *)p; return ptr->x; } static inline u64 __get_unaligned_cpu64(const void *p) { const struct __una_u64 *ptr = (const struct __una_u64 *)p; return ptr->x; } static inline void __put_unaligned_cpu16(u16 val, void *p) { struct __una_u16 *ptr = (struct __una_u16 *)p; ptr->x = val; } static inline void __put_unaligned_cpu32(u32 val, void *p) { struct __una_u32 *ptr = (struct __una_u32 *)p; ptr->x = val; } static inline void __put_unaligned_cpu64(u64 val, void *p) { struct __una_u64 *ptr = (struct __una_u64 *)p; ptr->x = val; } #endif /* _LINUX_UNALIGNED_PACKED_STRUCT_H */ compat-drivers-2012-09-18/include/linux/unaligned/le_memmove.h0000644000175000017500000000140212025673354023421 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_LE_MEMMOVE_H #define _LINUX_UNALIGNED_LE_MEMMOVE_H #include static inline u16 get_unaligned_le16(const void *p) { return __get_unaligned_memmove16((const u8 *)p); } static inline u32 get_unaligned_le32(const void *p) { return __get_unaligned_memmove32((const u8 *)p); } static inline u64 get_unaligned_le64(const void *p) { return __get_unaligned_memmove64((const u8 *)p); } static inline void put_unaligned_le16(u16 val, void *p) { __put_unaligned_memmove16(val, p); } static inline void put_unaligned_le32(u32 val, void *p) { __put_unaligned_memmove32(val, p); } static inline void put_unaligned_le64(u64 val, void *p) { __put_unaligned_memmove64(val, p); } #endif /* _LINUX_UNALIGNED_LE_MEMMOVE_H */ compat-drivers-2012-09-18/include/linux/unaligned/be_byteshift.h0000644000175000017500000000261712025673354023754 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_BE_BYTESHIFT_H #define _LINUX_UNALIGNED_BE_BYTESHIFT_H #include static inline u16 __get_unaligned_be16(const u8 *p) { return p[0] << 8 | p[1]; } static inline u32 __get_unaligned_be32(const u8 *p) { return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; } static inline u64 __get_unaligned_be64(const u8 *p) { return (u64)__get_unaligned_be32(p) << 32 | __get_unaligned_be32(p + 4); } static inline void __put_unaligned_be16(u16 val, u8 *p) { *p++ = val >> 8; *p++ = val; } static inline void __put_unaligned_be32(u32 val, u8 *p) { __put_unaligned_be16(val >> 16, p); __put_unaligned_be16(val, p + 2); } static inline void __put_unaligned_be64(u64 val, u8 *p) { __put_unaligned_be32(val >> 32, p); __put_unaligned_be32(val, p + 4); } static inline u16 get_unaligned_be16(const void *p) { return __get_unaligned_be16((const u8 *)p); } static inline u32 get_unaligned_be32(const void *p) { return __get_unaligned_be32((const u8 *)p); } static inline u64 get_unaligned_be64(const void *p) { return __get_unaligned_be64((const u8 *)p); } static inline void put_unaligned_be16(u16 val, void *p) { __put_unaligned_be16(val, p); } static inline void put_unaligned_be32(u32 val, void *p) { __put_unaligned_be32(val, p); } static inline void put_unaligned_be64(u64 val, void *p) { __put_unaligned_be64(val, p); } #endif /* _LINUX_UNALIGNED_BE_BYTESHIFT_H */ compat-drivers-2012-09-18/include/linux/unaligned/generic.h0000644000175000017500000000412112025673354022711 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_GENERIC_H #define _LINUX_UNALIGNED_GENERIC_H /* * Cause a link-time error if we try an unaligned access other than * 1,2,4 or 8 bytes long */ extern void __bad_unaligned_access_size(void); #define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({ \ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \ __bad_unaligned_access_size())))); \ })) #define __get_unaligned_be(ptr) ((__force typeof(*(ptr)))({ \ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_be16((ptr)), \ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_be32((ptr)), \ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_be64((ptr)), \ __bad_unaligned_access_size())))); \ })) #define __put_unaligned_le(val, ptr) ({ \ void *__gu_p = (ptr); \ switch (sizeof(*(ptr))) { \ case 1: \ *(u8 *)__gu_p = (__force u8)(val); \ break; \ case 2: \ put_unaligned_le16((__force u16)(val), __gu_p); \ break; \ case 4: \ put_unaligned_le32((__force u32)(val), __gu_p); \ break; \ case 8: \ put_unaligned_le64((__force u64)(val), __gu_p); \ break; \ default: \ __bad_unaligned_access_size(); \ break; \ } \ (void)0; }) #define __put_unaligned_be(val, ptr) ({ \ void *__gu_p = (ptr); \ switch (sizeof(*(ptr))) { \ case 1: \ *(u8 *)__gu_p = (__force u8)(val); \ break; \ case 2: \ put_unaligned_be16((__force u16)(val), __gu_p); \ break; \ case 4: \ put_unaligned_be32((__force u32)(val), __gu_p); \ break; \ case 8: \ put_unaligned_be64((__force u64)(val), __gu_p); \ break; \ default: \ __bad_unaligned_access_size(); \ break; \ } \ (void)0; }) #endif /* _LINUX_UNALIGNED_GENERIC_H */ compat-drivers-2012-09-18/include/linux/unaligned/access_ok.h0000644000175000017500000000243112025673354023231 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_ACCESS_OK_H #define _LINUX_UNALIGNED_ACCESS_OK_H #include #include static inline u16 get_unaligned_le16(const void *p) { return le16_to_cpup((__le16 *)p); } static inline u32 get_unaligned_le32(const void *p) { return le32_to_cpup((__le32 *)p); } static inline u64 get_unaligned_le64(const void *p) { return le64_to_cpup((__le64 *)p); } static inline u16 get_unaligned_be16(const void *p) { return be16_to_cpup((__be16 *)p); } static inline u32 get_unaligned_be32(const void *p) { return be32_to_cpup((__be32 *)p); } static inline u64 get_unaligned_be64(const void *p) { return be64_to_cpup((__be64 *)p); } static inline void put_unaligned_le16(u16 val, void *p) { *((__le16 *)p) = cpu_to_le16(val); } static inline void put_unaligned_le32(u32 val, void *p) { *((__le32 *)p) = cpu_to_le32(val); } static inline void put_unaligned_le64(u64 val, void *p) { *((__le64 *)p) = cpu_to_le64(val); } static inline void put_unaligned_be16(u16 val, void *p) { *((__be16 *)p) = cpu_to_be16(val); } static inline void put_unaligned_be32(u32 val, void *p) { *((__be32 *)p) = cpu_to_be32(val); } static inline void put_unaligned_be64(u64 val, void *p) { *((__be64 *)p) = cpu_to_be64(val); } #endif /* _LINUX_UNALIGNED_ACCESS_OK_H */ compat-drivers-2012-09-18/include/linux/unaligned/memmove.h0000644000175000017500000000147512025673354022753 0ustar mcgrofmcgrof#ifndef _LINUX_UNALIGNED_MEMMOVE_H #define _LINUX_UNALIGNED_MEMMOVE_H #include #include /* Use memmove here, so gcc does not insert a __builtin_memcpy. */ static inline u16 __get_unaligned_memmove16(const void *p) { u16 tmp; memmove(&tmp, p, 2); return tmp; } static inline u32 __get_unaligned_memmove32(const void *p) { u32 tmp; memmove(&tmp, p, 4); return tmp; } static inline u64 __get_unaligned_memmove64(const void *p) { u64 tmp; memmove(&tmp, p, 8); return tmp; } static inline void __put_unaligned_memmove16(u16 val, void *p) { memmove(p, &val, 2); } static inline void __put_unaligned_memmove32(u32 val, void *p) { memmove(p, &val, 4); } static inline void __put_unaligned_memmove64(u64 val, void *p) { memmove(p, &val, 8); } #endif /* _LINUX_UNALIGNED_MEMMOVE_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.38.h0000644000175000017500000000737412025673354021363 0ustar mcgrofmcgrof#ifndef LINUX_26_38_COMPAT_H #define LINUX_26_38_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) #include #include #include #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) static inline void bstats_update(struct gnet_stats_basic_packed *bstats, const struct sk_buff *skb) { bstats->bytes += qdisc_pkt_len((struct sk_buff *) skb); bstats->packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1; } static inline void qdisc_bstats_update(struct Qdisc *sch, const struct sk_buff *skb) { bstats_update(&sch->bstats, skb); } #else /* * kernels <= 2.6.30 do not pass a const skb to qdisc_pkt_len, and * gnet_stats_basic_packed did not exist (see c1a8f1f1c8) */ static inline void bstats_update(struct gnet_stats_basic *bstats, struct sk_buff *skb) { bstats->bytes += qdisc_pkt_len(skb); bstats->packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1; } static inline void qdisc_bstats_update(struct Qdisc *sch, struct sk_buff *skb) { bstats_update(&sch->bstats, skb); } #endif /* rename member in struct mmc_host in include/linux/mmc/host.h */ #define max_segs max_hw_segs /* Exponentially weighted moving average (EWMA) */ /* For more documentation see lib/average.c */ struct ewma { unsigned long internal; unsigned long factor; unsigned long weight; }; /* mask ewma_init as RHEL6 backports this */ #define ewma_init(a,b,c) compat_ewma_init(a,b,c) extern void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight); /* mask ewma_add as RHEL6 backports this */ #define ewma_add(a,b) compat_ewma_add(a,b) extern struct ewma *ewma_add(struct ewma *avg, unsigned long val); /** * ewma_read() - Get average value * @avg: Average structure * * Returns the average value held in @avg. */ static inline unsigned long ewma_read(const struct ewma *avg) { return DIV_ROUND_CLOSEST(avg->internal, avg->factor); } #define pr_warn pr_warning #define create_freezable_workqueue create_freezeable_workqueue static inline int skb_checksum_start_offset(const struct sk_buff *skb) { return skb->csum_start - skb_headroom(skb); } /* from include/linux/printk.h */ #define pr_emerg_once(fmt, ...) \ printk_once(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) #define pr_alert_once(fmt, ...) \ printk_once(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) #define pr_crit_once(fmt, ...) \ printk_once(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) #define pr_err_once(fmt, ...) \ printk_once(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) #define pr_warn_once(fmt, ...) \ printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) #define pr_notice_once(fmt, ...) \ printk_once(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) #define pr_info_once(fmt, ...) \ printk_once(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) #define pr_cont_once(fmt, ...) \ printk_once(KERN_CONT pr_fmt(fmt), ##__VA_ARGS__) #if defined(DEBUG) #define pr_debug_once(fmt, ...) \ printk_once(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug_once(fmt, ...) \ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #endif /* include/linux/netdevice.h */ #define alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs) \ alloc_netdev_mq(sizeof_priv, name, setup, \ max_t(unsigned int, txqs, rxqs)) #define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ /** * is_unicast_ether_addr - Determine if the Ethernet address is unicast * @addr: Pointer to a six-byte array containing the Ethernet address * * Return true if the address is a unicast address. */ static inline int is_unicast_ether_addr(const u8 *addr) { return !is_multicast_ether_addr(addr); } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) */ #endif /* LINUX_26_38_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.33.h0000644000175000017500000001342612025673354021351 0ustar mcgrofmcgrof#ifndef LINUX_26_33_COMPAT_H #define LINUX_26_33_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)) #include #include #if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE) #include #include #include #endif #include #include #if defined(CONFIG_COMPAT_FIRMWARE_CLASS) #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) #define release_firmware compat_release_firmware #define request_firmware compat_request_firmware #define request_firmware_nowait compat_request_firmware_nowait #endif #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) int compat_request_firmware(const struct firmware **fw, const char *name, struct device *device); int compat_request_firmware_nowait( struct module *module, int uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)); void compat_release_firmware(const struct firmware *fw); #else static inline int compat_request_firmware(const struct firmware **fw, const char *name, struct device *device) { return -EINVAL; } static inline int compat_request_firmware_nowait( struct module *module, int uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)) { return -EINVAL; } static inline void compat_release_firmware(const struct firmware *fw) { } #endif #endif /* mask KEY_RFKILL as RHEL6 backports this */ #if !defined(KEY_RFKILL) #define KEY_RFKILL 247 /* Key that controls all radios */ #endif /* mask IFF_DONT_BRIDGE as RHEL6 backports this */ #if !defined(IFF_DONT_BRIDGE) #define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */ /* source: include/linux/if.h */ #endif /* mask NETDEV_POST_INIT as RHEL6 backports this */ /* this will never happen on older kernels */ #if !defined(NETDEV_POST_INIT) #define NETDEV_POST_INIT 0xffff #endif /* mask netdev_alloc_skb_ip_align as debian squeeze also backports this */ #define netdev_alloc_skb_ip_align(a, b) compat_netdev_alloc_skb_ip_align(a, b) static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, unsigned int length) { struct sk_buff *skb = netdev_alloc_skb(dev, length + NET_IP_ALIGN); if (NET_IP_ALIGN && skb) skb_reserve(skb, NET_IP_ALIGN); return skb; } #if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE) #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) #define pcmcia_request_window(a, b, c) pcmcia_request_window(&a, b, c) #define pcmcia_map_mem_page(a, b, c) pcmcia_map_mem_page(b, c) /* loop over CIS entries */ int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, int (*loop_tuple) (struct pcmcia_device *p_dev, tuple_t *tuple, void *priv_data), void *priv_data); #endif /* CONFIG_PCMCIA */ /* loop over CIS entries */ int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, cisparse_t *parse, void *priv_data, int (*loop_tuple) (tuple_t *tuple, cisparse_t *parse, void *priv_data)); #endif /* CONFIG_PCCARD */ /** * list_for_each_entry_continue_rcu - continue iteration over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Continue to iterate over list of given type, continuing after * the current position. */ #define list_for_each_entry_continue_rcu(pos, head, member) \ for (pos = list_entry_rcu(pos->member.next, typeof(*pos), member); \ prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) #define sock_recv_ts_and_drops(msg, sk, skb) sock_recv_timestamp(msg, sk, skb) /* mask pci_pcie_cap as debian squeeze also backports this */ #define pci_pcie_cap(a) compat_pci_pcie_cap(a) /** * pci_pcie_cap - get the saved PCIe capability offset * @dev: PCI device * * PCIe capability offset is calculated at PCI device initialization * time and saved in the data structure. This function returns saved * PCIe capability offset. Using this instead of pci_find_capability() * reduces unnecessary search in the PCI configuration space. If you * need to calculate PCIe capability offset from raw device for some * reasons, please use pci_find_capability() instead. */ static inline int pci_pcie_cap(struct pci_dev *dev) { return pci_find_capability(dev, PCI_CAP_ID_EXP); } /* mask pci_is_pcie as RHEL6 backports this */ #define pci_is_pcie(a) compat_pci_is_pcie(a) /** * pci_is_pcie - check if the PCI device is PCI Express capable * @dev: PCI device * * Retrun true if the PCI device is PCI Express capable, false otherwise. */ static inline bool pci_is_pcie(struct pci_dev *dev) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) return dev->is_pcie; #else return !!pci_pcie_cap(dev); #endif } #ifdef __GNUC__ #define __always_unused __attribute__((unused)) #else #define __always_unused /* unimplemented */ #endif /* mask IS_ERR_OR_NULL as debian squeeze also backports this */ #define IS_ERR_OR_NULL(a) compat_IS_ERR_OR_NULL(a) static inline long __must_check IS_ERR_OR_NULL(const void *ptr) { return !ptr || IS_ERR_VALUE((unsigned long)ptr); } #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,32)) #undef SIMPLE_DEV_PM_OPS #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ const struct dev_pm_ops name = { \ .suspend = suspend_fn, \ .resume = resume_fn, \ .freeze = suspend_fn, \ .thaw = resume_fn, \ .poweroff = suspend_fn, \ .restore = resume_fn, \ } #endif /* (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,32)) */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)) */ #endif /* LINUX_26_33_COMPAT_H */ compat-drivers-2012-09-18/include/linux/tracepoint.h0000644000175000017500000000224612025673354021505 0ustar mcgrofmcgrof#ifndef _COMPAT_LINUX_TRACEPOINT_H #define _COMPAT_LINUX_TRACEPOINT_H 1 #include #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)) /* * Disable all tracing for older kernels * < 2.6.27 had no tracing * 2.6.27 had broken tracing * 2.6.28-2.6.32 didn't have anything like DECLARE_EVENT_CLASS * and faking it would be extremely difficult */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) /* * For 2.6.28+ include the original tracepoint.h but override * the defines new code uses to disable tracing completely. */ #include_next #endif #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ static inline void trace_ ## name(proto) {} #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(...) #undef DEFINE_EVENT #define DEFINE_EVENT(evt_class, name, proto, ...) \ static inline void trace_ ## name(proto) {} #define TP_PROTO(args...) args #define TP_ARGS(args...) args #define TP_CONDITION(args...) args #else /* since 2.6.33, tracing hasn't changed, so just include the kernel's file */ #include_next #endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)) */ #endif /* _COMPAT_LINUX_TRACEPOINT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.27.h0000644000175000017500000001752212025673354021355 0ustar mcgrofmcgrof#ifndef LINUX_26_27_COMPAT_H #define LINUX_26_27_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) #include #include #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) #include #include #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */ #include #include #include #include #include #include #include #include static inline struct net_device *qdisc_dev(const struct Qdisc *qdisc) { return qdisc->dev; } /* * Backports 378a2f09 and c27f339a * This may need a bit more work. */ enum net_xmit_qdisc_t { __NET_XMIT_STOLEN = 0x00010000, __NET_XMIT_BYPASS = 0x00020000, }; struct qdisc_skb_cb { unsigned int pkt_len; char data[]; }; static inline struct qdisc_skb_cb *qdisc_skb_cb(struct sk_buff *skb) { return (struct qdisc_skb_cb *)skb->cb; } static inline unsigned int qdisc_pkt_len(struct sk_buff *skb) { return qdisc_skb_cb(skb)->pkt_len; } #define PCI_PM_CAP_PME_SHIFT 11 /* I can't find a more suitable replacement... */ #define flush_work(work) cancel_work_sync(work) struct builtin_fw { char *name; void *data; unsigned long size; }; /* * On older kernels we do not have net_device Multi Queue support, but * since we no longer use MQ on mac80211 we can simply use the 0 queue. * Note that if other fullmac drivers make use of this they then need * to be backported somehow or deal with just 1 queueue from MQ. */ static inline void netif_tx_wake_all_queues(struct net_device *dev) { netif_wake_queue(dev); } static inline void netif_tx_start_all_queues(struct net_device *dev) { netif_start_queue(dev); } static inline void netif_tx_stop_all_queues(struct net_device *dev) { netif_stop_queue(dev); } /* Are all TX queues of the device empty? */ static inline bool qdisc_all_tx_empty(const struct net_device *dev) { return skb_queue_empty(&dev->qdisc->q); } bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); /* * The net_device has a spin_lock on newer kernels, on older kernels we're out of luck */ #define netif_addr_lock_bh(dev) #define netif_addr_unlock_bh(dev) /* * To port this properly we'd have to port warn_slowpath_null(), * which I'm lazy to do so just do a regular print for now. If you * want to port this read kernel/panic.c */ #define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0) /* This is ported directly as-is on newer kernels */ #ifndef WARN #define WARN(condition, format...) ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ __WARN_printf(format); \ unlikely(__ret_warn_on); \ }) #endif /* On 2.6.27 a second argument was added, on older kernels we ignore it */ #define dma_mapping_error(pdev, dma_addr) dma_mapping_error(dma_addr) #define pci_dma_mapping_error(pdev, dma_addr) dma_mapping_error(pdev, dma_addr) /* This is from include/linux/ieee80211.h */ #define IEEE80211_HT_CAP_DSSSCCK40 0x1000 /* New link list changes added as of 2.6.27, needed for ath9k */ static inline void __list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry) { struct list_head *new_first = entry->next; list->next = head->next; list->next->prev = list; list->prev = entry; entry->next = list; head->next = new_first; new_first->prev = head; } /** * list_cut_position - cut a list into two * @list: a new list to add all removed entries * @head: a list with entries * @entry: an entry within head, could be the head itself * and if so we won't cut the list * * This helper moves the initial part of @head, up to and * including @entry, from @head to @list. You should * pass on @entry an element you know is on @head. @list * should be an empty list or a list you do not care about * losing its data. * */ static inline void list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry) { if (list_empty(head)) return; if (list_is_singular(head) && (head->next != entry && head != entry)) return; if (entry == head) INIT_LIST_HEAD(list); else __list_cut_position(list, head, entry); } /* __list_splice as re-implemented on 2.6.27, we backport it */ static inline void __compat_list_splice_new_27(const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __compat_list_splice_new_27(list, head->prev, head); } /** * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __compat_list_splice_new_27(list, head->prev, head); INIT_LIST_HEAD(list); } } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */ #define iwe_stream_add_value(info, event, value, ends, iwe, event_len) iwe_stream_add_value(event, value, ends, iwe, event_len) #define iwe_stream_add_point(info, stream, ends, iwe, extra) iwe_stream_add_point(stream, ends, iwe, extra) #define iwe_stream_add_event(info, stream, ends, iwe, event_len) iwe_stream_add_event(stream, ends, iwe, event_len) /* Flags available in struct iw_request_info */ #define IW_REQUEST_FLAG_COMPAT 0x0001 /* Compat ioctl call */ static inline int iwe_stream_lcp_len(struct iw_request_info *info) { #ifdef CONFIG_COMPAT if (info->flags & IW_REQUEST_FLAG_COMPAT) return IW_EV_COMPAT_LCP_LEN; #endif return IW_EV_LCP_LEN; } #ifdef CONFIG_ARM /* * The caller asks to handle a range between offset and offset + size, * but we process a larger range from 0 to offset + size due to lack of * offset support. */ static inline void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t handle, unsigned long offset, size_t size, enum dma_data_direction dir) { dma_sync_single_for_cpu(dev, handle, offset + size, dir); } static inline void dma_sync_single_range_for_device(struct device *dev, dma_addr_t handle, unsigned long offset, size_t size, enum dma_data_direction dir) { dma_sync_single_for_device(dev, handle, offset + size, dir); } #endif /* arm */ #if defined(CONFIG_DEBUG_FS) void debugfs_remove_recursive(struct dentry *dentry); #else static inline void debugfs_remove_recursive(struct dentry *dentry) { } #endif #define device_create(cls, parent, devt, drvdata, fmt, ...) \ ({ \ struct device *_dev; \ _dev = (device_create)(cls, parent, devt, fmt, __VA_ARGS__); \ dev_set_drvdata(_dev, drvdata); \ _dev; \ }) #define dev_name(dev) dev_name((struct device *)dev) static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep, __u32 speed) { ep->speed = (__u16)speed; } static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) { return ep->speed; } /** * lower_32_bits - return bits 0-31 of a number * @n: the number we're accessing */ #define lower_32_bits(n) ((u32)(n)) #define netif_wake_subqueue netif_start_subqueue #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) */ #endif /* LINUX_26_27_COMPAT_H */ compat-drivers-2012-09-18/include/linux/ieee80211.h0000644000175000017500000017016712026211315020632 0ustar mcgrofmcgrof/* * IEEE 802.11 defines * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen * * Copyright (c) 2002-2003, Jouni Malinen * Copyright (c) 2005, Devicescape Software, Inc. * Copyright (c) 2006, Michael Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef LINUX_IEEE80211_H #define LINUX_IEEE80211_H #include #include /* * DS bit usage * * TA = transmitter address * RA = receiver address * DA = destination address * SA = source address * * ToDS FromDS A1(RA) A2(TA) A3 A4 Use * ----------------------------------------------------------------- * 0 0 DA SA BSSID - IBSS/DLS * 0 1 DA BSSID SA - AP -> STA * 1 0 BSSID SA DA - AP <- STA * 1 1 RA TA DA SA unspecified (WDS) */ #define FCS_LEN 4 #define IEEE80211_FCTL_VERS 0x0003 #define IEEE80211_FCTL_FTYPE 0x000c #define IEEE80211_FCTL_STYPE 0x00f0 #define IEEE80211_FCTL_TODS 0x0100 #define IEEE80211_FCTL_FROMDS 0x0200 #define IEEE80211_FCTL_MOREFRAGS 0x0400 #define IEEE80211_FCTL_RETRY 0x0800 #define IEEE80211_FCTL_PM 0x1000 #define IEEE80211_FCTL_MOREDATA 0x2000 #define IEEE80211_FCTL_PROTECTED 0x4000 #define IEEE80211_FCTL_ORDER 0x8000 #define IEEE80211_FCTL_CTL_EXT 0x0f00 #define IEEE80211_SCTL_FRAG 0x000F #define IEEE80211_SCTL_SEQ 0xFFF0 #define IEEE80211_FTYPE_MGMT 0x0000 #define IEEE80211_FTYPE_CTL 0x0004 #define IEEE80211_FTYPE_DATA 0x0008 #define IEEE80211_FTYPE_EXT 0x000c /* management */ #define IEEE80211_STYPE_ASSOC_REQ 0x0000 #define IEEE80211_STYPE_ASSOC_RESP 0x0010 #define IEEE80211_STYPE_REASSOC_REQ 0x0020 #define IEEE80211_STYPE_REASSOC_RESP 0x0030 #define IEEE80211_STYPE_PROBE_REQ 0x0040 #define IEEE80211_STYPE_PROBE_RESP 0x0050 #define IEEE80211_STYPE_BEACON 0x0080 #define IEEE80211_STYPE_ATIM 0x0090 #define IEEE80211_STYPE_DISASSOC 0x00A0 #define IEEE80211_STYPE_AUTH 0x00B0 #define IEEE80211_STYPE_DEAUTH 0x00C0 #define IEEE80211_STYPE_ACTION 0x00D0 /* control */ #define IEEE80211_STYPE_CTL_EXT 0x0060 #define IEEE80211_STYPE_BACK_REQ 0x0080 #define IEEE80211_STYPE_BACK 0x0090 #define IEEE80211_STYPE_PSPOLL 0x00A0 #define IEEE80211_STYPE_RTS 0x00B0 #define IEEE80211_STYPE_CTS 0x00C0 #define IEEE80211_STYPE_ACK 0x00D0 #define IEEE80211_STYPE_CFEND 0x00E0 #define IEEE80211_STYPE_CFENDACK 0x00F0 /* data */ #define IEEE80211_STYPE_DATA 0x0000 #define IEEE80211_STYPE_DATA_CFACK 0x0010 #define IEEE80211_STYPE_DATA_CFPOLL 0x0020 #define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 #define IEEE80211_STYPE_NULLFUNC 0x0040 #define IEEE80211_STYPE_CFACK 0x0050 #define IEEE80211_STYPE_CFPOLL 0x0060 #define IEEE80211_STYPE_CFACKPOLL 0x0070 #define IEEE80211_STYPE_QOS_DATA 0x0080 #define IEEE80211_STYPE_QOS_DATA_CFACK 0x0090 #define IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0 #define IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0 #define IEEE80211_STYPE_QOS_NULLFUNC 0x00C0 #define IEEE80211_STYPE_QOS_CFACK 0x00D0 #define IEEE80211_STYPE_QOS_CFPOLL 0x00E0 #define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0 /* extension, added by 802.11ad */ #define IEEE80211_STYPE_DMG_BEACON 0x0000 /* control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT */ #define IEEE80211_CTL_EXT_POLL 0x2000 #define IEEE80211_CTL_EXT_SPR 0x3000 #define IEEE80211_CTL_EXT_GRANT 0x4000 #define IEEE80211_CTL_EXT_DMG_CTS 0x5000 #define IEEE80211_CTL_EXT_DMG_DTS 0x6000 #define IEEE80211_CTL_EXT_SSW 0x8000 #define IEEE80211_CTL_EXT_SSW_FBACK 0x9000 #define IEEE80211_CTL_EXT_SSW_ACK 0xa000 /* miscellaneous IEEE 802.11 constants */ #define IEEE80211_MAX_FRAG_THRESHOLD 2352 #define IEEE80211_MAX_RTS_THRESHOLD 2353 #define IEEE80211_MAX_AID 2007 #define IEEE80211_MAX_TIM_LEN 251 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section 6.2.1.1.2. 802.11e clarifies the figure in section 7.1.2. The frame body is up to 2304 octets long (maximum MSDU size) plus any crypt overhead. */ #define IEEE80211_MAX_DATA_LEN 2304 /* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */ #define IEEE80211_MAX_FRAME_LEN 2352 #define IEEE80211_MAX_SSID_LEN 32 #define IEEE80211_MAX_MESH_ID_LEN 32 #define IEEE80211_QOS_CTL_LEN 2 /* 1d tag mask */ #define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 /* TID mask */ #define IEEE80211_QOS_CTL_TID_MASK 0x000f /* EOSP */ #define IEEE80211_QOS_CTL_EOSP 0x0010 /* ACK policy */ #define IEEE80211_QOS_CTL_ACK_POLICY_NORMAL 0x0000 #define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020 #define IEEE80211_QOS_CTL_ACK_POLICY_NO_EXPL 0x0040 #define IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK 0x0060 #define IEEE80211_QOS_CTL_ACK_POLICY_MASK 0x0060 /* A-MSDU 802.11n */ #define IEEE80211_QOS_CTL_A_MSDU_PRESENT 0x0080 /* Mesh Control 802.11s */ #define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 /* U-APSD queue for WMM IEs sent by AP */ #define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) #define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f /* U-APSD queues for WMM IEs sent by STA */ #define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0) #define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1) #define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2) #define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3) #define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f /* U-APSD max SP length for WMM IEs sent by STA */ #define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00 #define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01 #define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02 #define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03 #define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03 #define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5 #define IEEE80211_HT_CTL_LEN 4 struct ieee80211_hdr { __le16 frame_control; __le16 duration_id; u8 addr1[6]; u8 addr2[6]; u8 addr3[6]; __le16 seq_ctrl; u8 addr4[6]; } __attribute__ ((packed)); struct ieee80211_hdr_3addr { __le16 frame_control; __le16 duration_id; u8 addr1[6]; u8 addr2[6]; u8 addr3[6]; __le16 seq_ctrl; } __attribute__ ((packed)); struct ieee80211_qos_hdr { __le16 frame_control; __le16 duration_id; u8 addr1[6]; u8 addr2[6]; u8 addr3[6]; __le16 seq_ctrl; __le16 qos_ctrl; } __attribute__ ((packed)); /** * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_tods(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0; } /** * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_fromds(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0; } /** * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_a4(__le16 fc) { __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); return (fc & tmp) == tmp; } /** * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_morefrags(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0; } /** * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_retry(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0; } /** * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_pm(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0; } /** * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_moredata(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0; } /** * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_protected(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0; } /** * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_has_order(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0; } /** * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_mgmt(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT); } /** * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_ctl(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL); } /** * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_data(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == cpu_to_le16(IEEE80211_FTYPE_DATA); } /** * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_data_qos(__le16 fc) { /* * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need * to check the one bit */ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) == cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA); } /** * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_data_present(__le16 fc) { /* * mask with 0x40 and test that that bit is clear to only return true * for the data-containing substypes. */ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | 0x40)) == cpu_to_le16(IEEE80211_FTYPE_DATA); } /** * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_assoc_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); } /** * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_assoc_resp(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); } /** * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_reassoc_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); } /** * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_reassoc_resp(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); } /** * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_probe_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); } /** * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_probe_resp(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); } /** * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_beacon(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); } /** * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_atim(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); } /** * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_disassoc(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); } /** * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_auth(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); } /** * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_deauth(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); } /** * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_action(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); } /** * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_back_req(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); } /** * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_back(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); } /** * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_pspoll(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); } /** * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_rts(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); } /** * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_cts(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); } /** * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_ack(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); } /** * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_cfend(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND); } /** * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_cfendack(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK); } /** * ieee80211_is_nullfunc - check if frame is a regular (non-QoS) nullfunc frame * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_nullfunc(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); } /** * ieee80211_is_qos_nullfunc - check if frame is a QoS nullfunc frame * @fc: frame control bytes in little-endian byteorder */ static inline int ieee80211_is_qos_nullfunc(__le16 fc) { return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); } /** * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set * @seq_ctrl: frame sequence control bytes in little-endian byteorder */ static inline int ieee80211_is_first_frag(__le16 seq_ctrl) { return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0; } struct ieee80211s_hdr { u8 flags; u8 ttl; __le32 seqnum; u8 eaddr1[6]; u8 eaddr2[6]; } __attribute__ ((packed)); /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 #define MESH_FLAGS_AE_A5_A6 0x2 #define MESH_FLAGS_AE 0x3 #define MESH_FLAGS_PS_DEEP 0x4 /** * enum ieee80211_preq_flags - mesh PREQ element flags * * @IEEE80211_PREQ_PROACTIVE_PREP_FLAG: proactive PREP subfield */ enum ieee80211_preq_flags { IEEE80211_PREQ_PROACTIVE_PREP_FLAG = 1<<2, }; /** * enum ieee80211_preq_target_flags - mesh PREQ element per target flags * * @IEEE80211_PREQ_TO_FLAG: target only subfield * @IEEE80211_PREQ_USN_FLAG: unknown target HWMP sequence number subfield */ enum ieee80211_preq_target_flags { IEEE80211_PREQ_TO_FLAG = 1<<0, IEEE80211_PREQ_USN_FLAG = 1<<2, }; /** * struct ieee80211_quiet_ie * * This structure refers to "Quiet information element" */ struct ieee80211_quiet_ie { u8 count; u8 period; __le16 duration; __le16 offset; } __attribute__ ((packed)); /** * struct ieee80211_msrment_ie * * This structure refers to "Measurement Request/Report information element" */ struct ieee80211_msrment_ie { u8 token; u8 mode; u8 type; u8 request[0]; } __attribute__ ((packed)); /** * struct ieee80211_channel_sw_ie * * This structure refers to "Channel Switch Announcement information element" */ struct ieee80211_channel_sw_ie { u8 mode; u8 new_ch_num; u8 count; } __attribute__ ((packed)); /** * struct ieee80211_tim * * This structure refers to "Traffic Indication Map information element" */ struct ieee80211_tim_ie { u8 dtim_count; u8 dtim_period; u8 bitmap_ctrl; /* variable size: 1 - 251 bytes */ u8 virtual_map[1]; } __attribute__ ((packed)); /** * struct ieee80211_meshconf_ie * * This structure refers to "Mesh Configuration information element" */ struct ieee80211_meshconf_ie { u8 meshconf_psel; u8 meshconf_pmetric; u8 meshconf_congest; u8 meshconf_synch; u8 meshconf_auth; u8 meshconf_form; u8 meshconf_cap; } __attribute__ ((packed)); /** * struct ieee80211_rann_ie * * This structure refers to "Root Announcement information element" */ struct ieee80211_rann_ie { u8 rann_flags; u8 rann_hopcount; u8 rann_ttl; u8 rann_addr[6]; __le32 rann_seq; __le32 rann_interval; __le32 rann_metric; } __attribute__ ((packed)); enum ieee80211_rann_flags { RANN_FLAG_IS_GATE = 1 << 0, }; #define WLAN_SA_QUERY_TR_ID_LEN 2 struct ieee80211_mgmt { __le16 frame_control; __le16 duration; u8 da[6]; u8 sa[6]; u8 bssid[6]; __le16 seq_ctrl; union { struct { __le16 auth_alg; __le16 auth_transaction; __le16 status_code; /* possibly followed by Challenge text */ u8 variable[0]; } __attribute__ ((packed)) auth; struct { __le16 reason_code; } __attribute__ ((packed)) deauth; struct { __le16 capab_info; __le16 listen_interval; /* followed by SSID and Supported rates */ u8 variable[0]; } __attribute__ ((packed)) assoc_req; struct { __le16 capab_info; __le16 status_code; __le16 aid; /* followed by Supported rates */ u8 variable[0]; } __attribute__ ((packed)) assoc_resp, reassoc_resp; struct { __le16 capab_info; __le16 listen_interval; u8 current_ap[6]; /* followed by SSID and Supported rates */ u8 variable[0]; } __attribute__ ((packed)) reassoc_req; struct { __le16 reason_code; } __attribute__ ((packed)) disassoc; struct { __le64 timestamp; __le16 beacon_int; __le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ u8 variable[0]; } __attribute__ ((packed)) beacon; struct { /* only variable items: SSID, Supported rates */ u8 variable[0]; } __attribute__ ((packed)) probe_req; struct { __le64 timestamp; __le16 beacon_int; __le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ u8 variable[0]; } __attribute__ ((packed)) probe_resp; struct { u8 category; union { struct { u8 action_code; u8 dialog_token; u8 status_code; u8 variable[0]; } __attribute__ ((packed)) wme_action; struct{ u8 action_code; u8 element_id; u8 length; struct ieee80211_channel_sw_ie sw_elem; } __attribute__((packed)) chan_switch; struct{ u8 action_code; u8 dialog_token; u8 element_id; u8 length; struct ieee80211_msrment_ie msr_elem; } __attribute__((packed)) measurement; struct{ u8 action_code; u8 dialog_token; __le16 capab; __le16 timeout; __le16 start_seq_num; } __attribute__((packed)) addba_req; struct{ u8 action_code; u8 dialog_token; __le16 status; __le16 capab; __le16 timeout; } __attribute__((packed)) addba_resp; struct{ u8 action_code; __le16 params; __le16 reason_code; } __attribute__((packed)) delba; struct { u8 action_code; u8 variable[0]; } __attribute__((packed)) self_prot; struct{ u8 action_code; u8 variable[0]; } __attribute__((packed)) mesh_action; struct { u8 action; u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; } __attribute__ ((packed)) sa_query; struct { u8 action; u8 smps_control; } __attribute__ ((packed)) ht_smps; struct { u8 action_code; u8 dialog_token; __le16 capability; u8 variable[0]; } __packed tdls_discover_resp; } u; } __attribute__ ((packed)) action; } u; } __attribute__ ((packed)); /* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */ #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 /* mgmt header + 1 byte category code */ #define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) /* Management MIC information element (IEEE 802.11w) */ struct ieee80211_mmie { u8 element_id; u8 length; __le16 key_id; u8 sequence_number[6]; u8 mic[8]; } __attribute__ ((packed)); struct ieee80211_vendor_ie { u8 element_id; u8 len; u8 oui[3]; u8 oui_type; } __packed; /* Control frames */ struct ieee80211_rts { __le16 frame_control; __le16 duration; u8 ra[6]; u8 ta[6]; } __attribute__ ((packed)); struct ieee80211_cts { __le16 frame_control; __le16 duration; u8 ra[6]; } __attribute__ ((packed)); struct ieee80211_pspoll { __le16 frame_control; __le16 aid; u8 bssid[6]; u8 ta[6]; } __attribute__ ((packed)); /* TDLS */ /* Link-id information element */ struct ieee80211_tdls_lnkie { u8 ie_type; /* Link Identifier IE */ u8 ie_len; u8 bssid[6]; u8 init_sta[6]; u8 resp_sta[6]; } __packed; struct ieee80211_tdls_data { u8 da[6]; u8 sa[6]; __be16 ether_type; u8 payload_type; u8 category; u8 action_code; union { struct { u8 dialog_token; __le16 capability; u8 variable[0]; } __packed setup_req; struct { __le16 status_code; u8 dialog_token; __le16 capability; u8 variable[0]; } __packed setup_resp; struct { __le16 status_code; u8 dialog_token; u8 variable[0]; } __packed setup_cfm; struct { __le16 reason_code; u8 variable[0]; } __packed teardown; struct { u8 dialog_token; u8 variable[0]; } __packed discover_req; } u; } __packed; /** * struct ieee80211_bar - HT Block Ack Request * * This structure refers to "HT BlockAckReq" as * described in 802.11n draft section 7.2.1.7.1 */ struct ieee80211_bar { __le16 frame_control; __le16 duration; __u8 ra[6]; __u8 ta[6]; __le16 control; __le16 start_seq_num; } __attribute__((packed)); /* 802.11 BAR control masks */ #define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 #define IEEE80211_BAR_CTRL_MULTI_TID 0x0002 #define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 #define IEEE80211_BAR_CTRL_TID_INFO_MASK 0xf000 #define IEEE80211_BAR_CTRL_TID_INFO_SHIFT 12 #define IEEE80211_HT_MCS_MASK_LEN 10 /** * struct ieee80211_mcs_info - MCS information * @rx_mask: RX mask * @rx_highest: highest supported RX rate. If set represents * the highest supported RX data rate in units of 1 Mbps. * If this field is 0 this value should not be used to * consider the highest RX data rate supported. * @tx_params: TX parameters */ struct ieee80211_mcs_info { u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN]; __le16 rx_highest; u8 tx_params; u8 reserved[3]; } __attribute__((packed)); /* 802.11n HT capability MSC set */ #define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff #define IEEE80211_HT_MCS_TX_DEFINED 0x01 #define IEEE80211_HT_MCS_TX_RX_DIFF 0x02 /* value 0 == 1 stream etc */ #define IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK 0x0C #define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2 #define IEEE80211_HT_MCS_TX_MAX_STREAMS 4 #define IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION 0x10 /* * 802.11n D5.0 20.3.5 / 20.6 says: * - indices 0 to 7 and 32 are single spatial stream * - 8 to 31 are multiple spatial streams using equal modulation * [8..15 for two streams, 16..23 for three and 24..31 for four] * - remainder are multiple spatial streams using unequal modulation */ #define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START 33 #define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE \ (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START / 8) /** * struct ieee80211_ht_cap - HT capabilities * * This structure is the "HT capabilities element" as * described in 802.11n D5.0 7.3.2.57 */ struct ieee80211_ht_cap { __le16 cap_info; u8 ampdu_params_info; /* 16 bytes MCS information */ struct ieee80211_mcs_info mcs; __le16 extended_ht_cap_info; __le32 tx_BF_cap_info; u8 antenna_selection_info; } __attribute__ ((packed)); /* 802.11n HT capabilities masks (for cap_info) */ #define IEEE80211_HT_CAP_LDPC_CODING 0x0001 #define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 #define IEEE80211_HT_CAP_SM_PS 0x000C #define IEEE80211_HT_CAP_SM_PS_SHIFT 2 #define IEEE80211_HT_CAP_GRN_FLD 0x0010 #define IEEE80211_HT_CAP_SGI_20 0x0020 #define IEEE80211_HT_CAP_SGI_40 0x0040 #define IEEE80211_HT_CAP_TX_STBC 0x0080 #define IEEE80211_HT_CAP_RX_STBC 0x0300 #define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 #define IEEE80211_HT_CAP_DELAY_BA 0x0400 #define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 #define IEEE80211_HT_CAP_DSSSCCK40 0x1000 #define IEEE80211_HT_CAP_RESERVED 0x2000 #define IEEE80211_HT_CAP_40MHZ_INTOLERANT 0x4000 #define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000 /* 802.11n HT extended capabilities masks (for extended_ht_cap_info) */ #define IEEE80211_HT_EXT_CAP_PCO 0x0001 #define IEEE80211_HT_EXT_CAP_PCO_TIME 0x0006 #define IEEE80211_HT_EXT_CAP_PCO_TIME_SHIFT 1 #define IEEE80211_HT_EXT_CAP_MCS_FB 0x0300 #define IEEE80211_HT_EXT_CAP_MCS_FB_SHIFT 8 #define IEEE80211_HT_EXT_CAP_HTC_SUP 0x0400 #define IEEE80211_HT_EXT_CAP_RD_RESPONDER 0x0800 /* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ #define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03 #define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C #define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2 /* * Maximum length of AMPDU that the STA can receive. * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets) */ enum ieee80211_max_ampdu_length_exp { IEEE80211_HT_MAX_AMPDU_8K = 0, IEEE80211_HT_MAX_AMPDU_16K = 1, IEEE80211_HT_MAX_AMPDU_32K = 2, IEEE80211_HT_MAX_AMPDU_64K = 3 }; #define IEEE80211_HT_MAX_AMPDU_FACTOR 13 /* Minimum MPDU start spacing */ enum ieee80211_min_mpdu_spacing { IEEE80211_HT_MPDU_DENSITY_NONE = 0, /* No restriction */ IEEE80211_HT_MPDU_DENSITY_0_25 = 1, /* 1/4 usec */ IEEE80211_HT_MPDU_DENSITY_0_5 = 2, /* 1/2 usec */ IEEE80211_HT_MPDU_DENSITY_1 = 3, /* 1 usec */ IEEE80211_HT_MPDU_DENSITY_2 = 4, /* 2 usec */ IEEE80211_HT_MPDU_DENSITY_4 = 5, /* 4 usec */ IEEE80211_HT_MPDU_DENSITY_8 = 6, /* 8 usec */ IEEE80211_HT_MPDU_DENSITY_16 = 7 /* 16 usec */ }; /** * struct ieee80211_ht_operation - HT operation IE * * This structure is the "HT operation element" as * described in 802.11n-2009 7.3.2.57 */ struct ieee80211_ht_operation { u8 primary_chan; u8 ht_param; __le16 operation_mode; __le16 stbc_param; u8 basic_set[16]; } __attribute__ ((packed)); /* for ht_param */ #define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03 #define IEEE80211_HT_PARAM_CHA_SEC_NONE 0x00 #define IEEE80211_HT_PARAM_CHA_SEC_ABOVE 0x01 #define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03 #define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04 #define IEEE80211_HT_PARAM_RIFS_MODE 0x08 /* for operation_mode */ #define IEEE80211_HT_OP_MODE_PROTECTION 0x0003 #define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0 #define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 1 #define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 2 #define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3 #define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004 #define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010 /* for stbc_param */ #define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040 #define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080 #define IEEE80211_HT_STBC_PARAM_STBC_BEACON 0x0100 #define IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT 0x0200 #define IEEE80211_HT_STBC_PARAM_PCO_ACTIVE 0x0400 #define IEEE80211_HT_STBC_PARAM_PCO_PHASE 0x0800 /* block-ack parameters */ #define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 #define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C #define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFC0 #define IEEE80211_DELBA_PARAM_TID_MASK 0xF000 #define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800 /* * A-PMDU buffer sizes * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) */ #define IEEE80211_MIN_AMPDU_BUF 0x8 #define IEEE80211_MAX_AMPDU_BUF 0x40 /* Spatial Multiplexing Power Save Modes (for capability) */ #define WLAN_HT_CAP_SM_PS_STATIC 0 #define WLAN_HT_CAP_SM_PS_DYNAMIC 1 #define WLAN_HT_CAP_SM_PS_INVALID 2 #define WLAN_HT_CAP_SM_PS_DISABLED 3 /* for SM power control field lower two bits */ #define WLAN_HT_SMPS_CONTROL_DISABLED 0 #define WLAN_HT_SMPS_CONTROL_STATIC 1 #define WLAN_HT_SMPS_CONTROL_DYNAMIC 3 #define VHT_MCS_SUPPORTED_SET_SIZE 8 struct ieee80211_vht_capabilities { __le32 vht_capabilities_info; u8 vht_supported_mcs_set[VHT_MCS_SUPPORTED_SET_SIZE]; } __packed; struct ieee80211_vht_operation { u8 vht_op_info_chwidth; u8 vht_op_info_chan_center_freq_seg1_idx; u8 vht_op_info_chan_center_freq_seg2_idx; __le16 vht_basic_mcs_set; } __packed; /** * struct ieee80211_vht_mcs_info - VHT MCS information * @rx_mcs_map: RX MCS map 2 bits for each stream, total 8 streams * @rx_highest: Indicates highest long GI VHT PPDU data rate * STA can receive. Rate expressed in units of 1 Mbps. * If this field is 0 this value should not be used to * consider the highest RX data rate supported. * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams * @tx_highest: Indicates highest long GI VHT PPDU data rate * STA can transmit. Rate expressed in units of 1 Mbps. * If this field is 0 this value should not be used to * consider the highest TX data rate supported. */ struct ieee80211_vht_mcs_info { __le16 rx_mcs_map; __le16 rx_highest; __le16 tx_mcs_map; __le16 tx_highest; } __packed; #define IEEE80211_VHT_MCS_ZERO_TO_SEVEN_SUPPORT 0 #define IEEE80211_VHT_MCS_ZERO_TO_EIGHT_SUPPORT 1 #define IEEE80211_VHT_MCS_ZERO_TO_NINE_SUPPORT 2 #define IEEE80211_VHT_MCS_NOT_SUPPORTED 3 /* 802.11ac VHT Capabilities */ #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008 #define IEEE80211_VHT_CAP_RXLDPC 0x00000010 #define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020 #define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040 #define IEEE80211_VHT_CAP_TXSTBC 0x00000080 #define IEEE80211_VHT_CAP_RXSTBC_1 0x00000100 #define IEEE80211_VHT_CAP_RXSTBC_2 0x00000200 #define IEEE80211_VHT_CAP_RXSTBC_3 0x00000300 #define IEEE80211_VHT_CAP_RXSTBC_4 0x00000400 #define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800 #define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000 #define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000 #define IEEE80211_VHT_CAP_SOUNDING_DIMENTION_MAX 0x00030000 #define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000 #define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000 #define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000 #define IEEE80211_VHT_CAP_HTC_VHT 0x00400000 #define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT 0x00800000 #define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB 0x08000000 #define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000 #define IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN 0x10000000 #define IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN 0x20000000 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 #define WLAN_AUTH_SAE 3 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 #define WLAN_CAPABILITY_ESS (1<<0) #define WLAN_CAPABILITY_IBSS (1<<1) /* * A mesh STA sets the ESS and IBSS capability bits to zero. * however, this holds true for p2p probe responses (in the p2p_find * phase) as well. */ #define WLAN_CAPABILITY_IS_STA_BSS(cap) \ (!((cap) & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS))) #define WLAN_CAPABILITY_CF_POLLABLE (1<<2) #define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) #define WLAN_CAPABILITY_PRIVACY (1<<4) #define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) #define WLAN_CAPABILITY_PBCC (1<<6) #define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) /* 802.11h */ #define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) #define WLAN_CAPABILITY_QOS (1<<9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) #define WLAN_CAPABILITY_DSSS_OFDM (1<<13) /* DMG (60gHz) 802.11ad */ /* type - bits 0..1 */ #define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */ #define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */ #define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */ #define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2) #define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3) #define WLAN_CAPABILITY_DMG_PRIVACY (1<<4) #define WLAN_CAPABILITY_DMG_ECPAC (1<<5) #define WLAN_CAPABILITY_DMG_SPECTRUM_MGMT (1<<8) #define WLAN_CAPABILITY_DMG_RADIO_MEASURE (1<<12) /* measurement */ #define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0) #define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1) #define IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED (1<<2) #define IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC 0 #define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1 #define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2 /* 802.11g ERP information element */ #define WLAN_ERP_NON_ERP_PRESENT (1<<0) #define WLAN_ERP_USE_PROTECTION (1<<1) #define WLAN_ERP_BARKER_PREAMBLE (1<<2) /* WLAN_ERP_BARKER_PREAMBLE values */ enum { WLAN_ERP_PREAMBLE_SHORT = 0, WLAN_ERP_PREAMBLE_LONG = 1, }; /* Band ID, 802.11ad #8.4.1.45 */ enum { IEEE80211_BANDID_TV_WS = 0, /* TV white spaces */ IEEE80211_BANDID_SUB1 = 1, /* Sub-1 GHz (excluding TV white spaces) */ IEEE80211_BANDID_2G = 2, /* 2.4 GHz */ IEEE80211_BANDID_3G = 3, /* 3.6 GHz */ IEEE80211_BANDID_5G = 4, /* 4.9 and 5 GHz */ IEEE80211_BANDID_60G = 5, /* 60 GHz */ }; /* Status codes */ enum ieee80211_statuscode { WLAN_STATUS_SUCCESS = 0, WLAN_STATUS_UNSPECIFIED_FAILURE = 1, WLAN_STATUS_CAPS_UNSUPPORTED = 10, WLAN_STATUS_REASSOC_NO_ASSOC = 11, WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12, WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13, WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14, WLAN_STATUS_CHALLENGE_FAIL = 15, WLAN_STATUS_AUTH_TIMEOUT = 16, WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17, WLAN_STATUS_ASSOC_DENIED_RATES = 18, /* 802.11b */ WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19, WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20, WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21, /* 802.11h */ WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22, WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23, WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24, /* 802.11g */ WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25, WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26, /* 802.11w */ WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY = 30, WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION = 31, /* 802.11i */ WLAN_STATUS_INVALID_IE = 40, WLAN_STATUS_INVALID_GROUP_CIPHER = 41, WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42, WLAN_STATUS_INVALID_AKMP = 43, WLAN_STATUS_UNSUPP_RSN_VERSION = 44, WLAN_STATUS_INVALID_RSN_IE_CAP = 45, WLAN_STATUS_CIPHER_SUITE_REJECTED = 46, /* 802.11e */ WLAN_STATUS_UNSPECIFIED_QOS = 32, WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33, WLAN_STATUS_ASSOC_DENIED_LOWACK = 34, WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35, WLAN_STATUS_REQUEST_DECLINED = 37, WLAN_STATUS_INVALID_QOS_PARAM = 38, WLAN_STATUS_CHANGE_TSPEC = 39, WLAN_STATUS_WAIT_TS_DELAY = 47, WLAN_STATUS_NO_DIRECT_LINK = 48, WLAN_STATUS_STA_NOT_PRESENT = 49, WLAN_STATUS_STA_NOT_QSTA = 50, /* 802.11s */ WLAN_STATUS_ANTI_CLOG_REQUIRED = 76, WLAN_STATUS_FCG_NOT_SUPP = 78, WLAN_STATUS_STA_NO_TBTT = 78, /* 802.11ad */ WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES = 39, WLAN_STATUS_REJECTED_FOR_DELAY_PERIOD = 47, WLAN_STATUS_REJECT_WITH_SCHEDULE = 83, WLAN_STATUS_PENDING_ADMITTING_FST_SESSION = 86, WLAN_STATUS_PERFORMING_FST_NOW = 87, WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW = 88, WLAN_STATUS_REJECT_U_PID_SETTING = 89, WLAN_STATUS_REJECT_DSE_BAND = 96, WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99, WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103, }; /* Reason codes */ enum ieee80211_reasoncode { WLAN_REASON_UNSPECIFIED = 1, WLAN_REASON_PREV_AUTH_NOT_VALID = 2, WLAN_REASON_DEAUTH_LEAVING = 3, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4, WLAN_REASON_DISASSOC_AP_BUSY = 5, WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7, WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8, WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9, /* 802.11h */ WLAN_REASON_DISASSOC_BAD_POWER = 10, WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11, /* 802.11i */ WLAN_REASON_INVALID_IE = 13, WLAN_REASON_MIC_FAILURE = 14, WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16, WLAN_REASON_IE_DIFFERENT = 17, WLAN_REASON_INVALID_GROUP_CIPHER = 18, WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19, WLAN_REASON_INVALID_AKMP = 20, WLAN_REASON_UNSUPP_RSN_VERSION = 21, WLAN_REASON_INVALID_RSN_IE_CAP = 22, WLAN_REASON_IEEE8021X_FAILED = 23, WLAN_REASON_CIPHER_SUITE_REJECTED = 24, /* 802.11e */ WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32, WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33, WLAN_REASON_DISASSOC_LOW_ACK = 34, WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35, WLAN_REASON_QSTA_LEAVE_QBSS = 36, WLAN_REASON_QSTA_NOT_USE = 37, WLAN_REASON_QSTA_REQUIRE_SETUP = 38, WLAN_REASON_QSTA_TIMEOUT = 39, WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45, /* 802.11s */ WLAN_REASON_MESH_PEER_CANCELED = 52, WLAN_REASON_MESH_MAX_PEERS = 53, WLAN_REASON_MESH_CONFIG = 54, WLAN_REASON_MESH_CLOSE = 55, WLAN_REASON_MESH_MAX_RETRIES = 56, WLAN_REASON_MESH_CONFIRM_TIMEOUT = 57, WLAN_REASON_MESH_INVALID_GTK = 58, WLAN_REASON_MESH_INCONSISTENT_PARAM = 59, WLAN_REASON_MESH_INVALID_SECURITY = 60, WLAN_REASON_MESH_PATH_ERROR = 61, WLAN_REASON_MESH_PATH_NOFORWARD = 62, WLAN_REASON_MESH_PATH_DEST_UNREACHABLE = 63, WLAN_REASON_MAC_EXISTS_IN_MBSS = 64, WLAN_REASON_MESH_CHAN_REGULATORY = 65, WLAN_REASON_MESH_CHAN = 66, }; /* Information Element IDs */ enum ieee80211_eid { WLAN_EID_SSID = 0, WLAN_EID_SUPP_RATES = 1, WLAN_EID_FH_PARAMS = 2, WLAN_EID_DS_PARAMS = 3, WLAN_EID_CF_PARAMS = 4, WLAN_EID_TIM = 5, WLAN_EID_IBSS_PARAMS = 6, WLAN_EID_CHALLENGE = 16, WLAN_EID_COUNTRY = 7, WLAN_EID_HP_PARAMS = 8, WLAN_EID_HP_TABLE = 9, WLAN_EID_REQUEST = 10, WLAN_EID_QBSS_LOAD = 11, WLAN_EID_EDCA_PARAM_SET = 12, WLAN_EID_TSPEC = 13, WLAN_EID_TCLAS = 14, WLAN_EID_SCHEDULE = 15, WLAN_EID_TS_DELAY = 43, WLAN_EID_TCLAS_PROCESSING = 44, WLAN_EID_QOS_CAPA = 46, /* 802.11z */ WLAN_EID_LINK_ID = 101, /* 802.11s */ WLAN_EID_MESH_CONFIG = 113, WLAN_EID_MESH_ID = 114, WLAN_EID_LINK_METRIC_REPORT = 115, WLAN_EID_CONGESTION_NOTIFICATION = 116, WLAN_EID_PEER_MGMT = 117, WLAN_EID_CHAN_SWITCH_PARAM = 118, WLAN_EID_MESH_AWAKE_WINDOW = 119, WLAN_EID_BEACON_TIMING = 120, WLAN_EID_MCCAOP_SETUP_REQ = 121, WLAN_EID_MCCAOP_SETUP_RESP = 122, WLAN_EID_MCCAOP_ADVERT = 123, WLAN_EID_MCCAOP_TEARDOWN = 124, WLAN_EID_GANN = 125, WLAN_EID_RANN = 126, WLAN_EID_PREQ = 130, WLAN_EID_PREP = 131, WLAN_EID_PERR = 132, WLAN_EID_PXU = 137, WLAN_EID_PXUC = 138, WLAN_EID_AUTH_MESH_PEER_EXCH = 139, WLAN_EID_MIC = 140, WLAN_EID_PWR_CONSTRAINT = 32, WLAN_EID_PWR_CAPABILITY = 33, WLAN_EID_TPC_REQUEST = 34, WLAN_EID_TPC_REPORT = 35, WLAN_EID_SUPPORTED_CHANNELS = 36, WLAN_EID_CHANNEL_SWITCH = 37, WLAN_EID_MEASURE_REQUEST = 38, WLAN_EID_MEASURE_REPORT = 39, WLAN_EID_QUIET = 40, WLAN_EID_IBSS_DFS = 41, WLAN_EID_ERP_INFO = 42, WLAN_EID_EXT_SUPP_RATES = 50, WLAN_EID_HT_CAPABILITY = 45, WLAN_EID_HT_OPERATION = 61, WLAN_EID_RSN = 48, WLAN_EID_MMIE = 76, WLAN_EID_WPA = 221, WLAN_EID_GENERIC = 221, WLAN_EID_VENDOR_SPECIFIC = 221, WLAN_EID_QOS_PARAMETER = 222, WLAN_EID_AP_CHAN_REPORT = 51, WLAN_EID_NEIGHBOR_REPORT = 52, WLAN_EID_RCPI = 53, WLAN_EID_BSS_AVG_ACCESS_DELAY = 63, WLAN_EID_ANTENNA_INFO = 64, WLAN_EID_RSNI = 65, WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66, WLAN_EID_BSS_AVAILABLE_CAPACITY = 67, WLAN_EID_BSS_AC_ACCESS_DELAY = 68, WLAN_EID_RRM_ENABLED_CAPABILITIES = 70, WLAN_EID_MULTIPLE_BSSID = 71, WLAN_EID_BSS_COEX_2040 = 72, WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74, WLAN_EID_EXT_CAPABILITY = 127, WLAN_EID_MOBILITY_DOMAIN = 54, WLAN_EID_FAST_BSS_TRANSITION = 55, WLAN_EID_TIMEOUT_INTERVAL = 56, WLAN_EID_RIC_DATA = 57, WLAN_EID_RIC_DESCRIPTOR = 75, WLAN_EID_DSE_REGISTERED_LOCATION = 58, WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59, WLAN_EID_EXT_CHANSWITCH_ANN = 60, WLAN_EID_VHT_CAPABILITY = 191, WLAN_EID_VHT_OPERATION = 192, /* 802.11ad */ WLAN_EID_NON_TX_BSSID_CAP = 83, WLAN_EID_WAKEUP_SCHEDULE = 143, WLAN_EID_EXT_SCHEDULE = 144, WLAN_EID_STA_AVAILABILITY = 145, WLAN_EID_DMG_TSPEC = 146, WLAN_EID_DMG_AT = 147, WLAN_EID_DMG_CAP = 148, WLAN_EID_DMG_OPERATION = 151, WLAN_EID_DMG_BSS_PARAM_CHANGE = 152, WLAN_EID_DMG_BEAM_REFINEMENT = 153, WLAN_EID_CHANNEL_MEASURE_FEEDBACK = 154, WLAN_EID_AWAKE_WINDOW = 157, WLAN_EID_MULTI_BAND = 158, WLAN_EID_ADDBA_EXT = 159, WLAN_EID_NEXT_PCP_LIST = 160, WLAN_EID_PCP_HANDOVER = 161, WLAN_EID_DMG_LINK_MARGIN = 162, WLAN_EID_SWITCHING_STREAM = 163, WLAN_EID_SESSION_TRANSITION = 164, WLAN_EID_DYN_TONE_PAIRING_REPORT = 165, WLAN_EID_CLUSTER_REPORT = 166, WLAN_EID_RELAY_CAP = 167, WLAN_EID_RELAY_XFER_PARAM_SET = 168, WLAN_EID_BEAM_LINK_MAINT = 169, WLAN_EID_MULTIPLE_MAC_ADDR = 170, WLAN_EID_U_PID = 171, WLAN_EID_DMG_LINK_ADAPT_ACK = 172, WLAN_EID_QUIET_PERIOD_REQ = 175, WLAN_EID_QUIET_PERIOD_RESP = 177, WLAN_EID_EPAC_POLICY = 182, WLAN_EID_CLISTER_TIME_OFF = 183, WLAN_EID_ANTENNA_SECTOR_ID_PATTERN = 190, }; /* Action category code */ enum ieee80211_category { WLAN_CATEGORY_SPECTRUM_MGMT = 0, WLAN_CATEGORY_QOS = 1, WLAN_CATEGORY_DLS = 2, WLAN_CATEGORY_BACK = 3, WLAN_CATEGORY_PUBLIC = 4, WLAN_CATEGORY_HT = 7, WLAN_CATEGORY_SA_QUERY = 8, WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, WLAN_CATEGORY_TDLS = 12, WLAN_CATEGORY_MESH_ACTION = 13, WLAN_CATEGORY_MULTIHOP_ACTION = 14, WLAN_CATEGORY_SELF_PROTECTED = 15, WLAN_CATEGORY_DMG = 16, WLAN_CATEGORY_WMM = 17, WLAN_CATEGORY_FST = 18, WLAN_CATEGORY_UNPROT_DMG = 20, WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, WLAN_CATEGORY_VENDOR_SPECIFIC = 127, }; /* SPECTRUM_MGMT action code */ enum ieee80211_spectrum_mgmt_actioncode { WLAN_ACTION_SPCT_MSR_REQ = 0, WLAN_ACTION_SPCT_MSR_RPRT = 1, WLAN_ACTION_SPCT_TPC_REQ = 2, WLAN_ACTION_SPCT_TPC_RPRT = 3, WLAN_ACTION_SPCT_CHL_SWITCH = 4, }; /* HT action codes */ enum ieee80211_ht_actioncode { WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0, WLAN_HT_ACTION_SMPS = 1, WLAN_HT_ACTION_PSMP = 2, WLAN_HT_ACTION_PCO_PHASE = 3, WLAN_HT_ACTION_CSI = 4, WLAN_HT_ACTION_NONCOMPRESSED_BF = 5, WLAN_HT_ACTION_COMPRESSED_BF = 6, WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7, }; /* Self Protected Action codes */ enum ieee80211_self_protected_actioncode { WLAN_SP_RESERVED = 0, WLAN_SP_MESH_PEERING_OPEN = 1, WLAN_SP_MESH_PEERING_CONFIRM = 2, WLAN_SP_MESH_PEERING_CLOSE = 3, WLAN_SP_MGK_INFORM = 4, WLAN_SP_MGK_ACK = 5, }; /* Mesh action codes */ enum ieee80211_mesh_actioncode { WLAN_MESH_ACTION_LINK_METRIC_REPORT, WLAN_MESH_ACTION_HWMP_PATH_SELECTION, WLAN_MESH_ACTION_GATE_ANNOUNCEMENT, WLAN_MESH_ACTION_CONGESTION_CONTROL_NOTIFICATION, WLAN_MESH_ACTION_MCCA_SETUP_REQUEST, WLAN_MESH_ACTION_MCCA_SETUP_REPLY, WLAN_MESH_ACTION_MCCA_ADVERTISEMENT_REQUEST, WLAN_MESH_ACTION_MCCA_ADVERTISEMENT, WLAN_MESH_ACTION_MCCA_TEARDOWN, WLAN_MESH_ACTION_TBTT_ADJUSTMENT_REQUEST, WLAN_MESH_ACTION_TBTT_ADJUSTMENT_RESPONSE, }; /* Security key length */ enum ieee80211_key_len { WLAN_KEY_LEN_WEP40 = 5, WLAN_KEY_LEN_WEP104 = 13, WLAN_KEY_LEN_CCMP = 16, WLAN_KEY_LEN_TKIP = 32, WLAN_KEY_LEN_AES_CMAC = 16, }; /* Public action codes */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_TDLS_DISCOVER_RES = 14, }; /* TDLS action codes */ enum ieee80211_tdls_actioncode { WLAN_TDLS_SETUP_REQUEST = 0, WLAN_TDLS_SETUP_RESPONSE = 1, WLAN_TDLS_SETUP_CONFIRM = 2, WLAN_TDLS_TEARDOWN = 3, WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4, WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5, WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6, WLAN_TDLS_PEER_PSM_REQUEST = 7, WLAN_TDLS_PEER_PSM_RESPONSE = 8, WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9, WLAN_TDLS_DISCOVERY_REQUEST = 10, }; /* * TDLS capabililites to be enabled in the 5th byte of the * @WLAN_EID_EXT_CAPABILITY information element */ #define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5) #define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) /* TDLS specific payload type in the LLC/SNAP header */ #define WLAN_TDLS_SNAP_RFTYPE 0x2 /** * enum - mesh synchronization method identifier * * @IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET: the default synchronization method * @IEEE80211_SYNC_METHOD_VENDOR: a vendor specific synchronization method * that will be specified in a vendor specific information element */ enum { IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET = 1, IEEE80211_SYNC_METHOD_VENDOR = 255, }; /** * enum - mesh path selection protocol identifier * * @IEEE80211_PATH_PROTOCOL_HWMP: the default path selection protocol * @IEEE80211_PATH_PROTOCOL_VENDOR: a vendor specific protocol that will * be specified in a vendor specific information element */ enum { IEEE80211_PATH_PROTOCOL_HWMP = 1, IEEE80211_PATH_PROTOCOL_VENDOR = 255, }; /** * enum - mesh path selection metric identifier * * @IEEE80211_PATH_METRIC_AIRTIME: the default path selection metric * @IEEE80211_PATH_METRIC_VENDOR: a vendor specific metric that will be * specified in a vendor specific information element */ enum { IEEE80211_PATH_METRIC_AIRTIME = 1, IEEE80211_PATH_METRIC_VENDOR = 255, }; /** * enum ieee80211_root_mode_identifier - root mesh STA mode identifier * * These attribute are used by dot11MeshHWMPRootMode to set root mesh STA mode * * @IEEE80211_ROOTMODE_NO_ROOT: the mesh STA is not a root mesh STA (default) * @IEEE80211_ROOTMODE_ROOT: the mesh STA is a root mesh STA if greater than * this value * @IEEE80211_PROACTIVE_PREQ_NO_PREP: the mesh STA is a root mesh STA supports * the proactive PREQ with proactive PREP subfield set to 0 * @IEEE80211_PROACTIVE_PREQ_WITH_PREP: the mesh STA is a root mesh STA * supports the proactive PREQ with proactive PREP subfield set to 1 * @IEEE80211_PROACTIVE_RANN: the mesh STA is a root mesh STA supports * the proactive RANN */ enum ieee80211_root_mode_identifier { IEEE80211_ROOTMODE_NO_ROOT = 0, IEEE80211_ROOTMODE_ROOT = 1, IEEE80211_PROACTIVE_PREQ_NO_PREP = 2, IEEE80211_PROACTIVE_PREQ_WITH_PREP = 3, IEEE80211_PROACTIVE_RANN = 4, }; /* * IEEE 802.11-2007 7.3.2.9 Country information element * * Minimum length is 8 octets, ie len must be evenly * divisible by 2 */ /* Although the spec says 8 I'm seeing 6 in practice */ #define IEEE80211_COUNTRY_IE_MIN_LEN 6 /* The Country String field of the element shall be 3 octets in length */ #define IEEE80211_COUNTRY_STRING_LEN 3 /* * For regulatory extension stuff see IEEE 802.11-2007 * Annex I (page 1141) and Annex J (page 1147). Also * review 7.3.2.9. * * When dot11RegulatoryClassesRequired is true and the * first_channel/reg_extension_id is >= 201 then the IE * compromises of the 'ext' struct represented below: * * - Regulatory extension ID - when generating IE this just needs * to be monotonically increasing for each triplet passed in * the IE * - Regulatory class - index into set of rules * - Coverage class - index into air propagation time (Table 7-27), * in microseconds, you can compute the air propagation time from * the index by multiplying by 3, so index 10 yields a propagation * of 10 us. Valid values are 0-31, values 32-255 are not defined * yet. A value of 0 inicates air propagation of <= 1 us. * * See also Table I.2 for Emission limit sets and table * I.3 for Behavior limit sets. Table J.1 indicates how to map * a reg_class to an emission limit set and behavior limit set. */ #define IEEE80211_COUNTRY_EXTENSION_ID 201 /* * Channels numbers in the IE must be monotonically increasing * if dot11RegulatoryClassesRequired is not true. * * If dot11RegulatoryClassesRequired is true consecutive * subband triplets following a regulatory triplet shall * have monotonically increasing first_channel number fields. * * Channel numbers shall not overlap. * * Note that max_power is signed. */ struct ieee80211_country_ie_triplet { union { struct { u8 first_channel; u8 num_channels; s8 max_power; } __attribute__ ((packed)) chans; struct { u8 reg_extension_id; u8 reg_class; u8 coverage_class; } __attribute__ ((packed)) ext; }; } __attribute__ ((packed)); enum ieee80211_timeout_interval_type { WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */, WLAN_TIMEOUT_KEY_LIFETIME = 2 /* 802.11r */, WLAN_TIMEOUT_ASSOC_COMEBACK = 3 /* 802.11w */, }; /* BACK action code */ enum ieee80211_back_actioncode { WLAN_ACTION_ADDBA_REQ = 0, WLAN_ACTION_ADDBA_RESP = 1, WLAN_ACTION_DELBA = 2, }; /* BACK (block-ack) parties */ enum ieee80211_back_parties { WLAN_BACK_RECIPIENT = 0, WLAN_BACK_INITIATOR = 1, }; /* SA Query action */ enum ieee80211_sa_query_action { WLAN_ACTION_SA_QUERY_REQUEST = 0, WLAN_ACTION_SA_QUERY_RESPONSE = 1, }; /* cipher suite selectors */ #define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 #define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 #define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 /* reserved: 0x000FAC03 */ #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 #define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 #define WLAN_CIPHER_SUITE_SMS4 0x00147201 /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 #define WLAN_AKM_SUITE_SAE 0x000FAC08 #define WLAN_AKM_SUITE_FT_OVER_SAE 0x000FAC09 #define WLAN_MAX_KEY_LEN 32 #define WLAN_PMKID_LEN 16 #define WLAN_OUI_WFA 0x506f9a #define WLAN_OUI_TYPE_WFA_P2P 9 #define WLAN_OUI_MICROSOFT 0x0050f2 #define WLAN_OUI_TYPE_MICROSOFT_WPA 1 #define WLAN_OUI_TYPE_MICROSOFT_WMM 2 #define WLAN_OUI_TYPE_MICROSOFT_WPS 4 /* * WMM/802.11e Tspec Element */ #define IEEE80211_WMM_IE_TSPEC_TID_MASK 0x0F #define IEEE80211_WMM_IE_TSPEC_TID_SHIFT 1 enum ieee80211_tspec_status_code { IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED = 0, IEEE80211_TSPEC_STATUS_ADDTS_INVAL_PARAMS = 0x1, }; struct ieee80211_tspec_ie { u8 element_id; u8 len; u8 oui[3]; u8 oui_type; u8 oui_subtype; u8 version; __le16 tsinfo; u8 tsinfo_resvd; __le16 nominal_msdu; __le16 max_msdu; __le32 min_service_int; __le32 max_service_int; __le32 inactivity_int; __le32 suspension_int; __le32 service_start_time; __le32 min_data_rate; __le32 mean_data_rate; __le32 peak_data_rate; __le32 max_burst_size; __le32 delay_bound; __le32 min_phy_rate; __le16 sba; __le16 medium_time; } __packed; /** * ieee80211_get_qos_ctl - get pointer to qos control bytes * @hdr: the frame * * The qos ctrl bytes come after the frame_control, duration, seq_num * and 3 or 4 addresses of length ETH_ALEN. * 3 addr: 2 + 2 + 2 + 3*6 = 24 * 4 addr: 2 + 2 + 2 + 4*6 = 30 */ static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) { if (ieee80211_has_a4(hdr->frame_control)) return (u8 *)hdr + 30; else return (u8 *)hdr + 24; } /** * ieee80211_get_SA - get pointer to SA * @hdr: the frame * * Given an 802.11 frame, this function returns the offset * to the source address (SA). It does not verify that the * header is long enough to contain the address, and the * header must be long enough to contain the frame control * field. */ static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) { if (ieee80211_has_a4(hdr->frame_control)) return hdr->addr4; if (ieee80211_has_fromds(hdr->frame_control)) return hdr->addr3; return hdr->addr2; } /** * ieee80211_get_DA - get pointer to DA * @hdr: the frame * * Given an 802.11 frame, this function returns the offset * to the destination address (DA). It does not verify that * the header is long enough to contain the address, and the * header must be long enough to contain the frame control * field. */ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) { if (ieee80211_has_tods(hdr->frame_control)) return hdr->addr3; else return hdr->addr1; } /** * ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame * @hdr: the frame (buffer must include at least the first octet of payload) */ static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr) { if (ieee80211_is_disassoc(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control)) return true; if (ieee80211_is_action(hdr->frame_control)) { u8 *category; /* * Action frames, excluding Public Action frames, are Robust * Management Frames. However, if we are looking at a Protected * frame, skip the check since the data may be encrypted and * the frame has already been found to be a Robust Management * Frame (by the other end). */ if (ieee80211_has_protected(hdr->frame_control)) return true; category = ((u8 *) hdr) + 24; return *category != WLAN_CATEGORY_PUBLIC && *category != WLAN_CATEGORY_HT && *category != WLAN_CATEGORY_SELF_PROTECTED && *category != WLAN_CATEGORY_VENDOR_SPECIFIC; } return false; } /** * ieee80211_is_public_action - check if frame is a public action frame * @hdr: the frame * @len: length of the frame */ static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr, size_t len) { struct ieee80211_mgmt *mgmt = (void *)hdr; if (len < IEEE80211_MIN_ACTION_SIZE) return false; if (!ieee80211_is_action(hdr->frame_control)) return false; return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC; } /** * ieee80211_fhss_chan_to_freq - get channel frequency * @channel: the FHSS channel * * Convert IEEE802.11 FHSS channel to frequency (MHz) * Ref IEEE 802.11-2007 section 14.6 */ static inline int ieee80211_fhss_chan_to_freq(int channel) { if ((channel > 1) && (channel < 96)) return channel + 2400; else return -1; } /** * ieee80211_freq_to_fhss_chan - get channel * @freq: the channels frequency * * Convert frequency (MHz) to IEEE802.11 FHSS channel * Ref IEEE 802.11-2007 section 14.6 */ static inline int ieee80211_freq_to_fhss_chan(int freq) { if ((freq > 2401) && (freq < 2496)) return freq - 2400; else return -1; } /** * ieee80211_dsss_chan_to_freq - get channel center frequency * @channel: the DSSS channel * * Convert IEEE802.11 DSSS channel to the center frequency (MHz). * Ref IEEE 802.11-2007 section 15.6 */ static inline int ieee80211_dsss_chan_to_freq(int channel) { if ((channel > 0) && (channel < 14)) return 2407 + (channel * 5); else if (channel == 14) return 2484; else return -1; } /** * ieee80211_freq_to_dsss_chan - get channel * @freq: the frequency * * Convert frequency (MHz) to IEEE802.11 DSSS channel * Ref IEEE 802.11-2007 section 15.6 * * This routine selects the channel with the closest center frequency. */ static inline int ieee80211_freq_to_dsss_chan(int freq) { if ((freq >= 2410) && (freq < 2475)) return (freq - 2405) / 5; else if ((freq >= 2482) && (freq < 2487)) return 14; else return -1; } /* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back * Ref IEEE 802.11-2007 section 18.4.6.2 * * The channels and frequencies are the same as those defined for DSSS */ #define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan) #define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq) /* Convert IEEE802.11 ERP channel to frequency (MHz) and back * Ref IEEE 802.11-2007 section 19.4.2 */ #define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan) #define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq) /** * ieee80211_ofdm_chan_to_freq - get channel center frequency * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz * @channel: the OFDM channel * * Convert IEEE802.11 OFDM channel to center frequency (MHz) * Ref IEEE 802.11-2007 section 17.3.8.3.2 */ static inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel) { if ((channel > 0) && (channel <= 200) && (s_freq >= 4000)) return s_freq + (channel * 5); else return -1; } /** * ieee80211_freq_to_ofdm_channel - get channel * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz * @freq: the frequency * * Convert frequency (MHz) to IEEE802.11 OFDM channel * Ref IEEE 802.11-2007 section 17.3.8.3.2 * * This routine selects the channel with the closest center frequency. */ static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq) { if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) && (s_freq >= 4000)) return (freq + 2 - s_freq) / 5; else return -1; } /** * ieee80211_tu_to_usec - convert time units (TU) to microseconds * @tu: the TUs */ static inline unsigned long ieee80211_tu_to_usec(unsigned long tu) { return 1024 * tu; } /** * ieee80211_check_tim - check if AID bit is set in TIM * @tim: the TIM IE * @tim_len: length of the TIM IE * @aid: the AID to look for */ static inline bool ieee80211_check_tim(struct ieee80211_tim_ie *tim, u8 tim_len, u16 aid) { u8 mask; u8 index, indexn1, indexn2; if (unlikely(!tim || tim_len < sizeof(*tim))) return false; aid &= 0x3fff; index = aid / 8; mask = 1 << (aid & 7); indexn1 = tim->bitmap_ctrl & 0xfe; indexn2 = tim_len + indexn1 - 4; if (index < indexn1 || index > indexn2) return false; index -= indexn1; return !!(tim->virtual_map[index] & mask); } #endif /* LINUX_IEEE80211_H */ compat-drivers-2012-09-18/include/linux/compat-3.6.h0000644000175000017500000000625312025673354021126 0ustar mcgrofmcgrof#ifndef LINUX_3_6_COMPAT_H #define LINUX_3_6_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) /** * Backports * * commit d81a5d1956731c453b85c141458d4ff5d6cc5366 * Author: Gustavo Padovan * Date: Tue Jul 10 19:10:06 2012 -0300 * * USB: add USB_VENDOR_AND_INTERFACE_INFO() macro */ #include #define USB_VENDOR_AND_INTERFACE_INFO(vend, cl, sc, pr) \ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ | USB_DEVICE_ID_MATCH_VENDOR, \ .idVendor = (vend), \ .bInterfaceClass = (cl), \ .bInterfaceSubClass = (sc), \ .bInterfaceProtocol = (pr) /** * Backports * * commit cdcac9cd7741af2c2b9255cbf060f772596907bb * Author: Dave Airlie * Date: Wed Jun 27 08:35:52 2012 +0100 * * pci_regs: define LNKSTA2 pcie cap + bits. * * We need these for detecting the max link speed for drm drivers. * * Acked-by: Bjorn Helgaas * Signed-off-by: Dave Airlie */ #define PCI_EXP_LNKCAP2 44 /* Link Capability 2 */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x04 /* Current Link Speed 8.0GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x100 /* Crosslink supported */ #include #include /** * eth_broadcast_addr - Assign broadcast address * @addr: Pointer to a six-byte array containing the Ethernet address * * Assign the broadcast address to the given address array. */ static inline void eth_broadcast_addr(u8 *addr) { memset(addr, 0xff, ETH_ALEN); } /** * eth_random_addr - Generate software assigned random Ethernet address * @addr: Pointer to a six-byte array containing the Ethernet address * * Generate a random Ethernet address (MAC) that is not multicast * and has the local assigned bit set. */ static inline void eth_random_addr(u8 *addr) { get_random_bytes(addr, ETH_ALEN); addr[0] &= 0xfe; /* clear multicast bit */ addr[0] |= 0x02; /* set local assignment bit (IEEE802) */ } #define GENLMSG_DEFAULT_SIZE (NLMSG_DEFAULT_SIZE - GENL_HDRLEN) /* * Backports * * commit 959d62fa865d2e616b61a509e1cc5b88741f065e * Author: Shuah Khan * Date: Thu Jun 14 04:34:30 2012 +0800 * * leds: Rename led_brightness_set() to led_set_brightness() * * Rename leds external interface led_brightness_set() to led_set_brightness(). * This is the second phase of the change to reduce confusion between the * leds internal and external interfaces that set brightness. With this change, * now the external interface is led_set_brightness(). The first phase renamed * the internal interface led_set_brightness() to __led_set_brightness(). * There are no changes to the interface implementations. * * Signed-off-by: Shuah Khan * Signed-off-by: Bryan Wu */ #define led_set_brightness(_dev, _switch) led_brightness_set(_dev, _switch) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) */ #endif /* LINUX_3_6_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.h0000644000175000017500000000376112025673354021126 0ustar mcgrofmcgrof#ifndef LINUX_26_COMPAT_H #define LINUX_26_COMPAT_H #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) #include #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) #include #else #include #endif #include #include /* * The define overwriting module_init is based on the original module_init * which looks like this: * #define module_init(initfn) \ * static inline initcall_t __inittest(void) \ * { return initfn; } \ * int init_module(void) __attribute__((alias(#initfn))); * * To the call to the initfn we added the symbol dependency on compat * to make sure that compat.ko gets loaded for any compat modules. */ void compat_dependency_symbol(void); #undef module_init #define module_init(initfn) \ static int __init __init_compat(void) \ { \ compat_dependency_symbol(); \ return initfn(); \ } \ int init_module(void) __attribute__((alias("__init_compat"))); /* * Each compat file represents compatibility code for new kernel * code introduced for *that* kernel revision. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* LINUX_26_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.14.h0000644000175000017500000000044212025673354021342 0ustar mcgrofmcgrof#ifndef LINUX_26_14_COMPAT_H #define LINUX_26_14_COMPAT_H #include /* Compat work for 2.6.14 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)) typedef unsigned int gfp_t; #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)) */ #endif /* LINUX_26_14_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.22.h0000644000175000017500000000430012025673354021336 0ustar mcgrofmcgrof#ifndef LINUX_26_22_COMPAT_H #define LINUX_26_22_COMPAT_H #include /* Compat work for 2.6.21 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) #include #include /* reuse ax25_ptr */ #define ieee80211_ptr ax25_ptr #ifdef CONFIG_AX25 #error Compat reuses the AX.25 pointer so that may not be enabled! #endif static inline unsigned char *skb_mac_header(const struct sk_buff *skb) { return skb->mac.raw; } static inline void skb_set_mac_header(struct sk_buff *skb, int offset) { skb->mac.raw = skb->data + offset; } static inline void skb_reset_mac_header(struct sk_buff *skb) { skb->mac.raw = skb->data; } static inline void skb_reset_network_header(struct sk_buff *skb) { skb->nh.raw = skb->data; } static inline void skb_set_network_header(struct sk_buff *skb, int offset) { skb->nh.raw = skb->data + offset; } static inline void skb_set_transport_header(struct sk_buff *skb, int offset) { skb->h.raw = skb->data + offset; } static inline unsigned char *skb_transport_header(struct sk_buff *skb) { return skb->h.raw; } static inline unsigned char *skb_network_header(const struct sk_buff *skb) { return skb->nh.raw; } static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) { return skb->tail; } static inline struct iphdr *ip_hdr(const struct sk_buff *skb) { return (struct iphdr *)skb_network_header(skb); } static inline void skb_copy_from_linear_data(const struct sk_buff *skb, void *to, const unsigned int len) { memcpy(to, skb->data, len); } static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb, const int offset, void *to, const unsigned int len) { memcpy(to, skb->data + offset, len); } #define __maybe_unused __attribute__((unused)) #define uninitialized_var(x) x = x /* This will lead to very weird behaviour... */ #define NLA_BINARY NLA_STRING static inline int pci_set_mwi(struct pci_dev *dev) { return -ENOSYS; } static inline void pci_clear_mwi(struct pci_dev *dev) { } #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) */ #endif /* LINUX_26_22_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.30.h0000644000175000017500000000207112025673354021340 0ustar mcgrofmcgrof#ifndef LINUX_26_30_COMPAT_H #define LINUX_26_30_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) #include #include #ifndef TP_PROTO #define TP_PROTO(args...) TPPROTO(args) #endif #ifndef TP_ARGS #define TP_ARGS(args...) TPARGS(args) #endif #define IRQ_WAKE_THREAD (2) /* From : include/linux/pm.h */ /* How to reorder dpm_list after device_move() */ enum dpm_order { DPM_ORDER_NONE, DPM_ORDER_DEV_AFTER_PARENT, DPM_ORDER_PARENT_BEFORE_DEV, DPM_ORDER_DEV_LAST, }; static inline void dev_set_uevent_suppress(struct device *dev, int val) { dev->uevent_suppress = val; } /* * Print a one-time message (analogous to WARN_ONCE() et al): */ #define printk_once(x...) ({ \ static bool __print_once; \ \ if (!__print_once) { \ __print_once = true; \ printk(x); \ } \ }) #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) */ #endif /* LINUX_26_30_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-3.2.h0000644000175000017500000000531612025673354021121 0ustar mcgrofmcgrof#ifndef LINUX_3_2_COMPAT_H #define LINUX_3_2_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) #include #include /* backports b4625dab */ #define SDIO_CCCR_REV_3_00 3 /* CCCR/FBR Version 3.00 */ #define SDIO_SDIO_REV_3_00 4 /* SDIO Spec Version 3.00 */ #define PMSG_IS_AUTO(msg) (((msg).event & PM_EVENT_AUTO) != 0) /* mask skb_frag_page as RHEL6 backports this */ #define skb_frag_page(a) compat_skb_frag_page(a) /** * skb_frag_page - retrieve the page refered to by a paged fragment * @frag: the paged fragment * * Returns the &struct page associated with @frag. */ static inline struct page *skb_frag_page(const skb_frag_t *frag) { return frag->page; } /* mask skb_frag_dma_map as RHEL6 backports this */ #define skb_frag_dma_map(a,b,c,d,e) compat_skb_frag_dma_map(a,b,c,d,e) /** * skb_frag_dma_map - maps a paged fragment via the DMA API * @device: the device to map the fragment to * @frag: the paged fragment to map * @offset: the offset within the fragment (starting at the * fragment's own offset) * @size: the number of bytes to map * @direction: the direction of the mapping (%PCI_DMA_*) * * Maps the page associated with @frag to @device. */ static inline dma_addr_t skb_frag_dma_map(struct device *dev, const skb_frag_t *frag, size_t offset, size_t size, enum dma_data_direction dir) { return dma_map_page(dev, skb_frag_page(frag), frag->page_offset + offset, size, dir); } #define ETH_P_TDLS 0x890D /* TDLS */ /* mask skb_frag_size as RHEL6 backports this */ #define skb_frag_size(a) compat_skb_frag_size(a) static inline unsigned int skb_frag_size(const skb_frag_t *frag) { return frag->size; } static inline char *hex_byte_pack(char *buf, u8 byte) { *buf++ = hex_asc_hi(byte); *buf++ = hex_asc_lo(byte); return buf; } /* module_platform_driver() - Helper macro for drivers that don't do * anything special in module init/exit. This eliminates a lot of * boilerplate. Each module may only use this macro once, and * calling it replaces module_init() and module_exit() */ #define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister) static inline void *dma_zalloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) { void *ret = dma_alloc_coherent(dev, size, dma_handle, flag); if (ret) memset(ret, 0, size); return ret; } extern int __netdev_printk(const char *level, const struct net_device *dev, struct va_format *vaf); #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) */ #endif /* LINUX_3_2_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.18.h0000644000175000017500000000047612025673354021355 0ustar mcgrofmcgrof#ifndef LINUX_26_18_COMPAT_H #define LINUX_26_18_COMPAT_H #include /* Compat work for 2.6.18 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) */ #endif /* LINUX_26_18_COMPAT_H */ compat-drivers-2012-09-18/include/linux/compat-2.6.37.h0000644000175000017500000001031312025673354021345 0ustar mcgrofmcgrof#ifndef LINUX_26_37_COMPAT_H #define LINUX_26_37_COMPAT_H #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) #include #include #include #include static inline int proto_ports_offset(int proto) { switch (proto) { case IPPROTO_TCP: case IPPROTO_UDP: case IPPROTO_DCCP: case IPPROTO_ESP: /* SPI */ case IPPROTO_SCTP: case IPPROTO_UDPLITE: return 0; case IPPROTO_AH: /* SPI */ return 4; default: return -EINVAL; } } #define SDIO_CLASS_BT_AMP 0x09 /* Type-A Bluetooth AMP interface */ extern struct kobj_ns_type_operations net_ns_type_operations; /* mask skb_checksum_none_assert as RHEL6 backports this */ #define skb_checksum_none_assert(a) compat_skb_checksum_none_assert(a) /** * skb_checksum_none_assert - make sure skb ip_summed is CHECKSUM_NONE * @skb: skb to check * * fresh skbs have their ip_summed set to CHECKSUM_NONE. * Instead of forcing ip_summed to CHECKSUM_NONE, we can * use this helper, to document places where we make this assertion. */ static inline void skb_checksum_none_assert(struct sk_buff *skb) { #ifdef DEBUG BUG_ON(skb->ip_summed != CHECKSUM_NONE); #endif } #define pcmcia_enable_device(link) pcmcia_request_configuration(link, &link->conf) #include struct compat_genl_info { struct genl_info *info; u32 snd_seq; u32 snd_pid; struct genlmsghdr *genlhdr; struct nlattr **attrs; void *user_ptr[2]; }; #define genl_info compat_genl_info struct compat_genl_ops { struct genl_ops ops; u8 cmd; u8 internal_flags; unsigned int flags; const struct nla_policy *policy; int (*doit)(struct sk_buff *skb, struct genl_info *info); int (*dumpit)(struct sk_buff *skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); }; #define genl_ops compat_genl_ops struct compat_genl_family { struct genl_family family; struct list_head list; unsigned int id, hdrsize, version, maxattr; const char *name; bool netnsok; struct nlattr **attrbuf; int (*pre_doit)(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); void (*post_doit)(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); }; #define genl_family compat_genl_family #define genl_register_family_with_ops compat_genl_register_family_with_ops int genl_register_family_with_ops(struct genl_family *family, struct genl_ops *ops, size_t n_ops); #define genl_unregister_family compat_genl_unregister_family int genl_unregister_family(struct genl_family *family); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) #define genl_info_net(_info) genl_info_net((_info)->info) #endif #define genlmsg_reply(_msg, _info) genlmsg_reply(_msg, (_info)->info) #define genlmsg_put(_skb, _pid, _seq, _fam, _flags, _cmd) genlmsg_put(_skb, _pid, _seq, &(_fam)->family, _flags, _cmd) #define genl_register_mc_group(_fam, _grp) genl_register_mc_group(&(_fam)->family, _grp) #define genl_unregister_mc_group(_fam, _grp) genl_unregister_mc_group(&(_fam)->family, _grp) extern void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off); #define led_classdev_unregister compat_led_classdev_unregister extern void compat_led_classdev_unregister(struct led_classdev *led_cdev); #define led_brightness_set compat_led_brightness_set extern void compat_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness); #define alloc_ordered_workqueue(name, flags) create_singlethread_workqueue(name) #define netdev_refcnt_read(a) atomic_read(&a->refcnt) /* mask vzalloc as RHEL6 backports this */ #define vzalloc(a) compat_vzalloc(a) extern void *vzalloc(unsigned long size); #define rtnl_dereference(p) \ rcu_dereference_protected(p, lockdep_rtnl_is_held()) /** * RCU_INIT_POINTER() - initialize an RCU protected pointer * * Initialize an RCU-protected pointer in such a way to avoid RCU-lockdep * splats. */ #define RCU_INIT_POINTER(p, v) \ p = (typeof(*v) __force __rcu *)(v) static inline bool skb_has_frag_list(const struct sk_buff *skb) { return skb_shinfo(skb)->frag_list != NULL; } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) */ #endif /* LINUX_26_37_COMPAT_H */ compat-drivers-2012-09-18/include/linux/kmemleak.h0000644000175000017500000000025212025673354021116 0ustar mcgrofmcgrof#include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)) #include_next #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) */ compat-drivers-2012-09-18/include/net/0000755000175000017500000000000012026211315016571 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/net/mac80211.h0000644000175000017500000045733312026211315020115 0ustar mcgrofmcgrof/* * mac80211 <-> driver interface * * Copyright 2002-2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #ifndef MAC80211_H #define MAC80211_H #include #include #include #include #include #include #include /** * DOC: Introduction * * mac80211 is the Linux stack for 802.11 hardware that implements * only partial functionality in hard- or firmware. This document * defines the interface between mac80211 and low-level hardware * drivers. */ /** * DOC: Calling mac80211 from interrupts * * Only ieee80211_tx_status_irqsafe() and ieee80211_rx_irqsafe() can be * called in hardware interrupt context. The low-level driver must not call any * other functions in hardware interrupt context. If there is a need for such * call, the low-level driver should first ACK the interrupt and perform the * IEEE 802.11 code call after this, e.g. from a scheduled workqueue or even * tasklet function. * * NOTE: If the driver opts to use the _irqsafe() functions, it may not also * use the non-IRQ-safe functions! */ /** * DOC: Warning * * If you're reading this document and not the header file itself, it will * be incomplete because not all documentation has been converted yet. */ /** * DOC: Frame format * * As a general rule, when frames are passed between mac80211 and the driver, * they start with the IEEE 802.11 header and include the same octets that are * sent over the air except for the FCS which should be calculated by the * hardware. * * There are, however, various exceptions to this rule for advanced features: * * The first exception is for hardware encryption and decryption offload * where the IV/ICV may or may not be generated in hardware. * * Secondly, when the hardware handles fragmentation, the frame handed to * the driver from mac80211 is the MSDU, not the MPDU. * * Finally, for received frames, the driver is able to indicate that it has * filled a radiotap header and put that in front of the frame; if it does * not do so then mac80211 may add this under certain circumstances. */ /** * DOC: mac80211 workqueue * * mac80211 provides its own workqueue for drivers and internal mac80211 use. * The workqueue is a single threaded workqueue and can only be accessed by * helpers for sanity checking. Drivers must ensure all work added onto the * mac80211 workqueue should be cancelled on the driver stop() callback. * * mac80211 will flushed the workqueue upon interface removal and during * suspend. * * All work performed on the mac80211 workqueue must not acquire the RTNL lock. * */ struct device; /** * enum ieee80211_max_queues - maximum number of queues * * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues. */ enum ieee80211_max_queues { IEEE80211_MAX_QUEUES = 16, }; #define IEEE80211_INVAL_HW_QUEUE 0xff /** * enum ieee80211_ac_numbers - AC numbers as used in mac80211 * @IEEE80211_AC_VO: voice * @IEEE80211_AC_VI: video * @IEEE80211_AC_BE: best effort * @IEEE80211_AC_BK: background */ enum ieee80211_ac_numbers { IEEE80211_AC_VO = 0, IEEE80211_AC_VI = 1, IEEE80211_AC_BE = 2, IEEE80211_AC_BK = 3, }; #define IEEE80211_NUM_ACS 4 /** * struct ieee80211_tx_queue_params - transmit queue configuration * * The information provided in this structure is required for QoS * transmit queue configuration. Cf. IEEE 802.11 7.3.2.29. * * @aifs: arbitration interframe space [0..255] * @cw_min: minimum contention window [a value of the form * 2^n-1 in the range 1..32767] * @cw_max: maximum contention window [like @cw_min] * @txop: maximum burst time in units of 32 usecs, 0 meaning disabled * @uapsd: is U-APSD mode enabled for the queue */ struct ieee80211_tx_queue_params { u16 txop; u16 cw_min; u16 cw_max; u8 aifs; bool uapsd; }; struct ieee80211_low_level_stats { unsigned int dot11ACKFailureCount; unsigned int dot11RTSFailureCount; unsigned int dot11FCSErrorCount; unsigned int dot11RTSSuccessCount; }; /** * enum ieee80211_bss_change - BSS change notification flags * * These flags are used with the bss_info_changed() callback * to indicate which BSS parameter changed. * * @BSS_CHANGED_ASSOC: association status changed (associated/disassociated), * also implies a change in the AID. * @BSS_CHANGED_ERP_CTS_PROT: CTS protection changed * @BSS_CHANGED_ERP_PREAMBLE: preamble changed * @BSS_CHANGED_ERP_SLOT: slot timing changed * @BSS_CHANGED_HT: 802.11n parameters changed * @BSS_CHANGED_BASIC_RATES: Basic rateset changed * @BSS_CHANGED_BEACON_INT: Beacon interval changed * @BSS_CHANGED_BSSID: BSSID changed, for whatever * reason (IBSS and managed mode) * @BSS_CHANGED_BEACON: Beacon data changed, retrieve * new beacon (beaconing modes) * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be * enabled/disabled (beaconing modes) * @BSS_CHANGED_CQM: Connection quality monitor config changed * @BSS_CHANGED_IBSS: IBSS join status changed * @BSS_CHANGED_ARP_FILTER: Hardware ARP filter address list or state changed. * @BSS_CHANGED_QOS: QoS for this association was enabled/disabled. Note * that it is only ever disabled for station mode. * @BSS_CHANGED_IDLE: Idle changed for this BSS/interface. * @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode) * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode) * @BSS_CHANGED_PS: PS changed for this BSS (STA mode) */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, BSS_CHANGED_ERP_CTS_PROT = 1<<1, BSS_CHANGED_ERP_PREAMBLE = 1<<2, BSS_CHANGED_ERP_SLOT = 1<<3, BSS_CHANGED_HT = 1<<4, BSS_CHANGED_BASIC_RATES = 1<<5, BSS_CHANGED_BEACON_INT = 1<<6, BSS_CHANGED_BSSID = 1<<7, BSS_CHANGED_BEACON = 1<<8, BSS_CHANGED_BEACON_ENABLED = 1<<9, BSS_CHANGED_CQM = 1<<10, BSS_CHANGED_IBSS = 1<<11, BSS_CHANGED_ARP_FILTER = 1<<12, BSS_CHANGED_QOS = 1<<13, BSS_CHANGED_IDLE = 1<<14, BSS_CHANGED_SSID = 1<<15, BSS_CHANGED_AP_PROBE_RESP = 1<<16, BSS_CHANGED_PS = 1<<17, /* when adding here, make sure to change ieee80211_reconfig */ }; /* * The maximum number of IPv4 addresses listed for ARP filtering. If the number * of addresses for an interface increase beyond this value, hardware ARP * filtering will be disabled. */ #define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4 /** * enum ieee80211_rssi_event - RSSI threshold event * An indicator for when RSSI goes below/above a certain threshold. * @RSSI_EVENT_HIGH: AP's rssi crossed the high threshold set by the driver. * @RSSI_EVENT_LOW: AP's rssi crossed the low threshold set by the driver. */ enum ieee80211_rssi_event { RSSI_EVENT_HIGH, RSSI_EVENT_LOW, }; /** * struct ieee80211_bss_conf - holds the BSS's changing parameters * * This structure keeps information about a BSS (and an association * to that BSS) that can change during the lifetime of the BSS. * * @assoc: association status * @ibss_joined: indicates whether this station is part of an IBSS * or not * @aid: association ID number, valid only when @assoc is true * @use_cts_prot: use CTS protection * @use_short_preamble: use 802.11b short preamble; * if the hardware cannot handle this it must set the * IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE hardware flag * @use_short_slot: use short slot time (only relevant for ERP); * if the hardware cannot handle this it must set the * IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag * @dtim_period: num of beacons before the next DTIM, for beaconing, * valid in station mode only while @assoc is true and if also * requested by %IEEE80211_HW_NEED_DTIM_PERIOD (cf. also hw conf * @ps_dtim_period) * @sync_tsf: last beacon's/probe response's TSF timestamp (could be old * as it may have been received during scanning long ago) * @sync_device_ts: the device timestamp corresponding to the sync_tsf, * the driver/device can use this to calculate synchronisation * @beacon_int: beacon interval * @assoc_capability: capabilities taken from assoc resp * @basic_rates: bitmap of basic rates, each bit stands for an * index into the rate table configured by the driver in * the current band. * @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @bssid: The BSSID for this BSS * @enable_beacon: whether beaconing should be enabled or not * @channel_type: Channel type for this BSS -- the hardware might be * configured for HT40+ while this BSS only uses no-HT, for * example. * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation. * This field is only valid when the channel type is one of the HT types. * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value * implies disabled * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The * may filter ARP queries targeted for other addresses than listed here. * The driver must allow ARP queries targeted for all address listed here * to pass through. An empty list implies no ARP queries need to pass. * @arp_addr_cnt: Number of addresses currently on the list. * @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may * filter ARP queries based on the @arp_addr_list, if disabled, the * hardware must not perform any ARP filtering. Note, that the filter will * be enabled also in promiscuous mode. * @qos: This is a QoS-enabled BSS. * @idle: This interface is idle. There's also a global idle flag in the * hardware config which may be more appropriate depending on what * your driver/device needs to do. * @ps: power-save mode (STA only). This flag is NOT affected by * offchannel/dynamic_ps operations. * @ssid: The SSID of the current vif. Only valid in AP-mode. * @ssid_len: Length of SSID given in @ssid. * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode. */ struct ieee80211_bss_conf { const u8 *bssid; /* association related data */ bool assoc, ibss_joined; u16 aid; /* erp related data */ bool use_cts_prot; bool use_short_preamble; bool use_short_slot; bool enable_beacon; u8 dtim_period; u16 beacon_int; u16 assoc_capability; u64 sync_tsf; u32 sync_device_ts; u32 basic_rates; int mcast_rate[IEEE80211_NUM_BANDS]; u16 ht_operation_mode; s32 cqm_rssi_thold; u32 cqm_rssi_hyst; enum nl80211_channel_type channel_type; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; u8 arp_addr_cnt; bool arp_filter_enabled; bool qos; bool idle; bool ps; u8 ssid[IEEE80211_MAX_SSID_LEN]; size_t ssid_len; bool hidden_ssid; }; /** * enum mac80211_tx_control_flags - flags to describe transmission information/status * * These flags are used with the @flags member of &ieee80211_tx_info. * * @IEEE80211_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame. * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence * number to this frame, taking care of not overwriting the fragment * number and increasing the sequence number only when the * IEEE80211_TX_CTL_FIRST_FRAGMENT flag is set. mac80211 will properly * assign sequence numbers to QoS-data frames but cannot do so correctly * for non-QoS-data and management frames because beacons need them from * that counter as well and mac80211 cannot guarantee proper sequencing. * If this flag is set, the driver should instruct the hardware to * assign a sequence number to the frame or assign one itself. Cf. IEEE * 802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for * beacons and always be clear for frames without a sequence number field. * @IEEE80211_TX_CTL_NO_ACK: tell the low level not to wait for an ack * @IEEE80211_TX_CTL_CLEAR_PS_FILT: clear powersave filter for destination * station * @IEEE80211_TX_CTL_FIRST_FRAGMENT: this is a first fragment of the frame * @IEEE80211_TX_CTL_SEND_AFTER_DTIM: send this frame after DTIM beacon * @IEEE80211_TX_CTL_AMPDU: this frame should be sent as part of an A-MPDU * @IEEE80211_TX_CTL_INJECTED: Frame was injected, internal to mac80211. * @IEEE80211_TX_STAT_TX_FILTERED: The frame was not transmitted * because the destination STA was in powersave mode. Note that to * avoid race conditions, the filter must be set by the hardware or * firmware upon receiving a frame that indicates that the station * went to sleep (must be done on device to filter frames already on * the queue) and may only be unset after mac80211 gives the OK for * that by setting the IEEE80211_TX_CTL_CLEAR_PS_FILT (see above), * since only then is it guaranteed that no more frames are in the * hardware queue. * @IEEE80211_TX_STAT_ACK: Frame was acknowledged * @IEEE80211_TX_STAT_AMPDU: The frame was aggregated, so status * is for the whole aggregation. * @IEEE80211_TX_STAT_AMPDU_NO_BACK: no block ack was returned, * so consider using block ack request (BAR). * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be * set by rate control algorithms to indicate probe rate, will * be cleared for fragmented frames (except on the last fragment) * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211, * used to indicate that a pending frame requires TX processing before * it can be sent out. * @IEEE80211_TX_INTFL_RETRIED: completely internal to mac80211, * used to indicate that a frame was already retried due to PS * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, * used to indicate frame should not be encrypted * @IEEE80211_TX_CTL_NO_PS_BUFFER: This frame is a response to a poll * frame (PS-Poll or uAPSD) or a non-bufferable MMPDU and must * be sent although the station is in powersave mode. * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the * transmit function after the current frame, this can be used * by drivers to kick the DMA queue only if unset or when the * queue gets full. * @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted * after TX status because the destination was asleep, it must not * be modified again (no seqno assignment, crypto, etc.) * @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211 * MLME command (internal to mac80211 to figure out whether to send TX * status to user space) * @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame * @IEEE80211_TX_CTL_STBC: Enables Space-Time Block Coding (STBC) for this * frame and selects the maximum number of streams that it can use. * @IEEE80211_TX_CTL_TX_OFFCHAN: Marks this packet to be transmitted on * the off-channel channel when a remain-on-channel offload is done * in hardware -- normal packets still flow and are expected to be * handled properly by the device. * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP * testing. It will be sent out with incorrect Michael MIC key to allow * TKIP countermeasures to be tested. * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate. * This flag is actually used for management frame especially for P2P * frames not being sent at CCK rate in 2GHz band. * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, * when its status is reported the service period ends. For frames in * an SP that mac80211 transmits, it is already set; for driver frames * the driver may set this flag. It is also used to do the same for * PS-Poll responses. * @IEEE80211_TX_CTL_USE_MINRATE: This frame will be sent at lowest rate. * This flag is used to send nullfunc frame at minimum rate when * the nullfunc is used for connection monitoring purpose. * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it * would be fragmented by size (this is optional, only used for * monitor injection). * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1), IEEE80211_TX_CTL_NO_ACK = BIT(2), IEEE80211_TX_CTL_CLEAR_PS_FILT = BIT(3), IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(4), IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(5), IEEE80211_TX_CTL_AMPDU = BIT(6), IEEE80211_TX_CTL_INJECTED = BIT(7), IEEE80211_TX_STAT_TX_FILTERED = BIT(8), IEEE80211_TX_STAT_ACK = BIT(9), IEEE80211_TX_STAT_AMPDU = BIT(10), IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17), IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), /* hole at 20, use later */ IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21), IEEE80211_TX_CTL_LDPC = BIT(22), IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), IEEE80211_TX_STATUS_EOSP = BIT(28), IEEE80211_TX_CTL_USE_MINRATE = BIT(29), IEEE80211_TX_CTL_DONTFRAG = BIT(30), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 /* * This definition is used as a mask to clear all temporary flags, which are * set by the tx handlers for each transmission attempt by the mac80211 stack. */ #define IEEE80211_TX_TEMPORARY_FLAGS (IEEE80211_TX_CTL_NO_ACK | \ IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT | \ IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \ IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \ IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_NO_PS_BUFFER | \ IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \ IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP) /** * enum mac80211_rate_control_flags - per-rate flags set by the * Rate Control algorithm. * * These flags are set by the Rate control algorithm for each rate during tx, * in the @flags member of struct ieee80211_tx_rate. * * @IEEE80211_TX_RC_USE_RTS_CTS: Use RTS/CTS exchange for this rate. * @IEEE80211_TX_RC_USE_CTS_PROTECT: CTS-to-self protection is required. * This is set if the current BSS requires ERP protection. * @IEEE80211_TX_RC_USE_SHORT_PREAMBLE: Use short preamble. * @IEEE80211_TX_RC_MCS: HT rate. * @IEEE80211_TX_RC_GREEN_FIELD: Indicates whether this rate should be used in * Greenfield mode. * @IEEE80211_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be 40 MHz. * @IEEE80211_TX_RC_DUP_DATA: The frame should be transmitted on both of the * adjacent 20 MHz channels, if the current channel type is * NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS. * @IEEE80211_TX_RC_SHORT_GI: Short Guard interval should be used for this rate. */ enum mac80211_rate_control_flags { IEEE80211_TX_RC_USE_RTS_CTS = BIT(0), IEEE80211_TX_RC_USE_CTS_PROTECT = BIT(1), IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(2), /* rate index is an MCS rate number instead of an index */ IEEE80211_TX_RC_MCS = BIT(3), IEEE80211_TX_RC_GREEN_FIELD = BIT(4), IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(5), IEEE80211_TX_RC_DUP_DATA = BIT(6), IEEE80211_TX_RC_SHORT_GI = BIT(7), }; /* there are 40 bytes if you don't need the rateset to be kept */ #define IEEE80211_TX_INFO_DRIVER_DATA_SIZE 40 /* if you do need the rateset, then you have less space */ #define IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE 24 /* maximum number of rate stages */ #define IEEE80211_TX_MAX_RATES 4 /** * struct ieee80211_tx_rate - rate selection/status * * @idx: rate index to attempt to send with * @flags: rate control flags (&enum mac80211_rate_control_flags) * @count: number of tries in this rate before going to the next rate * * A value of -1 for @idx indicates an invalid rate and, if used * in an array of retry rates, that no more rates should be tried. * * When used for transmit status reporting, the driver should * always report the rate along with the flags it used. * * &struct ieee80211_tx_info contains an array of these structs * in the control information, and it will be filled by the rate * control algorithm according to what should be sent. For example, * if this array contains, in the format { , } the * information * { 3, 2 }, { 2, 2 }, { 1, 4 }, { -1, 0 }, { -1, 0 } * then this means that the frame should be transmitted * up to twice at rate 3, up to twice at rate 2, and up to four * times at rate 1 if it doesn't get acknowledged. Say it gets * acknowledged by the peer after the fifth attempt, the status * information should then contain * { 3, 2 }, { 2, 2 }, { 1, 1 }, { -1, 0 } ... * since it was transmitted twice at rate 3, twice at rate 2 * and once at rate 1 after which we received an acknowledgement. */ struct ieee80211_tx_rate { s8 idx; u8 count; u8 flags; } __packed; /** * struct ieee80211_tx_info - skb transmit information * * This structure is placed in skb->cb for three uses: * (1) mac80211 TX control - mac80211 tells the driver what to do * (2) driver internal use (if applicable) * (3) TX status information - driver tells mac80211 what happened * * @flags: transmit info flags, defined above * @band: the band to transmit on (use for checking for races) * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC * @ack_frame_id: internal frame ID for TX status, used internally * @control: union for control data * @status: union for status data * @driver_data: array of driver_data pointers * @ampdu_ack_len: number of acked aggregated frames. * relevant only if IEEE80211_TX_STAT_AMPDU was set. * @ampdu_len: number of aggregated frames. * relevant only if IEEE80211_TX_STAT_AMPDU was set. * @ack_signal: signal strength of the ACK frame */ struct ieee80211_tx_info { /* common information */ u32 flags; u8 band; u8 hw_queue; u16 ack_frame_id; union { struct { union { /* rate control */ struct { struct ieee80211_tx_rate rates[ IEEE80211_TX_MAX_RATES]; s8 rts_cts_rate_idx; /* 3 bytes free */ }; /* only needed before rate control */ unsigned long jiffies; }; /* NB: vif can be NULL for injected frames */ struct ieee80211_vif *vif; struct ieee80211_key_conf *hw_key; /* 8 bytes free */ } control; struct { struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES]; int ack_signal; u8 ampdu_ack_len; u8 ampdu_len; u8 antenna; /* 21 bytes free */ } status; struct { struct ieee80211_tx_rate driver_rates[ IEEE80211_TX_MAX_RATES]; void *rate_driver_data[ IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)]; }; void *driver_data[ IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)]; }; }; /** * struct ieee80211_sched_scan_ies - scheduled scan IEs * * This structure is used to pass the appropriate IEs to be used in scheduled * scans for all bands. It contains both the IEs passed from the userspace * and the ones generated by mac80211. * * @ie: array with the IEs for each supported band * @len: array with the total length of the IEs for each band */ struct ieee80211_sched_scan_ies { u8 *ie[IEEE80211_NUM_BANDS]; size_t len[IEEE80211_NUM_BANDS]; }; static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb) { return (struct ieee80211_tx_info *)skb->cb; } static inline struct ieee80211_rx_status *IEEE80211_SKB_RXCB(struct sk_buff *skb) { return (struct ieee80211_rx_status *)skb->cb; } /** * ieee80211_tx_info_clear_status - clear TX status * * @info: The &struct ieee80211_tx_info to be cleared. * * When the driver passes an skb back to mac80211, it must report * a number of things in TX status. This function clears everything * in the TX status but the rate control information (it does clear * the count since you need to fill that in anyway). * * NOTE: You can only use this function if you do NOT use * info->driver_data! Use info->rate_driver_data * instead if you need only the less space that allows. */ static inline void ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) { int i; BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != offsetof(struct ieee80211_tx_info, control.rates)); BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != offsetof(struct ieee80211_tx_info, driver_rates)); BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != 8); /* clear the rate counts */ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) info->status.rates[i].count = 0; BUILD_BUG_ON( offsetof(struct ieee80211_tx_info, status.ack_signal) != 20); memset(&info->status.ampdu_ack_len, 0, sizeof(struct ieee80211_tx_info) - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); } /** * enum mac80211_rx_flags - receive flags * * These flags are used with the @flag member of &struct ieee80211_rx_status. * @RX_FLAG_MMIC_ERROR: Michael MIC error was reported on this frame. * Use together with %RX_FLAG_MMIC_STRIPPED. * @RX_FLAG_DECRYPTED: This frame was decrypted in hardware. * @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame, * verification has been done by the hardware. * @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame. * If this flag is set, the stack cannot do any replay detection * hence the driver or hardware will have to do that. * @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on * the frame. * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on * the frame. * @RX_FLAG_MACTIME_MPDU: The timestamp passed in the RX status (@mactime * field) is valid and contains the time the first symbol of the MPDU * was received. This is useful in monitor mode and for proper IBSS * merging. * @RX_FLAG_SHORTPRE: Short preamble was used for this frame * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used * @RX_FLAG_SHORT_GI: Short guard interval was used * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present. * Valid only for data frames (mainly A-MPDU) * @RX_FLAG_HT_GF: This frame was received in a HT-greenfield transmission, if * the driver fills this value it should add %IEEE80211_RADIOTAP_MCS_HAVE_FMT * to hw.radiotap_mcs_details to advertise that fact * @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference * number (@ampdu_reference) must be populated and be a distinct number for * each A-MPDU * @RX_FLAG_AMPDU_REPORT_ZEROLEN: driver reports 0-length subframes * @RX_FLAG_AMPDU_IS_ZEROLEN: This is a zero-length subframe, for * monitoring purposes only * @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all * subframes of a single A-MPDU * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU * @RX_FLAG_AMPDU_DELIM_CRC_ERROR: A delimiter CRC error has been detected * on this subframe * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC * is stored in the @ampdu_delimiter_crc field) */ enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = BIT(0), RX_FLAG_DECRYPTED = BIT(1), RX_FLAG_MMIC_STRIPPED = BIT(3), RX_FLAG_IV_STRIPPED = BIT(4), RX_FLAG_FAILED_FCS_CRC = BIT(5), RX_FLAG_FAILED_PLCP_CRC = BIT(6), RX_FLAG_MACTIME_MPDU = BIT(7), RX_FLAG_SHORTPRE = BIT(8), RX_FLAG_HT = BIT(9), RX_FLAG_40MHZ = BIT(10), RX_FLAG_SHORT_GI = BIT(11), RX_FLAG_NO_SIGNAL_VAL = BIT(12), RX_FLAG_HT_GF = BIT(13), RX_FLAG_AMPDU_DETAILS = BIT(14), RX_FLAG_AMPDU_REPORT_ZEROLEN = BIT(15), RX_FLAG_AMPDU_IS_ZEROLEN = BIT(16), RX_FLAG_AMPDU_LAST_KNOWN = BIT(17), RX_FLAG_AMPDU_IS_LAST = BIT(18), RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19), RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20), }; /** * struct ieee80211_rx_status - receive status * * The low-level driver should provide this information (the subset * supported by hardware) to the 802.11 code with each received * frame, in the skb's control buffer (cb). * * @mactime: value in microseconds of the 64-bit Time Synchronization Function * (TSF) timer when the first data symbol (MPDU) arrived at the hardware. * @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use * it but can store it and pass it back to the driver for synchronisation * @band: the active band when this frame was received * @freq: frequency the radio was tuned to when receiving this frame, in MHz * @signal: signal strength when receiving this frame, either in dBm, in dB or * unspecified depending on the hardware capabilities flags * @IEEE80211_HW_SIGNAL_* * @antenna: antenna used * @rate_idx: index of data rate into band's supported rates or MCS index if * HT rates are use (RX_FLAG_HT) * @flag: %RX_FLAG_* * @rx_flags: internal RX flags for mac80211 * @ampdu_reference: A-MPDU reference number, must be a different value for * each A-MPDU but the same for each subframe within one A-MPDU * @ampdu_delimiter_crc: A-MPDU delimiter CRC */ struct ieee80211_rx_status { u64 mactime; u32 device_timestamp; u32 ampdu_reference; u32 flag; u16 freq; u8 rate_idx; u8 rx_flags; u8 band; u8 antenna; s8 signal; u8 ampdu_delimiter_crc; }; /** * enum ieee80211_conf_flags - configuration flags * * Flags to define PHY configuration options * * @IEEE80211_CONF_MONITOR: there's a monitor interface present -- use this * to determine for example whether to calculate timestamps for packets * or not, do not use instead of filter flags! * @IEEE80211_CONF_PS: Enable 802.11 power save mode (managed mode only). * This is the power save mode defined by IEEE 802.11-2007 section 11.2, * meaning that the hardware still wakes up for beacons, is able to * transmit frames and receive the possible acknowledgment frames. * Not to be confused with hardware specific wakeup/sleep states, * driver is responsible for that. See the section "Powersave support" * for more. * @IEEE80211_CONF_IDLE: The device is running, but idle; if the flag is set * the driver should be prepared to handle configuration requests but * may turn the device off as much as possible. Typically, this flag will * be set when an interface is set UP but not associated or scanning, but * it can also be unset in that case when monitor interfaces are active. * @IEEE80211_CONF_OFFCHANNEL: The device is currently not on its main * operating channel. */ enum ieee80211_conf_flags { IEEE80211_CONF_MONITOR = (1<<0), IEEE80211_CONF_PS = (1<<1), IEEE80211_CONF_IDLE = (1<<2), IEEE80211_CONF_OFFCHANNEL = (1<<3), }; /** * enum ieee80211_conf_changed - denotes which configuration changed * * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed * @IEEE80211_CONF_CHANGE_MONITOR: the monitor flag changed * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed * @IEEE80211_CONF_CHANGE_POWER: the TX power changed * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed */ enum ieee80211_conf_changed { IEEE80211_CONF_CHANGE_SMPS = BIT(1), IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), IEEE80211_CONF_CHANGE_MONITOR = BIT(3), IEEE80211_CONF_CHANGE_PS = BIT(4), IEEE80211_CONF_CHANGE_POWER = BIT(5), IEEE80211_CONF_CHANGE_CHANNEL = BIT(6), IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7), IEEE80211_CONF_CHANGE_IDLE = BIT(8), }; /** * enum ieee80211_smps_mode - spatial multiplexing power save mode * * @IEEE80211_SMPS_AUTOMATIC: automatic * @IEEE80211_SMPS_OFF: off * @IEEE80211_SMPS_STATIC: static * @IEEE80211_SMPS_DYNAMIC: dynamic * @IEEE80211_SMPS_NUM_MODES: internal, don't use */ enum ieee80211_smps_mode { IEEE80211_SMPS_AUTOMATIC, IEEE80211_SMPS_OFF, IEEE80211_SMPS_STATIC, IEEE80211_SMPS_DYNAMIC, /* keep last */ IEEE80211_SMPS_NUM_MODES, }; /** * struct ieee80211_conf - configuration of the device * * This struct indicates how the driver shall configure the hardware. * * @flags: configuration flags defined above * * @listen_interval: listen interval in units of beacon interval * @max_sleep_period: the maximum number of beacon intervals to sleep for * before checking the beacon for a TIM bit (managed mode only); this * value will be only achievable between DTIM frames, the hardware * needs to check for the multicast traffic bit in DTIM beacons. * This variable is valid only when the CONF_PS flag is set. * @ps_dtim_period: The DTIM period of the AP we're connected to, for use * in power saving. Power saving will not be enabled until a beacon * has been received and the DTIM period is known. * @dynamic_ps_timeout: The dynamic powersave timeout (in ms), see the * powersave documentation below. This variable is valid only when * the CONF_PS flag is set. * * @power_level: requested transmit power (in dBm) * * @channel: the channel to tune to * @channel_type: the channel (HT) type * * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, * but actually means the number of transmissions not the number of retries * @short_frame_max_tx_count: Maximum number of transmissions for a "short" * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the * number of transmissions not the number of retries * * @smps_mode: spatial multiplexing powersave mode; note that * %IEEE80211_SMPS_STATIC is used when the device is not * configured for an HT channel */ struct ieee80211_conf { u32 flags; int power_level, dynamic_ps_timeout; int max_sleep_period; u16 listen_interval; u8 ps_dtim_period; u8 long_frame_max_tx_count, short_frame_max_tx_count; struct ieee80211_channel *channel; enum nl80211_channel_type channel_type; enum ieee80211_smps_mode smps_mode; }; /** * struct ieee80211_channel_switch - holds the channel switch data * * The information provided in this structure is required for channel switch * operation. * * @timestamp: value in microseconds of the 64-bit Time Synchronization * Function (TSF) timer when the frame containing the channel switch * announcement was received. This is simply the rx.mactime parameter * the driver passed into mac80211. * @block_tx: Indicates whether transmission must be blocked before the * scheduled channel switch, as indicated by the AP. * @channel: the new channel to switch to * @count: the number of TBTT's until the channel switch event */ struct ieee80211_channel_switch { u64 timestamp; bool block_tx; struct ieee80211_channel *channel; u8 count; }; /** * enum ieee80211_vif_flags - virtual interface flags * * @IEEE80211_VIF_BEACON_FILTER: the device performs beacon filtering * on this virtual interface to avoid unnecessary CPU wakeups * @IEEE80211_VIF_SUPPORTS_CQM_RSSI: the device can do connection quality * monitoring on this virtual interface -- i.e. it can monitor * connection quality related parameters, such as the RSSI level and * provide notifications if configured trigger levels are reached. */ enum ieee80211_vif_flags { IEEE80211_VIF_BEACON_FILTER = BIT(0), IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1), }; /** * struct ieee80211_vif - per-interface data * * Data in this structure is continually present for driver * use during the life of a virtual interface. * * @type: type of this virtual interface * @bss_conf: BSS configuration for this interface, either our own * or the BSS we're associated to * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively * @driver_flags: flags/capabilities the driver has for this interface, * these need to be set (or cleared) when the interface is added * or, if supported by the driver, the interface type is changed * at runtime, mac80211 will never touch this field * @hw_queue: hardware queue for each AC * @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *). */ struct ieee80211_vif { enum nl80211_iftype type; struct ieee80211_bss_conf bss_conf; u8 addr[ETH_ALEN]; bool p2p; u8 cab_queue; u8 hw_queue[IEEE80211_NUM_ACS]; u32 driver_flags; /* must be last */ u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); }; static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) { #ifdef CONFIG_MAC80211_MESH return vif->type == NL80211_IFTYPE_MESH_POINT; #endif return false; } /** * enum ieee80211_key_flags - key flags * * These flags are used for communication about keys between the driver * and mac80211, with the @flags parameter of &struct ieee80211_key_conf. * * @IEEE80211_KEY_FLAG_WMM_STA: Set by mac80211, this flag indicates * that the STA this key will be used with could be using QoS. * @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the * driver to indicate that it requires IV generation for this * particular key. * @IEEE80211_KEY_FLAG_GENERATE_MMIC: This flag should be set by * the driver for a TKIP key if it requires Michael MIC * generation in software. * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates * that the key is pairwise rather then a shared key. * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a * CCMP key if it requires CCMP encryption of management frames (MFP) to * be done in software. * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver * if space should be prepared for the IV, but the IV * itself should not be generated. Do not set together with * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. */ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_WMM_STA = 1<<0, IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1, IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, IEEE80211_KEY_FLAG_SW_MGMT = 1<<4, IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5, }; /** * struct ieee80211_key_conf - key information * * This key information is given by mac80211 to the driver by * the set_key() callback in &struct ieee80211_ops. * * @hw_key_idx: To be set by the driver, this is the key index the driver * wants to be given when a frame is transmitted and needs to be * encrypted in hardware. * @cipher: The key's cipher suite selector. * @flags: key flags, see &enum ieee80211_key_flags. * @keyidx: the key index (0-3) * @keylen: key material length * @key: key material. For ALG_TKIP the key is encoded as a 256-bit (32 byte) * data block: * - Temporal Encryption Key (128 bits) * - Temporal Authenticator Tx MIC Key (64 bits) * - Temporal Authenticator Rx MIC Key (64 bits) * @icv_len: The ICV length for this key type * @iv_len: The IV length for this key type */ struct ieee80211_key_conf { u32 cipher; u8 icv_len; u8 iv_len; u8 hw_key_idx; u8 flags; s8 keyidx; u8 keylen; u8 key[0]; }; /** * enum set_key_cmd - key command * * Used with the set_key() callback in &struct ieee80211_ops, this * indicates whether a key is being removed or added. * * @SET_KEY: a key is set * @DISABLE_KEY: a key must be disabled */ enum set_key_cmd { SET_KEY, DISABLE_KEY, }; /** * enum ieee80211_sta_state - station state * * @IEEE80211_STA_NOTEXIST: station doesn't exist at all, * this is a special state for add/remove transitions * @IEEE80211_STA_NONE: station exists without special state * @IEEE80211_STA_AUTH: station is authenticated * @IEEE80211_STA_ASSOC: station is associated * @IEEE80211_STA_AUTHORIZED: station is authorized (802.1X) */ enum ieee80211_sta_state { /* NOTE: These need to be ordered correctly! */ IEEE80211_STA_NOTEXIST, IEEE80211_STA_NONE, IEEE80211_STA_AUTH, IEEE80211_STA_ASSOC, IEEE80211_STA_AUTHORIZED, }; /** * struct ieee80211_sta - station table entry * * A station table entry represents a station we are possibly * communicating with. Since stations are RCU-managed in * mac80211, any ieee80211_sta pointer you get access to must * either be protected by rcu_read_lock() explicitly or implicitly, * or you must take good care to not use such a pointer after a * call to your sta_remove callback that removed it. * * @addr: MAC address * @aid: AID we assigned to the station if we're an AP * @supp_rates: Bitmap of supported rates (per band) * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities * @wme: indicates whether the STA supports WME. Only valid during AP-mode. * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *), size is determined in hw information. * @uapsd_queues: bitmap of queues configured for uapsd. Only valid * if wme is supported. * @max_sp: max Service Period. Only valid if wme is supported. */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; u8 addr[ETH_ALEN]; u16 aid; struct ieee80211_sta_ht_cap ht_cap; bool wme; u8 uapsd_queues; u8 max_sp; /* must be last */ u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); }; /** * enum sta_notify_cmd - sta notify command * * Used with the sta_notify() callback in &struct ieee80211_ops, this * indicates if an associated station made a power state transition. * * @STA_NOTIFY_SLEEP: a station is now sleeping * @STA_NOTIFY_AWAKE: a sleeping station woke up */ enum sta_notify_cmd { STA_NOTIFY_SLEEP, STA_NOTIFY_AWAKE, }; /** * struct ieee80211_tx_control - TX control data * * @sta: station table entry, this sta pointer may be NULL and * it is not allowed to copy the pointer, due to RCU. */ struct ieee80211_tx_control { struct ieee80211_sta *sta; }; /** * enum ieee80211_hw_flags - hardware flags * * These flags are used to indicate hardware capabilities to * the stack. Generally, flags here should have their meaning * done in a way that the simplest hardware doesn't need setting * any particular flags. There are some exceptions to this rule, * however, so you are advised to review these flags carefully. * * @IEEE80211_HW_HAS_RATE_CONTROL: * The hardware or firmware includes rate control, and cannot be * controlled by the stack. As such, no rate control algorithm * should be instantiated, and the TX rate reported to userspace * will be taken from the TX status instead of the rate control * algorithm. * Note that this requires that the driver implement a number of * callbacks so it has the correct information, it needs to have * the @set_rts_threshold callback and must look at the BSS config * @use_cts_prot for G/N protection, @use_short_slot for slot * timing in 2.4 GHz and @use_short_preamble for preambles for * CCK frames. * * @IEEE80211_HW_RX_INCLUDES_FCS: * Indicates that received frames passed to the stack include * the FCS at the end. * * @IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING: * Some wireless LAN chipsets buffer broadcast/multicast frames * for power saving stations in the hardware/firmware and others * rely on the host system for such buffering. This option is used * to configure the IEEE 802.11 upper layer to buffer broadcast and * multicast frames when there are power saving stations so that * the driver can fetch them with ieee80211_get_buffered_bc(). * * @IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE: * Hardware is not capable of short slot operation on the 2.4 GHz band. * * @IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE: * Hardware is not capable of receiving frames with short preamble on * the 2.4 GHz band. * * @IEEE80211_HW_SIGNAL_UNSPEC: * Hardware can provide signal values but we don't know its units. We * expect values between 0 and @max_signal. * If possible please provide dB or dBm instead. * * @IEEE80211_HW_SIGNAL_DBM: * Hardware gives signal values in dBm, decibel difference from * one milliwatt. This is the preferred method since it is standardized * between different devices. @max_signal does not need to be set. * * @IEEE80211_HW_SPECTRUM_MGMT: * Hardware supports spectrum management defined in 802.11h * Measurement, Channel Switch, Quieting, TPC * * @IEEE80211_HW_AMPDU_AGGREGATION: * Hardware supports 11n A-MPDU aggregation. * * @IEEE80211_HW_SUPPORTS_PS: * Hardware has power save support (i.e. can go to sleep). * * @IEEE80211_HW_PS_NULLFUNC_STACK: * Hardware requires nullfunc frame handling in stack, implies * stack support for dynamic PS. * * @IEEE80211_HW_SUPPORTS_DYNAMIC_PS: * Hardware has support for dynamic PS. * * @IEEE80211_HW_MFP_CAPABLE: * Hardware supports management frame protection (MFP, IEEE 802.11w). * * @IEEE80211_HW_SUPPORTS_STATIC_SMPS: * Hardware supports static spatial multiplexing powersave, * ie. can turn off all but one chain even on HT connections * that should be using more chains. * * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS: * Hardware supports dynamic spatial multiplexing powersave, * ie. can turn off all but one chain and then wake the rest * up as required after, for example, rts/cts handshake. * * @IEEE80211_HW_SUPPORTS_UAPSD: * Hardware supports Unscheduled Automatic Power Save Delivery * (U-APSD) in managed mode. The mode is configured with * conf_tx() operation. * * @IEEE80211_HW_REPORTS_TX_ACK_STATUS: * Hardware can provide ack status reports of Tx frames to * the stack. * * @IEEE80211_HW_CONNECTION_MONITOR: * The hardware performs its own connection monitoring, including * periodic keep-alives to the AP and probing the AP on beacon loss. * When this flag is set, signaling beacon-loss will cause an immediate * change to disassociated state. * * @IEEE80211_HW_NEED_DTIM_PERIOD: * This device needs to know the DTIM period for the BSS before * associating. * * @IEEE80211_HW_SUPPORTS_PER_STA_GTK: The device's crypto engine supports * per-station GTKs as used by IBSS RSN or during fast transition. If * the device doesn't support per-station GTKs, but can be asked not * to decrypt group addressed frames, then IBSS RSN support is still * possible but software crypto will be used. Advertise the wiphy flag * only in that case. * * @IEEE80211_HW_AP_LINK_PS: When operating in AP mode the device * autonomously manages the PS status of connected stations. When * this flag is set mac80211 will not trigger PS mode for connected * stations based on the PM bit of incoming frames. * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure * the PS mode of connected stations. * * @IEEE80211_HW_TX_AMPDU_SETUP_IN_HW: The device handles TX A-MPDU session * setup strictly in HW. mac80211 should not attempt to do this in * software. * * @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while * being idle (i.e. mac80211 doesn't have to go idle-off during the * the scan). * * @IEEE80211_HW_WANT_MONITOR_VIF: The driver would like to be informed of * a virtual monitor interface when monitor interfaces are the only * active interfaces. * * @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface * queue mapping in order to use different queues (not just one per AC) * for different virtual interfaces. See the doc section on HW queue * control for more details. * * @IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF: Use the P2P Device address for any * P2P Interface. This will be honoured even if more than one interface * is supported. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING = 1<<2, IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE = 1<<3, IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE = 1<<4, IEEE80211_HW_SIGNAL_UNSPEC = 1<<5, IEEE80211_HW_SIGNAL_DBM = 1<<6, IEEE80211_HW_NEED_DTIM_PERIOD = 1<<7, IEEE80211_HW_SPECTRUM_MGMT = 1<<8, IEEE80211_HW_AMPDU_AGGREGATION = 1<<9, IEEE80211_HW_SUPPORTS_PS = 1<<10, IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, IEEE80211_HW_MFP_CAPABLE = 1<<13, IEEE80211_HW_WANT_MONITOR_VIF = 1<<14, IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_CONNECTION_MONITOR = 1<<19, IEEE80211_HW_QUEUE_CONTROL = 1<<20, IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21, IEEE80211_HW_AP_LINK_PS = 1<<22, IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24, IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25, }; /** * struct ieee80211_hw - hardware information and state * * This structure contains the configuration and hardware * information for an 802.11 PHY. * * @wiphy: This points to the &struct wiphy allocated for this * 802.11 PHY. You must fill in the @perm_addr and @dev * members of this structure using SET_IEEE80211_DEV() * and SET_IEEE80211_PERM_ADDR(). Additionally, all supported * bands (with channels, bitrates) are registered here. * * @conf: &struct ieee80211_conf, device configuration, don't use. * * @priv: pointer to private area that was allocated for driver use * along with this structure. * * @flags: hardware flags, see &enum ieee80211_hw_flags. * * @extra_tx_headroom: headroom to reserve in each transmit skb * for use by the driver (e.g. for transmit headers.) * * @channel_change_time: time (in microseconds) it takes to change channels. * * @max_signal: Maximum value for signal (rssi) in RX information, used * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB * * @max_listen_interval: max listen interval in units of beacon interval * that HW supports * * @queues: number of available hardware transmit queues for * data packets. WMM/QoS requires at least four, these * queues need to have configurable access parameters. * * @rate_control_algorithm: rate control algorithm for this hardware. * If unset (NULL), the default algorithm will be used. Must be * set before calling ieee80211_register_hw(). * * @vif_data_size: size (in bytes) of the drv_priv data area * within &struct ieee80211_vif. * @sta_data_size: size (in bytes) of the drv_priv data area * within &struct ieee80211_sta. * * @max_rates: maximum number of alternate rate retry stages the hw * can handle. * @max_report_rates: maximum number of alternate rate retry stages * the hw can report back. * @max_rate_tries: maximum number of tries for each stage * * @napi_weight: weight used for NAPI polling. You must specify an * appropriate value here if a napi_poll operation is provided * by your driver. * * @max_rx_aggregation_subframes: maximum buffer size (number of * sub-frames) to be used for A-MPDU block ack receiver * aggregation. * This is only relevant if the device has restrictions on the * number of subframes, if it relies on mac80211 to do reordering * it shouldn't be set. * * @max_tx_aggregation_subframes: maximum number of subframes in an * aggregate an HT driver will transmit, used by the peer as a * hint to size its reorder buffer. * * @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX * (if %IEEE80211_HW_QUEUE_CONTROL is set) * * @radiotap_mcs_details: lists which MCS information can the HW * reports, by default it is set to _MCS, _GI and _BW but doesn't * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only * adding _BW is supported today. * * @netdev_features: netdev features to be set in each netdev created * from this HW. Note only HW checksum features are currently * compatible with mac80211. Other feature bits will be rejected. */ struct ieee80211_hw { struct ieee80211_conf conf; struct wiphy *wiphy; const char *rate_control_algorithm; void *priv; u32 flags; unsigned int extra_tx_headroom; int channel_change_time; int vif_data_size; int sta_data_size; int napi_weight; u16 queues; u16 max_listen_interval; s8 max_signal; u8 max_rates; u8 max_report_rates; u8 max_rate_tries; u8 max_rx_aggregation_subframes; u8 max_tx_aggregation_subframes; u8 offchannel_tx_hw_queue; u8 radiotap_mcs_details; netdev_features_t netdev_features; }; /** * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy * * @wiphy: the &struct wiphy which we want to query * * mac80211 drivers can use this to get to their respective * &struct ieee80211_hw. Drivers wishing to get to their own private * structure can then access it via hw->priv. Note that mac802111 drivers should * not use wiphy_priv() to try to get their private driver structure as this * is already used internally by mac80211. */ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy); /** * SET_IEEE80211_DEV - set device for 802.11 hardware * * @hw: the &struct ieee80211_hw to set the device for * @dev: the &struct device of this 802.11 device */ static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev) { set_wiphy_dev(hw->wiphy, dev); } /** * SET_IEEE80211_PERM_ADDR - set the permanent MAC address for 802.11 hardware * * @hw: the &struct ieee80211_hw to set the MAC address for * @addr: the address to set */ static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr) { memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN); } static inline struct ieee80211_rate * ieee80211_get_tx_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c) { if (WARN_ON_ONCE(c->control.rates[0].idx < 0)) return NULL; return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx]; } static inline struct ieee80211_rate * ieee80211_get_rts_cts_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c) { if (c->control.rts_cts_rate_idx < 0) return NULL; return &hw->wiphy->bands[c->band]->bitrates[c->control.rts_cts_rate_idx]; } static inline struct ieee80211_rate * ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c, int idx) { if (c->control.rates[idx + 1].idx < 0) return NULL; return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[idx + 1].idx]; } /** * ieee80211_free_txskb - free TX skb * @hw: the hardware * @skb: the skb * * Free a transmit skb. Use this funtion when some failure * to transmit happened and thus status cannot be reported. */ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); /** * DOC: Hardware crypto acceleration * * mac80211 is capable of taking advantage of many hardware * acceleration designs for encryption and decryption operations. * * The set_key() callback in the &struct ieee80211_ops for a given * device is called to enable hardware acceleration of encryption and * decryption. The callback takes a @sta parameter that will be NULL * for default keys or keys used for transmission only, or point to * the station information for the peer for individual keys. * Multiple transmission keys with the same key index may be used when * VLANs are configured for an access point. * * When transmitting, the TX control data will use the @hw_key_idx * selected by the driver by modifying the &struct ieee80211_key_conf * pointed to by the @key parameter to the set_key() function. * * The set_key() call for the %SET_KEY command should return 0 if * the key is now in use, -%EOPNOTSUPP or -%ENOSPC if it couldn't be * added; if you return 0 then hw_key_idx must be assigned to the * hardware key index, you are free to use the full u8 range. * * When the cmd is %DISABLE_KEY then it must succeed. * * Note that it is permissible to not decrypt a frame even if a key * for it has been uploaded to hardware, the stack will not make any * decision based on whether a key has been uploaded or not but rather * based on the receive flags. * * The &struct ieee80211_key_conf structure pointed to by the @key * parameter is guaranteed to be valid until another call to set_key() * removes it, but it can only be used as a cookie to differentiate * keys. * * In TKIP some HW need to be provided a phase 1 key, for RX decryption * acceleration (i.e. iwlwifi). Those drivers should provide update_tkip_key * handler. * The update_tkip_key() call updates the driver with the new phase 1 key. * This happens every time the iv16 wraps around (every 65536 packets). The * set_key() call will happen only once for each key (unless the AP did * rekeying), it will not include a valid phase 1 key. The valid phase 1 key is * provided by update_tkip_key only. The trigger that makes mac80211 call this * handler is software decryption with wrap around of iv16. */ /** * DOC: Powersave support * * mac80211 has support for various powersave implementations. * * First, it can support hardware that handles all powersaving by itself, * such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS hardware * flag. In that case, it will be told about the desired powersave mode * with the %IEEE80211_CONF_PS flag depending on the association status. * The hardware must take care of sending nullfunc frames when necessary, * i.e. when entering and leaving powersave mode. The hardware is required * to look at the AID in beacons and signal to the AP that it woke up when * it finds traffic directed to it. * * %IEEE80211_CONF_PS flag enabled means that the powersave mode defined in * IEEE 802.11-2007 section 11.2 is enabled. This is not to be confused * with hardware wakeup and sleep states. Driver is responsible for waking * up the hardware before issuing commands to the hardware and putting it * back to sleep at appropriate times. * * When PS is enabled, hardware needs to wakeup for beacons and receive the * buffered multicast/broadcast frames after the beacon. Also it must be * possible to send frames and receive the acknowledment frame. * * Other hardware designs cannot send nullfunc frames by themselves and also * need software support for parsing the TIM bitmap. This is also supported * by mac80211 by combining the %IEEE80211_HW_SUPPORTS_PS and * %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still * required to pass up beacons. The hardware is still required to handle * waking up for multicast traffic; if it cannot the driver must handle that * as best as it can, mac80211 is too slow to do that. * * Dynamic powersave is an extension to normal powersave in which the * hardware stays awake for a user-specified period of time after sending a * frame so that reply frames need not be buffered and therefore delayed to * the next wakeup. It's compromise of getting good enough latency when * there's data traffic and still saving significantly power in idle * periods. * * Dynamic powersave is simply supported by mac80211 enabling and disabling * PS based on traffic. Driver needs to only set %IEEE80211_HW_SUPPORTS_PS * flag and mac80211 will handle everything automatically. Additionally, * hardware having support for the dynamic PS feature may set the * %IEEE80211_HW_SUPPORTS_DYNAMIC_PS flag to indicate that it can support * dynamic PS mode itself. The driver needs to look at the * @dynamic_ps_timeout hardware configuration value and use it that value * whenever %IEEE80211_CONF_PS is set. In this case mac80211 will disable * dynamic PS feature in stack and will just keep %IEEE80211_CONF_PS * enabled whenever user has enabled powersave. * * Some hardware need to toggle a single shared antenna between WLAN and * Bluetooth to facilitate co-existence. These types of hardware set * limitations on the use of host controlled dynamic powersave whenever there * is simultaneous WLAN and Bluetooth traffic. For these types of hardware, the * driver may request temporarily going into full power save, in order to * enable toggling the antenna between BT and WLAN. If the driver requests * disabling dynamic powersave, the @dynamic_ps_timeout value will be * temporarily set to zero until the driver re-enables dynamic powersave. * * Driver informs U-APSD client support by enabling * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the * uapsd paramater in conf_tx() operation. Hardware needs to send the QoS * Nullfunc frames and stay awake until the service period has ended. To * utilize U-APSD, dynamic powersave is disabled for voip AC and all frames * from that AC are transmitted with powersave enabled. * * Note: U-APSD client mode is not yet supported with * %IEEE80211_HW_PS_NULLFUNC_STACK. */ /** * DOC: Beacon filter support * * Some hardware have beacon filter support to reduce host cpu wakeups * which will reduce system power consumption. It usually works so that * the firmware creates a checksum of the beacon but omits all constantly * changing elements (TSF, TIM etc). Whenever the checksum changes the * beacon is forwarded to the host, otherwise it will be just dropped. That * way the host will only receive beacons where some relevant information * (for example ERP protection or WMM settings) have changed. * * Beacon filter support is advertised with the %IEEE80211_VIF_BEACON_FILTER * interface capability. The driver needs to enable beacon filter support * whenever power save is enabled, that is %IEEE80211_CONF_PS is set. When * power save is enabled, the stack will not check for beacon loss and the * driver needs to notify about loss of beacons with ieee80211_beacon_loss(). * * The time (or number of beacons missed) until the firmware notifies the * driver of a beacon loss event (which in turn causes the driver to call * ieee80211_beacon_loss()) should be configurable and will be controlled * by mac80211 and the roaming algorithm in the future. * * Since there may be constantly changing information elements that nothing * in the software stack cares about, we will, in the future, have mac80211 * tell the driver which information elements are interesting in the sense * that we want to see changes in them. This will include * - a list of information element IDs * - a list of OUIs for the vendor information element * * Ideally, the hardware would filter out any beacons without changes in the * requested elements, but if it cannot support that it may, at the expense * of some efficiency, filter out only a subset. For example, if the device * doesn't support checking for OUIs it should pass up all changes in all * vendor information elements. * * Note that change, for the sake of simplification, also includes information * elements appearing or disappearing from the beacon. * * Some hardware supports an "ignore list" instead, just make sure nothing * that was requested is on the ignore list, and include commonly changing * information element IDs in the ignore list, for example 11 (BSS load) and * the various vendor-assigned IEs with unknown contents (128, 129, 133-136, * 149, 150, 155, 156, 173, 176, 178, 179, 219); for forward compatibility * it could also include some currently unused IDs. * * * In addition to these capabilities, hardware should support notifying the * host of changes in the beacon RSSI. This is relevant to implement roaming * when no traffic is flowing (when traffic is flowing we see the RSSI of * the received data packets). This can consist in notifying the host when * the RSSI changes significantly or when it drops below or rises above * configurable thresholds. In the future these thresholds will also be * configured by mac80211 (which gets them from userspace) to implement * them as the roaming algorithm requires. * * If the hardware cannot implement this, the driver should ask it to * periodically pass beacon frames to the host so that software can do the * signal strength threshold checking. */ /** * DOC: Spatial multiplexing power save * * SMPS (Spatial multiplexing power save) is a mechanism to conserve * power in an 802.11n implementation. For details on the mechanism * and rationale, please refer to 802.11 (as amended by 802.11n-2009) * "11.2.3 SM power save". * * The mac80211 implementation is capable of sending action frames * to update the AP about the station's SMPS mode, and will instruct * the driver to enter the specific mode. It will also announce the * requested SMPS mode during the association handshake. Hardware * support for this feature is required, and can be indicated by * hardware flags. * * The default mode will be "automatic", which nl80211/cfg80211 * defines to be dynamic SMPS in (regular) powersave, and SMPS * turned off otherwise. * * To support this feature, the driver must set the appropriate * hardware support flags, and handle the SMPS flag to the config() * operation. It will then with this mechanism be instructed to * enter the requested SMPS mode while associated to an HT AP. */ /** * DOC: Frame filtering * * mac80211 requires to see many management frames for proper * operation, and users may want to see many more frames when * in monitor mode. However, for best CPU usage and power consumption, * having as few frames as possible percolate through the stack is * desirable. Hence, the hardware should filter as much as possible. * * To achieve this, mac80211 uses filter flags (see below) to tell * the driver's configure_filter() function which frames should be * passed to mac80211 and which should be filtered out. * * Before configure_filter() is invoked, the prepare_multicast() * callback is invoked with the parameters @mc_count and @mc_list * for the combined multicast address list of all virtual interfaces. * It's use is optional, and it returns a u64 that is passed to * configure_filter(). Additionally, configure_filter() has the * arguments @changed_flags telling which flags were changed and * @total_flags with the new flag states. * * If your device has no multicast address filters your driver will * need to check both the %FIF_ALLMULTI flag and the @mc_count * parameter to see whether multicast frames should be accepted * or dropped. * * All unsupported flags in @total_flags must be cleared. * Hardware does not support a flag if it is incapable of _passing_ * the frame to the stack. Otherwise the driver must ignore * the flag, but not clear it. * You must _only_ clear the flag (announce no support for the * flag to mac80211) if you are not able to pass the packet type * to the stack (so the hardware always filters it). * So for example, you should clear @FIF_CONTROL, if your hardware * always filters control frames. If your hardware always passes * control frames to the kernel and is incapable of filtering them, * you do _not_ clear the @FIF_CONTROL flag. * This rule applies to all other FIF flags as well. */ /** * DOC: AP support for powersaving clients * * In order to implement AP and P2P GO modes, mac80211 has support for * client powersaving, both "legacy" PS (PS-Poll/null data) and uAPSD. * There currently is no support for sAPSD. * * There is one assumption that mac80211 makes, namely that a client * will not poll with PS-Poll and trigger with uAPSD at the same time. * Both are supported, and both can be used by the same client, but * they can't be used concurrently by the same client. This simplifies * the driver code. * * The first thing to keep in mind is that there is a flag for complete * driver implementation: %IEEE80211_HW_AP_LINK_PS. If this flag is set, * mac80211 expects the driver to handle most of the state machine for * powersaving clients and will ignore the PM bit in incoming frames. * Drivers then use ieee80211_sta_ps_transition() to inform mac80211 of * stations' powersave transitions. In this mode, mac80211 also doesn't * handle PS-Poll/uAPSD. * * In the mode without %IEEE80211_HW_AP_LINK_PS, mac80211 will check the * PM bit in incoming frames for client powersave transitions. When a * station goes to sleep, we will stop transmitting to it. There is, * however, a race condition: a station might go to sleep while there is * data buffered on hardware queues. If the device has support for this * it will reject frames, and the driver should give the frames back to * mac80211 with the %IEEE80211_TX_STAT_TX_FILTERED flag set which will * cause mac80211 to retry the frame when the station wakes up. The * driver is also notified of powersave transitions by calling its * @sta_notify callback. * * When the station is asleep, it has three choices: it can wake up, * it can PS-Poll, or it can possibly start a uAPSD service period. * Waking up is implemented by simply transmitting all buffered (and * filtered) frames to the station. This is the easiest case. When * the station sends a PS-Poll or a uAPSD trigger frame, mac80211 * will inform the driver of this with the @allow_buffered_frames * callback; this callback is optional. mac80211 will then transmit * the frames as usual and set the %IEEE80211_TX_CTL_NO_PS_BUFFER * on each frame. The last frame in the service period (or the only * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to * indicate that it ends the service period; as this frame must have * TX status report it also sets %IEEE80211_TX_CTL_REQ_TX_STATUS. * When TX status is reported for this frame, the service period is * marked has having ended and a new one can be started by the peer. * * Additionally, non-bufferable MMPDUs can also be transmitted by * mac80211 with the %IEEE80211_TX_CTL_NO_PS_BUFFER set in them. * * Another race condition can happen on some devices like iwlwifi * when there are frames queued for the station and it wakes up * or polls; the frames that are already queued could end up being * transmitted first instead, causing reordering and/or wrong * processing of the EOSP. The cause is that allowing frames to be * transmitted to a certain station is out-of-band communication to * the device. To allow this problem to be solved, the driver can * call ieee80211_sta_block_awake() if frames are buffered when it * is notified that the station went to sleep. When all these frames * have been filtered (see above), it must call the function again * to indicate that the station is no longer blocked. * * If the driver buffers frames in the driver for aggregation in any * way, it must use the ieee80211_sta_set_buffered() call when it is * notified of the station going to sleep to inform mac80211 of any * TIDs that have frames buffered. Note that when a station wakes up * this information is reset (hence the requirement to call it when * informed of the station going to sleep). Then, when a service * period starts for any reason, @release_buffered_frames is called * with the number of frames to be released and which TIDs they are * to come from. In this case, the driver is responsible for setting * the EOSP (for uAPSD) and MORE_DATA bits in the released frames, * to help the @more_data paramter is passed to tell the driver if * there is more data on other TIDs -- the TIDs to release frames * from are ignored since mac80211 doesn't know how many frames the * buffers for those TIDs contain. * * If the driver also implement GO mode, where absence periods may * shorten service periods (or abort PS-Poll responses), it must * filter those response frames except in the case of frames that * are buffered in the driver -- those must remain buffered to avoid * reordering. Because it is possible that no frames are released * in this case, the driver must call ieee80211_sta_eosp_irqsafe() * to indicate to mac80211 that the service period ended anyway. * * Finally, if frames from multiple TIDs are released from mac80211 * but the driver might reorder them, it must clear & set the flags * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) * and also take care of the EOSP and MORE_DATA bits in the frame. * The driver may also use ieee80211_sta_eosp_irqsafe() in this case. */ /** * DOC: HW queue control * * Before HW queue control was introduced, mac80211 only had a single static * assignment of per-interface AC software queues to hardware queues. This * was problematic for a few reasons: * 1) off-channel transmissions might get stuck behind other frames * 2) multiple virtual interfaces couldn't be handled correctly * 3) after-DTIM frames could get stuck behind other frames * * To solve this, hardware typically uses multiple different queues for all * the different usages, and this needs to be propagated into mac80211 so it * won't have the same problem with the software queues. * * Therefore, mac80211 now offers the %IEEE80211_HW_QUEUE_CONTROL capability * flag that tells it that the driver implements its own queue control. To do * so, the driver will set up the various queues in each &struct ieee80211_vif * and the offchannel queue in &struct ieee80211_hw. In response, mac80211 will * use those queue IDs in the hw_queue field of &struct ieee80211_tx_info and * if necessary will queue the frame on the right software queue that mirrors * the hardware queue. * Additionally, the driver has to then use these HW queue IDs for the queue * management functions (ieee80211_stop_queue() et al.) * * The driver is free to set up the queue mappings as needed, multiple virtual * interfaces may map to the same hardware queues if needed. The setup has to * happen during add_interface or change_interface callbacks. For example, a * driver supporting station+station and station+AP modes might decide to have * 10 hardware queues to handle different scenarios: * * 4 AC HW queues for 1st vif: 0, 1, 2, 3 * 4 AC HW queues for 2nd vif: 4, 5, 6, 7 * after-DTIM queue for AP: 8 * off-channel queue: 9 * * It would then set up the hardware like this: * hw.offchannel_tx_hw_queue = 9 * * and the first virtual interface that is added as follows: * vif.hw_queue[IEEE80211_AC_VO] = 0 * vif.hw_queue[IEEE80211_AC_VI] = 1 * vif.hw_queue[IEEE80211_AC_BE] = 2 * vif.hw_queue[IEEE80211_AC_BK] = 3 * vif.cab_queue = 8 // if AP mode, otherwise %IEEE80211_INVAL_HW_QUEUE * and the second virtual interface with 4-7. * * If queue 6 gets full, for example, mac80211 would only stop the second * virtual interface's BE queue since virtual interface queues are per AC. * * Note that the vif.cab_queue value should be set to %IEEE80211_INVAL_HW_QUEUE * whenever the queue is not used (i.e. the interface is not in AP mode) if the * queue could potentially be shared since mac80211 will look at cab_queue when * a queue is stopped/woken even if the interface is not in AP mode. */ /** * enum ieee80211_filter_flags - hardware filter flags * * These flags determine what the filter in hardware should be * programmed to let through and what should not be passed to the * stack. It is always safe to pass more frames than requested, * but this has negative impact on power consumption. * * @FIF_PROMISC_IN_BSS: promiscuous mode within your BSS, * think of the BSS as your network segment and then this corresponds * to the regular ethernet device promiscuous mode. * * @FIF_ALLMULTI: pass all multicast frames, this is used if requested * by the user or if the hardware is not capable of filtering by * multicast address. * * @FIF_FCSFAIL: pass frames with failed FCS (but you need to set the * %RX_FLAG_FAILED_FCS_CRC for them) * * @FIF_PLCPFAIL: pass frames with failed PLCP CRC (but you need to set * the %RX_FLAG_FAILED_PLCP_CRC for them * * @FIF_BCN_PRBRESP_PROMISC: This flag is set during scanning to indicate * to the hardware that it should not filter beacons or probe responses * by BSSID. Filtering them can greatly reduce the amount of processing * mac80211 needs to do and the amount of CPU wakeups, so you should * honour this flag if possible. * * @FIF_CONTROL: pass control frames (except for PS Poll), if PROMISC_IN_BSS * is not set then only those addressed to this station. * * @FIF_OTHER_BSS: pass frames destined to other BSSes * * @FIF_PSPOLL: pass PS Poll frames, if PROMISC_IN_BSS is not set then only * those addressed to this station. * * @FIF_PROBE_REQ: pass probe request frames */ enum ieee80211_filter_flags { FIF_PROMISC_IN_BSS = 1<<0, FIF_ALLMULTI = 1<<1, FIF_FCSFAIL = 1<<2, FIF_PLCPFAIL = 1<<3, FIF_BCN_PRBRESP_PROMISC = 1<<4, FIF_CONTROL = 1<<5, FIF_OTHER_BSS = 1<<6, FIF_PSPOLL = 1<<7, FIF_PROBE_REQ = 1<<8, }; /** * enum ieee80211_ampdu_mlme_action - A-MPDU actions * * These flags are used with the ampdu_action() callback in * &struct ieee80211_ops to indicate which action is needed. * * Note that drivers MUST be able to deal with a TX aggregation * session being stopped even before they OK'ed starting it by * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer * might receive the addBA frame and send a delBA right away! * * @IEEE80211_AMPDU_RX_START: start Rx aggregation * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation * @IEEE80211_AMPDU_TX_START: start Tx aggregation * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational */ enum ieee80211_ampdu_mlme_action { IEEE80211_AMPDU_RX_START, IEEE80211_AMPDU_RX_STOP, IEEE80211_AMPDU_TX_START, IEEE80211_AMPDU_TX_STOP, IEEE80211_AMPDU_TX_OPERATIONAL, }; /** * enum ieee80211_frame_release_type - frame release reason * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll * @IEEE80211_FRAME_RELEASE_UAPSD: frame(s) released due to * frame received on trigger-enabled AC */ enum ieee80211_frame_release_type { IEEE80211_FRAME_RELEASE_PSPOLL, IEEE80211_FRAME_RELEASE_UAPSD, }; /** * enum ieee80211_rate_control_changed - flags to indicate what changed * * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit * to this station changed. * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed. * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer * changed (in IBSS mode) due to discovering more information about * the peer. */ enum ieee80211_rate_control_changed { IEEE80211_RC_BW_CHANGED = BIT(0), IEEE80211_RC_SMPS_CHANGED = BIT(1), IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2), }; /** * struct ieee80211_ops - callbacks from mac80211 to the driver * * This structure contains various callbacks that the driver may * handle or, in some cases, must handle, for example to configure * the hardware to a new channel or to transmit a frame. * * @tx: Handler that 802.11 module calls for each transmitted frame. * skb contains the buffer starting from the IEEE 802.11 header. * The low-level driver should send the frame out based on * configuration in the TX control data. This handler should, * preferably, never fail and stop queues appropriately. * Must be atomic. * * @start: Called before the first netdevice attached to the hardware * is enabled. This should turn on the hardware and must turn on * frame reception (for possibly enabled monitor interfaces.) * Returns negative error codes, these may be seen in userspace, * or zero. * When the device is started it should not have a MAC address * to avoid acknowledging frames before a non-monitor device * is added. * Must be implemented and can sleep. * * @stop: Called after last netdevice attached to the hardware * is disabled. This should turn off the hardware (at least * it must turn off frame reception.) * May be called right after add_interface if that rejects * an interface. If you added any work onto the mac80211 workqueue * you should ensure to cancel it on this callback. * Must be implemented and can sleep. * * @suspend: Suspend the device; mac80211 itself will quiesce before and * stop transmitting and doing any other configuration, and then * ask the device to suspend. This is only invoked when WoWLAN is * configured, otherwise the device is deconfigured completely and * reconfigured at resume time. * The driver may also impose special conditions under which it * wants to use the "normal" suspend (deconfigure), say if it only * supports WoWLAN when the device is associated. In this case, it * must return 1 from this function. * * @resume: If WoWLAN was configured, this indicates that mac80211 is * now resuming its operation, after this the device must be fully * functional again. If this returns an error, the only way out is * to also unregister the device. If it returns 1, then mac80211 * will also go through the regular complete restart on resume. * * @set_wakeup: Enable or disable wakeup when WoWLAN configuration is * modified. The reason is that device_set_wakeup_enable() is * supposed to be called when the configuration changes, not only * in suspend(). * * @add_interface: Called when a netdevice attached to the hardware is * enabled. Because it is not called for monitor mode devices, @start * and @stop must be implemented. * The driver should perform any initialization it needs before * the device can be enabled. The initial configuration for the * interface is given in the conf parameter. * The callback may refuse to add an interface by returning a * negative error code (which will be seen in userspace.) * Must be implemented and can sleep. * * @change_interface: Called when a netdevice changes type. This callback * is optional, but only if it is supported can interface types be * switched while the interface is UP. The callback may sleep. * Note that while an interface is being switched, it will not be * found by the interface iteration callbacks. * * @remove_interface: Notifies a driver that an interface is going down. * The @stop callback is called after this if it is the last interface * and no monitor interfaces are present. * When all interfaces are removed, the MAC address in the hardware * must be cleared so the device no longer acknowledges packets, * the mac_addr member of the conf structure is, however, set to the * MAC address of the device going away. * Hence, this callback must be implemented. It can sleep. * * @config: Handler for configuration requests. IEEE 802.11 code calls this * function to change hardware configuration, e.g., channel. * This function should never fail but returns a negative error code * if it does. The callback can sleep. * * @bss_info_changed: Handler for configuration requests related to BSS * parameters that may vary during BSS's lifespan, and may affect low * level driver (e.g. assoc/disassoc status, erp parameters). * This function should not be used if no BSS has been set, unless * for association indication. The @changed parameter indicates which * of the bss parameters has changed when a call is made. The callback * can sleep. * * @prepare_multicast: Prepare for multicast filter configuration. * This callback is optional, and its return value is passed * to configure_filter(). This callback must be atomic. * * @configure_filter: Configure the device's RX filter. * See the section "Frame filtering" for more information. * This callback must be implemented and can sleep. * * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit * must be set or cleared for a given STA. Must be atomic. * * @set_key: See the section "Hardware crypto acceleration" * This callback is only called between add_interface and * remove_interface calls, i.e. while the given virtual interface * is enabled. * Returns a negative error code if the key can't be added. * The callback can sleep. * * @update_tkip_key: See the section "Hardware crypto acceleration" * This callback will be called in the context of Rx. Called for drivers * which set IEEE80211_KEY_FLAG_TKIP_REQ_RX_P1_KEY. * The callback must be atomic. * * @set_rekey_data: If the device supports GTK rekeying, for example while the * host is suspended, it can assign this callback to retrieve the data * necessary to do GTK rekeying, this is the KEK, KCK and replay counter. * After rekeying was done it should (for example during resume) notify * userspace of the new replay counter using ieee80211_gtk_rekey_notify(). * * @hw_scan: Ask the hardware to service the scan request, no need to start * the scan state machine in stack. The scan must honour the channel * configuration done by the regulatory agent in the wiphy's * registered bands. The hardware (or the driver) needs to make sure * that power save is disabled. * The @req ie/ie_len members are rewritten by mac80211 to contain the * entire IEs after the SSID, so that drivers need not look at these * at all but just send them after the SSID -- mac80211 includes the * (extended) supported rates and HT information (where applicable). * When the scan finishes, ieee80211_scan_completed() must be called; * note that it also must be called when the scan cannot finish due to * any error unless this callback returned a negative error code. * The callback can sleep. * * @cancel_hw_scan: Ask the low-level tp cancel the active hw scan. * The driver should ask the hardware to cancel the scan (if possible), * but the scan will be completed only after the driver will call * ieee80211_scan_completed(). * This callback is needed for wowlan, to prevent enqueueing a new * scan_work after the low-level driver was already suspended. * The callback can sleep. * * @sched_scan_start: Ask the hardware to start scanning repeatedly at * specific intervals. The driver must call the * ieee80211_sched_scan_results() function whenever it finds results. * This process will continue until sched_scan_stop is called. * * @sched_scan_stop: Tell the hardware to stop an ongoing scheduled scan. * * @sw_scan_start: Notifier function that is called just before a software scan * is started. Can be NULL, if the driver doesn't need this notification. * The callback can sleep. * * @sw_scan_complete: Notifier function that is called just after a * software scan finished. Can be NULL, if the driver doesn't need * this notification. * The callback can sleep. * * @get_stats: Return low-level statistics. * Returns zero if statistics are available. * The callback can sleep. * * @get_tkip_seq: If your device implements TKIP encryption in hardware this * callback should be provided to read the TKIP transmit IVs (both IV32 * and IV16) for the given key from hardware. * The callback must be atomic. * * @set_frag_threshold: Configuration of fragmentation threshold. Assign this * if the device does fragmentation by itself; if this callback is * implemented then the stack will not do fragmentation. * The callback can sleep. * * @set_rts_threshold: Configuration of RTS threshold (if device needs it) * The callback can sleep. * * @sta_add: Notifies low level driver about addition of an associated station, * AP, IBSS/WDS/mesh peer etc. This callback can sleep. * * @sta_remove: Notifies low level driver about removal of an associated * station, AP, IBSS/WDS/mesh peer etc. This callback can sleep. * * @sta_notify: Notifies low level driver about power state transition of an * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating * in AP mode, this callback will not be called when the flag * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic. * * @sta_state: Notifies low level driver about state transition of a * station (which can be the AP, a client, IBSS/WDS/mesh peer etc.) * This callback is mutually exclusive with @sta_add/@sta_remove. * It must not fail for down transitions but may fail for transitions * up the list of states. * The callback can sleep. * * @sta_rc_update: Notifies the driver of changes to the bitrates that can be * used to transmit to the station. The changes are advertised with bits * from &enum ieee80211_rate_control_changed and the values are reflected * in the station data. This callback should only be used when the driver * uses hardware rate control (%IEEE80211_HW_HAS_RATE_CONTROL) since * otherwise the rate control algorithm is notified directly. * Must be atomic. * * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * bursting) for a hardware TX queue. * Returns a negative error code on failure. * The callback can sleep. * * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently, * this is only used for IBSS mode BSSID merging and debugging. Is not a * required function. * The callback can sleep. * * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware. * Currently, this is only used for IBSS mode debugging. Is not a * required function. * The callback can sleep. * * @reset_tsf: Reset the TSF timer and allow firmware/hardware to synchronize * with other STAs in the IBSS. This is only used in IBSS mode. This * function is optional if the firmware/hardware takes full care of * TSF synchronization. * The callback can sleep. * * @tx_last_beacon: Determine whether the last IBSS beacon was sent by us. * This is needed only for IBSS mode and the result of this function is * used to determine whether to reply to Probe Requests. * Returns non-zero if this device sent the last beacon. * The callback can sleep. * * @ampdu_action: Perform a certain A-MPDU action * The RA/TID combination determines the destination and TID we want * the ampdu action to be performed for. The action is defined through * ieee80211_ampdu_mlme_action. Starting sequence number (@ssn) * is the first frame we expect to perform the action on. Notice * that TX/RX_STOP can pass NULL for this parameter. * The @buf_size parameter is only valid when the action is set to * %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's reorder * buffer size (number of subframes) for this session -- the driver * may neither send aggregates containing more subframes than this * nor send aggregates in a way that lost frames would exceed the * buffer size. If just limiting the aggregate size, this would be * possible with a buf_size of 8: * - TX: 1.....7 * - RX: 2....7 (lost frame #1) * - TX: 8..1... * which is invalid since #1 was now re-transmitted well past the * buffer size of 8. Correct ways to retransmit #1 would be: * - TX: 1 or 18 or 81 * Even "189" would be wrong since 1 could be lost again. * * Returns a negative error code on failure. * The callback can sleep. * * @get_survey: Return per-channel survey information * * @rfkill_poll: Poll rfkill hardware state. If you need this, you also * need to set wiphy->rfkill_poll to %true before registration, * and need to call wiphy_rfkill_set_hw_state() in the callback. * The callback can sleep. * * @set_coverage_class: Set slot time for given coverage class as specified * in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout * accordingly. This callback is not required and may sleep. * * @testmode_cmd: Implement a cfg80211 test mode command. * The callback can sleep. * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep. * * @flush: Flush all pending frames from the hardware queue, making sure * that the hardware queues are empty. If the parameter @drop is set * to %true, pending frames may be dropped. The callback can sleep. * * @channel_switch: Drivers that need (or want) to offload the channel * switch operation for CSAs received from the AP may implement this * callback. They must then call ieee80211_chswitch_done() to indicate * completion of the channel switch. * * @napi_poll: Poll Rx queue for incoming data frames. * * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device. * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may * reject TX/RX mask combinations they cannot support by returning -EINVAL * (also see nl80211.h @NL80211_ATTR_WIPHY_ANTENNA_TX). * * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant). * * @remain_on_channel: Starts an off-channel period on the given channel, must * call back to ieee80211_ready_on_channel() when on that channel. Note * that normal channel traffic is not stopped as this is intended for hw * offload. Frames to transmit on the off-channel channel are transmitted * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the * duration (which will always be non-zero) expires, the driver must call * ieee80211_remain_on_channel_expired(). * Note that this callback may be called while the device is in IDLE and * must be accepted in this case. * This callback may sleep. * @cancel_remain_on_channel: Requests that an ongoing off-channel period is * aborted before it expires. This callback may sleep. * * @set_ringparam: Set tx and rx ring sizes. * * @get_ringparam: Get tx and rx ring current and maximum sizes. * * @tx_frames_pending: Check if there is any pending frame in the hardware * queues before entering power save. * * @set_bitrate_mask: Set a mask of rates to be used for rate control selection * when transmitting a frame. Currently only legacy rates are handled. * The callback can sleep. * @rssi_callback: Notify driver when the average RSSI goes above/below * thresholds that were registered previously. The callback can sleep. * * @release_buffered_frames: Release buffered frames according to the given * parameters. In the case where the driver buffers some frames for * sleeping stations mac80211 will use this callback to tell the driver * to release some frames, either for PS-poll or uAPSD. * Note that if the @more_data paramter is %false the driver must check * if there are more frames on the given TIDs, and if there are more than * the frames being released then it must still set the more-data bit in * the frame. If the @more_data parameter is %true, then of course the * more-data bit must always be set. * The @tids parameter tells the driver which TIDs to release frames * from, for PS-poll it will always have only a single bit set. * In the case this is used for a PS-poll initiated release, the * @num_frames parameter will always be 1 so code can be shared. In * this case the driver must also set %IEEE80211_TX_STATUS_EOSP flag * on the TX status (and must report TX status) so that the PS-poll * period is properly ended. This is used to avoid sending multiple * responses for a retried PS-poll frame. * In the case this is used for uAPSD, the @num_frames parameter may be * bigger than one, but the driver may send fewer frames (it must send * at least one, however). In this case it is also responsible for * setting the EOSP flag in the QoS header of the frames. Also, when the * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP * on the last frame in the SP. Alternatively, it may call the function * ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP. * This callback must be atomic. * @allow_buffered_frames: Prepare device to allow the given number of frames * to go out to the given station. The frames will be sent by mac80211 * via the usual TX path after this call. The TX information for frames * released will also have the %IEEE80211_TX_CTL_NO_PS_BUFFER flag set * and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case * frames from multiple TIDs are released and the driver might reorder * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag * on the last frame and clear it on all others and also handle the EOSP * bit in the QoS header correctly. Alternatively, it can also call the * ieee80211_sta_eosp_irqsafe() function. * The @tids parameter is a bitmap and tells the driver which TIDs the * frames will be on; it will at most have two bits set. * This callback must be atomic. * * @get_et_sset_count: Ethtool API to get string-set count. * * @get_et_stats: Ethtool API to get a set of u64 stats. * * @get_et_strings: Ethtool API to get a set of strings to describe stats * and perhaps other supported types of ethtool data-sets. * * @get_rssi: Get current signal strength in dBm, the function is optional * and can sleep. * * @mgd_prepare_tx: Prepare for transmitting a management frame for association * before associated. In multi-channel scenarios, a virtual interface is * bound to a channel before it is associated, but as it isn't associated * yet it need not necessarily be given airtime, in particular since any * transmission to a P2P GO needs to be synchronized against the GO's * powersave state. mac80211 will call this function before transmitting a * management frame prior to having successfully associated to allow the * driver to give it channel time for the transmission, to get a response * and to be able to synchronize with the GO. * The callback will be called before each transmission and upon return * mac80211 will transmit the frame right away. * The callback is optional and can (should!) sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); int (*start)(struct ieee80211_hw *hw); void (*stop)(struct ieee80211_hw *hw); #ifdef CONFIG_PM int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int (*resume)(struct ieee80211_hw *hw); void (*set_wakeup)(struct ieee80211_hw *hw, bool enabled); #endif int (*add_interface)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*change_interface)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype new_type, bool p2p); void (*remove_interface)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*config)(struct ieee80211_hw *hw, u32 changed); void (*bss_info_changed)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed); u64 (*prepare_multicast)(struct ieee80211_hw *hw, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list); #else int mc_count, struct dev_addr_list *mc_list); #endif void (*configure_filter)(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast); int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); void (*update_tkip_key)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_key_conf *conf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); void (*set_rekey_data)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *data); int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req); void (*cancel_hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*sched_scan_start)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); void (*sched_scan_stop)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*sw_scan_start)(struct ieee80211_hw *hw); void (*sw_scan_complete)(struct ieee80211_hw *hw); int (*get_stats)(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, u16 *iv16); int (*set_frag_threshold)(struct ieee80211_hw *hw, u32 value); int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value); int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state); void (*sta_rc_update)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u32 changed); int (*conf_tx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 ac, const struct ieee80211_tx_queue_params *params); u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf); void (*reset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*tx_last_beacon)(struct ieee80211_hw *hw); int (*ampdu_action)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size); int (*get_survey)(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void (*rfkill_poll)(struct ieee80211_hw *hw); void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class); #ifdef CONFIG_NL80211_TESTMODE int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len); #endif void (*flush)(struct ieee80211_hw *hw, bool drop); void (*channel_switch)(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch); int (*napi_poll)(struct ieee80211_hw *hw, int budget); int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); int (*remain_on_channel)(struct ieee80211_hw *hw, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, int duration); int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx); void (*get_ringparam)(struct ieee80211_hw *hw, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); bool (*tx_frames_pending)(struct ieee80211_hw *hw); int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask); void (*rssi_callback)(struct ieee80211_hw *hw, enum ieee80211_rssi_event rssi_event); void (*allow_buffered_frames)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data); void (*release_buffered_frames)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data); int (*get_et_sset_count)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int sset); void (*get_et_stats)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ethtool_stats *stats, u64 *data); void (*get_et_strings)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 sset, u8 *data); int (*get_rssi)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, s8 *rssi_dbm); void (*mgd_prepare_tx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); }; /** * ieee80211_alloc_hw - Allocate a new hardware device * * This must be called once for each hardware device. The returned pointer * must be used to refer to this device when calling other functions. * mac80211 allocates a private data area for the driver pointed to by * @priv in &struct ieee80211_hw, the size of this area is given as * @priv_data_len. * * @priv_data_len: length of private data * @ops: callbacks for this device */ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, const struct ieee80211_ops *ops); /** * ieee80211_register_hw - Register hardware device * * You must call this function before any other functions in * mac80211. Note that before a hardware can be registered, you * need to fill the contained wiphy's information. * * @hw: the device to register as returned by ieee80211_alloc_hw() */ int ieee80211_register_hw(struct ieee80211_hw *hw); /** * struct ieee80211_tpt_blink - throughput blink description * @throughput: throughput in Kbit/sec * @blink_time: blink time in milliseconds * (full cycle, ie. one off + one on period) */ struct ieee80211_tpt_blink { int throughput; int blink_time; }; /** * enum ieee80211_tpt_led_trigger_flags - throughput trigger flags * @IEEE80211_TPT_LEDTRIG_FL_RADIO: enable blinking with radio * @IEEE80211_TPT_LEDTRIG_FL_WORK: enable blinking when working * @IEEE80211_TPT_LEDTRIG_FL_CONNECTED: enable blinking when at least one * interface is connected in some way, including being an AP */ enum ieee80211_tpt_led_trigger_flags { IEEE80211_TPT_LEDTRIG_FL_RADIO = BIT(0), IEEE80211_TPT_LEDTRIG_FL_WORK = BIT(1), IEEE80211_TPT_LEDTRIG_FL_CONNECTED = BIT(2), }; #ifdef CONFIG_MAC80211_LEDS extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw); extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw); extern char *__ieee80211_create_tpt_led_trigger( struct ieee80211_hw *hw, unsigned int flags, const struct ieee80211_tpt_blink *blink_table, unsigned int blink_table_len); #endif /** * ieee80211_get_tx_led_name - get name of TX LED * * mac80211 creates a transmit LED trigger for each wireless hardware * that can be used to drive LEDs if your driver registers a LED device. * This function returns the name (or %NULL if not configured for LEDs) * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for */ static inline char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw) { #ifdef CONFIG_MAC80211_LEDS return __ieee80211_get_tx_led_name(hw); #else return NULL; #endif } /** * ieee80211_get_rx_led_name - get name of RX LED * * mac80211 creates a receive LED trigger for each wireless hardware * that can be used to drive LEDs if your driver registers a LED device. * This function returns the name (or %NULL if not configured for LEDs) * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for */ static inline char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw) { #ifdef CONFIG_MAC80211_LEDS return __ieee80211_get_rx_led_name(hw); #else return NULL; #endif } /** * ieee80211_get_assoc_led_name - get name of association LED * * mac80211 creates a association LED trigger for each wireless hardware * that can be used to drive LEDs if your driver registers a LED device. * This function returns the name (or %NULL if not configured for LEDs) * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for */ static inline char *ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) { #ifdef CONFIG_MAC80211_LEDS return __ieee80211_get_assoc_led_name(hw); #else return NULL; #endif } /** * ieee80211_get_radio_led_name - get name of radio LED * * mac80211 creates a radio change LED trigger for each wireless hardware * that can be used to drive LEDs if your driver registers a LED device. * This function returns the name (or %NULL if not configured for LEDs) * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for */ static inline char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw) { #ifdef CONFIG_MAC80211_LEDS return __ieee80211_get_radio_led_name(hw); #else return NULL; #endif } /** * ieee80211_create_tpt_led_trigger - create throughput LED trigger * @hw: the hardware to create the trigger for * @flags: trigger flags, see &enum ieee80211_tpt_led_trigger_flags * @blink_table: the blink table -- needs to be ordered by throughput * @blink_table_len: size of the blink table * * This function returns %NULL (in case of error, or if no LED * triggers are configured) or the name of the new trigger. * This function must be called before ieee80211_register_hw(). */ static inline char * ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags, const struct ieee80211_tpt_blink *blink_table, unsigned int blink_table_len) { #ifdef CONFIG_MAC80211_LEDS return __ieee80211_create_tpt_led_trigger(hw, flags, blink_table, blink_table_len); #else return NULL; #endif } /** * ieee80211_unregister_hw - Unregister a hardware device * * This function instructs mac80211 to free allocated resources * and unregister netdevices from the networking subsystem. * * @hw: the hardware to unregister */ void ieee80211_unregister_hw(struct ieee80211_hw *hw); /** * ieee80211_free_hw - free hardware descriptor * * This function frees everything that was allocated, including the * private data for the driver. You must call ieee80211_unregister_hw() * before calling this function. * * @hw: the hardware to free */ void ieee80211_free_hw(struct ieee80211_hw *hw); /** * ieee80211_restart_hw - restart hardware completely * * Call this function when the hardware was restarted for some reason * (hardware error, ...) and the driver is unable to restore its state * by itself. mac80211 assumes that at this point the driver/hardware * is completely uninitialised and stopped, it starts the process by * calling the ->start() operation. The driver will need to reset all * internal state that it has prior to calling this function. * * @hw: the hardware to restart */ void ieee80211_restart_hw(struct ieee80211_hw *hw); /** ieee80211_napi_schedule - schedule NAPI poll * * Use this function to schedule NAPI polling on a device. * * @hw: the hardware to start polling */ void ieee80211_napi_schedule(struct ieee80211_hw *hw); /** ieee80211_napi_complete - complete NAPI polling * * Use this function to finish NAPI polling on a device. * * @hw: the hardware to stop polling */ void ieee80211_napi_complete(struct ieee80211_hw *hw); /** * ieee80211_rx - receive frame * * Use this function to hand received frames to mac80211. The receive * buffer in @skb must start with an IEEE 802.11 header. In case of a * paged @skb is used, the driver is recommended to put the ieee80211 * header of the frame on the linear part of the @skb to avoid memory * allocation and/or memcpy by the stack. * * This function may not be called in IRQ context. Calls to this function * for a single hardware must be synchronized against each other. Calls to * this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be * mixed for a single hardware. * * In process context use instead ieee80211_rx_ni(). * * @hw: the hardware this frame came in on * @skb: the buffer to receive, owned by mac80211 after this call */ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb); /** * ieee80211_rx_irqsafe - receive frame * * Like ieee80211_rx() but can be called in IRQ context * (internally defers to a tasklet.) * * Calls to this function, ieee80211_rx() or ieee80211_rx_ni() may not * be mixed for a single hardware. * * @hw: the hardware this frame came in on * @skb: the buffer to receive, owned by mac80211 after this call */ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb); /** * ieee80211_rx_ni - receive frame (in process context) * * Like ieee80211_rx() but can be called in process context * (internally disables bottom halves). * * Calls to this function, ieee80211_rx() and ieee80211_rx_irqsafe() may * not be mixed for a single hardware. * * @hw: the hardware this frame came in on * @skb: the buffer to receive, owned by mac80211 after this call */ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw, struct sk_buff *skb) { local_bh_disable(); ieee80211_rx(hw, skb); local_bh_enable(); } /** * ieee80211_sta_ps_transition - PS transition for connected sta * * When operating in AP mode with the %IEEE80211_HW_AP_LINK_PS * flag set, use this function to inform mac80211 about a connected station * entering/leaving PS mode. * * This function may not be called in IRQ context or with softirqs enabled. * * Calls to this function for a single hardware must be synchronized against * each other. * * The function returns -EINVAL when the requested PS mode is already set. * * @sta: currently connected sta * @start: start or stop PS */ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); /** * ieee80211_sta_ps_transition_ni - PS transition for connected sta * (in process context) * * Like ieee80211_sta_ps_transition() but can be called in process context * (internally disables bottom halves). Concurrent call restriction still * applies. * * @sta: currently connected sta * @start: start or stop PS */ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, bool start) { int ret; local_bh_disable(); ret = ieee80211_sta_ps_transition(sta, start); local_bh_enable(); return ret; } /* * The TX headroom reserved by mac80211 for its own tx_status functions. * This is enough for the radiotap header. */ #define IEEE80211_TX_STATUS_HEADROOM 14 /** * ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames * @sta: &struct ieee80211_sta pointer for the sleeping station * @tid: the TID that has buffered frames * @buffered: indicates whether or not frames are buffered for this TID * * If a driver buffers frames for a powersave station instead of passing * them back to mac80211 for retransmission, the station may still need * to be told that there are buffered frames via the TIM bit. * * This function informs mac80211 whether or not there are frames that are * buffered in the driver for a given TID; mac80211 can then use this data * to set the TIM bit (NOTE: This may call back into the driver's set_tim * call! Beware of the locking!) * * If all frames are released to the station (due to PS-poll or uAPSD) * then the driver needs to inform mac80211 that there no longer are * frames buffered. However, when the station wakes up mac80211 assumes * that all buffered frames will be transmitted and clears this data, * drivers need to make sure they inform mac80211 about all buffered * frames on the sleep transition (sta_notify() with %STA_NOTIFY_SLEEP). * * Note that technically mac80211 only needs to know this per AC, not per * TID, but since driver buffering will inevitably happen per TID (since * it is related to aggregation) it is easier to make mac80211 map the * TID to the AC as required instead of keeping track in all drivers that * use this API. */ void ieee80211_sta_set_buffered(struct ieee80211_sta *sta, u8 tid, bool buffered); /** * ieee80211_tx_status - transmit status callback * * Call this function for all transmitted frames after they have been * transmitted. It is permissible to not call this function for * multicast frames but this can affect statistics. * * This function may not be called in IRQ context. Calls to this function * for a single hardware must be synchronized against each other. Calls * to this function, ieee80211_tx_status_ni() and ieee80211_tx_status_irqsafe() * may not be mixed for a single hardware. * * @hw: the hardware the frame was transmitted by * @skb: the frame that was transmitted, owned by mac80211 after this call */ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb); /** * ieee80211_tx_status_ni - transmit status callback (in process context) * * Like ieee80211_tx_status() but can be called in process context. * * Calls to this function, ieee80211_tx_status() and * ieee80211_tx_status_irqsafe() may not be mixed * for a single hardware. * * @hw: the hardware the frame was transmitted by * @skb: the frame that was transmitted, owned by mac80211 after this call */ static inline void ieee80211_tx_status_ni(struct ieee80211_hw *hw, struct sk_buff *skb) { local_bh_disable(); ieee80211_tx_status(hw, skb); local_bh_enable(); } /** * ieee80211_tx_status_irqsafe - IRQ-safe transmit status callback * * Like ieee80211_tx_status() but can be called in IRQ context * (internally defers to a tasklet.) * * Calls to this function, ieee80211_tx_status() and * ieee80211_tx_status_ni() may not be mixed for a single hardware. * * @hw: the hardware the frame was transmitted by * @skb: the frame that was transmitted, owned by mac80211 after this call */ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb); /** * ieee80211_report_low_ack - report non-responding station * * When operating in AP-mode, call this function to report a non-responding * connected STA. * * @sta: the non-responding connected sta * @num_packets: number of packets sent to @sta without a response */ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); /** * ieee80211_beacon_get_tim - beacon generation function * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @tim_offset: pointer to variable that will receive the TIM IE offset. * Set to 0 if invalid (in non-AP modes). * @tim_length: pointer to variable that will receive the TIM IE length, * (including the ID and length bytes!). * Set to 0 if invalid (in non-AP modes). * * If the driver implements beaconing modes, it must use this function to * obtain the beacon frame/template. * * If the beacon frames are generated by the host system (i.e., not in * hardware/firmware), the driver uses this function to get each beacon * frame from mac80211 -- it is responsible for calling this function * before the beacon is needed (e.g. based on hardware interrupt). * * If the beacon frames are generated by the device, then the driver * must use the returned beacon as the template and change the TIM IE * according to the current DTIM parameters/TIM bitmap. * * The driver is responsible for freeing the returned skb. */ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 *tim_offset, u16 *tim_length); /** * ieee80211_beacon_get - beacon generation function * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * See ieee80211_beacon_get_tim(). */ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { return ieee80211_beacon_get_tim(hw, vif, NULL, NULL); } /** * ieee80211_proberesp_get - retrieve a Probe Response template * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * Creates a Probe Response template which can, for example, be uploaded to * hardware. The destination address should be set by the caller. * * Can only be called in AP mode. */ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); /** * ieee80211_pspoll_get - retrieve a PS Poll template * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * Creates a PS Poll a template which can, for example, uploaded to * hardware. The template must be updated after association so that correct * AID, BSSID and MAC address is used. * * Note: Caller (or hardware) is responsible for setting the * &IEEE80211_FCTL_PM bit. */ struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); /** * ieee80211_nullfunc_get - retrieve a nullfunc template * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * Creates a Nullfunc template which can, for example, uploaded to * hardware. The template must be updated after association so that correct * BSSID and address is used. * * Note: Caller (or hardware) is responsible for setting the * &IEEE80211_FCTL_PM bit as well as Duration and Sequence Control fields. */ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); /** * ieee80211_probereq_get - retrieve a Probe Request template * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @ssid: SSID buffer * @ssid_len: length of SSID * @ie: buffer containing all IEs except SSID for the template * @ie_len: length of the IE buffer * * Creates a Probe Request template which can, for example, be uploaded to * hardware. */ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len); /** * ieee80211_rts_get - RTS frame generation function * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @frame: pointer to the frame that is going to be protected by the RTS. * @frame_len: the frame length (in octets). * @frame_txctl: &struct ieee80211_tx_info of the frame. * @rts: The buffer where to store the RTS frame. * * If the RTS frames are generated by the host system (i.e., not in * hardware/firmware), the low-level driver uses this function to receive * the next RTS frame from the 802.11 code. The low-level is responsible * for calling this function before and RTS frame is needed. */ void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const void *frame, size_t frame_len, const struct ieee80211_tx_info *frame_txctl, struct ieee80211_rts *rts); /** * ieee80211_rts_duration - Get the duration field for an RTS frame * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @frame_len: the length of the frame that is going to be protected by the RTS. * @frame_txctl: &struct ieee80211_tx_info of the frame. * * If the RTS is generated in firmware, but the host system must provide * the duration field, the low-level driver uses this function to receive * the duration field value in little-endian byteorder. */ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, size_t frame_len, const struct ieee80211_tx_info *frame_txctl); /** * ieee80211_ctstoself_get - CTS-to-self frame generation function * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @frame: pointer to the frame that is going to be protected by the CTS-to-self. * @frame_len: the frame length (in octets). * @frame_txctl: &struct ieee80211_tx_info of the frame. * @cts: The buffer where to store the CTS-to-self frame. * * If the CTS-to-self frames are generated by the host system (i.e., not in * hardware/firmware), the low-level driver uses this function to receive * the next CTS-to-self frame from the 802.11 code. The low-level is responsible * for calling this function before and CTS-to-self frame is needed. */ void ieee80211_ctstoself_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const void *frame, size_t frame_len, const struct ieee80211_tx_info *frame_txctl, struct ieee80211_cts *cts); /** * ieee80211_ctstoself_duration - Get the duration field for a CTS-to-self frame * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @frame_len: the length of the frame that is going to be protected by the CTS-to-self. * @frame_txctl: &struct ieee80211_tx_info of the frame. * * If the CTS-to-self is generated in firmware, but the host system must provide * the duration field, the low-level driver uses this function to receive * the duration field value in little-endian byteorder. */ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, size_t frame_len, const struct ieee80211_tx_info *frame_txctl); /** * ieee80211_generic_frame_duration - Calculate the duration field for a frame * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @band: the band to calculate the frame duration on * @frame_len: the length of the frame. * @rate: the rate at which the frame is going to be transmitted. * * Calculate the duration field of some generic frame, given its * length and transmission rate (in 100kbps). */ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_band band, size_t frame_len, struct ieee80211_rate *rate); /** * ieee80211_get_buffered_bc - accessing buffered broadcast and multicast frames * @hw: pointer as obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * Function for accessing buffered broadcast and multicast frames. If * hardware/firmware does not implement buffering of broadcast/multicast * frames when power saving is used, 802.11 code buffers them in the host * memory. The low-level driver uses this function to fetch next buffered * frame. In most cases, this is used when generating beacon frame. This * function returns a pointer to the next buffered skb or NULL if no more * buffered frames are available. * * Note: buffered frames are returned only after DTIM beacon frame was * generated with ieee80211_beacon_get() and the low-level driver must thus * call ieee80211_beacon_get() first. ieee80211_get_buffered_bc() returns * NULL if the previous generated beacon was not DTIM, so the low-level driver * does not need to check for DTIM beacons separately and should be able to * use common code for all beacons. */ struct sk_buff * ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif); /** * ieee80211_get_tkip_p1k_iv - get a TKIP phase 1 key for IV32 * * This function returns the TKIP phase 1 key for the given IV32. * * @keyconf: the parameter passed with the set key * @iv32: IV32 to get the P1K for * @p1k: a buffer to which the key will be written, as 5 u16 values */ void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, u32 iv32, u16 *p1k); /** * ieee80211_get_tkip_p1k - get a TKIP phase 1 key * * This function returns the TKIP phase 1 key for the IV32 taken * from the given packet. * * @keyconf: the parameter passed with the set key * @skb: the packet to take the IV32 value from that will be encrypted * with this P1K * @p1k: a buffer to which the key will be written, as 5 u16 values */ static inline void ieee80211_get_tkip_p1k(struct ieee80211_key_conf *keyconf, struct sk_buff *skb, u16 *p1k) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); u32 iv32 = get_unaligned_le32(&data[4]); ieee80211_get_tkip_p1k_iv(keyconf, iv32, p1k); } /** * ieee80211_get_tkip_rx_p1k - get a TKIP phase 1 key for RX * * This function returns the TKIP phase 1 key for the given IV32 * and transmitter address. * * @keyconf: the parameter passed with the set key * @ta: TA that will be used with the key * @iv32: IV32 to get the P1K for * @p1k: a buffer to which the key will be written, as 5 u16 values */ void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, const u8 *ta, u32 iv32, u16 *p1k); /** * ieee80211_get_tkip_p2k - get a TKIP phase 2 key * * This function computes the TKIP RC4 key for the IV values * in the packet. * * @keyconf: the parameter passed with the set key * @skb: the packet to take the IV32/IV16 values from that will be * encrypted with this key * @p2k: a buffer to which the key will be written, 16 bytes */ void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, struct sk_buff *skb, u8 *p2k); /** * struct ieee80211_key_seq - key sequence counter * * @tkip: TKIP data, containing IV32 and IV16 in host byte order * @ccmp: PN data, most significant byte first (big endian, * reverse order than in packet) * @aes_cmac: PN data, most significant byte first (big endian, * reverse order than in packet) */ struct ieee80211_key_seq { union { struct { u32 iv32; u16 iv16; } tkip; struct { u8 pn[6]; } ccmp; struct { u8 pn[6]; } aes_cmac; }; }; /** * ieee80211_get_key_tx_seq - get key TX sequence counter * * @keyconf: the parameter passed with the set key * @seq: buffer to receive the sequence data * * This function allows a driver to retrieve the current TX IV/PN * for the given key. It must not be called if IV generation is * offloaded to the device. * * Note that this function may only be called when no TX processing * can be done concurrently, for example when queues are stopped * and the stop has been synchronized. */ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, struct ieee80211_key_seq *seq); /** * ieee80211_get_key_rx_seq - get key RX sequence counter * * @keyconf: the parameter passed with the set key * @tid: The TID, or -1 for the management frame value (CCMP only); * the value on TID 0 is also used for non-QoS frames. For * CMAC, only TID 0 is valid. * @seq: buffer to receive the sequence data * * This function allows a driver to retrieve the current RX IV/PNs * for the given key. It must not be called if IV checking is done * by the device and not by mac80211. * * Note that this function may only be called when no RX processing * can be done concurrently. */ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int tid, struct ieee80211_key_seq *seq); /** * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying * @vif: virtual interface the rekeying was done on * @bssid: The BSSID of the AP, for checking association * @replay_ctr: the new replay counter after GTK rekeying * @gfp: allocation flags */ void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); /** * ieee80211_wake_queue - wake specific queue * @hw: pointer as obtained from ieee80211_alloc_hw(). * @queue: queue number (counted from zero). * * Drivers should use this function instead of netif_wake_queue. */ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue); /** * ieee80211_stop_queue - stop specific queue * @hw: pointer as obtained from ieee80211_alloc_hw(). * @queue: queue number (counted from zero). * * Drivers should use this function instead of netif_stop_queue. */ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue); /** * ieee80211_queue_stopped - test status of the queue * @hw: pointer as obtained from ieee80211_alloc_hw(). * @queue: queue number (counted from zero). * * Drivers should use this function instead of netif_stop_queue. */ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue); /** * ieee80211_stop_queues - stop all queues * @hw: pointer as obtained from ieee80211_alloc_hw(). * * Drivers should use this function instead of netif_stop_queue. */ void ieee80211_stop_queues(struct ieee80211_hw *hw); /** * ieee80211_wake_queues - wake all queues * @hw: pointer as obtained from ieee80211_alloc_hw(). * * Drivers should use this function instead of netif_wake_queue. */ void ieee80211_wake_queues(struct ieee80211_hw *hw); /** * ieee80211_scan_completed - completed hardware scan * * When hardware scan offload is used (i.e. the hw_scan() callback is * assigned) this function needs to be called by the driver to notify * mac80211 that the scan finished. This function can be called from * any context, including hardirq context. * * @hw: the hardware that finished the scan * @aborted: set to true if scan was aborted */ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted); /** * ieee80211_sched_scan_results - got results from scheduled scan * * When a scheduled scan is running, this function needs to be called by the * driver whenever there are new scan results available. * * @hw: the hardware that is performing scheduled scans */ void ieee80211_sched_scan_results(struct ieee80211_hw *hw); /** * ieee80211_sched_scan_stopped - inform that the scheduled scan has stopped * * When a scheduled scan is running, this function can be called by * the driver if it needs to stop the scan to perform another task. * Usual scenarios are drivers that cannot continue the scheduled scan * while associating, for instance. * * @hw: the hardware that is performing scheduled scans */ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); /** * ieee80211_iterate_active_interfaces - iterate active interfaces * * This function iterates over the interfaces associated with a given * hardware that are currently active and calls the callback for them. * This function allows the iterator function to sleep, when the iterator * function is atomic @ieee80211_iterate_active_interfaces_atomic can * be used. * Does not iterate over a new interface during add_interface() * * @hw: the hardware struct of which the interfaces should be iterated over * @iterator: the iterator function to call * @data: first argument of the iterator function */ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data); /** * ieee80211_iterate_active_interfaces_atomic - iterate active interfaces * * This function iterates over the interfaces associated with a given * hardware that are currently active and calls the callback for them. * This function requires the iterator callback function to be atomic, * if that is not desired, use @ieee80211_iterate_active_interfaces instead. * Does not iterate over a new interface during add_interface() * * @hw: the hardware struct of which the interfaces should be iterated over * @iterator: the iterator function to call, cannot sleep * @data: first argument of the iterator function */ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data); /** * ieee80211_queue_work - add work onto the mac80211 workqueue * * Drivers and mac80211 use this to add work onto the mac80211 workqueue. * This helper ensures drivers are not queueing work when they should not be. * * @hw: the hardware struct for the interface we are adding work for * @work: the work we want to add onto the mac80211 workqueue */ void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work); /** * ieee80211_queue_delayed_work - add work onto the mac80211 workqueue * * Drivers and mac80211 use this to queue delayed work onto the mac80211 * workqueue. * * @hw: the hardware struct for the interface we are adding work for * @dwork: delayable work to queue onto the mac80211 workqueue * @delay: number of jiffies to wait before queueing */ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, struct delayed_work *dwork, unsigned long delay); /** * ieee80211_start_tx_ba_session - Start a tx Block Ack session. * @sta: the station for which to start a BA session * @tid: the TID to BA on. * @timeout: session timeout value (in TUs) * * Return: success if addBA request was sent, failure otherwise * * Although mac80211/low level driver/user space application can estimate * the need to start aggregation on a certain RA/TID, the session level * will be managed by the mac80211. */ int ieee80211_start_tx_ba_session(struct ieee80211_sta *sta, u16 tid, u16 timeout); /** * ieee80211_start_tx_ba_cb_irqsafe - low level driver ready to aggregate. * @vif: &struct ieee80211_vif pointer from the add_interface callback * @ra: receiver address of the BA session recipient. * @tid: the TID to BA on. * * This function must be called by low level driver once it has * finished with preparations for the BA session. It can be called * from any context. */ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, u16 tid); /** * ieee80211_stop_tx_ba_session - Stop a Block Ack session. * @sta: the station whose BA session to stop * @tid: the TID to stop BA. * * Return: negative error if the TID is invalid, or no aggregation active * * Although mac80211/low level driver/user space application can estimate * the need to stop aggregation on a certain RA/TID, the session level * will be managed by the mac80211. */ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *sta, u16 tid); /** * ieee80211_stop_tx_ba_cb_irqsafe - low level driver ready to stop aggregate. * @vif: &struct ieee80211_vif pointer from the add_interface callback * @ra: receiver address of the BA session recipient. * @tid: the desired TID to BA on. * * This function must be called by low level driver once it has * finished with preparations for the BA session tear down. It * can be called from any context. */ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, u16 tid); /** * ieee80211_find_sta - find a station * * @vif: virtual interface to look for station on * @addr: station's address * * This function must be called under RCU lock and the * resulting pointer is only valid under RCU lock as well. */ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *addr); /** * ieee80211_find_sta_by_ifaddr - find a station on hardware * * @hw: pointer as obtained from ieee80211_alloc_hw() * @addr: remote station's address * @localaddr: local address (vif->sdata->vif.addr). Use NULL for 'any'. * * This function must be called under RCU lock and the * resulting pointer is only valid under RCU lock as well. * * NOTE: You may pass NULL for localaddr, but then you will just get * the first STA that matches the remote address 'addr'. * We can have multiple STA associated with multiple * logical stations (e.g. consider a station connecting to another * BSSID on the same AP hardware without disconnecting first). * In this case, the result of this method with localaddr NULL * is not reliable. * * DO NOT USE THIS FUNCTION with localaddr NULL if at all possible. */ struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, const u8 *addr, const u8 *localaddr); /** * ieee80211_sta_block_awake - block station from waking up * @hw: the hardware * @pubsta: the station * @block: whether to block or unblock * * Some devices require that all frames that are on the queues * for a specific station that went to sleep are flushed before * a poll response or frames after the station woke up can be * delivered to that it. Note that such frames must be rejected * by the driver as filtered, with the appropriate status flag. * * This function allows implementing this mode in a race-free * manner. * * To do this, a driver must keep track of the number of frames * still enqueued for a specific station. If this number is not * zero when the station goes to sleep, the driver must call * this function to force mac80211 to consider the station to * be asleep regardless of the station's actual state. Once the * number of outstanding frames reaches zero, the driver must * call this function again to unblock the station. That will * cause mac80211 to be able to send ps-poll responses, and if * the station queried in the meantime then frames will also * be sent out as a result of this. Additionally, the driver * will be notified that the station woke up some time after * it is unblocked, regardless of whether the station actually * woke up while blocked or not. */ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, bool block); /** * ieee80211_sta_eosp - notify mac80211 about end of SP * @pubsta: the station * * When a device transmits frames in a way that it can't tell * mac80211 in the TX status about the EOSP, it must clear the * %IEEE80211_TX_STATUS_EOSP bit and call this function instead. * This applies for PS-Poll as well as uAPSD. * * Note that there is no non-_irqsafe version right now as * it wasn't needed, but just like _tx_status() and _rx() * must not be mixed in irqsafe/non-irqsafe versions, this * function must not be mixed with those either. Use the * all irqsafe, or all non-irqsafe, don't mix! If you need * the non-irqsafe version of this, you need to add it. */ void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta); /** * ieee80211_iter_keys - iterate keys programmed into the device * @hw: pointer obtained from ieee80211_alloc_hw() * @vif: virtual interface to iterate, may be %NULL for all * @iter: iterator function that will be called for each key * @iter_data: custom data to pass to the iterator function * * This function can be used to iterate all the keys known to * mac80211, even those that weren't previously programmed into * the device. This is intended for use in WoWLAN if the device * needs reprogramming of the keys during suspend. Note that due * to locking reasons, it is also only safe to call this at few * spots since it must hold the RTNL and be able to sleep. * * The order in which the keys are iterated matches the order * in which they were originally installed and handed to the * set_key callback. */ void ieee80211_iter_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void (*iter)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, void *data), void *iter_data); /** * ieee80211_ap_probereq_get - retrieve a Probe Request template * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * Creates a Probe Request template which can, for example, be uploaded to * hardware. The template is filled with bssid, ssid and supported rate * information. This function must only be called from within the * .bss_info_changed callback function and only in managed mode. The function * is only useful when the interface is associated, otherwise it will return * NULL. */ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); /** * ieee80211_beacon_loss - inform hardware does not receive beacons * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER and * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the * hardware is not receiving beacons with this function. */ void ieee80211_beacon_loss(struct ieee80211_vif *vif); /** * ieee80211_connection_loss - inform hardware has lost connection to the AP * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER, and * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver * needs to inform if the connection to the AP has been lost. * * This function will cause immediate change to disassociated state, * without connection recovery attempts. */ void ieee80211_connection_loss(struct ieee80211_vif *vif); /** * ieee80211_resume_disconnect - disconnect from AP after resume * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * Instructs mac80211 to disconnect from the AP after resume. * Drivers can use this after WoWLAN if they know that the * connection cannot be kept up, for example because keys were * used while the device was asleep but the replay counters or * similar cannot be retrieved from the device during resume. * * Note that due to implementation issues, if the driver uses * the reconfiguration functionality during resume the interface * will still be added as associated first during resume and then * disconnect normally later. * * This function can only be called from the resume callback and * the driver must not be holding any of its own locks while it * calls this function, or at least not any locks it needs in the * key configuration paths (if it supports HW crypto). */ void ieee80211_resume_disconnect(struct ieee80211_vif *vif); /** * ieee80211_disable_dyn_ps - force mac80211 to temporarily disable dynamic psm * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * Some hardware require full power save to manage simultaneous BT traffic * on the WLAN frequency. Full PSM is required periodically, whenever there are * burst of BT traffic. The hardware gets information of BT traffic via * hardware co-existence lines, and consequentially requests mac80211 to * (temporarily) enter full psm. * This function will only temporarily disable dynamic PS, not enable PSM if * it was not already enabled. * The driver must make sure to re-enable dynamic PS using * ieee80211_enable_dyn_ps() if the driver has disabled it. * */ void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif); /** * ieee80211_enable_dyn_ps - restore dynamic psm after being disabled * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * This function restores dynamic PS after being temporarily disabled via * ieee80211_disable_dyn_ps(). Each ieee80211_disable_dyn_ps() call must * be coupled with an eventual call to this function. * */ void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif); /** * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring * rssi threshold triggered * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @rssi_event: the RSSI trigger event type * @gfp: context flags * * When the %IEEE80211_VIF_SUPPORTS_CQM_RSSI is set, and a connection quality * monitoring is configured with an rssi threshold, the driver will inform * whenever the rssi level reaches the threshold. */ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); /** * ieee80211_chswitch_done - Complete channel switch process * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @success: make the channel switch successful or not * * Complete the channel switch post-process: set the new operational channel * and wake up the suspended queues. */ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success); /** * ieee80211_request_smps - request SM PS transition * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @smps_mode: new SM PS mode * * This allows the driver to request an SM PS transition in managed * mode. This is useful when the driver has more information than * the stack about possible interference, for example by bluetooth. */ void ieee80211_request_smps(struct ieee80211_vif *vif, enum ieee80211_smps_mode smps_mode); /** * ieee80211_ready_on_channel - notification of remain-on-channel start * @hw: pointer as obtained from ieee80211_alloc_hw() */ void ieee80211_ready_on_channel(struct ieee80211_hw *hw); /** * ieee80211_remain_on_channel_expired - remain_on_channel duration expired * @hw: pointer as obtained from ieee80211_alloc_hw() */ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw); /** * ieee80211_stop_rx_ba_session - callback to stop existing BA sessions * * in order not to harm the system performance and user experience, the device * may request not to allow any rx ba session and tear down existing rx ba * sessions based on system constraints such as periodic BT activity that needs * to limit wlan activity (eg.sco or a2dp)." * in such cases, the intention is to limit the duration of the rx ppdu and * therefore prevent the peer device to use a-mpdu aggregation. * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @ba_rx_bitmap: Bit map of open rx ba per tid * @addr: & to bssid mac address */ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, const u8 *addr); /** * ieee80211_send_bar - send a BlockAckReq frame * * can be used to flush pending frames from the peer's aggregation reorder * buffer. * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @ra: the peer's destination address * @tid: the TID of the aggregation session * @ssn: the new starting sequence number for the receiver */ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn); /* Rate control API */ /** * struct ieee80211_tx_rate_control - rate control information for/from RC algo * * @hw: The hardware the algorithm is invoked for. * @sband: The band this frame is being transmitted on. * @bss_conf: the current BSS configuration * @skb: the skb that will be transmitted, the control information in it needs * to be filled in * @reported_rate: The rate control algorithm can fill this in to indicate * which rate should be reported to userspace as the current rate and * used for rate calculations in the mesh network. * @rts: whether RTS will be used for this frame because it is longer than the * RTS threshold * @short_preamble: whether mac80211 will request short-preamble transmission * if the selected rate supports it * @max_rate_idx: user-requested maximum (legacy) rate * (deprecated; this will be removed once drivers get updated to use * rate_idx_mask) * @rate_idx_mask: user-requested (legacy) rate mask * @rate_idx_mcs_mask: user-requested MCS rate mask * @bss: whether this frame is sent out in AP or IBSS mode */ struct ieee80211_tx_rate_control { struct ieee80211_hw *hw; struct ieee80211_supported_band *sband; struct ieee80211_bss_conf *bss_conf; struct sk_buff *skb; struct ieee80211_tx_rate reported_rate; bool rts, short_preamble; u8 max_rate_idx; u32 rate_idx_mask; u8 rate_idx_mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; bool bss; }; struct rate_control_ops { struct module *module; const char *name; void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir); void (*free)(void *priv); void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp); void (*rate_init)(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta); void (*rate_update)(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, u32 changed); void (*free_sta)(void *priv, struct ieee80211_sta *sta, void *priv_sta); void (*tx_status)(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb); void (*get_rate)(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc); void (*add_sta_debugfs)(void *priv, void *priv_sta, struct dentry *dir); void (*remove_sta_debugfs)(void *priv, void *priv_sta); }; static inline int rate_supported(struct ieee80211_sta *sta, enum ieee80211_band band, int index) { return (sta == NULL || sta->supp_rates[band] & BIT(index)); } /** * rate_control_send_low - helper for drivers for management/no-ack frames * * Rate control algorithms that agree to use the lowest rate to * send management frames and NO_ACK data with the respective hw * retries should use this in the beginning of their mac80211 get_rate * callback. If true is returned the rate control can simply return. * If false is returned we guarantee that sta and sta and priv_sta is * not null. * * Rate control algorithms wishing to do more intelligent selection of * rate for multicast/broadcast frames may choose to not use this. * * @sta: &struct ieee80211_sta pointer to the target destination. Note * that this may be null. * @priv_sta: private rate control structure. This may be null. * @txrc: rate control information we sholud populate for mac80211. */ bool rate_control_send_low(struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc); static inline s8 rate_lowest_index(struct ieee80211_supported_band *sband, struct ieee80211_sta *sta) { int i; for (i = 0; i < sband->n_bitrates; i++) if (rate_supported(sta, sband->band, i)) return i; /* warn when we cannot find a rate. */ WARN_ON_ONCE(1); /* and return 0 (the lowest index) */ return 0; } static inline bool rate_usable_index_exists(struct ieee80211_supported_band *sband, struct ieee80211_sta *sta) { unsigned int i; for (i = 0; i < sband->n_bitrates; i++) if (rate_supported(sta, sband->band, i)) return true; return false; } int ieee80211_rate_control_register(struct rate_control_ops *ops); void ieee80211_rate_control_unregister(struct rate_control_ops *ops); static inline bool conf_is_ht20(struct ieee80211_conf *conf) { return conf->channel_type == NL80211_CHAN_HT20; } static inline bool conf_is_ht40_minus(struct ieee80211_conf *conf) { return conf->channel_type == NL80211_CHAN_HT40MINUS; } static inline bool conf_is_ht40_plus(struct ieee80211_conf *conf) { return conf->channel_type == NL80211_CHAN_HT40PLUS; } static inline bool conf_is_ht40(struct ieee80211_conf *conf) { return conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf); } static inline bool conf_is_ht(struct ieee80211_conf *conf) { return conf->channel_type != NL80211_CHAN_NO_HT; } static inline enum nl80211_iftype ieee80211_iftype_p2p(enum nl80211_iftype type, bool p2p) { if (p2p) { switch (type) { case NL80211_IFTYPE_STATION: return NL80211_IFTYPE_P2P_CLIENT; case NL80211_IFTYPE_AP: return NL80211_IFTYPE_P2P_GO; default: break; } } return type; } static inline enum nl80211_iftype ieee80211_vif_type_p2p(struct ieee80211_vif *vif) { return ieee80211_iftype_p2p(vif->type, vif->p2p); } void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, int rssi_min_thold, int rssi_max_thold); void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); /** * ieee80211_ave_rssi - report the average rssi for the specified interface * * @vif: the specified virtual interface * * This function return the average rssi value for the requested interface. * It assumes that the given vif is valid. */ int ieee80211_ave_rssi(struct ieee80211_vif *vif); #endif /* MAC80211_H */ compat-drivers-2012-09-18/include/net/cfg80211.h0000644000175000017500000037340012026211315020104 0ustar mcgrofmcgrof#ifndef __NET_CFG80211_H #define __NET_CFG80211_H /* * 802.11 device and configuration interface * * Copyright 2006-2010 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include /** * DOC: Introduction * * cfg80211 is the configuration API for 802.11 devices in Linux. It bridges * userspace and drivers, and offers some utility functionality associated * with 802.11. cfg80211 must, directly or indirectly via mac80211, be used * by all modern wireless drivers in Linux, so that they offer a consistent * API through nl80211. For backward compatibility, cfg80211 also offers * wireless extensions to userspace, but hides them from drivers completely. * * Additionally, cfg80211 contains code to help enforce regulatory spectrum * use restrictions. */ /** * DOC: Device registration * * In order for a driver to use cfg80211, it must register the hardware device * with cfg80211. This happens through a number of hardware capability structs * described below. * * The fundamental structure for each device is the 'wiphy', of which each * instance describes a physical wireless device connected to the system. Each * such wiphy can have zero, one, or many virtual interfaces associated with * it, which need to be identified as such by pointing the network interface's * @ieee80211_ptr pointer to a &struct wireless_dev which further describes * the wireless part of the interface, normally this struct is embedded in the * network interface's private data area. Drivers can optionally allow creating * or destroying virtual interfaces on the fly, but without at least one or the * ability to create some the wireless device isn't useful. * * Each wiphy structure contains device capability information, and also has * a pointer to the various operations the driver offers. The definitions and * structures here describe these capabilities in detail. */ /* * wireless hardware capability structures */ /** * enum ieee80211_band - supported frequency bands * * The bands are assigned this way because the supported * bitrates differ in these bands. * * @IEEE80211_BAND_2GHZ: 2.4GHz ISM band * @IEEE80211_BAND_5GHZ: around 5GHz band (4.9-5.7) * @IEEE80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) * @IEEE80211_NUM_BANDS: number of defined bands */ enum ieee80211_band { IEEE80211_BAND_2GHZ = NL80211_BAND_2GHZ, IEEE80211_BAND_5GHZ = NL80211_BAND_5GHZ, IEEE80211_BAND_60GHZ = NL80211_BAND_60GHZ, /* keep last */ IEEE80211_NUM_BANDS }; /** * enum ieee80211_channel_flags - channel flags * * Channel flags set by the regulatory control code. * * @IEEE80211_CHAN_DISABLED: This channel is disabled. * @IEEE80211_CHAN_PASSIVE_SCAN: Only passive scanning is permitted * on this channel. * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel. * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel. * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel * is not permitted. * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel * is not permitted. * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, IEEE80211_CHAN_NO_IBSS = 1<<2, IEEE80211_CHAN_RADAR = 1<<3, IEEE80211_CHAN_NO_HT40PLUS = 1<<4, IEEE80211_CHAN_NO_HT40MINUS = 1<<5, IEEE80211_CHAN_NO_OFDM = 1<<6, }; #define IEEE80211_CHAN_NO_HT40 \ (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) /** * struct ieee80211_channel - channel definition * * This structure describes a single channel for use * with cfg80211. * * @center_freq: center frequency in MHz * @hw_value: hardware-specific value for the channel * @flags: channel flags from &enum ieee80211_channel_flags. * @orig_flags: channel flags at registration time, used by regulatory * code to support devices with additional restrictions * @band: band this channel belongs to. * @max_antenna_gain: maximum antenna gain in dBi * @max_power: maximum transmission power (in dBm) * @max_reg_power: maximum regulatory transmission power (in dBm) * @beacon_found: helper to regulatory code to indicate when a beacon * has been found on this channel. Use regulatory_hint_found_beacon() * to enable this, this is useful only on 5 GHz band. * @orig_mag: internal use * @orig_mpwr: internal use */ struct ieee80211_channel { enum ieee80211_band band; u16 center_freq; u16 hw_value; u32 flags; int max_antenna_gain; int max_power; int max_reg_power; bool beacon_found; u32 orig_flags; int orig_mag, orig_mpwr; }; /** * enum ieee80211_rate_flags - rate flags * * Hardware/specification flags for rates. These are structured * in a way that allows using the same bitrate structure for * different bands/PHY modes. * * @IEEE80211_RATE_SHORT_PREAMBLE: Hardware can send with short * preamble on this bitrate; only relevant in 2.4GHz band and * with CCK rates. * @IEEE80211_RATE_MANDATORY_A: This bitrate is a mandatory rate * when used with 802.11a (on the 5 GHz band); filled by the * core code when registering the wiphy. * @IEEE80211_RATE_MANDATORY_B: This bitrate is a mandatory rate * when used with 802.11b (on the 2.4 GHz band); filled by the * core code when registering the wiphy. * @IEEE80211_RATE_MANDATORY_G: This bitrate is a mandatory rate * when used with 802.11g (on the 2.4 GHz band); filled by the * core code when registering the wiphy. * @IEEE80211_RATE_ERP_G: This is an ERP rate in 802.11g mode. */ enum ieee80211_rate_flags { IEEE80211_RATE_SHORT_PREAMBLE = 1<<0, IEEE80211_RATE_MANDATORY_A = 1<<1, IEEE80211_RATE_MANDATORY_B = 1<<2, IEEE80211_RATE_MANDATORY_G = 1<<3, IEEE80211_RATE_ERP_G = 1<<4, }; /** * struct ieee80211_rate - bitrate definition * * This structure describes a bitrate that an 802.11 PHY can * operate with. The two values @hw_value and @hw_value_short * are only for driver use when pointers to this structure are * passed around. * * @flags: rate-specific flags * @bitrate: bitrate in units of 100 Kbps * @hw_value: driver/hardware value for this rate * @hw_value_short: driver/hardware value for this rate when * short preamble is used */ struct ieee80211_rate { u32 flags; u16 bitrate; u16 hw_value, hw_value_short; }; /** * struct ieee80211_sta_ht_cap - STA's HT capabilities * * This structure describes most essential parameters needed * to describe 802.11n HT capabilities for an STA. * * @ht_supported: is HT supported by the STA * @cap: HT capabilities map as described in 802.11n spec * @ampdu_factor: Maximum A-MPDU length factor * @ampdu_density: Minimum A-MPDU spacing * @mcs: Supported MCS rates */ struct ieee80211_sta_ht_cap { u16 cap; /* use IEEE80211_HT_CAP_ */ bool ht_supported; u8 ampdu_factor; u8 ampdu_density; struct ieee80211_mcs_info mcs; }; /** * struct ieee80211_sta_vht_cap - STA's VHT capabilities * * This structure describes most essential parameters needed * to describe 802.11ac VHT capabilities for an STA. * * @vht_supported: is VHT supported by the STA * @cap: VHT capabilities map as described in 802.11ac spec * @vht_mcs: Supported VHT MCS rates */ struct ieee80211_sta_vht_cap { bool vht_supported; u32 cap; /* use IEEE80211_VHT_CAP_ */ struct ieee80211_vht_mcs_info vht_mcs; }; /** * struct ieee80211_supported_band - frequency band definition * * This structure describes a frequency band a wiphy * is able to operate in. * * @channels: Array of channels the hardware can operate in * in this band. * @band: the band this structure represents * @n_channels: Number of channels in @channels * @bitrates: Array of bitrates the hardware can operate with * in this band. Must be sorted to give a valid "supported * rates" IE, i.e. CCK rates first, then OFDM. * @n_bitrates: Number of bitrates in @bitrates * @ht_cap: HT capabilities in this band * @vht_cap: VHT capabilities in this band */ struct ieee80211_supported_band { struct ieee80211_channel *channels; struct ieee80211_rate *bitrates; enum ieee80211_band band; int n_channels; int n_bitrates; struct ieee80211_sta_ht_cap ht_cap; struct ieee80211_sta_vht_cap vht_cap; }; /* * Wireless hardware/device configuration structures and methods */ /** * DOC: Actions and configuration * * Each wireless device and each virtual interface offer a set of configuration * operations and other actions that are invoked by userspace. Each of these * actions is described in the operations structure, and the parameters these * operations use are described separately. * * Additionally, some operations are asynchronous and expect to get status * information via some functions that drivers need to call. * * Scanning and BSS list handling with its associated functionality is described * in a separate chapter. */ /** * struct vif_params - describes virtual interface parameters * @use_4addr: use 4-address frames */ struct vif_params { int use_4addr; }; /** * struct key_params - key information * * Information about a key * * @key: key material * @key_len: length of key material * @cipher: cipher suite selector * @seq: sequence counter (IV/PN) for TKIP and CCMP keys, only used * with the get_key() callback, must be in little endian, * length given by @seq_len. * @seq_len: length of @seq. */ struct key_params { u8 *key; u8 *seq; int key_len; int seq_len; u32 cipher; }; /** * enum survey_info_flags - survey information flags * * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in * @SURVEY_INFO_IN_USE: channel is currently being used * @SURVEY_INFO_CHANNEL_TIME: channel active time (in ms) was filled in * @SURVEY_INFO_CHANNEL_TIME_BUSY: channel busy time was filled in * @SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: extension channel busy time was filled in * @SURVEY_INFO_CHANNEL_TIME_RX: channel receive time was filled in * @SURVEY_INFO_CHANNEL_TIME_TX: channel transmit time was filled in * * Used by the driver to indicate which info in &struct survey_info * it has filled in during the get_survey(). */ enum survey_info_flags { SURVEY_INFO_NOISE_DBM = 1<<0, SURVEY_INFO_IN_USE = 1<<1, SURVEY_INFO_CHANNEL_TIME = 1<<2, SURVEY_INFO_CHANNEL_TIME_BUSY = 1<<3, SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 1<<4, SURVEY_INFO_CHANNEL_TIME_RX = 1<<5, SURVEY_INFO_CHANNEL_TIME_TX = 1<<6, }; /** * struct survey_info - channel survey response * * @channel: the channel this survey record reports, mandatory * @filled: bitflag of flags from &enum survey_info_flags * @noise: channel noise in dBm. This and all following fields are * optional * @channel_time: amount of time in ms the radio spent on the channel * @channel_time_busy: amount of time the primary channel was sensed busy * @channel_time_ext_busy: amount of time the extension channel was sensed busy * @channel_time_rx: amount of time the radio spent receiving data * @channel_time_tx: amount of time the radio spent transmitting data * * Used by dump_survey() to report back per-channel survey information. * * This structure can later be expanded with things like * channel duty cycle etc. */ struct survey_info { struct ieee80211_channel *channel; u64 channel_time; u64 channel_time_busy; u64 channel_time_ext_busy; u64 channel_time_rx; u64 channel_time_tx; u32 filled; s8 noise; }; /** * struct cfg80211_crypto_settings - Crypto settings * @wpa_versions: indicates which, if any, WPA versions are enabled * (from enum nl80211_wpa_versions) * @cipher_group: group key cipher suite (or 0 if unset) * @n_ciphers_pairwise: number of AP supported unicast ciphers * @ciphers_pairwise: unicast key cipher suites * @n_akm_suites: number of AKM suites * @akm_suites: AKM suites * @control_port: Whether user space controls IEEE 802.1X port, i.e., * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is * required to assume that the port is unauthorized until authorized by * user space. Otherwise, port is marked authorized by default. * @control_port_ethertype: the control port protocol that should be * allowed through even on unauthorized ports * @control_port_no_encrypt: TRUE to prevent encryption of control port * protocol frames. */ struct cfg80211_crypto_settings { u32 wpa_versions; u32 cipher_group; int n_ciphers_pairwise; u32 ciphers_pairwise[NL80211_MAX_NR_CIPHER_SUITES]; int n_akm_suites; u32 akm_suites[NL80211_MAX_NR_AKM_SUITES]; bool control_port; __be16 control_port_ethertype; bool control_port_no_encrypt; }; /** * struct cfg80211_beacon_data - beacon data * @head: head portion of beacon (before TIM IE) * or %NULL if not changed * @tail: tail portion of beacon (after TIM IE) * or %NULL if not changed * @head_len: length of @head * @tail_len: length of @tail * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL * @beacon_ies_len: length of beacon_ies in octets * @proberesp_ies: extra information element(s) to add into Probe Response * frames or %NULL * @proberesp_ies_len: length of proberesp_ies in octets * @assocresp_ies: extra information element(s) to add into (Re)Association * Response frames or %NULL * @assocresp_ies_len: length of assocresp_ies in octets * @probe_resp_len: length of probe response template (@probe_resp) * @probe_resp: probe response template (AP mode only) */ struct cfg80211_beacon_data { const u8 *head, *tail; const u8 *beacon_ies; const u8 *proberesp_ies; const u8 *assocresp_ies; const u8 *probe_resp; size_t head_len, tail_len; size_t beacon_ies_len; size_t proberesp_ies_len; size_t assocresp_ies_len; size_t probe_resp_len; }; /** * struct cfg80211_ap_settings - AP configuration * * Used to configure an AP interface. * * @channel: the channel to start the AP on * @channel_type: the channel type to use * @beacon: beacon data * @beacon_interval: beacon interval * @dtim_period: DTIM period * @ssid: SSID to be used in the BSS (note: may be %NULL if not provided from * user space) * @ssid_len: length of @ssid * @hidden_ssid: whether to hide the SSID in Beacon/Probe Response frames * @crypto: crypto settings * @privacy: the BSS uses privacy * @auth_type: Authentication type (algorithm) * @inactivity_timeout: time in seconds to determine station's inactivity. */ struct cfg80211_ap_settings { struct ieee80211_channel *channel; enum nl80211_channel_type channel_type; struct cfg80211_beacon_data beacon; int beacon_interval, dtim_period; const u8 *ssid; size_t ssid_len; enum nl80211_hidden_ssid hidden_ssid; struct cfg80211_crypto_settings crypto; bool privacy; enum nl80211_auth_type auth_type; int inactivity_timeout; }; /** * enum plink_action - actions to perform in mesh peers * * @PLINK_ACTION_INVALID: action 0 is reserved * @PLINK_ACTION_OPEN: start mesh peer link establishment * @PLINK_ACTION_BLOCK: block traffic from this mesh peer */ enum plink_actions { PLINK_ACTION_INVALID, PLINK_ACTION_OPEN, PLINK_ACTION_BLOCK, }; /** * enum station_parameters_apply_mask - station parameter values to apply * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) * * Not all station parameters have in-band "no change" signalling, * for those that don't these flags will are used. */ enum station_parameters_apply_mask { STATION_PARAM_APPLY_UAPSD = BIT(0), }; /** * struct station_parameters - station parameters * * Used to change and create a new station. * * @vlan: vlan interface station should belong to * @supported_rates: supported rates in IEEE 802.11 format * (or NULL for no change) * @supported_rates_len: number of supported rates * @sta_flags_mask: station flags that changed * (bitmask of BIT(NL80211_STA_FLAG_...)) * @sta_flags_set: station flags values * (bitmask of BIT(NL80211_STA_FLAG_...)) * @listen_interval: listen interval or -1 for no change * @aid: AID or zero for no change * @plink_action: plink action to take * @plink_state: set the peer link state for a station * @ht_capa: HT capabilities of station * @uapsd_queues: bitmap of queues configured for uapsd. same format * as the AC bitmap in the QoS info field * @max_sp: max Service Period. same format as the MAX_SP in the * QoS info field (but already shifted down) * @sta_modify_mask: bitmap indicating which parameters changed * (for those that don't have a natural "no change" value), * see &enum station_parameters_apply_mask */ struct station_parameters { u8 *supported_rates; struct net_device *vlan; u32 sta_flags_mask, sta_flags_set; u32 sta_modify_mask; int listen_interval; u16 aid; u8 supported_rates_len; u8 plink_action; u8 plink_state; struct ieee80211_ht_cap *ht_capa; u8 uapsd_queues; u8 max_sp; }; /** * enum station_info_flags - station information flags * * Used by the driver to indicate which info in &struct station_info * it has filled in during get_station() or dump_station(). * * @STATION_INFO_INACTIVE_TIME: @inactive_time filled * @STATION_INFO_RX_BYTES: @rx_bytes filled * @STATION_INFO_TX_BYTES: @tx_bytes filled * @STATION_INFO_LLID: @llid filled * @STATION_INFO_PLID: @plid filled * @STATION_INFO_PLINK_STATE: @plink_state filled * @STATION_INFO_SIGNAL: @signal filled * @STATION_INFO_TX_BITRATE: @txrate fields are filled * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) * @STATION_INFO_RX_PACKETS: @rx_packets filled * @STATION_INFO_TX_PACKETS: @tx_packets filled * @STATION_INFO_TX_RETRIES: @tx_retries filled * @STATION_INFO_TX_FAILED: @tx_failed filled * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled * @STATION_INFO_SIGNAL_AVG: @signal_avg filled * @STATION_INFO_RX_BITRATE: @rxrate fields are filled * @STATION_INFO_BSS_PARAM: @bss_param filled * @STATION_INFO_CONNECTED_TIME: @connected_time filled * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled * @STATION_INFO_STA_FLAGS: @sta_flags filled * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled * @STATION_INFO_T_OFFSET: @t_offset filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, STATION_INFO_RX_BYTES = 1<<1, STATION_INFO_TX_BYTES = 1<<2, STATION_INFO_LLID = 1<<3, STATION_INFO_PLID = 1<<4, STATION_INFO_PLINK_STATE = 1<<5, STATION_INFO_SIGNAL = 1<<6, STATION_INFO_TX_BITRATE = 1<<7, STATION_INFO_RX_PACKETS = 1<<8, STATION_INFO_TX_PACKETS = 1<<9, STATION_INFO_TX_RETRIES = 1<<10, STATION_INFO_TX_FAILED = 1<<11, STATION_INFO_RX_DROP_MISC = 1<<12, STATION_INFO_SIGNAL_AVG = 1<<13, STATION_INFO_RX_BITRATE = 1<<14, STATION_INFO_BSS_PARAM = 1<<15, STATION_INFO_CONNECTED_TIME = 1<<16, STATION_INFO_ASSOC_REQ_IES = 1<<17, STATION_INFO_STA_FLAGS = 1<<18, STATION_INFO_BEACON_LOSS_COUNT = 1<<19, STATION_INFO_T_OFFSET = 1<<20, }; /** * enum station_info_rate_flags - bitrate info flags * * Used by the driver to indicate the specific rate transmission * type for 802.11n transmissions. * * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval * @RATE_INFO_FLAGS_60G: 60gHz MCS */ enum rate_info_flags { RATE_INFO_FLAGS_MCS = 1<<0, RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1, RATE_INFO_FLAGS_SHORT_GI = 1<<2, RATE_INFO_FLAGS_60G = 1<<3, }; /** * struct rate_info - bitrate information * * Information about a receiving or transmitting bitrate * * @flags: bitflag of flags from &enum rate_info_flags * @mcs: mcs index if struct describes a 802.11n bitrate * @legacy: bitrate in 100kbit/s for 802.11abg */ struct rate_info { u8 flags; u8 mcs; u16 legacy; }; /** * enum station_info_rate_flags - bitrate info flags * * Used by the driver to indicate the specific rate transmission * type for 802.11n transmissions. * * @BSS_PARAM_FLAGS_CTS_PROT: whether CTS protection is enabled * @BSS_PARAM_FLAGS_SHORT_PREAMBLE: whether short preamble is enabled * @BSS_PARAM_FLAGS_SHORT_SLOT_TIME: whether short slot time is enabled */ enum bss_param_flags { BSS_PARAM_FLAGS_CTS_PROT = 1<<0, BSS_PARAM_FLAGS_SHORT_PREAMBLE = 1<<1, BSS_PARAM_FLAGS_SHORT_SLOT_TIME = 1<<2, }; /** * struct sta_bss_parameters - BSS parameters for the attached station * * Information about the currently associated BSS * * @flags: bitflag of flags from &enum bss_param_flags * @dtim_period: DTIM period for the BSS * @beacon_interval: beacon interval */ struct sta_bss_parameters { u8 flags; u8 dtim_period; u16 beacon_interval; }; /** * struct station_info - station information * * Station information filled by driver for get_station() and dump_station. * * @filled: bitflag of flags from &enum station_info_flags * @connected_time: time(in secs) since a station is last connected * @inactive_time: time since last station activity (tx/rx) in milliseconds * @rx_bytes: bytes received from this station * @tx_bytes: bytes transmitted to this station * @llid: mesh local link id * @plid: mesh peer link id * @plink_state: mesh peer link state * @signal: The signal strength, type depends on the wiphy's signal_type. * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. * @signal_avg: Average signal strength, type depends on the wiphy's signal_type. * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. * @txrate: current unicast bitrate from this station * @rxrate: current unicast bitrate to this station * @rx_packets: packets received from this station * @tx_packets: packets transmitted to this station * @tx_retries: cumulative retry counts * @tx_failed: number of failed transmissions (retries exceeded, no ACK) * @rx_dropped_misc: Dropped for un-specified reason. * @bss_param: current BSS parameters * @generation: generation number for nl80211 dumps. * This number should increase every time the list of stations * changes, i.e. when a station is added or removed, so that * userspace can tell whether it got a consistent snapshot. * @assoc_req_ies: IEs from (Re)Association Request. * This is used only when in AP mode with drivers that do not use * user space MLME/SME implementation. The information is provided for * the cfg80211_new_sta() calls to notify user space of the IEs. * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets. * @sta_flags: station flags mask & values * @beacon_loss_count: Number of times beacon loss event has triggered. * @t_offset: Time offset of the station relative to this host. */ struct station_info { u32 filled; u32 connected_time; u32 inactive_time; u32 rx_bytes; u32 tx_bytes; u16 llid; u16 plid; u8 plink_state; s8 signal; s8 signal_avg; struct rate_info txrate; struct rate_info rxrate; u32 rx_packets; u32 tx_packets; u32 tx_retries; u32 tx_failed; u32 rx_dropped_misc; struct sta_bss_parameters bss_param; struct nl80211_sta_flag_update sta_flags; int generation; const u8 *assoc_req_ies; size_t assoc_req_ies_len; u32 beacon_loss_count; s64 t_offset; /* * Note: Add a new enum station_info_flags value for each new field and * use it to check which fields are initialized. */ }; /** * enum monitor_flags - monitor flags * * Monitor interface configuration flags. Note that these must be the bits * according to the nl80211 flags. * * @MONITOR_FLAG_FCSFAIL: pass frames with bad FCS * @MONITOR_FLAG_PLCPFAIL: pass frames with bad PLCP * @MONITOR_FLAG_CONTROL: pass control frames * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering * @MONITOR_FLAG_COOK_FRAMES: report frames after processing */ enum monitor_flags { MONITOR_FLAG_FCSFAIL = 1<bss_priv_size bytes */ struct cfg80211_bss { struct ieee80211_channel *channel; u8 bssid[ETH_ALEN]; u64 tsf; u16 beacon_interval; u16 capability; u8 *information_elements; size_t len_information_elements; u8 *beacon_ies; size_t len_beacon_ies; u8 *proberesp_ies; size_t len_proberesp_ies; s32 signal; void (*free_priv)(struct cfg80211_bss *bss); u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); }; /** * ieee80211_bss_get_ie - find IE with given ID * @bss: the bss to search * @ie: the IE ID * Returns %NULL if not found. */ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); /** * struct cfg80211_auth_request - Authentication request data * * This structure provides information needed to complete IEEE 802.11 * authentication. * * @bss: The BSS to authenticate with. * @auth_type: Authentication type (algorithm) * @ie: Extra IEs to add to Authentication frame or %NULL * @ie_len: Length of ie buffer in octets * @key_len: length of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication * @key: WEP key for shared key authentication */ struct cfg80211_auth_request { struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; enum nl80211_auth_type auth_type; const u8 *key; u8 key_len, key_idx; }; /** * enum cfg80211_assoc_req_flags - Over-ride default behaviour in association. * * @ASSOC_REQ_DISABLE_HT: Disable HT (802.11n) */ enum cfg80211_assoc_req_flags { ASSOC_REQ_DISABLE_HT = BIT(0), }; /** * struct cfg80211_assoc_request - (Re)Association request data * * This structure provides information needed to complete IEEE 802.11 * (re)association. * @bss: The BSS to associate with. If the call is successful the driver * is given a reference that it must release, normally via a call to * cfg80211_send_rx_assoc(), or, if association timed out, with a * call to cfg80211_put_bss() (in addition to calling * cfg80211_send_assoc_timeout()) * @ie: Extra IEs to add to (Re)Association Request frame or %NULL * @ie_len: Length of ie buffer in octets * @use_mfp: Use management frame protection (IEEE 802.11w) in this association * @crypto: crypto settings * @prev_bssid: previous BSSID, if not %NULL use reassociate frame * @flags: See &enum cfg80211_assoc_req_flags * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. */ struct cfg80211_assoc_request { struct cfg80211_bss *bss; const u8 *ie, *prev_bssid; size_t ie_len; struct cfg80211_crypto_settings crypto; bool use_mfp; u32 flags; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; }; /** * struct cfg80211_deauth_request - Deauthentication request data * * This structure provides information needed to complete IEEE 802.11 * deauthentication. * * @bssid: the BSSID of the BSS to deauthenticate from * @ie: Extra IEs to add to Deauthentication frame or %NULL * @ie_len: Length of ie buffer in octets * @reason_code: The reason code for the deauthentication */ struct cfg80211_deauth_request { const u8 *bssid; const u8 *ie; size_t ie_len; u16 reason_code; }; /** * struct cfg80211_disassoc_request - Disassociation request data * * This structure provides information needed to complete IEEE 802.11 * disassocation. * * @bss: the BSS to disassociate from * @ie: Extra IEs to add to Disassociation frame or %NULL * @ie_len: Length of ie buffer in octets * @reason_code: The reason code for the disassociation * @local_state_change: This is a request for a local state only, i.e., no * Disassociation frame is to be transmitted. */ struct cfg80211_disassoc_request { struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; u16 reason_code; bool local_state_change; }; /** * struct cfg80211_ibss_params - IBSS parameters * * This structure defines the IBSS parameters for the join_ibss() * method. * * @ssid: The SSID, will always be non-null. * @ssid_len: The length of the SSID, will always be non-zero. * @bssid: Fixed BSSID requested, maybe be %NULL, if set do not * search for IBSSs with a different BSSID. * @channel: The channel to use if no IBSS can be found to join. * @channel_type: channel type (HT mode) * @channel_fixed: The channel should be fixed -- do not search for * IBSSs to join on other channels. * @ie: information element(s) to include in the beacon * @ie_len: length of that * @beacon_interval: beacon interval to use * @privacy: this is a protected network, keys will be configured * after joining * @control_port: whether user space controls IEEE 802.1X port, i.e., * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is * required to assume that the port is unauthorized until authorized by * user space. Otherwise, port is marked authorized by default. * @basic_rates: bitmap of basic rates to use when creating the IBSS * @mcast_rate: per-band multicast rate index + 1 (0: disabled) */ struct cfg80211_ibss_params { u8 *ssid; u8 *bssid; struct ieee80211_channel *channel; enum nl80211_channel_type channel_type; u8 *ie; u8 ssid_len, ie_len; u16 beacon_interval; u32 basic_rates; bool channel_fixed; bool privacy; bool control_port; int mcast_rate[IEEE80211_NUM_BANDS]; }; /** * struct cfg80211_connect_params - Connection parameters * * This structure provides information needed to complete IEEE 802.11 * authentication and association. * * @channel: The channel to use or %NULL if not specified (auto-select based * on scan results) * @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan * results) * @ssid: SSID * @ssid_len: Length of ssid in octets * @auth_type: Authentication type (algorithm) * @ie: IEs for association request * @ie_len: Length of assoc_ie in octets * @privacy: indicates whether privacy-enabled APs should be used * @crypto: crypto settings * @key_len: length of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication * @key: WEP key for shared key authentication * @flags: See &enum cfg80211_assoc_req_flags * @bg_scan_period: Background scan period in seconds * or -1 to indicate that default value is to be used. * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. */ struct cfg80211_connect_params { struct ieee80211_channel *channel; u8 *bssid; u8 *ssid; size_t ssid_len; enum nl80211_auth_type auth_type; u8 *ie; size_t ie_len; bool privacy; struct cfg80211_crypto_settings crypto; const u8 *key; u8 key_len, key_idx; u32 flags; int bg_scan_period; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; }; /** * enum wiphy_params_flags - set_wiphy_params bitfield values * @WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed * @WIPHY_PARAM_RETRY_LONG: wiphy->retry_long has changed * @WIPHY_PARAM_FRAG_THRESHOLD: wiphy->frag_threshold has changed * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed */ enum wiphy_params_flags { WIPHY_PARAM_RETRY_SHORT = 1 << 0, WIPHY_PARAM_RETRY_LONG = 1 << 1, WIPHY_PARAM_FRAG_THRESHOLD = 1 << 2, WIPHY_PARAM_RTS_THRESHOLD = 1 << 3, WIPHY_PARAM_COVERAGE_CLASS = 1 << 4, }; /* * cfg80211_bitrate_mask - masks for bitrate control */ struct cfg80211_bitrate_mask { struct { u32 legacy; u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; } control[IEEE80211_NUM_BANDS]; }; /** * struct cfg80211_pmksa - PMK Security Association * * This structure is passed to the set/del_pmksa() method for PMKSA * caching. * * @bssid: The AP's BSSID. * @pmkid: The PMK material itself. */ struct cfg80211_pmksa { u8 *bssid; u8 *pmkid; }; /** * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern * @mask: bitmask where to match pattern and where to ignore bytes, * one bit per byte, in same format as nl80211 * @pattern: bytes to match where bitmask is 1 * @pattern_len: length of pattern (in bytes) * * Internal note: @mask and @pattern are allocated in one chunk of * memory, free @mask only! */ struct cfg80211_wowlan_trig_pkt_pattern { u8 *mask, *pattern; int pattern_len; }; /** * struct cfg80211_wowlan - Wake on Wireless-LAN support info * * This structure defines the enabled WoWLAN triggers for the device. * @any: wake up on any activity -- special trigger if device continues * operating as normal during suspend * @disconnect: wake up if getting disconnected * @magic_pkt: wake up on receiving magic packet * @patterns: wake up on receiving packet matching a pattern * @n_patterns: number of patterns * @gtk_rekey_failure: wake up on GTK rekey failure * @eap_identity_req: wake up on EAP identity request packet * @four_way_handshake: wake up on 4-way handshake * @rfkill_release: wake up when rfkill is released */ struct cfg80211_wowlan { bool any, disconnect, magic_pkt, gtk_rekey_failure, eap_identity_req, four_way_handshake, rfkill_release; struct cfg80211_wowlan_trig_pkt_pattern *patterns; int n_patterns; }; /** * struct cfg80211_gtk_rekey_data - rekey data * @kek: key encryption key * @kck: key confirmation key * @replay_ctr: replay counter */ struct cfg80211_gtk_rekey_data { u8 kek[NL80211_KEK_LEN]; u8 kck[NL80211_KCK_LEN]; u8 replay_ctr[NL80211_REPLAY_CTR_LEN]; }; /** * struct cfg80211_ops - backend description for wireless configuration * * This struct is registered by fullmac card drivers and/or wireless stacks * in order to handle configuration requests on their interfaces. * * All callbacks except where otherwise noted should return 0 * on success or a negative error code. * * All operations are currently invoked under rtnl for consistency with the * wireless extensions but this is subject to reevaluation as soon as this * code is used more widely and we have a first user without wext. * * @suspend: wiphy device needs to be suspended. The variable @wow will * be %NULL or contain the enabled Wake-on-Wireless triggers that are * configured for the device. * @resume: wiphy device needs to be resumed * @set_wakeup: Called when WoWLAN is enabled/disabled, use this callback * to call device_set_wakeup_enable() to enable/disable wakeup from * the device. * * @add_virtual_intf: create a new virtual interface with the given name, * must set the struct wireless_dev's iftype. Beware: You must create * the new netdev in the wiphy's network namespace! Returns the struct * wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must * also set the address member in the wdev. * * @del_virtual_intf: remove the virtual interface * * @change_virtual_intf: change type/configuration of virtual interface, * keep the struct wireless_dev's iftype updated. * * @add_key: add a key with the given parameters. @mac_addr will be %NULL * when adding a group key. * * @get_key: get information about the key with the given parameters. * @mac_addr will be %NULL when requesting information for a group * key. All pointers given to the @callback function need not be valid * after it returns. This function should return an error if it is * not possible to retrieve the key, -ENOENT if it doesn't exist. * * @del_key: remove a key given the @mac_addr (%NULL for a group key) * and @key_index, return -ENOENT if the key doesn't exist. * * @set_default_key: set the default key on an interface * * @set_default_mgmt_key: set the default management frame key on an interface * * @set_rekey_data: give the data necessary for GTK rekeying to the driver * * @start_ap: Start acting in AP mode defined by the parameters. * @change_beacon: Change the beacon parameters for an access point mode * interface. This should reject the call when AP mode wasn't started. * @stop_ap: Stop being an AP, including stopping beaconing. * * @add_station: Add a new station. * @del_station: Remove a station; @mac may be NULL to remove all stations. * @change_station: Modify a given station. Note that flags changes are not much * validated in cfg80211, in particular the auth/assoc/authorized flags * might come to the driver in invalid combinations -- make sure to check * them, also against the existing state! Also, supported_rates changes are * not checked in station mode -- drivers need to reject (or ignore) them * for anything but TDLS peers. * @get_station: get station information for the station identified by @mac * @dump_station: dump station callback -- resume dump at index @idx * * @add_mpath: add a fixed mesh path * @del_mpath: delete a given mesh path * @change_mpath: change a given mesh path * @get_mpath: get a mesh path for the given parameters * @dump_mpath: dump mesh path callback -- resume dump at index @idx * @join_mesh: join the mesh network with the specified parameters * @leave_mesh: leave the current mesh network * * @get_mesh_config: Get the current mesh configuration * * @update_mesh_config: Update mesh parameters on a running mesh. * The mask is a bitfield which tells us which parameters to * set, and which to leave alone. * * @change_bss: Modify parameters for a given BSS. * * @set_txq_params: Set TX queue parameters * * @libertas_set_mesh_channel: Only for backward compatibility for libertas, * as it doesn't implement join_mesh and needs to set the channel to * join the mesh instead. * * @set_monitor_channel: Set the monitor mode channel for the device. If other * interfaces are active this callback should reject the configuration. * If no interfaces are active or the device is down, the channel should * be stored for when a monitor interface becomes active. * * @scan: Request to do a scan. If returning zero, the scan request is given * the driver, and will be valid until passed to cfg80211_scan_done(). * For scan results, call cfg80211_inform_bss(); you can call this outside * the scan/scan_done bracket too. * * @auth: Request to authenticate with the specified peer * @assoc: Request to (re)associate with the specified peer * @deauth: Request to deauthenticate from the specified peer * @disassoc: Request to disassociate from the specified peer * * @connect: Connect to the ESS with the specified parameters. When connected, * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS. * If the connection fails for some reason, call cfg80211_connect_result() * with the status from the AP. * @disconnect: Disconnect from the BSS/ESS. * * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call * cfg80211_ibss_joined(), also call that function when changing BSSID due * to a merge. * @leave_ibss: Leave the IBSS. * * @set_wiphy_params: Notify that wiphy parameters have changed; * @changed bitfield (see &enum wiphy_params_flags) describes which values * have changed. The actual parameter values are available in * struct wiphy. If returning an error, no value should be changed. * * @set_tx_power: set the transmit power according to the parameters, * the power passed is in mBm, to get dBm use MBM_TO_DBM(). * @get_tx_power: store the current TX power into the dbm variable; * return 0 if successful * * @set_wds_peer: set the WDS peer for a WDS interface * * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting * functions to adjust rfkill hw state * * @dump_survey: get site survey information. * * @remain_on_channel: Request the driver to remain awake on the specified * channel for the specified duration to complete an off-channel * operation (e.g., public action frame exchange). When the driver is * ready on the requested channel, it must indicate this with an event * notification by calling cfg80211_ready_on_channel(). * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation. * This allows the operation to be terminated prior to timeout based on * the duration value. * @mgmt_tx: Transmit a management frame. * @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management * frame on another channel * * @testmode_cmd: run a test mode command * @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be * used by the function, but 0 and 1 must not be touched. Additionally, * return error codes other than -ENOBUFS and -ENOENT will terminate the * dump and return to userspace with an error, so be careful. If any data * was passed in from userspace then the data/len arguments will be present * and point to the data contained in %NL80211_ATTR_TESTDATA. * * @set_bitrate_mask: set the bitrate mask configuration * * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac * devices running firmwares capable of generating the (re) association * RSN IE. It allows for faster roaming between WPA2 BSSIDs. * @del_pmksa: Delete a cached PMKID. * @flush_pmksa: Flush all cached PMKIDs. * @set_power_mgmt: Configure WLAN power management. A timeout value of -1 * allows the driver to adjust the dynamic ps timeout value. * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. * @set_cqm_txe_config: Configure connection quality monitor TX error * thresholds. * @sched_scan_start: Tell the driver to start a scheduled scan. * @sched_scan_stop: Tell the driver to stop an ongoing scheduled * scan. The driver_initiated flag specifies whether the driver * itself has informed that the scan has stopped. * * @mgmt_frame_register: Notify driver that a management frame type was * registered. Note that this callback may not sleep, and cannot run * concurrently with itself. * * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device. * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may * reject TX/RX mask combinations they cannot support by returning -EINVAL * (also see nl80211.h @NL80211_ATTR_WIPHY_ANTENNA_TX). * * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant). * * @set_ringparam: Set tx and rx ring sizes. * * @get_ringparam: Get tx and rx ring current and maximum sizes. * * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * * @probe_client: probe an associated client, must return a cookie that it * later passes to cfg80211_probe_status(). * * @set_noack_map: Set the NoAck Map for the TIDs. * * @get_et_sset_count: Ethtool API to get string-set count. * See @ethtool_ops.get_sset_count * * @get_et_stats: Ethtool API to get a set of u64 stats. * See @ethtool_ops.get_ethtool_stats * * @get_et_strings: Ethtool API to get a set of strings to describe stats * and perhaps other supported types of ethtool data-sets. * See @ethtool_ops.get_strings * * @get_channel: Get the current operating channel for the virtual interface. * For monitor interfaces, it should return %NULL unless there's a single * current monitoring channel. * * @start_p2p_device: Start the given P2P device. * @stop_p2p_device: Stop the given P2P device. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); int (*resume)(struct wiphy *wiphy); void (*set_wakeup)(struct wiphy *wiphy, bool enabled); struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params); int (*del_virtual_intf)(struct wiphy *wiphy, struct wireless_dev *wdev); int (*change_virtual_intf)(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params); int (*add_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr, struct key_params *params); int (*get_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params*)); int (*del_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr); int (*set_default_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast); int (*set_default_mgmt_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index); int (*start_ap)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *settings); int (*change_beacon)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_beacon_data *info); int (*stop_ap)(struct wiphy *wiphy, struct net_device *dev); int (*add_station)(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params); int (*del_station)(struct wiphy *wiphy, struct net_device *dev, u8 *mac); int (*change_station)(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params); int (*get_station)(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo); int (*dump_station)(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo); int (*add_mpath)(struct wiphy *wiphy, struct net_device *dev, u8 *dst, u8 *next_hop); int (*del_mpath)(struct wiphy *wiphy, struct net_device *dev, u8 *dst); int (*change_mpath)(struct wiphy *wiphy, struct net_device *dev, u8 *dst, u8 *next_hop); int (*get_mpath)(struct wiphy *wiphy, struct net_device *dev, u8 *dst, u8 *next_hop, struct mpath_info *pinfo); int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *dst, u8 *next_hop, struct mpath_info *pinfo); int (*get_mesh_config)(struct wiphy *wiphy, struct net_device *dev, struct mesh_config *conf); int (*update_mesh_config)(struct wiphy *wiphy, struct net_device *dev, u32 mask, const struct mesh_config *nconf); int (*join_mesh)(struct wiphy *wiphy, struct net_device *dev, const struct mesh_config *conf, const struct mesh_setup *setup); int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev); int (*change_bss)(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params); int (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_txq_params *params); int (*libertas_set_mesh_channel)(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan); int (*set_monitor_channel)(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); int (*scan)(struct wiphy *wiphy, struct cfg80211_scan_request *request); int (*auth)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req); int (*assoc)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req); int (*deauth)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_deauth_request *req); int (*disassoc)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req); int (*connect)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); int (*disconnect)(struct wiphy *wiphy, struct net_device *dev, u16 reason_code); int (*join_ibss)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params); int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev); int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed); int (*set_tx_power)(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm); int (*get_tx_power)(struct wiphy *wiphy, int *dbm); int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, const u8 *addr); void (*rfkill_poll)(struct wiphy *wiphy); #ifdef CONFIG_NL80211_TESTMODE int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len); int (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len); #endif int (*set_bitrate_mask)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, const struct cfg80211_bitrate_mask *mask); int (*dump_survey)(struct wiphy *wiphy, struct net_device *netdev, int idx, struct survey_info *info); int (*set_pmksa)(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa); int (*del_pmksa)(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa); int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev); int (*remain_on_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie); int (*cancel_remain_on_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); int (*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout); int (*set_cqm_rssi_config)(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst); int (*set_cqm_txe_config)(struct wiphy *wiphy, struct net_device *dev, u32 rate, u32 pkts, u32 intvl); void (*mgmt_frame_register)(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg); int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant); int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant); int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx); void (*get_ringparam)(struct wiphy *wiphy, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); int (*sched_scan_start)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request); int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev); int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data); int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper); int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie); int (*set_noack_map)(struct wiphy *wiphy, struct net_device *dev, u16 noack_map); int (*get_et_sset_count)(struct wiphy *wiphy, struct net_device *dev, int sset); void (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev, struct ethtool_stats *stats, u64 *data); void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, u32 sset, u8 *data); struct ieee80211_channel * (*get_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_channel_type *type); int (*start_p2p_device)(struct wiphy *wiphy, struct wireless_dev *wdev); void (*stop_p2p_device)(struct wiphy *wiphy, struct wireless_dev *wdev); }; /* * wireless hardware and networking interfaces structures * and registration/helper functions */ /** * enum wiphy_flags - wiphy capability flags * * @WIPHY_FLAG_CUSTOM_REGULATORY: tells us the driver for this device * has its own custom regulatory domain and cannot identify the * ISO / IEC 3166 alpha2 it belongs to. When this is enabled * we will disregard the first regulatory hint (when the * initiator is %REGDOM_SET_BY_CORE). * @WIPHY_FLAG_STRICT_REGULATORY: tells us the driver for this device will * ignore regulatory domain settings until it gets its own regulatory * domain via its regulatory_hint() unless the regulatory hint is * from a country IE. After its gets its own regulatory domain it will * only allow further regulatory domain settings to further enhance * compliance. For example if channel 13 and 14 are disabled by this * regulatory domain no user regulatory domain can enable these channels * at a later time. This can be used for devices which do not have * calibration information guaranteed for frequencies or settings * outside of its regulatory domain. If used in combination with * WIPHY_FLAG_CUSTOM_REGULATORY the inspected country IE power settings * will be followed. * @WIPHY_FLAG_DISABLE_BEACON_HINTS: enable this if your driver needs to ensure * that passive scan flags and beaconing flags may not be lifted by * cfg80211 due to regulatory beacon hints. For more information on beacon * hints read the documenation for regulatory_hint_found_beacon() * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this * wiphy at all * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled * by default -- this flag will be set depending on the kernel's default * on wiphy_new(), but can be changed by the driver if it has a good * reason to override the default * @WIPHY_FLAG_4ADDR_AP: supports 4addr mode even on AP (with a single station * on a VLAN interface) * @WIPHY_FLAG_4ADDR_STATION: supports 4addr mode even as a station * @WIPHY_FLAG_CONTROL_PORT_PROTOCOL: This device supports setting the * control port protocol ethertype. The device also honours the * control_port_no_encrypt flag. * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN. * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH. * @WIPHY_FLAG_SUPPORTS_SCHED_SCAN: The device supports scheduled scans. * @WIPHY_FLAG_SUPPORTS_FW_ROAM: The device supports roaming feature in the * firmware. * @WIPHY_FLAG_AP_UAPSD: The device supports uapsd on AP. * @WIPHY_FLAG_SUPPORTS_TDLS: The device supports TDLS (802.11z) operation. * @WIPHY_FLAG_TDLS_EXTERNAL_SETUP: The device does not handle TDLS (802.11z) * link setup/discovery operations internally. Setup, discovery and * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be * used for asking the driver/firmware to perform a TDLS operation. * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME * @WIPHY_FLAG_REPORTS_OBSS: the device will report beacons from other BSSes * when there are virtual interfaces in AP mode by calling * cfg80211_report_obss_beacon(). * @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD: When operating as an AP, the device * responds to probe-requests in hardware. * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX. * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call. */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), WIPHY_FLAG_STRICT_REGULATORY = BIT(1), WIPHY_FLAG_DISABLE_BEACON_HINTS = BIT(2), WIPHY_FLAG_NETNS_OK = BIT(3), WIPHY_FLAG_PS_ON_BY_DEFAULT = BIT(4), WIPHY_FLAG_4ADDR_AP = BIT(5), WIPHY_FLAG_4ADDR_STATION = BIT(6), WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), WIPHY_FLAG_MESH_AUTH = BIT(10), WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), /* use hole at 12 */ WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), WIPHY_FLAG_AP_UAPSD = BIT(14), WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), WIPHY_FLAG_HAVE_AP_SME = BIT(17), WIPHY_FLAG_REPORTS_OBSS = BIT(18), WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19), WIPHY_FLAG_OFFCHAN_TX = BIT(20), WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21), }; /** * struct ieee80211_iface_limit - limit on certain interface types * @max: maximum number of interfaces of these types * @types: interface types (bits) */ struct ieee80211_iface_limit { u16 max; u16 types; }; /** * struct ieee80211_iface_combination - possible interface combination * @limits: limits for the given interface types * @n_limits: number of limitations * @num_different_channels: can use up to this many different channels * @max_interfaces: maximum number of interfaces in total allowed in this * group * @beacon_int_infra_match: In this combination, the beacon intervals * between infrastructure and AP types must match. This is required * only in special cases. * * These examples can be expressed as follows: * * Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total: * * struct ieee80211_iface_limit limits1[] = { * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, * { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, }, * }; * struct ieee80211_iface_combination combination1 = { * .limits = limits1, * .n_limits = ARRAY_SIZE(limits1), * .max_interfaces = 2, * .beacon_int_infra_match = true, * }; * * * Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total: * * struct ieee80211_iface_limit limits2[] = { * { .max = 8, .types = BIT(NL80211_IFTYPE_AP) | * BIT(NL80211_IFTYPE_P2P_GO), }, * }; * struct ieee80211_iface_combination combination2 = { * .limits = limits2, * .n_limits = ARRAY_SIZE(limits2), * .max_interfaces = 8, * .num_different_channels = 1, * }; * * * Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total. * This allows for an infrastructure connection and three P2P connections. * * struct ieee80211_iface_limit limits3[] = { * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, * { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) | * BIT(NL80211_IFTYPE_P2P_CLIENT), }, * }; * struct ieee80211_iface_combination combination3 = { * .limits = limits3, * .n_limits = ARRAY_SIZE(limits3), * .max_interfaces = 4, * .num_different_channels = 2, * }; */ struct ieee80211_iface_combination { const struct ieee80211_iface_limit *limits; u32 num_different_channels; u16 max_interfaces; u8 n_limits; bool beacon_int_infra_match; }; struct mac_address { u8 addr[ETH_ALEN]; }; struct ieee80211_txrx_stypes { u16 tx, rx; }; /** * enum wiphy_wowlan_support_flags - WoWLAN support flags * @WIPHY_WOWLAN_ANY: supports wakeup for the special "any" * trigger that keeps the device operating as-is and * wakes up the host on any activity, for example a * received packet that passed filtering; note that the * packet should be preserved in that case * @WIPHY_WOWLAN_MAGIC_PKT: supports wakeup on magic packet * (see nl80211.h) * @WIPHY_WOWLAN_DISCONNECT: supports wakeup on disconnect * @WIPHY_WOWLAN_SUPPORTS_GTK_REKEY: supports GTK rekeying while asleep * @WIPHY_WOWLAN_GTK_REKEY_FAILURE: supports wakeup on GTK rekey failure * @WIPHY_WOWLAN_EAP_IDENTITY_REQ: supports wakeup on EAP identity request * @WIPHY_WOWLAN_4WAY_HANDSHAKE: supports wakeup on 4-way handshake failure * @WIPHY_WOWLAN_RFKILL_RELEASE: supports wakeup on RF-kill release */ enum wiphy_wowlan_support_flags { WIPHY_WOWLAN_ANY = BIT(0), WIPHY_WOWLAN_MAGIC_PKT = BIT(1), WIPHY_WOWLAN_DISCONNECT = BIT(2), WIPHY_WOWLAN_SUPPORTS_GTK_REKEY = BIT(3), WIPHY_WOWLAN_GTK_REKEY_FAILURE = BIT(4), WIPHY_WOWLAN_EAP_IDENTITY_REQ = BIT(5), WIPHY_WOWLAN_4WAY_HANDSHAKE = BIT(6), WIPHY_WOWLAN_RFKILL_RELEASE = BIT(7), }; /** * struct wiphy_wowlan_support - WoWLAN support data * @flags: see &enum wiphy_wowlan_support_flags * @n_patterns: number of supported wakeup patterns * (see nl80211.h for the pattern definition) * @pattern_max_len: maximum length of each pattern * @pattern_min_len: minimum length of each pattern */ struct wiphy_wowlan_support { u32 flags; int n_patterns; int pattern_max_len; int pattern_min_len; }; /** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, * note that if your driver uses wiphy_apply_custom_regulatory() * the reg_notifier's request can be passed as NULL * @regd: the driver's regulatory domain, if one was requested via * the regulatory_hint() API. This can be used by the driver * on the reg_notifier() if it chooses to ignore future * regulatory domain changes caused by other drivers. * @signal_type: signal type reported in &struct cfg80211_bss. * @cipher_suites: supported cipher suites * @n_cipher_suites: number of supported cipher suites * @retry_short: Retry limit for short frames (dot11ShortRetryLimit) * @retry_long: Retry limit for long frames (dot11LongRetryLimit) * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold); * -1 = fragmentation disabled, only odd values >= 256 used * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled * @_net: the network namespace this wiphy currently lives in * @perm_addr: permanent MAC address of this device * @addr_mask: If the device supports multiple MAC addresses by masking, * set this to a mask with variable bits set to 1, e.g. if the last * four bits are variable then set it to 00:...:00:0f. The actual * variable bits shall be determined by the interfaces added, with * interfaces not matching the mask being rejected to be brought up. * @n_addresses: number of addresses in @addresses. * @addresses: If the device has more than one address, set this pointer * to a list of addresses (6 bytes each). The first one will be used * by default for perm_addr. In this case, the mask should be set to * all-zeroes. In this case it is assumed that the device can handle * the same number of arbitrary MAC addresses. * @registered: protects ->resume and ->suspend sysfs callbacks against * unregister hardware * @debugfsdir: debugfs directory used for this wiphy, will be renamed * automatically on wiphy renames * @dev: (virtual) struct device for this wiphy * @registered: helps synchronize suspend/resume with wiphy unregister * @wext: wireless extension handlers * @priv: driver private data (sized according to wiphy_new() parameter) * @interface_modes: bitmask of interfaces types valid for this wiphy, * must be set by driver * @iface_combinations: Valid interface combinations array, should not * list single interface types. * @n_iface_combinations: number of entries in @iface_combinations array. * @software_iftypes: bitmask of software interface types, these are not * subject to any restrictions since they are purely managed in SW. * @flags: wiphy flags, see &enum wiphy_flags * @features: features advertised to nl80211, see &enum nl80211_feature_flags. * @bss_priv_size: each BSS struct has private data allocated with it, * this variable determines its size * @max_scan_ssids: maximum number of SSIDs the device can scan for in * any given scan * @max_sched_scan_ssids: maximum number of SSIDs the device can scan * for in any given scheduled scan * @max_match_sets: maximum number of match sets the device can handle * when performing a scheduled scan, 0 if filtering is not * supported. * @max_scan_ie_len: maximum length of user-controlled IEs device can * add to probe request frames transmitted during a scan, must not * include fixed IEs like supported rates * @max_sched_scan_ie_len: same as max_scan_ie_len, but for scheduled * scans * @coverage_class: current coverage class * @fw_version: firmware version for ethtool reporting * @hw_version: hardware version for ethtool reporting * @max_num_pmkids: maximum number of PMKIDs supported by device * @privid: a pointer that drivers can use to identify if an arbitrary * wiphy is theirs, e.g. in global notifiers * @bands: information about bands/channels supported by this device * * @mgmt_stypes: bitmasks of frame subtypes that can be subscribed to or * transmitted through nl80211, points to an array indexed by interface * type * * @available_antennas_tx: bitmap of antennas which are available to be * configured as TX antennas. Antenna configuration commands will be * rejected unless this or @available_antennas_rx is set. * * @available_antennas_rx: bitmap of antennas which are available to be * configured as RX antennas. Antenna configuration commands will be * rejected unless this or @available_antennas_tx is set. * * @probe_resp_offload: * Bitmap of supported protocols for probe response offloading. * See &enum nl80211_probe_resp_offload_support_attr. Only valid * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set. * * @max_remain_on_channel_duration: Maximum time a remain-on-channel operation * may request, if implemented. * * @wowlan: WoWLAN support information * * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. * @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden. * If null, then none can be over-ridden. */ struct wiphy { /* assign these fields before you register the wiphy */ #define WIPHY_COMPAT_PAD_SIZE 2048 u8 padding[WIPHY_COMPAT_PAD_SIZE]; /* permanent MAC address(es) */ u8 perm_addr[ETH_ALEN]; u8 addr_mask[ETH_ALEN]; struct mac_address *addresses; const struct ieee80211_txrx_stypes *mgmt_stypes; const struct ieee80211_iface_combination *iface_combinations; int n_iface_combinations; u16 software_iftypes; u16 n_addresses; /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ u16 interface_modes; u32 flags, features; u32 ap_sme_capa; enum cfg80211_signal_type signal_type; int bss_priv_size; u8 max_scan_ssids; u8 max_sched_scan_ssids; u8 max_match_sets; u16 max_scan_ie_len; u16 max_sched_scan_ie_len; int n_cipher_suites; const u32 *cipher_suites; u8 retry_short; u8 retry_long; u32 frag_threshold; u32 rts_threshold; u8 coverage_class; char fw_version[ETHTOOL_BUSINFO_LEN]; u32 hw_version; #ifdef CONFIG_PM struct wiphy_wowlan_support wowlan; #endif u16 max_remain_on_channel_duration; u8 max_num_pmkids; u32 available_antennas_tx; u32 available_antennas_rx; /* * Bitmap of supported protocols for probe response offloading * see &enum nl80211_probe_resp_offload_support_attr. Only valid * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set. */ u32 probe_resp_offload; /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't * know whether it points to a wiphy your driver has registered * or not. Assign this to something global to your driver to * help determine whether you own this wiphy or not. */ const void *privid; struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; /* Lets us get back the wiphy on the callback */ int (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request); /* fields below are read-only, assigned by cfg80211 */ const struct ieee80211_regdomain *regd; /* the item in /sys/class/ieee80211/ points to this, * you need use set_wiphy_dev() (see below) */ struct device dev; /* protects ->resume, ->suspend sysfs callbacks against unregister hw */ bool registered; /* dir in debugfs: ieee80211/ */ struct dentry *debugfsdir; const struct ieee80211_ht_cap *ht_capa_mod_mask; #ifdef CONFIG_NET_NS /* the network namespace this phy lives in currently */ struct net *_net; #endif #ifdef CONFIG_CFG80211_WEXT const struct iw_handler_def *wext; #endif char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; static inline struct net *wiphy_net(struct wiphy *wiphy) { return read_pnet(&wiphy->_net); } static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) { write_pnet(&wiphy->_net, net); } /** * wiphy_priv - return priv from wiphy * * @wiphy: the wiphy whose priv pointer to return */ static inline void *wiphy_priv(struct wiphy *wiphy) { BUG_ON(!wiphy); return &wiphy->priv; } /** * priv_to_wiphy - return the wiphy containing the priv * * @priv: a pointer previously returned by wiphy_priv */ static inline struct wiphy *priv_to_wiphy(void *priv) { BUG_ON(!priv); return container_of(priv, struct wiphy, priv); } /** * set_wiphy_dev - set device pointer for wiphy * * @wiphy: The wiphy whose device to bind * @dev: The device to parent it to */ static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev) { wiphy->dev.parent = dev; } /** * wiphy_dev - get wiphy dev pointer * * @wiphy: The wiphy whose device struct to look up */ static inline struct device *wiphy_dev(struct wiphy *wiphy) { return wiphy->dev.parent; } /** * wiphy_name - get wiphy name * * @wiphy: The wiphy whose name to return */ static inline const char *wiphy_name(const struct wiphy *wiphy) { return dev_name(&wiphy->dev); } /** * wiphy_new - create a new wiphy for use with cfg80211 * * @ops: The configuration operations for this device * @sizeof_priv: The size of the private area to allocate * * Create a new wiphy and associate the given operations with it. * @sizeof_priv bytes are allocated for private use. * * The returned pointer must be assigned to each netdev's * ieee80211_ptr for proper operation. */ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv); /** * wiphy_register - register a wiphy with cfg80211 * * @wiphy: The wiphy to register. * * Returns a non-negative wiphy index or a negative error code. */ extern int wiphy_register(struct wiphy *wiphy); /** * wiphy_unregister - deregister a wiphy from cfg80211 * * @wiphy: The wiphy to unregister. * * After this call, no more requests can be made with this priv * pointer, but the call may sleep to wait for an outstanding * request that is being handled. */ extern void wiphy_unregister(struct wiphy *wiphy); /** * wiphy_free - free wiphy * * @wiphy: The wiphy to free */ extern void wiphy_free(struct wiphy *wiphy); /* internal structs */ struct cfg80211_conn; struct cfg80211_internal_bss; struct cfg80211_cached_keys; /** * struct wireless_dev - wireless device state * * For netdevs, this structure must be allocated by the driver * that uses the ieee80211_ptr field in struct net_device (this * is intentional so it can be allocated along with the netdev.) * It need not be registered then as netdev registration will * be intercepted by cfg80211 to see the new wireless device. * * For non-netdev uses, it must also be allocated by the driver * in response to the cfg80211 callbacks that require it, as * there's no netdev registration in that case it may not be * allocated outside of callback operations that return it. * * @wiphy: pointer to hardware description * @iftype: interface type * @list: (private) Used to collect the interfaces * @netdev: (private) Used to reference back to the netdev, may be %NULL * @identifier: (private) Identifier used in nl80211 to identify this * wireless device if it has no netdev * @current_bss: (private) Used by the internal configuration code * @channel: (private) Used by the internal configuration code to track * the user-set AP, monitor and WDS channel * @preset_chan: (private) Used by the internal configuration code to * track the channel to be used for AP later * @preset_chantype: (private) the corresponding channel type * @bssid: (private) Used by the internal configuration code * @ssid: (private) Used by the internal configuration code * @ssid_len: (private) Used by the internal configuration code * @mesh_id_len: (private) Used by the internal configuration code * @mesh_id_up_len: (private) Used by the internal configuration code * @wext: (private) Used by the internal wireless extensions compat code * @use_4addr: indicates 4addr mode is used on this interface, must be * set by driver (if supported) on add_interface BEFORE registering the * netdev and may otherwise be used by driver read-only, will be update * by cfg80211 on change_interface * @mgmt_registrations: list of registrations for management frames * @mgmt_registrations_lock: lock for the list * @mtx: mutex used to lock data in this struct * @cleanup_work: work struct used for cleanup that can't be done directly * @beacon_interval: beacon interval used on this device for transmitting * beacons, 0 when not valid * @address: The address for this device, valid only if @netdev is %NULL * @p2p_started: true if this is a P2P Device that has been started */ struct wireless_dev { struct wiphy *wiphy; enum nl80211_iftype iftype; /* the remainder of this struct should be private to cfg80211 */ struct list_head list; struct net_device *netdev; u32 identifier; struct list_head mgmt_registrations; spinlock_t mgmt_registrations_lock; struct mutex mtx; struct work_struct cleanup_work; bool use_4addr, p2p_started; u8 address[ETH_ALEN] __aligned(sizeof(u16)); /* currently used for IBSS and SME - might be rearranged later */ u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len, mesh_id_len, mesh_id_up_len; enum { CFG80211_SME_IDLE, CFG80211_SME_CONNECTING, CFG80211_SME_CONNECTED, } sme_state; struct cfg80211_conn *conn; struct cfg80211_cached_keys *connect_keys; struct list_head event_list; spinlock_t event_lock; struct cfg80211_internal_bss *current_bss; /* associated / joined */ struct ieee80211_channel *preset_chan; enum nl80211_channel_type preset_chantype; /* for AP and mesh channel tracking */ struct ieee80211_channel *channel; bool ibss_fixed; bool ps; int ps_timeout; int beacon_interval; u32 ap_unexpected_nlportid; #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct { struct cfg80211_ibss_params ibss; struct cfg80211_connect_params connect; struct cfg80211_cached_keys *keys; u8 *ie; size_t ie_len; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; s8 default_key, default_mgmt_key; bool prev_bssid_valid; } wext; #endif }; static inline u8 *wdev_address(struct wireless_dev *wdev) { if (wdev->netdev) return wdev->netdev->dev_addr; return wdev->address; } /** * wdev_priv - return wiphy priv from wireless_dev * * @wdev: The wireless device whose wiphy's priv pointer to return */ static inline void *wdev_priv(struct wireless_dev *wdev) { BUG_ON(!wdev); return wiphy_priv(wdev->wiphy); } /** * DOC: Utility functions * * cfg80211 offers a number of utility functions that can be useful. */ /** * ieee80211_channel_to_frequency - convert channel number to frequency * @chan: channel number * @band: band, necessary due to channel number overlap */ extern int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band); /** * ieee80211_frequency_to_channel - convert frequency to channel number * @freq: center frequency */ extern int ieee80211_frequency_to_channel(int freq); /* * Name indirection necessary because the ieee80211 code also has * a function named "ieee80211_get_channel", so if you include * cfg80211's header file you get cfg80211's version, if you try * to include both header files you'll (rightfully!) get a symbol * clash. */ extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, int freq); /** * ieee80211_get_channel - get channel struct from wiphy for specified frequency * @wiphy: the struct wiphy to get the channel for * @freq: the center frequency of the channel */ static inline struct ieee80211_channel * ieee80211_get_channel(struct wiphy *wiphy, int freq) { return __ieee80211_get_channel(wiphy, freq); } /** * ieee80211_get_response_rate - get basic rate for a given rate * * @sband: the band to look for rates in * @basic_rates: bitmap of basic rates * @bitrate: the bitrate for which to find the basic rate * * This function returns the basic rate corresponding to a given * bitrate, that is the next lower bitrate contained in the basic * rate map, which is, for this function, given as a bitmap of * indices of rates in the band's bitrate table. */ struct ieee80211_rate * ieee80211_get_response_rate(struct ieee80211_supported_band *sband, u32 basic_rates, int bitrate); /* * Radiotap parsing functions -- for controlled injection support * * Implemented in net/wireless/radiotap.c * Documentation in Documentation/networking/radiotap-headers.txt */ struct radiotap_align_size { uint8_t align:4, size:4; }; struct ieee80211_radiotap_namespace { const struct radiotap_align_size *align_size; int n_bits; uint32_t oui; uint8_t subns; }; struct ieee80211_radiotap_vendor_namespaces { const struct ieee80211_radiotap_namespace *ns; int n_ns; }; /** * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args * @this_arg_index: index of current arg, valid after each successful call * to ieee80211_radiotap_iterator_next() * @this_arg: pointer to current radiotap arg; it is valid after each * call to ieee80211_radiotap_iterator_next() but also after * ieee80211_radiotap_iterator_init() where it will point to * the beginning of the actual data portion * @this_arg_size: length of the current arg, for convenience * @current_namespace: pointer to the current namespace definition * (or internally %NULL if the current namespace is unknown) * @is_radiotap_ns: indicates whether the current namespace is the default * radiotap namespace or not * * @_rtheader: pointer to the radiotap header we are walking through * @_max_length: length of radiotap header in cpu byte ordering * @_arg_index: next argument index * @_arg: next argument pointer * @_next_bitmap: internal pointer to next present u32 * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present * @_vns: vendor namespace definitions * @_next_ns_data: beginning of the next namespace's data * @_reset_on_ext: internal; reset the arg index to 0 when going to the * next bitmap word * * Describes the radiotap parser state. Fields prefixed with an underscore * must not be used by users of the parser, only by the parser internally. */ struct ieee80211_radiotap_iterator { struct ieee80211_radiotap_header *_rtheader; const struct ieee80211_radiotap_vendor_namespaces *_vns; const struct ieee80211_radiotap_namespace *current_namespace; unsigned char *_arg, *_next_ns_data; __le32 *_next_bitmap; unsigned char *this_arg; int this_arg_index; int this_arg_size; int is_radiotap_ns; int _max_length; int _arg_index; uint32_t _bitmap_shifter; int _reset_on_ext; }; extern int ieee80211_radiotap_iterator_init( struct ieee80211_radiotap_iterator *iterator, struct ieee80211_radiotap_header *radiotap_header, int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns); extern int ieee80211_radiotap_iterator_next( struct ieee80211_radiotap_iterator *iterator); extern const unsigned char rfc1042_header[6]; extern const unsigned char bridge_tunnel_header[6]; /** * ieee80211_get_hdrlen_from_skb - get header length from data * * Given an skb with a raw 802.11 header at the data pointer this function * returns the 802.11 header length in bytes (not including encryption * headers). If the data in the sk_buff is too short to contain a valid 802.11 * header the function returns 0. * * @skb: the frame */ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); /** * ieee80211_hdrlen - get header length in bytes from frame control * @fc: frame control field in little-endian format */ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc); /** * DOC: Data path helpers * * In addition to generic utilities, cfg80211 also offers * functions that help implement the data path for devices * that do not do the 802.11/802.3 conversion on the device. */ /** * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3 * @skb: the 802.11 data frame * @addr: the device MAC address * @iftype: the virtual interface type */ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype); /** * ieee80211_data_from_8023 - convert an 802.3 frame to 802.11 * @skb: the 802.3 frame * @addr: the device MAC address * @iftype: the virtual interface type * @bssid: the network bssid (used only for iftype STATION and ADHOC) * @qos: build 802.11 QoS data frame */ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype, u8 *bssid, bool qos); /** * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame * * Decode an IEEE 802.11n A-MSDU frame and convert it to a list of * 802.3 frames. The @list will be empty if the decode fails. The * @skb is consumed after the function returns. * * @skb: The input IEEE 802.11n A-MSDU frame. * @list: The output list of 802.3 frames. It must be allocated and * initialized by by the caller. * @addr: The device MAC address. * @iftype: The device interface type. * @extra_headroom: The hardware extra headroom for SKBs in the @list. * @has_80211_header: Set it true if SKB is with IEEE 802.11 header. */ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, const u8 *addr, enum nl80211_iftype iftype, const unsigned int extra_headroom, bool has_80211_header); /** * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame * @skb: the data frame */ unsigned int cfg80211_classify8021d(struct sk_buff *skb); /** * cfg80211_find_ie - find information element in data * * @eid: element ID * @ies: data consisting of IEs * @len: length of data * * This function will return %NULL if the element ID could * not be found or if the element is invalid (claims to be * longer than the given data), or a pointer to the first byte * of the requested element, that is the byte containing the * element ID. There are no checks on the element length * other than having to fit into the given data. */ const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); /** * cfg80211_find_vendor_ie - find vendor specific information element in data * * @oui: vendor OUI * @oui_type: vendor-specific OUI type * @ies: data consisting of IEs * @len: length of data * * This function will return %NULL if the vendor specific element ID * could not be found or if the element is invalid (claims to be * longer than the given data), or a pointer to the first byte * of the requested element, that is the byte containing the * element ID. There are no checks on the element length * other than having to fit into the given data. */ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, const u8 *ies, int len); /** * DOC: Regulatory enforcement infrastructure * * TODO */ /** * regulatory_hint - driver hint to the wireless core a regulatory domain * @wiphy: the wireless device giving the hint (used only for reporting * conflicts) * @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain * should be in. If @rd is set this should be NULL. Note that if you * set this to NULL you should still set rd->alpha2 to some accepted * alpha2. * * Wireless drivers can use this function to hint to the wireless core * what it believes should be the current regulatory domain by * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory * domain should be in or by providing a completely build regulatory domain. * If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried * for a regulatory domain structure for the respective country. * * The wiphy must have been registered to cfg80211 prior to this call. * For cfg80211 drivers this means you must first use wiphy_register(), * for mac80211 drivers you must first use ieee80211_register_hw(). * * Drivers should check the return value, its possible you can get * an -ENOMEM. */ extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); /** * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain * @wiphy: the wireless device we want to process the regulatory domain on * @regd: the custom regulatory domain to use for this wiphy * * Drivers can sometimes have custom regulatory domains which do not apply * to a specific country. Drivers can use this to apply such custom regulatory * domains. This routine must be called prior to wiphy registration. The * custom regulatory domain will be trusted completely and as such previous * default channel settings will be disregarded. If no rule is found for a * channel on the regulatory domain the channel will be disabled. */ extern void wiphy_apply_custom_regulatory( struct wiphy *wiphy, const struct ieee80211_regdomain *regd); /** * freq_reg_info - get regulatory information for the given frequency * @wiphy: the wiphy for which we want to process this rule for * @center_freq: Frequency in KHz for which we want regulatory information for * @desired_bw_khz: the desired max bandwidth you want to use per * channel. Note that this is still 20 MHz if you want to use HT40 * as HT40 makes use of two channels for its 40 MHz width bandwidth. * If set to 0 we'll assume you want the standard 20 MHz. * @reg_rule: the regulatory rule which we have for this frequency * * Use this function to get the regulatory rule for a specific frequency on * a given wireless device. If the device has a specific regulatory domain * it wants to follow we respect that unless a country IE has been received * and processed already. * * Returns 0 if it was able to find a valid regulatory rule which does * apply to the given center_freq otherwise it returns non-zero. It will * also return -ERANGE if we determine the given center_freq does not even have * a regulatory rule for a frequency range in the center_freq's band. See * freq_in_rule_band() for our current definition of a band -- this is purely * subjective and right now its 802.11 specific. */ extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule); /* * callbacks for asynchronous cfg80211 methods, notification * functions and BSS handling helpers */ /** * cfg80211_scan_done - notify that scan finished * * @request: the corresponding scan request * @aborted: set to true if the scan was aborted for any reason, * userspace will be notified of that */ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); /** * cfg80211_sched_scan_results - notify that new scan results are available * * @wiphy: the wiphy which got scheduled scan results */ void cfg80211_sched_scan_results(struct wiphy *wiphy); /** * cfg80211_sched_scan_stopped - notify that the scheduled scan has stopped * * @wiphy: the wiphy on which the scheduled scan stopped * * The driver can call this function to inform cfg80211 that the * scheduled scan had to be stopped, for whatever reason. The driver * is then called back via the sched_scan_stop operation when done. */ void cfg80211_sched_scan_stopped(struct wiphy *wiphy); /** * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame * * @wiphy: the wiphy reporting the BSS * @channel: The channel the frame was received on * @mgmt: the management frame (probe response or beacon) * @len: length of the management frame * @signal: the signal strength, type depends on the wiphy's signal_type * @gfp: context flags * * This informs cfg80211 that BSS information was found and * the BSS should be updated/added. * * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! */ struct cfg80211_bss * __must_check cfg80211_inform_bss_frame(struct wiphy *wiphy, struct ieee80211_channel *channel, struct ieee80211_mgmt *mgmt, size_t len, s32 signal, gfp_t gfp); /** * cfg80211_inform_bss - inform cfg80211 of a new BSS * * @wiphy: the wiphy reporting the BSS * @channel: The channel the frame was received on * @bssid: the BSSID of the BSS * @tsf: the TSF sent by the peer in the beacon/probe response (or 0) * @capability: the capability field sent by the peer * @beacon_interval: the beacon interval announced by the peer * @ie: additional IEs sent by the peer * @ielen: length of the additional IEs * @signal: the signal strength, type depends on the wiphy's signal_type * @gfp: context flags * * This informs cfg80211 that BSS information was found and * the BSS should be updated/added. * * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! */ struct cfg80211_bss * __must_check cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp); struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, u16 capa_mask, u16 capa_val); static inline struct cfg80211_bss * cfg80211_get_ibss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *ssid, size_t ssid_len) { return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); } struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *meshid, size_t meshidlen, const u8 *meshcfg); /** * cfg80211_ref_bss - reference BSS struct * @bss: the BSS struct to reference * * Increments the refcount of the given BSS struct. */ void cfg80211_ref_bss(struct cfg80211_bss *bss); /** * cfg80211_put_bss - unref BSS struct * @bss: the BSS struct * * Decrements the refcount of the given BSS struct. */ void cfg80211_put_bss(struct cfg80211_bss *bss); /** * cfg80211_unlink_bss - unlink BSS from internal data structures * @wiphy: the wiphy * @bss: the bss to remove * * This function removes the given BSS from the internal data structures * thereby making it no longer show up in scan results etc. Use this * function when you detect a BSS is gone. Normally BSSes will also time * out, so it is not necessary to use this function at all. */ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); /** * cfg80211_send_rx_auth - notification of processed authentication * @dev: network device * @buf: authentication frame (header + body) * @len: length of the frame data * * This function is called whenever an authentication has been processed in * station mode. The driver is required to call either this function or * cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth() * call. This function may sleep. */ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); /** * cfg80211_send_auth_timeout - notification of timed out authentication * @dev: network device * @addr: The MAC address of the device with which the authentication timed out * * This function may sleep. */ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr); /** * cfg80211_send_rx_assoc - notification of processed association * @dev: network device * @bss: the BSS struct association was requested for, the struct reference * is owned by cfg80211 after this call * @buf: (re)association response frame (header + body) * @len: length of the frame data * * This function is called whenever a (re)association response has been * processed in station mode. The driver is required to call either this * function or cfg80211_send_assoc_timeout() to indicate the result of * cfg80211_ops::assoc() call. This function may sleep. */ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, const u8 *buf, size_t len); /** * cfg80211_send_assoc_timeout - notification of timed out association * @dev: network device * @addr: The MAC address of the device with which the association timed out * * This function may sleep. */ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr); /** * cfg80211_send_deauth - notification of processed deauthentication * @dev: network device * @buf: deauthentication frame (header + body) * @len: length of the frame data * * This function is called whenever deauthentication has been processed in * station mode. This includes both received deauthentication frames and * locally generated ones. This function may sleep. */ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); /** * __cfg80211_send_deauth - notification of processed deauthentication * @dev: network device * @buf: deauthentication frame (header + body) * @len: length of the frame data * * Like cfg80211_send_deauth(), but doesn't take the wdev lock. */ void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); /** * cfg80211_send_disassoc - notification of processed disassociation * @dev: network device * @buf: disassociation response frame (header + body) * @len: length of the frame data * * This function is called whenever disassociation has been processed in * station mode. This includes both received disassociation frames and locally * generated ones. This function may sleep. */ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len); /** * __cfg80211_send_disassoc - notification of processed disassociation * @dev: network device * @buf: disassociation response frame (header + body) * @len: length of the frame data * * Like cfg80211_send_disassoc(), but doesn't take the wdev lock. */ void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len); /** * cfg80211_send_unprot_deauth - notification of unprotected deauthentication * @dev: network device * @buf: deauthentication frame (header + body) * @len: length of the frame data * * This function is called whenever a received Deauthentication frame has been * dropped in station mode because of MFP being used but the Deauthentication * frame was not protected. This function may sleep. */ void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, size_t len); /** * cfg80211_send_unprot_disassoc - notification of unprotected disassociation * @dev: network device * @buf: disassociation frame (header + body) * @len: length of the frame data * * This function is called whenever a received Disassociation frame has been * dropped in station mode because of MFP being used but the Disassociation * frame was not protected. This function may sleep. */ void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, size_t len); /** * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP) * @dev: network device * @addr: The source MAC address of the frame * @key_type: The key type that the received frame used * @key_id: Key identifier (0..3). Can be -1 if missing. * @tsc: The TSC value of the frame that generated the MIC failure (6 octets) * @gfp: allocation flags * * This function is called whenever the local MAC detects a MIC failure in a * received frame. This matches with MLME-MICHAELMICFAILURE.indication() * primitive. */ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, enum nl80211_key_type key_type, int key_id, const u8 *tsc, gfp_t gfp); /** * cfg80211_ibss_joined - notify cfg80211 that device joined an IBSS * * @dev: network device * @bssid: the BSSID of the IBSS joined * @gfp: allocation flags * * This function notifies cfg80211 that the device joined an IBSS or * switched to a different BSSID. Before this function can be called, * either a beacon has to have been received from the IBSS, or one of * the cfg80211_inform_bss{,_frame} functions must have been called * with the locally generated beacon -- this guarantees that there is * always a scan result for this IBSS. cfg80211 will handle the rest. */ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); /** * cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate * * @dev: network device * @macaddr: the MAC address of the new candidate * @ie: information elements advertised by the peer candidate * @ie_len: lenght of the information elements buffer * @gfp: allocation flags * * This function notifies cfg80211 that the mesh peer candidate has been * detected, most likely via a beacon or, less likely, via a probe response. * cfg80211 then sends a notification to userspace. */ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *macaddr, const u8 *ie, u8 ie_len, gfp_t gfp); /** * DOC: RFkill integration * * RFkill integration in cfg80211 is almost invisible to drivers, * as cfg80211 automatically registers an rfkill instance for each * wireless device it knows about. Soft kill is also translated * into disconnecting and turning all interfaces off, drivers are * expected to turn off the device when all interfaces are down. * * However, devices may have a hard RFkill line, in which case they * also need to interact with the rfkill subsystem, via cfg80211. * They can do this with a few helper functions documented here. */ /** * wiphy_rfkill_set_hw_state - notify cfg80211 about hw block state * @wiphy: the wiphy * @blocked: block status */ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked); /** * wiphy_rfkill_start_polling - start polling rfkill * @wiphy: the wiphy */ void wiphy_rfkill_start_polling(struct wiphy *wiphy); /** * wiphy_rfkill_stop_polling - stop polling rfkill * @wiphy: the wiphy */ void wiphy_rfkill_stop_polling(struct wiphy *wiphy); #ifdef CONFIG_NL80211_TESTMODE /** * DOC: Test mode * * Test mode is a set of utility functions to allow drivers to * interact with driver-specific tools to aid, for instance, * factory programming. * * This chapter describes how drivers interact with it, for more * information see the nl80211 book's chapter on it. */ /** * cfg80211_testmode_alloc_reply_skb - allocate testmode reply * @wiphy: the wiphy * @approxlen: an upper bound of the length of the data that will * be put into the skb * * This function allocates and pre-fills an skb for a reply to * the testmode command. Since it is intended for a reply, calling * it outside of the @testmode_cmd operation is invalid. * * The returned skb (or %NULL if any errors happen) is pre-filled * with the wiphy index and set up in a way that any data that is * put into the skb (with skb_put(), nla_put() or similar) will end * up being within the %NL80211_ATTR_TESTDATA attribute, so all that * needs to be done with the skb is adding data for the corresponding * userspace tool which can then read that data out of the testdata * attribute. You must not modify the skb in any other way. * * When done, call cfg80211_testmode_reply() with the skb and return * its error code as the result of the @testmode_cmd operation. */ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen); /** * cfg80211_testmode_reply - send the reply skb * @skb: The skb, must have been allocated with * cfg80211_testmode_alloc_reply_skb() * * Returns an error code or 0 on success, since calling this * function will usually be the last thing before returning * from the @testmode_cmd you should return the error code. * Note that this function consumes the skb regardless of the * return value. */ int cfg80211_testmode_reply(struct sk_buff *skb); /** * cfg80211_testmode_alloc_event_skb - allocate testmode event * @wiphy: the wiphy * @approxlen: an upper bound of the length of the data that will * be put into the skb * @gfp: allocation flags * * This function allocates and pre-fills an skb for an event on the * testmode multicast group. * * The returned skb (or %NULL if any errors happen) is set up in the * same way as with cfg80211_testmode_alloc_reply_skb() but prepared * for an event. As there, you should simply add data to it that will * then end up in the %NL80211_ATTR_TESTDATA attribute. Again, you must * not modify the skb in any other way. * * When done filling the skb, call cfg80211_testmode_event() with the * skb to send the event. */ struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp); /** * cfg80211_testmode_event - send the event * @skb: The skb, must have been allocated with * cfg80211_testmode_alloc_event_skb() * @gfp: allocation flags * * This function sends the given @skb, which must have been allocated * by cfg80211_testmode_alloc_event_skb(), as an event. It always * consumes it. */ void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp); #define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd), #define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd), #else #define CFG80211_TESTMODE_CMD(cmd) #define CFG80211_TESTMODE_DUMP(cmd) #endif /** * cfg80211_connect_result - notify cfg80211 of connection result * * @dev: network device * @bssid: the BSSID of the AP * @req_ie: association request IEs (maybe be %NULL) * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length * @status: status code, 0 for successful connection, use * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you * the real status code for failures. * @gfp: allocation flags * * It should be called by the underlying driver whenever connect() has * succeeded. */ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, u16 status, gfp_t gfp); /** * cfg80211_roamed - notify cfg80211 of roaming * * @dev: network device * @channel: the channel of the new AP * @bssid: the BSSID of the new AP * @req_ie: association request IEs (maybe be %NULL) * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length * @gfp: allocation flags * * It should be called by the underlying driver whenever it roamed * from one AP to another while connected. */ void cfg80211_roamed(struct net_device *dev, struct ieee80211_channel *channel, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); /** * cfg80211_roamed_bss - notify cfg80211 of roaming * * @dev: network device * @bss: entry of bss to which STA got roamed * @req_ie: association request IEs (maybe be %NULL) * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length * @gfp: allocation flags * * This is just a wrapper to notify cfg80211 of roaming event with driver * passing bss to avoid a race in timeout of the bss entry. It should be * called by the underlying driver whenever it roamed from one AP to another * while connected. Drivers which have roaming implemented in firmware * may use this function to avoid a race in bss entry timeout where the bss * entry of the new AP is seen in the driver, but gets timed out by the time * it is accessed in __cfg80211_roamed() due to delay in scheduling * rdev->event_work. In case of any failures, the reference is released * either in cfg80211_roamed_bss() or in __cfg80211_romed(), Otherwise, * it will be released while diconneting from the current bss. */ void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); /** * cfg80211_disconnected - notify cfg80211 that connection was dropped * * @dev: network device * @ie: information elements of the deauth/disassoc frame (may be %NULL) * @ie_len: length of IEs * @reason: reason code for the disconnection, set it to 0 if unknown * @gfp: allocation flags * * After it calls this function, the driver should enter an idle state * and not try to connect to any AP any more. */ void cfg80211_disconnected(struct net_device *dev, u16 reason, u8 *ie, size_t ie_len, gfp_t gfp); /** * cfg80211_ready_on_channel - notification of remain_on_channel start * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) * @channel_type: Channel type * @duration: Duration in milliseconds that the driver intents to remain on the * channel * @gfp: allocation flags */ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); /** * cfg80211_remain_on_channel_expired - remain_on_channel duration expired * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) * @channel_type: Channel type * @gfp: allocation flags */ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp); /** * cfg80211_new_sta - notify userspace about station * * @dev: the netdev * @mac_addr: the station's address * @sinfo: the station information * @gfp: allocation flags */ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp); /** * cfg80211_del_sta - notify userspace about deletion of a station * * @dev: the netdev * @mac_addr: the station's address * @gfp: allocation flags */ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); /** * cfg80211_rx_mgmt - notification of received, unprocessed management frame * @wdev: wireless device receiving the frame * @freq: Frequency on which the frame was received in MHz * @sig_dbm: signal strength in mBm, or 0 if unknown * @buf: Management frame (header + body) * @len: length of the frame data * @gfp: context flags * * Returns %true if a user space application has registered for this frame. * For action frames, that makes it responsible for rejecting unrecognized * action frames; %false otherwise, in which case for action frames the * driver is responsible for rejecting the frame. * * This function is called whenever an Action frame is received for a station * mode interface, but is not processed in kernel. */ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp); /** * cfg80211_mgmt_tx_status - notification of TX status for management frame * @wdev: wireless device receiving the frame * @cookie: Cookie returned by cfg80211_ops::mgmt_tx() * @buf: Management frame (header + body) * @len: length of the frame data * @ack: Whether frame was acknowledged * @gfp: context flags * * This function is called whenever a management frame was requested to be * transmitted with cfg80211_ops::mgmt_tx() to report the TX status of the * transmission attempt. */ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); /** * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event * @dev: network device * @rssi_event: the triggered RSSI event * @gfp: context flags * * This function is called when a configured connection quality monitoring * rssi threshold reached event occurs. */ void cfg80211_cqm_rssi_notify(struct net_device *dev, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); /** * cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer * @dev: network device * @peer: peer's MAC address * @num_packets: how many packets were lost -- should be a fixed threshold * but probably no less than maybe 50, or maybe a throughput dependent * threshold (to account for temporary interference) * @gfp: context flags */ void cfg80211_cqm_pktloss_notify(struct net_device *dev, const u8 *peer, u32 num_packets, gfp_t gfp); /** * cfg80211_cqm_txe_notify - TX error rate event * @dev: network device * @peer: peer's MAC address * @num_packets: how many packets were lost * @rate: % of packets which failed transmission * @intvl: interval (in s) over which the TX failure threshold was breached. * @gfp: context flags * * Notify userspace when configured % TX failures over number of packets in a * given interval is exceeded. */ void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, u32 num_packets, u32 rate, u32 intvl, gfp_t gfp); /** * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying * @dev: network device * @bssid: BSSID of AP (to avoid races) * @replay_ctr: new replay counter * @gfp: allocation flags */ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); /** * cfg80211_pmksa_candidate_notify - notify about PMKSA caching candidate * @dev: network device * @index: candidate index (the smaller the index, the higher the priority) * @bssid: BSSID of AP * @preauth: Whether AP advertises support for RSN pre-authentication * @gfp: allocation flags */ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, const u8 *bssid, bool preauth, gfp_t gfp); /** * cfg80211_rx_spurious_frame - inform userspace about a spurious frame * @dev: The device the frame matched to * @addr: the transmitter address * @gfp: context flags * * This function is used in AP mode (only!) to inform userspace that * a spurious class 3 frame was received, to be able to deauth the * sender. * Returns %true if the frame was passed to userspace (or this failed * for a reason other than not having a subscription.) */ bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); /** * cfg80211_rx_unexpected_4addr_frame - inform about unexpected WDS frame * @dev: The device the frame matched to * @addr: the transmitter address * @gfp: context flags * * This function is used in AP mode (only!) to inform userspace that * an associated station sent a 4addr frame but that wasn't expected. * It is allowed and desirable to send this event only once for each * station to avoid event flooding. * Returns %true if the frame was passed to userspace (or this failed * for a reason other than not having a subscription.) */ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); /** * cfg80211_probe_status - notify userspace about probe status * @dev: the device the probe was sent on * @addr: the address of the peer * @cookie: the cookie filled in @probe_client previously * @acked: indicates whether probe was acked or not * @gfp: allocation flags */ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, u64 cookie, bool acked, gfp_t gfp); /** * cfg80211_report_obss_beacon - report beacon from other APs * @wiphy: The wiphy that received the beacon * @frame: the frame * @len: length of the frame * @freq: frequency the frame was received on * @sig_dbm: signal strength in mBm, or 0 if unknown * @gfp: allocation flags * * Use this function to report to userspace when a beacon was * received. It is not useful to call this when there is no * netdev that is in AP/GO mode. */ void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, int freq, int sig_dbm, gfp_t gfp); /** * cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used * @wiphy: the wiphy * @chan: main channel * @channel_type: HT mode * * This function returns true if there is no secondary channel or the secondary * channel can be used for beaconing (i.e. is not a radar channel etc.) */ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); /* * cfg80211_ch_switch_notify - update wdev channel and notify userspace * @dev: the device which switched channels * @freq: new channel frequency (in MHz) * @type: channel type * * Acquires wdev_lock, so must only be called from sleepable driver context! */ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, enum nl80211_channel_type type); /* * cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units) * @rate: given rate_info to calculate bitrate from * * return 0 if MCS index >= 32 */ u32 cfg80211_calculate_bitrate(struct rate_info *rate); /** * cfg80211_unregister_wdev - remove the given wdev * @wdev: struct wireless_dev to remove * * Call this function only for wdevs that have no netdev assigned, * e.g. P2P Devices. It removes the device from the list so that * it can no longer be used. It is necessary to call this function * even when cfg80211 requests the removal of the interface by * calling the del_virtual_intf() callback. The function must also * be called when the driver wishes to unregister the wdev, e.g. * when the device is unbound from the driver. * * Requires the RTNL to be held. */ void cfg80211_unregister_wdev(struct wireless_dev *wdev); /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ #define wiphy_printk(level, wiphy, format, args...) \ dev_printk(level, &(wiphy)->dev, format, ##args) #define wiphy_emerg(wiphy, format, args...) \ dev_emerg(&(wiphy)->dev, format, ##args) #define wiphy_alert(wiphy, format, args...) \ dev_alert(&(wiphy)->dev, format, ##args) #define wiphy_crit(wiphy, format, args...) \ dev_crit(&(wiphy)->dev, format, ##args) #define wiphy_err(wiphy, format, args...) \ dev_err(&(wiphy)->dev, format, ##args) #define wiphy_warn(wiphy, format, args...) \ dev_warn(&(wiphy)->dev, format, ##args) #define wiphy_notice(wiphy, format, args...) \ dev_notice(&(wiphy)->dev, format, ##args) #define wiphy_info(wiphy, format, args...) \ dev_info(&(wiphy)->dev, format, ##args) #define wiphy_debug(wiphy, format, args...) \ wiphy_printk(KERN_DEBUG, wiphy, format, ##args) #define wiphy_dbg(wiphy, format, args...) \ dev_dbg(&(wiphy)->dev, format, ##args) #if defined(VERBOSE_DEBUG) #define wiphy_vdbg wiphy_dbg #else #define wiphy_vdbg(wiphy, format, args...) \ ({ \ if (0) \ wiphy_printk(KERN_DEBUG, wiphy, format, ##args); \ 0; \ }) #endif /* * wiphy_WARN() acts like wiphy_printk(), but with the key difference * of using a WARN/WARN_ON to get the message out, including the * file/line information and a backtrace. */ #define wiphy_WARN(wiphy, format, args...) \ WARN(1, "wiphy: %s\n" format, wiphy_name(wiphy), ##args); #endif /* __NET_CFG80211_H */ compat-drivers-2012-09-18/include/net/flow_keys.h0000644000175000017500000000063712025673354020770 0ustar mcgrofmcgrof#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) #include_next #else #ifndef _NET_FLOW_KEYS_H #define _NET_FLOW_KEYS_H struct flow_keys { /* (src,dst) must be grouped, in the same way than in IP header */ __be32 src; __be32 dst; union { __be32 ports; __be16 port16[2]; }; u8 ip_proto; }; extern bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow); #endif #endif compat-drivers-2012-09-18/include/net/net_namespace.h0000644000175000017500000000044212025673354021562 0ustar mcgrofmcgrof#ifndef _COMPAT_NET_NET_NAMESPACE_H #define _COMPAT_NET_NET_NAMESPACE_H 1 #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)) #include_next #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)) */ #endif /* _COMPAT_NET_NET_NAMESPACE_H */ compat-drivers-2012-09-18/include/net/ieee80211_radiotap.h0000644000175000017500000002504212026211315022133 0ustar mcgrofmcgrof/* * Copyright (c) 2003, 2004 David Young. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of David Young may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ /* * Modifications to fit into the linux IEEE 802.11 stack, * Mike Kershaw (dragorn@kismetwireless.net) */ #ifndef IEEE80211RADIOTAP_H #define IEEE80211RADIOTAP_H #include #include #include /* Base version of the radiotap packet header data */ #define PKTHDR_RADIOTAP_VERSION 0 /* A generic radio capture format is desirable. There is one for * Linux, but it is neither rigidly defined (there were not even * units given for some fields) nor easily extensible. * * I suggest the following extensible radio capture format. It is * based on a bitmap indicating which fields are present. * * I am trying to describe precisely what the application programmer * should expect in the following, and for that reason I tell the * units and origin of each measurement (where it applies), or else I * use sufficiently weaselly language ("is a monotonically nondecreasing * function of...") that I cannot set false expectations for lawyerly * readers. */ /* * The radio capture header precedes the 802.11 header. * All data in the header is little endian on all platforms. */ struct ieee80211_radiotap_header { u8 it_version; /* Version 0. Only increases * for drastic changes, * introduction of compatible * new fields does not count. */ u8 it_pad; __le16 it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ __le32 it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. * Additional extensions are made * by setting bit 31. */ } __packed; /* Name Data type Units * ---- --------- ----- * * IEEE80211_RADIOTAP_TSFT __le64 microseconds * * Value in microseconds of the MAC's 64-bit 802.11 Time * Synchronization Function timer when the first bit of the * MPDU arrived at the MAC. For received frames, only. * * IEEE80211_RADIOTAP_CHANNEL 2 x __le16 MHz, bitmap * * Tx/Rx frequency in MHz, followed by flags (see below). * * IEEE80211_RADIOTAP_FHSS __le16 see below * * For frequency-hopping radios, the hop set (first byte) * and pattern (second byte). * * IEEE80211_RADIOTAP_RATE u8 500kb/s * * Tx/Rx data rate * * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from * one milliwatt (dBm) * * RF signal power at the antenna, decibel difference from * one milliwatt. * * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from * one milliwatt (dBm) * * RF noise power at the antenna, decibel difference from one * milliwatt. * * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) * * RF signal power at the antenna, decibel difference from an * arbitrary, fixed reference. * * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) * * RF noise power at the antenna, decibel difference from an * arbitrary, fixed reference point. * * IEEE80211_RADIOTAP_LOCK_QUALITY __le16 unitless * * Quality of Barker code lock. Unitless. Monotonically * nondecreasing with "better" lock strength. Called "Signal * Quality" in datasheets. (Is there a standard way to measure * this?) * * IEEE80211_RADIOTAP_TX_ATTENUATION __le16 unitless * * Transmit power expressed as unitless distance from max * power set at factory calibration. 0 is max power. * Monotonically nondecreasing with lower power levels. * * IEEE80211_RADIOTAP_DB_TX_ATTENUATION __le16 decibels (dB) * * Transmit power expressed as decibel distance from max power * set at factory calibration. 0 is max power. Monotonically * nondecreasing with lower power levels. * * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from * one milliwatt (dBm) * * Transmit power expressed as dBm (decibels from a 1 milliwatt * reference). This is the absolute power level measured at * the antenna port. * * IEEE80211_RADIOTAP_FLAGS u8 bitmap * * Properties of transmitted and received frames. See flags * defined below. * * IEEE80211_RADIOTAP_ANTENNA u8 antenna index * * Unitless indication of the Rx/Tx antenna for this packet. * The first antenna is antenna 0. * * IEEE80211_RADIOTAP_RX_FLAGS __le16 bitmap * * Properties of received frames. See flags defined below. * * IEEE80211_RADIOTAP_TX_FLAGS __le16 bitmap * * Properties of transmitted frames. See flags defined below. * * IEEE80211_RADIOTAP_RTS_RETRIES u8 data * * Number of rts retries a transmitted frame used. * * IEEE80211_RADIOTAP_DATA_RETRIES u8 data * * Number of unicast retries a transmitted frame used. * * IEEE80211_RADIOTAP_MCS u8, u8, u8 unitless * * Contains a bitmap of known fields/flags, the flags, and * the MCS index. * * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless * * Contains the AMPDU information for the subframe. */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, IEEE80211_RADIOTAP_FLAGS = 1, IEEE80211_RADIOTAP_RATE = 2, IEEE80211_RADIOTAP_CHANNEL = 3, IEEE80211_RADIOTAP_FHSS = 4, IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, IEEE80211_RADIOTAP_LOCK_QUALITY = 7, IEEE80211_RADIOTAP_TX_ATTENUATION = 8, IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, IEEE80211_RADIOTAP_DBM_TX_POWER = 10, IEEE80211_RADIOTAP_ANTENNA = 11, IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, IEEE80211_RADIOTAP_DB_ANTNOISE = 13, IEEE80211_RADIOTAP_RX_FLAGS = 14, IEEE80211_RADIOTAP_TX_FLAGS = 15, IEEE80211_RADIOTAP_RTS_RETRIES = 16, IEEE80211_RADIOTAP_DATA_RETRIES = 17, IEEE80211_RADIOTAP_MCS = 19, IEEE80211_RADIOTAP_AMPDU_STATUS = 20, /* valid in every it_present bitmap, even vendor namespaces */ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, IEEE80211_RADIOTAP_EXT = 31 }; /* Channel flags. */ #define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ #define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ #define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ #define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ #define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ /* For IEEE80211_RADIOTAP_FLAGS */ #define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received * during CFP */ #define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received * with short * preamble */ #define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received * with WEP encryption */ #define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received * with fragmentation */ #define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ #define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between * 802.11 header and payload * (to 32-bit boundary) */ #define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* bad FCS */ /* For IEEE80211_RADIOTAP_RX_FLAGS */ #define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* frame has bad PLCP */ /* For IEEE80211_RADIOTAP_TX_FLAGS */ #define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ #define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ack */ /* For IEEE80211_RADIOTAP_MCS */ #define IEEE80211_RADIOTAP_MCS_HAVE_BW 0x01 #define IEEE80211_RADIOTAP_MCS_HAVE_MCS 0x02 #define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04 #define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08 #define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10 #define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03 #define IEEE80211_RADIOTAP_MCS_BW_20 0 #define IEEE80211_RADIOTAP_MCS_BW_40 1 #define IEEE80211_RADIOTAP_MCS_BW_20L 2 #define IEEE80211_RADIOTAP_MCS_BW_20U 3 #define IEEE80211_RADIOTAP_MCS_SGI 0x04 #define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 #define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 /* For IEEE80211_RADIOTAP_AMPDU_STATUS */ #define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001 #define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002 #define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004 #define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008 #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 /* helpers */ static inline int ieee80211_get_radiotap_len(unsigned char *data) { struct ieee80211_radiotap_header *hdr = (struct ieee80211_radiotap_header *)data; return get_unaligned_le16(&hdr->it_len); } #endif /* IEEE80211_RADIOTAP_H */ compat-drivers-2012-09-18/include/net/cfg80211-wext.h0000644000175000017500000000372012026211315021064 0ustar mcgrofmcgrof#ifndef __NET_CFG80211_WEXT_H #define __NET_CFG80211_WEXT_H /* * 802.11 device and configuration interface -- wext handlers * * Copyright 2006-2010 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include /* * Temporary wext handlers & helper functions * * These are used only by drivers that aren't yet fully * converted to cfg80211. */ int cfg80211_wext_giwname(struct net_device *dev, struct iw_request_info *info, char *name, char *extra); int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra); int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra); int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); int cfg80211_wext_giwscan(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra); int cfg80211_wext_giwrange(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra); int cfg80211_wext_siwrts(struct net_device *dev, struct iw_request_info *info, struct iw_param *rts, char *extra); int cfg80211_wext_giwrts(struct net_device *dev, struct iw_request_info *info, struct iw_param *rts, char *extra); int cfg80211_wext_siwfrag(struct net_device *dev, struct iw_request_info *info, struct iw_param *frag, char *extra); int cfg80211_wext_giwfrag(struct net_device *dev, struct iw_request_info *info, struct iw_param *frag, char *extra); int cfg80211_wext_giwretry(struct net_device *dev, struct iw_request_info *info, struct iw_param *retry, char *extra); #endif /* __NET_CFG80211_WEXT_H */ compat-drivers-2012-09-18/include/net/codel.h0000644000175000017500000002574612025673354020064 0ustar mcgrofmcgrof#include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) || (defined(TCA_CODEL_MAX) && !defined(COMPAT_CODEL_BACKPORT)) #include_next #else #ifndef __NET_SCHED_CODEL_H #define __NET_SCHED_CODEL_H /* * Codel - The Controlled-Delay Active Queue Management algorithm * * Copyright (C) 2011-2012 Kathleen Nichols * Copyright (C) 2011-2012 Van Jacobson * Copyright (C) 2012 Michael D. Taht * Copyright (C) 2012 Eric Dumazet * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, provided that this notice is retained in full, this * software may be distributed under the terms of the GNU General * Public License ("GPL") version 2, in which case the provisions of the * GPL apply INSTEAD OF those given above. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * */ #include #include #include #include #include #include /* Controlling Queue Delay (CoDel) algorithm * ========================================= * Source : Kathleen Nichols and Van Jacobson * http://queue.acm.org/detail.cfm?id=2209336 * * Implemented on linux by Dave Taht and Eric Dumazet */ /* CoDel uses a 1024 nsec clock, encoded in u32 * This gives a range of 2199 seconds, because of signed compares */ typedef u32 codel_time_t; typedef s32 codel_tdiff_t; #define CODEL_SHIFT 10 #define MS2TIME(a) ((a * NSEC_PER_MSEC) >> CODEL_SHIFT) static inline codel_time_t codel_get_time(void) { u64 ns = ktime_to_ns(ktime_get()); return ns >> CODEL_SHIFT; } #define codel_time_after(a, b) ((s32)(a) - (s32)(b) > 0) #define codel_time_after_eq(a, b) ((s32)(a) - (s32)(b) >= 0) #define codel_time_before(a, b) ((s32)(a) - (s32)(b) < 0) #define codel_time_before_eq(a, b) ((s32)(a) - (s32)(b) <= 0) /* Qdiscs using codel plugin must use codel_skb_cb in their own cb[] */ struct codel_skb_cb { codel_time_t enqueue_time; }; static struct codel_skb_cb *get_codel_cb(const struct sk_buff *skb) { qdisc_cb_private_validate(skb, sizeof(struct codel_skb_cb)); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37)) return (struct codel_skb_cb *)qdisc_skb_cb((struct sk_buff *) skb)->data; #else return (struct codel_skb_cb *)qdisc_skb_cb(skb)->data; #endif } static codel_time_t codel_get_enqueue_time(const struct sk_buff *skb) { return get_codel_cb(skb)->enqueue_time; } static void codel_set_enqueue_time(struct sk_buff *skb) { get_codel_cb(skb)->enqueue_time = codel_get_time(); } static inline u32 codel_time_to_us(codel_time_t val) { u64 valns = ((u64)val << CODEL_SHIFT); do_div(valns, NSEC_PER_USEC); return (u32)valns; } /** * struct codel_params - contains codel parameters * @target: target queue size (in time units) * @interval: width of moving time window * @ecn: is Explicit Congestion Notification enabled */ struct codel_params { codel_time_t target; codel_time_t interval; bool ecn; }; /** * struct codel_vars - contains codel variables * @count: how many drops we've done since the last time we * entered dropping state * @lastcount: count at entry to dropping state * @dropping: set to true if in dropping state * @rec_inv_sqrt: reciprocal value of sqrt(count) >> 1 * @first_above_time: when we went (or will go) continuously above target * for interval * @drop_next: time to drop next packet, or when we dropped last * @ldelay: sojourn time of last dequeued packet */ struct codel_vars { u32 count; u32 lastcount; bool dropping; u16 rec_inv_sqrt; codel_time_t first_above_time; codel_time_t drop_next; codel_time_t ldelay; }; #define REC_INV_SQRT_BITS (8 * sizeof(u16)) /* or sizeof_in_bits(rec_inv_sqrt) */ /* needed shift to get a Q0.32 number from rec_inv_sqrt */ #define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS) /** * struct codel_stats - contains codel shared variables and stats * @maxpacket: largest packet we've seen so far * @drop_count: temp count of dropped packets in dequeue() * ecn_mark: number of packets we ECN marked instead of dropping */ struct codel_stats { u32 maxpacket; u32 drop_count; u32 ecn_mark; }; static void codel_params_init(struct codel_params *params) { params->interval = MS2TIME(100); params->target = MS2TIME(5); params->ecn = false; } static void codel_vars_init(struct codel_vars *vars) { memset(vars, 0, sizeof(*vars)); } static void codel_stats_init(struct codel_stats *stats) { stats->maxpacket = 256; } /* * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) * * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32 */ static void codel_Newton_step(struct codel_vars *vars) { u32 invsqrt = ((u32)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT; u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32; u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2); val >>= 2; /* avoid overflow in following multiply */ val = (val * invsqrt) >> (32 - 2 + 1); vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT; } /* * CoDel control_law is t + interval/sqrt(count) * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid * both sqrt() and divide operation. */ static codel_time_t codel_control_law(codel_time_t t, codel_time_t interval, u32 rec_inv_sqrt) { return t + reciprocal_divide(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT); } static bool codel_should_drop(const struct sk_buff *skb, struct Qdisc *sch, struct codel_vars *vars, struct codel_params *params, struct codel_stats *stats, codel_time_t now) { bool ok_to_drop; if (!skb) { vars->first_above_time = 0; return false; } vars->ldelay = now - codel_get_enqueue_time(skb); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37)) sch->qstats.backlog -= qdisc_pkt_len((struct sk_buff *)skb); #else sch->qstats.backlog -= qdisc_pkt_len(skb); #endif #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37)) if (unlikely(qdisc_pkt_len((struct sk_buff *)skb) > stats->maxpacket)) stats->maxpacket = qdisc_pkt_len((struct sk_buff *)skb); #else if (unlikely(qdisc_pkt_len(skb) > stats->maxpacket)) stats->maxpacket = qdisc_pkt_len(skb); #endif if (codel_time_before(vars->ldelay, params->target) || sch->qstats.backlog <= stats->maxpacket) { /* went below - stay below for at least interval */ vars->first_above_time = 0; return false; } ok_to_drop = false; if (vars->first_above_time == 0) { /* just went above from below. If we stay above * for at least interval we'll say it's ok to drop */ vars->first_above_time = now + params->interval; } else if (codel_time_after(now, vars->first_above_time)) { ok_to_drop = true; } return ok_to_drop; } typedef struct sk_buff * (*codel_skb_dequeue_t)(struct codel_vars *vars, struct Qdisc *sch); static struct sk_buff *codel_dequeue(struct Qdisc *sch, struct codel_params *params, struct codel_vars *vars, struct codel_stats *stats, codel_skb_dequeue_t dequeue_func) { struct sk_buff *skb = dequeue_func(vars, sch); codel_time_t now; bool drop; if (!skb) { vars->dropping = false; return skb; } now = codel_get_time(); drop = codel_should_drop(skb, sch, vars, params, stats, now); if (vars->dropping) { if (!drop) { /* sojourn time below target - leave dropping state */ vars->dropping = false; } else if (codel_time_after_eq(now, vars->drop_next)) { /* It's time for the next drop. Drop the current * packet and dequeue the next. The dequeue might * take us out of dropping state. * If not, schedule the next drop. * A large backlog might result in drop rates so high * that the next drop should happen now, * hence the while loop. */ while (vars->dropping && codel_time_after_eq(now, vars->drop_next)) { vars->count++; /* dont care of possible wrap * since there is no more divide */ codel_Newton_step(vars); if (params->ecn && INET_ECN_set_ce(skb)) { stats->ecn_mark++; vars->drop_next = codel_control_law(vars->drop_next, params->interval, vars->rec_inv_sqrt); goto end; } qdisc_drop(skb, sch); stats->drop_count++; skb = dequeue_func(vars, sch); if (!codel_should_drop(skb, sch, vars, params, stats, now)) { /* leave dropping state */ vars->dropping = false; } else { /* and schedule the next drop */ vars->drop_next = codel_control_law(vars->drop_next, params->interval, vars->rec_inv_sqrt); } } } } else if (drop) { if (params->ecn && INET_ECN_set_ce(skb)) { stats->ecn_mark++; } else { qdisc_drop(skb, sch); stats->drop_count++; skb = dequeue_func(vars, sch); drop = codel_should_drop(skb, sch, vars, params, stats, now); } vars->dropping = true; /* if min went above target close to when we last went below it * assume that the drop rate that controlled the queue on the * last cycle is a good starting point to control it now. */ if (codel_time_before(now - vars->drop_next, 16 * params->interval)) { vars->count = (vars->count - vars->lastcount) | 1; /* we dont care if rec_inv_sqrt approximation * is not very precise : * Next Newton steps will correct it quadratically. */ codel_Newton_step(vars); } else { vars->count = 1; vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT; } vars->lastcount = vars->count; vars->drop_next = codel_control_law(now, params->interval, vars->rec_inv_sqrt); } end: return skb; } #endif #endif compat-drivers-2012-09-18/include/net/lib80211.h0000644000175000017500000001021112026211315020077 0ustar mcgrofmcgrof/* * lib80211.h -- common bits for IEEE802.11 wireless drivers * * Copyright (c) 2008, John W. Linville * * Some bits copied from old ieee80211 component, w/ original copyright * notices below: * * Original code based on Host AP (software wireless LAN access point) driver * for Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen * * Copyright (c) 2002-2003, Jouni Malinen * * Adaption to a generic IEEE 802.11 stack by James Ketrenos * * * Copyright (c) 2004, Intel Corporation * */ #ifndef LIB80211_H #define LIB80211_H #include #include #include #include #include #include #include /* print_ssid() is intended to be used in debug (and possibly error) * messages. It should never be used for passing ssid to user space. */ const char *print_ssid(char *buf, const char *ssid, u8 ssid_len); #define DECLARE_SSID_BUF(var) char var[IEEE80211_MAX_SSID_LEN * 4 + 1] __maybe_unused #define NUM_WEP_KEYS 4 enum { IEEE80211_CRYPTO_TKIP_COUNTERMEASURES = (1 << 0), }; struct module; struct lib80211_crypto_ops { const char *name; struct list_head list; /* init new crypto context (e.g., allocate private data space, * select IV, etc.); returns NULL on failure or pointer to allocated * private data on success */ void *(*init) (int keyidx); /* deinitialize crypto context and free allocated private data */ void (*deinit) (void *priv); /* encrypt/decrypt return < 0 on error or >= 0 on success. The return * value from decrypt_mpdu is passed as the keyidx value for * decrypt_msdu. skb must have enough head and tail room for the * encryption; if not, error will be returned; these functions are * called for all MPDUs (i.e., fragments). */ int (*encrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv); int (*decrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv); /* These functions are called for full MSDUs, i.e. full frames. * These can be NULL if full MSDU operations are not needed. */ int (*encrypt_msdu) (struct sk_buff * skb, int hdr_len, void *priv); int (*decrypt_msdu) (struct sk_buff * skb, int keyidx, int hdr_len, void *priv); int (*set_key) (void *key, int len, u8 * seq, void *priv); int (*get_key) (void *key, int len, u8 * seq, void *priv); /* procfs handler for printing out key information and possible * statistics */ char *(*print_stats) (char *p, void *priv); /* Crypto specific flag get/set for configuration settings */ unsigned long (*get_flags) (void *priv); unsigned long (*set_flags) (unsigned long flags, void *priv); /* maximum number of bytes added by encryption; encrypt buf is * allocated with extra_prefix_len bytes, copy of in_buf, and * extra_postfix_len; encrypt need not use all this space, but * the result must start at the beginning of the buffer and correct * length must be returned */ int extra_mpdu_prefix_len, extra_mpdu_postfix_len; int extra_msdu_prefix_len, extra_msdu_postfix_len; struct module *owner; }; struct lib80211_crypt_data { struct list_head list; /* delayed deletion list */ struct lib80211_crypto_ops *ops; void *priv; atomic_t refcnt; }; struct lib80211_crypt_info { char *name; /* Most clients will already have a lock, so just point to that. */ spinlock_t *lock; struct lib80211_crypt_data *crypt[NUM_WEP_KEYS]; int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */ struct list_head crypt_deinit_list; struct timer_list crypt_deinit_timer; int crypt_quiesced; }; int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, spinlock_t *lock); void lib80211_crypt_info_free(struct lib80211_crypt_info *info); int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops); int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops); struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name); void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, struct lib80211_crypt_data **crypt); #endif /* LIB80211_H */ compat-drivers-2012-09-18/include/net/bluetooth/0000755000175000017500000000000012026211315020576 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/net/bluetooth/rfcomm.h0000644000175000017500000002166112026211315022240 0ustar mcgrofmcgrof/* RFCOMM implementation for Linux Bluetooth stack (BlueZ) Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __RFCOMM_H #define __RFCOMM_H #define RFCOMM_PSM 3 #define RFCOMM_CONN_TIMEOUT (HZ * 30) #define RFCOMM_DISC_TIMEOUT (HZ * 20) #define RFCOMM_AUTH_TIMEOUT (HZ * 25) #define RFCOMM_IDLE_TIMEOUT (HZ * 2) #define RFCOMM_DEFAULT_MTU 127 #define RFCOMM_DEFAULT_CREDITS 7 #define RFCOMM_MAX_L2CAP_MTU 1013 #define RFCOMM_MAX_CREDITS 40 #define RFCOMM_SKB_HEAD_RESERVE 8 #define RFCOMM_SKB_TAIL_RESERVE 2 #define RFCOMM_SKB_RESERVE (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE) #define RFCOMM_SABM 0x2f #define RFCOMM_DISC 0x43 #define RFCOMM_UA 0x63 #define RFCOMM_DM 0x0f #define RFCOMM_UIH 0xef #define RFCOMM_TEST 0x08 #define RFCOMM_FCON 0x28 #define RFCOMM_FCOFF 0x18 #define RFCOMM_MSC 0x38 #define RFCOMM_RPN 0x24 #define RFCOMM_RLS 0x14 #define RFCOMM_PN 0x20 #define RFCOMM_NSC 0x04 #define RFCOMM_V24_FC 0x02 #define RFCOMM_V24_RTC 0x04 #define RFCOMM_V24_RTR 0x08 #define RFCOMM_V24_IC 0x40 #define RFCOMM_V24_DV 0x80 #define RFCOMM_RPN_BR_2400 0x0 #define RFCOMM_RPN_BR_4800 0x1 #define RFCOMM_RPN_BR_7200 0x2 #define RFCOMM_RPN_BR_9600 0x3 #define RFCOMM_RPN_BR_19200 0x4 #define RFCOMM_RPN_BR_38400 0x5 #define RFCOMM_RPN_BR_57600 0x6 #define RFCOMM_RPN_BR_115200 0x7 #define RFCOMM_RPN_BR_230400 0x8 #define RFCOMM_RPN_DATA_5 0x0 #define RFCOMM_RPN_DATA_6 0x1 #define RFCOMM_RPN_DATA_7 0x2 #define RFCOMM_RPN_DATA_8 0x3 #define RFCOMM_RPN_STOP_1 0 #define RFCOMM_RPN_STOP_15 1 #define RFCOMM_RPN_PARITY_NONE 0x0 #define RFCOMM_RPN_PARITY_ODD 0x1 #define RFCOMM_RPN_PARITY_EVEN 0x3 #define RFCOMM_RPN_PARITY_MARK 0x5 #define RFCOMM_RPN_PARITY_SPACE 0x7 #define RFCOMM_RPN_FLOW_NONE 0x00 #define RFCOMM_RPN_XON_CHAR 0x11 #define RFCOMM_RPN_XOFF_CHAR 0x13 #define RFCOMM_RPN_PM_BITRATE 0x0001 #define RFCOMM_RPN_PM_DATA 0x0002 #define RFCOMM_RPN_PM_STOP 0x0004 #define RFCOMM_RPN_PM_PARITY 0x0008 #define RFCOMM_RPN_PM_PARITY_TYPE 0x0010 #define RFCOMM_RPN_PM_XON 0x0020 #define RFCOMM_RPN_PM_XOFF 0x0040 #define RFCOMM_RPN_PM_FLOW 0x3F00 #define RFCOMM_RPN_PM_ALL 0x3F7F struct rfcomm_hdr { u8 addr; u8 ctrl; u8 len; /* Actual size can be 2 bytes */ } __packed; struct rfcomm_cmd { u8 addr; u8 ctrl; u8 len; u8 fcs; } __packed; struct rfcomm_mcc { u8 type; u8 len; } __packed; struct rfcomm_pn { u8 dlci; u8 flow_ctrl; u8 priority; u8 ack_timer; __le16 mtu; u8 max_retrans; u8 credits; } __packed; struct rfcomm_rpn { u8 dlci; u8 bit_rate; u8 line_settings; u8 flow_ctrl; u8 xon_char; u8 xoff_char; __le16 param_mask; } __packed; struct rfcomm_rls { u8 dlci; u8 status; } __packed; struct rfcomm_msc { u8 dlci; u8 v24_sig; } __packed; /* ---- Core structures, flags etc ---- */ struct rfcomm_session { struct list_head list; struct socket *sock; struct timer_list timer; unsigned long state; unsigned long flags; atomic_t refcnt; int initiator; /* Default DLC parameters */ int cfc; uint mtu; struct list_head dlcs; }; struct rfcomm_dlc { struct list_head list; struct rfcomm_session *session; struct sk_buff_head tx_queue; struct timer_list timer; spinlock_t lock; unsigned long state; unsigned long flags; atomic_t refcnt; u8 dlci; u8 addr; u8 priority; u8 v24_sig; u8 remote_v24_sig; u8 mscex; u8 out; u8 sec_level; u8 role_switch; u32 defer_setup; uint mtu; uint cfc; uint rx_credits; uint tx_credits; void *owner; void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb); void (*state_change)(struct rfcomm_dlc *d, int err); void (*modem_status)(struct rfcomm_dlc *d, u8 v24_sig); }; /* DLC and session flags */ #define RFCOMM_RX_THROTTLED 0 #define RFCOMM_TX_THROTTLED 1 #define RFCOMM_TIMED_OUT 2 #define RFCOMM_MSC_PENDING 3 #define RFCOMM_SEC_PENDING 4 #define RFCOMM_AUTH_PENDING 5 #define RFCOMM_AUTH_ACCEPT 6 #define RFCOMM_AUTH_REJECT 7 #define RFCOMM_DEFER_SETUP 8 #define RFCOMM_ENC_DROP 9 /* Scheduling flags and events */ #define RFCOMM_SCHED_WAKEUP 31 /* MSC exchange flags */ #define RFCOMM_MSCEX_TX 1 #define RFCOMM_MSCEX_RX 2 #define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX) /* CFC states */ #define RFCOMM_CFC_UNKNOWN -1 #define RFCOMM_CFC_DISABLED 0 #define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS /* ---- RFCOMM SEND RPN ---- */ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, u8 bit_rate, u8 data_bits, u8 stop_bits, u8 parity, u8 flow_ctrl_settings, u8 xon_char, u8 xoff_char, u16 param_mask); /* ---- RFCOMM DLCs (channels) ---- */ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio); void rfcomm_dlc_free(struct rfcomm_dlc *d); int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel); int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); void rfcomm_dlc_accept(struct rfcomm_dlc *d); #define rfcomm_dlc_lock(d) spin_lock(&d->lock) #define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) { atomic_inc(&d->refcnt); } static inline void rfcomm_dlc_put(struct rfcomm_dlc *d) { if (atomic_dec_and_test(&d->refcnt)) rfcomm_dlc_free(d); } extern void __rfcomm_dlc_throttle(struct rfcomm_dlc *d); extern void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d); static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d) { if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags)) __rfcomm_dlc_throttle(d); } static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) { if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags)) __rfcomm_dlc_unthrottle(d); } /* ---- RFCOMM sessions ---- */ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst); static inline void rfcomm_session_hold(struct rfcomm_session *s) { atomic_inc(&s->refcnt); } /* ---- RFCOMM sockets ---- */ struct sockaddr_rc { sa_family_t rc_family; bdaddr_t rc_bdaddr; u8 rc_channel; }; #define RFCOMM_CONNINFO 0x02 struct rfcomm_conninfo { __u16 hci_handle; __u8 dev_class[3]; }; #define RFCOMM_LM 0x03 #define RFCOMM_LM_MASTER 0x0001 #define RFCOMM_LM_AUTH 0x0002 #define RFCOMM_LM_ENCRYPT 0x0004 #define RFCOMM_LM_TRUSTED 0x0008 #define RFCOMM_LM_RELIABLE 0x0010 #define RFCOMM_LM_SECURE 0x0020 #define rfcomm_pi(sk) ((struct rfcomm_pinfo *) sk) struct rfcomm_pinfo { struct bt_sock bt; struct rfcomm_dlc *dlc; u8 channel; u8 sec_level; u8 role_switch; }; int rfcomm_init_sockets(void); void rfcomm_cleanup_sockets(void); int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d); /* ---- RFCOMM TTY ---- */ #define RFCOMM_MAX_DEV 256 #define RFCOMMCREATEDEV _IOW('R', 200, int) #define RFCOMMRELEASEDEV _IOW('R', 201, int) #define RFCOMMGETDEVLIST _IOR('R', 210, int) #define RFCOMMGETDEVINFO _IOR('R', 211, int) #define RFCOMMSTEALDLC _IOW('R', 220, int) #define RFCOMM_REUSE_DLC 0 #define RFCOMM_RELEASE_ONHUP 1 #define RFCOMM_HANGUP_NOW 2 #define RFCOMM_TTY_ATTACHED 3 #define RFCOMM_TTY_RELEASED 4 struct rfcomm_dev_req { s16 dev_id; u32 flags; bdaddr_t src; bdaddr_t dst; u8 channel; }; struct rfcomm_dev_info { s16 id; u32 flags; u16 state; bdaddr_t src; bdaddr_t dst; u8 channel; }; struct rfcomm_dev_list_req { u16 dev_num; struct rfcomm_dev_info dev_info[0]; }; int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); #ifdef CONFIG_COMPAT_BT_RFCOMM_TTY int rfcomm_init_ttys(void); void rfcomm_cleanup_ttys(void); #else static inline int rfcomm_init_ttys(void) { return 0; } static inline void rfcomm_cleanup_ttys(void) { } #endif #endif /* __RFCOMM_H */ compat-drivers-2012-09-18/include/net/bluetooth/a2mp.h0000644000175000017500000000541512026211315021613 0ustar mcgrofmcgrof/* Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved. Copyright (c) 2011,2012 Intel Corp. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 and only version 2 as published by the Free Software Foundation. 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 __A2MP_H #define __A2MP_H #include #define A2MP_FEAT_EXT 0x8000 struct amp_mgr { struct l2cap_conn *l2cap_conn; struct l2cap_chan *a2mp_chan; struct kref kref; __u8 ident; __u8 handle; unsigned long flags; }; struct a2mp_cmd { __u8 code; __u8 ident; __le16 len; __u8 data[0]; } __packed; /* A2MP command codes */ #define A2MP_COMMAND_REJ 0x01 struct a2mp_cmd_rej { __le16 reason; __u8 data[0]; } __packed; #define A2MP_DISCOVER_REQ 0x02 struct a2mp_discov_req { __le16 mtu; __le16 ext_feat; } __packed; struct a2mp_cl { __u8 id; __u8 type; __u8 status; } __packed; #define A2MP_DISCOVER_RSP 0x03 struct a2mp_discov_rsp { __le16 mtu; __le16 ext_feat; struct a2mp_cl cl[0]; } __packed; #define A2MP_CHANGE_NOTIFY 0x04 #define A2MP_CHANGE_RSP 0x05 #define A2MP_GETINFO_REQ 0x06 struct a2mp_info_req { __u8 id; } __packed; #define A2MP_GETINFO_RSP 0x07 struct a2mp_info_rsp { __u8 id; __u8 status; __le32 total_bw; __le32 max_bw; __le32 min_latency; __le16 pal_cap; __le16 assoc_size; } __packed; #define A2MP_GETAMPASSOC_REQ 0x08 struct a2mp_amp_assoc_req { __u8 id; } __packed; #define A2MP_GETAMPASSOC_RSP 0x09 struct a2mp_amp_assoc_rsp { __u8 id; __u8 status; __u8 amp_assoc[0]; } __packed; #define A2MP_CREATEPHYSLINK_REQ 0x0A #define A2MP_DISCONNPHYSLINK_REQ 0x0C struct a2mp_physlink_req { __u8 local_id; __u8 remote_id; __u8 amp_assoc[0]; } __packed; #define A2MP_CREATEPHYSLINK_RSP 0x0B #define A2MP_DISCONNPHYSLINK_RSP 0x0D struct a2mp_physlink_rsp { __u8 local_id; __u8 remote_id; __u8 status; } __packed; /* A2MP response status */ #define A2MP_STATUS_SUCCESS 0x00 #define A2MP_STATUS_INVALID_CTRL_ID 0x01 #define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02 #define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02 #define A2MP_STATUS_COLLISION_OCCURED 0x03 #define A2MP_STATUS_DISCONN_REQ_RECVD 0x04 #define A2MP_STATUS_PHYS_LINK_EXISTS 0x05 #define A2MP_STATUS_SECURITY_VIOLATION 0x06 void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); #endif /* __A2MP_H */ compat-drivers-2012-09-18/include/net/bluetooth/hci_core.h0000644000175000017500000007044512026211315022534 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __HCI_CORE_H #define __HCI_CORE_H #include /* HCI priority */ #define HCI_PRIO_MAX 7 /* HCI Core structures */ struct inquiry_data { bdaddr_t bdaddr; __u8 pscan_rep_mode; __u8 pscan_period_mode; __u8 pscan_mode; __u8 dev_class[3]; __le16 clock_offset; __s8 rssi; __u8 ssp_mode; }; struct inquiry_entry { struct list_head all; /* inq_cache.all */ struct list_head list; /* unknown or resolve */ enum { NAME_NOT_KNOWN, NAME_NEEDED, NAME_PENDING, NAME_KNOWN, } name_state; __u32 timestamp; struct inquiry_data data; }; struct discovery_state { int type; enum { DISCOVERY_STOPPED, DISCOVERY_STARTING, DISCOVERY_FINDING, DISCOVERY_RESOLVING, DISCOVERY_STOPPING, } state; struct list_head all; /* All devices found during inquiry */ struct list_head unknown; /* Name state not known */ struct list_head resolve; /* Name needs to be resolved */ __u32 timestamp; }; struct hci_conn_hash { struct list_head list; unsigned int acl_num; unsigned int sco_num; unsigned int le_num; }; struct bdaddr_list { struct list_head list; bdaddr_t bdaddr; }; struct bt_uuid { struct list_head list; u8 uuid[16]; u8 svc_hint; }; struct smp_ltk { struct list_head list; bdaddr_t bdaddr; u8 bdaddr_type; u8 authenticated; u8 type; u8 enc_size; __le16 ediv; u8 rand[8]; u8 val[16]; } __packed; struct link_key { struct list_head list; bdaddr_t bdaddr; u8 type; u8 val[HCI_LINK_KEY_SIZE]; u8 pin_len; }; struct oob_data { struct list_head list; bdaddr_t bdaddr; u8 hash[16]; u8 randomizer[16]; }; struct le_scan_params { u8 type; u16 interval; u16 window; int timeout; }; #define HCI_MAX_SHORT_NAME_LENGTH 10 #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; struct mutex lock; char name[8]; unsigned long flags; __u16 id; __u8 bus; __u8 dev_type; bdaddr_t bdaddr; __u8 dev_name[HCI_MAX_NAME_LENGTH]; __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH]; __u8 dev_class[3]; __u8 major_class; __u8 minor_class; __u8 features[8]; __u8 host_features[8]; __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; __u8 lmp_ver; __u16 manufacturer; __u16 lmp_subver; __u16 voice_setting; __u8 io_capability; __s8 inq_tx_power; __u16 devid_source; __u16 devid_vendor; __u16 devid_product; __u16 devid_version; __u16 pkt_type; __u16 esco_type; __u16 link_policy; __u16 link_mode; __u32 idle_timeout; __u16 sniff_min_interval; __u16 sniff_max_interval; __u8 amp_status; __u32 amp_total_bw; __u32 amp_max_bw; __u32 amp_min_latency; __u32 amp_max_pdu; __u8 amp_type; __u16 amp_pal_cap; __u16 amp_assoc_size; __u32 amp_max_flush_to; __u32 amp_be_flush_to; __u8 flow_ctl_mode; unsigned int auto_accept_delay; unsigned long quirks; atomic_t cmd_cnt; unsigned int acl_cnt; unsigned int sco_cnt; unsigned int le_cnt; unsigned int acl_mtu; unsigned int sco_mtu; unsigned int le_mtu; unsigned int acl_pkts; unsigned int sco_pkts; unsigned int le_pkts; __u16 block_len; __u16 block_mtu; __u16 num_blocks; __u16 block_cnt; unsigned long acl_last_tx; unsigned long sco_last_tx; unsigned long le_last_tx; struct workqueue_struct *workqueue; struct work_struct power_on; struct delayed_work power_off; __u16 discov_timeout; struct delayed_work discov_off; struct delayed_work service_cache; struct timer_list cmd_timer; struct work_struct rx_work; struct work_struct cmd_work; struct work_struct tx_work; struct sk_buff_head rx_q; struct sk_buff_head raw_q; struct sk_buff_head cmd_q; struct sk_buff *sent_cmd; struct sk_buff *reassembly[NUM_REASSEMBLY]; struct mutex req_lock; wait_queue_head_t req_wait_q; __u32 req_status; __u32 req_result; __u16 init_last_cmd; struct list_head mgmt_pending; struct discovery_state discovery; struct hci_conn_hash conn_hash; struct list_head blacklist; struct list_head uuids; struct list_head link_keys; struct list_head long_term_keys; struct list_head remote_oob_data; struct hci_dev_stats stat; struct sk_buff_head driver_init; void *core_data; atomic_t promisc; struct dentry *debugfs; struct device dev; struct rfkill *rfkill; unsigned long dev_flags; struct delayed_work le_scan_disable; struct work_struct le_scan; struct le_scan_params le_scan_params; int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); int (*send)(struct sk_buff *skb); void (*notify)(struct hci_dev *hdev, unsigned int evt); int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); }; struct hci_conn { struct list_head list; atomic_t refcnt; bdaddr_t dst; __u8 dst_type; __u16 handle; __u16 state; __u8 mode; __u8 type; bool out; __u8 attempt; __u8 dev_class[3]; __u8 features[8]; __u16 interval; __u16 pkt_type; __u16 link_policy; __u32 link_mode; __u8 key_type; __u8 auth_type; __u8 sec_level; __u8 pending_sec_level; __u8 pin_length; __u8 enc_key_size; __u8 io_capability; __u16 disc_timeout; unsigned long flags; __u8 remote_cap; __u8 remote_auth; bool flush_key; unsigned int sent; struct sk_buff_head data_q; struct list_head chan_list; struct delayed_work disc_work; struct timer_list idle_timer; struct timer_list auto_accept_timer; struct device dev; atomic_t devref; struct hci_dev *hdev; void *l2cap_data; void *sco_data; void *smp_conn; struct amp_mgr *amp_mgr; struct hci_conn *link; void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); void (*security_cfm_cb) (struct hci_conn *conn, u8 status); void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason); }; struct hci_chan { struct list_head list; struct hci_conn *conn; struct sk_buff_head data_q; unsigned int sent; }; extern struct list_head hci_dev_list; extern struct list_head hci_cb_list; extern rwlock_t hci_dev_list_lock; extern rwlock_t hci_cb_list_lock; /* ----- HCI interface to upper protocols ----- */ extern int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); extern void l2cap_connect_cfm(struct hci_conn *hcon, u8 status); extern int l2cap_disconn_ind(struct hci_conn *hcon); extern void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status); extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); /* ----- Inquiry cache ----- */ #define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */ #define INQUIRY_ENTRY_AGE_MAX (HZ*60) /* 60 seconds */ static inline void discovery_init(struct hci_dev *hdev) { hdev->discovery.state = DISCOVERY_STOPPED; INIT_LIST_HEAD(&hdev->discovery.all); INIT_LIST_HEAD(&hdev->discovery.unknown); INIT_LIST_HEAD(&hdev->discovery.resolve); } bool hci_discovery_active(struct hci_dev *hdev); void hci_discovery_set_state(struct hci_dev *hdev, int state); static inline int inquiry_cache_empty(struct hci_dev *hdev) { return list_empty(&hdev->discovery.all); } static inline long inquiry_cache_age(struct hci_dev *hdev) { struct discovery_state *c = &hdev->discovery; return jiffies - c->timestamp; } static inline long inquiry_entry_age(struct inquiry_entry *e) { return jiffies - e->timestamp; } struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, bdaddr_t *bdaddr); struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, bdaddr_t *bdaddr, int state); void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, struct inquiry_entry *ie); bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known, bool *ssp); /* ----- HCI Connections ----- */ enum { HCI_CONN_AUTH_PEND, HCI_CONN_REAUTH_PEND, HCI_CONN_ENCRYPT_PEND, HCI_CONN_RSWITCH_PEND, HCI_CONN_MODE_CHANGE_PEND, HCI_CONN_SCO_SETUP_PEND, HCI_CONN_LE_SMP_PEND, HCI_CONN_MGMT_CONNECTED, HCI_CONN_SSP_ENABLED, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; return test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && test_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; list_add_rcu(&c->list, &h->list); switch (c->type) { case ACL_LINK: h->acl_num++; break; case LE_LINK: h->le_num++; break; case SCO_LINK: case ESCO_LINK: h->sco_num++; break; } } static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; list_del_rcu(&c->list); synchronize_rcu(); switch (c->type) { case ACL_LINK: h->acl_num--; break; case LE_LINK: h->le_num--; break; case SCO_LINK: case ESCO_LINK: h->sco_num--; break; } } static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) { struct hci_conn_hash *h = &hdev->conn_hash; switch (type) { case ACL_LINK: return h->acl_num; case LE_LINK: return h->le_num; case SCO_LINK: case ESCO_LINK: return h->sco_num; default: return 0; } } static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, __u16 handle) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { if (c->handle == handle) { rcu_read_unlock(); return c; } } rcu_read_unlock(); return NULL; } static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev, __u8 type, bdaddr_t *ba) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { if (c->type == type && !bacmp(&c->dst, ba)) { rcu_read_unlock(); return c; } } rcu_read_unlock(); return NULL; } static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, __u8 type, __u16 state) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { if (c->type == type && c->state == state) { rcu_read_unlock(); return c; } } rcu_read_unlock(); return NULL; } void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); struct hci_chan *hci_chan_create(struct hci_conn *conn); void hci_chan_del(struct hci_chan *chan); void hci_chan_list_flush(struct hci_conn *conn); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); int hci_conn_change_link_key(struct hci_conn *conn); int hci_conn_switch_role(struct hci_conn *conn, __u8 role); void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active); void hci_conn_hold_device(struct hci_conn *conn); void hci_conn_put_device(struct hci_conn *conn); static inline void hci_conn_hold(struct hci_conn *conn) { BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt)); atomic_inc(&conn->refcnt); cancel_delayed_work(&conn->disc_work); } static inline void hci_conn_put(struct hci_conn *conn) { BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt)); if (atomic_dec_and_test(&conn->refcnt)) { unsigned long timeo; if (conn->type == ACL_LINK || conn->type == LE_LINK) { del_timer(&conn->idle_timer); if (conn->state == BT_CONNECTED) { timeo = conn->disc_timeout; if (!conn->out) timeo *= 2; } else { timeo = msecs_to_jiffies(10); } } else { timeo = msecs_to_jiffies(10); } cancel_delayed_work(&conn->disc_work); queue_delayed_work(conn->hdev->workqueue, &conn->disc_work, timeo); } } /* ----- HCI Devices ----- */ static inline void hci_dev_put(struct hci_dev *d) { BT_DBG("%s orig refcnt %d", d->name, atomic_read(&d->dev.kobj.kref.refcount)); put_device(&d->dev); } static inline struct hci_dev *hci_dev_hold(struct hci_dev *d) { BT_DBG("%s orig refcnt %d", d->name, atomic_read(&d->dev.kobj.kref.refcount)); get_device(&d->dev); return d; } #define hci_dev_lock(d) mutex_lock(&d->lock) #define hci_dev_unlock(d) mutex_unlock(&d->lock) #define to_hci_dev(d) container_of(d, struct hci_dev, dev) #define to_hci_conn(c) container_of(c, struct hci_conn, dev) static inline void *hci_get_drvdata(struct hci_dev *hdev) { return dev_get_drvdata(&hdev->dev); } static inline void hci_set_drvdata(struct hci_dev *hdev, void *data) { dev_set_drvdata(&hdev->dev, data); } /* hci_dev_list shall be locked */ static inline uint8_t __hci_num_ctrl(void) { uint8_t count = 0; struct list_head *p; list_for_each(p, &hci_dev_list) { count++; } return count; } struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); struct hci_dev *hci_alloc_dev(void); void hci_free_dev(struct hci_dev *hdev); int hci_register_dev(struct hci_dev *hdev); void hci_unregister_dev(struct hci_dev *hdev); int hci_suspend_dev(struct hci_dev *hdev); int hci_resume_dev(struct hci_dev *hdev); int hci_dev_open(__u16 dev); int hci_dev_close(__u16 dev); int hci_dev_reset(__u16 dev); int hci_dev_reset_stat(__u16 dev); int hci_dev_cmd(unsigned int cmd, void __user *arg); int hci_get_dev_list(void __user *arg); int hci_get_dev_info(void __user *arg); int hci_get_conn_list(void __user *arg); int hci_get_conn_info(struct hci_dev *hdev, void __user *arg); int hci_get_auth_info(struct hci_dev *hdev, void __user *arg); int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_blacklist_clear(struct hci_dev *hdev); int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_uuids_clear(struct hci_dev *hdev); int hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, int new_key, u8 authenticated, u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_remote_oob_data_clear(struct hci_dev *hdev); struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, u8 *randomizer); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct sk_buff *skb); int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); void hci_init_sysfs(struct hci_dev *hdev); int hci_add_sysfs(struct hci_dev *hdev); void hci_del_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); void hci_conn_add_sysfs(struct hci_conn *conn); void hci_conn_del_sysfs(struct hci_conn *conn); #define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev)) /* ----- LMP capabilities ----- */ #define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH) #define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT) #define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF) #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) #define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) #define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) #define lmp_bredr_capable(dev) (!((dev)->features[4] & LMP_NO_BREDR)) /* ----- Extended LMP capabilities ----- */ #define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE) /* ----- HCI protocols ----- */ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) { switch (type) { case ACL_LINK: return l2cap_connect_ind(hdev, bdaddr); case SCO_LINK: case ESCO_LINK: return sco_connect_ind(hdev, bdaddr); default: BT_ERR("unknown link type %d", type); return -EINVAL; } } static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) { switch (conn->type) { case ACL_LINK: case LE_LINK: l2cap_connect_cfm(conn, status); break; case SCO_LINK: case ESCO_LINK: sco_connect_cfm(conn, status); break; default: BT_ERR("unknown link type %d", conn->type); break; } if (conn->connect_cfm_cb) conn->connect_cfm_cb(conn, status); } static inline int hci_proto_disconn_ind(struct hci_conn *conn) { if (conn->type != ACL_LINK && conn->type != LE_LINK) return HCI_ERROR_REMOTE_USER_TERM; return l2cap_disconn_ind(conn); } static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) { switch (conn->type) { case ACL_LINK: case LE_LINK: l2cap_disconn_cfm(conn, reason); break; case SCO_LINK: case ESCO_LINK: sco_disconn_cfm(conn, reason); break; default: BT_ERR("unknown link type %d", conn->type); break; } if (conn->disconn_cfm_cb) conn->disconn_cfm_cb(conn, reason); } static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) { __u8 encrypt; if (conn->type != ACL_LINK && conn->type != LE_LINK) return; if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; l2cap_security_cfm(conn, status, encrypt); if (conn->security_cfm_cb) conn->security_cfm_cb(conn, status); } static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) { if (conn->type != ACL_LINK && conn->type != LE_LINK) return; l2cap_security_cfm(conn, status, encrypt); if (conn->security_cfm_cb) conn->security_cfm_cb(conn, status); } /* ----- HCI callbacks ----- */ struct hci_cb { struct list_head list; char *name; void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); void (*key_change_cfm) (struct hci_conn *conn, __u8 status); void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); }; static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) { struct list_head *p; __u8 encrypt; hci_proto_auth_cfm(conn, status); if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; read_lock(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); if (cb->security_cfm) cb->security_cfm(conn, status, encrypt); } read_unlock(&hci_cb_list_lock); } static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) { struct list_head *p; if (conn->sec_level == BT_SECURITY_SDP) conn->sec_level = BT_SECURITY_LOW; if (conn->pending_sec_level > conn->sec_level) conn->sec_level = conn->pending_sec_level; hci_proto_encrypt_cfm(conn, status, encrypt); read_lock(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); if (cb->security_cfm) cb->security_cfm(conn, status, encrypt); } read_unlock(&hci_cb_list_lock); } static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) { struct list_head *p; read_lock(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); if (cb->key_change_cfm) cb->key_change_cfm(conn, status); } read_unlock(&hci_cb_list_lock); } static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, __u8 role) { struct list_head *p; read_lock(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); if (cb->role_switch_cfm) cb->role_switch_cfm(conn, status, role); } read_unlock(&hci_cb_list_lock); } static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) { size_t parsed = 0; if (data_len < 2) return false; while (parsed < data_len - 1) { u8 field_len = data[0]; if (field_len == 0) break; parsed += field_len + 1; if (parsed > data_len) break; if (data[1] == type) return true; data += field_len + 1; } return false; } static inline size_t eir_get_length(u8 *eir, size_t eir_len) { size_t parsed = 0; while (parsed < eir_len) { u8 field_len = eir[0]; if (field_len == 0) return parsed; parsed += field_len + 1; eir += field_len + 1; } return eir_len; } static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len) { eir[eir_len++] = sizeof(type) + data_len; eir[eir_len++] = type; memcpy(&eir[eir_len], data, data_len); eir_len += data_len; return eir_len; } int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); /* Management interface */ #define DISCOV_TYPE_BREDR (BIT(BDADDR_BREDR)) #define DISCOV_TYPE_LE (BIT(BDADDR_LE_PUBLIC) | \ BIT(BDADDR_LE_RANDOM)) #define DISCOV_TYPE_INTERLEAVED (BIT(BDADDR_BREDR) | \ BIT(BDADDR_LE_PUBLIC) | \ BIT(BDADDR_LE_RANDOM)) int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); int mgmt_index_added(struct hci_dev *hdev); int mgmt_index_removed(struct hci_dev *hdev); int mgmt_powered(struct hci_dev *hdev, u8 powered); int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); int mgmt_connectable(struct hci_dev *hdev, u8 connectable); int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u32 flags, u8 *name, u8 name_len, u8 *dev_class); int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 reason); int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, __le32 value, u8 confirm_hint); int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type); int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len); int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_discovering(struct hci_dev *hdev, u8 discovering); int mgmt_interleaved_discovery(struct hci_dev *hdev); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); bool mgmt_valid_hdev(struct hci_dev *hdev); int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) struct hci_pinfo { struct bt_sock bt; struct hci_dev *hdev; struct hci_filter filter; __u32 cmsg_mask; unsigned short channel; }; /* HCI security filter */ #define HCI_SFLT_MAX_OGF 5 struct hci_sec_filter { __u32 type_mask; __u32 event_mask[2]; __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; }; /* ----- HCI requests ----- */ #define HCI_REQ_DONE 0 #define HCI_REQ_PEND 1 #define HCI_REQ_CANCELED 2 #define hci_req_lock(d) mutex_lock(&d->req_lock) #define hci_req_unlock(d) mutex_unlock(&d->req_lock) void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result); void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], __u8 ltk[16]); int hci_do_inquiry(struct hci_dev *hdev, u8 length); int hci_cancel_inquiry(struct hci_dev *hdev); int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, int timeout); int hci_cancel_le_scan(struct hci_dev *hdev); u8 bdaddr_to_le(u8 bdaddr_type); #endif /* __HCI_CORE_H */ compat-drivers-2012-09-18/include/net/bluetooth/hci.h0000644000175000017500000010267312026211315021523 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __HCI_H #define __HCI_H #define HCI_MAX_ACL_SIZE 1024 #define HCI_MAX_SCO_SIZE 255 #define HCI_MAX_EVENT_SIZE 260 #define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4) #define HCI_LINK_KEY_SIZE 16 #define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE) /* HCI dev events */ #define HCI_DEV_REG 1 #define HCI_DEV_UNREG 2 #define HCI_DEV_UP 3 #define HCI_DEV_DOWN 4 #define HCI_DEV_SUSPEND 5 #define HCI_DEV_RESUME 6 /* HCI notify events */ #define HCI_NOTIFY_CONN_ADD 1 #define HCI_NOTIFY_CONN_DEL 2 #define HCI_NOTIFY_VOICE_SETTING 3 /* HCI bus types */ #define HCI_VIRTUAL 0 #define HCI_USB 1 #define HCI_PCCARD 2 #define HCI_UART 3 #define HCI_RS232 4 #define HCI_PCI 5 #define HCI_SDIO 6 /* HCI controller types */ #define HCI_BREDR 0x00 #define HCI_AMP 0x01 /* First BR/EDR Controller shall have ID = 0 */ #define HCI_BREDR_ID 0 /* AMP controller status */ #define AMP_CTRL_POWERED_DOWN 0x00 #define AMP_CTRL_BLUETOOTH_ONLY 0x01 #define AMP_CTRL_NO_CAPACITY 0x02 #define AMP_CTRL_LOW_CAPACITY 0x03 #define AMP_CTRL_MEDIUM_CAPACITY 0x04 #define AMP_CTRL_HIGH_CAPACITY 0x05 #define AMP_CTRL_FULL_CAPACITY 0x06 /* HCI device quirks */ enum { HCI_QUIRK_RESET_ON_CLOSE, HCI_QUIRK_RAW_DEVICE, HCI_QUIRK_FIXUP_BUFFER_SIZE }; /* HCI device flags */ enum { HCI_UP, HCI_INIT, HCI_RUNNING, HCI_PSCAN, HCI_ISCAN, HCI_AUTH, HCI_ENCRYPT, HCI_INQUIRY, HCI_RAW, HCI_RESET, }; /* * BR/EDR and/or LE controller flags: the flags defined here should represent * states from the controller. */ enum { HCI_SETUP, HCI_AUTO_OFF, HCI_MGMT, HCI_PAIRABLE, HCI_SERVICE_CACHE, HCI_LINK_KEYS, HCI_DEBUG_KEYS, HCI_UNREGISTER, HCI_LE_SCAN, HCI_SSP_ENABLED, HCI_HS_ENABLED, HCI_LE_ENABLED, HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LINK_SECURITY, HCI_PENDING_CLASS, HCI_PERIODIC_INQ, }; /* HCI ioctl defines */ #define HCIDEVUP _IOW('H', 201, int) #define HCIDEVDOWN _IOW('H', 202, int) #define HCIDEVRESET _IOW('H', 203, int) #define HCIDEVRESTAT _IOW('H', 204, int) #define HCIGETDEVLIST _IOR('H', 210, int) #define HCIGETDEVINFO _IOR('H', 211, int) #define HCIGETCONNLIST _IOR('H', 212, int) #define HCIGETCONNINFO _IOR('H', 213, int) #define HCIGETAUTHINFO _IOR('H', 215, int) #define HCISETRAW _IOW('H', 220, int) #define HCISETSCAN _IOW('H', 221, int) #define HCISETAUTH _IOW('H', 222, int) #define HCISETENCRYPT _IOW('H', 223, int) #define HCISETPTYPE _IOW('H', 224, int) #define HCISETLINKPOL _IOW('H', 225, int) #define HCISETLINKMODE _IOW('H', 226, int) #define HCISETACLMTU _IOW('H', 227, int) #define HCISETSCOMTU _IOW('H', 228, int) #define HCIBLOCKADDR _IOW('H', 230, int) #define HCIUNBLOCKADDR _IOW('H', 231, int) #define HCIINQUIRY _IOR('H', 240, int) /* HCI timeouts */ #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ #define HCI_INIT_TIMEOUT msecs_to_jiffies(10000) /* 10 seconds */ #define HCI_CMD_TIMEOUT msecs_to_jiffies(1000) /* 1 second */ #define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */ #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 #define HCI_ACLDATA_PKT 0x02 #define HCI_SCODATA_PKT 0x03 #define HCI_EVENT_PKT 0x04 #define HCI_VENDOR_PKT 0xff /* HCI packet types */ #define HCI_DM1 0x0008 #define HCI_DM3 0x0400 #define HCI_DM5 0x4000 #define HCI_DH1 0x0010 #define HCI_DH3 0x0800 #define HCI_DH5 0x8000 #define HCI_HV1 0x0020 #define HCI_HV2 0x0040 #define HCI_HV3 0x0080 #define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) #define ACL_PTYPE_MASK (~SCO_PTYPE_MASK) /* eSCO packet types */ #define ESCO_HV1 0x0001 #define ESCO_HV2 0x0002 #define ESCO_HV3 0x0004 #define ESCO_EV3 0x0008 #define ESCO_EV4 0x0010 #define ESCO_EV5 0x0020 #define ESCO_2EV3 0x0040 #define ESCO_3EV3 0x0080 #define ESCO_2EV5 0x0100 #define ESCO_3EV5 0x0200 #define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3) #define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) /* ACL flags */ #define ACL_START_NO_FLUSH 0x00 #define ACL_CONT 0x01 #define ACL_START 0x02 #define ACL_ACTIVE_BCAST 0x04 #define ACL_PICO_BCAST 0x08 /* Baseband links */ #define SCO_LINK 0x00 #define ACL_LINK 0x01 #define ESCO_LINK 0x02 /* Low Energy links do not have defined link type. Use invented one */ #define LE_LINK 0x80 /* LMP features */ #define LMP_3SLOT 0x01 #define LMP_5SLOT 0x02 #define LMP_ENCRYPT 0x04 #define LMP_SOFFSET 0x08 #define LMP_TACCURACY 0x10 #define LMP_RSWITCH 0x20 #define LMP_HOLD 0x40 #define LMP_SNIFF 0x80 #define LMP_PARK 0x01 #define LMP_RSSI 0x02 #define LMP_QUALITY 0x04 #define LMP_SCO 0x08 #define LMP_HV2 0x10 #define LMP_HV3 0x20 #define LMP_ULAW 0x40 #define LMP_ALAW 0x80 #define LMP_CVSD 0x01 #define LMP_PSCHEME 0x02 #define LMP_PCONTROL 0x04 #define LMP_RSSI_INQ 0x40 #define LMP_ESCO 0x80 #define LMP_EV4 0x01 #define LMP_EV5 0x02 #define LMP_NO_BREDR 0x20 #define LMP_LE 0x40 #define LMP_SNIFF_SUBR 0x02 #define LMP_PAUSE_ENC 0x04 #define LMP_EDR_ESCO_2M 0x20 #define LMP_EDR_ESCO_3M 0x40 #define LMP_EDR_3S_ESCO 0x80 #define LMP_EXT_INQ 0x01 #define LMP_SIMUL_LE_BR 0x02 #define LMP_SIMPLE_PAIR 0x08 #define LMP_NO_FLUSH 0x40 #define LMP_LSTO 0x01 #define LMP_INQ_TX_PWR 0x02 #define LMP_EXTFEATURES 0x80 /* Extended LMP features */ #define LMP_HOST_SSP 0x01 #define LMP_HOST_LE 0x02 #define LMP_HOST_LE_BREDR 0x04 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 #define HCI_CM_HOLD 0x0001 #define HCI_CM_SNIFF 0x0002 #define HCI_CM_PARK 0x0003 /* Link policies */ #define HCI_LP_RSWITCH 0x0001 #define HCI_LP_HOLD 0x0002 #define HCI_LP_SNIFF 0x0004 #define HCI_LP_PARK 0x0008 /* Link modes */ #define HCI_LM_ACCEPT 0x8000 #define HCI_LM_MASTER 0x0001 #define HCI_LM_AUTH 0x0002 #define HCI_LM_ENCRYPT 0x0004 #define HCI_LM_TRUSTED 0x0008 #define HCI_LM_RELIABLE 0x0010 #define HCI_LM_SECURE 0x0020 /* Authentication types */ #define HCI_AT_NO_BONDING 0x00 #define HCI_AT_NO_BONDING_MITM 0x01 #define HCI_AT_DEDICATED_BONDING 0x02 #define HCI_AT_DEDICATED_BONDING_MITM 0x03 #define HCI_AT_GENERAL_BONDING 0x04 #define HCI_AT_GENERAL_BONDING_MITM 0x05 /* Link Key types */ #define HCI_LK_COMBINATION 0x00 #define HCI_LK_LOCAL_UNIT 0x01 #define HCI_LK_REMOTE_UNIT 0x02 #define HCI_LK_DEBUG_COMBINATION 0x03 #define HCI_LK_UNAUTH_COMBINATION 0x04 #define HCI_LK_AUTH_COMBINATION 0x05 #define HCI_LK_CHANGED_COMBINATION 0x06 /* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ #define HCI_SMP_STK 0x80 #define HCI_SMP_STK_SLAVE 0x81 #define HCI_SMP_LTK 0x82 #define HCI_SMP_LTK_SLAVE 0x83 /* ---- HCI Error Codes ---- */ #define HCI_ERROR_AUTH_FAILURE 0x05 #define HCI_ERROR_CONNECTION_TIMEOUT 0x08 #define HCI_ERROR_REJ_BAD_ADDR 0x0f #define HCI_ERROR_REMOTE_USER_TERM 0x13 #define HCI_ERROR_REMOTE_LOW_RESOURCES 0x14 #define HCI_ERROR_REMOTE_POWER_OFF 0x15 #define HCI_ERROR_LOCAL_HOST_TERM 0x16 #define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 /* Flow control modes */ #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00 #define HCI_FLOW_CTL_MODE_BLOCK_BASED 0x01 /* Extended Inquiry Response field types */ #define EIR_FLAGS 0x01 /* flags */ #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ #define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ #define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ #define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ #define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ #define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ #define EIR_NAME_SHORT 0x08 /* shortened local name */ #define EIR_NAME_COMPLETE 0x09 /* complete local name */ #define EIR_TX_POWER 0x0A /* transmit power level */ #define EIR_CLASS_OF_DEV 0x0D /* Class of Device */ #define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ /* ----- HCI Commands ---- */ #define HCI_OP_NOP 0x0000 #define HCI_OP_INQUIRY 0x0401 struct hci_cp_inquiry { __u8 lap[3]; __u8 length; __u8 num_rsp; } __packed; #define HCI_OP_INQUIRY_CANCEL 0x0402 #define HCI_OP_PERIODIC_INQ 0x0403 #define HCI_OP_EXIT_PERIODIC_INQ 0x0404 #define HCI_OP_CREATE_CONN 0x0405 struct hci_cp_create_conn { bdaddr_t bdaddr; __le16 pkt_type; __u8 pscan_rep_mode; __u8 pscan_mode; __le16 clock_offset; __u8 role_switch; } __packed; #define HCI_OP_DISCONNECT 0x0406 struct hci_cp_disconnect { __le16 handle; __u8 reason; } __packed; #define HCI_OP_ADD_SCO 0x0407 struct hci_cp_add_sco { __le16 handle; __le16 pkt_type; } __packed; #define HCI_OP_CREATE_CONN_CANCEL 0x0408 struct hci_cp_create_conn_cancel { bdaddr_t bdaddr; } __packed; #define HCI_OP_ACCEPT_CONN_REQ 0x0409 struct hci_cp_accept_conn_req { bdaddr_t bdaddr; __u8 role; } __packed; #define HCI_OP_REJECT_CONN_REQ 0x040a struct hci_cp_reject_conn_req { bdaddr_t bdaddr; __u8 reason; } __packed; #define HCI_OP_LINK_KEY_REPLY 0x040b struct hci_cp_link_key_reply { bdaddr_t bdaddr; __u8 link_key[HCI_LINK_KEY_SIZE]; } __packed; #define HCI_OP_LINK_KEY_NEG_REPLY 0x040c struct hci_cp_link_key_neg_reply { bdaddr_t bdaddr; } __packed; #define HCI_OP_PIN_CODE_REPLY 0x040d struct hci_cp_pin_code_reply { bdaddr_t bdaddr; __u8 pin_len; __u8 pin_code[16]; } __packed; struct hci_rp_pin_code_reply { __u8 status; bdaddr_t bdaddr; } __packed; #define HCI_OP_PIN_CODE_NEG_REPLY 0x040e struct hci_cp_pin_code_neg_reply { bdaddr_t bdaddr; } __packed; struct hci_rp_pin_code_neg_reply { __u8 status; bdaddr_t bdaddr; } __packed; #define HCI_OP_CHANGE_CONN_PTYPE 0x040f struct hci_cp_change_conn_ptype { __le16 handle; __le16 pkt_type; } __packed; #define HCI_OP_AUTH_REQUESTED 0x0411 struct hci_cp_auth_requested { __le16 handle; } __packed; #define HCI_OP_SET_CONN_ENCRYPT 0x0413 struct hci_cp_set_conn_encrypt { __le16 handle; __u8 encrypt; } __packed; #define HCI_OP_CHANGE_CONN_LINK_KEY 0x0415 struct hci_cp_change_conn_link_key { __le16 handle; } __packed; #define HCI_OP_REMOTE_NAME_REQ 0x0419 struct hci_cp_remote_name_req { bdaddr_t bdaddr; __u8 pscan_rep_mode; __u8 pscan_mode; __le16 clock_offset; } __packed; #define HCI_OP_REMOTE_NAME_REQ_CANCEL 0x041a struct hci_cp_remote_name_req_cancel { bdaddr_t bdaddr; } __packed; #define HCI_OP_READ_REMOTE_FEATURES 0x041b struct hci_cp_read_remote_features { __le16 handle; } __packed; #define HCI_OP_READ_REMOTE_EXT_FEATURES 0x041c struct hci_cp_read_remote_ext_features { __le16 handle; __u8 page; } __packed; #define HCI_OP_READ_REMOTE_VERSION 0x041d struct hci_cp_read_remote_version { __le16 handle; } __packed; #define HCI_OP_SETUP_SYNC_CONN 0x0428 struct hci_cp_setup_sync_conn { __le16 handle; __le32 tx_bandwidth; __le32 rx_bandwidth; __le16 max_latency; __le16 voice_setting; __u8 retrans_effort; __le16 pkt_type; } __packed; #define HCI_OP_ACCEPT_SYNC_CONN_REQ 0x0429 struct hci_cp_accept_sync_conn_req { bdaddr_t bdaddr; __le32 tx_bandwidth; __le32 rx_bandwidth; __le16 max_latency; __le16 content_format; __u8 retrans_effort; __le16 pkt_type; } __packed; #define HCI_OP_REJECT_SYNC_CONN_REQ 0x042a struct hci_cp_reject_sync_conn_req { bdaddr_t bdaddr; __u8 reason; } __packed; #define HCI_OP_IO_CAPABILITY_REPLY 0x042b struct hci_cp_io_capability_reply { bdaddr_t bdaddr; __u8 capability; __u8 oob_data; __u8 authentication; } __packed; #define HCI_OP_USER_CONFIRM_REPLY 0x042c struct hci_cp_user_confirm_reply { bdaddr_t bdaddr; } __packed; struct hci_rp_user_confirm_reply { __u8 status; bdaddr_t bdaddr; } __packed; #define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d #define HCI_OP_USER_PASSKEY_REPLY 0x042e struct hci_cp_user_passkey_reply { bdaddr_t bdaddr; __le32 passkey; } __packed; #define HCI_OP_USER_PASSKEY_NEG_REPLY 0x042f #define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430 struct hci_cp_remote_oob_data_reply { bdaddr_t bdaddr; __u8 hash[16]; __u8 randomizer[16]; } __packed; #define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY 0x0433 struct hci_cp_remote_oob_data_neg_reply { bdaddr_t bdaddr; } __packed; #define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434 struct hci_cp_io_capability_neg_reply { bdaddr_t bdaddr; __u8 reason; } __packed; #define HCI_OP_CREATE_PHY_LINK 0x0435 struct hci_cp_create_phy_link { __u8 phy_handle; __u8 key_len; __u8 key_type; __u8 key[HCI_AMP_LINK_KEY_SIZE]; } __packed; #define HCI_OP_ACCEPT_PHY_LINK 0x0436 struct hci_cp_accept_phy_link { __u8 phy_handle; __u8 key_len; __u8 key_type; __u8 key[HCI_AMP_LINK_KEY_SIZE]; } __packed; #define HCI_OP_DISCONN_PHY_LINK 0x0437 struct hci_cp_disconn_phy_link { __u8 phy_handle; __u8 reason; } __packed; #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; __le16 max_interval; __le16 min_interval; __le16 attempt; __le16 timeout; } __packed; #define HCI_OP_EXIT_SNIFF_MODE 0x0804 struct hci_cp_exit_sniff_mode { __le16 handle; } __packed; #define HCI_OP_ROLE_DISCOVERY 0x0809 struct hci_cp_role_discovery { __le16 handle; } __packed; struct hci_rp_role_discovery { __u8 status; __le16 handle; __u8 role; } __packed; #define HCI_OP_SWITCH_ROLE 0x080b struct hci_cp_switch_role { bdaddr_t bdaddr; __u8 role; } __packed; #define HCI_OP_READ_LINK_POLICY 0x080c struct hci_cp_read_link_policy { __le16 handle; } __packed; struct hci_rp_read_link_policy { __u8 status; __le16 handle; __le16 policy; } __packed; #define HCI_OP_WRITE_LINK_POLICY 0x080d struct hci_cp_write_link_policy { __le16 handle; __le16 policy; } __packed; struct hci_rp_write_link_policy { __u8 status; __le16 handle; } __packed; #define HCI_OP_READ_DEF_LINK_POLICY 0x080e struct hci_rp_read_def_link_policy { __u8 status; __le16 policy; } __packed; #define HCI_OP_WRITE_DEF_LINK_POLICY 0x080f struct hci_cp_write_def_link_policy { __le16 policy; } __packed; #define HCI_OP_SNIFF_SUBRATE 0x0811 struct hci_cp_sniff_subrate { __le16 handle; __le16 max_latency; __le16 min_remote_timeout; __le16 min_local_timeout; } __packed; #define HCI_OP_SET_EVENT_MASK 0x0c01 struct hci_cp_set_event_mask { __u8 mask[8]; } __packed; #define HCI_OP_RESET 0x0c03 #define HCI_OP_SET_EVENT_FLT 0x0c05 struct hci_cp_set_event_flt { __u8 flt_type; __u8 cond_type; __u8 condition[0]; } __packed; /* Filter types */ #define HCI_FLT_CLEAR_ALL 0x00 #define HCI_FLT_INQ_RESULT 0x01 #define HCI_FLT_CONN_SETUP 0x02 /* CONN_SETUP Condition types */ #define HCI_CONN_SETUP_ALLOW_ALL 0x00 #define HCI_CONN_SETUP_ALLOW_CLASS 0x01 #define HCI_CONN_SETUP_ALLOW_BDADDR 0x02 /* CONN_SETUP Conditions */ #define HCI_CONN_SETUP_AUTO_OFF 0x01 #define HCI_CONN_SETUP_AUTO_ON 0x02 #define HCI_OP_DELETE_STORED_LINK_KEY 0x0c12 struct hci_cp_delete_stored_link_key { bdaddr_t bdaddr; __u8 delete_all; } __packed; #define HCI_MAX_NAME_LENGTH 248 #define HCI_OP_WRITE_LOCAL_NAME 0x0c13 struct hci_cp_write_local_name { __u8 name[HCI_MAX_NAME_LENGTH]; } __packed; #define HCI_OP_READ_LOCAL_NAME 0x0c14 struct hci_rp_read_local_name { __u8 status; __u8 name[HCI_MAX_NAME_LENGTH]; } __packed; #define HCI_OP_WRITE_CA_TIMEOUT 0x0c16 #define HCI_OP_WRITE_PG_TIMEOUT 0x0c18 #define HCI_OP_WRITE_SCAN_ENABLE 0x0c1a #define SCAN_DISABLED 0x00 #define SCAN_INQUIRY 0x01 #define SCAN_PAGE 0x02 #define HCI_OP_READ_AUTH_ENABLE 0x0c1f #define HCI_OP_WRITE_AUTH_ENABLE 0x0c20 #define AUTH_DISABLED 0x00 #define AUTH_ENABLED 0x01 #define HCI_OP_READ_ENCRYPT_MODE 0x0c21 #define HCI_OP_WRITE_ENCRYPT_MODE 0x0c22 #define ENCRYPT_DISABLED 0x00 #define ENCRYPT_P2P 0x01 #define ENCRYPT_BOTH 0x02 #define HCI_OP_READ_CLASS_OF_DEV 0x0c23 struct hci_rp_read_class_of_dev { __u8 status; __u8 dev_class[3]; } __packed; #define HCI_OP_WRITE_CLASS_OF_DEV 0x0c24 struct hci_cp_write_class_of_dev { __u8 dev_class[3]; } __packed; #define HCI_OP_READ_VOICE_SETTING 0x0c25 struct hci_rp_read_voice_setting { __u8 status; __le16 voice_setting; } __packed; #define HCI_OP_WRITE_VOICE_SETTING 0x0c26 struct hci_cp_write_voice_setting { __le16 voice_setting; } __packed; #define HCI_OP_HOST_BUFFER_SIZE 0x0c33 struct hci_cp_host_buffer_size { __le16 acl_mtu; __u8 sco_mtu; __le16 acl_max_pkt; __le16 sco_max_pkt; } __packed; #define HCI_OP_WRITE_INQUIRY_MODE 0x0c45 #define HCI_MAX_EIR_LENGTH 240 #define HCI_OP_WRITE_EIR 0x0c52 struct hci_cp_write_eir { __u8 fec; __u8 data[HCI_MAX_EIR_LENGTH]; } __packed; #define HCI_OP_READ_SSP_MODE 0x0c55 struct hci_rp_read_ssp_mode { __u8 status; __u8 mode; } __packed; #define HCI_OP_WRITE_SSP_MODE 0x0c56 struct hci_cp_write_ssp_mode { __u8 mode; } __packed; #define HCI_OP_READ_LOCAL_OOB_DATA 0x0c57 struct hci_rp_read_local_oob_data { __u8 status; __u8 hash[16]; __u8 randomizer[16]; } __packed; #define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58 struct hci_rp_read_inq_rsp_tx_power { __u8 status; __s8 tx_power; } __packed; #define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66 struct hci_rp_read_flow_control_mode { __u8 status; __u8 mode; } __packed; #define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d struct hci_cp_write_le_host_supported { __u8 le; __u8 simul; } __packed; #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; __u8 hci_ver; __le16 hci_rev; __u8 lmp_ver; __le16 manufacturer; __le16 lmp_subver; } __packed; #define HCI_OP_READ_LOCAL_COMMANDS 0x1002 struct hci_rp_read_local_commands { __u8 status; __u8 commands[64]; } __packed; #define HCI_OP_READ_LOCAL_FEATURES 0x1003 struct hci_rp_read_local_features { __u8 status; __u8 features[8]; } __packed; #define HCI_OP_READ_LOCAL_EXT_FEATURES 0x1004 struct hci_cp_read_local_ext_features { __u8 page; } __packed; struct hci_rp_read_local_ext_features { __u8 status; __u8 page; __u8 max_page; __u8 features[8]; } __packed; #define HCI_OP_READ_BUFFER_SIZE 0x1005 struct hci_rp_read_buffer_size { __u8 status; __le16 acl_mtu; __u8 sco_mtu; __le16 acl_max_pkt; __le16 sco_max_pkt; } __packed; #define HCI_OP_READ_BD_ADDR 0x1009 struct hci_rp_read_bd_addr { __u8 status; bdaddr_t bdaddr; } __packed; #define HCI_OP_READ_DATA_BLOCK_SIZE 0x100a struct hci_rp_read_data_block_size { __u8 status; __le16 max_acl_len; __le16 block_len; __le16 num_blocks; } __packed; #define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c struct hci_cp_write_page_scan_activity { __le16 interval; __le16 window; } __packed; #define HCI_OP_WRITE_PAGE_SCAN_TYPE 0x0c47 #define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_INTERLACED 0x01 #define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 struct hci_rp_read_local_amp_info { __u8 status; __u8 amp_status; __le32 total_bw; __le32 max_bw; __le32 min_latency; __le32 max_pdu; __u8 amp_type; __le16 pal_cap; __le16 max_assoc_size; __le32 max_flush_to; __le32 be_flush_to; } __packed; #define HCI_OP_READ_LOCAL_AMP_ASSOC 0x140a struct hci_cp_read_local_amp_assoc { __u8 phy_handle; __le16 len_so_far; __le16 max_len; } __packed; struct hci_rp_read_local_amp_assoc { __u8 status; __u8 phy_handle; __le16 rem_len; __u8 frag[0]; } __packed; #define HCI_OP_WRITE_REMOTE_AMP_ASSOC 0x140b struct hci_cp_write_remote_amp_assoc { __u8 phy_handle; __le16 len_so_far; __le16 rem_len; __u8 frag[0]; } __packed; struct hci_rp_write_remote_amp_assoc { __u8 status; __u8 phy_handle; } __packed; #define HCI_OP_LE_SET_EVENT_MASK 0x2001 struct hci_cp_le_set_event_mask { __u8 mask[8]; } __packed; #define HCI_OP_LE_READ_BUFFER_SIZE 0x2002 struct hci_rp_le_read_buffer_size { __u8 status; __le16 le_mtu; __u8 le_max_pkt; } __packed; #define HCI_OP_LE_SET_SCAN_PARAM 0x200b struct hci_cp_le_set_scan_param { __u8 type; __le16 interval; __le16 window; __u8 own_address_type; __u8 filter_policy; } __packed; #define LE_SCANNING_DISABLED 0x00 #define LE_SCANNING_ENABLED 0x01 #define HCI_OP_LE_SET_SCAN_ENABLE 0x200c struct hci_cp_le_set_scan_enable { __u8 enable; __u8 filter_dup; } __packed; #define HCI_OP_LE_CREATE_CONN 0x200d struct hci_cp_le_create_conn { __le16 scan_interval; __le16 scan_window; __u8 filter_policy; __u8 peer_addr_type; bdaddr_t peer_addr; __u8 own_address_type; __le16 conn_interval_min; __le16 conn_interval_max; __le16 conn_latency; __le16 supervision_timeout; __le16 min_ce_len; __le16 max_ce_len; } __packed; #define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e #define HCI_OP_LE_CONN_UPDATE 0x2013 struct hci_cp_le_conn_update { __le16 handle; __le16 conn_interval_min; __le16 conn_interval_max; __le16 conn_latency; __le16 supervision_timeout; __le16 min_ce_len; __le16 max_ce_len; } __packed; #define HCI_OP_LE_START_ENC 0x2019 struct hci_cp_le_start_enc { __le16 handle; __u8 rand[8]; __le16 ediv; __u8 ltk[16]; } __packed; #define HCI_OP_LE_LTK_REPLY 0x201a struct hci_cp_le_ltk_reply { __le16 handle; __u8 ltk[16]; } __packed; struct hci_rp_le_ltk_reply { __u8 status; __le16 handle; } __packed; #define HCI_OP_LE_LTK_NEG_REPLY 0x201b struct hci_cp_le_ltk_neg_reply { __le16 handle; } __packed; struct hci_rp_le_ltk_neg_reply { __u8 status; __le16 handle; } __packed; /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 #define HCI_EV_INQUIRY_RESULT 0x02 struct inquiry_info { bdaddr_t bdaddr; __u8 pscan_rep_mode; __u8 pscan_period_mode; __u8 pscan_mode; __u8 dev_class[3]; __le16 clock_offset; } __packed; #define HCI_EV_CONN_COMPLETE 0x03 struct hci_ev_conn_complete { __u8 status; __le16 handle; bdaddr_t bdaddr; __u8 link_type; __u8 encr_mode; } __packed; #define HCI_EV_CONN_REQUEST 0x04 struct hci_ev_conn_request { bdaddr_t bdaddr; __u8 dev_class[3]; __u8 link_type; } __packed; #define HCI_EV_DISCONN_COMPLETE 0x05 struct hci_ev_disconn_complete { __u8 status; __le16 handle; __u8 reason; } __packed; #define HCI_EV_AUTH_COMPLETE 0x06 struct hci_ev_auth_complete { __u8 status; __le16 handle; } __packed; #define HCI_EV_REMOTE_NAME 0x07 struct hci_ev_remote_name { __u8 status; bdaddr_t bdaddr; __u8 name[HCI_MAX_NAME_LENGTH]; } __packed; #define HCI_EV_ENCRYPT_CHANGE 0x08 struct hci_ev_encrypt_change { __u8 status; __le16 handle; __u8 encrypt; } __packed; #define HCI_EV_CHANGE_LINK_KEY_COMPLETE 0x09 struct hci_ev_change_link_key_complete { __u8 status; __le16 handle; } __packed; #define HCI_EV_REMOTE_FEATURES 0x0b struct hci_ev_remote_features { __u8 status; __le16 handle; __u8 features[8]; } __packed; #define HCI_EV_REMOTE_VERSION 0x0c struct hci_ev_remote_version { __u8 status; __le16 handle; __u8 lmp_ver; __le16 manufacturer; __le16 lmp_subver; } __packed; #define HCI_EV_QOS_SETUP_COMPLETE 0x0d struct hci_qos { __u8 service_type; __u32 token_rate; __u32 peak_bandwidth; __u32 latency; __u32 delay_variation; } __packed; struct hci_ev_qos_setup_complete { __u8 status; __le16 handle; struct hci_qos qos; } __packed; #define HCI_EV_CMD_COMPLETE 0x0e struct hci_ev_cmd_complete { __u8 ncmd; __le16 opcode; } __packed; #define HCI_EV_CMD_STATUS 0x0f struct hci_ev_cmd_status { __u8 status; __u8 ncmd; __le16 opcode; } __packed; #define HCI_EV_ROLE_CHANGE 0x12 struct hci_ev_role_change { __u8 status; bdaddr_t bdaddr; __u8 role; } __packed; #define HCI_EV_NUM_COMP_PKTS 0x13 struct hci_comp_pkts_info { __le16 handle; __le16 count; } __packed; struct hci_ev_num_comp_pkts { __u8 num_hndl; struct hci_comp_pkts_info handles[0]; } __packed; #define HCI_EV_MODE_CHANGE 0x14 struct hci_ev_mode_change { __u8 status; __le16 handle; __u8 mode; __le16 interval; } __packed; #define HCI_EV_PIN_CODE_REQ 0x16 struct hci_ev_pin_code_req { bdaddr_t bdaddr; } __packed; #define HCI_EV_LINK_KEY_REQ 0x17 struct hci_ev_link_key_req { bdaddr_t bdaddr; } __packed; #define HCI_EV_LINK_KEY_NOTIFY 0x18 struct hci_ev_link_key_notify { bdaddr_t bdaddr; __u8 link_key[HCI_LINK_KEY_SIZE]; __u8 key_type; } __packed; #define HCI_EV_CLOCK_OFFSET 0x1c struct hci_ev_clock_offset { __u8 status; __le16 handle; __le16 clock_offset; } __packed; #define HCI_EV_PKT_TYPE_CHANGE 0x1d struct hci_ev_pkt_type_change { __u8 status; __le16 handle; __le16 pkt_type; } __packed; #define HCI_EV_PSCAN_REP_MODE 0x20 struct hci_ev_pscan_rep_mode { bdaddr_t bdaddr; __u8 pscan_rep_mode; } __packed; #define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22 struct inquiry_info_with_rssi { bdaddr_t bdaddr; __u8 pscan_rep_mode; __u8 pscan_period_mode; __u8 dev_class[3]; __le16 clock_offset; __s8 rssi; } __packed; struct inquiry_info_with_rssi_and_pscan_mode { bdaddr_t bdaddr; __u8 pscan_rep_mode; __u8 pscan_period_mode; __u8 pscan_mode; __u8 dev_class[3]; __le16 clock_offset; __s8 rssi; } __packed; #define HCI_EV_REMOTE_EXT_FEATURES 0x23 struct hci_ev_remote_ext_features { __u8 status; __le16 handle; __u8 page; __u8 max_page; __u8 features[8]; } __packed; #define HCI_EV_SYNC_CONN_COMPLETE 0x2c struct hci_ev_sync_conn_complete { __u8 status; __le16 handle; bdaddr_t bdaddr; __u8 link_type; __u8 tx_interval; __u8 retrans_window; __le16 rx_pkt_len; __le16 tx_pkt_len; __u8 air_mode; } __packed; #define HCI_EV_SYNC_CONN_CHANGED 0x2d struct hci_ev_sync_conn_changed { __u8 status; __le16 handle; __u8 tx_interval; __u8 retrans_window; __le16 rx_pkt_len; __le16 tx_pkt_len; } __packed; #define HCI_EV_SNIFF_SUBRATE 0x2e struct hci_ev_sniff_subrate { __u8 status; __le16 handle; __le16 max_tx_latency; __le16 max_rx_latency; __le16 max_remote_timeout; __le16 max_local_timeout; } __packed; #define HCI_EV_EXTENDED_INQUIRY_RESULT 0x2f struct extended_inquiry_info { bdaddr_t bdaddr; __u8 pscan_rep_mode; __u8 pscan_period_mode; __u8 dev_class[3]; __le16 clock_offset; __s8 rssi; __u8 data[240]; } __packed; #define HCI_EV_KEY_REFRESH_COMPLETE 0x30 struct hci_ev_key_refresh_complete { __u8 status; __le16 handle; } __packed; #define HCI_EV_IO_CAPA_REQUEST 0x31 struct hci_ev_io_capa_request { bdaddr_t bdaddr; } __packed; #define HCI_EV_IO_CAPA_REPLY 0x32 struct hci_ev_io_capa_reply { bdaddr_t bdaddr; __u8 capability; __u8 oob_data; __u8 authentication; } __packed; #define HCI_EV_USER_CONFIRM_REQUEST 0x33 struct hci_ev_user_confirm_req { bdaddr_t bdaddr; __le32 passkey; } __packed; #define HCI_EV_USER_PASSKEY_REQUEST 0x34 struct hci_ev_user_passkey_req { bdaddr_t bdaddr; } __packed; #define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35 struct hci_ev_remote_oob_data_request { bdaddr_t bdaddr; } __packed; #define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36 struct hci_ev_simple_pair_complete { __u8 status; bdaddr_t bdaddr; } __packed; #define HCI_EV_REMOTE_HOST_FEATURES 0x3d struct hci_ev_remote_host_features { bdaddr_t bdaddr; __u8 features[8]; } __packed; #define HCI_EV_LE_META 0x3e struct hci_ev_le_meta { __u8 subevent; } __packed; #define HCI_EV_PHY_LINK_COMPLETE 0x40 struct hci_ev_phy_link_complete { __u8 status; __u8 phy_handle; } __packed; #define HCI_EV_CHANNEL_SELECTED 0x41 struct hci_ev_channel_selected { __u8 phy_handle; } __packed; #define HCI_EV_DISCONN_PHY_LINK_COMPLETE 0x42 struct hci_ev_disconn_phy_link_complete { __u8 status; __u8 phy_handle; __u8 reason; } __packed; #define HCI_EV_LOGICAL_LINK_COMPLETE 0x45 struct hci_ev_logical_link_complete { __u8 status; __le16 handle; __u8 phy_handle; __u8 flow_spec_id; } __packed; #define HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE 0x46 struct hci_ev_disconn_logical_link_complete { __u8 status; __le16 handle; __u8 reason; } __packed; #define HCI_EV_NUM_COMP_BLOCKS 0x48 struct hci_comp_blocks_info { __le16 handle; __le16 pkts; __le16 blocks; } __packed; struct hci_ev_num_comp_blocks { __le16 num_blocks; __u8 num_hndl; struct hci_comp_blocks_info handles[0]; } __packed; /* Low energy meta events */ #define LE_CONN_ROLE_MASTER 0x00 #define HCI_EV_LE_CONN_COMPLETE 0x01 struct hci_ev_le_conn_complete { __u8 status; __le16 handle; __u8 role; __u8 bdaddr_type; bdaddr_t bdaddr; __le16 interval; __le16 latency; __le16 supervision_timeout; __u8 clk_accurancy; } __packed; #define HCI_EV_LE_LTK_REQ 0x05 struct hci_ev_le_ltk_req { __le16 handle; __u8 random[8]; __le16 ediv; } __packed; /* Advertising report event types */ #define ADV_IND 0x00 #define ADV_DIRECT_IND 0x01 #define ADV_SCAN_IND 0x02 #define ADV_NONCONN_IND 0x03 #define ADV_SCAN_RSP 0x04 #define ADDR_LE_DEV_PUBLIC 0x00 #define ADDR_LE_DEV_RANDOM 0x01 #define HCI_EV_LE_ADVERTISING_REPORT 0x02 struct hci_ev_le_advertising_info { __u8 evt_type; __u8 bdaddr_type; bdaddr_t bdaddr; __u8 length; __u8 data[0]; } __packed; /* Internal events generated by Bluetooth stack */ #define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { __u16 type; __u8 data[0]; } __packed; #define HCI_EV_SI_DEVICE 0x01 struct hci_ev_si_device { __u16 event; __u16 dev_id; } __packed; #define HCI_EV_SI_SECURITY 0x02 struct hci_ev_si_security { __u16 event; __u16 proto; __u16 subproto; __u8 incoming; } __packed; /* ---- HCI Packet structures ---- */ #define HCI_COMMAND_HDR_SIZE 3 #define HCI_EVENT_HDR_SIZE 2 #define HCI_ACL_HDR_SIZE 4 #define HCI_SCO_HDR_SIZE 3 struct hci_command_hdr { __le16 opcode; /* OCF & OGF */ __u8 plen; } __packed; struct hci_event_hdr { __u8 evt; __u8 plen; } __packed; struct hci_acl_hdr { __le16 handle; /* Handle & Flags(PB, BC) */ __le16 dlen; } __packed; struct hci_sco_hdr { __le16 handle; __u8 dlen; } __packed; static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb) { return (struct hci_event_hdr *) skb->data; } static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb) { return (struct hci_acl_hdr *) skb->data; } static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb) { return (struct hci_sco_hdr *) skb->data; } /* Command opcode pack/unpack */ #define hci_opcode_pack(ogf, ocf) ((__u16) ((ocf & 0x03ff)|(ogf << 10))) #define hci_opcode_ogf(op) (op >> 10) #define hci_opcode_ocf(op) (op & 0x03ff) /* ACL handle and flags pack/unpack */ #define hci_handle_pack(h, f) ((__u16) ((h & 0x0fff)|(f << 12))) #define hci_handle(h) (h & 0x0fff) #define hci_flags(h) (h >> 12) /* ---- HCI Sockets ---- */ /* Socket options */ #define HCI_DATA_DIR 1 #define HCI_FILTER 2 #define HCI_TIME_STAMP 3 /* CMSG flags */ #define HCI_CMSG_DIR 0x0001 #define HCI_CMSG_TSTAMP 0x0002 struct sockaddr_hci { sa_family_t hci_family; unsigned short hci_dev; unsigned short hci_channel; }; #define HCI_DEV_NONE 0xffff #define HCI_CHANNEL_RAW 0 #define HCI_CHANNEL_MONITOR 2 #define HCI_CHANNEL_CONTROL 3 struct hci_filter { unsigned long type_mask; unsigned long event_mask[2]; __le16 opcode; }; struct hci_ufilter { __u32 type_mask; __u32 event_mask[2]; __le16 opcode; }; #define HCI_FLT_TYPE_BITS 31 #define HCI_FLT_EVENT_BITS 63 #define HCI_FLT_OGF_BITS 63 #define HCI_FLT_OCF_BITS 127 /* ---- HCI Ioctl requests structures ---- */ struct hci_dev_stats { __u32 err_rx; __u32 err_tx; __u32 cmd_tx; __u32 evt_rx; __u32 acl_tx; __u32 acl_rx; __u32 sco_tx; __u32 sco_rx; __u32 byte_rx; __u32 byte_tx; }; struct hci_dev_info { __u16 dev_id; char name[8]; bdaddr_t bdaddr; __u32 flags; __u8 type; __u8 features[8]; __u32 pkt_type; __u32 link_policy; __u32 link_mode; __u16 acl_mtu; __u16 acl_pkts; __u16 sco_mtu; __u16 sco_pkts; struct hci_dev_stats stat; }; struct hci_conn_info { __u16 handle; bdaddr_t bdaddr; __u8 type; __u8 out; __u16 state; __u32 link_mode; }; struct hci_dev_req { __u16 dev_id; __u32 dev_opt; }; struct hci_dev_list_req { __u16 dev_num; struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ }; struct hci_conn_list_req { __u16 dev_id; __u16 conn_num; struct hci_conn_info conn_info[0]; }; struct hci_conn_info_req { bdaddr_t bdaddr; __u8 type; struct hci_conn_info conn_info[0]; }; struct hci_auth_info_req { bdaddr_t bdaddr; __u8 type; }; struct hci_inquiry_req { __u16 dev_id; __u16 flags; __u8 lap[3]; __u8 length; __u8 num_rsp; }; #define IREQ_CACHE_FLUSH 0x0001 extern bool enable_hs; #endif /* __HCI_H */ compat-drivers-2012-09-18/include/net/bluetooth/sco.h0000644000175000017500000000403612026211315021536 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __SCO_H #define __SCO_H /* SCO defaults */ #define SCO_DEFAULT_MTU 500 #define SCO_DEFAULT_FLUSH_TO 0xFFFF #define SCO_CONN_TIMEOUT (HZ * 40) #define SCO_DISCONN_TIMEOUT (HZ * 2) #define SCO_CONN_IDLE_TIMEOUT (HZ * 60) /* SCO socket address */ struct sockaddr_sco { sa_family_t sco_family; bdaddr_t sco_bdaddr; }; /* SCO socket options */ #define SCO_OPTIONS 0x01 struct sco_options { __u16 mtu; }; #define SCO_CONNINFO 0x02 struct sco_conninfo { __u16 hci_handle; __u8 dev_class[3]; }; /* ---- SCO connections ---- */ struct sco_conn { struct hci_conn *hcon; bdaddr_t *dst; bdaddr_t *src; spinlock_t lock; struct sock *sk; unsigned int mtu; }; #define sco_conn_lock(c) spin_lock(&c->lock); #define sco_conn_unlock(c) spin_unlock(&c->lock); /* ----- SCO socket info ----- */ #define sco_pi(sk) ((struct sco_pinfo *) sk) struct sco_pinfo { struct bt_sock bt; __u32 flags; struct sco_conn *conn; }; #endif /* __SCO_H */ compat-drivers-2012-09-18/include/net/bluetooth/l2cap.h0000644000175000017500000004334212026211315021756 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __L2CAP_H #define __L2CAP_H #include /* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MIN_MTU 48 #define L2CAP_DEFAULT_FLUSH_TO 0xffff #define L2CAP_DEFAULT_TX_WINDOW 63 #define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF #define L2CAP_DEFAULT_MAX_TX 3 #define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ #define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ #define L2CAP_DEFAULT_ACK_TO 200 #define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF #define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF #define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF #define L2CAP_BREDR_MAX_PAYLOAD 1019 /* 3-DH5 packet */ #define L2CAP_LE_MIN_MTU 23 #define L2CAP_DISC_TIMEOUT msecs_to_jiffies(100) #define L2CAP_DISC_REJ_TIMEOUT msecs_to_jiffies(5000) #define L2CAP_ENC_TIMEOUT msecs_to_jiffies(5000) #define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000) #define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000) #define L2CAP_A2MP_DEFAULT_MTU 670 /* L2CAP socket address */ struct sockaddr_l2 { sa_family_t l2_family; __le16 l2_psm; bdaddr_t l2_bdaddr; __le16 l2_cid; __u8 l2_bdaddr_type; }; /* L2CAP socket options */ #define L2CAP_OPTIONS 0x01 struct l2cap_options { __u16 omtu; __u16 imtu; __u16 flush_to; __u8 mode; __u8 fcs; __u8 max_tx; __u16 txwin_size; }; #define L2CAP_CONNINFO 0x02 struct l2cap_conninfo { __u16 hci_handle; __u8 dev_class[3]; }; #define L2CAP_LM 0x03 #define L2CAP_LM_MASTER 0x0001 #define L2CAP_LM_AUTH 0x0002 #define L2CAP_LM_ENCRYPT 0x0004 #define L2CAP_LM_TRUSTED 0x0008 #define L2CAP_LM_RELIABLE 0x0010 #define L2CAP_LM_SECURE 0x0020 /* L2CAP command codes */ #define L2CAP_COMMAND_REJ 0x01 #define L2CAP_CONN_REQ 0x02 #define L2CAP_CONN_RSP 0x03 #define L2CAP_CONF_REQ 0x04 #define L2CAP_CONF_RSP 0x05 #define L2CAP_DISCONN_REQ 0x06 #define L2CAP_DISCONN_RSP 0x07 #define L2CAP_ECHO_REQ 0x08 #define L2CAP_ECHO_RSP 0x09 #define L2CAP_INFO_REQ 0x0a #define L2CAP_INFO_RSP 0x0b #define L2CAP_CREATE_CHAN_REQ 0x0c #define L2CAP_CREATE_CHAN_RSP 0x0d #define L2CAP_MOVE_CHAN_REQ 0x0e #define L2CAP_MOVE_CHAN_RSP 0x0f #define L2CAP_MOVE_CHAN_CFM 0x10 #define L2CAP_MOVE_CHAN_CFM_RSP 0x11 #define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 #define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 /* L2CAP extended feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 #define L2CAP_FEAT_RETRANS 0x00000002 #define L2CAP_FEAT_BIDIR_QOS 0x00000004 #define L2CAP_FEAT_ERTM 0x00000008 #define L2CAP_FEAT_STREAMING 0x00000010 #define L2CAP_FEAT_FCS 0x00000020 #define L2CAP_FEAT_EXT_FLOW 0x00000040 #define L2CAP_FEAT_FIXED_CHAN 0x00000080 #define L2CAP_FEAT_EXT_WINDOW 0x00000100 #define L2CAP_FEAT_UCD 0x00000200 /* L2CAP checksum option */ #define L2CAP_FCS_NONE 0x00 #define L2CAP_FCS_CRC16 0x01 /* L2CAP fixed channels */ #define L2CAP_FC_L2CAP 0x02 #define L2CAP_FC_A2MP 0x08 /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 #define L2CAP_CTRL_REQSEQ 0x3F00 #define L2CAP_CTRL_TXSEQ 0x007E #define L2CAP_CTRL_SUPERVISE 0x000C #define L2CAP_CTRL_RETRANS 0x0080 #define L2CAP_CTRL_FINAL 0x0080 #define L2CAP_CTRL_POLL 0x0010 #define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */ #define L2CAP_CTRL_TXSEQ_SHIFT 1 #define L2CAP_CTRL_SUPER_SHIFT 2 #define L2CAP_CTRL_POLL_SHIFT 4 #define L2CAP_CTRL_FINAL_SHIFT 7 #define L2CAP_CTRL_REQSEQ_SHIFT 8 #define L2CAP_CTRL_SAR_SHIFT 14 /* L2CAP Extended Control Field bit mask */ #define L2CAP_EXT_CTRL_TXSEQ 0xFFFC0000 #define L2CAP_EXT_CTRL_SAR 0x00030000 #define L2CAP_EXT_CTRL_SUPERVISE 0x00030000 #define L2CAP_EXT_CTRL_REQSEQ 0x0000FFFC #define L2CAP_EXT_CTRL_POLL 0x00040000 #define L2CAP_EXT_CTRL_FINAL 0x00000002 #define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */ #define L2CAP_EXT_CTRL_FINAL_SHIFT 1 #define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2 #define L2CAP_EXT_CTRL_SAR_SHIFT 16 #define L2CAP_EXT_CTRL_SUPER_SHIFT 16 #define L2CAP_EXT_CTRL_POLL_SHIFT 18 #define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18 /* L2CAP Supervisory Function */ #define L2CAP_SUPER_RR 0x00 #define L2CAP_SUPER_REJ 0x01 #define L2CAP_SUPER_RNR 0x02 #define L2CAP_SUPER_SREJ 0x03 /* L2CAP Segmentation and Reassembly */ #define L2CAP_SAR_UNSEGMENTED 0x00 #define L2CAP_SAR_START 0x01 #define L2CAP_SAR_END 0x02 #define L2CAP_SAR_CONTINUE 0x03 /* L2CAP Command rej. reasons */ #define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 #define L2CAP_REJ_MTU_EXCEEDED 0x0001 #define L2CAP_REJ_INVALID_CID 0x0002 /* L2CAP structures */ struct l2cap_hdr { __le16 len; __le16 cid; } __packed; #define L2CAP_HDR_SIZE 4 #define L2CAP_ENH_HDR_SIZE 6 #define L2CAP_EXT_HDR_SIZE 8 #define L2CAP_FCS_SIZE 2 #define L2CAP_SDULEN_SIZE 2 #define L2CAP_PSMLEN_SIZE 2 #define L2CAP_ENH_CTRL_SIZE 2 #define L2CAP_EXT_CTRL_SIZE 4 struct l2cap_cmd_hdr { __u8 code; __u8 ident; __le16 len; } __packed; #define L2CAP_CMD_HDR_SIZE 4 struct l2cap_cmd_rej_unk { __le16 reason; } __packed; struct l2cap_cmd_rej_mtu { __le16 reason; __le16 max_mtu; } __packed; struct l2cap_cmd_rej_cid { __le16 reason; __le16 scid; __le16 dcid; } __packed; struct l2cap_conn_req { __le16 psm; __le16 scid; } __packed; struct l2cap_conn_rsp { __le16 dcid; __le16 scid; __le16 result; __le16 status; } __packed; /* protocol/service multiplexer (PSM) */ #define L2CAP_PSM_SDP 0x0001 #define L2CAP_PSM_RFCOMM 0x0003 /* channel indentifier */ #define L2CAP_CID_SIGNALING 0x0001 #define L2CAP_CID_CONN_LESS 0x0002 #define L2CAP_CID_A2MP 0x0003 #define L2CAP_CID_LE_DATA 0x0004 #define L2CAP_CID_LE_SIGNALING 0x0005 #define L2CAP_CID_SMP 0x0006 #define L2CAP_CID_DYN_START 0x0040 #define L2CAP_CID_DYN_END 0xffff /* connect/create channel results */ #define L2CAP_CR_SUCCESS 0x0000 #define L2CAP_CR_PEND 0x0001 #define L2CAP_CR_BAD_PSM 0x0002 #define L2CAP_CR_SEC_BLOCK 0x0003 #define L2CAP_CR_NO_MEM 0x0004 #define L2CAP_CR_BAD_AMP 0x0005 /* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 #define L2CAP_CS_AUTHEN_PEND 0x0001 #define L2CAP_CS_AUTHOR_PEND 0x0002 struct l2cap_conf_req { __le16 dcid; __le16 flags; __u8 data[0]; } __packed; struct l2cap_conf_rsp { __le16 scid; __le16 flags; __le16 result; __u8 data[0]; } __packed; #define L2CAP_CONF_SUCCESS 0x0000 #define L2CAP_CONF_UNACCEPT 0x0001 #define L2CAP_CONF_REJECT 0x0002 #define L2CAP_CONF_UNKNOWN 0x0003 #define L2CAP_CONF_PENDING 0x0004 #define L2CAP_CONF_EFS_REJECT 0x0005 /* configuration req/rsp continuation flag */ #define L2CAP_CONF_FLAG_CONTINUATION 0x0001 struct l2cap_conf_opt { __u8 type; __u8 len; __u8 val[0]; } __packed; #define L2CAP_CONF_OPT_SIZE 2 #define L2CAP_CONF_HINT 0x80 #define L2CAP_CONF_MASK 0x7f #define L2CAP_CONF_MTU 0x01 #define L2CAP_CONF_FLUSH_TO 0x02 #define L2CAP_CONF_QOS 0x03 #define L2CAP_CONF_RFC 0x04 #define L2CAP_CONF_FCS 0x05 #define L2CAP_CONF_EFS 0x06 #define L2CAP_CONF_EWS 0x07 #define L2CAP_CONF_MAX_SIZE 22 struct l2cap_conf_rfc { __u8 mode; __u8 txwin_size; __u8 max_transmit; __le16 retrans_timeout; __le16 monitor_timeout; __le16 max_pdu_size; } __packed; #define L2CAP_MODE_BASIC 0x00 #define L2CAP_MODE_RETRANS 0x01 #define L2CAP_MODE_FLOWCTL 0x02 #define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_STREAMING 0x04 struct l2cap_conf_efs { __u8 id; __u8 stype; __le16 msdu; __le32 sdu_itime; __le32 acc_lat; __le32 flush_to; } __packed; #define L2CAP_SERV_NOTRAFIC 0x00 #define L2CAP_SERV_BESTEFFORT 0x01 #define L2CAP_SERV_GUARANTEED 0x02 #define L2CAP_BESTEFFORT_ID 0x01 struct l2cap_disconn_req { __le16 dcid; __le16 scid; } __packed; struct l2cap_disconn_rsp { __le16 dcid; __le16 scid; } __packed; struct l2cap_info_req { __le16 type; } __packed; struct l2cap_info_rsp { __le16 type; __le16 result; __u8 data[0]; } __packed; struct l2cap_create_chan_req { __le16 psm; __le16 scid; __u8 amp_id; } __packed; struct l2cap_create_chan_rsp { __le16 dcid; __le16 scid; __le16 result; __le16 status; } __packed; struct l2cap_move_chan_req { __le16 icid; __u8 dest_amp_id; } __packed; struct l2cap_move_chan_rsp { __le16 icid; __le16 result; } __packed; #define L2CAP_MR_SUCCESS 0x0000 #define L2CAP_MR_PEND 0x0001 #define L2CAP_MR_BAD_ID 0x0002 #define L2CAP_MR_SAME_ID 0x0003 #define L2CAP_MR_NOT_SUPP 0x0004 #define L2CAP_MR_COLLISION 0x0005 #define L2CAP_MR_NOT_ALLOWED 0x0006 struct l2cap_move_chan_cfm { __le16 icid; __le16 result; } __packed; #define L2CAP_MC_CONFIRMED 0x0000 #define L2CAP_MC_UNCONFIRMED 0x0001 struct l2cap_move_chan_cfm_rsp { __le16 icid; } __packed; /* info type */ #define L2CAP_IT_CL_MTU 0x0001 #define L2CAP_IT_FEAT_MASK 0x0002 #define L2CAP_IT_FIXED_CHAN 0x0003 /* info result */ #define L2CAP_IR_SUCCESS 0x0000 #define L2CAP_IR_NOTSUPP 0x0001 struct l2cap_conn_param_update_req { __le16 min; __le16 max; __le16 latency; __le16 to_multiplier; } __packed; struct l2cap_conn_param_update_rsp { __le16 result; } __packed; /* Connection Parameters result */ #define L2CAP_CONN_PARAM_ACCEPTED 0x0000 #define L2CAP_CONN_PARAM_REJECTED 0x0001 /* ----- L2CAP channels and connections ----- */ struct l2cap_seq_list { __u16 head; __u16 tail; __u16 mask; __u16 *list; }; #define L2CAP_SEQ_LIST_CLEAR 0xFFFF #define L2CAP_SEQ_LIST_TAIL 0x8000 struct l2cap_chan { struct sock *sk; struct l2cap_conn *conn; struct kref kref; __u8 state; __le16 psm; __u16 dcid; __u16 scid; __u16 imtu; __u16 omtu; __u16 flush_to; __u8 mode; __u8 chan_type; __u8 chan_policy; __le16 sport; __u8 sec_level; __u8 ident; __u8 conf_req[64]; __u8 conf_len; __u8 num_conf_req; __u8 num_conf_rsp; __u8 fcs; __u16 tx_win; __u16 tx_win_max; __u16 ack_win; __u8 max_tx; __u16 retrans_timeout; __u16 monitor_timeout; __u16 mps; __u8 tx_state; __u8 rx_state; unsigned long conf_state; unsigned long conn_state; unsigned long flags; __u16 next_tx_seq; __u16 expected_ack_seq; __u16 expected_tx_seq; __u16 buffer_seq; __u16 srej_save_reqseq; __u16 last_acked_seq; __u16 frames_sent; __u16 unacked_frames; __u8 retry_count; __u16 srej_queue_next; __u16 sdu_len; struct sk_buff *sdu; struct sk_buff *sdu_last_frag; __u16 remote_tx_win; __u8 remote_max_tx; __u16 remote_mps; __u8 local_id; __u8 local_stype; __u16 local_msdu; __u32 local_sdu_itime; __u32 local_acc_lat; __u32 local_flush_to; __u8 remote_id; __u8 remote_stype; __u16 remote_msdu; __u32 remote_sdu_itime; __u32 remote_acc_lat; __u32 remote_flush_to; struct delayed_work chan_timer; struct delayed_work retrans_timer; struct delayed_work monitor_timer; struct delayed_work ack_timer; struct sk_buff *tx_send_head; struct sk_buff_head tx_q; struct sk_buff_head srej_q; struct l2cap_seq_list srej_list; struct l2cap_seq_list retrans_list; struct list_head list; struct list_head global_l; void *data; struct l2cap_ops *ops; struct mutex lock; }; struct l2cap_ops { char *name; struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); int (*recv) (struct l2cap_chan * chan, struct sk_buff *skb); void (*teardown) (struct l2cap_chan *chan, int err); void (*close) (struct l2cap_chan *chan); void (*state_change) (struct l2cap_chan *chan, int state); void (*ready) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long len, int nb); }; struct l2cap_conn { struct hci_conn *hcon; struct hci_chan *hchan; bdaddr_t *dst; bdaddr_t *src; unsigned int mtu; __u32 feat_mask; __u8 fixed_chan_mask; __u8 info_state; __u8 info_ident; struct delayed_work info_timer; spinlock_t lock; struct sk_buff *rx_skb; __u32 rx_len; __u8 tx_ident; __u8 disc_reason; struct delayed_work security_timer; struct smp_chan *smp_chan; struct list_head chan_l; struct mutex chan_lock; }; #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 #define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04 #define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08 #define L2CAP_CHAN_RAW 1 #define L2CAP_CHAN_CONN_LESS 2 #define L2CAP_CHAN_CONN_ORIENTED 3 #define L2CAP_CHAN_CONN_FIX_A2MP 4 /* ----- L2CAP socket info ----- */ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) struct l2cap_pinfo { struct bt_sock bt; struct l2cap_chan *chan; struct sk_buff *rx_busy_skb; }; enum { CONF_REQ_SENT, CONF_INPUT_DONE, CONF_OUTPUT_DONE, CONF_MTU_DONE, CONF_MODE_DONE, CONF_CONNECT_PEND, CONF_NO_FCS_RECV, CONF_STATE2_DEVICE, CONF_EWS_RECV, CONF_LOC_CONF_PEND, CONF_REM_CONF_PEND, CONF_NOT_COMPLETE, }; #define L2CAP_CONF_MAX_CONF_REQ 2 #define L2CAP_CONF_MAX_CONF_RSP 2 enum { CONN_SREJ_SENT, CONN_WAIT_F, CONN_SREJ_ACT, CONN_SEND_PBIT, CONN_REMOTE_BUSY, CONN_LOCAL_BUSY, CONN_REJ_ACT, CONN_SEND_FBIT, CONN_RNR_SENT, }; /* Definitions for flags in l2cap_chan */ enum { FLAG_ROLE_SWITCH, FLAG_FORCE_ACTIVE, FLAG_FORCE_RELIABLE, FLAG_FLUSHABLE, FLAG_EXT_CTRL, FLAG_EFS_ENABLE, }; enum { L2CAP_TX_STATE_XMIT, L2CAP_TX_STATE_WAIT_F, }; enum { L2CAP_RX_STATE_RECV, L2CAP_RX_STATE_SREJ_SENT, }; enum { L2CAP_TXSEQ_EXPECTED, L2CAP_TXSEQ_EXPECTED_SREJ, L2CAP_TXSEQ_UNEXPECTED, L2CAP_TXSEQ_UNEXPECTED_SREJ, L2CAP_TXSEQ_DUPLICATE, L2CAP_TXSEQ_DUPLICATE_SREJ, L2CAP_TXSEQ_INVALID, L2CAP_TXSEQ_INVALID_IGNORE, }; enum { L2CAP_EV_DATA_REQUEST, L2CAP_EV_LOCAL_BUSY_DETECTED, L2CAP_EV_LOCAL_BUSY_CLEAR, L2CAP_EV_RECV_REQSEQ_AND_FBIT, L2CAP_EV_RECV_FBIT, L2CAP_EV_RETRANS_TO, L2CAP_EV_MONITOR_TO, L2CAP_EV_EXPLICIT_POLL, L2CAP_EV_RECV_IFRAME, L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ, L2CAP_EV_RECV_RNR, L2CAP_EV_RECV_SREJ, L2CAP_EV_RECV_FRAME, }; void l2cap_chan_hold(struct l2cap_chan *c); void l2cap_chan_put(struct l2cap_chan *c); static inline void l2cap_chan_lock(struct l2cap_chan *chan) { mutex_lock(&chan->lock); } static inline void l2cap_chan_unlock(struct l2cap_chan *chan) { mutex_unlock(&chan->lock); } static inline void l2cap_set_timer(struct l2cap_chan *chan, struct delayed_work *work, long timeout) { BT_DBG("chan %p state %s timeout %ld", chan, state_to_string(chan->state), timeout); /* If delayed work cancelled do not hold(chan) since it is already done with previous set_timer */ if (!cancel_delayed_work(work)) l2cap_chan_hold(chan); schedule_delayed_work(work, timeout); } static inline bool l2cap_clear_timer(struct l2cap_chan *chan, struct delayed_work *work) { bool ret; /* put(chan) if delayed work cancelled otherwise it is done in delayed work function */ ret = cancel_delayed_work(work); if (ret) l2cap_chan_put(chan); return ret; } #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) #define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) #define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) #define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO)); #define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2) { if (seq1 >= seq2) return seq1 - seq2; else return chan->tx_win_max + 1 - seq2 + seq1; } static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) { return (seq + 1) % (chan->tx_win_max + 1); } static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan) { return NULL; } static inline void l2cap_chan_no_teardown(struct l2cap_chan *chan, int err) { } static inline void l2cap_chan_no_ready(struct l2cap_chan *chan) { } extern bool disable_ertm; int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); int __l2cap_wait_ack(struct sock *sk); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); struct l2cap_chan *l2cap_chan_create(void); void l2cap_chan_close(struct l2cap_chan *chan, int reason); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u32 priority); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); int l2cap_chan_check_security(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); #endif /* __L2CAP_H */ compat-drivers-2012-09-18/include/net/bluetooth/hci_mon.h0000644000175000017500000000306512026211315022367 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2011-2012 Intel Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __HCI_MON_H #define __HCI_MON_H struct hci_mon_hdr { __le16 opcode; __le16 index; __le16 len; } __packed; #define HCI_MON_HDR_SIZE 6 #define HCI_MON_NEW_INDEX 0 #define HCI_MON_DEL_INDEX 1 #define HCI_MON_COMMAND_PKT 2 #define HCI_MON_EVENT_PKT 3 #define HCI_MON_ACL_TX_PKT 4 #define HCI_MON_ACL_RX_PKT 5 #define HCI_MON_SCO_TX_PKT 6 #define HCI_MON_SCO_RX_PKT 7 struct hci_mon_new_index { __u8 type; __u8 bus; bdaddr_t bdaddr; char name[8]; } __packed; #define HCI_MON_NEW_INDEX_SIZE 16 #endif /* __HCI_MON_H */ compat-drivers-2012-09-18/include/net/bluetooth/mgmt.h0000644000175000017500000002761712026211315021730 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2010 Nokia Corporation Copyright (C) 2011-2012 Intel Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #define MGMT_INDEX_NONE 0xFFFF #define MGMT_STATUS_SUCCESS 0x00 #define MGMT_STATUS_UNKNOWN_COMMAND 0x01 #define MGMT_STATUS_NOT_CONNECTED 0x02 #define MGMT_STATUS_FAILED 0x03 #define MGMT_STATUS_CONNECT_FAILED 0x04 #define MGMT_STATUS_AUTH_FAILED 0x05 #define MGMT_STATUS_NOT_PAIRED 0x06 #define MGMT_STATUS_NO_RESOURCES 0x07 #define MGMT_STATUS_TIMEOUT 0x08 #define MGMT_STATUS_ALREADY_CONNECTED 0x09 #define MGMT_STATUS_BUSY 0x0a #define MGMT_STATUS_REJECTED 0x0b #define MGMT_STATUS_NOT_SUPPORTED 0x0c #define MGMT_STATUS_INVALID_PARAMS 0x0d #define MGMT_STATUS_DISCONNECTED 0x0e #define MGMT_STATUS_NOT_POWERED 0x0f #define MGMT_STATUS_CANCELLED 0x10 #define MGMT_STATUS_INVALID_INDEX 0x11 struct mgmt_hdr { __le16 opcode; __le16 index; __le16 len; } __packed; struct mgmt_addr_info { bdaddr_t bdaddr; __u8 type; } __packed; #define MGMT_ADDR_INFO_SIZE 7 #define MGMT_OP_READ_VERSION 0x0001 #define MGMT_READ_VERSION_SIZE 0 struct mgmt_rp_read_version { __u8 version; __le16 revision; } __packed; #define MGMT_OP_READ_COMMANDS 0x0002 #define MGMT_READ_COMMANDS_SIZE 0 struct mgmt_rp_read_commands { __le16 num_commands; __le16 num_events; __le16 opcodes[0]; } __packed; #define MGMT_OP_READ_INDEX_LIST 0x0003 #define MGMT_READ_INDEX_LIST_SIZE 0 struct mgmt_rp_read_index_list { __le16 num_controllers; __le16 index[0]; } __packed; /* Reserve one extra byte for names in management messages so that they * are always guaranteed to be nul-terminated */ #define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1) #define MGMT_MAX_SHORT_NAME_LENGTH (HCI_MAX_SHORT_NAME_LENGTH + 1) #define MGMT_SETTING_POWERED 0x00000001 #define MGMT_SETTING_CONNECTABLE 0x00000002 #define MGMT_SETTING_FAST_CONNECTABLE 0x00000004 #define MGMT_SETTING_DISCOVERABLE 0x00000008 #define MGMT_SETTING_PAIRABLE 0x00000010 #define MGMT_SETTING_LINK_SECURITY 0x00000020 #define MGMT_SETTING_SSP 0x00000040 #define MGMT_SETTING_BREDR 0x00000080 #define MGMT_SETTING_HS 0x00000100 #define MGMT_SETTING_LE 0x00000200 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 struct mgmt_rp_read_info { bdaddr_t bdaddr; __u8 version; __le16 manufacturer; __le32 supported_settings; __le32 current_settings; __u8 dev_class[3]; __u8 name[MGMT_MAX_NAME_LENGTH]; __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; struct mgmt_mode { __u8 val; } __packed; #define MGMT_SETTING_SIZE 1 #define MGMT_OP_SET_POWERED 0x0005 #define MGMT_OP_SET_DISCOVERABLE 0x0006 struct mgmt_cp_set_discoverable { __u8 val; __le16 timeout; } __packed; #define MGMT_SET_DISCOVERABLE_SIZE 3 #define MGMT_OP_SET_CONNECTABLE 0x0007 #define MGMT_OP_SET_FAST_CONNECTABLE 0x0008 #define MGMT_OP_SET_PAIRABLE 0x0009 #define MGMT_OP_SET_LINK_SECURITY 0x000A #define MGMT_OP_SET_SSP 0x000B #define MGMT_OP_SET_HS 0x000C #define MGMT_OP_SET_LE 0x000D #define MGMT_OP_SET_DEV_CLASS 0x000E struct mgmt_cp_set_dev_class { __u8 major; __u8 minor; } __packed; #define MGMT_SET_DEV_CLASS_SIZE 2 #define MGMT_OP_SET_LOCAL_NAME 0x000F struct mgmt_cp_set_local_name { __u8 name[MGMT_MAX_NAME_LENGTH]; __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; #define MGMT_SET_LOCAL_NAME_SIZE 260 #define MGMT_OP_ADD_UUID 0x0010 struct mgmt_cp_add_uuid { __u8 uuid[16]; __u8 svc_hint; } __packed; #define MGMT_ADD_UUID_SIZE 17 #define MGMT_OP_REMOVE_UUID 0x0011 struct mgmt_cp_remove_uuid { __u8 uuid[16]; } __packed; #define MGMT_REMOVE_UUID_SIZE 16 struct mgmt_link_key_info { struct mgmt_addr_info addr; __u8 type; __u8 val[16]; __u8 pin_len; } __packed; #define MGMT_OP_LOAD_LINK_KEYS 0x0012 struct mgmt_cp_load_link_keys { __u8 debug_keys; __le16 key_count; struct mgmt_link_key_info keys[0]; } __packed; #define MGMT_LOAD_LINK_KEYS_SIZE 3 struct mgmt_ltk_info { struct mgmt_addr_info addr; __u8 authenticated; __u8 master; __u8 enc_size; __le16 ediv; __u8 rand[8]; __u8 val[16]; } __packed; #define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0013 struct mgmt_cp_load_long_term_keys { __le16 key_count; struct mgmt_ltk_info keys[0]; } __packed; #define MGMT_LOAD_LONG_TERM_KEYS_SIZE 2 #define MGMT_OP_DISCONNECT 0x0014 struct mgmt_cp_disconnect { struct mgmt_addr_info addr; } __packed; #define MGMT_DISCONNECT_SIZE MGMT_ADDR_INFO_SIZE struct mgmt_rp_disconnect { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_GET_CONNECTIONS 0x0015 #define MGMT_GET_CONNECTIONS_SIZE 0 struct mgmt_rp_get_connections { __le16 conn_count; struct mgmt_addr_info addr[0]; } __packed; #define MGMT_OP_PIN_CODE_REPLY 0x0016 struct mgmt_cp_pin_code_reply { struct mgmt_addr_info addr; __u8 pin_len; __u8 pin_code[16]; } __packed; #define MGMT_PIN_CODE_REPLY_SIZE (MGMT_ADDR_INFO_SIZE + 17) struct mgmt_rp_pin_code_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017 struct mgmt_cp_pin_code_neg_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_PIN_CODE_NEG_REPLY_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_SET_IO_CAPABILITY 0x0018 struct mgmt_cp_set_io_capability { __u8 io_capability; } __packed; #define MGMT_SET_IO_CAPABILITY_SIZE 1 #define MGMT_OP_PAIR_DEVICE 0x0019 struct mgmt_cp_pair_device { struct mgmt_addr_info addr; __u8 io_cap; } __packed; #define MGMT_PAIR_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_pair_device { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_CANCEL_PAIR_DEVICE 0x001A #define MGMT_CANCEL_PAIR_DEVICE_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_UNPAIR_DEVICE 0x001B struct mgmt_cp_unpair_device { struct mgmt_addr_info addr; __u8 disconnect; } __packed; #define MGMT_UNPAIR_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_unpair_device { struct mgmt_addr_info addr; }; #define MGMT_OP_USER_CONFIRM_REPLY 0x001C struct mgmt_cp_user_confirm_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_USER_CONFIRM_REPLY_SIZE MGMT_ADDR_INFO_SIZE struct mgmt_rp_user_confirm_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001D struct mgmt_cp_user_confirm_neg_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_USER_CONFIRM_NEG_REPLY_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_USER_PASSKEY_REPLY 0x001E struct mgmt_cp_user_passkey_reply { struct mgmt_addr_info addr; __le32 passkey; } __packed; #define MGMT_USER_PASSKEY_REPLY_SIZE (MGMT_ADDR_INFO_SIZE + 4) struct mgmt_rp_user_passkey_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001F struct mgmt_cp_user_passkey_neg_reply { struct mgmt_addr_info addr; } __packed; #define MGMT_USER_PASSKEY_NEG_REPLY_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020 #define MGMT_READ_LOCAL_OOB_DATA_SIZE 0 struct mgmt_rp_read_local_oob_data { __u8 hash[16]; __u8 randomizer[16]; } __packed; #define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021 struct mgmt_cp_add_remote_oob_data { struct mgmt_addr_info addr; __u8 hash[16]; __u8 randomizer[16]; } __packed; #define MGMT_ADD_REMOTE_OOB_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 32) #define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022 struct mgmt_cp_remove_remote_oob_data { struct mgmt_addr_info addr; } __packed; #define MGMT_REMOVE_REMOTE_OOB_DATA_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_START_DISCOVERY 0x0023 struct mgmt_cp_start_discovery { __u8 type; } __packed; #define MGMT_START_DISCOVERY_SIZE 1 #define MGMT_OP_STOP_DISCOVERY 0x0024 struct mgmt_cp_stop_discovery { __u8 type; } __packed; #define MGMT_STOP_DISCOVERY_SIZE 1 #define MGMT_OP_CONFIRM_NAME 0x0025 struct mgmt_cp_confirm_name { struct mgmt_addr_info addr; __u8 name_known; } __packed; #define MGMT_CONFIRM_NAME_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_confirm_name { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_BLOCK_DEVICE 0x0026 struct mgmt_cp_block_device { struct mgmt_addr_info addr; } __packed; #define MGMT_BLOCK_DEVICE_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_UNBLOCK_DEVICE 0x0027 struct mgmt_cp_unblock_device { struct mgmt_addr_info addr; } __packed; #define MGMT_UNBLOCK_DEVICE_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_SET_DEVICE_ID 0x0028 struct mgmt_cp_set_device_id { __le16 source; __le16 vendor; __le16 product; __le16 version; } __packed; #define MGMT_SET_DEVICE_ID_SIZE 8 #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; __u8 status; __u8 data[0]; } __packed; #define MGMT_EV_CMD_STATUS 0x0002 struct mgmt_ev_cmd_status { __le16 opcode; __u8 status; } __packed; #define MGMT_EV_CONTROLLER_ERROR 0x0003 struct mgmt_ev_controller_error { __u8 error_code; } __packed; #define MGMT_EV_INDEX_ADDED 0x0004 #define MGMT_EV_INDEX_REMOVED 0x0005 #define MGMT_EV_NEW_SETTINGS 0x0006 #define MGMT_EV_CLASS_OF_DEV_CHANGED 0x0007 struct mgmt_ev_class_of_dev_changed { __u8 dev_class[3]; }; #define MGMT_EV_LOCAL_NAME_CHANGED 0x0008 struct mgmt_ev_local_name_changed { __u8 name[MGMT_MAX_NAME_LENGTH]; __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; #define MGMT_EV_NEW_LINK_KEY 0x0009 struct mgmt_ev_new_link_key { __u8 store_hint; struct mgmt_link_key_info key; } __packed; #define MGMT_EV_NEW_LONG_TERM_KEY 0x000A struct mgmt_ev_new_long_term_key { __u8 store_hint; struct mgmt_ltk_info key; } __packed; #define MGMT_EV_DEVICE_CONNECTED 0x000B struct mgmt_ev_device_connected { struct mgmt_addr_info addr; __le32 flags; __le16 eir_len; __u8 eir[0]; } __packed; #define MGMT_DEV_DISCONN_UNKNOWN 0x00 #define MGMT_DEV_DISCONN_TIMEOUT 0x01 #define MGMT_DEV_DISCONN_LOCAL_HOST 0x02 #define MGMT_DEV_DISCONN_REMOTE 0x03 #define MGMT_EV_DEVICE_DISCONNECTED 0x000C struct mgmt_ev_device_disconnected { struct mgmt_addr_info addr; __u8 reason; } __packed; #define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { struct mgmt_addr_info addr; __u8 status; } __packed; #define MGMT_EV_PIN_CODE_REQUEST 0x000E struct mgmt_ev_pin_code_request { struct mgmt_addr_info addr; __u8 secure; } __packed; #define MGMT_EV_USER_CONFIRM_REQUEST 0x000F struct mgmt_ev_user_confirm_request { struct mgmt_addr_info addr; __u8 confirm_hint; __le32 value; } __packed; #define MGMT_EV_USER_PASSKEY_REQUEST 0x0010 struct mgmt_ev_user_passkey_request { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_AUTH_FAILED 0x0011 struct mgmt_ev_auth_failed { struct mgmt_addr_info addr; __u8 status; } __packed; #define MGMT_DEV_FOUND_CONFIRM_NAME 0x01 #define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02 #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { struct mgmt_addr_info addr; __s8 rssi; __le32 flags; __le16 eir_len; __u8 eir[0]; } __packed; #define MGMT_EV_DISCOVERING 0x0013 struct mgmt_ev_discovering { __u8 type; __u8 discovering; } __packed; #define MGMT_EV_DEVICE_BLOCKED 0x0014 struct mgmt_ev_device_blocked { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_DEVICE_UNBLOCKED 0x0015 struct mgmt_ev_device_unblocked { struct mgmt_addr_info addr; } __packed; #define MGMT_EV_DEVICE_UNPAIRED 0x0016 struct mgmt_ev_device_unpaired { struct mgmt_addr_info addr; } __packed; compat-drivers-2012-09-18/include/net/bluetooth/smp.h0000644000175000017500000000753412026211315021557 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __SMP_H #define __SMP_H struct smp_command_hdr { __u8 code; } __packed; #define SMP_CMD_PAIRING_REQ 0x01 #define SMP_CMD_PAIRING_RSP 0x02 struct smp_cmd_pairing { __u8 io_capability; __u8 oob_flag; __u8 auth_req; __u8 max_key_size; __u8 init_key_dist; __u8 resp_key_dist; } __packed; #define SMP_IO_DISPLAY_ONLY 0x00 #define SMP_IO_DISPLAY_YESNO 0x01 #define SMP_IO_KEYBOARD_ONLY 0x02 #define SMP_IO_NO_INPUT_OUTPUT 0x03 #define SMP_IO_KEYBOARD_DISPLAY 0x04 #define SMP_OOB_NOT_PRESENT 0x00 #define SMP_OOB_PRESENT 0x01 #define SMP_DIST_ENC_KEY 0x01 #define SMP_DIST_ID_KEY 0x02 #define SMP_DIST_SIGN 0x04 #define SMP_AUTH_NONE 0x00 #define SMP_AUTH_BONDING 0x01 #define SMP_AUTH_MITM 0x04 #define SMP_CMD_PAIRING_CONFIRM 0x03 struct smp_cmd_pairing_confirm { __u8 confirm_val[16]; } __packed; #define SMP_CMD_PAIRING_RANDOM 0x04 struct smp_cmd_pairing_random { __u8 rand_val[16]; } __packed; #define SMP_CMD_PAIRING_FAIL 0x05 struct smp_cmd_pairing_fail { __u8 reason; } __packed; #define SMP_CMD_ENCRYPT_INFO 0x06 struct smp_cmd_encrypt_info { __u8 ltk[16]; } __packed; #define SMP_CMD_MASTER_IDENT 0x07 struct smp_cmd_master_ident { __le16 ediv; __u8 rand[8]; } __packed; #define SMP_CMD_IDENT_INFO 0x08 struct smp_cmd_ident_info { __u8 irk[16]; } __packed; #define SMP_CMD_IDENT_ADDR_INFO 0x09 struct smp_cmd_ident_addr_info { __u8 addr_type; bdaddr_t bdaddr; } __packed; #define SMP_CMD_SIGN_INFO 0x0a struct smp_cmd_sign_info { __u8 csrk[16]; } __packed; #define SMP_CMD_SECURITY_REQ 0x0b struct smp_cmd_security_req { __u8 auth_req; } __packed; #define SMP_PASSKEY_ENTRY_FAILED 0x01 #define SMP_OOB_NOT_AVAIL 0x02 #define SMP_AUTH_REQUIREMENTS 0x03 #define SMP_CONFIRM_FAILED 0x04 #define SMP_PAIRING_NOTSUPP 0x05 #define SMP_ENC_KEY_SIZE 0x06 #define SMP_CMD_NOTSUPP 0x07 #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 #define SMP_MIN_ENC_KEY_SIZE 7 #define SMP_MAX_ENC_KEY_SIZE 16 #define SMP_FLAG_TK_VALID 1 #define SMP_FLAG_CFM_PENDING 2 #define SMP_FLAG_MITM_AUTH 3 struct smp_chan { struct l2cap_conn *conn; u8 preq[7]; /* SMP Pairing Request */ u8 prsp[7]; /* SMP Pairing Response */ u8 prnd[16]; /* SMP Pairing Random (local) */ u8 rrnd[16]; /* SMP Pairing Random (remote) */ u8 pcnf[16]; /* SMP Pairing Confirm */ u8 tk[16]; /* SMP Temporary Key */ u8 enc_key_size; unsigned long smp_flags; struct crypto_blkcipher *tfm; struct work_struct confirm; struct work_struct random; }; /* SMP Commands */ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); void smp_chan_destroy(struct l2cap_conn *conn); #endif /* __SMP_H */ compat-drivers-2012-09-18/include/net/bluetooth/bluetooth.h0000644000175000017500000001715712026211315022767 0ustar mcgrofmcgrof/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ #ifndef __BLUETOOTH_H #define __BLUETOOTH_H #include #include #include #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 31 #define PF_BLUETOOTH AF_BLUETOOTH #endif /* Bluetooth versions */ #define BLUETOOTH_VER_1_1 1 #define BLUETOOTH_VER_1_2 2 #define BLUETOOTH_VER_2_0 3 /* Reserv for core and drivers use */ #define BT_SKB_RESERVE 8 #define BTPROTO_L2CAP 0 #define BTPROTO_HCI 1 #define BTPROTO_SCO 2 #define BTPROTO_RFCOMM 3 #define BTPROTO_BNEP 4 #define BTPROTO_CMTP 5 #define BTPROTO_HIDP 6 #define BTPROTO_AVDTP 7 #define SOL_HCI 0 #define SOL_L2CAP 6 #define SOL_SCO 17 #define SOL_RFCOMM 18 #define BT_SECURITY 4 struct bt_security { __u8 level; __u8 key_size; }; #define BT_SECURITY_SDP 0 #define BT_SECURITY_LOW 1 #define BT_SECURITY_MEDIUM 2 #define BT_SECURITY_HIGH 3 #define BT_DEFER_SETUP 7 #define BT_FLUSHABLE 8 #define BT_FLUSHABLE_OFF 0 #define BT_FLUSHABLE_ON 1 #define BT_POWER 9 struct bt_power { __u8 force_active; }; #define BT_POWER_FORCE_ACTIVE_OFF 0 #define BT_POWER_FORCE_ACTIVE_ON 1 #define BT_CHANNEL_POLICY 10 /* BR/EDR only (default policy) * AMP controllers cannot be used. * Channel move requests from the remote device are denied. * If the L2CAP channel is currently using AMP, move the channel to BR/EDR. */ #define BT_CHANNEL_POLICY_BREDR_ONLY 0 /* BR/EDR Preferred * Allow use of AMP controllers. * If the L2CAP channel is currently on AMP, move it to BR/EDR. * Channel move requests from the remote device are allowed. */ #define BT_CHANNEL_POLICY_BREDR_PREFERRED 1 /* AMP Preferred * Allow use of AMP controllers * If the L2CAP channel is currently on BR/EDR and AMP controller * resources are available, initiate a channel move to AMP. * Channel move requests from the remote device are allowed. * If the L2CAP socket has not been connected yet, try to create * and configure the channel directly on an AMP controller rather * than BR/EDR. */ #define BT_CHANNEL_POLICY_AMP_PREFERRED 2 __printf(1, 2) int bt_info(const char *fmt, ...); __printf(1, 2) int bt_err(const char *fmt, ...); #define BT_INFO(fmt, ...) bt_info(fmt "\n", ##__VA_ARGS__) #define BT_ERR(fmt, ...) bt_err(fmt "\n", ##__VA_ARGS__) #define BT_DBG(fmt, ...) pr_debug(fmt "\n", ##__VA_ARGS__) /* Connection and socket states */ enum { BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ BT_OPEN, BT_BOUND, BT_LISTEN, BT_CONNECT, BT_CONNECT2, BT_CONFIG, BT_DISCONN, BT_CLOSED }; /* If unused will be removed by compiler */ static inline const char *state_to_string(int state) { switch (state) { case BT_CONNECTED: return "BT_CONNECTED"; case BT_OPEN: return "BT_OPEN"; case BT_BOUND: return "BT_BOUND"; case BT_LISTEN: return "BT_LISTEN"; case BT_CONNECT: return "BT_CONNECT"; case BT_CONNECT2: return "BT_CONNECT2"; case BT_CONFIG: return "BT_CONFIG"; case BT_DISCONN: return "BT_DISCONN"; case BT_CLOSED: return "BT_CLOSED"; } return "invalid state"; } /* BD Address */ typedef struct { __u8 b[6]; } __packed bdaddr_t; /* BD Address type */ #define BDADDR_BREDR 0x00 #define BDADDR_LE_PUBLIC 0x01 #define BDADDR_LE_RANDOM 0x02 #define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} }) #define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} }) /* Copy, swap, convert BD Address */ static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2) { return memcmp(ba1, ba2, sizeof(bdaddr_t)); } static inline void bacpy(bdaddr_t *dst, bdaddr_t *src) { memcpy(dst, src, sizeof(bdaddr_t)); } void baswap(bdaddr_t *dst, bdaddr_t *src); char *batostr(bdaddr_t *ba); /* Common socket structures and functions */ #define bt_sk(__sk) ((struct bt_sock *) __sk) struct bt_sock { struct sock sk; bdaddr_t src; bdaddr_t dst; struct list_head accept_q; struct sock *parent; unsigned long flags; }; enum { BT_SK_DEFER_SETUP, BT_SK_SUSPEND, }; struct bt_sock_list { struct hlist_head head; rwlock_t lock; #ifdef CONFIG_PROC_FS struct file_operations fops; int (* custom_seq_show)(struct seq_file *, void *); #endif }; int bt_sock_register(int proto, const struct net_proto_family *ops); int bt_sock_unregister(int proto); void bt_sock_link(struct bt_sock_list *l, struct sock *s); void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); void bt_accept_enqueue(struct sock *parent, struct sock *sk); void bt_accept_unlink(struct sock *sk); struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); /* Skb helpers */ struct l2cap_ctrl { unsigned int sframe:1, poll:1, final:1, fcs:1, sar:2, super:2; __u16 reqseq; __u16 txseq; __u8 retries; }; struct bt_skb_cb { __u8 pkt_type; __u8 incoming; __u16 expect; __u8 force_active; struct l2cap_ctrl control; }; #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how) { struct sk_buff *skb; skb = alloc_skb(len + BT_SKB_RESERVE, how); if (skb) { skb_reserve(skb, BT_SKB_RESERVE); bt_cb(skb)->incoming = 0; } return skb; } static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, unsigned long len, int nb, int *err) { struct sk_buff *skb; skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err); if (skb) { skb_reserve(skb, BT_SKB_RESERVE); bt_cb(skb)->incoming = 0; } if (!skb && *err) return NULL; *err = sock_error(sk); if (*err) goto out; if (sk->sk_shutdown) { *err = -ECONNRESET; goto out; } return skb; out: kfree_skb(skb); return NULL; } int bt_to_errno(__u16 code); extern int hci_sock_init(void); extern void hci_sock_cleanup(void); extern int bt_sysfs_init(void); extern void bt_sysfs_cleanup(void); extern int bt_procfs_init(struct module* module, struct net *net, const char *name, struct bt_sock_list* sk_list, int (* seq_show)(struct seq_file *, void *)); extern void bt_procfs_cleanup(struct net *net, const char *name); extern struct dentry *bt_debugfs; int l2cap_init(void); void l2cap_exit(void); int sco_init(void); void sco_exit(void); void bt_sock_reclassify_lock(struct sock *sk, int proto); #endif /* __BLUETOOTH_H */ compat-drivers-2012-09-18/include/net/regulatory.h0000644000175000017500000001102312026211315021134 0ustar mcgrofmcgrof#ifndef __NET_REGULATORY_H #define __NET_REGULATORY_H /* * regulatory support structures * * Copyright 2008-2009 Luis R. Rodriguez * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * enum environment_cap - Environment parsed from country IE * @ENVIRON_ANY: indicates country IE applies to both indoor and * outdoor operation. * @ENVIRON_INDOOR: indicates country IE applies only to indoor operation * @ENVIRON_OUTDOOR: indicates country IE applies only to outdoor operation */ enum environment_cap { ENVIRON_ANY, ENVIRON_INDOOR, ENVIRON_OUTDOOR, }; /** * struct regulatory_request - used to keep track of regulatory requests * * @wiphy_idx: this is set if this request's initiator is * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This * can be used by the wireless core to deal with conflicts * and potentially inform users of which devices specifically * cased the conflicts. * @initiator: indicates who sent this request, could be any of * of those set in nl80211_reg_initiator (%NL80211_REGDOM_SET_BY_*) * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested * regulatory domain. We have a few special codes: * 00 - World regulatory domain * 99 - built by driver but a specific alpha2 cannot be determined * 98 - result of an intersection between two regulatory domains * 97 - regulatory domain has not yet been configured * @dfs_region: If CRDA responded with a regulatory domain that requires * DFS master operation on a known DFS region (NL80211_DFS_*), * dfs_region represents that region. Drivers can use this and the * @alpha2 to adjust their device's DFS parameters as required. * @user_reg_hint_type: if the @initiator was of type * %NL80211_REGDOM_SET_BY_USER, this classifies the type * of hint passed. This could be any of the %NL80211_USER_REG_HINT_* * types. * @intersect: indicates whether the wireless core should intersect * the requested regulatory domain with the presently set regulatory * domain. * @processed: indicates whether or not this requests has already been * processed. When the last request is processed it means that the * currently regulatory domain set on cfg80211 is updated from * CRDA and can be used by other regulatory requests. When a * the last request is not yet processed we must yield until it * is processed before processing any new requests. * @country_ie_checksum: checksum of the last processed and accepted * country IE * @country_ie_env: lets us know if the AP is telling us we are outdoor, * indoor, or if it doesn't matter * @list: used to insert into the reg_requests_list linked list */ struct regulatory_request { int wiphy_idx; enum nl80211_reg_initiator initiator; enum nl80211_user_reg_hint_type user_reg_hint_type; char alpha2[2]; u8 dfs_region; bool intersect; bool processed; enum environment_cap country_ie_env; struct list_head list; }; struct ieee80211_freq_range { u32 start_freq_khz; u32 end_freq_khz; u32 max_bandwidth_khz; }; struct ieee80211_power_rule { u32 max_antenna_gain; u32 max_eirp; }; struct ieee80211_reg_rule { struct ieee80211_freq_range freq_range; struct ieee80211_power_rule power_rule; u32 flags; }; struct ieee80211_regdomain { u32 n_reg_rules; char alpha2[2]; u8 dfs_region; struct ieee80211_reg_rule reg_rules[]; }; #define MHZ_TO_KHZ(freq) ((freq) * 1000) #define KHZ_TO_MHZ(freq) ((freq) / 1000) #define DBI_TO_MBI(gain) ((gain) * 100) #define MBI_TO_DBI(gain) ((gain) / 100) #define DBM_TO_MBM(gain) ((gain) * 100) #define MBM_TO_DBM(gain) ((gain) / 100) #define REG_RULE(start, end, bw, gain, eirp, reg_flags) \ { \ .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \ .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \ .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \ .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\ .power_rule.max_eirp = DBM_TO_MBM(eirp), \ .flags = reg_flags, \ } #endif compat-drivers-2012-09-18/include/pcmcia/0000755000175000017500000000000012026176501017246 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/include/pcmcia/cistpl.h0000644000175000017500000000033712025673354020727 0ustar mcgrofmcgrof#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) #include #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) #include #endif #include_next compat-drivers-2012-09-18/.compat_version0000644000175000017500000000003212026211316017405 0ustar mcgrofmcgrofcompat-drivers-2012-09-18 compat-drivers-2012-09-18/.compat_base_tree_version0000644000175000017500000000001612026211316021420 0ustar mcgrofmcgrofnext-20120918 compat-drivers-2012-09-18/linux-next-pending/0000755000175000017500000000000012026176477020140 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/linux-next-pending/network/0000755000175000017500000000000012026176477021631 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/linux-next-pending/network/.gitignore0000644000175000017500000000000012026176477023607 0ustar mcgrofmcgrofcompat-drivers-2012-09-18/linux-next-pending/README0000644000175000017500000000146112026176477021022 0ustar mcgrofmcgrof compat-drivers linux-next-pending patches ========================================== You must have a really good reason to be adding files in this directory. The requirement is your patches must have been posted to a public mailing list for the subsystem you are working on. Each patch you add here must have a really good explanation on the top of the file which clarifies why the patch has not yet been merged OR specify it on the commit log when you add it on compat-drivers. We try to avoid having patch files because but we understand if you might because you need to test code posted but not yet merged into linux-next or a stable kernel release. This can happen often during the merge window, when the maintainers are unavailable, on vacation, suck at what they do, or for any other uncontrollable reasons. compat-drivers-2012-09-18/linux-next-cherry-picks/0000755000175000017500000000000012026176477021117 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/linux-next-cherry-picks/network/0000755000175000017500000000000012026176477022610 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/linux-next-cherry-picks/network/.gitignore0000644000175000017500000000000012026176477024566 0ustar mcgrofmcgrofcompat-drivers-2012-09-18/linux-next-cherry-picks/README0000644000175000017500000000150312026176477021776 0ustar mcgrofmcgrofcompat-drivers linux-next chery picked patches ============================================== We work hard to get patches in time onto the stable tree but sometimes a few things slip out, and sometimes a stable fix is simply too big in size to be merged into stable. In such cases though we do believe some of these patches are still relatively important to either enable new hardware which escaped the late rc cycles or to correct some behaviour which might be too late for stable. We apply these patches by default as they will be supported on these releases. The larger the number of patches you see in this directory the more we should be ashamed. We should strive to reduce this to 0 all the time. This directory will always be empty for bleeding edge releases as bleeding edge releases are always based on linux-next already. compat-drivers-2012-09-18/README.md0000644000175000017500000000160212026176477015661 0ustar mcgrofmcgrof# Linux compat drivers compatibility package This package provides backport support for drivers from newer kernels down to older kernels. It currently backports 3 subsystems: * Ethernet * Wireless * Bluetooth This package provides the latest Linux kernel subsystem enhancements for kernels 2.6.24 and above. It is technically possible to support kernels < 2.6.24 but more work is required for that. # Documentation This package is documented online and has more-up-to date information online than on this README file. You should read the wiki page and not rely on this README! https://backports.wiki.kernel.org # License This work is a subset of the Linux kernel as such we keep the kernel's Copyright practice. Some files have their own copyright and in those cases the license is mentioned in the file. All additional work made to building this package is licensed under the GPLv2. compat-drivers-2012-09-18/crap/0000755000175000017500000000000012026176477015330 5ustar mcgrofmcgrofcompat-drivers-2012-09-18/crap/network/0000755000175000017500000000000012026176477017021 5ustar mcgrofmcgrof././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootcompat-drivers-2012-09-18/crap/network/0001-alx-add-new-QCA-ethernet-driver-which-supercedes-atl.patchcompat-drivers-2012-09-18/crap/network/0001-alx-add-new-QCA-ethernet-driver-which-supercedes-atl.pat0000644000175000017500000146067712026176477031760 0ustar mcgrofmcgrofFrom 4d59bde6ad6120d81f8b8ac6ac0beda718f7fb21 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Tue, 28 Feb 2012 11:49:31 -0800 Subject: [PATCH] net: add new QCA alx ethernet driver which supercedes atl1c This driver is intended to replace the atl1c driver and provide support for a two new chipsets. Qualcomm Atheros (QCA) is commiting to fixing all bugs found on this driver. Test results show this driver performs better than atl1c on all supported chipsets, closes the gap between TX and RX throughput (they now match) and has also been verified with ASPM enabled on all supported chipsets. This driver and patch also have addressed all sparse and checkpatch warnings. QCA is commiting on fixing all bugs upstream on this driver. This driver is also permissively licensed thereby enabling developers of other OSes to cherry pick this driver to port to their OS, such as FreeBSD. Both atl1c and alx driver support the following chipsets: 1969:1063 - AR8131 Gigabit Ethernet 1969:1062 - AR8132 Fast Ethernet (10/100 Mbit/s) 1969:2062 - AR8152 v2.0 Fast Ethernet 1969:2060 - AR8152 v1.1 Fast Ethernet 1969:1073 - AR8151 v1.0 Gigabit Ethernet 1969:1083 - AR8151 v2.0 Gigabit Ethernet But alx also supports these two new chipstes: 1969:1091 - AR8161 Gigabit Ethernet 1969:1090 - AR8162 Fast Ethernet We leave the atl1c driver in place for now but mark it as deprecated in favor for the alx. Linux distributions should consider using alx moving forward and any issues found with the alx driver will be proactively addressed and tracked by assigned by QCA engineers. For more detail including a shiny graph of throughput in comparison to atl1c see the new shiny alx driver home page: https://www.linuxfoundation.org/collaborate/workgroups/networking/alx Signed-off-by: Stevent Li Signed-off-by: Hao-Ran Liu (Joseph Liu) Signed-off-by: Cloud Ren Signed-off-by: Joe Perches Signed-off-by: Luis R. Rodriguez --- MAINTAINERS | 11 + drivers/net/ethernet/atheros/Kconfig | 42 +- drivers/net/ethernet/atheros/Makefile | 1 + drivers/net/ethernet/atheros/alx/Makefile | 3 + drivers/net/ethernet/atheros/alx/alc_cb.c | 912 ++++++ drivers/net/ethernet/atheros/alx/alc_hw.c | 1087 +++++++ drivers/net/ethernet/atheros/alx/alc_hw.h | 1324 ++++++++ drivers/net/ethernet/atheros/alx/alf_cb.c | 1187 +++++++ drivers/net/ethernet/atheros/alx/alf_hw.c | 918 ++++++ drivers/net/ethernet/atheros/alx/alf_hw.h | 2098 +++++++++++++ drivers/net/ethernet/atheros/alx/alx.h | 670 ++++ drivers/net/ethernet/atheros/alx/alx_ethtool.c | 519 ++++ drivers/net/ethernet/atheros/alx/alx_hwcom.h | 187 ++ drivers/net/ethernet/atheros/alx/alx_main.c | 3899 ++++++++++++++++++++++++ drivers/net/ethernet/atheros/alx/alx_sw.h | 493 +++ 15 files changed, 13350 insertions(+), 1 deletions(-) create mode 100644 drivers/net/ethernet/atheros/alx/Makefile create mode 100644 drivers/net/ethernet/atheros/alx/alc_cb.c create mode 100644 drivers/net/ethernet/atheros/alx/alc_hw.c create mode 100644 drivers/net/ethernet/atheros/alx/alc_hw.h create mode 100644 drivers/net/ethernet/atheros/alx/alf_cb.c create mode 100644 drivers/net/ethernet/atheros/alx/alf_hw.c create mode 100644 drivers/net/ethernet/atheros/alx/alf_hw.h create mode 100644 drivers/net/ethernet/atheros/alx/alx.h create mode 100644 drivers/net/ethernet/atheros/alx/alx_ethtool.c create mode 100644 drivers/net/ethernet/atheros/alx/alx_hwcom.h create mode 100644 drivers/net/ethernet/atheros/alx/alx_main.c create mode 100644 drivers/net/ethernet/atheros/alx/alx_sw.h --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1301,6 +1301,17 @@ W: http://atl1.sourceforge.net S: Maintained F: drivers/net/ethernet/atheros/ +ALX ETHERNET DRIVERS +M: Cloud Ren +M: Stevent Li +M: Wu Ken +M: David Liu +L: netdev@vger.kernel.org +L: nic-devel@qualcomm.com +W: http://wireless.kernel.org/en/users/Drivers/ethernet/alx +S: Supported +F: drivers/net/ethernet/atheros/alx/ + ATM M: Chas Williams L: linux-atm-general@lists.sourceforge.net (moderated for non-subscribers) --- a/drivers/net/ethernet/atheros/Kconfig +++ b/drivers/net/ethernet/atheros/Kconfig @@ -56,15 +56,55 @@ config ATL1E will be called atl1e. config ATL1C - tristate "Atheros L1C Gigabit Ethernet support (EXPERIMENTAL)" + tristate "Atheros L1C Gigabit Ethernet support (DEPRECATED)" depends on PCI && EXPERIMENTAL select CRC32 select NET_CORE select MII ---help--- This driver supports the Atheros L1C gigabit ethernet adapter. + This driver is deprecated in favor for the alx (CONFIG_ALX) driver. + This driver supports the following chipsets: + + 1969:1063 - AR8131 Gigabit Ethernet + 1969:1062 - AR8132 Fast Ethernet (10/100 Mbit/s) + 1969:2062 - AR8152 v2.0 Fast Ethernet + 1969:2060 - AR8152 v1.1 Fast Ethernet + 1969:1073 - AR8151 v1.0 Gigabit Ethernet + 1969:1083 - AR8151 v2.0 Gigabit Ethernet To compile this driver as a module, choose M here. The module will be called atl1c. +config ALX + tristate "Atheros ALX Gigabit Ethernet support" + depends on PCI + select CRC32 + select NET_CORE + select MII + ---help--- + This driver supports the Atheros L1C/L1D/L1F gigabit ethernet + adapter. The alx driver is intended to replace completely the + atl1c driver with proper support and commitment from Qualcomm + Atheros (QCA). Both atl1c and alx supports the following chipsets: + + 1969:1063 - AR8131 Gigabit Ethernet + 1969:1062 - AR8132 Fast Ethernet (10/100 Mbit/s) + 1969:2062 - AR8152 v2.0 Fast Ethernet + 1969:2060 - AR8152 v1.1 Fast Ethernet + 1969:1073 - AR8151 v1.0 Gigabit Ethernet + 1969:1083 - AR8151 v2.0 Gigabit Ethernet + + Only alx supports the following chipsets: + + 1969:1091 - AR8161 + 1969:1090 - AR8162 + + For more information see: + + https://www.linuxfoundation.org/collaborate/workgroups/networking/alx + + To compile this driver as a module, choose M here. The module + will be called alx. + endif # NET_VENDOR_ATHEROS --- a/drivers/net/ethernet/atheros/Makefile +++ b/drivers/net/ethernet/atheros/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_ATL1) += atlx/ obj-$(CONFIG_ATL2) += atlx/ obj-$(CONFIG_ATL1E) += atl1e/ obj-$(CONFIG_ATL1C) += atl1c/ +obj-$(CONFIG_ALX) += alx/ --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_ALX) += alx.o +alx-objs := alx_main.o alx_ethtool.o alc_cb.o alc_hw.o alf_cb.o alf_hw.o +ccflags-y += -D__CHECK_ENDIAN__ --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alc_cb.c @@ -0,0 +1,912 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include + +#include "alc_hw.h" + + +/* NIC */ +static int alc_identify_nic(struct alx_hw *hw) +{ + return 0; +} + + +/* PHY */ +static int alc_read_phy_reg(struct alx_hw *hw, u16 reg_addr, u16 *phy_data) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&hw->mdio_lock, flags); + + if (l1c_read_phy(hw, false, ALX_MDIO_DEV_TYPE_NORM, false, reg_addr, + phy_data)) { + alx_hw_err(hw, "error when read phy reg\n"); + retval = -EINVAL; + } + + spin_unlock_irqrestore(&hw->mdio_lock, flags); + return retval; +} + + +static int alc_write_phy_reg(struct alx_hw *hw, u16 reg_addr, u16 phy_data) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&hw->mdio_lock, flags); + + if (l1c_write_phy(hw, false, ALX_MDIO_DEV_TYPE_NORM, false, reg_addr, + phy_data)) { + alx_hw_err(hw, "error when write phy reg\n"); + retval = -EINVAL; + } + + spin_unlock_irqrestore(&hw->mdio_lock, flags); + return retval; +} + + +static int alc_init_phy(struct alx_hw *hw) +{ + u16 phy_id[2]; + int retval; + + spin_lock_init(&hw->mdio_lock); + + retval = alc_read_phy_reg(hw, MII_PHYSID1, &phy_id[0]); + if (retval) + return retval; + retval = alc_read_phy_reg(hw, MII_PHYSID2, &phy_id[1]); + if (retval) + return retval; + + memcpy(&hw->phy_id, phy_id, sizeof(hw->phy_id)); + + hw->autoneg_advertised = ALX_LINK_SPEED_1GB_FULL | + ALX_LINK_SPEED_10_HALF | + ALX_LINK_SPEED_10_FULL | + ALX_LINK_SPEED_100_HALF | + ALX_LINK_SPEED_100_FULL; + return retval; +} + + +static int alc_reset_phy(struct alx_hw *hw) +{ + bool pws_en, az_en, ptp_en; + int retval = 0; + + pws_en = az_en = ptp_en = false; + CLI_HW_FLAG(PWSAVE_EN); + CLI_HW_FLAG(AZ_EN); + CLI_HW_FLAG(PTP_EN); + + if (CHK_HW_FLAG(PWSAVE_CAP)) { + pws_en = true; + SET_HW_FLAG(PWSAVE_EN); + } + + if (CHK_HW_FLAG(AZ_CAP)) { + az_en = true; + SET_HW_FLAG(AZ_EN); + } + + if (CHK_HW_FLAG(PTP_CAP)) { + ptp_en = true; + SET_HW_FLAG(PTP_EN); + } + + alx_hw_info(hw, "reset PHY, pws = %d, az = %d, ptp = %d\n", + pws_en, az_en, ptp_en); + + if (l1c_reset_phy(hw, pws_en, az_en, ptp_en)) { + alx_hw_err(hw, "error when reset phy\n"); + retval = -EINVAL; + } + + return retval; +} + + +/* LINK */ +static int alc_setup_phy_link(struct alx_hw *hw, u32 speed, bool autoneg, + bool fc) +{ + u8 link_cap = 0; + int retval = 0; + + alx_hw_info(hw, "speed = 0x%x, autoneg = %d\n", speed, autoneg); + if (speed & ALX_LINK_SPEED_1GB_FULL) + link_cap |= LX_LC_1000F; + + if (speed & ALX_LINK_SPEED_100_FULL) + link_cap |= LX_LC_100F; + + if (speed & ALX_LINK_SPEED_100_HALF) + link_cap |= LX_LC_100H; + + if (speed & ALX_LINK_SPEED_10_FULL) + link_cap |= LX_LC_10F; + + if (speed & ALX_LINK_SPEED_10_HALF) + link_cap |= LX_LC_10H; + + if (l1c_init_phy_spdfc(hw, autoneg, link_cap, fc)) { + alx_hw_err(hw, "error when init phy speed and fc\n"); + retval = -EINVAL; + } + + return retval; +} + + +static int alc_setup_phy_link_speed(struct alx_hw *hw, u32 speed, + bool autoneg, bool fc) +{ + /* + * Clear autoneg_advertised and set new values based on input link + * speed. + */ + hw->autoneg_advertised = 0; + + if (speed & ALX_LINK_SPEED_1GB_FULL) + hw->autoneg_advertised |= ALX_LINK_SPEED_1GB_FULL; + + if (speed & ALX_LINK_SPEED_100_FULL) + hw->autoneg_advertised |= ALX_LINK_SPEED_100_FULL; + + if (speed & ALX_LINK_SPEED_100_HALF) + hw->autoneg_advertised |= ALX_LINK_SPEED_100_HALF; + + if (speed & ALX_LINK_SPEED_10_FULL) + hw->autoneg_advertised |= ALX_LINK_SPEED_10_FULL; + + if (speed & ALX_LINK_SPEED_10_HALF) + hw->autoneg_advertised |= ALX_LINK_SPEED_10_HALF; + + return alc_setup_phy_link(hw, hw->autoneg_advertised, + autoneg, fc); +} + + +static int alc_check_phy_link(struct alx_hw *hw, u32 *speed, bool *link_up) +{ + u16 bmsr, giga; + int retval; + + alc_read_phy_reg(hw, MII_BMSR, &bmsr); + retval = alc_read_phy_reg(hw, MII_BMSR, &bmsr); + if (retval) + return retval; + + if (!(bmsr & BMSR_LSTATUS)) { + *link_up = false; + *speed = ALX_LINK_SPEED_UNKNOWN; + return 0; + } + *link_up = true; + + /* Read PHY Specific Status Register (17) */ + retval = alc_read_phy_reg(hw, L1C_MII_GIGA_PSSR, &giga); + if (retval) + return retval; + + + if (!(giga & L1C_GIGA_PSSR_SPD_DPLX_RESOLVED)) { + alx_hw_err(hw, "error for speed duplex resolved\n"); + return -EINVAL; + } + + switch (giga & L1C_GIGA_PSSR_SPEED) { + case L1C_GIGA_PSSR_1000MBS: + if (giga & L1C_GIGA_PSSR_DPLX) + *speed = ALX_LINK_SPEED_1GB_FULL; + else + alx_hw_err(hw, "1000M half is invalid\n"); + break; + case L1C_GIGA_PSSR_100MBS: + if (giga & L1C_GIGA_PSSR_DPLX) + *speed = ALX_LINK_SPEED_100_FULL; + else + *speed = ALX_LINK_SPEED_100_HALF; + break; + case L1C_GIGA_PSSR_10MBS: + if (giga & L1C_GIGA_PSSR_DPLX) + *speed = ALX_LINK_SPEED_10_FULL; + else + *speed = ALX_LINK_SPEED_10_HALF; + break; + default: + *speed = ALX_LINK_SPEED_UNKNOWN; + retval = -EINVAL; + break; + } + + return retval; +} + + +/* + * 1. stop_mac + * 2. reset mac & dma by reg1400(MASTER) + * 3. control speed/duplex, hash-alg + * 4. clock switch setting + */ +static int alc_reset_mac(struct alx_hw *hw) +{ + int retval = 0; + + if (l1c_reset_mac(hw)) { + alx_hw_err(hw, "error when reset mac\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alc_start_mac(struct alx_hw *hw) +{ + u16 en_ctrl = 0; + int retval = 0; + + /* set link speed param */ + switch (hw->link_speed) { + case ALX_LINK_SPEED_1GB_FULL: + en_ctrl |= LX_MACSPEED_1000; + /* fall through */ + case ALX_LINK_SPEED_100_FULL: + case ALX_LINK_SPEED_10_FULL: + en_ctrl |= LX_MACDUPLEX_FULL; + break; + } + + /* set fc param*/ + switch (hw->cur_fc_mode) { + case alx_fc_full: + en_ctrl |= LX_FC_RXEN; /* Flow Control RX Enable */ + en_ctrl |= LX_FC_TXEN; /* Flow Control TX Enable */ + break; + case alx_fc_rx_pause: + en_ctrl |= LX_FC_RXEN; /* Flow Control RX Enable */ + break; + case alx_fc_tx_pause: + en_ctrl |= LX_FC_TXEN; /* Flow Control TX Enable */ + break; + default: + break; + } + + if (hw->fc_single_pause) + en_ctrl |= LX_SINGLE_PAUSE; + + + en_ctrl |= LX_FLT_DIRECT; /* RX Enable; and TX Always Enable */ + en_ctrl |= LX_FLT_BROADCAST; /* RX Broadcast Enable */ + en_ctrl |= LX_ADD_FCS; + + if (CHK_HW_FLAG(VLANSTRIP_EN)) + en_ctrl |= LX_VLAN_STRIP; + + if (CHK_HW_FLAG(PROMISC_EN)) + en_ctrl |= LX_FLT_PROMISC; + + if (CHK_HW_FLAG(MULTIALL_EN)) + en_ctrl |= LX_FLT_MULTI_ALL; + + if (CHK_HW_FLAG(LOOPBACK_EN)) + en_ctrl |= LX_LOOPBACK; + + if (l1c_enable_mac(hw, true, en_ctrl)) { + alx_hw_err(hw, "error when start mac\n"); + retval = -EINVAL; + } + return retval; +} + + +/* + * 1. stop RXQ (reg15A0) and TXQ (reg1590) + * 2. stop MAC (reg1480) + */ +static int alc_stop_mac(struct alx_hw *hw) +{ + int retval = 0; + + if (l1c_enable_mac(hw, false, 0)) { + alx_hw_err(hw, "error when stop mac\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alc_config_mac(struct alx_hw *hw, u16 rxbuf_sz, u16 rx_qnum, + u16 rxring_sz, u16 tx_qnum, u16 txring_sz) +{ + u8 *addr; + + u32 txmem_hi, txmem_lo[4]; + + u32 rxmem_hi, rfdmem_lo, rrdmem_lo; + + u16 smb_timer, mtu_with_eth, int_mod; + bool hash_legacy; + + int i; + int retval = 0; + + addr = hw->mac_addr; + + txmem_hi = ALX_DMA_ADDR_HI(hw->tpdma[0]); + for (i = 0; i < tx_qnum; i++) + txmem_lo[i] = ALX_DMA_ADDR_LO(hw->tpdma[i]); + + + rxmem_hi = ALX_DMA_ADDR_HI(hw->rfdma[0]); + rfdmem_lo = ALX_DMA_ADDR_LO(hw->rfdma[0]); + rrdmem_lo = ALX_DMA_ADDR_LO(hw->rrdma[0]); + + + smb_timer = (u16)hw->smb_timer; + mtu_with_eth = hw->mtu + ALX_ETH_LENGTH_OF_HEADER; + int_mod = hw->imt; + + hash_legacy = true; + + if (l1c_init_mac(hw, addr, txmem_hi, txmem_lo, tx_qnum, txring_sz, + rxmem_hi, rfdmem_lo, rrdmem_lo, rxring_sz, rxbuf_sz, + smb_timer, mtu_with_eth, int_mod, hash_legacy)) { + alx_hw_err(hw, "error when config mac\n"); + retval = -EINVAL; + } + + return retval; +} + + +/** + * alc_get_mac_addr + * @hw: pointer to hardware structure + **/ +static int alc_get_mac_addr(struct alx_hw *hw, u8 *addr) +{ + int retval = 0; + + if (l1c_get_perm_macaddr(hw, addr)) { + alx_hw_err(hw, "error when get permanent mac address\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alc_reset_pcie(struct alx_hw *hw, bool l0s_en, bool l1_en) +{ + int retval = 0; + + if (!CHK_HW_FLAG(L0S_CAP)) + l0s_en = false; + + if (l0s_en) + SET_HW_FLAG(L0S_EN); + else + CLI_HW_FLAG(L0S_EN); + + + if (!CHK_HW_FLAG(L1_CAP)) + l1_en = false; + + if (l1_en) + SET_HW_FLAG(L1_EN); + else + CLI_HW_FLAG(L1_EN); + + if (l1c_reset_pcie(hw, l0s_en, l1_en)) { + alx_hw_err(hw, "error when reset pcie\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alc_config_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en) +{ + u8 link_stat; + int retval = 0; + + if (!CHK_HW_FLAG(L0S_CAP)) + l0s_en = false; + + if (l0s_en) + SET_HW_FLAG(L0S_EN); + else + CLI_HW_FLAG(L0S_EN); + + if (!CHK_HW_FLAG(L1_CAP)) + l1_en = false; + + if (l1_en) + SET_HW_FLAG(L1_EN); + else + CLI_HW_FLAG(L1_EN); + + link_stat = hw->link_up ? LX_LC_ALL : 0; + if (l1c_enable_aspm(hw, l0s_en, l1_en, link_stat)) { + alx_hw_err(hw, "error when enable aspm\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alc_config_wol(struct alx_hw *hw, u32 wufc) +{ + u32 wol = 0; + + /* turn on magic packet event */ + if (wufc & ALX_WOL_MAGIC) { + wol |= L1C_WOL0_MAGIC_EN | L1C_WOL0_PME_MAGIC_EN; + if (hw->mac_type == alx_mac_l2cb_v1 && + hw->pci_revid == ALX_REV_ID_AR8152_V1_1) { + wol |= L1C_WOL0_PATTERN_EN | L1C_WOL0_PME_PATTERN_EN; + } + /* magic packet maybe Broadcast&multicast&Unicast frame + * move to l1c_powersaving + */ + } + + /* turn on link up event */ + if (wufc & ALX_WOL_PHY) { + wol |= L1C_WOL0_LINK_EN | L1C_WOL0_PME_LINK; + /* only link up can wake up */ + alc_write_phy_reg(hw, L1C_MII_IER, L1C_IER_LINK_UP); + } + + alx_mem_w32(hw, L1C_WOL0, wol); + return 0; +} + + +static int alc_config_mac_ctrl(struct alx_hw *hw) +{ + u32 mac; + + alx_mem_r32(hw, L1C_MAC_CTRL, &mac); + + /* enable/disable VLAN tag insert,strip */ + if (CHK_HW_FLAG(VLANSTRIP_EN)) + mac |= L1C_MAC_CTRL_VLANSTRIP; + else + mac &= ~L1C_MAC_CTRL_VLANSTRIP; + + if (CHK_HW_FLAG(PROMISC_EN)) + mac |= L1C_MAC_CTRL_PROMISC_EN; + else + mac &= ~L1C_MAC_CTRL_PROMISC_EN; + + if (CHK_HW_FLAG(MULTIALL_EN)) + mac |= L1C_MAC_CTRL_MULTIALL_EN; + else + mac &= ~L1C_MAC_CTRL_MULTIALL_EN; + + if (CHK_HW_FLAG(LOOPBACK_EN)) + mac |= L1C_MAC_CTRL_LPBACK_EN; + else + mac &= ~L1C_MAC_CTRL_LPBACK_EN; + + alx_mem_w32(hw, L1C_MAC_CTRL, mac); + return 0; +} + + +static int alc_config_pow_save(struct alx_hw *hw, u32 speed, bool wol_en, + bool tx_en, bool rx_en, bool pws_en) +{ + u8 wire_spd = LX_LC_10H; + int retval = 0; + + switch (speed) { + case ALX_LINK_SPEED_UNKNOWN: + case ALX_LINK_SPEED_10_HALF: + wire_spd = LX_LC_10H; + break; + case ALX_LINK_SPEED_10_FULL: + wire_spd = LX_LC_10F; + break; + case ALX_LINK_SPEED_100_HALF: + wire_spd = LX_LC_100H; + break; + case ALX_LINK_SPEED_100_FULL: + wire_spd = LX_LC_100F; + break; + case ALX_LINK_SPEED_1GB_FULL: + wire_spd = LX_LC_1000F; + break; + } + + if (l1c_powersaving(hw, wire_spd, wol_en, tx_en, rx_en, pws_en)) { + alx_hw_err(hw, "error when set power saving\n"); + retval = -EINVAL; + } + return retval; +} + + +/* RAR, Multicast, VLAN */ +static int alc_set_mac_addr(struct alx_hw *hw, u8 *addr) +{ + u32 sta; + + /* + * for example: 00-0B-6A-F6-00-DC + * 0<-->6AF600DC, 1<-->000B. + */ + + /* low dword */ + sta = (((u32)addr[2]) << 24) | (((u32)addr[3]) << 16) | + (((u32)addr[4]) << 8) | (((u32)addr[5])) ; + alx_mem_w32(hw, L1C_STAD0, sta); + + /* hight dword */ + sta = (((u32)addr[0]) << 8) | (((u32)addr[1])) ; + alx_mem_w32(hw, L1C_STAD1, sta); + return 0; +} + + +static int alc_set_mc_addr(struct alx_hw *hw, u8 *addr) +{ + u32 crc32, bit, reg, mta; + + /* + * set hash value for a multicast address hash calcu processing. + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + */ + crc32 = ALX_ETH_CRC(addr, ALX_ETH_LENGTH_OF_ADDRESS); + + /* + * The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + reg = (crc32 >> 31) & 0x1; + bit = (crc32 >> 26) & 0x1F; + + alx_mem_r32(hw, L1C_HASH_TBL0 + (reg<<2), &mta); + mta |= (0x1 << bit); + alx_mem_w32(hw, L1C_HASH_TBL0 + (reg<<2), mta); + return 0; +} + + +static int alc_clear_mc_addr(struct alx_hw *hw) +{ + alx_mem_w32(hw, L1C_HASH_TBL0, 0); + alx_mem_w32(hw, L1C_HASH_TBL1, 0); + return 0; +} + + +/* RTX */ +static int alc_config_tx(struct alx_hw *hw) +{ + return 0; +} + + +/* INTR */ +static int alc_ack_phy_intr(struct alx_hw *hw) +{ + u16 isr; + return alc_read_phy_reg(hw, L1C_MII_ISR, &isr); +} + + +static int alc_enable_legacy_intr(struct alx_hw *hw) +{ + alx_mem_w32(hw, L1C_ISR, ~((u32) L1C_ISR_DIS)); + alx_mem_w32(hw, L1C_IMR, hw->intr_mask); + return 0; +} + + +static int alc_disable_legacy_intr(struct alx_hw *hw) +{ + alx_mem_w32(hw, L1C_ISR, L1C_ISR_DIS); + alx_mem_w32(hw, L1C_IMR, 0); + alx_mem_flush(hw); + return 0; +} + + +/* + * NV Ram + */ +static int alc_check_nvram(struct alx_hw *hw, bool *exist) +{ + *exist = false; + return 0; +} + + +static int alc_read_nvram(struct alx_hw *hw, u16 offset, u32 *data) +{ + int i; + u32 ectrl1, ectrl2, edata; + int retval = 0; + + if (offset & 0x3) + return retval; /* address do not align */ + + alx_mem_r32(hw, L1C_EFUSE_CTRL2, &ectrl2); + if (!(ectrl2 & L1C_EFUSE_CTRL2_CLK_EN)) + alx_mem_w32(hw, L1C_EFUSE_CTRL2, ectrl2|L1C_EFUSE_CTRL2_CLK_EN); + + alx_mem_w32(hw, L1C_EFUSE_DATA, 0); + ectrl1 = FIELDL(L1C_EFUSE_CTRL_ADDR, offset); + alx_mem_w32(hw, L1C_EFUSE_CTRL, ectrl1); + + for (i = 0; i < 10; i++) { + udelay(100); + alx_mem_r32(hw, L1C_EFUSE_CTRL, &ectrl1); + if (ectrl1 & L1C_EFUSE_CTRL_FLAG) + break; + } + if (ectrl1 & L1C_EFUSE_CTRL_FLAG) { + alx_mem_r32(hw, L1C_EFUSE_CTRL, &ectrl1); + alx_mem_r32(hw, L1C_EFUSE_DATA, &edata); + *data = LX_SWAP_DW((ectrl1 << 16) | (edata >> 16)); + return retval; + } + + if (!(ectrl2 & L1C_EFUSE_CTRL2_CLK_EN)) + alx_mem_w32(hw, L1C_EFUSE_CTRL2, ectrl2); + + return retval; +} + + +static int alc_write_nvram(struct alx_hw *hw, u16 offset, u32 data) +{ + return 0; +} + + +/* fc */ +static int alc_get_fc_mode(struct alx_hw *hw, enum alx_fc_mode *mode) +{ + u16 bmsr, giga; + int i; + int retval = 0; + + for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { + alc_read_phy_reg(hw, MII_BMSR, &bmsr); + alc_read_phy_reg(hw, MII_BMSR, &bmsr); + if (bmsr & BMSR_LSTATUS) { + /* Read phy Specific Status Register (17) */ + retval = alc_read_phy_reg(hw, L1C_MII_GIGA_PSSR, &giga); + if (retval) + return retval; + + if (!(giga & L1C_GIGA_PSSR_SPD_DPLX_RESOLVED)) { + alx_hw_err(hw, + "error for speed duplex resolved\n"); + return -EINVAL; + } + + if ((giga & L1C_GIGA_PSSR_FC_TXEN) && + (giga & L1C_GIGA_PSSR_FC_RXEN)) { + *mode = alx_fc_full; + } else if (giga & L1C_GIGA_PSSR_FC_TXEN) { + *mode = alx_fc_tx_pause; + } else if (giga & L1C_GIGA_PSSR_FC_RXEN) { + *mode = alx_fc_rx_pause; + } else { + *mode = alx_fc_none; + } + break; + } + mdelay(100); + } + + if (i == ALX_MAX_SETUP_LNK_CYCLE) { + alx_hw_err(hw, "error when get flow control mode\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alc_config_fc(struct alx_hw *hw) +{ + u32 mac; + int retval = 0; + + if (hw->disable_fc_autoneg) { + hw->fc_was_autonegged = false; + hw->cur_fc_mode = hw->req_fc_mode; + } else { + hw->fc_was_autonegged = true; + retval = alc_get_fc_mode(hw, &hw->cur_fc_mode); + if (retval) + return retval; + } + + alx_mem_r32(hw, L1C_MAC_CTRL, &mac); + + switch (hw->cur_fc_mode) { + case alx_fc_none: /* 0 */ + mac &= ~(L1C_MAC_CTRL_RXFC_EN | L1C_MAC_CTRL_TXFC_EN); + break; + case alx_fc_rx_pause: /* 1 */ + mac &= ~L1C_MAC_CTRL_TXFC_EN; + mac |= L1C_MAC_CTRL_RXFC_EN; + break; + case alx_fc_tx_pause: /* 2 */ + mac |= L1C_MAC_CTRL_TXFC_EN; + mac &= ~L1C_MAC_CTRL_RXFC_EN; + break; + case alx_fc_full: /* 3 */ + case alx_fc_default: /* 4 */ + mac |= (L1C_MAC_CTRL_TXFC_EN | L1C_MAC_CTRL_RXFC_EN); + break; + default: + alx_hw_err(hw, "flow control param set incorrectly\n"); + return -EINVAL; + } + + alx_mem_w32(hw, L1C_MAC_CTRL, mac); + return retval; +} + + +/* ethtool */ +static int alc_get_ethtool_regs(struct alx_hw *hw, void *buff) +{ + int i; + u32 *val = buff; + static const int reg[] = { + /* 0 */ + L1C_LNK_CAP, L1C_PMCTRL, L1C_HALFD, L1C_SLD, L1C_MASTER, + L1C_MANU_TIMER, L1C_IRQ_MODU_TIMER, L1C_PHY_CTRL, L1C_LNK_CTRL, + L1C_MAC_STS, + + /* 10 */ + L1C_MDIO, L1C_SERDES, L1C_MAC_CTRL, L1C_GAP, L1C_STAD0, + L1C_STAD1, L1C_HASH_TBL0, L1C_HASH_TBL1, L1C_RXQ0, L1C_RXQ1, + + /* 20 */ + L1C_RXQ2, L1C_RXQ3, L1C_TXQ0, L1C_TXQ1, L1C_TXQ2, L1C_MTU, + L1C_WOL0, L1C_WOL1, L1C_WOL2, + }; + + for (i = 0; i < ARRAY_SIZE(reg); i++) + alx_mem_r32(hw, reg[i], &val[i]); + return 0; +} + + +/******************************************************************************/ +static int alc_get_hw_capabilities(struct alx_hw *hw) +{ + /* + * because there is some hareware error on some platforms, just disable + * this feature when link connected. + */ + CLI_HW_FLAG(L0S_CAP); + CLI_HW_FLAG(L1_CAP); + + if ((hw->mac_type == alx_mac_l1c) || + (hw->mac_type == alx_mac_l1d_v1) || + (hw->mac_type == alx_mac_l1d_v2)) + SET_HW_FLAG(GIGA_CAP); + + SET_HW_FLAG(PWSAVE_CAP); + return 0; +} + + +/* alc_set_hw_info */ +static int alc_set_hw_infos(struct alx_hw *hw) +{ + hw->rxstat_reg = 0x1700; + hw->rxstat_sz = 0x60; + hw->txstat_reg = 0x1760; + hw->txstat_sz = 0x68; + + hw->rx_prod_reg[0] = L1C_RFD_PIDX; + hw->rx_cons_reg[0] = L1C_RFD_CIDX; + + hw->tx_prod_reg[0] = L1C_TPD_PRI0_PIDX; + hw->tx_cons_reg[0] = L1C_TPD_PRI0_CIDX; + hw->tx_prod_reg[1] = L1C_TPD_PRI1_PIDX; + hw->tx_cons_reg[1] = L1C_TPD_PRI1_CIDX; + + hw->hwreg_sz = 0x80; + hw->eeprom_sz = 0; + + return 0; +} + + +/** + * alc_init_hw_callbacks - Inits func ptrs and MAC type + * @hw: pointer to hardware structure + **/ +int alc_init_hw_callbacks(struct alx_hw *hw) +{ + /* NIC */ + hw->cbs.identify_nic = &alc_identify_nic; + /* MAC*/ + hw->cbs.reset_mac = &alc_reset_mac; + hw->cbs.start_mac = &alc_start_mac; + hw->cbs.stop_mac = &alc_stop_mac; + hw->cbs.config_mac = &alc_config_mac; + hw->cbs.get_mac_addr = &alc_get_mac_addr; + hw->cbs.set_mac_addr = &alc_set_mac_addr; + hw->cbs.set_mc_addr = &alc_set_mc_addr; + hw->cbs.clear_mc_addr = &alc_clear_mc_addr; + + /* PHY */ + hw->cbs.init_phy = &alc_init_phy; + hw->cbs.reset_phy = &alc_reset_phy; + hw->cbs.read_phy_reg = &alc_read_phy_reg; + hw->cbs.write_phy_reg = &alc_write_phy_reg; + hw->cbs.check_phy_link = &alc_check_phy_link; + hw->cbs.setup_phy_link = &alc_setup_phy_link; + hw->cbs.setup_phy_link_speed = &alc_setup_phy_link_speed; + + /* Interrupt */ + hw->cbs.ack_phy_intr = &alc_ack_phy_intr; + hw->cbs.enable_legacy_intr = &alc_enable_legacy_intr; + hw->cbs.disable_legacy_intr = &alc_disable_legacy_intr; + + /* Configure */ + hw->cbs.config_tx = &alc_config_tx; + hw->cbs.config_fc = &alc_config_fc; + hw->cbs.config_aspm = &alc_config_aspm; + hw->cbs.config_wol = &alc_config_wol; + hw->cbs.config_mac_ctrl = &alc_config_mac_ctrl; + hw->cbs.config_pow_save = &alc_config_pow_save; + hw->cbs.reset_pcie = &alc_reset_pcie; + + /* NVRam */ + hw->cbs.check_nvram = &alc_check_nvram; + hw->cbs.read_nvram = &alc_read_nvram; + hw->cbs.write_nvram = &alc_write_nvram; + + /* Others */ + hw->cbs.get_ethtool_regs = alc_get_ethtool_regs; + + /* get hw capabilitites to HW->flags */ + alc_get_hw_capabilities(hw); + alc_set_hw_infos(hw); + + alx_hw_info(hw, "HW Flags = 0x%x\n", hw->flags); + return 0; +} + --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alc_hw.c @@ -0,0 +1,1087 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "alc_hw.h" + + + +/* + * get permanent mac address + * 0: success + * non-0:fail + */ +u16 l1c_get_perm_macaddr(struct alx_hw *hw, u8 *addr) +{ + u32 val, otp_ctrl, otp_flag, mac0, mac1; + u16 i; + u16 phy_val; + + /* get it from register first */ + alx_mem_r32(hw, L1C_STAD0, &mac0); + alx_mem_r32(hw, L1C_STAD1, &mac1); + + *(u32 *)(addr + 2) = LX_SWAP_DW(mac0); + *(u16 *)addr = (u16)LX_SWAP_W((u16)mac1); + + if (macaddr_valid(addr)) + return 0; + + alx_mem_r32(hw, L1C_TWSI_DBG, &val); + alx_mem_r32(hw, L1C_EFUSE_CTRL2, &otp_ctrl); + alx_mem_r32(hw, L1C_MASTER, &otp_flag); + + if ((val & L1C_TWSI_DBG_DEV_EXIST) != 0 || + (otp_flag & L1C_MASTER_OTP_FLG) != 0) { + /* nov-memory exist, do software-autoload */ + /* enable OTP_CLK for L1C */ + if (hw->pci_devid == L1C_DEV_ID || + hw->pci_devid == L2C_DEV_ID) { + if ((otp_ctrl & L1C_EFUSE_CTRL2_CLK_EN) != 0) { + alx_mem_w32(hw, L1C_EFUSE_CTRL2, + otp_ctrl | L1C_EFUSE_CTRL2_CLK_EN); + udelay(5); + } + } + /* raise voltage temporally for L2CB/L1D */ + if (hw->pci_devid == L2CB_DEV_ID || + hw->pci_devid == L2CB2_DEV_ID) { + /* clear bit[7] of debugport 00 */ + l1c_read_phydbg(hw, true, L1C_MIIDBG_ANACTRL, + &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_ANACTRL, + phy_val & ~L1C_ANACTRL_HB_EN); + /* set bit[3] of debugport 3B */ + l1c_read_phydbg(hw, true, L1C_MIIDBG_VOLT_CTRL, + &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_VOLT_CTRL, + phy_val | L1C_VOLT_CTRL_SWLOWEST); + udelay(20); + } + /* do load */ + alx_mem_r32(hw, L1C_SLD, &val); + alx_mem_w32(hw, L1C_SLD, val | L1C_SLD_START); + for (i = 0; i < L1C_SLD_MAX_TO; i++) { + mdelay(1); + alx_mem_r32(hw, L1C_SLD, &val); + if ((val & L1C_SLD_START) == 0) + break; + } + /* disable OTP_CLK for L1C */ + if (hw->pci_devid == L1C_DEV_ID || + hw->pci_devid == L2C_DEV_ID) { + alx_mem_w32(hw, L1C_EFUSE_CTRL2, + otp_ctrl & ~L1C_EFUSE_CTRL2_CLK_EN); + udelay(5); + } + /* low voltage */ + if (hw->pci_devid == L2CB_DEV_ID || + hw->pci_devid == L2CB2_DEV_ID) { + /* set bit[7] of debugport 00 */ + l1c_read_phydbg(hw, true, L1C_MIIDBG_ANACTRL, + &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_ANACTRL, + phy_val | L1C_ANACTRL_HB_EN); + /* clear bit[3] of debugport 3B */ + l1c_read_phydbg(hw, true, L1C_MIIDBG_VOLT_CTRL, + &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_VOLT_CTRL, + phy_val & ~L1C_VOLT_CTRL_SWLOWEST); + udelay(20); + } + if (i == L1C_SLD_MAX_TO) + goto out; + } else { + if (hw->pci_devid == L1C_DEV_ID || + hw->pci_devid == L2C_DEV_ID) { + alx_mem_w32(hw, L1C_EFUSE_CTRL2, + otp_ctrl & ~L1C_EFUSE_CTRL2_CLK_EN); + udelay(5); + } + } + + alx_mem_r32(hw, L1C_STAD0, &mac0); + alx_mem_r32(hw, L1C_STAD1, &mac1); + + *(u32 *)(addr + 2) = LX_SWAP_DW(mac0); + *(u16 *)addr = (u16)LX_SWAP_W((u16)mac1); + + if (macaddr_valid(addr)) + return 0; + +out: + return LX_ERR_ALOAD; +} + +/* + * reset mac & dma + * return + * 0: success + * non-0:fail + */ +u16 l1c_reset_mac(struct alx_hw *hw) +{ + u32 val, mrst_val; + u16 ret; + u16 i; + + /* disable all interrupts, RXQ/TXQ */ + alx_mem_w32(hw, L1C_IMR, 0); + alx_mem_w32(hw, L1C_ISR, L1C_ISR_DIS); + + ret = l1c_enable_mac(hw, false, 0); + if (ret != 0) + return ret; + /* reset whole mac safely. OOB is meaningful for L1D only */ + alx_mem_r32(hw, L1C_MASTER, &mrst_val); + mrst_val |= L1C_MASTER_OOB_DIS; + alx_mem_w32(hw, L1C_MASTER, mrst_val | L1C_MASTER_DMA_MAC_RST); + + /* make sure it's idle */ + for (i = 0; i < L1C_DMA_MAC_RST_TO; i++) { + alx_mem_r32(hw, L1C_MASTER, &val); + if ((val & L1C_MASTER_DMA_MAC_RST) == 0) + break; + udelay(20); + } + if (i == L1C_DMA_MAC_RST_TO) + return LX_ERR_RSTMAC; + /* keep the old value */ + alx_mem_w32(hw, L1C_MASTER, mrst_val & ~L1C_MASTER_DMA_MAC_RST); + + /* driver control speed/duplex, hash-alg */ + alx_mem_r32(hw, L1C_MAC_CTRL, &val); + alx_mem_w32(hw, L1C_MAC_CTRL, val | L1C_MAC_CTRL_WOLSPED_SWEN); + + /* clk switch setting */ + alx_mem_r32(hw, L1C_SERDES, &val); + switch (hw->pci_devid) { + case L2CB_DEV_ID: + alx_mem_w32(hw, L1C_SERDES, val & ~L1C_SERDES_PHYCLK_SLWDWN); + break; + case L2CB2_DEV_ID: + case L1D2_DEV_ID: + alx_mem_w32(hw, L1C_SERDES, + val | L1C_SERDES_PHYCLK_SLWDWN | + L1C_SERDES_MACCLK_SLWDWN); + break; + default: + /* the defalut value of default product is OFF */; + } + + return 0; +} + +/* reset phy + * return + * 0: success + * non-0:fail + */ +u16 l1c_reset_phy(struct alx_hw *hw, bool pws_en, bool az_en, bool ptp_en) +{ + u32 val; + u16 i, phy_val; + + ptp_en = ptp_en; + + /* reset PHY core */ + alx_mem_r32(hw, L1C_PHY_CTRL, &val); + val &= ~(L1C_PHY_CTRL_DSPRST_OUT | L1C_PHY_CTRL_IDDQ | + L1C_PHY_CTRL_GATE_25M | L1C_PHY_CTRL_POWER_DOWN | + L1C_PHY_CTRL_CLS); + val |= L1C_PHY_CTRL_RST_ANALOG; + + if (pws_en) + val |= (L1C_PHY_CTRL_HIB_PULSE | L1C_PHY_CTRL_HIB_EN); + else + val &= ~(L1C_PHY_CTRL_HIB_PULSE | L1C_PHY_CTRL_HIB_EN); + + alx_mem_w32(hw, L1C_PHY_CTRL, val); + udelay(10); /* 5us is enough */ + alx_mem_w32(hw, L1C_PHY_CTRL, val | L1C_PHY_CTRL_DSPRST_OUT); + + /* delay 800us */ + for (i = 0; i < L1C_PHY_CTRL_DSPRST_TO; i++) + udelay(10); + + /* switch clock */ + if (hw->pci_devid == L2CB_DEV_ID) { + l1c_read_phydbg(hw, true, L1C_MIIDBG_CFGLPSPD, &phy_val); + /* clear bit13 */ + l1c_write_phydbg(hw, true, L1C_MIIDBG_CFGLPSPD, + phy_val & ~L1C_CFGLPSPD_RSTCNT_CLK125SW); + } + + /* fix tx-half-amp issue */ + if (hw->pci_devid == L2CB_DEV_ID || hw->pci_devid == L2CB2_DEV_ID) { + l1c_read_phydbg(hw, true, L1C_MIIDBG_CABLE1TH_DET, &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_CABLE1TH_DET, + phy_val | L1C_CABLE1TH_DET_EN); /* set bit15 */ + } + + if (pws_en) { + /* clear bit[3] of debugport 3B to 0, + * lower voltage to save power */ + if (hw->pci_devid == L2CB_DEV_ID || + hw->pci_devid == L2CB2_DEV_ID) { + l1c_read_phydbg(hw, true, L1C_MIIDBG_VOLT_CTRL, + &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_VOLT_CTRL, + phy_val & ~L1C_VOLT_CTRL_SWLOWEST); + } + /* power saving config */ + l1c_write_phydbg(hw, true, L1C_MIIDBG_LEGCYPS, + (hw->pci_devid == L1D_DEV_ID || + hw->pci_devid == L1D2_DEV_ID) ? + L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF); + /* hib */ + l1c_write_phydbg(hw, true, L1C_MIIDBG_SYSMODCTRL, + L1C_SYSMODCTRL_IECHOADJ_DEF); + } else { + /*dis powersaving */ + l1c_read_phydbg(hw, true, L1C_MIIDBG_LEGCYPS, &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_LEGCYPS, + phy_val & ~L1C_LEGCYPS_EN); + /* disable hibernate */ + l1c_read_phydbg(hw, true, L1C_MIIDBG_HIBNEG, &phy_val); + l1c_write_phydbg(hw, true, L1C_MIIDBG_HIBNEG, + phy_val & ~L1C_HIBNEG_PSHIB_EN); + } + + /* az is only for l2cbv2 / l1dv1 /l1dv2 */ + if (hw->pci_devid == L1D_DEV_ID || + hw->pci_devid == L1D2_DEV_ID || + hw->pci_devid == L2CB2_DEV_ID) { + if (az_en) { + switch (hw->pci_devid) { + case L2CB2_DEV_ID: + alx_mem_w32(hw, L1C_LPI_DECISN_TIMER, + L1C_LPI_DESISN_TIMER_L2CB); + /* az enable 100M */ + l1c_write_phy(hw, true, L1C_MIIEXT_ANEG, true, + L1C_MIIEXT_LOCAL_EEEADV, + L1C_LOCAL_EEEADV_100BT); + /* az long wake threshold */ + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL5, + L1C_AZCTRL5_WAKE_LTH_L2CB); + /* az short wake threshold */ + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL4, + L1C_AZCTRL4_WAKE_STH_L2CB); + + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_CLDCTRL3, + L1C_CLDCTRL3_L2CB); + + /* bit7 set to 0, otherwise ping fail */ + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_CLDCTRL7, + L1C_CLDCTRL7_L2CB); + + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL2, + L1C_AZCTRL2_L2CB); + break; + + case L1D_DEV_ID: + l1c_write_phydbg(hw, true, + L1C_MIIDBG_AZ_ANADECT, L1C_AZ_ANADECT_DEF); + phy_val = hw->long_cable ? L1C_CLDCTRL3_L1D : + (L1C_CLDCTRL3_L1D & + ~(L1C_CLDCTRL3_BP_CABLE1TH_DET_GT | + L1C_CLDCTRL3_AZ_DISAMP)); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_CLDCTRL3, phy_val); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL, + L1C_AZCTRL_L1D); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL2, + L1C_AZCTRL2_L2CB); + break; + + case L1D2_DEV_ID: + l1c_write_phydbg(hw, true, + L1C_MIIDBG_AZ_ANADECT, + L1C_AZ_ANADECT_DEF); + phy_val = hw->long_cable ? L1C_CLDCTRL3_L1D : + (L1C_CLDCTRL3_L1D & + ~L1C_CLDCTRL3_BP_CABLE1TH_DET_GT); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_CLDCTRL3, phy_val); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL, + L1C_AZCTRL_L1D); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL2, + L1C_AZCTRL2_L1D2); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_AZCTRL6, + L1C_AZCTRL6_L1D2); + break; + } + } else { + alx_mem_r32(hw, L1C_LPI_CTRL, &val); + alx_mem_w32(hw, L1C_LPI_CTRL, val & ~L1C_LPI_CTRL_EN); + l1c_write_phy(hw, true, L1C_MIIEXT_ANEG, true, + L1C_MIIEXT_LOCAL_EEEADV, 0); + l1c_write_phy(hw, true, L1C_MIIEXT_PCS, true, + L1C_MIIEXT_CLDCTRL3, L1C_CLDCTRL3_L2CB); + } + } + + /* other debug port need to set */ + l1c_write_phydbg(hw, true, L1C_MIIDBG_ANACTRL, L1C_ANACTRL_DEF); + l1c_write_phydbg(hw, true, L1C_MIIDBG_SRDSYSMOD, L1C_SRDSYSMOD_DEF); + l1c_write_phydbg(hw, true, L1C_MIIDBG_TST10BTCFG, L1C_TST10BTCFG_DEF); + /* L1c, L2c, L1d, L2cb link fail inhibit + timer issue of L1c UNH-IOL test fail, set bit7*/ + l1c_write_phydbg(hw, true, L1C_MIIDBG_TST100BTCFG, + L1C_TST100BTCFG_DEF | L1C_TST100BTCFG_LITCH_EN); + + /* set phy interrupt mask */ + l1c_write_phy(hw, false, 0, true, + L1C_MII_IER, L1C_IER_LINK_UP | L1C_IER_LINK_DOWN); + + return 0; +} + + +/* reset pcie + * just reset pcie relative registers (pci command, clk, aspm...) + * return + * 0:success + * non-0:fail + */ +u16 l1c_reset_pcie(struct alx_hw *hw, bool l0s_en, bool l1_en) +{ + u32 val; + u16 val16; + u16 ret; + + /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ + alx_cfg_r16(hw, PCI_COMMAND, &val16); + if ((val16 & (PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)) == 0 || + (val16 & PCI_COMMAND_INTX_DISABLE) != 0) { + val16 = (u16)((val16 | (PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)) + & ~PCI_COMMAND_INTX_DISABLE); + alx_cfg_w16(hw, PCI_COMMAND, val16); + } + + /* Clear any PowerSaving Settings */ + alx_cfg_w16(hw, L1C_PM_CSR, 0); + + /* close write attr for some registes */ + alx_mem_r32(hw, L1C_LTSSM_CTRL, &val); + alx_mem_w32(hw, L1C_LTSSM_CTRL, val & ~L1C_LTSSM_WRO_EN); + + /* mask some pcie error bits */ + alx_mem_r32(hw, L1C_UE_SVRT, &val); + val &= ~(L1C_UE_SVRT_DLPROTERR | L1C_UE_SVRT_FCPROTERR); + alx_mem_w32(hw, L1C_UE_SVRT, val); + + /* pclk */ + alx_mem_r32(hw, L1C_MASTER, &val); + val &= ~L1C_MASTER_PCLKSEL_SRDS; + alx_mem_w32(hw, L1C_MASTER, val); + + /* Set 1000 bit 2, only used for L1c/L2c , WOL usage */ + if (hw->pci_devid == L1C_DEV_ID || hw->pci_devid == L2C_DEV_ID) { + alx_mem_r32(hw, L1C_PPHY_MISC1, &val); + alx_mem_w32(hw, L1C_PPHY_MISC1, val | L1C_PPHY_MISC1_RCVDET); + } else { /* other device should set bit 5 of reg1400 for WOL */ + if ((val & L1C_MASTER_WAKEN_25M) == 0) + alx_mem_w32(hw, L1C_MASTER, val | L1C_MASTER_WAKEN_25M); + } + /* l2cb 1.0*/ + if (hw->pci_devid == L2CB_DEV_ID && hw->pci_revid == L2CB_V10) { + alx_mem_r32(hw, L1C_PPHY_MISC2, &val); + FIELD_SETL(val, L1C_PPHY_MISC2_L0S_TH, + L1C_PPHY_MISC2_L0S_TH_L2CB1); + FIELD_SETL(val, L1C_PPHY_MISC2_CDR_BW, + L1C_PPHY_MISC2_CDR_BW_L2CB1); + alx_mem_w32(hw, L1C_PPHY_MISC2, val); + /* extend L1 sync timer, this will use more power, + * only for L2cb v1.0*/ + if (!hw->aps_en) { + alx_mem_r32(hw, L1C_LNK_CTRL, &val); + alx_mem_w32(hw, L1C_LNK_CTRL, + val | L1C_LNK_CTRL_EXTSYNC); + } + } + + /* l2cbv1.x & l1dv1.x */ + if (hw->pci_devid == L2CB_DEV_ID || hw->pci_devid == L1D_DEV_ID) { + alx_mem_r32(hw, L1C_PMCTRL, &val); + alx_mem_w32(hw, L1C_PMCTRL, val | L1C_PMCTRL_L0S_BUFSRX_EN); + /* clear vendor message for L1d & L2cb */ + alx_mem_r32(hw, L1C_DMA_DBG, &val); + alx_mem_w32(hw, L1C_DMA_DBG, val & ~L1C_DMA_DBG_VENDOR_MSG); + } + + /* hi-tx-perf */ + if (hw->hi_txperf) { + alx_mem_r32(hw, L1C_PPHY_MISC1, &val); + FIELD_SETL(val, L1C_PPHY_MISC1_NFTS, + L1C_PPHY_MISC1_NFTS_HIPERF); + alx_mem_w32(hw, L1C_PPHY_MISC1, val); + } + /* l0s, l1 setting */ + ret = l1c_enable_aspm(hw, l0s_en, l1_en, 0); + + udelay(10); + + return ret; +} + + +/* disable/enable MAC/RXQ/TXQ + * en + * true:enable + * false:disable + * return + * 0:success + * non-0-fail + */ +u16 l1c_enable_mac(struct alx_hw *hw, bool en, u16 en_ctrl) +{ + u32 rxq, txq, mac, val; + u16 i; + + alx_mem_r32(hw, L1C_RXQ0, &rxq); + alx_mem_r32(hw, L1C_TXQ0, &txq); + alx_mem_r32(hw, L1C_MAC_CTRL, &mac); + + if (en) { /* enable */ + alx_mem_w32(hw, L1C_RXQ0, rxq | L1C_RXQ0_EN); + alx_mem_w32(hw, L1C_TXQ0, txq | L1C_TXQ0_EN); + if ((en_ctrl & LX_MACSPEED_1000) != 0) { + FIELD_SETL(mac, L1C_MAC_CTRL_SPEED, + L1C_MAC_CTRL_SPEED_1000); + } else { + FIELD_SETL(mac, L1C_MAC_CTRL_SPEED, + L1C_MAC_CTRL_SPEED_10_100); + } + + test_set_or_clear(mac, en_ctrl, LX_MACDUPLEX_FULL, + L1C_MAC_CTRL_FULLD); + + /* rx filter */ + test_set_or_clear(mac, en_ctrl, LX_FLT_PROMISC, + L1C_MAC_CTRL_PROMISC_EN); + test_set_or_clear(mac, en_ctrl, LX_FLT_MULTI_ALL, + L1C_MAC_CTRL_MULTIALL_EN); + test_set_or_clear(mac, en_ctrl, LX_FLT_BROADCAST, + L1C_MAC_CTRL_BRD_EN); + test_set_or_clear(mac, en_ctrl, LX_FLT_DIRECT, + L1C_MAC_CTRL_RX_EN); + test_set_or_clear(mac, en_ctrl, LX_FC_TXEN, + L1C_MAC_CTRL_TXFC_EN); + test_set_or_clear(mac, en_ctrl, LX_FC_RXEN, + L1C_MAC_CTRL_RXFC_EN); + test_set_or_clear(mac, en_ctrl, LX_VLAN_STRIP, + L1C_MAC_CTRL_VLANSTRIP); + test_set_or_clear(mac, en_ctrl, LX_LOOPBACK, + L1C_MAC_CTRL_LPBACK_EN); + test_set_or_clear(mac, en_ctrl, LX_SINGLE_PAUSE, + L1C_MAC_CTRL_SPAUSE_EN); + test_set_or_clear(mac, en_ctrl, LX_ADD_FCS, + (L1C_MAC_CTRL_PCRCE | L1C_MAC_CTRL_CRCE)); + + alx_mem_w32(hw, L1C_MAC_CTRL, mac | L1C_MAC_CTRL_TX_EN); + } else { /* disable mac */ + alx_mem_w32(hw, L1C_RXQ0, rxq & ~L1C_RXQ0_EN); + alx_mem_w32(hw, L1C_TXQ0, txq & ~L1C_TXQ0_EN); + + /* waiting for rxq/txq be idle */ + for (i = 0; i < L1C_DMA_MAC_RST_TO; i++) {/* wait atmost 1ms */ + alx_mem_r32(hw, L1C_MAC_STS, &val); + if ((val & (L1C_MAC_STS_TXQ_BUSY | + L1C_MAC_STS_RXQ_BUSY)) == 0) { + break; + } + udelay(20); + } + if (L1C_DMA_MAC_RST_TO == i) + return LX_ERR_RSTMAC; + /* stop mac tx/rx */ + alx_mem_w32(hw, L1C_MAC_CTRL, + mac & ~(L1C_MAC_CTRL_RX_EN | L1C_MAC_CTRL_TX_EN)); + + for (i = 0; i < L1C_DMA_MAC_RST_TO; i++) { + alx_mem_r32(hw, L1C_MAC_STS, &val); + if ((val & L1C_MAC_STS_IDLE) == 0) + break; + udelay(10); + } + if (L1C_DMA_MAC_RST_TO == i) + return LX_ERR_RSTMAC; + } + + return 0; +} + + +/* enable/disable aspm support + * that will change settings for phy/mac/pcie + */ +u16 l1c_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en, u8 lnk_stat) +{ + u32 pmctrl; + bool linkon; + + linkon = (lnk_stat == LX_LC_10H || lnk_stat == LX_LC_10F || + lnk_stat == LX_LC_100H || lnk_stat == LX_LC_100F || + lnk_stat == LX_LC_1000F) ? true : false; + + alx_mem_r32(hw, L1C_PMCTRL, &pmctrl); + pmctrl &= ~(L1C_PMCTRL_L0S_EN | + L1C_PMCTRL_L1_EN | + L1C_PMCTRL_ASPM_FCEN); + FIELD_SETL(pmctrl, L1C_PMCTRL_LCKDET_TIMER, + L1C_PMCTRL_LCKDET_TIMER_DEF); + + /* l1 timer */ + if (hw->pci_devid == L2CB2_DEV_ID || hw->pci_devid == L1D2_DEV_ID) { + pmctrl &= ~L1D_PMCTRL_TXL1_AFTER_L0S; + FIELD_SETL(pmctrl, L1D_PMCTRL_L1_TIMER, + (lnk_stat == LX_LC_100H || + lnk_stat == LX_LC_100F || + lnk_stat == LX_LC_1000F) ? + L1D_PMCTRL_L1_TIMER_16US : 1); + } else { + FIELD_SETL(pmctrl, L1C_PMCTRL_L1_TIMER, + (lnk_stat == LX_LC_100H || + lnk_stat == LX_LC_100F || + lnk_stat == LX_LC_1000F) ? + ((hw->pci_devid == L2CB_DEV_ID) ? + L1C_PMCTRL_L1_TIMER_L2CB1 : L1C_PMCTRL_L1_TIMER_DEF + ) : 1); + } + if (l0s_en) { /* on/off l0s only if bios/system enable l0s */ + pmctrl |= (L1C_PMCTRL_L0S_EN | L1C_PMCTRL_ASPM_FCEN); + } + if (l1_en) { /* on/off l1 only if bios/system enable l1 */ + pmctrl |= (L1C_PMCTRL_L1_EN | L1C_PMCTRL_ASPM_FCEN); + } + + if (hw->pci_devid == L2CB_DEV_ID || hw->pci_devid == L1D_DEV_ID || + hw->pci_devid == L2CB2_DEV_ID || hw->pci_devid == L1D2_DEV_ID) { + /* If the pm_request_l1 time exceeds the value of this timer, + it will enter L0s instead of L1 for this ASPM request.*/ + FIELD_SETL(pmctrl, L1C_PMCTRL_L1REQ_TO, + L1C_PMCTRL_L1REG_TO_DEF); + + pmctrl |= L1C_PMCTRL_RCVR_WT_1US | /* wait 1us not 2ms */ + L1C_PMCTRL_L1_SRDSRX_PWD | /* pwd serdes */ + L1C_PMCTRL_L1_CLKSW_EN; + pmctrl &= ~(L1C_PMCTRL_L1_SRDS_EN | + L1C_PMCTRL_L1_SRDSPLL_EN| + L1C_PMCTRL_L1_BUFSRX_EN | + L1C_PMCTRL_SADLY_EN | + L1C_PMCTRL_HOTRST_WTEN); + /* disable l0s if linkdown or l2cbv1.x */ + if (!linkon || + (!hw->aps_en && hw->pci_devid == L2CB_DEV_ID)) { + pmctrl &= ~L1C_PMCTRL_L0S_EN; + } + } else { /* l1c */ + FIELD_SETL(pmctrl, L1C_PMCTRL_L1_TIMER, 0); + if (linkon) { + pmctrl |= L1C_PMCTRL_L1_SRDS_EN | + L1C_PMCTRL_L1_SRDSPLL_EN | + L1C_PMCTRL_L1_BUFSRX_EN; + pmctrl &= ~(L1C_PMCTRL_L1_SRDSRX_PWD| + L1C_PMCTRL_L1_CLKSW_EN | + L1C_PMCTRL_L0S_EN | + L1C_PMCTRL_L1_EN); + } else { + pmctrl |= L1C_PMCTRL_L1_CLKSW_EN; + pmctrl &= ~(L1C_PMCTRL_L1_SRDS_EN | + L1C_PMCTRL_L1_SRDSPLL_EN| + L1C_PMCTRL_L1_BUFSRX_EN | + L1C_PMCTRL_L0S_EN); + } + } + + alx_mem_w32(hw, L1C_PMCTRL, pmctrl); + + return 0; +} + + +/* initialize phy for speed / flow control + * lnk_cap + * if autoNeg, is link capability to tell the peer + * if force mode, is forced speed/duplex + */ +u16 l1c_init_phy_spdfc(struct alx_hw *hw, bool auto_neg, + u8 lnk_cap, bool fc_en) +{ + u16 adv, giga, cr; + u32 val; + u16 ret; + + /* clear flag */ + l1c_write_phy(hw, false, 0, false, L1C_MII_DBG_ADDR, 0); + alx_mem_r32(hw, L1C_DRV, &val); + FIELD_SETL(val, LX_DRV_PHY, 0); + + if (auto_neg) { + adv = L1C_ADVERTISE_DEFAULT_CAP & ~L1C_ADVERTISE_SPEED_MASK; + giga = L1C_GIGA_CR_1000T_DEFAULT_CAP & + ~L1C_GIGA_CR_1000T_SPEED_MASK; + val |= LX_DRV_PHY_AUTO; + if (!fc_en) + adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + else + val |= LX_DRV_PHY_FC; + if ((LX_LC_10H & lnk_cap) != 0) { + adv |= ADVERTISE_10HALF; + val |= LX_DRV_PHY_10; + } + if ((LX_LC_10F & lnk_cap) != 0) { + adv |= ADVERTISE_10HALF | + ADVERTISE_10FULL; + val |= LX_DRV_PHY_10 | LX_DRV_PHY_DUPLEX; + } + if ((LX_LC_100H & lnk_cap) != 0) { + adv |= ADVERTISE_100HALF; + val |= LX_DRV_PHY_100; + } + if ((LX_LC_100F & lnk_cap) != 0) { + adv |= ADVERTISE_100HALF | + ADVERTISE_100FULL; + val |= LX_DRV_PHY_100 | LX_DRV_PHY_DUPLEX; + } + if ((LX_LC_1000F & lnk_cap) != 0) { + giga |= L1C_GIGA_CR_1000T_FD_CAPS; + val |= LX_DRV_PHY_1000 | LX_DRV_PHY_DUPLEX; + } + + ret = l1c_write_phy(hw, false, 0, false, MII_ADVERTISE, adv); + ret = l1c_write_phy(hw, false, 0, false, MII_CTRL1000, giga); + + cr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART; + ret = l1c_write_phy(hw, false, 0, false, MII_BMCR, cr); + } else { /* force mode */ + cr = BMCR_RESET; + switch (lnk_cap) { + case LX_LC_10H: + val |= LX_DRV_PHY_10; + break; + case LX_LC_10F: + cr |= BMCR_FULLDPLX; + val |= LX_DRV_PHY_10 | LX_DRV_PHY_DUPLEX; + break; + case LX_LC_100H: + cr |= BMCR_SPEED100; + val |= LX_DRV_PHY_100; + break; + case LX_LC_100F: + cr |= BMCR_SPEED100 | BMCR_FULLDPLX; + val |= LX_DRV_PHY_100 | LX_DRV_PHY_DUPLEX; + break; + default: + return LX_ERR_PARM; + } + ret = l1c_write_phy(hw, false, 0, false, MII_BMCR, cr); + } + + if (!ret) { + l1c_write_phy(hw, false, 0, false, L1C_MII_DBG_ADDR, + LX_PHY_INITED); + } + alx_mem_w32(hw, L1C_DRV, val); + + return ret; +} + + +/* do power saving setting befor enter suspend mode + * NOTE: + * 1. phy link must be established before calling this function + * 2. wol option (pattern,magic,link,etc.) is configed before call it. + */ +u16 l1c_powersaving(struct alx_hw *hw, u8 wire_spd, bool wol_en, + bool mac_txen, bool mac_rxen, bool pws_en) +{ + u32 master_ctrl, mac_ctrl, phy_ctrl; + u16 pm_ctrl, ret = 0; + + master_ctrl = 0; + mac_ctrl = 0; + phy_ctrl = 0; + + pws_en = pws_en; + + alx_mem_r32(hw, L1C_MASTER, &master_ctrl); + master_ctrl &= ~L1C_MASTER_PCLKSEL_SRDS; + + alx_mem_r32(hw, L1C_MAC_CTRL, &mac_ctrl); + /* 10/100 half */ + FIELD_SETL(mac_ctrl, L1C_MAC_CTRL_SPEED, L1C_MAC_CTRL_SPEED_10_100); + mac_ctrl &= ~(L1C_MAC_CTRL_FULLD | + L1C_MAC_CTRL_RX_EN | + L1C_MAC_CTRL_TX_EN); + + alx_mem_r32(hw, L1C_PHY_CTRL, &phy_ctrl); + phy_ctrl &= ~(L1C_PHY_CTRL_DSPRST_OUT | L1C_PHY_CTRL_CLS); + /* if (pws_en) */ + phy_ctrl |= (L1C_PHY_CTRL_RST_ANALOG | L1C_PHY_CTRL_HIB_PULSE | + L1C_PHY_CTRL_HIB_EN); + + if (wol_en) { /* enable rx packet or tx packet */ + if (mac_rxen) + mac_ctrl |= (L1C_MAC_CTRL_RX_EN | L1C_MAC_CTRL_BRD_EN); + if (mac_txen) + mac_ctrl |= L1C_MAC_CTRL_TX_EN; + if (LX_LC_1000F == wire_spd) { + FIELD_SETL(mac_ctrl, L1C_MAC_CTRL_SPEED, + L1C_MAC_CTRL_SPEED_1000); + } + if (LX_LC_10F == wire_spd || LX_LC_100F == wire_spd || + LX_LC_100F == wire_spd) { + mac_ctrl |= L1C_MAC_CTRL_FULLD; + } + phy_ctrl |= L1C_PHY_CTRL_DSPRST_OUT; + ret = l1c_write_phy(hw, false, 0, false, + L1C_MII_IER, L1C_IER_LINK_UP); + } else { + master_ctrl |= L1C_MASTER_PCLKSEL_SRDS; + ret = l1c_write_phy(hw, false, 0, false, L1C_MII_IER, 0); + phy_ctrl |= (L1C_PHY_CTRL_IDDQ | L1C_PHY_CTRL_POWER_DOWN); + } + alx_mem_w32(hw, L1C_MASTER, master_ctrl); + alx_mem_w32(hw, L1C_MAC_CTRL, mac_ctrl); + alx_mem_w32(hw, L1C_PHY_CTRL, phy_ctrl); + + /* set PME_EN ?? */ + if (wol_en) { + alx_cfg_r16(hw, L1C_PM_CSR, &pm_ctrl); + pm_ctrl |= L1C_PM_CSR_PME_EN; + alx_cfg_w16(hw, L1C_PM_CSR, pm_ctrl); + } + + return ret; +} + + +/* read phy register */ +u16 l1c_read_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, + u16 reg, u16 *data) +{ + u32 val; + u16 clk_sel, i, ret = 0; + + *data = 0; + clk_sel = fast ? + (u16)L1C_MDIO_CLK_SEL_25MD4 : (u16)L1C_MDIO_CLK_SEL_25MD128; + + if (ext) { + val = FIELDL(L1C_MDIO_EXTN_DEVAD, dev) | + FIELDL(L1C_MDIO_EXTN_REG, reg); + alx_mem_w32(hw, L1C_MDIO_EXTN, val); + + val = L1C_MDIO_SPRES_PRMBL | + FIELDL(L1C_MDIO_CLK_SEL, clk_sel) | + L1C_MDIO_START | + L1C_MDIO_MODE_EXT | + L1C_MDIO_OP_READ; + } else { + val = L1C_MDIO_SPRES_PRMBL | + FIELDL(L1C_MDIO_CLK_SEL, clk_sel) | + FIELDL(L1C_MDIO_REG, reg) | + L1C_MDIO_START | + L1C_MDIO_OP_READ; + } + + alx_mem_w32(hw, L1C_MDIO, val); + + for (i = 0; i < L1C_MDIO_MAX_AC_TO; i++) { + alx_mem_r32(hw, L1C_MDIO, &val); + if ((val & L1C_MDIO_BUSY) == 0) { + *data = (u16)FIELD_GETX(val, L1C_MDIO_DATA); + break; + } + udelay(10); + } + if (L1C_MDIO_MAX_AC_TO == i) + ret = LX_ERR_MIIBUSY; + + return ret; +} + +/* write phy register */ +u16 l1c_write_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, + u16 reg, u16 data) +{ + u32 val; + u16 clk_sel, i, ret = 0; + + clk_sel = fast ? + (u16)L1C_MDIO_CLK_SEL_25MD4 : (u16)L1C_MDIO_CLK_SEL_25MD128; + + if (ext) { + val = FIELDL(L1C_MDIO_EXTN_DEVAD, dev) | + FIELDL(L1C_MDIO_EXTN_REG, reg); + alx_mem_w32(hw, L1C_MDIO_EXTN, val); + + val = L1C_MDIO_SPRES_PRMBL | + FIELDL(L1C_MDIO_CLK_SEL, clk_sel) | + FIELDL(L1C_MDIO_DATA, data) | + L1C_MDIO_START | + L1C_MDIO_MODE_EXT; + } else { + val = L1C_MDIO_SPRES_PRMBL | + FIELDL(L1C_MDIO_CLK_SEL, clk_sel) | + FIELDL(L1C_MDIO_REG, reg) | + FIELDL(L1C_MDIO_DATA, data) | + L1C_MDIO_START; + } + + alx_mem_w32(hw, L1C_MDIO, val); + + for (i = 0; i < L1C_MDIO_MAX_AC_TO; i++) { + alx_mem_r32(hw, L1C_MDIO, &val); + if ((val & L1C_MDIO_BUSY) == 0) + break; + udelay(10); + } + + if (L1C_MDIO_MAX_AC_TO == i) + ret = LX_ERR_MIIBUSY; + + return ret; +} + +u16 l1c_read_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 *data) +{ + u16 ret; + + ret = l1c_write_phy(hw, false, 0, fast, L1C_MII_DBG_ADDR, reg); + ret = l1c_read_phy(hw, false, 0, fast, L1C_MII_DBG_DATA, data); + + return ret; +} + +u16 l1c_write_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 data) +{ + u16 ret; + + ret = l1c_write_phy(hw, false, 0, fast, L1C_MII_DBG_ADDR, reg); + ret = l1c_write_phy(hw, false, 0, fast, L1C_MII_DBG_DATA, data); + + return ret; +} + + +/* + * initialize mac basically + * most of hi-feature no init + * MAC/PHY should be reset before call this function + * smb_timer : million-second + * int_mod : micro-second + * disable RSS as default + */ +u16 l1c_init_mac(struct alx_hw *hw, u8 *addr, u32 txmem_hi, + u32 *tx_mem_lo, u8 tx_qnum, u16 txring_sz, + u32 rxmem_hi, u32 rfdmem_lo, u32 rrdmem_lo, + u16 rxring_sz, u16 rxbuf_sz, u16 smb_timer, + u16 mtu, u16 int_mod, bool hash_legacy) +{ + u32 val; + u16 val16; + u8 dmar_len; + + /* set mac-address */ + val = *(u32 *)(addr + 2); + alx_mem_w32(hw, L1C_STAD0, LX_SWAP_DW(val)); + val = *(u16 *)addr ; + alx_mem_w32(hw, L1C_STAD1, LX_SWAP_W((u16)val)); + + /* clear multicast hash table, algrithm */ + alx_mem_w32(hw, L1C_HASH_TBL0, 0); + alx_mem_w32(hw, L1C_HASH_TBL1, 0); + alx_mem_r32(hw, L1C_MAC_CTRL, &val); + if (hash_legacy) + val |= L1C_MAC_CTRL_MHASH_ALG_HI5B; + else + val &= ~L1C_MAC_CTRL_MHASH_ALG_HI5B; + alx_mem_w32(hw, L1C_MAC_CTRL, val); + + /* clear any wol setting/status */ + alx_mem_r32(hw, L1C_WOL0, &val); + alx_mem_w32(hw, L1C_WOL0, 0); + + /* clk gating */ + alx_mem_w32(hw, L1C_CLK_GATE, (hw->pci_devid == L1D_DEV_ID) ? 0 : + (L1C_CLK_GATE_DMAR | L1C_CLK_GATE_DMAW | + L1C_CLK_GATE_TXQ | L1C_CLK_GATE_RXQ | + L1C_CLK_GATE_TXMAC)); + + /* descriptor ring base memory */ + alx_mem_w32(hw, L1C_TX_BASE_ADDR_HI, txmem_hi); + alx_mem_w32(hw, L1C_TPD_RING_SZ, txring_sz); + switch (tx_qnum) { + case 2: + alx_mem_w32(hw, L1C_TPD_PRI1_ADDR_LO, tx_mem_lo[1]); + /* fall through */ + case 1: + alx_mem_w32(hw, L1C_TPD_PRI0_ADDR_LO, tx_mem_lo[0]); + break; + default: + return LX_ERR_PARM; + } + alx_mem_w32(hw, L1C_RX_BASE_ADDR_HI, rxmem_hi); + alx_mem_w32(hw, L1C_RFD_ADDR_LO, rfdmem_lo); + alx_mem_w32(hw, L1C_RRD_ADDR_LO, rrdmem_lo); + alx_mem_w32(hw, L1C_RFD_BUF_SZ, rxbuf_sz); + alx_mem_w32(hw, L1C_RRD_RING_SZ, rxring_sz); + alx_mem_w32(hw, L1C_RFD_RING_SZ, rxring_sz); + alx_mem_w32(hw, L1C_SMB_TIMER, smb_timer * 500UL); + + if (hw->pci_devid == L2CB_DEV_ID) { + /* revise SRAM configuration */ + alx_mem_w32(hw, L1C_SRAM5, L1C_SRAM_RXF_LEN_L2CB1); + alx_mem_w32(hw, L1C_SRAM7, L1C_SRAM_TXF_LEN_L2CB1); + alx_mem_w32(hw, L1C_SRAM4, L1C_SRAM_RXF_HT_L2CB1); + alx_mem_w32(hw, L1C_SRAM0, L1C_SRAM_RFD_HT_L2CB1); + alx_mem_w32(hw, L1C_SRAM6, L1C_SRAM_TXF_HT_L2CB1); + alx_mem_w32(hw, L1C_SRAM2, L1C_SRAM_TRD_HT_L2CB1); + alx_mem_w32(hw, L1C_TXQ2, 0); /* TX watermark, goto L1 state.*/ + alx_mem_w32(hw, L1C_RXQ3, 0); /* RXD threshold. */ + } + alx_mem_w32(hw, L1C_SRAM9, L1C_SRAM_LOAD_PTR); + + /* int moduration */ + alx_mem_r32(hw, L1C_MASTER, &val); + val |= L1C_MASTER_IRQMOD2_EN | L1C_MASTER_IRQMOD1_EN | + L1C_MASTER_SYSALVTIMER_EN; /* sysalive */ + alx_mem_w32(hw, L1C_MASTER, val); + /* set Interrupt Moderator Timer (max interrupt per sec) + * we use seperate time for rx/tx */ + alx_mem_w32(hw, L1C_IRQ_MODU_TIMER, + FIELDL(L1C_IRQ_MODU_TIMER1, int_mod) | + FIELDL(L1C_IRQ_MODU_TIMER2, int_mod >> 1)); + + /* tpd threshold to trig int */ + alx_mem_w32(hw, L1C_TINT_TPD_THRSHLD, (u32)txring_sz / 3); + alx_mem_w32(hw, L1C_TINT_TIMER, int_mod * 2); + /* re-send int */ + alx_mem_w32(hw, L1C_INT_RETRIG, L1C_INT_RETRIG_TO); + + /* mtu */ + alx_mem_w32(hw, L1C_MTU, (u32)(mtu + 4 + 4)); /* crc + vlan */ + + /* txq */ + if ((mtu + 8) < L1C_TXQ1_JUMBO_TSO_TH) + val = (u32)(mtu + 8 + 7); /* 7 for QWORD align */ + else + val = L1C_TXQ1_JUMBO_TSO_TH; + alx_mem_w32(hw, L1C_TXQ1, val >> 3); + + alx_mem_r32(hw, L1C_DEV_CTRL, &val); + dmar_len = (u8)FIELD_GETX(val, L1C_DEV_CTRL_MAXRRS); + /* if BIOS had changed the default dma read max length, + * restore it to default value */ + if (dmar_len < L1C_DEV_CTRL_MAXRRS_MIN) { + FIELD_SETL(val, L1C_DEV_CTRL_MAXRRS, L1C_DEV_CTRL_MAXRRS_MIN); + alx_mem_w32(hw, L1C_DEV_CTRL, val); + dmar_len = L1C_DEV_CTRL_MAXRRS_MIN; + } + val = FIELDL(L1C_TXQ0_TPD_BURSTPREF, L1C_TXQ0_TPD_BURSTPREF_DEF) | + L1C_TXQ0_MODE_ENHANCE | + L1C_TXQ0_LSO_8023_EN | + L1C_TXQ0_SUPT_IPOPT | + FIELDL(L1C_TXQ0_TXF_BURST_PREF, + (hw->pci_devid == L2CB_DEV_ID || + hw->pci_devid == L2CB2_DEV_ID) ? + L1C_TXQ0_TXF_BURST_PREF_L2CB : + L1C_TXQ0_TXF_BURST_PREF_DEF); + alx_mem_w32(hw, L1C_TXQ0, val); + + /* fc */ + alx_mem_r32(hw, L1C_SRAM5, &val); + val = FIELD_GETX(val, L1C_SRAM_RXF_LEN) << 3; /* bytes */ + if (val > L1C_SRAM_RXF_LEN_8K) { + val16 = L1C_MTU_STD_ALGN; + val = (val - (2 * L1C_MTU_STD_ALGN + L1C_MTU_MIN)); + } else { + val16 = L1C_MTU_STD_ALGN; + val = (val - L1C_MTU_STD_ALGN); + } + alx_mem_w32(hw, L1C_RXQ2, + FIELDL(L1C_RXQ2_RXF_XOFF_THRESH, val16 >> 3) | + FIELDL(L1C_RXQ2_RXF_XON_THRESH, val >> 3)); + /* rxq */ + val = FIELDL(L1C_RXQ0_NUM_RFD_PREF, L1C_RXQ0_NUM_RFD_PREF_DEF) | + L1C_RXQ0_IPV6_PARSE_EN; + if (mtu > L1C_MTU_JUMBO_TH) + val |= L1C_RXQ0_CUT_THRU_EN; + if ((hw->pci_devid & 1) != 0) { + FIELD_SETL(val, L1C_RXQ0_ASPM_THRESH, + (hw->pci_devid == L1D2_DEV_ID) ? + L1C_RXQ0_ASPM_THRESH_NO : + L1C_RXQ0_ASPM_THRESH_100M); + } + alx_mem_w32(hw, L1C_RXQ0, val); + + /* rfd producer index */ + alx_mem_w32(hw, L1C_RFD_PIDX, (u32)rxring_sz - 1); + + /* DMA */ + val = FIELDL(L1C_DMA_RORDER_MODE, L1C_DMA_RORDER_MODE_OUT) | + L1C_DMA_RREQ_PRI_DATA | + FIELDL(L1C_DMA_RREQ_BLEN, dmar_len) | + FIELDL(L1C_DMA_WDLY_CNT, L1C_DMA_WDLY_CNT_DEF) | + FIELDL(L1C_DMA_RDLY_CNT, L1C_DMA_RDLY_CNT_DEF) ; + alx_mem_w32(hw, L1C_DMA, val); + + return 0; +} + + +u16 l1c_get_phy_config(struct alx_hw *hw) +{ + u32 val; + u16 phy_val; + + alx_mem_r32(hw, L1C_PHY_CTRL, &val); + if ((val & L1C_PHY_CTRL_DSPRST_OUT) == 0) { /* phy in rst */ + return LX_DRV_PHY_UNKNOWN; + } + + alx_mem_r32(hw, L1C_DRV, &val); + val = FIELD_GETX(val, LX_DRV_PHY); + if (LX_DRV_PHY_UNKNOWN == val) + return LX_DRV_PHY_UNKNOWN; + + l1c_read_phy(hw, false, 0, false, L1C_MII_DBG_ADDR, &phy_val); + + if (LX_PHY_INITED == phy_val) + return (u16) val; + + return LX_DRV_PHY_UNKNOWN; +} + --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alc_hw.h @@ -0,0 +1,1324 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef L1C_HW_H_ +#define L1C_HW_H_ + +/********************************************************************* + * some reqs for l1x_sw.h + * + * 1. some basic type must be defined if there are not defined by + * your compiler: + * u8, u16, u32, bool + * + * 2. PETHCONTEXT difinition should be in l1x_sw.h and it must contain + * pci_devid & pci_venid & pci_revid + * + *********************************************************************/ + +#include "alx_hwcom.h" + +/******************************************************************************/ + +#define L1C_DEV_ID 0x1063 +#define L2C_DEV_ID 0x1062 +#define L2CB_DEV_ID 0x2060 +#define L2CB2_DEV_ID 0x2062 +#define L1D_DEV_ID 0x1073 +#define L1D2_DEV_ID 0x1083 + +#define L2CB_V10 0xC0 +#define L2CB_V11 0xC1 +#define L2CB_V20 0xC0 +#define L2CB_V21 0xC1 + +#define L1C_PM_CSR 0x0044 /* 16bit */ +#define L1C_PM_CSR_PME_STAT BIT(15) +#define L1C_PM_CSR_DSCAL_MASK ASHFT13(3U) +#define L1C_PM_CSR_DSCAL_SHIFT 13 +#define L1C_PM_CSR_DSEL_MASK ASHFT9(0xFU) +#define L1C_PM_CSR_DSEL_SHIFT 9 +#define L1C_PM_CSR_PME_EN BIT(8) +#define L1C_PM_CSR_PWST_MASK ASHFT0(3U) +#define L1C_PM_CSR_PWST_SHIFT 0 + +#define L1C_PM_DATA 0x0047 /* 8bit */ + +#define L1C_DEV_CAP 0x005C +#define L1C_DEV_CAP_SPLSL_MASK ASHFT26(3UL) +#define L1C_DEV_CAP_SPLSL_SHIFT 26 +#define L1C_DEV_CAP_SPLV_MASK ASHFT18(0xFFUL) +#define L1C_DEV_CAP_SPLV_SHIFT 18 +#define L1C_DEV_CAP_RBER BIT(15) +#define L1C_DEV_CAP_PIPRS BIT(14) +#define L1C_DEV_CAP_AIPRS BIT(13) +#define L1C_DEV_CAP_ABPRS BIT(12) +#define L1C_DEV_CAP_L1ACLAT_MASK ASHFT9(7UL) +#define L1C_DEV_CAP_L1ACLAT_SHIFT 9 +#define L1C_DEV_CAP_L0SACLAT_MASK ASHFT6(7UL) +#define L1C_DEV_CAP_L0SACLAT_SHIFT 6 +#define L1C_DEV_CAP_EXTAG BIT(5) +#define L1C_DEV_CAP_PHANTOM BIT(4) +#define L1C_DEV_CAP_MPL_MASK ASHFT0(7UL) +#define L1C_DEV_CAP_MPL_SHIFT 0 +#define L1C_DEV_CAP_MPL_128 1 +#define L1C_DEV_CAP_MPL_256 2 +#define L1C_DEV_CAP_MPL_512 3 +#define L1C_DEV_CAP_MPL_1024 4 +#define L1C_DEV_CAP_MPL_2048 5 +#define L1C_DEV_CAP_MPL_4096 6 + +#define L1C_DEV_CTRL 0x0060 /* 16bit */ +#define L1C_DEV_CTRL_MAXRRS_MASK ASHFT12(7U) +#define L1C_DEV_CTRL_MAXRRS_SHIFT 12 +#define L1C_DEV_CTRL_MAXRRS_MIN 2 +#define L1C_DEV_CTRL_NOSNP_EN BIT(11) +#define L1C_DEV_CTRL_AUXPWR_EN BIT(10) +#define L1C_DEV_CTRL_PHANTOM_EN BIT(9) +#define L1C_DEV_CTRL_EXTAG_EN BIT(8) +#define L1C_DEV_CTRL_MPL_MASK ASHFT5(7U) +#define L1C_DEV_CTRL_MPL_SHIFT 5 +#define L1C_DEV_CTRL_RELORD_EN BIT(4) +#define L1C_DEV_CTRL_URR_EN BIT(3) +#define L1C_DEV_CTRL_FERR_EN BIT(2) +#define L1C_DEV_CTRL_NFERR_EN BIT(1) +#define L1C_DEV_CTRL_CERR_EN BIT(0) + +#define L1C_DEV_STAT 0x0062 /* 16bit */ +#define L1C_DEV_STAT_XS_PEND BIT(5) +#define L1C_DEV_STAT_AUXPWR BIT(4) +#define L1C_DEV_STAT_UR BIT(3) +#define L1C_DEV_STAT_FERR BIT(2) +#define L1C_DEV_STAT_NFERR BIT(1) +#define L1C_DEV_STAT_CERR BIT(0) + +#define L1C_LNK_CAP 0x0064 +#define L1C_LNK_CAP_PRTNUM_MASK ASHFT24(0xFFUL) +#define L1C_LNK_CAP_PRTNUM_SHIFT 24 +#define L1C_LNK_CAP_CLK_PM BIT(18) +#define L1C_LNK_CAP_L1EXTLAT_MASK ASHFT15(7UL) +#define L1C_LNK_CAP_L1EXTLAT_SHIFT 15 +#define L1C_LNK_CAP_L0SEXTLAT_MASK ASHFT12(7UL) +#define L1C_LNK_CAP_L0SEXTLAT_SHIFT 12 +#define L1C_LNK_CAP_ASPM_SUP_MASK ASHFT10(3UL) +#define L1C_LNK_CAP_ASPM_SUP_SHIFT 10 +#define L1C_LNK_CAP_ASPM_SUP_L0S 1 +#define L1C_LNK_CAP_ASPM_SUP_L0SL1 3 +#define L1C_LNK_CAP_MAX_LWH_MASK ASHFT4(0x3FUL) +#define L1C_LNK_CAP_MAX_LWH_SHIFT 4 +#define L1C_LNK_CAP_MAX_LSPD_MASH ASHFT0(0xFUL) +#define L1C_LNK_CAP_MAX_LSPD_SHIFT 0 + +#define L1C_LNK_CTRL 0x0068 /* 16bit */ +#define L1C_LNK_CTRL_CLK_PM_EN BIT(8) +#define L1C_LNK_CTRL_EXTSYNC BIT(7) +#define L1C_LNK_CTRL_CMNCLK_CFG BIT(6) +#define L1C_LNK_CTRL_RCB_128B BIT(3) /* 0:64b,1:128b */ +#define L1C_LNK_CTRL_ASPM_MASK ASHFT0(3U) +#define L1C_LNK_CTRL_ASPM_SHIFT 0 +#define L1C_LNK_CTRL_ASPM_DIS 0 +#define L1C_LNK_CTRL_ASPM_ENL0S 1 +#define L1C_LNK_CTRL_ASPM_ENL1 2 +#define L1C_LNK_CTRL_ASPM_ENL0SL1 3 + +#define L1C_LNK_STAT 0x006A /* 16bit */ +#define L1C_LNK_STAT_SCLKCFG BIT(12) +#define L1C_LNK_STAT_LNKTRAIN BIT(11) +#define L1C_LNK_STAT_TRNERR BIT(10) +#define L1C_LNK_STAT_LNKSPD_MASK ASHFT0(0xFU) +#define L1C_LNK_STAT_LNKSPD_SHIFT 0 +#define L1C_LNK_STAT_NEGLW_MASK ASHFT4(0x3FU) +#define L1C_LNK_STAT_NEGLW_SHIFT 4 + +#define L1C_UE_SVRT 0x010C +#define L1C_UE_SVRT_UR BIT(20) +#define L1C_UE_SVRT_ECRCERR BIT(19) +#define L1C_UE_SVRT_MTLP BIT(18) +#define L1C_UE_SVRT_RCVOVFL BIT(17) +#define L1C_UE_SVRT_UNEXPCPL BIT(16) +#define L1C_UE_SVRT_CPLABRT BIT(15) +#define L1C_UE_SVRT_CPLTO BIT(14) +#define L1C_UE_SVRT_FCPROTERR BIT(13) +#define L1C_UE_SVRT_PTLP BIT(12) +#define L1C_UE_SVRT_DLPROTERR BIT(4) +#define L1C_UE_SVRT_TRNERR BIT(0) + +#define L1C_SLD 0x0218 /* efuse load */ +#define L1C_SLD_FREQ_MASK ASHFT24(3UL) +#define L1C_SLD_FREQ_SHIFT 24 +#define L1C_SLD_FREQ_100K 0 +#define L1C_SLD_FREQ_200K 1 +#define L1C_SLD_FREQ_300K 2 +#define L1C_SLD_FREQ_400K 3 +#define L1C_SLD_EXIST BIT(23) +#define L1C_SLD_SLVADDR_MASK ASHFT16(0x7FUL) +#define L1C_SLD_SLVADDR_SHIFT 16 +#define L1C_SLD_IDLE BIT(13) +#define L1C_SLD_STAT BIT(12) /* 0:finish,1:in progress */ +#define L1C_SLD_START BIT(11) +#define L1C_SLD_STARTADDR_MASK ASHFT0(0xFFUL) +#define L1C_SLD_STARTADDR_SHIFT 0 +#define L1C_SLD_MAX_TO 100 + +#define L1C_PPHY_MISC1 0x1000 +#define L1C_PPHY_MISC1_RCVDET BIT(2) +#define L1C_PPHY_MISC1_NFTS_MASK ASHFT16(0xFFUL) +#define L1C_PPHY_MISC1_NFTS_SHIFT 16 +#define L1C_PPHY_MISC1_NFTS_HIPERF 0xA0 /* ???? */ + +#define L1C_PPHY_MISC2 0x1004 +#define L1C_PPHY_MISC2_L0S_TH_MASK ASHFT18(0x3UL) +#define L1C_PPHY_MISC2_L0S_TH_SHIFT 18 +#define L1C_PPHY_MISC2_L0S_TH_L2CB1 3 +#define L1C_PPHY_MISC2_CDR_BW_MASK ASHFT16(0x3UL) +#define L1C_PPHY_MISC2_CDR_BW_SHIFT 16 +#define L1C_PPHY_MISC2_CDR_BW_L2CB1 3 + +#define L1C_PDLL_TRNS1 0x1104 +#define L1C_PDLL_TRNS1_D3PLLOFF_EN BIT(11) +#define L1C_PDLL_TRNS1_REGCLK_SEL_NORM BIT(10) +#define L1C_PDLL_TRNS1_REPLY_TO_MASK ASHFT0(0x3FFUL) +#define L1C_PDLL_TRNS1_REPLY_TO_SHIFT 0 + +#define L1C_TWSI_DBG 0x1108 +#define L1C_TWSI_DBG_DEV_EXIST BIT(29) + +#define L1C_DMA_DBG 0x1114 +#define L1C_DMA_DBG_VENDOR_MSG BIT(0) + +#define L1C_TLEXTN_STATS 0x1204 /* diff with l1f */ +#define L1C_TLEXTN_STATS_DEVNO_MASK ASHFT16(0x1FUL) +#define L1C_TLEXTN_STATS_DEVNO_SHIFT 16 +#define L1C_TLEXTN_STATS_BUSNO_MASK ASHFT8(0xFFUL) +#define L1C_TLEXTN_STATS_BUSNO_SHIFT 8 + +#define L1C_EFUSE_CTRL 0x12C0 +#define L1C_EFUSE_CTRL_FLAG BIT(31) /* 0:read,1:write */ +#define L1C_EUFSE_CTRL_ACK BIT(30) +#define L1C_EFUSE_CTRL_ADDR_MASK ASHFT16(0x3FFUL) +#define L1C_EFUSE_CTRL_ADDR_SHIFT 16 + +#define L1C_EFUSE_DATA 0x12C4 + +#define EFUSE_OP_MAX_AC_TIMER 100 /* 1ms */ + +#define L1C_EFUSE_CTRL2 0x12F0 +#define L1C_EFUSE_CTRL2_CLK_EN BIT(1) + +#define L1C_PMCTRL 0x12F8 +#define L1C_PMCTRL_HOTRST_WTEN BIT(31) +#define L1C_PMCTRL_ASPM_FCEN BIT(30) /* L0s/L1 dis by MAC based on + * thrghput(setting in 15A0) */ +#define L1C_PMCTRL_SADLY_EN BIT(29) +#define L1C_PMCTRL_L0S_BUFSRX_EN BIT(28) +#define L1C_PMCTRL_LCKDET_TIMER_MASK ASHFT24(0xFUL) +#define L1C_PMCTRL_LCKDET_TIMER_SHIFT 24 +#define L1C_PMCTRL_LCKDET_TIMER_DEF 0xC +#define L1C_PMCTRL_L1REQ_TO_MASK ASHFT20(0xFUL) +#define L1C_PMCTRL_L1REQ_TO_SHIFT 20 /* pm_request_l1 time > @ + * ->L0s not L1 */ +#define L1C_PMCTRL_L1REG_TO_DEF 0xC +#define L1D_PMCTRL_TXL1_AFTER_L0S BIT(19) /* l1dv2.0+ */ +#define L1D_PMCTRL_L1_TIMER_MASK ASHFT16(7UL) +#define L1D_PMCTRL_L1_TIMER_SHIFT 16 +#define L1D_PMCTRL_L1_TIMER_DIS 0 +#define L1D_PMCTRL_L1_TIMER_2US 1 +#define L1D_PMCTRL_L1_TIMER_4US 2 +#define L1D_PMCTRL_L1_TIMER_8US 3 +#define L1D_PMCTRL_L1_TIMER_16US 4 +#define L1D_PMCTRL_L1_TIMER_24US 5 +#define L1D_PMCTRL_L1_TIMER_32US 6 +#define L1D_PMCTRL_L1_TIMER_63US 7 +#define L1C_PMCTRL_L1_TIMER_MASK ASHFT16(0xFUL) +#define L1C_PMCTRL_L1_TIMER_SHIFT 16 +#define L1C_PMCTRL_L1_TIMER_L2CB1 7 +#define L1C_PMCTRL_L1_TIMER_DEF 0xF +#define L1C_PMCTRL_RCVR_WT_1US BIT(15) /* 1:1us, 0:2ms */ +#define L1C_PMCTRL_PWM_VER_11 BIT(14) /* 0:1.0a,1:1.1 */ +#define L1C_PMCTRL_L1_CLKSW_EN BIT(13) /* en pcie clk sw in L1 */ +#define L1C_PMCTRL_L0S_EN BIT(12) +#define L1D_PMCTRL_RXL1_AFTER_L0S BIT(11) /* l1dv2.0+ */ +#define L1D_PMCTRL_L0S_TIMER_MASK ASHFT8(7UL) +#define L1D_PMCTRL_L0S_TIMER_SHIFT 8 +#define L1C_PMCTRL_L0S_TIMER_MASK ASHFT8(0xFUL) +#define L1C_PMCTRL_L0S_TIMER_SHIFT 8 +#define L1C_PMCTRL_L1_BUFSRX_EN BIT(7) +#define L1C_PMCTRL_L1_SRDSRX_PWD BIT(6) /* power down serdes rx */ +#define L1C_PMCTRL_L1_SRDSPLL_EN BIT(5) +#define L1C_PMCTRL_L1_SRDS_EN BIT(4) +#define L1C_PMCTRL_L1_EN BIT(3) +#define L1C_PMCTRL_CLKREQ_EN BIT(2) +#define L1C_PMCTRL_RBER_EN BIT(1) +#define L1C_PMCTRL_SPRSDWER_EN BIT(0) + +#define L1C_LTSSM_CTRL 0x12FC +#define L1C_LTSSM_WRO_EN BIT(12) +#define L1C_LTSSM_TXTLP_BYPASS BIT(7) + +#define L1C_MASTER 0x1400 +#define L1C_MASTER_OTP_FLG BIT(31) +#define L1C_MASTER_DEV_NUM_MASK ASHFT24(0x7FUL) +#define L1C_MASTER_DEV_NUM_SHIFT 24 +#define L1C_MASTER_REV_NUM_MASK ASHFT16(0xFFUL) +#define L1C_MASTER_REV_NUM_SHIFT 16 +#define L1C_MASTER_RDCLR_INT BIT(14) +#define L1C_MASTER_CLKSW_L2EV1 BIT(13) /* 0:l2ev2.0,1:l2ev1.0 */ +#define L1C_MASTER_PCLKSEL_SRDS BIT(12) /* 1:alwys sel pclk from + * serdes, not sw to 25M */ +#define L1C_MASTER_IRQMOD2_EN BIT(11) /* IRQ MODURATION FOR RX */ +#define L1C_MASTER_IRQMOD1_EN BIT(10) /* MODURATION FOR TX/RX */ +#define L1C_MASTER_MANU_INT BIT(9) /* SOFT MANUAL INT */ +#define L1C_MASTER_MANUTIMER_EN BIT(8) +#define L1C_MASTER_SYSALVTIMER_EN BIT(7) /* SYS ALIVE TIMER EN */ +#define L1C_MASTER_OOB_DIS BIT(6) /* OUT OF BOX DIS */ +#define L1C_MASTER_WAKEN_25M BIT(5) /* WAKE WO. PCIE CLK */ +#define L1C_MASTER_BERT_START BIT(4) +#define L1C_MASTER_PCIE_TSTMOD_MASK ASHFT2(3UL) +#define L1C_MASTER_PCIE_TSTMOD_SHIFT 2 +#define L1C_MASTER_PCIE_RST BIT(1) +#define L1C_MASTER_DMA_MAC_RST BIT(0) /* RST MAC & DMA */ +#define L1C_DMA_MAC_RST_TO 50 + +#define L1C_MANU_TIMER 0x1404 + +#define L1C_IRQ_MODU_TIMER 0x1408 +#define L1C_IRQ_MODU_TIMER2_MASK ASHFT16(0xFFFFUL) +#define L1C_IRQ_MODU_TIMER2_SHIFT 16 /* ONLY FOR RX */ +#define L1C_IRQ_MODU_TIMER1_MASK ASHFT0(0xFFFFUL) +#define L1C_IRQ_MODU_TIMER1_SHIFT 0 + +#define L1C_PHY_CTRL 0x140C +#define L1C_PHY_CTRL_ADDR_MASK ASHFT19(0x1FUL) +#define L1C_PHY_CTRL_ADDR_SHIFT 19 +#define L1C_PHY_CTRL_BP_VLTGSW BIT(18) +#define L1C_PHY_CTRL_100AB_EN BIT(17) +#define L1C_PHY_CTRL_10AB_EN BIT(16) +#define L1C_PHY_CTRL_PLL_BYPASS BIT(15) +#define L1C_PHY_CTRL_POWER_DOWN BIT(14) /* affect MAC & PHY, + * go to low power sts */ +#define L1C_PHY_CTRL_PLL_ON BIT(13) /* 1:PLL ALWAYS ON + * 0:CAN SWITCH IN LPW */ +#define L1C_PHY_CTRL_RST_ANALOG BIT(12) +#define L1C_PHY_CTRL_HIB_PULSE BIT(11) +#define L1C_PHY_CTRL_HIB_EN BIT(10) +#define L1C_PHY_CTRL_GIGA_DIS BIT(9) +#define L1C_PHY_CTRL_IDDQ_DIS BIT(8) /* POWER ON RST */ +#define L1C_PHY_CTRL_IDDQ BIT(7) /* WHILE REBOOT, BIT8(1) + * EFFECTS BIT7 */ +#define L1C_PHY_CTRL_LPW_EXIT BIT(6) +#define L1C_PHY_CTRL_GATE_25M BIT(5) +#define L1C_PHY_CTRL_RVRS_ANEG BIT(4) +#define L1C_PHY_CTRL_ANEG_NOW BIT(3) +#define L1C_PHY_CTRL_LED_MODE BIT(2) +#define L1C_PHY_CTRL_RTL_MODE BIT(1) +#define L1C_PHY_CTRL_DSPRST_OUT BIT(0) /* OUT OF DSP RST STATE */ +#define L1C_PHY_CTRL_DSPRST_TO 80 +#define L1C_PHY_CTRL_CLS (\ + L1C_PHY_CTRL_LED_MODE |\ + L1C_PHY_CTRL_100AB_EN |\ + L1C_PHY_CTRL_PLL_ON) + + +#define L1C_MAC_STS 0x1410 +#define L1C_MAC_STS_SFORCE_MASK ASHFT14(0xFUL) +#define L1C_MAC_STS_SFORCE_SHIFT 14 +#define L1C_MAC_STS_CALIB_DONE BIT13 +#define L1C_MAC_STS_CALIB_RES_MASK ASHFT8(0x1FUL) +#define L1C_MAC_STS_CALIB_RES_SHIFT 8 +#define L1C_MAC_STS_CALIBERR_MASK ASHFT4(0xFUL) +#define L1C_MAC_STS_CALIBERR_SHIFT 4 +#define L1C_MAC_STS_TXQ_BUSY BIT(3) +#define L1C_MAC_STS_RXQ_BUSY BIT(2) +#define L1C_MAC_STS_TXMAC_BUSY BIT(1) +#define L1C_MAC_STS_RXMAC_BUSY BIT(0) +#define L1C_MAC_STS_IDLE (\ + L1C_MAC_STS_TXQ_BUSY |\ + L1C_MAC_STS_RXQ_BUSY |\ + L1C_MAC_STS_TXMAC_BUSY |\ + L1C_MAC_STS_RXMAC_BUSY) + +#define L1C_MDIO 0x1414 +#define L1C_MDIO_MODE_EXT BIT(30) /* 0:normal,1:ext */ +#define L1C_MDIO_POST_READ BIT(29) +#define L1C_MDIO_AUTO_POLLING BIT(28) +#define L1C_MDIO_BUSY BIT(27) +#define L1C_MDIO_CLK_SEL_MASK ASHFT24(7UL) +#define L1C_MDIO_CLK_SEL_SHIFT 24 +#define L1C_MDIO_CLK_SEL_25MD4 0 /* 25M DIV 4 */ +#define L1C_MDIO_CLK_SEL_25MD6 2 +#define L1C_MDIO_CLK_SEL_25MD8 3 +#define L1C_MDIO_CLK_SEL_25MD10 4 +#define L1C_MDIO_CLK_SEL_25MD32 5 +#define L1C_MDIO_CLK_SEL_25MD64 6 +#define L1C_MDIO_CLK_SEL_25MD128 7 +#define L1C_MDIO_START BIT(23) +#define L1C_MDIO_SPRES_PRMBL BIT(22) +#define L1C_MDIO_OP_READ BIT(21) /* 1:read,0:write */ +#define L1C_MDIO_REG_MASK ASHFT16(0x1FUL) +#define L1C_MDIO_REG_SHIFT 16 +#define L1C_MDIO_DATA_MASK ASHFT0(0xFFFFUL) +#define L1C_MDIO_DATA_SHIFT 0 +#define L1C_MDIO_MAX_AC_TO 120 + +#define L1C_MDIO_EXTN 0x1448 +#define L1C_MDIO_EXTN_PORTAD_MASK ASHFT21(0x1FUL) +#define L1C_MDIO_EXTN_PORTAD_SHIFT 21 +#define L1C_MDIO_EXTN_DEVAD_MASK ASHFT16(0x1FUL) +#define L1C_MDIO_EXTN_DEVAD_SHIFT 16 +#define L1C_MDIO_EXTN_REG_MASK ASHFT0(0xFFFFUL) +#define L1C_MDIO_EXTN_REG_SHIFT 0 + +#define L1C_PHY_STS 0x1418 +#define L1C_PHY_STS_LPW BIT(31) +#define L1C_PHY_STS_LPI BIT(30) +#define L1C_PHY_STS_PWON_STRIP_MASK ASHFT16(0xFFFUL) +#define L1C_PHY_STS_PWON_STRIP_SHIFT 16 + +#define L1C_PHY_STS_DUPLEX BIT(3) +#define L1C_PHY_STS_LINKUP BIT(2) +#define L1C_PHY_STS_SPEED_MASK ASHFT0(3UL) +#define L1C_PHY_STS_SPEED_SHIFT 0 +#define L1C_PHY_STS_SPEED_SHIFT 0 +#define L1C_PHY_STS_SPEED_1000M 2 +#define L1C_PHY_STS_SPEED_100M 1 +#define L1C_PHY_STS_SPEED_10M 0 + +#define L1C_BIST0 0x141C +#define L1C_BIST0_COL_MASK ASHFT24(0x3FUL) +#define L1C_BIST0_COL_SHIFT 24 +#define L1C_BIST0_ROW_MASK ASHFT12(0xFFFUL) +#define L1C_BIST0_ROW_SHIFT 12 +#define L1C_BIST0_STEP_MASK ASHFT8(0xFUL) +#define L1C_BIST0_STEP_SHIFT 8 +#define L1C_BIST0_PATTERN_MASK ASHFT4(7UL) +#define L1C_BIST0_PATTERN_SHIFT 4 +#define L1C_BIST0_CRIT BIT(3) +#define L1C_BIST0_FIXED BIT(2) +#define L1C_BIST0_FAIL BIT(1) +#define L1C_BIST0_START BIT(0) + +#define L1C_BIST1 0x1420 +#define L1C_BIST1_COL_MASK ASHFT24(0x3FUL) +#define L1C_BIST1_COL_SHIFT 24 +#define L1C_BIST1_ROW_MASK ASHFT12(0xFFFUL) +#define L1C_BIST1_ROW_SHIFT 12 +#define L1C_BIST1_STEP_MASK ASHFT8(0xFUL) +#define L1C_BIST1_STEP_SHIFT 8 +#define L1C_BIST1_PATTERN_MASK ASHFT4(7UL) +#define L1C_BIST1_PATTERN_SHIFT 4 +#define L1C_BIST1_CRIT BIT(3) +#define L1C_BIST1_FIXED BIT(2) +#define L1C_BIST1_FAIL BIT(1) +#define L1C_BIST1_START BIT(0) + +#define L1C_SERDES 0x1424 +#define L1C_SERDES_PHYCLK_SLWDWN BIT(18) +#define L1C_SERDES_MACCLK_SLWDWN BIT(17) +#define L1C_SERDES_SELFB_PLL_MASK ASHFT14(3UL) +#define L1C_SERDES_SELFB_PLL_SHIFT 14 +#define L1C_SERDES_PHYCLK_SEL_GTX BIT(13) /* 1:gtx_clk, 0:25M */ +#define L1C_SERDES_PCIECLK_SEL_SRDS BIT(12) /* 1:serdes,0:25M */ +#define L1C_SERDES_BUFS_RX_EN BIT(11) +#define L1C_SERDES_PD_RX BIT(10) +#define L1C_SERDES_PLL_EN BIT(9) +#define L1C_SERDES_EN BIT(8) +#define L1C_SERDES_SELFB_PLL_SEL_CSR BIT(6) /* 0:state-machine,1:csr */ +#define L1C_SERDES_SELFB_PLL_CSR_MASK ASHFT4(3UL) +#define L1C_SERDES_SELFB_PLL_CSR_SHIFT 4 +#define L1C_SERDES_SELFB_PLL_CSR_4 3 /* 4-12% OV-CLK */ +#define L1C_SERDES_SELFB_PLL_CSR_0 2 /* 0-4% OV-CLK */ +#define L1C_SERDES_SELFB_PLL_CSR_12 1 /* 12-18% OV-CLK */ +#define L1C_SERDES_SELFB_PLL_CSR_18 0 /* 18-25% OV-CLK */ +#define L1C_SERDES_VCO_SLOW BIT(3) +#define L1C_SERDES_VCO_FAST BIT(2) +#define L1C_SERDES_LOCKDCT_EN BIT(1) +#define L1C_SERDES_LOCKDCTED BIT(0) + +#define L1C_LED_CTRL 0x1428 +#define L1C_LED_CTRL_PATMAP2_MASK ASHFT8(3UL) +#define L1C_LED_CTRL_PATMAP2_SHIFT 8 +#define L1C_LED_CTRL_PATMAP1_MASK ASHFT6(3UL) +#define L1C_LED_CTRL_PATMAP1_SHIFT 6 +#define L1C_LED_CTRL_PATMAP0_MASK ASHFT4(3UL) +#define L1C_LED_CTRL_PATMAP0_SHIFT 4 +#define L1C_LED_CTRL_D3_MODE_MASK ASHFT2(3UL) +#define L1C_LED_CTRL_D3_MODE_SHIFT 2 +#define L1C_LED_CTRL_D3_MODE_NORMAL 0 +#define L1C_LED_CTRL_D3_MODE_WOL_DIS 1 +#define L1C_LED_CTRL_D3_MODE_WOL_ANY 2 +#define L1C_LED_CTRL_D3_MODE_WOL_EN 3 +#define L1C_LED_CTRL_DUTY_CYCL_MASK ASHFT0(3UL) +#define L1C_LED_CTRL_DUTY_CYCL_SHIFT 0 +#define L1C_LED_CTRL_DUTY_CYCL_50 0 /* 50% */ +#define L1C_LED_CTRL_DUTY_CYCL_125 1 /* 12.5% */ +#define L1C_LED_CTRL_DUTY_CYCL_25 2 /* 25% */ +#define L1C_LED_CTRL_DUTY_CYCL_75 3 /* 75% */ + +#define L1C_LED_PATN 0x142C +#define L1C_LED_PATN1_MASK ASHFT16(0xFFFFUL) +#define L1C_LED_PATN1_SHIFT 16 +#define L1C_LED_PATN0_MASK ASHFT0(0xFFFFUL) +#define L1C_LED_PATN0_SHIFT 0 + +#define L1C_LED_PATN2 0x1430 +#define L1C_LED_PATN2_MASK ASHFT0(0xFFFFUL) +#define L1C_LED_PATN2_SHIFT 0 + +#define L1C_SYSALV 0x1434 +#define L1C_SYSALV_FLAG BIT(0) + +#define L1C_PCIERR_INST 0x1438 +#define L1C_PCIERR_INST_TX_RATE_MASK ASHFT4(0xFUL) +#define L1C_PCIERR_INST_TX_RATE_SHIFT 4 +#define L1C_PCIERR_INST_RX_RATE_MASK ASHFT0(0xFUL) +#define L1C_PCIERR_INST_RX_RATE_SHIFT 0 + +#define L1C_LPI_DECISN_TIMER 0x143C +#define L1C_LPI_DESISN_TIMER_L2CB 0x7D00 + +#define L1C_LPI_CTRL 0x1440 +#define L1C_LPI_CTRL_CHK_DA BIT(31) +#define L1C_LPI_CTRL_ENH_TO_MASK ASHFT12(0x1FFFUL) +#define L1C_LPI_CTRL_ENH_TO_SHIFT 12 +#define L1C_LPI_CTRL_ENH_TH_MASK ASHFT6(0x1FUL) +#define L1C_LPI_CTRL_ENH_TH_SHIFT 6 +#define L1C_LPI_CTRL_ENH_EN BIT(5) +#define L1C_LPI_CTRL_CHK_RX BIT(4) +#define L1C_LPI_CTRL_CHK_STATE BIT(3) +#define L1C_LPI_CTRL_GMII BIT(2) +#define L1C_LPI_CTRL_TO_PHY BIT(1) +#define L1C_LPI_CTRL_EN BIT(0) + +#define L1C_LPI_WAIT 0x1444 +#define L1C_LPI_WAIT_TIMER_MASK ASHFT0(0xFFFFUL) +#define L1C_LPI_WAIT_TIMER_SHIFT 0 + +#define L1C_MAC_CTRL 0x1480 +#define L1C_MAC_CTRL_WOLSPED_SWEN BIT(30) /* 0:phy,1:sw */ +#define L1C_MAC_CTRL_MHASH_ALG_HI5B BIT(29) /* 1:legacy, 0:marvl(low5b)*/ +#define L1C_MAC_CTRL_SPAUSE_EN BIT(28) +#define L1C_MAC_CTRL_DBG_EN BIT(27) +#define L1C_MAC_CTRL_BRD_EN BIT(26) +#define L1C_MAC_CTRL_MULTIALL_EN BIT(25) +#define L1C_MAC_CTRL_RX_XSUM_EN BIT(24) +#define L1C_MAC_CTRL_THUGE BIT(23) +#define L1C_MAC_CTRL_MBOF BIT(22) +#define L1C_MAC_CTRL_SPEED_MASK ASHFT20(3UL) +#define L1C_MAC_CTRL_SPEED_SHIFT 20 +#define L1C_MAC_CTRL_SPEED_10_100 1 +#define L1C_MAC_CTRL_SPEED_1000 2 +#define L1C_MAC_CTRL_SIMR BIT(19) +#define L1C_MAC_CTRL_SSTCT BIT(17) +#define L1C_MAC_CTRL_TPAUSE BIT(16) +#define L1C_MAC_CTRL_PROMISC_EN BIT(15) +#define L1C_MAC_CTRL_VLANSTRIP BIT(14) +#define L1C_MAC_CTRL_PRMBLEN_MASK ASHFT10(0xFUL) +#define L1C_MAC_CTRL_PRMBLEN_SHIFT 10 +#define L1C_MAC_CTRL_RHUGE_EN BIT(9) +#define L1C_MAC_CTRL_FLCHK BIT(8) +#define L1C_MAC_CTRL_PCRCE BIT(7) +#define L1C_MAC_CTRL_CRCE BIT(6) +#define L1C_MAC_CTRL_FULLD BIT(5) +#define L1C_MAC_CTRL_LPBACK_EN BIT(4) +#define L1C_MAC_CTRL_RXFC_EN BIT(3) +#define L1C_MAC_CTRL_TXFC_EN BIT(2) +#define L1C_MAC_CTRL_RX_EN BIT(1) +#define L1C_MAC_CTRL_TX_EN BIT(0) + +#define L1C_GAP 0x1484 +#define L1C_GAP_IPGR2_MASK ASHFT24(0x7FUL) +#define L1C_GAP_IPGR2_SHIFT 24 +#define L1C_GAP_IPGR1_MASK ASHFT16(0x7FUL) +#define L1C_GAP_IPGR1_SHIFT 16 +#define L1C_GAP_MIN_IFG_MASK ASHFT8(0xFFUL) +#define L1C_GAP_MIN_IFG_SHIFT 8 +#define L1C_GAP_IPGT_MASK ASHFT0(0x7FUL) +#define L1C_GAP_IPGT_SHIFT 0 + +#define L1C_STAD0 0x1488 +#define L1C_STAD1 0x148C + +#define L1C_HASH_TBL0 0x1490 +#define L1C_HASH_TBL1 0x1494 + +#define L1C_HALFD 0x1498 +#define L1C_HALFD_JAM_IPG_MASK ASHFT24(0xFUL) +#define L1C_HALFD_JAM_IPG_SHIFT 24 +#define L1C_HALFD_ABEBT_MASK ASHFT20(0xFUL) +#define L1C_HALFD_ABEBT_SHIFT 20 +#define L1C_HALFD_ABEBE BIT(19) +#define L1C_HALFD_BPNB BIT(18) +#define L1C_HALFD_NOBO BIT(17) +#define L1C_HALFD_EDXSDFR BIT(16) +#define L1C_HALFD_RETRY_MASK ASHFT12(0xFUL) +#define L1C_HALFD_RETRY_SHIFT 12 +#define L1C_HALFD_LCOL_MASK ASHFT0(0x3FFUL) +#define L1C_HALFD_LCOL_SHIFT 0 + +#define L1C_MTU 0x149C +#define L1C_MTU_JUMBO_TH 1514 +#define L1C_MTU_STD_ALGN 1536 +#define L1C_MTU_MIN 64 + +#define L1C_WOL0 0x14A0 +#define L1C_WOL0_PT7_MATCH BIT(31) +#define L1C_WOL0_PT6_MATCH BIT(30) +#define L1C_WOL0_PT5_MATCH BIT(29) +#define L1C_WOL0_PT4_MATCH BIT(28) +#define L1C_WOL0_PT3_MATCH BIT(27) +#define L1C_WOL0_PT2_MATCH BIT(26) +#define L1C_WOL0_PT1_MATCH BIT(25) +#define L1C_WOL0_PT0_MATCH BIT(24) +#define L1C_WOL0_PT7_EN BIT(23) +#define L1C_WOL0_PT6_EN BIT(22) +#define L1C_WOL0_PT5_EN BIT(21) +#define L1C_WOL0_PT4_EN BIT(20) +#define L1C_WOL0_PT3_EN BIT(19) +#define L1C_WOL0_PT2_EN BIT(18) +#define L1C_WOL0_PT1_EN BIT(17) +#define L1C_WOL0_PT0_EN BIT(16) +#define L1C_WOL0_IPV4_SYNC_EVT BIT(14) +#define L1C_WOL0_IPV6_SYNC_EVT BIT(13) +#define L1C_WOL0_LINK_EVT BIT(10) +#define L1C_WOL0_MAGIC_EVT BIT(9) +#define L1C_WOL0_PATTERN_EVT BIT(8) +#define L1D_WOL0_OOB_EN BIT(6) +#define L1C_WOL0_PME_LINK BIT(5) +#define L1C_WOL0_LINK_EN BIT(4) +#define L1C_WOL0_PME_MAGIC_EN BIT(3) +#define L1C_WOL0_MAGIC_EN BIT(2) +#define L1C_WOL0_PME_PATTERN_EN BIT(1) +#define L1C_WOL0_PATTERN_EN BIT(0) + +#define L1C_WOL1 0x14A4 +#define L1C_WOL1_PT3_LEN_MASK ASHFT24(0xFFUL) +#define L1C_WOL1_PT3_LEN_SHIFT 24 +#define L1C_WOL1_PT2_LEN_MASK ASHFT16(0xFFUL) +#define L1C_WOL1_PT2_LEN_SHIFT 16 +#define L1C_WOL1_PT1_LEN_MASK ASHFT8(0xFFUL) +#define L1C_WOL1_PT1_LEN_SHIFT 8 +#define L1C_WOL1_PT0_LEN_MASK ASHFT0(0xFFUL) +#define L1C_WOL1_PT0_LEN_SHIFT 0 + +#define L1C_WOL2 0x14A8 +#define L1C_WOL2_PT7_LEN_MASK ASHFT24(0xFFUL) +#define L1C_WOL2_PT7_LEN_SHIFT 24 +#define L1C_WOL2_PT6_LEN_MASK ASHFT16(0xFFUL) +#define L1C_WOL2_PT6_LEN_SHIFT 16 +#define L1C_WOL2_PT5_LEN_MASK ASHFT8(0xFFUL) +#define L1C_WOL2_PT5_LEN_SHIFT 8 +#define L1C_WOL2_PT4_LEN_MASK ASHFT0(0xFFUL) +#define L1C_WOL2_PT4_LEN_SHIFT 0 + +#define L1C_SRAM0 0x1500 +#define L1C_SRAM_RFD_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1C_SRAM_RFD_TAIL_ADDR_SHIFT 16 +#define L1C_SRAM_RFD_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1C_SRAM_RFD_HEAD_ADDR_SHIFT 0 +#define L1C_SRAM_RFD_HT_L2CB1 0x02bf02a0L + +#define L1C_SRAM1 0x1510 +#define L1C_SRAM_RFD_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1C_SRAM_RFD_LEN_SHIFT 0 + +#define L1C_SRAM2 0x1518 +#define L1C_SRAM_TRD_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1C_SRAM_TRD_TAIL_ADDR_SHIFT 16 +#define L1C_SRMA_TRD_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1C_SRAM_TRD_HEAD_ADDR_SHIFT 0 +#define L1C_SRAM_TRD_HT_L2CB1 0x03df03c0L + +#define L1C_SRAM3 0x151C +#define L1C_SRAM_TRD_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1C_SRAM_TRD_LEN_SHIFT 0 + +#define L1C_SRAM4 0x1520 +#define L1C_SRAM_RXF_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1C_SRAM_RXF_TAIL_ADDR_SHIFT 16 +#define L1C_SRAM_RXF_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1C_SRAM_RXF_HEAD_ADDR_SHIFT 0 +#define L1C_SRAM_RXF_HT_L2CB1 0x029f0000L + +#define L1C_SRAM5 0x1524 +#define L1C_SRAM_RXF_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1C_SRAM_RXF_LEN_SHIFT 0 +#define L1C_SRAM_RXF_LEN_8K (8*1024) +#define L1C_SRAM_RXF_LEN_L2CB1 0x02a0L + +#define L1C_SRAM6 0x1528 +#define L1C_SRAM_TXF_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1C_SRAM_TXF_TAIL_ADDR_SHIFT 16 +#define L1C_SRAM_TXF_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1C_SRAM_TXF_HEAD_ADDR_SHIFT 0 +#define L1C_SRAM_TXF_HT_L2CB1 0x03bf02c0L + +#define L1C_SRAM7 0x152C +#define L1C_SRAM_TXF_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1C_SRAM_TXF_LEN_SHIFT 0 +#define L1C_SRAM_TXF_LEN_L2CB1 0x0100L + +#define L1C_SRAM8 0x1530 +#define L1C_SRAM_PATTERN_ADDR_MASK ASHFT16(0xFFFUL) +#define L1C_SRAM_PATTERN_ADDR_SHIFT 16 +#define L1C_SRAM_TSO_ADDR_MASK ASHFT0(0xFFFUL) +#define L1C_SRAM_TSO_ADDR_SHIFT 0 + +#define L1C_SRAM9 0x1534 +#define L1C_SRAM_LOAD_PTR BIT(0) + +#define L1C_RX_BASE_ADDR_HI 0x1540 + +#define L1C_TX_BASE_ADDR_HI 0x1544 + +#define L1C_RFD_ADDR_LO 0x1550 +#define L1C_RFD_RING_SZ 0x1560 +#define L1C_RFD_BUF_SZ 0x1564 +#define L1C_RFD_BUF_SZ_MASK ASHFT0(0xFFFFUL) +#define L1C_RFD_BUF_SZ_SHIFT 0 + +#define L1C_RRD_ADDR_LO 0x1568 +#define L1C_RRD_RING_SZ 0x1578 +#define L1C_RRD_RING_SZ_MASK ASHFT0(0xFFFUL) +#define L1C_RRD_RING_SZ_SHIFT 0 + +#define L1C_TPD_PRI1_ADDR_LO 0x157C +#define L1C_TPD_PRI0_ADDR_LO 0x1580 /* LOWEST PRORITY */ + +#define L1C_TPD_PRI1_PIDX 0x15F0 /* 16BIT */ +#define L1C_TPD_PRI0_PIDX 0x15F2 /* 16BIT */ + +#define L1C_TPD_PRI1_CIDX 0x15F4 /* 16BIT */ +#define L1C_TPD_PRI0_CIDX 0x15F6 /* 16BIT */ + +#define L1C_TPD_RING_SZ 0x1584 +#define L1C_TPD_RING_SZ_MASK ASHFT0(0xFFFFUL) +#define L1C_TPD_RING_SZ_SHIFT 0 + +#define L1C_TXQ0 0x1590 +#define L1C_TXQ0_TXF_BURST_PREF_MASK ASHFT16(0xFFFFUL) +#define L1C_TXQ0_TXF_BURST_PREF_SHIFT 16 +#define L1C_TXQ0_TXF_BURST_PREF_DEF 0x200 +#define L1C_TXQ0_TXF_BURST_PREF_L2CB 0x40 +#define L1D_TXQ0_PEDING_CLR BIT(8) +#define L1C_TXQ0_LSO_8023_EN BIT(7) +#define L1C_TXQ0_MODE_ENHANCE BIT(6) +#define L1C_TXQ0_EN BIT(5) +#define L1C_TXQ0_SUPT_IPOPT BIT(4) +#define L1C_TXQ0_TPD_BURSTPREF_MASK ASHFT0(0xFUL) +#define L1C_TXQ0_TPD_BURSTPREF_SHIFT 0 +#define L1C_TXQ0_TPD_BURSTPREF_DEF 5 + +#define L1C_TXQ1 0x1594 +#define L1C_TXQ1_JUMBO_TSOTHR_MASK ASHFT0(0x7FFUL) /* 8BYTES UNIT */ +#define L1C_TXQ1_JUMBO_TSOTHR_SHIFT 0 +#define L1C_TXQ1_JUMBO_TSO_TH (7*1024) /* byte */ + +#define L1C_TXQ2 0x1598 /* ENTER L1 CONTROL */ +#define L1C_TXQ2_BURST_EN BIT(31) +#define L1C_TXQ2_BURST_HI_WM_MASK ASHFT16(0xFFFUL) +#define L1C_TXQ2_BURST_HI_WM_SHIFT 16 +#define L1C_TXQ2_BURST_LO_WM_MASK ASHFT0(0xFFFUL) +#define L1C_TXQ2_BURST_LO_WM_SHIFT 0 + +#define L1C_RFD_PIDX 0x15E0 +#define L1C_RFD_PIDX_MASK ASHFT0(0xFFFUL) +#define L1C_RFD_PIDX_SHIFT 0 + +#define L1C_RFD_CIDX 0x15F8 +#define L1C_RFD_CIDX_MASK ASHFT0(0xFFFUL) +#define L1C_RFD_CIDX_SHIFT 0 + +#define L1C_RXQ0 0x15A0 +#define L1C_RXQ0_EN BIT(31) +#define L1C_RXQ0_CUT_THRU_EN BIT(30) +#define L1C_RXQ0_RSS_HASH_EN BIT(29) +#define L1C_RXQ0_NON_IP_QTBL BIT(28) /* 0:q0,1:table */ +#define L1C_RXQ0_RSS_MODE_MASK ASHFT26(3UL) +#define L1C_RXQ0_RSS_MODE_SHIFT 26 +#define L1C_RXQ0_RSS_MODE_DIS 0 +#define L1C_RXQ0_RSS_MODE_SQSI 1 +#define L1C_RXQ0_RSS_MODE_MQSI 2 +#define L1C_RXQ0_RSS_MODE_MQMI 3 +#define L1C_RXQ0_NUM_RFD_PREF_MASK ASHFT20(0x3FUL) +#define L1C_RXQ0_NUM_RFD_PREF_SHIFT 20 +#define L1C_RXQ0_NUM_RFD_PREF_DEF 8 +#define L1C_RXQ0_RSS_HSTYP_IPV6_TCP_EN BIT(19) +#define L1C_RXQ0_RSS_HSTYP_IPV6_EN BIT(18) +#define L1C_RXQ0_RSS_HSTYP_IPV4_TCP_EN BIT(17) +#define L1C_RXQ0_RSS_HSTYP_IPV4_EN BIT(16) +#define L1C_RXQ0_RSS_HSTYP_ALL (\ + L1C_RXQ0_RSS_HSTYP_IPV6_TCP_EN |\ + L1C_RXQ0_RSS_HSTYP_IPV4_TCP_EN |\ + L1C_RXQ0_RSS_HSTYP_IPV6_EN |\ + L1C_RXQ0_RSS_HSTYP_IPV4_EN) +#define L1C_RXQ0_IDT_TBL_SIZE_MASK ASHFT8(0xFFUL) +#define L1C_RXQ0_IDT_TBL_SIZE_SHIFT 8 +#define L1C_RXQ0_IDT_TBL_SIZE_DEF 0x80 +#define L1C_RXQ0_IPV6_PARSE_EN BIT(7) +#define L1C_RXQ0_ASPM_THRESH_MASK ASHFT0(3UL) +#define L1C_RXQ0_ASPM_THRESH_SHIFT 0 +#define L1C_RXQ0_ASPM_THRESH_NO 0 +#define L1C_RXQ0_ASPM_THRESH_1M 1 +#define L1C_RXQ0_ASPM_THRESH_10M 2 +#define L1C_RXQ0_ASPM_THRESH_100M 3 + +#define L1C_RXQ1 0x15A4 +#define L1C_RXQ1_RFD_PREF_DOWN_MASK ASHFT6(0x3FUL) +#define L1C_RXQ1_RFD_PREF_DOWN_SHIFT 6 +#define L1C_RXQ1_RFD_PREF_UP_MASK ASHFT0(0x3FUL) +#define L1C_RXQ1_RFD_PREF_UP_SHIFT 0 + +#define L1C_RXQ2 0x15A8 +/* XOFF: USED SRAM LOWER THAN IT, THEN NOTIFY THE PEER TO SEND AGAIN */ +#define L1C_RXQ2_RXF_XOFF_THRESH_MASK ASHFT16(0xFFFUL) +#define L1C_RXQ2_RXF_XOFF_THRESH_SHIFT 16 +#define L1C_RXQ2_RXF_XON_THRESH_MASK ASHFT0(0xFFFUL) +#define L1C_RXQ2_RXF_XON_THRESH_SHIFT 0 + +#define L1C_RXQ3 0x15AC +#define L1C_RXQ3_RXD_TIMER_MASK ASHFT16(0xFFFFUL) +#define L1C_RXQ3_RXD_TIMER_SHIFT 16 +#define L1C_RXQ3_RXD_THRESH_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1C_RXQ3_RXD_THRESH_SHIFT 0 + +#define L1C_DMA 0x15C0 +#define L1C_DMA_WPEND_CLR BIT(30) +#define L1C_DMA_RPEND_CLR BIT(29) +#define L1C_DMA_WDLY_CNT_MASK ASHFT16(0xFUL) +#define L1C_DMA_WDLY_CNT_SHIFT 16 +#define L1C_DMA_WDLY_CNT_DEF 4 +#define L1C_DMA_RDLY_CNT_MASK ASHFT11(0x1FUL) +#define L1C_DMA_RDLY_CNT_SHIFT 11 +#define L1C_DMA_RDLY_CNT_DEF 15 +#define L1C_DMA_RREQ_PRI_DATA BIT(10) /* 0:tpd, 1:data */ +#define L1C_DMA_WREQ_BLEN_MASK ASHFT7(7UL) +#define L1C_DMA_WREQ_BLEN_SHIFT 7 +#define L1C_DMA_RREQ_BLEN_MASK ASHFT4(7UL) +#define L1C_DMA_RREQ_BLEN_SHIFT 4 +#define L1C_DMA_RCB_LEN128 BIT(3) /* 0:64bytes,1:128bytes */ +#define L1C_DMA_RORDER_MODE_MASK ASHFT0(7UL) +#define L1C_DMA_RORDER_MODE_SHIFT 0 +#define L1C_DMA_RORDER_MODE_OUT 4 +#define L1C_DMA_RORDER_MODE_ENHANCE 2 +#define L1C_DMA_RORDER_MODE_IN 1 + +#define L1C_SMB_TIMER 0x15C4 + +#define L1C_TINT_TPD_THRSHLD 0x15C8 + +#define L1C_TINT_TIMER 0x15CC + +#define L1C_ISR 0x1600 +#define L1C_ISR_DIS BIT(31) +#define L1C_ISR_PCIE_LNKDOWN BIT(26) +#define L1C_ISR_PCIE_CERR BIT(25) +#define L1C_ISR_PCIE_NFERR BIT(24) +#define L1C_ISR_PCIE_FERR BIT(23) +#define L1C_ISR_PCIE_UR BIT(22) +#define L1C_ISR_MAC_TX BIT(21) +#define L1C_ISR_MAC_RX BIT(20) +#define L1C_ISR_RX_Q0 BIT(16) +#define L1C_ISR_TX_Q0 BIT(15) +#define L1C_ISR_TXQ_TO BIT(14) +#define L1C_ISR_PHY_LPW BIT(13) +#define L1C_ISR_PHY BIT(12) +#define L1C_ISR_TX_CREDIT BIT(11) +#define L1C_ISR_DMAW BIT(10) +#define L1C_ISR_DMAR BIT(9) +#define L1C_ISR_TXF_UR BIT(8) +#define L1C_ISR_RFD_UR BIT(4) +#define L1C_ISR_RXF_OV BIT(3) +#define L1C_ISR_MANU BIT(2) +#define L1C_ISR_TIMER BIT(1) +#define L1C_ISR_SMB BIT(0) + +#define L1C_IMR 0x1604 + +#define L1C_INT_RETRIG 0x1608 /* re-send deassrt/assert + * if sw no reflect */ +#define L1C_INT_RETRIG_TO 20000 /* 40 ms */ + +/* WOL mask register only for L1Dv2.0 and later chips */ +#define L1D_PATTERN_MASK 0x1620 /* 128bytes, sleep state */ +#define L1D_PATTERN_MASK_LEN 128 /* 128bytes, 32DWORDs */ + + +#define L1C_BTROM_CFG 0x1800 /* pwon rst */ + +#define L1C_DRV 0x1804 /* pwon rst */ +/* bit definition is in lx_hwcomm.h */ + +#define L1C_DRV_ERR1 0x1808 /* perst */ +#define L1C_DRV_ERR1_GEN BIT(31) /* geneneral err */ +#define L1C_DRV_ERR1_NOR BIT(30) /* rrd.nor */ +#define L1C_DRV_ERR1_TRUNC BIT(29) +#define L1C_DRV_ERR1_RES BIT(28) +#define L1C_DRV_ERR1_INTFATAL BIT(27) +#define L1C_DRV_ERR1_TXQPEND BIT(26) +#define L1C_DRV_ERR1_DMAW BIT(25) +#define L1C_DRV_ERR1_DMAR BIT(24) +#define L1C_DRV_ERR1_PCIELNKDWN BIT(23) +#define L1C_DRV_ERR1_PKTSIZE BIT(22) +#define L1C_DRV_ERR1_FIFOFUL BIT(21) +#define L1C_DRV_ERR1_RFDUR BIT(20) +#define L1C_DRV_ERR1_RRDSI BIT(19) +#define L1C_DRV_ERR1_UPDATE BIT(18) + +#define L1C_DRV_ERR2 0x180C /* perst */ + +#define L1C_CLK_GATE 0x1814 +#define L1C_CLK_GATE_RXMAC BIT(5) +#define L1C_CLK_GATE_TXMAC BIT(4) +#define L1C_CLK_GATE_RXQ BIT(3) +#define L1C_CLK_GATE_TXQ BIT(2) +#define L1C_CLK_GATE_DMAR BIT(1) +#define L1C_CLK_GATE_DMAW BIT(0) +#define L1C_CLK_GATE_ALL (\ + L1C_CLK_GATE_RXMAC |\ + L1C_CLK_GATE_TXMAC |\ + L1C_CLK_GATE_RXQ |\ + L1C_CLK_GATE_TXQ |\ + L1C_CLK_GATE_DMAR |\ + L1C_CLK_GATE_DMAW) + +#define L1C_DBG_ADDR 0x1900 /* DWORD reg */ +#define L1C_DBG_DATA 0x1904 /* DWORD reg */ + +/***************************** IO mapping registers ***************************/ +#define L1C_IO_ADDR 0x00 /* DWORD reg */ +#define L1C_IO_DATA 0x04 /* DWORD reg */ +#define L1C_IO_MASTER 0x08 /* DWORD same as reg0x1400 */ +#define L1C_IO_MAC_CTRL 0x0C /* DWORD same as reg0x1480*/ +#define L1C_IO_ISR 0x10 /* DWORD same as reg0x1600 */ +#define L1C_IO_IMR 0x14 /* DWORD same as reg0x1604 */ +#define L1C_IO_TPD_PRI1_PIDX 0x18 /* WORD same as reg0x15F0 */ +#define L1C_IO_TPD_PRI0_PIDX 0x1A /* WORD same as reg0x15F2 */ +#define L1C_IO_TPD_PRI1_CIDX 0x1C /* WORD same as reg0x15F4 */ +#define L1C_IO_TPD_PRI0_CIDX 0x1E /* WORD same as reg0x15F6 */ +#define L1C_IO_RFD_PIDX 0x20 /* WORD same as reg0x15E0 */ +#define L1C_IO_RFD_CIDX 0x30 /* WORD same as reg0x15F8 */ +#define L1C_IO_MDIO 0x38 /* WORD same as reg0x1414 */ +#define L1C_IO_PHY_CTRL 0x3C /* DWORD same as reg0x140C */ + + + +/********************* PHY regs definition ***************************/ + +/* Autoneg Advertisement Register (0x4) */ +#define L1C_ADVERTISE_SPEED_MASK 0x01E0 +#define L1C_ADVERTISE_DEFAULT_CAP 0x0DE0 /* diff with L1C */ + +/* 1000BASE-T Control Register (0x9) */ +#define L1C_GIGA_CR_1000T_HD_CAPS 0x0100 +#define L1C_GIGA_CR_1000T_FD_CAPS 0x0200 +#define L1C_GIGA_CR_1000T_REPEATER_DTE 0x0400 +#define L1C_GIGA_CR_1000T_MS_VALUE 0x0800 +#define L1C_GIGA_CR_1000T_MS_ENABLE 0x1000 +#define L1C_GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 +#define L1C_GIGA_CR_1000T_TEST_MODE_1 0x2000 +#define L1C_GIGA_CR_1000T_TEST_MODE_2 0x4000 +#define L1C_GIGA_CR_1000T_TEST_MODE_3 0x6000 +#define L1C_GIGA_CR_1000T_TEST_MODE_4 0x8000 +#define L1C_GIGA_CR_1000T_SPEED_MASK 0x0300 +#define L1C_GIGA_CR_1000T_DEFAULT_CAP 0x0300 + +/* 1000BASE-T Status Register */ +#define L1C_MII_GIGA_SR 0x0A + +/* PHY Specific Status Register */ +#define L1C_MII_GIGA_PSSR 0x11 +#define L1C_GIGA_PSSR_FC_RXEN 0x0004 +#define L1C_GIGA_PSSR_FC_TXEN 0x0008 +#define L1C_GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 +#define L1C_GIGA_PSSR_DPLX 0x2000 +#define L1C_GIGA_PSSR_SPEED 0xC000 +#define L1C_GIGA_PSSR_10MBS 0x0000 +#define L1C_GIGA_PSSR_100MBS 0x4000 +#define L1C_GIGA_PSSR_1000MBS 0x8000 + +/* PHY Interrupt Enable Register */ +#define L1C_MII_IER 0x12 +#define L1C_IER_LINK_UP 0x0400 +#define L1C_IER_LINK_DOWN 0x0800 + +/* PHY Interrupt Status Register */ +#define L1C_MII_ISR 0x13 +#define L1C_ISR_LINK_UP 0x0400 +#define L1C_ISR_LINK_DOWN 0x0800 + +/* Cable-Detect-Test Control Register */ +#define L1C_MII_CDTC 0x16 +#define L1C_CDTC_EN 1 /* sc */ +#define L1C_CDTC_PAIR_MASK ASHFT8(3U) +#define L1C_CDTC_PAIR_SHIFT 8 + + +/* Cable-Detect-Test Status Register */ +#define L1C_MII_CDTS 0x1C +#define L1C_CDTS_STATUS_MASK ASHFT8(3U) +#define L1C_CDTS_STATUS_SHIFT 8 +#define L1C_CDTS_STATUS_NORMAL 0 +#define L1C_CDTS_STATUS_SHORT 1 +#define L1C_CDTS_STATUS_OPEN 2 +#define L1C_CDTS_STATUS_INVALID 3 + +#define L1C_MII_DBG_ADDR 0x1D +#define L1C_MII_DBG_DATA 0x1E + +/***************************** debug port *************************************/ + +#define L1C_MIIDBG_ANACTRL 0x00 +#define L1C_ANACTRL_CLK125M_DELAY_EN BIT(15) +#define L1C_ANACTRL_VCO_FAST BIT(14) +#define L1C_ANACTRL_VCO_SLOW BIT(13) +#define L1C_ANACTRL_AFE_MODE_EN BIT(12) +#define L1C_ANACTRL_LCKDET_PHY BIT(11) +#define L1C_ANACTRL_LCKDET_EN BIT(10) +#define L1C_ANACTRL_OEN_125M BIT(9) +#define L1C_ANACTRL_HBIAS_EN BIT(8) +#define L1C_ANACTRL_HB_EN BIT(7) +#define L1C_ANACTRL_SEL_HSP BIT(6) +#define L1C_ANACTRL_CLASSA_EN BIT(5) +#define L1C_ANACTRL_MANUSWON_SWR_MASK ASHFT2(3U) +#define L1C_ANACTRL_MANUSWON_SWR_SHIFT 2 +#define L1C_ANACTRL_MANUSWON_SWR_2V 0 +#define L1C_ANACTRL_MANUSWON_SWR_1P9V 1 +#define L1C_ANACTRL_MANUSWON_SWR_1P8V 2 +#define L1C_ANACTRL_MANUSWON_SWR_1P7V 3 +#define L1C_ANACTRL_MANUSWON_BW3_4M BIT(1) +#define L1C_ANACTRL_RESTART_CAL BIT(0) +#define L1C_ANACTRL_DEF 0x02EF + + +#define L1C_MIIDBG_SYSMODCTRL 0x04 +#define L1C_SYSMODCTRL_IECHOADJ_PFMH_PHY BIT(15) +#define L1C_SYSMODCTRL_IECHOADJ_BIASGEN BIT(14) +#define L1C_SYSMODCTRL_IECHOADJ_PFML_PHY BIT(13) +#define L1C_SYSMODCTRL_IECHOADJ_PS_MASK ASHFT10(3U) +#define L1C_SYSMODCTRL_IECHOADJ_PS_SHIFT 10 +#define L1C_SYSMODCTRL_IECHOADJ_PS_40 3 +#define L1C_SYSMODCTRL_IECHOADJ_PS_20 2 +#define L1C_SYSMODCTRL_IECHOADJ_PS_0 1 +#define L1C_SYSMODCTRL_IECHOADJ_10BT_100MV BIT(6) /* 1:100mv, 0:200mv */ +#define L1C_SYSMODCTRL_IECHOADJ_HLFAP_MASK ASHFT4(3U) +#define L1C_SYSMODCTRL_IECHOADJ_HLFAP_SHIFT 4 +#define L1C_SYSMODCTRL_IECHOADJ_VDFULBW BIT(3) +#define L1C_SYSMODCTRL_IECHOADJ_VDBIASHLF BIT(2) +#define L1C_SYSMODCTRL_IECHOADJ_VDAMPHLF BIT(1) +#define L1C_SYSMODCTRL_IECHOADJ_VDLANSW BIT(0) +#define L1C_SYSMODCTRL_IECHOADJ_DEF 0x88BB /* ???? */ + + + +#define L1D_MIIDBG_SYSMODCTRL 0x04 /* l1d & l2cb */ +#define L1D_SYSMODCTRL_IECHOADJ_CUR_ADD BIT(15) +#define L1D_SYSMODCTRL_IECHOADJ_CUR_MASK ASHFT12(7U) +#define L1D_SYSMODCTRL_IECHOADJ_CUR_SHIFT 12 +#define L1D_SYSMODCTRL_IECHOADJ_VOL_MASK ASHFT8(0xFU) +#define L1D_SYSMODCTRL_IECHOADJ_VOL_SHIFT 8 +#define L1D_SYSMODCTRL_IECHOADJ_VOL_17ALL 3 +#define L1D_SYSMODCTRL_IECHOADJ_VOL_100M15 1 +#define L1D_SYSMODCTRL_IECHOADJ_VOL_10M17 0 +#define L1D_SYSMODCTRL_IECHOADJ_BIAS1_MASK ASHFT4(0xFU) +#define L1D_SYSMODCTRL_IECHOADJ_BIAS1_SHIFT 4 +#define L1D_SYSMODCTRL_IECHOADJ_BIAS2_MASK ASHFT0(0xFU) +#define L1D_SYSMODCTRL_IECHOADJ_BIAS2_SHIFT 0 +#define L1D_SYSMODCTRL_IECHOADJ_DEF 0x4FBB + + +#define L1C_MIIDBG_SRDSYSMOD 0x05 +#define L1C_SRDSYSMOD_LCKDET_EN BIT(13) +#define L1C_SRDSYSMOD_PLL_EN BIT(11) +#define L1C_SRDSYSMOD_SEL_HSP BIT(10) +#define L1C_SRDSYSMOD_HLFTXDR BIT(9) +#define L1C_SRDSYSMOD_TXCLK_DELAY_EN BIT(8) +#define L1C_SRDSYSMOD_TXELECIDLE BIT(7) +#define L1C_SRDSYSMOD_DEEMP_EN BIT(6) +#define L1C_SRDSYSMOD_MS_PAD BIT(2) +#define L1C_SRDSYSMOD_CDR_ADC_VLTG BIT(1) +#define L1C_SRDSYSMOD_CDR_DAC_1MA BIT(0) +#define L1C_SRDSYSMOD_DEF 0x2C46 + +#define L1C_MIIDBG_CFGLPSPD 0x0A +#define L1C_CFGLPSPD_RSTCNT_MASK ASHFT(3U) +#define L1C_CFGLPSPD_RSTCNT_SHIFT 14 +#define L1C_CFGLPSPD_RSTCNT_CLK125SW BIT(13) + +#define L1C_MIIDBG_HIBNEG 0x0B +#define L1C_HIBNEG_PSHIB_EN BIT(15) +#define L1C_HIBNEG_WAKE_BOTH BIT(14) +#define L1C_HIBNEG_ONOFF_ANACHG_SUDEN BIT(13) +#define L1C_HIBNEG_HIB_PULSE BIT(12) +#define L1C_HIBNEG_GATE_25M_EN BIT(11) +#define L1C_HIBNEG_RST_80U BIT(10) +#define L1C_HIBNEG_RST_TIMER_MASK ASHFT8(3U) +#define L1C_HIBNEG_RST_TIMER_SHIFT 8 +#define L1C_HIBNEG_GTX_CLK_DELAY_MASK ASHFT5(3U) +#define L1C_HIBNEG_GTX_CLK_DELAY_SHIFT 5 +#define L1C_HIBNEG_BYPSS_BRKTIMER BIT(4) +#define L1C_HIBNEG_DEF 0xBC40 + +#define L1C_MIIDBG_TST10BTCFG 0x12 +#define L1C_TST10BTCFG_INTV_TIMER_MASK ASHFT14(3U) +#define L1C_TST10BTCFG_INTV_TIMER_SHIFT 14 +#define L1C_TST10BTCFG_TRIGER_TIMER_MASK ASHFT12(3U) +#define L1C_TST10BTCFG_TRIGER_TIMER_SHIFT 12 +#define L1C_TST10BTCFG_DIV_MAN_MLT3_EN BIT(11) +#define L1C_TST10BTCFG_OFF_DAC_IDLE BIT(10) +#define L1C_TST10BTCFG_LPBK_DEEP BIT(2) /* 1:deep,0:shallow */ +#define L1C_TST10BTCFG_DEF 0x4C04 + +#define L1C_MIIDBG_AZ_ANADECT 0x15 +#define L1C_AZ_ANADECT_10BTRX_TH BIT(15) +#define L1C_AZ_ANADECT_BOTH_01CHNL BIT(14) +#define L1C_AZ_ANADECT_INTV_MASK ASHFT8(0x3FU) +#define L1C_AZ_ANADECT_INTV_SHIFT 8 +#define L1C_AZ_ANADECT_THRESH_MASK ASHFT4(0xFU) +#define L1C_AZ_ANADECT_THRESH_SHIFT 4 +#define L1C_AZ_ANADECT_CHNL_MASK ASHFT0(0xFU) +#define L1C_AZ_ANADECT_CHNL_SHIFT 0 +#define L1C_AZ_ANADECT_DEF 0x3220 +#define L1C_AZ_ANADECT_LONG 0xb210 + +#define L1D_MIIDBG_MSE16DB 0x18 +#define L1D_MSE16DB_UP 0x05EA +#define L1D_MSE16DB_DOWN 0x02EA + + +#define L1C_MIIDBG_LEGCYPS 0x29 +#define L1C_LEGCYPS_EN BIT(15) +#define L1C_LEGCYPS_DAC_AMP1000_MASK ASHFT12(7U) +#define L1C_LEGCYPS_DAC_AMP1000_SHIFT 12 +#define L1C_LEGCYPS_DAC_AMP100_MASK ASHFT9(7U) +#define L1C_LEGCYPS_DAC_AMP100_SHIFT 9 +#define L1C_LEGCYPS_DAC_AMP10_MASK ASHFT6(7U) +#define L1C_LEGCYPS_DAC_AMP10_SHIFT 6 +#define L1C_LEGCYPS_UNPLUG_TIMER_MASK ASHFT3(7U) +#define L1C_LEGCYPS_UNPLUG_TIMER_SHIFT 3 +#define L1C_LEGCYPS_UNPLUG_DECT_EN BIT(2) +#define L1C_LEGCYPS_ECNC_PS_EN BIT(0) +#define L1D_LEGCYPS_DEF 0x129D +#define L1C_LEGCYPS_DEF 0x36DD + +#define L1C_MIIDBG_TST100BTCFG 0x36 +#define L1C_TST100BTCFG_NORMAL_BW_EN BIT(15) +#define L1C_TST100BTCFG_BADLNK_BYPASS BIT(14) +#define L1C_TST100BTCFG_SHORTCABL_TH_MASK ASHFT8(0x3FU) +#define L1C_TST100BTCFG_SHORTCABL_TH_SHIFT 8 +#define L1C_TST100BTCFG_LITCH_EN BIT(7) +#define L1C_TST100BTCFG_VLT_SW BIT(6) +#define L1C_TST100BTCFG_LONGCABL_TH_MASK ASHFT0(0x3FU) +#define L1C_TST100BTCFG_LONGCABL_TH_SHIFT 0 +#define L1C_TST100BTCFG_DEF 0xE12C + +#define L1C_MIIDBG_VOLT_CTRL 0x3B +#define L1C_VOLT_CTRL_CABLE1TH_MASK ASHFT7(0x1FFU) +#define L1C_VOLT_CTRL_CABLE1TH_SHIFT 7 +#define L1C_VOLT_CTRL_AMPCTRL_MASK ASHFT5(3U) +#define L1C_VOLT_CTRL_AMPCTRL_SHIFT 5 +#define L1C_VOLT_CTRL_SW_BYPASS BIT(4) +#define L1C_VOLT_CTRL_SWLOWEST BIT(3) +#define L1C_VOLT_CTRL_DACAMP10_MASK ASHFT0(7U) +#define L1C_VOLT_CTRL_DACAMP10_SHIFT 0 + +#define L1C_MIIDBG_CABLE1TH_DET 0x3E +#define L1C_CABLE1TH_DET_EN BIT(15) + +/***************************** extension **************************************/ + +/******* dev 3 *********/ +#define L1C_MIIEXT_PCS 3 + +#define L1C_MIIEXT_CLDCTRL3 0x8003 +#define L1C_CLDCTRL3_BP_CABLE1TH_DET_GT BIT(15) +#define L1C_CLDCTRL3_AZ_DISAMP BIT(12) +#define L1C_CLDCTRL3_L2CB 0x4D19 +#define L1C_CLDCTRL3_L1D 0xDD19 + +#define L1C_MIIEXT_CLDCTRL6 0x8006 +#define L1C_CLDCTRL6_CAB_LEN_MASK ASHFT0(0x1FFU) +#define L1C_CLDCTRL6_CAB_LEN_SHIFT 0 +#define L1C_CLDCTRL6_CAB_LEN_SHORT 0x50 + +#define L1C_MIIEXT_CLDCTRL7 0x8007 +#define L1C_CLDCTRL7_VDHLF_BIAS_TH_MASK ASHFT9(0x7FU) +#define L1C_CLDCTRL7_VDHLF_BIAS_TH_SHIFT 9 +#define L1C_CLDCTRL7_AFE_AZ_MASK ASHFT4(0x1FU) +#define L1C_CLDCTRL7_AFE_AZ_SHIFT 4 +#define L1C_CLDCTRL7_SIDE_PEAK_TH_MASK ASHFT0(0xFU) +#define L1C_CLDCTRL7_SIDE_PEAK_TH_SHIFT 0 +#define L1C_CLDCTRL7_DEF 0x6BF6 /* ???? */ +#define L1C_CLDCTRL7_FPGA_DEF 0x0005 +#define L1C_CLDCTRL7_L2CB 0x0175 + +#define L1C_MIIEXT_AZCTRL 0x8008 +#define L1C_AZCTRL_SHORT_TH_MASK ASHFT8(0xFFU) +#define L1C_AZCTRL_SHORT_TH_SHIFT 8 +#define L1C_AZCTRL_LONG_TH_MASK ASHFT0(0xFFU) +#define L1C_AZCTRL_LONG_TH_SHIFT 0 +#define L1C_AZCTRL_DEF 0x1629 +#define L1C_AZCTRL_FPGA_DEF 0x101D +#define L1C_AZCTRL_L1D 0x2034 + +#define L1C_MIIEXT_AZCTRL2 0x8009 +#define L1C_AZCTRL2_WAKETRNING_MASK ASHFT8(0xFFU) +#define L1C_AZCTRL2_WAKETRNING_SHIFT 8 +#define L1C_AZCTRL2_QUIET_TIMER_MASH ASHFT6(3U) +#define L1C_AZCTRL2_QUIET_TIMER_SHIFT 6 +#define L1C_AZCTRL2_PHAS_JMP2 BIT(4) +#define L1C_AZCTRL2_CLKTRCV_125MD16 BIT(3) +#define L1C_AZCTRL2_GATE1000_EN BIT(2) +#define L1C_AZCTRL2_AVRG_FREQ BIT(1) +#define L1C_AZCTRL2_PHAS_JMP4 BIT(0) +#define L1C_AZCTRL2_DEF 0x32C0 +#define L1C_AZCTRL2_FPGA_DEF 0x40C8 +#define L1C_AZCTRL2_L2CB 0xE003 +#define L1C_AZCTRL2_L1D2 0x18C0 + + +#define L1C_MIIEXT_AZCTRL4 0x800B +#define L1C_AZCTRL4_WAKE_STH_L2CB 0x0094 + +#define L1C_MIIEXT_AZCTRL5 0x800C +#define L1C_AZCTRL5_WAKE_LTH_L2CB 0x00EB + +#define L1C_MIIEXT_AZCTRL6 0x800D +#define L1C_AZCTRL6_L1D2 0x003F + + + +/********* dev 7 **********/ +#define L1C_MIIEXT_ANEG 7 + +#define L1C_MIIEXT_LOCAL_EEEADV 0x3C +#define L1C_LOCAL_EEEADV_1000BT BIT(2) +#define L1C_LOCAL_EEEADV_100BT BIT(1) + +#define L1C_MIIEXT_REMOTE_EEEADV 0x3D +#define L1C_REMOTE_EEEADV_1000BT BIT(2) +#define L1C_REMOTE_EEEADV_100BT BIT(1) + +#define L1C_MIIEXT_EEE_ANEG 0x8000 +#define L1C_EEE_ANEG_1000M BIT(2) +#define L1C_EEE_ANEG_100M BIT(1) + + + + +/******************************************************************************/ + +/* functions */ + +/* get permanent mac address from + * return + * 0: success + * non-0:fail + */ +u16 l1c_get_perm_macaddr(struct alx_hw *hw, u8 *addr); + + +/* reset mac & dma + * return + * 0: success + * non-0:fail + */ +u16 l1c_reset_mac(struct alx_hw *hw); + +/* reset phy + * return + * 0: success + * non-0:fail + */ +u16 l1c_reset_phy(struct alx_hw *hw, bool pws_en, bool az_en, bool ptp_en); + + +/* reset pcie + * just reset pcie relative registers (pci command, clk, aspm...) + * return + * 0:success + * non-0:fail + */ +u16 l1c_reset_pcie(struct alx_hw *hw, bool l0s_en, bool l1_en); + + +/* disable/enable MAC/RXQ/TXQ + * en + * true:enable + * false:disable + * return + * 0:success + * non-0-fail + */ +u16 l1c_enable_mac(struct alx_hw *hw, bool en, u16 en_ctrl); + +/* enable/disable aspm support + * that will change settings for phy/mac/pcie + */ +u16 l1c_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en, u8 lnk_stat); + + +/* initialize phy for speed / flow control + * lnk_cap + * if autoNeg, is link capability to tell the peer + * if force mode, is forced speed/duplex + */ +u16 l1c_init_phy_spdfc(struct alx_hw *hw, bool auto_neg, + u8 lnk_cap, bool fc_en); + +/* do post setting on phy if link up/down event occur + */ +u16 l1c_post_phy_link(struct alx_hw *hw, bool linkon, u8 wire_spd); + + +/* do power saving setting befor enter suspend mode + * NOTE: + * 1. phy link must be established before calling this function + * 2. wol option (pattern,magic,link,etc.) is configed before call it. + */ +u16 l1c_powersaving(struct alx_hw *hw, u8 wire_spd, bool wol_en, + bool mac_txen, bool mac_rxen, bool pws_en); + + +/* read phy register */ +u16 l1c_read_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, u16 reg, + u16 *data); + +/* write phy register */ +u16 l1c_write_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, u16 reg, + u16 data); + +/* phy debug port */ +u16 l1c_read_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 *data); +u16 l1c_write_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 data); + +/* check the configuration of the PHY */ +u16 l1c_get_phy_config(struct alx_hw *hw); + +/* + * initialize mac basically + * most of hi-feature no init + * MAC/PHY should be reset before call this function + */ +u16 l1c_init_mac(struct alx_hw *hw, u8 *addr, u32 txmem_hi, + u32 *tx_mem_lo, u8 tx_qnum, u16 txring_sz, + u32 rxmem_hi, u32 rfdmem_lo, u32 rrdmem_lo, + u16 rxring_sz, u16 rxbuf_sz, u16 smb_timer, + u16 mtu, u16 int_mod, bool hash_legacy); + + + +#endif/*L1C_HW_H_*/ + --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alf_cb.c @@ -0,0 +1,1187 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include "alf_hw.h" + + +#define ALF_REV_ID_AR8161_B0 0x10 + +/* definition for MSIX */ +#define ALF_MSIX_ENTRY_BASE 0x2000 +#define ALF_MSIX_ENTRY_SIZE 16 +#define ALF_MSIX_MSG_LOADDR_OFF 0 +#define ALF_MSIX_MSG_HIADDR_OFF 4 +#define ALF_MSIX_MSG_DATA_OFF 8 +#define ALF_MSIX_MSG_CTRL_OFF 12 + +#define ALF_MSIX_INDEX_RXQ0 0 +#define ALF_MSIX_INDEX_RXQ1 1 +#define ALF_MSIX_INDEX_RXQ2 2 +#define ALF_MSIX_INDEX_RXQ3 3 +#define ALF_MSIX_INDEX_RXQ4 4 +#define ALF_MSIX_INDEX_RXQ5 5 +#define ALF_MSIX_INDEX_RXQ6 6 +#define ALF_MSIX_INDEX_RXQ7 7 +#define ALF_MSIX_INDEX_TXQ0 8 +#define ALF_MSIX_INDEX_TXQ1 9 +#define ALF_MSIX_INDEX_TXQ2 10 +#define ALF_MSIX_INDEX_TXQ3 11 +#define ALF_MSIX_INDEX_TIMER 12 +#define ALF_MSIX_INDEX_ALERT 13 +#define ALF_MSIX_INDEX_SMB 14 +#define ALF_MSIX_INDEX_PHY 15 + + +#define ALF_SRAM_BASE L1F_SRAM0 +#define ALF_SRAM(_i, _type) \ + (ALF_SRAM_BASE + ((_i) * sizeof(_type))) + +#define ALF_MIB_BASE L1F_MIB_BASE +#define ALF_MIB(_i, _type) \ + (ALF_MIB_BASE + ((_i) * sizeof(_type))) + +/* definition for RSS */ +#define ALF_RSS_KEY_BASE L1F_RSS_KEY0 +#define ALF_RSS_IDT_BASE L1F_RSS_IDT_TBL0 +#define ALF_RSS_KEY(_i, _type) \ + (ALF_RSS_KEY_BASE + ((_i) * sizeof(_type))) +#define ALF_RSS_TBL(_i, _type) \ + (L1F_RSS_IDT_TBL0 + ((_i) * sizeof(_type))) + + +/* NIC */ +static int alf_identify_nic(struct alx_hw *hw) +{ + u32 drv; + + if (hw->pci_revid < ALX_REV_ID_AR8161_V2_0) + return 0; + + /* check from V2_0(b0) to ... */ + switch (hw->pci_revid) { + default: + alx_mem_r32(hw, L1F_DRV, &drv); + if (drv & LX_DRV_DISABLE) + return -EINVAL; + break; + } + return 0; +} + + +/* PHY */ +static int alf_read_phy_reg(struct alx_hw *hw, u16 reg_addr, u16 *phy_data) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&hw->mdio_lock, flags); + + if (l1f_read_phy(hw, false, ALX_MDIO_DEV_TYPE_NORM, false, reg_addr, + phy_data)) { + alx_hw_err(hw, "error when read phy reg\n"); + retval = -EINVAL; + } + + spin_unlock_irqrestore(&hw->mdio_lock, flags); + return retval; +} + + +static int alf_write_phy_reg(struct alx_hw *hw, u16 reg_addr, u16 phy_data) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&hw->mdio_lock, flags); + + if (l1f_write_phy(hw, false, ALX_MDIO_DEV_TYPE_NORM, false, reg_addr, + phy_data)) { + alx_hw_err(hw, "error when write phy reg\n"); + retval = -EINVAL; + } + + spin_unlock_irqrestore(&hw->mdio_lock, flags); + return retval; +} + + +static int alf_init_phy(struct alx_hw *hw) +{ + u16 phy_id[2]; + int retval; + + spin_lock_init(&hw->mdio_lock); + + retval = alf_read_phy_reg(hw, MII_PHYSID1, &phy_id[0]); + if (retval) + return retval; + retval = alf_read_phy_reg(hw, MII_PHYSID2, &phy_id[1]); + if (retval) + return retval; + memcpy(&hw->phy_id, phy_id, sizeof(hw->phy_id)); + + hw->autoneg_advertised = ALX_LINK_SPEED_1GB_FULL | + ALX_LINK_SPEED_10_HALF | + ALX_LINK_SPEED_10_FULL | + ALX_LINK_SPEED_100_HALF | + ALX_LINK_SPEED_100_FULL; + return retval; +} + + +static int alf_reset_phy(struct alx_hw *hw) +{ + int retval = 0; + bool pws_en, az_en, ptp_en; + + pws_en = az_en = ptp_en = false; + CLI_HW_FLAG(PWSAVE_EN); + CLI_HW_FLAG(AZ_EN); + CLI_HW_FLAG(PTP_EN); + + if (CHK_HW_FLAG(PWSAVE_CAP)) { + pws_en = true; + SET_HW_FLAG(PWSAVE_EN); + } + + if (CHK_HW_FLAG(AZ_CAP)) { + az_en = true; + SET_HW_FLAG(AZ_EN); + } + + if (CHK_HW_FLAG(PTP_CAP)) { + ptp_en = true; + SET_HW_FLAG(PTP_EN); + } + + alx_hw_info(hw, "reset PHY, pws = %d, az = %d, ptp = %d\n", + pws_en, az_en, ptp_en); + if (l1f_reset_phy(hw, pws_en, az_en, ptp_en)) { + alx_hw_err(hw, "error when reset phy\n"); + retval = -EINVAL; + } + return retval; +} + + +/* LINK */ +static int alf_setup_phy_link(struct alx_hw *hw, u32 speed, bool autoneg, + bool fc) +{ + u8 link_cap = 0; + int retval = 0; + + alx_hw_info(hw, "speed = 0x%x, autoneg = %d\n", speed, autoneg); + if (speed & ALX_LINK_SPEED_1GB_FULL) + link_cap |= LX_LC_1000F; + + if (speed & ALX_LINK_SPEED_100_FULL) + link_cap |= LX_LC_100F; + + if (speed & ALX_LINK_SPEED_100_HALF) + link_cap |= LX_LC_100H; + + if (speed & ALX_LINK_SPEED_10_FULL) + link_cap |= LX_LC_10F; + + if (speed & ALX_LINK_SPEED_10_HALF) + link_cap |= LX_LC_10H; + + if (l1f_init_phy_spdfc(hw, autoneg, link_cap, fc)) { + alx_hw_err(hw, "error when init phy speed and fc\n"); + retval = -EINVAL; + } + + return retval; +} + + +static int alf_setup_phy_link_speed(struct alx_hw *hw, u32 speed, + bool autoneg, bool fc) +{ + /* + * Clear autoneg_advertised and set new values based on input link + * speed. + */ + hw->autoneg_advertised = 0; + + if (speed & ALX_LINK_SPEED_1GB_FULL) + hw->autoneg_advertised |= ALX_LINK_SPEED_1GB_FULL; + + if (speed & ALX_LINK_SPEED_100_FULL) + hw->autoneg_advertised |= ALX_LINK_SPEED_100_FULL; + + if (speed & ALX_LINK_SPEED_100_HALF) + hw->autoneg_advertised |= ALX_LINK_SPEED_100_HALF; + + if (speed & ALX_LINK_SPEED_10_FULL) + hw->autoneg_advertised |= ALX_LINK_SPEED_10_FULL; + + if (speed & ALX_LINK_SPEED_10_HALF) + hw->autoneg_advertised |= ALX_LINK_SPEED_10_HALF; + + return alf_setup_phy_link(hw, hw->autoneg_advertised, + autoneg, fc); +} + + +static int alf_check_phy_link(struct alx_hw *hw, u32 *speed, bool *link_up) +{ + u16 bmsr, giga; + int retval; + + alf_read_phy_reg(hw, MII_BMSR, &bmsr); + retval = alf_read_phy_reg(hw, MII_BMSR, &bmsr); + if (retval) + return retval; + + if (!(bmsr & BMSR_LSTATUS)) { + *link_up = false; + *speed = ALX_LINK_SPEED_UNKNOWN; + return 0; + } + *link_up = true; + + /* Read PHY Specific Status Register (17) */ + retval = alf_read_phy_reg(hw, L1F_MII_GIGA_PSSR, &giga); + if (retval) + return retval; + + + if (!(giga & L1F_GIGA_PSSR_SPD_DPLX_RESOLVED)) { + alx_hw_err(hw, "error for speed duplex resolved\n"); + return -EINVAL; + } + + switch (giga & L1F_GIGA_PSSR_SPEED) { + case L1F_GIGA_PSSR_1000MBS: + if (giga & L1F_GIGA_PSSR_DPLX) + *speed = ALX_LINK_SPEED_1GB_FULL; + else + alx_hw_err(hw, "1000M half is invalid"); + break; + case L1F_GIGA_PSSR_100MBS: + if (giga & L1F_GIGA_PSSR_DPLX) + *speed = ALX_LINK_SPEED_100_FULL; + else + *speed = ALX_LINK_SPEED_100_HALF; + break; + case L1F_GIGA_PSSR_10MBS: + if (giga & L1F_GIGA_PSSR_DPLX) + *speed = ALX_LINK_SPEED_10_FULL; + else + *speed = ALX_LINK_SPEED_10_HALF; + break; + default: + *speed = ALX_LINK_SPEED_UNKNOWN; + retval = -EINVAL; + break; + } + return retval; +} + + +/* + * 1. stop_mac + * 2. reset mac & dma by reg1400(MASTER) + * 3. control speed/duplex, hash-alg + * 4. clock switch setting + */ +static int alf_reset_mac(struct alx_hw *hw) +{ + int retval = 0; + + if (l1f_reset_mac(hw)) { + alx_hw_err(hw, "error when reset mac\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alf_start_mac(struct alx_hw *hw) +{ + u16 en_ctrl = 0; + int retval = 0; + + /* set link speed param */ + switch (hw->link_speed) { + case ALX_LINK_SPEED_1GB_FULL: + en_ctrl |= LX_MACSPEED_1000; + /* fall through */ + case ALX_LINK_SPEED_100_FULL: + case ALX_LINK_SPEED_10_FULL: + en_ctrl |= LX_MACDUPLEX_FULL; + break; + } + + /* set fc param*/ + switch (hw->cur_fc_mode) { + case alx_fc_full: + en_ctrl |= LX_FC_RXEN; /* Flow Control RX Enable */ + en_ctrl |= LX_FC_TXEN; /* Flow Control TX Enable */ + break; + case alx_fc_rx_pause: + en_ctrl |= LX_FC_RXEN; /* Flow Control RX Enable */ + break; + case alx_fc_tx_pause: + en_ctrl |= LX_FC_TXEN; /* Flow Control TX Enable */ + break; + default: + break; + } + + if (hw->fc_single_pause) + en_ctrl |= LX_SINGLE_PAUSE; + + en_ctrl |= LX_FLT_DIRECT; /* RX Enable; and TX Always Enable */ + en_ctrl |= LX_FLT_BROADCAST; /* RX Broadcast Enable */ + en_ctrl |= LX_ADD_FCS; + + if (CHK_HW_FLAG(VLANSTRIP_EN)) + en_ctrl |= LX_VLAN_STRIP; + + if (CHK_HW_FLAG(PROMISC_EN)) + en_ctrl |= LX_FLT_PROMISC; + + if (CHK_HW_FLAG(MULTIALL_EN)) + en_ctrl |= LX_FLT_MULTI_ALL; + + if (CHK_HW_FLAG(LOOPBACK_EN)) + en_ctrl |= LX_LOOPBACK; + + if (l1f_enable_mac(hw, true, en_ctrl)) { + alx_hw_err(hw, "error when start mac\n"); + retval = -EINVAL; + } + return retval; +} + + +/* + * 1. stop RXQ (reg15A0) and TXQ (reg1590) + * 2. stop MAC (reg1480) + */ +static int alf_stop_mac(struct alx_hw *hw) +{ + int retval = 0; + + if (l1f_enable_mac(hw, false, 0)) { + alx_hw_err(hw, "error when stop mac\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alf_config_mac(struct alx_hw *hw, u16 rxbuf_sz, u16 rx_qnum, + u16 rxring_sz, u16 tx_qnum, u16 txring_sz) +{ + u8 *addr; + u32 txmem_hi, txmem_lo[4]; + u32 rxmem_hi, rfdmem_lo, rrdmem_lo; + u16 smb_timer, mtu_with_eth, int_mod; + bool hash_legacy; + int i; + int retval = 0; + + addr = hw->mac_addr; + + txmem_hi = ALX_DMA_ADDR_HI(hw->tpdma[0]); + for (i = 0; i < tx_qnum; i++) + txmem_lo[i] = ALX_DMA_ADDR_LO(hw->tpdma[i]); + + + rxmem_hi = ALX_DMA_ADDR_HI(hw->rfdma[0]); + rfdmem_lo = ALX_DMA_ADDR_LO(hw->rfdma[0]); + rrdmem_lo = ALX_DMA_ADDR_LO(hw->rrdma[0]); + + smb_timer = (u16)hw->smb_timer; + mtu_with_eth = hw->mtu + ALX_ETH_LENGTH_OF_HEADER; + int_mod = hw->imt; + + hash_legacy = true; + + if (l1f_init_mac(hw, addr, txmem_hi, txmem_lo, tx_qnum, txring_sz, + rxmem_hi, rfdmem_lo, rrdmem_lo, rxring_sz, rxbuf_sz, + smb_timer, mtu_with_eth, int_mod, hash_legacy)) { + alx_hw_err(hw, "error when config mac\n"); + retval = -EINVAL; + } + + return retval; +} + + +/** + * alf_get_mac_addr + * @hw: pointer to hardware structure + **/ +static int alf_get_mac_addr(struct alx_hw *hw, u8 *addr) +{ + int retval = 0; + + if (l1f_get_perm_macaddr(hw, addr)) { + alx_hw_err(hw, "error when get permanent mac address\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alf_reset_pcie(struct alx_hw *hw, bool l0s_en, bool l1_en) +{ + int retval = 0; + + if (!CHK_HW_FLAG(L0S_CAP)) + l0s_en = false; + + if (l0s_en) + SET_HW_FLAG(L0S_EN); + else + CLI_HW_FLAG(L0S_EN); + + + if (!CHK_HW_FLAG(L1_CAP)) + l1_en = false; + + if (l1_en) + SET_HW_FLAG(L1_EN); + else + CLI_HW_FLAG(L1_EN); + + if (l1f_reset_pcie(hw, l0s_en, l1_en)) { + alx_hw_err(hw, "error when reset pcie\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alf_config_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en) +{ + int retval = 0; + + if (!CHK_HW_FLAG(L0S_CAP)) + l0s_en = false; + + if (l0s_en) + SET_HW_FLAG(L0S_EN); + else + CLI_HW_FLAG(L0S_EN); + + if (!CHK_HW_FLAG(L1_CAP)) + l1_en = false; + + if (l1_en) + SET_HW_FLAG(L1_EN); + else + CLI_HW_FLAG(L1_EN); + + if (l1f_enable_aspm(hw, l0s_en, l1_en, 0)) { + alx_hw_err(hw, "error when enable aspm\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alf_config_wol(struct alx_hw *hw, u32 wufc) +{ + u32 wol; + int retval = 0; + + wol = 0; + /* turn on magic packet event */ + if (wufc & ALX_WOL_MAGIC) { + wol |= L1F_WOL0_MAGIC_EN | L1F_WOL0_PME_MAGIC_EN; + /* magic packet maybe Broadcast&multicast&Unicast frame */ + /* mac |= MAC_CTRL_BC_EN; */ + } + + /* turn on link up event */ + if (wufc & ALX_WOL_PHY) { + wol |= L1F_WOL0_LINK_EN | L1F_WOL0_PME_LINK; + /* only link up can wake up */ + retval = alf_write_phy_reg(hw, L1F_MII_IER, L1F_IER_LINK_UP); + } + alx_mem_w32(hw, L1F_WOL0, wol); + return retval; +} + + +static int alf_config_mac_ctrl(struct alx_hw *hw) +{ + u32 mac; + + alx_mem_r32(hw, L1F_MAC_CTRL, &mac); + + /* enable/disable VLAN tag insert,strip */ + if (CHK_HW_FLAG(VLANSTRIP_EN)) + mac |= L1F_MAC_CTRL_VLANSTRIP; + else + mac &= ~L1F_MAC_CTRL_VLANSTRIP; + + if (CHK_HW_FLAG(PROMISC_EN)) + mac |= L1F_MAC_CTRL_PROMISC_EN; + else + mac &= ~L1F_MAC_CTRL_PROMISC_EN; + + if (CHK_HW_FLAG(MULTIALL_EN)) + mac |= L1F_MAC_CTRL_MULTIALL_EN; + else + mac &= ~L1F_MAC_CTRL_MULTIALL_EN; + + if (CHK_HW_FLAG(LOOPBACK_EN)) + mac |= L1F_MAC_CTRL_LPBACK_EN; + else + mac &= ~L1F_MAC_CTRL_LPBACK_EN; + + alx_mem_w32(hw, L1F_MAC_CTRL, mac); + return 0; +} + + +static int alf_config_pow_save(struct alx_hw *hw, u32 speed, bool wol_en, + bool tx_en, bool rx_en, bool pws_en) +{ + u8 wire_spd = LX_LC_10H; + int retval = 0; + + switch (speed) { + case ALX_LINK_SPEED_UNKNOWN: + case ALX_LINK_SPEED_10_HALF: + wire_spd = LX_LC_10H; + break; + case ALX_LINK_SPEED_10_FULL: + wire_spd = LX_LC_10F; + break; + case ALX_LINK_SPEED_100_HALF: + wire_spd = LX_LC_100H; + break; + case ALX_LINK_SPEED_100_FULL: + wire_spd = LX_LC_100F; + break; + case ALX_LINK_SPEED_1GB_FULL: + wire_spd = LX_LC_1000F; + break; + } + + if (l1f_powersaving(hw, wire_spd, wol_en, tx_en, rx_en, pws_en)) { + alx_hw_err(hw, "error when set power saving\n"); + retval = -EINVAL; + } + return retval; +} + + +/* RAR, Multicast, VLAN */ +static int alf_set_mac_addr(struct alx_hw *hw, u8 *addr) +{ + u32 sta; + + /* + * for example: 00-0B-6A-F6-00-DC + * 0<-->6AF600DC, 1<-->000B. + */ + + /* low dword */ + sta = (((u32)addr[2]) << 24) | (((u32)addr[3]) << 16) | + (((u32)addr[4]) << 8) | (((u32)addr[5])) ; + alx_mem_w32(hw, L1F_STAD0, sta); + + /* hight dword */ + sta = (((u32)addr[0]) << 8) | (((u32)addr[1])) ; + alx_mem_w32(hw, L1F_STAD1, sta); + return 0; +} + + +static int alf_set_mc_addr(struct alx_hw *hw, u8 *addr) +{ + u32 crc32, bit, reg, mta; + + /* + * set hash value for a multicast address hash calcu processing. + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + */ + crc32 = ALX_ETH_CRC(addr, ALX_ETH_LENGTH_OF_ADDRESS); + + /* + * The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + reg = (crc32 >> 31) & 0x1; + bit = (crc32 >> 26) & 0x1F; + + alx_mem_r32(hw, L1F_HASH_TBL0 + (reg<<2), &mta); + mta |= (0x1 << bit); + alx_mem_w32(hw, L1F_HASH_TBL0 + (reg<<2), mta); + return 0; +} + + +static int alf_clear_mc_addr(struct alx_hw *hw) +{ + alx_mem_w32(hw, L1F_HASH_TBL0, 0); + alx_mem_w32(hw, L1F_HASH_TBL1, 0); + return 0; +} + + +/* RTX, IRQ */ +static int alf_config_tx(struct alx_hw *hw) +{ + u32 wrr; + + alx_mem_r32(hw, L1F_WRR, &wrr); + switch (hw->wrr_mode) { + case alx_wrr_mode_none: + FIELD_SETL(wrr, L1F_WRR_PRI, L1F_WRR_PRI_RESTRICT_NONE); + break; + case alx_wrr_mode_high: + FIELD_SETL(wrr, L1F_WRR_PRI, L1F_WRR_PRI_RESTRICT_HI); + break; + case alx_wrr_mode_high2: + FIELD_SETL(wrr, L1F_WRR_PRI, L1F_WRR_PRI_RESTRICT_HI2); + break; + case alx_wrr_mode_all: + FIELD_SETL(wrr, L1F_WRR_PRI, L1F_WRR_PRI_RESTRICT_ALL); + break; + } + FIELD_SETL(wrr, L1F_WRR_PRI0, hw->wrr_prio0); + FIELD_SETL(wrr, L1F_WRR_PRI1, hw->wrr_prio1); + FIELD_SETL(wrr, L1F_WRR_PRI2, hw->wrr_prio2); + FIELD_SETL(wrr, L1F_WRR_PRI3, hw->wrr_prio3); + alx_mem_w32(hw, L1F_WRR, wrr); + return 0; +} + + +static int alf_config_msix(struct alx_hw *hw, u16 num_intrs, + bool msix_en, bool msi_en) +{ + u32 map[2]; + u32 type; + int msix_idx; + + if (!msix_en) + goto configure_legacy; + + memset(map, 0, sizeof(map)); + for (msix_idx = 0; msix_idx < num_intrs; msix_idx++) { + switch (msix_idx) { + case ALF_MSIX_INDEX_RXQ0: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_RXQ0, + ALF_MSIX_INDEX_RXQ0); + break; + case ALF_MSIX_INDEX_RXQ1: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_RXQ1, + ALF_MSIX_INDEX_RXQ1); + break; + case ALF_MSIX_INDEX_RXQ2: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_RXQ2, + ALF_MSIX_INDEX_RXQ2); + break; + case ALF_MSIX_INDEX_RXQ3: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_RXQ3, + ALF_MSIX_INDEX_RXQ3); + break; + case ALF_MSIX_INDEX_RXQ4: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_RXQ4, + ALF_MSIX_INDEX_RXQ4); + break; + case ALF_MSIX_INDEX_RXQ5: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_RXQ5, + ALF_MSIX_INDEX_RXQ5); + break; + case ALF_MSIX_INDEX_RXQ6: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_RXQ6, + ALF_MSIX_INDEX_RXQ6); + break; + case ALF_MSIX_INDEX_RXQ7: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_RXQ7, + ALF_MSIX_INDEX_RXQ7); + break; + case ALF_MSIX_INDEX_TXQ0: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_TXQ0, + ALF_MSIX_INDEX_TXQ0); + break; + case ALF_MSIX_INDEX_TXQ1: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_TXQ1, + ALF_MSIX_INDEX_TXQ1); + break; + case ALF_MSIX_INDEX_TXQ2: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_TXQ2, + ALF_MSIX_INDEX_TXQ2); + break; + case ALF_MSIX_INDEX_TXQ3: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_TXQ3, + ALF_MSIX_INDEX_TXQ3); + break; + case ALF_MSIX_INDEX_TIMER: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_TIMER, + ALF_MSIX_INDEX_TIMER); + break; + case ALF_MSIX_INDEX_ALERT: + FIELD_SETL(map[0], L1F_MSI_MAP_TBL1_ALERT, + ALF_MSIX_INDEX_ALERT); + break; + case ALF_MSIX_INDEX_SMB: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_SMB, + ALF_MSIX_INDEX_SMB); + break; + case ALF_MSIX_INDEX_PHY: + FIELD_SETL(map[1], L1F_MSI_MAP_TBL2_PHY, + ALF_MSIX_INDEX_PHY); + break; + default: + break; + + } + + } + + alx_mem_w32(hw, L1F_MSI_MAP_TBL1, map[0]); + alx_mem_w32(hw, L1F_MSI_MAP_TBL2, map[1]); + + /* 0 to alert, 1 to timer */ + type = (L1F_MSI_ID_MAP_DMAW | + L1F_MSI_ID_MAP_DMAR | + L1F_MSI_ID_MAP_PCIELNKDW | + L1F_MSI_ID_MAP_PCIECERR | + L1F_MSI_ID_MAP_PCIENFERR | + L1F_MSI_ID_MAP_PCIEFERR | + L1F_MSI_ID_MAP_PCIEUR); + + alx_mem_w32(hw, L1F_MSI_ID_MAP, type); + return 0; + +configure_legacy: + alx_mem_w32(hw, L1F_MSI_MAP_TBL1, 0x0); + alx_mem_w32(hw, L1F_MSI_MAP_TBL2, 0x0); + alx_mem_w32(hw, L1F_MSI_ID_MAP, 0x0); + if (msi_en) { + u32 msi; + alx_mem_r32(hw, 0x1920, &msi); + msi |= 0x10000; + alx_mem_w32(hw, 0x1920, msi); + } + return 0; +} + + +/* + * Interrupt + */ +static int alf_ack_phy_intr(struct alx_hw *hw) +{ + u16 isr; + return alf_read_phy_reg(hw, L1F_MII_ISR, &isr); +} + + +static int alf_enable_legacy_intr(struct alx_hw *hw) +{ + u16 cmd; + + alx_cfg_r16(hw, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_INTX_DISABLE; + alx_cfg_w16(hw, PCI_COMMAND, cmd); + + alx_mem_w32(hw, L1F_ISR, ~((u32) L1F_ISR_DIS)); + alx_mem_w32(hw, L1F_IMR, hw->intr_mask); + return 0; +} + + +static int alf_disable_legacy_intr(struct alx_hw *hw) +{ + alx_mem_w32(hw, L1F_ISR, L1F_ISR_DIS); + alx_mem_w32(hw, L1F_IMR, 0); + alx_mem_flush(hw); + return 0; +} + + +static int alf_enable_msix_intr(struct alx_hw *hw, u8 entry_idx) +{ + u32 ctrl_reg; + + ctrl_reg = ALF_MSIX_ENTRY_BASE + (entry_idx * ALF_MSIX_ENTRY_SIZE) + + ALF_MSIX_MSG_CTRL_OFF; + + alx_mem_w32(hw, ctrl_reg, 0x0); + alx_mem_flush(hw); + return 0; +} + + +static int alf_disable_msix_intr(struct alx_hw *hw, u8 entry_idx) +{ + u32 ctrl_reg; + + ctrl_reg = ALF_MSIX_ENTRY_BASE + (entry_idx * ALF_MSIX_ENTRY_SIZE) + + ALF_MSIX_MSG_CTRL_OFF; + + alx_mem_w32(hw, ctrl_reg, 0x1); + alx_mem_flush(hw); + return 0; +} + + +/* RSS */ +static int alf_config_rss(struct alx_hw *hw, bool rss_en) +{ + int key_len_by_u8 = sizeof(hw->rss_key); + int idt_len_by_u32 = sizeof(hw->rss_idt) / sizeof(u32); + u32 rxq0; + int i; + + /* Fill out hash function keys */ + for (i = 0; i < key_len_by_u8; i++) { + alx_mem_w8(hw, ALF_RSS_KEY(i, u8), + hw->rss_key[key_len_by_u8 - i - 1]); + } + + /* Fill out redirection table */ + for (i = 0; i < idt_len_by_u32; i++) + alx_mem_w32(hw, ALF_RSS_TBL(i, u32), hw->rss_idt[i]); + + alx_mem_w32(hw, L1F_RSS_BASE_CPU_NUM, hw->rss_base_cpu); + + alx_mem_r32(hw, L1F_RXQ0, &rxq0); + if (hw->rss_hstype & ALX_RSS_HSTYP_IPV4_EN) + rxq0 |= L1F_RXQ0_RSS_HSTYP_IPV4_EN; + else + rxq0 &= ~L1F_RXQ0_RSS_HSTYP_IPV4_EN; + + if (hw->rss_hstype & ALX_RSS_HSTYP_TCP4_EN) + rxq0 |= L1F_RXQ0_RSS_HSTYP_IPV4_TCP_EN; + else + rxq0 &= ~L1F_RXQ0_RSS_HSTYP_IPV4_TCP_EN; + + if (hw->rss_hstype & ALX_RSS_HSTYP_IPV6_EN) + rxq0 |= L1F_RXQ0_RSS_HSTYP_IPV6_EN; + else + rxq0 &= ~L1F_RXQ0_RSS_HSTYP_IPV6_EN; + + if (hw->rss_hstype & ALX_RSS_HSTYP_TCP6_EN) + rxq0 |= L1F_RXQ0_RSS_HSTYP_IPV6_TCP_EN; + else + rxq0 &= ~L1F_RXQ0_RSS_HSTYP_IPV6_TCP_EN; + + FIELD_SETL(rxq0, L1F_RXQ0_RSS_MODE, hw->rss_mode); + FIELD_SETL(rxq0, L1F_RXQ0_IDT_TBL_SIZE, hw->rss_idt_size); + + if (rss_en) + rxq0 |= L1F_RXQ0_RSS_HASH_EN; + else + rxq0 &= ~L1F_RXQ0_RSS_HASH_EN; + + alx_mem_w32(hw, L1F_RXQ0, rxq0); + return 0; +} + + +/* fc */ +static int alf_get_fc_mode(struct alx_hw *hw, enum alx_fc_mode *mode) +{ + u16 bmsr, giga; + int i; + int retval = 0; + + for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { + alf_read_phy_reg(hw, MII_BMSR, &bmsr); + alf_read_phy_reg(hw, MII_BMSR, &bmsr); + if (bmsr & BMSR_LSTATUS) { + /* Read phy Specific Status Register (17) */ + retval = alf_read_phy_reg(hw, L1F_MII_GIGA_PSSR, &giga); + if (retval) + return retval; + + if (!(giga & L1F_GIGA_PSSR_SPD_DPLX_RESOLVED)) { + alx_hw_err(hw, + "error for speed duplex resolved\n"); + return -EINVAL; + } + + if ((giga & L1F_GIGA_PSSR_FC_TXEN) && + (giga & L1F_GIGA_PSSR_FC_RXEN)) { + *mode = alx_fc_full; + } else if (giga & L1F_GIGA_PSSR_FC_TXEN) { + *mode = alx_fc_tx_pause; + } else if (giga & L1F_GIGA_PSSR_FC_RXEN) { + *mode = alx_fc_rx_pause; + } else { + *mode = alx_fc_none; + } + break; + } + mdelay(100); + } + + if (i == ALX_MAX_SETUP_LNK_CYCLE) { + alx_hw_err(hw, "error when get flow control mode\n"); + retval = -EINVAL; + } + return retval; +} + + +static int alf_config_fc(struct alx_hw *hw) +{ + u32 mac; + int retval = 0; + + if (hw->disable_fc_autoneg) { + hw->fc_was_autonegged = false; + hw->cur_fc_mode = hw->req_fc_mode; + } else { + hw->fc_was_autonegged = true; + retval = alf_get_fc_mode(hw, &hw->cur_fc_mode); + if (retval) + return retval; + } + + alx_mem_r32(hw, L1F_MAC_CTRL, &mac); + + switch (hw->cur_fc_mode) { + case alx_fc_none: /* 0 */ + mac &= ~(L1F_MAC_CTRL_RXFC_EN | L1F_MAC_CTRL_TXFC_EN); + break; + case alx_fc_rx_pause: /* 1 */ + mac &= ~L1F_MAC_CTRL_TXFC_EN; + mac |= L1F_MAC_CTRL_RXFC_EN; + break; + case alx_fc_tx_pause: /* 2 */ + mac |= L1F_MAC_CTRL_TXFC_EN; + mac &= ~L1F_MAC_CTRL_RXFC_EN; + break; + case alx_fc_full: /* 3 */ + case alx_fc_default: /* 4 */ + mac |= (L1F_MAC_CTRL_TXFC_EN | L1F_MAC_CTRL_RXFC_EN); + break; + default: + alx_hw_err(hw, "flow control param set incorrectly\n"); + return -EINVAL; + break; + } + + alx_mem_w32(hw, L1F_MAC_CTRL, mac); + + return retval; +} + + +/* + * NVRam + */ +static int alf_check_nvram(struct alx_hw *hw, bool *exist) +{ + *exist = false; + return 0; +} + + +/* ethtool */ +static int alf_get_ethtool_regs(struct alx_hw *hw, void *buff) +{ + int i; + u32 *val = buff; + static const u32 reg[] = { + /* 0 */ + L1F_DEV_CAP, L1F_DEV_CTRL, L1F_LNK_CAP, L1F_LNK_CTRL, + L1F_UE_SVRT, L1F_EFLD, L1F_SLD, L1F_PPHY_MISC1, + L1F_PPHY_MISC2, L1F_PDLL_TRNS1, + + /* 10 */ + L1F_TLEXTN_STATS, L1F_EFUSE_CTRL, L1F_EFUSE_DATA, L1F_SPI_OP1, + L1F_SPI_OP2, L1F_SPI_OP3, L1F_EF_CTRL, L1F_EF_ADDR, + L1F_EF_DATA, L1F_SPI_ID, + + /* 20 */ + L1F_SPI_CFG_START, L1F_PMCTRL, L1F_LTSSM_CTRL, L1F_MASTER, + L1F_MANU_TIMER, L1F_IRQ_MODU_TIMER, L1F_PHY_CTRL, L1F_MAC_STS, + L1F_MDIO, L1F_MDIO_EXTN, + + /* 30 */ + L1F_PHY_STS, L1F_BIST0, L1F_BIST1, L1F_SERDES, + L1F_LED_CTRL, L1F_LED_PATN, L1F_LED_PATN2, L1F_SYSALV, + L1F_PCIERR_INST, L1F_LPI_DECISN_TIMER, + + /* 40 */ + L1F_LPI_CTRL, L1F_LPI_WAIT, L1F_HRTBT_VLAN, L1F_HRTBT_CTRL, + L1F_RXPARSE, L1F_MAC_CTRL, L1F_GAP, L1F_STAD1, + L1F_LED_CTRL, L1F_HASH_TBL0, + + /* 50 */ + L1F_HASH_TBL1, L1F_HALFD, L1F_DMA, L1F_WOL0, + L1F_WOL1, L1F_WOL2, L1F_WRR, L1F_HQTPD, + L1F_CPUMAP1, L1F_CPUMAP2, + + /* 60 */ + L1F_MISC, L1F_RX_BASE_ADDR_HI, L1F_RFD_ADDR_LO, L1F_RFD_RING_SZ, + L1F_RFD_BUF_SZ, L1F_RRD_ADDR_LO, L1F_RRD_RING_SZ, + L1F_RFD_PIDX, L1F_RFD_CIDX, L1F_RXQ0, + + /* 70 */ + L1F_RXQ1, L1F_RXQ2, L1F_RXQ3, L1F_TX_BASE_ADDR_HI, + L1F_TPD_PRI0_ADDR_LO, L1F_TPD_PRI1_ADDR_LO, + L1F_TPD_PRI2_ADDR_LO, L1F_TPD_PRI3_ADDR_LO, + L1F_TPD_PRI0_PIDX, L1F_TPD_PRI1_PIDX, + + /* 80 */ + L1F_TPD_PRI2_PIDX, L1F_TPD_PRI3_PIDX, L1F_TPD_PRI0_CIDX, + L1F_TPD_PRI1_CIDX, L1F_TPD_PRI2_CIDX, L1F_TPD_PRI3_CIDX, + L1F_TPD_RING_SZ, L1F_TXQ0, L1F_TXQ1, L1F_TXQ2, + + /* 90 */ + L1F_MSI_MAP_TBL1, L1F_MSI_MAP_TBL2, L1F_MSI_ID_MAP, + L1F_MSIX_MASK, L1F_MSIX_PENDING, + }; + + for (i = 0; i < ARRAY_SIZE(reg); i++) + alx_mem_r32(hw, reg[i], &val[i]); + + /* SRAM */ + for (i = 0; i < 16; i++) + alx_mem_r32(hw, ALF_SRAM(i, u32), &val[100 + i]); + + /* RSS */ + for (i = 0; i < 10; i++) + alx_mem_r32(hw, ALF_RSS_KEY(i, u32), &val[120 + i]); + for (i = 0; i < 32; i++) + alx_mem_r32(hw, ALF_RSS_TBL(i, u32), &val[130 + i]); + alx_mem_r32(hw, L1F_RSS_HASH_VAL, &val[162]); + alx_mem_r32(hw, L1F_RSS_HASH_FLAG, &val[163]); + alx_mem_r32(hw, L1F_RSS_BASE_CPU_NUM, &val[164]); + + /* MIB */ + for (i = 0; i < 48; i++) + alx_mem_r32(hw, ALF_MIB(i, u32), &val[170 + i]); + return 0; +} + + +/******************************************************************************/ +static int alf_set_hw_capabilities(struct alx_hw *hw) +{ + SET_HW_FLAG(L0S_CAP); + SET_HW_FLAG(L1_CAP); + + if (hw->mac_type == alx_mac_l1f) + SET_HW_FLAG(GIGA_CAP); + + /* set flags of alx_phy_info */ + SET_HW_FLAG(PWSAVE_CAP); + return 0; +} + + +/* alc_set_hw_info */ +static int alf_set_hw_infos(struct alx_hw *hw) +{ + hw->rxstat_reg = L1F_MIB_RX_OK; + hw->rxstat_sz = 0x60; + hw->txstat_reg = L1F_MIB_TX_OK; + hw->txstat_sz = 0x68; + + hw->rx_prod_reg[0] = L1F_RFD_PIDX; + hw->rx_cons_reg[0] = L1F_RFD_CIDX; + + hw->tx_prod_reg[0] = L1F_TPD_PRI0_PIDX; + hw->tx_cons_reg[0] = L1F_TPD_PRI0_CIDX; + hw->tx_prod_reg[1] = L1F_TPD_PRI1_PIDX; + hw->tx_cons_reg[1] = L1F_TPD_PRI1_CIDX; + hw->tx_prod_reg[2] = L1F_TPD_PRI2_PIDX; + hw->tx_cons_reg[2] = L1F_TPD_PRI2_CIDX; + hw->tx_prod_reg[3] = L1F_TPD_PRI3_PIDX; + hw->tx_cons_reg[3] = L1F_TPD_PRI3_CIDX; + + hw->hwreg_sz = 0x200; + hw->eeprom_sz = 0; + + return 0; +} + + +/* + * alf_init_hw_callbacks + */ +int alf_init_hw_callbacks(struct alx_hw *hw) +{ + /* NIC */ + hw->cbs.identify_nic = &alf_identify_nic; + /* MAC */ + hw->cbs.reset_mac = &alf_reset_mac; + hw->cbs.start_mac = &alf_start_mac; + hw->cbs.stop_mac = &alf_stop_mac; + hw->cbs.config_mac = &alf_config_mac; + hw->cbs.get_mac_addr = &alf_get_mac_addr; + hw->cbs.set_mac_addr = &alf_set_mac_addr; + hw->cbs.set_mc_addr = &alf_set_mc_addr; + hw->cbs.clear_mc_addr = &alf_clear_mc_addr; + + /* PHY */ + hw->cbs.init_phy = &alf_init_phy; + hw->cbs.reset_phy = &alf_reset_phy; + hw->cbs.read_phy_reg = &alf_read_phy_reg; + hw->cbs.write_phy_reg = &alf_write_phy_reg; + hw->cbs.check_phy_link = &alf_check_phy_link; + hw->cbs.setup_phy_link = &alf_setup_phy_link; + hw->cbs.setup_phy_link_speed = &alf_setup_phy_link_speed; + + /* Interrupt */ + hw->cbs.ack_phy_intr = &alf_ack_phy_intr; + hw->cbs.enable_legacy_intr = &alf_enable_legacy_intr; + hw->cbs.disable_legacy_intr = &alf_disable_legacy_intr; + hw->cbs.enable_msix_intr = &alf_enable_msix_intr; + hw->cbs.disable_msix_intr = &alf_disable_msix_intr; + + /* Configure */ + hw->cbs.config_tx = &alf_config_tx; + hw->cbs.config_fc = &alf_config_fc; + hw->cbs.config_rss = &alf_config_rss; + hw->cbs.config_msix = &alf_config_msix; + hw->cbs.config_wol = &alf_config_wol; + hw->cbs.config_aspm = &alf_config_aspm; + hw->cbs.config_mac_ctrl = &alf_config_mac_ctrl; + hw->cbs.config_pow_save = &alf_config_pow_save; + hw->cbs.reset_pcie = &alf_reset_pcie; + + /* NVRam */ + hw->cbs.check_nvram = &alf_check_nvram; + + /* Others */ + hw->cbs.get_ethtool_regs = alf_get_ethtool_regs; + + alf_set_hw_capabilities(hw); + alf_set_hw_infos(hw); + + alx_hw_info(hw, "HW Flags = 0x%x\n", hw->flags); + return 0; +} + --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alf_hw.c @@ -0,0 +1,918 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "alf_hw.h" + + +/* get permanent mac address from + * 0: success + * non-0:fail + */ +u16 l1f_get_perm_macaddr(struct alx_hw *hw, u8 *addr) +{ + u32 val, mac0, mac1; + u16 flag, i; + +#define INTN_LOADED 0x1 +#define EXTN_LOADED 0x2 + + flag = 0; + val = 0; + +read_mcadr: + + /* get it from register first */ + alx_mem_r32(hw, L1F_STAD0, &mac0); + alx_mem_r32(hw, L1F_STAD1, &mac1); + + *(u32 *)(addr + 2) = LX_SWAP_DW(mac0); + *(u16 *)addr = (u16)LX_SWAP_W((u16)mac1); + + if (macaddr_valid(addr)) + return 0; + + if ((flag & INTN_LOADED) == 0) { + /* load from efuse ? */ + for (i = 0; i < L1F_SLD_MAX_TO; i++) { + alx_mem_r32(hw, L1F_SLD, &val); + if ((val & (L1F_SLD_STAT | L1F_SLD_START)) == 0) + break; + mdelay(1); + } + if (i == L1F_SLD_MAX_TO) + goto out; + alx_mem_w32(hw, L1F_SLD, val | L1F_SLD_START); + for (i = 0; i < L1F_SLD_MAX_TO; i++) { + mdelay(1); + alx_mem_r32(hw, L1F_SLD, &val); + if ((val & L1F_SLD_START) == 0) + break; + } + if (i == L1F_SLD_MAX_TO) + goto out; + flag |= INTN_LOADED; + goto read_mcadr; + } + + if ((flag & EXTN_LOADED) == 0) { + alx_mem_r32(hw, L1F_EFLD, &val); + if ((val & (L1F_EFLD_F_EXIST | L1F_EFLD_E_EXIST)) != 0) { + /* load from eeprom/flash ? */ + for (i = 0; i < L1F_SLD_MAX_TO; i++) { + alx_mem_r32(hw, L1F_EFLD, &val); + if ((val & (L1F_EFLD_STAT | + L1F_EFLD_START)) == 0) { + break; + } + mdelay(1); + } + if (i == L1F_SLD_MAX_TO) + goto out; + alx_mem_w32(hw, L1F_EFLD, val | L1F_EFLD_START); + for (i = 0; i < L1F_SLD_MAX_TO; i++) { + mdelay(1); + alx_mem_r32(hw, L1F_EFLD, &val); + if ((val & L1F_EFLD_START) == 0) + break; + } + if (i == L1F_SLD_MAX_TO) + goto out; + flag |= EXTN_LOADED; + goto read_mcadr; + } + } + +out: + return LX_ERR_ALOAD; +} + + +/* reset mac & dma + * return + * 0: success + * non-0:fail + */ +u16 l1f_reset_mac(struct alx_hw *hw) +{ + u32 val, pmctrl = 0; + u16 ret; + u16 i; + u8 rev = (u8)(FIELD_GETX(hw->pci_revid, L1F_PCI_REVID)); + + /* disable all interrupts, RXQ/TXQ */ + alx_mem_w32(hw, L1F_MSIX_MASK, BIT_ALL); /* ???? msi-x */ + alx_mem_w32(hw, L1F_IMR, 0); + alx_mem_w32(hw, L1F_ISR, L1F_ISR_DIS); + + ret = l1f_enable_mac(hw, false, 0); + if (ret != 0) + return ret; + + /* mac reset workaroud */ + alx_mem_w32(hw, L1F_RFD_PIDX, 1); + + /* dis l0s/l1 before mac reset */ + if ((rev == L1F_REV_A0 || rev == L1F_REV_A1) && + (hw->pci_revid & L1F_PCI_REVID_WTH_CR) != 0) { + alx_mem_r32(hw, L1F_PMCTRL, &pmctrl); + if ((pmctrl & (L1F_PMCTRL_L1_EN | L1F_PMCTRL_L0S_EN)) != 0) { + alx_mem_w32(hw, L1F_PMCTRL, + pmctrl & ~(L1F_PMCTRL_L1_EN | + L1F_PMCTRL_L0S_EN)); + } + } + + /* reset whole mac safely */ + alx_mem_r32(hw, L1F_MASTER, &val); + alx_mem_w32(hw, L1F_MASTER, + val | L1F_MASTER_DMA_MAC_RST | L1F_MASTER_OOB_DIS); + + /* make sure it's real idle */ + udelay(10); + for (i = 0; i < L1F_DMA_MAC_RST_TO; i++) { + alx_mem_r32(hw, L1F_RFD_PIDX, &val); + if (val == 0) + break; + udelay(10); + } + for (; i < L1F_DMA_MAC_RST_TO; i++) { + alx_mem_r32(hw, L1F_MASTER, &val); + if ((val & L1F_MASTER_DMA_MAC_RST) == 0) + break; + udelay(10); + } + if (i == L1F_DMA_MAC_RST_TO) + return LX_ERR_RSTMAC; + udelay(10); + + if ((rev == L1F_REV_A0 || rev == L1F_REV_A1) && + (hw->pci_revid & L1F_PCI_REVID_WTH_CR) != 0) { + /* set L1F_MASTER_PCLKSEL_SRDS (affect by soft-rst, PERST) */ + alx_mem_w32(hw, L1F_MASTER, val | L1F_MASTER_PCLKSEL_SRDS); + /* resoter l0s / l1 */ + if ((pmctrl & (L1F_PMCTRL_L1_EN | L1F_PMCTRL_L0S_EN)) != 0) + alx_mem_w32(hw, L1F_PMCTRL, pmctrl); + } + + /* clear Internal OSC settings, switching OSC by hw itself, + * disable isoloate for A0 */ + alx_mem_r32(hw, L1F_MISC3, &val); + alx_mem_w32(hw, L1F_MISC3, + (val & ~L1F_MISC3_25M_BY_SW) | L1F_MISC3_25M_NOTO_INTNL); + alx_mem_r32(hw, L1F_MISC, &val); + val &= ~L1F_MISC_INTNLOSC_OPEN; + if (rev == L1F_REV_A0 || rev == L1F_REV_A1) + val &= ~L1F_MISC_ISO_EN; + alx_mem_w32(hw, L1F_MISC, val); + udelay(20); + + /* driver control speed/duplex, hash-alg */ + alx_mem_r32(hw, L1F_MAC_CTRL, &val); + alx_mem_w32(hw, L1F_MAC_CTRL, val | L1F_MAC_CTRL_WOLSPED_SWEN); + + /* clk sw */ + alx_mem_r32(hw, L1F_SERDES, &val); + alx_mem_w32(hw, L1F_SERDES, + val | L1F_SERDES_MACCLK_SLWDWN | L1F_SERDES_PHYCLK_SLWDWN); + + return 0; +} + +/* reset phy + * return + * 0: success + * non-0:fail + */ +u16 l1f_reset_phy(struct alx_hw *hw, bool pws_en, bool az_en, bool ptp_en) +{ + u32 val; + u16 i, phy_val; + + az_en = az_en; + ptp_en = ptp_en; + + /* reset PHY core */ + alx_mem_r32(hw, L1F_PHY_CTRL, &val); + val &= ~(L1F_PHY_CTRL_DSPRST_OUT | L1F_PHY_CTRL_IDDQ | + L1F_PHY_CTRL_GATE_25M | L1F_PHY_CTRL_POWER_DOWN | + L1F_PHY_CTRL_CLS); + val |= L1F_PHY_CTRL_RST_ANALOG; + + if (pws_en) + val |= (L1F_PHY_CTRL_HIB_PULSE | L1F_PHY_CTRL_HIB_EN); + else + val &= ~(L1F_PHY_CTRL_HIB_PULSE | L1F_PHY_CTRL_HIB_EN); + alx_mem_w32(hw, L1F_PHY_CTRL, val); + udelay(10); /* 5us is enough */ + alx_mem_w32(hw, L1F_PHY_CTRL, val | L1F_PHY_CTRL_DSPRST_OUT); + + for (i = 0; i < L1F_PHY_CTRL_DSPRST_TO; i++) { /* delay 800us */ + udelay(10); + } + + /* ???? phy power saving */ + + l1f_write_phydbg(hw, true, + L1F_MIIDBG_TST10BTCFG, L1F_TST10BTCFG_DEF); + l1f_write_phydbg(hw, true, L1F_MIIDBG_SRDSYSMOD, L1F_SRDSYSMOD_DEF); + l1f_write_phydbg(hw, true, + L1F_MIIDBG_TST100BTCFG, L1F_TST100BTCFG_DEF); + l1f_write_phydbg(hw, true, L1F_MIIDBG_ANACTRL, L1F_ANACTRL_DEF); + l1f_read_phydbg(hw, true, L1F_MIIDBG_GREENCFG2, &phy_val); + l1f_write_phydbg(hw, true, L1F_MIIDBG_GREENCFG2, + phy_val & ~L1F_GREENCFG2_GATE_DFSE_EN); + /* rtl8139c, 120m */ + l1f_write_phy(hw, true, L1F_MIIEXT_ANEG, true, + L1F_MIIEXT_NLP78, L1F_MIIEXT_NLP78_120M_DEF); + + /* set phy interrupt mask */ + l1f_write_phy(hw, false, 0, true, + L1F_MII_IER, L1F_IER_LINK_UP | L1F_IER_LINK_DOWN); + + + /* TODO *****???? */ + return 0; +} + + +/* reset pcie + * just reset pcie relative registers (pci command, clk, aspm...) + * return + * 0:success + * non-0:fail + */ +u16 l1f_reset_pcie(struct alx_hw *hw, bool l0s_en, bool l1_en) +{ + u32 val; + u16 val16; + u16 ret; + u8 rev = (u8)(FIELD_GETX(hw->pci_revid, L1F_PCI_REVID)); + + /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ + alx_cfg_r16(hw, PCI_COMMAND, &val16); + if ((val16 & (PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)) == 0 || + (val16 & PCI_COMMAND_INTX_DISABLE) != 0) { + val16 = (u16)((val16 | (PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)) + & ~PCI_COMMAND_INTX_DISABLE); + alx_cfg_w16(hw, PCI_COMMAND, val16); + } + + /* Clear any PowerSaving Settings */ + alx_cfg_w16(hw, L1F_PM_CSR, 0); + + /* deflt val of PDLL D3PLLOFF */ + alx_mem_r32(hw, L1F_PDLL_TRNS1, &val); + alx_mem_w32(hw, L1F_PDLL_TRNS1, val & ~L1F_PDLL_TRNS1_D3PLLOFF_EN); + + /* mask some pcie error bits */ + alx_mem_r32(hw, L1F_UE_SVRT, &val); + val &= ~(L1F_UE_SVRT_DLPROTERR | L1F_UE_SVRT_FCPROTERR); + alx_mem_w32(hw, L1F_UE_SVRT, val); + + /* wol 25M & pclk */ + alx_mem_r32(hw, L1F_MASTER, &val); + if ((rev == L1F_REV_A0 || rev == L1F_REV_A1) && + (hw->pci_revid & L1F_PCI_REVID_WTH_CR) != 0) { + if ((val & L1F_MASTER_WAKEN_25M) == 0 || + (val & L1F_MASTER_PCLKSEL_SRDS) == 0) { + alx_mem_w32(hw, L1F_MASTER, + val | L1F_MASTER_PCLKSEL_SRDS | + L1F_MASTER_WAKEN_25M); + } + } else { + if ((val & L1F_MASTER_WAKEN_25M) == 0 || + (val & L1F_MASTER_PCLKSEL_SRDS) != 0) { + alx_mem_w32(hw, L1F_MASTER, + (val & ~L1F_MASTER_PCLKSEL_SRDS) | + L1F_MASTER_WAKEN_25M); + } + } + + /* l0s, l1 setting */ + ret = l1f_enable_aspm(hw, l0s_en, l1_en, 0); + + udelay(10); + + return ret; +} + + +/* disable/enable MAC/RXQ/TXQ + * en + * true:enable + * false:disable + * return + * 0:success + * non-0-fail + */ +u16 l1f_enable_mac(struct alx_hw *hw, bool en, u16 en_ctrl) +{ + u32 rxq, txq, mac, val; + u16 i; + + alx_mem_r32(hw, L1F_RXQ0, &rxq); + alx_mem_r32(hw, L1F_TXQ0, &txq); + alx_mem_r32(hw, L1F_MAC_CTRL, &mac); + + if (en) { /* enable */ + alx_mem_w32(hw, L1F_RXQ0, rxq | L1F_RXQ0_EN); + alx_mem_w32(hw, L1F_TXQ0, txq | L1F_TXQ0_EN); + if ((en_ctrl & LX_MACSPEED_1000) != 0) { + FIELD_SETL(mac, L1F_MAC_CTRL_SPEED, + L1F_MAC_CTRL_SPEED_1000); + } else { + FIELD_SETL(mac, L1F_MAC_CTRL_SPEED, + L1F_MAC_CTRL_SPEED_10_100); + } + + test_set_or_clear(mac, en_ctrl, LX_MACDUPLEX_FULL, + L1F_MAC_CTRL_FULLD); + /* rx filter */ + test_set_or_clear(mac, en_ctrl, LX_FLT_PROMISC, + L1F_MAC_CTRL_PROMISC_EN); + test_set_or_clear(mac, en_ctrl, LX_FLT_MULTI_ALL, + L1F_MAC_CTRL_MULTIALL_EN); + test_set_or_clear(mac, en_ctrl, LX_FLT_BROADCAST, + L1F_MAC_CTRL_BRD_EN); + test_set_or_clear(mac, en_ctrl, LX_FLT_DIRECT, + L1F_MAC_CTRL_RX_EN); + test_set_or_clear(mac, en_ctrl, LX_FC_TXEN, + L1F_MAC_CTRL_TXFC_EN); + test_set_or_clear(mac, en_ctrl, LX_FC_RXEN, + L1F_MAC_CTRL_RXFC_EN); + test_set_or_clear(mac, en_ctrl, LX_VLAN_STRIP, + L1F_MAC_CTRL_VLANSTRIP); + test_set_or_clear(mac, en_ctrl, LX_LOOPBACK, + L1F_MAC_CTRL_LPBACK_EN); + test_set_or_clear(mac, en_ctrl, LX_SINGLE_PAUSE, + L1F_MAC_CTRL_SPAUSE_EN); + test_set_or_clear(mac, en_ctrl, LX_ADD_FCS, + (L1F_MAC_CTRL_PCRCE | L1F_MAC_CTRL_CRCE)); + + alx_mem_w32(hw, L1F_MAC_CTRL, mac | L1F_MAC_CTRL_TX_EN); + } else { /* disable mac */ + alx_mem_w32(hw, L1F_RXQ0, rxq & ~L1F_RXQ0_EN); + alx_mem_w32(hw, L1F_TXQ0, txq & ~L1F_TXQ0_EN); + + /* waiting for rxq/txq be idle */ + udelay(40); + + /* stop mac tx/rx */ + alx_mem_w32(hw, L1F_MAC_CTRL, + mac & ~(L1F_MAC_CTRL_RX_EN | L1F_MAC_CTRL_TX_EN)); + + for (i = 0; i < L1F_DMA_MAC_RST_TO; i++) { + alx_mem_r32(hw, L1F_MAC_STS, &val); + if ((val & L1F_MAC_STS_IDLE) == 0) + break; + udelay(10); + } + if (L1F_DMA_MAC_RST_TO == i) + return LX_ERR_RSTMAC; + } + + return 0; +} + +/* enable/disable aspm support + * that will change settings for phy/mac/pcie + */ +u16 l1f_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en, u8 lnk_stat) +{ + u32 pmctrl; + u8 rev = (u8)(FIELD_GETX(hw->pci_revid, L1F_PCI_REVID)); + + lnk_stat = lnk_stat; + + + alx_mem_r32(hw, L1F_PMCTRL, &pmctrl); + + /* ????default */ + FIELD_SETL(pmctrl, L1F_PMCTRL_LCKDET_TIMER, + L1F_PMCTRL_LCKDET_TIMER_DEF); + pmctrl |= L1F_PMCTRL_RCVR_WT_1US | /* wait 1us */ + L1F_PMCTRL_L1_CLKSW_EN | /* pcie clk sw */ + L1F_PMCTRL_L1_SRDSRX_PWD ; /* pwd serdes ????default */ + /* ????default */ + FIELD_SETL(pmctrl, L1F_PMCTRL_L1REQ_TO, L1F_PMCTRL_L1REG_TO_DEF); + FIELD_SETL(pmctrl, L1F_PMCTRL_L1_TIMER, L1F_PMCTRL_L1_TIMER_16US); + pmctrl &= ~(L1F_PMCTRL_L1_SRDS_EN | + L1F_PMCTRL_L1_SRDSPLL_EN | + L1F_PMCTRL_L1_BUFSRX_EN | + L1F_PMCTRL_SADLY_EN | /* ???default */ + L1F_PMCTRL_HOTRST_WTEN| + L1F_PMCTRL_L0S_EN | + L1F_PMCTRL_L1_EN | + L1F_PMCTRL_ASPM_FCEN | + L1F_PMCTRL_TXL1_AFTER_L0S | + L1F_PMCTRL_RXL1_AFTER_L0S + ); + if ((rev == L1F_REV_A0 || rev == L1F_REV_A1) && + (hw->pci_revid & L1F_PCI_REVID_WTH_CR) != 0) { + pmctrl |= L1F_PMCTRL_L1_SRDS_EN | L1F_PMCTRL_L1_SRDSPLL_EN; + } + + /* on/off l0s only if bios/system enable l0s */ + if (/* sysl0s_en && */ l0s_en) + pmctrl |= (L1F_PMCTRL_L0S_EN | L1F_PMCTRL_ASPM_FCEN); + /* on/off l1 only if bios/system enable l1 */ + if (/* sysl1_en && */ l1_en) + pmctrl |= (L1F_PMCTRL_L1_EN | L1F_PMCTRL_ASPM_FCEN); + + alx_mem_w32(hw, L1F_PMCTRL, pmctrl); + + return 0; +} + + +/* initialize phy for speed / flow control + * lnk_cap + * if autoNeg, is link capability to tell the peer + * if force mode, is forced speed/duplex + */ +u16 l1f_init_phy_spdfc(struct alx_hw *hw, bool auto_neg, + u8 lnk_cap, bool fc_en) +{ + u16 adv, giga, cr; + u32 val; + u16 ret; + + /* clear flag */ + l1f_write_phy(hw, false, 0, false, L1F_MII_DBG_ADDR, 0); + alx_mem_r32(hw, L1F_DRV, &val); + FIELD_SETL(val, LX_DRV_PHY, 0); + + if (auto_neg) { + adv = L1F_ADVERTISE_DEFAULT_CAP & ~L1F_ADVERTISE_SPEED_MASK; + giga = L1F_GIGA_CR_1000T_DEFAULT_CAP & + ~L1F_GIGA_CR_1000T_SPEED_MASK; + val |= LX_DRV_PHY_AUTO; + if (!fc_en) + adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + else + val |= LX_DRV_PHY_FC; + if ((LX_LC_10H & lnk_cap) != 0) { + adv |= ADVERTISE_10HALF; + val |= LX_DRV_PHY_10; + } + if ((LX_LC_10F & lnk_cap) != 0) { + adv |= ADVERTISE_10HALF | + ADVERTISE_10FULL; + val |= LX_DRV_PHY_10 | LX_DRV_PHY_DUPLEX; + } + if ((LX_LC_100H & lnk_cap) != 0) { + adv |= ADVERTISE_100HALF; + val |= LX_DRV_PHY_100; + } + if ((LX_LC_100F & lnk_cap) != 0) { + adv |= ADVERTISE_100HALF | + ADVERTISE_100FULL; + val |= LX_DRV_PHY_100 | LX_DRV_PHY_DUPLEX; + } + if ((LX_LC_1000F & lnk_cap) != 0) { + giga |= L1F_GIGA_CR_1000T_FD_CAPS; + val |= LX_DRV_PHY_1000 | LX_DRV_PHY_DUPLEX; + } + + ret = l1f_write_phy(hw, false, 0, false, MII_ADVERTISE, adv); + ret = l1f_write_phy(hw, false, 0, false, MII_CTRL1000, giga); + + cr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART; + ret = l1f_write_phy(hw, false, 0, false, MII_BMCR, cr); + } else { /* force mode */ + cr = BMCR_RESET; + switch (lnk_cap) { + case LX_LC_10H: + val |= LX_DRV_PHY_10; + break; + case LX_LC_10F: + cr |= BMCR_FULLDPLX; + val |= LX_DRV_PHY_10 | LX_DRV_PHY_DUPLEX; + break; + case LX_LC_100H: + cr |= BMCR_SPEED100; + val |= LX_DRV_PHY_100; + break; + case LX_LC_100F: + cr |= BMCR_SPEED100 | BMCR_FULLDPLX; + val |= LX_DRV_PHY_100 | LX_DRV_PHY_DUPLEX; + break; + default: + return LX_ERR_PARM; + } + ret = l1f_write_phy(hw, false, 0, false, MII_BMCR, cr); + } + + if (!ret) { + l1f_write_phy(hw, false, 0, false, + L1F_MII_DBG_ADDR, LX_PHY_INITED); + } + alx_mem_w32(hw, L1F_DRV, val); + + return ret; +} + + +/* do power saving setting befor enter suspend mode + * NOTE: + * 1. phy link must be established before calling this function + * 2. wol option (pattern,magic,link,etc.) is configed before call it. + */ +u16 l1f_powersaving(struct alx_hw *hw, + u8 wire_spd, + bool wol_en, + bool mactx_en, + bool macrx_en, + bool pws_en) +{ + u32 master_ctrl, mac_ctrl, phy_ctrl, val; + u16 pm_ctrl, ret = 0; + + master_ctrl = 0; + mac_ctrl = 0; + phy_ctrl = 0; + + pws_en = pws_en; + + alx_mem_r32(hw, L1F_MASTER, &master_ctrl); + master_ctrl &= ~L1F_MASTER_PCLKSEL_SRDS; + + alx_mem_r32(hw, L1F_MAC_CTRL, &mac_ctrl); + /* 10/100 half */ + FIELD_SETL(mac_ctrl, L1F_MAC_CTRL_SPEED, L1F_MAC_CTRL_SPEED_10_100); + mac_ctrl &= ~(L1F_MAC_CTRL_FULLD | + L1F_MAC_CTRL_RX_EN | + L1F_MAC_CTRL_TX_EN); + + alx_mem_r32(hw, L1F_PHY_CTRL, &phy_ctrl); + phy_ctrl &= ~(L1F_PHY_CTRL_DSPRST_OUT | L1F_PHY_CTRL_CLS); + /* if (pws_en) { */ + phy_ctrl |= (L1F_PHY_CTRL_RST_ANALOG | L1F_PHY_CTRL_HIB_PULSE | + L1F_PHY_CTRL_HIB_EN); + + if (wol_en) { /* enable rx packet or tx packet */ + if (macrx_en) + mac_ctrl |= (L1F_MAC_CTRL_RX_EN | L1F_MAC_CTRL_BRD_EN); + if (mactx_en) + mac_ctrl |= L1F_MAC_CTRL_TX_EN; + if (LX_LC_1000F == wire_spd) { + FIELD_SETL(mac_ctrl, L1F_MAC_CTRL_SPEED, + L1F_MAC_CTRL_SPEED_1000); + } + if (LX_LC_10F == wire_spd || + LX_LC_100F == wire_spd || + LX_LC_100F == wire_spd) { + mac_ctrl |= L1F_MAC_CTRL_FULLD; + } + phy_ctrl |= L1F_PHY_CTRL_DSPRST_OUT; + ret = l1f_write_phy(hw, false, 0, false, L1F_MII_IER, + L1F_IER_LINK_UP); + } else { + ret = l1f_write_phy(hw, false, 0, false, L1F_MII_IER, 0); + phy_ctrl |= (L1F_PHY_CTRL_IDDQ | L1F_PHY_CTRL_POWER_DOWN); + } + alx_mem_w32(hw, L1F_MASTER, master_ctrl); + alx_mem_w32(hw, L1F_MAC_CTRL, mac_ctrl); + alx_mem_w32(hw, L1F_PHY_CTRL, phy_ctrl); + + /* set val of PDLL D3PLLOFF */ + alx_mem_r32(hw, L1F_PDLL_TRNS1, &val); + alx_mem_w32(hw, L1F_PDLL_TRNS1, val | L1F_PDLL_TRNS1_D3PLLOFF_EN); + + /* set PME_EN */ + if (wol_en) { + alx_cfg_r16(hw, L1F_PM_CSR, &pm_ctrl); + pm_ctrl |= L1F_PM_CSR_PME_EN; + alx_cfg_w16(hw, L1F_PM_CSR, pm_ctrl); + } + + return ret; +} + + +/* read phy register */ +u16 l1f_read_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, + u16 reg, u16 *data) +{ + u32 val; + u16 clk_sel, i, ret = 0; + + *data = 0; + clk_sel = fast ? + (u16)L1F_MDIO_CLK_SEL_25MD4 : (u16)L1F_MDIO_CLK_SEL_25MD128; + + if (ext) { + val = FIELDL(L1F_MDIO_EXTN_DEVAD, dev) | + FIELDL(L1F_MDIO_EXTN_REG, reg); + alx_mem_w32(hw, L1F_MDIO_EXTN, val); + + val = L1F_MDIO_SPRES_PRMBL | + FIELDL(L1F_MDIO_CLK_SEL, clk_sel) | + L1F_MDIO_START | + L1F_MDIO_MODE_EXT | + L1F_MDIO_OP_READ; + } else { + val = L1F_MDIO_SPRES_PRMBL | + FIELDL(L1F_MDIO_CLK_SEL, clk_sel) | + FIELDL(L1F_MDIO_REG, reg) | + L1F_MDIO_START | + L1F_MDIO_OP_READ; + } + + alx_mem_w32(hw, L1F_MDIO, val); + + for (i = 0; i < L1F_MDIO_MAX_AC_TO; i++) { + alx_mem_r32(hw, L1F_MDIO, &val); + if ((val & L1F_MDIO_BUSY) == 0) { + *data = (u16)FIELD_GETX(val, L1F_MDIO_DATA); + break; + } + udelay(10); + } + + if (L1F_MDIO_MAX_AC_TO == i) + ret = LX_ERR_MIIBUSY; + + return ret; +} + +/* write phy register */ +u16 l1f_write_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, + u16 reg, u16 data) +{ + u32 val; + u16 clk_sel, i, ret = 0; + + clk_sel = fast ? + (u16)L1F_MDIO_CLK_SEL_25MD4 : (u16)L1F_MDIO_CLK_SEL_25MD128; + + if (ext) { + val = FIELDL(L1F_MDIO_EXTN_DEVAD, dev) | + FIELDL(L1F_MDIO_EXTN_REG, reg); + alx_mem_w32(hw, L1F_MDIO_EXTN, val); + + val = L1F_MDIO_SPRES_PRMBL | + FIELDL(L1F_MDIO_CLK_SEL, clk_sel) | + FIELDL(L1F_MDIO_DATA, data) | + L1F_MDIO_START | + L1F_MDIO_MODE_EXT; + } else { + val = L1F_MDIO_SPRES_PRMBL | + FIELDL(L1F_MDIO_CLK_SEL, clk_sel) | + FIELDL(L1F_MDIO_REG, reg) | + FIELDL(L1F_MDIO_DATA, data) | + L1F_MDIO_START; + } + + alx_mem_w32(hw, L1F_MDIO, val); + + for (i = 0; i < L1F_MDIO_MAX_AC_TO; i++) { + alx_mem_r32(hw, L1F_MDIO, &val); + if ((val & L1F_MDIO_BUSY) == 0) + break; + udelay(10); + } + + if (L1F_MDIO_MAX_AC_TO == i) + ret = LX_ERR_MIIBUSY; + + return ret; +} + +u16 l1f_read_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 *data) +{ + u16 ret; + + ret = l1f_write_phy(hw, false, 0, fast, L1F_MII_DBG_ADDR, reg); + ret = l1f_read_phy(hw, false, 0, fast, L1F_MII_DBG_DATA, data); + + return ret; +} + +u16 l1f_write_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 data) +{ + u16 ret; + + ret = l1f_write_phy(hw, false, 0, fast, L1F_MII_DBG_ADDR, reg); + ret = l1f_write_phy(hw, false, 0, fast, L1F_MII_DBG_DATA, data); + + return ret; +} + +/* + * initialize mac basically + * most of hi-feature no init + * MAC/PHY should be reset before call this function + * smb_timer : million-second + * int_mod : micro-second + * disable RSS as default + */ +u16 l1f_init_mac(struct alx_hw *hw, u8 *addr, u32 txmem_hi, + u32 *tx_mem_lo, u8 tx_qnum, u16 txring_sz, + u32 rxmem_hi, u32 rfdmem_lo, u32 rrdmem_lo, + u16 rxring_sz, u16 rxbuf_sz, u16 smb_timer, + u16 mtu, u16 int_mod, bool hash_legacy) +{ + u32 val; + u16 val16, devid; + u8 dmar_len; + + alx_cfg_r16(hw, PCI_DEVICE_ID, &devid); + + /* set mac-address */ + val = *(u32 *)(addr + 2); + alx_mem_w32(hw, L1F_STAD0, LX_SWAP_DW(val)); + val = *(u16 *)addr ; + alx_mem_w32(hw, L1F_STAD1, LX_SWAP_W((u16)val)); + + /* clear multicast hash table, algrithm */ + alx_mem_w32(hw, L1F_HASH_TBL0, 0); + alx_mem_w32(hw, L1F_HASH_TBL1, 0); + alx_mem_r32(hw, L1F_MAC_CTRL, &val); + if (hash_legacy) + val |= L1F_MAC_CTRL_MHASH_ALG_HI5B; + else + val &= ~L1F_MAC_CTRL_MHASH_ALG_HI5B; + alx_mem_w32(hw, L1F_MAC_CTRL, val); + + /* clear any wol setting/status */ + alx_mem_r32(hw, L1F_WOL0, &val); + alx_mem_w32(hw, L1F_WOL0, 0); + + /* clk gating */ + alx_mem_w32(hw, L1F_CLK_GATE, + (FIELD_GETX(hw->pci_revid, L1F_PCI_REVID) == L1F_REV_B0) ? + L1F_CLK_GATE_ALL_B0 : L1F_CLK_GATE_ALL_A0); + + /* idle timeout to switch clk_125M */ + if (FIELD_GETX(hw->pci_revid, L1F_PCI_REVID) == L1F_REV_B0) { + alx_mem_w32(hw, L1F_IDLE_DECISN_TIMER, + L1F_IDLE_DECISN_TIMER_DEF); + } + + /* descriptor ring base memory */ + alx_mem_w32(hw, L1F_TX_BASE_ADDR_HI, txmem_hi); + alx_mem_w32(hw, L1F_TPD_RING_SZ, txring_sz); + switch (tx_qnum) { + case 4: + alx_mem_w32(hw, L1F_TPD_PRI3_ADDR_LO, tx_mem_lo[3]); + /* fall through */ + case 3: + alx_mem_w32(hw, L1F_TPD_PRI2_ADDR_LO, tx_mem_lo[2]); + /* fall through */ + case 2: + alx_mem_w32(hw, L1F_TPD_PRI1_ADDR_LO, tx_mem_lo[1]); + /* fall through */ + case 1: + alx_mem_w32(hw, L1F_TPD_PRI0_ADDR_LO, tx_mem_lo[0]); + break; + default: + return LX_ERR_PARM; + } + alx_mem_w32(hw, L1F_RX_BASE_ADDR_HI, rxmem_hi); + alx_mem_w32(hw, L1F_RFD_ADDR_LO, rfdmem_lo); + alx_mem_w32(hw, L1F_RRD_ADDR_LO, rrdmem_lo); + alx_mem_w32(hw, L1F_RFD_BUF_SZ, rxbuf_sz); + alx_mem_w32(hw, L1F_RRD_RING_SZ, rxring_sz); + alx_mem_w32(hw, L1F_RFD_RING_SZ, rxring_sz); + alx_mem_w32(hw, L1F_SMB_TIMER, smb_timer * 500UL); + alx_mem_w32(hw, L1F_SRAM9, L1F_SRAM_LOAD_PTR); + + /* int moduration */ + alx_mem_r32(hw, L1F_MASTER, &val); +/* val = (val & ~L1F_MASTER_IRQMOD2_EN) | */ + val = val | L1F_MASTER_IRQMOD2_EN | + L1F_MASTER_IRQMOD1_EN | + L1F_MASTER_SYSALVTIMER_EN; /* sysalive */ + alx_mem_w32(hw, L1F_MASTER, val); + alx_mem_w32(hw, L1F_IRQ_MODU_TIMER, + FIELDL(L1F_IRQ_MODU_TIMER1, int_mod >> 1)); + + /* tpd threshold to trig int */ + alx_mem_w32(hw, L1F_TINT_TPD_THRSHLD, (u32)txring_sz / 3); + alx_mem_w32(hw, L1F_TINT_TIMER, int_mod); + /* re-send int */ + alx_mem_w32(hw, L1F_INT_RETRIG, L1F_INT_RETRIG_TO); + + /* mtu */ + alx_mem_w32(hw, L1F_MTU, (u32)(mtu + 4 + 4)); /* crc + vlan */ + if (mtu > L1F_MTU_JUMBO_TH) { + alx_mem_r32(hw, L1F_MAC_CTRL, &val); + alx_mem_w32(hw, L1F_MAC_CTRL, val & ~L1F_MAC_CTRL_FAST_PAUSE); + } + + /* txq */ + if ((mtu + 8) < L1F_TXQ1_JUMBO_TSO_TH) + val = (u32)(mtu + 8 + 7) >> 3; /* 7 for QWORD align */ + else + val = L1F_TXQ1_JUMBO_TSO_TH >> 3; + alx_mem_w32(hw, L1F_TXQ1, val | L1F_TXQ1_ERRLGPKT_DROP_EN); + alx_mem_r32(hw, L1F_DEV_CTRL, &val); + dmar_len = (u8)FIELD_GETX(val, L1F_DEV_CTRL_MAXRRS); + /* if BIOS had changed the default dma read max length, + * restore it to default value */ + if (dmar_len < L1F_DEV_CTRL_MAXRRS_MIN) { + FIELD_SETL(val, L1F_DEV_CTRL_MAXRRS, L1F_DEV_CTRL_MAXRRS_MIN); + alx_mem_w32(hw, L1F_DEV_CTRL, val); + } + val = FIELDL(L1F_TXQ0_TPD_BURSTPREF, L1F_TXQ_TPD_BURSTPREF_DEF) | + L1F_TXQ0_MODE_ENHANCE | + L1F_TXQ0_LSO_8023_EN | + L1F_TXQ0_SUPT_IPOPT | + FIELDL(L1F_TXQ0_TXF_BURST_PREF, L1F_TXQ_TXF_BURST_PREF_DEF); + alx_mem_w32(hw, L1F_TXQ0, val); + val = FIELDL(L1F_HQTPD_Q1_NUMPREF, L1F_TXQ_TPD_BURSTPREF_DEF) | + FIELDL(L1F_HQTPD_Q2_NUMPREF, L1F_TXQ_TPD_BURSTPREF_DEF) | + FIELDL(L1F_HQTPD_Q3_NUMPREF, L1F_TXQ_TPD_BURSTPREF_DEF) | + L1F_HQTPD_BURST_EN; + alx_mem_w32(hw, L1F_HQTPD, val); + + /* rxq */ + alx_mem_r32(hw, L1F_SRAM5, &val); + val = FIELD_GETX(val, L1F_SRAM_RXF_LEN) << 3; /* bytes */ + if (val > L1F_SRAM_RXF_LEN_8K) { + val16 = L1F_MTU_STD_ALGN >> 3; + val = (val - (2 * L1F_MTU_STD_ALGN + L1F_MTU_MIN)) >> 3; + } else { + val16 = L1F_MTU_STD_ALGN >> 3; + val = (val - L1F_MTU_STD_ALGN) >> 3; + } + alx_mem_w32(hw, L1F_RXQ2, + FIELDL(L1F_RXQ2_RXF_XOFF_THRESH, val16) | + FIELDL(L1F_RXQ2_RXF_XON_THRESH, val)); + val = FIELDL(L1F_RXQ0_NUM_RFD_PREF, L1F_RXQ0_NUM_RFD_PREF_DEF) | + FIELDL(L1F_RXQ0_RSS_MODE, L1F_RXQ0_RSS_MODE_DIS) | + FIELDL(L1F_RXQ0_IDT_TBL_SIZE, L1F_RXQ0_IDT_TBL_SIZE_DEF) | + L1F_RXQ0_RSS_HSTYP_ALL | + L1F_RXQ0_RSS_HASH_EN | + L1F_RXQ0_IPV6_PARSE_EN; + if (mtu > L1F_MTU_JUMBO_TH) + val |= L1F_RXQ0_CUT_THRU_EN; + if ((devid & 1) != 0) { + FIELD_SETL(val, L1F_RXQ0_ASPM_THRESH, + L1F_RXQ0_ASPM_THRESH_100M); + } + alx_mem_w32(hw, L1F_RXQ0, val); + + /* rfd producer index */ + alx_mem_w32(hw, L1F_RFD_PIDX, (u32)rxring_sz - 1); + + /* DMA */ + alx_mem_r32(hw, L1F_DMA, &val); + val = FIELDL(L1F_DMA_RORDER_MODE, L1F_DMA_RORDER_MODE_OUT) | + L1F_DMA_RREQ_PRI_DATA | + FIELDL(L1F_DMA_RREQ_BLEN, dmar_len) | + FIELDL(L1F_DMA_WDLY_CNT, L1F_DMA_WDLY_CNT_DEF) | + FIELDL(L1F_DMA_RDLY_CNT, L1F_DMA_RDLY_CNT_DEF) | + FIELDL(L1F_DMA_RCHNL_SEL, hw->dma_chnl - 1); + alx_mem_w32(hw, L1F_DMA, val); + + return 0; +} + + +u16 l1f_get_phy_config(struct alx_hw *hw) +{ + u32 val; + u16 phy_val; + + alx_mem_r32(hw, L1F_PHY_CTRL, &val); + + /* phy in rst */ + if ((val & L1F_PHY_CTRL_DSPRST_OUT) == 0) + return LX_DRV_PHY_UNKNOWN; + + alx_mem_r32(hw, L1F_DRV, &val); + val = FIELD_GETX(val, LX_DRV_PHY); + + if (LX_DRV_PHY_UNKNOWN == val) + return LX_DRV_PHY_UNKNOWN; + + l1f_read_phy(hw, false, 0, false, L1F_MII_DBG_ADDR, &phy_val); + + if (LX_PHY_INITED == phy_val) + return (u16) val; + + return LX_DRV_PHY_UNKNOWN; +} + --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alf_hw.h @@ -0,0 +1,2098 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef L1F_HW_H_ +#define L1F_HW_H_ + +/********************************************************************* + * some reqs for l1f_sw.h + * + * 1. some basic type must be defined if there are not defined by + * your compiler: + * u8, u16, u32, bool + * + * 2. PETHCONTEXT difinition should be in l1x_sw.h and it must contain + * pci_devid & pci_venid + * + *********************************************************************/ + +#include "alx_hwcom.h" + +/******************************************************************************/ +#define L1F_DEV_ID 0x1091 +#define L2F_DEV_ID 0x1090 + + +#define L1F_PCI_REVID_WTH_CR BIT(1) +#define L1F_PCI_REVID_WTH_XD BIT(0) +#define L1F_PCI_REVID_MASK ASHFT3(0x1FU) +#define L1F_PCI_REVID_SHIFT 3 +#define L1F_REV_A0 0 +#define L1F_REV_A1 1 +#define L1F_REV_B0 2 + +#define L1F_PM_CSR 0x0044 /* 16bit */ +#define L1F_PM_CSR_PME_STAT BIT(15) +#define L1F_PM_CSR_DSCAL_MASK ASHFT13(3U) +#define L1F_PM_CSR_DSCAL_SHIFT 13 +#define L1F_PM_CSR_DSEL_MASK ASHFT9(0xFU) +#define L1F_PM_CSR_DSEL_SHIFT 9 +#define L1F_PM_CSR_PME_EN BIT(8) +#define L1F_PM_CSR_PWST_MASK ASHFT0(3U) +#define L1F_PM_CSR_PWST_SHIFT 0 + +#define L1F_PM_DATA 0x0047 /* 8bit */ + + +#define L1F_DEV_CAP 0x005C +#define L1F_DEV_CAP_SPLSL_MASK ASHFT26(3UL) +#define L1F_DEV_CAP_SPLSL_SHIFT 26 +#define L1F_DEV_CAP_SPLV_MASK ASHFT18(0xFFUL) +#define L1F_DEV_CAP_SPLV_SHIFT 18 +#define L1F_DEV_CAP_RBER BIT(15) +#define L1F_DEV_CAP_PIPRS BIT(14) +#define L1F_DEV_CAP_AIPRS BIT(13) +#define L1F_DEV_CAP_ABPRS BIT(12) +#define L1F_DEV_CAP_L1ACLAT_MASK ASHFT9(7UL) +#define L1F_DEV_CAP_L1ACLAT_SHIFT 9 +#define L1F_DEV_CAP_L0SACLAT_MASK ASHFT6(7UL) +#define L1F_DEV_CAP_L0SACLAT_SHIFT 6 +#define L1F_DEV_CAP_EXTAG BIT(5) +#define L1F_DEV_CAP_PHANTOM BIT(4) +#define L1F_DEV_CAP_MPL_MASK ASHFT0(7UL) +#define L1F_DEV_CAP_MPL_SHIFT 0 +#define L1F_DEV_CAP_MPL_128 1 +#define L1F_DEV_CAP_MPL_256 2 +#define L1F_DEV_CAP_MPL_512 3 +#define L1F_DEV_CAP_MPL_1024 4 +#define L1F_DEV_CAP_MPL_2048 5 +#define L1F_DEV_CAP_MPL_4096 6 + +#define L1F_DEV_CTRL 0x0060 /* 16bit */ +#define L1F_DEV_CTRL_MAXRRS_MASK ASHFT12(7U) +#define L1F_DEV_CTRL_MAXRRS_SHIFT 12 +#define L1F_DEV_CTRL_MAXRRS_MIN 2 +#define L1F_DEV_CTRL_NOSNP_EN BIT(11) +#define L1F_DEV_CTRL_AUXPWR_EN BIT(10) +#define L1F_DEV_CTRL_PHANTOM_EN BIT(9) +#define L1F_DEV_CTRL_EXTAG_EN BIT(8) +#define L1F_DEV_CTRL_MPL_MASK ASHFT5(7U) +#define L1F_DEV_CTRL_MPL_SHIFT 5 +#define L1F_DEV_CTRL_RELORD_EN BIT(4) +#define L1F_DEV_CTRL_URR_EN BIT(3) +#define L1F_DEV_CTRL_FERR_EN BIT(2) +#define L1F_DEV_CTRL_NFERR_EN BIT(1) +#define L1F_DEV_CTRL_CERR_EN BIT(0) + + +#define L1F_DEV_STAT 0x0062 /* 16bit */ +#define L1F_DEV_STAT_XS_PEND BIT(5) +#define L1F_DEV_STAT_AUXPWR BIT(4) +#define L1F_DEV_STAT_UR BIT(3) +#define L1F_DEV_STAT_FERR BIT(2) +#define L1F_DEV_STAT_NFERR BIT(1) +#define L1F_DEV_STAT_CERR BIT(0) + +#define L1F_LNK_CAP 0x0064 +#define L1F_LNK_CAP_PRTNUM_MASK ASHFT24(0xFFUL) +#define L1F_LNK_CAP_PRTNUM_SHIFT 24 +#define L1F_LNK_CAP_CLK_PM BIT(18) +#define L1F_LNK_CAP_L1EXTLAT_MASK ASHFT15(7UL) +#define L1F_LNK_CAP_L1EXTLAT_SHIFT 15 +#define L1F_LNK_CAP_L0SEXTLAT_MASK ASHFT12(7UL) +#define L1F_LNK_CAP_L0SEXTLAT_SHIFT 12 +#define L1F_LNK_CAP_ASPM_SUP_MASK ASHFT10(3UL) +#define L1F_LNK_CAP_ASPM_SUP_SHIFT 10 +#define L1F_LNK_CAP_ASPM_SUP_L0S 1 +#define L1F_LNK_CAP_ASPM_SUP_L0SL1 3 +#define L1F_LNK_CAP_MAX_LWH_MASK ASHFT4(0x3FUL) +#define L1F_LNK_CAP_MAX_LWH_SHIFT 4 +#define L1F_LNK_CAP_MAX_LSPD_MASH ASHFT0(0xFUL) +#define L1F_LNK_CAP_MAX_LSPD_SHIFT 0 + +#define L1F_LNK_CTRL 0x0068 /* 16bit */ +#define L1F_LNK_CTRL_CLK_PM_EN BIT(8) +#define L1F_LNK_CTRL_EXTSYNC BIT(7) +#define L1F_LNK_CTRL_CMNCLK_CFG BIT(6) +#define L1F_LNK_CTRL_RCB_128B BIT(3) /* 0:64b,1:128b */ +#define L1F_LNK_CTRL_ASPM_MASK ASHFT0(3U) +#define L1F_LNK_CTRL_ASPM_SHIFT 0 +#define L1F_LNK_CTRL_ASPM_DIS 0 +#define L1F_LNK_CTRL_ASPM_ENL0S 1 +#define L1F_LNK_CTRL_ASPM_ENL1 2 +#define L1F_LNK_CTRL_ASPM_ENL0SL1 3 + +#define L1F_LNK_STAT 0x006A /* 16bit */ +#define L1F_LNK_STAT_SCLKCFG BIT(12) +#define L1F_LNK_STAT_LNKTRAIN BIT(11) +#define L1F_LNK_STAT_TRNERR BIT(10) +#define L1F_LNK_STAT_LNKSPD_MASK ASHFT0(0xFU) +#define L1F_LNK_STAT_LNKSPD_SHIFT 0 +#define L1F_LNK_STAT_NEGLW_MASK ASHFT4(0x3FU) +#define L1F_LNK_STAT_NEGLW_SHIFT 4 + +#define L1F_MSIX_MASK 0x0090 +#define L1F_MSIX_PENDING 0x0094 + +#define L1F_UE_SVRT 0x010C +#define L1F_UE_SVRT_UR BIT(20) +#define L1F_UE_SVRT_ECRCERR BIT(19) +#define L1F_UE_SVRT_MTLP BIT(18) +#define L1F_UE_SVRT_RCVOVFL BIT(17) +#define L1F_UE_SVRT_UNEXPCPL BIT(16) +#define L1F_UE_SVRT_CPLABRT BIT(15) +#define L1F_UE_SVRT_CPLTO BIT(14) +#define L1F_UE_SVRT_FCPROTERR BIT(13) +#define L1F_UE_SVRT_PTLP BIT(12) +#define L1F_UE_SVRT_DLPROTERR BIT(4) +#define L1F_UE_SVRT_TRNERR BIT(0) + +#define L1F_EFLD 0x0204 /* eeprom/flash load */ +#define L1F_EFLD_F_ENDADDR_MASK ASHFT16(0x3FFUL) +#define L1F_EFLD_F_ENDADDR_SHIFT 16 +#define L1F_EFLD_F_EXIST BIT(10) +#define L1F_EFLD_E_EXIST BIT(9) +#define L1F_EFLD_EXIST BIT(8) +#define L1F_EFLD_STAT BIT(5) /* 0:finish,1:in progress */ +#define L1F_EFLD_IDLE BIT(4) +#define L1F_EFLD_START BIT(0) + +#define L1F_SLD 0x0218 /* efuse load */ +#define L1F_SLD_FREQ_MASK ASHFT24(3UL) +#define L1F_SLD_FREQ_SHIFT 24 +#define L1F_SLD_FREQ_100K 0 +#define L1F_SLD_FREQ_200K 1 +#define L1F_SLD_FREQ_300K 2 +#define L1F_SLD_FREQ_400K 3 +#define L1F_SLD_EXIST BIT(23) +#define L1F_SLD_SLVADDR_MASK ASHFT16(0x7FUL) +#define L1F_SLD_SLVADDR_SHIFT 16 +#define L1F_SLD_IDLE BIT(13) +#define L1F_SLD_STAT BIT(12) /* 0:finish,1:in progress */ +#define L1F_SLD_START BIT(11) +#define L1F_SLD_STARTADDR_MASK ASHFT0(0xFFUL) +#define L1F_SLD_STARTADDR_SHIFT 0 +#define L1F_SLD_MAX_TO 100 + +#define L1F_PCIE_MSIC 0x021C +#define L1F_PCIE_MSIC_MSIX_DIS BIT(22) +#define L1F_PCIE_MSIC_MSI_DIS BIT(21) + +#define L1F_PPHY_MISC1 0x1000 +#define L1F_PPHY_MISC1_RCVDET BIT(2) +#define L1F_PPHY_MISC1_NFTS_MASK ASHFT16(0xFFUL) +#define L1F_PPHY_MISC1_NFTS_SHIFT 16 +#define L1F_PPHY_MISC1_NFTS_HIPERF 0xA0 /* ???? */ + +#define L1F_PPHY_MISC2 0x1004 +#define L1F_PPHY_MISC2_L0S_TH_MASK ASHFT18(0x3UL) +#define L1F_PPHY_MISC2_L0S_TH_SHIFT 18 +#define L1F_PPHY_MISC2_CDR_BW_MASK ASHFT16(0x3UL) +#define L1F_PPHY_MISC2_CDR_BW_SHIFT 16 + +#define L1F_PDLL_TRNS1 0x1104 +#define L1F_PDLL_TRNS1_D3PLLOFF_EN BIT(11) +#define L1F_PDLL_TRNS1_REGCLK_SEL_NORM BIT(10) +#define L1F_PDLL_TRNS1_REPLY_TO_MASK ASHFT0(0x3FFUL) +#define L1F_PDLL_TRNS1_REPLY_TO_SHIFT 0 + + +#define L1F_TLEXTN_STATS 0x1208 +#define L1F_TLEXTN_STATS_DEVNO_MASK ASHFT16(0x1FUL) +#define L1F_TLEXTN_STATS_DEVNO_SHIFT 16 +#define L1F_TLEXTN_STATS_BUSNO_MASK ASHFT8(0xFFUL) +#define L1F_TLEXTN_STATS_BUSNO_SHIFT 8 + +#define L1F_EFUSE_CTRL 0x12C0 +#define L1F_EFUSE_CTRL_FLAG BIT(31) /* 0:read,1:write */ +#define L1F_EUFSE_CTRL_ACK BIT(30) +#define L1F_EFUSE_CTRL_ADDR_MASK ASHFT16(0x3FFUL) +#define L1F_EFUSE_CTRL_ADDR_SHIFT 16 + +#define L1F_EFUSE_DATA 0x12C4 + +#define L1F_SPI_OP1 0x12C8 +#define L1F_SPI_OP1_RDID_MASK ASHFT24(0xFFUL) +#define L1F_SPI_OP1_RDID_SHIFT 24 +#define L1F_SPI_OP1_CE_MASK ASHFT16(0xFFUL) +#define L1F_SPI_OP1_CE_SHIFT 16 +#define L1F_SPI_OP1_SE_MASK ASHFT8(0xFFUL) +#define L1F_SPI_OP1_SE_SHIFT 8 +#define L1F_SPI_OP1_PRGRM_MASK ASHFT0(0xFFUL) +#define L1F_SPI_OP1_PRGRM_SHIFT 0 + +#define L1F_SPI_OP2 0x12CC +#define L1F_SPI_OP2_READ_MASK ASHFT24(0xFFUL) +#define L1F_SPI_OP2_READ_SHIFT 24 +#define L1F_SPI_OP2_WRSR_MASK ASHFT16(0xFFUL) +#define L1F_SPI_OP2_WRSR_SHIFT 16 +#define L1F_SPI_OP2_RDSR_MASK ASHFT8(0xFFUL) +#define L1F_SPI_OP2_RDSR_SHIFT 8 +#define L1F_SPI_OP2_WREN_MASK ASHFT0(0xFFUL) +#define L1F_SPI_OP2_WREN_SHIFT 0 + +#define L1F_SPI_OP3 0x12E4 +#define L1F_SPI_OP3_WRDI_MASK ASHFT8(0xFFUL) +#define L1F_SPI_OP3_WRDI_SHIFT 8 +#define L1F_SPI_OP3_EWSR_MASK ASHFT0(0xFFUL) +#define L1F_SPI_OP3_EWSR_SHIFT 0 + +#define L1F_EF_CTRL 0x12D0 +#define L1F_EF_CTRL_FSTS_MASK ASHFT20(0xFFUL) +#define L1F_EF_CTRL_FSTS_SHIFT 20 +#define L1F_EF_CTRL_CLASS_MASK ASHFT16(7UL) +#define L1F_EF_CTRL_CLASS_SHIFT 16 +#define L1F_EF_CTRL_CLASS_F_UNKNOWN 0 +#define L1F_EF_CTRL_CLASS_F_STD 1 +#define L1F_EF_CTRL_CLASS_F_SST 2 +#define L1F_EF_CTRL_CLASS_E_UNKNOWN 0 +#define L1F_EF_CTRL_CLASS_E_1K 1 +#define L1F_EF_CTRL_CLASS_E_4K 2 +#define L1F_EF_CTRL_FRET BIT(15) /* 0:OK,1:fail */ +#define L1F_EF_CTRL_TYP_MASK ASHFT12(3UL) +#define L1F_EF_CTRL_TYP_SHIFT 12 +#define L1F_EF_CTRL_TYP_NONE 0 +#define L1F_EF_CTRL_TYP_F 1 +#define L1F_EF_CTRL_TYP_E 2 +#define L1F_EF_CTRL_TYP_UNKNOWN 3 +#define L1F_EF_CTRL_ONE_CLK BIT(10) +#define L1F_EF_CTRL_ECLK_MASK ASHFT8(3UL) +#define L1F_EF_CTRL_ECLK_SHIFT 8 +#define L1F_EF_CTRL_ECLK_125K 0 +#define L1F_EF_CTRL_ECLK_250K 1 +#define L1F_EF_CTRL_ECLK_500K 2 +#define L1F_EF_CTRL_ECLK_1M 3 +#define L1F_EF_CTRL_FBUSY BIT(7) +#define L1F_EF_CTRL_ACTION BIT(6) /* 1:start,0:stop */ +#define L1F_EF_CTRL_AUTO_OP BIT(5) +#define L1F_EF_CTRL_SST_MODE BIT(4) /* force using sst */ +#define L1F_EF_CTRL_INST_MASK ASHFT0(0xFUL) +#define L1F_EF_CTRL_INST_SHIFT 0 +#define L1F_EF_CTRL_INST_NONE 0 +#define L1F_EF_CTRL_INST_READ 1 /* for flash & eeprom */ +#define L1F_EF_CTRL_INST_RDID 2 +#define L1F_EF_CTRL_INST_RDSR 3 +#define L1F_EF_CTRL_INST_WREN 4 +#define L1F_EF_CTRL_INST_PRGRM 5 +#define L1F_EF_CTRL_INST_SE 6 +#define L1F_EF_CTRL_INST_CE 7 +#define L1F_EF_CTRL_INST_WRSR 10 +#define L1F_EF_CTRL_INST_EWSR 11 +#define L1F_EF_CTRL_INST_WRDI 12 +#define L1F_EF_CTRL_INST_WRITE 2 /* only for eeprom */ + +#define L1F_EF_ADDR 0x12D4 +#define L1F_EF_DATA 0x12D8 +#define L1F_SPI_ID 0x12DC + +#define L1F_SPI_CFG_START 0x12E0 + +#define L1F_PMCTRL 0x12F8 +#define L1F_PMCTRL_HOTRST_WTEN BIT(31) +#define L1F_PMCTRL_ASPM_FCEN BIT(30) /* L0s/L1 dis by MAC based on + * thrghput(setting in 15A0) */ +#define L1F_PMCTRL_SADLY_EN BIT(29) +#define L1F_PMCTRL_L0S_BUFSRX_EN BIT(28) +#define L1F_PMCTRL_LCKDET_TIMER_MASK ASHFT24(0xFUL) +#define L1F_PMCTRL_LCKDET_TIMER_SHIFT 24 +#define L1F_PMCTRL_LCKDET_TIMER_DEF 0xC +#define L1F_PMCTRL_L1REQ_TO_MASK ASHFT20(0xFUL) +#define L1F_PMCTRL_L1REQ_TO_SHIFT 20 /* pm_request_l1 time > @ + * ->L0s not L1 */ +#define L1F_PMCTRL_L1REG_TO_DEF 0xC +#define L1F_PMCTRL_TXL1_AFTER_L0S BIT(19) +#define L1F_PMCTRL_L1_TIMER_MASK ASHFT16(7UL) +#define L1F_PMCTRL_L1_TIMER_SHIFT 16 +#define L1F_PMCTRL_L1_TIMER_DIS 0 +#define L1F_PMCTRL_L1_TIMER_2US 1 +#define L1F_PMCTRL_L1_TIMER_4US 2 +#define L1F_PMCTRL_L1_TIMER_8US 3 +#define L1F_PMCTRL_L1_TIMER_16US 4 +#define L1F_PMCTRL_L1_TIMER_24US 5 +#define L1F_PMCTRL_L1_TIMER_32US 6 +#define L1F_PMCTRL_L1_TIMER_63US 7 +#define L1F_PMCTRL_RCVR_WT_1US BIT(15) /* 1:1us, 0:2ms */ +#define L1F_PMCTRL_PWM_VER_11 BIT(14) /* 0:1.0a,1:1.1 */ +#define L1F_PMCTRL_L1_CLKSW_EN BIT(13) /* en pcie clk sw in L1 */ +#define L1F_PMCTRL_L0S_EN BIT(12) +#define L1F_PMCTRL_RXL1_AFTER_L0S BIT(11) +#define L1F_PMCTRL_L0S_TIMER_MASK ASHFT8(7UL) +#define L1F_PMCTRL_L0S_TIMER_SHIFT 8 +#define L1F_PMCTRL_L1_BUFSRX_EN BIT(7) +#define L1F_PMCTRL_L1_SRDSRX_PWD BIT(6) /* power down serdes rx */ +#define L1F_PMCTRL_L1_SRDSPLL_EN BIT(5) +#define L1F_PMCTRL_L1_SRDS_EN BIT(4) +#define L1F_PMCTRL_L1_EN BIT(3) +#define L1F_PMCTRL_CLKREQ_EN BIT(2) +#define L1F_PMCTRL_RBER_EN BIT(1) +#define L1F_PMCTRL_SPRSDWER_EN BIT(0) + +#define L1F_LTSSM_CTRL 0x12FC +#define L1F_LTSSM_WRO_EN BIT(12) + + +/******************************************************************************/ + +#define L1F_MASTER 0x1400 +#define L1F_MASTER_OTP_FLG BIT(31) +#define L1F_MASTER_DEV_NUM_MASK ASHFT24(0x7FUL) +#define L1F_MASTER_DEV_NUM_SHIFT 24 +#define L1F_MASTER_REV_NUM_MASK ASHFT16(0xFFUL) +#define L1F_MASTER_REV_NUM_SHIFT 16 +#define L1F_MASTER_DEASSRT BIT(15) /*ISSUE DE-ASSERT MSG */ +#define L1F_MASTER_RDCLR_INT BIT(14) +#define L1F_MASTER_DMA_RST BIT(13) +#define L1F_MASTER_PCLKSEL_SRDS BIT(12) /* 1:alwys sel pclk from + * serdes, not sw to 25M */ +#define L1F_MASTER_IRQMOD2_EN BIT(11) /* IRQ MODURATION FOR RX */ +#define L1F_MASTER_IRQMOD1_EN BIT(10) /* MODURATION FOR TX/RX */ +#define L1F_MASTER_MANU_INT BIT(9) /* SOFT MANUAL INT */ +#define L1F_MASTER_MANUTIMER_EN BIT(8) +#define L1F_MASTER_SYSALVTIMER_EN BIT(7) /* SYS ALIVE TIMER EN */ +#define L1F_MASTER_OOB_DIS BIT(6) /* OUT OF BOX DIS */ +#define L1F_MASTER_WAKEN_25M BIT(5) /* WAKE WO. PCIE CLK */ +#define L1F_MASTER_BERT_START BIT(4) +#define L1F_MASTER_PCIE_TSTMOD_MASK ASHFT2(3UL) +#define L1F_MASTER_PCIE_TSTMOD_SHIFT 2 +#define L1F_MASTER_PCIE_RST BIT(1) +#define L1F_MASTER_DMA_MAC_RST BIT(0) /* RST MAC & DMA */ +#define L1F_DMA_MAC_RST_TO 50 + +#define L1F_MANU_TIMER 0x1404 + +#define L1F_IRQ_MODU_TIMER 0x1408 +#define L1F_IRQ_MODU_TIMER2_MASK ASHFT16(0xFFFFUL) +#define L1F_IRQ_MODU_TIMER2_SHIFT 16 /* ONLY FOR RX */ +#define L1F_IRQ_MODU_TIMER1_MASK ASHFT0(0xFFFFUL) +#define L1F_IRQ_MODU_TIMER1_SHIFT 0 + +#define L1F_PHY_CTRL 0x140C +#define L1F_PHY_CTRL_ADDR_MASK ASHFT19(0x1FUL) +#define L1F_PHY_CTRL_ADDR_SHIFT 19 +#define L1F_PHY_CTRL_BP_VLTGSW BIT(18) +#define L1F_PHY_CTRL_100AB_EN BIT(17) +#define L1F_PHY_CTRL_10AB_EN BIT(16) +#define L1F_PHY_CTRL_PLL_BYPASS BIT(15) +#define L1F_PHY_CTRL_POWER_DOWN BIT(14) /* affect MAC & PHY, + * go to low power sts */ +#define L1F_PHY_CTRL_PLL_ON BIT(13) /* 1:PLL ALWAYS ON + * 0:CAN SWITCH IN LPW */ +#define L1F_PHY_CTRL_RST_ANALOG BIT(12) +#define L1F_PHY_CTRL_HIB_PULSE BIT(11) +#define L1F_PHY_CTRL_HIB_EN BIT(10) +#define L1F_PHY_CTRL_GIGA_DIS BIT(9) +#define L1F_PHY_CTRL_IDDQ_DIS BIT(8) /* POWER ON RST */ +#define L1F_PHY_CTRL_IDDQ BIT(7) /* WHILE REBOOT, BIT8(1) + * EFFECTS BIT7 */ +#define L1F_PHY_CTRL_LPW_EXIT BIT(6) +#define L1F_PHY_CTRL_GATE_25M BIT(5) +#define L1F_PHY_CTRL_RVRS_ANEG BIT(4) +#define L1F_PHY_CTRL_ANEG_NOW BIT(3) +#define L1F_PHY_CTRL_LED_MODE BIT(2) +#define L1F_PHY_CTRL_RTL_MODE BIT(1) +#define L1F_PHY_CTRL_DSPRST_OUT BIT(0) /* OUT OF DSP RST STATE */ +#define L1F_PHY_CTRL_DSPRST_TO 80 +#define L1F_PHY_CTRL_CLS (\ + L1F_PHY_CTRL_LED_MODE |\ + L1F_PHY_CTRL_100AB_EN |\ + L1F_PHY_CTRL_PLL_ON) + +#define L1F_MAC_STS 0x1410 +#define L1F_MAC_STS_SFORCE_MASK ASHFT14(0xFUL) +#define L1F_MAC_STS_SFORCE_SHIFT 14 +#define L1F_MAC_STS_CALIB_DONE BIT13 +#define L1F_MAC_STS_CALIB_RES_MASK ASHFT8(0x1FUL) +#define L1F_MAC_STS_CALIB_RES_SHIFT 8 +#define L1F_MAC_STS_CALIBERR_MASK ASHFT4(0xFUL) +#define L1F_MAC_STS_CALIBERR_SHIFT 4 +#define L1F_MAC_STS_TXQ_BUSY BIT(3) +#define L1F_MAC_STS_RXQ_BUSY BIT(2) +#define L1F_MAC_STS_TXMAC_BUSY BIT(1) +#define L1F_MAC_STS_RXMAC_BUSY BIT(0) +#define L1F_MAC_STS_IDLE (\ + L1F_MAC_STS_TXQ_BUSY |\ + L1F_MAC_STS_RXQ_BUSY |\ + L1F_MAC_STS_TXMAC_BUSY |\ + L1F_MAC_STS_RXMAC_BUSY) + +#define L1F_MDIO 0x1414 +#define L1F_MDIO_MODE_EXT BIT(30) /* 0:normal,1:ext */ +#define L1F_MDIO_POST_READ BIT(29) +#define L1F_MDIO_AUTO_POLLING BIT(28) +#define L1F_MDIO_BUSY BIT(27) +#define L1F_MDIO_CLK_SEL_MASK ASHFT24(7UL) +#define L1F_MDIO_CLK_SEL_SHIFT 24 +#define L1F_MDIO_CLK_SEL_25MD4 0 /* 25M DIV 4 */ +#define L1F_MDIO_CLK_SEL_25MD6 2 +#define L1F_MDIO_CLK_SEL_25MD8 3 +#define L1F_MDIO_CLK_SEL_25MD10 4 +#define L1F_MDIO_CLK_SEL_25MD32 5 +#define L1F_MDIO_CLK_SEL_25MD64 6 +#define L1F_MDIO_CLK_SEL_25MD128 7 +#define L1F_MDIO_START BIT(23) +#define L1F_MDIO_SPRES_PRMBL BIT(22) +#define L1F_MDIO_OP_READ BIT(21) /* 1:read,0:write */ +#define L1F_MDIO_REG_MASK ASHFT16(0x1FUL) +#define L1F_MDIO_REG_SHIFT 16 +#define L1F_MDIO_DATA_MASK ASHFT0(0xFFFFUL) +#define L1F_MDIO_DATA_SHIFT 0 +#define L1F_MDIO_MAX_AC_TO 120 + +#define L1F_MDIO_EXTN 0x1448 +#define L1F_MDIO_EXTN_PORTAD_MASK ASHFT21(0x1FUL) +#define L1F_MDIO_EXTN_PORTAD_SHIFT 21 +#define L1F_MDIO_EXTN_DEVAD_MASK ASHFT16(0x1FUL) +#define L1F_MDIO_EXTN_DEVAD_SHIFT 16 +#define L1F_MDIO_EXTN_REG_MASK ASHFT0(0xFFFFUL) +#define L1F_MDIO_EXTN_REG_SHIFT 0 + +#define L1F_PHY_STS 0x1418 +#define L1F_PHY_STS_LPW BIT(31) +#define L1F_PHY_STS_LPI BIT(30) +#define L1F_PHY_STS_PWON_STRIP_MASK ASHFT16(0xFFFUL) +#define L1F_PHY_STS_PWON_STRIP_SHIFT 16 + +#define L1F_PHY_STS_DUPLEX BIT(3) +#define L1F_PHY_STS_LINKUP BIT(2) +#define L1F_PHY_STS_SPEED_MASK ASHFT0(3UL) +#define L1F_PHY_STS_SPEED_SHIFT 0 +#define L1F_PHY_STS_SPEED_1000M 2 +#define L1F_PHY_STS_SPEED_100M 1 +#define L1F_PHY_STS_SPEED_10M 0 + +#define L1F_BIST0 0x141C +#define L1F_BIST0_COL_MASK ASHFT24(0x3FUL) +#define L1F_BIST0_COL_SHIFT 24 +#define L1F_BIST0_ROW_MASK ASHFT12(0xFFFUL) +#define L1F_BIST0_ROW_SHIFT 12 +#define L1F_BIST0_STEP_MASK ASHFT8(0xFUL) +#define L1F_BIST0_STEP_SHIFT 8 +#define L1F_BIST0_PATTERN_MASK ASHFT4(7UL) +#define L1F_BIST0_PATTERN_SHIFT 4 +#define L1F_BIST0_CRIT BIT(3) +#define L1F_BIST0_FIXED BIT(2) +#define L1F_BIST0_FAIL BIT(1) +#define L1F_BIST0_START BIT(0) + +#define L1F_BIST1 0x1420 +#define L1F_BIST1_COL_MASK ASHFT24(0x3FUL) +#define L1F_BIST1_COL_SHIFT 24 +#define L1F_BIST1_ROW_MASK ASHFT12(0xFFFUL) +#define L1F_BIST1_ROW_SHIFT 12 +#define L1F_BIST1_STEP_MASK ASHFT8(0xFUL) +#define L1F_BIST1_STEP_SHIFT 8 +#define L1F_BIST1_PATTERN_MASK ASHFT4(7UL) +#define L1F_BIST1_PATTERN_SHIFT 4 +#define L1F_BIST1_CRIT BIT(3) +#define L1F_BIST1_FIXED BIT(2) +#define L1F_BIST1_FAIL BIT(1) +#define L1F_BIST1_START BIT(0) + +#define L1F_SERDES 0x1424 +#define L1F_SERDES_PHYCLK_SLWDWN BIT(18) +#define L1F_SERDES_MACCLK_SLWDWN BIT(17) +#define L1F_SERDES_SELFB_PLL_MASK ASHFT14(3UL) +#define L1F_SERDES_SELFB_PLL_SHIFT 14 +#define L1F_SERDES_PHYCLK_SEL_GTX BIT(13) /* 1:gtx_clk, 0:25M */ +#define L1F_SERDES_PCIECLK_SEL_SRDS BIT(12) /* 1:serdes,0:25M */ +#define L1F_SERDES_BUFS_RX_EN BIT(11) +#define L1F_SERDES_PD_RX BIT(10) +#define L1F_SERDES_PLL_EN BIT(9) +#define L1F_SERDES_EN BIT(8) +#define L1F_SERDES_SELFB_PLL_SEL_CSR BIT(6) /* 0:state-machine,1:csr */ +#define L1F_SERDES_SELFB_PLL_CSR_MASK ASHFT4(3UL) +#define L1F_SERDES_SELFB_PLL_CSR_SHIFT 4 +#define L1F_SERDES_SELFB_PLL_CSR_4 3 /* 4-12% OV-CLK */ +#define L1F_SERDES_SELFB_PLL_CSR_0 2 /* 0-4% OV-CLK */ +#define L1F_SERDES_SELFB_PLL_CSR_12 1 /* 12-18% OV-CLK */ +#define L1F_SERDES_SELFB_PLL_CSR_18 0 /* 18-25% OV-CLK */ +#define L1F_SERDES_VCO_SLOW BIT(3) +#define L1F_SERDES_VCO_FAST BIT(2) +#define L1F_SERDES_LOCKDCT_EN BIT(1) +#define L1F_SERDES_LOCKDCTED BIT(0) + +#define L1F_LED_CTRL 0x1428 +#define L1F_LED_CTRL_PATMAP2_MASK ASHFT8(3UL) +#define L1F_LED_CTRL_PATMAP2_SHIFT 8 +#define L1F_LED_CTRL_PATMAP1_MASK ASHFT6(3UL) +#define L1F_LED_CTRL_PATMAP1_SHIFT 6 +#define L1F_LED_CTRL_PATMAP0_MASK ASHFT4(3UL) +#define L1F_LED_CTRL_PATMAP0_SHIFT 4 +#define L1F_LED_CTRL_D3_MODE_MASK ASHFT2(3UL) +#define L1F_LED_CTRL_D3_MODE_SHIFT 2 +#define L1F_LED_CTRL_D3_MODE_NORMAL 0 +#define L1F_LED_CTRL_D3_MODE_WOL_DIS 1 +#define L1F_LED_CTRL_D3_MODE_WOL_ANY 2 +#define L1F_LED_CTRL_D3_MODE_WOL_EN 3 +#define L1F_LED_CTRL_DUTY_CYCL_MASK ASHFT0(3UL) +#define L1F_LED_CTRL_DUTY_CYCL_SHIFT 0 +#define L1F_LED_CTRL_DUTY_CYCL_50 0 /* 50% */ +#define L1F_LED_CTRL_DUTY_CYCL_125 1 /* 12.5% */ +#define L1F_LED_CTRL_DUTY_CYCL_25 2 /* 25% */ +#define L1F_LED_CTRL_DUTY_CYCL_75 3 /* 75% */ + +#define L1F_LED_PATN 0x142C +#define L1F_LED_PATN1_MASK ASHFT16(0xFFFFUL) +#define L1F_LED_PATN1_SHIFT 16 +#define L1F_LED_PATN0_MASK ASHFT0(0xFFFFUL) +#define L1F_LED_PATN0_SHIFT 0 + +#define L1F_LED_PATN2 0x1430 +#define L1F_LED_PATN2_MASK ASHFT0(0xFFFFUL) +#define L1F_LED_PATN2_SHIFT 0 + +#define L1F_SYSALV 0x1434 +#define L1F_SYSALV_FLAG BIT(0) + +#define L1F_PCIERR_INST 0x1438 +#define L1F_PCIERR_INST_TX_RATE_MASK ASHFT4(0xFUL) +#define L1F_PCIERR_INST_TX_RATE_SHIFT 4 +#define L1F_PCIERR_INST_RX_RATE_MASK ASHFT0(0xFUL) +#define L1F_PCIERR_INST_RX_RATE_SHIFT 0 + +#define L1F_LPI_DECISN_TIMER 0x143C + +#define L1F_LPI_CTRL 0x1440 +#define L1F_LPI_CTRL_CHK_DA BIT(31) +#define L1F_LPI_CTRL_ENH_TO_MASK ASHFT12(0x1FFFUL) +#define L1F_LPI_CTRL_ENH_TO_SHIFT 12 +#define L1F_LPI_CTRL_ENH_TH_MASK ASHFT6(0x1FUL) +#define L1F_LPI_CTRL_ENH_TH_SHIFT 6 +#define L1F_LPI_CTRL_ENH_EN BIT(5) +#define L1F_LPI_CTRL_CHK_RX BIT(4) +#define L1F_LPI_CTRL_CHK_STATE BIT(3) +#define L1F_LPI_CTRL_GMII BIT(2) +#define L1F_LPI_CTRL_TO_PHY BIT(1) +#define L1F_LPI_CTRL_EN BIT(0) + +#define L1F_LPI_WAIT 0x1444 +#define L1F_LPI_WAIT_TIMER_MASK ASHFT0(0xFFFFUL) +#define L1F_LPI_WAIT_TIMER_SHIFT 0 + +#define L1F_HRTBT_VLAN 0x1450 /* HEARTBEAT, FOR CIFS */ +#define L1F_HRTBT_VLANID_MASK ASHFT0(0xFFFFUL) /* OR CLOUD */ +#define L1F_HRRBT_VLANID_SHIFT 0 + +#define L1F_HRTBT_CTRL 0x1454 +#define L1F_HRTBT_CTRL_EN BIT(31) +#define L1F_HRTBT_CTRL_PERIOD_MASK ASHFT25(0x3FUL) +#define L1F_HRTBT_CTRL_PERIOD_SHIFT 25 +#define L1F_HRTBT_CTRL_HASVLAN BIT(24) +#define L1F_HRTBT_CTRL_HDRADDR_MASK ASHFT12(0xFFFUL) /* A0 */ +#define L1F_HRTBT_CTRL_HDRADDR_SHIFT 12 +#define L1F_HRTBT_CTRL_HDRADDRB0_MASK ASHFT13(0x7FFUL) /* B0 */ +#define L1F_HRTBT_CTRL_HDRADDRB0_SHIFT 13 +#define L1F_HRTBT_CTRL_PKT_FRAG BIT(12) /* B0 */ +#define L1F_HRTBT_CTRL_PKTLEN_MASK ASHFT0(0xFFFUL) +#define L1F_HRTBT_CTRL_PKTLEN_SHIFT 0 + +#define L1F_HRTBT_EXT_CTRL 0x1AD0 /* B0 */ +#define L1F_HRTBT_EXT_CTRL_NS_EN BIT(12) +#define L1F_HRTBT_EXT_CTRL_FRAG_LEN_MASK ASHFT4(0xFFUL) +#define L1F_HRTBT_EXT_CTRL_FRAG_LEN_SHIFT 4 +#define L1F_HRTBT_EXT_CTRL_IS_8023 BIT(3) +#define L1F_HRTBT_EXT_CTRL_IS_IPV6 BIT(2) +#define L1F_HRTBT_EXT_CTRL_WAKEUP_EN BIT(1) +#define L1F_HRTBT_EXT_CTRL_ARP_EN BIT(0) + +#define L1F_HRTBT_REM_IPV4_ADDR 0x1AD4 +#define L1F_HRTBT_HOST_IPV4_ADDR 0x1478/*use L1F_TRD_BUBBLE_DA_IP4*/ +#define L1F_HRTBT_REM_IPV6_ADDR3 0x1AD8 +#define L1F_HRTBT_REM_IPV6_ADDR2 0x1ADC +#define L1F_HRTBT_REM_IPV6_ADDR1 0x1AE0 +#define L1F_HRTBT_REM_IPV6_ADDR0 0x1AE4 +/*SWOI_HOST_IPV6_ADDR reuse reg1a60-1a6c, 1a70-1a7c, 1aa0-1aac, 1ab0-1abc.*/ +#define L1F_HRTBT_WAKEUP_PORT 0x1AE8 +#define L1F_HRTBT_WAKEUP_PORT_SRC_MASK ASHFT16(0xFFFFUL) +#define L1F_HRTBT_WAKEUP_PORT_SRC_SHIFT 16 +#define L1F_HRTBT_WAKEUP_PORT_DEST_MASK ASHFT0(0xFFFFUL) +#define L1F_HRTBT_WAKEUP_PORT_DEST_SHIFT 0 + +#define L1F_HRTBT_WAKEUP_DATA7 0x1AEC +#define L1F_HRTBT_WAKEUP_DATA6 0x1AF0 +#define L1F_HRTBT_WAKEUP_DATA5 0x1AF4 +#define L1F_HRTBT_WAKEUP_DATA4 0x1AF8 +#define L1F_HRTBT_WAKEUP_DATA3 0x1AFC +#define L1F_HRTBT_WAKEUP_DATA2 0x1B80 +#define L1F_HRTBT_WAKEUP_DATA1 0x1B84 +#define L1F_HRTBT_WAKEUP_DATA0 0x1B88 + +#define L1F_RXPARSE 0x1458 +#define L1F_RXPARSE_FLT6_L4_MASK ASHFT30(3UL) +#define L1F_RXPARSE_FLT6_L4_SHIFT 30 +#define L1F_RXPARSE_FLT6_L3_MASK ASHFT28(3UL) +#define L1F_RXPARSE_FLT6_L3_SHIFT 28 +#define L1F_RXPARSE_FLT5_L4_MASK ASHFT26(3UL) +#define L1F_RXPARSE_FLT5_L4_SHIFT 26 +#define L1F_RXPARSE_FLT5_L3_MASK ASHFT24(3UL) +#define L1F_RXPARSE_FLT5_L3_SHIFT 24 +#define L1F_RXPARSE_FLT4_L4_MASK ASHFT22(3UL) +#define L1F_RXPARSE_FLT4_L4_SHIFT 22 +#define L1F_RXPARSE_FLT4_L3_MASK ASHFT20(3UL) +#define L1F_RXPARSE_FLT4_L3_SHIFT 20 +#define L1F_RXPARSE_FLT3_L4_MASK ASHFT18(3UL) +#define L1F_RXPARSE_FLT3_L4_SHIFT 18 +#define L1F_RXPARSE_FLT3_L3_MASK ASHFT16(3UL) +#define L1F_RXPARSE_FLT3_L3_SHIFT 16 +#define L1F_RXPARSE_FLT2_L4_MASK ASHFT14(3UL) +#define L1F_RXPARSE_FLT2_L4_SHIFT 14 +#define L1F_RXPARSE_FLT2_L3_MASK ASHFT12(3UL) +#define L1F_RXPARSE_FLT2_L3_SHIFT 12 +#define L1F_RXPARSE_FLT1_L4_MASK ASHFT10(3UL) +#define L1F_RXPARSE_FLT1_L4_SHIFT 10 +#define L1F_RXPARSE_FLT1_L3_MASK ASHFT8(3UL) +#define L1F_RXPARSE_FLT1_L3_SHIFT 8 +#define L1F_RXPARSE_FLT6_EN BIT(5) +#define L1F_RXPARSE_FLT5_EN BIT(4) +#define L1F_RXPARSE_FLT4_EN BIT(3) +#define L1F_RXPARSE_FLT3_EN BIT(2) +#define L1F_RXPARSE_FLT2_EN BIT(1) +#define L1F_RXPARSE_FLT1_EN BIT(0) +#define L1F_RXPARSE_FLT_L4_UDP 0 +#define L1F_RXPARSE_FLT_L4_TCP 1 +#define L1F_RXPARSE_FLT_L4_BOTH 2 +#define L1F_RXPARSE_FLT_L4_NONE 3 +#define L1F_RXPARSE_FLT_L3_IPV6 0 +#define L1F_RXPARSE_FLT_L3_IPV4 1 +#define L1F_RXPARSE_FLT_L3_BOTH 2 + +/* Terodo support */ +#define L1F_TRD_CTRL 0x145C +#define L1F_TRD_CTRL_EN BIT(31) +#define L1F_TRD_CTRL_BUBBLE_WAKE_EN BIT(30) +#define L1F_TRD_CTRL_PREFIX_CMP_HW BIT(28) +#define L1F_TRD_CTRL_RSHDR_ADDR_MASK ASHFT16(0xFFFUL) +#define L1F_TRD_CTRL_RSHDR_ADDR_SHIFT 16 +#define L1F_TRD_CTRL_SINTV_MAX_MASK ASHFT8(0xFFUL) +#define L1F_TRD_CTRL_SINTV_MAX_SHIFT 8 +#define L1F_TRD_CTRL_SINTV_MIN_MASK ASHFT0(0xFFUL) +#define L1F_TRD_CTRL_SINTV_MIN_SHIFT 0 + +#define L1F_TRD_RS 0x1460 +#define L1F_TRD_RS_SZ_MASK ASHFT20(0xFFFUL) +#define L1F_TRD_RS_SZ_SHIFT 20 +#define L1F_TRD_RS_NONCE_OFS_MASK ASHFT8(0xFFFUL) +#define L1F_TRD_RS_NONCE_OFS_SHIFT 8 +#define L1F_TRD_RS_SEQ_OFS_MASK ASHFT0(0xFFUL) +#define L1F_TRD_RS_SEQ_OFS_SHIFT 0 + +#define L1F_TRD_SRV_IP4 0x1464 + +#define L1F_TRD_CLNT_EXTNL_IP4 0x1468 + +#define L1F_TRD_PORT 0x146C +#define L1F_TRD_PORT_CLNT_EXTNL_MASK ASHFT16(0xFFFFUL) +#define L1F_TRD_PORT_CLNT_EXTNL_SHIFT 16 +#define L1F_TRD_PORT_SRV_MASK ASHFT0(0xFFFFUL) +#define L1F_TRD_PORT_SRV_SHIFT 0 + +#define L1F_TRD_PREFIX 0x1470 + +#define L1F_TRD_BUBBLE_DA_IP4 0x1478 + +#define L1F_TRD_BUBBLE_DA_PORT 0x147C + + +#define L1F_IDLE_DECISN_TIMER 0x1474 /* B0 */ +#define L1F_IDLE_DECISN_TIMER_DEF 0x400 /* 1ms */ + + +#define L1F_MAC_CTRL 0x1480 +#define L1F_MAC_CTRL_FAST_PAUSE BIT(31) +#define L1F_MAC_CTRL_WOLSPED_SWEN BIT(30) +#define L1F_MAC_CTRL_MHASH_ALG_HI5B BIT(29) /* 1:legacy, 0:marvl(low5b)*/ +#define L1F_MAC_CTRL_SPAUSE_EN BIT(28) +#define L1F_MAC_CTRL_DBG_EN BIT(27) +#define L1F_MAC_CTRL_BRD_EN BIT(26) +#define L1F_MAC_CTRL_MULTIALL_EN BIT(25) +#define L1F_MAC_CTRL_RX_XSUM_EN BIT(24) +#define L1F_MAC_CTRL_THUGE BIT(23) +#define L1F_MAC_CTRL_MBOF BIT(22) +#define L1F_MAC_CTRL_SPEED_MASK ASHFT20(3UL) +#define L1F_MAC_CTRL_SPEED_SHIFT 20 +#define L1F_MAC_CTRL_SPEED_10_100 1 +#define L1F_MAC_CTRL_SPEED_1000 2 +#define L1F_MAC_CTRL_SIMR BIT(19) +#define L1F_MAC_CTRL_SSTCT BIT(17) +#define L1F_MAC_CTRL_TPAUSE BIT(16) +#define L1F_MAC_CTRL_PROMISC_EN BIT(15) +#define L1F_MAC_CTRL_VLANSTRIP BIT(14) +#define L1F_MAC_CTRL_PRMBLEN_MASK ASHFT10(0xFUL) +#define L1F_MAC_CTRL_PRMBLEN_SHIFT 10 +#define L1F_MAC_CTRL_RHUGE_EN BIT(9) +#define L1F_MAC_CTRL_FLCHK BIT(8) +#define L1F_MAC_CTRL_PCRCE BIT(7) +#define L1F_MAC_CTRL_CRCE BIT(6) +#define L1F_MAC_CTRL_FULLD BIT(5) +#define L1F_MAC_CTRL_LPBACK_EN BIT(4) +#define L1F_MAC_CTRL_RXFC_EN BIT(3) +#define L1F_MAC_CTRL_TXFC_EN BIT(2) +#define L1F_MAC_CTRL_RX_EN BIT(1) +#define L1F_MAC_CTRL_TX_EN BIT(0) + +#define L1F_GAP 0x1484 +#define L1F_GAP_IPGR2_MASK ASHFT24(0x7FUL) +#define L1F_GAP_IPGR2_SHIFT 24 +#define L1F_GAP_IPGR1_MASK ASHFT16(0x7FUL) +#define L1F_GAP_IPGR1_SHIFT 16 +#define L1F_GAP_MIN_IFG_MASK ASHFT8(0xFFUL) +#define L1F_GAP_MIN_IFG_SHIFT 8 +#define L1F_GAP_IPGT_MASK ASHFT0(0x7FUL) /* A0 diff with B0 */ +#define L1F_GAP_IPGT_SHIFT 0 + +#define L1F_STAD0 0x1488 +#define L1F_STAD1 0x148C + +#define L1F_HASH_TBL0 0x1490 +#define L1F_HASH_TBL1 0x1494 + +#define L1F_HALFD 0x1498 +#define L1F_HALFD_JAM_IPG_MASK ASHFT24(0xFUL) +#define L1F_HALFD_JAM_IPG_SHIFT 24 +#define L1F_HALFD_ABEBT_MASK ASHFT20(0xFUL) +#define L1F_HALFD_ABEBT_SHIFT 20 +#define L1F_HALFD_ABEBE BIT(19) +#define L1F_HALFD_BPNB BIT(18) +#define L1F_HALFD_NOBO BIT(17) +#define L1F_HALFD_EDXSDFR BIT(16) +#define L1F_HALFD_RETRY_MASK ASHFT12(0xFUL) +#define L1F_HALFD_RETRY_SHIFT 12 +#define L1F_HALFD_LCOL_MASK ASHFT0(0x3FFUL) +#define L1F_HALFD_LCOL_SHIFT 0 + +#define L1F_MTU 0x149C +#define L1F_MTU_JUMBO_TH 1514 +#define L1F_MTU_STD_ALGN 1536 +#define L1F_MTU_MIN 64 + +#define L1F_SRAM0 0x1500 +#define L1F_SRAM_RFD_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1F_SRAM_RFD_TAIL_ADDR_SHIFT 16 +#define L1F_SRAM_RFD_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1F_SRAM_RFD_HEAD_ADDR_SHIFT 0 + +#define L1F_SRAM1 0x1510 +#define L1F_SRAM_RFD_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1F_SRAM_RFD_LEN_SHIFT 0 + +#define L1F_SRAM2 0x1518 +#define L1F_SRAM_TRD_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1F_SRAM_TRD_TAIL_ADDR_SHIFT 16 +#define L1F_SRMA_TRD_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1F_SRAM_TRD_HEAD_ADDR_SHIFT 0 + +#define L1F_SRAM3 0x151C +#define L1F_SRAM_TRD_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1F_SRAM_TRD_LEN_SHIFT 0 + +#define L1F_SRAM4 0x1520 +#define L1F_SRAM_RXF_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1F_SRAM_RXF_TAIL_ADDR_SHIFT 16 +#define L1F_SRAM_RXF_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1F_SRAM_RXF_HEAD_ADDR_SHIFT 0 + +#define L1F_SRAM5 0x1524 +#define L1F_SRAM_RXF_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1F_SRAM_RXF_LEN_SHIFT 0 +#define L1F_SRAM_RXF_LEN_8K (8*1024) + +#define L1F_SRAM6 0x1528 +#define L1F_SRAM_TXF_TAIL_ADDR_MASK ASHFT16(0xFFFUL) +#define L1F_SRAM_TXF_TAIL_ADDR_SHIFT 16 +#define L1F_SRAM_TXF_HEAD_ADDR_MASK ASHFT0(0xFFFUL) +#define L1F_SRAM_TXF_HEAD_ADDR_SHIFT 0 + +#define L1F_SRAM7 0x152C +#define L1F_SRAM_TXF_LEN_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1F_SRAM_TXF_LEN_SHIFT 0 + +#define L1F_SRAM8 0x1530 +#define L1F_SRAM_PATTERN_ADDR_MASK ASHFT16(0xFFFUL) +#define L1F_SRAM_PATTERN_ADDR_SHIFT 16 +#define L1F_SRAM_TSO_ADDR_MASK ASHFT0(0xFFFUL) +#define L1F_SRAM_TSO_ADDR_SHIFT 0 + +#define L1F_SRAM9 0x1534 +#define L1F_SRAM_LOAD_PTR BIT(0) + +#define L1F_RX_BASE_ADDR_HI 0x1540 + +#define L1F_TX_BASE_ADDR_HI 0x1544 + +#define L1F_RFD_ADDR_LO 0x1550 +#define L1F_RFD_RING_SZ 0x1560 +#define L1F_RFD_BUF_SZ 0x1564 +#define L1F_RFD_BUF_SZ_MASK ASHFT0(0xFFFFUL) +#define L1F_RFD_BUF_SZ_SHIFT 0 + +#define L1F_RRD_ADDR_LO 0x1568 +#define L1F_RRD_RING_SZ 0x1578 +#define L1F_RRD_RING_SZ_MASK ASHFT0(0xFFFUL) +#define L1F_RRD_RING_SZ_SHIFT 0 + +#define L1F_TPD_PRI3_ADDR_LO 0x14E4 /* HIGHEST PRIORITY */ +#define L1F_TPD_PRI2_ADDR_LO 0x14E0 +#define L1F_TPD_PRI1_ADDR_LO 0x157C +#define L1F_TPD_PRI0_ADDR_LO 0x1580 /* LOWEST PRORITY */ + +#define L1F_TPD_PRI3_PIDX 0x1618 /* 16BIT */ +#define L1F_TPD_PRI2_PIDX 0x161A /* 16BIT */ +#define L1F_TPD_PRI1_PIDX 0x15F0 /* 16BIT */ +#define L1F_TPD_PRI0_PIDX 0x15F2 /* 16BIT */ + +#define L1F_TPD_PRI3_CIDX 0x161C /* 16BIT */ +#define L1F_TPD_PRI2_CIDX 0x161E /* 16BIT */ +#define L1F_TPD_PRI1_CIDX 0x15F4 /* 16BIT */ +#define L1F_TPD_PRI0_CIDX 0x15F6 /* 16BIT */ + +#define L1F_TPD_RING_SZ 0x1584 +#define L1F_TPD_RING_SZ_MASK ASHFT0(0xFFFFUL) +#define L1F_TPD_RING_SZ_SHIFT 0 + +#define L1F_CMB_ADDR_LO 0x1588 /* NOT USED */ + +#define L1F_TXQ0 0x1590 +#define L1F_TXQ0_TXF_BURST_PREF_MASK ASHFT16(0xFFFFUL) +#define L1F_TXQ0_TXF_BURST_PREF_SHIFT 16 +#define L1F_TXQ_TXF_BURST_PREF_DEF 0x200 +#define L1F_TXQ0_PEDING_CLR BIT(8) +#define L1F_TXQ0_LSO_8023_EN BIT(7) +#define L1F_TXQ0_MODE_ENHANCE BIT(6) +#define L1F_TXQ0_EN BIT(5) +#define L1F_TXQ0_SUPT_IPOPT BIT(4) +#define L1F_TXQ0_TPD_BURSTPREF_MASK ASHFT0(0xFUL) +#define L1F_TXQ0_TPD_BURSTPREF_SHIFT 0 +#define L1F_TXQ_TPD_BURSTPREF_DEF 5 + +#define L1F_TXQ1 0x1594 +#define L1F_TXQ1_ERRLGPKT_DROP_EN BIT(11) /* drop error large + * (>rfd buf) packet */ +#define L1F_TXQ1_JUMBO_TSOTHR_MASK ASHFT0(0x7FFUL) /* 8BYTES UNIT */ +#define L1F_TXQ1_JUMBO_TSOTHR_SHIFT 0 +#define L1F_TXQ1_JUMBO_TSO_TH (7*1024) /* byte */ + +#define L1F_TXQ2 0x1598 /* ENTER L1 CONTROL */ +#define L1F_TXQ2_BURST_EN BIT(31) +#define L1F_TXQ2_BURST_HI_WM_MASK ASHFT16(0xFFFUL) +#define L1F_TXQ2_BURST_HI_WM_SHIFT 16 +#define L1F_TXQ2_BURST_LO_WM_MASK ASHFT0(0xFFFUL) +#define L1F_TXQ2_BURST_LO_WM_SHIFT 0 + +#define L1F_RXQ0 0x15A0 +#define L1F_RXQ0_EN BIT(31) +#define L1F_RXQ0_CUT_THRU_EN BIT(30) +#define L1F_RXQ0_RSS_HASH_EN BIT(29) +#define L1F_RXQ0_NON_IP_QTBL BIT(28) /* 0:q0,1:table */ +#define L1F_RXQ0_RSS_MODE_MASK ASHFT26(3UL) +#define L1F_RXQ0_RSS_MODE_SHIFT 26 +#define L1F_RXQ0_RSS_MODE_DIS 0 +#define L1F_RXQ0_RSS_MODE_SQSI 1 +#define L1F_RXQ0_RSS_MODE_MQSI 2 +#define L1F_RXQ0_RSS_MODE_MQMI 3 +#define L1F_RXQ0_NUM_RFD_PREF_MASK ASHFT20(0x3FUL) +#define L1F_RXQ0_NUM_RFD_PREF_SHIFT 20 +#define L1F_RXQ0_NUM_RFD_PREF_DEF 8 +#define L1F_RXQ0_IDT_TBL_SIZE_MASK ASHFT8(0x1FFUL) +#define L1F_RXQ0_IDT_TBL_SIZE_SHIFT 8 +#define L1F_RXQ0_IDT_TBL_SIZE_DEF 0x100 +#define L1F_RXQ0_IPV6_PARSE_EN BIT(7) +#define L1F_RXQ0_RSS_HSTYP_IPV6_TCP_EN BIT(5) +#define L1F_RXQ0_RSS_HSTYP_IPV6_EN BIT(4) +#define L1F_RXQ0_RSS_HSTYP_IPV4_TCP_EN BIT(3) +#define L1F_RXQ0_RSS_HSTYP_IPV4_EN BIT(2) +#define L1F_RXQ0_RSS_HSTYP_ALL (\ + L1F_RXQ0_RSS_HSTYP_IPV6_TCP_EN |\ + L1F_RXQ0_RSS_HSTYP_IPV4_TCP_EN |\ + L1F_RXQ0_RSS_HSTYP_IPV6_EN |\ + L1F_RXQ0_RSS_HSTYP_IPV4_EN) +#define L1F_RXQ0_ASPM_THRESH_MASK ASHFT0(3UL) +#define L1F_RXQ0_ASPM_THRESH_SHIFT 0 +#define L1F_RXQ0_ASPM_THRESH_NO 0 +#define L1F_RXQ0_ASPM_THRESH_1M 1 +#define L1F_RXQ0_ASPM_THRESH_10M 2 +#define L1F_RXQ0_ASPM_THRESH_100M 3 + +#define L1F_RXQ1 0x15A4 +#define L1F_RXQ1_JUMBO_LKAH_MASK ASHFT12(0xFUL) /* 32BYTES UNIT */ +#define L1F_RXQ1_JUMBO_LKAH_SHIFT 12 +#define L1F_RXQ1_RFD_PREF_DOWN_MASK ASHFT6(0x3FUL) +#define L1F_RXQ1_RFD_PREF_DOWN_SHIFT 6 +#define L1F_RXQ1_RFD_PREF_UP_MASK ASHFT0(0x3FUL) +#define L1F_RXQ1_RFD_PREF_UP_SHIFT 0 + +#define L1F_RXQ2 0x15A8 +/* XOFF: USED SRAM LOWER THAN IT, THEN NOTIFY THE PEER TO SEND AGAIN */ +#define L1F_RXQ2_RXF_XOFF_THRESH_MASK ASHFT16(0xFFFUL) +#define L1F_RXQ2_RXF_XOFF_THRESH_SHIFT 16 +#define L1F_RXQ2_RXF_XON_THRESH_MASK ASHFT0(0xFFFUL) +#define L1F_RXQ2_RXF_XON_THRESH_SHIFT 0 + +#define L1F_RXQ3 0x15AC +#define L1F_RXQ3_RXD_TIMER_MASK ASHFT16(0x7FFFUL) +#define L1F_RXQ3_RXD_TIMER_SHIFT 16 +#define L1F_RXQ3_RXD_THRESH_MASK ASHFT0(0xFFFUL) /* 8BYTES UNIT */ +#define L1F_RXQ3_RXD_THRESH_SHIFT 0 + +#define L1F_DMA 0x15C0 +#define L1F_DMA_SMB_NOW BIT(31) +#define L1F_DMA_WPEND_CLR BIT(30) +#define L1F_DMA_RPEND_CLR BIT(29) +#define L1F_DMA_WSRAM_RDCTRL BIT(28) +#define L1F_DMA_RCHNL_SEL_MASK ASHFT26(3UL) +#define L1F_DMA_RCHNL_SEL_SHIFT 26 +#define L1F_DMA_RCHNL_SEL_1 0 +#define L1F_DMA_RCHNL_SEL_2 1 +#define L1F_DMA_RCHNL_SEL_3 2 +#define L1F_DMA_RCHNL_SEL_4 3 +#define L1F_DMA_SMB_EN BIT(21) /* smb dma enable */ +#define L1F_DMA_WDLY_CNT_MASK ASHFT16(0xFUL) +#define L1F_DMA_WDLY_CNT_SHIFT 16 +#define L1F_DMA_WDLY_CNT_DEF 4 +#define L1F_DMA_RDLY_CNT_MASK ASHFT11(0x1FUL) +#define L1F_DMA_RDLY_CNT_SHIFT 11 +#define L1F_DMA_RDLY_CNT_DEF 15 +#define L1F_DMA_RREQ_PRI_DATA BIT(10) /* 0:tpd, 1:data */ +#define L1F_DMA_WREQ_BLEN_MASK ASHFT7(7UL) +#define L1F_DMA_WREQ_BLEN_SHIFT 7 +#define L1F_DMA_RREQ_BLEN_MASK ASHFT4(7UL) +#define L1F_DMA_RREQ_BLEN_SHIFT 4 +#define L1F_DMA_PENDING_AUTO_RST BIT(3) +#define L1F_DMA_RORDER_MODE_MASK ASHFT0(7UL) +#define L1F_DMA_RORDER_MODE_SHIFT 0 +#define L1F_DMA_RORDER_MODE_OUT 4 +#define L1F_DMA_RORDER_MODE_ENHANCE 2 +#define L1F_DMA_RORDER_MODE_IN 1 + +#define L1F_WOL0 0x14A0 +#define L1F_WOL0_PT7_MATCH BIT(31) +#define L1F_WOL0_PT6_MATCH BIT(30) +#define L1F_WOL0_PT5_MATCH BIT(29) +#define L1F_WOL0_PT4_MATCH BIT(28) +#define L1F_WOL0_PT3_MATCH BIT(27) +#define L1F_WOL0_PT2_MATCH BIT(26) +#define L1F_WOL0_PT1_MATCH BIT(25) +#define L1F_WOL0_PT0_MATCH BIT(24) +#define L1F_WOL0_PT7_EN BIT(23) +#define L1F_WOL0_PT6_EN BIT(22) +#define L1F_WOL0_PT5_EN BIT(21) +#define L1F_WOL0_PT4_EN BIT(20) +#define L1F_WOL0_PT3_EN BIT(19) +#define L1F_WOL0_PT2_EN BIT(18) +#define L1F_WOL0_PT1_EN BIT(17) +#define L1F_WOL0_PT0_EN BIT(16) +#define L1F_WOL0_IPV4_SYNC_EVT BIT(14) +#define L1F_WOL0_IPV6_SYNC_EVT BIT(13) +#define L1F_WOL0_LINK_EVT BIT(10) +#define L1F_WOL0_MAGIC_EVT BIT(9) +#define L1F_WOL0_PATTERN_EVT BIT(8) +#define L1F_WOL0_OOB_EN BIT(6) +#define L1F_WOL0_PME_LINK BIT(5) +#define L1F_WOL0_LINK_EN BIT(4) +#define L1F_WOL0_PME_MAGIC_EN BIT(3) +#define L1F_WOL0_MAGIC_EN BIT(2) +#define L1F_WOL0_PME_PATTERN_EN BIT(1) +#define L1F_WOL0_PATTERN_EN BIT(0) + +#define L1F_WOL1 0x14A4 +#define L1F_WOL1_PT3_LEN_MASK ASHFT24(0xFFUL) +#define L1F_WOL1_PT3_LEN_SHIFT 24 +#define L1F_WOL1_PT2_LEN_MASK ASHFT16(0xFFUL) +#define L1F_WOL1_PT2_LEN_SHIFT 16 +#define L1F_WOL1_PT1_LEN_MASK ASHFT8(0xFFUL) +#define L1F_WOL1_PT1_LEN_SHIFT 8 +#define L1F_WOL1_PT0_LEN_MASK ASHFT0(0xFFUL) +#define L1F_WOL1_PT0_LEN_SHIFT 0 + +#define L1F_WOL2 0x14A8 +#define L1F_WOL2_PT7_LEN_MASK ASHFT24(0xFFUL) +#define L1F_WOL2_PT7_LEN_SHIFT 24 +#define L1F_WOL2_PT6_LEN_MASK ASHFT16(0xFFUL) +#define L1F_WOL2_PT6_LEN_SHIFT 16 +#define L1F_WOL2_PT5_LEN_MASK ASHFT8(0xFFUL) +#define L1F_WOL2_PT5_LEN_SHIFT 8 +#define L1F_WOL2_PT4_LEN_MASK ASHFT0(0xFFUL) +#define L1F_WOL2_PT4_LEN_SHIFT 0 + +#define L1F_RFD_PIDX 0x15E0 +#define L1F_RFD_PIDX_MASK ASHFT0(0xFFFUL) +#define L1F_RFD_PIDX_SHIFT 0 + +#define L1F_RFD_CIDX 0x15F8 +#define L1F_RFD_CIDX_MASK ASHFT0(0xFFFUL) +#define L1F_RFD_CIDX_SHIFT 0 + +/* MIB */ +#define L1F_MIB_BASE 0x1700 +#define L1F_MIB_RX_OK (L1F_MIB_BASE + 0) +#define L1F_MIB_RX_BC (L1F_MIB_BASE + 4) +#define L1F_MIB_RX_MC (L1F_MIB_BASE + 8) +#define L1F_MIB_RX_PAUSE (L1F_MIB_BASE + 12) +#define L1F_MIB_RX_CTRL (L1F_MIB_BASE + 16) +#define L1F_MIB_RX_FCS (L1F_MIB_BASE + 20) +#define L1F_MIB_RX_LENERR (L1F_MIB_BASE + 24) +#define L1F_MIB_RX_BYTCNT (L1F_MIB_BASE + 28) +#define L1F_MIB_RX_RUNT (L1F_MIB_BASE + 32) +#define L1F_MIB_RX_FRAGMENT (L1F_MIB_BASE + 36) +#define L1F_MIB_RX_64B (L1F_MIB_BASE + 40) +#define L1F_MIB_RX_127B (L1F_MIB_BASE + 44) +#define L1F_MIB_RX_255B (L1F_MIB_BASE + 48) +#define L1F_MIB_RX_511B (L1F_MIB_BASE + 52) +#define L1F_MIB_RX_1023B (L1F_MIB_BASE + 56) +#define L1F_MIB_RX_1518B (L1F_MIB_BASE + 60) +#define L1F_MIB_RX_SZMAX (L1F_MIB_BASE + 64) +#define L1F_MIB_RX_OVSZ (L1F_MIB_BASE + 68) +#define L1F_MIB_RXF_OV (L1F_MIB_BASE + 72) +#define L1F_MIB_RRD_OV (L1F_MIB_BASE + 76) +#define L1F_MIB_RX_ALIGN (L1F_MIB_BASE + 80) +#define L1F_MIB_RX_BCCNT (L1F_MIB_BASE + 84) +#define L1F_MIB_RX_MCCNT (L1F_MIB_BASE + 88) +#define L1F_MIB_RX_ERRADDR (L1F_MIB_BASE + 92) +#define L1F_MIB_TX_OK (L1F_MIB_BASE + 96) +#define L1F_MIB_TX_BC (L1F_MIB_BASE + 100) +#define L1F_MIB_TX_MC (L1F_MIB_BASE + 104) +#define L1F_MIB_TX_PAUSE (L1F_MIB_BASE + 108) +#define L1F_MIB_TX_EXCDEFER (L1F_MIB_BASE + 112) +#define L1F_MIB_TX_CTRL (L1F_MIB_BASE + 116) +#define L1F_MIB_TX_DEFER (L1F_MIB_BASE + 120) +#define L1F_MIB_TX_BYTCNT (L1F_MIB_BASE + 124) +#define L1F_MIB_TX_64B (L1F_MIB_BASE + 128) +#define L1F_MIB_TX_127B (L1F_MIB_BASE + 132) +#define L1F_MIB_TX_255B (L1F_MIB_BASE + 136) +#define L1F_MIB_TX_511B (L1F_MIB_BASE + 140) +#define L1F_MIB_TX_1023B (L1F_MIB_BASE + 144) +#define L1F_MIB_TX_1518B (L1F_MIB_BASE + 148) +#define L1F_MIB_TX_SZMAX (L1F_MIB_BASE + 152) +#define L1F_MIB_TX_1COL (L1F_MIB_BASE + 156) +#define L1F_MIB_TX_2COL (L1F_MIB_BASE + 160) +#define L1F_MIB_TX_LATCOL (L1F_MIB_BASE + 164) +#define L1F_MIB_TX_ABRTCOL (L1F_MIB_BASE + 168) +#define L1F_MIB_TX_UNDRUN (L1F_MIB_BASE + 172) +#define L1F_MIB_TX_TRDBEOP (L1F_MIB_BASE + 176) +#define L1F_MIB_TX_LENERR (L1F_MIB_BASE + 180) +#define L1F_MIB_TX_TRUNC (L1F_MIB_BASE + 184) +#define L1F_MIB_TX_BCCNT (L1F_MIB_BASE + 188) +#define L1F_MIB_TX_MCCNT (L1F_MIB_BASE + 192) +#define L1F_MIB_UPDATE (L1F_MIB_BASE + 196) + +/******************************************************************************/ + +#define L1F_ISR 0x1600 +#define L1F_ISR_DIS BIT(31) +#define L1F_ISR_RX_Q7 BIT(30) +#define L1F_ISR_RX_Q6 BIT(29) +#define L1F_ISR_RX_Q5 BIT(28) +#define L1F_ISR_RX_Q4 BIT(27) +#define L1F_ISR_PCIE_LNKDOWN BIT(26) +#define L1F_ISR_PCIE_CERR BIT(25) +#define L1F_ISR_PCIE_NFERR BIT(24) +#define L1F_ISR_PCIE_FERR BIT(23) +#define L1F_ISR_PCIE_UR BIT(22) +#define L1F_ISR_MAC_TX BIT(21) +#define L1F_ISR_MAC_RX BIT(20) +#define L1F_ISR_RX_Q3 BIT(19) +#define L1F_ISR_RX_Q2 BIT(18) +#define L1F_ISR_RX_Q1 BIT(17) +#define L1F_ISR_RX_Q0 BIT(16) +#define L1F_ISR_TX_Q0 BIT(15) +#define L1F_ISR_TXQ_TO BIT(14) +#define L1F_ISR_PHY_LPW BIT(13) +#define L1F_ISR_PHY BIT(12) +#define L1F_ISR_TX_CREDIT BIT(11) +#define L1F_ISR_DMAW BIT(10) +#define L1F_ISR_DMAR BIT(9) +#define L1F_ISR_TXF_UR BIT(8) +#define L1F_ISR_TX_Q3 BIT(7) +#define L1F_ISR_TX_Q2 BIT(6) +#define L1F_ISR_TX_Q1 BIT(5) +#define L1F_ISR_RFD_UR BIT(4) +#define L1F_ISR_RXF_OV BIT(3) +#define L1F_ISR_MANU BIT(2) +#define L1F_ISR_TIMER BIT(1) +#define L1F_ISR_SMB BIT(0) + +#define L1F_IMR 0x1604 + +#define L1F_INT_RETRIG 0x1608 /* re-send deassrt/assert + * if sw no reflect */ +#define L1F_INT_RETRIG_TIMER_MASK ASHFT0(0xFFFFUL) +#define L1F_INT_RETRIG_TIMER_SHIFT 0 +#define L1F_INT_RETRIG_TO 20000 /* 40ms */ + +#define L1F_INT_DEASST_TIMER 0x1614 /* re-send deassert + * if sw no reflect */ + +#define L1F_PATTERN_MASK 0x1620 /* 128bytes, sleep state */ +#define L1F_PATTERN_MASK_LEN 128 + + +#define L1F_FLT1_SRC_IP0 0x1A00 +#define L1F_FLT1_SRC_IP1 0x1A04 +#define L1F_FLT1_SRC_IP2 0x1A08 +#define L1F_FLT1_SRC_IP3 0x1A0C +#define L1F_FLT1_DST_IP0 0x1A10 +#define L1F_FLT1_DST_IP1 0x1A14 +#define L1F_FLT1_DST_IP2 0x1A18 +#define L1F_FLT1_DST_IP3 0x1A1C +#define L1F_FLT1_PORT 0x1A20 +#define L1F_FLT1_PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_FLT1_PORT_DST_SHIFT 16 +#define L1F_FLT1_PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_FLT1_PORT_SRC_SHIFT 0 + +#define L1F_FLT2_SRC_IP0 0x1A24 +#define L1F_FLT2_SRC_IP1 0x1A28 +#define L1F_FLT2_SRC_IP2 0x1A2C +#define L1F_FLT2_SRC_IP3 0x1A30 +#define L1F_FLT2_DST_IP0 0x1A34 +#define L1F_FLT2_DST_IP1 0x1A38 +#define L1F_FLT2_DST_IP2 0x1A40 +#define L1F_FLT2_DST_IP3 0x1A44 +#define L1F_FLT2_PORT 0x1A48 +#define L1F_FLT2_PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_FLT2_PORT_DST_SHIFT 16 +#define L1F_FLT2_PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_FLT2_PORT_SRC_SHIFT 0 + +#define L1F_FLT3_SRC_IP0 0x1A4C +#define L1F_FLT3_SRC_IP1 0x1A50 +#define L1F_FLT3_SRC_IP2 0x1A54 +#define L1F_FLT3_SRC_IP3 0x1A58 +#define L1F_FLT3_DST_IP0 0x1A5C +#define L1F_FLT3_DST_IP1 0x1A60 +#define L1F_FLT3_DST_IP2 0x1A64 +#define L1F_FLT3_DST_IP3 0x1A68 +#define L1F_FLT3_PORT 0x1A6C +#define L1F_FLT3_PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_FLT3_PORT_DST_SHIFT 16 +#define L1F_FLT3_PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_FLT3_PORT_SRC_SHIFT 0 + +#define L1F_FLT4_SRC_IP0 0x1A70 +#define L1F_FLT4_SRC_IP1 0x1A74 +#define L1F_FLT4_SRC_IP2 0x1A78 +#define L1F_FLT4_SRC_IP3 0x1A7C +#define L1F_FLT4_DST_IP0 0x1A80 +#define L1F_FLT4_DST_IP1 0x1A84 +#define L1F_FLT4_DST_IP2 0x1A88 +#define L1F_FLT4_DST_IP3 0x1A8C +#define L1F_FLT4_PORT 0x1A90 +#define L1F_FLT4_PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_FLT4_PORT_DST_SHIFT 16 +#define L1F_FLT4_PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_FLT4_PORT_SRC_SHIFT 0 + +#define L1F_FLT5_SRC_IP0 0x1A94 +#define L1F_FLT5_SRC_IP1 0x1A98 +#define L1F_FLT5_SRC_IP2 0x1A9C +#define L1F_FLT5_SRC_IP3 0x1AA0 +#define L1F_FLT5_DST_IP0 0x1AA4 +#define L1F_FLT5_DST_IP1 0x1AA8 +#define L1F_FLT5_DST_IP2 0x1AAC +#define L1F_FLT5_DST_IP3 0x1AB0 +#define L1F_FLT5_PORT 0x1AB4 +#define L1F_FLT5_PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_FLT5_PORT_DST_SHIFT 16 +#define L1F_FLT5_PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_FLT5_PORT_SRC_SHIFT 0 + +#define L1F_FLT6_SRC_IP0 0x1AB8 +#define L1F_FLT6_SRC_IP1 0x1ABC +#define L1F_FLT6_SRC_IP2 0x1AC0 +#define L1F_FLT6_SRC_IP3 0x1AC8 +#define L1F_FLT6_DST_IP0 0x1620 /* only S0 state */ +#define L1F_FLT6_DST_IP1 0x1624 +#define L1F_FLT6_DST_IP2 0x1628 +#define L1F_FLT6_DST_IP3 0x162C +#define L1F_FLT6_PORT 0x1630 +#define L1F_FLT6_PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_FLT6_PORT_DST_SHIFT 16 +#define L1F_FLT6_PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_FLT6_PORT_SRC_SHIFT 0 + +#define L1F_FLTCTRL 0x1634 +#define L1F_FLTCTRL_PSTHR_TIMER_MASK ASHFT24(0xFFUL) +#define L1F_FLTCTRL_PSTHR_TIMER_SHIFT 24 +#define L1F_FLTCTRL_CHK_DSTPRT6 BIT(23) +#define L1F_FLTCTRL_CHK_SRCPRT6 BIT(22) +#define L1F_FLTCTRL_CHK_DSTIP6 BIT(21) +#define L1F_FLTCTRL_CHK_SRCIP6 BIT(20) +#define L1F_FLTCTRL_CHK_DSTPRT5 BIT(19) +#define L1F_FLTCTRL_CHK_SRCPRT5 BIT(18) +#define L1F_FLTCTRL_CHK_DSTIP5 BIT(17) +#define L1F_FLTCTRL_CHK_SRCIP5 BIT(16) +#define L1F_FLTCTRL_CHK_DSTPRT4 BIT(15) +#define L1F_FLTCTRL_CHK_SRCPRT4 BIT(14) +#define L1F_FLTCTRL_CHK_DSTIP4 BIT(13) +#define L1F_FLTCTRL_CHK_SRCIP4 BIT(12) +#define L1F_FLTCTRL_CHK_DSTPRT3 BIT(11) +#define L1F_FLTCTRL_CHK_SRCPRT3 BIT(10) +#define L1F_FLTCTRL_CHK_DSTIP3 BIT(9) +#define L1F_FLTCTRL_CHK_SRCIP3 BIT(8) +#define L1F_FLTCTRL_CHK_DSTPRT2 BIT(7) +#define L1F_FLTCTRL_CHK_SRCPRT2 BIT(6) +#define L1F_FLTCTRL_CHK_DSTIP2 BIT(5) +#define L1F_FLTCTRL_CHK_SRCIP2 BIT(4) +#define L1F_FLTCTRL_CHK_DSTPRT1 BIT(3) +#define L1F_FLTCTRL_CHK_SRCPRT1 BIT(2) +#define L1F_FLTCTRL_CHK_DSTIP1 BIT(1) +#define L1F_FLTCTRL_CHK_SRCIP1 BIT(0) + +#define L1F_DROP_ALG1 0x1638 +#define L1F_DROP_ALG1_BWCHGVAL_MASK ASHFT12(0xFFFFFUL) +#define L1F_DROP_ALG1_BWCHGVAL_SHIFT 12 +#define L1F_DROP_ALG1_BWCHGSCL_6 BIT(11) /* 0:3.125%, 1:6.25% */ +#define L1F_DROP_ALG1_ASUR_LWQ_EN BIT(10) +#define L1F_DROP_ALG1_BWCHGVAL_EN BIT(9) +#define L1F_DROP_ALG1_BWCHGSCL_EN BIT(8) +#define L1F_DROP_ALG1_PSTHR_AUTO BIT(7) /* 0:manual, 1:auto */ +#define L1F_DROP_ALG1_MIN_PSTHR_MASK ASHFT5(3UL) +#define L1F_DROP_ALG1_MIN_PSTHR_SHIFT 5 +#define L1F_DROP_ALG1_MIN_PSTHR_1_16 0 +#define L1F_DROP_ALG1_MIN_PSTHR_1_8 1 +#define L1F_DROP_ALG1_MIN_PSTHR_1_4 2 +#define L1F_DROP_ALG1_MIN_PSTHR_1_2 3 +#define L1F_DROP_ALG1_PSCL_MASK ASHFT3(3UL) +#define L1F_DROP_ALG1_PSCL_SHIFT 3 +#define L1F_DROP_ALG1_PSCL_1_4 0 +#define L1F_DROP_ALG1_PSCL_1_8 1 +#define L1F_DROP_ALG1_PSCL_1_16 2 +#define L1F_DROP_ALG1_PSCL_1_32 3 +#define L1F_DROP_ALG1_TIMESLOT_MASK ASHFT0(7UL) +#define L1F_DROP_ALG1_TIMESLOT_SHIFT 0 +#define L1F_DROP_ALG1_TIMESLOT_4MS 0 +#define L1F_DROP_ALG1_TIMESLOT_8MS 1 +#define L1F_DROP_ALG1_TIMESLOT_16MS 2 +#define L1F_DROP_ALG1_TIMESLOT_32MS 3 +#define L1F_DROP_ALG1_TIMESLOT_64MS 4 +#define L1F_DROP_ALG1_TIMESLOT_128MS 5 +#define L1F_DROP_ALG1_TIMESLOT_256MS 6 +#define L1F_DROP_ALG1_TIMESLOT_512MS 7 + +#define L1F_DROP_ALG2 0x163C +#define L1F_DROP_ALG2_SMPLTIME_MASK ASHFT24(0xFUL) +#define L1F_DROP_ALG2_SMPLTIME_SHIFT 24 +#define L1F_DROP_ALG2_LWQBW_MASK ASHFT0(0xFFFFFFUL) +#define L1F_DROP_ALG2_LWQBW_SHIFT 0 + +#define L1F_SMB_TIMER 0x15C4 + +#define L1F_TINT_TPD_THRSHLD 0x15C8 + +#define L1F_TINT_TIMER 0x15CC + +#define L1F_CLK_GATE 0x1814 +#define L1F_CLK_GATE_125M_SW_DIS_CR BIT(8) /* B0 */ +#define L1F_CLK_GATE_125M_SW_AZ BIT(7) /* B0 */ +#define L1F_CLK_GATE_125M_SW_IDLE BIT(6) /* B0 */ +#define L1F_CLK_GATE_RXMAC BIT(5) +#define L1F_CLK_GATE_TXMAC BIT(4) +#define L1F_CLK_GATE_RXQ BIT(3) +#define L1F_CLK_GATE_TXQ BIT(2) +#define L1F_CLK_GATE_DMAR BIT(1) +#define L1F_CLK_GATE_DMAW BIT(0) +#define L1F_CLK_GATE_ALL_A0 (\ + L1F_CLK_GATE_RXMAC |\ + L1F_CLK_GATE_TXMAC |\ + L1F_CLK_GATE_RXQ |\ + L1F_CLK_GATE_TXQ |\ + L1F_CLK_GATE_DMAR |\ + L1F_CLK_GATE_DMAW) +#define L1F_CLK_GATE_ALL_B0 (\ + L1F_CLK_GATE_ALL_A0 |\ + L1F_CLK_GATE_125M_SW_AZ |\ + L1F_CLK_GATE_125M_SW_IDLE) + + + + + +#define L1F_BTROM_CFG 0x1800 /* pwon rst */ + +#define L1F_DRV 0x1804 +/* bit definition is in lx_hwcomm.h */ + +#define L1F_DRV_ERR1 0x1808 /* perst */ +#define L1F_DRV_ERR1_GEN BIT(31) /* geneneral err */ +#define L1F_DRV_ERR1_NOR BIT(30) /* rrd.nor */ +#define L1F_DRV_ERR1_TRUNC BIT(29) +#define L1F_DRV_ERR1_RES BIT(28) +#define L1F_DRV_ERR1_INTFATAL BIT(27) +#define L1F_DRV_ERR1_TXQPEND BIT(26) +#define L1F_DRV_ERR1_DMAW BIT(25) +#define L1F_DRV_ERR1_DMAR BIT(24) +#define L1F_DRV_ERR1_PCIELNKDWN BIT(23) +#define L1F_DRV_ERR1_PKTSIZE BIT(22) +#define L1F_DRV_ERR1_FIFOFUL BIT(21) +#define L1F_DRV_ERR1_RFDUR BIT(20) +#define L1F_DRV_ERR1_RRDSI BIT(19) +#define L1F_DRV_ERR1_UPDATE BIT(18) + +#define L1F_DRV_ERR2 0x180C + +#define L1F_DBG_ADDR 0x1900 /* DWORD reg */ +#define L1F_DBG_DATA 0x1904 /* DWORD reg */ + +#define L1F_SYNC_IPV4_SA 0x1A00 +#define L1F_SYNC_IPV4_DA 0x1A04 + +#define L1F_SYNC_V4PORT 0x1A08 +#define L1F_SYNC_V4PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_SYNC_V4PORT_DST_SHIFT 16 +#define L1F_SYNC_V4PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_SYNC_V4PORT_SRC_SHIFT 0 + +#define L1F_SYNC_IPV6_SA0 0x1A0C +#define L1F_SYNC_IPV6_SA1 0x1A10 +#define L1F_SYNC_IPV6_SA2 0x1A14 +#define L1F_SYNC_IPV6_SA3 0x1A18 +#define L1F_SYNC_IPV6_DA0 0x1A1C +#define L1F_SYNC_IPV6_DA1 0x1A20 +#define L1F_SYNC_IPV6_DA2 0x1A24 +#define L1F_SYNC_IPV6_DA3 0x1A28 + +#define L1F_SYNC_V6PORT 0x1A2C +#define L1F_SYNC_V6PORT_DST_MASK ASHFT16(0xFFFFUL) +#define L1F_SYNC_V6PORT_DST_SHIFT 16 +#define L1F_SYNC_V6PORT_SRC_MASK ASHFT0(0xFFFFUL) +#define L1F_SYNC_V6PORT_SRC_SHIFT 0 + +#define L1F_ARP_REMOTE_IPV4 0x1A30 +#define L1F_ARP_HOST_IPV4 0x1A34 +#define L1F_ARP_MAC0 0x1A38 +#define L1F_ARP_MAC1 0x1A3C + +#define L1F_1ST_REMOTE_IPV6_0 0x1A40 +#define L1F_1ST_REMOTE_IPV6_1 0x1A44 +#define L1F_1ST_REMOTE_IPV6_2 0x1A48 +#define L1F_1ST_REMOTE_IPV6_3 0x1A4C + +#define L1F_1ST_SN_IPV6_0 0x1A50 +#define L1F_1ST_SN_IPV6_1 0x1A54 +#define L1F_1ST_SN_IPV6_2 0x1A58 +#define L1F_1ST_SN_IPV6_3 0x1A5C + +#define L1F_1ST_TAR_IPV6_1_0 0x1A60 +#define L1F_1ST_TAR_IPV6_1_1 0x1A64 +#define L1F_1ST_TAR_IPV6_1_2 0x1A68 +#define L1F_1ST_TAR_IPV6_1_3 0x1A6C +#define L1F_1ST_TAR_IPV6_2_0 0x1A70 +#define L1F_1ST_TAR_IPV6_2_1 0x1A74 +#define L1F_1ST_TAR_IPV6_2_2 0x1A78 +#define L1F_1ST_TAR_IPV6_2_3 0x1A7C + +#define L1F_2ND_REMOTE_IPV6_0 0x1A80 +#define L1F_2ND_REMOTE_IPV6_1 0x1A84 +#define L1F_2ND_REMOTE_IPV6_2 0x1A88 +#define L1F_2ND_REMOTE_IPV6_3 0x1A8C + +#define L1F_2ND_SN_IPV6_0 0x1A90 +#define L1F_2ND_SN_IPV6_1 0x1A94 +#define L1F_2ND_SN_IPV6_2 0x1A98 +#define L1F_2ND_SN_IPV6_3 0x1A9C + +#define L1F_2ND_TAR_IPV6_1_0 0x1AA0 +#define L1F_2ND_TAR_IPV6_1_1 0x1AA4 +#define L1F_2ND_TAR_IPV6_1_2 0x1AA8 +#define L1F_2ND_TAR_IPV6_1_3 0x1AAC +#define L1F_2ND_TAR_IPV6_2_0 0x1AB0 +#define L1F_2ND_TAR_IPV6_2_1 0x1AB4 +#define L1F_2ND_TAR_IPV6_2_2 0x1AB8 +#define L1F_2ND_TAR_IPV6_2_3 0x1ABC + +#define L1F_1ST_NS_MAC0 0x1AC0 +#define L1F_1ST_NS_MAC1 0x1AC4 + +#define L1F_2ND_NS_MAC0 0x1AC8 +#define L1F_2ND_NS_MAC1 0x1ACC + +#define L1F_PMOFLD 0x144C +#define L1F_PMOFLD_ECMA_IGNR_FRG_SSSR BIT(11) /* B0 */ +#define L1F_PMOFLD_ARP_CNFLCT_WAKEUP BIT(10) /* B0 */ +#define L1F_PMOFLD_MULTI_SOLD BIT(9) +#define L1F_PMOFLD_ICMP_XSUM BIT(8) +#define L1F_PMOFLD_GARP_REPLY BIT(7) +#define L1F_PMOFLD_SYNCV6_ANY BIT(6) +#define L1F_PMOFLD_SYNCV4_ANY BIT(5) +#define L1F_PMOFLD_BY_HW BIT(4) +#define L1F_PMOFLD_NS_EN BIT(3) +#define L1F_PMOFLD_ARP_EN BIT(2) +#define L1F_PMOFLD_SYNCV6_EN BIT(1) +#define L1F_PMOFLD_SYNCV4_EN BIT(0) + +#define L1F_RSS_KEY0 0x14B0 +#define L1F_RSS_KEY1 0x14B4 +#define L1F_RSS_KEY2 0x14B8 +#define L1F_RSS_KEY3 0x14BC +#define L1F_RSS_KEY4 0x14C0 +#define L1F_RSS_KEY5 0x14C4 +#define L1F_RSS_KEY6 0x14C8 +#define L1F_RSS_KEY7 0x14CC +#define L1F_RSS_KEY8 0x14D0 +#define L1F_RSS_KEY9 0x14D4 + +#define L1F_RSS_IDT_TBL0 0x1B00 +#define L1F_RSS_IDT_TBL1 0x1B04 +#define L1F_RSS_IDT_TBL2 0x1B08 +#define L1F_RSS_IDT_TBL3 0x1B0C +#define L1F_RSS_IDT_TBL4 0x1B10 +#define L1F_RSS_IDT_TBL5 0x1B14 +#define L1F_RSS_IDT_TBL6 0x1B18 +#define L1F_RSS_IDT_TBL7 0x1B1C +#define L1F_RSS_IDT_TBL8 0x1B20 +#define L1F_RSS_IDT_TBL9 0x1B24 +#define L1F_RSS_IDT_TBL10 0x1B28 +#define L1F_RSS_IDT_TBL11 0x1B2C +#define L1F_RSS_IDT_TBL12 0x1B30 +#define L1F_RSS_IDT_TBL13 0x1B34 +#define L1F_RSS_IDT_TBL14 0x1B38 +#define L1F_RSS_IDT_TBL15 0x1B3C +#define L1F_RSS_IDT_TBL16 0x1B40 +#define L1F_RSS_IDT_TBL17 0x1B44 +#define L1F_RSS_IDT_TBL18 0x1B48 +#define L1F_RSS_IDT_TBL19 0x1B4C +#define L1F_RSS_IDT_TBL20 0x1B50 +#define L1F_RSS_IDT_TBL21 0x1B54 +#define L1F_RSS_IDT_TBL22 0x1B58 +#define L1F_RSS_IDT_TBL23 0x1B5C +#define L1F_RSS_IDT_TBL24 0x1B60 +#define L1F_RSS_IDT_TBL25 0x1B64 +#define L1F_RSS_IDT_TBL26 0x1B68 +#define L1F_RSS_IDT_TBL27 0x1B6C +#define L1F_RSS_IDT_TBL28 0x1B70 +#define L1F_RSS_IDT_TBL29 0x1B74 +#define L1F_RSS_IDT_TBL30 0x1B78 +#define L1F_RSS_IDT_TBL31 0x1B7C + +#define L1F_RSS_HASH_VAL 0x15B0 +#define L1F_RSS_HASH_FLAG 0x15B4 + +#define L1F_RSS_BASE_CPU_NUM 0x15B8 + +#define L1F_MSI_MAP_TBL1 0x15D0 +#define L1F_MSI_MAP_TBL1_ALERT_MASK ASHFT28(0xFUL) +#define L1F_MSI_MAP_TBL1_ALERT_SHIFT 28 +#define L1F_MSI_MAP_TBL1_TIMER_MASK ASHFT24(0xFUL) +#define L1F_MSI_MAP_TBL1_TIMER_SHIFT 24 +#define L1F_MSI_MAP_TBL1_TXQ1_MASK ASHFT20(0xFUL) +#define L1F_MSI_MAP_TBL1_TXQ1_SHIFT 20 +#define L1F_MSI_MAP_TBL1_TXQ0_MASK ASHFT16(0xFUL) +#define L1F_MSI_MAP_TBL1_TXQ0_SHIFT 16 +#define L1F_MSI_MAP_TBL1_RXQ3_MASK ASHFT12(0xFUL) +#define L1F_MSI_MAP_TBL1_RXQ3_SHIFT 12 +#define L1F_MSI_MAP_TBL1_RXQ2_MASK ASHFT8(0xFUL) +#define L1F_MSI_MAP_TBL1_RXQ2_SHIFT 8 +#define L1F_MSI_MAP_TBL1_RXQ1_MASK ASHFT4(0xFUL) +#define L1F_MSI_MAP_TBL1_RXQ1_SHIFT 4 +#define L1F_MSI_MAP_TBL1_RXQ0_MASK ASHFT0(0xFUL) +#define L1F_MSI_MAP_TBL1_RXQ0_SHIFT 0 + +#define L1F_MSI_MAP_TBL2 0x15D8 +#define L1F_MSI_MAP_TBL2_PHY_MASK ASHFT28(0xFUL) +#define L1F_MSI_MAP_TBL2_PHY_SHIFT 28 +#define L1F_MSI_MAP_TBL2_SMB_MASK ASHFT24(0xFUL) +#define L1F_MSI_MAP_TBL2_SMB_SHIFT 24 +#define L1F_MSI_MAP_TBL2_TXQ3_MASK ASHFT20(0xFUL) +#define L1F_MSI_MAP_TBL2_TXQ3_SHIFT 20 +#define L1F_MSI_MAP_TBL2_TXQ2_MASK ASHFT16(0xFUL) +#define L1F_MSI_MAP_TBL2_TXQ2_SHIFT 16 +#define L1F_MSI_MAP_TBL2_RXQ7_MASK ASHFT12(0xFUL) +#define L1F_MSI_MAP_TBL2_RXQ7_SHIFT 12 +#define L1F_MSI_MAP_TBL2_RXQ6_MASK ASHFT8(0xFUL) +#define L1F_MSI_MAP_TBL2_RXQ6_SHIFT 8 +#define L1F_MSI_MAP_TBL2_RXQ5_MASK ASHFT4(0xFUL) +#define L1F_MSI_MAP_TBL2_RXQ5_SHIFT 4 +#define L1F_MSI_MAP_TBL2_RXQ4_MASK ASHFT0(0xFUL) +#define L1F_MSI_MAP_TBL2_RXQ4_SHIFT 0 + +#define L1F_MSI_ID_MAP 0x15D4 +#define L1F_MSI_ID_MAP_RXQ7 BIT(30) +#define L1F_MSI_ID_MAP_RXQ6 BIT(29) +#define L1F_MSI_ID_MAP_RXQ5 BIT(28) +#define L1F_MSI_ID_MAP_RXQ4 BIT(27) +#define L1F_MSI_ID_MAP_PCIELNKDW BIT(26) /* 0:common,1:timer */ +#define L1F_MSI_ID_MAP_PCIECERR BIT(25) +#define L1F_MSI_ID_MAP_PCIENFERR BIT(24) +#define L1F_MSI_ID_MAP_PCIEFERR BIT(23) +#define L1F_MSI_ID_MAP_PCIEUR BIT(22) +#define L1F_MSI_ID_MAP_MACTX BIT(21) +#define L1F_MSI_ID_MAP_MACRX BIT(20) +#define L1F_MSI_ID_MAP_RXQ3 BIT(19) +#define L1F_MSI_ID_MAP_RXQ2 BIT(18) +#define L1F_MSI_ID_MAP_RXQ1 BIT(17) +#define L1F_MSI_ID_MAP_RXQ0 BIT(16) +#define L1F_MSI_ID_MAP_TXQ0 BIT(15) +#define L1F_MSI_ID_MAP_TXQTO BIT(14) +#define L1F_MSI_ID_MAP_LPW BIT(13) +#define L1F_MSI_ID_MAP_PHY BIT(12) +#define L1F_MSI_ID_MAP_TXCREDIT BIT(11) +#define L1F_MSI_ID_MAP_DMAW BIT(10) +#define L1F_MSI_ID_MAP_DMAR BIT(9) +#define L1F_MSI_ID_MAP_TXFUR BIT(8) +#define L1F_MSI_ID_MAP_TXQ3 BIT(7) +#define L1F_MSI_ID_MAP_TXQ2 BIT(6) +#define L1F_MSI_ID_MAP_TXQ1 BIT(5) +#define L1F_MSI_ID_MAP_RFDUR BIT(4) +#define L1F_MSI_ID_MAP_RXFOV BIT(3) +#define L1F_MSI_ID_MAP_MANU BIT(2) +#define L1F_MSI_ID_MAP_TIMER BIT(1) +#define L1F_MSI_ID_MAP_SMB BIT(0) + +#define L1F_MSI_RETRANS_TIMER 0x1920 +#define L1F_MSI_MASK_SEL_LINE BIT(16) /* 1:line,0:standard*/ +#define L1F_MSI_RETRANS_TM_MASK ASHFT0(0xFFFFUL) +#define L1F_MSI_RETRANS_TM_SHIFT 0 + +#define L1F_CR_DMA_CTRL 0x1930 +#define L1F_CR_DMA_CTRL_PRI BIT(22) +#define L1F_CR_DMA_CTRL_RRDRXD_JOINT BIT(21) +#define L1F_CR_DMA_CTRL_BWCREDIT_MASK ASHFT19(0x3UL) +#define L1F_CR_DMA_CTRL_BWCREDIT_SHIFT 19 +#define L1F_CR_DMA_CTRL_BWCREDIT_2KB 0 +#define L1F_CR_DMA_CTRL_BWCREDIT_1KB 1 +#define L1F_CR_DMA_CTRL_BWCREDIT_4KB 2 +#define L1F_CR_DMA_CTRL_BWCREDIT_8KB 3 +#define L1F_CR_DMA_CTRL_BW_EN BIT(18) +#define L1F_CR_DMA_CTRL_BW_RATIO_MASK ASHFT16(0x3UL) +#define L1F_CR_DMA_CTRL_BW_RATIO_1_2 0 +#define L1F_CR_DMA_CTRL_BW_RATIO_1_4 1 +#define L1F_CR_DMA_CTRL_BW_RATIO_1_8 2 +#define L1F_CR_DMA_CTRL_BW_RATIO_2_1 3 +#define L1F_CR_DMA_CTRL_SOFT_RST BIT(11) +#define L1F_CR_DMA_CTRL_TXEARLY_EN BIT(10) +#define L1F_CR_DMA_CTRL_RXEARLY_EN BIT(9) +#define L1F_CR_DMA_CTRL_WEARLY_EN BIT(8) +#define L1F_CR_DMA_CTRL_RXTH_MASK ASHFT4(0xFUL) +#define L1F_CR_DMA_CTRL_WTH_MASK ASHFT0(0xFUL) + + +#define L1F_EFUSE_BIST 0x1934 +#define L1F_EFUSE_BIST_COL_MASK ASHFT24(0x3FUL) +#define L1F_EFUSE_BIST_COL_SHIFT 24 +#define L1F_EFUSE_BIST_ROW_MASK ASHFT12(0x7FUL) +#define L1F_EFUSE_BIST_ROW_SHIFT 12 +#define L1F_EFUSE_BIST_STEP_MASK ASHFT8(0xFUL) +#define L1F_EFUSE_BIST_STEP_SHIFT 8 +#define L1F_EFUSE_BIST_PAT_MASK ASHFT4(0x7UL) +#define L1F_EFUSE_BIST_PAT_SHIFT 4 +#define L1F_EFUSE_BIST_CRITICAL BIT(3) +#define L1F_EFUSE_BIST_FIXED BIT(2) +#define L1F_EFUSE_BIST_FAIL BIT(1) +#define L1F_EFUSE_BIST_NOW BIT(0) + +/* CR DMA ctrl */ + +/* TX QoS */ +#define L1F_WRR 0x1938 +#define L1F_WRR_PRI_MASK ASHFT29(3UL) +#define L1F_WRR_PRI_SHIFT 29 +#define L1F_WRR_PRI_RESTRICT_ALL 0 +#define L1F_WRR_PRI_RESTRICT_HI 1 +#define L1F_WRR_PRI_RESTRICT_HI2 2 +#define L1F_WRR_PRI_RESTRICT_NONE 3 +#define L1F_WRR_PRI3_MASK ASHFT24(0x1FUL) +#define L1F_WRR_PRI3_SHIFT 24 +#define L1F_WRR_PRI2_MASK ASHFT16(0x1FUL) +#define L1F_WRR_PRI2_SHIFT 16 +#define L1F_WRR_PRI1_MASK ASHFT8(0x1FUL) +#define L1F_WRR_PRI1_SHIFT 8 +#define L1F_WRR_PRI0_MASK ASHFT0(0x1FUL) +#define L1F_WRR_PRI0_SHIFT 0 + +#define L1F_HQTPD 0x193C +#define L1F_HQTPD_BURST_EN BIT(31) +#define L1F_HQTPD_Q3_NUMPREF_MASK ASHFT8(0xFUL) +#define L1F_HQTPD_Q3_NUMPREF_SHIFT 8 +#define L1F_HQTPD_Q2_NUMPREF_MASK ASHFT4(0xFUL) +#define L1F_HQTPD_Q2_NUMPREF_SHIFT 4 +#define L1F_HQTPD_Q1_NUMPREF_MASK ASHFT0(0xFUL) +#define L1F_HQTPD_Q1_NUMPREF_SHIFT 0 + +#define L1F_CPUMAP1 0x19A0 +#define L1F_CPUMAP1_VCT7_MASK ASHFT28(0xFUL) +#define L1F_CPUMAP1_VCT7_SHIFT 28 +#define L1F_CPUMAP1_VCT6_MASK ASHFT24(0xFUL) +#define L1F_CPUMAP1_VCT6_SHIFT 24 +#define L1F_CPUMAP1_VCT5_MASK ASHFT20(0xFUL) +#define L1F_CPUMAP1_VCT5_SHIFT 20 +#define L1F_CPUMAP1_VCT4_MASK ASHFT16(0xFUL) +#define L1F_CPUMAP1_VCT4_SHIFT 16 +#define L1F_CPUMAP1_VCT3_MASK ASHFT12(0xFUL) +#define L1F_CPUMAP1_VCT3_SHIFT 12 +#define L1F_CPUMAP1_VCT2_MASK ASHFT8(0xFUL) +#define L1F_CPUMAP1_VCT2_SHIFT 8 +#define L1F_CPUMAP1_VCT1_MASK ASHFT4(0xFUL) +#define L1F_CPUMAP1_VCT1_SHIFT 4 +#define L1F_CPUMAP1_VCT0_MASK ASHFT0(0xFUL) +#define L1F_CPUMAP1_VCT0_SHIFT 0 + +#define L1F_CPUMAP2 0x19A4 +#define L1F_CPUMAP2_VCT15_MASK ASHFT28(0xFUL) +#define L1F_CPUMAP2_VCT15_SHIFT 28 +#define L1F_CPUMAP2_VCT14_MASK ASHFT24(0xFUL) +#define L1F_CPUMAP2_VCT14_SHIFT 24 +#define L1F_CPUMAP2_VCT13_MASK ASHFT20(0xFUL) +#define L1F_CPUMAP2_VCT13_SHIFT 20 +#define L1F_CPUMAP2_VCT12_MASK ASHFT16(0xFUL) +#define L1F_CPUMAP2_VCT12_SHIFT 16 +#define L1F_CPUMAP2_VCT11_MASK ASHFT12(0xFUL) +#define L1F_CPUMAP2_VCT11_SHIFT 12 +#define L1F_CPUMAP2_VCT10_MASK ASHFT8(0xFUL) +#define L1F_CPUMAP2_VCT10_SHIFT 8 +#define L1F_CPUMAP2_VCT9_MASK ASHFT4(0xFUL) +#define L1F_CPUMAP2_VCT9_SHIFT 4 +#define L1F_CPUMAP2_VCT8_MASK ASHFT0(0xFUL) +#define L1F_CPUMAP2_VCT8_SHIFT 0 + +#define L1F_MISC 0x19C0 +#define L1F_MISC_MODU BIT(31) /* 0:vector,1:cpu */ +#define L1F_MISC_OVERCUR BIT(29) +#define L1F_MISC_PSWR_EN BIT(28) +#define L1F_MISC_PSW_CTRL_MASK ASHFT24(0xFUL) +#define L1F_MISC_PSW_CTRL_SHIFT 24 +#define L1F_MISC_PSW_OCP_MASK ASHFT21(7UL) +#define L1F_MISC_PSW_OCP_SHIFT 21 +#define L1F_MISC_V18_HIGH BIT(20) +#define L1F_MISC_LPO_CTRL_MASK ASHFT16(0xFUL) +#define L1F_MISC_LPO_CTRL_SHIFT 16 +#define L1F_MISC_ISO_EN BIT(12) +#define L1F_MISC_XSTANA_ALWAYS_ON BIT(11) +#define L1F_MISC_SYS25M_SEL_ADAPTIVE BIT(10) +#define L1F_MISC_SPEED_SIM BIT(9) +#define L1F_MISC_S1_LWP_EN BIT(8) +#define L1F_MISC_MACLPW BIT(7) /* pcie/mac do pwsaving + * as phy in lpw state */ +#define L1F_MISC_125M_SW BIT(6) +#define L1F_MISC_INTNLOSC_OFF_EN BIT(5) +#define L1F_MISC_EXTN25M_SEL BIT(4) /* 0:chipset,1:cystle */ +#define L1F_MISC_INTNLOSC_OPEN BIT(3) +#define L1F_MISC_SMBUS_AT_LED BIT(2) +#define L1F_MISC_PPS_AT_LED_MASK ASHFT0(3UL) +#define L1F_MISC_PPS_AT_LED_SHIFT 0 +#define L1F_MISC_PPS_AT_LED_ACT 1 +#define L1F_MISC_PPS_AT_LED_10_100 2 +#define L1F_MISC_PPS_AT_LED_1000 3 + +#define L1F_MISC1 0x19C4 +#define L1F_MSC1_BLK_CRASPM_REQ BIT(15) + +#define L1F_MISC3 0x19CC +#define L1F_MISC3_25M_BY_SW BIT(1) /* 1:Software control 25M */ +#define L1F_MISC3_25M_NOTO_INTNL BIT(0) /* 0:25M switch to intnl OSC */ + + + +/***************************** IO mapping registers ***************************/ +#define L1F_IO_ADDR 0x00 /* DWORD reg */ +#define L1F_IO_DATA 0x04 /* DWORD reg */ +#define L1F_IO_MASTER 0x08 /* DWORD same as reg0x1400 */ +#define L1F_IO_MAC_CTRL 0x0C /* DWORD same as reg0x1480*/ +#define L1F_IO_ISR 0x10 /* DWORD same as reg0x1600 */ +#define L1F_IO_IMR 0x14 /* DWORD same as reg0x1604 */ +#define L1F_IO_TPD_PRI1_PIDX 0x18 /* WORD same as reg0x15F0 */ +#define L1F_IO_TPD_PRI0_PIDX 0x1A /* WORD same as reg0x15F2 */ +#define L1F_IO_TPD_PRI1_CIDX 0x1C /* WORD same as reg0x15F4 */ +#define L1F_IO_TPD_PRI0_CIDX 0x1E /* WORD same as reg0x15F6 */ +#define L1F_IO_RFD_PIDX 0x20 /* WORD same as reg0x15E0 */ +#define L1F_IO_RFD_CIDX 0x30 /* WORD same as reg0x15F8 */ +#define L1F_IO_MDIO 0x38 /* WORD same as reg0x1414 */ +#define L1F_IO_PHY_CTRL 0x3C /* DWORD same as reg0x140C */ + + +/********************* PHY regs definition ***************************/ + +/* Autoneg Advertisement Register */ +#define L1F_ADVERTISE_SPEED_MASK 0x01E0 +#define L1F_ADVERTISE_DEFAULT_CAP 0x1DE0 /* diff with L1C */ + +/* 1000BASE-T Control Register (0x9) */ +#define L1F_GIGA_CR_1000T_HD_CAPS 0x0100 +#define L1F_GIGA_CR_1000T_FD_CAPS 0x0200 +#define L1F_GIGA_CR_1000T_REPEATER_DTE 0x0400 + +#define L1F_GIGA_CR_1000T_MS_VALUE 0x0800 + +#define L1F_GIGA_CR_1000T_MS_ENABLE 0x1000 + +#define L1F_GIGA_CR_1000T_TEST_MODE_NORMAL 0x0000 +#define L1F_GIGA_CR_1000T_TEST_MODE_1 0x2000 +#define L1F_GIGA_CR_1000T_TEST_MODE_2 0x4000 +#define L1F_GIGA_CR_1000T_TEST_MODE_3 0x6000 +#define L1F_GIGA_CR_1000T_TEST_MODE_4 0x8000 +#define L1F_GIGA_CR_1000T_SPEED_MASK 0x0300 +#define L1F_GIGA_CR_1000T_DEFAULT_CAP 0x0300 + +/* 1000BASE-T Status Register */ +#define L1F_MII_GIGA_SR 0x0A + +/* PHY Specific Status Register */ +#define L1F_MII_GIGA_PSSR 0x11 +#define L1F_GIGA_PSSR_FC_RXEN 0x0004 +#define L1F_GIGA_PSSR_FC_TXEN 0x0008 +#define L1F_GIGA_PSSR_SPD_DPLX_RESOLVED 0x0800 +#define L1F_GIGA_PSSR_DPLX 0x2000 +#define L1F_GIGA_PSSR_SPEED 0xC000 +#define L1F_GIGA_PSSR_10MBS 0x0000 +#define L1F_GIGA_PSSR_100MBS 0x4000 +#define L1F_GIGA_PSSR_1000MBS 0x8000 + +/* PHY Interrupt Enable Register */ +#define L1F_MII_IER 0x12 +#define L1F_IER_LINK_UP 0x0400 +#define L1F_IER_LINK_DOWN 0x0800 + +/* PHY Interrupt Status Register */ +#define L1F_MII_ISR 0x13 +#define L1F_ISR_LINK_UP 0x0400 +#define L1F_ISR_LINK_DOWN 0x0800 + +/* Cable-Detect-Test Control Register */ +#define L1F_MII_CDTC 0x16 +#define L1F_CDTC_EN 1 /* sc */ +#define L1F_CDTC_PAIR_MASK ASHFT8(3U) +#define L1F_CDTC_PAIR_SHIFT 8 + + +/* Cable-Detect-Test Status Register */ +#define L1F_MII_CDTS 0x1C +#define L1F_CDTS_STATUS_MASK ASHFT8(3U) +#define L1F_CDTS_STATUS_SHIFT 8 +#define L1F_CDTS_STATUS_NORMAL 0 +#define L1F_CDTS_STATUS_SHORT 1 +#define L1F_CDTS_STATUS_OPEN 2 +#define L1F_CDTS_STATUS_INVALID 3 + +#define L1F_MII_DBG_ADDR 0x1D +#define L1F_MII_DBG_DATA 0x1E + +/***************************** debug port *************************************/ + +#define L1F_MIIDBG_ANACTRL 0x00 +#define L1F_ANACTRL_CLK125M_DELAY_EN BIT(15) +#define L1F_ANACTRL_VCO_FAST BIT(14) +#define L1F_ANACTRL_VCO_SLOW BIT(13) +#define L1F_ANACTRL_AFE_MODE_EN BIT(12) +#define L1F_ANACTRL_LCKDET_PHY BIT(11) +#define L1F_ANACTRL_LCKDET_EN BIT(10) +#define L1F_ANACTRL_OEN_125M BIT(9) +#define L1F_ANACTRL_HBIAS_EN BIT(8) +#define L1F_ANACTRL_HB_EN BIT(7) +#define L1F_ANACTRL_SEL_HSP BIT(6) +#define L1F_ANACTRL_CLASSA_EN BIT(5) +#define L1F_ANACTRL_MANUSWON_SWR_MASK ASHFT2(3U) +#define L1F_ANACTRL_MANUSWON_SWR_SHIFT 2 +#define L1F_ANACTRL_MANUSWON_SWR_2V 0 +#define L1F_ANACTRL_MANUSWON_SWR_1P9V 1 +#define L1F_ANACTRL_MANUSWON_SWR_1P8V 2 +#define L1F_ANACTRL_MANUSWON_SWR_1P7V 3 +#define L1F_ANACTRL_MANUSWON_BW3_4M BIT(1) +#define L1F_ANACTRL_RESTART_CAL BIT(0) +#define L1F_ANACTRL_DEF 0x02EF + + +#define L1F_MIIDBG_SYSMODCTRL 0x04 +#define L1F_SYSMODCTRL_IECHOADJ_PFMH_PHY BIT(15) +#define L1F_SYSMODCTRL_IECHOADJ_BIASGEN BIT(14) +#define L1F_SYSMODCTRL_IECHOADJ_PFML_PHY BIT(13) +#define L1F_SYSMODCTRL_IECHOADJ_PS_MASK ASHFT10(3U) +#define L1F_SYSMODCTRL_IECHOADJ_PS_SHIFT 10 +#define L1F_SYSMODCTRL_IECHOADJ_PS_40 3 +#define L1F_SYSMODCTRL_IECHOADJ_PS_20 2 +#define L1F_SYSMODCTRL_IECHOADJ_PS_0 1 +#define L1F_SYSMODCTRL_IECHOADJ_10BT_100MV BIT(6) /* 1:100mv, 0:200mv */ +#define L1F_SYSMODCTRL_IECHOADJ_HLFAP_MASK ASHFT4(3U) +#define L1F_SYSMODCTRL_IECHOADJ_HLFAP_SHIFT 4 +#define L1F_SYSMODCTRL_IECHOADJ_VDFULBW BIT(3) +#define L1F_SYSMODCTRL_IECHOADJ_VDBIASHLF BIT(2) +#define L1F_SYSMODCTRL_IECHOADJ_VDAMPHLF BIT(1) +#define L1F_SYSMODCTRL_IECHOADJ_VDLANSW BIT(0) +#define L1F_SYSMODCTRL_IECHOADJ_DEF 0xBB8B /* en half bias */ + + +#define L1F_MIIDBG_SRDSYSMOD 0x05 +#define L1F_SRDSYSMOD_LCKDET_EN BIT(13) +#define L1F_SRDSYSMOD_PLL_EN BIT(11) +#define L1F_SRDSYSMOD_SEL_HSP BIT(10) +#define L1F_SRDSYSMOD_HLFTXDR BIT(9) +#define L1F_SRDSYSMOD_TXCLK_DELAY_EN BIT(8) +#define L1F_SRDSYSMOD_TXELECIDLE BIT(7) +#define L1F_SRDSYSMOD_DEEMP_EN BIT(6) +#define L1F_SRDSYSMOD_MS_PAD BIT(2) +#define L1F_SRDSYSMOD_CDR_ADC_VLTG BIT(1) +#define L1F_SRDSYSMOD_CDR_DAC_1MA BIT(0) +#define L1F_SRDSYSMOD_DEF 0x2C46 + + +#define L1F_MIIDBG_HIBNEG 0x0B +#define L1F_HIBNEG_PSHIB_EN BIT(15) +#define L1F_HIBNEG_WAKE_BOTH BIT(14) +#define L1F_HIBNEG_ONOFF_ANACHG_SUDEN BIT(13) +#define L1F_HIBNEG_HIB_PULSE BIT(12) +#define L1F_HIBNEG_GATE_25M_EN BIT(11) +#define L1F_HIBNEG_RST_80U BIT(10) +#define L1F_HIBNEG_RST_TIMER_MASK ASHFT8(3U) +#define L1F_HIBNEG_RST_TIMER_SHIFT 8 +#define L1F_HIBNEG_GTX_CLK_DELAY_MASK ASHFT5(3U) +#define L1F_HIBNEG_GTX_CLK_DELAY_SHIFT 5 +#define L1F_HIBNEG_BYPSS_BRKTIMER BIT(4) +#define L1F_HIBNEG_DEF 0xBC40 + +#define L1F_MIIDBG_TST10BTCFG 0x12 +#define L1F_TST10BTCFG_INTV_TIMER_MASK ASHFT14(3U) +#define L1F_TST10BTCFG_INTV_TIMER_SHIFT 14 +#define L1F_TST10BTCFG_TRIGER_TIMER_MASK ASHFT12(3U) +#define L1F_TST10BTCFG_TRIGER_TIMER_SHIFT 12 +#define L1F_TST10BTCFG_DIV_MAN_MLT3_EN BIT(11) +#define L1F_TST10BTCFG_OFF_DAC_IDLE BIT(10) +#define L1F_TST10BTCFG_LPBK_DEEP BIT(2) /* 1:deep,0:shallow */ +#define L1F_TST10BTCFG_DEF 0x4C04 + +#define L1F_MIIDBG_AZ_ANADECT 0x15 +#define L1F_AZ_ANADECT_10BTRX_TH BIT(15) +#define L1F_AZ_ANADECT_BOTH_01CHNL BIT(14) +#define L1F_AZ_ANADECT_INTV_MASK ASHFT8(0x3FU) +#define L1F_AZ_ANADECT_INTV_SHIFT 8 +#define L1F_AZ_ANADECT_THRESH_MASK ASHFT4(0xFU) +#define L1F_AZ_ANADECT_THRESH_SHIFT 4 +#define L1F_AZ_ANADECT_CHNL_MASK ASHFT0(0xFU) +#define L1F_AZ_ANADECT_CHNL_SHIFT 0 +#define L1F_AZ_ANADECT_DEF 0x3220 +#define L1F_AZ_ANADECT_LONG 0x3210 + +#define L1F_MIIDBG_AGC 0x23 +#define L1F_AGC_2_VGA_MASK ASHFT8(0x3FU) +#define L1F_AGC_2_VGA_SHIFT 8 +#define L1F_AGC_LONG1G_LIMT 40 +#define L1F_AGC_LONG100M_LIMT 44 + +#define L1F_MIIDBG_LEGCYPS 0x29 +#define L1F_LEGCYPS_EN BIT(15) +#define L1F_LEGCYPS_DAC_AMP1000_MASK ASHFT12(7U) +#define L1F_LEGCYPS_DAC_AMP1000_SHIFT 12 +#define L1F_LEGCYPS_DAC_AMP100_MASK ASHFT9(7U) +#define L1F_LEGCYPS_DAC_AMP100_SHIFT 9 +#define L1F_LEGCYPS_DAC_AMP10_MASK ASHFT6(7U) +#define L1F_LEGCYPS_DAC_AMP10_SHIFT 6 +#define L1F_LEGCYPS_UNPLUG_TIMER_MASK ASHFT3(7U) +#define L1F_LEGCYPS_UNPLUG_TIMER_SHIFT 3 +#define L1F_LEGCYPS_UNPLUG_DECT_EN BIT(2) +#define L1F_LEGCYPS_ECNC_PS_EN BIT(0) +#define L1F_LEGCYPS_DEF 0x129D + +#define L1F_MIIDBG_TST100BTCFG 0x36 +#define L1F_TST100BTCFG_NORMAL_BW_EN BIT(15) +#define L1F_TST100BTCFG_BADLNK_BYPASS BIT(14) +#define L1F_TST100BTCFG_SHORTCABL_TH_MASK ASHFT8(0x3FU) +#define L1F_TST100BTCFG_SHORTCABL_TH_SHIFT 8 +#define L1F_TST100BTCFG_LITCH_EN BIT(7) +#define L1F_TST100BTCFG_VLT_SW BIT(6) +#define L1F_TST100BTCFG_LONGCABL_TH_MASK ASHFT0(0x3FU) +#define L1F_TST100BTCFG_LONGCABL_TH_SHIFT 0 +#define L1F_TST100BTCFG_DEF 0xE12C + +#define L1F_MIIDBG_GREENCFG 0x3B +#define L1F_GREENCFG_MSTPS_MSETH2_MASK ASHFT8(0xFFU) +#define L1F_GREENCFG_MSTPS_MSETH2_SHIFT 8 +#define L1F_GREENCFG_MSTPS_MSETH1_MASK ASHFT0(0xFFU) +#define L1F_GREENCFG_MSTPS_MSETH1_SHIFT 0 +#define L1F_GREENCFG_DEF 0x7078 + +#define L1F_MIIDBG_GREENCFG2 0x3D +#define L1F_GREENCFG2_GATE_DFSE_EN BIT(7) + + +/***************************** extension **************************************/ + +/******* dev 3 *********/ +#define L1F_MIIEXT_PCS 3 + +#define L1F_MIIEXT_CLDCTRL6 0x8006 +#define L1F_CLDCTRL6_CAB_LEN_MASK ASHFT0(0xFFU) +#define L1F_CLDCTRL6_CAB_LEN_SHIFT 0 +#define L1F_CLDCTRL6_CAB_LEN_SHORT1G 116 +#define L1F_CLDCTRL6_CAB_LEN_SHORT100M 152 + +#define L1F_MIIEXT_CLDCTRL7 0x8007 +#define L1F_CLDCTRL7_VDHLF_BIAS_TH_MASK ASHFT9(0x7FU) +#define L1F_CLDCTRL7_VDHLF_BIAS_TH_SHIFT 9 +#define L1F_CLDCTRL7_AFE_AZ_MASK ASHFT4(0x1FU) +#define L1F_CLDCTRL7_AFE_AZ_SHIFT 4 +#define L1F_CLDCTRL7_SIDE_PEAK_TH_MASK ASHFT0(0xFU) +#define L1F_CLDCTRL7_SIDE_PEAK_TH_SHIFT 0 +#define L1F_CLDCTRL7_DEF 0x6BF6 /* ???? */ + +#define L1F_MIIEXT_AZCTRL 0x8008 +#define L1F_AZCTRL_SHORT_TH_MASK ASHFT8(0xFFU) +#define L1F_AZCTRL_SHORT_TH_SHIFT 8 +#define L1F_AZCTRL_LONG_TH_MASK ASHFT0(0xFFU) +#define L1F_AZCTRL_LONG_TH_SHIFT 0 +#define L1F_AZCTRL_DEF 0x1629 + +#define L1F_MIIEXT_AZCTRL2 0x8009 +#define L1F_AZCTRL2_WAKETRNING_MASK ASHFT8(0xFFU) +#define L1F_AZCTRL2_WAKETRNING_SHIFT 8 +#define L1F_AZCTRL2_QUIET_TIMER_MASH ASHFT6(3U) +#define L1F_AZCTRL2_QUIET_TIMER_SHIFT 6 +#define L1F_AZCTRL2_PHAS_JMP2 BIT(4) +#define L1F_AZCTRL2_CLKTRCV_125MD16 BIT(3) +#define L1F_AZCTRL2_GATE1000_EN BIT(2) +#define L1F_AZCTRL2_AVRG_FREQ BIT(1) +#define L1F_AZCTRL2_PHAS_JMP4 BIT(0) +#define L1F_AZCTRL2_DEF 0x32C0 + +#define L1F_MIIEXT_AZCTRL6 0x800D + +#define L1F_MIIEXT_VDRVBIAS 0x8062 +#define L1F_VDRVBIAS_SEL_MASK ASHFT0(0x3U) +#define L1F_VDRVBIAS_SEL_SHIFT 0 +#define L1F_VDRVBIAS_DEF 0x3 + +/********* dev 7 **********/ +#define L1F_MIIEXT_ANEG 7 + +#define L1F_MIIEXT_LOCAL_EEEADV 0x3C +#define L1F_LOCAL_EEEADV_1000BT BIT(2) +#define L1F_LOCAL_EEEADV_100BT BIT(1) + +#define L1F_MIIEXT_REMOTE_EEEADV 0x3D +#define L1F_REMOTE_EEEADV_1000BT BIT(2) +#define L1F_REMOTE_EEEADV_100BT BIT(1) + +#define L1F_MIIEXT_EEE_ANEG 0x8000 +#define L1F_EEE_ANEG_1000M BIT(2) +#define L1F_EEE_ANEG_100M BIT(1) + +#define L1F_MIIEXT_AFE 0x801A +#define L1F_AFE_10BT_100M_TH BIT(6) + + +#define L1F_MIIEXT_NLP34 0x8025 +#define L1F_MIIEXT_NLP34_DEF 0x1010 /* for 160m */ + +#define L1F_MIIEXT_NLP56 0x8026 +#define L1F_MIIEXT_NLP56_DEF 0x1010 /* for 160m */ + +#define L1F_MIIEXT_NLP78 0x8027 +#define L1F_MIIEXT_NLP78_160M_DEF 0x8D05 /* for 160m */ +#define L1F_MIIEXT_NLP78_120M_DEF 0x8A05 /* for 120m */ + + + +/******************************************************************************/ + +/* functions */ + + +/* get permanent mac address from + * return + * 0: success + * non-0:fail + */ +u16 l1f_get_perm_macaddr(struct alx_hw *hw, u8 *addr); + + +/* reset mac & dma + * return + * 0: success + * non-0:fail + */ +u16 l1f_reset_mac(struct alx_hw *hw); + +/* reset phy + * return + * 0: success + * non-0:fail + */ +u16 l1f_reset_phy(struct alx_hw *hw, bool pws_en, bool az_en, bool ptp_en); + + +/* reset pcie + * just reset pcie relative registers (pci command, clk, aspm...) + * return + * 0:success + * non-0:fail + */ +u16 l1f_reset_pcie(struct alx_hw *hw, bool l0s_en, bool l1_en); + + +/* disable/enable MAC/RXQ/TXQ + * en + * true:enable + * false:disable + * return + * 0:success + * non-0-fail + */ +u16 l1f_enable_mac(struct alx_hw *hw, bool en, u16 en_ctrl); + + +/* enable/disable aspm support + * that will change settings for phy/mac/pcie + */ +u16 l1f_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en, u8 lnk_stat); + + +/* initialize phy for speed / flow control + * lnk_cap + * if autoNeg, is link capability to tell the peer + * if force mode, is forced speed/duplex + */ +u16 l1f_init_phy_spdfc(struct alx_hw *hw, bool auto_neg, + u8 lnk_cap, bool fc_en); + +/* do post setting on phy if link up/down event occur + */ +u16 l1f_post_phy_link(struct alx_hw *hw, bool linkon, u8 wire_spd); + + +/* do power saving setting befor enter suspend mode + * NOTE: + * 1. phy link must be established before calling this function + * 2. wol option (pattern,magic,link,etc.) is configed before call it. + */ +u16 l1f_powersaving(struct alx_hw *hw, u8 wire_spd, bool wol_en, + bool mahw_en, bool macrx_en, bool pws_en); + +/* read phy register */ +u16 l1f_read_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, u16 reg, + u16 *data); + +/* write phy register */ +u16 l1f_write_phy(struct alx_hw *hw, bool ext, u8 dev, bool fast, u16 reg, + u16 data); + +/* phy debug port */ +u16 l1f_read_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 *data); +u16 l1f_write_phydbg(struct alx_hw *hw, bool fast, u16 reg, u16 data); + + +/* check the configuration of the PHY */ +u16 l1f_get_phy_config(struct alx_hw *hw); + +/* + * initialize mac basically + * most of hi-feature no init + * MAC/PHY should be reset before call this function + */ +u16 l1f_init_mac(struct alx_hw *hw, u8 *addr, u32 txmem_hi, + u32 *tx_mem_lo, u8 tx_qnum, u16 txring_sz, + u32 rxmem_hi, u32 rfdmem_lo, u32 rrdmem_lo, + u16 rxring_sz, u16 rxbuf_sz, u16 smb_timer, + u16 mtu, u16 int_mod, bool hash_legacy); + + + +#endif/*L1F_HW_H_*/ + --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alx.h @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ALX_H_ +#define _ALX_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alx_sw.h" + +/* + * Definition to enable some features + */ +#undef CONFIG_ALX_MSIX +#undef CONFIG_ALX_MSI +#undef CONFIG_ALX_MTQ +#undef CONFIG_ALX_MRQ +#undef CONFIG_ALX_RSS +/* #define CONFIG_ALX_MSIX */ +#define CONFIG_ALX_MSI +#define CONFIG_ALX_MTQ +#define CONFIG_ALX_MRQ +#ifdef CONFIG_ALX_MRQ +#define CONFIG_ALX_RSS +#endif + +#define ALX_MSG_DEFAULT 0 + +/* Logging functions and macros */ +#define alx_err(adpt, fmt, ...) \ + netdev_err(adpt->netdev, fmt, ##__VA_ARGS__) + +#define ALX_VLAN_TO_TAG(_vlan, _tag) \ + do { \ + _tag = ((((_vlan) >> 8) & 0xFF) | (((_vlan) & 0xFF) << 8)); \ + } while (0) + +#define ALX_TAG_TO_VLAN(_tag, _vlan) \ + do { \ + _vlan = ((((_tag) >> 8) & 0xFF) | (((_tag) & 0xFF) << 8)) ; \ + } while (0) + +/* Coalescing Message Block */ +struct coals_msg_block { + int test; +}; + + +#define BAR_0 0 + +#define ALX_DEF_RX_BUF_SIZE 1536 +#define ALX_MAX_JUMBO_PKT_SIZE (9*1024) +#define ALX_MAX_TSO_PKT_SIZE (7*1024) + +#define ALX_MAX_ETH_FRAME_SIZE ALX_MAX_JUMBO_PKT_SIZE +#define ALX_MIN_ETH_FRAME_SIZE 68 + + +#define ALX_MAX_RX_QUEUES 8 +#define ALX_MAX_TX_QUEUES 4 +#define ALX_MAX_HANDLED_INTRS 5 + +#define ALX_WATCHDOG_TIME (5 * HZ) + +struct alx_cmb { + char name[IFNAMSIZ + 9]; + void *cmb; + dma_addr_t dma; +}; +struct alx_smb { + char name[IFNAMSIZ + 9]; + void *smb; + dma_addr_t dma; +}; + + +/* + * RRD : definition + */ + +/* general parameter format of rrd */ +struct alx_rrdes_general { + u32 xsum:16; + u32 nor:4; /* number of RFD */ + u32 si:12; /* start index of rfd-ring */ + + u32 hash; + + /* dword 3 */ + u32 vlan_tag:16; /* vlan-tag */ + u32 pid:8; /* Header Length of Header-Data Split. WORD unit */ + u32 reserve0:1; + u32 rss_cpu:3; /* CPU number used by RSS */ + u32 rss_flag:4; /* rss_flag 0, TCP(IPv6) flag for RSS hash algrithm + * rss_flag 1, IPv6 flag for RSS hash algrithm + * rss_flag 2, TCP(IPv4) flag for RSS hash algrithm + * rss_flag 3, IPv4 flag for RSS hash algrithm + */ + + /* dword 3 */ + u32 pkt_len:14; /* length of the packet */ + u32 l4f:1; /* L4(TCP/UDP) checksum failed */ + u32 ipf:1; /* IP checksum failed */ + u32 vlan_flag:1;/* vlan tag */ + u32 reserve:3; + u32 res:1; /* received error summary */ + u32 crc:1; /* crc error */ + u32 fae:1; /* frame alignment error */ + u32 trunc:1; /* truncated packet, larger than MTU */ + u32 runt:1; /* runt packet */ + u32 icmp:1; /* incomplete packet, + * due to insufficient rx-descriptor + */ + u32 bar:1; /* broadcast address received */ + u32 mar:1; /* multicast address received */ + u32 type:1; /* ethernet type */ + u32 fov:1; /* fifo overflow*/ + u32 lene:1; /* length error */ + u32 update:1; /* update*/ +}; + +union alx_rrdesc { + /* dword flat format */ + struct { + __le32 dw0; + __le32 dw1; + __le32 dw2; + __le32 dw3; + } dfmt; + + /* qword flat format */ + struct { + __le64 qw0; + __le64 qw1; + } qfmt; +}; + +/* + * XXX: we should not use this guy, best to just + * do all le32_to_cpu() conversions on the spot. + */ +union alx_sw_rrdesc { + struct alx_rrdes_general genr; + + /* dword flat format */ + struct { + u32 dw0; + u32 dw1; + u32 dw2; + u32 dw3; + } dfmt; + + /* qword flat format */ + struct { + u64 qw0; + u64 qw1; + } qfmt; +}; + +/* + * RFD : definition + */ + +/* general parameter format of rfd */ +struct alx_rfdes_general { + u64 addr; +}; + +union alx_rfdesc { + /* dword flat format */ + struct { + __le32 dw0; + __le32 dw1; + } dfmt; + + /* qword flat format */ + struct { + __le64 qw0; + } qfmt; +}; + +/* + * XXX: we should not use this guy, best to just + * do all le32_to_cpu() conversions on the spot. + */ +union alx_sw_rfdesc { + struct alx_rfdes_general genr; + + /* dword flat format */ + struct { + u32 dw0; + u32 dw1; + } dfmt; + + /* qword flat format */ + struct { + u64 qw0; + } qfmt; +}; + +/* + * TPD : definition + */ + +/* general parameter format of tpd */ +struct alx_tpdes_general { + u32 buffer_len:16; /* include 4-byte CRC */ + u32 vlan_tag:16; + + u32 l4hdr_offset:8; /* tcp/udp header offset to the 1st byte of + * the packet */ + u32 c_csum:1; /* must be 0 in this format */ + u32 ip_csum:1; /* do ip(v4) header checksum offload */ + u32 tcp_csum:1; /* do tcp checksum offload, both ipv4 and ipv6 */ + u32 udp_csum:1; /* do udp checksum offlaod, both ipv4 and ipv6 */ + u32 lso:1; + u32 lso_v2:1; /* must be 0 in this format */ + u32 vtagged:1; /* vlan-id tagged already */ + u32 instag:1; /* insert vlan tag */ + + u32 ipv4:1; /* ipv4 packet */ + u32 type:1; /* type of packet (ethernet_ii(1) or snap(0)) */ + u32 reserve:12; /* reserved, must be 0 */ + u32 epad:1; /* even byte padding when this packet */ + u32 last_frag:1; /* last fragment(buffer) of the packet */ + + u64 addr; +}; + +/* custom checksum parameter format of tpd */ +struct alx_tpdes_checksum { + u32 buffer_len:16; /* include 4-byte CRC */ + u32 vlan_tag:16; + + u32 payld_offset:8; /* payload offset to the 1st byte of + * the packet + */ + u32 c_sum:1; /* do custom chekcusm offload, + * must be 1 in this format + */ + u32 ip_sum:1; /* must be 0 in thhis format */ + u32 tcp_sum:1; /* must be 0 in this format */ + u32 udp_sum:1; /* must be 0 in this format */ + u32 lso:1; /* must be 0 in this format */ + u32 lso_v2:1; /* must be 0 in this format */ + u32 vtagged:1; /* vlan-id tagged already */ + u32 instag:1; /* insert vlan tag */ + + u32 ipv4:1; /* ipv4 packet */ + u32 type:1; /* type of packet (ethernet_ii(1) or snap(0)) */ + u32 cxsum_offset:8; /* checksum offset to the 1st byte of + * the packet + */ + u32 reserve:4; /* reserved, must be 0 */ + u32 epad:1; /* even byte padding when this packet */ + u32 last_frag:1; /* last fragment(buffer) of the packet */ + + u64 addr; +}; + + +/* tcp large send format (v1/v2) of tpd */ +struct alx_tpdes_tso { + u32 buffer_len:16; /* include 4-byte CRC */ + u32 vlan_tag:16; + + u32 tcphdr_offset:8; /* tcp hdr offset to the 1st byte of packet */ + u32 c_sum:1; /* must be 0 in this format */ + u32 ip_sum:1; /* must be 0 in thhis format */ + u32 tcp_sum:1; /* must be 0 in this format */ + u32 udp_sum:1; /* must be 0 in this format */ + u32 lso:1; /* do tcp large send (ipv4 only) */ + u32 lso_v2:1; /* must be 0 in this format */ + u32 vtagged:1; /* vlan-id tagged already */ + u32 instag:1; /* insert vlan tag */ + + u32 ipv4:1; /* ipv4 packet */ + u32 type:1; /* type of packet (ethernet_ii(1) or snap(0)) */ + u32 mss:13; /* MSS if do tcp large send */ + u32 last_frag:1; /* last fragment(buffer) of the packet */ + + u32 addr_lo; + u32 addr_hi; +}; + +union alx_tpdesc { + /* dword flat format */ + struct { + __le32 dw0; + __le32 dw1; + __le32 dw2; + __le32 dw3; + } dfmt; + + /* qword flat format */ + struct { + __le64 qw0; + __le64 qw1; + } qfmt; +}; + +/* + * XXX: we should not use this guy, best to just + * do all le32_to_cpu() conversions on the spot. + */ +union alx_sw_tpdesc { + struct alx_tpdes_general genr; + struct alx_tpdes_checksum csum; + struct alx_tpdes_tso tso; + + /* dword flat format */ + struct { + u32 dw0; + u32 dw1; + u32 dw2; + u32 dw3; + } dfmt; + + /* qword flat format */ + struct { + u64 qw0; + u64 qw1; + } qfmt; +}; + +#define ALX_RRD(_que, _i) \ + (&(((union alx_rrdesc *)(_que)->rrq.rrdesc)[(_i)])) +#define ALX_RFD(_que, _i) \ + (&(((union alx_rfdesc *)(_que)->rfq.rfdesc)[(_i)])) +#define ALX_TPD(_que, _i) \ + (&(((union alx_tpdesc *)(_que)->tpq.tpdesc)[(_i)])) + + +/* + * alx_ring_header represents a single, contiguous block of DMA space + * mapped for the three descriptor rings (tpd, rfd, rrd) and the two + * message blocks (cmb, smb) described below + */ +struct alx_ring_header { + void *desc; /* virtual address */ + dma_addr_t dma; /* physical address*/ + unsigned int size; /* length in bytes */ + unsigned int used; +}; + + +/* + * alx_buffer is wrapper around a pointer to a socket buffer + * so a DMA handle can be stored along with the skb + */ +struct alx_buffer { + struct sk_buff *skb; /* socket buffer */ + u16 length; /* rx buffer length */ + dma_addr_t dma; +}; + +struct alx_sw_buffer { + struct sk_buff *skb; /* socket buffer */ + u32 vlan_tag:16; + u32 vlan_flag:1; + u32 reserved:15; +}; + +/* receive free descriptor (rfd) queue */ +struct alx_rfd_queue { + struct alx_buffer *rfbuff; + union alx_rfdesc *rfdesc; /* virtual address */ + dma_addr_t rfdma; /* physical address */ + u16 size; /* length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 produce_idx; /* it's written to rxque->produce_reg */ + u16 consume_idx; /* unused*/ +}; + +/* receive return desciptor (rrd) queue */ +struct alx_rrd_queue { + union alx_rrdesc *rrdesc; /* virtual address */ + dma_addr_t rrdma; /* physical address */ + u16 size; /* length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 produce_idx; /* unused */ + u16 consume_idx; /* rxque->consume_reg */ +}; + +/* software desciptor (swd) queue */ +struct alx_swd_queue { + struct alx_sw_buffer *swbuff; + u16 count; /* number of descriptors in the ring */ + u16 produce_idx; + u16 consume_idx; +}; + +/* rx queue */ +struct alx_rx_queue { + struct device *dev; /* device for dma mapping */ + struct net_device *netdev; /* netdev ring belongs to */ + struct alx_msix_param *msix; + struct alx_rrd_queue rrq; + struct alx_rfd_queue rfq; + struct alx_swd_queue swq; + + u16 que_idx; /* index in multi rx queues*/ + u16 max_packets; /* max work per interrupt */ + u16 produce_reg; + u16 consume_reg; + u32 flags; +}; +#define ALX_RX_FLAG_SW_QUE 0x00000001 +#define ALX_RX_FLAG_HW_QUE 0x00000002 +#define CHK_RX_FLAG(_flag) CHK_FLAG(rxque, RX, _flag) +#define SET_RX_FLAG(_flag) SET_FLAG(rxque, RX, _flag) +#define CLI_RX_FLAG(_flag) CLI_FLAG(rxque, RX, _flag) + +#define GET_RF_BUFFER(_rque, _i) (&((_rque)->rfq.rfbuff[(_i)])) +#define GET_SW_BUFFER(_rque, _i) (&((_rque)->swq.swbuff[(_i)])) + + +/* transimit packet descriptor (tpd) ring */ +struct alx_tpd_queue { + struct alx_buffer *tpbuff; + union alx_tpdesc *tpdesc; /* virtual address */ + dma_addr_t tpdma; /* physical address */ + + u16 size; /* length in bytes */ + u16 count; /* number of descriptors in the ring */ + u16 produce_idx; + u16 consume_idx; + u16 last_produce_idx; +}; + +/* tx queue */ +struct alx_tx_queue { + struct device *dev; /* device for dma mapping */ + struct net_device *netdev; /* netdev ring belongs to */ + struct alx_tpd_queue tpq; + struct alx_msix_param *msix; + + u16 que_idx; /* needed for multiqueue queue management */ + u16 max_packets; /* max packets per interrupt */ + u16 produce_reg; + u16 consume_reg; +}; +#define GET_TP_BUFFER(_tque, _i) (&((_tque)->tpq.tpbuff[(_i)])) + + +/* + * definition for array allocations. + */ +#define ALX_MAX_MSIX_INTRS 16 +#define ALX_MAX_RX_QUEUES 8 +#define ALX_MAX_TX_QUEUES 4 + +enum alx_msix_type { + alx_msix_type_rx, + alx_msix_type_tx, + alx_msix_type_other, +}; +#define ALX_MSIX_TYPE_OTH_TIMER 0 +#define ALX_MSIX_TYPE_OTH_ALERT 1 +#define ALX_MSIX_TYPE_OTH_SMB 2 +#define ALX_MSIX_TYPE_OTH_PHY 3 + +/* ALX_MAX_MSIX_INTRS of these are allocated, + * but we only use one per queue-specific vector. + */ +struct alx_msix_param { + struct alx_adapter *adpt; + unsigned int vec_idx; /* index in HW interrupt vector */ + char name[IFNAMSIZ + 9]; + + /* msix interrupts for queue */ + u8 rx_map[ALX_MAX_RX_QUEUES]; + u8 tx_map[ALX_MAX_TX_QUEUES]; + u8 rx_count; /* Rx ring count assigned to this vector */ + u8 tx_count; /* Tx ring count assigned to this vector */ + + struct napi_struct napi; + cpumask_var_t affinity_mask; + u32 flags; +}; + +#define ALX_MSIX_FLAG_RX0 0x00000001 +#define ALX_MSIX_FLAG_RX1 0x00000002 +#define ALX_MSIX_FLAG_RX2 0x00000004 +#define ALX_MSIX_FLAG_RX3 0x00000008 +#define ALX_MSIX_FLAG_RX4 0x00000010 +#define ALX_MSIX_FLAG_RX5 0x00000020 +#define ALX_MSIX_FLAG_RX6 0x00000040 +#define ALX_MSIX_FLAG_RX7 0x00000080 +#define ALX_MSIX_FLAG_TX0 0x00000100 +#define ALX_MSIX_FLAG_TX1 0x00000200 +#define ALX_MSIX_FLAG_TX2 0x00000400 +#define ALX_MSIX_FLAG_TX3 0x00000800 +#define ALX_MSIX_FLAG_TIMER 0x00001000 +#define ALX_MSIX_FLAG_ALERT 0x00002000 +#define ALX_MSIX_FLAG_SMB 0x00004000 +#define ALX_MSIX_FLAG_PHY 0x00008000 + +#define ALX_MSIX_FLAG_RXS (\ + ALX_MSIX_FLAG_RX0 |\ + ALX_MSIX_FLAG_RX1 |\ + ALX_MSIX_FLAG_RX2 |\ + ALX_MSIX_FLAG_RX3 |\ + ALX_MSIX_FLAG_RX4 |\ + ALX_MSIX_FLAG_RX5 |\ + ALX_MSIX_FLAG_RX6 |\ + ALX_MSIX_FLAG_RX7) +#define ALX_MSIX_FLAG_TXS (\ + ALX_MSIX_FLAG_TX0 |\ + ALX_MSIX_FLAG_TX1 |\ + ALX_MSIX_FLAG_TX2 |\ + ALX_MSIX_FLAG_TX3) +#define ALX_MSIX_FLAG_ALL (\ + ALX_MSIX_FLAG_RXS |\ + ALX_MSIX_FLAG_TXS |\ + ALX_MSIX_FLAG_TIMER |\ + ALX_MSIX_FLAG_ALERT |\ + ALX_MSIX_FLAG_SMB |\ + ALX_MSIX_FLAG_PHY) + +#define CHK_MSIX_FLAG(_flag) CHK_FLAG(msix, MSIX, _flag) +#define SET_MSIX_FLAG(_flag) SET_FLAG(msix, MSIX, _flag) +#define CLI_MSIX_FLAG(_flag) CLI_FLAG(msix, MSIX, _flag) + +/* + *board specific private data structure + */ +struct alx_adapter { + struct net_device *netdev; + struct pci_dev *pdev; + struct net_device_stats net_stats; + bool netdev_registered; + u16 bd_number; /* board number;*/ + + struct alx_msix_param *msix[ALX_MAX_MSIX_INTRS]; + struct msix_entry *msix_entries; + int num_msix_rxques; + int num_msix_txques; + int num_msix_noques; /* true count of msix_noques for device */ + int num_msix_intrs; + + int min_msix_intrs; + int max_msix_intrs; + + /* All Descriptor memory */ + struct alx_ring_header ring_header; + + /* TX */ + struct alx_tx_queue *tx_queue[ALX_MAX_TX_QUEUES]; + /* RX */ + struct alx_rx_queue *rx_queue[ALX_MAX_RX_QUEUES]; + + u16 num_txques; + u16 num_rxques; /* equal max(num_hw_rxques, num_sw_rxques) */ + u16 num_hw_rxques; + u16 num_sw_rxques; + u16 max_rxques; + u16 max_txques; + + u16 num_txdescs; + u16 num_rxdescs; + + u32 rxbuf_size; + + struct alx_cmb cmb; + struct alx_smb smb; + + /* structs defined in alx_hw.h */ + struct alx_hw hw; + struct alx_hw_stats hw_stats; + + u32 *config_space; + + struct work_struct alx_task; + struct timer_list alx_timer; + + unsigned long link_jiffies; + + u32 wol; + spinlock_t tx_lock; + spinlock_t rx_lock; + atomic_t irq_sem; + + u16 msg_enable; + unsigned long flags[2]; +}; + +#define ALX_ADPT_FLAG_0_MSI_CAP 0x00000001 +#define ALX_ADPT_FLAG_0_MSI_EN 0x00000002 +#define ALX_ADPT_FLAG_0_MSIX_CAP 0x00000004 +#define ALX_ADPT_FLAG_0_MSIX_EN 0x00000008 +#define ALX_ADPT_FLAG_0_MRQ_CAP 0x00000010 +#define ALX_ADPT_FLAG_0_MRQ_EN 0x00000020 +#define ALX_ADPT_FLAG_0_MTQ_CAP 0x00000040 +#define ALX_ADPT_FLAG_0_MTQ_EN 0x00000080 +#define ALX_ADPT_FLAG_0_SRSS_CAP 0x00000100 +#define ALX_ADPT_FLAG_0_SRSS_EN 0x00000200 +#define ALX_ADPT_FLAG_0_FIXED_MSIX 0x00000400 + +#define ALX_ADPT_FLAG_0_TASK_REINIT_REQ 0x00010000 /* reinit */ +#define ALX_ADPT_FLAG_0_TASK_LSC_REQ 0x00020000 + +#define ALX_ADPT_FLAG_1_STATE_TESTING 0x00000001 +#define ALX_ADPT_FLAG_1_STATE_RESETTING 0x00000002 +#define ALX_ADPT_FLAG_1_STATE_DOWN 0x00000004 +#define ALX_ADPT_FLAG_1_STATE_WATCH_DOG 0x00000008 +#define ALX_ADPT_FLAG_1_STATE_DIAG_RUNNING 0x00000010 +#define ALX_ADPT_FLAG_1_STATE_INACTIVE 0x00000020 + + +#define CHK_ADPT_FLAG(_idx, _flag) \ + CHK_FLAG_ARRAY(adpt, _idx, ADPT, _flag) +#define SET_ADPT_FLAG(_idx, _flag) \ + SET_FLAG_ARRAY(adpt, _idx, ADPT, _flag) +#define CLI_ADPT_FLAG(_idx, _flag) \ + CLI_FLAG_ARRAY(adpt, _idx, ADPT, _flag) + +/* default to trying for four seconds */ +#define ALX_TRY_LINK_TIMEOUT (4 * HZ) + + +#define ALX_OPEN_CTRL_IRQ_EN 0x00000001 +#define ALX_OPEN_CTRL_RESET_MAC 0x00000002 +#define ALX_OPEN_CTRL_RESET_PHY 0x00000004 +#define ALX_OPEN_CTRL_RESET_ALL (\ + ALX_OPEN_CTRL_RESET_MAC |\ + ALX_OPEN_CTRL_RESET_PHY) + +/* needed by alx_ethtool.c */ +extern char alx_drv_name[]; +extern void alx_reinit_locked(struct alx_adapter *adpt); +extern void alx_set_ethtool_ops(struct net_device *netdev); +#ifdef ETHTOOL_OPS_COMPAT +extern int ethtool_ioctl(struct ifreq *ifr); +#endif + +#endif /* _ALX_H_ */ --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alx_ethtool.c @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "alx.h" +#include "alx_hwcom.h" + +#ifdef ETHTOOL_OPS_COMPAT +#include "alx_compat_ethtool.c" +#endif + + +static int alx_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + u32 link_speed = hw->link_speed; + bool link_up = hw->link_up; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP); + if (CHK_HW_FLAG(GIGA_CAP)) + ecmd->supported |= SUPPORTED_1000baseT_Full; + + ecmd->advertising = ADVERTISED_TP; + + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->advertising |= hw->autoneg_advertised; + + ecmd->port = PORT_TP; + ecmd->phy_address = 0; + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->transceiver = XCVR_INTERNAL; + + if (!in_interrupt()) { + hw->cbs.check_phy_link(hw, &link_speed, &link_up); + hw->link_speed = link_speed; + hw->link_up = link_up; + } + + if (link_up) { + switch (link_speed) { + case ALX_LINK_SPEED_10_HALF: + ethtool_cmd_speed_set(ecmd, SPEED_10); + ecmd->duplex = DUPLEX_HALF; + break; + case ALX_LINK_SPEED_10_FULL: + ethtool_cmd_speed_set(ecmd, SPEED_10); + ecmd->duplex = DUPLEX_FULL; + break; + case ALX_LINK_SPEED_100_HALF: + ethtool_cmd_speed_set(ecmd, SPEED_100); + ecmd->duplex = DUPLEX_HALF; + break; + case ALX_LINK_SPEED_100_FULL: + ethtool_cmd_speed_set(ecmd, SPEED_100); + ecmd->duplex = DUPLEX_FULL; + break; + case ALX_LINK_SPEED_1GB_FULL: + ethtool_cmd_speed_set(ecmd, SPEED_1000); + ecmd->duplex = DUPLEX_FULL; + break; + default: + ecmd->speed = -1; + ecmd->duplex = -1; + break; + } + } else { + ethtool_cmd_speed_set(ecmd, -1); + ecmd->duplex = -1; + } + + return 0; +} + + +static int alx_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + u32 advertised, old; + int error = 0; + + while (CHK_ADPT_FLAG(1, STATE_RESETTING)) + msleep(20); + SET_ADPT_FLAG(1, STATE_RESETTING); + + old = hw->autoneg_advertised; + advertised = 0; + if (ecmd->autoneg == AUTONEG_ENABLE) { + advertised = ALX_LINK_SPEED_DEFAULT; + } else { + u32 speed = ethtool_cmd_speed(ecmd); + if (speed == SPEED_1000) { + if (ecmd->duplex != DUPLEX_FULL) { + dev_warn(&adpt->pdev->dev, + "1000M half is invalid\n"); + CLI_ADPT_FLAG(1, STATE_RESETTING); + return -EINVAL; + } + advertised = ALX_LINK_SPEED_1GB_FULL; + } else if (speed == SPEED_100) { + if (ecmd->duplex == DUPLEX_FULL) + advertised = ALX_LINK_SPEED_100_FULL; + else + advertised = ALX_LINK_SPEED_100_HALF; + } else { + if (ecmd->duplex == DUPLEX_FULL) + advertised = ALX_LINK_SPEED_10_FULL; + else + advertised = ALX_LINK_SPEED_10_HALF; + } + } + + if (hw->autoneg_advertised == advertised) { + CLI_ADPT_FLAG(1, STATE_RESETTING); + return error; + } + + error = hw->cbs.setup_phy_link_speed(hw, advertised, true, + !hw->disable_fc_autoneg); + if (error) { + dev_err(&adpt->pdev->dev, + "setup link failed with code %d\n", error); + hw->cbs.setup_phy_link_speed(hw, old, true, + !hw->disable_fc_autoneg); + } + CLI_ADPT_FLAG(1, STATE_RESETTING); + return error; +} + + +static void alx_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + + + if (hw->disable_fc_autoneg || + hw->cur_fc_mode == alx_fc_none) + pause->autoneg = 0; + else + pause->autoneg = 1; + + if (hw->cur_fc_mode == alx_fc_rx_pause) { + pause->rx_pause = 1; + } else if (hw->cur_fc_mode == alx_fc_tx_pause) { + pause->tx_pause = 1; + } else if (hw->cur_fc_mode == alx_fc_full) { + pause->rx_pause = 1; + pause->tx_pause = 1; + } +} + + +static int alx_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + enum alx_fc_mode req_fc_mode; + bool disable_fc_autoneg; + int retval; + + while (CHK_ADPT_FLAG(1, STATE_RESETTING)) + msleep(20); + SET_ADPT_FLAG(1, STATE_RESETTING); + + req_fc_mode = hw->req_fc_mode; + disable_fc_autoneg = hw->disable_fc_autoneg; + + + if (pause->autoneg != AUTONEG_ENABLE) + disable_fc_autoneg = true; + else + disable_fc_autoneg = false; + + if ((pause->rx_pause && pause->tx_pause) || pause->autoneg) + req_fc_mode = alx_fc_full; + else if (pause->rx_pause && !pause->tx_pause) + req_fc_mode = alx_fc_rx_pause; + else if (!pause->rx_pause && pause->tx_pause) + req_fc_mode = alx_fc_tx_pause; + else if (!pause->rx_pause && !pause->tx_pause) + req_fc_mode = alx_fc_none; + else + return -EINVAL; + + if ((hw->req_fc_mode != req_fc_mode) || + (hw->disable_fc_autoneg != disable_fc_autoneg)) { + hw->req_fc_mode = req_fc_mode; + hw->disable_fc_autoneg = disable_fc_autoneg; + if (!hw->disable_fc_autoneg) + retval = hw->cbs.setup_phy_link(hw, + hw->autoneg_advertised, true, true); + + if (hw->cbs.config_fc) + hw->cbs.config_fc(hw); + } + + CLI_ADPT_FLAG(1, STATE_RESETTING); + return 0; +} + + +static u32 alx_get_msglevel(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + return adpt->msg_enable; +} + + +static void alx_set_msglevel(struct net_device *netdev, u32 data) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + adpt->msg_enable = data; +} + + +static int alx_get_regs_len(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + return hw->hwreg_sz * sizeof(32); +} + + +static void alx_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *buff) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + + regs->version = 0; + + memset(buff, 0, hw->hwreg_sz * sizeof(u32)); + if (hw->cbs.get_ethtool_regs) + hw->cbs.get_ethtool_regs(hw, buff); +} + + +static int alx_get_eeprom_len(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + return hw->eeprom_sz; +} + + +static int alx_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + bool eeprom_exist = false; + u32 *eeprom_buff; + int first_dword, last_dword; + int retval = 0; + int i; + + if (eeprom->len == 0) + return -EINVAL; + + if (hw->cbs.check_nvram) + hw->cbs.check_nvram(hw, &eeprom_exist); + if (!eeprom_exist) + return -EOPNOTSUPP; + + eeprom->magic = adpt->pdev->vendor | + (adpt->pdev->device << 16); + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + + eeprom_buff = kmalloc(sizeof(u32) * + (last_dword - first_dword + 1), GFP_KERNEL); + if (eeprom_buff == NULL) + return -ENOMEM; + + for (i = first_dword; i < last_dword; i++) { + if (hw->cbs.read_nvram) { + retval = hw->cbs.read_nvram(hw, i*4, + &(eeprom_buff[i-first_dword])); + if (retval) { + retval = -EIO; + goto out; + } + } + } + + /* Device's eeprom is always little-endian, word addressable */ + for (i = 0; i < last_dword - first_dword; i++) + le32_to_cpus(&eeprom_buff[i]); + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), eeprom->len); +out: + kfree(eeprom_buff); + return retval; +} + + +static int alx_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + bool eeprom_exist = false; + u32 *eeprom_buff; + u32 *ptr; + int first_dword, last_dword; + int retval = 0; + int i; + + if (eeprom->len == 0) + return -EINVAL; + + if (hw->cbs.check_nvram) + hw->cbs.check_nvram(hw, &eeprom_exist); + if (!eeprom_exist) + return -EOPNOTSUPP; + + + if (eeprom->magic != (adpt->pdev->vendor | + (adpt->pdev->device << 16))) + return -EINVAL; + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + eeprom_buff = kmalloc(ALX_MAX_EEPROM_LEN, GFP_KERNEL); + if (eeprom_buff == NULL) + return -ENOMEM; + + ptr = (u32 *)eeprom_buff; + + if (eeprom->offset & 3) { + /* need read/modify/write of first changed EEPROM word */ + /* only the second byte of the word is being modified */ + if (hw->cbs.read_nvram) { + retval = hw->cbs.read_nvram(hw, first_dword * 4, + &(eeprom_buff[0])); + if (retval) { + retval = -EIO; + goto out; + } + } + ptr++; + } + + if (((eeprom->offset + eeprom->len) & 3)) { + /* need read/modify/write of last changed EEPROM word */ + /* only the first byte of the word is being modified */ + if (hw->cbs.read_nvram) { + retval = hw->cbs.read_nvram(hw, last_dword * 4, + &(eeprom_buff[last_dword - first_dword])); + if (retval) { + retval = -EIO; + goto out; + } + } + } + + /* Device's eeprom is always little-endian, word addressable */ + memcpy(ptr, bytes, eeprom->len); + for (i = 0; i < last_dword - first_dword + 1; i++) { + if (hw->cbs.write_nvram) { + retval = hw->cbs.write_nvram(hw, (first_dword + i) * 4, + eeprom_buff[i]); + if (retval) { + retval = -EIO; + goto out; + } + } + } +out: + kfree(eeprom_buff); + return retval; +} + + +static void alx_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + + strlcpy(drvinfo->driver, alx_drv_name, sizeof(drvinfo->driver)); + strlcpy(drvinfo->fw_version, "alx", 32); + strlcpy(drvinfo->bus_info, pci_name(adpt->pdev), + sizeof(drvinfo->bus_info)); + drvinfo->n_stats = 0; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = adpt->hw.hwreg_sz; + drvinfo->eedump_len = adpt->hw.eeprom_sz; +} + + +static int alx_wol_exclusion(struct alx_adapter *adpt, + struct ethtool_wolinfo *wol) +{ + struct alx_hw *hw = &adpt->hw; + int retval = 1; + + /* WOL not supported except for the following */ + switch (hw->pci_devid) { + case ALX_DEV_ID_AR8131: + case ALX_DEV_ID_AR8132: + case ALX_DEV_ID_AR8151_V1: + case ALX_DEV_ID_AR8151_V2: + case ALX_DEV_ID_AR8152_V1: + case ALX_DEV_ID_AR8152_V2: + case ALX_DEV_ID_AR8161: + case ALX_DEV_ID_AR8162: + retval = 0; + break; + default: + wol->supported = 0; + } + + return retval; +} + + +static void alx_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + + wol->supported = WAKE_MAGIC | WAKE_PHY; + wol->wolopts = 0; + + if (adpt->wol & ALX_WOL_MAGIC) + wol->wolopts |= WAKE_MAGIC; + if (adpt->wol & ALX_WOL_PHY) + wol->wolopts |= WAKE_PHY; + + netif_info(adpt, wol, adpt->netdev, + "wol->wolopts = %x\n", wol->wolopts); +} + + +static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | + WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) + return -EOPNOTSUPP; + + if (alx_wol_exclusion(adpt, wol)) + return wol->wolopts ? -EOPNOTSUPP : 0; + + adpt->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + adpt->wol |= ALX_WOL_MAGIC; + if (wol->wolopts & WAKE_PHY) + adpt->wol |= ALX_WOL_PHY; + + device_set_wakeup_enable(&adpt->pdev->dev, adpt->wol); + + return 0; +} + + +static int alx_nway_reset(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + if (netif_running(netdev)) + alx_reinit_locked(adpt); + return 0; +} + + +static const struct ethtool_ops alx_ethtool_ops = { + .get_settings = alx_get_settings, + .set_settings = alx_set_settings, + .get_pauseparam = alx_get_pauseparam, + .set_pauseparam = alx_set_pauseparam, + .get_drvinfo = alx_get_drvinfo, + .get_regs_len = alx_get_regs_len, + .get_regs = alx_get_regs, + .get_wol = alx_get_wol, + .set_wol = alx_set_wol, + .get_msglevel = alx_get_msglevel, + .set_msglevel = alx_set_msglevel, + .nway_reset = alx_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = alx_get_eeprom_len, + .get_eeprom = alx_get_eeprom, + .set_eeprom = alx_set_eeprom, +}; + + +void alx_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &alx_ethtool_ops); +} --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alx_hwcom.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ALX_HWCOMMON_H_ +#define _ALX_HWCOMMON_H_ + +#include +#include "alx_sw.h" + + +#define BIT_ALL 0xffffffffUL + +#define ASHFT31(_x) ((_x) << 31) +#define ASHFT30(_x) ((_x) << 30) +#define ASHFT29(_x) ((_x) << 29) +#define ASHFT28(_x) ((_x) << 28) +#define ASHFT27(_x) ((_x) << 27) +#define ASHFT26(_x) ((_x) << 26) +#define ASHFT25(_x) ((_x) << 25) +#define ASHFT24(_x) ((_x) << 24) +#define ASHFT23(_x) ((_x) << 23) +#define ASHFT22(_x) ((_x) << 22) +#define ASHFT21(_x) ((_x) << 21) +#define ASHFT20(_x) ((_x) << 20) +#define ASHFT19(_x) ((_x) << 19) +#define ASHFT18(_x) ((_x) << 18) +#define ASHFT17(_x) ((_x) << 17) +#define ASHFT16(_x) ((_x) << 16) +#define ASHFT15(_x) ((_x) << 15) +#define ASHFT14(_x) ((_x) << 14) +#define ASHFT13(_x) ((_x) << 13) +#define ASHFT12(_x) ((_x) << 12) +#define ASHFT11(_x) ((_x) << 11) +#define ASHFT10(_x) ((_x) << 10) +#define ASHFT9(_x) ((_x) << 9) +#define ASHFT8(_x) ((_x) << 8) +#define ASHFT7(_x) ((_x) << 7) +#define ASHFT6(_x) ((_x) << 6) +#define ASHFT5(_x) ((_x) << 5) +#define ASHFT4(_x) ((_x) << 4) +#define ASHFT3(_x) ((_x) << 3) +#define ASHFT2(_x) ((_x) << 2) +#define ASHFT1(_x) ((_x) << 1) +#define ASHFT0(_x) ((_x) << 0) + + +#define FIELD_GETX(_x, _name) (((_x) & (_name##_MASK)) >> (_name##_SHIFT)) +#define FIELD_SETS(_x, _name, _v) (\ +(_x) = \ +((_x) & ~(_name##_MASK)) |\ +(((u16)(_v) << (_name##_SHIFT)) & (_name##_MASK))) +#define FIELD_SETL(_x, _name, _v) (\ +(_x) = \ +((_x) & ~(_name##_MASK)) |\ +(((u32)(_v) << (_name##_SHIFT)) & (_name##_MASK))) +#define FIELDL(_name, _v) (((u32)(_v) << (_name##_SHIFT)) & (_name##_MASK)) +#define FIELDS(_name, _v) (((u16)(_v) << (_name##_SHIFT)) & (_name##_MASK)) + + + +#define LX_SWAP_DW(_x) (\ + (((_x) << 24) & 0xFF000000UL) |\ + (((_x) << 8) & 0x00FF0000UL) |\ + (((_x) >> 8) & 0x0000FF00UL) |\ + (((_x) >> 24) & 0x000000FFUL)) + +#define LX_SWAP_W(_x) (\ + (((_x) >> 8) & 0x00FFU) |\ + (((_x) << 8) & 0xFF00U)) + + +#define LX_ERR_SUCCESS 0x0000 +#define LX_ERR_ALOAD 0x0001 +#define LX_ERR_RSTMAC 0x0002 +#define LX_ERR_PARM 0x0003 +#define LX_ERR_MIIBUSY 0x0004 + +/* link capability */ +#define LX_LC_10H 0x01 +#define LX_LC_10F 0x02 +#define LX_LC_100H 0x04 +#define LX_LC_100F 0x08 +#define LX_LC_1000F 0x10 +#define LX_LC_ALL \ + (LX_LC_10H|LX_LC_10F|LX_LC_100H|LX_LC_100F|LX_LC_1000F) + +/* options for MAC contrl */ +#define LX_MACSPEED_1000 BIT(0) /* 1:1000M, 0:10/100M */ +#define LX_MACDUPLEX_FULL BIT(1) /* 1:full, 0:half */ +#define LX_FLT_BROADCAST BIT(2) /* 1:enable rx-broadcast */ +#define LX_FLT_MULTI_ALL BIT(3) +#define LX_FLT_DIRECT BIT(4) +#define LX_FLT_PROMISC BIT(5) +#define LX_FC_TXEN BIT(6) +#define LX_FC_RXEN BIT(7) +#define LX_VLAN_STRIP BIT(8) +#define LX_LOOPBACK BIT(9) +#define LX_ADD_FCS BIT(10) +#define LX_SINGLE_PAUSE BIT(11) + + +/* interop between drivers */ +#define LX_DRV_TYPE_MASK ASHFT27(0x1FUL) +#define LX_DRV_TYPE_SHIFT 27 +#define LX_DRV_TYPE_UNKNOWN 0 +#define LX_DRV_TYPE_BIOS 1 +#define LX_DRV_TYPE_BTROM 2 +#define LX_DRV_TYPE_PKT 3 +#define LX_DRV_TYPE_NDS2 4 +#define LX_DRV_TYPE_UEFI 5 +#define LX_DRV_TYPE_NDS5 6 +#define LX_DRV_TYPE_NDS62 7 +#define LX_DRV_TYPE_NDS63 8 +#define LX_DRV_TYPE_LNX 9 +#define LX_DRV_TYPE_ODI16 10 +#define LX_DRV_TYPE_ODI32 11 +#define LX_DRV_TYPE_FRBSD 12 +#define LX_DRV_TYPE_NTBSD 13 +#define LX_DRV_TYPE_WCE 14 +#define LX_DRV_PHY_AUTO BIT(26) /* 1:auto, 0:force */ +#define LX_DRV_PHY_1000 BIT(25) +#define LX_DRV_PHY_100 BIT(24) +#define LX_DRV_PHY_10 BIT(23) +#define LX_DRV_PHY_DUPLEX BIT(22) /* 1:full, 0:half */ +#define LX_DRV_PHY_FC BIT(21) /* 1:en flow control */ +#define LX_DRV_PHY_MASK ASHFT21(0x1FUL) +#define LX_DRV_PHY_SHIFT 21 +#define LX_DRV_PHY_UNKNOWN 0 +#define LX_DRV_DISABLE BIT(18) +#define LX_DRV_WOLS5_EN BIT(17) +#define LX_DRV_WOLS5_BIOS_EN BIT(16) +#define LX_DRV_AZ_EN BIT(12) +#define LX_DRV_WOLPATTERN_EN BIT(11) +#define LX_DRV_WOLLINKUP_EN BIT(10) +#define LX_DRV_WOLMAGIC_EN BIT(9) +#define LX_DRV_WOLCAP_BIOS_EN BIT(8) +#define LX_DRV_ASPM_SPD1000LMT_MASK ASHFT4(3UL) +#define LX_DRV_ASPM_SPD1000LMT_SHIFT 4 +#define LX_DRV_ASPM_SPD1000LMT_100M 0 +#define LX_DRV_ASPM_SPD1000LMT_NO 1 +#define LX_DRV_ASPM_SPD1000LMT_1M 2 +#define LX_DRV_ASPM_SPD1000LMT_10M 3 +#define LX_DRV_ASPM_SPD100LMT_MASK ASHFT2(3UL) +#define LX_DRV_ASPM_SPD100LMT_SHIFT 2 +#define LX_DRV_ASPM_SPD100LMT_1M 0 +#define LX_DRV_ASPM_SPD100LMT_10M 1 +#define LX_DRV_ASPM_SPD100LMT_100M 2 +#define LX_DRV_ASPM_SPD100LMT_NO 3 +#define LX_DRV_ASPM_SPD10LMT_MASK ASHFT0(3UL) +#define LX_DRV_ASPM_SPD10LMT_SHIFT 0 +#define LX_DRV_ASPM_SPD10LMT_1M 0 +#define LX_DRV_ASPM_SPD10LMT_10M 1 +#define LX_DRV_ASPM_SPD10LMT_100M 2 +#define LX_DRV_ASPM_SPD10LMT_NO 3 + +/* flag of phy inited */ +#define LX_PHY_INITED 0x003F + +/* check if the mac address is valid */ +#define macaddr_valid(_addr) (\ + ((*(u8 *)(_addr))&1) == 0 && \ + !(*(u32 *)(_addr) == 0 && *((u16 *)(_addr)+2) == 0)) + +#define test_set_or_clear(_val, _ctrl, _flag, _bit) \ +do { \ + if ((_ctrl) & (_flag)) \ + (_val) |= (_bit); \ + else \ + (_val) &= ~(_bit); \ +} while (0) + + +#endif/*_ALX_HWCOMMON_H_*/ + --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alx_main.c @@ -0,0 +1,3899 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "alx.h" +#include "alx_hwcom.h" + +char alx_drv_name[] = "alx"; +static const char alx_drv_description[] = + "Qualcomm Atheros(R) " + "AR813x/AR815x/AR816x PCI-E Ethernet Network Driver"; + +/* alx_pci_tbl - PCI Device ID Table + * + * Wildcard entries (PCI_ANY_ID) should come last + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, private data (not used) } + */ +#define ALX_ETHER_DEVICE(device_id) {\ + PCI_DEVICE(ALX_VENDOR_ID, device_id)} +static DEFINE_PCI_DEVICE_TABLE(alx_pci_tbl) = { + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8131), + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8132), + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8151_V1), + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8151_V2), + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8152_V1), + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8152_V2), + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8161), + ALX_ETHER_DEVICE(ALX_DEV_ID_AR8162), + {0,} +}; +MODULE_DEVICE_TABLE(pci, alx_pci_tbl); +MODULE_AUTHOR("Qualcomm Corporation, "); +MODULE_DESCRIPTION("Qualcomm Atheros Gigabit Ethernet Driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static int alx_open_internal(struct alx_adapter *adpt, u32 ctrl); +static void alx_stop_internal(struct alx_adapter *adpt, u32 ctrl); + +int alx_cfg_r16(const struct alx_hw *hw, int reg, u16 *pval) +{ + if (!(hw && hw->adpt && hw->adpt->pdev)) + return -EINVAL; + return pci_read_config_word(hw->adpt->pdev, reg, pval); +} + + +int alx_cfg_w16(const struct alx_hw *hw, int reg, u16 val) +{ + if (!(hw && hw->adpt && hw->adpt->pdev)) + return -EINVAL; + return pci_write_config_word(hw->adpt->pdev, reg, val); +} + + +void alx_mem_flush(const struct alx_hw *hw) +{ + readl(hw->hw_addr); +} + + +void alx_mem_r32(const struct alx_hw *hw, int reg, u32 *val) +{ + if (unlikely(!hw->link_up)) + readl(hw->hw_addr + reg); + *(u32 *)val = readl(hw->hw_addr + reg); +} + + +void alx_mem_w32(const struct alx_hw *hw, int reg, u32 val) +{ + if (hw->mac_type == alx_mac_l2cb_v20 && reg < 0x1400) + readl(hw->hw_addr + reg); + writel(val, hw->hw_addr + reg); +} + + +static void alx_mem_r16(const struct alx_hw *hw, int reg, u16 *val) +{ + if (unlikely(!hw->link_up)) + readl(hw->hw_addr + reg); + *(u16 *)val = readw(hw->hw_addr + reg); +} + + +static void alx_mem_w16(const struct alx_hw *hw, int reg, u16 val) +{ + if (hw->mac_type == alx_mac_l2cb_v20 && reg < 0x1400) + readl(hw->hw_addr + reg); + writew(val, hw->hw_addr + reg); +} + + +void alx_mem_w8(const struct alx_hw *hw, int reg, u8 val) +{ + if (hw->mac_type == alx_mac_l2cb_v20 && reg < 0x1400) + readl(hw->hw_addr + reg); + writeb(val, hw->hw_addr + reg); +} + + +/* + * alx_hw_printk + */ +void alx_hw_printk(const char *level, const struct alx_hw *hw, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + if (hw && hw->adpt && hw->adpt->netdev) + __netdev_printk(level, hw->adpt->netdev, &vaf); + else + printk("%salx_hw: %pV", level, &vaf); + + va_end(args); +} + + +/* + * alx_validate_mac_addr - Validate MAC address + */ +static int alx_validate_mac_addr(u8 *mac_addr) +{ + int retval = 0; + + if (mac_addr[0] & 0x01) { + printk(KERN_DEBUG "MAC address is multicast\n"); + retval = -EADDRNOTAVAIL; + } else if (mac_addr[0] == 0xff && mac_addr[1] == 0xff) { + printk(KERN_DEBUG "MAC address is broadcast\n"); + retval = -EADDRNOTAVAIL; + } else if (mac_addr[0] == 0 && mac_addr[1] == 0 && + mac_addr[2] == 0 && mac_addr[3] == 0 && + mac_addr[4] == 0 && mac_addr[5] == 0) { + printk(KERN_DEBUG "MAC address is all zeros\n"); + retval = -EADDRNOTAVAIL; + } + return retval; +} + + +/* + * alx_set_mac_type - Sets MAC type + */ +static int alx_set_mac_type(struct alx_adapter *adpt) +{ + struct alx_hw *hw = &adpt->hw; + int retval = 0; + + if (hw->pci_venid == ALX_VENDOR_ID) { + switch (hw->pci_devid) { + case ALX_DEV_ID_AR8131: + hw->mac_type = alx_mac_l1c; + break; + case ALX_DEV_ID_AR8132: + hw->mac_type = alx_mac_l2c; + break; + case ALX_DEV_ID_AR8151_V1: + hw->mac_type = alx_mac_l1d_v1; + break; + case ALX_DEV_ID_AR8151_V2: + /* just use l1d configure */ + hw->mac_type = alx_mac_l1d_v2; + break; + case ALX_DEV_ID_AR8152_V1: + hw->mac_type = alx_mac_l2cb_v1; + break; + case ALX_DEV_ID_AR8152_V2: + if (hw->pci_revid == ALX_REV_ID_AR8152_V2_0) + hw->mac_type = alx_mac_l2cb_v20; + else + hw->mac_type = alx_mac_l2cb_v21; + break; + case ALX_DEV_ID_AR8161: + hw->mac_type = alx_mac_l1f; + break; + case ALX_DEV_ID_AR8162: + hw->mac_type = alx_mac_l2f; + break; + default: + retval = -EINVAL; + break; + } + } else { + retval = -EINVAL; + } + + netif_info(adpt, hw, adpt->netdev, + "found mac: %d, returns: %d\n", hw->mac_type, retval); + return retval; +} + + +/* + * alx_init_hw_callbacks + */ +static int alx_init_hw_callbacks(struct alx_adapter *adpt) +{ + struct alx_hw *hw = &adpt->hw; + int retval = 0; + + alx_set_mac_type(adpt); + + switch (hw->mac_type) { + case alx_mac_l1f: + case alx_mac_l2f: + retval = alf_init_hw_callbacks(hw); + break; + case alx_mac_l1c: + case alx_mac_l2c: + case alx_mac_l2cb_v1: + case alx_mac_l2cb_v20: + case alx_mac_l2cb_v21: + case alx_mac_l1d_v1: + case alx_mac_l1d_v2: + retval = alc_init_hw_callbacks(hw); + break; + default: + retval = -EINVAL; + break; + } + return retval; +} + + +void alx_reinit_locked(struct alx_adapter *adpt) +{ + WARN_ON(in_interrupt()); + + while (CHK_ADPT_FLAG(1, STATE_RESETTING)) + msleep(20); + SET_ADPT_FLAG(1, STATE_RESETTING); + + alx_stop_internal(adpt, ALX_OPEN_CTRL_RESET_MAC); + alx_open_internal(adpt, ALX_OPEN_CTRL_RESET_MAC); + + CLI_ADPT_FLAG(1, STATE_RESETTING); +} + + +static void alx_task_schedule(struct alx_adapter *adpt) +{ + if (!CHK_ADPT_FLAG(1, STATE_DOWN) && + !CHK_ADPT_FLAG(1, STATE_WATCH_DOG)) { + SET_ADPT_FLAG(1, STATE_WATCH_DOG); + schedule_work(&adpt->alx_task); + } +} + + +static void alx_check_lsc(struct alx_adapter *adpt) +{ + SET_ADPT_FLAG(0, TASK_LSC_REQ); + adpt->link_jiffies = jiffies + ALX_TRY_LINK_TIMEOUT; + + if (!CHK_ADPT_FLAG(1, STATE_DOWN)) + alx_task_schedule(adpt); +} + + +/* + * alx_tx_timeout - Respond to a Tx Hang + */ +static void alx_tx_timeout(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + + /* Do the reset outside of interrupt context */ + if (!CHK_ADPT_FLAG(1, STATE_DOWN)) { + SET_ADPT_FLAG(0, TASK_REINIT_REQ); + alx_task_schedule(adpt); + } +} + + +/* + * alx_set_multicase_list - Multicast and Promiscuous mode set + */ +static void alx_set_multicase_list(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + struct netdev_hw_addr *ha; + + /* Check for Promiscuous and All Multicast modes */ + if (netdev->flags & IFF_PROMISC) { + SET_HW_FLAG(PROMISC_EN); + } else if (netdev->flags & IFF_ALLMULTI) { + SET_HW_FLAG(MULTIALL_EN); + CLI_HW_FLAG(PROMISC_EN); + } else { + CLI_HW_FLAG(MULTIALL_EN); + CLI_HW_FLAG(PROMISC_EN); + } + hw->cbs.config_mac_ctrl(hw); + + /* clear the old settings from the multicast hash table */ + hw->cbs.clear_mc_addr(hw); + + /* comoute mc addresses' hash value ,and put it into hash table */ + netdev_for_each_mc_addr(ha, netdev) + hw->cbs.set_mc_addr(hw, ha->addr); +} + + +/* + * alx_set_mac - Change the Ethernet Address of the NIC + */ +static int alx_set_mac_address(struct net_device *netdev, void *data) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + struct sockaddr *addr = data; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (netif_running(netdev)) + return -EBUSY; + + if (netdev->addr_assign_type & NET_ADDR_RANDOM) + netdev->addr_assign_type ^= NET_ADDR_RANDOM; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len); + + if (hw->cbs.set_mac_addr) + hw->cbs.set_mac_addr(hw, hw->mac_addr); + return 0; +} + + +/* + * Read / Write Ptr Initialize: + */ +static void alx_init_ring_ptrs(struct alx_adapter *adpt) +{ + int i, j; + + for (i = 0; i < adpt->num_txques; i++) { + struct alx_tx_queue *txque = adpt->tx_queue[i]; + struct alx_buffer *tpbuf = txque->tpq.tpbuff; + txque->tpq.produce_idx = 0; + txque->tpq.consume_idx = 0; + for (j = 0; j < txque->tpq.count; j++) + tpbuf[j].dma = 0; + } + + for (i = 0; i < adpt->num_hw_rxques; i++) { + struct alx_rx_queue *rxque = adpt->rx_queue[i]; + struct alx_buffer *rfbuf = rxque->rfq.rfbuff; + rxque->rrq.produce_idx = 0; + rxque->rrq.consume_idx = 0; + rxque->rfq.produce_idx = 0; + rxque->rfq.consume_idx = 0; + for (j = 0; j < rxque->rfq.count; j++) + rfbuf[j].dma = 0; + } + + if (CHK_ADPT_FLAG(0, SRSS_EN)) + goto srrs_enable; + + return; + +srrs_enable: + for (i = 0; i < adpt->num_sw_rxques; i++) { + struct alx_rx_queue *rxque = adpt->rx_queue[i]; + rxque->swq.produce_idx = 0; + rxque->swq.consume_idx = 0; + } +} + + +static void alx_config_rss(struct alx_adapter *adpt) +{ + static const u8 key[40] = { + 0xE2, 0x91, 0xD7, 0x3D, 0x18, 0x05, 0xEC, 0x6C, + 0x2A, 0x94, 0xB3, 0x0D, 0xA5, 0x4F, 0x2B, 0xEC, + 0xEA, 0x49, 0xAF, 0x7C, 0xE2, 0x14, 0xAD, 0x3D, + 0xB8, 0x55, 0xAA, 0xBE, 0x6A, 0x3E, 0x67, 0xEA, + 0x14, 0x36, 0x4D, 0x17, 0x3B, 0xED, 0x20, 0x0D}; + + struct alx_hw *hw = &adpt->hw; + u32 reta = 0; + int i, j; + + /* initialize rss hash type and idt table size */ + hw->rss_hstype = ALX_RSS_HSTYP_ALL_EN; + hw->rss_idt_size = 0x100; + + /* Fill out redirection table */ + memcpy(hw->rss_key, key, sizeof(hw->rss_key)); + + /* Fill out redirection table */ + memset(hw->rss_idt, 0x0, sizeof(hw->rss_idt)); + for (i = 0, j = 0; i < 256; i++, j++) { + if (j == adpt->max_rxques) + j = 0; + reta |= (j << ((i & 7) * 4)); + if ((i & 7) == 7) { + hw->rss_idt[i>>3] = reta; + reta = 0; + } + } + + if (hw->cbs.config_rss) + hw->cbs.config_rss(hw, CHK_ADPT_FLAG(0, SRSS_EN)); +} + + +/* + * alx_receive_skb + */ +static void alx_receive_skb(struct alx_adapter *adpt, + struct sk_buff *skb, + u16 vlan_tag, bool vlan_flag) +{ + if (vlan_flag) { + u16 vlan; + ALX_TAG_TO_VLAN(vlan_tag, vlan); + __vlan_hwaccel_put_tag(skb, vlan); + } + netif_receive_skb(skb); +} + + +static bool alx_get_rrdesc(struct alx_rx_queue *rxque, + union alx_sw_rrdesc *srrd) +{ + union alx_rrdesc *hrrd = + ALX_RRD(rxque, rxque->rrq.consume_idx); + + srrd->dfmt.dw0 = le32_to_cpu(hrrd->dfmt.dw0); + srrd->dfmt.dw1 = le32_to_cpu(hrrd->dfmt.dw1); + srrd->dfmt.dw2 = le32_to_cpu(hrrd->dfmt.dw2); + srrd->dfmt.dw3 = le32_to_cpu(hrrd->dfmt.dw3); + + if (!srrd->genr.update) + return false; + + if (likely(srrd->genr.nor != 1)) { + /* TODO support mul rfd*/ + printk(KERN_EMERG "Multi rfd not support yet!\n"); + } + + srrd->genr.update = 0; + hrrd->dfmt.dw3 = cpu_to_le32(srrd->dfmt.dw3); + if (++rxque->rrq.consume_idx == rxque->rrq.count) + rxque->rrq.consume_idx = 0; + + return true; +} + + +static bool alx_set_rfdesc(struct alx_rx_queue *rxque, + union alx_sw_rfdesc *srfd) +{ + union alx_rfdesc *hrfd = + ALX_RFD(rxque, rxque->rfq.produce_idx); + + hrfd->qfmt.qw0 = cpu_to_le64(srfd->qfmt.qw0); + + if (++rxque->rfq.produce_idx == rxque->rfq.count) + rxque->rfq.produce_idx = 0; + + return true; +} + + +static bool alx_set_tpdesc(struct alx_tx_queue *txque, + union alx_sw_tpdesc *stpd) +{ + union alx_tpdesc *htpd; + + txque->tpq.last_produce_idx = txque->tpq.produce_idx; + htpd = ALX_TPD(txque, txque->tpq.produce_idx); + + if (++txque->tpq.produce_idx == txque->tpq.count) + txque->tpq.produce_idx = 0; + + htpd->dfmt.dw0 = cpu_to_le32(stpd->dfmt.dw0); + htpd->dfmt.dw1 = cpu_to_le32(stpd->dfmt.dw1); + htpd->qfmt.qw1 = cpu_to_le64(stpd->qfmt.qw1); + + return true; +} + + +static void alx_set_tpdesc_lastfrag(struct alx_tx_queue *txque) +{ + union alx_tpdesc *htpd; +#define ALX_TPD_LAST_FLAGMENT 0x80000000 + htpd = ALX_TPD(txque, txque->tpq.last_produce_idx); + htpd->dfmt.dw1 |= cpu_to_le32(ALX_TPD_LAST_FLAGMENT); +} + + +static int alx_refresh_rx_buffer(struct alx_rx_queue *rxque) +{ + struct alx_adapter *adpt = netdev_priv(rxque->netdev); + struct alx_hw *hw = &adpt->hw; + struct alx_buffer *curr_rxbuf; + struct alx_buffer *next_rxbuf; + union alx_sw_rfdesc srfd; + struct sk_buff *skb; + void *skb_data = NULL; + u16 count = 0; + u16 next_produce_idx; + + next_produce_idx = rxque->rfq.produce_idx; + if (++next_produce_idx == rxque->rfq.count) + next_produce_idx = 0; + curr_rxbuf = GET_RF_BUFFER(rxque, rxque->rfq.produce_idx); + next_rxbuf = GET_RF_BUFFER(rxque, next_produce_idx); + + /* this always has a blank rx_buffer*/ + while (next_rxbuf->dma == 0) { + skb = dev_alloc_skb(adpt->rxbuf_size); + if (unlikely(!skb)) { + alx_err(adpt, "alloc rx buffer failed\n"); + break; + } + + /* + * Make buffer alignment 2 beyond a 16 byte boundary + * this will result in a 16 byte aligned IP header after + * the 14 byte MAC header is removed + */ + skb_data = skb->data; + /*skb_reserve(skb, NET_IP_ALIGN);*/ + curr_rxbuf->skb = skb; + curr_rxbuf->length = adpt->rxbuf_size; + curr_rxbuf->dma = dma_map_single(rxque->dev, + skb_data, + curr_rxbuf->length, + DMA_FROM_DEVICE); + srfd.genr.addr = curr_rxbuf->dma; + alx_set_rfdesc(rxque, &srfd); + + next_produce_idx = rxque->rfq.produce_idx; + if (++next_produce_idx == rxque->rfq.count) + next_produce_idx = 0; + curr_rxbuf = GET_RF_BUFFER(rxque, rxque->rfq.produce_idx); + next_rxbuf = GET_RF_BUFFER(rxque, next_produce_idx); + count++; + } + + if (count) { + wmb(); + alx_mem_w16(hw, rxque->produce_reg, rxque->rfq.produce_idx); + netif_info(adpt, rx_err, adpt->netdev, + "RX[%d]: prod_reg[%x] = 0x%x, rfq.prod_idx = 0x%x\n", + rxque->que_idx, rxque->produce_reg, + rxque->rfq.produce_idx, rxque->rfq.produce_idx); + } + return count; +} + + +static void alx_clean_rfdesc(struct alx_rx_queue *rxque, + union alx_sw_rrdesc *srrd) +{ + struct alx_buffer *rfbuf = rxque->rfq.rfbuff; + u32 consume_idx = srrd->genr.si; + u32 i; + + for (i = 0; i < srrd->genr.nor; i++) { + rfbuf[consume_idx].skb = NULL; + if (++consume_idx == rxque->rfq.count) + consume_idx = 0; + } + rxque->rfq.consume_idx = consume_idx; +} + + +static bool alx_dispatch_rx_irq(struct alx_msix_param *msix, + struct alx_rx_queue *rxque) +{ + struct alx_adapter *adpt = msix->adpt; + struct pci_dev *pdev = adpt->pdev; + struct net_device *netdev = adpt->netdev; + + union alx_sw_rrdesc srrd; + struct alx_buffer *rfbuf; + struct sk_buff *skb; + struct alx_rx_queue *swque; + struct alx_sw_buffer *curr_swbuf; + struct alx_sw_buffer *next_swbuf; + + u16 next_produce_idx; + u16 count = 0; + + while (1) { + if (!alx_get_rrdesc(rxque, &srrd)) + break; + + if (srrd.genr.res || srrd.genr.lene) { + alx_clean_rfdesc(rxque, &srrd); + netif_warn(adpt, rx_err, adpt->netdev, + "wrong packet! rrd->word3 is 0x%08x\n", + srrd.dfmt.dw3); + continue; + } + + /* Good Receive */ + if (likely(srrd.genr.nor == 1)) { + rfbuf = GET_RF_BUFFER(rxque, srrd.genr.si); + pci_unmap_single(pdev, rfbuf->dma, + rfbuf->length, DMA_FROM_DEVICE); + rfbuf->dma = 0; + skb = rfbuf->skb; + netif_info(adpt, rx_err, adpt->netdev, + "skb addr = %p, rxbuf_len = %x\n", + skb->data, rfbuf->length); + } else { + /* TODO */ + alx_err(adpt, "Multil rfd not support yet!\n"); + break; + } + alx_clean_rfdesc(rxque, &srrd); + + skb_put(skb, srrd.genr.pkt_len - ETH_FCS_LEN); + skb->protocol = eth_type_trans(skb, netdev); + skb_checksum_none_assert(skb); + + /* start to dispatch */ + swque = adpt->rx_queue[srrd.genr.rss_cpu]; + next_produce_idx = swque->swq.produce_idx; + if (++next_produce_idx == swque->swq.count) + next_produce_idx = 0; + + curr_swbuf = GET_SW_BUFFER(swque, swque->swq.produce_idx); + next_swbuf = GET_SW_BUFFER(swque, next_produce_idx); + + /* + * if full, will discard the packet, + * and at lease has a blank sw_buffer. + */ + if (!next_swbuf->skb) { + curr_swbuf->skb = skb; + curr_swbuf->vlan_tag = srrd.genr.vlan_tag; + curr_swbuf->vlan_flag = srrd.genr.vlan_flag; + if (++swque->swq.produce_idx == swque->swq.count) + swque->swq.produce_idx = 0; + } + + count++; + if (count == 32) + break; + } + if (count) + alx_refresh_rx_buffer(rxque); + return true; +} + + +static bool alx_handle_srx_irq(struct alx_msix_param *msix, + struct alx_rx_queue *rxque, + int *num_pkts, int max_pkts) +{ + struct alx_adapter *adpt = msix->adpt; + struct alx_sw_buffer *swbuf; + bool retval = true; + + while (rxque->swq.consume_idx != rxque->swq.produce_idx) { + swbuf = GET_SW_BUFFER(rxque, rxque->swq.consume_idx); + + alx_receive_skb(adpt, swbuf->skb, (u16)swbuf->vlan_tag, + (bool)swbuf->vlan_flag); + swbuf->skb = NULL; + + if (++rxque->swq.consume_idx == rxque->swq.count) + rxque->swq.consume_idx = 0; + + (*num_pkts)++; + if (*num_pkts >= max_pkts) { + retval = false; + break; + } + } + return retval; +} + + +static bool alx_handle_rx_irq(struct alx_msix_param *msix, + struct alx_rx_queue *rxque, + int *num_pkts, int max_pkts) +{ + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + struct pci_dev *pdev = adpt->pdev; + struct net_device *netdev = adpt->netdev; + + union alx_sw_rrdesc srrd; + struct alx_buffer *rfbuf; + struct sk_buff *skb; + + u16 hw_consume_idx, num_consume_pkts; + u16 count = 0; + + alx_mem_r16(hw, rxque->consume_reg, &hw_consume_idx); + num_consume_pkts = (hw_consume_idx > rxque->rrq.consume_idx) ? + (hw_consume_idx - rxque->rrq.consume_idx) : + (hw_consume_idx + rxque->rrq.count - rxque->rrq.consume_idx); + + while (1) { + if (!num_consume_pkts) + break; + + if (!alx_get_rrdesc(rxque, &srrd)) + break; + + if (srrd.genr.res || srrd.genr.lene) { + alx_clean_rfdesc(rxque, &srrd); + netif_warn(adpt, rx_err, adpt->netdev, + "wrong packet! rrd->word3 is 0x%08x\n", + srrd.dfmt.dw3); + continue; + } + + /* TODO: Good Receive */ + if (likely(srrd.genr.nor == 1)) { + rfbuf = GET_RF_BUFFER(rxque, srrd.genr.si); + pci_unmap_single(pdev, rfbuf->dma, rfbuf->length, + DMA_FROM_DEVICE); + rfbuf->dma = 0; + skb = rfbuf->skb; + } else { + /* TODO */ + alx_err(adpt, "Multil rfd not support yet!\n"); + break; + } + alx_clean_rfdesc(rxque, &srrd); + + skb_put(skb, srrd.genr.pkt_len - ETH_FCS_LEN); + skb->protocol = eth_type_trans(skb, netdev); + skb_checksum_none_assert(skb); + alx_receive_skb(adpt, skb, (u16)srrd.genr.vlan_tag, + (bool)srrd.genr.vlan_flag); + + num_consume_pkts--; + count++; + (*num_pkts)++; + if (*num_pkts >= max_pkts) + break; + } + if (count) + alx_refresh_rx_buffer(rxque); + + return true; +} + + +static bool alx_handle_tx_irq(struct alx_msix_param *msix, + struct alx_tx_queue *txque) +{ + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + struct alx_buffer *tpbuf; + u16 consume_data; + + alx_mem_r16(hw, txque->consume_reg, &consume_data); + netif_info(adpt, tx_err, adpt->netdev, + "TX[%d]: consume_reg[0x%x] = 0x%x, tpq.consume_idx = 0x%x\n", + txque->que_idx, txque->consume_reg, consume_data, + txque->tpq.consume_idx); + + + while (txque->tpq.consume_idx != consume_data) { + tpbuf = GET_TP_BUFFER(txque, txque->tpq.consume_idx); + if (tpbuf->dma) { + pci_unmap_page(adpt->pdev, tpbuf->dma, tpbuf->length, + DMA_TO_DEVICE); + tpbuf->dma = 0; + } + + if (tpbuf->skb) { + dev_kfree_skb_irq(tpbuf->skb); + tpbuf->skb = NULL; + } + + if (++txque->tpq.consume_idx == txque->tpq.count) + txque->tpq.consume_idx = 0; + } + + if (netif_queue_stopped(adpt->netdev) && + netif_carrier_ok(adpt->netdev)) { + netif_wake_queue(adpt->netdev); + } + return true; +} + + +static irqreturn_t alx_msix_timer(int irq, void *data) +{ + struct alx_msix_param *msix = data; + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + u32 isr; + + hw->cbs.disable_msix_intr(hw, msix->vec_idx); + + alx_mem_r32(hw, ALX_ISR, &isr); + isr = isr & (ALX_ISR_TIMER | ALX_ISR_MANU); + + + if (isr == 0) { + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + return IRQ_NONE; + } + + /* Ack ISR */ + alx_mem_w32(hw, ALX_ISR, isr); + + if (isr & ALX_ISR_MANU) { + adpt->net_stats.tx_carrier_errors++; + alx_check_lsc(adpt); + } + + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + + return IRQ_HANDLED; +} + + +static irqreturn_t alx_msix_alert(int irq, void *data) +{ + struct alx_msix_param *msix = data; + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + u32 isr; + + hw->cbs.disable_msix_intr(hw, msix->vec_idx); + + alx_mem_r32(hw, ALX_ISR, &isr); + isr = isr & ALX_ISR_ALERT_MASK; + + if (isr == 0) { + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + return IRQ_NONE; + } + alx_mem_w32(hw, ALX_ISR, isr); + + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + + return IRQ_HANDLED; +} + + +static irqreturn_t alx_msix_smb(int irq, void *data) +{ + struct alx_msix_param *msix = data; + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + + hw->cbs.disable_msix_intr(hw, msix->vec_idx); + + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + + return IRQ_HANDLED; +} + + +static irqreturn_t alx_msix_phy(int irq, void *data) +{ + struct alx_msix_param *msix = data; + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + + hw->cbs.disable_msix_intr(hw, msix->vec_idx); + + if (hw->cbs.ack_phy_intr) + hw->cbs.ack_phy_intr(hw); + + adpt->net_stats.tx_carrier_errors++; + alx_check_lsc(adpt); + + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + + return IRQ_HANDLED; +} + + +/* + * alx_msix_rtx + */ +static irqreturn_t alx_msix_rtx(int irq, void *data) +{ + struct alx_msix_param *msix = data; + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + + netif_info(adpt, intr, adpt->netdev, + "msix vec_idx = %d\n", msix->vec_idx); + + hw->cbs.disable_msix_intr(hw, msix->vec_idx); + if (!msix->rx_count && !msix->tx_count) { + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + return IRQ_HANDLED; + } + + napi_schedule(&msix->napi); + return IRQ_HANDLED; +} + + +/* + * alx_napi_msix_rtx + */ +static int alx_napi_msix_rtx(struct napi_struct *napi, int max_pkts) +{ + struct alx_msix_param *msix = + container_of(napi, struct alx_msix_param, napi); + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + struct alx_rx_queue *rxque; + struct alx_rx_queue *swque; + struct alx_tx_queue *txque; + unsigned long flags = 0; + bool complete = true; + int num_pkts = 0; + int rque_idx, tque_idx; + int i, j; + + netif_info(adpt, intr, adpt->netdev, + "NAPI: msix vec_idx = %d\n", msix->vec_idx); + + /* RX */ + for (i = 0; i < msix->rx_count; i++) { + rque_idx = msix->rx_map[i]; + num_pkts = 0; + if (CHK_ADPT_FLAG(0, SRSS_EN)) { + if (!spin_trylock_irqsave(&adpt->rx_lock, flags)) + goto clean_sw_irq; + + for (j = 0; j < adpt->num_hw_rxques; j++) + alx_dispatch_rx_irq(msix, adpt->rx_queue[j]); + + spin_unlock_irqrestore(&adpt->rx_lock, flags); +clean_sw_irq: + swque = adpt->rx_queue[rque_idx]; + complete &= alx_handle_srx_irq(msix, swque, &num_pkts, + max_pkts); + + } else { + rxque = adpt->rx_queue[rque_idx]; + complete &= alx_handle_rx_irq(msix, rxque, &num_pkts, + max_pkts); + } + } + + + /* Handle TX */ + for (i = 0; i < msix->tx_count; i++) { + tque_idx = msix->tx_map[i]; + txque = adpt->tx_queue[tque_idx]; + complete &= alx_handle_tx_irq(msix, txque); + } + + if (!complete) { + netif_info(adpt, intr, adpt->netdev, + "Some packets in the queue are not handled!\n"); + num_pkts = max_pkts; + } + + netif_info(adpt, intr, adpt->netdev, + "num_pkts = %d, max_pkts = %d\n", num_pkts, max_pkts); + /* If all work done, exit the polling mode */ + if (num_pkts < max_pkts) { + napi_complete(napi); + if (!CHK_ADPT_FLAG(1, STATE_DOWN)) + hw->cbs.enable_msix_intr(hw, msix->vec_idx); + } + + return num_pkts; +} + + + +/* + * alx_napi_legacy_rtx - NAPI Rx polling callback + */ +static int alx_napi_legacy_rtx(struct napi_struct *napi, int max_pkts) +{ + struct alx_msix_param *msix = + container_of(napi, struct alx_msix_param, napi); + struct alx_adapter *adpt = msix->adpt; + struct alx_hw *hw = &adpt->hw; + int complete = true; + int num_pkts = 0; + int que_idx; + + netif_info(adpt, intr, adpt->netdev, + "NAPI: msix vec_idx = %d\n", msix->vec_idx); + + /* Keep link state information with original netdev */ + if (!netif_carrier_ok(adpt->netdev)) + goto enable_rtx_irq; + + for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) + complete &= alx_handle_tx_irq(msix, adpt->tx_queue[que_idx]); + + for (que_idx = 0; que_idx < adpt->num_hw_rxques; que_idx++) { + num_pkts = 0; + complete &= alx_handle_rx_irq(msix, adpt->rx_queue[que_idx], + &num_pkts, max_pkts); + } + + if (!complete) + num_pkts = max_pkts; + + if (num_pkts < max_pkts) { +enable_rtx_irq: + napi_complete(napi); + hw->intr_mask |= (ALX_ISR_RXQ | ALX_ISR_TXQ); + alx_mem_w32(hw, ALX_IMR, hw->intr_mask); + } + return num_pkts; +} + + +static void alx_set_msix_flags(struct alx_msix_param *msix, + enum alx_msix_type type, int index) +{ + if (type == alx_msix_type_rx) { + switch (index) { + case 0: + SET_MSIX_FLAG(RX0); + break; + case 1: + SET_MSIX_FLAG(RX1); + break; + case 2: + SET_MSIX_FLAG(RX2); + break; + case 3: + SET_MSIX_FLAG(RX3); + break; + case 4: + SET_MSIX_FLAG(RX4); + break; + case 5: + SET_MSIX_FLAG(RX5); + break; + case 6: + SET_MSIX_FLAG(RX6); + break; + case 7: + SET_MSIX_FLAG(RX7); + break; + default: + printk(KERN_ERR "alx_set_msix_flags: rx error."); + break; + } + } else if (type == alx_msix_type_tx) { + switch (index) { + case 0: + SET_MSIX_FLAG(TX0); + break; + case 1: + SET_MSIX_FLAG(TX1); + break; + case 2: + SET_MSIX_FLAG(TX2); + break; + case 3: + SET_MSIX_FLAG(TX3); + break; + default: + printk(KERN_ERR "alx_set_msix_flags: tx error."); + break; + } + } else if (type == alx_msix_type_other) { + switch (index) { + case ALX_MSIX_TYPE_OTH_TIMER: + SET_MSIX_FLAG(TIMER); + break; + case ALX_MSIX_TYPE_OTH_ALERT: + SET_MSIX_FLAG(ALERT); + break; + case ALX_MSIX_TYPE_OTH_SMB: + SET_MSIX_FLAG(SMB); + break; + case ALX_MSIX_TYPE_OTH_PHY: + SET_MSIX_FLAG(PHY); + break; + default: + printk(KERN_ERR "alx_set_msix_flags: other error."); + break; + } + } +} + + +/* alx_setup_msix_maps */ +static int alx_setup_msix_maps(struct alx_adapter *adpt) +{ + int msix_idx = 0; + int que_idx = 0; + int num_rxques = adpt->num_rxques; + int num_txques = adpt->num_txques; + int num_msix_rxques = adpt->num_msix_rxques; + int num_msix_txques = adpt->num_msix_txques; + int num_msix_noques = adpt->num_msix_noques; + + if (CHK_ADPT_FLAG(0, FIXED_MSIX)) + goto fixed_msix_map; + + netif_warn(adpt, ifup, adpt->netdev, + "don't support non-fixed msix map\n"); + return -EINVAL; + +fixed_msix_map: + /* + * For RX queue msix map + */ + msix_idx = 0; + for (que_idx = 0; que_idx < num_msix_rxques; que_idx++, msix_idx++) { + struct alx_msix_param *msix = adpt->msix[msix_idx]; + if (que_idx < num_rxques) { + adpt->rx_queue[que_idx]->msix = msix; + msix->rx_map[msix->rx_count] = que_idx; + msix->rx_count++; + alx_set_msix_flags(msix, alx_msix_type_rx, que_idx); + } + } + if (msix_idx != num_msix_rxques) + netif_warn(adpt, ifup, adpt->netdev, "msix_idx is wrong\n"); + + /* + * For TX queue msix map + */ + for (que_idx = 0; que_idx < num_msix_txques; que_idx++, msix_idx++) { + struct alx_msix_param *msix = adpt->msix[msix_idx]; + if (que_idx < num_txques) { + adpt->tx_queue[que_idx]->msix = msix; + msix->tx_map[msix->tx_count] = que_idx; + msix->tx_count++; + alx_set_msix_flags(msix, alx_msix_type_tx, que_idx); + } + } + if (msix_idx != (num_msix_rxques + num_msix_txques)) + netif_warn(adpt, ifup, adpt->netdev, "msix_idx is wrong\n"); + + + /* + * For NON queue msix map + */ + for (que_idx = 0; que_idx < num_msix_noques; que_idx++, msix_idx++) { + struct alx_msix_param *msix = adpt->msix[msix_idx]; + alx_set_msix_flags(msix, alx_msix_type_other, que_idx); + } + return 0; +} + + +static inline void alx_reset_msix_maps(struct alx_adapter *adpt) +{ + int que_idx, msix_idx; + + for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) + adpt->rx_queue[que_idx]->msix = NULL; + for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) + adpt->tx_queue[que_idx]->msix = NULL; + + for (msix_idx = 0; msix_idx < adpt->num_msix_intrs; msix_idx++) { + struct alx_msix_param *msix = adpt->msix[msix_idx]; + memset(msix->rx_map, 0, sizeof(msix->rx_map)); + memset(msix->tx_map, 0, sizeof(msix->tx_map)); + msix->rx_count = 0; + msix->tx_count = 0; + CLI_MSIX_FLAG(ALL); + } +} + + +/* + * alx_enable_intr - Enable default interrupt generation settings + */ +static inline void alx_enable_intr(struct alx_adapter *adpt) +{ + struct alx_hw *hw = &adpt->hw; + int i; + + if (!atomic_dec_and_test(&adpt->irq_sem)) + return; + + if (hw->cbs.enable_legacy_intr) + hw->cbs.enable_legacy_intr(hw); + + /* enable all MSIX IRQs */ + for (i = 0; i < adpt->num_msix_intrs; i++) { + if (hw->cbs.disable_msix_intr) + hw->cbs.disable_msix_intr(hw, i); + if (hw->cbs.enable_msix_intr) + hw->cbs.enable_msix_intr(hw, i); + } +} + + +/* alx_disable_intr - Mask off interrupt generation on the NIC */ +static inline void alx_disable_intr(struct alx_adapter *adpt) +{ + struct alx_hw *hw = &adpt->hw; + atomic_inc(&adpt->irq_sem); + + if (hw->cbs.disable_legacy_intr) + hw->cbs.disable_legacy_intr(hw); + + if (CHK_ADPT_FLAG(0, MSIX_EN)) { + int i; + for (i = 0; i < adpt->num_msix_intrs; i++) { + synchronize_irq(adpt->msix_entries[i].vector); + hw->cbs.disable_msix_intr(hw, i); + } + } else { + synchronize_irq(adpt->pdev->irq); + } + + +} + + +/* + * alx_interrupt - Interrupt Handler + */ +static irqreturn_t alx_interrupt(int irq, void *data) +{ + struct net_device *netdev = data; + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + struct alx_msix_param *msix = adpt->msix[0]; + int max_intrs = ALX_MAX_HANDLED_INTRS; + u32 isr, status; + + do { + alx_mem_r32(hw, ALX_ISR, &isr); + status = isr & hw->intr_mask; + + if (status == 0) { + alx_mem_w32(hw, ALX_ISR, 0); + if (max_intrs != ALX_MAX_HANDLED_INTRS) + return IRQ_HANDLED; + return IRQ_NONE; + } + + /* ack ISR to PHY register */ + if (status & ALX_ISR_PHY) + hw->cbs.ack_phy_intr(hw); + /* ack ISR to MAC register */ + alx_mem_w32(hw, ALX_ISR, status | ALX_ISR_DIS); + + if (status & ALX_ISR_ERROR) { + netif_warn(adpt, intr, adpt->netdev, + "isr error (status = 0x%x)\n", + status & ALX_ISR_ERROR); + if (status & ALX_ISR_PCIE_FERR) { + alx_mem_w16(hw, ALX_DEV_STAT, + ALX_DEV_STAT_FERR | + ALX_DEV_STAT_NFERR | + ALX_DEV_STAT_CERR); + } + /* reset MAC */ + SET_ADPT_FLAG(0, TASK_REINIT_REQ); + alx_task_schedule(adpt); + return IRQ_HANDLED; + } + + if (status & (ALX_ISR_RXQ | ALX_ISR_TXQ)) { + if (napi_schedule_prep(&(msix->napi))) { + hw->intr_mask &= ~(ALX_ISR_RXQ | ALX_ISR_TXQ); + alx_mem_w32(hw, ALX_IMR, hw->intr_mask); + __napi_schedule(&(msix->napi)); + } + } + + if (status & ALX_ISR_OVER) { + netif_warn(adpt, intr, adpt->netdev, + "TX/RX overflow (status = 0x%x)\n", + status & ALX_ISR_OVER); + } + + /* link event */ + if (status & (ALX_ISR_PHY | ALX_ISR_MANU)) { + netdev->stats.tx_carrier_errors++; + alx_check_lsc(adpt); + break; + } + + } while (--max_intrs > 0); + /* re-enable Interrupt*/ + alx_mem_w32(hw, ALX_ISR, 0); + return IRQ_HANDLED; +} + + +/* + * alx_request_msix_irqs - Initialize MSI-X interrupts + */ +static int alx_request_msix_irq(struct alx_adapter *adpt) +{ + struct net_device *netdev = adpt->netdev; + irqreturn_t (*handler)(int, void *); + int msix_idx; + int num_msix_intrs = adpt->num_msix_intrs; + int rx_idx = 0, tx_idx = 0; + int i; + int retval; + + retval = alx_setup_msix_maps(adpt); + if (retval) + return retval; + + for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { + struct alx_msix_param *msix = adpt->msix[msix_idx]; + + if (CHK_MSIX_FLAG(RXS) && CHK_MSIX_FLAG(TXS)) { + handler = alx_msix_rtx; + sprintf(msix->name, "%s:%s%d", + netdev->name, "rtx", rx_idx); + rx_idx++; + tx_idx++; + } else if (CHK_MSIX_FLAG(RXS)) { + handler = alx_msix_rtx; + sprintf(msix->name, "%s:%s%d", + netdev->name, "rx", rx_idx); + rx_idx++; + } else if (CHK_MSIX_FLAG(TXS)) { + handler = alx_msix_rtx; + sprintf(msix->name, "%s:%s%d", + netdev->name, "tx", tx_idx); + tx_idx++; + } else if (CHK_MSIX_FLAG(TIMER)) { + handler = alx_msix_timer; + sprintf(msix->name, "%s:%s", netdev->name, "timer"); + } else if (CHK_MSIX_FLAG(ALERT)) { + handler = alx_msix_alert; + sprintf(msix->name, "%s:%s", netdev->name, "alert"); + } else if (CHK_MSIX_FLAG(SMB)) { + handler = alx_msix_smb; + sprintf(msix->name, "%s:%s", netdev->name, "smb"); + } else if (CHK_MSIX_FLAG(PHY)) { + handler = alx_msix_phy; + sprintf(msix->name, "%s:%s", netdev->name, "phy"); + } else { + netif_info(adpt, ifup, adpt->netdev, + "MSIX entry [%d] is blank\n", + msix->vec_idx); + continue; + } + netif_info(adpt, ifup, adpt->netdev, + "MSIX entry [%d] is %s\n", + msix->vec_idx, msix->name); + retval = request_irq(adpt->msix_entries[msix_idx].vector, + handler, 0, msix->name, msix); + if (retval) + goto free_msix_irq; + + /* assign the mask for this irq */ + irq_set_affinity_hint(adpt->msix_entries[msix_idx].vector, + msix->affinity_mask); + } + return retval; + + +free_msix_irq: + for (i = 0; i < msix_idx; i++) { + irq_set_affinity_hint(adpt->msix_entries[i].vector, NULL); + free_irq(adpt->msix_entries[i].vector, adpt->msix[i]); + } + CLI_ADPT_FLAG(0, MSIX_EN); + pci_disable_msix(adpt->pdev); + kfree(adpt->msix_entries); + adpt->msix_entries = NULL; + return retval; +} + + +/* + * alx_request_irq - initialize interrupts + */ +static int alx_request_irq(struct alx_adapter *adpt) +{ + struct net_device *netdev = adpt->netdev; + int retval; + + /* request MSIX irq */ + if (CHK_ADPT_FLAG(0, MSIX_EN)) { + retval = alx_request_msix_irq(adpt); + if (retval) { + alx_err(adpt, "request msix irq failed, error = %d\n", + retval); + } + goto out; + } + + /* request MSI irq */ + if (CHK_ADPT_FLAG(0, MSI_EN)) { + retval = request_irq(adpt->pdev->irq, alx_interrupt, 0, + netdev->name, netdev); + if (retval) { + alx_err(adpt, "request msix irq failed, error = %d\n", + retval); + } + goto out; + } + + /* request shared irq */ + retval = request_irq(adpt->pdev->irq, alx_interrupt, IRQF_SHARED, + netdev->name, netdev); + if (retval) { + alx_err(adpt, "request shared irq failed, error = %d\n", + retval); + } +out: + return retval; +} + + +static void alx_free_irq(struct alx_adapter *adpt) +{ + struct net_device *netdev = adpt->netdev; + int i; + + if (CHK_ADPT_FLAG(0, MSIX_EN)) { + for (i = 0; i < adpt->num_msix_intrs; i++) { + struct alx_msix_param *msix = adpt->msix[i]; + netif_info(adpt, ifdown, adpt->netdev, + "msix entry = %d\n", i); + if (!CHK_MSIX_FLAG(ALL)) + continue; + if (CHK_MSIX_FLAG(RXS) || CHK_MSIX_FLAG(TXS)) { + irq_set_affinity_hint( + adpt->msix_entries[i].vector, NULL); + } + free_irq(adpt->msix_entries[i].vector, msix); + } + alx_reset_msix_maps(adpt); + } else { + free_irq(adpt->pdev->irq, netdev); + } +} + + +static void alx_vlan_mode(struct net_device *netdev, + netdev_features_t features) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + + if (!CHK_ADPT_FLAG(1, STATE_DOWN)) + alx_disable_intr(adpt); + + if (features & NETIF_F_HW_VLAN_RX) { + /* enable VLAN tag insert/strip */ + SET_HW_FLAG(VLANSTRIP_EN); + } else { + /* disable VLAN tag insert/strip */ + CLI_HW_FLAG(VLANSTRIP_EN); + } + hw->cbs.config_mac_ctrl(hw); + + if (!CHK_ADPT_FLAG(1, STATE_DOWN)) + alx_enable_intr(adpt); +} + + +static void alx_restore_vlan(struct alx_adapter *adpt) +{ + alx_vlan_mode(adpt->netdev, adpt->netdev->features); +} + + +static void alx_napi_enable_all(struct alx_adapter *adpt) +{ + struct alx_msix_param *msix; + int num_msix_intrs = adpt->num_msix_intrs; + int msix_idx; + + if (!CHK_ADPT_FLAG(0, MSIX_EN)) + num_msix_intrs = 1; + + for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { + struct napi_struct *napi; + msix = adpt->msix[msix_idx]; + napi = &msix->napi; + napi_enable(napi); + } +} + + +static void alx_napi_disable_all(struct alx_adapter *adpt) +{ + struct alx_msix_param *msix; + int num_msix_intrs = adpt->num_msix_intrs; + int msix_idx; + + if (!CHK_ADPT_FLAG(0, MSIX_EN)) + num_msix_intrs = 1; + + for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { + msix = adpt->msix[msix_idx]; + napi_disable(&msix->napi); + } +} + + +static void alx_clean_tx_queue(struct alx_tx_queue *txque) +{ + struct device *dev = txque->dev; + unsigned long size; + u16 i; + + /* ring already cleared, nothing to do */ + if (!txque->tpq.tpbuff) + return; + + for (i = 0; i < txque->tpq.count; i++) { + struct alx_buffer *tpbuf; + tpbuf = GET_TP_BUFFER(txque, i); + if (tpbuf->dma) { + pci_unmap_single(to_pci_dev(dev), + tpbuf->dma, + tpbuf->length, + DMA_TO_DEVICE); + tpbuf->dma = 0; + } + if (tpbuf->skb) { + dev_kfree_skb_any(tpbuf->skb); + tpbuf->skb = NULL; + } + } + + size = sizeof(struct alx_buffer) * txque->tpq.count; + memset(txque->tpq.tpbuff, 0, size); + + /* Zero out Tx-buffers */ + memset(txque->tpq.tpdesc, 0, txque->tpq.size); + + txque->tpq.consume_idx = 0; + txque->tpq.produce_idx = 0; +} + + +/* + * alx_clean_all_tx_queues + */ +static void alx_clean_all_tx_queues(struct alx_adapter *adpt) +{ + int i; + + for (i = 0; i < adpt->num_txques; i++) + alx_clean_tx_queue(adpt->tx_queue[i]); +} + + +static void alx_clean_rx_queue(struct alx_rx_queue *rxque) +{ + struct device *dev = rxque->dev; + unsigned long size; + int i; + + if (CHK_RX_FLAG(HW_QUE)) { + /* ring already cleared, nothing to do */ + if (!rxque->rfq.rfbuff) + goto clean_sw_queue; + + for (i = 0; i < rxque->rfq.count; i++) { + struct alx_buffer *rfbuf; + rfbuf = GET_RF_BUFFER(rxque, i); + + if (rfbuf->dma) { + pci_unmap_single(to_pci_dev(dev), + rfbuf->dma, + rfbuf->length, + DMA_FROM_DEVICE); + rfbuf->dma = 0; + } + if (rfbuf->skb) { + dev_kfree_skb(rfbuf->skb); + rfbuf->skb = NULL; + } + } + size = sizeof(struct alx_buffer) * rxque->rfq.count; + memset(rxque->rfq.rfbuff, 0, size); + + /* zero out the descriptor ring */ + memset(rxque->rrq.rrdesc, 0, rxque->rrq.size); + rxque->rrq.produce_idx = 0; + rxque->rrq.consume_idx = 0; + + memset(rxque->rfq.rfdesc, 0, rxque->rfq.size); + rxque->rfq.produce_idx = 0; + rxque->rfq.consume_idx = 0; + } +clean_sw_queue: + if (CHK_RX_FLAG(SW_QUE)) { + /* ring already cleared, nothing to do */ + if (!rxque->swq.swbuff) + return; + + for (i = 0; i < rxque->swq.count; i++) { + struct alx_sw_buffer *swbuf; + swbuf = GET_SW_BUFFER(rxque, i); + + /* swq doesn't map DMA*/ + + if (swbuf->skb) { + dev_kfree_skb(swbuf->skb); + swbuf->skb = NULL; + } + } + size = sizeof(struct alx_buffer) * rxque->swq.count; + memset(rxque->swq.swbuff, 0, size); + + /* swq doesn't have any descripter rings */ + rxque->swq.produce_idx = 0; + rxque->swq.consume_idx = 0; + } +} + + +/* + * alx_clean_all_rx_queues + */ +static void alx_clean_all_rx_queues(struct alx_adapter *adpt) +{ + int i; + for (i = 0; i < adpt->num_rxques; i++) + alx_clean_rx_queue(adpt->rx_queue[i]); +} + + +/* + * alx_set_rss_queues: Allocate queues for RSS + */ +static inline void alx_set_num_txques(struct alx_adapter *adpt) +{ + struct alx_hw *hw = &adpt->hw; + + if (hw->mac_type == alx_mac_l1f || hw->mac_type == alx_mac_l2f) + adpt->num_txques = 4; + else + adpt->num_txques = 2; +} + + +/* + * alx_set_rss_queues: Allocate queues for RSS + */ +static inline void alx_set_num_rxques(struct alx_adapter *adpt) +{ + if (CHK_ADPT_FLAG(0, SRSS_CAP)) { + adpt->num_hw_rxques = 1; + adpt->num_sw_rxques = adpt->max_rxques; + adpt->num_rxques = + max_t(u16, adpt->num_hw_rxques, adpt->num_sw_rxques); + } +} + + +/* + * alx_set_num_queues: Allocate queues for device, feature dependant + */ +static void alx_set_num_queues(struct alx_adapter *adpt) +{ + /* Start with default case */ + adpt->num_txques = 1; + adpt->num_rxques = 1; + adpt->num_hw_rxques = 1; + adpt->num_sw_rxques = 0; + + alx_set_num_rxques(adpt); + alx_set_num_txques(adpt); +} + + +/* alx_alloc_all_rtx_queue - allocate all queues */ +static int alx_alloc_all_rtx_queue(struct alx_adapter *adpt) +{ + int que_idx; + + for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) { + struct alx_tx_queue *txque = adpt->tx_queue[que_idx]; + + txque = kzalloc(sizeof(struct alx_tx_queue), GFP_KERNEL); + if (!txque) + goto err_alloc_tx_queue; + txque->tpq.count = adpt->num_txdescs; + txque->que_idx = que_idx; + txque->dev = &adpt->pdev->dev; + txque->netdev = adpt->netdev; + + adpt->tx_queue[que_idx] = txque; + } + + for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) { + struct alx_rx_queue *rxque = adpt->rx_queue[que_idx]; + + rxque = kzalloc(sizeof(struct alx_rx_queue), GFP_KERNEL); + if (!rxque) + goto err_alloc_rx_queue; + rxque->rrq.count = adpt->num_rxdescs; + rxque->rfq.count = adpt->num_rxdescs; + rxque->swq.count = adpt->num_rxdescs; + rxque->que_idx = que_idx; + rxque->dev = &adpt->pdev->dev; + rxque->netdev = adpt->netdev; + + if (CHK_ADPT_FLAG(0, SRSS_EN)) { + if (que_idx < adpt->num_hw_rxques) + SET_RX_FLAG(HW_QUE); + if (que_idx < adpt->num_sw_rxques) + SET_RX_FLAG(SW_QUE); + } else { + SET_RX_FLAG(HW_QUE); + } + adpt->rx_queue[que_idx] = rxque; + } + netif_dbg(adpt, probe, adpt->netdev, + "num_tx_descs = %d, num_rx_descs = %d\n", + adpt->num_txdescs, adpt->num_rxdescs); + return 0; + +err_alloc_rx_queue: + alx_err(adpt, "goto err_alloc_rx_queue"); + for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) + kfree(adpt->rx_queue[que_idx]); +err_alloc_tx_queue: + alx_err(adpt, "goto err_alloc_tx_queue"); + for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) + kfree(adpt->tx_queue[que_idx]); + return -ENOMEM; +} + + +/* alx_free_all_rtx_queue */ +static void alx_free_all_rtx_queue(struct alx_adapter *adpt) +{ + int que_idx; + + for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) { + kfree(adpt->tx_queue[que_idx]); + adpt->tx_queue[que_idx] = NULL; + } + for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) { + kfree(adpt->rx_queue[que_idx]); + adpt->rx_queue[que_idx] = NULL; + } +} + + +/* alx_set_interrupt_param - set interrupt parameter */ +static int alx_set_interrupt_param(struct alx_adapter *adpt) +{ + struct alx_msix_param *msix; + int (*poll)(struct napi_struct *, int); + int msix_idx; + + if (CHK_ADPT_FLAG(0, MSIX_EN)) { + poll = &alx_napi_msix_rtx; + } else { + adpt->num_msix_intrs = 1; + poll = &alx_napi_legacy_rtx; + } + + for (msix_idx = 0; msix_idx < adpt->num_msix_intrs; msix_idx++) { + msix = kzalloc(sizeof(struct alx_msix_param), + GFP_KERNEL); + if (!msix) + goto err_alloc_msix; + msix->adpt = adpt; + msix->vec_idx = msix_idx; + /* Allocate the affinity_hint cpumask, configure the mask */ + if (!alloc_cpumask_var(&msix->affinity_mask, GFP_KERNEL)) + goto err_alloc_cpumask; + + cpumask_set_cpu((msix_idx % num_online_cpus()), + msix->affinity_mask); + + netif_napi_add(adpt->netdev, &msix->napi, (*poll), 64); + adpt->msix[msix_idx] = msix; + } + return 0; + +err_alloc_cpumask: + kfree(msix); + adpt->msix[msix_idx] = NULL; +err_alloc_msix: + for (msix_idx--; msix_idx >= 0; msix_idx--) { + msix = adpt->msix[msix_idx]; + netif_napi_del(&msix->napi); + free_cpumask_var(msix->affinity_mask); + kfree(msix); + adpt->msix[msix_idx] = NULL; + } + alx_err(adpt, "can't allocate memory\n"); + return -ENOMEM; +} + + +/* + * alx_reset_interrupt_param - Free memory allocated for interrupt vectors + */ +static void alx_reset_interrupt_param(struct alx_adapter *adpt) +{ + int msix_idx; + + for (msix_idx = 0; msix_idx < adpt->num_msix_intrs; msix_idx++) { + struct alx_msix_param *msix = adpt->msix[msix_idx]; + netif_napi_del(&msix->napi); + free_cpumask_var(msix->affinity_mask); + kfree(msix); + adpt->msix[msix_idx] = NULL; + } +} + + +/* set msix interrupt mode */ +static int alx_set_msix_interrupt_mode(struct alx_adapter *adpt) +{ + int msix_intrs, msix_idx; + int retval = 0; + + adpt->msix_entries = kcalloc(adpt->max_msix_intrs, + sizeof(struct msix_entry), GFP_KERNEL); + if (!adpt->msix_entries) { + netif_info(adpt, probe, adpt->netdev, + "can't allocate msix entry\n"); + CLI_ADPT_FLAG(0, MSIX_EN); + goto try_msi_mode; + } + + for (msix_idx = 0; msix_idx < adpt->max_msix_intrs; msix_idx++) + adpt->msix_entries[msix_idx].entry = msix_idx; + + + msix_intrs = adpt->max_msix_intrs; + while (msix_intrs >= adpt->min_msix_intrs) { + retval = pci_enable_msix(adpt->pdev, adpt->msix_entries, + msix_intrs); + if (!retval) /* Success in acquiring all requested vectors. */ + break; + else if (retval < 0) + msix_intrs = 0; /* Nasty failure, quit now */ + else /* error == number of vectors we should try again with */ + msix_intrs = retval; + } + if (msix_intrs < adpt->min_msix_intrs) { + netif_info(adpt, probe, adpt->netdev, + "can't enable MSI-X interrupts\n"); + CLI_ADPT_FLAG(0, MSIX_EN); + kfree(adpt->msix_entries); + adpt->msix_entries = NULL; + goto try_msi_mode; + } + + netif_info(adpt, probe, adpt->netdev, + "enable MSI-X interrupts, num_msix_intrs = %d\n", + msix_intrs); + SET_ADPT_FLAG(0, MSIX_EN); + if (CHK_ADPT_FLAG(0, SRSS_CAP)) + SET_ADPT_FLAG(0, SRSS_EN); + + adpt->num_msix_intrs = min_t(int, msix_intrs, adpt->max_msix_intrs); + retval = 0; + return retval; + +try_msi_mode: + CLI_ADPT_FLAG(0, SRSS_CAP); + CLI_ADPT_FLAG(0, SRSS_EN); + alx_set_num_queues(adpt); + retval = -1; + return retval; +} + + +/* set msi interrupt mode */ +static int alx_set_msi_interrupt_mode(struct alx_adapter *adpt) +{ + int retval; + + retval = pci_enable_msi(adpt->pdev); + if (retval) { + netif_info(adpt, probe, adpt->netdev, + "can't enable MSI interrupt, error = %d\n", retval); + return retval; + } + SET_ADPT_FLAG(0, MSI_EN); + return retval; +} + + +/* set interrupt mode */ +static int alx_set_interrupt_mode(struct alx_adapter *adpt) +{ + int retval = 0; + + if (CHK_ADPT_FLAG(0, MSIX_CAP)) { + netif_info(adpt, probe, adpt->netdev, + "try to set MSIX interrupt\n"); + retval = alx_set_msix_interrupt_mode(adpt); + if (!retval) + return retval; + } + + if (CHK_ADPT_FLAG(0, MSI_CAP)) { + netif_info(adpt, probe, adpt->netdev, + "try to set MSI interrupt\n"); + retval = alx_set_msi_interrupt_mode(adpt); + if (!retval) + return retval; + } + + netif_info(adpt, probe, adpt->netdev, + "can't enable MSIX and MSI, will enable shared interrupt\n"); + retval = 0; + return retval; +} + + +static void alx_reset_interrupt_mode(struct alx_adapter *adpt) +{ + if (CHK_ADPT_FLAG(0, MSIX_EN)) { + CLI_ADPT_FLAG(0, MSIX_EN); + pci_disable_msix(adpt->pdev); + kfree(adpt->msix_entries); + adpt->msix_entries = NULL; + } else if (CHK_ADPT_FLAG(0, MSI_EN)) { + CLI_ADPT_FLAG(0, MSI_EN); + pci_disable_msi(adpt->pdev); + } +} + + +static int __devinit alx_init_adapter_special(struct alx_adapter *adpt) +{ + switch (adpt->hw.mac_type) { + case alx_mac_l1f: + case alx_mac_l2f: + goto init_alf_adapter; + break; + case alx_mac_l1c: + case alx_mac_l1d_v1: + case alx_mac_l1d_v2: + case alx_mac_l2c: + case alx_mac_l2cb_v1: + case alx_mac_l2cb_v20: + case alx_mac_l2cb_v21: + goto init_alc_adapter; + break; + default: + break; + } + return -1; + +init_alc_adapter: + if (CHK_ADPT_FLAG(0, MSIX_CAP)) + alx_err(adpt, "ALC doesn't support MSIX\n"); + + /* msi for tx, rx and none queues */ + adpt->num_msix_txques = 0; + adpt->num_msix_rxques = 0; + adpt->num_msix_noques = 0; + return 0; + +init_alf_adapter: + if (CHK_ADPT_FLAG(0, MSIX_CAP)) { + /* msix for tx, rx and none queues */ + adpt->num_msix_txques = 4; + adpt->num_msix_rxques = 8; + adpt->num_msix_noques = ALF_MAX_MSIX_NOQUE_INTRS; + + /* msix vector range */ + adpt->max_msix_intrs = ALF_MAX_MSIX_INTRS; + adpt->min_msix_intrs = ALF_MIN_MSIX_INTRS; + } else { + /* msi for tx, rx and none queues */ + adpt->num_msix_txques = 0; + adpt->num_msix_rxques = 0; + adpt->num_msix_noques = 0; + } + return 0; + +} + + +/* + * alx_init_adapter + */ +static int __devinit alx_init_adapter(struct alx_adapter *adpt) +{ + struct alx_hw *hw = &adpt->hw; + struct pci_dev *pdev = adpt->pdev; + u16 revision; + int max_frame; + + /* PCI config space info */ + hw->pci_venid = pdev->vendor; + hw->pci_devid = pdev->device; + alx_cfg_r16(hw, PCI_CLASS_REVISION, &revision); + hw->pci_revid = revision & 0xFF; + hw->pci_sub_venid = pdev->subsystem_vendor; + hw->pci_sub_devid = pdev->subsystem_device; + + if (alx_init_hw_callbacks(adpt) != 0) { + alx_err(adpt, "set HW function pointers failed\n"); + return -1; + } + + if (hw->cbs.identify_nic(hw) != 0) { + alx_err(adpt, "HW is disabled\n"); + return -1; + } + + /* Set adapter flags */ + switch (hw->mac_type) { + case alx_mac_l1f: + case alx_mac_l2f: +#ifdef CONFIG_ALX_MSI + SET_ADPT_FLAG(0, MSI_CAP); +#endif +#ifdef CONFIG_ALX_MSIX + SET_ADPT_FLAG(0, MSIX_CAP); +#endif + if (CHK_ADPT_FLAG(0, MSIX_CAP)) { + SET_ADPT_FLAG(0, FIXED_MSIX); + SET_ADPT_FLAG(0, MRQ_CAP); +#ifdef CONFIG_ALX_RSS + SET_ADPT_FLAG(0, SRSS_CAP); +#endif + } + pdev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG; + break; + case alx_mac_l1c: + case alx_mac_l1d_v1: + case alx_mac_l1d_v2: + case alx_mac_l2c: + case alx_mac_l2cb_v1: + case alx_mac_l2cb_v20: + case alx_mac_l2cb_v21: +#ifdef CONFIG_ALX_MSI + SET_ADPT_FLAG(0, MSI_CAP); +#endif + break; + default: + break; + } + + /* set default for alx_adapter */ + adpt->max_msix_intrs = 1; + adpt->min_msix_intrs = 1; + max_frame = adpt->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + adpt->rxbuf_size = adpt->netdev->mtu > ALX_DEF_RX_BUF_SIZE ? + ALIGN(max_frame, 8) : ALX_DEF_RX_BUF_SIZE; + adpt->wol = 0; + device_set_wakeup_enable(&pdev->dev, false); + + /* set default for alx_hw */ + hw->link_up = false; + hw->link_speed = ALX_LINK_SPEED_UNKNOWN; + hw->preamble = 7; + hw->intr_mask = ALX_IMR_NORMAL_MASK; + hw->smb_timer = 400; /* 400ms */ + hw->mtu = adpt->netdev->mtu; + hw->imt = 100; /* set to 200us */ + + /* set default for wrr */ + hw->wrr_prio0 = 4; + hw->wrr_prio1 = 4; + hw->wrr_prio2 = 4; + hw->wrr_prio3 = 4; + hw->wrr_mode = alx_wrr_mode_none; + + /* set default flow control settings */ + hw->req_fc_mode = alx_fc_full; + hw->cur_fc_mode = alx_fc_full; /* init for ethtool output */ + hw->disable_fc_autoneg = false; + hw->fc_was_autonegged = false; + hw->fc_single_pause = true; + + /* set default for rss info*/ + hw->rss_hstype = 0; + hw->rss_mode = alx_rss_mode_disable; + hw->rss_idt_size = 0; + hw->rss_base_cpu = 0; + memset(hw->rss_idt, 0x0, sizeof(hw->rss_idt)); + memset(hw->rss_key, 0x0, sizeof(hw->rss_key)); + + atomic_set(&adpt->irq_sem, 1); + spin_lock_init(&adpt->tx_lock); + spin_lock_init(&adpt->rx_lock); + + alx_init_adapter_special(adpt); + + if (hw->cbs.init_phy) { + if (hw->cbs.init_phy(hw)) + return -EINVAL; + } + + SET_ADPT_FLAG(1, STATE_DOWN); + return 0; +} + + +static int alx_set_register_info_special(struct alx_adapter *adpt) +{ + struct alx_hw *hw = &adpt->hw; + int num_txques = adpt->num_txques; + + switch (adpt->hw.mac_type) { + case alx_mac_l1f: + case alx_mac_l2f: + goto cache_alf_register; + break; + case alx_mac_l1c: + case alx_mac_l1d_v1: + case alx_mac_l1d_v2: + case alx_mac_l2c: + case alx_mac_l2cb_v1: + case alx_mac_l2cb_v20: + case alx_mac_l2cb_v21: + goto cache_alc_register; + break; + default: + break; + } + return -1; + +cache_alc_register: + /* setting for Produce Index and Consume Index */ + adpt->rx_queue[0]->produce_reg = hw->rx_prod_reg[0]; + adpt->rx_queue[0]->consume_reg = hw->rx_cons_reg[0]; + + switch (num_txques) { + case 2: + adpt->tx_queue[1]->produce_reg = hw->tx_prod_reg[1]; + adpt->tx_queue[1]->consume_reg = hw->tx_cons_reg[1]; + case 1: + adpt->tx_queue[0]->produce_reg = hw->tx_prod_reg[0]; + adpt->tx_queue[0]->consume_reg = hw->tx_cons_reg[0]; + break; + } + return 0; + +cache_alf_register: + /* setting for Produce Index and Consume Index */ + adpt->rx_queue[0]->produce_reg = hw->rx_prod_reg[0]; + adpt->rx_queue[0]->consume_reg = hw->rx_cons_reg[0]; + + switch (num_txques) { + case 4: + adpt->tx_queue[3]->produce_reg = hw->tx_prod_reg[3]; + adpt->tx_queue[3]->consume_reg = hw->tx_cons_reg[3]; + case 3: + adpt->tx_queue[2]->produce_reg = hw->tx_prod_reg[2]; + adpt->tx_queue[2]->consume_reg = hw->tx_cons_reg[2]; + case 2: + adpt->tx_queue[1]->produce_reg = hw->tx_prod_reg[1]; + adpt->tx_queue[1]->consume_reg = hw->tx_cons_reg[1]; + case 1: + adpt->tx_queue[0]->produce_reg = hw->tx_prod_reg[0]; + adpt->tx_queue[0]->consume_reg = hw->tx_cons_reg[0]; + } + return 0; +} + + +/* alx_alloc_tx_descriptor - allocate Tx Descriptors */ +static int alx_alloc_tx_descriptor(struct alx_adapter *adpt, + struct alx_tx_queue *txque) +{ + struct alx_ring_header *ring_header = &adpt->ring_header; + int size; + + netif_info(adpt, ifup, adpt->netdev, + "tpq.count = %d\n", txque->tpq.count); + + size = sizeof(struct alx_buffer) * txque->tpq.count; + txque->tpq.tpbuff = kzalloc(size, GFP_KERNEL); + if (!txque->tpq.tpbuff) + goto err_alloc_tpq_buffer; + + /* round up to nearest 4K */ + txque->tpq.size = txque->tpq.count * sizeof(union alx_tpdesc); + + txque->tpq.tpdma = ring_header->dma + ring_header->used; + txque->tpq.tpdesc = ring_header->desc + ring_header->used; + adpt->hw.tpdma[txque->que_idx] = (u64)txque->tpq.tpdma; + ring_header->used += ALIGN(txque->tpq.size, 8); + + txque->tpq.produce_idx = 0; + txque->tpq.consume_idx = 0; + txque->max_packets = txque->tpq.count; + return 0; + +err_alloc_tpq_buffer: + alx_err(adpt, "Unable to allocate memory for the Tx descriptor\n"); + return -ENOMEM; +} + + +/* alx_alloc_all_tx_descriptor - allocate all Tx Descriptors */ +static int alx_alloc_all_tx_descriptor(struct alx_adapter *adpt) +{ + int i, retval = 0; + netif_info(adpt, ifup, adpt->netdev, + "num_tques = %d\n", adpt->num_txques); + + for (i = 0; i < adpt->num_txques; i++) { + retval = alx_alloc_tx_descriptor(adpt, adpt->tx_queue[i]); + if (!retval) + continue; + + alx_err(adpt, "Allocation for Tx Queue %u failed\n", i); + break; + } + + return retval; +} + + +/* alx_alloc_rx_descriptor - allocate Rx Descriptors */ +static int alx_alloc_rx_descriptor(struct alx_adapter *adpt, + struct alx_rx_queue *rxque) +{ + struct alx_ring_header *ring_header = &adpt->ring_header; + int size; + + netif_info(adpt, ifup, adpt->netdev, + "RRD.count = %d, RFD.count = %d, SWD.count = %d\n", + rxque->rrq.count, rxque->rfq.count, rxque->swq.count); + + if (CHK_RX_FLAG(HW_QUE)) { + /* alloc buffer info */ + size = sizeof(struct alx_buffer) * rxque->rfq.count; + rxque->rfq.rfbuff = kzalloc(size, GFP_KERNEL); + if (!rxque->rfq.rfbuff) + goto err_alloc_rfq_buffer; + /* + * set dma's point of rrq and rfq + */ + + /* Round up to nearest 4K */ + rxque->rrq.size = + rxque->rrq.count * sizeof(union alx_rrdesc); + rxque->rfq.size = + rxque->rfq.count * sizeof(union alx_rfdesc); + + rxque->rrq.rrdma = ring_header->dma + ring_header->used; + rxque->rrq.rrdesc = ring_header->desc + ring_header->used; + adpt->hw.rrdma[rxque->que_idx] = (u64)rxque->rrq.rrdma; + ring_header->used += ALIGN(rxque->rrq.size, 8); + + rxque->rfq.rfdma = ring_header->dma + ring_header->used; + rxque->rfq.rfdesc = ring_header->desc + ring_header->used; + adpt->hw.rfdma[rxque->que_idx] = (u64)rxque->rfq.rfdma; + ring_header->used += ALIGN(rxque->rfq.size, 8); + + /* clean all counts within rxque */ + rxque->rrq.produce_idx = 0; + rxque->rrq.consume_idx = 0; + + rxque->rfq.produce_idx = 0; + rxque->rfq.consume_idx = 0; + } + + if (CHK_RX_FLAG(SW_QUE)) { + size = sizeof(struct alx_sw_buffer) * rxque->swq.count; + rxque->swq.swbuff = kzalloc(size, GFP_KERNEL); + if (!rxque->swq.swbuff) + goto err_alloc_swq_buffer; + + rxque->swq.consume_idx = 0; + rxque->swq.produce_idx = 0; + } + + rxque->max_packets = rxque->rrq.count / 2; + return 0; + +err_alloc_swq_buffer: + kfree(rxque->rfq.rfbuff); + rxque->rfq.rfbuff = NULL; +err_alloc_rfq_buffer: + alx_err(adpt, "Unable to allocate memory for the Rx descriptor\n"); + return -ENOMEM; +} + + +/* alx_alloc_all_rx_descriptor - allocate all Rx Descriptors */ +static int alx_alloc_all_rx_descriptor(struct alx_adapter *adpt) +{ + int i, error = 0; + + for (i = 0; i < adpt->num_rxques; i++) { + error = alx_alloc_rx_descriptor(adpt, adpt->rx_queue[i]); + if (!error) + continue; + alx_err(adpt, "Allocation for Rx Queue %u failed\n", i); + break; + } + + return error; +} + + +/* alx_free_tx_descriptor - Free Tx Descriptor */ +static void alx_free_tx_descriptor(struct alx_tx_queue *txque) +{ + alx_clean_tx_queue(txque); + + kfree(txque->tpq.tpbuff); + txque->tpq.tpbuff = NULL; + + /* if not set, then don't free */ + if (!txque->tpq.tpdesc) + return; + txque->tpq.tpdesc = NULL; +} + + +/* alx_free_all_tx_descriptor - Free all Tx Descriptor */ +static void alx_free_all_tx_descriptor(struct alx_adapter *adpt) +{ + int i; + + for (i = 0; i < adpt->num_txques; i++) + alx_free_tx_descriptor(adpt->tx_queue[i]); +} + + +/* alx_free_all_rx_descriptor - Free all Rx Descriptor */ +static void alx_free_rx_descriptor(struct alx_rx_queue *rxque) +{ + alx_clean_rx_queue(rxque); + + if (CHK_RX_FLAG(HW_QUE)) { + kfree(rxque->rfq.rfbuff); + rxque->rfq.rfbuff = NULL; + + /* if not set, then don't free */ + if (!rxque->rrq.rrdesc) + return; + rxque->rrq.rrdesc = NULL; + + if (!rxque->rfq.rfdesc) + return; + rxque->rfq.rfdesc = NULL; + } + + if (CHK_RX_FLAG(SW_QUE)) { + kfree(rxque->swq.swbuff); + rxque->swq.swbuff = NULL; + } +} + + +/* alx_free_all_rx_descriptor - Free all Rx Descriptor */ +static void alx_free_all_rx_descriptor(struct alx_adapter *adpt) +{ + int i; + for (i = 0; i < adpt->num_rxques; i++) + alx_free_rx_descriptor(adpt->rx_queue[i]); +} + + +/* + * alx_alloc_all_rtx_descriptor - allocate Tx / RX descriptor queues + */ +static int alx_alloc_all_rtx_descriptor(struct alx_adapter *adpt) +{ + struct device *dev = &adpt->pdev->dev; + struct alx_ring_header *ring_header = &adpt->ring_header; + int num_tques = adpt->num_txques; + int num_rques = adpt->num_hw_rxques; + unsigned int num_tx_descs = adpt->num_txdescs; + unsigned int num_rx_descs = adpt->num_rxdescs; + int retval; + + /* + * real ring DMA buffer + * each ring/block may need up to 8 bytes for alignment, hence the + * additional bytes tacked onto the end. + */ + ring_header->size = + num_tques * num_tx_descs * sizeof(union alx_tpdesc) + + num_rques * num_rx_descs * sizeof(union alx_rfdesc) + + num_rques * num_rx_descs * sizeof(union alx_rrdesc) + + sizeof(struct coals_msg_block) + + sizeof(struct alx_hw_stats) + + num_tques * 8 + num_rques * 2 * 8 + 8 * 2; + netif_info(adpt, ifup, adpt->netdev, + "num_tques = %d, num_tx_descs = %d\n", + num_tques, num_tx_descs); + netif_info(adpt, ifup, adpt->netdev, + "num_rques = %d, num_rx_descs = %d\n", + num_rques, num_rx_descs); + + ring_header->used = 0; + ring_header->desc = dma_alloc_coherent(dev, ring_header->size, + &ring_header->dma, GFP_KERNEL); + + if (!ring_header->desc) { + alx_err(adpt, "dma_alloc_coherent failed\n"); + retval = -ENOMEM; + goto err_alloc_dma; + } + memset(ring_header->desc, 0, ring_header->size); + ring_header->used = ALIGN(ring_header->dma, 8) - ring_header->dma; + + netif_info(adpt, ifup, adpt->netdev, + "ring header: size = %d, used= %d\n", + ring_header->size, ring_header->used); + + /* allocate receive descriptors */ + retval = alx_alloc_all_tx_descriptor(adpt); + if (retval) + goto err_alloc_tx; + + /* allocate receive descriptors */ + retval = alx_alloc_all_rx_descriptor(adpt); + if (retval) + goto err_alloc_rx; + + /* Init CMB dma address */ + adpt->cmb.dma = ring_header->dma + ring_header->used; + adpt->cmb.cmb = (u8 *) ring_header->desc + ring_header->used; + ring_header->used += ALIGN(sizeof(struct coals_msg_block), 8); + + adpt->smb.dma = ring_header->dma + ring_header->used; + adpt->smb.smb = (u8 *)ring_header->desc + ring_header->used; + ring_header->used += ALIGN(sizeof(struct alx_hw_stats), 8); + + return 0; + +err_alloc_rx: + alx_free_all_rx_descriptor(adpt); +err_alloc_tx: + alx_free_all_tx_descriptor(adpt); +err_alloc_dma: + return retval; +} + + +/* + * alx_alloc_all_rtx_descriptor - allocate Tx / RX descriptor queues + */ +static void alx_free_all_rtx_descriptor(struct alx_adapter *adpt) +{ + struct pci_dev *pdev = adpt->pdev; + struct alx_ring_header *ring_header = &adpt->ring_header; + + alx_free_all_tx_descriptor(adpt); + alx_free_all_rx_descriptor(adpt); + + adpt->cmb.dma = 0; + adpt->cmb.cmb = NULL; + adpt->smb.dma = 0; + adpt->smb.smb = NULL; + + pci_free_consistent(pdev, ring_header->size, ring_header->desc, + ring_header->dma); + ring_header->desc = NULL; + ring_header->size = ring_header->used = 0; +} + + +static netdev_features_t alx_fix_features(struct net_device *netdev, + netdev_features_t features) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + /* + * Since there is no support for separate rx/tx vlan accel + * enable/disable make sure tx flag is always in same state as rx. + */ + if (features & NETIF_F_HW_VLAN_RX) + features |= NETIF_F_HW_VLAN_TX; + else + features &= ~NETIF_F_HW_VLAN_TX; + + if (netdev->mtu > ALX_MAX_TSO_PKT_SIZE || + adpt->hw.mac_type == alx_mac_l1c || + adpt->hw.mac_type == alx_mac_l2c) + features &= ~(NETIF_F_TSO | NETIF_F_TSO6); + + return features; +} + + +static int alx_set_features(struct net_device *netdev, + netdev_features_t features) +{ + netdev_features_t changed = netdev->features ^ features; + + if (changed & NETIF_F_HW_VLAN_RX) + alx_vlan_mode(netdev, features); + return 0; +} +/* + * alx_change_mtu - Change the Maximum Transfer Unit + */ +static int alx_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + int old_mtu = netdev->mtu; + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((max_frame < ALX_MIN_ETH_FRAME_SIZE) || + (max_frame > ALX_MAX_ETH_FRAME_SIZE)) { + alx_err(adpt, "invalid MTU setting\n"); + return -EINVAL; + } + /* set MTU */ + if (old_mtu != new_mtu && netif_running(netdev)) { + netif_info(adpt, hw, adpt->netdev, + "changing MTU from %d to %d\n", + netdev->mtu, new_mtu); + netdev->mtu = new_mtu; + adpt->hw.mtu = new_mtu; + adpt->rxbuf_size = new_mtu > ALX_DEF_RX_BUF_SIZE ? + ALIGN(max_frame, 8) : ALX_DEF_RX_BUF_SIZE; + netdev_update_features(netdev); + alx_reinit_locked(adpt); + } + + return 0; +} + + +static int alx_open_internal(struct alx_adapter *adpt, u32 ctrl) +{ + struct alx_hw *hw = &adpt->hw; + int retval = 0; + int i; + + alx_init_ring_ptrs(adpt); + + alx_set_multicase_list(adpt->netdev); + alx_restore_vlan(adpt); + + if (hw->cbs.config_mac) + retval = hw->cbs.config_mac(hw, adpt->rxbuf_size, + adpt->num_hw_rxques, adpt->num_rxdescs, + adpt->num_txques, adpt->num_txdescs); + + if (hw->cbs.config_tx) + retval = hw->cbs.config_tx(hw); + + if (hw->cbs.config_rx) + retval = hw->cbs.config_rx(hw); + + alx_config_rss(adpt); + + for (i = 0; i < adpt->num_hw_rxques; i++) + alx_refresh_rx_buffer(adpt->rx_queue[i]); + + /* configure HW regsiters of MSIX */ + if (hw->cbs.config_msix) + retval = hw->cbs.config_msix(hw, adpt->num_msix_intrs, + CHK_ADPT_FLAG(0, MSIX_EN), + CHK_ADPT_FLAG(0, MSI_EN)); + + if (ctrl & ALX_OPEN_CTRL_IRQ_EN) { + retval = alx_request_irq(adpt); + if (retval) + goto err_request_irq; + } + + /* enable NAPI, INTR and TX */ + alx_napi_enable_all(adpt); + + alx_enable_intr(adpt); + + netif_tx_start_all_queues(adpt->netdev); + + CLI_ADPT_FLAG(1, STATE_DOWN); + + /* check link status */ + SET_ADPT_FLAG(0, TASK_LSC_REQ); + adpt->link_jiffies = jiffies + ALX_TRY_LINK_TIMEOUT; + mod_timer(&adpt->alx_timer, jiffies); + + return retval; + +err_request_irq: + alx_clean_all_rx_queues(adpt); + return retval; +} + + +static void alx_stop_internal(struct alx_adapter *adpt, u32 ctrl) +{ + struct net_device *netdev = adpt->netdev; + struct alx_hw *hw = &adpt->hw; + + SET_ADPT_FLAG(1, STATE_DOWN); + + netif_tx_stop_all_queues(netdev); + /* call carrier off first to avoid false dev_watchdog timeouts */ + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + alx_disable_intr(adpt); + + alx_napi_disable_all(adpt); + + if (ctrl & ALX_OPEN_CTRL_IRQ_EN) + alx_free_irq(adpt); + + CLI_ADPT_FLAG(0, TASK_LSC_REQ); + CLI_ADPT_FLAG(0, TASK_REINIT_REQ); + del_timer_sync(&adpt->alx_timer); + + if (ctrl & ALX_OPEN_CTRL_RESET_PHY) + hw->cbs.reset_phy(hw); + + if (ctrl & ALX_OPEN_CTRL_RESET_MAC) + hw->cbs.reset_mac(hw); + + adpt->hw.link_speed = ALX_LINK_SPEED_UNKNOWN; + + alx_clean_all_tx_queues(adpt); + alx_clean_all_rx_queues(adpt); +} + + +/* + * alx_open - Called when a network interface is made active + */ +static int alx_open(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + int retval; + + /* disallow open during test */ + if (CHK_ADPT_FLAG(1, STATE_TESTING) || + CHK_ADPT_FLAG(1, STATE_DIAG_RUNNING)) + return -EBUSY; + + netif_carrier_off(netdev); + + /* allocate rx/tx dma buffer & descriptors */ + retval = alx_alloc_all_rtx_descriptor(adpt); + if (retval) { + alx_err(adpt, "error in alx_alloc_all_rtx_descriptor\n"); + goto err_alloc_rtx; + } + + retval = alx_open_internal(adpt, ALX_OPEN_CTRL_IRQ_EN); + if (retval) + goto err_open_internal; + + return retval; + +err_open_internal: + alx_stop_internal(adpt, ALX_OPEN_CTRL_IRQ_EN); +err_alloc_rtx: + alx_free_all_rtx_descriptor(adpt); + hw->cbs.reset_mac(hw); + return retval; +} + + +/* + * alx_stop - Disables a network interface + */ +static int alx_stop(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + + if (CHK_ADPT_FLAG(1, STATE_RESETTING)) + netif_warn(adpt, ifdown, adpt->netdev, + "flag STATE_RESETTING has already set\n"); + + alx_stop_internal(adpt, ALX_OPEN_CTRL_IRQ_EN | + ALX_OPEN_CTRL_RESET_MAC); + alx_free_all_rtx_descriptor(adpt); + + return 0; +} + + +static int alx_shutdown_internal(struct pci_dev *pdev, bool *wakeup) +{ + struct alx_adapter *adpt = pci_get_drvdata(pdev); + struct net_device *netdev = adpt->netdev; + struct alx_hw *hw = &adpt->hw; + u32 wufc = adpt->wol; + u16 lpa; + u32 speed, adv_speed, misc; + bool link_up; + int i; + int retval = 0; + + hw->cbs.config_aspm(hw, false, false); + + netif_device_detach(netdev); + if (netif_running(netdev)) + alx_stop_internal(adpt, 0); + +#ifdef CONFIG_PM_SLEEP + retval = pci_save_state(pdev); + if (retval) + return retval; +#endif + hw->cbs.check_phy_link(hw, &speed, &link_up); + + if (link_up) { + if (hw->mac_type == alx_mac_l1f || + hw->mac_type == alx_mac_l2f) { + alx_mem_r32(hw, ALX_MISC, &misc); + misc |= ALX_MISC_INTNLOSC_OPEN; + alx_mem_w32(hw, ALX_MISC, misc); + } + + retval = hw->cbs.read_phy_reg(hw, MII_LPA, &lpa); + if (retval) + return retval; + + adv_speed = ALX_LINK_SPEED_10_HALF; + if (lpa & LPA_10FULL) + adv_speed = ALX_LINK_SPEED_10_FULL; + else if (lpa & LPA_10HALF) + adv_speed = ALX_LINK_SPEED_10_HALF; + else if (lpa & LPA_100FULL) + adv_speed = ALX_LINK_SPEED_100_FULL; + else if (lpa & LPA_100HALF) + adv_speed = ALX_LINK_SPEED_100_HALF; + + retval = hw->cbs.setup_phy_link(hw, adv_speed, true, + !hw->disable_fc_autoneg); + if (retval) + return retval; + + for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { + mdelay(100); + retval = hw->cbs.check_phy_link(hw, &speed, &link_up); + if (retval) + continue; + if (link_up) + break; + } + } else { + speed = ALX_LINK_SPEED_10_HALF; + link_up = false; + } + hw->link_speed = speed; + hw->link_up = link_up; + + retval = hw->cbs.config_wol(hw, wufc); + if (retval) + return retval; + + /* clear phy interrupt */ + retval = hw->cbs.ack_phy_intr(hw); + if (retval) + return retval; + + if (wufc) { + /* pcie patch */ + device_set_wakeup_enable(&pdev->dev, 1); + } + + retval = hw->cbs.config_pow_save(hw, adpt->hw.link_speed, + (wufc ? true : false), false, + (wufc & ALX_WOL_MAGIC ? true : false), true); + if (retval) + return retval; + + *wakeup = wufc ? true : false; + pci_disable_device(pdev); + return 0; +} + + +static void alx_shutdown(struct pci_dev *pdev) +{ + bool wakeup; + alx_shutdown_internal(pdev, &wakeup); + + pci_wake_from_d3(pdev, wakeup); + pci_set_power_state(pdev, PCI_D3hot); +} + + +#ifdef CONFIG_PM_SLEEP +static int alx_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int retval; + bool wakeup; + + retval = alx_shutdown_internal(pdev, &wakeup); + if (retval) + return retval; + + if (wakeup) { + pci_prepare_to_sleep(pdev); + } else { + pci_wake_from_d3(pdev, false); + pci_set_power_state(pdev, PCI_D3hot); + } + + return 0; +} + + +static int alx_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct alx_adapter *adpt = pci_get_drvdata(pdev); + struct net_device *netdev = adpt->netdev; + struct alx_hw *hw = &adpt->hw; + u32 retval; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + /* + * pci_restore_state clears dev->state_saved so call + * pci_save_state to restore it. + */ + pci_save_state(pdev); + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + retval = hw->cbs.reset_pcie(hw, true, true); + retval = hw->cbs.reset_phy(hw); + retval = hw->cbs.reset_mac(hw); + retval = hw->cbs.setup_phy_link(hw, hw->autoneg_advertised, true, + !hw->disable_fc_autoneg); + + retval = hw->cbs.config_wol(hw, 0); + + if (netif_running(netdev)) { + retval = alx_open_internal(adpt, 0); + if (retval) + return retval; + } + + netif_device_attach(netdev); + return 0; +} +#endif + + +/* + * alx_update_hw_stats - Update the board statistics counters. + */ +static void alx_update_hw_stats(struct alx_adapter *adpt) +{ + struct net_device_stats *net_stats; + struct alx_hw *hw = &adpt->hw; + struct alx_hw_stats *hwstats = &adpt->hw_stats; + unsigned long *hwstat_item = NULL; + u32 hwstat_reg; + u32 hwstat_data; + + if (CHK_ADPT_FLAG(1, STATE_DOWN) || CHK_ADPT_FLAG(1, STATE_RESETTING)) + return; + + /* update RX status */ + hwstat_reg = hw->rxstat_reg; + hwstat_item = &hwstats->rx_ok; + while (hwstat_reg < hw->rxstat_reg + hw->rxstat_sz) { + alx_mem_r32(hw, hwstat_reg, &hwstat_data); + *hwstat_item += hwstat_data; + hwstat_reg += 4; + hwstat_item++; + } + + /* update TX status */ + hwstat_reg = hw->txstat_reg; + hwstat_item = &hwstats->tx_ok; + while (hwstat_reg < hw->txstat_reg + hw->txstat_sz) { + alx_mem_r32(hw, hwstat_reg, &hwstat_data); + *hwstat_item += hwstat_data; + hwstat_reg += 4; + hwstat_item++; + } + + net_stats = &adpt->netdev->stats; + net_stats->rx_packets = hwstats->rx_ok; + net_stats->tx_packets = hwstats->tx_ok; + net_stats->rx_bytes = hwstats->rx_byte_cnt; + net_stats->tx_bytes = hwstats->tx_byte_cnt; + net_stats->multicast = hwstats->rx_mcast; + net_stats->collisions = hwstats->tx_single_col + + hwstats->tx_multi_col * 2 + + hwstats->tx_late_col + hwstats->tx_abort_col; + + net_stats->rx_errors = hwstats->rx_frag + hwstats->rx_fcs_err + + hwstats->rx_len_err + hwstats->rx_ov_sz + + hwstats->rx_ov_rrd + hwstats->rx_align_err; + + net_stats->rx_fifo_errors = hwstats->rx_ov_rxf; + net_stats->rx_length_errors = hwstats->rx_len_err; + net_stats->rx_crc_errors = hwstats->rx_fcs_err; + net_stats->rx_frame_errors = hwstats->rx_align_err; + net_stats->rx_over_errors = hwstats->rx_ov_rrd + hwstats->rx_ov_rxf; + + net_stats->rx_missed_errors = hwstats->rx_ov_rrd + hwstats->rx_ov_rxf; + + net_stats->tx_errors = hwstats->tx_late_col + hwstats->tx_abort_col + + hwstats->tx_underrun + hwstats->tx_trunc; + net_stats->tx_fifo_errors = hwstats->tx_underrun; + net_stats->tx_aborted_errors = hwstats->tx_abort_col; + net_stats->tx_window_errors = hwstats->tx_late_col; +} + + +/* + * alx_get_stats - Get System Network Statistics + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + */ +static struct net_device_stats *alx_get_stats(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + + alx_update_hw_stats(adpt); + return &netdev->stats; +} + + +static void alx_link_task_routine(struct alx_adapter *adpt) +{ + struct net_device *netdev = adpt->netdev; + struct alx_hw *hw = &adpt->hw; + char *link_desc; + + if (!CHK_ADPT_FLAG(0, TASK_LSC_REQ)) + return; + CLI_ADPT_FLAG(0, TASK_LSC_REQ); + + if (CHK_ADPT_FLAG(1, STATE_DOWN)) + return; + + if (hw->cbs.check_phy_link) { + hw->cbs.check_phy_link(hw, + &hw->link_speed, &hw->link_up); + } else { + /* always assume link is up, if no check link function */ + hw->link_speed = ALX_LINK_SPEED_1GB_FULL; + hw->link_up = true; + } + netif_info(adpt, timer, adpt->netdev, + "link_speed = %d, link_up = %d\n", + hw->link_speed, hw->link_up); + + if (!hw->link_up && time_after(adpt->link_jiffies, jiffies)) + SET_ADPT_FLAG(0, TASK_LSC_REQ); + + if (hw->link_up) { + if (netif_carrier_ok(netdev)) + return; + + link_desc = (hw->link_speed == ALX_LINK_SPEED_1GB_FULL) ? + "1 Gbps Duplex Full" : + (hw->link_speed == ALX_LINK_SPEED_100_FULL ? + "100 Mbps Duplex Full" : + (hw->link_speed == ALX_LINK_SPEED_100_HALF ? + "100 Mbps Duplex Half" : + (hw->link_speed == ALX_LINK_SPEED_10_FULL ? + "10 Mbps Duplex Full" : + (hw->link_speed == ALX_LINK_SPEED_10_HALF ? + "10 Mbps Duplex HALF" : + "unknown speed")))); + netif_info(adpt, timer, adpt->netdev, + "NIC Link is Up %s\n", link_desc); + + hw->cbs.config_aspm(hw, true, true); + hw->cbs.start_mac(hw); + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + } else { + /* only continue if link was up previously */ + if (!netif_carrier_ok(netdev)) + return; + + hw->link_speed = 0; + netif_info(adpt, timer, adpt->netdev, "NIC Link is Down\n"); + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); + + hw->cbs.stop_mac(hw); + hw->cbs.config_aspm(hw, false, true); + hw->cbs.setup_phy_link(hw, hw->autoneg_advertised, true, + !hw->disable_fc_autoneg); + } +} + + +static void alx_reinit_task_routine(struct alx_adapter *adpt) +{ + if (!CHK_ADPT_FLAG(0, TASK_REINIT_REQ)) + return; + CLI_ADPT_FLAG(0, TASK_REINIT_REQ); + + if (CHK_ADPT_FLAG(1, STATE_DOWN) || CHK_ADPT_FLAG(1, STATE_RESETTING)) + return; + + alx_reinit_locked(adpt); +} + + +/* + * alx_timer_routine - Timer Call-back + */ +static void alx_timer_routine(unsigned long data) +{ + struct alx_adapter *adpt = (struct alx_adapter *)data; + unsigned long delay; + + /* poll faster when waiting for link */ + if (CHK_ADPT_FLAG(0, TASK_LSC_REQ)) + delay = HZ / 10; + else + delay = HZ * 2; + + /* Reset the timer */ + mod_timer(&adpt->alx_timer, delay + jiffies); + + alx_task_schedule(adpt); +} + + +/* + * alx_task_routine - manages and runs subtasks + */ +static void alx_task_routine(struct work_struct *work) +{ + struct alx_adapter *adpt = container_of(work, + struct alx_adapter, alx_task); + /* test state of adapter */ + if (!CHK_ADPT_FLAG(1, STATE_WATCH_DOG)) + netif_warn(adpt, timer, adpt->netdev, + "flag STATE_WATCH_DOG doesn't set\n"); + + /* reinit task */ + alx_reinit_task_routine(adpt); + + /* link task */ + alx_link_task_routine(adpt); + + /* flush memory to make sure state is correct before next watchog */ + smp_mb__before_clear_bit(); + + CLI_ADPT_FLAG(1, STATE_WATCH_DOG); +} + + +/* Calculate the transmit packet descript needed*/ +static bool alx_check_num_tpdescs(struct alx_tx_queue *txque, + const struct sk_buff *skb) +{ + u16 num_required = 1; + u16 num_available = 0; + u16 produce_idx = txque->tpq.produce_idx; + u16 consume_idx = txque->tpq.consume_idx; + int i = 0; + + u16 proto_hdr_len = 0; + if (skb_is_gso(skb)) { + proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + if (proto_hdr_len < skb_headlen(skb)) + num_required++; + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + num_required++; + } + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) + num_required++; + num_available = (u16)(consume_idx > produce_idx) ? + (consume_idx - produce_idx - 1) : + (txque->tpq.count + consume_idx - produce_idx - 1); + + return num_required < num_available; +} + + +static int alx_tso_csum(struct alx_adapter *adpt, + struct alx_tx_queue *txque, + struct sk_buff *skb, + union alx_sw_tpdesc *stpd) +{ + struct pci_dev *pdev = adpt->pdev; + u8 hdr_len; + int retval; + + if (skb_is_gso(skb)) { + if (skb_header_cloned(skb)) { + retval = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (unlikely(retval)) + return retval; + } + + if (skb->protocol == htons(ETH_P_IP)) { + u32 pkt_len = + ((unsigned char *)ip_hdr(skb) - skb->data) + + ntohs(ip_hdr(skb)->tot_len); + if (skb->len > pkt_len) + pskb_trim(skb, pkt_len); + } + + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + if (unlikely(skb->len == hdr_len)) { + /* we only need to do csum */ + dev_warn(&pdev->dev, + "tso doesn't need, if packet with 0 data\n"); + goto do_csum; + } + + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { + ip_hdr(skb)->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic( + ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + stpd->genr.ipv4 = 1; + } + + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { + /* ipv6 tso need an extra tpd */ + union alx_sw_tpdesc extra_tpd; + + memset(stpd, 0, sizeof(union alx_sw_tpdesc)); + memset(&extra_tpd, 0, sizeof(union alx_sw_tpdesc)); + + ipv6_hdr(skb)->payload_len = 0; + tcp_hdr(skb)->check = ~csum_ipv6_magic( + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + extra_tpd.tso.addr_lo = skb->len; + extra_tpd.tso.lso = 0x1; + extra_tpd.tso.lso_v2 = 0x1; + alx_set_tpdesc(txque, &extra_tpd); + stpd->tso.lso_v2 = 0x1; + } + + stpd->tso.lso = 0x1; + stpd->tso.tcphdr_offset = skb_transport_offset(skb); + stpd->tso.mss = skb_shinfo(skb)->gso_size; + return 0; + } + +do_csum: + if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + u8 css, cso; + cso = skb_checksum_start_offset(skb); + + if (unlikely(cso & 0x1)) { + dev_err(&pdev->dev, "pay load offset should not be an " + "event number\n"); + return -1; + } else { + css = cso + skb->csum_offset; + + stpd->csum.payld_offset = cso >> 1; + stpd->csum.cxsum_offset = css >> 1; + stpd->csum.c_sum = 0x1; + } + } + return 0; +} + + +static void alx_tx_map(struct alx_adapter *adpt, + struct alx_tx_queue *txque, + struct sk_buff *skb, + union alx_sw_tpdesc *stpd) +{ + struct alx_buffer *tpbuf = NULL; + + unsigned int nr_frags = skb_shinfo(skb)->nr_frags; + + unsigned int len = skb_headlen(skb); + + u16 map_len = 0; + u16 mapped_len = 0; + u16 hdr_len = 0; + u16 f; + u32 tso = stpd->tso.lso; + + if (tso) { + /* TSO */ + map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + + tpbuf = GET_TP_BUFFER(txque, txque->tpq.produce_idx); + tpbuf->length = map_len; + tpbuf->dma = dma_map_single(txque->dev, + skb->data, hdr_len, DMA_TO_DEVICE); + mapped_len += map_len; + stpd->genr.addr = tpbuf->dma; + stpd->genr.buffer_len = tpbuf->length; + + alx_set_tpdesc(txque, stpd); + } + + if (mapped_len < len) { + tpbuf = GET_TP_BUFFER(txque, txque->tpq.produce_idx); + tpbuf->length = len - mapped_len; + tpbuf->dma = + dma_map_single(txque->dev, skb->data + mapped_len, + tpbuf->length, DMA_TO_DEVICE); + stpd->genr.addr = tpbuf->dma; + stpd->genr.buffer_len = tpbuf->length; + alx_set_tpdesc(txque, stpd); + } + + for (f = 0; f < nr_frags; f++) { + struct skb_frag_struct *frag; + + frag = &skb_shinfo(skb)->frags[f]; + + tpbuf = GET_TP_BUFFER(txque, txque->tpq.produce_idx); + tpbuf->length = skb_frag_size(frag); + tpbuf->dma = skb_frag_dma_map(txque->dev, frag, 0, + tpbuf->length, DMA_TO_DEVICE); + stpd->genr.addr = tpbuf->dma; + stpd->genr.buffer_len = tpbuf->length; + alx_set_tpdesc(txque, stpd); + } + + + /* The last tpd */ + alx_set_tpdesc_lastfrag(txque); + /* + * The last buffer info contain the skb address, + * so it will be free after unmap + */ + tpbuf->skb = skb; +} + + +static netdev_tx_t alx_start_xmit_frame(struct alx_adapter *adpt, + struct alx_tx_queue *txque, + struct sk_buff *skb) +{ + struct alx_hw *hw = &adpt->hw; + unsigned long flags = 0; + union alx_sw_tpdesc stpd; /* normal*/ + + if (CHK_ADPT_FLAG(1, STATE_DOWN) || + CHK_ADPT_FLAG(1, STATE_DIAG_RUNNING)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (!spin_trylock_irqsave(&adpt->tx_lock, flags)) { + alx_err(adpt, "tx locked!\n"); + return NETDEV_TX_LOCKED; + } + + if (!alx_check_num_tpdescs(txque, skb)) { + /* no enough descriptor, just stop queue */ + netif_stop_queue(adpt->netdev); + spin_unlock_irqrestore(&adpt->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + memset(&stpd, 0, sizeof(union alx_sw_tpdesc)); + /* do TSO and check sum */ + if (alx_tso_csum(adpt, txque, skb, &stpd) != 0) { + spin_unlock_irqrestore(&adpt->tx_lock, flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (unlikely(vlan_tx_tag_present(skb))) { + u16 vlan = vlan_tx_tag_get(skb); + u16 tag; + ALX_VLAN_TO_TAG(vlan, tag); + stpd.genr.vlan_tag = tag; + stpd.genr.instag = 0x1; + } + + if (skb_network_offset(skb) != ETH_HLEN) + stpd.genr.type = 0x1; /* Ethernet frame */ + + alx_tx_map(adpt, txque, skb, &stpd); + + + /* update produce idx */ + wmb(); + alx_mem_w16(hw, txque->produce_reg, txque->tpq.produce_idx); + netif_info(adpt, tx_err, adpt->netdev, + "TX[%d]: tpq.consume_idx = 0x%x, tpq.produce_idx = 0x%x\n", + txque->que_idx, txque->tpq.consume_idx, + txque->tpq.produce_idx); + netif_info(adpt, tx_err, adpt->netdev, + "TX[%d]: Produce Reg[%x] = 0x%x\n", + txque->que_idx, txque->produce_reg, txque->tpq.produce_idx); + + spin_unlock_irqrestore(&adpt->tx_lock, flags); + return NETDEV_TX_OK; +} + + +static netdev_tx_t alx_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_tx_queue *txque; + + txque = adpt->tx_queue[0]; + return alx_start_xmit_frame(adpt, txque, skb); +} + + +/* + * alx_mii_ioctl + */ +static int alx_mii_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + struct alx_hw *hw = &adpt->hw; + struct mii_ioctl_data *data = if_mii(ifr); + int retval = 0; + + if (!netif_running(netdev)) + return -EINVAL; + + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 0; + break; + + case SIOCGMIIREG: + if (data->reg_num & ~(0x1F)) { + retval = -EFAULT; + goto out; + } + + retval = hw->cbs.read_phy_reg(hw, data->reg_num, + &data->val_out); + netif_dbg(adpt, hw, adpt->netdev, "read phy %02x %04x\n", + data->reg_num, data->val_out); + if (retval) { + retval = -EIO; + goto out; + } + break; + + case SIOCSMIIREG: + if (data->reg_num & ~(0x1F)) { + retval = -EFAULT; + goto out; + } + + retval = hw->cbs.write_phy_reg(hw, data->reg_num, data->val_in); + netif_dbg(adpt, hw, adpt->netdev, "write phy %02x %04x\n", + data->reg_num, data->val_in); + if (retval) { + retval = -EIO; + goto out; + } + break; + default: + retval = -EOPNOTSUPP; + break; + } +out: + return retval; + +} + + +static int alx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return alx_mii_ioctl(netdev, ifr, cmd); + default: + return -EOPNOTSUPP; + } +} + + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void alx_poll_controller(struct net_device *netdev) +{ + struct alx_adapter *adpt = netdev_priv(netdev); + int num_msix_intrs = adpt->num_msix_intrs; + int msix_idx; + + /* if interface is down do nothing */ + if (CHK_ADPT_FLAG(1, STATE_DOWN)) + return; + + if (CHK_ADPT_FLAG(0, MSIX_EN)) { + for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { + struct alx_msix_param *msix = adpt->msix[msix_idx]; + if (CHK_MSIX_FLAG(RXS) || CHK_MSIX_FLAG(TXS)) + alx_msix_rtx(0, msix); + else if (CHK_MSIX_FLAG(TIMER)) + alx_msix_timer(0, msix); + else if (CHK_MSIX_FLAG(ALERT)) + alx_msix_alert(0, msix); + else if (CHK_MSIX_FLAG(SMB)) + alx_msix_smb(0, msix); + else if (CHK_MSIX_FLAG(PHY)) + alx_msix_phy(0, msix); + } + } else { + alx_interrupt(adpt->pdev->irq, netdev); + } +} +#endif + + +static const struct net_device_ops alx_netdev_ops = { + .ndo_open = alx_open, + .ndo_stop = alx_stop, + .ndo_start_xmit = alx_start_xmit, + .ndo_get_stats = alx_get_stats, + .ndo_set_rx_mode = alx_set_multicase_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = alx_set_mac_address, + .ndo_change_mtu = alx_change_mtu, + .ndo_do_ioctl = alx_ioctl, + .ndo_tx_timeout = alx_tx_timeout, + .ndo_fix_features = alx_fix_features, + .ndo_set_features = alx_set_features, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = alx_poll_controller, +#endif +}; + + +/* + * alx_init - Device Initialization Routine + */ +static int __devinit alx_init(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct alx_adapter *adpt = NULL; + struct alx_hw *hw = NULL; + static int cards_found; + int retval; + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + retval = pci_enable_device_mem(pdev); + if (retval) { + dev_err(&pdev->dev, "cannot enable PCI device\n"); + goto err_alloc_device; + } + + /* + * The alx chip can DMA to 64-bit addresses, but it uses a single + * shared register for the high 32 bits, so only a single, aligned, + * 4 GB physical address range can be used at a time. + */ + if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) && + !dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) { + dev_info(&pdev->dev, "DMA to 64-BIT addresses\n"); + } else { + retval = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (retval) { + retval = dma_set_coherent_mask(&pdev->dev, + DMA_BIT_MASK(32)); + if (retval) { + dev_err(&pdev->dev, + "No usable DMA config, aborting\n"); + goto err_alloc_pci_res_mem; + } + } + } + + retval = pci_request_selected_regions(pdev, pci_select_bars(pdev, + IORESOURCE_MEM), alx_drv_name); + if (retval) { + dev_err(&pdev->dev, + "pci_request_selected_regions failed 0x%x\n", retval); + goto err_alloc_pci_res_mem; + } + + + pci_enable_pcie_error_reporting(pdev); + pci_set_master(pdev); + + netdev = alloc_etherdev(sizeof(struct alx_adapter)); + if (netdev == NULL) { + dev_err(&pdev->dev, "etherdev alloc failed\n"); + retval = -ENOMEM; + goto err_alloc_netdev; + } + + SET_NETDEV_DEV(netdev, &pdev->dev); + netdev->irq = pdev->irq; + + adpt = netdev_priv(netdev); + pci_set_drvdata(pdev, adpt); + adpt->netdev = netdev; + adpt->pdev = pdev; + hw = &adpt->hw; + hw->adpt = adpt; + adpt->msg_enable = ALX_MSG_DEFAULT; + + adpt->hw.hw_addr = ioremap(pci_resource_start(pdev, BAR_0), + pci_resource_len(pdev, BAR_0)); + if (!adpt->hw.hw_addr) { + alx_err(adpt, "cannot map device registers\n"); + retval = -EIO; + goto err_iomap; + } + netdev->base_addr = (unsigned long)adpt->hw.hw_addr; + + /* set cb member of netdev structure*/ + netdev->netdev_ops = &alx_netdev_ops; + alx_set_ethtool_ops(netdev); + netdev->watchdog_timeo = ALX_WATCHDOG_TIME; + strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); + + adpt->bd_number = cards_found; + + /* init alx_adapte structure */ + retval = alx_init_adapter(adpt); + if (retval) { + alx_err(adpt, "net device private data init failed\n"); + goto err_init_adapter; + } + + /* reset pcie */ + retval = hw->cbs.reset_pcie(hw, true, true); + if (retval) { + alx_err(adpt, "PCIE Reset failed, error = %d\n", retval); + retval = -EIO; + goto err_init_adapter; + } + + /* Init GPHY as early as possible due to power saving issue */ + retval = hw->cbs.reset_phy(hw); + if (retval) { + alx_err(adpt, "PHY Reset failed, error = %d\n", retval); + retval = -EIO; + goto err_init_adapter; + } + + /* reset mac */ + retval = hw->cbs.reset_mac(hw); + if (retval) { + alx_err(adpt, "MAC Reset failed, error = %d\n", retval); + retval = -EIO; + goto err_init_adapter; + } + + /* setup link to put it in a known good starting state */ + retval = hw->cbs.setup_phy_link(hw, hw->autoneg_advertised, true, + !hw->disable_fc_autoneg); + + /* get user settings */ + adpt->num_txdescs = 1024; + adpt->num_rxdescs = 512; + adpt->max_rxques = min_t(int, ALX_MAX_RX_QUEUES, num_online_cpus()); + adpt->max_txques = min_t(int, ALX_MAX_TX_QUEUES, num_online_cpus()); + + netdev->hw_features = NETIF_F_SG | + NETIF_F_HW_CSUM | + NETIF_F_HW_VLAN_RX; + if (adpt->hw.mac_type != alx_mac_l1c && + adpt->hw.mac_type != alx_mac_l2c) { + netdev->hw_features = netdev->hw_features | + NETIF_F_TSO | + NETIF_F_TSO6; + } + netdev->features = netdev->hw_features | + NETIF_F_HW_VLAN_TX; + + /* get mac addr and perm mac addr, set to register */ + if (hw->cbs.get_mac_addr) + retval = hw->cbs.get_mac_addr(hw, hw->mac_perm_addr); + else + retval = -EINVAL; + + if (retval) { + eth_hw_addr_random(netdev); + memcpy(hw->mac_perm_addr, netdev->dev_addr, netdev->addr_len); + } + + memcpy(hw->mac_addr, hw->mac_perm_addr, netdev->addr_len); + if (hw->cbs.set_mac_addr) + hw->cbs.set_mac_addr(hw, hw->mac_addr); + + memcpy(netdev->dev_addr, hw->mac_perm_addr, netdev->addr_len); + memcpy(netdev->perm_addr, hw->mac_perm_addr, netdev->addr_len); + retval = alx_validate_mac_addr(netdev->perm_addr); + if (retval) { + alx_err(adpt, "invalid MAC address\n"); + goto err_init_adapter; + } + + setup_timer(&adpt->alx_timer, &alx_timer_routine, + (unsigned long)adpt); + INIT_WORK(&adpt->alx_task, alx_task_routine); + + /* Number of supported queues */ + alx_set_num_queues(adpt); + retval = alx_set_interrupt_mode(adpt); + if (retval) { + alx_err(adpt, "can't set interrupt mode\n"); + goto err_set_interrupt_mode; + } + + retval = alx_set_interrupt_param(adpt); + if (retval) { + alx_err(adpt, "can't set interrupt parameter\n"); + goto err_set_interrupt_param; + } + + retval = alx_alloc_all_rtx_queue(adpt); + if (retval) { + alx_err(adpt, "can't allocate memory for queues\n"); + goto err_alloc_rtx_queue; + } + + alx_set_register_info_special(adpt); + + netif_dbg(adpt, probe, adpt->netdev, + "num_msix_noque_intrs = %d, num_msix_rxque_intrs = %d, " + "num_msix_txque_intrs = %d\n", + adpt->num_msix_noques, adpt->num_msix_rxques, + adpt->num_msix_txques); + netif_dbg(adpt, probe, adpt->netdev, "num_msix_all_intrs = %d\n", + adpt->num_msix_intrs); + + netif_dbg(adpt, probe, adpt->netdev, + "RX Queue Count = %u, HRX Queue Count = %u, " + "SRX Queue Count = %u, TX Queue Count = %u\n", + adpt->num_rxques, adpt->num_hw_rxques, adpt->num_sw_rxques, + adpt->num_txques); + + /* WOL not supported for all but the following */ + switch (hw->pci_devid) { + case ALX_DEV_ID_AR8131: + case ALX_DEV_ID_AR8132: + case ALX_DEV_ID_AR8151_V1: + case ALX_DEV_ID_AR8151_V2: + case ALX_DEV_ID_AR8152_V1: + case ALX_DEV_ID_AR8152_V2: + adpt->wol = (ALX_WOL_MAGIC | ALX_WOL_PHY); + break; + case ALX_DEV_ID_AR8161: + case ALX_DEV_ID_AR8162: + adpt->wol = (ALX_WOL_MAGIC | ALX_WOL_PHY); + break; + default: + adpt->wol = 0; + break; + } + device_set_wakeup_enable(&adpt->pdev->dev, adpt->wol); + + SET_ADPT_FLAG(1, STATE_DOWN); + strcpy(netdev->name, "eth%d"); + retval = register_netdev(netdev); + if (retval) { + alx_err(adpt, "register netdevice failed\n"); + goto err_register_netdev; + } + adpt->netdev_registered = true; + + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + /* keep stopping all the transmit queues for older kernels */ + netif_tx_stop_all_queues(netdev); + + /* print the MAC address */ + netif_info(adpt, probe, adpt->netdev, "%pM\n", netdev->dev_addr); + + /* print the adapter capability */ + if (CHK_ADPT_FLAG(0, MSI_CAP)) { + netif_info(adpt, probe, adpt->netdev, + "MSI Capable: %s\n", + CHK_ADPT_FLAG(0, MSI_EN) ? "Enable" : "Disable"); + } + if (CHK_ADPT_FLAG(0, MSIX_CAP)) { + netif_info(adpt, probe, adpt->netdev, + "MSIX Capable: %s\n", + CHK_ADPT_FLAG(0, MSIX_EN) ? "Enable" : "Disable"); + } + if (CHK_ADPT_FLAG(0, MRQ_CAP)) { + netif_info(adpt, probe, adpt->netdev, + "MRQ Capable: %s\n", + CHK_ADPT_FLAG(0, MRQ_EN) ? "Enable" : "Disable"); + } + if (CHK_ADPT_FLAG(0, MRQ_CAP)) { + netif_info(adpt, probe, adpt->netdev, + "MTQ Capable: %s\n", + CHK_ADPT_FLAG(0, MTQ_EN) ? "Enable" : "Disable"); + } + if (CHK_ADPT_FLAG(0, SRSS_CAP)) { + netif_info(adpt, probe, adpt->netdev, + "RSS(SW) Capable: %s\n", + CHK_ADPT_FLAG(0, SRSS_EN) ? "Enable" : "Disable"); + } + + printk(KERN_INFO "alx: Atheros Gigabit Network Connection\n"); + cards_found++; + return 0; + +err_register_netdev: + alx_free_all_rtx_queue(adpt); +err_alloc_rtx_queue: + alx_reset_interrupt_param(adpt); +err_set_interrupt_param: + alx_reset_interrupt_mode(adpt); +err_set_interrupt_mode: +err_init_adapter: + iounmap(adpt->hw.hw_addr); +err_iomap: + free_netdev(netdev); +err_alloc_netdev: + pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM)); +err_alloc_pci_res_mem: + pci_disable_device(pdev); +err_alloc_device: + dev_err(&pdev->dev, + "error when probe device, error = %d\n", retval); + return retval; +} + + +/* + * alx_remove - Device Removal Routine + */ +static void __devexit alx_remove(struct pci_dev *pdev) +{ + struct alx_adapter *adpt = pci_get_drvdata(pdev); + struct alx_hw *hw = &adpt->hw; + struct net_device *netdev = adpt->netdev; + + SET_ADPT_FLAG(1, STATE_DOWN); + cancel_work_sync(&adpt->alx_task); + + hw->cbs.config_pow_save(hw, ALX_LINK_SPEED_UNKNOWN, + false, false, false, false); + + /* resume permanent mac address */ + hw->cbs.set_mac_addr(hw, hw->mac_perm_addr); + + if (adpt->netdev_registered) { + unregister_netdev(netdev); + adpt->netdev_registered = false; + } + + alx_free_all_rtx_queue(adpt); + alx_reset_interrupt_param(adpt); + alx_reset_interrupt_mode(adpt); + + iounmap(adpt->hw.hw_addr); + pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM)); + + netif_info(adpt, probe, adpt->netdev, "complete\n"); + free_netdev(netdev); + + pci_disable_pcie_error_reporting(pdev); + + pci_disable_device(pdev); +} + + +/* + * alx_pci_error_detected + */ +static pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct alx_adapter *adpt = pci_get_drvdata(pdev); + struct net_device *netdev = adpt->netdev; + pci_ers_result_t retval = PCI_ERS_RESULT_NEED_RESET; + + netif_device_detach(netdev); + + if (state == pci_channel_io_perm_failure) { + retval = PCI_ERS_RESULT_DISCONNECT; + goto out; + } + + if (netif_running(netdev)) + alx_stop_internal(adpt, ALX_OPEN_CTRL_RESET_MAC); + pci_disable_device(pdev); +out: + return retval; +} + + +/* + * alx_pci_error_slot_reset + */ +static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev) +{ + struct alx_adapter *adpt = pci_get_drvdata(pdev); + pci_ers_result_t retval = PCI_ERS_RESULT_DISCONNECT; + + if (pci_enable_device(pdev)) { + alx_err(adpt, "cannot re-enable PCI device after reset\n"); + goto out; + } + + pci_set_master(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + adpt->hw.cbs.reset_mac(&adpt->hw); + retval = PCI_ERS_RESULT_RECOVERED; +out: + pci_cleanup_aer_uncorrect_error_status(pdev); + return retval; +} + + +/* + * alx_pci_error_resume + */ +static void alx_pci_error_resume(struct pci_dev *pdev) +{ + struct alx_adapter *adpt = pci_get_drvdata(pdev); + struct net_device *netdev = adpt->netdev; + + if (netif_running(netdev)) { + if (alx_open_internal(adpt, 0)) + return; + } + + netif_device_attach(netdev); +} + + +static struct pci_error_handlers alx_err_handler = { + .error_detected = alx_pci_error_detected, + .slot_reset = alx_pci_error_slot_reset, + .resume = alx_pci_error_resume, +}; + + +#ifdef CONFIG_PM_SLEEP +static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); +#define ALX_PM_OPS (&alx_pm_ops) +#else +#define ALX_PM_OPS NULL +#endif + + +static struct pci_driver alx_driver = { + .name = alx_drv_name, + .id_table = alx_pci_tbl, + .probe = alx_init, + .remove = __devexit_p(alx_remove), + .shutdown = alx_shutdown, + .err_handler = &alx_err_handler, + .driver.pm = ALX_PM_OPS, +}; + + +static int __init alx_init_module(void) +{ + int retval; + + printk(KERN_INFO "%s\n", alx_drv_description); + retval = pci_register_driver(&alx_driver); + + return retval; +} +module_init(alx_init_module); + + +static void __exit alx_exit_module(void) +{ + pci_unregister_driver(&alx_driver); +} + + +module_exit(alx_exit_module); --- /dev/null +++ b/drivers/net/ethernet/atheros/alx/alx_sw.h @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ALX_SW_H_ +#define _ALX_SW_H_ + +#include +#include + +/* Vendor ID */ +#define ALX_VENDOR_ID 0x1969 + +/* Device IDs */ +#define ALX_DEV_ID_AR8131 0x1063 /* l1c */ +#define ALX_DEV_ID_AR8132 0x1062 /* l2c */ +#define ALX_DEV_ID_AR8151_V1 0x1073 /* l1d_v1 */ +#define ALX_DEV_ID_AR8151_V2 0x1083 /* l1d_v2 */ +#define ALX_DEV_ID_AR8152_V1 0x2060 /* l2cb_v1 */ +#define ALX_DEV_ID_AR8152_V2 0x2062 /* l2cb_v2 */ +#define ALX_DEV_ID_AR8161 0x1091 /* l1f */ +#define ALX_DEV_ID_AR8162 0x1090 /* l2f */ + +#define ALX_REV_ID_AR8152_V1_0 0xc0 +#define ALX_REV_ID_AR8152_V1_1 0xc1 +#define ALX_REV_ID_AR8152_V2_0 0xc0 +#define ALX_REV_ID_AR8152_V2_1 0xc1 +#define ALX_REV_ID_AR8161_V2_0 0x10 /* B0 */ + +/* Generic Registers */ +#define ALX_DEV_STAT 0x62 /* 16 bits */ +#define ALX_DEV_STAT_CERR 0x0001 +#define ALX_DEV_STAT_NFERR 0x0002 +#define ALX_DEV_STAT_FERR 0x0004 + +#define ALX_ISR 0x1600 +#define ALX_IMR 0x1604 +#define ALX_ISR_SMB 0x00000001 +#define ALX_ISR_TIMER 0x00000002 +#define ALX_ISR_MANU 0x00000004 +#define ALX_ISR_RXF_OV 0x00000008 +#define ALX_ISR_RFD_UR 0x00000010 +#define ALX_ISR_TX_Q1 0x00000020 +#define ALX_ISR_TX_Q2 0x00000040 +#define ALX_ISR_TX_Q3 0x00000080 +#define ALX_ISR_TXF_UR 0x00000100 +#define ALX_ISR_DMAR 0x00000200 +#define ALX_ISR_DMAW 0x00000400 +#define ALX_ISR_TX_CREDIT 0x00000800 +#define ALX_ISR_PHY 0x00001000 +#define ALX_ISR_PHY_LPW 0x00002000 +#define ALX_ISR_TXQ_TO 0x00004000 +#define ALX_ISR_TX_Q0 0x00008000 +#define ALX_ISR_RX_Q0 0x00010000 +#define ALX_ISR_RX_Q1 0x00020000 +#define ALX_ISR_RX_Q2 0x00040000 +#define ALX_ISR_RX_Q3 0x00080000 +#define ALX_ISR_MAC_RX 0x00100000 +#define ALX_ISR_MAC_TX 0x00200000 +#define ALX_ISR_PCIE_UR 0x00400000 +#define ALX_ISR_PCIE_FERR 0x00800000 +#define ALX_ISR_PCIE_NFERR 0x01000000 +#define ALX_ISR_PCIE_CERR 0x02000000 +#define ALX_ISR_PCIE_LNKDOWN 0x04000000 +#define ALX_ISR_RX_Q4 0x08000000 +#define ALX_ISR_RX_Q5 0x10000000 +#define ALX_ISR_RX_Q6 0x20000000 +#define ALX_ISR_RX_Q7 0x40000000 +#define ALX_ISR_DIS 0x80000000 + + +#define ALX_IMR_NORMAL_MASK (\ + ALX_ISR_MANU |\ + ALX_ISR_OVER |\ + ALX_ISR_TXQ |\ + ALX_ISR_RXQ |\ + ALX_ISR_PHY_LPW |\ + ALX_ISR_PHY |\ + ALX_ISR_ERROR) + +#define ALX_ISR_ALERT_MASK (\ + ALX_ISR_DMAR |\ + ALX_ISR_DMAW |\ + ALX_ISR_TXQ_TO |\ + ALX_ISR_PCIE_FERR |\ + ALX_ISR_PCIE_LNKDOWN |\ + ALX_ISR_RFD_UR |\ + ALX_ISR_RXF_OV) + +#define ALX_ISR_TXQ (\ + ALX_ISR_TX_Q0 |\ + ALX_ISR_TX_Q1 |\ + ALX_ISR_TX_Q2 |\ + ALX_ISR_TX_Q3) + +#define ALX_ISR_RXQ (\ + ALX_ISR_RX_Q0 |\ + ALX_ISR_RX_Q1 |\ + ALX_ISR_RX_Q2 |\ + ALX_ISR_RX_Q3 |\ + ALX_ISR_RX_Q4 |\ + ALX_ISR_RX_Q5 |\ + ALX_ISR_RX_Q6 |\ + ALX_ISR_RX_Q7) + +#define ALX_ISR_OVER (\ + ALX_ISR_RFD_UR |\ + ALX_ISR_RXF_OV |\ + ALX_ISR_TXF_UR) + +#define ALX_ISR_ERROR (\ + ALX_ISR_DMAR |\ + ALX_ISR_TXQ_TO |\ + ALX_ISR_DMAW |\ + ALX_ISR_PCIE_ERROR) + +#define ALX_ISR_PCIE_ERROR (\ + ALX_ISR_PCIE_FERR |\ + ALX_ISR_PCIE_LNKDOWN) + +/* MISC Register */ +#define ALX_MISC 0x19C0 +#define ALX_MISC_INTNLOSC_OPEN 0x00000008 + +#define ALX_CLK_GATE 0x1814 + +/* DMA address */ +#define DMA_ADDR_HI_MASK 0xffffffff00000000ULL +#define DMA_ADDR_LO_MASK 0x00000000ffffffffULL + +#define ALX_DMA_ADDR_HI(_addr) \ + ((u32)(((u64)(_addr) & DMA_ADDR_HI_MASK) >> 32)) +#define ALX_DMA_ADDR_LO(_addr) \ + ((u32)((u64)(_addr) & DMA_ADDR_LO_MASK)) + +/* mac address length */ +#define ALX_ETH_LENGTH_OF_ADDRESS 6 +#define ALX_ETH_LENGTH_OF_HEADER ETH_HLEN + +#define ALX_ETH_CRC(_addr, _len) ether_crc((_len), (_addr)); + +/* Autonegotiation advertised speeds */ +/* Link speed */ +#define ALX_LINK_SPEED_UNKNOWN 0x0 +#define ALX_LINK_SPEED_10_HALF 0x0001 +#define ALX_LINK_SPEED_10_FULL 0x0002 +#define ALX_LINK_SPEED_100_HALF 0x0004 +#define ALX_LINK_SPEED_100_FULL 0x0008 +#define ALX_LINK_SPEED_1GB_FULL 0x0020 +#define ALX_LINK_SPEED_DEFAULT (\ + ALX_LINK_SPEED_10_HALF |\ + ALX_LINK_SPEED_10_FULL |\ + ALX_LINK_SPEED_100_HALF |\ + ALX_LINK_SPEED_100_FULL |\ + ALX_LINK_SPEED_1GB_FULL) + +#define ALX_MAX_SETUP_LNK_CYCLE 100 + +/* Device Type definitions for new protocol MDIO commands */ +#define ALX_MDIO_DEV_TYPE_NORM 0 + +/* Wake On Lan */ +#define ALX_WOL_PHY 0x00000001 /* PHY Status Change */ +#define ALX_WOL_MAGIC 0x00000002 /* Magic Packet */ + +#define ALX_MAX_EEPROM_LEN 0x200 +#define ALX_MAX_HWREG_LEN 0x200 + +/* RSS Settings */ +enum alx_rss_mode { + alx_rss_mode_disable = 0, + alx_rss_sig_que = 1, + alx_rss_mul_que_sig_int = 2, + alx_rss_mul_que_mul_int = 4, +}; + +/* Flow Control Settings */ +enum alx_fc_mode { + alx_fc_none = 0, + alx_fc_rx_pause, + alx_fc_tx_pause, + alx_fc_full, + alx_fc_default +}; + +/* WRR Restrict Settings */ +enum alx_wrr_mode { + alx_wrr_mode_none = 0, + alx_wrr_mode_high, + alx_wrr_mode_high2, + alx_wrr_mode_all +}; + +enum alx_mac_type { + alx_mac_unknown = 0, + alx_mac_l1c, + alx_mac_l2c, + alx_mac_l1d_v1, + alx_mac_l1d_v2, + alx_mac_l2cb_v1, + alx_mac_l2cb_v20, + alx_mac_l2cb_v21, + alx_mac_l1f, + alx_mac_l2f, +}; + + +/* Statistics counters collected by the MAC */ +struct alx_hw_stats { + /* rx */ + unsigned long rx_ok; + unsigned long rx_bcast; + unsigned long rx_mcast; + unsigned long rx_pause; + unsigned long rx_ctrl; + unsigned long rx_fcs_err; + unsigned long rx_len_err; + unsigned long rx_byte_cnt; + unsigned long rx_runt; + unsigned long rx_frag; + unsigned long rx_sz_64B; + unsigned long rx_sz_127B; + unsigned long rx_sz_255B; + unsigned long rx_sz_511B; + unsigned long rx_sz_1023B; + unsigned long rx_sz_1518B; + unsigned long rx_sz_max; + unsigned long rx_ov_sz; + unsigned long rx_ov_rxf; + unsigned long rx_ov_rrd; + unsigned long rx_align_err; + unsigned long rx_bc_byte_cnt; + unsigned long rx_mc_byte_cnt; + unsigned long rx_err_addr; + + /* tx */ + unsigned long tx_ok; + unsigned long tx_bcast; + unsigned long tx_mcast; + unsigned long tx_pause; + unsigned long tx_exc_defer; + unsigned long tx_ctrl; + unsigned long tx_defer; + unsigned long tx_byte_cnt; + unsigned long tx_sz_64B; + unsigned long tx_sz_127B; + unsigned long tx_sz_255B; + unsigned long tx_sz_511B; + unsigned long tx_sz_1023B; + unsigned long tx_sz_1518B; + unsigned long tx_sz_max; + unsigned long tx_single_col; + unsigned long tx_multi_col; + unsigned long tx_late_col; + unsigned long tx_abort_col; + unsigned long tx_underrun; + unsigned long tx_trd_eop; + unsigned long tx_len_err; + unsigned long tx_trunc; + unsigned long tx_bc_byte_cnt; + unsigned long tx_mc_byte_cnt; + unsigned long update; +}; + +/* HW callback function pointer table */ +struct alx_hw; +struct alx_hw_callbacks { + /* NIC */ + int (*identify_nic)(struct alx_hw *); + /* PHY */ + int (*init_phy)(struct alx_hw *); + int (*reset_phy)(struct alx_hw *); + int (*read_phy_reg)(struct alx_hw *, u16, u16 *); + int (*write_phy_reg)(struct alx_hw *, u16, u16); + /* Link */ + int (*setup_phy_link)(struct alx_hw *, u32, bool, bool); + int (*setup_phy_link_speed)(struct alx_hw *, u32, bool, bool); + int (*check_phy_link)(struct alx_hw *, u32 *, bool *); + + /* MAC */ + int (*reset_mac)(struct alx_hw *); + int (*start_mac)(struct alx_hw *); + int (*stop_mac)(struct alx_hw *); + int (*config_mac)(struct alx_hw *, u16, u16, u16, u16, u16); + int (*get_mac_addr)(struct alx_hw *, u8 *); + int (*set_mac_addr)(struct alx_hw *, u8 *); + int (*set_mc_addr)(struct alx_hw *, u8 *); + int (*clear_mc_addr)(struct alx_hw *); + + /* intr */ + int (*ack_phy_intr)(struct alx_hw *); + int (*enable_legacy_intr)(struct alx_hw *); + int (*disable_legacy_intr)(struct alx_hw *); + int (*enable_msix_intr)(struct alx_hw *, u8); + int (*disable_msix_intr)(struct alx_hw *, u8); + + /* Configure */ + int (*config_rx)(struct alx_hw *); + int (*config_tx)(struct alx_hw *); + int (*config_fc)(struct alx_hw *); + int (*config_rss)(struct alx_hw *, bool); + int (*config_msix)(struct alx_hw *, u16, bool, bool); + int (*config_wol)(struct alx_hw *, u32); + int (*config_aspm)(struct alx_hw *, bool, bool); + int (*config_mac_ctrl)(struct alx_hw *); + int (*config_pow_save)(struct alx_hw *, u32, + bool, bool, bool, bool); + int (*reset_pcie)(struct alx_hw *, bool, bool); + + /* NVRam function */ + int (*check_nvram)(struct alx_hw *, bool *); + int (*read_nvram)(struct alx_hw *, u16, u32 *); + int (*write_nvram)(struct alx_hw *, u16, u32); + + /* Others */ + int (*get_ethtool_regs)(struct alx_hw *, void *); +}; + +struct alx_hw { + struct alx_adapter *adpt; + struct alx_hw_callbacks cbs; + u8 __iomem *hw_addr; /* inner register address */ + u16 pci_venid; + u16 pci_devid; + u16 pci_sub_devid; + u16 pci_sub_venid; + u8 pci_revid; + + bool long_cable; + bool aps_en; + bool hi_txperf; + bool msi_lnkpatch; + u32 dma_chnl; + u32 hwreg_sz; + u32 eeprom_sz; + + /* PHY parameter */ + u32 phy_id; + u32 autoneg_advertised; + u32 link_speed; + bool link_up; + spinlock_t mdio_lock; + + /* MAC parameter */ + enum alx_mac_type mac_type; + u8 mac_addr[ALX_ETH_LENGTH_OF_ADDRESS]; + u8 mac_perm_addr[ALX_ETH_LENGTH_OF_ADDRESS]; + + u32 mtu; + u16 rxstat_reg; + u16 rxstat_sz; + u16 txstat_reg; + u16 txstat_sz; + + u16 tx_prod_reg[4]; + u16 tx_cons_reg[4]; + u16 rx_prod_reg[2]; + u16 rx_cons_reg[2]; + u64 tpdma[4]; + u64 rfdma[2]; + u64 rrdma[2]; + + /* WRR parameter */ + enum alx_wrr_mode wrr_mode; + u32 wrr_prio0; + u32 wrr_prio1; + u32 wrr_prio2; + u32 wrr_prio3; + + /* RSS parameter */ + enum alx_rss_mode rss_mode; + u8 rss_hstype; + u8 rss_base_cpu; + u16 rss_idt_size; + u32 rss_idt[32]; + u8 rss_key[40]; + + /* flow control parameter */ + enum alx_fc_mode cur_fc_mode; /* FC mode in effect */ + enum alx_fc_mode req_fc_mode; /* FC mode requested by caller */ + bool disable_fc_autoneg; /* Do not autonegotiate FC */ + bool fc_was_autonegged; /* the result of autonegging */ + bool fc_single_pause; + + /* Others */ + u32 preamble; + u32 intr_mask; + u16 smb_timer; + u16 imt; /* Interrupt Moderator timer (2us) */ + u32 flags; +}; + +#define ALX_HW_FLAG_L0S_CAP 0x00000001 +#define ALX_HW_FLAG_L0S_EN 0x00000002 +#define ALX_HW_FLAG_L1_CAP 0x00000004 +#define ALX_HW_FLAG_L1_EN 0x00000008 +#define ALX_HW_FLAG_PWSAVE_CAP 0x00000010 +#define ALX_HW_FLAG_PWSAVE_EN 0x00000020 +#define ALX_HW_FLAG_AZ_CAP 0x00000040 +#define ALX_HW_FLAG_AZ_EN 0x00000080 +#define ALX_HW_FLAG_PTP_CAP 0x00000100 +#define ALX_HW_FLAG_PTP_EN 0x00000200 +#define ALX_HW_FLAG_GIGA_CAP 0x00000400 + +#define ALX_HW_FLAG_PROMISC_EN 0x00010000 /* for mac ctrl reg */ +#define ALX_HW_FLAG_VLANSTRIP_EN 0x00020000 /* for mac ctrl reg */ +#define ALX_HW_FLAG_MULTIALL_EN 0x00040000 /* for mac ctrl reg */ +#define ALX_HW_FLAG_LOOPBACK_EN 0x00080000 /* for mac ctrl reg */ + +#define CHK_HW_FLAG(_flag) CHK_FLAG(hw, HW, _flag) +#define SET_HW_FLAG(_flag) SET_FLAG(hw, HW, _flag) +#define CLI_HW_FLAG(_flag) CLI_FLAG(hw, HW, _flag) + + +/* RSS hstype Definitions */ +#define ALX_RSS_HSTYP_IPV4_EN 0x00000001 +#define ALX_RSS_HSTYP_TCP4_EN 0x00000002 +#define ALX_RSS_HSTYP_IPV6_EN 0x00000004 +#define ALX_RSS_HSTYP_TCP6_EN 0x00000008 +#define ALX_RSS_HSTYP_ALL_EN (\ + ALX_RSS_HSTYP_IPV4_EN |\ + ALX_RSS_HSTYP_TCP4_EN |\ + ALX_RSS_HSTYP_IPV6_EN |\ + ALX_RSS_HSTYP_TCP6_EN) + + +/* definitions for flags */ + +#define CHK_FLAG_ARRAY(_st, _idx, _type, _flag) \ + ((_st)->flags[_idx] & (ALX_##_type##_FLAG_##_idx##_##_flag)) +#define CHK_FLAG(_st, _type, _flag) \ + ((_st)->flags & (ALX_##_type##_FLAG_##_flag)) + +#define SET_FLAG_ARRAY(_st, _idx, _type, _flag) \ + ((_st)->flags[_idx] |= (ALX_##_type##_FLAG_##_idx##_##_flag)) +#define SET_FLAG(_st, _type, _flag) \ + ((_st)->flags |= (ALX_##_type##_FLAG_##_flag)) + +#define CLI_FLAG_ARRAY(_st, _idx, _type, _flag) \ + ((_st)->flags[_idx] &= ~(ALX_##_type##_FLAG_##_idx##_##_flag)) +#define CLI_FLAG(_st, _type, _flag) \ + ((_st)->flags &= ~(ALX_##_type##_FLAG_##_flag)) + +int alx_cfg_r16(const struct alx_hw *hw, int reg, u16 *pval); +int alx_cfg_w16(const struct alx_hw *hw, int reg, u16 val); + + +void alx_mem_flush(const struct alx_hw *hw); +void alx_mem_r32(const struct alx_hw *hw, int reg, u32 *val); +void alx_mem_w32(const struct alx_hw *hw, int reg, u32 val); +void alx_mem_w8(const struct alx_hw *hw, int reg, u8 val); + + +/* special definitions for hw */ +#define ALF_MAX_MSIX_NOQUE_INTRS 4 +#define ALF_MIN_MSIX_NOQUE_INTRS 4 +#define ALF_MAX_MSIX_QUEUE_INTRS 12 +#define ALF_MIN_MSIX_QUEUE_INTRS 12 +#define ALF_MAX_MSIX_INTRS \ + (ALF_MAX_MSIX_QUEUE_INTRS + ALF_MAX_MSIX_NOQUE_INTRS) +#define ALF_MIN_MSIX_INTRS \ + (ALF_MIN_MSIX_NOQUE_INTRS + ALF_MIN_MSIX_QUEUE_INTRS) + + +/* function */ +extern int alc_init_hw_callbacks(struct alx_hw *hw); +extern int alf_init_hw_callbacks(struct alx_hw *hw); + +/* Logging message functions */ +void __printf(3, 4) alx_hw_printk(const char *level, const struct alx_hw *hw, + const char *fmt, ...); + +#define alx_hw_err(_hw, _format, ...) \ + alx_hw_printk(KERN_ERR, _hw, _format, ##__VA_ARGS__) +#define alx_hw_warn(_hw, _format, ...) \ + alx_hw_printk(KERN_WARNING, _hw, _format, ##__VA_ARGS__) +#define alx_hw_info(_hw, _format, ...) \ + alx_hw_printk(KERN_INFO, _hw, _format, ##__VA_ARGS__) + +#endif /* _ALX_SW_H_ */ + compat-drivers-2012-09-18/crap/network/0002-backport-alx.patch0000644000175000017500000001053312026176477023012 0ustar mcgrofmcgrofThis should go into patches/01-netdev.patch --- a/drivers/net/ethernet/atheros/alx/alx_main.c +++ b/drivers/net/ethernet/atheros/alx/alx_main.c @@ -318,7 +318,11 @@ static void alx_set_multicase_list(struc /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hw->cbs.set_mc_addr(hw, ha->addr); +#else + hw->cbs.set_mc_addr(hw, ha->dmi_addr); +#endif } @@ -337,8 +341,10 @@ static int alx_set_mac_address(struct ne if (netif_running(netdev)) return -EBUSY; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) if (netdev->addr_assign_type & NET_ADDR_RANDOM) netdev->addr_assign_type ^= NET_ADDR_RANDOM; +#endif memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len); @@ -2483,7 +2489,7 @@ static void alx_free_all_rtx_descriptor( ring_header->size = ring_header->used = 0; } - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t alx_fix_features(struct net_device *netdev, netdev_features_t features) { @@ -2515,6 +2521,8 @@ static int alx_set_features(struct net_d alx_vlan_mode(netdev, features); return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ + /* * alx_change_mtu - Change the Maximum Transfer Unit */ @@ -2538,7 +2546,17 @@ static int alx_change_mtu(struct net_dev adpt->hw.mtu = new_mtu; adpt->rxbuf_size = new_mtu > ALX_DEF_RX_BUF_SIZE ? ALIGN(max_frame, 8) : ALX_DEF_RX_BUF_SIZE; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) + if (new_mtu > (7*1024)) { + netdev->features &= ~NETIF_F_TSO; + netdev->features &= ~NETIF_F_TSO6; + } else { + netdev->features |= NETIF_F_TSO; + netdev->features |= NETIF_F_TSO6; + } +#else netdev_update_features(netdev); +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ alx_reinit_locked(adpt); } @@ -3444,8 +3462,10 @@ static const struct net_device_ops alx_n .ndo_change_mtu = alx_change_mtu, .ndo_do_ioctl = alx_ioctl, .ndo_tx_timeout = alx_tx_timeout, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = alx_fix_features, .ndo_set_features = alx_set_features, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = alx_poll_controller, #endif @@ -3532,7 +3552,7 @@ static int __devinit alx_init(struct pci netdev->base_addr = (unsigned long)adpt->hw.hw_addr; /* set cb member of netdev structure*/ - netdev->netdev_ops = &alx_netdev_ops; + netdev_attach_ops(netdev, &alx_netdev_ops); alx_set_ethtool_ops(netdev); netdev->watchdog_timeo = ALX_WATCHDOG_TIME; strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); @@ -3580,6 +3600,7 @@ static int __devinit alx_init(struct pci adpt->max_rxques = min_t(int, ALX_MAX_RX_QUEUES, num_online_cpus()); adpt->max_txques = min_t(int, ALX_MAX_TX_QUEUES, num_online_cpus()); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_RX; @@ -3591,6 +3612,19 @@ static int __devinit alx_init(struct pci } netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_TX; +#else + netdev->features = NETIF_F_SG | + NETIF_F_HW_CSUM | + NETIF_F_HW_VLAN_RX; + if (adpt->hw.mac_type != alx_mac_l1c && + adpt->hw.mac_type != alx_mac_l2c) { + netdev->features = netdev->features | + NETIF_F_TSO | + NETIF_F_TSO6; + } + netdev->features = netdev->features | + NETIF_F_HW_VLAN_TX; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /* get mac addr and perm mac addr, set to register */ if (hw->cbs.get_mac_addr) @@ -3860,6 +3894,8 @@ static struct pci_error_handlers alx_err #ifdef CONFIG_PM_SLEEP +compat_pci_suspend(alx_suspend) +compat_pci_resume(alx_resume) static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); #define ALX_PM_OPS (&alx_pm_ops) #else @@ -3874,7 +3910,12 @@ static struct pci_driver alx_driver = { .remove = __devexit_p(alx_remove), .shutdown = alx_shutdown, .err_handler = &alx_err_handler, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ALX_PM_OPS, +#elif defined(CONFIG_PM_SLEEP) + .suspend = alx_suspend_compat, + .resume = alx_resume_compat, +#endif }; compat-drivers-2012-09-18/crap/network/0003-remove-atl1c-devices-from-alx.patch0000644000175000017500000000133512026176477026066 0ustar mcgrofmcgrof The alx driver is to only support the AR8161 and AR8162 devices. The older devices are supported through atl1c. --- a/drivers/net/ethernet/atheros/alx/alx_main.c +++ b/drivers/net/ethernet/atheros/alx/alx_main.c @@ -33,12 +33,6 @@ static const char alx_drv_description[] #define ALX_ETHER_DEVICE(device_id) {\ PCI_DEVICE(ALX_VENDOR_ID, device_id)} static DEFINE_PCI_DEVICE_TABLE(alx_pci_tbl) = { - ALX_ETHER_DEVICE(ALX_DEV_ID_AR8131), - ALX_ETHER_DEVICE(ALX_DEV_ID_AR8132), - ALX_ETHER_DEVICE(ALX_DEV_ID_AR8151_V1), - ALX_ETHER_DEVICE(ALX_DEV_ID_AR8151_V2), - ALX_ETHER_DEVICE(ALX_DEV_ID_AR8152_V1), - ALX_ETHER_DEVICE(ALX_DEV_ID_AR8152_V2), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8161), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8162), {0,} compat-drivers-2012-09-18/crap/network/0004-wireless-adds-the-802.11ad-60-GHz-wil6210-driver.patch0000644000175000017500000050714212026176477030320 0ustar mcgrofmcgrofFrom 1202ba3f79d5190f18ab3588ad2ea667fca0f273 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 28 Jun 2012 17:20:55 +0300 Subject: [PATCH 6/6] wireless: adds the 802.11ad / 60 GHz wil6210 driver This is development snapshot of the driver for Wilocity 60ghz chip wil6210 It depends on the 60ghz infrastructure patches submitted to the linux-wireless. What works: - sniffer. Due to hardware limitation it captures either only CP (control PHY) or DP (data PHY) frames - BSS between 2 peers. Both AP and managed modes supported. Single peer is hardware limitation. Iperf gives slightly above 1gbps Not yet: - P2P and FST flows - after disconnect, upon 2-nd connect hardware goes crazy - MSI interrupt - various offloads Signed-off-by: Vladimir Kondratiev Signed-off-by: Luis R. Rodriguez --- drivers/net/wireless/ath/Kconfig | 1 + drivers/net/wireless/ath/wil6210/Makefile | 20 + drivers/net/wireless/ath/wil6210/cfg80211.c | 617 ++++++++++++++ drivers/net/wireless/ath/wil6210/cfg80211.h | 23 + drivers/net/wireless/ath/wil6210/debug.c | 42 + drivers/net/wireless/ath/wil6210/debugfs.c | 458 ++++++++++ drivers/net/wireless/ath/wil6210/ifc.sh | 20 + drivers/net/wireless/ath/wil6210/interrupt.c | 324 ++++++++ drivers/net/wireless/ath/wil6210/main.c | 336 ++++++++ drivers/net/wireless/ath/wil6210/memdump.sh | 9 + drivers/net/wireless/ath/wil6210/netdev.c | 166 ++++ drivers/net/wireless/ath/wil6210/pcie_bus.c | 239 ++++++ drivers/net/wireless/ath/wil6210/read_dw.sh | 7 + drivers/net/wireless/ath/wil6210/tools/Makefile | 4 + drivers/net/wireless/ath/wil6210/tools/trace.c | 190 +++++ drivers/net/wireless/ath/wil6210/tools/vhex2bin.py | 17 + drivers/net/wireless/ath/wil6210/trace.sh | 4 + drivers/net/wireless/ath/wil6210/txdesc.sh | 8 + drivers/net/wireless/ath/wil6210/txrx.c | 871 ++++++++++++++++++++ drivers/net/wireless/ath/wil6210/txrx.h | 352 ++++++++ drivers/net/wireless/ath/wil6210/vrwatch.sh | 6 + drivers/net/wireless/ath/wil6210/wil6210.h | 234 ++++++ drivers/net/wireless/ath/wil6210/wil6210_rgf.h | 93 +++ drivers/net/wireless/ath/wil6210/wmi.c | 710 ++++++++++++++++ drivers/net/wireless/ath/wil6210/wmi.h | 837 +++++++++++++++++++ 25 files changed, 5588 insertions(+) create mode 100644 drivers/net/wireless/ath/wil6210/Makefile create mode 100644 drivers/net/wireless/ath/wil6210/cfg80211.c create mode 100644 drivers/net/wireless/ath/wil6210/cfg80211.h create mode 100644 drivers/net/wireless/ath/wil6210/debug.c create mode 100644 drivers/net/wireless/ath/wil6210/debugfs.c create mode 100755 drivers/net/wireless/ath/wil6210/ifc.sh create mode 100644 drivers/net/wireless/ath/wil6210/interrupt.c create mode 100644 drivers/net/wireless/ath/wil6210/main.c create mode 100755 drivers/net/wireless/ath/wil6210/memdump.sh create mode 100644 drivers/net/wireless/ath/wil6210/netdev.c create mode 100644 drivers/net/wireless/ath/wil6210/pcie_bus.c create mode 100755 drivers/net/wireless/ath/wil6210/read_dw.sh create mode 100644 drivers/net/wireless/ath/wil6210/tools/Makefile create mode 100644 drivers/net/wireless/ath/wil6210/tools/trace.c create mode 100755 drivers/net/wireless/ath/wil6210/tools/vhex2bin.py create mode 100755 drivers/net/wireless/ath/wil6210/trace.sh create mode 100755 drivers/net/wireless/ath/wil6210/txdesc.sh create mode 100644 drivers/net/wireless/ath/wil6210/txrx.c create mode 100644 drivers/net/wireless/ath/wil6210/txrx.h create mode 100755 drivers/net/wireless/ath/wil6210/vrwatch.sh create mode 100644 drivers/net/wireless/ath/wil6210/wil6210.h create mode 100644 drivers/net/wireless/ath/wil6210/wil6210_rgf.h create mode 100644 drivers/net/wireless/ath/wil6210/wmi.c create mode 100644 drivers/net/wireless/ath/wil6210/wmi.h --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -26,5 +26,6 @@ source "drivers/net/wireless/ath/ath5k/K source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" +source "drivers/net/wireless/ath/wil6210/Kconfig" endif --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -0,0 +1,20 @@ +obj-$(CONFIG_WIL6210) += wil6210.o + +wil6210-objs := main.o +wil6210-objs += netdev.o +wil6210-objs += cfg80211.o +wil6210-objs += pcie_bus.o +wil6210-objs += debugfs.o +wil6210-objs += wmi.o +wil6210-objs += interrupt.o +wil6210-objs += debug.o +wil6210-objs += txrx.o + +#subdir-ccflags-y += -Werror + +# Debug Tx/Rx flows +#subdir-ccflags-y += -DWIL_DEBUG_TXRX + +# Use Clear-On-Read +subdir-ccflags-y += -DWIL6210_ISR_COR=1 + --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "cfg80211.h" +#include "wmi.h" + +#define CHAN60G(_channel, _flags) { \ + .band = IEEE80211_BAND_60GHZ, \ + .center_freq = 56160 + (2160 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 40, \ +} + +static struct ieee80211_channel wil_60ghz_channels[] = { + CHAN60G(1, 0), + CHAN60G(2, 0), + CHAN60G(3, 0), +#if 0 + CHAN60G(4, 0), +#endif +}; + +static struct ieee80211_supported_band wil_band_60ghz = { + .channels = wil_60ghz_channels, + .n_channels = ARRAY_SIZE(wil_60ghz_channels), + .ht_cap = { + .ht_supported = true, + .cap = 0, /* TODO */ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ + .mcs = { + .rx_mask = {0xfe, 0xff, 0xff, 0x0f}, /* 1..27 */ + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ + }, + }, +}; + +static const struct ieee80211_txrx_stypes +wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, +}; + + + +int iftype_nl2wmi(enum nl80211_iftype type) +{ + static const struct { + enum nl80211_iftype nl; + enum WMI_NETWORK_TYPE wmi; + } __nl2wmi[] = { + {NL80211_IFTYPE_ADHOC, ADHOC_NETWORK}, + {NL80211_IFTYPE_STATION, INFRA_NETWORK}, + {NL80211_IFTYPE_AP, AP_NETWORK}, + {NL80211_IFTYPE_P2P_CLIENT, P2P_NETWORK}, + {NL80211_IFTYPE_P2P_GO, P2P_NETWORK}, + {NL80211_IFTYPE_MONITOR, ADHOC_NETWORK}, /* FIXME */ + #if 0 + {NL80211_IFTYPE_AP_VLAN, 0}, + {NL80211_IFTYPE_WDS, 0}, + {NL80211_IFTYPE_MESH_POINT, 0}, + INFRA_NETWORK = 0x01, + ADHOC_NETWORK = 0x02, + ADHOC_CREATOR = 0x04, + AP_NETWORK = 0x10, + P2P_NETWORK = 0x20, + WBE_NETWORK = 0x40, + #endif + }; + int i; + for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { + if (__nl2wmi[i].nl == type) + return __nl2wmi[i].wmi; + } + return -EOPNOTSUPP; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *ndev, + u8 *mac, struct station_info *sinfo) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_NOTIFY_REQ_CMD cmd; + } __packed wmi = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 1, + .len = sizeof(wmi.wmi) + + sizeof(wmi.cmd), + }, + .wmi = { + .id = WMI_NOTIFY_REQ_CMDID, + .info1 = 0, + }, + .cmd = { + .cid = 0, + .interval_usec = 0, + }, + }; + +#if 0 + wil_info(wil, "%s(%pM)\n", __func__, mac); +#endif + if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) + return -ENOENT; + rc = wmi_call(wil, &wmi, WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); + if (rc) + return rc; + + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->txrate.mcs = wil->stats.bf_mcs; + sinfo->filled |= STATION_INFO_RX_BITRATE; + sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->rxrate.mcs = wil->stats.last_mcs_rx; + + if (test_bit(wil_status_fwconnected, &wil->status)) { + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = 12; /* TODO: provide real number */ + } + + return 0; +} + +static int wil_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + wil_info(wil, "%s()\n", __func__); + + switch (type) { + case NL80211_IFTYPE_STATION: + wil_info(wil, "type: STATION\n"); + break; + case NL80211_IFTYPE_AP: + wil_info(wil, "type: AP\n"); + break; + case NL80211_IFTYPE_P2P_CLIENT: + wil_info(wil, "type: P2P_CLIENT\n"); + break; + case NL80211_IFTYPE_P2P_GO: + wil_info(wil, "type: P2P_GO\n"); + break; + case NL80211_IFTYPE_MONITOR: + wil_info(wil, "type: Monitor\n"); + if (flags) { + wil_info(wil, "Monitor flags: 0x%08x\n", *flags); + wil->monitor_flags = *flags; + } else { + wil->monitor_flags = 0; + } + break; + default: + return -EOPNOTSUPP; + } + wdev->iftype = type; + return 0; +} + +static int wil_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ +#if 0 + int rc; +#endif + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_START_SCAN_CMD scan; + u16 channels[4]; + } __packed wmi_scan = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 0, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_START_SCAN_CMD), + }, + .wmi = { + .id = WMI_START_SCAN_CMDID, + .info1 = 0, + }, + .scan = { + }, + }; +#if 0 + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_SET_BSS_FILTER_CMD filter; + } __packed wmi_bss_filter = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 0, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_SET_BSS_FILTER_CMD), + }, + .wmi = { + .id = WMI_SET_BSS_FILTER_CMDID, + .info1 = 0, + }, + .filter = { + .bssFilter = ALL_BSS_FILTER, + }, + }; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_SET_SCAN_PARAMS_CMD params; + } __packed wmi_scan_params = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 0, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_SET_BSS_FILTER_CMD), + }, + .wmi = { + .id = WMI_SET_SCAN_PARAMS_CMDID, + .info1 = 0, + }, + .params = { + .fg_start_period = 0, /* seconds */ + .fg_end_period = 0, /* seconds */ + .bg_period = 0, /* seconds */ + .maxact_chdwell_time = 0, /* msec */ + .pas_chdwell_time = 0, /* msec */ + .shortScanRatio = 0, /* how many short scans */ + /* for one long */ + .scanCtrlFlags = DEFAULT_SCAN_CTRL_FLAGS, + .minact_chdwell_time = 0, /* msec */ + .maxact_scan_per_ssid = 0, /* max active scans */ + /* per ssid */ + .max_dfsch_act_time = 0, /* msecs */ + }, + }; +#endif + int i, n; + wil_info(wil, "%s()\n", __func__); + if (wil->scan_request) { + wil_err(wil, "Already scanning\n"); + return -EAGAIN; + } + /* check we are client side */ + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + break; + default: + return -EOPNOTSUPP; + + } + /** + * FW don't support scan after connection attempt + */ + if (test_bit(wil_status_dontscan, &wil->status)) { + wil_err(wil, "Scan after connect attempt not supported\n"); + return -EBUSY; + } + + wil->scan_request = request; + wmi_scan.scan.forceFgScan = 0; + wmi_scan.scan.isLegacy = 0; + wmi_scan.scan.homeDwellTime = 0; + wmi_scan.scan.forceScanInterval = 0; + wmi_scan.scan.scanType = 0; + wmi_scan.scan.numChannels = 0; + n = min(request->n_channels, 4U); + for (i = 0; i < n; i++) { + int ch = ieee80211_frequency_to_channel( + request->channels[i]->center_freq); + if (ch == 0) { + wil_err(wil, + "Scan requested for unknown frequency %dMhz\n", + request->channels[i]->center_freq); + continue; + } + /* 0-based index */ + /* TODO convert CPU to LE */ + wmi_scan.scan.channelList[wmi_scan.scan.numChannels++] = ch-1; + wil_info(wil, "Scan for ch %d : %d MHz\n", ch, + request->channels[i]->center_freq); + } + wmi_scan.hdr.len += wmi_scan.scan.numChannels * + sizeof(wmi_scan.scan.channelList[0]); +#if 0 + rc = wmi_send_cmd(wil, &wmi_bss_filter); + if (rc) + return rc; + rc = wmi_send_cmd(wil, &wmi_scan_params); + if (rc) + return rc; +#endif + return wmi_send_cmd(wil, &wmi_scan); +} + +static int wil_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + struct cfg80211_bss *bss; + const u8 *ssid_eid; + int rc = 0; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_CONNECT_CMD conn; + } __packed wmi_conn = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 0, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_CONNECT_CMD), + }, + .wmi = { + .id = WMI_CONNECT_CMDID, + .info1 = 0, + }, + .conn = { + }, + }; + wil_info(wil, "%s()\n", __func__); + if (sme->channel) + wil_info(wil, "Channel : %d MHz\n", + sme->channel->center_freq); + if (sme->bssid) + wil_info(wil, "BSSID : %pM\n", sme->bssid); + if (sme->ssid && sme->ssid_len) + print_hex_dump(KERN_INFO, "SSID : ", DUMP_PREFIX_NONE, 16, 1, + sme->ssid, sme->ssid_len, true); + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!bss) { + wil_err(wil, "Unable to find BSS\n"); + return -ENOENT; + } + ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (!ssid_eid) { + wil_err(wil, "No SSID\n"); + rc = -ENOENT; + goto out; + } + wmi_conn.conn.networkType = iftype_nl2wmi(wdev->iftype); + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ + wmi_conn.conn.networkType = iftype_nl2wmi(NL80211_IFTYPE_ADHOC); + wmi_conn.conn.dot11AuthMode = OPEN_AUTH; /* TODO: crypto flow */ + wmi_conn.conn.authMode = NONE_AUTH; /* TODO: crypto flow */ + /* wmi_conn.conn.pairwiseCryptoType; */ + /* wmi_conn.conn.pairwiseCryptoLen; */ + wmi_conn.conn.groupCryptoType = NONE_CRYPT; /* TODO: crypto flow */ + /* wmi_conn.conn.groupCryptoLen; */ + wmi_conn.conn.ssidLength = min_t(u8, ssid_eid[1], 32); + memcpy(wmi_conn.conn.ssid, ssid_eid+2, wmi_conn.conn.ssidLength); + { + int ch = ieee80211_frequency_to_channel( + bss->channel->center_freq); + if (ch == 0) { + wil_err(wil, "BSS at unknown frequency %dMhz\n", + bss->channel->center_freq); + rc = -EOPNOTSUPP; + goto out; + } + wmi_conn.conn.channel = ch - 1; + } + memcpy(wmi_conn.conn.bssid, bss->bssid, 6); + wmi_conn.conn.ctrl_flags = 0; /* TODO: set real value */ + memcpy(wmi_conn.conn.destMacAddr, bss->bssid, 6); + /** + * FW don't support scan after connection attempt + */ + set_bit(wil_status_dontscan, &wil->status); + rc = wmi_send_cmd(wil, &wmi_conn); + if (rc == 0) { + /* Connect can take lots of time */ + mod_timer(&wil->connect_timer, + jiffies + msecs_to_jiffies(2000)); + } + out: + cfg80211_put_bss(bss); + return rc; +} + +static int wil_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *ndev, + u16 reason_code) +{ + int rc; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + } __packed wmi_conn = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 0, + .len = sizeof(struct wil6210_mbox_hdr_wmi), + }, + .wmi = { + .id = WMI_DISCONNECT_CMDID, + .info1 = 0, + }, + }; + wil_info(wil, "%s()\n", __func__); + rc = wmi_send_cmd(wil, &wmi_conn); + return rc; +} + +static int wil_cfg80211_set_txpower(struct wiphy *wiphy, + enum nl80211_tx_power_setting type, int mbm) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_info(wil, "%s()\n", __func__); + return 0; +} + +static int wil_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_info(wil, "%s()\n", __func__); + + *dbm = 43; + + return 0; +} + +static int wil_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, + u64 *cookie) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_info(wil, "%s()\n", __func__); + print_hex_dump(KERN_INFO, "mgmt_tx ", DUMP_PREFIX_OFFSET, 16, 1, + buf, len, true); + return 0; +} + +static void wil_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, u16 frame_type, bool reg) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_info(wil, "%s()\n", __func__); + wil_info(wil, "frame_type = 0x%04x, reg = %d\n", frame_type, reg); +} + +static int wil_set_monitor_channel(struct wiphy *wiphy, +#if defined(OLD_SET_CHANNEL_API) + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_info(wil, "%s()\n", __func__); + wil_info(wil, "freq = %d\n", chan->center_freq); + wil->monitor_chan = chan; + return 0; +} + +static struct cfg80211_ops wil_cfg80211_ops = { + .scan = wil_cfg80211_scan, + .connect = wil_cfg80211_connect, + .disconnect = wil_cfg80211_disconnect, + .set_tx_power = wil_cfg80211_set_txpower, + .get_tx_power = wil_cfg80211_get_txpower, + .change_virtual_intf = wil_cfg80211_change_iface, + .get_station = wil_cfg80211_get_station, + .mgmt_tx = wil_mgmt_tx, + .mgmt_frame_register = wil_mgmt_frame_register, +#if defined(OLD_SET_CHANNEL_API) + .set_channel = wil_set_monitor_channel, +#else + .set_monitor_channel = wil_set_monitor_channel, +#endif /* defined(NEW_SET_CHANNEL_API) */ +}; + +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_CCMP, /* keep for debug, TODO: remove */ + WLAN_CIPHER_SUITE_GCMP, +}; + +static void wil_wiphy_init(struct wiphy *wiphy) +{ + /* TODO: figure this out */ + wiphy->max_scan_ssids = 10; +#if 0 + wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS; +#endif + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_MONITOR); + + wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; + + /* TODO: figure this out */ + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + wiphy->mgmt_stypes = wil_mgmt_stypes; +} + +struct wireless_dev *wil_cfg80211_init(struct device *dev) +{ + int r = 0; + struct wireless_dev *wdev; + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy_new(&wil_cfg80211_ops, + sizeof(struct wil6210_priv)); + if (!wdev->wiphy) { + r = -ENOMEM; + goto out; + } + + set_wiphy_dev(wdev->wiphy, dev); + wil_wiphy_init(wdev->wiphy); + r = wiphy_register(wdev->wiphy); + + if (r < 0) + goto out_failed_reg; + + return wdev; + +out_failed_reg: + wiphy_free(wdev->wiphy); +out: + kfree(wdev); + return ERR_PTR(r); +} + +void wil_wdev_free(struct wil6210_priv *wil) +{ + struct wireless_dev *wdev = wil_to_wdev(wil); + struct device *dev = wil_to_dev(wil); + dev_info(dev, "%s()\n", __func__); + + if (!wdev) + return; + + wiphy_unregister(wdev->wiphy); + wiphy_free(wdev->wiphy); + kfree(wdev); +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __wil_CFG80211_H__ +#define __wil_CFG80211_H__ + +struct wireless_dev *wil_cfg80211_init(struct device *dev); +void wil_wdev_free(struct wil6210_priv *wil); + +#endif /* __wil_CFG80211_H__ */ --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debug.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wil6210.h" +#include "wmi.h" + +static struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + u32 val; +} __packed wmi_echo = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 1, + .len = 8 + 4, + }, + .wmi = { + .id = WMI_ECHO_CMDID, + .info1 = 0, + }, + .val = 0x12345678, +}; + +void send_echo(struct wil6210_priv *wil) +{ + wmi_call(wil, &wmi_echo, WMI_ECHO_RSP_EVENTID, NULL, 0, 20); +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wil6210_rgf.h" +#include "txrx.h" + +static void print_vring(struct seq_file *s, struct wil6210_priv *wil, + const char *name, struct vring *vring) +{ + void __iomem *x = wmi_addr(wil, vring->hwtail); + seq_printf(s, "VRING %s = {\n", name); + seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); + seq_printf(s, " va = 0x%p\n", vring->va); + seq_printf(s, " size = %d\n", vring->size); + seq_printf(s, " swtail = %d\n", vring->swtail); + seq_printf(s, " swhead = %d\n", vring->swhead); + seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); + if (x) + seq_printf(s, "0x%08x\n", ioread32(x)); + else + seq_printf(s, "???\n"); + if (vring->va && (vring->size < 1025)) { + int i; + for (i = 0; i < vring->size; i++) { + struct vring_tx_desc *d = &vring->va[i].tx; + if ((i % 64) == 0 && (i != 0)) + seq_printf(s, "\n"); + seq_printf(s, "%s", (d->dma.status & BIT(0)) ? + "S" : (vring->ctx[i] ? "H" : "h")); + } + seq_printf(s, "\n"); + } + seq_printf(s, "}\n"); +} + +static int vring_debugfs_show(struct seq_file *s, void *data) +{ + int i; + struct wil6210_priv *wil = s->private; + print_vring(s, wil, "rx", &wil->vring_rx); + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + struct vring *vring = &(wil->vring_tx[i]); + if (vring->va) { + char name[10]; + snprintf(name, sizeof(name), "tx_%2d", i); + print_vring(s, wil, name, vring); + } + } + return 0; +} + +static int vring_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, vring_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_vring = { + .open = vring_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static void print_ring(struct seq_file *s, const char *prefix + , void __iomem *off) +{ + struct wil6210_priv *wil = s->private; + struct wil6210_mbox_ring r; + int rsize; + int i; + memcpy_fromio_32(&r, off, sizeof(r)); + /* + * we just read memory block from NIC. This memory may be + * garbage. Check validity before using it. + */ + rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); + seq_printf(s, "ring %s = {\n", prefix); + seq_printf(s, " base = 0x%08x\n", r.base); + seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); + seq_printf(s, " tail = 0x%08x\n", r.tail); + seq_printf(s, " head = 0x%08x\n", r.head); + if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { + seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", + sizeof(struct wil6210_mbox_ring_desc)); + goto out; + } + if (!wmi_addr(wil, r.base) || + !wmi_addr(wil, r.tail) || + !wmi_addr(wil, r.head)) { + seq_printf(s, " ??? pointers are garbage?\n"); + goto out; + } + for (i = 0; i < rsize; i++) { + struct wil6210_mbox_ring_desc d; + struct wil6210_mbox_hdr hdr; + size_t delta = i * sizeof(d); + void __iomem *x = wil->csr + HOSTADDR(r.base) + + delta; + memcpy_fromio_32(&d, x, sizeof(d)); + seq_printf(s, " [%2x] %s %s%s 0x%08x", i, + d.sync ? "F" : "E", + (r.tail - r.base == delta) ? "t" : " ", + (r.head - r.base == delta) ? "h" : " ", + d.addr); + if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { + seq_printf(s, " -> %04x %04x %04x %02x %02x\n", + hdr.seq, hdr.ctx, hdr.type, hdr.flags, hdr.len); + if (hdr.len <= MAX_MBOXITEM_SIZE) { + int n = 0; + unsigned char printbuf[16 * 3 + 2]; + unsigned char databuf[MAX_MBOXITEM_SIZE]; + void __iomem *src = wmi_buffer(wil, d.addr) + + sizeof(struct wil6210_mbox_hdr); + /* + * No need to check @src for validity - + * we already validated @d.addr while + * reading header + */ + memcpy_fromio_32(databuf, src, hdr.len); + while (n < hdr.len) { + int l = min(hdr.len - n, 16); + hex_dump_to_buffer(databuf + n, l, + 16, 1, printbuf, + sizeof(printbuf), + false); + seq_printf(s, " : %s\n", printbuf); + n += l; + } + } + } else + seq_printf(s, "\n"); + } + out: + seq_printf(s, "}\n"); +} + +static int mbox_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + print_ring(s, "tx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx)); + print_ring(s, "rx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx)); + return 0; +} + +static int mbox_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, mbox_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_mbox = { + .open = mbox_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int debugfs_iomem_x32_set(void *data, u64 val) +{ + iowrite32(val, (void __iomem *)data); + wmb(); + return 0; +} + +static int debugfs_iomem_x32_get(void *data, u64 *val) +{ + *val = ioread32((void __iomem *)data); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get, + debugfs_iomem_x32_set, "0x%08llx\n"); + +static struct dentry *debugfs_create_iomem_x32(const char *name, mode_t mode, + struct dentry *parent, void __iomem *value) +{ + return debugfs_create_file(name, mode, parent, + (void * __force)value, &fops_iomem_x32); +} + +static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, + const char *name, struct dentry *parent, u32 off) { + struct dentry *d = debugfs_create_dir(name, parent); + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUGO, d, + wil->csr + off); + debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUGO, d, + wil->csr + off + 4); + debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUGO, d, + wil->csr + off + 8); + debugfs_create_iomem_x32("ICS", S_IWUGO, d, + wil->csr + off + 12); + debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUGO, d, + wil->csr + off + 16); + debugfs_create_iomem_x32("IMS", S_IWUGO, d, + wil->csr + off + 20); + debugfs_create_iomem_x32("IMC", S_IWUGO, d, + wil->csr + off + 24); + return 0; +} + +static u32 mem_addr; + +static int memread_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + void __iomem *a = wmi_buffer(wil, mem_addr); + if (a) + seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); + else + seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); + return 0; +} + +static int memread_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, memread_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_memread = { + .open = memread_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int default_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t read_file_ioblob(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + enum { max_count = 4096 }; + struct debugfs_blob_wrapper *blob = file->private_data; + loff_t pos = *ppos; + size_t available = blob->size; + void *buf; + size_t ret; + if (pos < 0) + return -EINVAL; + if (pos >= available || !count) + return 0; + if (count > available - pos) + count = available - pos; + if (count > max_count) + count = max_count; + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + pos, + count); + ret = copy_to_user(user_buf, buf, count); + kfree(buf); + if (ret == count) + return -EFAULT; + count -= ret; + *ppos = pos + count; + return count; +} + +static const struct file_operations fops_ioblob = { + .read = read_file_ioblob, + .open = default_open, + .llseek = default_llseek, +}; + +static struct dentry *debugfs_create_ioblob(const char *name, mode_t mode, + struct dentry *parent, + struct debugfs_blob_wrapper *blob) +{ + return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); +} +/*---reset---*/ +static ssize_t write_file_reset(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct net_device *ndev = wil_to_ndev(wil); + /** + * BUG: + * this code does NOT sync device state with the rest of system + * use with care, debug only!!! + */ + rtnl_lock(); + dev_close(ndev); + ndev->flags &= ~IFF_UP; + rtnl_unlock(); + wil_reset(wil); + return len; +} + +static const struct file_operations fops_reset = { + .write = write_file_reset, + .open = default_open, +}; +/*---------Tx descriptor------------*/ + +static u32 dbg_txdesc_index; /* = 0; */ + +static int txdesc_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct vring *vring = &(wil->vring_tx[0]); + if (!vring->va) { + seq_printf(s, "No Tx VRING\n"); + return 0; + } + if (dbg_txdesc_index < vring->size) { + struct vring_tx_desc *d = &(vring->va[dbg_txdesc_index].tx); + u32 *u = (u32 *)d; + struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; + seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); + seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[0], u[1], u[2], u[3]); + seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[4], u[5], u[6], u[7]); + seq_printf(s, " SKB = %p\n", skb); + if (skb) { + unsigned char printbuf[16 * 3 + 2]; + int i = 0; + int len = skb_headlen(skb); + void *p = skb->data; + seq_printf(s, " len = %d\n", len); + while (i < len) { + int l = min(len - i, 16); + hex_dump_to_buffer(p + i, l, 16, 1 + , printbuf, sizeof(printbuf) + , false); + seq_printf(s, " : %s\n", printbuf); + i += l; + } + } + seq_printf(s, "}\n"); + } else { + seq_printf(s, "TxDesc index (%d) >= size (%d)\n", + dbg_txdesc_index, vring->size); + } + return 0; +} + +static int txdesc_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, txdesc_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_txdesc = { + .open = txdesc_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; +/*----------------*/ +int wil6210_debugfs_init(struct wil6210_priv *wil) +{ + struct dentry *pseudo; + struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, + wil_to_wiphy(wil)->debugfsdir); + + if (IS_ERR_OR_NULL(dbg)) + return -ENODEV; + debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); + debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); + debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); + debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUGO, dbg, + &dbg_txdesc_index); + wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg + , HOSTADDR(RGF_USER_USER_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg + , HOSTADDR(RGF_DMA_EP_TX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg + , HOSTADDR(RGF_DMA_EP_RX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg + , HOSTADDR(RGF_DMA_EP_MISC_ICR)); + pseudo = debugfs_create_dir("PSEUDO_ISR", dbg); + if (!IS_ERR_OR_NULL(pseudo)) { + debugfs_create_iomem_x32("CAUSE", S_IRUGO, pseudo, + wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + debugfs_create_iomem_x32("MASK_SW", S_IRUGO, pseudo, + wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + debugfs_create_iomem_x32("MASK_FW", S_IRUGO, pseudo, + wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); + } + { + struct dentry *rst = debugfs_create_dir("Reset", dbg); + if (!IS_ERR_OR_NULL(rst)) { + debugfs_create_iomem_x32("vec0", S_IRUGO | S_IWUGO, rst, + wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0)); + debugfs_create_iomem_x32("vec1", S_IRUGO | S_IWUGO, rst, + wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_1)); + debugfs_create_iomem_x32("vec2", S_IRUGO | S_IWUGO, rst, + wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2)); + debugfs_create_iomem_x32("vec3", S_IRUGO | S_IWUGO, rst, + wil->csr + HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3)); + } + } + debugfs_create_u32("mem_addr", S_IRUGO | S_IWUGO, dbg, &mem_addr); + debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); + debugfs_create_file("reset", S_IWUGO, dbg, wil, &fops_reset); + + wil->rgf_blob.data = (void * __force)wil->csr + 0; + wil->rgf_blob.size = 0xa000; + debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); + wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; + wil->fw_code_blob.size = 0x40000; + debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, + &wil->fw_code_blob); + wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; + wil->fw_data_blob.size = 0x8000; + debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, + &wil->fw_data_blob); + wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; + wil->fw_peri_blob.size = 0x18000; + debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, + &wil->fw_peri_blob); + wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; + wil->uc_code_blob.size = 0x8000; + debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, + &wil->uc_code_blob); + wil->uc_data_blob.data = (void * __force)wil->csr + 0xa8000; + wil->uc_data_blob.size = 0x2000; + debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, + &wil->uc_data_blob); + + return 0; +} + +void wil6210_debugfs_remove(struct wil6210_priv *wil) +{ + debugfs_remove_recursive(wil->debug); + wil->debug = NULL; +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/ifc.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# +# Print and set $WLAN to the name of the +# network interface for the 'wil6210' driver +# + +DRV="wil6210" + +for f in /sys/class/net/*; do { + drv=`readlink $f/device/driver`; + drv=${drv##.*/} + if [[ $drv == $DRV ]]; then { + ifc=${f#/sys/class/net/} + echo $ifc + export WLAN=$ifc + break + } ; fi +} ; done + --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "wil6210.h" +#include "wil6210_rgf.h" + +/** + * Theory of operation: + * + * There is ISR pseudo-cause register, + * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE + * Its bits represents OR'ed bits from 3 real ISR registers: + * TX, RX, and MISC. + * + * Registers may be configured to either "write 1 to clear" or + * "clear on read" mode + * + * When handling interrupt, one have to mask/unmask interrupts for the + * real ISR registers, or hardware may malfunction. + * + */ + +#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) +#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE +#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ + BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) +#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) + +static inline u32 ioread32_and_clear(void __iomem *addr) +{ + u32 x = ioread32(addr); +#if !defined(WIL6210_ISR_COR) + iowrite32(x, addr); +#endif + return x; +} + +static void wil6210_mask_irq(struct wil6210_priv *wil) +{ +#if 0 + wil_info(wil, "%s()\n", __func__); +#endif + clear_bit(wil_status_irqen, &wil->status); + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMS)); + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMS)); + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_unmask_irq(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_RX, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMC)); + iowrite32(WIL6210_IMC_TX, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMC)); + iowrite32(WIL6210_IMC_MISC, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMC)); + set_bit(wil_status_irqen, &wil->status); +} + +void wil6210_disable_irq(struct wil6210_priv *wil) +{ + wil6210_mask_irq(wil); +} + +void wil6210_enable_irq(struct wil6210_priv *wil) +{ +#if 0 + wil_info(wil, "%s()\n", __func__); +#endif +#if defined(WIL6210_ISR_COR) + /* configure to Clear-On-Read */ + iowrite32(0xFFFFFFFFUL, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(0xFFFFFFFFUL, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(0xFFFFFFFFUL, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICC)); +#else + iowrite32(0, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(0, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(0, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICC)); +#endif + + wil6210_unmask_irq(wil); +} + +static irqreturn_t wil6210_irq_rx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil->isr_rx; +#if 0 + wil_info(wil, "ISR RX 0x%08x\n", isr); +#endif + if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { +#if 0 + wil_info(wil, "RX done\n"); +#endif + isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; + rx_handle(wil); + } + if (isr) + wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_tx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil->isr_tx; +#if 0 + wil_info(wil, "ISR TX 0x%08x\n", isr); +#endif + if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { + int i; +#if 0 + wil_info(wil, "TX done\n"); +#endif + isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; + for (i = 0; i < 24; i++) { + u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); + if (isr & mask) { + isr &= ~mask; +#if 0 + wil_info(wil, "TX done(%i)\n", i); +#endif + tx_complete(wil, i); + } + } + } + if (isr) + wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_misc(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil->isr_misc; +#if 0 + wil_info(wil, "ISR MISC 0x%08x\n", isr); +#endif + if (isr & ISR_MISC_FW_READY) { + wil_info(wil, "IRQ: FW ready\n"); + /** + * Actual FW ready indicated by the + * WMI_FW_READY_EVENTID + */ + isr &= ~ISR_MISC_FW_READY; + } + if (isr & ISR_MISC_MBOX_EVT) { +#if 0 + wil_info(wil, "MBOX event\n"); +#endif + wmi_recv_cmd(wil); + isr &= ~ISR_MISC_MBOX_EVT; + } + if (isr) + wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); + return IRQ_HANDLED; +} + +/** + * thread IRQ handler + */ +static irqreturn_t wil6210_thread_irq(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; +#if 0 + wil_info(wil, "Thread IRQ\n"); +#endif + /* Discover real IRQ cause */ + if (wil->isr_misc) { + wil6210_irq_misc(irq, cookie); + wil->isr_misc = 0; + } + wil6210_unmask_irq(wil); + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_hardirq(int irq, void *cookie) +{ + irqreturn_t rc = IRQ_HANDLED; + struct wil6210_priv *wil = cookie; + u32 pseudo_cause = ioread32(wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + /** + * pseudo_cause is Clear-On-Read, no need to ACK + */ + if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) + return IRQ_NONE; + /* FIXME: IRQ mask debug */ + if (!test_bit(wil_status_irqen, &wil->status)) { + u32 icm_rx = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_rx = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_rx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_tx = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_tx = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_tx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_misc = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_misc = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_misc = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMV)); + wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" + "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", + pseudo_cause, + icm_rx, icr_rx, imv_rx, + icm_tx, icr_tx, imv_tx, + icm_misc, icr_misc, imv_misc); + return IRQ_NONE; + } + wil6210_mask_irq(wil); + /* Discover real IRQ cause */ + /* All ISR regs configured Clear-On-Read, no need to ACK */ + if (pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) { + wil->isr_rx = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + } + if (pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) { + wil->isr_tx = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + } + if (pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) { + wil->isr_misc = ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + rc = IRQ_WAKE_THREAD; + } + /* process what to be done right in hard IRQ */ + if (wil->isr_rx) { + if (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD) + rc = IRQ_WAKE_THREAD; + else + wil->isr_rx = 0; + } + if (wil->isr_tx) { + if (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD) + rc = IRQ_WAKE_THREAD; + else + wil->isr_tx = 0; + } + /* if thread is requested, it will unmask IRQ */ + if (rc != IRQ_WAKE_THREAD) + wil6210_unmask_irq(wil); +#if 0 + wil_info(wil, "Hard IRQ 0x%08x\n", pseudo_cause); +#endif + return rc; +} + +int wil6210_init_irq(struct wil6210_priv *wil, int irq) +{ + int rc; + wil_info(wil, "%s()\n", __func__); + /* TODO: handle multiple MSI */ + rc = request_threaded_irq(irq, + wil6210_hardirq, wil6210_thread_irq, + wil->n_msi ? 0 : IRQF_SHARED, + WIL_NAME, wil); + if (rc) + return rc; + wil6210_enable_irq(wil); + return 0; +} + +void wil6210_fini_irq(struct wil6210_priv *wil, int irq) +{ + wil_info(wil, "%s()\n", __func__); + wil6210_disable_irq(wil); + free_irq(irq, wil); +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wil6210_rgf.h" + +/** + * We have to read/write to/from NIC in 32-bit chunks; + * otherwise it is not work on 64-bit platform + */ +void memcpy_fromio_32(void *dst, const volatile void __iomem *src, size_t count) +{ + u32 *d = dst; + const volatile u32 __iomem *s = src; + /* size_t is unsigned, if (count%4 != 0) it will wrap */ + for (count += 4; count > 4; count -= 4) + *d++ = readl(s++); +} + +void memcpy_toio_32(volatile void __iomem *dst, const void *src, size_t count) +{ + volatile u32 __iomem *d = dst; + const u32 *s = src; + for (count += 4; count > 4; count -= 4) + writel(*s++, d++); +} + +/* debug */ +void send_echo(struct wil6210_priv *wil); + +static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + int i; + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + wil_info(wil, "%s()\n", __func__); + wil_link_off(wil); + clear_bit(wil_status_fwconnected, &wil->status); + switch (wdev->sme_state) { + case CFG80211_SME_CONNECTED: + cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, + NULL, 0, GFP_KERNEL); + break; + case CFG80211_SME_CONNECTING: + cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); + break; + default: + ; + } + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) + vring_fini_tx(wil, i); +} + +static void connect_timer_fn(unsigned long x) +{ + struct wil6210_priv *wil = (void *)x; + wil_info(wil, "Connect timeout\n"); + _wil6210_disconnect(wil, NULL); +} + +int wil_priv_init(struct wil6210_priv *wil) +{ + wil_info(wil, "%s()\n", __func__); + + mutex_init(&wil->mutex); + mutex_init(&wil->wmi_mutex); + init_completion(&wil->wmi_ready); + wil->pending_connect_cid = -1; + setup_timer(&wil->connect_timer, connect_timer_fn, + (unsigned long)wil); + INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); + INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); + INIT_LIST_HEAD(&wil->pending_wmi_ev); + spin_lock_init(&wil->wmi_ev_lock); + wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); + if (!wil->wmi_wq) + return -EAGAIN; + wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); + if (!wil->wmi_wq_conn) { + destroy_workqueue(wil->wmi_wq); + return -EAGAIN; + } + /* make shadow copy of registers that should not change on run time */ + memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + return 0; +} + +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + del_timer_sync(&wil->connect_timer); + _wil6210_disconnect(wil, bssid); +} + +void wil_priv_deinit(struct wil6210_priv *wil) +{ + wil6210_disconnect(wil, NULL); + wmi_event_flush(wil); + destroy_workqueue(wil->wmi_wq_conn); + destroy_workqueue(wil->wmi_wq); +} + +static void wil_target_reset(struct wil6210_priv *wil) +{ + u32 x; + u32 off; + wil_info(wil, "Resetting...\n"); + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_MASK_0); + x = ioread32(wil->csr + off); + x |= BIT(6); /* hpal_perst_from_pad_src_n_mask */ + iowrite32(x, wil->csr + off); + x |= BIT(7); /* car_perst_rst_src_n_mask */ + iowrite32(x, wil->csr + off); + + off = HOSTADDR(RGF_USER_MAC_CPU_0); + iowrite32(BIT(1), wil->csr + off); /* mac_cpu_man_rst */ + + off = HOSTADDR(RGF_USER_USER_CPU_0); + iowrite32(BIT(1), wil->csr + off); /* user_cpu_man_rst */ + + msleep(100); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2); + iowrite32(0xFE000000, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_1); + iowrite32(0x0000003F, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3); + iowrite32(0x00000170, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0); + iowrite32(0xFFE7FC00, wil->csr + off); + + msleep(100); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3); + iowrite32(0, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2); + iowrite32(0, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_1); + iowrite32(0, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0); + iowrite32(0, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_3); + iowrite32(0x00000001, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_2); + iowrite32(0x00000080, wil->csr + off); + + off = HOSTADDR(RGF_USER_CLKS_CTL_SW_RST_VEC_0); + iowrite32(0, wil->csr + off); + + msleep(2000); + + off = HOSTADDR(RGF_USER_USER_CPU_0); + iowrite32(BIT(0), wil->csr + off); /* user_cpu_man_de_rst */ + + msleep(2000); + wil_info(wil, "Reset completed\n"); +} + +/* + * We reset all the structures, and we reset the UMAC. + * After calling this routine, you're expected to reload + * the firmware. + */ +int wil_reset(struct wil6210_priv *wil) +{ + wil_info(wil, "%s()\n", __func__); + wil6210_disconnect(wil, NULL); + wmi_event_flush(wil); + flush_workqueue(wil->wmi_wq); + flush_workqueue(wil->wmi_wq_conn); + wil6210_disable_irq(wil); + wil->status = 0; + /* TODO: put MAC in reset */ + wil_target_reset(wil); + /* init after reset */ + wil->pending_connect_cid = -1; + INIT_COMPLETION(wil->wmi_ready); + /* make shadow copy of registers that should not change on run time */ + memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + /* TODO: release MAC reset */ + wil6210_enable_irq(wil); + /* we just started MAC, wait for FW ready */ + { + unsigned long to = msecs_to_jiffies(1000); + unsigned long left = wait_for_completion_timeout( + &wil->wmi_ready, to); + if (0 == left) { + wil_err(wil, "Firmware not ready\n"); + return -ETIME; + } else { + wil_info(wil, "FW ready after %d ms\n", + jiffies_to_msecs(to-left)); + } + } + return 0; +} + + +void wil_link_on(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + wil_info(wil, "%s()\n", __func__); + netif_carrier_on(ndev); + netif_tx_wake_all_queues(ndev); +} + +void wil_link_off(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + wil_info(wil, "%s()\n", __func__); + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); +} + +static int __wil_up(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + int rc = wil_reset(wil); + if (rc) + return rc; + /* Apply profile in the following order: */ + /* MAC address - pre-requisite for other commands */ + wil6210_set_mac_address(wil, ndev->dev_addr); + /* Interface type. Set up beaconing if required. After MAC */ + { + u16 wmi_nettype = iftype_nl2wmi(wdev->iftype); + int bi; + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ + wmi_nettype = iftype_nl2wmi(NL80211_IFTYPE_ADHOC); + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + wil_info(wil, "type: STATION\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_AP: + wil_info(wil, "type: AP\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_CLIENT: + wil_info(wil, "type: P2P_CLIENT\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_GO: + wil_info(wil, "type: P2P_GO\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_MONITOR: + wil_info(wil, "type: Monitor\n"); + bi = 0; + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ + break; + default: + return -EOPNOTSUPP; + } + rc = wil6210_set_bcon(wil, bi, wmi_nettype); + if (rc) + return rc; + } + /* Rx VRING. After MAC and beacon */ + rx_init(wil); + return 0; +} + +int wil_up(struct wil6210_priv *wil) +{ + int ret; + wil_info(wil, "%s()\n", __func__); + + mutex_lock(&wil->mutex); + ret = __wil_up(wil); + mutex_unlock(&wil->mutex); + + return ret; +} + +static int __wil_down(struct wil6210_priv *wil) +{ + if (wil->scan_request) { + cfg80211_scan_done(wil->scan_request, true); + wil->scan_request = NULL; + } + wil6210_disconnect(wil, NULL); + rx_fini(wil); + return 0; +} + +int wil_down(struct wil6210_priv *wil) +{ + int ret; + wil_info(wil, "%s()\n", __func__); + + mutex_lock(&wil->mutex); + ret = __wil_down(wil); + mutex_unlock(&wil->mutex); + + return ret; +} + --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/memdump.sh @@ -0,0 +1,9 @@ +#!/bin/bash +### parameter - memdump prefix +P=$1 +### where is wil6210 debugfs? +D=$(find /sys/kernel/debug/ieee80211/ -name wil6210) + +for f in fw_data fw_peri uc_data; do { + cat $D/blob_${f} > ${P}${f} +} done --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "wil6210.h" +#include "cfg80211.h" + +static int wil_open(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + wil_info(wil, "%s()\n", __func__); + + return wil_up(wil); +} + +static int wil_stop(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + wil_info(wil, "%s()\n", __func__); + + return wil_down(wil); +} + +/* + * AC to queue mapping + * + * AC_VO -> queue 3 + * AC_VI -> queue 2 + * AC_BE -> queue 1 + * AC_BK -> queue 0 + */ +static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; +#if 0 +static int wil_tid_to_queue(u16 tid) +{ + if (tid >= ARRAY_SIZE(wil_1d_to_queue)) + return -EINVAL; + + return wil_1d_to_queue[tid]; +} +#endif +static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) +{ +#ifdef WIL_DEBUG_TXRX + struct wil6210_priv *wil = ndev_to_wil(ndev); +#endif + u16 ret; + skb->priority = cfg80211_classify8021d(skb); + + ret = wil_1d_to_queue[skb->priority]; +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "%s() %d -> %d\n", __func__, + (int)skb->priority, (int)ret); +#endif + return ret; +} + +static const struct net_device_ops wil_netdev_ops = { + .ndo_open = wil_open, + .ndo_stop = wil_stop, + .ndo_start_xmit = wil_start_xmit, + .ndo_select_queue = wil_select_queue, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +void *wil_if_alloc(struct device *dev, void __iomem *csr) +{ + struct net_device *ndev; + struct wireless_dev *wdev; + struct wil6210_priv *wil; + int ret = 0; + wdev = wil_cfg80211_init(dev); + if (IS_ERR(wdev)) { + dev_err(dev, "wil_cfg80211_init failed\n"); + return wdev; + } + + wil = wdev_to_wil(wdev); + wil->csr = csr; + wil->wdev = wdev; + + ret = wil_priv_init(wil); + if (ret) { + dev_err(dev, "wil_priv_init failed\n"); + goto out_wdev; + } + + wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ + /* default monitor channel */ + wil->monitor_chan = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; + + ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); + if (!ndev) { + dev_err(dev, "alloc_netdev_mqs failed\n"); + ret = -ENOMEM; + goto out_priv; + } + + ndev->netdev_ops = &wil_netdev_ops; + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + + wil_link_off(wil); + return wil; + +/*out_profile:*/ + free_netdev(ndev); + + out_priv: + wil_priv_deinit(wil); + + out_wdev: + wil_wdev_free(wil); + return ERR_PTR(ret); +} + +void wil_if_free(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + if (!ndev) + return; + + free_netdev(ndev); + wil_priv_deinit(wil); + wil_wdev_free(wil); +} + +int wil_if_add(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + int ret; + + ret = register_netdev(ndev); + if (ret < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret); + return ret; + } + wil_link_off(wil); + + return 0; +} + +void wil_if_remove(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + unregister_netdev(ndev); +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" + +static int use_msi; /* TODO: set default to 1 or 3 when hardware fixed */ +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, + " Use MSI interrupt: 0 - (default) - don't, 1 - single, or 3"); + +/* debug */ +void send_echo(struct wil6210_priv *wil); + +/* Bus ops */ +static int if_pcie_enable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + int rc; + wil_info(wil, "%s()\n", __func__); + /* + * how many MSI interrupts to request? + */ + wil->n_msi = use_msi; + /* TODO: how to deal with 3 MSI? */ + if (wil->n_msi) { + wil_info(wil, "Setup %d MSI interrupts\n", use_msi); + rc = pci_enable_msi_block(pdev, wil->n_msi); + if (rc) { + wil_err(wil, "pci_enable_msi failed, use INTx\n"); + wil->n_msi = 0; + } + } else { + wil_info(wil, "MSI interrupts disabled, use INTx\n"); + } + rc = wil6210_init_irq(wil, pdev->irq); + if (rc) + return rc; + /* need reset here to obtain MAC */ + rc = wil_reset(wil); + if (rc) + goto release_irq; + pci_set_master(pdev); + + return 0; + release_irq: + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + return rc; +} + +static int if_pcie_disable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + struct device *dev = wil_to_dev(wil); + dev_info(dev, "%s()\n", __func__); + + pci_clear_master(pdev); + /* disable IRQ */ + /* release IRQ */ + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + /* disable HW */ +#if 0 + wil_reset(wil); +#endif + return 0; +} + +static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct wil6210_priv *wil; + struct device *dev = &pdev->dev; + void __iomem *csr; + int rc; + dev_info(dev, "%s()\n", __func__); + + /* check HW */ + dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { + dev_err(&pdev->dev, "Not " WIL_NAME "? " + "BAR0 size is %lu while expecting %lu\n", + (unsigned long)pci_resource_len(pdev, 0), + WIL6210_MEM_SIZE); + return -ENODEV; + } + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + /* rollback to err_disable_pdev */ + + rc = pci_request_region(pdev, 0, WIL_NAME); + if (rc) { + dev_err(&pdev->dev, "pci_request_region failed\n"); + goto err_disable_pdev; + } + /* rollback to err_release_reg */ + csr = pci_ioremap_bar(pdev, 0); + if (!csr) { + dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); + rc = -ENODEV; + goto err_release_reg; + } + /* rollback to err_iounmap */ + dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], + csr); + + wil = wil_if_alloc(dev, csr); + if (IS_ERR(wil)) { + rc = (int)PTR_ERR(wil); + dev_err(dev, "wil_if_alloc failed: %d\n", rc); + goto err_iounmap; + } + /* rollback to if_free */ + + pci_set_drvdata(pdev, wil); + wil->pdev = pdev; + + /* FW should raise IRQ when ready */ + rc = if_pcie_enable(wil); + if (rc) { + wil_err(wil, "Enable device failed\n"); + goto if_free; + } + /* rollback to bus_disable */ + + rc = wil_if_add(wil); + if (rc) { + wil_err(wil, "wil_if_add failed: %d\n", rc); + goto bus_disable; + } + wil6210_debugfs_init(wil); + /* rollback to debugfs_exit */ + + { /* print various info */ + struct net_device *ndev = wil_to_ndev(wil); + const char *pdev_name = pci_name(pdev); + const char *wiphydev_name = dev_name(wil_to_dev(wil)); + const char *ndev_name = netdev_name(ndev); + const char *ifc_name = ndev->name; + struct pci_driver *drv = pci_dev_driver(pdev); + const char *drv_name = drv ? drv->name : "(no drv)"; + pr_info("Driver : <%s>\n", drv_name ?: "(null)"); + pr_info("PCI dev : <%s>\n", pdev_name ?: "(null)"); + pr_info("Net dev : <%s>\n", ndev_name ?: "(null)"); + pr_info("Net ifc : <%s>\n", ifc_name ?: "(null)"); + pr_info("Wiphy : <%s>\n", wiphydev_name ?: "(null)"); + + } + send_echo(wil); + return 0; +#if 0 + debugfs_exit: + wil6210_debugfs_remove(wil); +#endif + bus_disable: + if_pcie_disable(wil); + if_free: + wil_if_free(wil); + err_iounmap: + pci_iounmap(pdev, csr); + err_release_reg: + pci_release_region(pdev, 0); + err_disable_pdev: + pci_disable_device(pdev); + return rc; +} + +static void wil_pcie_remove(struct pci_dev *pdev) +{ + struct wil6210_priv *wil = pci_get_drvdata(pdev); + wil_info(wil, "%s()\n", __func__); + + wil6210_debugfs_remove(wil); + if_pcie_disable(wil); + wil_if_remove(wil); + wil_if_free(wil); + pci_iounmap(pdev, wil->csr); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { + { PCI_DEVICE(PCI_VENDOR_ID_WIL6210, PCI_DEVICE_ID_WIL6210), + /*.driver_data = (kernel_ulong_t)0,*/}, + { /* end: all zeroes */ }, +}; +MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); + +static struct pci_driver wil_driver = { + .probe = wil_pcie_probe, + .remove = __devexit_p(wil_pcie_remove), + .id_table = wil6210_pcie_ids, + .name = WIL_NAME, +}; + + +static int __init wil_init_module(void) +{ + return pci_register_driver(&wil_driver); +} + +static void __exit wil_exit_module(void) +{ + pci_unregister_driver(&wil_driver); +} + +module_init(wil_init_module); +module_exit(wil_exit_module); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Qualcomm Atheros "); +MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/read_dw.sh @@ -0,0 +1,7 @@ +#!/bin/bash +### parameter - FW address to read +A=$1 +### where is wil6210 debugfs? +D=$(find /sys/kernel/debug/ieee80211/ -name wil6210) +echo $A > $D/mem_addr +cat $D/mem_val --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/tools/Makefile @@ -0,0 +1,4 @@ +hostprogs-y := trace + +always := $(hostprogs-y) + --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/tools/trace.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Dumps firmware trace. + * + * Uses binary representation of 'strings' file, + * it should be named fw_strings.bin and be in the current directory + * Periodically reads peripheral memory like in blob_fw_peri on the debugfs + * Name of peripheral memory file passed as parameter + */ + +typedef uint32_t u32; +typedef unsigned int uint; + +struct module_level_enable { /* Little Endian */ + uint error_level_enable:1; + uint warn_level_enable:1; + uint info_level_enable:1; + uint verbose_level_enable:1; + uint reserved0:4; +} __attribute__((packed)); + +struct log_trace_header { /* Little Endian */ + uint strring_offset:20; /* the offset of the trace string in the strings sections */ + uint module:4; /* module that outputs the trace */ + uint level:2; /* 0 - Error + 1- WARN + 2 - INFO + 3 - VERBOSE */ + uint parameters_num:2; /* [0..3] */ + uint is_string:1; /* this bit was timestamp_present:1; and changed to indicate if the printf uses %s */ + uint signature:3; /* should be 5 (2'101) in valid header */ +} __attribute__((packed)); + +union log_event { + struct log_trace_header hdr; + u32 param; +} __attribute__((packed)); + +struct log_table_header { + u32 write_ptr; /* incremented by trace producer every write */ + struct module_level_enable module_level_enable[16]; + union log_event evt[0]; +} __attribute__((packed)); + +static size_t read_all(int f, void *buf, size_t n) +{ + size_t actual = 0, r; + do { + r = read(f, buf + actual, n - actual); + actual += r; + } while ((r > 0) && (actual < n)); + return actual; +} + +static void *read_file(const char *name, size_t *size) +{ + int f = open(name, O_RDONLY); + size_t sz = *size; + size_t r; + void *buf; + if (f < 0) + return NULL; + + if (!sz) { + sz = lseek(f, 0, SEEK_END); + lseek(f, 0, SEEK_SET); + } + buf = malloc(sz); + if (!buf) { + close(f); + return NULL; + } + r = read_all(f, buf, sz); + close(f); + if (r != sz) { + printf("Error: from %s read %zd bytes out of %zd\n", + name, r, sz); + free(buf); + return NULL; + } + *size = sz; + return buf; +} + +static char *strings_bin = "fw_strings.bin"; +static char *peri = "peri.dump"; +enum { + log_buf_sizedw = 0x1000/4, + str_mask = 0xFFFF, +}; + +static void *peri_buf; +static void *str_buf; +static size_t str_sz; +static u32 rptr = 0; +static const char* levels[] = { + "E", + "W", + "I", + "V", +}; + +static const char *modules[16]; + +static void do_parse(void) +{ + struct log_table_header *h = peri_buf; + u32 wptr = h->write_ptr; + if ((wptr - rptr) >= log_buf_sizedw) { + /* overflow; try to parse last wrap */ + rptr = wptr - log_buf_sizedw; + } + for (;(long)(wptr - rptr) > 0; rptr++) { + int i; + u32 p[3] = {0}; + union log_event *evt = &h->evt[rptr % log_buf_sizedw]; + const char *fmt; + if (evt->hdr.signature != 5) + continue; + if (evt->hdr.strring_offset > str_sz) + continue; + if (evt->hdr.parameters_num > 3) + continue; + fmt = str_buf + evt->hdr.strring_offset; + for (i = 0; i < evt->hdr.parameters_num; i++) { + p[i] = h->evt[(rptr + i + 1) % log_buf_sizedw].param; + } + printf("[%6d] %9s %s :", rptr, modules[evt->hdr.module], + levels[evt->hdr.level]); + if (evt->hdr.is_string) { + printf(fmt, str_buf + (p[0] & str_mask), + str_buf + (p[1] & str_mask), + str_buf + (p[2] & str_mask)); + } else { + printf(fmt, p[0], p[1], p[2]); + } + printf("\n"); + rptr += evt->hdr.parameters_num; + } + fflush(stdout); +} + +int main(int argc, char* argv[]) +{ + if (argc > 1) + peri = argv[1]; + const char *mod; + size_t peri_sz = 8*1024; + int i; + str_buf = read_file(strings_bin, &str_sz); + mod = str_buf; + peri_buf = read_file(peri, &peri_sz); + if (!str_buf || !peri_buf) + return -1; + struct log_table_header *h = peri_buf; + u32 wptr = h->write_ptr; + if ((wptr - rptr) >= log_buf_sizedw) { + /* overflow; try to parse last wrap */ + rptr = wptr - log_buf_sizedw; + } + printf(" wptr = %d rptr = %d\n", wptr, rptr); + for (i = 0; i < 16; i++) { + modules[i] = mod; + struct module_level_enable *m = &h->module_level_enable[i]; + printf(" %s[%2d] : %s%s%s%s\n", modules[i], i, + m->error_level_enable ? "E" : " ", + m->warn_level_enable ? "W" : " ", + m->info_level_enable ? "I" : " ", + m->verbose_level_enable ? "V" : " "); + mod = strchr(mod, '\0') + 1; + } + for(;;) { + do_parse(); + sleep(1); + int f = open(peri, O_RDONLY); + size_t r = read_all(f, peri_buf, peri_sz); + close(f); + if (r != peri_sz) + break; + } + return 0; +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/tools/vhex2bin.py @@ -0,0 +1,17 @@ +#!/usr/bin/python + +''' +Convert .vhex file into binary representation +''' + +import sys +import fileinput +import re +import struct + +for line in fileinput.input(): + if not re.match("[0-9a-f]+", line): + continue + x = int(line,16) + s = struct.pack(" $D/txdesc_index +cat $D/txdesc --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" +#include "txrx.h" +#include "wil6210_rgf.h" + +static bool rtap_include_phy_info; +module_param(rtap_include_phy_info, bool, S_IRUGO); +MODULE_PARM_DESC(rtap_include_phy_info, + " Include PHY info in the radiotap header, default - no"); + +static inline int vring_is_empty(struct vring *vring) +{ + return vring->swhead == vring->swtail; +} + +static inline u32 vring_next_tail(struct vring *vring) +{ + return (vring->swtail+1)%vring->size; +} + +static inline void vring_advance_head(struct vring *vring) +{ + vring->swhead = (vring->swhead+1)%vring->size; +} + +static inline int vring_is_full(struct vring *vring) +{ + return vring_next_tail(vring) == vring->swhead; +} + +static int vring_alloc(struct wil6210_priv *wil, struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + int i; + BUILD_BUG_ON(sizeof(vring->va[0]) != 32); + wil_info(wil, "%s()\n", __func__); + vring->swhead = 0; + vring->swtail = 0; + vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); + if (!vring->ctx) { + wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", + vring->size); + vring->va = NULL; + return -ENOMEM; + } + vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); + if (!vring->va) { + wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", + vring->size); + kfree(vring->ctx); + vring->ctx = NULL; + return -ENOMEM; + } + /* initially, all descriptors are SW owned + * For Tx and Rx, ownership bit is at the same location, thus + * we can use any + */ + for (i = 0; i < vring->size; i++) { + struct vring_tx_desc *d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + } + wil_info(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, + vring->va, (unsigned long long)vring->pa, vring->ctx); + return 0; +} + +static void vring_free(struct wil6210_priv *wil, struct vring *vring, int tx) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + wil_info(wil, "%s()\n", __func__); + while (!vring_is_empty(vring)) { + if (tx) { + struct vring_tx_desc *d = &vring->va[vring->swtail].tx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, + DMA_TO_DEVICE); + } + vring->swtail = vring_next_tail(vring); + } else { /* rx */ + struct vring_rx_desc *d = &vring->va[vring->swtail].rx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, d->dma.length, + DMA_FROM_DEVICE); + kfree_skb(skb); + vring_advance_head(vring); + } + } + dma_free_coherent(dev, sz, vring->va, vring->pa); + kfree(vring->ctx); + vring->pa = 0; + vring->va = NULL; + vring->ctx = NULL; +} + +/** + * Allocate one skb for Rx VRING + * + * Safe to call from IRQ + */ +static int vring_alloc_skb(struct wil6210_priv *wil, + struct vring *vring, u32 i, int headroom) +{ + struct device *dev = wil_to_dev(wil); + unsigned int sz = RX_BUF_LEN; + struct vring_rx_desc *d = &(vring->va[i].rx); + dma_addr_t pa; + /* TODO align */ + struct sk_buff *skb = dev_alloc_skb(sz + headroom); + if (unlikely(!skb)) + return -ENOMEM; + skb_reserve(skb, headroom); + skb_put(skb, sz); + pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) { + kfree_skb(skb); + return -ENOMEM; + } + d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + /* ip_length don't care */ + /* b11 don't care */ + /* error don't care */ + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = sz; + vring->ctx[i] = skb; + return 0; +} + +/** + * Adds radiotap header + * + * Any error indicated as "Bad FCS" + * + * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: + * - Rx descriptor: 32 bytes + * - Phy info + */ +static void rx_add_radiotap_header(struct wil6210_priv *wil, + struct sk_buff *skb, + struct vring_rx_desc *d) +{ + struct wil6210_rtap { + struct ieee80211_radiotap_header rthdr; + /* fields should be in the order of bits in rthdr.it_present */ + /* flags */ + u8 flags; + /* channel */ + __le16 chnl_freq __aligned(2); + __le16 chnl_flags; + /* MCS */ + u8 mcs_present; + u8 mcs_flags; + u8 mcs_index; + } __packed; + struct wil6210_rtap_vendor { + struct wil6210_rtap rtap; + /* vendor */ + u8 vendor_oui[3] __aligned(2); + u8 vendor_ns; + __le16 vendor_skip; + u8 vendor_data[0]; + } __packed; + struct wil6210_rtap_vendor *rtap_vendor; + int rtap_len = sizeof(struct wil6210_rtap); + int phy_length = 0; /* phy info header size, bytes */ + static char phy_data[128]; + if (rtap_include_phy_info) { + rtap_len = sizeof(*rtap_vendor) + sizeof(*d); + /* calculate additional length */ + if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { + /** + * PHY info starts from 8-byte boundary + * there are 8-byte lines, last line may be partially + * written (HW bug), thus FW configures for last line + * to be excessive. Driver skips this last line. + */ + int len = min_t(int, 8 + sizeof(phy_data), + rxdesc_phy_length(d)); + if (len > 8) { + void *p = skb_tail_pointer(skb); + void *pa = PTR_ALIGN(p, 8); + if (skb_tailroom(skb) >= len + (pa - p)) { + phy_length = len - 8; + memcpy(phy_data, pa, phy_length); + } + } + } + rtap_len += phy_length; + } + + if (skb_headroom(skb) < rtap_len && + pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { + wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); + return; + } + + rtap_vendor = (void *)skb_push(skb, rtap_len); + memset(rtap_vendor, 0, rtap_len); + + rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; + rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); + rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_MCS)); + if (d->dma.status & RX_DMA_STATUS_ERROR) + rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; + + rtap_vendor->rtap.chnl_freq = cpu_to_le16(wil->monitor_chan ? + wil->monitor_chan->center_freq : 58320); + rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); + + rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; + rtap_vendor->rtap.mcs_flags = 0; + rtap_vendor->rtap.mcs_index = rxdesc_mcs(d); + + if (rtap_include_phy_info) { + rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << + IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + /* OUI for Wilocity 04:ce:14 */ + rtap_vendor->vendor_oui[0] = 0x04; + rtap_vendor->vendor_oui[1] = 0xce; + rtap_vendor->vendor_oui[2] = 0x14; + rtap_vendor->vendor_ns = 1; + /* Rx descriptor + PHY data */ + rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + + phy_length); + memcpy(rtap_vendor->vendor_data, d, sizeof(*d)); + memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, + phy_length); + } +} + +/** + * reap 1 frame from @swhead + * + * Safe to call from IRQ + */ +static struct sk_buff *vring_reap_rx(struct wil6210_priv *wil, + struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + struct net_device *ndev = wil_to_ndev(wil); + struct vring_rx_desc *d; + struct sk_buff *skb; + dma_addr_t pa; + unsigned int sz = RX_BUF_LEN; + if (vring_is_empty(vring)) + return NULL; + d = &(vring->va[vring->swhead].rx); + if (!(d->dma.status & RX_DMA_STATUS_DU)) { + /* it is not error, we just reached end of Rx done area */ + return NULL; + } + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); + skb_trim(skb, d->dma.length); + wil->stats.last_mcs_rx = rxdesc_mcs(d); + /* use radiotap header only if required */ + if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) + rx_add_radiotap_header(wil, skb, d); +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); + print_hex_dump(KERN_INFO, "Rx ", DUMP_PREFIX_NONE, 32, 4, d + , sizeof(*d), false); +#endif + vring_advance_head(vring); + return skb; +} + +/** + * allocate and fill up to @count buffers in rx ring + * buffers posted at @swtail + */ +static int rx_refill(struct wil6210_priv *wil, int count) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + u32 next_tail; + int rc = 0; + int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? + WIL6210_RTAP_SIZE : 0; + for (; next_tail = vring_next_tail(v), + (next_tail != v->swhead) && (count-- > 0); + v->swtail = next_tail) { + rc = vring_alloc_skb(wil, v, v->swtail, headroom); + if (rc) { + wil_err(wil, "Error %d in rx_refill[%d]\n", + rc, v->swtail); + break; + } + } + iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); + return rc; +} + +static int netif_rx_any(struct sk_buff *skb) +{ + if (in_interrupt()) + return netif_rx(skb); + else + return netif_rx_ni(skb); +} + +/** + * Proceed all completed skb's from Rx VRING + * + * Safe to call from IRQ + */ +void rx_handle(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + struct sk_buff *skb; + if (!v->va) { + wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); + return; + } +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "%s()\n", __func__); +#endif + while (NULL != (skb = vring_reap_rx(wil, v))) { +#ifdef WIL_DEBUG_TXRX + print_hex_dump(KERN_INFO, "Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); +#endif + skb_orphan(skb); + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + skb->dev = ndev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + } else { + skb->protocol = eth_type_trans(skb, ndev); + } + if (likely(netif_rx_any(skb) == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + + } else { + ndev->stats.rx_dropped++; + } + } + rx_refill(wil, v->size); +} + +int rx_init(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct vring *vring = &wil->vring_rx; + int rc; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_CFG_RX_CHAIN_CMD cfg; + } __packed wmi_rx_cfg = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 1, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_CFG_RX_CHAIN_CMD), + }, + .wmi = { + .id = WMI_CFG_RX_CHAIN_CMDID, + .info1 = 0, + }, + .cfg = { + .action = ADD_RX_CHAIN, + .sw_ring = { + .max_mpdu_size = RX_BUF_LEN, + }, + .mid = 0, /* TODO - what is it? */ + .decap_trans_type = DECAP_TYPE_802_3, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_CFG_RX_CHAIN_DONE_EVENT cfg; + } __packed wmi_rx_cfg_reply; + wil_info(wil, "%s()\n", __func__); + vring->size = WIL6210_RX_RING_SIZE; + rc = vring_alloc(wil, vring); + if (rc) + return rc; + wmi_rx_cfg.cfg.sw_ring.ring_mem_base = vring->pa; + wmi_rx_cfg.cfg.sw_ring.ring_size = vring->size; + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + wmi_rx_cfg.cfg.sniffer_mode = 1; + if (wil->monitor_chan) + wmi_rx_cfg.cfg.sniffer_channel = + wil->monitor_chan->hw_value - 1; + wmi_rx_cfg.cfg.sniffer_phy_info = + (ndev->type == ARPHRD_IEEE80211_RADIOTAP); + /* 0 - CP, 1 - DP */ + wmi_rx_cfg.cfg.sniffer_phy = + (wil->monitor_flags & MONITOR_FLAG_CONTROL) ? + 0 : 1; + } + rc = wmi_call(wil, &wmi_rx_cfg, WMI_CFG_RX_CHAIN_DONE_EVENTID, + &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), 200); + if (rc) + goto err_free; + vring->hwtail = wmi_rx_cfg_reply.cfg.rx_ring_tail_ptr; + wil_info(wil, "Rx init: status %d tail 0x%08x\n", + wmi_rx_cfg_reply.cfg.status, vring->hwtail); + rc = rx_refill(wil, vring->size); + if (rc) + goto err_free; + return 0; + err_free: + vring_free(wil, vring, 0); + return rc; +} + +void rx_fini(struct wil6210_priv *wil) +{ + struct vring *vring = &wil->vring_rx; + wil_info(wil, "%s()\n", __func__); + if (vring->va) { + int rc; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_CFG_RX_CHAIN_CMD cfg; + } __packed wmi_rx_cfg = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 1, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_CFG_RX_CHAIN_CMD), + }, + .wmi = { + .id = WMI_CFG_RX_CHAIN_CMDID, + .info1 = 0, + }, + .cfg = { + .action = DELETE_RX_CHAIN, + .sw_ring = { + .max_mpdu_size = RX_BUF_LEN, + }, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_CFG_RX_CHAIN_DONE_EVENT cfg; + } __packed wmi_rx_cfg_reply; + rc = wmi_call(wil, &wmi_rx_cfg, WMI_CFG_RX_CHAIN_DONE_EVENTID, + &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), + 100); + vring_free(wil, vring, 0); + } +} + +int vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid) +{ + struct wireless_dev *wdev = wil->wdev; + int rc; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_VRING_CFG_CMD cfg; + } __packed wmi_tx_cfg = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 1, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_VRING_CFG_CMD), + }, + .wmi = { + .id = WMI_VRING_CFG_CMDID, + .info1 = 0, + }, + .cfg = { + .action = ADD_VRING, + .sw_ring = { + .max_mpdu_size = TX_BUF_LEN, + }, + .ringid = id, + .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), + .encap_trans_type = ENC_TYPE_802_3, + .mac_ctrl = 0, + .to_resolution = 0, + .agg_max_wsize = 0, + .priority = 0, + .timeslot_us = 0xfff, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_VRING_CFG_DONE_EVENT cfg; + } __packed reply; + struct vring *vring = &wil->vring_tx[id]; + wil_info(wil, "%s(%d)\n", __func__, id); + if (vring->va) { + wil_err(wil, "Tx ring [%d] already allocated\n", id); + rc = -EINVAL; + goto out; + } + vring->size = size; + rc = vring_alloc(wil, vring); + if (rc) + goto out; + wmi_tx_cfg.cfg.sw_ring.ring_mem_base = vring->pa; + wmi_tx_cfg.cfg.sw_ring.ring_size = vring->size; + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: +#if 0 + wmi_tx_cfg.cfg.ds_cfg = STATION_MODE; +#else + wmi_tx_cfg.cfg.ds_cfg = PBSS_MODE; +#endif + break; + case NL80211_IFTYPE_AP: +#if 0 + wmi_tx_cfg.cfg.ds_cfg = AP_MODE; +#else + wmi_tx_cfg.cfg.ds_cfg = PBSS_MODE; +#endif + break; + case NL80211_IFTYPE_P2P_CLIENT: + wmi_tx_cfg.cfg.ds_cfg = STATION_MODE; + break; + case NL80211_IFTYPE_P2P_GO: + wmi_tx_cfg.cfg.ds_cfg = AP_MODE; + break; + default: + rc = -EOPNOTSUPP; + goto out_free; + + } + rc = wmi_call(wil, &wmi_tx_cfg, WMI_VRING_CFG_DONE_EVENTID, + &reply, sizeof(reply), 100); + if (rc) + goto out_free; + if (reply.cfg.status != VRING_CFG_SUCCESS) { + wil_err(wil, "Tx config failed, status 0x%02x\n", + reply.cfg.status); + goto out_free; + } + vring->hwtail = reply.cfg.tx_vring_tail_ptr; + return 0; + out_free: + vring_free(wil, vring, 1); + out: + return rc; +} + +void vring_fini_tx(struct wil6210_priv *wil, int id) +{ +#if 0 + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_VRING_CFG_CMD cfg; + } __packed wmi_tx_cfg = { + .hdr = { + .seq = 1, + .ctx = 1, + .type = 0, + .flags = 1, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_VRING_CFG_CMD), + }, + .wmi = { + .id = WMI_VRING_CFG_CMDID, + .info1 = 0, + }, + .cfg = { + .action = DELETE_VRING, + .sw_ring = { + }, + .ringid = id, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_VRING_CFG_DONE_EVENT cfg; + } __packed reply; +#endif + struct vring *vring = &wil->vring_tx[id]; + if (!vring->va) + return; + wil_info(wil, "%s(%d)\n", __func__, id); +#if 0 + wmi_call(wil, &wmi_tx_cfg, WMI_VRING_CFG_DONE_EVENTID, + &reply, sizeof(reply), 100); +#endif + vring_free(wil, vring, 1); +} + +static struct vring *find_tx_vring(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v = &wil->vring_tx[0]; + if (v->va) + return v; + return NULL; +} + +static int tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len) +{ + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + d->dma.ip_length = 0; + /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ + d->dma.b11 = 0/*14 | BIT(7)*/; + d->dma.error = 0; + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = len; + d->dma.d0 = 0; + d->mac.d[0] = 0; + d->mac.d[1] = 0; + d->mac.d[2] = 0; + d->mac.ucode_cmd = 0; +#if 0 + /* use MCS 1 */ + d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_POS) | + (1 << MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_POS); +#endif +#if 1 + /* use dst index 0 */ + d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_POS) | + (0 << MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_POS); +#endif +#if 0 + d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_POS); + d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_POS); +#endif + /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ + d->mac.d[2] = BIT(MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_POS) | + (1 << MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_POS); + return 0; +} + +static int tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb) +{ + struct device *dev = wil_to_dev(wil); + struct vring_tx_desc *d; + u32 swhead = vring->swhead; + u32 swtail = vring->swtail; + int used = (vring->size + swhead - swtail) % vring->size; + int avail = vring->size - used - 1; + int nr_frags = skb_shinfo(skb)->nr_frags; + int f; + int vring_index = vring - wil->vring_tx; + int i = swhead; + dma_addr_t pa; +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "%s()\n", __func__); +#endif + if (avail < vring->size/8) + netif_tx_stop_all_queues(wil_to_ndev(wil)); + if (avail < 1 + nr_frags) { + wil_err(wil, "Tx ring full. No space for %d fragments\n", + 1 + nr_frags); + return -ENOMEM; + } + d = &(vring->va[i].tx); + + /* FIXME FW can accept only unicast frames for the peer */ + memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); + + pa = dma_map_single(dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "Tx skb %d bytes %p -> %#08llx\n", + skb_headlen(skb), skb->data, (unsigned long long)pa); + print_hex_dump(KERN_INFO, "Tx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data + , skb_headlen(skb), false); +#endif + if (unlikely(dma_mapping_error(dev, pa))) + return -EINVAL; + /* 1-st segment */ + tx_desc_map(d, pa, skb_headlen(skb)); + d->mac.d[2] |= ((nr_frags + 1) << + MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_POS); + vring->ctx[i] = skb; + /* middle segments */ + for (f = 0; f < nr_frags; f++) { + const struct skb_frag_struct *frag = + &skb_shinfo(skb)->frags[f]; + int len = skb_frag_size(frag); + i = (swhead + f + 1) % vring->size; + d = &(vring->va[i].tx); + pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) + goto dma_error; + tx_desc_map(d, pa, len); + vring->ctx[i] = NULL; + } + /* for the last seg only */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_POS); + d->dma.d0 |= BIT(9); + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_POS); + d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_POS); +#ifdef WIL_DEBUG_TXRX + print_hex_dump(KERN_INFO, "Tx ", DUMP_PREFIX_NONE, 32, 4, d + , sizeof(*d), false); +#endif + /* advance swhead */ + vring->swhead = (swhead + nr_frags + 1) % vring->size; +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); +#endif + iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); + return 0; + dma_error: + /* unmap what we have mapped */ + for (; f > -1; f--) { + i = (swhead + f + 1) % vring->size; + d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + if (vring->ctx[i]) + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + else + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + return -EINVAL; +} + + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + struct vring *vring; +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "%s()\n", __func__); +#endif + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + goto drop; + } + if (!test_bit(wil_status_fwconnected, &wil->status)) { + wil_err(wil, "FW not connected\n"); + goto drop; + } + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + wil_err(wil, "Xmit in monitor mode not supported\n"); + goto drop; + } + /* find vring */ + vring = find_tx_vring(wil, skb); + if (!vring) { + wil_err(wil, "No Tx VRING available\n"); + goto drop; + } + /* set up vring entry */ + switch (tx_vring(wil, vring, skb)) { + case 0: + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + return NETDEV_TX_OK; + break; + case -ENOMEM: + return NETDEV_TX_BUSY; + default: + ; /* goto drop; */ + break; + } + drop: + netif_tx_stop_all_queues(ndev); + ndev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + return NET_XMIT_DROP; +} + +/** + * Clean up transmitted skb's from the Tx VRING + * + * Safe to call from IRQ + */ +void tx_complete(struct wil6210_priv *wil, int ringid) +{ + struct device *dev = wil_to_dev(wil); + struct vring *vring = &wil->vring_tx[ringid]; + if (!vring->va) { + wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); + return; + } +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "%s(%d)\n", __func__, ringid); +#endif + while (!vring_is_empty(vring)) { + struct vring_tx_desc *d = &vring->va[vring->swtail].tx; + dma_addr_t pa; + struct sk_buff *skb; + if (!(d->dma.status & TX_DMA_STATUS_DU)) + break; +#ifdef WIL_DEBUG_TXRX + wil_info(wil, "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", + vring->swtail, d->dma.length, d->dma.status, + d->dma.error); + print_hex_dump(KERN_INFO, "TxC ", DUMP_PREFIX_NONE, 32, 4, d + , sizeof(*d), false); +#endif + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + d->dma.addr_low = 0; + d->dma.addr_high = 0; + d->dma.length = 0; + d->dma.status = TX_DMA_STATUS_DU; + vring->swtail = (vring->swtail + 1) % vring->size; + } + { + u32 swhead = vring->swhead; + u32 swtail = vring->swtail; + int used = (vring->size + swhead - swtail) % vring->size; + int avail = vring->size - used - 1; + if (avail > vring->size/4) + netif_tx_wake_all_queues(wil_to_ndev(wil)); + } +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WIL6210_TXRX_H +#define WIL6210_TXRX_H + +#define BUF_SW_OWNED (1) +#define BUF_HW_OWNED (0) + +/* size of max. Rx packet */ +#define RX_BUF_LEN (2048) +#define TX_BUF_LEN (2048) +/* how many bytes to reserve for rtap header? */ +#define WIL6210_RTAP_SIZE (128) + +/* Tx/Rx path */ +/* + * Tx descriptor - MAC part + * [dword 0] + * bit 0.. 9 : lifetime_expiry_value:10 + * bit 10 : interrup_en:1 + * bit 11 : status_en:1 + * bit 12..13 : txss_override:2 + * bit 14 : timestamp_insertion:1 + * bit 15 : duration_preserve:1 + * bit 16..21 : reserved0:6 + * bit 22..26 : mcs_index:5 + * bit 27 : mcs_en:1 + * bit 28..29 : reserved1:2 + * bit 30 : reserved2:1 + * bit 31 : sn_preserved:1 + * [dword 1] + * bit 0.. 3 : pkt_mode:4 + * bit 4 : pkt_mode_en:1 + * bit 5.. 7 : reserved0:3 + * bit 8..13 : reserved1:6 + * bit 14 : reserved2:1 + * bit 15 : ack_policy_en:1 + * bit 16..19 : dst_index:4 + * bit 20 : dst_index_en:1 + * bit 21..22 : ack_policy:2 + * bit 23 : lifetime_en:1 + * bit 24..30 : max_retry:7 + * bit 31 : max_retry_en:1 + * [dword 2] + * bit 0.. 7 : num_of_descriptors:8 + * bit 8..17 : reserved:10 + * bit 18..19 : l2_translation_type:2 + * bit 20 : snap_hdr_insertion_en:1 + * bit 21 : vlan_removal_en:1 + * bit 22..31 : reserved0:10 + * [dword 3] + * bit 0.. 31: ucode_cmd:32 + */ +struct vring_tx_mac { + u32 d[3]; + u32 ucode_cmd; +} __packed; + +/* TX MAC Dword 0 */ +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_LIFETIME_EXPIRY_VALUE_POS 0 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_LIFETIME_EXPIRY_VALUE_LEN 10 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_POS 10 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_INTERRUP_EN_MSK 0x400 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_POS 11 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_STATUS_EN_MSK 0x800 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TXSS_OVERRIDE_POS 12 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TXSS_OVERRIDE_LEN 2 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TXSS_OVERRIDE_MSK 0x3000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TIMESTAMP_INSERTION_POS 14 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TIMESTAMP_INSERTION_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_TIMESTAMP_INSERTION_MSK 0x4000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_DURATION_PRESERVE_POS 15 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_DURATION_PRESERVE_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_DURATION_PRESERVE_MSK 0x8000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_POS 22 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_LEN 5 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_INDEX_MSK 0x7C00000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_POS 27 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_MCS_EN_MSK 0x8000000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_SN_PRESERVED_POS 31 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_SN_PRESERVED_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_0_SN_PRESERVED_MSK 0x80000000 + +/* TX MAC Dword 1 */ +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_POS 0 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_LEN 4 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_MSK 0xF + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_EN_POS 4 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_PKT_MODE_EN_MSK 0x10 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_EN_POS 15 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_EN_MSK 0x8000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_POS 16 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_LEN 4 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_MSK 0xF0000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_POS 20 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_DST_INDEX_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_POS 21 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_LEN 2 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_ACK_POLICY_MSK 0x600000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_LIFETIME_EN_POS 23 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_LIFETIME_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_LIFETIME_EN_MSK 0x800000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_POS 24 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_LEN 7 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_MSK 0x7F000000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_EN_POS 31 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_1_MAX_RETRY_EN_MSK 0x80000000 + +/* TX MAC Dword 2 */ +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_POS 0 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_LEN 8 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_NUM_OF_DESCRIPTORS_MSK 0xFF + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_RESERVED_POS 8 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_RESERVED_LEN 10 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_RESERVED_MSK 0x3FF00 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_POS 18 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_LEN 2 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_L2_TRANSLATION_TYPE_MSK 0xC0000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_POS 20 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_VLAN_REMOVAL_EN_POS 21 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_VLAN_REMOVAL_EN_LEN 1 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_2_VLAN_REMOVAL_EN_MSK 0x200000 + +/* TX MAC Dword 3 */ +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_3_UCODE_CMD_POS 0 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_3_UCODE_CMD_LEN 32 +#define MAC_CFG_DESC_TX_DESCRIPTOR_MAC_3_UCODE_CMD_MSK 0xFFFFFFFF + +/* TX DMA Dword 0 */ +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_LENGTH_POS 0 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_LENGTH_LEN 8 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_LENGTH_MSK 0xFF + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_POS 8 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_LEN 1 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_EOP_MSK 0x100 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_POS 10 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_LEN 1 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_CMD_DMA_IT_MSK 0x400 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_SEGMENT_BUF_DETAILS_POS 11 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_SEGMENT_BUF_DETAILS_LEN 2 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_SEGMENT_BUF_DETAILS_MSK 0x1800 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_SEG_EN_POS 13 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_SEG_EN_LEN 1 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_SEG_EN_MSK 0x2000 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_IPV4_CHECKSUM_EN_POS 14 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_IPV4_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_IPV4_CHECKSUM_EN_MSK 0x4000 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_UDP_CHECKSUM_EN_POS 15 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_UDP_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_POS 16 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_LEN 5 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_QID_MSK 0x1F0000 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_PSEUDO_HEADER_CALC_EN_POS 21 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_PSEUDO_HEADER_CALC_EN_LEN 1 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 + +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_TYPE_POS 30 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_TYPE_LEN 2 +#define DMA_CFG_DESC_TX_DESCRIPTOR_DMA_0_L4_TYPE_MSK 0xC0000000 + + +#define TX_DMA_STATUS_DU BIT(0) + +struct vring_tx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; /* 0..6: mac_length; 7:ip_version */ + u8 error; /* 0..2: err; 3..7: reserved; */ + u8 status; /* 0: used; 1..7; reserved */ + u16 length; +} __packed; + +/* + * Rx descriptor - MAC part + * [dword 0] + * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field + * bit 4.. 6 : connection_id:3 :The Source index that was found during + * Parsing the TA. This field is used to define the source of the packet + * bit 7 : reserved:1 + * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) + * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type + * (management, data, control and extension) + * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype + * bit 16..27 : seq_number:12 The received Sequence number field + * bit 28..31 : extended:4 extended subtype + * [dword 1] + * bit 0.. 3 : reserved + * bit 4.. 5 : key_id:2 + * bit 6 : decrypt_bypass:1 + * bit 7 : security:1 + * bit 8.. 9 : ds_bits:2 + * bit 10 : a_msdu_present:1 from qos header + * bit 11 : a_msdu_type:1 from qos header + * bit 12 : a_mpdu:1 part of AMPDU aggregation + * bit 13 : broadcast:1 + * bit 14 : mutlicast:1 + * bit 15 : reserved:1 + * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet + * is received from + * bit 21..24 : mcs:4 + * bit 25..28 : mic_icr:4 + * bit 29..31 : reserved:3 + * [dword 2] + * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received + * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version + * bit 4 : fc_order:1 The FC Control (b15) -Order + * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field + * bit 8 : esop:1 The QoS (b4) ESOP field + * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field + * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field + * bit 15 : qos_ac_constraint:1 + * bit 16..31 : pn_15_0:16 low 2 bytes of PN + * [dword 3] + * bit 0..31 : pn_47_16:32 high 4 bytes of PN + */ +struct vring_rx_mac { + u32 d0; + u32 d1; + u16 w4; + u16 pn_15_0; + u32 pn_47_16; +} __packed; + +/* + * Rx descriptor - DMA part + * [dword 0] + * bit 0.. 7 : l4_length:8 layer 4 length + * bit 8.. 9 : reserved:2 + * bit 10 : cmd_dma_it:1 + * bit 11..15 : reserved:5 + * bit 16..29 : phy_info_length:14 + * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field + * [dword 1] + * bit 0..31 : addr_low:32 The payload buffer low address + * [dword 2] + * bit 0..15 : addr_high:16 The payload buffer high address + * bit 16..23 : ip_length:8 + * bit 24..30 : mac_length:7 + * bit 31 : ip_version:1 + * [dword 3] + * [byte 12] error + * [byte 13] status + * bit 0 : du:1 + * bit 1 : eop:1 + * bit 2 : error:1 + * bit 3 : mi:1 + * bit 4 : l3_identified:1 + * bit 5 : l4_identified:1 + * bit 6 : phy_info_included:1 + * bit 7 : reserved:1 + * [word 7] length + * + */ + +#define RX_DMA_D0_CMD_DMA_IT BIT(10) + +#define RX_DMA_STATUS_DU BIT(0) +#define RX_DMA_STATUS_ERROR BIT(2) +#define RX_DMA_STATUS_PHY_INFO BIT(6) + +struct vring_rx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; + u8 error; + u8 status; + u16 length; +} __packed; + +struct vring_tx_desc { + struct vring_tx_mac mac; + struct vring_tx_dma dma; +} __packed; + +struct vring_rx_desc { + struct vring_rx_mac mac; + struct vring_rx_dma dma; +} __packed; + +union vring_desc { + struct vring_tx_desc tx; + struct vring_rx_desc rx; +} __packed; + +static inline int rxdesc_phy_length(struct vring_rx_desc *d) +{ + return GET_BITS(d->dma.d0, 16, 29); +} + +static inline int rxdesc_mcs(struct vring_rx_desc *d) +{ + return GET_BITS(d->mac.d1, 21, 24); +} + +#endif /* WIL6210_TXRX_H */ --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/vrwatch.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Watch VRING's +### where is wil6210 debugfs? +D=$(find /sys/kernel/debug/ieee80211/ -name wil6210) +exec watch -n 0.2 -d cat $D/vrings + --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WIL6210_H__ +#define __WIL6210_H__ + +#include +#include +#include + +#define WIL_NAME "wil6210" +#define PCI_VENDOR_ID_WIL6210 (0x1ae9) +#define PCI_DEVICE_ID_WIL6210 (0x0301) + +/** + * extract bits [@b0:@b1] (inclusive) from the value @x + * it should be @b0 <= @b1, or result is incorrect + */ +static inline u32 GET_BITS(u32 x, int b0, int b1) +{ + return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); +} + +#define WIL6210_MEM_SIZE (2*1024*1024UL) + +#define WIL6210_TX_QUEUES (4) + +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) + +struct wil6210_mbox_ring { + u32 base; + u16 unused; + u16 size; + u32 tail; + u32 head; +} __packed; + +struct wil6210_mbox_ring_desc { + u32 sync; + u32 addr; +} __packed; + +/* at HOST_OFF_WIL6210_MBOX_CTL */ +struct wil6210_mbox_ctl { + struct wil6210_mbox_ring tx; + struct wil6210_mbox_ring rx; +} __packed; + +struct wil6210_mbox_hdr { + u16 seq; + u16 ctx; + u16 type; + u8 flags; + u8 len; /* payload, bytes after this header */ +} __packed; + +/* max. value for wil6210_mbox_hdr.len */ +#define MAX_MBOXITEM_SIZE (120) + +struct wil6210_mbox_hdr_wmi { + u16 reserved0; + u16 id; + u16 info1; + u16 reserved1; +} __packed; + +struct pending_wmi_event { + struct list_head list; + struct { + struct wil6210_mbox_hdr mbox_hdr; + struct wil6210_mbox_hdr_wmi wmi; + u8 data[0]; + } __packed event; +}; + +union vring_desc; + +struct vring { + dma_addr_t pa; + union vring_desc *va; /* vring_desc[size] */ + u16 size; /* number of vring_desc elements */ + u32 swtail; + u32 swhead; + u32 hwtail; /* write here to inform hw */ + void **ctx; /* void *ctx[size] - software context */ +}; + +enum { /* for wil6210_priv.status */ + wil_status_fwready = 0, + wil_status_fwconnected, + wil_status_dontscan, + wil_status_irqen, /* FIXME: interrupts enabled - for debug */ +}; + +struct pci_dev; + +struct wil6210_stats { + u16 last_mcs_rx; + u16 bf_mcs; /* last BF, used for Tx */ +}; + +struct wil6210_priv { + struct pci_dev *pdev; + int n_msi; + struct wireless_dev *wdev; + void __iomem *csr; + unsigned long status; + /* profile */ + u32 monitor_flags; + struct ieee80211_channel *monitor_chan; + /* cached ISR registers */ + u32 isr_rx; + u32 isr_tx; + u32 isr_misc; + /* mailbox related */ + struct mutex wmi_mutex; + struct wil6210_mbox_ctl mbox_ctl; + struct completion wmi_ready; + u16 wmi_seq; + u16 reply_id; /**< wait for this WMI event */ + void *reply_buf; + u8 reply_size; + struct workqueue_struct *wmi_wq; /* for deferred calls */ + struct work_struct wmi_event_worker; + struct workqueue_struct *wmi_wq_conn; /* for connect worker */ + struct work_struct wmi_connect_worker; + struct timer_list connect_timer; + int pending_connect_cid; + struct list_head pending_wmi_ev; + spinlock_t wmi_ev_lock; + + /* DMA related */ + struct vring vring_rx; + struct vring vring_tx[WIL6210_MAX_TX_RINGS]; + u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; + /* scan */ + struct cfg80211_scan_request *scan_request; + + struct mutex mutex; + /* statistics */ + struct wil6210_stats stats; + /* debugfs */ + struct dentry *debug; + struct debugfs_blob_wrapper fw_code_blob; + struct debugfs_blob_wrapper fw_data_blob; + struct debugfs_blob_wrapper fw_peri_blob; + struct debugfs_blob_wrapper uc_code_blob; + struct debugfs_blob_wrapper uc_data_blob; + struct debugfs_blob_wrapper rgf_blob; +}; + +#define wil_to_wiphy(i) (i->wdev->wiphy) +#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) +#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) +#define wil_to_wdev(i) (i->wdev) +#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) +#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) +#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) + +#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) +#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) + +void memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count); +void memcpy_toio_32(volatile void __iomem *dst, const void *src, size_t count); + +void *wil_if_alloc(struct device *dev, void __iomem *csr); +void wil_if_free(struct wil6210_priv *wil); +int wil_if_add(struct wil6210_priv *wil); +void wil_if_remove(struct wil6210_priv *wil); +int wil_priv_init(struct wil6210_priv *wil); +void wil_priv_deinit(struct wil6210_priv *wil); +int wil_reset(struct wil6210_priv *wil); +void wil_link_on(struct wil6210_priv *wil); +void wil_link_off(struct wil6210_priv *wil); +int wil_up(struct wil6210_priv *wil); +int wil_down(struct wil6210_priv *wil); + +void __iomem *wmi_buffer(struct wil6210_priv *wil, u32 ptr); +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); +int wmi_read_hdr(struct wil6210_priv *wil, u32 ptr, + struct wil6210_mbox_hdr *hdr); +int wmi_send_cmd(struct wil6210_priv *wil, void *buf); +void wmi_recv_cmd(struct wil6210_priv *wil); +int wmi_call(struct wil6210_priv *wil, void *buf, + u16 reply_id, void *reply, u8 reply_size, int to_msec); +void wmi_connect_worker(struct work_struct *work); +void wmi_event_worker(struct work_struct *work); +void wmi_event_flush(struct wil6210_priv *wil); + +int wil6210_init_irq(struct wil6210_priv *wil, int irq); +void wil6210_fini_irq(struct wil6210_priv *wil, int irq); +void wil6210_disable_irq(struct wil6210_priv *wil); +void wil6210_enable_irq(struct wil6210_priv *wil); + +int wil6210_debugfs_init(struct wil6210_priv *wil); +void wil6210_debugfs_remove(struct wil6210_priv *wil); + +int wil6210_set_mac_address(struct wil6210_priv *wil, void *addr); +int wil6210_set_bcon(struct wil6210_priv *wil, int bi, u16 wmi_nettype); +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); + +int rx_init(struct wil6210_priv *wil); +void rx_fini(struct wil6210_priv *wil); + +/* TX API */ +int vring_init_tx(struct wil6210_priv *wil, int id, + int size, int cid, int tid); +void vring_fini_tx(struct wil6210_priv *wil, int id); + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); +void tx_complete(struct wil6210_priv *wil, int ringid); + +/* RX API */ +void rx_handle(struct wil6210_priv *wil); + +int iftype_nl2wmi(enum nl80211_iftype type); + +#endif /* __WIL6210_H__ */ --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210_rgf.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WIL6210_RGF_H +#define WIL6210_RGF_H + +/** + * Hardware registers map + * + */ + +/** + * Mapping + * RGF File | Host addr | FW addr + * | | + * user_rgf | 0x000000 | 0x880000 + * dma_rgf | 0x001000 | 0x881000 + * pcie_rgf | 0x002000 | 0x882000 + * | | + */ + +/* Where various structures placed in host address space */ +#define WIL6210_FW_HOST_OFF (0x880000UL) + +#define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF) + +/** + * Interrupt control registers block + * + * each interrupt controlled by the same bit in all registers + */ +struct RGF_ICR { + u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ + u32 ICR; /* Cause, W1C/COR depending on ICC */ + u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ + u32 ICS; /* Cause Set, WO */ + u32 IMV; /* Mask, RW+S/C */ + u32 IMS; /* Mask Set, write 1 to set */ + u32 IMC; /* Mask Clear, write 1 to clear */ +} __packed; + +/* registers - FW addresses */ +#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) +#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ + #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) +#define RGF_USER_MAC_CPU_0 (0x8801fc) +#define RGF_USER_USER_CPU_0 (0x8801e0) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) + +#define RGF_DMA_PSEUDO_CAUSE (0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) + #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) + #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) + #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) + +#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ + #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) + #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */ +#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */ + #define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0) +#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ + #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) + #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) + +/* popular locations */ +#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) +#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ + offsetof(struct RGF_ICR, ICS)) +#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 + +/* ISR register bits */ +#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 +#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 + +#endif /* WIL6210_RGF_H */ --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -0,0 +1,710 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "wil6210.h" +#include "wil6210_rgf.h" +#include "wmi.h" + +/** + * WMI event receiving - theory of operations + * + * When firmware about to report WMI event, it fills memory area + * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for + * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. + * + * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the + * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up + * and handles events within the @wmi_event_worker. Every event get detached + * from list, processed and deleted. + * + * Purpose for this mechanism is to release IRQ thread; otherwise, + * if WMI event handling involves another WMI command flow, this 2-nd flow + * won't be completed because of blocked IRQ thread. + */ + +/** + * Addressing - theory of operations + * + * There are several buses present on the WIL6210 card. + * Same memory areas are visible at different address on + * the different busses. There are 3 main bus masters: + * - MAC CPU (ucode) + * - User CPU (firmware) + * - AHB (host) + * + * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing + * AHB addresses starting from 0x880000 + * + * Internally, firmware uses addresses that allows faster access but + * are invisible from the host. To read from these addresses, alternative + * AHB address must be used. + * + * Memory mapping + * Linker address PCI/Host address + * 0x880000 .. 0xa80000 2Mb BAR0 + * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM + * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH + */ + +/** + * @fw_mapping provides memory remapping table + */ +static const struct { + u32 from; /* linker address - from, inclusive */ + u32 to; /* linker address - to, exclusive */ + u32 host; /* PCI/Host address */ +} fw_mapping[] = { + {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ + {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ + {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ + {0x880000, 0x88a000, 0x880000}, /* various RGF */ + {0x8c0000, 0x92a000, 0x8c0000}, /* trivial mapping for upper area */ + /* + * 920000..928000 ucode code RAM + * 928000..92a000 ucode data RAM + */ +}; + +/** + * return AHB address for given firmware/ucode internal (linker) address + * @x - internal address + * If address have no valid AHB mapping, return 0 + */ +static u32 wmi_addr_remap(u32 x) +{ + int i; + for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { + if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) + return x + fw_mapping[i].host - fw_mapping[i].from; + } + return 0; +} + +/** + * Check address validity for WMI buffer; remap if needed + * @ptr - internal (linker) fw/ucode address + * + * Valid buffer should be DWORD aligned + * + * return address for accessing buffer from the host; + * if buffer is not valid, return NULL. + */ +void __iomem *wmi_buffer(struct wil6210_priv *wil, u32 ptr) +{ + u32 off; + if (ptr % 4) + return NULL; + ptr = wmi_addr_remap(ptr); + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + return wil->csr + off; +} + +/** + * Check address validity + */ +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) +{ + u32 off; + if (ptr % 4) + return NULL; + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + return wil->csr + off; +} + +int wmi_read_hdr(struct wil6210_priv *wil, u32 ptr, + struct wil6210_mbox_hdr *hdr) +{ + void __iomem *src = wmi_buffer(wil, ptr); + if (!src) + return -EINVAL; + memcpy_fromio_32(hdr, src, sizeof(*hdr)); + return 0; +} + +static int __wmi_send_cmd(struct wil6210_priv *wil, void *buf) +{ + struct wil6210_mbox_hdr *hdr = buf; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; + struct wil6210_mbox_ring_desc d_head; + u32 next_head; + void __iomem *dst; + void __iomem *head = wmi_addr(wil, r->head); + int retry; + wil_info(wil, "%s()\n", __func__); + might_sleep(); + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + return -EAGAIN; + } + if (!head) { + wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); + return -EINVAL; + } + /* read Tx head */ + memcpy_fromio_32(&d_head, head + , sizeof(struct wil6210_mbox_ring_desc)); + if (d_head.sync != 0) { + wil_err(wil, "WMI head busy\n"); + return -EBUSY; + } + /* next head */ + next_head = r->base + ((r->head - r->base + + sizeof(struct wil6210_mbox_ring_desc)) % r->size); + wil_info(wil, "Head 0x%08x -> 0x%08x\n" + , r->head, next_head); + /* wait till FW finish with previous command */ + for (retry = 5; retry > 0; retry--) { + r->tail = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.tail)); + if (next_head != r->tail) + break; + msleep(20); + } + if (next_head == r->tail) { + wil_err(wil, "WMI ring full\n"); + return -EBUSY; + } + dst = wmi_buffer(wil, d_head.addr); + if (!dst) { + wil_err(wil, "invalid WMI buffer: 0x%08x\n", d_head.addr); + return -EINVAL; + } + hdr->seq = ++wil->wmi_seq; + /* set command */ + if ((hdr->type == 0) && /* TODO: define */ + (hdr->len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); + wil_info(wil, "WMI command 0x%04x\n", wmi->id); + } + print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf + , sizeof(*hdr) + hdr->len, true); + memcpy_toio_32(dst, buf, sizeof(*hdr) + hdr->len); + /* mark entry as full */ + iowrite32(1, wil->csr + HOSTADDR(r->head) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* advance next ptr */ + iowrite32(r->head = next_head, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.head)); + + /* interrupt to FW */ + iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); + + return 0; +} + +int wmi_send_cmd(struct wil6210_priv *wil, void *buf) +{ + int rc; + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send_cmd(wil, buf); + mutex_unlock(&wil->wmi_mutex); + return rc; +} + +/*=== Event handlers ===*/ +static void wmi_evt_ready(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct WMI_READY_EVENT *evt = d; + wil_info(wil, "FW ver. %d; MAC %pM\n", + evt->sw_version, evt->macaddr); + if (!is_valid_ether_addr(ndev->dev_addr)) { + memcpy(ndev->dev_addr, evt->macaddr, ETH_ALEN); + memcpy(ndev->perm_addr, evt->macaddr, ETH_ALEN); + } + snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), + "%d", evt->sw_version); +} + +static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, + void *d, int len) +{ + wil_info(wil, "WMI: FW ready\n"); + set_bit(wil_status_fwready, &wil->status); + /* reuse wmi_ready for the firmware ready indication */ + complete(&wil->wmi_ready); +} + +static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct WMI_RX_MGMT_PACKET_EVENT *data = d; + struct wiphy *wiphy = wil_to_wiphy(wil); + struct ieee80211_mgmt *rx_mgmt_frame = + (struct ieee80211_mgmt *)data->payload; + int ch_no = data->channel+1; + u32 freq = ieee80211_channel_to_frequency(ch_no, + IEEE80211_BAND_60GHZ); + struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); + /* TODO convert LE to CPU */ + s32 signal = data->rssi; /* TODO */ + __le16 fc = rx_mgmt_frame->frame_control; + wil_info(wil, "MGMT: channel %d MCS %d RSSI %d\n", + data->channel, data->mcs, data->rssi); + wil_info(wil, "status 0x%08x len %d\n", + data->status, data->length); + wil_info(wil, "qid %d mid %d cid %d\n", + data->qid, data->mid, data->cid); + if (!channel) { + wil_err(wil, "Frame on unsupported channel\n"); + return; + } + if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { + struct cfg80211_bss *bss; + u64 tsf = rx_mgmt_frame->u.beacon.timestamp; + u16 cap = rx_mgmt_frame->u.beacon.capab_info; + u16 bi = rx_mgmt_frame->u.beacon.beacon_int; + const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; + size_t ie_len = data->length - + offsetof(struct ieee80211_mgmt, + u.beacon.variable); + /* Hack to work around wrong cap. info TODO: remove when fixed*/ + if (!(cap & WLAN_CAPABILITY_ESS)) { + wil_info(wil, "No ESS bit in capabitity. Set it.\n"); + cap |= WLAN_CAPABILITY_ESS; + } + if (cap & WLAN_CAPABILITY_IBSS) { + wil_info(wil, "IBSS bit in capabitity. Clear it.\n"); + cap &= ~WLAN_CAPABILITY_IBSS; + } + bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, + tsf, cap, bi, ie_buf, ie_len, + signal, GFP_KERNEL); + if (bss) { + wil_info(wil, "Added BSS %pM\n", + rx_mgmt_frame->bssid); + cfg80211_put_bss(bss); + } else { + wil_err(wil, "cfg80211_inform_bss() failed\n"); + } + } +} + +static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, + void *d, int len) +{ + if (wil->scan_request) { + struct WMI_SCAN_COMPLETE_EVENT *data = d; + bool aborted = (data->status != 0); + wil_info(wil, "SCAN_COMPLETE(0x%08x)\n", + data->status); + cfg80211_scan_done(wil->scan_request, aborted); + wil->scan_request = NULL; + } else { + wil_err(wil, "SCAN_COMPLETE while not scanning\n"); + } +} + +static void wmi_evt_connect(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct WMI_CONNECT_EVENT *evt = d; + + if (len < sizeof(*evt)) { + wil_err(wil, "Connect event too short : %d bytes\n", len); + return; + } + if (len != sizeof(*evt) + evt->beaconIeLen + evt->assocReqLen + + evt->assocRespLen) { + wil_err(wil, + "Connect event corrupted : %d != %d + %d + %d + %d\n", + len, (int)sizeof(*evt), evt->beaconIeLen, + evt->assocReqLen, evt->assocRespLen); + return; + } + wil_info(wil, "Connect %pM channel [%d] cid %d\n", + evt->bssid, evt->channel, evt->cid); + print_hex_dump(KERN_INFO, "connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, + evt->assocInfo, len - sizeof(*evt), true); + if ((wdev->iftype == NL80211_IFTYPE_STATION) || + (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + /* capinfo + listen interval */ + u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); + /* capinfo + status code + associd */ + u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + + sizeof(u16); + u8 *assoc_req_ie = &evt->assocInfo[evt->beaconIeLen + + assoc_req_ie_offset]; + u8 *assoc_resp_ie = &evt->assocInfo[evt->beaconIeLen + + evt->assocReqLen + assoc_resp_ie_offset]; + + u8 assoc_req_ielen = evt->assocReqLen - assoc_req_ie_offset; + u8 assoc_resp_ielen = evt->assocRespLen - assoc_resp_ie_offset; + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wil_err(wil, "Not in connecting state\n"); + return; + } + if (evt->assocReqLen < assoc_req_ie_offset) { + wil_info(wil, "No Assoc. Req. info\n"); + assoc_req_ie = NULL; + assoc_req_ielen = 0; + } + if (evt->assocRespLen < assoc_resp_ie_offset) { + wil_info(wil, "No Assoc. Resp. info\n"); + assoc_resp_ie = NULL; + assoc_resp_ielen = 0; + } + del_timer_sync(&wil->connect_timer); + cfg80211_connect_result(ndev, evt->bssid, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + + } + set_bit(wil_status_fwconnected, &wil->status); + + /* FIXME FW can transmit only ucast frames to peer */ + /* FIXME real ring_id instead of hard coded 0 */ + memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); + + wil->pending_connect_cid = evt->cid; + queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); +} + +static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct WMI_DISCONNECT_EVENT *evt = d; + wil_info(wil, "Disconnect %pM reason %d proto %d wmi\n", + evt->bssid, + evt->protocolReasonStatus, evt->disconnectReason); + wil6210_disconnect(wil, evt->bssid); + clear_bit(wil_status_dontscan, &wil->status); +} + +static void wmi_evt_notify(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct WMI_NOTIFY_REQ_DONE_EVENT *evt = d; + union { + u64 tsf; + u32 tsf_part[2]; + } tsf; + if (len < sizeof(*evt)) { + wil_err(wil, "Short NOTIFY event\n"); + return; + } + tsf.tsf_part[0] = evt->tsf_lo; + tsf.tsf_part[1] = evt->tsf_hi; + + wil_info(wil, "Link status, MCS %d TSF 0x%016llx\n" + "BF status 0x%08x metric 0x%08x\n" + "Tx Tpt %d goodput %d Rx goodput %d\n" + "Sectors(rx:tx) my %d:%d peer %d:%d\n", + evt->bf_mcs, tsf.tsf, + evt->bf_status, evt->bf_metric, + evt->tx_tpt, evt->tx_gput, evt->rx_gput, + evt->my_rx_sector, evt->my_tx_sector, + evt->peer_rx_sector, evt->peer_tx_sector); + wil->stats.bf_mcs = evt->bf_mcs; +} + +static const struct { + int eventid; + void (*handler)(struct wil6210_priv *wil, int eventid, + void *data, int data_len); +} wmi_evt_handlers[] = { + {WMI_READY_EVENTID, wmi_evt_ready}, + {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, + {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, + {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, + {WMI_CONNECT_EVENTID, wmi_evt_connect}, + {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, + {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, +}; + +void wmi_recv_cmd(struct wil6210_priv *wil) +{ + struct wil6210_mbox_ring_desc d_tail; + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; + struct pending_wmi_event *evt; + u8 *cmd; + void __iomem *src; + unsigned long flags; + wil_info(wil, "%s()\n", __func__); + for (;;) { + r->head = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.head)); + if (r->tail == r->head) + return; + /* read cmd from tail */ + memcpy_fromio_32(&d_tail, + wil->csr + HOSTADDR(r->tail), + sizeof(struct wil6210_mbox_ring_desc)); + if (d_tail.sync == 0) { + wil_err(wil, "Mbox evt not owned by FW?\n"); + return; + } + if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { + wil_err(wil, "Mbox evt at 0x%08x?\n", + d_tail.addr); + return; + } + src = wmi_buffer(wil, d_tail.addr) + + sizeof(struct wil6210_mbox_hdr); + evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, + event.wmi) + hdr.len, 4), GFP_KERNEL); + if (!evt) { + wil_err(wil, "kmalloc for WMI event (%d) failed\n", + hdr.len); + return; + } + evt->event.mbox_hdr = hdr; + cmd = (void *)&evt->event.wmi; + memcpy_fromio_32(cmd, src, hdr.len); + /* mark entry as empty */ + iowrite32(0, wil->csr + HOSTADDR(r->tail) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* indicate */ + wil_info(wil, "Mbox evt %04x %04x %04x %02x %02x\n", + hdr.seq, hdr.ctx, hdr.type, hdr.flags, hdr.len); + if ((hdr.type == 0) && /* TODO: define */ + (hdr.len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + wil_info(wil, "WMI event 0x%04x\n", + evt->event.wmi.id); + } + print_hex_dump(KERN_INFO, "evt ", DUMP_PREFIX_OFFSET, 16, 1, + &evt->event.mbox_hdr, sizeof(hdr) + hdr.len, + true); + /* advance tail */ + r->tail = r->base + ((r->tail - r->base + + sizeof(struct wil6210_mbox_ring_desc)) % r->size); + iowrite32(r->tail, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.tail)); + /* add to the pending list */ + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_add_tail(&evt->list, &wil->pending_wmi_ev); + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + { + int q = queue_work(wil->wmi_wq, + &wil->wmi_event_worker); + wil_info(wil, "queue_work -> %d\n", q); + } + } +} + +int wmi_call(struct wil6210_priv *wil, void *buf, + u16 reply_id, void *reply, u8 reply_size, int to_msec) +{ + int rc; + int remain; + struct wil6210_mbox_wmi { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + } __packed * wmi = buf; + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send_cmd(wil, buf); + if (rc) + goto out; + wil->reply_id = reply_id; + wil->reply_buf = reply; + wil->reply_size = reply_size; + remain = wait_for_completion_timeout(&wil->wmi_ready, + msecs_to_jiffies(to_msec)); + if (0 == remain) { + wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", + wmi->wmi.id, reply_id, to_msec); + rc = -ETIME; + } else { + wil_info(wil, + "wmi_call(0x%04x->0x%04x) completed in %d msec\n", + wmi->wmi.id, reply_id, + to_msec - jiffies_to_msecs(remain)); + } + wil->reply_id = 0; + wil->reply_buf = NULL; + wil->reply_size = 0; + out: + mutex_unlock(&wil->wmi_mutex); + return rc; +} + +int wil6210_set_mac_address(struct wil6210_priv *wil, void *addr) +{ + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_SET_MAC_ADDRESS_CMD mac; + } __packed cmd = { + .hdr = { + .seq = 1, + .ctx = 0, + .type = 0, + .flags = 0, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_SET_MAC_ADDRESS_CMD), + }, + .wmi = { + .id = WMI_SET_MAC_ADDRESS_CMDID, + .info1 = 0, + }, + .mac = { + }, + }; + memcpy(cmd.mac.macaddr, addr, ETH_ALEN); + wil_info(wil, "Set MAC %pM\n", addr); + return wmi_send_cmd(wil, &cmd); +} + +int wil6210_set_bcon(struct wil6210_priv *wil, int bi, u16 wmi_nettype) +{ + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + struct WMI_SET_BEACON_INT_CMD bcon; + } __packed cmd = { + .hdr = { + .seq = 1, + .ctx = 0, + .type = 0, + .flags = 0, + .len = sizeof(struct wil6210_mbox_hdr_wmi) + + sizeof(struct WMI_SET_BEACON_INT_CMD), + }, + .wmi = { + .id = WMI_SET_BEACON_INT_CMDID, + .info1 = 0, + }, + .bcon = { + .bcon_interval = bi, + .network_type = wmi_nettype, + }, + }; + return wmi_send_cmd(wil, &cmd); +} + +void wmi_event_flush(struct wil6210_priv *wil) +{ + struct pending_wmi_event *evt, *t; + wil_info(wil, "%s()\n", __func__); + list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { + list_del(&evt->list); + kfree(evt); + } +} + +static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, + void *d, int len) +{ + int i; + for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { + if (wmi_evt_handlers[i].eventid == id) { + wmi_evt_handlers[i].handler(wil, id, d, len); + return true; + } + } + return false; +} + +static void wmi_event_handle(struct wil6210_priv *wil, + struct wil6210_mbox_hdr *hdr) +{ + wil_info(wil, "%s()\n", __func__); + if ((hdr->type == 0) && /* TODO: define */ + (hdr->len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); + void *evt_data = (void *)(&wmi[1]); + /* check if someone waits for this event */ + if (wil->reply_id && wil->reply_id == wmi->id) { + if (wil->reply_buf) { + memcpy(wil->reply_buf, wmi, + min(hdr->len, wil->reply_size)); + } else { + wmi_evt_call_handler(wil, wmi->id, evt_data, + hdr->len - sizeof(*wmi)); + } + wil_info(wil, "Complete WMI 0x%04x\n", + wmi->id); + complete(&wil->wmi_ready); + return; + } + /* unsolicited event */ + /* search for handler */ + if (!wmi_evt_call_handler(wil, wmi->id, evt_data, + hdr->len - sizeof(*wmi))) { + wil_err(wil, "Unhandled event 0x%04x\n", + wmi->id); + } + } else { + wil_err(wil, "Unknown event type\n"); + print_hex_dump(KERN_INFO, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, + hdr, sizeof(*hdr) + hdr->len, true); + } +} + +static struct list_head *next_wmi_ev(struct wil6210_priv *wil) +{ + unsigned long flags; + struct list_head *ret = NULL; + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + if (!list_empty(&wil->pending_wmi_ev)) { + ret = wil->pending_wmi_ev.next; + list_del(ret); + } + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + return ret; +} + +void wmi_event_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, + struct wil6210_priv, wmi_event_worker); + struct pending_wmi_event *evt; + struct list_head *lh; + wil_info(wil, "%s()\n", __func__); + while ((lh = next_wmi_ev(wil)) != NULL) { + evt = list_entry(lh, struct pending_wmi_event, list); + wmi_event_handle(wil, &evt->event.mbox_hdr); + kfree(evt); + } + wil_info(wil, "%s() done\n", __func__); +} + +void wmi_connect_worker(struct work_struct *work) +{ + int rc; + struct wil6210_priv *wil = container_of(work, + struct wil6210_priv, wmi_connect_worker); + if (wil->pending_connect_cid < 0) { + wil_err(wil, "No connection pending\n"); + return; + } + wil_info(wil, "Configure for connection CID %d\n", + wil->pending_connect_cid); + rc = vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, + wil->pending_connect_cid, 0); + wil->pending_connect_cid = -1; + if (rc == 0) + wil_link_on(wil); +} --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -0,0 +1,837 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WMI_H__ +#define __WMI_H__ +/* WMI API */ + +/* + * List of Commnands + */ +enum WMI_COMMAND_ID { + WMI_CONNECT_CMDID = 0x0001, + WMI_RECONNECT_CMDID, + WMI_DISCONNECT_CMDID, + WMI_SYNCHRONIZE_CMDID, + WMI_CREATE_PSTREAM_CMDID, + WMI_DELETE_PSTREAM_CMDID, + WMI_START_SCAN_CMDID, + WMI_SET_SCAN_PARAMS_CMDID, + WMI_SET_BSS_FILTER_CMDID, + WMI_SET_PROBED_SSID_CMDID, /* 10 */ + WMI_SET_LISTEN_INT_CMDID, + WMI_SET_BMISS_TIME_CMDID, + WMI_SET_DISC_TIMEOUT_CMDID, + WMI_GET_CHANNEL_LIST_CMDID, + WMI_SET_BEACON_INT_CMDID, + WMI_GET_STATISTICS_CMDID, + WMI_SET_CHANNEL_PARAMS_CMDID, + WMI_SET_POWER_MODE_CMDID, + WMI_SET_IBSS_PM_CAPS_CMDID, + WMI_SET_POWER_PARAMS_CMDID, /* 20 */ + WMI_SET_POWERSAVE_TIMERS_POLICY_CMDID, + WMI_ADD_CIPHER_KEY_CMDID, + WMI_DELETE_CIPHER_KEY_CMDID, + WMI_ADD_KRK_CMDID, + WMI_DELETE_KRK_CMDID, + WMI_SET_PMKID_CMDID, + WMI_SET_TX_PWR_CMDID, + WMI_GET_TX_PWR_CMDID, + WMI_SET_ASSOC_INFO_CMDID, + WMI_ADD_BAD_AP_CMDID, /* 30 */ + WMI_DELETE_BAD_AP_CMDID, + WMI_SET_TKIP_COUNTERMEASURES_CMDID, + WMI_RSSI_THRESHOLD_PARAMS_CMDID, + WMI_TARGET_ERROR_REPORT_BITMASK_CMDID, + WMI_SET_ACCESS_PARAMS_CMDID, + WMI_SET_RETRY_LIMITS_CMDID, + WMI_SET_OPT_MODE_CMDID, + WMI_OPT_TX_FRAME_CMDID, + WMI_SET_VOICE_PKT_SIZE_CMDID, + WMI_SET_MAX_SP_LEN_CMDID, /* 40 */ + WMI_SET_ROAM_CTRL_CMDID, + WMI_GET_ROAM_TBL_CMDID, + WMI_GET_ROAM_DATA_CMDID, + WMI_ENABLE_RM_CMDID, + WMI_SET_MAX_OFFHOME_DURATION_CMDID, + WMI_EXTENSION_CMDID, /* Non-wireless extensions */ + WMI_SNR_THRESHOLD_PARAMS_CMDID, + WMI_LQ_THRESHOLD_PARAMS_CMDID, + WMI_SET_LPREAMBLE_CMDID, + WMI_SET_RTS_CMDID, /* 50 */ + WMI_CLR_RSSI_SNR_CMDID, + WMI_SET_FIXRATES_CMDID, + WMI_GET_FIXRATES_CMDID, + WMI_SET_AUTH_MODE_CMDID, + WMI_SET_REASSOC_MODE_CMDID, + WMI_SET_WMM_CMDID, + WMI_SET_WMM_TXOP_CMDID, + WMI_TEST_CMDID, + /* COEX AR6002 only*/ + WMI_SET_BT_STATUS_CMDID, + WMI_SET_BT_PARAMS_CMDID, /* 60 */ + + WMI_SET_KEEPALIVE_CMDID, + WMI_GET_KEEPALIVE_CMDID, + WMI_SET_APPIE_CMDID, + WMI_GET_APPIE_CMDID, + WMI_SET_WSC_STATUS_CMDID, + + /* Wake on Wireless */ + WMI_SET_HOST_SLEEP_MODE_CMDID, + WMI_SET_WOW_MODE_CMDID, + WMI_GET_WOW_LIST_CMDID, + WMI_ADD_WOW_PATTERN_CMDID, + WMI_DEL_WOW_PATTERN_CMDID, /* 70 */ + + WMI_SET_FRAMERATES_CMDID, + WMI_SET_AP_PS_CMDID, + WMI_SET_QOS_SUPP_CMDID, + + /* WILOCITY types */ + WMI_MEM_READ_CMDID = 0x0800, /* WILOCITY types */ + WMI_MEM_WR_CMDID = 0x0801, + WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, + WMI_ECHO_CMDID = 0x0803, + WMI_DEEP_ECHO_CMDID = 0x0804, + WMI_CONFIG_MAC_CMDID = 0x0805, + WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806, + WMI_ADD_STATION_CMDID = 0x0807, + WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808, + WMI_PHY_GET_STATISTICS_CMDID = 0x0809, + WMI_FS_TUNE_CMDID = 0x080A, + WMI_CORR_MEASURE_CMDID = 0x080B, + WMI_TEMP_SENSE_CMDID = 0x080E, + WMI_DC_CALIB_CMDID = 0x080F, + WMI_SEND_TONE_CMDID = 0x0810, + WMI_IQ_TX_CALIB_CMDID = 0x0811, + WMI_IQ_RX_CALIB_CMDID = 0x0812, + WMI_SET_UCODE_IDLE_CMDID = 0x0813, + WMI_SET_CHANNEL_IND_CMDID = 0x0814, + WMI_SET_WORK_MODE_CMDID = 0x0815, + WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, + WMI_WIL6210_R_ACTIVATE_CMDID = 0x0817, + WMI_WIL6210_R_READ_CMDID = 0x0818, + WMI_WIL6210_R_WRITE_CMDID = 0x0819, + WMI_WIL6210_R_TXRX_SEL_CMDID = 0x081A, + MAC_IO_STATIC_PARAMS_CMD = 0x081B, + MAC_IO_DYNAMIC_PARAMS_CMD = 0x081C, + WMI_SILENT_RSSI_CALIB_CMDID = 0x081D, + + WMI_CFG_RX_CHAIN_CMDID = 0x0820, + WMI_VRING_CFG_CMDID = 0x0821, + WMI_RN_ON_CMDID = 0x0822, + WMI_VRING_BA_EN_CMDID = 0x0823, + WMI_VRING_BA_DIS_CMDID = 0x0824, + WMI_RCP_ADDBA_RESP_CMDID = 0x0825, + WMI_RCP_DELBA_CMDID = 0x0826, + + WMI_READ_MAC_RXQ_CMDID = 0x0830, + WMI_READ_MAC_TXQ_CMDID = 0x0831, + WMI_WRITE_MAC_RXQ_CMDID = 0x0832, + WMI_WRITE_MAC_TXQ_CMDID = 0x0833, + WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834, + + WMI_MLME_PUSH_CMDID = 0x0835, + WMI_BF_START_TXSS_CMDID = 0x0836, + WMI_BF_TXSS_MGMT_CMDID = 0x0837, + WMI_BF_SM_MGMT_CMDID = 0x0838, + WMI_BF_RXSS_MGMT_CMDID = 0x0839, + WMI_RS_MGMT_CMDID = 0x0852, + + WMI_LINK_START_CMDID = 0x0850, + WMI_LINK_STOP_CMDID = 0x0851, + + /* Performance monitoring commands */ + WMI_BF_CTRL_CMDID = 0x0862, + WMI_NOTIFY_REQ_CMDID = 0x0863, + + WMI_UNIT_TEST_CMD_ID = 0x0900, + WMI_HICCUP_CMD_ID = 0x0901, + +#if 0 + WMI_TXRX_SEND_CMDID = 0x0810, +#endif + /* WMI_THIN_RESERVED_... mark the start and end + * values for WMI_THIN_RESERVED command IDs. These + * command IDs can be found in wmi_thin.h */ + WMI_THIN_RESERVED_START = 0x8000, + WMI_THIN_RESERVED_END = 0x8fff, + /* + * Developer commands starts at 0xF000 + */ + WMI_SET_BITRATE_CMDID = 0xF000, + WMI_GET_BITRATE_CMDID, + WMI_SET_WHALPARAM_CMDID, + + /*Should add the new command to the tail for compatible with + * etna. + */ + WMI_SET_MAC_ADDRESS_CMDID, + WMI_SET_AKMP_PARAMS_CMDID, + WMI_SET_PMKID_LIST_CMDID, + WMI_GET_PMKID_LIST_CMDID, + WMI_ABORT_SCAN_CMDID, + WMI_SET_TARGET_EVENT_REPORT_CMDID, + + /* Unused */ + WMI_UNUSED1, + WMI_UNUSED2, + + /* + * AP mode commands + */ + WMI_AP_HIDDEN_SSID_CMDID, /* F00B */ + WMI_AP_SET_NUM_STA_CMDID, + WMI_AP_ACL_POLICY_CMDID, + WMI_AP_ACL_MAC_LIST_CMDID, + WMI_AP_CONFIG_COMMIT_CMDID, + WMI_AP_SET_MLME_CMDID, /* F010 */ + WMI_AP_SET_PVB_CMDID, + WMI_AP_CONN_INACT_CMDID, + WMI_AP_PROT_SCAN_TIME_CMDID, + WMI_AP_SET_COUNTRY_CMDID, + WMI_AP_SET_DTIM_CMDID, + WMI_AP_MODE_STAT_CMDID, + + WMI_SET_IP_CMDID, /* F017 */ + WMI_SET_PARAMS_CMDID, + WMI_SET_MCAST_FILTER_CMDID, + WMI_DEL_MCAST_FILTER_CMDID, + + WMI_ALLOW_AGGR_CMDID, /* F01B */ + WMI_ADDBA_REQ_CMDID, + WMI_DELBA_REQ_CMDID, + WMI_SET_HT_CAP_CMDID, + WMI_SET_HT_OP_CMDID, + WMI_SET_TX_SELECT_RATES_CMDID, + WMI_SET_TX_SGI_PARAM_CMDID, + WMI_SET_RATE_POLICY_CMDID, + + WMI_HCI_CMD_CMDID, /* F023 */ + WMI_RX_FRAME_FORMAT_CMDID, + WMI_SET_THIN_MODE_CMDID, + WMI_SET_BT_WLAN_CONN_PRECEDENCE_CMDID, + + WMI_AP_SET_11BG_RATESET_CMDID, /* F027 */ + WMI_SET_PMK_CMDID, + WMI_MCAST_FILTER_CMDID, + /* COEX CMDID AR6003*/ + WMI_SET_BTCOEX_FE_ANT_CMDID, /* F02A */ + WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID, + WMI_SET_BTCOEX_SCO_CONFIG_CMDID, + WMI_SET_BTCOEX_A2DP_CONFIG_CMDID, + WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID, + WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID, + WMI_SET_BTCOEX_DEBUG_CMDID, + WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID, + WMI_GET_BTCOEX_STATS_CMDID, + WMI_GET_BTCOEX_CONFIG_CMDID, + + WMI_SET_DFS_ENABLE_CMDID, /* F034 */ + WMI_SET_DFS_MINRSSITHRESH_CMDID, + WMI_SET_DFS_MAXPULSEDUR_CMDID, + WMI_DFS_RADAR_DETECTED_CMDID, + + /* P2P CMDS */ + WMI_P2P_SET_CONFIG_CMDID, /* F038 */ + WMI_WPS_SET_CONFIG_CMDID, + WMI_SET_REQ_DEV_ATTR_CMDID, + WMI_P2P_FIND_CMDID, + WMI_P2P_STOP_FIND_CMDID, + WMI_P2P_GO_NEG_START_CMDID, + WMI_P2P_LISTEN_CMDID, + + WMI_CONFIG_TX_MAC_RULES_CMDID, /* F040 */ + WMI_SET_PROMISCUOUS_MODE_CMDID, + WMI_RX_FRAME_FILTER_CMDID, + WMI_SET_CHANNEL_CMDID, + + /* WAC commands */ + WMI_ENABLE_WAC_CMDID, + WMI_WAC_SCAN_REPLY_CMDID, + WMI_WAC_CTRL_REQ_CMDID, + WMI_SET_DIV_PARAMS_CMDID, + + WMI_GET_PMK_CMDID, + WMI_SET_PASSPHRASE_CMDID, + WMI_SEND_ASSOC_RES_CMDID, + WMI_SET_ASSOC_REQ_RELAY_CMDID, +}; + +/* + * List of Events (target to host) + */ +enum WMI_EVENT_ID { + WMI_READY_EVENTID = 0x1001, + WMI_CONNECT_EVENTID, + WMI_DISCONNECT_EVENTID, + WMI_BSSINFO_EVENTID, + WMI_CMDERROR_EVENTID, + WMI_REGDOMAIN_EVENTID, + WMI_PSTREAM_TIMEOUT_EVENTID, + WMI_NEIGHBOR_REPORT_EVENTID, + WMI_TKIP_MICERR_EVENTID, + WMI_SCAN_COMPLETE_EVENTID, /* 0x100a */ + WMI_REPORT_STATISTICS_EVENTID, + WMI_RSSI_THRESHOLD_EVENTID, + WMI_ERROR_REPORT_EVENTID, + WMI_OPT_RX_FRAME_EVENTID, + WMI_REPORT_ROAM_TBL_EVENTID, + WMI_EXTENSION_EVENTID, + WMI_CAC_EVENTID, + WMI_SNR_THRESHOLD_EVENTID, + WMI_LQ_THRESHOLD_EVENTID, + WMI_TX_RETRY_ERR_EVENTID, /* 0x1014 */ + WMI_REPORT_ROAM_DATA_EVENTID, + WMI_TEST_EVENTID, + WMI_APLIST_EVENTID, + WMI_GET_WOW_LIST_EVENTID, + WMI_GET_PMKID_LIST_EVENTID, + WMI_CHANNEL_CHANGE_EVENTID, + WMI_PEER_NODE_EVENTID, + WMI_PSPOLL_EVENTID, + WMI_DTIMEXPIRY_EVENTID, + WMI_WLAN_VERSION_EVENTID, + WMI_SET_PARAMS_REPLY_EVENTID, + WMI_ADDBA_REQ_EVENTID, /*0x1020 */ + WMI_ADDBA_RESP_EVENTID, + WMI_DELBA_REQ_EVENTID, + WMI_TX_COMPLETE_EVENTID, + WMI_HCI_EVENT_EVENTID, + WMI_ACL_DATA_EVENTID, + WMI_REPORT_SLEEP_STATE_EVENTID, + WMI_REPORT_BTCOEX_STATS_EVENTID, + WMI_REPORT_BTCOEX_CONFIG_EVENTID, + WMI_GET_PMK_EVENTID, + + /* DFS Events */ + WMI_DFS_HOST_ATTACH_EVENTID, + WMI_DFS_HOST_INIT_EVENTID, + WMI_DFS_RESET_DELAYLINES_EVENTID, + WMI_DFS_RESET_RADARQ_EVENTID, + WMI_DFS_RESET_AR_EVENTID, + WMI_DFS_RESET_ARQ_EVENTID, + WMI_DFS_SET_DUR_MULTIPLIER_EVENTID, + WMI_DFS_SET_BANGRADAR_EVENTID, + WMI_DFS_SET_DEBUGLEVEL_EVENTID, + WMI_DFS_PHYERR_EVENTID, + /* CCX Evants */ + WMI_CCX_RM_STATUS_EVENTID, + + /* P2P Events */ + WMI_P2P_GO_NEG_RESULT_EVENTID, + + WMI_WAC_SCAN_DONE_EVENTID, + WMI_WAC_REPORT_BSS_EVENTID, + WMI_WAC_START_WPS_EVENTID, + WMI_WAC_CTRL_REQ_REPLY_EVENTID, + WMI_REPORT_WMM_PARAMS_EVENTID, + + /* WILOCITY types */ + WMI_IMM_RSP_EVENTID = 0x0000, + WMI_RD_MEM_RSP_EVENTID = 0x1800, + WMI_FW_READY_EVENTID = 0x1801, + WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200, + WMI_ECHO_RSP_EVENTID = 0x1803, + WMI_CONFIG_MAC_DONE_EVENTID = 0x1805, + WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806, + WMI_ADD_STATION_DONE_EVENTID = 0x1807, + WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808, + WMI_PHY_GET_STATISTICS_EVENTID = 0x1809, + WMI_FS_TUNE_DONE_EVENTID = 0x180A, + WMI_CORR_MEASURE_DONE_EVENTID = 0x180B, + WMI_TEMP_SENSE_DONE_EVENTID = 0x180E, + WMI_DC_CALIB_DONE_EVENTID = 0x180F, + WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, + WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, + WMI_SET_CHANNEL_DONE_EVENTID = 0x1814, + WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, + WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, + WMI_WIL6210_R_ACTIVATE_DONE_EVENTID = 0x1817, + WMI_WIL6210_R_READ_DONE_EVENTID = 0x1818, + WMI_WIL6210_R_WRITE_DONE_EVENTID = 0x1819, + WMI_WIL6210_R_TXRX_SEL_DONE_EVENTID = 0x181A, + WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181D, + + WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, + WMI_VRING_CFG_DONE_EVENTID = 0x1821, + WMI_RX_ON_EVENTID = 0x1822, + WMI_BA_STATUS_EVENTID = 0x1823, + WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, + WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, + WMI_DELBA_EVENTID = 0x1826, + + WMI_READ_MAC_RXQ_EVENTID = 0x1830, + WMI_READ_MAC_TXQ_EVENTID = 0x1831, + WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, + WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, + WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, + + WMI_BF_START_TXSS_DONE_EVENTID = 0x1836, + WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, + WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, + WMI_RS_MGMT_DONE_EVENTID = 0x1852, + WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, + WMI_RX_MGMT_PACKET_EVENTID = 0x1840, + WMI_LINK_START_DONE_EVENTID = 0x1850, + WMI_LINK_STOP_DONE_EVENTID = 0x1851, + + /* Performance monitoring events */ + WMI_WBE_LINKUP_EVENTID = 0x1860, + WMI_WBE_LINKDOWN_EVENTID = 0x1861, + + WMI_BF_CTRL_DONE_EVENTID = 0x1862, + WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, + + WMI_UNIT_TEST_EVENTID = 0x1900, + + WMI_THIN_RESERVED_START_EVENTID = 0x8000, + /* Events in this range are reserved for thinmode + * See wmi_thin.h for actual definitions */ + WMI_THIN_RESERVED_END_EVENTID = 0x8fff, + + WMI_SET_CHANNEL_EVENTID, + WMI_ASSOC_REQ_EVENTID +}; + +/* + * WMI_READY_EVENTID + */ +struct WMI_READY_EVENT { + u32 sw_version; + u32 abi_version; + u8 macaddr[ETH_ALEN]; + u8 phyCapability; /* WMI_PHY_CAPABILITY */ +} __packed; + +enum WMI_NETWORK_TYPE { + INFRA_NETWORK = 0x01, + ADHOC_NETWORK = 0x02, + ADHOC_CREATOR = 0x04, + AP_NETWORK = 0x10, + P2P_NETWORK = 0x20, + WBE_NETWORK = 0x40, +}; + +enum DOT11_AUTH_MODE { + OPEN_AUTH = 0x01, + SHARED_AUTH = 0x02, + LEAP_AUTH = 0x04, /* different from IEEE_AUTH_MODE definitions */ +}; + +enum AUTH_MODE { + NONE_AUTH = 0x01, + WPA_AUTH = 0x02, + WPA2_AUTH = 0x04, + WPA_PSK_AUTH = 0x08, + WPA2_PSK_AUTH = 0x10, + WPA_AUTH_CCKM = 0x20, + WPA2_AUTH_CCKM = 0x40, +}; + +enum CRYPTO_TYPE { + NONE_CRYPT = 0x01, + WEP_CRYPT = 0x02, + TKIP_CRYPT = 0x04, + AES_CRYPT = 0x08, +#ifdef WAPI_ENABLE + WAPI_CRYPT = 0x10, +#endif /*WAPI_ENABLE*/ +}; + +#define WMI_MIN_CRYPTO_TYPE NONE_CRYPT +#define WMI_MAX_CRYPTO_TYPE (AES_CRYPT + 1) + +#ifdef WAPI_ENABLE +#undef WMI_MAX_CRYPTO_TYPE +#define WMI_MAX_CRYPTO_TYPE (WAPI_CRYPT + 1) +#endif /* WAPI_ENABLE */ + +#ifdef WAPI_ENABLE +#define IW_ENCODE_ALG_SM4 0x20 +#define IW_AUTH_WAPI_ENABLED 0x20 +#endif + +#define WMI_MIN_KEY_INDEX 0 +#define WMI_MAX_KEY_INDEX 3 + +#ifdef WAPI_ENABLE +#undef WMI_MAX_KEY_INDEX +#define WMI_MAX_KEY_INDEX 7 /* wapi grpKey 0-3, prwKey 4-7 */ +#endif /* WAPI_ENABLE */ + +enum WMI_CONNECT_CTRL_FLAGS_BITS { + CONNECT_ASSOC_POLICY_USER = 0x0001, + CONNECT_SEND_REASSOC = 0x0002, + CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, + CONNECT_PROFILE_MATCH_DONE = 0x0008, + CONNECT_IGNORE_AAC_BEACON = 0x0010, + CONNECT_CSA_FOLLOW_BSS = 0x0020, + CONNECT_DO_WPA_OFFLOAD = 0x0040, + CONNECT_DO_NOT_DEAUTH = 0x0080, +}; +#define DEFAULT_CONNECT_CTRL_FLAGS (CONNECT_CSA_FOLLOW_BSS) + +/* + * WMI_CONNECT_CMDID + */ +struct WMI_CONNECT_CMD { + u8 networkType; + u8 dot11AuthMode; + u8 authMode; + u8 pairwiseCryptoType; + u8 pairwiseCryptoLen; + u8 groupCryptoType; + u8 groupCryptoLen; + u8 ssidLength; + u8 ssid[32]; + u16 channel; + u8 bssid[ETH_ALEN]; + u32 ctrl_flags; + u8 destMacAddr[ETH_ALEN]; + u16 reserved; +} __packed; + +/* + * WMI_CONNECT_EVENTID + */ +struct WMI_CONNECT_EVENT { + u16 channel; + u8 bssid[ETH_ALEN]; + u16 listenInterval; + u16 beaconInterval; + u32 networkType; + u8 beaconIeLen; + u8 assocReqLen; + u8 assocRespLen; + u8 cid; + u8 reserved0; + u16 reserved1; + u8 assocInfo[0]; +} __packed; + +/* + * WMI_DISCONNECT_EVENTID + */ +enum WMI_DISCONNECT_REASON { + NO_NETWORK_AVAIL = 0x01, + LOST_LINK = 0x02, /* bmiss */ + DISCONNECT_CMD = 0x03, + BSS_DISCONNECTED = 0x04, + AUTH_FAILED = 0x05, + ASSOC_FAILED = 0x06, + NO_RESOURCES_AVAIL = 0x07, + CSERV_DISCONNECT = 0x08, + INVALID_PROFILE = 0x0a, + DOT11H_CHANNEL_SWITCH = 0x0b, + PROFILE_MISMATCH = 0x0c, + CONNECTION_EVICTED = 0x0d, + IBSS_MERGE = 0xe, +}; + +struct WMI_DISCONNECT_EVENT { + u16 protocolReasonStatus; /* reason code, see 802.11 spec. */ + u8 bssid[ETH_ALEN]; /* set if known */ + u8 disconnectReason; /* see WMI_DISCONNECT_REASON */ + u8 assocRespLen; + u8 assocInfo[0]; +} __packed; + +/* + * WMI_START_SCAN_CMDID + */ +enum WMI_SCAN_TYPE { + WMI_LONG_SCAN = 0, WMI_SHORT_SCAN = 1, +}; + +struct WMI_START_SCAN_CMD { + u32 forceFgScan; + u32 isLegacy; /* For Legacy Cisco AP compatibility */ + u32 homeDwellTime; /* Maximum duration in the home channel(msec) */ + u32 forceScanInterval; /* Time interval between scans (milliseconds)*/ + u8 scanType; /* WMI_SCAN_TYPE */ + u8 numChannels; /* how many channels follow */ + u16 channelList[0]; /* channels in Mhz */ +} __packed; + +/* + * WMI_SET_SCAN_PARAMS_CMDID + */ +#define WMI_SHORTSCANRATIO_DEFAULT 3 +/* + * Warning: ScanCtrlFlag value of 0xFF is used + * to disable all flags in WMI_SCAN_PARAMS_CMD + * Do not add any more flags to WMI_SCAN_CTRL_FLAG_BITS + */ +enum WMI_SCAN_CTRL_FLAGS_BITS { + CONNECT_SCAN_CTRL_FLAGS = 0x01, /* set if can scan in the Connect cmd */ + SCAN_CONNECTED_CTRL_FLAGS = 0x02, /* set if scan for the SSID it is */ + /* already connected to */ + ACTIVE_SCAN_CTRL_FLAGS = 0x04, /* set if enable active scan */ + ROAM_SCAN_CTRL_FLAGS = 0x08, /* set if enable roam scan */ + /* when bmiss and lowrssi */ + REPORT_BSSINFO_CTRL_FLAGS = 0x10, /* set if follows customer */ + /* BSSINFO reporting rule */ + ENABLE_AUTO_CTRL_FLAGS = 0x20, /* if disabled, target doesn't */ + /* scan after a disconnect event */ + ENABLE_SCAN_ABORT_EVENT = 0x40, /* Scan complete event with canceled */ + /* status will be generated */ + /* when a scan is prempted */ + /* before it gets completed */ +}; + +#define CAN_SCAN_IN_CONNECT(flags) (flags & CONNECT_SCAN_CTRL_FLAGS) +#define CAN_SCAN_CONNECTED(flags) (flags & SCAN_CONNECTED_CTRL_FLAGS) +#define ENABLE_ACTIVE_SCAN(flags) (flags & ACTIVE_SCAN_CTRL_FLAGS) +#define ENABLE_ROAM_SCAN(flags) (flags & ROAM_SCAN_CTRL_FLAGS) +#define CONFIG_REPORT_BSSINFO(flags) (flags & REPORT_BSSINFO_CTRL_FLAGS) +#define IS_AUTO_SCAN_ENABLED(flags) (flags & ENABLE_AUTO_CTRL_FLAGS) +#define SCAN_ABORT_EVENT_ENABLED(flags) (flags & ENABLE_SCAN_ABORT_EVENT) + +#define DEFAULT_SCAN_CTRL_FLAGS (CONNECT_SCAN_CTRL_FLAGS | \ + SCAN_CONNECTED_CTRL_FLAGS | ACTIVE_SCAN_CTRL_FLAGS | \ + ROAM_SCAN_CTRL_FLAGS | ENABLE_AUTO_CTRL_FLAGS) + +struct WMI_SET_SCAN_PARAMS_CMD { + u16 fg_start_period; /* seconds */ + u16 fg_end_period; /* seconds */ + u16 bg_period; /* seconds */ + u16 maxact_chdwell_time; /* msec */ + u16 pas_chdwell_time; /* msec */ + u8 shortScanRatio; /* how many shorts scan for one long */ + u8 scanCtrlFlags; + u16 minact_chdwell_time; /* msec */ + u16 maxact_scan_per_ssid; /* max active scans per ssid */ + u32 max_dfsch_act_time; /* msecs */ +} __packed; + +/* + * WMI_SET_BSS_FILTER_CMDID + */ +enum WMI_BSS_FILTER { + NONE_BSS_FILTER = 0x0, /* no beacons forwarded */ + ALL_BSS_FILTER, /* all beacons forwarded */ + PROFILE_FILTER, /* only beacons matching profile */ + ALL_BUT_PROFILE_FILTER, /* all but beacons matching profile */ + CURRENT_BSS_FILTER, /* only beacons matching current BSS */ + ALL_BUT_BSS_FILTER, /* all but beacons matching BSS */ + PROBED_SSID_FILTER, /* beacons matching probed ssid */ + LAST_BSS_FILTER, /* marker only */ +}; + +struct WMI_SET_BSS_FILTER_CMD { + u8 bssFilter; /* see WMI_BSS_FILTER */ + u8 reserved1; /* For alignment */ + u16 reserved2; /* For alignment */ + u32 ieMask; +} __packed; + +/* + * WMI_SCAN_COMPLETE_EVENTID - status parameter + */ +struct WMI_SCAN_COMPLETE_EVENT { + u32 status; +} __packed; + +/* + * WMI_RX_MGMT_PACKET_EVENTID + */ +struct WMI_RX_MGMT_PACKET_EVENT { + u16 mcs; + u16 rssi; + u32 status; + u32 length; + u8 qid; /* Not resolved when == 0xFFFFFFFF ==> Broadcast to all MIDS */ + u8 mid; /* Not resolved when == 0xFFFFFFFF ==> Broadcast to all MIDS */ + u8 cid; + u8 channel; /* From Radio MNGR */ + u8 payload[0]; /* struct ieee80211_mgmt */ +} __packed; + +/* DMA rings */ + +struct wmi_sw_ring_cfg { + u64 ring_mem_base; /* 48 bit; upper 16 bits reserved */ + u16 ring_size; + u16 max_mpdu_size; +} __packed; + +enum wmi_cfg_rx_chain_cmd_action { + ADD_RX_CHAIN = 0x0, DELETE_RX_CHAIN = 0x1, +}; + +enum wmi_cfg_rx_chain_cmd_decap_trans_type { + DECAP_TYPE_802_3 = 0x0, DECAP_TYPE_NATIVE_WIFI = 0x1, +}; + +enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { + NWIFI_RX_NO_TRANS_MODE = 0x0, + NWIFI_RX_PBSS2AP_TRANS_MODE = 0x1, + NWIFI_RX_PBSS2STA_TRANS_MODE = 0x2, +}; + +/* + * WMI_CFG_RX_CHAIN_CMDID + */ +struct WMI_CFG_RX_CHAIN_CMD { + u32 action; /* enum wmi_cfg_rx_chain_cmd_action */ + struct wmi_sw_ring_cfg sw_ring; + u8 mid; + u8 decap_trans_type; /* enum wmi_cfg_rx_chain_cmd_decap_trans_type */ + u8 l2_802_3_offload_ctrl; + u8 l2_nwifi_offload_ctrl; + u8 vlan_id; + u8 nwifi_ds_trans_type; + /* enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type */ + u8 l3_l4_ctrl; + u8 ring_ctrl; + u16 prefetch_thrsh; + u16 wb_thrsh; + u32 itr_value; + u16 host_thrsh; + u16 reserved; + /* added for sniffer mode */ + u32 sniffer_mode; /* 0/1, other sniffer fields ignored if 0 */ + u32 sniffer_phy_info; /* 0/1, should phy_info be included? */ + u32 sniffer_phy; /* 0 - CP; 1 - DP */ + u32 sniffer_channel; /* channel index 0..2 */ +} __packed; + +enum wmi_cfg_rx_chain_done_event_status { + CFG_RX_CHAIN_SUCCESS = 0x1, +}; + +/* + * WMI_CFG_RX_CHAIN_DONE_EVENTID + */ +struct WMI_CFG_RX_CHAIN_DONE_EVENT { + u32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */ + u32 status; /* enum wmi_cfg_rx_chain_done_event_status */ +} __packed; + +/* + * WMI_VRING_CFG_CMDID + */ +enum wmi_vring_cfg_cmd_action { + ADD_VRING = 0x0, + MODIFY_VRING = 0x1, + DELETE_VRING = 0x2, +}; + +enum wmi_vring_cfg_encap_trans_type { + ENC_TYPE_802_3 = 0x0, + ENC_TYPE_NATIVE_WIFI = 0x1, +}; + +enum wmi_vring_cfg_ds_cfg { + PBSS_MODE = 0x0, + STATION_MODE = 0x1, + AP_MODE = 0x2, + ADDR4_MODE = 0x3, +}; + +enum wmi_vring_cfg_nwifi_ds_trans_type { + NWIFI_TX_NO_TRANS_MODE = 0x0, + NWIFI_TX_AP2PBSS_TRANS_MODE = 0x1, + NWIFI_TX_STA2PBSS_TRANS_MODE = 0x2, +}; + +enum wmi_vring_cfg_schd_params_priority { + REGULAR = 0x0, + HIGH = 0x1, +}; + +struct WMI_VRING_CFG_CMD { + u32 action; /* enum wmi_vring_cfg_cmd_action */ + struct wmi_sw_ring_cfg sw_ring; + u8 ringid; /* 0-23 */ + u8 cidxtid; /* 0..3: cid; 4..7: tid */ + u8 encap_trans_type; /* enum wmi_vring_cfg_encap_trans_type */ + u8 ds_cfg; /* enum wmi_vring_cfg_ds_cfg - 802.3 DS cfg */ + u8 nwifi_ds_trans_type; /* enum wmi_vring_cfg_nwifi_ds_trans_type */ + u8 mac_ctrl; /* 0: lifetime_en; 1: aggr_en */ +#define VRING_CFG_MAC_CTRL_LIFETIME_EN BIT(0) +#define VRING_CFG_MAC_CTRL_AGGR_EN BIT(1) + u8 to_resolution; /* 0..5: value; 6..7: reserved */ + u8 agg_max_wsize; + /* schd_params */ + u16 priority; /* enum wmi_vring_cfg_schd_params_priority */ + u16 timeslot_us; +} __packed; + +/* + * WMI_VRING_CFG_DONE_EVENTID + */ +enum wmi_vring_cfg_done_event_status { + VRING_CFG_SUCCESS = 0x0, VRING_CFG_FAILURE = 0x1, +}; + +struct WMI_VRING_CFG_DONE_EVENT { + u8 ringid; + u8 status; + u16 reserved; + u32 tx_vring_tail_ptr; /* Tx vring tail pointer */ +} __packed; + +/* + * WMI_SET_MAC_ADDRESS_CMDID + */ +struct WMI_SET_MAC_ADDRESS_CMD { + u8 macaddr[ETH_ALEN]; + u16 reserved; +} __packed; + +/* + * WMI_SET_BEACON_INT_CMDID + */ +struct WMI_SET_BEACON_INT_CMD { + u16 bcon_interval; + u16 frag_num; + u32 ss_mask_low; + u32 ss_mask_high; + u16 network_type; + u16 reserved; +} __packed; + +/* + * WMI_NOTIFY_REQ_CMDID + */ +struct WMI_NOTIFY_REQ_CMD { + u32 cid; + u32 interval_usec; +} __packed; + +/* + * WMI_NOTIFY_REQ_DONE_EVENTID + */ +struct WMI_NOTIFY_REQ_DONE_EVENT { + u32 bf_status; + u32 tsf_hi; + u32 tsf_lo; + u32 bf_metric; + u32 tx_tpt; + u32 tx_gput; + u32 rx_gput; + u16 bf_mcs; + u16 my_rx_sector; + u16 my_tx_sector; + u16 peer_rx_sector; + u16 peer_tx_sector; + u16 reserved; +} __packed; + +#endif /* __WMI_H__ */ --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ +obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH_COMMON) += ath.o compat-drivers-2012-09-18/crap/README0000644000175000017500000000106412026176477016211 0ustar mcgrofmcgrof compat-drivers crap patches ============================ If you are including patches into this directory you must be fixing some critical bug for a customer which needs immediate release or immediate testing. Alternatively you would use this to apply some sort of crap code you are maintaining. You must have a really good reason to be adding files in this directory. If possible you should explain your reasoning of why the patch is getting included here and not upstream and why it hasn't even yet been posted. You should avoid these patches at all costs.